diff options
42 files changed, 10933 insertions, 202 deletions
| diff --git a/libdrm/Makefile.am b/libdrm/Makefile.am index e7e07e47..24c32038 100644 --- a/libdrm/Makefile.am +++ b/libdrm/Makefile.am @@ -23,9 +23,9 @@ libdrm_ladir = $(libdir)  libdrm_la_LDFLAGS = -version-number 2:3:0 -no-undefined  AM_CFLAGS = -I$(top_srcdir)/shared-core -libdrm_la_SOURCES = xf86drm.c xf86drmHash.c xf86drmRandom.c xf86drmSL.c +libdrm_la_SOURCES = xf86drm.c xf86drmHash.c xf86drmRandom.c xf86drmSL.c xf86drmMode.c  libdrmincludedir = ${includedir} -libdrminclude_HEADERS = xf86drm.h xf86mm.h +libdrminclude_HEADERS = xf86drm.h xf86mm.h xf86drmMode.h  EXTRA_DIST = ChangeLog TODO diff --git a/libdrm/xf86drmMode.c b/libdrm/xf86drmMode.c new file mode 100644 index 00000000..a7241ffd --- /dev/null +++ b/libdrm/xf86drmMode.c @@ -0,0 +1,395 @@ +/* + * \file xf86drmMode.c + * Header for DRM modesetting interface. + * + * \author Jakob Bornecrantz <wallbraker@gmail.com> + * + * \par Acknowledgements: + * Feb 2007, Dave Airlie <airlied@linux.ie> + */ + +/* + * Copyright (c) <year> <copyright holders> + * + * 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 + * 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. + * + */ + +/* + * TODO the types we are after are defined in diffrent headers on diffrent + * platforms find which headers to include to get uint32_t + */ +#include <stdint.h> + +#include "xf86drmMode.h" +#include "xf86drm.h" +#include <drm.h> +#include <string.h> + +/* + * Util functions + */ + +void* drmAllocCpy(void *array, int count, int entry_size) +{ +	char *r; +	int i; + +	if (!count || !array || !entry_size) +		return 0; + +	if (!(r = drmMalloc(count*entry_size))) +		return 0; + +	for (i = 0; i < count; i++) +		memcpy(r+(entry_size*i), array+(entry_size*i), entry_size); + +	return r; +} + +/** + * Generate crtc and output ids. + * + * Will generate ids starting from 1 up to count if count is greater then 0. + */ +static uint32_t* drmAllocGenerate(int count) +{ +	uint32_t *r; +	int i; + +	if(0 <= count) +		return 0; + +	if (!(r = drmMalloc(count*sizeof(*r)))) +		return 0; + +	for (i = 0; i < count; r[i] = ++i); + +	return 0; +} + +/* + * A couple of free functions. + */ + +void drmModeFreeModeInfo(struct drm_mode_modeinfo *ptr) +{ +	if (!ptr) +		return; + +	drmFree(ptr); +} + +void drmModeFreeResources(drmModeResPtr ptr) +{ +	if (!ptr) +		return; + +	drmFree(ptr->modes); +	drmFree(ptr); + +} + +void drmModeFreeFB(drmModeFBPtr ptr) +{ +	if (!ptr) +		return; + +	/* we might add more frees later. */ +	drmFree(ptr); +} + +void drmModeFreeCrtc(drmModeCrtcPtr ptr) +{ +	if (!ptr) +		return; + +	drmFree(ptr); + +} + +void drmModeFreeOutput(drmModeOutputPtr ptr) +{ +	if (!ptr) +		return; + +	drmFree(ptr->modes); +	drmFree(ptr); + +} + +/* + * ModeSetting functions. + */ + +drmModeResPtr drmModeGetResources(int fd) +{ +	struct drm_mode_card_res res; +	int i; +	drmModeResPtr r = 0; + +	memset(&res, 0, sizeof(struct drm_mode_card_res)); + +	if (ioctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res)) +		return 0; + +	if (res.count_fbs) +		res.fb_id = drmMalloc(res.count_fbs*sizeof(uint32_t)); +	if (res.count_crtcs) +		res.crtc_id = drmMalloc(res.count_crtcs*sizeof(uint32_t)); +	if (res.count_outputs) +		res.output_id = drmMalloc(res.count_outputs*sizeof(uint32_t)); +	if (res.count_modes) +		res.modes = drmMalloc(res.count_modes*sizeof(*res.modes)); + +	if (ioctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res)) { +		r = NULL; +		goto err_allocs; +	} + +	/* +	 * return +	 */ + + +	if (!(r = drmMalloc(sizeof(*r)))) +		return 0; + +	r->count_fbs     = res.count_fbs; +	r->count_crtcs   = res.count_crtcs; +	r->count_outputs = res.count_outputs; +	r->count_modes   = res.count_modes; +	/* TODO we realy should test if these allocs fails. */ +	r->fbs           = drmAllocCpy(res.fb_id, res.count_fbs, sizeof(uint32_t)); +	r->crtcs         = drmAllocCpy(res.crtc_id, res.count_crtcs, sizeof(uint32_t)); +	r->outputs       = drmAllocCpy(res.output_id, res.count_outputs, sizeof(uint32_t)); +	r->modes         = drmAllocCpy(res.modes, res.count_modes, sizeof(struct drm_mode_modeinfo)); + +err_allocs: +	drmFree(res.fb_id); +	drmFree(res.crtc_id); +	drmFree(res.output_id); +	drmFree(res.modes); + +	return r; +} + +int drmModeAddFB(int fd, uint32_t width, uint32_t height, uint8_t depth, +                 uint8_t bpp, uint32_t pitch, drmBO *bo, uint32_t *buf_id) +{ +	struct drm_mode_fb_cmd f; +	int ret; + +	f.width  = width; +	f.height = height; +	f.pitch  = pitch; +	f.bpp    = bpp; +	f.depth  = depth; +	f.handle = bo->handle; + +	if (ret = ioctl(fd, DRM_IOCTL_MODE_ADDFB, &f)) +		return ret; + +	*buf_id = f.buffer_id; +	return 0; +} + +int drmModeRmFB(int fd, uint32_t bufferId) +{ +	return ioctl(fd, DRM_IOCTL_MODE_RMFB, bufferId); +} + +drmModeFBPtr drmModeGetFB(int fd, uint32_t buf) +{ +	struct drm_mode_fb_cmd info; +	drmModeFBPtr r; + +	info.buffer_id = buf; + +	if (ioctl(fd, DRM_IOCTL_MODE_GETFB, &info)) +		return NULL; + +	if (!(r = drmMalloc(sizeof(*r)))) +		return NULL; + +	r->buffer_id = info.buffer_id; +	r->width = info.width; +	r->height = info.height; +	r->pitch = info.pitch; +	r->bpp = info.bpp; +	r->handle = info.handle; +	r->depth = info.depth; + +	return r; +} + + +/* + * Crtc functions + */ + +drmModeCrtcPtr drmModeGetCrtc(int fd, uint32_t crtcId) +{ +	struct drm_mode_crtc crtc; +	drmModeCrtcPtr r; +	int i = 0; + +	crtc.count_outputs   = 0; +	crtc.outputs         = 0; +	crtc.count_possibles = 0; +	crtc.possibles       = 0; +	crtc.crtc_id = crtcId; + +	if (ioctl(fd, DRM_IOCTL_MODE_GETCRTC, &crtc)) +		return 0; + +	/* +	 * return +	 */ + +	if (!(r = drmMalloc(sizeof(*r)))) +		return 0; + +	r->x               = crtc.x; +	r->y               = crtc.y; +	r->mode            = crtc.mode; +	r->buffer_id       = crtc.fb_id; +	r->gamma_size      = crtc.gamma_size; +	r->count_outputs   = crtc.count_outputs; +	r->count_possibles = crtc.count_possibles; +	/* TODO we realy should test if these alloc & cpy fails. */ +	r->outputs         = crtc.outputs; +	r->possibles       = crtc.possibles; + +	return r; + +err_allocs: + +	return 0; +} + + +int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId, +                   uint32_t x, uint32_t y, uint32_t modeId, +                   uint32_t *outputs, int count) +{ +	struct drm_mode_crtc crtc; + +	crtc.count_outputs   = 0; +	crtc.outputs         = 0; +	crtc.count_possibles = 0; +	crtc.possibles       = 0; + +	crtc.x             = x; +	crtc.y             = y; +	crtc.crtc_id       = crtcId; +	crtc.fb_id         = bufferId; +	crtc.set_outputs   = outputs; +	crtc.count_outputs = count; +	crtc.mode          = modeId; + +	return ioctl(fd, DRM_IOCTL_MODE_SETCRTC, &crtc); +} + + +/* + * Output manipulation + */ + +drmModeOutputPtr drmModeGetOutput(int fd, uint32_t output_id) +{ +	struct drm_mode_get_output out; +	drmModeOutputPtr r = 0; + +	out.output = output_id; +	out.count_crtcs  = 0; +	out.crtcs        = 0; +	out.count_clones = 0; +	out.clones       = 0; +	out.count_modes  = 0; +	out.modes        = 0; + +	if (ioctl(fd, DRM_IOCTL_MODE_GETOUTPUT, &out)) +		return 0; + +	if (out.count_modes) +		out.modes = drmMalloc(out.count_modes*sizeof(uint32_t)); + +	if (ioctl(fd, DRM_IOCTL_MODE_GETOUTPUT, &out)) +		goto err_allocs; + +	if(!(r = drmMalloc(sizeof(*r)))) +		return 0; + +	r->connection   = out.connection; +	r->mmWidth      = out.mm_width; +	r->mmHeight     = out.mm_height; +	r->subpixel     = out.subpixel; +	r->count_crtcs  = out.count_crtcs; +	r->count_clones = out.count_clones; +	r->count_modes  = out.count_modes; +	/* TODO we should test if these alloc & cpy fails. */ +	r->crtcs        = out.crtcs; +	r->clones       = out.clones; +	r->modes        = drmAllocCpy(out.modes, out.count_modes, sizeof(uint32_t)); +	strncpy(r->name, out.name, DRM_OUTPUT_NAME_LEN); +	r->name[DRM_OUTPUT_NAME_LEN-1] = 0; +	return r; + +err_allocs: +	drmFree(out.modes); + +	return 0; +} + +uint32_t drmModeAddMode(int fd, struct drm_mode_modeinfo *mode_info) +{ +	if (ioctl(fd, DRM_IOCTL_MODE_ADDMODE, mode_info)) +		return 0; +	 +	return mode_info->id; +} + +int drmModeRmMode(int fd, uint32_t mode_id) +{ +  	return ioctl(fd, DRM_IOCTL_MODE_RMMODE, mode_id); +} + +int drmModeAttachMode(int fd, uint32_t output_id, uint32_t mode_id) +{ + +	struct drm_mode_mode_cmd res; + +	res.output_id = output_id; +	res.mode_id = mode_id; + +	return ioctl(fd, DRM_IOCTL_MODE_ATTACHMODE, &res); +} + +int drmModeDetachMode(int fd, uint32_t output_id, uint32_t mode_id) +{ +	struct drm_mode_mode_cmd res; + +	res.output_id = output_id; +	res.mode_id = mode_id; + +	return ioctl(fd, DRM_IOCTL_MODE_DETACHMODE, &res); +} + + diff --git a/libdrm/xf86drmMode.h b/libdrm/xf86drmMode.h new file mode 100644 index 00000000..b25a6610 --- /dev/null +++ b/libdrm/xf86drmMode.h @@ -0,0 +1,208 @@ +/* + * \file xf86drmMode.h + * Header for DRM modesetting interface. + * + * \author Jakob Bornecrantz <wallbraker@gmail.com> + * + * \par Acknowledgements: + * Feb 2007, Dave Airlie <airlied@linux.ie> + */ + +/* + * Copyright (c) <year> <copyright holders> + * + * 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 + * 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 <drm.h> +#include "xf86mm.h" + +/* + * This is the interface for modesetting for drm. + * + * In order to use this interface you must include either <stdint.h> or another + * header defining uint32_t, int32_t and uint16_t. + * + * It aims to provide a randr1.2 compatible interface for modesettings in the + * kernel, the interface is also ment to be used by libraries like EGL. + * + * More information can be found in randrproto.txt which can be found here: + * http://gitweb.freedesktop.org/?p=xorg/proto/randrproto.git + * + * There are some major diffrences to be noted. Unlike the randr1.2 proto you + * need to create the memory object of the framebuffer yourself with the ttm + * buffer object interface. This object needs to be pinned. + */ + + +typedef struct _drmModeRes { + +	int count_fbs; +	uint32_t *fbs; + +	int count_crtcs; +	uint32_t *crtcs; + +	int count_outputs; +	uint32_t *outputs; + +	int count_modes; +	struct drm_mode_modeinfo *modes; + +} drmModeRes, *drmModeResPtr; + +typedef struct drm_mode_fb_cmd drmModeFB, *drmModeFBPtr; + +typedef struct _drmModeCrtc { + +	unsigned int buffer_id; /**< FB id to connect to 0 = disconnect*/ + +	uint32_t x, y; /**< Position on the frameuffer */ +	uint32_t width, height; +	uint32_t mode; /**< Current mode used */ + +	int count_outputs; +	uint32_t outputs; /**< Outputs that are connected */ + +	int count_possibles; +	uint32_t possibles; /**< Outputs that can be connected */ + +	int gamma_size; /**< Number of gamma stops */ + +} drmModeCrtc, *drmModeCrtcPtr; + +typedef enum { +	DRM_MODE_CONNECTED         = 1, +	DRM_MODE_DISCONNECTED      = 2, +	DRM_MODE_UNKNOWNCONNECTION = 3 +} drmModeConnection; + +typedef enum { +	DRM_MODE_SUBPIXEL_UNKNOWN        = 1, +	DRM_MODE_SUBPIXEL_HORIZONTAL_RGB = 2, +	DRM_MODE_SUBPIXEL_HORIZONTAL_BGR = 3, +	DRM_MODE_SUBPIXEL_VERTICAL_RGB   = 4, +	DRM_MODE_SUBPIXEL_VERTICAL_BGR   = 5, +	DRM_MODE_SUBPIXEL_NONE           = 6 +} drmModeSubPixel; + +typedef struct _drmModeOutput { + +	unsigned int crtc; /**< Crtc currently connected to */ +	unsigned char name[DRM_OUTPUT_NAME_LEN]; +	drmModeConnection connection; +	uint32_t mmWidth, mmHeight; /**< HxW in millimeters */ +	drmModeSubPixel subpixel; + +	int count_crtcs; +	uint32_t crtcs; /**< Possible crtc to connect to */ + +	int count_clones; +	uint32_t clones; /**< Mask of clones */ + +	int count_modes; +	uint32_t *modes; /**< List of modes ids */ + +} drmModeOutput, *drmModeOutputPtr; + + + +extern void drmModeFreeModeInfo( struct drm_mode_modeinfo *ptr ); +extern void drmModeFreeResources( drmModeResPtr ptr ); +extern void drmModeFreeFB( drmModeFBPtr ptr ); +extern void drmModeFreeCrtc( drmModeCrtcPtr ptr ); +extern void drmModeFreeOutput( drmModeOutputPtr ptr ); + +/** + * Retrives all of the resources associated with a card. + */ +extern drmModeResPtr drmModeGetResources(int fd); + + +/* + * FrameBuffer manipulation. + */ + +/** + * Retrive information about framebuffer bufferId + */ +extern drmModeFBPtr drmModeGetFB(int fd, uint32_t bufferId); + +/** + * Creates a new framebuffer with an buffer object as its scanout buffer. + */ +extern int drmModeAddFB(int fd, uint32_t width, uint32_t height, uint8_t depth, +			uint8_t bpp, uint32_t pitch, drmBO *bo, +			uint32_t *buf_id); +/** + * Destroies the given framebuffer. + */ +extern int drmModeRmFB(int fd, uint32_t bufferId); + + +/* + * Crtc functions + */ + +/** + * Retrive information about the ctrt crtcId + */ +extern drmModeCrtcPtr drmModeGetCrtc(int fd, uint32_t crtcId); + +/** + * Set the mode on a crtc crtcId with the given mode modeId. + */ +extern int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId, +		uint32_t x, uint32_t y, uint32_t modeId, +		uint32_t *outputs, int count); + + +/* + * Output manipulation + */ + +/** + * Retrive information about the output outputId. + */ +extern drmModeOutputPtr drmModeGetOutput(int fd, +		uint32_t outputId); + +/** + * Adds a new mode from the given mode info. + * Name must be unique. + */ +extern uint32_t drmModeAddMode(int fd, struct drm_mode_modeinfo *modeInfo); + +/** + * Removes a mode created with AddMode, must be unused. + */ +extern int drmModeRmMode(int fd, uint32_t modeId); + +/** + * Attaches the given mode to an output. + */ +extern int drmModeAttachMode(int fd, uint32_t outputId, uint32_t modeId); + +/** + * Detaches a mode from the output + * must be unused, by the given mode. + */ +extern int drmModeDetachMode(int fd, uint32_t outputId, uint32_t modeId); + diff --git a/linux-core/Makefile.kernel b/linux-core/Makefile.kernel index 79136431..457e3f3c 100644 --- a/linux-core/Makefile.kernel +++ b/linux-core/Makefile.kernel @@ -13,13 +13,15 @@ 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_mm.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_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  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_buffer.o intel_display.o intel_crt.o intel_lvds.o \ +		intel_sdvo.o intel_modes.o intel_i2c.o i915_init.o intel_fb.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 \ diff --git a/linux-core/drmP.h b/linux-core/drmP.h index d0ab2c94..0237f593 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -167,6 +167,8 @@ struct drm_file;  #include "drm_compat.h" +#include "drm_crtc.h" +  /***********************************************************************/  /** \name Macros to make printk easier */  /*@{*/ @@ -432,6 +434,8 @@ struct drm_file {  	struct drm_open_hash refd_object_hash[_DRM_NO_REF_TYPES];  	struct file *filp;  	void *driver_priv; + +	struct list_head fbs;  };  /** Wait queue */ @@ -659,6 +663,10 @@ struct drm_driver {  	unsigned long (*get_reg_ofs) (struct drm_device * dev);  	void (*set_version) (struct drm_device * dev, struct drm_set_version * sv); +	/* FB routines, if present */ +	int (*fb_probe)(struct drm_device *dev, struct drm_crtc *crtc); +	int (*fb_remove)(struct drm_device *dev, struct drm_crtc *crtc); +  	struct drm_fence_driver *fence_driver;  	struct drm_bo_driver *bo_driver; @@ -827,6 +835,9 @@ struct drm_device {  	spinlock_t drw_lock;  	struct idr drw_idr;  	/*@} */ + +	/* DRM mode setting */ +	struct drm_mode_config mode_config;  };  #if __OS_HAS_AGP diff --git a/linux-core/drm_bo.c b/linux-core/drm_bo.c index 8d1e2f56..b6a972ed 100644 --- a/linux-core/drm_bo.c +++ b/linux-core/drm_bo.c @@ -415,7 +415,6 @@ static void drm_bo_destroy_locked(struct drm_buffer_object * bo)  		atomic_dec(&bm->count); -		//		BUG_ON(!list_empty(&bo->base.list));  		drm_ctl_free(bo, sizeof(*bo), DRM_MEM_BUFOBJ);  		return; @@ -2191,6 +2190,7 @@ int drm_bo_driver_finish(struct drm_device * dev)  	mutex_unlock(&dev->struct_mutex);  	return ret;  } +EXPORT_SYMBOL(drm_bo_driver_finish);  /*   * This function is intended to be called on drm driver load. diff --git a/linux-core/drm_bo_move.c b/linux-core/drm_bo_move.c index 2a35d45b..d6c58970 100644 --- a/linux-core/drm_bo_move.c +++ b/linux-core/drm_bo_move.c @@ -149,6 +149,7 @@ void drm_mem_reg_iounmap(struct drm_device * dev, struct drm_bo_mem_reg * mem,  		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 60eca60c..dc7f342a 100644 --- a/linux-core/drm_bufs.c +++ b/linux-core/drm_bufs.c @@ -54,7 +54,7 @@ struct drm_map_list *drm_find_matching_map(struct drm_device *dev, drm_local_map  	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))) { +		     ((map->type == _DRM_SHM) && (map->flags&_DRM_CONTAINS_LOCK)))) {  			return entry;  		}  	} @@ -407,6 +407,7 @@ int drm_rmmap_locked(struct drm_device *dev, drm_local_map_t *map)  		break;  	case _DRM_SHM:  		vfree(map->handle); +		dev->sigdata.lock = dev->lock.hw_lock = NULL;   /* SHM removed */  		break;  	case _DRM_AGP:  	case _DRM_SCATTER_GATHER: diff --git a/linux-core/drm_compat.h b/linux-core/drm_compat.h index f74f4bc2..0895e5e5 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 diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c new file mode 100644 index 00000000..5bc02206 --- /dev/null +++ b/linux-core/drm_crtc.c @@ -0,0 +1,1872 @@ +/* + * Copyright (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 <linux/list.h> +#include "drm.h" +#include "drmP.h" +#include "drm_crtc.h" + +/** + * 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 outputs. + * + * RETURNS: + * New unique (relative to other objects in @dev) integer identifier for the + * object. + */ +int drm_idr_get(struct drm_device *dev, void *ptr) +{ +	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 0; +	} + +	ret = idr_get_new_above(&dev->mode_config.crtc_idr, ptr, 1, &new_id); +	if (ret == -EAGAIN) +		goto again;	 + +	return new_id; +} + +/** + * drm_idr_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. + */ +void drm_idr_put(struct drm_device *dev, int id) +{ +	idr_remove(&dev->mode_config.crtc_idr, id); +} + +/** + * 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_create(struct drm_device *dev) +{ +	struct drm_framebuffer *fb; + +	/* Limit to single framebuffer for now */ +	if (dev->mode_config.num_fb > 1) { +		mutex_unlock(&dev->mode_config.mutex); +		DRM_ERROR("Attempt to add multiple framebuffers failed\n"); +		return NULL; +	} + +	fb = kzalloc(sizeof(struct drm_framebuffer), GFP_KERNEL); +	if (!fb) +		return NULL; +	 +	fb->id = drm_idr_get(dev, fb); +	fb->dev = dev; +	dev->mode_config.num_fb++; +	list_add(&fb->head, &dev->mode_config.fb_list); + +	return fb; +} +EXPORT_SYMBOL(drm_framebuffer_create); + +/** + * drm_framebuffer_destroy - 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_destroy(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_idr_put(dev, fb->id); +	list_del(&fb->head); +	dev->mode_config.num_fb--; + +	kfree(fb); +} +EXPORT_SYMBOL(drm_framebuffer_destroy); + +/** + * drm_crtc_create - create a new CRTC object + * @dev: DRM device + * @funcs: callbacks for the new CRTC + * + * LOCKING: + * Caller must hold mode config lock. + * + * Creates a new CRTC object and adds it to @dev's mode_config structure. + * + * RETURNS: + * Pointer to new CRTC object or NULL on error. + */ +struct drm_crtc *drm_crtc_create(struct drm_device *dev, +				 const struct drm_crtc_funcs *funcs) +{ +	struct drm_crtc *crtc; + +	crtc = kzalloc(sizeof(struct drm_crtc), GFP_KERNEL); +	if (!crtc) +		return NULL; + +	crtc->dev = dev; +	crtc->funcs = funcs; + +	crtc->id = drm_idr_get(dev, crtc); + +	list_add_tail(&crtc->head, &dev->mode_config.crtc_list); +	dev->mode_config.num_crtc++; + +	return crtc; +} +EXPORT_SYMBOL(drm_crtc_create); + +/** + * drm_crtc_destroy - remove a CRTC object + * @crtc: CRTC to remove + * + * LOCKING: + * Caller must hold mode config lock. + * + * Cleanup @crtc.  Calls @crtc's cleanup function, then removes @crtc from + * its associated DRM device's mode_config.  Frees it afterwards. + */ +void drm_crtc_destroy(struct drm_crtc *crtc) +{ +	struct drm_device *dev = crtc->dev; + +	if (crtc->funcs->cleanup) +		(*crtc->funcs->cleanup)(crtc); + +	drm_idr_put(dev, crtc->id); +	list_del(&crtc->head); +	dev->mode_config.num_crtc--; +	kfree(crtc); +} +EXPORT_SYMBOL(drm_crtc_destroy); + +/** + * drm_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_crtc_in_use(struct drm_crtc *crtc) +{ +	struct drm_output *output; +	struct drm_device *dev = crtc->dev; +	/* FIXME: Locking around list access? */ +	list_for_each_entry(output, &dev->mode_config.output_list, head) +		if (output->crtc == crtc) +			return true; +	return false; +} +EXPORT_SYMBOL(drm_crtc_in_use); + +/* + * 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_crtc_probe_output_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 outputs and try to detect + * modes on them.  Modes will first be added to the output'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_crtc_probe_output_modes(struct drm_device *dev, int maxX, int maxY) +{ +	struct drm_output *output; +	struct drm_display_mode *mode, *t; +	int ret; +	//if (maxX == 0 || maxY == 0)  +	// TODO + +	list_for_each_entry(output, &dev->mode_config.output_list, head) { + +		/* set all modes to the unverified state */ +		list_for_each_entry_safe(mode, t, &output->modes, head) +			mode->status = MODE_UNVERIFIED; +		 +		output->status = (*output->funcs->detect)(output); + +		if (output->status == output_status_disconnected) { +			DRM_DEBUG("%s is disconnected\n", output->name); +			/* TODO set EDID to NULL */ +			continue; +		} + +		ret = (*output->funcs->get_modes)(output); + +		if (ret) { +			drm_mode_output_list_update(output); +		} + +		if (maxX && maxY) +			drm_mode_validate_size(dev, &output->modes, maxX, +					       maxY, 0); +		list_for_each_entry_safe(mode, t, &output->modes, head) { +			if (mode->status == MODE_OK) +				mode->status = (*output->funcs->mode_valid)(output,mode); +		} +		 + +		drm_mode_prune_invalid(dev, &output->modes, TRUE); + +		if (list_empty(&output->modes)) { +			struct drm_display_mode *stdmode; + +			DRM_DEBUG("No valid modes on %s\n", output->name); + +			/* 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(output, stdmode); +			drm_mode_list_concat(&output->probed_modes, +					     &output->modes); + +			DRM_DEBUG("Adding standard 640x480 @ 60Hz to %s\n", +								output->name); +		} + +		drm_mode_sort(&output->modes); + +		DRM_DEBUG("Probed modes for %s\n", output->name); +		list_for_each_entry_safe(mode, t, &output->modes, head) { +			mode->vrefresh = drm_mode_vrefresh(mode); + +			drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); +			drm_mode_debug_printmodeline(dev, mode); +		} +	} +} + +/** + * 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 outputs 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_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; +	int saved_x, saved_y; +	bool didLock = false; +	bool ret = false; +	struct drm_output *output; + +	adjusted_mode = drm_mode_duplicate(dev, mode); + +	crtc->enabled = drm_crtc_in_use(crtc); + +	if (!crtc->enabled) { +		return true; +	} + +	didLock = crtc->funcs->lock(crtc); + +	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; + +	/* XXX short-circuit changes to base location only */ +	 +	/* Pass our mode to the outputs and the CRTC to give them a chance to +	 * adjust it according to limitations or output properties, and also +	 * a chance to reject the mode entirely. +	 */ +	list_for_each_entry(output, &dev->mode_config.output_list, head) { +		 +		if (output->crtc != crtc) +			continue; +		 +		if (!output->funcs->mode_fixup(output, mode, adjusted_mode)) { +			goto done; +		} +	} +	 +	if (!crtc->funcs->mode_fixup(crtc, mode, adjusted_mode)) { +		goto done; +	} + +	/* Prepare the outputs and CRTCs before setting the mode. */ +	list_for_each_entry(output, &dev->mode_config.output_list, head) { + +		if (output->crtc != crtc) +			continue; +		 +		/* Disable the output as the first thing we do. */ +		output->funcs->prepare(output); +	} +	 +	crtc->funcs->prepare(crtc); +	 +	/* Set up the DPLL and any output state that needs to adjust or depend +	 * on the DPLL. +	 */ +	crtc->funcs->mode_set(crtc, mode, adjusted_mode, x, y); + +	list_for_each_entry(output, &dev->mode_config.output_list, head) { + +		if (output->crtc != crtc) +			continue; +		 +		DRM_INFO("%s: set mode %s\n", output->name, mode->name); + +		output->funcs->mode_set(output, mode, adjusted_mode); +	} +	 +	/* Now, enable the clocks, plane, pipe, and outputs that we set up. */ +	crtc->funcs->commit(crtc); + +	list_for_each_entry(output, &dev->mode_config.output_list, head) { + +		if (output->crtc != crtc) +			continue; +		 +		output->funcs->commit(output); + +#if 0 // TODO def RANDR_12_INTERFACE +		if (output->randr_output) +			RRPostPendingProperties (output->randr_output); +#endif +	} +	 +	/* XXX free adjustedmode */ +	drm_mode_destroy(dev, adjusted_mode); +	ret = TRUE; +	/* TODO */ +//	if (scrn->pScreen) +//		drm_crtc_set_screen_sub_pixel_order(dev); + +done: +	if (!ret) { +		crtc->x = saved_x; +		crtc->y = saved_y; +		crtc->mode = saved_mode; +	} +	 +	if (didLock) +		crtc->funcs->unlock (crtc); +	 +	return ret; +} +EXPORT_SYMBOL(drm_crtc_set_mode); + +/** + * drm_disable_unused_functions - disable unused objects + * @dev: DRM device + * + * LOCKING: + * Caller must hold mode config lock. + * + * If an output 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_disable_unused_functions(struct drm_device *dev) +{ +	struct drm_output *output; +	struct drm_crtc *crtc; + +	list_for_each_entry(output, &dev->mode_config.output_list, head) { +		if (!output->crtc) +			(*output->funcs->dpms)(output, DPMSModeOff); +	} + +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +		if (!crtc->enabled) +			crtc->funcs->dpms(crtc, DPMSModeOff); +	} +} +	 +/** + * drm_mode_probed_add - add a mode to the specified output's probed mode list + * @output: output the new mode + * @mode: mode data + * + * LOCKING: + * Caller must hold mode config lock. + *  + * Add @mode to @output's mode list for later use. + */ +void drm_mode_probed_add(struct drm_output *output, +			 struct drm_display_mode *mode) +{ +	list_add(&mode->head, &output->probed_modes); +} +EXPORT_SYMBOL(drm_mode_probed_add); + +/** + * drm_mode_remove - remove and free a mode + * @output: output list to modify + * @mode: mode to remove + * + * LOCKING: + * Caller must hold mode config lock. + *  + * Remove @mode from @output's mode list, then free it. + */ +void drm_mode_remove(struct drm_output *output, struct drm_display_mode *mode) +{ +	list_del(&mode->head); +	kfree(mode); +} +EXPORT_SYMBOL(drm_mode_remove); + +/** + * drm_output_create - create a new output + * @dev: DRM device + * @funcs: callbacks for this output + * @name: user visible name of the output + * + * LOCKING: + * Caller must hold @dev's mode_config lock. + * + * Creates a new drm_output structure and adds it to @dev's mode_config + * structure. + * + * RETURNS: + * Pointer to the new output or NULL on error. + */ +struct drm_output *drm_output_create(struct drm_device *dev, +				     const struct drm_output_funcs *funcs, +				     const char *name) +{ +	struct drm_output *output = NULL; + +	output = kzalloc(sizeof(struct drm_output), GFP_KERNEL); +	if (!output) +		return NULL; +		 +	output->dev = dev; +	output->funcs = funcs; +	output->id = drm_idr_get(dev, output); +	if (name) +		strncpy(output->name, name, DRM_OUTPUT_LEN); +	output->name[DRM_OUTPUT_LEN - 1] = 0; +	output->subpixel_order = SubPixelUnknown; +	INIT_LIST_HEAD(&output->probed_modes); +	INIT_LIST_HEAD(&output->modes); +	/* randr_output? */ +	/* output_set_monitor(output)? */ +	/* check for output_ignored(output)? */ + +	mutex_lock(&dev->mode_config.mutex); +	list_add_tail(&output->head, &dev->mode_config.output_list); +	dev->mode_config.num_output++; + +	mutex_unlock(&dev->mode_config.mutex); + +	return output; + +} +EXPORT_SYMBOL(drm_output_create); + +/** + * drm_output_destroy - remove an output + * @output: output to remove + * + * LOCKING: + * Caller must hold @dev's mode_config lock. + * + * Call @output's cleanup function, then remove the output from the DRM + * mode_config after freeing @output's modes. + */ +void drm_output_destroy(struct drm_output *output) +{ +	struct drm_device *dev = output->dev; +	struct drm_display_mode *mode, *t; + +	if (*output->funcs->cleanup) +		(*output->funcs->cleanup)(output); + +	list_for_each_entry_safe(mode, t, &output->probed_modes, head) +		drm_mode_remove(output, mode); + +	list_for_each_entry_safe(mode, t, &output->modes, head) +		drm_mode_remove(output, mode); + +	mutex_lock(&dev->mode_config.mutex); +	drm_idr_put(dev, output->id); +	list_del(&output->head); +	mutex_unlock(&dev->mode_config.mutex); +	kfree(output); +} +EXPORT_SYMBOL(drm_output_destroy); + +/** + * drm_output_rename - rename an output + * @output: output to rename + * @name: new user visible name + * + * LOCKING: + * None. + * + * Simply stuff a new name into @output's name field, based on @name. + * + * RETURNS: + * True if the name was changed, false otherwise. + */ +bool drm_output_rename(struct drm_output *output, const char *name) +{ +	if (!name) +		return false; + +	strncpy(output->name, name, DRM_OUTPUT_LEN); +	output->name[DRM_OUTPUT_LEN - 1] = 0; + +	DRM_DEBUG("Changed name to %s\n", output->name); +//	drm_output_set_monitor(output); +//	if (drm_output_ignored(output)) +//		return FALSE; + +	return TRUE; +} +EXPORT_SYMBOL(drm_output_rename); + +/** + * 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; + +	nmode->mode_id = drm_idr_get(dev, nmode); +	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_idr_put(dev, mode->mode_id); + +	kfree(mode); +} +EXPORT_SYMBOL(drm_mode_destroy); + +/** + * 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.crtc_list); +	INIT_LIST_HEAD(&dev->mode_config.output_list); +	INIT_LIST_HEAD(&dev->mode_config.usermode_list); +	idr_init(&dev->mode_config.crtc_idr); +} +EXPORT_SYMBOL(drm_mode_config_init); + +/** + * 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. + */ +static 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; +} + +/** + * drm_pick_crtcs - pick crtcs for output 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_output *output, *output_equal; +	struct drm_crtc   *crtc; +	struct drm_display_mode *des_mode = NULL, *modes, *modes_equal; + +	list_for_each_entry(output, &dev->mode_config.output_list, head) { +       		output->crtc = NULL; +     +    		/* Don't hook up outputs 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 outputs now, then we only create +		 * /dev/fbX for the output that's enabled, that's good as +		 * the users console will be on that output. +		 * +		 * If we do hook up outputs 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 output. Ugh. +		 */ +    		if (output->status != output_status_connected) +			continue; + +		des_mode = NULL; +		list_for_each_entry(des_mode, &output->modes, head) { +			if (des_mode->type & DRM_MODE_TYPE_PREFERRED) +				break; +		} + +		/* No preferred mode, let's just select the first available */ +		if (!des_mode || !(des_mode->type & DRM_MODE_TYPE_PREFERRED)) { +			list_for_each_entry(des_mode, &output->modes, head) { +				if (des_mode) +					break; +			} +		} + +		c = -1; +		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +			assigned = 0; + +			c++; +			if ((output->possible_crtcs & (1 << c)) == 0) +		    		continue; +	 +			list_for_each_entry(output_equal, &dev->mode_config.output_list, head) { +				if (output->id == output_equal->id) +					continue; + +				/* Find out if crtc has been assigned before */ +				if (output_equal->crtc == crtc) +					assigned = 1; +			} + +#if 1 /* continue for now */ +			if (assigned) +				continue; +#endif + +			o = -1; +			list_for_each_entry(output_equal, &dev->mode_config.output_list, head) { +				o++; +				if (output->id == output_equal->id) +					continue; + +				list_for_each_entry(modes, &output->modes, head) { +					list_for_each_entry(modes_equal, &output_equal->modes, head) { +						if (drm_mode_equal (modes, modes_equal)) { +							if ((output->possible_clones & output_equal->possible_clones) && (output_equal->crtc == crtc)) { +								printk("Cloning %s (0x%lx) to %s (0x%lx)\n",output->name,output->possible_clones,output_equal->name,output_equal->possible_clones); +								assigned = 0; +								goto clone; +							} +						} +					} +				} +			} + +clone: +			/* crtc has been assigned skip it */ +			if (assigned) +				continue; + +			/* Found a CRTC to attach to, do it ! */ +			output->crtc = crtc; +			output->crtc->desired_mode = des_mode; +			output->initial_x = 0; +			output->initial_y = 0; +			DRM_DEBUG("Desired mode for CRTC %d is %s\n",c,des_mode->name); +			break; +    		} +	} +} + + +/** + * drm_initial_config - setup a sane initial output configuration + * @dev: DRM device + * @can_grow: this configuration is growable + * + * LOCKING: + * Called at init time, must take mode config lock. + * + * Scan the CRTCs and outputs 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_initial_config(struct drm_device *dev, bool can_grow) +{ +	struct drm_output *output; +	struct drm_crtc *crtc; +	int ret = false; + +	mutex_lock(&dev->mode_config.mutex); + +	drm_crtc_probe_output_modes(dev, 2048, 2048); + +	drm_pick_crtcs(dev); + +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + +		/* can't setup the crtc if there's no assigned mode */ +		if (!crtc->desired_mode) +			continue; + +		/* Now setup the fbdev for attached crtcs */ +		dev->driver->fb_probe(dev, crtc); +	} + +	/* This is a little screwy, as we've already walked the outputs  +	 * above, but it's a little bit of magic too. There's the potential +	 * for things not to get setup above if an existing device gets +	 * re-assigned thus confusing the hardware. By walking the outputs +	 * this fixes up their crtc's. +	 */ +	list_for_each_entry(output, &dev->mode_config.output_list, head) { + +		/* can't setup the output if there's no assigned mode */ +		if (!output->crtc || !output->crtc->desired_mode) +			continue; + +		/* and needs an attached fb */ +		if (output->crtc->fb) +			drm_crtc_set_mode(output->crtc, output->crtc->desired_mode, 0, 0); +	} + +	drm_disable_unused_functions(dev); + +	mutex_unlock(&dev->mode_config.mutex); +	return ret; +} +EXPORT_SYMBOL(drm_initial_config); + +/** + * drm_mode_config_cleanup - free up DRM mode_config info + * @dev: DRM device + * + * LOCKING: + * Caller must hold mode config lock. + * + * Free up all the outputs 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_output *output, *ot; +	struct drm_crtc *crtc, *ct; +	struct drm_framebuffer *fb, *fbt; +	struct drm_display_mode *mode, *mt; +	list_for_each_entry_safe(output, ot, &dev->mode_config.output_list, head) { +		drm_output_destroy(output); +	} + +	list_for_each_entry_safe(mode, mt, &dev->mode_config.usermode_list, head) { +		drm_mode_destroy(dev, mode); +	} +		 +	list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { +		if (fb->bo->type != drm_bo_type_kernel) +			drm_framebuffer_destroy(fb); +		else +			dev->driver->fb_remove(dev, drm_crtc_from_fb(dev, fb)); +	} + +	list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) { +		drm_crtc_destroy(crtc); +	} + +} +EXPORT_SYMBOL(drm_mode_config_cleanup); + +/** + * drm_crtc_set_config - set a new config from userspace + * @crtc: CRTC to setup + * @crtc_info: user provided configuration + * @new_mode: new mode to set + * @output_set: set of outputs 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_set_config(struct drm_crtc *crtc, struct drm_mode_crtc *crtc_info, struct drm_display_mode *new_mode, struct drm_output **output_set, struct drm_framebuffer *fb) +{ +	struct drm_device *dev = crtc->dev; +	struct drm_crtc **save_crtcs, *new_crtc; +	bool save_enabled = crtc->enabled; +	bool changed; +	struct drm_output *output; +	int count = 0, ro; + +	save_crtcs = kzalloc(dev->mode_config.num_crtc * sizeof(struct drm_crtc *), GFP_KERNEL); +	if (!save_crtcs) +		return -ENOMEM; + +	if (crtc->fb != fb) +		changed = true; + +	if (crtc_info->x != crtc->x || crtc_info->y != crtc->y) +		changed = true; + +	if (new_mode && (crtc->mode.mode_id != new_mode->mode_id)) +		changed = true; + +	list_for_each_entry(output, &dev->mode_config.output_list, head) { +		save_crtcs[count++] = output->crtc; + +		if (output->crtc == crtc) +			new_crtc = NULL; +		else +			new_crtc = output->crtc; + +		for (ro = 0; ro < crtc_info->count_outputs; ro++) { +			if (output_set[ro] == output) +				new_crtc = crtc; +		} +		if (new_crtc != output->crtc) { +			changed = true; +			output->crtc = new_crtc; +		} +	} + +	if (changed) { +		crtc->fb = fb; +		crtc->enabled = (new_mode != NULL); +		if (new_mode != NULL) { +			DRM_DEBUG("attempting to set mode from userspace\n"); +			drm_mode_debug_printmodeline(dev, new_mode); +			if (!drm_crtc_set_mode(crtc, new_mode, crtc_info->x, +					       crtc_info->y)) { +				crtc->enabled = save_enabled; +				count = 0; +				list_for_each_entry(output, &dev->mode_config.output_list, head) +					output->crtc = save_crtcs[count++]; +				kfree(save_crtcs); +				return -EINVAL; +			} +			crtc->desired_x = crtc_info->x; +			crtc->desired_y = crtc_info->y; +			crtc->desired_mode = new_mode; +		} +		drm_disable_unused_functions(dev); +	} +	kfree(save_crtcs); +	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->id = in->mode_id; +	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; +	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; +	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, output 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_output *output; +	struct drm_crtc *crtc; +	struct drm_mode_modeinfo u_mode; +	struct drm_display_mode *mode; +	int ret = 0; +	int mode_count= 0; +	int output_count = 0; +	int crtc_count = 0; +	int fb_count = 0; +	int copied = 0; +	 +	memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); + +	mutex_lock(&dev->mode_config.mutex); + +	list_for_each(lh, &dev->mode_config.fb_list) +		fb_count++; + +	list_for_each(lh, &dev->mode_config.crtc_list) +		crtc_count++; + +	list_for_each_entry(output, &dev->mode_config.output_list, +			    head) { +		output_count++; +		list_for_each(lh, &output->modes) +			mode_count++; +	} +	list_for_each(lh, &dev->mode_config.usermode_list) +		mode_count++; + +	if (card_res->count_modes == 0) { +		DRM_DEBUG("probing modes %dx%d\n", dev->mode_config.max_width, dev->mode_config.max_height); +		drm_crtc_probe_output_modes(dev, dev->mode_config.max_width, dev->mode_config.max_height); +		mode_count = 0; +		list_for_each_entry(output, &dev->mode_config.output_list, head) { +			list_for_each(lh, &output->modes) +				mode_count++; +		} +		list_for_each(lh, &dev->mode_config.usermode_list) +			mode_count++; +	} + +	/* handle this in 4 parts */ +	/* FBs */ +	if (card_res->count_fbs >= fb_count) { +		copied = 0; +		list_for_each_entry(fb, &dev->mode_config.fb_list, head) { +			card_res->fb_id[copied++] = fb->id; +		} +	} +	card_res->count_fbs = fb_count; + +	/* CRTCs */ +	if (card_res->count_crtcs >= crtc_count) { +		copied = 0; +		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head){ +			DRM_DEBUG("CRTC ID is %d\n", crtc->id); +			card_res->crtc_id[copied++] = crtc->id; +		} +	} +	card_res->count_crtcs = crtc_count; + + +	/* Outputs */ +	if (card_res->count_outputs >= output_count) { +		copied = 0; +		list_for_each_entry(output, &dev->mode_config.output_list, +				    head) { + 			DRM_DEBUG("OUTPUT ID is %d\n", output->id); +			card_res->output_id[copied++] = output->id; +		} +	} +	card_res->count_outputs = output_count; +	 +	/* Modes */ +	if (card_res->count_modes >= mode_count) { +		copied = 0; +		list_for_each_entry(output, &dev->mode_config.output_list, +				    head) { +			list_for_each_entry(mode, &output->modes, head) { +				drm_crtc_convert_to_umode(&u_mode, mode); +				card_res->modes[copied++] = u_mode; +			} +		} +		/* add in user modes */ +		list_for_each_entry(mode, &dev->mode_config.usermode_list, head) { +			drm_crtc_convert_to_umode(&u_mode, mode); +			card_res->modes[copied++] = u_mode; +		} +	} +	card_res->count_modes = mode_count; + +	DRM_DEBUG("Counted %d %d %d\n", card_res->count_crtcs, +		  card_res->count_outputs, +		  card_res->count_modes); +	 +	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_output *output; +	int ocount; +	int ret = 0; + +	mutex_lock(&dev->mode_config.mutex); +	crtc = idr_find(&dev->mode_config.crtc_idr, crtc_resp->crtc_id); +	if (!crtc || (crtc->id != crtc_resp->crtc_id)) { +		ret = -EINVAL; +		goto out; +	} + +	crtc_resp->x = crtc->x; +	crtc_resp->y = crtc->y; +	crtc_resp->fb_id = 1; + +	crtc_resp->outputs = 0; +	if (crtc->enabled) { + +		crtc_resp->mode = crtc->mode.mode_id; +		ocount = 0; +		list_for_each_entry(output, &dev->mode_config.output_list, head) { +			if (output->crtc == crtc) +				crtc_resp->outputs |= 1 << (ocount++); +		} +	} else { +		crtc_resp->mode = 0; +	} + +out: +	mutex_unlock(&dev->mode_config.mutex); +	return ret; +} + +/** + * drm_mode_getoutput - get output configuration + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * LOCKING: + * Caller? (FIXME) + * + * Construct a output configuration structure to return to the user. + * + * Called by the user via ioctl. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_mode_getoutput(struct drm_device *dev, +		       void *data, struct drm_file *file_priv) +{ +	struct drm_mode_get_output *out_resp = data; +	struct drm_output *output; +	struct drm_display_mode *mode; +	int mode_count = 0; +	int ret = 0; +	int copied = 0; +	int i; + +	DRM_DEBUG("output id %d:\n", out_resp->output); + +	mutex_lock(&dev->mode_config.mutex); +	output= idr_find(&dev->mode_config.crtc_idr, out_resp->output); +	if (!output || (output->id != out_resp->output)) { +		ret = -EINVAL; +		goto done; +	} + +	list_for_each_entry(mode, &output->modes, head) +		mode_count++; +	 +	for (i = 0; i < DRM_OUTPUT_MAX_UMODES; i++) +		if (output->user_mode_ids[i] != 0) +			mode_count++; + +	strncpy(out_resp->name, output->name, DRM_OUTPUT_NAME_LEN); +	out_resp->name[DRM_OUTPUT_NAME_LEN-1] = 0; + +	out_resp->mm_width = output->mm_width; +	out_resp->mm_height = output->mm_height; +	out_resp->subpixel = output->subpixel_order; +	out_resp->connection = output->status; +	if (output->crtc) +		out_resp->crtc = output->crtc->id; +	else +		out_resp->crtc = 0; + +	if ((out_resp->count_modes >= mode_count) && mode_count) { +		copied = 0; +		list_for_each_entry(mode, &output->modes, head) { +			out_resp->modes[copied++] = mode->mode_id; +		} +		for (i = 0; i < DRM_OUTPUT_MAX_UMODES; i++) { +			if (output->user_mode_ids[i] != 0) +				out_resp->modes[copied++] = output->user_mode_ids[i]; +		} +			 +	} +	out_resp->count_modes = mode_count; + +done: +	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_crtc *crtc; +	struct drm_output **output_set = NULL, *output; +	struct drm_display_mode *mode; +	struct drm_framebuffer *fb = NULL; +	int ret = 0; +	int i; + +	mutex_lock(&dev->mode_config.mutex); +	crtc = idr_find(&dev->mode_config.crtc_idr, crtc_req->crtc_id); +	if (!crtc || (crtc->id != crtc_req->crtc_id)) { +		DRM_DEBUG("Unknown CRTC ID %d\n", crtc_req->crtc_id); +		ret = -EINVAL; +		goto out; +	} + +	if (crtc_req->mode) { +		/* if we have a mode we need a framebuffer */ +		if (crtc_req->fb_id) { +			fb = idr_find(&dev->mode_config.crtc_idr, crtc_req->fb_id); +			if (!fb || (fb->id != crtc_req->fb_id)) { +				DRM_DEBUG("Unknown FB ID%d\n", crtc_req->fb_id); +				ret = -EINVAL; +				goto out; +			} +		} +		mode = idr_find(&dev->mode_config.crtc_idr, crtc_req->mode); +		if (!mode || (mode->mode_id != crtc_req->mode)) { +			struct drm_output *output; +			 +			list_for_each_entry(output,  +					    &dev->mode_config.output_list, +					    head) { +				list_for_each_entry(mode, &output->modes, +						    head) { +					drm_mode_debug_printmodeline(dev,  +								     mode); +				} +			} + +			DRM_DEBUG("Unknown mode id %d, %p\n", crtc_req->mode, mode); +			ret = -EINVAL; +			goto out; +		} +	} else +		mode = NULL; + +	if (crtc_req->count_outputs == 0 && mode) { +		DRM_DEBUG("Count outputs is 0 but mode set\n"); +		ret = -EINVAL; +		goto out; +	} + +	if (crtc_req->count_outputs > 0 && !mode && !fb) { +		DRM_DEBUG("Count outputs is %d but no mode or fb set\n", crtc_req->count_outputs); +		ret = -EINVAL; +		goto out; +	} + +	if (crtc_req->count_outputs > 0) { +		u32 out_id; +		output_set = kmalloc(crtc_req->count_outputs * +				     sizeof(struct drm_output *), GFP_KERNEL); +		if (!output_set) { +			ret = -ENOMEM; +			goto out; +		} + +		for (i = 0; i < crtc_req->count_outputs; i++) { +			if (get_user(out_id, &crtc_req->set_outputs[i])) { +				ret = -EFAULT; +				goto out; +			} + +			output = idr_find(&dev->mode_config.crtc_idr, out_id); +			if (!output || (out_id != output->id)) { +				DRM_DEBUG("Output id %d unknown\n", out_id); +				ret = -EINVAL; +				goto out; +			} + +			output_set[i] = output; +		} +	} +		 +	ret = drm_crtc_set_config(crtc, crtc_req, mode, output_set, fb); + +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; +	struct drm_buffer_object *bo; +	struct drm_crtc *crtc; +	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 limits are okay */ +	ret = drm_get_buffer_object(dev, &bo, r->handle); +	if (ret || !bo) { +		ret = -EINVAL; +		goto out; +	} + +	/* TODO check buffer is sufficently large */ +	/* TODO setup destructor callback */ + +	fb = drm_framebuffer_create(dev); +	if (!fb) { +		ret = -EINVAL; +		goto out; +	} + +	fb->width = r->width; +	fb->height = r->height; +	fb->pitch = r->pitch; +	fb->bits_per_pixel = r->bpp; +	fb->depth = r->depth; +	fb->offset = bo->offset; +	fb->bo = bo; + +	r->buffer_id = fb->id; + +	list_add(&fb->filp_head, &file_priv->fbs); + +	/* FIXME: bind the fb to the right crtc */ +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +		crtc->fb = fb; +		dev->driver->fb_probe(dev, crtc); +	} + +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_framebuffer *fb = 0; +	uint32_t *id = data; +	int ret = 0; + +	fb = idr_find(&dev->mode_config.crtc_idr, *id); +	/* TODO check that we realy get a framebuffer back. */ +	if (!fb || (*id != fb->id)) { +		DRM_ERROR("mode invalid framebuffer id\n"); +		ret = -EINVAL; +		goto out; +	} + +	/* TODO check if we own the buffer */ +	/* TODO release all crtc connected to the framebuffer */ +	/* bind the fb to the crtc for now */ +	/* TODO unhock the destructor from the buffer object */ + +	if (fb->bo->type != drm_bo_type_kernel) +		drm_framebuffer_destroy(fb); +	else +		dev->driver->fb_remove(dev, drm_crtc_from_fb(dev, 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_framebuffer *fb; +	int ret = 0; + +	mutex_lock(&dev->mode_config.mutex); +	fb = idr_find(&dev->mode_config.crtc_idr, r->buffer_id); +	if (!fb || (r->buffer_id != fb->id)) { +		DRM_ERROR("invalid framebuffer id\n"); +		ret = -EINVAL; +		goto out; +	} + +	r->height = fb->height; +	r->width = fb->width; +	r->depth = fb->depth; +	r->bpp = fb->bits_per_pixel; +	r->handle = fb->bo->base.hash.key; +	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->head->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); +		if (fb->bo->type != drm_bo_type_kernel) +			drm_framebuffer_destroy(fb); +		else +			dev->driver->fb_remove(dev, drm_crtc_from_fb(dev, fb)); +	} +	mutex_unlock(&dev->mode_config.mutex); +} + +/** + * drm_fb_newmode - adds a user defined mode + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * Adds a user specified mode to the kernel. + * + * Called by the user via ioctl. + * + * RETURNS: + * writes new mode id into arg. + * Zero on success, errno on failure. + */ +int drm_mode_addmode(struct drm_device *dev, +		     void *data, struct drm_file *file_priv) +{ +	struct drm_mode_modeinfo *new_mode = data; +	struct drm_display_mode *user_mode; +	int ret = 0; + +	mutex_lock(&dev->mode_config.mutex); +	user_mode = drm_mode_create(dev); +	if (!user_mode) { +		ret = -ENOMEM; +		goto out; +	} + +	drm_crtc_convert_umode(user_mode, new_mode); +	user_mode->type |= DRM_MODE_TYPE_USERDEF; + +	user_mode->output_count = 0; + +	list_add(&user_mode->head, &dev->mode_config.usermode_list); + +	new_mode->id = user_mode->mode_id; + +out: +	mutex_unlock(&dev->mode_config.mutex); +	return ret; +} + +/** + * drm_fb_rmmode - removes a user defined mode + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * Remove the user defined mode specified by the user. + * + * Called by the user via ioctl + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_mode_rmmode(struct drm_device *dev, +		    void *data, struct drm_file *file_priv) +{ +	uint32_t *id = data; +	struct drm_display_mode *mode, *t; +	int ret = -EINVAL; + +	mutex_lock(&dev->mode_config.mutex);	 +	mode = idr_find(&dev->mode_config.crtc_idr, *id); +	if (!mode || (*id != mode->mode_id)) { +		ret = -EINVAL; +		goto out; +	} + +	if (!(mode->type & DRM_MODE_TYPE_USERDEF)) { +		ret = -EINVAL; +		goto out; +	} + +	if (mode->output_count) { +		ret = -EINVAL; +		goto out; +	} + +	list_for_each_entry(t, &dev->mode_config.usermode_list, head) { +		if (t == mode) { +			list_del(&mode->head); +			drm_mode_destroy(dev, mode); +			ret = 0; +			break; +		} +	} + +out: +	mutex_unlock(&dev->mode_config.mutex); +	return ret; +} + +/** + * drm_fb_attachmode - Attach a user mode to an output + * @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 output. + * Called by the user via ioctl. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_mode_attachmode(struct drm_device *dev, +			void *data, struct drm_file *file_priv) +{ +	struct drm_mode_mode_cmd *mode_cmd = data; +	struct drm_output *output; +	struct drm_display_mode *mode; +	int i, ret = 0; + +	mutex_lock(&dev->mode_config.mutex); + +	mode = idr_find(&dev->mode_config.crtc_idr, mode_cmd->mode_id); +	if (!mode || (mode->mode_id != mode_cmd->mode_id)) { +		ret = -EINVAL; +		goto out; +	} + +	output = idr_find(&dev->mode_config.crtc_idr, mode_cmd->output_id); +	if (!output || (output->id != mode_cmd->output_id)) { +		ret = -EINVAL; +		goto out; +	} + +	for (i = 0; i < DRM_OUTPUT_MAX_UMODES; i++) { +		if (output->user_mode_ids[i] == 0) { +			output->user_mode_ids[i] = mode->mode_id; +			mode->output_count++; +			break; +		} +	} + +	if (i == DRM_OUTPUT_MAX_UMODES) +		ret = -ENOSPC; + +out: +	mutex_unlock(&dev->mode_config.mutex); +	return ret; +} + + +/** + * drm_fb_detachmode - Detach a user specified mode from an output + * @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(struct drm_device *dev, +			void *data, struct drm_file *file_priv) +{ +	struct drm_mode_mode_cmd *mode_cmd = data; +	struct drm_output *output; +	struct drm_display_mode *mode; +	int i, found = 0, ret = 0; + +	mutex_lock(&dev->mode_config.mutex); + +	mode = idr_find(&dev->mode_config.crtc_idr, mode_cmd->mode_id); +	if (!mode || (mode->mode_id != mode_cmd->mode_id)) { +		ret = -EINVAL; +		goto out; +	} + +	output = idr_find(&dev->mode_config.crtc_idr, mode_cmd->output_id); +	if (!output || (output->id != mode_cmd->output_id)) { +		ret = -EINVAL; +		goto out; +	} + + +	for (i = 0; i < DRM_OUTPUT_MAX_UMODES; i++) { +		if (output->user_mode_ids[i] == mode->mode_id) { +			output->user_mode_ids[i] = 0; +			mode->output_count--; +			found = 1; +		} +	} + +	if (!found) +		ret = -EINVAL; + +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..d97cd196 --- /dev/null +++ b/linux-core/drm_crtc.h @@ -0,0 +1,556 @@ +/* + * 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; + +/* + * Note on terminology:  here, for brevity and convenience, we refer to output + * control chips as 'CRTCs'.  They can control any type of output, VGA, LVDS, + * DVI, etc.  And 'screen' refers to the whole of the visible display, which + * may span multiple monitors (and therefore multiple CRTC and output + * 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_BUILTIN	(1<<0) +#define DRM_MODE_TYPE_CLOCK_C	((1<<1) | DRM_MODE_TYPE_BUILTIN) +#define DRM_MODE_TYPE_CRTC_C	((1<<2) | DRM_MODE_TYPE_BUILTIN) +#define DRM_MODE_TYPE_PREFERRED	(1<<3) +#define DRM_MODE_TYPE_DEFAULT	(1<<4) +#define DRM_MODE_TYPE_USERDEF	(1<<5) +#define DRM_MODE_TYPE_DRIVER	(1<<6) + +#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; +	char name[DRM_DISPLAY_MODE_LEN]; +	int mode_id; +	int output_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; + +	/* 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 + +enum drm_output_status { +	output_status_connected, +	output_status_disconnected, +	output_status_unknown, +}; + +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, +		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; + +	/* Preferred mode (if any) */ +	struct drm_display_mode *preferred_mode; +	char *raw_edid; /* if any */ +}; + +struct drm_framebuffer { +	struct drm_device *dev; +	struct list_head head; +	int id; /* idr assigned */ +	unsigned int pitch; +	unsigned long offset; +	unsigned int width; +	unsigned int height; +	/* depth can be 15 or 16 */ +	unsigned int depth; +	int bits_per_pixel; +	int flags; +	struct drm_buffer_object *bo; +	void *fbdev; +	u32 pseudo_palette[17]; +	void *virtual_base; +	struct list_head filp_head; +}; +struct drm_crtc; +struct drm_output; + +/** + * 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 + * @cleanup: cleanup driver private state prior to close + * + * The drm_crtc_funcs structure is the central CRTC management structure + * in the DRM.  Each CRTC controls one or more outputs (note that the name + * CRTC is simply historical, a CRTC may control LVDS, VGA, DVI, TV out, etc. + * outputs, 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 { +	/* +	 * 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); + +	/* JJJ:  Are these needed? */ +	/* Save CRTC state */ +	void (*save)(struct drm_crtc *crtc); /* suspend? */ +	/* Restore CRTC state */ +	void (*restore)(struct drm_crtc *crtc); /* resume? */ +	bool (*lock)(struct drm_crtc *crtc); +	void (*unlock)(struct drm_crtc *crtc); + +	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); +	/* Set gamma on the CRTC */ +	void (*gamma_set)(struct drm_crtc *crtc, u16 r, u16 g, u16 b, +			  int regno); +	/* Driver cleanup routine */ +	void (*cleanup)(struct drm_crtc *crtc); +}; + +/** + * 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 + * @driver_private: arbitrary driver data + * + * Each CRTC may have one or more outputs associated with it.  This structure + * allows the CRTC to be controlled. + */ +struct drm_crtc { +	struct drm_device *dev; +	struct list_head head; + +	int id; /* idr assigned */ + +	/* framebuffer the output is currently bound to */ +	struct drm_framebuffer *fb; + +	bool enabled; + +	/* JJJ: are these needed? */ +	bool cursor_in_range; +	bool cursor_shown; + +	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; +	void *driver_private; + +	/* RRCrtcPtr randr_crtc? */ +}; + +extern struct drm_crtc *drm_crtc_create(struct drm_device *dev, +					const struct drm_crtc_funcs *funcs); + +/** + * drm_output_funcs - control outputs on a given device + * @init: setup this output + * @dpms: set power state (see drm_crtc_funcs above) + * @save: save output state + * @restore: restore output state + * @mode_valid: is this mode valid on the given output? + * @mode_fixup: try to fixup proposed mode for this output + * @mode_set: set this mode + * @detect: is this output active? + * @get_modes: get mode list for this output + * @set_property: property for this output may need update + * @cleanup: output is going away, cleanup + * + * Each CRTC may have one or more outputs attached to it.  The functions + * below allow the core DRM code to control outputs, enumerate available modes, + * etc. + */ +struct drm_output_funcs { +	void (*init)(struct drm_output *output); +	void (*dpms)(struct drm_output *output, int mode); +	void (*save)(struct drm_output *output); +	void (*restore)(struct drm_output *output); +	int (*mode_valid)(struct drm_output *output, +			  struct drm_display_mode *mode); +	bool (*mode_fixup)(struct drm_output *output, +			   struct drm_display_mode *mode, +			   struct drm_display_mode *adjusted_mode); +	void (*prepare)(struct drm_output *output); +	void (*commit)(struct drm_output *output); +	void (*mode_set)(struct drm_output *output, +			 struct drm_display_mode *mode, +			 struct drm_display_mode *adjusted_mode); +	enum drm_output_status (*detect)(struct drm_output *output); +	int (*get_modes)(struct drm_output *output); +	/* JJJ: type checking for properties via property value type */ +	bool (*set_property)(struct drm_output *output, int prop, void *val); +	void (*cleanup)(struct drm_output *output); +}; + +#define DRM_OUTPUT_MAX_UMODES 16 +#define DRM_OUTPUT_LEN 32 +/** + * drm_output - central DRM output control structure + * @crtc: CRTC this output is currently connected to, NULL if none + * @possible_crtcs: bitmap of CRTCS this output could be attached to + * @possible_clones: bitmap of possible outputs this output could clone + * @interlace_allowed: can this output handle interlaced modes? + * @doublescan_allowed: can this output handle doublescan? + * @available_modes: modes available on this output (from get_modes() + user) + * @initial_x: initial x position for this output + * @initial_y: initial y position for this output + * @status: output connected? + * @subpixel_order: for this output + * @mm_width: displayable width of output in mm + * @mm_height: displayable height of output in mm + * @name: name of output (should be one of a few standard names) + * @funcs: output control functions + * @driver_private: private driver data + * + * Each output may be connected to one or more CRTCs, or may be clonable by + * another output if they can share a CRTC.  Each output also has a specific + * position in the broader display (referred to as a 'screen' though it could + * span multiple monitors). + */ +struct drm_output { +	struct drm_device *dev; +	struct list_head head; +	struct drm_crtc *crtc; +	int id; /* idr assigned */ +	unsigned long possible_crtcs; +	unsigned long possible_clones; +	bool interlace_allowed; +	bool doublescan_allowed; +	struct list_head modes; /* list of modes on this output */ + +	/* +	  OptionInfoPtr options; +	  XF86ConfMonitorPtr conf_monitor; +	 */ +	int initial_x, initial_y; +	enum drm_output_status status; + +	/* these are modes added by probing with DDC or the BIOS */ +	struct list_head probed_modes; +	 +	/* xf86MonPtr MonInfo; */ +	enum subpixel_order subpixel_order; +	int mm_width, mm_height; +	struct drm_display_info *monitor_info; /* if any */ +	char name[DRM_OUTPUT_LEN]; +	const struct drm_output_funcs *funcs; +	void *driver_private; + +	u32 user_mode_ids[DRM_OUTPUT_MAX_UMODES]; + +}; + +/** + * 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<->output mappings as needed and update its view of the screen. + */ +struct drm_mode_config_funcs { +	bool (*resize)(struct drm_device *dev, int width, int height); +}; + +/** + * 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, output, modes - just makes life easier */ +	/* this is limited to one for now */ +	int num_fb; +	struct list_head fb_list; +	int num_output; +	struct list_head output_list; + +	/* int compat_output? */ +	int num_crtc; +	struct list_head crtc_list; + +	struct list_head usermode_list; +	int min_width, min_height; +	int max_width, max_height; +	/* DamagePtr rotationDamage? */ +	/* DGA stuff? */ +	struct drm_mode_config_funcs *funcs; +	unsigned long fb_base; +}; + +struct drm_output *drm_output_create(struct drm_device *dev, +				     const struct drm_output_funcs *funcs, +				     const char *name); +extern void drm_output_destroy(struct drm_output *output); +extern bool drm_output_rename(struct drm_output *output, const char *name); +extern void drm_fb_release(struct file *filp); + +extern int drm_add_edid_modes(struct drm_output *output, +			struct i2c_adapter *adapter); +extern void drm_mode_probed_add(struct drm_output *output, struct drm_display_mode *mode); +extern void drm_mode_remove(struct drm_output *output, 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_device *dev, +					 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); +extern void drm_disable_unused_functions(struct drm_device *dev); + +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_output_list_update(struct drm_output *output); + +extern struct drm_display_mode *drm_crtc_mode_create(struct drm_device *dev); +extern bool drm_initial_config(struct drm_device *dev, bool cangrow); +extern void drm_framebuffer_set_object(struct drm_device *dev, +				       unsigned long handle); +extern struct drm_framebuffer *drm_framebuffer_create(struct drm_device *dev); +extern void drm_framebuffer_destroy(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 bool drm_crtc_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, +		       int x, int y); + +/* 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_getoutput(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_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(struct drm_device *dev, +			    void *data, struct drm_file *file_priv); +extern int drm_mode_rmmode(struct drm_device *dev, +			   void *data, struct drm_file *file_priv); +extern int drm_mode_attachmode(struct drm_device *dev, +			       void *data, struct drm_file *file_priv); +extern int drm_mode_detachmode(struct drm_device *dev, +			       void *data, struct drm_file *file_priv); + +#endif /* __DRM_CRTC_H__ */ + diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c index fe2b1200..4a871d63 100644 --- a/linux-core/drm_drv.c +++ b/linux-core/drm_drv.c @@ -115,9 +115,22 @@ static struct drm_ioctl_desc drm_ioctls[] = {  	DRM_IOCTL_DEF(DRM_IOCTL_SG_ALLOC, drm_sg_alloc_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),  	DRM_IOCTL_DEF(DRM_IOCTL_SG_FREE, drm_sg_free, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), -  	DRM_IOCTL_DEF(DRM_IOCTL_WAIT_VBLANK, drm_wait_vblank, 0), - +	// DRM_IOCTL_DEF(DRM_IOCTL_BUFOBJ, drm_bo_ioctl, DRM_AUTH), +	DRM_IOCTL_DEF(DRM_IOCTL_MM_INIT, drm_mm_init_ioctl,  +					     DRM_AUTH ), +	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_ROOT_ONLY), +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_MASTER|DRM_ROOT_ONLY), +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETOUTPUT, drm_mode_getoutput, DRM_MASTER|DRM_ROOT_ONLY), +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_ROOT_ONLY), +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_MASTER|DRM_ROOT_ONLY), +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_MASTER|DRM_ROOT_ONLY), +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_MASTER|DRM_ROOT_ONLY), +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDMODE, drm_mode_addmode, DRM_MASTER|DRM_ROOT_ONLY), +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMMODE, drm_mode_rmmode, DRM_MASTER|DRM_ROOT_ONLY), +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATTACHMODE, drm_mode_attachmode, DRM_MASTER|DRM_ROOT_ONLY), +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_DETACHMODE, drm_mode_detachmode, DRM_MASTER|DRM_ROOT_ONLY),  	//	DRM_IOCTL_DEF(DRM_IOCTL_BUFOBJ, drm_bo_ioctl, DRM_AUTH),  	DRM_IOCTL_DEF(DRM_IOCTL_UPDATE_DRAW, drm_update_drawable_info, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), @@ -177,8 +190,6 @@ int drm_lastclose(struct drm_device * 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"); @@ -392,7 +403,6 @@ static void drm_cleanup(struct drm_device * dev)  	drm_lastclose(dev);  	drm_fence_manager_takedown(dev); -	drm_ht_remove(&dev->map_hash);  	drm_mm_takedown(&dev->offset_manager);  	drm_ht_remove(&dev->object_hash); @@ -410,12 +420,17 @@ 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);  	drm_put_head(&dev->primary);  	if (drm_put_dev(dev)) diff --git a/linux-core/drm_edid.c b/linux-core/drm_edid.c new file mode 100644 index 00000000..d7d3939a --- /dev/null +++ b/linux-core/drm_edid.c @@ -0,0 +1,467 @@ +/* + * 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> + */ +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include "drmP.h" +#include "drm_edid.h" + +/* Valid EDID header has these bytes */ +static u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; + +/** + * edid_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_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; +} + +/** + * 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 + * @timing: EDID detailed timing info + * @preferred: is this a preferred mode? + * + * An EDID detailed timing block contains enough info for us to create and + * return a new struct drm_display_mode.  The @preferred flag will be set + * if this is the display's preferred timing, and we'll use it to indicate + * to the other layers that this mode is desired. + */ +struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, +					   struct detailed_timing *timing) +{ +	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; +	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; + +	mode->flags |= pt->hsync_positive ? V_PHSYNC : V_NHSYNC; +	mode->flags |= pt->vsync_positive ? V_PVSYNC : V_NVSYNC; + +	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_output *output, struct edid *edid) +{ +	struct drm_device *dev = output->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]); +			drm_mode_probed_add(output, 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_output *output, struct edid *edid) +{ +	struct drm_device *dev = output->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]); +		drm_mode_probed_add(output, newmode); +		modes++; +	} + +	return modes; +} + +/** + * add_detailed_modes - get detailed mode info from EDID data + * @edid: EDID block to scan + * + * 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_output *output, struct edid *edid) +{ +	struct drm_device *dev = output->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, timing); +			/* First detailed mode is preferred */ +			if (i == 0 && edid->preferred_timing) +				newmode->type |= DRM_MODE_TYPE_PREFERRED; +			drm_mode_probed_add(output, 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); +				drm_mode_probed_add(output, 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_add_edid_modes - add modes from EDID data, if available + * @output: output we're probing + * @adapter: i2c adapter to use for DDC + * + * Poke the given output's i2c channel to grab EDID data if possible.  If we + * get any, add the specified modes to the output's mode list. + * + * Return number of modes added or 0 if we couldn't find any. + */ +int drm_add_edid_modes(struct drm_output *output, struct i2c_adapter *adapter) +{ +	struct edid *edid; +	int num_modes = 0; + +	edid = (struct edid *)drm_ddc_read(adapter); +	if (!edid) { +		dev_warn(&output->dev->pdev->dev, "%s: no EDID data\n", +			 output->name); +		goto out_err; +	} + +	if (!edid_valid(edid)) { +		dev_warn(&output->dev->pdev->dev, "%s: EDID invalid.\n", +			 output->name); +		goto out_err; +	} + +	num_modes += add_established_modes(output, edid); +	num_modes += add_standard_modes(output, edid); +	num_modes += add_detailed_info(output, edid); + +	return num_modes; + +out_err: +	kfree(edid); +	return 0; +} +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..0d2eeaa1 --- /dev/null +++ b/linux-core/drm_edid.h @@ -0,0 +1,176 @@ +#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 */ +	u16 mfg_id; /* FIXME: byte order */ +	u16 prod_code; /* FIXME: byte order */ +	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)); + +#endif /* __DRM_EDID_H__ */ diff --git a/linux-core/drm_fb.c b/linux-core/drm_fb.c new file mode 100644 index 00000000..775fd180 --- /dev/null +++ b/linux-core/drm_fb.c @@ -0,0 +1,435 @@ +/* + * 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) { +		if (crtc->funcs->gamma_set) { +			crtc->funcs->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 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 0ccaed5b..a6222613 100644 --- a/linux-core/drm_fops.c +++ b/linux-core/drm_fops.c @@ -264,6 +264,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], DRM_FILE_HASH_ORDER); @@ -473,6 +474,7 @@ int drm_release(struct inode *inode, struct file *filp)  	mutex_unlock(&dev->ctxlist_mutex);  	mutex_lock(&dev->struct_mutex); +	drm_fb_release(filp);  	drm_object_release(filp);  	if (file_priv->remove_auth_on_close == 1) {  		struct drm_file *temp; diff --git a/linux-core/drm_modes.c b/linux-core/drm_modes.c new file mode 100644 index 00000000..fd00841e --- /dev/null +++ b/linux-core/drm_modes.c @@ -0,0 +1,560 @@ +/* + * 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_device *dev, +				  struct drm_display_mode *mode) +{ +	DRM_DEBUG("Modeline %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x\n", +		  mode->mode_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); +} +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->mode_id; +	*nmode = *mode; +	nmode->mode_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) +{ +	if (mode1->clock == mode2->clock && +	    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(dev, 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_output_list_update - update the mode list for the output + * @output: the output to update + * + * LOCKING: + * Caller must hold a lock protecting @mode_list. + * + * This moves the modes from the @output 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_output_list_update(struct drm_output *output) +{ +	struct drm_display_mode *mode; +	struct drm_display_mode *pmode, *pt; +	int found_it; +	list_for_each_entry_safe(pmode, pt, &output->probed_modes, +				 head) { +		found_it = 0; +		/* go through current modes checking for the new probed mode */ +		list_for_each_entry(mode, &output->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, &output->modes); +		} +	} +} diff --git a/linux-core/drm_objects.h b/linux-core/drm_objects.h index 702ece56..8ae55bf0 100644 --- a/linux-core/drm_objects.h +++ b/linux-core/drm_objects.h @@ -469,12 +469,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); @@ -612,6 +612,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_stub.c b/linux-core/drm_stub.c index 9e140ac2..34e8d2b8 100644 --- a/linux-core/drm_stub.c +++ b/linux-core/drm_stub.c @@ -85,9 +85,9 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,  #endif  	dev->irq = pdev->irq; -	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_mm_init(&dev->offset_manager, DRM_FILE_PAGE_OFFSET_START,  			DRM_FILE_PAGE_OFFSET_SIZE)) {  		drm_ht_remove(&dev->map_hash); @@ -111,10 +111,6 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,  	dev->driver = driver; -	if (dev->driver->load) -		if ((retcode = dev->driver->load(dev, ent->driver_data))) -			goto error_out_unreg; -  	if (drm_core_has_AGP(dev)) {  		if (drm_device_is_agp(dev))  			dev->agp = drm_agp_init(dev); @@ -134,6 +130,10 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,  		}  	} +	if (dev->driver->load) +		if ((retcode = dev->driver->load(dev, ent->driver_data))) +			goto error_out_unreg; +  	retcode = drm_ctxbitmap_init(dev);  	if (retcode) {  		DRM_ERROR("Cannot allocate memory for context bitmap.\n"); diff --git a/linux-core/i915_buffer.c b/linux-core/i915_buffer.c index f81def8f..01fae64a 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; @@ -130,7 +133,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) diff --git a/linux-core/i915_drv.c b/linux-core/i915_drv.c index e337e1d2..27e95d65 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" @@ -52,8 +53,8 @@ static 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_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_VRAM, DRM_BO_MEM_LOCAL};  static struct drm_bo_driver i915_bo_driver = {  	.mem_type_prio = i915_mem_prios, @@ -69,6 +70,234 @@ static struct drm_bo_driver i915_bo_driver = {  };  #endif +static int i915_suspend(struct pci_dev *pdev, pm_message_t state) +{ +	struct drm_device *dev = pci_get_drvdata(pdev); +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_output *output; +	int i; + +	pci_save_state(pdev); + +	/* Save video mode information for native mode-setting. */ +	dev_priv->saveDSPACNTR = I915_READ(DSPACNTR); +	dev_priv->savePIPEACONF = I915_READ(PIPEACONF); +	dev_priv->savePIPEASRC = I915_READ(PIPEASRC); +	dev_priv->saveFPA0 = I915_READ(FPA0); +	dev_priv->saveFPA1 = I915_READ(FPA1); +	dev_priv->saveDPLL_A = I915_READ(DPLL_A); +	if (IS_I965G(dev)) +		dev_priv->saveDPLL_A_MD = I915_READ(DPLL_A_MD); +	dev_priv->saveHTOTAL_A = I915_READ(HTOTAL_A); +	dev_priv->saveHBLANK_A = I915_READ(HBLANK_A); +	dev_priv->saveHSYNC_A = I915_READ(HSYNC_A); +	dev_priv->saveVTOTAL_A = I915_READ(VTOTAL_A); +	dev_priv->saveVBLANK_A = I915_READ(VBLANK_A); +	dev_priv->saveVSYNC_A = I915_READ(VSYNC_A); +	dev_priv->saveDSPASTRIDE = I915_READ(DSPASTRIDE); +	dev_priv->saveDSPASIZE = I915_READ(DSPASIZE); +	dev_priv->saveDSPAPOS = I915_READ(DSPAPOS); +	dev_priv->saveDSPABASE = I915_READ(DSPABASE); + +	for(i= 0; i < 256; i++) +		dev_priv->savePaletteA[i] = I915_READ(PALETTE_A + (i << 2)); + +	if(dev->mode_config.num_crtc == 2) { +		dev_priv->savePIPEBCONF = I915_READ(PIPEBCONF); +		dev_priv->savePIPEBSRC = I915_READ(PIPEBSRC); +		dev_priv->saveDSPBCNTR = I915_READ(DSPBCNTR); +		dev_priv->saveFPB0 = I915_READ(FPB0); +		dev_priv->saveFPB1 = I915_READ(FPB1); +		dev_priv->saveDPLL_B = I915_READ(DPLL_B); +		if (IS_I965G(dev)) +			dev_priv->saveDPLL_B_MD = I915_READ(DPLL_B_MD); +		dev_priv->saveHTOTAL_B = I915_READ(HTOTAL_B); +		dev_priv->saveHBLANK_B = I915_READ(HBLANK_B); +		dev_priv->saveHSYNC_B = I915_READ(HSYNC_B); +		dev_priv->saveVTOTAL_B = I915_READ(VTOTAL_B); +		dev_priv->saveVBLANK_B = I915_READ(VBLANK_B); +		dev_priv->saveVSYNC_B = I915_READ(VSYNC_B); +		dev_priv->saveDSPBSTRIDE = I915_READ(DSPBSTRIDE); +		dev_priv->saveDSPBSIZE = I915_READ(DSPBSIZE); +		dev_priv->saveDSPBPOS = I915_READ(DSPBPOS); +		dev_priv->saveDSPBBASE = I915_READ(DSPBBASE); +		for(i= 0; i < 256; i++) +			dev_priv->savePaletteB[i] = +				I915_READ(PALETTE_B + (i << 2)); +	} + +	if (IS_I965G(dev)) { +		dev_priv->saveDSPASURF = I915_READ(DSPASURF); +		dev_priv->saveDSPBSURF = I915_READ(DSPBSURF); +	} + +	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->saveVGACNTRL = I915_READ(VGACNTRL); + +	for(i = 0; i < 7; i++) { +		dev_priv->saveSWF[i] = I915_READ(SWF0 + (i << 2)); +		dev_priv->saveSWF[i+7] = I915_READ(SWF00 + (i << 2)); +	} +	dev_priv->saveSWF[14] = I915_READ(SWF30); +	dev_priv->saveSWF[15] = I915_READ(SWF31); +	dev_priv->saveSWF[16] = I915_READ(SWF32); + +	if (IS_MOBILE(dev) && !IS_I830(dev)) +		dev_priv->saveLVDS = I915_READ(LVDS); +	dev_priv->savePFIT_CONTROL = I915_READ(PFIT_CONTROL); + +	list_for_each_entry(output, &dev->mode_config.output_list, head) +		if (output->funcs->save) +			(*output->funcs->save) (output); + +#if 0 /* FIXME: save VGA bits */ +	vgaHWUnlock(hwp); +	vgaHWSave(pScrn, vgaReg, VGA_SR_FONTS); +#endif +	pci_disable_device(pdev); +	pci_set_power_state(pdev, PCI_D3hot); + +	return 0; +} + +static int i915_resume(struct pci_dev *pdev) +{ +	struct drm_device *dev = pci_get_drvdata(pdev); +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_output *output; +	struct drm_crtc *crtc; +	int i; + +	pci_set_power_state(pdev, PCI_D0); +	pci_restore_state(pdev); +	if (pci_enable_device(pdev)) +		return -1; + +	/* Disable outputs */ +	list_for_each_entry(output, &dev->mode_config.output_list, head) +		output->funcs->dpms(output, DPMSModeOff); + +	i915_driver_wait_next_vblank(dev, 0); +    +	/* Disable pipes */ +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) +		crtc->funcs->dpms(crtc, DPMSModeOff); + +	/* FIXME: wait for vblank on each pipe? */ +	i915_driver_wait_next_vblank(dev, 0); + +	if (IS_MOBILE(dev) && !IS_I830(dev)) +		I915_WRITE(LVDS, dev_priv->saveLVDS); + +	if (!IS_I830(dev) && !IS_845G(dev)) +		I915_WRITE(PFIT_CONTROL, dev_priv->savePFIT_CONTROL); + +	if (dev_priv->saveDPLL_A & DPLL_VCO_ENABLE) { +		I915_WRITE(DPLL_A, dev_priv->saveDPLL_A & ~DPLL_VCO_ENABLE); +		udelay(150); +	} +	I915_WRITE(FPA0, dev_priv->saveFPA0); +	I915_WRITE(FPA1, dev_priv->saveFPA1); +	I915_WRITE(DPLL_A, dev_priv->saveDPLL_A); +	udelay(150); +	if (IS_I965G(dev)) +		I915_WRITE(DPLL_A_MD, dev_priv->saveDPLL_A_MD); +	else +		I915_WRITE(DPLL_A, dev_priv->saveDPLL_A); +	udelay(150); + +	I915_WRITE(HTOTAL_A, dev_priv->saveHTOTAL_A); +	I915_WRITE(HBLANK_A, dev_priv->saveHBLANK_A); +	I915_WRITE(HSYNC_A, dev_priv->saveHSYNC_A); +	I915_WRITE(VTOTAL_A, dev_priv->saveVTOTAL_A); +	I915_WRITE(VBLANK_A, dev_priv->saveVBLANK_A); +	I915_WRITE(VSYNC_A, dev_priv->saveVSYNC_A); +    +	I915_WRITE(DSPASTRIDE, dev_priv->saveDSPASTRIDE); +	I915_WRITE(DSPASIZE, dev_priv->saveDSPASIZE); +	I915_WRITE(DSPAPOS, dev_priv->saveDSPAPOS); +	I915_WRITE(PIPEASRC, dev_priv->savePIPEASRC); +	I915_WRITE(DSPABASE, dev_priv->saveDSPABASE); +	if (IS_I965G(dev)) +		I915_WRITE(DSPASURF, dev_priv->saveDSPASURF); +	I915_WRITE(PIPEACONF, dev_priv->savePIPEACONF); +	i915_driver_wait_next_vblank(dev, 0); +	I915_WRITE(DSPACNTR, dev_priv->saveDSPACNTR); +	I915_WRITE(DSPABASE, I915_READ(DSPABASE)); +	i915_driver_wait_next_vblank(dev, 0); +    +	if(dev->mode_config.num_crtc == 2) { +		if (dev_priv->saveDPLL_B & DPLL_VCO_ENABLE) { +			I915_WRITE(DPLL_B, dev_priv->saveDPLL_B & ~DPLL_VCO_ENABLE); +			udelay(150); +		} +		I915_WRITE(FPB0, dev_priv->saveFPB0); +		I915_WRITE(FPB1, dev_priv->saveFPB1); +		I915_WRITE(DPLL_B, dev_priv->saveDPLL_B); +		udelay(150); +		if (IS_I965G(dev)) +			I915_WRITE(DPLL_B_MD, dev_priv->saveDPLL_B_MD); +		else +			I915_WRITE(DPLL_B, dev_priv->saveDPLL_B); +		udelay(150); +    +		I915_WRITE(HTOTAL_B, dev_priv->saveHTOTAL_B); +		I915_WRITE(HBLANK_B, dev_priv->saveHBLANK_B); +		I915_WRITE(HSYNC_B, dev_priv->saveHSYNC_B); +		I915_WRITE(VTOTAL_B, dev_priv->saveVTOTAL_B); +		I915_WRITE(VBLANK_B, dev_priv->saveVBLANK_B); +		I915_WRITE(VSYNC_B, dev_priv->saveVSYNC_B); +		I915_WRITE(DSPBSTRIDE, dev_priv->saveDSPBSTRIDE); +		I915_WRITE(DSPBSIZE, dev_priv->saveDSPBSIZE); +		I915_WRITE(DSPBPOS, dev_priv->saveDSPBPOS); +		I915_WRITE(PIPEBSRC, dev_priv->savePIPEBSRC); +		I915_WRITE(DSPBBASE, dev_priv->saveDSPBBASE); +		if (IS_I965G(dev)) +			I915_WRITE(DSPBSURF, dev_priv->saveDSPBSURF); +		I915_WRITE(PIPEBCONF, dev_priv->savePIPEBCONF); +		i915_driver_wait_next_vblank(dev, 0); +		I915_WRITE(DSPBCNTR, dev_priv->saveDSPBCNTR); +		I915_WRITE(DSPBBASE, I915_READ(DSPBBASE)); +		i915_driver_wait_next_vblank(dev, 0); +	} + +	/* Restore outputs */ +	list_for_each_entry(output, &dev->mode_config.output_list, head) +		if (output->funcs->restore) +			output->funcs->restore(output); +     +	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); + +	for(i = 0; i < 256; i++) +		I915_WRITE(PALETTE_A + (i << 2), dev_priv->savePaletteA[i]); +    +	if(dev->mode_config.num_crtc == 2) +		for(i= 0; i < 256; i++) +			I915_WRITE(PALETTE_B + (i << 2), dev_priv->savePaletteB[i]); + +	for(i = 0; i < 7; i++) { +		I915_WRITE(SWF0 + (i << 2), dev_priv->saveSWF[i]); +		I915_WRITE(SWF00 + (i << 2), dev_priv->saveSWF[i+7]); +	} + +	I915_WRITE(SWF30, dev_priv->saveSWF[14]); +	I915_WRITE(SWF31, dev_priv->saveSWF[15]); +	I915_WRITE(SWF32, dev_priv->saveSWF[16]); + +#if 0 /* FIXME: restore VGA bits */ +	vgaHWRestore(pScrn, vgaReg, VGA_SR_FONTS); +	vgaHWLock(hwp); +#endif + +	return 0; +} +  static int probe(struct pci_dev *pdev, const struct pci_device_id *ent);  static struct drm_driver driver = {  	/* don't use mtrr's here, the Xserver or user space app should @@ -79,7 +308,7 @@ static struct drm_driver driver = {  	    DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_IRQ_VBL |  	    DRIVER_IRQ_VBL2,  	.load = i915_driver_load, -	.firstopen = i915_driver_firstopen, +	.unload = i915_driver_unload,  	.lastclose = i915_driver_lastclose,  	.preclose = i915_driver_preclose,  	.device_is_agp = i915_driver_device_is_agp, @@ -92,6 +321,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, +	.fb_probe = intelfb_probe, +	.fb_remove = intelfb_remove,  	.ioctls = i915_ioctls,  	.fops = {  		.owner = THIS_MODULE, @@ -110,6 +341,8 @@ static struct drm_driver driver = {  		.id_table = pciidlist,  		.probe = probe,  		.remove = __devexit_p(drm_cleanup_pci), +		.suspend = i915_suspend, +		.resume = i915_resume,  		},  #ifdef I915_HAVE_FENCE  	.fence_driver = &i915_fence_driver, diff --git a/linux-core/i915_fence.c b/linux-core/i915_fence.c index a0f22785..330c870e 100644 --- a/linux-core/i915_fence.c +++ b/linux-core/i915_fence.c @@ -40,7 +40,7 @@  static void i915_perform_flush(struct drm_device * dev)  { -	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];  	struct drm_fence_driver *driver = dev->driver->fence_driver; @@ -123,7 +123,7 @@ void i915_poke_flush(struct drm_device * dev, uint32_t class)  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 (!dev_priv)  		return -EINVAL; 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_crt.c b/linux-core/intel_crt.c new file mode 100644 index 00000000..d2e1f95c --- /dev/null +++ b/linux-core/intel_crt.c @@ -0,0 +1,248 @@ +/* + * 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 "intel_drv.h" +#include "i915_drm.h" +#include "i915_drv.h" + +static void intel_crt_dpms(struct drm_output *output, int mode) +{ +	struct drm_device *dev = output->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_output *output) +{ +	 +} + +static void intel_crt_restore(struct drm_output *output) +{ + +} + +static int intel_crt_mode_valid(struct drm_output *output, +				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_output *output, +				 struct drm_display_mode *mode, +				 struct drm_display_mode *adjusted_mode) +{ +	return true; +} + +static void intel_crt_mode_set(struct drm_output *output, +			       struct drm_display_mode *mode, +			       struct drm_display_mode *adjusted_mode) +{ +	struct drm_device *dev = output->dev; +	struct drm_crtc *crtc = output->crtc; +	struct intel_crtc *intel_crtc = crtc->driver_private; +	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. + * + * Only for I945G/GM. + * + * \return TRUE if CRT is connected. + * \return FALSE if CRT is disconnected. + */ +static bool intel_crt_detect_hotplug(struct drm_output *output) +{ +	struct drm_device *dev = output->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_output *output) +{ +	struct intel_output *intel_output = output->driver_private; + +	/* CRT should always be at 0, but check anyway */ +	if (intel_output->type != INTEL_OUTPUT_ANALOG) +		return false; +	 +	return intel_ddc_probe(output); +} + +static enum drm_output_status intel_crt_detect(struct drm_output *output) +{ +	struct drm_device *dev = output->dev; +	 +	if (IS_I945G(dev)| IS_I945GM(dev) || IS_I965G(dev)) { +		if (intel_crt_detect_hotplug(output)) +			return output_status_connected; +		else +			return output_status_disconnected; +	} + +	if (intel_crt_detect_ddc(output)) +		return output_status_connected; + +	/* TODO use load detect */ +	return output_status_unknown; +} + +static void intel_crt_destroy(struct drm_output *output) +{ +	struct intel_output *intel_output = output->driver_private; + +	intel_i2c_destroy(intel_output->ddc_bus); +	kfree(output->driver_private); +} + +static int intel_crt_get_modes(struct drm_output *output) +{ +	return intel_ddc_get_modes(output); +} + +/* + * Routines for controlling stuff on the analog port + */ +static const struct drm_output_funcs intel_crt_output_funcs = { +	.dpms = intel_crt_dpms, +	.save = intel_crt_save, +	.restore = intel_crt_restore, +	.mode_valid = intel_crt_mode_valid, +	.mode_fixup = intel_crt_mode_fixup, +	.prepare = intel_output_prepare, +	.mode_set = intel_crt_mode_set, +	.commit = intel_output_commit, +	.detect = intel_crt_detect, +	.get_modes = intel_crt_get_modes, +	.cleanup = intel_crt_destroy, +}; + +void intel_crt_init(struct drm_device *dev) +{ +	struct drm_output *output; +	struct intel_output *intel_output; + +	output = drm_output_create(dev, &intel_crt_output_funcs, "VGA"); + +	intel_output = kmalloc(sizeof(struct intel_output), GFP_KERNEL); +	if (!intel_output) { +		drm_output_destroy(output); +		return; +	} +	/* 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; +	output->driver_private = intel_output; +	output->interlace_allowed = 0; +	output->doublescan_allowed = 0; +} diff --git a/linux-core/intel_display.c b/linux-core/intel_display.c new file mode 100644 index 00000000..a81cfe69 --- /dev/null +++ b/linux-core/intel_display.c @@ -0,0 +1,1235 @@ +/* + * 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" + +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_output *l_entry; + +    list_for_each_entry(l_entry, &mode_config->output_list, head) { +	    if (l_entry->crtc == crtc) { +		    struct intel_output *intel_output = l_entry->driver_private; +		    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 outputs. + */ + +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, +	 * output, 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 = crtc->driver_private; + +		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 intel_crtc *intel_crtc = crtc->driver_private; +	int pipe = intel_crtc->pipe; +	unsigned long Start, Offset; +	int dspbase = (pipe == 0 ? DSPABASE : DSPBBASE); +	int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF); + +	Start = crtc->fb->offset; +	Offset = y * crtc->fb->pitch + x; + +	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_priv->sarea_priv)  +		return; +		 +	switch (pipe) { +	case 0: +		dev_priv->sarea_priv->planeA_x = x; +		dev_priv->sarea_priv->planeA_y = y; +		break; +	case 1: +		dev_priv->sarea_priv->planeB_x = x; +		dev_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_private *dev_priv = dev->dev_private; +	struct intel_crtc *intel_crtc = crtc->driver_private; +	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) ? DSPABASE : DSPBBASE; +	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_priv->sarea_priv) +		return; + +	enabled = crtc->enabled && mode != DPMSModeOff; +	 +	switch (pipe) { +	case 0: +		dev_priv->sarea_priv->planeA_w = enabled ? crtc->mode.hdisplay : 0; +		dev_priv->sarea_priv->planeA_h = enabled ? crtc->mode.vdisplay : 0; +		break; +	case 1: +		dev_priv->sarea_priv->planeB_w = enabled ? crtc->mode.hdisplay : 0; +		dev_priv->sarea_priv->planeB_h = enabled ? crtc->mode.vdisplay : 0; +		break; +	default: +		DRM_ERROR("Can't update pipe %d in SAREA\n", pipe); +		break; +	} +} + +static bool intel_crtc_lock(struct drm_crtc *crtc) +{ +   /* Sync the engine before mode switch */ +//   i830WaitSync(crtc->scrn); + +#if 0 // TODO def XF86DRI +    return I830DRILock(crtc->scrn); +#else +    return FALSE; +#endif +} + +static void intel_crtc_unlock (struct drm_crtc *crtc) +{ +#if 0 // TODO def XF86DRI +    I830DRIUnlock (crtc->scrn); +#endif +} + +static void intel_crtc_prepare (struct drm_crtc *crtc) +{ +	crtc->funcs->dpms(crtc, DPMSModeOff); +} + +static void intel_crtc_commit (struct drm_crtc *crtc) +{ +	crtc->funcs->dpms(crtc, DPMSModeOn); +} + +void intel_output_prepare (struct drm_output *output) +{ +	/* lvds has its own version of prepare see intel_lvds_prepare */ +	output->funcs->dpms(output, DPMSModeOff); +} + +void intel_output_commit (struct drm_output *output) +{ +	/* lvds has its own version of commit see intel_lvds_commit */ +	output->funcs->dpms(output, 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, I915_GCFGC, &gcfgc); +		 +		if (gcfgc & I915_LOW_FREQUENCY_ENABLE) +			return 133000; +		else { +			switch (gcfgc & I915_DISPLAY_CLOCK_MASK) { +			case I915_DISPLAY_CLOCK_333_MHZ: +				return 333000; +			default: +			case I915_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, I855_HPLLCC); +		 +#endif +		u16 hpllcc = 0; +		/* Assume that the hardware is in the high speed state.  This +		 * should be the default. +		 */ +		switch (hpllcc & I855_CLOCK_CONTROL_MASK) { +		case I855_CLOCK_133_200: +		case I855_CLOCK_100_200: +			return 200000; +		case I855_CLOCK_166_250: +			return 250000; +		case I855_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 = crtc->driver_private; +	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 dspstride_reg = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE; +	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_output *output; + +	list_for_each_entry(output, &mode_config->output_list, head) { +		struct intel_output *intel_output = output->driver_private; + +		if (output->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; + +	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 32: +		dspcntr |= DISPPLANE_32BPP_NO_ALPHA; +		break; +	default: +		DRM_ERROR("Unknown color depth\n"); +		return; +	} +	 + +	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(dev, 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)); +	I915_WRITE(dspstride_reg, crtc->fb->pitch); +	/* 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 = crtc->driver_private; +	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]); +	} +} + +/** Sets the color ramps on behalf of RandR */ +static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, +				 u16 blue, int regno) +{ +	struct intel_crtc *intel_crtc = crtc->driver_private; +	 +	intel_crtc->lut_r[regno] = red >> 8; +	intel_crtc->lut_g[regno] = green >> 8; +	intel_crtc->lut_b[regno] = blue >> 8; +} + +/* 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 = crtc->driver_private; +	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 output +	 * 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 = crtc->driver_private; +	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 const struct drm_crtc_funcs intel_crtc_funcs = { +	.dpms = intel_crtc_dpms, +	.lock = intel_crtc_lock, +	.unlock = intel_crtc_unlock, +	.mode_fixup = intel_crtc_mode_fixup, +	.mode_set = intel_crtc_mode_set, +	.gamma_set = intel_crtc_gamma_set, +	.prepare = intel_crtc_prepare, +	.commit = intel_crtc_commit, +}; + + +void intel_crtc_init(struct drm_device *dev, int pipe) +{ +	struct drm_crtc *crtc; +	struct intel_crtc *intel_crtc; +	int i; + +	crtc = drm_crtc_create(dev, &intel_crtc_funcs); +	if (crtc == NULL) +		return; + +	intel_crtc = kzalloc(sizeof(struct intel_crtc), GFP_KERNEL); +	if (intel_crtc == NULL) { +		kfree(crtc); +		return; +	} + +	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; +	} + +	crtc->driver_private = intel_crtc; +} + +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 = crtc->driver_private; +		if (intel_crtc->pipe == pipe) +			break; +	} +	return crtc; +} + +int intel_output_clones(struct drm_device *dev, int type_mask) +{ +	int index_mask = 0; +	struct drm_output *output; +	int entry = 0; + +        list_for_each_entry(output, &dev->mode_config.output_list, head) { +		struct intel_output *intel_output = output->driver_private; +		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_output *output; + +	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); +	} + +	list_for_each_entry(output, &dev->mode_config.output_list, head) { +		struct intel_output *intel_output = output->driver_private; +		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; +		} +		output->possible_crtcs = crtc_mask; +		output->possible_clones = intel_output_clones(dev, clone_mask); +	} +} + +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.max_width = 4096; +	dev->mode_config.max_height = 4096; + +	/* 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); + +	//drm_initial_config(dev, false); +} + +void intel_modeset_cleanup(struct drm_device *dev) +{ +	drm_mode_config_cleanup(dev); +} diff --git a/linux-core/intel_drv.h b/linux-core/intel_drv.h new file mode 100644 index 00000000..7de4b924 --- /dev/null +++ b/linux-core/intel_drv.h @@ -0,0 +1,82 @@ +/* + * 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" + +/* + * 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 + +#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_output { +	int type; +	struct intel_i2c_chan *i2c_bus; /* for control functions */ +	struct intel_i2c_chan *ddc_bus; /* for DDC only stuff */ +	bool load_detect_tmp; +	void *dev_priv; +}; + +struct intel_crtc { +	int pipe; +	u8 lut_r[256], lut_g[256], lut_b[256]; +}; + +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 drm_output *output); +extern bool intel_ddc_probe(struct drm_output *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_lvds_init(struct drm_device *dev); + +extern void intel_crtc_load_lut(struct drm_crtc *crtc); +extern void intel_output_prepare (struct drm_output *output); +extern void intel_output_commit (struct drm_output *output); +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 int intelfb_probe(struct drm_device *dev, struct drm_crtc *crtc); +extern int intelfb_remove(struct drm_device *dev, struct drm_crtc *crtc); + +#endif /* __INTEL_DRV_H__ */ diff --git a/linux-core/intel_fb.c b/linux-core/intel_fb.c new file mode 100644 index 00000000..564a913e --- /dev/null +++ b/linux-core/intel_fb.c @@ -0,0 +1,659 @@ +/* + * 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 "i915_drm.h" +#include "i915_drv.h" + +struct intelfb_par { +	struct drm_device *dev; +	struct drm_crtc *crtc; +}; + +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_framebuffer *fb = par->crtc->fb; +	struct drm_crtc *crtc = par->crtc; + +	if (regno > 255) +		return 1; + +	if (fb->depth == 8) { +		if (crtc->funcs->gamma_set) +			crtc->funcs->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 drm_device *dev = par->dev; +	struct drm_framebuffer *fb = par->crtc->fb; +        struct drm_display_mode *drm_mode; +        struct drm_output *output; +        int depth, found = 0; + +        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 && +                    (((PICOS2KHZ(var->pixclock))/1000) >= ((drm_mode->clock/1000)-1)) && +                    (((PICOS2KHZ(var->pixclock))/1000) <= ((drm_mode->clock/1000)+1))) { +			found = 1; +			break; +		} +	} +  +        if (!found) +                return -EINVAL; +#endif + +	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_framebuffer *fb = par->crtc->fb; +	struct drm_device *dev = par->dev; +        struct drm_display_mode *drm_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: +                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 && +                    (((PICOS2KHZ(var->pixclock))/1000) >= ((drm_mode->clock/1000)-1)) && +                    (((PICOS2KHZ(var->pixclock))/1000) <= ((drm_mode->clock/1000)+1))) { +			found = 1; +			break; +		} +        } + +	if (!found) { +		DRM_ERROR("Couldn't find a mode for requested %dx%d-%d\n", +			var->xres,var->yres,var_to_refresh(var)); +		return -EINVAL; +	} +#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 + +	drm_mode_debug_printmodeline(dev, drm_mode); + +        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; +} + +#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 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, +}; + +int intelfb_probe(struct drm_device *dev, struct drm_crtc *crtc) +{ +	struct fb_info *info; +	struct intelfb_par *par; +	struct device *device = &dev->pdev->dev;  +	struct drm_framebuffer *fb; +	struct drm_display_mode *mode = crtc->desired_mode; +	struct drm_buffer_object *fbo = NULL; +	int ret; + +	info = framebuffer_alloc(sizeof(struct intelfb_par), device); +	if (!info){ +		return -EINVAL; +	} + +	fb = drm_framebuffer_create(dev); +	if (!fb) { +		framebuffer_release(info); +		DRM_ERROR("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; +	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_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"); +		drm_framebuffer_destroy(fb); +		framebuffer_release(info); +		return -EINVAL; +	} + +	fb->offset = fbo->offset; +	fb->bo = fbo; +	printk("allocated %dx%d fb: 0x%08lx, bo %p\n", fb->width, +		       fb->height, fbo->offset, fbo); + + +	fb->fbdev = info; +		 +	par = info->par; + +	par->dev = dev; +	par->crtc = crtc; + +	info->fbops = &intelfb_ops; + +	strcpy(info->fix.id, "intelfb"); +	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_I830; +	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; /* 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 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; + 		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) +		return -EINVAL; + +	printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, +	       info->fix.id); +	return 0; +} +EXPORT_SYMBOL(intelfb_probe); + +int intelfb_remove(struct drm_device *dev, struct drm_crtc *crtc) +{ +	struct drm_framebuffer *fb = crtc->fb; +	struct fb_info *info = fb->fbdev; +	 +	if (info) { +		unregister_framebuffer(info); +		framebuffer_release(info); +		drm_mem_reg_iounmap(dev, &fb->bo->mem, fb->virtual_base); +		drm_bo_usage_deref_unlocked(&fb->bo); +		drm_framebuffer_destroy(fb); +	} +	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..4f15c13a --- /dev/null +++ b/linux-core/intel_lvds.c @@ -0,0 +1,502 @@ +/* + * 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_output *output, int mode) +{ +	struct drm_device *dev = output->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_output *output) +{ +	struct drm_device *dev = output->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; + +	dev_priv->savePP_ON = I915_READ(LVDSPP_ON); +	dev_priv->savePP_OFF = I915_READ(LVDSPP_OFF); +	dev_priv->savePP_CONTROL = I915_READ(PP_CONTROL); +	dev_priv->savePP_CYCLE = I915_READ(PP_CYCLE); +	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_output *output) +{ +	struct drm_device *dev = output->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; + +	I915_WRITE(BLC_PWM_CTL, dev_priv->saveBLC_PWM_CTL); +	I915_WRITE(LVDSPP_ON, dev_priv->savePP_ON); +	I915_WRITE(LVDSPP_OFF, dev_priv->savePP_OFF); +	I915_WRITE(PP_CYCLE, dev_priv->savePP_CYCLE); +	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_output *output, +				 struct drm_display_mode *mode) +{ +	struct drm_device *dev = output->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_output *output, +				  struct drm_display_mode *mode, +				  struct drm_display_mode *adjusted_mode) +{ +	struct drm_device *dev = output->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_crtc *intel_crtc = output->crtc->driver_private; +	struct drm_output *tmp_output; + +	/* 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_output, &dev->mode_config.output_list, head) { +		if (tmp_output != output && tmp_output->crtc == output->crtc) { +			printk(KERN_ERR "Can't enable LVDS and another " +			       "output 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_output *output) +{ +	struct drm_device *dev = output->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_output *output) +{ +	struct drm_device *dev = output->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_output *output, +				struct drm_display_mode *mode, +				struct drm_display_mode *adjusted_mode) +{ +	struct drm_device *dev = output->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_crtc *intel_crtc = output->crtc->driver_private; +	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. +	 */ +	pfit_control = (PFIT_ENABLE | VERT_AUTO_SCALE | HORIZ_AUTO_SCALE | +			VERT_INTERP_BILINEAR | HORIZ_INTERP_BILINEAR); + +	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 OUTPUT_STATUS_CONNECTED.  This output should only have + * been set up if the LVDS was actually connected anyway. + */ +static enum drm_output_status intel_lvds_detect(struct drm_output *output) +{ +	return output_status_connected; +} + +/** + * Return the list of DDC modes if available, or the BIOS fixed mode otherwise. + */ +static int intel_lvds_get_modes(struct drm_output *output) +{ +	struct drm_device *dev = output->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	int ret = 0; + +	ret = intel_ddc_get_modes(output); + +	if (ret) +		return ret; + +	/* Didn't get an EDID */ +	if (!output->monitor_info) { +		struct drm_display_info *dspinfo; +		dspinfo = kzalloc(sizeof(*output->monitor_info), GFP_KERNEL); +		if (!dspinfo) +			goto out; + +		/* Set wide sync ranges so we get all modes +		 * handed to valid_mode for checking +		 */ +		dspinfo->min_vfreq = 0; +		dspinfo->max_vfreq = 200; +		dspinfo->min_hfreq = 0; +		dspinfo->max_hfreq = 200; +		output->monitor_info = dspinfo; +	} + +out: +	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(output, mode); +		return 1; +	} + +	return 0; +} + +/** + * intel_lvds_destroy - unregister and free LVDS structures + * @output: output to free + * + * Unregister the DDC bus for this output then free the driver private + * structure. + */ +static void intel_lvds_destroy(struct drm_output *output) +{ +	struct intel_output *intel_output = output->driver_private; + +	intel_i2c_destroy(intel_output->ddc_bus); +	kfree(output->driver_private); +} + +static const struct drm_output_funcs intel_lvds_output_funcs = { +	.dpms = intel_lvds_dpms, +	.save = intel_lvds_save, +	.restore = intel_lvds_restore, +	.mode_valid = intel_lvds_mode_valid, +	.mode_fixup = intel_lvds_mode_fixup, +	.prepare = intel_lvds_prepare, +	.mode_set = intel_lvds_mode_set, +	.commit = intel_lvds_commit, +	.detect = intel_lvds_detect, +	.get_modes = intel_lvds_get_modes, +	.cleanup = intel_lvds_destroy +}; + +/** + * intel_lvds_init - setup LVDS outputs on this device + * @dev: drm device + * + * Create the output, 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 drm_output *output; +	struct intel_output *intel_output; +	struct drm_display_mode *scan; /* *modes, *bios_mode; */ +	struct drm_crtc *crtc; +	u32 lvds; +	int pipe; + +	output = drm_output_create(dev, &intel_lvds_output_funcs, "LVDS"); +	if (!output) +		return; + +	intel_output = kmalloc(sizeof(struct intel_output), GFP_KERNEL); +	if (!intel_output) { +		drm_output_destroy(output); +		return; +	} + +	intel_output->type = INTEL_OUTPUT_LVDS; +	output->driver_private = intel_output; +	output->subpixel_order = SubPixelHorizontalRGB; +	output->interlace_allowed = FALSE; +	output->doublescan_allowed = FALSE; + +	/* 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"); +		return; +	} + +	/* +	 * Attempt to get the fixed panel mode from DDC.  Assume that the +	 * preferred mode is the right one. +	 */ +	intel_ddc_get_modes(output); + +	list_for_each_entry(scan, &output->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 */ +		} +	} + +	/* +	 * 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: probe the BIOS for modes and check for LVDS quirks */ +#if 0 +	/* Get the LVDS fixed mode out of the BIOS.  We should support LVDS +	 * with the BIOS being unavailable or broken, but lack the +	 * configuration options for now. +	 */ +	bios_mode = intel_bios_get_panel_mode(pScrn); +	if (bios_mode != NULL) { +		if (dev_priv->panel_fixed_mode != NULL) { +			if (dev_priv->debug_modes && +			    !xf86ModesEqual(dev_priv->panel_fixed_mode, +					    bios_mode)) +			{ +				xf86DrvMsg(pScrn->scrnIndex, X_WARNING, +					   "BIOS panel mode data doesn't match probed data, " +					   "continuing with probed.\n"); +				xf86DrvMsg(pScrn->scrnIndex, X_INFO, "BIOS mode:\n"); +				xf86PrintModeline(pScrn->scrnIndex, bios_mode); +				xf86DrvMsg(pScrn->scrnIndex, X_INFO, "probed mode:\n"); +				xf86PrintModeline(pScrn->scrnIndex, dev_priv->panel_fixed_mode); +				xfree(bios_mode->name); +				xfree(bios_mode); +			} +		}  else { +			dev_priv->panel_fixed_mode = bios_mode; +		} +	} else { +		xf86DrvMsg(pScrn->scrnIndex, X_WARNING, +			   "Couldn't detect panel mode.  Disabling panel\n"); +		goto disable_exit; +	} + +	/* +	 * Blacklist machines with BIOSes that list an LVDS panel without +	 * actually having one. +	 */ +	if (dev_priv->PciInfo->chipType == PCI_CHIP_I945_GM) { +		/* aopen mini pc */ +		if (dev_priv->PciInfo->subsysVendor == 0xa0a0) +			goto disable_exit; + +		if ((dev_priv->PciInfo->subsysVendor == 0x8086) && +		    (dev_priv->PciInfo->subsysCard == 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) +			{ +				xf86DrvMsg(pScrn->scrnIndex, X_INFO, +					   "Suspected Mac Mini, ignoring the LVDS\n"); +				goto disable_exit; +			} +		} +	} + +#endif + +out: +	return; + +failed: +        DRM_DEBUG("No LVDS modes found, disabling.\n"); +	drm_output_destroy(output); /* calls intel_lvds_destroy above */ +} diff --git a/linux-core/intel_modes.c b/linux-core/intel_modes.c new file mode 100644 index 00000000..601770e1 --- /dev/null +++ b/linux-core/intel_modes.c @@ -0,0 +1,55 @@ +/* + * 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 drm_output *output) +{ +	struct intel_output *intel_output = output->driver_private; +	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 + * @output: DRM output device to use + * + * Fetch the EDID information from @output using the DDC bus. + */ +int intel_ddc_get_modes(struct drm_output *output) +{ +	struct intel_output *intel_output = output->driver_private; + +	return drm_add_edid_modes(output, &intel_output->ddc_bus->adapter); +} diff --git a/linux-core/intel_sdvo.c b/linux-core/intel_sdvo.c new file mode 100644 index 00000000..51fe43cb --- /dev/null +++ b/linux-core/intel_sdvo.c @@ -0,0 +1,1087 @@ +/* + * 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). + */ +static void intel_sdvo_write_sdvox(struct drm_output *output, u32 val) +{ +	struct drm_device *dev = output->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_output *intel_output = output->driver_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 drm_output *output, u8 addr, +				 u8 *ch) +{ +	struct intel_output *intel_output = output->driver_private; +	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_read_byte_quiet(struct drm_output *output, int addr, +				       u8 *ch) +{ +	return true; + +} + +static bool intel_sdvo_write_byte(struct drm_output *output, int addr, +				  u8 ch) +{ +	struct intel_output *intel_output = output->driver_private; +	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 drm_output *output, u8 cmd, +				 void *args, int args_len) +{ +	struct intel_output *intel_output = output->driver_private; +	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(output, SDVO_I2C_ARG_0 - i, ((u8*)args)[i]); +	} + +	intel_sdvo_write_byte(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 drm_output *output, void *response, +				   int response_len) +{ +	struct intel_output *intel_output = output->driver_private; +	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(output, SDVO_I2C_RETURN_0 + i, +				     &((u8 *)response)[i]); +		} + +		/* read the return status */ +		intel_sdvo_read_byte(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 drm_output *output, u8 target) +{ +	intel_sdvo_write_cmd(output, SDVO_CMD_SET_CONTROL_BUS_SWITCH, &target, 1); +} + +static bool intel_sdvo_set_target_input(struct drm_output *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(output, SDVO_CMD_SET_TARGET_INPUT, &targets, +			     sizeof(targets)); + +	status = intel_sdvo_read_response(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 drm_output *output, bool *input_1, bool *input_2) +{ +	struct intel_sdvo_get_trained_inputs_response response; +	u8 status; + +	intel_sdvo_write_cmd(output, SDVO_CMD_GET_TRAINED_INPUTS, NULL, 0); +	status = intel_sdvo_read_response(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 drm_output *output, +					  u16 *outputs) +{ +	u8 status; + +	intel_sdvo_write_cmd(output, SDVO_CMD_GET_ACTIVE_OUTPUTS, NULL, 0); +	status = intel_sdvo_read_response(output, outputs, sizeof(*outputs)); + +	return (status == SDVO_CMD_STATUS_SUCCESS); +} + +static bool intel_sdvo_set_active_outputs(struct drm_output *output, +					  u16 outputs) +{ +	u8 status; + +	intel_sdvo_write_cmd(output, SDVO_CMD_SET_ACTIVE_OUTPUTS, &outputs, +			     sizeof(outputs)); +	status = intel_sdvo_read_response(output, NULL, 0); +	return (status == SDVO_CMD_STATUS_SUCCESS); +} + +static bool intel_sdvo_set_encoder_power_state(struct drm_output *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(output, SDVO_CMD_SET_ENCODER_POWER_STATE, &state, +			     sizeof(state)); +	status = intel_sdvo_read_response(output, NULL, 0); + +	return (status == SDVO_CMD_STATUS_SUCCESS); +} + +static bool intel_sdvo_get_input_pixel_clock_range(struct drm_output *output, +						   int *clock_min, +						   int *clock_max) +{ +	struct intel_sdvo_pixel_clock_range clocks; +	u8 status; + +	intel_sdvo_write_cmd(output, SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE, +			     NULL, 0); + +	status = intel_sdvo_read_response(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 drm_output *output, +					 u16 outputs) +{ +	u8 status; + +	intel_sdvo_write_cmd(output, SDVO_CMD_SET_TARGET_OUTPUT, &outputs, +			     sizeof(outputs)); + +	status = intel_sdvo_read_response(output, NULL, 0); +	return (status == SDVO_CMD_STATUS_SUCCESS); +} + +static bool intel_sdvo_get_timing(struct drm_output *output, u8 cmd, +				  struct intel_sdvo_dtd *dtd) +{ +	u8 status; + +	intel_sdvo_write_cmd(output, cmd, NULL, 0); +	status = intel_sdvo_read_response(output, &dtd->part1, +					  sizeof(dtd->part1)); +	if (status != SDVO_CMD_STATUS_SUCCESS) +		return false; + +	intel_sdvo_write_cmd(output, cmd + 1, NULL, 0); +	status = intel_sdvo_read_response(output, &dtd->part2, +					  sizeof(dtd->part2)); +	if (status != SDVO_CMD_STATUS_SUCCESS) +		return false; + +	return true; +} + +static bool intel_sdvo_get_input_timing(struct drm_output *output, +					 struct intel_sdvo_dtd *dtd) +{ +	return intel_sdvo_get_timing(output, +				     SDVO_CMD_GET_INPUT_TIMINGS_PART1, dtd); +} + +static bool intel_sdvo_get_output_timing(struct drm_output *output, +					 struct intel_sdvo_dtd *dtd) +{ +	return intel_sdvo_get_timing(output, +				     SDVO_CMD_GET_OUTPUT_TIMINGS_PART1, dtd); +} + +static bool intel_sdvo_set_timing(struct drm_output *output, u8 cmd, +				  struct intel_sdvo_dtd *dtd) +{ +	u8 status; + +	intel_sdvo_write_cmd(output, cmd, &dtd->part1, sizeof(dtd->part1)); +	status = intel_sdvo_read_response(output, NULL, 0); +	if (status != SDVO_CMD_STATUS_SUCCESS) +		return false; + +	intel_sdvo_write_cmd(output, cmd + 1, &dtd->part2, sizeof(dtd->part2)); +	status = intel_sdvo_read_response(output, NULL, 0); +	if (status != SDVO_CMD_STATUS_SUCCESS) +		return false; + +	return true; +} + +static bool intel_sdvo_set_input_timing(struct drm_output *output, +					 struct intel_sdvo_dtd *dtd) +{ +	return intel_sdvo_set_timing(output, +				     SDVO_CMD_SET_INPUT_TIMINGS_PART1, dtd); +} + +static bool intel_sdvo_set_output_timing(struct drm_output *output, +					 struct intel_sdvo_dtd *dtd) +{ +	return intel_sdvo_set_timing(output, +				     SDVO_CMD_SET_OUTPUT_TIMINGS_PART1, dtd); +} + +#if 0 +static bool intel_sdvo_get_preferred_input_timing(struct drm_output *output, +						  struct intel_sdvo_dtd *dtd) +{ +	struct intel_output *intel_output = output->driver_private; +	struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; +	u8 status; + +	intel_sdvo_write_cmd(output, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1, +			     NULL, 0); + +	status = intel_sdvo_read_response(output, &dtd->part1, +					  sizeof(dtd->part1)); +	if (status != SDVO_CMD_STATUS_SUCCESS) +		return false; + +	intel_sdvo_write_cmd(output, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2, +			     NULL, 0); +	status = intel_sdvo_read_response(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 drm_output *output) +{ +	u8 response, status; + +	intel_sdvo_write_cmd(output, SDVO_CMD_GET_CLOCK_RATE_MULT, NULL, 0); +	status = intel_sdvo_read_response(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 drm_output *output, u8 val) +{ +	u8 status; + +	intel_sdvo_write_cmd(output, SDVO_CMD_SET_CLOCK_RATE_MULT, &val, 1); +	status = intel_sdvo_read_response(output, NULL, 0); +	if (status != SDVO_CMD_STATUS_SUCCESS) +		return false; + +	return true; +} + +static bool intel_sdvo_mode_fixup(struct drm_output *output, +				  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_output *output, +				struct drm_display_mode *mode, +				struct drm_display_mode *adjusted_mode) +{ +	struct drm_device *dev = output->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_crtc *crtc = output->crtc; +	struct intel_crtc *intel_crtc = crtc->driver_private; +	struct intel_output *intel_output = output->driver_private; +	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(output, sdvo_priv->active_outputs); +	intel_sdvo_set_output_timing(output, &output_dtd); + +	/* Set the input timing to the screen. Assume always input 0. */ +	intel_sdvo_set_target_input(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(output, clock, +							   width, height); +	if (success) { +		struct intel_sdvo_dtd *input_dtd; +		 +		intel_sdvo_get_preferred_input_timing(output, &input_dtd); +		intel_sdvo_set_input_timing(output, &input_dtd); +	} +#else +	intel_sdvo_set_input_timing(output, &output_dtd); +#endif	 + +	switch (intel_sdvo_get_pixel_multiplier(mode)) { +	case 1: +		intel_sdvo_set_clock_rate_mult(output, +					       SDVO_CLOCK_RATE_MULT_1X); +		break; +	case 2: +		intel_sdvo_set_clock_rate_mult(output, +					       SDVO_CLOCK_RATE_MULT_2X); +		break; +	case 4: +		intel_sdvo_set_clock_rate_mult(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(output, sdvox); +} + +static void intel_sdvo_dpms(struct drm_output *output, int mode) +{ +	struct drm_device *dev = output->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_output *intel_output = output->driver_private; +	struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; +	u32 temp; + +	if (mode != DPMSModeOn) { +		intel_sdvo_set_active_outputs(output, 0); +		if (0) +			intel_sdvo_set_encoder_power_state(output, mode); + +		if (mode == DPMSModeOff) { +			temp = I915_READ(sdvo_priv->output_device); +			if ((temp & SDVO_ENABLE) != 0) { +				intel_sdvo_write_sdvox(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(output, temp | SDVO_ENABLE); +		for (i = 0; i < 2; i++) +		  intel_wait_for_vblank(dev); +		 +		status = intel_sdvo_get_trained_inputs(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(output, mode); +		intel_sdvo_set_active_outputs(output, sdvo_priv->active_outputs); +	}	 +	return; +} + +static void intel_sdvo_save(struct drm_output *output) +{ +	struct drm_device *dev = output->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_output *intel_output = output->driver_private; +	struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; +	int o; + +	sdvo_priv->save_sdvo_mult = intel_sdvo_get_clock_rate_mult(output); +	intel_sdvo_get_active_outputs(output, &sdvo_priv->save_active_outputs); + +	if (sdvo_priv->caps.sdvo_inputs_mask & 0x1) { +		intel_sdvo_set_target_input(output, true, false); +		intel_sdvo_get_input_timing(output, +					    &sdvo_priv->save_input_dtd_1); +	} + +	if (sdvo_priv->caps.sdvo_inputs_mask & 0x2) { +		intel_sdvo_set_target_input(output, false, true); +		intel_sdvo_get_input_timing(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(output, this_output); +			intel_sdvo_get_output_timing(output, +						     &sdvo_priv->save_output_dtd[o]); +		} +	} + +	sdvo_priv->save_SDVOX = I915_READ(sdvo_priv->output_device); +} + +static void intel_sdvo_restore(struct drm_output *output) +{ +	struct drm_device *dev = output->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_output *intel_output = output->driver_private; +	struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; +	int o; +	int i; +	bool input1, input2; +	u8 status; + +	intel_sdvo_set_active_outputs(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(output, this_output); +			intel_sdvo_set_output_timing(output, &sdvo_priv->save_output_dtd[o]); +		} +	} + +	if (sdvo_priv->caps.sdvo_inputs_mask & 0x1) { +		intel_sdvo_set_target_input(output, true, false); +		intel_sdvo_set_input_timing(output, &sdvo_priv->save_input_dtd_1); +	} + +	if (sdvo_priv->caps.sdvo_inputs_mask & 0x2) { +		intel_sdvo_set_target_input(output, false, true); +		intel_sdvo_set_input_timing(output, &sdvo_priv->save_input_dtd_2); +	} +	 +	intel_sdvo_set_clock_rate_mult(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(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(output, sdvo_priv->save_active_outputs); +} + +static int intel_sdvo_mode_valid(struct drm_output *output, +				 struct drm_display_mode *mode) +{ +	struct intel_output *intel_output = output->driver_private; +	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 drm_output *output, struct intel_sdvo_caps *caps) +{ +	u8 status; + +	intel_sdvo_write_cmd(output, SDVO_CMD_GET_DEVICE_CAPS, NULL, 0); +	status = intel_sdvo_read_response(output, caps, sizeof(*caps)); +	if (status != SDVO_CMD_STATUS_SUCCESS) +		return false; + +	return true; +} + + +static void intel_sdvo_dump_cmd(struct drm_output *output, int opcode) +{ + + +} + +static void intel_sdvo_dump_device(struct drm_output *output) +{ + +} + +void intel_sdvo_dump(void) +{ + +} + + +static enum drm_output_status intel_sdvo_detect(struct drm_output *output) +{ +	u8 response[2]; +	u8 status; + +	intel_sdvo_write_cmd(output, SDVO_CMD_GET_ATTACHED_DISPLAYS, NULL, 0); +	status = intel_sdvo_read_response(output, &response, 2); + +	DRM_DEBUG("SDVO response %d %d\n", response[0], response[1]); +	if ((response[0] != 0) || (response[1] != 0)) +		return output_status_connected; +	else +		return output_status_disconnected; +} + +static int intel_sdvo_get_modes(struct drm_output *output) +{ +	/* set the bus switch and get the modes */ +	intel_sdvo_set_control_bus_switch(output, SDVO_CONTROL_BUS_DDC2); +	intel_ddc_get_modes(output); + +	if (list_empty(&output->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_output *output) +{ +	struct intel_output *intel_output = output->driver_private; + +	if (intel_output->i2c_bus) +		intel_i2c_destroy(intel_output->i2c_bus); + +	if (intel_output) { +		kfree(intel_output); +		output->driver_private = NULL; +	} +} + +static const struct drm_output_funcs intel_sdvo_output_funcs = { +	.dpms = intel_sdvo_dpms, +	.save = intel_sdvo_save, +	.restore = intel_sdvo_restore, +	.mode_valid = intel_sdvo_mode_valid, +	.mode_fixup = intel_sdvo_mode_fixup, +	.prepare = intel_output_prepare, +	.mode_set = intel_sdvo_mode_set, +	.commit = intel_output_commit, +	.detect = intel_sdvo_detect, +	.get_modes = intel_sdvo_get_modes, +	.cleanup = intel_sdvo_destroy +}; + +void intel_sdvo_init(struct drm_device *dev, int output_device) +{ +	struct drm_output *output; +	struct intel_output *intel_output; +	struct intel_sdvo_priv *sdvo_priv; +	struct intel_i2c_chan *i2cbus = NULL; +	u8 ch[0x40]; +	int i; +	char name[DRM_OUTPUT_LEN]; +	char *name_prefix; +	char *name_suffix; +	 + +	output = drm_output_create(dev, &intel_sdvo_output_funcs, NULL); +	if (!output) +		return; + +	intel_output = kcalloc(sizeof(struct intel_output)+sizeof(struct intel_sdvo_priv), 1, GFP_KERNEL); +	if (!intel_output) { +		drm_output_destroy(output); +		return; +	} + +	sdvo_priv = (struct intel_sdvo_priv *)(intel_output + 1); +	intel_output->type = INTEL_OUTPUT_SDVO; +	output->driver_private = intel_output; +	output->interlace_allowed = 0; +	output->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 == NULL) { +		drm_output_destroy(output); +		return; +	} + +	sdvo_priv->i2c_bus = i2cbus; + +	if (output_device == SDVOB) { +		name_suffix = "-1"; +		sdvo_priv->i2c_bus->slave_addr = 0x38; +	} else { +		name_suffix = "-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(output, i, &ch[i])) { +			DRM_DEBUG("No SDVO device found on SDVO%c\n", +				  output_device == SDVOB ? 'B' : 'C'); +			drm_output_destroy(output); +			return; +		} +	} + +	intel_sdvo_get_capabilities(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; +		output->subpixel_order = SubPixelHorizontalRGB; +		name_prefix="RGB"; +	} +	else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB1) +	{ +		sdvo_priv->active_outputs = SDVO_OUTPUT_RGB1; +		output->subpixel_order = SubPixelHorizontalRGB; +		name_prefix="RGB"; +	} +	else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0) +	{ +		sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS0; +		output->subpixel_order = SubPixelHorizontalRGB; +		name_prefix="TMDS"; +	} +	else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS1) +	{ +		sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS1; +		output->subpixel_order = SubPixelHorizontalRGB; +		name_prefix="TMDS"; +	} +	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]); +		drm_output_destroy(output); +		return; +	} +	strcpy (name, name_prefix); +	strcat (name, name_suffix); +	if (!drm_output_rename(output, name)) +	{ +		drm_output_destroy(output); +		return; +	} +		 + +	/* Set the input timing to the screen. Assume always input 0. */ +	intel_sdvo_set_target_input(output, true, false); +	 +	intel_sdvo_get_input_pixel_clock_range(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;	 +} 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/shared-core/drm.h b/shared-core/drm.h index ae88ce61..5c8acfa6 100644 --- a/shared-core/drm.h +++ b/shared-core/drm.h @@ -257,7 +257,8 @@ enum drm_map_flags {  	_DRM_KERNEL = 0x08,	     /**< kernel requires access */  	_DRM_WRITE_COMBINING = 0x10, /**< use write-combining if available */  	_DRM_CONTAINS_LOCK = 0x20,   /**< SHM page that contains lock */ -	_DRM_REMOVABLE = 0x40	     /**< Removable mapping */ +	_DRM_REMOVABLE = 0x40,	     /**< Removable mapping */ +        _DRM_DRIVER = 0x80           /**< Driver will take care of it */  };  struct drm_ctx_priv_map { @@ -894,6 +895,99 @@ struct drm_mm_init_arg {  	uint64_t p_size;  }; +/* + * Drm mode setting + */ +#define DRM_DISPLAY_INFO_LEN 32 +#define DRM_OUTPUT_NAME_LEN 32 +#define DRM_DISPLAY_MODE_LEN 32 + +struct drm_mode_modeinfo { + +	unsigned int id; + +	unsigned int clock; +	unsigned short hdisplay, hsync_start, hsync_end, htotal, hskew; +	unsigned short vdisplay, vsync_start, vsync_end, vtotal, vscan; + +	unsigned int vrefresh; /* vertical refresh * 1000 */ + +	unsigned int flags; + +	char name[DRM_DISPLAY_MODE_LEN]; +}; + +struct drm_mode_card_res { + +	int count_fbs; +	unsigned int __user *fb_id; + +	int count_crtcs; +	unsigned int __user *crtc_id; + +	int count_outputs; +	unsigned int __user *output_id; + +	int count_modes; +        struct drm_mode_modeinfo __user *modes; + +}; + +struct drm_mode_crtc { +	unsigned int crtc_id; /**< Id */ +	unsigned int fb_id; /**< Id of framebuffer */ + +	int x, y; /**< Position on the frameuffer */ + +	unsigned int mode; /**< Current mode used */ + +	int count_outputs; +	unsigned int outputs; /**< Outputs that are connected */ + +	int count_possibles; +	unsigned int possibles; /**< Outputs that can be connected */ + +	unsigned int __user *set_outputs; /**< Outputs to be connected */ + +	int gamma_size; + +}; + +struct drm_mode_get_output { + +	unsigned int output; /**< Id */ +	unsigned int crtc; /**< Id of crtc */ +	unsigned char name[DRM_OUTPUT_NAME_LEN]; + +	unsigned int connection; +	unsigned int mm_width, mm_height; /**< HxW in millimeters */ +	unsigned int subpixel; + +	int count_crtcs; +	unsigned int crtcs; /**< possible crtc to connect to */ + +	int count_clones; +	unsigned int clones; /**< list of clones */ + +	int count_modes; +	unsigned int __user *modes; /**< list of modes it supports */ + +}; + +struct drm_mode_fb_cmd { +        unsigned int buffer_id; +        unsigned int width, height; +        unsigned int pitch; +        unsigned int bpp; +        unsigned int handle; +	unsigned int depth; +}; + +struct drm_mode_mode_cmd { +	unsigned int output_id; +	unsigned int mode_id; +}; +  /**   * \name Ioctls Definitions   */ @@ -986,6 +1080,18 @@ struct drm_mm_init_arg {  #define DRM_IOCTL_BO_VERSION          DRM_IOR(0xd6, struct drm_bo_version_arg) +#define DRM_IOCTL_MODE_GETRESOURCES     DRM_IOWR(0xA0, struct drm_mode_card_res) +#define DRM_IOCTL_MODE_GETCRTC          DRM_IOWR(0xA1, struct drm_mode_crtc) +#define DRM_IOCTL_MODE_GETOUTPUT        DRM_IOWR(0xA2, struct drm_mode_get_output) +#define DRM_IOCTL_MODE_SETCRTC          DRM_IOWR(0xA3, struct drm_mode_crtc) +#define DRM_IOCTL_MODE_ADDFB            DRM_IOWR(0xA4, struct drm_mode_fb_cmd) +#define DRM_IOCTL_MODE_RMFB             DRM_IOWR(0xA5, unsigned int) +#define DRM_IOCTL_MODE_GETFB            DRM_IOWR(0xA6, struct drm_mode_fb_cmd) + +#define DRM_IOCTL_MODE_ADDMODE         DRM_IOWR(0xA7, struct drm_mode_modeinfo) +#define DRM_IOCTL_MODE_RMMODE          DRM_IOWR(0xA8, unsigned int) +#define DRM_IOCTL_MODE_ATTACHMODE      DRM_IOWR(0xA9, struct drm_mode_mode_cmd) +#define DRM_IOCTL_MODE_DETACHMODE      DRM_IOWR(0xAA, struct drm_mode_mode_cmd)  /*@}*/  /** diff --git a/shared-core/i915_dma.c b/shared-core/i915_dma.c index b1168635..07b3a35b 100644 --- a/shared-core/i915_dma.c +++ b/shared-core/i915_dma.c @@ -31,17 +31,6 @@  #include "i915_drm.h"  #include "i915_drv.h" -#define IS_I965G(dev)  (dev->pci_device == 0x2972 || \ -			dev->pci_device == 0x2982 || \ -			dev->pci_device == 0x2992 || \ -			dev->pci_device == 0x29A2 || \ -			dev->pci_device == 0x2A02 || \ -			dev->pci_device == 0x2A12) - -#define IS_G33(dev)    (dev->pci_device == 0x29C2 || \ -		   	dev->pci_device == 0x29B2 || \ -			dev->pci_device == 0x29D2)  -  /* Really want an OS-independent resettable timer.  Would like to have   * this loop run for (eg) 3 sec, but have the timer reset every time   * the head pointer changes, so that EBUSY only happens if the ring @@ -49,8 +38,8 @@   */  int i915_wait_ring(struct drm_device * dev, int n, const char *caller)  { -	drm_i915_private_t *dev_priv = dev->dev_private; -	drm_i915_ring_buffer_t *ring = &(dev_priv->ring); +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_i915_ring_buffer *ring = &(dev_priv->ring);  	u32 last_head = I915_READ(LP_RING + RING_HEAD) & HEAD_ADDR;  	int i; @@ -76,8 +65,8 @@ int i915_wait_ring(struct drm_device * dev, int n, const char *caller)  void i915_kernel_lost_context(struct drm_device * dev)  { -	drm_i915_private_t *dev_priv = dev->dev_private; -	drm_i915_ring_buffer_t *ring = &(dev_priv->ring); +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_i915_ring_buffer *ring = &(dev_priv->ring);  	ring->head = I915_READ(LP_RING + RING_HEAD) & HEAD_ADDR;  	ring->tail = I915_READ(LP_RING + RING_TAIL) & TAIL_ADDR; @@ -89,8 +78,10 @@ void i915_kernel_lost_context(struct drm_device * dev)  		dev_priv->sarea_priv->perf_boxes |= I915_BOX_RING_EMPTY;  } -static int i915_dma_cleanup(struct drm_device * dev) +int i915_dma_cleanup(struct drm_device * dev)  { +	struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; +  	/* Make sure interrupts are disabled here because the uninstall ioctl  	 * may not have been called from userspace and after dev_private  	 * is freed, it's too late. @@ -98,38 +89,14 @@ static int i915_dma_cleanup(struct drm_device * dev)  	if (dev->irq)  		drm_irq_uninstall(dev); -	if (dev->dev_private) { -		drm_i915_private_t *dev_priv = -		    (drm_i915_private_t *) dev->dev_private; - -		if (dev_priv->ring.virtual_start) { -			drm_core_ioremapfree(&dev_priv->ring.map, dev); -		} - -		if (dev_priv->status_page_dmah) { -			drm_pci_free(dev, dev_priv->status_page_dmah); -			/* Need to rewrite hardware status page */ -			I915_WRITE(0x02080, 0x1ffff000); -		} -		if (dev_priv->status_gfx_addr) { -			dev_priv->status_gfx_addr = 0; -			drm_core_ioremapfree(&dev_priv->hws_map, dev); -			I915_WRITE(0x02080, 0x1ffff000); -		} -		drm_free(dev->dev_private, sizeof(drm_i915_private_t), -			 DRM_MEM_DRIVER); - -		dev->dev_private = NULL; -	} -  	return 0;  }  static int i915_initialize(struct drm_device * dev, -			   drm_i915_private_t * dev_priv, -			   drm_i915_init_t * init) +			   struct drm_i915_private * dev_priv, +			   struct drm_i915_init * init)  { -	memset(dev_priv, 0, sizeof(drm_i915_private_t)); +	memset(dev_priv, 0, sizeof(struct drm_i915_private));  	dev_priv->sarea = drm_getsarea(dev);  	if (!dev_priv->sarea) { @@ -219,7 +186,7 @@ static int i915_initialize(struct drm_device * dev,  static int i915_dma_resume(struct drm_device * dev)  { -	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;  	DRM_DEBUG("%s\n", __FUNCTION__); @@ -258,13 +225,13 @@ static int i915_dma_resume(struct drm_device * dev)  static int i915_dma_init(struct drm_device *dev, void *data,  			 struct drm_file *file_priv)  { -	drm_i915_private_t *dev_priv; -	drm_i915_init_t *init = data; +	struct drm_i915_private *dev_priv; +	struct drm_i915_init *init = data;  	int retcode = 0;  	switch (init->func) {  	case I915_INIT_DMA: -		dev_priv = drm_alloc(sizeof(drm_i915_private_t), +		dev_priv = drm_alloc(sizeof(struct drm_i915_private),  				     DRM_MEM_DRIVER);  		if (dev_priv == NULL)  			return -ENOMEM; @@ -363,7 +330,7 @@ static int validate_cmd(int cmd)  static int i915_emit_cmds(struct drm_device * dev, int __user * buffer,  			  int dwords)  { -	drm_i915_private_t *dev_priv = dev->dev_private; +	struct drm_i915_private *dev_priv = dev->dev_private;  	int i;  	RING_LOCALS; @@ -404,7 +371,7 @@ static int i915_emit_box(struct drm_device * dev,  			 struct drm_clip_rect __user * boxes,  			 int i, int DR1, int DR4)  { -	drm_i915_private_t *dev_priv = dev->dev_private; +	struct drm_i915_private *dev_priv = dev->dev_private;  	struct drm_clip_rect box;  	RING_LOCALS; @@ -445,7 +412,7 @@ static int i915_emit_box(struct drm_device * dev,  void i915_emit_breadcrumb(struct drm_device *dev)  { -	drm_i915_private_t *dev_priv = dev->dev_private; +	struct drm_i915_private *dev_priv = dev->dev_private;  	RING_LOCALS;  	if (++dev_priv->counter > BREADCRUMB_MASK) { @@ -466,7 +433,7 @@ void i915_emit_breadcrumb(struct drm_device *dev)  int i915_emit_mi_flush(struct drm_device *dev, uint32_t flush)  { -	drm_i915_private_t *dev_priv = dev->dev_private; +	struct drm_i915_private *dev_priv = dev->dev_private;  	uint32_t flush_cmd = CMD_MI_FLUSH;  	RING_LOCALS; @@ -486,10 +453,10 @@ int i915_emit_mi_flush(struct drm_device *dev, uint32_t flush)  static int i915_dispatch_cmdbuffer(struct drm_device * dev, -				   drm_i915_cmdbuffer_t * cmd) +				   struct drm_i915_cmdbuffer * cmd)  {  #ifdef I915_HAVE_FENCE -	drm_i915_private_t *dev_priv = dev->dev_private; +	struct drm_i915_private *dev_priv = dev->dev_private;  #endif  	int nbox = cmd->num_cliprects;  	int i = 0, count, ret; @@ -526,7 +493,7 @@ static int i915_dispatch_cmdbuffer(struct drm_device * dev,  static int i915_dispatch_batchbuffer(struct drm_device * dev,  				     drm_i915_batchbuffer_t * batch)  { -	drm_i915_private_t *dev_priv = dev->dev_private; +	struct drm_i915_private *dev_priv = dev->dev_private;  	struct drm_clip_rect __user *boxes = batch->cliprects;  	int nbox = batch->num_cliprects;  	int i = 0, count; @@ -579,7 +546,7 @@ static int i915_dispatch_batchbuffer(struct drm_device * dev,  static void i915_do_dispatch_flip(struct drm_device * dev, int plane, int sync)  { -	drm_i915_private_t *dev_priv = dev->dev_private; +	struct drm_i915_private *dev_priv = dev->dev_private;  	u32 num_pages, current_page, next_page, dspbase;  	int shift = 2 * plane, x, y;  	RING_LOCALS; @@ -631,7 +598,7 @@ static void i915_do_dispatch_flip(struct drm_device * dev, int plane, int sync)  void i915_dispatch_flip(struct drm_device * dev, int planes, int sync)  { -	drm_i915_private_t *dev_priv = dev->dev_private; +	struct drm_i915_private *dev_priv = dev->dev_private;  	int i;  	DRM_DEBUG("%s: planes=0x%x pfCurrentPage=%d\n", @@ -653,7 +620,7 @@ void i915_dispatch_flip(struct drm_device * dev, int planes, int sync)  static int i915_quiescent(struct drm_device * dev)  { -	drm_i915_private_t *dev_priv = dev->dev_private; +	struct drm_i915_private *dev_priv = dev->dev_private;  	i915_kernel_lost_context(dev);  	return i915_wait_ring(dev, dev_priv->ring.Size - 8, __FUNCTION__); @@ -671,7 +638,7 @@ static int i915_flush_ioctl(struct drm_device *dev, void *data,  static int i915_batchbuffer(struct drm_device *dev, void *data,  			    struct drm_file *file_priv)  { -	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;  	drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *)  	    dev_priv->sarea_priv;  	drm_i915_batchbuffer_t *batch = data; @@ -701,10 +668,10 @@ static int i915_batchbuffer(struct drm_device *dev, void *data,  static int i915_cmdbuffer(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 *) +	struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; +	struct drm_i915_sarea *sarea_priv = (struct drm_i915_sarea *)  	    dev_priv->sarea_priv; -	drm_i915_cmdbuffer_t *cmdbuf = data; +	struct drm_i915_cmdbuffer *cmdbuf = data;  	int ret;  	DRM_DEBUG("i915 cmdbuffer, buf %p sz %d cliprects %d\n", @@ -1011,11 +978,11 @@ out_err:  static 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; +	struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;  	drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *)  		dev_priv->sarea_priv;  	struct drm_i915_execbuffer *exec_buf = data; -	struct _drm_i915_batchbuffer *batch = &exec_buf->batch; +	struct drm_i915_batchbuffer *batch = &exec_buf->batch;  	struct drm_fence_arg *fence_arg = &exec_buf->fence_arg;  	int num_buffers;  	int ret; @@ -1113,14 +1080,14 @@ out_free:  }  #endif -static int i915_do_cleanup_pageflip(struct drm_device * dev) +int i915_do_cleanup_pageflip(struct drm_device * dev)  { -	drm_i915_private_t *dev_priv = dev->dev_private; +	struct drm_i915_private *dev_priv = dev->dev_private;  	int i, planes, num_pages = dev_priv->sarea_priv->third_handle ? 3 : 2;  	DRM_DEBUG("%s\n", __FUNCTION__); -	for (i = 0, planes = 0; i < 2; i++) +	for (i = 0, planes = 0; i < 2; i++) {  		if (dev_priv->sarea_priv->pf_current_page & (0x3 << (2 * i))) {  			dev_priv->sarea_priv->pf_current_page =  				(dev_priv->sarea_priv->pf_current_page & @@ -1128,6 +1095,7 @@ static int i915_do_cleanup_pageflip(struct drm_device * dev)  			planes |= 1 << i;  		} +	}  	if (planes)  		i915_dispatch_flip(dev, planes, 0); @@ -1137,7 +1105,7 @@ static int i915_do_cleanup_pageflip(struct drm_device * dev)  static int i915_flip_bufs(struct drm_device *dev, void *data, struct drm_file *file_priv)  { -	drm_i915_flip_t *param = data; +	struct drm_i915_flip *param = data;  	DRM_DEBUG("%s\n", __FUNCTION__); @@ -1159,8 +1127,8 @@ static int i915_flip_bufs(struct drm_device *dev, void *data, struct drm_file *f  static int i915_getparam(struct drm_device *dev, void *data,  			 struct drm_file *file_priv)  { -	drm_i915_private_t *dev_priv = dev->dev_private; -	drm_i915_getparam_t *param = data; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_i915_getparam *param = data;  	int value;  	if (!dev_priv) { @@ -1194,7 +1162,7 @@ static int i915_getparam(struct drm_device *dev, void *data,  static int i915_setparam(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;  	drm_i915_setparam_t *param = data;  	if (!dev_priv) { @@ -1234,7 +1202,7 @@ static int i915_mmio(struct drm_device *dev, void *data,  		     struct drm_file *file_priv)  {  	uint32_t buf[8]; -	drm_i915_private_t *dev_priv = dev->dev_private; +	struct drm_i915_private *dev_priv = dev->dev_private;  	drm_i915_mmio_entry_t *e;	   	drm_i915_mmio_t *mmio = data;  	void __iomem *base; @@ -1280,7 +1248,7 @@ static int i915_mmio(struct drm_device *dev, void *data,  static int i915_set_status_page(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;  	drm_i915_hws_addr_t *hws = data;  	if (!dev_priv) { @@ -1309,43 +1277,13 @@ static int i915_set_status_page(struct drm_device *dev, void *data,  	dev_priv->hw_status_page = dev_priv->hws_map.handle;  	memset(dev_priv->hw_status_page, 0, PAGE_SIZE); -	I915_WRITE(0x02080, dev_priv->status_gfx_addr); +	I915_WRITE(I915REG_HWS_PGA, dev_priv->status_gfx_addr);  	DRM_DEBUG("load hws 0x2080 with gfx mem 0x%x\n",  			dev_priv->status_gfx_addr);  	DRM_DEBUG("load hws at %p\n", dev_priv->hw_status_page);  	return 0;  } -int i915_driver_load(struct drm_device *dev, unsigned long flags) -{ -	/* i915 has 4 more counters */ -	dev->counters += 4; -	dev->types[6] = _DRM_STAT_IRQ; -	dev->types[7] = _DRM_STAT_PRIMARY; -	dev->types[8] = _DRM_STAT_SECONDARY; -	dev->types[9] = _DRM_STAT_DMA; - -	return 0; -} - -void i915_driver_lastclose(struct drm_device * dev) -{ -	if (dev->dev_private) { -		drm_i915_private_t *dev_priv = dev->dev_private; -		i915_do_cleanup_pageflip(dev); -		i915_mem_takedown(&(dev_priv->agp_heap)); -	} -	i915_dma_cleanup(dev); -} - -void i915_driver_preclose(struct drm_device * dev, struct drm_file *file_priv) -{ -	if (dev->dev_private) { -		drm_i915_private_t *dev_priv = dev->dev_private; -		i915_mem_release(dev, file_priv, dev_priv->agp_heap); -	} -} -  struct drm_ioctl_desc i915_ioctls[] = {  	DRM_IOCTL_DEF(DRM_I915_INIT, i915_dma_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),  	DRM_IOCTL_DEF(DRM_I915_FLUSH, i915_flush_ioctl, DRM_AUTH), @@ -1388,10 +1326,4 @@ int i915_driver_device_is_agp(struct drm_device * dev)  	return 1;  } -int i915_driver_firstopen(struct drm_device *dev) -{ -#ifdef I915_HAVE_BUFFER -	drm_bo_driver_init(dev); -#endif -	return 0; -} + diff --git a/shared-core/i915_drm.h b/shared-core/i915_drm.h index a6c3cf30..e517fac0 100644 --- a/shared-core/i915_drm.h +++ b/shared-core/i915_drm.h @@ -39,7 +39,7 @@  				 * of chars for next/prev indices */  #define I915_LOG_MIN_TEX_REGION_SIZE 14 -typedef struct _drm_i915_init { +typedef struct drm_i915_init {  	enum {  		I915_INIT_DMA = 0x01,  		I915_CLEANUP_DMA = 0x02, @@ -63,7 +63,7 @@ typedef struct _drm_i915_init {  	unsigned int chipset;  } drm_i915_init_t; -typedef struct _drm_i915_sarea { +typedef struct drm_i915_sarea {  	struct drm_tex_region texList[I915_NR_TEX_REGIONS + 1];  	int last_upload;	/* last time texture was uploaded */  	int last_enqueue;	/* last time a buffer was enqueued */ @@ -195,7 +195,7 @@ typedef struct drm_i915_flip {  /* Allow drivers to submit batchbuffers directly to hardware, relying   * on the security mechanisms provided by hardware.   */ -typedef struct _drm_i915_batchbuffer { +typedef struct drm_i915_batchbuffer {  	int start;		/* agp offset */  	int used;		/* nr bytes in use */  	int DR1;		/* hw flags for GFX_OP_DRAWRECT_INFO */ @@ -207,7 +207,7 @@ typedef struct _drm_i915_batchbuffer {  /* As above, but pass a pointer to userspace buffer which can be   * validated by the kernel prior to sending to hardware.   */ -typedef struct _drm_i915_cmdbuffer { +typedef struct drm_i915_cmdbuffer {  	char __user *buf;	/* pointer to userspace command buffer */  	int sz;			/* nr bytes in buf */  	int DR1;		/* hw flags for GFX_OP_DRAWRECT_INFO */ @@ -358,7 +358,7 @@ struct drm_i915_op_arg {  struct drm_i915_execbuffer {  	uint64_t ops_list;  	uint32_t num_buffers; -	struct _drm_i915_batchbuffer batch; +	struct drm_i915_batchbuffer batch;  	struct drm_fence_arg fence_arg;  }; diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index 817288b6..be3212d1 100644 --- a/shared-core/i915_drv.h +++ b/shared-core/i915_drv.h @@ -70,7 +70,7 @@  #define I915_MAX_VALIDATE_BUFFERS 4096  #endif -typedef struct _drm_i915_ring_buffer { +struct drm_i915_ring_buffer {  	int tail_mask;  	unsigned long Start;  	unsigned long End; @@ -80,7 +80,7 @@ typedef struct _drm_i915_ring_buffer {  	int tail;  	int space;  	drm_local_map_t map; -} drm_i915_ring_buffer_t; +};  struct mem_block {  	struct mem_block *next; @@ -90,22 +90,26 @@ struct mem_block {  	struct drm_file *file_priv; /* NULL: free, -1: heap, other: real files */  }; -typedef struct _drm_i915_vbl_swap { +struct drm_i915_vbl_swap {  	struct list_head head;  	drm_drawable_t drw_id;  	unsigned int plane;  	unsigned int sequence;  	int flip; -} drm_i915_vbl_swap_t; +}; -typedef struct drm_i915_private { +struct drm_i915_private { +	struct drm_buffer_object *ring_buffer;  	drm_local_map_t *sarea;  	drm_local_map_t *mmio_map; -	drm_i915_sarea_t *sarea_priv; -	drm_i915_ring_buffer_t ring; +	unsigned long mmiobase; +	unsigned long mmiolen; + +	struct drm_i915_sarea *sarea_priv; +	struct drm_i915_ring_buffer ring; -	drm_dma_handle_t *status_page_dmah; +	struct drm_dma_handle *status_page_dmah;  	void *hw_status_page;  	dma_addr_t dma_status_page;  	uint32_t counter; @@ -143,10 +147,70 @@ typedef struct drm_i915_private {  #endif  	DRM_SPINTYPE swaps_lock; -	drm_i915_vbl_swap_t vbl_swaps; +	struct drm_i915_vbl_swap vbl_swaps;  	unsigned int swaps_pending; -} drm_i915_private_t; +	/* LVDS info */ +	int backlight_duty_cycle;  /* restore backlight to this value */ +	bool panel_wants_dither; +	struct drm_display_mode *panel_fixed_mode; + +	/* Register state */ +	u32 saveDSPACNTR; +	u32 saveDSPBCNTR; +	u32 savePIPEACONF; +	u32 savePIPEBCONF; +	u32 savePIPEASRC; +	u32 savePIPEBSRC; +	u32 saveFPA0; +	u32 saveFPA1; +	u32 saveDPLL_A; +	u32 saveDPLL_A_MD; +	u32 saveHTOTAL_A; +	u32 saveHBLANK_A; +	u32 saveHSYNC_A; +	u32 saveVTOTAL_A; +	u32 saveVBLANK_A; +	u32 saveVSYNC_A; +	u32 saveDSPASTRIDE; +	u32 saveDSPASIZE; +	u32 saveDSPAPOS; +	u32 saveDSPABASE; +	u32 saveDSPASURF; +	u32 saveFPB0; +	u32 saveFPB1; +	u32 saveDPLL_B; +	u32 saveDPLL_B_MD; +	u32 saveHTOTAL_B; +	u32 saveHBLANK_B; +	u32 saveHSYNC_B; +	u32 saveVTOTAL_B; +	u32 saveVBLANK_B; +	u32 saveVSYNC_B; +	u32 saveDSPBSTRIDE; +	u32 saveDSPBSIZE; +	u32 saveDSPBPOS; +	u32 saveDSPBBASE; +	u32 saveDSPBSURF; +	u32 saveVCLK_DIVISOR_VGA0; +	u32 saveVCLK_DIVISOR_VGA1; +	u32 saveVCLK_POST_DIV; +	u32 saveVGACNTRL; +	u32 saveADPA; +	u32 saveLVDS; +	u32 saveDVOA; +	u32 saveDVOB; +	u32 saveDVOC; +	u32 savePP_ON; +	u32 savePP_OFF; +	u32 savePP_CONTROL; +	u32 savePP_CYCLE; +	u32 savePFIT_CONTROL; +	u32 savePaletteA[256]; +	u32 savePaletteB[256]; +	u32 saveSWF[17]; +	u32 saveBLC_PWM_CTL; +};  enum intel_chip_family {  	CHIP_I8XX = 0x01, @@ -161,6 +225,7 @@ extern int i915_max_ioctl;  				/* i915_dma.c */  extern void i915_kernel_lost_context(struct drm_device * dev);  extern int i915_driver_load(struct drm_device *, unsigned long flags); +extern int i915_driver_unload(struct drm_device *dev);  extern void i915_driver_lastclose(struct drm_device * dev);  extern void i915_driver_preclose(struct drm_device *dev,  				 struct drm_file *file_priv); @@ -171,6 +236,8 @@ extern void i915_emit_breadcrumb(struct drm_device *dev);  extern void i915_dispatch_flip(struct drm_device * dev, int pipes, int sync);  extern int i915_emit_mi_flush(struct drm_device *dev, uint32_t flush);  extern int i915_driver_firstopen(struct drm_device *dev); +extern int i915_do_cleanup_pageflip(struct drm_device *dev); +extern int i915_dma_cleanup(struct drm_device *dev);  /* i915_irq.c */  extern int i915_irq_emit(struct drm_device *dev, void *data, @@ -178,6 +245,7 @@ extern int i915_irq_emit(struct drm_device *dev, void *data,  extern int i915_irq_wait(struct drm_device *dev, void *data,  			 struct drm_file *file_priv); +extern void i915_driver_wait_next_vblank(struct drm_device *dev, int pipe);  extern int i915_driver_vblank_wait(struct drm_device *dev, unsigned int *sequence);  extern int i915_driver_vblank_wait2(struct drm_device *dev, unsigned int *sequence);  extern irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS); @@ -189,8 +257,9 @@ extern int i915_vblank_pipe_set(struct drm_device *dev, void *data,  extern int i915_vblank_pipe_get(struct drm_device *dev, void *data,  				struct drm_file *file_priv);  extern int i915_emit_irq(struct drm_device * dev); -extern void i915_user_irq_on(drm_i915_private_t *dev_priv); -extern void i915_user_irq_off(drm_i915_private_t *dev_priv); +extern void i915_user_irq_on(struct drm_i915_private *dev_priv); +extern void i915_user_irq_off(struct drm_i915_private *dev_priv); +extern void i915_enable_interrupt (struct drm_device *dev);  extern int i915_vblank_swap(struct drm_device *dev, void *data,  			    struct drm_file *file_priv); @@ -234,6 +303,12 @@ extern int i915_move(struct drm_buffer_object *bo, int evict,  #endif + +/* modesetting */ +extern void intel_modeset_init(struct drm_device *dev); +extern void intel_modeset_cleanup(struct drm_device *dev); + +  #define I915_READ(reg)          DRM_READ32(dev_priv->mmio_map, (reg))  #define I915_WRITE(reg,val)     DRM_WRITE32(dev_priv->mmio_map, (reg), (val))  #define I915_READ16(reg) 	DRM_READ16(dev_priv->mmio_map, (reg)) @@ -271,8 +346,31 @@ extern int i915_move(struct drm_buffer_object *bo, int evict,  	I915_WRITE(LP_RING + RING_TAIL, outring);			\  } while(0) +#define MI_NOOP	(0x00 << 23) +  extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); +/* + * The Bridge device's PCI config space has information about the + * fb aperture size and the amount of pre-reserved memory. + */ +#define INTEL_GMCH_CTRL		0x52 +#define INTEL_GMCH_ENABLED	0x4 +#define INTEL_GMCH_MEM_MASK	0x1 +#define INTEL_GMCH_MEM_64M	0x1 +#define INTEL_GMCH_MEM_128M	0 + +#define INTEL_855_GMCH_GMS_MASK		(0x7 << 4) +#define INTEL_855_GMCH_GMS_DISABLED	(0x0 << 4) +#define INTEL_855_GMCH_GMS_STOLEN_1M	(0x1 << 4) +#define INTEL_855_GMCH_GMS_STOLEN_4M	(0x2 << 4) +#define INTEL_855_GMCH_GMS_STOLEN_8M	(0x3 << 4) +#define INTEL_855_GMCH_GMS_STOLEN_16M	(0x4 << 4) +#define INTEL_855_GMCH_GMS_STOLEN_32M	(0x5 << 4) + +#define INTEL_915G_GMCH_GMS_STOLEN_48M	(0x6 << 4) +#define INTEL_915G_GMCH_GMS_STOLEN_64M	(0x7 << 4) +  #define GFX_OP_USER_INTERRUPT 		((0<<29)|(2<<23))  #define GFX_OP_BREAKPOINT_INTERRUPT	((0<<29)|(1<<23))  #define CMD_REPORT_HEAD			(7<<23) @@ -295,6 +393,8 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);  #define BB1_UNPROTECTED       (0<<0)  #define BB2_END_ADDR_MASK     (~0x7) +#define I915REG_HWS_PGA		0x02080 +  /* Interrupt bits:   */  #define USER_INT_FLAG    (1<<1) @@ -314,6 +414,63 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);  #define I915_VBLANK_INTERRUPT_ENABLE	(1UL<<17)  #define I915_VBLANK_CLEAR		(1UL<<1) +#define GPIOA			0x5010 +#define GPIOB			0x5014 +#define GPIOC			0x5018 +#define GPIOD			0x501c +#define GPIOE			0x5020 +#define GPIOF			0x5024 +#define GPIOG			0x5028 +#define GPIOH			0x502c +# define GPIO_CLOCK_DIR_MASK		(1 << 0) +# define GPIO_CLOCK_DIR_IN		(0 << 1) +# define GPIO_CLOCK_DIR_OUT		(1 << 1) +# define GPIO_CLOCK_VAL_MASK		(1 << 2) +# define GPIO_CLOCK_VAL_OUT		(1 << 3) +# define GPIO_CLOCK_VAL_IN		(1 << 4) +# define GPIO_CLOCK_PULLUP_DISABLE	(1 << 5) +# define GPIO_DATA_DIR_MASK		(1 << 8) +# define GPIO_DATA_DIR_IN		(0 << 9) +# define GPIO_DATA_DIR_OUT		(1 << 9) +# define GPIO_DATA_VAL_MASK		(1 << 10) +# define GPIO_DATA_VAL_OUT		(1 << 11) +# define GPIO_DATA_VAL_IN		(1 << 12) +# define GPIO_DATA_PULLUP_DISABLE	(1 << 13) + +/* p317, 319 + */ +#define VCLK2_VCO_M        0x6008 /* treat as 16 bit? (includes msbs) */ +#define VCLK2_VCO_N        0x600a +#define VCLK2_VCO_DIV_SEL  0x6012 + +#define VCLK_DIVISOR_VGA0   0x6000 +#define VCLK_DIVISOR_VGA1   0x6004 +#define VCLK_POST_DIV	    0x6010 +/** Selects a post divisor of 4 instead of 2. */ +# define VGA1_PD_P2_DIV_4	(1 << 15) +/** Overrides the p2 post divisor field */ +# define VGA1_PD_P1_DIV_2	(1 << 13) +# define VGA1_PD_P1_SHIFT	8 +/** P1 value is 2 greater than this field */ +# define VGA1_PD_P1_MASK	(0x1f << 8) +/** Selects a post divisor of 4 instead of 2. */ +# define VGA0_PD_P2_DIV_4	(1 << 7) +/** Overrides the p2 post divisor field */ +# define VGA0_PD_P1_DIV_2	(1 << 5) +# define VGA0_PD_P1_SHIFT	0 +/** P1 value is 2 greater than this field */ +# define VGA0_PD_P1_MASK	(0x1f << 0) + +#define POST_DIV_SELECT        0x70 +#define POST_DIV_1             0x00 +#define POST_DIV_2             0x10 +#define POST_DIV_4             0x20 +#define POST_DIV_8             0x30 +#define POST_DIV_16            0x40 +#define POST_DIV_32            0x50 +#define VCO_LOOP_DIV_BY_4M     0x00 +#define VCO_LOOP_DIV_BY_16M    0x04 +  #define SRX_INDEX		0x3c4  #define SRX_DATA		0x3c5  #define SR01			1 @@ -322,6 +479,8 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);  #define PPCR			0x61204  #define PPCR_ON			(1<<0) +#define DVOA			0x61120 +#define DVOA_ON			(1<<31)  #define DVOB			0x61140  #define DVOB_ON			(1<<31)  #define DVOC			0x61160 @@ -478,8 +637,14 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);  #define SRC_COPY_BLT_CMD                ((2<<29)|(0x43<<22)|4)  #define XY_SRC_COPY_BLT_CMD		((2<<29)|(0x53<<22)|6) +#define XY_MONO_SRC_COPY_IMM_BLT	((2<<29)|(0x71<<22)|5)  #define XY_SRC_COPY_BLT_WRITE_ALPHA	(1<<21)  #define XY_SRC_COPY_BLT_WRITE_RGB	(1<<20) +#define   BLT_DEPTH_8			(0<<24) +#define   BLT_DEPTH_16_565		(1<<24) +#define   BLT_DEPTH_16_1555		(2<<24) +#define   BLT_DEPTH_32			(3<<24) +#define   BLT_ROP_GXCOPY		(0xcc<<16)  #define MI_BATCH_BUFFER 	((0x30<<23)|1)  #define MI_BATCH_BUFFER_START 	(0x31<<23) @@ -516,4 +681,506 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);  #define READ_BREADCRUMB(dev_priv)  (((volatile u32*)(dev_priv->hw_status_page))[5])  #define READ_HWSP(dev_priv, reg)  (((volatile u32*)(dev_priv->hw_status_page))[reg]) + +#define BLC_PWM_CTL		0x61254 +#define BACKLIGHT_MODULATION_FREQ_SHIFT		(17) +/** + * This is the most significant 15 bits of the number of backlight cycles in a + * complete cycle of the modulated backlight control. + * + * The actual value is this field multiplied by two. + */ +#define BACKLIGHT_MODULATION_FREQ_MASK		(0x7fff << 17) +#define BLM_LEGACY_MODE				(1 << 16) +/** + * This is the number of cycles out of the backlight modulation cycle for which + * the backlight is on. + * + * This field must be no greater than the number of cycles in the complete + * backlight modulation cycle. + */ +#define BACKLIGHT_DUTY_CYCLE_SHIFT		(0) +#define BACKLIGHT_DUTY_CYCLE_MASK		(0xffff) + +#define I915_GCFGC			0xf0 +#define I915_LOW_FREQUENCY_ENABLE		(1 << 7) +#define I915_DISPLAY_CLOCK_190_200_MHZ		(0 << 4) +#define I915_DISPLAY_CLOCK_333_MHZ		(4 << 4) +#define I915_DISPLAY_CLOCK_MASK			(7 << 4) + +#define I855_HPLLCC			0xc0 +#define I855_CLOCK_CONTROL_MASK			(3 << 0) +#define I855_CLOCK_133_200			(0 << 0) +#define I855_CLOCK_100_200			(1 << 0) +#define I855_CLOCK_100_133			(2 << 0) +#define I855_CLOCK_166_250			(3 << 0) + +/* I830 CRTC registers */ +#define HTOTAL_A	0x60000 +#define HBLANK_A	0x60004 +#define HSYNC_A 	0x60008 +#define VTOTAL_A	0x6000c +#define VBLANK_A	0x60010 +#define VSYNC_A 	0x60014 +#define PIPEASRC	0x6001c +#define BCLRPAT_A	0x60020 +#define VSYNCSHIFT_A	0x60028 + +#define HTOTAL_B	0x61000 +#define HBLANK_B	0x61004 +#define HSYNC_B 	0x61008 +#define VTOTAL_B	0x6100c +#define VBLANK_B	0x61010 +#define VSYNC_B 	0x61014 +#define PIPEBSRC	0x6101c +#define BCLRPAT_B	0x61020 +#define VSYNCSHIFT_B	0x61028 + +#define PP_STATUS	0x61200 +# define PP_ON					(1 << 31) +/** + * Indicates that all dependencies of the panel are on: + * + * - PLL enabled + * - pipe enabled + * - LVDS/DVOB/DVOC on + */ +# define PP_READY				(1 << 30) +# define PP_SEQUENCE_NONE			(0 << 28) +# define PP_SEQUENCE_ON				(1 << 28) +# define PP_SEQUENCE_OFF			(2 << 28) +# define PP_SEQUENCE_MASK			0x30000000 +#define PP_CONTROL	0x61204 +# define POWER_TARGET_ON			(1 << 0) + +#define LVDSPP_ON       0x61208 +#define LVDSPP_OFF      0x6120c +#define PP_CYCLE        0x61210 + +#define PFIT_CONTROL	0x61230 +# define PFIT_ENABLE				(1 << 31) +# define PFIT_PIPE_MASK				(3 << 29) +# define PFIT_PIPE_SHIFT			29 +# define VERT_INTERP_DISABLE			(0 << 10) +# define VERT_INTERP_BILINEAR			(1 << 10) +# define VERT_INTERP_MASK			(3 << 10) +# define VERT_AUTO_SCALE			(1 << 9) +# define HORIZ_INTERP_DISABLE			(0 << 6) +# define HORIZ_INTERP_BILINEAR			(1 << 6) +# define HORIZ_INTERP_MASK			(3 << 6) +# define HORIZ_AUTO_SCALE			(1 << 5) +# define PANEL_8TO6_DITHER_ENABLE		(1 << 3) + +#define PFIT_PGM_RATIOS	0x61234 +# define PFIT_VERT_SCALE_MASK			0xfff00000 +# define PFIT_HORIZ_SCALE_MASK			0x0000fff0 + +#define PFIT_AUTO_RATIOS	0x61238 + + +#define DPLL_A		0x06014 +#define DPLL_B		0x06018 +# define DPLL_VCO_ENABLE			(1 << 31) +# define DPLL_DVO_HIGH_SPEED			(1 << 30) +# define DPLL_SYNCLOCK_ENABLE			(1 << 29) +# define DPLL_VGA_MODE_DIS			(1 << 28) +# define DPLLB_MODE_DAC_SERIAL			(1 << 26) /* i915 */ +# define DPLLB_MODE_LVDS			(2 << 26) /* i915 */ +# define DPLL_MODE_MASK				(3 << 26) +# define DPLL_DAC_SERIAL_P2_CLOCK_DIV_10	(0 << 24) /* i915 */ +# define DPLL_DAC_SERIAL_P2_CLOCK_DIV_5		(1 << 24) /* i915 */ +# define DPLLB_LVDS_P2_CLOCK_DIV_14		(0 << 24) /* i915 */ +# define DPLLB_LVDS_P2_CLOCK_DIV_7		(1 << 24) /* i915 */ +# define DPLL_P2_CLOCK_DIV_MASK			0x03000000 /* i915 */ +# define DPLL_FPA01_P1_POST_DIV_MASK		0x00ff0000 /* i915 */ +/** + *  The i830 generation, in DAC/serial mode, defines p1 as two plus this + * bitfield, or just 2 if PLL_P1_DIVIDE_BY_TWO is set. + */ +# define DPLL_FPA01_P1_POST_DIV_MASK_I830	0x001f0000 +/** + * The i830 generation, in LVDS mode, defines P1 as the bit number set within + * this field (only one bit may be set). + */ +# define DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS	0x003f0000 +# define DPLL_FPA01_P1_POST_DIV_SHIFT		16 +# define PLL_P2_DIVIDE_BY_4			(1 << 23) /* i830, required in DVO non-gang */ +# define PLL_P1_DIVIDE_BY_TWO			(1 << 21) /* i830 */ +# define PLL_REF_INPUT_DREFCLK			(0 << 13) +# define PLL_REF_INPUT_TVCLKINA			(1 << 13) /* i830 */ +# define PLL_REF_INPUT_TVCLKINBC		(2 << 13) /* SDVO TVCLKIN */ +# define PLLB_REF_INPUT_SPREADSPECTRUMIN	(3 << 13) +# define PLL_REF_INPUT_MASK			(3 << 13) +# define PLL_LOAD_PULSE_PHASE_SHIFT		9 +/* + * Parallel to Serial Load Pulse phase selection. + * Selects the phase for the 10X DPLL clock for the PCIe + * digital display port. The range is 4 to 13; 10 or more + * is just a flip delay. The default is 6 + */ +# define PLL_LOAD_PULSE_PHASE_MASK		(0xf << PLL_LOAD_PULSE_PHASE_SHIFT) +# define DISPLAY_RATE_SELECT_FPA1		(1 << 8) + +/** + * SDVO multiplier for 945G/GM. Not used on 965. + * + * \sa DPLL_MD_UDI_MULTIPLIER_MASK + */ +# define SDVO_MULTIPLIER_MASK			0x000000ff +# define SDVO_MULTIPLIER_SHIFT_HIRES		4 +# define SDVO_MULTIPLIER_SHIFT_VGA		0 + +/** @defgroup DPLL_MD + * @{ + */ +/** Pipe A SDVO/UDI clock multiplier/divider register for G965. */ +#define DPLL_A_MD		0x0601c +/** Pipe B SDVO/UDI clock multiplier/divider register for G965. */ +#define DPLL_B_MD		0x06020 +/** + * UDI pixel divider, controlling how many pixels are stuffed into a packet. + * + * Value is pixels minus 1.  Must be set to 1 pixel for SDVO. + */ +# define DPLL_MD_UDI_DIVIDER_MASK		0x3f000000 +# define DPLL_MD_UDI_DIVIDER_SHIFT		24 +/** UDI pixel divider for VGA, same as DPLL_MD_UDI_DIVIDER_MASK. */ +# define DPLL_MD_VGA_UDI_DIVIDER_MASK		0x003f0000 +# define DPLL_MD_VGA_UDI_DIVIDER_SHIFT		16 +/** + * SDVO/UDI pixel multiplier. + * + * SDVO requires that the bus clock rate be between 1 and 2 Ghz, and the bus + * clock rate is 10 times the DPLL clock.  At low resolution/refresh rate + * modes, the bus rate would be below the limits, so SDVO allows for stuffing + * dummy bytes in the datastream at an increased clock rate, with both sides of + * the link knowing how many bytes are fill. + * + * So, for a mode with a dotclock of 65Mhz, we would want to double the clock + * rate to 130Mhz to get a bus rate of 1.30Ghz.  The DPLL clock rate would be + * set to 130Mhz, and the SDVO multiplier set to 2x in this register and + * through an SDVO command. + * + * This register field has values of multiplication factor minus 1, with + * a maximum multiplier of 5 for SDVO. + */ +# define DPLL_MD_UDI_MULTIPLIER_MASK		0x00003f00 +# define DPLL_MD_UDI_MULTIPLIER_SHIFT		8 +/** SDVO/UDI pixel multiplier for VGA, same as DPLL_MD_UDI_MULTIPLIER_MASK.  + * This best be set to the default value (3) or the CRT won't work. No, + * I don't entirely understand what this does... + */ +# define DPLL_MD_VGA_UDI_MULTIPLIER_MASK	0x0000003f +# define DPLL_MD_VGA_UDI_MULTIPLIER_SHIFT	0 +/** @} */ + +#define DPLL_TEST		0x606c +# define DPLLB_TEST_SDVO_DIV_1			(0 << 22) +# define DPLLB_TEST_SDVO_DIV_2			(1 << 22) +# define DPLLB_TEST_SDVO_DIV_4			(2 << 22) +# define DPLLB_TEST_SDVO_DIV_MASK		(3 << 22) +# define DPLLB_TEST_N_BYPASS			(1 << 19) +# define DPLLB_TEST_M_BYPASS			(1 << 18) +# define DPLLB_INPUT_BUFFER_ENABLE		(1 << 16) +# define DPLLA_TEST_N_BYPASS			(1 << 3) +# define DPLLA_TEST_M_BYPASS			(1 << 2) +# define DPLLA_INPUT_BUFFER_ENABLE		(1 << 0) + +#define ADPA			0x61100 +#define ADPA_DAC_ENABLE 	(1<<31) +#define ADPA_DAC_DISABLE	0 +#define ADPA_PIPE_SELECT_MASK	(1<<30) +#define ADPA_PIPE_A_SELECT	0 +#define ADPA_PIPE_B_SELECT	(1<<30) +#define ADPA_USE_VGA_HVPOLARITY (1<<15) +#define ADPA_SETS_HVPOLARITY	0 +#define ADPA_VSYNC_CNTL_DISABLE (1<<11) +#define ADPA_VSYNC_CNTL_ENABLE	0 +#define ADPA_HSYNC_CNTL_DISABLE (1<<10) +#define ADPA_HSYNC_CNTL_ENABLE	0 +#define ADPA_VSYNC_ACTIVE_HIGH	(1<<4) +#define ADPA_VSYNC_ACTIVE_LOW	0 +#define ADPA_HSYNC_ACTIVE_HIGH	(1<<3) +#define ADPA_HSYNC_ACTIVE_LOW	0 + +#define FPA0		0x06040 +#define FPA1		0x06044 +#define FPB0		0x06048 +#define FPB1		0x0604c +# define FP_N_DIV_MASK				0x003f0000 +# define FP_N_DIV_SHIFT				16 +# define FP_M1_DIV_MASK				0x00003f00 +# define FP_M1_DIV_SHIFT			8 +# define FP_M2_DIV_MASK				0x0000003f +# define FP_M2_DIV_SHIFT			0 + + +#define PORT_HOTPLUG_EN		0x61110 +# define SDVOB_HOTPLUG_INT_EN			(1 << 26) +# define SDVOC_HOTPLUG_INT_EN			(1 << 25) +# define TV_HOTPLUG_INT_EN			(1 << 18) +# define CRT_HOTPLUG_INT_EN			(1 << 9) +# define CRT_HOTPLUG_FORCE_DETECT		(1 << 3) + +#define PORT_HOTPLUG_STAT	0x61114 +# define CRT_HOTPLUG_INT_STATUS			(1 << 11) +# define TV_HOTPLUG_INT_STATUS			(1 << 10) +# define CRT_HOTPLUG_MONITOR_MASK		(3 << 8) +# define CRT_HOTPLUG_MONITOR_COLOR		(3 << 8) +# define CRT_HOTPLUG_MONITOR_MONO		(2 << 8) +# define CRT_HOTPLUG_MONITOR_NONE		(0 << 8) +# define SDVOC_HOTPLUG_INT_STATUS		(1 << 7) +# define SDVOB_HOTPLUG_INT_STATUS		(1 << 6) + +#define SDVOB			0x61140 +#define SDVOC			0x61160 +#define SDVO_ENABLE				(1 << 31) +#define SDVO_PIPE_B_SELECT			(1 << 30) +#define SDVO_STALL_SELECT			(1 << 29) +#define SDVO_INTERRUPT_ENABLE			(1 << 26) +/** + * 915G/GM SDVO pixel multiplier. + * + * Programmed value is multiplier - 1, up to 5x. + * + * \sa DPLL_MD_UDI_MULTIPLIER_MASK + */ +#define SDVO_PORT_MULTIPLY_MASK			(7 << 23) +#define SDVO_PORT_MULTIPLY_SHIFT		23 +#define SDVO_PHASE_SELECT_MASK			(15 << 19) +#define SDVO_PHASE_SELECT_DEFAULT		(6 << 19) +#define SDVO_CLOCK_OUTPUT_INVERT		(1 << 18) +#define SDVOC_GANG_MODE				(1 << 16) +#define SDVO_BORDER_ENABLE			(1 << 7) +#define SDVOB_PCIE_CONCURRENCY			(1 << 3) +#define SDVO_DETECTED				(1 << 2) +/* Bits to be preserved when writing */ +#define SDVOB_PRESERVE_MASK			((1 << 17) | (1 << 16) | (1 << 14)) +#define SDVOC_PRESERVE_MASK			(1 << 17) + +/** @defgroup LVDS + * @{ + */ +/** + * This register controls the LVDS output enable, pipe selection, and data + * format selection. + * + * All of the clock/data pairs are force powered down by power sequencing. + */ +#define LVDS			0x61180 +/** + * Enables the LVDS port.  This bit must be set before DPLLs are enabled, as + * the DPLL semantics change when the LVDS is assigned to that pipe. + */ +# define LVDS_PORT_EN			(1 << 31) +/** Selects pipe B for LVDS data.  Must be set on pre-965. */ +# define LVDS_PIPEB_SELECT		(1 << 30) + +/** + * Enables the A0-A2 data pairs and CLKA, containing 18 bits of color data per + * pixel. + */ +# define LVDS_A0A2_CLKA_POWER_MASK	(3 << 8) +# define LVDS_A0A2_CLKA_POWER_DOWN	(0 << 8) +# define LVDS_A0A2_CLKA_POWER_UP	(3 << 8) +/** + * Controls the A3 data pair, which contains the additional LSBs for 24 bit + * mode.  Only enabled if LVDS_A0A2_CLKA_POWER_UP also indicates it should be + * on. + */ +# define LVDS_A3_POWER_MASK		(3 << 6) +# define LVDS_A3_POWER_DOWN		(0 << 6) +# define LVDS_A3_POWER_UP		(3 << 6) +/** + * Controls the CLKB pair.  This should only be set when LVDS_B0B3_POWER_UP + * is set. + */ +# define LVDS_CLKB_POWER_MASK		(3 << 4) +# define LVDS_CLKB_POWER_DOWN		(0 << 4) +# define LVDS_CLKB_POWER_UP		(3 << 4) + +/** + * Controls the B0-B3 data pairs.  This must be set to match the DPLL p2 + * setting for whether we are in dual-channel mode.  The B3 pair will + * additionally only be powered up when LVDS_A3_POWER_UP is set. + */ +# define LVDS_B0B3_POWER_MASK		(3 << 2) +# define LVDS_B0B3_POWER_DOWN		(0 << 2) +# define LVDS_B0B3_POWER_UP		(3 << 2) + +#define PIPEACONF 0x70008 +#define PIPEACONF_ENABLE	(1<<31) +#define PIPEACONF_DISABLE	0 +#define PIPEACONF_DOUBLE_WIDE	(1<<30) +#define I965_PIPECONF_ACTIVE	(1<<30) +#define PIPEACONF_SINGLE_WIDE	0 +#define PIPEACONF_PIPE_UNLOCKED 0 +#define PIPEACONF_PIPE_LOCKED	(1<<25) +#define PIPEACONF_PALETTE	0 +#define PIPEACONF_GAMMA 	(1<<24) +#define PIPECONF_FORCE_BORDER	(1<<25) +#define PIPECONF_PROGRESSIVE	(0 << 21) +#define PIPECONF_INTERLACE_W_FIELD_INDICATION	(6 << 21) +#define PIPECONF_INTERLACE_FIELD_0_ONLY		(7 << 21) + +#define PIPEBCONF 0x71008 +#define PIPEBCONF_ENABLE	(1<<31) +#define PIPEBCONF_DISABLE	0 +#define PIPEBCONF_DOUBLE_WIDE	(1<<30) +#define PIPEBCONF_DISABLE	0 +#define PIPEBCONF_GAMMA 	(1<<24) +#define PIPEBCONF_PALETTE	0 + +#define PIPEBGCMAXRED		0x71010 +#define PIPEBGCMAXGREEN		0x71014 +#define PIPEBGCMAXBLUE		0x71018 +#define PIPEBSTAT		0x71024 +#define PIPEBFRAMEHIGH		0x71040 +#define PIPEBFRAMEPIXEL		0x71044 + +#define DSPACNTR		0x70180 +#define DSPBCNTR		0x71180 +#define DISPLAY_PLANE_ENABLE 			(1<<31) +#define DISPLAY_PLANE_DISABLE			0 +#define DISPPLANE_GAMMA_ENABLE			(1<<30) +#define DISPPLANE_GAMMA_DISABLE			0 +#define DISPPLANE_PIXFORMAT_MASK		(0xf<<26) +#define DISPPLANE_8BPP				(0x2<<26) +#define DISPPLANE_15_16BPP			(0x4<<26) +#define DISPPLANE_16BPP				(0x5<<26) +#define DISPPLANE_32BPP_NO_ALPHA 		(0x6<<26) +#define DISPPLANE_32BPP				(0x7<<26) +#define DISPPLANE_STEREO_ENABLE			(1<<25) +#define DISPPLANE_STEREO_DISABLE		0 +#define DISPPLANE_SEL_PIPE_MASK			(1<<24) +#define DISPPLANE_SEL_PIPE_A			0 +#define DISPPLANE_SEL_PIPE_B			(1<<24) +#define DISPPLANE_SRC_KEY_ENABLE		(1<<22) +#define DISPPLANE_SRC_KEY_DISABLE		0 +#define DISPPLANE_LINE_DOUBLE			(1<<20) +#define DISPPLANE_NO_LINE_DOUBLE		0 +#define DISPPLANE_STEREO_POLARITY_FIRST		0 +#define DISPPLANE_STEREO_POLARITY_SECOND	(1<<18) +/* plane B only */ +#define DISPPLANE_ALPHA_TRANS_ENABLE		(1<<15) +#define DISPPLANE_ALPHA_TRANS_DISABLE		0 +#define DISPPLANE_SPRITE_ABOVE_DISPLAYA		0 +#define DISPPLANE_SPRITE_ABOVE_OVERLAY		(1) + +#define DSPABASE		0x70184 +#define DSPASTRIDE		0x70188 + +#define DSPBBASE		0x71184 +#define DSPBADDR		DSPBBASE +#define DSPBSTRIDE		0x71188 + +#define DSPAKEYVAL		0x70194 +#define DSPAKEYMASK		0x70198 + +#define DSPAPOS			0x7018C /* reserved */ +#define DSPASIZE		0x70190 +#define DSPBPOS			0x7118C +#define DSPBSIZE		0x71190 + +#define DSPASURF		0x7019C +#define DSPATILEOFF		0x701A4 + +#define DSPBSURF		0x7119C +#define DSPBTILEOFF		0x711A4 + +#define VGACNTRL		0x71400 +# define VGA_DISP_DISABLE			(1 << 31) +# define VGA_2X_MODE				(1 << 30) +# define VGA_PIPE_B_SELECT			(1 << 29) + +/* + * Some BIOS scratch area registers.  The 845 (and 830?) store the amount + * of video memory available to the BIOS in SWF1. + */ + +#define SWF0			0x71410 +#define SWF1			0x71414 +#define SWF2			0x71418 +#define SWF3			0x7141c +#define SWF4			0x71420 +#define SWF5			0x71424 +#define SWF6			0x71428 + +/* + * 855 scratch registers. + */ +#define SWF00			0x70410 +#define SWF01			0x70414 +#define SWF02			0x70418 +#define SWF03			0x7041c +#define SWF04			0x70420 +#define SWF05			0x70424 +#define SWF06			0x70428 + +#define SWF10			SWF0 +#define SWF11			SWF1 +#define SWF12			SWF2 +#define SWF13			SWF3 +#define SWF14			SWF4 +#define SWF15			SWF5 +#define SWF16			SWF6 + +#define SWF30			0x72414 +#define SWF31			0x72418 +#define SWF32			0x7241c + +/* + * Overlay registers.  These are overlay registers accessed via MMIO. + * Those loaded via the overlay register page are defined in i830_video.c. + */ +#define OVADD			0x30000 + +#define DOVSTA			0x30008 +#define OC_BUF			(0x3<<20) + +#define OGAMC5			0x30010 +#define OGAMC4			0x30014 +#define OGAMC3			0x30018 +#define OGAMC2			0x3001c +#define OGAMC1			0x30020 +#define OGAMC0			0x30024 + +/* + * Palette registers + */ +#define PALETTE_A		0x0a000 +#define PALETTE_B		0x0a800 + +#define IS_I830(dev) ((dev)->pci_device == PCI_DEVICE_ID_INTEL_82830_CGC) +#define IS_845G(dev) ((dev)->pci_device == PCI_DEVICE_ID_INTEL_82845G_IG) +#define IS_I85X(dev) ((dev)->pci_device == PCI_DEVICE_ID_INTEL_82855GM_IG) +#define IS_I855(dev) ((dev)->pci_device == PCI_DEVICE_ID_INTEL_82855GM_IG) +#define IS_I865G(dev) ((dev)->pci_device == PCI_DEVICE_ID_INTEL_82865_IG) + +#define IS_I915G(dev) (dev->pci_device == PCI_DEVICE_ID_INTEL_82915G_IG)/* || dev->pci_device == PCI_DEVICE_ID_INTELPCI_CHIP_E7221_G)*/ +#define IS_I915GM(dev) ((dev)->pci_device == PCI_DEVICE_ID_INTEL_82915GM_IG) +#define IS_I945G(dev) ((dev)->pci_device == PCI_DEVICE_ID_INTEL_82945G_IG) +#define IS_I945GM(dev) ((dev)->pci_device == PCI_DEVICE_ID_INTEL_82945GM_IG) + +#define IS_I965G(dev) ((dev)->pci_device == 0x2972 || \ +		       (dev)->pci_device == 0x2982 || \ +		       (dev)->pci_device == 0x2992 || \ +		       (dev)->pci_device == 0x29A2 || \ +		       (dev)->pci_device == 0x2A02 || \ +		       (dev)->pci_device == 0x2A12) + +#define IS_I965GM(dev) (((dev)->pci_device == 0x2A02)) + +#define IS_I9XX(dev) (IS_I915G(dev) || IS_I915GM(dev) || IS_I945G(dev) || \ +		      IS_I945GM(dev) || IS_I965G(dev)) + +#define IS_G33(dev)    ((dev)->pci_device == 0x29C2 || \ +		   	(dev)->pci_device == 0x29B2 || \ +			(dev)->pci_device == 0x29D2)  + +#define IS_MOBILE(dev) (IS_I830(dev) || IS_I85X(dev) || IS_I915GM(dev) || \ +			IS_I945GM(dev) || IS_I965GM(dev)) + +#define PRIMARY_RINGBUFFER_SIZE         (128*1024) +  #endif diff --git a/shared-core/i915_init.c b/shared-core/i915_init.c new file mode 100644 index 00000000..3b43c722 --- /dev/null +++ b/shared-core/i915_init.c @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2007 Intel Corporation + *   Jesse Barnes <jesse.barnes@intel.com> + * + * Copyright © 2002, 2003 David Dawes <dawes@xfree86.org> + *                   2004 Sylvain Meyer + * + * GPL/BSD dual license + */ +#include "drmP.h" +#include "drm.h" +#include "drm_sarea.h" +#include "i915_drm.h" +#include "i915_drv.h" + +/** + * i915_probe_agp - get AGP bootup configuration + * @pdev: PCI device + * @aperture_size: returns AGP aperture configured size + * @preallocated_size: returns size of BIOS preallocated AGP space + * + * Since Intel integrated graphics are UMA, the BIOS has to set aside + * some RAM for the framebuffer at early boot.  This code figures out + * how much was set aside so we can use it for our own purposes. + */ +int i915_probe_agp(struct pci_dev *pdev, unsigned long *aperture_size, +		   unsigned long *preallocated_size) +{ +	struct pci_dev *bridge_dev; +	u16 tmp = 0; +	unsigned long overhead; + +	bridge_dev = pci_get_bus_and_slot(0, PCI_DEVFN(0,0)); +	if (!bridge_dev) { +		DRM_ERROR("bridge device not found\n"); +		return -1; +	} + +	/* Get the fb aperture size and "stolen" memory amount. */ +	pci_read_config_word(bridge_dev, INTEL_GMCH_CTRL, &tmp); +	pci_dev_put(bridge_dev); + +	*aperture_size = 1024 * 1024; +	*preallocated_size = 1024 * 1024; + +	switch (pdev->device) { +	case PCI_DEVICE_ID_INTEL_82830_CGC: +	case PCI_DEVICE_ID_INTEL_82845G_IG: +	case PCI_DEVICE_ID_INTEL_82855GM_IG: +	case PCI_DEVICE_ID_INTEL_82865_IG: +		if ((tmp & INTEL_GMCH_MEM_MASK) == INTEL_GMCH_MEM_64M) +			*aperture_size *= 64; +		else +			*aperture_size *= 128; +		break; +	default: +		/* 9xx supports large sizes, just look at the length */ +		*aperture_size = pci_resource_len(pdev, 2); +		break; +	} + +	/* +	 * Some of the preallocated space is taken by the GTT +	 * and popup.  GTT is 1K per MB of aperture size, and popup is 4K. +	 */ +	overhead = (*aperture_size / 1024) + 4096; +	switch (tmp & INTEL_855_GMCH_GMS_MASK) { +	case INTEL_855_GMCH_GMS_STOLEN_1M: +		break; /* 1M already */ +	case INTEL_855_GMCH_GMS_STOLEN_4M: +		*preallocated_size *= 4; +		break; +	case INTEL_855_GMCH_GMS_STOLEN_8M: +		*preallocated_size *= 8; +		break; +	case INTEL_855_GMCH_GMS_STOLEN_16M: +		*preallocated_size *= 16; +		break; +	case INTEL_855_GMCH_GMS_STOLEN_32M: +		*preallocated_size *= 32; +		break; +	case INTEL_915G_GMCH_GMS_STOLEN_48M: +		*preallocated_size *= 48; +		break; +	case INTEL_915G_GMCH_GMS_STOLEN_64M: +		*preallocated_size *= 64; +		break; +	case INTEL_855_GMCH_GMS_DISABLED: +		DRM_ERROR("video memory is disabled\n"); +		return -1; +	default: +		DRM_ERROR("unexpected GMCH_GMS value: 0x%02x\n", +			tmp & INTEL_855_GMCH_GMS_MASK); +		return -1; +	} +	*preallocated_size -= overhead; + +	return 0; +} + +/** + * i915_driver_load - setup chip and create an initial config + * @dev: DRM device + * @flags: startup flags + * + * The driver load routine has to do several things: + *   - drive output discovery via intel_modeset_init() + *   - initialize the memory manager + *   - allocate initial config memory + *   - setup the DRM framebuffer with the allocated memory + */ +int i915_driver_load(struct drm_device *dev, unsigned long flags) +{ +	struct drm_i915_private *dev_priv; +	unsigned long agp_size, prealloc_size; +	unsigned long sareapage; +	int size, ret; + +	dev_priv = drm_alloc(sizeof(struct drm_i915_private), DRM_MEM_DRIVER); +	if (dev_priv == NULL) +		return -ENOMEM; + +	memset(dev_priv, 0, sizeof(struct drm_i915_private)); +	dev->dev_private = (void *)dev_priv; +//	dev_priv->flags = flags; + +	/* i915 has 4 more counters */ +	dev->counters += 4; +	dev->types[6] = _DRM_STAT_IRQ; +	dev->types[7] = _DRM_STAT_PRIMARY; +	dev->types[8] = _DRM_STAT_SECONDARY; +	dev->types[9] = _DRM_STAT_DMA; + +	if (IS_I9XX(dev)) { +		dev_priv->mmiobase = drm_get_resource_start(dev, 0); +		dev_priv->mmiolen = drm_get_resource_len(dev, 0); +		dev->mode_config.fb_base = +			drm_get_resource_start(dev, 2) & 0xff000000; +	} else if (drm_get_resource_start(dev, 1)) { +		dev_priv->mmiobase = drm_get_resource_start(dev, 1); +		dev_priv->mmiolen = drm_get_resource_len(dev, 1); +		dev->mode_config.fb_base = +			drm_get_resource_start(dev, 0) & 0xff000000; +	} else { +		DRM_ERROR("Unable to find MMIO registers\n"); +		return -ENODEV; +	} + +	DRM_DEBUG("fb_base: 0x%08lx\n", dev->mode_config.fb_base); + +	ret = drm_addmap(dev, dev_priv->mmiobase, dev_priv->mmiolen, +			 _DRM_REGISTERS, _DRM_READ_ONLY|_DRM_DRIVER, &dev_priv->mmio_map); +	if (ret != 0) { +		DRM_ERROR("Cannot add mapping for MMIO registers\n"); +		return ret; +	} + +	/* prebuild the SAREA */ +	sareapage = max(SAREA_MAX, PAGE_SIZE); +	ret = drm_addmap(dev, 0, sareapage, _DRM_SHM, _DRM_CONTAINS_LOCK|_DRM_DRIVER, +			 &dev_priv->sarea); +	if (ret) { +		DRM_ERROR("SAREA setup failed\n"); +		return ret; +	} + +	init_waitqueue_head(&dev->lock.lock_queue); + +	/* FIXME: assume sarea_priv is right after SAREA */ +        dev_priv->sarea_priv = dev_priv->sarea->handle + sizeof(struct drm_sarea); + +	/* +	 * Initialize the memory manager for local and AGP space +	 */ +	drm_bo_driver_init(dev); + +	i915_probe_agp(dev->pdev, &agp_size, &prealloc_size); +	printk("setting up %ld bytes of VRAM space\n", prealloc_size); +	printk("setting up %ld bytes of TT space\n", (agp_size - prealloc_size)); +	drm_bo_init_mm(dev, DRM_BO_MEM_VRAM, 0, prealloc_size >> PAGE_SHIFT); +	drm_bo_init_mm(dev, DRM_BO_MEM_TT, prealloc_size >> PAGE_SHIFT, (agp_size - prealloc_size) >> PAGE_SHIFT); + +	I915_WRITE(LP_RING + RING_LEN, 0); +	I915_WRITE(LP_RING + RING_HEAD, 0); +	I915_WRITE(LP_RING + RING_TAIL, 0); + +	size = PRIMARY_RINGBUFFER_SIZE; +	ret = drm_buffer_object_create(dev, size, drm_bo_type_kernel, +				       DRM_BO_FLAG_READ | DRM_BO_FLAG_WRITE | +				       DRM_BO_FLAG_MEM_VRAM | +				       DRM_BO_FLAG_NO_EVICT | +				       DRM_BO_HINT_DONT_FENCE, 0, 0x1, 0, +				       &dev_priv->ring_buffer); +	if (ret < 0) { +		DRM_ERROR("Unable to allocate or pin ring buffer\n"); +		return -EINVAL; +	} + +	/* remap the buffer object properly */ +	dev_priv->ring.Start = dev_priv->ring_buffer->offset; +	dev_priv->ring.End = dev_priv->ring.Start + size; +	dev_priv->ring.Size = size; +	dev_priv->ring.tail_mask = dev_priv->ring.Size - 1; + +	/* FIXME: need wrapper with PCI mem checks */ +	ret = drm_mem_reg_ioremap(dev, &dev_priv->ring_buffer->mem, +				  (void **) &dev_priv->ring.virtual_start); +	if (ret) +		DRM_ERROR("error mapping ring buffer: %d\n", ret); + +	DRM_DEBUG("ring start %08lX, %p, %08lX\n", dev_priv->ring.Start, +		  dev_priv->ring.virtual_start, dev_priv->ring.Size); + +	dev_priv->sarea_priv->pf_current_page = 0; + +	memset((void *)(dev_priv->ring.virtual_start), 0, dev_priv->ring.Size); + +	I915_WRITE(LP_RING + RING_START, dev_priv->ring.Start); +	I915_WRITE(LP_RING + RING_LEN, +		   ((dev_priv->ring.Size - 4096) & RING_NR_PAGES) | +		   (RING_NO_REPORT | RING_VALID)); + +	/* We are using separate values as placeholders for mechanisms for +	 * private backbuffer/depthbuffer usage. +	 */ +	dev_priv->use_mi_batchbuffer_start = 0; + +	/* Allow hardware batchbuffers unless told otherwise. +	 */ +	dev_priv->allow_batchbuffer = 1; + +	/* Program Hardware Status Page */ +	if (!IS_G33(dev)) { +		dev_priv->status_page_dmah =  +			drm_pci_alloc(dev, PAGE_SIZE, PAGE_SIZE, 0xffffffff); + +		if (!dev_priv->status_page_dmah) { +			dev->dev_private = (void *)dev_priv; +			i915_dma_cleanup(dev); +			DRM_ERROR("Can not allocate hardware status page\n"); +			return -ENOMEM; +		} +		dev_priv->hw_status_page = dev_priv->status_page_dmah->vaddr; +		dev_priv->dma_status_page = dev_priv->status_page_dmah->busaddr; + +		memset(dev_priv->hw_status_page, 0, PAGE_SIZE); + +		I915_WRITE(I915REG_HWS_PGA, dev_priv->dma_status_page); +	} +	DRM_DEBUG("Enabled hardware status page\n"); + +	intel_modeset_init(dev); +	drm_initial_config(dev, false); + +	return 0; +} + +int i915_driver_unload(struct drm_device *dev) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; + +	if (dev_priv->ring.virtual_start) { +		drm_core_ioremapfree(&dev_priv->ring.map, dev); +	} + +	if (dev_priv->status_page_dmah) { +		drm_pci_free(dev, dev_priv->status_page_dmah); +		dev_priv->status_page_dmah = NULL; +		dev_priv->hw_status_page = NULL; +		dev_priv->dma_status_page = 0; +		/* Need to rewrite hardware status page */ +		I915_WRITE(I915REG_HWS_PGA, 0x1ffff000); +	} + +	if (dev_priv->status_gfx_addr) { +		dev_priv->status_gfx_addr = 0; +		drm_core_ioremapfree(&dev_priv->hws_map, dev); +		I915_WRITE(I915REG_HWS_PGA, 0x1ffff000); +	} + +	I915_WRITE(LP_RING + RING_LEN, 0); + +	intel_modeset_cleanup(dev); + +	drm_mem_reg_iounmap(dev, &dev_priv->ring_buffer->mem, +			    dev_priv->ring.virtual_start); + +	DRM_DEBUG("usage is %d\n", atomic_read(&dev_priv->ring_buffer->usage)); +	mutex_lock(&dev->struct_mutex); +	drm_bo_usage_deref_locked(&dev_priv->ring_buffer); + +	if (drm_bo_clean_mm(dev, DRM_BO_MEM_TT)) { +		DRM_ERROR("Memory manager type 3 not clean. " +			  "Delaying takedown\n"); +	} +	if (drm_bo_clean_mm(dev, DRM_BO_MEM_VRAM)) { +		DRM_ERROR("Memory manager type 3 not clean. " +			  "Delaying takedown\n"); +	} +	mutex_unlock(&dev->struct_mutex); + +	drm_bo_driver_finish(dev); + +        DRM_DEBUG("%p, %p\n", dev_priv->mmio_map, dev_priv->sarea); +        drm_rmmap(dev, dev_priv->mmio_map); +        drm_rmmap(dev, dev_priv->sarea); + +	drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER); + +	dev->dev_private = NULL; +	return 0; +} + +void i915_driver_lastclose(struct drm_device *dev) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; + +	i915_do_cleanup_pageflip(dev); +	//i915_mem_takedown(&(dev_priv->agp_heap)); +	i915_dma_cleanup(dev); +} + +void i915_driver_preclose(struct drm_device *dev, struct drm_file *filp) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; + +	//i915_mem_release(dev, filp, dev_priv->agp_heap); +} + diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index db18a895..11cb20ab 100644 --- a/shared-core/i915_irq.c +++ b/shared-core/i915_irq.c @@ -49,7 +49,7 @@  static int  i915_get_pipe(struct drm_device *dev, int plane)  { -	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;  	u32 dspcntr;  	dspcntr = plane ? I915_READ(DSPBCNTR) : I915_READ(DSPACNTR); @@ -66,8 +66,8 @@ static void  i915_dispatch_vsync_flip(struct drm_device *dev, struct drm_drawable_info *drw,  			 int plane)  { -	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; -	drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv; +	struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; +	struct drm_i915_sarea *sarea_priv = dev_priv->sarea_priv;  	u16 x1, y1, x2, y2;  	int pf_planes = 1 << plane; @@ -111,13 +111,13 @@ i915_dispatch_vsync_flip(struct drm_device *dev, struct drm_drawable_info *drw,   */  static void i915_vblank_tasklet(struct drm_device *dev)  { -	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 list_head *list, *tmp, hits, *hit;  	int nhits, nrects, slice[2], upper[2], lower[2], i, num_pages;  	unsigned counter[2] = { atomic_read(&dev->vbl_received),  				atomic_read(&dev->vbl_received2) };  	struct drm_drawable_info *drw; -	drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv; +	struct drm_i915_sarea *sarea_priv = dev_priv->sarea_priv;  	u32 cpp = dev_priv->cpp,  offsets[3];  	u32 cmd = (cpp == 4) ? (XY_SRC_COPY_BLT_CMD |  				XY_SRC_COPY_BLT_WRITE_ALPHA | @@ -142,8 +142,8 @@ static void i915_vblank_tasklet(struct drm_device *dev)  	/* Find buffer swaps scheduled for this vertical blank */  	list_for_each_safe(list, tmp, &dev_priv->vbl_swaps.head) { -		drm_i915_vbl_swap_t *vbl_swap = -			list_entry(list, drm_i915_vbl_swap_t, head); +		struct drm_i915_vbl_swap *vbl_swap = +			list_entry(list, struct drm_i915_vbl_swap, head);  		int pipe = i915_get_pipe(dev, vbl_swap->plane);  		if ((counter[pipe] - vbl_swap->sequence) > (1<<23)) @@ -165,8 +165,8 @@ static void i915_vblank_tasklet(struct drm_device *dev)  		}  		list_for_each(hit, &hits) { -			drm_i915_vbl_swap_t *swap_cmp = -				list_entry(hit, drm_i915_vbl_swap_t, head); +			struct drm_i915_vbl_swap *swap_cmp = +				list_entry(hit, struct drm_i915_vbl_swap, head);  			struct drm_drawable_info *drw_cmp =  				drm_get_drawable_info(dev, swap_cmp->drw_id); @@ -223,8 +223,8 @@ static void i915_vblank_tasklet(struct drm_device *dev)  			lower[0] = lower[1] = sarea_priv->height;  		list_for_each(hit, &hits) { -			drm_i915_vbl_swap_t *swap_hit = -				list_entry(hit, drm_i915_vbl_swap_t, head); +			struct drm_i915_vbl_swap *swap_hit = +				list_entry(hit, struct drm_i915_vbl_swap, head);  			struct drm_clip_rect *rect;  			int num_rects, plane, front, back;  			unsigned short top, bottom; @@ -292,8 +292,8 @@ static void i915_vblank_tasklet(struct drm_device *dev)  	DRM_SPINUNLOCK(&dev->drw_lock);  	list_for_each_safe(hit, tmp, &hits) { -		drm_i915_vbl_swap_t *swap_hit = -			list_entry(hit, drm_i915_vbl_swap_t, head); +		struct drm_i915_vbl_swap *swap_hit = +			list_entry(hit, struct drm_i915_vbl_swap, head);  		list_del(hit); @@ -304,7 +304,7 @@ static void i915_vblank_tasklet(struct drm_device *dev)  irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)  {  	struct drm_device *dev = (struct drm_device *) arg; -	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;  	u16 temp;  	u32 pipea_stats, pipeb_stats; @@ -368,7 +368,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)  int i915_emit_irq(struct drm_device * dev)  { -	drm_i915_private_t *dev_priv = dev->dev_private; +	struct drm_i915_private *dev_priv = dev->dev_private;  	RING_LOCALS;  	i915_kernel_lost_context(dev); @@ -387,7 +387,7 @@ int i915_emit_irq(struct drm_device * dev)  } -void i915_user_irq_on(drm_i915_private_t *dev_priv) +void i915_user_irq_on(struct drm_i915_private *dev_priv)  {  	DRM_SPINLOCK(&dev_priv->user_irq_lock);  	if (dev_priv->irq_enabled && (++dev_priv->user_irq_refcount == 1)){ @@ -398,7 +398,7 @@ void i915_user_irq_on(drm_i915_private_t *dev_priv)  } -void i915_user_irq_off(drm_i915_private_t *dev_priv) +void i915_user_irq_off(struct drm_i915_private *dev_priv)  {  	DRM_SPINLOCK(&dev_priv->user_irq_lock);  	if (dev_priv->irq_enabled && (--dev_priv->user_irq_refcount == 0)) { @@ -411,7 +411,7 @@ void i915_user_irq_off(drm_i915_private_t *dev_priv)  static int i915_wait_irq(struct drm_device * dev, int irq_nr)  { -	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;  	int ret = 0;  	DRM_DEBUG("%s irq_nr=%d breadcrumb=%d\n", __FUNCTION__, irq_nr, @@ -441,7 +441,7 @@ static int i915_driver_vblank_do_wait(struct drm_device *dev,  				      unsigned int *sequence,  				      atomic_t *counter)  { -	drm_i915_private_t *dev_priv = dev->dev_private; +	struct drm_i915_private *dev_priv = dev->dev_private;  	unsigned int cur_vblank;  	int ret = 0; @@ -459,6 +459,19 @@ static int i915_driver_vblank_do_wait(struct drm_device *dev,  	return ret;  } +void i915_driver_wait_next_vblank(struct drm_device *dev, int pipe) +{ +	unsigned int seq; + +	seq = pipe ? atomic_read(&dev->vbl_received2) + 1 : +		atomic_read(&dev->vbl_received) + 1; + +	if (!pipe) +		i915_driver_vblank_do_wait(dev, &seq, &dev->vbl_received); +	else +		i915_driver_vblank_do_wait(dev, &seq, &dev->vbl_received2); +} +  int i915_driver_vblank_wait(struct drm_device *dev, unsigned int *sequence)  {  	return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received); @@ -473,8 +486,8 @@ int i915_driver_vblank_wait2(struct drm_device *dev, unsigned int *sequence)   */  int i915_irq_emit(struct drm_device *dev, void *data, struct drm_file *file_priv)  { -	drm_i915_private_t *dev_priv = dev->dev_private; -	drm_i915_irq_emit_t *emit = data; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_i915_irq_emit *emit = data;  	int result;  	LOCK_TEST_WITH_RETURN(dev, file_priv); @@ -499,8 +512,8 @@ int i915_irq_emit(struct drm_device *dev, void *data, struct drm_file *file_priv  int i915_irq_wait(struct drm_device *dev, void *data,  		  struct drm_file *file_priv)  { -	drm_i915_private_t *dev_priv = dev->dev_private; -	drm_i915_irq_wait_t *irqwait = data; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_i915_irq_wait *irqwait = data;  	if (!dev_priv) {  		DRM_ERROR("%s called with no initialization\n", __FUNCTION__); @@ -510,9 +523,9 @@ int i915_irq_wait(struct drm_device *dev, void *data,  	return i915_wait_irq(dev, irqwait->irq_seq);  } -static void i915_enable_interrupt (struct drm_device *dev) +void i915_enable_interrupt (struct drm_device *dev)  { -	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;  	dev_priv->irq_enable_reg = USER_INT_FLAG;   	if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A) @@ -529,8 +542,8 @@ static void i915_enable_interrupt (struct drm_device *dev)  int i915_vblank_pipe_set(struct drm_device *dev, void *data,  			 struct drm_file *file_priv)  { -	drm_i915_private_t *dev_priv = dev->dev_private; -	drm_i915_vblank_pipe_t *pipe = data; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_i915_vblank_pipe *pipe = data;  	if (!dev_priv) {  		DRM_ERROR("%s called with no initialization\n", __FUNCTION__); @@ -553,8 +566,8 @@ int i915_vblank_pipe_set(struct drm_device *dev, void *data,  int i915_vblank_pipe_get(struct drm_device *dev, void *data,  			 struct drm_file *file_priv)  { -	drm_i915_private_t *dev_priv = dev->dev_private; -	drm_i915_vblank_pipe_t *pipe = data; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_i915_vblank_pipe *pipe = data;  	u16 flag;  	if (!dev_priv) { @@ -578,9 +591,9 @@ int i915_vblank_pipe_get(struct drm_device *dev, void *data,  int i915_vblank_swap(struct drm_device *dev, void *data,  		     struct drm_file *file_priv)  { -	drm_i915_private_t *dev_priv = dev->dev_private; -	drm_i915_vblank_swap_t *swap = data; -	drm_i915_vbl_swap_t *vbl_swap; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_i915_vblank_swap *swap = data; +	struct drm_i915_vbl_swap *vbl_swap;  	unsigned int pipe, seqtype, curseq, plane;  	unsigned long irqflags;  	struct list_head *list; @@ -672,7 +685,7 @@ int i915_vblank_swap(struct drm_device *dev, void *data,  	DRM_SPINLOCK_IRQSAVE(&dev_priv->swaps_lock, irqflags);  	list_for_each(list, &dev_priv->vbl_swaps.head) { -		vbl_swap = list_entry(list, drm_i915_vbl_swap_t, head); +		vbl_swap = list_entry(list, struct drm_i915_vbl_swap, head);  		if (vbl_swap->drw_id == swap->drawable &&  		    vbl_swap->plane == plane && @@ -722,7 +735,7 @@ int i915_vblank_swap(struct drm_device *dev, void *data,  */  void i915_driver_irq_preinstall(struct drm_device * dev)  { -	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;  	I915_WRITE16(I915REG_HWSTAM, 0xeffe);  	I915_WRITE16(I915REG_INT_MASK_R, 0x0); @@ -731,7 +744,7 @@ void i915_driver_irq_preinstall(struct drm_device * dev)  void i915_driver_irq_postinstall(struct drm_device * dev)  { -	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;  	DRM_SPININIT(&dev_priv->swaps_lock, "swap");  	INIT_LIST_HEAD(&dev_priv->vbl_swaps.head); @@ -752,7 +765,7 @@ void i915_driver_irq_postinstall(struct drm_device * dev)  void i915_driver_irq_uninstall(struct drm_device * dev)  { -	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; +	struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;  	u16 temp;  	if (!dev_priv)  		return; diff --git a/shared-core/i915_mem.c b/shared-core/i915_mem.c index 5bf29a1e..2916d5a1 100644 --- a/shared-core/i915_mem.c +++ b/shared-core/i915_mem.c @@ -45,8 +45,8 @@   */  static void mark_block(struct drm_device * dev, struct mem_block *p, int in_use)  { -	drm_i915_private_t *dev_priv = dev->dev_private; -	drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_i915_sarea *sarea_priv = dev_priv->sarea_priv;  	struct drm_tex_region *list;  	unsigned shift, nr;  	unsigned start; @@ -256,7 +256,7 @@ void i915_mem_takedown(struct mem_block **heap)  	*heap = NULL;  } -static struct mem_block **get_heap(drm_i915_private_t * dev_priv, int region) +static struct mem_block **get_heap(struct drm_i915_private * dev_priv, int region)  {  	switch (region) {  	case I915_MEM_REGION_AGP: @@ -271,8 +271,8 @@ static struct mem_block **get_heap(drm_i915_private_t * dev_priv, int region)  int i915_mem_alloc(struct drm_device *dev, void *data,  		   struct drm_file *file_priv)  { -	drm_i915_private_t *dev_priv = dev->dev_private; -	drm_i915_mem_alloc_t *alloc = data; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_i915_mem_alloc *alloc = data;  	struct mem_block *block, **heap;  	if (!dev_priv) { @@ -309,8 +309,8 @@ int i915_mem_alloc(struct drm_device *dev, void *data,  int i915_mem_free(struct drm_device *dev, void *data,  		  struct drm_file *file_priv)  { -	drm_i915_private_t *dev_priv = dev->dev_private; -	drm_i915_mem_free_t *memfree = data; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_i915_mem_free *memfree = data;  	struct mem_block *block, **heap;  	if (!dev_priv) { @@ -337,8 +337,8 @@ int i915_mem_free(struct drm_device *dev, void *data,  int i915_mem_init_heap(struct drm_device *dev, void *data,  		       struct drm_file *file_priv)  { -	drm_i915_private_t *dev_priv = dev->dev_private; -	drm_i915_mem_init_heap_t *initheap = data; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_i915_mem_init_heap *initheap = data;  	struct mem_block **heap;  	if (!dev_priv) { @@ -361,8 +361,8 @@ int i915_mem_init_heap(struct drm_device *dev, void *data,  int i915_mem_destroy_heap( struct drm_device *dev, void *data,  			   struct drm_file *file_priv )  { -	drm_i915_private_t *dev_priv = dev->dev_private; -	drm_i915_mem_destroy_heap_t *destroyheap = data; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_i915_mem_destroy_heap *destroyheap = data;  	struct mem_block **heap;  	if ( !dev_priv ) { diff --git a/tests/mode/Makefile b/tests/mode/Makefile new file mode 100644 index 00000000..205c2ba1 --- /dev/null +++ b/tests/mode/Makefile @@ -0,0 +1,11 @@ + +all: modetest + +#CFLAGS = -g -ansi -pedantic -DPOSIX_C_SOURCE=199309L \ +#        -D_POSIX_SOURCE -D_XOPEN_SOURCE -D_BSD_SOURCE -D_SVID_SOURCE \ + +modetest: modetest.c +	@gcc -o modetest -Wall -I../../libdrm -I../../shared-core -L../../libdrm/.libs -ldrm modetest.c  + +clean: +	@rm -f modetest diff --git a/tests/mode/modetest.c b/tests/mode/modetest.c new file mode 100644 index 00000000..bf1a5169 --- /dev/null +++ b/tests/mode/modetest.c @@ -0,0 +1,337 @@ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> + +#include "xf86drm.h" +#include "xf86drmMode.h" + +const char* getConnectionText(drmModeConnection conn) +{ +	switch (conn) { +	case DRM_MODE_CONNECTED: +		return "connected"; +	case DRM_MODE_DISCONNECTED: +		return "disconnected"; +	default: +		return "unknown"; +	} + +} + +struct drm_mode_modeinfo* findMode(drmModeResPtr res, uint32_t id) +{ +	int i; +	for (i = 0; i < res->count_modes; i++) { +		if (res->modes[i].id == id) +			return &res->modes[i]; +	} + +	return 0; +} + +int printMode(struct drm_mode_modeinfo *mode) +{ +#if 0 +	printf("Mode: %s\n", mode->name); +	printf("\tclock       : %i\n", mode->clock); +	printf("\thdisplay    : %i\n", mode->hdisplay); +	printf("\thsync_start : %i\n", mode->hsync_start); +	printf("\thsync_end   : %i\n", mode->hsync_end); +	printf("\thtotal      : %i\n", mode->htotal); +	printf("\thskew       : %i\n", mode->hskew); +	printf("\tvdisplay    : %i\n", mode->vdisplay); +	printf("\tvsync_start : %i\n", mode->vsync_start); +	printf("\tvsync_end   : %i\n", mode->vsync_end); +	printf("\tvtotal      : %i\n", mode->vtotal); +	printf("\tvscan       : %i\n", mode->vscan); +	printf("\tvrefresh    : %i\n", mode->vrefresh); +	printf("\tflags       : %i\n", mode->flags); +#else +	printf("Mode: %i \"%s\" %ix%i %.0f\n", mode->id, mode->name, +		mode->hdisplay, mode->vdisplay, mode->vrefresh / 1000.0); +#endif +	return 0; +} + +int printOutput(int fd, drmModeResPtr res, drmModeOutputPtr output, uint32_t id) +{ +	int i = 0; +	struct drm_mode_modeinfo *mode = NULL; + +	printf("Output: %s\n", output->name); +	printf("\tid           : %i\n", id); +	printf("\tcrtc id      : %i\n", output->crtc); +	printf("\tconn         : %s\n", getConnectionText(output->connection)); +	printf("\tsize         : %ix%i (mm)\n", output->mmWidth, output->mmHeight); +	printf("\tcount_crtcs  : %i\n", output->count_crtcs); +	printf("\tcrtcs        : %i\n", output->crtcs); +	printf("\tcount_clones : %i\n", output->count_clones); +	printf("\tclones       : %i\n", output->clones); +	printf("\tcount_modes  : %i\n", output->count_modes); + +	for (i = 0; i < output->count_modes; i++) { +		mode = findMode(res, output->modes[i]); + +		if (mode) +			printf("\t\tmode: %i \"%s\" %ix%i %.0f\n", mode->id, mode->name, +				mode->hdisplay, mode->vdisplay, mode->vrefresh / 1000.0); +		else +			printf("\t\tmode: Invalid mode %i\n", output->modes[i]); +	} + +	return 0; +} + +int printCrtc(int fd, drmModeResPtr res, drmModeCrtcPtr crtc, uint32_t id) +{ +	printf("Crtc\n"); +	printf("\tid           : %i\n", id); +	printf("\tx            : %i\n", crtc->x); +	printf("\ty            : %i\n", crtc->y); +	printf("\twidth        : %i\n", crtc->width); +	printf("\theight       : %i\n", crtc->height); +	printf("\tmode         : %i\n", crtc->mode); +	printf("\tnum outputs  : %i\n", crtc->count_outputs); +	printf("\toutputs      : %i\n", crtc->outputs); +	printf("\tnum possible : %i\n", crtc->count_possibles); +	printf("\tpossibles    : %i\n", crtc->possibles); + +	return 0; +} + +int printFrameBuffer(int fd, drmModeResPtr res, drmModeFBPtr fb) +{ +	printf("Framebuffer\n"); +	printf("\thandle    : %i\n", fb->handle); +	printf("\twidth     : %i\n", fb->width); +	printf("\theight    : %i\n", fb->height); +	printf("\tpitch     : %i\n", fb->pitch);; +	printf("\tbpp       : %i\n", fb->bpp); +	printf("\tdepth     : %i\n", fb->depth); +	printf("\tbuffer_id : %i\n", fb->buffer_id); + +	return 0; +} + +int printRes(int fd, drmModeResPtr res) +{ +	int i; +	drmModeOutputPtr output; +	drmModeCrtcPtr crtc; +	drmModeFBPtr fb; + +	for (i = 0; i < res->count_modes; i++) { +		printMode(&res->modes[i]); +	} + +	for (i = 0; i < res->count_outputs; i++) { +		output = drmModeGetOutput(fd, res->outputs[i]); + +		if (!output) +			printf("Could not get output %i\n", i); +		else { +			printOutput(fd, res, output, res->outputs[i]); +			drmModeFreeOutput(output); +		} +	} + +	for (i = 0; i < res->count_crtcs; i++) { +		crtc = drmModeGetCrtc(fd, res->crtcs[i]); + +		if (!crtc) +			printf("Could not get crtc %i\n", i); +		else { +			printCrtc(fd, res, crtc, res->crtcs[i]); +			drmModeFreeCrtc(crtc); +		} +	} + +	for (i = 0; i < res->count_fbs; i++) { +		fb = drmModeGetFB(fd, res->fbs[i]); + +		if (!fb) +			printf("Could not get fb %i\n", res->fbs[i]); +		else { +			printFrameBuffer(fd, res, fb); +			drmModeFreeFB(fb); +		} +	} + +	return 0; +} + +static struct drm_mode_modeinfo mode = { +	.name = "Test mode", +	.clock = 25200, +	.hdisplay = 640, +	.hsync_start = 656, +	.hsync_end = 752, +	.htotal = 800, +	.hskew = 0, +	.vdisplay = 480, +	.vsync_start = 490, +	.vsync_end = 492, +	.vtotal = 525, +	.vscan = 0, +	.vrefresh = 60000, /* vertical refresh * 1000 */ +	.flags = 10, +}; + +int testMode(int fd, drmModeResPtr res) +{ +	uint32_t output = res->outputs[0]; +	uint32_t newMode = 0; +	int ret = 0; +	int error = 0; + +	printf("Test: adding mode to output %i\n", output); + +	/* printMode(&mode); */ + +	printf("\tAdding mode\n"); +	newMode = drmModeAddMode(fd, &mode); +	if (!newMode) +		goto err; + +	printf("\tAttaching mode %i to output %i\n", newMode, output); + +	ret = drmModeAttachMode(fd, output, newMode); + +	if (ret) +		goto err_mode; + +	printf("\tDetaching mode %i from output %i\n", newMode, output); +	ret = drmModeDetachMode(fd, output, newMode); + +	if (ret) +		goto err_mode; + +	printf("\tRemoveing new mode %i\n", newMode); +	ret = drmModeRmMode(fd, newMode); +	if (ret) +		goto err; + +	return 0; + +err_mode: +	error = drmModeRmMode(fd, newMode); + +err: +	printf("\tFailed\n"); + +	if (error) +		printf("\tFailed to delete mode %i\n", newMode); + +	return 1; +} + +/* +int testFrameBufferGet(int fd, uint32_t fb) +{ +	drmModeFBPtr frame; + +	printf("Test: get framebuffer %i\n", fb); + +	frame = drmModeGetFB(fd, fb); + +	if (!frame) { +		printf("\tFailed\n"); +	} else { +		printFrameBuffer(fd, frame); +		drmModeFreeFB(frame); +	} + +	return 0; +} +*/ + +int testFrameBufferAdd(int fd, drmModeResPtr res) +{ +	uint32_t fb = 0; +	int ret = 0; +	drmModeFBPtr frame = 0; +	drmBO bo; + +	printf("Test: adding framebuffer\n"); + +	printf("\tCreating BO\n"); + +	/* TODO */ +	ret = 1; +	if (ret) +		goto err; + +	printf("\tAdding FB\n"); +	ret = drmModeAddFB(fd, 640, 480, 32, 8, 0, &bo, &fb); +	if (ret) +		goto err_bo; + +	frame = drmModeGetFB(fd, fb); + +	if (!frame) { +		printf("Couldn't retrive created framebuffer\n"); +	} else { +		printFrameBuffer(fd, res, frame); +		drmModeFreeFB(frame); +	} + +	printf("\tRemoveing FB\n"); + +	ret = drmModeRmFB(fd, fb); + +	if (ret) { +		printf("\tFailed this shouldn't happen!\n"); +		goto err_bo; +	} + +	printf("\tRemoveing BO\n"); + +	ret = drmBODestroy(fb, &bo); + +	return 0; +	 +err_bo: +	drmBODestroy(fd, &bo); + +err: +	printf("\tFailed\n"); + +	return 1; +} + + +int main(int argc, char **argv) +{ +	int fd; +	const char *driver = "i915"; /* hardcoded for now */ +	drmModeResPtr res; + +	printf("Starting test\n"); + +	fd = drmOpen(driver, NULL); + +	if (fd < 0) { +		printf("Failed to open the card fb\n"); +		return 1; +	} + +	res = drmModeGetResources(fd); +	if (res == 0) { +		printf("Failed to get resources from card\n"); +		drmClose(fd); +		return 1; +	} + +	printRes(fd, res); + +	testMode(fd, res); + +	testFrameBufferAdd(fd, res); + +	drmModeFreeResources(res); +	printf("Ok\n"); + +	return 0; +} diff --git a/tests/mode/test b/tests/mode/test new file mode 100755 index 00000000..fa155f4e --- /dev/null +++ b/tests/mode/test @@ -0,0 +1 @@ +LD_PRELOAD=../../libdrm/.libs/libdrm.so ./modetest | 
