summaryrefslogtreecommitdiff
path: root/linux-core
diff options
context:
space:
mode:
Diffstat (limited to 'linux-core')
-rw-r--r--linux-core/Makefile7
-rw-r--r--linux-core/Makefile.kernel21
l---------linux-core/amd.h1
l---------linux-core/amd_legacy.h1
l---------linux-core/amd_legacy_cbuffer.c1
l---------linux-core/amd_legacy_fence.h1
-rw-r--r--linux-core/drmP.h122
-rw-r--r--linux-core/drm_auth.c28
-rw-r--r--linux-core/drm_bo.c18
-rw-r--r--linux-core/drm_bo_move.c1
-rw-r--r--linux-core/drm_bufs.c22
-rw-r--r--linux-core/drm_compat.c53
-rw-r--r--linux-core/drm_compat.h28
-rw-r--r--linux-core/drm_context.c10
-rw-r--r--linux-core/drm_crtc.c2209
-rw-r--r--linux-core/drm_crtc.h733
-rw-r--r--linux-core/drm_crtc_helper.c761
-rw-r--r--linux-core/drm_crtc_helper.h95
-rw-r--r--linux-core/drm_drv.c110
-rw-r--r--linux-core/drm_edid.c733
-rw-r--r--linux-core/drm_edid.h178
-rw-r--r--linux-core/drm_fb.c432
-rw-r--r--linux-core/drm_fops.c175
-rw-r--r--linux-core/drm_ioctl.c50
-rw-r--r--linux-core/drm_irq.c178
-rw-r--r--linux-core/drm_lock.c102
-rw-r--r--linux-core/drm_memrange.c1
-rw-r--r--linux-core/drm_modes.c566
-rw-r--r--linux-core/drm_objects.h7
-rw-r--r--linux-core/drm_proc.c8
-rw-r--r--linux-core/drm_stub.c118
-rw-r--r--linux-core/drm_sysfs.c239
-rw-r--r--linux-core/dvo.h159
-rw-r--r--linux-core/dvo_ch7017.c454
-rw-r--r--linux-core/dvo_ch7xxx.c368
-rw-r--r--linux-core/dvo_ivch.c463
-rw-r--r--linux-core/dvo_sil164.c302
-rw-r--r--linux-core/dvo_tfp410.c335
-rw-r--r--linux-core/i915_buffer.c12
-rw-r--r--linux-core/i915_drv.c82
-rw-r--r--linux-core/i915_execbuf.c10
-rw-r--r--linux-core/i915_fence.c12
-rw-r--r--linux-core/i915_gem.c54
l---------linux-core/i915_init.c1
-rw-r--r--linux-core/intel_bios.c242
-rw-r--r--linux-core/intel_bios.h405
-rw-r--r--linux-core/intel_crt.c296
-rw-r--r--linux-core/intel_display.c1613
-rw-r--r--linux-core/intel_drv.h126
-rw-r--r--linux-core/intel_dvo.c507
-rw-r--r--linux-core/intel_fb.c1011
-rw-r--r--linux-core/intel_i2c.c190
-rw-r--r--linux-core/intel_lvds.c515
-rw-r--r--linux-core/intel_modes.c61
-rw-r--r--linux-core/intel_sdvo.c1153
-rw-r--r--linux-core/intel_sdvo_regs.h328
-rw-r--r--linux-core/intel_tv.c1734
l---------linux-core/radeon_ms.h1
l---------linux-core/radeon_ms_bo.c1
l---------linux-core/radeon_ms_bus.c1
l---------linux-core/radeon_ms_combios.c1
l---------linux-core/radeon_ms_combios.h1
-rw-r--r--linux-core/radeon_ms_compat.c67
l---------linux-core/radeon_ms_cp.c1
l---------linux-core/radeon_ms_cp_mc.c1
l---------linux-core/radeon_ms_crtc.c1
l---------linux-core/radeon_ms_dac.c1
l---------linux-core/radeon_ms_drm.c1
l---------linux-core/radeon_ms_drm.h1
-rw-r--r--linux-core/radeon_ms_drv.c144
-rw-r--r--linux-core/radeon_ms_drv.h46
l---------linux-core/radeon_ms_exec.c1
l---------linux-core/radeon_ms_family.c1
-rw-r--r--linux-core/radeon_ms_fb.c462
l---------linux-core/radeon_ms_fence.c1
l---------linux-core/radeon_ms_gpu.c1
l---------linux-core/radeon_ms_i2c.c1
l---------linux-core/radeon_ms_irq.c1
l---------linux-core/radeon_ms_output.c1
l---------linux-core/radeon_ms_properties.c1
l---------linux-core/radeon_ms_properties.h1
l---------linux-core/radeon_ms_reg.h1
l---------linux-core/radeon_ms_rom.c1
l---------linux-core/radeon_ms_rom.h1
l---------linux-core/radeon_ms_state.c1
-rw-r--r--linux-core/via_fence.c4
86 files changed, 17820 insertions, 369 deletions
diff --git a/linux-core/Makefile b/linux-core/Makefile
index fc32676f..846386a5 100644
--- a/linux-core/Makefile
+++ b/linux-core/Makefile
@@ -59,7 +59,7 @@ endif
# Modules for all architectures
MODULE_LIST := drm.o tdfx.o r128.o radeon.o mga.o sis.o savage.o via.o \
- mach64.o nv.o nouveau.o xgi.o
+ mach64.o nv.o nouveau.o xgi.o radeon_ms.o
# Modules only for ix86 architectures
ifneq (,$(findstring 86,$(MACHINE)))
@@ -93,6 +93,7 @@ NVHEADERS = nv_drv.h $(DRMHEADERS)
FFBHEADERS = ffb_drv.h $(DRMHEADERS)
NOUVEAUHEADERS = nouveau_drv.h nouveau_drm.h nouveau_reg.h $(DRMHEADERS)
XGIHEADERS = xgi_cmdlist.h xgi_drv.h xgi_misc.h xgi_regs.h $(DRMHEADERS)
+RADEONMSHEADERS = radeon_ms_driver.h $(DRMHEADERS)
PROGS = dristat drmstat
@@ -287,6 +288,7 @@ CONFIG_DRM_MACH64 := n
CONFIG_DRM_NV := n
CONFIG_DRM_NOUVEAU := n
CONFIG_DRM_XGI := n
+CONFIG_DRM_RADEON_MS := n
# Enable module builds for the modules requested/supported.
@@ -326,6 +328,9 @@ endif
ifneq (,$(findstring xgi,$(DRM_MODULES)))
CONFIG_DRM_XGI := m
endif
+ifneq (,$(findstring radeon_ms,$(DRM_MODULES)))
+#CONFIG_DRM_RADEON_MS := m
+endif
# These require AGP support
diff --git a/linux-core/Makefile.kernel b/linux-core/Makefile.kernel
index 82d200b3..29503004 100644
--- a/linux-core/Makefile.kernel
+++ b/linux-core/Makefile.kernel
@@ -13,14 +13,19 @@ drm-objs := drm_auth.o drm_bufs.o drm_context.o drm_dma.o drm_drawable.o \
drm_sysfs.o drm_pci.o drm_agpsupport.o drm_scatter.o \
drm_memory_debug.o ati_pcigart.o drm_sman.o \
drm_hashtab.o drm_memrange.o drm_object.o drm_compat.o \
- drm_fence.o drm_ttm.o drm_bo.o drm_bo_move.o drm_bo_lock.o \
- drm_regman.o drm_vm_nopage_compat.o drm_gem.o
+ drm_fence.o drm_ttm.o drm_bo.o drm_bo_move.o drm_crtc.o \
+ drm_edid.o drm_modes.o drm_bo_lock.o drm_regman.o \
+ drm_vm_nopage_compat.o drm_crtc_helper.o drm_gem.o
tdfx-objs := tdfx_drv.o
r128-objs := r128_drv.o r128_cce.o r128_state.o r128_irq.o
mga-objs := mga_drv.o mga_dma.o mga_state.o mga_warp.o mga_irq.o
i810-objs := i810_drv.o i810_dma.o
i915-objs := i915_drv.o i915_dma.o i915_irq.o i915_mem.o i915_fence.o \
- i915_buffer.o i915_compat.o i915_execbuf.o i915_gem.o
+ i915_buffer.o i915_execbuf.o i915_gem.o \
+ intel_display.o intel_crt.o intel_lvds.o intel_bios.o \
+ intel_sdvo.o intel_modes.o intel_i2c.o i915_init.o intel_fb.o \
+ intel_tv.o i915_compat.o intel_dvo.o dvo_ch7xxx.o \
+ dvo_ch7017.o dvo_ivch.o dvo_tfp410.o dvo_sil164.o
nouveau-objs := nouveau_drv.o nouveau_state.o nouveau_fifo.o nouveau_mem.o \
nouveau_object.o nouveau_irq.o nouveau_notifier.o nouveau_swmthd.o \
nouveau_sgdma.o nouveau_dma.o nouveau_bo.o nouveau_fence.o \
@@ -32,6 +37,14 @@ nouveau-objs := nouveau_drv.o nouveau_state.o nouveau_fifo.o nouveau_mem.o \
nv40_graph.o nv50_graph.o \
nv04_instmem.o nv50_instmem.o
radeon-objs := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o radeon_irq.o r300_cmdbuf.o
+radeon_ms-objs := radeon_ms_drv.o radeon_ms_drm.o radeon_ms_family.o \
+ radeon_ms_state.o radeon_ms_bo.o radeon_ms_irq.o \
+ radeon_ms_bus.o radeon_ms_fence.o \
+ radeon_ms_cp.o radeon_ms_cp_mc.o radeon_ms_i2c.o \
+ radeon_ms_output.o radeon_ms_crtc.o radeon_ms_fb.o \
+ radeon_ms_exec.o radeon_ms_gpu.o radeon_ms_dac.o \
+ radeon_ms_properties.o radeon_ms_rom.o radeon_ms_combios.o \
+ amd_legacy_cbuffer.o
sis-objs := sis_drv.o sis_mm.o
ffb-objs := ffb_drv.o ffb_context.o
savage-objs := savage_drv.o savage_bci.o savage_state.o
@@ -45,6 +58,7 @@ xgi-objs := xgi_cmdlist.o xgi_drv.o xgi_fb.o xgi_misc.o xgi_pcie.o \
ifeq ($(CONFIG_COMPAT),y)
drm-objs += drm_ioc32.o
radeon-objs += radeon_ioc32.o
+radeon_ms-objs += radeon_ms_compat.o
mga-objs += mga_ioc32.o
r128-objs += r128_ioc32.o
i915-objs += i915_ioc32.o
@@ -67,3 +81,4 @@ obj-$(CONFIG_DRM_MACH64)+= mach64.o
obj-$(CONFIG_DRM_NV) += nv.o
obj-$(CONFIG_DRM_NOUVEAU) += nouveau.o
obj-$(CONFIG_DRM_XGI) += xgi.o
+obj-$(CONFIG_DRM_RADEON_MS) += radeon_ms.o
diff --git a/linux-core/amd.h b/linux-core/amd.h
new file mode 120000
index 00000000..b4882447
--- /dev/null
+++ b/linux-core/amd.h
@@ -0,0 +1 @@
+../shared-core/amd.h \ No newline at end of file
diff --git a/linux-core/amd_legacy.h b/linux-core/amd_legacy.h
new file mode 120000
index 00000000..1a7786fc
--- /dev/null
+++ b/linux-core/amd_legacy.h
@@ -0,0 +1 @@
+../shared-core/amd_legacy.h \ No newline at end of file
diff --git a/linux-core/amd_legacy_cbuffer.c b/linux-core/amd_legacy_cbuffer.c
new file mode 120000
index 00000000..eab329b5
--- /dev/null
+++ b/linux-core/amd_legacy_cbuffer.c
@@ -0,0 +1 @@
+../shared-core/amd_legacy_cbuffer.c \ No newline at end of file
diff --git a/linux-core/amd_legacy_fence.h b/linux-core/amd_legacy_fence.h
new file mode 120000
index 00000000..e7b30f25
--- /dev/null
+++ b/linux-core/amd_legacy_fence.h
@@ -0,0 +1 @@
+../shared-core/amd_legacy_fence.h \ No newline at end of file
diff --git a/linux-core/drmP.h b/linux-core/drmP.h
index 8246f44a..c18159f8 100644
--- a/linux-core/drmP.h
+++ b/linux-core/drmP.h
@@ -112,7 +112,8 @@ typedef unsigned long uintptr_t;
#define DRIVER_IRQ_SHARED 0x80
#define DRIVER_DMA_QUEUE 0x100
#define DRIVER_FB_DMA 0x200
-#define DRIVER_GEM 0x400
+#define DRIVER_MODESET 0x400
+#define DRIVER_GEM 0x800
/*@}*/
@@ -266,11 +267,11 @@ typedef unsigned long uintptr_t;
*/
#define LOCK_TEST_WITH_RETURN( dev, file_priv ) \
do { \
- if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || \
- dev->lock.file_priv != file_priv ) { \
+ if ( !_DRM_LOCK_IS_HELD( file_priv->master->lock.hw_lock->lock ) || \
+ file_priv->master->lock.file_priv != file_priv ) { \
DRM_ERROR( "%s called without lock held, held %d owner %p %p\n",\
- __FUNCTION__, _DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ),\
- dev->lock.file_priv, file_priv ); \
+ __FUNCTION__, _DRM_LOCK_IS_HELD( file_priv->master->lock.hw_lock->lock ),\
+ file_priv->master->lock.file_priv, file_priv ); \
return -EINVAL; \
} \
} while (0)
@@ -304,6 +305,7 @@ typedef int drm_ioctl_compat_t(struct file *filp, unsigned int cmd,
#define DRM_AUTH 0x1
#define DRM_MASTER 0x2
#define DRM_ROOT_ONLY 0x4
+#define DRM_CONTROL_ALLOW 0x8 // allow ioctl to operate on control node
struct drm_ioctl_desc {
unsigned int cmd;
@@ -413,14 +415,12 @@ enum drm_ref_type {
/** File private data */
struct drm_file {
int authenticated;
- int master;
pid_t pid;
uid_t uid;
drm_magic_t magic;
unsigned long ioctl_count;
struct list_head lhead;
struct drm_minor *minor;
- int remove_auth_on_close;
unsigned long lock_count;
/*
@@ -440,6 +440,11 @@ struct drm_file {
struct drm_open_hash refd_object_hash[_DRM_NO_REF_TYPES];
struct file *filp;
void *driver_priv;
+
+ int is_master; /* this file private is a master for a minor */
+ struct drm_master *master; /* master this node is currently associated with
+ N.B. not always minor->master */
+ struct list_head fbs;
};
/** Wait queue */
@@ -578,6 +583,8 @@ struct drm_map_list {
struct drm_hash_item hash;
struct drm_map *map; /**< mapping */
uint64_t user_token;
+ struct drm_master *master; /** if this map is associated with a specific
+ master */
struct drm_memrange_node *file_offset_node;
};
@@ -599,6 +606,13 @@ struct drm_vbl_sig {
struct task_struct *task;
};
+struct drm_hotplug_sig {
+ struct list_head head;
+ unsigned int counter;
+ struct siginfo info;
+ struct task_struct *task;
+};
+
/* location of GART table */
#define DRM_ATI_GART_MAIN 1
#define DRM_ATI_GART_FB 2
@@ -670,6 +684,29 @@ struct drm_gem_object {
};
#include "drm_objects.h"
+#include "drm_crtc.h"
+
+/* per-master structure */
+struct drm_master {
+
+ struct list_head head; /**< each minor contains a list of masters */
+ struct drm_minor *minor; /**< link back to minor we are a master for */
+
+ char *unique; /**< Unique identifier: e.g., busid */
+ int unique_len; /**< Length of unique field */
+
+ int blocked; /**< Blocked due to VC switch? */
+
+ /** \name Authentication */
+ /*@{ */
+ struct drm_open_hash magiclist;
+ struct list_head magicfree;
+ /*@} */
+
+ struct drm_lock_data lock; /**< Information on hardware lock */
+
+ void *driver_priv; /**< Private structure for driver to use */
+};
/**
* DRM driver structure. This structure represent the common code for
@@ -770,6 +807,9 @@ struct drm_driver {
void (*set_version) (struct drm_device *dev,
struct drm_set_version *sv);
+ /* Master routines */
+ int (*master_create)(struct drm_device *dev, struct drm_master *master);
+ void (*master_destroy)(struct drm_device *dev, struct drm_master *master);
/**
* Driver-specific constructor for drm_gem_objects, to set up
* obj->driver_private.
@@ -815,6 +855,8 @@ struct drm_driver {
#define DRM_MINOR_UNASSIGNED 0
#define DRM_MINOR_LEGACY 1
+#define DRM_MINOR_CONTROL 2
+#define DRM_MINOR_RENDER 3
/**
* DRM minor structure. This structure represents a drm minor number.
@@ -825,8 +867,15 @@ struct drm_minor {
dev_t device; /**< Device number for mknod */
struct device kdev; /**< Linux device */
struct drm_device *dev;
+ /* for render nodes */
struct proc_dir_entry *dev_root; /**< proc directory entry */
struct class_device *dev_class;
+
+ /* for control nodes - a pointer to the current master for this control node */
+ struct drm_master *master; /* currently active master for this node */
+ struct list_head master_list;
+
+ struct drm_mode_group mode_group;
};
@@ -835,13 +884,9 @@ struct drm_minor {
* may contain multiple heads.
*/
struct drm_device {
- char *unique; /**< Unique identifier: e.g., busid */
- int unique_len; /**< Length of unique field */
char *devname; /**< For /proc/interrupts */
int if_version; /**< Highest interface version set */
- int blocked; /**< Blocked due to VC switch? */
-
/** \name Locks */
/*@{ */
spinlock_t count_lock; /**< For inuse, drm_device::open_count, drm_device::buf_use */
@@ -864,12 +909,6 @@ struct drm_device {
atomic_t counts[15];
/*@} */
- /** \name Authentication */
- /*@{ */
- struct list_head filelist;
- struct drm_open_hash magiclist;
- struct list_head magicfree;
- /*@} */
/** \name Memory management */
/*@{ */
@@ -890,7 +929,9 @@ struct drm_device {
struct idr ctx_idr;
struct list_head vmalist; /**< List of vmas (for debugging) */
- struct drm_lock_data lock; /**< Information on hardware lock */
+
+ struct list_head filelist;
+
/*@} */
/** \name DMA queues (contexts) */
@@ -902,6 +943,7 @@ struct drm_device {
struct drm_device_dma *dma; /**< Optional pointer for DMA support */
/*@} */
+
/** \name Context support */
/*@{ */
int irq; /**< Interrupt used by board */
@@ -918,6 +960,15 @@ struct drm_device {
struct work_struct work;
+ /** \name HOTPLUG IRQ support */
+ /*@{ */
+ wait_queue_head_t hotplug_queue; /**< HOTPLUG wait queue */
+ spinlock_t hotplug_lock;
+ struct list_head *hotplug_sigs; /**< signal list to send on HOTPLUG */
+ atomic_t hotplug_signal_pending; /* number of signals pending on all crtcs*/
+
+ /*@} */
+
/** \name VBLANK IRQ support */
/*@{ */
@@ -964,6 +1015,9 @@ struct drm_device {
struct drm_driver *driver;
drm_local_map_t *agp_buffer_map;
unsigned int agp_buffer_token;
+
+ /* minor number for control node */
+ struct drm_minor *control;
struct drm_minor *primary; /**< render type primary screen head */
struct drm_fence_manager fm;
@@ -975,6 +1029,9 @@ struct drm_device {
struct idr drw_idr;
/*@} */
+ /* DRM mode setting */
+ struct drm_mode_config mode_config;
+
/** \name GEM information */
/*@{ */
spinlock_t object_name_lock;
@@ -994,6 +1051,17 @@ struct drm_agp_ttm_backend {
};
#endif
+typedef struct ati_pcigart_ttm_backend {
+ struct drm_ttm_backend backend;
+ int populated;
+ void (*gart_flush_fn)(struct drm_device *dev);
+ struct drm_ati_pcigart_info *gart_info;
+ unsigned long offset;
+ struct page **pages;
+ int num_pages;
+ int bound;
+ struct drm_device *dev;
+} ati_pcigart_ttm_backend_t;
static __inline__ int drm_core_check_feature(struct drm_device *dev,
int feature)
@@ -1186,7 +1254,8 @@ extern void drm_idlelock_take(struct drm_lock_data *lock_data);
extern void drm_idlelock_release(struct drm_lock_data *lock_data);
extern int drm_client_lock_take(struct drm_device *dev,
struct drm_file *file_priv);
-extern void drm_client_lock_release(struct drm_device *dev);
+extern void drm_client_lock_release(struct drm_device *dev,
+ struct drm_file *file_priv);
/*
* These are exported to drivers so that they can implement fencing using
@@ -1245,13 +1314,17 @@ 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_uninstall(struct drm_device *dev);
+extern int drm_hotplug_init(struct drm_device *dev);
+extern int drm_wait_hotplug(struct drm_device *dev, void *data, struct drm_file *filp);
extern int drm_vblank_init(struct drm_device *dev, int num_crtcs);
extern int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *filp);
+extern int drm_wait_hotplug(struct drm_device *dev, void *data, struct drm_file *filp);
extern int drm_vblank_wait(struct drm_device * dev, unsigned int *vbl_seq);
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 void drm_handle_hotplug(struct drm_device *dev);
extern int drm_vblank_get(struct drm_device *dev, int crtc);
extern void drm_vblank_put(struct drm_device *dev, int crtc);
@@ -1296,6 +1369,12 @@ extern int drm_agp_unbind_memory(DRM_AGP_MEM * handle);
extern struct drm_ttm_backend *drm_agp_init_ttm(struct drm_device *dev);
extern void drm_agp_chipset_flush(struct drm_device *dev);
/* Stub support (drm_stub.h) */
+extern int drm_setmaster_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+extern int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+extern struct drm_master *drm_get_master(struct drm_minor *minor);
+extern void drm_put_master(struct drm_master *master);
extern int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
struct drm_driver *driver);
extern int drm_put_dev(struct drm_device *dev);
@@ -1336,7 +1415,11 @@ struct drm_sysfs_class;
extern struct class *drm_sysfs_create(struct module *owner, char *name);
extern void drm_sysfs_destroy(void);
extern int drm_sysfs_device_add(struct drm_minor *minor);
+extern void drm_sysfs_hotplug_event(struct drm_device *dev);
extern void drm_sysfs_device_remove(struct drm_minor *minor);
+extern char *drm_get_connector_status_name(enum drm_connector_status status);
+extern int drm_sysfs_connector_add(struct drm_connector *connector);
+extern void drm_sysfs_connector_remove(struct drm_connector *connector);
/*
* Basic memory manager support (drm_memrange.c)
@@ -1358,7 +1441,6 @@ extern int drm_memrange_remove_space_from_tail(struct drm_memrange *mm,
unsigned long size);
extern int drm_memrange_add_space_to_tail(struct drm_memrange *mm,
unsigned long size);
-
static inline struct drm_memrange *drm_get_mm(struct drm_memrange_node *block)
{
return block->mm;
diff --git a/linux-core/drm_auth.c b/linux-core/drm_auth.c
index c904a91d..20c8a634 100644
--- a/linux-core/drm_auth.c
+++ b/linux-core/drm_auth.c
@@ -45,14 +45,15 @@
* the one with matching magic number, while holding the drm_device::struct_mutex
* lock.
*/
-static struct drm_file *drm_find_file(struct drm_device * dev, drm_magic_t magic)
+static struct drm_file *drm_find_file(struct drm_master *master , drm_magic_t magic)
{
struct drm_file *retval = NULL;
struct drm_magic_entry *pt;
struct drm_hash_item *hash;
+ struct drm_device *dev = master->minor->dev;
mutex_lock(&dev->struct_mutex);
- if (!drm_ht_find_item(&dev->magiclist, (unsigned long)magic, &hash)) {
+ if (!drm_ht_find_item(&master->magiclist, (unsigned long)magic, &hash)) {
pt = drm_hash_entry(hash, struct drm_magic_entry, hash_item);
retval = pt->priv;
}
@@ -71,11 +72,11 @@ static struct drm_file *drm_find_file(struct drm_device * dev, drm_magic_t magic
* associated the magic number hash key in drm_device::magiclist, while holding
* the drm_device::struct_mutex lock.
*/
-static int drm_add_magic(struct drm_device * dev, struct drm_file * priv,
+static int drm_add_magic(struct drm_master *master, struct drm_file * priv,
drm_magic_t magic)
{
struct drm_magic_entry *entry;
-
+ struct drm_device *dev = master->minor->dev;
DRM_DEBUG("%d\n", magic);
entry = drm_alloc(sizeof(*entry), DRM_MEM_MAGIC);
@@ -85,8 +86,8 @@ static int drm_add_magic(struct drm_device * dev, struct drm_file * priv,
entry->priv = priv;
entry->hash_item.key = (unsigned long)magic;
mutex_lock(&dev->struct_mutex);
- drm_ht_insert_item(&dev->magiclist, &entry->hash_item);
- list_add_tail(&entry->head, &dev->magicfree);
+ drm_ht_insert_item(&master->magiclist, &entry->hash_item);
+ list_add_tail(&entry->head, &master->magicfree);
mutex_unlock(&dev->struct_mutex);
return 0;
@@ -101,20 +102,21 @@ static int drm_add_magic(struct drm_device * dev, struct drm_file * priv,
* Searches and unlinks the entry in drm_device::magiclist with the magic
* number hash key, while holding the drm_device::struct_mutex lock.
*/
-static int drm_remove_magic(struct drm_device * dev, drm_magic_t magic)
+static int drm_remove_magic(struct drm_master *master, drm_magic_t magic)
{
struct drm_magic_entry *pt;
struct drm_hash_item *hash;
+ struct drm_device *dev = master->minor->dev;
DRM_DEBUG("%d\n", magic);
mutex_lock(&dev->struct_mutex);
- if (drm_ht_find_item(&dev->magiclist, (unsigned long)magic, &hash)) {
+ if (drm_ht_find_item(&master->magiclist, (unsigned long)magic, &hash)) {
mutex_unlock(&dev->struct_mutex);
return -EINVAL;
}
pt = drm_hash_entry(hash, struct drm_magic_entry, hash_item);
- drm_ht_remove_item(&dev->magiclist, hash);
+ drm_ht_remove_item(&master->magiclist, hash);
list_del(&pt->head);
mutex_unlock(&dev->struct_mutex);
@@ -152,9 +154,9 @@ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv)
++sequence; /* reserve 0 */
auth->magic = sequence++;
spin_unlock(&lock);
- } while (drm_find_file(dev, auth->magic));
+ } while (drm_find_file(file_priv->master, auth->magic));
file_priv->magic = auth->magic;
- drm_add_magic(dev, file_priv, auth->magic);
+ drm_add_magic(file_priv->master, file_priv, auth->magic);
}
DRM_DEBUG("%u\n", auth->magic);
@@ -180,9 +182,9 @@ int drm_authmagic(struct drm_device *dev, void *data,
struct drm_file *file;
DRM_DEBUG("%u\n", auth->magic);
- if ((file = drm_find_file(dev, auth->magic))) {
+ if ((file = drm_find_file(file_priv->master, auth->magic))) {
file->authenticated = 1;
- drm_remove_magic(dev, auth->magic);
+ drm_remove_magic(file_priv->master, auth->magic);
return 0;
}
return -EINVAL;
diff --git a/linux-core/drm_bo.c b/linux-core/drm_bo.c
index 3abbb8c4..2e0d1243 100644
--- a/linux-core/drm_bo.c
+++ b/linux-core/drm_bo.c
@@ -624,7 +624,6 @@ void drm_putback_buffer_objects(struct drm_device *dev)
}
EXPORT_SYMBOL(drm_putback_buffer_objects);
-
/*
* Note. The caller has to register (if applicable)
* and deregister fence object usage.
@@ -1766,7 +1765,7 @@ int drm_buffer_object_create(struct drm_device *dev,
size += buffer_start & ~PAGE_MASK;
num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
if (num_pages == 0) {
- DRM_ERROR("Illegal buffer object size.\n");
+ DRM_ERROR("Illegal buffer object size %ld.\n", size);
return -EINVAL;
}
@@ -2362,15 +2361,18 @@ int drm_bo_driver_finish(struct drm_device *dev)
if (list_empty(&bm->unfenced))
DRM_DEBUG("Unfenced list was clean\n");
+ if (bm->dummy_read_page) {
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15))
- ClearPageReserved(bm->dummy_read_page);
+ ClearPageReserved(bm->dummy_read_page);
#endif
- __free_page(bm->dummy_read_page);
+ __free_page(bm->dummy_read_page);
+ }
out:
mutex_unlock(&dev->struct_mutex);
return ret;
}
+EXPORT_SYMBOL(drm_bo_driver_finish);
/*
* This function is intended to be called on drm driver load.
@@ -2406,8 +2408,14 @@ int drm_bo_driver_init(struct drm_device *dev)
* Other types need to be driver / IOCTL initialized.
*/
ret = drm_bo_init_mm(dev, DRM_BO_MEM_LOCAL, 0, 0, 1);
- if (ret)
+ if (ret) {
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15))
+ ClearPageReserved(bm->dummy_read_page);
+#endif
+ __free_page(bm->dummy_read_page);
+ bm->dummy_read_page = NULL;
goto out_unlock;
+ }
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
INIT_WORK(&bm->wq, &drm_bo_delayed_workqueue, dev);
diff --git a/linux-core/drm_bo_move.c b/linux-core/drm_bo_move.c
index 850be5a3..9147a475 100644
--- a/linux-core/drm_bo_move.c
+++ b/linux-core/drm_bo_move.c
@@ -146,6 +146,7 @@ void drm_mem_reg_iounmap(struct drm_device *dev, struct drm_bo_mem_reg *mem,
if (virtual && (man->flags & _DRM_FLAG_NEEDS_IOREMAP))
iounmap(virtual);
}
+EXPORT_SYMBOL(drm_mem_reg_iounmap);
static int drm_copy_io_page(void *dst, void *src, unsigned long page)
{
diff --git a/linux-core/drm_bufs.c b/linux-core/drm_bufs.c
index 75c75c2f..e9052570 100644
--- a/linux-core/drm_bufs.c
+++ b/linux-core/drm_bufs.c
@@ -52,9 +52,9 @@ struct drm_map_list *drm_find_matching_map(struct drm_device *dev, drm_local_map
{
struct drm_map_list *entry;
list_for_each_entry(entry, &dev->maplist, head) {
- if (entry->map && map->type == entry->map->type &&
- ((entry->map->offset == map->offset) ||
- (map->type == _DRM_SHM && map->flags==_DRM_CONTAINS_LOCK))) {
+ if (entry->map && (entry->master == dev->primary->master) && (map->type == entry->map->type) &&
+ ((entry->map->offset == map->offset) ||
+ ((map->type == _DRM_SHM) && (map->flags&_DRM_CONTAINS_LOCK)))) {
return entry;
}
}
@@ -209,12 +209,12 @@ static int drm_addmap_core(struct drm_device *dev, unsigned int offset,
map->offset = (unsigned long)map->handle;
if (map->flags & _DRM_CONTAINS_LOCK) {
/* Prevent a 2nd X Server from creating a 2nd lock */
- if (dev->lock.hw_lock != NULL) {
+ if (dev->primary->master->lock.hw_lock != NULL) {
vfree(map->handle);
drm_free(map, sizeof(*map), DRM_MEM_MAPS);
return -EBUSY;
}
- dev->sigdata.lock = dev->lock.hw_lock = map->handle; /* Pointer to lock */
+ dev->sigdata.lock = dev->primary->master->lock.hw_lock = map->handle; /* Pointer to lock */
}
break;
case _DRM_AGP: {
@@ -318,6 +318,7 @@ static int drm_addmap_core(struct drm_device *dev, unsigned int offset,
list->user_token = list->hash.key << PAGE_SHIFT;
mutex_unlock(&dev->struct_mutex);
+ list->master = dev->primary->master;
*maplist = list;
return 0;
}
@@ -344,7 +345,7 @@ int drm_addmap_ioctl(struct drm_device *dev, void *data,
struct drm_map_list *maplist;
int err;
- if (!(capable(CAP_SYS_ADMIN) || map->type == _DRM_AGP))
+ if (!(capable(CAP_SYS_ADMIN) || map->type == _DRM_AGP || map->type == _DRM_SHM))
return -EPERM;
err = drm_addmap_core(dev, map->offset, map->size, map->type,
@@ -379,10 +380,12 @@ int drm_rmmap_locked(struct drm_device *dev, drm_local_map_t *map)
struct drm_map_list *r_list = NULL, *list_t;
drm_dma_handle_t dmah;
int found = 0;
+ struct drm_master *master;
/* Find the list entry for the map and remove it */
list_for_each_entry_safe(r_list, list_t, &dev->maplist, head) {
if (r_list->map == map) {
+ master = r_list->master;
list_del(&r_list->head);
drm_ht_remove_key(&dev->map_hash,
r_list->user_token >> PAGE_SHIFT);
@@ -412,6 +415,13 @@ int drm_rmmap_locked(struct drm_device *dev, drm_local_map_t *map)
break;
case _DRM_SHM:
vfree(map->handle);
+ if (master) {
+ if (dev->sigdata.lock == master->lock.hw_lock)
+ dev->sigdata.lock = NULL;
+ master->lock.hw_lock = NULL; /* SHM removed */
+ master->lock.file_priv = NULL;
+ wake_up_interruptible(&master->lock.lock_queue);
+ }
break;
case _DRM_AGP:
case _DRM_SCATTER_GATHER:
diff --git a/linux-core/drm_compat.c b/linux-core/drm_compat.c
index 3d082e7e..dbb31578 100644
--- a/linux-core/drm_compat.c
+++ b/linux-core/drm_compat.c
@@ -217,7 +217,7 @@ static struct page *drm_bo_vm_fault(struct vm_area_struct *vma,
mutex_lock(&bo->mutex);
- err = drm_bo_wait(bo, 0, 1, 0);
+ err = drm_bo_wait(bo, 0, 1, 0, 1);
if (err) {
data->type = (err == -EAGAIN) ?
VM_FAULT_MINOR : VM_FAULT_SIGBUS;
@@ -730,6 +730,56 @@ void *idr_replace(struct idr *idp, void *ptr, int id)
EXPORT_SYMBOL(idr_replace);
#endif
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
+static __inline__ unsigned long __round_jiffies(unsigned long j, int cpu)
+{
+ int rem;
+ unsigned long original = j;
+
+ j += cpu * 3;
+
+ rem = j % HZ;
+
+ if (rem < HZ/4) /* round down */
+ j = j - rem;
+ else /* round up */
+ j = j - rem + HZ;
+
+ /* now that we have rounded, subtract the extra skew again */
+ j -= cpu * 3;
+
+ if (j <= jiffies) /* rounding ate our timeout entirely; */
+ return original;
+ return j;
+}
+
+static __inline__ unsigned long __round_jiffies_relative(unsigned long j, int cpu)
+{
+ return __round_jiffies(j + jiffies, cpu) - jiffies;
+}
+
+unsigned long round_jiffies_relative(unsigned long j)
+{
+ return __round_jiffies_relative(j, raw_smp_processor_id());
+}
+EXPORT_SYMBOL(round_jiffies_relative);
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
+struct pci_dev * pci_get_bus_and_slot(unsigned int bus, unsigned int devfn)
+{
+ struct pci_dev *dev = NULL;
+
+ while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
+ if (pci_domain_nr(dev->bus) == 0 &&
+ (dev->bus->number == bus && dev->devfn == devfn))
+ return dev;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(pci_get_bus_and_slot);
+#endif
+
#if defined(DRM_KMAP_ATOMIC_PROT_PFN)
#define drm_kmap_get_fixmap_pte(vaddr) \
pte_offset_kernel(pmd_offset(pud_offset(pgd_offset_k(vaddr), vaddr), (vaddr)), (vaddr))
@@ -756,6 +806,5 @@ void *kmap_atomic_prot_pfn(unsigned long pfn, enum km_type type,
}
EXPORT_SYMBOL(kmap_atomic_prot_pfn);
-
#endif
diff --git a/linux-core/drm_compat.h b/linux-core/drm_compat.h
index 30834f33..f35821bd 100644
--- a/linux-core/drm_compat.h
+++ b/linux-core/drm_compat.h
@@ -60,6 +60,13 @@
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
#undef DRM_IRQ_ARGS
#define DRM_IRQ_ARGS int irq, void *arg, struct pt_regs *regs
+
+typedef _Bool bool;
+enum {
+ false = 0,
+ true = 1
+};
+
#endif
#ifndef list_for_each_safe
@@ -152,7 +159,7 @@ static __inline__ void *kcalloc(size_t nmemb, size_t size, int flags)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
#define vmalloc_user(_size) ({void * tmp = vmalloc(_size); \
- if (tmp) memset(tmp, 0, size); \
+ if (tmp) memset(tmp, 0, _size); \
(tmp);})
#endif
@@ -325,7 +332,24 @@ void *idr_replace(struct idr *idp, void *ptr, int id);
#endif
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
-typedef _Bool bool;
+extern unsigned long round_jiffies_relative(unsigned long j);
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
+extern struct pci_dev * pci_get_bus_and_slot(unsigned int bus, unsigned int devfn);
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
+static inline int kobject_uevent_env(struct kobject *kobj,
+ enum kobject_action action,
+ char *envp[])
+{
+ return 0;
+}
+#endif
+
+#ifndef PM_EVENT_PRETHAW
+#define PM_EVENT_PRETHAW 3
#endif
diff --git a/linux-core/drm_context.c b/linux-core/drm_context.c
index 83ad291e..f3d69ff7 100644
--- a/linux-core/drm_context.c
+++ b/linux-core/drm_context.c
@@ -257,12 +257,13 @@ static int drm_context_switch(struct drm_device *dev, int old, int new)
* hardware lock is held, clears the drm_device::context_flag and wakes up
* drm_device::context_wait.
*/
-static int drm_context_switch_complete(struct drm_device *dev, int new)
+static int drm_context_switch_complete(struct drm_device *dev,
+ struct drm_file *file_priv, int new)
{
dev->last_context = new; /* PRE/POST: This is the _only_ writer. */
dev->last_switch = jiffies;
- if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+ if (!_DRM_LOCK_IS_HELD(file_priv->master->lock.hw_lock->lock)) {
DRM_ERROR("Lock isn't held after context switch\n");
}
@@ -421,7 +422,7 @@ int drm_newctx(struct drm_device *dev, void *data,
struct drm_ctx *ctx = data;
DRM_DEBUG("%d\n", ctx->handle);
- drm_context_switch_complete(dev, ctx->handle);
+ drm_context_switch_complete(dev, file_priv, ctx->handle);
return 0;
}
@@ -443,9 +444,6 @@ int drm_rmctx(struct drm_device *dev, void *data,
struct drm_ctx *ctx = data;
DRM_DEBUG("%d\n", ctx->handle);
- if (ctx->handle == DRM_KERNEL_CONTEXT + 1) {
- file_priv->remove_auth_on_close = 1;
- }
if (ctx->handle != DRM_KERNEL_CONTEXT) {
if (dev->driver->context_dtor)
dev->driver->context_dtor(dev, ctx->handle);
diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c
new file mode 100644
index 00000000..e1b371cc
--- /dev/null
+++ b/linux-core/drm_crtc.c
@@ -0,0 +1,2209 @@
+/*
+ * Copyright (c) 2006-2007 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (c) 2008 Red Hat Inc.
+ *
+ * DRM core CRTC related functions
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Keith Packard
+ * Eric Anholt <eric@anholt.net>
+ * Dave Airlie <airlied@linux.ie>
+ * Jesse Barnes <jesse.barnes@intel.com>
+ */
+#include <linux/list.h>
+#include "drm.h"
+#include "drmP.h"
+#include "drm_crtc.h"
+
+struct drm_prop_enum_list {
+ int type;
+ char *name;
+};
+
+/*
+ * Global properties
+ */
+static struct drm_prop_enum_list drm_dpms_enum_list[] =
+{ { DPMSModeOn, "On" },
+ { DPMSModeStandby, "Standby" },
+ { DPMSModeSuspend, "Suspend" },
+ { DPMSModeOff, "Off" }
+};
+
+char *drm_get_dpms_name(int val)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(drm_dpms_enum_list); i++)
+ if (drm_dpms_enum_list[i].type == val)
+ return drm_dpms_enum_list[i].name;
+
+ return "unknown";
+}
+
+static struct drm_prop_enum_list drm_connector_enum_list[] =
+{ { DRM_MODE_CONNECTOR_Unknown, "Unknown" },
+ { DRM_MODE_CONNECTOR_VGA, "VGA" },
+ { DRM_MODE_CONNECTOR_DVII, "DVI-I" },
+ { DRM_MODE_CONNECTOR_DVID, "DVI-D" },
+ { DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
+ { DRM_MODE_CONNECTOR_Composite, "Composite" },
+ { DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO" },
+ { DRM_MODE_CONNECTOR_LVDS, "LVDS" },
+ { DRM_MODE_CONNECTOR_Component, "Component" },
+ { DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN" },
+ { DRM_MODE_CONNECTOR_DisplayPort, "DisplayPort" },
+ { DRM_MODE_CONNECTOR_HDMIA, "HDMI Type A" },
+ { DRM_MODE_CONNECTOR_HDMIB, "HDMI Type B" },
+};
+static struct drm_prop_enum_list drm_encoder_enum_list[] =
+{ { DRM_MODE_ENCODER_NONE, "None" },
+ { DRM_MODE_ENCODER_DAC, "DAC" },
+ { DRM_MODE_ENCODER_TMDS, "TMDS" },
+ { DRM_MODE_ENCODER_LVDS, "LVDS" },
+ { DRM_MODE_ENCODER_TVDAC, "TV" },
+};
+
+char *drm_get_encoder_name(struct drm_encoder *encoder)
+{
+ static char buf[32];
+
+ snprintf(buf, 32, "%s-%d", drm_encoder_enum_list[encoder->encoder_type].name,
+ encoder->base.id);
+ return buf;
+}
+
+char *drm_get_connector_name(struct drm_connector *connector)
+{
+ static char buf[32];
+
+ snprintf(buf, 32, "%s-%d", drm_connector_enum_list[connector->connector_type].name,
+ connector->connector_type_id);
+ return buf;
+}
+
+char *drm_get_connector_status_name(enum drm_connector_status status)
+{
+ if (status == connector_status_connected)
+ return "connected";
+ else if (status == connector_status_disconnected)
+ return "disconnected";
+ else
+ return "unknown";
+}
+
+/**
+ * drm_idr_get - allocate a new identifier
+ * @dev: DRM device
+ * @ptr: object pointer, used to generate unique ID
+ *
+ * LOCKING:
+ * Caller must hold DRM mode_config lock.
+ *
+ * Create a unique identifier based on @ptr in @dev's identifier space. Used
+ * for tracking modes, CRTCs and connectors.
+ *
+ * RETURNS:
+ * New unique (relative to other objects in @dev) integer identifier for the
+ * object.
+ */
+static int drm_mode_object_get(struct drm_device *dev, struct drm_mode_object *obj, uint32_t obj_type)
+{
+ int new_id = 0;
+ int ret;
+again:
+ if (idr_pre_get(&dev->mode_config.crtc_idr, GFP_KERNEL) == 0) {
+ DRM_ERROR("Ran out memory getting a mode number\n");
+ return -EINVAL;
+ }
+
+ ret = idr_get_new_above(&dev->mode_config.crtc_idr, obj, 1, &new_id);
+ if (ret == -EAGAIN)
+ goto again;
+
+ obj->id = new_id;
+ obj->type = obj_type;
+ return 0;
+}
+
+/**
+ * drm_mode_object_put - free an identifer
+ * @dev: DRM device
+ * @id: ID to free
+ *
+ * LOCKING:
+ * Caller must hold DRM mode_config lock.
+ *
+ * Free @id from @dev's unique identifier pool.
+ */
+static void drm_mode_object_put(struct drm_device *dev, struct drm_mode_object *object)
+{
+ idr_remove(&dev->mode_config.crtc_idr, object->id);
+}
+
+static void *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type)
+{
+ struct drm_mode_object *obj;
+
+ obj = idr_find(&dev->mode_config.crtc_idr, id);
+ if (!obj || (obj->type != type) || (obj->id != id))
+ return NULL;
+
+ return obj;
+}
+
+/**
+ * drm_crtc_from_fb - find the CRTC structure associated with an fb
+ * @dev: DRM device
+ * @fb: framebuffer in question
+ *
+ * LOCKING:
+ * Caller must hold mode_config lock.
+ *
+ * Find CRTC in the mode_config structure that matches @fb.
+ *
+ * RETURNS:
+ * Pointer to the CRTC or NULL if it wasn't found.
+ */
+struct drm_crtc *drm_crtc_from_fb(struct drm_device *dev,
+ struct drm_framebuffer *fb)
+{
+ struct drm_crtc *crtc;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ if (crtc->fb == fb)
+ return crtc;
+ }
+ return NULL;
+}
+
+/**
+ * drm_framebuffer_create - create a new framebuffer object
+ * @dev: DRM device
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Creates a new framebuffer objects and adds it to @dev's DRM mode_config.
+ *
+ * RETURNS:
+ * Pointer to new framebuffer or NULL on error.
+ */
+struct drm_framebuffer *drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
+ const struct drm_framebuffer_funcs *funcs)
+{
+ drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB);
+ fb->dev = dev;
+ fb->funcs = funcs;
+ dev->mode_config.num_fb++;
+ list_add(&fb->head, &dev->mode_config.fb_list);
+
+ return fb;
+}
+EXPORT_SYMBOL(drm_framebuffer_init);
+
+/**
+ * drm_framebuffer_cleanup - remove a framebuffer object
+ * @fb: framebuffer to remove
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Scans all the CRTCs in @dev's mode_config. If they're using @fb, removes
+ * it, setting it to NULL.
+ */
+void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
+{
+ struct drm_device *dev = fb->dev;
+ struct drm_crtc *crtc;
+
+ /* remove from any CRTC */
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ if (crtc->fb == fb)
+ crtc->fb = NULL;
+ }
+
+ drm_mode_object_put(dev, &fb->base);
+ list_del(&fb->head);
+ dev->mode_config.num_fb--;
+}
+EXPORT_SYMBOL(drm_framebuffer_cleanup);
+
+/**
+ * drm_crtc_init - Initialise a new CRTC object
+ * @dev: DRM device
+ * @crtc: CRTC object to init
+ * @funcs: callbacks for the new CRTC
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Inits a new object created as base part of an driver crtc object.
+ */
+void drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
+ const struct drm_crtc_funcs *funcs)
+{
+ crtc->dev = dev;
+ crtc->funcs = funcs;
+
+ drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
+
+ list_add_tail(&crtc->head, &dev->mode_config.crtc_list);
+ dev->mode_config.num_crtc++;
+}
+EXPORT_SYMBOL(drm_crtc_init);
+
+/**
+ * drm_crtc_cleanup - Cleans up the core crtc usage.
+ * @crtc: CRTC to cleanup
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Cleanup @crtc. Removes from drm modesetting space
+ * does NOT free object, caller does that.
+ */
+void drm_crtc_cleanup(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+
+ if (crtc->gamma_store) {
+ kfree(crtc->gamma_store);
+ crtc->gamma_store = NULL;
+ }
+
+ drm_mode_object_put(dev, &crtc->base);
+ list_del(&crtc->head);
+ dev->mode_config.num_crtc--;
+}
+EXPORT_SYMBOL(drm_crtc_cleanup);
+
+/**
+ * drm_mode_probed_add - add a mode to the specified connector's probed mode list
+ * @connector: connector the new mode
+ * @mode: mode data
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Add @mode to @connector's mode list for later use.
+ */
+void drm_mode_probed_add(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ list_add(&mode->head, &connector->probed_modes);
+}
+EXPORT_SYMBOL(drm_mode_probed_add);
+
+/**
+ * drm_mode_remove - remove and free a mode
+ * @connector: connector list to modify
+ * @mode: mode to remove
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Remove @mode from @connector's mode list, then free it.
+ */
+void drm_mode_remove(struct drm_connector *connector, struct drm_display_mode *mode)
+{
+ list_del(&mode->head);
+ kfree(mode);
+}
+EXPORT_SYMBOL(drm_mode_remove);
+
+/**
+ * drm_connector_init - Init a preallocated connector
+ * @dev: DRM device
+ * @connector: the connector to init
+ * @funcs: callbacks for this connector
+ * @name: user visible name of the connector
+ *
+ * LOCKING:
+ * Caller must hold @dev's mode_config lock.
+ *
+ * Initialises a preallocated connector. Connectors should be
+ * subclassed as part of driver connector objects.
+ */
+void drm_connector_init(struct drm_device *dev,
+ struct drm_connector *connector,
+ const struct drm_connector_funcs *funcs,
+ int connector_type)
+{
+ connector->dev = dev;
+ connector->funcs = funcs;
+ drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR);
+ connector->connector_type = connector_type;
+ connector->connector_type_id = 1; /* TODO */
+ INIT_LIST_HEAD(&connector->user_modes);
+ INIT_LIST_HEAD(&connector->probed_modes);
+ INIT_LIST_HEAD(&connector->modes);
+ connector->edid_blob_ptr = NULL;
+ /* randr_connector? */
+ /* connector_set_monitor(connector)? */
+ /* check for connector_ignored(connector)? */
+
+ mutex_lock(&dev->mode_config.mutex);
+ list_add_tail(&connector->head, &dev->mode_config.connector_list);
+ dev->mode_config.num_connector++;
+
+ drm_connector_attach_property(connector, dev->mode_config.edid_property, 0);
+
+ drm_connector_attach_property(connector, dev->mode_config.dpms_property, 0);
+
+ mutex_unlock(&dev->mode_config.mutex);
+}
+EXPORT_SYMBOL(drm_connector_init);
+
+/**
+ * drm_connector_cleanup - cleans up an initialised connector
+ * @connector: connector to cleanup
+ *
+ * LOCKING:
+ * Caller must hold @dev's mode_config lock.
+ *
+ * Cleans up the connector but doesn't free the object.
+ */
+void drm_connector_cleanup(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_display_mode *mode, *t;
+
+ list_for_each_entry_safe(mode, t, &connector->probed_modes, head)
+ drm_mode_remove(connector, mode);
+
+ list_for_each_entry_safe(mode, t, &connector->modes, head)
+ drm_mode_remove(connector, mode);
+
+ list_for_each_entry_safe(mode, t, &connector->user_modes, head)
+ drm_mode_remove(connector, mode);
+
+ mutex_lock(&dev->mode_config.mutex);
+ drm_mode_object_put(dev, &connector->base);
+ list_del(&connector->head);
+ mutex_unlock(&dev->mode_config.mutex);
+}
+EXPORT_SYMBOL(drm_connector_cleanup);
+
+void drm_encoder_init(struct drm_device *dev,
+ struct drm_encoder *encoder,
+ const struct drm_encoder_funcs *funcs,
+ int encoder_type)
+{
+ encoder->dev = dev;
+
+ drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
+ encoder->encoder_type = encoder_type;
+ encoder->funcs = funcs;
+
+ mutex_lock(&dev->mode_config.mutex);
+ list_add_tail(&encoder->head, &dev->mode_config.encoder_list);
+ dev->mode_config.num_encoder++;
+
+ mutex_unlock(&dev->mode_config.mutex);
+}
+EXPORT_SYMBOL(drm_encoder_init);
+
+void drm_encoder_cleanup(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ mutex_lock(&dev->mode_config.mutex);
+ drm_mode_object_put(dev, &encoder->base);
+ list_del(&encoder->head);
+ mutex_unlock(&dev->mode_config.mutex);
+}
+EXPORT_SYMBOL(drm_encoder_cleanup);
+
+/**
+ * drm_mode_create - create a new display mode
+ * @dev: DRM device
+ *
+ * LOCKING:
+ * None.
+ *
+ * Create a new drm_display_mode, give it an ID, and return it.
+ *
+ * RETURNS:
+ * Pointer to new mode on success, NULL on error.
+ */
+struct drm_display_mode *drm_mode_create(struct drm_device *dev)
+{
+ struct drm_display_mode *nmode;
+
+ nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL);
+ if (!nmode)
+ return NULL;
+
+ drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE);
+ return nmode;
+}
+EXPORT_SYMBOL(drm_mode_create);
+
+/**
+ * drm_mode_destroy - remove a mode
+ * @dev: DRM device
+ * @mode: mode to remove
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Free @mode's unique identifier, then free it.
+ */
+void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode)
+{
+ drm_mode_object_put(dev, &mode->base);
+
+ kfree(mode);
+}
+EXPORT_SYMBOL(drm_mode_destroy);
+
+static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
+{
+ int i;
+
+ /*
+ * Standard properties (apply to all connectors)
+ */
+ dev->mode_config.edid_property =
+ drm_property_create(dev, DRM_MODE_PROP_BLOB | DRM_MODE_PROP_IMMUTABLE,
+ "EDID", 0);
+
+ dev->mode_config.dpms_property =
+ drm_property_create(dev, DRM_MODE_PROP_ENUM,
+ "DPMS", ARRAY_SIZE(drm_dpms_enum_list));
+ for (i = 0; i < ARRAY_SIZE(drm_dpms_enum_list); i++)
+ drm_property_add_enum(dev->mode_config.dpms_property, i, drm_dpms_enum_list[i].type, drm_dpms_enum_list[i].name);
+
+ return 0;
+}
+
+/**
+ * drm_create_tv_properties - create TV specific connector properties
+ * @dev: DRM device
+ * @num_modes: number of different TV formats (modes) supported
+ * @modes: array of pointers to strings containing name of each format
+ *
+ * Called by a driver's TV initialization routine, this function creates
+ * the TV specific connector properties for a given device. Caller is
+ * responsible for allocating a list of format names and passing them to
+ * this routine.
+ */
+bool drm_create_tv_properties(struct drm_device *dev, int num_modes,
+ char *modes[])
+{
+ int i;
+
+ dev->mode_config.tv_left_margin_property =
+ drm_property_create(dev, DRM_MODE_PROP_RANGE |
+ DRM_MODE_PROP_IMMUTABLE,
+ "left margin", 2);
+ dev->mode_config.tv_left_margin_property->values[0] = 0;
+ dev->mode_config.tv_left_margin_property->values[1] = 100;
+
+ dev->mode_config.tv_right_margin_property =
+ drm_property_create(dev, DRM_MODE_PROP_RANGE,
+ "right margin", 2);
+ dev->mode_config.tv_right_margin_property->values[0] = 0;
+ dev->mode_config.tv_right_margin_property->values[1] = 100;
+
+ dev->mode_config.tv_top_margin_property =
+ drm_property_create(dev, DRM_MODE_PROP_RANGE,
+ "top margin", 2);
+ dev->mode_config.tv_top_margin_property->values[0] = 0;
+ dev->mode_config.tv_top_margin_property->values[1] = 100;
+
+ dev->mode_config.tv_bottom_margin_property =
+ drm_property_create(dev, DRM_MODE_PROP_RANGE,
+ "bottom margin", 2);
+ dev->mode_config.tv_bottom_margin_property->values[0] = 0;
+ dev->mode_config.tv_bottom_margin_property->values[1] = 100;
+
+ dev->mode_config.tv_mode_property =
+ drm_property_create(dev, DRM_MODE_PROP_ENUM,
+ "mode", num_modes);
+ for (i = 0; i < num_modes; i++)
+ drm_property_add_enum(dev->mode_config.tv_mode_property, i,
+ i, modes[i]);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_create_tv_properties);
+
+/**
+ * drm_mode_config_init - initialize DRM mode_configuration structure
+ * @dev: DRM device
+ *
+ * LOCKING:
+ * None, should happen single threaded at init time.
+ *
+ * Initialize @dev's mode_config structure, used for tracking the graphics
+ * configuration of @dev.
+ */
+void drm_mode_config_init(struct drm_device *dev)
+{
+ mutex_init(&dev->mode_config.mutex);
+ INIT_LIST_HEAD(&dev->mode_config.fb_list);
+ INIT_LIST_HEAD(&dev->mode_config.fb_kernel_list);
+ INIT_LIST_HEAD(&dev->mode_config.crtc_list);
+ INIT_LIST_HEAD(&dev->mode_config.connector_list);
+ INIT_LIST_HEAD(&dev->mode_config.encoder_list);
+ INIT_LIST_HEAD(&dev->mode_config.property_list);
+ INIT_LIST_HEAD(&dev->mode_config.property_blob_list);
+ idr_init(&dev->mode_config.crtc_idr);
+
+ drm_mode_create_standard_connector_properties(dev);
+
+ /* Just to be sure */
+ dev->mode_config.current_generation = 0;
+ dev->mode_config.num_fb = 0;
+ dev->mode_config.num_connector = 0;
+ dev->mode_config.num_crtc = 0;
+ dev->mode_config.num_encoder = 0;
+ dev->mode_config.hotplug_counter = 0;
+}
+EXPORT_SYMBOL(drm_mode_config_init);
+
+int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *group)
+{
+ uint32_t total_objects = 0;
+
+ total_objects += dev->mode_config.num_crtc;
+ total_objects += dev->mode_config.num_connector;
+ total_objects += dev->mode_config.num_encoder;
+
+ group->id_list = kzalloc(total_objects * sizeof(uint32_t), GFP_KERNEL);
+ if (!group->id_list)
+ return -ENOMEM;
+
+ group->num_crtcs = 0;
+ group->num_connectors = 0;
+ group->num_encoders = 0;
+ return 0;
+}
+
+int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group)
+{
+ struct drm_crtc *crtc;
+ struct drm_encoder *encoder;
+ struct drm_connector *connector;
+
+ if (drm_mode_group_init(dev, group))
+ return -ENOMEM;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+ group->id_list[group->num_crtcs++] = crtc->base.id;
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
+ group->id_list[group->num_crtcs + group->num_encoders++] = encoder->base.id;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+ group->id_list[group->num_crtcs + group->num_encoders + group->num_connectors++] = connector->base.id;
+
+ return 0;
+}
+
+/**
+ * drm_mode_config_cleanup - free up DRM mode_config info
+ * @dev: DRM device
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Free up all the connectors and CRTCs associated with this DRM device, then
+ * free up the framebuffers and associated buffer objects.
+ *
+ * FIXME: cleanup any dangling user buffer objects too
+ */
+void drm_mode_config_cleanup(struct drm_device *dev)
+{
+ struct drm_connector *connector, *ot;
+ struct drm_crtc *crtc, *ct;
+ struct drm_encoder *encoder, *enct;
+ struct drm_framebuffer *fb, *fbt;
+ struct drm_property *property, *pt;
+
+ list_for_each_entry_safe(encoder, enct, &dev->mode_config.encoder_list, head) {
+ encoder->funcs->destroy(encoder);
+ }
+
+ list_for_each_entry_safe(connector, ot, &dev->mode_config.connector_list, head) {
+ connector->funcs->destroy(connector);
+ }
+
+ list_for_each_entry_safe(property, pt, &dev->mode_config.property_list, head) {
+ drm_property_destroy(dev, property);
+ }
+
+ list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) {
+ fb->funcs->destroy(fb);
+ }
+
+ list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) {
+ crtc->funcs->destroy(crtc);
+ }
+
+}
+EXPORT_SYMBOL(drm_mode_config_cleanup);
+
+int drm_mode_hotplug_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_hotplug *arg = data;
+
+ arg->counter = dev->mode_config.hotplug_counter;
+
+ return 0;
+}
+
+/**
+ * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo
+ * @out: drm_mode_modeinfo struct to return to the user
+ * @in: drm_display_mode to use
+ *
+ * LOCKING:
+ * None.
+ *
+ * Convert a drm_display_mode into a drm_mode_modeinfo structure to return to
+ * the user.
+ */
+void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, struct drm_display_mode *in)
+{
+ out->clock = in->clock;
+ out->hdisplay = in->hdisplay;
+ out->hsync_start = in->hsync_start;
+ out->hsync_end = in->hsync_end;
+ out->htotal = in->htotal;
+ out->hskew = in->hskew;
+ out->vdisplay = in->vdisplay;
+ out->vsync_start = in->vsync_start;
+ out->vsync_end = in->vsync_end;
+ out->vtotal = in->vtotal;
+ out->vscan = in->vscan;
+ out->vrefresh = in->vrefresh;
+ out->flags = in->flags;
+ out->type = in->type;
+ strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN);
+ out->name[DRM_DISPLAY_MODE_LEN-1] = 0;
+}
+
+/**
+ * drm_crtc_convert_to_umode - convert a modeinfo into a drm_display_mode
+ * @out: drm_display_mode to return to the user
+ * @in: drm_mode_modeinfo to use
+ *
+ * LOCKING:
+ * None.
+ *
+ * Convert a drmo_mode_modeinfo into a drm_display_mode structure to return to
+ * the caller.
+ */
+void drm_crtc_convert_umode(struct drm_display_mode *out, struct drm_mode_modeinfo *in)
+{
+ out->clock = in->clock;
+ out->hdisplay = in->hdisplay;
+ out->hsync_start = in->hsync_start;
+ out->hsync_end = in->hsync_end;
+ out->htotal = in->htotal;
+ out->hskew = in->hskew;
+ out->vdisplay = in->vdisplay;
+ out->vsync_start = in->vsync_start;
+ out->vsync_end = in->vsync_end;
+ out->vtotal = in->vtotal;
+ out->vscan = in->vscan;
+ out->vrefresh = in->vrefresh;
+ out->flags = in->flags;
+ out->type = in->type;
+ strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN);
+ out->name[DRM_DISPLAY_MODE_LEN-1] = 0;
+}
+
+/**
+ * drm_mode_getresources - get graphics configuration
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * LOCKING:
+ * Takes mode config lock.
+ *
+ * Construct a set of configuration description structures and return
+ * them to the user, including CRTC, connector and framebuffer configuration.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_getresources(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_card_res *card_res = data;
+ struct list_head *lh;
+ struct drm_framebuffer *fb;
+ struct drm_connector *connector;
+ struct drm_crtc *crtc;
+ struct drm_encoder *encoder;
+ int ret = 0;
+ int connector_count = 0;
+ int crtc_count = 0;
+ int fb_count = 0;
+ int encoder_count = 0;
+ int copied = 0, i;
+ uint32_t __user *fb_id;
+ uint32_t __user *crtc_id;
+ uint32_t __user *connector_id;
+ uint32_t __user *encoder_id;
+ struct drm_mode_group *mode_group;
+
+ mutex_lock(&dev->mode_config.mutex);
+
+ /* for the non-control nodes we need to limit the list of resources by IDs in the
+ group list for this node */
+ list_for_each(lh, &file_priv->fbs)
+ fb_count++;
+
+ mode_group = &file_priv->master->minor->mode_group;
+ if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
+
+ list_for_each(lh, &dev->mode_config.crtc_list)
+ crtc_count++;
+
+ list_for_each(lh, &dev->mode_config.connector_list)
+ connector_count++;
+
+ list_for_each(lh, &dev->mode_config.encoder_list)
+ encoder_count++;
+ } else {
+
+ crtc_count = mode_group->num_crtcs;
+ connector_count = mode_group->num_connectors;
+ encoder_count = mode_group->num_encoders;
+ }
+
+ card_res->max_height = dev->mode_config.max_height;
+ card_res->min_height = dev->mode_config.min_height;
+ card_res->max_width = dev->mode_config.max_width;
+ card_res->min_width = dev->mode_config.min_width;
+ card_res->generation = dev->mode_config.current_generation;
+
+ /* handle this in 4 parts */
+ /* FBs */
+ if (card_res->count_fbs >= fb_count) {
+ copied = 0;
+ fb_id = (uint32_t *)(unsigned long)card_res->fb_id_ptr;
+ list_for_each_entry(fb, &file_priv->fbs, head) {
+ if (put_user(fb->base.id, fb_id + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ }
+ card_res->count_fbs = fb_count;
+
+ /* CRTCs */
+ if (card_res->count_crtcs >= crtc_count) {
+ copied = 0;
+ crtc_id = (uint32_t *)(unsigned long)card_res->crtc_id_ptr;
+ if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ DRM_DEBUG("CRTC ID is %d\n", crtc->base.id);
+ if (put_user(crtc->base.id, crtc_id + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ } else {
+ for (i = 0; i < mode_group->num_crtcs; i++) {
+ if (put_user(mode_group->id_list[i], crtc_id + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ }
+ }
+ card_res->count_crtcs = crtc_count;
+
+ /* Encoders */
+ if (card_res->count_encoders >= encoder_count) {
+ copied = 0;
+ encoder_id = (uint32_t *)(unsigned long)card_res->encoder_id_ptr;
+ if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list,
+ head) {
+ DRM_DEBUG("ENCODER ID is %d\n", encoder->base.id);
+ if (put_user(encoder->base.id, encoder_id + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ } else {
+ for (i = mode_group->num_crtcs; i < mode_group->num_crtcs + mode_group->num_encoders; i++) {
+ if (put_user(mode_group->id_list[i], encoder_id + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+
+ }
+ }
+ card_res->count_encoders = encoder_count;
+
+ /* Connectors */
+ if (card_res->count_connectors >= connector_count) {
+ copied = 0;
+ connector_id = (uint32_t *)(unsigned long)card_res->connector_id_ptr;
+ if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
+ list_for_each_entry(connector, &dev->mode_config.connector_list,
+ head) {
+ DRM_DEBUG("CONNECTOR ID is %d\n", connector->base.id);
+ if (put_user(connector->base.id, connector_id + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ } else {
+ int start = mode_group->num_crtcs + mode_group->num_encoders;
+ for (i = start; i < start + mode_group->num_connectors; i++) {
+ if (put_user(mode_group->id_list[i], connector_id + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ }
+ }
+ card_res->count_connectors = connector_count;
+
+
+
+ DRM_DEBUG("Counted %d %d %d\n", card_res->count_crtcs,
+ card_res->count_connectors, card_res->count_encoders);
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+/**
+ * drm_mode_getcrtc - get CRTC configuration
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * LOCKING:
+ * Caller? (FIXME)
+ *
+ * Construct a CRTC configuration structure to return to the user.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_getcrtc(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_crtc *crtc_resp = data;
+ struct drm_crtc *crtc;
+ struct drm_mode_object *obj;
+ int ret = 0;
+
+ mutex_lock(&dev->mode_config.mutex);
+
+ obj = drm_mode_object_find(dev, crtc_resp->crtc_id, DRM_MODE_OBJECT_CRTC);
+ if (!obj) {
+ ret = -EINVAL;
+ goto out;
+ }
+ crtc = obj_to_crtc(obj);
+
+ crtc_resp->x = crtc->x;
+ crtc_resp->y = crtc->y;
+ crtc_resp->gamma_size = crtc->gamma_size;
+ crtc_resp->generation = dev->mode_config.current_generation;
+ if (crtc->fb)
+ crtc_resp->fb_id = crtc->fb->base.id;
+ else
+ crtc_resp->fb_id = 0;
+
+ crtc_resp->connectors = 0;
+ if (crtc->enabled) {
+
+ drm_crtc_convert_to_umode(&crtc_resp->mode, &crtc->mode);
+ crtc_resp->mode_valid = 1;
+
+ } else {
+ crtc_resp->mode_valid = 0;
+ }
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+/**
+ * drm_mode_getconnector - get connector configuration
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * LOCKING:
+ * Caller? (FIXME)
+ *
+ * Construct a connector configuration structure to return to the user.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_getconnector(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_get_connector *out_resp = data;
+ struct drm_mode_object *obj;
+ struct drm_connector *connector;
+ struct drm_display_mode *mode;
+ int mode_count = 0;
+ int props_count = 0;
+ int encoders_count = 0;
+ int ret = 0;
+ int copied = 0;
+ int i;
+ struct drm_mode_modeinfo u_mode;
+ struct drm_mode_modeinfo __user *mode_ptr;
+ uint32_t __user *prop_ptr;
+ uint64_t __user *prop_values;
+ uint32_t __user *encoder_ptr;
+
+ memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo));
+
+ DRM_DEBUG("connector id %d:\n", out_resp->connector);
+
+ mutex_lock(&dev->mode_config.mutex);
+
+ obj = drm_mode_object_find(dev, out_resp->connector, DRM_MODE_OBJECT_CONNECTOR);
+ if (!obj) {
+ ret = -EINVAL;
+ goto out;
+ }
+ connector = obj_to_connector(obj);
+
+ list_for_each_entry(mode, &connector->modes, head)
+ mode_count++;
+
+ for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) {
+ if (connector->property_ids[i] != 0) {
+ props_count++;
+ }
+ }
+
+ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+ if (connector->encoder_ids[i] != 0) {
+ encoders_count++;
+ }
+ }
+
+ if (out_resp->count_modes == 0) {
+ connector->funcs->fill_modes(connector, dev->mode_config.max_width, dev->mode_config.max_height);
+ }
+
+ out_resp->generation = dev->mode_config.current_generation;
+ out_resp->connector_type = connector->connector_type;
+ out_resp->connector_type_id = connector->connector_type_id;
+ out_resp->mm_width = connector->display_info.width_mm;
+ out_resp->mm_height = connector->display_info.height_mm;
+ out_resp->subpixel = connector->display_info.subpixel_order;
+ out_resp->connection = connector->status;
+ if (connector->encoder)
+ out_resp->encoder = connector->encoder->base.id;
+ else
+ out_resp->encoder = 0;
+
+ if ((out_resp->count_modes >= mode_count) && mode_count) {
+ copied = 0;
+ mode_ptr = (struct drm_mode_modeinfo *)(unsigned long)out_resp->modes_ptr;
+ list_for_each_entry(mode, &connector->modes, head) {
+ drm_crtc_convert_to_umode(&u_mode, mode);
+ if (copy_to_user(mode_ptr + copied,
+ &u_mode, sizeof(u_mode))) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+
+ }
+ }
+ out_resp->count_modes = mode_count;
+
+ if ((out_resp->count_props >= props_count) && props_count) {
+ copied = 0;
+ prop_ptr = (uint32_t *)(unsigned long)(out_resp->props_ptr);
+ prop_values = (uint64_t *)(unsigned long)(out_resp->prop_values_ptr);
+ for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) {
+ if (connector->property_ids[i] != 0) {
+ if (put_user(connector->property_ids[i], prop_ptr + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ if (put_user(connector->property_values[i], prop_values + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ }
+ }
+ out_resp->count_props = props_count;
+
+ if ((out_resp->count_encoders >= encoders_count) && encoders_count) {
+ copied = 0;
+ encoder_ptr = (uint32_t *)(unsigned long)(out_resp->encoders_ptr);
+ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+ if (connector->encoder_ids[i] != 0) {
+ if (put_user(connector->encoder_ids[i], encoder_ptr + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ }
+ }
+ out_resp->count_encoders = encoders_count;
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+int drm_mode_getencoder(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_get_encoder *enc_resp = data;
+ struct drm_mode_object *obj;
+ struct drm_encoder *encoder;
+ int ret = 0;
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, enc_resp->encoder_id, DRM_MODE_OBJECT_ENCODER);
+ if (!obj) {
+ ret = -EINVAL;
+ goto out;
+ }
+ encoder = obj_to_encoder(obj);
+
+ if (encoder->crtc)
+ enc_resp->crtc = encoder->crtc->base.id;
+ else
+ enc_resp->crtc = 0;
+ enc_resp->generation = dev->mode_config.current_generation;
+ enc_resp->encoder_type = encoder->encoder_type;
+ enc_resp->encoder_id = encoder->base.id;
+ enc_resp->crtcs = encoder->possible_crtcs;
+ enc_resp->clones = encoder->possible_clones;
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+/**
+ * drm_mode_setcrtc - set CRTC configuration
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * LOCKING:
+ * Caller? (FIXME)
+ *
+ * Build a new CRTC configuration based on user request.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_setcrtc(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_crtc *crtc_req = data;
+ struct drm_mode_object *obj;
+ struct drm_crtc *crtc, *crtcfb;
+ struct drm_connector **connector_set = NULL, *connector;
+ struct drm_framebuffer *fb = NULL;
+ struct drm_display_mode *mode = NULL;
+ struct drm_mode_set set;
+ uint32_t __user *set_connectors_ptr;
+ int ret = 0;
+ int i;
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, crtc_req->crtc_id, DRM_MODE_OBJECT_CRTC);
+ if (!obj) {
+ DRM_DEBUG("Unknown CRTC ID %d\n", crtc_req->crtc_id);
+ ret = -EINVAL;
+ goto out;
+ }
+ crtc = obj_to_crtc(obj);
+
+ if (crtc_req->mode_valid) {
+ /* If we have a mode we need a framebuffer. */
+ /* If we pass -1, set the mode with the currently bound fb */
+ if (crtc_req->fb_id == -1) {
+ list_for_each_entry(crtcfb, &dev->mode_config.crtc_list, head) {
+ if (crtcfb == crtc) {
+ DRM_DEBUG("Using current fb for setmode\n");
+ fb = crtc->fb;
+ }
+ }
+ } else {
+ obj = drm_mode_object_find(dev, crtc_req->fb_id, DRM_MODE_OBJECT_FB);
+ if (!obj) {
+ DRM_DEBUG("Unknown FB ID%d\n", crtc_req->fb_id);
+ ret = -EINVAL;
+ goto out;
+ }
+ fb = obj_to_fb(obj);
+ }
+
+ mode = drm_mode_create(dev);
+ drm_crtc_convert_umode(mode, &crtc_req->mode);
+ drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
+ }
+
+ if (crtc_req->count_connectors == 0 && mode) {
+ DRM_DEBUG("Count connectors is 0 but mode set\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (crtc_req->count_connectors > 0 && !mode && !fb) {
+ DRM_DEBUG("Count connectors is %d but no mode or fb set\n", crtc_req->count_connectors);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (crtc_req->count_connectors > 0) {
+ u32 out_id;
+ /* Maybe we should check that count_connectors is a sensible value. */
+ connector_set = kmalloc(crtc_req->count_connectors *
+ sizeof(struct drm_connector *), GFP_KERNEL);
+ if (!connector_set) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < crtc_req->count_connectors; i++) {
+ set_connectors_ptr = (uint32_t *)(unsigned long)crtc_req->set_connectors_ptr;
+ if (get_user(out_id, &set_connectors_ptr[i])) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ obj = drm_mode_object_find(dev, out_id, DRM_MODE_OBJECT_CONNECTOR);
+ if (!obj) {
+ DRM_DEBUG("Connector id %d unknown\n", out_id);
+ ret = -EINVAL;
+ goto out;
+ }
+ connector = obj_to_connector(obj);
+
+ connector_set[i] = connector;
+ }
+ }
+
+ set.crtc = crtc;
+ set.x = crtc_req->x;
+ set.y = crtc_req->y;
+ set.mode = mode;
+ set.connectors = connector_set;
+ set.num_connectors = crtc_req->count_connectors;
+ set.fb =fb;
+ ret = crtc->funcs->set_config(&set);
+
+out:
+ kfree(connector_set);
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+int drm_mode_cursor_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_cursor *req = data;
+ struct drm_mode_object *obj;
+ struct drm_crtc *crtc;
+ int ret = 0;
+
+ DRM_DEBUG("\n");
+
+ if (!req->flags) {
+ DRM_ERROR("no operation set\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, req->crtc, DRM_MODE_OBJECT_CRTC);
+ if (!obj) {
+ DRM_DEBUG("Unknown CRTC ID %d\n", req->crtc);
+ ret = -EINVAL;
+ goto out;
+ }
+ crtc = obj_to_crtc(obj);
+
+ if (req->flags & DRM_MODE_CURSOR_BO) {
+ /* Turn of the cursor if handle is 0 */
+ if (crtc->funcs->cursor_set) {
+ ret = crtc->funcs->cursor_set(crtc, req->handle, req->width, req->height);
+ } else {
+ DRM_ERROR("crtc does not support cursor\n");
+ ret = -EFAULT;
+ goto out;
+ }
+ }
+
+ if (req->flags & DRM_MODE_CURSOR_MOVE) {
+ if (crtc->funcs->cursor_move) {
+ ret = crtc->funcs->cursor_move(crtc, req->x, req->y);
+ } else {
+ DRM_ERROR("crtc does not support cursor\n");
+ ret = -EFAULT;
+ goto out;
+ }
+ }
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+/**
+ * drm_mode_addfb - add an FB to the graphics configuration
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * LOCKING:
+ * Takes mode config lock.
+ *
+ * Add a new FB to the specified CRTC, given a user request.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_addfb(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_fb_cmd *r = data;
+ struct drm_mode_config *config = &dev->mode_config;
+ struct drm_framebuffer *fb;
+ int ret = 0;
+
+ if ((config->min_width > r->width) || (r->width > config->max_width)) {
+ DRM_ERROR("mode new framebuffer width not within limits\n");
+ return -EINVAL;
+ }
+ if ((config->min_height > r->height) || (r->height > config->max_height)) {
+ DRM_ERROR("mode new framebuffer height not within limits\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&dev->mode_config.mutex);
+
+ /* TODO check buffer is sufficently large */
+ /* TODO setup destructor callback */
+
+ fb = dev->mode_config.funcs->fb_create(dev, file_priv, r);
+ if (!fb) {
+ DRM_ERROR("could not create framebuffer\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ r->buffer_id = fb->base.id;
+ list_add(&fb->filp_head, &file_priv->fbs);
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+/**
+ * drm_mode_rmfb - remove an FB from the configuration
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * LOCKING:
+ * Takes mode config lock.
+ *
+ * Remove the FB specified by the user.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_rmfb(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_object *obj;
+ struct drm_framebuffer *fb = NULL;
+ struct drm_framebuffer *fbl = NULL;
+ uint32_t *id = data;
+ int ret = 0;
+ int found = 0;
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB);
+ /* TODO check that we realy get a framebuffer back. */
+ if (!obj) {
+ DRM_ERROR("mode invalid framebuffer id\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ fb = obj_to_fb(obj);
+
+ list_for_each_entry(fbl, &file_priv->fbs, filp_head)
+ if (fb == fbl)
+ found = 1;
+
+ if (!found) {
+ DRM_ERROR("tried to remove a fb that we didn't own\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* TODO release all crtc connected to the framebuffer */
+ /* TODO unhock the destructor from the buffer object */
+
+ list_del(&fb->filp_head);
+ fb->funcs->destroy(fb);
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+/**
+ * drm_mode_getfb - get FB info
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * LOCKING:
+ * Caller? (FIXME)
+ *
+ * Lookup the FB given its ID and return info about it.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_getfb(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_fb_cmd *r = data;
+ struct drm_mode_object *obj;
+ struct drm_framebuffer *fb;
+ int ret = 0;
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, r->buffer_id, DRM_MODE_OBJECT_FB);
+ if (!obj) {
+ DRM_ERROR("invalid framebuffer id\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ fb = obj_to_fb(obj);
+
+ r->height = fb->height;
+ r->width = fb->width;
+ r->depth = fb->depth;
+ r->bpp = fb->bits_per_pixel;
+ r->handle = fb->mm_handle;
+ r->pitch = fb->pitch;
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+/**
+ * drm_fb_release - remove and free the FBs on this file
+ * @filp: file * from the ioctl
+ *
+ * LOCKING:
+ * Takes mode config lock.
+ *
+ * Destroy all the FBs associated with @filp.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+void drm_fb_release(struct file *filp)
+{
+ struct drm_file *priv = filp->private_data;
+ struct drm_device *dev = priv->minor->dev;
+ struct drm_framebuffer *fb, *tfb;
+
+ mutex_lock(&dev->mode_config.mutex);
+ list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) {
+ list_del(&fb->filp_head);
+ fb->funcs->destroy(fb);
+ }
+ mutex_unlock(&dev->mode_config.mutex);
+}
+
+/*
+ *
+ */
+
+static int drm_mode_attachmode(struct drm_device *dev,
+ struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ int ret = 0;
+
+ list_add_tail(&mode->head, &connector->user_modes);
+ return ret;
+}
+
+int drm_mode_attachmode_crtc(struct drm_device *dev, struct drm_crtc *crtc,
+ struct drm_display_mode *mode)
+{
+ struct drm_connector *connector;
+ int ret = 0;
+ struct drm_display_mode *dup_mode;
+ int need_dup = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (!connector->encoder)
+ break;
+ if (connector->encoder->crtc == crtc) {
+ if (need_dup)
+ dup_mode = drm_mode_duplicate(dev, mode);
+ else
+ dup_mode = mode;
+ ret = drm_mode_attachmode(dev, connector, dup_mode);
+ if (ret)
+ return ret;
+ need_dup = 1;
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL(drm_mode_attachmode_crtc);
+
+static int drm_mode_detachmode(struct drm_device *dev,
+ struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ int found = 0;
+ int ret = 0;
+ struct drm_display_mode *match_mode, *t;
+
+ list_for_each_entry_safe(match_mode, t, &connector->user_modes, head) {
+ if (drm_mode_equal(match_mode, mode)) {
+ list_del(&match_mode->head);
+ drm_mode_destroy(dev, match_mode);
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ ret = -EINVAL;
+
+ return ret;
+}
+
+int drm_mode_detachmode_crtc(struct drm_device *dev, struct drm_display_mode *mode)
+{
+ struct drm_connector *connector;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ drm_mode_detachmode(dev, connector, mode);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(drm_mode_detachmode_crtc);
+
+/**
+ * drm_fb_attachmode - Attach a user mode to an connector
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * This attaches a user specified mode to an connector.
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_attachmode_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_mode_cmd *mode_cmd = data;
+ struct drm_connector *connector;
+ struct drm_display_mode *mode;
+ struct drm_mode_object *obj;
+ struct drm_mode_modeinfo *umode = &mode_cmd->mode;
+ int ret = 0;
+
+ mutex_lock(&dev->mode_config.mutex);
+
+ obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR);
+ if (!obj) {
+ ret = -EINVAL;
+ goto out;
+ }
+ connector = obj_to_connector(obj);
+
+ mode = drm_mode_create(dev);
+ if (!mode) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ drm_crtc_convert_umode(mode, umode);
+
+ ret = drm_mode_attachmode(dev, connector, mode);
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+
+/**
+ * drm_fb_detachmode - Detach a user specified mode from an connector
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_detachmode_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_object *obj;
+ struct drm_mode_mode_cmd *mode_cmd = data;
+ struct drm_connector *connector;
+ struct drm_display_mode mode;
+ struct drm_mode_modeinfo *umode = &mode_cmd->mode;
+ int ret = 0;
+
+ mutex_lock(&dev->mode_config.mutex);
+
+ obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR);
+ if (!obj) {
+ ret = -EINVAL;
+ goto out;
+ }
+ connector = obj_to_connector(obj);
+
+ drm_crtc_convert_umode(&mode, umode);
+ ret = drm_mode_detachmode(dev, connector, &mode);
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+struct drm_property *drm_property_create(struct drm_device *dev, int flags,
+ const char *name, int num_values)
+{
+ struct drm_property *property = NULL;
+
+ property = kzalloc(sizeof(struct drm_property), GFP_KERNEL);
+ if (!property)
+ return NULL;
+
+ if (num_values) {
+ property->values = kzalloc(sizeof(uint64_t)*num_values, GFP_KERNEL);
+ if (!property->values)
+ goto fail;
+ }
+
+ drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY);
+ property->flags = flags;
+ property->num_values = num_values;
+ INIT_LIST_HEAD(&property->enum_blob_list);
+
+ if (name)
+ strncpy(property->name, name, DRM_PROP_NAME_LEN);
+
+ list_add_tail(&property->head, &dev->mode_config.property_list);
+ return property;
+fail:
+ kfree(property);
+ return NULL;
+}
+EXPORT_SYMBOL(drm_property_create);
+
+int drm_property_add_enum(struct drm_property *property, int index,
+ uint64_t value, const char *name)
+{
+ struct drm_property_enum *prop_enum;
+
+ if (!(property->flags & DRM_MODE_PROP_ENUM))
+ return -EINVAL;
+
+ if (!list_empty(&property->enum_blob_list)) {
+ list_for_each_entry(prop_enum, &property->enum_blob_list, head) {
+ if (prop_enum->value == value) {
+ strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN);
+ prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0';
+ return 0;
+ }
+ }
+ }
+
+ prop_enum = kzalloc(sizeof(struct drm_property_enum), GFP_KERNEL);
+ if (!prop_enum)
+ return -ENOMEM;
+
+ strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN);
+ prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0';
+ prop_enum->value = value;
+
+ property->values[index] = value;
+ list_add_tail(&prop_enum->head, &property->enum_blob_list);
+ return 0;
+}
+EXPORT_SYMBOL(drm_property_add_enum);
+
+void drm_property_destroy(struct drm_device *dev, struct drm_property *property)
+{
+ struct drm_property_enum *prop_enum, *pt;
+
+ list_for_each_entry_safe(prop_enum, pt, &property->enum_blob_list, head) {
+ list_del(&prop_enum->head);
+ kfree(prop_enum);
+ }
+
+ if (property->num_values)
+ kfree(property->values);
+ drm_mode_object_put(dev, &property->base);
+ list_del(&property->head);
+ kfree(property);
+}
+EXPORT_SYMBOL(drm_property_destroy);
+
+int drm_connector_attach_property(struct drm_connector *connector,
+ struct drm_property *property, uint64_t init_val)
+{
+ int i;
+
+ for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) {
+ if (connector->property_ids[i] == 0) {
+ connector->property_ids[i] = property->base.id;
+ connector->property_values[i] = init_val;
+ break;
+ }
+ }
+
+ if (i == DRM_CONNECTOR_MAX_PROPERTY)
+ return -EINVAL;
+ return 0;
+}
+EXPORT_SYMBOL(drm_connector_attach_property);
+
+int drm_connector_property_set_value(struct drm_connector *connector,
+ struct drm_property *property, uint64_t value)
+{
+ int i;
+
+ for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) {
+ if (connector->property_ids[i] == property->base.id) {
+ connector->property_values[i] = value;
+ break;
+ }
+ }
+
+ if (i == DRM_CONNECTOR_MAX_PROPERTY)
+ return -EINVAL;
+ return 0;
+}
+EXPORT_SYMBOL(drm_connector_property_set_value);
+
+int drm_connector_property_get_value(struct drm_connector *connector,
+ struct drm_property *property, uint64_t *val)
+{
+ int i;
+
+ for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) {
+ if (connector->property_ids[i] == property->base.id) {
+ *val = connector->property_values[i];
+ break;
+ }
+ }
+
+ if (i == DRM_CONNECTOR_MAX_PROPERTY)
+ return -EINVAL;
+ return 0;
+}
+EXPORT_SYMBOL(drm_connector_property_get_value);
+
+int drm_mode_getproperty_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_object *obj;
+ struct drm_mode_get_property *out_resp = data;
+ struct drm_property *property;
+ int enum_count = 0;
+ int blob_count = 0;
+ int value_count = 0;
+ int ret = 0, i;
+ int copied;
+ struct drm_property_enum *prop_enum;
+ struct drm_mode_property_enum __user *enum_ptr;
+ struct drm_property_blob *prop_blob;
+ uint32_t *blob_id_ptr;
+ uint64_t __user *values_ptr;
+ uint32_t __user *blob_length_ptr;
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY);
+ if (!obj) {
+ ret = -EINVAL;
+ goto done;
+ }
+ property = obj_to_property(obj);
+
+ if (property->flags & DRM_MODE_PROP_ENUM) {
+ list_for_each_entry(prop_enum, &property->enum_blob_list, head)
+ enum_count++;
+ } else if (property->flags & DRM_MODE_PROP_BLOB) {
+ list_for_each_entry(prop_blob, &property->enum_blob_list, head)
+ blob_count++;
+ }
+
+ value_count = property->num_values;
+
+ strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN);
+ out_resp->name[DRM_PROP_NAME_LEN-1] = 0;
+ out_resp->flags = property->flags;
+
+ if ((out_resp->count_values >= value_count) && value_count) {
+ values_ptr = (uint64_t *)(unsigned long)out_resp->values_ptr;
+ for (i = 0; i < value_count; i++) {
+ if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) {
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+ }
+ out_resp->count_values = value_count;
+
+ if (property->flags & DRM_MODE_PROP_ENUM) {
+
+ if ((out_resp->count_enum_blobs >= enum_count) && enum_count) {
+ copied = 0;
+ enum_ptr = (struct drm_mode_property_enum *)(unsigned long)out_resp->enum_blob_ptr;
+ list_for_each_entry(prop_enum, &property->enum_blob_list, head) {
+
+ if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (copy_to_user(&enum_ptr[copied].name,
+ &prop_enum->name, DRM_PROP_NAME_LEN)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ copied++;
+ }
+ }
+ out_resp->count_enum_blobs = enum_count;
+ }
+
+ if (property->flags & DRM_MODE_PROP_BLOB) {
+ if ((out_resp->count_enum_blobs >= blob_count) && blob_count) {
+ copied = 0;
+ blob_id_ptr = (uint32_t *)(unsigned long)out_resp->enum_blob_ptr;
+ blob_length_ptr = (uint32_t *)(unsigned long)out_resp->values_ptr;
+
+ list_for_each_entry(prop_blob, &property->enum_blob_list, head) {
+ if (put_user(prop_blob->base.id, blob_id_ptr + copied)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (put_user(prop_blob->length, blob_length_ptr + copied)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ copied++;
+ }
+ }
+ out_resp->count_enum_blobs = enum_count;
+ }
+done:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, int length,
+ void *data)
+{
+ struct drm_property_blob *blob;
+
+ if (!length || !data)
+ return NULL;
+
+ blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL);
+ if (!blob)
+ return NULL;
+
+ blob->data = (void *)((char *)blob + sizeof(struct drm_property_blob));
+ blob->length = length;
+
+ memcpy(blob->data, data, length);
+
+ drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB);
+
+ list_add_tail(&blob->head, &dev->mode_config.property_blob_list);
+ return blob;
+}
+
+static void drm_property_destroy_blob(struct drm_device *dev,
+ struct drm_property_blob *blob)
+{
+ drm_mode_object_put(dev, &blob->base);
+ list_del(&blob->head);
+ kfree(blob);
+}
+
+int drm_mode_getblob_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_object *obj;
+ struct drm_mode_get_blob *out_resp = data;
+ struct drm_property_blob *blob;
+ int ret = 0;
+ void *blob_ptr;
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_CONNECTOR);
+ if (!obj) {
+ ret = -EINVAL;
+ goto done;
+ }
+ blob = obj_to_blob(obj);
+
+ if (out_resp->length == blob->length) {
+ blob_ptr = (void *)(unsigned long)out_resp->data;
+ if (copy_to_user(blob_ptr, blob->data, blob->length)){
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+ out_resp->length = blob->length;
+
+done:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+int drm_mode_connector_update_edid_property(struct drm_connector *connector, struct edid *edid)
+{
+ struct drm_device *dev = connector->dev;
+ int ret = 0;
+ if (connector->edid_blob_ptr)
+ drm_property_destroy_blob(dev, connector->edid_blob_ptr);
+
+ connector->edid_blob_ptr = drm_property_create_blob(connector->dev, 128, edid);
+
+ ret = drm_connector_property_set_value(connector, dev->mode_config.edid_property, connector->edid_blob_ptr->base.id);
+ return ret;
+}
+EXPORT_SYMBOL(drm_mode_connector_update_edid_property);
+
+int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_connector_set_property *out_resp = data;
+ struct drm_mode_object *obj;
+ struct drm_property *property;
+ struct drm_connector *connector;
+ int ret = -EINVAL;
+ int i;
+
+ mutex_lock(&dev->mode_config.mutex);
+
+ obj = drm_mode_object_find(dev, out_resp->connector_id, DRM_MODE_OBJECT_CONNECTOR);
+ if (!obj) {
+ goto out;
+ }
+ connector = obj_to_connector(obj);
+
+ for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) {
+ if (connector->property_ids[i] == out_resp->prop_id)
+ break;
+ }
+
+ if (i == DRM_CONNECTOR_MAX_PROPERTY) {
+ goto out;
+ }
+
+ obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY);
+ if (!obj) {
+ goto out;
+ }
+ property = obj_to_property(obj);
+
+ if (property->flags & DRM_MODE_PROP_IMMUTABLE)
+ goto out;
+
+ if (property->flags & DRM_MODE_PROP_RANGE) {
+ if (out_resp->value < property->values[0])
+ goto out;
+
+ if (out_resp->value > property->values[1])
+ goto out;
+ } else {
+ int found = 0;
+ for (i = 0; i < property->num_values; i++) {
+ if (property->values[i] == out_resp->value) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ goto out;
+ }
+ }
+
+ if (connector->funcs->set_property)
+ ret = connector->funcs->set_property(connector, property, out_resp->value);
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+
+int drm_mode_replacefb(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_fb_cmd *r = data;
+ struct drm_mode_object *obj;
+ struct drm_framebuffer *fb;
+ int found = 0;
+ struct drm_framebuffer *fbl = NULL;
+ int ret = 0;
+
+ /* right replace the current bo attached to this fb with a new bo */
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, r->buffer_id, DRM_MODE_OBJECT_FB);
+ if (!obj) {
+ ret = -EINVAL;
+ goto out;
+ }
+ fb = obj_to_fb(obj);
+
+ list_for_each_entry(fbl, &file_priv->fbs, filp_head)
+ if (fb == fbl)
+ found = 1;
+
+ if (!found) {
+ DRM_ERROR("tried to replace an fb we didn't own\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (dev->mode_config.funcs->resize_fb)
+ ret = dev->mode_config.funcs->resize_fb(dev, file_priv, fb, r);
+ else
+ ret = -EINVAL;
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+
+}
+
+int drm_mode_connector_attach_encoder(struct drm_connector *connector,
+ struct drm_encoder *encoder)
+{
+ int i;
+
+ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+ if (connector->encoder_ids[i] == 0) {
+ connector->encoder_ids[i] = encoder->base.id;
+ return 0;
+ }
+ }
+ return -ENOMEM;
+}
+EXPORT_SYMBOL(drm_mode_connector_attach_encoder);
+
+void drm_mode_connector_detach_encoder(struct drm_connector *connector,
+ struct drm_encoder *encoder)
+{
+ int i;
+ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+ if (connector->encoder_ids[i] == encoder->base.id) {
+ connector->encoder_ids[i] = 0;
+ if (connector->encoder == encoder)
+ connector->encoder = NULL;
+ break;
+ }
+ }
+}
+EXPORT_SYMBOL(drm_mode_connector_detach_encoder);
+
+bool drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
+ int gamma_size)
+{
+ crtc->gamma_size = gamma_size;
+
+ crtc->gamma_store = kzalloc(gamma_size * sizeof(uint16_t) * 3, GFP_KERNEL);
+ if (!crtc->gamma_store) {
+ crtc->gamma_size = 0;
+ return false;
+ }
+
+ return true;
+}
+EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size);
+
+int drm_mode_gamma_set_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_crtc_lut *crtc_lut = data;
+ struct drm_mode_object *obj;
+ struct drm_crtc *crtc;
+ void *r_base, *g_base, *b_base;
+ int size;
+ int ret = 0;
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
+ if (!obj) {
+ ret = -EINVAL;
+ goto out;
+ }
+ crtc = obj_to_crtc(obj);
+
+ /* memcpy into gamma store */
+ if (crtc_lut->gamma_size != crtc->gamma_size) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ size = crtc_lut->gamma_size * (sizeof(uint16_t));
+ r_base = crtc->gamma_store;
+ if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ g_base = r_base + size;
+ if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ b_base = g_base + size;
+ if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, crtc->gamma_size);
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+
+}
+
+int drm_mode_gamma_get_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_crtc_lut *crtc_lut = data;
+ struct drm_mode_object *obj;
+ struct drm_crtc *crtc;
+ void *r_base, *g_base, *b_base;
+ int size;
+ int ret = 0;
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
+ if (!obj) {
+ ret = -EINVAL;
+ goto out;
+ }
+ crtc = obj_to_crtc(obj);
+
+ /* memcpy into gamma store */
+ if (crtc_lut->gamma_size != crtc->gamma_size) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ size = crtc_lut->gamma_size * (sizeof(uint16_t));
+ r_base = crtc->gamma_store;
+ if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ g_base = r_base + size;
+ if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ b_base = g_base + size;
+ if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) {
+ ret = -EFAULT;
+ goto out;
+ }
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h
new file mode 100644
index 00000000..2b577b93
--- /dev/null
+++ b/linux-core/drm_crtc.h
@@ -0,0 +1,733 @@
+/*
+ * Copyright © 2006 Keith Packard
+ * Copyright © 2007 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ */
+#ifndef __DRM_CRTC_H__
+#define __DRM_CRTC_H__
+
+#include <linux/i2c.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/idr.h>
+
+#include <linux/fb.h>
+
+struct drm_device;
+struct drm_mode_set;
+struct drm_framebuffer;
+
+
+#define DRM_MODE_OBJECT_CRTC 0xcccccccc
+#define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0
+#define DRM_MODE_OBJECT_ENCODER 0xe0e0e0e0
+#define DRM_MODE_OBJECT_MODE 0xdededede
+#define DRM_MODE_OBJECT_PROPERTY 0xb0b0b0b0
+#define DRM_MODE_OBJECT_FB 0xfbfbfbfb
+#define DRM_MODE_OBJECT_BLOB 0xbbbbbbbb
+
+struct drm_mode_object {
+ uint32_t id;
+ uint32_t type;
+};
+
+/*
+ * Note on terminology: here, for brevity and convenience, we refer to connector
+ * control chips as 'CRTCs'. They can control any type of connector, VGA, LVDS,
+ * DVI, etc. And 'screen' refers to the whole of the visible display, which
+ * may span multiple monitors (and therefore multiple CRTC and connector
+ * structures).
+ */
+
+enum drm_mode_status {
+ MODE_OK = 0, /* Mode OK */
+ MODE_HSYNC, /* hsync out of range */
+ MODE_VSYNC, /* vsync out of range */
+ MODE_H_ILLEGAL, /* mode has illegal horizontal timings */
+ MODE_V_ILLEGAL, /* mode has illegal horizontal timings */
+ MODE_BAD_WIDTH, /* requires an unsupported linepitch */
+ MODE_NOMODE, /* no mode with a maching name */
+ MODE_NO_INTERLACE, /* interlaced mode not supported */
+ MODE_NO_DBLESCAN, /* doublescan mode not supported */
+ MODE_NO_VSCAN, /* multiscan mode not supported */
+ MODE_MEM, /* insufficient video memory */
+ MODE_VIRTUAL_X, /* mode width too large for specified virtual size */
+ MODE_VIRTUAL_Y, /* mode height too large for specified virtual size */
+ MODE_MEM_VIRT, /* insufficient video memory given virtual size */
+ MODE_NOCLOCK, /* no fixed clock available */
+ MODE_CLOCK_HIGH, /* clock required is too high */
+ MODE_CLOCK_LOW, /* clock required is too low */
+ MODE_CLOCK_RANGE, /* clock/mode isn't in a ClockRange */
+ MODE_BAD_HVALUE, /* horizontal timing was out of range */
+ MODE_BAD_VVALUE, /* vertical timing was out of range */
+ MODE_BAD_VSCAN, /* VScan value out of range */
+ MODE_HSYNC_NARROW, /* horizontal sync too narrow */
+ MODE_HSYNC_WIDE, /* horizontal sync too wide */
+ MODE_HBLANK_NARROW, /* horizontal blanking too narrow */
+ MODE_HBLANK_WIDE, /* horizontal blanking too wide */
+ MODE_VSYNC_NARROW, /* vertical sync too narrow */
+ MODE_VSYNC_WIDE, /* vertical sync too wide */
+ MODE_VBLANK_NARROW, /* vertical blanking too narrow */
+ MODE_VBLANK_WIDE, /* vertical blanking too wide */
+ MODE_PANEL, /* exceeds panel dimensions */
+ MODE_INTERLACE_WIDTH, /* width too large for interlaced mode */
+ MODE_ONE_WIDTH, /* only one width is supported */
+ MODE_ONE_HEIGHT, /* only one height is supported */
+ MODE_ONE_SIZE, /* only one resolution is supported */
+ MODE_NO_REDUCED, /* monitor doesn't accept reduced blanking */
+ MODE_UNVERIFIED = -3, /* mode needs to reverified */
+ MODE_BAD = -2, /* unspecified reason */
+ MODE_ERROR = -1 /* error condition */
+};
+
+#define DRM_MODE_TYPE_CLOCK_CRTC_C (DRM_MODE_TYPE_CLOCK_C | \
+ DRM_MODE_TYPE_CRTC_C)
+
+#define DRM_MODE(nm, t, c, hd, hss, hse, ht, hsk, vd, vss, vse, vt, vs, f) \
+ .name = nm, .status = 0, .type = (t), .clock = (c), \
+ .hdisplay = (hd), .hsync_start = (hss), .hsync_end = (hse), \
+ .htotal = (ht), .hskew = (hsk), .vdisplay = (vd), \
+ .vsync_start = (vss), .vsync_end = (vse), .vtotal = (vt), \
+ .vscan = (vs), .flags = (f), .vrefresh = 0
+
+struct drm_display_mode {
+ /* Header */
+ struct list_head head;
+ struct drm_mode_object base;
+
+ char name[DRM_DISPLAY_MODE_LEN];
+
+ int connector_count;
+ enum drm_mode_status status;
+ int type;
+
+ /* Proposed mode values */
+ int clock;
+ int hdisplay;
+ int hsync_start;
+ int hsync_end;
+ int htotal;
+ int hskew;
+ int vdisplay;
+ int vsync_start;
+ int vsync_end;
+ int vtotal;
+ int vscan;
+ unsigned int flags;
+
+ /* Addressable image size (may be 0 for projectors, etc.) */
+ int width_mm;
+ int height_mm;
+
+ /* Actual mode we give to hw */
+ int clock_index;
+ int synth_clock;
+ int crtc_hdisplay;
+ int crtc_hblank_start;
+ int crtc_hblank_end;
+ int crtc_hsync_start;
+ int crtc_hsync_end;
+ int crtc_htotal;
+ int crtc_hskew;
+ int crtc_vdisplay;
+ int crtc_vblank_start;
+ int crtc_vblank_end;
+ int crtc_vsync_start;
+ int crtc_vsync_end;
+ int crtc_vtotal;
+ int crtc_hadjusted;
+ int crtc_vadjusted;
+
+ /* Driver private mode info */
+ int private_size;
+ int *private;
+ int private_flags;
+
+ int vrefresh;
+ float hsync;
+};
+
+/* Video mode flags */
+#define V_PHSYNC (1<<0)
+#define V_NHSYNC (1<<1)
+#define V_PVSYNC (1<<2)
+#define V_NVSYNC (1<<3)
+#define V_INTERLACE (1<<4)
+#define V_DBLSCAN (1<<5)
+#define V_CSYNC (1<<6)
+#define V_PCSYNC (1<<7)
+#define V_NCSYNC (1<<8)
+#define V_HSKEW (1<<9) /* hskew provided */
+#define V_BCAST (1<<10)
+#define V_PIXMUX (1<<11)
+#define V_DBLCLK (1<<12)
+#define V_CLKDIV2 (1<<13)
+
+#define CRTC_INTERLACE_HALVE_V 0x1 /* halve V values for interlacing */
+
+#define DPMSModeOn 0
+#define DPMSModeStandby 1
+#define DPMSModeSuspend 2
+#define DPMSModeOff 3
+
+#define DRM_MODE_CONNECTOR_Unknown 0
+#define DRM_MODE_CONNECTOR_VGA 1
+#define DRM_MODE_CONNECTOR_DVII 2
+#define DRM_MODE_CONNECTOR_DVID 3
+#define DRM_MODE_CONNECTOR_DVIA 4
+#define DRM_MODE_CONNECTOR_Composite 5
+#define DRM_MODE_CONNECTOR_SVIDEO 6
+#define DRM_MODE_CONNECTOR_LVDS 7
+#define DRM_MODE_CONNECTOR_Component 8
+#define DRM_MODE_CONNECTOR_9PinDIN 9
+#define DRM_MODE_CONNECTOR_DisplayPort 10
+#define DRM_MODE_CONNECTOR_HDMIA 11
+#define DRM_MODE_CONNECTOR_HDMIB 12
+
+enum drm_connector_status {
+ connector_status_connected = 1,
+ connector_status_disconnected = 2,
+ connector_status_unknown = 3,
+};
+
+enum subpixel_order {
+ SubPixelUnknown = 0,
+ SubPixelHorizontalRGB,
+ SubPixelHorizontalBGR,
+ SubPixelVerticalRGB,
+ SubPixelVerticalBGR,
+ SubPixelNone,
+};
+
+
+/*
+ * Describes a given display (e.g. CRT or flat panel) and its limitations.
+ */
+struct drm_display_info {
+ char name[DRM_DISPLAY_INFO_LEN];
+ /* Input info */
+ bool serration_vsync;
+ bool sync_on_green;
+ bool composite_sync;
+ bool separate_syncs;
+ bool blank_to_black;
+ unsigned char video_level;
+ bool digital;
+ /* Physical size */
+ unsigned int width_mm;
+ unsigned int height_mm;
+
+ /* Display parameters */
+ unsigned char gamma; /* FIXME: storage format */
+ bool gtf_supported;
+ bool standard_color;
+ enum {
+ monochrome = 0,
+ rgb,
+ other,
+ unknown,
+ } display_type;
+ bool active_off_supported;
+ bool suspend_supported;
+ bool standby_supported;
+
+ /* Color info FIXME: storage format */
+ unsigned short redx, redy;
+ unsigned short greenx, greeny;
+ unsigned short bluex, bluey;
+ unsigned short whitex, whitey;
+
+ /* Clock limits FIXME: storage format */
+ unsigned int min_vfreq, max_vfreq;
+ unsigned int min_hfreq, max_hfreq;
+ unsigned int pixel_clock;
+
+ /* White point indices FIXME: storage format */
+ unsigned int wpx1, wpy1;
+ unsigned int wpgamma1;
+ unsigned int wpx2, wpy2;
+ unsigned int wpgamma2;
+
+ enum subpixel_order subpixel_order;
+
+ char *raw_edid; /* if any */
+};
+
+struct drm_framebuffer_funcs {
+ void (*destroy)(struct drm_framebuffer *framebuffer);
+};
+
+struct drm_framebuffer {
+ struct drm_device *dev;
+ struct list_head head;
+ struct drm_mode_object base;
+ const struct drm_framebuffer_funcs *funcs;
+ unsigned int pitch;
+ unsigned int width;
+ unsigned int height;
+ /* depth can be 15 or 16 */
+ unsigned int depth;
+ int bits_per_pixel;
+ int flags;
+ void *fbdev;
+ u32 pseudo_palette[17];
+ struct list_head filp_head;
+ uint32_t mm_handle;
+};
+
+struct drm_property_blob {
+ struct drm_mode_object base;
+ struct list_head head;
+ unsigned int length;
+ void *data;
+};
+
+struct drm_property_enum {
+ uint64_t value;
+ struct list_head head;
+ char name[DRM_PROP_NAME_LEN];
+};
+
+struct drm_property {
+ struct list_head head;
+ struct drm_mode_object base;
+ uint32_t flags;
+ char name[DRM_PROP_NAME_LEN];
+ uint32_t num_values;
+ uint64_t *values;
+
+ struct list_head enum_blob_list;
+};
+
+struct drm_crtc;
+struct drm_connector;
+struct drm_encoder;
+
+/**
+ * drm_crtc_funcs - control CRTCs for a given device
+ * @dpms: control display power levels
+ * @save: save CRTC state
+ * @resore: restore CRTC state
+ * @lock: lock the CRTC
+ * @unlock: unlock the CRTC
+ * @shadow_allocate: allocate shadow pixmap
+ * @shadow_create: create shadow pixmap for rotation support
+ * @shadow_destroy: free shadow pixmap
+ * @mode_fixup: fixup proposed mode
+ * @mode_set: set the desired mode on the CRTC
+ * @gamma_set: specify color ramp for CRTC
+ * @destroy: deinit and free object.
+ *
+ * The drm_crtc_funcs structure is the central CRTC management structure
+ * in the DRM. Each CRTC controls one or more connectors (note that the name
+ * CRTC is simply historical, a CRTC may control LVDS, VGA, DVI, TV out, etc.
+ * connectors, not just CRTs).
+ *
+ * Each driver is responsible for filling out this structure at startup time,
+ * in addition to providing other modesetting features, like i2c and DDC
+ * bus accessors.
+ */
+struct drm_crtc_funcs {
+ /* Save CRTC state */
+ void (*save)(struct drm_crtc *crtc); /* suspend? */
+ /* Restore CRTC state */
+ void (*restore)(struct drm_crtc *crtc); /* resume? */
+
+ /* cursor controls */
+ int (*cursor_set)(struct drm_crtc *crtc, uint32_t buffer_handle,
+ uint32_t width, uint32_t height);
+ int (*cursor_move)(struct drm_crtc *crtc, int x, int y);
+
+ /* Set gamma on the CRTC */
+ void (*gamma_set)(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,
+ uint32_t size);
+ /* Object destroy routine */
+ void (*destroy)(struct drm_crtc *crtc);
+
+ int (*set_config)(struct drm_mode_set *set);
+};
+
+/**
+ * drm_crtc - central CRTC control structure
+ * @enabled: is this CRTC enabled?
+ * @x: x position on screen
+ * @y: y position on screen
+ * @desired_mode: new desired mode
+ * @desired_x: desired x for desired_mode
+ * @desired_y: desired y for desired_mode
+ * @funcs: CRTC control functions
+ *
+ * Each CRTC may have one or more connectors associated with it. This structure
+ * allows the CRTC to be controlled.
+ */
+struct drm_crtc {
+ struct drm_device *dev;
+ struct list_head head;
+
+ struct drm_mode_object base;
+
+ /* framebuffer the connector is currently bound to */
+ struct drm_framebuffer *fb;
+
+ bool enabled;
+
+ struct drm_display_mode mode;
+
+ int x, y;
+ struct drm_display_mode *desired_mode;
+ int desired_x, desired_y;
+ const struct drm_crtc_funcs *funcs;
+
+ /* CRTC gamma size for reporting to userspace */
+ uint32_t gamma_size;
+ uint16_t *gamma_store;
+
+ /* if you are using the helper */
+ void *helper_private;
+};
+
+
+/**
+ * drm_connector_funcs - control connectors on a given device
+ * @dpms: set power state (see drm_crtc_funcs above)
+ * @save: save connector state
+ * @restore: restore connector state
+ * @mode_valid: is this mode valid on the given connector?
+ * @mode_fixup: try to fixup proposed mode for this connector
+ * @mode_set: set this mode
+ * @detect: is this connector active?
+ * @get_modes: get mode list for this connector
+ * @set_property: property for this connector may need update
+ * @destroy: make object go away
+ *
+ * Each CRTC may have one or more connectors attached to it. The functions
+ * below allow the core DRM code to control connectors, enumerate available modes,
+ * etc.
+ */
+struct drm_connector_funcs {
+ void (*dpms)(struct drm_connector *connector, int mode);
+ void (*save)(struct drm_connector *connector);
+ void (*restore)(struct drm_connector *connector);
+ enum drm_connector_status (*detect)(struct drm_connector *connector);
+ void (*fill_modes)(struct drm_connector *connector, uint32_t max_width, uint32_t max_height);
+ bool (*set_property)(struct drm_connector *connector, struct drm_property *property,
+ uint64_t val);
+ void (*destroy)(struct drm_connector *connector);
+};
+
+struct drm_encoder_funcs {
+ void (*destroy)(struct drm_encoder *encoder);
+};
+
+#define DRM_CONNECTOR_MAX_UMODES 16
+#define DRM_CONNECTOR_MAX_PROPERTY 16
+#define DRM_CONNECTOR_LEN 32
+#define DRM_CONNECTOR_MAX_ENCODER 2
+
+/**
+ * drm_encoder - central DRM encoder structure
+ */
+struct drm_encoder {
+ struct drm_device *dev;
+ struct list_head head;
+
+ struct drm_mode_object base;
+ int encoder_type;
+ uint32_t possible_crtcs;
+ uint32_t possible_clones;
+
+ struct drm_crtc *crtc;
+ const struct drm_encoder_funcs *funcs;
+ void *helper_private;
+};
+
+/**
+ * drm_connector - central DRM connector control structure
+ * @crtc: CRTC this connector is currently connected to, NULL if none
+ * @interlace_allowed: can this connector handle interlaced modes?
+ * @doublescan_allowed: can this connector handle doublescan?
+ * @available_modes: modes available on this connector (from get_modes() + user)
+ * @initial_x: initial x position for this connector
+ * @initial_y: initial y position for this connector
+ * @status: connector connected?
+ * @funcs: connector control functions
+ *
+ * Each connector may be connected to one or more CRTCs, or may be clonable by
+ * another connector if they can share a CRTC. Each connector also has a specific
+ * position in the broader display (referred to as a 'screen' though it could
+ * span multiple monitors).
+ */
+struct drm_connector {
+ struct drm_device *dev;
+ struct device kdev;
+ struct device_attribute *attr;
+ struct list_head head;
+
+ struct drm_mode_object base;
+
+ int connector_type;
+ int connector_type_id;
+ bool interlace_allowed;
+ bool doublescan_allowed;
+ struct list_head modes; /* list of modes on this connector */
+
+ int initial_x, initial_y;
+ enum drm_connector_status status;
+
+ /* these are modes added by probing with DDC or the BIOS */
+ struct list_head probed_modes;
+
+ struct drm_display_info display_info;
+ const struct drm_connector_funcs *funcs;
+
+ struct list_head user_modes;
+ struct drm_property_blob *edid_blob_ptr;
+ u32 property_ids[DRM_CONNECTOR_MAX_PROPERTY];
+ uint64_t property_values[DRM_CONNECTOR_MAX_PROPERTY];
+
+ void *helper_private;
+
+ uint32_t encoder_ids[DRM_CONNECTOR_MAX_ENCODER];
+ uint32_t force_encoder_id;
+ struct drm_encoder *encoder; /* currently active encoder */
+};
+
+/**
+ * struct drm_mode_set
+ *
+ * Represents a single crtc the connectors that it drives with what mode
+ * and from which framebuffer it scans out from.
+ *
+ * This is used to set modes.
+ */
+struct drm_mode_set {
+ struct list_head head;
+
+ struct drm_framebuffer *fb;
+ struct drm_crtc *crtc;
+ struct drm_display_mode *mode;
+
+ uint32_t x;
+ uint32_t y;
+
+ struct drm_connector **connectors;
+ size_t num_connectors;
+};
+
+/**
+ * struct drm_mode_config_funcs - configure CRTCs for a given screen layout
+ * @resize: adjust CRTCs as necessary for the proposed layout
+ *
+ * Currently only a resize hook is available. DRM will call back into the
+ * driver with a new screen width and height. If the driver can't support
+ * the proposed size, it can return false. Otherwise it should adjust
+ * the CRTC<->connector mappings as needed and update its view of the screen.
+ */
+struct drm_mode_config_funcs {
+ int (*resize_fb)(struct drm_device *dev, struct drm_file *file_priv, struct drm_framebuffer *fb, struct drm_mode_fb_cmd *mode_cmd);
+ struct drm_framebuffer *(*fb_create)(struct drm_device *dev, struct drm_file *file_priv, struct drm_mode_fb_cmd *mode_cmd);
+ int (*fb_changed)(struct drm_device *dev);
+};
+
+struct drm_mode_group {
+ uint32_t num_crtcs;
+ uint32_t num_encoders;
+ uint32_t num_connectors;
+
+ /* list of object IDs for this group */
+ uint32_t *id_list;
+};
+
+/**
+ * drm_mode_config - Mode configuration control structure
+ *
+ */
+struct drm_mode_config {
+ struct mutex mutex; /* protects configuration and IDR */
+ struct idr crtc_idr; /* use this idr for all IDs, fb, crtc, connector, modes - just makes life easier */
+ /* this is limited to one for now */
+ int num_fb;
+ struct list_head fb_list;
+ int num_connector;
+ struct list_head connector_list;
+ int num_encoder;
+ struct list_head encoder_list;
+
+ int num_crtc;
+ struct list_head crtc_list;
+
+ struct list_head property_list;
+
+ /* in-kernel framebuffers - hung of filp_head in drm_framebuffer */
+ struct list_head fb_kernel_list;
+
+ /* currently in use generation id */
+ int current_generation;
+
+ int min_width, min_height;
+ int max_width, max_height;
+ struct drm_mode_config_funcs *funcs;
+ unsigned long fb_base;
+
+ /* pointers to standard properties */
+ struct list_head property_blob_list;
+ struct drm_property *edid_property;
+ struct drm_property *dpms_property;
+
+ /* TV properties */
+ struct drm_property *tv_mode_property;
+ struct drm_property *tv_left_margin_property;
+ struct drm_property *tv_right_margin_property;
+ struct drm_property *tv_top_margin_property;
+ struct drm_property *tv_bottom_margin_property;
+
+ /* hotplug */
+ uint32_t hotplug_counter;
+};
+
+#define obj_to_crtc(x) container_of(x, struct drm_crtc, base)
+#define obj_to_connector(x) container_of(x, struct drm_connector, base)
+#define obj_to_encoder(x) container_of(x, struct drm_encoder, base)
+#define obj_to_mode(x) container_of(x, struct drm_display_mode, base)
+#define obj_to_fb(x) container_of(x, struct drm_framebuffer, base)
+#define obj_to_property(x) container_of(x, struct drm_property, base)
+#define obj_to_blob(x) container_of(x, struct drm_property_blob, base)
+
+
+extern void drm_crtc_init(struct drm_device *dev,
+ struct drm_crtc *crtc,
+ const struct drm_crtc_funcs *funcs);
+extern void drm_crtc_cleanup(struct drm_crtc *crtc);
+
+extern void drm_connector_init(struct drm_device *dev,
+ struct drm_connector *connector,
+ const struct drm_connector_funcs *funcs,
+ int connector_type);
+
+extern void drm_connector_cleanup(struct drm_connector *connector);
+
+extern void drm_encoder_init(struct drm_device *dev,
+ struct drm_encoder *encoder,
+ const struct drm_encoder_funcs *funcs,
+ int encoder_type);
+
+extern void drm_encoder_cleanup(struct drm_encoder *encoder);
+
+extern char *drm_get_connector_name(struct drm_connector *connector);
+extern char *drm_get_dpms_name(int val);
+extern void drm_fb_release(struct file *filp);
+extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group);
+extern struct edid *drm_get_edid(struct drm_connector *connector,
+ struct i2c_adapter *adapter);
+extern int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid);
+extern void drm_mode_probed_add(struct drm_connector *connector, struct drm_display_mode *mode);
+extern void drm_mode_remove(struct drm_connector *connector, struct drm_display_mode *mode);
+extern struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev,
+ struct drm_display_mode *mode);
+extern void drm_mode_debug_printmodeline(struct drm_display_mode *mode);
+extern void drm_mode_config_init(struct drm_device *dev);
+extern void drm_mode_config_cleanup(struct drm_device *dev);
+extern void drm_mode_set_name(struct drm_display_mode *mode);
+extern bool drm_mode_equal(struct drm_display_mode *mode1, struct drm_display_mode *mode2);
+
+/* for us by fb module */
+extern int drm_mode_attachmode_crtc(struct drm_device *dev,
+ struct drm_crtc *crtc,
+ struct drm_display_mode *mode);
+extern int drm_mode_detachmode_crtc(struct drm_device *dev, struct drm_display_mode *mode);
+
+extern struct drm_display_mode *drm_mode_create(struct drm_device *dev);
+extern void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode);
+extern void drm_mode_list_concat(struct list_head *head,
+ struct list_head *new);
+extern void drm_mode_validate_size(struct drm_device *dev,
+ struct list_head *mode_list,
+ int maxX, int maxY, int maxPitch);
+extern void drm_mode_prune_invalid(struct drm_device *dev,
+ struct list_head *mode_list, bool verbose);
+extern void drm_mode_sort(struct list_head *mode_list);
+extern int drm_mode_vrefresh(struct drm_display_mode *mode);
+extern void drm_mode_set_crtcinfo(struct drm_display_mode *p,
+ int adjust_flags);
+extern void drm_mode_connector_list_update(struct drm_connector *connector);
+extern int drm_mode_connector_update_edid_property(struct drm_connector *connector,
+ struct edid *edid);
+extern int drm_connector_property_set_value(struct drm_connector *connector,
+ struct drm_property *property,
+ uint64_t value);
+extern int drm_connector_property_get_value(struct drm_connector *connector,
+ struct drm_property *property,
+ uint64_t *value);
+extern struct drm_display_mode *drm_crtc_mode_create(struct drm_device *dev);
+extern void drm_framebuffer_set_object(struct drm_device *dev,
+ unsigned long handle);
+extern struct drm_framebuffer *drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
+ const struct drm_framebuffer_funcs *funcs);
+extern void drm_framebuffer_cleanup(struct drm_framebuffer *fb);
+extern int drmfb_probe(struct drm_device *dev, struct drm_crtc *crtc);
+extern int drmfb_remove(struct drm_device *dev, struct drm_framebuffer *fb);
+extern void drm_crtc_probe_connector_modes(struct drm_device *dev, int maxX, int maxY);
+extern bool drm_crtc_in_use(struct drm_crtc *crtc);
+
+extern int drm_connector_attach_property(struct drm_connector *connector,
+ struct drm_property *property, uint64_t init_val);
+extern struct drm_property *drm_property_create(struct drm_device *dev, int flags,
+ const char *name, int num_values);
+extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property);
+extern int drm_property_add_enum(struct drm_property *property, int index,
+ uint64_t value, const char *name);
+extern bool drm_create_tv_properties(struct drm_device *dev, int num_formats,
+ char *formats[]);
+extern char *drm_get_encoder_name(struct drm_encoder *encoder);
+
+extern int drm_mode_connector_attach_encoder(struct drm_connector *connector,
+ struct drm_encoder *encoder);
+extern void drm_mode_connector_detach_encoder(struct drm_connector *connector,
+ struct drm_encoder *encoder);
+extern bool drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
+ int gamma_size);
+/* IOCTLs */
+extern int drm_mode_getresources(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+
+extern int drm_mode_getcrtc(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_getconnector(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_setcrtc(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_cursor_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_addfb(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_rmfb(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_getfb(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_addmode_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_rmmode_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_attachmode_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_detachmode_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+
+extern int drm_mode_getproperty_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_getblob_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_hotplug_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_replacefb(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_getencoder(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_gamma_get_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_gamma_set_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+#endif /* __DRM_CRTC_H__ */
+
diff --git a/linux-core/drm_crtc_helper.c b/linux-core/drm_crtc_helper.c
new file mode 100644
index 00000000..bfddab99
--- /dev/null
+++ b/linux-core/drm_crtc_helper.c
@@ -0,0 +1,761 @@
+/* (c) 2006-2007 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ *
+ * DRM core CRTC related functions
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Keith Packard
+ * Eric Anholt <eric@anholt.net>
+ * Dave Airlie <airlied@linux.ie>
+ * Jesse Barnes <jesse.barnes@intel.com>
+ */
+
+#include "drmP.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+
+/*
+ * Detailed mode info for a standard 640x480@60Hz monitor
+ */
+static struct drm_display_mode std_mode[] = {
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DEFAULT, 25200, 640, 656,
+ 752, 800, 0, 480, 490, 492, 525, 0,
+ V_NHSYNC | V_NVSYNC) }, /* 640x480@60Hz */
+};
+
+/**
+ * drm_helper_probe_connector_modes - get complete set of display modes
+ * @dev: DRM device
+ * @maxX: max width for modes
+ * @maxY: max height for modes
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Based on @dev's mode_config layout, scan all the connectors and try to detect
+ * modes on them. Modes will first be added to the connector's probed_modes
+ * list, then culled (based on validity and the @maxX, @maxY parameters) and
+ * put into the normal modes list.
+ *
+ * Intended to be used either at bootup time or when major configuration
+ * changes have occurred.
+ *
+ * FIXME: take into account monitor limits
+ */
+void drm_helper_probe_single_connector_modes(struct drm_connector *connector, uint32_t maxX, uint32_t maxY)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_display_mode *mode, *t;
+ struct drm_connector_helper_funcs *connector_funcs = connector->helper_private;
+ int ret;
+
+ DRM_DEBUG("%s\n", drm_get_connector_name(connector));
+ /* set all modes to the unverified state */
+ list_for_each_entry_safe(mode, t, &connector->modes, head)
+ mode->status = MODE_UNVERIFIED;
+
+ connector->status = (*connector->funcs->detect)(connector);
+
+ if (connector->status == connector_status_disconnected) {
+ DRM_DEBUG("%s is disconnected\n", drm_get_connector_name(connector));
+ /* TODO set EDID to NULL */
+ return;
+ }
+
+ ret = (*connector_funcs->get_modes)(connector);
+
+ if (ret) {
+ drm_mode_connector_list_update(connector);
+ }
+
+ if (maxX && maxY)
+ drm_mode_validate_size(dev, &connector->modes, maxX,
+ maxY, 0);
+ list_for_each_entry_safe(mode, t, &connector->modes, head) {
+ if (mode->status == MODE_OK)
+ mode->status = (*connector_funcs->mode_valid)(connector,mode);
+ }
+
+
+ drm_mode_prune_invalid(dev, &connector->modes, TRUE);
+
+ if (list_empty(&connector->modes)) {
+ struct drm_display_mode *stdmode;
+
+ DRM_DEBUG("No valid modes on %s\n", drm_get_connector_name(connector));
+
+ /* Should we do this here ???
+ * When no valid EDID modes are available we end up
+ * here and bailed in the past, now we add a standard
+ * 640x480@60Hz mode and carry on.
+ */
+ stdmode = drm_mode_duplicate(dev, &std_mode[0]);
+ drm_mode_probed_add(connector, stdmode);
+ drm_mode_list_concat(&connector->probed_modes,
+ &connector->modes);
+
+ DRM_DEBUG("Adding standard 640x480 @ 60Hz to %s\n",
+ drm_get_connector_name(connector));
+ }
+
+ drm_mode_sort(&connector->modes);
+
+ DRM_DEBUG("Probed modes for %s\n", drm_get_connector_name(connector));
+ list_for_each_entry_safe(mode, t, &connector->modes, head) {
+ mode->vrefresh = drm_mode_vrefresh(mode);
+
+ drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
+ drm_mode_debug_printmodeline(mode);
+ }
+}
+EXPORT_SYMBOL(drm_helper_probe_single_connector_modes);
+
+void drm_helper_probe_connector_modes(struct drm_device *dev, uint32_t maxX, uint32_t maxY)
+{
+ struct drm_connector *connector;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ drm_helper_probe_single_connector_modes(connector, maxX, maxY);
+ }
+}
+EXPORT_SYMBOL(drm_helper_probe_connector_modes);
+
+
+/**
+ * drm_helper_crtc_in_use - check if a given CRTC is in a mode_config
+ * @crtc: CRTC to check
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Walk @crtc's DRM device's mode_config and see if it's in use.
+ *
+ * RETURNS:
+ * True if @crtc is part of the mode_config, false otherwise.
+ */
+bool drm_helper_crtc_in_use(struct drm_crtc *crtc)
+{
+ struct drm_encoder *encoder;
+ struct drm_device *dev = crtc->dev;
+ /* FIXME: Locking around list access? */
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
+ if (encoder->crtc == crtc)
+ return true;
+ return false;
+}
+EXPORT_SYMBOL(drm_helper_crtc_in_use);
+
+/**
+ * drm_disable_unused_functions - disable unused objects
+ * @dev: DRM device
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * If an connector or CRTC isn't part of @dev's mode_config, it can be disabled
+ * by calling its dpms function, which should power it off.
+ */
+void drm_helper_disable_unused_functions(struct drm_device *dev)
+{
+ struct drm_encoder *encoder;
+ struct drm_encoder_helper_funcs *encoder_funcs;
+ struct drm_crtc *crtc;
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ encoder_funcs = encoder->helper_private;
+ if (!encoder->crtc)
+ (*encoder_funcs->dpms)(encoder, DPMSModeOff);
+ }
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+ crtc->enabled = drm_helper_crtc_in_use(crtc);
+ if (!crtc->enabled) {
+ crtc_funcs->dpms(crtc, DPMSModeOff);
+ crtc->fb = NULL;
+ }
+ }
+}
+EXPORT_SYMBOL(drm_helper_disable_unused_functions);
+
+/**
+ * drm_pick_crtcs - pick crtcs for connector devices
+ * @dev: DRM device
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ */
+static void drm_pick_crtcs (struct drm_device *dev)
+{
+ int c, o, assigned;
+ struct drm_connector *connector, *connector_equal;
+ struct drm_encoder *encoder, *encoder_equal;
+ struct drm_crtc *crtc;
+ struct drm_display_mode *des_mode = NULL, *modes, *modes_equal;
+ struct drm_connector_helper_funcs *connector_funcs;
+ int found;
+
+ DRM_DEBUG("\n");
+ /* clean out all the encoder/crtc combos */
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ encoder->crtc = NULL;
+ }
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ connector_funcs = connector->helper_private;
+ connector->encoder = NULL;
+
+ /* Don't hook up connectors that are disconnected ??
+ *
+ * This is debateable. Do we want fixed /dev/fbX or
+ * dynamic on hotplug (need mode code for that though) ?
+ *
+ * If we don't hook up connectors now, then we only create
+ * /dev/fbX for the connector that's enabled, that's good as
+ * the users console will be on that connector.
+ *
+ * If we do hook up connectors that are disconnected now, then
+ * the user may end up having to muck about with the fbcon
+ * map flags to assign his console to the enabled connector. Ugh.
+ */
+ if (connector->status != connector_status_connected)
+ continue;
+
+ if (list_empty(&connector->modes))
+ continue;
+
+ des_mode = NULL;
+ found = 0;
+ list_for_each_entry(des_mode, &connector->modes, head) {
+ if (des_mode->type & DRM_MODE_TYPE_PREFERRED) {
+ found = 1;
+ break;
+ }
+ }
+
+ /* No preferred mode, let's just select the first available */
+ if (!found) {
+ des_mode = NULL;
+ list_for_each_entry(des_mode, &connector->modes, head) {
+ break;
+ }
+ }
+
+ encoder = connector_funcs->best_encoder(connector);
+ if (!encoder)
+ continue;
+
+ connector->encoder = encoder;
+
+ c = -1;
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ assigned = 0;
+
+ c++;
+ if ((encoder->possible_crtcs & (1 << c)) == 0)
+ continue;
+
+ list_for_each_entry(encoder_equal, &dev->mode_config.encoder_list, head) {
+ if (encoder->base.id == encoder_equal->base.id)
+ continue;
+
+ /* Find out if crtc has been assigned before */
+ if (encoder_equal->crtc == crtc)
+ assigned = 1;
+ }
+
+#if 1 /* continue for now */
+ if (assigned)
+ continue;
+#endif
+
+ o = -1;
+ list_for_each_entry(connector_equal, &dev->mode_config.connector_list, head) {
+ o++;
+ if (connector->base.id == connector_equal->base.id)
+ continue;
+
+ encoder_equal = connector_equal->encoder;
+
+ if (!encoder_equal)
+ continue;
+
+ list_for_each_entry(modes, &connector->modes, head) {
+ list_for_each_entry(modes_equal, &connector_equal->modes, head) {
+ if (drm_mode_equal (modes, modes_equal)) {
+ if ((encoder->possible_clones & encoder_equal->possible_clones) && (connector_equal->encoder->crtc == crtc)) {
+ printk("Cloning %s (0x%x) to %s (0x%x)\n",drm_get_connector_name(connector),encoder->possible_clones,drm_get_connector_name(connector_equal),encoder_equal->possible_clones);
+ des_mode = modes;
+ assigned = 0;
+ goto clone;
+ }
+ }
+ }
+ }
+ }
+
+clone:
+ /* crtc has been assigned skip it */
+ if (assigned)
+ continue;
+
+ /* Found a CRTC to attach to, do it ! */
+ encoder->crtc = crtc;
+ encoder->crtc->desired_mode = des_mode;
+ connector->initial_x = 0;
+ connector->initial_y = 0;
+ DRM_DEBUG("Desired mode for CRTC %d is 0x%x:%s\n",c,des_mode->base.id, des_mode->name);
+ break;
+ }
+ }
+}
+EXPORT_SYMBOL(drm_pick_crtcs);
+
+/**
+ * drm_crtc_set_mode - set a mode
+ * @crtc: CRTC to program
+ * @mode: mode to use
+ * @x: width of mode
+ * @y: height of mode
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Try to set @mode on @crtc. Give @crtc and its associated connectors a chance
+ * to fixup or reject the mode prior to trying to set it.
+ *
+ * RETURNS:
+ * True if the mode was set successfully, or false otherwise.
+ */
+bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode,
+ int x, int y)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_display_mode *adjusted_mode, saved_mode;
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+ struct drm_encoder_helper_funcs *encoder_funcs;
+ int saved_x, saved_y;
+ struct drm_encoder *encoder;
+ bool ret = true;
+
+ adjusted_mode = drm_mode_duplicate(dev, mode);
+
+ crtc->enabled = drm_helper_crtc_in_use(crtc);
+
+ if (!crtc->enabled)
+ return true;
+
+ saved_mode = crtc->mode;
+ saved_x = crtc->x;
+ saved_y = crtc->y;
+
+ /* Update crtc values up front so the driver can rely on them for mode
+ * setting.
+ */
+ crtc->mode = *mode;
+ crtc->x = x;
+ crtc->y = y;
+
+ if (drm_mode_equal(&saved_mode, &crtc->mode)) {
+ if (saved_x != crtc->x || saved_y != crtc->y) {
+ crtc_funcs->mode_set_base(crtc, crtc->x, crtc->y);
+ goto done;
+ }
+ }
+
+ /* Pass our mode to the connectors and the CRTC to give them a chance to
+ * adjust it according to limitations or connector properties, and also
+ * a chance to reject the mode entirely.
+ */
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+
+ if (encoder->crtc != crtc)
+ continue;
+ encoder_funcs = encoder->helper_private;
+ if (!(ret = encoder_funcs->mode_fixup(encoder, mode, adjusted_mode))) {
+ goto done;
+ }
+ }
+
+ if (!(ret = crtc_funcs->mode_fixup(crtc, mode, adjusted_mode))) {
+ goto done;
+ }
+
+ /* Prepare the encoders and CRTCs before setting the mode. */
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+
+ if (encoder->crtc != crtc)
+ continue;
+ encoder_funcs = encoder->helper_private;
+ /* Disable the encoders as the first thing we do. */
+ encoder_funcs->prepare(encoder);
+ }
+
+ crtc_funcs->prepare(crtc);
+
+ /* Set up the DPLL and any encoders state that needs to adjust or depend
+ * on the DPLL.
+ */
+ crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y);
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+
+ if (encoder->crtc != crtc)
+ continue;
+
+ DRM_INFO("%s: set mode %s %x\n", drm_get_encoder_name(encoder), mode->name, mode->base.id);
+ encoder_funcs = encoder->helper_private;
+ encoder_funcs->mode_set(encoder, mode, adjusted_mode);
+ }
+
+ /* Now, enable the clocks, plane, pipe, and connectors that we set up. */
+ crtc_funcs->commit(crtc);
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+
+ if (encoder->crtc != crtc)
+ continue;
+
+ encoder_funcs = encoder->helper_private;
+ encoder_funcs->commit(encoder);
+
+ }
+
+ /* XXX free adjustedmode */
+ drm_mode_destroy(dev, adjusted_mode);
+ /* TODO */
+// if (scrn->pScreen)
+// drm_crtc_set_screen_sub_pixel_order(dev);
+
+done:
+ if (!ret) {
+ crtc->mode = saved_mode;
+ crtc->x = saved_x;
+ crtc->y = saved_y;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(drm_crtc_helper_set_mode);
+
+
+/**
+ * drm_crtc_helper_set_config - set a new config from userspace
+ * @crtc: CRTC to setup
+ * @crtc_info: user provided configuration
+ * @new_mode: new mode to set
+ * @connector_set: set of connectors for the new config
+ * @fb: new framebuffer
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Setup a new configuration, provided by the user in @crtc_info, and enable
+ * it.
+ *
+ * RETURNS:
+ * Zero. (FIXME)
+ */
+int drm_crtc_helper_set_config(struct drm_mode_set *set)
+{
+ struct drm_device *dev;
+ struct drm_crtc **save_crtcs, *new_crtc;
+ struct drm_encoder **save_encoders, *new_encoder;
+ bool save_enabled;
+ bool changed = false;
+ bool flip_or_move = false;
+ struct drm_connector *connector;
+ int count = 0, ro, fail = 0;
+ struct drm_crtc_helper_funcs *crtc_funcs;
+ int ret = 0;
+
+ DRM_DEBUG("\n");
+
+ if (!set)
+ return -EINVAL;
+
+ if (!set->crtc)
+ return -EINVAL;
+
+ if (!set->crtc->helper_private)
+ return -EINVAL;
+
+ crtc_funcs = set->crtc->helper_private;
+
+ DRM_DEBUG("crtc: %p fb: %p connectors: %p num_connectors: %i (x, y) (%i, %i)\n", set->crtc, set->fb, set->connectors, set->num_connectors, set->x, set->y);
+
+ dev = set->crtc->dev;
+
+ /* save previous config */
+ save_enabled = set->crtc->enabled;
+
+ /* this is meant to be num_connector not num_crtc */
+ save_crtcs = kzalloc(dev->mode_config.num_connector * sizeof(struct drm_crtc *), GFP_KERNEL);
+ if (!save_crtcs)
+ return -ENOMEM;
+
+ save_encoders = kzalloc(dev->mode_config.num_connector * sizeof(struct drm_encoders *), GFP_KERNEL);
+ if (!save_encoders) {
+ kfree(save_crtcs);
+ return -ENOMEM;
+ }
+
+ /* We should be able to check here if the fb has the same properties
+ * and then just flip_or_move it */
+ if (set->crtc->fb != set->fb) {
+ /* if we have no fb then its a change not a flip */
+ if (set->crtc->fb == NULL)
+ changed = true;
+ else
+ flip_or_move = true;
+ }
+
+ if (set->x != set->crtc->x || set->y != set->crtc->y)
+ flip_or_move = true;
+
+ if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) {
+ DRM_DEBUG("modes are different\n");
+ drm_mode_debug_printmodeline(&set->crtc->mode);
+ drm_mode_debug_printmodeline(set->mode);
+ changed = true;
+ }
+
+ /* a) traverse passed in connector list and get encoders for them */
+ count = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ struct drm_connector_helper_funcs *connector_funcs = connector->helper_private;
+ save_encoders[count++] = connector->encoder;
+ new_encoder = connector->encoder;
+ for (ro = 0; ro < set->num_connectors; ro++) {
+ if (set->connectors[ro] == connector) {
+ new_encoder = connector_funcs->best_encoder(connector);
+ /* if we can't get an encoder for a connector
+ we are setting now - then fail */
+ if (new_encoder == NULL)
+ /* don't break so fail path works correct */
+ fail = 1;
+ break;
+ }
+ }
+
+ if (new_encoder != connector->encoder) {
+ changed = true;
+ connector->encoder = new_encoder;
+ }
+ }
+
+ if (fail) {
+ ret = -EINVAL;
+ goto fail_no_encoder;
+ }
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (!connector->encoder)
+ continue;
+
+ save_crtcs[count++] = connector->encoder->crtc;
+
+ if (connector->encoder->crtc == set->crtc)
+ new_crtc = NULL;
+ else
+ new_crtc = connector->encoder->crtc;
+
+ for (ro = 0; ro < set->num_connectors; ro++) {
+ if (set->connectors[ro] == connector)
+ new_crtc = set->crtc;
+ }
+ if (new_crtc != connector->encoder->crtc) {
+ changed = true;
+ connector->encoder->crtc = new_crtc;
+ }
+ }
+
+ /* mode_set_base is not a required function */
+ if (flip_or_move && !crtc_funcs->mode_set_base)
+ changed = true;
+
+ if (changed) {
+ set->crtc->fb = set->fb;
+ set->crtc->enabled = (set->mode != NULL);
+ if (set->mode != NULL) {
+ DRM_DEBUG("attempting to set mode from userspace\n");
+ drm_mode_debug_printmodeline(set->mode);
+ if (!drm_crtc_helper_set_mode(set->crtc, set->mode, set->x,
+ set->y)) {
+ ret = -EINVAL;
+ goto fail_set_mode;
+ }
+ /* TODO are these needed? */
+ set->crtc->desired_x = set->x;
+ set->crtc->desired_y = set->y;
+ set->crtc->desired_mode = set->mode;
+ }
+ drm_helper_disable_unused_functions(dev);
+ } else if (flip_or_move) {
+ if (set->crtc->fb != set->fb)
+ set->crtc->fb = set->fb;
+ crtc_funcs->mode_set_base(set->crtc, set->x, set->y);
+ }
+
+ kfree(save_encoders);
+ kfree(save_crtcs);
+ return 0;
+
+fail_set_mode:
+ set->crtc->enabled = save_enabled;
+ count = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+ connector->encoder->crtc = save_crtcs[count++];
+fail_no_encoder:
+ kfree(save_crtcs);
+ count = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ connector->encoder = save_encoders[count++];
+ }
+ kfree(save_encoders);
+ return ret;
+
+
+}
+EXPORT_SYMBOL(drm_crtc_helper_set_config);
+
+bool drm_helper_plugged_event(struct drm_device *dev)
+{
+ DRM_DEBUG("\n");
+
+ drm_helper_probe_connector_modes(dev, dev->mode_config.max_width, dev->mode_config.max_height);
+
+ drm_pick_crtcs(dev);
+
+ /* alert the driver fb layer */
+ dev->mode_config.funcs->fb_changed(dev);
+
+ drm_helper_disable_unused_functions(dev);
+
+ drm_sysfs_hotplug_event(dev);
+ return true;
+}
+/**
+ * drm_initial_config - setup a sane initial connector configuration
+ * @dev: DRM device
+ * @can_grow: this configuration is growable
+ *
+ * LOCKING:
+ * Called at init time, must take mode config lock.
+ *
+ * Scan the CRTCs and connectors and try to put together an initial setup.
+ * At the moment, this is a cloned configuration across all heads with
+ * a new framebuffer object as the backing store.
+ *
+ * RETURNS:
+ * Zero if everything went ok, nonzero otherwise.
+ */
+bool drm_helper_initial_config(struct drm_device *dev, bool can_grow)
+{
+ int ret = false;
+
+ drm_helper_plugged_event(dev);
+ return ret;
+}
+EXPORT_SYMBOL(drm_helper_initial_config);
+
+/**
+ * drm_hotplug_stage_two
+ * @dev DRM device
+ * @connector hotpluged connector
+ *
+ * LOCKING.
+ * Caller must hold mode config lock, function might grab struct lock.
+ *
+ * Stage two of a hotplug.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_helper_hotplug_stage_two(struct drm_device *dev)
+{
+ dev->mode_config.hotplug_counter++;
+
+ drm_helper_plugged_event(dev);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_helper_hotplug_stage_two);
+
+int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
+ struct drm_mode_fb_cmd *mode_cmd)
+{
+ fb->width = mode_cmd->width;
+ fb->height = mode_cmd->height;
+ fb->pitch = mode_cmd->pitch;
+ fb->bits_per_pixel = mode_cmd->bpp;
+ fb->depth = mode_cmd->depth;
+ fb->mm_handle = mode_cmd->handle;
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct);
+
+/**
+ * drm_get_buffer_object - find the buffer object for a given handle
+ * @dev: DRM device
+ * @bo: pointer to caller's buffer_object pointer
+ * @handle: handle to lookup
+ *
+ * LOCKING:
+ * Must take @dev's struct_mutex to protect buffer object lookup.
+ *
+ * Given @handle, lookup the buffer object in @dev and put it in the caller's
+ * @bo pointer.
+ *
+ * RETURNS:
+ * Zero on success, -EINVAL if the handle couldn't be found.
+ */
+int drm_get_buffer_object(struct drm_device *dev, struct drm_buffer_object **bo, unsigned long handle)
+{
+ struct drm_user_object *uo;
+ struct drm_hash_item *hash;
+ int ret;
+
+ *bo = NULL;
+
+ mutex_lock(&dev->struct_mutex);
+ ret = drm_ht_find_item(&dev->object_hash, handle, &hash);
+ if (ret) {
+ DRM_ERROR("Couldn't find handle.\n");
+ ret = -EINVAL;
+ goto out_err;
+ }
+
+ uo = drm_hash_entry(hash, struct drm_user_object, hash);
+ if (uo->type != drm_buffer_type) {
+ ret = -EINVAL;
+ goto out_err;
+ }
+
+ *bo = drm_user_object_entry(uo, struct drm_buffer_object, base);
+ ret = 0;
+out_err:
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
+EXPORT_SYMBOL(drm_get_buffer_object);
+
diff --git a/linux-core/drm_crtc_helper.h b/linux-core/drm_crtc_helper.h
new file mode 100644
index 00000000..45a67f98
--- /dev/null
+++ b/linux-core/drm_crtc_helper.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright © 2006 Keith Packard
+ * Copyright © 2007 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ */
+
+/*
+ * The DRM mode setting helper functions are common code for drivers to use if they wish.
+ * Drivers are not forced to use this code in their implementations but it would be useful
+ * if they code they do use at least provides a consistent interface and operation to userspace
+ */
+
+#ifndef __DRM_CRTC_HELPER_H__
+#define __DRM_CRTC_HELPER_H__
+
+#include <linux/i2c.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/idr.h>
+
+#include <linux/fb.h>
+
+struct drm_crtc_helper_funcs {
+ /*
+ * Control power levels on the CRTC. If the mode passed in is
+ * unsupported, the provider must use the next lowest power level.
+ */
+ void (*dpms)(struct drm_crtc *crtc, int mode);
+ void (*prepare)(struct drm_crtc *crtc);
+ void (*commit)(struct drm_crtc *crtc);
+
+ /* Provider can fixup or change mode timings before modeset occurs */
+ bool (*mode_fixup)(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
+ /* Actually set the mode */
+ void (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode, int x, int y);
+
+ /* Move the crtc on the current fb to the given position *optional* */
+ void (*mode_set_base)(struct drm_crtc *crtc, int x, int y);
+};
+
+struct drm_encoder_helper_funcs {
+ void (*dpms)(struct drm_encoder *encoder, int mode);
+ void (*save)(struct drm_encoder *encoder);
+ void (*restore)(struct drm_encoder *encoder);
+
+ bool (*mode_fixup)(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
+ void (*prepare)(struct drm_encoder *encoder);
+ void (*commit)(struct drm_encoder *encoder);
+ void (*mode_set)(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
+};
+
+struct drm_connector_helper_funcs {
+ int (*get_modes)(struct drm_connector *connector);
+ int (*mode_valid)(struct drm_connector *connector,
+ struct drm_display_mode *mode);
+ struct drm_encoder *(*best_encoder)(struct drm_connector *connector);
+};
+
+extern void drm_helper_probe_single_connector_modes(struct drm_connector *connector, uint32_t maxX, uint32_t maxY);
+extern void drm_helper_disable_unused_functions(struct drm_device *dev);
+extern int drm_helper_hotplug_stage_two(struct drm_device *dev);
+extern bool drm_helper_initial_config(struct drm_device *dev, bool can_grow);
+extern int drm_crtc_helper_set_config(struct drm_mode_set *set);
+extern bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode,
+ int x, int y);
+extern bool drm_helper_crtc_in_use(struct drm_crtc *crtc);
+
+extern int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
+ struct drm_mode_fb_cmd *mode_cmd);
+
+static inline void drm_crtc_helper_add(struct drm_crtc *crtc, const struct drm_crtc_helper_funcs *funcs)
+{
+ crtc->helper_private = (void *)funcs;
+}
+
+static inline void drm_encoder_helper_add(struct drm_encoder *encoder, const struct drm_encoder_helper_funcs *funcs)
+{
+ encoder->helper_private = (void *)funcs;
+}
+
+static inline void drm_connector_helper_add(struct drm_connector *connector, const struct drm_connector_helper_funcs *funcs)
+{
+ connector->helper_private = (void *)funcs;
+}
+
+extern int drm_get_buffer_object(struct drm_device *dev, struct drm_buffer_object **bo, unsigned long handle);
+
+#endif
diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c
index edc1f057..df09e72b 100644
--- a/linux-core/drm_drv.c
+++ b/linux-core/drm_drv.c
@@ -56,26 +56,29 @@ static int drm_version(struct drm_device *dev, void *data,
/** Ioctl table */
static struct drm_ioctl_desc drm_ioctls[] = {
- DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, 0),
+ DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, DRM_CONTROL_ALLOW),
DRM_IOCTL_DEF(DRM_IOCTL_GET_UNIQUE, drm_getunique, 0),
DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, 0),
- DRM_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_irq_by_busid, DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_irq_by_busid, DRM_MASTER),
DRM_IOCTL_DEF(DRM_IOCTL_GET_MAP, drm_getmap, 0),
DRM_IOCTL_DEF(DRM_IOCTL_GET_CLIENT, drm_getclient, 0),
DRM_IOCTL_DEF(DRM_IOCTL_GET_STATS, drm_getstats, 0),
- DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_MASTER),
- DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_setunique, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_setunique, DRM_AUTH|DRM_MASTER),
DRM_IOCTL_DEF(DRM_IOCTL_BLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_UNBLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_AUTH_MAGIC, drm_authmagic, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_AUTH_MAGIC, drm_authmagic, DRM_AUTH|DRM_MASTER),
- DRM_IOCTL_DEF(DRM_IOCTL_ADD_MAP, drm_addmap_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_ADD_MAP, drm_addmap_ioctl, DRM_AUTH|DRM_MASTER),
DRM_IOCTL_DEF(DRM_IOCTL_RM_MAP, drm_rmmap_ioctl, DRM_AUTH),
DRM_IOCTL_DEF(DRM_IOCTL_SET_SAREA_CTX, drm_setsareactx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_GET_SAREA_CTX, drm_getsareactx, DRM_AUTH),
+ DRM_IOCTL_DEF(DRM_IOCTL_SET_MASTER, drm_setmaster_ioctl, DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_DROP_MASTER, drm_dropmaster_ioctl, DRM_ROOT_ONLY),
+
DRM_IOCTL_DEF(DRM_IOCTL_ADD_CTX, drm_addctx, DRM_AUTH|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_RM_CTX, drm_rmctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_MOD_CTX, drm_modctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
@@ -100,7 +103,7 @@ static struct drm_ioctl_desc drm_ioctls[] = {
/* The DRM_IOCTL_DMA ioctl should be defined by the driver. */
DRM_IOCTL_DEF(DRM_IOCTL_DMA, NULL, DRM_AUTH),
- DRM_IOCTL_DEF(DRM_IOCTL_CONTROL, drm_control, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_CONTROL, drm_control, DRM_AUTH|DRM_MASTER),
#if __OS_HAS_AGP
DRM_IOCTL_DEF(DRM_IOCTL_AGP_ACQUIRE, drm_agp_acquire_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
@@ -121,6 +124,28 @@ static struct drm_ioctl_desc drm_ioctls[] = {
DRM_IOCTL_DEF(DRM_IOCTL_UPDATE_DRAW, drm_update_drawable_info, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_MASTER|DRM_CONTROL_ALLOW),
+
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPROPERTY, drm_mode_connector_property_set_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATTACHMODE, drm_mode_attachmode_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_DETACHMODE, drm_mode_detachmode_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_MASTER | DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_HOTPLUG, drm_mode_hotplug_ioctl, DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_WAIT_HOTPLUG, drm_wait_hotplug, 0),
+
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_REPLACEFB, drm_mode_replacefb, DRM_MASTER|DRM_ROOT_ONLY|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETENCODER, drm_mode_getencoder, DRM_MASTER|DRM_ROOT_ONLY|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, DRM_MASTER),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETGAMMA, drm_mode_gamma_set_ioctl, DRM_MASTER),
+
DRM_IOCTL_DEF(DRM_IOCTL_MM_INIT, drm_mm_init_ioctl,
DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_MM_TAKEDOWN, drm_mm_takedown_ioctl,
@@ -175,31 +200,25 @@ static struct drm_ioctl_desc drm_ioctls[] = {
*/
int drm_lastclose(struct drm_device * dev)
{
- struct drm_magic_entry *pt, *next;
- struct drm_map_list *r_list, *list_t;
struct drm_vma_entry *vma, *vma_temp;
int i;
DRM_DEBUG("\n");
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ drm_bo_driver_finish(dev);
+
/*
* We can't do much about this function failing.
*/
- drm_bo_driver_finish(dev);
-
if (dev->driver->lastclose)
dev->driver->lastclose(dev);
DRM_DEBUG("driver lastclose completed\n");
- if (dev->unique) {
- drm_free(dev->unique, strlen(dev->unique) + 1, DRM_MEM_DRIVER);
- dev->unique = NULL;
- dev->unique_len = 0;
- }
- if (dev->irq_enabled)
- drm_irq_uninstall(dev);
+/* if (dev->irq_enabled)
+ drm_irq_uninstall(dev); */
/* Free drawable information memory */
mutex_lock(&dev->struct_mutex);
@@ -207,22 +226,11 @@ int drm_lastclose(struct drm_device * dev)
drm_drawable_free_all(dev);
del_timer(&dev->timer);
- if (dev->unique) {
- drm_free(dev->unique, strlen(dev->unique) + 1, DRM_MEM_DRIVER);
- dev->unique = NULL;
- dev->unique_len = 0;
+ if (dev->primary->master) {
+ drm_put_master(dev->primary->master);
+ dev->primary->master = NULL;
}
-
- if (dev->magicfree.next) {
- list_for_each_entry_safe(pt, next, &dev->magicfree, head) {
- list_del(&pt->head);
- drm_ht_remove_item(&dev->magiclist, &pt->hash_item);
- drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
- }
- drm_ht_remove(&dev->magiclist);
- }
-
-
+
/* Clear AGP information */
if (drm_core_has_AGP(dev) && dev->agp) {
struct drm_agp_mem *entry, *tempe;
@@ -254,12 +262,13 @@ int drm_lastclose(struct drm_device * dev)
drm_ctl_free(vma, sizeof(*vma), DRM_MEM_VMAS);
}
+ /*
list_for_each_entry_safe(r_list, list_t, &dev->maplist, head) {
if (!(r_list->map->flags & _DRM_DRIVER)) {
drm_rmmap_locked(dev, r_list->map);
r_list = NULL;
}
- }
+ }*/
if (drm_core_check_feature(dev, DRIVER_DMA_QUEUE) && dev->queuelist) {
for (i = 0; i < dev->queue_count; i++) {
@@ -281,11 +290,6 @@ int drm_lastclose(struct drm_device * dev)
if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))
drm_dma_takedown(dev);
- if (dev->lock.hw_lock) {
- dev->sigdata.lock = dev->lock.hw_lock = NULL; /* SHM removed */
- dev->lock.file_priv = NULL;
- wake_up_interruptible(&dev->lock.lock_queue);
- }
dev->dev_mapping = NULL;
mutex_unlock(&dev->struct_mutex);
@@ -412,12 +416,14 @@ static void drm_cleanup(struct drm_device * dev)
DRM_DEBUG("mtrr_del=%d\n", retval);
}
+ if (dev->driver->unload)
+ dev->driver->unload(dev);
+
+ drm_ht_remove(&dev->map_hash);
if (drm_core_has_AGP(dev) && dev->agp) {
drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS);
dev->agp = NULL;
}
- if (dev->driver->unload)
- dev->driver->unload(dev);
if (!drm_fb_loaded)
pci_disable_device(dev->pdev);
@@ -428,6 +434,8 @@ static void drm_cleanup(struct drm_device * dev)
drm_ht_remove(&dev->object_hash);
drm_put_minor(&dev->primary);
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ drm_put_minor(&dev->control);
if (drm_put_dev(dev))
DRM_ERROR("Cannot unload module\n");
}
@@ -442,8 +450,14 @@ int drm_minors_cleanup(int id, void *ptr, void *data)
if (minor->dev->driver != driver)
return 0;
- if (minor->type != DRM_MINOR_LEGACY)
- return 0;
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ if (minor->type != DRM_MINOR_CONTROL)
+ return 0;
+ } else {
+ if (minor->type != DRM_MINOR_LEGACY)
+ return 0;
+ }
+
if (dev)
pci_dev_put(dev->pdev);
@@ -628,6 +642,7 @@ long drm_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retcode = -EINVAL;
goto err_i1;
}
+
#if 0
/*
* This check is disabled, because driver private ioctl->cmd
@@ -648,7 +663,7 @@ long drm_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (cmd & IOC_IN) {
if (copy_from_user(kdata, (void __user *)arg,
_IOC_SIZE(cmd)) != 0) {
- retcode = -EACCES;
+ retcode = -EFAULT;
goto err_i1;
}
}
@@ -658,7 +673,8 @@ long drm_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retcode = -EINVAL;
} else if (((ioctl->flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN)) ||
((ioctl->flags & DRM_AUTH) && !file_priv->authenticated) ||
- ((ioctl->flags & DRM_MASTER) && !file_priv->master)) {
+ ((ioctl->flags & DRM_MASTER) && !file_priv->master) ||
+ ((!(ioctl->flags & DRM_CONTROL_ALLOW)) && (file_priv->minor->type == DRM_MINOR_CONTROL)) ) {
retcode = -EACCES;
} else {
retcode = func(dev, kdata, file_priv);
@@ -667,13 +683,13 @@ long drm_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if ((retcode == 0) && (cmd & IOC_OUT)) {
if (copy_to_user((void __user *)arg, kdata,
_IOC_SIZE(cmd)) != 0)
- retcode = -EACCES;
+ retcode = -EFAULT;
}
err_i1:
atomic_dec(&dev->ioctl_count);
if (retcode)
- DRM_DEBUG("ret = %d\n", retcode);
+ DRM_ERROR("ret = %x %d\n", nr, retcode);
return retcode;
}
EXPORT_SYMBOL(drm_unlocked_ioctl);
diff --git a/linux-core/drm_edid.c b/linux-core/drm_edid.c
new file mode 100644
index 00000000..0d600396
--- /dev/null
+++ b/linux-core/drm_edid.c
@@ -0,0 +1,733 @@
+/*
+ * Copyright (c) 2006 Luc Verhaegen (quirks list)
+ * Copyright (c) 2007 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ *
+ * DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid) originally from
+ * FB layer.
+ * Copyright (C) 2006 Dennis Munsie <dmunsie@cecropia.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sub license,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include "drmP.h"
+#include "drm_edid.h"
+
+/*
+ * TODO:
+ * - support EDID 1.4
+ * - port quirks from X server code
+ */
+
+/*
+ * EDID blocks out in the wild have a variety of bugs, try to collect
+ * them here (note that userspace may work around broken monitors first,
+ * but fixes should make their way here so that the kernel "just works"
+ * on as many displays as possible).
+ */
+
+/* First detailed mode wrong, use largest 60Hz mode */
+#define EDID_QUIRK_PREFER_LARGE_60 (1 << 0)
+/* Reported 135MHz pixel clock is too high, needs adjustment */
+#define EDID_QUIRK_135_CLOCK_TOO_HIGH (1 << 1)
+/* Prefer the largest mode at 75 Hz */
+#define EDID_QUIRK_PREFER_LARGE_75 (1 << 2)
+/* Detail timing is in cm not mm */
+#define EDID_QUIRK_DETAILED_IN_CM (1 << 3)
+/* Detailed timing descriptors have bogus size values, so just take the
+ * maximum size and use that.
+ */
+#define EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE (1 << 4)
+/* Monitor forgot to set the first detailed is preferred bit. */
+#define EDID_QUIRK_FIRST_DETAILED_PREFERRED (1 << 5)
+/* use +hsync +vsync for detailed mode */
+#define EDID_QUIRK_DETAILED_SYNC_PP (1 << 6)
+
+static struct edid_quirk {
+ char *vendor;
+ int product_id;
+ u32 quirks;
+} edid_quirk_list[] = {
+ /* Acer AL1706 */
+ { "ACR", 44358, EDID_QUIRK_PREFER_LARGE_60 },
+ /* Acer F51 */
+ { "API", 0x7602, EDID_QUIRK_PREFER_LARGE_60 },
+ /* Unknown Acer */
+ { "ACR", 2423, EDID_QUIRK_FIRST_DETAILED_PREFERRED },
+
+ /* Belinea 10 15 55 */
+ { "MAX", 1516, EDID_QUIRK_PREFER_LARGE_60 },
+ { "MAX", 0x77e, EDID_QUIRK_PREFER_LARGE_60 },
+
+ /* Envision Peripherals, Inc. EN-7100e */
+ { "EPI", 59264, EDID_QUIRK_135_CLOCK_TOO_HIGH },
+
+ /* Funai Electronics PM36B */
+ { "FCM", 13600, EDID_QUIRK_PREFER_LARGE_75 |
+ EDID_QUIRK_DETAILED_IN_CM },
+
+ /* LG Philips LCD LP154W01-A5 */
+ { "LPL", 0, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE },
+ { "LPL", 0x2a00, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE },
+
+ /* Philips 107p5 CRT */
+ { "PHL", 57364, EDID_QUIRK_FIRST_DETAILED_PREFERRED },
+
+ /* Proview AY765C */
+ { "PTS", 765, EDID_QUIRK_FIRST_DETAILED_PREFERRED },
+
+ /* Samsung SyncMaster 205BW. Note: irony */
+ { "SAM", 541, EDID_QUIRK_DETAILED_SYNC_PP },
+ /* Samsung SyncMaster 22[5-6]BW */
+ { "SAM", 596, EDID_QUIRK_PREFER_LARGE_60 },
+ { "SAM", 638, EDID_QUIRK_PREFER_LARGE_60 },
+};
+
+
+/* Valid EDID header has these bytes */
+static u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 };
+
+/**
+ * edid_is_valid - sanity check EDID data
+ * @edid: EDID data
+ *
+ * Sanity check the EDID block by looking at the header, the version number
+ * and the checksum. Return 0 if the EDID doesn't check out, or 1 if it's
+ * valid.
+ */
+static bool edid_is_valid(struct edid *edid)
+{
+ int i;
+ u8 csum = 0;
+ u8 *raw_edid = (u8 *)edid;
+
+ if (memcmp(edid->header, edid_header, sizeof(edid_header)))
+ goto bad;
+ if (edid->version != 1)
+ goto bad;
+ if (edid->revision <= 0 || edid->revision > 3)
+ goto bad;
+
+ for (i = 0; i < EDID_LENGTH; i++)
+ csum += raw_edid[i];
+ if (csum)
+ goto bad;
+
+ return 1;
+
+bad:
+ return 0;
+}
+
+/**
+ * edid_vendor - match a string against EDID's obfuscated vendor field
+ * @edid: EDID to match
+ * @vendor: vendor string
+ *
+ * Returns true if @vendor is in @edid, false otherwise
+ */
+static bool edid_vendor(struct edid *edid, char *vendor)
+{
+ char edid_vendor[3];
+
+ edid_vendor[0] = ((edid->mfg_id[0] & 0x7c) >> 2) + '@';
+ edid_vendor[1] = (((edid->mfg_id[0] & 0x3) << 3) |
+ ((edid->mfg_id[1] & 0xe0) >> 5)) + '@';
+ edid_vendor[2] = (edid->mfg_id[2] & 0x1f) + '@';
+
+ return !strncmp(edid_vendor, vendor, 3);
+}
+
+/**
+ * edid_get_quirks - return quirk flags for a given EDID
+ * @edid: EDID to process
+ *
+ * This tells subsequent routines what fixes they need to apply.
+ */
+static u32 edid_get_quirks(struct edid *edid)
+{
+ struct edid_quirk *quirk;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) {
+ quirk = &edid_quirk_list[i];
+
+ if (edid_vendor(edid, quirk->vendor) &&
+ (EDID_PRODUCT_ID(edid) == quirk->product_id))
+ return quirk->quirks;
+ }
+
+ return 0;
+}
+
+#define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay)
+#define MODE_REFRESH_DIFF(m,r) (abs((m)->vrefresh - target_refresh))
+
+
+/**
+ * edid_fixup_preferred - set preferred modes based on quirk list
+ * @connector: has mode list to fix up
+ * @quirks: quirks list
+ *
+ * Walk the mode list for @connector, clearing the preferred status
+ * on existing modes and setting it anew for the right mode ala @quirks.
+ */
+static void edid_fixup_preferred(struct drm_connector *connector,
+ u32 quirks)
+{
+ struct drm_display_mode *t, *cur_mode, *preferred_mode;
+ int target_refresh;
+
+ if (list_empty(&connector->probed_modes))
+ return;
+
+ if (quirks & EDID_QUIRK_PREFER_LARGE_60)
+ target_refresh = 60;
+ if (quirks & EDID_QUIRK_PREFER_LARGE_75)
+ target_refresh = 75;
+
+ preferred_mode = list_first_entry(&connector->probed_modes,
+ struct drm_display_mode, head);
+
+ list_for_each_entry_safe(cur_mode, t, &connector->probed_modes, head) {
+ cur_mode->type &= ~DRM_MODE_TYPE_PREFERRED;
+
+ if (cur_mode == preferred_mode)
+ continue;
+
+ /* Largest mode is preferred */
+ if (MODE_SIZE(cur_mode) > MODE_SIZE(preferred_mode))
+ preferred_mode = cur_mode;
+
+ /* At a given size, try to get closest to target refresh */
+ if ((MODE_SIZE(cur_mode) == MODE_SIZE(preferred_mode)) &&
+ MODE_REFRESH_DIFF(cur_mode, target_refresh) <
+ MODE_REFRESH_DIFF(preferred_mode, target_refresh)) {
+ preferred_mode = cur_mode;
+ }
+ }
+
+ preferred_mode->type |= DRM_MODE_TYPE_PREFERRED;
+}
+
+/**
+ * drm_mode_std - convert standard mode info (width, height, refresh) into mode
+ * @t: standard timing params
+ *
+ * Take the standard timing params (in this case width, aspect, and refresh)
+ * and convert them into a real mode using CVT.
+ *
+ * Punts for now, but should eventually use the FB layer's CVT based mode
+ * generation code.
+ */
+struct drm_display_mode *drm_mode_std(struct drm_device *dev,
+ struct std_timing *t)
+{
+// struct fb_videomode mode;
+
+// fb_find_mode_cvt(&mode, 0, 0);
+ /* JJJ: convert to drm_display_mode */
+ struct drm_display_mode *mode;
+ int hsize = t->hsize * 8 + 248, vsize;
+
+ mode = drm_mode_create(dev);
+ if (!mode)
+ return NULL;
+
+ if (t->aspect_ratio == 0)
+ vsize = (hsize * 10) / 16;
+ else if (t->aspect_ratio == 1)
+ vsize = (hsize * 3) / 4;
+ else if (t->aspect_ratio == 2)
+ vsize = (hsize * 4) / 5;
+ else
+ vsize = (hsize * 9) / 16;
+
+ drm_mode_set_name(mode);
+
+ return mode;
+}
+
+/**
+ * drm_mode_detailed - create a new mode from an EDID detailed timing section
+ * @dev: DRM device (needed to create new mode)
+ * @edid: EDID block
+ * @timing: EDID detailed timing info
+ * @quirks: quirks to apply
+ *
+ * An EDID detailed timing block contains enough info for us to create and
+ * return a new struct drm_display_mode.
+ */
+static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
+ struct edid *edid,
+ struct detailed_timing *timing,
+ u32 quirks)
+{
+ struct drm_display_mode *mode;
+ struct detailed_pixel_timing *pt = &timing->data.pixel_data;
+
+ if (pt->stereo) {
+ printk(KERN_WARNING "stereo mode not supported\n");
+ return NULL;
+ }
+ if (!pt->separate_sync) {
+ printk(KERN_WARNING "integrated sync not supported\n");
+ return NULL;
+ }
+
+ mode = drm_mode_create(dev);
+ if (!mode)
+ return NULL;
+
+ mode->type = DRM_MODE_TYPE_DRIVER;
+
+ if (quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH)
+ timing->pixel_clock = 1088;
+
+ mode->clock = timing->pixel_clock * 10;
+
+ mode->hdisplay = (pt->hactive_hi << 8) | pt->hactive_lo;
+ mode->hsync_start = mode->hdisplay + ((pt->hsync_offset_hi << 8) |
+ pt->hsync_offset_lo);
+ mode->hsync_end = mode->hsync_start +
+ ((pt->hsync_pulse_width_hi << 8) |
+ pt->hsync_pulse_width_lo);
+ mode->htotal = mode->hdisplay + ((pt->hblank_hi << 8) | pt->hblank_lo);
+
+ mode->vdisplay = (pt->vactive_hi << 8) | pt->vactive_lo;
+ mode->vsync_start = mode->vdisplay + ((pt->vsync_offset_hi << 8) |
+ pt->vsync_offset_lo);
+ mode->vsync_end = mode->vsync_start +
+ ((pt->vsync_pulse_width_hi << 8) |
+ pt->vsync_pulse_width_lo);
+ mode->vtotal = mode->vdisplay + ((pt->vblank_hi << 8) | pt->vblank_lo);
+
+ drm_mode_set_name(mode);
+
+ if (pt->interlaced)
+ mode->flags |= V_INTERLACE;
+
+ if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) {
+ pt->hsync_positive = 1;
+ pt->vsync_positive = 1;
+ }
+
+ mode->flags |= pt->hsync_positive ? V_PHSYNC : V_NHSYNC;
+ mode->flags |= pt->vsync_positive ? V_PVSYNC : V_NVSYNC;
+
+ mode->width_mm = pt->width_mm_lo | (pt->width_mm_hi << 8);
+ mode->height_mm = pt->height_mm_lo | (pt->height_mm_hi << 8);
+
+ if (quirks & EDID_QUIRK_DETAILED_IN_CM) {
+ mode->width_mm *= 10;
+ mode->height_mm *= 10;
+ }
+
+ if (quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) {
+ mode->width_mm = edid->width_cm * 10;
+ mode->height_mm = edid->height_cm * 10;
+ }
+
+ return mode;
+}
+
+/*
+ * Detailed mode info for the EDID "established modes" data to use.
+ */
+static struct drm_display_mode edid_est_modes[] = {
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
+ 968, 1056, 0, 600, 601, 605, 628, 0,
+ V_PHSYNC | V_PVSYNC) }, /* 800x600@60Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824,
+ 896, 1024, 0, 600, 601, 603, 625, 0,
+ V_PHSYNC | V_PVSYNC) }, /* 800x600@56Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656,
+ 720, 840, 0, 480, 481, 484, 500, 0,
+ V_NHSYNC | V_NVSYNC) }, /* 640x480@75Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664,
+ 704, 832, 0, 480, 489, 491, 520, 0,
+ V_NHSYNC | V_NVSYNC) }, /* 640x480@72Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 30240, 640, 704,
+ 768, 864, 0, 480, 483, 486, 525, 0,
+ V_NHSYNC | V_NVSYNC) }, /* 640x480@67Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25200, 640, 656,
+ 752, 800, 0, 480, 490, 492, 525, 0,
+ V_NHSYNC | V_NVSYNC) }, /* 640x480@60Hz */
+ { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 738,
+ 846, 900, 0, 400, 421, 423, 449, 0,
+ V_NHSYNC | V_NVSYNC) }, /* 720x400@88Hz */
+ { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738,
+ 846, 900, 0, 400, 412, 414, 449, 0,
+ V_NHSYNC | V_PVSYNC) }, /* 720x400@70Hz */
+ { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296,
+ 1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
+ V_PHSYNC | V_PVSYNC) }, /* 1280x1024@75Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78800, 1024, 1040,
+ 1136, 1312, 0, 768, 769, 772, 800, 0,
+ V_PHSYNC | V_PVSYNC) }, /* 1024x768@75Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048,
+ 1184, 1328, 0, 768, 771, 777, 806, 0,
+ V_NHSYNC | V_NVSYNC) }, /* 1024x768@70Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
+ 1184, 1344, 0, 768, 771, 777, 806, 0,
+ V_NHSYNC | V_NVSYNC) }, /* 1024x768@60Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032,
+ 1208, 1264, 0, 768, 768, 776, 817, 0,
+ V_PHSYNC | V_PVSYNC | V_INTERLACE) }, /* 1024x768@43Hz */
+ { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864,
+ 928, 1152, 0, 624, 625, 628, 667, 0,
+ V_NHSYNC | V_NVSYNC) }, /* 832x624@75Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816,
+ 896, 1056, 0, 600, 601, 604, 625, 0,
+ V_PHSYNC | V_PVSYNC) }, /* 800x600@75Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856,
+ 976, 1040, 0, 600, 637, 643, 666, 0,
+ V_PHSYNC | V_PVSYNC) }, /* 800x600@72Hz */
+ { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
+ 1344, 1600, 0, 864, 865, 868, 900, 0,
+ V_PHSYNC | V_PVSYNC) }, /* 1152x864@75Hz */
+};
+
+#define EDID_EST_TIMINGS 16
+#define EDID_STD_TIMINGS 8
+#define EDID_DETAILED_TIMINGS 4
+
+/**
+ * add_established_modes - get est. modes from EDID and add them
+ * @edid: EDID block to scan
+ *
+ * Each EDID block contains a bitmap of the supported "established modes" list
+ * (defined above). Tease them out and add them to the global modes list.
+ */
+static int add_established_modes(struct drm_connector *connector, struct edid *edid)
+{
+ struct drm_device *dev = connector->dev;
+ unsigned long est_bits = edid->established_timings.t1 |
+ (edid->established_timings.t2 << 8) |
+ ((edid->established_timings.mfg_rsvd & 0x80) << 9);
+ int i, modes = 0;
+
+ for (i = 0; i <= EDID_EST_TIMINGS; i++)
+ if (est_bits & (1<<i)) {
+ struct drm_display_mode *newmode;
+ newmode = drm_mode_duplicate(dev, &edid_est_modes[i]);
+ if (newmode) {
+ drm_mode_probed_add(connector, newmode);
+ modes++;
+ }
+ }
+
+ return modes;
+}
+
+/**
+ * add_standard_modes - get std. modes from EDID and add them
+ * @edid: EDID block to scan
+ *
+ * Standard modes can be calculated using the CVT standard. Grab them from
+ * @edid, calculate them, and add them to the list.
+ */
+static int add_standard_modes(struct drm_connector *connector, struct edid *edid)
+{
+ struct drm_device *dev = connector->dev;
+ int i, modes = 0;
+
+ for (i = 0; i < EDID_STD_TIMINGS; i++) {
+ struct std_timing *t = &edid->standard_timings[i];
+ struct drm_display_mode *newmode;
+
+ /* If std timings bytes are 1, 1 it's empty */
+ if (t->hsize == 1 && (t->aspect_ratio | t->vfreq) == 1)
+ continue;
+
+ newmode = drm_mode_std(dev, &edid->standard_timings[i]);
+ if (newmode) {
+ drm_mode_probed_add(connector, newmode);
+ modes++;
+ }
+ }
+
+ return modes;
+}
+
+/**
+ * add_detailed_modes - get detailed mode info from EDID data
+ * @connector: attached connector
+ * @edid: EDID block to scan
+ * @quirks: quirks to apply
+ *
+ * Some of the detailed timing sections may contain mode information. Grab
+ * it and add it to the list.
+ */
+static int add_detailed_info(struct drm_connector *connector,
+ struct edid *edid, u32 quirks)
+{
+ struct drm_device *dev = connector->dev;
+ int i, j, modes = 0;
+
+ for (i = 0; i < EDID_DETAILED_TIMINGS; i++) {
+ struct detailed_timing *timing = &edid->detailed_timings[i];
+ struct detailed_non_pixel *data = &timing->data.other_data;
+ struct drm_display_mode *newmode;
+
+ /* EDID up to and including 1.2 may put monitor info here */
+ if (edid->version == 1 && edid->revision < 3)
+ continue;
+
+ /* Detailed mode timing */
+ if (timing->pixel_clock) {
+ newmode = drm_mode_detailed(dev, edid, timing, quirks);
+ if (!newmode)
+ continue;
+
+ /* First detailed mode is preferred */
+ if (i == 0 && edid->preferred_timing)
+ newmode->type |= DRM_MODE_TYPE_PREFERRED;
+ drm_mode_probed_add(connector, newmode);
+
+ modes++;
+ continue;
+ }
+
+ /* Other timing or info */
+ switch (data->type) {
+ case EDID_DETAIL_MONITOR_SERIAL:
+ break;
+ case EDID_DETAIL_MONITOR_STRING:
+ break;
+ case EDID_DETAIL_MONITOR_RANGE:
+ /* Get monitor range data */
+ break;
+ case EDID_DETAIL_MONITOR_NAME:
+ break;
+ case EDID_DETAIL_MONITOR_CPDATA:
+ break;
+ case EDID_DETAIL_STD_MODES:
+ /* Five modes per detailed section */
+ for (j = 0; j < 5; i++) {
+ struct std_timing *std;
+ struct drm_display_mode *newmode;
+
+ std = &data->data.timings[j];
+ newmode = drm_mode_std(dev, std);
+ if (newmode) {
+ drm_mode_probed_add(connector, newmode);
+ modes++;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return modes;
+}
+
+#define DDC_ADDR 0x50
+
+static unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter *adapter)
+{
+ unsigned char start = 0x0;
+ unsigned char *buf = kmalloc(EDID_LENGTH, GFP_KERNEL);
+ struct i2c_msg msgs[] = {
+ {
+ .addr = DDC_ADDR,
+ .flags = 0,
+ .len = 1,
+ .buf = &start,
+ }, {
+ .addr = DDC_ADDR,
+ .flags = I2C_M_RD,
+ .len = EDID_LENGTH,
+ .buf = buf,
+ }
+ };
+
+ if (!buf) {
+ dev_warn(&adapter->dev, "unable to allocate memory for EDID "
+ "block.\n");
+ return NULL;
+ }
+
+ if (i2c_transfer(adapter, msgs, 2) == 2)
+ return buf;
+
+ dev_info(&adapter->dev, "unable to read EDID block.\n");
+ kfree(buf);
+ return NULL;
+}
+
+static unsigned char *drm_ddc_read(struct i2c_adapter *adapter)
+{
+ struct i2c_algo_bit_data *algo_data = adapter->algo_data;
+ unsigned char *edid = NULL;
+ int i, j;
+
+ /*
+ * Startup the bus:
+ * Set clock line high (but give it time to come up)
+ * Then set clock & data low
+ */
+ algo_data->setscl(algo_data->data, 1);
+ udelay(550); /* startup delay */
+ algo_data->setscl(algo_data->data, 0);
+ algo_data->setsda(algo_data->data, 0);
+
+ for (i = 0; i < 3; i++) {
+ /* For some old monitors we need the
+ * following process to initialize/stop DDC
+ */
+ algo_data->setsda(algo_data->data, 0);
+ msleep(13);
+
+ algo_data->setscl(algo_data->data, 1);
+ for (j = 0; j < 5; j++) {
+ msleep(10);
+ if (algo_data->getscl(algo_data->data))
+ break;
+ }
+ if (j == 5)
+ continue;
+
+ algo_data->setsda(algo_data->data, 0);
+ msleep(15);
+ algo_data->setscl(algo_data->data, 0);
+ msleep(15);
+ algo_data->setsda(algo_data->data, 1);
+ msleep(15);
+
+ /* Do the real work */
+ edid = drm_do_probe_ddc_edid(adapter);
+ algo_data->setsda(algo_data->data, 0);
+ algo_data->setscl(algo_data->data, 0);
+ msleep(15);
+
+ algo_data->setscl(algo_data->data, 1);
+ for (j = 0; j < 10; j++) {
+ msleep(10);
+ if (algo_data->getscl(algo_data->data))
+ break;
+ }
+
+ algo_data->setsda(algo_data->data, 1);
+ msleep(15);
+ algo_data->setscl(algo_data->data, 0);
+ if (edid)
+ break;
+ }
+ /* Release the DDC lines when done or the Apple Cinema HD display
+ * will switch off
+ */
+ algo_data->setsda(algo_data->data, 0);
+ algo_data->setscl(algo_data->data, 0);
+ algo_data->setscl(algo_data->data, 1);
+
+ return edid;
+}
+
+/**
+ * drm_get_edid - get EDID data, if available
+ * @connector: connector we're probing
+ * @adapter: i2c adapter to use for DDC
+ *
+ * Poke the given connector's i2c channel to grab EDID data if possible.
+ *
+ * Return edid data or NULL if we couldn't find any.
+ */
+struct edid *drm_get_edid(struct drm_connector *connector,
+ struct i2c_adapter *adapter)
+{
+ struct edid *edid;
+
+ edid = (struct edid *)drm_ddc_read(adapter);
+ if (!edid) {
+ dev_warn(&connector->dev->pdev->dev, "%s: no EDID data\n",
+ drm_get_connector_name(connector));
+ return NULL;
+ }
+ if (!edid_is_valid(edid)) {
+ dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n",
+ drm_get_connector_name(connector));
+ kfree(edid);
+ return NULL;
+ }
+
+ connector->display_info.raw_edid = (char *)edid;
+
+ return edid;
+}
+EXPORT_SYMBOL(drm_get_edid);
+
+/**
+ * drm_add_edid_modes - add modes from EDID data, if available
+ * @connector: connector we're probing
+ * @edid: edid data
+ *
+ * Add the specified modes to the connector's mode list.
+ *
+ * Return number of modes added or 0 if we couldn't find any.
+ */
+int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
+{
+ int num_modes = 0;
+ u32 quirks;
+
+ if (edid == NULL) {
+ return 0;
+ }
+ if (!edid_is_valid(edid)) {
+ dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n",
+ drm_get_connector_name(connector));
+ return 0;
+ }
+
+ quirks = edid_get_quirks(edid);
+
+ num_modes += add_established_modes(connector, edid);
+ num_modes += add_standard_modes(connector, edid);
+ num_modes += add_detailed_info(connector, edid, quirks);
+
+ if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75))
+ edid_fixup_preferred(connector, quirks);
+
+ connector->display_info.serration_vsync = edid->serration_vsync;
+ connector->display_info.sync_on_green = edid->sync_on_green;
+ connector->display_info.composite_sync = edid->composite_sync;
+ connector->display_info.separate_syncs = edid->separate_syncs;
+ connector->display_info.blank_to_black = edid->blank_to_black;
+ connector->display_info.video_level = edid->video_level;
+ connector->display_info.digital = edid->digital;
+ connector->display_info.width_mm = edid->width_cm * 10;
+ connector->display_info.height_mm = edid->height_cm * 10;
+ connector->display_info.gamma = edid->gamma;
+ connector->display_info.gtf_supported = edid->default_gtf;
+ connector->display_info.standard_color = edid->standard_color;
+ connector->display_info.display_type = edid->display_type;
+ connector->display_info.active_off_supported = edid->pm_active_off;
+ connector->display_info.suspend_supported = edid->pm_suspend;
+ connector->display_info.standby_supported = edid->pm_standby;
+ connector->display_info.gamma = edid->gamma;
+
+ return num_modes;
+}
+EXPORT_SYMBOL(drm_add_edid_modes);
diff --git a/linux-core/drm_edid.h b/linux-core/drm_edid.h
new file mode 100644
index 00000000..dccfba91
--- /dev/null
+++ b/linux-core/drm_edid.h
@@ -0,0 +1,178 @@
+#ifndef __DRM_EDID_H__
+#define __DRM_EDID_H__
+
+#include <linux/types.h>
+
+#define EDID_LENGTH 128
+#define DDC_ADDR 0x50
+
+#ifdef BIG_ENDIAN
+#error "EDID structure is little endian, need big endian versions"
+#endif
+
+struct est_timings {
+ u8 t1;
+ u8 t2;
+ u8 mfg_rsvd;
+} __attribute__((packed));
+
+struct std_timing {
+ u8 hsize; /* need to multiply by 8 then add 248 */
+ u8 vfreq:6; /* need to add 60 */
+ u8 aspect_ratio:2; /* 00=16:10, 01=4:3, 10=5:4, 11=16:9 */
+} __attribute__((packed));
+
+/* If detailed data is pixel timing */
+struct detailed_pixel_timing {
+ u8 hactive_lo;
+ u8 hblank_lo;
+ u8 hblank_hi:4;
+ u8 hactive_hi:4;
+ u8 vactive_lo;
+ u8 vblank_lo;
+ u8 vblank_hi:4;
+ u8 vactive_hi:4;
+ u8 hsync_offset_lo;
+ u8 hsync_pulse_width_lo;
+ u8 vsync_pulse_width_lo:4;
+ u8 vsync_offset_lo:4;
+ u8 hsync_pulse_width_hi:2;
+ u8 hsync_offset_hi:2;
+ u8 vsync_pulse_width_hi:2;
+ u8 vsync_offset_hi:2;
+ u8 width_mm_lo;
+ u8 height_mm_lo;
+ u8 height_mm_hi:4;
+ u8 width_mm_hi:4;
+ u8 hborder;
+ u8 vborder;
+ u8 unknown0:1;
+ u8 vsync_positive:1;
+ u8 hsync_positive:1;
+ u8 separate_sync:2;
+ u8 stereo:1;
+ u8 unknown6:1;
+ u8 interlaced:1;
+} __attribute__((packed));
+
+/* If it's not pixel timing, it'll be one of the below */
+struct detailed_data_string {
+ u8 str[13];
+} __attribute__((packed));
+
+struct detailed_data_monitor_range {
+ u8 min_vfreq;
+ u8 max_vfreq;
+ u8 min_hfreq_khz;
+ u8 max_hfreq_khz;
+ u8 pixel_clock_mhz; /* need to multiply by 10 */
+ u16 sec_gtf_toggle; /* A000=use above, 20=use below */ /* FIXME: byte order */
+ u8 hfreq_start_khz; /* need to multiply by 2 */
+ u8 c; /* need to divide by 2 */
+ u16 m; /* FIXME: byte order */
+ u8 k;
+ u8 j; /* need to divide by 2 */
+} __attribute__((packed));
+
+struct detailed_data_wpindex {
+ u8 white_y_lo:2;
+ u8 white_x_lo:2;
+ u8 pad:4;
+ u8 white_x_hi;
+ u8 white_y_hi;
+ u8 gamma; /* need to divide by 100 then add 1 */
+} __attribute__((packed));
+
+struct detailed_data_color_point {
+ u8 windex1;
+ u8 wpindex1[3];
+ u8 windex2;
+ u8 wpindex2[3];
+} __attribute__((packed));
+
+struct detailed_non_pixel {
+ u8 pad1;
+ u8 type; /* ff=serial, fe=string, fd=monitor range, fc=monitor name
+ fb=color point data, fa=standard timing data,
+ f9=undefined, f8=mfg. reserved */
+ u8 pad2;
+ union {
+ struct detailed_data_string str;
+ struct detailed_data_monitor_range range;
+ struct detailed_data_wpindex color;
+ struct std_timing timings[5];
+ } data;
+} __attribute__((packed));
+
+#define EDID_DETAIL_STD_MODES 0xfa
+#define EDID_DETAIL_MONITOR_CPDATA 0xfb
+#define EDID_DETAIL_MONITOR_NAME 0xfc
+#define EDID_DETAIL_MONITOR_RANGE 0xfd
+#define EDID_DETAIL_MONITOR_STRING 0xfe
+#define EDID_DETAIL_MONITOR_SERIAL 0xff
+
+struct detailed_timing {
+ u16 pixel_clock; /* need to multiply by 10 KHz */ /* FIXME: byte order */
+ union {
+ struct detailed_pixel_timing pixel_data;
+ struct detailed_non_pixel other_data;
+ } data;
+} __attribute__((packed));
+
+struct edid {
+ u8 header[8];
+ /* Vendor & product info */
+ u8 mfg_id[2];
+ u8 prod_code[2];
+ u32 serial; /* FIXME: byte order */
+ u8 mfg_week;
+ u8 mfg_year;
+ /* EDID version */
+ u8 version;
+ u8 revision;
+ /* Display info: */
+ /* input definition */
+ u8 serration_vsync:1;
+ u8 sync_on_green:1;
+ u8 composite_sync:1;
+ u8 separate_syncs:1;
+ u8 blank_to_black:1;
+ u8 video_level:2;
+ u8 digital:1; /* bits below must be zero if set */
+ u8 width_cm;
+ u8 height_cm;
+ u8 gamma;
+ /* feature support */
+ u8 default_gtf:1;
+ u8 preferred_timing:1;
+ u8 standard_color:1;
+ u8 display_type:2; /* 00=mono, 01=rgb, 10=non-rgb, 11=unknown */
+ u8 pm_active_off:1;
+ u8 pm_suspend:1;
+ u8 pm_standby:1;
+ /* Color characteristics */
+ u8 red_green_lo;
+ u8 black_white_lo;
+ u8 red_x;
+ u8 red_y;
+ u8 green_x;
+ u8 green_y;
+ u8 blue_x;
+ u8 blue_y;
+ u8 white_x;
+ u8 white_y;
+ /* Est. timings and mfg rsvd timings*/
+ struct est_timings established_timings;
+ /* Standard timings 1-8*/
+ struct std_timing standard_timings[8];
+ /* Detailing timings 1-4 */
+ struct detailed_timing detailed_timings[4];
+ /* Number of 128 byte ext. blocks */
+ u8 extensions;
+ /* Checksum */
+ u8 checksum;
+} __attribute__((packed));
+
+#define EDID_PRODUCT_ID(e) ((e)->prod_code[0] | ((e)->prod_code[1] << 8))
+
+#endif /* __DRM_EDID_H__ */
diff --git a/linux-core/drm_fb.c b/linux-core/drm_fb.c
new file mode 100644
index 00000000..74a4394a
--- /dev/null
+++ b/linux-core/drm_fb.c
@@ -0,0 +1,432 @@
+/*
+ * Copyright © 2007 David Airlie
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * David Airlie
+ */
+ /*
+ * Modularization
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+
+#include "drmP.h"
+#include "drm_crtc.h"
+
+struct drmfb_par {
+ struct drm_device *dev;
+ struct drm_crtc *crtc;
+};
+
+static int drmfb_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp,
+ struct fb_info *info)
+{
+ struct drmfb_par *par = info->par;
+ struct drm_framebuffer *fb = par->crtc->fb;
+ struct drm_crtc *crtc = par->crtc;
+
+ if (regno > 255)
+ return 1;
+
+ if (fb->depth == 8) {
+ return 0;
+ }
+
+ if (regno < 16) {
+ switch (fb->depth) {
+ case 15:
+ fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) |
+ ((green & 0xf800) >> 6) |
+ ((blue & 0xf800) >> 11);
+ break;
+ case 16:
+ fb->pseudo_palette[regno] = (red & 0xf800) |
+ ((green & 0xfc00) >> 5) |
+ ((blue & 0xf800) >> 11);
+ break;
+ case 24:
+ case 32:
+ fb->pseudo_palette[regno] = ((red & 0xff00) << 8) |
+ (green & 0xff00) |
+ ((blue & 0xff00) >> 8);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int drmfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct drmfb_par *par = info->par;
+ struct drm_device *dev = par->dev;
+ struct drm_framebuffer *fb = par->crtc->fb;
+ struct drm_display_mode *drm_mode;
+ struct drm_output *output;
+ int depth;
+
+ if (!var->pixclock)
+ return -EINVAL;
+
+ /* Need to resize the fb object !!! */
+ if (var->xres > fb->width || var->yres > fb->height) {
+ DRM_ERROR("Requested width/height is greater than current fb object %dx%d > %dx%d\n",var->xres,var->yres,fb->width,fb->height);
+ DRM_ERROR("Need resizing code.\n");
+ return -EINVAL;
+ }
+
+ switch (var->bits_per_pixel) {
+ case 16:
+ depth = (var->green.length == 6) ? 16 : 15;
+ break;
+ case 32:
+ depth = (var->transp.length > 0) ? 32 : 24;
+ break;
+ default:
+ depth = var->bits_per_pixel;
+ break;
+ }
+
+ switch (depth) {
+ case 8:
+ var->red.offset = 0;
+ var->green.offset = 0;
+ var->blue.offset = 0;
+ var->red.length = 8;
+ var->green.length = 8;
+ var->blue.length = 8;
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ break;
+ case 15:
+ var->red.offset = 10;
+ var->green.offset = 5;
+ var->blue.offset = 0;
+ var->red.length = 5;
+ var->green.length = 5;
+ var->blue.length = 5;
+ var->transp.length = 1;
+ var->transp.offset = 15;
+ break;
+ case 16:
+ var->red.offset = 11;
+ var->green.offset = 6;
+ var->blue.offset = 0;
+ var->red.length = 5;
+ var->green.length = 6;
+ var->blue.length = 5;
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ break;
+ case 24:
+ var->red.offset = 16;
+ var->green.offset = 8;
+ var->blue.offset = 0;
+ var->red.length = 8;
+ var->green.length = 8;
+ var->blue.length = 8;
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ break;
+ case 32:
+ var->red.offset = 16;
+ var->green.offset = 8;
+ var->blue.offset = 0;
+ var->red.length = 8;
+ var->green.length = 8;
+ var->blue.length = 8;
+ var->transp.length = 8;
+ var->transp.offset = 24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+#if 0
+ /* Here we walk the output mode list and look for modes. If we haven't
+ * got it, then bail. Not very nice, so this is disabled.
+ * In the set_par code, we create our mode based on the incoming
+ * parameters. Nicer, but may not be desired by some.
+ */
+ list_for_each_entry(output, &dev->mode_config.output_list, head) {
+ if (output->crtc == par->crtc)
+ break;
+ }
+
+ list_for_each_entry(drm_mode, &output->modes, head) {
+ if (drm_mode->hdisplay == var->xres &&
+ drm_mode->vdisplay == var->yres &&
+ drm_mode->clock != 0)
+ break;
+ }
+
+ if (!drm_mode)
+ return -EINVAL;
+#endif
+
+ return 0;
+}
+
+/* this will let fbcon do the mode init */
+static int drmfb_set_par(struct fb_info *info)
+{
+ struct drmfb_par *par = info->par;
+ struct drm_framebuffer *fb = par->crtc->fb;
+ struct drm_device *dev = par->dev;
+ struct drm_display_mode *drm_mode;
+ struct fb_var_screeninfo *var = &info->var;
+ struct drm_output *output;
+
+ switch (var->bits_per_pixel) {
+ case 16:
+ fb->depth = (var->green.length == 6) ? 16 : 15;
+ break;
+ case 32:
+ fb->depth = (var->transp.length > 0) ? 32 : 24;
+ break;
+ default:
+ fb->depth = var->bits_per_pixel;
+ break;
+ }
+
+ fb->bits_per_pixel = var->bits_per_pixel;
+
+ info->fix.line_length = fb->pitch;
+ info->fix.smem_len = info->fix.line_length * fb->height;
+ info->fix.visual = (fb->depth == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
+
+ info->screen_size = info->fix.smem_len; /* ??? */
+
+ /* Should we walk the output's modelist or just create our own ???
+ * For now, we create and destroy a mode based on the incoming
+ * parameters. But there's commented out code below which scans
+ * the output list too.
+ */
+#if 0
+ list_for_each_entry(output, &dev->mode_config.output_list, head) {
+ if (output->crtc == par->crtc)
+ break;
+ }
+
+ list_for_each_entry(drm_mode, &output->modes, head) {
+ if (drm_mode->hdisplay == var->xres &&
+ drm_mode->vdisplay == var->yres &&
+ drm_mode->clock != 0)
+ break;
+ }
+#else
+ drm_mode = drm_mode_create(dev);
+ drm_mode->hdisplay = var->xres;
+ drm_mode->hsync_start = drm_mode->hdisplay + var->right_margin;
+ drm_mode->hsync_end = drm_mode->hsync_start + var->hsync_len;
+ drm_mode->htotal = drm_mode->hsync_end + var->left_margin;
+ drm_mode->vdisplay = var->yres;
+ drm_mode->vsync_start = drm_mode->vdisplay + var->lower_margin;
+ drm_mode->vsync_end = drm_mode->vsync_start + var->vsync_len;
+ drm_mode->vtotal = drm_mode->vsync_end + var->upper_margin;
+ drm_mode->clock = PICOS2KHZ(var->pixclock);
+ drm_mode->vrefresh = drm_mode_vrefresh(drm_mode);
+ drm_mode_set_name(drm_mode);
+ drm_mode_set_crtcinfo(drm_mode, CRTC_INTERLACE_HALVE_V);
+#endif
+
+ if (!drm_crtc_set_mode(par->crtc, drm_mode, 0, 0))
+ return -EINVAL;
+
+ /* Have to destroy our created mode if we're not searching the mode
+ * list for it.
+ */
+#if 1
+ drm_mode_destroy(dev, drm_mode);
+#endif
+
+ return 0;
+}
+
+static struct fb_ops drmfb_ops = {
+ .owner = THIS_MODULE,
+ // .fb_open = drmfb_open,
+ // .fb_read = drmfb_read,
+ // .fb_write = drmfb_write,
+ // .fb_release = drmfb_release,
+ // .fb_ioctl = drmfb_ioctl,
+ .fb_check_var = drmfb_check_var,
+ .fb_set_par = drmfb_set_par,
+ .fb_setcolreg = drmfb_setcolreg,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+int drmfb_probe(struct drm_device *dev, struct drm_crtc *crtc)
+{
+ struct fb_info *info;
+ struct drm_framebuffer *fb = crtc->fb;
+ struct drmfb_par *par;
+ struct device *device = &dev->pdev->dev;
+ struct drm_display_mode *mode = crtc->desired_mode;
+ int ret;
+
+ info = framebuffer_alloc(sizeof(struct drmfb_par), device);
+ if (!info)
+ return -ENOMEM;
+
+ fb->fbdev = info;
+
+ par = info->par;
+
+ par->dev = dev;
+ par->crtc = crtc;
+
+ info->fbops = &drmfb_ops;
+
+ strcpy(info->fix.id, "drmfb");
+ info->fix.type = FB_TYPE_PACKED_PIXELS;
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ info->fix.accel = FB_ACCEL_NONE;
+ info->fix.type_aux = 0;
+ info->fix.mmio_start = 0;
+ info->fix.mmio_len = 0;
+ info->fix.line_length = fb->pitch;
+ info->fix.smem_start = fb->offset + dev->mode_config.fb_base;
+ info->fix.smem_len = info->fix.line_length * fb->height;
+
+ info->flags = FBINFO_DEFAULT;
+
+ ret = drm_mem_reg_ioremap(dev, &fb->bo->mem, &fb->virtual_base);
+ if (ret)
+ DRM_ERROR("error mapping fb: %d\n", ret);
+
+ info->screen_base = fb->virtual_base;
+ info->screen_size = info->fix.smem_len; /* ??? */
+ info->pseudo_palette = fb->pseudo_palette;
+ info->var.xres_virtual = fb->width;
+ info->var.yres_virtual = fb->height;
+ info->var.bits_per_pixel = fb->bits_per_pixel;
+ info->var.xoffset = 0;
+ info->var.yoffset = 0;
+ info->var.activate = FB_ACTIVATE_NOW;
+ info->var.height = -1;
+ info->var.width = -1;
+ info->var.vmode = FB_VMODE_NONINTERLACED;
+
+ info->var.xres = mode->hdisplay;
+ info->var.right_margin = mode->hsync_start - mode->hdisplay;
+ info->var.hsync_len = mode->hsync_end - mode->hsync_start;
+ info->var.left_margin = mode->htotal - mode->hsync_end;
+ info->var.yres = mode->vdisplay;
+ info->var.lower_margin = mode->vsync_start - mode->vdisplay;
+ info->var.vsync_len = mode->vsync_end - mode->vsync_start;
+ info->var.upper_margin = mode->vtotal - mode->vsync_end;
+ info->var.pixclock = 10000000 / mode->htotal * 1000 /
+ mode->vtotal * 100;
+ /* avoid overflow */
+ info->var.pixclock = info->var.pixclock * 1000 / mode->vrefresh;
+
+ DRM_DEBUG("fb depth is %d\n", fb->depth);
+ switch(fb->depth) {
+ case 8:
+ info->var.red.offset = 0;
+ info->var.green.offset = 0;
+ info->var.blue.offset = 0;
+ info->var.red.length = 8; /* 8bit DAC */
+ info->var.green.length = 8;
+ info->var.blue.length = 8;
+ info->var.transp.offset = 0;
+ info->var.transp.length = 0;
+ break;
+ case 15:
+ info->var.red.offset = 10;
+ info->var.green.offset = 5;
+ info->var.blue.offset = 0;
+ info->var.red.length = info->var.green.length =
+ info->var.blue.length = 5;
+ info->var.transp.offset = 15;
+ info->var.transp.length = 1;
+ break;
+ case 16:
+ info->var.red.offset = 11;
+ info->var.green.offset = 5;
+ info->var.blue.offset = 0;
+ info->var.red.length = 5;
+ info->var.green.length = 6;
+ info->var.blue.length = 5;
+ info->var.transp.offset = 0;
+ info->var.transp.length = 0;
+ break;
+ case 24:
+ info->var.red.offset = 16;
+ info->var.green.offset = 8;
+ info->var.blue.offset = 0;
+ info->var.red.length = info->var.green.length =
+ info->var.blue.length = 8;
+ info->var.transp.offset = 0;
+ info->var.transp.length = 0;
+ break;
+ case 32:
+ info->var.red.offset = 16;
+ info->var.green.offset = 8;
+ info->var.blue.offset = 0;
+ info->var.red.length = info->var.green.length =
+ info->var.blue.length = 8;
+ info->var.transp.offset = 24;
+ info->var.transp.length = 8;
+ break;
+ default:
+ break;
+ }
+
+ if (register_framebuffer(info) < 0) {
+ unregister_framebuffer(info);
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
+ info->fix.id);
+ return 0;
+}
+EXPORT_SYMBOL(drmfb_probe);
+
+int drmfb_remove(struct drm_device *dev, struct drm_crtc *crtc)
+{
+ struct fb_info *info = fb->fbdev;
+ struct drm_framebuffer *fb = crtc->fb;
+
+ if (info) {
+ drm_mem_reg_iounmap(dev, &fb->bo->mem, fb->virtual_base);
+ unregister_framebuffer(info);
+ framebuffer_release(info);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(drmfb_remove);
+MODULE_LICENSE("GPL");
diff --git a/linux-core/drm_fops.c b/linux-core/drm_fops.c
index ec521101..3b3a0a3c 100644
--- a/linux-core/drm_fops.c
+++ b/linux-core/drm_fops.c
@@ -43,10 +43,8 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
static int drm_setup(struct drm_device * dev)
{
- drm_local_map_t *map;
int i;
int ret;
- int sareapage;
if (dev->driver->firstopen) {
ret = dev->driver->firstopen(dev);
@@ -54,14 +52,6 @@ static int drm_setup(struct drm_device * dev)
return ret;
}
- dev->magicfree.next = NULL;
-
- /* prebuild the SAREA */
- sareapage = max(SAREA_MAX, PAGE_SIZE);
- i = drm_addmap(dev, 0, sareapage, _DRM_SHM, _DRM_CONTAINS_LOCK, &map);
- if (i != 0)
- return i;
-
atomic_set(&dev->ioctl_count, 0);
atomic_set(&dev->vma_count, 0);
dev->buf_use = 0;
@@ -76,11 +66,8 @@ static int drm_setup(struct drm_device * dev)
for (i = 0; i < ARRAY_SIZE(dev->counts); i++)
atomic_set(&dev->counts[i], 0);
- drm_ht_create(&dev->magiclist, DRM_MAGIC_HASH_ORDER);
- INIT_LIST_HEAD(&dev->magicfree);
-
dev->sigdata.lock = NULL;
- init_waitqueue_head(&dev->lock.lock_queue);
+
dev->queue_count = 0;
dev->queue_reserved = 0;
dev->queue_slots = 0;
@@ -260,6 +247,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
INIT_LIST_HEAD(&priv->lhead);
INIT_LIST_HEAD(&priv->refd_objects);
+ INIT_LIST_HEAD(&priv->fbs);
for (i = 0; i < _DRM_NO_REF_TYPES; ++i) {
ret = drm_ht_create(&priv->refd_object_hash[i],
@@ -283,10 +271,36 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
goto out_free;
}
+
+ /* if there is no current master make this fd it */
mutex_lock(&dev->struct_mutex);
- if (list_empty(&dev->filelist))
- priv->master = 1;
+ if (!priv->minor->master) {
+ priv->minor->master = drm_get_master(priv->minor);
+ if (!priv->minor->master) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+ priv->is_master = 1;
+ priv->master = priv->minor->master;
+
+ priv->authenticated = 1;
+ mutex_unlock(&dev->struct_mutex);
+ if (dev->driver->master_create) {
+ ret = dev->driver->master_create(dev, priv->master);
+ if (ret) {
+ drm_put_master(priv->minor->master);
+ priv->minor->master = priv->master = NULL;
+ mutex_unlock(&dev->struct_mutex);
+ goto out_free;
+ }
+ }
+ } else {
+ priv->master = priv->minor->master;
+ mutex_unlock(&dev->struct_mutex);
+ }
+
+ mutex_lock(&dev->struct_mutex);
list_add(&priv->lhead, &dev->filelist);
mutex_unlock(&dev->struct_mutex);
@@ -392,59 +406,63 @@ int drm_release(struct inode *inode, struct file *filp)
current->pid, (long)old_encode_dev(file_priv->minor->device),
dev->open_count);
- if (dev->driver->reclaim_buffers_locked && dev->lock.hw_lock) {
- if (drm_i_have_hw_lock(dev, file_priv)) {
- dev->driver->reclaim_buffers_locked(dev, file_priv);
- } else {
- unsigned long _end=jiffies + 3*DRM_HZ;
- int locked = 0;
-
- drm_idlelock_take(&dev->lock);
-
- /*
- * Wait for a while.
- */
-
- do{
- spin_lock_bh(&dev->lock.spinlock);
- locked = dev->lock.idle_has_lock;
- spin_unlock_bh(&dev->lock.spinlock);
- if (locked)
- break;
- schedule();
- } while (!time_after_eq(jiffies, _end));
-
- if (!locked) {
- DRM_ERROR("reclaim_buffers_locked() deadlock. Please rework this\n"
- "\tdriver to use reclaim_buffers_idlelocked() instead.\n"
- "\tI will go on reclaiming the buffers anyway.\n");
+ /* if the master has gone away we can't do anything with the lock */
+ if (file_priv->minor->master) {
+ if (dev->driver->reclaim_buffers_locked && file_priv->master->lock.hw_lock) {
+ if (drm_i_have_hw_lock(dev, file_priv)) {
+ dev->driver->reclaim_buffers_locked(dev, file_priv);
+ } else {
+ unsigned long _end=jiffies + 3*DRM_HZ;
+ int locked = 0;
+
+ drm_idlelock_take(&file_priv->master->lock);
+
+ /*
+ * Wait for a while.
+ */
+
+ do{
+ spin_lock_bh(&file_priv->master->lock.spinlock);
+ locked = file_priv->master->lock.idle_has_lock;
+ spin_unlock_bh(&file_priv->master->lock.spinlock);
+ if (locked)
+ break;
+ schedule();
+ } while (!time_after_eq(jiffies, _end));
+
+ if (!locked) {
+ DRM_ERROR("reclaim_buffers_locked() deadlock. Please rework this\n"
+ "\tdriver to use reclaim_buffers_idlelocked() instead.\n"
+ "\tI will go on reclaiming the buffers anyway.\n");
+ }
+
+ dev->driver->reclaim_buffers_locked(dev, file_priv);
+ drm_idlelock_release(&file_priv->master->lock);
}
-
- dev->driver->reclaim_buffers_locked(dev, file_priv);
- drm_idlelock_release(&dev->lock);
}
- }
-
- if (dev->driver->reclaim_buffers_idlelocked && dev->lock.hw_lock) {
-
- drm_idlelock_take(&dev->lock);
- dev->driver->reclaim_buffers_idlelocked(dev, file_priv);
- drm_idlelock_release(&dev->lock);
- }
+ if (dev->driver->reclaim_buffers_idlelocked && file_priv->master->lock.hw_lock) {
+
+ drm_idlelock_take(&file_priv->master->lock);
+ dev->driver->reclaim_buffers_idlelocked(dev, file_priv);
+ drm_idlelock_release(&file_priv->master->lock);
+
+ }
- if (drm_i_have_hw_lock(dev, file_priv)) {
- DRM_DEBUG("File %p released, freeing lock for context %d\n",
- filp, _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
-
- drm_lock_free(&dev->lock,
- _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
- }
+ if (drm_i_have_hw_lock(dev, file_priv)) {
+ DRM_DEBUG("File %p released, freeing lock for context %d\n",
+ filp, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
+
+ drm_lock_free(&file_priv->master->lock,
+ _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
+ }
+
- if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) &&
- !dev->driver->reclaim_buffers_locked) {
- dev->driver->reclaim_buffers(dev, file_priv);
+ if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) &&
+ !dev->driver->reclaim_buffers_locked) {
+ dev->driver->reclaim_buffers(dev, file_priv);
+ }
}
if (dev->driver->driver_features & DRIVER_GEM)
@@ -474,15 +492,30 @@ int drm_release(struct inode *inode, struct file *filp)
}
mutex_unlock(&dev->ctxlist_mutex);
- mutex_lock(&dev->struct_mutex);
- drm_object_release(filp);
- if (file_priv->remove_auth_on_close == 1) {
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ drm_fb_release(filp);
+
+ if (file_priv->is_master) {
struct drm_file *temp;
+ list_for_each_entry(temp, &dev->filelist, lhead) {
+ if ((temp->master == file_priv->master) &&
+ (temp != file_priv))
+ temp->authenticated = 0;
+ }
- list_for_each_entry(temp, &dev->filelist, lhead)
- temp->authenticated = 0;
+ if (file_priv->minor->master == file_priv->master)
+ file_priv->minor->master = NULL;
+ drm_put_master(file_priv->master);
}
+
+ file_priv->master = NULL;
+ file_priv->is_master = 0;
+
+ mutex_lock(&dev->struct_mutex);
+ drm_object_release(filp);
+
list_del(&file_priv->lhead);
+
mutex_unlock(&dev->struct_mutex);
if (dev->driver->postclose)
@@ -496,9 +529,9 @@ int drm_release(struct inode *inode, struct file *filp)
atomic_inc(&dev->counts[_DRM_STAT_CLOSES]);
spin_lock(&dev->count_lock);
if (!--dev->open_count) {
- if (atomic_read(&dev->ioctl_count) || dev->blocked) {
- DRM_ERROR("Device busy: %d %d\n",
- atomic_read(&dev->ioctl_count), dev->blocked);
+ if (atomic_read(&dev->ioctl_count)) {
+ DRM_ERROR("Device busy: %d\n",
+ atomic_read(&dev->ioctl_count));
spin_unlock(&dev->count_lock);
unlock_kernel();
return -EBUSY;
diff --git a/linux-core/drm_ioctl.c b/linux-core/drm_ioctl.c
index 3df163db..e35126a3 100644
--- a/linux-core/drm_ioctl.c
+++ b/linux-core/drm_ioctl.c
@@ -53,12 +53,13 @@ int drm_getunique(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_unique *u = data;
+ struct drm_master *master = file_priv->master;
- if (u->unique_len >= dev->unique_len) {
- if (copy_to_user(u->unique, dev->unique, dev->unique_len))
+ if (u->unique_len >= master->unique_len) {
+ if (copy_to_user(u->unique, master->unique, master->unique_len))
return -EFAULT;
}
- u->unique_len = dev->unique_len;
+ u->unique_len = master->unique_len;
return 0;
}
@@ -81,36 +82,37 @@ int drm_setunique(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_unique *u = data;
+ struct drm_master *master = file_priv->master;
int domain, bus, slot, func, ret;
- if (dev->unique_len || dev->unique)
+ if (master->unique_len || master->unique)
return -EBUSY;
if (!u->unique_len || u->unique_len > 1024)
return -EINVAL;
- dev->unique_len = u->unique_len;
- dev->unique = drm_alloc(u->unique_len + 1, DRM_MEM_DRIVER);
- if (!dev->unique)
+ master->unique_len = u->unique_len;
+ master->unique = drm_alloc(u->unique_len + 1, DRM_MEM_DRIVER);
+ if (!master->unique)
return -ENOMEM;
- if (copy_from_user(dev->unique, u->unique, dev->unique_len))
+ if (copy_from_user(master->unique, u->unique, master->unique_len))
return -EFAULT;
- dev->unique[dev->unique_len] = '\0';
+ master->unique[master->unique_len] = '\0';
dev->devname =
drm_alloc(strlen(dev->driver->pci_driver.name) +
- strlen(dev->unique) + 2, DRM_MEM_DRIVER);
+ strlen(master->unique) + 2, DRM_MEM_DRIVER);
if (!dev->devname)
return -ENOMEM;
sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name,
- dev->unique);
+ master->unique);
/* Return error if the busid submitted doesn't match the device's actual
* busid.
*/
- ret = sscanf(dev->unique, "PCI:%d:%d:%d", &bus, &slot, &func);
+ ret = sscanf(master->unique, "PCI:%d:%d:%d", &bus, &slot, &func);
if (ret != 3)
return -EINVAL;
domain = bus >> 8;
@@ -125,33 +127,35 @@ int drm_setunique(struct drm_device *dev, void *data,
return 0;
}
-static int drm_set_busid(struct drm_device * dev)
+static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv)
{
+ struct drm_master *master = file_priv->master;
int len;
- if (dev->unique != NULL)
+
+ if (master->unique != NULL)
return -EBUSY;
- dev->unique_len = 40;
- dev->unique = drm_alloc(dev->unique_len + 1, DRM_MEM_DRIVER);
- if (dev->unique == NULL)
+ master->unique_len = 40;
+ master->unique = drm_alloc(master->unique_len + 1, DRM_MEM_DRIVER);
+ if (master->unique == NULL)
return -ENOMEM;
- len = snprintf(dev->unique, dev->unique_len, "pci:%04x:%02x:%02x.%d",
+ len = snprintf(master->unique, master->unique_len, "pci:%04x:%02x:%02x.%d",
drm_get_pci_domain(dev),
dev->pdev->bus->number,
PCI_SLOT(dev->pdev->devfn),
PCI_FUNC(dev->pdev->devfn));
- if (len > dev->unique_len)
+ if (len > master->unique_len)
DRM_ERROR("buffer overflow");
dev->devname =
- drm_alloc(strlen(dev->driver->pci_driver.name) + dev->unique_len +
+ drm_alloc(strlen(dev->driver->pci_driver.name) + master->unique_len +
2, DRM_MEM_DRIVER);
if (dev->devname == NULL)
return -ENOMEM;
sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name,
- dev->unique);
+ master->unique);
return 0;
}
@@ -275,7 +279,7 @@ int drm_getstats(struct drm_device *dev, void *data,
for (i = 0; i < dev->counters; i++) {
if (dev->types[i] == _DRM_STAT_LOCK)
stats->data[i].value =
- (dev->lock.hw_lock ? dev->lock.hw_lock->lock : 0);
+ (file_priv->master->lock.hw_lock ? file_priv->master->lock.hw_lock->lock : 0);
else
stats->data[i].value = atomic_read(&dev->counts[i]);
stats->data[i].type = dev->types[i];
@@ -317,7 +321,7 @@ int drm_setversion(struct drm_device *dev, void *data, struct drm_file *file_pri
/*
* Version 1.1 includes tying of DRM to specific device
*/
- drm_set_busid(dev);
+ drm_set_busid(dev, file_priv);
}
}
diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c
index 318d9d7a..c6024d95 100644
--- a/linux-core/drm_irq.c
+++ b/linux-core/drm_irq.c
@@ -90,32 +90,41 @@ static void vblank_disable_fn(unsigned long arg)
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,
+ if (dev->vbl_queue)
+ 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,
+
+ if (dev->vbl_sigs)
+ drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * dev->num_crtcs,
DRM_MEM_DRIVER);
- drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) *
+
+ if (dev->_vblank_count)
+ drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) *
dev->num_crtcs, DRM_MEM_DRIVER);
- drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) *
+
+ if (dev->vblank_refcount)
+ drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) *
dev->num_crtcs, DRM_MEM_DRIVER);
- drm_free(dev->vblank_enabled, sizeof(*dev->vblank_enabled) *
+
+ if (dev->vblank_enabled)
+ 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,
+
+ if (dev->last_vblank)
+ drm_free(dev->last_vblank, sizeof(*dev->last_vblank) * dev->num_crtcs,
DRM_MEM_DRIVER);
- drm_free(dev->vblank_premodeset, sizeof(*dev->vblank_premodeset) *
+
+ if (dev->vblank_premodeset)
+ 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;
+ if (dev->vblank_offset)
+ drm_free(dev->vblank_offset, sizeof(*dev->vblank_offset) * dev->num_crtcs,
+ DRM_MEM_DRIVER);
}
int drm_vblank_init(struct drm_device *dev, int num_crtcs)
@@ -183,6 +192,82 @@ err:
}
EXPORT_SYMBOL(drm_vblank_init);
+int drm_wait_hotplug(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ union drm_wait_hotplug *hotplugwait = data;
+ struct timeval now;
+ int ret = 0;
+ unsigned int flags;
+
+ if ((!dev->irq) || (!dev->irq_enabled))
+ return -EINVAL;
+
+ flags = hotplugwait->request.type;
+
+ if (flags & _DRM_HOTPLUG_SIGNAL) {
+ unsigned long irqflags;
+ struct list_head *hotplug_sigs = dev->hotplug_sigs;
+ struct drm_hotplug_sig *hotplug_sig;
+
+ hotplug_sig = drm_calloc(1, sizeof(struct drm_hotplug_sig),
+ DRM_MEM_DRIVER);
+ if (!hotplug_sig)
+ return -ENOMEM;
+
+ atomic_inc(&dev->hotplug_signal_pending);
+
+ hotplug_sig->info.si_signo = hotplugwait->request.signal;
+ hotplug_sig->task = current;
+ hotplug_sig->counter =
+ hotplugwait->reply.counter =
+ dev->mode_config.hotplug_counter;
+
+ spin_lock_irqsave(&dev->hotplug_lock, irqflags);
+
+ list_add_tail(&hotplug_sig->head, hotplug_sigs);
+
+ spin_unlock_irqrestore(&dev->hotplug_lock, irqflags);
+ } else {
+ int cur_hotplug = dev->mode_config.hotplug_counter;
+
+ DRM_WAIT_ON(ret, dev->hotplug_queue, 3 * DRM_HZ,
+ dev->mode_config.hotplug_counter > cur_hotplug);
+
+ do_gettimeofday(&now);
+
+ hotplugwait->reply.tval_sec = now.tv_sec;
+ hotplugwait->reply.tval_usec = now.tv_usec;
+ hotplugwait->reply.counter = dev->mode_config.hotplug_counter;
+ }
+
+ return ret;
+}
+
+static void drm_hotplug_cleanup(struct drm_device *dev)
+{
+ if (dev->hotplug_sigs)
+ drm_free(dev->hotplug_sigs, sizeof(*dev->hotplug_sigs),
+ DRM_MEM_DRIVER);
+}
+EXPORT_SYMBOL(drm_hotplug_cleanup);
+
+int drm_hotplug_init(struct drm_device *dev)
+{
+ spin_lock_init(&dev->hotplug_lock);
+ atomic_set(&dev->hotplug_signal_pending, 0);
+
+ dev->hotplug_sigs = drm_alloc(sizeof(struct list_head), DRM_MEM_DRIVER);
+ if (!dev->hotplug_sigs)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(dev->hotplug_sigs);
+ init_waitqueue_head(&dev->hotplug_queue);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_hotplug_init);
+
/**
* Install IRQ handler.
*
@@ -213,7 +298,7 @@ int drm_irq_install(struct drm_device * dev)
if (dev->irq_enabled) {
mutex_unlock(&dev->struct_mutex);
- return -EBUSY;
+ return 0;
}
dev->irq_enabled = 1;
mutex_unlock(&dev->struct_mutex);
@@ -278,6 +363,8 @@ int drm_irq_uninstall(struct drm_device * dev)
drm_vblank_cleanup(dev);
+ drm_hotplug_cleanup(dev);
+
dev->locked_tasklet_func = NULL;
return 0;
@@ -307,6 +394,8 @@ int drm_control(struct drm_device *dev, void *data,
case DRM_INST_HANDLER:
if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
return 0;
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ return 0;
if (dev->if_version < DRM_IF_VERSION(1, 2) &&
ctl->irq != dev->irq)
return -EINVAL;
@@ -314,6 +403,8 @@ int drm_control(struct drm_device *dev, void *data,
case DRM_UNINST_HANDLER:
if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
return 0;
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ return 0;
return drm_irq_uninstall(dev);
default:
return -EINVAL;
@@ -527,7 +618,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
if (flags & _DRM_VBLANK_SIGNAL) {
unsigned long irqflags;
struct list_head *vbl_sigs = &dev->vbl_sigs[crtc];
- struct drm_vbl_sig *vbl_sig;
+ struct drm_vbl_sig *vbl_sig, *tmp;
spin_lock_irqsave(&dev->vbl_lock, irqflags);
@@ -535,7 +626,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
* for the same vblank sequence number; nothing to be done in
* that case
*/
- list_for_each_entry(vbl_sig, vbl_sigs, head) {
+ list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) {
if (vbl_sig->sequence == vblwait->request.sequence
&& vbl_sig->info.si_signo ==
vblwait->request.signal
@@ -657,6 +748,53 @@ void drm_handle_vblank(struct drm_device *dev, int crtc)
EXPORT_SYMBOL(drm_handle_vblank);
/**
+ * Send the HOTPLUG signals.
+ *
+ * \param dev DRM device.
+ *
+ * Sends a signal for each task in drm_device::hotplug_sigs and empties the list.
+ */
+static void drm_hotplug_send_signals(struct drm_device * dev)
+{
+ struct drm_hotplug_sig *hotplug_sig, *tmp;
+ struct list_head *hotplug_sigs;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->hotplug_lock, flags);
+
+ hotplug_sigs = dev->hotplug_sigs;
+
+ list_for_each_entry_safe(hotplug_sig, tmp, hotplug_sigs, head) {
+ hotplug_sig->info.si_code = hotplug_sig->counter;
+
+ send_sig_info(hotplug_sig->info.si_signo,
+ &hotplug_sig->info, hotplug_sig->task);
+
+ list_del(&hotplug_sig->head);
+
+ drm_free(hotplug_sig, sizeof(*hotplug_sig),
+ DRM_MEM_DRIVER);
+ atomic_dec(&dev->hotplug_signal_pending);
+ }
+
+ spin_unlock_irqrestore(&dev->hotplug_lock, flags);
+}
+
+/**
+ * drm_handle_hotplug - handle a hotplug event
+ * @dev: DRM device
+ * @crtc: where this event occurred
+ *
+ * Drivers should call this routine in their hotplug interrupt handlers.
+ */
+void drm_handle_hotplug(struct drm_device *dev)
+{
+ DRM_WAKEUP(&dev->hotplug_queue);
+ drm_hotplug_send_signals(dev);
+}
+EXPORT_SYMBOL(drm_handle_hotplug);
+
+/**
* Tasklet wrapper function.
*
* \param data DRM device in disguise.
@@ -673,18 +811,18 @@ static void drm_locked_tasklet_func(unsigned long data)
spin_lock_irqsave(&dev->tasklet_lock, irqflags);
if (!dev->locked_tasklet_func ||
- !drm_lock_take(&dev->lock,
+ !drm_lock_take(&dev->primary->master->lock,
DRM_KERNEL_CONTEXT)) {
spin_unlock_irqrestore(&dev->tasklet_lock, irqflags);
return;
}
- dev->lock.lock_time = jiffies;
+ dev->primary->master->lock.lock_time = jiffies;
atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);
dev->locked_tasklet_func(dev);
- drm_lock_free(&dev->lock,
+ drm_lock_free(&dev->primary->master->lock,
DRM_KERNEL_CONTEXT);
dev->locked_tasklet_func = NULL;
diff --git a/linux-core/drm_lock.c b/linux-core/drm_lock.c
index e6eae42b..d2fb1feb 100644
--- a/linux-core/drm_lock.c
+++ b/linux-core/drm_lock.c
@@ -52,6 +52,7 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
DECLARE_WAITQUEUE(entry, current);
struct drm_lock *lock = data;
+ struct drm_master *master = file_priv->master;
int ret = 0;
++file_priv->lock_count;
@@ -64,26 +65,27 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
lock->context, current->pid,
- dev->lock.hw_lock->lock, lock->flags);
+ master->lock.hw_lock->lock, lock->flags);
if (drm_core_check_feature(dev, DRIVER_DMA_QUEUE))
if (lock->context < 0)
return -EINVAL;
- add_wait_queue(&dev->lock.lock_queue, &entry);
- spin_lock_bh(&dev->lock.spinlock);
- dev->lock.user_waiters++;
- spin_unlock_bh(&dev->lock.spinlock);
+ add_wait_queue(&master->lock.lock_queue, &entry);
+ spin_lock_bh(&master->lock.spinlock);
+ master->lock.user_waiters++;
+ spin_unlock_bh(&master->lock.spinlock);
+
for (;;) {
__set_current_state(TASK_INTERRUPTIBLE);
- if (!dev->lock.hw_lock) {
+ if (!master->lock.hw_lock) {
/* Device has been unregistered */
ret = -EINTR;
break;
}
- if (drm_lock_take(&dev->lock, lock->context)) {
- dev->lock.file_priv = file_priv;
- dev->lock.lock_time = jiffies;
+ if (drm_lock_take(&master->lock, lock->context)) {
+ master->lock.file_priv = file_priv;
+ master->lock.lock_time = jiffies;
atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);
break; /* Got lock */
}
@@ -95,11 +97,11 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
break;
}
}
- spin_lock_bh(&dev->lock.spinlock);
- dev->lock.user_waiters--;
- spin_unlock_bh(&dev->lock.spinlock);
+ spin_lock_bh(&master->lock.spinlock);
+ master->lock.user_waiters--;
+ spin_unlock_bh(&master->lock.spinlock);
__set_current_state(TASK_RUNNING);
- remove_wait_queue(&dev->lock.lock_queue, &entry);
+ remove_wait_queue(&master->lock.lock_queue, &entry);
DRM_DEBUG("%d %s\n", lock->context,
ret ? "interrupted" : "has lock");
@@ -111,7 +113,7 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
sigaddset(&dev->sigmask, SIGTTIN);
sigaddset(&dev->sigmask, SIGTTOU);
dev->sigdata.context = lock->context;
- dev->sigdata.lock = dev->lock.hw_lock;
+ dev->sigdata.lock = master->lock.hw_lock;
block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask);
if (dev->driver->dma_ready && (lock->flags & _DRM_LOCK_READY))
@@ -149,6 +151,7 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
int drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
struct drm_lock *lock = data;
+ struct drm_master *master = file_priv->master;
unsigned long irqflags;
if (lock->context == DRM_KERNEL_CONTEXT) {
@@ -175,7 +178,7 @@ int drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv)
if (dev->driver->kernel_context_switch_unlock)
dev->driver->kernel_context_switch_unlock(dev);
else {
- if (drm_lock_free(&dev->lock,lock->context)) {
+ if (drm_lock_free(&master->lock,lock->context)) {
/* FIXME: Should really bail out here. */
}
}
@@ -213,16 +216,22 @@ int drm_lock_take(struct drm_lock_data *lock_data,
} while (prev != old);
spin_unlock_bh(&lock_data->spinlock);
- /* Warn on recursive locking of user contexts. */
- if (_DRM_LOCKING_CONTEXT(old) == context && _DRM_LOCK_IS_HELD(old)) {
- if (context != DRM_KERNEL_CONTEXT) {
- DRM_ERROR("%d holds heavyweight lock\n",
- context);
+ if (_DRM_LOCKING_CONTEXT(old) == context) {
+ if (old & _DRM_LOCK_HELD) {
+ if (context != DRM_KERNEL_CONTEXT) {
+ DRM_ERROR("%d holds heavyweight lock\n",
+ context);
+ }
+ return 0;
}
- return 0;
}
- return !_DRM_LOCK_IS_HELD(old);
+ if ((_DRM_LOCKING_CONTEXT(new)) == context && (new & _DRM_LOCK_HELD)) {
+ /* Have lock */
+
+ return 1;
+ }
+ return 0;
}
/**
@@ -379,13 +388,15 @@ EXPORT_SYMBOL(drm_idlelock_release);
* Takes the lock on behalf of the client if needed, using the kernel context.
*
* This allows us to hide the hardware lock when it's required for protection
- * of data structures (such as command ringbuffer) shared with the X Server, and
+ * of data structures (such as command ringbuffer) shared with the X Server, an
+
* a way for us to transition to lockless for those requests when the X Server
* stops accessing the ringbuffer directly, without having to update the
* other userland clients.
*/
int drm_client_lock_take(struct drm_device *dev, struct drm_file *file_priv)
{
+ struct drm_master *master = file_priv->master;
int ret;
unsigned long irqflags;
@@ -398,45 +409,46 @@ int drm_client_lock_take(struct drm_device *dev, struct drm_file *file_priv)
* context on behalf of the client, and return whether we were
* successful.
*/
- spin_lock_irqsave(&dev->lock.spinlock, irqflags);
- dev->lock.user_waiters++;
- spin_unlock_irqrestore(&dev->lock.spinlock, irqflags);
- ret = wait_event_interruptible(dev->lock.lock_queue,
- drm_lock_take(&dev->lock,
+ spin_lock_irqsave(&master->lock.spinlock, irqflags);
+ master->lock.user_waiters++;
+ spin_unlock_irqrestore(&master->lock.spinlock, irqflags);
+ ret = wait_event_interruptible(master->lock.lock_queue,
+ drm_lock_take(&master->lock,
DRM_KERNEL_CONTEXT));
- spin_lock_irqsave(&dev->lock.spinlock, irqflags);
- dev->lock.user_waiters--;
+ spin_lock_irqsave(&master->lock.spinlock, irqflags);
+ master->lock.user_waiters--;
if (ret != 0) {
- spin_unlock_irqrestore(&dev->lock.spinlock, irqflags);
+ spin_unlock_irqrestore(&master->lock.spinlock, irqflags);
} else {
- dev->lock.file_priv = file_priv;
- dev->lock.lock_time = jiffies;
- dev->lock.kernel_held = 1;
+ master->lock.file_priv = file_priv;
+ master->lock.lock_time = jiffies;
+ master->lock.kernel_held = 1;
file_priv->lock_count++;
- spin_unlock_irqrestore(&dev->lock.spinlock, irqflags);
+ spin_unlock_irqrestore(&master->lock.spinlock, irqflags);
}
mutex_lock (&dev->struct_mutex);
return ret;
}
EXPORT_SYMBOL(drm_client_lock_take);
-void drm_client_lock_release(struct drm_device *dev)
+void drm_client_lock_release(struct drm_device *dev, struct drm_file *file_priv)
{
- if (dev->lock.kernel_held) {
- dev->lock.kernel_held = 0;
- dev->lock.file_priv = NULL;
- drm_lock_free(&dev->lock, DRM_KERNEL_CONTEXT);
+ struct drm_master *master = file_priv->master;
+
+ if (master->lock.kernel_held) {
+ master->lock.kernel_held = 0;
+ master->lock.file_priv = NULL;
+ drm_lock_free(&master->lock, DRM_KERNEL_CONTEXT);
}
}
EXPORT_SYMBOL(drm_client_lock_release);
-
int drm_i_have_hw_lock(struct drm_device *dev, struct drm_file *file_priv)
{
-
- return (file_priv->lock_count && dev->lock.hw_lock &&
- _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) &&
- dev->lock.file_priv == file_priv);
+ struct drm_master *master = file_priv->master;
+ return (file_priv->lock_count && master->lock.hw_lock &&
+ _DRM_LOCK_IS_HELD(master->lock.hw_lock->lock) &&
+ master->lock.file_priv == file_priv);
}
EXPORT_SYMBOL(drm_i_have_hw_lock);
diff --git a/linux-core/drm_memrange.c b/linux-core/drm_memrange.c
index 7014c4e2..5921eff8 100644
--- a/linux-core/drm_memrange.c
+++ b/linux-core/drm_memrange.c
@@ -294,5 +294,4 @@ void drm_memrange_takedown(struct drm_memrange * mm)
list_del(&entry->ml_entry);
drm_ctl_free(entry, sizeof(*entry), DRM_MEM_MM);
}
-
EXPORT_SYMBOL(drm_memrange_takedown);
diff --git a/linux-core/drm_modes.c b/linux-core/drm_modes.c
new file mode 100644
index 00000000..3ed427fb
--- /dev/null
+++ b/linux-core/drm_modes.c
@@ -0,0 +1,566 @@
+/*
+ * Copyright © 1997-2003 by The XFree86 Project, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of the copyright holder(s)
+ * and author(s) shall not be used in advertising or otherwise to promote
+ * the sale, use or other dealings in this Software without prior written
+ * authorization from the copyright holder(s) and author(s).
+ */
+/*
+ * Copyright © 2007 Dave Airlie
+ */
+
+#include <linux/list.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+
+/**
+ * drm_mode_debug_printmodeline - debug print a mode
+ * @dev: DRM device
+ * @mode: mode to print
+ *
+ * LOCKING:
+ * None.
+ *
+ * Describe @mode using DRM_DEBUG.
+ */
+void drm_mode_debug_printmodeline(struct drm_display_mode *mode)
+{
+ DRM_DEBUG("Modeline %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x\n",
+ mode->base.id, mode->name, mode->vrefresh, mode->clock,
+ mode->hdisplay, mode->hsync_start,
+ mode->hsync_end, mode->htotal,
+ mode->vdisplay, mode->vsync_start,
+ mode->vsync_end, mode->vtotal, mode->type, mode->flags);
+}
+EXPORT_SYMBOL(drm_mode_debug_printmodeline);
+
+/**
+ * drm_mode_set_name - set the name on a mode
+ * @mode: name will be set in this mode
+ *
+ * LOCKING:
+ * None.
+ *
+ * Set the name of @mode to a standard format.
+ */
+void drm_mode_set_name(struct drm_display_mode *mode)
+{
+ snprintf(mode->name, DRM_DISPLAY_MODE_LEN, "%dx%d", mode->hdisplay,
+ mode->vdisplay);
+}
+EXPORT_SYMBOL(drm_mode_set_name);
+
+/**
+ * drm_mode_list_concat - move modes from one list to another
+ * @head: source list
+ * @new: dst list
+ *
+ * LOCKING:
+ * Caller must ensure both lists are locked.
+ *
+ * Move all the modes from @head to @new.
+ */
+void drm_mode_list_concat(struct list_head *head, struct list_head *new)
+{
+
+ struct list_head *entry, *tmp;
+
+ list_for_each_safe(entry, tmp, head) {
+ list_move_tail(entry, new);
+ }
+}
+
+/**
+ * drm_mode_width - get the width of a mode
+ * @mode: mode
+ *
+ * LOCKING:
+ * None.
+ *
+ * Return @mode's width (hdisplay) value.
+ *
+ * FIXME: is this needed?
+ *
+ * RETURNS:
+ * @mode->hdisplay
+ */
+int drm_mode_width(struct drm_display_mode *mode)
+{
+ return mode->hdisplay;
+
+}
+EXPORT_SYMBOL(drm_mode_width);
+
+/**
+ * drm_mode_height - get the height of a mode
+ * @mode: mode
+ *
+ * LOCKING:
+ * None.
+ *
+ * Return @mode's height (vdisplay) value.
+ *
+ * FIXME: is this needed?
+ *
+ * RETURNS:
+ * @mode->vdisplay
+ */
+int drm_mode_height(struct drm_display_mode *mode)
+{
+ return mode->vdisplay;
+}
+EXPORT_SYMBOL(drm_mode_height);
+
+/**
+ * drm_mode_vrefresh - get the vrefresh of a mode
+ * @mode: mode
+ *
+ * LOCKING:
+ * None.
+ *
+ * Return @mode's vrefresh rate or calculate it if necessary.
+ *
+ * FIXME: why is this needed? shouldn't vrefresh be set already?
+ *
+ * RETURNS:
+ * Vertical refresh rate of @mode x 1000. For precision reasons.
+ */
+int drm_mode_vrefresh(struct drm_display_mode *mode)
+{
+ int refresh = 0;
+ unsigned int calc_val;
+
+ if (mode->vrefresh > 0)
+ refresh = mode->vrefresh;
+ else if (mode->htotal > 0 && mode->vtotal > 0) {
+ /* work out vrefresh the value will be x1000 */
+ calc_val = (mode->clock * 1000);
+
+ calc_val /= mode->htotal;
+ calc_val *= 1000;
+ calc_val /= mode->vtotal;
+
+ refresh = calc_val;
+ if (mode->flags & V_INTERLACE)
+ refresh *= 2;
+ if (mode->flags & V_DBLSCAN)
+ refresh /= 2;
+ if (mode->vscan > 1)
+ refresh /= mode->vscan;
+ }
+ return refresh;
+}
+EXPORT_SYMBOL(drm_mode_vrefresh);
+
+/**
+ * drm_mode_set_crtcinfo - set CRTC modesetting parameters
+ * @p: mode
+ * @adjust_flags: unused? (FIXME)
+ *
+ * LOCKING:
+ * None.
+ *
+ * Setup the CRTC modesetting parameters for @p, adjusting if necessary.
+ */
+void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags)
+{
+ if ((p == NULL) || ((p->type & DRM_MODE_TYPE_CRTC_C) == DRM_MODE_TYPE_BUILTIN))
+ return;
+
+ p->crtc_hdisplay = p->hdisplay;
+ p->crtc_hsync_start = p->hsync_start;
+ p->crtc_hsync_end = p->hsync_end;
+ p->crtc_htotal = p->htotal;
+ p->crtc_hskew = p->hskew;
+ p->crtc_vdisplay = p->vdisplay;
+ p->crtc_vsync_start = p->vsync_start;
+ p->crtc_vsync_end = p->vsync_end;
+ p->crtc_vtotal = p->vtotal;
+
+ if (p->flags & V_INTERLACE) {
+ if (adjust_flags & CRTC_INTERLACE_HALVE_V) {
+ p->crtc_vdisplay /= 2;
+ p->crtc_vsync_start /= 2;
+ p->crtc_vsync_end /= 2;
+ p->crtc_vtotal /= 2;
+ }
+
+ p->crtc_vtotal |= 1;
+ }
+
+ if (p->flags & V_DBLSCAN) {
+ p->crtc_vdisplay *= 2;
+ p->crtc_vsync_start *= 2;
+ p->crtc_vsync_end *= 2;
+ p->crtc_vtotal *= 2;
+ }
+
+ if (p->vscan > 1) {
+ p->crtc_vdisplay *= p->vscan;
+ p->crtc_vsync_start *= p->vscan;
+ p->crtc_vsync_end *= p->vscan;
+ p->crtc_vtotal *= p->vscan;
+ }
+
+ p->crtc_vblank_start = min(p->crtc_vsync_start, p->crtc_vdisplay);
+ p->crtc_vblank_end = max(p->crtc_vsync_end, p->crtc_vtotal);
+ p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay);
+ p->crtc_hblank_end = max(p->crtc_hsync_end, p->crtc_htotal);
+
+ p->crtc_hadjusted = false;
+ p->crtc_vadjusted = false;
+}
+EXPORT_SYMBOL(drm_mode_set_crtcinfo);
+
+
+/**
+ * drm_mode_duplicate - allocate and duplicate an existing mode
+ * @m: mode to duplicate
+ *
+ * LOCKING:
+ * None.
+ *
+ * Just allocate a new mode, copy the existing mode into it, and return
+ * a pointer to it. Used to create new instances of established modes.
+ */
+struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev,
+ struct drm_display_mode *mode)
+{
+ struct drm_display_mode *nmode;
+ int new_id;
+
+ nmode = drm_mode_create(dev);
+ if (!nmode)
+ return NULL;
+
+ new_id = nmode->base.id;
+ *nmode = *mode;
+ nmode->base.id = new_id;
+ INIT_LIST_HEAD(&nmode->head);
+ return nmode;
+}
+EXPORT_SYMBOL(drm_mode_duplicate);
+
+/**
+ * drm_mode_equal - test modes for equality
+ * @mode1: first mode
+ * @mode2: second mode
+ *
+ * LOCKING:
+ * None.
+ *
+ * Check to see if @mode1 and @mode2 are equivalent.
+ *
+ * RETURNS:
+ * True if the modes are equal, false otherwise.
+ */
+bool drm_mode_equal(struct drm_display_mode *mode1, struct drm_display_mode *mode2)
+{
+ /* do clock check convert to PICOS so fb modes get matched
+ * the same */
+ if (mode1->clock && mode2->clock) {
+ if (KHZ2PICOS(mode1->clock) != KHZ2PICOS(mode2->clock))
+ return false;
+ } else if (mode1->clock != mode2->clock)
+ return false;
+
+ if (mode1->hdisplay == mode2->hdisplay &&
+ mode1->hsync_start == mode2->hsync_start &&
+ mode1->hsync_end == mode2->hsync_end &&
+ mode1->htotal == mode2->htotal &&
+ mode1->hskew == mode2->hskew &&
+ mode1->vdisplay == mode2->vdisplay &&
+ mode1->vsync_start == mode2->vsync_start &&
+ mode1->vsync_end == mode2->vsync_end &&
+ mode1->vtotal == mode2->vtotal &&
+ mode1->vscan == mode2->vscan &&
+ mode1->flags == mode2->flags)
+ return true;
+
+ return false;
+}
+EXPORT_SYMBOL(drm_mode_equal);
+
+/**
+ * drm_mode_validate_size - make sure modes adhere to size constraints
+ * @dev: DRM device
+ * @mode_list: list of modes to check
+ * @maxX: maximum width
+ * @maxY: maximum height
+ * @maxPitch: max pitch
+ *
+ * LOCKING:
+ * Caller must hold a lock protecting @mode_list.
+ *
+ * The DRM device (@dev) has size and pitch limits. Here we validate the
+ * modes we probed for @dev against those limits and set their status as
+ * necessary.
+ */
+void drm_mode_validate_size(struct drm_device *dev,
+ struct list_head *mode_list,
+ int maxX, int maxY, int maxPitch)
+{
+ struct drm_display_mode *mode;
+
+ list_for_each_entry(mode, mode_list, head) {
+ if (maxPitch > 0 && mode->hdisplay > maxPitch)
+ mode->status = MODE_BAD_WIDTH;
+
+ if (maxX > 0 && mode->hdisplay > maxX)
+ mode->status = MODE_VIRTUAL_X;
+
+ if (maxY > 0 && mode->vdisplay > maxY)
+ mode->status = MODE_VIRTUAL_Y;
+ }
+}
+EXPORT_SYMBOL(drm_mode_validate_size);
+
+/**
+ * drm_mode_validate_clocks - validate modes against clock limits
+ * @dev: DRM device
+ * @mode_list: list of modes to check
+ * @min: minimum clock rate array
+ * @max: maximum clock rate array
+ * @n_ranges: number of clock ranges (size of arrays)
+ *
+ * LOCKING:
+ * Caller must hold a lock protecting @mode_list.
+ *
+ * Some code may need to check a mode list against the clock limits of the
+ * device in question. This function walks the mode list, testing to make
+ * sure each mode falls within a given range (defined by @min and @max
+ * arrays) and sets @mode->status as needed.
+ */
+void drm_mode_validate_clocks(struct drm_device *dev,
+ struct list_head *mode_list,
+ int *min, int *max, int n_ranges)
+{
+ struct drm_display_mode *mode;
+ int i;
+
+ list_for_each_entry(mode, mode_list, head) {
+ bool good = false;
+ for (i = 0; i < n_ranges; i++) {
+ if (mode->clock >= min[i] && mode->clock <= max[i]) {
+ good = true;
+ break;
+ }
+ }
+ if (!good)
+ mode->status = MODE_CLOCK_RANGE;
+ }
+}
+EXPORT_SYMBOL(drm_mode_validate_clocks);
+
+/**
+ * drm_mode_prune_invalid - remove invalid modes from mode list
+ * @dev: DRM device
+ * @mode_list: list of modes to check
+ * @verbose: be verbose about it
+ *
+ * LOCKING:
+ * Caller must hold a lock protecting @mode_list.
+ *
+ * Once mode list generation is complete, a caller can use this routine to
+ * remove invalid modes from a mode list. If any of the modes have a
+ * status other than %MODE_OK, they are removed from @mode_list and freed.
+ */
+void drm_mode_prune_invalid(struct drm_device *dev,
+ struct list_head *mode_list, bool verbose)
+{
+ struct drm_display_mode *mode, *t;
+
+ list_for_each_entry_safe(mode, t, mode_list, head) {
+ if (mode->status != MODE_OK) {
+ list_del(&mode->head);
+ if (verbose) {
+ drm_mode_debug_printmodeline(mode);
+ DRM_DEBUG("Not using %s mode %d\n", mode->name, mode->status);
+ }
+ kfree(mode);
+ }
+ }
+}
+
+/**
+ * drm_mode_compare - compare modes for favorability
+ * @lh_a: list_head for first mode
+ * @lh_b: list_head for second mode
+ *
+ * LOCKING:
+ * None.
+ *
+ * Compare two modes, given by @lh_a and @lh_b, returning a value indicating
+ * which is better.
+ *
+ * RETURNS:
+ * Negative if @lh_a is better than @lh_b, zero if they're equivalent, or
+ * positive if @lh_b is better than @lh_a.
+ */
+static int drm_mode_compare(struct list_head *lh_a, struct list_head *lh_b)
+{
+ struct drm_display_mode *a = list_entry(lh_a, struct drm_display_mode, head);
+ struct drm_display_mode *b = list_entry(lh_b, struct drm_display_mode, head);
+ int diff;
+
+ diff = ((b->type & DRM_MODE_TYPE_PREFERRED) != 0) -
+ ((a->type & DRM_MODE_TYPE_PREFERRED) != 0);
+ if (diff)
+ return diff;
+ diff = b->hdisplay * b->vdisplay - a->hdisplay * a->vdisplay;
+ if (diff)
+ return diff;
+ diff = b->clock - a->clock;
+ return diff;
+}
+
+/* FIXME: what we don't have a list sort function? */
+/* list sort from Mark J Roberts (mjr@znex.org) */
+void list_sort(struct list_head *head, int (*cmp)(struct list_head *a, struct list_head *b))
+{
+ struct list_head *p, *q, *e, *list, *tail, *oldhead;
+ int insize, nmerges, psize, qsize, i;
+
+ list = head->next;
+ list_del(head);
+ insize = 1;
+ for (;;) {
+ p = oldhead = list;
+ list = tail = NULL;
+ nmerges = 0;
+
+ while (p) {
+ nmerges++;
+ q = p;
+ psize = 0;
+ for (i = 0; i < insize; i++) {
+ psize++;
+ q = q->next == oldhead ? NULL : q->next;
+ if (!q)
+ break;
+ }
+
+ qsize = insize;
+ while (psize > 0 || (qsize > 0 && q)) {
+ if (!psize) {
+ e = q;
+ q = q->next;
+ qsize--;
+ if (q == oldhead)
+ q = NULL;
+ } else if (!qsize || !q) {
+ e = p;
+ p = p->next;
+ psize--;
+ if (p == oldhead)
+ p = NULL;
+ } else if (cmp(p, q) <= 0) {
+ e = p;
+ p = p->next;
+ psize--;
+ if (p == oldhead)
+ p = NULL;
+ } else {
+ e = q;
+ q = q->next;
+ qsize--;
+ if (q == oldhead)
+ q = NULL;
+ }
+ if (tail)
+ tail->next = e;
+ else
+ list = e;
+ e->prev = tail;
+ tail = e;
+ }
+ p = q;
+ }
+
+ tail->next = list;
+ list->prev = tail;
+
+ if (nmerges <= 1)
+ break;
+
+ insize *= 2;
+ }
+
+ head->next = list;
+ head->prev = list->prev;
+ list->prev->next = head;
+ list->prev = head;
+}
+
+/**
+ * drm_mode_sort - sort mode list
+ * @mode_list: list to sort
+ *
+ * LOCKING:
+ * Caller must hold a lock protecting @mode_list.
+ *
+ * Sort @mode_list by favorability, putting good modes first.
+ */
+void drm_mode_sort(struct list_head *mode_list)
+{
+ list_sort(mode_list, drm_mode_compare);
+}
+
+
+/**
+ * drm_mode_connector_list_update - update the mode list for the connector
+ * @connector: the connector to update
+ *
+ * LOCKING:
+ * Caller must hold a lock protecting @mode_list.
+ *
+ * This moves the modes from the @connector probed_modes list
+ * to the actual mode list. It compares the probed mode against the current
+ * list and only adds different modes. All modes unverified after this point
+ * will be removed by the prune invalid modes.
+ */
+void drm_mode_connector_list_update(struct drm_connector *connector)
+{
+ struct drm_display_mode *mode;
+ struct drm_display_mode *pmode, *pt;
+ int found_it;
+ list_for_each_entry_safe(pmode, pt, &connector->probed_modes,
+ head) {
+ found_it = 0;
+ /* go through current modes checking for the new probed mode */
+ list_for_each_entry(mode, &connector->modes, head) {
+ if (drm_mode_equal(pmode, mode)) {
+ found_it = 1;
+ /* if equal delete the probed mode */
+ mode->status = pmode->status;
+ list_del(&pmode->head);
+ kfree(pmode);
+ break;
+ }
+ }
+
+ if (!found_it) {
+ list_move_tail(&pmode->head, &connector->modes);
+ }
+ }
+}
diff --git a/linux-core/drm_objects.h b/linux-core/drm_objects.h
index 6ec09ef8..d0c34ca3 100644
--- a/linux-core/drm_objects.h
+++ b/linux-core/drm_objects.h
@@ -156,7 +156,6 @@ struct drm_fence_object {
};
#define _DRM_FENCE_CLASSES 8
-#define _DRM_FENCE_TYPE_EXE 0x00
struct drm_fence_class_manager {
struct list_head ring;
@@ -656,12 +655,12 @@ struct drm_bo_driver {
/*
* buffer objects (drm_bo.c)
*/
-
extern int drm_bo_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
extern int drm_bo_destroy_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
extern int drm_bo_map_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
extern int drm_bo_unmap_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
extern int drm_bo_reference_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
+extern int drm_bo_set_pin(struct drm_device *dev, struct drm_buffer_object *bo, int pin);
extern int drm_bo_unreference_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
extern int drm_bo_wait_idle_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
extern int drm_bo_info_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
@@ -805,6 +804,10 @@ extern void drm_regs_init(struct drm_reg_manager *manager,
const void *),
void (*reg_destroy)(struct drm_reg *));
+extern int drm_mem_reg_ioremap(struct drm_device *dev, struct drm_bo_mem_reg * mem,
+ void **virtual);
+extern void drm_mem_reg_iounmap(struct drm_device *dev, struct drm_bo_mem_reg * mem,
+ void *virtual);
/*
* drm_bo_lock.c
* Simple replacement for the hardware lock on buffer manager init and clean.
diff --git a/linux-core/drm_proc.c b/linux-core/drm_proc.c
index b1b976b2..690e081c 100644
--- a/linux-core/drm_proc.c
+++ b/linux-core/drm_proc.c
@@ -172,6 +172,7 @@ static int drm_name_info(char *buf, char **start, off_t offset, int request,
int *eof, void *data)
{
struct drm_minor *minor = (struct drm_minor *) data;
+ struct drm_master *master = minor->master;
struct drm_device *dev = minor->dev;
int len = 0;
@@ -180,13 +181,16 @@ static int drm_name_info(char *buf, char **start, off_t offset, int request,
return 0;
}
+ if (!master)
+ return 0;
+
*start = &buf[offset];
*eof = 0;
- if (dev->unique) {
+ if (master->unique) {
DRM_PROC_PRINT("%s %s %s\n",
dev->driver->pci_driver.name,
- pci_name(dev->pdev), dev->unique);
+ pci_name(dev->pdev), master->unique);
} else {
DRM_PROC_PRINT("%s %s\n", dev->driver->pci_driver.name,
pci_name(dev->pdev));
diff --git a/linux-core/drm_stub.c b/linux-core/drm_stub.c
index 55841826..dc853b23 100644
--- a/linux-core/drm_stub.c
+++ b/linux-core/drm_stub.c
@@ -58,6 +58,14 @@ static int drm_minor_get_id(struct drm_device *dev, int type)
int ret;
int base = 0, limit = 63;
+ if (type == DRM_MINOR_CONTROL) {
+ base += 64;
+ limit = base + 127;
+ } else if (type == DRM_MINOR_RENDER) {
+ base += 128;
+ limit = base + 255;
+ }
+
again:
if (idr_pre_get(&drm_minors_idr, GFP_KERNEL) == 0) {
DRM_ERROR("Out of memory expanding drawable idr\n");
@@ -80,21 +88,99 @@ again:
return new_id;
}
+int drm_setmaster_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ if (file_priv->minor->master && file_priv->minor->master != file_priv->master)
+ return -EINVAL;
+
+ if (!file_priv->master)
+ return -EINVAL;
+
+ if (!file_priv->minor->master && file_priv->minor->master != file_priv->master)
+ file_priv->minor->master = file_priv->master;
+ return 0;
+}
+
+int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ if (!file_priv->master)
+ return -EINVAL;
+ file_priv->minor->master = NULL;
+ return 0;
+}
+
+struct drm_master *drm_get_master(struct drm_minor *minor)
+{
+ struct drm_master *master;
+
+ master = drm_calloc(1, sizeof(*master), DRM_MEM_DRIVER);
+ if (!master)
+ return NULL;
+
+// INIT_LIST_HEAD(&master->filelist);
+ spin_lock_init(&master->lock.spinlock);
+ init_waitqueue_head(&master->lock.lock_queue);
+ drm_ht_create(&master->magiclist, DRM_MAGIC_HASH_ORDER);
+ INIT_LIST_HEAD(&master->magicfree);
+ master->minor = minor;
+
+ list_add_tail(&master->head, &minor->master_list);
+
+ return master;
+}
+
+void drm_put_master(struct drm_master *master)
+{
+ struct drm_magic_entry *pt, *next;
+ struct drm_device *dev = master->minor->dev;
+
+ list_del(&master->head);
+
+ if (dev->driver->master_destroy)
+ dev->driver->master_destroy(dev, master);
+
+ if (master->unique) {
+ drm_free(master->unique, strlen(master->unique) + 1, DRM_MEM_DRIVER);
+ master->unique = NULL;
+ master->unique_len = 0;
+ }
+
+ list_for_each_entry_safe(pt, next, &master->magicfree, head) {
+ list_del(&pt->head);
+ drm_ht_remove_item(&master->magiclist, &pt->hash_item);
+ drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+ }
+
+ drm_ht_remove(&master->magiclist);
+
+ if (master->lock.hw_lock) {
+ if (dev->sigdata.lock == master->lock.hw_lock)
+ dev->sigdata.lock = NULL;
+ master->lock.hw_lock = NULL; /* SHM removed */
+ master->lock.file_priv = NULL;
+ wake_up_interruptible(&master->lock.lock_queue);
+ }
+
+ drm_free(master, sizeof(*master), DRM_MEM_DRIVER);
+}
+
static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,
const struct pci_device_id *ent,
struct drm_driver *driver)
{
int retcode;
- INIT_LIST_HEAD(&dev->filelist);
INIT_LIST_HEAD(&dev->ctxlist);
INIT_LIST_HEAD(&dev->vmalist);
INIT_LIST_HEAD(&dev->maplist);
+ INIT_LIST_HEAD(&dev->filelist);
spin_lock_init(&dev->count_lock);
spin_lock_init(&dev->drw_lock);
spin_lock_init(&dev->tasklet_lock);
- spin_lock_init(&dev->lock.spinlock);
+// spin_lock_init(&dev->lock.spinlock);
init_timer(&dev->timer);
mutex_init(&dev->struct_mutex);
mutex_init(&dev->ctxlist_mutex);
@@ -112,9 +198,9 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,
dev->irq = pdev->irq;
dev->irq_enabled = 0;
- if (drm_ht_create(&dev->map_hash, DRM_MAP_HASH_ORDER)) {
+ if (drm_ht_create(&dev->map_hash, DRM_MAP_HASH_ORDER))
return -ENOMEM;
- }
+
if (drm_memrange_init(&dev->offset_manager, DRM_FILE_PAGE_OFFSET_START,
DRM_FILE_PAGE_OFFSET_SIZE)) {
drm_ht_remove(&dev->map_hash);
@@ -213,6 +299,7 @@ static int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int t
new_minor->device = MKDEV(DRM_MAJOR, minor_id);
new_minor->dev = dev;
new_minor->index = minor_id;
+ INIT_LIST_HEAD(&new_minor->master_list);
idr_replace(&drm_minors_idr, new_minor, minor_id);
@@ -289,20 +376,32 @@ int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
}
/* only add the control node on a modesetting platform */
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ if ((ret = drm_get_minor(dev, &dev->control, DRM_MINOR_CONTROL)))
+ goto err_g3;
+
if ((ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY)))
- goto err_g3;
+ goto err_g4;
if (dev->driver->load)
if ((ret = dev->driver->load(dev, ent->driver_data)))
- goto err_g4;
+ goto err_g5;
+
+ /* setup the grouping for the legacy output */
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ if (drm_mode_group_init_legacy_group(dev, &dev->primary->mode_group))
+ goto err_g5;
DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
driver->name, driver->major, driver->minor, driver->patchlevel,
driver->date, dev->primary->index);
return 0;
-err_g4:
+err_g5:
drm_put_minor(&dev->primary);
+err_g4:
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ drm_put_minor(&dev->control);
err_g3:
if (!drm_fb_loaded)
pci_disable_device(pdev);
@@ -334,11 +433,6 @@ int drm_put_dev(struct drm_device * dev)
{
DRM_DEBUG("release primary %s\n", dev->driver->pci_driver.name);
- if (dev->unique) {
- drm_free(dev->unique, strlen(dev->unique) + 1, DRM_MEM_DRIVER);
- dev->unique = NULL;
- dev->unique_len = 0;
- }
if (dev->devname) {
drm_free(dev->devname, strlen(dev->devname) + 1,
DRM_MEM_DRIVER);
diff --git a/linux-core/drm_sysfs.c b/linux-core/drm_sysfs.c
index 32759424..92371c22 100644
--- a/linux-core/drm_sysfs.c
+++ b/linux-core/drm_sysfs.c
@@ -36,8 +36,9 @@ static int drm_sysfs_suspend(struct device *dev, pm_message_t state)
printk(KERN_ERR "%s\n", __FUNCTION__);
- if (drm_dev->driver->suspend)
- return drm_dev->driver->suspend(drm_dev, state);
+ if (drm_minor->type == DRM_MINOR_CONTROL)
+ if (drm_dev->driver->suspend)
+ return drm_dev->driver->suspend(drm_dev, state);
return 0;
}
@@ -54,8 +55,9 @@ static int drm_sysfs_resume(struct device *dev)
struct drm_minor *drm_minor = to_drm_minor(dev);
struct drm_device *drm_dev = drm_minor->dev;
- if (drm_dev->driver->resume)
- return drm_dev->driver->resume(drm_dev);
+ if (drm_minor->type == DRM_MINOR_CONTROL)
+ if (drm_dev->driver->resume)
+ return drm_dev->driver->resume(drm_dev);
return 0;
}
@@ -131,10 +133,6 @@ static ssize_t show_dri(struct device *device, struct device_attribute *attr,
return snprintf(buf, PAGE_SIZE, "%s\n", drm_dev->driver->pci_driver.name);
}
-static struct device_attribute device_attrs[] = {
- __ATTR(dri_library_name, S_IRUGO, show_dri, NULL),
-};
-
/**
* drm_sysfs_device_release - do nothing
* @dev: Linux device
@@ -148,6 +146,204 @@ static void drm_sysfs_device_release(struct device *dev)
return;
}
+/*
+ * Connector properties
+ */
+static ssize_t status_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_connector *connector = container_of(device, struct drm_connector, kdev);
+ return snprintf(buf, PAGE_SIZE, "%s",
+ drm_get_connector_status_name(connector->funcs->detect(connector)));
+}
+
+static ssize_t dpms_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_connector *connector = container_of(device, struct drm_connector, kdev);
+ struct drm_device *dev = connector->dev;
+ uint64_t dpms_status;
+ int ret;
+
+ ret = drm_connector_property_get_value(connector,
+ dev->mode_config.dpms_property,
+ &dpms_status);
+ if (ret)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%s", drm_get_dpms_name((int)dpms_status));
+}
+
+static ssize_t edid_show(struct kobject *kobj, struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct device *connector_dev = container_of(kobj, struct device, kobj);
+ struct drm_connector *connector = container_of(connector_dev, struct drm_connector,
+ kdev);
+ unsigned char *edid;
+ size_t size;
+
+ if (!connector->edid_blob_ptr)
+ return 0;
+
+ edid = connector->edid_blob_ptr->data;
+ size = connector->edid_blob_ptr->length;
+ if (!edid)
+ return 0;
+
+ if (off >= size)
+ return 0;
+
+ if (off + count > size)
+ count = size - off;
+ memcpy(buf, edid + off, count);
+
+ return count;
+}
+
+static ssize_t modes_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_connector *connector = container_of(device, struct drm_connector, kdev);
+ struct drm_display_mode *mode;
+ int written = 0;
+
+ list_for_each_entry(mode, &connector->modes, head) {
+ written += snprintf(buf + written, PAGE_SIZE - written, "%s\n",
+ mode->name);
+ }
+
+ return written;
+}
+
+static struct device_attribute connector_attrs[] = {
+ __ATTR_RO(status),
+ __ATTR_RO(dpms),
+ __ATTR_RO(modes),
+};
+
+static struct bin_attribute edid_attr = {
+ .attr.name = "edid",
+ .size = 128,
+ .read = edid_show,
+};
+
+/**
+ * drm_sysfs_connector_add - add an connector to sysfs
+ * @connector: connector to add
+ *
+ * Create an connector device in sysfs, along with its associated connector
+ * properties (so far, connection status, dpms, mode list & edid) and
+ * generate a hotplug event so userspace knows there's a new connector
+ * available.
+ *
+ * Note:
+ * This routine should only be called *once* for each DRM minor registered.
+ * A second call for an already registered device will trigger the BUG_ON
+ * below.
+ */
+int drm_sysfs_connector_add(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ int ret = 0, i, j;
+
+ /* We shouldn't get called more than once for the same connector */
+ BUG_ON(device_is_registered(&connector->kdev));
+
+ connector->kdev.parent = &dev->primary->kdev;
+ connector->kdev.class = drm_class;
+ connector->kdev.release = drm_sysfs_device_release;
+
+ DRM_DEBUG("adding \"%s\" to sysfs\n",
+ drm_get_connector_name(connector));
+
+ snprintf(connector->kdev.bus_id, BUS_ID_SIZE, "card%d-%s",
+ dev->primary->index, drm_get_connector_name(connector));
+ ret = device_register(&connector->kdev);
+
+ if (ret) {
+ DRM_ERROR("failed to register connector device: %d\n", ret);
+ goto out;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(connector_attrs); i++) {
+ ret = device_create_file(&connector->kdev, &connector_attrs[i]);
+ if (ret)
+ goto err_out_files;
+ }
+
+ ret = sysfs_create_bin_file(&connector->kdev.kobj, &edid_attr);
+ if (ret)
+ goto err_out_files;
+
+ /* Let userspace know we have a new connector */
+ drm_sysfs_hotplug_event(dev);
+
+ return 0;
+
+err_out_files:
+ if (i > 0)
+ for (j = 0; j < i; j++)
+ device_remove_file(&connector->kdev, &connector_attrs[i]);
+ device_unregister(&connector->kdev);
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL(drm_sysfs_connector_add);
+
+/**
+ * drm_sysfs_connector_remove - remove an connector device from sysfs
+ * @connector: connector to remove
+ *
+ * Remove @connector and its associated attributes from sysfs. Note that
+ * the device model core will take care of sending the "remove" uevent
+ * at this time, so we don't need to do it.
+ *
+ * Note:
+ * This routine should only be called if the connector was previously
+ * successfully registered. If @connector hasn't been registered yet,
+ * you'll likely see a panic somewhere deep in sysfs code when called.
+ */
+void drm_sysfs_connector_remove(struct drm_connector *connector)
+{
+ int i;
+
+ DRM_DEBUG("removing \"%s\" from sysfs\n",
+ drm_get_connector_name(connector));
+
+ for (i = 0; i < ARRAY_SIZE(connector_attrs); i++)
+ device_remove_file(&connector->kdev, &connector_attrs[i]);
+ sysfs_remove_bin_file(&connector->kdev.kobj, &edid_attr);
+ device_unregister(&connector->kdev);
+}
+EXPORT_SYMBOL(drm_sysfs_connector_remove);
+
+/**
+ * drm_sysfs_hotplug_event - generate a DRM uevent
+ * @dev: DRM device
+ *
+ * Send a uevent for the DRM device specified by @dev. Currently we only
+ * set HOTPLUG=1 in the uevent environment, but this could be expanded to
+ * deal with other types of events.
+ */
+void drm_sysfs_hotplug_event(struct drm_device *dev)
+{
+ char *event_string = "HOTPLUG=1";
+ char *envp[] = { event_string, NULL };
+
+ DRM_DEBUG("generating hotplug event\n");
+
+ kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, envp);
+}
+
+static struct device_attribute dri_attrs[] = {
+ __ATTR(dri_library_name, S_IRUGO, show_dri, NULL),
+};
+
/**
* drm_sysfs_device_add - adds a class device to sysfs for a character driver
* @dev: DRM device to be added
@@ -156,6 +352,11 @@ static void drm_sysfs_device_release(struct device *dev)
* Add a DRM device to the DRM's device model class. We use @dev's PCI device
* as the parent for the Linux device, and make sure it has a file containing
* the driver we're using (for userspace compatibility).
+ *
+ * Note:
+ * This routine should only be called *once* for each DRM minor registered.
+ * A second call for an already registered device will trigger the BUG_ON
+ * below.
*/
int drm_sysfs_device_add(struct drm_minor *minor)
{
@@ -167,18 +368,28 @@ int drm_sysfs_device_add(struct drm_minor *minor)
minor->kdev.class = drm_class;
minor->kdev.release = drm_sysfs_device_release;
minor->kdev.devt = minor->device;
- minor_str = "card%d";
+ if (minor->type == DRM_MINOR_CONTROL)
+ minor_str = "controlD%d";
+ else if (minor->type == DRM_MINOR_RENDER)
+ minor_str = "renderD%d";
+ else
+ minor_str = "card%d";
snprintf(minor->kdev.bus_id, BUS_ID_SIZE, minor_str, minor->index);
+ /* Shouldn't register more than once */
+ BUG_ON(device_is_registered(&minor->kdev));
+
+ DRM_DEBUG("registering DRM device \"%s\"\n", minor->kdev.bus_id);
+
err = device_register(&minor->kdev);
if (err) {
DRM_ERROR("device add failed: %d\n", err);
goto err_out;
}
- for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
- err = device_create_file(&minor->kdev, &device_attrs[i]);
+ for (i = 0; i < ARRAY_SIZE(dri_attrs); i++) {
+ err = device_create_file(&minor->kdev, &dri_attrs[i]);
if (err)
goto err_out_files;
}
@@ -188,7 +399,7 @@ int drm_sysfs_device_add(struct drm_minor *minor)
err_out_files:
if (i > 0)
for (j = 0; j < i; j++)
- device_remove_file(&minor->kdev, &device_attrs[i]);
+ device_remove_file(&minor->kdev, &dri_attrs[i]);
device_unregister(&minor->kdev);
err_out:
@@ -206,7 +417,7 @@ void drm_sysfs_device_remove(struct drm_minor *minor)
{
int i;
- for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
- device_remove_file(&minor->kdev, &device_attrs[i]);
+ for (i = 0; i < ARRAY_SIZE(dri_attrs); i++)
+ device_remove_file(&minor->kdev, &dri_attrs[i]);
device_unregister(&minor->kdev);
}
diff --git a/linux-core/dvo.h b/linux-core/dvo.h
new file mode 100644
index 00000000..b122ea1f
--- /dev/null
+++ b/linux-core/dvo.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright © 2006 Eric Anholt
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef _INTEL_DVO_H
+#define _INTEL_DVO_H
+
+#include <linux/i2c.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "intel_drv.h"
+
+struct intel_dvo_device {
+ char *name;
+ int type;
+ /* DVOA/B/C output register */
+ u32 dvo_reg;
+ /* GPIO register used for i2c bus to control this device */
+ u32 gpio;
+ int slave_addr;
+ struct intel_i2c_chan *i2c_bus;
+
+ const struct intel_dvo_dev_ops *dev_ops;
+ void *dev_priv;
+
+ struct drm_display_mode *panel_fixed_mode;
+ bool panel_wants_dither;
+};
+
+struct intel_dvo_dev_ops {
+ /*
+ * Initialize the device at startup time.
+ * Returns NULL if the device does not exist.
+ */
+ bool (*init)(struct intel_dvo_device *dvo,
+ struct intel_i2c_chan *i2cbus);
+
+ /*
+ * Called to allow the output a chance to create properties after the
+ * RandR objects have been created.
+ */
+ void (*create_resources)(struct intel_dvo_device *dvo);
+
+ /*
+ * Turn on/off output or set intermediate power levels if available.
+ *
+ * Unsupported intermediate modes drop to the lower power setting.
+ * If the mode is DPMSModeOff, the output must be disabled,
+ * as the DPLL may be disabled afterwards.
+ */
+ void (*dpms)(struct intel_dvo_device *dvo, int mode);
+
+ /*
+ * Saves the output's state for restoration on VT switch.
+ */
+ void (*save)(struct intel_dvo_device *dvo);
+
+ /*
+ * Restore's the output's state at VT switch.
+ */
+ void (*restore)(struct intel_dvo_device *dvo);
+
+ /*
+ * Callback for testing a video mode for a given output.
+ *
+ * This function should only check for cases where a mode can't
+ * be supported on the output specifically, and not represent
+ * generic CRTC limitations.
+ *
+ * \return MODE_OK if the mode is valid, or another MODE_* otherwise.
+ */
+ int (*mode_valid)(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode);
+
+ /*
+ * Callback to adjust the mode to be set in the CRTC.
+ *
+ * This allows an output to adjust the clock or even the entire set of
+ * timings, which is used for panels with fixed timings or for
+ * buses with clock limitations.
+ */
+ bool (*mode_fixup)(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
+
+ /*
+ * Callback for preparing mode changes on an output
+ */
+ void (*prepare)(struct intel_dvo_device *dvo);
+
+ /*
+ * Callback for committing mode changes on an output
+ */
+ void (*commit)(struct intel_dvo_device *dvo);
+
+ /*
+ * Callback for setting up a video mode after fixups have been made.
+ *
+ * This is only called while the output is disabled. The dpms callback
+ * must be all that's necessary for the output, to turn the output on
+ * after this function is called.
+ */
+ void (*mode_set)(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
+
+ /*
+ * Probe for a connected output, and return detect_status.
+ */
+ enum drm_connector_status (*detect)(struct intel_dvo_device *dvo);
+
+ /**
+ * Query the device for the modes it provides.
+ *
+ * This function may also update MonInfo, mm_width, and mm_height.
+ *
+ * \return singly-linked list of modes or NULL if no modes found.
+ */
+ struct drm_display_mode *(*get_modes)(struct intel_dvo_device *dvo);
+
+#ifdef RANDR_12_INTERFACE
+ /**
+ * Callback when an output's property has changed.
+ */
+ bool (*set_property)(struct intel_dvo_device *dvo,
+ struct drm_property *property, uint64_t val);
+#endif
+
+ /**
+ * Clean up driver-specific bits of the output
+ */
+ void (*destroy) (struct intel_dvo_device *dvo);
+
+ /**
+ * Debugging hook to dump device registers to log file
+ */
+ void (*dump_regs)(struct intel_dvo_device *dvo);
+};
+
+#endif /* _INTEL_DVO_H */
diff --git a/linux-core/dvo_ch7017.c b/linux-core/dvo_ch7017.c
new file mode 100644
index 00000000..194a7af1
--- /dev/null
+++ b/linux-core/dvo_ch7017.c
@@ -0,0 +1,454 @@
+/*
+ * Copyright © 2006 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ *
+ */
+
+#include "dvo.h"
+
+#define CH7017_TV_DISPLAY_MODE 0x00
+#define CH7017_FLICKER_FILTER 0x01
+#define CH7017_VIDEO_BANDWIDTH 0x02
+#define CH7017_TEXT_ENHANCEMENT 0x03
+#define CH7017_START_ACTIVE_VIDEO 0x04
+#define CH7017_HORIZONTAL_POSITION 0x05
+#define CH7017_VERTICAL_POSITION 0x06
+#define CH7017_BLACK_LEVEL 0x07
+#define CH7017_CONTRAST_ENHANCEMENT 0x08
+#define CH7017_TV_PLL 0x09
+#define CH7017_TV_PLL_M 0x0a
+#define CH7017_TV_PLL_N 0x0b
+#define CH7017_SUB_CARRIER_0 0x0c
+#define CH7017_CIV_CONTROL 0x10
+#define CH7017_CIV_0 0x11
+#define CH7017_CHROMA_BOOST 0x14
+#define CH7017_CLOCK_MODE 0x1c
+#define CH7017_INPUT_CLOCK 0x1d
+#define CH7017_GPIO_CONTROL 0x1e
+#define CH7017_INPUT_DATA_FORMAT 0x1f
+#define CH7017_CONNECTION_DETECT 0x20
+#define CH7017_DAC_CONTROL 0x21
+#define CH7017_BUFFERED_CLOCK_OUTPUT 0x22
+#define CH7017_DEFEAT_VSYNC 0x47
+#define CH7017_TEST_PATTERN 0x48
+
+#define CH7017_POWER_MANAGEMENT 0x49
+/** Enables the TV output path. */
+#define CH7017_TV_EN (1 << 0)
+#define CH7017_DAC0_POWER_DOWN (1 << 1)
+#define CH7017_DAC1_POWER_DOWN (1 << 2)
+#define CH7017_DAC2_POWER_DOWN (1 << 3)
+#define CH7017_DAC3_POWER_DOWN (1 << 4)
+/** Powers down the TV out block, and DAC0-3 */
+#define CH7017_TV_POWER_DOWN_EN (1 << 5)
+
+#define CH7017_VERSION_ID 0x4a
+
+#define CH7017_DEVICE_ID 0x4b
+#define CH7017_DEVICE_ID_VALUE 0x1b
+#define CH7018_DEVICE_ID_VALUE 0x1a
+#define CH7019_DEVICE_ID_VALUE 0x19
+
+#define CH7017_XCLK_D2_ADJUST 0x53
+#define CH7017_UP_SCALER_COEFF_0 0x55
+#define CH7017_UP_SCALER_COEFF_1 0x56
+#define CH7017_UP_SCALER_COEFF_2 0x57
+#define CH7017_UP_SCALER_COEFF_3 0x58
+#define CH7017_UP_SCALER_COEFF_4 0x59
+#define CH7017_UP_SCALER_VERTICAL_INC_0 0x5a
+#define CH7017_UP_SCALER_VERTICAL_INC_1 0x5b
+#define CH7017_GPIO_INVERT 0x5c
+#define CH7017_UP_SCALER_HORIZONTAL_INC_0 0x5d
+#define CH7017_UP_SCALER_HORIZONTAL_INC_1 0x5e
+
+#define CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT 0x5f
+/**< Low bits of horizontal active pixel input */
+
+#define CH7017_ACTIVE_INPUT_LINE_OUTPUT 0x60
+/** High bits of horizontal active pixel input */
+#define CH7017_LVDS_HAP_INPUT_MASK (0x7 << 0)
+/** High bits of vertical active line output */
+#define CH7017_LVDS_VAL_HIGH_MASK (0x7 << 3)
+
+#define CH7017_VERTICAL_ACTIVE_LINE_OUTPUT 0x61
+/**< Low bits of vertical active line output */
+
+#define CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT 0x62
+/**< Low bits of horizontal active pixel output */
+
+#define CH7017_LVDS_POWER_DOWN 0x63
+/** High bits of horizontal active pixel output */
+#define CH7017_LVDS_HAP_HIGH_MASK (0x7 << 0)
+/** Enables the LVDS power down state transition */
+#define CH7017_LVDS_POWER_DOWN_EN (1 << 6)
+/** Enables the LVDS upscaler */
+#define CH7017_LVDS_UPSCALER_EN (1 << 7)
+#define CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED 0x08
+
+#define CH7017_LVDS_ENCODING 0x64
+#define CH7017_LVDS_DITHER_2D (1 << 2)
+#define CH7017_LVDS_DITHER_DIS (1 << 3)
+#define CH7017_LVDS_DUAL_CHANNEL_EN (1 << 4)
+#define CH7017_LVDS_24_BIT (1 << 5)
+
+#define CH7017_LVDS_ENCODING_2 0x65
+
+#define CH7017_LVDS_PLL_CONTROL 0x66
+/** Enables the LVDS panel output path */
+#define CH7017_LVDS_PANEN (1 << 0)
+/** Enables the LVDS panel backlight */
+#define CH7017_LVDS_BKLEN (1 << 3)
+
+#define CH7017_POWER_SEQUENCING_T1 0x67
+#define CH7017_POWER_SEQUENCING_T2 0x68
+#define CH7017_POWER_SEQUENCING_T3 0x69
+#define CH7017_POWER_SEQUENCING_T4 0x6a
+#define CH7017_POWER_SEQUENCING_T5 0x6b
+#define CH7017_GPIO_DRIVER_TYPE 0x6c
+#define CH7017_GPIO_DATA 0x6d
+#define CH7017_GPIO_DIRECTION_CONTROL 0x6e
+
+#define CH7017_LVDS_PLL_FEEDBACK_DIV 0x71
+# define CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT 4
+# define CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT 0
+# define CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED 0x80
+
+#define CH7017_LVDS_PLL_VCO_CONTROL 0x72
+# define CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED 0x80
+# define CH7017_LVDS_PLL_VCO_SHIFT 4
+# define CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT 0
+
+#define CH7017_OUTPUTS_ENABLE 0x73
+# define CH7017_CHARGE_PUMP_LOW 0x0
+# define CH7017_CHARGE_PUMP_HIGH 0x3
+# define CH7017_LVDS_CHANNEL_A (1 << 3)
+# define CH7017_LVDS_CHANNEL_B (1 << 4)
+# define CH7017_TV_DAC_A (1 << 5)
+# define CH7017_TV_DAC_B (1 << 6)
+# define CH7017_DDC_SELECT_DC2 (1 << 7)
+
+#define CH7017_LVDS_OUTPUT_AMPLITUDE 0x74
+#define CH7017_LVDS_PLL_EMI_REDUCTION 0x75
+#define CH7017_LVDS_POWER_DOWN_FLICKER 0x76
+
+#define CH7017_LVDS_CONTROL_2 0x78
+# define CH7017_LOOP_FILTER_SHIFT 5
+# define CH7017_PHASE_DETECTOR_SHIFT 0
+
+#define CH7017_BANG_LIMIT_CONTROL 0x7f
+
+struct ch7017_priv {
+ uint8_t save_hapi;
+ uint8_t save_vali;
+ uint8_t save_valo;
+ uint8_t save_ailo;
+ uint8_t save_lvds_pll_vco;
+ uint8_t save_feedback_div;
+ uint8_t save_lvds_control_2;
+ uint8_t save_outputs_enable;
+ uint8_t save_lvds_power_down;
+ uint8_t save_power_management;
+};
+
+static void ch7017_dump_regs(struct intel_dvo_device *dvo);
+static void ch7017_dpms(struct intel_dvo_device *dvo, int mode);
+
+static bool ch7017_read(struct intel_dvo_device *dvo, int addr, uint8_t *val)
+{
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ u8 out_buf[2];
+ u8 in_buf[2];
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = out_buf,
+ },
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = in_buf,
+ }
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = 0;
+
+ if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
+ *val= in_buf[0];
+ return true;
+ };
+
+ return false;
+}
+
+static bool ch7017_write(struct intel_dvo_device *dvo, int addr, uint8_t val)
+{
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ uint8_t out_buf[2];
+ struct i2c_msg msg = {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 2,
+ .buf = out_buf,
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = val;
+
+ if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
+ return true;
+
+ return false;
+}
+
+/** Probes for a CH7017 on the given bus and slave address. */
+static bool ch7017_init(struct intel_dvo_device *dvo,
+ struct intel_i2c_chan *i2cbus)
+{
+ struct ch7017_priv *priv;
+ uint8_t val;
+
+ priv = kzalloc(sizeof(struct ch7017_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return false;
+
+ dvo->i2c_bus = i2cbus;
+ dvo->i2c_bus->slave_addr = dvo->slave_addr;
+ dvo->dev_priv = priv;
+
+ if (!ch7017_read(dvo, CH7017_DEVICE_ID, &val))
+ goto fail;
+
+ if (val != CH7017_DEVICE_ID_VALUE &&
+ val != CH7018_DEVICE_ID_VALUE &&
+ val != CH7019_DEVICE_ID_VALUE) {
+ DRM_DEBUG("ch701x not detected, got %d: from %s Slave %d.\n",
+ val, i2cbus->adapter.name,i2cbus->slave_addr);
+ goto fail;
+ }
+
+ return true;
+fail:
+ kfree(priv);
+ return false;
+}
+
+static enum drm_connector_status ch7017_detect(struct intel_dvo_device *dvo)
+{
+ return connector_status_unknown;
+}
+
+static enum drm_mode_status ch7017_mode_valid(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode)
+{
+ if (mode->clock > 160000)
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+}
+
+static void ch7017_mode_set(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ uint8_t lvds_pll_feedback_div, lvds_pll_vco_control;
+ uint8_t outputs_enable, lvds_control_2, lvds_power_down;
+ uint8_t horizontal_active_pixel_input;
+ uint8_t horizontal_active_pixel_output, vertical_active_line_output;
+ uint8_t active_input_line_output;
+
+ DRM_DEBUG("Registers before mode setting\n");
+ ch7017_dump_regs(dvo);
+
+ /* LVDS PLL settings from page 75 of 7017-7017ds.pdf*/
+ if (mode->clock < 100000) {
+ outputs_enable = CH7017_LVDS_CHANNEL_A | CH7017_CHARGE_PUMP_LOW;
+ lvds_pll_feedback_div = CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED |
+ (2 << CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT) |
+ (13 << CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT);
+ lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED |
+ (2 << CH7017_LVDS_PLL_VCO_SHIFT) |
+ (3 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT);
+ lvds_control_2 = (1 << CH7017_LOOP_FILTER_SHIFT) |
+ (0 << CH7017_PHASE_DETECTOR_SHIFT);
+ } else {
+ outputs_enable = CH7017_LVDS_CHANNEL_A | CH7017_CHARGE_PUMP_HIGH;
+ lvds_pll_feedback_div = CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED |
+ (2 << CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT) |
+ (3 << CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT);
+ lvds_pll_feedback_div = 35;
+ lvds_control_2 = (3 << CH7017_LOOP_FILTER_SHIFT) |
+ (0 << CH7017_PHASE_DETECTOR_SHIFT);
+ if (1) { /* XXX: dual channel panel detection. Assume yes for now. */
+ outputs_enable |= CH7017_LVDS_CHANNEL_B;
+ lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED |
+ (2 << CH7017_LVDS_PLL_VCO_SHIFT) |
+ (13 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT);
+ } else {
+ lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED |
+ (1 << CH7017_LVDS_PLL_VCO_SHIFT) |
+ (13 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT);
+ }
+ }
+
+ horizontal_active_pixel_input = mode->hdisplay & 0x00ff;
+
+ vertical_active_line_output = mode->vdisplay & 0x00ff;
+ horizontal_active_pixel_output = mode->hdisplay & 0x00ff;
+
+ active_input_line_output = ((mode->hdisplay & 0x0700) >> 8) |
+ (((mode->vdisplay & 0x0700) >> 8) << 3);
+
+ lvds_power_down = CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED |
+ (mode->hdisplay & 0x0700) >> 8;
+
+ ch7017_dpms(dvo, DPMSModeOff);
+ ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT,
+ horizontal_active_pixel_input);
+ ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT,
+ horizontal_active_pixel_output);
+ ch7017_write(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT,
+ vertical_active_line_output);
+ ch7017_write(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT,
+ active_input_line_output);
+ ch7017_write(dvo, CH7017_LVDS_PLL_VCO_CONTROL, lvds_pll_vco_control);
+ ch7017_write(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, lvds_pll_feedback_div);
+ ch7017_write(dvo, CH7017_LVDS_CONTROL_2, lvds_control_2);
+ ch7017_write(dvo, CH7017_OUTPUTS_ENABLE, outputs_enable);
+
+ /* Turn the LVDS back on with new settings. */
+ ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, lvds_power_down);
+
+ DRM_DEBUG("Registers after mode setting\n");
+ ch7017_dump_regs(dvo);
+}
+
+/* set the CH7017 power state */
+static void ch7017_dpms(struct intel_dvo_device *dvo, int mode)
+{
+ uint8_t val;
+
+ ch7017_read(dvo, CH7017_LVDS_POWER_DOWN, &val);
+
+ /* Turn off TV/VGA, and never turn it on since we don't support it. */
+ ch7017_write(dvo, CH7017_POWER_MANAGEMENT,
+ CH7017_DAC0_POWER_DOWN |
+ CH7017_DAC1_POWER_DOWN |
+ CH7017_DAC2_POWER_DOWN |
+ CH7017_DAC3_POWER_DOWN |
+ CH7017_TV_POWER_DOWN_EN);
+
+ if (mode == DPMSModeOn) {
+ /* Turn on the LVDS */
+ ch7017_write(dvo, CH7017_LVDS_POWER_DOWN,
+ val & ~CH7017_LVDS_POWER_DOWN_EN);
+ } else {
+ /* Turn off the LVDS */
+ ch7017_write(dvo, CH7017_LVDS_POWER_DOWN,
+ val | CH7017_LVDS_POWER_DOWN_EN);
+ }
+
+ /* XXX: Should actually wait for update power status somehow */
+ udelay(20000);
+}
+
+static void ch7017_dump_regs(struct intel_dvo_device *dvo)
+{
+ uint8_t val;
+
+#define DUMP(reg) \
+do { \
+ ch7017_read(dvo, reg, &val); \
+ DRM_DEBUG(#reg ": %02x\n", val); \
+} while (0)
+
+ DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT);
+ DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT);
+ DUMP(CH7017_VERTICAL_ACTIVE_LINE_OUTPUT);
+ DUMP(CH7017_ACTIVE_INPUT_LINE_OUTPUT);
+ DUMP(CH7017_LVDS_PLL_VCO_CONTROL);
+ DUMP(CH7017_LVDS_PLL_FEEDBACK_DIV);
+ DUMP(CH7017_LVDS_CONTROL_2);
+ DUMP(CH7017_OUTPUTS_ENABLE);
+ DUMP(CH7017_LVDS_POWER_DOWN);
+}
+
+static void ch7017_save(struct intel_dvo_device *dvo)
+{
+ struct ch7017_priv *priv = dvo->dev_priv;
+
+ ch7017_read(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT, &priv->save_hapi);
+ ch7017_read(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT, &priv->save_valo);
+ ch7017_read(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT, &priv->save_ailo);
+ ch7017_read(dvo, CH7017_LVDS_PLL_VCO_CONTROL, &priv->save_lvds_pll_vco);
+ ch7017_read(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, &priv->save_feedback_div);
+ ch7017_read(dvo, CH7017_LVDS_CONTROL_2, &priv->save_lvds_control_2);
+ ch7017_read(dvo, CH7017_OUTPUTS_ENABLE, &priv->save_outputs_enable);
+ ch7017_read(dvo, CH7017_LVDS_POWER_DOWN, &priv->save_lvds_power_down);
+ ch7017_read(dvo, CH7017_POWER_MANAGEMENT, &priv->save_power_management);
+}
+
+static void ch7017_restore(struct intel_dvo_device *dvo)
+{
+ struct ch7017_priv *priv = dvo->dev_priv;
+
+ /* Power down before changing mode */
+ ch7017_dpms(dvo, DPMSModeOff);
+
+ ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT, priv->save_hapi);
+ ch7017_write(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT, priv->save_valo);
+ ch7017_write(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT, priv->save_ailo);
+ ch7017_write(dvo, CH7017_LVDS_PLL_VCO_CONTROL, priv->save_lvds_pll_vco);
+ ch7017_write(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, priv->save_feedback_div);
+ ch7017_write(dvo, CH7017_LVDS_CONTROL_2, priv->save_lvds_control_2);
+ ch7017_write(dvo, CH7017_OUTPUTS_ENABLE, priv->save_outputs_enable);
+ ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, priv->save_lvds_power_down);
+ ch7017_write(dvo, CH7017_POWER_MANAGEMENT, priv->save_power_management);
+}
+
+static void ch7017_destroy(struct intel_dvo_device *dvo)
+{
+ struct ch7017_priv *priv = dvo->dev_priv;
+
+ if (priv) {
+ kfree(priv);
+ dvo->dev_priv = NULL;
+ }
+}
+
+struct intel_dvo_dev_ops ch7017_ops = {
+ .init = ch7017_init,
+ .detect = ch7017_detect,
+ .mode_valid = ch7017_mode_valid,
+ .mode_set = ch7017_mode_set,
+ .dpms = ch7017_dpms,
+ .dump_regs = ch7017_dump_regs,
+ .save = ch7017_save,
+ .restore = ch7017_restore,
+ .destroy = ch7017_destroy,
+};
diff --git a/linux-core/dvo_ch7xxx.c b/linux-core/dvo_ch7xxx.c
new file mode 100644
index 00000000..18922556
--- /dev/null
+++ b/linux-core/dvo_ch7xxx.c
@@ -0,0 +1,368 @@
+/**************************************************************************
+
+Copyright © 2006 Dave Airlie
+
+All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sub license, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice (including the
+next paragraph) shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+**************************************************************************/
+
+#include "dvo.h"
+
+#define CH7xxx_REG_VID 0x4a
+#define CH7xxx_REG_DID 0x4b
+
+#define CH7011_VID 0x83 /* 7010 as well */
+#define CH7009A_VID 0x84
+#define CH7009B_VID 0x85
+#define CH7301_VID 0x95
+
+#define CH7xxx_VID 0x84
+#define CH7xxx_DID 0x17
+
+#define CH7xxx_NUM_REGS 0x4c
+
+#define CH7xxx_CM 0x1c
+#define CH7xxx_CM_XCM (1<<0)
+#define CH7xxx_CM_MCP (1<<2)
+#define CH7xxx_INPUT_CLOCK 0x1d
+#define CH7xxx_GPIO 0x1e
+#define CH7xxx_GPIO_HPIR (1<<3)
+#define CH7xxx_IDF 0x1f
+
+#define CH7xxx_IDF_HSP (1<<3)
+#define CH7xxx_IDF_VSP (1<<4)
+
+#define CH7xxx_CONNECTION_DETECT 0x20
+#define CH7xxx_CDET_DVI (1<<5)
+
+#define CH7301_DAC_CNTL 0x21
+#define CH7301_HOTPLUG 0x23
+#define CH7xxx_TCTL 0x31
+#define CH7xxx_TVCO 0x32
+#define CH7xxx_TPCP 0x33
+#define CH7xxx_TPD 0x34
+#define CH7xxx_TPVT 0x35
+#define CH7xxx_TLPF 0x36
+#define CH7xxx_TCT 0x37
+#define CH7301_TEST_PATTERN 0x48
+
+#define CH7xxx_PM 0x49
+#define CH7xxx_PM_FPD (1<<0)
+#define CH7301_PM_DACPD0 (1<<1)
+#define CH7301_PM_DACPD1 (1<<2)
+#define CH7301_PM_DACPD2 (1<<3)
+#define CH7xxx_PM_DVIL (1<<6)
+#define CH7xxx_PM_DVIP (1<<7)
+
+#define CH7301_SYNC_POLARITY 0x56
+#define CH7301_SYNC_RGB_YUV (1<<0)
+#define CH7301_SYNC_POL_DVI (1<<5)
+
+/** @file
+ * driver for the Chrontel 7xxx DVI chip over DVO.
+ */
+
+static struct ch7xxx_id_struct {
+ uint8_t vid;
+ char *name;
+} ch7xxx_ids[] = {
+ { CH7011_VID, "CH7011" },
+ { CH7009A_VID, "CH7009A" },
+ { CH7009B_VID, "CH7009B" },
+ { CH7301_VID, "CH7301" },
+};
+
+struct ch7xxx_reg_state {
+ uint8_t regs[CH7xxx_NUM_REGS];
+};
+
+struct ch7xxx_priv {
+ bool quiet;
+
+ struct ch7xxx_reg_state save_reg;
+ struct ch7xxx_reg_state mode_reg;
+ uint8_t save_TCTL, save_TPCP, save_TPD, save_TPVT;
+ uint8_t save_TLPF, save_TCT, save_PM, save_IDF;
+};
+
+static void ch7xxx_save(struct intel_dvo_device *dvo);
+
+static char *ch7xxx_get_id(uint8_t vid)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ch7xxx_ids); i++) {
+ if (ch7xxx_ids[i].vid == vid)
+ return ch7xxx_ids[i].name;
+ }
+
+ return NULL;
+}
+
+/** Reads an 8 bit register */
+static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
+{
+ struct ch7xxx_priv *ch7xxx= dvo->dev_priv;
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ u8 out_buf[2];
+ u8 in_buf[2];
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = out_buf,
+ },
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = in_buf,
+ }
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = 0;
+
+ if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
+ *ch = in_buf[0];
+ return true;
+ };
+
+ if (!ch7xxx->quiet) {
+ DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
+ addr, i2cbus->adapter.name, i2cbus->slave_addr);
+ }
+ return false;
+}
+
+/** Writes an 8 bit register */
+static bool ch7xxx_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
+{
+ struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ uint8_t out_buf[2];
+ struct i2c_msg msg = {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 2,
+ .buf = out_buf,
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = ch;
+
+ if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
+ return true;
+
+ if (!ch7xxx->quiet) {
+ DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
+ addr, i2cbus->adapter.name, i2cbus->slave_addr);
+ }
+
+ return false;
+}
+
+static bool ch7xxx_init(struct intel_dvo_device *dvo,
+ struct intel_i2c_chan *i2cbus)
+{
+ /* this will detect the CH7xxx chip on the specified i2c bus */
+ struct ch7xxx_priv *ch7xxx;
+ uint8_t vendor, device;
+ char *name;
+
+ ch7xxx = kzalloc(sizeof(struct ch7xxx_priv), GFP_KERNEL);
+ if (ch7xxx == NULL)
+ return false;
+
+ dvo->i2c_bus = i2cbus;
+ dvo->i2c_bus->slave_addr = dvo->slave_addr;
+ dvo->dev_priv = ch7xxx;
+ ch7xxx->quiet = true;
+
+ if (!ch7xxx_readb(dvo, CH7xxx_REG_VID, &vendor))
+ goto out;
+
+ name = ch7xxx_get_id(vendor);
+ if (!name) {
+ DRM_DEBUG("ch7xxx not detected; got 0x%02x from %s slave %d.\n",
+ vendor, i2cbus->adapter.name, i2cbus->slave_addr);
+ goto out;
+ }
+
+
+ if (!ch7xxx_readb(dvo, CH7xxx_REG_DID, &device))
+ goto out;
+
+ if (device != CH7xxx_DID) {
+ DRM_DEBUG("ch7xxx not detected; got 0x%02x from %s slave %d.\n",
+ vendor, i2cbus->adapter.name, i2cbus->slave_addr);
+ goto out;
+ }
+
+ ch7xxx->quiet = FALSE;
+ DRM_DEBUG("Detected %s chipset, vendor/device ID 0x%02x/0x%02x\n",
+ name, vendor, device);
+ return true;
+out:
+ kfree(ch7xxx);
+ return false;
+}
+
+static enum drm_connector_status ch7xxx_detect(struct intel_dvo_device *dvo)
+{
+ uint8_t cdet, orig_pm, pm;
+
+ ch7xxx_readb(dvo, CH7xxx_PM, &orig_pm);
+
+ pm = orig_pm;
+ pm &= ~CH7xxx_PM_FPD;
+ pm |= CH7xxx_PM_DVIL | CH7xxx_PM_DVIP;
+
+ ch7xxx_writeb(dvo, CH7xxx_PM, pm);
+
+ ch7xxx_readb(dvo, CH7xxx_CONNECTION_DETECT, &cdet);
+
+ ch7xxx_writeb(dvo, CH7xxx_PM, orig_pm);
+
+ if (cdet & CH7xxx_CDET_DVI)
+ return connector_status_connected;
+ return connector_status_disconnected;
+}
+
+static enum drm_mode_status ch7xxx_mode_valid(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode)
+{
+ if (mode->clock > 165000)
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+}
+
+static void ch7xxx_mode_set(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ uint8_t tvco, tpcp, tpd, tlpf, idf;
+
+ if (mode->clock <= 65000) {
+ tvco = 0x23;
+ tpcp = 0x08;
+ tpd = 0x16;
+ tlpf = 0x60;
+ } else {
+ tvco = 0x2d;
+ tpcp = 0x06;
+ tpd = 0x26;
+ tlpf = 0xa0;
+ }
+
+ ch7xxx_writeb(dvo, CH7xxx_TCTL, 0x00);
+ ch7xxx_writeb(dvo, CH7xxx_TVCO, tvco);
+ ch7xxx_writeb(dvo, CH7xxx_TPCP, tpcp);
+ ch7xxx_writeb(dvo, CH7xxx_TPD, tpd);
+ ch7xxx_writeb(dvo, CH7xxx_TPVT, 0x30);
+ ch7xxx_writeb(dvo, CH7xxx_TLPF, tlpf);
+ ch7xxx_writeb(dvo, CH7xxx_TCT, 0x00);
+
+ ch7xxx_readb(dvo, CH7xxx_IDF, &idf);
+
+ idf &= ~(CH7xxx_IDF_HSP | CH7xxx_IDF_VSP);
+ if (mode->flags & V_PHSYNC)
+ idf |= CH7xxx_IDF_HSP;
+
+ if (mode->flags & V_PVSYNC)
+ idf |= CH7xxx_IDF_HSP;
+
+ ch7xxx_writeb(dvo, CH7xxx_IDF, idf);
+}
+
+/* set the CH7xxx power state */
+static void ch7xxx_dpms(struct intel_dvo_device *dvo, int mode)
+{
+ if (mode == DPMSModeOn)
+ ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_DVIL | CH7xxx_PM_DVIP);
+ else
+ ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_FPD);
+}
+
+static void ch7xxx_dump_regs(struct intel_dvo_device *dvo)
+{
+ struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
+ int i;
+
+ for (i = 0; i < CH7xxx_NUM_REGS; i++) {
+ if ((i % 8) == 0 )
+ DRM_DEBUG("\n %02X: ", i);
+ DRM_DEBUG("%02X ", ch7xxx->mode_reg.regs[i]);
+ }
+}
+
+static void ch7xxx_save(struct intel_dvo_device *dvo)
+{
+ struct ch7xxx_priv *ch7xxx= dvo->dev_priv;
+
+ ch7xxx_readb(dvo, CH7xxx_TCTL, &ch7xxx->save_TCTL);
+ ch7xxx_readb(dvo, CH7xxx_TPCP, &ch7xxx->save_TPCP);
+ ch7xxx_readb(dvo, CH7xxx_TPD, &ch7xxx->save_TPD);
+ ch7xxx_readb(dvo, CH7xxx_TPVT, &ch7xxx->save_TPVT);
+ ch7xxx_readb(dvo, CH7xxx_TLPF, &ch7xxx->save_TLPF);
+ ch7xxx_readb(dvo, CH7xxx_PM, &ch7xxx->save_PM);
+ ch7xxx_readb(dvo, CH7xxx_IDF, &ch7xxx->save_IDF);
+}
+
+static void ch7xxx_restore(struct intel_dvo_device *dvo)
+{
+ struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
+
+ ch7xxx_writeb(dvo, CH7xxx_TCTL, ch7xxx->save_TCTL);
+ ch7xxx_writeb(dvo, CH7xxx_TPCP, ch7xxx->save_TPCP);
+ ch7xxx_writeb(dvo, CH7xxx_TPD, ch7xxx->save_TPD);
+ ch7xxx_writeb(dvo, CH7xxx_TPVT, ch7xxx->save_TPVT);
+ ch7xxx_writeb(dvo, CH7xxx_TLPF, ch7xxx->save_TLPF);
+ ch7xxx_writeb(dvo, CH7xxx_IDF, ch7xxx->save_IDF);
+ ch7xxx_writeb(dvo, CH7xxx_PM, ch7xxx->save_PM);
+}
+
+static void ch7xxx_destroy(struct intel_dvo_device *dvo)
+{
+ struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
+
+ if (ch7xxx) {
+ kfree(ch7xxx);
+ dvo->dev_priv = NULL;
+ }
+}
+
+struct intel_dvo_dev_ops ch7xxx_ops = {
+ .init = ch7xxx_init,
+ .detect = ch7xxx_detect,
+ .mode_valid = ch7xxx_mode_valid,
+ .mode_set = ch7xxx_mode_set,
+ .dpms = ch7xxx_dpms,
+ .dump_regs = ch7xxx_dump_regs,
+ .save = ch7xxx_save,
+ .restore = ch7xxx_restore,
+ .destroy = ch7xxx_destroy,
+};
diff --git a/linux-core/dvo_ivch.c b/linux-core/dvo_ivch.c
new file mode 100644
index 00000000..7ba00b34
--- /dev/null
+++ b/linux-core/dvo_ivch.c
@@ -0,0 +1,463 @@
+/*
+ * Copyright © 2006 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ *
+ */
+
+#include "dvo.h"
+
+/*
+ * register definitions for the i82807aa.
+ *
+ * Documentation on this chipset can be found in datasheet #29069001 at
+ * intel.com.
+ */
+
+/*
+ * VCH Revision & GMBus Base Addr
+ */
+#define VR00 0x00
+# define VR00_BASE_ADDRESS_MASK 0x007f
+
+/*
+ * Functionality Enable
+ */
+#define VR01 0x01
+
+/*
+ * Enable the panel fitter
+ */
+# define VR01_PANEL_FIT_ENABLE (1 << 3)
+/*
+ * Enables the LCD display.
+ *
+ * This must not be set while VR01_DVO_BYPASS_ENABLE is set.
+ */
+# define VR01_LCD_ENABLE (1 << 2)
+/** Enables the DVO repeater. */
+# define VR01_DVO_BYPASS_ENABLE (1 << 1)
+/** Enables the DVO clock */
+# define VR01_DVO_ENABLE (1 << 0)
+
+/*
+ * LCD Interface Format
+ */
+#define VR10 0x10
+/** Enables LVDS output instead of CMOS */
+# define VR10_LVDS_ENABLE (1 << 4)
+/** Enables 18-bit LVDS output. */
+# define VR10_INTERFACE_1X18 (0 << 2)
+/** Enables 24-bit LVDS or CMOS output */
+# define VR10_INTERFACE_1X24 (1 << 2)
+/** Enables 2x18-bit LVDS or CMOS output. */
+# define VR10_INTERFACE_2X18 (2 << 2)
+/** Enables 2x24-bit LVDS output */
+# define VR10_INTERFACE_2X24 (3 << 2)
+
+/*
+ * VR20 LCD Horizontal Display Size
+ */
+#define VR20 0x20
+
+/*
+ * LCD Vertical Display Size
+ */
+#define VR21 0x20
+
+/*
+ * Panel power down status
+ */
+#define VR30 0x30
+/** Read only bit indicating that the panel is not in a safe poweroff state. */
+# define VR30_PANEL_ON (1 << 15)
+
+#define VR40 0x40
+# define VR40_STALL_ENABLE (1 << 13)
+# define VR40_VERTICAL_INTERP_ENABLE (1 << 12)
+# define VR40_ENHANCED_PANEL_FITTING (1 << 11)
+# define VR40_HORIZONTAL_INTERP_ENABLE (1 << 10)
+# define VR40_AUTO_RATIO_ENABLE (1 << 9)
+# define VR40_CLOCK_GATING_ENABLE (1 << 8)
+
+/*
+ * Panel Fitting Vertical Ratio
+ * (((image_height - 1) << 16) / ((panel_height - 1))) >> 2
+ */
+#define VR41 0x41
+
+/*
+ * Panel Fitting Horizontal Ratio
+ * (((image_width - 1) << 16) / ((panel_width - 1))) >> 2
+ */
+#define VR42 0x42
+
+/*
+ * Horizontal Image Size
+ */
+#define VR43 0x43
+
+/* VR80 GPIO 0
+ */
+#define VR80 0x80
+#define VR81 0x81
+#define VR82 0x82
+#define VR83 0x83
+#define VR84 0x84
+#define VR85 0x85
+#define VR86 0x86
+#define VR87 0x87
+
+/* VR88 GPIO 8
+ */
+#define VR88 0x88
+
+/* Graphics BIOS scratch 0
+ */
+#define VR8E 0x8E
+# define VR8E_PANEL_TYPE_MASK (0xf << 0)
+# define VR8E_PANEL_INTERFACE_CMOS (0 << 4)
+# define VR8E_PANEL_INTERFACE_LVDS (1 << 4)
+# define VR8E_FORCE_DEFAULT_PANEL (1 << 5)
+
+/* Graphics BIOS scratch 1
+ */
+#define VR8F 0x8F
+# define VR8F_VCH_PRESENT (1 << 0)
+# define VR8F_DISPLAY_CONN (1 << 1)
+# define VR8F_POWER_MASK (0x3c)
+# define VR8F_POWER_POS (2)
+
+
+struct ivch_priv {
+ bool quiet;
+
+ uint16_t width, height;
+
+ uint16_t save_VR01;
+ uint16_t save_VR40;
+};
+
+#if 0
+struct vch_capabilities {
+ struct aimdb_block aimdb_block;
+ uint8_t panel_type;
+ uint8_t set_panel_type;
+ uint8_t slave_address;
+ uint8_t capabilities;
+#define VCH_PANEL_FITTING_SUPPORT (0x3 << 0)
+#define VCH_PANEL_FITTING_TEXT (1 << 2)
+#define VCH_PANEL_FITTING_GRAPHICS (1 << 3)
+#define VCH_PANEL_FITTING_RATIO (1 << 4)
+#define VCH_DITHERING (1 << 5)
+ uint8_t backlight_gpio;
+ uint8_t set_panel_type_us_gpios;
+} __attribute__ ((packed));
+#endif
+
+static void ivch_dump_regs(struct intel_dvo_device *dvo);
+
+/**
+ * Reads a register on the ivch.
+ *
+ * Each of the 256 registers are 16 bits long.
+ */
+static bool ivch_read(struct intel_dvo_device *dvo, int addr, uint16_t *data)
+{
+ struct ivch_priv *priv = dvo->dev_priv;
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ u8 out_buf[1];
+ u8 in_buf[2];
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = I2C_M_RD,
+ .len = 0,
+ },
+ {
+ .addr = 0,
+ .flags = I2C_M_NOSTART,
+ .len = 1,
+ .buf = out_buf,
+ },
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = I2C_M_RD | I2C_M_NOSTART,
+ .len = 2,
+ .buf = in_buf,
+ }
+ };
+
+ out_buf[0] = addr;
+
+ if (i2c_transfer(&i2cbus->adapter, msgs, 3) == 3) {
+ *data = (in_buf[1] << 8) | in_buf[0];
+ return true;
+ };
+
+ if (!priv->quiet) {
+ DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
+ addr, i2cbus->adapter.name, i2cbus->slave_addr);
+ }
+ return false;
+}
+
+/** Writes a 16-bit register on the ivch */
+static bool ivch_write(struct intel_dvo_device *dvo, int addr, uint16_t data)
+{
+ struct ivch_priv *priv = dvo->dev_priv;
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ u8 out_buf[3];
+ struct i2c_msg msg = {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 3,
+ .buf = out_buf,
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = data & 0xff;
+ out_buf[2] = data >> 8;
+
+ if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
+ return true;
+
+ if (!priv->quiet) {
+ DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
+ addr, i2cbus->adapter.name, i2cbus->slave_addr);
+ }
+
+ return false;
+}
+
+/** Probes the given bus and slave address for an ivch */
+static bool ivch_init(struct intel_dvo_device *dvo,
+ struct intel_i2c_chan *i2cbus)
+{
+ struct ivch_priv *priv;
+ uint16_t temp;
+
+ priv = kzalloc(sizeof(struct ivch_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return false;
+
+ dvo->i2c_bus = i2cbus;
+ dvo->i2c_bus->slave_addr = dvo->slave_addr;
+ dvo->dev_priv = priv;
+ priv->quiet = TRUE;
+
+ if (!ivch_read(dvo, VR00, &temp))
+ goto out;
+ priv->quiet = false;
+
+ /* Since the identification bits are probably zeroes, which doesn't seem
+ * very unique, check that the value in the base address field matches
+ * the address it's responding on.
+ */
+ if ((temp & VR00_BASE_ADDRESS_MASK) != dvo->slave_addr) {
+ DRM_DEBUG("ivch detect failed due to address mismatch "
+ "(%d vs %d)\n",
+ (temp & VR00_BASE_ADDRESS_MASK), dvo->slave_addr);
+ goto out;
+ }
+
+#if 0
+ if (!xf86I2CDevInit(&priv->d)) {
+ goto out;
+ }
+#endif
+ ivch_read(dvo, VR20, &priv->width);
+ ivch_read(dvo, VR21, &priv->height);
+
+ return true;
+
+out:
+ kfree(priv);
+ return false;
+}
+
+static enum drm_connector_status ivch_detect(struct intel_dvo_device *dvo)
+{
+ return connector_status_connected;
+}
+
+static enum drm_mode_status ivch_mode_valid(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode)
+{
+ if (mode->clock > 112000)
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+}
+
+/** Sets the power state of the panel connected to the ivch */
+static void ivch_dpms(struct intel_dvo_device *dvo, int mode)
+{
+ int i;
+ uint16_t vr01, vr30, backlight;
+
+ /* Set the new power state of the panel. */
+ if (!ivch_read(dvo, VR01, &vr01))
+ return;
+
+ if (mode == DPMSModeOn)
+ backlight = 1;
+ else
+ backlight = 0;
+ ivch_write(dvo, VR80, backlight);
+
+ if (mode == DPMSModeOn)
+ vr01 |= VR01_LCD_ENABLE | VR01_DVO_ENABLE;
+ else
+ vr01 &= ~(VR01_LCD_ENABLE | VR01_DVO_ENABLE);
+
+ ivch_write(dvo, VR01, vr01);
+
+ /* Wait for the panel to make its state transition */
+ for (i = 0; i < 100; i++) {
+ if (!ivch_read(dvo, VR30, &vr30))
+ break;
+
+ if (((vr30 & VR30_PANEL_ON) != 0) == (mode == DPMSModeOn))
+ break;
+ udelay(1000);
+ }
+ /* wait some more; vch may fail to resync sometimes without this */
+ udelay(16 * 1000);
+}
+
+static void ivch_mode_set(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ uint16_t vr40 = 0;
+ uint16_t vr01;
+
+ vr01 = 0;
+ vr40 = (VR40_STALL_ENABLE | VR40_VERTICAL_INTERP_ENABLE |
+ VR40_HORIZONTAL_INTERP_ENABLE);
+
+ if (mode->hdisplay != adjusted_mode->hdisplay ||
+ mode->vdisplay != adjusted_mode->vdisplay) {
+ uint16_t x_ratio, y_ratio;
+
+ vr01 |= VR01_PANEL_FIT_ENABLE;
+ vr40 |= VR40_CLOCK_GATING_ENABLE;
+ x_ratio = (((mode->hdisplay - 1) << 16) /
+ (adjusted_mode->hdisplay - 1)) >> 2;
+ y_ratio = (((mode->vdisplay - 1) << 16) /
+ (adjusted_mode->vdisplay - 1)) >> 2;
+ ivch_write (dvo, VR42, x_ratio);
+ ivch_write (dvo, VR41, y_ratio);
+ } else {
+ vr01 &= ~VR01_PANEL_FIT_ENABLE;
+ vr40 &= ~VR40_CLOCK_GATING_ENABLE;
+ }
+ vr40 &= ~VR40_AUTO_RATIO_ENABLE;
+
+ ivch_write(dvo, VR01, vr01);
+ ivch_write(dvo, VR40, vr40);
+
+ ivch_dump_regs(dvo);
+}
+
+static void ivch_dump_regs(struct intel_dvo_device *dvo)
+{
+ uint16_t val;
+
+ ivch_read(dvo, VR00, &val);
+ DRM_DEBUG("VR00: 0x%04x\n", val);
+ ivch_read(dvo, VR01, &val);
+ DRM_DEBUG("VR01: 0x%04x\n", val);
+ ivch_read(dvo, VR30, &val);
+ DRM_DEBUG("VR30: 0x%04x\n", val);
+ ivch_read(dvo, VR40, &val);
+ DRM_DEBUG("VR40: 0x%04x\n", val);
+
+ /* GPIO registers */
+ ivch_read(dvo, VR80, &val);
+ DRM_DEBUG("VR80: 0x%04x\n", val);
+ ivch_read(dvo, VR81, &val);
+ DRM_DEBUG("VR81: 0x%04x\n", val);
+ ivch_read(dvo, VR82, &val);
+ DRM_DEBUG("VR82: 0x%04x\n", val);
+ ivch_read(dvo, VR83, &val);
+ DRM_DEBUG("VR83: 0x%04x\n", val);
+ ivch_read(dvo, VR84, &val);
+ DRM_DEBUG("VR84: 0x%04x\n", val);
+ ivch_read(dvo, VR85, &val);
+ DRM_DEBUG("VR85: 0x%04x\n", val);
+ ivch_read(dvo, VR86, &val);
+ DRM_DEBUG("VR86: 0x%04x\n", val);
+ ivch_read(dvo, VR87, &val);
+ DRM_DEBUG("VR87: 0x%04x\n", val);
+ ivch_read(dvo, VR88, &val);
+ DRM_DEBUG("VR88: 0x%04x\n", val);
+
+ /* Scratch register 0 - AIM Panel type */
+ ivch_read(dvo, VR8E, &val);
+ DRM_DEBUG("VR8E: 0x%04x\n", val);
+
+ /* Scratch register 1 - Status register */
+ ivch_read(dvo, VR8F, &val);
+ DRM_DEBUG("VR8F: 0x%04x\n", val);
+}
+
+static void ivch_save(struct intel_dvo_device *dvo)
+{
+ struct ivch_priv *priv = dvo->dev_priv;
+
+ ivch_read(dvo, VR01, &priv->save_VR01);
+ ivch_read(dvo, VR40, &priv->save_VR40);
+}
+
+static void ivch_restore(struct intel_dvo_device *dvo)
+{
+ struct ivch_priv *priv = dvo->dev_priv;
+
+ ivch_write(dvo, VR01, priv->save_VR01);
+ ivch_write(dvo, VR40, priv->save_VR40);
+}
+
+static void ivch_destroy(struct intel_dvo_device *dvo)
+{
+ struct ivch_priv *priv = dvo->dev_priv;
+
+ if (priv) {
+ kfree(priv);
+ dvo->dev_priv = NULL;
+ }
+}
+
+struct intel_dvo_dev_ops ivch_ops= {
+ .init = ivch_init,
+ .dpms = ivch_dpms,
+ .save = ivch_save,
+ .restore = ivch_restore,
+ .mode_valid = ivch_mode_valid,
+ .mode_set = ivch_mode_set,
+ .detect = ivch_detect,
+ .dump_regs = ivch_dump_regs,
+ .destroy = ivch_destroy,
+};
diff --git a/linux-core/dvo_sil164.c b/linux-core/dvo_sil164.c
new file mode 100644
index 00000000..d0fa4913
--- /dev/null
+++ b/linux-core/dvo_sil164.c
@@ -0,0 +1,302 @@
+/**************************************************************************
+
+Copyright © 2006 Dave Airlie
+
+All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sub license, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice (including the
+next paragraph) shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+**************************************************************************/
+
+#include "dvo.h"
+
+#define SIL164_VID 0x0001
+#define SIL164_DID 0x0006
+
+#define SIL164_VID_LO 0x00
+#define SIL164_VID_HI 0x01
+#define SIL164_DID_LO 0x02
+#define SIL164_DID_HI 0x03
+#define SIL164_REV 0x04
+#define SIL164_RSVD 0x05
+#define SIL164_FREQ_LO 0x06
+#define SIL164_FREQ_HI 0x07
+
+#define SIL164_REG8 0x08
+#define SIL164_8_VEN (1<<5)
+#define SIL164_8_HEN (1<<4)
+#define SIL164_8_DSEL (1<<3)
+#define SIL164_8_BSEL (1<<2)
+#define SIL164_8_EDGE (1<<1)
+#define SIL164_8_PD (1<<0)
+
+#define SIL164_REG9 0x09
+#define SIL164_9_VLOW (1<<7)
+#define SIL164_9_MSEL_MASK (0x7<<4)
+#define SIL164_9_TSEL (1<<3)
+#define SIL164_9_RSEN (1<<2)
+#define SIL164_9_HTPLG (1<<1)
+#define SIL164_9_MDI (1<<0)
+
+#define SIL164_REGC 0x0c
+
+struct sil164_save_rec {
+ uint8_t reg8;
+ uint8_t reg9;
+ uint8_t regc;
+};
+
+struct sil164_priv {
+ //I2CDevRec d;
+ bool quiet;
+ struct sil164_save_rec save_regs;
+ struct sil164_save_rec mode_regs;
+};
+
+#define SILPTR(d) ((SIL164Ptr)(d->DriverPrivate.ptr))
+
+static bool sil164_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
+{
+ struct sil164_priv *sil = dvo->dev_priv;
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ u8 out_buf[2];
+ u8 in_buf[2];
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = out_buf,
+ },
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = in_buf,
+ }
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = 0;
+
+ if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
+ *ch = in_buf[0];
+ return true;
+ };
+
+ if (!sil->quiet) {
+ DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
+ addr, i2cbus->adapter.name, i2cbus->slave_addr);
+ }
+ return false;
+}
+
+static bool sil164_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
+{
+ struct sil164_priv *sil= dvo->dev_priv;
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ uint8_t out_buf[2];
+ struct i2c_msg msg = {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 2,
+ .buf = out_buf,
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = ch;
+
+ if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
+ return true;
+
+ if (!sil->quiet) {
+ DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
+ addr, i2cbus->adapter.name, i2cbus->slave_addr);
+ }
+
+ return false;
+}
+
+/* Silicon Image 164 driver for chip on i2c bus */
+static bool sil164_init(struct intel_dvo_device *dvo,
+ struct intel_i2c_chan *i2cbus)
+{
+ /* this will detect the SIL164 chip on the specified i2c bus */
+ struct sil164_priv *sil;
+ unsigned char ch;
+
+ sil = kzalloc(sizeof(struct sil164_priv), GFP_KERNEL);
+ if (sil == NULL)
+ return false;
+
+ dvo->i2c_bus = i2cbus;
+ dvo->i2c_bus->slave_addr = dvo->slave_addr;
+ dvo->dev_priv = sil;
+ sil->quiet = true;
+
+ if (!sil164_readb(dvo, SIL164_VID_LO, &ch))
+ goto out;
+
+ if (ch != (SIL164_VID & 0xff)) {
+ DRM_DEBUG("sil164 not detected got %d: from %s Slave %d.\n",
+ ch, i2cbus->adapter.name, i2cbus->slave_addr);
+ goto out;
+ }
+
+ if (!sil164_readb(dvo, SIL164_DID_LO, &ch))
+ goto out;
+
+ if (ch != (SIL164_DID & 0xff)) {
+ DRM_DEBUG("sil164 not detected got %d: from %s Slave %d.\n",
+ ch, i2cbus->adapter.name, i2cbus->slave_addr);
+ goto out;
+ }
+ sil->quiet = false;
+
+ DRM_DEBUG("init sil164 dvo controller successfully!\n");
+ return true;
+
+out:
+ kfree(sil);
+ return false;
+}
+
+static enum drm_connector_status sil164_detect(struct intel_dvo_device *dvo)
+{
+ uint8_t reg9;
+
+ sil164_readb(dvo, SIL164_REG9, &reg9);
+
+ if (reg9 & SIL164_9_HTPLG)
+ return connector_status_connected;
+ else
+ return connector_status_disconnected;
+}
+
+static enum drm_mode_status sil164_mode_valid(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode)
+{
+ return MODE_OK;
+}
+
+static void sil164_mode_set(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ /* As long as the basics are set up, since we don't have clock
+ * dependencies in the mode setup, we can just leave the
+ * registers alone and everything will work fine.
+ */
+ /* recommended programming sequence from doc */
+ /*sil164_writeb(sil, 0x08, 0x30);
+ sil164_writeb(sil, 0x09, 0x00);
+ sil164_writeb(sil, 0x0a, 0x90);
+ sil164_writeb(sil, 0x0c, 0x89);
+ sil164_writeb(sil, 0x08, 0x31);*/
+ /* don't do much */
+ return;
+}
+
+/* set the SIL164 power state */
+static void sil164_dpms(struct intel_dvo_device *dvo, int mode)
+{
+ int ret;
+ unsigned char ch;
+
+ ret = sil164_readb(dvo, SIL164_REG8, &ch);
+ if (ret == false)
+ return;
+
+ if (mode == DPMSModeOn)
+ ch |= SIL164_8_PD;
+ else
+ ch &= ~SIL164_8_PD;
+
+ sil164_writeb(dvo, SIL164_REG8, ch);
+ return;
+}
+
+static void sil164_dump_regs(struct intel_dvo_device *dvo)
+{
+ uint8_t val;
+
+ sil164_readb(dvo, SIL164_FREQ_LO, &val);
+ DRM_DEBUG("SIL164_FREQ_LO: 0x%02x\n", val);
+ sil164_readb(dvo, SIL164_FREQ_HI, &val);
+ DRM_DEBUG("SIL164_FREQ_HI: 0x%02x\n", val);
+ sil164_readb(dvo, SIL164_REG8, &val);
+ DRM_DEBUG("SIL164_REG8: 0x%02x\n", val);
+ sil164_readb(dvo, SIL164_REG9, &val);
+ DRM_DEBUG("SIL164_REG9: 0x%02x\n", val);
+ sil164_readb(dvo, SIL164_REGC, &val);
+ DRM_DEBUG("SIL164_REGC: 0x%02x\n", val);
+}
+
+static void sil164_save(struct intel_dvo_device *dvo)
+{
+ struct sil164_priv *sil= dvo->dev_priv;
+
+ if (!sil164_readb(dvo, SIL164_REG8, &sil->save_regs.reg8))
+ return;
+
+ if (!sil164_readb(dvo, SIL164_REG9, &sil->save_regs.reg9))
+ return;
+
+ if (!sil164_readb(dvo, SIL164_REGC, &sil->save_regs.regc))
+ return;
+
+ return;
+}
+
+static void sil164_restore(struct intel_dvo_device *dvo)
+{
+ struct sil164_priv *sil = dvo->dev_priv;
+
+ /* Restore it powered down initially */
+ sil164_writeb(dvo, SIL164_REG8, sil->save_regs.reg8 & ~0x1);
+
+ sil164_writeb(dvo, SIL164_REG9, sil->save_regs.reg9);
+ sil164_writeb(dvo, SIL164_REGC, sil->save_regs.regc);
+ sil164_writeb(dvo, SIL164_REG8, sil->save_regs.reg8);
+}
+
+static void sil164_destroy(struct intel_dvo_device *dvo)
+{
+ struct sil164_priv *sil = dvo->dev_priv;
+
+ if (sil) {
+ kfree(sil);
+ dvo->dev_priv = NULL;
+ }
+}
+
+struct intel_dvo_dev_ops sil164_ops = {
+ .init = sil164_init,
+ .detect = sil164_detect,
+ .mode_valid = sil164_mode_valid,
+ .mode_set = sil164_mode_set,
+ .dpms = sil164_dpms,
+ .dump_regs = sil164_dump_regs,
+ .save = sil164_save,
+ .restore = sil164_restore,
+ .destroy = sil164_destroy,
+};
diff --git a/linux-core/dvo_tfp410.c b/linux-core/dvo_tfp410.c
new file mode 100644
index 00000000..c1d1aa96
--- /dev/null
+++ b/linux-core/dvo_tfp410.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright © 2007 Dave Mueller
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ * Dave Mueller <dave.mueller@gmx.ch>
+ *
+ */
+
+#include "dvo.h"
+
+/* register definitions according to the TFP410 data sheet */
+#define TFP410_VID 0x014C
+#define TFP410_DID 0x0410
+
+#define TFP410_VID_LO 0x00
+#define TFP410_VID_HI 0x01
+#define TFP410_DID_LO 0x02
+#define TFP410_DID_HI 0x03
+#define TFP410_REV 0x04
+
+#define TFP410_CTL_1 0x08
+#define TFP410_CTL_1_TDIS (1<<6)
+#define TFP410_CTL_1_VEN (1<<5)
+#define TFP410_CTL_1_HEN (1<<4)
+#define TFP410_CTL_1_DSEL (1<<3)
+#define TFP410_CTL_1_BSEL (1<<2)
+#define TFP410_CTL_1_EDGE (1<<1)
+#define TFP410_CTL_1_PD (1<<0)
+
+#define TFP410_CTL_2 0x09
+#define TFP410_CTL_2_VLOW (1<<7)
+#define TFP410_CTL_2_MSEL_MASK (0x7<<4)
+#define TFP410_CTL_2_MSEL (1<<4)
+#define TFP410_CTL_2_TSEL (1<<3)
+#define TFP410_CTL_2_RSEN (1<<2)
+#define TFP410_CTL_2_HTPLG (1<<1)
+#define TFP410_CTL_2_MDI (1<<0)
+
+#define TFP410_CTL_3 0x0A
+#define TFP410_CTL_3_DK_MASK (0x7<<5)
+#define TFP410_CTL_3_DK (1<<5)
+#define TFP410_CTL_3_DKEN (1<<4)
+#define TFP410_CTL_3_CTL_MASK (0x7<<1)
+#define TFP410_CTL_3_CTL (1<<1)
+
+#define TFP410_USERCFG 0x0B
+
+#define TFP410_DE_DLY 0x32
+
+#define TFP410_DE_CTL 0x33
+#define TFP410_DE_CTL_DEGEN (1<<6)
+#define TFP410_DE_CTL_VSPOL (1<<5)
+#define TFP410_DE_CTL_HSPOL (1<<4)
+#define TFP410_DE_CTL_DEDLY8 (1<<0)
+
+#define TFP410_DE_TOP 0x34
+
+#define TFP410_DE_CNT_LO 0x36
+#define TFP410_DE_CNT_HI 0x37
+
+#define TFP410_DE_LIN_LO 0x38
+#define TFP410_DE_LIN_HI 0x39
+
+#define TFP410_H_RES_LO 0x3A
+#define TFP410_H_RES_HI 0x3B
+
+#define TFP410_V_RES_LO 0x3C
+#define TFP410_V_RES_HI 0x3D
+
+struct tfp410_save_rec {
+ uint8_t ctl1;
+ uint8_t ctl2;
+};
+
+struct tfp410_priv {
+ bool quiet;
+
+ struct tfp410_save_rec saved_reg;
+ struct tfp410_save_rec mode_reg;
+};
+
+static bool tfp410_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
+{
+ struct tfp410_priv *tfp = dvo->dev_priv;
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ u8 out_buf[2];
+ u8 in_buf[2];
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = out_buf,
+ },
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = in_buf,
+ }
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = 0;
+
+ if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
+ *ch = in_buf[0];
+ return true;
+ };
+
+ if (!tfp->quiet) {
+ DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
+ addr, i2cbus->adapter.name, i2cbus->slave_addr);
+ }
+ return false;
+}
+
+static bool tfp410_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
+{
+ struct tfp410_priv *tfp = dvo->dev_priv;
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ uint8_t out_buf[2];
+ struct i2c_msg msg = {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 2,
+ .buf = out_buf,
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = ch;
+
+ if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
+ return true;
+
+ if (!tfp->quiet) {
+ DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
+ addr, i2cbus->adapter.name, i2cbus->slave_addr);
+ }
+
+ return false;
+}
+
+static int tfp410_getid(struct intel_dvo_device *dvo, int addr)
+{
+ uint8_t ch1, ch2;
+
+ if (tfp410_readb(dvo, addr+0, &ch1) &&
+ tfp410_readb(dvo, addr+1, &ch2))
+ return ((ch2 << 8) & 0xFF00) | (ch1 & 0x00FF);
+
+ return -1;
+}
+
+/* Ti TFP410 driver for chip on i2c bus */
+static bool tfp410_init(struct intel_dvo_device *dvo,
+ struct intel_i2c_chan *i2cbus)
+{
+ /* this will detect the tfp410 chip on the specified i2c bus */
+ struct tfp410_priv *tfp;
+ int id;
+
+ tfp = kzalloc(sizeof(struct tfp410_priv), GFP_KERNEL);
+ if (tfp == NULL)
+ return false;
+
+ dvo->i2c_bus = i2cbus;
+ dvo->i2c_bus->slave_addr = dvo->slave_addr;
+ dvo->dev_priv = tfp;
+ tfp->quiet = TRUE;
+
+ if ((id = tfp410_getid(dvo, TFP410_VID_LO)) != TFP410_VID) {
+ DRM_DEBUG("tfp410 not detected got VID %X: from %s Slave %d.\n",
+ id, i2cbus->adapter.name, i2cbus->slave_addr);
+ goto out;
+ }
+
+ if ((id = tfp410_getid(dvo, TFP410_DID_LO)) != TFP410_DID) {
+ DRM_DEBUG("tfp410 not detected got DID %X: from %s Slave %d.\n",
+ id, i2cbus->adapter.name, i2cbus->slave_addr);
+ goto out;
+ }
+ tfp->quiet = FALSE;
+ return true;
+out:
+ kfree(tfp);
+ return false;
+}
+
+static enum drm_connector_status tfp410_detect(struct intel_dvo_device *dvo)
+{
+ enum drm_connector_status ret = connector_status_disconnected;
+ uint8_t ctl2;
+
+ if (tfp410_readb(dvo, TFP410_CTL_2, &ctl2)) {
+ if (ctl2 & TFP410_CTL_2_HTPLG)
+ ret = connector_status_connected;
+ else
+ ret = connector_status_disconnected;
+ }
+
+ return ret;
+}
+
+static enum drm_mode_status tfp410_mode_valid(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode)
+{
+ return MODE_OK;
+}
+
+static void tfp410_mode_set(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ /* As long as the basics are set up, since we don't have clock dependencies
+ * in the mode setup, we can just leave the registers alone and everything
+ * will work fine.
+ */
+ /* don't do much */
+ return;
+}
+
+/* set the tfp410 power state */
+static void tfp410_dpms(struct intel_dvo_device *dvo, int mode)
+{
+ uint8_t ctl1;
+
+ if (!tfp410_readb(dvo, TFP410_CTL_1, &ctl1))
+ return;
+
+ if (mode == DPMSModeOn)
+ ctl1 |= TFP410_CTL_1_PD;
+ else
+ ctl1 &= ~TFP410_CTL_1_PD;
+
+ tfp410_writeb(dvo, TFP410_CTL_1, ctl1);
+}
+
+static void tfp410_dump_regs(struct intel_dvo_device *dvo)
+{
+ uint8_t val, val2;
+
+ tfp410_readb(dvo, TFP410_REV, &val);
+ DRM_DEBUG("TFP410_REV: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_CTL_1, &val);
+ DRM_DEBUG("TFP410_CTL1: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_CTL_2, &val);
+ DRM_DEBUG("TFP410_CTL2: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_CTL_3, &val);
+ DRM_DEBUG("TFP410_CTL3: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_USERCFG, &val);
+ DRM_DEBUG("TFP410_USERCFG: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_DE_DLY, &val);
+ DRM_DEBUG("TFP410_DE_DLY: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_DE_CTL, &val);
+ DRM_DEBUG("TFP410_DE_CTL: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_DE_TOP, &val);
+ DRM_DEBUG("TFP410_DE_TOP: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_DE_CNT_LO, &val);
+ tfp410_readb(dvo, TFP410_DE_CNT_HI, &val2);
+ DRM_DEBUG("TFP410_DE_CNT: 0x%02X%02X\n", val2, val);
+ tfp410_readb(dvo, TFP410_DE_LIN_LO, &val);
+ tfp410_readb(dvo, TFP410_DE_LIN_HI, &val2);
+ DRM_DEBUG("TFP410_DE_LIN: 0x%02X%02X\n", val2, val);
+ tfp410_readb(dvo, TFP410_H_RES_LO, &val);
+ tfp410_readb(dvo, TFP410_H_RES_HI, &val2);
+ DRM_DEBUG("TFP410_H_RES: 0x%02X%02X\n", val2, val);
+ tfp410_readb(dvo, TFP410_V_RES_LO, &val);
+ tfp410_readb(dvo, TFP410_V_RES_HI, &val2);
+ DRM_DEBUG("TFP410_V_RES: 0x%02X%02X\n", val2, val);
+}
+
+static void tfp410_save(struct intel_dvo_device *dvo)
+{
+ struct tfp410_priv *tfp = dvo->dev_priv;
+
+ if (!tfp410_readb(dvo, TFP410_CTL_1, &tfp->saved_reg.ctl1))
+ return;
+
+ if (!tfp410_readb(dvo, TFP410_CTL_2, &tfp->saved_reg.ctl2))
+ return;
+}
+
+static void tfp410_restore(struct intel_dvo_device *dvo)
+{
+ struct tfp410_priv *tfp = dvo->dev_priv;
+
+ /* Restore it powered down initially */
+ tfp410_writeb(dvo, TFP410_CTL_1, tfp->saved_reg.ctl1 & ~0x1);
+
+ tfp410_writeb(dvo, TFP410_CTL_2, tfp->saved_reg.ctl2);
+ tfp410_writeb(dvo, TFP410_CTL_1, tfp->saved_reg.ctl1);
+}
+
+static void tfp410_destroy(struct intel_dvo_device *dvo)
+{
+ struct tfp410_priv *tfp = dvo->dev_priv;
+
+ if (tfp) {
+ kfree(tfp);
+ dvo->dev_priv = NULL;
+ }
+}
+
+struct intel_dvo_dev_ops tfp410_ops = {
+ .init = tfp410_init,
+ .detect = tfp410_detect,
+ .mode_valid = tfp410_mode_valid,
+ .mode_set = tfp410_mode_set,
+ .dpms = tfp410_dpms,
+ .dump_regs = tfp410_dump_regs,
+ .save = tfp410_save,
+ .restore = tfp410_restore,
+ .destroy = tfp410_destroy,
+};
diff --git a/linux-core/i915_buffer.c b/linux-core/i915_buffer.c
index 8d991c42..4224b737 100644
--- a/linux-core/i915_buffer.c
+++ b/linux-core/i915_buffer.c
@@ -89,7 +89,7 @@ int i915_init_mem_type(struct drm_device *dev, uint32_t type,
man->drm_bus_maptype = _DRM_AGP;
man->gpu_offset = 0;
break;
- case DRM_BO_MEM_PRIV0:
+ case DRM_BO_MEM_VRAM:
if (!(drm_core_has_AGP(dev) && dev->agp)) {
DRM_ERROR("AGP is not enabled for memory type %u\n",
(unsigned)type);
@@ -103,6 +103,9 @@ int i915_init_mem_type(struct drm_device *dev, uint32_t type,
man->drm_bus_maptype = _DRM_AGP;
man->gpu_offset = 0;
break;
+ case DRM_BO_MEM_PRIV0: /* for OS preallocated space */
+ DRM_ERROR("PRIV0 not used yet.\n");
+ break;
default:
DRM_ERROR("Unsupported memory type %u\n", (unsigned)type);
return -EINVAL;
@@ -139,7 +142,7 @@ static void i915_emit_copy_blit(struct drm_device * dev,
{
uint32_t cur_pages;
uint32_t stride = PAGE_SIZE;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
RING_LOCALS;
if (!dev_priv)
@@ -267,10 +270,12 @@ static inline void clflush(volatile void *__p)
static inline void drm_cache_flush_addr(void *virt)
{
+#ifdef cpu_has_clflush
int i;
for (i = 0; i < PAGE_SIZE; i += boot_cpu_data.x86_clflush_size)
clflush(virt+i);
+#endif
}
static inline void drm_cache_flush_page(struct page *p)
@@ -288,6 +293,9 @@ void i915_flush_ttm(struct drm_ttm *ttm)
DRM_MEMORYBARRIER();
#ifdef CONFIG_X86_32
+#ifndef cpu_has_clflush
+#define cpu_has_clflush 0
+#endif
/* Hopefully nobody has built an x86-64 processor without clflush */
if (!cpu_has_clflush) {
wbinvd();
diff --git a/linux-core/i915_drv.c b/linux-core/i915_drv.c
index 3f246a08..8718bd10 100644
--- a/linux-core/i915_drv.c
+++ b/linux-core/i915_drv.c
@@ -30,6 +30,7 @@
#include "drmP.h"
#include "drm.h"
#include "i915_drm.h"
+#include "intel_drv.h"
#include "i915_drv.h"
#include "drm_pciids.h"
@@ -38,14 +39,23 @@ static struct pci_device_id pciidlist[] = {
i915_PCI_IDS
};
+unsigned int i915_modeset = 0;
+module_param_named(modeset, i915_modeset, int, 0400);
+
+unsigned int i915_fbpercrtc = 0;
+module_param_named(fbpercrtc, i915_fbpercrtc, int, 0400);
+
+unsigned int i915_rightof = 1;
+module_param_named(i915_rightof, i915_rightof, int, 0400);
+
#ifdef I915_HAVE_FENCE
extern struct drm_fence_driver i915_fence_driver;
#endif
#ifdef I915_HAVE_BUFFER
-static uint32_t i915_mem_prios[] = {DRM_BO_MEM_PRIV0, DRM_BO_MEM_TT, DRM_BO_MEM_LOCAL};
-static uint32_t i915_busy_prios[] = {DRM_BO_MEM_TT, DRM_BO_MEM_PRIV0, DRM_BO_MEM_LOCAL};
+static uint32_t i915_mem_prios[] = {DRM_BO_MEM_VRAM, DRM_BO_MEM_TT, DRM_BO_MEM_LOCAL};
+static uint32_t i915_busy_prios[] = {DRM_BO_MEM_TT, DRM_BO_MEM_VRAM, DRM_BO_MEM_LOCAL};
static struct drm_bo_driver i915_bo_driver = {
.mem_type_prio = i915_mem_prios,
@@ -63,11 +73,6 @@ static struct drm_bo_driver i915_bo_driver = {
};
#endif
-enum pipe {
- PIPE_A = 0,
- PIPE_B,
-};
-
static bool i915_pipe_enabled(struct drm_device *dev, enum pipe pipe)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -304,13 +309,13 @@ static int i915_suspend(struct drm_device *dev, pm_message_t state)
dev_priv->saveDSPASTRIDE = I915_READ(DSPASTRIDE);
dev_priv->saveDSPASIZE = I915_READ(DSPASIZE);
dev_priv->saveDSPAPOS = I915_READ(DSPAPOS);
- dev_priv->saveDSPABASE = I915_READ(DSPABASE);
+ dev_priv->saveDSPAADDR = I915_READ(DSPAADDR);
if (IS_I965G(dev)) {
dev_priv->saveDSPASURF = I915_READ(DSPASURF);
dev_priv->saveDSPATILEOFF = I915_READ(DSPATILEOFF);
}
i915_save_palette(dev, PIPE_A);
- dev_priv->savePIPEASTAT = I915_READ(I915REG_PIPEASTAT);
+ dev_priv->savePIPEASTAT = I915_READ(PIPEASTAT);
/* Pipe & plane B info */
dev_priv->savePIPEBCONF = I915_READ(PIPEBCONF);
@@ -332,13 +337,13 @@ static int i915_suspend(struct drm_device *dev, pm_message_t state)
dev_priv->saveDSPBSTRIDE = I915_READ(DSPBSTRIDE);
dev_priv->saveDSPBSIZE = I915_READ(DSPBSIZE);
dev_priv->saveDSPBPOS = I915_READ(DSPBPOS);
- dev_priv->saveDSPBBASE = I915_READ(DSPBBASE);
+ dev_priv->saveDSPBADDR = I915_READ(DSPBADDR);
if (IS_I965GM(dev) || IS_IGD_GM(dev)) {
dev_priv->saveDSPBSURF = I915_READ(DSPBSURF);
dev_priv->saveDSPBTILEOFF = I915_READ(DSPBTILEOFF);
}
i915_save_palette(dev, PIPE_B);
- dev_priv->savePIPEBSTAT = I915_READ(I915REG_PIPEBSTAT);
+ dev_priv->savePIPEBSTAT = I915_READ(PIPEBSTAT);
/* CRT state */
dev_priv->saveADPA = I915_READ(ADPA);
@@ -353,9 +358,9 @@ static int i915_suspend(struct drm_device *dev, pm_message_t state)
dev_priv->saveLVDS = I915_READ(LVDS);
if (!IS_I830(dev) && !IS_845G(dev))
dev_priv->savePFIT_CONTROL = I915_READ(PFIT_CONTROL);
- dev_priv->saveLVDSPP_ON = I915_READ(LVDSPP_ON);
- dev_priv->saveLVDSPP_OFF = I915_READ(LVDSPP_OFF);
- dev_priv->savePP_CYCLE = I915_READ(PP_CYCLE);
+ dev_priv->savePP_ON_DELAYS = I915_READ(PP_ON_DELAYS);
+ dev_priv->savePP_OFF_DELAYS = I915_READ(PP_OFF_DELAYS);
+ dev_priv->savePP_DIVISOR = I915_READ(PP_DIVISOR);
/* FIXME: save TV & SDVO state */
@@ -366,19 +371,19 @@ static int i915_suspend(struct drm_device *dev, pm_message_t state)
dev_priv->saveFBC_CONTROL = I915_READ(FBC_CONTROL);
/* Interrupt state */
- dev_priv->saveIIR = I915_READ(I915REG_INT_IDENTITY_R);
- dev_priv->saveIER = I915_READ(I915REG_INT_ENABLE_R);
- dev_priv->saveIMR = I915_READ(I915REG_INT_MASK_R);
+ dev_priv->saveIIR = I915_READ(IIR);
+ dev_priv->saveIER = I915_READ(IER);
+ dev_priv->saveIMR = I915_READ(IMR);
/* VGA state */
- dev_priv->saveVCLK_DIVISOR_VGA0 = I915_READ(VCLK_DIVISOR_VGA0);
- dev_priv->saveVCLK_DIVISOR_VGA1 = I915_READ(VCLK_DIVISOR_VGA1);
- dev_priv->saveVCLK_POST_DIV = I915_READ(VCLK_POST_DIV);
+ dev_priv->saveVGA0 = I915_READ(VGA0);
+ dev_priv->saveVGA1 = I915_READ(VGA1);
+ dev_priv->saveVGA_PD = I915_READ(VGA_PD);
dev_priv->saveVGACNTRL = I915_READ(VGACNTRL);
/* Clock gating state */
dev_priv->saveD_STATE = I915_READ(D_STATE);
- dev_priv->saveDSPCLK_GATE_D = I915_READ(DSPCLK_GATE_D);
+ dev_priv->saveCG_2D_DIS = I915_READ(CG_2D_DIS);
/* Cache mode state */
dev_priv->saveCACHE_MODE_0 = I915_READ(CACHE_MODE_0);
@@ -388,7 +393,7 @@ static int i915_suspend(struct drm_device *dev, pm_message_t state)
/* Scratch space */
for (i = 0; i < 16; i++) {
- dev_priv->saveSWF0[i] = I915_READ(SWF0 + (i << 2));
+ dev_priv->saveSWF0[i] = I915_READ(SWF00 + (i << 2));
dev_priv->saveSWF1[i] = I915_READ(SWF10 + (i << 2));
}
for (i = 0; i < 3; i++)
@@ -415,6 +420,8 @@ static int i915_resume(struct drm_device *dev)
if (pci_enable_device(dev->pdev))
return -1;
+ DRM_INFO("resuming i915\n");
+
pci_write_config_byte(dev->pdev, LBB, dev_priv->saveLBB);
I915_WRITE(DSPARB, dev_priv->saveDSPARB);
@@ -448,7 +455,7 @@ static int i915_resume(struct drm_device *dev)
I915_WRITE(DSPASIZE, dev_priv->saveDSPASIZE);
I915_WRITE(DSPAPOS, dev_priv->saveDSPAPOS);
I915_WRITE(PIPEASRC, dev_priv->savePIPEASRC);
- I915_WRITE(DSPABASE, dev_priv->saveDSPABASE);
+ I915_WRITE(DSPAADDR, dev_priv->saveDSPAADDR);
I915_WRITE(DSPASTRIDE, dev_priv->saveDSPASTRIDE);
if (IS_I965G(dev)) {
I915_WRITE(DSPASURF, dev_priv->saveDSPASURF);
@@ -460,10 +467,11 @@ static int i915_resume(struct drm_device *dev)
i915_restore_palette(dev, PIPE_A);
/* Enable the plane */
I915_WRITE(DSPACNTR, dev_priv->saveDSPACNTR);
- I915_WRITE(DSPABASE, I915_READ(DSPABASE));
+ I915_WRITE(DSPAADDR, I915_READ(DSPAADDR));
/* Pipe & plane B info */
if (dev_priv->saveDPLL_B & DPLL_VCO_ENABLE) {
+ DRM_INFO("restoring DPLL_B: 0x%08x\n", dev_priv->saveDPLL_B);
I915_WRITE(DPLL_B, dev_priv->saveDPLL_B &
~DPLL_VCO_ENABLE);
udelay(150);
@@ -472,6 +480,7 @@ static int i915_resume(struct drm_device *dev)
I915_WRITE(FPB1, dev_priv->saveFPB1);
/* Actually enable it */
I915_WRITE(DPLL_B, dev_priv->saveDPLL_B);
+ DRM_INFO("restoring DPLL_B: 0x%08x\n", dev_priv->saveDPLL_B);
udelay(150);
if (IS_I965G(dev))
I915_WRITE(DPLL_B_MD, dev_priv->saveDPLL_B_MD);
@@ -490,7 +499,7 @@ static int i915_resume(struct drm_device *dev)
I915_WRITE(DSPBSIZE, dev_priv->saveDSPBSIZE);
I915_WRITE(DSPBPOS, dev_priv->saveDSPBPOS);
I915_WRITE(PIPEBSRC, dev_priv->savePIPEBSRC);
- I915_WRITE(DSPBBASE, dev_priv->saveDSPBBASE);
+ I915_WRITE(DSPBADDR, dev_priv->saveDSPBADDR);
I915_WRITE(DSPBSTRIDE, dev_priv->saveDSPBSTRIDE);
if (IS_I965G(dev)) {
I915_WRITE(DSPBSURF, dev_priv->saveDSPBSURF);
@@ -502,7 +511,7 @@ static int i915_resume(struct drm_device *dev)
i915_restore_palette(dev, PIPE_B);
/* Enable the plane */
I915_WRITE(DSPBCNTR, dev_priv->saveDSPBCNTR);
- I915_WRITE(DSPBBASE, I915_READ(DSPBBASE));
+ I915_WRITE(DSPBADDR, I915_READ(DSPBADDR));
/* CRT state */
I915_WRITE(ADPA, dev_priv->saveADPA);
@@ -517,9 +526,9 @@ static int i915_resume(struct drm_device *dev)
I915_WRITE(PFIT_PGM_RATIOS, dev_priv->savePFIT_PGM_RATIOS);
I915_WRITE(BLC_PWM_CTL, dev_priv->saveBLC_PWM_CTL);
- I915_WRITE(LVDSPP_ON, dev_priv->saveLVDSPP_ON);
- I915_WRITE(LVDSPP_OFF, dev_priv->saveLVDSPP_OFF);
- I915_WRITE(PP_CYCLE, dev_priv->savePP_CYCLE);
+ I915_WRITE(PP_ON_DELAYS, dev_priv->savePP_ON_DELAYS);
+ I915_WRITE(PP_OFF_DELAYS, dev_priv->savePP_OFF_DELAYS);
+ I915_WRITE(PP_DIVISOR, dev_priv->savePP_DIVISOR);
I915_WRITE(PP_CONTROL, dev_priv->savePP_CONTROL);
/* FIXME: restore TV & SDVO state */
@@ -532,14 +541,14 @@ static int i915_resume(struct drm_device *dev)
/* VGA state */
I915_WRITE(VGACNTRL, dev_priv->saveVGACNTRL);
- I915_WRITE(VCLK_DIVISOR_VGA0, dev_priv->saveVCLK_DIVISOR_VGA0);
- I915_WRITE(VCLK_DIVISOR_VGA1, dev_priv->saveVCLK_DIVISOR_VGA1);
- I915_WRITE(VCLK_POST_DIV, dev_priv->saveVCLK_POST_DIV);
+ I915_WRITE(VGA0, dev_priv->saveVGA0);
+ I915_WRITE(VGA1, dev_priv->saveVGA1);
+ I915_WRITE(VGA_PD, dev_priv->saveVGA_PD);
udelay(150);
/* Clock gating state */
I915_WRITE (D_STATE, dev_priv->saveD_STATE);
- I915_WRITE (DSPCLK_GATE_D, dev_priv->saveDSPCLK_GATE_D);
+ I915_WRITE (CG_2D_DIS, dev_priv->saveCG_2D_DIS);
/* Cache mode state */
I915_WRITE (CACHE_MODE_0, dev_priv->saveCACHE_MODE_0 | 0xffff0000);
@@ -548,7 +557,7 @@ static int i915_resume(struct drm_device *dev)
I915_WRITE (MI_ARB_STATE, dev_priv->saveMI_ARB_STATE | 0xffff0000);
for (i = 0; i < 16; i++) {
- I915_WRITE(SWF0 + (i << 2), dev_priv->saveSWF0[i]);
+ I915_WRITE(SWF00 + (i << 2), dev_priv->saveSWF0[i]);
I915_WRITE(SWF10 + (i << 2), dev_priv->saveSWF1[i+7]);
}
for (i = 0; i < 3; i++)
@@ -585,6 +594,8 @@ static struct drm_driver driver = {
.reclaim_buffers = drm_core_reclaim_buffers,
.get_map_ofs = drm_core_get_map_ofs,
.get_reg_ofs = drm_core_get_reg_ofs,
+ .master_create = i915_master_create,
+ .master_destroy = i915_master_destroy,
.ioctls = i915_ioctls,
.gem_init_object = i915_gem_init_object,
.gem_free_object = i915_gem_free_object,
@@ -630,6 +641,9 @@ static int probe(struct pci_dev *pdev, const struct pci_device_id *ent)
static int __init i915_init(void)
{
driver.num_ioctls = i915_max_ioctl;
+ if (i915_modeset == 1)
+ driver.driver_features |= DRIVER_MODESET;
+
return drm_init(&driver, pciidlist);
}
diff --git a/linux-core/i915_execbuf.c b/linux-core/i915_execbuf.c
index 804f3ac1..932882dd 100644
--- a/linux-core/i915_execbuf.c
+++ b/linux-core/i915_execbuf.c
@@ -819,9 +819,13 @@ void i915_fence_or_sync(struct drm_file *file_priv,
int i915_execbuffer(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
- drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *)
- dev_priv->sarea_priv;
+ struct drm_i915_private *dev_priv = (struct drm_i915_private *)
+ dev->dev_private;
+ struct drm_i915_master_private *master_priv =
+ (struct drm_i915_master_private *)
+ dev->primary->master->driver_priv;
+ struct drm_i915_sarea *sarea_priv = (struct drm_i915_sarea *)
+ master_priv->sarea_priv;
struct drm_i915_execbuffer *exec_buf = data;
struct drm_i915_batchbuffer *batch = &exec_buf->batch;
struct drm_fence_arg *fence_arg = &exec_buf->fence_arg;
diff --git a/linux-core/i915_fence.c b/linux-core/i915_fence.c
index e403be6a..436b7e1f 100644
--- a/linux-core/i915_fence.c
+++ b/linux-core/i915_fence.c
@@ -46,7 +46,7 @@ static inline void i915_initiate_rwflush(struct drm_i915_private *dev_priv,
dev_priv->flush_sequence = (uint32_t) READ_BREADCRUMB(dev_priv);
dev_priv->flush_flags = fc->pending_flush;
dev_priv->saved_flush_status = READ_HWSP(dev_priv, 0);
- I915_WRITE(I915REG_INSTPM, (1 << 5) | (1 << 21));
+ I915_WRITE(INSTPM, (1 << 5) | (1 << 21));
dev_priv->flush_pending = 1;
fc->pending_flush &= ~DRM_I915_FENCE_TYPE_RW;
}
@@ -94,7 +94,7 @@ static void i915_fence_flush(struct drm_device *dev,
static void i915_fence_poll(struct drm_device *dev, uint32_t fence_class,
uint32_t waiting_types)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
struct drm_fence_manager *fm = &dev->fm;
struct drm_fence_class_manager *fc = &fm->fence_class[0];
uint32_t sequence;
@@ -120,11 +120,11 @@ static void i915_fence_poll(struct drm_device *dev, uint32_t fence_class,
if (dev_priv->fence_irq_on &&
!(fc->waiting_types & DRM_FENCE_TYPE_EXE)) {
- i915_user_irq_off(dev_priv);
+ i915_user_irq_off(dev);
dev_priv->fence_irq_on = 0;
} else if (!dev_priv->fence_irq_on &&
(fc->waiting_types & DRM_FENCE_TYPE_EXE)) {
- i915_user_irq_on(dev_priv);
+ i915_user_irq_on(dev);
dev_priv->fence_irq_on = 1;
}
}
@@ -147,7 +147,7 @@ static int i915_fence_emit_sequence(struct drm_device *dev, uint32_t class,
uint32_t flags, uint32_t *sequence,
uint32_t *native_type)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
if (unlikely(!dev_priv))
return -EINVAL;
@@ -181,7 +181,7 @@ static int i915_fence_wait(struct drm_fence_object *fence,
int lazy, int interruptible, uint32_t mask)
{
struct drm_device *dev = fence->dev;
- drm_i915_private_t *dev_priv = (struct drm_i915_private *) dev->dev_private;
+ struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
struct drm_fence_manager *fm = &dev->fm;
struct drm_fence_class_manager *fc = &fm->fence_class[0];
int ret;
diff --git a/linux-core/i915_gem.c b/linux-core/i915_gem.c
index d60a98f8..b2870893 100644
--- a/linux-core/i915_gem.c
+++ b/linux-core/i915_gem.c
@@ -45,7 +45,7 @@ int
i915_gem_init_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_init *args = data;
mutex_lock(&dev->struct_mutex);
@@ -90,7 +90,7 @@ static void
i915_gem_object_move_to_active(struct drm_gem_object *obj)
{
struct drm_device *dev = obj->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj_priv = obj->driver_private;
/* Add a reference if we're newly entering the active list. */
@@ -107,7 +107,7 @@ static void
i915_gem_object_move_to_inactive(struct drm_gem_object *obj)
{
struct drm_device *dev = obj->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj_priv = obj->driver_private;
if (obj_priv->pin_count != 0)
@@ -132,7 +132,7 @@ i915_gem_object_move_to_inactive(struct drm_gem_object *obj)
static uint32_t
i915_add_request(struct drm_device *dev, uint32_t flush_domains)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_request *request;
uint32_t seqno;
RING_LOCALS;
@@ -150,8 +150,8 @@ i915_add_request(struct drm_device *dev, uint32_t flush_domains)
dev_priv->mm.next_gem_seqno++;
BEGIN_LP_RING(4);
- OUT_RING(CMD_STORE_DWORD_IDX);
- OUT_RING(I915_GEM_HWS_INDEX << STORE_DWORD_INDEX_SHIFT);
+ OUT_RING(MI_STORE_DWORD_INDEX);
+ OUT_RING(I915_GEM_HWS_INDEX << MI_STORE_DWORD_INDEX_SHIFT);
OUT_RING(seqno);
OUT_RING(GFX_OP_USER_INTERRUPT);
@@ -180,8 +180,8 @@ i915_add_request(struct drm_device *dev, uint32_t flush_domains)
uint32_t
i915_retire_commands(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
- uint32_t cmd = CMD_MI_FLUSH | MI_NO_WRITE_FLUSH;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ uint32_t cmd = MI_FLUSH | MI_NO_WRITE_FLUSH;
uint32_t flush_domains = 0;
RING_LOCALS;
@@ -203,7 +203,7 @@ static void
i915_gem_retire_request(struct drm_device *dev,
struct drm_i915_gem_request *request)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
if (request->flush_domains != 0) {
struct drm_i915_gem_object *obj_priv, *next;
@@ -268,7 +268,7 @@ i915_seqno_passed(uint32_t seq1, uint32_t seq2)
static uint32_t
i915_get_gem_seqno(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
return READ_HWSP(dev_priv, I915_GEM_HWS_INDEX);
}
@@ -279,7 +279,7 @@ i915_get_gem_seqno(struct drm_device *dev)
void
i915_gem_retire_requests(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t seqno;
seqno = i915_get_gem_seqno(dev);
@@ -307,7 +307,7 @@ void
i915_gem_retire_timeout(unsigned long data)
{
struct drm_device *dev = (struct drm_device *) data;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
schedule_work(&dev_priv->mm.retire_task);
}
@@ -315,10 +315,10 @@ i915_gem_retire_timeout(unsigned long data)
void
i915_gem_retire_handler(struct work_struct *work)
{
- drm_i915_private_t *dev_priv;
+ struct drm_i915_private *dev_priv;
struct drm_device *dev;
- dev_priv = container_of(work, drm_i915_private_t,
+ dev_priv = container_of(work, struct drm_i915_private,
mm.retire_task);
dev = dev_priv->dev;
@@ -336,17 +336,17 @@ i915_gem_retire_handler(struct work_struct *work)
int
i915_wait_request(struct drm_device *dev, uint32_t seqno)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int ret = 0;
BUG_ON(seqno == 0);
if (!i915_seqno_passed(i915_get_gem_seqno(dev), seqno)) {
- i915_user_irq_on(dev_priv);
+ i915_user_irq_on(dev);
ret = wait_event_interruptible(dev_priv->irq_queue,
i915_seqno_passed(i915_get_gem_seqno(dev),
seqno));
- i915_user_irq_off(dev_priv);
+ i915_user_irq_off(dev);
}
if (ret)
DRM_ERROR ("%s returns %d (awaiting %d at %d)\n",
@@ -368,7 +368,7 @@ i915_gem_flush(struct drm_device *dev,
uint32_t invalidate_domains,
uint32_t flush_domains)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t cmd;
RING_LOCALS;
@@ -409,7 +409,7 @@ i915_gem_flush(struct drm_device *dev,
* are flushed at any MI_FLUSH.
*/
- cmd = CMD_MI_FLUSH | MI_NO_WRITE_FLUSH;
+ cmd = MI_FLUSH | MI_NO_WRITE_FLUSH;
if ((invalidate_domains|flush_domains) &
DRM_GEM_DOMAIN_I915_RENDER)
cmd &= ~MI_NO_WRITE_FLUSH;
@@ -581,7 +581,7 @@ i915_gem_dump_object(struct drm_gem_object *obj, int len,
static void
i915_dump_lru(struct drm_device *dev, const char *where)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj_priv;
DRM_INFO("active list %s {\n", where);
@@ -612,7 +612,7 @@ i915_dump_lru(struct drm_device *dev, const char *where)
static int
i915_gem_evict_something(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_gem_object *obj;
struct drm_i915_gem_object *obj_priv;
int ret;
@@ -725,7 +725,7 @@ static int
i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
{
struct drm_device *dev = obj->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj_priv = obj->driver_private;
struct drm_memrange_node *free_space;
int page_count, ret;
@@ -1286,7 +1286,7 @@ i915_dispatch_gem_execbuffer(struct drm_device *dev,
struct drm_i915_gem_execbuffer *exec,
uint64_t exec_offset)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_clip_rect __user *boxes = (struct drm_clip_rect __user *)
(uintptr_t) exec->cliprects_ptr;
int nbox = exec->num_cliprects;
@@ -1351,7 +1351,7 @@ i915_dispatch_gem_execbuffer(struct drm_device *dev,
static int
i915_gem_ring_throttle(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int ret = 0;
mutex_lock(&dev->struct_mutex);
@@ -1696,11 +1696,11 @@ i915_gem_set_domain(struct drm_gem_object *obj,
i915_kernel_lost_context(dev);
ret = i915_gem_object_set_domain(obj, read_domains, write_domain);
if (ret) {
- drm_client_lock_release(dev);
+ drm_client_lock_release(dev, file_priv);
return ret;
}
i915_gem_dev_set_domain(obj->dev);
- drm_client_lock_release(dev);
+ drm_client_lock_release(dev, file_priv);
return 0;
}
@@ -1733,7 +1733,7 @@ i915_gem_flush_pwrite(struct drm_gem_object *obj,
void
i915_gem_lastclose(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
mutex_lock(&dev->struct_mutex);
diff --git a/linux-core/i915_init.c b/linux-core/i915_init.c
new file mode 120000
index 00000000..473ddf7b
--- /dev/null
+++ b/linux-core/i915_init.c
@@ -0,0 +1 @@
+../shared-core/i915_init.c \ No newline at end of file
diff --git a/linux-core/intel_bios.c b/linux-core/intel_bios.c
new file mode 100644
index 00000000..0cdc915c
--- /dev/null
+++ b/linux-core/intel_bios.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright © 2006 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ *
+ */
+#include "drmP.h"
+#include "drm.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+#include "intel_bios.h"
+
+#if 0
+unsigned char *
+i830_bios_get_aim_data_block (ScrnInfoPtr pScrn, int aim, int data_block)
+{
+ unsigned char *bios;
+ int bdb_off;
+ int vbt_off;
+ int aim_off;
+ struct vbt_header *vbt;
+ struct aimdb_header *aimdb;
+ struct aimdb_block *aimdb_block;
+
+ bios = i830_bios_get (pScrn);
+ if (!bios)
+ return NULL;
+
+ vbt_off = INTEL_BIOS_16(0x1a);
+ vbt = (struct vbt_header *)(bios + vbt_off);
+
+ aim_off = vbt->aim_offset[aim];
+ if (!aim_off)
+ {
+ free (bios);
+ return NULL;
+ }
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "aim_off %d\n", aim_off);
+ aimdb = (struct aimdb_header *) (bios + vbt_off + aim_off);
+ bdb_off = aimdb->aimdb_header_size;
+ while (bdb_off < aimdb->aimdb_size)
+ {
+ aimdb_block = (struct aimdb_block *) (bios + vbt_off + aim_off + bdb_off);
+ if (aimdb_block->aimdb_id == data_block)
+ {
+ unsigned char *aim = malloc (aimdb_block->aimdb_size + sizeof (struct aimdb_block));
+ if (!aim)
+ {
+ free (bios);
+ return NULL;
+ }
+ memcpy (aim, aimdb_block, aimdb_block->aimdb_size + sizeof (struct aimdb_block));
+ free (bios);
+ return aim;
+ }
+ bdb_off += aimdb_block->aimdb_size + sizeof (struct aimdb_block);
+ }
+ free (bios);
+ return NULL;
+}
+#endif
+
+static void *
+find_section(struct bdb_header *bdb, int section_id)
+{
+ u8 *base = (u8 *)bdb;
+ int index = 0;
+ u16 total, current_size;
+ u8 current_id;
+
+ /* skip to first section */
+ index += bdb->header_size;
+ total = bdb->bdb_size;
+
+ /* walk the sections looking for section_id */
+ while (index < total) {
+ current_id = *(base + index);
+ index++;
+ current_size = *((u16 *)(base + index));
+ index += 2;
+ if (current_id == section_id)
+ return base + index;
+ index += current_size;
+ }
+
+ return NULL;
+}
+
+/* Try to find panel data */
+static void
+parse_panel_data(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
+{
+ struct bdb_lvds_options *lvds_options;
+ struct bdb_lvds_lfp_data *lvds_lfp_data;
+ struct bdb_lvds_lfp_data_entry *entry;
+ struct lvds_dvo_timing *dvo_timing;
+ struct drm_display_mode *panel_fixed_mode;
+
+ /* Defaults if we can't find VBT info */
+ dev_priv->lvds_dither = 0;
+ dev_priv->lvds_vbt = 0;
+
+ lvds_options = find_section(bdb, BDB_LVDS_OPTIONS);
+ if (!lvds_options)
+ return;
+
+ dev_priv->lvds_dither = lvds_options->pixel_dither;
+ if (lvds_options->panel_type == 0xff)
+ return;
+
+ lvds_lfp_data = find_section(bdb, BDB_LVDS_LFP_DATA);
+ if (!lvds_lfp_data)
+ return;
+
+ dev_priv->lvds_vbt = 1;
+
+ entry = &lvds_lfp_data->data[lvds_options->panel_type];
+ dvo_timing = &entry->dvo_timing;
+
+ panel_fixed_mode = drm_calloc(1, sizeof(*panel_fixed_mode),
+ DRM_MEM_DRIVER);
+
+ panel_fixed_mode->hdisplay = (dvo_timing->hactive_hi << 8) |
+ dvo_timing->hactive_lo;
+ panel_fixed_mode->hsync_start = panel_fixed_mode->hdisplay +
+ ((dvo_timing->hsync_off_hi << 8) | dvo_timing->hsync_off_lo);
+ panel_fixed_mode->hsync_end = panel_fixed_mode->hsync_start +
+ dvo_timing->hsync_pulse_width;
+ panel_fixed_mode->htotal = panel_fixed_mode->hdisplay +
+ ((dvo_timing->hblank_hi << 8) | dvo_timing->hblank_lo);
+
+ panel_fixed_mode->vdisplay = (dvo_timing->vactive_hi << 8) |
+ dvo_timing->vactive_lo;
+ panel_fixed_mode->vsync_start = panel_fixed_mode->vdisplay +
+ dvo_timing->vsync_off;
+ panel_fixed_mode->vsync_end = panel_fixed_mode->vsync_start +
+ dvo_timing->vsync_pulse_width;
+ panel_fixed_mode->vtotal = panel_fixed_mode->vdisplay +
+ ((dvo_timing->vblank_hi << 8) | dvo_timing->vblank_lo);
+ panel_fixed_mode->clock = dvo_timing->clock * 10;
+ panel_fixed_mode->type = DRM_MODE_TYPE_PREFERRED;
+
+ drm_mode_set_name(panel_fixed_mode);
+
+ dev_priv->vbt_mode = panel_fixed_mode;
+
+ DRM_DEBUG("Found panel mode in BIOS VBT tables:\n");
+ drm_mode_debug_printmodeline(panel_fixed_mode);
+
+ return;
+}
+
+static void
+parse_general_features(struct drm_i915_private *dev_priv,
+ struct bdb_header *bdb)
+{
+ struct bdb_general_features *general;
+
+ /* Set sensible defaults in case we can't find the general block */
+ dev_priv->int_tv_support = 1;
+ dev_priv->int_crt_support = 1;
+
+ general = find_section(bdb, BDB_GENERAL_FEATURES);
+ if (general) {
+ dev_priv->int_tv_support = general->int_tv_support;
+ dev_priv->int_crt_support = general->int_crt_support;
+ }
+}
+
+/**
+ * intel_init_bios - initialize VBIOS settings & find VBT
+ * @dev: DRM device
+ *
+ * Loads the Video BIOS and checks that the VBT exists. Sets scratch registers
+ * to appropriate values.
+ *
+ * VBT existence is a sanity check that is relied on by other i830_bios.c code.
+ * Note that it would be better to use a BIOS call to get the VBT, as BIOSes may
+ * feed an updated VBT back through that, compared to what we'll fetch using
+ * this method of groping around in the BIOS data.
+ *
+ * Returns 0 on success, nonzero on failure.
+ */
+bool
+intel_init_bios(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct pci_dev *pdev = dev->pdev;
+ struct vbt_header *vbt = NULL;
+ struct bdb_header *bdb;
+ u8 __iomem *bios;
+ size_t size;
+ int i;
+
+ bios = pci_map_rom(pdev, &size);
+ if (!bios)
+ return -1;
+
+ /* Scour memory looking for the VBT signature */
+ for (i = 0; i + 4 < size; i++) {
+ if (!memcmp(bios + i, "$VBT", 4)) {
+ vbt = (struct vbt_header *)(bios + i);
+ break;
+ }
+ }
+
+ if (!vbt) {
+ DRM_ERROR("VBT signature missing\n");
+ pci_unmap_rom(pdev, bios);
+ return -1;
+ }
+
+ bdb = (struct bdb_header *)(bios + i + vbt->bdb_offset);
+
+ /* Grab useful general definitions */
+ parse_general_features(dev_priv, bdb);
+ parse_panel_data(dev_priv, bdb);
+
+ pci_unmap_rom(pdev, bios);
+
+ return 0;
+}
diff --git a/linux-core/intel_bios.h b/linux-core/intel_bios.h
new file mode 100644
index 00000000..5ea715ac
--- /dev/null
+++ b/linux-core/intel_bios.h
@@ -0,0 +1,405 @@
+/*
+ * Copyright © 2006 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ *
+ */
+
+#ifndef _I830_BIOS_H_
+#define _I830_BIOS_H_
+
+#include "drmP.h"
+
+struct vbt_header {
+ u8 signature[20]; /**< Always starts with 'VBT$' */
+ u16 version; /**< decimal */
+ u16 header_size; /**< in bytes */
+ u16 vbt_size; /**< in bytes */
+ u8 vbt_checksum;
+ u8 reserved0;
+ u32 bdb_offset; /**< from beginning of VBT */
+ u32 aim_offset[4]; /**< from beginning of VBT */
+} __attribute__((packed));
+
+struct bdb_header {
+ u8 signature[16]; /**< Always 'BIOS_DATA_BLOCK' */
+ u16 version; /**< decimal */
+ u16 header_size; /**< in bytes */
+ u16 bdb_size; /**< in bytes */
+};
+
+/* strictly speaking, this is a "skip" block, but it has interesting info */
+struct vbios_data {
+ u8 type; /* 0 == desktop, 1 == mobile */
+ u8 relstage;
+ u8 chipset;
+ u8 lvds_present:1;
+ u8 tv_present:1;
+ u8 rsvd2:6; /* finish byte */
+ u8 rsvd3[4];
+ u8 signon[155];
+ u8 copyright[61];
+ u16 code_segment;
+ u8 dos_boot_mode;
+ u8 bandwidth_percent;
+ u8 rsvd4; /* popup memory size */
+ u8 resize_pci_bios;
+ u8 rsvd5; /* is crt already on ddc2 */
+} __attribute__((packed));
+
+/*
+ * There are several types of BIOS data blocks (BDBs), each block has
+ * an ID and size in the first 3 bytes (ID in first, size in next 2).
+ * Known types are listed below.
+ */
+#define BDB_GENERAL_FEATURES 1
+#define BDB_GENERAL_DEFINITIONS 2
+#define BDB_OLD_TOGGLE_LIST 3
+#define BDB_MODE_SUPPORT_LIST 4
+#define BDB_GENERIC_MODE_TABLE 5
+#define BDB_EXT_MMIO_REGS 6
+#define BDB_SWF_IO 7
+#define BDB_SWF_MMIO 8
+#define BDB_DOT_CLOCK_TABLE 9
+#define BDB_MODE_REMOVAL_TABLE 10
+#define BDB_CHILD_DEVICE_TABLE 11
+#define BDB_DRIVER_FEATURES 12
+#define BDB_DRIVER_PERSISTENCE 13
+#define BDB_EXT_TABLE_PTRS 14
+#define BDB_DOT_CLOCK_OVERRIDE 15
+#define BDB_DISPLAY_SELECT 16
+/* 17 rsvd */
+#define BDB_DRIVER_ROTATION 18
+#define BDB_DISPLAY_REMOVE 19
+#define BDB_OEM_CUSTOM 20
+#define BDB_EFP_LIST 21 /* workarounds for VGA hsync/vsync */
+#define BDB_SDVO_LVDS_OPTIONS 22
+#define BDB_SDVO_PANEL_DTDS 23
+#define BDB_SDVO_LVDS_PNP_IDS 24
+#define BDB_SDVO_LVDS_POWER_SEQ 25
+#define BDB_TV_OPTIONS 26
+#define BDB_LVDS_OPTIONS 40
+#define BDB_LVDS_LFP_DATA_PTRS 41
+#define BDB_LVDS_LFP_DATA 42
+#define BDB_LVDS_BACKLIGHT 43
+#define BDB_LVDS_POWER 44
+#define BDB_SKIP 254 /* VBIOS private block, ignore */
+
+struct bdb_general_features {
+ /* bits 1 */
+ u8 panel_fitting:2;
+ u8 flexaim:1;
+ u8 msg_enable:1;
+ u8 clear_screen:3;
+ u8 color_flip:1;
+
+ /* bits 2 */
+ u8 download_ext_vbt:1;
+ u8 enable_ssc:1;
+ u8 ssc_freq:1;
+ u8 enable_lfp_on_override:1;
+ u8 disable_ssc_ddt:1;
+ u8 rsvd8:3; /* finish byte */
+
+ /* bits 3 */
+ u8 disable_smooth_vision:1;
+ u8 single_dvi:1;
+ u8 rsvd9:6; /* finish byte */
+
+ /* bits 4 */
+ u8 legacy_monitor_detect;
+
+ /* bits 5 */
+ u8 int_crt_support:1;
+ u8 int_tv_support:1;
+ u8 rsvd11:6; /* finish byte */
+} __attribute__((packed));
+
+struct bdb_general_definitions {
+ /* DDC GPIO */
+ u8 crt_ddc_gmbus_pin;
+
+ /* DPMS bits */
+ u8 dpms_acpi:1;
+ u8 skip_boot_crt_detect:1;
+ u8 dpms_aim:1;
+ u8 rsvd1:5; /* finish byte */
+
+ /* boot device bits */
+ u8 boot_display[2];
+ u8 child_dev_size;
+
+ /* device info */
+ u8 tv_or_lvds_info[33];
+ u8 dev1[33];
+ u8 dev2[33];
+ u8 dev3[33];
+ u8 dev4[33];
+ /* may be another device block here on some platforms */
+};
+
+struct bdb_lvds_options {
+ u8 panel_type;
+ u8 rsvd1;
+ /* LVDS capabilities, stored in a dword */
+ u8 rsvd2:1;
+ u8 lvds_edid:1;
+ u8 pixel_dither:1;
+ u8 pfit_ratio_auto:1;
+ u8 pfit_gfx_mode_enhanced:1;
+ u8 pfit_text_mode_enhanced:1;
+ u8 pfit_mode:2;
+ u8 rsvd4;
+} __attribute__((packed));
+
+/* LFP pointer table contains entries to the struct below */
+struct bdb_lvds_lfp_data_ptr {
+ u16 fp_timing_offset; /* offsets are from start of bdb */
+ u8 fp_table_size;
+ u16 dvo_timing_offset;
+ u8 dvo_table_size;
+ u16 panel_pnp_id_offset;
+ u8 pnp_table_size;
+} __attribute__((packed));
+
+struct bdb_lvds_lfp_data_ptrs {
+ u8 lvds_entries; /* followed by one or more lvds_data_ptr structs */
+ struct bdb_lvds_lfp_data_ptr ptr[16];
+} __attribute__((packed));
+
+/* LFP data has 3 blocks per entry */
+struct lvds_fp_timing {
+ u16 x_res;
+ u16 y_res;
+ u32 lvds_reg;
+ u32 lvds_reg_val;
+ u32 pp_on_reg;
+ u32 pp_on_reg_val;
+ u32 pp_off_reg;
+ u32 pp_off_reg_val;
+ u32 pp_cycle_reg;
+ u32 pp_cycle_reg_val;
+ u32 pfit_reg;
+ u32 pfit_reg_val;
+ u16 terminator;
+} __attribute__((packed));
+
+struct lvds_dvo_timing {
+ u16 clock; /**< In 10khz */
+ u8 hactive_lo;
+ u8 hblank_lo;
+ u8 hblank_hi:4;
+ u8 hactive_hi:4;
+ u8 vactive_lo;
+ u8 vblank_lo;
+ u8 vblank_hi:4;
+ u8 vactive_hi:4;
+ u8 hsync_off_lo;
+ u8 hsync_pulse_width;
+ u8 vsync_pulse_width:4;
+ u8 vsync_off:4;
+ u8 rsvd0:6;
+ u8 hsync_off_hi:2;
+ u8 h_image;
+ u8 v_image;
+ u8 max_hv;
+ u8 h_border;
+ u8 v_border;
+ u8 rsvd1:3;
+ u8 digital:2;
+ u8 vsync_positive:1;
+ u8 hsync_positive:1;
+ u8 rsvd2:1;
+} __attribute__((packed));
+
+struct lvds_pnp_id {
+ u16 mfg_name;
+ u16 product_code;
+ u32 serial;
+ u8 mfg_week;
+ u8 mfg_year;
+} __attribute__((packed));
+
+struct bdb_lvds_lfp_data_entry {
+ struct lvds_fp_timing fp_timing;
+ struct lvds_dvo_timing dvo_timing;
+ struct lvds_pnp_id pnp_id;
+} __attribute__((packed));
+
+struct bdb_lvds_lfp_data {
+ struct bdb_lvds_lfp_data_entry data[16];
+} __attribute__((packed));
+
+struct aimdb_header {
+ char signature[16];
+ char oem_device[20];
+ u16 aimdb_version;
+ u16 aimdb_header_size;
+ u16 aimdb_size;
+} __attribute__((packed));
+
+struct aimdb_block {
+ u8 aimdb_id;
+ u16 aimdb_size;
+} __attribute__((packed));
+
+struct vch_panel_data {
+ u16 fp_timing_offset;
+ u8 fp_timing_size;
+ u16 dvo_timing_offset;
+ u8 dvo_timing_size;
+ u16 text_fitting_offset;
+ u8 text_fitting_size;
+ u16 graphics_fitting_offset;
+ u8 graphics_fitting_size;
+} __attribute__((packed));
+
+struct vch_bdb_22 {
+ struct aimdb_block aimdb_block;
+ struct vch_panel_data panels[16];
+} __attribute__((packed));
+
+bool intel_init_bios(struct drm_device *dev);
+
+/*
+ * Driver<->VBIOS interaction occurs through scratch bits in
+ * GR18 & SWF*.
+ */
+
+/* GR18 bits are set on display switch and hotkey events */
+#define GR18_DRIVER_SWITCH_EN (1<<7) /* 0: VBIOS control, 1: driver control */
+#define GR18_HOTKEY_MASK 0x78 /* See also SWF4 15:0 */
+#define GR18_HK_NONE (0x0<<3)
+#define GR18_HK_LFP_STRETCH (0x1<<3)
+#define GR18_HK_TOGGLE_DISP (0x2<<3)
+#define GR18_HK_DISP_SWITCH (0x4<<3) /* see SWF14 15:0 for what to enable */
+#define GR18_HK_POPUP_DISABLED (0x6<<3)
+#define GR18_HK_POPUP_ENABLED (0x7<<3)
+#define GR18_HK_PFIT (0x8<<3)
+#define GR18_HK_APM_CHANGE (0xa<<3)
+#define GR18_HK_MULTIPLE (0xc<<3)
+#define GR18_USER_INT_EN (1<<2)
+#define GR18_A0000_FLUSH_EN (1<<1)
+#define GR18_SMM_EN (1<<0)
+
+/* Set by driver, cleared by VBIOS */
+#define SWF00_YRES_SHIFT 16
+#define SWF00_XRES_SHIFT 0
+#define SWF00_RES_MASK 0xffff
+
+/* Set by VBIOS at boot time and driver at runtime */
+#define SWF01_TV2_FORMAT_SHIFT 8
+#define SWF01_TV1_FORMAT_SHIFT 0
+#define SWF01_TV_FORMAT_MASK 0xffff
+
+#define SWF10_VBIOS_BLC_I2C_EN (1<<29)
+#define SWF10_GTT_OVERRIDE_EN (1<<28)
+#define SWF10_LFP_DPMS_OVR (1<<27) /* override DPMS on display switch */
+#define SWF10_ACTIVE_TOGGLE_LIST_MASK (7<<24)
+#define SWF10_OLD_TOGGLE 0x0
+#define SWF10_TOGGLE_LIST_1 0x1
+#define SWF10_TOGGLE_LIST_2 0x2
+#define SWF10_TOGGLE_LIST_3 0x3
+#define SWF10_TOGGLE_LIST_4 0x4
+#define SWF10_PANNING_EN (1<<23)
+#define SWF10_DRIVER_LOADED (1<<22)
+#define SWF10_EXTENDED_DESKTOP (1<<21)
+#define SWF10_EXCLUSIVE_MODE (1<<20)
+#define SWF10_OVERLAY_EN (1<<19)
+#define SWF10_PLANEB_HOLDOFF (1<<18)
+#define SWF10_PLANEA_HOLDOFF (1<<17)
+#define SWF10_VGA_HOLDOFF (1<<16)
+#define SWF10_ACTIVE_DISP_MASK 0xffff
+#define SWF10_PIPEB_LFP2 (1<<15)
+#define SWF10_PIPEB_EFP2 (1<<14)
+#define SWF10_PIPEB_TV2 (1<<13)
+#define SWF10_PIPEB_CRT2 (1<<12)
+#define SWF10_PIPEB_LFP (1<<11)
+#define SWF10_PIPEB_EFP (1<<10)
+#define SWF10_PIPEB_TV (1<<9)
+#define SWF10_PIPEB_CRT (1<<8)
+#define SWF10_PIPEA_LFP2 (1<<7)
+#define SWF10_PIPEA_EFP2 (1<<6)
+#define SWF10_PIPEA_TV2 (1<<5)
+#define SWF10_PIPEA_CRT2 (1<<4)
+#define SWF10_PIPEA_LFP (1<<3)
+#define SWF10_PIPEA_EFP (1<<2)
+#define SWF10_PIPEA_TV (1<<1)
+#define SWF10_PIPEA_CRT (1<<0)
+
+#define SWF11_MEMORY_SIZE_SHIFT 16
+#define SWF11_SV_TEST_EN (1<<15)
+#define SWF11_IS_AGP (1<<14)
+#define SWF11_DISPLAY_HOLDOFF (1<<13)
+#define SWF11_DPMS_REDUCED (1<<12)
+#define SWF11_IS_VBE_MODE (1<<11)
+#define SWF11_PIPEB_ACCESS (1<<10) /* 0 here means pipe a */
+#define SWF11_DPMS_MASK 0x07
+#define SWF11_DPMS_OFF (1<<2)
+#define SWF11_DPMS_SUSPEND (1<<1)
+#define SWF11_DPMS_STANDBY (1<<0)
+#define SWF11_DPMS_ON 0
+
+#define SWF14_GFX_PFIT_EN (1<<31)
+#define SWF14_TEXT_PFIT_EN (1<<30)
+#define SWF14_LID_STATUS_CLOSED (1<<29) /* 0 here means open */
+#define SWF14_POPUP_EN (1<<28)
+#define SWF14_DISPLAY_HOLDOFF (1<<27)
+#define SWF14_DISP_DETECT_EN (1<<26)
+#define SWF14_DOCKING_STATUS_DOCKED (1<<25) /* 0 here means undocked */
+#define SWF14_DRIVER_STATUS (1<<24)
+#define SWF14_OS_TYPE_WIN9X (1<<23)
+#define SWF14_OS_TYPE_WINNT (1<<22)
+/* 21:19 rsvd */
+#define SWF14_PM_TYPE_MASK 0x00070000
+#define SWF14_PM_ACPI_VIDEO (0x4 << 16)
+#define SWF14_PM_ACPI (0x3 << 16)
+#define SWF14_PM_APM_12 (0x2 << 16)
+#define SWF14_PM_APM_11 (0x1 << 16)
+#define SWF14_HK_REQUEST_MASK 0x0000ffff /* see GR18 6:3 for event type */
+ /* if GR18 indicates a display switch */
+#define SWF14_DS_PIPEB_LFP2_EN (1<<15)
+#define SWF14_DS_PIPEB_EFP2_EN (1<<14)
+#define SWF14_DS_PIPEB_TV2_EN (1<<13)
+#define SWF14_DS_PIPEB_CRT2_EN (1<<12)
+#define SWF14_DS_PIPEB_LFP_EN (1<<11)
+#define SWF14_DS_PIPEB_EFP_EN (1<<10)
+#define SWF14_DS_PIPEB_TV_EN (1<<9)
+#define SWF14_DS_PIPEB_CRT_EN (1<<8)
+#define SWF14_DS_PIPEA_LFP2_EN (1<<7)
+#define SWF14_DS_PIPEA_EFP2_EN (1<<6)
+#define SWF14_DS_PIPEA_TV2_EN (1<<5)
+#define SWF14_DS_PIPEA_CRT2_EN (1<<4)
+#define SWF14_DS_PIPEA_LFP_EN (1<<3)
+#define SWF14_DS_PIPEA_EFP_EN (1<<2)
+#define SWF14_DS_PIPEA_TV_EN (1<<1)
+#define SWF14_DS_PIPEA_CRT_EN (1<<0)
+ /* if GR18 indicates a panel fitting request */
+#define SWF14_PFIT_EN (1<<0) /* 0 means disable */
+ /* if GR18 indicates an APM change request */
+#define SWF14_APM_HIBERNATE 0x4
+#define SWF14_APM_SUSPEND 0x3
+#define SWF14_APM_STANDBY 0x1
+#define SWF14_APM_RESTORE 0x0
+
+#endif /* _I830_BIOS_H_ */
diff --git a/linux-core/intel_crt.c b/linux-core/intel_crt.c
new file mode 100644
index 00000000..b9e8ee63
--- /dev/null
+++ b/linux-core/intel_crt.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright © 2006-2007 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ */
+
+#include <linux/i2c.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 temp;
+
+ temp = I915_READ(ADPA);
+ temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
+ temp &= ~ADPA_DAC_ENABLE;
+
+ switch(mode) {
+ case DPMSModeOn:
+ temp |= ADPA_DAC_ENABLE;
+ break;
+ case DPMSModeStandby:
+ temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE;
+ break;
+ case DPMSModeSuspend:
+ temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE;
+ break;
+ case DPMSModeOff:
+ temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE;
+ break;
+ }
+
+ I915_WRITE(ADPA, temp);
+}
+
+static void intel_crt_save(struct drm_connector *connector)
+{
+
+}
+
+static void intel_crt_restore(struct drm_connector *connector)
+{
+
+}
+
+static int intel_crt_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ if (mode->flags & V_DBLSCAN)
+ return MODE_NO_DBLESCAN;
+
+ if (mode->clock > 400000 || mode->clock < 25000)
+ return MODE_CLOCK_RANGE;
+
+ return MODE_OK;
+}
+
+static bool intel_crt_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+static void intel_crt_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+
+ struct drm_device *dev = encoder->dev;
+ struct drm_crtc *crtc = encoder->crtc;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int dpll_md_reg;
+ u32 adpa, dpll_md;
+
+ if (intel_crtc->pipe == 0)
+ dpll_md_reg = DPLL_A_MD;
+ else
+ dpll_md_reg = DPLL_B_MD;
+
+ /*
+ * Disable separate mode multiplier used when cloning SDVO to CRT
+ * XXX this needs to be adjusted when we really are cloning
+ */
+ if (IS_I965G(dev)) {
+ dpll_md = I915_READ(dpll_md_reg);
+ I915_WRITE(dpll_md_reg,
+ dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK);
+ }
+
+ adpa = 0;
+ if (adjusted_mode->flags & V_PHSYNC)
+ adpa |= ADPA_HSYNC_ACTIVE_HIGH;
+ if (adjusted_mode->flags & V_PVSYNC)
+ adpa |= ADPA_VSYNC_ACTIVE_HIGH;
+
+ if (intel_crtc->pipe == 0)
+ adpa |= ADPA_PIPE_A_SELECT;
+ else
+ adpa |= ADPA_PIPE_B_SELECT;
+
+ I915_WRITE(ADPA, adpa);
+}
+
+/**
+ * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence.
+ *
+ * Not for i915G/i915GM
+ *
+ * \return TRUE if CRT is connected.
+ * \return FALSE if CRT is disconnected.
+ */
+static bool intel_crt_detect_hotplug(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 temp;
+
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+
+ temp = I915_READ(PORT_HOTPLUG_EN);
+
+ I915_WRITE(PORT_HOTPLUG_EN,
+ temp | CRT_HOTPLUG_FORCE_DETECT | (1 << 5));
+
+ do {
+ if (!(I915_READ(PORT_HOTPLUG_EN) & CRT_HOTPLUG_FORCE_DETECT))
+ break;
+ msleep(1);
+ } while (time_after(timeout, jiffies));
+
+ if ((I915_READ(PORT_HOTPLUG_STAT) & CRT_HOTPLUG_MONITOR_MASK) ==
+ CRT_HOTPLUG_MONITOR_COLOR)
+ return true;
+
+ return false;
+}
+
+static bool intel_crt_detect_ddc(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ /* CRT should always be at 0, but check anyway */
+ if (intel_output->type != INTEL_OUTPUT_ANALOG)
+ return false;
+
+ return intel_ddc_probe(intel_output);
+}
+
+static enum drm_connector_status intel_crt_detect(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+
+ if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
+ if (intel_crt_detect_hotplug(connector))
+ return connector_status_connected;
+ else
+ return connector_status_disconnected;
+ }
+
+ if (intel_crt_detect_ddc(connector))
+ return connector_status_connected;
+
+ /* TODO use load detect */
+ return connector_status_unknown;
+}
+
+static void intel_crt_destroy(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ intel_i2c_destroy(intel_output->ddc_bus);
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
+ kfree(connector);
+}
+
+static int intel_crt_get_modes(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ return intel_ddc_get_modes(intel_output);
+}
+
+static bool intel_crt_set_property(struct drm_connector *connector,
+ struct drm_property *property,
+ uint64_t value)
+{
+ struct drm_device *dev = connector->dev;
+
+ if (property == dev->mode_config.dpms_property && connector->encoder)
+ intel_crt_dpms(connector->encoder, (uint32_t)(value & 0xf));
+
+ return true;
+}
+
+/*
+ * Routines for controlling stuff on the analog port
+ */
+
+static const struct drm_encoder_helper_funcs intel_crt_helper_funcs = {
+ .dpms = intel_crt_dpms,
+ .mode_fixup = intel_crt_mode_fixup,
+ .prepare = intel_encoder_prepare,
+ .commit = intel_encoder_commit,
+ .mode_set = intel_crt_mode_set,
+};
+
+static const struct drm_connector_funcs intel_crt_connector_funcs = {
+ .save = intel_crt_save,
+ .restore = intel_crt_restore,
+ .detect = intel_crt_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = intel_crt_destroy,
+ .set_property = intel_crt_set_property,
+};
+
+static const struct drm_connector_helper_funcs intel_crt_connector_helper_funcs = {
+ .mode_valid = intel_crt_mode_valid,
+ .get_modes = intel_crt_get_modes,
+ .best_encoder = intel_best_encoder,
+};
+
+void intel_crt_enc_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs intel_crt_enc_funcs = {
+ .destroy = intel_crt_enc_destroy,
+};
+
+void intel_crt_init(struct drm_device *dev)
+{
+ struct drm_connector *connector;
+ struct intel_output *intel_output;
+
+ intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL);
+ if (!intel_output)
+ return;
+
+ connector = &intel_output->base;
+ drm_connector_init(dev, &intel_output->base,
+ &intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA);
+
+ drm_encoder_init(dev, &intel_output->enc, &intel_crt_enc_funcs,
+ DRM_MODE_ENCODER_DAC);
+
+ drm_mode_connector_attach_encoder(&intel_output->base,
+ &intel_output->enc);
+
+ /* Set up the DDC bus. */
+ intel_output->ddc_bus = intel_i2c_create(dev, GPIOA, "CRTDDC_A");
+ if (!intel_output->ddc_bus) {
+ dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration "
+ "failed.\n");
+ return;
+ }
+
+ intel_output->type = INTEL_OUTPUT_ANALOG;
+ connector->interlace_allowed = 0;
+ connector->doublescan_allowed = 0;
+
+ drm_encoder_helper_add(&intel_output->enc, &intel_crt_helper_funcs);
+ drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs);
+
+ drm_sysfs_connector_add(connector);
+}
diff --git a/linux-core/intel_display.c b/linux-core/intel_display.c
new file mode 100644
index 00000000..0a2854a2
--- /dev/null
+++ b/linux-core/intel_display.c
@@ -0,0 +1,1613 @@
+/*
+ * Copyright © 2006-2007 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ */
+
+#include <linux/i2c.h>
+#include "drmP.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+#include "drm_crtc_helper.h"
+
+bool intel_pipe_has_type (struct drm_crtc *crtc, int type);
+
+typedef struct {
+ /* given values */
+ int n;
+ int m1, m2;
+ int p1, p2;
+ /* derived values */
+ int dot;
+ int vco;
+ int m;
+ int p;
+} intel_clock_t;
+
+typedef struct {
+ int min, max;
+} intel_range_t;
+
+typedef struct {
+ int dot_limit;
+ int p2_slow, p2_fast;
+} intel_p2_t;
+
+#define INTEL_P2_NUM 2
+
+typedef struct {
+ intel_range_t dot, vco, n, m, m1, m2, p, p1;
+ intel_p2_t p2;
+} intel_limit_t;
+
+#define I8XX_DOT_MIN 25000
+#define I8XX_DOT_MAX 350000
+#define I8XX_VCO_MIN 930000
+#define I8XX_VCO_MAX 1400000
+#define I8XX_N_MIN 3
+#define I8XX_N_MAX 16
+#define I8XX_M_MIN 96
+#define I8XX_M_MAX 140
+#define I8XX_M1_MIN 18
+#define I8XX_M1_MAX 26
+#define I8XX_M2_MIN 6
+#define I8XX_M2_MAX 16
+#define I8XX_P_MIN 4
+#define I8XX_P_MAX 128
+#define I8XX_P1_MIN 2
+#define I8XX_P1_MAX 33
+#define I8XX_P1_LVDS_MIN 1
+#define I8XX_P1_LVDS_MAX 6
+#define I8XX_P2_SLOW 4
+#define I8XX_P2_FAST 2
+#define I8XX_P2_LVDS_SLOW 14
+#define I8XX_P2_LVDS_FAST 14 /* No fast option */
+#define I8XX_P2_SLOW_LIMIT 165000
+
+#define I9XX_DOT_MIN 20000
+#define I9XX_DOT_MAX 400000
+#define I9XX_VCO_MIN 1400000
+#define I9XX_VCO_MAX 2800000
+#define I9XX_N_MIN 3
+#define I9XX_N_MAX 8
+#define I9XX_M_MIN 70
+#define I9XX_M_MAX 120
+#define I9XX_M1_MIN 10
+#define I9XX_M1_MAX 20
+#define I9XX_M2_MIN 5
+#define I9XX_M2_MAX 9
+#define I9XX_P_SDVO_DAC_MIN 5
+#define I9XX_P_SDVO_DAC_MAX 80
+#define I9XX_P_LVDS_MIN 7
+#define I9XX_P_LVDS_MAX 98
+#define I9XX_P1_MIN 1
+#define I9XX_P1_MAX 8
+#define I9XX_P2_SDVO_DAC_SLOW 10
+#define I9XX_P2_SDVO_DAC_FAST 5
+#define I9XX_P2_SDVO_DAC_SLOW_LIMIT 200000
+#define I9XX_P2_LVDS_SLOW 14
+#define I9XX_P2_LVDS_FAST 7
+#define I9XX_P2_LVDS_SLOW_LIMIT 112000
+
+#define INTEL_LIMIT_I8XX_DVO_DAC 0
+#define INTEL_LIMIT_I8XX_LVDS 1
+#define INTEL_LIMIT_I9XX_SDVO_DAC 2
+#define INTEL_LIMIT_I9XX_LVDS 3
+
+static const intel_limit_t intel_limits[] = {
+ { /* INTEL_LIMIT_I8XX_DVO_DAC */
+ .dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX },
+ .vco = { .min = I8XX_VCO_MIN, .max = I8XX_VCO_MAX },
+ .n = { .min = I8XX_N_MIN, .max = I8XX_N_MAX },
+ .m = { .min = I8XX_M_MIN, .max = I8XX_M_MAX },
+ .m1 = { .min = I8XX_M1_MIN, .max = I8XX_M1_MAX },
+ .m2 = { .min = I8XX_M2_MIN, .max = I8XX_M2_MAX },
+ .p = { .min = I8XX_P_MIN, .max = I8XX_P_MAX },
+ .p1 = { .min = I8XX_P1_MIN, .max = I8XX_P1_MAX },
+ .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT,
+ .p2_slow = I8XX_P2_SLOW, .p2_fast = I8XX_P2_FAST },
+ },
+ { /* INTEL_LIMIT_I8XX_LVDS */
+ .dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX },
+ .vco = { .min = I8XX_VCO_MIN, .max = I8XX_VCO_MAX },
+ .n = { .min = I8XX_N_MIN, .max = I8XX_N_MAX },
+ .m = { .min = I8XX_M_MIN, .max = I8XX_M_MAX },
+ .m1 = { .min = I8XX_M1_MIN, .max = I8XX_M1_MAX },
+ .m2 = { .min = I8XX_M2_MIN, .max = I8XX_M2_MAX },
+ .p = { .min = I8XX_P_MIN, .max = I8XX_P_MAX },
+ .p1 = { .min = I8XX_P1_LVDS_MIN, .max = I8XX_P1_LVDS_MAX },
+ .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT,
+ .p2_slow = I8XX_P2_LVDS_SLOW, .p2_fast = I8XX_P2_LVDS_FAST },
+ },
+ { /* INTEL_LIMIT_I9XX_SDVO_DAC */
+ .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX },
+ .vco = { .min = I9XX_VCO_MIN, .max = I9XX_VCO_MAX },
+ .n = { .min = I9XX_N_MIN, .max = I9XX_N_MAX },
+ .m = { .min = I9XX_M_MIN, .max = I9XX_M_MAX },
+ .m1 = { .min = I9XX_M1_MIN, .max = I9XX_M1_MAX },
+ .m2 = { .min = I9XX_M2_MIN, .max = I9XX_M2_MAX },
+ .p = { .min = I9XX_P_SDVO_DAC_MIN, .max = I9XX_P_SDVO_DAC_MAX },
+ .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX },
+ .p2 = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT,
+ .p2_slow = I9XX_P2_SDVO_DAC_SLOW, .p2_fast = I9XX_P2_SDVO_DAC_FAST },
+ },
+ { /* INTEL_LIMIT_I9XX_LVDS */
+ .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX },
+ .vco = { .min = I9XX_VCO_MIN, .max = I9XX_VCO_MAX },
+ .n = { .min = I9XX_N_MIN, .max = I9XX_N_MAX },
+ .m = { .min = I9XX_M_MIN, .max = I9XX_M_MAX },
+ .m1 = { .min = I9XX_M1_MIN, .max = I9XX_M1_MAX },
+ .m2 = { .min = I9XX_M2_MIN, .max = I9XX_M2_MAX },
+ .p = { .min = I9XX_P_LVDS_MIN, .max = I9XX_P_LVDS_MAX },
+ .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX },
+ /* The single-channel range is 25-112Mhz, and dual-channel
+ * is 80-224Mhz. Prefer single channel as much as possible.
+ */
+ .p2 = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT,
+ .p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_FAST },
+ },
+};
+
+static const intel_limit_t *intel_limit(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ const intel_limit_t *limit;
+
+ if (IS_I9XX(dev)) {
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+ limit = &intel_limits[INTEL_LIMIT_I9XX_LVDS];
+ else
+ limit = &intel_limits[INTEL_LIMIT_I9XX_SDVO_DAC];
+ } else {
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+ limit = &intel_limits[INTEL_LIMIT_I8XX_LVDS];
+ else
+ limit = &intel_limits[INTEL_LIMIT_I8XX_DVO_DAC];
+ }
+ return limit;
+}
+
+/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */
+
+static void i8xx_clock(int refclk, intel_clock_t *clock)
+{
+ clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2);
+ clock->p = clock->p1 * clock->p2;
+ clock->vco = refclk * clock->m / (clock->n + 2);
+ clock->dot = clock->vco / clock->p;
+}
+
+/** Derive the pixel clock for the given refclk and divisors for 9xx chips. */
+
+static void i9xx_clock(int refclk, intel_clock_t *clock)
+{
+ clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2);
+ clock->p = clock->p1 * clock->p2;
+ clock->vco = refclk * clock->m / (clock->n + 2);
+ clock->dot = clock->vco / clock->p;
+}
+
+static void intel_clock(struct drm_device *dev, int refclk,
+ intel_clock_t *clock)
+{
+ if (IS_I9XX(dev))
+ return i9xx_clock (refclk, clock);
+ else
+ return i8xx_clock (refclk, clock);
+}
+
+/**
+ * Returns whether any output on the specified pipe is of the specified type
+ */
+bool intel_pipe_has_type (struct drm_crtc *crtc, int type)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_mode_config *mode_config = &dev->mode_config;
+ struct drm_connector *l_entry;
+
+ list_for_each_entry(l_entry, &mode_config->connector_list, head) {
+ if (l_entry->encoder &&
+ l_entry->encoder->crtc == crtc) {
+ struct intel_output *intel_output = to_intel_output(l_entry);
+ if (intel_output->type == type)
+ return true;
+ }
+ }
+ return false;
+}
+
+#define INTELPllInvalid(s) { /* ErrorF (s) */; return false; }
+/**
+ * Returns whether the given set of divisors are valid for a given refclk with
+ * the given connectors.
+ */
+
+static bool intel_PLL_is_valid(struct drm_crtc *crtc, intel_clock_t *clock)
+{
+ const intel_limit_t *limit = intel_limit (crtc);
+
+ if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1)
+ INTELPllInvalid ("p1 out of range\n");
+ if (clock->p < limit->p.min || limit->p.max < clock->p)
+ INTELPllInvalid ("p out of range\n");
+ if (clock->m2 < limit->m2.min || limit->m2.max < clock->m2)
+ INTELPllInvalid ("m2 out of range\n");
+ if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1)
+ INTELPllInvalid ("m1 out of range\n");
+ if (clock->m1 <= clock->m2)
+ INTELPllInvalid ("m1 <= m2\n");
+ if (clock->m < limit->m.min || limit->m.max < clock->m)
+ INTELPllInvalid ("m out of range\n");
+ if (clock->n < limit->n.min || limit->n.max < clock->n)
+ INTELPllInvalid ("n out of range\n");
+ if (clock->vco < limit->vco.min || limit->vco.max < clock->vco)
+ INTELPllInvalid ("vco out of range\n");
+ /* XXX: We may need to be checking "Dot clock" depending on the multiplier,
+ * connector, etc., rather than just a single range.
+ */
+ if (clock->dot < limit->dot.min || limit->dot.max < clock->dot)
+ INTELPllInvalid ("dot out of range\n");
+
+ return true;
+}
+
+/**
+ * Returns a set of divisors for the desired target clock with the given
+ * refclk, or FALSE. The returned values represent the clock equation:
+ * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
+ */
+static bool intel_find_best_PLL(struct drm_crtc *crtc, int target,
+ int refclk, intel_clock_t *best_clock)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ intel_clock_t clock;
+ const intel_limit_t *limit = intel_limit(crtc);
+ int err = target;
+
+ if (IS_I9XX(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) &&
+ (I915_READ(LVDS) & LVDS_PORT_EN) != 0) {
+ /*
+ * For LVDS, if the panel is on, just rely on its current
+ * settings for dual-channel. We haven't figured out how to
+ * reliably set up different single/dual channel state, if we
+ * even can.
+ */
+ if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
+ LVDS_CLKB_POWER_UP)
+ clock.p2 = limit->p2.p2_fast;
+ else
+ clock.p2 = limit->p2.p2_slow;
+ } else {
+ if (target < limit->p2.dot_limit)
+ clock.p2 = limit->p2.p2_slow;
+ else
+ clock.p2 = limit->p2.p2_fast;
+ }
+
+ memset (best_clock, 0, sizeof (*best_clock));
+
+ for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) {
+ for (clock.m2 = limit->m2.min; clock.m2 < clock.m1 &&
+ clock.m2 <= limit->m2.max; clock.m2++) {
+ for (clock.n = limit->n.min; clock.n <= limit->n.max;
+ clock.n++) {
+ for (clock.p1 = limit->p1.min;
+ clock.p1 <= limit->p1.max; clock.p1++) {
+ int this_err;
+
+ intel_clock(dev, refclk, &clock);
+
+ if (!intel_PLL_is_valid(crtc, &clock))
+ continue;
+
+ this_err = abs(clock.dot - target);
+ if (this_err < err) {
+ *best_clock = clock;
+ err = this_err;
+ }
+ }
+ }
+ }
+ }
+
+ return (err != target);
+}
+
+void
+intel_set_vblank(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_crtc *crtc;
+ struct intel_crtc *intel_crtc;
+ int vbl_pipe = 0;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ intel_crtc = to_intel_crtc(crtc);
+
+ if (crtc->enabled)
+ vbl_pipe |= (1<<intel_crtc->pipe);
+ }
+
+ dev_priv->vblank_pipe = vbl_pipe;
+ i915_enable_interrupt(dev);
+}
+void
+intel_wait_for_vblank(struct drm_device *dev)
+{
+ /* Wait for 20ms, i.e. one cycle at 50hz. */
+ udelay(20000);
+}
+
+void
+intel_pipe_set_base(struct drm_crtc *crtc, int x, int y)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_master_private *master_priv;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct intel_framebuffer *intel_fb;
+ int pipe = intel_crtc->pipe;
+ unsigned long Start, Offset;
+ int dspbase = (pipe == 0 ? DSPAADDR : DSPBADDR);
+ int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF);
+ int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE;
+ int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
+ u32 dspcntr;
+
+ /* no fb bound */
+ if (!crtc->fb) {
+ DRM_DEBUG("No FB bound\n");
+ return;
+ }
+
+ intel_fb = to_intel_framebuffer(crtc->fb);
+
+ Start = intel_fb->bo->offset;
+ Offset = y * crtc->fb->pitch + x * (crtc->fb->bits_per_pixel / 8);
+
+ I915_WRITE(dspstride, crtc->fb->pitch);
+
+ dspcntr = I915_READ(dspcntr_reg);
+ switch (crtc->fb->bits_per_pixel) {
+ case 8:
+ dspcntr |= DISPPLANE_8BPP;
+ break;
+ case 16:
+ if (crtc->fb->depth == 15)
+ dspcntr |= DISPPLANE_15_16BPP;
+ else
+ dspcntr |= DISPPLANE_16BPP;
+ break;
+ case 24:
+ case 32:
+ dspcntr |= DISPPLANE_32BPP_NO_ALPHA;
+ break;
+ default:
+ DRM_ERROR("Unknown color depth\n");
+ return;
+ }
+ I915_WRITE(dspcntr_reg, dspcntr);
+
+ DRM_DEBUG("Writing base %08lX %08lX %d %d\n", Start, Offset, x, y);
+ if (IS_I965G(dev)) {
+ I915_WRITE(dspbase, Offset);
+ I915_READ(dspbase);
+ I915_WRITE(dspsurf, Start);
+ I915_READ(dspsurf);
+ } else {
+ I915_WRITE(dspbase, Start + Offset);
+ I915_READ(dspbase);
+ }
+
+
+ if (!dev->primary->master)
+ return;
+
+ master_priv = dev->primary->master->driver_priv;
+ if (!master_priv->sarea_priv)
+ return;
+
+ switch (pipe) {
+ case 0:
+ master_priv->sarea_priv->planeA_x = x;
+ master_priv->sarea_priv->planeA_y = y;
+ break;
+ case 1:
+ master_priv->sarea_priv->planeB_x = x;
+ master_priv->sarea_priv->planeB_y = y;
+ break;
+ default:
+ DRM_ERROR("Can't update pipe %d in SAREA\n", pipe);
+ break;
+ }
+}
+
+
+
+/**
+ * Sets the power management mode of the pipe and plane.
+ *
+ * This code should probably grow support for turning the cursor off and back
+ * on appropriately at the same time as we're turning the pipe off/on.
+ */
+static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_master_private *master_priv;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
+ int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
+ int dspbase_reg = (pipe == 0) ? DSPAADDR : DSPBADDR;
+ int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
+ u32 temp;
+ bool enabled;
+
+ /* XXX: When our outputs are all unaware of DPMS modes other than off
+ * and on, we should map those modes to DPMSModeOff in the CRTC.
+ */
+ switch (mode) {
+ case DPMSModeOn:
+ case DPMSModeStandby:
+ case DPMSModeSuspend:
+ /* Enable the DPLL */
+ temp = I915_READ(dpll_reg);
+ if ((temp & DPLL_VCO_ENABLE) == 0) {
+ I915_WRITE(dpll_reg, temp);
+ I915_READ(dpll_reg);
+ /* Wait for the clocks to stabilize. */
+ udelay(150);
+ I915_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE);
+ I915_READ(dpll_reg);
+ /* Wait for the clocks to stabilize. */
+ udelay(150);
+ I915_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE);
+ I915_READ(dpll_reg);
+ /* Wait for the clocks to stabilize. */
+ udelay(150);
+ }
+
+ /* Enable the pipe */
+ temp = I915_READ(pipeconf_reg);
+ if ((temp & PIPEACONF_ENABLE) == 0)
+ I915_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE);
+
+ /* Enable the plane */
+ temp = I915_READ(dspcntr_reg);
+ if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
+ I915_WRITE(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE);
+ /* Flush the plane changes */
+ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+ }
+
+ intel_crtc_load_lut(crtc);
+
+ /* Give the overlay scaler a chance to enable if it's on this pipe */
+ //intel_crtc_dpms_video(crtc, TRUE); TODO
+ break;
+ case DPMSModeOff:
+ /* Give the overlay scaler a chance to disable if it's on this pipe */
+ //intel_crtc_dpms_video(crtc, FALSE); TODO
+
+ /* Disable the VGA plane that we never use */
+ I915_WRITE(VGACNTRL, VGA_DISP_DISABLE);
+
+ /* Disable display plane */
+ temp = I915_READ(dspcntr_reg);
+ if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
+ I915_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE);
+ /* Flush the plane changes */
+ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+ I915_READ(dspbase_reg);
+ }
+
+ if (!IS_I9XX(dev)) {
+ /* Wait for vblank for the disable to take effect */
+ intel_wait_for_vblank(dev);
+ }
+
+ /* Next, disable display pipes */
+ temp = I915_READ(pipeconf_reg);
+ if ((temp & PIPEACONF_ENABLE) != 0) {
+ I915_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE);
+ I915_READ(pipeconf_reg);
+ }
+
+ /* Wait for vblank for the disable to take effect. */
+ intel_wait_for_vblank(dev);
+
+ temp = I915_READ(dpll_reg);
+ if ((temp & DPLL_VCO_ENABLE) != 0) {
+ I915_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE);
+ I915_READ(dpll_reg);
+ }
+
+ /* Wait for the clocks to turn off. */
+ udelay(150);
+ break;
+ }
+
+ if (!dev->primary->master)
+ return;
+
+ master_priv = dev->primary->master->driver_priv;
+ if (!master_priv->sarea_priv)
+ return;
+
+ enabled = crtc->enabled && mode != DPMSModeOff;
+
+ switch (pipe) {
+ case 0:
+ master_priv->sarea_priv->planeA_w = enabled ? crtc->mode.hdisplay : 0;
+ master_priv->sarea_priv->planeA_h = enabled ? crtc->mode.vdisplay : 0;
+ break;
+ case 1:
+ master_priv->sarea_priv->planeB_w = enabled ? crtc->mode.hdisplay : 0;
+ master_priv->sarea_priv->planeB_h = enabled ? crtc->mode.vdisplay : 0;
+ break;
+ default:
+ DRM_ERROR("Can't update pipe %d in SAREA\n", pipe);
+ break;
+ }
+
+ intel_crtc->dpms_mode = mode;
+}
+
+static void intel_crtc_prepare (struct drm_crtc *crtc)
+{
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+ crtc_funcs->dpms(crtc, DPMSModeOff);
+}
+
+static void intel_crtc_commit (struct drm_crtc *crtc)
+{
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+ crtc_funcs->dpms(crtc, DPMSModeOn);
+}
+
+void intel_encoder_prepare (struct drm_encoder *encoder)
+{
+ struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
+ /* lvds has its own version of prepare see intel_lvds_prepare */
+ encoder_funcs->dpms(encoder, DPMSModeOff);
+}
+
+void intel_encoder_commit (struct drm_encoder *encoder)
+{
+ struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
+ /* lvds has its own version of commit see intel_lvds_commit */
+ encoder_funcs->dpms(encoder, DPMSModeOn);
+}
+
+static bool intel_crtc_mode_fixup(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+
+/** Returns the core display clock speed for i830 - i945 */
+static int intel_get_core_clock_speed(struct drm_device *dev)
+{
+
+ /* Core clock values taken from the published datasheets.
+ * The 830 may go up to 166 Mhz, which we should check.
+ */
+ if (IS_I945G(dev))
+ return 400000;
+ else if (IS_I915G(dev))
+ return 333000;
+ else if (IS_I945GM(dev) || IS_845G(dev))
+ return 200000;
+ else if (IS_I915GM(dev)) {
+ u16 gcfgc = 0;
+
+ pci_read_config_word(dev->pdev, GCFGC, &gcfgc);
+
+ if (gcfgc & GC_LOW_FREQUENCY_ENABLE)
+ return 133000;
+ else {
+ switch (gcfgc & GC_DISPLAY_CLOCK_MASK) {
+ case GC_DISPLAY_CLOCK_333_MHZ:
+ return 333000;
+ default:
+ case GC_DISPLAY_CLOCK_190_200_MHZ:
+ return 190000;
+ }
+ }
+ } else if (IS_I865G(dev))
+ return 266000;
+ else if (IS_I855(dev)) {
+#if 0
+ PCITAG bridge = pciTag(0, 0, 0); /* This is always the host bridge */
+ u16 hpllcc = pciReadWord(bridge, HPLLCC);
+
+#endif
+ u16 hpllcc = 0;
+ /* Assume that the hardware is in the high speed state. This
+ * should be the default.
+ */
+ switch (hpllcc & GC_CLOCK_CONTROL_MASK) {
+ case GC_CLOCK_133_200:
+ case GC_CLOCK_100_200:
+ return 200000;
+ case GC_CLOCK_166_250:
+ return 250000;
+ case GC_CLOCK_100_133:
+ return 133000;
+ }
+ } else /* 852, 830 */
+ return 133000;
+
+ return 0; /* Silence gcc warning */
+}
+
+
+/**
+ * Return the pipe currently connected to the panel fitter,
+ * or -1 if the panel fitter is not present or not in use
+ */
+static int intel_panel_fitter_pipe (struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 pfit_control;
+
+ /* i830 doesn't have a panel fitter */
+ if (IS_I830(dev))
+ return -1;
+
+ pfit_control = I915_READ(PFIT_CONTROL);
+
+ /* See if the panel fitter is in use */
+ if ((pfit_control & PFIT_ENABLE) == 0)
+ return -1;
+
+ /* 965 can place panel fitter on either pipe */
+ if (IS_I965G(dev))
+ return (pfit_control >> 29) & 0x3;
+
+ /* older chips can only use pipe 1 */
+ return 1;
+}
+
+static void intel_crtc_mode_set(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode,
+ int x, int y)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ int fp_reg = (pipe == 0) ? FPA0 : FPB0;
+ int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
+ int dpll_md_reg = (intel_crtc->pipe == 0) ? DPLL_A_MD : DPLL_B_MD;
+ int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
+ int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
+ int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B;
+ int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B;
+ int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B;
+ int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B;
+ int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B;
+ int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B;
+ int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE;
+ int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS;
+ int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC;
+ int refclk;
+ intel_clock_t clock;
+ u32 dpll = 0, fp = 0, dspcntr, pipeconf;
+ bool ok, is_sdvo = false, is_dvo = false;
+ bool is_crt = false, is_lvds = false, is_tv = false;
+ struct drm_mode_config *mode_config = &dev->mode_config;
+ struct drm_connector *connector;
+
+ list_for_each_entry(connector, &mode_config->connector_list, head) {
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ if (!connector->encoder || connector->encoder->crtc != crtc)
+ continue;
+
+ switch (intel_output->type) {
+ case INTEL_OUTPUT_LVDS:
+ is_lvds = TRUE;
+ break;
+ case INTEL_OUTPUT_SDVO:
+ is_sdvo = TRUE;
+ break;
+ case INTEL_OUTPUT_DVO:
+ is_dvo = TRUE;
+ break;
+ case INTEL_OUTPUT_TVOUT:
+ is_tv = TRUE;
+ break;
+ case INTEL_OUTPUT_ANALOG:
+ is_crt = TRUE;
+ break;
+ }
+ }
+
+ if (IS_I9XX(dev)) {
+ refclk = 96000;
+ } else {
+ refclk = 48000;
+ }
+
+ ok = intel_find_best_PLL(crtc, adjusted_mode->clock, refclk, &clock);
+ if (!ok) {
+ DRM_ERROR("Couldn't find PLL settings for mode!\n");
+ return;
+ }
+
+ fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
+
+ dpll = DPLL_VGA_MODE_DIS;
+ if (IS_I9XX(dev)) {
+ if (is_lvds)
+ dpll |= DPLLB_MODE_LVDS;
+ else
+ dpll |= DPLLB_MODE_DAC_SERIAL;
+ if (is_sdvo) {
+ dpll |= DPLL_DVO_HIGH_SPEED;
+ if (IS_I945G(dev) || IS_I945GM(dev)) {
+ int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
+ dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
+ }
+ }
+
+ /* compute bitmask from p1 value */
+ dpll |= (1 << (clock.p1 - 1)) << 16;
+ switch (clock.p2) {
+ case 5:
+ dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5;
+ break;
+ case 7:
+ dpll |= DPLLB_LVDS_P2_CLOCK_DIV_7;
+ break;
+ case 10:
+ dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_10;
+ break;
+ case 14:
+ dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14;
+ break;
+ }
+ if (IS_I965G(dev))
+ dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT);
+ } else {
+ if (is_lvds) {
+ dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT;
+ } else {
+ if (clock.p1 == 2)
+ dpll |= PLL_P1_DIVIDE_BY_TWO;
+ else
+ dpll |= (clock.p1 - 2) << DPLL_FPA01_P1_POST_DIV_SHIFT;
+ if (clock.p2 == 4)
+ dpll |= PLL_P2_DIVIDE_BY_4;
+ }
+ }
+
+ if (is_tv) {
+ /* XXX: just matching BIOS for now */
+/* dpll |= PLL_REF_INPUT_TVCLKINBC; */
+ dpll |= 3;
+ }
+#if 0
+ else if (is_lvds)
+ dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN;
+#endif
+ else
+ dpll |= PLL_REF_INPUT_DREFCLK;
+
+ /* setup pipeconf */
+ pipeconf = I915_READ(pipeconf_reg);
+
+ /* Set up the display plane register */
+ dspcntr = DISPPLANE_GAMMA_ENABLE;
+
+ if (pipe == 0)
+ dspcntr |= DISPPLANE_SEL_PIPE_A;
+ else
+ dspcntr |= DISPPLANE_SEL_PIPE_B;
+
+ if (pipe == 0 && !IS_I965G(dev)) {
+ /* Enable pixel doubling when the dot clock is > 90% of the (display)
+ * core speed.
+ *
+ * XXX: No double-wide on 915GM pipe B. Is that the only reason for the
+ * pipe == 0 check?
+ */
+ if (mode->clock > intel_get_core_clock_speed(dev) * 9 / 10)
+ pipeconf |= PIPEACONF_DOUBLE_WIDE;
+ else
+ pipeconf &= ~PIPEACONF_DOUBLE_WIDE;
+ }
+
+ dspcntr |= DISPLAY_PLANE_ENABLE;
+ pipeconf |= PIPEACONF_ENABLE;
+ dpll |= DPLL_VCO_ENABLE;
+
+
+ /* Disable the panel fitter if it was on our pipe */
+ if (intel_panel_fitter_pipe(dev) == pipe)
+ I915_WRITE(PFIT_CONTROL, 0);
+
+ DRM_DEBUG("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
+ drm_mode_debug_printmodeline(mode);
+
+#if 0
+ if (!xf86ModesEqual(mode, adjusted_mode)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Adjusted mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
+ xf86PrintModeline(pScrn->scrnIndex, mode);
+ }
+ i830PrintPll("chosen", &clock);
+#endif
+
+ if (dpll & DPLL_VCO_ENABLE) {
+ I915_WRITE(fp_reg, fp);
+ I915_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE);
+ I915_READ(dpll_reg);
+ udelay(150);
+ }
+
+ /* The LVDS pin pair needs to be on before the DPLLs are enabled.
+ * This is an exception to the general rule that mode_set doesn't turn
+ * things on.
+ */
+ if (is_lvds) {
+ u32 lvds = I915_READ(LVDS);
+
+ lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP | LVDS_PIPEB_SELECT;
+ /* Set the B0-B3 data pairs corresponding to whether we're going to
+ * set the DPLLs for dual-channel mode or not.
+ */
+ if (clock.p2 == 7)
+ lvds |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP;
+ else
+ lvds &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP);
+
+ /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP)
+ * appropriately here, but we need to look more thoroughly into how
+ * panels behave in the two modes.
+ */
+
+ I915_WRITE(LVDS, lvds);
+ I915_READ(LVDS);
+ }
+
+ I915_WRITE(fp_reg, fp);
+ I915_WRITE(dpll_reg, dpll);
+ I915_READ(dpll_reg);
+ /* Wait for the clocks to stabilize. */
+ udelay(150);
+
+ if (IS_I965G(dev)) {
+ int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
+ I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) |
+ ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT));
+ } else {
+ /* write it again -- the BIOS does, after all */
+ I915_WRITE(dpll_reg, dpll);
+ }
+ I915_READ(dpll_reg);
+ /* Wait for the clocks to stabilize. */
+ udelay(150);
+
+ I915_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) |
+ ((adjusted_mode->crtc_htotal - 1) << 16));
+ I915_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) |
+ ((adjusted_mode->crtc_hblank_end - 1) << 16));
+ I915_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) |
+ ((adjusted_mode->crtc_hsync_end - 1) << 16));
+ I915_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) |
+ ((adjusted_mode->crtc_vtotal - 1) << 16));
+ I915_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) |
+ ((adjusted_mode->crtc_vblank_end - 1) << 16));
+ I915_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) |
+ ((adjusted_mode->crtc_vsync_end - 1) << 16));
+ /* pipesrc and dspsize control the size that is scaled from, which should
+ * always be the user's requested size.
+ */
+ I915_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1));
+ I915_WRITE(dsppos_reg, 0);
+ I915_WRITE(pipesrc_reg, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
+ I915_WRITE(pipeconf_reg, pipeconf);
+ I915_READ(pipeconf_reg);
+
+ intel_wait_for_vblank(dev);
+
+ I915_WRITE(dspcntr_reg, dspcntr);
+
+ /* Flush the plane changes */
+ intel_pipe_set_base(crtc, x, y);
+
+ intel_set_vblank(dev);
+
+ intel_wait_for_vblank(dev);
+}
+
+/** Loads the palette/gamma unit for the CRTC with the prepared values */
+void intel_crtc_load_lut(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int palreg = (intel_crtc->pipe == 0) ? PALETTE_A : PALETTE_B;
+ int i;
+
+ /* The clocks have to be on to load the palette. */
+ if (!crtc->enabled)
+ return;
+
+ for (i = 0; i < 256; i++) {
+ I915_WRITE(palreg + 4 * i,
+ (intel_crtc->lut_r[i] << 16) |
+ (intel_crtc->lut_g[i] << 8) |
+ intel_crtc->lut_b[i]);
+ }
+}
+
+static int intel_crtc_cursor_set(struct drm_crtc *crtc,
+ uint32_t handle,
+ uint32_t width, uint32_t height)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct drm_buffer_object *bo;
+ int pipe = intel_crtc->pipe;
+ uint32_t control = (pipe == 0) ? CURACNTR : CURBCNTR;
+ uint32_t base = (pipe == 0) ? CURABASE : CURBBASE;
+ uint32_t temp;
+ int ret;
+ size_t addr;
+
+ DRM_DEBUG("\n");
+
+ /* if we want to turn of the cursor ignore width and height */
+ if (!handle) {
+ DRM_DEBUG("cursor off\n");
+ /* turn of the cursor */
+ temp = 0;
+ temp |= CURSOR_MODE_DISABLE;
+
+ I915_WRITE(control, temp);
+ I915_WRITE(base, 0);
+ return 0;
+ }
+
+ /* Currently we only support 64x64 cursors */
+ if (width != 64 || height != 64) {
+ DRM_ERROR("we currently only support 64x64 cursors\n");
+ return -EINVAL;
+ }
+
+ ret = drm_get_buffer_object(dev, &bo, handle);
+ if (ret) {
+ return -EINVAL;
+ }
+
+ if ((bo->mem.flags & DRM_BO_MASK_MEM) != DRM_BO_FLAG_MEM_VRAM) {
+ DRM_ERROR("buffer needs to be in VRAM\n");
+ return -ENOMEM;
+ }
+
+ if (bo->mem.size < width * height * 4) {
+ DRM_ERROR("buffer is to small\n");
+ return -ENOMEM;
+ }
+
+ if (dev_priv->cursor_needs_physical)
+ addr = dev_priv->stolen_base + bo->offset;
+ else
+ addr = bo->offset;
+
+ intel_crtc->cursor_addr = addr;
+ temp = 0;
+ /* set the pipe for the cursor */
+ temp |= (pipe << 28);
+ temp |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE;
+
+ I915_WRITE(control, temp);
+ I915_WRITE(base, addr);
+
+ return 0;
+}
+
+static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ uint32_t temp = 0;
+ uint32_t adder;
+
+ if (x < 0) {
+ temp |= (CURSOR_POS_SIGN << CURSOR_X_SHIFT);
+ x = -x;
+ }
+ if (y < 0) {
+ temp |= (CURSOR_POS_SIGN << CURSOR_Y_SHIFT);
+ y = -y;
+ }
+
+ temp |= ((x & CURSOR_POS_MASK) << CURSOR_X_SHIFT);
+ temp |= ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT);
+
+ adder = intel_crtc->cursor_addr;
+ I915_WRITE((pipe == 0) ? CURAPOS : CURBPOS, temp);
+ I915_WRITE((pipe == 0) ? CURABASE : CURBBASE, adder);
+
+ return 0;
+}
+
+/** Sets the color ramps on behalf of RandR */
+void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
+ u16 blue, int regno)
+{
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+ intel_crtc->lut_r[regno] = red >> 8;
+ intel_crtc->lut_g[regno] = green >> 8;
+ intel_crtc->lut_b[regno] = blue >> 8;
+}
+
+static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
+ u16 *blue, uint32_t size)
+{
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int i;
+
+ if (size != 256)
+ return;
+
+ for (i = 0; i < 256; i++) {
+ intel_crtc->lut_r[i] = red[i] >> 8;
+ intel_crtc->lut_g[i] = green[i] >> 8;
+ intel_crtc->lut_b[i] = blue[i] >> 8;
+ }
+
+ intel_crtc_load_lut(crtc);
+}
+
+/**
+ * Get a pipe with a simple mode set on it for doing load-based monitor
+ * detection.
+ *
+ * It will be up to the load-detect code to adjust the pipe as appropriate for
+ * its requirements. The pipe will be connected to no other outputs.
+ *
+ * Currently this code will only succeed if there is a pipe with no outputs
+ * configured for it. In the future, it could choose to temporarily disable
+ * some outputs to free up a pipe for its use.
+ *
+ * \return crtc, or NULL if no pipes are available.
+ */
+
+/* VESA 640x480x72Hz mode to set on the pipe */
+static struct drm_display_mode load_detect_mode = {
+ DRM_MODE("640x480", DRM_MODE_TYPE_DEFAULT, 31500, 640, 664,
+ 704, 832, 0, 480, 489, 491, 520, 0, V_NHSYNC | V_NVSYNC),
+};
+
+struct drm_crtc *intel_get_load_detect_pipe(struct intel_output *intel_output,
+ struct drm_display_mode *mode,
+ int *dpms_mode)
+{
+ struct intel_crtc *intel_crtc;
+ struct drm_crtc *possible_crtc;
+ struct drm_crtc *supported_crtc =NULL;
+ struct drm_encoder *encoder = &intel_output->enc;
+ struct drm_crtc *crtc = NULL;
+ struct drm_device *dev = encoder->dev;
+ struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
+ struct drm_crtc_helper_funcs *crtc_funcs;
+ int i = -1;
+
+ /*
+ * Algorithm gets a little messy:
+ * - if the connector already has an assigned crtc, use it (but make
+ * sure it's on first)
+ * - try to find the first unused crtc that can drive this connector,
+ * and use that if we find one
+ * - if there are no unused crtcs available, try to use the first
+ * one we found that supports the connector
+ */
+
+ /* See if we already have a CRTC for this connector */
+ if (encoder->crtc) {
+ crtc = encoder->crtc;
+ /* Make sure the crtc and connector are running */
+ intel_crtc = to_intel_crtc(crtc);
+ *dpms_mode = intel_crtc->dpms_mode;
+ if (intel_crtc->dpms_mode != DPMSModeOn) {
+ crtc_funcs = crtc->helper_private;
+ crtc_funcs->dpms(crtc, DPMSModeOn);
+ encoder_funcs->dpms(encoder, DPMSModeOn);
+ }
+ return crtc;
+ }
+
+ /* Find an unused one (if possible) */
+ list_for_each_entry(possible_crtc, &dev->mode_config.crtc_list, head) {
+ i++;
+ if (!(encoder->possible_crtcs & (1 << i)))
+ continue;
+ if (!possible_crtc->enabled) {
+ crtc = possible_crtc;
+ break;
+ }
+ if (!supported_crtc)
+ supported_crtc = possible_crtc;
+ }
+
+ /*
+ * If we didn't find an unused CRTC, don't use any.
+ */
+ if (!crtc) {
+ return NULL;
+ }
+
+ encoder->crtc = crtc;
+ intel_output->load_detect_temp = TRUE;
+
+ intel_crtc = to_intel_crtc(crtc);
+ *dpms_mode = intel_crtc->dpms_mode;
+
+ if (!crtc->enabled) {
+ if (!mode)
+ mode = &load_detect_mode;
+ drm_crtc_helper_set_mode(crtc, mode, 0, 0);
+ } else {
+ if (intel_crtc->dpms_mode != DPMSModeOn) {
+ crtc_funcs = crtc->helper_private;
+ crtc_funcs->dpms(crtc, DPMSModeOn);
+ }
+
+ /* Add this connector to the crtc */
+ encoder_funcs->mode_set(encoder, &crtc->mode, &crtc->mode);
+ encoder_funcs->commit(encoder);
+ }
+ /* let the connector get through one full cycle before testing */
+ intel_wait_for_vblank(dev);
+
+ return crtc;
+}
+
+void intel_release_load_detect_pipe(struct intel_output *intel_output, int dpms_mode)
+{
+ struct drm_encoder *encoder = &intel_output->enc;
+ struct drm_device *dev = encoder->dev;
+ struct drm_crtc *crtc = encoder->crtc;
+ struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+
+ if (intel_output->load_detect_temp) {
+ encoder->crtc = NULL;
+ intel_output->load_detect_temp = FALSE;
+ crtc->enabled = drm_helper_crtc_in_use(crtc);
+ drm_helper_disable_unused_functions(dev);
+ }
+
+ /* Switch crtc and output back off if necessary */
+ if (crtc->enabled && dpms_mode != DPMSModeOn) {
+ if (encoder->crtc == crtc)
+ encoder_funcs->dpms(encoder, dpms_mode);
+ crtc_funcs->dpms(crtc, dpms_mode);
+ }
+}
+
+/* Returns the clock of the currently programmed mode of the given pipe. */
+static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ u32 dpll = I915_READ((pipe == 0) ? DPLL_A : DPLL_B);
+ u32 fp;
+ intel_clock_t clock;
+
+ if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0)
+ fp = I915_READ((pipe == 0) ? FPA0 : FPB0);
+ else
+ fp = I915_READ((pipe == 0) ? FPA1 : FPB1);
+
+ clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT;
+ clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT;
+ clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT;
+ if (IS_I9XX(dev)) {
+ clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK) >>
+ DPLL_FPA01_P1_POST_DIV_SHIFT);
+
+ switch (dpll & DPLL_MODE_MASK) {
+ case DPLLB_MODE_DAC_SERIAL:
+ clock.p2 = dpll & DPLL_DAC_SERIAL_P2_CLOCK_DIV_5 ?
+ 5 : 10;
+ break;
+ case DPLLB_MODE_LVDS:
+ clock.p2 = dpll & DPLLB_LVDS_P2_CLOCK_DIV_7 ?
+ 7 : 14;
+ break;
+ default:
+ DRM_DEBUG("Unknown DPLL mode %08x in programmed "
+ "mode\n", (int)(dpll & DPLL_MODE_MASK));
+ return 0;
+ }
+
+ /* XXX: Handle the 100Mhz refclk */
+ i9xx_clock(96000, &clock);
+ } else {
+ bool is_lvds = (pipe == 1) && (I915_READ(LVDS) & LVDS_PORT_EN);
+
+ if (is_lvds) {
+ clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS) >>
+ DPLL_FPA01_P1_POST_DIV_SHIFT);
+ clock.p2 = 14;
+
+ if ((dpll & PLL_REF_INPUT_MASK) ==
+ PLLB_REF_INPUT_SPREADSPECTRUMIN) {
+ /* XXX: might not be 66MHz */
+ i8xx_clock(66000, &clock);
+ } else
+ i8xx_clock(48000, &clock);
+ } else {
+ if (dpll & PLL_P1_DIVIDE_BY_TWO)
+ clock.p1 = 2;
+ else {
+ clock.p1 = ((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830) >>
+ DPLL_FPA01_P1_POST_DIV_SHIFT) + 2;
+ }
+ if (dpll & PLL_P2_DIVIDE_BY_4)
+ clock.p2 = 4;
+ else
+ clock.p2 = 2;
+
+ i8xx_clock(48000, &clock);
+ }
+ }
+
+ /* XXX: It would be nice to validate the clocks, but we can't reuse
+ * i830PllIsValid() because it relies on the xf86_config connector
+ * configuration being accurate, which it isn't necessarily.
+ */
+
+ return clock.dot;
+}
+
+/** Returns the currently programmed mode of the given pipe. */
+struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev,
+ struct drm_crtc *crtc)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ struct drm_display_mode *mode;
+ int htot = I915_READ((pipe == 0) ? HTOTAL_A : HTOTAL_B);
+ int hsync = I915_READ((pipe == 0) ? HSYNC_A : HSYNC_B);
+ int vtot = I915_READ((pipe == 0) ? VTOTAL_A : VTOTAL_B);
+ int vsync = I915_READ((pipe == 0) ? VSYNC_A : VSYNC_B);
+
+ mode = kzalloc(sizeof(*mode), GFP_KERNEL);
+ if (!mode)
+ return NULL;
+
+ mode->clock = intel_crtc_clock_get(dev, crtc);
+ mode->hdisplay = (htot & 0xffff) + 1;
+ mode->htotal = ((htot & 0xffff0000) >> 16) + 1;
+ mode->hsync_start = (hsync & 0xffff) + 1;
+ mode->hsync_end = ((hsync & 0xffff0000) >> 16) + 1;
+ mode->vdisplay = (vtot & 0xffff) + 1;
+ mode->vtotal = ((vtot & 0xffff0000) >> 16) + 1;
+ mode->vsync_start = (vsync & 0xffff) + 1;
+ mode->vsync_end = ((vsync & 0xffff0000) >> 16) + 1;
+
+ drm_mode_set_name(mode);
+ drm_mode_set_crtcinfo(mode, 0);
+
+ return mode;
+}
+
+static void intel_crtc_destroy(struct drm_crtc *crtc)
+{
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+ drm_crtc_cleanup(crtc);
+ kfree(intel_crtc);
+}
+
+static const struct drm_crtc_helper_funcs intel_helper_funcs = {
+ .dpms = intel_crtc_dpms,
+ .mode_fixup = intel_crtc_mode_fixup,
+ .mode_set = intel_crtc_mode_set,
+ .mode_set_base = intel_pipe_set_base,
+ .prepare = intel_crtc_prepare,
+ .commit = intel_crtc_commit,
+};
+
+static const struct drm_crtc_funcs intel_crtc_funcs = {
+ .cursor_set = intel_crtc_cursor_set,
+ .cursor_move = intel_crtc_cursor_move,
+ .gamma_set = intel_crtc_gamma_set,
+ .set_config = drm_crtc_helper_set_config,
+ .destroy = intel_crtc_destroy,
+};
+
+
+void intel_crtc_init(struct drm_device *dev, int pipe)
+{
+ struct intel_crtc *intel_crtc;
+ int i;
+
+ intel_crtc = kzalloc(sizeof(struct intel_crtc) + (INTELFB_CONN_LIMIT * sizeof(struct drm_connector *)), GFP_KERNEL);
+ if (intel_crtc == NULL)
+ return;
+
+ drm_crtc_init(dev, &intel_crtc->base, &intel_crtc_funcs);
+
+ drm_mode_crtc_set_gamma_size(&intel_crtc->base, 256);
+ intel_crtc->pipe = pipe;
+ for (i = 0; i < 256; i++) {
+ intel_crtc->lut_r[i] = i;
+ intel_crtc->lut_g[i] = i;
+ intel_crtc->lut_b[i] = i;
+ }
+
+ intel_crtc->cursor_addr = 0;
+ intel_crtc->dpms_mode = DPMSModeOff;
+ drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs);
+
+ intel_crtc->mode_set.crtc = &intel_crtc->base;
+ intel_crtc->mode_set.connectors = (struct drm_connector **)(intel_crtc + 1);
+ intel_crtc->mode_set.num_connectors = 0;
+
+ if (i915_fbpercrtc) {
+
+
+
+ }
+}
+
+struct drm_crtc *intel_get_crtc_from_pipe(struct drm_device *dev, int pipe)
+{
+ struct drm_crtc *crtc = NULL;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ if (intel_crtc->pipe == pipe)
+ break;
+ }
+ return crtc;
+}
+
+int intel_connector_clones(struct drm_device *dev, int type_mask)
+{
+ int index_mask = 0;
+ struct drm_connector *connector;
+ int entry = 0;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ struct intel_output *intel_output = to_intel_output(connector);
+ if (type_mask & (1 << intel_output->type))
+ index_mask |= (1 << entry);
+ entry++;
+ }
+ return index_mask;
+}
+
+
+static void intel_setup_outputs(struct drm_device *dev)
+{
+ struct drm_connector *connector;
+
+ intel_crt_init(dev);
+
+ /* Set up integrated LVDS */
+ if (IS_MOBILE(dev) && !IS_I830(dev))
+ intel_lvds_init(dev);
+
+ if (IS_I9XX(dev)) {
+ intel_sdvo_init(dev, SDVOB);
+ intel_sdvo_init(dev, SDVOC);
+ } else
+ intel_dvo_init(dev);
+
+ if (IS_I9XX(dev) && !IS_I915G(dev))
+ intel_tv_init(dev);
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct drm_encoder *encoder = &intel_output->enc;
+ int crtc_mask = 0, clone_mask = 0;
+
+ /* valid crtcs */
+ switch(intel_output->type) {
+ case INTEL_OUTPUT_DVO:
+ case INTEL_OUTPUT_SDVO:
+ crtc_mask = ((1 << 0)|
+ (1 << 1));
+ clone_mask = ((1 << INTEL_OUTPUT_ANALOG) |
+ (1 << INTEL_OUTPUT_DVO) |
+ (1 << INTEL_OUTPUT_SDVO));
+ break;
+ case INTEL_OUTPUT_ANALOG:
+ crtc_mask = ((1 << 0)|
+ (1 << 1));
+ clone_mask = ((1 << INTEL_OUTPUT_ANALOG) |
+ (1 << INTEL_OUTPUT_DVO) |
+ (1 << INTEL_OUTPUT_SDVO));
+ break;
+ case INTEL_OUTPUT_LVDS:
+ crtc_mask = (1 << 1);
+ clone_mask = (1 << INTEL_OUTPUT_LVDS);
+ break;
+ case INTEL_OUTPUT_TVOUT:
+ crtc_mask = ((1 << 0) |
+ (1 << 1));
+ clone_mask = (1 << INTEL_OUTPUT_TVOUT);
+ break;
+ }
+ encoder->possible_crtcs = crtc_mask;
+ encoder->possible_clones = intel_connector_clones(dev, clone_mask);
+ }
+}
+
+static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb)
+{
+ struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
+ struct drm_device *dev = fb->dev;
+ if (fb->fbdev)
+ intelfb_remove(dev, fb);
+
+ drm_framebuffer_cleanup(fb);
+
+ kfree(intel_fb);
+}
+
+static const struct drm_framebuffer_funcs intel_fb_funcs = {
+ .destroy = intel_user_framebuffer_destroy,
+};
+
+struct drm_framebuffer *intel_user_framebuffer_create(struct drm_device *dev,
+ struct drm_file *file_priv,
+ struct drm_mode_fb_cmd *mode_cmd)
+{
+ struct intel_framebuffer *intel_fb;
+
+ intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL);
+ if (!intel_fb)
+ return NULL;
+
+ drm_framebuffer_init(dev, &intel_fb->base, &intel_fb_funcs);
+ drm_helper_mode_fill_fb_struct(&intel_fb->base, mode_cmd);
+
+ if (file_priv) {
+ mutex_lock(&dev->struct_mutex);
+ intel_fb->bo = drm_lookup_buffer_object(file_priv, intel_fb->base.mm_handle, 0);
+ mutex_unlock(&dev->struct_mutex);
+ if (!intel_fb->bo) {
+ kfree(intel_fb);
+ return NULL;
+ }
+ }
+ return &intel_fb->base;
+}
+
+static int intel_insert_new_fb(struct drm_device *dev, struct drm_file *file_priv,
+ struct drm_framebuffer *fb, struct drm_mode_fb_cmd *mode_cmd)
+{
+ struct intel_framebuffer *intel_fb;
+ struct drm_buffer_object *bo;
+ struct drm_crtc *crtc;
+
+ intel_fb = to_intel_framebuffer(fb);
+
+ mutex_lock(&dev->struct_mutex);
+ bo = drm_lookup_buffer_object(file_priv, mode_cmd->handle, 0);
+ mutex_unlock(&dev->struct_mutex);
+
+ if (!bo)
+ return -EINVAL;
+ drm_helper_mode_fill_fb_struct(fb, mode_cmd);
+
+ drm_bo_usage_deref_unlocked(&intel_fb->bo);
+
+ intel_fb->bo = bo;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ if (crtc->fb == fb) {
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+ crtc_funcs->mode_set_base(crtc, crtc->x, crtc->y);
+ }
+ }
+ return 0;
+}
+
+static const struct drm_mode_config_funcs intel_mode_funcs = {
+ .resize_fb = intel_insert_new_fb,
+ .fb_create = intel_user_framebuffer_create,
+ .fb_changed = intelfb_probe,
+};
+
+void intel_modeset_init(struct drm_device *dev)
+{
+ int num_pipe;
+ int i;
+
+ drm_mode_config_init(dev);
+
+ dev->mode_config.min_width = 0;
+ dev->mode_config.min_height = 0;
+
+ dev->mode_config.funcs = (void *)&intel_mode_funcs;
+
+ if (IS_I965G(dev)) {
+ dev->mode_config.max_width = 8192;
+ dev->mode_config.max_height = 8192;
+ } else {
+ dev->mode_config.max_width = 2048;
+ dev->mode_config.max_height = 2048;
+ }
+
+ /* set memory base */
+ if (IS_I9XX(dev))
+ dev->mode_config.fb_base = pci_resource_start(dev->pdev, 2);
+ else
+ dev->mode_config.fb_base = pci_resource_start(dev->pdev, 0);
+
+ if (IS_MOBILE(dev) || IS_I9XX(dev))
+ num_pipe = 2;
+ else
+ num_pipe = 1;
+ DRM_DEBUG("%d display pipe%s available.\n",
+ num_pipe, num_pipe > 1 ? "s" : "");
+
+ for (i = 0; i < num_pipe; i++) {
+ intel_crtc_init(dev, i);
+ }
+
+ intel_setup_outputs(dev);
+
+ /* setup fbs */
+ //drm_initial_config(dev, false);
+}
+
+void intel_modeset_cleanup(struct drm_device *dev)
+{
+ drm_mode_config_cleanup(dev);
+}
+
+
+/* current intel driver doesn't take advantage of encoders
+ always give back the encoder for the connector
+*/
+struct drm_encoder *intel_best_encoder(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ return &intel_output->enc;
+}
diff --git a/linux-core/intel_drv.h b/linux-core/intel_drv.h
new file mode 100644
index 00000000..1008e271
--- /dev/null
+++ b/linux-core/intel_drv.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
+ * Copyright (c) 2007 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ */
+#ifndef __INTEL_DRV_H__
+#define __INTEL_DRV_H__
+
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/i2c-algo-bit.h>
+#include "drm_crtc.h"
+
+#include "drm_crtc_helper.h"
+/*
+ * Display related stuff
+ */
+
+/* store information about an Ixxx DVO */
+/* The i830->i865 use multiple DVOs with multiple i2cs */
+/* the i915, i945 have a single sDVO i2c bus - which is different */
+#define MAX_OUTPUTS 6
+/* maximum connectors per crtcs in the mode set */
+#define INTELFB_CONN_LIMIT 4
+
+#define INTEL_I2C_BUS_DVO 1
+#define INTEL_I2C_BUS_SDVO 2
+
+/* these are outputs from the chip - integrated only
+ external chips are via DVO or SDVO output */
+#define INTEL_OUTPUT_UNUSED 0
+#define INTEL_OUTPUT_ANALOG 1
+#define INTEL_OUTPUT_DVO 2
+#define INTEL_OUTPUT_SDVO 3
+#define INTEL_OUTPUT_LVDS 4
+#define INTEL_OUTPUT_TVOUT 5
+
+#define INTEL_DVO_CHIP_NONE 0
+#define INTEL_DVO_CHIP_LVDS 1
+#define INTEL_DVO_CHIP_TMDS 2
+#define INTEL_DVO_CHIP_TVOUT 4
+
+struct intel_i2c_chan {
+ struct drm_device *drm_dev; /* for getting at dev. private (mmio etc.) */
+ u32 reg; /* GPIO reg */
+ struct i2c_adapter adapter;
+ struct i2c_algo_bit_data algo;
+ u8 slave_addr;
+};
+
+struct intel_framebuffer {
+ struct drm_framebuffer base;
+ struct drm_buffer_object *bo;
+ struct drm_bo_kmap_obj kmap;
+};
+
+
+struct intel_output {
+ struct drm_connector base;
+
+ struct drm_encoder enc;
+ int type;
+ struct intel_i2c_chan *i2c_bus; /* for control functions */
+ struct intel_i2c_chan *ddc_bus; /* for DDC only stuff */
+ bool load_detect_temp;
+ void *dev_priv;
+};
+
+struct intel_crtc {
+ struct drm_crtc base;
+ int pipe;
+ int plane;
+ uint32_t cursor_addr;
+ u8 lut_r[256], lut_g[256], lut_b[256];
+ int dpms_mode;
+ struct intel_framebuffer *fbdev_fb;
+ /* a mode_set for fbdev users on this crtc */
+ struct drm_mode_set mode_set;
+};
+
+#define to_intel_crtc(x) container_of(x, struct intel_crtc, base)
+#define to_intel_output(x) container_of(x, struct intel_output, base)
+#define enc_to_intel_output(x) container_of(x, struct intel_output, enc)
+#define to_intel_framebuffer(x) container_of(x, struct intel_framebuffer, base)
+
+struct intel_i2c_chan *intel_i2c_create(struct drm_device *dev, const u32 reg,
+ const char *name);
+void intel_i2c_destroy(struct intel_i2c_chan *chan);
+int intel_ddc_get_modes(struct intel_output *intel_output);
+extern bool intel_ddc_probe(struct intel_output *intel_output);
+
+extern void intel_crt_init(struct drm_device *dev);
+extern void intel_sdvo_init(struct drm_device *dev, int output_device);
+extern void intel_dvo_init(struct drm_device *dev);
+extern void intel_tv_init(struct drm_device *dev);
+extern void intel_lvds_init(struct drm_device *dev);
+
+extern void intel_crtc_load_lut(struct drm_crtc *crtc);
+extern void intel_encoder_prepare (struct drm_encoder *encoder);
+extern void intel_encoder_commit (struct drm_encoder *encoder);
+
+extern struct drm_encoder *intel_best_encoder(struct drm_connector *connector);
+
+extern struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev,
+ struct drm_crtc *crtc);
+extern void intel_wait_for_vblank(struct drm_device *dev);
+extern struct drm_crtc *intel_get_crtc_from_pipe(struct drm_device *dev, int pipe);
+extern struct drm_crtc *intel_get_load_detect_pipe(struct intel_output *intel_output,
+ struct drm_display_mode *mode,
+ int *dpms_mode);
+extern void intel_release_load_detect_pipe(struct intel_output *intel_output,
+ int dpms_mode);
+
+extern struct drm_connector* intel_sdvo_find(struct drm_device *dev, int sdvoB);
+extern int intel_sdvo_supports_hotplug(struct drm_connector *connector);
+extern void intel_sdvo_set_hotplug(struct drm_connector *connector, int enable);
+extern int intelfb_probe(struct drm_device *dev);
+extern int intelfb_remove(struct drm_device *dev, struct drm_framebuffer *fb);
+extern int intelfb_resize(struct drm_device *dev, struct drm_crtc *crtc);
+extern void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
+ u16 blue, int regno);
+
+extern struct drm_framebuffer *intel_user_framebuffer_create(struct drm_device *dev,
+ struct drm_file *file_priv,
+ struct drm_mode_fb_cmd *mode_cmd);
+#endif /* __INTEL_DRV_H__ */
diff --git a/linux-core/intel_dvo.c b/linux-core/intel_dvo.c
new file mode 100644
index 00000000..e6df8fdd
--- /dev/null
+++ b/linux-core/intel_dvo.c
@@ -0,0 +1,507 @@
+/*
+ * Copyright © 2006-2007 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ */
+/*
+ * Copyright 2006 Dave Airlie <airlied@linux.ie>
+ */
+
+#include <linux/i2c.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+#include "dvo.h"
+
+#define SIL164_ADDR 0x38
+#define CH7xxx_ADDR 0x76
+#define TFP410_ADDR 0x38
+
+extern struct intel_dvo_dev_ops sil164_ops;
+extern struct intel_dvo_dev_ops ch7xxx_ops;
+extern struct intel_dvo_dev_ops ivch_ops;
+extern struct intel_dvo_dev_ops tfp410_ops;
+extern struct intel_dvo_dev_ops ch7017_ops;
+
+struct intel_dvo_device intel_dvo_devices[] = {
+ {
+ .type = INTEL_DVO_CHIP_TMDS,
+ .name = "sil164",
+ .dvo_reg = DVOC,
+ .slave_addr = SIL164_ADDR,
+ .dev_ops = &sil164_ops,
+ },
+ {
+ .type = INTEL_DVO_CHIP_TMDS,
+ .name = "ch7xxx",
+ .dvo_reg = DVOC,
+ .slave_addr = CH7xxx_ADDR,
+ .dev_ops = &ch7xxx_ops,
+ },
+ {
+ .type = INTEL_DVO_CHIP_LVDS,
+ .name = "ivch",
+ .dvo_reg = DVOA,
+ .slave_addr = 0x02, /* Might also be 0x44, 0x84, 0xc4 */
+ .dev_ops = &ivch_ops,
+ },
+ {
+ .type = INTEL_DVO_CHIP_TMDS,
+ .name = "tfp410",
+ .dvo_reg = DVOC,
+ .slave_addr = TFP410_ADDR,
+ .dev_ops = &tfp410_ops,
+ },
+ {
+ .type = INTEL_DVO_CHIP_LVDS,
+ .name = "ch7017",
+ .dvo_reg = DVOC,
+ .slave_addr = 0x75,
+ .gpio = GPIOE,
+ .dev_ops = &ch7017_ops,
+ }
+};
+
+static void intel_dvo_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_i915_private *dev_priv = encoder->dev->dev_private;
+ struct intel_output *intel_output = enc_to_intel_output(encoder);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+ u32 dvo_reg = dvo->dvo_reg;
+ u32 temp = I915_READ(dvo_reg);
+
+ if (mode == DPMSModeOn) {
+ I915_WRITE(dvo_reg, temp | DVO_ENABLE);
+ I915_READ(dvo_reg);
+ dvo->dev_ops->dpms(dvo, mode);
+ } else {
+ dvo->dev_ops->dpms(dvo, mode);
+ I915_WRITE(dvo_reg, temp & ~DVO_ENABLE);
+ I915_READ(dvo_reg);
+ }
+}
+
+static void intel_dvo_save(struct drm_connector *connector)
+{
+ struct drm_i915_private *dev_priv = connector->dev->dev_private;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+ /* Each output should probably just save the registers it touches,
+ * but for now, use more overkill.
+ */
+ dev_priv->saveDVOA = I915_READ(DVOA);
+ dev_priv->saveDVOB = I915_READ(DVOB);
+ dev_priv->saveDVOC = I915_READ(DVOC);
+
+ dvo->dev_ops->save(dvo);
+}
+
+static void intel_dvo_restore(struct drm_connector *connector)
+{
+ struct drm_i915_private *dev_priv = connector->dev->dev_private;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+ dvo->dev_ops->restore(dvo);
+
+ I915_WRITE(DVOA, dev_priv->saveDVOA);
+ I915_WRITE(DVOB, dev_priv->saveDVOB);
+ I915_WRITE(DVOC, dev_priv->saveDVOC);
+}
+
+static int intel_dvo_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+ if (mode->flags & V_DBLSCAN)
+ return MODE_NO_DBLESCAN;
+
+ /* XXX: Validate clock range */
+
+ if (dvo->panel_fixed_mode) {
+ if (mode->hdisplay > dvo->panel_fixed_mode->hdisplay)
+ return MODE_PANEL;
+ if (mode->vdisplay > dvo->panel_fixed_mode->vdisplay)
+ return MODE_PANEL;
+ }
+
+ return dvo->dev_ops->mode_valid(dvo, mode);
+}
+
+static bool intel_dvo_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct intel_output *intel_output = enc_to_intel_output(encoder);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+ /* If we have timings from the BIOS for the panel, put them in
+ * to the adjusted mode. The CRTC will be set up for this mode,
+ * with the panel scaling set up to source from the H/VDisplay
+ * of the original mode.
+ */
+ if (dvo->panel_fixed_mode != NULL) {
+#define C(x) adjusted_mode->x = dvo->panel_fixed_mode->x
+ C(hdisplay);
+ C(hsync_start);
+ C(hsync_end);
+ C(htotal);
+ C(vdisplay);
+ C(vsync_start);
+ C(vsync_end);
+ C(vtotal);
+ C(clock);
+ drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);
+#undef C
+ }
+
+ if (dvo->dev_ops->mode_fixup)
+ return dvo->dev_ops->mode_fixup(dvo, mode, adjusted_mode);
+
+ return true;
+}
+
+static void intel_dvo_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+ struct intel_output *intel_output = enc_to_intel_output(encoder);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+ int pipe = intel_crtc->pipe;
+ u32 dvo_val;
+ u32 dvo_reg = dvo->dvo_reg, dvo_srcdim_reg;
+ int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
+
+ switch (dvo_reg) {
+ case DVOA:
+ default:
+ dvo_srcdim_reg = DVOA_SRCDIM;
+ break;
+ case DVOB:
+ dvo_srcdim_reg = DVOB_SRCDIM;
+ break;
+ case DVOC:
+ dvo_srcdim_reg = DVOC_SRCDIM;
+ break;
+ }
+
+ dvo->dev_ops->mode_set(dvo, mode, adjusted_mode);
+
+ /* Save the data order, since I don't know what it should be set to. */
+ dvo_val = I915_READ(dvo_reg) &
+ (DVO_PRESERVE_MASK | DVO_DATA_ORDER_GBRG);
+ dvo_val |= DVO_DATA_ORDER_FP | DVO_BORDER_ENABLE |
+ DVO_BLANK_ACTIVE_HIGH;
+
+ if (pipe == 1)
+ dvo_val |= DVO_PIPE_B_SELECT;
+ dvo_val |= DVO_PIPE_STALL;
+ if (adjusted_mode->flags & V_PHSYNC)
+ dvo_val |= DVO_HSYNC_ACTIVE_HIGH;
+ if (adjusted_mode->flags & V_PVSYNC)
+ dvo_val |= DVO_VSYNC_ACTIVE_HIGH;
+
+ I915_WRITE(dpll_reg, I915_READ(dpll_reg) | DPLL_DVO_HIGH_SPEED);
+
+ /*I915_WRITE(DVOB_SRCDIM,
+ (adjusted_mode->hdisplay << DVO_SRCDIM_HORIZONTAL_SHIFT) |
+ (adjusted_mode->VDisplay << DVO_SRCDIM_VERTICAL_SHIFT));*/
+ I915_WRITE(dvo_srcdim_reg,
+ (adjusted_mode->hdisplay << DVO_SRCDIM_HORIZONTAL_SHIFT) |
+ (adjusted_mode->vdisplay << DVO_SRCDIM_VERTICAL_SHIFT));
+ /*I915_WRITE(DVOB, dvo_val);*/
+ I915_WRITE(dvo_reg, dvo_val);
+}
+
+/**
+ * Detect the output connection on our DVO device.
+ *
+ * Unimplemented.
+ */
+static enum drm_connector_status intel_dvo_detect(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+ return dvo->dev_ops->detect(dvo);
+}
+
+static int intel_dvo_get_modes(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+ /* We should probably have an i2c driver get_modes function for those
+ * devices which will have a fixed set of modes determined by the chip
+ * (TV-out, for example), but for now with just TMDS and LVDS,
+ * that's not the case.
+ */
+ intel_ddc_get_modes(intel_output);
+ if (!list_empty(&connector->probed_modes))
+ return 1;
+
+#if 0
+ if (intel_output->i2c_drv->vid_rec->get_modes)
+ {
+ modes = intel_output->i2c_drv->vid_rec->get_modes (intel_output->i2c_drv->dev_priv);
+ if (modes != NULL)
+ return modes;
+ }
+#endif
+
+ if (dvo->panel_fixed_mode != NULL) {
+ struct drm_display_mode *mode;
+ mode = drm_mode_duplicate(connector->dev, dvo->panel_fixed_mode);
+ if (mode) {
+ drm_mode_probed_add(connector, mode);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void intel_dvo_destroy (struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+ if (dvo) {
+ if (dvo->dev_ops->destroy)
+ dvo->dev_ops->destroy(dvo);
+ if (dvo->panel_fixed_mode)
+ kfree(dvo->panel_fixed_mode);
+ /* no need, in i830_dvoices[] now */
+ //kfree(dvo);
+ }
+ if (intel_output->i2c_bus)
+ intel_i2c_destroy(intel_output->i2c_bus);
+ if (intel_output->ddc_bus)
+ intel_i2c_destroy(intel_output->ddc_bus);
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
+ kfree(intel_output);
+}
+
+#ifdef RANDR_GET_CRTC_INTERFACE
+static struct drm_crtc *intel_dvo_get_crtc(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+ int pipe = !!(I915_READ(dvo->dvo_reg) & SDVO_PIPE_B_SELECT);
+
+ return intel_pipe_to_crtc(pScrn, pipe);
+}
+#endif
+
+static const struct drm_encoder_helper_funcs intel_dvo_helper_funcs = {
+ .dpms = intel_dvo_dpms,
+ .mode_fixup = intel_dvo_mode_fixup,
+ .prepare = intel_encoder_prepare,
+ .mode_set = intel_dvo_mode_set,
+ .commit = intel_encoder_commit,
+};
+
+static const struct drm_connector_funcs intel_dvo_connector_funcs = {
+ .save = intel_dvo_save,
+ .restore = intel_dvo_restore,
+ .detect = intel_dvo_detect,
+ .destroy = intel_dvo_destroy,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+};
+
+static const struct drm_connector_helper_funcs intel_dvo_connector_helper_funcs = {
+ .mode_valid = intel_dvo_mode_valid,
+ .get_modes = intel_dvo_get_modes,
+ .best_encoder = intel_best_encoder,
+};
+
+void intel_dvo_enc_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs intel_dvo_enc_funcs = {
+ .destroy = intel_dvo_enc_destroy,
+};
+
+
+/**
+ * Attempts to get a fixed panel timing for LVDS (currently only the i830).
+ *
+ * Other chips with DVO LVDS will need to extend this to deal with the LVDS
+ * chip being on DVOB/C and having multiple pipes.
+ */
+static struct drm_display_mode *
+intel_dvo_get_current_mode (struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+ uint32_t dvo_reg = dvo->dvo_reg;
+ uint32_t dvo_val = I915_READ(dvo_reg);
+ struct drm_display_mode *mode = NULL;
+
+ /* If the DVO port is active, that'll be the LVDS, so we can pull out
+ * its timings to get how the BIOS set up the panel.
+ */
+ if (dvo_val & DVO_ENABLE) {
+ struct drm_crtc *crtc;
+ int pipe = (dvo_val & DVO_PIPE_B_SELECT) ? 1 : 0;
+
+ crtc = intel_get_crtc_from_pipe(dev, pipe);
+ if (crtc) {
+ mode = intel_crtc_mode_get(dev, crtc);
+
+ if (mode) {
+ mode->type |= DRM_MODE_TYPE_PREFERRED;
+ if (dvo_val & DVO_HSYNC_ACTIVE_HIGH)
+ mode->flags |= V_PHSYNC;
+ if (dvo_val & DVO_VSYNC_ACTIVE_HIGH)
+ mode->flags |= V_PVSYNC;
+ }
+ }
+ }
+ return mode;
+}
+
+void intel_dvo_init(struct drm_device *dev)
+{
+ struct intel_output *intel_output;
+ struct intel_dvo_device *dvo;
+ struct intel_i2c_chan *i2cbus = NULL;
+ int ret = 0;
+ int i;
+ int gpio_inited = 0;
+ int encoder_type = DRM_MODE_ENCODER_NONE;
+ intel_output = kzalloc (sizeof(struct intel_output), GFP_KERNEL);
+ if (!intel_output)
+ return;
+
+ /* Set up the DDC bus */
+ intel_output->ddc_bus = intel_i2c_create(dev, GPIOD, "DVODDC_D");
+ if (!intel_output->ddc_bus)
+ goto free_intel;
+
+ /* Now, try to find a controller */
+ for (i = 0; i < ARRAY_SIZE(intel_dvo_devices); i++) {
+ struct drm_connector *connector = &intel_output->base;
+ int gpio;
+
+ dvo = &intel_dvo_devices[i];
+
+ /* Allow the I2C driver info to specify the GPIO to be used in
+ * special cases, but otherwise default to what's defined
+ * in the spec.
+ */
+ if (dvo->gpio != 0)
+ gpio = dvo->gpio;
+ else if (dvo->type == INTEL_DVO_CHIP_LVDS)
+ gpio = GPIOB;
+ else
+ gpio = GPIOE;
+
+ /* Set up the I2C bus necessary for the chip we're probing.
+ * It appears that everything is on GPIOE except for panels
+ * on i830 laptops, which are on GPIOB (DVOA).
+ */
+ if (gpio_inited != gpio) {
+ if (i2cbus != NULL)
+ intel_i2c_destroy(i2cbus);
+ if (!(i2cbus = intel_i2c_create(dev, gpio,
+ gpio == GPIOB ? "DVOI2C_B" : "DVOI2C_E"))) {
+ continue;
+ }
+ gpio_inited = gpio;
+ }
+
+ if (dvo->dev_ops!= NULL)
+ ret = dvo->dev_ops->init(dvo, i2cbus);
+ else
+ ret = false;
+
+ if (!ret)
+ continue;
+
+ intel_output->type = INTEL_OUTPUT_DVO;
+ switch (dvo->type) {
+ case INTEL_DVO_CHIP_TMDS:
+ // connector = DRM_MODE_CONNECTOR_DVID;
+ drm_connector_init(dev, connector, &intel_dvo_connector_funcs,
+ DRM_MODE_CONNECTOR_DVII);
+ encoder_type = DRM_MODE_ENCODER_TMDS;
+ break;
+ case INTEL_DVO_CHIP_LVDS:
+ // connector = DRM_MODE_CONNECTOR_LVDS;
+ drm_connector_init(dev, connector, &intel_dvo_connector_funcs,
+ DRM_MODE_CONNECTOR_LVDS);
+ encoder_type = DRM_MODE_ENCODER_LVDS;
+ break;
+ }
+
+ drm_connector_helper_add(connector, &intel_dvo_connector_helper_funcs);
+ connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+ connector->interlace_allowed = false;
+ connector->doublescan_allowed = false;
+
+ intel_output->dev_priv = dvo;
+ intel_output->i2c_bus = i2cbus;
+
+ drm_encoder_init(dev, &intel_output->enc, &intel_dvo_enc_funcs, encoder_type);
+ drm_encoder_helper_add(&intel_output->enc, &intel_dvo_helper_funcs);
+
+ drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc);
+ if (dvo->type == INTEL_DVO_CHIP_LVDS) {
+ /* For our LVDS chipsets, we should hopefully be able
+ * to dig the fixed panel mode out of the BIOS data.
+ * However, it's in a different format from the BIOS
+ * data on chipsets with integrated LVDS (stored in AIM
+ * headers, likely), so for now, just get the current
+ * mode being output through DVO.
+ */
+ dvo->panel_fixed_mode = intel_dvo_get_current_mode(connector);
+ dvo->panel_wants_dither = true;
+ }
+
+ drm_sysfs_connector_add(connector);
+ return;
+ }
+
+ intel_i2c_destroy(intel_output->ddc_bus);
+ /* Didn't find a chip, so tear down. */
+ if (i2cbus != NULL)
+ intel_i2c_destroy(i2cbus);
+free_intel:
+ kfree(intel_output);
+}
diff --git a/linux-core/intel_fb.c b/linux-core/intel_fb.c
new file mode 100644
index 00000000..856ec868
--- /dev/null
+++ b/linux-core/intel_fb.c
@@ -0,0 +1,1011 @@
+/*
+ * Copyright © 2007 David Airlie
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * David Airlie
+ */
+ /*
+ * Modularization
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+struct intelfb_par {
+ struct drm_device *dev;
+ struct drm_display_mode *our_mode;
+ struct intel_framebuffer *intel_fb;
+ int crtc_count;
+ /* crtc currently bound to this */
+ uint32_t crtc_ids[2];
+};
+/*
+static int
+var_to_refresh(const struct fb_var_screeninfo *var)
+{
+ int xtot = var->xres + var->left_margin + var->right_margin +
+ var->hsync_len;
+ int ytot = var->yres + var->upper_margin + var->lower_margin +
+ var->vsync_len;
+
+ return (1000000000 / var->pixclock * 1000 + 500) / xtot / ytot;
+}*/
+
+static int intelfb_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp,
+ struct fb_info *info)
+{
+ struct intelfb_par *par = info->par;
+ struct drm_device *dev = par->dev;
+ struct drm_crtc *crtc;
+ int i;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct drm_mode_set *modeset = &intel_crtc->mode_set;
+ struct drm_framebuffer *fb = modeset->fb;
+
+ for (i = 0; i < par->crtc_count; i++)
+ if (crtc->base.id == par->crtc_ids[i])
+ break;
+
+ if (i == par->crtc_count)
+ continue;
+
+
+ if (regno > 255)
+ return 1;
+
+ if (fb->depth == 8) {
+ intel_crtc_fb_gamma_set(crtc, red, green, blue, regno);
+ return 0;
+ }
+
+ if (regno < 16) {
+ switch (fb->depth) {
+ case 15:
+ fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) |
+ ((green & 0xf800) >> 6) |
+ ((blue & 0xf800) >> 11);
+ break;
+ case 16:
+ fb->pseudo_palette[regno] = (red & 0xf800) |
+ ((green & 0xfc00) >> 5) |
+ ((blue & 0xf800) >> 11);
+ break;
+ case 24:
+ case 32:
+ fb->pseudo_palette[regno] = ((red & 0xff00) << 8) |
+ (green & 0xff00) |
+ ((blue & 0xff00) >> 8);
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int intelfb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct intelfb_par *par = info->par;
+ struct intel_framebuffer *intel_fb = par->intel_fb;
+ struct drm_framebuffer *fb = &intel_fb->base;
+ int depth;
+
+ if (var->pixclock == -1 || !var->pixclock)
+ return -EINVAL;
+
+ /* Need to resize the fb object !!! */
+ if (var->xres > fb->width || var->yres > fb->height) {
+ DRM_ERROR("Requested width/height is greater than current fb object %dx%d > %dx%d\n",var->xres,var->yres,fb->width,fb->height);
+ DRM_ERROR("Need resizing code.\n");
+ return -EINVAL;
+ }
+
+ switch (var->bits_per_pixel) {
+ case 16:
+ depth = (var->green.length == 6) ? 16 : 15;
+ break;
+ case 32:
+ depth = (var->transp.length > 0) ? 32 : 24;
+ break;
+ default:
+ depth = var->bits_per_pixel;
+ break;
+ }
+
+ switch (depth) {
+ case 8:
+ var->red.offset = 0;
+ var->green.offset = 0;
+ var->blue.offset = 0;
+ var->red.length = 8;
+ var->green.length = 8;
+ var->blue.length = 8;
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ break;
+ case 15:
+ var->red.offset = 10;
+ var->green.offset = 5;
+ var->blue.offset = 0;
+ var->red.length = 5;
+ var->green.length = 5;
+ var->blue.length = 5;
+ var->transp.length = 1;
+ var->transp.offset = 15;
+ break;
+ case 16:
+ var->red.offset = 11;
+ var->green.offset = 5;
+ var->blue.offset = 0;
+ var->red.length = 5;
+ var->green.length = 6;
+ var->blue.length = 5;
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ break;
+ case 24:
+ var->red.offset = 16;
+ var->green.offset = 8;
+ var->blue.offset = 0;
+ var->red.length = 8;
+ var->green.length = 8;
+ var->blue.length = 8;
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ break;
+ case 32:
+ var->red.offset = 16;
+ var->green.offset = 8;
+ var->blue.offset = 0;
+ var->red.length = 8;
+ var->green.length = 8;
+ var->blue.length = 8;
+ var->transp.length = 8;
+ var->transp.offset = 24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* this will let fbcon do the mode init */
+/* FIXME: take mode config lock? */
+static int intelfb_set_par(struct fb_info *info)
+{
+ struct intelfb_par *par = info->par;
+ struct drm_device *dev = par->dev;
+ struct fb_var_screeninfo *var = &info->var;
+ int i;
+
+ DRM_DEBUG("%d %d\n", var->xres, var->pixclock);
+
+ if (var->pixclock != -1) {
+
+ DRM_ERROR("PIXEL CLCOK SET\n");
+#if 0
+ struct intel_framebuffer *intel_fb = par->intel_fb;
+ struct drm_framebuffer *fb = &intel_fb->base;
+ struct drm_display_mode *drm_mode, *search_mode;
+ struct drm_connector *connector = NULL;
+ struct drm_device *dev = par->dev;
+
+ int found = 0;
+
+ switch (var->bits_per_pixel) {
+ case 16:
+ fb->depth = (var->green.length == 6) ? 16 : 15;
+ break;
+ case 32:
+ fb->depth = (var->transp.length > 0) ? 32 : 24;
+ break;
+ default:
+ fb->depth = var->bits_per_pixel;
+ break;
+ }
+
+ fb->bits_per_pixel = var->bits_per_pixel;
+
+ info->fix.line_length = fb->pitch;
+ info->fix.smem_len = info->fix.line_length * fb->height;
+ info->fix.visual = (fb->depth == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
+
+ info->screen_size = info->fix.smem_len; /* ??? */
+ /* reuse desired mode if possible */
+ /* create a drm mode */
+ drm_mode = drm_mode_create(dev);
+ drm_mode->hdisplay = var->xres;
+ drm_mode->hsync_start = drm_mode->hdisplay + var->right_margin;
+ drm_mode->hsync_end = drm_mode->hsync_start + var->hsync_len;
+ drm_mode->htotal = drm_mode->hsync_end + var->left_margin;
+ drm_mode->vdisplay = var->yres;
+ drm_mode->vsync_start = drm_mode->vdisplay + var->lower_margin;
+ drm_mode->vsync_end = drm_mode->vsync_start + var->vsync_len;
+ drm_mode->vtotal = drm_mode->vsync_end + var->upper_margin;
+ drm_mode->clock = PICOS2KHZ(var->pixclock);
+ drm_mode->vrefresh = drm_mode_vrefresh(drm_mode);
+ drm_mode->flags = 0;
+ drm_mode->flags |= var->sync & FB_SYNC_HOR_HIGH_ACT ? V_PHSYNC : V_NHSYNC;
+ drm_mode->flags |= var->sync & FB_SYNC_VERT_HIGH_ACT ? V_PVSYNC : V_NVSYNC;
+
+ drm_mode_set_name(drm_mode);
+ drm_mode_set_crtcinfo(drm_mode, CRTC_INTERLACE_HALVE_V);
+
+ found = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (connector->encoder &&
+ connector->encoder->crtc == par->set.crtc){
+ found = 1;
+ break;
+ }
+ }
+
+ /* no connector bound, bail */
+ if (!found)
+ return -EINVAL;
+
+ found = 0;
+ drm_mode_debug_printmodeline(drm_mode);
+ list_for_each_entry(search_mode, &connector->modes, head) {
+ drm_mode_debug_printmodeline(search_mode);
+ if (drm_mode_equal(drm_mode, search_mode)) {
+ drm_mode_destroy(dev, drm_mode);
+ drm_mode = search_mode;
+ found = 1;
+ break;
+ }
+ }
+
+ /* If we didn't find a matching mode that exists on our connector,
+ * create a new attachment for the incoming user specified mode
+ */
+ if (!found) {
+ if (par->our_mode) {
+ /* this also destroys the mode */
+ drm_mode_detachmode_crtc(dev, par->our_mode);
+ }
+
+ par->set.mode = drm_mode;
+ par->our_mode = drm_mode;
+ drm_mode_debug_printmodeline(drm_mode);
+ /* attach mode */
+ drm_mode_attachmode_crtc(dev, par->set.crtc, par->set.mode);
+ } else {
+ par->set.mode = drm_mode;
+ if (par->our_mode)
+ drm_mode_detachmode_crtc(dev, par->our_mode);
+ par->our_mode = NULL;
+ }
+ return par->set.crtc->funcs->set_config(&par->set);
+#endif
+ return -EINVAL;
+ } else {
+ struct drm_crtc *crtc;
+ int ret;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+ for (i = 0; i < par->crtc_count; i++)
+ if (crtc->base.id == par->crtc_ids[i])
+ break;
+
+ if (i == par->crtc_count)
+ continue;
+
+ if (crtc->fb == intel_crtc->mode_set.fb) {
+ ret = crtc->funcs->set_config(&intel_crtc->mode_set);
+ if (ret)
+ return ret;
+ }
+ }
+ return 0;
+ }
+}
+
+#if 0
+static void intelfb_copyarea(struct fb_info *info,
+ const struct fb_copyarea *region)
+{
+ struct intelfb_par *par = info->par;
+ struct drm_device *dev = par->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 src_x1, src_y1, dst_x1, dst_y1, dst_x2, dst_y2, offset;
+ u32 cmd, rop_depth_pitch, src_pitch;
+ RING_LOCALS;
+
+ cmd = XY_SRC_COPY_BLT_CMD;
+ src_x1 = region->sx;
+ src_y1 = region->sy;
+ dst_x1 = region->dx;
+ dst_y1 = region->dy;
+ dst_x2 = region->dx + region->width;
+ dst_y2 = region->dy + region->height;
+ offset = par->fb->offset;
+ rop_depth_pitch = BLT_ROP_GXCOPY | par->fb->pitch;
+ src_pitch = par->fb->pitch;
+
+ switch (par->fb->bits_per_pixel) {
+ case 16:
+ rop_depth_pitch |= BLT_DEPTH_16_565;
+ break;
+ case 32:
+ rop_depth_pitch |= BLT_DEPTH_32;
+ cmd |= XY_SRC_COPY_BLT_WRITE_ALPHA | XY_SRC_COPY_BLT_WRITE_RGB;
+ break;
+ }
+
+ BEGIN_LP_RING(8);
+ OUT_RING(cmd);
+ OUT_RING(rop_depth_pitch);
+ OUT_RING((dst_y1 << 16) | (dst_x1 & 0xffff));
+ OUT_RING((dst_y2 << 16) | (dst_x2 & 0xffff));
+ OUT_RING(offset);
+ OUT_RING((src_y1 << 16) | (src_x1 & 0xffff));
+ OUT_RING(src_pitch);
+ OUT_RING(offset);
+ ADVANCE_LP_RING();
+}
+
+#define ROUND_UP_TO(x, y) (((x) + (y) - 1) / (y) * (y))
+#define ROUND_DOWN_TO(x, y) ((x) / (y) * (y))
+
+void intelfb_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+ struct intelfb_par *par = info->par;
+ struct drm_device *dev = par->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 cmd, rop_pitch_depth, tmp;
+ int nbytes, ndwords, pad;
+ u32 dst_x1, dst_y1, dst_x2, dst_y2, offset, bg, fg;
+ int dat, ix, iy, iw;
+ int i, j;
+ RING_LOCALS;
+
+ /* size in bytes of a padded scanline */
+ nbytes = ROUND_UP_TO(image->width, 16) / 8;
+
+ /* Total bytes of padded scanline data to write out. */
+ nbytes *= image->height;
+
+ /*
+ * Check if the glyph data exceeds the immediate mode limit.
+ * It would take a large font (1K pixels) to hit this limit.
+ */
+ if (nbytes > 128 || image->depth != 1)
+ return cfb_imageblit(info, image);
+
+ /* Src data is packaged a dword (32-bit) at a time. */
+ ndwords = ROUND_UP_TO(nbytes, 4) / 4;
+
+ /*
+ * Ring has to be padded to a quad word. But because the command starts
+ with 7 bytes, pad only if there is an even number of ndwords
+ */
+ pad = !(ndwords % 2);
+
+ DRM_DEBUG("imageblit %dx%dx%d to (%d,%d)\n", image->width,
+ image->height, image->depth, image->dx, image->dy);
+ DRM_DEBUG("nbytes: %d, ndwords: %d, pad: %d\n", nbytes, ndwords, pad);
+
+ tmp = (XY_MONO_SRC_COPY_IMM_BLT & 0xff) + ndwords;
+ cmd = (XY_MONO_SRC_COPY_IMM_BLT & ~0xff) | tmp;
+ offset = par->fb->offset;
+ dst_x1 = image->dx;
+ dst_y1 = image->dy;
+ dst_x2 = image->dx + image->width;
+ dst_y2 = image->dy + image->height;
+ rop_pitch_depth = BLT_ROP_GXCOPY | par->fb->pitch;
+
+ switch (par->fb->bits_per_pixel) {
+ case 8:
+ rop_pitch_depth |= BLT_DEPTH_8;
+ fg = image->fg_color;
+ bg = image->bg_color;
+ break;
+ case 16:
+ rop_pitch_depth |= BLT_DEPTH_16_565;
+ fg = par->fb->pseudo_palette[image->fg_color];
+ bg = par->fb->pseudo_palette[image->bg_color];
+ break;
+ case 32:
+ rop_pitch_depth |= BLT_DEPTH_32;
+ cmd |= XY_SRC_COPY_BLT_WRITE_ALPHA | XY_SRC_COPY_BLT_WRITE_RGB;
+ fg = par->fb->pseudo_palette[image->fg_color];
+ bg = par->fb->pseudo_palette[image->bg_color];
+ break;
+ default:
+ DRM_ERROR("unknown depth %d\n", par->fb->bits_per_pixel);
+ break;
+ }
+
+ BEGIN_LP_RING(8 + ndwords);
+ OUT_RING(cmd);
+ OUT_RING(rop_pitch_depth);
+ OUT_RING((dst_y1 << 16) | (dst_x1 & 0xffff));
+ OUT_RING((dst_y2 << 16) | (dst_x2 & 0xffff));
+ OUT_RING(offset);
+ OUT_RING(bg);
+ OUT_RING(fg);
+ ix = iy = 0;
+ iw = ROUND_UP_TO(image->width, 8) / 8;
+ while (ndwords--) {
+ dat = 0;
+ for (j = 0; j < 2; ++j) {
+ for (i = 0; i < 2; ++i) {
+ if (ix != iw || i == 0)
+ dat |= image->data[iy*iw + ix++] << (i+j*2)*8;
+ }
+ if (ix == iw && iy != (image->height - 1)) {
+ ix = 0;
+ ++iy;
+ }
+ }
+ OUT_RING(dat);
+ }
+ if (pad)
+ OUT_RING(MI_NOOP);
+ ADVANCE_LP_RING();
+}
+#endif
+static int intelfb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct intelfb_par *par = info->par;
+ struct drm_device *dev = par->dev;
+ struct drm_mode_set *modeset;
+ struct drm_crtc *crtc;
+ struct intel_crtc *intel_crtc;
+ int ret = 0;
+ int i;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+
+ for (i = 0; i < par->crtc_count; i++)
+ if (crtc->base.id == par->crtc_ids[i])
+ break;
+
+ if (i == par->crtc_count)
+ continue;
+
+ intel_crtc = to_intel_crtc(crtc);
+ modeset = &intel_crtc->mode_set;
+
+ modeset->x = var->xoffset;
+ modeset->y = var->yoffset;
+
+ if (modeset->num_connectors) {
+ ret = crtc->funcs->set_config(modeset);
+
+ if (!ret) {
+ info->var.xoffset = var->xoffset;
+ info->var.yoffset = var->yoffset;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static struct fb_ops intelfb_ops = {
+ .owner = THIS_MODULE,
+ //.fb_open = intelfb_open,
+ //.fb_read = intelfb_read,
+ //.fb_write = intelfb_write,
+ //.fb_release = intelfb_release,
+ //.fb_ioctl = intelfb_ioctl,
+ .fb_check_var = intelfb_check_var,
+ .fb_set_par = intelfb_set_par,
+ .fb_setcolreg = intelfb_setcolreg,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea, //intelfb_copyarea,
+ .fb_imageblit = cfb_imageblit, //intelfb_imageblit,
+ .fb_pan_display = intelfb_pan_display,
+};
+
+/**
+ * Curretly it is assumed that the old framebuffer is reused.
+ *
+ * LOCKING
+ * caller should hold the mode config lock.
+ *
+ */
+int intelfb_resize(struct drm_device *dev, struct drm_crtc *crtc)
+{
+ struct fb_info *info;
+ struct drm_framebuffer *fb;
+ struct drm_display_mode *mode = crtc->desired_mode;
+
+ fb = crtc->fb;
+ if (!fb)
+ return 1;
+
+ info = fb->fbdev;
+ if (!info)
+ return 1;
+
+ if (!mode)
+ return 1;
+
+ info->var.xres = mode->hdisplay;
+ info->var.right_margin = mode->hsync_start - mode->hdisplay;
+ info->var.hsync_len = mode->hsync_end - mode->hsync_start;
+ info->var.left_margin = mode->htotal - mode->hsync_end;
+ info->var.yres = mode->vdisplay;
+ info->var.lower_margin = mode->vsync_start - mode->vdisplay;
+ info->var.vsync_len = mode->vsync_end - mode->vsync_start;
+ info->var.upper_margin = mode->vtotal - mode->vsync_end;
+ info->var.pixclock = 10000000 / mode->htotal * 1000 / mode->vtotal * 100;
+ /* avoid overflow */
+ info->var.pixclock = info->var.pixclock * 1000 / mode->vrefresh;
+
+ return 0;
+}
+EXPORT_SYMBOL(intelfb_resize);
+
+int intelfb_create(struct drm_device *dev, uint32_t fb_width, uint32_t fb_height,
+ uint32_t surface_width, uint32_t surface_height,
+ struct intel_framebuffer **intel_fb_p)
+{
+ struct fb_info *info;
+ struct intelfb_par *par;
+ struct drm_framebuffer *fb;
+ struct intel_framebuffer *intel_fb;
+ struct drm_mode_fb_cmd mode_cmd;
+ struct drm_buffer_object *fbo = NULL;
+ struct device *device = &dev->pdev->dev;
+ int ret;
+
+ mode_cmd.width = surface_width;/* crtc->desired_mode->hdisplay; */
+ mode_cmd.height = surface_height;/* crtc->desired_mode->vdisplay; */
+
+ mode_cmd.bpp = 32;
+ mode_cmd.pitch = mode_cmd.width * ((mode_cmd.bpp + 1) / 8);
+ mode_cmd.depth = 24;
+
+ ret = drm_buffer_object_create(dev, mode_cmd.pitch * mode_cmd.height,
+ drm_bo_type_kernel,
+ DRM_BO_FLAG_READ |
+ DRM_BO_FLAG_WRITE |
+ DRM_BO_FLAG_MEM_TT |
+ DRM_BO_FLAG_MEM_VRAM |
+ DRM_BO_FLAG_NO_EVICT,
+ DRM_BO_HINT_DONT_FENCE, 0, 0,
+ &fbo);
+ if (ret || !fbo) {
+ printk(KERN_ERR "failed to allocate framebuffer\n");
+ return -EINVAL;
+ }
+
+
+ fb = intel_user_framebuffer_create(dev, NULL, &mode_cmd);
+ if (!fb) {
+ drm_bo_usage_deref_unlocked(&fbo);
+ DRM_ERROR("failed to allocate fb.\n");
+ return -EINVAL;
+ }
+
+ list_add(&fb->filp_head, &dev->mode_config.fb_kernel_list);
+
+ intel_fb = to_intel_framebuffer(fb);
+ *intel_fb_p = intel_fb;
+
+ intel_fb->bo = fbo;
+
+ info = framebuffer_alloc(sizeof(struct intelfb_par), device);
+ if (!info)
+ return -EINVAL;
+
+ par = info->par;
+
+ strcpy(info->fix.id, "inteldrmfb");
+ info->fix.type = FB_TYPE_PACKED_PIXELS;
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ info->fix.type_aux = 0;
+ info->fix.xpanstep = 1; /* doing it in hw */
+ info->fix.ypanstep = 1; /* doing it in hw */
+ info->fix.ywrapstep = 0;
+ info->fix.accel = FB_ACCEL_I830;
+ info->fix.type_aux = 0;
+
+ info->flags = FBINFO_DEFAULT;
+
+ info->fbops = &intelfb_ops;
+
+ info->fix.line_length = fb->pitch;
+ info->fix.smem_start = intel_fb->bo->offset + dev->mode_config.fb_base;
+ info->fix.smem_len = info->fix.line_length * fb->height;
+
+ info->flags = FBINFO_DEFAULT;
+
+ ret = drm_bo_kmap(intel_fb->bo, 0, intel_fb->bo->num_pages, &intel_fb->kmap);
+ if (ret)
+ DRM_ERROR("error mapping fb: %d\n", ret);
+
+ info->screen_base = intel_fb->kmap.virtual;
+ info->screen_size = info->fix.smem_len; /* FIXME */
+
+ memset(intel_fb->kmap.virtual, 0, info->screen_size);
+
+ info->pseudo_palette = fb->pseudo_palette;
+ info->var.xres_virtual = fb->width;
+ info->var.yres_virtual = fb->height;
+ info->var.bits_per_pixel = fb->bits_per_pixel;
+ info->var.xoffset = 0;
+ info->var.yoffset = 0;
+ info->var.activate = FB_ACTIVATE_NOW;
+ info->var.height = -1;
+ info->var.width = -1;
+
+ info->var.xres = fb_width;
+ info->var.yres = fb_height;
+
+ if (IS_I9XX(dev)) {
+ info->fix.mmio_start = pci_resource_start(dev->pdev, 0);
+ info->fix.mmio_len = pci_resource_len(dev->pdev, 0);
+ } else {
+ info->fix.mmio_start = pci_resource_start(dev->pdev, 1);
+ info->fix.mmio_len = pci_resource_len(dev->pdev, 1);
+ }
+
+ info->pixmap.size = 64*1024;
+ info->pixmap.buf_align = 8;
+ info->pixmap.access_align = 32;
+ info->pixmap.flags = FB_PIXMAP_SYSTEM;
+ info->pixmap.scan_align = 1;
+
+ DRM_DEBUG("fb depth is %d\n", fb->depth);
+ DRM_DEBUG(" pitch is %d\n", fb->pitch);
+ switch(fb->depth) {
+ case 8:
+ info->var.red.offset = 0;
+ info->var.green.offset = 0;
+ info->var.blue.offset = 0;
+ info->var.red.length = 8; /* 8bit DAC */
+ info->var.green.length = 8;
+ info->var.blue.length = 8;
+ info->var.transp.offset = 0;
+ info->var.transp.length = 0;
+ break;
+ case 15:
+ info->var.red.offset = 10;
+ info->var.green.offset = 5;
+ info->var.blue.offset = 0;
+ info->var.red.length = 5;
+ info->var.green.length = 5;
+ info->var.blue.length = 5;
+ info->var.transp.offset = 15;
+ info->var.transp.length = 1;
+ break;
+ case 16:
+ info->var.red.offset = 11;
+ info->var.green.offset = 5;
+ info->var.blue.offset = 0;
+ info->var.red.length = 5;
+ info->var.green.length = 6;
+ info->var.blue.length = 5;
+ info->var.transp.offset = 0;
+ break;
+ case 24:
+ info->var.red.offset = 16;
+ info->var.green.offset = 8;
+ info->var.blue.offset = 0;
+ info->var.red.length = 8;
+ info->var.green.length = 8;
+ info->var.blue.length = 8;
+ info->var.transp.offset = 0;
+ info->var.transp.length = 0;
+ break;
+ case 32:
+ info->var.red.offset = 16;
+ info->var.green.offset = 8;
+ info->var.blue.offset = 0;
+ info->var.red.length = 8;
+ info->var.green.length = 8;
+ info->var.blue.length = 8;
+ info->var.transp.offset = 24;
+ info->var.transp.length = 8;
+ break;
+ default:
+ break;
+ }
+
+ fb->fbdev = info;
+
+ par->intel_fb = intel_fb;
+ par->dev = dev;
+
+ /* To allow resizeing without swapping buffers */
+ printk("allocated %dx%d fb: 0x%08lx, bo %p\n", intel_fb->base.width,
+ intel_fb->base.height, intel_fb->bo->offset, fbo);
+
+ return 0;
+}
+
+static int intelfb_multi_fb_probe_crtc(struct drm_device *dev, struct drm_crtc *crtc)
+{
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct intel_framebuffer *intel_fb;
+ struct drm_framebuffer *fb;
+ struct drm_connector *connector;
+ struct fb_info *info;
+ struct intelfb_par *par;
+ struct drm_mode_set *modeset;
+ unsigned int width, height;
+ int new_fb = 0;
+ int ret, i, conn_count;
+
+ if (!drm_helper_crtc_in_use(crtc))
+ return 0;
+
+ if (!crtc->desired_mode)
+ return 0;
+
+ width = crtc->desired_mode->hdisplay;
+ height = crtc->desired_mode->vdisplay;
+
+ /* is there an fb bound to this crtc already */
+ if (!intel_crtc->mode_set.fb) {
+ ret = intelfb_create(dev, width, height, width, height, &intel_fb);
+ if (ret)
+ return -EINVAL;
+ new_fb = 1;
+ } else {
+ fb = intel_crtc->mode_set.fb;
+ intel_fb = to_intel_framebuffer(fb);
+ if ((intel_fb->base.width < width) || (intel_fb->base.height < height))
+ return -EINVAL;
+ }
+
+ info = intel_fb->base.fbdev;
+ par = info->par;
+
+ modeset = &intel_crtc->mode_set;
+ modeset->fb = &intel_fb->base;
+ conn_count = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (connector->encoder)
+ if (connector->encoder->crtc == modeset->crtc) {
+ modeset->connectors[conn_count] = connector;
+ conn_count++;
+ if (conn_count > INTELFB_CONN_LIMIT)
+ BUG();
+ }
+ }
+
+ for (i = conn_count; i < INTELFB_CONN_LIMIT; i++)
+ modeset->connectors[i] = NULL;
+
+ par->crtc_ids[0] = crtc->base.id;
+
+ modeset->num_connectors = conn_count;
+ if (modeset->mode != modeset->crtc->desired_mode)
+ modeset->mode = modeset->crtc->desired_mode;
+
+ par->crtc_count = 1;
+
+ if (new_fb) {
+ info->var.pixclock = -1;
+ if (register_framebuffer(info) < 0)
+ return -EINVAL;
+ } else
+ intelfb_set_par(info);
+
+ printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
+ info->fix.id);
+ return 0;
+}
+
+static int intelfb_multi_fb_probe(struct drm_device *dev)
+{
+
+ struct drm_crtc *crtc;
+ int ret = 0;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ ret = intelfb_multi_fb_probe_crtc(dev, crtc);
+ if (ret)
+ return ret;
+ }
+ return ret;
+}
+
+static int intelfb_single_fb_probe(struct drm_device *dev)
+{
+ struct drm_crtc *crtc;
+ struct drm_connector *connector;
+ unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1;
+ unsigned int surface_width = 0, surface_height = 0;
+ int new_fb = 0;
+ int crtc_count = 0;
+ int ret, i, conn_count = 0;
+ struct intel_framebuffer *intel_fb;
+ struct fb_info *info;
+ struct intelfb_par *par;
+ struct drm_mode_set *modeset;
+
+ DRM_DEBUG("\n");
+ /* first up get a count of crtcs now in use and new min/maxes width/heights */
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ if (drm_helper_crtc_in_use(crtc)) {
+ if (crtc->desired_mode) {
+ if (crtc->desired_mode->hdisplay < fb_width)
+ fb_width = crtc->desired_mode->hdisplay;
+
+ if (crtc->desired_mode->vdisplay < fb_height)
+ fb_height = crtc->desired_mode->vdisplay;
+
+ if (crtc->desired_mode->hdisplay > surface_width)
+ surface_width = crtc->desired_mode->hdisplay;
+
+ if (crtc->desired_mode->vdisplay > surface_height)
+ surface_height = crtc->desired_mode->vdisplay;
+
+ }
+ crtc_count++;
+ }
+ }
+
+ if (crtc_count == 0 || fb_width == -1 || fb_height == -1) {
+ /* hmm everyone went away - assume VGA cable just fell out
+ and will come back later. */
+ return 0;
+ }
+
+ /* do we have an fb already? */
+ if (list_empty(&dev->mode_config.fb_kernel_list)) {
+ /* create an fb if we don't have one */
+ ret = intelfb_create(dev, fb_width, fb_height, surface_width, surface_height, &intel_fb);
+ if (ret)
+ return -EINVAL;
+ new_fb = 1;
+ } else {
+ struct drm_framebuffer *fb;
+ fb = list_first_entry(&dev->mode_config.fb_kernel_list, struct drm_framebuffer, filp_head);
+ intel_fb = to_intel_framebuffer(fb);
+
+ /* if someone hotplugs something bigger than we have already allocated, we are pwned.
+ As really we can't resize an fbdev that is in the wild currently due to fbdev
+ not really being designed for the lower layers moving stuff around under it.
+ - so in the grand style of things - punt. */
+ if ((fb->width < surface_width) || (fb->height < surface_height)) {
+ DRM_ERROR("Framebuffer not large enough to scale console onto.\n");
+ return -EINVAL;
+ }
+ }
+
+ info = intel_fb->base.fbdev;
+ par = info->par;
+
+ crtc_count = 0;
+ /* okay we need to setup new connector sets in the crtcs */
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ modeset = &intel_crtc->mode_set;
+ modeset->fb = &intel_fb->base;
+ conn_count = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (connector->encoder)
+ if(connector->encoder->crtc == modeset->crtc) {
+ modeset->connectors[conn_count] = connector;
+ conn_count++;
+ if (conn_count > INTELFB_CONN_LIMIT)
+ BUG();
+ }
+ }
+
+ for (i = conn_count; i < INTELFB_CONN_LIMIT; i++)
+ modeset->connectors[i] = NULL;
+
+ par->crtc_ids[crtc_count++] = crtc->base.id;
+
+ modeset->num_connectors = conn_count;
+ if (modeset->mode != modeset->crtc->desired_mode)
+ modeset->mode = modeset->crtc->desired_mode;
+ }
+ par->crtc_count = crtc_count;
+
+ if (new_fb) {
+ info->var.pixclock = -1;
+ if (register_framebuffer(info) < 0)
+ return -EINVAL;
+ } else
+ intelfb_set_par(info);
+
+ printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
+ info->fix.id);
+ return 0;
+}
+
+int intelfb_probe(struct drm_device *dev)
+{
+ int ret;
+
+ DRM_DEBUG("\n");
+
+ /* something has changed in the lower levels of hell - deal with it
+ here */
+
+ /* two modes : a) 1 fb to rule all crtcs.
+ b) one fb per crtc.
+ two actions 1) new connected device
+ 2) device removed.
+ case a/1 : if the fb surface isn't big enough - resize the surface fb.
+ if the fb size isn't big enough - resize fb into surface.
+ if everything big enough configure the new crtc/etc.
+ case a/2 : undo the configuration
+ possibly resize down the fb to fit the new configuration.
+ case b/1 : see if it is on a new crtc - setup a new fb and add it.
+ case b/2 : teardown the new fb.
+ */
+
+ /* mode a first */
+ /* search for an fb */
+ if (i915_fbpercrtc == 1) {
+ ret = intelfb_multi_fb_probe(dev);
+ } else {
+ ret = intelfb_single_fb_probe(dev);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(intelfb_probe);
+
+int intelfb_remove(struct drm_device *dev, struct drm_framebuffer *fb)
+{
+ struct fb_info *info;
+ struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
+
+ if (!fb)
+ return -EINVAL;
+
+ info = fb->fbdev;
+
+ if (info) {
+ unregister_framebuffer(info);
+ drm_bo_kunmap(&intel_fb->kmap);
+ drm_bo_usage_deref_unlocked(&intel_fb->bo);
+ framebuffer_release(info);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(intelfb_remove);
+MODULE_LICENSE("GPL");
diff --git a/linux-core/intel_i2c.c b/linux-core/intel_i2c.c
new file mode 100644
index 00000000..efcbf656
--- /dev/null
+++ b/linux-core/intel_i2c.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright © 2006-2007 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ */
+/*
+ * Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
+ * Jesse Barnes <jesse.barnes@intel.com>
+ */
+
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/i2c-algo-bit.h>
+#include "drmP.h"
+#include "drm.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+/*
+ * Intel GPIO access functions
+ */
+
+#define I2C_RISEFALL_TIME 20
+
+static int get_clock(void *data)
+{
+ struct intel_i2c_chan *chan = data;
+ struct drm_i915_private *dev_priv = chan->drm_dev->dev_private;
+ u32 val;
+
+ val = I915_READ(chan->reg);
+ return ((val & GPIO_CLOCK_VAL_IN) != 0);
+}
+
+static int get_data(void *data)
+{
+ struct intel_i2c_chan *chan = data;
+ struct drm_i915_private *dev_priv = chan->drm_dev->dev_private;
+ u32 val;
+
+ val = I915_READ(chan->reg);
+ return ((val & GPIO_DATA_VAL_IN) != 0);
+}
+
+static void set_clock(void *data, int state_high)
+{
+ struct intel_i2c_chan *chan = data;
+ struct drm_device *dev = chan->drm_dev;
+ struct drm_i915_private *dev_priv = chan->drm_dev->dev_private;
+ u32 reserved = 0, clock_bits;
+
+ /* On most chips, these bits must be preserved in software. */
+ if (!IS_I830(dev) && !IS_845G(dev))
+ reserved = I915_READ(chan->reg) & (GPIO_DATA_PULLUP_DISABLE |
+ GPIO_CLOCK_PULLUP_DISABLE);
+
+ if (state_high)
+ clock_bits = GPIO_CLOCK_DIR_IN | GPIO_CLOCK_DIR_MASK;
+ else
+ clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK |
+ GPIO_CLOCK_VAL_MASK;
+ I915_WRITE(chan->reg, reserved | clock_bits);
+ udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */
+}
+
+static void set_data(void *data, int state_high)
+{
+ struct intel_i2c_chan *chan = data;
+ struct drm_device *dev = chan->drm_dev;
+ struct drm_i915_private *dev_priv = chan->drm_dev->dev_private;
+ u32 reserved = 0, data_bits;
+
+ /* On most chips, these bits must be preserved in software. */
+ if (!IS_I830(dev) && !IS_845G(dev))
+ reserved = I915_READ(chan->reg) & (GPIO_DATA_PULLUP_DISABLE |
+ GPIO_CLOCK_PULLUP_DISABLE);
+
+ if (state_high)
+ data_bits = GPIO_DATA_DIR_IN | GPIO_DATA_DIR_MASK;
+ else
+ data_bits = GPIO_DATA_DIR_OUT | GPIO_DATA_DIR_MASK |
+ GPIO_DATA_VAL_MASK;
+
+ I915_WRITE(chan->reg, reserved | data_bits);
+ udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */
+}
+
+/**
+ * intel_i2c_create - instantiate an Intel i2c bus using the specified GPIO reg
+ * @dev: DRM device
+ * @output: driver specific output device
+ * @reg: GPIO reg to use
+ * @name: name for this bus
+ *
+ * Creates and registers a new i2c bus with the Linux i2c layer, for use
+ * in output probing and control (e.g. DDC or SDVO control functions).
+ *
+ * Possible values for @reg include:
+ * %GPIOA
+ * %GPIOB
+ * %GPIOC
+ * %GPIOD
+ * %GPIOE
+ * %GPIOF
+ * %GPIOG
+ * %GPIOH
+ * see PRM for details on how these different busses are used.
+ */
+struct intel_i2c_chan *intel_i2c_create(struct drm_device *dev, const u32 reg,
+ const char *name)
+{
+ struct intel_i2c_chan *chan;
+
+ chan = kzalloc(sizeof(struct intel_i2c_chan), GFP_KERNEL);
+ if (!chan)
+ goto out_free;
+
+ chan->drm_dev = dev;
+ chan->reg = reg;
+ snprintf(chan->adapter.name, I2C_NAME_SIZE, "intel drm %s", name);
+ chan->adapter.owner = THIS_MODULE;
+#ifndef I2C_HW_B_INTELFB
+#define I2C_HW_B_INTELFB I2C_HW_B_I810
+#endif
+ chan->adapter.id = I2C_HW_B_INTELFB;
+ chan->adapter.algo_data = &chan->algo;
+ chan->adapter.dev.parent = &dev->pdev->dev;
+ chan->algo.setsda = set_data;
+ chan->algo.setscl = set_clock;
+ chan->algo.getsda = get_data;
+ chan->algo.getscl = get_clock;
+ chan->algo.udelay = 20;
+ chan->algo.timeout = usecs_to_jiffies(2200);
+ chan->algo.data = chan;
+
+ i2c_set_adapdata(&chan->adapter, chan);
+
+ if(i2c_bit_add_bus(&chan->adapter))
+ goto out_free;
+
+ /* JJJ: raise SCL and SDA? */
+ set_data(chan, 1);
+ set_clock(chan, 1);
+ udelay(20);
+
+ return chan;
+
+out_free:
+ kfree(chan);
+ return NULL;
+}
+
+/**
+ * intel_i2c_destroy - unregister and free i2c bus resources
+ * @output: channel to free
+ *
+ * Unregister the adapter from the i2c layer, then free the structure.
+ */
+void intel_i2c_destroy(struct intel_i2c_chan *chan)
+{
+ if (!chan)
+ return;
+
+ i2c_del_adapter(&chan->adapter);
+ kfree(chan);
+}
+
+
+
diff --git a/linux-core/intel_lvds.c b/linux-core/intel_lvds.c
new file mode 100644
index 00000000..04e110e4
--- /dev/null
+++ b/linux-core/intel_lvds.c
@@ -0,0 +1,515 @@
+/*
+ * Copyright © 2006-2007 Intel Corporation
+ * Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ * Dave Airlie <airlied@linux.ie>
+ * Jesse Barnes <jesse.barnes@intel.com>
+ */
+
+#include <linux/i2c.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "drm_edid.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+/**
+ * Sets the backlight level.
+ *
+ * \param level backlight level, from 0 to intel_lvds_get_max_backlight().
+ */
+static void intel_lvds_set_backlight(struct drm_device *dev, int level)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 blc_pwm_ctl;
+
+ blc_pwm_ctl = I915_READ(BLC_PWM_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK;
+ I915_WRITE(BLC_PWM_CTL, (blc_pwm_ctl |
+ (level << BACKLIGHT_DUTY_CYCLE_SHIFT)));
+}
+
+/**
+ * Returns the maximum level of the backlight duty cycle field.
+ */
+static u32 intel_lvds_get_max_backlight(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ return ((I915_READ(BLC_PWM_CTL) & BACKLIGHT_MODULATION_FREQ_MASK) >>
+ BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
+}
+
+/**
+ * Sets the power state for the panel.
+ */
+static void intel_lvds_set_power(struct drm_device *dev, bool on)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 pp_status;
+
+ if (on) {
+ I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) |
+ POWER_TARGET_ON);
+ do {
+ pp_status = I915_READ(PP_STATUS);
+ } while ((pp_status & PP_ON) == 0);
+
+ intel_lvds_set_backlight(dev, dev_priv->backlight_duty_cycle);
+ } else {
+ intel_lvds_set_backlight(dev, 0);
+
+ I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) &
+ ~POWER_TARGET_ON);
+ do {
+ pp_status = I915_READ(PP_STATUS);
+ } while (pp_status & PP_ON);
+ }
+}
+
+static void intel_lvds_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+
+ if (mode == DPMSModeOn)
+ intel_lvds_set_power(dev, true);
+ else
+ intel_lvds_set_power(dev, false);
+
+ /* XXX: We never power down the LVDS pairs. */
+}
+
+static void intel_lvds_save(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ dev_priv->savePP_ON = I915_READ(PP_ON_DELAYS);
+ dev_priv->savePP_OFF = I915_READ(PP_OFF_DELAYS);
+ dev_priv->savePP_CONTROL = I915_READ(PP_CONTROL);
+ dev_priv->savePP_DIVISOR = I915_READ(PP_DIVISOR);
+ dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL);
+ dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL &
+ BACKLIGHT_DUTY_CYCLE_MASK);
+
+ /*
+ * If the light is off at server startup, just make it full brightness
+ */
+ if (dev_priv->backlight_duty_cycle == 0)
+ dev_priv->backlight_duty_cycle =
+ intel_lvds_get_max_backlight(dev);
+}
+
+static void intel_lvds_restore(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ I915_WRITE(BLC_PWM_CTL, dev_priv->saveBLC_PWM_CTL);
+ I915_WRITE(PP_ON_DELAYS, dev_priv->savePP_ON);
+ I915_WRITE(PP_OFF_DELAYS, dev_priv->savePP_OFF);
+ I915_WRITE(PP_DIVISOR, dev_priv->savePP_DIVISOR);
+ I915_WRITE(PP_CONTROL, dev_priv->savePP_CONTROL);
+ if (dev_priv->savePP_CONTROL & POWER_TARGET_ON)
+ intel_lvds_set_power(dev, true);
+ else
+ intel_lvds_set_power(dev, false);
+}
+
+static int intel_lvds_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_display_mode *fixed_mode = dev_priv->panel_fixed_mode;
+
+ if (fixed_mode) {
+ if (mode->hdisplay > fixed_mode->hdisplay)
+ return MODE_PANEL;
+ if (mode->vdisplay > fixed_mode->vdisplay)
+ return MODE_PANEL;
+ }
+
+ return MODE_OK;
+}
+
+static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+ struct drm_encoder *tmp_encoder;
+
+ /* Should never happen!! */
+ if (!IS_I965G(dev) && intel_crtc->pipe == 0) {
+ printk(KERN_ERR "Can't support LVDS on pipe A\n");
+ return false;
+ }
+
+ /* Should never happen!! */
+ list_for_each_entry(tmp_encoder, &dev->mode_config.encoder_list, head) {
+ if (tmp_encoder != encoder && tmp_encoder->crtc == encoder->crtc) {
+ printk(KERN_ERR "Can't enable LVDS and another "
+ "encoder on the same pipe\n");
+ return false;
+ }
+ }
+
+ /*
+ * If we have timings from the BIOS for the panel, put them in
+ * to the adjusted mode. The CRTC will be set up for this mode,
+ * with the panel scaling set up to source from the H/VDisplay
+ * of the original mode.
+ */
+ if (dev_priv->panel_fixed_mode != NULL) {
+ adjusted_mode->hdisplay = dev_priv->panel_fixed_mode->hdisplay;
+ adjusted_mode->hsync_start =
+ dev_priv->panel_fixed_mode->hsync_start;
+ adjusted_mode->hsync_end =
+ dev_priv->panel_fixed_mode->hsync_end;
+ adjusted_mode->htotal = dev_priv->panel_fixed_mode->htotal;
+ adjusted_mode->vdisplay = dev_priv->panel_fixed_mode->vdisplay;
+ adjusted_mode->vsync_start =
+ dev_priv->panel_fixed_mode->vsync_start;
+ adjusted_mode->vsync_end =
+ dev_priv->panel_fixed_mode->vsync_end;
+ adjusted_mode->vtotal = dev_priv->panel_fixed_mode->vtotal;
+ adjusted_mode->clock = dev_priv->panel_fixed_mode->clock;
+ drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);
+ }
+
+ /*
+ * XXX: It would be nice to support lower refresh rates on the
+ * panels to reduce power consumption, and perhaps match the
+ * user's requested refresh rate.
+ */
+
+ return true;
+}
+
+static void intel_lvds_prepare(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL);
+ dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL &
+ BACKLIGHT_DUTY_CYCLE_MASK);
+
+ intel_lvds_set_power(dev, false);
+}
+
+static void intel_lvds_commit( struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (dev_priv->backlight_duty_cycle == 0)
+ dev_priv->backlight_duty_cycle =
+ intel_lvds_get_max_backlight(dev);
+
+ intel_lvds_set_power(dev, true);
+}
+
+static void intel_lvds_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+ u32 pfit_control;
+
+ /*
+ * The LVDS pin pair will already have been turned on in the
+ * intel_crtc_mode_set since it has a large impact on the DPLL
+ * settings.
+ */
+
+ /*
+ * Enable automatic panel scaling so that non-native modes fill the
+ * screen. Should be enabled before the pipe is enabled, according to
+ * register description and PRM.
+ */
+ if (mode->hdisplay != adjusted_mode->hdisplay ||
+ mode->vdisplay != adjusted_mode->vdisplay)
+ pfit_control = (PFIT_ENABLE | VERT_AUTO_SCALE |
+ HORIZ_AUTO_SCALE | VERT_INTERP_BILINEAR |
+ HORIZ_INTERP_BILINEAR);
+ else
+ pfit_control = 0;
+
+ if (!IS_I965G(dev)) {
+ if (dev_priv->panel_wants_dither)
+ pfit_control |= PANEL_8TO6_DITHER_ENABLE;
+ }
+ else
+ pfit_control |= intel_crtc->pipe << PFIT_PIPE_SHIFT;
+
+ I915_WRITE(PFIT_CONTROL, pfit_control);
+}
+
+/**
+ * Detect the LVDS connection.
+ *
+ * This always returns CONNECTOR_STATUS_CONNECTED. This connector should only have
+ * been set up if the LVDS was actually connected anyway.
+ */
+static enum drm_connector_status intel_lvds_detect(struct drm_connector *connector)
+{
+ return connector_status_connected;
+}
+
+/**
+ * Return the list of DDC modes if available, or the BIOS fixed mode otherwise.
+ */
+static int intel_lvds_get_modes(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret = 0;
+
+ ret = intel_ddc_get_modes(intel_output);
+
+ if (ret)
+ return ret;
+
+ /* Didn't get an EDID, so
+ * Set wide sync ranges so we get all modes
+ * handed to valid_mode for checking
+ */
+ connector->display_info.min_vfreq = 0;
+ connector->display_info.max_vfreq = 200;
+ connector->display_info.min_hfreq = 0;
+ connector->display_info.max_hfreq = 200;
+
+ if (dev_priv->panel_fixed_mode != NULL) {
+ struct drm_display_mode *mode =
+ drm_mode_duplicate(dev, dev_priv->panel_fixed_mode);
+ drm_mode_probed_add(connector, mode);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * intel_lvds_destroy - unregister and free LVDS structures
+ * @connector: connector to free
+ *
+ * Unregister the DDC bus for this connector then free the driver private
+ * structure.
+ */
+static void intel_lvds_destroy(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ if (intel_output->ddc_bus)
+ intel_i2c_destroy(intel_output->ddc_bus);
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
+ kfree(connector);
+}
+
+static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = {
+ .dpms = intel_lvds_dpms,
+ .mode_fixup = intel_lvds_mode_fixup,
+ .prepare = intel_lvds_prepare,
+ .mode_set = intel_lvds_mode_set,
+ .commit = intel_lvds_commit,
+};
+
+static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = {
+ .get_modes = intel_lvds_get_modes,
+ .mode_valid = intel_lvds_mode_valid,
+ .best_encoder = intel_best_encoder,
+};
+
+static const struct drm_connector_funcs intel_lvds_connector_funcs = {
+ .save = intel_lvds_save,
+ .restore = intel_lvds_restore,
+ .detect = intel_lvds_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = intel_lvds_destroy,
+};
+
+
+static void intel_lvds_enc_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs intel_lvds_enc_funcs = {
+ .destroy = intel_lvds_enc_destroy,
+};
+
+
+
+/**
+ * intel_lvds_init - setup LVDS connectors on this device
+ * @dev: drm device
+ *
+ * Create the connector, register the LVDS DDC bus, and try to figure out what
+ * modes we can display on the LVDS panel (if present).
+ */
+void intel_lvds_init(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_output *intel_output;
+ struct drm_connector *connector;
+ struct drm_encoder *encoder;
+ struct drm_display_mode *scan; /* *modes, *bios_mode; */
+ struct drm_crtc *crtc;
+ u32 lvds;
+ int pipe;
+
+ intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL);
+ if (!intel_output) {
+ return;
+ }
+
+ connector = &intel_output->base;
+ encoder = &intel_output->enc;
+ drm_connector_init(dev, &intel_output->base, &intel_lvds_connector_funcs,
+ DRM_MODE_CONNECTOR_LVDS);
+
+ drm_encoder_init(dev, &intel_output->enc, &intel_lvds_enc_funcs,
+ DRM_MODE_ENCODER_LVDS);
+
+ drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc);
+ intel_output->type = INTEL_OUTPUT_LVDS;
+
+ drm_encoder_helper_add(encoder, &intel_lvds_helper_funcs);
+ drm_connector_helper_add(connector, &intel_lvds_connector_helper_funcs);
+ connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+ connector->interlace_allowed = FALSE;
+ connector->doublescan_allowed = FALSE;
+
+
+ /*
+ * LVDS discovery:
+ * 1) check for EDID on DDC
+ * 2) check for VBT data
+ * 3) check to see if LVDS is already on
+ * if none of the above, no panel
+ * 4) make sure lid is open
+ * if closed, act like it's not there for now
+ */
+
+ /* Set up the DDC bus. */
+ intel_output->ddc_bus = intel_i2c_create(dev, GPIOC, "LVDSDDC_C");
+ if (!intel_output->ddc_bus) {
+ dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration "
+ "failed.\n");
+ goto failed;
+ }
+
+ /*
+ * Attempt to get the fixed panel mode from DDC. Assume that the
+ * preferred mode is the right one.
+ */
+ intel_ddc_get_modes(intel_output);
+
+ list_for_each_entry(scan, &connector->probed_modes, head) {
+ if (scan->type & DRM_MODE_TYPE_PREFERRED) {
+ dev_priv->panel_fixed_mode =
+ drm_mode_duplicate(dev, scan);
+ goto out; /* FIXME: check for quirks */
+ }
+ }
+
+ /* Failed to get EDID, what about VBT? */
+ if (dev_priv->vbt_mode)
+ dev_priv->panel_fixed_mode =
+ drm_mode_duplicate(dev, dev_priv->vbt_mode);
+
+ /*
+ * If we didn't get EDID, try checking if the panel is already turned
+ * on. If so, assume that whatever is currently programmed is the
+ * correct mode.
+ */
+ lvds = I915_READ(LVDS);
+ pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0;
+ crtc = intel_get_crtc_from_pipe(dev, pipe);
+
+ if (crtc && (lvds & LVDS_PORT_EN)) {
+ dev_priv->panel_fixed_mode = intel_crtc_mode_get(dev, crtc);
+ if (dev_priv->panel_fixed_mode) {
+ dev_priv->panel_fixed_mode->type |=
+ DRM_MODE_TYPE_PREFERRED;
+ goto out; /* FIXME: check for quirks */
+ }
+ }
+
+ /* If we still don't have a mode after all that, give up. */
+ if (!dev_priv->panel_fixed_mode)
+ goto failed;
+
+ /* FIXME: detect aopen & mac mini type stuff automatically? */
+ /*
+ * Blacklist machines with BIOSes that list an LVDS panel without
+ * actually having one.
+ */
+ if (IS_I945GM(dev)) {
+ /* aopen mini pc */
+ if (dev->pdev->subsystem_vendor == 0xa0a0)
+ goto failed;
+
+ if ((dev->pdev->subsystem_vendor == 0x8086) &&
+ (dev->pdev->subsystem_device == 0x7270)) {
+ /* It's a Mac Mini or Macbook Pro.
+ *
+ * Apple hardware is out to get us. The macbook pro
+ * has a real LVDS panel, but the mac mini does not,
+ * and they have the same device IDs. We'll
+ * distinguish by panel size, on the assumption
+ * that Apple isn't about to make any machines with an
+ * 800x600 display.
+ */
+
+ if (dev_priv->panel_fixed_mode != NULL &&
+ dev_priv->panel_fixed_mode->hdisplay == 800 &&
+ dev_priv->panel_fixed_mode->vdisplay == 600) {
+ DRM_DEBUG("Suspected Mac Mini, ignoring the LVDS\n");
+ goto failed;
+ }
+ }
+ }
+
+
+out:
+ drm_sysfs_connector_add(connector);
+ return;
+
+failed:
+ DRM_DEBUG("No LVDS modes found, disabling.\n");
+ if (intel_output->ddc_bus)
+ intel_i2c_destroy(intel_output->ddc_bus);
+ drm_connector_cleanup(connector);
+ kfree(connector);
+}
diff --git a/linux-core/intel_modes.c b/linux-core/intel_modes.c
new file mode 100644
index 00000000..79be3575
--- /dev/null
+++ b/linux-core/intel_modes.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (c) 2007 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ */
+
+#include <linux/i2c.h>
+#include <linux/fb.h>
+#include "drmP.h"
+#include "intel_drv.h"
+
+/**
+ * intel_ddc_probe
+ *
+ */
+bool intel_ddc_probe(struct intel_output *intel_output)
+{
+ u8 out_buf[] = { 0x0, 0x0};
+ u8 buf[2];
+ int ret;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = 0x50,
+ .flags = 0,
+ .len = 1,
+ .buf = out_buf,
+ },
+ {
+ .addr = 0x50,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = buf,
+ }
+ };
+
+ ret = i2c_transfer(&intel_output->ddc_bus->adapter, msgs, 2);
+ if (ret == 2)
+ return true;
+
+ return false;
+}
+
+/**
+ * intel_ddc_get_modes - get modelist from monitor
+ * @connector: DRM connector device to use
+ *
+ * Fetch the EDID information from @connector using the DDC bus.
+ */
+int intel_ddc_get_modes(struct intel_output *intel_output)
+{
+ struct edid *edid;
+ int ret = 0;
+
+ edid = drm_get_edid(&intel_output->base, &intel_output->ddc_bus->adapter);
+ if (edid) {
+ drm_mode_connector_update_edid_property(&intel_output->base, edid);
+ ret = drm_add_edid_modes(&intel_output->base, edid);
+ kfree(edid);
+ }
+ return ret;
+}
diff --git a/linux-core/intel_sdvo.c b/linux-core/intel_sdvo.c
new file mode 100644
index 00000000..f0a47e2e
--- /dev/null
+++ b/linux-core/intel_sdvo.c
@@ -0,0 +1,1153 @@
+/*
+ * Copyright © 2006-2007 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ */
+/*
+ * Copyright 2006 Dave Airlie <airlied@linux.ie>
+ * Jesse Barnes <jesse.barnes@intel.com>
+ */
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+#include "intel_sdvo_regs.h"
+
+struct intel_sdvo_priv {
+ struct intel_i2c_chan *i2c_bus;
+ int slaveaddr;
+ int output_device;
+
+ u16 active_outputs;
+
+ struct intel_sdvo_caps caps;
+ int pixel_clock_min, pixel_clock_max;
+
+ int save_sdvo_mult;
+ u16 save_active_outputs;
+ struct intel_sdvo_dtd save_input_dtd_1, save_input_dtd_2;
+ struct intel_sdvo_dtd save_output_dtd[16];
+ u32 save_SDVOX;
+};
+
+/**
+ * Writes the SDVOB or SDVOC with the given value, but always writes both
+ * SDVOB and SDVOC to work around apparent hardware issues (according to
+ * comments in the BIOS).
+ */
+void intel_sdvo_write_sdvox(struct intel_output *intel_output, u32 val)
+{
+ struct drm_device *dev = intel_output->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ u32 bval = val, cval = val;
+ int i;
+
+ if (sdvo_priv->output_device == SDVOB) {
+ cval = I915_READ(SDVOC);
+ } else {
+ bval = I915_READ(SDVOB);
+ }
+ /*
+ * Write the registers twice for luck. Sometimes,
+ * writing them only once doesn't appear to 'stick'.
+ * The BIOS does this too. Yay, magic
+ */
+ for (i = 0; i < 2; i++)
+ {
+ I915_WRITE(SDVOB, bval);
+ I915_READ(SDVOB);
+ I915_WRITE(SDVOC, cval);
+ I915_READ(SDVOC);
+ }
+}
+
+static bool intel_sdvo_read_byte(struct intel_output *intel_output, u8 addr,
+ u8 *ch)
+{
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ u8 out_buf[2];
+ u8 buf[2];
+ int ret;
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = sdvo_priv->i2c_bus->slave_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = out_buf,
+ },
+ {
+ .addr = sdvo_priv->i2c_bus->slave_addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = buf,
+ }
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = 0;
+
+ if ((ret = i2c_transfer(&sdvo_priv->i2c_bus->adapter, msgs, 2)) == 2)
+ {
+// DRM_DEBUG("got back from addr %02X = %02x\n", out_buf[0], buf[0]);
+ *ch = buf[0];
+ return true;
+ }
+
+ DRM_DEBUG("i2c transfer returned %d\n", ret);
+ return false;
+}
+
+static bool intel_sdvo_write_byte(struct intel_output *intel_output, int addr,
+ u8 ch)
+{
+ u8 out_buf[2];
+ struct i2c_msg msgs[] = {
+ {
+ .addr = intel_output->i2c_bus->slave_addr,
+ .flags = 0,
+ .len = 2,
+ .buf = out_buf,
+ }
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = ch;
+
+ if (i2c_transfer(&intel_output->i2c_bus->adapter, msgs, 1) == 1)
+ {
+ return true;
+ }
+ return false;
+}
+
+#define SDVO_CMD_NAME_ENTRY(cmd) {cmd, #cmd}
+/** Mapping of command numbers to names, for debug output */
+const static struct _sdvo_cmd_name {
+ u8 cmd;
+ char *name;
+} sdvo_cmd_names[] = {
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_RESET),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DEVICE_CAPS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FIRMWARE_REV),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TRAINED_INPUTS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_OUTPUTS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_OUTPUTS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_IN_OUT_MAP),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_IN_OUT_MAP),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ATTACHED_DISPLAYS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HOT_PLUG_SUPPORT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_HOT_PLUG),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_HOT_PLUG),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_INPUT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_OUTPUT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART2),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART2),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART2),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART2),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CLOCK_RATE_MULT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CLOCK_RATE_MULT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_TV_FORMATS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_FORMAT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_FORMAT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_RESOLUTION_SUPPORT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTROL_BUS_SWITCH),
+};
+
+#define SDVO_NAME(dev_priv) ((dev_priv)->output_device == SDVOB ? "SDVOB" : "SDVOC")
+#define SDVO_PRIV(output) ((struct intel_sdvo_priv *) (output)->dev_priv)
+
+static void intel_sdvo_write_cmd(struct intel_output *intel_output, u8 cmd,
+ void *args, int args_len)
+{
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ int i;
+
+ if (1) {
+ DRM_DEBUG("%s: W: %02X ", SDVO_NAME(sdvo_priv), cmd);
+ for (i = 0; i < args_len; i++)
+ printk("%02X ", ((u8 *)args)[i]);
+ for (; i < 8; i++)
+ printk(" ");
+ for (i = 0; i < sizeof(sdvo_cmd_names) / sizeof(sdvo_cmd_names[0]); i++) {
+ if (cmd == sdvo_cmd_names[i].cmd) {
+ printk("(%s)", sdvo_cmd_names[i].name);
+ break;
+ }
+ }
+ if (i == sizeof(sdvo_cmd_names)/ sizeof(sdvo_cmd_names[0]))
+ printk("(%02X)",cmd);
+ printk("\n");
+ }
+
+ for (i = 0; i < args_len; i++) {
+ intel_sdvo_write_byte(intel_output, SDVO_I2C_ARG_0 - i, ((u8*)args)[i]);
+ }
+
+ intel_sdvo_write_byte(intel_output, SDVO_I2C_OPCODE, cmd);
+}
+
+static const char *cmd_status_names[] = {
+ "Power on",
+ "Success",
+ "Not supported",
+ "Invalid arg",
+ "Pending",
+ "Target not specified",
+ "Scaling not supported"
+};
+
+static u8 intel_sdvo_read_response(struct intel_output *intel_output, void *response,
+ int response_len)
+{
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ int i;
+ u8 status;
+ u8 retry = 50;
+
+ while (retry--) {
+ /* Read the command response */
+ for (i = 0; i < response_len; i++) {
+ intel_sdvo_read_byte(intel_output, SDVO_I2C_RETURN_0 + i,
+ &((u8 *)response)[i]);
+ }
+
+ /* read the return status */
+ intel_sdvo_read_byte(intel_output, SDVO_I2C_CMD_STATUS, &status);
+
+ if (1) {
+ DRM_DEBUG("%s: R: ", SDVO_NAME(sdvo_priv));
+ for (i = 0; i < response_len; i++)
+ printk("%02X ", ((u8 *)response)[i]);
+ for (; i < 8; i++)
+ printk(" ");
+ if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP)
+ printk("(%s)", cmd_status_names[status]);
+ else
+ printk("(??? %d)", status);
+ printk("\n");
+ }
+
+ if (status != SDVO_CMD_STATUS_PENDING)
+ return status;
+
+ mdelay(50);
+ }
+
+ return status;
+}
+
+int intel_sdvo_get_pixel_multiplier(struct drm_display_mode *mode)
+{
+ if (mode->clock >= 100000)
+ return 1;
+ else if (mode->clock >= 50000)
+ return 2;
+ else
+ return 4;
+}
+
+/**
+ * Don't check status code from this as it switches the bus back to the
+ * SDVO chips which defeats the purpose of doing a bus switch in the first
+ * place.
+ */
+void intel_sdvo_set_control_bus_switch(struct intel_output *intel_output, u8 target)
+{
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_CONTROL_BUS_SWITCH, &target, 1);
+}
+
+static bool intel_sdvo_set_target_input(struct intel_output *intel_output, bool target_0, bool target_1)
+{
+ struct intel_sdvo_set_target_input_args targets = {0};
+ u8 status;
+
+ if (target_0 && target_1)
+ return SDVO_CMD_STATUS_NOTSUPP;
+
+ if (target_1)
+ targets.target_1 = 1;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_TARGET_INPUT, &targets,
+ sizeof(targets));
+
+ status = intel_sdvo_read_response(intel_output, NULL, 0);
+
+ return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+/**
+ * Return whether each input is trained.
+ *
+ * This function is making an assumption about the layout of the response,
+ * which should be checked against the docs.
+ */
+static bool intel_sdvo_get_trained_inputs(struct intel_output *intel_output, bool *input_1, bool *input_2)
+{
+ struct intel_sdvo_get_trained_inputs_response response;
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_TRAINED_INPUTS, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, &response, sizeof(response));
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ *input_1 = response.input0_trained;
+ *input_2 = response.input1_trained;
+ return true;
+}
+
+static bool intel_sdvo_get_active_outputs(struct intel_output *intel_output,
+ u16 *outputs)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_ACTIVE_OUTPUTS, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, outputs, sizeof(*outputs));
+
+ return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+static bool intel_sdvo_set_active_outputs(struct intel_output *intel_output,
+ u16 outputs)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_ACTIVE_OUTPUTS, &outputs,
+ sizeof(outputs));
+ status = intel_sdvo_read_response(intel_output, NULL, 0);
+ return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+static bool intel_sdvo_set_encoder_power_state(struct intel_output *intel_output,
+ int mode)
+{
+ u8 status, state = SDVO_ENCODER_STATE_ON;
+
+ switch (mode) {
+ case DPMSModeOn:
+ state = SDVO_ENCODER_STATE_ON;
+ break;
+ case DPMSModeStandby:
+ state = SDVO_ENCODER_STATE_STANDBY;
+ break;
+ case DPMSModeSuspend:
+ state = SDVO_ENCODER_STATE_SUSPEND;
+ break;
+ case DPMSModeOff:
+ state = SDVO_ENCODER_STATE_OFF;
+ break;
+ }
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_ENCODER_POWER_STATE, &state,
+ sizeof(state));
+ status = intel_sdvo_read_response(intel_output, NULL, 0);
+
+ return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+static bool intel_sdvo_get_input_pixel_clock_range(struct intel_output *intel_output,
+ int *clock_min,
+ int *clock_max)
+{
+ struct intel_sdvo_pixel_clock_range clocks;
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE,
+ NULL, 0);
+
+ status = intel_sdvo_read_response(intel_output, &clocks, sizeof(clocks));
+
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ /* Convert the values from units of 10 kHz to kHz. */
+ *clock_min = clocks.min * 10;
+ *clock_max = clocks.max * 10;
+
+ return true;
+}
+
+static bool intel_sdvo_set_target_output(struct intel_output *intel_output,
+ u16 outputs)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_TARGET_OUTPUT, &outputs,
+ sizeof(outputs));
+
+ status = intel_sdvo_read_response(intel_output, NULL, 0);
+ return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+static bool intel_sdvo_get_timing(struct intel_output *intel_output, u8 cmd,
+ struct intel_sdvo_dtd *dtd)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, cmd, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, &dtd->part1,
+ sizeof(dtd->part1));
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ intel_sdvo_write_cmd(intel_output, cmd + 1, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, &dtd->part2,
+ sizeof(dtd->part2));
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ return true;
+}
+
+static bool intel_sdvo_get_input_timing(struct intel_output *intel_output,
+ struct intel_sdvo_dtd *dtd)
+{
+ return intel_sdvo_get_timing(intel_output,
+ SDVO_CMD_GET_INPUT_TIMINGS_PART1, dtd);
+}
+
+static bool intel_sdvo_get_output_timing(struct intel_output *intel_output,
+ struct intel_sdvo_dtd *dtd)
+{
+ return intel_sdvo_get_timing(intel_output,
+ SDVO_CMD_GET_OUTPUT_TIMINGS_PART1, dtd);
+}
+
+static bool intel_sdvo_set_timing(struct intel_output *intel_output, u8 cmd,
+ struct intel_sdvo_dtd *dtd)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, cmd, &dtd->part1, sizeof(dtd->part1));
+ status = intel_sdvo_read_response(intel_output, NULL, 0);
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ intel_sdvo_write_cmd(intel_output, cmd + 1, &dtd->part2, sizeof(dtd->part2));
+ status = intel_sdvo_read_response(intel_output, NULL, 0);
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ return true;
+}
+
+static bool intel_sdvo_set_input_timing(struct intel_output *intel_output,
+ struct intel_sdvo_dtd *dtd)
+{
+ return intel_sdvo_set_timing(intel_output,
+ SDVO_CMD_SET_INPUT_TIMINGS_PART1, dtd);
+}
+
+static bool intel_sdvo_set_output_timing(struct intel_output *intel_output,
+ struct intel_sdvo_dtd *dtd)
+{
+ return intel_sdvo_set_timing(intel_output,
+ SDVO_CMD_SET_OUTPUT_TIMINGS_PART1, dtd);
+}
+
+#if 0
+static bool intel_sdvo_get_preferred_input_timing(struct intel_output *intel_output,
+ struct intel_sdvo_dtd *dtd)
+{
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1,
+ NULL, 0);
+
+ status = intel_sdvo_read_response(intel_output, &dtd->part1,
+ sizeof(dtd->part1));
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2,
+ NULL, 0);
+ status = intel_sdvo_read_response(intel_output, &dtd->part2,
+ sizeof(dtd->part2));
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ return true;
+}
+#endif
+
+static int intel_sdvo_get_clock_rate_mult(struct intel_output *intel_output)
+{
+ u8 response, status;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_CLOCK_RATE_MULT, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, &response, 1);
+
+ if (status != SDVO_CMD_STATUS_SUCCESS) {
+ DRM_DEBUG("Couldn't get SDVO clock rate multiplier\n");
+ return SDVO_CLOCK_RATE_MULT_1X;
+ } else {
+ DRM_DEBUG("Current clock rate multiplier: %d\n", response);
+ }
+
+ return response;
+}
+
+static bool intel_sdvo_set_clock_rate_mult(struct intel_output *intel_output, u8 val)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_CLOCK_RATE_MULT, &val, 1);
+ status = intel_sdvo_read_response(intel_output, NULL, 0);
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ return true;
+}
+
+static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ /* Make the CRTC code factor in the SDVO pixel multiplier. The SDVO
+ * device will be told of the multiplier during mode_set.
+ */
+ adjusted_mode->clock *= intel_sdvo_get_pixel_multiplier(mode);
+ return true;
+}
+
+static void intel_sdvo_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_crtc *crtc = encoder->crtc;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct intel_output *intel_output = enc_to_intel_output(encoder);
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ u16 width, height;
+ u16 h_blank_len, h_sync_len, v_blank_len, v_sync_len;
+ u16 h_sync_offset, v_sync_offset;
+ u32 sdvox;
+ struct intel_sdvo_dtd output_dtd;
+ int sdvo_pixel_multiply;
+
+ if (!mode)
+ return;
+
+ width = mode->crtc_hdisplay;
+ height = mode->crtc_vdisplay;
+
+ /* do some mode translations */
+ h_blank_len = mode->crtc_hblank_end - mode->crtc_hblank_start;
+ h_sync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
+
+ v_blank_len = mode->crtc_vblank_end - mode->crtc_vblank_start;
+ v_sync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
+
+ h_sync_offset = mode->crtc_hsync_start - mode->crtc_hblank_start;
+ v_sync_offset = mode->crtc_vsync_start - mode->crtc_vblank_start;
+
+ output_dtd.part1.clock = mode->clock / 10;
+ output_dtd.part1.h_active = width & 0xff;
+ output_dtd.part1.h_blank = h_blank_len & 0xff;
+ output_dtd.part1.h_high = (((width >> 8) & 0xf) << 4) |
+ ((h_blank_len >> 8) & 0xf);
+ output_dtd.part1.v_active = height & 0xff;
+ output_dtd.part1.v_blank = v_blank_len & 0xff;
+ output_dtd.part1.v_high = (((height >> 8) & 0xf) << 4) |
+ ((v_blank_len >> 8) & 0xf);
+
+ output_dtd.part2.h_sync_off = h_sync_offset;
+ output_dtd.part2.h_sync_width = h_sync_len & 0xff;
+ output_dtd.part2.v_sync_off_width = (v_sync_offset & 0xf) << 4 |
+ (v_sync_len & 0xf);
+ output_dtd.part2.sync_off_width_high = ((h_sync_offset & 0x300) >> 2) |
+ ((h_sync_len & 0x300) >> 4) | ((v_sync_offset & 0x30) >> 2) |
+ ((v_sync_len & 0x30) >> 4);
+
+ output_dtd.part2.dtd_flags = 0x18;
+ if (mode->flags & V_PHSYNC)
+ output_dtd.part2.dtd_flags |= 0x2;
+ if (mode->flags & V_PVSYNC)
+ output_dtd.part2.dtd_flags |= 0x4;
+
+ output_dtd.part2.sdvo_flags = 0;
+ output_dtd.part2.v_sync_off_high = v_sync_offset & 0xc0;
+ output_dtd.part2.reserved = 0;
+
+ /* Set the output timing to the screen */
+ intel_sdvo_set_target_output(intel_output, sdvo_priv->active_outputs);
+ intel_sdvo_set_output_timing(intel_output, &output_dtd);
+
+ /* Set the input timing to the screen. Assume always input 0. */
+ intel_sdvo_set_target_input(intel_output, true, false);
+
+ /* We would like to use i830_sdvo_create_preferred_input_timing() to
+ * provide the device with a timing it can support, if it supports that
+ * feature. However, presumably we would need to adjust the CRTC to
+ * output the preferred timing, and we don't support that currently.
+ */
+#if 0
+ success = intel_sdvo_create_preferred_input_timing(intel_output, clock,
+ width, height);
+ if (success) {
+ struct intel_sdvo_dtd *input_dtd;
+
+ intel_sdvo_get_preferred_input_timing(intel_output, &input_dtd);
+ intel_sdvo_set_input_timing(intel_output, &input_dtd);
+ }
+#else
+ intel_sdvo_set_input_timing(intel_output, &output_dtd);
+#endif
+
+ switch (intel_sdvo_get_pixel_multiplier(mode)) {
+ case 1:
+ intel_sdvo_set_clock_rate_mult(intel_output,
+ SDVO_CLOCK_RATE_MULT_1X);
+ break;
+ case 2:
+ intel_sdvo_set_clock_rate_mult(intel_output,
+ SDVO_CLOCK_RATE_MULT_2X);
+ break;
+ case 4:
+ intel_sdvo_set_clock_rate_mult(intel_output,
+ SDVO_CLOCK_RATE_MULT_4X);
+ break;
+ }
+
+ /* Set the SDVO control regs. */
+ if (0/*IS_I965GM(dev)*/) {
+ sdvox = SDVO_BORDER_ENABLE;
+ } else {
+ sdvox = I915_READ(sdvo_priv->output_device);
+ switch (sdvo_priv->output_device) {
+ case SDVOB:
+ sdvox &= SDVOB_PRESERVE_MASK;
+ break;
+ case SDVOC:
+ sdvox &= SDVOC_PRESERVE_MASK;
+ break;
+ }
+ sdvox |= (9 << 19) | SDVO_BORDER_ENABLE;
+ }
+ if (intel_crtc->pipe == 1)
+ sdvox |= SDVO_PIPE_B_SELECT;
+
+ sdvo_pixel_multiply = intel_sdvo_get_pixel_multiplier(mode);
+ if (IS_I965G(dev)) {
+ /* done in crtc_mode_set as the dpll_md reg must be written
+ early */
+ } else if (IS_I945G(dev) || IS_I945GM(dev)) {
+ /* done in crtc_mode_set as it lives inside the
+ dpll register */
+ } else {
+ sdvox |= (sdvo_pixel_multiply - 1) << SDVO_PORT_MULTIPLY_SHIFT;
+ }
+
+ intel_sdvo_write_sdvox(intel_output, sdvox);
+}
+
+static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_output *intel_output = enc_to_intel_output(encoder);
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ u32 temp;
+
+ if (mode != DPMSModeOn) {
+ intel_sdvo_set_active_outputs(intel_output, 0);
+ if (0)
+ intel_sdvo_set_encoder_power_state(intel_output, mode);
+
+ if (mode == DPMSModeOff) {
+ temp = I915_READ(sdvo_priv->output_device);
+ if ((temp & SDVO_ENABLE) != 0) {
+ intel_sdvo_write_sdvox(intel_output, temp & ~SDVO_ENABLE);
+ }
+ }
+ } else {
+ bool input1, input2;
+ int i;
+ u8 status;
+
+ temp = I915_READ(sdvo_priv->output_device);
+ if ((temp & SDVO_ENABLE) == 0)
+ intel_sdvo_write_sdvox(intel_output, temp | SDVO_ENABLE);
+ for (i = 0; i < 2; i++)
+ intel_wait_for_vblank(dev);
+
+ status = intel_sdvo_get_trained_inputs(intel_output, &input1,
+ &input2);
+
+
+ /* Warn if the device reported failure to sync.
+ * A lot of SDVO devices fail to notify of sync, but it's
+ * a given it the status is a success, we succeeded.
+ */
+ if (status == SDVO_CMD_STATUS_SUCCESS && !input1) {
+ DRM_DEBUG("First %s output reported failure to sync\n",
+ SDVO_NAME(sdvo_priv));
+ }
+
+ if (0)
+ intel_sdvo_set_encoder_power_state(intel_output, mode);
+ intel_sdvo_set_active_outputs(intel_output, sdvo_priv->active_outputs);
+ }
+ return;
+}
+
+static void intel_sdvo_save(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ int o;
+
+ sdvo_priv->save_sdvo_mult = intel_sdvo_get_clock_rate_mult(intel_output);
+ intel_sdvo_get_active_outputs(intel_output, &sdvo_priv->save_active_outputs);
+
+ if (sdvo_priv->caps.sdvo_inputs_mask & 0x1) {
+ intel_sdvo_set_target_input(intel_output, true, false);
+ intel_sdvo_get_input_timing(intel_output,
+ &sdvo_priv->save_input_dtd_1);
+ }
+
+ if (sdvo_priv->caps.sdvo_inputs_mask & 0x2) {
+ intel_sdvo_set_target_input(intel_output, false, true);
+ intel_sdvo_get_input_timing(intel_output,
+ &sdvo_priv->save_input_dtd_2);
+ }
+
+ for (o = SDVO_OUTPUT_FIRST; o <= SDVO_OUTPUT_LAST; o++)
+ {
+ u16 this_output = (1 << o);
+ if (sdvo_priv->caps.output_flags & this_output)
+ {
+ intel_sdvo_set_target_output(intel_output, this_output);
+ intel_sdvo_get_output_timing(intel_output,
+ &sdvo_priv->save_output_dtd[o]);
+ }
+ }
+
+ sdvo_priv->save_SDVOX = I915_READ(sdvo_priv->output_device);
+}
+
+static void intel_sdvo_restore(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ int o;
+ int i;
+ bool input1, input2;
+ u8 status;
+
+ intel_sdvo_set_active_outputs(intel_output, 0);
+
+ for (o = SDVO_OUTPUT_FIRST; o <= SDVO_OUTPUT_LAST; o++)
+ {
+ u16 this_output = (1 << o);
+ if (sdvo_priv->caps.output_flags & this_output) {
+ intel_sdvo_set_target_output(intel_output, this_output);
+ intel_sdvo_set_output_timing(intel_output, &sdvo_priv->save_output_dtd[o]);
+ }
+ }
+
+ if (sdvo_priv->caps.sdvo_inputs_mask & 0x1) {
+ intel_sdvo_set_target_input(intel_output, true, false);
+ intel_sdvo_set_input_timing(intel_output, &sdvo_priv->save_input_dtd_1);
+ }
+
+ if (sdvo_priv->caps.sdvo_inputs_mask & 0x2) {
+ intel_sdvo_set_target_input(intel_output, false, true);
+ intel_sdvo_set_input_timing(intel_output, &sdvo_priv->save_input_dtd_2);
+ }
+
+ intel_sdvo_set_clock_rate_mult(intel_output, sdvo_priv->save_sdvo_mult);
+
+ I915_WRITE(sdvo_priv->output_device, sdvo_priv->save_SDVOX);
+
+ if (sdvo_priv->save_SDVOX & SDVO_ENABLE)
+ {
+ for (i = 0; i < 2; i++)
+ intel_wait_for_vblank(dev);
+ status = intel_sdvo_get_trained_inputs(intel_output, &input1, &input2);
+ if (status == SDVO_CMD_STATUS_SUCCESS && !input1)
+ DRM_DEBUG("First %s output reported failure to sync\n",
+ SDVO_NAME(sdvo_priv));
+ }
+
+ intel_sdvo_set_active_outputs(intel_output, sdvo_priv->save_active_outputs);
+}
+
+static int intel_sdvo_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+
+ if (mode->flags & V_DBLSCAN)
+ return MODE_NO_DBLESCAN;
+
+ if (sdvo_priv->pixel_clock_min > mode->clock)
+ return MODE_CLOCK_LOW;
+
+ if (sdvo_priv->pixel_clock_max < mode->clock)
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+}
+
+static bool intel_sdvo_get_capabilities(struct intel_output *intel_output, struct intel_sdvo_caps *caps)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_DEVICE_CAPS, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, caps, sizeof(*caps));
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ return true;
+}
+
+struct drm_connector* intel_sdvo_find(struct drm_device *dev, int sdvoB)
+{
+ struct drm_connector *connector = NULL;
+ struct intel_output *iout = NULL;
+ struct intel_sdvo_priv *sdvo;
+
+ /* find the sdvo connector */
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ iout = to_intel_output(connector);
+
+ if (iout->type != INTEL_OUTPUT_SDVO)
+ continue;
+
+ sdvo = iout->dev_priv;
+
+ if (sdvo->output_device == SDVOB && sdvoB)
+ return connector;
+
+ if (sdvo->output_device == SDVOC && !sdvoB)
+ return connector;
+
+ }
+
+ return NULL;
+}
+
+int intel_sdvo_supports_hotplug(struct drm_connector *connector)
+{
+ u8 response[2];
+ u8 status;
+ struct intel_output *intel_output;
+ DRM_DEBUG("\n");
+
+ if (!connector)
+ return 0;
+
+ intel_output = to_intel_output(connector);
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_HOT_PLUG_SUPPORT, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, &response, 2);
+
+ if (response[0] !=0)
+ return 1;
+
+ return 0;
+}
+
+void intel_sdvo_set_hotplug(struct drm_connector *connector, int on)
+{
+ u8 response[2];
+ u8 status;
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_ACTIVE_HOT_PLUG, NULL, 0);
+ intel_sdvo_read_response(intel_output, &response, 2);
+
+ if (on) {
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_HOT_PLUG_SUPPORT, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, &response, 2);
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_ACTIVE_HOT_PLUG, &response, 2);
+ } else {
+ response[0] = 0;
+ response[1] = 0;
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_ACTIVE_HOT_PLUG, &response, 2);
+ }
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_ACTIVE_HOT_PLUG, NULL, 0);
+ intel_sdvo_read_response(intel_output, &response, 2);
+}
+
+static enum drm_connector_status intel_sdvo_detect(struct drm_connector *connector)
+{
+ u8 response[2];
+ u8 status;
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_ATTACHED_DISPLAYS, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, &response, 2);
+
+ DRM_DEBUG("SDVO response %d %d\n", response[0], response[1]);
+ if ((response[0] != 0) || (response[1] != 0))
+ return connector_status_connected;
+ else
+ return connector_status_disconnected;
+}
+
+static int intel_sdvo_get_modes(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ /* set the bus switch and get the modes */
+ intel_sdvo_set_control_bus_switch(intel_output, SDVO_CONTROL_BUS_DDC2);
+ intel_ddc_get_modes(intel_output);
+
+ if (list_empty(&connector->probed_modes))
+ return 0;
+ return 1;
+#if 0
+ /* Mac mini hack. On this device, I get DDC through the analog, which
+ * load-detects as disconnected. I fail to DDC through the SDVO DDC,
+ * but it does load-detect as connected. So, just steal the DDC bits
+ * from analog when we fail at finding it the right way.
+ */
+ /* TODO */
+ return NULL;
+
+ return NULL;
+#endif
+}
+
+static void intel_sdvo_destroy(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ if (intel_output->i2c_bus)
+ intel_i2c_destroy(intel_output->i2c_bus);
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
+ kfree(intel_output);
+}
+
+static const struct drm_encoder_helper_funcs intel_sdvo_helper_funcs = {
+ .dpms = intel_sdvo_dpms,
+ .mode_fixup = intel_sdvo_mode_fixup,
+ .prepare = intel_encoder_prepare,
+ .mode_set = intel_sdvo_mode_set,
+ .commit = intel_encoder_commit,
+};
+
+static const struct drm_connector_funcs intel_sdvo_connector_funcs = {
+ .save = intel_sdvo_save,
+ .restore = intel_sdvo_restore,
+ .detect = intel_sdvo_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = intel_sdvo_destroy,
+};
+
+static const struct drm_connector_helper_funcs intel_sdvo_connector_helper_funcs = {
+ .get_modes = intel_sdvo_get_modes,
+ .mode_valid = intel_sdvo_mode_valid,
+ .best_encoder = intel_best_encoder,
+};
+
+void intel_sdvo_enc_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs intel_sdvo_enc_funcs = {
+ .destroy = intel_sdvo_enc_destroy,
+};
+
+
+void intel_sdvo_init(struct drm_device *dev, int output_device)
+{
+ struct drm_connector *connector;
+ struct intel_output *intel_output;
+ struct intel_sdvo_priv *sdvo_priv;
+ struct intel_i2c_chan *i2cbus = NULL;
+ int connector_type;
+ u8 ch[0x40];
+ int i;
+ int encoder_type, output_id;
+
+ intel_output = kcalloc(sizeof(struct intel_output)+sizeof(struct intel_sdvo_priv), 1, GFP_KERNEL);
+ if (!intel_output) {
+ return;
+ }
+
+ connector = &intel_output->base;
+
+ drm_connector_init(dev, connector, &intel_sdvo_connector_funcs,
+ DRM_MODE_CONNECTOR_Unknown);
+ drm_connector_helper_add(connector, &intel_sdvo_connector_helper_funcs);
+ sdvo_priv = (struct intel_sdvo_priv *)(intel_output + 1);
+ intel_output->type = INTEL_OUTPUT_SDVO;
+
+ connector->interlace_allowed = 0;
+ connector->doublescan_allowed = 0;
+
+ /* setup the DDC bus. */
+ if (output_device == SDVOB)
+ i2cbus = intel_i2c_create(dev, GPIOE, "SDVOCTRL_E for SDVOB");
+ else
+ i2cbus = intel_i2c_create(dev, GPIOE, "SDVOCTRL_E for SDVOC");
+
+ if (!i2cbus)
+ goto err_connector;
+
+ sdvo_priv->i2c_bus = i2cbus;
+
+ if (output_device == SDVOB) {
+ output_id = 1;
+ sdvo_priv->i2c_bus->slave_addr = 0x38;
+ } else {
+ output_id = 2;
+ sdvo_priv->i2c_bus->slave_addr = 0x39;
+ }
+
+ sdvo_priv->output_device = output_device;
+ intel_output->i2c_bus = i2cbus;
+ intel_output->dev_priv = sdvo_priv;
+
+
+ /* Read the regs to test if we can talk to the device */
+ for (i = 0; i < 0x40; i++) {
+ if (!intel_sdvo_read_byte(intel_output, i, &ch[i])) {
+ DRM_DEBUG("No SDVO device found on SDVO%c\n",
+ output_device == SDVOB ? 'B' : 'C');
+ goto err_i2c;
+ }
+ }
+
+ intel_sdvo_get_capabilities(intel_output, &sdvo_priv->caps);
+
+ memset(&sdvo_priv->active_outputs, 0, sizeof(sdvo_priv->active_outputs));
+
+ /* TODO, CVBS, SVID, YPRPB & SCART outputs. */
+ if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB0)
+ {
+ sdvo_priv->active_outputs = SDVO_OUTPUT_RGB0;
+ connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+ encoder_type = DRM_MODE_ENCODER_DAC;
+ connector_type = DRM_MODE_CONNECTOR_VGA;
+ }
+ else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB1)
+ {
+ sdvo_priv->active_outputs = SDVO_OUTPUT_RGB1;
+ connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+ encoder_type = DRM_MODE_ENCODER_DAC;
+ connector_type = DRM_MODE_CONNECTOR_VGA;
+ }
+ else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0)
+ {
+ sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS0;
+ connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+ encoder_type = DRM_MODE_ENCODER_TMDS;
+ connector_type = DRM_MODE_CONNECTOR_DVID;
+ }
+ else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS1)
+ {
+ sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS1;
+ connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+ encoder_type = DRM_MODE_ENCODER_TMDS;
+ connector_type = DRM_MODE_CONNECTOR_DVID;
+ }
+ else
+ {
+ unsigned char bytes[2];
+
+ memcpy (bytes, &sdvo_priv->caps.output_flags, 2);
+ DRM_DEBUG("%s: No active RGB or TMDS outputs (0x%02x%02x)\n",
+ SDVO_NAME(sdvo_priv),
+ bytes[0], bytes[1]);
+ goto err_i2c;
+ }
+
+ drm_encoder_init(dev, &intel_output->enc, &intel_sdvo_enc_funcs, encoder_type);
+ drm_encoder_helper_add(&intel_output->enc, &intel_sdvo_helper_funcs);
+ connector->connector_type = connector_type;
+
+ drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc);
+ drm_sysfs_connector_add(connector);
+
+ /* Set the input timing to the screen. Assume always input 0. */
+ intel_sdvo_set_target_input(intel_output, true, false);
+
+ intel_sdvo_get_input_pixel_clock_range(intel_output,
+ &sdvo_priv->pixel_clock_min,
+ &sdvo_priv->pixel_clock_max);
+
+
+ DRM_DEBUG("%s device VID/DID: %02X:%02X.%02X, "
+ "clock range %dMHz - %dMHz, "
+ "input 1: %c, input 2: %c, "
+ "output 1: %c, output 2: %c\n",
+ SDVO_NAME(sdvo_priv),
+ sdvo_priv->caps.vendor_id, sdvo_priv->caps.device_id,
+ sdvo_priv->caps.device_rev_id,
+ sdvo_priv->pixel_clock_min / 1000,
+ sdvo_priv->pixel_clock_max / 1000,
+ (sdvo_priv->caps.sdvo_inputs_mask & 0x1) ? 'Y' : 'N',
+ (sdvo_priv->caps.sdvo_inputs_mask & 0x2) ? 'Y' : 'N',
+ /* check currently supported outputs */
+ sdvo_priv->caps.output_flags &
+ (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_RGB0) ? 'Y' : 'N',
+ sdvo_priv->caps.output_flags &
+ (SDVO_OUTPUT_TMDS1 | SDVO_OUTPUT_RGB1) ? 'Y' : 'N');
+
+ intel_output->ddc_bus = i2cbus;
+
+ return;
+
+err_i2c:
+ intel_i2c_destroy(intel_output->i2c_bus);
+err_connector:
+ drm_connector_cleanup(connector);
+ kfree(intel_output);
+
+ return;
+}
diff --git a/linux-core/intel_sdvo_regs.h b/linux-core/intel_sdvo_regs.h
new file mode 100644
index 00000000..a9d16711
--- /dev/null
+++ b/linux-core/intel_sdvo_regs.h
@@ -0,0 +1,328 @@
+/*
+ * Copyright © 2006-2007 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ */
+
+/**
+ * @file SDVO command definitions and structures.
+ */
+
+#define SDVO_OUTPUT_FIRST (0)
+#define SDVO_OUTPUT_TMDS0 (1 << 0)
+#define SDVO_OUTPUT_RGB0 (1 << 1)
+#define SDVO_OUTPUT_CVBS0 (1 << 2)
+#define SDVO_OUTPUT_SVID0 (1 << 3)
+#define SDVO_OUTPUT_YPRPB0 (1 << 4)
+#define SDVO_OUTPUT_SCART0 (1 << 5)
+#define SDVO_OUTPUT_LVDS0 (1 << 6)
+#define SDVO_OUTPUT_TMDS1 (1 << 8)
+#define SDVO_OUTPUT_RGB1 (1 << 9)
+#define SDVO_OUTPUT_CVBS1 (1 << 10)
+#define SDVO_OUTPUT_SVID1 (1 << 11)
+#define SDVO_OUTPUT_YPRPB1 (1 << 12)
+#define SDVO_OUTPUT_SCART1 (1 << 13)
+#define SDVO_OUTPUT_LVDS1 (1 << 14)
+#define SDVO_OUTPUT_LAST (14)
+
+struct intel_sdvo_caps {
+ u8 vendor_id;
+ u8 device_id;
+ u8 device_rev_id;
+ u8 sdvo_version_major;
+ u8 sdvo_version_minor;
+ unsigned int sdvo_inputs_mask:2;
+ unsigned int smooth_scaling:1;
+ unsigned int sharp_scaling:1;
+ unsigned int up_scaling:1;
+ unsigned int down_scaling:1;
+ unsigned int stall_support:1;
+ unsigned int pad:1;
+ u16 output_flags;
+} __attribute__((packed));
+
+/** This matches the EDID DTD structure, more or less */
+struct intel_sdvo_dtd {
+ struct {
+ u16 clock; /**< pixel clock, in 10kHz units */
+ u8 h_active; /**< lower 8 bits (pixels) */
+ u8 h_blank; /**< lower 8 bits (pixels) */
+ u8 h_high; /**< upper 4 bits each h_active, h_blank */
+ u8 v_active; /**< lower 8 bits (lines) */
+ u8 v_blank; /**< lower 8 bits (lines) */
+ u8 v_high; /**< upper 4 bits each v_active, v_blank */
+ } part1;
+
+ struct {
+ u8 h_sync_off; /**< lower 8 bits, from hblank start */
+ u8 h_sync_width; /**< lower 8 bits (pixels) */
+ /** lower 4 bits each vsync offset, vsync width */
+ u8 v_sync_off_width;
+ /**
+ * 2 high bits of hsync offset, 2 high bits of hsync width,
+ * bits 4-5 of vsync offset, and 2 high bits of vsync width.
+ */
+ u8 sync_off_width_high;
+ u8 dtd_flags;
+ u8 sdvo_flags;
+ /** bits 6-7 of vsync offset at bits 6-7 */
+ u8 v_sync_off_high;
+ u8 reserved;
+ } part2;
+} __attribute__((packed));
+
+struct intel_sdvo_pixel_clock_range {
+ u16 min; /**< pixel clock, in 10kHz units */
+ u16 max; /**< pixel clock, in 10kHz units */
+} __attribute__((packed));
+
+struct intel_sdvo_preferred_input_timing_args {
+ u16 clock;
+ u16 width;
+ u16 height;
+} __attribute__((packed));
+
+/* I2C registers for SDVO */
+#define SDVO_I2C_ARG_0 0x07
+#define SDVO_I2C_ARG_1 0x06
+#define SDVO_I2C_ARG_2 0x05
+#define SDVO_I2C_ARG_3 0x04
+#define SDVO_I2C_ARG_4 0x03
+#define SDVO_I2C_ARG_5 0x02
+#define SDVO_I2C_ARG_6 0x01
+#define SDVO_I2C_ARG_7 0x00
+#define SDVO_I2C_OPCODE 0x08
+#define SDVO_I2C_CMD_STATUS 0x09
+#define SDVO_I2C_RETURN_0 0x0a
+#define SDVO_I2C_RETURN_1 0x0b
+#define SDVO_I2C_RETURN_2 0x0c
+#define SDVO_I2C_RETURN_3 0x0d
+#define SDVO_I2C_RETURN_4 0x0e
+#define SDVO_I2C_RETURN_5 0x0f
+#define SDVO_I2C_RETURN_6 0x10
+#define SDVO_I2C_RETURN_7 0x11
+#define SDVO_I2C_VENDOR_BEGIN 0x20
+
+/* Status results */
+#define SDVO_CMD_STATUS_POWER_ON 0x0
+#define SDVO_CMD_STATUS_SUCCESS 0x1
+#define SDVO_CMD_STATUS_NOTSUPP 0x2
+#define SDVO_CMD_STATUS_INVALID_ARG 0x3
+#define SDVO_CMD_STATUS_PENDING 0x4
+#define SDVO_CMD_STATUS_TARGET_NOT_SPECIFIED 0x5
+#define SDVO_CMD_STATUS_SCALING_NOT_SUPP 0x6
+
+/* SDVO commands, argument/result registers */
+
+#define SDVO_CMD_RESET 0x01
+
+/** Returns a struct intel_sdvo_caps */
+#define SDVO_CMD_GET_DEVICE_CAPS 0x02
+
+#define SDVO_CMD_GET_FIRMWARE_REV 0x86
+# define SDVO_DEVICE_FIRMWARE_MINOR SDVO_I2C_RETURN_0
+# define SDVO_DEVICE_FIRMWARE_MAJOR SDVO_I2C_RETURN_1
+# define SDVO_DEVICE_FIRMWARE_PATCH SDVO_I2C_RETURN_2
+
+/**
+ * Reports which inputs are trained (managed to sync).
+ *
+ * Devices must have trained within 2 vsyncs of a mode change.
+ */
+#define SDVO_CMD_GET_TRAINED_INPUTS 0x03
+struct intel_sdvo_get_trained_inputs_response {
+ unsigned int input0_trained:1;
+ unsigned int input1_trained:1;
+ unsigned int pad:6;
+} __attribute__((packed));
+
+/** Returns a struct intel_sdvo_output_flags of active outputs. */
+#define SDVO_CMD_GET_ACTIVE_OUTPUTS 0x04
+
+/**
+ * Sets the current set of active outputs.
+ *
+ * Takes a struct intel_sdvo_output_flags. Must be preceded by a SET_IN_OUT_MAP
+ * on multi-output devices.
+ */
+#define SDVO_CMD_SET_ACTIVE_OUTPUTS 0x05
+
+/**
+ * Returns the current mapping of SDVO inputs to outputs on the device.
+ *
+ * Returns two struct intel_sdvo_output_flags structures.
+ */
+#define SDVO_CMD_GET_IN_OUT_MAP 0x06
+
+/**
+ * Sets the current mapping of SDVO inputs to outputs on the device.
+ *
+ * Takes two struct i380_sdvo_output_flags structures.
+ */
+#define SDVO_CMD_SET_IN_OUT_MAP 0x07
+
+/**
+ * Returns a struct intel_sdvo_output_flags of attached displays.
+ */
+#define SDVO_CMD_GET_ATTACHED_DISPLAYS 0x0b
+
+/**
+ * Returns a struct intel_sdvo_ouptut_flags of displays supporting hot plugging.
+ */
+#define SDVO_CMD_GET_HOT_PLUG_SUPPORT 0x0c
+
+/**
+ * Takes a struct intel_sdvo_output_flags.
+ */
+#define SDVO_CMD_SET_ACTIVE_HOT_PLUG 0x0d
+
+/**
+ * Returns a struct intel_sdvo_output_flags of displays with hot plug
+ * interrupts enabled.
+ */
+#define SDVO_CMD_GET_ACTIVE_HOT_PLUG 0x0e
+
+#define SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE 0x0f
+struct intel_sdvo_get_interrupt_event_source_response {
+ u16 interrupt_status;
+ unsigned int ambient_light_interrupt:1;
+ unsigned int pad:7;
+} __attribute__((packed));
+
+/**
+ * Selects which input is affected by future input commands.
+ *
+ * Commands affected include SET_INPUT_TIMINGS_PART[12],
+ * GET_INPUT_TIMINGS_PART[12], GET_PREFERRED_INPUT_TIMINGS_PART[12],
+ * GET_INPUT_PIXEL_CLOCK_RANGE, and CREATE_PREFERRED_INPUT_TIMINGS.
+ */
+#define SDVO_CMD_SET_TARGET_INPUT 0x10
+struct intel_sdvo_set_target_input_args {
+ unsigned int target_1:1;
+ unsigned int pad:7;
+} __attribute__((packed));
+
+/**
+ * Takes a struct intel_sdvo_output_flags of which outputs are targetted by
+ * future output commands.
+ *
+ * Affected commands inclue SET_OUTPUT_TIMINGS_PART[12],
+ * GET_OUTPUT_TIMINGS_PART[12], and GET_OUTPUT_PIXEL_CLOCK_RANGE.
+ */
+#define SDVO_CMD_SET_TARGET_OUTPUT 0x11
+
+#define SDVO_CMD_GET_INPUT_TIMINGS_PART1 0x12
+#define SDVO_CMD_GET_INPUT_TIMINGS_PART2 0x13
+#define SDVO_CMD_SET_INPUT_TIMINGS_PART1 0x14
+#define SDVO_CMD_SET_INPUT_TIMINGS_PART2 0x15
+#define SDVO_CMD_SET_OUTPUT_TIMINGS_PART1 0x16
+#define SDVO_CMD_SET_OUTPUT_TIMINGS_PART2 0x17
+#define SDVO_CMD_GET_OUTPUT_TIMINGS_PART1 0x18
+#define SDVO_CMD_GET_OUTPUT_TIMINGS_PART2 0x19
+/* Part 1 */
+# define SDVO_DTD_CLOCK_LOW SDVO_I2C_ARG_0
+# define SDVO_DTD_CLOCK_HIGH SDVO_I2C_ARG_1
+# define SDVO_DTD_H_ACTIVE SDVO_I2C_ARG_2
+# define SDVO_DTD_H_BLANK SDVO_I2C_ARG_3
+# define SDVO_DTD_H_HIGH SDVO_I2C_ARG_4
+# define SDVO_DTD_V_ACTIVE SDVO_I2C_ARG_5
+# define SDVO_DTD_V_BLANK SDVO_I2C_ARG_6
+# define SDVO_DTD_V_HIGH SDVO_I2C_ARG_7
+/* Part 2 */
+# define SDVO_DTD_HSYNC_OFF SDVO_I2C_ARG_0
+# define SDVO_DTD_HSYNC_WIDTH SDVO_I2C_ARG_1
+# define SDVO_DTD_VSYNC_OFF_WIDTH SDVO_I2C_ARG_2
+# define SDVO_DTD_SYNC_OFF_WIDTH_HIGH SDVO_I2C_ARG_3
+# define SDVO_DTD_DTD_FLAGS SDVO_I2C_ARG_4
+# define SDVO_DTD_DTD_FLAG_INTERLACED (1 << 7)
+# define SDVO_DTD_DTD_FLAG_STEREO_MASK (3 << 5)
+# define SDVO_DTD_DTD_FLAG_INPUT_MASK (3 << 3)
+# define SDVO_DTD_DTD_FLAG_SYNC_MASK (3 << 1)
+# define SDVO_DTD_SDVO_FLAS SDVO_I2C_ARG_5
+# define SDVO_DTD_SDVO_FLAG_STALL (1 << 7)
+# define SDVO_DTD_SDVO_FLAG_CENTERED (0 << 6)
+# define SDVO_DTD_SDVO_FLAG_UPPER_LEFT (1 << 6)
+# define SDVO_DTD_SDVO_FLAG_SCALING_MASK (3 << 4)
+# define SDVO_DTD_SDVO_FLAG_SCALING_NONE (0 << 4)
+# define SDVO_DTD_SDVO_FLAG_SCALING_SHARP (1 << 4)
+# define SDVO_DTD_SDVO_FLAG_SCALING_SMOOTH (2 << 4)
+# define SDVO_DTD_VSYNC_OFF_HIGH SDVO_I2C_ARG_6
+
+/**
+ * Generates a DTD based on the given width, height, and flags.
+ *
+ * This will be supported by any device supporting scaling or interlaced
+ * modes.
+ */
+#define SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING 0x1a
+# define SDVO_PREFERRED_INPUT_TIMING_CLOCK_LOW SDVO_I2C_ARG_0
+# define SDVO_PREFERRED_INPUT_TIMING_CLOCK_HIGH SDVO_I2C_ARG_1
+# define SDVO_PREFERRED_INPUT_TIMING_WIDTH_LOW SDVO_I2C_ARG_2
+# define SDVO_PREFERRED_INPUT_TIMING_WIDTH_HIGH SDVO_I2C_ARG_3
+# define SDVO_PREFERRED_INPUT_TIMING_HEIGHT_LOW SDVO_I2C_ARG_4
+# define SDVO_PREFERRED_INPUT_TIMING_HEIGHT_HIGH SDVO_I2C_ARG_5
+# define SDVO_PREFERRED_INPUT_TIMING_FLAGS SDVO_I2C_ARG_6
+# define SDVO_PREFERRED_INPUT_TIMING_FLAGS_INTERLACED (1 << 0)
+# define SDVO_PREFERRED_INPUT_TIMING_FLAGS_SCALED (1 << 1)
+
+#define SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1 0x1b
+#define SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2 0x1c
+
+/** Returns a struct intel_sdvo_pixel_clock_range */
+#define SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE 0x1d
+/** Returns a struct intel_sdvo_pixel_clock_range */
+#define SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE 0x1e
+
+/** Returns a byte bitfield containing SDVO_CLOCK_RATE_MULT_* flags */
+#define SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS 0x1f
+
+/** Returns a byte containing a SDVO_CLOCK_RATE_MULT_* flag */
+#define SDVO_CMD_GET_CLOCK_RATE_MULT 0x20
+/** Takes a byte containing a SDVO_CLOCK_RATE_MULT_* flag */
+#define SDVO_CMD_SET_CLOCK_RATE_MULT 0x21
+# define SDVO_CLOCK_RATE_MULT_1X (1 << 0)
+# define SDVO_CLOCK_RATE_MULT_2X (1 << 1)
+# define SDVO_CLOCK_RATE_MULT_4X (1 << 3)
+
+#define SDVO_CMD_GET_SUPPORTED_TV_FORMATS 0x27
+
+#define SDVO_CMD_GET_TV_FORMAT 0x28
+
+#define SDVO_CMD_SET_TV_FORMAT 0x29
+
+#define SDVO_CMD_GET_SUPPORTED_POWER_STATES 0x2a
+#define SDVO_CMD_GET_ENCODER_POWER_STATE 0x2b
+#define SDVO_CMD_SET_ENCODER_POWER_STATE 0x2c
+# define SDVO_ENCODER_STATE_ON (1 << 0)
+# define SDVO_ENCODER_STATE_STANDBY (1 << 1)
+# define SDVO_ENCODER_STATE_SUSPEND (1 << 2)
+# define SDVO_ENCODER_STATE_OFF (1 << 3)
+
+#define SDVO_CMD_SET_TV_RESOLUTION_SUPPORT 0x93
+
+#define SDVO_CMD_SET_CONTROL_BUS_SWITCH 0x7a
+# define SDVO_CONTROL_BUS_PROM 0x0
+# define SDVO_CONTROL_BUS_DDC1 0x1
+# define SDVO_CONTROL_BUS_DDC2 0x2
+# define SDVO_CONTROL_BUS_DDC3 0x3
+
diff --git a/linux-core/intel_tv.c b/linux-core/intel_tv.c
new file mode 100644
index 00000000..39f33d6c
--- /dev/null
+++ b/linux-core/intel_tv.c
@@ -0,0 +1,1734 @@
+/*
+ * Copyright © 2006 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ *
+ */
+
+/** @file
+ * Integrated TV-out support for the 915GM and 945GM.
+ */
+
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "drm_edid.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+enum tv_margin {
+ TV_MARGIN_LEFT, TV_MARGIN_TOP,
+ TV_MARGIN_RIGHT, TV_MARGIN_BOTTOM
+};
+
+/** Private structure for the integrated TV support */
+struct intel_tv_priv {
+ int type;
+ char *tv_format;
+ int margin[4];
+ u32 save_TV_H_CTL_1;
+ u32 save_TV_H_CTL_2;
+ u32 save_TV_H_CTL_3;
+ u32 save_TV_V_CTL_1;
+ u32 save_TV_V_CTL_2;
+ u32 save_TV_V_CTL_3;
+ u32 save_TV_V_CTL_4;
+ u32 save_TV_V_CTL_5;
+ u32 save_TV_V_CTL_6;
+ u32 save_TV_V_CTL_7;
+ u32 save_TV_SC_CTL_1, save_TV_SC_CTL_2, save_TV_SC_CTL_3;
+
+ u32 save_TV_CSC_Y;
+ u32 save_TV_CSC_Y2;
+ u32 save_TV_CSC_U;
+ u32 save_TV_CSC_U2;
+ u32 save_TV_CSC_V;
+ u32 save_TV_CSC_V2;
+ u32 save_TV_CLR_KNOBS;
+ u32 save_TV_CLR_LEVEL;
+ u32 save_TV_WIN_POS;
+ u32 save_TV_WIN_SIZE;
+ u32 save_TV_FILTER_CTL_1;
+ u32 save_TV_FILTER_CTL_2;
+ u32 save_TV_FILTER_CTL_3;
+
+ u32 save_TV_H_LUMA[60];
+ u32 save_TV_H_CHROMA[60];
+ u32 save_TV_V_LUMA[43];
+ u32 save_TV_V_CHROMA[43];
+
+ u32 save_TV_DAC;
+ u32 save_TV_CTL;
+};
+
+struct video_levels {
+ int blank, black, burst;
+};
+
+struct color_conversion {
+ u16 ry, gy, by, ay;
+ u16 ru, gu, bu, au;
+ u16 rv, gv, bv, av;
+};
+
+static const u32 filter_table[] = {
+ 0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140,
+ 0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000,
+ 0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160,
+ 0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780,
+ 0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50,
+ 0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20,
+ 0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0,
+ 0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0,
+ 0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020,
+ 0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140,
+ 0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20,
+ 0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848,
+ 0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900,
+ 0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080,
+ 0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060,
+ 0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140,
+ 0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000,
+ 0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160,
+ 0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780,
+ 0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50,
+ 0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20,
+ 0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0,
+ 0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0,
+ 0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020,
+ 0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140,
+ 0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20,
+ 0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848,
+ 0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900,
+ 0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080,
+ 0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060,
+ 0x36403000, 0x2D002CC0, 0x30003640, 0x2D0036C0,
+ 0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540,
+ 0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00,
+ 0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000,
+ 0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00,
+ 0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40,
+ 0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240,
+ 0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00,
+ 0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0,
+ 0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840,
+ 0x28003100, 0x28002F00, 0x00003100, 0x36403000,
+ 0x2D002CC0, 0x30003640, 0x2D0036C0,
+ 0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540,
+ 0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00,
+ 0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000,
+ 0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00,
+ 0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40,
+ 0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240,
+ 0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00,
+ 0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0,
+ 0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840,
+ 0x28003100, 0x28002F00, 0x00003100,
+};
+
+/*
+ * Color conversion values have 3 separate fixed point formats:
+ *
+ * 10 bit fields (ay, au)
+ * 1.9 fixed point (b.bbbbbbbbb)
+ * 11 bit fields (ry, by, ru, gu, gv)
+ * exp.mantissa (ee.mmmmmmmmm)
+ * ee = 00 = 10^-1 (0.mmmmmmmmm)
+ * ee = 01 = 10^-2 (0.0mmmmmmmmm)
+ * ee = 10 = 10^-3 (0.00mmmmmmmmm)
+ * ee = 11 = 10^-4 (0.000mmmmmmmmm)
+ * 12 bit fields (gy, rv, bu)
+ * exp.mantissa (eee.mmmmmmmmm)
+ * eee = 000 = 10^-1 (0.mmmmmmmmm)
+ * eee = 001 = 10^-2 (0.0mmmmmmmmm)
+ * eee = 010 = 10^-3 (0.00mmmmmmmmm)
+ * eee = 011 = 10^-4 (0.000mmmmmmmmm)
+ * eee = 100 = reserved
+ * eee = 101 = reserved
+ * eee = 110 = reserved
+ * eee = 111 = 10^0 (m.mmmmmmmm) (only usable for 1.0 representation)
+ *
+ * Saturation and contrast are 8 bits, with their own representation:
+ * 8 bit field (saturation, contrast)
+ * exp.mantissa (ee.mmmmmm)
+ * ee = 00 = 10^-1 (0.mmmmmm)
+ * ee = 01 = 10^0 (m.mmmmm)
+ * ee = 10 = 10^1 (mm.mmmm)
+ * ee = 11 = 10^2 (mmm.mmm)
+ *
+ * Simple conversion function:
+ *
+ * static u32
+ * float_to_csc_11(float f)
+ * {
+ * u32 exp;
+ * u32 mant;
+ * u32 ret;
+ *
+ * if (f < 0)
+ * f = -f;
+ *
+ * if (f >= 1) {
+ * exp = 0x7;
+ * mant = 1 << 8;
+ * } else {
+ * for (exp = 0; exp < 3 && f < 0.5; exp++)
+ * f *= 2.0;
+ * mant = (f * (1 << 9) + 0.5);
+ * if (mant >= (1 << 9))
+ * mant = (1 << 9) - 1;
+ * }
+ * ret = (exp << 9) | mant;
+ * return ret;
+ * }
+ */
+
+/*
+ * Behold, magic numbers! If we plant them they might grow a big
+ * s-video cable to the sky... or something.
+ *
+ * Pre-converted to appropriate hex value.
+ */
+
+/*
+ * PAL & NTSC values for composite & s-video connections
+ */
+static const struct color_conversion ntsc_m_csc_composite = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
+ .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0f00,
+ .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0f00,
+};
+
+static const struct video_levels ntsc_m_levels_composite = {
+ .blank = 225, .black = 267, .burst = 113,
+};
+
+static const struct color_conversion ntsc_m_csc_svideo = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0134,
+ .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0f00,
+ .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0f00,
+};
+
+static const struct video_levels ntsc_m_levels_svideo = {
+ .blank = 266, .black = 316, .burst = 133,
+};
+
+static const struct color_conversion ntsc_j_csc_composite = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0119,
+ .ru = 0x074c, .gu = 0x0546, .bu = 0x05ec, .au = 0x0f00,
+ .rv = 0x035a, .gv = 0x0322, .bv = 0x06e1, .av = 0x0f00,
+};
+
+static const struct video_levels ntsc_j_levels_composite = {
+ .blank = 225, .black = 225, .burst = 113,
+};
+
+static const struct color_conversion ntsc_j_csc_svideo = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x014c,
+ .ru = 0x0788, .gu = 0x0581, .bu = 0x0322, .au = 0x0f00,
+ .rv = 0x0399, .gv = 0x0356, .bv = 0x070a, .av = 0x0f00,
+};
+
+static const struct video_levels ntsc_j_levels_svideo = {
+ .blank = 266, .black = 266, .burst = 133,
+};
+
+static const struct color_conversion pal_csc_composite = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0113,
+ .ru = 0x0745, .gu = 0x053f, .bu = 0x05e1, .au = 0x0f00,
+ .rv = 0x0353, .gv = 0x031c, .bv = 0x06dc, .av = 0x0f00,
+};
+
+static const struct video_levels pal_levels_composite = {
+ .blank = 237, .black = 237, .burst = 118,
+};
+
+static const struct color_conversion pal_csc_svideo = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145,
+ .ru = 0x0780, .gu = 0x0579, .bu = 0x031c, .au = 0x0f00,
+ .rv = 0x0390, .gv = 0x034f, .bv = 0x0705, .av = 0x0f00,
+};
+
+static const struct video_levels pal_levels_svideo = {
+ .blank = 280, .black = 280, .burst = 139,
+};
+
+static const struct color_conversion pal_m_csc_composite = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
+ .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0f00,
+ .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0f00,
+};
+
+static const struct video_levels pal_m_levels_composite = {
+ .blank = 225, .black = 267, .burst = 113,
+};
+
+static const struct color_conversion pal_m_csc_svideo = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0134,
+ .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0f00,
+ .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0f00,
+};
+
+static const struct video_levels pal_m_levels_svideo = {
+ .blank = 266, .black = 316, .burst = 133,
+};
+
+static const struct color_conversion pal_n_csc_composite = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
+ .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0f00,
+ .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0f00,
+};
+
+static const struct video_levels pal_n_levels_composite = {
+ .blank = 225, .black = 267, .burst = 118,
+};
+
+static const struct color_conversion pal_n_csc_svideo = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0134,
+ .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0f00,
+ .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0f00,
+};
+
+static const struct video_levels pal_n_levels_svideo = {
+ .blank = 266, .black = 316, .burst = 139,
+};
+
+/*
+ * Component connections
+ */
+static const struct color_conversion sdtv_csc_yprpb = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0146,
+ .ru = 0x0559, .gu = 0x0353, .bu = 0x0100, .au = 0x0f00,
+ .rv = 0x0100, .gv = 0x03ad, .bv = 0x074d, .av = 0x0f00,
+};
+
+static const struct color_conversion sdtv_csc_rgb = {
+ .ry = 0x0000, .gy = 0x0f00, .by = 0x0000, .ay = 0x0166,
+ .ru = 0x0000, .gu = 0x0000, .bu = 0x0f00, .au = 0x0166,
+ .rv = 0x0f00, .gv = 0x0000, .bv = 0x0000, .av = 0x0166,
+};
+
+static const struct color_conversion hdtv_csc_yprpb = {
+ .ry = 0x05b3, .gy = 0x016e, .by = 0x0728, .ay = 0x0146,
+ .ru = 0x07d5, .gu = 0x038b, .bu = 0x0100, .au = 0x0f00,
+ .rv = 0x0100, .gv = 0x03d1, .bv = 0x06bc, .av = 0x0f00,
+};
+
+static const struct color_conversion hdtv_csc_rgb = {
+ .ry = 0x0000, .gy = 0x0f00, .by = 0x0000, .ay = 0x0166,
+ .ru = 0x0000, .gu = 0x0000, .bu = 0x0f00, .au = 0x0166,
+ .rv = 0x0f00, .gv = 0x0000, .bv = 0x0000, .av = 0x0166,
+};
+
+static const struct video_levels component_levels = {
+ .blank = 279, .black = 279, .burst = 0,
+};
+
+
+struct tv_mode {
+ char *name;
+ int clock;
+ int refresh; /* in millihertz (for precision) */
+ u32 oversample;
+ int hsync_end, hblank_start, hblank_end, htotal;
+ bool progressive, trilevel_sync, component_only;
+ int vsync_start_f1, vsync_start_f2, vsync_len;
+ bool veq_ena;
+ int veq_start_f1, veq_start_f2, veq_len;
+ int vi_end_f1, vi_end_f2, nbr_end;
+ bool burst_ena;
+ int hburst_start, hburst_len;
+ int vburst_start_f1, vburst_end_f1;
+ int vburst_start_f2, vburst_end_f2;
+ int vburst_start_f3, vburst_end_f3;
+ int vburst_start_f4, vburst_end_f4;
+ /*
+ * subcarrier programming
+ */
+ int dda2_size, dda3_size, dda1_inc, dda2_inc, dda3_inc;
+ u32 sc_reset;
+ bool pal_burst;
+ /*
+ * blank/black levels
+ */
+ const struct video_levels *composite_levels, *svideo_levels;
+ const struct color_conversion *composite_color, *svideo_color;
+ const u32 *filter_table;
+ int max_srcw;
+};
+
+
+/*
+ * Sub carrier DDA
+ *
+ * I think this works as follows:
+ *
+ * subcarrier freq = pixel_clock * (dda1_inc + dda2_inc / dda2_size) / 4096
+ *
+ * Presumably, when dda3 is added in, it gets to adjust the dda2_inc value
+ *
+ * So,
+ * dda1_ideal = subcarrier/pixel * 4096
+ * dda1_inc = floor (dda1_ideal)
+ * dda2 = dda1_ideal - dda1_inc
+ *
+ * then pick a ratio for dda2 that gives the closest approximation. If
+ * you can't get close enough, you can play with dda3 as well. This
+ * seems likely to happen when dda2 is small as the jumps would be larger
+ *
+ * To invert this,
+ *
+ * pixel_clock = subcarrier * 4096 / (dda1_inc + dda2_inc / dda2_size)
+ *
+ * The constants below were all computed using a 107.520MHz clock
+ */
+
+/**
+ * Register programming values for TV modes.
+ *
+ * These values account for -1s required.
+ */
+
+const static struct tv_mode tv_modes[] = {
+ {
+ .name = "NTSC-M",
+ .clock = 107520,
+ .refresh = 29970,
+ .oversample = TV_OVERSAMPLE_8X,
+ .component_only = 0,
+ /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
+
+ .hsync_end = 64, .hblank_end = 124,
+ .hblank_start = 836, .htotal = 857,
+
+ .progressive = FALSE, .trilevel_sync = FALSE,
+
+ .vsync_start_f1 = 6, .vsync_start_f2 = 7,
+ .vsync_len = 6,
+
+ .veq_ena = TRUE, .veq_start_f1 = 0,
+ .veq_start_f2 = 1, .veq_len = 18,
+
+ .vi_end_f1 = 20, .vi_end_f2 = 21,
+ .nbr_end = 240,
+
+ .burst_ena = TRUE,
+ .hburst_start = 72, .hburst_len = 34,
+ .vburst_start_f1 = 9, .vburst_end_f1 = 240,
+ .vburst_start_f2 = 10, .vburst_end_f2 = 240,
+ .vburst_start_f3 = 9, .vburst_end_f3 = 240,
+ .vburst_start_f4 = 10, .vburst_end_f4 = 240,
+
+ /* desired 3.5800000 actual 3.5800000 clock 107.52 */
+ .dda1_inc = 136,
+ .dda2_inc = 7624, .dda2_size = 20013,
+ .dda3_inc = 0, .dda3_size = 0,
+ .sc_reset = TV_SC_RESET_EVERY_4,
+ .pal_burst = FALSE,
+
+ .composite_levels = &ntsc_m_levels_composite,
+ .composite_color = &ntsc_m_csc_composite,
+ .svideo_levels = &ntsc_m_levels_svideo,
+ .svideo_color = &ntsc_m_csc_svideo,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "NTSC-443",
+ .clock = 107520,
+ .refresh = 29970,
+ .oversample = TV_OVERSAMPLE_8X,
+ .component_only = 0,
+ /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 4.43MHz */
+ .hsync_end = 64, .hblank_end = 124,
+ .hblank_start = 836, .htotal = 857,
+
+ .progressive = FALSE, .trilevel_sync = FALSE,
+
+ .vsync_start_f1 = 6, .vsync_start_f2 = 7,
+ .vsync_len = 6,
+
+ .veq_ena = TRUE, .veq_start_f1 = 0,
+ .veq_start_f2 = 1, .veq_len = 18,
+
+ .vi_end_f1 = 20, .vi_end_f2 = 21,
+ .nbr_end = 240,
+
+ .burst_ena = 8,
+ .hburst_start = 72, .hburst_len = 34,
+ .vburst_start_f1 = 9, .vburst_end_f1 = 240,
+ .vburst_start_f2 = 10, .vburst_end_f2 = 240,
+ .vburst_start_f3 = 9, .vburst_end_f3 = 240,
+ .vburst_start_f4 = 10, .vburst_end_f4 = 240,
+
+ /* desired 4.4336180 actual 4.4336180 clock 107.52 */
+ .dda1_inc = 168,
+ .dda2_inc = 18557, .dda2_size = 20625,
+ .dda3_inc = 0, .dda3_size = 0,
+ .sc_reset = TV_SC_RESET_EVERY_8,
+ .pal_burst = TRUE,
+
+ .composite_levels = &ntsc_m_levels_composite,
+ .composite_color = &ntsc_m_csc_composite,
+ .svideo_levels = &ntsc_m_levels_svideo,
+ .svideo_color = &ntsc_m_csc_svideo,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "NTSC-J",
+ .clock = 107520,
+ .refresh = 29970,
+ .oversample = TV_OVERSAMPLE_8X,
+ .component_only = 0,
+
+ /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
+ .hsync_end = 64, .hblank_end = 124,
+ .hblank_start = 836, .htotal = 857,
+
+ .progressive = FALSE, .trilevel_sync = FALSE,
+
+ .vsync_start_f1 = 6, .vsync_start_f2 = 7,
+ .vsync_len = 6,
+
+ .veq_ena = TRUE, .veq_start_f1 = 0,
+ .veq_start_f2 = 1, .veq_len = 18,
+
+ .vi_end_f1 = 20, .vi_end_f2 = 21,
+ .nbr_end = 240,
+
+ .burst_ena = TRUE,
+ .hburst_start = 72, .hburst_len = 34,
+ .vburst_start_f1 = 9, .vburst_end_f1 = 240,
+ .vburst_start_f2 = 10, .vburst_end_f2 = 240,
+ .vburst_start_f3 = 9, .vburst_end_f3 = 240,
+ .vburst_start_f4 = 10, .vburst_end_f4 = 240,
+
+ /* desired 3.5800000 actual 3.5800000 clock 107.52 */
+ .dda1_inc = 136,
+ .dda2_inc = 7624, .dda2_size = 20013,
+ .dda3_inc = 0, .dda3_size = 0,
+ .sc_reset = TV_SC_RESET_EVERY_4,
+ .pal_burst = FALSE,
+
+ .composite_levels = &ntsc_j_levels_composite,
+ .composite_color = &ntsc_j_csc_composite,
+ .svideo_levels = &ntsc_j_levels_svideo,
+ .svideo_color = &ntsc_j_csc_svideo,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "PAL-M",
+ .clock = 107520,
+ .refresh = 29970,
+ .oversample = TV_OVERSAMPLE_8X,
+ .component_only = 0,
+
+ /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
+ .hsync_end = 64, .hblank_end = 124,
+ .hblank_start = 836, .htotal = 857,
+
+ .progressive = FALSE, .trilevel_sync = FALSE,
+
+ .vsync_start_f1 = 6, .vsync_start_f2 = 7,
+ .vsync_len = 6,
+
+ .veq_ena = TRUE, .veq_start_f1 = 0,
+ .veq_start_f2 = 1, .veq_len = 18,
+
+ .vi_end_f1 = 20, .vi_end_f2 = 21,
+ .nbr_end = 240,
+
+ .burst_ena = TRUE,
+ .hburst_start = 72, .hburst_len = 34,
+ .vburst_start_f1 = 9, .vburst_end_f1 = 240,
+ .vburst_start_f2 = 10, .vburst_end_f2 = 240,
+ .vburst_start_f3 = 9, .vburst_end_f3 = 240,
+ .vburst_start_f4 = 10, .vburst_end_f4 = 240,
+
+ /* desired 3.5800000 actual 3.5800000 clock 107.52 */
+ .dda1_inc = 136,
+ .dda2_inc = 7624, .dda2_size = 20013,
+ .dda3_inc = 0, .dda3_size = 0,
+ .sc_reset = TV_SC_RESET_EVERY_4,
+ .pal_burst = FALSE,
+
+ .composite_levels = &pal_m_levels_composite,
+ .composite_color = &pal_m_csc_composite,
+ .svideo_levels = &pal_m_levels_svideo,
+ .svideo_color = &pal_m_csc_svideo,
+
+ .filter_table = filter_table,
+ },
+ {
+ /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
+ .name = "PAL-N",
+ .clock = 107520,
+ .refresh = 25000,
+ .oversample = TV_OVERSAMPLE_8X,
+ .component_only = 0,
+
+ .hsync_end = 64, .hblank_end = 128,
+ .hblank_start = 844, .htotal = 863,
+
+ .progressive = FALSE, .trilevel_sync = FALSE,
+
+
+ .vsync_start_f1 = 6, .vsync_start_f2 = 7,
+ .vsync_len = 6,
+
+ .veq_ena = TRUE, .veq_start_f1 = 0,
+ .veq_start_f2 = 1, .veq_len = 18,
+
+ .vi_end_f1 = 24, .vi_end_f2 = 25,
+ .nbr_end = 286,
+
+ .burst_ena = TRUE,
+ .hburst_start = 73, .hburst_len = 34,
+ .vburst_start_f1 = 8, .vburst_end_f1 = 285,
+ .vburst_start_f2 = 8, .vburst_end_f2 = 286,
+ .vburst_start_f3 = 9, .vburst_end_f3 = 286,
+ .vburst_start_f4 = 9, .vburst_end_f4 = 285,
+
+
+ /* desired 4.4336180 actual 4.4336180 clock 107.52 */
+ .dda1_inc = 168,
+ .dda2_inc = 18557, .dda2_size = 20625,
+ .dda3_inc = 0, .dda3_size = 0,
+ .sc_reset = TV_SC_RESET_EVERY_8,
+ .pal_burst = TRUE,
+
+ .composite_levels = &pal_n_levels_composite,
+ .composite_color = &pal_n_csc_composite,
+ .svideo_levels = &pal_n_levels_svideo,
+ .svideo_color = &pal_n_csc_svideo,
+
+ .filter_table = filter_table,
+ },
+ {
+ /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
+ .name = "PAL",
+ .clock = 107520,
+ .refresh = 25000,
+ .oversample = TV_OVERSAMPLE_8X,
+ .component_only = 0,
+
+ .hsync_end = 64, .hblank_end = 128,
+ .hblank_start = 844, .htotal = 863,
+
+ .progressive = FALSE, .trilevel_sync = FALSE,
+
+ .vsync_start_f1 = 5, .vsync_start_f2 = 6,
+ .vsync_len = 5,
+
+ .veq_ena = TRUE, .veq_start_f1 = 0,
+ .veq_start_f2 = 1, .veq_len = 15,
+
+ .vi_end_f1 = 24, .vi_end_f2 = 25,
+ .nbr_end = 286,
+
+ .burst_ena = TRUE,
+ .hburst_start = 73, .hburst_len = 32,
+ .vburst_start_f1 = 8, .vburst_end_f1 = 285,
+ .vburst_start_f2 = 8, .vburst_end_f2 = 286,
+ .vburst_start_f3 = 9, .vburst_end_f3 = 286,
+ .vburst_start_f4 = 9, .vburst_end_f4 = 285,
+
+ /* desired 4.4336180 actual 4.4336180 clock 107.52 */
+ .dda1_inc = 168,
+ .dda2_inc = 18557, .dda2_size = 20625,
+ .dda3_inc = 0, .dda3_size = 0,
+ .sc_reset = TV_SC_RESET_EVERY_8,
+ .pal_burst = TRUE,
+
+ .composite_levels = &pal_levels_composite,
+ .composite_color = &pal_csc_composite,
+ .svideo_levels = &pal_levels_svideo,
+ .svideo_color = &pal_csc_svideo,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "480p@59.94Hz",
+ .clock = 107520,
+ .refresh = 59940,
+ .oversample = TV_OVERSAMPLE_4X,
+ .component_only = 1,
+
+ .hsync_end = 64, .hblank_end = 122,
+ .hblank_start = 842, .htotal = 857,
+
+ .progressive = TRUE,.trilevel_sync = FALSE,
+
+ .vsync_start_f1 = 12, .vsync_start_f2 = 12,
+ .vsync_len = 12,
+
+ .veq_ena = FALSE,
+
+ .vi_end_f1 = 44, .vi_end_f2 = 44,
+ .nbr_end = 496,
+
+ .burst_ena = FALSE,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "480p@60Hz",
+ .clock = 107520,
+ .refresh = 60000,
+ .oversample = TV_OVERSAMPLE_4X,
+ .component_only = 1,
+
+ .hsync_end = 64, .hblank_end = 122,
+ .hblank_start = 842, .htotal = 856,
+
+ .progressive = TRUE,.trilevel_sync = FALSE,
+
+ .vsync_start_f1 = 12, .vsync_start_f2 = 12,
+ .vsync_len = 12,
+
+ .veq_ena = FALSE,
+
+ .vi_end_f1 = 44, .vi_end_f2 = 44,
+ .nbr_end = 496,
+
+ .burst_ena = FALSE,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "576p",
+ .clock = 107520,
+ .refresh = 50000,
+ .oversample = TV_OVERSAMPLE_4X,
+ .component_only = 1,
+
+ .hsync_end = 64, .hblank_end = 139,
+ .hblank_start = 859, .htotal = 863,
+
+ .progressive = TRUE, .trilevel_sync = FALSE,
+
+ .vsync_start_f1 = 10, .vsync_start_f2 = 10,
+ .vsync_len = 10,
+
+ .veq_ena = FALSE,
+
+ .vi_end_f1 = 48, .vi_end_f2 = 48,
+ .nbr_end = 575,
+
+ .burst_ena = FALSE,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "720p@60Hz",
+ .clock = 148800,
+ .refresh = 60000,
+ .oversample = TV_OVERSAMPLE_2X,
+ .component_only = 1,
+
+ .hsync_end = 80, .hblank_end = 300,
+ .hblank_start = 1580, .htotal = 1649,
+
+ .progressive = TRUE, .trilevel_sync = TRUE,
+
+ .vsync_start_f1 = 10, .vsync_start_f2 = 10,
+ .vsync_len = 10,
+
+ .veq_ena = FALSE,
+
+ .vi_end_f1 = 29, .vi_end_f2 = 29,
+ .nbr_end = 719,
+
+ .burst_ena = FALSE,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "720p@59.94Hz",
+ .clock = 148800,
+ .refresh = 59940,
+ .oversample = TV_OVERSAMPLE_2X,
+ .component_only = 1,
+
+ .hsync_end = 80, .hblank_end = 300,
+ .hblank_start = 1580, .htotal = 1651,
+
+ .progressive = TRUE, .trilevel_sync = TRUE,
+
+ .vsync_start_f1 = 10, .vsync_start_f2 = 10,
+ .vsync_len = 10,
+
+ .veq_ena = FALSE,
+
+ .vi_end_f1 = 29, .vi_end_f2 = 29,
+ .nbr_end = 719,
+
+ .burst_ena = FALSE,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "720p@50Hz",
+ .clock = 148800,
+ .refresh = 50000,
+ .oversample = TV_OVERSAMPLE_2X,
+ .component_only = 1,
+
+ .hsync_end = 80, .hblank_end = 300,
+ .hblank_start = 1580, .htotal = 1979,
+
+ .progressive = TRUE, .trilevel_sync = TRUE,
+
+ .vsync_start_f1 = 10, .vsync_start_f2 = 10,
+ .vsync_len = 10,
+
+ .veq_ena = FALSE,
+
+ .vi_end_f1 = 29, .vi_end_f2 = 29,
+ .nbr_end = 719,
+
+ .burst_ena = FALSE,
+
+ .filter_table = filter_table,
+ .max_srcw = 800
+ },
+ {
+ .name = "1080i@50Hz",
+ .clock = 148800,
+ .refresh = 25000,
+ .oversample = TV_OVERSAMPLE_2X,
+ .component_only = 1,
+
+ .hsync_end = 88, .hblank_end = 235,
+ .hblank_start = 2155, .htotal = 2639,
+
+ .progressive = FALSE, .trilevel_sync = TRUE,
+
+ .vsync_start_f1 = 4, .vsync_start_f2 = 5,
+ .vsync_len = 10,
+
+ .veq_ena = TRUE, .veq_start_f1 = 4,
+ .veq_start_f2 = 4, .veq_len = 10,
+
+
+ .vi_end_f1 = 21, .vi_end_f2 = 22,
+ .nbr_end = 539,
+
+ .burst_ena = FALSE,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "1080i@60Hz",
+ .clock = 148800,
+ .refresh = 30000,
+ .oversample = TV_OVERSAMPLE_2X,
+ .component_only = 1,
+
+ .hsync_end = 88, .hblank_end = 235,
+ .hblank_start = 2155, .htotal = 2199,
+
+ .progressive = FALSE, .trilevel_sync = TRUE,
+
+ .vsync_start_f1 = 4, .vsync_start_f2 = 5,
+ .vsync_len = 10,
+
+ .veq_ena = TRUE, .veq_start_f1 = 4,
+ .veq_start_f2 = 4, .veq_len = 10,
+
+
+ .vi_end_f1 = 21, .vi_end_f2 = 22,
+ .nbr_end = 539,
+
+ .burst_ena = FALSE,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "1080i@59.94Hz",
+ .clock = 148800,
+ .refresh = 29970,
+ .oversample = TV_OVERSAMPLE_2X,
+ .component_only = 1,
+
+ .hsync_end = 88, .hblank_end = 235,
+ .hblank_start = 2155, .htotal = 2200,
+
+ .progressive = FALSE, .trilevel_sync = TRUE,
+
+ .vsync_start_f1 = 4, .vsync_start_f2 = 5,
+ .vsync_len = 10,
+
+ .veq_ena = TRUE, .veq_start_f1 = 4,
+ .veq_start_f2 = 4, .veq_len = 10,
+
+
+ .vi_end_f1 = 21, .vi_end_f2 = 22,
+ .nbr_end = 539,
+
+ .burst_ena = FALSE,
+
+ .filter_table = filter_table,
+ },
+};
+
+#define NUM_TV_MODES sizeof(tv_modes) / sizeof (tv_modes[0])
+
+static void
+intel_tv_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ switch(mode) {
+ case DPMSModeOn:
+ I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE);
+ break;
+ case DPMSModeStandby:
+ case DPMSModeSuspend:
+ case DPMSModeOff:
+ I915_WRITE(TV_CTL, I915_READ(TV_CTL) & ~TV_ENC_ENABLE);
+ break;
+ }
+}
+
+static void
+intel_tv_save(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_tv_priv *tv_priv = intel_output->dev_priv;
+ int i;
+
+ tv_priv->save_TV_H_CTL_1 = I915_READ(TV_H_CTL_1);
+ tv_priv->save_TV_H_CTL_2 = I915_READ(TV_H_CTL_2);
+ tv_priv->save_TV_H_CTL_3 = I915_READ(TV_H_CTL_3);
+ tv_priv->save_TV_V_CTL_1 = I915_READ(TV_V_CTL_1);
+ tv_priv->save_TV_V_CTL_2 = I915_READ(TV_V_CTL_2);
+ tv_priv->save_TV_V_CTL_3 = I915_READ(TV_V_CTL_3);
+ tv_priv->save_TV_V_CTL_4 = I915_READ(TV_V_CTL_4);
+ tv_priv->save_TV_V_CTL_5 = I915_READ(TV_V_CTL_5);
+ tv_priv->save_TV_V_CTL_6 = I915_READ(TV_V_CTL_6);
+ tv_priv->save_TV_V_CTL_7 = I915_READ(TV_V_CTL_7);
+ tv_priv->save_TV_SC_CTL_1 = I915_READ(TV_SC_CTL_1);
+ tv_priv->save_TV_SC_CTL_2 = I915_READ(TV_SC_CTL_2);
+ tv_priv->save_TV_SC_CTL_3 = I915_READ(TV_SC_CTL_3);
+
+ tv_priv->save_TV_CSC_Y = I915_READ(TV_CSC_Y);
+ tv_priv->save_TV_CSC_Y2 = I915_READ(TV_CSC_Y2);
+ tv_priv->save_TV_CSC_U = I915_READ(TV_CSC_U);
+ tv_priv->save_TV_CSC_U2 = I915_READ(TV_CSC_U2);
+ tv_priv->save_TV_CSC_V = I915_READ(TV_CSC_V);
+ tv_priv->save_TV_CSC_V2 = I915_READ(TV_CSC_V2);
+ tv_priv->save_TV_CLR_KNOBS = I915_READ(TV_CLR_KNOBS);
+ tv_priv->save_TV_CLR_LEVEL = I915_READ(TV_CLR_LEVEL);
+ tv_priv->save_TV_WIN_POS = I915_READ(TV_WIN_POS);
+ tv_priv->save_TV_WIN_SIZE = I915_READ(TV_WIN_SIZE);
+ tv_priv->save_TV_FILTER_CTL_1 = I915_READ(TV_FILTER_CTL_1);
+ tv_priv->save_TV_FILTER_CTL_2 = I915_READ(TV_FILTER_CTL_2);
+ tv_priv->save_TV_FILTER_CTL_3 = I915_READ(TV_FILTER_CTL_3);
+
+ for (i = 0; i < 60; i++)
+ tv_priv->save_TV_H_LUMA[i] = I915_READ(TV_H_LUMA_0 + (i <<2));
+ for (i = 0; i < 60; i++)
+ tv_priv->save_TV_H_CHROMA[i] = I915_READ(TV_H_CHROMA_0 + (i <<2));
+ for (i = 0; i < 43; i++)
+ tv_priv->save_TV_V_LUMA[i] = I915_READ(TV_V_LUMA_0 + (i <<2));
+ for (i = 0; i < 43; i++)
+ tv_priv->save_TV_V_CHROMA[i] = I915_READ(TV_V_CHROMA_0 + (i <<2));
+
+ tv_priv->save_TV_DAC = I915_READ(TV_DAC);
+ tv_priv->save_TV_CTL = I915_READ(TV_CTL);
+}
+
+static void
+intel_tv_restore(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_tv_priv *tv_priv = intel_output->dev_priv;
+ struct drm_crtc *crtc = connector->encoder->crtc;
+ struct intel_crtc *intel_crtc;
+ int i;
+
+ /* FIXME: No CRTC? */
+ if (!crtc)
+ return;
+
+ intel_crtc = to_intel_crtc(crtc);
+ I915_WRITE(TV_H_CTL_1, tv_priv->save_TV_H_CTL_1);
+ I915_WRITE(TV_H_CTL_2, tv_priv->save_TV_H_CTL_2);
+ I915_WRITE(TV_H_CTL_3, tv_priv->save_TV_H_CTL_3);
+ I915_WRITE(TV_V_CTL_1, tv_priv->save_TV_V_CTL_1);
+ I915_WRITE(TV_V_CTL_2, tv_priv->save_TV_V_CTL_2);
+ I915_WRITE(TV_V_CTL_3, tv_priv->save_TV_V_CTL_3);
+ I915_WRITE(TV_V_CTL_4, tv_priv->save_TV_V_CTL_4);
+ I915_WRITE(TV_V_CTL_5, tv_priv->save_TV_V_CTL_5);
+ I915_WRITE(TV_V_CTL_6, tv_priv->save_TV_V_CTL_6);
+ I915_WRITE(TV_V_CTL_7, tv_priv->save_TV_V_CTL_7);
+ I915_WRITE(TV_SC_CTL_1, tv_priv->save_TV_SC_CTL_1);
+ I915_WRITE(TV_SC_CTL_2, tv_priv->save_TV_SC_CTL_2);
+ I915_WRITE(TV_SC_CTL_3, tv_priv->save_TV_SC_CTL_3);
+
+ I915_WRITE(TV_CSC_Y, tv_priv->save_TV_CSC_Y);
+ I915_WRITE(TV_CSC_Y2, tv_priv->save_TV_CSC_Y2);
+ I915_WRITE(TV_CSC_U, tv_priv->save_TV_CSC_U);
+ I915_WRITE(TV_CSC_U2, tv_priv->save_TV_CSC_U2);
+ I915_WRITE(TV_CSC_V, tv_priv->save_TV_CSC_V);
+ I915_WRITE(TV_CSC_V2, tv_priv->save_TV_CSC_V2);
+ I915_WRITE(TV_CLR_KNOBS, tv_priv->save_TV_CLR_KNOBS);
+ I915_WRITE(TV_CLR_LEVEL, tv_priv->save_TV_CLR_LEVEL);
+
+ {
+ int pipeconf_reg = (intel_crtc->pipe == 0) ?
+ PIPEACONF : PIPEBCONF;
+ int dspcntr_reg = (intel_crtc->plane == 0) ?
+ DSPACNTR : DSPBCNTR;
+ int pipeconf = I915_READ(pipeconf_reg);
+ int dspcntr = I915_READ(dspcntr_reg);
+ int dspbase_reg = (intel_crtc->plane == 0) ?
+ DSPAADDR : DSPBADDR;
+ /* Pipe must be off here */
+ I915_WRITE(dspcntr_reg, dspcntr & ~DISPLAY_PLANE_ENABLE);
+ /* Flush the plane changes */
+ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+
+ if (!IS_I9XX(dev)) {
+ /* Wait for vblank for the disable to take effect */
+ intel_wait_for_vblank(dev);
+ }
+
+ I915_WRITE(pipeconf_reg, pipeconf & ~PIPEACONF_ENABLE);
+ /* Wait for vblank for the disable to take effect. */
+ intel_wait_for_vblank(dev);
+
+ /* Filter ctl must be set before TV_WIN_SIZE */
+ I915_WRITE(TV_FILTER_CTL_1, tv_priv->save_TV_FILTER_CTL_1);
+ I915_WRITE(TV_FILTER_CTL_2, tv_priv->save_TV_FILTER_CTL_2);
+ I915_WRITE(TV_FILTER_CTL_3, tv_priv->save_TV_FILTER_CTL_3);
+ I915_WRITE(TV_WIN_POS, tv_priv->save_TV_WIN_POS);
+ I915_WRITE(TV_WIN_SIZE, tv_priv->save_TV_WIN_SIZE);
+ I915_WRITE(pipeconf_reg, pipeconf);
+ I915_WRITE(dspcntr_reg, dspcntr);
+ /* Flush the plane changes */
+ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+ }
+
+ for (i = 0; i < 60; i++)
+ I915_WRITE(TV_H_LUMA_0 + (i <<2), tv_priv->save_TV_H_LUMA[i]);
+ for (i = 0; i < 60; i++)
+ I915_WRITE(TV_H_CHROMA_0 + (i <<2), tv_priv->save_TV_H_CHROMA[i]);
+ for (i = 0; i < 43; i++)
+ I915_WRITE(TV_V_LUMA_0 + (i <<2), tv_priv->save_TV_V_LUMA[i]);
+ for (i = 0; i < 43; i++)
+ I915_WRITE(TV_V_CHROMA_0 + (i <<2), tv_priv->save_TV_V_CHROMA[i]);
+
+ I915_WRITE(TV_DAC, tv_priv->save_TV_DAC);
+ I915_WRITE(TV_CTL, tv_priv->save_TV_CTL);
+}
+
+static const struct tv_mode *
+intel_tv_mode_lookup (char *tv_format)
+{
+ int i;
+
+ for (i = 0; i < sizeof(tv_modes) / sizeof (tv_modes[0]); i++) {
+ const struct tv_mode *tv_mode = &tv_modes[i];
+
+ if (!strcmp(tv_format, tv_mode->name))
+ return tv_mode;
+ }
+ return NULL;
+}
+
+static const struct tv_mode *
+intel_tv_mode_find (struct intel_output *intel_output)
+{
+ struct intel_tv_priv *tv_priv = intel_output->dev_priv;
+
+ return intel_tv_mode_lookup(tv_priv->tv_format);
+}
+
+static enum drm_mode_status
+intel_tv_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ const struct tv_mode *tv_mode = intel_tv_mode_find(intel_output);
+
+ /* Ensure TV refresh is close to desired refresh */
+ if (tv_mode && abs(tv_mode->refresh - drm_mode_vrefresh(mode)) < 1)
+ return MODE_OK;
+ return MODE_CLOCK_RANGE;
+}
+
+
+static bool
+intel_tv_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_mode_config *drm_config = &dev->mode_config;
+ struct intel_output *intel_output = enc_to_intel_output(encoder);
+ const struct tv_mode *tv_mode = intel_tv_mode_find (intel_output);
+ struct drm_encoder *other_encoder;
+
+ if (!tv_mode)
+ return FALSE;
+
+ /* FIXME: lock encoder list */
+ list_for_each_entry(other_encoder, &drm_config->encoder_list, head) {
+ if (other_encoder != encoder &&
+ other_encoder->crtc == encoder->crtc)
+ return FALSE;
+ }
+
+ adjusted_mode->clock = tv_mode->clock;
+ return TRUE;
+}
+
+static void
+intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_crtc *crtc = encoder->crtc;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct intel_output *intel_output = enc_to_intel_output(encoder);
+ struct intel_tv_priv *tv_priv = intel_output->dev_priv;
+ const struct tv_mode *tv_mode = intel_tv_mode_find(intel_output);
+ u32 tv_ctl;
+ u32 hctl1, hctl2, hctl3;
+ u32 vctl1, vctl2, vctl3, vctl4, vctl5, vctl6, vctl7;
+ u32 scctl1, scctl2, scctl3;
+ int i, j;
+ const struct video_levels *video_levels;
+ const struct color_conversion *color_conversion;
+ bool burst_ena;
+
+ if (!tv_mode)
+ return; /* can't happen (mode_prepare prevents this) */
+
+ tv_ctl = 0;
+
+ switch (tv_priv->type) {
+ default:
+ case DRM_MODE_CONNECTOR_Unknown:
+ case DRM_MODE_CONNECTOR_Composite:
+ tv_ctl |= TV_ENC_OUTPUT_COMPOSITE;
+ video_levels = tv_mode->composite_levels;
+ color_conversion = tv_mode->composite_color;
+ burst_ena = tv_mode->burst_ena;
+ break;
+ case DRM_MODE_CONNECTOR_Component:
+ tv_ctl |= TV_ENC_OUTPUT_COMPONENT;
+ video_levels = &component_levels;
+ if (tv_mode->burst_ena)
+ color_conversion = &sdtv_csc_yprpb;
+ else
+ color_conversion = &hdtv_csc_yprpb;
+ burst_ena = FALSE;
+ break;
+ case DRM_MODE_CONNECTOR_SVIDEO:
+ tv_ctl |= TV_ENC_OUTPUT_SVIDEO;
+ video_levels = tv_mode->svideo_levels;
+ color_conversion = tv_mode->svideo_color;
+ burst_ena = tv_mode->burst_ena;
+ break;
+ }
+ hctl1 = (tv_mode->hsync_end << TV_HSYNC_END_SHIFT) |
+ (tv_mode->htotal << TV_HTOTAL_SHIFT);
+
+ hctl2 = (tv_mode->hburst_start << 16) |
+ (tv_mode->hburst_len << TV_HBURST_LEN_SHIFT);
+
+ if (burst_ena)
+ hctl2 |= TV_BURST_ENA;
+
+ hctl3 = (tv_mode->hblank_start << TV_HBLANK_START_SHIFT) |
+ (tv_mode->hblank_end << TV_HBLANK_END_SHIFT);
+
+ vctl1 = (tv_mode->nbr_end << TV_NBR_END_SHIFT) |
+ (tv_mode->vi_end_f1 << TV_VI_END_F1_SHIFT) |
+ (tv_mode->vi_end_f2 << TV_VI_END_F2_SHIFT);
+
+ vctl2 = (tv_mode->vsync_len << TV_VSYNC_LEN_SHIFT) |
+ (tv_mode->vsync_start_f1 << TV_VSYNC_START_F1_SHIFT) |
+ (tv_mode->vsync_start_f2 << TV_VSYNC_START_F2_SHIFT);
+
+ vctl3 = (tv_mode->veq_len << TV_VEQ_LEN_SHIFT) |
+ (tv_mode->veq_start_f1 << TV_VEQ_START_F1_SHIFT) |
+ (tv_mode->veq_start_f2 << TV_VEQ_START_F2_SHIFT);
+
+ if (tv_mode->veq_ena)
+ vctl3 |= TV_EQUAL_ENA;
+
+ vctl4 = (tv_mode->vburst_start_f1 << TV_VBURST_START_F1_SHIFT) |
+ (tv_mode->vburst_end_f1 << TV_VBURST_END_F1_SHIFT);
+
+ vctl5 = (tv_mode->vburst_start_f2 << TV_VBURST_START_F2_SHIFT) |
+ (tv_mode->vburst_end_f2 << TV_VBURST_END_F2_SHIFT);
+
+ vctl6 = (tv_mode->vburst_start_f3 << TV_VBURST_START_F3_SHIFT) |
+ (tv_mode->vburst_end_f3 << TV_VBURST_END_F3_SHIFT);
+
+ vctl7 = (tv_mode->vburst_start_f4 << TV_VBURST_START_F4_SHIFT) |
+ (tv_mode->vburst_end_f4 << TV_VBURST_END_F4_SHIFT);
+
+ if (intel_crtc->pipe == 1)
+ tv_ctl |= TV_ENC_PIPEB_SELECT;
+ tv_ctl |= tv_mode->oversample;
+
+ if (tv_mode->progressive)
+ tv_ctl |= TV_PROGRESSIVE;
+ if (tv_mode->trilevel_sync)
+ tv_ctl |= TV_TRILEVEL_SYNC;
+ if (tv_mode->pal_burst)
+ tv_ctl |= TV_PAL_BURST;
+ scctl1 = 0;
+ /* dda1 implies valid video levels */
+ if (tv_mode->dda1_inc) {
+ scctl1 |= TV_SC_DDA1_EN;
+ scctl1 |= video_levels->burst << TV_BURST_LEVEL_SHIFT;
+ }
+
+ if (tv_mode->dda2_inc)
+ scctl1 |= TV_SC_DDA2_EN;
+
+ if (tv_mode->dda3_inc)
+ scctl1 |= TV_SC_DDA3_EN;
+
+ scctl1 |= tv_mode->sc_reset;
+ scctl1 |= tv_mode->dda1_inc << TV_SCDDA1_INC_SHIFT;
+
+ scctl2 = tv_mode->dda2_size << TV_SCDDA2_SIZE_SHIFT |
+ tv_mode->dda2_inc << TV_SCDDA2_INC_SHIFT;
+
+ scctl3 = tv_mode->dda3_size << TV_SCDDA3_SIZE_SHIFT |
+ tv_mode->dda3_inc << TV_SCDDA3_INC_SHIFT;
+
+ /* Enable two fixes for the chips that need them. */
+ if (dev->pci_device < 0x2772)
+ tv_ctl |= TV_ENC_C0_FIX | TV_ENC_SDP_FIX;
+
+ I915_WRITE(TV_H_CTL_1, hctl1);
+ I915_WRITE(TV_H_CTL_2, hctl2);
+ I915_WRITE(TV_H_CTL_3, hctl3);
+ I915_WRITE(TV_V_CTL_1, vctl1);
+ I915_WRITE(TV_V_CTL_2, vctl2);
+ I915_WRITE(TV_V_CTL_3, vctl3);
+ I915_WRITE(TV_V_CTL_4, vctl4);
+ I915_WRITE(TV_V_CTL_5, vctl5);
+ I915_WRITE(TV_V_CTL_6, vctl6);
+ I915_WRITE(TV_V_CTL_7, vctl7);
+ I915_WRITE(TV_SC_CTL_1, scctl1);
+ I915_WRITE(TV_SC_CTL_2, scctl2);
+ I915_WRITE(TV_SC_CTL_3, scctl3);
+
+ if (color_conversion) {
+ I915_WRITE(TV_CSC_Y, (color_conversion->ry << 16) |
+ color_conversion->gy);
+ I915_WRITE(TV_CSC_Y2,(color_conversion->by << 16) |
+ color_conversion->ay);
+ I915_WRITE(TV_CSC_U, (color_conversion->ru << 16) |
+ color_conversion->gu);
+ I915_WRITE(TV_CSC_U2, (color_conversion->bu << 16) |
+ color_conversion->au);
+ I915_WRITE(TV_CSC_V, (color_conversion->rv << 16) |
+ color_conversion->gv);
+ I915_WRITE(TV_CSC_V2, (color_conversion->bv << 16) |
+ color_conversion->av);
+ }
+
+ I915_WRITE(TV_CLR_KNOBS, 0x00606000);
+ if (video_levels)
+ I915_WRITE(TV_CLR_LEVEL,
+ ((video_levels->black << TV_BLACK_LEVEL_SHIFT) |
+ (video_levels->blank << TV_BLANK_LEVEL_SHIFT)));
+ {
+ int pipeconf_reg = (intel_crtc->pipe == 0) ?
+ PIPEACONF : PIPEBCONF;
+ int dspcntr_reg = (intel_crtc->plane == 0) ?
+ DSPACNTR : DSPBCNTR;
+ int pipeconf = I915_READ(pipeconf_reg);
+ int dspcntr = I915_READ(dspcntr_reg);
+ int dspbase_reg = (intel_crtc->plane == 0) ?
+ DSPAADDR : DSPBADDR;
+ int xpos = 0x0, ypos = 0x0;
+ unsigned int xsize, ysize;
+ /* Pipe must be off here */
+ I915_WRITE(dspcntr_reg, dspcntr & ~DISPLAY_PLANE_ENABLE);
+ /* Flush the plane changes */
+ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+
+ /* Wait for vblank for the disable to take effect */
+ if (!IS_I9XX(dev))
+ intel_wait_for_vblank(dev);
+
+ I915_WRITE(pipeconf_reg, pipeconf & ~PIPEACONF_ENABLE);
+ /* Wait for vblank for the disable to take effect. */
+ intel_wait_for_vblank(dev);
+
+ /* Filter ctl must be set before TV_WIN_SIZE */
+ I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE);
+ xsize = tv_mode->hblank_start - tv_mode->hblank_end;
+ if (tv_mode->progressive)
+ ysize = tv_mode->nbr_end + 1;
+ else
+ ysize = 2*tv_mode->nbr_end + 1;
+
+ xpos += tv_priv->margin[TV_MARGIN_LEFT];
+ ypos += tv_priv->margin[TV_MARGIN_TOP];
+ xsize -= (tv_priv->margin[TV_MARGIN_LEFT] +
+ tv_priv->margin[TV_MARGIN_RIGHT]);
+ ysize -= (tv_priv->margin[TV_MARGIN_TOP] +
+ tv_priv->margin[TV_MARGIN_BOTTOM]);
+ I915_WRITE(TV_WIN_POS, (xpos<<16)|ypos);
+ I915_WRITE(TV_WIN_SIZE, (xsize<<16)|ysize);
+
+ I915_WRITE(pipeconf_reg, pipeconf);
+ I915_WRITE(dspcntr_reg, dspcntr);
+ /* Flush the plane changes */
+ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+ }
+
+ j = 0;
+ for (i = 0; i < 60; i++)
+ I915_WRITE(TV_H_LUMA_0 + (i<<2), tv_mode->filter_table[j++]);
+ for (i = 0; i < 60; i++)
+ I915_WRITE(TV_H_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]);
+ for (i = 0; i < 43; i++)
+ I915_WRITE(TV_V_LUMA_0 + (i<<2), tv_mode->filter_table[j++]);
+ for (i = 0; i < 43; i++)
+ I915_WRITE(TV_V_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]);
+ I915_WRITE(TV_DAC, 0);
+ I915_WRITE(TV_CTL, tv_ctl);
+}
+
+static const struct drm_display_mode reported_modes[] = {
+ {
+ .name = "NTSC 480i",
+ .clock = 107520,
+ .hdisplay = 1280,
+ .hsync_start = 1368,
+ .hsync_end = 1496,
+ .htotal = 1712,
+
+ .vdisplay = 1024,
+ .vsync_start = 1027,
+ .vsync_end = 1034,
+ .vtotal = 1104,
+ .type = DRM_MODE_TYPE_DRIVER,
+ },
+};
+
+/**
+ * Detects TV presence by checking for load.
+ *
+ * Requires that the current pipe's DPLL is active.
+
+ * \return TRUE if TV is connected.
+ * \return FALSE if TV is disconnected.
+ */
+static int
+intel_tv_detect_type (struct drm_crtc *crtc, struct intel_output *intel_output)
+{
+ struct drm_encoder *encoder = &intel_output->enc;
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 pipeastat, pipeastat_save;
+ u32 tv_ctl, save_tv_ctl;
+ u32 tv_dac, save_tv_dac;
+ int type = DRM_MODE_CONNECTOR_Unknown;
+
+ tv_dac = I915_READ(TV_DAC);
+
+ /* Disable TV interrupts around load detect or we'll recurse */
+ pipeastat = I915_READ(PIPEASTAT);
+ pipeastat_save = pipeastat;
+ pipeastat &= ~PIPE_HOTPLUG_INTERRUPT_ENABLE;
+ pipeastat &= ~PIPE_HOTPLUG_TV_INTERRUPT_ENABLE;
+ I915_WRITE(PIPEASTAT, pipeastat | PIPE_HOTPLUG_TV_INTERRUPT_STATUS |
+ PIPE_HOTPLUG_INTERRUPT_STATUS);
+
+ /*
+ * Detect TV by polling)
+ */
+ if (intel_output->load_detect_temp) {
+ /* TV not currently running, prod it with destructive detect */
+ save_tv_dac = tv_dac;
+ tv_ctl = I915_READ(TV_CTL);
+ save_tv_ctl = tv_ctl;
+ tv_ctl &= ~TV_ENC_ENABLE;
+ tv_ctl &= ~TV_TEST_MODE_MASK;
+ tv_ctl |= TV_TEST_MODE_MONITOR_DETECT;
+ tv_dac &= ~TVDAC_SENSE_MASK;
+ tv_dac |= (TVDAC_STATE_CHG_EN |
+ TVDAC_A_SENSE_CTL |
+ TVDAC_B_SENSE_CTL |
+ TVDAC_C_SENSE_CTL |
+ DAC_CTL_OVERRIDE |
+ DAC_A_0_7_V |
+ DAC_B_0_7_V |
+ DAC_C_0_7_V);
+ I915_WRITE(TV_CTL, tv_ctl);
+ I915_WRITE(TV_DAC, tv_dac);
+ intel_wait_for_vblank(dev);
+ tv_dac = I915_READ(TV_DAC);
+ I915_WRITE(TV_DAC, save_tv_dac);
+ I915_WRITE(TV_CTL, save_tv_ctl);
+ }
+ /*
+ * A B C
+ * 0 1 1 Composite
+ * 1 0 X svideo
+ * 0 0 0 Component
+ */
+ if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) {
+ DRM_DEBUG("Detected Composite TV connection\n");
+ type = DRM_MODE_CONNECTOR_Composite;
+ } else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) {
+ DRM_DEBUG("Detected S-Video TV connection\n");
+ type = DRM_MODE_CONNECTOR_SVIDEO;
+ } else if ((tv_dac & TVDAC_SENSE_MASK) == 0) {
+ DRM_DEBUG("Detected Component TV connection\n");
+ type = DRM_MODE_CONNECTOR_Component;
+ } else {
+ DRM_DEBUG("No TV connection detected\n");
+ type = -1;
+ }
+
+ /* Restore interrupt config */
+ I915_WRITE(PIPEASTAT, pipeastat_save | PIPE_HOTPLUG_TV_INTERRUPT_STATUS |
+ PIPE_HOTPLUG_INTERRUPT_STATUS);
+
+ return type;
+}
+
+/**
+ * Detect the TV connection.
+ *
+ * Currently this always returns CONNECTOR_STATUS_UNKNOWN, as we need to be sure
+ * we have a pipe programmed in order to probe the TV.
+ */
+static enum drm_connector_status
+intel_tv_detect(struct drm_connector *connector)
+{
+ struct drm_crtc *crtc;
+ struct drm_display_mode mode;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_tv_priv *tv_priv = intel_output->dev_priv;
+ struct drm_encoder *encoder = &intel_output->enc;
+ int dpms_mode;
+ int type = tv_priv->type;
+
+ mode = reported_modes[0];
+ drm_mode_set_crtcinfo(&mode, CRTC_INTERLACE_HALVE_V);
+
+ if (encoder->crtc) {
+ type = intel_tv_detect_type(encoder->crtc, intel_output);
+ } else {
+ crtc = intel_get_load_detect_pipe(intel_output, &mode, &dpms_mode);
+ if (crtc) {
+ type = intel_tv_detect_type(crtc, intel_output);
+ intel_release_load_detect_pipe(intel_output, dpms_mode);
+ } else
+ type = -1;
+ }
+
+#if 0
+ if (type != tv_priv->type) {
+ struct drm_property *connector_property =
+ connector->dev->mode_config.connector_type_property;
+
+ tv_priv->type = type;
+ drm_connector_property_set_value(connector, connector_property,
+ type);
+ }
+#endif
+ if (type < 0)
+ return connector_status_disconnected;
+
+ return connector_status_connected;
+}
+
+static struct input_res {
+ char *name;
+ int w, h;
+} input_res_table[] =
+{
+ {"640x480", 640, 480},
+ {"800x600", 800, 600},
+ {"1024x768", 1024, 768},
+ {"1280x1024", 1280, 1024},
+ {"848x480", 848, 480},
+ {"1280x720", 1280, 720},
+ {"1920x1080", 1920, 1080},
+};
+
+/**
+ * Stub get_modes function.
+ *
+ * This should probably return a set of fixed modes, unless we can figure out
+ * how to probe modes off of TV connections.
+ */
+
+static int
+intel_tv_get_modes(struct drm_connector *connector)
+{
+ struct drm_display_mode *mode_ptr;
+ struct intel_output *intel_output = to_intel_output(connector);
+ const struct tv_mode *tv_mode = intel_tv_mode_find(intel_output);
+ int j;
+
+ for (j = 0; j < sizeof(input_res_table) / sizeof(input_res_table[0]);
+ j++) {
+ struct input_res *input = &input_res_table[j];
+ unsigned int hactive_s = input->w;
+ unsigned int vactive_s = input->h;
+
+ if (tv_mode->max_srcw && input->w > tv_mode->max_srcw)
+ continue;
+
+ if (input->w > 1024 && (!tv_mode->progressive
+ && !tv_mode->component_only))
+ continue;
+
+ mode_ptr = drm_calloc(1, sizeof(struct drm_display_mode),
+ DRM_MEM_DRIVER);
+ strncpy(mode_ptr->name, input->name, DRM_DISPLAY_MODE_LEN);
+
+ mode_ptr->hdisplay = hactive_s;
+ mode_ptr->hsync_start = hactive_s + 1;
+ mode_ptr->hsync_end = hactive_s + 64;
+ if (mode_ptr->hsync_end <= mode_ptr->hsync_start)
+ mode_ptr->hsync_end = mode_ptr->hsync_start + 1;
+ mode_ptr->htotal = hactive_s + 96;
+
+ mode_ptr->vdisplay = vactive_s;
+ mode_ptr->vsync_start = vactive_s + 1;
+ mode_ptr->vsync_end = vactive_s + 32;
+ if (mode_ptr->vsync_end <= mode_ptr->vsync_start)
+ mode_ptr->vsync_end = mode_ptr->vsync_start + 1;
+ mode_ptr->vtotal = vactive_s + 33;
+
+ mode_ptr->clock = (int) (tv_mode->refresh *
+ mode_ptr->vtotal *
+ mode_ptr->htotal / 1000) / 1000;
+
+ mode_ptr->type = DRM_MODE_TYPE_DRIVER;
+ drm_mode_probed_add(connector, mode_ptr);
+ }
+
+ return 0;
+}
+
+static void
+intel_tv_destroy (struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
+ drm_free(intel_output, sizeof(struct intel_output) + sizeof(struct intel_tv_priv),
+ DRM_MEM_DRIVER);
+}
+
+
+static bool
+intel_tv_set_property(struct drm_connector *connector, struct drm_property *property,
+ uint64_t val)
+{
+ struct drm_device *dev = connector->dev;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_tv_priv *tv_priv = intel_output->dev_priv;
+ int ret = 0;
+
+ ret = drm_connector_property_set_value(connector, property, val);
+ if (ret < 0)
+ goto out;
+
+ if (property == dev->mode_config.tv_left_margin_property)
+ tv_priv->margin[TV_MARGIN_LEFT] = val;
+ else if (property == dev->mode_config.tv_right_margin_property)
+ tv_priv->margin[TV_MARGIN_RIGHT] = val;
+ else if (property == dev->mode_config.tv_top_margin_property)
+ tv_priv->margin[TV_MARGIN_TOP] = val;
+ else if (property == dev->mode_config.tv_bottom_margin_property)
+ tv_priv->margin[TV_MARGIN_BOTTOM] = val;
+ else if (property == dev->mode_config.tv_mode_property) {
+ if (val >= NUM_TV_MODES) {
+ ret = -EINVAL;
+ goto out;
+ }
+ tv_priv->tv_format = tv_modes[val].name;
+ intel_tv_mode_set(&intel_output->enc, NULL, NULL);
+ } else {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ intel_tv_mode_set(&intel_output->enc, NULL, NULL);
+out:
+ return ret;
+}
+
+static const struct drm_encoder_helper_funcs intel_tv_helper_funcs = {
+ .dpms = intel_tv_dpms,
+ .mode_fixup = intel_tv_mode_fixup,
+ .prepare = intel_encoder_prepare,
+ .mode_set = intel_tv_mode_set,
+ .commit = intel_encoder_commit,
+};
+
+static const struct drm_connector_funcs intel_tv_connector_funcs = {
+ .save = intel_tv_save,
+ .restore = intel_tv_restore,
+ .detect = intel_tv_detect,
+ .destroy = intel_tv_destroy,
+ .set_property = intel_tv_set_property,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+};
+
+static const struct drm_connector_helper_funcs intel_tv_connector_helper_funcs = {
+ .mode_valid = intel_tv_mode_valid,
+ .get_modes = intel_tv_get_modes,
+ .best_encoder = intel_best_encoder,
+};
+
+void intel_tv_enc_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs intel_tv_enc_funcs = {
+ .destroy = intel_tv_enc_destroy,
+};
+
+
+void
+intel_tv_init(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_connector *connector;
+ struct intel_output *intel_output;
+ struct intel_tv_priv *tv_priv;
+ u32 tv_dac_on, tv_dac_off, save_tv_dac;
+ char **tv_format_names;
+ int i, initial_mode = 0;
+
+ if ((I915_READ(TV_CTL) & TV_FUSE_STATE_MASK) == TV_FUSE_STATE_DISABLED)
+ return;
+
+ /* Even if we have an encoder we may not have a connector */
+ if (!dev_priv->int_tv_support)
+ return;
+
+ /*
+ * Sanity check the TV output by checking to see if the
+ * DAC register holds a value
+ */
+ save_tv_dac = I915_READ(TV_DAC);
+
+ I915_WRITE(TV_DAC, save_tv_dac | TVDAC_STATE_CHG_EN);
+ tv_dac_on = I915_READ(TV_DAC);
+
+ I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN);
+ tv_dac_off = I915_READ(TV_DAC);
+
+ I915_WRITE(TV_DAC, save_tv_dac);
+
+ /*
+ * If the register does not hold the state change enable
+ * bit, (either as a 0 or a 1), assume it doesn't really
+ * exist
+ */
+ if ((tv_dac_on & TVDAC_STATE_CHG_EN) == 0 ||
+ (tv_dac_off & TVDAC_STATE_CHG_EN) != 0)
+ return;
+
+ intel_output = drm_calloc(1, sizeof(struct intel_output) +
+ sizeof(struct intel_tv_priv), DRM_MEM_DRIVER);
+ if (!intel_output) {
+ return;
+ }
+ connector = &intel_output->base;
+
+ drm_connector_init(dev, connector, &intel_tv_connector_funcs,
+ DRM_MODE_CONNECTOR_SVIDEO);
+
+ drm_encoder_init(dev, &intel_output->enc, &intel_tv_enc_funcs,
+ DRM_MODE_ENCODER_TVDAC);
+
+ drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc);
+ tv_priv = (struct intel_tv_priv *)(intel_output + 1);
+ intel_output->type = INTEL_OUTPUT_TVOUT;
+ intel_output->enc.possible_crtcs = ((1 << 0) | (1 << 1));
+ intel_output->enc.possible_clones = (1 << INTEL_OUTPUT_TVOUT);
+ intel_output->dev_priv = tv_priv;
+ tv_priv->type = DRM_MODE_CONNECTOR_Unknown;
+
+ /* BIOS margin values */
+ tv_priv->margin[TV_MARGIN_LEFT] = 54;
+ tv_priv->margin[TV_MARGIN_TOP] = 36;
+ tv_priv->margin[TV_MARGIN_RIGHT] = 46;
+ tv_priv->margin[TV_MARGIN_BOTTOM] = 37;
+
+ tv_priv->tv_format = kstrdup(tv_modes[initial_mode].name, GFP_KERNEL);
+
+ drm_encoder_helper_add(&intel_output->enc, &intel_tv_helper_funcs);
+ drm_connector_helper_add(connector, &intel_tv_connector_helper_funcs);
+ connector->interlace_allowed = FALSE;
+ connector->doublescan_allowed = FALSE;
+
+ /* Create TV properties then attach current values */
+ tv_format_names = drm_alloc(sizeof(char *) * NUM_TV_MODES,
+ DRM_MEM_DRIVER);
+ if (!tv_format_names)
+ goto out;
+ for (i = 0; i < NUM_TV_MODES; i++)
+ tv_format_names[i] = tv_modes[i].name;
+ drm_create_tv_properties(dev, NUM_TV_MODES, tv_format_names);
+
+ drm_connector_attach_property(connector, dev->mode_config.tv_mode_property,
+ initial_mode);
+ drm_connector_attach_property(connector,
+ dev->mode_config.tv_left_margin_property,
+ tv_priv->margin[TV_MARGIN_LEFT]);
+ drm_connector_attach_property(connector,
+ dev->mode_config.tv_top_margin_property,
+ tv_priv->margin[TV_MARGIN_TOP]);
+ drm_connector_attach_property(connector,
+ dev->mode_config.tv_right_margin_property,
+ tv_priv->margin[TV_MARGIN_RIGHT]);
+ drm_connector_attach_property(connector,
+ dev->mode_config.tv_bottom_margin_property,
+ tv_priv->margin[TV_MARGIN_BOTTOM]);
+out:
+ drm_sysfs_connector_add(connector);
+}
diff --git a/linux-core/radeon_ms.h b/linux-core/radeon_ms.h
new file mode 120000
index 00000000..da340c5d
--- /dev/null
+++ b/linux-core/radeon_ms.h
@@ -0,0 +1 @@
+../shared-core/radeon_ms.h \ No newline at end of file
diff --git a/linux-core/radeon_ms_bo.c b/linux-core/radeon_ms_bo.c
new file mode 120000
index 00000000..d05df59f
--- /dev/null
+++ b/linux-core/radeon_ms_bo.c
@@ -0,0 +1 @@
+../shared-core/radeon_ms_bo.c \ No newline at end of file
diff --git a/linux-core/radeon_ms_bus.c b/linux-core/radeon_ms_bus.c
new file mode 120000
index 00000000..50f649d0
--- /dev/null
+++ b/linux-core/radeon_ms_bus.c
@@ -0,0 +1 @@
+../shared-core/radeon_ms_bus.c \ No newline at end of file
diff --git a/linux-core/radeon_ms_combios.c b/linux-core/radeon_ms_combios.c
new file mode 120000
index 00000000..d7b99958
--- /dev/null
+++ b/linux-core/radeon_ms_combios.c
@@ -0,0 +1 @@
+../shared-core/radeon_ms_combios.c \ No newline at end of file
diff --git a/linux-core/radeon_ms_combios.h b/linux-core/radeon_ms_combios.h
new file mode 120000
index 00000000..5b19c708
--- /dev/null
+++ b/linux-core/radeon_ms_combios.h
@@ -0,0 +1 @@
+../shared-core/radeon_ms_combios.h \ No newline at end of file
diff --git a/linux-core/radeon_ms_compat.c b/linux-core/radeon_ms_compat.c
new file mode 100644
index 00000000..6efdc786
--- /dev/null
+++ b/linux-core/radeon_ms_compat.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2007 Dave Airlie
+ * Copyright 2007 Alex Deucher
+ * Copyright 2007 Michel Dänzer
+ * Copyright 2007 Roland Scheidegger
+ * Copyright 2007 Vladimir Dergachev
+ * Copyright 2007 Nicolai Haehnle
+ * Copyright 2007 Aapo Tahkola
+ * Copyright 2007 Ben Skeggs
+ * Copyright 2007 Jérôme Glisse
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * Authors:
+ * Jerome Glisse <glisse@freedesktop.org>
+ */
+#include "drmP.h"
+#include "drm.h"
+#include "radeon_ms.h"
+
+/**
+ * Called whenever a 32-bit process running under a 64-bit kernel
+ * performs an ioctl on /dev/dri/card<n>.
+
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg user argument.
+ * \return zero on success or negative number on failure.
+ */
+long radeon_ms_compat_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ unsigned int nr = DRM_IOCTL_NR(cmd);
+ drm_ioctl_compat_t *fn = NULL;
+ int ret;
+
+ if (nr < DRM_COMMAND_BASE)
+ return drm_compat_ioctl(filp, cmd, arg);
+
+ lock_kernel();
+ if (fn != NULL)
+ ret = (*fn)(filp, cmd, arg);
+ else
+ ret = drm_ioctl(filp->f_dentry->d_inode, filp, cmd, arg);
+ unlock_kernel();
+
+ return ret;
+}
diff --git a/linux-core/radeon_ms_cp.c b/linux-core/radeon_ms_cp.c
new file mode 120000
index 00000000..6aee3e6e
--- /dev/null
+++ b/linux-core/radeon_ms_cp.c
@@ -0,0 +1 @@
+../shared-core/radeon_ms_cp.c \ No newline at end of file
diff --git a/linux-core/radeon_ms_cp_mc.c b/linux-core/radeon_ms_cp_mc.c
new file mode 120000
index 00000000..0ae1a647
--- /dev/null
+++ b/linux-core/radeon_ms_cp_mc.c
@@ -0,0 +1 @@
+../shared-core/radeon_ms_cp_mc.c \ No newline at end of file
diff --git a/linux-core/radeon_ms_crtc.c b/linux-core/radeon_ms_crtc.c
new file mode 120000
index 00000000..31f11447
--- /dev/null
+++ b/linux-core/radeon_ms_crtc.c
@@ -0,0 +1 @@
+../shared-core/radeon_ms_crtc.c \ No newline at end of file
diff --git a/linux-core/radeon_ms_dac.c b/linux-core/radeon_ms_dac.c
new file mode 120000
index 00000000..cb523cfb
--- /dev/null
+++ b/linux-core/radeon_ms_dac.c
@@ -0,0 +1 @@
+../shared-core/radeon_ms_dac.c \ No newline at end of file
diff --git a/linux-core/radeon_ms_drm.c b/linux-core/radeon_ms_drm.c
new file mode 120000
index 00000000..8bbf19a5
--- /dev/null
+++ b/linux-core/radeon_ms_drm.c
@@ -0,0 +1 @@
+../shared-core/radeon_ms_drm.c \ No newline at end of file
diff --git a/linux-core/radeon_ms_drm.h b/linux-core/radeon_ms_drm.h
new file mode 120000
index 00000000..5d9d7319
--- /dev/null
+++ b/linux-core/radeon_ms_drm.h
@@ -0,0 +1 @@
+../shared-core/radeon_ms_drm.h \ No newline at end of file
diff --git a/linux-core/radeon_ms_drv.c b/linux-core/radeon_ms_drv.c
new file mode 100644
index 00000000..fc3c106a
--- /dev/null
+++ b/linux-core/radeon_ms_drv.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2007 Jerome Glisse.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * Authors:
+ * Jerome Glisse <glisse@freedesktop.org>
+ */
+#include "drm_pciids.h"
+#include "radeon_ms.h"
+
+extern struct drm_fence_driver r3xx_fence_driver;
+extern struct drm_bo_driver radeon_ms_bo_driver;
+extern struct drm_ioctl_desc radeon_ms_ioctls[];
+extern int radeon_ms_num_ioctls;
+
+static int radeon_ms_driver_dri_library_name(struct drm_device * dev,
+ char * buf);
+static int radeon_ms_driver_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent);
+
+static struct pci_device_id pciidlist[] = {
+ radeon_ms_PCI_IDS
+};
+
+static struct drm_driver driver = {
+ .load = radeon_ms_driver_load,
+ .firstopen = NULL,
+ .open = radeon_ms_driver_open,
+ .preclose = NULL,
+ .postclose = NULL,
+ .lastclose = radeon_ms_driver_lastclose,
+ .unload = radeon_ms_driver_unload,
+ .dma_ioctl = radeon_ms_driver_dma_ioctl,
+ .dma_ready = NULL,
+ .dma_quiescent = NULL,
+ .context_ctor = NULL,
+ .context_dtor = NULL,
+ .kernel_context_switch = NULL,
+ .kernel_context_switch_unlock = NULL,
+ .dri_library_name = radeon_ms_driver_dri_library_name,
+ .device_is_agp = NULL,
+ .irq_handler = radeon_ms_irq_handler,
+ .irq_preinstall = radeon_ms_irq_preinstall,
+ .irq_postinstall = radeon_ms_irq_postinstall,
+ .irq_uninstall = radeon_ms_irq_uninstall,
+ .reclaim_buffers = drm_core_reclaim_buffers,
+ .reclaim_buffers_locked = NULL,
+ .reclaim_buffers_idlelocked = NULL,
+ .get_map_ofs = drm_core_get_map_ofs,
+ .get_reg_ofs = drm_core_get_reg_ofs,
+ .set_version = NULL,
+ .fb_probe = radeonfb_probe,
+ .fb_remove = radeonfb_remove,
+ .fence_driver = &r3xx_fence_driver,
+ .bo_driver = &radeon_ms_bo_driver,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+ .patchlevel = DRIVER_PATCHLEVEL,
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .driver_features =
+ DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG |
+ DRIVER_HAVE_IRQ | DRIVER_HAVE_DMA | DRIVER_IRQ_SHARED,
+ .dev_priv_size = 0,
+ .ioctls = radeon_ms_ioctls,
+ .num_ioctls = 0,
+ .fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+ .ioctl = drm_ioctl,
+ .mmap = drm_mmap,
+ .poll = drm_poll,
+ .fasync = drm_fasync,
+#if defined(CONFIG_COMPAT) && LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9)
+ .compat_ioctl = radeon_ms_compat_ioctl,
+#endif
+ },
+ .pci_driver = {
+ .name = DRIVER_NAME,
+ .id_table = pciidlist,
+ .probe = radeon_ms_driver_probe,
+ .remove = __devexit_p(drm_cleanup_pci),
+ },
+};
+
+static int radeon_ms_driver_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ return drm_get_dev(pdev, ent, &driver);
+}
+
+static int radeon_ms_driver_dri_library_name(struct drm_device * dev,
+ char * buf)
+{
+ struct drm_radeon_private *dev_priv = dev->dev_private;
+ int ret;
+
+ switch (dev_priv->family) {
+ default:
+ ret = snprintf(buf, PAGE_SIZE, "\n");
+ }
+ return ret;
+}
+
+static void __exit radeon_ms_driver_exit(void)
+{
+ drm_exit(&driver);
+}
+
+static int __init radeon_ms_driver_init(void)
+{
+ driver.num_ioctls = radeon_ms_num_ioctls;
+ driver.driver_features |= DRIVER_MODESET;
+ return drm_init(&driver, pciidlist);
+}
+
+module_init(radeon_ms_driver_init);
+module_exit(radeon_ms_driver_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL and additional rights");
diff --git a/linux-core/radeon_ms_drv.h b/linux-core/radeon_ms_drv.h
new file mode 100644
index 00000000..b80ed442
--- /dev/null
+++ b/linux-core/radeon_ms_drv.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2007 Dave Airlie
+ * Copyright 2007 Alex Deucher
+ * Copyright 2007 Michel Dänzer
+ * Copyright 2007 Roland Scheidegger
+ * Copyright 2007 Vladimir Dergachev
+ * Copyright 2007 Nicolai Haehnle
+ * Copyright 2007 Aapo Tahkola
+ * Copyright 2007 Ben Skeggs
+ * Copyright 2007 Jérôme Glisse
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * Authors:
+ * Jérôme Glisse <glisse@freedesktop.org>
+ */
+#ifndef __RADEON_MS_DRV_H__
+#define __RADEON_MS_DRV_H__
+
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/pci.h>
+#include "drm.h"
+#include "drmP.h"
+
+#endif
diff --git a/linux-core/radeon_ms_exec.c b/linux-core/radeon_ms_exec.c
new file mode 120000
index 00000000..cb397fbf
--- /dev/null
+++ b/linux-core/radeon_ms_exec.c
@@ -0,0 +1 @@
+../shared-core/radeon_ms_exec.c \ No newline at end of file
diff --git a/linux-core/radeon_ms_family.c b/linux-core/radeon_ms_family.c
new file mode 120000
index 00000000..1f12e092
--- /dev/null
+++ b/linux-core/radeon_ms_family.c
@@ -0,0 +1 @@
+../shared-core/radeon_ms_family.c \ No newline at end of file
diff --git a/linux-core/radeon_ms_fb.c b/linux-core/radeon_ms_fb.c
new file mode 100644
index 00000000..0ac9207c
--- /dev/null
+++ b/linux-core/radeon_ms_fb.c
@@ -0,0 +1,462 @@
+/*
+ * Copyright © 2007 David Airlie
+ * Copyright © 2007 Jerome Glisse
+ *
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation on the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT. IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR
+ * THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "radeon_ms.h"
+#include "amd.h"
+
+
+static int radeonfb_setcolreg(unsigned regno, unsigned red,
+ unsigned green, unsigned blue,
+ unsigned transp, struct fb_info *info)
+{
+ struct amd_fb *par = info->par;
+ struct drm_framebuffer *fb = par->fb;
+ struct drm_crtc *crtc = par->crtc;
+
+ if (regno > 255) {
+ return 1;
+ }
+ // if (crtc->funcs->gamma_set) {
+ // crtc->funcs->gamma_set(crtc, red, green, blue, regno);
+ // }
+ if (regno < 16) {
+ switch (fb->depth) {
+ case 15:
+ fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) |
+ ((green & 0xf800) >> 6) |
+ ((blue & 0xf800) >> 11);
+ break;
+ case 16:
+ fb->pseudo_palette[regno] = (red & 0xf800) |
+ ((green & 0xfc00) >> 5) |
+ ((blue & 0xf800) >> 11);
+ break;
+ case 24:
+ case 32:
+ fb->pseudo_palette[regno] = ((red & 0xff00) << 8) |
+ (green & 0xff00) |
+ ((blue & 0xff00) >> 8);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int radeonfb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct amd_fb *par = info->par;
+ struct drm_framebuffer *fb = par->fb;
+
+ if (!var->pixclock)
+ return -EINVAL;
+
+ /* Need to resize the fb object !!! */
+ if (var->xres > fb->width || var->yres > fb->height) {
+ DRM_ERROR("Requested width/height is greater than "
+ "current fb object %dx%d > %dx%d\n",
+ var->xres, var->yres, fb->width, fb->height);
+ DRM_ERROR("Need resizing code.\n");
+ return -EINVAL;
+ }
+
+ switch (var->bits_per_pixel) {
+ case 16:
+ if (var->green.length == 5) {
+ var->red.offset = 10;
+ var->green.offset = 5;
+ var->blue.offset = 0;
+ var->red.length = 5;
+ var->green.length = 5;
+ var->blue.length = 5;
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ } else {
+ var->red.offset = 11;
+ var->green.offset = 6;
+ var->blue.offset = 0;
+ var->red.length = 5;
+ var->green.length = 6;
+ var->blue.length = 5;
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ }
+ break;
+ case 32:
+ if (var->transp.length) {
+ var->red.offset = 16;
+ var->green.offset = 8;
+ var->blue.offset = 0;
+ var->red.length = 8;
+ var->green.length = 8;
+ var->blue.length = 8;
+ var->transp.length = 8;
+ var->transp.offset = 24;
+ } else {
+ var->red.offset = 16;
+ var->green.offset = 8;
+ var->blue.offset = 0;
+ var->red.length = 8;
+ var->green.length = 8;
+ var->blue.length = 8;
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static bool radeonfb_mode_equal(struct drm_display_mode *mode1,
+ struct drm_display_mode *mode2)
+{
+ if (mode1->hdisplay == mode2->hdisplay &&
+ mode1->hsync_start == mode2->hsync_start &&
+ mode1->hsync_end == mode2->hsync_end &&
+ mode1->htotal == mode2->htotal &&
+ mode1->hskew == mode2->hskew &&
+ mode1->vdisplay == mode2->vdisplay &&
+ mode1->vsync_start == mode2->vsync_start &&
+ mode1->vsync_end == mode2->vsync_end &&
+ mode1->vtotal == mode2->vtotal &&
+ mode1->vscan == mode2->vscan &&
+ mode1->flags == mode2->flags) {
+ /* FIXME: what about adding a margin for clock ? */
+ if (mode1->clock == mode2->clock)
+ return true;
+ return false;
+ }
+
+ return false;
+}
+
+static int radeonfb_set_par(struct fb_info *info)
+{
+ struct amd_fb *par = info->par;
+ struct drm_framebuffer *fb = par->fb;
+ struct drm_device *dev = par->dev;
+ struct drm_display_mode *drm_mode, *search_mode;
+ struct drm_output *output;
+ struct fb_var_screeninfo *var = &info->var;
+ int found = 0;
+
+ switch (var->bits_per_pixel) {
+ case 16:
+ fb->depth = (var->green.length == 6) ? 16 : 15;
+ break;
+ case 32:
+ fb->depth = (var->transp.length > 0) ? 32 : 24;
+ break;
+ default:
+ return -EINVAL;
+ }
+ fb->bits_per_pixel = var->bits_per_pixel;
+
+ info->fix.line_length = fb->pitch;
+ info->fix.smem_len = info->fix.line_length * fb->height;
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ info->screen_size = info->fix.smem_len; /* ??? */
+
+ /* Should we walk the output's modelist or just create our own ???
+ * For now, we create and destroy a mode based on the incoming
+ * parameters. But there's commented out code below which scans
+ * the output list too.
+ */
+ drm_mode = drm_mode_create(dev);
+ drm_mode->hdisplay = var->xres;
+ drm_mode->hsync_start = drm_mode->hdisplay + var->right_margin;
+ drm_mode->hsync_end = drm_mode->hsync_start + var->hsync_len;
+ drm_mode->htotal = drm_mode->hsync_end + var->left_margin;
+ drm_mode->vdisplay = var->yres;
+ drm_mode->vsync_start = drm_mode->vdisplay + var->lower_margin;
+ drm_mode->vsync_end = drm_mode->vsync_start + var->vsync_len;
+ drm_mode->vtotal = drm_mode->vsync_end + var->upper_margin;
+ drm_mode->clock = PICOS2KHZ(var->pixclock);
+ drm_mode->vrefresh = drm_mode_vrefresh(drm_mode);
+ drm_mode_set_name(drm_mode);
+ drm_mode_set_crtcinfo(drm_mode, CRTC_INTERLACE_HALVE_V);
+
+ list_for_each_entry(output, &dev->mode_config.output_list, head) {
+ if (output->crtc == par->crtc)
+ break;
+ }
+
+ drm_mode_debug_printmodeline(drm_mode);
+ list_for_each_entry(search_mode, &output->modes, head) {
+ drm_mode_debug_printmodeline(search_mode);
+ if (radeonfb_mode_equal(drm_mode, search_mode)) {
+ drm_mode_destroy(dev, drm_mode);
+ drm_mode = search_mode;
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ if (par->fb_mode) {
+ drm_mode_detachmode_crtc(dev, par->fb_mode);
+ }
+ par->fb_mode = drm_mode;
+ drm_mode_debug_printmodeline(drm_mode);
+ /* attach mode */
+ drm_mode_attachmode_crtc(dev, par->crtc, par->fb_mode);
+ }
+
+ if (par->crtc->enabled) {
+ if (!drm_mode_equal(&par->crtc->mode, drm_mode) ||
+ par->crtc->fb != par->fb) {
+ par->crtc->fb = par->fb;
+ if (!drm_crtc_set_mode(par->crtc, drm_mode, 0, 0)) {
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static struct fb_ops radeonfb_ops = {
+ .owner = THIS_MODULE,
+ // .fb_open = radeonfb_open,
+ // .fb_read = radeonfb_read,
+ // .fb_write = radeonfb_write,
+ // .fb_release = radeonfb_release,
+ // .fb_ioctl = radeonfb_ioctl,
+ .fb_check_var = radeonfb_check_var,
+ .fb_set_par = radeonfb_set_par,
+ .fb_setcolreg = radeonfb_setcolreg,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+int radeonfb_probe(struct drm_device *dev, struct drm_crtc *crtc, struct drm_output *output)
+{
+ struct drm_radeon_private *dev_priv = dev->dev_private;
+ struct fb_info *info;
+ struct amd_fb *par;
+ struct device *device = &dev->pdev->dev;
+ struct drm_framebuffer *fb;
+ struct drm_display_mode *mode = crtc->desired_mode;
+ int ret;
+
+ info = framebuffer_alloc(sizeof(struct amd_fb), device);
+ if (!info){
+ DRM_INFO("[radeon_ms] framebuffer_alloc failed\n");
+ return -EINVAL;
+ }
+
+ fb = drm_framebuffer_create(dev);
+ if (!fb) {
+ framebuffer_release(info);
+ DRM_ERROR("[radeon_ms] failed to allocate fb.\n");
+ return -EINVAL;
+ }
+ crtc->fb = fb;
+
+ fb->width = crtc->desired_mode->hdisplay;
+ fb->height = crtc->desired_mode->vdisplay;
+ fb->bits_per_pixel = 32;
+ fb->pitch = fb->width * ((fb->bits_per_pixel + 1) / 8);
+ fb->depth = 24;
+ /* one page alignment should be fine for constraint (micro|macro tiling,
+ * bit depth, color buffer offset, ...) */
+ ret = drm_buffer_object_create(dev, fb->width * fb->height * 4,
+ drm_bo_type_kernel,
+ DRM_BO_FLAG_READ |
+ DRM_BO_FLAG_WRITE |
+ DRM_BO_FLAG_NO_EVICT |
+ DRM_BO_FLAG_MEM_VRAM,
+ DRM_BO_HINT_DONT_FENCE,
+ 1,
+ 0,
+ &fb->bo);
+ if (ret || fb->bo == NULL) {
+ DRM_ERROR("[radeon_ms] failed to allocate framebuffer\n");
+ drm_framebuffer_destroy(fb);
+ framebuffer_release(info);
+ return -EINVAL;
+ }
+
+ DRM_INFO("[radeon_ms] framebuffer %dx%d at 0x%08lX\n",
+ fb->width, fb->height, fb->bo->offset);
+
+ fb->fbdev = info;
+ par = info->par;
+ dev_priv->fb = par;
+ par->dev = dev;
+ par->crtc = crtc;
+ par->fb = fb;
+ info->fbops = &radeonfb_ops;
+ strcpy(info->fix.id, "radeonfb");
+ info->fix.type = FB_TYPE_PACKED_PIXELS;
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ info->fix.type_aux = 0;
+ info->fix.xpanstep = 8;
+ info->fix.ypanstep = 1;
+ info->fix.ywrapstep = 0;
+ info->fix.accel = FB_ACCEL_ATI_RADEON;
+ info->fix.type_aux = 0;
+ info->fix.mmio_start = 0;
+ info->fix.mmio_len = 0;
+ info->fix.line_length = fb->pitch;
+ info->fix.smem_start = fb->bo->offset + dev->mode_config.fb_base;
+ info->fix.smem_len = info->fix.line_length * fb->height;
+ info->flags = FBINFO_DEFAULT;
+ DRM_INFO("[radeon_ms] fb physical start : 0x%lX\n", info->fix.smem_start);
+ DRM_INFO("[radeon_ms] fb physical size : %d\n", info->fix.smem_len);
+
+ ret = drm_bo_kmap(fb->bo, 0, fb->bo->num_pages, &fb->kmap);
+ if (ret) {
+ DRM_ERROR("error mapping fb: %d\n", ret);
+ }
+ info->screen_base = fb->kmap.virtual;
+ info->screen_size = info->fix.smem_len; /* FIXME */
+ info->pseudo_palette = fb->pseudo_palette;
+ info->var.xres_virtual = fb->width;
+ info->var.yres_virtual = fb->height;
+ info->var.bits_per_pixel = fb->bits_per_pixel;
+ info->var.xoffset = 0;
+ info->var.yoffset = 0;
+ info->var.activate = FB_ACTIVATE_NOW;
+ info->var.height = -1;
+ info->var.width = -1;
+ info->var.vmode = FB_VMODE_NONINTERLACED;
+
+ info->var.xres = mode->hdisplay;
+ info->var.right_margin = mode->hsync_start - mode->hdisplay;
+ info->var.hsync_len = mode->hsync_end - mode->hsync_start;
+ info->var.left_margin = mode->htotal - mode->hsync_end;
+ info->var.yres = mode->vdisplay;
+ info->var.lower_margin = mode->vsync_start - mode->vdisplay;
+ info->var.vsync_len = mode->vsync_end - mode->vsync_start;
+ info->var.upper_margin = mode->vtotal - mode->vsync_end;
+ info->var.pixclock = 10000000 / mode->htotal * 1000 /
+ mode->vtotal * 100;
+ /* avoid overflow */
+ info->var.pixclock = info->var.pixclock * 1000 / mode->vrefresh;
+
+ info->pixmap.size = 64*1024;
+ info->pixmap.buf_align = 8;
+ info->pixmap.access_align = 32;
+ info->pixmap.flags = FB_PIXMAP_SYSTEM;
+ info->pixmap.scan_align = 1;
+
+ DRM_DEBUG("fb depth is %d\n", fb->depth);
+ DRM_DEBUG(" pitch is %d\n", fb->pitch);
+ switch(fb->depth) {
+ case 15:
+ info->var.red.offset = 10;
+ info->var.green.offset = 5;
+ info->var.blue.offset = 0;
+ info->var.red.length = info->var.green.length =
+ info->var.blue.length = 5;
+ info->var.transp.offset = 15;
+ info->var.transp.length = 1;
+ break;
+ case 16:
+ info->var.red.offset = 11;
+ info->var.green.offset = 5;
+ info->var.blue.offset = 0;
+ info->var.red.length = 5;
+ info->var.green.length = 6;
+ info->var.blue.length = 5;
+ info->var.transp.offset = 0;
+ break;
+ case 24:
+ info->var.red.offset = 16;
+ info->var.green.offset = 8;
+ info->var.blue.offset = 0;
+ info->var.red.length = info->var.green.length =
+ info->var.blue.length = 8;
+ info->var.transp.offset = 0;
+ info->var.transp.length = 0;
+ break;
+ case 32:
+ info->var.red.offset = 16;
+ info->var.green.offset = 8;
+ info->var.blue.offset = 0;
+ info->var.red.length = info->var.green.length =
+ info->var.blue.length = 8;
+ info->var.transp.offset = 24;
+ info->var.transp.length = 8;
+ break;
+ default:
+ DRM_ERROR("only support 15, 16, 24 or 32bits per pixel "
+ "got %d\n", fb->depth);
+ return -EINVAL;
+ break;
+ }
+
+ if (register_framebuffer(info) < 0) {
+ return -EINVAL;
+ }
+
+ DRM_INFO("[radeon_ms] fb%d: %s frame buffer device\n", info->node,
+ info->fix.id);
+ return 0;
+}
+EXPORT_SYMBOL(radeonfb_probe);
+
+int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *kern_fb)
+{
+ struct drm_radeon_private *dev_priv = dev->dev_private;
+ struct amd_fb *fb = dev_priv->fb;
+ struct fb_info *info;
+
+ if (fb == NULL || fb->fb == NULL || fb->fb->fbdev == NULL) {
+ DRM_INFO("[radeon_ms] %s: no crtc, or fb or fbdev\n",
+ __func__);
+ return 0;
+ }
+ info = fb->fb->fbdev;
+ unregister_framebuffer(info);
+ drm_bo_kunmap(&fb->fb->kmap);
+ drm_bo_usage_deref_unlocked(&fb->fb->bo);
+ drm_framebuffer_destroy(fb->fb);
+ framebuffer_release(info);
+ dev_priv->fb = NULL;
+ return 0;
+}
+EXPORT_SYMBOL(radeonfb_remove);
+MODULE_LICENSE("GPL");
diff --git a/linux-core/radeon_ms_fence.c b/linux-core/radeon_ms_fence.c
new file mode 120000
index 00000000..383cc070
--- /dev/null
+++ b/linux-core/radeon_ms_fence.c
@@ -0,0 +1 @@
+../shared-core/radeon_ms_fence.c \ No newline at end of file
diff --git a/linux-core/radeon_ms_gpu.c b/linux-core/radeon_ms_gpu.c
new file mode 120000
index 00000000..fa5e05bf
--- /dev/null
+++ b/linux-core/radeon_ms_gpu.c
@@ -0,0 +1 @@
+../shared-core/radeon_ms_gpu.c \ No newline at end of file
diff --git a/linux-core/radeon_ms_i2c.c b/linux-core/radeon_ms_i2c.c
new file mode 120000
index 00000000..1863e6d6
--- /dev/null
+++ b/linux-core/radeon_ms_i2c.c
@@ -0,0 +1 @@
+../shared-core/radeon_ms_i2c.c \ No newline at end of file
diff --git a/linux-core/radeon_ms_irq.c b/linux-core/radeon_ms_irq.c
new file mode 120000
index 00000000..c4e60ba6
--- /dev/null
+++ b/linux-core/radeon_ms_irq.c
@@ -0,0 +1 @@
+../shared-core/radeon_ms_irq.c \ No newline at end of file
diff --git a/linux-core/radeon_ms_output.c b/linux-core/radeon_ms_output.c
new file mode 120000
index 00000000..6a38b671
--- /dev/null
+++ b/linux-core/radeon_ms_output.c
@@ -0,0 +1 @@
+../shared-core/radeon_ms_output.c \ No newline at end of file
diff --git a/linux-core/radeon_ms_properties.c b/linux-core/radeon_ms_properties.c
new file mode 120000
index 00000000..e2e0dc0b
--- /dev/null
+++ b/linux-core/radeon_ms_properties.c
@@ -0,0 +1 @@
+../shared-core/radeon_ms_properties.c \ No newline at end of file
diff --git a/linux-core/radeon_ms_properties.h b/linux-core/radeon_ms_properties.h
new file mode 120000
index 00000000..59783e89
--- /dev/null
+++ b/linux-core/radeon_ms_properties.h
@@ -0,0 +1 @@
+../shared-core/radeon_ms_properties.h \ No newline at end of file
diff --git a/linux-core/radeon_ms_reg.h b/linux-core/radeon_ms_reg.h
new file mode 120000
index 00000000..24b01b42
--- /dev/null
+++ b/linux-core/radeon_ms_reg.h
@@ -0,0 +1 @@
+../shared-core/radeon_ms_reg.h \ No newline at end of file
diff --git a/linux-core/radeon_ms_rom.c b/linux-core/radeon_ms_rom.c
new file mode 120000
index 00000000..80f5f606
--- /dev/null
+++ b/linux-core/radeon_ms_rom.c
@@ -0,0 +1 @@
+../shared-core/radeon_ms_rom.c \ No newline at end of file
diff --git a/linux-core/radeon_ms_rom.h b/linux-core/radeon_ms_rom.h
new file mode 120000
index 00000000..f20e42be
--- /dev/null
+++ b/linux-core/radeon_ms_rom.h
@@ -0,0 +1 @@
+../shared-core/radeon_ms_rom.h \ No newline at end of file
diff --git a/linux-core/radeon_ms_state.c b/linux-core/radeon_ms_state.c
new file mode 120000
index 00000000..2d2e2ef7
--- /dev/null
+++ b/linux-core/radeon_ms_state.c
@@ -0,0 +1 @@
+../shared-core/radeon_ms_state.c \ No newline at end of file
diff --git a/linux-core/via_fence.c b/linux-core/via_fence.c
index 3a680a32..6473e701 100644
--- a/linux-core/via_fence.c
+++ b/linux-core/via_fence.c
@@ -63,7 +63,7 @@ static void via_fence_poll(struct drm_device *dev, uint32_t class,
if (!dev_priv->have_idlelock) {
- drm_idlelock_take(&dev->lock);
+ drm_idlelock_take(&dev->primary->master->lock);
dev_priv->have_idlelock = 1;
}
@@ -89,7 +89,7 @@ static void via_fence_poll(struct drm_device *dev, uint32_t class,
if (signaled_flush_types) {
waiting_types &= ~signaled_flush_types;
if (!waiting_types && dev_priv->have_idlelock) {
- drm_idlelock_release(&dev->lock);
+ drm_idlelock_release(&dev->primary->master->lock);
dev_priv->have_idlelock = 0;
}
drm_fence_handler(dev, 0, dev_priv->emit_0_sequence,