diff options
92 files changed, 20129 insertions, 269 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..726c55ab --- /dev/null +++ b/libdrm/xf86drmMode.c @@ -0,0 +1,531 @@ +/* + * \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> + +#define U642VOID(x) ((void *)(unsigned long)(x)) +#define VOID2U64(x) ((uint64_t)(unsigned long)(x)) + +/* + * 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); + +} + +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_ptr = VOID2U64(drmMalloc(res.count_fbs*sizeof(uint32_t))); + if (res.count_crtcs) + res.crtc_id_ptr = VOID2U64(drmMalloc(res.count_crtcs*sizeof(uint32_t))); + if (res.count_outputs) + res.output_id_ptr = VOID2U64(drmMalloc(res.count_outputs*sizeof(uint32_t))); + + if (ioctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res)) { + r = NULL; + goto err_allocs; + } + + /* + * return + */ + + + if (!(r = drmMalloc(sizeof(*r)))) + return 0; + + r->min_width = res.min_width; + r->max_width = res.max_width; + r->min_height = res.min_height; + r->max_height = res.max_height; + r->count_fbs = res.count_fbs; + r->count_crtcs = res.count_crtcs; + r->count_outputs = res.count_outputs; + /* TODO we realy should test if these allocs fails. */ + r->fbs = drmAllocCpy(U642VOID(res.fb_id_ptr), res.count_fbs, sizeof(uint32_t)); + r->crtcs = drmAllocCpy(U642VOID(res.crtc_id_ptr), res.count_crtcs, sizeof(uint32_t)); + r->outputs = drmAllocCpy(U642VOID(res.output_id_ptr), res.count_outputs, sizeof(uint32_t)); + +err_allocs: + drmFree(U642VOID(res.fb_id_ptr)); + drmFree(U642VOID(res.crtc_id_ptr)); + drmFree(U642VOID(res.output_id_ptr)); + + 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->crtc_id = crtc.crtc_id; + r->x = crtc.x; + r->y = crtc.y; + r->mode_valid = crtc.mode_valid; + if (r->mode_valid) + memcpy(&r->mode, &crtc.mode, sizeof(struct drm_mode_modeinfo)); + 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 *outputs, int count, + struct drm_mode_modeinfo *mode) +{ + 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_ptr = VOID2U64(outputs); + crtc.count_outputs = count; + if (mode) { + memcpy(&crtc.mode, mode, sizeof(struct drm_mode_modeinfo)); + crtc.mode_valid = 1; + } else + crtc.mode_valid = 0; + + 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 = NULL; + + out.output = output_id; + out.count_crtcs = 0; + out.crtcs = 0; + out.count_clones = 0; + out.clones = 0; + out.count_modes = 0; + out.modes_ptr = 0; + out.count_props = 0; + out.props_ptr = 0; + out.prop_values_ptr = 0; + + if (ioctl(fd, DRM_IOCTL_MODE_GETOUTPUT, &out)) + return 0; + + if (out.count_props) { + out.props_ptr = VOID2U64(drmMalloc(out.count_props*sizeof(uint32_t))); + out.prop_values_ptr = VOID2U64(drmMalloc(out.count_props*sizeof(uint64_t))); + } + + if (out.count_modes) + out.modes_ptr = VOID2U64(drmMalloc(out.count_modes*sizeof(struct drm_mode_modeinfo))); + + if (ioctl(fd, DRM_IOCTL_MODE_GETOUTPUT, &out)) + goto err_allocs; + + if(!(r = drmMalloc(sizeof(*r)))) { + goto err_allocs; + } + + r->output_id = out.output; + r->crtc = out.crtc; + 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->count_props = out.count_props; + r->props = drmAllocCpy(U642VOID(out.props_ptr), out.count_props, sizeof(uint32_t)); + r->prop_values = drmAllocCpy(U642VOID(out.prop_values_ptr), out.count_props, sizeof(uint64_t)); + r->modes = drmAllocCpy(U642VOID(out.modes_ptr), out.count_modes, sizeof(struct drm_mode_modeinfo)); + strncpy(r->name, out.name, DRM_OUTPUT_NAME_LEN); + r->name[DRM_OUTPUT_NAME_LEN-1] = 0; + +err_allocs: + drmFree(U642VOID(out.prop_values_ptr)); + drmFree(U642VOID(out.props_ptr)); + drmFree(U642VOID(out.modes_ptr)); + + return r; +} + +int drmModeAttachMode(int fd, uint32_t output_id, struct drm_mode_modeinfo *mode_info) +{ + struct drm_mode_mode_cmd res; + + memcpy(&res.mode, mode_info, sizeof(struct drm_mode_modeinfo)); + res.output_id = output_id; + + return ioctl(fd, DRM_IOCTL_MODE_ATTACHMODE, &res); +} + +int drmModeDetachMode(int fd, uint32_t output_id, struct drm_mode_modeinfo *mode_info) +{ + struct drm_mode_mode_cmd res; + + memcpy(&res.mode, mode_info, sizeof(struct drm_mode_modeinfo)); + res.output_id = output_id; + + return ioctl(fd, DRM_IOCTL_MODE_DETACHMODE, &res); +} + + +drmModePropertyPtr drmModeGetProperty(int fd, uint32_t property_id) +{ + struct drm_mode_get_property prop; + drmModePropertyPtr r; + struct drm_mode_property_blob *blob_tmp; + int i; + prop.prop_id = property_id; + prop.count_enum_blobs = 0; + prop.count_values = 0; + prop.flags = 0; + prop.enum_blob_ptr = 0; + prop.values_ptr = 0; + + if (ioctl(fd, DRM_IOCTL_MODE_GETPROPERTY, &prop)) + return 0; + + if (prop.count_values) + prop.values_ptr = VOID2U64(drmMalloc(prop.count_values * sizeof(uint64_t))); + + if (prop.count_enum_blobs && (prop.flags & DRM_MODE_PROP_ENUM)) + prop.enum_blob_ptr = VOID2U64(drmMalloc(prop.count_enum_blobs * sizeof(struct drm_mode_property_enum))); + + if (prop.count_enum_blobs && (prop.flags & DRM_MODE_PROP_BLOB)) { + prop.values_ptr = VOID2U64(drmMalloc(prop.count_enum_blobs * sizeof(uint32_t))); + prop.enum_blob_ptr = VOID2U64(drmMalloc(prop.count_enum_blobs * sizeof(uint32_t))); + } + + if (ioctl(fd, DRM_IOCTL_MODE_GETPROPERTY, &prop)) { + r = NULL; + goto err_allocs; + } + + if (!(r = drmMalloc(sizeof(*r)))) + return NULL; + + r->prop_id = prop.prop_id; + r->count_values = prop.count_values; + + r->flags = prop.flags; + if (prop.count_values) + r->values = drmAllocCpy(U642VOID(prop.values_ptr), prop.count_values, sizeof(uint64_t)); + if (prop.flags & DRM_MODE_PROP_ENUM) { + r->count_enums = prop.count_enum_blobs; + r->enums = drmAllocCpy(U642VOID(prop.enum_blob_ptr), prop.count_enum_blobs, sizeof(struct drm_mode_property_enum)); + } else if (prop.flags & DRM_MODE_PROP_ENUM) { + r->values = drmAllocCpy(U642VOID(prop.values_ptr), prop.count_enum_blobs, sizeof(uint32_t)); + r->blob_ids = drmAllocCpy(U642VOID(prop.enum_blob_ptr), prop.count_enum_blobs, sizeof(uint32_t)); + r->count_blobs = prop.count_enum_blobs; + } + strncpy(r->name, prop.name, DRM_PROP_NAME_LEN); + r->name[DRM_PROP_NAME_LEN-1] = 0; + +err_allocs: + drmFree(U642VOID(prop.values_ptr)); + drmFree(U642VOID(prop.enum_blob_ptr)); + + return r; +} + +void drmModeFreeProperty(drmModePropertyPtr ptr) +{ + if (!ptr) + return; + + drmFree(ptr->values); + drmFree(ptr->enums); + drmFree(ptr); +} + +drmModePropertyBlobPtr drmModeGetPropertyBlob(int fd, uint32_t blob_id) +{ + struct drm_mode_get_blob blob; + drmModePropertyBlobPtr r; + + blob.length = 0; + blob.data = 0; + blob.blob_id = blob_id; + + if (ioctl(fd, DRM_IOCTL_MODE_GETPROPBLOB, &blob)) + return NULL; + + if (blob.length) + blob.data = VOID2U64(drmMalloc(blob.length)); + + if (ioctl(fd, DRM_IOCTL_MODE_GETPROPBLOB, &blob)) { + r = NULL; + goto err_allocs; + } + + if (!(r = drmMalloc(sizeof(*r)))) + return NULL; + + r->id = blob.blob_id; + r->length = blob.length; + r->data = drmAllocCpy(U642VOID(blob.data), 1, blob.length); + +err_allocs: + drmFree(U642VOID(blob.data)); + return r; +} + +void drmModeFreePropertyBlob(drmModePropertyBlobPtr ptr) +{ + if (!ptr) + return; + + drmFree(ptr->data); + drmFree(ptr); +} + +int drmModeOutputSetProperty(int fd, uint32_t output_id, uint32_t property_id, + uint64_t value) +{ + struct drm_mode_output_set_property osp; + int ret; + + osp.output_id = output_id; + osp.prop_id = property_id; + osp.value = value; + + if (ret = ioctl(fd, DRM_IOCTL_MODE_SETPROPERTY, &osp)) + return ret; + + return 0; +} diff --git a/libdrm/xf86drmMode.h b/libdrm/xf86drmMode.h new file mode 100644 index 00000000..05b61bc8 --- /dev/null +++ b/libdrm/xf86drmMode.h @@ -0,0 +1,227 @@ +/* + * \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; + + uint32_t min_width, max_width; + uint32_t min_height, max_height; +} drmModeRes, *drmModeResPtr; + +typedef struct drm_mode_fb_cmd drmModeFB, *drmModeFBPtr; + +typedef struct _drmModePropertyBlob { + uint32_t id; + uint32_t length; + void *data; +} drmModePropertyBlobRes, *drmModePropertyBlobPtr; + +typedef struct _drmModeProperty { + unsigned int prop_id; + unsigned int flags; + unsigned char name[DRM_PROP_NAME_LEN]; + int count_values; + uint64_t *values; // store the blob lengths + int count_enums; + struct drm_mode_property_enum *enums; + int count_blobs; + uint32_t *blob_ids; // store the blob IDs +} drmModePropertyRes, *drmModePropertyPtr; + +typedef struct _drmModeCrtc { + unsigned int crtc_id; + unsigned int buffer_id; /**< FB id to connect to 0 = disconnect*/ + + uint32_t x, y; /**< Position on the frameuffer */ + uint32_t width, height; + int mode_valid; + struct drm_mode_modeinfo mode; + + 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 output_id; + + 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; + struct drm_mode_modeinfo *modes; + + int count_props; + uint32_t *props; /**< List of property ids */ + uint64_t *prop_values; /**< List of property values */ + +} 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. + */ +int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId, + uint32_t x, uint32_t y, uint32_t *outputs, int count, + struct drm_mode_modeinfo *mode); + + +/* + * Output manipulation + */ + +/** + * Retrive information about the output outputId. + */ +extern drmModeOutputPtr drmModeGetOutput(int fd, + uint32_t outputId); + +/** + * Attaches the given mode to an output. + */ +extern int drmModeAttachMode(int fd, uint32_t outputId, struct drm_mode_modeinfo *mode_info); + +/** + * Detaches a mode from the output + * must be unused, by the given mode. + */ +extern int drmModeDetachMode(int fd, uint32_t outputId, struct drm_mode_modeinfo *mode_info); + +extern drmModePropertyPtr drmModeGetProperty(int fd, uint32_t propertyId); +extern void drmModeFreeProperty(drmModePropertyPtr ptr); + +extern drmModePropertyBlobPtr drmModeGetPropertyBlob(int fd, uint32_t blob_id); +extern void drmModeFreePropertyBlob(drmModePropertyBlobPtr ptr); +extern int drmModeOutputSetProperty(int fd, uint32_t output_id, uint32_t property_id, + uint64_t value); diff --git a/linux-core/Makefile b/linux-core/Makefile index 7f6b123e..76ed3e39 100644 --- a/linux-core/Makefile +++ b/linux-core/Makefile @@ -58,7 +58,7 @@ endif # Modules for all architectures MODULE_LIST := drm.o tdfx.o r128.o radeon.o mga.o sis.o savage.o via.o \ - mach64.o nv.o nouveau.o xgi.o + mach64.o nv.o nouveau.o xgi.o radeon_ms.o # Modules only for ix86 architectures ifneq (,$(findstring 86,$(MACHINE))) @@ -92,6 +92,7 @@ NVHEADERS = nv_drv.h $(DRMHEADERS) FFBHEADERS = ffb_drv.h $(DRMHEADERS) NOUVEAUHEADERS = nouveau_drv.h nouveau_drm.h nouveau_reg.h $(DRMHEADERS) XGIHEADERS = xgi_cmdlist.h xgi_drv.h xgi_misc.h xgi_regs.h $(DRMHEADERS) +RADEONMSHEADERS = radeon_ms_driver.h $(DRMHEADERS) PROGS = dristat drmstat @@ -286,6 +287,7 @@ CONFIG_DRM_MACH64 := n CONFIG_DRM_NV := n CONFIG_DRM_NOUVEAU := n CONFIG_DRM_XGI := n +CONFIG_DRM_RADEON_MS := m # Enable module builds for the modules requested/supported. diff --git a/linux-core/Makefile.kernel b/linux-core/Makefile.kernel index e7c280d0..7ef504ad 100644 --- a/linux-core/Makefile.kernel +++ b/linux-core/Makefile.kernel @@ -13,14 +13,16 @@ 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_regman.o + drm_fence.o drm_ttm.o drm_bo.o drm_bo_move.o drm_crtc.o \ + drm_edid.o drm_modes.o drm_bo_lock.o drm_regman.o tdfx-objs := tdfx_drv.o r128-objs := r128_drv.o r128_cce.o r128_state.o r128_irq.o mga-objs := mga_drv.o mga_dma.o mga_state.o mga_warp.o mga_irq.o i810-objs := i810_drv.o i810_dma.o i915-objs := i915_drv.o i915_dma.o i915_irq.o i915_mem.o i915_fence.o \ - i915_buffer.o i915_compat.o + i915_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 \ + i915_compat.o nouveau-objs := nouveau_drv.o nouveau_state.o nouveau_fifo.o nouveau_mem.o \ nouveau_object.o nouveau_irq.o nouveau_notifier.o nouveau_swmthd.o \ nouveau_sgdma.o nouveau_dma.o nouveau_buffer.o nouveau_fence.o \ @@ -31,7 +33,13 @@ nouveau-objs := nouveau_drv.o nouveau_state.o nouveau_fifo.o nouveau_mem.o \ nv04_graph.o nv10_graph.o nv20_graph.o \ nv40_graph.o nv50_graph.o \ nv04_instmem.o nv50_instmem.o -radeon-objs := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o radeon_irq.o r300_cmdbuf.o +radeon-objs := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o radeon_irq.o r300_cmdbuf.o radeon_fence.o radeon_buffer.o +radeon_ms-objs := radeon_ms_drv.o radeon_ms_drm.o radeon_ms_family.o \ + radeon_ms_state.o radeon_ms_bo.o radeon_ms_irq.o \ + radeon_ms_bus.o radeon_ms_fence.o \ + radeon_ms_cp.o radeon_ms_cp_mc.o radeon_ms_i2c.o \ + radeon_ms_output.o radeon_ms_crtc.o radeon_ms_fb.o \ + radeon_ms_exec.o radeon_ms_gpu.o radeon_ms_dac.o sis-objs := sis_drv.o sis_mm.o ffb-objs := ffb_drv.o ffb_context.o savage-objs := savage_drv.o savage_bci.o savage_state.o @@ -45,6 +53,7 @@ xgi-objs := xgi_cmdlist.o xgi_drv.o xgi_fb.o xgi_misc.o xgi_pcie.o \ ifeq ($(CONFIG_COMPAT),y) drm-objs += drm_ioc32.o radeon-objs += radeon_ioc32.o +radeon_ms-objs += radeon_ms_compat.o mga-objs += mga_ioc32.o r128-objs += r128_ioc32.o i915-objs += i915_ioc32.o @@ -67,3 +76,4 @@ obj-$(CONFIG_DRM_MACH64)+= mach64.o obj-$(CONFIG_DRM_NV) += nv.o obj-$(CONFIG_DRM_NOUVEAU) += nouveau.o obj-$(CONFIG_DRM_XGI) += xgi.o +obj-$(CONFIG_DRM_RADEON_MS) += radeon_ms.o diff --git a/linux-core/ati_pcigart.c b/linux-core/ati_pcigart.c index e6187365..5fafbb93 100644 --- a/linux-core/ati_pcigart.c +++ b/linux-core/ati_pcigart.c @@ -35,6 +35,45 @@ # define ATI_PCIGART_PAGE_SIZE 4096 /**< PCI GART page size */ +static __inline__ void insert_page_into_table(struct drm_ati_pcigart_info *info, u32 page_base, u32 *pci_gart) +{ + switch(info->gart_reg_if) { + case DRM_ATI_GART_IGP: + *pci_gart = cpu_to_le32((page_base) | 0xc); + break; + case DRM_ATI_GART_PCIE: + *pci_gart = cpu_to_le32((page_base >> 8) | 0xc); + break; + default: + case DRM_ATI_GART_PCI: + *pci_gart = cpu_to_le32(page_base); + break; + } +} + +static __inline__ u32 get_page_base_from_table(struct drm_ati_pcigart_info *info, u32 *pci_gart) +{ + u32 retval; + switch(info->gart_reg_if) { + case DRM_ATI_GART_IGP: + retval = *pci_gart; + retval &= ~0xc; + break; + case DRM_ATI_GART_PCIE: + retval = *pci_gart; + retval &= ~0xc; + retval <<= 8; + break; + default: + case DRM_ATI_GART_PCI: + retval = *pci_gart; + break; + } + return retval; +} + + + static void *drm_ati_alloc_pcigart_table(int order) { unsigned long address; @@ -207,18 +246,7 @@ int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *ga page_base = (u32) entry->busaddr[i]; for (j = 0; j < (PAGE_SIZE / ATI_PCIGART_PAGE_SIZE); j++) { - switch(gart_info->gart_reg_if) { - case DRM_ATI_GART_IGP: - *pci_gart = cpu_to_le32((page_base) | 0xc); - break; - case DRM_ATI_GART_PCIE: - *pci_gart = cpu_to_le32((page_base >> 8) | 0xc); - break; - default: - case DRM_ATI_GART_PCI: - *pci_gart = cpu_to_le32(page_base); - break; - } + insert_page_into_table(gart_info, page_base, pci_gart); pci_gart++; page_base += ATI_PCIGART_PAGE_SIZE; } @@ -238,3 +266,146 @@ int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *ga return ret; } EXPORT_SYMBOL(drm_ati_pcigart_init); + +static int ati_pcigart_needs_unbind_cache_adjust(struct drm_ttm_backend *backend) +{ + return ((backend->flags & DRM_BE_FLAG_BOUND_CACHED) ? 0 : 1); +} + +static int ati_pcigart_populate(struct drm_ttm_backend *backend, + unsigned long num_pages, + struct page **pages) +{ + ati_pcigart_ttm_backend_t *atipci_be = + container_of(backend, ati_pcigart_ttm_backend_t, backend); + + DRM_ERROR("%ld\n", num_pages); + atipci_be->pages = pages; + atipci_be->num_pages = num_pages; + atipci_be->populated = 1; + return 0; +} + +static int ati_pcigart_bind_ttm(struct drm_ttm_backend *backend, + struct drm_bo_mem_reg *bo_mem) +{ + ati_pcigart_ttm_backend_t *atipci_be = + container_of(backend, ati_pcigart_ttm_backend_t, backend); + off_t j; + int i; + struct drm_ati_pcigart_info *info = atipci_be->gart_info; + u32 *pci_gart; + u32 page_base; + unsigned long offset = bo_mem->mm_node->start; + pci_gart = info->addr; + + DRM_ERROR("Offset is %08lX\n", bo_mem->mm_node->start); + j = offset; + while (j < (offset + atipci_be->num_pages)) { + if (get_page_base_from_table(info, pci_gart+j)) + return -EBUSY; + j++; + } + + for (i = 0, j = offset; i < atipci_be->num_pages; i++, j++) { + struct page *cur_page = atipci_be->pages[i]; + /* write value */ + page_base = page_to_phys(cur_page); + insert_page_into_table(info, page_base, pci_gart + j); + } + +#if defined(__i386__) || defined(__x86_64__) + wbinvd(); +#else + mb(); +#endif + + atipci_be->gart_flush_fn(atipci_be->dev); + + atipci_be->bound = 1; + atipci_be->offset = offset; + /* need to traverse table and add entries */ + DRM_DEBUG("\n"); + return 0; +} + +static int ati_pcigart_unbind_ttm(struct drm_ttm_backend *backend) +{ + ati_pcigart_ttm_backend_t *atipci_be = + container_of(backend, ati_pcigart_ttm_backend_t, backend); + struct drm_ati_pcigart_info *info = atipci_be->gart_info; + unsigned long offset = atipci_be->offset; + int i; + off_t j; + u32 *pci_gart = info->addr; + + DRM_DEBUG("\n"); + + if (atipci_be->bound != 1) + return -EINVAL; + + for (i = 0, j = offset; i < atipci_be->num_pages; i++, j++) { + *(pci_gart + j) = 0; + } + atipci_be->gart_flush_fn(atipci_be->dev); + atipci_be->bound = 0; + atipci_be->offset = 0; + return 0; +} + +static void ati_pcigart_clear_ttm(struct drm_ttm_backend *backend) +{ + ati_pcigart_ttm_backend_t *atipci_be = + container_of(backend, ati_pcigart_ttm_backend_t, backend); + + DRM_DEBUG("\n"); + if (atipci_be->pages) { + backend->func->unbind(backend); + atipci_be->pages = NULL; + + } + atipci_be->num_pages = 0; +} + +static void ati_pcigart_destroy_ttm(struct drm_ttm_backend *backend) +{ + ati_pcigart_ttm_backend_t *atipci_be; + if (backend) { + DRM_DEBUG("\n"); + atipci_be = container_of(backend, ati_pcigart_ttm_backend_t, backend); + if (atipci_be) { + if (atipci_be->pages) { + backend->func->clear(backend); + } + drm_ctl_free(atipci_be, sizeof(*atipci_be), DRM_MEM_TTM); + } + } +} + +static struct drm_ttm_backend_func ati_pcigart_ttm_backend = +{ + .needs_ub_cache_adjust = ati_pcigart_needs_unbind_cache_adjust, + .populate = ati_pcigart_populate, + .clear = ati_pcigart_clear_ttm, + .bind = ati_pcigart_bind_ttm, + .unbind = ati_pcigart_unbind_ttm, + .destroy = ati_pcigart_destroy_ttm, +}; + +struct drm_ttm_backend *ati_pcigart_init_ttm(struct drm_device *dev, struct drm_ati_pcigart_info *info, void (*gart_flush_fn)(struct drm_device *dev)) +{ + ati_pcigart_ttm_backend_t *atipci_be; + + atipci_be = drm_ctl_calloc(1, sizeof (*atipci_be), DRM_MEM_TTM); + if (!atipci_be) + return NULL; + + atipci_be->populated = 0; + atipci_be->backend.func = &ati_pcigart_ttm_backend; + atipci_be->gart_info = info; + atipci_be->gart_flush_fn = gart_flush_fn; + atipci_be->dev = dev; + + return &atipci_be->backend; +} +EXPORT_SYMBOL(ati_pcigart_init_ttm); diff --git a/linux-core/drmP.h b/linux-core/drmP.h index dbf2a924..4a0cabc3 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -175,6 +175,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 */ @@ -662,6 +666,10 @@ struct drm_driver { 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; @@ -831,6 +839,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 @@ -842,6 +853,17 @@ struct drm_agp_ttm_backend { }; #endif +typedef struct ati_pcigart_ttm_backend { + struct drm_ttm_backend backend; + int populated; + void (*gart_flush_fn)(struct drm_device *dev); + struct drm_ati_pcigart_info *gart_info; + unsigned long offset; + struct page **pages; + int num_pages; + int bound; + struct drm_device *dev; +} ati_pcigart_ttm_backend_t; static __inline__ int drm_core_check_feature(struct drm_device *dev, int feature) @@ -1159,6 +1181,7 @@ extern int drm_sg_free(struct drm_device *dev, void *data, /* ATI PCIGART support (ati_pcigart.h) */ extern int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *gart_info); extern int drm_ati_pcigart_cleanup(struct drm_device *dev, struct drm_ati_pcigart_info *gart_info); +extern struct drm_ttm_backend *ati_pcigart_init_ttm(struct drm_device *dev, struct drm_ati_pcigart_info *info, void (*gart_flush_fn)(struct drm_device *dev)); extern drm_dma_handle_t *drm_pci_alloc(struct drm_device *dev, size_t size, size_t align, dma_addr_t maxaddr); diff --git a/linux-core/drm_bo.c b/linux-core/drm_bo.c index 2b8ef1bf..e912eb21 100644 --- a/linux-core/drm_bo.c +++ b/linux-core/drm_bo.c @@ -2222,6 +2222,7 @@ out: mutex_unlock(&dev->struct_mutex); return ret; } +EXPORT_SYMBOL(drm_bo_driver_finish); /* * This function is intended to be called on drm driver load. diff --git a/linux-core/drm_bo_move.c b/linux-core/drm_bo_move.c index 971b4af8..97946b3c 100644 --- a/linux-core/drm_bo_move.c +++ b/linux-core/drm_bo_move.c @@ -147,6 +147,7 @@ void drm_mem_reg_iounmap(struct drm_device *dev, struct drm_bo_mem_reg *mem, if (virtual && (man->flags & _DRM_FLAG_NEEDS_IOREMAP)) iounmap(virtual); } +EXPORT_SYMBOL(drm_mem_reg_iounmap); static int drm_copy_io_page(void *dst, void *src, unsigned long page) { diff --git a/linux-core/drm_bufs.c b/linux-core/drm_bufs.c index 75c75c2f..967e9a2d 100644 --- a/linux-core/drm_bufs.c +++ b/linux-core/drm_bufs.c @@ -53,8 +53,8 @@ struct drm_map_list *drm_find_matching_map(struct drm_device *dev, drm_local_map struct drm_map_list *entry; list_for_each_entry(entry, &dev->maplist, head) { if (entry->map && map->type == entry->map->type && - ((entry->map->offset == map->offset) || - (map->type == _DRM_SHM && map->flags==_DRM_CONTAINS_LOCK))) { + ((entry->map->offset == map->offset) || + ((map->type == _DRM_SHM) && (map->flags&_DRM_CONTAINS_LOCK)))) { return entry; } } @@ -412,6 +412,9 @@ 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 */ + dev->lock.file_priv = NULL; + wake_up_interruptible(&dev->lock.lock_queue); break; case _DRM_AGP: case _DRM_SCATTER_GATHER: diff --git a/linux-core/drm_compat.h b/linux-core/drm_compat.h index f8933e0c..3f3abc9a 100644 --- a/linux-core/drm_compat.h +++ b/linux-core/drm_compat.h @@ -60,6 +60,13 @@ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) #undef DRM_IRQ_ARGS #define DRM_IRQ_ARGS int irq, void *arg, struct pt_regs *regs + +typedef _Bool bool; +enum { + false = 0, + true = 1 +}; + #endif #ifndef list_for_each_safe @@ -152,7 +159,7 @@ static __inline__ void *kcalloc(size_t nmemb, size_t size, int flags) #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) #define vmalloc_user(_size) ({void * tmp = vmalloc(_size); \ - if (tmp) memset(tmp, 0, size); \ + if (tmp) memset(tmp, 0, _size); \ (tmp);}) #endif diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c new file mode 100644 index 00000000..e1b37c0b --- /dev/null +++ b/linux-core/drm_crtc.c @@ -0,0 +1,2266 @@ +/* + * 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" + +struct drm_prop_enum_list { + int type; + char *name; +}; + +static struct drm_prop_enum_list drm_dpms_enum_list[] = +{ { DPMSModeOn, "On" }, + { DPMSModeStandby, "Standby" }, + { DPMSModeSuspend, "Suspend" }, + { DPMSModeOff, "Off" } +}; +static struct drm_prop_enum_list drm_conn_enum_list[] = +{ { ConnectorVGA, "VGA" }, + { ConnectorDVII, "DVI-I" }, + { ConnectorDVID, "DVI-D" }, + { ConnectorDVIA, "DVI-A" }, + { ConnectorComposite, "Composite" }, + { ConnectorSVIDEO, "SVIDEO" }, + { ConnectorLVDS, "LVDS" }, + { ConnectorComponent, "Component" }, + { Connector9PinDIN, "9-pin DIN" }, + { ConnectorDisplayPort, "DisplayPort" }, + { ConnectorHDMIA, "HDMI Type A" }, + { ConnectorHDMIB, "HDMI Type B" }, +}; + + +/** + * 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_single_output_modes(struct drm_output *output, int maxX, int maxY) +{ + struct drm_device *dev = output->dev; + struct drm_display_mode *mode, *t; + int ret; + //if (maxX == 0 || maxY == 0) + // TODO + + /* 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 */ + return; + } + + 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); + } +} + +void drm_crtc_probe_output_modes(struct drm_device *dev, int maxX, int maxY) +{ + struct drm_output *output; + + list_for_each_entry(output, &dev->mode_config.output_list, head) { + drm_crtc_probe_single_output_modes(output, maxX, maxY); + } +} + +/** + * 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 %x\n", output->name, mode->name, mode->mode_id); + + 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->user_modes); + 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++; + + drm_output_attach_property(output, dev->mode_config.edid_property, 0); + + drm_output_attach_property(output, dev->mode_config.dpms_property, 0); + + 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); + + list_for_each_entry_safe(mode, t, &output->user_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); + +static int drm_mode_create_standard_output_properties(struct drm_device *dev) +{ + int i; + + dev->mode_config.edid_property = + drm_property_create(dev, DRM_MODE_PROP_BLOB | DRM_MODE_PROP_IMMUTABLE, + "EDID", 0); + + dev->mode_config.dpms_property = + drm_property_create(dev, DRM_MODE_PROP_ENUM, "DPMS", 4); + + for (i = 0; i < ARRAY_SIZE(drm_dpms_enum_list); i++) + drm_property_add_enum(dev->mode_config.dpms_property, i, drm_dpms_enum_list[i].type, drm_dpms_enum_list[i].name); + + dev->mode_config.connector_type_property = + drm_property_create(dev, DRM_MODE_PROP_ENUM | DRM_MODE_PROP_IMMUTABLE, + "Connector Type", 10); + for (i = 0; i < ARRAY_SIZE(drm_conn_enum_list); i++) + drm_property_add_enum(dev->mode_config.connector_type_property, i, drm_conn_enum_list[i].type, drm_conn_enum_list[i].name); + + dev->mode_config.connector_num_property = + drm_property_create(dev, DRM_MODE_PROP_RANGE | DRM_MODE_PROP_IMMUTABLE, + "Connector ID", 2); + dev->mode_config.connector_num_property->values[0] = 0; + dev->mode_config.connector_num_property->values[1] = 20; + return 0; +} + +/** + * 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.property_list); + INIT_LIST_HEAD(&dev->mode_config.property_blob_list); + idr_init(&dev->mode_config.crtc_idr); + + drm_mode_create_standard_output_properties(dev); + +} +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 0x%x:%s\n",c,des_mode->mode_id, 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_property *property, *pt; + + list_for_each_entry_safe(output, ot, &dev->mode_config.output_list, head) { + drm_output_destroy(output); + } + + list_for_each_entry_safe(property, pt, &dev->mode_config.property_list, head) { + drm_property_destroy(dev, property); + } + + list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { + 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 = false; + 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 && !drm_mode_equal(new_mode, &crtc->mode)) + 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->clock = in->clock; + out->hdisplay = in->hdisplay; + out->hsync_start = in->hsync_start; + out->hsync_end = in->hsync_end; + out->htotal = in->htotal; + out->hskew = in->hskew; + out->vdisplay = in->vdisplay; + out->vsync_start = in->vsync_start; + out->vsync_end = in->vsync_end; + out->vtotal = in->vtotal; + out->vscan = in->vscan; + out->vrefresh = in->vrefresh; + out->flags = in->flags; + out->type = in->type; + strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); + out->name[DRM_DISPLAY_MODE_LEN-1] = 0; +} + +/** + * drm_crtc_convert_to_umode - convert a modeinfo into a drm_display_mode + * @out: drm_display_mode to return to the user + * @in: drm_mode_modeinfo to use + * + * LOCKING: + * None. + * + * Convert a drmo_mode_modeinfo into a drm_display_mode structure to return to + * the caller. + */ +void drm_crtc_convert_umode(struct drm_display_mode *out, struct drm_mode_modeinfo *in) +{ + out->clock = in->clock; + out->hdisplay = in->hdisplay; + out->hsync_start = in->hsync_start; + out->hsync_end = in->hsync_end; + out->htotal = in->htotal; + out->hskew = in->hskew; + out->vdisplay = in->vdisplay; + out->vsync_start = in->vsync_start; + out->vsync_end = in->vsync_end; + out->vtotal = in->vtotal; + out->vscan = in->vscan; + out->vrefresh = in->vrefresh; + out->flags = in->flags; + out->type = in->type; + strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); + out->name[DRM_DISPLAY_MODE_LEN-1] = 0; +} + +/** + * drm_mode_getresources - get graphics configuration + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * LOCKING: + * Takes mode config lock. + * + * Construct a set of configuration description structures and return + * them to the user, including CRTC, 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; + int ret = 0; + int output_count = 0; + int crtc_count = 0; + int fb_count = 0; + int copied = 0; + uint32_t __user *fb_id; + uint32_t __user *crtc_id; + uint32_t __user *output_id; + + 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(lh, &dev->mode_config.output_list) + output_count++; + + card_res->max_height = dev->mode_config.max_height; + card_res->min_height = dev->mode_config.min_height; + card_res->max_width = dev->mode_config.max_width; + card_res->min_width = dev->mode_config.min_width; + + /* handle this in 4 parts */ + /* FBs */ + if (card_res->count_fbs >= fb_count) { + copied = 0; + fb_id = (uint32_t *)(unsigned long)card_res->fb_id_ptr; + list_for_each_entry(fb, &dev->mode_config.fb_list, head) { + if (put_user(fb->id, fb_id + copied)) { + ret = -EFAULT; + goto out; + } + copied++; + } + } + card_res->count_fbs = fb_count; + + /* CRTCs */ + if (card_res->count_crtcs >= crtc_count) { + copied = 0; + crtc_id = (uint32_t *)(unsigned long)card_res->crtc_id_ptr; + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head){ + DRM_DEBUG("CRTC ID is %d\n", crtc->id); + if (put_user(crtc->id, crtc_id + copied)) { + ret = -EFAULT; + goto out; + } + copied++; + } + } + card_res->count_crtcs = crtc_count; + + + /* Outputs */ + if (card_res->count_outputs >= output_count) { + copied = 0; + output_id = (uint32_t *)(unsigned long)card_res->output_id_ptr; + list_for_each_entry(output, &dev->mode_config.output_list, + head) { + DRM_DEBUG("OUTPUT ID is %d\n", output->id); + if (put_user(output->id, output_id + copied)) { + ret = -EFAULT; + goto out; + } + copied++; + } + } + card_res->count_outputs = output_count; + + DRM_DEBUG("Counted %d %d\n", card_res->count_crtcs, + card_res->count_outputs); + +out: + mutex_unlock(&dev->mode_config.mutex); + return ret; +} + +/** + * drm_mode_getcrtc - get CRTC configuration + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * LOCKING: + * Caller? (FIXME) + * + * Construct a CRTC configuration structure to return to the user. + * + * Called by the user via ioctl. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_mode_getcrtc(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_crtc *crtc_resp = data; + struct drm_crtc *crtc; + struct drm_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; + + if (crtc->fb) + crtc_resp->fb_id = crtc->fb->id; + else + crtc_resp->fb_id = 0; + + crtc_resp->outputs = 0; + if (crtc->enabled) { + + drm_crtc_convert_to_umode(&crtc_resp->mode, &crtc->mode); + crtc_resp->mode_valid = 1; + 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_valid = 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 props_count = 0; + int ret = 0; + int copied = 0; + int i; + struct drm_mode_modeinfo u_mode; + struct drm_mode_modeinfo __user *mode_ptr; + uint32_t __user *prop_ptr; + uint64_t __user *prop_values; + + memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); + + 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 out; + } + + list_for_each_entry(mode, &output->modes, head) + mode_count++; + + for (i = 0; i < DRM_OUTPUT_MAX_PROPERTY; i++) { + if (output->property_ids[i] != 0) { + props_count++; + } + } + + if (out_resp->count_modes == 0) { + drm_crtc_probe_single_output_modes(output, dev->mode_config.max_width, dev->mode_config.max_height); + } + + 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; + + out_resp->crtcs = output->possible_crtcs; + out_resp->clones = output->possible_clones; + + if ((out_resp->count_modes >= mode_count) && mode_count) { + copied = 0; + mode_ptr = (struct drm_mode_modeinfo *)(unsigned long)out_resp->modes_ptr; + list_for_each_entry(mode, &output->modes, head) { + drm_crtc_convert_to_umode(&u_mode, mode); + if (copy_to_user(mode_ptr + copied, + &u_mode, sizeof(u_mode))) { + ret = -EFAULT; + goto out; + } + copied++; + + } + } + out_resp->count_modes = mode_count; + + if ((out_resp->count_props >= props_count) && props_count) { + copied = 0; + prop_ptr = (uint32_t *)(unsigned long)(out_resp->props_ptr); + prop_values = (uint64_t *)(unsigned long)(out_resp->prop_values_ptr); + for (i = 0; i < DRM_OUTPUT_MAX_PROPERTY; i++) { + if (output->property_ids[i] != 0) { + if (put_user(output->property_ids[i], prop_ptr + copied)) { + ret = -EFAULT; + goto out; + } + + if (put_user(output->property_values[i], prop_values + copied)) { + ret = -EFAULT; + goto out; + } + copied++; + } + } + } + out_resp->count_props = props_count; + +out: + mutex_unlock(&dev->mode_config.mutex); + return ret; +} + +/** + * drm_mode_setcrtc - set CRTC configuration + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * LOCKING: + * Caller? (FIXME) + * + * Build a new CRTC configuration based on user request. + * + * Called by the user via ioctl. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_mode_setcrtc(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_crtc *crtc_req = data; + struct drm_crtc *crtc; + struct drm_output **output_set = NULL, *output; + struct drm_framebuffer *fb = NULL; + struct drm_display_mode *mode = NULL; + int ret = 0; + int i; + uint32_t __user *set_outputs_ptr; + + 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_valid) { + /* 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 = drm_mode_create(dev); + drm_crtc_convert_umode(mode, &crtc_req->mode); + } + + 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++) { + set_outputs_ptr = (uint32_t *)(unsigned long)crtc_req->set_outputs_ptr; + if (get_user(out_id, &set_outputs_ptr[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; + + mutex_lock(&dev->mode_config.mutex); + 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); +} + +/* + * + */ + +static int drm_mode_attachmode(struct drm_device *dev, + struct drm_output *output, + struct drm_display_mode *mode) +{ + int ret = 0; + + list_add_tail(&mode->head, &output->user_modes); + return ret; +} + +int drm_mode_attachmode_crtc(struct drm_device *dev, struct drm_crtc *crtc, + struct drm_display_mode *mode) +{ + struct drm_output *output; + int ret = 0; + struct drm_display_mode *dup_mode; + int need_dup = 0; + list_for_each_entry(output, &dev->mode_config.output_list, head) { + if (output->crtc == crtc) { + if (need_dup) + dup_mode = drm_mode_duplicate(dev, mode); + else + dup_mode = mode; + ret = drm_mode_attachmode(dev, output, dup_mode); + if (ret) + return ret; + need_dup = 1; + } + } + return 0; +} +EXPORT_SYMBOL(drm_mode_attachmode_crtc); + +static int drm_mode_detachmode(struct drm_device *dev, + struct drm_output *output, + struct drm_display_mode *mode) +{ + int found = 0; + int ret = 0; + struct drm_display_mode *match_mode, *t; + + list_for_each_entry_safe(match_mode, t, &output->user_modes, head) { + if (drm_mode_equal(match_mode, mode)) { + list_del(&match_mode->head); + drm_mode_destroy(dev, match_mode); + found = 1; + break; + } + } + + if (!found) + ret = -EINVAL; + + return ret; +} + +int drm_mode_detachmode_crtc(struct drm_device *dev, struct drm_display_mode *mode) +{ + struct drm_output *output; + + list_for_each_entry(output, &dev->mode_config.output_list, head) { + drm_mode_detachmode(dev, output, mode); + } + return 0; +} +EXPORT_SYMBOL(drm_mode_detachmode_crtc); + +/** + * 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_ioctl(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; + struct drm_mode_modeinfo *umode = &mode_cmd->mode; + int ret = 0; + + mutex_lock(&dev->mode_config.mutex); + + output = idr_find(&dev->mode_config.crtc_idr, mode_cmd->output_id); + if (!output || (output->id != mode_cmd->output_id)) { + ret = -EINVAL; + goto out; + } + + mode = drm_mode_create(dev); + if (!mode) { + ret = -ENOMEM; + goto out; + } + + drm_crtc_convert_umode(mode, umode); + + ret = drm_mode_attachmode(dev, output, mode); +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_ioctl(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; + struct drm_mode_modeinfo *umode = &mode_cmd->mode; + int ret = 0; + + mutex_lock(&dev->mode_config.mutex); + + output = idr_find(&dev->mode_config.crtc_idr, mode_cmd->output_id); + if (!output || (output->id != mode_cmd->output_id)) { + ret = -EINVAL; + goto out; + } + + drm_crtc_convert_umode(&mode, umode); + ret = drm_mode_detachmode(dev, output, &mode); +out: + mutex_unlock(&dev->mode_config.mutex); + return ret; +} + +struct drm_property *drm_property_create(struct drm_device *dev, int flags, + const char *name, int num_values) +{ + struct drm_property *property = NULL; + + property = kzalloc(sizeof(struct drm_output), GFP_KERNEL); + if (!property) + return NULL; + + if (num_values) { + property->values = kzalloc(sizeof(uint64_t)*num_values, GFP_KERNEL); + if (!property->values) + goto fail; + } + + property->id = drm_idr_get(dev, property); + property->flags = flags; + property->num_values = num_values; + INIT_LIST_HEAD(&property->enum_blob_list); + + if (name) + strncpy(property->name, name, DRM_PROP_NAME_LEN); + + list_add_tail(&property->head, &dev->mode_config.property_list); + return property; +fail: + kfree(property); + return NULL; +} +EXPORT_SYMBOL(drm_property_create); + +int drm_property_add_enum(struct drm_property *property, int index, + uint64_t value, const char *name) +{ + struct drm_property_enum *prop_enum; + + if (!(property->flags & DRM_MODE_PROP_ENUM)) + return -EINVAL; + + if (!list_empty(&property->enum_blob_list)) { + list_for_each_entry(prop_enum, &property->enum_blob_list, head) { + if (prop_enum->value == value) { + strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); + prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; + return 0; + } + } + } + + prop_enum = kzalloc(sizeof(struct drm_property_enum), GFP_KERNEL); + if (!prop_enum) + return -ENOMEM; + + strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); + prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; + prop_enum->value = value; + + property->values[index] = value; + list_add_tail(&prop_enum->head, &property->enum_blob_list); + return 0; +} +EXPORT_SYMBOL(drm_property_add_enum); + +void drm_property_destroy(struct drm_device *dev, struct drm_property *property) +{ + struct drm_property_enum *prop_enum, *pt; + + list_for_each_entry_safe(prop_enum, pt, &property->enum_blob_list, head) { + list_del(&prop_enum->head); + kfree(prop_enum); + } + + if (property->num_values) + kfree(property->values); + drm_idr_put(dev, property->id); + list_del(&property->head); + kfree(property); +} +EXPORT_SYMBOL(drm_property_destroy); + +int drm_output_attach_property(struct drm_output *output, + struct drm_property *property, uint64_t init_val) +{ + int i; + + for (i = 0; i < DRM_OUTPUT_MAX_PROPERTY; i++) { + if (output->property_ids[i] == 0) { + output->property_ids[i] = property->id; + output->property_values[i] = init_val; + break; + } + } + + if (i == DRM_OUTPUT_MAX_PROPERTY) + return -EINVAL; + return 0; +} +EXPORT_SYMBOL(drm_output_attach_property); + +int drm_output_property_set_value(struct drm_output *output, + struct drm_property *property, uint64_t value) +{ + int i; + + for (i = 0; i < DRM_OUTPUT_MAX_PROPERTY; i++) { + if (output->property_ids[i] == property->id) { + output->property_values[i] = value; + break; + } + } + + if (i == DRM_OUTPUT_MAX_PROPERTY) + return -EINVAL; + return 0; +} +EXPORT_SYMBOL(drm_output_property_set_value); + +int drm_mode_getproperty_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_get_property *out_resp = data; + struct drm_property *property; + int enum_count = 0; + int blob_count = 0; + int value_count = 0; + int ret = 0, i; + int copied; + struct drm_property_enum *prop_enum; + struct drm_mode_property_enum __user *enum_ptr; + struct drm_property_blob *prop_blob; + uint32_t *blob_id_ptr; + uint64_t __user *values_ptr; + uint32_t __user *blob_length_ptr; + + mutex_lock(&dev->mode_config.mutex); + property = idr_find(&dev->mode_config.crtc_idr, out_resp->prop_id); + if (!property || (property->id != out_resp->prop_id)) { + ret = -EINVAL; + goto done; + } + + if (property->flags & DRM_MODE_PROP_ENUM) { + list_for_each_entry(prop_enum, &property->enum_blob_list, head) + enum_count++; + } else if (property->flags & DRM_MODE_PROP_BLOB) { + list_for_each_entry(prop_blob, &property->enum_blob_list, head) + blob_count++; + } + + value_count = property->num_values; + + strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN); + out_resp->name[DRM_PROP_NAME_LEN-1] = 0; + out_resp->flags = property->flags; + + if ((out_resp->count_values >= value_count) && value_count) { + values_ptr = (uint64_t *)(unsigned long)out_resp->values_ptr; + for (i = 0; i < value_count; i++) { + if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) { + ret = -EFAULT; + goto done; + } + } + } + out_resp->count_values = value_count; + + if (property->flags & DRM_MODE_PROP_ENUM) { + + if ((out_resp->count_enum_blobs >= enum_count) && enum_count) { + copied = 0; + enum_ptr = (struct drm_mode_property_enum *)(unsigned long)out_resp->enum_blob_ptr; + list_for_each_entry(prop_enum, &property->enum_blob_list, head) { + + if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) { + ret = -EFAULT; + goto done; + } + + if (copy_to_user(&enum_ptr[copied].name, + &prop_enum->name, DRM_PROP_NAME_LEN)) { + ret = -EFAULT; + goto done; + } + copied++; + } + } + out_resp->count_enum_blobs = enum_count; + } + + if (property->flags & DRM_MODE_PROP_BLOB) { + if ((out_resp->count_enum_blobs >= blob_count) && blob_count) { + copied = 0; + blob_id_ptr = (uint32_t *)(unsigned long)out_resp->enum_blob_ptr; + blob_length_ptr = (uint32_t *)(unsigned long)out_resp->values_ptr; + + list_for_each_entry(prop_blob, &property->enum_blob_list, head) { + if (put_user(prop_blob->id, blob_id_ptr + copied)) { + ret = -EFAULT; + goto done; + } + + if (put_user(prop_blob->length, blob_length_ptr + copied)) { + ret = -EFAULT; + goto done; + } + + copied++; + } + } + out_resp->count_enum_blobs = enum_count; + } +done: + mutex_unlock(&dev->mode_config.mutex); + return ret; +} + +static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, int length, + void *data) +{ + struct drm_property_blob *blob; + + if (!length || !data) + return NULL; + + blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL); + if (!blob) + return NULL; + + blob->data = (void *)((char *)blob + sizeof(struct drm_property_blob)); + blob->length = length; + + memcpy(blob->data, data, length); + + blob->id = drm_idr_get(dev, blob); + + list_add_tail(&blob->head, &dev->mode_config.property_blob_list); + return blob; +} + +static void drm_property_destroy_blob(struct drm_device *dev, + struct drm_property_blob *blob) +{ + drm_idr_put(dev, blob->id); + list_del(&blob->head); + kfree(blob); +} + +int drm_mode_getblob_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_get_blob *out_resp = data; + struct drm_property_blob *blob; + int ret = 0; + void *blob_ptr; + + mutex_lock(&dev->mode_config.mutex); + + blob = idr_find(&dev->mode_config.crtc_idr, out_resp->blob_id); + if (!blob || (blob->id != out_resp->blob_id)) { + ret = -EINVAL; + goto done; + } + + if (out_resp->length == blob->length) { + blob_ptr = (void *)(unsigned long)out_resp->data; + if (copy_to_user(blob_ptr, blob->data, blob->length)){ + ret = -EFAULT; + goto done; + } + } + out_resp->length = blob->length; + +done: + mutex_unlock(&dev->mode_config.mutex); + return ret; +} + +int drm_mode_output_update_edid_property(struct drm_output *output, unsigned char *edid) +{ + struct drm_device *dev = output->dev; + int ret = 0; + if (output->edid_blob_ptr) + drm_property_destroy_blob(dev, output->edid_blob_ptr); + + output->edid_blob_ptr = drm_property_create_blob(output->dev, 128, edid); + + ret = drm_output_property_set_value(output, dev->mode_config.edid_property, output->edid_blob_ptr->id); + return ret; +} +EXPORT_SYMBOL(drm_mode_output_update_edid_property); + +int drm_mode_output_property_set_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_output_set_property *out_resp = data; + struct drm_property *property; + struct drm_output *output; + int ret = -EINVAL; + int i; + + mutex_lock(&dev->mode_config.mutex); + output = idr_find(&dev->mode_config.crtc_idr, out_resp->output_id); + if (!output || (output->id != out_resp->output_id)) { + goto out; + } + + for (i = 0; i < DRM_OUTPUT_MAX_PROPERTY; i++) { + if (output->property_ids[i] == out_resp->prop_id) + break; + } + + if (i == DRM_OUTPUT_MAX_PROPERTY) { + goto out; + } + + property = idr_find(&dev->mode_config.crtc_idr, out_resp->prop_id); + if (!property || (property->id != out_resp->prop_id)) { + goto out; + } + + if (property->flags & DRM_MODE_PROP_IMMUTABLE) + goto out; + + if (property->flags & DRM_MODE_PROP_RANGE) { + if (out_resp->value < property->values[0]) + goto out; + + if (out_resp->value > property->values[1]) + goto out; + } else { + int found = 0; + for (i = 0; i < property->num_values; i++) { + if (property->values[i] == out_resp->value) { + found = 1; + break; + } + } + if (!found) { + goto out; + } + } + + if (output->funcs->set_property) + ret = output->funcs->set_property(output, property, out_resp->value); + +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..6b6e1dbf --- /dev/null +++ b/linux-core/drm_crtc.h @@ -0,0 +1,621 @@ +/* + * 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_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 + +#define ConnectorUnknown 0 +#define ConnectorVGA 1 +#define ConnectorDVII 2 +#define ConnectorDVID 3 +#define ConnectorDVIA 4 +#define ConnectorComposite 5 +#define ConnectorSVIDEO 6 +#define ConnectorLVDS 7 +#define ConnectorComponent 8 +#define Connector9PinDIN 9 +#define ConnectorDisplayPort 10 +#define ConnectorHDMIA 11 +#define ConnectorHDMIB 12 + +enum drm_output_status { + output_status_connected = 1, + output_status_disconnected = 2, + output_status_unknown = 3, +}; + +enum subpixel_order { + SubPixelUnknown = 0, + SubPixelHorizontalRGB, + SubPixelHorizontalBGR, + SubPixelVerticalRGB, + SubPixelVerticalBGR, + SubPixelNone, +}; + +/* + * Describes a given display (e.g. CRT or flat panel) and its limitations. + */ +struct drm_display_info { + char name[DRM_DISPLAY_INFO_LEN]; + /* Input info */ + bool serration_vsync; + bool sync_on_green; + bool composite_sync; + bool separate_syncs; + bool blank_to_black; + unsigned char video_level; + bool digital; + /* Physical size */ + unsigned int width_mm; + unsigned int height_mm; + + /* Display parameters */ + unsigned char gamma; /* FIXME: storage format */ + bool gtf_supported; + bool standard_color; + enum { + monochrome, + 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_property_blob { + struct list_head head; + unsigned int length; + unsigned int id; + void *data; +}; + +struct drm_property_enum { + uint64_t value; + struct list_head head; + unsigned char name[DRM_PROP_NAME_LEN]; +}; + +struct drm_property { + struct list_head head; + int id; /* idr assigned */ + uint32_t flags; + char name[DRM_PROP_NAME_LEN]; + uint32_t num_values; + uint64_t *values; + + struct list_head enum_blob_list; +}; + +struct drm_crtc; +struct drm_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, struct drm_property *property, + uint64_t val); + void (*cleanup)(struct drm_output *output); +}; + +#define DRM_OUTPUT_MAX_UMODES 16 +#define DRM_OUTPUT_MAX_PROPERTY 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; + + struct list_head user_modes; + struct drm_property_blob *edid_blob_ptr; + u32 property_ids[DRM_OUTPUT_MAX_PROPERTY]; + uint64_t property_values[DRM_OUTPUT_MAX_PROPERTY]; +}; + +/** + * 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 property_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; + + /* pointers to standard properties */ + struct list_head property_blob_list; + struct drm_property *edid_property; + struct drm_property *dpms_property; + struct drm_property *connector_type_property; + struct drm_property *connector_num_property; +}; + +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 struct edid *drm_get_edid(struct drm_output *output, + struct i2c_adapter *adapter); +extern int drm_add_edid_modes(struct drm_output *output, struct edid *edid); +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); + +/* for us by fb module */ +extern int drm_mode_attachmode_crtc(struct drm_device *dev, + struct drm_crtc *crtc, + struct drm_display_mode *mode); +extern int drm_mode_detachmode_crtc(struct drm_device *dev, struct drm_display_mode *mode); + +extern struct drm_display_mode *drm_mode_create(struct drm_device *dev); +extern void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode); +extern void drm_mode_list_concat(struct list_head *head, + struct list_head *new); +extern void drm_mode_validate_size(struct drm_device *dev, + struct list_head *mode_list, + int maxX, int maxY, int maxPitch); +extern void drm_mode_prune_invalid(struct drm_device *dev, + struct list_head *mode_list, bool verbose); +extern void drm_mode_sort(struct list_head *mode_list); +extern int drm_mode_vrefresh(struct drm_display_mode *mode); +extern void drm_mode_set_crtcinfo(struct drm_display_mode *p, + int adjust_flags); +extern void drm_mode_output_list_update(struct drm_output *output); +extern int drm_mode_output_update_edid_property(struct drm_output *output, unsigned char *edid); +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); + +extern int drm_output_attach_property(struct drm_output *output, + struct drm_property *property, uint64_t init_val); +extern struct drm_property *drm_property_create(struct drm_device *dev, int flags, + const char *name, int num_values); +extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property); +extern int drm_property_add_enum(struct drm_property *property, int index, + uint64_t value, const char *name); + +/* 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_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +extern int drm_mode_rmmode_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +extern int drm_mode_attachmode_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +extern int drm_mode_detachmode_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); + +extern int drm_mode_getproperty_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +extern int drm_mode_getblob_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +extern int drm_mode_output_property_set_ioctl(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 343d5f32..0b9ce30a 100644 --- a/linux-core/drm_drv.c +++ b/linux-core/drm_drv.c @@ -115,13 +115,21 @@ 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_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_SETPROPERTY, drm_mode_output_property_set_ioctl, DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATTACHMODE, drm_mode_attachmode_ioctl, DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_DETACHMODE, drm_mode_detachmode_ioctl, DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_MASTER | DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_MM_INIT, drm_mm_init_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), @@ -177,8 +185,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"); @@ -272,11 +278,6 @@ int drm_lastclose(struct drm_device * dev) if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) drm_dma_takedown(dev); - if (dev->lock.hw_lock) { - dev->sigdata.lock = dev->lock.hw_lock = NULL; /* SHM removed */ - dev->lock.file_priv = NULL; - wake_up_interruptible(&dev->lock.lock_queue); - } dev->dev_mapping = NULL; mutex_unlock(&dev->struct_mutex); @@ -403,12 +404,14 @@ static void drm_cleanup(struct drm_device * dev) DRM_DEBUG("mtrr_del=%d\n", retval); } + if (dev->driver->unload) + dev->driver->unload(dev); + + drm_ht_remove(&dev->map_hash); if (drm_core_has_AGP(dev) && dev->agp) { drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS); dev->agp = NULL; } - if (dev->driver->unload) - dev->driver->unload(dev); if (!drm_fb_loaded) pci_disable_device(dev->pdev); diff --git a/linux-core/drm_edid.c b/linux-core/drm_edid.c new file mode 100644 index 00000000..7068cef3 --- /dev/null +++ b/linux-core/drm_edid.c @@ -0,0 +1,485 @@ +/* + * 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_get_edid - get 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. + * + * Return edid data or NULL if we couldn't find any. + */ +struct edid *drm_get_edid(struct drm_output *output, + struct i2c_adapter *adapter) +{ + struct edid *edid; + + edid = (struct edid *)drm_ddc_read(adapter); + if (!edid) { + dev_warn(&output->dev->pdev->dev, "%s: no EDID data\n", + output->name); + return NULL; + } + if (!edid_valid(edid)) { + dev_warn(&output->dev->pdev->dev, "%s: EDID invalid.\n", + output->name); + kfree(edid); + return NULL; + } + return edid; +} +EXPORT_SYMBOL(drm_get_edid); + +/** + * drm_add_edid_modes - add modes from EDID data, if available + * @output: output we're probing + * @edid: edid data + * + * 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 edid *edid) +{ + int num_modes = 0; + + if (edid == NULL) { + return 0; + } + if (!edid_valid(edid)) { + dev_warn(&output->dev->pdev->dev, "%s: EDID invalid.\n", + output->name); + return 0; + } + num_modes += add_established_modes(output, edid); + num_modes += add_standard_modes(output, edid); + num_modes += add_detailed_info(output, edid); + return num_modes; +} +EXPORT_SYMBOL(drm_add_edid_modes); diff --git a/linux-core/drm_edid.h b/linux-core/drm_edid.h new file mode 100644 index 00000000..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 0e1c486c..cc09fc14 100644 --- a/linux-core/drm_fops.c +++ b/linux-core/drm_fops.c @@ -263,6 +263,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], @@ -472,6 +473,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..3763ca69 --- /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 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, mode->flags); +} +EXPORT_SYMBOL(drm_mode_debug_printmodeline); + +/** + * drm_mode_set_name - set the name on a mode + * @mode: name will be set in this mode + * + * LOCKING: + * None. + * + * Set the name of @mode to a standard format. + */ +void drm_mode_set_name(struct drm_display_mode *mode) +{ + snprintf(mode->name, DRM_DISPLAY_MODE_LEN, "%dx%d", mode->hdisplay, + mode->vdisplay); +} +EXPORT_SYMBOL(drm_mode_set_name); + +/** + * drm_mode_list_concat - move modes from one list to another + * @head: source list + * @new: dst list + * + * LOCKING: + * Caller must ensure both lists are locked. + * + * Move all the modes from @head to @new. + */ +void drm_mode_list_concat(struct list_head *head, struct list_head *new) +{ + + struct list_head *entry, *tmp; + + list_for_each_safe(entry, tmp, head) { + list_move_tail(entry, new); + } +} + +/** + * drm_mode_width - get the width of a mode + * @mode: mode + * + * LOCKING: + * None. + * + * Return @mode's width (hdisplay) value. + * + * FIXME: is this needed? + * + * RETURNS: + * @mode->hdisplay + */ +int drm_mode_width(struct drm_display_mode *mode) +{ + return mode->hdisplay; + +} +EXPORT_SYMBOL(drm_mode_width); + +/** + * drm_mode_height - get the height of a mode + * @mode: mode + * + * LOCKING: + * None. + * + * Return @mode's height (vdisplay) value. + * + * FIXME: is this needed? + * + * RETURNS: + * @mode->vdisplay + */ +int drm_mode_height(struct drm_display_mode *mode) +{ + return mode->vdisplay; +} +EXPORT_SYMBOL(drm_mode_height); + +/** + * drm_mode_vrefresh - get the vrefresh of a mode + * @mode: mode + * + * LOCKING: + * None. + * + * Return @mode's vrefresh rate or calculate it if necessary. + * + * FIXME: why is this needed? shouldn't vrefresh be set already? + * + * RETURNS: + * Vertical refresh rate of @mode x 1000. For precision reasons. + */ +int drm_mode_vrefresh(struct drm_display_mode *mode) +{ + int refresh = 0; + unsigned int calc_val; + + if (mode->vrefresh > 0) + refresh = mode->vrefresh; + else if (mode->htotal > 0 && mode->vtotal > 0) { + /* work out vrefresh the value will be x1000 */ + calc_val = (mode->clock * 1000); + + calc_val /= mode->htotal; + calc_val *= 1000; + calc_val /= mode->vtotal; + + refresh = calc_val; + if (mode->flags & V_INTERLACE) + refresh *= 2; + if (mode->flags & V_DBLSCAN) + refresh /= 2; + if (mode->vscan > 1) + refresh /= mode->vscan; + } + return refresh; +} +EXPORT_SYMBOL(drm_mode_vrefresh); + +/** + * drm_mode_set_crtcinfo - set CRTC modesetting parameters + * @p: mode + * @adjust_flags: unused? (FIXME) + * + * LOCKING: + * None. + * + * Setup the CRTC modesetting parameters for @p, adjusting if necessary. + */ +void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags) +{ + if ((p == NULL) || ((p->type & DRM_MODE_TYPE_CRTC_C) == DRM_MODE_TYPE_BUILTIN)) + return; + + p->crtc_hdisplay = p->hdisplay; + p->crtc_hsync_start = p->hsync_start; + p->crtc_hsync_end = p->hsync_end; + p->crtc_htotal = p->htotal; + p->crtc_hskew = p->hskew; + p->crtc_vdisplay = p->vdisplay; + p->crtc_vsync_start = p->vsync_start; + p->crtc_vsync_end = p->vsync_end; + p->crtc_vtotal = p->vtotal; + + if (p->flags & V_INTERLACE) { + if (adjust_flags & CRTC_INTERLACE_HALVE_V) { + p->crtc_vdisplay /= 2; + p->crtc_vsync_start /= 2; + p->crtc_vsync_end /= 2; + p->crtc_vtotal /= 2; + } + + p->crtc_vtotal |= 1; + } + + if (p->flags & V_DBLSCAN) { + p->crtc_vdisplay *= 2; + p->crtc_vsync_start *= 2; + p->crtc_vsync_end *= 2; + p->crtc_vtotal *= 2; + } + + if (p->vscan > 1) { + p->crtc_vdisplay *= p->vscan; + p->crtc_vsync_start *= p->vscan; + p->crtc_vsync_end *= p->vscan; + p->crtc_vtotal *= p->vscan; + } + + p->crtc_vblank_start = min(p->crtc_vsync_start, p->crtc_vdisplay); + p->crtc_vblank_end = max(p->crtc_vsync_end, p->crtc_vtotal); + p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay); + p->crtc_hblank_end = max(p->crtc_hsync_end, p->crtc_htotal); + + p->crtc_hadjusted = false; + p->crtc_vadjusted = false; +} +EXPORT_SYMBOL(drm_mode_set_crtcinfo); + + +/** + * drm_mode_duplicate - allocate and duplicate an existing mode + * @m: mode to duplicate + * + * LOCKING: + * None. + * + * Just allocate a new mode, copy the existing mode into it, and return + * a pointer to it. Used to create new instances of established modes. + */ +struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, + struct drm_display_mode *mode) +{ + struct drm_display_mode *nmode; + int new_id; + + nmode = drm_mode_create(dev); + if (!nmode) + return NULL; + + new_id = nmode->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 1dc61fde..394a2721 100644 --- a/linux-core/drm_objects.h +++ b/linux-core/drm_objects.h @@ -157,7 +157,6 @@ struct drm_fence_object { }; #define _DRM_FENCE_CLASSES 8 -#define _DRM_FENCE_TYPE_EXE 0x00 struct drm_fence_class_manager { struct list_head ring; @@ -486,12 +485,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); @@ -629,6 +628,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 00a24521..cc759d5c 100644 --- a/linux-core/drm_stub.c +++ b/linux-core/drm_stub.c @@ -86,9 +86,9 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev, dev->irq = pdev->irq; dev->irq_enabled = 0; - if (drm_ht_create(&dev->map_hash, DRM_MAP_HASH_ORDER)) { + if (drm_ht_create(&dev->map_hash, DRM_MAP_HASH_ORDER)) return -ENOMEM; - } + if (drm_mm_init(&dev->offset_manager, DRM_FILE_PAGE_OFFSET_START, DRM_FILE_PAGE_OFFSET_SIZE)) { drm_ht_remove(&dev->map_hash); @@ -135,7 +135,6 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev, 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 b000a725..c455a515 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 d2bcf4b5..fb4149c4 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, @@ -540,7 +541,6 @@ static struct drm_driver driver = { DRIVER_IRQ_VBL2, .load = i915_driver_load, .unload = i915_driver_unload, - .firstopen = i915_driver_firstopen, .lastclose = i915_driver_lastclose, .preclose = i915_driver_preclose, .suspend = i915_suspend, @@ -555,6 +555,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, diff --git a/linux-core/i915_fence.c b/linux-core/i915_fence.c index e3c76df6..21d032b0 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; @@ -124,7 +124,7 @@ 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..722a62c8 --- /dev/null +++ b/linux-core/intel_crt.c @@ -0,0 +1,264 @@ +/* + * 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); +} + +static bool intel_crt_set_property(struct drm_output *output, + struct drm_property *property, + uint64_t value) +{ + struct drm_device *dev = output->dev; + int i; + + if (property == dev->mode_config.dpms_property) { + intel_crt_dpms(output, (uint32_t)(value & 0xf)); + } + return true; +} + +/* + * 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, + .set_property = intel_crt_set_property, +}; + +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; + + drm_output_attach_property(output, dev->mode_config.connector_type_property, ConnectorVGA); +} 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..32c7dc31 --- /dev/null +++ b/linux-core/intel_fb.c @@ -0,0 +1,691 @@ +/* + * 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; + struct drm_display_mode *fb_mode; +}; + +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_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; +} + +bool i915_drmfb_mode_equal(struct drm_display_mode *mode1, struct drm_display_mode *mode2, unsigned int pixclock) +{ + + if (mode1->hdisplay == mode2->hdisplay && + mode1->hsync_start == mode2->hsync_start && + mode1->hsync_end == mode2->hsync_end && + mode1->htotal == mode2->htotal && + mode1->hskew == mode2->hskew && + mode1->vdisplay == mode2->vdisplay && + mode1->vsync_start == mode2->vsync_start && + mode1->vsync_end == mode2->vsync_end && + mode1->vtotal == mode2->vtotal && + mode1->vscan == mode2->vscan && + mode1->flags == mode2->flags) + { + if (mode1->clock == mode2->clock) + return true; + + if (KHZ2PICOS(mode2->clock) == pixclock) + return true; + return false; + } + + return false; +} + +/* 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, *search_mode; + struct drm_output *output; + struct fb_var_screeninfo *var = &info->var; + int found = 0; + + switch (var->bits_per_pixel) { + case 16: + fb->depth = (var->green.length == 6) ? 16 : 15; + break; + case 32: + fb->depth = (var->transp.length > 0) ? 32 : 24; + break; + default: + 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; /* ??? */ + + /* create a drm mode */ + drm_mode = drm_mode_create(dev); + drm_mode->hdisplay = var->xres; + drm_mode->hsync_start = drm_mode->hdisplay + var->right_margin; + drm_mode->hsync_end = drm_mode->hsync_start + var->hsync_len; + drm_mode->htotal = drm_mode->hsync_end + var->left_margin; + drm_mode->vdisplay = var->yres; + drm_mode->vsync_start = drm_mode->vdisplay + var->lower_margin; + drm_mode->vsync_end = drm_mode->vsync_start + var->vsync_len; + drm_mode->vtotal = drm_mode->vsync_end + var->upper_margin; + drm_mode->clock = PICOS2KHZ(var->pixclock); + drm_mode->vrefresh = drm_mode_vrefresh(drm_mode); + drm_mode->flags = 0; + drm_mode->flags |= var->sync & FB_SYNC_HOR_HIGH_ACT ? V_PHSYNC : V_NHSYNC; + drm_mode->flags |= var->sync & FB_SYNC_VERT_HIGH_ACT ? V_PVSYNC : V_NVSYNC; + + drm_mode_set_name(drm_mode); + drm_mode_set_crtcinfo(drm_mode, CRTC_INTERLACE_HALVE_V); + + list_for_each_entry(output, &dev->mode_config.output_list, head) { + if (output->crtc == par->crtc) + break; + } + + drm_mode_debug_printmodeline(dev, drm_mode); + list_for_each_entry(search_mode, &output->modes, head) { + drm_mode_debug_printmodeline(dev, search_mode); + if (i915_drmfb_mode_equal(drm_mode, search_mode, var->pixclock)) { + drm_mode_destroy(dev, drm_mode); + drm_mode = search_mode; + found = 1; + break; + } + } + + if (!found) { + if (par->fb_mode) { + drm_mode_detachmode_crtc(dev, par->fb_mode); + } + + par->fb_mode = drm_mode; + drm_mode_debug_printmodeline(dev, drm_mode); + /* attach mode */ + drm_mode_attachmode_crtc(dev, par->crtc, par->fb_mode); + } + + if (par->crtc->enabled) { + if (!drm_mode_equal(&par->crtc->mode, drm_mode)) + if (!drm_crtc_set_mode(par->crtc, drm_mode, 0, 0)) + return -EINVAL; + } + 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.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 = KHZ2PICOS(mode->clock); + + if (mode->flags & V_PHSYNC) + info->var.sync |= FB_SYNC_HOR_HIGH_ACT; + + if (mode->flags & V_PVSYNC) + info->var.sync |= FB_SYNC_VERT_HIGH_ACT; + + if (mode->flags & V_INTERLACE) + info->var.vmode = FB_VMODE_INTERLACED; + else if (mode->flags & V_DBLSCAN) + info->var.vmode = FB_VMODE_DOUBLE; + else + info->var.vmode = FB_VMODE_NONINTERLACED; + + 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..94232b94 --- /dev/null +++ b/linux-core/intel_lvds.c @@ -0,0 +1,508 @@ +/* + * 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. + */ + if (mode->hdisplay != adjusted_mode->hdisplay || + mode->vdisplay != adjusted_mode->vdisplay) + pfit_control = (PFIT_ENABLE | VERT_AUTO_SCALE | + HORIZ_AUTO_SCALE | VERT_INTERP_BILINEAR | + HORIZ_INTERP_BILINEAR); + else + pfit_control = 0; + + if (!IS_I965G(dev)) { + if (dev_priv->panel_wants_dither) + pfit_control |= PANEL_8TO6_DITHER_ENABLE; + } + else + pfit_control |= intel_crtc->pipe << PFIT_PIPE_SHIFT; + + I915_WRITE(PFIT_CONTROL, pfit_control); +} + +/** + * Detect the LVDS connection. + * + * This always returns 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: + drm_output_attach_property(output, dev->mode_config.connector_type_property, ConnectorLVDS); + 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..f8bf496c --- /dev/null +++ b/linux-core/intel_modes.c @@ -0,0 +1,63 @@ +/* + * 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; + struct edid *edid; + int ret = 0; + + edid = drm_get_edid(output, &intel_output->ddc_bus->adapter); + if (edid) { + drm_mode_output_update_edid_property(output, edid); + ret = drm_add_edid_modes(output, edid); + kfree(edid); + } + return ret; +} diff --git a/linux-core/intel_sdvo.c b/linux-core/intel_sdvo.c new file mode 100644 index 00000000..0da57faa --- /dev/null +++ b/linux-core/intel_sdvo.c @@ -0,0 +1,1094 @@ +/* + * 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; + int connector_type; + 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"; + connector_type = ConnectorVGA; + } + else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB1) + { + sdvo_priv->active_outputs = SDVO_OUTPUT_RGB1; + output->subpixel_order = SubPixelHorizontalRGB; + name_prefix="RGB"; + connector_type = ConnectorVGA; + } + else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0) + { + sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS0; + output->subpixel_order = SubPixelHorizontalRGB; + name_prefix="TMDS"; + connector_type = ConnectorDVID; + } + else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS1) + { + sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS1; + output->subpixel_order = SubPixelHorizontalRGB; + name_prefix="TMDS"; + connector_type = ConnectorDVID; + } + 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; + + drm_output_attach_property(output, dev->mode_config.connector_type_property, connector_type); +} diff --git a/linux-core/intel_sdvo_regs.h b/linux-core/intel_sdvo_regs.h new file mode 100644 index 00000000..a9d16711 --- /dev/null +++ b/linux-core/intel_sdvo_regs.h @@ -0,0 +1,328 @@ +/* + * Copyright © 2006-2007 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Eric Anholt <eric@anholt.net> + */ + +/** + * @file SDVO command definitions and structures. + */ + +#define SDVO_OUTPUT_FIRST (0) +#define SDVO_OUTPUT_TMDS0 (1 << 0) +#define SDVO_OUTPUT_RGB0 (1 << 1) +#define SDVO_OUTPUT_CVBS0 (1 << 2) +#define SDVO_OUTPUT_SVID0 (1 << 3) +#define SDVO_OUTPUT_YPRPB0 (1 << 4) +#define SDVO_OUTPUT_SCART0 (1 << 5) +#define SDVO_OUTPUT_LVDS0 (1 << 6) +#define SDVO_OUTPUT_TMDS1 (1 << 8) +#define SDVO_OUTPUT_RGB1 (1 << 9) +#define SDVO_OUTPUT_CVBS1 (1 << 10) +#define SDVO_OUTPUT_SVID1 (1 << 11) +#define SDVO_OUTPUT_YPRPB1 (1 << 12) +#define SDVO_OUTPUT_SCART1 (1 << 13) +#define SDVO_OUTPUT_LVDS1 (1 << 14) +#define SDVO_OUTPUT_LAST (14) + +struct intel_sdvo_caps { + u8 vendor_id; + u8 device_id; + u8 device_rev_id; + u8 sdvo_version_major; + u8 sdvo_version_minor; + unsigned int sdvo_inputs_mask:2; + unsigned int smooth_scaling:1; + unsigned int sharp_scaling:1; + unsigned int up_scaling:1; + unsigned int down_scaling:1; + unsigned int stall_support:1; + unsigned int pad:1; + u16 output_flags; +} __attribute__((packed)); + +/** This matches the EDID DTD structure, more or less */ +struct intel_sdvo_dtd { + struct { + u16 clock; /**< pixel clock, in 10kHz units */ + u8 h_active; /**< lower 8 bits (pixels) */ + u8 h_blank; /**< lower 8 bits (pixels) */ + u8 h_high; /**< upper 4 bits each h_active, h_blank */ + u8 v_active; /**< lower 8 bits (lines) */ + u8 v_blank; /**< lower 8 bits (lines) */ + u8 v_high; /**< upper 4 bits each v_active, v_blank */ + } part1; + + struct { + u8 h_sync_off; /**< lower 8 bits, from hblank start */ + u8 h_sync_width; /**< lower 8 bits (pixels) */ + /** lower 4 bits each vsync offset, vsync width */ + u8 v_sync_off_width; + /** + * 2 high bits of hsync offset, 2 high bits of hsync width, + * bits 4-5 of vsync offset, and 2 high bits of vsync width. + */ + u8 sync_off_width_high; + u8 dtd_flags; + u8 sdvo_flags; + /** bits 6-7 of vsync offset at bits 6-7 */ + u8 v_sync_off_high; + u8 reserved; + } part2; +} __attribute__((packed)); + +struct intel_sdvo_pixel_clock_range { + u16 min; /**< pixel clock, in 10kHz units */ + u16 max; /**< pixel clock, in 10kHz units */ +} __attribute__((packed)); + +struct intel_sdvo_preferred_input_timing_args { + u16 clock; + u16 width; + u16 height; +} __attribute__((packed)); + +/* I2C registers for SDVO */ +#define SDVO_I2C_ARG_0 0x07 +#define SDVO_I2C_ARG_1 0x06 +#define SDVO_I2C_ARG_2 0x05 +#define SDVO_I2C_ARG_3 0x04 +#define SDVO_I2C_ARG_4 0x03 +#define SDVO_I2C_ARG_5 0x02 +#define SDVO_I2C_ARG_6 0x01 +#define SDVO_I2C_ARG_7 0x00 +#define SDVO_I2C_OPCODE 0x08 +#define SDVO_I2C_CMD_STATUS 0x09 +#define SDVO_I2C_RETURN_0 0x0a +#define SDVO_I2C_RETURN_1 0x0b +#define SDVO_I2C_RETURN_2 0x0c +#define SDVO_I2C_RETURN_3 0x0d +#define SDVO_I2C_RETURN_4 0x0e +#define SDVO_I2C_RETURN_5 0x0f +#define SDVO_I2C_RETURN_6 0x10 +#define SDVO_I2C_RETURN_7 0x11 +#define SDVO_I2C_VENDOR_BEGIN 0x20 + +/* Status results */ +#define SDVO_CMD_STATUS_POWER_ON 0x0 +#define SDVO_CMD_STATUS_SUCCESS 0x1 +#define SDVO_CMD_STATUS_NOTSUPP 0x2 +#define SDVO_CMD_STATUS_INVALID_ARG 0x3 +#define SDVO_CMD_STATUS_PENDING 0x4 +#define SDVO_CMD_STATUS_TARGET_NOT_SPECIFIED 0x5 +#define SDVO_CMD_STATUS_SCALING_NOT_SUPP 0x6 + +/* SDVO commands, argument/result registers */ + +#define SDVO_CMD_RESET 0x01 + +/** Returns a struct intel_sdvo_caps */ +#define SDVO_CMD_GET_DEVICE_CAPS 0x02 + +#define SDVO_CMD_GET_FIRMWARE_REV 0x86 +# define SDVO_DEVICE_FIRMWARE_MINOR SDVO_I2C_RETURN_0 +# define SDVO_DEVICE_FIRMWARE_MAJOR SDVO_I2C_RETURN_1 +# define SDVO_DEVICE_FIRMWARE_PATCH SDVO_I2C_RETURN_2 + +/** + * Reports which inputs are trained (managed to sync). + * + * Devices must have trained within 2 vsyncs of a mode change. + */ +#define SDVO_CMD_GET_TRAINED_INPUTS 0x03 +struct intel_sdvo_get_trained_inputs_response { + unsigned int input0_trained:1; + unsigned int input1_trained:1; + unsigned int pad:6; +} __attribute__((packed)); + +/** Returns a struct intel_sdvo_output_flags of active outputs. */ +#define SDVO_CMD_GET_ACTIVE_OUTPUTS 0x04 + +/** + * Sets the current set of active outputs. + * + * Takes a struct intel_sdvo_output_flags. Must be preceded by a SET_IN_OUT_MAP + * on multi-output devices. + */ +#define SDVO_CMD_SET_ACTIVE_OUTPUTS 0x05 + +/** + * Returns the current mapping of SDVO inputs to outputs on the device. + * + * Returns two struct intel_sdvo_output_flags structures. + */ +#define SDVO_CMD_GET_IN_OUT_MAP 0x06 + +/** + * Sets the current mapping of SDVO inputs to outputs on the device. + * + * Takes two struct i380_sdvo_output_flags structures. + */ +#define SDVO_CMD_SET_IN_OUT_MAP 0x07 + +/** + * Returns a struct intel_sdvo_output_flags of attached displays. + */ +#define SDVO_CMD_GET_ATTACHED_DISPLAYS 0x0b + +/** + * Returns a struct intel_sdvo_ouptut_flags of displays supporting hot plugging. + */ +#define SDVO_CMD_GET_HOT_PLUG_SUPPORT 0x0c + +/** + * Takes a struct intel_sdvo_output_flags. + */ +#define SDVO_CMD_SET_ACTIVE_HOT_PLUG 0x0d + +/** + * Returns a struct intel_sdvo_output_flags of displays with hot plug + * interrupts enabled. + */ +#define SDVO_CMD_GET_ACTIVE_HOT_PLUG 0x0e + +#define SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE 0x0f +struct intel_sdvo_get_interrupt_event_source_response { + u16 interrupt_status; + unsigned int ambient_light_interrupt:1; + unsigned int pad:7; +} __attribute__((packed)); + +/** + * Selects which input is affected by future input commands. + * + * Commands affected include SET_INPUT_TIMINGS_PART[12], + * GET_INPUT_TIMINGS_PART[12], GET_PREFERRED_INPUT_TIMINGS_PART[12], + * GET_INPUT_PIXEL_CLOCK_RANGE, and CREATE_PREFERRED_INPUT_TIMINGS. + */ +#define SDVO_CMD_SET_TARGET_INPUT 0x10 +struct intel_sdvo_set_target_input_args { + unsigned int target_1:1; + unsigned int pad:7; +} __attribute__((packed)); + +/** + * Takes a struct intel_sdvo_output_flags of which outputs are targetted by + * future output commands. + * + * Affected commands inclue SET_OUTPUT_TIMINGS_PART[12], + * GET_OUTPUT_TIMINGS_PART[12], and GET_OUTPUT_PIXEL_CLOCK_RANGE. + */ +#define SDVO_CMD_SET_TARGET_OUTPUT 0x11 + +#define SDVO_CMD_GET_INPUT_TIMINGS_PART1 0x12 +#define SDVO_CMD_GET_INPUT_TIMINGS_PART2 0x13 +#define SDVO_CMD_SET_INPUT_TIMINGS_PART1 0x14 +#define SDVO_CMD_SET_INPUT_TIMINGS_PART2 0x15 +#define SDVO_CMD_SET_OUTPUT_TIMINGS_PART1 0x16 +#define SDVO_CMD_SET_OUTPUT_TIMINGS_PART2 0x17 +#define SDVO_CMD_GET_OUTPUT_TIMINGS_PART1 0x18 +#define SDVO_CMD_GET_OUTPUT_TIMINGS_PART2 0x19 +/* Part 1 */ +# define SDVO_DTD_CLOCK_LOW SDVO_I2C_ARG_0 +# define SDVO_DTD_CLOCK_HIGH SDVO_I2C_ARG_1 +# define SDVO_DTD_H_ACTIVE SDVO_I2C_ARG_2 +# define SDVO_DTD_H_BLANK SDVO_I2C_ARG_3 +# define SDVO_DTD_H_HIGH SDVO_I2C_ARG_4 +# define SDVO_DTD_V_ACTIVE SDVO_I2C_ARG_5 +# define SDVO_DTD_V_BLANK SDVO_I2C_ARG_6 +# define SDVO_DTD_V_HIGH SDVO_I2C_ARG_7 +/* Part 2 */ +# define SDVO_DTD_HSYNC_OFF SDVO_I2C_ARG_0 +# define SDVO_DTD_HSYNC_WIDTH SDVO_I2C_ARG_1 +# define SDVO_DTD_VSYNC_OFF_WIDTH SDVO_I2C_ARG_2 +# define SDVO_DTD_SYNC_OFF_WIDTH_HIGH SDVO_I2C_ARG_3 +# define SDVO_DTD_DTD_FLAGS SDVO_I2C_ARG_4 +# define SDVO_DTD_DTD_FLAG_INTERLACED (1 << 7) +# define SDVO_DTD_DTD_FLAG_STEREO_MASK (3 << 5) +# define SDVO_DTD_DTD_FLAG_INPUT_MASK (3 << 3) +# define SDVO_DTD_DTD_FLAG_SYNC_MASK (3 << 1) +# define SDVO_DTD_SDVO_FLAS SDVO_I2C_ARG_5 +# define SDVO_DTD_SDVO_FLAG_STALL (1 << 7) +# define SDVO_DTD_SDVO_FLAG_CENTERED (0 << 6) +# define SDVO_DTD_SDVO_FLAG_UPPER_LEFT (1 << 6) +# define SDVO_DTD_SDVO_FLAG_SCALING_MASK (3 << 4) +# define SDVO_DTD_SDVO_FLAG_SCALING_NONE (0 << 4) +# define SDVO_DTD_SDVO_FLAG_SCALING_SHARP (1 << 4) +# define SDVO_DTD_SDVO_FLAG_SCALING_SMOOTH (2 << 4) +# define SDVO_DTD_VSYNC_OFF_HIGH SDVO_I2C_ARG_6 + +/** + * Generates a DTD based on the given width, height, and flags. + * + * This will be supported by any device supporting scaling or interlaced + * modes. + */ +#define SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING 0x1a +# define SDVO_PREFERRED_INPUT_TIMING_CLOCK_LOW SDVO_I2C_ARG_0 +# define SDVO_PREFERRED_INPUT_TIMING_CLOCK_HIGH SDVO_I2C_ARG_1 +# define SDVO_PREFERRED_INPUT_TIMING_WIDTH_LOW SDVO_I2C_ARG_2 +# define SDVO_PREFERRED_INPUT_TIMING_WIDTH_HIGH SDVO_I2C_ARG_3 +# define SDVO_PREFERRED_INPUT_TIMING_HEIGHT_LOW SDVO_I2C_ARG_4 +# define SDVO_PREFERRED_INPUT_TIMING_HEIGHT_HIGH SDVO_I2C_ARG_5 +# define SDVO_PREFERRED_INPUT_TIMING_FLAGS SDVO_I2C_ARG_6 +# define SDVO_PREFERRED_INPUT_TIMING_FLAGS_INTERLACED (1 << 0) +# define SDVO_PREFERRED_INPUT_TIMING_FLAGS_SCALED (1 << 1) + +#define SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1 0x1b +#define SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2 0x1c + +/** Returns a struct intel_sdvo_pixel_clock_range */ +#define SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE 0x1d +/** Returns a struct intel_sdvo_pixel_clock_range */ +#define SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE 0x1e + +/** Returns a byte bitfield containing SDVO_CLOCK_RATE_MULT_* flags */ +#define SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS 0x1f + +/** Returns a byte containing a SDVO_CLOCK_RATE_MULT_* flag */ +#define SDVO_CMD_GET_CLOCK_RATE_MULT 0x20 +/** Takes a byte containing a SDVO_CLOCK_RATE_MULT_* flag */ +#define SDVO_CMD_SET_CLOCK_RATE_MULT 0x21 +# define SDVO_CLOCK_RATE_MULT_1X (1 << 0) +# define SDVO_CLOCK_RATE_MULT_2X (1 << 1) +# define SDVO_CLOCK_RATE_MULT_4X (1 << 3) + +#define SDVO_CMD_GET_SUPPORTED_TV_FORMATS 0x27 + +#define SDVO_CMD_GET_TV_FORMAT 0x28 + +#define SDVO_CMD_SET_TV_FORMAT 0x29 + +#define SDVO_CMD_GET_SUPPORTED_POWER_STATES 0x2a +#define SDVO_CMD_GET_ENCODER_POWER_STATE 0x2b +#define SDVO_CMD_SET_ENCODER_POWER_STATE 0x2c +# define SDVO_ENCODER_STATE_ON (1 << 0) +# define SDVO_ENCODER_STATE_STANDBY (1 << 1) +# define SDVO_ENCODER_STATE_SUSPEND (1 << 2) +# define SDVO_ENCODER_STATE_OFF (1 << 3) + +#define SDVO_CMD_SET_TV_RESOLUTION_SUPPORT 0x93 + +#define SDVO_CMD_SET_CONTROL_BUS_SWITCH 0x7a +# define SDVO_CONTROL_BUS_PROM 0x0 +# define SDVO_CONTROL_BUS_DDC1 0x1 +# define SDVO_CONTROL_BUS_DDC2 0x2 +# define SDVO_CONTROL_BUS_DDC3 0x3 + diff --git a/linux-core/radeon_buffer.c b/linux-core/radeon_buffer.c new file mode 100644 index 00000000..8e2b20b1 --- /dev/null +++ b/linux-core/radeon_buffer.c @@ -0,0 +1,263 @@ +/************************************************************************** + * + * Copyright 2007 Dave Airlie + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * + **************************************************************************/ +/* + * Authors: Dave Airlie <airlied@linux.ie> + */ + +#include "drmP.h" +#include "radeon_drm.h" +#include "radeon_drv.h" + +struct drm_ttm_backend *radeon_create_ttm_backend_entry(struct drm_device * dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + if(dev_priv->flags & RADEON_IS_AGP) + return drm_agp_init_ttm(dev); + else + return ati_pcigart_init_ttm(dev, &dev_priv->gart_info, radeon_gart_flush); +} + +int radeon_fence_types(struct drm_buffer_object *bo, uint32_t * class, uint32_t * type) +{ + *class = 0; + if (bo->mem.flags & (DRM_BO_FLAG_READ | DRM_BO_FLAG_WRITE)) + *type = 3; + else + *type = 1; + return 0; +} + +int radeon_invalidate_caches(struct drm_device * dev, uint64_t flags) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + RING_LOCALS; + + BEGIN_RING(4); + RADEON_FLUSH_CACHE(); + RADEON_FLUSH_ZCACHE(); + ADVANCE_RING(); + return 0; +} + +uint32_t radeon_evict_mask(struct drm_buffer_object *bo) +{ + switch (bo->mem.mem_type) { + case DRM_BO_MEM_LOCAL: + case DRM_BO_MEM_TT: + return DRM_BO_FLAG_MEM_LOCAL; + case DRM_BO_MEM_VRAM: + if (bo->mem.num_pages > 128) + return DRM_BO_MEM_TT; + else + return DRM_BO_MEM_LOCAL; + default: + return DRM_BO_FLAG_MEM_TT | DRM_BO_FLAG_CACHED; + } +} + +int radeon_init_mem_type(struct drm_device * dev, uint32_t type, + struct drm_mem_type_manager * man) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + switch (type) { + case DRM_BO_MEM_LOCAL: + man->flags = _DRM_FLAG_MEMTYPE_MAPPABLE | + _DRM_FLAG_MEMTYPE_CACHED; + man->drm_bus_maptype = 0; + break; + case DRM_BO_MEM_VRAM: + man->flags = _DRM_FLAG_MEMTYPE_FIXED | _DRM_FLAG_MEMTYPE_MAPPABLE | _DRM_FLAG_NEEDS_IOREMAP; + man->io_addr = NULL; + man->drm_bus_maptype = _DRM_FRAME_BUFFER; + man->io_offset = drm_get_resource_start(dev, 0); + man->io_size = drm_get_resource_len(dev, 0); + break; + case DRM_BO_MEM_TT: + if (dev_priv->flags & RADEON_IS_AGP) { + if (!(drm_core_has_AGP(dev) && dev->agp)) { + DRM_ERROR("AGP is not enabled for memory type %u\n", + (unsigned)type); + return -EINVAL; + } + man->io_offset = dev->agp->agp_info.aper_base; + man->io_size = dev->agp->agp_info.aper_size * 1024 * 1024; + man->io_addr = NULL; + man->flags = _DRM_FLAG_MEMTYPE_MAPPABLE | + _DRM_FLAG_MEMTYPE_CSELECT | _DRM_FLAG_NEEDS_IOREMAP; + man->drm_bus_maptype = _DRM_AGP; + } else { + man->io_offset = dev_priv->gart_vm_start; + man->io_size = dev_priv->gart_size; + man->io_addr = NULL; + man->flags = _DRM_FLAG_MEMTYPE_CSELECT | _DRM_FLAG_MEMTYPE_MAPPABLE | _DRM_FLAG_MEMTYPE_CMA; + man->drm_bus_maptype = _DRM_SCATTER_GATHER; + } + break; + default: + DRM_ERROR("Unsupported memory type %u\n", (unsigned)type); + return -EINVAL; + } + return 0; +} + +static void radeon_emit_copy_blit(struct drm_device * dev, + uint32_t src_offset, + uint32_t dst_offset, + uint32_t pages, int direction) +{ + uint32_t cur_pages; + uint32_t stride = PAGE_SIZE; + drm_radeon_private_t *dev_priv = dev->dev_private; + uint32_t format, height; + RING_LOCALS; + + if (!dev_priv) + return; + + /* 32-bit copy format */ + format = RADEON_COLOR_FORMAT_ARGB8888; + + /* radeon limited to 16k stride */ + stride &= 0x3fff; + while(pages > 0) { + cur_pages = pages; + if (cur_pages > 2048) + cur_pages = 2048; + pages -= cur_pages; + + /* needs verification */ + BEGIN_RING(7); + OUT_RING(CP_PACKET3(RADEON_CNTL_BITBLT_MULTI, 5)); + OUT_RING(RADEON_GMC_SRC_PITCH_OFFSET_CNTL | + RADEON_GMC_DST_PITCH_OFFSET_CNTL | + RADEON_GMC_BRUSH_NONE | + (format << 8) | + RADEON_GMC_SRC_DATATYPE_COLOR | + RADEON_ROP3_S | + RADEON_DP_SRC_SOURCE_MEMORY | + RADEON_GMC_CLR_CMP_CNTL_DIS | RADEON_GMC_WR_MSK_DIS); + if (direction) { + OUT_RING((stride << 22) | (src_offset >> 10)); + OUT_RING((stride << 22) | (dst_offset >> 10)); + } else { + OUT_RING((stride << 22) | (dst_offset >> 10)); + OUT_RING((stride << 22) | (src_offset >> 10)); + } + OUT_RING(0); + OUT_RING(pages); /* x - y */ + OUT_RING((stride << 16) | cur_pages); + ADVANCE_RING(); + } + + BEGIN_RING(2); + RADEON_WAIT_UNTIL_2D_IDLE(); + ADVANCE_RING(); + + return; +} + +static int radeon_move_blit(struct drm_buffer_object * bo, + int evict, int no_wait, struct drm_bo_mem_reg *new_mem) +{ + struct drm_bo_mem_reg *old_mem = &bo->mem; + int dir = 0; + + if ((old_mem->mem_type == new_mem->mem_type) && + (new_mem->mm_node->start < + old_mem->mm_node->start + old_mem->mm_node->size)) { + dir = 1; + } + + radeon_emit_copy_blit(bo->dev, + old_mem->mm_node->start << PAGE_SHIFT, + new_mem->mm_node->start << PAGE_SHIFT, + new_mem->num_pages, dir); + + + return drm_bo_move_accel_cleanup(bo, evict, no_wait, 0, + DRM_FENCE_TYPE_EXE | + DRM_RADEON_FENCE_TYPE_RW, + DRM_RADEON_FENCE_FLAG_FLUSHED, new_mem); +} + +static int radeon_move_flip(struct drm_buffer_object * bo, + int evict, int no_wait, struct drm_bo_mem_reg * new_mem) +{ + struct drm_device *dev = bo->dev; + struct drm_bo_mem_reg tmp_mem; + int ret; + + tmp_mem = *new_mem; + tmp_mem.mm_node = NULL; + tmp_mem.mask = DRM_BO_FLAG_MEM_TT | + DRM_BO_FLAG_CACHED | DRM_BO_FLAG_FORCE_CACHING; + + ret = drm_bo_mem_space(bo, &tmp_mem, no_wait); + if (ret) + return ret; + + ret = drm_bind_ttm(bo->ttm, &tmp_mem); + if (ret) + goto out_cleanup; + + ret = radeon_move_blit(bo, 1, no_wait, &tmp_mem); + if (ret) + goto out_cleanup; + + ret = drm_bo_move_ttm(bo, evict, no_wait, new_mem); +out_cleanup: + if (tmp_mem.mm_node) { + mutex_lock(&dev->struct_mutex); + if (tmp_mem.mm_node != bo->pinned_node) + drm_mm_put_block(tmp_mem.mm_node); + tmp_mem.mm_node = NULL; + mutex_unlock(&dev->struct_mutex); + } + return ret; +} + +int radeon_move(struct drm_buffer_object * bo, + int evict, int no_wait, struct drm_bo_mem_reg * new_mem) +{ + struct drm_bo_mem_reg *old_mem = &bo->mem; + + DRM_DEBUG("\n"); + if (old_mem->mem_type == DRM_BO_MEM_LOCAL) { + return drm_bo_move_memcpy(bo, evict, no_wait, new_mem); + } else if (new_mem->mem_type == DRM_BO_MEM_LOCAL) { + if (radeon_move_flip(bo, evict, no_wait, new_mem)) + return drm_bo_move_memcpy(bo, evict, no_wait, new_mem); + } else { + if (radeon_move_blit(bo, evict, no_wait, new_mem)) + return drm_bo_move_memcpy(bo, evict, no_wait, new_mem); + } + return 0; +} + diff --git a/linux-core/radeon_drv.c b/linux-core/radeon_drv.c index d847f3cd..2e491304 100644 --- a/linux-core/radeon_drv.c +++ b/linux-core/radeon_drv.c @@ -56,6 +56,38 @@ static struct pci_device_id pciidlist[] = { radeon_PCI_IDS }; + +#ifdef RADEON_HAVE_FENCE +static struct drm_fence_driver radeon_fence_driver = { + .num_classes = 1, + .wrap_diff = (1 << 30), + .flush_diff = (1 << 29), + .sequence_mask = 0xffffffffU, + .lazy_capable = 1, + .emit = radeon_fence_emit_sequence, + .poke_flush = radeon_poke_flush, + .has_irq = radeon_fence_has_irq, +}; +#endif +#ifdef RADEON_HAVE_BUFFER + +static uint32_t radeon_mem_prios[] = {DRM_BO_MEM_VRAM, DRM_BO_MEM_TT, DRM_BO_MEM_LOCAL}; +static uint32_t radeon_busy_prios[] = {DRM_BO_MEM_TT, DRM_BO_MEM_VRAM, DRM_BO_MEM_LOCAL}; + +static struct drm_bo_driver radeon_bo_driver = { + .mem_type_prio = radeon_mem_prios, + .mem_busy_prio = radeon_busy_prios, + .num_mem_type_prio = sizeof(radeon_mem_prios)/sizeof(uint32_t), + .num_mem_busy_prio = sizeof(radeon_busy_prios)/sizeof(uint32_t), + .create_ttm_backend_entry = radeon_create_ttm_backend_entry, + .fence_type = radeon_fence_types, + .invalidate_caches = radeon_invalidate_caches, + .init_mem_type = radeon_init_mem_type, + .evict_mask = radeon_evict_mask, + .move = radeon_move, +}; +#endif + static int probe(struct pci_dev *pdev, const struct pci_device_id *ent); static struct drm_driver driver = { .driver_features = @@ -101,6 +133,13 @@ static struct drm_driver driver = { .remove = __devexit_p(drm_cleanup_pci), }, +#ifdef RADEON_HAVE_FENCE + .fence_driver = &radeon_fence_driver, +#endif +#ifdef RADEON_HAVE_BUFFER + .bo_driver = &radeon_bo_driver, +#endif + .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = DRIVER_DATE, diff --git a/linux-core/radeon_fence.c b/linux-core/radeon_fence.c new file mode 100644 index 00000000..682f0bee --- /dev/null +++ b/linux-core/radeon_fence.c @@ -0,0 +1,125 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * + **************************************************************************/ +/* + * Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + +#include "drmP.h" +#include "drm.h" +#include "radeon_drm.h" +#include "radeon_drv.h" + +/* + * Implements an intel sync flush operation. + */ + +static void radeon_perform_flush(struct drm_device * dev) +{ + drm_radeon_private_t *dev_priv = (drm_radeon_private_t *) dev->dev_private; + struct drm_fence_manager *fm = &dev->fm; + struct drm_fence_class_manager *fc = &dev->fm.fence_class[0]; + struct drm_fence_driver *driver = dev->driver->fence_driver; + uint32_t pending_flush_types = 0; + uint32_t sequence; + + if (!dev_priv) + return; + + pending_flush_types = fc->pending_flush | + ((fc->pending_exe_flush) ? DRM_FENCE_TYPE_EXE : 0); + + if (pending_flush_types) { + sequence = READ_BREADCRUMB(dev_priv); + + drm_fence_handler(dev, 0, sequence, pending_flush_types, 0); + } + + return; +} + +void radeon_poke_flush(struct drm_device * dev, uint32_t class) +{ + struct drm_fence_manager *fm = &dev->fm; + unsigned long flags; + + if (class != 0) + return; + + write_lock_irqsave(&fm->lock, flags); + radeon_perform_flush(dev); + write_unlock_irqrestore(&fm->lock, flags); +} + +int radeon_fence_emit_sequence(struct drm_device *dev, uint32_t class, + uint32_t flags, uint32_t *sequence, + uint32_t *native_type) +{ + drm_radeon_private_t *dev_priv = (drm_radeon_private_t *) dev->dev_private; + RING_LOCALS; + + if (!dev_priv) + return -EINVAL; + + *native_type = DRM_FENCE_TYPE_EXE; + if (flags & DRM_RADEON_FENCE_FLAG_FLUSHED) { + *native_type |= DRM_RADEON_FENCE_TYPE_RW; + + BEGIN_RING(4); + + RADEON_FLUSH_CACHE(); + RADEON_FLUSH_ZCACHE(); + ADVANCE_RING(); + } + + radeon_emit_irq(dev); + *sequence = (uint32_t) dev_priv->counter; + + + return 0; +} + +void radeon_fence_handler(struct drm_device * dev) +{ + struct drm_fence_manager *fm = &dev->fm; + + write_lock(&fm->lock); + radeon_perform_flush(dev); + write_unlock(&fm->lock); +} + +int radeon_fence_has_irq(struct drm_device *dev, uint32_t class, uint32_t flags) +{ + /* + * We have an irq that tells us when we have a new breadcrumb. + */ + + if (class == 0 && flags == DRM_FENCE_TYPE_EXE) + return 1; + + return 0; +} diff --git a/linux-core/radeon_ms.h b/linux-core/radeon_ms.h new file mode 120000 index 00000000..da340c5d --- /dev/null +++ b/linux-core/radeon_ms.h @@ -0,0 +1 @@ +../shared-core/radeon_ms.h
\ No newline at end of file diff --git a/linux-core/radeon_ms_bo.c b/linux-core/radeon_ms_bo.c new file mode 120000 index 00000000..d05df59f --- /dev/null +++ b/linux-core/radeon_ms_bo.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_bo.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_bus.c b/linux-core/radeon_ms_bus.c new file mode 120000 index 00000000..50f649d0 --- /dev/null +++ b/linux-core/radeon_ms_bus.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_bus.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_compat.c b/linux-core/radeon_ms_compat.c new file mode 100644 index 00000000..6efdc786 --- /dev/null +++ b/linux-core/radeon_ms_compat.c @@ -0,0 +1,67 @@ +/* + * Copyright 2007 Dave Airlie + * Copyright 2007 Alex Deucher + * Copyright 2007 Michel Dänzer + * Copyright 2007 Roland Scheidegger + * Copyright 2007 Vladimir Dergachev + * Copyright 2007 Nicolai Haehnle + * Copyright 2007 Aapo Tahkola + * Copyright 2007 Ben Skeggs + * Copyright 2007 Jérôme Glisse + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/* + * Authors: + * Jerome Glisse <glisse@freedesktop.org> + */ +#include "drmP.h" +#include "drm.h" +#include "radeon_ms.h" + +/** + * Called whenever a 32-bit process running under a 64-bit kernel + * performs an ioctl on /dev/dri/card<n>. + + * \param filp file pointer. + * \param cmd command. + * \param arg user argument. + * \return zero on success or negative number on failure. + */ +long radeon_ms_compat_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + unsigned int nr = DRM_IOCTL_NR(cmd); + drm_ioctl_compat_t *fn = NULL; + int ret; + + if (nr < DRM_COMMAND_BASE) + return drm_compat_ioctl(filp, cmd, arg); + + lock_kernel(); + if (fn != NULL) + ret = (*fn)(filp, cmd, arg); + else + ret = drm_ioctl(filp->f_dentry->d_inode, filp, cmd, arg); + unlock_kernel(); + + return ret; +} diff --git a/linux-core/radeon_ms_cp.c b/linux-core/radeon_ms_cp.c new file mode 120000 index 00000000..6aee3e6e --- /dev/null +++ b/linux-core/radeon_ms_cp.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_cp.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_cp_mc.c b/linux-core/radeon_ms_cp_mc.c new file mode 120000 index 00000000..0ae1a647 --- /dev/null +++ b/linux-core/radeon_ms_cp_mc.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_cp_mc.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_crtc.c b/linux-core/radeon_ms_crtc.c new file mode 120000 index 00000000..31f11447 --- /dev/null +++ b/linux-core/radeon_ms_crtc.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_crtc.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_dac.c b/linux-core/radeon_ms_dac.c new file mode 120000 index 00000000..cb523cfb --- /dev/null +++ b/linux-core/radeon_ms_dac.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_dac.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_drm.c b/linux-core/radeon_ms_drm.c new file mode 120000 index 00000000..8bbf19a5 --- /dev/null +++ b/linux-core/radeon_ms_drm.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_drm.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_drm.h b/linux-core/radeon_ms_drm.h new file mode 120000 index 00000000..5d9d7319 --- /dev/null +++ b/linux-core/radeon_ms_drm.h @@ -0,0 +1 @@ +../shared-core/radeon_ms_drm.h
\ No newline at end of file diff --git a/linux-core/radeon_ms_drv.c b/linux-core/radeon_ms_drv.c new file mode 100644 index 00000000..4b52a7d7 --- /dev/null +++ b/linux-core/radeon_ms_drv.c @@ -0,0 +1,146 @@ +/* + * Copyright 2007 Jerome Glisse. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/* + * Authors: + * Jerome Glisse <glisse@freedesktop.org> + */ +#include "drm_pciids.h" +#include "radeon_ms.h" + +extern struct drm_fence_driver radeon_ms_fence_driver; +extern struct drm_bo_driver radeon_ms_bo_driver; +extern struct drm_ioctl_desc radeon_ms_ioctls[]; +extern int radeon_ms_num_ioctls; + +static int radeon_ms_driver_dri_library_name(struct drm_device * dev, + char * buf); +static int radeon_ms_driver_probe(struct pci_dev *pdev, + const struct pci_device_id *ent); + +static struct pci_device_id pciidlist[] = { + radeon_ms_PCI_IDS +}; + +static struct drm_driver driver = { + .load = radeon_ms_driver_load, + .firstopen = NULL, + .open = radeon_ms_driver_open, + .preclose = NULL, + .postclose = NULL, + .lastclose = radeon_ms_driver_lastclose, + .unload = radeon_ms_driver_unload, + .dma_ioctl = radeon_ms_driver_dma_ioctl, + .dma_ready = NULL, + .dma_quiescent = NULL, + .context_ctor = NULL, + .context_dtor = NULL, + .kernel_context_switch = NULL, + .kernel_context_switch_unlock = NULL, + .vblank_wait = NULL, + .vblank_wait2 = NULL, + .dri_library_name = radeon_ms_driver_dri_library_name, + .device_is_agp = NULL, + .irq_handler = radeon_ms_irq_handler, + .irq_preinstall = radeon_ms_irq_preinstall, + .irq_postinstall = radeon_ms_irq_postinstall, + .irq_uninstall = radeon_ms_irq_uninstall, + .reclaim_buffers = drm_core_reclaim_buffers, + .reclaim_buffers_locked = NULL, + .reclaim_buffers_idlelocked = NULL, + .get_map_ofs = drm_core_get_map_ofs, + .get_reg_ofs = drm_core_get_reg_ofs, + .set_version = NULL, + .fb_probe = radeonfb_probe, + .fb_remove = radeonfb_remove, + .fence_driver = &radeon_ms_fence_driver, + .bo_driver = &radeon_ms_bo_driver, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .driver_features = + DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG | + DRIVER_HAVE_IRQ | DRIVER_HAVE_DMA | DRIVER_IRQ_SHARED | + DRIVER_IRQ_VBL | DRIVER_IRQ_VBL2, + .dev_priv_size = 0, + .ioctls = radeon_ms_ioctls, + .num_ioctls = 0, + .fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .ioctl = drm_ioctl, + .mmap = drm_mmap, + .poll = drm_poll, + .fasync = drm_fasync, +#if defined(CONFIG_COMPAT) && LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) + .compat_ioctl = radeon_ms_compat_ioctl, +#endif + }, + .pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, + .probe = radeon_ms_driver_probe, + .remove = __devexit_p(drm_cleanup_pci), + }, +}; + +static int radeon_ms_driver_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + return drm_get_dev(pdev, ent, &driver); +} + +static int radeon_ms_driver_dri_library_name(struct drm_device * dev, + char * buf) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + int ret; + + switch (dev_priv->family) { + default: + ret = snprintf(buf, PAGE_SIZE, "\n"); + } + return ret; +} + +static void __exit radeon_ms_driver_exit(void) +{ + drm_exit(&driver); +} + +static int __init radeon_ms_driver_init(void) +{ + driver.num_ioctls = radeon_ms_num_ioctls; + return drm_init(&driver, pciidlist); +} + +module_init(radeon_ms_driver_init); +module_exit(radeon_ms_driver_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL and additional rights"); diff --git a/linux-core/radeon_ms_drv.h b/linux-core/radeon_ms_drv.h new file mode 100644 index 00000000..529f9c42 --- /dev/null +++ b/linux-core/radeon_ms_drv.h @@ -0,0 +1,45 @@ +/* + * Copyright 2007 Dave Airlie + * Copyright 2007 Alex Deucher + * Copyright 2007 Michel Dänzer + * Copyright 2007 Roland Scheidegger + * Copyright 2007 Vladimir Dergachev + * Copyright 2007 Nicolai Haehnle + * Copyright 2007 Aapo Tahkola + * Copyright 2007 Ben Skeggs + * Copyright 2007 Jérôme Glisse + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/* + * Authors: + * Jérôme Glisse <glisse@freedesktop.org> + */ +#ifndef __RADEON_MS_DRV_H__ +#define __RADEON_MS_DRV_H__ + +#include <linux/i2c.h> +#include <linux/i2c-id.h> +#include <linux/i2c-algo-bit.h> +#include "drm.h" +#include "drmP.h" + +#endif diff --git a/linux-core/radeon_ms_exec.c b/linux-core/radeon_ms_exec.c new file mode 120000 index 00000000..cb397fbf --- /dev/null +++ b/linux-core/radeon_ms_exec.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_exec.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_family.c b/linux-core/radeon_ms_family.c new file mode 120000 index 00000000..1f12e092 --- /dev/null +++ b/linux-core/radeon_ms_family.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_family.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_fb.c b/linux-core/radeon_ms_fb.c new file mode 100644 index 00000000..fc9e99ec --- /dev/null +++ b/linux-core/radeon_ms_fb.c @@ -0,0 +1,457 @@ +/* + * Copyright © 2007 David Airlie + * Copyright © 2007 Jerome Glisse + * + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation on the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT. IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR + * THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/init.h> + +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "radeon_ms.h" + +struct radeonfb_par { + struct drm_device *dev; + struct drm_crtc *crtc; + struct drm_display_mode *fb_mode; +}; + +static int radeonfb_setcolreg(unsigned regno, unsigned red, + unsigned green, unsigned blue, + unsigned transp, struct fb_info *info) +{ + struct radeonfb_par *par = info->par; + struct drm_framebuffer *fb = par->crtc->fb; + struct drm_crtc *crtc = par->crtc; + + if (regno > 255) { + return 1; + } + if (crtc->funcs->gamma_set) { + crtc->funcs->gamma_set(crtc, red, green, blue, regno); + } + if (regno < 16) { + switch (fb->depth) { + case 15: + fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) | + ((green & 0xf800) >> 6) | + ((blue & 0xf800) >> 11); + break; + case 16: + fb->pseudo_palette[regno] = (red & 0xf800) | + ((green & 0xfc00) >> 5) | + ((blue & 0xf800) >> 11); + break; + case 24: + case 32: + fb->pseudo_palette[regno] = ((red & 0xff00) << 8) | + (green & 0xff00) | + ((blue & 0xff00) >> 8); + break; + } + } + return 0; +} + +static int radeonfb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct radeonfb_par *par = info->par; + struct drm_framebuffer *fb = par->crtc->fb; + + if (!var->pixclock) + return -EINVAL; + + /* Need to resize the fb object !!! */ + if (var->xres > fb->width || var->yres > fb->height) { + DRM_ERROR("Requested width/height is greater than " + "current fb object %dx%d > %dx%d\n", + var->xres, var->yres, fb->width, fb->height); + DRM_ERROR("Need resizing code.\n"); + return -EINVAL; + } + + switch (var->bits_per_pixel) { + case 16: + if (var->green.length == 5) { + var->red.offset = 10; + var->green.offset = 5; + var->blue.offset = 0; + var->red.length = 5; + var->green.length = 5; + var->blue.length = 5; + var->transp.length = 0; + var->transp.offset = 0; + } else { + var->red.offset = 11; + var->green.offset = 6; + var->blue.offset = 0; + var->red.length = 5; + var->green.length = 6; + var->blue.length = 5; + var->transp.length = 0; + var->transp.offset = 0; + } + break; + case 32: + if (var->transp.length) { + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 8; + var->transp.offset = 24; + } else { + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 0; + var->transp.offset = 0; + } + break; + default: + return -EINVAL; + } + return 0; +} + +static bool radeonfb_mode_equal(struct drm_display_mode *mode1, + struct drm_display_mode *mode2) +{ + if (mode1->hdisplay == mode2->hdisplay && + mode1->hsync_start == mode2->hsync_start && + mode1->hsync_end == mode2->hsync_end && + mode1->htotal == mode2->htotal && + mode1->hskew == mode2->hskew && + mode1->vdisplay == mode2->vdisplay && + mode1->vsync_start == mode2->vsync_start && + mode1->vsync_end == mode2->vsync_end && + mode1->vtotal == mode2->vtotal && + mode1->vscan == mode2->vscan && + mode1->flags == mode2->flags) { + /* FIXME: what about adding a margin for clock ? */ + if (mode1->clock == mode2->clock) + return true; + return false; + } + + return false; +} + +static int radeonfb_set_par(struct fb_info *info) +{ + struct radeonfb_par *par = info->par; + struct drm_framebuffer *fb = par->crtc->fb; + struct drm_device *dev = par->dev; + struct drm_display_mode *drm_mode, *search_mode; + struct drm_output *output; + struct fb_var_screeninfo *var = &info->var; + int found = 0; + + switch (var->bits_per_pixel) { + case 16: + fb->depth = (var->green.length == 6) ? 16 : 15; + break; + case 32: + fb->depth = (var->transp.length > 0) ? 32 : 24; + break; + default: + return -EINVAL; + } + fb->bits_per_pixel = var->bits_per_pixel; + + info->fix.line_length = fb->pitch; + info->fix.smem_len = info->fix.line_length * fb->height; + info->fix.visual = FB_VISUAL_TRUECOLOR; + info->screen_size = info->fix.smem_len; /* ??? */ + + /* Should we walk the output's modelist or just create our own ??? + * For now, we create and destroy a mode based on the incoming + * parameters. But there's commented out code below which scans + * the output list too. + */ + drm_mode = drm_mode_create(dev); + drm_mode->hdisplay = var->xres; + drm_mode->hsync_start = drm_mode->hdisplay + var->right_margin; + drm_mode->hsync_end = drm_mode->hsync_start + var->hsync_len; + drm_mode->htotal = drm_mode->hsync_end + var->left_margin; + drm_mode->vdisplay = var->yres; + drm_mode->vsync_start = drm_mode->vdisplay + var->lower_margin; + drm_mode->vsync_end = drm_mode->vsync_start + var->vsync_len; + drm_mode->vtotal = drm_mode->vsync_end + var->upper_margin; + drm_mode->clock = PICOS2KHZ(var->pixclock); + drm_mode->vrefresh = drm_mode_vrefresh(drm_mode); + drm_mode_set_name(drm_mode); + drm_mode_set_crtcinfo(drm_mode, CRTC_INTERLACE_HALVE_V); + + list_for_each_entry(output, &dev->mode_config.output_list, head) { + if (output->crtc == par->crtc) + break; + } + + drm_mode_debug_printmodeline(dev, drm_mode); + list_for_each_entry(search_mode, &output->modes, head) { + drm_mode_debug_printmodeline(dev, search_mode); + if (radeonfb_mode_equal(drm_mode, search_mode)) { + drm_mode_destroy(dev, drm_mode); + drm_mode = search_mode; + found = 1; + break; + } + } + + if (!found) { + if (par->fb_mode) { + drm_mode_detachmode_crtc(dev, par->fb_mode); + } + par->fb_mode = drm_mode; + drm_mode_debug_printmodeline(dev, drm_mode); + /* attach mode */ + drm_mode_attachmode_crtc(dev, par->crtc, par->fb_mode); + } + + if (par->crtc->enabled) { + if (!drm_mode_equal(&par->crtc->mode, drm_mode)) { + if (!drm_crtc_set_mode(par->crtc, drm_mode, 0, 0)) { + return -EINVAL; + } + } + } + + return 0; +} + +static struct fb_ops radeonfb_ops = { + .owner = THIS_MODULE, + // .fb_open = radeonfb_open, + // .fb_read = radeonfb_read, + // .fb_write = radeonfb_write, + // .fb_release = radeonfb_release, + // .fb_ioctl = radeonfb_ioctl, + .fb_check_var = radeonfb_check_var, + .fb_set_par = radeonfb_set_par, + .fb_setcolreg = radeonfb_setcolreg, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +int radeonfb_probe(struct drm_device *dev, struct drm_crtc *crtc) +{ + struct fb_info *info; + struct radeonfb_par *par; + struct device *device = &dev->pdev->dev; + struct drm_framebuffer *fb; + struct drm_display_mode *mode = crtc->desired_mode; + int ret; + + info = framebuffer_alloc(sizeof(struct radeonfb_par), device); + if (!info){ + DRM_INFO("[radeon_ms] framebuffer_alloc failed\n"); + return -EINVAL; + } + + fb = drm_framebuffer_create(dev); + if (!fb) { + framebuffer_release(info); + DRM_ERROR("[radeon_ms] failed to allocate fb.\n"); + return -EINVAL; + } + crtc->fb = fb; + + fb->width = crtc->desired_mode->hdisplay; + fb->height = crtc->desired_mode->vdisplay; + fb->bits_per_pixel = 32; + fb->pitch = fb->width * ((fb->bits_per_pixel + 1) / 8); + fb->depth = 24; + /* one page alignment should be fine for constraint (micro|macro tiling, + * bit depth, color buffer offset, ...) */ + ret = drm_buffer_object_create(dev, fb->width * fb->height * 4, + drm_bo_type_kernel, + DRM_BO_FLAG_READ | + DRM_BO_FLAG_WRITE | + DRM_BO_FLAG_NO_EVICT | + DRM_BO_FLAG_MEM_VRAM, + DRM_BO_HINT_DONT_FENCE, + 1, + 0, + &fb->bo); + if (ret || fb->bo == NULL) { + DRM_ERROR("[radeon_ms] failed to allocate framebuffer\n"); + drm_framebuffer_destroy(fb); + framebuffer_release(info); + return -EINVAL; + } + + fb->offset = fb->bo->offset; + DRM_INFO("[radeon_ms] framebuffer %dx%d at 0x%08lX\n", + fb->width, fb->height, fb->bo->offset); + + fb->fbdev = info; + par = info->par; + par->dev = dev; + par->crtc = crtc; + info->fbops = &radeonfb_ops; + strcpy(info->fix.id, "radeonfb"); + info->fix.type = FB_TYPE_PACKED_PIXELS; + info->fix.visual = FB_VISUAL_TRUECOLOR; + info->fix.type_aux = 0; + info->fix.xpanstep = 8; + info->fix.ypanstep = 1; + info->fix.ywrapstep = 0; + info->fix.accel = FB_ACCEL_ATI_RADEON; + info->fix.type_aux = 0; + info->fix.mmio_start = 0; + info->fix.mmio_len = 0; + info->fix.line_length = fb->pitch; + info->fix.smem_start = fb->offset + dev->mode_config.fb_base; + info->fix.smem_len = info->fix.line_length * fb->height; + info->flags = FBINFO_DEFAULT; + DRM_INFO("[radeon_ms] fb physical start : 0x%lX\n", info->fix.smem_start); + DRM_INFO("[radeon_ms] fb physical size : %d\n", info->fix.smem_len); + + ret = drm_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 15: + info->var.red.offset = 10; + info->var.green.offset = 5; + info->var.blue.offset = 0; + info->var.red.length = info->var.green.length = + info->var.blue.length = 5; + info->var.transp.offset = 15; + info->var.transp.length = 1; + break; + case 16: + info->var.red.offset = 11; + info->var.green.offset = 5; + info->var.blue.offset = 0; + info->var.red.length = 5; + info->var.green.length = 6; + info->var.blue.length = 5; + info->var.transp.offset = 0; + break; + case 24: + info->var.red.offset = 16; + info->var.green.offset = 8; + info->var.blue.offset = 0; + info->var.red.length = info->var.green.length = + info->var.blue.length = 8; + info->var.transp.offset = 0; + info->var.transp.length = 0; + break; + case 32: + info->var.red.offset = 16; + info->var.green.offset = 8; + info->var.blue.offset = 0; + info->var.red.length = info->var.green.length = + info->var.blue.length = 8; + info->var.transp.offset = 24; + info->var.transp.length = 8; + break; + default: + DRM_ERROR("only support 15, 16, 24 or 32bits per pixel " + "got %d\n", fb->depth); + return -EINVAL; + break; + } + + if (register_framebuffer(info) < 0) { + return -EINVAL; + } + + DRM_INFO("[radeon_ms] fb%d: %s frame buffer device\n", info->node, + info->fix.id); + return 0; +} +EXPORT_SYMBOL(radeonfb_probe); + +int radeonfb_remove(struct drm_device *dev, struct drm_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(radeonfb_remove); +MODULE_LICENSE("GPL"); diff --git a/linux-core/radeon_ms_fence.c b/linux-core/radeon_ms_fence.c new file mode 120000 index 00000000..383cc070 --- /dev/null +++ b/linux-core/radeon_ms_fence.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_fence.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_gpu.c b/linux-core/radeon_ms_gpu.c new file mode 120000 index 00000000..fa5e05bf --- /dev/null +++ b/linux-core/radeon_ms_gpu.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_gpu.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_i2c.c b/linux-core/radeon_ms_i2c.c new file mode 120000 index 00000000..1863e6d6 --- /dev/null +++ b/linux-core/radeon_ms_i2c.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_i2c.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_irq.c b/linux-core/radeon_ms_irq.c new file mode 120000 index 00000000..c4e60ba6 --- /dev/null +++ b/linux-core/radeon_ms_irq.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_irq.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_output.c b/linux-core/radeon_ms_output.c new file mode 120000 index 00000000..6a38b671 --- /dev/null +++ b/linux-core/radeon_ms_output.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_output.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_reg.h b/linux-core/radeon_ms_reg.h new file mode 120000 index 00000000..24b01b42 --- /dev/null +++ b/linux-core/radeon_ms_reg.h @@ -0,0 +1 @@ +../shared-core/radeon_ms_reg.h
\ No newline at end of file diff --git a/linux-core/radeon_ms_state.c b/linux-core/radeon_ms_state.c new file mode 120000 index 00000000..2d2e2ef7 --- /dev/null +++ b/linux-core/radeon_ms_state.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_state.c
\ No newline at end of file diff --git a/shared-core/drm.h b/shared-core/drm.h index ec07b895..9186b64b 100644 --- a/shared-core/drm.h +++ b/shared-core/drm.h @@ -891,6 +891,133 @@ 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 +#define DRM_PROP_NAME_LEN 32 + +#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) + +struct drm_mode_modeinfo { + 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; + unsigned int type; + char name[DRM_DISPLAY_MODE_LEN]; +}; + +struct drm_mode_card_res { + uint64_t fb_id_ptr; + uint64_t crtc_id_ptr; + uint64_t output_id_ptr; + int count_fbs; + int count_crtcs; + int count_outputs; + int min_width, max_width; + int min_height, max_height; +}; + +struct drm_mode_crtc { + uint64_t set_outputs_ptr; + + unsigned int crtc_id; /**< Id */ + unsigned int fb_id; /**< Id of framebuffer */ + + int x, y; /**< Position on the frameuffer */ + + int count_outputs; + unsigned int outputs; /**< Outputs that are connected */ + + int count_possibles; + unsigned int possibles; /**< Outputs that can be connected */ + int gamma_size; + int mode_valid; + struct drm_mode_modeinfo mode; +}; + +struct drm_mode_get_output { + + uint64_t modes_ptr; + uint64_t props_ptr; + uint64_t prop_values_ptr; + + int count_modes; + int count_props; + 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; + int count_clones; + unsigned int crtcs; /**< possible crtc to connect to */ + unsigned int clones; /**< list of clones */ +}; + +#define DRM_MODE_PROP_PENDING (1<<0) +#define DRM_MODE_PROP_RANGE (1<<1) +#define DRM_MODE_PROP_IMMUTABLE (1<<2) +#define DRM_MODE_PROP_ENUM (1<<3) // enumerated type with text strings +#define DRM_MODE_PROP_BLOB (1<<4) + +struct drm_mode_property_enum { + uint64_t value; + unsigned char name[DRM_PROP_NAME_LEN]; +}; + +struct drm_mode_get_property { + uint64_t values_ptr; /* values and blob lengths */ + uint64_t enum_blob_ptr; /* enum and blob id ptrs */ + + unsigned int prop_id; + unsigned int flags; + unsigned char name[DRM_PROP_NAME_LEN]; + + int count_values; + int count_enum_blobs; +}; + +struct drm_mode_output_set_property { + uint64_t value; + unsigned int prop_id; + unsigned int output_id; +}; + +struct drm_mode_get_blob { + uint32_t blob_id; + uint32_t length; + uint64_t data; +}; + +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; + struct drm_mode_modeinfo mode; +}; + /** * \name Ioctls Definitions */ @@ -983,6 +1110,20 @@ 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_SETPROPERTY DRM_IOWR(0xA7, struct drm_mode_output_set_property) +#define DRM_IOCTL_MODE_GETPROPBLOB DRM_IOWR(0xA8, struct drm_mode_get_blob) +#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) + +#define DRM_IOCTL_MODE_GETPROPERTY DRM_IOWR(0xAB, struct drm_mode_get_property) /*@}*/ /** diff --git a/shared-core/drm_pciids.txt b/shared-core/drm_pciids.txt index 05d32f2e..a76413d4 100644 --- a/shared-core/drm_pciids.txt +++ b/shared-core/drm_pciids.txt @@ -1,3 +1,7 @@ +[radeon_ms] +0x1002 0x4150 CHIP_RV350|RADEON_AGP "ATI Radeon RV350 9600" +0x1002 0x5b63 CHIP_RV370|RADEON_PCIE "ATI Radeon RV370 X550" + [radeon] 0x1002 0x3150 CHIP_RV380|RADEON_IS_MOBILITY "ATI Radeon Mobility X600 M24" 0x1002 0x3152 CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP "ATI Radeon Mobility X300 M24" diff --git a/shared-core/i915_dma.c b/shared-core/i915_dma.c index 42114beb..a6684ce0 100644 --- a/shared-core/i915_dma.c +++ b/shared-core/i915_dma.c @@ -38,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; @@ -63,8 +63,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; @@ -73,9 +73,8 @@ void i915_kernel_lost_context(struct drm_device * dev) ring->space += ring->Size; } -static int i915_dma_cleanup(struct drm_device * dev) +int i915_dma_cleanup(struct drm_device * dev) { - drm_i915_private_t *dev_priv = 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. @@ -83,32 +82,12 @@ static int i915_dma_cleanup(struct drm_device * dev) if (dev->irq) drm_irq_uninstall(dev); - if (dev_priv->ring.virtual_start) { - drm_core_ioremapfree(&dev_priv->ring.map, dev); - dev_priv->ring.virtual_start = 0; - dev_priv->ring.map.handle = 0; - dev_priv->ring.map.size = 0; - } - - if (dev_priv->status_page_dmah) { - drm_pci_free(dev, dev_priv->status_page_dmah); - dev_priv->status_page_dmah = NULL; - /* 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); - } - return 0; } static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; dev_priv->sarea = drm_getsarea(dev); if (!dev_priv->sarea) { @@ -197,7 +176,7 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init) 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("\n"); @@ -236,7 +215,7 @@ 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_init_t *init = data; + struct drm_i915_init *init = data; int retcode = 0; switch (init->func) { @@ -336,7 +315,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; @@ -377,7 +356,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; @@ -418,7 +397,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) { @@ -439,7 +418,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; @@ -459,10 +438,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; @@ -499,7 +478,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; @@ -552,7 +531,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; @@ -604,7 +583,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("planes=0x%x pfCurrentPage=%d\n", @@ -625,7 +604,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__); @@ -643,7 +622,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; @@ -673,10 +652,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", @@ -983,11 +962,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; @@ -1086,14 +1065,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; - int i, planes, num_pages = dev_priv->sarea_priv->third_handle ? 3 : 2; + struct drm_i915_private *dev_priv = dev->dev_private; + int i, planes, num_pages; DRM_DEBUG("\n"); - - for (i = 0, planes = 0; i < 2; i++) + num_pages = dev_priv->sarea_priv->third_handle ? 3 : 2; + 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 & @@ -1101,6 +1080,7 @@ static int i915_do_cleanup_pageflip(struct drm_device * dev) planes |= 1 << i; } + } if (planes) i915_dispatch_flip(dev, planes, 0); @@ -1110,7 +1090,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("\n"); @@ -1132,8 +1112,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) { @@ -1167,7 +1147,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) { @@ -1208,8 +1188,8 @@ 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; - drm_i915_mmio_entry_t *e; + struct drm_i915_private *dev_priv = dev->dev_private; + drm_i915_mmio_entry_t *e; drm_i915_mmio_t *mmio = data; void __iomem *base; int i; @@ -1254,7 +1234,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) { @@ -1282,85 +1262,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) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - unsigned long base, size; - int ret = 0, mmio_bar = IS_I9XX(dev) ? 0 : 1; - - /* 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; - - dev_priv = drm_alloc(sizeof(drm_i915_private_t), DRM_MEM_DRIVER); - if (dev_priv == NULL) - return -ENOMEM; - - memset(dev_priv, 0, sizeof(drm_i915_private_t)); - - dev->dev_private = (void *)dev_priv; - - /* Add register map (needed for suspend/resume) */ - base = drm_get_resource_start(dev, mmio_bar); - size = drm_get_resource_len(dev, mmio_bar); - - ret = drm_addmap(dev, base, size, _DRM_REGISTERS, - _DRM_KERNEL | _DRM_DRIVER, &dev_priv->mmio_map); - -#ifdef __linux__ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) - intel_init_chipset_flush_compat(dev); -#endif -#endif - - return ret; -} - -int i915_driver_unload(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - - if (dev_priv->mmio_map) - drm_rmmap(dev, dev_priv->mmio_map); - - drm_free(dev->dev_private, sizeof(drm_i915_private_t), - DRM_MEM_DRIVER); -#ifdef __linux__ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) - intel_fini_chipset_flush_compat(dev); -#endif -#endif - return 0; -} - -void i915_driver_lastclose(struct drm_device * dev) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - - if (drm_getsarea(dev) && dev_priv->sarea_priv) - i915_do_cleanup_pageflip(dev); - if (dev_priv->agp_heap) - i915_mem_takedown(&(dev_priv->agp_heap)); - - i915_dma_cleanup(dev); -} - -void i915_driver_preclose(struct drm_device * dev, struct drm_file *file_priv) -{ - 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), @@ -1403,10 +1311,3 @@ 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 cfa3f93a..c5819953 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 */ @@ -196,7 +196,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 */ @@ -208,7 +208,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 */ @@ -359,7 +359,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; drm_context_t context; /* for lockless use in the future */ struct drm_fence_arg fence_arg; }; diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index cb336659..9382fa6f 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,15 @@ 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; - /* Register state */ + /* LVDS info */ + int backlight_duty_cycle; /* restore backlight to this value */ + bool panel_wants_dither; + struct drm_display_mode *panel_fixed_mode; + + /* Register state */ u8 saveLBB; u32 saveDSPACNTR; u32 saveDSPBCNTR; @@ -224,7 +233,7 @@ typedef struct drm_i915_private { u8 saveDACMASK; u8 saveDACDATA[256*3]; /* 256 3-byte colors */ u8 saveCR[36]; -} drm_i915_private_t; +}; enum intel_chip_family { CHIP_I8XX = 0x01, @@ -239,7 +248,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 *); +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); @@ -250,6 +259,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, @@ -257,6 +268,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); @@ -267,9 +279,10 @@ extern int i915_vblank_pipe_set(struct drm_device *dev, void *data, struct drm_file *file_priv); 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 int i915_emit_irq(struct drm_device * dev); +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); @@ -320,6 +333,12 @@ extern void intel_fini_chipset_flush_compat(struct drm_device *dev); #endif #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)) @@ -357,8 +376,31 @@ extern void intel_fini_chipset_flush_compat(struct drm_device *dev); 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) + /* Extended config space */ #define LBB 0xf4 @@ -425,6 +467,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 + /* Framebuffer compression */ #define FBC_CFB_BASE 0x03200 /* 4k page aligned */ #define FBC_LL_BASE 0x03204 /* 4k page aligned */ @@ -475,6 +519,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 @@ -483,6 +584,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 @@ -639,8 +742,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) @@ -682,6 +791,7 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); #define BACKLIGHT_MODULATION_FREQ_SHIFT (17) #define BLC_PWM_CTL2 0x61250 + /** * This is the most significant 15 bits of the number of backlight cycles in a * complete cycle of the modulated backlight control. @@ -1121,13 +1231,17 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); */ #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 SWF10 0x70410 - #define SWF30 0x72414 +#define SWF31 0x72418 +#define SWF32 0x7241c /* * Overlay registers. These are overlay registers accessed via MMIO. @@ -1144,6 +1258,7 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); #define OGAMC2 0x3001c #define OGAMC1 0x30020 #define OGAMC0 0x30024 + /* * Palette registers */ 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 ee7c40b5..4312eae2 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; @@ -367,7 +367,8 @@ 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); @@ -384,7 +385,7 @@ int i915_emit_irq(struct drm_device *dev) return dev_priv->counter; } -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)){ @@ -394,8 +395,8 @@ void i915_user_irq_on(drm_i915_private_t *dev_priv) DRM_SPINUNLOCK(&dev_priv->user_irq_lock); } - -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)) { @@ -408,7 +409,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, @@ -436,7 +437,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; @@ -454,6 +455,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) { atomic_t *counter; @@ -482,8 +496,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); @@ -508,8 +522,8 @@ int i915_irq_emit(struct drm_device *dev, void *data, 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__); @@ -519,11 +533,12 @@ 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; - dev_priv->irq_enable_reg = USER_INT_FLAG; if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A) dev_priv->irq_enable_reg |= VSYNC_PIPEA_FLAG; if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B) @@ -538,8 +553,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__); @@ -562,8 +577,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) { @@ -587,9 +602,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; @@ -681,7 +696,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 && @@ -731,7 +746,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); @@ -740,7 +755,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); @@ -761,7 +776,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) diff --git a/shared-core/i915_mem.c b/shared-core/i915_mem.c index d360896f..4c7ced24 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/shared-core/radeon_cp.c b/shared-core/radeon_cp.c index fc1fe07a..c2863346 100644 --- a/shared-core/radeon_cp.c +++ b/shared-core/radeon_cp.c @@ -1405,6 +1405,28 @@ static void radeon_set_pcigart(drm_radeon_private_t * dev_priv, int on) } } +void radeon_gart_flush(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + if (dev_priv->flags & RADEON_IS_IGPGART) { + RADEON_READ_IGPGART(dev_priv, RADEON_IGPGART_FLUSH); + RADEON_WRITE_IGPGART(RADEON_IGPGART_FLUSH, 0x1); + RADEON_READ_IGPGART(dev_priv, RADEON_IGPGART_FLUSH); + RADEON_WRITE_IGPGART(RADEON_IGPGART_FLUSH, 0x0); + } else if (dev_priv->flags & RADEON_IS_PCIE) { + u32 tmp = RADEON_READ_PCIE(dev_priv, RADEON_PCIE_TX_GART_CNTL); + tmp |= RADEON_PCIE_TX_GART_INVALIDATE_TLB; + RADEON_WRITE_PCIE(RADEON_PCIE_TX_GART_CNTL, tmp); + tmp &= ~RADEON_PCIE_TX_GART_INVALIDATE_TLB; + RADEON_WRITE_PCIE(RADEON_PCIE_TX_GART_CNTL, tmp); + } else { + + + } + +} + static int radeon_do_init_cp(struct drm_device * dev, drm_radeon_init_t * init) { drm_radeon_private_t *dev_priv = dev->dev_private; @@ -2316,6 +2338,9 @@ int radeon_driver_firstopen(struct drm_device *dev) if (ret != 0) return ret; +#ifdef RADEON_HAVE_BUFFER + drm_bo_driver_init(dev); +#endif return 0; } diff --git a/shared-core/radeon_drm.h b/shared-core/radeon_drm.h index 0971f970..67536c26 100644 --- a/shared-core/radeon_drm.h +++ b/shared-core/radeon_drm.h @@ -434,8 +434,17 @@ typedef struct { int pfCurrentPage; /* which buffer is being displayed? */ int crtc2_base; /* CRTC2 frame offset */ int tiling_enabled; /* set by drm, read by 2d + 3d clients */ + + unsigned int last_fence; } drm_radeon_sarea_t; +/* The only fence class we support */ +#define DRM_RADEON_FENCE_CLASS_ACCEL 0 +/* Fence type that guarantees read-write flush */ +#define DRM_RADEON_FENCE_TYPE_RW 2 +/* cache flushes programmed just before the fence */ +#define DRM_RADEON_FENCE_FLAG_FLUSHED 0x01000000 + /* WARNING: If you change any of these defines, make sure to change the * defines in the Xserver file (xf86drmRadeon.h) * diff --git a/shared-core/radeon_drv.h b/shared-core/radeon_drv.h index fcbdc2e2..37b9c43f 100644 --- a/shared-core/radeon_drv.h +++ b/shared-core/radeon_drv.h @@ -104,6 +104,11 @@ #define DRIVER_MINOR 28 #define DRIVER_PATCHLEVEL 0 +#if defined(__linux__) +#define RADEON_HAVE_FENCE +#define RADEON_HAVE_BUFFER +#endif + /* * Radeon chip families */ @@ -284,8 +289,9 @@ typedef struct drm_radeon_private { struct mem_block *fb_heap; /* SW interrupt */ - wait_queue_head_t swi_queue; - atomic_t swi_emitted; + wait_queue_head_t irq_queue; + int counter; + int vblank_crtc; uint32_t irq_enable_reg; int irq_enabled; @@ -345,6 +351,7 @@ extern int radeon_cp_resume(struct drm_device *dev, void *data, struct drm_file extern int radeon_engine_reset(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int radeon_fullscreen(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int radeon_cp_buffers(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern void radeon_gart_flush(struct drm_device *dev); extern u32 radeon_read_fb_location(drm_radeon_private_t *dev_priv); extern void radeon_freelist_reset(struct drm_device * dev); @@ -364,6 +371,7 @@ extern void radeon_mem_release(struct drm_file *file_priv, /* radeon_irq.c */ extern int radeon_irq_emit(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int radeon_irq_wait(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int radeon_emit_irq(struct drm_device * dev); extern void radeon_do_release(struct drm_device * dev); extern int radeon_driver_vblank_wait(struct drm_device * dev, @@ -397,6 +405,30 @@ extern int r300_do_cp_cmdbuf(struct drm_device *dev, struct drm_file *file_priv, drm_radeon_kcmd_buffer_t *cmdbuf); + +#ifdef RADEON_HAVE_FENCE +/* i915_fence.c */ + + +extern void radeon_fence_handler(struct drm_device *dev); +extern int radeon_fence_emit_sequence(struct drm_device *dev, uint32_t class, + uint32_t flags, uint32_t *sequence, + uint32_t *native_type); +extern void radeon_poke_flush(struct drm_device *dev, uint32_t class); +extern int radeon_fence_has_irq(struct drm_device *dev, uint32_t class, uint32_t flags); +#endif + +#ifdef RADEON_HAVE_BUFFER +/* radeon_buffer.c */ +extern struct drm_ttm_backend *radeon_create_ttm_backend_entry(struct drm_device *dev); +extern int radeon_fence_types(struct drm_buffer_object *bo, uint32_t *class, uint32_t *type); +extern int radeon_invalidate_caches(struct drm_device *dev, uint64_t buffer_flags); +extern uint32_t radeon_evict_mask(struct drm_buffer_object *bo); +extern int radeon_init_mem_type(struct drm_device * dev, uint32_t type, + struct drm_mem_type_manager * man); +extern int radeon_move(struct drm_buffer_object * bo, + int evict, int no_wait, struct drm_bo_mem_reg * new_mem); +#endif /* Flags for stats.boxes */ #define RADEON_BOX_DMA_IDLE 0x1 @@ -1268,4 +1300,19 @@ do { \ write &= mask; \ } while (0) +/* Breadcrumb - swi irq */ +#define READ_BREADCRUMB(dev_priv) RADEON_READ(RADEON_LAST_SWI_REG) + +static inline int radeon_update_breadcrumb(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + dev_priv->sarea_priv->last_fence = ++dev_priv->counter; + + if (dev_priv->counter > 0x7FFFFFFFUL) + dev_priv->sarea_priv->last_fence = dev_priv->counter = 1; + + return dev_priv->counter; +} + #endif /* __RADEON_DRV_H__ */ diff --git a/shared-core/radeon_irq.c b/shared-core/radeon_irq.c index 2b2407ee..544e3a0f 100644 --- a/shared-core/radeon_irq.c +++ b/shared-core/radeon_irq.c @@ -82,7 +82,10 @@ irqreturn_t radeon_driver_irq_handler(DRM_IRQ_ARGS) /* SW interrupt */ if (stat & RADEON_SW_INT_TEST) { - DRM_WAKEUP(&dev_priv->swi_queue); + DRM_WAKEUP(&dev_priv->irq_queue); +#ifdef RADEON_HAVE_FENCE + radeon_fence_handler(dev); +#endif } /* VBLANK interrupt */ @@ -109,14 +112,13 @@ irqreturn_t radeon_driver_irq_handler(DRM_IRQ_ARGS) return IRQ_HANDLED; } -static int radeon_emit_irq(struct drm_device * dev) +int radeon_emit_irq(struct drm_device * dev) { drm_radeon_private_t *dev_priv = dev->dev_private; unsigned int ret; RING_LOCALS; - atomic_inc(&dev_priv->swi_emitted); - ret = atomic_read(&dev_priv->swi_emitted); + ret = radeon_update_breadcrumb(dev); BEGIN_RING(4); OUT_RING_REG(RADEON_LAST_SWI_REG, ret); @@ -133,13 +135,13 @@ static int radeon_wait_irq(struct drm_device * dev, int swi_nr) (drm_radeon_private_t *) dev->dev_private; int ret = 0; - if (RADEON_READ(RADEON_LAST_SWI_REG) >= swi_nr) + if (READ_BREADCRUMB(dev_priv) >= swi_nr) return 0; dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE; - DRM_WAIT_ON(ret, dev_priv->swi_queue, 3 * DRM_HZ, - RADEON_READ(RADEON_LAST_SWI_REG) >= swi_nr); + DRM_WAIT_ON(ret, dev_priv->irq_queue, 3 * DRM_HZ, + READ_BREADCRUMB(dev_priv) >= swi_nr); return ret; } @@ -270,8 +272,8 @@ void radeon_driver_irq_postinstall(struct drm_device * dev) drm_radeon_private_t *dev_priv = (drm_radeon_private_t *) dev->dev_private; - atomic_set(&dev_priv->swi_emitted, 0); - DRM_INIT_WAITQUEUE(&dev_priv->swi_queue); + dev_priv->counter = 0; + DRM_INIT_WAITQUEUE(&dev_priv->irq_queue); radeon_enable_interrupt(dev); } diff --git a/shared-core/radeon_ms.h b/shared-core/radeon_ms.h new file mode 100644 index 00000000..ee795f3a --- /dev/null +++ b/shared-core/radeon_ms.h @@ -0,0 +1,607 @@ +/* + * Copyright 2007 Jérôme Glisse + * Copyright 2007 Dave Airlie + * Copyright 2007 Alex Deucher + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/* + * Authors: + * Jérôme Glisse <glisse@freedesktop.org> + */ +#ifndef __RADEON_MS_H__ +#define __RADEON_MS_H__ + +#include "radeon_ms_drv.h" +#include "radeon_ms_reg.h" +#include "radeon_ms_drm.h" + +#define DRIVER_AUTHOR "Jerome Glisse, Dave Airlie, Gareth Hughes, "\ + "Keith Whitwell, others." +#define DRIVER_NAME "radeon_ms" +#define DRIVER_DESC "radeon kernel modesetting" +#define DRIVER_DATE "20071108" +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 +#define DRIVER_PATCHLEVEL 0 + +#define RADEON_PAGE_SIZE 4096 +#define RADEON_MAX_CONNECTORS 8 +#define RADEON_MAX_OUTPUTS 8 + +enum radeon_bus_type { + RADEON_PCI = 0x10000, + RADEON_AGP = 0x20000, + RADEON_PCIE = 0x30000, +}; + +enum radeon_family { + CHIP_R100, + CHIP_RV100, + CHIP_RS100, + CHIP_RV200, + CHIP_RS200, + CHIP_R200, + CHIP_RV250, + CHIP_RS300, + CHIP_RV280, + CHIP_R300, + CHIP_R350, + CHIP_R360, + CHIP_RV350, + CHIP_RV370, + CHIP_RV380, + CHIP_RS400, + CHIP_RV410, + CHIP_R420, + CHIP_R430, + CHIP_R480, + CHIP_LAST, +}; + +enum radeon_monitor_type { + MT_UNKNOWN = -1, + MT_NONE = 0, + MT_CRT = 1, + MT_LCD = 2, + MT_DFP = 3, + MT_CTV = 4, + MT_STV = 5 +}; + +enum radeon_connector_type { + CONNECTOR_NONE, + CONNECTOR_PROPRIETARY, + CONNECTOR_VGA, + CONNECTOR_DVI_I, + CONNECTOR_DVI_D, + CONNECTOR_CTV, + CONNECTOR_STV, + CONNECTOR_UNSUPPORTED +}; + +enum radeon_output_type { + OUTPUT_NONE, + OUTPUT_DAC1, + OUTPUT_DAC2, + OUTPUT_TMDS, + OUTPUT_LVDS +}; + +struct radeon_state; + +struct radeon_ms_crtc { + int crtc; + uint16_t lut_r[256]; + uint16_t lut_g[256]; + uint16_t lut_b[256]; +}; + +struct radeon_ms_i2c { + struct drm_device *drm_dev; + uint32_t reg; + struct i2c_adapter adapter; + struct i2c_algo_bit_data algo; +}; + +struct radeon_ms_connector { + struct radeon_ms_i2c *i2c; + struct edid *edid; + struct drm_output *output; + int type; + int monitor_type; + int crtc; + uint32_t i2c_reg; + char outputs[RADEON_MAX_OUTPUTS]; + char name[32]; +}; + +struct radeon_ms_output { + int type; + struct drm_device *dev; + struct radeon_ms_connector *connector; + int (*initialize)(struct radeon_ms_output *output); + enum drm_output_status (*detect)(struct radeon_ms_output *output); + void (*dpms)(struct radeon_ms_output *output, int mode); + int (*get_modes)(struct radeon_ms_output *output); + bool (*mode_fixup)(struct radeon_ms_output *output, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + int (*mode_set)(struct radeon_ms_output *output, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + void (*restore)(struct radeon_ms_output *output, + struct radeon_state *state); + void (*save)(struct radeon_ms_output *output, + struct radeon_state *state); +}; + +struct radeon_ms_properties { + uint16_t subvendor; + uint16_t subdevice; + int16_t pll_reference_freq; + int32_t pll_min_pll_freq; + int32_t pll_max_pll_freq; + char pll_use_bios; + char pll_dummy_reads; + char pll_delay; + char pll_r300_errata; + struct radeon_ms_output *outputs[RADEON_MAX_OUTPUTS]; + struct radeon_ms_connector *connectors[RADEON_MAX_CONNECTORS]; +}; + +struct radeon_state { + /* memory */ + uint32_t config_aper_0_base; + uint32_t config_aper_1_base; + uint32_t config_aper_size; + uint32_t mc_fb_location; + uint32_t display_base_addr; + /* irq */ + uint32_t gen_int_cntl; + /* pci */ + uint32_t aic_ctrl; + uint32_t aic_pt_base; + uint32_t aic_pt_base_lo; + uint32_t aic_pt_base_hi; + uint32_t aic_lo_addr; + uint32_t aic_hi_addr; + /* agp */ + uint32_t agp_cntl; + uint32_t agp_command; + uint32_t agp_base; + uint32_t agp_base_2; + uint32_t bus_cntl; + uint32_t mc_agp_location; + /* cp */ + uint32_t cp_rb_cntl; + uint32_t cp_rb_base; + uint32_t cp_rb_rptr_addr; + uint32_t cp_rb_wptr; + uint32_t cp_rb_wptr_delay; + uint32_t scratch_umsk; + uint32_t scratch_addr; + /* pcie */ + uint32_t pcie_tx_gart_cntl; + uint32_t pcie_tx_gart_discard_rd_addr_lo; + uint32_t pcie_tx_gart_discard_rd_addr_hi; + uint32_t pcie_tx_gart_base; + uint32_t pcie_tx_gart_start_lo; + uint32_t pcie_tx_gart_start_hi; + uint32_t pcie_tx_gart_end_lo; + uint32_t pcie_tx_gart_end_hi; + /* surface */ + uint32_t surface_cntl; + uint32_t surface0_info; + uint32_t surface0_lower_bound; + uint32_t surface0_upper_bound; + uint32_t surface1_info; + uint32_t surface1_lower_bound; + uint32_t surface1_upper_bound; + uint32_t surface2_info; + uint32_t surface2_lower_bound; + uint32_t surface2_upper_bound; + uint32_t surface3_info; + uint32_t surface3_lower_bound; + uint32_t surface3_upper_bound; + uint32_t surface4_info; + uint32_t surface4_lower_bound; + uint32_t surface4_upper_bound; + uint32_t surface5_info; + uint32_t surface5_lower_bound; + uint32_t surface5_upper_bound; + uint32_t surface6_info; + uint32_t surface6_lower_bound; + uint32_t surface6_upper_bound; + uint32_t surface7_info; + uint32_t surface7_lower_bound; + uint32_t surface7_upper_bound; + /* crtc */ + uint32_t crtc_gen_cntl; + uint32_t crtc_ext_cntl; + uint32_t crtc_h_total_disp; + uint32_t crtc_h_sync_strt_wid; + uint32_t crtc_v_total_disp; + uint32_t crtc_v_sync_strt_wid; + uint32_t crtc_offset; + uint32_t crtc_offset_cntl; + uint32_t crtc_pitch; + uint32_t crtc_more_cntl; + uint32_t crtc_tile_x0_y0; + uint32_t fp_h_sync_strt_wid; + uint32_t fp_v_sync_strt_wid; + uint32_t fp_crtc_h_total_disp; + uint32_t fp_crtc_v_total_disp; + /* pll */ + uint32_t clock_cntl_index; + uint32_t ppll_cntl; + uint32_t ppll_ref_div; + uint32_t ppll_div_0; + uint32_t ppll_div_1; + uint32_t ppll_div_2; + uint32_t ppll_div_3; + uint32_t vclk_ecp_cntl; + uint32_t htotal_cntl; + /* dac */ + uint32_t dac_cntl; + uint32_t dac_cntl2; + uint32_t dac_ext_cntl; + uint32_t disp_misc_cntl; + uint32_t dac_macro_cntl; + uint32_t disp_pwr_man; + uint32_t disp_merge_cntl; + uint32_t disp_output_cntl; + uint32_t disp2_merge_cntl; + uint32_t dac_embedded_sync_cntl; + uint32_t dac_broad_pulse; + uint32_t dac_skew_clks; + uint32_t dac_incr; + uint32_t dac_neg_sync_level; + uint32_t dac_pos_sync_level; + uint32_t dac_blank_level; + uint32_t dac_sync_equalization; + uint32_t tv_dac_cntl; + uint32_t tv_master_cntl; +}; + +struct drm_radeon_private { + /* driver family specific functions */ + int (*bus_finish)(struct drm_device *dev); + int (*bus_init)(struct drm_device *dev); + void (*bus_restore)(struct drm_device *dev, struct radeon_state *state); + void (*bus_save)(struct drm_device *dev, struct radeon_state *state); + struct drm_ttm_backend *(*create_ttm)(struct drm_device *dev); + void (*irq_emit)(struct drm_device *dev); + void (*flush_cache)(struct drm_device *dev); + /* bus informations */ + void *bus; + uint32_t bus_type; + /* cp */ + uint32_t ring_buffer_size; + uint32_t ring_rptr; + uint32_t ring_wptr; + uint32_t ring_mask; + int ring_free; + uint32_t ring_tail_mask; + uint32_t write_back_area_size; + struct drm_buffer_object *ring_buffer_object; + struct drm_bo_kmap_obj ring_buffer_map; + uint32_t *ring_buffer; + uint32_t *write_back_area; + const uint32_t *microcode; + /* card family */ + uint32_t usec_timeout; + uint32_t family; + struct radeon_ms_properties *properties; + struct radeon_ms_output *outputs[RADEON_MAX_OUTPUTS]; + struct radeon_ms_connector *connectors[RADEON_MAX_CONNECTORS]; + /* drm map (MMIO, FB) */ + struct drm_map mmio; + struct drm_map vram; + /* gpu address space */ + uint32_t gpu_vram_size; + uint32_t gpu_vram_start; + uint32_t gpu_vram_end; + uint32_t gpu_gart_size; + uint32_t gpu_gart_start; + uint32_t gpu_gart_end; + /* state of the card when module was loaded */ + struct radeon_state load_state; + /* state the driver wants */ + struct radeon_state driver_state; + /* last emitted fence */ + uint32_t fence_id_last; + uint32_t fence_reg; + /* when doing gpu stop we save here current state */ + uint32_t crtc_ext_cntl; + uint32_t crtc_gen_cntl; + uint32_t crtc2_gen_cntl; + uint32_t ov0_scale_cntl; + /* bool & type on the hw */ + uint8_t crtc1_dpms; + uint8_t crtc2_dpms; + uint8_t restore_state; + uint8_t cp_ready; + uint8_t bus_ready; + uint8_t write_back; +}; + + +/* radeon_ms_bo.c */ +int radeon_ms_bo_get_gpu_addr(struct drm_device *dev, + struct drm_bo_mem_reg *mem, + uint32_t *gpu_addr); +int radeon_ms_bo_move(struct drm_buffer_object * bo, int evict, + int no_wait, struct drm_bo_mem_reg * new_mem); +struct drm_ttm_backend *radeon_ms_create_ttm_backend(struct drm_device * dev); +uint32_t radeon_ms_evict_mask(struct drm_buffer_object *bo); +int radeon_ms_init_mem_type(struct drm_device * dev, uint32_t type, + struct drm_mem_type_manager * man); +int radeon_ms_invalidate_caches(struct drm_device * dev, uint64_t flags); +void radeon_ms_ttm_flush(struct drm_ttm *ttm); + +/* radeon_ms_bus.c */ +int radeon_ms_agp_finish(struct drm_device *dev); +int radeon_ms_agp_init(struct drm_device *dev); +void radeon_ms_agp_restore(struct drm_device *dev, struct radeon_state *state); +void radeon_ms_agp_save(struct drm_device *dev, struct radeon_state *state); +struct drm_ttm_backend *radeon_ms_pcie_create_ttm(struct drm_device *dev); +int radeon_ms_pcie_finish(struct drm_device *dev); +int radeon_ms_pcie_init(struct drm_device *dev); +void radeon_ms_pcie_restore(struct drm_device *dev, struct radeon_state *state); +void radeon_ms_pcie_save(struct drm_device *dev, struct radeon_state *state); + +/* radeon_ms_compat.c */ +long radeon_ms_compat_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg); + +/* radeon_ms_cp.c */ +int radeon_ms_cp_finish(struct drm_device *dev); +int radeon_ms_cp_init(struct drm_device *dev); +void radeon_ms_cp_restore(struct drm_device *dev, struct radeon_state *state); +void radeon_ms_cp_save(struct drm_device *dev, struct radeon_state *state); +void radeon_ms_cp_stop(struct drm_device *dev); +int radeon_ms_cp_wait(struct drm_device *dev, int n); +int radeon_ms_ring_emit(struct drm_device *dev, uint32_t *cmd, uint32_t count); + +/* radeon_ms_crtc.c */ +int radeon_ms_crtc_create(struct drm_device *dev, int crtc); +void radeon_ms_crtc1_restore(struct drm_device *dev, + struct radeon_state *state); +void radeon_ms_crtc1_save(struct drm_device *dev, struct radeon_state *state); + +/* radeon_ms_dac.c */ +int radeon_ms_dac1_initialize(struct radeon_ms_output *output); +enum drm_output_status radeon_ms_dac1_detect(struct radeon_ms_output *output); +void radeon_ms_dac1_dpms(struct radeon_ms_output *output, int mode); +int radeon_ms_dac1_get_modes(struct radeon_ms_output *output); +bool radeon_ms_dac1_mode_fixup(struct radeon_ms_output *output, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); +int radeon_ms_dac1_mode_set(struct radeon_ms_output *output, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); +void radeon_ms_dac1_restore(struct radeon_ms_output *output, + struct radeon_state *state); +void radeon_ms_dac1_save(struct radeon_ms_output *output, + struct radeon_state *state); +int radeon_ms_dac2_initialize(struct radeon_ms_output *output); +enum drm_output_status radeon_ms_dac2_detect(struct radeon_ms_output *output); +void radeon_ms_dac2_dpms(struct radeon_ms_output *output, int mode); +int radeon_ms_dac2_get_modes(struct radeon_ms_output *output); +bool radeon_ms_dac2_mode_fixup(struct radeon_ms_output *output, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); +int radeon_ms_dac2_mode_set(struct radeon_ms_output *output, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); +void radeon_ms_dac2_restore(struct radeon_ms_output *output, + struct radeon_state *state); +void radeon_ms_dac2_save(struct radeon_ms_output *output, + struct radeon_state *state); + +/* radeon_ms_drm.c */ +int radeon_ms_driver_dma_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +void radeon_ms_driver_lastclose(struct drm_device * dev); +int radeon_ms_driver_load(struct drm_device *dev, unsigned long flags); +int radeon_ms_driver_open(struct drm_device * dev, struct drm_file *file_priv); +int radeon_ms_driver_unload(struct drm_device *dev); + +/* radeon_ms_exec.c */ +int radeon_ms_execbuffer(struct drm_device *dev, void *data, + struct drm_file *file_priv); + +/* radeon_ms_family.c */ +int radeon_ms_family_init(struct drm_device *dev); + +/* radeon_ms_fence.c */ +int radeon_ms_fence_emit_sequence(struct drm_device *dev, uint32_t class, + uint32_t flags, uint32_t *sequence, + uint32_t *native_type); +void radeon_ms_fence_handler(struct drm_device * dev); +int radeon_ms_fence_has_irq(struct drm_device *dev, uint32_t class, + uint32_t flags); +int radeon_ms_fence_types(struct drm_buffer_object *bo, + uint32_t * class, uint32_t * type); +void radeon_ms_poke_flush(struct drm_device * dev, uint32_t class); + +/* radeon_ms_fb.c */ +int radeonfb_probe(struct drm_device *dev, struct drm_crtc *crtc); +int radeonfb_remove(struct drm_device *dev, struct drm_crtc *crtc); + +/* radeon_ms_gpu.c */ +int radeon_ms_gpu_initialize(struct drm_device *dev); +void radeon_ms_gpu_dpms(struct drm_device *dev); +void radeon_ms_gpu_flush(struct drm_device *dev); +void radeon_ms_gpu_restore(struct drm_device *dev, struct radeon_state *state); +void radeon_ms_gpu_save(struct drm_device *dev, struct radeon_state *state); +int radeon_ms_wait_for_idle(struct drm_device *dev); + +/* radeon_ms_i2c.c */ +void radeon_ms_i2c_destroy(struct radeon_ms_i2c *i2c); +struct radeon_ms_i2c *radeon_ms_i2c_create(struct drm_device *dev, + const uint32_t reg, + const char *name); + +/* radeon_ms_irq.c */ +void radeon_ms_irq_emit(struct drm_device *dev); +irqreturn_t radeon_ms_irq_handler(DRM_IRQ_ARGS); +void radeon_ms_irq_preinstall(struct drm_device * dev); +void radeon_ms_irq_postinstall(struct drm_device * dev); +int radeon_ms_irq_init(struct drm_device *dev); +void radeon_ms_irq_restore(struct drm_device *dev, struct radeon_state *state); +void radeon_ms_irq_save(struct drm_device *dev, struct radeon_state *state); +void radeon_ms_irq_uninstall(struct drm_device * dev); + +/* radeon_ms_output.c */ +void radeon_ms_connectors_destroy(struct drm_device *dev); +int radeon_ms_connectors_from_properties(struct drm_device *dev); +void radeon_ms_outputs_destroy(struct drm_device *dev); +int radeon_ms_outputs_from_properties(struct drm_device *dev); +void radeon_ms_outputs_restore(struct drm_device *dev, + struct radeon_state *state); +void radeon_ms_outputs_save(struct drm_device *dev, struct radeon_state *state); + +/* radeon_ms_state.c */ +void radeon_ms_state_save(struct drm_device *dev, struct radeon_state *state); +void radeon_ms_state_restore(struct drm_device *dev, + struct radeon_state *state); + + +/* packect stuff **************************************************************/ +#define RADEON_CP_PACKET0 0x00000000 +#define CP_PACKET0(reg, n) \ + (RADEON_CP_PACKET0 | ((n) << 16) | ((reg) >> 2)) +#define CP_PACKET3_CNTL_BITBLT_MULTI 0xC0009B00 +# define GMC_SRC_PITCH_OFFSET_CNTL (1 << 0) +# define GMC_DST_PITCH_OFFSET_CNTL (1 << 1) +# define GMC_BRUSH_NONE (15 << 4) +# define GMC_SRC_DATATYPE_COLOR (3 << 12) +# define ROP3_S 0x00cc0000 +# define DP_SRC_SOURCE_MEMORY (2 << 24) +# define GMC_CLR_CMP_CNTL_DIS (1 << 28) +# define GMC_WR_MSK_DIS (1 << 30) + +/* helper macro & functions ***************************************************/ +#define REG_S(rn, bn, v) (((v) << rn##__##bn##__SHIFT) & rn##__##bn##__MASK) +#define REG_G(rn, bn, v) (((v) & rn##__##bn##__MASK) >> rn##__##bn##__SHIFT) +#define MMIO_R(rid) mmio_read(dev_priv, rid) +#define MMIO_W(rid, v) mmio_write(dev_priv, rid, v) +#define PCIE_R(rid) pcie_read(dev_priv, rid) +#define PCIE_W(rid, v) pcie_write(dev_priv, rid, v) +#define PPLL_R(rid) pll_read(dev_priv, rid) +#define PPLL_W(rid, v) pll_write(dev_priv, rid, v) + +static __inline__ uint32_t mmio_read(struct drm_radeon_private *dev_priv, + uint32_t offset) +{ + return DRM_READ32(&dev_priv->mmio, offset); +} + + +static __inline__ void mmio_write(struct drm_radeon_private *dev_priv, + uint32_t offset, uint32_t v) +{ + DRM_WRITE32(&dev_priv->mmio, offset, v); +} + +static __inline__ uint32_t pcie_read(struct drm_radeon_private *dev_priv, + uint32_t offset) +{ + MMIO_W(PCIE_INDEX, REG_S(PCIE_INDEX, PCIE_INDEX, offset)); + return MMIO_R(PCIE_DATA); +} + +static __inline__ void pcie_write(struct drm_radeon_private *dev_priv, + uint32_t offset, uint32_t v) +{ + MMIO_W(PCIE_INDEX, REG_S(PCIE_INDEX, PCIE_INDEX, offset)); + MMIO_W(PCIE_DATA, v); +} + +static __inline__ void pll_index_errata(struct drm_radeon_private *dev_priv) +{ + uint32_t tmp, save; + + /* This workaround is necessary on rv200 and RS200 or PLL + * reads may return garbage (among others...) + */ + if (dev_priv->properties->pll_dummy_reads) { + tmp = MMIO_R(CLOCK_CNTL_DATA); + tmp = MMIO_R(CRTC_GEN_CNTL); + } + /* This function is required to workaround a hardware bug in some (all?) + * revisions of the R300. This workaround should be called after every + * CLOCK_CNTL_INDEX register access. If not, register reads afterward + * may not be correct. + */ + if (dev_priv->properties->pll_r300_errata) { + tmp = save = MMIO_R(CLOCK_CNTL_INDEX); + tmp = tmp & ~CLOCK_CNTL_INDEX__PLL_ADDR__MASK; + tmp = tmp & ~CLOCK_CNTL_INDEX__PLL_WR_EN; + MMIO_W(CLOCK_CNTL_INDEX, tmp); + tmp = MMIO_R(CLOCK_CNTL_DATA); + MMIO_W(CLOCK_CNTL_INDEX, save); + } +} + +static __inline__ void pll_data_errata(struct drm_radeon_private *dev_priv) +{ + /* This workarounds is necessary on RV100, RS100 and RS200 chips + * or the chip could hang on a subsequent access + */ + if (dev_priv->properties->pll_delay) { + /* we can't deal with posted writes here ... */ + udelay(5000); + } +} + +static __inline__ uint32_t pll_read(struct drm_radeon_private *dev_priv, + uint32_t offset) +{ + uint32_t clock_cntl_index = dev_priv->driver_state.clock_cntl_index; + uint32_t data; + + clock_cntl_index &= ~CLOCK_CNTL_INDEX__PLL_ADDR__MASK; + clock_cntl_index |= REG_S(CLOCK_CNTL_INDEX, PLL_ADDR, offset); + MMIO_W(CLOCK_CNTL_INDEX, clock_cntl_index); + pll_index_errata(dev_priv); + data = MMIO_R(CLOCK_CNTL_DATA); + pll_data_errata(dev_priv); + return data; +} + +static __inline__ void pll_write(struct drm_radeon_private *dev_priv, + uint32_t offset, uint32_t value) +{ + uint32_t clock_cntl_index = dev_priv->driver_state.clock_cntl_index; + + clock_cntl_index &= ~CLOCK_CNTL_INDEX__PLL_ADDR__MASK; + clock_cntl_index |= REG_S(CLOCK_CNTL_INDEX, PLL_ADDR, offset); + clock_cntl_index |= CLOCK_CNTL_INDEX__PLL_WR_EN; + MMIO_W(CLOCK_CNTL_INDEX, clock_cntl_index); + pll_index_errata(dev_priv); + MMIO_W(CLOCK_CNTL_DATA, value); + pll_data_errata(dev_priv); +} + +#endif diff --git a/shared-core/radeon_ms_bo.c b/shared-core/radeon_ms_bo.c new file mode 100644 index 00000000..cedad689 --- /dev/null +++ b/shared-core/radeon_ms_bo.c @@ -0,0 +1,311 @@ +/* + * Copyright 2007 Dave Airlie + * Copyright 2007 Jérôme Glisse + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + */ +/* + * Authors: + * Dave Airlie <airlied@linux.ie> + * Jerome Glisse <glisse@freedesktop.org> + */ +#include "drmP.h" +#include "drm.h" + +#include "radeon_ms.h" + +void radeon_ms_bo_copy_blit(struct drm_device *dev, + uint32_t src_offset, + uint32_t dst_offset, + uint32_t pages) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t num_pages, stride, c; + uint32_t offset_inc = 0; + uint32_t cmd[7]; + + if (!dev_priv) { + return; + } + + /* radeon limited to 16320=255*64 bytes per row so copy at + * most 2 pages */ + num_pages = 2; + stride = ((num_pages * PAGE_SIZE) / 64) & 0xff; + while(pages > 0) { + if (num_pages > pages) { + num_pages = pages; + stride = ((num_pages * PAGE_SIZE) / 64) & 0xff; + } + c = pages / num_pages; + if (c >= 8192) { + c = 8191; + } + cmd[0] = CP_PACKET3_CNTL_BITBLT_MULTI | (5 << 16); + cmd[1] = GMC_SRC_PITCH_OFFSET_CNTL | + GMC_DST_PITCH_OFFSET_CNTL | + GMC_BRUSH_NONE | + (0x6 << 8) | + GMC_SRC_DATATYPE_COLOR | + ROP3_S | + DP_SRC_SOURCE_MEMORY | + GMC_CLR_CMP_CNTL_DIS | + GMC_WR_MSK_DIS; + cmd[2] = (stride << 22) | (src_offset >> 10); + cmd[3] = (stride << 22) | (dst_offset >> 10); + cmd[4] = (0 << 16) | 0; + cmd[5] = (0 << 16) | 0; + cmd[6] = ((stride * 16) << 16) | c; + radeon_ms_ring_emit(dev, cmd, 7); + offset_inc = num_pages * c * PAGE_SIZE; + src_offset += offset_inc; + dst_offset += offset_inc; + pages -= num_pages * c; + } + /* wait for 2d engine to go busy so wait_until stall */ + for (c = 0; c < dev_priv->usec_timeout; c++) { + uint32_t status = MMIO_R(RBBM_STATUS); + if ((RBBM_STATUS__E2_BUSY & status) || + (RBBM_STATUS__CBA2D_BUSY & status)) { + DRM_INFO("[radeon_ms] RBBM_STATUS 0x%08X\n", status); + break; + } + DRM_UDELAY(1); + } + /* Sync everything up */ + cmd[0] = CP_PACKET0(WAIT_UNTIL, 0); + cmd[1] = WAIT_UNTIL__WAIT_2D_IDLECLEAN | + WAIT_UNTIL__WAIT_HOST_IDLECLEAN; + radeon_ms_ring_emit(dev, cmd, 2); + return; +} + +static int radeon_ms_bo_move_blit(struct drm_buffer_object *bo, + int evict, int no_wait, + struct drm_bo_mem_reg *new_mem) +{ + struct drm_device *dev = bo->dev; + struct drm_bo_mem_reg *old_mem = &bo->mem; + uint32_t gpu_src_addr; + uint32_t gpu_dst_addr; + int ret; + + ret = radeon_ms_bo_get_gpu_addr(dev, old_mem, &gpu_src_addr); + if (ret) { + return ret; + } + ret = radeon_ms_bo_get_gpu_addr(dev, new_mem, &gpu_dst_addr); + if (ret) { + return ret; + } + + radeon_ms_bo_copy_blit(bo->dev, + gpu_src_addr, + gpu_dst_addr, + new_mem->num_pages); + + ret = drm_bo_move_accel_cleanup(bo, evict, no_wait, 0, + DRM_FENCE_TYPE_EXE | + DRM_RADEON_FENCE_TYPE_RW, + DRM_RADEON_FENCE_FLAG_FLUSHED, + new_mem); + return ret; +} + +static int radeon_ms_bo_move_flip(struct drm_buffer_object *bo, + int evict, int no_wait, + struct drm_bo_mem_reg *new_mem) +{ + struct drm_device *dev = bo->dev; + struct drm_bo_mem_reg tmp_mem; + int ret; + + tmp_mem = *new_mem; + tmp_mem.mm_node = NULL; + tmp_mem.mask = DRM_BO_FLAG_MEM_TT | + DRM_BO_FLAG_CACHED | + DRM_BO_FLAG_FORCE_CACHING; + ret = drm_bo_mem_space(bo, &tmp_mem, no_wait); + if (ret) { + return ret; + } + + ret = drm_bind_ttm(bo->ttm, &tmp_mem); + if (ret) { + goto out_cleanup; + } + ret = radeon_ms_bo_move_blit(bo, 1, no_wait, &tmp_mem); + if (ret) { + goto out_cleanup; + } + ret = drm_bo_move_ttm(bo, evict, no_wait, new_mem); +out_cleanup: + if (tmp_mem.mm_node) { + mutex_lock(&dev->struct_mutex); + if (tmp_mem.mm_node != bo->pinned_node) + drm_mm_put_block(tmp_mem.mm_node); + tmp_mem.mm_node = NULL; + mutex_unlock(&dev->struct_mutex); + } + return ret; +} + +int radeon_ms_bo_get_gpu_addr(struct drm_device *dev, + struct drm_bo_mem_reg *mem, + uint32_t *gpu_addr) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + *gpu_addr = mem->mm_node->start << PAGE_SHIFT; + switch (mem->flags & DRM_BO_MASK_MEM) { + case DRM_BO_FLAG_MEM_TT: + *gpu_addr += dev_priv->gpu_gart_start; + DRM_INFO("[radeon_ms] GPU TT: 0x%08X\n", *gpu_addr); + break; + case DRM_BO_FLAG_MEM_VRAM: + *gpu_addr += dev_priv->gpu_vram_start; + DRM_INFO("[radeon_ms] GPU VRAM: 0x%08X\n", *gpu_addr); + break; + default: + DRM_ERROR("[radeon_ms] memory not accessible by GPU\n"); + return -EINVAL; + } + return 0; +} + +int radeon_ms_bo_move(struct drm_buffer_object *bo, int evict, + int no_wait, struct drm_bo_mem_reg *new_mem) +{ + struct drm_bo_mem_reg *old_mem = &bo->mem; + if (old_mem->mem_type == DRM_BO_MEM_LOCAL) { + return drm_bo_move_memcpy(bo, evict, no_wait, new_mem); + } else if (new_mem->mem_type == DRM_BO_MEM_LOCAL) { + if (radeon_ms_bo_move_flip(bo, evict, no_wait, new_mem)) + return drm_bo_move_memcpy(bo, evict, no_wait, new_mem); + } else { + if (radeon_ms_bo_move_blit(bo, evict, no_wait, new_mem)) + return drm_bo_move_memcpy(bo, evict, no_wait, new_mem); + } + return 0; +} + +struct drm_ttm_backend *radeon_ms_create_ttm_backend(struct drm_device * dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + if (dev_priv && dev_priv->create_ttm) + return dev_priv->create_ttm(dev); + return NULL; +} + +uint32_t radeon_ms_evict_mask(struct drm_buffer_object *bo) +{ + switch (bo->mem.mem_type) { + case DRM_BO_MEM_LOCAL: + case DRM_BO_MEM_TT: + return DRM_BO_FLAG_MEM_LOCAL; + case DRM_BO_MEM_VRAM: + if (bo->mem.num_pages > 128) + return DRM_BO_MEM_TT; + else + return DRM_BO_MEM_LOCAL; + default: + return DRM_BO_FLAG_MEM_TT | DRM_BO_FLAG_CACHED; + } +} + +int radeon_ms_init_mem_type(struct drm_device * dev, uint32_t type, + struct drm_mem_type_manager * man) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + switch (type) { + case DRM_BO_MEM_LOCAL: + man->flags = _DRM_FLAG_MEMTYPE_MAPPABLE | + _DRM_FLAG_MEMTYPE_CACHED; + man->drm_bus_maptype = 0; + break; + case DRM_BO_MEM_VRAM: + man->flags = _DRM_FLAG_MEMTYPE_FIXED | + _DRM_FLAG_MEMTYPE_MAPPABLE | + _DRM_FLAG_NEEDS_IOREMAP; + man->io_addr = NULL; + man->drm_bus_maptype = _DRM_FRAME_BUFFER; + man->io_offset = dev_priv->vram.offset; + man->io_size = dev_priv->vram.size; + break; + case DRM_BO_MEM_TT: + if (!dev_priv->bus_ready) { + DRM_ERROR("Bus isn't initialized while " + "intializing TT memory type\n"); + return -EINVAL; + } + switch(dev_priv->bus_type) { + case RADEON_AGP: + if (!(drm_core_has_AGP(dev) && dev->agp)) { + DRM_ERROR("AGP is not enabled for memory " + "type %u\n", (unsigned)type); + return -EINVAL; + } + man->io_offset = dev->agp->agp_info.aper_base; + man->io_size = dev->agp->agp_info.aper_size * + 1024 * 1024; + man->io_addr = NULL; + man->flags = _DRM_FLAG_MEMTYPE_MAPPABLE | + _DRM_FLAG_MEMTYPE_CSELECT | + _DRM_FLAG_NEEDS_IOREMAP; + man->drm_bus_maptype = _DRM_AGP; + man->gpu_offset = 0; + break; + default: + man->io_offset = dev_priv->gpu_gart_start; + man->io_size = dev_priv->gpu_gart_size; + man->io_addr = NULL; + man->flags = _DRM_FLAG_MEMTYPE_CSELECT | + _DRM_FLAG_MEMTYPE_MAPPABLE | + _DRM_FLAG_MEMTYPE_CMA; + man->drm_bus_maptype = _DRM_SCATTER_GATHER; + break; + } + break; + default: + DRM_ERROR("Unsupported memory type %u\n", (unsigned)type); + return -EINVAL; + } + return 0; +} + +int radeon_ms_invalidate_caches(struct drm_device * dev, uint64_t flags) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + dev_priv->flush_cache(dev); + return 0; +} + +void radeon_ms_ttm_flush(struct drm_ttm *ttm) +{ + if (!ttm) + return; + + DRM_MEMORYBARRIER(); +} diff --git a/shared-core/radeon_ms_bus.c b/shared-core/radeon_ms_bus.c new file mode 100644 index 00000000..d50c9fb8 --- /dev/null +++ b/shared-core/radeon_ms_bus.c @@ -0,0 +1,453 @@ +/* + * Copyright 2007 Jérôme Glisse + * Copyright 2007 Alex Deucher + * Copyright 2007 Dave Airlie + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + */ +/* + * Authors: + * Dave Airlie <airlied@linux.ie> + * Jerome Glisse <glisse@freedesktop.org> + */ +#include "drmP.h" +#include "drm.h" +#include "radeon_ms.h" + +struct radeon_pcie { + uint32_t gart_table_size; + struct drm_buffer_object *gart_table_object; + volatile uint32_t *gart_table; + struct drm_device *dev; + unsigned long page_last; +}; + +struct radeon_pcie_gart { + struct drm_ttm_backend backend; + struct radeon_pcie *pcie; + unsigned long page_first; + struct page **pages; + unsigned long num_pages; + int populated; + int bound; +}; + +static int pcie_ttm_bind(struct drm_ttm_backend *backend, + struct drm_bo_mem_reg *bo_mem); +static void pcie_ttm_clear(struct drm_ttm_backend *backend); +static void pcie_ttm_destroy(struct drm_ttm_backend *backend); +static int pcie_ttm_needs_ub_cache_adjust(struct drm_ttm_backend *backend); +static int pcie_ttm_populate(struct drm_ttm_backend *backend, + unsigned long num_pages, struct page **pages); +static int pcie_ttm_unbind(struct drm_ttm_backend *backend); + +static struct drm_ttm_backend_func radeon_pcie_gart_ttm_backend = +{ + .needs_ub_cache_adjust = pcie_ttm_needs_ub_cache_adjust, + .populate = pcie_ttm_populate, + .clear = pcie_ttm_clear, + .bind = pcie_ttm_bind, + .unbind = pcie_ttm_unbind, + .destroy = pcie_ttm_destroy, +}; + +static void pcie_gart_flush(struct radeon_pcie *pcie) +{ + struct drm_device *dev; + struct drm_radeon_private *dev_priv; + uint32_t flush; + + if (pcie == NULL) { + return; + } + dev = pcie->dev; + dev_priv = dev->dev_private; + flush = dev_priv->driver_state.pcie_tx_gart_cntl; + flush |= PCIE_TX_GART_CNTL__GART_INVALIDATE_TLB; + PCIE_W(PCIE_TX_GART_CNTL, flush); + PCIE_W(PCIE_TX_GART_CNTL, dev_priv->driver_state.pcie_tx_gart_cntl); +} + +static __inline__ uint32_t pcie_gart_get_page_base(struct radeon_pcie *pcie, + unsigned long page) +{ + if (pcie == NULL || pcie->gart_table == NULL) { + return 0; + } + return ((pcie->gart_table[page] & (~0xC)) << 8); +} + +static __inline__ void pcie_gart_set_page_base(struct radeon_pcie *pcie, + unsigned long page, + uint32_t page_base) +{ + if (pcie == NULL || pcie->gart_table == NULL) { + return; + } + pcie->gart_table[page] = cpu_to_le32((page_base >> 8) | 0xC); +} + +static int pcie_ttm_bind(struct drm_ttm_backend *backend, + struct drm_bo_mem_reg *bo_mem) +{ + struct radeon_pcie_gart *pcie_gart; + unsigned long page_first; + unsigned long page_last; + unsigned long page, i; + uint32_t page_base; + + pcie_gart = container_of(backend, struct radeon_pcie_gart, backend); + page = page_first = bo_mem->mm_node->start; + page_last = page_first + pcie_gart->num_pages; + if (page_first >= pcie_gart->pcie->page_last || + page_last >= pcie_gart->pcie->page_last) + return -EINVAL; + while (page < page_last) { + if (pcie_gart_get_page_base(pcie_gart->pcie, page)) { + return -EBUSY; + } + page++; + } + + for (i = 0, page = page_first; i < pcie_gart->num_pages; i++, page++) { + struct page *cur_page = pcie_gart->pages[i]; + /* write value */ + page_base = page_to_phys(cur_page); + pcie_gart_set_page_base(pcie_gart->pcie, page, page_base); + } + DRM_MEMORYBARRIER(); + pcie_gart_flush(pcie_gart->pcie); + pcie_gart->bound = 1; + pcie_gart->page_first = page_first; + return 0; +} + +static void pcie_ttm_clear(struct drm_ttm_backend *backend) +{ + struct radeon_pcie_gart *pcie_gart; + + pcie_gart = container_of(backend, struct radeon_pcie_gart, backend); + if (pcie_gart->pages) { + backend->func->unbind(backend); + pcie_gart->pages = NULL; + } + pcie_gart->num_pages = 0; +} + +static void pcie_ttm_destroy(struct drm_ttm_backend *backend) +{ + struct radeon_pcie_gart *pcie_gart; + + if (backend == NULL) { + return; + } + pcie_gart = container_of(backend, struct radeon_pcie_gart, backend); + if (pcie_gart->pages) { + backend->func->clear(backend); + } + drm_ctl_free(pcie_gart, sizeof(*pcie_gart), DRM_MEM_TTM); +} + +static int pcie_ttm_needs_ub_cache_adjust(struct drm_ttm_backend *backend) +{ + return ((backend->flags & DRM_BE_FLAG_BOUND_CACHED) ? 0 : 1); +} + +static int pcie_ttm_populate(struct drm_ttm_backend *backend, + unsigned long num_pages, struct page **pages) +{ + struct radeon_pcie_gart *pcie_gart; + + pcie_gart = container_of(backend, struct radeon_pcie_gart, backend); + pcie_gart->pages = pages; + pcie_gart->num_pages = num_pages; + pcie_gart->populated = 1; + return 0; +} + +static int pcie_ttm_unbind(struct drm_ttm_backend *backend) +{ + struct radeon_pcie_gart *pcie_gart; + unsigned long page, i; + + pcie_gart = container_of(backend, struct radeon_pcie_gart, backend); + if (pcie_gart->bound != 1 || pcie_gart->pcie->gart_table == NULL) { + return -EINVAL; + } + for (i = 0, page = pcie_gart->page_first; i < pcie_gart->num_pages; + i++, page++) { + pcie_gart->pcie->gart_table[page] = 0; + } + pcie_gart_flush(pcie_gart->pcie); + pcie_gart->bound = 0; + pcie_gart->page_first = 0; + return 0; +} + +int radeon_ms_agp_finish(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + if (!dev_priv->bus_ready) { + return 0; + } + dev_priv->bus_ready = 0; + drm_agp_release(dev); + return 0; +} + +int radeon_ms_agp_init(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_state *state = &dev_priv->driver_state; + struct drm_agp_mode mode; + uint32_t agp_status; + int ret; + + dev_priv->bus_ready = -1; + if (dev->agp == NULL) { + DRM_ERROR("[radeon_ms] can't initialize AGP\n"); + return -EINVAL; + } + ret = drm_agp_acquire(dev); + if (ret) { + DRM_ERROR("[radeon_ms] error failed to acquire agp\n"); + return ret; + } + agp_status = MMIO_R(AGP_STATUS); + if ((AGP_STATUS__MODE_AGP30 & agp_status)) { + mode.mode = AGP_STATUS__RATE4X; + } else { + mode.mode = AGP_STATUS__RATE2X_8X; + } + ret = drm_agp_enable(dev, mode); + if (ret) { + DRM_ERROR("[radeon_ms] error failed to enable agp\n"); + return ret; + } + state->agp_command = MMIO_R(AGP_COMMAND) | AGP_COMMAND__AGP_EN; + state->agp_command &= ~AGP_COMMAND__FW_EN; + state->agp_command &= ~AGP_COMMAND__MODE_4G_EN; + state->aic_ctrl = 0; + state->agp_base = REG_S(AGP_BASE, AGP_BASE_ADDR, dev->agp->base); + state->agp_base_2 = 0; + state->bus_cntl = MMIO_R(BUS_CNTL); + state->bus_cntl &= ~BUS_CNTL__BUS_MASTER_DIS; + state->mc_agp_location = + REG_S(MC_AGP_LOCATION, MC_AGP_START, + dev_priv->gpu_gart_start >> 16) | + REG_S(MC_AGP_LOCATION, MC_AGP_TOP, + dev_priv->gpu_gart_end >> 16); + DRM_INFO("[radeon_ms] gpu agp base 0x%08X\n", MMIO_R(AGP_BASE)); + DRM_INFO("[radeon_ms] gpu agp location 0x%08X\n", + MMIO_R(MC_AGP_LOCATION)); + DRM_INFO("[radeon_ms] gpu agp location 0x%08X\n", + state->mc_agp_location); + DRM_INFO("[radeon_ms] bus ready\n"); + dev_priv->bus_ready = 1; + return 0; +} + +void radeon_ms_agp_restore(struct drm_device *dev, struct radeon_state *state) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + MMIO_W(MC_AGP_LOCATION, state->mc_agp_location); + MMIO_W(AGP_BASE, state->agp_base); + MMIO_W(AGP_BASE_2, state->agp_base_2); + MMIO_W(AGP_COMMAND, state->agp_command); +} + +void radeon_ms_agp_save(struct drm_device *dev, struct radeon_state *state) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + state->agp_command = MMIO_R(AGP_COMMAND); + state->agp_base = MMIO_R(AGP_BASE); + state->agp_base_2 = MMIO_R(AGP_BASE_2); + state->mc_agp_location = MMIO_R(MC_AGP_LOCATION); +} + +struct drm_ttm_backend *radeon_ms_pcie_create_ttm(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_pcie_gart *pcie_gart; + + pcie_gart = drm_ctl_calloc(1, sizeof (*pcie_gart), DRM_MEM_TTM); + if (pcie_gart == NULL) { + return NULL; + } + memset(pcie_gart, 0, sizeof(struct radeon_pcie_gart)); + pcie_gart->populated = 0; + pcie_gart->pcie = dev_priv->bus; + pcie_gart->backend.func = &radeon_pcie_gart_ttm_backend; + + return &pcie_gart->backend; +} + +int radeon_ms_pcie_finish(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_pcie *pcie = dev_priv->bus; + + if (!dev_priv->bus_ready || pcie == NULL) { + dev_priv->bus_ready = 0; + return 0; + } + dev_priv->bus_ready = 0; + if (pcie->gart_table) { + drm_mem_reg_iounmap(dev, &pcie->gart_table_object->mem, + (void *)pcie->gart_table); + } + pcie->gart_table = NULL; + if (pcie->gart_table_object) { + mutex_lock(&dev->struct_mutex); + drm_bo_usage_deref_locked(&pcie->gart_table_object); + mutex_unlock(&dev->struct_mutex); + } + dev_priv->bus = NULL; + drm_free(pcie, sizeof(*pcie), DRM_MEM_DRIVER); + return 0; +} + +int radeon_ms_pcie_init(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_state *state = &dev_priv->driver_state; + struct radeon_pcie *pcie; + int ret = 0; + + dev_priv->bus_ready = -1; + /* allocate and clear device private structure */ + pcie = drm_alloc(sizeof(struct radeon_pcie), DRM_MEM_DRIVER); + if (pcie == NULL) { + return -ENOMEM; + } + memset(pcie, 0, sizeof(struct radeon_pcie)); + pcie->dev = dev; + dev_priv->bus = (void *)pcie; + pcie->gart_table_size = (dev_priv->gpu_gart_size / RADEON_PAGE_SIZE) * + 4; + /* gart table start must be aligned on 16bytes, align it on one page */ + ret = drm_buffer_object_create(dev, + pcie->gart_table_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, + 1, + 0, + &pcie->gart_table_object); + if (ret) { + return ret; + } + ret = drm_mem_reg_ioremap(dev, &pcie->gart_table_object->mem, + (void **) &pcie->gart_table); + if (ret) { + DRM_ERROR("[radeon_ms] error mapping gart table: %d\n", ret); + return ret; + } + DRM_INFO("[radeon_ms] gart table in vram at 0x%08lX\n", + pcie->gart_table_object->offset); + memset((void *)pcie->gart_table, 0, pcie->gart_table_size); + pcie->page_last = pcie->gart_table_size >> 2; + state->pcie_tx_gart_discard_rd_addr_lo = + REG_S(PCIE_TX_GART_DISCARD_RD_ADDR_LO, + GART_DISCARD_RD_ADDR_LO, + dev_priv->gpu_gart_start); + state->pcie_tx_gart_discard_rd_addr_hi = + REG_S(PCIE_TX_GART_DISCARD_RD_ADDR_HI, + GART_DISCARD_RD_ADDR_HI, 0); + state->pcie_tx_gart_base = + REG_S(PCIE_TX_GART_BASE, GART_BASE, + pcie->gart_table_object->offset); + state->pcie_tx_gart_start_lo = + REG_S(PCIE_TX_GART_START_LO, GART_START_LO, + dev_priv->gpu_gart_start); + state->pcie_tx_gart_start_hi = + REG_S(PCIE_TX_GART_START_HI, GART_START_HI, 0); + state->pcie_tx_gart_end_lo = + REG_S(PCIE_TX_GART_END_LO, GART_END_LO, dev_priv->gpu_gart_end); + state->pcie_tx_gart_end_hi = + REG_S(PCIE_TX_GART_END_HI, GART_END_HI, 0); + /* FIXME: why this ? */ + state->aic_ctrl = 0; + state->agp_base = 0; + state->agp_base_2 = 0; + state->bus_cntl = MMIO_R(BUS_CNTL); + state->mc_agp_location = REG_S(MC_AGP_LOCATION, MC_AGP_START, 0xffc0) | + REG_S(MC_AGP_LOCATION, MC_AGP_TOP, 0xffff); + state->pcie_tx_gart_cntl = + PCIE_TX_GART_CNTL__GART_EN | + REG_S(PCIE_TX_GART_CNTL, GART_UNMAPPED_ACCESS, + GART_UNMAPPED_ACCESS__DISCARD) | + REG_S(PCIE_TX_GART_CNTL, GART_MODE, GART_MODE__CACHE_32x128) | + REG_S(PCIE_TX_GART_CNTL, GART_RDREQPATH_SEL, + GART_RDREQPATH_SEL__HDP); + DRM_INFO("[radeon_ms] gpu gart start 0x%08X\n", + PCIE_R(PCIE_TX_GART_START_LO)); + DRM_INFO("[radeon_ms] gpu gart end 0x%08X\n", + PCIE_R(PCIE_TX_GART_END_LO)); + DRM_INFO("[radeon_ms] bus ready\n"); + dev_priv->bus_ready = 1; + return 0; +} + +void radeon_ms_pcie_restore(struct drm_device *dev, struct radeon_state *state) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + /* disable gart before programing other registers */ + radeon_ms_agp_restore(dev, state); + PCIE_W(PCIE_TX_GART_CNTL, 0); + PCIE_W(PCIE_TX_GART_BASE, state->pcie_tx_gart_base); + PCIE_W(PCIE_TX_GART_BASE, state->pcie_tx_gart_base); + PCIE_W(PCIE_TX_GART_DISCARD_RD_ADDR_HI, + state->pcie_tx_gart_discard_rd_addr_hi); + PCIE_W(PCIE_TX_GART_DISCARD_RD_ADDR_LO, + state->pcie_tx_gart_discard_rd_addr_lo); + PCIE_W(PCIE_TX_GART_START_HI, state->pcie_tx_gart_start_hi); + PCIE_W(PCIE_TX_GART_START_LO, state->pcie_tx_gart_start_lo); + PCIE_W(PCIE_TX_GART_END_HI, state->pcie_tx_gart_end_hi); + PCIE_W(PCIE_TX_GART_END_LO, state->pcie_tx_gart_end_lo); + PCIE_W(PCIE_TX_GART_CNTL, state->pcie_tx_gart_cntl); +} + +void radeon_ms_pcie_save(struct drm_device *dev, struct radeon_state *state) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + radeon_ms_agp_save(dev, state); + state->pcie_tx_gart_base = PCIE_R(PCIE_TX_GART_BASE); + state->pcie_tx_gart_base = PCIE_R(PCIE_TX_GART_BASE); + state->pcie_tx_gart_discard_rd_addr_hi = + PCIE_R(PCIE_TX_GART_DISCARD_RD_ADDR_HI); + state->pcie_tx_gart_discard_rd_addr_lo = + PCIE_R(PCIE_TX_GART_DISCARD_RD_ADDR_LO); + state->pcie_tx_gart_start_hi = PCIE_R(PCIE_TX_GART_START_HI); + state->pcie_tx_gart_start_lo = PCIE_R(PCIE_TX_GART_START_LO); + state->pcie_tx_gart_end_hi = PCIE_R(PCIE_TX_GART_END_HI); + state->pcie_tx_gart_end_lo = PCIE_R(PCIE_TX_GART_END_LO); + state->pcie_tx_gart_cntl = PCIE_R(PCIE_TX_GART_CNTL); +} diff --git a/shared-core/radeon_ms_cp.c b/shared-core/radeon_ms_cp.c new file mode 100644 index 00000000..c01769bd --- /dev/null +++ b/shared-core/radeon_ms_cp.c @@ -0,0 +1,345 @@ +/* + * Copyright 2007 Jérôme Glisse + * Copyright 2007 Dave Airlie + * Copyright 2007 Alex Deucher + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/* + * Authors: + * Jerome Glisse <glisse@freedesktop.org> + */ +#include "radeon_ms.h" + +static int radeon_ms_test_ring_buffer(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + int i, ret; + uint32_t cmd[4]; + + MMIO_W(SCRATCH_REG4, 0); + cmd[0] = CP_PACKET0(SCRATCH_REG4, 0); + cmd[1] = 0xdeadbeef; + cmd[2] = CP_PACKET0(WAIT_UNTIL, 0); + cmd[3] = WAIT_UNTIL__WAIT_2D_IDLECLEAN | + WAIT_UNTIL__WAIT_HOST_IDLECLEAN; + DRM_MEMORYBARRIER(); + ret = radeon_ms_ring_emit(dev, cmd, 4); + if (ret) { + return 0; + } + DRM_UDELAY(100); + + for (i = 0; i < dev_priv->usec_timeout; i++) { + if (MMIO_R(SCRATCH_REG4) == 0xdeadbeef) { + DRM_INFO("[radeon_ms] cp test succeeded in %d usecs\n", + i); + return 1; + } + DRM_UDELAY(1); + } + DRM_INFO("[radeon_ms] cp test failed\n"); + return 0; +} + +static int radeon_ms_test_write_back(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t tmp; + + if (dev_priv->ring_buffer_object == NULL || + dev_priv->ring_buffer == NULL) + return 0; + dev_priv->write_back_area[0] = 0x0; + MMIO_W(SCRATCH_REG0, 0xdeadbeef); + for (tmp = 0; tmp < dev_priv->usec_timeout; tmp++) { + if (dev_priv->write_back_area[0] == 0xdeadbeef) + break; + DRM_UDELAY(1); + } + if (tmp < dev_priv->usec_timeout) { + DRM_INFO("[radeon_ms] writeback test succeeded in %d usecs\n", + tmp); + return 1; + } + MMIO_W(SCRATCH_UMSK, 0x0); + DRM_INFO("[radeon_ms] writeback test failed\n"); + return 0; +} + +static __inline__ void radeon_ms_load_mc(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + int i; + + MMIO_W(CP_ME_RAM_ADDR, 0); + for (i = 0; i < 256; i++) { + MMIO_W(CP_ME_RAM_DATAH, dev_priv->microcode[(i * 2) + 1]); + MMIO_W(CP_ME_RAM_DATAL, dev_priv->microcode[(i * 2) + 0]); + } +} + +int radeon_ms_cp_finish(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + if (!dev_priv->cp_ready) { + return 0; + } + dev_priv->cp_ready = 0; + radeon_ms_wait_for_idle(dev); + DRM_INFO("[radeon_ms] cp idle\n"); + radeon_ms_cp_stop(dev); + + DRM_INFO("[radeon_ms] ring buffer %p\n", dev_priv->ring_buffer); + if (dev_priv->ring_buffer) { + drm_bo_kunmap(&dev_priv->ring_buffer_map); + } + dev_priv->ring_buffer = NULL; + DRM_INFO("[radeon_ms] ring buffer object %p\n", dev_priv->ring_buffer_object); + if (dev_priv->ring_buffer_object) { + mutex_lock(&dev->struct_mutex); + drm_bo_usage_deref_locked(&dev_priv->ring_buffer_object); + mutex_unlock(&dev->struct_mutex); + } + return 0; +} + +int radeon_ms_cp_init(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_state *state = &dev_priv->driver_state; + int ret = 0; + + dev_priv->cp_ready = -1; + if (dev_priv->microcode == NULL) { + DRM_INFO("[radeon_ms] no microcode not starting cp"); + return 0; + } + /* we allocate an extra page for all write back stuff */ + ret = drm_buffer_object_create(dev, + dev_priv->ring_buffer_size + + dev_priv->write_back_area_size, + drm_bo_type_kernel, + DRM_BO_FLAG_READ | + DRM_BO_FLAG_WRITE | + DRM_BO_FLAG_MEM_TT | + DRM_BO_FLAG_NO_EVICT, + DRM_BO_HINT_DONT_FENCE, + 1, + 0, + &dev_priv->ring_buffer_object); + if (ret) { + return ret; + } + memset(&dev_priv->ring_buffer_map, 0, sizeof(struct drm_bo_kmap_obj)); + ret = drm_bo_kmap(dev_priv->ring_buffer_object, + dev_priv->ring_buffer_object->mem.mm_node->start, + dev_priv->ring_buffer_object->mem.num_pages, + &dev_priv->ring_buffer_map); + if (ret) { + DRM_ERROR("[radeon_ms] error mapping ring buffer: %d\n", ret); + return ret; + } + dev_priv->ring_buffer = dev_priv->ring_buffer_map.virtual; + dev_priv->write_back_area = + &dev_priv->ring_buffer[dev_priv->ring_buffer_size >> 2]; + /* setup write back offset */ + state->scratch_umsk = 0x7; + state->scratch_addr = + REG_S(SCRATCH_ADDR, SCRATCH_ADDR, + (dev_priv->ring_buffer_object->offset + + dev_priv->ring_buffer_size + + dev_priv->gpu_gart_start) >> 5); + MMIO_W(SCRATCH_ADDR, state->scratch_addr); + MMIO_W(SCRATCH_UMSK, REG_S(SCRATCH_UMSK, SCRATCH_UMSK, 0x7)); + DRM_INFO("[radeon_ms] write back at 0x%08X in gpu space\n", + MMIO_R(SCRATCH_ADDR)); + dev_priv->write_back = radeon_ms_test_write_back(dev); + + /* stop cp so it's in know state */ + radeon_ms_cp_stop(dev); + if (dev_priv->ring_rptr) { + DRM_INFO("[radeon_ms] failed to set cp read ptr to 0\n"); + } else { + DRM_INFO("[radeon_ms] set cp read ptr to 0\n"); + } + dev_priv->ring_mask = (dev_priv->ring_buffer_size / 4) - 1; + + /* load microcode */ + DRM_INFO("[radeon_ms] load microcode\n"); + radeon_ms_load_mc(dev); + /* initialize CP registers */ + state->cp_rb_cntl = + REG_S(CP_RB_CNTL, RB_BUFSZ, + drm_order(dev_priv->ring_buffer_size / 8)) | + REG_S(CP_RB_CNTL, RB_BLKSZ, drm_order(4096 / 8)) | + REG_S(CP_RB_CNTL, MAX_FETCH, 2); + if (!dev_priv->write_back) { + state->cp_rb_cntl |= CP_RB_CNTL__RB_NO_UPDATE; + } + state->cp_rb_base = + REG_S(CP_RB_BASE, RB_BASE, + (dev_priv->ring_buffer_object->offset + + dev_priv->gpu_gart_start) >> 2); + /* read ptr writeback just after the + * 8 scratch registers 32 = 8*4 */ + state->cp_rb_rptr_addr = + REG_S(CP_RB_RPTR_ADDR, RB_RPTR_ADDR, + (dev_priv->ring_buffer_object->offset + + dev_priv->ring_buffer_size + 32 + + dev_priv->gpu_gart_start) >> 2); + state->cp_rb_wptr = dev_priv->ring_wptr; + state->cp_rb_wptr_delay = + REG_S(CP_RB_WPTR_DELAY, PRE_WRITE_TIMER, 64) | + REG_S(CP_RB_WPTR_DELAY, PRE_WRITE_LIMIT, 8); + state->cp_rb_wptr_delay = 0; + + radeon_ms_cp_restore(dev, state); + DRM_INFO("[radeon_ms] ring buffer at 0x%08X in gpu space\n", + MMIO_R(CP_RB_BASE)); + + /* compute free space */ + dev_priv->ring_free = 0; + ret = radeon_ms_cp_wait(dev, 64); + if (ret) { + /* we shouldn't fail here */ + DRM_INFO("[radeon_ms] failed to get ring free space\n"); + return ret; + } + DRM_INFO("[radeon_ms] free ring size: %d\n", dev_priv->ring_free * 4); + + MMIO_W(CP_CSQ_CNTL, REG_S(CP_CSQ_CNTL, CSQ_MODE, + CSQ_MODE__CSQ_PRIBM_INDBM)); + if (!radeon_ms_test_ring_buffer(dev)) { + DRM_INFO("[radeon_ms] cp doesn't work\n"); + /* disable ring should wait idle before */ + radeon_ms_cp_stop(dev); + return -EBUSY; + } + /* waaooo the cp is ready & working */ + DRM_INFO("[radeon_ms] cp ready, enjoy\n"); + dev_priv->cp_ready = 1; + return 0; +} + +void radeon_ms_cp_restore(struct drm_device *dev, struct radeon_state *state) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + radeon_ms_wait_for_idle(dev); + MMIO_W(SCRATCH_ADDR, state->scratch_addr); + MMIO_W(SCRATCH_UMSK, state->scratch_umsk); + MMIO_W(CP_RB_BASE, state->cp_rb_base); + MMIO_W(CP_RB_RPTR_ADDR, state->cp_rb_rptr_addr); + MMIO_W(CP_RB_WPTR_DELAY, state->cp_rb_wptr_delay); + MMIO_W(CP_RB_CNTL, state->cp_rb_cntl); + /* Sync everything up */ + MMIO_W(ISYNC_CNTL, ISYNC_CNTL__ISYNC_ANY2D_IDLE3D | + ISYNC_CNTL__ISYNC_ANY3D_IDLE2D | + ISYNC_CNTL__ISYNC_WAIT_IDLEGUI | + ISYNC_CNTL__ISYNC_CPSCRATCH_IDLEGUI); +} + +void radeon_ms_cp_save(struct drm_device *dev, struct radeon_state *state) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + state->scratch_addr = MMIO_R(SCRATCH_ADDR); + state->scratch_umsk = MMIO_R(SCRATCH_UMSK); + state->cp_rb_base = MMIO_R(CP_RB_BASE); + state->cp_rb_rptr_addr = MMIO_R(CP_RB_RPTR_ADDR); + state->cp_rb_wptr_delay = MMIO_R(CP_RB_WPTR_DELAY); + state->cp_rb_wptr = MMIO_R(CP_RB_WPTR); + state->cp_rb_cntl = MMIO_R(CP_RB_CNTL); +} + +void radeon_ms_cp_stop(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + MMIO_W(CP_CSQ_CNTL, REG_S(CP_CSQ_CNTL, CSQ_MODE, + CSQ_MODE__CSQ_PRIDIS_INDDIS)); + MMIO_W(CP_RB_CNTL, CP_RB_CNTL__RB_RPTR_WR_ENA); + MMIO_W(CP_RB_RPTR_WR, 0); + MMIO_W(CP_RB_WPTR, 0); + DRM_UDELAY(5); + dev_priv->ring_wptr = dev_priv->ring_rptr = MMIO_R(CP_RB_RPTR); + MMIO_W(CP_RB_WPTR, dev_priv->ring_wptr); +} + +int radeon_ms_cp_wait(struct drm_device *dev, int n) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t i, last_rptr, p = 0; + + last_rptr = MMIO_R(CP_RB_RPTR); + for (i = 0; i < dev_priv->usec_timeout; i++) { + dev_priv->ring_rptr = MMIO_R(CP_RB_RPTR); + if (last_rptr != dev_priv->ring_rptr) { + /* the ring is progressing no lockup */ + p = 1; + } + dev_priv->ring_free = (((int)dev_priv->ring_rptr) - + ((int)dev_priv->ring_wptr)); + if (dev_priv->ring_free <= 0) + dev_priv->ring_free += (dev_priv->ring_buffer_size / 4); + if (dev_priv->ring_free > n) + return 0; + last_rptr = dev_priv->ring_rptr; + DRM_UDELAY(1); + } + if (p) { + DRM_INFO("[radeon_ms] timed out waiting free slot\n"); + } else { + DRM_INFO("[radeon_ms] cp have lickely locked up\n"); + } + return -EBUSY; +} + +int radeon_ms_ring_emit(struct drm_device *dev, uint32_t *cmd, uint32_t count) +{ + static spinlock_t ring_lock = SPIN_LOCK_UNLOCKED; + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t i = 0; + + if (!count) + return -EINVAL; + + spin_lock(&ring_lock); + if (dev_priv->ring_free <= (count)) { + spin_unlock(&ring_lock); + return -EBUSY; + } + dev_priv->ring_free -= count; + for (i = 0; i < count; i++) { + dev_priv->ring_buffer[dev_priv->ring_wptr] = cmd[i]; + dev_priv->ring_wptr++; + dev_priv->ring_wptr &= dev_priv->ring_mask; + } + /* commit ring */ + DRM_MEMORYBARRIER(); + MMIO_W(CP_RB_WPTR, REG_S(CP_RB_WPTR, RB_WPTR, dev_priv->ring_wptr)); + /* read from PCI bus to ensure correct posting */ + MMIO_R(CP_RB_WPTR); + spin_unlock(&ring_lock); + return 0; +} diff --git a/shared-core/radeon_ms_cp_mc.c b/shared-core/radeon_ms_cp_mc.c new file mode 100644 index 00000000..f0397d87 --- /dev/null +++ b/shared-core/radeon_ms_cp_mc.c @@ -0,0 +1,801 @@ +/* + * Copyright 2007 Advanced Micro Devices, 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. + */ +#include "radeon_ms.h" + +/* CP microcode (from ATI) */ + +const uint32_t radeon_cp_microcode[] = { + 0x21007000, 0000000000, + 0x20007000, 0000000000, + 0x000000b4, 0x00000004, + 0x000000b8, 0x00000004, + 0x6f5b4d4c, 0000000000, + 0x4c4c427f, 0000000000, + 0x5b568a92, 0000000000, + 0x4ca09c6d, 0000000000, + 0xad4c4c4c, 0000000000, + 0x4ce1af3d, 0000000000, + 0xd8afafaf, 0000000000, + 0xd64c4cdc, 0000000000, + 0x4cd10d10, 0000000000, + 0x000f0000, 0x00000016, + 0x362f242d, 0000000000, + 0x00000012, 0x00000004, + 0x000f0000, 0x00000016, + 0x362f282d, 0000000000, + 0x000380e7, 0x00000002, + 0x04002c97, 0x00000002, + 0x000f0001, 0x00000016, + 0x333a3730, 0000000000, + 0x000077ef, 0x00000002, + 0x00061000, 0x00000002, + 0x00000021, 0x0000001a, + 0x00004000, 0x0000001e, + 0x00061000, 0x00000002, + 0x00000021, 0x0000001a, + 0x00004000, 0x0000001e, + 0x00061000, 0x00000002, + 0x00000021, 0x0000001a, + 0x00004000, 0x0000001e, + 0x00000017, 0x00000004, + 0x0003802b, 0x00000002, + 0x040067e0, 0x00000002, + 0x00000017, 0x00000004, + 0x000077e0, 0x00000002, + 0x00065000, 0x00000002, + 0x000037e1, 0x00000002, + 0x040067e1, 0x00000006, + 0x000077e0, 0x00000002, + 0x000077e1, 0x00000002, + 0x000077e1, 0x00000006, + 0xffffffff, 0000000000, + 0x10000000, 0000000000, + 0x0003802b, 0x00000002, + 0x040067e0, 0x00000006, + 0x00007675, 0x00000002, + 0x00007676, 0x00000002, + 0x00007677, 0x00000002, + 0x00007678, 0x00000006, + 0x0003802c, 0x00000002, + 0x04002676, 0x00000002, + 0x00007677, 0x00000002, + 0x00007678, 0x00000006, + 0x0000002f, 0x00000018, + 0x0000002f, 0x00000018, + 0000000000, 0x00000006, + 0x00000030, 0x00000018, + 0x00000030, 0x00000018, + 0000000000, 0x00000006, + 0x01605000, 0x00000002, + 0x00065000, 0x00000002, + 0x00098000, 0x00000002, + 0x00061000, 0x00000002, + 0x64c0603e, 0x00000004, + 0x000380e6, 0x00000002, + 0x040025c5, 0x00000002, + 0x00080000, 0x00000016, + 0000000000, 0000000000, + 0x0400251d, 0x00000002, + 0x00007580, 0x00000002, + 0x00067581, 0x00000002, + 0x04002580, 0x00000002, + 0x00067581, 0x00000002, + 0x00000049, 0x00000004, + 0x00005000, 0000000000, + 0x000380e6, 0x00000002, + 0x040025c5, 0x00000002, + 0x00061000, 0x00000002, + 0x0000750e, 0x00000002, + 0x00019000, 0x00000002, + 0x00011055, 0x00000014, + 0x00000055, 0x00000012, + 0x0400250f, 0x00000002, + 0x0000504f, 0x00000004, + 0x000380e6, 0x00000002, + 0x040025c5, 0x00000002, + 0x00007565, 0x00000002, + 0x00007566, 0x00000002, + 0x00000058, 0x00000004, + 0x000380e6, 0x00000002, + 0x040025c5, 0x00000002, + 0x01e655b4, 0x00000002, + 0x4401b0e4, 0x00000002, + 0x01c110e4, 0x00000002, + 0x26667066, 0x00000018, + 0x040c2565, 0x00000002, + 0x00000066, 0x00000018, + 0x04002564, 0x00000002, + 0x00007566, 0x00000002, + 0x0000005d, 0x00000004, + 0x00401069, 0x00000008, + 0x00101000, 0x00000002, + 0x000d80ff, 0x00000002, + 0x0080006c, 0x00000008, + 0x000f9000, 0x00000002, + 0x000e00ff, 0x00000002, + 0000000000, 0x00000006, + 0x0000008f, 0x00000018, + 0x0000005b, 0x00000004, + 0x000380e6, 0x00000002, + 0x040025c5, 0x00000002, + 0x00007576, 0x00000002, + 0x00065000, 0x00000002, + 0x00009000, 0x00000002, + 0x00041000, 0x00000002, + 0x0c00350e, 0x00000002, + 0x00049000, 0x00000002, + 0x00051000, 0x00000002, + 0x01e785f8, 0x00000002, + 0x00200000, 0x00000002, + 0x0060007e, 0x0000000c, + 0x00007563, 0x00000002, + 0x006075f0, 0x00000021, + 0x20007073, 0x00000004, + 0x00005073, 0x00000004, + 0x000380e6, 0x00000002, + 0x040025c5, 0x00000002, + 0x00007576, 0x00000002, + 0x00007577, 0x00000002, + 0x0000750e, 0x00000002, + 0x0000750f, 0x00000002, + 0x00a05000, 0x00000002, + 0x00600083, 0x0000000c, + 0x006075f0, 0x00000021, + 0x000075f8, 0x00000002, + 0x00000083, 0x00000004, + 0x000a750e, 0x00000002, + 0x000380e6, 0x00000002, + 0x040025c5, 0x00000002, + 0x0020750f, 0x00000002, + 0x00600086, 0x00000004, + 0x00007570, 0x00000002, + 0x00007571, 0x00000002, + 0x00007572, 0x00000006, + 0x000380e6, 0x00000002, + 0x040025c5, 0x00000002, + 0x00005000, 0x00000002, + 0x00a05000, 0x00000002, + 0x00007568, 0x00000002, + 0x00061000, 0x00000002, + 0x00000095, 0x0000000c, + 0x00058000, 0x00000002, + 0x0c607562, 0x00000002, + 0x00000097, 0x00000004, + 0x000380e6, 0x00000002, + 0x040025c5, 0x00000002, + 0x00600096, 0x00000004, + 0x400070e5, 0000000000, + 0x000380e6, 0x00000002, + 0x040025c5, 0x00000002, + 0x000380e5, 0x00000002, + 0x000000a8, 0x0000001c, + 0x000650aa, 0x00000018, + 0x040025bb, 0x00000002, + 0x000610ab, 0x00000018, + 0x040075bc, 0000000000, + 0x000075bb, 0x00000002, + 0x000075bc, 0000000000, + 0x00090000, 0x00000006, + 0x00090000, 0x00000002, + 0x000d8002, 0x00000006, + 0x00007832, 0x00000002, + 0x00005000, 0x00000002, + 0x000380e7, 0x00000002, + 0x04002c97, 0x00000002, + 0x00007820, 0x00000002, + 0x00007821, 0x00000002, + 0x00007800, 0000000000, + 0x01200000, 0x00000002, + 0x20077000, 0x00000002, + 0x01200000, 0x00000002, + 0x20007000, 0x00000002, + 0x00061000, 0x00000002, + 0x0120751b, 0x00000002, + 0x8040750a, 0x00000002, + 0x8040750b, 0x00000002, + 0x00110000, 0x00000002, + 0x000380e5, 0x00000002, + 0x000000c6, 0x0000001c, + 0x000610ab, 0x00000018, + 0x844075bd, 0x00000002, + 0x000610aa, 0x00000018, + 0x840075bb, 0x00000002, + 0x000610ab, 0x00000018, + 0x844075bc, 0x00000002, + 0x000000c9, 0x00000004, + 0x804075bd, 0x00000002, + 0x800075bb, 0x00000002, + 0x804075bc, 0x00000002, + 0x00108000, 0x00000002, + 0x01400000, 0x00000002, + 0x006000cd, 0x0000000c, + 0x20c07000, 0x00000020, + 0x000000cf, 0x00000012, + 0x00800000, 0x00000006, + 0x0080751d, 0x00000006, + 0000000000, 0000000000, + 0x0000775c, 0x00000002, + 0x00a05000, 0x00000002, + 0x00661000, 0x00000002, + 0x0460275d, 0x00000020, + 0x00004000, 0000000000, + 0x01e00830, 0x00000002, + 0x21007000, 0000000000, + 0x6464614d, 0000000000, + 0x69687420, 0000000000, + 0x00000073, 0000000000, + 0000000000, 0000000000, + 0x00005000, 0x00000002, + 0x000380d0, 0x00000002, + 0x040025e0, 0x00000002, + 0x000075e1, 0000000000, + 0x00000001, 0000000000, + 0x000380e0, 0x00000002, + 0x04002394, 0x00000002, + 0x00005000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0x00000008, 0000000000, + 0x00000004, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, +}; + +const uint32_t r200_cp_microcode[] = { + 0x21007000, 0000000000, + 0x20007000, 0000000000, + 0x000000ab, 0x00000004, + 0x000000af, 0x00000004, + 0x66544a49, 0000000000, + 0x49494174, 0000000000, + 0x54517d83, 0000000000, + 0x498d8b64, 0000000000, + 0x49494949, 0000000000, + 0x49da493c, 0000000000, + 0x49989898, 0000000000, + 0xd34949d5, 0000000000, + 0x9dc90e11, 0000000000, + 0xce9b9b9b, 0000000000, + 0x000f0000, 0x00000016, + 0x352e232c, 0000000000, + 0x00000013, 0x00000004, + 0x000f0000, 0x00000016, + 0x352e272c, 0000000000, + 0x000f0001, 0x00000016, + 0x3239362f, 0000000000, + 0x000077ef, 0x00000002, + 0x00061000, 0x00000002, + 0x00000020, 0x0000001a, + 0x00004000, 0x0000001e, + 0x00061000, 0x00000002, + 0x00000020, 0x0000001a, + 0x00004000, 0x0000001e, + 0x00061000, 0x00000002, + 0x00000020, 0x0000001a, + 0x00004000, 0x0000001e, + 0x00000016, 0x00000004, + 0x0003802a, 0x00000002, + 0x040067e0, 0x00000002, + 0x00000016, 0x00000004, + 0x000077e0, 0x00000002, + 0x00065000, 0x00000002, + 0x000037e1, 0x00000002, + 0x040067e1, 0x00000006, + 0x000077e0, 0x00000002, + 0x000077e1, 0x00000002, + 0x000077e1, 0x00000006, + 0xffffffff, 0000000000, + 0x10000000, 0000000000, + 0x0003802a, 0x00000002, + 0x040067e0, 0x00000006, + 0x00007675, 0x00000002, + 0x00007676, 0x00000002, + 0x00007677, 0x00000002, + 0x00007678, 0x00000006, + 0x0003802b, 0x00000002, + 0x04002676, 0x00000002, + 0x00007677, 0x00000002, + 0x00007678, 0x00000006, + 0x0000002e, 0x00000018, + 0x0000002e, 0x00000018, + 0000000000, 0x00000006, + 0x0000002f, 0x00000018, + 0x0000002f, 0x00000018, + 0000000000, 0x00000006, + 0x01605000, 0x00000002, + 0x00065000, 0x00000002, + 0x00098000, 0x00000002, + 0x00061000, 0x00000002, + 0x64c0603d, 0x00000004, + 0x00080000, 0x00000016, + 0000000000, 0000000000, + 0x0400251d, 0x00000002, + 0x00007580, 0x00000002, + 0x00067581, 0x00000002, + 0x04002580, 0x00000002, + 0x00067581, 0x00000002, + 0x00000046, 0x00000004, + 0x00005000, 0000000000, + 0x00061000, 0x00000002, + 0x0000750e, 0x00000002, + 0x00019000, 0x00000002, + 0x00011055, 0x00000014, + 0x00000055, 0x00000012, + 0x0400250f, 0x00000002, + 0x0000504a, 0x00000004, + 0x00007565, 0x00000002, + 0x00007566, 0x00000002, + 0x00000051, 0x00000004, + 0x01e655b4, 0x00000002, + 0x4401b0dc, 0x00000002, + 0x01c110dc, 0x00000002, + 0x2666705d, 0x00000018, + 0x040c2565, 0x00000002, + 0x0000005d, 0x00000018, + 0x04002564, 0x00000002, + 0x00007566, 0x00000002, + 0x00000054, 0x00000004, + 0x00401060, 0x00000008, + 0x00101000, 0x00000002, + 0x000d80ff, 0x00000002, + 0x00800063, 0x00000008, + 0x000f9000, 0x00000002, + 0x000e00ff, 0x00000002, + 0000000000, 0x00000006, + 0x00000080, 0x00000018, + 0x00000054, 0x00000004, + 0x00007576, 0x00000002, + 0x00065000, 0x00000002, + 0x00009000, 0x00000002, + 0x00041000, 0x00000002, + 0x0c00350e, 0x00000002, + 0x00049000, 0x00000002, + 0x00051000, 0x00000002, + 0x01e785f8, 0x00000002, + 0x00200000, 0x00000002, + 0x00600073, 0x0000000c, + 0x00007563, 0x00000002, + 0x006075f0, 0x00000021, + 0x20007068, 0x00000004, + 0x00005068, 0x00000004, + 0x00007576, 0x00000002, + 0x00007577, 0x00000002, + 0x0000750e, 0x00000002, + 0x0000750f, 0x00000002, + 0x00a05000, 0x00000002, + 0x00600076, 0x0000000c, + 0x006075f0, 0x00000021, + 0x000075f8, 0x00000002, + 0x00000076, 0x00000004, + 0x000a750e, 0x00000002, + 0x0020750f, 0x00000002, + 0x00600079, 0x00000004, + 0x00007570, 0x00000002, + 0x00007571, 0x00000002, + 0x00007572, 0x00000006, + 0x00005000, 0x00000002, + 0x00a05000, 0x00000002, + 0x00007568, 0x00000002, + 0x00061000, 0x00000002, + 0x00000084, 0x0000000c, + 0x00058000, 0x00000002, + 0x0c607562, 0x00000002, + 0x00000086, 0x00000004, + 0x00600085, 0x00000004, + 0x400070dd, 0000000000, + 0x000380dd, 0x00000002, + 0x00000093, 0x0000001c, + 0x00065095, 0x00000018, + 0x040025bb, 0x00000002, + 0x00061096, 0x00000018, + 0x040075bc, 0000000000, + 0x000075bb, 0x00000002, + 0x000075bc, 0000000000, + 0x00090000, 0x00000006, + 0x00090000, 0x00000002, + 0x000d8002, 0x00000006, + 0x00005000, 0x00000002, + 0x00007821, 0x00000002, + 0x00007800, 0000000000, + 0x00007821, 0x00000002, + 0x00007800, 0000000000, + 0x01665000, 0x00000002, + 0x000a0000, 0x00000002, + 0x000671cc, 0x00000002, + 0x0286f1cd, 0x00000002, + 0x000000a3, 0x00000010, + 0x21007000, 0000000000, + 0x000000aa, 0x0000001c, + 0x00065000, 0x00000002, + 0x000a0000, 0x00000002, + 0x00061000, 0x00000002, + 0x000b0000, 0x00000002, + 0x38067000, 0x00000002, + 0x000a00a6, 0x00000004, + 0x20007000, 0000000000, + 0x01200000, 0x00000002, + 0x20077000, 0x00000002, + 0x01200000, 0x00000002, + 0x20007000, 0000000000, + 0x00061000, 0x00000002, + 0x0120751b, 0x00000002, + 0x8040750a, 0x00000002, + 0x8040750b, 0x00000002, + 0x00110000, 0x00000002, + 0x000380dd, 0x00000002, + 0x000000bd, 0x0000001c, + 0x00061096, 0x00000018, + 0x844075bd, 0x00000002, + 0x00061095, 0x00000018, + 0x840075bb, 0x00000002, + 0x00061096, 0x00000018, + 0x844075bc, 0x00000002, + 0x000000c0, 0x00000004, + 0x804075bd, 0x00000002, + 0x800075bb, 0x00000002, + 0x804075bc, 0x00000002, + 0x00108000, 0x00000002, + 0x01400000, 0x00000002, + 0x006000c4, 0x0000000c, + 0x20c07000, 0x00000020, + 0x000000c6, 0x00000012, + 0x00800000, 0x00000006, + 0x0080751d, 0x00000006, + 0x000025bb, 0x00000002, + 0x000040c0, 0x00000004, + 0x0000775c, 0x00000002, + 0x00a05000, 0x00000002, + 0x00661000, 0x00000002, + 0x0460275d, 0x00000020, + 0x00004000, 0000000000, + 0x00007999, 0x00000002, + 0x00a05000, 0x00000002, + 0x00661000, 0x00000002, + 0x0460299b, 0x00000020, + 0x00004000, 0000000000, + 0x01e00830, 0x00000002, + 0x21007000, 0000000000, + 0x00005000, 0x00000002, + 0x00038042, 0x00000002, + 0x040025e0, 0x00000002, + 0x000075e1, 0000000000, + 0x00000001, 0000000000, + 0x000380d9, 0x00000002, + 0x04007394, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, +}; + +const uint32_t r300_cp_microcode[] = { + 0x4200e000, 0000000000, + 0x4000e000, 0000000000, + 0x000000af, 0x00000008, + 0x000000b3, 0x00000008, + 0x6c5a504f, 0000000000, + 0x4f4f497a, 0000000000, + 0x5a578288, 0000000000, + 0x4f91906a, 0000000000, + 0x4f4f4f4f, 0000000000, + 0x4fe24f44, 0000000000, + 0x4f9c9c9c, 0000000000, + 0xdc4f4fde, 0000000000, + 0xa1cd4f4f, 0000000000, + 0xd29d9d9d, 0000000000, + 0x4f0f9fd7, 0000000000, + 0x000ca000, 0x00000004, + 0x000d0012, 0x00000038, + 0x0000e8b4, 0x00000004, + 0x000d0014, 0x00000038, + 0x0000e8b6, 0x00000004, + 0x000d0016, 0x00000038, + 0x0000e854, 0x00000004, + 0x000d0018, 0x00000038, + 0x0000e855, 0x00000004, + 0x000d001a, 0x00000038, + 0x0000e856, 0x00000004, + 0x000d001c, 0x00000038, + 0x0000e857, 0x00000004, + 0x000d001e, 0x00000038, + 0x0000e824, 0x00000004, + 0x000d0020, 0x00000038, + 0x0000e825, 0x00000004, + 0x000d0022, 0x00000038, + 0x0000e830, 0x00000004, + 0x000d0024, 0x00000038, + 0x0000f0c0, 0x00000004, + 0x000d0026, 0x00000038, + 0x0000f0c1, 0x00000004, + 0x000d0028, 0x00000038, + 0x0000f041, 0x00000004, + 0x000d002a, 0x00000038, + 0x0000f184, 0x00000004, + 0x000d002c, 0x00000038, + 0x0000f185, 0x00000004, + 0x000d002e, 0x00000038, + 0x0000f186, 0x00000004, + 0x000d0030, 0x00000038, + 0x0000f187, 0x00000004, + 0x000d0032, 0x00000038, + 0x0000f180, 0x00000004, + 0x000d0034, 0x00000038, + 0x0000f393, 0x00000004, + 0x000d0036, 0x00000038, + 0x0000f38a, 0x00000004, + 0x000d0038, 0x00000038, + 0x0000f38e, 0x00000004, + 0x0000e821, 0x00000004, + 0x0140a000, 0x00000004, + 0x00000043, 0x00000018, + 0x00cce800, 0x00000004, + 0x001b0001, 0x00000004, + 0x08004800, 0x00000004, + 0x001b0001, 0x00000004, + 0x08004800, 0x00000004, + 0x001b0001, 0x00000004, + 0x08004800, 0x00000004, + 0x0000003a, 0x00000008, + 0x0000a000, 0000000000, + 0x02c0a000, 0x00000004, + 0x000ca000, 0x00000004, + 0x00130000, 0x00000004, + 0x000c2000, 0x00000004, + 0xc980c045, 0x00000008, + 0x2000451d, 0x00000004, + 0x0000e580, 0x00000004, + 0x000ce581, 0x00000004, + 0x08004580, 0x00000004, + 0x000ce581, 0x00000004, + 0x0000004c, 0x00000008, + 0x0000a000, 0000000000, + 0x000c2000, 0x00000004, + 0x0000e50e, 0x00000004, + 0x00032000, 0x00000004, + 0x00022056, 0x00000028, + 0x00000056, 0x00000024, + 0x0800450f, 0x00000004, + 0x0000a050, 0x00000008, + 0x0000e565, 0x00000004, + 0x0000e566, 0x00000004, + 0x00000057, 0x00000008, + 0x03cca5b4, 0x00000004, + 0x05432000, 0x00000004, + 0x00022000, 0x00000004, + 0x4ccce063, 0x00000030, + 0x08274565, 0x00000004, + 0x00000063, 0x00000030, + 0x08004564, 0x00000004, + 0x0000e566, 0x00000004, + 0x0000005a, 0x00000008, + 0x00802066, 0x00000010, + 0x00202000, 0x00000004, + 0x001b00ff, 0x00000004, + 0x01000069, 0x00000010, + 0x001f2000, 0x00000004, + 0x001c00ff, 0x00000004, + 0000000000, 0x0000000c, + 0x00000085, 0x00000030, + 0x0000005a, 0x00000008, + 0x0000e576, 0x00000004, + 0x000ca000, 0x00000004, + 0x00012000, 0x00000004, + 0x00082000, 0x00000004, + 0x1800650e, 0x00000004, + 0x00092000, 0x00000004, + 0x000a2000, 0x00000004, + 0x000f0000, 0x00000004, + 0x00400000, 0x00000004, + 0x00000079, 0x00000018, + 0x0000e563, 0x00000004, + 0x00c0e5f9, 0x000000c2, + 0x0000006e, 0x00000008, + 0x0000a06e, 0x00000008, + 0x0000e576, 0x00000004, + 0x0000e577, 0x00000004, + 0x0000e50e, 0x00000004, + 0x0000e50f, 0x00000004, + 0x0140a000, 0x00000004, + 0x0000007c, 0x00000018, + 0x00c0e5f9, 0x000000c2, + 0x0000007c, 0x00000008, + 0x0014e50e, 0x00000004, + 0x0040e50f, 0x00000004, + 0x00c0007f, 0x00000008, + 0x0000e570, 0x00000004, + 0x0000e571, 0x00000004, + 0x0000e572, 0x0000000c, + 0x0000a000, 0x00000004, + 0x0140a000, 0x00000004, + 0x0000e568, 0x00000004, + 0x000c2000, 0x00000004, + 0x00000089, 0x00000018, + 0x000b0000, 0x00000004, + 0x18c0e562, 0x00000004, + 0x0000008b, 0x00000008, + 0x00c0008a, 0x00000008, + 0x000700e4, 0x00000004, + 0x00000097, 0x00000038, + 0x000ca099, 0x00000030, + 0x080045bb, 0x00000004, + 0x000c209a, 0x00000030, + 0x0800e5bc, 0000000000, + 0x0000e5bb, 0x00000004, + 0x0000e5bc, 0000000000, + 0x00120000, 0x0000000c, + 0x00120000, 0x00000004, + 0x001b0002, 0x0000000c, + 0x0000a000, 0x00000004, + 0x0000e821, 0x00000004, + 0x0000e800, 0000000000, + 0x0000e821, 0x00000004, + 0x0000e82e, 0000000000, + 0x02cca000, 0x00000004, + 0x00140000, 0x00000004, + 0x000ce1cc, 0x00000004, + 0x050de1cd, 0x00000004, + 0x000000a7, 0x00000020, + 0x4200e000, 0000000000, + 0x000000ae, 0x00000038, + 0x000ca000, 0x00000004, + 0x00140000, 0x00000004, + 0x000c2000, 0x00000004, + 0x00160000, 0x00000004, + 0x700ce000, 0x00000004, + 0x001400aa, 0x00000008, + 0x4000e000, 0000000000, + 0x02400000, 0x00000004, + 0x400ee000, 0x00000004, + 0x02400000, 0x00000004, + 0x4000e000, 0000000000, + 0x000c2000, 0x00000004, + 0x0240e51b, 0x00000004, + 0x0080e50a, 0x00000005, + 0x0080e50b, 0x00000005, + 0x00220000, 0x00000004, + 0x000700e4, 0x00000004, + 0x000000c1, 0x00000038, + 0x000c209a, 0x00000030, + 0x0880e5bd, 0x00000005, + 0x000c2099, 0x00000030, + 0x0800e5bb, 0x00000005, + 0x000c209a, 0x00000030, + 0x0880e5bc, 0x00000005, + 0x000000c4, 0x00000008, + 0x0080e5bd, 0x00000005, + 0x0000e5bb, 0x00000005, + 0x0080e5bc, 0x00000005, + 0x00210000, 0x00000004, + 0x02800000, 0x00000004, + 0x00c000c8, 0x00000018, + 0x4180e000, 0x00000040, + 0x000000ca, 0x00000024, + 0x01000000, 0x0000000c, + 0x0100e51d, 0x0000000c, + 0x000045bb, 0x00000004, + 0x000080c4, 0x00000008, + 0x0000f3ce, 0x00000004, + 0x0140a000, 0x00000004, + 0x00cc2000, 0x00000004, + 0x08c053cf, 0x00000040, + 0x00008000, 0000000000, + 0x0000f3d2, 0x00000004, + 0x0140a000, 0x00000004, + 0x00cc2000, 0x00000004, + 0x08c053d3, 0x00000040, + 0x00008000, 0000000000, + 0x0000f39d, 0x00000004, + 0x0140a000, 0x00000004, + 0x00cc2000, 0x00000004, + 0x08c0539e, 0x00000040, + 0x00008000, 0000000000, + 0x03c00830, 0x00000004, + 0x4200e000, 0000000000, + 0x0000a000, 0x00000004, + 0x200045e0, 0x00000004, + 0x0000e5e1, 0000000000, + 0x00000001, 0000000000, + 0x000700e1, 0x00000004, + 0x0800e394, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, + 0000000000, 0000000000, +}; diff --git a/shared-core/radeon_ms_crtc.c b/shared-core/radeon_ms_crtc.c new file mode 100644 index 00000000..fe89e5e4 --- /dev/null +++ b/shared-core/radeon_ms_crtc.c @@ -0,0 +1,800 @@ +/* + * Copyright © 2007 Alex Deucher + * Copyright © 2007 Dave Airlie + * Copyright © 2007 Michel Dänzer + * Copyright © 2007 Jerome Glisse + * + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation on the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT. IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR + * THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "radeon_ms.h" + +static void radeon_pll1_init(struct drm_radeon_private *dev_priv, + struct radeon_state *state); +static void radeon_pll1_restore(struct drm_radeon_private *dev_priv, + struct radeon_state *state); +static void radeon_pll1_save(struct drm_radeon_private *dev_priv, + struct radeon_state *state); +static void radeon_ms_crtc_load_lut(struct drm_crtc *crtc); + +/** + * radeon_ms_crtc1_init - initialize CRTC state + * @dev_priv: radeon private structure + * @state: state structure to initialize to default value + * + * Initialize CRTC state to default values + */ +static void radeon_ms_crtc1_init(struct drm_radeon_private *dev_priv, + struct radeon_state *state) +{ + state->surface_cntl = SURFACE_CNTL__SURF_TRANSLATION_DIS; + state->surface0_info = 0; + state->surface0_lower_bound = 0; + state->surface0_upper_bound = 0; + state->surface1_info = 0; + state->surface1_lower_bound = 0; + state->surface1_upper_bound = 0; + state->surface2_info = 0; + state->surface2_lower_bound = 0; + state->surface2_upper_bound = 0; + state->surface3_info = 0; + state->surface3_lower_bound = 0; + state->surface3_upper_bound = 0; + state->surface4_info = 0; + state->surface4_lower_bound = 0; + state->surface4_upper_bound = 0; + state->surface5_info = 0; + state->surface5_lower_bound = 0; + state->surface5_upper_bound = 0; + state->surface6_info = 0; + state->surface6_lower_bound = 0; + state->surface6_upper_bound = 0; + state->surface7_info = 0; + state->surface7_lower_bound = 0; + state->surface7_upper_bound = 0; + state->crtc_gen_cntl = CRTC_GEN_CNTL__CRTC_EXT_DISP_EN | + CRTC_GEN_CNTL__CRTC_DISP_REQ_EN_B; + state->crtc_ext_cntl = CRTC_EXT_CNTL__VGA_ATI_LINEAR | + CRTC_EXT_CNTL__VGA_XCRT_CNT_EN | + CRTC_EXT_CNTL__CRT_ON; + state->crtc_h_total_disp = 0; + state->crtc_h_sync_strt_wid = 0; + state->crtc_v_total_disp = 0; + state->crtc_v_sync_strt_wid = 0; + state->crtc_offset = 0; + state->crtc_pitch = 0; + state->crtc_more_cntl = 0; + state->crtc_tile_x0_y0 = 0; + state->crtc_offset_cntl = 0; + switch (dev_priv->family) { + case CHIP_R100: + case CHIP_R200: + case CHIP_RV200: + case CHIP_RV250: + case CHIP_RV280: + case CHIP_RS300: + break; + case CHIP_R300: + case CHIP_R350: + case CHIP_R360: + case CHIP_RV350: + case CHIP_RV370: + case CHIP_RV380: + case CHIP_RS400: + case CHIP_RV410: + case CHIP_R420: + case CHIP_R430: + case CHIP_R480: + state->crtc_offset_cntl |= REG_S(CRTC_OFFSET_CNTL, + CRTC_MICRO_TILE_BUFFER_MODE, + CRTC_MICRO_TILE_BUFFER_MODE__DIS); + break; + default: + DRM_ERROR("Unknown radeon family, aborting\n"); + return; + } + radeon_pll1_init(dev_priv, state); +} + +/** + * radeon_pll1_init - initialize PLL1 state + * @dev_priv: radeon private structure + * @state: state structure to initialize to default value + * + * Initialize PLL1 state to default values + */ +static void radeon_pll1_init(struct drm_radeon_private *dev_priv, + struct radeon_state *state) +{ + state->clock_cntl_index = 0; + state->ppll_cntl = PPLL_R(PPLL_CNTL); + state->ppll_cntl |= PPLL_CNTL__PPLL_ATOMIC_UPDATE_EN | + PPLL_CNTL__PPLL_ATOMIC_UPDATE_SYNC | + PPLL_CNTL__PPLL_VGA_ATOMIC_UPDATE_EN; + state->ppll_cntl &= ~PPLL_CNTL__PPLL_TST_EN; + state->ppll_cntl &= ~PPLL_CNTL__PPLL_TCPOFF; + state->ppll_cntl &= ~PPLL_CNTL__PPLL_TVCOMAX; + state->ppll_cntl &= ~PPLL_CNTL__PPLL_DISABLE_AUTO_RESET; + state->ppll_ref_div = 0; + state->ppll_ref_div = REG_S(PPLL_REF_DIV, PPLL_REF_DIV, 12) | + REG_S(PPLL_REF_DIV, PPLL_REF_DIV_SRC, PPLL_REF_DIV_SRC__XTALIN); + state->ppll_div_0 = 0; + state->ppll_div_1 = 0; + state->ppll_div_2 = 0; + state->ppll_div_3 = 0; + state->vclk_ecp_cntl = 0; + state->htotal_cntl = 0; +} + +/** + * radeon_ms_crtc1_restore - restore CRTC state + * @dev_priv: radeon private structure + * @state: CRTC state to restore + */ +void radeon_ms_crtc1_restore(struct drm_device *dev, struct radeon_state *state) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + /* We prevent the CRTC from hitting the memory controller until + * fully programmed + */ + MMIO_W(CRTC_GEN_CNTL, ~CRTC_GEN_CNTL__CRTC_DISP_REQ_EN_B & + state->crtc_gen_cntl); + MMIO_W(CRTC_EXT_CNTL, CRTC_EXT_CNTL__CRTC_VSYNC_DIS | + CRTC_EXT_CNTL__CRTC_HSYNC_DIS | + CRTC_EXT_CNTL__CRTC_DISPLAY_DIS | + state->crtc_ext_cntl); + MMIO_W(SURFACE_CNTL, state->surface_cntl); + MMIO_W(SURFACE0_INFO, state->surface0_info); + MMIO_W(SURFACE0_LOWER_BOUND, state->surface0_lower_bound); + MMIO_W(SURFACE0_UPPER_BOUND, state->surface0_upper_bound); + MMIO_W(SURFACE1_INFO, state->surface1_info); + MMIO_W(SURFACE1_LOWER_BOUND, state->surface1_lower_bound); + MMIO_W(SURFACE1_UPPER_BOUND, state->surface1_upper_bound); + MMIO_W(SURFACE2_INFO, state->surface2_info); + MMIO_W(SURFACE2_LOWER_BOUND, state->surface2_lower_bound); + MMIO_W(SURFACE2_UPPER_BOUND, state->surface2_upper_bound); + MMIO_W(SURFACE3_INFO, state->surface3_info); + MMIO_W(SURFACE3_LOWER_BOUND, state->surface3_lower_bound); + MMIO_W(SURFACE3_UPPER_BOUND, state->surface3_upper_bound); + MMIO_W(SURFACE4_INFO, state->surface4_info); + MMIO_W(SURFACE4_LOWER_BOUND, state->surface4_lower_bound); + MMIO_W(SURFACE4_UPPER_BOUND, state->surface4_upper_bound); + MMIO_W(SURFACE5_INFO, state->surface5_info); + MMIO_W(SURFACE5_LOWER_BOUND, state->surface5_lower_bound); + MMIO_W(SURFACE5_UPPER_BOUND, state->surface5_upper_bound); + MMIO_W(SURFACE6_INFO, state->surface6_info); + MMIO_W(SURFACE6_LOWER_BOUND, state->surface6_lower_bound); + MMIO_W(SURFACE6_UPPER_BOUND, state->surface6_upper_bound); + MMIO_W(SURFACE7_INFO, state->surface7_info); + MMIO_W(SURFACE7_LOWER_BOUND, state->surface7_lower_bound); + MMIO_W(SURFACE7_UPPER_BOUND, state->surface7_upper_bound); + MMIO_W(CRTC_H_TOTAL_DISP, state->crtc_h_total_disp); + MMIO_W(CRTC_H_SYNC_STRT_WID, state->crtc_h_sync_strt_wid); + MMIO_W(CRTC_V_TOTAL_DISP, state->crtc_v_total_disp); + MMIO_W(CRTC_V_SYNC_STRT_WID, state->crtc_v_sync_strt_wid); + MMIO_W(FP_H_SYNC_STRT_WID, state->fp_h_sync_strt_wid); + MMIO_W(FP_V_SYNC_STRT_WID, state->fp_v_sync_strt_wid); + MMIO_W(FP_CRTC_H_TOTAL_DISP, state->fp_crtc_h_total_disp); + MMIO_W(FP_CRTC_V_TOTAL_DISP, state->fp_crtc_v_total_disp); + MMIO_W(CRTC_TILE_X0_Y0, state->crtc_tile_x0_y0); + MMIO_W(CRTC_OFFSET_CNTL, state->crtc_offset_cntl); + MMIO_W(CRTC_OFFSET, state->crtc_offset); + MMIO_W(CRTC_PITCH, state->crtc_pitch); + radeon_pll1_restore(dev_priv, state); + MMIO_W(CRTC_MORE_CNTL, state->crtc_more_cntl); + MMIO_W(CRTC_GEN_CNTL, state->crtc_gen_cntl); + MMIO_W(CRTC_EXT_CNTL, state->crtc_ext_cntl); +} + +/** + * radeon_pll1_restore - restore PLL1 state + * @dev_priv: radeon private structure + * @state: PLL1 state to restore + */ +static void radeon_pll1_restore(struct drm_radeon_private *dev_priv, + struct radeon_state *state) +{ + uint32_t tmp; + + /* switch to gpu clock while programing new clock */ + MMIO_W(CLOCK_CNTL_INDEX, state->clock_cntl_index); + tmp = state->vclk_ecp_cntl; + tmp = REG_S(VCLK_ECP_CNTL, VCLK_SRC_SEL, VCLK_SRC_SEL__CPUCLK); + PPLL_W(VCLK_ECP_CNTL, tmp); + /* reset PLL and update atomicly */ + state->ppll_cntl |= PPLL_CNTL__PPLL_ATOMIC_UPDATE_EN | + PPLL_CNTL__PPLL_ATOMIC_UPDATE_SYNC; + + PPLL_W(PPLL_CNTL, state->ppll_cntl | PPLL_CNTL__PPLL_RESET); + PPLL_W(PPLL_REF_DIV, state->ppll_ref_div); + PPLL_W(PPLL_DIV_0, state->ppll_div_0); + PPLL_W(PPLL_DIV_1, state->ppll_div_1); + PPLL_W(PPLL_DIV_2, state->ppll_div_2); + PPLL_W(PPLL_DIV_3, state->ppll_div_3); + PPLL_W(HTOTAL_CNTL, state->htotal_cntl); + + /* update */ + PPLL_W(PPLL_REF_DIV, state->ppll_ref_div | + PPLL_REF_DIV__PPLL_ATOMIC_UPDATE_W); + for (tmp = 0; tmp < 100; tmp++) { + if (!(PPLL_REF_DIV__PPLL_ATOMIC_UPDATE_R & + PPLL_R(PPLL_REF_DIV))) { + break; + } + DRM_UDELAY(10); + } + state->ppll_cntl &= ~PPLL_CNTL__PPLL_RESET; + PPLL_W(PPLL_CNTL, state->ppll_cntl); + PPLL_W(VCLK_ECP_CNTL, state->vclk_ecp_cntl); +} + +/** + * radeon_ms_crtc1_save - save CRTC state + * @dev_priv: radeon private structure + * @state: state where saving current CRTC state + */ +void radeon_ms_crtc1_save(struct drm_device *dev, struct radeon_state *state) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + state->surface_cntl = MMIO_R(SURFACE_CNTL); + state->surface0_info = MMIO_R(SURFACE0_INFO); + state->surface0_lower_bound = MMIO_R(SURFACE0_LOWER_BOUND); + state->surface0_upper_bound = MMIO_R(SURFACE0_UPPER_BOUND); + state->surface1_info = MMIO_R(SURFACE1_INFO); + state->surface1_lower_bound = MMIO_R(SURFACE1_LOWER_BOUND); + state->surface1_upper_bound = MMIO_R(SURFACE1_UPPER_BOUND); + state->surface2_info = MMIO_R(SURFACE2_INFO); + state->surface2_lower_bound = MMIO_R(SURFACE2_LOWER_BOUND); + state->surface2_upper_bound = MMIO_R(SURFACE2_UPPER_BOUND); + state->surface3_info = MMIO_R(SURFACE3_INFO); + state->surface3_lower_bound = MMIO_R(SURFACE3_LOWER_BOUND); + state->surface3_upper_bound = MMIO_R(SURFACE3_UPPER_BOUND); + state->surface4_info = MMIO_R(SURFACE4_INFO); + state->surface4_lower_bound = MMIO_R(SURFACE4_LOWER_BOUND); + state->surface4_upper_bound = MMIO_R(SURFACE4_UPPER_BOUND); + state->surface5_info = MMIO_R(SURFACE5_INFO); + state->surface5_lower_bound = MMIO_R(SURFACE5_LOWER_BOUND); + state->surface5_upper_bound = MMIO_R(SURFACE5_UPPER_BOUND); + state->surface6_info = MMIO_R(SURFACE6_INFO); + state->surface6_lower_bound = MMIO_R(SURFACE6_LOWER_BOUND); + state->surface6_upper_bound = MMIO_R(SURFACE6_UPPER_BOUND); + state->surface7_info = MMIO_R(SURFACE7_INFO); + state->surface7_lower_bound = MMIO_R(SURFACE7_LOWER_BOUND); + state->surface7_upper_bound = MMIO_R(SURFACE7_UPPER_BOUND); + state->crtc_gen_cntl = MMIO_R(CRTC_GEN_CNTL); + state->crtc_ext_cntl = MMIO_R(CRTC_EXT_CNTL); + state->crtc_h_total_disp = MMIO_R(CRTC_H_TOTAL_DISP); + state->crtc_h_sync_strt_wid = MMIO_R(CRTC_H_SYNC_STRT_WID); + state->crtc_v_total_disp = MMIO_R(CRTC_V_TOTAL_DISP); + state->crtc_v_sync_strt_wid = MMIO_R(CRTC_V_SYNC_STRT_WID); + state->fp_h_sync_strt_wid = MMIO_R(FP_H_SYNC_STRT_WID); + state->fp_v_sync_strt_wid = MMIO_R(FP_V_SYNC_STRT_WID); + state->fp_crtc_h_total_disp = MMIO_R(FP_CRTC_H_TOTAL_DISP); + state->fp_crtc_v_total_disp = MMIO_R(FP_CRTC_V_TOTAL_DISP); + state->crtc_offset = MMIO_R(CRTC_OFFSET); + state->crtc_offset_cntl = MMIO_R(CRTC_OFFSET_CNTL); + state->crtc_pitch = MMIO_R(CRTC_PITCH); + state->crtc_more_cntl = MMIO_R(CRTC_MORE_CNTL); + state->crtc_tile_x0_y0 = MMIO_R(CRTC_TILE_X0_Y0); + radeon_pll1_save(dev_priv,state); +} + +/** + * radeon_pll1_save - save PLL1 state + * @dev_priv: radeon private structure + * @state: state where saving current PLL1 state + */ +static void radeon_pll1_save(struct drm_radeon_private *dev_priv, + struct radeon_state *state) +{ + state->clock_cntl_index = MMIO_R(CLOCK_CNTL_INDEX); + state->ppll_cntl = PPLL_R(PPLL_CNTL); + state->ppll_ref_div = PPLL_R(PPLL_REF_DIV); + state->ppll_div_0 = PPLL_R(PPLL_DIV_0); + state->ppll_div_1 = PPLL_R(PPLL_DIV_1); + state->ppll_div_2 = PPLL_R(PPLL_DIV_2); + state->ppll_div_3 = PPLL_R(PPLL_DIV_3); + state->vclk_ecp_cntl = PPLL_R(VCLK_ECP_CNTL); + state->htotal_cntl = PPLL_R(HTOTAL_CNTL); +} + +static void radeon_ms_crtc1_dpms(struct drm_crtc *crtc, int mode) +{ + struct drm_radeon_private *dev_priv = crtc->dev->dev_private; + struct radeon_state *state = &dev_priv->driver_state; + + state->crtc_gen_cntl &= ~CRTC_GEN_CNTL__CRTC_DISP_REQ_EN_B; + state->crtc_ext_cntl &= ~CRTC_EXT_CNTL__CRTC_DISPLAY_DIS; + state->crtc_ext_cntl &= ~CRTC_EXT_CNTL__CRTC_HSYNC_DIS; + state->crtc_ext_cntl &= ~CRTC_EXT_CNTL__CRTC_VSYNC_DIS; + switch(mode) { + case DPMSModeOn: + break; + case DPMSModeStandby: + state->crtc_ext_cntl |= + CRTC_EXT_CNTL__CRTC_DISPLAY_DIS | + CRTC_EXT_CNTL__CRTC_HSYNC_DIS; + break; + case DPMSModeSuspend: + state->crtc_ext_cntl |= + CRTC_EXT_CNTL__CRTC_DISPLAY_DIS | + CRTC_EXT_CNTL__CRTC_VSYNC_DIS; + break; + case DPMSModeOff: + state->crtc_ext_cntl |= + CRTC_EXT_CNTL__CRTC_DISPLAY_DIS | + CRTC_EXT_CNTL__CRTC_HSYNC_DIS | + CRTC_EXT_CNTL__CRTC_VSYNC_DIS; + state->crtc_gen_cntl |= + CRTC_GEN_CNTL__CRTC_DISP_REQ_EN_B; + break; + } + MMIO_W(CRTC_GEN_CNTL, state->crtc_gen_cntl); + MMIO_W(CRTC_EXT_CNTL, state->crtc_ext_cntl); + + dev_priv->crtc1_dpms = mode; + /* FIXME: once adding crtc2 remove this */ + dev_priv->crtc2_dpms = mode; + radeon_ms_gpu_dpms(crtc->dev); + + if (mode != DPMSModeOff) { + radeon_ms_crtc_load_lut(crtc); + } +} + +static bool radeon_ms_crtc_mode_fixup(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void radeon_ms_crtc_mode_prepare(struct drm_crtc *crtc) +{ + crtc->funcs->dpms(crtc, DPMSModeOff); +} + +/* compute PLL registers values for requested video mode */ +static int radeon_pll1_constraint(int clock, int rdiv, + int fdiv, int pdiv, + int rfrq, int pfrq) +{ + int dfrq; + + if (rdiv < 2 || fdiv < 4) { + return 0; + } + dfrq = rfrq / rdiv; + if (dfrq < 2000 || dfrq > 3300) { + return 0; + } + if (pfrq < 125000 || pfrq > 250000) { + return 0; + } + return 1; +} + +static void radeon_pll1_compute(struct drm_crtc *crtc, + struct drm_display_mode *mode) +{ + struct { + int divider; + int divider_id; + } *post_div, post_divs[] = { + /* From RAGE 128 VR/RAGE 128 GL Register + * Reference Manual (Technical Reference + * Manual P/N RRG-G04100-C Rev. 0.04), page + * 3-17 (PLL_DIV_[3:0]). + */ + { 1, 0 }, /* VCLK_SRC */ + { 2, 1 }, /* VCLK_SRC/2 */ + { 4, 2 }, /* VCLK_SRC/4 */ + { 8, 3 }, /* VCLK_SRC/8 */ + { 3, 4 }, /* VCLK_SRC/3 */ + { 16, 5 }, /* VCLK_SRC/16 */ + { 6, 6 }, /* VCLK_SRC/6 */ + { 12, 7 }, /* VCLK_SRC/12 */ + { 0, 0 } + }; + struct drm_radeon_private *dev_priv = crtc->dev->dev_private; + struct radeon_state *state = &dev_priv->driver_state; + int clock = mode->clock; + int rfrq = dev_priv->properties->pll_reference_freq; + int pdiv = 1; + int pdiv_id = 0; + int rdiv_best = 2; + int fdiv_best = 4; + int tfrq_best = 0; + int pfrq_best = 0; + int diff_cpfrq_best = 350000; + int vco_freq; + int vco_gain; + int rdiv = 0; + int fdiv = 0; + int tfrq = 35000; + int pfrq = 35000; + int diff_cpfrq = 350000; + + /* clamp frequency into pll [min; max] frequency range */ + if (clock > dev_priv->properties->pll_max_pll_freq) { + clock = dev_priv->properties->pll_max_pll_freq; + } + if ((clock * 12) < dev_priv->properties->pll_min_pll_freq) { + clock = dev_priv->properties->pll_min_pll_freq / 12; + } + + /* maximize pll_ref_div while staying in boundary and minimizing + * the difference btw target frequency and programmed frequency */ + for (post_div = &post_divs[0]; post_div->divider; ++post_div) { + if (post_div->divider == 0) { + break; + } + tfrq = clock * post_div->divider; + for (fdiv = 1023; fdiv >= 4; fdiv--) { + rdiv = (fdiv * rfrq) / tfrq; + if (radeon_pll1_constraint(clock, rdiv, fdiv, + pdiv, rfrq, tfrq)) { + pfrq = (fdiv * rfrq) / rdiv; + diff_cpfrq = pfrq - tfrq; + if ((diff_cpfrq >= 0 && + diff_cpfrq < diff_cpfrq_best) || + (diff_cpfrq == diff_cpfrq_best && + rdiv > rdiv_best)) { + rdiv_best = rdiv; + fdiv_best = fdiv; + tfrq_best = tfrq; + pfrq_best = pfrq; + pdiv = post_div->divider; + pdiv_id = post_div->divider_id; + diff_cpfrq_best = diff_cpfrq; + } + } + } + } + state->ppll_ref_div = + REG_S(PPLL_REF_DIV, PPLL_REF_DIV, rdiv_best) | + REG_S(PPLL_REF_DIV, PPLL_REF_DIV_ACC, rdiv_best); + state->ppll_div_0 = REG_S(PPLL_DIV_0, PPLL_FB0_DIV, fdiv_best) | + REG_S(PPLL_DIV_0, PPLL_POST0_DIV, pdiv_id); + + vco_freq = (fdiv_best * rfrq) / rdiv_best; + /* This is horribly crude: the VCO frequency range is divided into + * 3 parts, each part having a fixed PLL gain value. + */ + if (vco_freq >= 300000) { + /* [300..max] MHz : 7 */ + vco_gain = 7; + } else if (vco_freq >= 180000) { + /* [180..300) MHz : 4 */ + vco_gain = 4; + } else { + /* [0..180) MHz : 1 */ + vco_gain = 1; + } + state->ppll_cntl |= REG_S(PPLL_CNTL, PPLL_PVG, vco_gain); + state->vclk_ecp_cntl |= REG_S(VCLK_ECP_CNTL, VCLK_SRC_SEL, + VCLK_SRC_SEL__PPLLCLK); + state->htotal_cntl = 0; + DRM_INFO("rdiv: %d\n", rdiv_best); + DRM_INFO("fdiv: %d\n", fdiv_best); + DRM_INFO("pdiv: %d\n", pdiv); + DRM_INFO("pdiv: %d\n", pdiv_id); + DRM_INFO("tfrq: %d\n", tfrq_best); + DRM_INFO("pfrq: %d\n", pfrq_best); + DRM_INFO("PPLL_REF_DIV: 0x%08X\n", state->ppll_ref_div); + DRM_INFO("PPLL_DIV_0: 0x%08X\n", state->ppll_div_0); + DRM_INFO("PPLL_CNTL: 0x%08X\n", state->ppll_cntl); + DRM_INFO("VCLK_ECP_CNTL: 0x%08X\n", state->vclk_ecp_cntl); +} + +static void radeon_ms_crtc1_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_radeon_private *dev_priv = dev->dev_private; + struct radeon_state *state = &dev_priv->driver_state; + int format, hsync_wid, vsync_wid, pitch; + + DRM_INFO("[radeon_ms] set 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); + DRM_INFO("[radeon_ms] set modeline %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x (adjusted)\n", + adjusted_mode->mode_id, adjusted_mode->name, adjusted_mode->vrefresh, adjusted_mode->clock, + adjusted_mode->hdisplay, adjusted_mode->hsync_start, + adjusted_mode->hsync_end, adjusted_mode->htotal, + adjusted_mode->vdisplay, adjusted_mode->vsync_start, + adjusted_mode->vsync_end, adjusted_mode->vtotal, adjusted_mode->type); + + /* only support RGB555,RGB565,ARGB8888 should satisfy all users */ + switch (crtc->fb->bits_per_pixel) { + case 16: + if (crtc->fb->depth == 15) { + format = 3; + } else { + format = 4; + } + break; + case 32: + format = 6; + break; + default: + DRM_ERROR("Unknown color depth\n"); + return; + } + radeon_pll1_compute(crtc, adjusted_mode); + + state->crtc_offset = REG_S(CRTC_OFFSET, CRTC_OFFSET, crtc->fb->offset); + state->crtc_gen_cntl = CRTC_GEN_CNTL__CRTC_EXT_DISP_EN | + CRTC_GEN_CNTL__CRTC_EN | + REG_S(CRTC_GEN_CNTL, CRTC_PIX_WIDTH, format); + if (adjusted_mode->flags & V_DBLSCAN) { + state->crtc_gen_cntl |= CRTC_GEN_CNTL__CRTC_DBL_SCAN_EN; + } + if (adjusted_mode->flags & V_CSYNC) { + state->crtc_gen_cntl |= CRTC_GEN_CNTL__CRTC_C_SYNC_EN; + } + if (adjusted_mode->flags & V_INTERLACE) { + state->crtc_gen_cntl |= CRTC_GEN_CNTL__CRTC_INTERLACE_EN; + } + state->crtc_more_cntl = 0; + state->crtc_h_total_disp = + REG_S(CRTC_H_TOTAL_DISP, + CRTC_H_TOTAL, + (adjusted_mode->crtc_htotal/8) - 1) | + REG_S(CRTC_H_TOTAL_DISP, + CRTC_H_DISP, + (adjusted_mode->crtc_hdisplay/8) - 1); + hsync_wid = (adjusted_mode->crtc_hsync_end - + adjusted_mode->crtc_hsync_start) / 8; + if (!hsync_wid) { + hsync_wid = 1; + } + if (hsync_wid > 0x3f) { + hsync_wid = 0x3f; + } + state->crtc_h_sync_strt_wid = + REG_S(CRTC_H_SYNC_STRT_WID, + CRTC_H_SYNC_WID, hsync_wid) | + REG_S(CRTC_H_SYNC_STRT_WID, + CRTC_H_SYNC_STRT_PIX, + adjusted_mode->crtc_hsync_start) | + REG_S(CRTC_H_SYNC_STRT_WID, + CRTC_H_SYNC_STRT_CHAR, + adjusted_mode->crtc_hsync_start/8); + if (adjusted_mode->flags & V_NHSYNC) { + state->crtc_h_sync_strt_wid |= + CRTC_H_SYNC_STRT_WID__CRTC_H_SYNC_POL; + } + + state->crtc_v_total_disp = + REG_S(CRTC_V_TOTAL_DISP, CRTC_V_TOTAL, + adjusted_mode->crtc_vtotal - 1) | + REG_S(CRTC_V_TOTAL_DISP, CRTC_V_DISP, + adjusted_mode->crtc_vdisplay - 1); + vsync_wid = adjusted_mode->crtc_vsync_end - + adjusted_mode->crtc_vsync_start; + if (!vsync_wid) { + vsync_wid = 1; + } + if (vsync_wid > 0x1f) { + vsync_wid = 0x1f; + } + state->crtc_v_sync_strt_wid = + REG_S(CRTC_V_SYNC_STRT_WID, + CRTC_V_SYNC_WID, + vsync_wid) | + REG_S(CRTC_V_SYNC_STRT_WID, + CRTC_V_SYNC_STRT, + adjusted_mode->crtc_vsync_start); + if (adjusted_mode->flags & V_NVSYNC) { + state->crtc_v_sync_strt_wid |= + CRTC_V_SYNC_STRT_WID__CRTC_V_SYNC_POL; + } + + pitch = (crtc->fb->width * crtc->fb->bits_per_pixel + + ((crtc->fb->bits_per_pixel * 8)- 1)) / + (crtc->fb->bits_per_pixel * 8); + state->crtc_pitch = REG_S(CRTC_PITCH, CRTC_PITCH, pitch) | + REG_S(CRTC_PITCH, CRTC_PITCH_RIGHT, pitch); + + state->fp_h_sync_strt_wid = state->crtc_h_sync_strt_wid; + state->fp_v_sync_strt_wid = state->crtc_v_sync_strt_wid; + state->fp_crtc_h_total_disp = state->crtc_h_total_disp; + state->fp_crtc_v_total_disp = state->crtc_v_total_disp; + + radeon_ms_crtc1_restore(dev, state); +} + +static void radeon_ms_crtc_mode_commit(struct drm_crtc *crtc) +{ + crtc->funcs->dpms(crtc, DPMSModeOn); +} + +static void radeon_ms_crtc_gamma_set(struct drm_crtc *crtc, u16 r, + u16 g, u16 b, int regno) +{ + struct drm_radeon_private *dev_priv = crtc->dev->dev_private; + struct radeon_ms_crtc *radeon_ms_crtc = crtc->driver_private; + struct radeon_state *state = &dev_priv->driver_state; + uint32_t color; + + switch(radeon_ms_crtc->crtc) { + case 1: + state->dac_cntl2 &= ~DAC_CNTL2__PALETTE_ACCESS_CNTL; + break; + case 2: + state->dac_cntl2 |= DAC_CNTL2__PALETTE_ACCESS_CNTL; + break; + } + MMIO_W(DAC_CNTL2, state->dac_cntl2); + if (crtc->fb->bits_per_pixel == 16 && crtc->fb->depth == 16) { + if (regno >= 64) { + return; + } + MMIO_W(PALETTE_INDEX, + REG_S(PALETTE_INDEX, PALETTE_W_INDEX, + regno * 4)); + color = 0; + color = REG_S(PALETTE_DATA, PALETTE_DATA_R, r >> 8) | + REG_S(PALETTE_DATA, PALETTE_DATA_G, g >> 8) | + REG_S(PALETTE_DATA, PALETTE_DATA_B, b >> 8); + MMIO_W(PALETTE_DATA, color); + MMIO_W(PALETTE_INDEX, + REG_S(PALETTE_INDEX, PALETTE_W_INDEX, regno * 4)); + color = 0; + color = REG_S(PALETTE_30_DATA, PALETTE_DATA_R, r >> 6) | + REG_S(PALETTE_30_DATA, PALETTE_DATA_G, g >> 6) | + REG_S(PALETTE_30_DATA, PALETTE_DATA_B, b >> 6); + MMIO_W(PALETTE_30_DATA, color); + radeon_ms_crtc->lut_r[regno * 4] = r; + radeon_ms_crtc->lut_g[regno * 4] = g; + radeon_ms_crtc->lut_b[regno * 4] = b; + if (regno < 32) { + MMIO_W(PALETTE_INDEX, + REG_S(PALETTE_INDEX, PALETTE_W_INDEX, + regno * 8)); + color = 0; + color = REG_S(PALETTE_DATA, PALETTE_DATA_R, r >> 8) | + REG_S(PALETTE_DATA, PALETTE_DATA_G, g >> 8) | + REG_S(PALETTE_DATA, PALETTE_DATA_B, b >> 8); + MMIO_W(PALETTE_DATA, color); + MMIO_W(PALETTE_INDEX, + REG_S(PALETTE_INDEX, PALETTE_W_INDEX, + regno * 8)); + color = 0; + color = REG_S(PALETTE_30_DATA, PALETTE_DATA_R,r >> 6) | + REG_S(PALETTE_30_DATA, PALETTE_DATA_G,g >> 6) | + REG_S(PALETTE_30_DATA, PALETTE_DATA_B,b >> 6); + MMIO_W(PALETTE_30_DATA, color); + radeon_ms_crtc->lut_r[regno * 8] = r; + radeon_ms_crtc->lut_g[regno * 8] = g; + radeon_ms_crtc->lut_b[regno * 8] = b; + } + } else { + if (regno >= 256) { + return; + } + radeon_ms_crtc->lut_r[regno] = r; + radeon_ms_crtc->lut_g[regno] = g; + radeon_ms_crtc->lut_b[regno] = b; + MMIO_W(PALETTE_INDEX, + REG_S(PALETTE_INDEX, PALETTE_W_INDEX, regno)); + color = 0; + color = REG_S(PALETTE_DATA, PALETTE_DATA_R, r >> 8) | + REG_S(PALETTE_DATA, PALETTE_DATA_G, g >> 8) | + REG_S(PALETTE_DATA, PALETTE_DATA_B, b >> 8); + MMIO_W(PALETTE_DATA, color); + MMIO_W(PALETTE_INDEX, + REG_S(PALETTE_INDEX, PALETTE_W_INDEX, regno)); + color = 0; + color = REG_S(PALETTE_30_DATA, PALETTE_DATA_R, r >> 6) | + REG_S(PALETTE_30_DATA, PALETTE_DATA_G, g >> 6) | + REG_S(PALETTE_30_DATA, PALETTE_DATA_B, b >> 6); + } +} + +static void radeon_ms_crtc_load_lut(struct drm_crtc *crtc) +{ + struct radeon_ms_crtc *radeon_ms_crtc = crtc->driver_private; + int i; + + if (!crtc->enabled) + return; + + for (i = 0; i < 256; i++) { + radeon_ms_crtc_gamma_set(crtc, + radeon_ms_crtc->lut_r[i], + radeon_ms_crtc->lut_g[i], + radeon_ms_crtc->lut_b[i], + i); + } +} + +static bool radeon_ms_crtc_lock(struct drm_crtc *crtc) +{ + return true; +} + +static void radeon_ms_crtc_unlock(struct drm_crtc *crtc) +{ +} + +static const struct drm_crtc_funcs radeon_ms_crtc1_funcs= { + .dpms = radeon_ms_crtc1_dpms, + .save = NULL, /* XXX */ + .restore = NULL, /* XXX */ + .lock = radeon_ms_crtc_lock, + .unlock = radeon_ms_crtc_unlock, + .prepare = radeon_ms_crtc_mode_prepare, + .commit = radeon_ms_crtc_mode_commit, + .mode_fixup = radeon_ms_crtc_mode_fixup, + .mode_set = radeon_ms_crtc1_mode_set, + .gamma_set = radeon_ms_crtc_gamma_set, + .cleanup = NULL, /* XXX */ +}; + +int radeon_ms_crtc_create(struct drm_device *dev, int crtc) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct drm_crtc *drm_crtc; + struct radeon_ms_crtc *radeon_ms_crtc; + int i; + + switch (crtc) { + case 1: + radeon_ms_crtc1_init(dev_priv, &dev_priv->driver_state); + drm_crtc = drm_crtc_create(dev, &radeon_ms_crtc1_funcs); + break; + case 2: + default: + return -EINVAL; + } + if (drm_crtc == NULL) { + return -ENOMEM; + } + + radeon_ms_crtc = drm_alloc(sizeof(struct radeon_ms_crtc), DRM_MEM_DRIVER); + if (radeon_ms_crtc == NULL) { + kfree(drm_crtc); + return -ENOMEM; + } + + radeon_ms_crtc->crtc = crtc; + for (i = 0; i < 256; i++) { + radeon_ms_crtc->lut_r[i] = i << 8; + radeon_ms_crtc->lut_g[i] = i << 8; + radeon_ms_crtc->lut_b[i] = i << 8; + } + drm_crtc->driver_private = radeon_ms_crtc; + return 0; +} diff --git a/shared-core/radeon_ms_dac.c b/shared-core/radeon_ms_dac.c new file mode 100644 index 00000000..297623a0 --- /dev/null +++ b/shared-core/radeon_ms_dac.c @@ -0,0 +1,400 @@ +/* + * Copyright 2007 Jérôme Glisse + * Copyright 2007 Alex Deucher + * Copyright 2007 Dave Airlie + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation on the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT. IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR + * THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "radeon_ms.h" + +int radeon_ms_dac1_initialize(struct radeon_ms_output *output) +{ + struct drm_radeon_private *dev_priv = output->dev->dev_private; + struct radeon_state *state = &dev_priv->driver_state; + + state->dac_cntl = + REG_S(DAC_CNTL, DAC_RANGE_CNTL, DAC_RANGE_CNTL__PS2) | + DAC_CNTL__DAC_8BIT_EN | + DAC_CNTL__DAC_VGA_ADR_EN | + DAC_CNTL__DAC_PDWN | + REG_S(DAC_CNTL, DAC, 0xff); + state->dac_ext_cntl = 0; + state->dac_macro_cntl = + DAC_MACRO_CNTL__DAC_PDWN_R | + DAC_MACRO_CNTL__DAC_PDWN_G | + DAC_MACRO_CNTL__DAC_PDWN_B | + REG_S(DAC_MACRO_CNTL, DAC_WHITE_CNTL, 7) | + REG_S(DAC_MACRO_CNTL, DAC_BG_ADJ, 7); + state->dac_embedded_sync_cntl = + DAC_EMBEDDED_SYNC_CNTL__DAC_EMBED_VSYNC_EN_Y_G; + state->dac_broad_pulse = 0; + state->dac_skew_clks = 0; + state->dac_incr = 0; + state->dac_neg_sync_level = 0; + state->dac_pos_sync_level = 0; + state->dac_blank_level = 0; + state->dac_sync_equalization = 0; + state->disp_output_cntl = 0; + radeon_ms_dac1_restore(output, state); + return 0; +} + +enum drm_output_status radeon_ms_dac1_detect(struct radeon_ms_output *output) +{ + return output_status_unknown; +} + +void radeon_ms_dac1_dpms(struct radeon_ms_output *output, int mode) +{ + struct drm_radeon_private *dev_priv = output->dev->dev_private; + struct radeon_state *state = &dev_priv->driver_state; + uint32_t dac_cntl; + uint32_t dac_macro_cntl; + + dac_cntl = DAC_CNTL__DAC_PDWN; + dac_macro_cntl = DAC_MACRO_CNTL__DAC_PDWN_R | + DAC_MACRO_CNTL__DAC_PDWN_G | + DAC_MACRO_CNTL__DAC_PDWN_B; + switch(mode) { + case DPMSModeOn: + state->dac_cntl &= ~dac_cntl; + state->dac_macro_cntl &= ~dac_macro_cntl; + break; + case DPMSModeStandby: + case DPMSModeSuspend: + case DPMSModeOff: + state->dac_cntl |= dac_cntl; + state->dac_macro_cntl |= dac_macro_cntl; + break; + default: + /* error */ + break; + } + MMIO_W(DAC_CNTL, state->dac_cntl); + MMIO_W(DAC_MACRO_CNTL, state->dac_macro_cntl); +} + +int radeon_ms_dac1_get_modes(struct radeon_ms_output *output) +{ + return 0; +} + +bool radeon_ms_dac1_mode_fixup(struct radeon_ms_output *output, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +int radeon_ms_dac1_mode_set(struct radeon_ms_output *output, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_radeon_private *dev_priv = output->dev->dev_private; + struct radeon_ms_connector *connector = output->connector; + struct radeon_state *state = &dev_priv->driver_state; + uint32_t v = 0; + + if (connector == NULL) { + /* output not associated with a connector */ + return -EINVAL; + } + state->disp_output_cntl &= ~DISP_OUTPUT_CNTL__DISP_DAC_SOURCE__MASK; + state->dac_cntl2 &= ~DAC_CNTL2__DAC_CLK_SEL; + switch (connector->crtc) { + case 1: + v = DISP_DAC_SOURCE__PRIMARYCRTC; + break; + case 2: + v = DISP_DAC_SOURCE__SECONDARYCRTC; + state->dac_cntl2 |= DAC_CNTL2__DAC_CLK_SEL; + break; + } + state->disp_output_cntl |= REG_S(DISP_OUTPUT_CNTL, DISP_DAC_SOURCE, v); + MMIO_W(DISP_OUTPUT_CNTL, state->disp_output_cntl); + MMIO_W(DAC_CNTL2, state->dac_cntl2); + return 0; +} + +void radeon_ms_dac1_restore(struct radeon_ms_output *output, + struct radeon_state *state) +{ + struct drm_radeon_private *dev_priv = output->dev->dev_private; + + MMIO_W(DAC_CNTL, state->dac_cntl); + MMIO_W(DAC_EXT_CNTL, state->dac_ext_cntl); + MMIO_W(DAC_MACRO_CNTL, state->dac_macro_cntl); + MMIO_W(DAC_EMBEDDED_SYNC_CNTL, state->dac_embedded_sync_cntl); + MMIO_W(DAC_BROAD_PULSE, state->dac_broad_pulse); + MMIO_W(DAC_SKEW_CLKS, state->dac_skew_clks); + MMIO_W(DAC_INCR, state->dac_incr); + MMIO_W(DAC_NEG_SYNC_LEVEL, state->dac_neg_sync_level); + MMIO_W(DAC_POS_SYNC_LEVEL, state->dac_pos_sync_level); + MMIO_W(DAC_BLANK_LEVEL, state->dac_blank_level); + MMIO_W(DAC_SYNC_EQUALIZATION, state->dac_sync_equalization); +} + +void radeon_ms_dac1_save(struct radeon_ms_output *output, + struct radeon_state *state) +{ + struct drm_radeon_private *dev_priv = output->dev->dev_private; + + state->dac_cntl = MMIO_R(DAC_CNTL); + state->dac_ext_cntl = MMIO_R(DAC_EXT_CNTL); + state->dac_macro_cntl = MMIO_R(DAC_MACRO_CNTL); + state->dac_embedded_sync_cntl = MMIO_R(DAC_EMBEDDED_SYNC_CNTL); + state->dac_broad_pulse = MMIO_R(DAC_BROAD_PULSE); + state->dac_skew_clks = MMIO_R(DAC_SKEW_CLKS); + state->dac_incr = MMIO_R(DAC_INCR); + state->dac_neg_sync_level = MMIO_R(DAC_NEG_SYNC_LEVEL); + state->dac_pos_sync_level = MMIO_R(DAC_POS_SYNC_LEVEL); + state->dac_blank_level = MMIO_R(DAC_BLANK_LEVEL); + state->dac_sync_equalization = MMIO_R(DAC_SYNC_EQUALIZATION); +} + +int radeon_ms_dac2_initialize(struct radeon_ms_output *output) +{ + struct drm_radeon_private *dev_priv = output->dev->dev_private; + struct radeon_state *state = &dev_priv->driver_state; + + state->tv_dac_cntl = TV_DAC_CNTL__BGSLEEP | + REG_S(TV_DAC_CNTL, STD, STD__PS2) | + REG_S(TV_DAC_CNTL, BGADJ, 0); + switch (dev_priv->family) { + case CHIP_R100: + case CHIP_R200: + case CHIP_RV200: + case CHIP_RV250: + case CHIP_RV280: + case CHIP_RS300: + case CHIP_R300: + case CHIP_R350: + case CHIP_R360: + case CHIP_RV350: + case CHIP_RV370: + case CHIP_RV380: + case CHIP_RS400: + state->tv_dac_cntl |= TV_DAC_CNTL__RDACPD | + TV_DAC_CNTL__GDACPD | + TV_DAC_CNTL__BDACPD | + REG_S(TV_DAC_CNTL, DACADJ, 0); + break; + case CHIP_RV410: + case CHIP_R420: + case CHIP_R430: + case CHIP_R480: + state->tv_dac_cntl |= TV_DAC_CNTL__RDACPD_R4 | + TV_DAC_CNTL__GDACPD_R4 | + TV_DAC_CNTL__BDACPD_R4 | + REG_S(TV_DAC_CNTL, DACADJ_R4, 0); + break; + } + state->tv_master_cntl = TV_MASTER_CNTL__TV_ASYNC_RST | + TV_MASTER_CNTL__CRT_ASYNC_RST | + TV_MASTER_CNTL__RESTART_PHASE_FIX | + TV_MASTER_CNTL__CRT_FIFO_CE_EN | + TV_MASTER_CNTL__TV_FIFO_CE_EN; + state->dac_cntl2 = 0; + state->disp_output_cntl = 0; + radeon_ms_dac2_restore(output, state); + return 0; +} + +enum drm_output_status radeon_ms_dac2_detect(struct radeon_ms_output *output) +{ + return output_status_unknown; +} + +void radeon_ms_dac2_dpms(struct radeon_ms_output *output, int mode) +{ + struct drm_radeon_private *dev_priv = output->dev->dev_private; + struct radeon_state *state = &dev_priv->driver_state; + uint32_t tv_dac_cntl_on, tv_dac_cntl_off; + + tv_dac_cntl_off = TV_DAC_CNTL__BGSLEEP; + tv_dac_cntl_on = TV_DAC_CNTL__NBLANK | + TV_DAC_CNTL__NHOLD; + switch (dev_priv->family) { + case CHIP_R100: + case CHIP_R200: + case CHIP_RV200: + case CHIP_RV250: + case CHIP_RV280: + case CHIP_RS300: + case CHIP_R300: + case CHIP_R350: + case CHIP_R360: + case CHIP_RV350: + case CHIP_RV370: + case CHIP_RV380: + case CHIP_RS400: + tv_dac_cntl_off |= TV_DAC_CNTL__RDACPD | + TV_DAC_CNTL__GDACPD | + TV_DAC_CNTL__BDACPD; + break; + case CHIP_RV410: + case CHIP_R420: + case CHIP_R430: + case CHIP_R480: + tv_dac_cntl_off |= TV_DAC_CNTL__RDACPD_R4 | + TV_DAC_CNTL__GDACPD_R4 | + TV_DAC_CNTL__BDACPD_R4; + break; + } + switch(mode) { + case DPMSModeOn: + state->tv_dac_cntl &= ~tv_dac_cntl_off; + state->tv_dac_cntl |= tv_dac_cntl_on; + break; + case DPMSModeStandby: + case DPMSModeSuspend: + case DPMSModeOff: + state->tv_dac_cntl &= ~tv_dac_cntl_on; + state->tv_dac_cntl |= tv_dac_cntl_off; + break; + default: + /* error */ + break; + } + MMIO_W(TV_DAC_CNTL, state->tv_dac_cntl); +} + +int radeon_ms_dac2_get_modes(struct radeon_ms_output *output) +{ + return 0; +} + +bool radeon_ms_dac2_mode_fixup(struct radeon_ms_output *output, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +int radeon_ms_dac2_mode_set(struct radeon_ms_output *output, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_radeon_private *dev_priv = output->dev->dev_private; + struct radeon_ms_connector *connector = output->connector; + struct radeon_state *state = &dev_priv->driver_state; + + if (connector == NULL) { + /* output not associated with a connector */ + return -EINVAL; + } + switch (dev_priv->family) { + case CHIP_R100: + case CHIP_R200: + state->disp_output_cntl &= ~DISP_OUTPUT_CNTL__DISP_TV_SOURCE; + switch (connector->crtc) { + case 1: + break; + case 2: + state->disp_output_cntl |= + DISP_OUTPUT_CNTL__DISP_TV_SOURCE; + break; + } + break; + case CHIP_RV200: + case CHIP_RV250: + case CHIP_RV280: + case CHIP_RS300: + break; + case CHIP_R300: + case CHIP_R350: + case CHIP_R360: + case CHIP_RV350: + case CHIP_RV370: + case CHIP_RV380: + case CHIP_RS400: + case CHIP_RV410: + case CHIP_R420: + case CHIP_R430: + case CHIP_R480: + state->disp_output_cntl &= + ~DISP_OUTPUT_CNTL__DISP_TVDAC_SOURCE__MASK; + switch (connector->crtc) { + case 1: + state->disp_output_cntl |= + REG_S(DISP_OUTPUT_CNTL, + DISP_TVDAC_SOURCE, + DISP_TVDAC_SOURCE__PRIMARYCRTC); + break; + case 2: + state->disp_output_cntl |= + REG_S(DISP_OUTPUT_CNTL, + DISP_TVDAC_SOURCE, + DISP_TVDAC_SOURCE__SECONDARYCRTC); + break; + } + break; + } + switch (dev_priv->family) { + case CHIP_R200: + break; + case CHIP_R100: + case CHIP_RV200: + case CHIP_RV250: + case CHIP_RV280: + case CHIP_RS300: + case CHIP_R300: + case CHIP_R350: + case CHIP_R360: + case CHIP_RV350: + case CHIP_RV370: + case CHIP_RV380: + case CHIP_RS400: + case CHIP_RV410: + case CHIP_R420: + case CHIP_R430: + case CHIP_R480: + if (connector->type != CONNECTOR_CTV && + connector->type != CONNECTOR_STV) { + state->dac_cntl2 |= DAC_CNTL2__DAC2_CLK_SEL; + } + } + MMIO_W(DAC_CNTL2, state->dac_cntl2); + MMIO_W(DISP_OUTPUT_CNTL, state->disp_output_cntl); + return 0; +} + +void radeon_ms_dac2_restore(struct radeon_ms_output *output, + struct radeon_state *state) +{ + struct drm_radeon_private *dev_priv = output->dev->dev_private; + + MMIO_W(DAC_CNTL2, state->dac_cntl2); + MMIO_W(TV_DAC_CNTL, state->tv_dac_cntl); + MMIO_W(TV_MASTER_CNTL, state->tv_master_cntl); +} + +void radeon_ms_dac2_save(struct radeon_ms_output *output, + struct radeon_state *state) +{ + struct drm_radeon_private *dev_priv = output->dev->dev_private; + + state->dac_cntl2 = MMIO_R(DAC_CNTL2); + state->tv_dac_cntl = MMIO_R(TV_DAC_CNTL); + state->tv_master_cntl = MMIO_R(TV_MASTER_CNTL); +} diff --git a/shared-core/radeon_ms_drm.c b/shared-core/radeon_ms_drm.c new file mode 100644 index 00000000..8d0481e1 --- /dev/null +++ b/shared-core/radeon_ms_drm.c @@ -0,0 +1,298 @@ +/* + * Copyright 2007 Jérôme Glisse + * Copyright 2007 Alex Deucher + * Copyright 2007 Dave Airlie + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/* + * Authors: + * Jerome Glisse <glisse@freedesktop.org> + */ +#include "drm_pciids.h" +#include "radeon_ms.h" + + +static uint32_t radeon_ms_mem_prios[] = { + DRM_BO_MEM_VRAM, + DRM_BO_MEM_TT, + DRM_BO_MEM_LOCAL, +}; + +static uint32_t radeon_ms_busy_prios[] = { + DRM_BO_MEM_TT, + DRM_BO_MEM_VRAM, + DRM_BO_MEM_LOCAL, +}; + +struct drm_fence_driver radeon_ms_fence_driver = { + .num_classes = 1, + .wrap_diff = (1 << 30), + .flush_diff = (1 << 29), + .sequence_mask = 0xffffffffU, + .lazy_capable = 1, + .emit = radeon_ms_fence_emit_sequence, + .poke_flush = radeon_ms_poke_flush, + .has_irq = radeon_ms_fence_has_irq, +}; + +struct drm_bo_driver radeon_ms_bo_driver = { + .mem_type_prio = radeon_ms_mem_prios, + .mem_busy_prio = radeon_ms_busy_prios, + .num_mem_type_prio = sizeof(radeon_ms_mem_prios)/sizeof(uint32_t), + .num_mem_busy_prio = sizeof(radeon_ms_busy_prios)/sizeof(uint32_t), + .create_ttm_backend_entry = radeon_ms_create_ttm_backend, + .fence_type = radeon_ms_fence_types, + .invalidate_caches = radeon_ms_invalidate_caches, + .init_mem_type = radeon_ms_init_mem_type, + .evict_mask = radeon_ms_evict_mask, + .move = radeon_ms_bo_move, + .ttm_cache_flush = radeon_ms_ttm_flush, +}; + +struct drm_ioctl_desc radeon_ms_ioctls[] = { + DRM_IOCTL_DEF(DRM_RADEON_EXECBUFFER, radeon_ms_execbuffer, DRM_AUTH), +}; +int radeon_ms_num_ioctls = DRM_ARRAY_SIZE(radeon_ms_ioctls); + +int radeon_ms_driver_dma_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_device_dma *dma = dev->dma; + struct drm_dma *d = data; + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + /* Please don't send us buffers. + */ + if (d->send_count != 0) { + DRM_ERROR("Process %d trying to send %d buffers via drmDMA\n", + DRM_CURRENTPID, d->send_count); + return -EINVAL; + } + + /* Don't ask us buffer neither :) + */ + DRM_ERROR("Process %d trying to get %d buffers (of %d max)\n", + DRM_CURRENTPID, d->request_count, dma->buf_count); + return -EINVAL; +} + +void radeon_ms_driver_lastclose(struct drm_device * dev) +{ +} + +int radeon_ms_driver_load(struct drm_device *dev, unsigned long flags) +{ + struct drm_radeon_private *dev_priv; + int ret = 0; + + DRM_INFO("[radeon_ms] loading\n"); + /* allocate and clear device private structure */ + dev_priv = drm_alloc(sizeof(struct drm_radeon_private), DRM_MEM_DRIVER); + if (dev_priv == NULL) + return -ENOMEM; + memset(dev_priv, 0, sizeof(struct drm_radeon_private)); + dev->dev_private = (void *)dev_priv; + + /* initialize modesetting structure (must be done here) */ + drm_mode_config_init(dev); + + /* flags correspond to chipset family */ + dev_priv->usec_timeout = 100; + dev_priv->family = flags & 0xffffU; + dev_priv->bus_type = flags & 0xff0000U; + /* initialize family functions */ + ret = radeon_ms_family_init(dev); + if (ret != 0) { + radeon_ms_driver_unload(dev); + return ret; + } + + /* we don't want userspace to be able to map this so don't use + * drm_addmap */ + dev_priv->mmio.offset = drm_get_resource_start(dev, 2); + dev_priv->mmio.size = drm_get_resource_len(dev, 2); + dev_priv->mmio.type = _DRM_REGISTERS; + dev_priv->mmio.flags = _DRM_RESTRICTED; + drm_core_ioremap(&dev_priv->mmio, dev); + /* map vram FIXME: IGP likely don't have any of this */ + dev_priv->vram.offset = drm_get_resource_start(dev, 0); + dev_priv->vram.size = drm_get_resource_len(dev, 0); + dev_priv->vram.type = _DRM_FRAME_BUFFER; + dev_priv->vram.flags = _DRM_RESTRICTED; + drm_core_ioremap(&dev_priv->vram, dev); + + /* save radeon initial state which will be restored upon module + * exit */ + radeon_ms_state_save(dev, &dev_priv->load_state); + dev_priv->restore_state = 1; + memcpy(&dev_priv->driver_state, &dev_priv->load_state, + sizeof(struct radeon_state)); + + /* initialize irq */ + ret = radeon_ms_irq_init(dev); + if (ret != 0) { + radeon_ms_driver_unload(dev); + return ret; + } + + /* init bo driver */ + dev_priv->fence_id_last = 1; + dev_priv->fence_reg = SCRATCH_REG2; + drm_bo_driver_init(dev); + /* initialize vram */ + ret = drm_bo_init_mm(dev, DRM_BO_MEM_VRAM, 0, dev_priv->vram.size); + if (ret != 0) { + radeon_ms_driver_unload(dev); + return ret; + } + + /* initialize gpu address space (only after) VRAM initialization */ + ret = radeon_ms_gpu_initialize(dev); + if (ret != 0) { + radeon_ms_driver_unload(dev); + return ret; + } + radeon_ms_gpu_restore(dev, &dev_priv->driver_state); + + /* initialize ttm */ + ret = drm_bo_init_mm(dev, DRM_BO_MEM_TT, 0, + dev_priv->gpu_gart_size / RADEON_PAGE_SIZE); + if (ret != 0) { + radeon_ms_driver_unload(dev); + return ret; + } + + /* initialize ring buffer */ + /* set ring size to 4Mo FIXME: should make a parameter for this */ + dev_priv->write_back_area_size = 4 * 1024; + dev_priv->ring_buffer_size = 4 * 1024 * 1024; + ret = radeon_ms_cp_init(dev); + if (ret != 0) { + radeon_ms_driver_unload(dev); + return ret; + } + + /* initialize modesetting */ + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + dev->mode_config.max_width = 4096; + dev->mode_config.max_height = 4096; + dev->mode_config.fb_base = dev_priv->vram.offset; + ret = radeon_ms_crtc_create(dev, 1); + if (ret != 0) { + radeon_ms_driver_unload(dev); + return ret; + } + ret = radeon_ms_outputs_from_properties(dev); + if (ret != 0) { + radeon_ms_driver_unload(dev); + return ret; + } + ret = radeon_ms_connectors_from_properties(dev); + if (ret != 0) { + radeon_ms_driver_unload(dev); + return ret; + } + radeon_ms_outputs_save(dev, &dev_priv->load_state); + drm_initial_config(dev, false); + + ret = drm_irq_install(dev); + if (ret != 0) { + radeon_ms_driver_unload(dev); + return ret; + } + + DRM_INFO("[radeon_ms] successfull initialization\n"); + return 0; +} + +int radeon_ms_driver_open(struct drm_device * dev, struct drm_file *file_priv) +{ + return 0; +} + + +int radeon_ms_driver_unload(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + if (dev_priv == NULL) { + return 0; + } + + /* cleanup modesetting */ + drm_mode_config_cleanup(dev); + DRM_INFO("[radeon_ms] modesetting clean\n"); + radeon_ms_outputs_restore(dev, &dev_priv->load_state); + radeon_ms_connectors_destroy(dev); + radeon_ms_outputs_destroy(dev); + + /* shutdown cp engine */ + radeon_ms_cp_finish(dev); + DRM_INFO("[radeon_ms] cp clean\n"); + + drm_irq_uninstall(dev); + DRM_INFO("[radeon_ms] irq uninstalled\n"); + + DRM_INFO("[radeon_ms] unloading\n"); + /* clean ttm memory manager */ + mutex_lock(&dev->struct_mutex); + if (drm_bo_clean_mm(dev, DRM_BO_MEM_TT)) { + DRM_ERROR("TT memory manager not clean. Delaying takedown\n"); + } + mutex_unlock(&dev->struct_mutex); + DRM_INFO("[radeon_ms] TT memory clean\n"); + /* finish */ + if (dev_priv->bus_finish) { + dev_priv->bus_finish(dev); + } + DRM_INFO("[radeon_ms] bus down\n"); + /* clean vram memory manager */ + mutex_lock(&dev->struct_mutex); + if (drm_bo_clean_mm(dev, DRM_BO_MEM_VRAM)) { + DRM_ERROR("VRAM memory manager not clean. Delaying takedown\n"); + } + mutex_unlock(&dev->struct_mutex); + DRM_INFO("[radeon_ms] VRAM memory clean\n"); + /* clean memory manager */ + drm_bo_driver_finish(dev); + DRM_INFO("[radeon_ms] memory manager clean\n"); + /* restore card state */ + if (dev_priv->restore_state) { + radeon_ms_state_restore(dev, &dev_priv->load_state); + } + DRM_INFO("[radeon_ms] state restored\n"); + if (dev_priv->mmio.handle) { + drm_core_ioremapfree(&dev_priv->mmio, dev); + } + if (dev_priv->vram.handle) { + drm_core_ioremapfree(&dev_priv->vram, dev); + } + DRM_INFO("[radeon_ms] map released\n"); + drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER); + dev->dev_private = NULL; + + DRM_INFO("[radeon_ms] that's all the folks\n"); + return 0; +} + diff --git a/shared-core/radeon_ms_drm.h b/shared-core/radeon_ms_drm.h new file mode 100644 index 00000000..842d5331 --- /dev/null +++ b/shared-core/radeon_ms_drm.h @@ -0,0 +1,60 @@ +/* + * Copyright 2007 Jérôme Glisse + * Copyright 2007 Dave Airlie + * Copyright 2007 Alex Deucher + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/* + * Authors: + * Jérôme Glisse <glisse@freedesktop.org> + */ +#ifndef __RADEON_MS_DRM_H__ +#define __RADEON_MS_DRM_H__ + +/* fence definitions */ +/* The only fence class we support */ +#define DRM_RADEON_FENCE_CLASS_ACCEL 0 +/* Fence type that guarantees read-write flush */ +#define DRM_RADEON_FENCE_TYPE_RW 2 +/* cache flushes programmed just before the fence */ +#define DRM_RADEON_FENCE_FLAG_FLUSHED 0x01000000 + +/* radeon ms ioctl */ +#define DRM_RADEON_EXECBUFFER 0x00 + +struct drm_radeon_execbuffer_arg { + uint64_t next; + uint32_t reloc_offset; + union { + struct drm_bo_op_req req; + struct drm_bo_arg_rep rep; + } d; +}; + +struct drm_radeon_execbuffer { + uint32_t args_count; + uint64_t args; + uint32_t cmd_size; + struct drm_fence_arg fence_arg; +}; + +#endif diff --git a/shared-core/radeon_ms_exec.c b/shared-core/radeon_ms_exec.c new file mode 100644 index 00000000..b2ce3cbb --- /dev/null +++ b/shared-core/radeon_ms_exec.c @@ -0,0 +1,242 @@ +/* + * Copyright 2007 Jérôme Glisse + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Jerome Glisse <glisse@freedesktop.org> + */ +#include "radeon_ms.h" + +static void radeon_ms_execbuffer_args_clean(struct drm_device *dev, + struct drm_buffer_object **buffers, + uint32_t args_count) +{ + mutex_lock(&dev->struct_mutex); + while (args_count--) { + drm_bo_usage_deref_locked(&buffers[args_count]); + } + mutex_unlock(&dev->struct_mutex); +} + +static int radeon_ms_execbuffer_args(struct drm_device *dev, + struct drm_file *file_priv, + struct drm_radeon_execbuffer *execbuffer, + struct drm_buffer_object **buffers, + uint32_t *relocs) +{ + struct drm_radeon_execbuffer_arg arg; + struct drm_bo_arg_rep rep; + uint32_t args_count = 0; + uint64_t next = 0; + uint64_t data = execbuffer->args; + int ret = 0; + + do { + if (args_count >= execbuffer->args_count) { + DRM_ERROR("[radeon_ms] buffer count exceeded %d\n.", + execbuffer->args_count); + ret = -EINVAL; + goto out_err; + } + buffers[args_count] = NULL; + if (copy_from_user(&arg, (void __user *)((unsigned)data), + sizeof(struct drm_radeon_execbuffer_arg))) { + ret = -EFAULT; + goto out_err; + } + mutex_lock(&dev->struct_mutex); + buffers[args_count] = + drm_lookup_buffer_object(file_priv, + arg.d.req.arg_handle, 1); + relocs[args_count] = arg.reloc_offset; + mutex_unlock(&dev->struct_mutex); + if (arg.d.req.op != drm_bo_validate) { + DRM_ERROR("[radeon_ms] buffer object operation wasn't " + "validate.\n"); + ret = -EINVAL; + goto out_err; + } + memset(&rep, 0, sizeof(struct drm_bo_arg_rep)); + if (args_count >= 1) { + ret = drm_bo_handle_validate(file_priv, + arg.d.req.bo_req.handle, + arg.d.req.bo_req.fence_class, + arg.d.req.bo_req.flags, + arg.d.req.bo_req.mask, + arg.d.req.bo_req.hint, + 0, + &rep.bo_info, + &buffers[args_count]); + } + if (ret) { + DRM_ERROR("[radeon_ms] error on handle validate %d\n", + ret); + rep.ret = ret; + goto out_err; + } + next = arg.next; + arg.d.rep = rep; + if (copy_to_user((void __user *)((unsigned)data), &arg, + sizeof(struct drm_radeon_execbuffer_arg))) { + ret = -EFAULT; + goto out_err; + } + data = next; + args_count++; + } while (next != 0); + if (args_count != execbuffer->args_count) { + DRM_ERROR("[radeon_ms] not enought buffer got %d waited %d\n.", + args_count, execbuffer->args_count); + ret = -EINVAL; + goto out_err; + } + return 0; +out_err: + radeon_ms_execbuffer_args_clean(dev, buffers, args_count); + return ret; +} + +static int radeon_ms_execbuffer_check(struct drm_device *dev, + struct drm_file *file_priv, + struct drm_radeon_execbuffer *execbuffer, + struct drm_buffer_object **buffers, + uint32_t *relocs, + uint32_t *cmd) +{ + uint32_t i, gpu_addr; + int ret; + + for (i = 0; i < execbuffer->args_count; i++) { + if (relocs[i]) { + ret = radeon_ms_bo_get_gpu_addr(dev, &buffers[i]->mem, + &gpu_addr); + if (ret) { + return ret; + } + cmd[relocs[i]] |= (gpu_addr) >> 10; + } + } + for (i = 0; i < execbuffer->cmd_size; i++) { +#if 0 + DRM_INFO("cmd[%d]=0x%08X\n", i, cmd[i]); +#endif + } + return 0; +} + +int radeon_ms_execbuffer(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_radeon_execbuffer *execbuffer = data; + struct drm_fence_arg *fence_arg = &execbuffer->fence_arg; + struct drm_buffer_object **buffers; + struct drm_bo_kmap_obj cmd_kmap; + struct drm_fence_object *fence; + uint32_t *relocs; + uint32_t *cmd; + int cmd_is_iomem; + int ret = 0; + + + ret = drm_bo_read_lock(&dev->bm.bm_lock); + if (ret) { + return ret; + } + + relocs = drm_calloc(execbuffer->args_count, sizeof(uint32_t), + DRM_MEM_DRIVER); + if (relocs == NULL) { + drm_bo_read_unlock(&dev->bm.bm_lock); + return -ENOMEM; + } + buffers = drm_calloc(execbuffer->args_count, + sizeof(struct drm_buffer_object *), + DRM_MEM_DRIVER); + if (buffers == NULL) { + drm_free(relocs, (execbuffer->args_count * sizeof(uint32_t)), + DRM_MEM_DRIVER); + drm_bo_read_unlock(&dev->bm.bm_lock); + return -ENOMEM; + } + /* process arguments */ + ret = radeon_ms_execbuffer_args(dev, file_priv, execbuffer, + buffers, relocs); + if (ret) { + DRM_ERROR("[radeon_ms] execbuffer wrong arguments\n"); + goto out_free; + } + /* map command buffer */ + memset(&cmd_kmap, 0, sizeof(struct drm_bo_kmap_obj)); + ret = drm_bo_kmap(buffers[0], + 0, + buffers[0]->mem.num_pages, + &cmd_kmap); + if (ret) { + DRM_ERROR("[radeon_ms] error mapping ring buffer: %d\n", ret); + goto out_free_release; + } + cmd = drm_bmo_virtual(&cmd_kmap, &cmd_is_iomem); + /* do cmd checking & relocations */ + ret = radeon_ms_execbuffer_check(dev, file_priv, execbuffer, + buffers, relocs, cmd); + if (ret) { + drm_putback_buffer_objects(dev); + goto out_free_release; + } + + ret = radeon_ms_ring_emit(dev, cmd, execbuffer->cmd_size); + if (ret) { + drm_putback_buffer_objects(dev); + goto out_free_release; + } + + /* fence */ + ret = drm_fence_buffer_objects(dev, NULL, 0, NULL, &fence); + if (ret) { + drm_putback_buffer_objects(dev); + DRM_ERROR("[radeon_ms] fence buffer objects failed\n"); + goto out_free_release; + } + if (!(fence_arg->flags & DRM_FENCE_FLAG_NO_USER)) { + ret = drm_fence_add_user_object(file_priv, fence, + fence_arg->flags & DRM_FENCE_FLAG_SHAREABLE); + if (!ret) { + fence_arg->handle = fence->base.hash.key; + fence_arg->fence_class = fence->fence_class; + fence_arg->type = fence->type; + fence_arg->signaled = fence->signaled; + fence_arg->sequence = fence->sequence; + } + } + drm_fence_usage_deref_unlocked(&fence); +out_free_release: + drm_bo_kunmap(&cmd_kmap); + radeon_ms_execbuffer_args_clean(dev, buffers, execbuffer->args_count); +out_free: + drm_free(relocs, (execbuffer->args_count * sizeof(uint32_t)), + DRM_MEM_DRIVER); + drm_free(buffers, + (execbuffer->args_count * sizeof(struct drm_buffer_object *)), + DRM_MEM_DRIVER); + drm_bo_read_unlock(&dev->bm.bm_lock); + return ret; +} diff --git a/shared-core/radeon_ms_family.c b/shared-core/radeon_ms_family.c new file mode 100644 index 00000000..779595d6 --- /dev/null +++ b/shared-core/radeon_ms_family.c @@ -0,0 +1,229 @@ +/* + * Copyright 2007 Jérôme Glisse + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/* + * Authors: + * Jerome Glisse <glisse@freedesktop.org> + */ +#include "drmP.h" +#include "drm.h" +#include "radeon_ms.h" + +static struct radeon_ms_output radeon_ms_dac1 = { + OUTPUT_DAC1, + NULL, + NULL, + radeon_ms_dac1_initialize, + radeon_ms_dac1_detect, + radeon_ms_dac1_dpms, + radeon_ms_dac1_get_modes, + radeon_ms_dac1_mode_fixup, + radeon_ms_dac1_mode_set, + radeon_ms_dac1_restore, + radeon_ms_dac1_save +}; + +static struct radeon_ms_output radeon_ms_dac2 = { + OUTPUT_DAC2, + NULL, + NULL, + radeon_ms_dac2_initialize, + radeon_ms_dac2_detect, + radeon_ms_dac2_dpms, + radeon_ms_dac2_get_modes, + radeon_ms_dac2_mode_fixup, + radeon_ms_dac2_mode_set, + radeon_ms_dac2_restore, + radeon_ms_dac2_save +}; + +static struct radeon_ms_connector radeon_ms_vga = { + NULL, NULL, NULL, CONNECTOR_VGA, MT_NONE, 0, GPIO_DDC1, + { + 0, -1, -1, -1, -1, -1, -1, -1 + }, + "VGA" +}; + +static struct radeon_ms_connector radeon_ms_dvi_i_2 = { + NULL, NULL, NULL, CONNECTOR_DVI_I, MT_NONE, 0, GPIO_DDC2, + { + 1, -1, -1, -1, -1, -1, -1, -1 + }, + "DVI-I" +}; + +static struct radeon_ms_properties properties[] = { + /* default only one VGA connector */ + { + 0, 0, 27000, 25000, 200000, 1, 1, 1, 1, + { + &radeon_ms_dac1, NULL, NULL, NULL, NULL, NULL, NULL, + NULL + }, + { + &radeon_ms_vga, NULL, NULL, NULL, NULL, NULL, NULL, + NULL + } + }, + { + 0x1043, 0x176, 27000, 25000, 200000, 1, 1, 1, 1, + { + &radeon_ms_dac1, &radeon_ms_dac2, NULL, NULL, NULL, + NULL, NULL, NULL + }, + { + &radeon_ms_vga, &radeon_ms_dvi_i_2, NULL, NULL, NULL, + NULL, NULL, NULL + } + }, + { + 0x1002, 0x4150, 27000, 25000, 200000, 1, 1, 1, 1, + { + &radeon_ms_dac1, &radeon_ms_dac2, NULL, NULL, NULL, + NULL, NULL, NULL + }, + { + &radeon_ms_vga, &radeon_ms_dvi_i_2, NULL, NULL, NULL, + NULL, NULL, NULL + } + }, +}; + +extern const uint32_t radeon_cp_microcode[]; +extern const uint32_t r200_cp_microcode[]; +extern const uint32_t r300_cp_microcode[]; + +static void radeon_flush_cache(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t cmd[6]; + int i, ret; + + cmd[0] = CP_PACKET0(RB2D_DSTCACHE_CTLSTAT, 0); + cmd[1] = REG_S(RB2D_DSTCACHE_CTLSTAT, DC_FLUSH, 3); + cmd[2] = CP_PACKET0(RB3D_DSTCACHE_CTLSTAT, 0); + cmd[3] = REG_S(RB3D_DSTCACHE_CTLSTAT, DC_FLUSH, 3); + cmd[4] = CP_PACKET0(RB3D_ZCACHE_CTLSTAT, 0); + cmd[5] = RB3D_ZCACHE_CTLSTAT__ZC_FLUSH; + /* try to wait but if we timeout we likely are in bad situation */ + for (i = 0; i < dev_priv->usec_timeout; i++) { + ret = radeon_ms_ring_emit(dev, cmd, 6); + if (!ret) { + break; + } + } +} + +static void r300_flush_cache(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t cmd[6]; + int i, ret; + + cmd[0] = CP_PACKET0(RB2D_DSTCACHE_CTLSTAT, 0); + cmd[1] = REG_S(RB2D_DSTCACHE_CTLSTAT, DC_FLUSH, 3); + cmd[2] = CP_PACKET0(RB3D_DSTCACHE_CTLSTAT_R3, 0); + cmd[3] = REG_S(RB3D_DSTCACHE_CTLSTAT_R3, DC_FLUSH, 3); + cmd[4] = CP_PACKET0(RB3D_ZCACHE_CTLSTAT_R3, 0); + cmd[5] = RB3D_ZCACHE_CTLSTAT_R3__ZC_FLUSH; + /* try to wait but if we timeout we likely are in bad situation */ + for (i = 0; i < dev_priv->usec_timeout; i++) { + ret = radeon_ms_ring_emit(dev, cmd, 6); + if (!ret) { + break; + } + } +} + +int radeon_ms_family_init(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + int i; + + dev_priv->microcode = radeon_cp_microcode; + dev_priv->irq_emit = radeon_ms_irq_emit; + + switch (dev_priv->family) { + case CHIP_R100: + case CHIP_R200: + dev_priv->microcode = radeon_cp_microcode; + dev_priv->flush_cache = radeon_flush_cache; + break; + case CHIP_RV200: + case CHIP_RV250: + case CHIP_RV280: + case CHIP_RS300: + dev_priv->microcode = r200_cp_microcode; + dev_priv->flush_cache = radeon_flush_cache; + break; + case CHIP_R300: + case CHIP_R350: + case CHIP_R360: + case CHIP_RV350: + case CHIP_RV370: + case CHIP_RV380: + case CHIP_RS400: + case CHIP_RV410: + case CHIP_R420: + case CHIP_R430: + case CHIP_R480: + dev_priv->microcode = r300_cp_microcode; + dev_priv->flush_cache = r300_flush_cache; + break; + default: + DRM_ERROR("Unknown radeon family, aborting\n"); + return -EINVAL; + } + switch (dev_priv->bus_type) { + case RADEON_AGP: + dev_priv->create_ttm = drm_agp_init_ttm; + dev_priv->bus_init = radeon_ms_agp_init; + dev_priv->bus_restore = radeon_ms_agp_restore; + dev_priv->bus_save = radeon_ms_agp_save; + break; + case RADEON_PCIE: + dev_priv->create_ttm = radeon_ms_pcie_create_ttm; + dev_priv->bus_finish = radeon_ms_pcie_finish; + dev_priv->bus_init = radeon_ms_pcie_init; + dev_priv->bus_restore = radeon_ms_pcie_restore; + dev_priv->bus_save = radeon_ms_pcie_save; + break; + default: + DRM_ERROR("Unknown radeon bus type, aborting\n"); + return -EINVAL; + } + dev_priv->properties = NULL; + for (i = 1; i < sizeof(properties)/sizeof(properties[0]); i++) { + if (dev->pdev->subsystem_vendor == properties[i].subvendor && + dev->pdev->subsystem_device == properties[i].subdevice) { + DRM_INFO("[radeon_ms] found properties for 0x%04X:0x%04X\n", + properties[i].subvendor, properties[i].subdevice); + dev_priv->properties = &properties[i]; + } + } + if (dev_priv->properties == NULL) { + dev_priv->properties = &properties[0]; + } + return 0; +} diff --git a/shared-core/radeon_ms_fence.c b/shared-core/radeon_ms_fence.c new file mode 100644 index 00000000..6fcf5437 --- /dev/null +++ b/shared-core/radeon_ms_fence.c @@ -0,0 +1,129 @@ +/* + * Copyright 2007 Dave Airlie. + * Copyright 2007 Jérôme Glisse + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Dave Airlie + * Jerome Glisse <glisse@freedesktop.org> + */ +#include "radeon_ms.h" + +static void radeon_ms_fence_flush(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct drm_fence_class_manager *fc = &dev->fm.fence_class[0]; + uint32_t pending_flush_types = 0; + uint32_t sequence; + + if (dev_priv == NULL) { + return; + } + pending_flush_types = fc->pending_flush | + ((fc->pending_exe_flush) ? + DRM_FENCE_TYPE_EXE : 0); + if (pending_flush_types) { + sequence = mmio_read(dev_priv, dev_priv->fence_reg); + drm_fence_handler(dev, 0, sequence, pending_flush_types, 0); + } +} + +int radeon_ms_fence_emit_sequence(struct drm_device *dev, uint32_t class, + uint32_t flags, uint32_t *sequence, + uint32_t *native_type) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t fence_id, cmd[2], i, ret; + + if (!dev_priv || dev_priv->cp_ready != 1) { + return -EINVAL; + } + fence_id = (++dev_priv->fence_id_last); + if (dev_priv->fence_id_last > 0x7FFFFFFF) { + fence_id = dev_priv->fence_id_last = 1; + } + *sequence = fence_id; + *native_type = DRM_FENCE_TYPE_EXE; + if (flags & DRM_RADEON_FENCE_FLAG_FLUSHED) { + *native_type |= DRM_RADEON_FENCE_TYPE_RW; + dev_priv->flush_cache(dev); + } + cmd[0] = CP_PACKET0(dev_priv->fence_reg, 0); + cmd[1] = fence_id; + for (i = 0; i < dev_priv->usec_timeout; i++) { + ret = radeon_ms_ring_emit(dev, cmd, 2); + if (!ret) { + dev_priv->irq_emit(dev); + return 0; + } + } + return -EBUSY; +} + +void radeon_ms_fence_handler(struct drm_device * dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct drm_fence_manager *fm = &dev->fm; + + if (dev_priv == NULL) { + return; + } + + write_lock(&fm->lock); + radeon_ms_fence_flush(dev); + write_unlock(&fm->lock); +} + +int radeon_ms_fence_has_irq(struct drm_device *dev, uint32_t class, + uint32_t flags) +{ + /* + * We have an irq that tells us when we have a new breadcrumb. + */ + if (class == 0 && flags == DRM_FENCE_TYPE_EXE) + return 1; + + return 0; +} + +int radeon_ms_fence_types(struct drm_buffer_object *bo, + uint32_t *class, uint32_t *type) +{ + *class = 0; + if (bo->mem.flags & (DRM_BO_FLAG_READ | DRM_BO_FLAG_WRITE)) + *type = 3; + else + *type = 1; + return 0; +} + +void radeon_ms_poke_flush(struct drm_device *dev, uint32_t class) +{ + struct drm_fence_manager *fm = &dev->fm; + unsigned long flags; + + if (class != 0) + return; + write_lock_irqsave(&fm->lock, flags); + radeon_ms_fence_flush(dev); + write_unlock_irqrestore(&fm->lock, flags); +} diff --git a/shared-core/radeon_ms_gpu.c b/shared-core/radeon_ms_gpu.c new file mode 100644 index 00000000..28683781 --- /dev/null +++ b/shared-core/radeon_ms_gpu.c @@ -0,0 +1,589 @@ +/* + * Copyright 2007 Jérôme Glisse + * Copyright 2007 Alex Deucher + * Copyright 2007 Dave Airlie + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation on the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT. IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR + * THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "radeon_ms.h" + +static int radeon_ms_gpu_address_space_init(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_state *state = &dev_priv->driver_state; + + /* initialize gpu mapping */ + dev_priv->gpu_vram_start = dev_priv->vram.offset; + dev_priv->gpu_vram_end = dev_priv->gpu_vram_start + dev_priv->vram.size; + /* align it on 16Mo boundary (clamp memory which is then + * unreachable but not manufacturer should use strange + * memory size */ + dev_priv->gpu_vram_end = dev_priv->gpu_vram_end & (~0xFFFFFF); + dev_priv->gpu_vram_end -= 1; + dev_priv->gpu_vram_size = dev_priv->gpu_vram_end - + dev_priv->gpu_vram_start + 1; + /* set gart size to 32Mo FIXME: should make a parameter for this */ + dev_priv->gpu_gart_size = 1024 * 1024 * 32; + if (dev_priv->gpu_gart_size > (0xffffffffU - dev_priv->gpu_vram_end)) { + /* align gart start to next 4Ko in gpu address space */ + dev_priv->gpu_gart_start = (dev_priv->gpu_vram_end + 1) + 0xfff; + dev_priv->gpu_gart_start = dev_priv->gpu_gart_start & (~0xfff); + dev_priv->gpu_gart_end = dev_priv->gpu_gart_start + + dev_priv->gpu_gart_size; + dev_priv->gpu_gart_end = (dev_priv->gpu_gart_end & (~0xfff)) - + 0x1000; + } else { + /* align gart start to next 4Ko in gpu address space */ + dev_priv->gpu_gart_start = (dev_priv->gpu_vram_start & ~0xfff) - + dev_priv->gpu_gart_size; + dev_priv->gpu_gart_start = dev_priv->gpu_gart_start & (~0xfff); + dev_priv->gpu_gart_end = dev_priv->gpu_gart_start + + dev_priv->gpu_gart_size; + dev_priv->gpu_gart_end = (dev_priv->gpu_gart_end & (~0xfff)) - + 0x1000; + } + state->mc_fb_location = + REG_S(MC_FB_LOCATION, MC_FB_START, + dev_priv->gpu_vram_start >> 16) | + REG_S(MC_FB_LOCATION, MC_FB_TOP, dev_priv->gpu_vram_end >> 16); + state->display_base_addr = + REG_S(DISPLAY_BASE_ADDR, DISPLAY_BASE_ADDR, + dev_priv->gpu_vram_start); + state->config_aper_0_base = dev_priv->gpu_vram_start; + state->config_aper_1_base = dev_priv->gpu_vram_start; + state->config_aper_size = dev_priv->gpu_vram_size; + DRM_INFO("[radeon_ms] gpu vram start 0x%08X\n", + dev_priv->gpu_vram_start); + DRM_INFO("[radeon_ms] gpu vram end 0x%08X\n", + dev_priv->gpu_vram_end); + DRM_INFO("[radeon_ms] gpu gart start 0x%08X\n", + dev_priv->gpu_gart_start); + DRM_INFO("[radeon_ms] gpu gart end 0x%08X\n", + dev_priv->gpu_gart_end); + return 0; +} + +static void radeon_ms_gpu_reset(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t clock_cntl_index, mclk_cntl, rbbm_soft_reset; + uint32_t reset_mask, host_path_cntl, cache_mode; + + radeon_ms_cp_stop(dev); + radeon_ms_gpu_flush(dev); + + /* reset clock */ + clock_cntl_index = MMIO_R(CLOCK_CNTL_INDEX); + pll_index_errata(dev_priv); + mclk_cntl = PPLL_R(MCLK_CNTL); + PPLL_W(MCLK_CNTL, + mclk_cntl | + MCLK_CNTL__FORCE_MCLKA | + MCLK_CNTL__FORCE_MCLKB | + MCLK_CNTL__FORCE_YCLKA | + MCLK_CNTL__FORCE_YCLKB | + MCLK_CNTL__FORCE_MC | + MCLK_CNTL__FORCE_AIC); + PPLL_W(SCLK_CNTL, + PPLL_R(SCLK_CNTL) | + SCLK_CNTL__FORCE_CP | + SCLK_CNTL__FORCE_VIP); + + /* Soft resetting HDP thru RBBM_SOFT_RESET register can cause some + * unexpected behaviour on some machines. Here we use + * RADEON_HOST_PATH_CNTL to reset it. + */ + host_path_cntl = MMIO_R(HOST_PATH_CNTL); + rbbm_soft_reset = MMIO_R(RBBM_SOFT_RESET); + reset_mask = RBBM_SOFT_RESET__SOFT_RESET_CP | + RBBM_SOFT_RESET__SOFT_RESET_HI | + RBBM_SOFT_RESET__SOFT_RESET_VAP | + RBBM_SOFT_RESET__SOFT_RESET_SE | + RBBM_SOFT_RESET__SOFT_RESET_RE | + RBBM_SOFT_RESET__SOFT_RESET_PP | + RBBM_SOFT_RESET__SOFT_RESET_E2 | + RBBM_SOFT_RESET__SOFT_RESET_RB; + MMIO_W(RBBM_SOFT_RESET, rbbm_soft_reset | reset_mask); + MMIO_R(RBBM_SOFT_RESET); + MMIO_W(RBBM_SOFT_RESET, 0); + MMIO_R(RBBM_SOFT_RESET); + + cache_mode = MMIO_R(RB2D_DSTCACHE_MODE); + MMIO_W(RB2D_DSTCACHE_MODE, + cache_mode | RB2D_DSTCACHE_MODE__DC_DISABLE_IGNORE_PE); + + MMIO_W(HOST_PATH_CNTL, host_path_cntl | HOST_PATH_CNTL__HDP_SOFT_RESET); + MMIO_R(HOST_PATH_CNTL); + MMIO_W(HOST_PATH_CNTL, host_path_cntl); + MMIO_R(HOST_PATH_CNTL); + + MMIO_W(CLOCK_CNTL_INDEX, clock_cntl_index); + pll_index_errata(dev_priv); + PPLL_W(MCLK_CNTL, mclk_cntl); +} + +static void radeon_ms_gpu_resume(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t a; + uint32_t i; + + /* make sure we have sane offset before restoring crtc */ + a = (MMIO_R(MC_FB_LOCATION) & MC_FB_LOCATION__MC_FB_START__MASK) << 16; + MMIO_W(DISPLAY_BASE_ADDR, a); + MMIO_W(CRTC2_DISPLAY_BASE_ADDR, a); + MMIO_W(CRTC_OFFSET, 0); + MMIO_W(CUR_OFFSET, 0); + MMIO_W(CRTC_OFFSET_CNTL, CRTC_OFFSET_CNTL__CRTC_OFFSET_FLIP_CNTL); + MMIO_W(CRTC2_OFFSET, 0); + MMIO_W(CUR2_OFFSET, 0); + MMIO_W(CRTC2_OFFSET_CNTL, CRTC2_OFFSET_CNTL__CRTC2_OFFSET_FLIP_CNTL); + for (i = 0; i < dev_priv->usec_timeout; i++) { + if (!(CRTC_OFFSET__CRTC_GUI_TRIG_OFFSET & + MMIO_R(CRTC_OFFSET))) { + break; + } + DRM_UDELAY(1); + } + if (i >= dev_priv->usec_timeout) { + DRM_ERROR("[radeon_ms] timeout waiting for crtc...\n"); + } + for (i = 0; i < dev_priv->usec_timeout; i++) { + if (!(CRTC2_OFFSET__CRTC2_GUI_TRIG_OFFSET & + MMIO_R(CRTC2_OFFSET))) { + break; + } + DRM_UDELAY(1); + } + if (i >= dev_priv->usec_timeout) { + DRM_ERROR("[radeon_ms] timeout waiting for crtc...\n"); + } + DRM_UDELAY(10000); +} + +static void radeon_ms_gpu_stop(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t ov0_scale_cntl, crtc_ext_cntl, crtc_gen_cntl; + uint32_t crtc2_gen_cntl, i; + + radeon_ms_wait_for_idle(dev); + /* Capture MC_STATUS in case things go wrong ... */ + ov0_scale_cntl = dev_priv->ov0_scale_cntl = MMIO_R(OV0_SCALE_CNTL); + crtc_ext_cntl = dev_priv->crtc_ext_cntl = MMIO_R(CRTC_EXT_CNTL); + crtc_gen_cntl = dev_priv->crtc_gen_cntl = MMIO_R(CRTC_GEN_CNTL); + crtc2_gen_cntl = dev_priv->crtc2_gen_cntl = MMIO_R(CRTC2_GEN_CNTL); + ov0_scale_cntl &= ~OV0_SCALE_CNTL__OV0_OVERLAY_EN__MASK; + crtc_ext_cntl |= CRTC_EXT_CNTL__CRTC_DISPLAY_DIS; + crtc_gen_cntl &= ~CRTC_GEN_CNTL__CRTC_CUR_EN; + crtc_gen_cntl &= ~CRTC_GEN_CNTL__CRTC_ICON_EN; + crtc_gen_cntl |= CRTC_GEN_CNTL__CRTC_EXT_DISP_EN; + crtc_gen_cntl |= CRTC_GEN_CNTL__CRTC_DISP_REQ_EN_B; + crtc2_gen_cntl &= ~CRTC2_GEN_CNTL__CRTC2_CUR_EN; + crtc2_gen_cntl &= ~CRTC2_GEN_CNTL__CRTC2_ICON_EN; + crtc2_gen_cntl |= CRTC2_GEN_CNTL__CRTC2_DISP_REQ_EN_B; + MMIO_W(OV0_SCALE_CNTL, ov0_scale_cntl); + MMIO_W(CRTC_EXT_CNTL, crtc_ext_cntl); + MMIO_W(CRTC_GEN_CNTL, crtc_gen_cntl); + MMIO_W(CRTC2_GEN_CNTL, crtc2_gen_cntl); + DRM_UDELAY(10000); + switch (dev_priv->family) { + case CHIP_R100: + case CHIP_R200: + case CHIP_RV200: + case CHIP_RV250: + case CHIP_RV280: + case CHIP_RS300: + for (i = 0; i < dev_priv->usec_timeout; i++) { + if ((MC_STATUS__MC_IDLE & MMIO_R(MC_STATUS))) { + DRM_INFO("[radeon_ms] gpu stoped in %d usecs\n", + i); + return; + } + DRM_UDELAY(1); + } + break; + case CHIP_R300: + case CHIP_R350: + case CHIP_R360: + case CHIP_RV350: + case CHIP_RV370: + case CHIP_RV380: + case CHIP_RS400: + case CHIP_RV410: + case CHIP_R420: + case CHIP_R430: + case CHIP_R480: + for (i = 0; i < dev_priv->usec_timeout; i++) { + if ((MC_STATUS__MC_IDLE_R3 & MMIO_R(MC_STATUS))) { + DRM_INFO("[radeon_ms] gpu stoped in %d usecs\n", + i); + return; + } + DRM_UDELAY(1); + } + break; + default: + DRM_ERROR("Unknown radeon family, aborting\n"); + return; + } + DRM_ERROR("[radeon_ms] failed to stop gpu...will proceed anyway\n"); + DRM_UDELAY(20000); +} + +static int radeon_ms_wait_for_fifo(struct drm_device *dev, int num_fifo) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + int i; + + for (i = 0; i < dev_priv->usec_timeout; i++) { + int t; + t = RBBM_STATUS__CMDFIFO_AVAIL__MASK & MMIO_R(RBBM_STATUS); + t = t >> RBBM_STATUS__CMDFIFO_AVAIL__SHIFT; + if (t >= num_fifo) + return 0; + DRM_UDELAY(1); + } + DRM_ERROR("[radeon_ms] failed to wait for fifo\n"); + return -EBUSY; +} + +int radeon_ms_gpu_initialize(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_state *state = &dev_priv->driver_state; + int ret; + + state->disp_misc_cntl = DISP_MISC_CNTL__SYNC_PAD_FLOP_EN | + REG_S(DISP_MISC_CNTL, SYNC_STRENGTH, 2) | + REG_S(DISP_MISC_CNTL, PALETTE_MEM_RD_MARGIN, 0xb) | + REG_S(DISP_MISC_CNTL, PALETTE2_MEM_RD_MARGIN, 0xb) | + REG_S(DISP_MISC_CNTL, RMX_BUF_MEM_RD_MARGIN, 0x5); + state->disp_merge_cntl = REG_S(DISP_MERGE_CNTL, DISP_GRPH_ALPHA, 0xff) | + REG_S(DISP_MERGE_CNTL, DISP_OV0_ALPHA, 0xff); + state->disp_pwr_man = DISP_PWR_MAN__DISP_PWR_MAN_D3_CRTC_EN | + DISP_PWR_MAN__DISP2_PWR_MAN_D3_CRTC2_EN | + REG_S(DISP_PWR_MAN, DISP_PWR_MAN_DPMS, DISP_PWR_MAN_DPMS__OFF) | + DISP_PWR_MAN__DISP_D3_RST | + DISP_PWR_MAN__DISP_D3_REG_RST | + DISP_PWR_MAN__DISP_D3_GRPH_RST | + DISP_PWR_MAN__DISP_D3_SUBPIC_RST | + DISP_PWR_MAN__DISP_D3_OV0_RST | + DISP_PWR_MAN__DISP_D1D2_GRPH_RST | + DISP_PWR_MAN__DISP_D1D2_SUBPIC_RST | + DISP_PWR_MAN__DISP_D1D2_OV0_RST | + DISP_PWR_MAN__DISP_DVO_ENABLE_RST | + DISP_PWR_MAN__TV_ENABLE_RST; + state->disp2_merge_cntl = 0; + ret = radeon_ms_gpu_address_space_init(dev); + if (ret) { + return ret; + } + + /* initialize bus */ + ret = dev_priv->bus_init(dev); + if (ret != 0) { + return ret; + } + return 0; +} + +void radeon_ms_gpu_dpms(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_state *state = &dev_priv->driver_state; + + if (dev_priv->crtc1_dpms == dev_priv->crtc2_dpms) { + /* both crtc are in same state so use global display pwr */ + state->disp_pwr_man &= ~DISP_PWR_MAN__DISP_PWR_MAN_DPMS__MASK; + switch(dev_priv->crtc1_dpms) { + case DPMSModeOn: + state->disp_pwr_man |= REG_S(DISP_PWR_MAN, + DISP_PWR_MAN_DPMS, + DISP_PWR_MAN_DPMS__ON); + break; + case DPMSModeStandby: + state->disp_pwr_man |= REG_S(DISP_PWR_MAN, + DISP_PWR_MAN_DPMS, + DISP_PWR_MAN_DPMS__STANDBY); + break; + case DPMSModeSuspend: + state->disp_pwr_man |= REG_S(DISP_PWR_MAN, + DISP_PWR_MAN_DPMS, + DISP_PWR_MAN_DPMS__SUSPEND); + break; + case DPMSModeOff: + state->disp_pwr_man |= REG_S(DISP_PWR_MAN, + DISP_PWR_MAN_DPMS, + DISP_PWR_MAN_DPMS__OFF); + break; + default: + /* error */ + break; + } + MMIO_W(DISP_PWR_MAN, state->disp_pwr_man); + } else { + state->disp_pwr_man &= ~DISP_PWR_MAN__DISP_PWR_MAN_DPMS__MASK; + state->disp_pwr_man |= REG_S(DISP_PWR_MAN, + DISP_PWR_MAN_DPMS, + DISP_PWR_MAN_DPMS__ON); + MMIO_W(DISP_PWR_MAN, state->disp_pwr_man); + } +} + +void radeon_ms_gpu_flush(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t i; + uint32_t purge2d; + uint32_t purge3d; + + switch (dev_priv->family) { + case CHIP_R100: + case CHIP_R200: + case CHIP_RV200: + case CHIP_RV250: + case CHIP_RV280: + case CHIP_RS300: + purge2d = REG_S(RB2D_DSTCACHE_CTLSTAT, DC_FLUSH, 3) | + REG_S(RB2D_DSTCACHE_CTLSTAT, DC_FREE, 3); + purge3d = REG_S(RB3D_DSTCACHE_CTLSTAT, DC_FLUSH, 3) | + REG_S(RB3D_DSTCACHE_CTLSTAT, DC_FREE, 3); + MMIO_W(RB2D_DSTCACHE_CTLSTAT, purge2d); + MMIO_W(RB3D_DSTCACHE_CTLSTAT, purge3d); + break; + case CHIP_R300: + case CHIP_R350: + case CHIP_R360: + case CHIP_RV350: + case CHIP_RV370: + case CHIP_RV380: + case CHIP_RS400: + case CHIP_RV410: + case CHIP_R420: + case CHIP_R430: + case CHIP_R480: + purge2d = REG_S(RB2D_DSTCACHE_CTLSTAT, DC_FLUSH, 3) | + REG_S(RB2D_DSTCACHE_CTLSTAT, DC_FREE, 3); + purge3d = REG_S(RB3D_DSTCACHE_CTLSTAT_R3, DC_FLUSH, 3) | + REG_S(RB3D_DSTCACHE_CTLSTAT_R3, DC_FREE, 3); + MMIO_W(RB2D_DSTCACHE_CTLSTAT, purge2d); + MMIO_W(RB3D_DSTCACHE_CTLSTAT_R3, purge3d); + break; + default: + DRM_ERROR("Unknown radeon family, aborting\n"); + return; + } + for (i = 0; i < dev_priv->usec_timeout; i++) { + if (!(RB2D_DSTCACHE_CTLSTAT__DC_BUSY & + MMIO_R(RB2D_DSTCACHE_CTLSTAT))) { + return; + } + DRM_UDELAY(1); + } + DRM_ERROR("[radeon_ms] gpu flush timeout\n"); +} + +void radeon_ms_gpu_restore(struct drm_device *dev, struct radeon_state *state) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t wait_until; + uint32_t fbstart; + int ret, ok = 1; + + radeon_ms_gpu_reset(dev); + radeon_ms_wait_for_idle(dev); + radeon_ms_gpu_stop(dev); + + MMIO_W(AIC_CTRL, state->aic_ctrl); + MMIO_W(MC_FB_LOCATION, state->mc_fb_location); + MMIO_R(MC_FB_LOCATION); + MMIO_W(CONFIG_APER_0_BASE, state->config_aper_0_base); + MMIO_W(CONFIG_APER_1_BASE, state->config_aper_1_base); + MMIO_W(CONFIG_APER_SIZE, state->config_aper_size); + MMIO_W(DISPLAY_BASE_ADDR, state->display_base_addr); + if (dev_priv->bus_restore) { + dev_priv->bus_restore(dev, state); + } + + radeon_ms_gpu_reset(dev); + radeon_ms_gpu_resume(dev); + + MMIO_W(BUS_CNTL, state->bus_cntl); + wait_until = WAIT_UNTIL__WAIT_DMA_VIPH0_IDLE | + WAIT_UNTIL__WAIT_DMA_VIPH1_IDLE | + WAIT_UNTIL__WAIT_DMA_VIPH2_IDLE | + WAIT_UNTIL__WAIT_DMA_VIPH3_IDLE | + WAIT_UNTIL__WAIT_DMA_VID_IDLE | + WAIT_UNTIL__WAIT_DMA_GUI_IDLE | + WAIT_UNTIL__WAIT_2D_IDLE | + WAIT_UNTIL__WAIT_3D_IDLE | + WAIT_UNTIL__WAIT_2D_IDLECLEAN | + WAIT_UNTIL__WAIT_3D_IDLECLEAN | + WAIT_UNTIL__WAIT_HOST_IDLECLEAN; + switch (dev_priv->family) { + case CHIP_R100: + case CHIP_R200: + case CHIP_RV200: + case CHIP_RV250: + case CHIP_RV280: + case CHIP_RS300: + break; + case CHIP_R300: + case CHIP_R350: + case CHIP_R360: + case CHIP_RV350: + case CHIP_RV370: + case CHIP_RV380: + case CHIP_RS400: + case CHIP_RV410: + case CHIP_R420: + case CHIP_R430: + case CHIP_R480: + wait_until |= WAIT_UNTIL__WAIT_VAP_IDLE; + break; + } + MMIO_W(WAIT_UNTIL, wait_until); + MMIO_W(DISP_MISC_CNTL, state->disp_misc_cntl); + MMIO_W(DISP_PWR_MAN, state->disp_pwr_man); + MMIO_W(DISP_MERGE_CNTL, state->disp_merge_cntl); + MMIO_W(DISP_OUTPUT_CNTL, state->disp_output_cntl); + MMIO_W(DISP2_MERGE_CNTL, state->disp2_merge_cntl); + + /* Setup engine location. This shouldn't be necessary since we + * set them appropriately before any accel ops, but let's avoid + * random bogus DMA in case we inadvertently trigger the engine + * in the wrong place (happened). + */ + ret = radeon_ms_wait_for_fifo(dev, 2); + if (ret) { + ok = 0; + DRM_ERROR("[radeon_ms] no fifo for setting up dst & src gui\n"); + DRM_ERROR("[radeon_ms] proceed anyway\n"); + } + fbstart = (MC_FB_LOCATION__MC_FB_START__MASK & + MMIO_R(MC_FB_LOCATION)) << 16; + MMIO_W(DST_PITCH_OFFSET, + REG_S(DST_PITCH_OFFSET, DST_OFFSET, fbstart >> 10)); + MMIO_W(SRC_PITCH_OFFSET, + REG_S(SRC_PITCH_OFFSET, SRC_OFFSET, fbstart >> 10)); + + ret = radeon_ms_wait_for_fifo(dev, 1); + if (ret) { + ok = 0; + DRM_ERROR("[radeon_ms] no fifo for setting up dp data type\n"); + DRM_ERROR("[radeon_ms] proceed anyway\n"); + } +#ifdef __BIG_ENDIAN + MMIO_W(DP_DATATYPE, DP_DATATYPE__DP_BYTE_PIX_ORDER); +#else + MMIO_W(DP_DATATYPE, 0); +#endif + + ret = radeon_ms_wait_for_fifo(dev, 1); + if (ret) { + ok = 0; + DRM_ERROR("[radeon_ms] no fifo for setting up surface cntl\n"); + DRM_ERROR("[radeon_ms] proceed anyway\n"); + } + MMIO_W(SURFACE_CNTL, SURFACE_CNTL__SURF_TRANSLATION_DIS); + + ret = radeon_ms_wait_for_fifo(dev, 2); + if (ret) { + ok = 0; + DRM_ERROR("[radeon_ms] no fifo for setting scissor\n"); + DRM_ERROR("[radeon_ms] proceed anyway\n"); + } + MMIO_W(DEFAULT_SC_BOTTOM_RIGHT, 0x1fff1fff); + MMIO_W(DEFAULT2_SC_BOTTOM_RIGHT, 0x1fff1fff); + + ret = radeon_ms_wait_for_fifo(dev, 1); + if (ret) { + ok = 0; + DRM_ERROR("[radeon_ms] no fifo for setting up gui cntl\n"); + DRM_ERROR("[radeon_ms] proceed anyway\n"); + } + MMIO_W(DP_GUI_MASTER_CNTL, 0); + + ret = radeon_ms_wait_for_fifo(dev, 5); + if (ret) { + ok = 0; + DRM_ERROR("[radeon_ms] no fifo for setting up clear color\n"); + DRM_ERROR("[radeon_ms] proceed anyway\n"); + } + MMIO_W(DP_BRUSH_BKGD_CLR, 0x00000000); + MMIO_W(DP_BRUSH_FRGD_CLR, 0xffffffff); + MMIO_W(DP_SRC_BKGD_CLR, 0x00000000); + MMIO_W(DP_SRC_FRGD_CLR, 0xffffffff); + MMIO_W(DP_WRITE_MSK, 0xffffffff); + + if (!ok) { + DRM_ERROR("[radeon_ms] engine restore not enough fifo\n"); + } +} + +void radeon_ms_gpu_save(struct drm_device *dev, struct radeon_state *state) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + state->aic_ctrl = MMIO_R(AIC_CTRL); + state->bus_cntl = MMIO_R(BUS_CNTL); + state->mc_fb_location = MMIO_R(MC_FB_LOCATION); + state->display_base_addr = MMIO_R(DISPLAY_BASE_ADDR); + state->config_aper_0_base = MMIO_R(CONFIG_APER_0_BASE); + state->config_aper_1_base = MMIO_R(CONFIG_APER_1_BASE); + state->config_aper_size = MMIO_R(CONFIG_APER_SIZE); + state->disp_misc_cntl = MMIO_R(DISP_MISC_CNTL); + state->disp_pwr_man = MMIO_R(DISP_PWR_MAN); + state->disp_merge_cntl = MMIO_R(DISP_MERGE_CNTL); + state->disp_output_cntl = MMIO_R(DISP_OUTPUT_CNTL); + state->disp2_merge_cntl = MMIO_R(DISP2_MERGE_CNTL); + if (dev_priv->bus_save) { + dev_priv->bus_save(dev, state); + } +} + +int radeon_ms_wait_for_idle(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + int i, j, ret; + + for (i = 0; i < 2; i++) { + ret = radeon_ms_wait_for_fifo(dev, 64); + if (ret) { + DRM_ERROR("[radeon_ms] fifo not empty\n"); + } + for (j = 0; j < dev_priv->usec_timeout; j++) { + if (!(RBBM_STATUS__GUI_ACTIVE & MMIO_R(RBBM_STATUS))) { + radeon_ms_gpu_flush(dev); + return 0; + } + DRM_UDELAY(1); + } + DRM_ERROR("[radeon_ms] idle timed out: status=0x%08x\n", + MMIO_R(RBBM_STATUS)); + radeon_ms_gpu_stop(dev); + radeon_ms_gpu_reset(dev); + } + return -EBUSY; +} diff --git a/shared-core/radeon_ms_i2c.c b/shared-core/radeon_ms_i2c.c new file mode 100644 index 00000000..1801c496 --- /dev/null +++ b/shared-core/radeon_ms_i2c.c @@ -0,0 +1,279 @@ +/* + * Copyright 2007 Jérôme Glisse + * Copyright 2007 Alex Deucher + * Copyright 2007 Dave Airlie + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation on the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT. IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR + * THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "radeon_ms.h" + +static int get_clock(void *data) +{ + struct radeon_ms_i2c *i2c = data; + struct drm_radeon_private *dev_priv = i2c->drm_dev->dev_private; + int v; + + switch (i2c->reg) { + case VIPPAD_EN: + v = MMIO_R(VIPPAD_Y); + if ((REG_G(VIPPAD_Y, VIPPAD_Y_VHAD, v) & 2)) { + v = 1; + } else { + v = 0; + } + break; + case VIPPAD1_EN: + v = MMIO_R(VIPPAD1_Y); + if ((REG_G(VIPPAD1_Y, VIPPAD_Y_DVODATA, v) & 8)) { + v = 1; + } else { + v = 0; + } + break; + case GPIO_DDC1: + v = MMIO_R(GPIO_DDC1); + if ((GPIO_DDC1__DDC1_CLK_INPUT & v)) { + v = 1; + } else { + v = 0; + } + break; + case GPIO_DDC2: + v = MMIO_R(GPIO_DDC2); + if ((GPIO_DDC2__DDC2_CLK_INPUT & v)) { + v = 1; + } else { + v = 0; + } + break; + default: + v = 0; + break; + } + return v; +} + +static int get_data(void *data) +{ + struct radeon_ms_i2c *i2c = data; + struct drm_radeon_private *dev_priv = i2c->drm_dev->dev_private; + int v; + + switch (i2c->reg) { + case VIPPAD_EN: + v = MMIO_R(VIPPAD_Y); + if ((REG_G(VIPPAD_Y, VIPPAD_Y_VHAD, v) & 1)) { + v = 1; + } else { + v = 0; + } + break; + case VIPPAD1_EN: + v = MMIO_R(VIPPAD1_Y); + if ((REG_G(VIPPAD1_Y, VIPPAD_Y_DVODATA, v) & 4)) { + v = 1; + } else { + v = 0; + } + break; + case GPIO_DDC1: + v = MMIO_R(GPIO_DDC1); + if ((GPIO_DDC1__DDC1_DATA_INPUT & v)) { + v = 1; + } else { + v = 0; + } + break; + case GPIO_DDC2: + v = MMIO_R(GPIO_DDC2); + if ((GPIO_DDC2__DDC2_DATA_INPUT & v)) { + v = 1; + } else { + v = 0; + } + break; + default: + v = 0; + break; + } + return v; +} + +static void set_clock(void *i2c_priv, int clock) +{ + struct radeon_ms_i2c *i2c = i2c_priv; + struct drm_radeon_private *dev_priv = i2c->drm_dev->dev_private; + int v, line; + + v = MMIO_R(i2c->reg); + switch (i2c->reg) { + case VIPPAD_EN: + line = REG_G(VIPPAD_EN, VIPPAD_EN_VHAD, v) & ~2; + v &= ~VIPPAD_EN__VIPPAD_EN_VHAD__MASK; + if (!clock) { + v |= REG_S(VIPPAD_EN, VIPPAD_EN_VHAD, line | 2); + } else { + v |= REG_S(VIPPAD_EN, VIPPAD_EN_VHAD, line); + } + break; + case VIPPAD1_EN: + line = REG_G(VIPPAD1_EN, VIPPAD_EN_DVODATA, v) & ~8; + v &= ~VIPPAD1_EN__VIPPAD_EN_DVODATA__MASK; + if (!clock) { + v |= REG_S(VIPPAD1_EN, VIPPAD_EN_DVODATA, line | 8); + } else { + v |= REG_S(VIPPAD1_EN, VIPPAD_EN_DVODATA, line); + } + break; + case GPIO_DDC1: + v &= ~GPIO_DDC1__DDC1_CLK_OUT_EN; + if (!clock) { + v |= GPIO_DDC1__DDC1_CLK_OUT_EN; + } + break; + case GPIO_DDC2: + v &= ~GPIO_DDC2__DDC2_CLK_OUT_EN; + if (!clock) { + v |= GPIO_DDC2__DDC2_CLK_OUT_EN; + } + break; + default: + return; + } + MMIO_W(i2c->reg, v); +} + +static void set_data(void *i2c_priv, int data) +{ + struct radeon_ms_i2c *i2c = i2c_priv; + struct drm_radeon_private *dev_priv = i2c->drm_dev->dev_private; + int v, line; + + v = MMIO_R(i2c->reg); + switch (i2c->reg) { + case VIPPAD_EN: + line = REG_G(VIPPAD_EN, VIPPAD_EN_VHAD, v) & ~1; + v &= ~VIPPAD_EN__VIPPAD_EN_VHAD__MASK; + if (!data) { + v |= REG_S(VIPPAD_EN, VIPPAD_EN_VHAD, line | 1); + } else { + v |= REG_S(VIPPAD_EN, VIPPAD_EN_VHAD, line); + } + break; + case VIPPAD1_EN: + line = REG_G(VIPPAD1_EN, VIPPAD_EN_DVODATA, v) & ~4; + v &= ~VIPPAD1_EN__VIPPAD_EN_DVODATA__MASK; + if (!data) { + v |= REG_S(VIPPAD1_EN, VIPPAD_EN_DVODATA, line | 4); + } else { + v |= REG_S(VIPPAD1_EN, VIPPAD_EN_DVODATA, line); + } + break; + case GPIO_DDC1: + v &= ~GPIO_DDC1__DDC1_DATA_OUT_EN; + if (!data) { + v |= GPIO_DDC1__DDC1_DATA_OUT_EN; + } + break; + case GPIO_DDC2: + v &= ~GPIO_DDC2__DDC2_DATA_OUT_EN; + if (!data) { + v |= GPIO_DDC2__DDC2_DATA_OUT_EN; + } + break; + default: + return; + } + MMIO_W(i2c->reg, v); +} + +/** + * radeon_ms_i2c_create - instantiate an radeon i2c bus on 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). + * + */ +struct radeon_ms_i2c *radeon_ms_i2c_create(struct drm_device *dev, + const uint32_t reg, + const char *name) +{ + struct radeon_ms_i2c *i2c; + int ret; + + i2c = drm_alloc(sizeof(struct radeon_ms_i2c), DRM_MEM_DRIVER); + if (i2c == NULL) { + return NULL; + } + memset(i2c, 0, sizeof(struct radeon_ms_i2c)); + + i2c->drm_dev = dev; + i2c->reg = reg; + snprintf(i2c->adapter.name, I2C_NAME_SIZE, "radeon-%s", name); + i2c->adapter.owner = THIS_MODULE; + /* fixme need to take a look at what its needed for */ + i2c->adapter.id = I2C_HW_B_RADEON; + i2c->adapter.algo_data = &i2c->algo; + i2c->adapter.dev.parent = &dev->pdev->dev; + i2c->algo.setsda = set_data; + i2c->algo.setscl = set_clock; + i2c->algo.getsda = get_data; + i2c->algo.getscl = get_clock; + i2c->algo.udelay = 20; + i2c->algo.timeout = usecs_to_jiffies(2200); + i2c->algo.data = i2c; + + i2c_set_adapdata(&i2c->adapter, i2c); + + ret = i2c_bit_add_bus(&i2c->adapter); + if(ret) { + DRM_INFO("[radeon_ms] failed to register I2C '%s' bus\n", + i2c->adapter.name); + goto out_free; + } + DRM_INFO("[radeon_ms] registered I2C '%s' bus\n", i2c->adapter.name); + return i2c; + +out_free: + drm_free(i2c, sizeof(struct radeon_ms_i2c), DRM_MEM_DRIVER); + return NULL; +} + +/** + * radeon_ms_i2c_destroy - unregister and free i2c bus resources + * @output: channel to free + * + * Unregister the adapter from the i2c layer, then free the structure. + */ +void radeon_ms_i2c_destroy(struct radeon_ms_i2c *i2c) +{ + if (i2c == NULL) { + return; + } + i2c_del_adapter(&i2c->adapter); + drm_free(i2c, sizeof(struct radeon_ms_i2c), DRM_MEM_DRIVER); +} diff --git a/shared-core/radeon_ms_irq.c b/shared-core/radeon_ms_irq.c new file mode 100644 index 00000000..24182c75 --- /dev/null +++ b/shared-core/radeon_ms_irq.c @@ -0,0 +1,160 @@ +/* + * Copyright 2007 Jérôme Glisse + * Copyright 2007 Alex Deucher + * Copyright 2007 Dave Airlie + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Jerome Glisse <glisse@freedesktop.org> + */ +#include "drmP.h" +#include "drm.h" +#include "radeon_ms.h" + +static uint32_t radeon_ack_irqs(struct drm_radeon_private *dev_priv, + uint32_t mask) +{ + uint32_t irqs; + + irqs = MMIO_R(GEN_INT_STATUS); + if (irqs) { + MMIO_W(GEN_INT_STATUS, irqs); + } + if (irqs & (~mask)) { + /* reprogram irq */ + MMIO_W(GEN_INT_CNTL, dev_priv->driver_state.gen_int_cntl); + } + return irqs; +} + +void radeon_ms_irq_emit(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t cmd[4]; + int i, ret; + + cmd[0] = CP_PACKET0(GEN_INT_CNTL, 1); + cmd[1] = dev_priv->driver_state.gen_int_cntl | GEN_INT_CNTL__SW_INT_EN; + cmd[2] = GEN_INT_STATUS__SW_INT_SET; + /* try to wait but if we timeout we likely are in bad situation */ + for (i = 0; i < dev_priv->usec_timeout; i++) { + ret = radeon_ms_ring_emit(dev, cmd, 3); + if (!ret) { + break; + } + } +} + +static void radeon_ms_irq_enable(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_state *state = &dev_priv->driver_state; + + state->gen_int_cntl = GEN_INT_CNTL__SW_INT_EN; + radeon_ms_irq_restore(dev, state); +} + +irqreturn_t radeon_ms_irq_handler(DRM_IRQ_ARGS) +{ + struct drm_device *dev = (struct drm_device *)arg; + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t status, mask; + + /* Only consider the bits we're interested in - others could be used + * outside the DRM + */ + mask = GEN_INT_STATUS__SW_INT | + GEN_INT_STATUS__CRTC_VBLANK_STAT | + GEN_INT_STATUS__CRTC2_VBLANK_STAT; + status = radeon_ack_irqs(dev_priv, mask); + if (!status) { + return IRQ_NONE; + } + + /* SW interrupt */ + if (GEN_INT_STATUS__SW_INT & status) { + radeon_ms_fence_handler(dev); + } + radeon_ms_fence_handler(dev); + return IRQ_HANDLED; +} + +void radeon_ms_irq_preinstall(struct drm_device * dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_state *state = &dev_priv->driver_state; + uint32_t mask; + + /* Disable *all* interrupts */ + state->gen_int_cntl = 0; + radeon_ms_irq_restore(dev, state); + + /* Clear bits if they're already high */ + mask = GEN_INT_STATUS__SW_INT | + GEN_INT_STATUS__CRTC_VBLANK_STAT | + GEN_INT_STATUS__CRTC2_VBLANK_STAT; + radeon_ack_irqs(dev_priv, mask); +} + +void radeon_ms_irq_postinstall(struct drm_device * dev) +{ + radeon_ms_irq_enable(dev); +} + +int radeon_ms_irq_init(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_state *state = &dev_priv->driver_state; + + /* Disable *all* interrupts */ + state->gen_int_cntl = 0; + radeon_ms_irq_restore(dev, state); + return 0; +} + +void radeon_ms_irq_restore(struct drm_device *dev, struct radeon_state *state) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + MMIO_W(GEN_INT_CNTL, state->gen_int_cntl); +} + +void radeon_ms_irq_save(struct drm_device *dev, struct radeon_state *state) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + state->gen_int_cntl = MMIO_R(GEN_INT_CNTL); +} + +void radeon_ms_irq_uninstall(struct drm_device * dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_state *state = &dev_priv->driver_state; + + if (dev_priv == NULL) { + return; + } + + /* Disable *all* interrupts */ + state->gen_int_cntl = 0; + radeon_ms_irq_restore(dev, state); +} diff --git a/shared-core/radeon_ms_output.c b/shared-core/radeon_ms_output.c new file mode 100644 index 00000000..35e5c376 --- /dev/null +++ b/shared-core/radeon_ms_output.c @@ -0,0 +1,337 @@ +/* + * Copyright 2007 Jérôme Glisse + * Copyright 2007 Alex Deucher + * Copyright 2007 Dave Airlie + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation on the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT. IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR + * THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "radeon_ms.h" + +static struct radeon_ms_output *radeon_ms_connector_get_output( + struct drm_radeon_private *dev_priv, + struct radeon_ms_connector *connector, int i) +{ + if (connector->outputs[i] < 0) { + return NULL; + } + if (connector->outputs[i] >= RADEON_MAX_OUTPUTS) { + return NULL; + } + i = connector->outputs[i]; + if (dev_priv->outputs[i] == NULL) { + return NULL; + } + if (dev_priv->outputs[i]->connector == NULL) { + return dev_priv->outputs[i]; + } + if (dev_priv->outputs[i]->connector == connector) { + return dev_priv->outputs[i]; + } + return NULL; +} + +static void radeon_ms_output_dpms(struct drm_output *output, int mode) +{ + struct drm_radeon_private *dev_priv = output->dev->dev_private; + struct radeon_ms_connector *connector = output->driver_private; + struct radeon_ms_output *routput = NULL; + int i; + + if (connector == NULL) { + return; + } + for (i = 0; i < RADEON_MAX_OUTPUTS; i++) { + routput = radeon_ms_connector_get_output(dev_priv, + connector, i); + + if (routput) { + routput->connector = connector; + routput->dpms(routput, mode); + } + } + radeon_ms_gpu_dpms(output->dev); +} + +static int radeon_ms_output_mode_valid(struct drm_output *output, + struct drm_display_mode *mode) +{ + struct radeon_ms_connector *connector = output->driver_private; + + if (connector == NULL) { + return MODE_ERROR; + } + return MODE_OK; +} + +static bool radeon_ms_output_mode_fixup(struct drm_output *output, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void radeon_ms_output_prepare(struct drm_output *output) +{ + if (output->funcs->dpms) { + output->funcs->dpms(output, DPMSModeOff); + } +} + +static void radeon_ms_output_commit(struct drm_output *output) +{ + if (output->funcs->dpms) { + output->funcs->dpms(output, DPMSModeOn); + } +} + +static void radeon_ms_output_mode_set(struct drm_output *output, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_radeon_private *dev_priv = output->dev->dev_private; + struct radeon_ms_connector *connector = output->driver_private; + struct radeon_ms_crtc *crtc; + struct radeon_ms_output *routput = NULL; + int i; + + if (connector == NULL) { + return; + } + if (output->crtc == NULL) { + return; + } + crtc = output->crtc->driver_private; + connector->crtc = crtc->crtc; + /* catch unknown crtc */ + switch (connector->crtc) { + case 1: + case 2: + break; + default: + /* error */ + return; + } + for (i = 0; i < RADEON_MAX_OUTPUTS; i++) { + routput = radeon_ms_connector_get_output(dev_priv, + connector, i); + if (routput) { + routput->connector = connector; + routput->mode_set(routput, mode, adjusted_mode); + } + } +} + +static enum drm_output_status radeon_ms_output_detect(struct drm_output *output) +{ + struct radeon_ms_connector *connector = output->driver_private; + + if (connector == NULL || connector->i2c == NULL) { + return output_status_unknown; + } + kfree(connector->edid); + connector->edid = drm_get_edid(output, &connector->i2c->adapter); + if (connector->edid == NULL) { + return output_status_unknown; + } + return output_status_connected; +} + +static int radeon_ms_output_get_modes(struct drm_output *output) +{ + struct radeon_ms_connector *connector = output->driver_private; + int ret = 0; + + if (connector == NULL || connector->i2c == NULL) { + return 0; + } + if (connector->edid == NULL) { + return 0; + } + drm_mode_output_update_edid_property(output, connector->edid); + ret = drm_add_edid_modes(output, connector->edid); + kfree(connector->edid); + connector->edid = NULL; + return ret; +} + +static void radeon_ms_output_cleanup(struct drm_output *output) +{ + struct radeon_ms_connector *connector = output->driver_private; + + if (connector == NULL) { + return; + } + if (connector->edid) { + kfree(connector->edid); + } + connector->edid = NULL; + connector->output = NULL; + output->driver_private = NULL; +} + +static const struct drm_output_funcs radeon_ms_output_funcs = { + .dpms = radeon_ms_output_dpms, + .save = NULL, + .restore = NULL, + .mode_valid = radeon_ms_output_mode_valid, + .mode_fixup = radeon_ms_output_mode_fixup, + .prepare = radeon_ms_output_prepare, + .mode_set = radeon_ms_output_mode_set, + .commit = radeon_ms_output_commit, + .detect = radeon_ms_output_detect, + .get_modes = radeon_ms_output_get_modes, + .cleanup = radeon_ms_output_cleanup, +}; + +void radeon_ms_connectors_destroy(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_ms_connector *connector = NULL; + int i = 0; + + for (i = 0; i < RADEON_MAX_CONNECTORS; i++) { + if (dev_priv->connectors[i]) { + connector = dev_priv->connectors[i]; + dev_priv->connectors[i] = NULL; + if (connector->output) { + drm_output_destroy(connector->output); + connector->output = NULL; + } + if (connector->i2c) { + radeon_ms_i2c_destroy(connector->i2c); + connector->i2c = NULL; + } + drm_free(connector, + sizeof(struct radeon_ms_connector), + DRM_MEM_DRIVER); + } + } +} + +int radeon_ms_connectors_from_properties(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_ms_connector *connector = NULL; + struct drm_output *output = NULL; + int i = 0; + + radeon_ms_connectors_destroy(dev); + for (i = 0; i < RADEON_MAX_CONNECTORS; i++) { + if (dev_priv->properties->connectors[i]) { + connector = + drm_alloc(sizeof(struct radeon_ms_connector), + DRM_MEM_DRIVER); + if (connector == NULL) { + radeon_ms_connectors_destroy(dev); + return -ENOMEM; + } + memcpy(connector, + dev_priv->properties->connectors[i], + sizeof(struct radeon_ms_connector)); + connector->i2c = radeon_ms_i2c_create(dev, + connector->i2c_reg, connector->name); + if (connector->i2c == NULL) { + radeon_ms_connectors_destroy(dev); + return -ENOMEM; + } + output = drm_output_create(dev, + &radeon_ms_output_funcs, + connector->name); + if (output == NULL) { + radeon_ms_connectors_destroy(dev); + return -EINVAL; + } + connector->output = output; + output->driver_private = connector; + output->possible_crtcs = 0x3; + dev_priv->connectors[i] = connector; + } + } + return 0; +} + +void radeon_ms_outputs_destroy(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + int i = 0; + + for (i = 0; i < RADEON_MAX_OUTPUTS; i++) { + if (dev_priv->outputs[i]) { + drm_free(dev_priv->outputs[i], + sizeof(struct radeon_ms_output), + DRM_MEM_DRIVER); + dev_priv->outputs[i] = NULL; + } + } +} + +int radeon_ms_outputs_from_properties(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + int i = 0; + + radeon_ms_outputs_destroy(dev); + for (i = 0; i < RADEON_MAX_OUTPUTS; i++) { + if (dev_priv->properties->outputs[i]) { + dev_priv->outputs[i] = + drm_alloc(sizeof(struct radeon_ms_output), + DRM_MEM_DRIVER); + if (dev_priv->outputs[i] == NULL) { + radeon_ms_outputs_destroy(dev); + return -ENOMEM; + } + memcpy(dev_priv->outputs[i], + dev_priv->properties->outputs[i], + sizeof(struct radeon_ms_output)); + dev_priv->outputs[i]->dev = dev; + dev_priv->outputs[i]->initialize(dev_priv->outputs[i]); + } + } + return 0; +} + +void radeon_ms_outputs_restore(struct drm_device *dev, + struct radeon_state *state) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + int i; + + for (i = 0; i < RADEON_MAX_OUTPUTS; i++) { + if (dev_priv->outputs[i]) { + dev_priv->outputs[i]->restore(dev_priv->outputs[i], + state); + } + } +} + +void radeon_ms_outputs_save(struct drm_device *dev, struct radeon_state *state) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + int i; + + for (i = 0; i < RADEON_MAX_OUTPUTS; i++) { + if (dev_priv->outputs[i]) { + dev_priv->outputs[i]->save(dev_priv->outputs[i], state); + } + } +} diff --git a/shared-core/radeon_ms_reg.h b/shared-core/radeon_ms_reg.h new file mode 100644 index 00000000..d450280c --- /dev/null +++ b/shared-core/radeon_ms_reg.h @@ -0,0 +1,1655 @@ +/* + * Copyright 2007 Advanced Micro Devices, 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. + */ +#ifndef __RADEON_REG_H__ +#define __RADEON_REG_H__ + +#define MC_FB_LOCATION 0x00000148 +#define MC_FB_LOCATION__MC_FB_START__MASK 0x0000FFFF +#define MC_FB_LOCATION__MC_FB_START__SHIFT 0 +#define MC_FB_LOCATION__MC_FB_TOP__MASK 0xFFFF0000 +#define MC_FB_LOCATION__MC_FB_TOP__SHIFT 16 +#define MC_AGP_LOCATION 0x0000014C +#define MC_AGP_LOCATION__MC_AGP_START__MASK 0x0000FFFF +#define MC_AGP_LOCATION__MC_AGP_START__SHIFT 0 +#define MC_AGP_LOCATION__MC_AGP_TOP__MASK 0xFFFF0000 +#define MC_AGP_LOCATION__MC_AGP_TOP__SHIFT 16 +#define AGP_COMMAND 0x00000F60 +#define AGP_COMMAND__DATA_RATE__MASK 0x00000007 +#define AGP_COMMAND__DATA_RATE__SHIFT 0 +#define DATA_RATE__v2_1X 0x1 +#define DATA_RATE__v2_2X 0x2 +#define DATA_RATE__v2_4X 0x4 +#define DATA_RATE__v3_4X 0x1 +#define DATA_RATE__v3_8X 0x2 +#define AGP_COMMAND__AGP_EN 0x00000100 +#define AGP_COMMAND__SBA_EN 0x00000200 +#define AGP_COMMAND__RQ_DEPTH__MASK 0xFF000000 +#define AGP_COMMAND__RQ_DEPTH__SHIFT 24 +#define AGP_COMMAND__FW_EN 0x00000010 +#define AGP_COMMAND__MODE_4G_EN 0x00000020 +#define AGP_COMMAND__PARQSZ__MASK 0x0000E000 +#define AGP_COMMAND__PARQSZ__SHIFT 13 +#define AGP_STATUS 0x00000F5C +#define AGP_STATUS__RATE1X 0x00000001 +#define AGP_STATUS__RATE2X 0x00000002 +#define AGP_STATUS__RATE4X 0x00000004 +#define AGP_STATUS__SBA 0x00000200 +#define AGP_STATUS__RQ__MASK 0xFF000000 +#define AGP_STATUS__RQ__SHIFT 24 +#define AGP_STATUS__FW 0x00000010 +#define AGP_STATUS__MODE_4G 0x00000020 +#define AGP_STATUS__RATE1X_4X 0x00000001 +#define AGP_STATUS__RATE2X_8X 0x00000002 +#define AGP_STATUS__MODE_AGP30 0x00000008 +#define AGP_STATUS__CAL_CYCLE__MASK 0x00001C00 +#define AGP_STATUS__CAL_CYCLE__SHIFT 10 +#define AGP_STATUS__ISOCH_SUPPORT 0x00020000 +#define AGP_BASE 0x00000170 +#define AGP_BASE__AGP_BASE_ADDR__MASK 0xFFFFFFFF +#define AGP_BASE__AGP_BASE_ADDR__SHIFT 0 +#define AGP_BASE_2 0x0000015C +#define AGP_BASE_2__AGP_BASE_ADDR_2__MASK 0x0000000F +#define AGP_BASE_2__AGP_BASE_ADDR_2__SHIFT 0 +#define CONFIG_MEMSIZE 0x000000F8 +#define CONFIG_MEMSIZE__CONFIG_MEMSIZE__MASK 0x1F000000 +#define CONFIG_MEMSIZE__CONFIG_MEMSIZE__SHIFT 24 +#define CONFIG_MEMSIZE__CONFIG_MEMSIZE_R2__MASK 0x1FF00000 +#define CONFIG_MEMSIZE__CONFIG_MEMSIZE_R2__SHIFT 20 +#define CONFIG_APER_0_BASE 0x00000100 +#define CONFIG_APER_0_BASE__APER_0_BASE__MASK 0xFE000000 +#define CONFIG_APER_0_BASE__APER_0_BASE__SHIFT 25 +#define CONFIG_APER_1_BASE 0x00000104 +#define CONFIG_APER_1_BASE__APER_1_BASE__MASK 0xFF000000 +#define CONFIG_APER_1_BASE__APER_1_BASE__SHIFT 24 +#define CONFIG_APER_SIZE 0x00000108 +#define CONFIG_APER_SIZE__APER_SIZE__MASK 0x0F000000 +#define CONFIG_APER_SIZE__APER_SIZE__SHIFT 24 +#define GEN_INT_CNTL 0x00000040 +#define GEN_INT_CNTL__CRTC_VBLANK 0x00000001 +#define GEN_INT_CNTL__CRTC_VLINE 0x00000002 +#define GEN_INT_CNTL__CRTC_VSYNC 0x00000004 +#define GEN_INT_CNTL__SNAPSHOT 0x00000008 +#define GEN_INT_CNTL__FP_DETECT 0x00000010 +#define GEN_INT_CNTL__CRTC2_VLINE 0x00000020 +#define GEN_INT_CNTL__DMA_VIPH0_INT_EN 0x00001000 +#define GEN_INT_CNTL__CRTC2_VSYNC 0x00000040 +#define GEN_INT_CNTL__SNAPSHOT2 0x00000080 +#define GEN_INT_CNTL__CRTC2_VBLANK 0x00000200 +#define GEN_INT_CNTL__FP2_DETECT 0x00000400 +#define GEN_INT_CNTL__VSYNC_DIFF_OVER_LIMIT 0x00000800 +#define GEN_INT_CNTL__DMA_VIPH1_INT_EN 0x00002000 +#define GEN_INT_CNTL__DMA_VIPH2_INT_EN 0x00004000 +#define GEN_INT_CNTL__DMA_VIPH3_INT_EN 0x00008000 +#define GEN_INT_CNTL__I2C_INT_EN 0x00020000 +#define GEN_INT_CNTL__GUI_IDLE 0x00080000 +#define GEN_INT_CNTL__VIPH_INT_EN 0x01000000 +#define GEN_INT_CNTL__SW_INT_EN 0x02000000 +#define GEN_INT_CNTL__GEYSERVILLE 0x08000000 +#define GEN_INT_CNTL__DVI_I2C_INT 0x20000000 +#define GEN_INT_CNTL__GUIDMA 0x40000000 +#define GEN_INT_CNTL__VIDDMA 0x80000000 +#define GEN_INT_CNTL__TIMER_INT 0x00010000 +#define GEN_INT_CNTL__IDCT_INT_EN 0x08000000 +#define GEN_INT_STATUS 0x00000044 +#define GEN_INT_STATUS__CRTC_VBLANK_STAT 0x00000001 +#define GEN_INT_STATUS__CRTC_VBLANK_STAT_AK 0x00000001 +#define GEN_INT_STATUS__CRTC_VLINE_STAT 0x00000002 +#define GEN_INT_STATUS__CRTC_VLINE_STAT_AK 0x00000002 +#define GEN_INT_STATUS__CRTC_VSYNC_STAT 0x00000004 +#define GEN_INT_STATUS__CRTC_VSYNC_STAT_AK 0x00000004 +#define GEN_INT_STATUS__SNAPSHOT_STAT 0x00000008 +#define GEN_INT_STATUS__SNAPSHOT_STAT_AK 0x00000008 +#define GEN_INT_STATUS__FP_DETECT_STAT 0x00000010 +#define GEN_INT_STATUS__FP_DETECT_STAT_AK 0x00000010 +#define GEN_INT_STATUS__CRTC2_VLINE_STAT 0x00000020 +#define GEN_INT_STATUS__CRTC2_VLINE_STAT_AK 0x00000020 +#define GEN_INT_STATUS__CRTC2_VSYNC_STAT 0x00000040 +#define GEN_INT_STATUS__CRTC2_VSYNC_STAT_AK 0x00000040 +#define GEN_INT_STATUS__SNAPSHOT2_STAT 0x00000080 +#define GEN_INT_STATUS__SNAPSHOT2_STAT_AK 0x00000080 +#define GEN_INT_STATUS__CAP0_INT_ACTIVE 0x00000100 +#define GEN_INT_STATUS__CRTC2_VBLANK_STAT 0x00000200 +#define GEN_INT_STATUS__CRTC2_VBLANK_STAT_AK 0x00000200 +#define GEN_INT_STATUS__FP2_DETECT_STAT 0x00000400 +#define GEN_INT_STATUS__FP2_DETECT_STAT_AK 0x00000400 +#define GEN_INT_STATUS__VSYNC_DIFF_OVER_LIMIT_STAT 0x00000800 +#define GEN_INT_STATUS__VSYNC_DIFF_OVER_LIMIT_STAT_AK 0x00000800 +#define GEN_INT_STATUS__DMA_VIPH0_INT 0x00001000 +#define GEN_INT_STATUS__DMA_VIPH0_INT_AK 0x00001000 +#define GEN_INT_STATUS__DMA_VIPH1_INT 0x00002000 +#define GEN_INT_STATUS__DMA_VIPH1_INT_AK 0x00002000 +#define GEN_INT_STATUS__DMA_VIPH2_INT 0x00004000 +#define GEN_INT_STATUS__DMA_VIPH2_INT_AK 0x00004000 +#define GEN_INT_STATUS__DMA_VIPH3_INT 0x00008000 +#define GEN_INT_STATUS__DMA_VIPH3_INT_AK 0x00008000 +#define GEN_INT_STATUS__I2C_INT 0x00020000 +#define GEN_INT_STATUS__I2C_INT_AK 0x00020000 +#define GEN_INT_STATUS__GUI_IDLE_STAT 0x00080000 +#define GEN_INT_STATUS__GUI_IDLE_STAT_AK 0x00080000 +#define GEN_INT_STATUS__VIPH_INT 0x01000000 +#define GEN_INT_STATUS__SW_INT 0x02000000 +#define GEN_INT_STATUS__SW_INT_AK 0x02000000 +#define GEN_INT_STATUS__SW_INT_SET 0x04000000 +#define GEN_INT_STATUS__GEYSERVILLE_STAT 0x08000000 +#define GEN_INT_STATUS__GEYSERVILLE_STAT_AK 0x08000000 +#define GEN_INT_STATUS__DVI_I2C_INT_STAT 0x20000000 +#define GEN_INT_STATUS__DVI_I2C_INT_AK 0x20000000 +#define GEN_INT_STATUS__GUIDMA_STAT 0x40000000 +#define GEN_INT_STATUS__GUIDMA_AK 0x40000000 +#define GEN_INT_STATUS__VIDDMA_STAT 0x80000000 +#define GEN_INT_STATUS__VIDDMA_AK 0x80000000 +#define GEN_INT_STATUS__TIMER_INT_STAT 0x00010000 +#define GEN_INT_STATUS__TIMER_INT_STAT_AK 0x00010000 +#define GEN_INT_STATUS__IDCT_INT_STAT 0x08000000 +#define GEN_INT_STATUS__IDCT_INT_STAT_AK 0x08000000 +#define RB2D_DSTCACHE_MODE 0x00003428 +#define RB2D_DSTCACHE_MODE__DC_BYPASS__MASK 0x00000003 +#define RB2D_DSTCACHE_MODE__DC_BYPASS__SHIFT 0 +#define RB2D_DSTCACHE_MODE__DC_LINE_SIZE__MASK 0x0000000C +#define RB2D_DSTCACHE_MODE__DC_LINE_SIZE__SHIFT 2 +#define RB2D_DSTCACHE_MODE__DC_AUTOFLUSH_ENABLE__MASK 0x00000300 +#define RB2D_DSTCACHE_MODE__DC_AUTOFLUSH_ENABLE__SHIFT 8 +#define RB2D_DSTCACHE_MODE__DC_FORCE_RMW 0x00010000 +#define RB2D_DSTCACHE_MODE__DC_DISABLE_RI_FILL 0x01000000 +#define RB2D_DSTCACHE_MODE__DC_DISABLE_RI_READ 0x02000000 +#define RB2D_DSTCACHE_MODE__DC_AUTOFREE_ENABLE__MASK 0x00000C00 +#define RB2D_DSTCACHE_MODE__DC_AUTOFREE_ENABLE__SHIFT 10 +#define RB2D_DSTCACHE_MODE__DC_DISABLE 0x04000000 +#define RB2D_DSTCACHE_MODE__DC_DISABLE_IGNORE_PE 0x00020000 +#define RB2D_DSTCACHE_CTLSTAT 0x0000342C +#define RB2D_DSTCACHE_CTLSTAT__DC_FLUSH__MASK 0x00000003 +#define RB2D_DSTCACHE_CTLSTAT__DC_FLUSH__SHIFT 0 +#define RB2D_DSTCACHE_CTLSTAT__DC_FREE__MASK 0x0000000C +#define RB2D_DSTCACHE_CTLSTAT__DC_FREE__SHIFT 2 +#define RB2D_DSTCACHE_CTLSTAT__DC_BUSY 0x80000000 +#define RB3D_DSTCACHE_CTLSTAT 0x0000325C +#define RB3D_DSTCACHE_CTLSTAT__DC_FLUSH__MASK 0x00000003 +#define RB3D_DSTCACHE_CTLSTAT__DC_FLUSH__SHIFT 0 +#define RB3D_DSTCACHE_CTLSTAT__DC_FREE__MASK 0x0000000C +#define RB3D_DSTCACHE_CTLSTAT__DC_FREE__SHIFT 2 +#define RB3D_DSTCACHE_CTLSTAT__DC_BUSY 0x80000000 +#define RB3D_DSTCACHE_CTLSTAT_R3 0x00004E4C +#define RB3D_DSTCACHE_CTLSTAT_R3__DC_FLUSH__MASK 0x00000003 +#define RB3D_DSTCACHE_CTLSTAT_R3__DC_FLUSH__SHIFT 0 +#define RB3D_DSTCACHE_CTLSTAT_R3__DC_FREE__MASK 0x0000000C +#define RB3D_DSTCACHE_CTLSTAT_R3__DC_FREE__SHIFT 2 +#define RB3D_DSTCACHE_CTLSTAT_R3__DC_FINISH 0x00000010 +#define RB3D_ZCACHE_CTLSTAT 0x00003254 +#define RB3D_ZCACHE_CTLSTAT__ZC_FLUSH 0x00000001 +#define RB3D_ZCACHE_CTLSTAT__ZC_FREE 0x00000004 +#define RB3D_ZCACHE_CTLSTAT__ZC_DIRTY 0x40000000 +#define RB3D_ZCACHE_CTLSTAT__ZC_BUSY 0x80000000 +#define RB3D_ZCACHE_CTLSTAT_R3 0x00004F18 +#define RB3D_ZCACHE_CTLSTAT_R3__ZC_FLUSH 0x00000001 +#define RB3D_ZCACHE_CTLSTAT_R3__ZC_FREE 0x00000002 +#define RB3D_ZCACHE_CTLSTAT_R3__ZC_BUSY 0x80000000 +#define SCRATCH_REG0 0x000015E0 +#define SCRATCH_REG0__SCRATCH_REG0__MASK 0xFFFFFFFF +#define SCRATCH_REG0__SCRATCH_REG0__SHIFT 0 +#define SCRATCH_REG1 0x000015E4 +#define SCRATCH_REG1__SCRATCH_REG1__MASK 0xFFFFFFFF +#define SCRATCH_REG1__SCRATCH_REG1__SHIFT 0 +#define SCRATCH_REG2 0x000015E8 +#define SCRATCH_REG2__SCRATCH_REG2__MASK 0xFFFFFFFF +#define SCRATCH_REG2__SCRATCH_REG2__SHIFT 0 +#define SCRATCH_REG3 0x000015EC +#define SCRATCH_REG3__SCRATCH_REG3__MASK 0xFFFFFFFF +#define SCRATCH_REG3__SCRATCH_REG3__SHIFT 0 +#define SCRATCH_REG4 0x000015F0 +#define SCRATCH_REG4__SCRATCH_REG4__MASK 0xFFFFFFFF +#define SCRATCH_REG4__SCRATCH_REG4__SHIFT 0 +#define SCRATCH_REG5 0x000015F4 +#define SCRATCH_REG5__SCRATCH_REG5__MASK 0xFFFFFFFF +#define SCRATCH_REG5__SCRATCH_REG5__SHIFT 0 +#define SCRATCH_REG6 0x000015F8 +#define SCRATCH_REG6__SCRATCH_REG6__MASK 0xFFFFFFFF +#define SCRATCH_REG6__SCRATCH_REG6__SHIFT 0 +#define SCRATCH_REG7 0x000015FC +#define SCRATCH_REG7__SCRATCH_REG7__MASK 0xFFFFFFFF +#define SCRATCH_REG7__SCRATCH_REG7__SHIFT 0 +#define PCIE_INDEX 0x00000030 +#define PCIE_INDEX__PCIE_INDEX__MASK 0x000007FF +#define PCIE_INDEX__PCIE_INDEX__SHIFT 0 +#define PCIE_DATA 0x00000034 +#define PCIE_DATA__PCIE_DATA__MASK 0xFFFFFFFF +#define PCIE_DATA__PCIE_DATA__SHIFT 0 +#define PCIE_TX_GART_CNTL 0x00000010 +#define PCIE_TX_GART_CNTL__GART_EN 0x00000001 +#define PCIE_TX_GART_CNTL__GART_UNMAPPED_ACCESS__MASK 0x00000006 +#define PCIE_TX_GART_CNTL__GART_UNMAPPED_ACCESS__SHIFT 1 +#define GART_UNMAPPED_ACCESS__PTHRU 0x0 +#define GART_UNMAPPED_ACCESS__CLAMP 0x1 +#define GART_UNMAPPED_ACCESS__DISCARD 0x3 +#define PCIE_TX_GART_CNTL__GART_MODE__MASK 0x00000018 +#define PCIE_TX_GART_CNTL__GART_MODE__SHIFT 3 +#define GART_MODE__CACHE_32x128 0x0 +#define GART_MODE__CACHE_8x4x128 0x1 +#define PCIE_TX_GART_CNTL__GART_CHK_RW_VALID_EN 0x00000020 +#define PCIE_TX_GART_CNTL__GART_RDREQPATH_SEL__MASK 0x00000040 +#define PCIE_TX_GART_CNTL__GART_RDREQPATH_SEL__SHIFT 6 +#define GART_RDREQPATH_SEL__HDP 0x0 +#define GART_RDREQPATH_SEL__DRQMC 0x1 +#define PCIE_TX_GART_CNTL__GART_INVALIDATE_TLB 0x00000100 +#define PCIE_TX_GART_DISCARD_RD_ADDR_LO 0x00000011 +#define PCIE_TX_GART_DISCARD_RD_ADDR_LO__GART_DISCARD_RD_ADDR_LO__MASK 0xFFFFFFFF +#define PCIE_TX_GART_DISCARD_RD_ADDR_LO__GART_DISCARD_RD_ADDR_LO__SHIFT 0 +#define PCIE_TX_GART_DISCARD_RD_ADDR_HI 0x00000012 +#define PCIE_TX_GART_DISCARD_RD_ADDR_HI__GART_DISCARD_RD_ADDR_HI__MASK 0x000000FF +#define PCIE_TX_GART_DISCARD_RD_ADDR_HI__GART_DISCARD_RD_ADDR_HI__SHIFT 0 +#define PCIE_TX_GART_BASE 0x00000013 +#define PCIE_TX_GART_BASE__GART_BASE__MASK 0xFFFFFFFF +#define PCIE_TX_GART_BASE__GART_BASE__SHIFT 0 +#define PCIE_TX_GART_START_LO 0x00000014 +#define PCIE_TX_GART_START_LO__GART_START_LO__MASK 0xFFFFFFFF +#define PCIE_TX_GART_START_LO__GART_START_LO__SHIFT 0 +#define PCIE_TX_GART_START_HI 0x00000015 +#define PCIE_TX_GART_START_HI__GART_START_HI__MASK 0x000000FF +#define PCIE_TX_GART_START_HI__GART_START_HI__SHIFT 0 +#define PCIE_TX_GART_END_LO 0x00000016 +#define PCIE_TX_GART_END_LO__GART_END_LO__MASK 0xFFFFFFFF +#define PCIE_TX_GART_END_LO__GART_END_LO__SHIFT 0 +#define PCIE_TX_GART_END_HI 0x00000017 +#define PCIE_TX_GART_END_HI__GART_END_HI__MASK 0x000000FF +#define PCIE_TX_GART_END_HI__GART_END_HI__SHIFT 0 +#define PCIE_TX_GART_ERROR 0x00000018 +#define PCIE_TX_GART_ERROR__GART_UNMAPPED 0x00000002 +#define PCIE_TX_GART_ERROR__GART_INVALID_READ 0x00000004 +#define PCIE_TX_GART_ERROR__GART_INVALID_WRITE 0x00000008 +#define PCIE_TX_GART_ERROR__GART_INVALID_ADDR__MASK 0xFFFFFFF0 +#define PCIE_TX_GART_ERROR__GART_INVALID_ADDR__SHIFT 4 +#define CP_RB_CNTL 0x00000704 +#define CP_RB_CNTL__RB_BUFSZ__MASK 0x0000003F +#define CP_RB_CNTL__RB_BUFSZ__SHIFT 0 +#define CP_RB_CNTL__RB_BLKSZ__MASK 0x00003F00 +#define CP_RB_CNTL__RB_BLKSZ__SHIFT 8 +#define CP_RB_CNTL__BUF_SWAP__MASK 0x00030000 +#define CP_RB_CNTL__BUF_SWAP__SHIFT 16 +#define CP_RB_CNTL__MAX_FETCH__MASK 0x000C0000 +#define CP_RB_CNTL__MAX_FETCH__SHIFT 18 +#define CP_RB_CNTL__RB_NO_UPDATE 0x08000000 +#define CP_RB_CNTL__RB_RPTR_WR_ENA 0x80000000 +#define CP_RB_BASE 0x00000700 +#define CP_RB_BASE__RB_BASE__MASK 0xFFFFFFFC +#define CP_RB_BASE__RB_BASE__SHIFT 2 +#define CP_RB_RPTR_ADDR 0x0000070C +#define CP_RB_RPTR_ADDR__RB_RPTR_SWAP__MASK 0x00000003 +#define CP_RB_RPTR_ADDR__RB_RPTR_SWAP__SHIFT 0 +#define CP_RB_RPTR_ADDR__RB_RPTR_ADDR__MASK 0xFFFFFFFC +#define CP_RB_RPTR_ADDR__RB_RPTR_ADDR__SHIFT 2 +#define CP_RB_RPTR 0x00000710 +#define CP_RB_RPTR__RB_RPTR__MASK 0x007FFFFF +#define CP_RB_RPTR__RB_RPTR__SHIFT 0 +#define CP_RB_RPTR_WR 0x0000071C +#define CP_RB_RPTR_WR__RB_RPTR_WR__MASK 0x007FFFFF +#define CP_RB_RPTR_WR__RB_RPTR_WR__SHIFT 0 +#define CP_RB_WPTR 0x00000714 +#define CP_RB_WPTR__RB_WPTR__MASK 0x007FFFFF +#define CP_RB_WPTR__RB_WPTR__SHIFT 0 +#define CP_RB_WPTR_DELAY 0x00000718 +#define CP_RB_WPTR_DELAY__PRE_WRITE_TIMER__MASK 0x0FFFFFFF +#define CP_RB_WPTR_DELAY__PRE_WRITE_TIMER__SHIFT 0 +#define CP_RB_WPTR_DELAY__PRE_WRITE_LIMIT__MASK 0xF0000000 +#define CP_RB_WPTR_DELAY__PRE_WRITE_LIMIT__SHIFT 28 +#define SCRATCH_UMSK 0x00000770 +#define SCRATCH_UMSK__SCRATCH_UMSK__MASK 0x0000003F +#define SCRATCH_UMSK__SCRATCH_UMSK__SHIFT 0 +#define SCRATCH_UMSK__SCRATCH_SWAP__MASK 0x00030000 +#define SCRATCH_UMSK__SCRATCH_SWAP__SHIFT 16 +#define SCRATCH_UMSK__SCRATCH_UMSK_R2__MASK 0x000000FF +#define SCRATCH_UMSK__SCRATCH_UMSK_R2__SHIFT 0 +#define SCRATCH_ADDR 0x00000774 +#define SCRATCH_ADDR__SCRATCH_ADDR__MASK 0xFFFFFFE0 +#define SCRATCH_ADDR__SCRATCH_ADDR__SHIFT 5 +#define CP_ME_RAM_ADDR 0x000007D4 +#define CP_ME_RAM_ADDR__ME_RAM_ADDR__MASK 0x000000FF +#define CP_ME_RAM_ADDR__ME_RAM_ADDR__SHIFT 0 +#define CP_ME_RAM_DATAH 0x000007DC +#define CP_ME_RAM_DATAH__ME_RAM_DATAH__MASK 0x0000003F +#define CP_ME_RAM_DATAH__ME_RAM_DATAH__SHIFT 0 +#define CP_ME_RAM_DATAH__ME_RAM_DATAH_R3__MASK 0x000000FF +#define CP_ME_RAM_DATAH__ME_RAM_DATAH_R3__SHIFT 0 +#define CP_ME_RAM_DATAL 0x000007E0 +#define CP_ME_RAM_DATAL__ME_RAM_DATAL__MASK 0xFFFFFFFF +#define CP_ME_RAM_DATAL__ME_RAM_DATAL__SHIFT 0 +#define CP_CSQ_CNTL 0x00000740 +#define CP_CSQ_CNTL__CSQ_CNT_PRIMARY__MASK 0x000000FF +#define CP_CSQ_CNTL__CSQ_CNT_PRIMARY__SHIFT 0 +#define CP_CSQ_CNTL__CSQ_CNT_INDIRECT__MASK 0x0000FF00 +#define CP_CSQ_CNTL__CSQ_CNT_INDIRECT__SHIFT 8 +#define CP_CSQ_CNTL__CSQ_MODE__MASK 0xF0000000 +#define CP_CSQ_CNTL__CSQ_MODE__SHIFT 28 +#define CSQ_MODE__CSQ_PRIDIS_INDDIS 0x0 +#define CSQ_MODE__CSQ_PRIPIO_INDDIS 0x1 +#define CSQ_MODE__CSQ_PRIBM_INDDIS 0x2 +#define CSQ_MODE__CSQ_PRIPIO_INDBM 0x3 +#define CSQ_MODE__CSQ_PRIBM_INDBM 0x4 +#define CSQ_MODE__CSQ_PRIPIO_INDPIO 0xF +#define CP_CSQ_CNTL__CSQ_CNT_PRIMARY_R2__MASK 0x000001FF +#define CP_CSQ_CNTL__CSQ_CNT_PRIMARY_R2__SHIFT 0 +#define CP_CSQ_CNTL__CSQ_CNT_INDIRECT_R2__MASK 0x0003FE00 +#define CP_CSQ_CNTL__CSQ_CNT_INDIRECT_R2__SHIFT 9 +#define CP_CSQ_CNTL__CSQ_CNT_INDIRECT2__MASK 0x07FC0000 +#define CP_CSQ_CNTL__CSQ_CNT_INDIRECT2__SHIFT 18 +#define CRTC_GEN_CNTL 0x00000050 +#define CRTC_GEN_CNTL__CRTC_DBL_SCAN_EN 0x00000001 +#define CRTC_GEN_CNTL__CRTC_INTERLACE_EN 0x00000002 +#define CRTC_GEN_CNTL__CRTC_C_SYNC_EN 0x00000010 +#define CRTC_GEN_CNTL__CRTC_PIX_WIDTH__MASK 0x00000F00 +#define CRTC_GEN_CNTL__CRTC_PIX_WIDTH__SHIFT 8 +#define CRTC_PIX_WIDTH__4BPP 0x100 +#define CRTC_PIX_WIDTH__8BPP 0x200 +#define CRTC_PIX_WIDTH__15BPP 0x300 +#define CRTC_PIX_WIDTH__16BPP 0x400 +#define CRTC_PIX_WIDTH__24BPP 0x500 +#define CRTC_PIX_WIDTH__34BPP 0x600 +#define CRTC_PIX_WIDTH__16BPP_4444 0x700 +#define CRTC_PIX_WIDTH__16BPP_88 0x800 +#define CRTC_GEN_CNTL__CRTC_ICON_EN 0x00008000 +#define CRTC_GEN_CNTL__CRTC_CUR_EN 0x00010000 +#define CRTC_GEN_CNTL__CRTC_VSTAT_MODE__MASK 0x00060000 +#define CRTC_GEN_CNTL__CRTC_VSTAT_MODE__SHIFT 17 +#define CRTC_GEN_CNTL__CRTC_CUR_MODE__MASK 0x00700000 +#define CRTC_GEN_CNTL__CRTC_CUR_MODE__SHIFT 20 +#define CRTC_CUR_MODE__PREMULTI_ALPHA 0x2 +#define CRTC_CUR_MODE__COLOR24BPP 0x1 +#define CRTC_GEN_CNTL__CRTC_EXT_DISP_EN 0x01000000 +#define CRTC_GEN_CNTL__CRTC_EN 0x02000000 +#define CRTC_GEN_CNTL__CRTC_DISP_REQ_EN_B 0x04000000 +#define CRTC_GEN_CNTL__CRTC_MODE9_COLOR_ORDER 0x00001000 +#define CRTC_EXT_CNTL 0x00000054 +#define CRTC_EXT_CNTL__CRTC_VGA_XOVERSCAN 0x00000001 +#define CRTC_EXT_CNTL__VGA_BLINK_RATE__MASK 0x00000006 +#define CRTC_EXT_CNTL__VGA_BLINK_RATE__SHIFT 1 +#define CRTC_EXT_CNTL__VGA_ATI_LINEAR 0x00000008 +#define CRTC_EXT_CNTL__VGA_128KAP_PAGING 0x00000010 +#define CRTC_EXT_CNTL__VGA_TEXT_132 0x00000020 +#define CRTC_EXT_CNTL__VGA_XCRT_CNT_EN 0x00000040 +#define CRTC_EXT_CNTL__CRTC_HSYNC_DIS 0x00000100 +#define CRTC_EXT_CNTL__CRTC_VSYNC_DIS 0x00000200 +#define CRTC_EXT_CNTL__CRTC_DISPLAY_DIS 0x00000400 +#define CRTC_EXT_CNTL__CRTC_SYNC_TRISTATE 0x00000800 +#define CRTC_EXT_CNTL__CRTC_HSYNC_TRISTATE 0x00001000 +#define CRTC_EXT_CNTL__CRTC_VSYNC_TRISTATE 0x00002000 +#define CRTC_EXT_CNTL__CRT_ON 0x00008000 +#define CRTC_EXT_CNTL__VGA_CUR_B_TEST 0x00020000 +#define CRTC_EXT_CNTL__VGA_PACK_DIS 0x00040000 +#define CRTC_EXT_CNTL__VGA_MEM_PS_EN 0x00080000 +#define CRTC_EXT_CNTL__VCRTC_IDX_MASTER__MASK 0x7F000000 +#define CRTC_EXT_CNTL__VCRTC_IDX_MASTER__SHIFT 24 +#define CRTC_H_TOTAL_DISP 0x00000200 +#define CRTC_H_TOTAL_DISP__CRTC_H_TOTAL__MASK 0x000003FF +#define CRTC_H_TOTAL_DISP__CRTC_H_TOTAL__SHIFT 0 +#define CRTC_H_TOTAL_DISP__CRTC_H_DISP__MASK 0x01FF0000 +#define CRTC_H_TOTAL_DISP__CRTC_H_DISP__SHIFT 16 +#define CRTC_H_SYNC_STRT_WID 0x00000204 +#define CRTC_H_SYNC_STRT_WID__CRTC_H_SYNC_STRT_PIX__MASK 0x00000007 +#define CRTC_H_SYNC_STRT_WID__CRTC_H_SYNC_STRT_PIX__SHIFT 0 +#define CRTC_H_SYNC_STRT_WID__CRTC_H_SYNC_STRT_CHAR__MASK 0x00001FF8 +#define CRTC_H_SYNC_STRT_WID__CRTC_H_SYNC_STRT_CHAR__SHIFT 3 +#define CRTC_H_SYNC_STRT_WID__CRTC_H_SYNC_WID__MASK 0x003F0000 +#define CRTC_H_SYNC_STRT_WID__CRTC_H_SYNC_WID__SHIFT 16 +#define CRTC_H_SYNC_STRT_WID__CRTC_H_SYNC_POL 0x00800000 +#define CRTC_H_SYNC_STRT_WID__CRTC_H_SYNC_SKEW_TUNE__MASK 0x07000000 +#define CRTC_H_SYNC_STRT_WID__CRTC_H_SYNC_SKEW_TUNE__SHIFT 24 +#define CRTC_H_SYNC_STRT_WID__CRTC_H_SYNC_SKEW_TUNE_MODE 0x10000000 +#define CRTC_V_TOTAL_DISP 0x00000208 +#define CRTC_V_TOTAL_DISP__CRTC_V_TOTAL__MASK 0x00000FFF +#define CRTC_V_TOTAL_DISP__CRTC_V_TOTAL__SHIFT 0 +#define CRTC_V_TOTAL_DISP__CRTC_V_DISP__MASK 0x0FFF0000 +#define CRTC_V_TOTAL_DISP__CRTC_V_DISP__SHIFT 16 +#define CRTC_V_SYNC_STRT_WID 0x0000020C +#define CRTC_V_SYNC_STRT_WID__CRTC_V_SYNC_STRT__MASK 0x00000FFF +#define CRTC_V_SYNC_STRT_WID__CRTC_V_SYNC_STRT__SHIFT 0 +#define CRTC_V_SYNC_STRT_WID__CRTC_V_SYNC_WID__MASK 0x001F0000 +#define CRTC_V_SYNC_STRT_WID__CRTC_V_SYNC_WID__SHIFT 16 +#define CRTC_V_SYNC_STRT_WID__CRTC_V_SYNC_POL 0x00800000 +#define CRTC_OFFSET 0x00000224 +#define CRTC_OFFSET__CRTC_OFFSET__MASK 0x07FFFFFF +#define CRTC_OFFSET__CRTC_OFFSET__SHIFT 0 +#define CRTC_OFFSET__CRTC_GUI_TRIG_OFFSET 0x40000000 +#define CRTC_OFFSET__CRTC_OFFSET_LOCK 0x80000000 +#define CRTC_OFFSET__CRTC_OFFSET_R3__MASK 0x0FFFFFFF +#define CRTC_OFFSET__CRTC_OFFSET_R3__SHIFT 0 +#define CRTC_OFFSET_CNTL 0x00000228 +#define CRTC_OFFSET_CNTL__CRTC_TILE_LINE__MASK 0x0000000F +#define CRTC_OFFSET_CNTL__CRTC_TILE_LINE__SHIFT 0 +#define CRTC_OFFSET_CNTL__CRTC_TILE_LINE_RIGHT__MASK 0x000000F0 +#define CRTC_OFFSET_CNTL__CRTC_TILE_LINE_RIGHT__SHIFT 4 +#define CRTC_OFFSET_CNTL__CRTC_TILE_EN_RIGHT 0x00004000 +#define CRTC_OFFSET_CNTL__CRTC_TILE_EN 0x00008000 +#define CRTC_OFFSET_CNTL__CRTC_OFFSET_FLIP_CNTL 0x00010000 +#define CRTC_OFFSET_CNTL__CRTC_STEREO_OFFSET_EN 0x00020000 +#define CRTC_OFFSET_CNTL__CRTC_STEREO_SYNC_EN__MASK 0x000C0000 +#define CRTC_OFFSET_CNTL__CRTC_STEREO_SYNC_EN__SHIFT 18 +#define CRTC_OFFSET_CNTL__CRTC_STEREO_SYNC_OUT_EN 0x00100000 +#define CRTC_OFFSET_CNTL__CRTC_STEREO_SYNC 0x00200000 +#define CRTC_OFFSET_CNTL__CRTC_GUI_TRIG_OFFSET_LEFT_EN 0x10000000 +#define CRTC_OFFSET_CNTL__CRTC_GUI_TRIG_OFFSET_RIGHT_EN 0x20000000 +#define CRTC_OFFSET_CNTL__CRTC_GUI_TRIG_OFFSET 0x40000000 +#define CRTC_OFFSET_CNTL__CRTC_OFFSET_LOCK 0x80000000 +#define CRTC_OFFSET_CNTL__CRTC_X_Y_MODE_EN_RIGHT 0x00000040 +#define CRTC_OFFSET_CNTL__CRTC_MICRO_TILE_BUFFER_MODE_RIGHT__MASK 0x00000180 +#define CRTC_OFFSET_CNTL__CRTC_MICRO_TILE_BUFFER_MODE_RIGHT__SHIFT 7 +#define CRTC_OFFSET_CNTL__CRTC_X_Y_MODE_EN 0x00000200 +#define CRTC_OFFSET_CNTL__CRTC_MICRO_TILE_BUFFER_MODE__MASK 0x00000C00 +#define CRTC_OFFSET_CNTL__CRTC_MICRO_TILE_BUFFER_MODE__SHIFT 10 +#define CRTC_MICRO_TILE_BUFFER_MODE__AUTO 0x0 +#define CRTC_MICRO_TILE_BUFFER_MODE__SLINE 0x1 +#define CRTC_MICRO_TILE_BUFFER_MODE__DLINE 0x2 +#define CRTC_MICRO_TILE_BUFFER_MODE__DIS 0x3 +#define CRTC_OFFSET_CNTL__CRTC_MICRO_TILE_EN_RIGHT 0x00001000 +#define CRTC_OFFSET_CNTL__CRTC_MICRO_TILE_EN 0x00002000 +#define CRTC_OFFSET_CNTL__CRTC_MACRO_TILE_EN_RIGHT 0x00004000 +#define CRTC_OFFSET_CNTL__CRTC_MACRO_TILE_EN 0x00008000 +#define CRTC_PITCH 0x0000022C +#define CRTC_PITCH__CRTC_PITCH__MASK 0x000007FF +#define CRTC_PITCH__CRTC_PITCH__SHIFT 0 +#define CRTC_PITCH__CRTC_PITCH_RIGHT__MASK 0x07FF0000 +#define CRTC_PITCH__CRTC_PITCH_RIGHT__SHIFT 16 +#define CRTC_MORE_CNTL 0x0000027C +#define CRTC_MORE_CNTL__CRTC_HORZ_BLANK_MODE_SEL 0x00000001 +#define CRTC_MORE_CNTL__CRTC_VERT_BLANK_MODE_SEL 0x00000002 +#define CRTC_MORE_CNTL__CRTC_AUTO_HORZ_CENTER_EN 0x00000004 +#define CRTC_MORE_CNTL__CRTC_AUTO_VERT_CENTER_EN 0x00000008 +#define CRTC_MORE_CNTL__CRTC_H_CUTOFF_ACTIVE_EN 0x00000010 +#define CRTC_MORE_CNTL__CRTC_V_CUTOFF_ACTIVE_EN 0x00000020 +#define CRTC_MORE_CNTL__FORCE_H_EVEN_PIXEL_COUNT 0x00000040 +#define CRTC_MORE_CNTL__RMX_H_FILT_COEFFICIENT__MASK 0x07000000 +#define CRTC_MORE_CNTL__RMX_H_FILT_COEFFICIENT__SHIFT 24 +#define CRTC_MORE_CNTL__RMX_H_FILTER_EN 0x08000000 +#define CRTC_MORE_CNTL__RMX_V_FILT_COEFFICIENT__MASK 0x70000000 +#define CRTC_MORE_CNTL__RMX_V_FILT_COEFFICIENT__SHIFT 28 +#define CRTC_MORE_CNTL__RMX_V_FILTER_EN 0x80000000 +#define CRTC_MORE_CNTL__DSP_RST_HCOUNT 0x00000100 +#define CRTC_MORE_CNTL__DSP_RST_VCOUNT 0x00000200 +#define CRTC_MORE_CNTL__HCOUNT_RST_POS 0x00000400 +#define CRTC_MORE_CNTL__VCOUNT_RST_POS 0x00000800 +#define CRTC_MORE_CNTL__CRTC_FIX_VSYNC_EDGE_POSITION_EN 0x00001000 +#define CRTC_TILE_X0_Y0 0x00000350 +#define CRTC_TILE_X0_Y0__CRTC_TILE_X0__MASK 0x00000FFF +#define CRTC_TILE_X0_Y0__CRTC_TILE_X0__SHIFT 0 +#define CRTC_TILE_X0_Y0__CRTC_TILE_Y0__MASK 0x0FFF0000 +#define CRTC_TILE_X0_Y0__CRTC_TILE_Y0__SHIFT 16 +#define CRTC_TILE_X0_Y0__CRTC_GUI_TRIG_OFFSET 0x40000000 +#define CRTC_TILE_X0_Y0__CRTC_OFFSET_LOCK 0x80000000 +#define DAC_CNTL 0x00000058 +#define DAC_CNTL__DAC_RANGE_CNTL__MASK 0x00000003 +#define DAC_CNTL__DAC_RANGE_CNTL__SHIFT 0 +#define DAC_RANGE_CNTL__PS2 0x2 +#define DAC_RANGE_CNTL__YPbPr 0x3 +#define DAC_CNTL__DAC_BLANKING 0x00000004 +#define DAC_CNTL__DAC_CMP_EN 0x00000008 +#define DAC_CNTL__DAC_CMP_OUT_R 0x00000010 +#define DAC_CNTL__DAC_CMP_OUT_G 0x00000020 +#define DAC_CNTL__DAC_CMP_OUT_B 0x00000040 +#define DAC_CNTL__DAC_CMP_OUTPUT 0x00000080 +#define DAC_CNTL__DAC_8BIT_EN 0x00000100 +#define DAC_CNTL__DAC_4BPP_PIX_ORDER 0x00000200 +#define DAC_CNTL__DAC_TVO_EN 0x00000400 +#define DAC_CNTL__DAC_VGA_ADR_EN 0x00002000 +#define DAC_CNTL__DAC_EXPAND_MODE 0x00004000 +#define DAC_CNTL__DAC_PDWN 0x00008000 +#define DAC_CNTL__CRT_SENSE 0x00010000 +#define DAC_CNTL__CRT_DETECTION_ON 0x00020000 +#define DAC_CNTL__DAC_CRC_CONT_EN 0x00040000 +#define DAC_CNTL__DAC_CRC_EN 0x00080000 +#define DAC_CNTL__DAC_CRC_FIELD 0x00100000 +#define DAC_CNTL__DAC_LUT_COUNTER_LIMIT__MASK 0x00600000 +#define DAC_CNTL__DAC_LUT_COUNTER_LIMIT__SHIFT 21 +#define DAC_CNTL__DAC_LUT_READ_SEL 0x00800000 +#define DAC_CNTL__DAC__MASK 0xFF000000 +#define DAC_CNTL__DAC__SHIFT 24 +#define DAC_CNTL__DAC_CRC_BLANKb_ONLY 0x00000800 +#define DAC_CNTL2 0x0000007C +#define DAC_CNTL2__DAC_CLK_SEL 0x00000001 +#define DAC_CNTL2__DAC2_CLK_SEL 0x00000002 +#define DAC_CNTL2__PALETTE_ACCESS_CNTL 0x00000020 +#define DAC_CNTL2__DAC2_CMP_EN 0x00000080 +#define DAC_CNTL2__DAC2_CMP_OUT_R 0x00000100 +#define DAC_CNTL2__DAC2_CMP_OUT_G 0x00000200 +#define DAC_CNTL2__DAC2_CMP_OUT_B 0x00000400 +#define DAC_CNTL2__DAC2_CMP_OUTPUT 0x00000800 +#define DAC_CNTL2__DAC2_EXPAND_MODE 0x00004000 +#define DAC_CNTL2__CRT2_SENSE 0x00010000 +#define DAC_CNTL2__CRT2_DETECTION_ON 0x00020000 +#define DAC_CNTL2__DAC_CRC2_CONT_EN 0x00040000 +#define DAC_CNTL2__DAC_CRC2_EN 0x00080000 +#define DAC_CNTL2__DAC_CRC2_FIELD 0x00100000 +#define DAC_CNTL2__DAC2_LUT_COUNTER_LIMIT__MASK 0x00600000 +#define DAC_CNTL2__DAC2_LUT_COUNTER_LIMIT__SHIFT 21 +#define DAC_CNTL2__PALETTE_AUTOFILL_PRIMARY_W 0x00000800 +#define DAC_CNTL2__PALETTE_AUTOFILL_PRIMARY_R 0x00000800 +#define DAC_CNTL2__PALETTE_AUTOFILL_SECONDARY_W 0x00001000 +#define DAC_CNTL2__PALETTE_AUTOFILL_SECONDARY_R 0x00001000 +#define DAC_CNTL2__DAC2_CMP_EN_R3 0x00000040 +#define DAC_CNTL2__DAC2_CMP_OUT_R_R3 0x00000080 +#define DAC_CNTL2__DAC2_CMP_OUT_G_R3 0x00000100 +#define DAC_CNTL2__DAC2_CMP_OUT_B_R3 0x00000200 +#define DAC_CNTL2__DAC2_CMP_OUTPUT_R3 0x00000400 +#define DAC_CNTL2__DAC_CRC2_BLANKb_ONLY 0x00020000 +#define DAC_EXT_CNTL 0x00000280 +#define DAC_EXT_CNTL__DAC2_FORCE_BLANK_OFF_EN 0x00000001 +#define DAC_EXT_CNTL__DAC2_FORCE_DATA_EN 0x00000002 +#define DAC_EXT_CNTL__DAC_FORCE_BLANK_OFF_EN 0x00000010 +#define DAC_EXT_CNTL__DAC_FORCE_DATA_EN 0x00000020 +#define DAC_EXT_CNTL__DAC_FORCE_DATA_SEL__MASK 0x000000C0 +#define DAC_EXT_CNTL__DAC_FORCE_DATA_SEL__SHIFT 6 +#define DAC_EXT_CNTL__DAC_FORCE_DATA__MASK 0x0003FF00 +#define DAC_EXT_CNTL__DAC_FORCE_DATA__SHIFT 8 +#define DISP_MISC_CNTL 0x00000D00 +#define DISP_MISC_CNTL__SOFT_RESET_GRPH_PP 0x00000001 +#define DISP_MISC_CNTL__SOFT_RESET_SUBPIC_PP 0x00000002 +#define DISP_MISC_CNTL__SOFT_RESET_OV0_PP 0x00000004 +#define DISP_MISC_CNTL__SOFT_RESET_GRPH_SCLK 0x00000010 +#define DISP_MISC_CNTL__SOFT_RESET_SUBPIC_SCLK 0x00000020 +#define DISP_MISC_CNTL__SOFT_RESET_OV0_SCLK 0x00000040 +#define DISP_MISC_CNTL__SYNC_STRENGTH__MASK 0x00000300 +#define DISP_MISC_CNTL__SYNC_STRENGTH__SHIFT 8 +#define DISP_MISC_CNTL__SYNC_PAD_FLOP_EN 0x00000400 +#define DISP_MISC_CNTL__SOFT_RESET_GRPH2_PP 0x00001000 +#define DISP_MISC_CNTL__SOFT_RESET_GRPH2_SCLK 0x00008000 +#define DISP_MISC_CNTL__SOFT_RESET_LVDS 0x00010000 +#define DISP_MISC_CNTL__SOFT_RESET_TMDS 0x00020000 +#define DISP_MISC_CNTL__SOFT_RESET_DIG_TMDS 0x00040000 +#define DISP_MISC_CNTL__SOFT_RESET_TV 0x00080000 +#define DISP_MISC_CNTL__PALETTE2_MEM_RD_MARGIN__MASK 0x00F00000 +#define DISP_MISC_CNTL__PALETTE2_MEM_RD_MARGIN__SHIFT 20 +#define DISP_MISC_CNTL__PALETTE_MEM_RD_MARGIN__MASK 0x0F000000 +#define DISP_MISC_CNTL__PALETTE_MEM_RD_MARGIN__SHIFT 24 +#define DISP_MISC_CNTL__RMX_BUF_MEM_RD_MARGIN__MASK 0xF0000000 +#define DISP_MISC_CNTL__RMX_BUF_MEM_RD_MARGIN__SHIFT 28 +#define DISP_MISC_CNTL__SOFT_RESET_DVO 0x00040000 +#define DISP_MISC_CNTL__SOFT_RESET_TV_R2 0x00000800 +#define DAC_MACRO_CNTL 0x00000D04 +#define DAC_MACRO_CNTL__DAC_WHITE_CNTL__MASK 0x0000000F +#define DAC_MACRO_CNTL__DAC_WHITE_CNTL__SHIFT 0 +#define DAC_MACRO_CNTL__DAC_BG_ADJ__MASK 0x00000F00 +#define DAC_MACRO_CNTL__DAC_BG_ADJ__SHIFT 8 +#define DAC_MACRO_CNTL__DAC_PDWN_R 0x00010000 +#define DAC_MACRO_CNTL__DAC_PDWN_G 0x00020000 +#define DAC_MACRO_CNTL__DAC_PDWN_B 0x00040000 +#define DISP_PWR_MAN 0x00000D08 +#define DISP_PWR_MAN__DISP_PWR_MAN_D3_CRTC_EN 0x00000001 +#define DISP_PWR_MAN__DISP2_PWR_MAN_D3_CRTC2_EN 0x00000010 +#define DISP_PWR_MAN__DISP_PWR_MAN_DPMS__MASK 0x00000300 +#define DISP_PWR_MAN__DISP_PWR_MAN_DPMS__SHIFT 8 +#define DISP_PWR_MAN_DPMS__ON 0x0 +#define DISP_PWR_MAN_DPMS__STANDBY 0x1 +#define DISP_PWR_MAN_DPMS__SUSPEND 0x2 +#define DISP_PWR_MAN_DPMS__OFF 0x3 +#define DISP_PWR_MAN__DISP_D3_RST 0x00010000 +#define DISP_PWR_MAN__DISP_D3_REG_RST 0x00020000 +#define DISP_PWR_MAN__DISP_D3_GRPH_RST 0x00040000 +#define DISP_PWR_MAN__DISP_D3_SUBPIC_RST 0x00080000 +#define DISP_PWR_MAN__DISP_D3_OV0_RST 0x00100000 +#define DISP_PWR_MAN__DISP_D1D2_GRPH_RST 0x00200000 +#define DISP_PWR_MAN__DISP_D1D2_SUBPIC_RST 0x00400000 +#define DISP_PWR_MAN__DISP_D1D2_OV0_RST 0x00800000 +#define DISP_PWR_MAN__DIG_TMDS_ENABLE_RST 0x01000000 +#define DISP_PWR_MAN__TV_ENABLE_RST 0x02000000 +#define DISP_PWR_MAN__AUTO_PWRUP_EN 0x04000000 +#define DISP_PWR_MAN__DISP_DVO_ENABLE_RST 0x01000000 +#define DISP_MERGE_CNTL 0x00000D60 +#define DISP_MERGE_CNTL__DISP_ALPHA_MODE__MASK 0x00000003 +#define DISP_MERGE_CNTL__DISP_ALPHA_MODE__SHIFT 0 +#define DISP_MERGE_CNTL__DISP_ALPHA_INV 0x00000004 +#define DISP_MERGE_CNTL__DISP_ALPHA_PREMULT 0x00000008 +#define DISP_MERGE_CNTL__DISP_RGB_OFFSET_EN 0x00000100 +#define DISP_MERGE_CNTL__DISP_LIN_TRANS_BYPASS 0x00000200 +#define DISP_MERGE_CNTL__DISP_GRPH_ALPHA__MASK 0x00FF0000 +#define DISP_MERGE_CNTL__DISP_GRPH_ALPHA__SHIFT 16 +#define DISP_MERGE_CNTL__DISP_OV0_ALPHA__MASK 0xFF000000 +#define DISP_MERGE_CNTL__DISP_OV0_ALPHA__SHIFT 24 +#define DISP_OUTPUT_CNTL 0x00000D64 +#define DISP_OUTPUT_CNTL__DISP_DAC_SOURCE__MASK 0x00000003 +#define DISP_OUTPUT_CNTL__DISP_DAC_SOURCE__SHIFT 0 +#define DISP_DAC_SOURCE__YPbPr 0x3 +#define DISP_DAC_SOURCE__PRIMARYCRTC 0x0 +#define DISP_DAC_SOURCE__SECONDARYCRTC 0x1 +#define DISP_DAC_SOURCE__RMX 0x2 +#define DISP_OUTPUT_CNTL__DISP_TRANS_MATRIX_SEL__MASK 0x00000030 +#define DISP_OUTPUT_CNTL__DISP_TRANS_MATRIX_SEL__SHIFT 4 +#define DISP_OUTPUT_CNTL__DISP_RMX_SOURCE 0x00000100 +#define DISP_OUTPUT_CNTL__DISP_RMX_HTAP_SEL 0x00000200 +#define DISP_OUTPUT_CNTL__DISP_RMX_DITH_EN 0x00000400 +#define DISP_OUTPUT_CNTL__DISP_TV_SOURCE 0x00010000 +#define DISP_OUTPUT_CNTL__DISP_TV_MODE__MASK 0x00060000 +#define DISP_OUTPUT_CNTL__DISP_TV_MODE__SHIFT 17 +#define DISP_OUTPUT_CNTL__DISP_TV_YG_DITH_EN 0x00080000 +#define DISP_OUTPUT_CNTL__DISP_TV_CbB_CrR_DITH_EN 0x00100000 +#define DISP_OUTPUT_CNTL__DISP_TV_BIT_WIDTH 0x00200000 +#define DISP_OUTPUT_CNTL__DISP_TV_SYNC_MODE__MASK 0x00C00000 +#define DISP_OUTPUT_CNTL__DISP_TV_SYNC_MODE__SHIFT 22 +#define DISP_OUTPUT_CNTL__DISP_TV_SYNC_FORCE 0x01000000 +#define DISP_OUTPUT_CNTL__DISP_TV_SYNC_COLOR__MASK 0x06000000 +#define DISP_OUTPUT_CNTL__DISP_TV_SYNC_COLOR__SHIFT 25 +#define DISP_OUTPUT_CNTL__DISP_TV_EVEN_FLAG_CNTL__MASK 0x18000000 +#define DISP_OUTPUT_CNTL__DISP_TV_EVEN_FLAG_CNTL__SHIFT 27 +#define DISP_OUTPUT_CNTL__DISP_TV_SYNC_STATUS 0x20000000 +#define DISP_OUTPUT_CNTL__DISP_TV_H_DOWNSCALE 0x40000000 +#define DISP_OUTPUT_CNTL__DISP_TRANS_SOURCE__MASK 0x00003000 +#define DISP_OUTPUT_CNTL__DISP_TRANS_SOURCE__SHIFT 12 +#define DISP_TRANS_SOURCE__PRIMARYCRTC 0x0 +#define DISP_TRANS_SOURCE__SECONDARYCRTC 0x1 +#define DISP_TRANS_SOURCE__RMX 0x2 +#define DISP_OUTPUT_CNTL__DISP_TVDAC_SOURCE__MASK 0x0000000C +#define DISP_OUTPUT_CNTL__DISP_TVDAC_SOURCE__SHIFT 2 +#define DISP_TVDAC_SOURCE__PRIMARYCRTC 0x0 +#define DISP_TVDAC_SOURCE__SECONDARYCRTC 0x1 +#define DISP_TVDAC_SOURCE__RMX 0x2 +#define DISP_TVDAC_SOURCE__YPbPr 0x3 +#define DISP2_MERGE_CNTL 0x00000D68 +#define DISP2_MERGE_CNTL__DISP2_RGB_OFFSET_EN 0x00000100 +#define DAC_EMBEDDED_SYNC_CNTL 0x00000DC0 +#define DAC_EMBEDDED_SYNC_CNTL__DAC_EMBED_SYNC_EN_Y_G 0x00000001 +#define DAC_EMBEDDED_SYNC_CNTL__DAC_EMBED_SYNC_EN_Cb_B 0x00000002 +#define DAC_EMBEDDED_SYNC_CNTL__DAC_EMBED_SYNC_EN_Cr_R 0x00000004 +#define DAC_EMBEDDED_SYNC_CNTL__DAC_TRILEVEL_SYNC_EN 0x00000008 +#define DAC_EMBEDDED_SYNC_CNTL__DAC_EMBED_VSYNC_EN_Y_G 0x00000010 +#define DAC_EMBEDDED_SYNC_CNTL__DAC_EMBED_VSYNC_EN_CbCr_BR 0x00000020 +#define DAC_EMBEDDED_SYNC_CNTL__DAC_HSYNC_WID_LSB__MASK 0x00070000 +#define DAC_EMBEDDED_SYNC_CNTL__DAC_HSYNC_WID_LSB__SHIFT 16 +#define DAC_BROAD_PULSE 0x00000DC4 +#define DAC_BROAD_PULSE__DAC_BROAD_PULSE_START__MASK 0x00001FFF +#define DAC_BROAD_PULSE__DAC_BROAD_PULSE_START__SHIFT 0 +#define DAC_BROAD_PULSE__DAC_BROAD_PULSE_END__MASK 0x1FFF0000 +#define DAC_BROAD_PULSE__DAC_BROAD_PULSE_END__SHIFT 16 +#define DAC_BROAD_PULSE__DAC_BROAD_PULSE_START_R2__MASK 0x00000FFF +#define DAC_BROAD_PULSE__DAC_BROAD_PULSE_START_R2__SHIFT 0 +#define DAC_BROAD_PULSE__DAC_BROAD_PULSE_END_R2__MASK 0x0FFF0000 +#define DAC_BROAD_PULSE__DAC_BROAD_PULSE_END_R2__SHIFT 16 +#define DAC_SKEW_CLKS 0x00000DC8 +#define DAC_SKEW_CLKS__DAC_SKEW_CLKS__MASK 0x000000FF +#define DAC_SKEW_CLKS__DAC_SKEW_CLKS__SHIFT 0 +#define DAC_INCR 0x00000DCC +#define DAC_INCR__DAC_INCR_Y_G__MASK 0x000003FF +#define DAC_INCR__DAC_INCR_Y_G__SHIFT 0 +#define DAC_INCR__DAC_INCR_CrCb_RB__MASK 0x03FF0000 +#define DAC_INCR__DAC_INCR_CrCb_RB__SHIFT 16 +#define DAC_NEG_SYNC_LEVEL 0x00000DD0 +#define DAC_NEG_SYNC_LEVEL__DAC_NEG_SYNC_LEVEL_Y_G__MASK 0x000003FF +#define DAC_NEG_SYNC_LEVEL__DAC_NEG_SYNC_LEVEL_Y_G__SHIFT 0 +#define DAC_NEG_SYNC_LEVEL__DAC_NEG_SYNC_LEVEL_CrCb_RB__MASK 0x03FF0000 +#define DAC_NEG_SYNC_LEVEL__DAC_NEG_SYNC_LEVEL_CrCb_RB__SHIFT 16 +#define DAC_POS_SYNC_LEVEL 0x00000DD4 +#define DAC_POS_SYNC_LEVEL__DAC_POS_SYNC_LEVEL_Y_G__MASK 0x000003FF +#define DAC_POS_SYNC_LEVEL__DAC_POS_SYNC_LEVEL_Y_G__SHIFT 0 +#define DAC_POS_SYNC_LEVEL__DAC_POS_SYNC_LEVEL_CrCb_RB__MASK 0x03FF0000 +#define DAC_POS_SYNC_LEVEL__DAC_POS_SYNC_LEVEL_CrCb_RB__SHIFT 16 +#define DAC_BLANK_LEVEL 0x00000DD8 +#define DAC_BLANK_LEVEL__DAC_BLANK_LEVEL_Y_G__MASK 0x000003FF +#define DAC_BLANK_LEVEL__DAC_BLANK_LEVEL_Y_G__SHIFT 0 +#define DAC_BLANK_LEVEL__DAC_BLANK_LEVEL_CrCb_RB__MASK 0x03FF0000 +#define DAC_BLANK_LEVEL__DAC_BLANK_LEVEL_CrCb_RB__SHIFT 16 +#define DAC_SYNC_EQUALIZATION 0x00000DDC +#define DAC_SYNC_EQUALIZATION__DAC_SYNC_EQ_START__MASK 0x000007FF +#define DAC_SYNC_EQUALIZATION__DAC_SYNC_EQ_START__SHIFT 0 +#define DAC_SYNC_EQUALIZATION__DAC_SYNC_EQ_END__MASK 0x07FF0000 +#define DAC_SYNC_EQUALIZATION__DAC_SYNC_EQ_END__SHIFT 16 +#define TV_MASTER_CNTL 0x00000800 +#define TV_MASTER_CNTL__TV_ASYNC_RST 0x00000001 +#define TV_MASTER_CNTL__CRT_ASYNC_RST 0x00000002 +#define TV_MASTER_CNTL__RESTART_PHASE_FIX 0x00000008 +#define TV_MASTER_CNTL__TV_FIFO_ASYNC_RST 0x00000010 +#define TV_MASTER_CNTL__MV_BP_LEVEL_FIX_EN 0x00000020 +#define TV_MASTER_CNTL__EXTRA_BIT_ONE_0 0x00000040 +#define TV_MASTER_CNTL__CRT_FIFO_CE_EN 0x00000200 +#define TV_MASTER_CNTL__TV_FIFO_CE_EN 0x00000400 +#define TV_MASTER_CNTL__RE_SYNC_NOW_SEL__MASK 0x0000C000 +#define TV_MASTER_CNTL__RE_SYNC_NOW_SEL__SHIFT 14 +#define TV_MASTER_CNTL__EXTRA_BIT_ZERO_1 0x00010000 +#define TV_MASTER_CNTL__EXTRA_BIT_ONE_1 0x00020000 +#define TV_MASTER_CNTL__EXTRA_BIT_ZERO_2 0x00040000 +#define TV_MASTER_CNTL__EXTRA_BIT_ONE_2 0x00080000 +#define TV_MASTER_CNTL__TVCLK_ALWAYS_ONb 0x40000000 +#define TV_MASTER_CNTL__TV_ON 0x80000000 +#define TV_DAC_CNTL 0x0000088C +#define TV_DAC_CNTL__NBLANK 0x00000001 +#define TV_DAC_CNTL__NHOLD 0x00000002 +#define TV_DAC_CNTL__PEDESTAL 0x00000004 +#define TV_DAC_CNTL__DETECT 0x00000010 +#define TV_DAC_CNTL__CMPOUT 0x00000020 +#define TV_DAC_CNTL__BGSLEEP 0x00000040 +#define TV_DAC_CNTL__STD__MASK 0x00000300 +#define TV_DAC_CNTL__STD__SHIFT 8 +#define STD__PAL 0x0 +#define STD__NTSC 0x1 +#define STD__PS2 0x2 +#define STD__RS343 0x3 +#define TV_DAC_CNTL__MON__MASK 0x0000F000 +#define TV_DAC_CNTL__MON__SHIFT 12 +#define TV_DAC_CNTL__BGADJ__MASK 0x000F0000 +#define TV_DAC_CNTL__BGADJ__SHIFT 16 +#define TV_DAC_CNTL__DACADJ__MASK 0x00F00000 +#define TV_DAC_CNTL__DACADJ__SHIFT 20 +#define TV_DAC_CNTL__RDACPD 0x01000000 +#define TV_DAC_CNTL__GDACPD 0x02000000 +#define TV_DAC_CNTL__BDACPD 0x04000000 +#define TV_DAC_CNTL__RDACDET 0x20000000 +#define TV_DAC_CNTL__GDACDET 0x40000000 +#define TV_DAC_CNTL__BDACDET 0x80000000 +#define TV_DAC_CNTL__DACADJ_R4__MASK 0x01F00000 +#define TV_DAC_CNTL__DACADJ_R4__SHIFT 20 +#define TV_DAC_CNTL__RDACPD_R4 0x02000000 +#define TV_DAC_CNTL__GDACPD_R4 0x04000000 +#define TV_DAC_CNTL__BDACPD_R4 0x08000000 +#define TV_DAC_CNTL__TVENABLE_R4 0x10000000 +#define VIPPAD_EN 0x000001A0 +#define VIPPAD_EN__VIPPAD_EN__MASK 0x0007FFFF +#define VIPPAD_EN__VIPPAD_EN__SHIFT 0 +#define VIPPAD_EN__VIPPAD_EN_TVODATA__MASK 0x000003FF +#define VIPPAD_EN__VIPPAD_EN_TVODATA__SHIFT 0 +#define VIPPAD_EN__VIPPAD_EN_TVOCLKO 0x00000400 +#define VIPPAD_EN__VIPPAD_EN_ROMCSb 0x00000800 +#define VIPPAD_EN__VIPPAD_EN_VHAD__MASK 0x00003000 +#define VIPPAD_EN__VIPPAD_EN_VHAD__SHIFT 12 +#define VIPPAD_EN__VIPPAD_EN_VPHCTL 0x00010000 +#define VIPPAD_EN__VIPPAD_EN_VIPCLK 0x00020000 +#define VIPPAD_EN__VIPPAD_EN_SI 0x00080000 +#define VIPPAD_EN__VIPPAD_EN_SO 0x00100000 +#define VIPPAD_EN__VIPPAD_EN_SCK 0x00200000 +#define VIPPAD_Y 0x000001A4 +#define VIPPAD_Y__VIPPAD_Y__MASK 0x0007FFFF +#define VIPPAD_Y__VIPPAD_Y__SHIFT 0 +#define VIPPAD_Y__VIPPAD_Y_TVODATA__MASK 0x000003FF +#define VIPPAD_Y__VIPPAD_Y_TVODATA__SHIFT 0 +#define VIPPAD_Y__VIPPAD_Y_TVOCLKO 0x00000400 +#define VIPPAD_Y__VIPPAD_Y_ROMCSb 0x00000800 +#define VIPPAD_Y__VIPPAD_Y_VHAD__MASK 0x00003000 +#define VIPPAD_Y__VIPPAD_Y_VHAD__SHIFT 12 +#define VIPPAD_Y__VIPPAD_Y_VPHCTL 0x00010000 +#define VIPPAD_Y__VIPPAD_Y_VIPCLK 0x00020000 +#define VIPPAD_Y__VIPPAD_Y_SI 0x00080000 +#define VIPPAD_Y__VIPPAD_Y_SO 0x00100000 +#define VIPPAD_Y__VIPPAD_Y_SCK 0x00200000 +#define VIPPAD1_EN 0x000001B0 +#define VIPPAD1_EN__VIPPAD1_EN__MASK 0x0003FFFF +#define VIPPAD1_EN__VIPPAD1_EN__SHIFT 0 +#define VIPPAD1_EN__VIPPAD_EN_VID__MASK 0x000000FF +#define VIPPAD1_EN__VIPPAD_EN_VID__SHIFT 0 +#define VIPPAD1_EN__VIPPAD_EN_VPCLK0 0x00000100 +#define VIPPAD1_EN__VIPPAD_EN_DVALID 0x00000200 +#define VIPPAD1_EN__VIPPAD_EN_PSYNC 0x00000400 +#define VIPPAD1_EN__VIPPAD_EN_DVODATA__MASK 0x0FFF0000 +#define VIPPAD1_EN__VIPPAD_EN_DVODATA__SHIFT 16 +#define VIPPAD1_EN__VIPPAD_EN_DVOCNTL__MASK 0x70000000 +#define VIPPAD1_EN__VIPPAD_EN_DVOCNTL__SHIFT 28 +#define VIPPAD1_Y 0x000001B4 +#define VIPPAD1_Y__VIPPAD1_Y__MASK 0x0003FFFF +#define VIPPAD1_Y__VIPPAD1_Y__SHIFT 0 +#define VIPPAD1_Y__VIPPAD_Y_VID__MASK 0x000000FF +#define VIPPAD1_Y__VIPPAD_Y_VID__SHIFT 0 +#define VIPPAD1_Y__VIPPAD_Y_VPCLK0 0x00000100 +#define VIPPAD1_Y__VIPPAD_Y_DVALID 0x00000200 +#define VIPPAD1_Y__VIPPAD_Y_PSYNC 0x00000400 +#define VIPPAD1_Y__VIPPAD_Y_DVODATA__MASK 0x0FFF0000 +#define VIPPAD1_Y__VIPPAD_Y_DVODATA__SHIFT 16 +#define VIPPAD1_Y__VIPPAD_Y_DVOCNTL__MASK 0x70000000 +#define VIPPAD1_Y__VIPPAD_Y_DVOCNTL__SHIFT 28 +#define GPIO_DDC1 0x00000060 +#define GPIO_DDC1__DDC1_DATA_OUTPUT 0x00000001 +#define GPIO_DDC1__DDC1_CLK_OUTPUT 0x00000002 +#define GPIO_DDC1__DDC1_DATA_INPUT 0x00000100 +#define GPIO_DDC1__DDC1_CLK_INPUT 0x00000200 +#define GPIO_DDC1__DDC1_DATA_OUT_EN 0x00010000 +#define GPIO_DDC1__DDC1_CLK_OUT_EN 0x00020000 +#define GPIO_DDC1__SW_WANTS_TO_USE_DVI_I2C 0x00100000 +#define GPIO_DDC1__SW_CAN_USE_DVI_I2C 0x00100000 +#define GPIO_DDC1__SW_DONE_USING_DVI_I2C 0x00200000 +#define GPIO_DDC1__HW_USING_DVI_I2C 0x00400000 +#define GPIO_DDC2 0x00000064 +#define GPIO_DDC2__DDC2_DATA_OUTPUT 0x00000001 +#define GPIO_DDC2__DDC2_CLK_OUTPUT 0x00000002 +#define GPIO_DDC2__DDC2_DATA_INPUT 0x00000100 +#define GPIO_DDC2__DDC2_CLK_INPUT 0x00000200 +#define GPIO_DDC2__DDC2_DATA_OUT_EN 0x00010000 +#define GPIO_DDC2__DDC2_CLK_OUT_EN 0x00020000 +#define GPIO_DDC2__SW_WANTS_TO_USE_DVI_I2C 0x00100000 +#define GPIO_DDC2__SW_CAN_USE_DVI_I2C 0x00100000 +#define GPIO_DDC2__SW_DONE_USING_DVI_I2C 0x00200000 +#define GPIO_DDC2__HW_USING_DVI_I2C 0x00400000 +#define CLOCK_CNTL_INDEX 0x00000008 +#define CLOCK_CNTL_INDEX__PLL_ADDR__MASK 0x0000001F +#define CLOCK_CNTL_INDEX__PLL_ADDR__SHIFT 0 +#define CLOCK_CNTL_INDEX__PLL_WR_EN 0x00000080 +#define CLOCK_CNTL_INDEX__PPLL_DIV_SEL__MASK 0x00000300 +#define CLOCK_CNTL_INDEX__PPLL_DIV_SEL__SHIFT 8 +#define CLOCK_CNTL_INDEX__PLL_ADDR_R2__MASK 0x0000003F +#define CLOCK_CNTL_INDEX__PLL_ADDR_R2__SHIFT 0 +#define CLOCK_CNTL_DATA 0x0000000C +#define CLOCK_CNTL_DATA__PLL_DATA__MASK 0xFFFFFFFF +#define CLOCK_CNTL_DATA__PLL_DATA__SHIFT 0 +#define MCLK_CNTL 0x00000012 +#define MCLK_CNTL__MCLKA_SRC_SEL__MASK 0x00000007 +#define MCLK_CNTL__MCLKA_SRC_SEL__SHIFT 0 +#define MCLK_CNTL__YCLKA_SRC_SEL__MASK 0x00000070 +#define MCLK_CNTL__YCLKA_SRC_SEL__SHIFT 4 +#define MCLK_CNTL__MCLKB_SRC_SEL__MASK 0x00000700 +#define MCLK_CNTL__MCLKB_SRC_SEL__SHIFT 8 +#define MCLK_CNTL__YCLKB_SRC_SEL__MASK 0x00007000 +#define MCLK_CNTL__YCLKB_SRC_SEL__SHIFT 12 +#define MCLK_CNTL__FORCE_MCLKA 0x00010000 +#define MCLK_CNTL__FORCE_MCLKB 0x00020000 +#define MCLK_CNTL__FORCE_YCLKA 0x00040000 +#define MCLK_CNTL__FORCE_YCLKB 0x00080000 +#define MCLK_CNTL__FORCE_MC 0x00100000 +#define MCLK_CNTL__FORCE_AIC 0x00200000 +#define MCLK_CNTL__MRDCKA0_SOUTSEL__MASK 0x03000000 +#define MCLK_CNTL__MRDCKA0_SOUTSEL__SHIFT 24 +#define MCLK_CNTL__MRDCKA1_SOUTSEL__MASK 0x0C000000 +#define MCLK_CNTL__MRDCKA1_SOUTSEL__SHIFT 26 +#define MCLK_CNTL__MRDCKB0_SOUTSEL__MASK 0x30000000 +#define MCLK_CNTL__MRDCKB0_SOUTSEL__SHIFT 28 +#define MCLK_CNTL__MRDCKB1_SOUTSEL__MASK 0xC0000000 +#define MCLK_CNTL__MRDCKB1_SOUTSEL__SHIFT 30 +#define MCLK_CNTL__FORCE_MC_MCLKA 0x00010000 +#define MCLK_CNTL__FORCE_MC_MCLKB 0x00020000 +#define MCLK_CNTL__FORCE_MC_MCLK 0x00100000 +#define MCLK_CNTL__DISABLE_MC_MCLKA 0x00200000 +#define MCLK_CNTL__DISABLE_MC_MCLKB 0x00400000 +#define SCLK_CNTL 0x0000000D +#define SCLK_CNTL__SCLK_SRC_SEL__MASK 0x00000007 +#define SCLK_CNTL__SCLK_SRC_SEL__SHIFT 0 +#define SCLK_CNTL__TCLK_SRC_SEL__MASK 0x00000700 +#define SCLK_CNTL__TCLK_SRC_SEL__SHIFT 8 +#define SCLK_CNTL__FORCE_CP 0x00010000 +#define SCLK_CNTL__FORCE_HDP 0x00020000 +#define SCLK_CNTL__FORCE_DISP 0x00040000 +#define SCLK_CNTL__FORCE_TOP 0x00080000 +#define SCLK_CNTL__FORCE_E2 0x00100000 +#define SCLK_CNTL__FORCE_SE 0x00200000 +#define SCLK_CNTL__FORCE_IDCT 0x00400000 +#define SCLK_CNTL__FORCE_VIP 0x00800000 +#define SCLK_CNTL__FORCE_RE 0x01000000 +#define SCLK_CNTL__FORCE_PB 0x02000000 +#define SCLK_CNTL__FORCE_TAM 0x04000000 +#define SCLK_CNTL__FORCE_TDM 0x08000000 +#define SCLK_CNTL__FORCE_RB 0x10000000 +#define SCLK_CNTL__CP_MAX_DYN_STOP_LAT 0x00000008 +#define SCLK_CNTL__HDP_MAX_DYN_STOP_LAT 0x00000010 +#define SCLK_CNTL__E2_MAX_DYN_STOP_LAT 0x00000040 +#define SCLK_CNTL__SE_MAX_DYN_STOP_LAT 0x00000080 +#define SCLK_CNTL__IDCT_MAX_DYN_STOP_LAT 0x00000100 +#define SCLK_CNTL__VIP_MAX_DYN_STOP_LAT 0x00000200 +#define SCLK_CNTL__RE_MAX_DYN_STOP_LAT 0x00000400 +#define SCLK_CNTL__PB_MAX_DYN_STOP_LAT 0x00000800 +#define SCLK_CNTL__TAM_MAX_DYN_STOP_LAT 0x00001000 +#define SCLK_CNTL__TDM_MAX_DYN_STOP_LAT 0x00002000 +#define SCLK_CNTL__RB_MAX_DYN_STOP_LAT 0x00004000 +#define SCLK_CNTL__FORCE_DISP2 0x00008000 +#define SCLK_CNTL__FORCE_DISP1 0x00040000 +#define SCLK_CNTL__FORCE_SUBPIC 0x40000000 +#define SCLK_CNTL__FORCE_OV0 0x80000000 +#define SCLK_CNTL__TV_MAX_DYN_STOP_LAT 0x00000020 +#define SCLK_CNTL__FORCE_TV_SCLK 0x20000000 +#define SCLK_CNTL__VAP_MAX_DYN_STOP_LAT 0x00000080 +#define SCLK_CNTL__SR_MAX_DYN_STOP_LAT 0x00000400 +#define SCLK_CNTL__PX_MAX_DYN_STOP_LAT 0x00000800 +#define SCLK_CNTL__TX_MAX_DYN_STOP_LAT 0x00001000 +#define SCLK_CNTL__US_MAX_DYN_STOP_LAT 0x00002000 +#define SCLK_CNTL__SU_MAX_DYN_STOP_LAT 0x00004000 +#define SCLK_CNTL__FORCE_VAP 0x00200000 +#define SCLK_CNTL__FORCE_SR 0x02000000 +#define SCLK_CNTL__FORCE_PX 0x04000000 +#define SCLK_CNTL__FORCE_TX 0x08000000 +#define SCLK_CNTL__FORCE_US 0x10000000 +#define SCLK_CNTL__FORCE_SU 0x40000000 +#define PPLL_CNTL 0x00000002 +#define PPLL_CNTL__PPLL_RESET 0x00000001 +#define PPLL_CNTL__PPLL_SLEEP 0x00000002 +#define PPLL_CNTL__PPLL_TST_EN 0x00000004 +#define PPLL_CNTL__PPLL_REFCLK_SEL 0x00000010 +#define PPLL_CNTL__PPLL_FBCLK_SEL 0x00000020 +#define PPLL_CNTL__PPLL_TCPOFF 0x00000040 +#define PPLL_CNTL__PPLL_TVCOMAX 0x00000080 +#define PPLL_CNTL__PPLL_PCP__MASK 0x00000700 +#define PPLL_CNTL__PPLL_PCP__SHIFT 8 +#define PPLL_CNTL__PPLL_PVG__MASK 0x00003800 +#define PPLL_CNTL__PPLL_PVG__SHIFT 11 +#define PPLL_CNTL__PPLL_PDC__MASK 0x0000C000 +#define PPLL_CNTL__PPLL_PDC__SHIFT 14 +#define PPLL_CNTL__PPLL_ATOMIC_UPDATE_EN 0x00010000 +#define PPLL_CNTL__PPLL_VGA_ATOMIC_UPDATE_EN 0x00020000 +#define PPLL_CNTL__PPLL_ATOMIC_UPDATE_SYNC 0x00040000 +#define PPLL_CNTL__PPLL_DISABLE_AUTO_RESET 0x00080000 +#define PPLL_CNTL__PPLL_DIV_RESET 0x00000008 +#define PPLL_REF_DIV 0x00000003 +#define PPLL_REF_DIV__PPLL_REF_DIV__MASK 0x000003FF +#define PPLL_REF_DIV__PPLL_REF_DIV__SHIFT 0 +#define PPLL_REF_DIV__PPLL_ATOMIC_UPDATE_W 0x00008000 +#define PPLL_REF_DIV__PPLL_ATOMIC_UPDATE_R 0x00008000 +#define PPLL_REF_DIV__PPLL_REF_DIV_SRC__MASK 0x00030000 +#define PPLL_REF_DIV__PPLL_REF_DIV_SRC__SHIFT 16 +#define PPLL_REF_DIV_SRC__XTALIN 0x0 +#define PPLL_REF_DIV_SRC__PLLSCLK_2 0x1 +#define PPLL_REF_DIV_SRC__PLLSCLK_4 0x2 +#define PPLL_REF_DIV_SRC__SREFCLK 0x3 +#define PPLL_REF_DIV__PPLL_REF_DIV_ACC__MASK 0x0FFC0000 +#define PPLL_REF_DIV__PPLL_REF_DIV_ACC__SHIFT 18 +#define PPLL_DIV_0 0x00000004 +#define PPLL_DIV_0__PPLL_FB0_DIV__MASK 0x000007FF +#define PPLL_DIV_0__PPLL_FB0_DIV__SHIFT 0 +#define PPLL_DIV_0__PPLL_ATOMIC_UPDATE_W 0x00008000 +#define PPLL_DIV_0__PPLL_ATOMIC_UPDATE_R 0x00008000 +#define PPLL_DIV_0__PPLL_POST0_DIV__MASK 0x00070000 +#define PPLL_DIV_0__PPLL_POST0_DIV__SHIFT 16 +#define PPLL_DIV_0__PPLL_FB_DIV_FRACTION__MASK 0x00380000 +#define PPLL_DIV_0__PPLL_FB_DIV_FRACTION__SHIFT 19 +#define PPLL_DIV_0__PPLL_FB_DIV_FRACTION_UPDATE 0x00400000 +#define PPLL_DIV_0__PPLL_FB_DIV_FRACTION_EN 0x00800000 +#define PPLL_DIV_1 0x00000005 +#define PPLL_DIV_1__PPLL_FB1_DIV__MASK 0x000007FF +#define PPLL_DIV_1__PPLL_FB1_DIV__SHIFT 0 +#define PPLL_DIV_1__PPLL_ATOMIC_UPDATE_W 0x00008000 +#define PPLL_DIV_1__PPLL_ATOMIC_UPDATE_R 0x00008000 +#define PPLL_DIV_1__PPLL_POST1_DIV__MASK 0x00070000 +#define PPLL_DIV_1__PPLL_POST1_DIV__SHIFT 16 +#define PPLL_DIV_2 0x00000006 +#define PPLL_DIV_2__PPLL_FB2_DIV__MASK 0x000007FF +#define PPLL_DIV_2__PPLL_FB2_DIV__SHIFT 0 +#define PPLL_DIV_2__PPLL_ATOMIC_UPDATE_W 0x00008000 +#define PPLL_DIV_2__PPLL_ATOMIC_UPDATE_R 0x00008000 +#define PPLL_DIV_2__PPLL_POST2_DIV__MASK 0x00070000 +#define PPLL_DIV_2__PPLL_POST2_DIV__SHIFT 16 +#define PPLL_DIV_3 0x00000007 +#define PPLL_DIV_3__PPLL_FB3_DIV__MASK 0x000007FF +#define PPLL_DIV_3__PPLL_FB3_DIV__SHIFT 0 +#define PPLL_DIV_3__PPLL_ATOMIC_UPDATE_W 0x00008000 +#define PPLL_DIV_3__PPLL_ATOMIC_UPDATE_R 0x00008000 +#define PPLL_DIV_3__PPLL_POST3_DIV__MASK 0x00070000 +#define PPLL_DIV_3__PPLL_POST3_DIV__SHIFT 16 +#define VCLK_ECP_CNTL 0x00000008 +#define VCLK_ECP_CNTL__VCLK_SRC_SEL__MASK 0x00000003 +#define VCLK_ECP_CNTL__VCLK_SRC_SEL__SHIFT 0 +#define VCLK_SRC_SEL__CPUCLK 0x0 +#define VCLK_SRC_SEL__PSCANCLK 0x1 +#define VCLK_SRC_SEL__BYTE_CLK 0x2 +#define VCLK_SRC_SEL__PPLLCLK 0x3 +#define VCLK_ECP_CNTL__VCLK_INVERT 0x00000010 +#define VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb 0x00000040 +#define VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb 0x00000080 +#define VCLK_ECP_CNTL__ECP_DIV__MASK 0x00000300 +#define VCLK_ECP_CNTL__ECP_DIV__SHIFT 8 +#define VCLK_ECP_CNTL__ECP_FORCE_ON 0x00040000 +#define VCLK_ECP_CNTL__SUBCLK_FORCE_ON 0x00080000 +#define VCLK_ECP_CNTL__BYTE_CLK_POST_DIV__MASK 0x00030000 +#define VCLK_ECP_CNTL__BYTE_CLK_POST_DIV__SHIFT 16 +#define VCLK_ECP_CNTL__BYTE_CLK_OUT_EN 0x00100000 +#define VCLK_ECP_CNTL__BYTE_CLK_SKEW__MASK 0x07000000 +#define VCLK_ECP_CNTL__BYTE_CLK_SKEW__SHIFT 24 +#define VCLK_ECP_CNTL__PCICLK_INVERT 0x00000020 +#define VCLK_ECP_CNTL__PIXCLK_SRC_INVERT 0x00000020 +#define VCLK_ECP_CNTL__PIXCLK_SRC_INVERT_R3 0x08000000 +#define VCLK_ECP_CNTL__DISP_DAC_PIXCLK_DAC_BLANK_OFF 0x00800000 +#define HTOTAL_CNTL 0x00000009 +#define HTOTAL_CNTL__HTOT_PIX_SLIP__MASK 0x0000000F +#define HTOTAL_CNTL__HTOT_PIX_SLIP__SHIFT 0 +#define HTOTAL_CNTL__HTOT_VCLK_SLIP__MASK 0x00000F00 +#define HTOTAL_CNTL__HTOT_VCLK_SLIP__SHIFT 8 +#define HTOTAL_CNTL__HTOT_PPLL_SLIP__MASK 0x00070000 +#define HTOTAL_CNTL__HTOT_PPLL_SLIP__SHIFT 16 +#define HTOTAL_CNTL__HTOT_CNTL_EDGE 0x01000000 +#define HTOTAL_CNTL__HTOT_CNTL_VGA_EN 0x10000000 +#define FP_H_SYNC_STRT_WID 0x000002C4 +#define FP_H_SYNC_STRT_WID__FP_H_SYNC_STRT_PIX__MASK 0x00000007 +#define FP_H_SYNC_STRT_WID__FP_H_SYNC_STRT_PIX__SHIFT 0 +#define FP_H_SYNC_STRT_WID__FP_H_SYNC_STRT_CHAR__MASK 0x00001FF8 +#define FP_H_SYNC_STRT_WID__FP_H_SYNC_STRT_CHAR__SHIFT 3 +#define FP_H_SYNC_STRT_WID__FP_H_SYNC_WID__MASK 0x003F0000 +#define FP_H_SYNC_STRT_WID__FP_H_SYNC_WID__SHIFT 16 +#define FP_H_SYNC_STRT_WID__FP_H_SYNC_POL 0x00800000 +#define FP_V_SYNC_STRT_WID 0x000002C8 +#define FP_V_SYNC_STRT_WID__FP_V_SYNC_STRT__MASK 0x00000FFF +#define FP_V_SYNC_STRT_WID__FP_V_SYNC_STRT__SHIFT 0 +#define FP_V_SYNC_STRT_WID__FP_V_SYNC_WID__MASK 0x001F0000 +#define FP_V_SYNC_STRT_WID__FP_V_SYNC_WID__SHIFT 16 +#define FP_V_SYNC_STRT_WID__FP_V_SYNC_POL 0x00800000 +#define FP_CRTC_H_TOTAL_DISP 0x00000250 +#define FP_CRTC_H_TOTAL_DISP__FP_CRTC_H_TOTAL__MASK 0x000003FF +#define FP_CRTC_H_TOTAL_DISP__FP_CRTC_H_TOTAL__SHIFT 0 +#define FP_CRTC_H_TOTAL_DISP__FP_CRTC_H_DISP__MASK 0x01FF0000 +#define FP_CRTC_H_TOTAL_DISP__FP_CRTC_H_DISP__SHIFT 16 +#define FP_CRTC_V_TOTAL_DISP 0x00000254 +#define FP_CRTC_V_TOTAL_DISP__FP_CRTC_V_TOTAL__MASK 0x00000FFF +#define FP_CRTC_V_TOTAL_DISP__FP_CRTC_V_TOTAL__SHIFT 0 +#define FP_CRTC_V_TOTAL_DISP__FP_CRTC_V_DISP__MASK 0x0FFF0000 +#define FP_CRTC_V_TOTAL_DISP__FP_CRTC_V_DISP__SHIFT 16 +#define PALETTE_INDEX 0x000000B0 +#define PALETTE_INDEX__PALETTE_W_INDEX__MASK 0x000000FF +#define PALETTE_INDEX__PALETTE_W_INDEX__SHIFT 0 +#define PALETTE_INDEX__PALETTE_R_INDEX__MASK 0x00FF0000 +#define PALETTE_INDEX__PALETTE_R_INDEX__SHIFT 16 +#define PALETTE_DATA 0x000000B4 +#define PALETTE_DATA__PALETTE_DATA_B__MASK 0x000000FF +#define PALETTE_DATA__PALETTE_DATA_B__SHIFT 0 +#define PALETTE_DATA__PALETTE_DATA_G__MASK 0x0000FF00 +#define PALETTE_DATA__PALETTE_DATA_G__SHIFT 8 +#define PALETTE_DATA__PALETTE_DATA_R__MASK 0x00FF0000 +#define PALETTE_DATA__PALETTE_DATA_R__SHIFT 16 +#define PALETTE_30_DATA 0x000000B8 +#define PALETTE_30_DATA__PALETTE_DATA_B__MASK 0x000003FF +#define PALETTE_30_DATA__PALETTE_DATA_B__SHIFT 0 +#define PALETTE_30_DATA__PALETTE_DATA_G__MASK 0x000FFC00 +#define PALETTE_30_DATA__PALETTE_DATA_G__SHIFT 10 +#define PALETTE_30_DATA__PALETTE_DATA_R__MASK 0x3FF00000 +#define PALETTE_30_DATA__PALETTE_DATA_R__SHIFT 20 +#define SURFACE_CNTL 0x00000B00 +#define SURFACE_CNTL__SURF_TRANSLATION_DIS 0x00000100 +#define SURFACE_CNTL__NONSURF_AP0_SWP__MASK 0x00300000 +#define SURFACE_CNTL__NONSURF_AP0_SWP__SHIFT 20 +#define SURFACE_CNTL__NONSURF_AP1_SWP__MASK 0x00C00000 +#define SURFACE_CNTL__NONSURF_AP1_SWP__SHIFT 22 +#define SURFACE0_INFO 0x00000B0C +#define SURFACE0_INFO__SURF0_PITCHSEL__MASK 0x000003FF +#define SURFACE0_INFO__SURF0_PITCHSEL__SHIFT 0 +#define SURFACE0_INFO__SURF0_TILE_MODE__MASK 0x00030000 +#define SURFACE0_INFO__SURF0_TILE_MODE__SHIFT 16 +#define SURF0_TILE_MODE__NO_TILING(p) 0x0 +#define SURF0_TILE_MODE__MACRO_TILING(p) 0x0 +#define SURF0_TILE_MODE__MICRO_TILING(p) 0x0 +#define SURF0_TILE_MODE__MACRO_MICRO_TILING(p) 0x0 +#define SURF0_TILE_MODE__32_BIT_Z_TILING(p) 0x0 +#define SURF0_TILE_MODE__16_BIT_Z_TILING(p) 0x0 +#define SURFACE0_INFO__SURF0_AP0_SWP__MASK 0x00300000 +#define SURFACE0_INFO__SURF0_AP0_SWP__SHIFT 20 +#define SURFACE0_INFO__SURF0_AP1_SWP__MASK 0x00C00000 +#define SURFACE0_INFO__SURF0_AP1_SWP__SHIFT 22 +#define SURFACE0_INFO__SURF0_WRITE_FLAG 0x01000000 +#define SURFACE0_INFO__SURF0_READ_FLAG 0x02000000 +#define SURFACE0_INFO__SURF0_TILE_MODE_R2__MASK 0x00070000 +#define SURFACE0_INFO__SURF0_TILE_MODE_R2__SHIFT 16 +#define SURFACE0_INFO__SURF0_PITCHSEL_R3__MASK 0x00001FFF +#define SURFACE0_INFO__SURF0_PITCHSEL_R3__SHIFT 0 +#define SURFACE0_LOWER_BOUND 0x00000B04 +#define SURFACE0_LOWER_BOUND__SURF_LOWER__MASK 0x0FFFFFFF +#define SURFACE0_LOWER_BOUND__SURF_LOWER__SHIFT 0 +#define SURFACE0_UPPER_BOUND 0x00000B08 +#define SURFACE0_UPPER_BOUND__SURF_UPPER__MASK 0x0FFFFFFF +#define SURFACE0_UPPER_BOUND__SURF_UPPER__SHIFT 0 +#define SURFACE1_INFO 0x00000B1C +#define SURFACE1_INFO__SURF1_PITCHSEL__MASK 0x000003FF +#define SURFACE1_INFO__SURF1_PITCHSEL__SHIFT 0 +#define SURFACE1_INFO__SURF1_TILE_MODE__MASK 0x00030000 +#define SURFACE1_INFO__SURF1_TILE_MODE__SHIFT 16 +#define SURFACE1_INFO__SURF1_AP0_SWP__MASK 0x00300000 +#define SURFACE1_INFO__SURF1_AP0_SWP__SHIFT 20 +#define SURFACE1_INFO__SURF1_AP1_SWP__MASK 0x00C00000 +#define SURFACE1_INFO__SURF1_AP1_SWP__SHIFT 22 +#define SURFACE1_INFO__SURF1_WRITE_FLAG 0x01000000 +#define SURFACE1_INFO__SURF1_READ_FLAG 0x02000000 +#define SURFACE1_INFO__SURF1_TILE_MODE_R2__MASK 0x00070000 +#define SURFACE1_INFO__SURF1_TILE_MODE_R2__SHIFT 16 +#define SURFACE1_INFO__SURF1_PITCHSEL_R3__MASK 0x00001FFF +#define SURFACE1_INFO__SURF1_PITCHSEL_R3__SHIFT 0 +#define SURFACE1_LOWER_BOUND 0x00000B14 +#define SURFACE1_LOWER_BOUND__SURF_LOWER__MASK 0x0FFFFFFF +#define SURFACE1_LOWER_BOUND__SURF_LOWER__SHIFT 0 +#define SURFACE1_UPPER_BOUND 0x00000B18 +#define SURFACE1_UPPER_BOUND__SURF_UPPER__MASK 0x0FFFFFFF +#define SURFACE1_UPPER_BOUND__SURF_UPPER__SHIFT 0 +#define SURFACE2_INFO 0x00000B2C +#define SURFACE2_INFO__SURF2_PITCHSEL__MASK 0x000003FF +#define SURFACE2_INFO__SURF2_PITCHSEL__SHIFT 0 +#define SURFACE2_INFO__SURF2_TILE_MODE__MASK 0x00030000 +#define SURFACE2_INFO__SURF2_TILE_MODE__SHIFT 16 +#define SURFACE2_INFO__SURF2_AP0_SWP__MASK 0x00300000 +#define SURFACE2_INFO__SURF2_AP0_SWP__SHIFT 20 +#define SURFACE2_INFO__SURF2_AP1_SWP__MASK 0x00C00000 +#define SURFACE2_INFO__SURF2_AP1_SWP__SHIFT 22 +#define SURFACE2_INFO__SURF2_WRITE_FLAG 0x01000000 +#define SURFACE2_INFO__SURF2_READ_FLAG 0x02000000 +#define SURFACE2_INFO__SURF2_TILE_MODE_R2__MASK 0x00070000 +#define SURFACE2_INFO__SURF2_TILE_MODE_R2__SHIFT 16 +#define SURFACE2_INFO__SURF2_PITCHSEL_R3__MASK 0x00001FFF +#define SURFACE2_INFO__SURF2_PITCHSEL_R3__SHIFT 0 +#define SURFACE2_LOWER_BOUND 0x00000B24 +#define SURFACE2_LOWER_BOUND__SURF_LOWER__MASK 0x0FFFFFFF +#define SURFACE2_LOWER_BOUND__SURF_LOWER__SHIFT 0 +#define SURFACE2_UPPER_BOUND 0x00000B28 +#define SURFACE2_UPPER_BOUND__SURF_UPPER__MASK 0x0FFFFFFF +#define SURFACE2_UPPER_BOUND__SURF_UPPER__SHIFT 0 +#define SURFACE3_INFO 0x00000B3C +#define SURFACE3_INFO__SURF3_PITCHSEL__MASK 0x000003FF +#define SURFACE3_INFO__SURF3_PITCHSEL__SHIFT 0 +#define SURFACE3_INFO__SURF3_TILE_MODE__MASK 0x00030000 +#define SURFACE3_INFO__SURF3_TILE_MODE__SHIFT 16 +#define SURFACE3_INFO__SURF3_AP0_SWP__MASK 0x00300000 +#define SURFACE3_INFO__SURF3_AP0_SWP__SHIFT 20 +#define SURFACE3_INFO__SURF3_AP1_SWP__MASK 0x00C00000 +#define SURFACE3_INFO__SURF3_AP1_SWP__SHIFT 22 +#define SURFACE3_INFO__SURF3_WRITE_FLAG 0x01000000 +#define SURFACE3_INFO__SURF3_READ_FLAG 0x02000000 +#define SURFACE3_INFO__SURF3_TILE_MODE_R2__MASK 0x00070000 +#define SURFACE3_INFO__SURF3_TILE_MODE_R2__SHIFT 16 +#define SURFACE3_INFO__SURF3_PITCHSEL_R3__MASK 0x00001FFF +#define SURFACE3_INFO__SURF3_PITCHSEL_R3__SHIFT 0 +#define SURFACE3_LOWER_BOUND 0x00000B34 +#define SURFACE3_LOWER_BOUND__SURF_LOWER__MASK 0x0FFFFFFF +#define SURFACE3_LOWER_BOUND__SURF_LOWER__SHIFT 0 +#define SURFACE3_UPPER_BOUND 0x00000B38 +#define SURFACE3_UPPER_BOUND__SURF_UPPER__MASK 0x0FFFFFFF +#define SURFACE3_UPPER_BOUND__SURF_UPPER__SHIFT 0 +#define SURFACE4_INFO 0x00000B4C +#define SURFACE4_INFO__SURF4_PITCHSEL__MASK 0x000003FF +#define SURFACE4_INFO__SURF4_PITCHSEL__SHIFT 0 +#define SURFACE4_INFO__SURF4_TILE_MODE__MASK 0x00030000 +#define SURFACE4_INFO__SURF4_TILE_MODE__SHIFT 16 +#define SURFACE4_INFO__SURF4_AP0_SWP__MASK 0x00300000 +#define SURFACE4_INFO__SURF4_AP0_SWP__SHIFT 20 +#define SURFACE4_INFO__SURF4_AP1_SWP__MASK 0x00C00000 +#define SURFACE4_INFO__SURF4_AP1_SWP__SHIFT 22 +#define SURFACE4_INFO__SURF4_WRITE_FLAG 0x01000000 +#define SURFACE4_INFO__SURF4_READ_FLAG 0x02000000 +#define SURFACE4_INFO__SURF4_TILE_MODE_R2__MASK 0x00070000 +#define SURFACE4_INFO__SURF4_TILE_MODE_R2__SHIFT 16 +#define SURFACE4_INFO__SURF4_PITCHSEL_R3__MASK 0x00001FFF +#define SURFACE4_INFO__SURF4_PITCHSEL_R3__SHIFT 0 +#define SURFACE4_LOWER_BOUND 0x00000B44 +#define SURFACE4_LOWER_BOUND__SURF_LOWER__MASK 0x0FFFFFFF +#define SURFACE4_LOWER_BOUND__SURF_LOWER__SHIFT 0 +#define SURFACE4_UPPER_BOUND 0x00000B48 +#define SURFACE4_UPPER_BOUND__SURF_UPPER__MASK 0x0FFFFFFF +#define SURFACE4_UPPER_BOUND__SURF_UPPER__SHIFT 0 +#define SURFACE5_INFO 0x00000B5C +#define SURFACE5_INFO__SURF5_PITCHSEL__MASK 0x000003FF +#define SURFACE5_INFO__SURF5_PITCHSEL__SHIFT 0 +#define SURFACE5_INFO__SURF5_TILE_MODE__MASK 0x00030000 +#define SURFACE5_INFO__SURF5_TILE_MODE__SHIFT 16 +#define SURFACE5_INFO__SURF5_AP0_SWP__MASK 0x00300000 +#define SURFACE5_INFO__SURF5_AP0_SWP__SHIFT 20 +#define SURFACE5_INFO__SURF5_AP1_SWP__MASK 0x00C00000 +#define SURFACE5_INFO__SURF5_AP1_SWP__SHIFT 22 +#define SURFACE5_INFO__SURF5_WRITE_FLAG 0x01000000 +#define SURFACE5_INFO__SURF5_READ_FLAG 0x02000000 +#define SURFACE5_INFO__SURF5_TILE_MODE_R2__MASK 0x00070000 +#define SURFACE5_INFO__SURF5_TILE_MODE_R2__SHIFT 16 +#define SURFACE5_INFO__SURF5_PITCHSEL_R3__MASK 0x00001FFF +#define SURFACE5_INFO__SURF5_PITCHSEL_R3__SHIFT 0 +#define SURFACE5_LOWER_BOUND 0x00000B54 +#define SURFACE5_LOWER_BOUND__SURF_LOWER__MASK 0x0FFFFFFF +#define SURFACE5_LOWER_BOUND__SURF_LOWER__SHIFT 0 +#define SURFACE5_UPPER_BOUND 0x00000B58 +#define SURFACE5_UPPER_BOUND__SURF_UPPER__MASK 0x0FFFFFFF +#define SURFACE5_UPPER_BOUND__SURF_UPPER__SHIFT 0 +#define SURFACE6_INFO 0x00000B6C +#define SURFACE6_INFO__SURF6_PITCHSEL__MASK 0x000003FF +#define SURFACE6_INFO__SURF6_PITCHSEL__SHIFT 0 +#define SURFACE6_INFO__SURF6_TILE_MODE__MASK 0x00030000 +#define SURFACE6_INFO__SURF6_TILE_MODE__SHIFT 16 +#define SURFACE6_INFO__SURF6_AP0_SWP__MASK 0x00300000 +#define SURFACE6_INFO__SURF6_AP0_SWP__SHIFT 20 +#define SURFACE6_INFO__SURF6_AP1_SWP__MASK 0x00C00000 +#define SURFACE6_INFO__SURF6_AP1_SWP__SHIFT 22 +#define SURFACE6_INFO__SURF6_WRITE_FLAG 0x01000000 +#define SURFACE6_INFO__SURF6_READ_FLAG 0x02000000 +#define SURFACE6_INFO__SURF6_TILE_MODE_R2__MASK 0x00070000 +#define SURFACE6_INFO__SURF6_TILE_MODE_R2__SHIFT 16 +#define SURFACE6_INFO__SURF6_PITCHSEL_R3__MASK 0x00001FFF +#define SURFACE6_INFO__SURF6_PITCHSEL_R3__SHIFT 0 +#define SURFACE6_LOWER_BOUND 0x00000B64 +#define SURFACE6_LOWER_BOUND__SURF_LOWER__MASK 0x0FFFFFFF +#define SURFACE6_LOWER_BOUND__SURF_LOWER__SHIFT 0 +#define SURFACE6_UPPER_BOUND 0x00000B68 +#define SURFACE6_UPPER_BOUND__SURF_UPPER__MASK 0x0FFFFFFF +#define SURFACE6_UPPER_BOUND__SURF_UPPER__SHIFT 0 +#define SURFACE7_INFO 0x00000B7C +#define SURFACE7_INFO__SURF7_PITCHSEL__MASK 0x000003FF +#define SURFACE7_INFO__SURF7_PITCHSEL__SHIFT 0 +#define SURFACE7_INFO__SURF7_TILE_MODE__MASK 0x00030000 +#define SURFACE7_INFO__SURF7_TILE_MODE__SHIFT 16 +#define SURFACE7_INFO__SURF7_AP0_SWP__MASK 0x00300000 +#define SURFACE7_INFO__SURF7_AP0_SWP__SHIFT 20 +#define SURFACE7_INFO__SURF7_AP1_SWP__MASK 0x00C00000 +#define SURFACE7_INFO__SURF7_AP1_SWP__SHIFT 22 +#define SURFACE7_INFO__SURF7_WRITE_FLAG 0x01000000 +#define SURFACE7_INFO__SURF7_READ_FLAG 0x02000000 +#define SURFACE7_INFO__SURF7_TILE_MODE_R2__MASK 0x00070000 +#define SURFACE7_INFO__SURF7_TILE_MODE_R2__SHIFT 16 +#define SURFACE7_INFO__SURF7_PITCHSEL_R3__MASK 0x00001FFF +#define SURFACE7_INFO__SURF7_PITCHSEL_R3__SHIFT 0 +#define SURFACE7_LOWER_BOUND 0x00000B74 +#define SURFACE7_LOWER_BOUND__SURF_LOWER__MASK 0x0FFFFFFF +#define SURFACE7_LOWER_BOUND__SURF_LOWER__SHIFT 0 +#define SURFACE7_UPPER_BOUND 0x00000B78 +#define SURFACE7_UPPER_BOUND__SURF_UPPER__MASK 0x0FFFFFFF +#define SURFACE7_UPPER_BOUND__SURF_UPPER__SHIFT 0 +#define ISYNC_CNTL 0x00001724 +#define ISYNC_CNTL__ISYNC_ANY2D_IDLE3D 0x00000001 +#define ISYNC_CNTL__ISYNC_ANY3D_IDLE2D 0x00000002 +#define ISYNC_CNTL__ISYNC_TRIG2D_IDLE3D 0x00000004 +#define ISYNC_CNTL__ISYNC_TRIG3D_IDLE2D 0x00000008 +#define ISYNC_CNTL__ISYNC_WAIT_IDLEGUI 0x00000010 +#define ISYNC_CNTL__ISYNC_CPSCRATCH_IDLEGUI 0x00000020 +#define RBBM_STATUS 0x00000E40 +#define RBBM_STATUS__CMDFIFO_AVAIL__MASK 0x0000007F +#define RBBM_STATUS__CMDFIFO_AVAIL__SHIFT 0 +#define RBBM_STATUS__HIRQ_ON_RBB 0x00000100 +#define RBBM_STATUS__CPRQ_ON_RBB 0x00000200 +#define RBBM_STATUS__CFRQ_ON_RBB 0x00000400 +#define RBBM_STATUS__HIRQ_IN_RTBUF 0x00000800 +#define RBBM_STATUS__CPRQ_IN_RTBUF 0x00001000 +#define RBBM_STATUS__CFRQ_IN_RTBUF 0x00002000 +#define RBBM_STATUS__CF_PIPE_BUSY 0x00004000 +#define RBBM_STATUS__ENG_EV_BUSY 0x00008000 +#define RBBM_STATUS__CP_CMDSTRM_BUSY 0x00010000 +#define RBBM_STATUS__E2_BUSY 0x00020000 +#define RBBM_STATUS__RB2D_BUSY 0x00040000 +#define RBBM_STATUS__RB3D_BUSY 0x00080000 +#define RBBM_STATUS__SE_BUSY 0x00100000 +#define RBBM_STATUS__RE_BUSY 0x00200000 +#define RBBM_STATUS__TAM_BUSY 0x00400000 +#define RBBM_STATUS__TDM_BUSY 0x00800000 +#define RBBM_STATUS__PB_BUSY 0x01000000 +#define RBBM_STATUS__GUI_ACTIVE 0x80000000 +#define RBBM_STATUS__VAP_BUSY 0x00100000 +#define RBBM_STATUS__TIM_BUSY 0x02000000 +#define RBBM_STATUS__GA_BUSY 0x04000000 +#define RBBM_STATUS__CBA2D_BUSY 0x08000000 +#define RBBM_SOFT_RESET 0x000000F0 +#define RBBM_SOFT_RESET__SOFT_RESET_CP 0x00000001 +#define RBBM_SOFT_RESET__SOFT_RESET_HI 0x00000002 +#define RBBM_SOFT_RESET__SOFT_RESET_SE 0x00000004 +#define RBBM_SOFT_RESET__SOFT_RESET_RE 0x00000008 +#define RBBM_SOFT_RESET__SOFT_RESET_PP 0x00000010 +#define RBBM_SOFT_RESET__SOFT_RESET_E2 0x00000020 +#define RBBM_SOFT_RESET__SOFT_RESET_RB 0x00000040 +#define RBBM_SOFT_RESET__SOFT_RESET_HDP 0x00000080 +#define RBBM_SOFT_RESET__SOFT_RESET_MC 0x00000100 +#define RBBM_SOFT_RESET__SOFT_RESET_AIC 0x00000200 +#define RBBM_SOFT_RESET__SOFT_RESET_VIP 0x00000400 +#define RBBM_SOFT_RESET__SOFT_RESET_DISP 0x00000800 +#define RBBM_SOFT_RESET__SOFT_RESET_CG 0x00001000 +#define RBBM_SOFT_RESET__SOFT_RESET_VAP 0x00000004 +#define RBBM_SOFT_RESET__SOFT_RESET_GA 0x00002000 +#define RBBM_SOFT_RESET__SOFT_RESET_IDCT 0x00004000 +#define WAIT_UNTIL 0x00001720 +#define WAIT_UNTIL__WAIT_CRTC_PFLIP 0x00000001 +#define WAIT_UNTIL__WAIT_RE_CRTC_VLINE 0x00000002 +#define WAIT_UNTIL__WAIT_FE_CRTC_VLINE 0x00000004 +#define WAIT_UNTIL__WAIT_CRTC_VLINE 0x00000008 +#define WAIT_UNTIL__WAIT_DMA_VIPH0_IDLE 0x00000010 +#define WAIT_UNTIL__WAIT_DMA_VIPH1_IDLE 0x00000020 +#define WAIT_UNTIL__WAIT_DMA_VIPH2_IDLE 0x00000040 +#define WAIT_UNTIL__WAIT_DMA_VIPH3_IDLE 0x00000080 +#define WAIT_UNTIL__WAIT_DMA_VID_IDLE 0x00000100 +#define WAIT_UNTIL__WAIT_DMA_GUI_IDLE 0x00000200 +#define WAIT_UNTIL__WAIT_CMDFIFO 0x00000400 +#define WAIT_UNTIL__WAIT_OV0_FLIP 0x00000800 +#define WAIT_UNTIL__WAIT_OV0_SLICEDONE 0x00001000 +#define WAIT_UNTIL__WAIT_2D_IDLE 0x00004000 +#define WAIT_UNTIL__WAIT_3D_IDLE 0x00008000 +#define WAIT_UNTIL__WAIT_2D_IDLECLEAN 0x00010000 +#define WAIT_UNTIL__WAIT_3D_IDLECLEAN 0x00020000 +#define WAIT_UNTIL__WAIT_HOST_IDLECLEAN 0x00040000 +#define WAIT_UNTIL__WAIT_EXTERN_SIG 0x00080000 +#define WAIT_UNTIL__CMDFIFO_ENTRIES__MASK 0x07F00000 +#define WAIT_UNTIL__CMDFIFO_ENTRIES__SHIFT 20 +#define WAIT_UNTIL__WAIT_BOTH_CRTC_PFLIP 0x40000000 +#define WAIT_UNTIL__ENG_DISPLAY_SELECT 0x80000000 +#define WAIT_UNTIL__WAIT_AGP_FLUSH 0x00002000 +#define WAIT_UNTIL__WAIT_IDCT_SEMAPHORE 0x08000000 +#define WAIT_UNTIL__WAIT_VAP_IDLE 0x10000000 +#define DISPLAY_BASE_ADDR 0x0000023C +#define DISPLAY_BASE_ADDR__DISPLAY_BASE_ADDR__MASK 0xFFFFFFFF +#define DISPLAY_BASE_ADDR__DISPLAY_BASE_ADDR__SHIFT 0 +#define CRTC2_DISPLAY_BASE_ADDR 0x0000033C +#define CRTC2_DISPLAY_BASE_ADDR__CRTC2_DISPLAY_BASE_ADDR__MASK 0xFFFFFFFF +#define CRTC2_DISPLAY_BASE_ADDR__CRTC2_DISPLAY_BASE_ADDR__SHIFT 0 +#define AIC_CTRL 0x000001D0 +#define AIC_CTRL__TRANSLATE_EN 0x00000001 +#define AIC_CTRL__HW_0_DEBUG 0x00000002 +#define AIC_CTRL__HW_1_DEBUG 0x00000004 +#define AIC_CTRL__HW_2_DEBUG 0x00000008 +#define AIC_CTRL__HW_3_DEBUG 0x00000010 +#define AIC_CTRL__HW_4_DEBUG 0x00000020 +#define AIC_CTRL__HW_5_DEBUG 0x00000040 +#define AIC_CTRL__HW_6_DEBUG 0x00000080 +#define AIC_CTRL__HW_7_DEBUG 0x00000100 +#define AIC_CTRL__HW_8_DEBUG 0x00000200 +#define AIC_CTRL__HW_9_DEBUG 0x00000400 +#define AIC_CTRL__HW_A_DEBUG 0x00000800 +#define AIC_CTRL__HW_B_DEBUG 0x00001000 +#define AIC_CTRL__HW_C_DEBUG 0x00002000 +#define AIC_CTRL__HW_D_DEBUG 0x00004000 +#define AIC_CTRL__HW_E_DEBUG 0x00008000 +#define AIC_CTRL__HW_F_DEBUG 0x00010000 +#define AIC_CTRL__HW_10_DEBUG 0x00020000 +#define AIC_CTRL__HW_11_DEBUG 0x00040000 +#define AIC_CTRL__HW_12_DEBUG 0x00080000 +#define AIC_CTRL__HW_13_DEBUG 0x00100000 +#define AIC_CTRL__HW_14_DEBUG 0x00200000 +#define AIC_CTRL__HW_15_DEBUG 0x00400000 +#define AIC_CTRL__HW_16_DEBUG 0x00800000 +#define AIC_CTRL__HW_17_DEBUG 0x01000000 +#define AIC_CTRL__HW_18_DEBUG 0x02000000 +#define AIC_CTRL__HW_19_DEBUG 0x04000000 +#define AIC_CTRL__HW_1A_DEBUG 0x08000000 +#define AIC_CTRL__HW_1B_DEBUG 0x10000000 +#define AIC_CTRL__HW_1C_DEBUG 0x20000000 +#define AIC_CTRL__HW_1D_DEBUG 0x40000000 +#define AIC_CTRL__HW_1E_DEBUG 0x80000000 +#define AIC_CTRL__DIS_OUT_OF_PCI_GART_ACCESS 0x00000002 +#define AIC_CTRL__HW_02_DEBUG 0x00000004 +#define AIC_CTRL__HW_03_DEBUG 0x00000008 +#define AIC_CTRL__TEST_RBF_DIV_VAL__MASK 0x00000070 +#define AIC_CTRL__TEST_RBF_DIV_VAL__SHIFT 4 +#define AIC_CTRL__TEST_RBF_EN 0x00000080 +#define AIC_CTRL__HW_08_DEBUG 0x00000100 +#define AIC_CTRL__HW_09_DEBUG 0x00000200 +#define AIC_CTRL__HW_10_DEBUG_R3 0x00000400 +#define AIC_CTRL__HW_11_DEBUG_R3 0x00000800 +#define AIC_CTRL__HW_12_DEBUG_R3 0x00001000 +#define AIC_CTRL__HW_13_DEBUG_R3 0x00002000 +#define AIC_CTRL__HW_14_DEBUG_R3 0x00004000 +#define AIC_CTRL__HW_15_DEBUG_R3 0x00008000 +#define AIC_CTRL__HW_16_DEBUG_R3 0x00010000 +#define AIC_CTRL__HW_17_DEBUG_R3 0x00020000 +#define AIC_CTRL__HW_18_DEBUG_R3 0x00040000 +#define AIC_CTRL__HW_19_DEBUG_R3 0x00080000 +#define AIC_CTRL__HW_20_DEBUG 0x00100000 +#define AIC_CTRL__HW_21_DEBUG 0x00200000 +#define AIC_CTRL__HW_22_DEBUG 0x00400000 +#define AIC_CTRL__HW_23_DEBUG 0x00800000 +#define AIC_CTRL__HW_24_DEBUG 0x01000000 +#define AIC_CTRL__HW_25_DEBUG 0x02000000 +#define AIC_CTRL__HW_26_DEBUG 0x04000000 +#define AIC_CTRL__HW_27_DEBUG 0x08000000 +#define AIC_CTRL__HW_28_DEBUG 0x10000000 +#define AIC_CTRL__HW_29_DEBUG 0x20000000 +#define AIC_CTRL__HW_30_DEBUG 0x40000000 +#define AIC_CTRL__HW_31_DEBUG 0x80000000 +#define BUS_CNTL 0x00000030 +#define BUS_CNTL__BUS_DBL_RESYNC 0x00000001 +#define BUS_CNTL__BUS_MSTR_RESET 0x00000002 +#define BUS_CNTL__BUS_FLUSH_BUF 0x00000004 +#define BUS_CNTL__BUS_STOP_REQ_DIS 0x00000008 +#define BUS_CNTL__BUS_READ_COMBINE_EN 0x00000010 +#define BUS_CNTL__BUS_WRT_COMBINE_EN 0x00000020 +#define BUS_CNTL__BUS_MASTER_DIS 0x00000040 +#define BUS_CNTL__BIOS_ROM_WRT_EN 0x00000080 +#define BUS_CNTL__BUS_PREFETCH_MODE__MASK 0x00000300 +#define BUS_CNTL__BUS_PREFETCH_MODE__SHIFT 8 +#define BUS_CNTL__BUS_VGA_PREFETCH_EN 0x00000400 +#define BUS_CNTL__BUS_SGL_READ_DISABLE 0x00000800 +#define BUS_CNTL__BIOS_DIS_ROM 0x00001000 +#define BUS_CNTL__BUS_PCI_READ_RETRY_EN 0x00002000 +#define BUS_CNTL__BUS_AGP_AD_STEPPING_EN 0x00004000 +#define BUS_CNTL__BUS_PCI_WRT_RETRY_EN 0x00008000 +#define BUS_CNTL__BUS_RETRY_WS__MASK 0x000F0000 +#define BUS_CNTL__BUS_RETRY_WS__SHIFT 16 +#define BUS_CNTL__BUS_MSTR_RD_MULT 0x00100000 +#define BUS_CNTL__BUS_MSTR_RD_LINE 0x00200000 +#define BUS_CNTL__BUS_SUSPEND 0x00400000 +#define BUS_CNTL__LAT_16X 0x00800000 +#define BUS_CNTL__BUS_RD_DISCARD_EN 0x01000000 +#define BUS_CNTL__ENFRCWRDY 0x02000000 +#define BUS_CNTL__BUS_MSTR_WS 0x04000000 +#define BUS_CNTL__BUS_PARKING_DIS 0x08000000 +#define BUS_CNTL__BUS_MSTR_DISCONNECT_EN 0x10000000 +#define BUS_CNTL__SERR_EN 0x20000000 +#define BUS_CNTL__BUS_READ_BURST 0x40000000 +#define BUS_CNTL__BUS_RDY_READ_DLY 0x80000000 +#define BUS_CNTL__BUS_PM4_READ_COMBINE_EN 0x00000010 +#define BUS_CNTL__BM_DAC_CRIPPLE 0x00000100 +#define BUS_CNTL__BUS_NON_PM4_READ_COMBINE_EN 0x00000200 +#define BUS_CNTL__BUS_XFERD_DISCARD_EN 0x00000400 +#define MC_STATUS 0x00000150 +#define MC_STATUS__MEM_PWRUP_COMPL_A 0x00000001 +#define MC_STATUS__MEM_PWRUP_COMPL_B 0x00000002 +#define MC_STATUS__MC_IDLE 0x00000004 +#define MC_STATUS__SPARE__MASK 0x0000FFF8 +#define MC_STATUS__SPARE__SHIFT 3 +#define MC_STATUS__IMP_N_VALUE_R_BACK__MASK 0x00000078 +#define MC_STATUS__IMP_N_VALUE_R_BACK__SHIFT 3 +#define MC_STATUS__IMP_P_VALUE_R_BACK__MASK 0x00000780 +#define MC_STATUS__IMP_P_VALUE_R_BACK__SHIFT 7 +#define MC_STATUS__TEST_OUT_R_BACK 0x00000800 +#define MC_STATUS__DUMMY_OUT_R_BACK 0x00001000 +#define MC_STATUS__IMP_N_VALUE_A_R_BACK__MASK 0x0001E000 +#define MC_STATUS__IMP_N_VALUE_A_R_BACK__SHIFT 13 +#define MC_STATUS__IMP_P_VALUE_A_R_BACK__MASK 0x001E0000 +#define MC_STATUS__IMP_P_VALUE_A_R_BACK__SHIFT 17 +#define MC_STATUS__IMP_N_VALUE_CK_R_BACK__MASK 0x01E00000 +#define MC_STATUS__IMP_N_VALUE_CK_R_BACK__SHIFT 21 +#define MC_STATUS__IMP_P_VALUE_CK_R_BACK__MASK 0x1E000000 +#define MC_STATUS__IMP_P_VALUE_CK_R_BACK__SHIFT 25 +#define MC_STATUS__MEM_PWRUP_COMPL_C 0x00000004 +#define MC_STATUS__MEM_PWRUP_COMPL_D 0x00000008 +#define MC_STATUS__MC_IDLE_R3 0x00000010 +#define MC_STATUS__IMP_CAL_COUNT__MASK 0x0000F000 +#define MC_STATUS__IMP_CAL_COUNT__SHIFT 12 +#define OV0_SCALE_CNTL 0x00000420 +#define OV0_SCALE_CNTL__OV0_NO_READ_BEHIND_SCAN 0x00000002 +#define OV0_SCALE_CNTL__OV0_HORZ_PICK_NEAREST 0x00000004 +#define OV0_SCALE_CNTL__OV0_VERT_PICK_NEAREST 0x00000008 +#define OV0_SCALE_CNTL__OV0_SIGNED_UV 0x00000010 +#define OV0_SCALE_CNTL__OV0_GAMMA_SEL__MASK 0x000000E0 +#define OV0_SCALE_CNTL__OV0_GAMMA_SEL__SHIFT 5 +#define OV0_SCALE_CNTL__OV0_SURFACE_FORMAT__MASK 0x00000F00 +#define OV0_SCALE_CNTL__OV0_SURFACE_FORMAT__SHIFT 8 +#define OV0_SURFACE_FORMAT__RESERVED0 0x0 +#define OV0_SURFACE_FORMAT__RESERVED1 0x100 +#define OV0_SURFACE_FORMAT__RESERVED2 0x200 +#define OV0_SURFACE_FORMAT__16BPP_ARGB 0x300 +#define OV0_SURFACE_FORMAT__16BPP_RGB 0x400 +#define OV0_SURFACE_FORMAT__RESERVED5 0x500 +#define OV0_SURFACE_FORMAT__32BPP_ARGB 0x600 +#define OV0_SURFACE_FORMAT__RESERVED7 0x700 +#define OV0_SURFACE_FORMAT__RESERVED8 0x800 +#define OV0_SURFACE_FORMAT__IF09_PLANAR 0x900 +#define OV0_SURFACE_FORMAT__YV12_PLANAR 0xA00 +#define OV0_SURFACE_FORMAT__YUY2_PACKED 0xB00 +#define OV0_SURFACE_FORMAT__UYVY_PACKED 0xC00 +#define OV0_SURFACE_FORMAT__YYUV9_PLANAR 0xD00 +#define OV0_SURFACE_FORMAT__YYUV12_PLANAR 0xE00 +#define OV0_SURFACE_FORMAT__RESERVED15 0xF00 +#define OV0_SCALE_CNTL__OV0_ADAPTIVE_DEINT 0x00001000 +#define OV0_SCALE_CNTL__OV0_CRTC_SEL 0x00004000 +#define OV0_SCALE_CNTL__OV0_BURST_PER_PLANE__MASK 0x007F0000 +#define OV0_SCALE_CNTL__OV0_BURST_PER_PLANE__SHIFT 16 +#define OV0_SCALE_CNTL__OV0_DOUBLE_BUFFER_REGS 0x01000000 +#define OV0_SCALE_CNTL__OV0_BANDWIDTH 0x04000000 +#define OV0_SCALE_CNTL__OV0_LIN_TRANS_BYPASS 0x10000000 +#define OV0_SCALE_CNTL__OV0_INT_EMU 0x20000000 +#define OV0_SCALE_CNTL__OV0_OVERLAY_EN__MASK 0x40000000 +#define OV0_SCALE_CNTL__OV0_OVERLAY_EN__SHIFT 30 +#define OV0_OVERLAY_EN__ENABLE 0x40000000 +#define OV0_SCALE_CNTL__OV0_SOFT_RESET__MASK 0x80000000 +#define OV0_SCALE_CNTL__OV0_SOFT_RESET__SHIFT 31 +#define OV0_SOFT_RESET__RESET 0x80000000 +#define OV0_SCALE_CNTL__OV0_TEMPORAL_DEINT 0x00002000 +#define OV0_SCALE_CNTL__OV0_PULLDOWN_ON_P1_ONLY 0x00008000 +#define OV0_SCALE_CNTL__OV0_FULL_BYPASS 0x00000020 +#define OV0_SCALE_CNTL__OV0_DYNAMIC_EXT 0x00000040 +#define OV0_SCALE_CNTL__OV0_RGB30_ON 0x00000080 +#define CRTC2_GEN_CNTL 0x000003F8 +#define CRTC2_GEN_CNTL__CRTC2_DBL_SCAN_EN 0x00000001 +#define CRTC2_GEN_CNTL__CRTC2_INTERLACE_EN 0x00000002 +#define CRTC2_GEN_CNTL__CRTC2_SYNC_TRISTATE 0x00000010 +#define CRTC2_GEN_CNTL__CRTC2_HSYNC_TRISTATE 0x00000020 +#define CRTC2_GEN_CNTL__CRTC2_VSYNC_TRISTATE 0x00000040 +#define CRTC2_GEN_CNTL__CRT2_ON 0x00000080 +#define CRTC2_GEN_CNTL__CRTC2_PIX_WIDTH__MASK 0x00000F00 +#define CRTC2_GEN_CNTL__CRTC2_PIX_WIDTH__SHIFT 8 +#define CRTC2_GEN_CNTL__CRTC2_ICON_EN 0x00008000 +#define CRTC2_GEN_CNTL__CRTC2_CUR_EN 0x00010000 +#define CRTC2_GEN_CNTL__CRTC2_CUR_MODE__MASK 0x00700000 +#define CRTC2_GEN_CNTL__CRTC2_CUR_MODE__SHIFT 20 +#define CRTC2_GEN_CNTL__CRTC2_DISPLAY_DIS 0x00800000 +#define CRTC2_GEN_CNTL__CRTC2_EN 0x02000000 +#define CRTC2_GEN_CNTL__CRTC2_DISP_REQ_EN_B 0x04000000 +#define CRTC2_GEN_CNTL__CRTC2_C_SYNC_EN 0x08000000 +#define CRTC2_GEN_CNTL__CRTC2_HSYNC_DIS 0x10000000 +#define CRTC2_GEN_CNTL__CRTC2_VSYNC_DIS 0x20000000 +#define CRTC2_GEN_CNTL__CRTC2_MODE9_COLOR_ORDER 0x00001000 +#define CRTC2_GEN_CNTL__CRTC2_FIX_VSYNC_EDGE_POSITION_EN 0x40000000 +#define CRTC2_OFFSET 0x00000324 +#define CRTC2_OFFSET__CRTC2_OFFSET__MASK 0x07FFFFFF +#define CRTC2_OFFSET__CRTC2_OFFSET__SHIFT 0 +#define CRTC2_OFFSET__CRTC2_GUI_TRIG_OFFSET 0x40000000 +#define CRTC2_OFFSET__CRTC2_OFFSET_LOCK 0x80000000 +#define CRTC2_OFFSET__CRTC2_OFFSET_R3__MASK 0x0FFFFFFF +#define CRTC2_OFFSET__CRTC2_OFFSET_R3__SHIFT 0 +#define CRTC2_OFFSET_CNTL 0x00000328 +#define CRTC2_OFFSET_CNTL__CRTC2_TILE_LINE__MASK 0x0000000F +#define CRTC2_OFFSET_CNTL__CRTC2_TILE_LINE__SHIFT 0 +#define CRTC2_OFFSET_CNTL__CRTC2_TILE_EN 0x00008000 +#define CRTC2_OFFSET_CNTL__CRTC2_OFFSET_FLIP_CNTL 0x00010000 +#define CRTC2_OFFSET_CNTL__CRTC2_GUI_TRIG_OFFSET_LEFT_EN 0x10000000 +#define CRTC2_OFFSET_CNTL__CRTC2_GUI_TRIG_OFFSET 0x40000000 +#define CRTC2_OFFSET_CNTL__CRTC2_OFFSET_LOCK 0x80000000 +#define CRTC2_OFFSET_CNTL__CRTC2_TILE_LINE_RIGHT__MASK 0x000000F0 +#define CRTC2_OFFSET_CNTL__CRTC2_TILE_LINE_RIGHT__SHIFT 4 +#define CRTC2_OFFSET_CNTL__CRTC2_TILE_EN_RIGHT 0x00004000 +#define CRTC2_OFFSET_CNTL__CRTC2_STEREO_OFFSET_EN 0x00020000 +#define CRTC2_OFFSET_CNTL__CRTC2_STEREO_SYNC_EN__MASK 0x000C0000 +#define CRTC2_OFFSET_CNTL__CRTC2_STEREO_SYNC_EN__SHIFT 18 +#define CRTC2_OFFSET_CNTL__CRTC2_STEREO_SYNC 0x00200000 +#define CRTC2_OFFSET_CNTL__CRTC2_GUI_TRIG_OFFSET_RIGHT_EN 0x20000000 +#define CRTC2_OFFSET_CNTL__CRTC2_X_Y_MODE_EN_RIGHT 0x00000100 +#define CRTC2_OFFSET_CNTL__CRTC2_X_Y_MODE_EN 0x00000200 +#define CRTC2_OFFSET_CNTL__CRTC2_MICRO_TILE_EN_RIGHT 0x00001000 +#define CRTC2_OFFSET_CNTL__CRTC2_MICRO_TILE_EN 0x00002000 +#define CRTC2_OFFSET_CNTL__CRTC2_MACRO_TILE_EN_RIGHT 0x00004000 +#define CRTC2_OFFSET_CNTL__CRTC2_MACRO_TILE_EN 0x00008000 +#define CUR_OFFSET 0x00000260 +#define CUR_OFFSET__CUR_OFFSET__MASK 0x07FFFFFF +#define CUR_OFFSET__CUR_OFFSET__SHIFT 0 +#define CUR_OFFSET__CUR_LOCK 0x80000000 +#define CUR2_OFFSET 0x00000360 +#define CUR2_OFFSET__CUR2_OFFSET__MASK 0x07FFFFFF +#define CUR2_OFFSET__CUR2_OFFSET__SHIFT 0 +#define CUR2_OFFSET__CUR2_LOCK 0x80000000 +#define HOST_PATH_CNTL 0x00000130 +#define HOST_PATH_CNTL__HDP_APER_CNTL 0x00800000 +#define HOST_PATH_CNTL__HP_LIN_RD_CACHE_DIS 0x01000000 +#define HOST_PATH_CNTL__HP_RBBM_LOCK_DIS 0x02000000 +#define HOST_PATH_CNTL__HDP_SOFT_RESET 0x04000000 +#define HOST_PATH_CNTL__HDP_WRITE_COMBINER_TIMEOUT__MASK 0x70000000 +#define HOST_PATH_CNTL__HDP_WRITE_COMBINER_TIMEOUT__SHIFT 28 +#define HOST_PATH_CNTL__HP_TEST_RST_CNTL 0x80000000 +#define HOST_PATH_CNTL__HDP_WRITE_THROUGH_CACHE_DIS 0x00400000 +#define HOST_PATH_CNTL__HDP_READ_BUFFER_INVALIDATE 0x08000000 +#define DST_PITCH_OFFSET 0x0000142C +#define DST_PITCH_OFFSET__DST_OFFSET__MASK 0x003FFFFF +#define DST_PITCH_OFFSET__DST_OFFSET__SHIFT 0 +#define DST_PITCH_OFFSET__DST_PITCH__MASK 0x3FC00000 +#define DST_PITCH_OFFSET__DST_PITCH__SHIFT 22 +#define DST_PITCH_OFFSET__DST_TILE__MASK 0xC0000000 +#define DST_PITCH_OFFSET__DST_TILE__SHIFT 30 +#define DST_TILE__MACRO 0x1 +#define DST_TILE__MICRO 0x2 +#define SRC_PITCH_OFFSET 0x00001428 +#define SRC_PITCH_OFFSET__SRC_OFFSET__MASK 0x003FFFFF +#define SRC_PITCH_OFFSET__SRC_OFFSET__SHIFT 0 +#define SRC_PITCH_OFFSET__SRC_PITCH__MASK 0x3FC00000 +#define SRC_PITCH_OFFSET__SRC_PITCH__SHIFT 22 +#define SRC_PITCH_OFFSET__SRC_TILE 0x40000000 +#define DEFAULT_SC_BOTTOM_RIGHT 0x000016E8 +#define DEFAULT_SC_BOTTOM_RIGHT__DEFAULT_SC_RIGHT__MASK 0x00003FFF +#define DEFAULT_SC_BOTTOM_RIGHT__DEFAULT_SC_RIGHT__SHIFT 0 +#define DEFAULT_SC_BOTTOM_RIGHT__DEFAULT_SC_BOTTOM__MASK 0x3FFF0000 +#define DEFAULT_SC_BOTTOM_RIGHT__DEFAULT_SC_BOTTOM__SHIFT 16 +#define DEFAULT2_SC_BOTTOM_RIGHT 0x000016DC +#define DEFAULT2_SC_BOTTOM_RIGHT__DEFAULT_SC_RIGHT__MASK 0x00003FFF +#define DEFAULT2_SC_BOTTOM_RIGHT__DEFAULT_SC_RIGHT__SHIFT 0 +#define DEFAULT2_SC_BOTTOM_RIGHT__DEFAULT_SC_BOTTOM__MASK 0x3FFF0000 +#define DEFAULT2_SC_BOTTOM_RIGHT__DEFAULT_SC_BOTTOM__SHIFT 16 +#define DP_DATATYPE 0x000016C4 +#define DP_DATATYPE__DP_DST_DATATYPE__MASK 0x0000000F +#define DP_DATATYPE__DP_DST_DATATYPE__SHIFT 0 +#define DP_DATATYPE__DP_BRUSH_DATATYPE__MASK 0x00000F00 +#define DP_DATATYPE__DP_BRUSH_DATATYPE__SHIFT 8 +#define DP_DATATYPE__DP_SRC_DATATYPE__MASK 0x00070000 +#define DP_DATATYPE__DP_SRC_DATATYPE__SHIFT 16 +#define DP_DATATYPE__DP_BYTE_PIX_ORDER 0x40000000 +#define DP_GUI_MASTER_CNTL 0x0000146C +#define DP_GUI_MASTER_CNTL__GMC_SRC_PITCH_OFFSET_CNTL 0x00000001 +#define DP_GUI_MASTER_CNTL__GMC_DST_PITCH_OFFSET_CNTL 0x00000002 +#define DP_GUI_MASTER_CNTL__GMC_SRC_CLIPPING 0x00000004 +#define DP_GUI_MASTER_CNTL__GMC_DST_CLIPPING 0x00000008 +#define DP_GUI_MASTER_CNTL__GMC_BRUSH_DATATYPE__MASK 0x000000F0 +#define DP_GUI_MASTER_CNTL__GMC_BRUSH_DATATYPE__SHIFT 4 +#define GMC_BRUSH_DATATYPE__8X8_MONO_FG_BG 0x0 +#define GMC_BRUSH_DATATYPE__8X8_MONO_FG 0x1 +#define GMC_BRUSH_DATATYPE__32X1_MONO_LINE_FG_BG 0x6 +#define GMC_BRUSH_DATATYPE__32X1_MONO_LINE_FG 0x7 +#define GMC_BRUSH_DATATYPE__8X8_COLOR 0xA +#define GMC_BRUSH_DATATYPE__SOLID_COLOR_FG 0xD +#define GMC_BRUSH_DATATYPE__SOLID_COLOR_RESERVED 0xF +#define GMC_BRUSH_DATATYPE__SOLID 0xD0 +#define GMC_BRUSH_DATATYPE__MONO8x8 0x0 +#define GMC_BRUSH_DATATYPE__COLOR8x8 0xA0 +#define DP_GUI_MASTER_CNTL__GMC_DST_DATATYPE__MASK 0x00000F00 +#define DP_GUI_MASTER_CNTL__GMC_DST_DATATYPE__SHIFT 8 +#define GMC_DST_DATATYPE__8BPP_CLUT 0x2 +#define GMC_DST_DATATYPE__16BPP_1555 0x3 +#define GMC_DST_DATATYPE__16BPP_565 0x4 +#define GMC_DST_DATATYPE__32BPP_8888 0x6 +#define GMC_DST_DATATYPE__CI8 0x200 +#define GMC_DST_DATATYPE__RGB16_1555 0x300 +#define GMC_DST_DATATYPE__RGB16_565 0x400 +#define GMC_DST_DATATYPE__RGB32 0x600 +#define DP_GUI_MASTER_CNTL__GMC_SRC_DATATYPE__MASK 0x00003000 +#define DP_GUI_MASTER_CNTL__GMC_SRC_DATATYPE__SHIFT 12 +#define GMC_SRC_DATATYPE__BUILD(x) 0x0 +#define GMC_SRC_DATATYPE__MONO_OPAQUE 0x0 +#define GMC_SRC_DATATYPE__MONO_TRANSPARENT 0x0 +#define GMC_SRC_DATATYPE__SAME_AS_DST 0x0 +#define GMC_SRC_DATATYPE__8BPP_CLUT_XLAT 0x0 +#define GMC_SRC_DATATYPE__32BPP_CLUT_XLAT 0x0 +#define GMC_SRC_DATATYPE__MONO_FG_BG 0x0 +#define GMC_SRC_DATATYPE__MONO_FG 0x1000 +#define GMC_SRC_DATATYPE__COLOR 0x3000 +#define GMC_SRC_DATATYPE__DST 0x3000 +#define DP_GUI_MASTER_CNTL__GMC_BYTE_PIX_ORDER 0x00004000 +#define DP_GUI_MASTER_CNTL__GMC_DEFAULT_SEL 0x00008000 +#define DP_GUI_MASTER_CNTL__GMC_ROP3__MASK 0x00FF0000 +#define DP_GUI_MASTER_CNTL__GMC_ROP3__SHIFT 16 +#define GMC_ROP3__SRCCPY 0xCC +#define GMC_ROP3__WHITENESS 0xFF +#define GMC_ROP3__BLACKNESS 0x0 +#define DP_GUI_MASTER_CNTL__GMC_DP_SRC_SOURCE__MASK 0x07000000 +#define DP_GUI_MASTER_CNTL__GMC_DP_SRC_SOURCE__SHIFT 24 +#define GMC_DP_SRC_SOURCE__VIDEO_MEM 0x2 +#define GMC_DP_SRC_SOURCE__HOSTDATA 0x3 +#define GMC_DP_SRC_SOURCE__HOSTDATA_BYTE 0x4 +#define DP_GUI_MASTER_CNTL__GMC_SRC_DATATYPE2 0x08000000 +#define DP_GUI_MASTER_CNTL__GMC_CLR_CMP_FCN_DIS 0x10000000 +#define DP_GUI_MASTER_CNTL__GMC_WR_MSK_DIS 0x40000000 +#define DP_BRUSH_FRGD_CLR 0x0000147C +#define DP_BRUSH_FRGD_CLR__DP_BRUSH_FRGD_CLR__MASK 0xFFFFFFFF +#define DP_BRUSH_FRGD_CLR__DP_BRUSH_FRGD_CLR__SHIFT 0 +#define DP_BRUSH_BKGD_CLR 0x00001478 +#define DP_BRUSH_BKGD_CLR__DP_BRUSH_BKGD_CLR__MASK 0xFFFFFFFF +#define DP_BRUSH_BKGD_CLR__DP_BRUSH_BKGD_CLR__SHIFT 0 +#define DP_SRC_FRGD_CLR 0x000015D8 +#define DP_SRC_FRGD_CLR__DP_SRC_FRGD_CLR__MASK 0xFFFFFFFF +#define DP_SRC_FRGD_CLR__DP_SRC_FRGD_CLR__SHIFT 0 +#define DP_SRC_BKGD_CLR 0x000015DC +#define DP_SRC_BKGD_CLR__DP_SRC_BKGD_CLR__MASK 0xFFFFFFFF +#define DP_SRC_BKGD_CLR__DP_SRC_BKGD_CLR__SHIFT 0 +#define DP_WRITE_MSK 0x000016CC +#define DP_WRITE_MSK__DP_WRITE_MSK__MASK 0xFFFFFFFF +#define DP_WRITE_MSK__DP_WRITE_MSK__SHIFT 0 + +#endif diff --git a/shared-core/radeon_ms_state.c b/shared-core/radeon_ms_state.c new file mode 100644 index 00000000..17f8b764 --- /dev/null +++ b/shared-core/radeon_ms_state.c @@ -0,0 +1,45 @@ +/* + * Copyright 2007 Jérôme Glisse + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Jerome Glisse <glisse@freedesktop.org> + */ +#include "drmP.h" +#include "drm.h" +#include "radeon_ms.h" + +void radeon_ms_state_restore(struct drm_device *dev, struct radeon_state *state) +{ + radeon_ms_irq_restore(dev, state); + radeon_ms_gpu_restore(dev, state); + radeon_ms_cp_restore(dev, state); + radeon_ms_crtc1_restore(dev, state); +} + +void radeon_ms_state_save(struct drm_device *dev, struct radeon_state *state) +{ + radeon_ms_crtc1_save(dev, state); + radeon_ms_cp_save(dev, state); + radeon_ms_gpu_save(dev, state); + radeon_ms_irq_save(dev, state); +} diff --git a/tests/mode/Makefile b/tests/mode/Makefile new file mode 100644 index 00000000..a3d3b49a --- /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 $(CFLAGS) -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..f1fe64df --- /dev/null +++ b/tests/mode/modetest.c @@ -0,0 +1,373 @@ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <unistd.h> +#include <string.h> + +#include "xf86drm.h" +#include "xf86drmMode.h" + +int dpms_prop_id = 0; +const char* getConnectionText(drmModeConnection conn) +{ + switch (conn) { + case DRM_MODE_CONNECTED: + return "connected"; + case DRM_MODE_DISCONNECTED: + return "disconnected"; + default: + return "unknown"; + } + +} + +int printMode(struct drm_mode_modeinfo *mode) +{ +#if 1 + 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: \"%s\" %ix%i %.0f\n", 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, j; + struct drm_mode_modeinfo *mode = NULL; + drmModePropertyPtr props; + unsigned char *name = 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); + printf("\tcount_props : %i\n", output->count_props); + + for (i = 0; i < output->count_props; i++) { + props = drmModeGetProperty(fd, output->props[i]); + name = NULL; + if (props) { + printf("Property: %s\n", props->name); + printf("\tid: %i\n", props->prop_id); + printf("\tflags: %i\n", props->flags); + printf("\tvalues %d: ", props->count_values); + for (j = 0; j < props->count_values; j++) + printf("%lld ", props->values[j]); + + printf("\n\tenums %d: \n", props->count_enums); + + if (props->flags & DRM_MODE_PROP_BLOB) { + drmModePropertyBlobPtr blob; + + blob = drmModeGetPropertyBlob(fd, output->prop_values[i]); + if (blob) { + printf("blob is %d length, %08X\n", blob->length, *(uint32_t *)blob->data); + drmModeFreePropertyBlob(blob); + } + + } else { + if (!strncmp(props->name, "DPMS", 4)) + dpms_prop_id = props->prop_id; + + for (j = 0; j < props->count_enums; j++) { + printf("\t\t%lld = %s\n", props->enums[j].value, props->enums[j].name); + if (output->prop_values[i] == props->enums[j].value) + name = props->enums[j].name; + + } + + if (props->count_enums && name) { + printf("\toutput property name %s %s\n", props->name, name); + } else { + printf("\toutput property id %s %lli\n", props->name, output->prop_values[i]); + } + } + + drmModeFreeProperty(props); + } + } + + for (i = 0; i < output->count_modes; i++) { + mode = &output->modes[i]; + if (mode) + printMode(mode); + else + printf("\t\tmode: Invalid mode %p\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 : %p\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_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("\tAttaching mode %i to output %i\n", newMode, output); + + ret = drmModeAttachMode(fd, output, &mode); + + if (ret) + goto err_mode; + + printf("\tDetaching mode %i from output %i\n", newMode, output); + ret = drmModeDetachMode(fd, output, &mode); + + if (ret) + goto err_mode; + return 0; + +err_mode: + + 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 = drmBOUnreference(fb, &bo); + + return 0; + +err_bo: + drmBOUnreference(fd, &bo); + +err: + printf("\tFailed\n"); + + return 1; +} + +int testDPMS(int fd, drmModeResPtr res) +{ + int output_id; + int i; + + for (i = 0; i < res->count_outputs; i++) { + output_id = res->outputs[i]; + /* turn output off */ + drmModeOutputSetProperty(fd, output_id, dpms_prop_id, 3); + sleep(2); + drmModeOutputSetProperty(fd, output_id, dpms_prop_id, 0); + } + 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); + + /* try dpms test */ + testDPMS(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 |