diff options
244 files changed, 68471 insertions, 4978 deletions
diff --git a/configure.ac b/configure.ac index 0f7c79e6..531f3308 100644 --- a/configure.ac +++ b/configure.ac @@ -117,6 +117,7 @@ AC_OUTPUT([ Makefile libdrm/Makefile libdrm/intel/Makefile + libdrm/radeon/Makefile shared-core/Makefile tests/Makefile libdrm.pc]) diff --git a/libdrm/Makefile.am b/libdrm/Makefile.am index 63f6e644..ded86308 100644 --- a/libdrm/Makefile.am +++ b/libdrm/Makefile.am @@ -18,7 +18,7 @@ # 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. -SUBDIRS = . intel +SUBDIRS = . intel radeon libdrm_la_LTLIBRARIES = libdrm.la libdrm_ladir = $(libdir) @@ -26,9 +26,10 @@ 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 \ + xf86drmMode.c libdrm_lists.h libdrm_lists.h libdrmincludedir = ${includedir} -libdrminclude_HEADERS = xf86drm.h +libdrminclude_HEADERS = xf86drm.h xf86drmMode.h EXTRA_DIST = ChangeLog TODO diff --git a/libdrm/intel/intel_bufmgr.h b/libdrm/intel/intel_bufmgr.h index 361e4345..1f7f73a4 100644 --- a/libdrm/intel/intel_bufmgr.h +++ b/libdrm/intel/intel_bufmgr.h @@ -66,6 +66,11 @@ struct _drm_intel_bo { /** Buffer manager context associated with this buffer object */ drm_intel_bufmgr *bufmgr; + + /** + * MM-specific handle for accessing object + */ + int handle; }; drm_intel_bo *drm_intel_bo_alloc(drm_intel_bufmgr *bufmgr, const char *name, diff --git a/libdrm/intel/intel_bufmgr_fake.c b/libdrm/intel/intel_bufmgr_fake.c index 6d8ee85a..0e465303 100644 --- a/libdrm/intel/intel_bufmgr_fake.c +++ b/libdrm/intel/intel_bufmgr_fake.c @@ -833,7 +833,7 @@ drm_intel_bo_fake_alloc_static(drm_intel_bufmgr *bufmgr, const char *name, bo_fake->refcount = 1; bo_fake->id = ++bufmgr_fake->buf_nr; bo_fake->name = name; - bo_fake->flags = BM_PINNED | DRM_BO_FLAG_NO_MOVE; + bo_fake->flags = BM_PINNED; bo_fake->is_static = 1; DBG("drm_bo_alloc_static: (buf %d: %s, %d kb)\n", bo_fake->id, bo_fake->name, @@ -1070,7 +1070,6 @@ drm_intel_fake_kick_all_locked(drm_intel_bufmgr_fake *bufmgr_fake) if (!(bo_fake->flags & BM_NO_BACKING_STORE)) bo_fake->dirty = 1; } - } static int diff --git a/libdrm/intel/intel_bufmgr_gem.c b/libdrm/intel/intel_bufmgr_gem.c index be41474b..64d32d38 100644 --- a/libdrm/intel/intel_bufmgr_gem.c +++ b/libdrm/intel/intel_bufmgr_gem.c @@ -352,6 +352,7 @@ drm_intel_gem_bo_alloc(drm_intel_bufmgr *bufmgr, const char *name, ret = ioctl(bufmgr_gem->fd, DRM_IOCTL_I915_GEM_CREATE, &create); bo_gem->gem_handle = create.handle; + bo_gem->bo.handle = bo_gem->gem_handle; if (ret != 0) { free(bo_gem); return NULL; @@ -426,6 +427,14 @@ drm_intel_gem_bo_reference(drm_intel_bo *bo) } static void +dri_gem_bo_reference_locked(dri_bo *bo) +{ + drm_intel_bo_gem *bo_gem = (drm_intel_bo_gem *)bo; + + bo_gem->refcount++; +} + +static void drm_intel_gem_bo_reference_locked(drm_intel_bo *bo) { drm_intel_bo_gem *bo_gem = (drm_intel_bo_gem *)bo; diff --git a/libdrm/radeon/Makefile.am b/libdrm/radeon/Makefile.am new file mode 100644 index 00000000..39ee021f --- /dev/null +++ b/libdrm/radeon/Makefile.am @@ -0,0 +1,48 @@ +# Copyright © 2008 Jérôme Glisse +# +# 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: +# Jérôme Glisse <glisse@freedesktop.org> + +AM_CFLAGS = \ + $(WARN_CFLAGS) \ + -I$(top_srcdir)/libdrm \ + -I$(top_srcdir)/libdrm/radeon \ + $(PTHREADSTUBS_CFLAGS) \ + -I$(top_srcdir)/shared-core + +libdrm_radeon_la_LTLIBRARIES = libdrm-radeon.la +libdrm_radeon_ladir = $(libdir) +libdrm_radeon_la_LDFLAGS = -version-number 1:0:0 -no-undefined +libdrm_radeon_la_LIBADD = ../libdrm.la @PTHREADSTUBS_LIBS@ + +libdrm_radeon_la_SOURCES = \ + radeon_bo_gem.c \ + radeon_cs_gem.c \ + radeon_track.c + +libdrm_radeonincludedir = ${includedir}/drm +libdrm_radeoninclude_HEADERS = \ + radeon_bo.h \ + radeon_cs.h \ + radeon_bo_gem.h \ + radeon_cs_gem.h \ + radeon_track.h diff --git a/libdrm/radeon/radeon_bo.h b/libdrm/radeon/radeon_bo.h new file mode 100644 index 00000000..44dc0901 --- /dev/null +++ b/libdrm/radeon/radeon_bo.h @@ -0,0 +1,167 @@ +/* + * Copyright © 2008 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: + * Jérôme Glisse <glisse@freedesktop.org> + */ +#ifndef RADEON_BO_H +#define RADEON_BO_H + +#include <stdio.h> +#include <stdint.h> +#include "radeon_track.h" + +/* bo object */ +#define RADEON_BO_FLAGS_MACRO_TILE 1 +#define RADEON_BO_FLAGS_MICRO_TILE 2 + +struct radeon_bo_manager; + +struct radeon_bo { + uint32_t alignment; + uint32_t handle; + uint32_t size; + uint32_t domains; + uint32_t flags; + unsigned cref; +#ifdef RADEON_BO_TRACK + struct radeon_track *track; +#endif + void *ptr; + struct radeon_bo_manager *bom; +}; + +/* bo functions */ +struct radeon_bo_funcs { + struct radeon_bo *(*bo_open)(struct radeon_bo_manager *bom, + uint32_t handle, + uint32_t size, + uint32_t alignment, + uint32_t domains, + uint32_t flags); + void (*bo_ref)(struct radeon_bo *bo); + struct radeon_bo *(*bo_unref)(struct radeon_bo *bo); + int (*bo_map)(struct radeon_bo *bo, int write); + int (*bo_unmap)(struct radeon_bo *bo); +}; + +struct radeon_bo_manager { + struct radeon_bo_funcs *funcs; + int fd; + struct radeon_tracker tracker; +}; + +static inline void _radeon_bo_debug(struct radeon_bo *bo, + const char *op, + const char *file, + const char *func, + int line) +{ + fprintf(stderr, "%s %p 0x%08X 0x%08X 0x%08X [%s %s %d]\n", + op, bo, bo->handle, bo->size, bo->cref, file, func, line); +} + +static inline struct radeon_bo *_radeon_bo_open(struct radeon_bo_manager *bom, + uint32_t handle, + uint32_t size, + uint32_t alignment, + uint32_t domains, + uint32_t flags, + const char *file, + const char *func, + int line) +{ + struct radeon_bo *bo; + + bo = bom->funcs->bo_open(bom, handle, size, alignment, domains, flags); +#ifdef RADEON_BO_TRACK + if (bo) { + bo->track = radeon_tracker_add_track(&bom->tracker, bo->handle); + radeon_track_add_event(bo->track, file, func, "open", line); + } +#endif + return bo; +} + +static inline void _radeon_bo_ref(struct radeon_bo *bo, + const char *file, + const char *func, + int line) +{ + bo->cref++; +#ifdef RADEON_BO_TRACK + radeon_track_add_event(bo->track, file, func, "ref", line); +#endif + bo->bom->funcs->bo_ref(bo); +} + +static inline struct radeon_bo *_radeon_bo_unref(struct radeon_bo *bo, + const char *file, + const char *func, + int line) +{ + bo->cref--; +#ifdef RADEON_BO_TRACK + radeon_track_add_event(bo->track, file, func, "unref", line); + if (bo->cref <= 0) { + radeon_tracker_remove_track(&bo->bom->tracker, bo->track); + bo->track = NULL; + } +#endif + return bo->bom->funcs->bo_unref(bo); +} + +static inline int _radeon_bo_map(struct radeon_bo *bo, + int write, + const char *file, + const char *func, + int line) +{ + return bo->bom->funcs->bo_map(bo, write); +} + +static inline int _radeon_bo_unmap(struct radeon_bo *bo, + const char *file, + const char *func, + int line) +{ + return bo->bom->funcs->bo_unmap(bo); +} + +#define radeon_bo_open(bom, h, s, a, d, f)\ + _radeon_bo_open(bom, h, s, a, d, f, __FILE__, __FUNCTION__, __LINE__) +#define radeon_bo_ref(bo)\ + _radeon_bo_ref(bo, __FILE__, __FUNCTION__, __LINE__) +#define radeon_bo_unref(bo)\ + _radeon_bo_unref(bo, __FILE__, __FUNCTION__, __LINE__) +#define radeon_bo_map(bo, w)\ + _radeon_bo_map(bo, w, __FILE__, __FUNCTION__, __LINE__) +#define radeon_bo_unmap(bo)\ + _radeon_bo_unmap(bo, __FILE__, __FUNCTION__, __LINE__) +#define radeon_bo_debug(bo, opcode)\ + _radeon_bo_debug(bo, opcode, __FILE__, __FUNCTION__, __LINE__) + +#endif diff --git a/libdrm/radeon/radeon_bo_gem.c b/libdrm/radeon/radeon_bo_gem.c new file mode 100644 index 00000000..fdf852a2 --- /dev/null +++ b/libdrm/radeon/radeon_bo_gem.c @@ -0,0 +1,208 @@ +/* + * Copyright © 2008 Dave Airlie + * Copyright © 2008 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 + * Jérôme Glisse <glisse@freedesktop.org> + */ +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/ioctl.h> +#include "xf86drm.h" +#include "drm.h" +#include "radeon_drm.h" +#include "radeon_bo.h" +#include "radeon_bo_gem.h" + +struct radeon_bo_gem { + struct radeon_bo base; + uint32_t name; + int map_count; +}; + +struct bo_manager_gem { + struct radeon_bo_manager base; +}; + +static struct radeon_bo *bo_open(struct radeon_bo_manager *bom, + uint32_t handle, + uint32_t size, + uint32_t alignment, + uint32_t domains, + uint32_t flags) +{ + struct radeon_bo_gem *bo; + int r; + + bo = (struct radeon_bo_gem*)calloc(1, sizeof(struct radeon_bo_gem)); + if (bo == NULL) { + return NULL; + } + + bo->base.bom = bom; + bo->base.handle = 0; + bo->base.size = size; + bo->base.alignment = alignment; + bo->base.domains = domains; + bo->base.flags = flags; + bo->base.ptr = NULL; + bo->map_count = 0; + if (handle) { + struct drm_gem_open open_arg; + + memset(&open_arg, 0, sizeof(open_arg)); + open_arg.name = handle; + r = ioctl(bom->fd, DRM_IOCTL_GEM_OPEN, &open_arg); + if (r != 0) { + free(bo); + return NULL; + } + bo->base.handle = open_arg.handle; + bo->base.size = open_arg.size; + bo->name = handle; + } else { + struct drm_radeon_gem_create args; + + args.size = size; + args.alignment = alignment; + args.initial_domain = bo->base.domains; + args.no_backing_store = 0; + args.handle = 0; + r = drmCommandWriteRead(bom->fd, DRM_RADEON_GEM_CREATE, + &args, sizeof(args)); + bo->base.handle = args.handle; + if (r) { + fprintf(stderr, "Failed to allocate :\n"); + fprintf(stderr, " size : %d bytes\n", size); + fprintf(stderr, " alignment : %d bytes\n", alignment); + fprintf(stderr, " domains : %d\n", bo->base.domains); + free(bo); + return NULL; + } + } + radeon_bo_ref((struct radeon_bo*)bo); + return (struct radeon_bo*)bo; +} + +static void bo_ref(struct radeon_bo *bo) +{ +} + +static struct radeon_bo *bo_unref(struct radeon_bo *bo) +{ + struct radeon_bo_gem *bo_gem = (struct radeon_bo_gem*)bo; + struct drm_gem_close args; + + if (bo == NULL) { + return NULL; + } + if (bo->cref) { + return bo; + } + if (bo_gem->map_count) { + munmap(bo->ptr, bo->size); + } + + /* close object */ + args.handle = bo->handle; + ioctl(bo->bom->fd, DRM_IOCTL_GEM_CLOSE, &args); + memset(bo_gem, 0, sizeof(struct radeon_bo_gem)); + free(bo_gem); + return NULL; +} + +static int bo_map(struct radeon_bo *bo, int write) +{ + struct radeon_bo_gem *bo_gem = (struct radeon_bo_gem*)bo; + struct drm_radeon_gem_mmap args; + int r; + + if (bo_gem->map_count++ != 0) { + return 0; + } + bo->ptr = NULL; + args.handle = bo->handle; + args.offset = 0; + args.size = (uint64_t)bo->size; + r = drmCommandWriteRead(bo->bom->fd, + DRM_RADEON_GEM_MMAP, + &args, + sizeof(args)); + if (!r) { + bo->ptr = (void *)(unsigned long)args.addr_ptr; + } else { + fprintf(stderr, "error mapping %p 0x%08X (error = %d)\n", + bo, bo->handle, r); + } + return r; +} + +static int bo_unmap(struct radeon_bo *bo) +{ + struct radeon_bo_gem *bo_gem = (struct radeon_bo_gem*)bo; + + if (--bo_gem->map_count > 0) { + return 0; + } + munmap(bo->ptr, bo->size); + bo->ptr = NULL; + return 0; +} + +static struct radeon_bo_funcs bo_gem_funcs = { + bo_open, + bo_ref, + bo_unref, + bo_map, + bo_unmap +}; + +struct radeon_bo_manager *radeon_bo_manager_gem_ctor(int fd) +{ + struct bo_manager_gem *bomg; + + bomg = (struct bo_manager_gem*)calloc(1, sizeof(struct bo_manager_gem)); + if (bomg == NULL) { + return NULL; + } + bomg->base.funcs = &bo_gem_funcs; + bomg->base.fd = fd; + return (struct radeon_bo_manager*)bomg; +} + +void radeon_bo_manager_gem_dtor(struct radeon_bo_manager *bom) +{ + struct bo_manager_gem *bomg = (struct bo_manager_gem*)bom; + + if (bom == NULL) { + return; + } + free(bomg); +} diff --git a/libdrm/radeon/radeon_bo_gem.h b/libdrm/radeon/radeon_bo_gem.h new file mode 100644 index 00000000..c0f68e6d --- /dev/null +++ b/libdrm/radeon/radeon_bo_gem.h @@ -0,0 +1,40 @@ +/* + * Copyright © 2008 Dave Airlie + * Copyright © 2008 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 + * Jérôme Glisse <glisse@freedesktop.org> + */ +#ifndef RADEON_BO_GEM_H +#define RADEON_BO_GEM_H + +#include "radeon_bo.h" + +struct radeon_bo_manager *radeon_bo_manager_gem_ctor(int fd); +void radeon_bo_manager_gem_dtor(struct radeon_bo_manager *bom); + +#endif diff --git a/libdrm/radeon/radeon_cs.h b/libdrm/radeon/radeon_cs.h new file mode 100644 index 00000000..e76121ea --- /dev/null +++ b/libdrm/radeon/radeon_cs.h @@ -0,0 +1,168 @@ +/* + * Copyright © 2008 Nicolai Haehnle + * Copyright © 2008 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: + * Aapo Tahkola <aet@rasterburn.org> + * Nicolai Haehnle <prefect_@gmx.net> + * Jérôme Glisse <glisse@freedesktop.org> + */ +#ifndef RADEON_CS_H +#define RADEON_CS_H + +#include <stdint.h> +#include "radeon_bo.h" + +struct radeon_cs_reloc { + struct radeon_bo *bo; + uint32_t start_offset; + uint32_t end_offset; + uint32_t read_domain; + uint32_t write_domain; + uint32_t flags; +}; + +struct radeon_cs_manager; + +struct radeon_cs { + struct radeon_cs_manager *csm; + void *relocs; + uint32_t *packets; + unsigned crelocs; + unsigned relocs_total_size; + unsigned cdw; + unsigned ndw; + int section; + unsigned section_ndw; + unsigned section_cdw; + const char *section_file; + const char *section_func; + int section_line; +}; + +/* cs functions */ +struct radeon_cs_funcs { + struct radeon_cs *(*cs_create)(struct radeon_cs_manager *csm, + uint32_t ndw); + int (*cs_write_dword)(struct radeon_cs *cs, uint32_t dword); + int (*cs_write_reloc)(struct radeon_cs *cs, + struct radeon_bo *bo, + uint32_t start_offset, + uint32_t end_offset, + uint32_t read_domain, + uint32_t write_domain, + uint32_t flags); + int (*cs_begin)(struct radeon_cs *cs, + uint32_t ndw, + const char *file, + const char *func, + int line); + int (*cs_end)(struct radeon_cs *cs, + const char *file, + const char *func, + int line); + int (*cs_emit)(struct radeon_cs *cs); + int (*cs_destroy)(struct radeon_cs *cs); + int (*cs_erase)(struct radeon_cs *cs); + int (*cs_need_flush)(struct radeon_cs *cs); + void (*cs_print)(struct radeon_cs *cs, FILE *file); +}; + +struct radeon_cs_manager { + struct radeon_cs_funcs *funcs; + int fd; +}; + +static inline struct radeon_cs *radeon_cs_create(struct radeon_cs_manager *csm, + uint32_t ndw) +{ + return csm->funcs->cs_create(csm, ndw); +} + +static inline int radeon_cs_write_dword(struct radeon_cs *cs, uint32_t dword) +{ + return cs->csm->funcs->cs_write_dword(cs, dword); +} + +static inline int radeon_cs_write_reloc(struct radeon_cs *cs, + struct radeon_bo *bo, + uint32_t start_offset, + uint32_t end_offset, + uint32_t read_domain, + uint32_t write_domain, + uint32_t flags) +{ + return cs->csm->funcs->cs_write_reloc(cs, + bo, + start_offset, + end_offset, + read_domain, + write_domain, + flags); +} + +static inline int radeon_cs_begin(struct radeon_cs *cs, + uint32_t ndw, + const char *file, + const char *func, + int line) +{ + return cs->csm->funcs->cs_begin(cs, ndw, file, func, line); +} + +static inline int radeon_cs_end(struct radeon_cs *cs, + const char *file, + const char *func, + int line) +{ + return cs->csm->funcs->cs_end(cs, file, func, line); +} + +static inline int radeon_cs_emit(struct radeon_cs *cs) +{ + return cs->csm->funcs->cs_emit(cs); +} + +static inline int radeon_cs_destroy(struct radeon_cs *cs) +{ + return cs->csm->funcs->cs_destroy(cs); +} + +static inline int radeon_cs_erase(struct radeon_cs *cs) +{ + return cs->csm->funcs->cs_erase(cs); +} + +static inline int radeon_cs_need_flush(struct radeon_cs *cs) +{ + return cs->csm->funcs->cs_need_flush(cs); +} + +static inline void radeon_cs_print(struct radeon_cs *cs, FILE *file) +{ + cs->csm->funcs->cs_print(cs, file); +} + +#endif diff --git a/libdrm/radeon/radeon_cs_gem.c b/libdrm/radeon/radeon_cs_gem.c new file mode 100644 index 00000000..319d1b9e --- /dev/null +++ b/libdrm/radeon/radeon_cs_gem.c @@ -0,0 +1,438 @@ +/* + * Copyright © 2008 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: + * Aapo Tahkola <aet@rasterburn.org> + * Nicolai Haehnle <prefect_@gmx.net> + * Jérôme Glisse <glisse@freedesktop.org> + */ +#include <errno.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <sys/ioctl.h> +#include "radeon_cs.h" +#include "radeon_cs_gem.h" +#include "radeon_bo_gem.h" +#include "drm.h" +#include "xf86drm.h" +#include "radeon_drm.h" + +#pragma pack(1) +struct cs_reloc_gem { + uint32_t handle; + uint32_t start_offset; + uint32_t end_offset; + uint32_t read_domain; + uint32_t write_domain; + uint32_t flags; +}; +#pragma pack() + +struct cs_gem { + struct radeon_cs base; + struct drm_radeon_cs2 cs; + struct drm_radeon_cs_chunk chunks[2]; + unsigned nrelocs; + uint32_t *relocs; + struct radeon_bo **relocs_bo; +}; + +static struct radeon_cs *cs_gem_create(struct radeon_cs_manager *csm, + uint32_t ndw) +{ + struct cs_gem *csg; + + /* max cmd buffer size is 64Kb */ + if (ndw > (64 * 1024 / 4)) { + return NULL; + } + csg = (struct cs_gem*)calloc(1, sizeof(struct cs_gem)); + if (csg == NULL) { + return NULL; + } + csg->base.csm = csm; + csg->base.ndw = 64 * 1024 / 4; + csg->base.packets = (uint32_t*)calloc(1, 64 * 1024); + if (csg->base.packets == NULL) { + free(csg); + return NULL; + } + csg->base.relocs_total_size = 0; + csg->base.crelocs = 0; + csg->nrelocs = 4096 / (4 * 4) ; + csg->relocs_bo = (struct radeon_bo**)calloc(1, + csg->nrelocs*sizeof(void*)); + if (csg->relocs_bo == NULL) { + free(csg->base.packets); + free(csg); + return NULL; + } + csg->base.relocs = csg->relocs = (uint32_t*)calloc(1, 4096); + if (csg->relocs == NULL) { + free(csg->relocs_bo); + free(csg->base.packets); + free(csg); + return NULL; + } + csg->chunks[0].chunk_id = RADEON_CHUNK_ID_IB; + csg->chunks[0].length_dw = 0; + csg->chunks[0].chunk_data = (uint64_t)(intptr_t)csg->base.packets; + csg->chunks[1].chunk_id = RADEON_CHUNK_ID_RELOCS; + csg->chunks[1].length_dw = 0; + csg->chunks[1].chunk_data = (uint64_t)(intptr_t)csg->relocs; + return (struct radeon_cs*)csg; +} + +static int cs_gem_write_dword(struct radeon_cs *cs, uint32_t dword) +{ + struct cs_gem *csg = (struct cs_gem*)cs; + if (cs->cdw >= cs->ndw) { + uint32_t tmp, *ptr; + tmp = (cs->cdw + 1 + 0x3FF) & (~0x3FF); + ptr = (uint32_t*)realloc(cs->packets, 4 * tmp); + if (ptr == NULL) { + return -ENOMEM; + } + cs->packets = ptr; + cs->ndw = tmp; + csg->chunks[0].chunk_data = (uint64_t)(intptr_t)csg->base.packets; + } + cs->packets[cs->cdw++] = dword; + csg->chunks[0].length_dw += 1; + return 0; +} + +static int cs_gem_write_reloc(struct radeon_cs *cs, + struct radeon_bo *bo, + uint32_t start_offset, + uint32_t end_offset, + uint32_t read_domain, + uint32_t write_domain, + uint32_t flags) +{ + struct cs_gem *csg = (struct cs_gem*)cs; + struct cs_reloc_gem *reloc; + uint32_t idx; + unsigned i; + + /* check domains */ + if ((read_domain && write_domain) || (!read_domain && !write_domain)) { + /* in one CS a bo can only be in read or write domain but not + * in read & write domain at the same sime + */ + return -EINVAL; + } + if (read_domain == RADEON_GEM_DOMAIN_CPU) { + return -EINVAL; + } + if (write_domain == RADEON_GEM_DOMAIN_CPU) { + return -EINVAL; + } + /* check reloc window */ + if (end_offset > bo->size) { + return -EINVAL; + } + if (start_offset > end_offset) { + return -EINVAL; + } + /* check if bo is already referenced */ + for(i = 0; i < cs->crelocs; i++) { + idx = i * 6; + reloc = (struct cs_reloc_gem*)&csg->relocs[idx]; + if (reloc->handle == bo->handle) { + /* Check domains must be in read or write. As we check already + * checked that in argument one of the read or write domain was + * set we only need to check that if previous reloc as the read + * domain set then the read_domain should also be set for this + * new relocation. + */ + if (reloc->read_domain && !read_domain) { + return -EINVAL; + } + if (reloc->write_domain && !write_domain) { + return -EINVAL; + } + reloc->read_domain |= read_domain; + reloc->write_domain |= write_domain; + /* update start and end offset */ + if (start_offset < reloc->start_offset) { + reloc->start_offset = start_offset; + } + if (end_offset > reloc->end_offset) { + reloc->end_offset = end_offset; + } + /* update flags */ + reloc->flags |= (flags & reloc->flags); + /* write relocation packet */ + cs_gem_write_dword(cs, 0xc0001000); + cs_gem_write_dword(cs, idx); + return 0; + } + } + /* new relocation */ + if (csg->base.crelocs >= csg->nrelocs) { + /* allocate more memory (TODO: should use a slab allocatore maybe) */ + uint32_t *tmp, size; + size = ((csg->nrelocs + 1) * sizeof(struct radeon_bo*)); + tmp = (uint32_t*)realloc(csg->relocs_bo, size); + if (tmp == NULL) { + return -ENOMEM; + } + csg->relocs_bo = (struct radeon_bo**)tmp; + size = ((csg->nrelocs + 1) * 6 * 4); + tmp = (uint32_t*)realloc(csg->relocs, size); + if (tmp == NULL) { + return -ENOMEM; + } + cs->relocs = csg->relocs = tmp; + csg->nrelocs += 1; + csg->chunks[1].chunk_data = (uint64_t)(intptr_t)csg->relocs; + } + csg->relocs_bo[csg->base.crelocs] = bo; + idx = (csg->base.crelocs++) * 6; + reloc = (struct cs_reloc_gem*)&csg->relocs[idx]; + reloc->handle = bo->handle; + reloc->start_offset = start_offset; + reloc->end_offset = end_offset; + reloc->read_domain = read_domain; + reloc->write_domain = write_domain; + reloc->flags = flags; + csg->chunks[1].length_dw += 6; + radeon_bo_ref(bo); + cs->relocs_total_size += bo->size; + cs_gem_write_dword(cs, 0xc0001000); + cs_gem_write_dword(cs, idx); + return 0; +} + +static int cs_gem_begin(struct radeon_cs *cs, + uint32_t ndw, + const char *file, + const char *func, + int line) +{ + return 0; +} + +static int cs_gem_end(struct radeon_cs *cs, + const char *file, + const char *func, + int line) + +{ + cs->section = 0; + return 0; +} + +static int cs_gem_emit(struct radeon_cs *cs) +{ + struct cs_gem *csg = (struct cs_gem*)cs; + uint64_t chunk_array[2]; + unsigned i; + int r; + + csg->chunks[0].length_dw = cs->cdw; + + chunk_array[0] = (uint64_t)(intptr_t)&csg->chunks[0]; + chunk_array[1] = (uint64_t)(intptr_t)&csg->chunks[1]; + + csg->cs.num_chunks = 2; + csg->cs.chunks = (uint64_t)(intptr_t)chunk_array; + + r = drmCommandWriteRead(cs->csm->fd, DRM_RADEON_CS2, + &csg->cs, sizeof(struct drm_radeon_cs2)); + for (i = 0; i < csg->base.crelocs; i++) { + radeon_bo_unref(csg->relocs_bo[i]); + csg->relocs_bo[i] = NULL; + } + return r; +} + +static int cs_gem_destroy(struct radeon_cs *cs) +{ + struct cs_gem *csg = (struct cs_gem*)cs; + + free(csg->relocs_bo); + free(cs->relocs); + free(cs->packets); + free(cs); + return 0; +} + +static int cs_gem_erase(struct radeon_cs *cs) +{ + struct cs_gem *csg = (struct cs_gem*)cs; + unsigned i; + + if (csg->relocs_bo) { + for (i = 0; i < csg->base.crelocs; i++) { + if (csg->relocs_bo[i]) { + radeon_bo_unref(csg->relocs_bo[i]); + csg->relocs_bo[i] = NULL; + } + } + } + cs->relocs_total_size = 0; + cs->cdw = 0; + cs->section = 0; + cs->crelocs = 0; + csg->chunks[0].length_dw = 0; + csg->chunks[1].length_dw = 0; + return 0; +} + +static int cs_gem_need_flush(struct radeon_cs *cs) +{ + return (cs->relocs_total_size > (32*1024*1024)); +} + +#define PACKET_TYPE0 0 +#define PACKET_TYPE1 1 +#define PACKET_TYPE2 2 +#define PACKET_TYPE3 3 + +#define PACKET3_NOP 0x10 +#define PACKET3_SET_SCISSORS 0x1E +#define PACKET3_3D_DRAW_VBUF 0x28 +#define PACKET3_3D_DRAW_IMMD 0x29 +#define PACKET3_3D_DRAW_INDX 0x2A +#define PACKET3_3D_LOAD_VBPNTR 0x2F +#define PACKET3_INDX_BUFFER 0x33 +#define PACKET3_3D_DRAW_VBUF_2 0x34 +#define PACKET3_3D_DRAW_IMMD_2 0x35 +#define PACKET3_3D_DRAW_INDX_2 0x36 + +#define CP_PACKET_GET_TYPE(h) (((h) >> 30) & 3) +#define CP_PACKET_GET_COUNT(h) (((h) >> 16) & 0x3FFF) +#define CP_PACKET0_GET_REG(h) (((h) & 0x1FFF) << 2) +#define CP_PACKET0_GET_ONE_REG_WR(h) (((h) >> 15) & 1) +#define CP_PACKET3_GET_OPCODE(h) (((h) >> 8) & 0xFF) + +static void cs_gem_print(struct radeon_cs *cs, FILE *file) +{ + unsigned opcode; + unsigned reg; + unsigned cnt; + int i, j; + + for (i = 0; i < cs->cdw;) { + cnt = CP_PACKET_GET_COUNT(cs->packets[i]); + switch (CP_PACKET_GET_TYPE(cs->packets[i])) { + case PACKET_TYPE0: + fprintf(file, "Pkt0 at %d (%d dwords):\n", i, cnt + 1); + reg = CP_PACKET0_GET_REG(cs->packets[i]); + if (CP_PACKET0_GET_ONE_REG_WR(cs->packets[i++])) { + for (j = 0; j <= cnt; j++) { + fprintf(file, " 0x%08X -> 0x%04X\n", + cs->packets[i++], reg); + } + } else { + for (j = 0; j <= cnt; j++) { + fprintf(file, " 0x%08X -> 0x%04X\n", + cs->packets[i++], reg); + reg += 4; + } + } + break; + case PACKET_TYPE3: + fprintf(file, "Pkt3 at %d :\n", i); + opcode = CP_PACKET3_GET_OPCODE(cs->packets[i++]); + switch (opcode) { + case PACKET3_NOP: + fprintf(file, " PACKET3_NOP:\n"); + break; + case PACKET3_3D_DRAW_VBUF: + fprintf(file, " PACKET3_3D_DRAW_VBUF:\n"); + break; + case PACKET3_3D_DRAW_IMMD: + fprintf(file, " PACKET3_3D_DRAW_IMMD:\n"); + break; + case PACKET3_3D_DRAW_INDX: + fprintf(file, " PACKET3_3D_DRAW_INDX:\n"); + break; + case PACKET3_3D_LOAD_VBPNTR: + fprintf(file, " PACKET3_3D_LOAD_VBPNTR:\n"); + break; + case PACKET3_INDX_BUFFER: + fprintf(file, " PACKET3_INDX_BUFFER:\n"); + break; + case PACKET3_3D_DRAW_VBUF_2: + fprintf(file, " PACKET3_3D_DRAW_VBUF_2:\n"); + break; + case PACKET3_3D_DRAW_IMMD_2: + fprintf(file, " PACKET3_3D_DRAW_IMMD_2:\n"); + break; + case PACKET3_3D_DRAW_INDX_2: + fprintf(file, " PACKET3_3D_DRAW_INDX_2:\n"); + break; + default: + fprintf(file, "Unknow opcode 0x%02X at %d\n", opcode, i); + return; + } + for (j = 0; j <= cnt; j++) { + fprintf(file, " 0x%08X\n", cs->packets[i++]); + } + break; + case PACKET_TYPE1: + case PACKET_TYPE2: + default: + fprintf(file, "Unknow packet 0x%08X at %d\n", cs->packets[i], i); + return; + } + } +} + +static struct radeon_cs_funcs radeon_cs_gem_funcs = { + cs_gem_create, + cs_gem_write_dword, + cs_gem_write_reloc, + cs_gem_begin, + cs_gem_end, + cs_gem_emit, + cs_gem_destroy, + cs_gem_erase, + cs_gem_need_flush, + cs_gem_print +}; + +struct radeon_cs_manager *radeon_cs_manager_gem_ctor(int fd) +{ + struct radeon_cs_manager *csm; + + csm = (struct radeon_cs_manager*)calloc(1, + sizeof(struct radeon_cs_manager)); + if (csm == NULL) { + return NULL; + } + csm->funcs = &radeon_cs_gem_funcs; + csm->fd = fd; + return csm; +} + +void radeon_cs_manager_gem_dtor(struct radeon_cs_manager *csm) +{ + free(csm); +} diff --git a/libdrm/radeon/radeon_cs_gem.h b/libdrm/radeon/radeon_cs_gem.h new file mode 100644 index 00000000..5efd146f --- /dev/null +++ b/libdrm/radeon/radeon_cs_gem.h @@ -0,0 +1,41 @@ +/* + * Copyright © 2008 Nicolai Haehnle + * Copyright © 2008 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: + * Aapo Tahkola <aet@rasterburn.org> + * Nicolai Haehnle <prefect_@gmx.net> + * Jérôme Glisse <glisse@freedesktop.org> + */ +#ifndef RADEON_CS_GEM_H +#define RADEON_CS_GEM_H + +#include "radeon_cs.h" + +struct radeon_cs_manager *radeon_cs_manager_gem_ctor(int fd); +void radeon_cs_manager_gem_dtor(struct radeon_cs_manager *csm); + +#endif diff --git a/libdrm/radeon/radeon_track.c b/libdrm/radeon/radeon_track.c new file mode 100644 index 00000000..1623906f --- /dev/null +++ b/libdrm/radeon/radeon_track.c @@ -0,0 +1,140 @@ +/* + * Copyright © 2008 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: + * Jérôme Glisse <glisse@freedesktop.org> + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "radeon_track.h" + +void radeon_track_add_event(struct radeon_track *track, + const char *file, + const char *func, + const char *op, + unsigned line) +{ + struct radeon_track_event *event; + + if (track == NULL) { + return; + } + event = (void*)calloc(1,sizeof(struct radeon_track_event)); + if (event == NULL) { + return; + } + event->line = line; + event->file = strdup(file); + event->func = strdup(func); + event->op = strdup(op); + if (event->file == NULL || event->func == NULL || event->op == NULL) { + free(event->file); + free(event->func); + free(event->op); + free(event); + return; + } + event->next = track->events; + track->events = event; +} + +struct radeon_track *radeon_tracker_add_track(struct radeon_tracker *tracker, + unsigned key) +{ + struct radeon_track *track; + + track = (struct radeon_track*)calloc(1, sizeof(struct radeon_track)); + if (track) { + track->next = tracker->tracks.next; + track->prev = &tracker->tracks; + tracker->tracks.next = track; + if (track->next) { + track->next->prev = track; + } + track->key = key; + track->events = NULL; + } + return track; +} + +void radeon_tracker_remove_track(struct radeon_tracker *tracker, + struct radeon_track *track) +{ + struct radeon_track_event *event; + void *tmp; + + if (track == NULL) { + return; + } + track->prev->next = track->next; + if (track->next) { + track->next->prev = track->prev; + } + track->next = track->prev = NULL; + event = track->events; + while (event) { + tmp = event; + free(event->file); + free(event->func); + free(event->op); + event = event->next; + free(tmp); + } + track->events = NULL; + free(track); +} + +void radeon_tracker_print(struct radeon_tracker *tracker, FILE *file) +{ + struct radeon_track *track; + struct radeon_track_event *event; + void *tmp; + + track = tracker->tracks.next; + while (track) { + event = track->events; + fprintf(file, "[0x%08X] :\n", track->key); + while (event) { + tmp = event; + fprintf(file, " [0x%08X:%s](%s:%s:%d)\n", + track->key, event->op, event->file, + event->func, event->line); + free(event->file); + free(event->func); + free(event->op); + event->file = NULL; + event->func = NULL; + event->op = NULL; + event = event->next; + free(tmp); + } + track->events = NULL; + tmp = track; + track = track->next; + free(tmp); + } +} diff --git a/libdrm/radeon/radeon_track.h b/libdrm/radeon/radeon_track.h new file mode 100644 index 00000000..838d1f38 --- /dev/null +++ b/libdrm/radeon/radeon_track.h @@ -0,0 +1,64 @@ +/* + * Copyright © 2008 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: + * Jérôme Glisse <glisse@freedesktop.org> + */ +#ifndef RADEON_TRACK_H +#define RADEON_TRACK_H + +struct radeon_track_event { + struct radeon_track_event *next; + char *file; + char *func; + char *op; + unsigned line; +}; + +struct radeon_track { + struct radeon_track *next; + struct radeon_track *prev; + unsigned key; + struct radeon_track_event *events; +}; + +struct radeon_tracker { + struct radeon_track tracks; +}; + +void radeon_track_add_event(struct radeon_track *track, + const char *file, + const char *func, + const char *op, + unsigned line); +struct radeon_track *radeon_tracker_add_track(struct radeon_tracker *tracker, + unsigned key); +void radeon_tracker_remove_track(struct radeon_tracker *tracker, + struct radeon_track *track); +void radeon_tracker_print(struct radeon_tracker *tracker, + FILE *file); + +#endif diff --git a/libdrm/xf86drm.c b/libdrm/xf86drm.c index c36f1964..94f5dfad 100644 --- a/libdrm/xf86drm.c +++ b/libdrm/xf86drm.c @@ -87,6 +87,9 @@ #define DRM_MSG_VERBOSITY 3 +#define DRM_NODE_CONTROL 0 +#define DRM_NODE_RENDER 1 + static drmServerInfoPtr drm_server_info; void drmSetServerInfo(drmServerInfoPtr info) @@ -277,7 +280,7 @@ static int drmMatchBusID(const char *id1, const char *id2) * special file node with the major and minor numbers specified by \p dev and * parent directory if necessary and was called by root. */ -static int drmOpenDevice(long dev, int minor) +static int drmOpenDevice(long dev, int minor, int type) { stat_t st; char buf[64]; @@ -287,7 +290,7 @@ static int drmOpenDevice(long dev, int minor) uid_t user = DRM_DEV_UID; gid_t group = DRM_DEV_GID, serv_group; - sprintf(buf, DRM_DEV_NAME, DRM_DIR_NAME, minor); + sprintf(buf, type ? DRM_DEV_NAME : DRM_CONTROL_DEV_NAME, DRM_DIR_NAME, minor); drmMsg("drmOpenDevice: node name is %s\n", buf); if (drm_server_info) { @@ -386,15 +389,15 @@ wait_for_udev: * Calls drmOpenDevice() if \p create is set, otherwise assembles the device * name from \p minor and opens it. */ -static int drmOpenMinor(int minor, int create) +static int drmOpenMinor(int minor, int create, int type) { int fd; char buf[64]; if (create) - return drmOpenDevice(makedev(DRM_MAJOR, minor), minor); + return drmOpenDevice(makedev(DRM_MAJOR, minor), minor, type); - sprintf(buf, DRM_DEV_NAME, DRM_DIR_NAME, minor); + sprintf(buf, type ? DRM_DEV_NAME : DRM_CONTROL_DEV_NAME, DRM_DIR_NAME, minor); if ((fd = open(buf, O_RDWR, 0)) >= 0) return fd; return -errno; @@ -417,7 +420,7 @@ int drmAvailable(void) int retval = 0; int fd; - if ((fd = drmOpenMinor(0, 1)) < 0) { + if ((fd = drmOpenMinor(0, 1, DRM_NODE_RENDER)) < 0) { #ifdef __linux__ /* Try proc for backward Linux compatibility */ if (!access("/proc/dri/0", R_OK)) @@ -458,7 +461,7 @@ static int drmOpenByBusid(const char *busid) drmMsg("drmOpenByBusid: Searching for BusID %s\n", busid); for (i = 0; i < DRM_MAX_MINOR; i++) { - fd = drmOpenMinor(i, 1); + fd = drmOpenMinor(i, 1, DRM_NODE_RENDER); drmMsg("drmOpenByBusid: drmOpenMinor returns %d\n", fd); if (fd >= 0) { sv.drm_di_major = 1; @@ -520,7 +523,7 @@ static int drmOpenByName(const char *name) * already in use. If it's in use it will have a busid assigned already. */ for (i = 0; i < DRM_MAX_MINOR; i++) { - if ((fd = drmOpenMinor(i, 1)) >= 0) { + if ((fd = drmOpenMinor(i, 1, DRM_NODE_RENDER)) >= 0) { if ((version = drmGetVersion(fd))) { if (!strcmp(version->name, name)) { drmFreeVersion(version); @@ -564,7 +567,7 @@ static int drmOpenByName(const char *name) if (*pt) { /* Found busid */ return drmOpenByBusid(++pt); } else { /* No busid */ - return drmOpenDevice(strtol(devstring, NULL, 0),i); + return drmOpenDevice(strtol(devstring, NULL, 0),i, DRM_NODE_RENDER); } } } @@ -614,6 +617,10 @@ int drmOpen(const char *name, const char *busid) return -1; } +int drmOpenControl(int minor) +{ + return drmOpenMinor(minor, 0, DRM_NODE_CONTROL); +} /** * Free the version information returned by drmGetVersion(). @@ -2434,3 +2441,20 @@ void drmCloseOnce(int fd) } } } + +int drmSetMaster(int fd) +{ + int ret; + + fprintf(stderr,"Setting master \n"); + ret = ioctl(fd, DRM_IOCTL_SET_MASTER, 0); + return ret; +} + +int drmDropMaster(int fd) +{ + int ret; + fprintf(stderr,"Dropping master \n"); + ret = ioctl(fd, DRM_IOCTL_DROP_MASTER, 0); + return ret; +} diff --git a/libdrm/xf86drm.h b/libdrm/xf86drm.h index f4fa71b0..584d2a41 100644 --- a/libdrm/xf86drm.h +++ b/libdrm/xf86drm.h @@ -49,6 +49,7 @@ #define DRM_DIR_NAME "/dev/dri" #define DRM_DEV_NAME "%s/card%d" +#define DRM_CONTROL_DEV_NAME "%s/controlD%d" #define DRM_PROC_NAME "/proc/dri/" /* For backward Linux compatibility */ #define DRM_ERR_NO_DEVICE (-1001) @@ -508,6 +509,7 @@ do { register unsigned int __old __asm("o0"); \ /* General user-level programmer's API: unprivileged */ extern int drmAvailable(void); extern int drmOpen(const char *name, const char *busid); +extern int drmOpenControl(int minor); extern int drmClose(int fd); extern drmVersionPtr drmGetVersion(int fd); extern drmVersionPtr drmGetLibVersion(int fd); @@ -659,4 +661,7 @@ extern int drmOpenOnce(void *unused, const char *BusID, int *newlyopened); extern void drmCloseOnce(int fd); extern void drmMsg(const char *format, ...); +extern int drmSetMaster(int fd); +extern int drmDropMaster(int fd); + #endif diff --git a/libdrm/xf86drmMode.c b/libdrm/xf86drmMode.c new file mode 100644 index 00000000..a0a164de --- /dev/null +++ b/libdrm/xf86drmMode.c @@ -0,0 +1,684 @@ +/* + * \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 <sys/ioctl.h> +#include <stdio.h> + +#include "xf86drmMode.h" +#include "xf86drm.h" +#include <drm.h> +#include <string.h> +#include <dirent.h> +#include <errno.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; +} + +/* + * 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 drmModeFreeConnector(drmModeConnectorPtr ptr) +{ + if (!ptr) + return; + + drmFree(ptr->modes); + drmFree(ptr); + +} + +void drmModeFreeEncoder(drmModeEncoderPtr ptr) +{ + drmFree(ptr); +} + +/* + * ModeSetting functions. + */ + +drmModeResPtr drmModeGetResources(int fd) +{ + struct drm_mode_card_res res; + 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_connectors) + res.connector_id_ptr = VOID2U64(drmMalloc(res.count_connectors*sizeof(uint32_t))); + if (res.count_encoders) + res.encoder_id_ptr = VOID2U64(drmMalloc(res.count_encoders*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_connectors = res.count_connectors; + r->count_encoders = res.count_encoders; + /* 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->connectors = drmAllocCpy(U642VOID(res.connector_id_ptr), res.count_connectors, sizeof(uint32_t)); + r->encoders = drmAllocCpy(U642VOID(res.encoder_id_ptr), res.count_encoders, sizeof(uint32_t)); + +err_allocs: + drmFree(U642VOID(res.fb_id_ptr)); + drmFree(U642VOID(res.crtc_id_ptr)); + drmFree(U642VOID(res.connector_id_ptr)); + drmFree(U642VOID(res.encoder_id_ptr)); + + return r; +} + +int drmModeAddFB(int fd, uint32_t width, uint32_t height, uint8_t depth, + uint8_t bpp, uint32_t pitch, uint32_t bo_handle, + 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.fb_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.fb_id = buf; + + if (ioctl(fd, DRM_IOCTL_MODE_GETFB, &info)) + return NULL; + + if (!(r = drmMalloc(sizeof(*r)))) + return NULL; + + r->fb_id = info.fb_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; + + 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; + return r; +} + + +int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId, + uint32_t x, uint32_t y, uint32_t *connectors, int count, + struct drm_mode_modeinfo *mode) +{ + struct drm_mode_crtc crtc; + + crtc.x = x; + crtc.y = y; + crtc.crtc_id = crtcId; + crtc.fb_id = bufferId; + crtc.set_connectors_ptr = VOID2U64(connectors); + crtc.count_connectors = 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); +} + +/* + * Cursor manipulation + */ + +int drmModeSetCursor(int fd, uint32_t crtcId, uint32_t bo_handle, uint32_t width, uint32_t height) +{ + struct drm_mode_cursor arg; + + arg.flags = DRM_MODE_CURSOR_BO; + arg.crtc_id = crtcId; + arg.width = width; + arg.height = height; + arg.handle = bo_handle; + + return ioctl(fd, DRM_IOCTL_MODE_CURSOR, &arg); +} + +int drmModeMoveCursor(int fd, uint32_t crtcId, int x, int y) +{ + struct drm_mode_cursor arg; + + arg.flags = DRM_MODE_CURSOR_MOVE; + arg.crtc_id = crtcId; + arg.x = x; + arg.y = y; + + return ioctl(fd, DRM_IOCTL_MODE_CURSOR, &arg); +} + +/* + * Encoder get + */ +drmModeEncoderPtr drmModeGetEncoder(int fd, uint32_t encoder_id) +{ + struct drm_mode_get_encoder enc; + drmModeEncoderPtr r = NULL; + + enc.encoder_id = encoder_id; + enc.encoder_type = 0; + enc.possible_crtcs = 0; + enc.possible_clones = 0; + + if (ioctl(fd, DRM_IOCTL_MODE_GETENCODER, &enc)) + return 0; + + if (!(r = drmMalloc(sizeof(*r)))) + return 0; + + r->encoder_id = enc.encoder_id; + r->crtc_id = enc.crtc_id; + r->encoder_type = enc.encoder_type; + r->possible_crtcs = enc.possible_crtcs; + r->possible_clones = enc.possible_clones; + + return r; +} + +/* + * Connector manipulation + */ + +drmModeConnectorPtr drmModeGetConnector(int fd, uint32_t connector_id) +{ + struct drm_mode_get_connector conn; + drmModeConnectorPtr r = NULL; + + conn.connector_id = connector_id; + conn.connector_type_id = 0; + conn.connector_type = 0; + conn.count_modes = 0; + conn.modes_ptr = 0; + conn.count_props = 0; + conn.props_ptr = 0; + conn.prop_values_ptr = 0; + conn.count_encoders = 0; + conn.encoders_ptr = 0; + + if (ioctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn)) + return 0; + + if (conn.count_props) { + conn.props_ptr = VOID2U64(drmMalloc(conn.count_props*sizeof(uint32_t))); + conn.prop_values_ptr = VOID2U64(drmMalloc(conn.count_props*sizeof(uint64_t))); + } + + if (conn.count_modes) + conn.modes_ptr = VOID2U64(drmMalloc(conn.count_modes*sizeof(struct drm_mode_modeinfo))); + + if (conn.count_encoders) + conn.encoders_ptr = VOID2U64(drmMalloc(conn.count_encoders*sizeof(uint32_t))); + + if (ioctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn)) + goto err_allocs; + + if(!(r = drmMalloc(sizeof(*r)))) { + goto err_allocs; + } + + r->connector_id = conn.connector_id; + r->encoder_id = conn.encoder_id; + r->connection = conn.connection; + r->mmWidth = conn.mm_width; + r->mmHeight = conn.mm_height; + r->subpixel = conn.subpixel; + r->count_modes = conn.count_modes; + /* TODO we should test if these alloc & cpy fails. */ + r->count_props = conn.count_props; + r->props = drmAllocCpy(U642VOID(conn.props_ptr), conn.count_props, sizeof(uint32_t)); + r->prop_values = drmAllocCpy(U642VOID(conn.prop_values_ptr), conn.count_props, sizeof(uint64_t)); + r->modes = drmAllocCpy(U642VOID(conn.modes_ptr), conn.count_modes, sizeof(struct drm_mode_modeinfo)); + r->count_encoders = conn.count_encoders; + r->encoders = drmAllocCpy(U642VOID(conn.encoders_ptr), conn.count_encoders, sizeof(uint32_t)); + r->connector_type = conn.connector_type; + r->connector_type_id = conn.connector_type_id; + + if (!r->props || !r->prop_values || !r->modes || !r->encoders) + goto err_allocs; + +err_allocs: + drmFree(U642VOID(conn.prop_values_ptr)); + drmFree(U642VOID(conn.props_ptr)); + drmFree(U642VOID(conn.modes_ptr)); + drmFree(U642VOID(conn.encoders_ptr)); + + return r; +} + +int drmModeAttachMode(int fd, uint32_t connector_id, struct drm_mode_modeinfo *mode_info) +{ + struct drm_mode_mode_cmd res; + + memcpy(&res.mode, mode_info, sizeof(struct drm_mode_modeinfo)); + res.connector_id = connector_id; + + return ioctl(fd, DRM_IOCTL_MODE_ATTACHMODE, &res); +} + +int drmModeDetachMode(int fd, uint32_t connector_id, struct drm_mode_modeinfo *mode_info) +{ + struct drm_mode_mode_cmd res; + + memcpy(&res.mode, mode_info, sizeof(struct drm_mode_modeinfo)); + res.connector_id = connector_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; + + 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_BLOB) { + 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 drmModeConnectorSetProperty(int fd, uint32_t connector_id, uint32_t property_id, + uint64_t value) +{ + struct drm_mode_connector_set_property osp; + int ret; + + osp.connector_id = connector_id; + osp.prop_id = property_id; + osp.value = value; + + if ((ret = ioctl(fd, DRM_IOCTL_MODE_SETPROPERTY, &osp))) + return ret; + + return 0; +} + +/* + * checks if a modesetting capable driver has attached to the pci id + * returns 0 if modesetting supported. + * -EINVAL or invalid bus id + * -ENOSYS if no modesetting support +*/ +int drmCheckModesettingSupported(const char *busid) +{ +#ifdef __linux__ + char pci_dev_dir[1024]; + int domain, bus, dev, func; + DIR *sysdir; + struct dirent *dent; + int found = 0, ret; + + ret = sscanf(busid, "pci:%04x:%02x:%02x.%d", &domain, &bus, &dev, &func); + if (ret != 4) + return -EINVAL; + + sprintf(pci_dev_dir, "/sys/bus/pci/devices/%04x:%02x:%02x.%d/drm", + domain, bus, dev, func); + + sysdir = opendir(pci_dev_dir); + if (sysdir) { + dent = readdir(sysdir); + while (dent) { + if (!strncmp(dent->d_name, "controlD", 8)) { + found = 1; + break; + } + + dent = readdir(sysdir); + } + closedir(sysdir); + if (found) + return 0; + } + + sprintf(pci_dev_dir, "/sys/bus/pci/devices/%04x:%02x:%02x.%d/", + domain, bus, dev, func); + + sysdir = opendir(pci_dev_dir); + if (!sysdir) + return -EINVAL; + + dent = readdir(sysdir); + while (dent) { + if (!strncmp(dent->d_name, "drm:controlD", 12)) { + found = 1; + break; + } + + dent = readdir(sysdir); + } + + closedir(sysdir); + if (found) + return 0; +#endif + return -ENOSYS; + +} + +int drmModeReplaceFB(int fd, uint32_t buffer_id, + uint32_t width, uint32_t height, uint8_t depth, + uint8_t bpp, uint32_t pitch, uint32_t bo_handle) +{ + 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; + f.fb_id = buffer_id; + + if ((ret = ioctl(fd, DRM_IOCTL_MODE_REPLACEFB, &f))) + return ret; + + return 0; +} + +int drmModeCrtcGetGamma(int fd, uint32_t crtc_id, uint32_t size, + uint16_t *red, uint16_t *green, uint16_t *blue) +{ + int ret; + struct drm_mode_crtc_lut l; + + l.crtc_id = crtc_id; + l.gamma_size = size; + l.red = VOID2U64(red); + l.green = VOID2U64(green); + l.blue = VOID2U64(blue); + + if ((ret = ioctl(fd, DRM_IOCTL_MODE_GETGAMMA, &l))) + return ret; + + return 0; +} + +int drmModeCrtcSetGamma(int fd, uint32_t crtc_id, uint32_t size, + uint16_t *red, uint16_t *green, uint16_t *blue) +{ + int ret; + struct drm_mode_crtc_lut l; + + l.crtc_id = crtc_id; + l.gamma_size = size; + l.red = VOID2U64(red); + l.green = VOID2U64(green); + l.blue = VOID2U64(blue); + + if ((ret = ioctl(fd, DRM_IOCTL_MODE_SETGAMMA, &l))) + return ret; + + return 0; +} diff --git a/libdrm/xf86drmMode.h b/libdrm/xf86drmMode.h new file mode 100644 index 00000000..b68d1469 --- /dev/null +++ b/libdrm/xf86drmMode.h @@ -0,0 +1,256 @@ +/* + * \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> + +/* + * 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_connectors; + uint32_t *connectors; + + int count_encoders; + uint32_t *encoders; + + 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 { + uint32_t prop_id; + uint32_t flags; + 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 { + uint32_t crtc_id; + uint32_t buffer_id; /**< FB id to connect to 0 = disconnect */ + + uint32_t x, y; /**< Position on the framebuffer */ + uint32_t width, height; + int mode_valid; + struct drm_mode_modeinfo mode; + + int gamma_size; /**< Number of gamma stops */ + +} drmModeCrtc, *drmModeCrtcPtr; + +typedef struct _drmModeEncoder { + uint32_t encoder_id; + uint32_t encoder_type; + uint32_t crtc_id; + uint32_t possible_crtcs; + uint32_t possible_clones; +} drmModeEncoder, *drmModeEncoderPtr; + +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 _drmModeConnector { + uint32_t connector_id; + uint32_t encoder_id; /**< Encoder currently connected to */ + uint32_t connector_type; + uint32_t connector_type_id; + drmModeConnection connection; + uint32_t mmWidth, mmHeight; /**< HxW in millimeters */ + drmModeSubPixel subpixel; + + 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 */ + + int count_encoders; + uint32_t *encoders; /**< List of encoder ids */ +} drmModeConnector, *drmModeConnectorPtr; + + + +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 drmModeFreeConnector( drmModeConnectorPtr ptr ); +extern void drmModeFreeEncoder( drmModeEncoderPtr 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, uint32_t bo_handle, + uint32_t *buf_id); +/** + * Destroies the given framebuffer. + */ +extern int drmModeRmFB(int fd, uint32_t bufferId); + +/** + * Replace a framebuffer object with a new one - for resizing the screen. + */ +extern int drmModeReplaceFB(int fd, uint32_t buffer_id, + uint32_t width, uint32_t height, uint8_t depth, + uint8_t bpp, uint32_t pitch, uint32_t bo_handle); + +/* + * 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 *connectors, int count, + struct drm_mode_modeinfo *mode); + +/* + * Cursor functions + */ + +/** + * Set the cursor on crtc + */ +int drmModeSetCursor(int fd, uint32_t crtcId, uint32_t bo_handle, uint32_t width, uint32_t height); + +/** + * Move the cursor on crtc + */ +int drmModeMoveCursor(int fd, uint32_t crtcId, int x, int y); + +/** + * Encoder functions + */ +drmModeEncoderPtr drmModeGetEncoder(int fd, uint32_t encoder_id); + +/* + * Connector manipulation + */ + +/** + * Retrive information about the connector connectorId. + */ +extern drmModeConnectorPtr drmModeGetConnector(int fd, + uint32_t connectorId); + +/** + * Attaches the given mode to an connector. + */ +extern int drmModeAttachMode(int fd, uint32_t connectorId, struct drm_mode_modeinfo *mode_info); + +/** + * Detaches a mode from the connector + * must be unused, by the given mode. + */ +extern int drmModeDetachMode(int fd, uint32_t connectorId, 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 drmModeConnectorSetProperty(int fd, uint32_t connector_id, uint32_t property_id, + uint64_t value); +extern int drmCheckModesettingSupported(const char *busid); + +extern int drmModeCrtcSetGamma(int fd, uint32_t crtc_id, uint32_t size, + uint16_t *red, uint16_t *green, uint16_t *blue); +extern int drmModeCrtcGetGamma(int fd, uint32_t crtc_id, uint32_t size, + uint16_t *red, uint16_t *green, uint16_t *blue); diff --git a/linux-core/Makefile b/linux-core/Makefile index 157664ef..66bc4f25 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))) @@ -90,8 +90,9 @@ VIAHEADERS = via_drm.h via_drv.h via_3d_reg.h via_verifier.h $(DRMHEADERS) MACH64HEADERS = mach64_drv.h mach64_drm.h $(DRMHEADERS) NVHEADERS = nv_drv.h $(DRMHEADERS) FFBHEADERS = ffb_drv.h $(DRMHEADERS) -NOUVEAUHEADERS = nouveau_drv.h nouveau_drm.h nouveau_reg.h $(DRMHEADERS) +NOUVEAUHEADERS = nouveau_drv.h nouveau_drm.h nouveau_reg.h nouveau_display.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 @@ -290,6 +291,7 @@ CONFIG_DRM_MACH64 := n CONFIG_DRM_NV := n CONFIG_DRM_NOUVEAU := n CONFIG_DRM_XGI := n +CONFIG_DRM_RADEON_MS := n # Enable module builds for the modules requested/supported. @@ -329,6 +331,9 @@ endif ifneq (,$(findstring xgi,$(DRM_MODULES))) CONFIG_DRM_XGI := m endif +ifneq (,$(findstring radeon_ms,$(DRM_MODULES))) +#CONFIG_DRM_RADEON_MS := m +endif # These require AGP support diff --git a/linux-core/Makefile.kernel b/linux-core/Makefile.kernel index e5af2ec4..9ef9890d 100644 --- a/linux-core/Makefile.kernel +++ b/linux-core/Makefile.kernel @@ -12,17 +12,21 @@ drm-objs := drm_auth.o drm_bufs.o drm_context.o drm_dma.o drm_drawable.o \ drm_lock.o drm_memory.o drm_proc.o drm_stub.o drm_vm.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_vm_nopage_compat.o drm_gem.o + drm_hashtab.o drm_mm.o drm_compat.o \ + drm_fence.o drm_ttm.o drm_bo.o drm_bo_move.o \ + drm_crtc.o drm_edid.o drm_modes.o drm_crtc_helper.o \ + drm_regman.o drm_vm_nopage_compat.o drm_gem.o drm_uncached.o tdfx-objs := tdfx_drv.o r128-objs := r128_drv.o r128_cce.o r128_state.o r128_irq.o mga-objs := mga_drv.o mga_dma.o mga_state.o mga_warp.o mga_irq.o i810-objs := i810_drv.o i810_dma.o -i915-objs := i915_drv.o i915_dma.o i915_irq.o i915_mem.o i915_fence.o \ - i915_buffer.o i915_compat.o i915_execbuf.o i915_suspend.o \ - i915_opregion.o \ - i915_gem.o i915_gem_debug.o i915_gem_proc.o i915_gem_tiling.o +i915-objs := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \ + i915_compat.o i915_suspend.o i915_opregion.o \ + i915_gem.o i915_gem_debug.o i915_gem_proc.o i915_gem_tiling.o \ + intel_display.o intel_crt.o intel_lvds.o intel_bios.o \ + intel_sdvo.o intel_modes.o intel_i2c.o i915_init.o intel_fb.o \ + intel_tv.o intel_dvo.o dvo_ch7xxx.o \ + dvo_ch7017.o dvo_ivch.o dvo_tfp410.o dvo_sil164.o nouveau-objs := nouveau_drv.o nouveau_state.o nouveau_fifo.o nouveau_mem.o \ nouveau_object.o nouveau_irq.o nouveau_notifier.o nouveau_swmthd.o \ nouveau_sgdma.o nouveau_dma.o nouveau_bo.o nouveau_fence.o \ @@ -32,8 +36,15 @@ nouveau-objs := nouveau_drv.o nouveau_state.o nouveau_fifo.o nouveau_mem.o \ nv04_fifo.o nv10_fifo.o nv40_fifo.o nv50_fifo.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 + nv04_instmem.o nv50_instmem.o \ + nouveau_bios.o \ + nv50_crtc.o nv50_cursor.o nv50_lut.o nv50_fb.o nv50_output.o nv50_sor.o nv50_dac.o nv50_connector.o nv50_i2c.o nv50_display.o \ + nv50_kms_wrapper.o \ + nv50_fbcon.o +radeon-objs := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o radeon_irq.o r300_cmdbuf.o radeon_gem.o \ + radeon_buffer.o radeon_fence.o atom.o radeon_display.o radeon_atombios.o radeon_i2c.o radeon_connectors.o radeon_cs.o \ + atombios_crtc.o radeon_encoders.o radeon_fb.o radeon_combios.o radeon_legacy_crtc.o radeon_legacy_encoders.o \ + radeon_cursor.o radeon_pm.o radeon_gem_proc.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 @@ -47,6 +58,7 @@ xgi-objs := xgi_cmdlist.o xgi_drv.o xgi_fb.o xgi_misc.o xgi_pcie.o \ ifeq ($(CONFIG_COMPAT),y) drm-objs += drm_ioc32.o radeon-objs += radeon_ioc32.o +radeon_ms-objs += radeon_ms_compat.o mga-objs += mga_ioc32.o r128-objs += r128_ioc32.o i915-objs += i915_ioc32.o @@ -69,3 +81,4 @@ obj-$(CONFIG_DRM_MACH64)+= mach64.o obj-$(CONFIG_DRM_NV) += nv.o obj-$(CONFIG_DRM_NOUVEAU) += nouveau.o obj-$(CONFIG_DRM_XGI) += xgi.o +obj-$(CONFIG_DRM_RADEON_MS) += radeon_ms.o diff --git a/linux-core/ObjectID.h b/linux-core/ObjectID.h new file mode 100644 index 00000000..f1f18a48 --- /dev/null +++ b/linux-core/ObjectID.h @@ -0,0 +1,518 @@ +/* +* Copyright 2006-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. +*/ +/* based on stg/asic_reg/drivers/inc/asic_reg/ObjectID.h ver 23 */ + +#ifndef _OBJECTID_H +#define _OBJECTID_H + +#if defined(_X86_) +#pragma pack(1) +#endif + +/****************************************************/ +/* Graphics Object Type Definition */ +/****************************************************/ +#define GRAPH_OBJECT_TYPE_NONE 0x0 +#define GRAPH_OBJECT_TYPE_GPU 0x1 +#define GRAPH_OBJECT_TYPE_ENCODER 0x2 +#define GRAPH_OBJECT_TYPE_CONNECTOR 0x3 +#define GRAPH_OBJECT_TYPE_ROUTER 0x4 +/* deleted */ + +/****************************************************/ +/* Encoder Object ID Definition */ +/****************************************************/ +#define ENCODER_OBJECT_ID_NONE 0x00 + +/* Radeon Class Display Hardware */ +#define ENCODER_OBJECT_ID_INTERNAL_LVDS 0x01 +#define ENCODER_OBJECT_ID_INTERNAL_TMDS1 0x02 +#define ENCODER_OBJECT_ID_INTERNAL_TMDS2 0x03 +#define ENCODER_OBJECT_ID_INTERNAL_DAC1 0x04 +#define ENCODER_OBJECT_ID_INTERNAL_DAC2 0x05 /* TV/CV DAC */ +#define ENCODER_OBJECT_ID_INTERNAL_SDVOA 0x06 +#define ENCODER_OBJECT_ID_INTERNAL_SDVOB 0x07 + +/* External Third Party Encoders */ +#define ENCODER_OBJECT_ID_SI170B 0x08 +#define ENCODER_OBJECT_ID_CH7303 0x09 +#define ENCODER_OBJECT_ID_CH7301 0x0A +#define ENCODER_OBJECT_ID_INTERNAL_DVO1 0x0B /* This belongs to Radeon Class Display Hardware */ +#define ENCODER_OBJECT_ID_EXTERNAL_SDVOA 0x0C +#define ENCODER_OBJECT_ID_EXTERNAL_SDVOB 0x0D +#define ENCODER_OBJECT_ID_TITFP513 0x0E +#define ENCODER_OBJECT_ID_INTERNAL_LVTM1 0x0F /* not used for Radeon */ +#define ENCODER_OBJECT_ID_VT1623 0x10 +#define ENCODER_OBJECT_ID_HDMI_SI1930 0x11 +#define ENCODER_OBJECT_ID_HDMI_INTERNAL 0x12 +/* Kaleidoscope (KLDSCP) Class Display Hardware (internal) */ +#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1 0x13 +#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1 0x14 +#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1 0x15 +#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2 0x16 /* Shared with CV/TV and CRT */ +#define ENCODER_OBJECT_ID_SI178 0X17 /* External TMDS (dual link, no HDCP.) */ +#define ENCODER_OBJECT_ID_MVPU_FPGA 0x18 /* MVPU FPGA chip */ +#define ENCODER_OBJECT_ID_INTERNAL_DDI 0x19 +#define ENCODER_OBJECT_ID_VT1625 0x1A +#define ENCODER_OBJECT_ID_HDMI_SI1932 0x1B +#define ENCODER_OBJECT_ID_DP_AN9801 0x1C +#define ENCODER_OBJECT_ID_DP_DP501 0x1D +#define ENCODER_OBJECT_ID_INTERNAL_UNIPHY 0x1E +#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA 0x1F +#define ENCODER_OBJECT_ID_INTERNAL_UNIPHY1 0x20 +#define ENCODER_OBJECT_ID_INTERNAL_UNIPHY2 0x21 + +#define ENCODER_OBJECT_ID_GENERAL_EXTERNAL_DVO 0xFF + +/****************************************************/ +/* Connector Object ID Definition */ +/****************************************************/ +#define CONNECTOR_OBJECT_ID_NONE 0x00 +#define CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I 0x01 +#define CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I 0x02 +#define CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D 0x03 +#define CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D 0x04 +#define CONNECTOR_OBJECT_ID_VGA 0x05 +#define CONNECTOR_OBJECT_ID_COMPOSITE 0x06 +#define CONNECTOR_OBJECT_ID_SVIDEO 0x07 +#define CONNECTOR_OBJECT_ID_YPbPr 0x08 +#define CONNECTOR_OBJECT_ID_D_CONNECTOR 0x09 +#define CONNECTOR_OBJECT_ID_9PIN_DIN 0x0A /* Supports both CV & TV */ +#define CONNECTOR_OBJECT_ID_SCART 0x0B +#define CONNECTOR_OBJECT_ID_HDMI_TYPE_A 0x0C +#define CONNECTOR_OBJECT_ID_HDMI_TYPE_B 0x0D +#define CONNECTOR_OBJECT_ID_LVDS 0x0E +#define CONNECTOR_OBJECT_ID_7PIN_DIN 0x0F +#define CONNECTOR_OBJECT_ID_PCIE_CONNECTOR 0x10 +#define CONNECTOR_OBJECT_ID_CROSSFIRE 0x11 +#define CONNECTOR_OBJECT_ID_HARDCODE_DVI 0x12 +#define CONNECTOR_OBJECT_ID_DISPLAYPORT 0x13 + +/* deleted */ + +/****************************************************/ +/* Router Object ID Definition */ +/****************************************************/ +#define ROUTER_OBJECT_ID_NONE 0x00 +#define ROUTER_OBJECT_ID_I2C_EXTENDER_CNTL 0x01 + +/****************************************************/ +// Graphics Object ENUM ID Definition */ +/****************************************************/ +#define GRAPH_OBJECT_ENUM_ID1 0x01 +#define GRAPH_OBJECT_ENUM_ID2 0x02 +#define GRAPH_OBJECT_ENUM_ID3 0x03 +#define GRAPH_OBJECT_ENUM_ID4 0x04 +#define GRAPH_OBJECT_ENUM_ID5 0x05 +#define GRAPH_OBJECT_ENUM_ID6 0x06 + +/****************************************************/ +/* Graphics Object ID Bit definition */ +/****************************************************/ +#define OBJECT_ID_MASK 0x00FF +#define ENUM_ID_MASK 0x0700 +#define RESERVED1_ID_MASK 0x0800 +#define OBJECT_TYPE_MASK 0x7000 +#define RESERVED2_ID_MASK 0x8000 + +#define OBJECT_ID_SHIFT 0x00 +#define ENUM_ID_SHIFT 0x08 +#define OBJECT_TYPE_SHIFT 0x0C + + +/****************************************************/ +/* Graphics Object family definition */ +/****************************************************/ +#define CONSTRUCTOBJECTFAMILYID(GRAPHICS_OBJECT_TYPE, GRAPHICS_OBJECT_ID) (GRAPHICS_OBJECT_TYPE << OBJECT_TYPE_SHIFT | \ + GRAPHICS_OBJECT_ID << OBJECT_ID_SHIFT) +/****************************************************/ +/* GPU Object ID definition - Shared with BIOS */ +/****************************************************/ +#define GPU_ENUM_ID1 ( GRAPH_OBJECT_TYPE_GPU << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT) + +/****************************************************/ +/* Encoder Object ID definition - Shared with BIOS */ +/****************************************************/ +/* +#define ENCODER_INTERNAL_LVDS_ENUM_ID1 0x2101 +#define ENCODER_INTERNAL_TMDS1_ENUM_ID1 0x2102 +#define ENCODER_INTERNAL_TMDS2_ENUM_ID1 0x2103 +#define ENCODER_INTERNAL_DAC1_ENUM_ID1 0x2104 +#define ENCODER_INTERNAL_DAC2_ENUM_ID1 0x2105 +#define ENCODER_INTERNAL_SDVOA_ENUM_ID1 0x2106 +#define ENCODER_INTERNAL_SDVOB_ENUM_ID1 0x2107 +#define ENCODER_SIL170B_ENUM_ID1 0x2108 +#define ENCODER_CH7303_ENUM_ID1 0x2109 +#define ENCODER_CH7301_ENUM_ID1 0x210A +#define ENCODER_INTERNAL_DVO1_ENUM_ID1 0x210B +#define ENCODER_EXTERNAL_SDVOA_ENUM_ID1 0x210C +#define ENCODER_EXTERNAL_SDVOB_ENUM_ID1 0x210D +#define ENCODER_TITFP513_ENUM_ID1 0x210E +#define ENCODER_INTERNAL_LVTM1_ENUM_ID1 0x210F +#define ENCODER_VT1623_ENUM_ID1 0x2110 +#define ENCODER_HDMI_SI1930_ENUM_ID1 0x2111 +#define ENCODER_HDMI_INTERNAL_ENUM_ID1 0x2112 +#define ENCODER_INTERNAL_KLDSCP_TMDS1_ENUM_ID1 0x2113 +#define ENCODER_INTERNAL_KLDSCP_DVO1_ENUM_ID1 0x2114 +#define ENCODER_INTERNAL_KLDSCP_DAC1_ENUM_ID1 0x2115 +#define ENCODER_INTERNAL_KLDSCP_DAC2_ENUM_ID1 0x2116 +#define ENCODER_SI178_ENUM_ID1 0x2117 +#define ENCODER_MVPU_FPGA_ENUM_ID1 0x2118 +#define ENCODER_INTERNAL_DDI_ENUM_ID1 0x2119 +#define ENCODER_VT1625_ENUM_ID1 0x211A +#define ENCODER_HDMI_SI1932_ENUM_ID1 0x211B +#define ENCODER_ENCODER_DP_AN9801_ENUM_ID1 0x211C +#define ENCODER_DP_DP501_ENUM_ID1 0x211D +#define ENCODER_INTERNAL_UNIPHY_ENUM_ID1 0x211E +*/ +#define ENCODER_INTERNAL_LVDS_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_LVDS << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_TMDS1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_TMDS1 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_TMDS2_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_TMDS2 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_DAC1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_DAC1 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_DAC2_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_DAC2 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_SDVOA_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_SDVOA << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_SDVOA_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_SDVOA << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_SDVOB_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_SDVOB << OBJECT_ID_SHIFT) + +#define ENCODER_SIL170B_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_SI170B << OBJECT_ID_SHIFT) + +#define ENCODER_CH7303_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_CH7303 << OBJECT_ID_SHIFT) + +#define ENCODER_CH7301_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_CH7301 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_DVO1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_DVO1 << OBJECT_ID_SHIFT) + +#define ENCODER_EXTERNAL_SDVOA_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_EXTERNAL_SDVOA << OBJECT_ID_SHIFT) + +#define ENCODER_EXTERNAL_SDVOA_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_EXTERNAL_SDVOA << OBJECT_ID_SHIFT) + + +#define ENCODER_EXTERNAL_SDVOB_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_EXTERNAL_SDVOB << OBJECT_ID_SHIFT) + + +#define ENCODER_TITFP513_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_TITFP513 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_LVTM1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_LVTM1 << OBJECT_ID_SHIFT) + +#define ENCODER_VT1623_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_VT1623 << OBJECT_ID_SHIFT) + +#define ENCODER_HDMI_SI1930_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_HDMI_SI1930 << OBJECT_ID_SHIFT) + +#define ENCODER_HDMI_INTERNAL_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_HDMI_INTERNAL << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_KLDSCP_TMDS1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1 << OBJECT_ID_SHIFT) + + +#define ENCODER_INTERNAL_KLDSCP_TMDS1_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1 << OBJECT_ID_SHIFT) + + +#define ENCODER_INTERNAL_KLDSCP_DVO1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_KLDSCP_DAC1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_KLDSCP_DAC2_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2 << OBJECT_ID_SHIFT) // Shared with CV/TV and CRT + +#define ENCODER_SI178_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_SI178 << OBJECT_ID_SHIFT) + +#define ENCODER_MVPU_FPGA_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_MVPU_FPGA << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_DDI_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_DDI << OBJECT_ID_SHIFT) + +#define ENCODER_VT1625_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_VT1625 << OBJECT_ID_SHIFT) + +#define ENCODER_HDMI_SI1932_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_HDMI_SI1932 << OBJECT_ID_SHIFT) + +#define ENCODER_DP_DP501_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_DP_DP501 << OBJECT_ID_SHIFT) + +#define ENCODER_DP_AN9801_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_DP_AN9801 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_UNIPHY_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_UNIPHY << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_UNIPHY_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_UNIPHY << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_KLDSCP_LVTMA_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_UNIPHY1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_UNIPHY1 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_UNIPHY1_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_UNIPHY1 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_UNIPHY2_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_UNIPHY2 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_UNIPHY2_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_UNIPHY2 << OBJECT_ID_SHIFT) + +#define ENCODER_GENERAL_EXTERNAL_DVO_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_GENERAL_EXTERNAL_DVO << OBJECT_ID_SHIFT) + +/****************************************************/ +/* Connector Object ID definition - Shared with BIOS */ +/****************************************************/ +/* +#define CONNECTOR_SINGLE_LINK_DVI_I_ENUM_ID1 0x3101 +#define CONNECTOR_DUAL_LINK_DVI_I_ENUM_ID1 0x3102 +#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID1 0x3103 +#define CONNECTOR_DUAL_LINK_DVI_D_ENUM_ID1 0x3104 +#define CONNECTOR_VGA_ENUM_ID1 0x3105 +#define CONNECTOR_COMPOSITE_ENUM_ID1 0x3106 +#define CONNECTOR_SVIDEO_ENUM_ID1 0x3107 +#define CONNECTOR_YPbPr_ENUM_ID1 0x3108 +#define CONNECTOR_D_CONNECTORE_ENUM_ID1 0x3109 +#define CONNECTOR_9PIN_DIN_ENUM_ID1 0x310A +#define CONNECTOR_SCART_ENUM_ID1 0x310B +#define CONNECTOR_HDMI_TYPE_A_ENUM_ID1 0x310C +#define CONNECTOR_HDMI_TYPE_B_ENUM_ID1 0x310D +#define CONNECTOR_LVDS_ENUM_ID1 0x310E +#define CONNECTOR_7PIN_DIN_ENUM_ID1 0x310F +#define CONNECTOR_PCIE_CONNECTOR_ENUM_ID1 0x3110 +*/ +#define CONNECTOR_LVDS_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_LVDS << OBJECT_ID_SHIFT) + +#define CONNECTOR_SINGLE_LINK_DVI_I_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I << OBJECT_ID_SHIFT) + +#define CONNECTOR_SINGLE_LINK_DVI_I_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I << OBJECT_ID_SHIFT) + +#define CONNECTOR_DUAL_LINK_DVI_I_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I << OBJECT_ID_SHIFT) + +#define CONNECTOR_DUAL_LINK_DVI_I_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I << OBJECT_ID_SHIFT) + +#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT) + +#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT) + +#define CONNECTOR_DUAL_LINK_DVI_D_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D << OBJECT_ID_SHIFT) + +#define CONNECTOR_VGA_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_VGA << OBJECT_ID_SHIFT) + +#define CONNECTOR_VGA_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_VGA << OBJECT_ID_SHIFT) + +#define CONNECTOR_COMPOSITE_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_COMPOSITE << OBJECT_ID_SHIFT) + +#define CONNECTOR_SVIDEO_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_SVIDEO << OBJECT_ID_SHIFT) + +#define CONNECTOR_YPbPr_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_YPbPr << OBJECT_ID_SHIFT) + +#define CONNECTOR_D_CONNECTOR_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_D_CONNECTOR << OBJECT_ID_SHIFT) + +#define CONNECTOR_9PIN_DIN_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_9PIN_DIN << OBJECT_ID_SHIFT) + +#define CONNECTOR_SCART_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_SCART << OBJECT_ID_SHIFT) + +#define CONNECTOR_HDMI_TYPE_A_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT) + +#define CONNECTOR_HDMI_TYPE_B_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_HDMI_TYPE_B << OBJECT_ID_SHIFT) + +#define CONNECTOR_7PIN_DIN_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_7PIN_DIN << OBJECT_ID_SHIFT) + +#define CONNECTOR_PCIE_CONNECTOR_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_PCIE_CONNECTOR << OBJECT_ID_SHIFT) + +#define CONNECTOR_PCIE_CONNECTOR_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_PCIE_CONNECTOR << OBJECT_ID_SHIFT) + +#define CONNECTOR_CROSSFIRE_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_CROSSFIRE << OBJECT_ID_SHIFT) + +#define CONNECTOR_CROSSFIRE_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_CROSSFIRE << OBJECT_ID_SHIFT) + + +#define CONNECTOR_HARDCODE_DVI_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_HARDCODE_DVI << OBJECT_ID_SHIFT) + +#define CONNECTOR_HARDCODE_DVI_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_HARDCODE_DVI << OBJECT_ID_SHIFT) + +#define CONNECTOR_DISPLAYPORT_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT) + +#define CONNECTOR_DISPLAYPORT_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT) + +#define CONNECTOR_DISPLAYPORT_ENUM_ID3 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID3 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT) + +#define CONNECTOR_DISPLAYPORT_ENUM_ID4 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID4 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT) + +/****************************************************/ +/* Router Object ID definition - Shared with BIOS */ +/****************************************************/ +#define ROUTER_I2C_EXTENDER_CNTL_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ROUTER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ROUTER_OBJECT_ID_I2C_EXTENDER_CNTL << OBJECT_ID_SHIFT) + +/* deleted */ + +/****************************************************/ +/* Object Cap definition - Shared with BIOS */ +/****************************************************/ +#define GRAPHICS_OBJECT_CAP_I2C 0x00000001L +#define GRAPHICS_OBJECT_CAP_TABLE_ID 0x00000002L + + +#define GRAPHICS_OBJECT_I2CCOMMAND_TABLE_ID 0x01 +#define GRAPHICS_OBJECT_HOTPLUGDETECTIONINTERUPT_TABLE_ID 0x02 +#define GRAPHICS_OBJECT_ENCODER_OUTPUT_PROTECTION_TABLE_ID 0x03 + +#if defined(_X86_) +#pragma pack() +#endif + +#endif /*GRAPHICTYPE */ + + + + diff --git a/linux-core/amd.h b/linux-core/amd.h new file mode 120000 index 00000000..b4882447 --- /dev/null +++ b/linux-core/amd.h @@ -0,0 +1 @@ +../shared-core/amd.h
\ No newline at end of file diff --git a/linux-core/amd_legacy.h b/linux-core/amd_legacy.h new file mode 120000 index 00000000..1a7786fc --- /dev/null +++ b/linux-core/amd_legacy.h @@ -0,0 +1 @@ +../shared-core/amd_legacy.h
\ No newline at end of file diff --git a/linux-core/amd_legacy_cbuffer.c b/linux-core/amd_legacy_cbuffer.c new file mode 120000 index 00000000..eab329b5 --- /dev/null +++ b/linux-core/amd_legacy_cbuffer.c @@ -0,0 +1 @@ +../shared-core/amd_legacy_cbuffer.c
\ No newline at end of file diff --git a/linux-core/amd_legacy_fence.h b/linux-core/amd_legacy_fence.h new file mode 120000 index 00000000..e7b30f25 --- /dev/null +++ b/linux-core/amd_legacy_fence.h @@ -0,0 +1 @@ +../shared-core/amd_legacy_fence.h
\ No newline at end of file diff --git a/linux-core/ati_pcigart.c b/linux-core/ati_pcigart.c index 40f8f8dd..6b0d8947 100644 --- a/linux-core/ati_pcigart.c +++ b/linux-core/ati_pcigart.c @@ -39,7 +39,7 @@ #define ATI_PCIE_WRITE 0x4 #define ATI_PCIE_READ 0x8 -static __inline__ void gart_insert_page_into_table(struct drm_ati_pcigart_info *gart_info, dma_addr_t addr, u32 *pci_gart) +static __inline__ void gart_insert_page_into_table(struct drm_ati_pcigart_info *gart_info, dma_addr_t addr, volatile u32 *pci_gart) { u32 page_base; @@ -61,8 +61,28 @@ static __inline__ void gart_insert_page_into_table(struct drm_ati_pcigart_info * *pci_gart = cpu_to_le32(page_base); } -static int drm_ati_alloc_pcigart_table(struct drm_device *dev, - struct drm_ati_pcigart_info *gart_info) +static __inline__ dma_addr_t gart_get_page_from_table(struct drm_ati_pcigart_info *gart_info, volatile u32 *pci_gart) +{ + dma_addr_t retval; + switch(gart_info->gart_reg_if) { + case DRM_ATI_GART_IGP: + retval = (*pci_gart & ATI_PCIGART_PAGE_MASK); + retval += (((*pci_gart & 0xf0) >> 4) << 16) << 16; + break; + case DRM_ATI_GART_PCIE: + retval = (*pci_gart & ~0xc); + retval <<= 8; + break; + case DRM_ATI_GART_PCI: + retval = *pci_gart; + break; + } + + return retval; +} + +int drm_ati_alloc_pcigart_table(struct drm_device *dev, + struct drm_ati_pcigart_info *gart_info) { gart_info->table_handle = drm_pci_alloc(dev, gart_info->table_size, PAGE_SIZE, @@ -70,12 +90,25 @@ static int drm_ati_alloc_pcigart_table(struct drm_device *dev, if (gart_info->table_handle == NULL) return -ENOMEM; +#ifdef CONFIG_X86 + /* IGPs only exist on x86 in any case */ + if (gart_info->gart_reg_if == DRM_ATI_GART_IGP) + set_memory_uc((unsigned long)gart_info->table_handle->vaddr, gart_info->table_size >> PAGE_SHIFT); +#endif + + memset(gart_info->table_handle->vaddr, 0, gart_info->table_size); return 0; } +EXPORT_SYMBOL(drm_ati_alloc_pcigart_table); static void drm_ati_free_pcigart_table(struct drm_device *dev, struct drm_ati_pcigart_info *gart_info) { +#ifdef CONFIG_X86 + /* IGPs only exist on x86 in any case */ + if (gart_info->gart_reg_if == DRM_ATI_GART_IGP) + set_memory_wb((unsigned long)gart_info->table_handle->vaddr, gart_info->table_size >> PAGE_SHIFT); +#endif drm_pci_free(dev, gart_info->table_handle); gart_info->table_handle = NULL; } @@ -89,7 +122,6 @@ int drm_ati_pcigart_cleanup(struct drm_device *dev, struct drm_ati_pcigart_info /* we need to support large memory configurations */ if (!entry) { - DRM_ERROR("no scatter/gather memory!\n"); return 0; } @@ -132,12 +164,8 @@ int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *ga int max_pages; dma_addr_t entry_addr; - if (!entry) { - DRM_ERROR("no scatter/gather memory!\n"); - goto done; - } - if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) { + if (gart_info->gart_table_location == DRM_ATI_GART_MAIN && gart_info->table_handle == NULL) { DRM_DEBUG("PCI: no table in VRAM: using normal RAM\n"); ret = drm_ati_alloc_pcigart_table(dev, gart_info); @@ -145,14 +173,19 @@ int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *ga DRM_ERROR("cannot allocate PCI GART page!\n"); goto done; } + } + if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) { address = gart_info->table_handle->vaddr; bus_address = gart_info->table_handle->busaddr; } else { address = gart_info->addr; bus_address = gart_info->bus_addr; - DRM_DEBUG("PCI: Gart Table: VRAM %08X mapped at %08lX\n", - bus_address, (unsigned long)address); + } + + if (!entry) { + DRM_ERROR("no scatter/gather memory!\n"); + goto done; } pci_gart = (u32 *) address; @@ -161,8 +194,6 @@ int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *ga pages = (entry->pages <= max_pages) ? entry->pages : max_pages; - memset(pci_gart, 0, max_pages * sizeof(u32)); - for (i = 0; i < pages; i++) { /* we need to support large memory configurations */ entry->busaddr[i] = pci_map_page(dev->pdev, entry->pagelist[i], @@ -185,11 +216,7 @@ int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *ga ret = 1; -#if defined(__i386__) || defined(__x86_64__) - wbinvd(); -#else mb(); -#endif done: gart_info->addr = address; @@ -197,3 +224,141 @@ 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, + struct page *dummy_read_page) +{ + struct ati_pcigart_ttm_backend *atipci_be = + container_of(backend, struct ati_pcigart_ttm_backend, backend); + + 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) +{ + struct ati_pcigart_ttm_backend *atipci_be = + container_of(backend, struct ati_pcigart_ttm_backend, backend); + off_t j; + int i; + struct drm_ati_pcigart_info *info = atipci_be->gart_info; + u32 *pci_gart; + dma_addr_t offset = bo_mem->mm_node->start; + dma_addr_t page_base; + + pci_gart = info->addr; + + j = offset; + while (j < (offset + atipci_be->num_pages)) { + if (gart_get_page_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); + gart_insert_page_into_table(info, page_base, pci_gart + j); + } + + mb(); + + 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) +{ + struct ati_pcigart_ttm_backend *atipci_be = + container_of(backend, struct ati_pcigart_ttm_backend, 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; + + 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) +{ + struct ati_pcigart_ttm_backend *atipci_be = + container_of(backend, struct ati_pcigart_ttm_backend, 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) +{ + struct ati_pcigart_ttm_backend *atipci_be; + if (backend) { + DRM_DEBUG("\n"); + atipci_be = container_of(backend, struct ati_pcigart_ttm_backend, 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)) +{ + struct ati_pcigart_ttm_backend *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->backend.mem_type = DRM_BO_MEM_TT; + 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/atom-bits.h b/linux-core/atom-bits.h new file mode 100644 index 00000000..f94d2e27 --- /dev/null +++ b/linux-core/atom-bits.h @@ -0,0 +1,48 @@ +/* + * Copyright 2008 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. + * + * Author: Stanislaw Skowronek + */ + +#ifndef ATOM_BITS_H +#define ATOM_BITS_H + +static inline uint8_t get_u8(void *bios, int ptr) +{ + return ((unsigned char *)bios)[ptr]; +} +#define U8(ptr) get_u8(ctx->ctx->bios,(ptr)) +#define CU8(ptr) get_u8(ctx->bios,(ptr)) +static inline uint16_t get_u16(void *bios, int ptr) +{ + return get_u8(bios,ptr)|(((uint16_t)get_u8(bios,ptr+1))<<8); +} +#define U16(ptr) get_u16(ctx->ctx->bios,(ptr)) +#define CU16(ptr) get_u16(ctx->bios,(ptr)) +static inline uint32_t get_u32(void *bios, int ptr) +{ + return get_u16(bios,ptr)|(((uint32_t)get_u16(bios,ptr+2))<<16); +} +#define U32(ptr) get_u32(ctx->ctx->bios,(ptr)) +#define CU32(ptr) get_u32(ctx->bios,(ptr)) +#define CSTR(ptr) (((char *)(ctx->bios))+(ptr)) + +#endif diff --git a/linux-core/atom-names.h b/linux-core/atom-names.h new file mode 100644 index 00000000..2cdc170b --- /dev/null +++ b/linux-core/atom-names.h @@ -0,0 +1,100 @@ +/* + * Copyright 2008 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. + * + * Author: Stanislaw Skowronek + */ + +#ifndef ATOM_NAMES_H +#define ATOM_NAMES_H + +#include "atom.h" + +#ifdef ATOM_DEBUG + +#define ATOM_OP_NAMES_CNT 123 +static char *atom_op_names[ATOM_OP_NAMES_CNT]={ +"RESERVED", "MOVE_REG", "MOVE_PS", "MOVE_WS", "MOVE_FB", "MOVE_PLL", +"MOVE_MC", "AND_REG", "AND_PS", "AND_WS", "AND_FB", "AND_PLL", "AND_MC", +"OR_REG", "OR_PS", "OR_WS", "OR_FB", "OR_PLL", "OR_MC", "SHIFT_LEFT_REG", +"SHIFT_LEFT_PS", "SHIFT_LEFT_WS", "SHIFT_LEFT_FB", "SHIFT_LEFT_PLL", +"SHIFT_LEFT_MC", "SHIFT_RIGHT_REG", "SHIFT_RIGHT_PS", "SHIFT_RIGHT_WS", +"SHIFT_RIGHT_FB", "SHIFT_RIGHT_PLL", "SHIFT_RIGHT_MC", "MUL_REG", +"MUL_PS", "MUL_WS", "MUL_FB", "MUL_PLL", "MUL_MC", "DIV_REG", "DIV_PS", +"DIV_WS", "DIV_FB", "DIV_PLL", "DIV_MC", "ADD_REG", "ADD_PS", "ADD_WS", +"ADD_FB", "ADD_PLL", "ADD_MC", "SUB_REG", "SUB_PS", "SUB_WS", "SUB_FB", +"SUB_PLL", "SUB_MC", "SET_ATI_PORT", "SET_PCI_PORT", "SET_SYS_IO_PORT", +"SET_REG_BLOCK", "SET_FB_BASE", "COMPARE_REG", "COMPARE_PS", +"COMPARE_WS", "COMPARE_FB", "COMPARE_PLL", "COMPARE_MC", "SWITCH", +"JUMP", "JUMP_EQUAL", "JUMP_BELOW", "JUMP_ABOVE", "JUMP_BELOW_OR_EQUAL", +"JUMP_ABOVE_OR_EQUAL", "JUMP_NOT_EQUAL", "TEST_REG", "TEST_PS", "TEST_WS", +"TEST_FB", "TEST_PLL", "TEST_MC", "DELAY_MILLISEC", "DELAY_MICROSEC", +"CALL_TABLE", "REPEAT", "CLEAR_REG", "CLEAR_PS", "CLEAR_WS", "CLEAR_FB", +"CLEAR_PLL", "CLEAR_MC", "NOP", "EOT", "MASK_REG", "MASK_PS", "MASK_WS", +"MASK_FB", "MASK_PLL", "MASK_MC", "POST_CARD", "BEEP", "SAVE_REG", +"RESTORE_REG", "SET_DATA_BLOCK", "XOR_REG", "XOR_PS", "XOR_WS", "XOR_FB", +"XOR_PLL", "XOR_MC", "SHL_REG", "SHL_PS", "SHL_WS", "SHL_FB", "SHL_PLL", +"SHL_MC", "SHR_REG", "SHR_PS", "SHR_WS", "SHR_FB", "SHR_PLL", "SHR_MC", +"DEBUG", "CTB_DS", +}; + +#define ATOM_TABLE_NAMES_CNT 74 +static char *atom_table_names[ATOM_TABLE_NAMES_CNT]={ +"ASIC_Init", "GetDisplaySurfaceSize", "ASIC_RegistersInit", +"VRAM_BlockVenderDetection", "SetClocksRatio", "MemoryControllerInit", +"GPIO_PinInit", "MemoryParamAdjust", "DVOEncoderControl", +"GPIOPinControl", "SetEngineClock", "SetMemoryClock", "SetPixelClock", +"DynamicClockGating", "ResetMemoryDLL", "ResetMemoryDevice", +"MemoryPLLInit", "EnableMemorySelfRefresh", "AdjustMemoryController", +"EnableASIC_StaticPwrMgt", "ASIC_StaticPwrMgtStatusChange", +"DAC_LoadDetection", "TMDS2EncoderControl", "LCD1OutputControl", +"DAC1EncoderControl", "DAC2EncoderControl", "DVOOutputControl", +"CV1OutputControl", "SetCRTC_DPM_State", "TVEncoderControl", +"TMDS1EncoderControl", "LVDSEncoderControl", "TV1OutputControl", +"EnableScaler", "BlankCRTC", "EnableCRTC", "GetPixelClock", +"EnableVGA_Render", "EnableVGA_Access", "SetCRTC_Timing", +"SetCRTC_OverScan", "SetCRTC_Replication", "SelectCRTC_Source", +"EnableGraphSurfaces", "UpdateCRTC_DoubleBufferRegisters", +"LUT_AutoFill", "EnableHW_IconCursor", "GetMemoryClock", +"GetEngineClock", "SetCRTC_UsingDTDTiming", "TVBootUpStdPinDetection", +"DFP2OutputControl", "VRAM_BlockDetectionByStrap", "MemoryCleanUp", +"ReadEDIDFromHWAssistedI2C", "WriteOneByteToHWAssistedI2C", +"ReadHWAssistedI2CStatus", "SpeedFanControl", "PowerConnectorDetection", +"MC_Synchronization", "ComputeMemoryEnginePLL", "MemoryRefreshConversion", +"VRAM_GetCurrentInfoBlock", "DynamicMemorySettings", "MemoryTraining", +"EnableLVDS_SS", "DFP1OutputControl", "SetVoltage", "CRT1OutputControl", +"CRT2OutputControl", "SetupHWAssistedI2CStatus", "ClockSource", +"MemoryDeviceInit", "EnableYUV", +}; + +#define ATOM_IO_NAMES_CNT 5 +static char *atom_io_names[ATOM_IO_NAMES_CNT]={ +"MM", "PLL", "MC", "PCIE", "PCIE PORT", +}; + +#else + +#define ATOM_OP_NAMES_CNT 0 +#define ATOM_TABLE_NAMES_CNT 0 +#define ATOM_IO_NAMES_CNT 0 + +#endif + +#endif diff --git a/linux-core/atom-types.h b/linux-core/atom-types.h new file mode 100644 index 00000000..1125b866 --- /dev/null +++ b/linux-core/atom-types.h @@ -0,0 +1,42 @@ +/* + * Copyright 2008 Red Hat 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. + * + * Author: Dave Airlie + */ + +#ifndef ATOM_TYPES_H +#define ATOM_TYPES_H + +/* sync atom types to kernel types */ + +typedef uint16_t USHORT; +typedef uint32_t ULONG; +typedef uint8_t UCHAR; + + +#ifndef ATOM_BIG_ENDIAN +#if defined(__BIG_ENDIAN) +#define ATOM_BIG_ENDIAN 1 +#else +#define ATOM_BIG_ENDIAN 0 +#endif +#endif +#endif diff --git a/linux-core/atom.c b/linux-core/atom.c new file mode 100644 index 00000000..2a660a4f --- /dev/null +++ b/linux-core/atom.c @@ -0,0 +1,1143 @@ +/* + * Copyright 2008 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. + * + * Author: Stanislaw Skowronek + */ + +#include <linux/module.h> +#include <linux/sched.h> + +#define ATOM_DEBUG + +#include "atom.h" +#include "atom-names.h" +#include "atom-bits.h" + +#define ATOM_COND_ABOVE 0 +#define ATOM_COND_ABOVEOREQUAL 1 +#define ATOM_COND_ALWAYS 2 +#define ATOM_COND_BELOW 3 +#define ATOM_COND_BELOWOREQUAL 4 +#define ATOM_COND_EQUAL 5 +#define ATOM_COND_NOTEQUAL 6 + +#define ATOM_PORT_ATI 0 +#define ATOM_PORT_PCI 1 +#define ATOM_PORT_SYSIO 2 + +#define ATOM_UNIT_MICROSEC 0 +#define ATOM_UNIT_MILLISEC 1 + +#define PLL_INDEX 2 +#define PLL_DATA 3 + +typedef struct { + struct atom_context *ctx; + + uint32_t *ps, *ws; + int ps_shift; + uint16_t start; +} atom_exec_context; + +int atom_debug = 0; +void atom_execute_table(struct atom_context *ctx, int index, uint32_t *params); + +static uint32_t atom_arg_mask[8] = {0xFFFFFFFF, 0xFFFF, 0xFFFF00, 0xFFFF0000, 0xFF, 0xFF00, 0xFF0000, 0xFF000000}; +static int atom_arg_shift[8] = {0, 0, 8, 16, 0, 8, 16, 24}; +static int atom_dst_to_src[8][4] = { // translate destination alignment field to the source alignment encoding + { 0, 0, 0, 0 }, + { 1, 2, 3, 0 }, + { 1, 2, 3, 0 }, + { 1, 2, 3, 0 }, + { 4, 5, 6, 7 }, + { 4, 5, 6, 7 }, + { 4, 5, 6, 7 }, + { 4, 5, 6, 7 }, +}; +static int atom_def_dst[8] = { 0, 0, 1, 2, 0, 1, 2, 3 }; + +static int debug_depth = 0; +#ifdef ATOM_DEBUG +static void debug_print_spaces(int n) +{ + while(n--) + printk(" "); +} +#define DEBUG(...) do if(atom_debug) { printk(KERN_DEBUG __VA_ARGS__); } while(0) +#define SDEBUG(...) do if(atom_debug) { printk(KERN_DEBUG); debug_print_spaces(debug_depth); printk(__VA_ARGS__); } while(0) +#else +#define DEBUG(...) do { } while(0) +#define SDEBUG(...) do { } while(0) +#endif + +static uint32_t atom_iio_execute(struct atom_context *ctx, int base, uint32_t index, uint32_t data) +{ + uint32_t temp = 0xCDCDCDCD; + while(1) + switch(CU8(base)) { + case ATOM_IIO_NOP: + base++; + break; + case ATOM_IIO_READ: + temp = ctx->card->reg_read(ctx->card, CU16(base+1)); + base+=3; + break; + case ATOM_IIO_WRITE: + ctx->card->reg_write(ctx->card, CU16(base+1), temp); + base+=3; + break; + case ATOM_IIO_CLEAR: + temp &= ~((0xFFFFFFFF >> (32-CU8(base+1))) << CU8(base+2)); + base+=3; + break; + case ATOM_IIO_SET: + temp |= (0xFFFFFFFF >> (32-CU8(base+1))) << CU8(base+2); + base+=3; + break; + case ATOM_IIO_MOVE_INDEX: + temp &= ~((0xFFFFFFFF >> (32-CU8(base+1))) << CU8(base+2)); + temp |= ((index >> CU8(base+2)) & (0xFFFFFFFF >> (32-CU8(base+1)))) << CU8(base+3); + base+=4; + break; + case ATOM_IIO_MOVE_DATA: + temp &= ~((0xFFFFFFFF >> (32-CU8(base+1))) << CU8(base+2)); + temp |= ((data >> CU8(base+2)) & (0xFFFFFFFF >> (32-CU8(base+1)))) << CU8(base+3); + base+=4; + break; + case ATOM_IIO_MOVE_ATTR: + temp &= ~((0xFFFFFFFF >> (32-CU8(base+1))) << CU8(base+2)); + temp |= ((ctx->io_attr >> CU8(base+2)) & (0xFFFFFFFF >> (32-CU8(base+1)))) << CU8(base+3); + base+=4; + break; + case ATOM_IIO_END: + return temp; + default: + printk(KERN_INFO "Unknown IIO opcode.\n"); + return 0; + } +} + +static uint32_t atom_get_src_int(atom_exec_context *ctx, uint8_t attr, int *ptr, uint32_t *saved, int print) +{ + uint32_t idx, val = 0xCDCDCDCD, align, arg; + struct atom_context *gctx = ctx->ctx; + arg = attr & 7; + align = (attr >> 3) & 7; + switch(arg) { + case ATOM_ARG_REG: + idx = U16(*ptr); + (*ptr)+=2; + if(print) + DEBUG("REG[0x%04X]", idx); + idx += gctx->reg_block; + switch(gctx->io_mode) { + case ATOM_IO_MM: + val = gctx->card->reg_read(gctx->card, idx); + break; + case ATOM_IO_PCI: + printk(KERN_INFO "PCI registers are not implemented.\n"); + return 0; + case ATOM_IO_SYSIO: + printk(KERN_INFO "SYSIO registers are not implemented.\n"); + return 0; + default: + if(!(gctx->io_mode&0x80)) { + printk(KERN_INFO "Bad IO mode.\n"); + return 0; + } + if(!gctx->iio[gctx->io_mode&0x7F]) { + printk(KERN_INFO "Undefined indirect IO read method %d.\n", gctx->io_mode&0x7F); + return 0; + } + val = atom_iio_execute(gctx, gctx->iio[gctx->io_mode&0x7F], idx, 0); + } + break; + case ATOM_ARG_PS: + idx = U8(*ptr); + (*ptr)++; + val = ctx->ps[idx]; + if(print) + DEBUG("PS[0x%02X,0x%04X]", idx, val); + break; + case ATOM_ARG_WS: + idx = U8(*ptr); + (*ptr)++; + if(print) + DEBUG("WS[0x%02X]", idx); + switch(idx) { + case ATOM_WS_QUOTIENT: + val = gctx->divmul[0]; + break; + case ATOM_WS_REMAINDER: + val = gctx->divmul[1]; + break; + case ATOM_WS_DATAPTR: + val = gctx->data_block; + break; + case ATOM_WS_SHIFT: + val = gctx->shift; + break; + case ATOM_WS_OR_MASK: + val = 1<<gctx->shift; + break; + case ATOM_WS_AND_MASK: + val = ~(1<<gctx->shift); + break; + case ATOM_WS_FB_WINDOW: + val = gctx->fb_base; + break; + case ATOM_WS_ATTRIBUTES: + val = gctx->io_attr; + break; + default: + val = ctx->ws[idx]; + } + break; + case ATOM_ARG_ID: + idx = U16(*ptr); + (*ptr)+=2; + if(print) { + if(gctx->data_block) + DEBUG("ID[0x%04X+%04X]", idx, gctx->data_block); + else + DEBUG("ID[0x%04X]", idx); + } + val = U32(idx + gctx->data_block); + break; + case ATOM_ARG_FB: + idx = U8(*ptr); + (*ptr)++; + if(print) + DEBUG("FB[0x%02X]", idx); + printk(KERN_INFO "FB access is not implemented.\n"); + return 0; + case ATOM_ARG_IMM: + switch(align) { + case ATOM_SRC_DWORD: + val = U32(*ptr); + (*ptr)+=4; + if(print) + DEBUG("IMM 0x%08X\n", val); + return val; + case ATOM_SRC_WORD0: + case ATOM_SRC_WORD8: + case ATOM_SRC_WORD16: + val = U16(*ptr); + (*ptr)+=2; + if(print) + DEBUG("IMM 0x%04X\n", val); + return val; + case ATOM_SRC_BYTE0: + case ATOM_SRC_BYTE8: + case ATOM_SRC_BYTE16: + case ATOM_SRC_BYTE24: + val = U8(*ptr); + (*ptr)++; + if(print) + DEBUG("IMM 0x%02X\n", val); + return val; + } + return 0; + case ATOM_ARG_PLL: + idx = U8(*ptr); + (*ptr)++; + if(print) + DEBUG("PLL[0x%02X]", idx); + gctx->card->reg_write(gctx->card, PLL_INDEX, idx); + val = gctx->card->reg_read(gctx->card, PLL_DATA); + break; + case ATOM_ARG_MC: + idx = U8(*ptr); + (*ptr)++; + if(print) + DEBUG("MC[0x%02X]", idx); + val = gctx->card->mc_read(gctx->card, idx); + return 0; + } + if(saved) + *saved = val; + val &= atom_arg_mask[align]; + val >>= atom_arg_shift[align]; + if(print) + switch(align) { + case ATOM_SRC_DWORD: + DEBUG(".[31:0] -> 0x%08X\n", val); + break; + case ATOM_SRC_WORD0: + DEBUG(".[15:0] -> 0x%04X\n", val); + break; + case ATOM_SRC_WORD8: + DEBUG(".[23:8] -> 0x%04X\n", val); + break; + case ATOM_SRC_WORD16: + DEBUG(".[31:16] -> 0x%04X\n", val); + break; + case ATOM_SRC_BYTE0: + DEBUG(".[7:0] -> 0x%02X\n", val); + break; + case ATOM_SRC_BYTE8: + DEBUG(".[15:8] -> 0x%02X\n", val); + break; + case ATOM_SRC_BYTE16: + DEBUG(".[23:16] -> 0x%02X\n", val); + break; + case ATOM_SRC_BYTE24: + DEBUG(".[31:24] -> 0x%02X\n", val); + break; + } + return val; +} + +static void atom_skip_src_int(atom_exec_context *ctx, uint8_t attr, int *ptr) +{ + uint32_t align = (attr >> 3) & 7, arg = attr & 7; + switch(arg) { + case ATOM_ARG_REG: + case ATOM_ARG_ID: + (*ptr)+=2; + break; + case ATOM_ARG_PLL: + case ATOM_ARG_MC: + case ATOM_ARG_PS: + case ATOM_ARG_WS: + case ATOM_ARG_FB: + (*ptr)++; + break; + case ATOM_ARG_IMM: + switch(align) { + case ATOM_SRC_DWORD: + (*ptr)+=4; + return; + case ATOM_SRC_WORD0: + case ATOM_SRC_WORD8: + case ATOM_SRC_WORD16: + (*ptr)+=2; + return; + case ATOM_SRC_BYTE0: + case ATOM_SRC_BYTE8: + case ATOM_SRC_BYTE16: + case ATOM_SRC_BYTE24: + (*ptr)++; + return; + } + return; + } +} + +static uint32_t atom_get_src(atom_exec_context *ctx, uint8_t attr, int *ptr) +{ + return atom_get_src_int(ctx, attr, ptr, NULL, 1); +} + +static uint32_t atom_get_dst(atom_exec_context *ctx, int arg, uint8_t attr, int *ptr, uint32_t *saved, int print) +{ + return atom_get_src_int(ctx, arg|atom_dst_to_src[(attr>>3)&7][(attr>>6)&3]<<3, ptr, saved, print); +} + +static void atom_skip_dst(atom_exec_context *ctx, int arg, uint8_t attr, int *ptr) +{ + atom_skip_src_int(ctx, arg|atom_dst_to_src[(attr>>3)&7][(attr>>6)&3]<<3, ptr); +} + +static void atom_put_dst(atom_exec_context *ctx, int arg, uint8_t attr, int *ptr, uint32_t val, uint32_t saved) +{ + uint32_t align = atom_dst_to_src[(attr>>3)&7][(attr>>6)&3], old_val = val, idx; + struct atom_context *gctx = ctx->ctx; + old_val &= atom_arg_mask[align] >> atom_arg_shift[align]; + val <<= atom_arg_shift[align]; + val &= atom_arg_mask[align]; + saved &= ~atom_arg_mask[align]; + val |= saved; + switch(arg) { + case ATOM_ARG_REG: + idx = U16(*ptr); + (*ptr)+=2; + DEBUG("REG[0x%04X]", idx); + idx += gctx->reg_block; + switch(gctx->io_mode) { + case ATOM_IO_MM: + if(idx == 0) + gctx->card->reg_write(gctx->card, idx, val<<2); + else + gctx->card->reg_write(gctx->card, idx, val); + break; + case ATOM_IO_PCI: + printk(KERN_INFO "PCI registers are not implemented.\n"); + return; + case ATOM_IO_SYSIO: + printk(KERN_INFO "SYSIO registers are not implemented.\n"); + return; + default: + if(!(gctx->io_mode&0x80)) { + printk(KERN_INFO "Bad IO mode.\n"); + return; + } + if(!gctx->iio[gctx->io_mode&0xFF]) { + printk(KERN_INFO "Undefined indirect IO write method %d.\n", gctx->io_mode&0x7F); + return; + } + atom_iio_execute(gctx, gctx->iio[gctx->io_mode&0xFF], idx, val); + } + break; + case ATOM_ARG_PS: + idx = U8(*ptr); + (*ptr)++; + DEBUG("PS[0x%02X]", idx); + ctx->ps[idx] = val; + break; + case ATOM_ARG_WS: + idx = U8(*ptr); + (*ptr)++; + DEBUG("WS[0x%02X]", idx); + switch(idx) { + case ATOM_WS_QUOTIENT: + gctx->divmul[0] = val; + break; + case ATOM_WS_REMAINDER: + gctx->divmul[1] = val; + break; + case ATOM_WS_DATAPTR: + gctx->data_block = val; + break; + case ATOM_WS_SHIFT: + gctx->shift = val; + break; + case ATOM_WS_OR_MASK: + case ATOM_WS_AND_MASK: + break; + case ATOM_WS_FB_WINDOW: + gctx->fb_base = val; + break; + case ATOM_WS_ATTRIBUTES: + gctx->io_attr = val; + break; + default: + ctx->ws[idx] = val; + } + break; + case ATOM_ARG_FB: + idx = U8(*ptr); + (*ptr)++; + DEBUG("FB[0x%02X]", idx); + printk(KERN_INFO "FB access is not implemented.\n"); + return; + case ATOM_ARG_PLL: + idx = U8(*ptr); + (*ptr)++; + DEBUG("PLL[0x%02X]", idx); + gctx->card->reg_write(gctx->card, PLL_INDEX, idx); + gctx->card->reg_write(gctx->card, PLL_DATA, val); + break; + case ATOM_ARG_MC: + idx = U8(*ptr); + (*ptr)++; + DEBUG("MC[0x%02X]", idx); + gctx->card->mc_write(gctx->card, idx, val); + return; + } + switch(align) { + case ATOM_SRC_DWORD: + DEBUG(".[31:0] <- 0x%08X\n", old_val); + break; + case ATOM_SRC_WORD0: + DEBUG(".[15:0] <- 0x%04X\n", old_val); + break; + case ATOM_SRC_WORD8: + DEBUG(".[23:8] <- 0x%04X\n", old_val); + break; + case ATOM_SRC_WORD16: + DEBUG(".[31:16] <- 0x%04X\n", old_val); + break; + case ATOM_SRC_BYTE0: + DEBUG(".[7:0] <- 0x%02X\n", old_val); + break; + case ATOM_SRC_BYTE8: + DEBUG(".[15:8] <- 0x%02X\n", old_val); + break; + case ATOM_SRC_BYTE16: + DEBUG(".[23:16] <- 0x%02X\n", old_val); + break; + case ATOM_SRC_BYTE24: + DEBUG(".[31:24] <- 0x%02X\n", old_val); + break; + } +} + +static void atom_op_add(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src, saved; + int dptr = *ptr; + SDEBUG(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + SDEBUG(" src: "); + src = atom_get_src(ctx, attr, ptr); + dst += src; + SDEBUG(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_and(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src, saved; + int dptr = *ptr; + SDEBUG(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + SDEBUG(" src: "); + src = atom_get_src(ctx, attr, ptr); + dst &= src; + SDEBUG(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_beep(atom_exec_context *ctx, int *ptr, int arg) +{ + printk("ATOM BIOS beeped!\n"); +} + +static void atom_op_calltable(atom_exec_context *ctx, int *ptr, int arg) +{ + int idx = U8((*ptr)++); + if(idx < ATOM_TABLE_NAMES_CNT) + SDEBUG(" table: %d (%s)\n", idx, atom_table_names[idx]); + else + SDEBUG(" table: %d\n", idx); + if(U16(ctx->ctx->cmd_table + 4 + 2*idx)) + atom_execute_table(ctx->ctx, idx, ctx->ps+ctx->ps_shift); +} + +static void atom_op_clear(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t saved; + int dptr = *ptr; + attr &= 0x38; + attr |= atom_def_dst[attr>>3]<<6; + atom_get_dst(ctx, arg, attr, ptr, &saved, 0); + SDEBUG(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, 0, saved); +} + +static void atom_op_compare(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src; + SDEBUG(" src1: "); + dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1); + SDEBUG(" src2: "); + src = atom_get_src(ctx, attr, ptr); + ctx->ctx->cs_equal = (dst == src); + ctx->ctx->cs_above = (dst > src); + SDEBUG(" result: %s %s\n", ctx->ctx->cs_equal?"EQ":"NE", ctx->ctx->cs_above?"GT":"LE"); +} + +static void atom_op_delay(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t count = U8((*ptr)++); + SDEBUG(" count: %d\n", count); + if(arg == ATOM_UNIT_MICROSEC) + schedule_timeout_uninterruptible(usecs_to_jiffies(count)); + else + schedule_timeout_uninterruptible(msecs_to_jiffies(count)); +} + +static void atom_op_div(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src; + SDEBUG(" src1: "); + dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1); + SDEBUG(" src2: "); + src = atom_get_src(ctx, attr, ptr); + if(src != 0) { + ctx->ctx->divmul[0] = dst/src; + ctx->ctx->divmul[1] = dst%src; + } else { + ctx->ctx->divmul[0] = 0; + ctx->ctx->divmul[1] = 0; + } +} + +static void atom_op_eot(atom_exec_context *ctx, int *ptr, int arg) +{ + /* functionally, a nop */ +} + +static void atom_op_jump(atom_exec_context *ctx, int *ptr, int arg) +{ + int execute = 0, target = U16(*ptr); + (*ptr)+=2; + switch(arg) { + case ATOM_COND_ABOVE: + execute = ctx->ctx->cs_above; + break; + case ATOM_COND_ABOVEOREQUAL: + execute = ctx->ctx->cs_above || ctx->ctx->cs_equal; + break; + case ATOM_COND_ALWAYS: + execute = 1; + break; + case ATOM_COND_BELOW: + execute = !(ctx->ctx->cs_above || ctx->ctx->cs_equal); + break; + case ATOM_COND_BELOWOREQUAL: + execute = !ctx->ctx->cs_above; + break; + case ATOM_COND_EQUAL: + execute = ctx->ctx->cs_equal; + break; + case ATOM_COND_NOTEQUAL: + execute = !ctx->ctx->cs_equal; + break; + } + if(arg != ATOM_COND_ALWAYS) + SDEBUG(" taken: %s\n", execute?"yes":"no"); + SDEBUG(" target: 0x%04X\n", target); + if(execute) + *ptr = ctx->start+target; +} + +static void atom_op_mask(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src1, src2, saved; + int dptr = *ptr; + SDEBUG(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + SDEBUG(" src1: "); + src1 = atom_get_src(ctx, attr, ptr); + SDEBUG(" src2: "); + src2 = atom_get_src(ctx, attr, ptr); + dst &= src1; + dst |= src2; + SDEBUG(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_move(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t src, saved; + int dptr = *ptr; + if(((attr>>3)&7) != ATOM_SRC_DWORD) + atom_get_dst(ctx, arg, attr, ptr, &saved, 0); + else { + atom_skip_dst(ctx, arg, attr, ptr); + saved = 0xCDCDCDCD; + } + SDEBUG(" src: "); + src = atom_get_src(ctx, attr, ptr); + SDEBUG(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, src, saved); +} + +static void atom_op_mul(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src; + SDEBUG(" src1: "); + dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1); + SDEBUG(" src2: "); + src = atom_get_src(ctx, attr, ptr); + ctx->ctx->divmul[0] = dst*src; +} + +static void atom_op_nop(atom_exec_context *ctx, int *ptr, int arg) +{ + /* nothing */ +} + +static void atom_op_or(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src, saved; + int dptr = *ptr; + SDEBUG(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + SDEBUG(" src: "); + src = atom_get_src(ctx, attr, ptr); + dst |= src; + SDEBUG(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_postcard(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t val = U8((*ptr)++); + SDEBUG("POST card output: 0x%02X\n", val); +} + +static void atom_op_repeat(atom_exec_context *ctx, int *ptr, int arg) +{ + printk(KERN_INFO "unimplemented!\n"); +} + +static void atom_op_restorereg(atom_exec_context *ctx, int *ptr, int arg) +{ + printk(KERN_INFO "unimplemented!\n"); +} + +static void atom_op_savereg(atom_exec_context *ctx, int *ptr, int arg) +{ + printk(KERN_INFO "unimplemented!\n"); +} + +static void atom_op_setdatablock(atom_exec_context *ctx, int *ptr, int arg) +{ + int idx = U8(*ptr); + (*ptr)++; + SDEBUG(" block: %d\n", idx); + if(!idx) + ctx->ctx->data_block = 0; + else if(idx==255) + ctx->ctx->data_block = ctx->start; + else + ctx->ctx->data_block = U16(ctx->ctx->data_table + 4 + 2*idx); + SDEBUG(" base: 0x%04X\n", ctx->ctx->data_block); +} + +static void atom_op_setfbbase(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + SDEBUG(" fb_base: "); + ctx->ctx->fb_base = atom_get_src(ctx, attr, ptr); +} + +static void atom_op_setport(atom_exec_context *ctx, int *ptr, int arg) +{ + int port; + switch(arg) { + case ATOM_PORT_ATI: + port = U16(*ptr); + if(port < ATOM_IO_NAMES_CNT) + SDEBUG(" port: %d (%s)\n", port, atom_io_names[port]); + else + SDEBUG(" port: %d\n", port); + if(!port) + ctx->ctx->io_mode = ATOM_IO_MM; + else + ctx->ctx->io_mode = ATOM_IO_IIO|port; + (*ptr)+=2; + break; + case ATOM_PORT_PCI: + ctx->ctx->io_mode = ATOM_IO_PCI; + (*ptr)++; + break; + case ATOM_PORT_SYSIO: + ctx->ctx->io_mode = ATOM_IO_SYSIO; + (*ptr)++; + break; + } +} + +static void atom_op_setregblock(atom_exec_context *ctx, int *ptr, int arg) +{ + ctx->ctx->reg_block = U16(*ptr); + (*ptr)+=2; + SDEBUG(" base: 0x%04X\n", ctx->ctx->reg_block); +} + +static void atom_op_shl(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++), shift; + uint32_t saved, dst; + int dptr = *ptr; + attr &= 0x38; + attr |= atom_def_dst[attr>>3]<<6; + SDEBUG(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + shift = U8((*ptr)++); + SDEBUG(" shift: %d\n", shift); + dst <<= shift; + SDEBUG(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_shr(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++), shift; + uint32_t saved, dst; + int dptr = *ptr; + attr &= 0x38; + attr |= atom_def_dst[attr>>3]<<6; + SDEBUG(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + shift = U8((*ptr)++); + SDEBUG(" shift: %d\n", shift); + dst >>= shift; + SDEBUG(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_sub(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src, saved; + int dptr = *ptr; + SDEBUG(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + SDEBUG(" src: "); + src = atom_get_src(ctx, attr, ptr); + dst -= src; + SDEBUG(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_switch(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t src, val, target; + SDEBUG(" switch: "); + src = atom_get_src(ctx, attr, ptr); + while(U16(*ptr) != ATOM_CASE_END) + if(U8(*ptr) == ATOM_CASE_MAGIC) { + (*ptr)++; + SDEBUG(" case: "); + val = atom_get_src(ctx, (attr&0x38)|ATOM_ARG_IMM, ptr); + target = U16(*ptr); + if(val == src) { + SDEBUG(" target: %04X\n", target); + *ptr = ctx->start+target; + return; + } + (*ptr) += 2; + } else { + printk(KERN_INFO "Bad case.\n"); + return; + } + (*ptr) += 2; +} + +static void atom_op_test(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src; + SDEBUG(" src1: "); + dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1); + SDEBUG(" src2: "); + src = atom_get_src(ctx, attr, ptr); + ctx->ctx->cs_equal = ((dst & src) == 0); + SDEBUG(" result: %s\n", ctx->ctx->cs_equal?"EQ":"NE"); +} + +static void atom_op_xor(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src, saved; + int dptr = *ptr; + SDEBUG(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + SDEBUG(" src: "); + src = atom_get_src(ctx, attr, ptr); + dst ^= src; + SDEBUG(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_debug(atom_exec_context *ctx, int *ptr, int arg) +{ + printk(KERN_INFO "unimplemented!\n"); +} + +static struct { + void (*func)(atom_exec_context *, int *, int); + int arg; +} opcode_table[ATOM_OP_CNT] = { + { NULL, 0 }, + { atom_op_move, ATOM_ARG_REG }, + { atom_op_move, ATOM_ARG_PS }, + { atom_op_move, ATOM_ARG_WS }, + { atom_op_move, ATOM_ARG_FB }, + { atom_op_move, ATOM_ARG_PLL }, + { atom_op_move, ATOM_ARG_MC }, + { atom_op_and, ATOM_ARG_REG }, + { atom_op_and, ATOM_ARG_PS }, + { atom_op_and, ATOM_ARG_WS }, + { atom_op_and, ATOM_ARG_FB }, + { atom_op_and, ATOM_ARG_PLL }, + { atom_op_and, ATOM_ARG_MC }, + { atom_op_or, ATOM_ARG_REG }, + { atom_op_or, ATOM_ARG_PS }, + { atom_op_or, ATOM_ARG_WS }, + { atom_op_or, ATOM_ARG_FB }, + { atom_op_or, ATOM_ARG_PLL }, + { atom_op_or, ATOM_ARG_MC }, + { atom_op_shl, ATOM_ARG_REG }, + { atom_op_shl, ATOM_ARG_PS }, + { atom_op_shl, ATOM_ARG_WS }, + { atom_op_shl, ATOM_ARG_FB }, + { atom_op_shl, ATOM_ARG_PLL }, + { atom_op_shl, ATOM_ARG_MC }, + { atom_op_shr, ATOM_ARG_REG }, + { atom_op_shr, ATOM_ARG_PS }, + { atom_op_shr, ATOM_ARG_WS }, + { atom_op_shr, ATOM_ARG_FB }, + { atom_op_shr, ATOM_ARG_PLL }, + { atom_op_shr, ATOM_ARG_MC }, + { atom_op_mul, ATOM_ARG_REG }, + { atom_op_mul, ATOM_ARG_PS }, + { atom_op_mul, ATOM_ARG_WS }, + { atom_op_mul, ATOM_ARG_FB }, + { atom_op_mul, ATOM_ARG_PLL }, + { atom_op_mul, ATOM_ARG_MC }, + { atom_op_div, ATOM_ARG_REG }, + { atom_op_div, ATOM_ARG_PS }, + { atom_op_div, ATOM_ARG_WS }, + { atom_op_div, ATOM_ARG_FB }, + { atom_op_div, ATOM_ARG_PLL }, + { atom_op_div, ATOM_ARG_MC }, + { atom_op_add, ATOM_ARG_REG }, + { atom_op_add, ATOM_ARG_PS }, + { atom_op_add, ATOM_ARG_WS }, + { atom_op_add, ATOM_ARG_FB }, + { atom_op_add, ATOM_ARG_PLL }, + { atom_op_add, ATOM_ARG_MC }, + { atom_op_sub, ATOM_ARG_REG }, + { atom_op_sub, ATOM_ARG_PS }, + { atom_op_sub, ATOM_ARG_WS }, + { atom_op_sub, ATOM_ARG_FB }, + { atom_op_sub, ATOM_ARG_PLL }, + { atom_op_sub, ATOM_ARG_MC }, + { atom_op_setport, ATOM_PORT_ATI }, + { atom_op_setport, ATOM_PORT_PCI }, + { atom_op_setport, ATOM_PORT_SYSIO }, + { atom_op_setregblock, 0 }, + { atom_op_setfbbase, 0 }, + { atom_op_compare, ATOM_ARG_REG }, + { atom_op_compare, ATOM_ARG_PS }, + { atom_op_compare, ATOM_ARG_WS }, + { atom_op_compare, ATOM_ARG_FB }, + { atom_op_compare, ATOM_ARG_PLL }, + { atom_op_compare, ATOM_ARG_MC }, + { atom_op_switch, 0 }, + { atom_op_jump, ATOM_COND_ALWAYS }, + { atom_op_jump, ATOM_COND_EQUAL }, + { atom_op_jump, ATOM_COND_BELOW }, + { atom_op_jump, ATOM_COND_ABOVE }, + { atom_op_jump, ATOM_COND_BELOWOREQUAL }, + { atom_op_jump, ATOM_COND_ABOVEOREQUAL }, + { atom_op_jump, ATOM_COND_NOTEQUAL }, + { atom_op_test, ATOM_ARG_REG }, + { atom_op_test, ATOM_ARG_PS }, + { atom_op_test, ATOM_ARG_WS }, + { atom_op_test, ATOM_ARG_FB }, + { atom_op_test, ATOM_ARG_PLL }, + { atom_op_test, ATOM_ARG_MC }, + { atom_op_delay, ATOM_UNIT_MILLISEC }, + { atom_op_delay, ATOM_UNIT_MICROSEC }, + { atom_op_calltable, 0 }, + { atom_op_repeat, 0 }, + { atom_op_clear, ATOM_ARG_REG }, + { atom_op_clear, ATOM_ARG_PS }, + { atom_op_clear, ATOM_ARG_WS }, + { atom_op_clear, ATOM_ARG_FB }, + { atom_op_clear, ATOM_ARG_PLL }, + { atom_op_clear, ATOM_ARG_MC }, + { atom_op_nop, 0 }, + { atom_op_eot, 0 }, + { atom_op_mask, ATOM_ARG_REG }, + { atom_op_mask, ATOM_ARG_PS }, + { atom_op_mask, ATOM_ARG_WS }, + { atom_op_mask, ATOM_ARG_FB }, + { atom_op_mask, ATOM_ARG_PLL }, + { atom_op_mask, ATOM_ARG_MC }, + { atom_op_postcard, 0 }, + { atom_op_beep, 0 }, + { atom_op_savereg, 0 }, + { atom_op_restorereg, 0 }, + { atom_op_setdatablock, 0 }, + { atom_op_xor, ATOM_ARG_REG }, + { atom_op_xor, ATOM_ARG_PS }, + { atom_op_xor, ATOM_ARG_WS }, + { atom_op_xor, ATOM_ARG_FB }, + { atom_op_xor, ATOM_ARG_PLL }, + { atom_op_xor, ATOM_ARG_MC }, + { atom_op_shl, ATOM_ARG_REG }, + { atom_op_shl, ATOM_ARG_PS }, + { atom_op_shl, ATOM_ARG_WS }, + { atom_op_shl, ATOM_ARG_FB }, + { atom_op_shl, ATOM_ARG_PLL }, + { atom_op_shl, ATOM_ARG_MC }, + { atom_op_shr, ATOM_ARG_REG }, + { atom_op_shr, ATOM_ARG_PS }, + { atom_op_shr, ATOM_ARG_WS }, + { atom_op_shr, ATOM_ARG_FB }, + { atom_op_shr, ATOM_ARG_PLL }, + { atom_op_shr, ATOM_ARG_MC }, + { atom_op_debug, 0 }, +}; + +void atom_execute_table(struct atom_context *ctx, int index, uint32_t *params) +{ + int base = CU16(ctx->cmd_table+4+2*index); + int len, ws, ps, ptr; + unsigned char op; + atom_exec_context ectx; + + if(!base) + return; + + len = CU16(base+ATOM_CT_SIZE_PTR); + ws = CU8(base+ATOM_CT_WS_PTR); + ps = CU8(base+ATOM_CT_PS_PTR) & ATOM_CT_PS_MASK; + ptr = base+ATOM_CT_CODE_PTR; + + SDEBUG(">> execute %04X (len %d, WS %d, PS %d)\n", base, len, ws, ps); + + /* reset reg block */ + ctx->reg_block = 0; + ectx.ctx = ctx; + ectx.ps_shift = ps/4; + ectx.start = base; + ectx.ps = params; + if(ws) + ectx.ws = kzalloc(4*ws, GFP_KERNEL); + else + ectx.ws = NULL; + + debug_depth++; + while(1) { + op = CU8(ptr++); + if(op<ATOM_OP_NAMES_CNT) + SDEBUG("%s @ 0x%04X\n", atom_op_names[op], ptr-1); + else + SDEBUG("[%d] @ 0x%04X\n", op, ptr-1); + + if(op<ATOM_OP_CNT && op>0) + opcode_table[op].func(&ectx, &ptr, opcode_table[op].arg); + else + break; + + if(op == ATOM_OP_EOT) + break; + } + debug_depth--; + SDEBUG("<<\n"); + + if(ws) + kfree(ectx.ws); +} + +static int atom_iio_len[] = { 1, 2, 3, 3, 3, 3, 4, 4, 4, 3 }; +static void atom_index_iio(struct atom_context *ctx, int base) +{ + ctx->iio = kzalloc(2*256, GFP_KERNEL); + while(CU8(base) == ATOM_IIO_START) { + ctx->iio[CU8(base+1)] = base+2; + base += 2; + while(CU8(base) != ATOM_IIO_END) + base += atom_iio_len[CU8(base)]; + base += 3; + } +} + +struct atom_context *atom_parse(struct card_info *card, void *bios) +{ + int base; + struct atom_context *ctx = kzalloc(sizeof(struct atom_context), GFP_KERNEL); + char *str; + + ctx->card = card; + ctx->bios = bios; + + if(CU16(0) != ATOM_BIOS_MAGIC) { + printk(KERN_INFO "Invalid BIOS magic.\n"); + kfree(ctx); + return NULL; + } + if(strncmp(CSTR(ATOM_ATI_MAGIC_PTR), ATOM_ATI_MAGIC, strlen(ATOM_ATI_MAGIC))) { + printk(KERN_INFO "Invalid ATI magic.\n"); + kfree(ctx); + return NULL; + } + + base = CU16(ATOM_ROM_TABLE_PTR); + if(strncmp(CSTR(base+ATOM_ROM_MAGIC_PTR), ATOM_ROM_MAGIC, strlen(ATOM_ROM_MAGIC))) { + printk(KERN_INFO "Invalid ATOM magic.\n"); + kfree(ctx); + return NULL; + } + + ctx->cmd_table = CU16(base+ATOM_ROM_CMD_PTR); + ctx->data_table = CU16(base+ATOM_ROM_DATA_PTR); + atom_index_iio(ctx, CU16(ctx->data_table+ATOM_DATA_IIO_PTR)+4); + + str = CSTR(CU16(base+ATOM_ROM_MSG_PTR)); + while(*str && ((*str == '\n') || (*str == '\r'))) + str++; + printk(KERN_INFO "ATOM BIOS: %s", str); + + return ctx; +} + +int atom_asic_init(struct atom_context *ctx) +{ + int hwi = CU16(ctx->data_table + ATOM_DATA_FWI_PTR); + uint32_t ps[16]; + memset(ps, 0, 64); + + ps[0] = CU32(hwi + ATOM_FWI_DEFSCLK_PTR); + ps[1] = CU32(hwi + ATOM_FWI_DEFMCLK_PTR); + if(!ps[0] || !ps[1]) + return 1; + + if(!CU16(ctx->cmd_table+4+2*ATOM_CMD_INIT)) + return 1; + atom_execute_table(ctx, ATOM_CMD_INIT, ps); + + return 0; +} + +void atom_destroy(struct atom_context *ctx) +{ + if(ctx->iio) + kfree(ctx->iio); + kfree(ctx); +} + + +void atom_parse_data_header(struct atom_context *ctx, int index, uint16_t *size, uint8_t *frev, uint8_t *crev, uint16_t *data_start) +{ + int offset = index * 2 + 4; + int idx = CU16(ctx->data_table + offset); + + if (size) + *size = CU16(idx); + if (frev) + *frev = CU8(idx + 2); + if (crev) + *crev = CU8(idx + 3); + *data_start = idx; + return; +} + +void atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t *frev, uint8_t *crev) +{ + int offset = index * 2 + 4; + int idx = CU16(ctx->cmd_table + offset); + + if (frev) + *frev = CU8(idx + 2); + if (crev) + *crev = CU8(idx + 3); + return; +} diff --git a/linux-core/atom.h b/linux-core/atom.h new file mode 100644 index 00000000..a5d93322 --- /dev/null +++ b/linux-core/atom.h @@ -0,0 +1,148 @@ +/* + * Copyright 2008 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. + * + * Author: Stanislaw Skowronek + */ + +#ifndef ATOM_H +#define ATOM_H + +#include <linux/types.h> +#include "drmP.h" + +#define ATOM_BIOS_MAGIC 0xAA55 +#define ATOM_ATI_MAGIC_PTR 0x30 +#define ATOM_ATI_MAGIC " 761295520" +#define ATOM_ROM_TABLE_PTR 0x48 + +#define ATOM_ROM_MAGIC "ATOM" +#define ATOM_ROM_MAGIC_PTR 4 + +#define ATOM_ROM_MSG_PTR 0x10 +#define ATOM_ROM_CMD_PTR 0x1E +#define ATOM_ROM_DATA_PTR 0x20 + +#define ATOM_CMD_INIT 0 +#define ATOM_CMD_SETSCLK 0x0A +#define ATOM_CMD_SETMCLK 0x0B +#define ATOM_CMD_SETPCLK 0x0C + +#define ATOM_DATA_FWI_PTR 0xC +#define ATOM_DATA_IIO_PTR 0x32 + +#define ATOM_FWI_DEFSCLK_PTR 8 +#define ATOM_FWI_DEFMCLK_PTR 0xC +#define ATOM_FWI_MAXSCLK_PTR 0x24 +#define ATOM_FWI_MAXMCLK_PTR 0x28 + +#define ATOM_CT_SIZE_PTR 0 +#define ATOM_CT_WS_PTR 4 +#define ATOM_CT_PS_PTR 5 +#define ATOM_CT_PS_MASK 0x7F +#define ATOM_CT_CODE_PTR 6 + +#define ATOM_OP_CNT 123 +#define ATOM_OP_EOT 91 + +#define ATOM_CASE_MAGIC 0x63 +#define ATOM_CASE_END 0x5A5A + +#define ATOM_ARG_REG 0 +#define ATOM_ARG_PS 1 +#define ATOM_ARG_WS 2 +#define ATOM_ARG_ID 4 +#define ATOM_ARG_FB 3 +#define ATOM_ARG_IMM 5 +#define ATOM_ARG_PLL 6 +#define ATOM_ARG_MC 7 + +#define ATOM_SRC_DWORD 0 +#define ATOM_SRC_WORD0 1 +#define ATOM_SRC_WORD8 2 +#define ATOM_SRC_WORD16 3 +#define ATOM_SRC_BYTE0 4 +#define ATOM_SRC_BYTE8 5 +#define ATOM_SRC_BYTE16 6 +#define ATOM_SRC_BYTE24 7 + +#define ATOM_WS_QUOTIENT 0x40 +#define ATOM_WS_REMAINDER 0x41 +#define ATOM_WS_DATAPTR 0x42 +#define ATOM_WS_SHIFT 0x43 +#define ATOM_WS_OR_MASK 0x44 +#define ATOM_WS_AND_MASK 0x45 +#define ATOM_WS_FB_WINDOW 0x46 +#define ATOM_WS_ATTRIBUTES 0x47 + +#define ATOM_IIO_NOP 0 +#define ATOM_IIO_START 1 +#define ATOM_IIO_READ 2 +#define ATOM_IIO_WRITE 3 +#define ATOM_IIO_CLEAR 4 +#define ATOM_IIO_SET 5 +#define ATOM_IIO_MOVE_INDEX 6 +#define ATOM_IIO_MOVE_ATTR 7 +#define ATOM_IIO_MOVE_DATA 8 +#define ATOM_IIO_END 9 + +#define ATOM_IO_MM 0 +#define ATOM_IO_PCI 1 +#define ATOM_IO_SYSIO 2 +#define ATOM_IO_IIO 0x80 + +struct card_info { + struct drm_device *dev; + void (* reg_write)(struct card_info *, uint32_t, uint32_t); // filled by driver + uint32_t (* reg_read)(struct card_info *, uint32_t); // filled by driver + void (* mc_write)(struct card_info *, uint32_t, uint32_t); // filled by driver + uint32_t (* mc_read)(struct card_info *, uint32_t); // filled by driver +// int (* read_rom)(struct card_info *, uint8_t *); // filled by driver +}; + +struct atom_context { + struct card_info *card; + void *bios; + uint32_t cmd_table, data_table; + uint16_t *iio; + + uint16_t data_block; + uint32_t fb_base; + uint32_t divmul[2]; + uint16_t io_attr; + uint16_t reg_block; + uint8_t shift; + int cs_equal, cs_above; + int io_mode; +}; + +extern int atom_debug; + +struct atom_context *atom_parse(struct card_info *, void *); +void atom_execute_table(struct atom_context *, int, uint32_t *); +int atom_asic_init(struct atom_context *); +void atom_destroy(struct atom_context *); +void atom_parse_data_header(struct atom_context *ctx, int index, uint16_t *size, uint8_t *frev, uint8_t *crev, uint16_t *data_start); +void atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t *frev, uint8_t *crev); +#include "atom-types.h" +#include "atombios.h" +#include "ObjectID.h" + +#endif diff --git a/linux-core/atombios.h b/linux-core/atombios.h new file mode 100644 index 00000000..9932b096 --- /dev/null +++ b/linux-core/atombios.h @@ -0,0 +1,5025 @@ +/* + * Copyright 2006-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. + */ + + +/****************************************************************************/ +/*Portion I: Definitions shared between VBIOS and Driver */ +/****************************************************************************/ + + +#ifndef _ATOMBIOS_H +#define _ATOMBIOS_H + +#define ATOM_VERSION_MAJOR 0x00020000 +#define ATOM_VERSION_MINOR 0x00000002 + +#define ATOM_HEADER_VERSION (ATOM_VERSION_MAJOR | ATOM_VERSION_MINOR) + +/* Endianness should be specified before inclusion, + * default to little endian + */ +#ifndef ATOM_BIG_ENDIAN +#error Endian not specified +#endif + +#ifdef _H2INC + #ifndef ULONG + typedef unsigned long ULONG; + #endif + + #ifndef UCHAR + typedef unsigned char UCHAR; + #endif + + #ifndef USHORT + typedef unsigned short USHORT; + #endif +#endif + +#define ATOM_DAC_A 0 +#define ATOM_DAC_B 1 +#define ATOM_EXT_DAC 2 + +#define ATOM_CRTC1 0 +#define ATOM_CRTC2 1 + +#define ATOM_DIGA 0 +#define ATOM_DIGB 1 + +#define ATOM_PPLL1 0 +#define ATOM_PPLL2 1 + +#define ATOM_SCALER1 0 +#define ATOM_SCALER2 1 + +#define ATOM_SCALER_DISABLE 0 +#define ATOM_SCALER_CENTER 1 +#define ATOM_SCALER_EXPANSION 2 +#define ATOM_SCALER_MULTI_EX 3 + +#define ATOM_DISABLE 0 +#define ATOM_ENABLE 1 +#define ATOM_LCD_BLOFF (ATOM_DISABLE+2) +#define ATOM_LCD_BLON (ATOM_ENABLE+2) +#define ATOM_LCD_BL_BRIGHTNESS_CONTROL (ATOM_ENABLE+3) +#define ATOM_LCD_SELFTEST_START (ATOM_DISABLE+5) +#define ATOM_LCD_SELFTEST_STOP (ATOM_ENABLE+5) +#define ATOM_ENCODER_INIT (ATOM_DISABLE+7) + +#define ATOM_BLANKING 1 +#define ATOM_BLANKING_OFF 0 + +#define ATOM_CURSOR1 0 +#define ATOM_CURSOR2 1 + +#define ATOM_ICON1 0 +#define ATOM_ICON2 1 + +#define ATOM_CRT1 0 +#define ATOM_CRT2 1 + +#define ATOM_TV_NTSC 1 +#define ATOM_TV_NTSCJ 2 +#define ATOM_TV_PAL 3 +#define ATOM_TV_PALM 4 +#define ATOM_TV_PALCN 5 +#define ATOM_TV_PALN 6 +#define ATOM_TV_PAL60 7 +#define ATOM_TV_SECAM 8 +#define ATOM_TV_CV 16 + +#define ATOM_DAC1_PS2 1 +#define ATOM_DAC1_CV 2 +#define ATOM_DAC1_NTSC 3 +#define ATOM_DAC1_PAL 4 + +#define ATOM_DAC2_PS2 ATOM_DAC1_PS2 +#define ATOM_DAC2_CV ATOM_DAC1_CV +#define ATOM_DAC2_NTSC ATOM_DAC1_NTSC +#define ATOM_DAC2_PAL ATOM_DAC1_PAL + +#define ATOM_PM_ON 0 +#define ATOM_PM_STANDBY 1 +#define ATOM_PM_SUSPEND 2 +#define ATOM_PM_OFF 3 + +/* Bit0:{=0:single, =1:dual}, + Bit1 {=0:666RGB, =1:888RGB}, + Bit2:3:{Grey level} + Bit4:{=0:LDI format for RGB888, =1 FPDI format for RGB888}*/ + +#define ATOM_PANEL_MISC_DUAL 0x00000001 +#define ATOM_PANEL_MISC_888RGB 0x00000002 +#define ATOM_PANEL_MISC_GREY_LEVEL 0x0000000C +#define ATOM_PANEL_MISC_FPDI 0x00000010 +#define ATOM_PANEL_MISC_GREY_LEVEL_SHIFT 2 +#define ATOM_PANEL_MISC_SPATIAL 0x00000020 +#define ATOM_PANEL_MISC_TEMPORAL 0x00000040 +#define ATOM_PANEL_MISC_API_ENABLED 0x00000080 + + +#define MEMTYPE_DDR1 "DDR1" +#define MEMTYPE_DDR2 "DDR2" +#define MEMTYPE_DDR3 "DDR3" +#define MEMTYPE_DDR4 "DDR4" + +#define ASIC_BUS_TYPE_PCI "PCI" +#define ASIC_BUS_TYPE_AGP "AGP" +#define ASIC_BUS_TYPE_PCIE "PCI_EXPRESS" + +/* Maximum size of that FireGL flag string */ + +#define ATOM_FIREGL_FLAG_STRING "FGL" //Flag used to enable FireGL Support +#define ATOM_MAX_SIZE_OF_FIREGL_FLAG_STRING 3 //sizeof( ATOM_FIREGL_FLAG_STRING ) + +#define ATOM_FAKE_DESKTOP_STRING "DSK" //Flag used to enable mobile ASIC on Desktop +#define ATOM_MAX_SIZE_OF_FAKE_DESKTOP_STRING ATOM_MAX_SIZE_OF_FIREGL_FLAG_STRING + +#define ATOM_M54T_FLAG_STRING "M54T" //Flag used to enable M54T Support +#define ATOM_MAX_SIZE_OF_M54T_FLAG_STRING 4 //sizeof( ATOM_M54T_FLAG_STRING ) + +#define HW_ASSISTED_I2C_STATUS_FAILURE 2 +#define HW_ASSISTED_I2C_STATUS_SUCCESS 1 + +#pragma pack(1) /* BIOS data must use byte aligment */ + +/* Define offset to location of ROM header. */ + +#define OFFSET_TO_POINTER_TO_ATOM_ROM_HEADER 0x00000048L +#define OFFSET_TO_ATOM_ROM_IMAGE_SIZE 0x00000002L + +#define OFFSET_TO_ATOMBIOS_ASIC_BUS_MEM_TYPE 0x94 +#define MAXSIZE_OF_ATOMBIOS_ASIC_BUS_MEM_TYPE 20 /* including the terminator 0x0! */ +#define OFFSET_TO_GET_ATOMBIOS_STRINGS_NUMBER 0x002f +#define OFFSET_TO_GET_ATOMBIOS_STRINGS_START 0x006e + +/* Common header for all ROM Data tables. + Every table pointed _ATOM_MASTER_DATA_TABLE has this common header. + And the pointer actually points to this header. */ + +typedef struct _ATOM_COMMON_TABLE_HEADER +{ + USHORT usStructureSize; + UCHAR ucTableFormatRevision; /*Change it when the Parser is not backward compatible */ + UCHAR ucTableContentRevision; /*Change it only when the table needs to change but the firmware */ + /*Image can't be updated, while Driver needs to carry the new table! */ +}ATOM_COMMON_TABLE_HEADER; + +typedef struct _ATOM_ROM_HEADER +{ + ATOM_COMMON_TABLE_HEADER sHeader; + UCHAR uaFirmWareSignature[4]; /*Signature to distinguish between Atombios and non-atombios, + atombios should init it as "ATOM", don't change the position */ + USHORT usBiosRuntimeSegmentAddress; + USHORT usProtectedModeInfoOffset; + USHORT usConfigFilenameOffset; + USHORT usCRC_BlockOffset; + USHORT usBIOS_BootupMessageOffset; + USHORT usInt10Offset; + USHORT usPciBusDevInitCode; + USHORT usIoBaseAddress; + USHORT usSubsystemVendorID; + USHORT usSubsystemID; + USHORT usPCI_InfoOffset; + USHORT usMasterCommandTableOffset; /*Offset for SW to get all command table offsets, Don't change the position */ + USHORT usMasterDataTableOffset; /*Offset for SW to get all data table offsets, Don't change the position */ + UCHAR ucExtendedFunctionCode; + UCHAR ucReserved; +}ATOM_ROM_HEADER; + +/*==============================Command Table Portion==================================== */ + +#ifdef UEFI_BUILD + #define UTEMP USHORT + #define USHORT void* +#endif + +typedef struct _ATOM_MASTER_LIST_OF_COMMAND_TABLES{ + USHORT ASIC_Init; //Function Table, used by various SW components,latest version 1.1 + USHORT GetDisplaySurfaceSize; //Atomic Table, Used by Bios when enabling HW ICON + USHORT ASIC_RegistersInit; //Atomic Table, indirectly used by various SW components,called from ASIC_Init + USHORT VRAM_BlockVenderDetection; //Atomic Table, used only by Bios + USHORT DIGxEncoderControl; //Only used by Bios + USHORT MemoryControllerInit; //Atomic Table, indirectly used by various SW components,called from ASIC_Init + USHORT EnableCRTCMemReq; //Function Table,directly used by various SW components,latest version 2.1 + USHORT MemoryParamAdjust; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock if needed + USHORT DVOEncoderControl; //Function Table,directly used by various SW components,latest version 1.2 + USHORT GPIOPinControl; //Atomic Table, only used by Bios + USHORT SetEngineClock; //Function Table,directly used by various SW components,latest version 1.1 + USHORT SetMemoryClock; //Function Table,directly used by various SW components,latest version 1.1 + USHORT SetPixelClock; //Function Table,directly used by various SW components,latest version 1.2 + USHORT DynamicClockGating; //Atomic Table, indirectly used by various SW components,called from ASIC_Init + USHORT ResetMemoryDLL; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock + USHORT ResetMemoryDevice; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock + USHORT MemoryPLLInit; + USHORT AdjustDisplayPll; //only used by Bios + USHORT AdjustMemoryController; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock + USHORT EnableASIC_StaticPwrMgt; //Atomic Table, only used by Bios + USHORT ASIC_StaticPwrMgtStatusChange; //Obsolete , only used by Bios + USHORT DAC_LoadDetection; //Atomic Table, directly used by various SW components,latest version 1.2 + USHORT LVTMAEncoderControl; //Atomic Table,directly used by various SW components,latest version 1.3 + USHORT LCD1OutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT DAC1EncoderControl; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT DAC2EncoderControl; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT DVOOutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT CV1OutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT GetConditionalGoldenSetting; //only used by Bios + USHORT TVEncoderControl; //Function Table,directly used by various SW components,latest version 1.1 + USHORT TMDSAEncoderControl; //Atomic Table, directly used by various SW components,latest version 1.3 + USHORT LVDSEncoderControl; //Atomic Table, directly used by various SW components,latest version 1.3 + USHORT TV1OutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT EnableScaler; //Atomic Table, used only by Bios + USHORT BlankCRTC; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT EnableCRTC; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT GetPixelClock; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT EnableVGA_Render; //Function Table,directly used by various SW components,latest version 1.1 + USHORT EnableVGA_Access; //Obsolete , only used by Bios + USHORT SetCRTC_Timing; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT SetCRTC_OverScan; //Atomic Table, used by various SW components,latest version 1.1 + USHORT SetCRTC_Replication; //Atomic Table, used only by Bios + USHORT SelectCRTC_Source; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT EnableGraphSurfaces; //Atomic Table, used only by Bios + USHORT UpdateCRTC_DoubleBufferRegisters; + USHORT LUT_AutoFill; //Atomic Table, only used by Bios + USHORT EnableHW_IconCursor; //Atomic Table, only used by Bios + USHORT GetMemoryClock; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT GetEngineClock; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT SetCRTC_UsingDTDTiming; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT ExternalEncoderControl; //Atomic Table, directly used by various SW components,latest version 2.1 + USHORT LVTMAOutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT VRAM_BlockDetectionByStrap; //Atomic Table, used only by Bios + USHORT MemoryCleanUp; //Atomic Table, only used by Bios + USHORT ProcessI2cChannelTransaction; //Function Table,only used by Bios + USHORT WriteOneByteToHWAssistedI2C; //Function Table,indirectly used by various SW components + USHORT ReadHWAssistedI2CStatus; //Atomic Table, indirectly used by various SW components + USHORT SpeedFanControl; //Function Table,indirectly used by various SW components,called from ASIC_Init + USHORT PowerConnectorDetection; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT MC_Synchronization; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock + USHORT ComputeMemoryEnginePLL; //Atomic Table, indirectly used by various SW components,called from SetMemory/EngineClock + USHORT MemoryRefreshConversion; //Atomic Table, indirectly used by various SW components,called from SetMemory or SetEngineClock + USHORT VRAM_GetCurrentInfoBlock; //Atomic Table, used only by Bios + USHORT DynamicMemorySettings; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock + USHORT MemoryTraining; //Atomic Table, used only by Bios + USHORT EnableSpreadSpectrumOnPPLL; //Atomic Table, directly used by various SW components,latest version 1.2 + USHORT TMDSAOutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT SetVoltage; //Function Table,directly and/or indirectly used by various SW components,latest version 1.1 + USHORT DAC1OutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT DAC2OutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT SetupHWAssistedI2CStatus; //Function Table,only used by Bios, obsolete soon.Switch to use "ReadEDIDFromHWAssistedI2C" + USHORT ClockSource; //Atomic Table, indirectly used by various SW components,called from ASIC_Init + USHORT MemoryDeviceInit; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock + USHORT EnableYUV; //Atomic Table, indirectly used by various SW components,called from EnableVGARender + USHORT DIG1EncoderControl; //Atomic Table,directly used by various SW components,latest version 1.1 + USHORT DIG2EncoderControl; //Atomic Table,directly used by various SW components,latest version 1.1 + USHORT DIG1TransmitterControl; //Atomic Table,directly used by various SW components,latest version 1.1 + USHORT DIG2TransmitterControl; //Atomic Table,directly used by various SW components,latest version 1.1 + USHORT ProcessAuxChannelTransaction; //Function Table,only used by Bios + USHORT DPEncoderService; //Function Table,only used by Bios +}ATOM_MASTER_LIST_OF_COMMAND_TABLES; + +// For backward compatible +#define ReadEDIDFromHWAssistedI2C ProcessI2cChannelTransaction +#define UNIPHYTransmitterControl DIG1TransmitterControl +#define LVTMATransmitterControl DIG2TransmitterControl +#define SetCRTC_DPM_State GetConditionalGoldenSetting +#define SetUniphyInstance ASIC_StaticPwrMgtStatusChange + +typedef struct _ATOM_MASTER_COMMAND_TABLE +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_MASTER_LIST_OF_COMMAND_TABLES ListOfCommandTables; +}ATOM_MASTER_COMMAND_TABLE; + +/****************************************************************************/ +// Structures used in every command table +/****************************************************************************/ +typedef struct _ATOM_TABLE_ATTRIBUTE +{ +#if ATOM_BIG_ENDIAN + USHORT UpdatedByUtility:1; //[15]=Table updated by utility flag + USHORT PS_SizeInBytes:7; //[14:8]=Size of parameter space in Bytes (multiple of a dword), + USHORT WS_SizeInBytes:8; //[7:0]=Size of workspace in Bytes (in multiple of a dword), +#else + USHORT WS_SizeInBytes:8; //[7:0]=Size of workspace in Bytes (in multiple of a dword), + USHORT PS_SizeInBytes:7; //[14:8]=Size of parameter space in Bytes (multiple of a dword), + USHORT UpdatedByUtility:1; //[15]=Table updated by utility flag +#endif +}ATOM_TABLE_ATTRIBUTE; + +typedef union _ATOM_TABLE_ATTRIBUTE_ACCESS +{ + ATOM_TABLE_ATTRIBUTE sbfAccess; + USHORT susAccess; +}ATOM_TABLE_ATTRIBUTE_ACCESS; + +/****************************************************************************/ +// Common header for all command tables. +// Every table pointed by _ATOM_MASTER_COMMAND_TABLE has this common header. +// And the pointer actually points to this header. +/****************************************************************************/ +typedef struct _ATOM_COMMON_ROM_COMMAND_TABLE_HEADER +{ + ATOM_COMMON_TABLE_HEADER CommonHeader; + ATOM_TABLE_ATTRIBUTE TableAttribute; +}ATOM_COMMON_ROM_COMMAND_TABLE_HEADER; + +/****************************************************************************/ +// Structures used by ComputeMemoryEnginePLLTable +/****************************************************************************/ +#define COMPUTE_MEMORY_PLL_PARAM 1 +#define COMPUTE_ENGINE_PLL_PARAM 2 + +typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS +{ + ULONG ulClock; //When returen, it's the re-calculated clock based on given Fb_div Post_Div and ref_div + UCHAR ucAction; //0:reserved //1:Memory //2:Engine + UCHAR ucReserved; //may expand to return larger Fbdiv later + UCHAR ucFbDiv; //return value + UCHAR ucPostDiv; //return value +}COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS; + +typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V2 +{ + ULONG ulClock; //When return, [23:0] return real clock + UCHAR ucAction; //0:reserved;COMPUTE_MEMORY_PLL_PARAM:Memory;COMPUTE_ENGINE_PLL_PARAM:Engine. it return ref_div to be written to register + USHORT usFbDiv; //return Feedback value to be written to register + UCHAR ucPostDiv; //return post div to be written to register +}COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V2; +#define COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_PS_ALLOCATION COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS + + +#define SET_CLOCK_FREQ_MASK 0x00FFFFFF //Clock change tables only take bit [23:0] as the requested clock value +#define USE_NON_BUS_CLOCK_MASK 0x01000000 //Applicable to both memory and engine clock change, when set, it uses another clock as the temporary clock (engine uses memory and vice versa) +#define USE_MEMORY_SELF_REFRESH_MASK 0x02000000 //Only applicable to memory clock change, when set, using memory self refresh during clock transition +#define SKIP_INTERNAL_MEMORY_PARAMETER_CHANGE 0x04000000 //Only applicable to memory clock change, when set, the table will skip predefined internal memory parameter change +#define FIRST_TIME_CHANGE_CLOCK 0x08000000 //Applicable to both memory and engine clock change,when set, it means this is 1st time to change clock after ASIC bootup +#define SKIP_SW_PROGRAM_PLL 0x10000000 //Applicable to both memory and engine clock change, when set, it means the table will not program SPLL/MPLL +#define USE_SS_ENABLED_PIXEL_CLOCK USE_NON_BUS_CLOCK_MASK + +#define b3USE_NON_BUS_CLOCK_MASK 0x01 //Applicable to both memory and engine clock change, when set, it uses another clock as the temporary clock (engine uses memory and vice versa) +#define b3USE_MEMORY_SELF_REFRESH 0x02 //Only applicable to memory clock change, when set, using memory self refresh during clock transition +#define b3SKIP_INTERNAL_MEMORY_PARAMETER_CHANGE 0x04 //Only applicable to memory clock change, when set, the table will skip predefined internal memory parameter change +#define b3FIRST_TIME_CHANGE_CLOCK 0x08 //Applicable to both memory and engine clock change,when set, it means this is 1st time to change clock after ASIC bootup +#define b3SKIP_SW_PROGRAM_PLL 0x10 //Applicable to both memory and engine clock change, when set, it means the table will not program SPLL/MPLL + +typedef struct _ATOM_COMPUTE_CLOCK_FREQ +{ +#if ATOM_BIG_ENDIAN + ULONG ulComputeClockFlag:8; // =1: COMPUTE_MEMORY_PLL_PARAM, =2: COMPUTE_ENGINE_PLL_PARAM + ULONG ulClockFreq:24; // in unit of 10kHz +#else + ULONG ulClockFreq:24; // in unit of 10kHz + ULONG ulComputeClockFlag:8; // =1: COMPUTE_MEMORY_PLL_PARAM, =2: COMPUTE_ENGINE_PLL_PARAM +#endif +}ATOM_COMPUTE_CLOCK_FREQ; + +typedef struct _ATOM_S_MPLL_FB_DIVIDER +{ + USHORT usFbDivFrac; + USHORT usFbDiv; +}ATOM_S_MPLL_FB_DIVIDER; + +typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V3 +{ + union + { + ATOM_COMPUTE_CLOCK_FREQ ulClock; //Input Parameter + ATOM_S_MPLL_FB_DIVIDER ulFbDiv; //Output Parameter + }; + UCHAR ucRefDiv; //Output Parameter + UCHAR ucPostDiv; //Output Parameter + UCHAR ucCntlFlag; //Output Parameter + UCHAR ucReserved; +}COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V3; + +// ucCntlFlag +#define ATOM_PLL_CNTL_FLAG_PLL_POST_DIV_EN 1 +#define ATOM_PLL_CNTL_FLAG_MPLL_VCO_MODE 2 +#define ATOM_PLL_CNTL_FLAG_FRACTION_DISABLE 4 + +typedef struct _DYNAMICE_MEMORY_SETTINGS_PARAMETER +{ + ATOM_COMPUTE_CLOCK_FREQ ulClock; + ULONG ulReserved[2]; +}DYNAMICE_MEMORY_SETTINGS_PARAMETER; + +typedef struct _DYNAMICE_ENGINE_SETTINGS_PARAMETER +{ + ATOM_COMPUTE_CLOCK_FREQ ulClock; + ULONG ulMemoryClock; + ULONG ulReserved; +}DYNAMICE_ENGINE_SETTINGS_PARAMETER; + +/****************************************************************************/ +// Structures used by SetEngineClockTable +/****************************************************************************/ +typedef struct _SET_ENGINE_CLOCK_PARAMETERS +{ + ULONG ulTargetEngineClock; //In 10Khz unit +}SET_ENGINE_CLOCK_PARAMETERS; + +typedef struct _SET_ENGINE_CLOCK_PS_ALLOCATION +{ + ULONG ulTargetEngineClock; //In 10Khz unit + COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_PS_ALLOCATION sReserved; +}SET_ENGINE_CLOCK_PS_ALLOCATION; + +/****************************************************************************/ +// Structures used by SetMemoryClockTable +/****************************************************************************/ +typedef struct _SET_MEMORY_CLOCK_PARAMETERS +{ + ULONG ulTargetMemoryClock; //In 10Khz unit +}SET_MEMORY_CLOCK_PARAMETERS; + +typedef struct _SET_MEMORY_CLOCK_PS_ALLOCATION +{ + ULONG ulTargetMemoryClock; //In 10Khz unit + COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_PS_ALLOCATION sReserved; +}SET_MEMORY_CLOCK_PS_ALLOCATION; + +/****************************************************************************/ +// Structures used by ASIC_Init.ctb +/****************************************************************************/ +typedef struct _ASIC_INIT_PARAMETERS +{ + ULONG ulDefaultEngineClock; //In 10Khz unit + ULONG ulDefaultMemoryClock; //In 10Khz unit +}ASIC_INIT_PARAMETERS; + +typedef struct _ASIC_INIT_PS_ALLOCATION +{ + ASIC_INIT_PARAMETERS sASICInitClocks; + SET_ENGINE_CLOCK_PS_ALLOCATION sReserved; //Caller doesn't need to init this structure +}ASIC_INIT_PS_ALLOCATION; + +/****************************************************************************/ +// Structure used by DynamicClockGatingTable.ctb +/****************************************************************************/ +typedef struct _DYNAMIC_CLOCK_GATING_PARAMETERS +{ + UCHAR ucEnable; // ATOM_ENABLE or ATOM_DISABLE + UCHAR ucPadding[3]; +}DYNAMIC_CLOCK_GATING_PARAMETERS; +#define DYNAMIC_CLOCK_GATING_PS_ALLOCATION DYNAMIC_CLOCK_GATING_PARAMETERS + +/****************************************************************************/ +// Structure used by EnableASIC_StaticPwrMgtTable.ctb +/****************************************************************************/ +typedef struct _ENABLE_ASIC_STATIC_PWR_MGT_PARAMETERS +{ + UCHAR ucEnable; // ATOM_ENABLE or ATOM_DISABLE + UCHAR ucPadding[3]; +}ENABLE_ASIC_STATIC_PWR_MGT_PARAMETERS; +#define ENABLE_ASIC_STATIC_PWR_MGT_PS_ALLOCATION ENABLE_ASIC_STATIC_PWR_MGT_PARAMETERS + +/****************************************************************************/ +// Structures used by DAC_LoadDetectionTable.ctb +/****************************************************************************/ +typedef struct _DAC_LOAD_DETECTION_PARAMETERS +{ + USHORT usDeviceID; //{ATOM_DEVICE_CRTx_SUPPORT,ATOM_DEVICE_TVx_SUPPORT,ATOM_DEVICE_CVx_SUPPORT} + UCHAR ucDacType; //{ATOM_DAC_A,ATOM_DAC_B, ATOM_EXT_DAC} + UCHAR ucMisc; //Valid only when table revision =1.3 and above +}DAC_LOAD_DETECTION_PARAMETERS; + +// DAC_LOAD_DETECTION_PARAMETERS.ucMisc +#define DAC_LOAD_MISC_YPrPb 0x01 + +typedef struct _DAC_LOAD_DETECTION_PS_ALLOCATION +{ + DAC_LOAD_DETECTION_PARAMETERS sDacload; + ULONG Reserved[2];// Don't set this one, allocation for EXT DAC +}DAC_LOAD_DETECTION_PS_ALLOCATION; + +/****************************************************************************/ +// Structures used by DAC1EncoderControlTable.ctb and DAC2EncoderControlTable.ctb +/****************************************************************************/ +typedef struct _DAC_ENCODER_CONTROL_PARAMETERS +{ + USHORT usPixelClock; // in 10KHz; for bios convenient + UCHAR ucDacStandard; // See definition of ATOM_DACx_xxx, For DEC3.0, bit 7 used as internal flag to indicate DAC2 (==1) or DAC1 (==0) + UCHAR ucAction; // 0: turn off encoder + // 1: setup and turn on encoder + // 7: ATOM_ENCODER_INIT Initialize DAC +}DAC_ENCODER_CONTROL_PARAMETERS; + +#define DAC_ENCODER_CONTROL_PS_ALLOCATION DAC_ENCODER_CONTROL_PARAMETERS + +/****************************************************************************/ +// Structures used by DIG1EncoderControlTable +// DIG2EncoderControlTable +// ExternalEncoderControlTable +/****************************************************************************/ +typedef struct _DIG_ENCODER_CONTROL_PARAMETERS +{ + USHORT usPixelClock; // in 10KHz; for bios convenient + UCHAR ucConfig; + // [2] Link Select: + // =0: PHY linkA if bfLane<3 + // =1: PHY linkB if bfLanes<3 + // =0: PHY linkA+B if bfLanes=3 + // [3] Transmitter Sel + // =0: UNIPHY or PCIEPHY + // =1: LVTMA + UCHAR ucAction; // =0: turn off encoder + // =1: turn on encoder + UCHAR ucEncoderMode; + // =0: DP encoder + // =1: LVDS encoder + // =2: DVI encoder + // =3: HDMI encoder + // =4: SDVO encoder + UCHAR ucLaneNum; // how many lanes to enable + UCHAR ucReserved[2]; +}DIG_ENCODER_CONTROL_PARAMETERS; +#define DIG_ENCODER_CONTROL_PS_ALLOCATION DIG_ENCODER_CONTROL_PARAMETERS +#define EXTERNAL_ENCODER_CONTROL_PARAMETER DIG_ENCODER_CONTROL_PARAMETERS + +//ucConfig +#define ATOM_ENCODER_CONFIG_DPLINKRATE_MASK 0x01 +#define ATOM_ENCODER_CONFIG_DPLINKRATE_1_62GHZ 0x00 +#define ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ 0x01 +#define ATOM_ENCODER_CONFIG_LINK_SEL_MASK 0x04 +#define ATOM_ENCODER_CONFIG_LINKA 0x00 +#define ATOM_ENCODER_CONFIG_LINKB 0x04 +#define ATOM_ENCODER_CONFIG_LINKA_B ATOM_TRANSMITTER_CONFIG_LINKA +#define ATOM_ENCODER_CONFIG_LINKB_A ATOM_ENCODER_CONFIG_LINKB +#define ATOM_ENCODER_CONFIG_TRANSMITTER_SEL_MASK 0x08 +#define ATOM_ENCODER_CONFIG_UNIPHY 0x00 +#define ATOM_ENCODER_CONFIG_LVTMA 0x08 +#define ATOM_ENCODER_CONFIG_TRANSMITTER1 0x00 +#define ATOM_ENCODER_CONFIG_TRANSMITTER2 0x08 +#define ATOM_ENCODER_CONFIG_DIGB 0x80 // VBIOS Internal use, outside SW should set this bit=0 +// ucAction +// ATOM_ENABLE: Enable Encoder +// ATOM_DISABLE: Disable Encoder + +//ucEncoderMode +#define ATOM_ENCODER_MODE_DP 0 +#define ATOM_ENCODER_MODE_LVDS 1 +#define ATOM_ENCODER_MODE_DVI 2 +#define ATOM_ENCODER_MODE_HDMI 3 +#define ATOM_ENCODER_MODE_SDVO 4 +#define ATOM_ENCODER_MODE_TV 13 +#define ATOM_ENCODER_MODE_CV 14 +#define ATOM_ENCODER_MODE_CRT 15 + +typedef struct _ATOM_DIG_ENCODER_CONFIG_V2 +{ +#if ATOM_BIG_ENDIAN + UCHAR ucReserved1:2; + UCHAR ucTransmitterSel:2; // =0: UniphyAB, =1: UniphyCD =2: UniphyEF + UCHAR ucLinkSel:1; // =0: linkA/C/E =1: linkB/D/F + UCHAR ucReserved:1; + UCHAR ucDPLinkRate:1; // =0: 1.62Ghz, =1: 2.7Ghz +#else + UCHAR ucDPLinkRate:1; // =0: 1.62Ghz, =1: 2.7Ghz + UCHAR ucReserved:1; + UCHAR ucLinkSel:1; // =0: linkA/C/E =1: linkB/D/F + UCHAR ucTransmitterSel:2; // =0: UniphyAB, =1: UniphyCD =2: UniphyEF + UCHAR ucReserved1:2; +#endif +}ATOM_DIG_ENCODER_CONFIG_V2; + + +typedef struct _DIG_ENCODER_CONTROL_PARAMETERS_V2 +{ + USHORT usPixelClock; // in 10KHz; for bios convenient + ATOM_DIG_ENCODER_CONFIG_V2 acConfig; + UCHAR ucAction; + UCHAR ucEncoderMode; + // =0: DP encoder + // =1: LVDS encoder + // =2: DVI encoder + // =3: HDMI encoder + // =4: SDVO encoder + UCHAR ucLaneNum; // how many lanes to enable + UCHAR ucReserved[2]; +}DIG_ENCODER_CONTROL_PARAMETERS_V2; + +//ucConfig +#define ATOM_ENCODER_CONFIG_V2_DPLINKRATE_MASK 0x01 +#define ATOM_ENCODER_CONFIG_V2_DPLINKRATE_1_62GHZ 0x00 +#define ATOM_ENCODER_CONFIG_V2_DPLINKRATE_2_70GHZ 0x01 +#define ATOM_ENCODER_CONFIG_V2_LINK_SEL_MASK 0x04 +#define ATOM_ENCODER_CONFIG_V2_LINKA 0x00 +#define ATOM_ENCODER_CONFIG_V2_LINKB 0x04 +#define ATOM_ENCODER_CONFIG_V2_TRANSMITTER_SEL_MASK 0x18 +#define ATOM_ENCODER_CONFIG_V2_TRANSMITTER1 0x00 +#define ATOM_ENCODER_CONFIG_V2_TRANSMITTER2 0x08 +#define ATOM_ENCODER_CONFIG_V2_TRANSMITTER3 0x10 + +/****************************************************************************/ +// Structures used by UNIPHYTransmitterControlTable +// LVTMATransmitterControlTable +// DVOOutputControlTable +/****************************************************************************/ +typedef struct _ATOM_DP_VS_MODE +{ + UCHAR ucLaneSel; + UCHAR ucLaneSet; +}ATOM_DP_VS_MODE; + +typedef struct _DIG_TRANSMITTER_CONTROL_PARAMETERS +{ + union + { + USHORT usPixelClock; // in 10KHz; for bios convenient + USHORT usInitInfo; // when init uniphy,lower 8bit is used for connector type defined in objectid.h + ATOM_DP_VS_MODE asMode; // DP Voltage swing mode + }; + UCHAR ucConfig; + // [0]=0: 4 lane Link, + // =1: 8 lane Link ( Dual Links TMDS ) + // [1]=0: InCoherent mode + // =1: Coherent Mode + // [2] Link Select: + // =0: PHY linkA if bfLane<3 + // =1: PHY linkB if bfLanes<3 + // =0: PHY linkA+B if bfLanes=3 + // [5:4]PCIE lane Sel + // =0: lane 0~3 or 0~7 + // =1: lane 4~7 + // =2: lane 8~11 or 8~15 + // =3: lane 12~15 + UCHAR ucAction; // =0: turn off encoder + // =1: turn on encoder + UCHAR ucReserved[4]; +}DIG_TRANSMITTER_CONTROL_PARAMETERS; + +#define DIG_TRANSMITTER_CONTROL_PS_ALLOCATION DIG_TRANSMITTER_CONTROL_PARAMETERS + +//ucInitInfo +#define ATOM_TRAMITTER_INITINFO_CONNECTOR_MASK 0x00ff + +//ucConfig +#define ATOM_TRANSMITTER_CONFIG_8LANE_LINK 0x01 +#define ATOM_TRANSMITTER_CONFIG_COHERENT 0x02 +#define ATOM_TRANSMITTER_CONFIG_LINK_SEL_MASK 0x04 +#define ATOM_TRANSMITTER_CONFIG_LINKA 0x00 +#define ATOM_TRANSMITTER_CONFIG_LINKB 0x04 +#define ATOM_TRANSMITTER_CONFIG_LINKA_B 0x00 +#define ATOM_TRANSMITTER_CONFIG_LINKB_A 0x04 + +#define ATOM_TRANSMITTER_CONFIG_ENCODER_SEL_MASK 0x08 // only used when ATOM_TRANSMITTER_ACTION_ENABLE +#define ATOM_TRANSMITTER_CONFIG_DIG1_ENCODER 0x00 // only used when ATOM_TRANSMITTER_ACTION_ENABLE +#define ATOM_TRANSMITTER_CONFIG_DIG2_ENCODER 0x08 // only used when ATOM_TRANSMITTER_ACTION_ENABLE + +#define ATOM_TRANSMITTER_CONFIG_CLKSRC_MASK 0x30 +#define ATOM_TRANSMITTER_CONFIG_CLKSRC_PPLL 0x00 +#define ATOM_TRANSMITTER_CONFIG_CLKSRC_PCIE 0x20 +#define ATOM_TRANSMITTER_CONFIG_CLKSRC_XTALIN 0x30 +#define ATOM_TRANSMITTER_CONFIG_LANE_SEL_MASK 0xc0 +#define ATOM_TRANSMITTER_CONFIG_LANE_0_3 0x00 +#define ATOM_TRANSMITTER_CONFIG_LANE_0_7 0x00 +#define ATOM_TRANSMITTER_CONFIG_LANE_4_7 0x40 +#define ATOM_TRANSMITTER_CONFIG_LANE_8_11 0x80 +#define ATOM_TRANSMITTER_CONFIG_LANE_8_15 0x80 +#define ATOM_TRANSMITTER_CONFIG_LANE_12_15 0xc0 + +//ucAction +#define ATOM_TRANSMITTER_ACTION_DISABLE 0 +#define ATOM_TRANSMITTER_ACTION_ENABLE 1 +#define ATOM_TRANSMITTER_ACTION_LCD_BLOFF 2 +#define ATOM_TRANSMITTER_ACTION_LCD_BLON 3 +#define ATOM_TRANSMITTER_ACTION_BL_BRIGHTNESS_CONTROL 4 +#define ATOM_TRANSMITTER_ACTION_LCD_SELFTEST_START 5 +#define ATOM_TRANSMITTER_ACTION_LCD_SELFTEST_STOP 6 +#define ATOM_TRANSMITTER_ACTION_INIT 7 +#define ATOM_TRANSMITTER_ACTION_DISABLE_OUTPUT 8 +#define ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT 9 +#define ATOM_TRANSMITTER_ACTION_SETUP 10 +#define ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH 11 + + +// Following are used for DigTransmitterControlTable ver1.2 +typedef struct _ATOM_DIG_TRANSMITTER_CONFIG_V2 +{ +#if ATOM_BIG_ENDIAN + UCHAR ucTransmitterSel:2; //bit7:6: =0 Dig Transmitter 1 ( Uniphy AB ) + // =1 Dig Transmitter 2 ( Uniphy CD ) + // =2 Dig Transmitter 3 ( Uniphy EF ) + UCHAR ucReserved:1; + UCHAR fDPConnector:1; //bit4=0: DP connector =1: None DP connector + UCHAR ucEncoderSel:1; //bit3=0: Data/Clk path source from DIGA( DIG inst0 ). =1: Data/clk path source from DIGB ( DIG inst1 ) + UCHAR ucLinkSel:1; //bit2=0: Uniphy LINKA or C or E when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is A or C or E + // =1: Uniphy LINKB or D or F when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is B or D or F + + UCHAR fCoherentMode:1; //bit1=1: Coherent Mode ( for DVI/HDMI mode ) + UCHAR fDualLinkConnector:1; //bit0=1: Dual Link DVI connector +#else + UCHAR fDualLinkConnector:1; //bit0=1: Dual Link DVI connector + UCHAR fCoherentMode:1; //bit1=1: Coherent Mode ( for DVI/HDMI mode ) + UCHAR ucLinkSel:1; //bit2=0: Uniphy LINKA or C or E when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is A or C or E + // =1: Uniphy LINKB or D or F when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is B or D or F + UCHAR ucEncoderSel:1; //bit3=0: Data/Clk path source from DIGA( DIG inst0 ). =1: Data/clk path source from DIGB ( DIG inst1 ) + UCHAR fDPConnector:1; //bit4=0: DP connector =1: None DP connector + UCHAR ucReserved:1; + UCHAR ucTransmitterSel:2; //bit7:6: =0 Dig Transmitter 1 ( Uniphy AB ) + // =1 Dig Transmitter 2 ( Uniphy CD ) + // =2 Dig Transmitter 3 ( Uniphy EF ) +#endif +}ATOM_DIG_TRANSMITTER_CONFIG_V2; + +//ucConfig +//Bit0 +#define ATOM_TRANSMITTER_CONFIG_V2_DUAL_LINK_CONNECTOR 0x01 + +//Bit1 +#define ATOM_TRANSMITTER_CONFIG_V2_COHERENT 0x02 + +//Bit2 +#define ATOM_TRANSMITTER_CONFIG_V2_LINK_SEL_MASK 0x04 +#define ATOM_TRANSMITTER_CONFIG_V2_LINKA 0x00 +#define ATOM_TRANSMITTER_CONFIG_V2_LINKB 0x04 + +// Bit3 +#define ATOM_TRANSMITTER_CONFIG_V2_ENCODER_SEL_MASK 0x08 +#define ATOM_TRANSMITTER_CONFIG_V2_DIG1_ENCODER 0x00 // only used when ucAction == ATOM_TRANSMITTER_ACTION_ENABLE or ATOM_TRANSMITTER_ACTION_SETUP +#define ATOM_TRANSMITTER_CONFIG_V2_DIG2_ENCODER 0x08 // only used when ucAction == ATOM_TRANSMITTER_ACTION_ENABLE or ATOM_TRANSMITTER_ACTION_SETUP + +// Bit4 +#define ATOM_TRASMITTER_CONFIG_V2_DP_CONNECTOR 0x10 + +// Bit7:6 +#define ATOM_TRANSMITTER_CONFIG_V2_TRANSMITTER_SEL_MASK 0xC0 +#define ATOM_TRANSMITTER_CONFIG_V2_TRANSMITTER1 0x00 //AB +#define ATOM_TRANSMITTER_CONFIG_V2_TRANSMITTER2 0x40 //CD +#define ATOM_TRANSMITTER_CONFIG_V2_TRANSMITTER3 0x80 //EF + +typedef struct _DIG_TRANSMITTER_CONTROL_PARAMETERS_V2 +{ + union + { + USHORT usPixelClock; // in 10KHz; for bios convenient + USHORT usInitInfo; // when init uniphy,lower 8bit is used for connector type defined in objectid.h + ATOM_DP_VS_MODE asMode; // DP Voltage swing mode + }; + ATOM_DIG_TRANSMITTER_CONFIG_V2 acConfig; + UCHAR ucAction; // define as ATOM_TRANSMITER_ACTION_XXX + UCHAR ucReserved[4]; +}DIG_TRANSMITTER_CONTROL_PARAMETERS_V2; + + +/****************************************************************************/ +// Structures used by DAC1OuputControlTable +// DAC2OuputControlTable +// LVTMAOutputControlTable (Before DEC30) +// TMDSAOutputControlTable (Before DEC30) +/****************************************************************************/ +typedef struct _DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS +{ + UCHAR ucAction; // Possible input:ATOM_ENABLE||ATOMDISABLE + // When the display is LCD, in addition to above: + // ATOM_LCD_BLOFF|| ATOM_LCD_BLON ||ATOM_LCD_BL_BRIGHTNESS_CONTROL||ATOM_LCD_SELFTEST_START|| + // ATOM_LCD_SELFTEST_STOP + + UCHAR aucPadding[3]; // padding to DWORD aligned +}DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS; + +#define DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS + + +#define CRT1_OUTPUT_CONTROL_PARAMETERS DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS +#define CRT1_OUTPUT_CONTROL_PS_ALLOCATION DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION + +#define CRT2_OUTPUT_CONTROL_PARAMETERS DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS +#define CRT2_OUTPUT_CONTROL_PS_ALLOCATION DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION + +#define CV1_OUTPUT_CONTROL_PARAMETERS DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS +#define CV1_OUTPUT_CONTROL_PS_ALLOCATION DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION + +#define TV1_OUTPUT_CONTROL_PARAMETERS DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS +#define TV1_OUTPUT_CONTROL_PS_ALLOCATION DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION + +#define DFP1_OUTPUT_CONTROL_PARAMETERS DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS +#define DFP1_OUTPUT_CONTROL_PS_ALLOCATION DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION + +#define DFP2_OUTPUT_CONTROL_PARAMETERS DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS +#define DFP2_OUTPUT_CONTROL_PS_ALLOCATION DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION + +#define LCD1_OUTPUT_CONTROL_PARAMETERS DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS +#define LCD1_OUTPUT_CONTROL_PS_ALLOCATION DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION + +#define DVO_OUTPUT_CONTROL_PARAMETERS DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS +#define DVO_OUTPUT_CONTROL_PS_ALLOCATION DIG_TRANSMITTER_CONTROL_PS_ALLOCATION +#define DVO_OUTPUT_CONTROL_PARAMETERS_V3 DIG_TRANSMITTER_CONTROL_PARAMETERS + +/****************************************************************************/ +// Structures used by BlankCRTCTable +/****************************************************************************/ +typedef struct _BLANK_CRTC_PARAMETERS +{ + UCHAR ucCRTC; // ATOM_CRTC1 or ATOM_CRTC2 + UCHAR ucBlanking; // ATOM_BLANKING or ATOM_BLANKINGOFF + USHORT usBlackColorRCr; + USHORT usBlackColorGY; + USHORT usBlackColorBCb; +}BLANK_CRTC_PARAMETERS; +#define BLANK_CRTC_PS_ALLOCATION BLANK_CRTC_PARAMETERS + +/****************************************************************************/ +// Structures used by EnableCRTCTable +// EnableCRTCMemReqTable +// UpdateCRTC_DoubleBufferRegistersTable +/****************************************************************************/ +typedef struct _ENABLE_CRTC_PARAMETERS +{ + UCHAR ucCRTC; // ATOM_CRTC1 or ATOM_CRTC2 + UCHAR ucEnable; // ATOM_ENABLE or ATOM_DISABLE + UCHAR ucPadding[2]; +}ENABLE_CRTC_PARAMETERS; +#define ENABLE_CRTC_PS_ALLOCATION ENABLE_CRTC_PARAMETERS + +/****************************************************************************/ +// Structures used by SetCRTC_OverScanTable +/****************************************************************************/ +typedef struct _SET_CRTC_OVERSCAN_PARAMETERS +{ + USHORT usOverscanRight; // right + USHORT usOverscanLeft; // left + USHORT usOverscanBottom; // bottom + USHORT usOverscanTop; // top + UCHAR ucCRTC; // ATOM_CRTC1 or ATOM_CRTC2 + UCHAR ucPadding[3]; +}SET_CRTC_OVERSCAN_PARAMETERS; +#define SET_CRTC_OVERSCAN_PS_ALLOCATION SET_CRTC_OVERSCAN_PARAMETERS + +/****************************************************************************/ +// Structures used by SetCRTC_ReplicationTable +/****************************************************************************/ +typedef struct _SET_CRTC_REPLICATION_PARAMETERS +{ + UCHAR ucH_Replication; // horizontal replication + UCHAR ucV_Replication; // vertical replication + UCHAR usCRTC; // ATOM_CRTC1 or ATOM_CRTC2 + UCHAR ucPadding; +}SET_CRTC_REPLICATION_PARAMETERS; +#define SET_CRTC_REPLICATION_PS_ALLOCATION SET_CRTC_REPLICATION_PARAMETERS + +/****************************************************************************/ +// Structures used by SelectCRTC_SourceTable +/****************************************************************************/ +typedef struct _SELECT_CRTC_SOURCE_PARAMETERS +{ + UCHAR ucCRTC; // ATOM_CRTC1 or ATOM_CRTC2 + UCHAR ucDevice; // ATOM_DEVICE_CRT1|ATOM_DEVICE_CRT2|.... + UCHAR ucPadding[2]; +}SELECT_CRTC_SOURCE_PARAMETERS; +#define SELECT_CRTC_SOURCE_PS_ALLOCATION SELECT_CRTC_SOURCE_PARAMETERS + +typedef struct _SELECT_CRTC_SOURCE_PARAMETERS_V2 +{ + UCHAR ucCRTC; // ATOM_CRTC1 or ATOM_CRTC2 + UCHAR ucEncoderID; // DAC1/DAC2/TVOUT/DIG1/DIG2/DVO + UCHAR ucEncodeMode; // Encoding mode, only valid when using DIG1/DIG2/DVO + UCHAR ucPadding; +}SELECT_CRTC_SOURCE_PARAMETERS_V2; + +//ucEncoderID +//#define ASIC_INT_DAC1_ENCODER_ID 0x00 +//#define ASIC_INT_TV_ENCODER_ID 0x02 +//#define ASIC_INT_DIG1_ENCODER_ID 0x03 +//#define ASIC_INT_DAC2_ENCODER_ID 0x04 +//#define ASIC_EXT_TV_ENCODER_ID 0x06 +//#define ASIC_INT_DVO_ENCODER_ID 0x07 +//#define ASIC_INT_DIG2_ENCODER_ID 0x09 +//#define ASIC_EXT_DIG_ENCODER_ID 0x05 + +//ucEncodeMode +//#define ATOM_ENCODER_MODE_DP 0 +//#define ATOM_ENCODER_MODE_LVDS 1 +//#define ATOM_ENCODER_MODE_DVI 2 +//#define ATOM_ENCODER_MODE_HDMI 3 +//#define ATOM_ENCODER_MODE_SDVO 4 +//#define ATOM_ENCODER_MODE_TV 13 +//#define ATOM_ENCODER_MODE_CV 14 +//#define ATOM_ENCODER_MODE_CRT 15 + +/****************************************************************************/ +// Structures used by SetPixelClockTable +// GetPixelClockTable +/****************************************************************************/ +//Major revision=1., Minor revision=1 +typedef struct _PIXEL_CLOCK_PARAMETERS +{ + USHORT usPixelClock; // in 10kHz unit; for bios convenient = (RefClk*FB_Div)/(Ref_Div*Post_Div) + // 0 means disable PPLL + USHORT usRefDiv; // Reference divider + USHORT usFbDiv; // feedback divider + UCHAR ucPostDiv; // post divider + UCHAR ucFracFbDiv; // fractional feedback divider + UCHAR ucPpll; // ATOM_PPLL1 or ATOM_PPL2 + UCHAR ucRefDivSrc; // ATOM_PJITTER or ATO_NONPJITTER + UCHAR ucCRTC; // Which CRTC uses this Ppll + UCHAR ucPadding; +}PIXEL_CLOCK_PARAMETERS; + +//Major revision=1., Minor revision=2, add ucMiscIfno +//ucMiscInfo: +#define MISC_FORCE_REPROG_PIXEL_CLOCK 0x1 +#define MISC_DEVICE_INDEX_MASK 0xF0 +#define MISC_DEVICE_INDEX_SHIFT 4 + +typedef struct _PIXEL_CLOCK_PARAMETERS_V2 +{ + USHORT usPixelClock; // in 10kHz unit; for bios convenient = (RefClk*FB_Div)/(Ref_Div*Post_Div) + // 0 means disable PPLL + USHORT usRefDiv; // Reference divider + USHORT usFbDiv; // feedback divider + UCHAR ucPostDiv; // post divider + UCHAR ucFracFbDiv; // fractional feedback divider + UCHAR ucPpll; // ATOM_PPLL1 or ATOM_PPL2 + UCHAR ucRefDivSrc; // ATOM_PJITTER or ATO_NONPJITTER + UCHAR ucCRTC; // Which CRTC uses this Ppll + UCHAR ucMiscInfo; // Different bits for different purpose, bit [7:4] as device index, bit[0]=Force prog +}PIXEL_CLOCK_PARAMETERS_V2; + +//Major revision=1., Minor revision=3, structure/definition change +//ucEncoderMode: +//ATOM_ENCODER_MODE_DP +//ATOM_ENOCDER_MODE_LVDS +//ATOM_ENOCDER_MODE_DVI +//ATOM_ENOCDER_MODE_HDMI +//ATOM_ENOCDER_MODE_SDVO +//ATOM_ENCODER_MODE_TV 13 +//ATOM_ENCODER_MODE_CV 14 +//ATOM_ENCODER_MODE_CRT 15 + +//ucDVOConfig +//#define DVO_ENCODER_CONFIG_RATE_SEL 0x01 +//#define DVO_ENCODER_CONFIG_DDR_SPEED 0x00 +//#define DVO_ENCODER_CONFIG_SDR_SPEED 0x01 +//#define DVO_ENCODER_CONFIG_OUTPUT_SEL 0x0c +//#define DVO_ENCODER_CONFIG_LOW12BIT 0x00 +//#define DVO_ENCODER_CONFIG_UPPER12BIT 0x04 +//#define DVO_ENCODER_CONFIG_24BIT 0x08 + +//ucMiscInfo: also changed, see below +#define PIXEL_CLOCK_MISC_FORCE_PROG_PPLL 0x01 +#define PIXEL_CLOCK_MISC_VGA_MODE 0x02 +#define PIXEL_CLOCK_MISC_CRTC_SEL_MASK 0x04 +#define PIXEL_CLOCK_MISC_CRTC_SEL_CRTC1 0x00 +#define PIXEL_CLOCK_MISC_CRTC_SEL_CRTC2 0x04 +#define PIXEL_CLOCK_MISC_USE_ENGINE_FOR_DISPCLK 0x08 + +typedef struct _PIXEL_CLOCK_PARAMETERS_V3 +{ + USHORT usPixelClock; // in 10kHz unit; for bios convenient = (RefClk*FB_Div)/(Ref_Div*Post_Div) + // 0 means disable PPLL. For VGA PPLL,make sure this value is not 0. + USHORT usRefDiv; // Reference divider + USHORT usFbDiv; // feedback divider + UCHAR ucPostDiv; // post divider + UCHAR ucFracFbDiv; // fractional feedback divider + UCHAR ucPpll; // ATOM_PPLL1 or ATOM_PPL2 + UCHAR ucTransmitterId; // graphic encoder id defined in objectId.h + union + { + UCHAR ucEncoderMode; // encoder type defined as ATOM_ENCODER_MODE_DP/DVI/HDMI/ + UCHAR ucDVOConfig; // when use DVO, need to know SDR/DDR, 12bit or 24bit + }; + UCHAR ucMiscInfo; // bit[0]=Force program, bit[1]= set pclk for VGA, b[2]= CRTC sel + // bit[3]=0:use PPLL for dispclk source, =1: use engine clock for dispclock source +}PIXEL_CLOCK_PARAMETERS_V3; + +#define PIXEL_CLOCK_PARAMETERS_LAST PIXEL_CLOCK_PARAMETERS_V2 +#define GET_PIXEL_CLOCK_PS_ALLOCATION PIXEL_CLOCK_PARAMETERS_LAST + +/****************************************************************************/ +// Structures used by AdjustDisplayPllTable +/****************************************************************************/ +typedef struct _ADJUST_DISPLAY_PLL_PARAMETERS +{ + USHORT usPixelClock; + UCHAR ucTransmitterID; + UCHAR ucEncodeMode; + union + { + UCHAR ucDVOConfig; //if DVO, need passing link rate and output 12bitlow or 24bit + UCHAR ucConfig; //if none DVO, not defined yet + }; + UCHAR ucReserved[3]; +}ADJUST_DISPLAY_PLL_PARAMETERS; + +#define ADJUST_DISPLAY_CONFIG_SS_ENABLE 0x10 + +#define ADJUST_DISPLAY_PLL_PS_ALLOCATION ADJUST_DISPLAY_PLL_PARAMETERS + +/****************************************************************************/ +// Structures used by EnableYUVTable +/****************************************************************************/ +typedef struct _ENABLE_YUV_PARAMETERS +{ + UCHAR ucEnable; // ATOM_ENABLE:Enable YUV or ATOM_DISABLE:Disable YUV (RGB) + UCHAR ucCRTC; // Which CRTC needs this YUV or RGB format + UCHAR ucPadding[2]; +}ENABLE_YUV_PARAMETERS; +#define ENABLE_YUV_PS_ALLOCATION ENABLE_YUV_PARAMETERS + +/****************************************************************************/ +// Structures used by GetMemoryClockTable +/****************************************************************************/ +typedef struct _GET_MEMORY_CLOCK_PARAMETERS +{ + ULONG ulReturnMemoryClock; // current memory speed in 10KHz unit +} GET_MEMORY_CLOCK_PARAMETERS; +#define GET_MEMORY_CLOCK_PS_ALLOCATION GET_MEMORY_CLOCK_PARAMETERS + +/****************************************************************************/ +// Structures used by GetEngineClockTable +/****************************************************************************/ +typedef struct _GET_ENGINE_CLOCK_PARAMETERS +{ + ULONG ulReturnEngineClock; // current engine speed in 10KHz unit +} GET_ENGINE_CLOCK_PARAMETERS; +#define GET_ENGINE_CLOCK_PS_ALLOCATION GET_ENGINE_CLOCK_PARAMETERS + +/****************************************************************************/ +// Following Structures and constant may be obsolete +/****************************************************************************/ +//Maxium 8 bytes,the data read in will be placed in the parameter space. +//Read operaion successeful when the paramter space is non-zero, otherwise read operation failed +typedef struct _READ_EDID_FROM_HW_I2C_DATA_PARAMETERS +{ + USHORT usPrescale; //Ratio between Engine clock and I2C clock + USHORT usVRAMAddress; //Adress in Frame Buffer where to pace raw EDID + USHORT usStatus; //When use output: lower byte EDID checksum, high byte hardware status + //WHen use input: lower byte as 'byte to read':currently limited to 128byte or 1byte + UCHAR ucSlaveAddr; //Read from which slave + UCHAR ucLineNumber; //Read from which HW assisted line +}READ_EDID_FROM_HW_I2C_DATA_PARAMETERS; +#define READ_EDID_FROM_HW_I2C_DATA_PS_ALLOCATION READ_EDID_FROM_HW_I2C_DATA_PARAMETERS + + +#define ATOM_WRITE_I2C_FORMAT_PSOFFSET_PSDATABYTE 0 +#define ATOM_WRITE_I2C_FORMAT_PSOFFSET_PSTWODATABYTES 1 +#define ATOM_WRITE_I2C_FORMAT_PSCOUNTER_PSOFFSET_IDDATABLOCK 2 +#define ATOM_WRITE_I2C_FORMAT_PSCOUNTER_IDOFFSET_PLUS_IDDATABLOCK 3 +#define ATOM_WRITE_I2C_FORMAT_IDCOUNTER_IDOFFSET_IDDATABLOCK 4 + +typedef struct _WRITE_ONE_BYTE_HW_I2C_DATA_PARAMETERS +{ + USHORT usPrescale; //Ratio between Engine clock and I2C clock + USHORT usByteOffset; //Write to which byte + //Upper portion of usByteOffset is Format of data + //1bytePS+offsetPS + //2bytesPS+offsetPS + //blockID+offsetPS + //blockID+offsetID + //blockID+counterID+offsetID + UCHAR ucData; //PS data1 + UCHAR ucStatus; //Status byte 1=success, 2=failure, Also is used as PS data2 + UCHAR ucSlaveAddr; //Write to which slave + UCHAR ucLineNumber; //Write from which HW assisted line +}WRITE_ONE_BYTE_HW_I2C_DATA_PARAMETERS; + +#define WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION WRITE_ONE_BYTE_HW_I2C_DATA_PARAMETERS + +typedef struct _SET_UP_HW_I2C_DATA_PARAMETERS +{ + USHORT usPrescale; //Ratio between Engine clock and I2C clock + UCHAR ucSlaveAddr; //Write to which slave + UCHAR ucLineNumber; //Write from which HW assisted line +}SET_UP_HW_I2C_DATA_PARAMETERS; + + +/**************************************************************************/ +#define SPEED_FAN_CONTROL_PS_ALLOCATION WRITE_ONE_BYTE_HW_I2C_DATA_PARAMETERS + +/****************************************************************************/ +// Structures used by PowerConnectorDetectionTable +/****************************************************************************/ +typedef struct _POWER_CONNECTOR_DETECTION_PARAMETERS +{ + UCHAR ucPowerConnectorStatus; //Used for return value 0: detected, 1:not detected + UCHAR ucPwrBehaviorId; + USHORT usPwrBudget; //how much power currently boot to in unit of watt +}POWER_CONNECTOR_DETECTION_PARAMETERS; + +typedef struct POWER_CONNECTOR_DETECTION_PS_ALLOCATION +{ + UCHAR ucPowerConnectorStatus; //Used for return value 0: detected, 1:not detected + UCHAR ucReserved; + USHORT usPwrBudget; //how much power currently boot to in unit of watt + WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION sReserved; +}POWER_CONNECTOR_DETECTION_PS_ALLOCATION; + +/****************************LVDS SS Command Table Definitions**********************/ + +/****************************************************************************/ +// Structures used by EnableSpreadSpectrumOnPPLLTable +/****************************************************************************/ +typedef struct _ENABLE_LVDS_SS_PARAMETERS +{ + USHORT usSpreadSpectrumPercentage; + UCHAR ucSpreadSpectrumType; //Bit1=0 Down Spread,=1 Center Spread. Bit1=1 Ext. =0 Int. Others:TBD + UCHAR ucSpreadSpectrumStepSize_Delay; //bits3:2 SS_STEP_SIZE; bit 6:4 SS_DELAY + UCHAR ucEnable; //ATOM_ENABLE or ATOM_DISABLE + UCHAR ucPadding[3]; +}ENABLE_LVDS_SS_PARAMETERS; + +//ucTableFormatRevision=1,ucTableContentRevision=2 +typedef struct _ENABLE_LVDS_SS_PARAMETERS_V2 +{ + USHORT usSpreadSpectrumPercentage; + UCHAR ucSpreadSpectrumType; //Bit1=0 Down Spread,=1 Center Spread. Bit1=1 Ext. =0 Int. Others:TBD + UCHAR ucSpreadSpectrumStep; // + UCHAR ucEnable; //ATOM_ENABLE or ATOM_DISABLE + UCHAR ucSpreadSpectrumDelay; + UCHAR ucSpreadSpectrumRange; + UCHAR ucPadding; +}ENABLE_LVDS_SS_PARAMETERS_V2; + +//This new structure is based on ENABLE_LVDS_SS_PARAMETERS but expands to SS on PPLL, so other devices can use SS. +typedef struct _ENABLE_SPREAD_SPECTRUM_ON_PPLL +{ + USHORT usSpreadSpectrumPercentage; + UCHAR ucSpreadSpectrumType; // Bit1=0 Down Spread,=1 Center Spread. Bit1=1 Ext. =0 Int. Others:TBD + UCHAR ucSpreadSpectrumStep; // + UCHAR ucEnable; // ATOM_ENABLE or ATOM_DISABLE + UCHAR ucSpreadSpectrumDelay; + UCHAR ucSpreadSpectrumRange; + UCHAR ucPpll; // ATOM_PPLL1/ATOM_PPLL2 +}ENABLE_SPREAD_SPECTRUM_ON_PPLL; + +#define ENABLE_SPREAD_SPECTRUM_ON_PPLL_PS_ALLOCATION ENABLE_SPREAD_SPECTRUM_ON_PPLL + +/**************************************************************************/ + +typedef struct _SET_PIXEL_CLOCK_PS_ALLOCATION +{ + PIXEL_CLOCK_PARAMETERS sPCLKInput; + ENABLE_SPREAD_SPECTRUM_ON_PPLL sReserved;//Caller doesn't need to init this portion +}SET_PIXEL_CLOCK_PS_ALLOCATION; + +#define ENABLE_VGA_RENDER_PS_ALLOCATION SET_PIXEL_CLOCK_PS_ALLOCATION + +/****************************************************************************/ +// Structures used by ### +/****************************************************************************/ +typedef struct _MEMORY_TRAINING_PARAMETERS +{ + ULONG ulTargetMemoryClock; //In 10Khz unit +}MEMORY_TRAINING_PARAMETERS; +#define MEMORY_TRAINING_PS_ALLOCATION MEMORY_TRAINING_PARAMETERS + + +/****************************LVDS and other encoder command table definitions **********************/ + + +/****************************************************************************/ +// Structures used by LVDSEncoderControlTable (Before DCE30) +// LVTMAEncoderControlTable (Before DCE30) +// TMDSAEncoderControlTable (Before DCE30) +/****************************************************************************/ +typedef struct _LVDS_ENCODER_CONTROL_PARAMETERS +{ + USHORT usPixelClock; // in 10KHz; for bios convenient + UCHAR ucMisc; // bit0=0: Enable single link + // =1: Enable dual link + // Bit1=0: 666RGB + // =1: 888RGB + UCHAR ucAction; // 0: turn off encoder + // 1: setup and turn on encoder +}LVDS_ENCODER_CONTROL_PARAMETERS; + +#define LVDS_ENCODER_CONTROL_PS_ALLOCATION LVDS_ENCODER_CONTROL_PARAMETERS + +#define TMDS1_ENCODER_CONTROL_PARAMETERS LVDS_ENCODER_CONTROL_PARAMETERS +#define TMDS1_ENCODER_CONTROL_PS_ALLOCATION TMDS1_ENCODER_CONTROL_PARAMETERS + +#define TMDS2_ENCODER_CONTROL_PARAMETERS TMDS1_ENCODER_CONTROL_PARAMETERS +#define TMDS2_ENCODER_CONTROL_PS_ALLOCATION TMDS2_ENCODER_CONTROL_PARAMETERS + + +//ucTableFormatRevision=1,ucTableContentRevision=2 +typedef struct _LVDS_ENCODER_CONTROL_PARAMETERS_V2 +{ + USHORT usPixelClock; // in 10KHz; for bios convenient + UCHAR ucMisc; // see PANEL_ENCODER_MISC_xx defintions below + UCHAR ucAction; // 0: turn off encoder + // 1: setup and turn on encoder + UCHAR ucTruncate; // bit0=0: Disable truncate + // =1: Enable truncate + // bit4=0: 666RGB + // =1: 888RGB + UCHAR ucSpatial; // bit0=0: Disable spatial dithering + // =1: Enable spatial dithering + // bit4=0: 666RGB + // =1: 888RGB + UCHAR ucTemporal; // bit0=0: Disable temporal dithering + // =1: Enable temporal dithering + // bit4=0: 666RGB + // =1: 888RGB + // bit5=0: Gray level 2 + // =1: Gray level 4 + UCHAR ucFRC; // bit4=0: 25FRC_SEL pattern E + // =1: 25FRC_SEL pattern F + // bit6:5=0: 50FRC_SEL pattern A + // =1: 50FRC_SEL pattern B + // =2: 50FRC_SEL pattern C + // =3: 50FRC_SEL pattern D + // bit7=0: 75FRC_SEL pattern E + // =1: 75FRC_SEL pattern F +}LVDS_ENCODER_CONTROL_PARAMETERS_V2; + +#define LVDS_ENCODER_CONTROL_PS_ALLOCATION_V2 LVDS_ENCODER_CONTROL_PARAMETERS_V2 + +#define TMDS1_ENCODER_CONTROL_PARAMETERS_V2 LVDS_ENCODER_CONTROL_PARAMETERS_V2 +#define TMDS1_ENCODER_CONTROL_PS_ALLOCATION_V2 TMDS1_ENCODER_CONTROL_PARAMETERS_V2 + +#define TMDS2_ENCODER_CONTROL_PARAMETERS_V2 TMDS1_ENCODER_CONTROL_PARAMETERS_V2 +#define TMDS2_ENCODER_CONTROL_PS_ALLOCATION_V2 TMDS2_ENCODER_CONTROL_PARAMETERS_V2 + +#define LVDS_ENCODER_CONTROL_PARAMETERS_V3 LVDS_ENCODER_CONTROL_PARAMETERS_V2 +#define LVDS_ENCODER_CONTROL_PS_ALLOCATION_V3 LVDS_ENCODER_CONTROL_PARAMETERS_V3 + +#define TMDS1_ENCODER_CONTROL_PARAMETERS_V3 LVDS_ENCODER_CONTROL_PARAMETERS_V3 +#define TMDS1_ENCODER_CONTROL_PS_ALLOCATION_V3 TMDS1_ENCODER_CONTROL_PARAMETERS_V3 + +#define TMDS2_ENCODER_CONTROL_PARAMETERS_V3 LVDS_ENCODER_CONTROL_PARAMETERS_V3 +#define TMDS2_ENCODER_CONTROL_PS_ALLOCATION_V3 TMDS2_ENCODER_CONTROL_PARAMETERS_V3 + +/****************************************************************************/ +// Structures used by ### +/****************************************************************************/ +typedef struct _ENABLE_EXTERNAL_TMDS_ENCODER_PARAMETERS +{ + UCHAR ucEnable; // Enable or Disable External TMDS encoder + UCHAR ucMisc; // Bit0=0:Enable Single link;=1:Enable Dual link;Bit1 {=0:666RGB, =1:888RGB} + UCHAR ucPadding[2]; +}ENABLE_EXTERNAL_TMDS_ENCODER_PARAMETERS; + +typedef struct _ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION +{ + ENABLE_EXTERNAL_TMDS_ENCODER_PARAMETERS sXTmdsEncoder; + WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION sReserved; //Caller doesn't need to init this portion +}ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION; + +#define ENABLE_EXTERNAL_TMDS_ENCODER_PARAMETERS_V2 LVDS_ENCODER_CONTROL_PARAMETERS_V2 + +typedef struct _ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION_V2 +{ + ENABLE_EXTERNAL_TMDS_ENCODER_PARAMETERS_V2 sXTmdsEncoder; + WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION sReserved; //Caller doesn't need to init this portion +}ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION_V2; + +typedef struct _EXTERNAL_ENCODER_CONTROL_PS_ALLOCATION +{ + DIG_ENCODER_CONTROL_PARAMETERS sDigEncoder; + WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION sReserved; +}EXTERNAL_ENCODER_CONTROL_PS_ALLOCATION; + +/****************************************************************************/ +// Structures used by DVOEncoderControlTable +/****************************************************************************/ +//ucTableFormatRevision=1,ucTableContentRevision=3 + +//ucDVOConfig: +#define DVO_ENCODER_CONFIG_RATE_SEL 0x01 +#define DVO_ENCODER_CONFIG_DDR_SPEED 0x00 +#define DVO_ENCODER_CONFIG_SDR_SPEED 0x01 +#define DVO_ENCODER_CONFIG_OUTPUT_SEL 0x0c +#define DVO_ENCODER_CONFIG_LOW12BIT 0x00 +#define DVO_ENCODER_CONFIG_UPPER12BIT 0x04 +#define DVO_ENCODER_CONFIG_24BIT 0x08 + +typedef struct _DVO_ENCODER_CONTROL_PARAMETERS_V3 +{ + USHORT usPixelClock; + UCHAR ucDVOConfig; + UCHAR ucAction; //ATOM_ENABLE/ATOM_DISABLE/ATOM_HPD_INIT + UCHAR ucReseved[4]; +}DVO_ENCODER_CONTROL_PARAMETERS_V3; +#define DVO_ENCODER_CONTROL_PS_ALLOCATION_V3 DVO_ENCODER_CONTROL_PARAMETERS_V3 + +//ucTableFormatRevision=1 +//ucTableContentRevision=3 structure is not changed but usMisc add bit 1 as another input for +// bit1=0: non-coherent mode +// =1: coherent mode + +//========================================================================================== +//Only change is here next time when changing encoder parameter definitions again! +#define LVDS_ENCODER_CONTROL_PARAMETERS_LAST LVDS_ENCODER_CONTROL_PARAMETERS_V3 +#define LVDS_ENCODER_CONTROL_PS_ALLOCATION_LAST LVDS_ENCODER_CONTROL_PARAMETERS_LAST + +#define TMDS1_ENCODER_CONTROL_PARAMETERS_LAST LVDS_ENCODER_CONTROL_PARAMETERS_V3 +#define TMDS1_ENCODER_CONTROL_PS_ALLOCATION_LAST TMDS1_ENCODER_CONTROL_PARAMETERS_LAST + +#define TMDS2_ENCODER_CONTROL_PARAMETERS_LAST LVDS_ENCODER_CONTROL_PARAMETERS_V3 +#define TMDS2_ENCODER_CONTROL_PS_ALLOCATION_LAST TMDS2_ENCODER_CONTROL_PARAMETERS_LAST + +#define DVO_ENCODER_CONTROL_PARAMETERS_LAST DVO_ENCODER_CONTROL_PARAMETERS +#define DVO_ENCODER_CONTROL_PS_ALLOCATION_LAST DVO_ENCODER_CONTROL_PS_ALLOCATION + +//========================================================================================== +#define PANEL_ENCODER_MISC_DUAL 0x01 +#define PANEL_ENCODER_MISC_COHERENT 0x02 +#define PANEL_ENCODER_MISC_TMDS_LINKB 0x04 +#define PANEL_ENCODER_MISC_HDMI_TYPE 0x08 + +#define PANEL_ENCODER_ACTION_DISABLE ATOM_DISABLE +#define PANEL_ENCODER_ACTION_ENABLE ATOM_ENABLE +#define PANEL_ENCODER_ACTION_COHERENTSEQ (ATOM_ENABLE+1) + +#define PANEL_ENCODER_TRUNCATE_EN 0x01 +#define PANEL_ENCODER_TRUNCATE_DEPTH 0x10 +#define PANEL_ENCODER_SPATIAL_DITHER_EN 0x01 +#define PANEL_ENCODER_SPATIAL_DITHER_DEPTH 0x10 +#define PANEL_ENCODER_TEMPORAL_DITHER_EN 0x01 +#define PANEL_ENCODER_TEMPORAL_DITHER_DEPTH 0x10 +#define PANEL_ENCODER_TEMPORAL_LEVEL_4 0x20 +#define PANEL_ENCODER_25FRC_MASK 0x10 +#define PANEL_ENCODER_25FRC_E 0x00 +#define PANEL_ENCODER_25FRC_F 0x10 +#define PANEL_ENCODER_50FRC_MASK 0x60 +#define PANEL_ENCODER_50FRC_A 0x00 +#define PANEL_ENCODER_50FRC_B 0x20 +#define PANEL_ENCODER_50FRC_C 0x40 +#define PANEL_ENCODER_50FRC_D 0x60 +#define PANEL_ENCODER_75FRC_MASK 0x80 +#define PANEL_ENCODER_75FRC_E 0x00 +#define PANEL_ENCODER_75FRC_F 0x80 + +/****************************************************************************/ +// Structures used by SetVoltageTable +/****************************************************************************/ +#define SET_VOLTAGE_TYPE_ASIC_VDDC 1 +#define SET_VOLTAGE_TYPE_ASIC_MVDDC 2 +#define SET_VOLTAGE_TYPE_ASIC_MVDDQ 3 +#define SET_VOLTAGE_TYPE_ASIC_VDDCI 4 +#define SET_VOLTAGE_INIT_MODE 5 +#define SET_VOLTAGE_GET_MAX_VOLTAGE 6 //Gets the Max. voltage for the soldered Asic + +#define SET_ASIC_VOLTAGE_MODE_ALL_SOURCE 0x1 +#define SET_ASIC_VOLTAGE_MODE_SOURCE_A 0x2 +#define SET_ASIC_VOLTAGE_MODE_SOURCE_B 0x4 + +#define SET_ASIC_VOLTAGE_MODE_SET_VOLTAGE 0x0 +#define SET_ASIC_VOLTAGE_MODE_GET_GPIOVAL 0x1 +#define SET_ASIC_VOLTAGE_MODE_GET_GPIOMASK 0x2 + +typedef struct _SET_VOLTAGE_PARAMETERS +{ + UCHAR ucVoltageType; // To tell which voltage to set up, VDDC/MVDDC/MVDDQ + UCHAR ucVoltageMode; // To set all, to set source A or source B or ... + UCHAR ucVoltageIndex; // An index to tell which voltage level + UCHAR ucReserved; +}SET_VOLTAGE_PARAMETERS; + +typedef struct _SET_VOLTAGE_PARAMETERS_V2 +{ + UCHAR ucVoltageType; // To tell which voltage to set up, VDDC/MVDDC/MVDDQ + UCHAR ucVoltageMode; // Not used, maybe use for state machine for differen power mode + USHORT usVoltageLevel; // real voltage level +}SET_VOLTAGE_PARAMETERS_V2; + +typedef struct _SET_VOLTAGE_PS_ALLOCATION +{ + SET_VOLTAGE_PARAMETERS sASICSetVoltage; + WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION sReserved; +}SET_VOLTAGE_PS_ALLOCATION; + +/****************************************************************************/ +// Structures used by TVEncoderControlTable +/****************************************************************************/ +typedef struct _TV_ENCODER_CONTROL_PARAMETERS +{ + USHORT usPixelClock; // in 10KHz; for bios convenient + UCHAR ucTvStandard; // See definition "ATOM_TV_NTSC ..." + UCHAR ucAction; // 0: turn off encoder + // 1: setup and turn on encoder +}TV_ENCODER_CONTROL_PARAMETERS; + +typedef struct _TV_ENCODER_CONTROL_PS_ALLOCATION +{ + TV_ENCODER_CONTROL_PARAMETERS sTVEncoder; + WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION sReserved; // Don't set this one +}TV_ENCODER_CONTROL_PS_ALLOCATION; + +//==============================Data Table Portion==================================== + +#ifdef UEFI_BUILD + #define UTEMP USHORT + #define USHORT void* +#endif + +/****************************************************************************/ +// Structure used in Data.mtb +/****************************************************************************/ +typedef struct _ATOM_MASTER_LIST_OF_DATA_TABLES +{ + USHORT UtilityPipeLine; // Offest for the utility to get parser info,Don't change this position! + USHORT MultimediaCapabilityInfo; // Only used by MM Lib,latest version 1.1, not configuable from Bios, need to include the table to build Bios + USHORT MultimediaConfigInfo; // Only used by MM Lib,latest version 2.1, not configuable from Bios, need to include the table to build Bios + USHORT StandardVESA_Timing; // Only used by Bios + USHORT FirmwareInfo; // Shared by various SW components,latest version 1.4 + USHORT DAC_Info; // Will be obsolete from R600 + USHORT LVDS_Info; // Shared by various SW components,latest version 1.1 + USHORT TMDS_Info; // Will be obsolete from R600 + USHORT AnalogTV_Info; // Shared by various SW components,latest version 1.1 + USHORT SupportedDevicesInfo; // Will be obsolete from R600 + USHORT GPIO_I2C_Info; // Shared by various SW components,latest version 1.2 will be used from R600 + USHORT VRAM_UsageByFirmware; // Shared by various SW components,latest version 1.3 will be used from R600 + USHORT GPIO_Pin_LUT; // Shared by various SW components,latest version 1.1 + USHORT VESA_ToInternalModeLUT; // Only used by Bios + USHORT ComponentVideoInfo; // Shared by various SW components,latest version 2.1 will be used from R600 + USHORT PowerPlayInfo; // Shared by various SW components,latest version 2.1,new design from R600 + USHORT CompassionateData; // Will be obsolete from R600 + USHORT SaveRestoreInfo; // Only used by Bios + USHORT PPLL_SS_Info; // Shared by various SW components,latest version 1.2, used to call SS_Info, change to new name because of int ASIC SS info + USHORT OemInfo; // Defined and used by external SW, should be obsolete soon + USHORT XTMDS_Info; // Will be obsolete from R600 + USHORT MclkSS_Info; // Shared by various SW components,latest version 1.1, only enabled when ext SS chip is used + USHORT Object_Header; // Shared by various SW components,latest version 1.1 + USHORT IndirectIOAccess; // Only used by Bios,this table position can't change at all!! + USHORT MC_InitParameter; // Only used by command table + USHORT ASIC_VDDC_Info; // Will be obsolete from R600 + USHORT ASIC_InternalSS_Info; // New tabel name from R600, used to be called "ASIC_MVDDC_Info" + USHORT TV_VideoMode; // Only used by command table + USHORT VRAM_Info; // Only used by command table, latest version 1.3 + USHORT MemoryTrainingInfo; // Used for VBIOS and Diag utility for memory training purpose since R600. the new table rev start from 2.1 + USHORT IntegratedSystemInfo; // Shared by various SW components + USHORT ASIC_ProfilingInfo; // New table name from R600, used to be called "ASIC_VDDCI_Info" for pre-R600 + USHORT VoltageObjectInfo; // Shared by various SW components, latest version 1.1 + USHORT PowerSourceInfo; // Shared by various SW components, latest versoin 1.1 +}ATOM_MASTER_LIST_OF_DATA_TABLES; + +#ifdef UEFI_BUILD + #define USHORT UTEMP +#endif + +typedef struct _ATOM_MASTER_DATA_TABLE +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_MASTER_LIST_OF_DATA_TABLES ListOfDataTables; +}ATOM_MASTER_DATA_TABLE; + +/****************************************************************************/ +// Structure used in MultimediaCapabilityInfoTable +/****************************************************************************/ +typedef struct _ATOM_MULTIMEDIA_CAPABILITY_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ULONG ulSignature; // HW info table signature string "$ATI" + UCHAR ucI2C_Type; // I2C type (normal GP_IO, ImpactTV GP_IO, Dedicated I2C pin, etc) + UCHAR ucTV_OutInfo; // Type of TV out supported (3:0) and video out crystal frequency (6:4) and TV data port (7) + UCHAR ucVideoPortInfo; // Provides the video port capabilities + UCHAR ucHostPortInfo; // Provides host port configuration information +}ATOM_MULTIMEDIA_CAPABILITY_INFO; + +/****************************************************************************/ +// Structure used in MultimediaConfigInfoTable +/****************************************************************************/ +typedef struct _ATOM_MULTIMEDIA_CONFIG_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ULONG ulSignature; // MM info table signature sting "$MMT" + UCHAR ucTunerInfo; // Type of tuner installed on the adapter (4:0) and video input for tuner (7:5) + UCHAR ucAudioChipInfo; // List the audio chip type (3:0) product type (4) and OEM revision (7:5) + UCHAR ucProductID; // Defines as OEM ID or ATI board ID dependent on product type setting + UCHAR ucMiscInfo1; // Tuner voltage (1:0) HW teletext support (3:2) FM audio decoder (5:4) reserved (6) audio scrambling (7) + UCHAR ucMiscInfo2; // I2S input config (0) I2S output config (1) I2S Audio Chip (4:2) SPDIF Output Config (5) reserved (7:6) + UCHAR ucMiscInfo3; // Video Decoder Type (3:0) Video In Standard/Crystal (7:4) + UCHAR ucMiscInfo4; // Video Decoder Host Config (2:0) reserved (7:3) + UCHAR ucVideoInput0Info;// Video Input 0 Type (1:0) F/B setting (2) physical connector ID (5:3) reserved (7:6) + UCHAR ucVideoInput1Info;// Video Input 1 Type (1:0) F/B setting (2) physical connector ID (5:3) reserved (7:6) + UCHAR ucVideoInput2Info;// Video Input 2 Type (1:0) F/B setting (2) physical connector ID (5:3) reserved (7:6) + UCHAR ucVideoInput3Info;// Video Input 3 Type (1:0) F/B setting (2) physical connector ID (5:3) reserved (7:6) + UCHAR ucVideoInput4Info;// Video Input 4 Type (1:0) F/B setting (2) physical connector ID (5:3) reserved (7:6) +}ATOM_MULTIMEDIA_CONFIG_INFO; + +/****************************************************************************/ +// Structures used in FirmwareInfoTable +/****************************************************************************/ + +// usBIOSCapability Defintion: +// Bit 0 = 0: Bios image is not Posted, =1:Bios image is Posted; +// Bit 1 = 0: Dual CRTC is not supported, =1: Dual CRTC is supported; +// Bit 2 = 0: Extended Desktop is not supported, =1: Extended Desktop is supported; +// Others: Reserved +#define ATOM_BIOS_INFO_ATOM_FIRMWARE_POSTED 0x0001 +#define ATOM_BIOS_INFO_DUAL_CRTC_SUPPORT 0x0002 +#define ATOM_BIOS_INFO_EXTENDED_DESKTOP_SUPPORT 0x0004 +#define ATOM_BIOS_INFO_MEMORY_CLOCK_SS_SUPPORT 0x0008 +#define ATOM_BIOS_INFO_ENGINE_CLOCK_SS_SUPPORT 0x0010 +#define ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU 0x0020 +#define ATOM_BIOS_INFO_WMI_SUPPORT 0x0040 +#define ATOM_BIOS_INFO_PPMODE_ASSIGNGED_BY_SYSTEM 0x0080 +#define ATOM_BIOS_INFO_HYPERMEMORY_SUPPORT 0x0100 +#define ATOM_BIOS_INFO_HYPERMEMORY_SIZE_MASK 0x1E00 +#define ATOM_BIOS_INFO_VPOST_WITHOUT_FIRST_MODE_SET 0x2000 +#define ATOM_BIOS_INFO_BIOS_SCRATCH6_SCL2_REDEFINE 0x4000 + + +#ifndef _H2INC + +//Please don't add or expand this bitfield structure below, this one will retire soon.! +typedef struct _ATOM_FIRMWARE_CAPABILITY +{ +#if ATOM_BIG_ENDIAN + USHORT Reserved:3; + USHORT HyperMemory_Size:4; + USHORT HyperMemory_Support:1; + USHORT PPMode_Assigned:1; + USHORT WMI_SUPPORT:1; + USHORT GPUControlsBL:1; + USHORT EngineClockSS_Support:1; + USHORT MemoryClockSS_Support:1; + USHORT ExtendedDesktopSupport:1; + USHORT DualCRTC_Support:1; + USHORT FirmwarePosted:1; +#else + USHORT FirmwarePosted:1; + USHORT DualCRTC_Support:1; + USHORT ExtendedDesktopSupport:1; + USHORT MemoryClockSS_Support:1; + USHORT EngineClockSS_Support:1; + USHORT GPUControlsBL:1; + USHORT WMI_SUPPORT:1; + USHORT PPMode_Assigned:1; + USHORT HyperMemory_Support:1; + USHORT HyperMemory_Size:4; + USHORT Reserved:3; +#endif +}ATOM_FIRMWARE_CAPABILITY; + +typedef union _ATOM_FIRMWARE_CAPABILITY_ACCESS +{ + ATOM_FIRMWARE_CAPABILITY sbfAccess; + USHORT susAccess; +}ATOM_FIRMWARE_CAPABILITY_ACCESS; + +#else + +typedef union _ATOM_FIRMWARE_CAPABILITY_ACCESS +{ + USHORT susAccess; +}ATOM_FIRMWARE_CAPABILITY_ACCESS; + +#endif + +typedef struct _ATOM_FIRMWARE_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ULONG ulFirmwareRevision; + ULONG ulDefaultEngineClock; //In 10Khz unit + ULONG ulDefaultMemoryClock; //In 10Khz unit + ULONG ulDriverTargetEngineClock; //In 10Khz unit + ULONG ulDriverTargetMemoryClock; //In 10Khz unit + ULONG ulMaxEngineClockPLL_Output; //In 10Khz unit + ULONG ulMaxMemoryClockPLL_Output; //In 10Khz unit + ULONG ulMaxPixelClockPLL_Output; //In 10Khz unit + ULONG ulASICMaxEngineClock; //In 10Khz unit + ULONG ulASICMaxMemoryClock; //In 10Khz unit + UCHAR ucASICMaxTemperature; + UCHAR ucPadding[3]; //Don't use them + ULONG aulReservedForBIOS[3]; //Don't use them + USHORT usMinEngineClockPLL_Input; //In 10Khz unit + USHORT usMaxEngineClockPLL_Input; //In 10Khz unit + USHORT usMinEngineClockPLL_Output; //In 10Khz unit + USHORT usMinMemoryClockPLL_Input; //In 10Khz unit + USHORT usMaxMemoryClockPLL_Input; //In 10Khz unit + USHORT usMinMemoryClockPLL_Output; //In 10Khz unit + USHORT usMaxPixelClock; //In 10Khz unit, Max. Pclk + USHORT usMinPixelClockPLL_Input; //In 10Khz unit + USHORT usMaxPixelClockPLL_Input; //In 10Khz unit + USHORT usMinPixelClockPLL_Output; //In 10Khz unit, the definitions above can't change!!! + ATOM_FIRMWARE_CAPABILITY_ACCESS usFirmwareCapability; + USHORT usReferenceClock; //In 10Khz unit + USHORT usPM_RTS_Location; //RTS PM4 starting location in ROM in 1Kb unit + UCHAR ucPM_RTS_StreamSize; //RTS PM4 packets in Kb unit + UCHAR ucDesign_ID; //Indicate what is the board design + UCHAR ucMemoryModule_ID; //Indicate what is the board design +}ATOM_FIRMWARE_INFO; + +typedef struct _ATOM_FIRMWARE_INFO_V1_2 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ULONG ulFirmwareRevision; + ULONG ulDefaultEngineClock; //In 10Khz unit + ULONG ulDefaultMemoryClock; //In 10Khz unit + ULONG ulDriverTargetEngineClock; //In 10Khz unit + ULONG ulDriverTargetMemoryClock; //In 10Khz unit + ULONG ulMaxEngineClockPLL_Output; //In 10Khz unit + ULONG ulMaxMemoryClockPLL_Output; //In 10Khz unit + ULONG ulMaxPixelClockPLL_Output; //In 10Khz unit + ULONG ulASICMaxEngineClock; //In 10Khz unit + ULONG ulASICMaxMemoryClock; //In 10Khz unit + UCHAR ucASICMaxTemperature; + UCHAR ucMinAllowedBL_Level; + UCHAR ucPadding[2]; //Don't use them + ULONG aulReservedForBIOS[2]; //Don't use them + ULONG ulMinPixelClockPLL_Output; //In 10Khz unit + USHORT usMinEngineClockPLL_Input; //In 10Khz unit + USHORT usMaxEngineClockPLL_Input; //In 10Khz unit + USHORT usMinEngineClockPLL_Output; //In 10Khz unit + USHORT usMinMemoryClockPLL_Input; //In 10Khz unit + USHORT usMaxMemoryClockPLL_Input; //In 10Khz unit + USHORT usMinMemoryClockPLL_Output; //In 10Khz unit + USHORT usMaxPixelClock; //In 10Khz unit, Max. Pclk + USHORT usMinPixelClockPLL_Input; //In 10Khz unit + USHORT usMaxPixelClockPLL_Input; //In 10Khz unit + USHORT usMinPixelClockPLL_Output; //In 10Khz unit - lower 16bit of ulMinPixelClockPLL_Output + ATOM_FIRMWARE_CAPABILITY_ACCESS usFirmwareCapability; + USHORT usReferenceClock; //In 10Khz unit + USHORT usPM_RTS_Location; //RTS PM4 starting location in ROM in 1Kb unit + UCHAR ucPM_RTS_StreamSize; //RTS PM4 packets in Kb unit + UCHAR ucDesign_ID; //Indicate what is the board design + UCHAR ucMemoryModule_ID; //Indicate what is the board design +}ATOM_FIRMWARE_INFO_V1_2; + +typedef struct _ATOM_FIRMWARE_INFO_V1_3 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ULONG ulFirmwareRevision; + ULONG ulDefaultEngineClock; //In 10Khz unit + ULONG ulDefaultMemoryClock; //In 10Khz unit + ULONG ulDriverTargetEngineClock; //In 10Khz unit + ULONG ulDriverTargetMemoryClock; //In 10Khz unit + ULONG ulMaxEngineClockPLL_Output; //In 10Khz unit + ULONG ulMaxMemoryClockPLL_Output; //In 10Khz unit + ULONG ulMaxPixelClockPLL_Output; //In 10Khz unit + ULONG ulASICMaxEngineClock; //In 10Khz unit + ULONG ulASICMaxMemoryClock; //In 10Khz unit + UCHAR ucASICMaxTemperature; + UCHAR ucMinAllowedBL_Level; + UCHAR ucPadding[2]; //Don't use them + ULONG aulReservedForBIOS; //Don't use them + ULONG ul3DAccelerationEngineClock;//In 10Khz unit + ULONG ulMinPixelClockPLL_Output; //In 10Khz unit + USHORT usMinEngineClockPLL_Input; //In 10Khz unit + USHORT usMaxEngineClockPLL_Input; //In 10Khz unit + USHORT usMinEngineClockPLL_Output; //In 10Khz unit + USHORT usMinMemoryClockPLL_Input; //In 10Khz unit + USHORT usMaxMemoryClockPLL_Input; //In 10Khz unit + USHORT usMinMemoryClockPLL_Output; //In 10Khz unit + USHORT usMaxPixelClock; //In 10Khz unit, Max. Pclk + USHORT usMinPixelClockPLL_Input; //In 10Khz unit + USHORT usMaxPixelClockPLL_Input; //In 10Khz unit + USHORT usMinPixelClockPLL_Output; //In 10Khz unit - lower 16bit of ulMinPixelClockPLL_Output + ATOM_FIRMWARE_CAPABILITY_ACCESS usFirmwareCapability; + USHORT usReferenceClock; //In 10Khz unit + USHORT usPM_RTS_Location; //RTS PM4 starting location in ROM in 1Kb unit + UCHAR ucPM_RTS_StreamSize; //RTS PM4 packets in Kb unit + UCHAR ucDesign_ID; //Indicate what is the board design + UCHAR ucMemoryModule_ID; //Indicate what is the board design +}ATOM_FIRMWARE_INFO_V1_3; + +typedef struct _ATOM_FIRMWARE_INFO_V1_4 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ULONG ulFirmwareRevision; + ULONG ulDefaultEngineClock; //In 10Khz unit + ULONG ulDefaultMemoryClock; //In 10Khz unit + ULONG ulDriverTargetEngineClock; //In 10Khz unit + ULONG ulDriverTargetMemoryClock; //In 10Khz unit + ULONG ulMaxEngineClockPLL_Output; //In 10Khz unit + ULONG ulMaxMemoryClockPLL_Output; //In 10Khz unit + ULONG ulMaxPixelClockPLL_Output; //In 10Khz unit + ULONG ulASICMaxEngineClock; //In 10Khz unit + ULONG ulASICMaxMemoryClock; //In 10Khz unit + UCHAR ucASICMaxTemperature; + UCHAR ucMinAllowedBL_Level; + USHORT usBootUpVDDCVoltage; //In MV unit + USHORT usLcdMinPixelClockPLL_Output; // In MHz unit + USHORT usLcdMaxPixelClockPLL_Output; // In MHz unit + ULONG ul3DAccelerationEngineClock;//In 10Khz unit + ULONG ulMinPixelClockPLL_Output; //In 10Khz unit + USHORT usMinEngineClockPLL_Input; //In 10Khz unit + USHORT usMaxEngineClockPLL_Input; //In 10Khz unit + USHORT usMinEngineClockPLL_Output; //In 10Khz unit + USHORT usMinMemoryClockPLL_Input; //In 10Khz unit + USHORT usMaxMemoryClockPLL_Input; //In 10Khz unit + USHORT usMinMemoryClockPLL_Output; //In 10Khz unit + USHORT usMaxPixelClock; //In 10Khz unit, Max. Pclk + USHORT usMinPixelClockPLL_Input; //In 10Khz unit + USHORT usMaxPixelClockPLL_Input; //In 10Khz unit + USHORT usMinPixelClockPLL_Output; //In 10Khz unit - lower 16bit of ulMinPixelClockPLL_Output + ATOM_FIRMWARE_CAPABILITY_ACCESS usFirmwareCapability; + USHORT usReferenceClock; //In 10Khz unit + USHORT usPM_RTS_Location; //RTS PM4 starting location in ROM in 1Kb unit + UCHAR ucPM_RTS_StreamSize; //RTS PM4 packets in Kb unit + UCHAR ucDesign_ID; //Indicate what is the board design + UCHAR ucMemoryModule_ID; //Indicate what is the board design +}ATOM_FIRMWARE_INFO_V1_4; + +#define ATOM_FIRMWARE_INFO_LAST ATOM_FIRMWARE_INFO_V1_4 + +/****************************************************************************/ +// Structures used in IntegratedSystemInfoTable +/****************************************************************************/ +#define IGP_CAP_FLAG_DYNAMIC_CLOCK_EN 0x2 +#define IGP_CAP_FLAG_AC_CARD 0x4 +#define IGP_CAP_FLAG_SDVO_CARD 0x8 +#define IGP_CAP_FLAG_POSTDIV_BY_2_MODE 0x10 + +typedef struct _ATOM_INTEGRATED_SYSTEM_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ULONG ulBootUpEngineClock; //in 10kHz unit + ULONG ulBootUpMemoryClock; //in 10kHz unit + ULONG ulMaxSystemMemoryClock; //in 10kHz unit + ULONG ulMinSystemMemoryClock; //in 10kHz unit + UCHAR ucNumberOfCyclesInPeriodHi; + UCHAR ucLCDTimingSel; //=0:not valid.!=0 sel this timing descriptor from LCD EDID. + USHORT usReserved1; + USHORT usInterNBVoltageLow; //An intermidiate PMW value to set the voltage + USHORT usInterNBVoltageHigh; //Another intermidiate PMW value to set the voltage + ULONG ulReserved[2]; + + USHORT usFSBClock; //In MHz unit + USHORT usCapabilityFlag; //Bit0=1 indicates the fake HDMI support,Bit1=0/1 for Dynamic clocking dis/enable + //Bit[3:2]== 0:No PCIE card, 1:AC card, 2:SDVO card + //Bit[4]==1: P/2 mode, ==0: P/1 mode + USHORT usPCIENBCfgReg7; //bit[7:0]=MUX_Sel, bit[9:8]=MUX_SEL_LEVEL2, bit[10]=Lane_Reversal + USHORT usK8MemoryClock; //in MHz unit + USHORT usK8SyncStartDelay; //in 0.01 us unit + USHORT usK8DataReturnTime; //in 0.01 us unit + UCHAR ucMaxNBVoltage; + UCHAR ucMinNBVoltage; + UCHAR ucMemoryType; //[7:4]=1:DDR1;=2:DDR2;=3:DDR3.[3:0] is reserved + UCHAR ucNumberOfCyclesInPeriod; //CG.FVTHROT_PWM_CTRL_REG0.NumberOfCyclesInPeriod + UCHAR ucStartingPWM_HighTime; //CG.FVTHROT_PWM_CTRL_REG0.StartingPWM_HighTime + UCHAR ucHTLinkWidth; //16 bit vs. 8 bit + UCHAR ucMaxNBVoltageHigh; + UCHAR ucMinNBVoltageHigh; +}ATOM_INTEGRATED_SYSTEM_INFO; + +/* Explanation on entries in ATOM_INTEGRATED_SYSTEM_INFO +ulBootUpMemoryClock: For Intel IGP,it's the UMA system memory clock + For AMD IGP,it's 0 if no SidePort memory installed or it's the boot-up SidePort memory clock +ulMaxSystemMemoryClock: For Intel IGP,it's the Max freq from memory SPD if memory runs in ASYNC mode or otherwise (SYNC mode) it's 0 + For AMD IGP,for now this can be 0 +ulMinSystemMemoryClock: For Intel IGP,it's 133MHz if memory runs in ASYNC mode or otherwise (SYNC mode) it's 0 + For AMD IGP,for now this can be 0 + +usFSBClock: For Intel IGP,it's FSB Freq + For AMD IGP,it's HT Link Speed + +usK8MemoryClock: For AMD IGP only. For RevF CPU, set it to 200 +usK8SyncStartDelay: For AMD IGP only. Memory access latency in K8, required for watermark calculation +usK8DataReturnTime: For AMD IGP only. Memory access latency in K8, required for watermark calculation + +VC:Voltage Control +ucMaxNBVoltage: Voltage regulator dependent PWM value. Low 8 bits of the value for the max voltage.Set this one to 0xFF if VC without PWM. Set this to 0x0 if no VC at all. +ucMinNBVoltage: Voltage regulator dependent PWM value. Low 8 bits of the value for the min voltage.Set this one to 0x00 if VC without PWM or no VC at all. + +ucNumberOfCyclesInPeriod: Indicate how many cycles when PWM duty is 100%. low 8 bits of the value. +ucNumberOfCyclesInPeriodHi: Indicate how many cycles when PWM duty is 100%. high 8 bits of the value.If the PWM has an inverter,set bit [7]==1,otherwise set it 0 + +ucMaxNBVoltageHigh: Voltage regulator dependent PWM value. High 8 bits of the value for the max voltage.Set this one to 0xFF if VC without PWM. Set this to 0x0 if no VC at all. +ucMinNBVoltageHigh: Voltage regulator dependent PWM value. High 8 bits of the value for the min voltage.Set this one to 0x00 if VC without PWM or no VC at all. + + +usInterNBVoltageLow: Voltage regulator dependent PWM value. The value makes the the voltage >=Min NB voltage but <=InterNBVoltageHigh. Set this to 0x0000 if VC without PWM or no VC at all. +usInterNBVoltageHigh: Voltage regulator dependent PWM value. The value makes the the voltage >=InterNBVoltageLow but <=Max NB voltage.Set this to 0x0000 if VC without PWM or no VC at all. +*/ + + +/* +The following IGP table is introduced from RS780, which is supposed to be put by SBIOS in FB before IGP VBIOS starts VPOST; +Then VBIOS will copy the whole structure to its image so all GPU SW components can access this data structure to get whatever they need. +The enough reservation should allow us to never change table revisions. Whenever needed, a GPU SW component can use reserved portion for new data entries. + +SW components can access the IGP system infor structure in the same way as before +*/ + + +typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ULONG ulBootUpEngineClock; //in 10kHz unit + ULONG ulReserved1[2]; //must be 0x0 for the reserved + ULONG ulBootUpUMAClock; //in 10kHz unit + ULONG ulBootUpSidePortClock; //in 10kHz unit + ULONG ulMinSidePortClock; //in 10kHz unit + ULONG ulReserved2[6]; //must be 0x0 for the reserved + ULONG ulSystemConfig; //see explanation below + ULONG ulBootUpReqDisplayVector; + ULONG ulOtherDisplayMisc; + ULONG ulDDISlot1Config; + ULONG ulDDISlot2Config; + UCHAR ucMemoryType; //[3:0]=1:DDR1;=2:DDR2;=3:DDR3.[7:4] is reserved + UCHAR ucUMAChannelNumber; + UCHAR ucDockingPinBit; + UCHAR ucDockingPinPolarity; + ULONG ulDockingPinCFGInfo; + ULONG ulCPUCapInfo; + USHORT usNumberOfCyclesInPeriod; + USHORT usMaxNBVoltage; + USHORT usMinNBVoltage; + USHORT usBootUpNBVoltage; + ULONG ulHTLinkFreq; //in 10Khz + USHORT usMinHTLinkWidth; + USHORT usMaxHTLinkWidth; + USHORT usUMASyncStartDelay; + USHORT usUMADataReturnTime; + USHORT usLinkStatusZeroTime; + USHORT usReserved; + ULONG ulHighVoltageHTLinkFreq; // in 10Khz + ULONG ulLowVoltageHTLinkFreq; // in 10Khz + USHORT usMaxUpStreamHTLinkWidth; + USHORT usMaxDownStreamHTLinkWidth; + USHORT usMinUpStreamHTLinkWidth; + USHORT usMinDownStreamHTLinkWidth; + ULONG ulReserved3[97]; //must be 0x0 +}ATOM_INTEGRATED_SYSTEM_INFO_V2; + +/* +ulBootUpEngineClock: Boot-up Engine Clock in 10Khz; +ulBootUpUMAClock: Boot-up UMA Clock in 10Khz; it must be 0x0 when UMA is not present +ulBootUpSidePortClock: Boot-up SidePort Clock in 10Khz; it must be 0x0 when SidePort Memory is not present,this could be equal to or less than maximum supported Sideport memory clock + +ulSystemConfig: +Bit[0]=1: PowerExpress mode =0 Non-PowerExpress mode; +Bit[1]=1: system boots up at AMD overdrived state or user customized mode. In this case, driver will just stick to this boot-up mode. No other PowerPlay state + =0: system boots up at driver control state. Power state depends on PowerPlay table. +Bit[2]=1: PWM method is used on NB voltage control. =0: GPIO method is used. +Bit[3]=1: Only one power state(Performance) will be supported. + =0: Multiple power states supported from PowerPlay table. +Bit[4]=1: CLMC is supported and enabled on current system. + =0: CLMC is not supported or enabled on current system. SBIOS need to support HT link/freq change through ATIF interface. +Bit[5]=1: Enable CDLW for all driver control power states. Max HT width is from SBIOS, while Min HT width is determined by display requirement. + =0: CDLW is disabled. If CLMC is enabled case, Min HT width will be set equal to Max HT width. If CLMC disabled case, Max HT width will be applied. +Bit[6]=1: High Voltage requested for all power states. In this case, voltage will be forced at 1.1v and powerplay table voltage drop/throttling request will be ignored. + =0: Voltage settings is determined by powerplay table. +Bit[7]=1: Enable CLMC as hybrid Mode. CDLD and CILR will be disabled in this case and we're using legacy C1E. This is workaround for CPU(Griffin) performance issue. + =0: Enable CLMC as regular mode, CDLD and CILR will be enabled. + +ulBootUpReqDisplayVector: This dword is a bit vector indicates what display devices are requested during boot-up. Refer to ATOM_DEVICE_xxx_SUPPORT for the bit vector definitions. + +ulOtherDisplayMisc: [15:8]- Bootup LCD Expansion selection; 0-center, 1-full panel size expansion; + [7:0] - BootupTV standard selection; This is a bit vector to indicate what TV standards are supported by the system. Refer to ucTVSuppportedStd definition; + +ulDDISlot1Config: Describes the PCIE lane configuration on this DDI PCIE slot (ADD2 card) or connector (Mobile design). + [3:0] - Bit vector to indicate PCIE lane config of the DDI slot/connector on chassis (bit 0=1 lane 3:0; bit 1=1 lane 7:4; bit 2=1 lane 11:8; bit 3=1 lane 15:12) + [7:4] - Bit vector to indicate PCIE lane config of the same DDI slot/connector on docking station (bit 0=1 lane 3:0; bit 1=1 lane 7:4; bit 2=1 lane 11:8; bit 3=1 lane 15:12) + [15:8] - Lane configuration attribute; + [23:16]- Connector type, possible value: + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D + CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D + CONNECTOR_OBJECT_ID_HDMI_TYPE_A + CONNECTOR_OBJECT_ID_DISPLAYPORT + [31:24]- Reserved + +ulDDISlot2Config: Same as Slot1. +ucMemoryType: SidePort memory type, set it to 0x0 when Sideport memory is not installed. Driver needs this info to change sideport memory clock. Not for display in CCC. +For IGP, Hypermemory is the only memory type showed in CCC. + +ucUMAChannelNumber: how many channels for the UMA; + +ulDockingPinCFGInfo: [15:0]-Bus/Device/Function # to CFG to read this Docking Pin; [31:16]-reg offset in CFG to read this pin +ucDockingPinBit: which bit in this register to read the pin status; +ucDockingPinPolarity:Polarity of the pin when docked; + +ulCPUCapInfo: [7:0]=1:Griffin;[7:0]=2:Greyhound;[7:0]=3:K8, other bits reserved for now and must be 0x0 + +usNumberOfCyclesInPeriod:Indicate how many cycles when PWM duty is 100%. +usMaxNBVoltage:Max. voltage control value in either PWM or GPIO mode. +usMinNBVoltage:Min. voltage control value in either PWM or GPIO mode. + GPIO mode: both usMaxNBVoltage & usMinNBVoltage have a valid value ulSystemConfig.SYSTEM_CONFIG_USE_PWM_ON_VOLTAGE=0 + PWM mode: both usMaxNBVoltage & usMinNBVoltage have a valid value ulSystemConfig.SYSTEM_CONFIG_USE_PWM_ON_VOLTAGE=1 + GPU SW don't control mode: usMaxNBVoltage & usMinNBVoltage=0 and no care about ulSystemConfig.SYSTEM_CONFIG_USE_PWM_ON_VOLTAGE +usBootUpNBVoltage:Boot-up voltage regulator dependent PWM value. + +ulHTLinkFreq: Bootup HT link Frequency in 10Khz. +usMinHTLinkWidth: Bootup minimum HT link width. If CDLW disabled, this is equal to usMaxHTLinkWidth. + If CDLW enabled, both upstream and downstream width should be the same during bootup. +usMaxHTLinkWidth: Bootup maximum HT link width. If CDLW disabled, this is equal to usMinHTLinkWidth. + If CDLW enabled, both upstream and downstream width should be the same during bootup. + +usUMASyncStartDelay: Memory access latency, required for watermark calculation +usUMADataReturnTime: Memory access latency, required for watermark calculation +usLinkStatusZeroTime:Memory access latency required for watermark calculation, set this to 0x0 for K8 CPU, set a proper value in 0.01 the unit of us +for Griffin or Greyhound. SBIOS needs to convert to actual time by: + if T0Ttime [5:4]=00b, then usLinkStatusZeroTime=T0Ttime [3:0]*0.1us (0.0 to 1.5us) + if T0Ttime [5:4]=01b, then usLinkStatusZeroTime=T0Ttime [3:0]*0.5us (0.0 to 7.5us) + if T0Ttime [5:4]=10b, then usLinkStatusZeroTime=T0Ttime [3:0]*2.0us (0.0 to 30us) + if T0Ttime [5:4]=11b, and T0Ttime [3:0]=0x0 to 0xa, then usLinkStatusZeroTime=T0Ttime [3:0]*20us (0.0 to 200us) + +ulHighVoltageHTLinkFreq: HT link frequency for power state with low voltage. If boot up runs in HT1, this must be 0. + This must be less than or equal to ulHTLinkFreq(bootup frequency). +ulLowVoltageHTLinkFreq: HT link frequency for power state with low voltage or voltage scaling 1.0v~1.1v. If boot up runs in HT1, this must be 0. + This must be less than or equal to ulHighVoltageHTLinkFreq. + +usMaxUpStreamHTLinkWidth: Asymmetric link width support in the future, to replace usMaxHTLinkWidth. Not used for now. +usMaxDownStreamHTLinkWidth: same as above. +usMinUpStreamHTLinkWidth: Asymmetric link width support in the future, to replace usMinHTLinkWidth. Not used for now. +usMinDownStreamHTLinkWidth: same as above. +*/ + + +#define SYSTEM_CONFIG_POWEREXPRESS_ENABLE 0x00000001 +#define SYSTEM_CONFIG_RUN_AT_OVERDRIVE_ENGINE 0x00000002 +#define SYSTEM_CONFIG_USE_PWM_ON_VOLTAGE 0x00000004 +#define SYSTEM_CONFIG_PERFORMANCE_POWERSTATE_ONLY 0x00000008 +#define SYSTEM_CONFIG_CLMC_ENABLED 0x00000010 +#define SYSTEM_CONFIG_CDLW_ENABLED 0x00000020 +#define SYSTEM_CONFIG_HIGH_VOLTAGE_REQUESTED 0x00000040 +#define SYSTEM_CONFIG_CLMC_HYBRID_MODE_ENABLED 0x00000080 + +#define IGP_DDI_SLOT_LANE_CONFIG_MASK 0x000000FF + +#define b0IGP_DDI_SLOT_LANE_MAP_MASK 0x0F +#define b0IGP_DDI_SLOT_DOCKING_LANE_MAP_MASK 0xF0 +#define b0IGP_DDI_SLOT_CONFIG_LANE_0_3 0x01 +#define b0IGP_DDI_SLOT_CONFIG_LANE_4_7 0x02 +#define b0IGP_DDI_SLOT_CONFIG_LANE_8_11 0x04 +#define b0IGP_DDI_SLOT_CONFIG_LANE_12_15 0x08 + +#define IGP_DDI_SLOT_ATTRIBUTE_MASK 0x0000FF00 +#define IGP_DDI_SLOT_CONFIG_REVERSED 0x00000100 +#define b1IGP_DDI_SLOT_CONFIG_REVERSED 0x01 + +#define IGP_DDI_SLOT_CONNECTOR_TYPE_MASK 0x00FF0000 + +#define ATOM_CRT_INT_ENCODER1_INDEX 0x00000000 +#define ATOM_LCD_INT_ENCODER1_INDEX 0x00000001 +#define ATOM_TV_INT_ENCODER1_INDEX 0x00000002 +#define ATOM_DFP_INT_ENCODER1_INDEX 0x00000003 +#define ATOM_CRT_INT_ENCODER2_INDEX 0x00000004 +#define ATOM_LCD_EXT_ENCODER1_INDEX 0x00000005 +#define ATOM_TV_EXT_ENCODER1_INDEX 0x00000006 +#define ATOM_DFP_EXT_ENCODER1_INDEX 0x00000007 +#define ATOM_CV_INT_ENCODER1_INDEX 0x00000008 +#define ATOM_DFP_INT_ENCODER2_INDEX 0x00000009 +#define ATOM_CRT_EXT_ENCODER1_INDEX 0x0000000A +#define ATOM_CV_EXT_ENCODER1_INDEX 0x0000000B +#define ATOM_DFP_INT_ENCODER3_INDEX 0x0000000C +#define ATOM_DFP_INT_ENCODER4_INDEX 0x0000000D + +// define ASIC internal encoder id ( bit vector ) +#define ASIC_INT_DAC1_ENCODER_ID 0x00 +#define ASIC_INT_TV_ENCODER_ID 0x02 +#define ASIC_INT_DIG1_ENCODER_ID 0x03 +#define ASIC_INT_DAC2_ENCODER_ID 0x04 +#define ASIC_EXT_TV_ENCODER_ID 0x06 +#define ASIC_INT_DVO_ENCODER_ID 0x07 +#define ASIC_INT_DIG2_ENCODER_ID 0x09 +#define ASIC_EXT_DIG_ENCODER_ID 0x05 + +//define Encoder attribute +#define ATOM_ANALOG_ENCODER 0 +#define ATOM_DIGITAL_ENCODER 1 + +#define ATOM_DEVICE_CRT1_INDEX 0x00000000 +#define ATOM_DEVICE_LCD1_INDEX 0x00000001 +#define ATOM_DEVICE_TV1_INDEX 0x00000002 +#define ATOM_DEVICE_DFP1_INDEX 0x00000003 +#define ATOM_DEVICE_CRT2_INDEX 0x00000004 +#define ATOM_DEVICE_LCD2_INDEX 0x00000005 +#define ATOM_DEVICE_TV2_INDEX 0x00000006 +#define ATOM_DEVICE_DFP2_INDEX 0x00000007 +#define ATOM_DEVICE_CV_INDEX 0x00000008 +#define ATOM_DEVICE_DFP3_INDEX 0x00000009 +#define ATOM_DEVICE_DFP4_INDEX 0x0000000A +#define ATOM_DEVICE_DFP5_INDEX 0x0000000B +#define ATOM_DEVICE_RESERVEDC_INDEX 0x0000000C +#define ATOM_DEVICE_RESERVEDD_INDEX 0x0000000D +#define ATOM_DEVICE_RESERVEDE_INDEX 0x0000000E +#define ATOM_DEVICE_RESERVEDF_INDEX 0x0000000F +#define ATOM_MAX_SUPPORTED_DEVICE_INFO (ATOM_DEVICE_DFP3_INDEX+1) +#define ATOM_MAX_SUPPORTED_DEVICE_INFO_2 ATOM_MAX_SUPPORTED_DEVICE_INFO +#define ATOM_MAX_SUPPORTED_DEVICE_INFO_3 (ATOM_DEVICE_DFP5_INDEX + 1 ) + +#define ATOM_MAX_SUPPORTED_DEVICE (ATOM_DEVICE_RESERVEDF_INDEX+1) + +#define ATOM_DEVICE_CRT1_SUPPORT (0x1L << ATOM_DEVICE_CRT1_INDEX ) +#define ATOM_DEVICE_LCD1_SUPPORT (0x1L << ATOM_DEVICE_LCD1_INDEX ) +#define ATOM_DEVICE_TV1_SUPPORT (0x1L << ATOM_DEVICE_TV1_INDEX ) +#define ATOM_DEVICE_DFP1_SUPPORT (0x1L << ATOM_DEVICE_DFP1_INDEX) +#define ATOM_DEVICE_CRT2_SUPPORT (0x1L << ATOM_DEVICE_CRT2_INDEX ) +#define ATOM_DEVICE_LCD2_SUPPORT (0x1L << ATOM_DEVICE_LCD2_INDEX ) +#define ATOM_DEVICE_TV2_SUPPORT (0x1L << ATOM_DEVICE_TV2_INDEX ) +#define ATOM_DEVICE_DFP2_SUPPORT (0x1L << ATOM_DEVICE_DFP2_INDEX) +#define ATOM_DEVICE_CV_SUPPORT (0x1L << ATOM_DEVICE_CV_INDEX ) +#define ATOM_DEVICE_DFP3_SUPPORT (0x1L << ATOM_DEVICE_DFP3_INDEX ) +#define ATOM_DEVICE_DFP4_SUPPORT (0x1L << ATOM_DEVICE_DFP4_INDEX ) +#define ATOM_DEVICE_DFP5_SUPPORT (0x1L << ATOM_DEVICE_DFP5_INDEX ) + +#define ATOM_DEVICE_CRT_SUPPORT ATOM_DEVICE_CRT1_SUPPORT | ATOM_DEVICE_CRT2_SUPPORT +#define ATOM_DEVICE_DFP_SUPPORT ATOM_DEVICE_DFP1_SUPPORT | ATOM_DEVICE_DFP2_SUPPORT | ATOM_DEVICE_DFP3_SUPPORT | ATOM_DEVICE_DFP4_SUPPORT | ATOM_DEVICE_DFP5_SUPPORT +#define ATOM_DEVICE_TV_SUPPORT ATOM_DEVICE_TV1_SUPPORT | ATOM_DEVICE_TV2_SUPPORT +#define ATOM_DEVICE_LCD_SUPPORT ATOM_DEVICE_LCD1_SUPPORT | ATOM_DEVICE_LCD2_SUPPORT + +#define ATOM_DEVICE_CONNECTOR_TYPE_MASK 0x000000F0 +#define ATOM_DEVICE_CONNECTOR_TYPE_SHIFT 0x00000004 +#define ATOM_DEVICE_CONNECTOR_VGA 0x00000001 +#define ATOM_DEVICE_CONNECTOR_DVI_I 0x00000002 +#define ATOM_DEVICE_CONNECTOR_DVI_D 0x00000003 +#define ATOM_DEVICE_CONNECTOR_DVI_A 0x00000004 +#define ATOM_DEVICE_CONNECTOR_SVIDEO 0x00000005 +#define ATOM_DEVICE_CONNECTOR_COMPOSITE 0x00000006 +#define ATOM_DEVICE_CONNECTOR_LVDS 0x00000007 +#define ATOM_DEVICE_CONNECTOR_DIGI_LINK 0x00000008 +#define ATOM_DEVICE_CONNECTOR_SCART 0x00000009 +#define ATOM_DEVICE_CONNECTOR_HDMI_TYPE_A 0x0000000A +#define ATOM_DEVICE_CONNECTOR_HDMI_TYPE_B 0x0000000B +#define ATOM_DEVICE_CONNECTOR_CASE_1 0x0000000E +#define ATOM_DEVICE_CONNECTOR_DISPLAYPORT 0x0000000F + + +#define ATOM_DEVICE_DAC_INFO_MASK 0x0000000F +#define ATOM_DEVICE_DAC_INFO_SHIFT 0x00000000 +#define ATOM_DEVICE_DAC_INFO_NODAC 0x00000000 +#define ATOM_DEVICE_DAC_INFO_DACA 0x00000001 +#define ATOM_DEVICE_DAC_INFO_DACB 0x00000002 +#define ATOM_DEVICE_DAC_INFO_EXDAC 0x00000003 + +#define ATOM_DEVICE_I2C_ID_NOI2C 0x00000000 + +#define ATOM_DEVICE_I2C_LINEMUX_MASK 0x0000000F +#define ATOM_DEVICE_I2C_LINEMUX_SHIFT 0x00000000 + +#define ATOM_DEVICE_I2C_ID_MASK 0x00000070 +#define ATOM_DEVICE_I2C_ID_SHIFT 0x00000004 +#define ATOM_DEVICE_I2C_ID_IS_FOR_NON_MM_USE 0x00000001 +#define ATOM_DEVICE_I2C_ID_IS_FOR_MM_USE 0x00000002 +#define ATOM_DEVICE_I2C_ID_IS_FOR_SDVO_USE 0x00000003 //For IGP RS600 +#define ATOM_DEVICE_I2C_ID_IS_FOR_DAC_SCL 0x00000004 //For IGP RS690 + +#define ATOM_DEVICE_I2C_HARDWARE_CAP_MASK 0x00000080 +#define ATOM_DEVICE_I2C_HARDWARE_CAP_SHIFT 0x00000007 +#define ATOM_DEVICE_USES_SOFTWARE_ASSISTED_I2C 0x00000000 +#define ATOM_DEVICE_USES_HARDWARE_ASSISTED_I2C 0x00000001 + +// usDeviceSupport: +// Bits0 = 0 - no CRT1 support= 1- CRT1 is supported +// Bit 1 = 0 - no LCD1 support= 1- LCD1 is supported +// Bit 2 = 0 - no TV1 support= 1- TV1 is supported +// Bit 3 = 0 - no DFP1 support= 1- DFP1 is supported +// Bit 4 = 0 - no CRT2 support= 1- CRT2 is supported +// Bit 5 = 0 - no LCD2 support= 1- LCD2 is supported +// Bit 6 = 0 - no TV2 support= 1- TV2 is supported +// Bit 7 = 0 - no DFP2 support= 1- DFP2 is supported +// Bit 8 = 0 - no CV support= 1- CV is supported +// Bit 9 = 0 - no DFP3 support= 1- DFP3 is supported +// Byte1 (Supported Device Info) +// Bit 0 = = 0 - no CV support= 1- CV is supported +// +// + +// ucI2C_ConfigID +// [7:0] - I2C LINE Associate ID +// = 0 - no I2C +// [7] - HW_Cap = 1, [6:0]=HW assisted I2C ID(HW line selection) +// = 0, [6:0]=SW assisted I2C ID +// [6-4] - HW_ENGINE_ID = 1, HW engine for NON multimedia use +// = 2, HW engine for Multimedia use +// = 3-7 Reserved for future I2C engines +// [3-0] - I2C_LINE_MUX = A Mux number when it's HW assisted I2C or GPIO ID when it's SW I2C + +typedef struct _ATOM_I2C_ID_CONFIG +{ +#if ATOM_BIG_ENDIAN + UCHAR bfHW_Capable:1; + UCHAR bfHW_EngineID:3; + UCHAR bfI2C_LineMux:4; +#else + UCHAR bfI2C_LineMux:4; + UCHAR bfHW_EngineID:3; + UCHAR bfHW_Capable:1; +#endif +}ATOM_I2C_ID_CONFIG; + +typedef union _ATOM_I2C_ID_CONFIG_ACCESS +{ + ATOM_I2C_ID_CONFIG sbfAccess; + UCHAR ucAccess; +}ATOM_I2C_ID_CONFIG_ACCESS; + + +/****************************************************************************/ +// Structure used in GPIO_I2C_InfoTable +/****************************************************************************/ +typedef struct _ATOM_GPIO_I2C_ASSIGMENT +{ + USHORT usClkMaskRegisterIndex; + USHORT usClkEnRegisterIndex; + USHORT usClkY_RegisterIndex; + USHORT usClkA_RegisterIndex; + USHORT usDataMaskRegisterIndex; + USHORT usDataEnRegisterIndex; + USHORT usDataY_RegisterIndex; + USHORT usDataA_RegisterIndex; + ATOM_I2C_ID_CONFIG_ACCESS sucI2cId; + UCHAR ucClkMaskShift; + UCHAR ucClkEnShift; + UCHAR ucClkY_Shift; + UCHAR ucClkA_Shift; + UCHAR ucDataMaskShift; + UCHAR ucDataEnShift; + UCHAR ucDataY_Shift; + UCHAR ucDataA_Shift; + UCHAR ucReserved1; + UCHAR ucReserved2; +}ATOM_GPIO_I2C_ASSIGMENT; + +typedef struct _ATOM_GPIO_I2C_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_GPIO_I2C_ASSIGMENT asGPIO_Info[ATOM_MAX_SUPPORTED_DEVICE]; +}ATOM_GPIO_I2C_INFO; + +/****************************************************************************/ +// Common Structure used in other structures +/****************************************************************************/ + +#ifndef _H2INC + +//Please don't add or expand this bitfield structure below, this one will retire soon.! +typedef struct _ATOM_MODE_MISC_INFO +{ +#if ATOM_BIG_ENDIAN + USHORT Reserved:6; + USHORT RGB888:1; + USHORT DoubleClock:1; + USHORT Interlace:1; + USHORT CompositeSync:1; + USHORT V_ReplicationBy2:1; + USHORT H_ReplicationBy2:1; + USHORT VerticalCutOff:1; + USHORT VSyncPolarity:1; //0=Active High, 1=Active Low + USHORT HSyncPolarity:1; //0=Active High, 1=Active Low + USHORT HorizontalCutOff:1; +#else + USHORT HorizontalCutOff:1; + USHORT HSyncPolarity:1; //0=Active High, 1=Active Low + USHORT VSyncPolarity:1; //0=Active High, 1=Active Low + USHORT VerticalCutOff:1; + USHORT H_ReplicationBy2:1; + USHORT V_ReplicationBy2:1; + USHORT CompositeSync:1; + USHORT Interlace:1; + USHORT DoubleClock:1; + USHORT RGB888:1; + USHORT Reserved:6; +#endif +}ATOM_MODE_MISC_INFO; + +typedef union _ATOM_MODE_MISC_INFO_ACCESS +{ + ATOM_MODE_MISC_INFO sbfAccess; + USHORT usAccess; +}ATOM_MODE_MISC_INFO_ACCESS; + +#else + +typedef union _ATOM_MODE_MISC_INFO_ACCESS +{ + USHORT usAccess; +}ATOM_MODE_MISC_INFO_ACCESS; + +#endif + +// usModeMiscInfo- +#define ATOM_H_CUTOFF 0x01 +#define ATOM_HSYNC_POLARITY 0x02 //0=Active High, 1=Active Low +#define ATOM_VSYNC_POLARITY 0x04 //0=Active High, 1=Active Low +#define ATOM_V_CUTOFF 0x08 +#define ATOM_H_REPLICATIONBY2 0x10 +#define ATOM_V_REPLICATIONBY2 0x20 +#define ATOM_COMPOSITESYNC 0x40 +#define ATOM_INTERLACE 0x80 +#define ATOM_DOUBLE_CLOCK_MODE 0x100 +#define ATOM_RGB888_MODE 0x200 + +//usRefreshRate- +#define ATOM_REFRESH_43 43 +#define ATOM_REFRESH_47 47 +#define ATOM_REFRESH_56 56 +#define ATOM_REFRESH_60 60 +#define ATOM_REFRESH_65 65 +#define ATOM_REFRESH_70 70 +#define ATOM_REFRESH_72 72 +#define ATOM_REFRESH_75 75 +#define ATOM_REFRESH_85 85 + +// ATOM_MODE_TIMING data are exactly the same as VESA timing data. +// Translation from EDID to ATOM_MODE_TIMING, use the following formula. +// +// VESA_HTOTAL = VESA_ACTIVE + 2* VESA_BORDER + VESA_BLANK +// = EDID_HA + EDID_HBL +// VESA_HDISP = VESA_ACTIVE = EDID_HA +// VESA_HSYNC_START = VESA_ACTIVE + VESA_BORDER + VESA_FRONT_PORCH +// = EDID_HA + EDID_HSO +// VESA_HSYNC_WIDTH = VESA_HSYNC_TIME = EDID_HSPW +// VESA_BORDER = EDID_BORDER + +/****************************************************************************/ +// Structure used in SetCRTC_UsingDTDTimingTable +/****************************************************************************/ +typedef struct _SET_CRTC_USING_DTD_TIMING_PARAMETERS +{ + USHORT usH_Size; + USHORT usH_Blanking_Time; + USHORT usV_Size; + USHORT usV_Blanking_Time; + USHORT usH_SyncOffset; + USHORT usH_SyncWidth; + USHORT usV_SyncOffset; + USHORT usV_SyncWidth; + ATOM_MODE_MISC_INFO_ACCESS susModeMiscInfo; + UCHAR ucH_Border; // From DFP EDID + UCHAR ucV_Border; + UCHAR ucCRTC; // ATOM_CRTC1 or ATOM_CRTC2 + UCHAR ucPadding[3]; +}SET_CRTC_USING_DTD_TIMING_PARAMETERS; + +/****************************************************************************/ +// Structure used in SetCRTC_TimingTable +/****************************************************************************/ +typedef struct _SET_CRTC_TIMING_PARAMETERS +{ + USHORT usH_Total; // horizontal total + USHORT usH_Disp; // horizontal display + USHORT usH_SyncStart; // horozontal Sync start + USHORT usH_SyncWidth; // horizontal Sync width + USHORT usV_Total; // vertical total + USHORT usV_Disp; // vertical display + USHORT usV_SyncStart; // vertical Sync start + USHORT usV_SyncWidth; // vertical Sync width + ATOM_MODE_MISC_INFO_ACCESS susModeMiscInfo; + UCHAR ucCRTC; // ATOM_CRTC1 or ATOM_CRTC2 + UCHAR ucOverscanRight; // right + UCHAR ucOverscanLeft; // left + UCHAR ucOverscanBottom; // bottom + UCHAR ucOverscanTop; // top + UCHAR ucReserved; +}SET_CRTC_TIMING_PARAMETERS; +#define SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION SET_CRTC_TIMING_PARAMETERS + +/****************************************************************************/ +// Structure used in StandardVESA_TimingTable +// AnalogTV_InfoTable +// ComponentVideoInfoTable +/****************************************************************************/ +typedef struct _ATOM_MODE_TIMING +{ + USHORT usCRTC_H_Total; + USHORT usCRTC_H_Disp; + USHORT usCRTC_H_SyncStart; + USHORT usCRTC_H_SyncWidth; + USHORT usCRTC_V_Total; + USHORT usCRTC_V_Disp; + USHORT usCRTC_V_SyncStart; + USHORT usCRTC_V_SyncWidth; + USHORT usPixelClock; //in 10Khz unit + ATOM_MODE_MISC_INFO_ACCESS susModeMiscInfo; + USHORT usCRTC_OverscanRight; + USHORT usCRTC_OverscanLeft; + USHORT usCRTC_OverscanBottom; + USHORT usCRTC_OverscanTop; + USHORT usReserve; + UCHAR ucInternalModeNumber; + UCHAR ucRefreshRate; +}ATOM_MODE_TIMING; + +typedef struct _ATOM_DTD_FORMAT +{ + USHORT usPixClk; + USHORT usHActive; + USHORT usHBlanking_Time; + USHORT usVActive; + USHORT usVBlanking_Time; + USHORT usHSyncOffset; + USHORT usHSyncWidth; + USHORT usVSyncOffset; + USHORT usVSyncWidth; + USHORT usImageHSize; + USHORT usImageVSize; + UCHAR ucHBorder; + UCHAR ucVBorder; + ATOM_MODE_MISC_INFO_ACCESS susModeMiscInfo; + UCHAR ucInternalModeNumber; + UCHAR ucRefreshRate; +}ATOM_DTD_FORMAT; + +/****************************************************************************/ +// Structure used in LVDS_InfoTable +// * Need a document to describe this table +/****************************************************************************/ +#define SUPPORTED_LCD_REFRESHRATE_30Hz 0x0004 +#define SUPPORTED_LCD_REFRESHRATE_40Hz 0x0008 +#define SUPPORTED_LCD_REFRESHRATE_50Hz 0x0010 +#define SUPPORTED_LCD_REFRESHRATE_60Hz 0x0020 + +//Once DAL sees this CAP is set, it will read EDID from LCD on its own instead of using sLCDTiming in ATOM_LVDS_INFO_V12. +//Other entries in ATOM_LVDS_INFO_V12 are still valid/useful to DAL +#define LCDPANEL_CAP_READ_EDID 0x1 + +//ucTableFormatRevision=1 +//ucTableContentRevision=1 +typedef struct _ATOM_LVDS_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_DTD_FORMAT sLCDTiming; + USHORT usModePatchTableOffset; + USHORT usSupportedRefreshRate; //Refer to panel info table in ATOMBIOS extension Spec. + USHORT usOffDelayInMs; + UCHAR ucPowerSequenceDigOntoDEin10Ms; + UCHAR ucPowerSequenceDEtoBLOnin10Ms; + UCHAR ucLVDS_Misc; // Bit0:{=0:single, =1:dual},Bit1 {=0:666RGB, =1:888RGB},Bit2:3:{Grey level} + // Bit4:{=0:LDI format for RGB888, =1 FPDI format for RGB888} + // Bit5:{=0:Spatial Dithering disabled;1 Spatial Dithering enabled} + // Bit6:{=0:Temporal Dithering disabled;1 Temporal Dithering enabled} + UCHAR ucPanelDefaultRefreshRate; + UCHAR ucPanelIdentification; + UCHAR ucSS_Id; +}ATOM_LVDS_INFO; + +//ucTableFormatRevision=1 +//ucTableContentRevision=2 +typedef struct _ATOM_LVDS_INFO_V12 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_DTD_FORMAT sLCDTiming; + USHORT usExtInfoTableOffset; + USHORT usSupportedRefreshRate; //Refer to panel info table in ATOMBIOS extension Spec. + USHORT usOffDelayInMs; + UCHAR ucPowerSequenceDigOntoDEin10Ms; + UCHAR ucPowerSequenceDEtoBLOnin10Ms; + UCHAR ucLVDS_Misc; // Bit0:{=0:single, =1:dual},Bit1 {=0:666RGB, =1:888RGB},Bit2:3:{Grey level} + // Bit4:{=0:LDI format for RGB888, =1 FPDI format for RGB888} + // Bit5:{=0:Spatial Dithering disabled;1 Spatial Dithering enabled} + // Bit6:{=0:Temporal Dithering disabled;1 Temporal Dithering enabled} + UCHAR ucPanelDefaultRefreshRate; + UCHAR ucPanelIdentification; + UCHAR ucSS_Id; + USHORT usLCDVenderID; + USHORT usLCDProductID; + UCHAR ucLCDPanel_SpecialHandlingCap; + UCHAR ucPanelInfoSize; // start from ATOM_DTD_FORMAT to end of panel info, include ExtInfoTable + UCHAR ucReserved[2]; +}ATOM_LVDS_INFO_V12; + +#define ATOM_LVDS_INFO_LAST ATOM_LVDS_INFO_V12 + +typedef struct _ATOM_PATCH_RECORD_MODE +{ + UCHAR ucRecordType; + USHORT usHDisp; + USHORT usVDisp; +}ATOM_PATCH_RECORD_MODE; + +typedef struct _ATOM_LCD_RTS_RECORD +{ + UCHAR ucRecordType; + UCHAR ucRTSValue; +}ATOM_LCD_RTS_RECORD; + +//!! If the record below exits, it shoud always be the first record for easy use in command table!!! +typedef struct _ATOM_LCD_MODE_CONTROL_CAP +{ + UCHAR ucRecordType; + USHORT usLCDCap; +}ATOM_LCD_MODE_CONTROL_CAP; + +#define LCD_MODE_CAP_BL_OFF 1 +#define LCD_MODE_CAP_CRTC_OFF 2 +#define LCD_MODE_CAP_PANEL_OFF 4 + +typedef struct _ATOM_FAKE_EDID_PATCH_RECORD +{ + UCHAR ucRecordType; + UCHAR ucFakeEDIDLength; + UCHAR ucFakeEDIDString[1]; // This actually has ucFakeEdidLength elements. +} ATOM_FAKE_EDID_PATCH_RECORD; + +typedef struct _ATOM_PANEL_RESOLUTION_PATCH_RECORD +{ + UCHAR ucRecordType; + USHORT usHSize; + USHORT usVSize; +}ATOM_PANEL_RESOLUTION_PATCH_RECORD; + +#define LCD_MODE_PATCH_RECORD_MODE_TYPE 1 +#define LCD_RTS_RECORD_TYPE 2 +#define LCD_CAP_RECORD_TYPE 3 +#define LCD_FAKE_EDID_PATCH_RECORD_TYPE 4 +#define LCD_PANEL_RESOLUTION_RECORD_TYPE 5 +#define ATOM_RECORD_END_TYPE 0xFF + +/****************************Spread Spectrum Info Table Definitions **********************/ + +//ucTableFormatRevision=1 +//ucTableContentRevision=2 +typedef struct _ATOM_SPREAD_SPECTRUM_ASSIGNMENT +{ + USHORT usSpreadSpectrumPercentage; + UCHAR ucSpreadSpectrumType; //Bit1=0 Down Spread,=1 Center Spread. Bit1=1 Ext. =0 Int. Others:TBD + UCHAR ucSS_Step; + UCHAR ucSS_Delay; + UCHAR ucSS_Id; + UCHAR ucRecommandedRef_Div; + UCHAR ucSS_Range; //it was reserved for V11 +}ATOM_SPREAD_SPECTRUM_ASSIGNMENT; + +#define ATOM_MAX_SS_ENTRY 16 +#define ATOM_DP_SS_ID1 0x0f1 // SS modulation freq=30k +#define ATOM_DP_SS_ID2 0x0f2 // SS modulation freq=33k + + +#define ATOM_SS_DOWN_SPREAD_MODE_MASK 0x00000000 +#define ATOM_SS_DOWN_SPREAD_MODE 0x00000000 +#define ATOM_SS_CENTRE_SPREAD_MODE_MASK 0x00000001 +#define ATOM_SS_CENTRE_SPREAD_MODE 0x00000001 +#define ATOM_INTERNAL_SS_MASK 0x00000000 +#define ATOM_EXTERNAL_SS_MASK 0x00000002 +#define EXEC_SS_STEP_SIZE_SHIFT 2 +#define EXEC_SS_DELAY_SHIFT 4 +#define ACTIVEDATA_TO_BLON_DELAY_SHIFT 4 + +typedef struct _ATOM_SPREAD_SPECTRUM_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_SPREAD_SPECTRUM_ASSIGNMENT asSS_Info[ATOM_MAX_SS_ENTRY]; +}ATOM_SPREAD_SPECTRUM_INFO; + +/****************************************************************************/ +// Structure used in AnalogTV_InfoTable (Top level) +/****************************************************************************/ +//ucTVBootUpDefaultStd definiton: + +//ATOM_TV_NTSC 1 +//ATOM_TV_NTSCJ 2 +//ATOM_TV_PAL 3 +//ATOM_TV_PALM 4 +//ATOM_TV_PALCN 5 +//ATOM_TV_PALN 6 +//ATOM_TV_PAL60 7 +//ATOM_TV_SECAM 8 + +//ucTVSuppportedStd definition: +#define NTSC_SUPPORT 0x1 +#define NTSCJ_SUPPORT 0x2 + +#define PAL_SUPPORT 0x4 +#define PALM_SUPPORT 0x8 +#define PALCN_SUPPORT 0x10 +#define PALN_SUPPORT 0x20 +#define PAL60_SUPPORT 0x40 +#define SECAM_SUPPORT 0x80 + +#define MAX_SUPPORTED_TV_TIMING 2 + +typedef struct _ATOM_ANALOG_TV_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + UCHAR ucTV_SupportedStandard; + UCHAR ucTV_BootUpDefaultStandard; + UCHAR ucExt_TV_ASIC_ID; + UCHAR ucExt_TV_ASIC_SlaveAddr; + /*ATOM_DTD_FORMAT aModeTimings[MAX_SUPPORTED_TV_TIMING];*/ + ATOM_MODE_TIMING aModeTimings[MAX_SUPPORTED_TV_TIMING]; +}ATOM_ANALOG_TV_INFO; + + +/**************************************************************************/ +// VRAM usage and their defintions + +// One chunk of VRAM used by Bios are for HWICON surfaces,EDID data. +// Current Mode timing and Dail Timing and/or STD timing data EACH device. They can be broken down as below. +// All the addresses below are the offsets from the frame buffer start.They all MUST be Dword aligned! +// To driver: The physical address of this memory portion=mmFB_START(4K aligned)+ATOMBIOS_VRAM_USAGE_START_ADDR+ATOM_x_ADDR +// To Bios: ATOMBIOS_VRAM_USAGE_START_ADDR+ATOM_x_ADDR->MM_INDEX + +#ifndef VESA_MEMORY_IN_64K_BLOCK +#define VESA_MEMORY_IN_64K_BLOCK 0x100 //256*64K=16Mb (Max. VESA memory is 16Mb!) +#endif + +#define ATOM_EDID_RAW_DATASIZE 256 //In Bytes +#define ATOM_HWICON_SURFACE_SIZE 4096 //In Bytes +#define ATOM_HWICON_INFOTABLE_SIZE 32 +#define MAX_DTD_MODE_IN_VRAM 6 +#define ATOM_DTD_MODE_SUPPORT_TBL_SIZE (MAX_DTD_MODE_IN_VRAM*28) //28= (SIZEOF ATOM_DTD_FORMAT) +#define ATOM_STD_MODE_SUPPORT_TBL_SIZE 32*8 //32 is a predefined number,8= (SIZEOF ATOM_STD_FORMAT) +#define DFP_ENCODER_TYPE_OFFSET 0x80 +#define DP_ENCODER_LANE_NUM_OFFSET 0x84 +#define DP_ENCODER_LINK_RATE_OFFSET 0x88 + +#define ATOM_HWICON1_SURFACE_ADDR 0 +#define ATOM_HWICON2_SURFACE_ADDR (ATOM_HWICON1_SURFACE_ADDR + ATOM_HWICON_SURFACE_SIZE) +#define ATOM_HWICON_INFOTABLE_ADDR (ATOM_HWICON2_SURFACE_ADDR + ATOM_HWICON_SURFACE_SIZE) +#define ATOM_CRT1_EDID_ADDR (ATOM_HWICON_INFOTABLE_ADDR + ATOM_HWICON_INFOTABLE_SIZE) +#define ATOM_CRT1_DTD_MODE_TBL_ADDR (ATOM_CRT1_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_CRT1_STD_MODE_TBL_ADDR (ATOM_CRT1_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_LCD1_EDID_ADDR (ATOM_CRT1_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_LCD1_DTD_MODE_TBL_ADDR (ATOM_LCD1_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_LCD1_STD_MODE_TBL_ADDR (ATOM_LCD1_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_TV1_DTD_MODE_TBL_ADDR (ATOM_LCD1_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_DFP1_EDID_ADDR (ATOM_TV1_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_DFP1_DTD_MODE_TBL_ADDR (ATOM_DFP1_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_DFP1_STD_MODE_TBL_ADDR (ATOM_DFP1_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_CRT2_EDID_ADDR (ATOM_DFP1_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_CRT2_DTD_MODE_TBL_ADDR (ATOM_CRT2_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_CRT2_STD_MODE_TBL_ADDR (ATOM_CRT2_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_LCD2_EDID_ADDR (ATOM_CRT2_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_LCD2_DTD_MODE_TBL_ADDR (ATOM_LCD2_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_LCD2_STD_MODE_TBL_ADDR (ATOM_LCD2_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_TV2_EDID_ADDR (ATOM_LCD2_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_TV2_DTD_MODE_TBL_ADDR (ATOM_TV2_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_TV2_STD_MODE_TBL_ADDR (ATOM_TV2_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_DFP2_EDID_ADDR (ATOM_TV2_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_DFP2_DTD_MODE_TBL_ADDR (ATOM_DFP2_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_DFP2_STD_MODE_TBL_ADDR (ATOM_DFP2_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_CV_EDID_ADDR (ATOM_DFP2_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_CV_DTD_MODE_TBL_ADDR (ATOM_CV_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_CV_STD_MODE_TBL_ADDR (ATOM_CV_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_DFP3_EDID_ADDR (ATOM_CV_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_DFP3_DTD_MODE_TBL_ADDR (ATOM_DFP3_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_DFP3_STD_MODE_TBL_ADDR (ATOM_DFP3_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_DFP4_EDID_ADDR (ATOM_DFP3_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_DFP4_DTD_MODE_TBL_ADDR (ATOM_DFP4_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_DFP4_STD_MODE_TBL_ADDR (ATOM_DFP4_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_DFP5_EDID_ADDR (ATOM_DFP4_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_DFP5_DTD_MODE_TBL_ADDR (ATOM_DFP5_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_DFP5_STD_MODE_TBL_ADDR (ATOM_DFP5_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_DP_TRAINING_TBL_ADDR (ATOM_DFP5_STD_MODE_TBL_ADDR+ATOM_STD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_STACK_STORAGE_START (ATOM_DP_TRAINING_TBL_ADDR+256) +#define ATOM_STACK_STORAGE_END ATOM_STACK_STORAGE_START+512 + +//The size below is in Kb! +#define ATOM_VRAM_RESERVE_SIZE ((((ATOM_STACK_STORAGE_END - ATOM_HWICON1_SURFACE_ADDR)>>10)+4)&0xFFFC) + +#define ATOM_VRAM_OPERATION_FLAGS_MASK 0xC0000000L +#define ATOM_VRAM_OPERATION_FLAGS_SHIFT 30 +#define ATOM_VRAM_BLOCK_NEEDS_NO_RESERVATION 0x1 +#define ATOM_VRAM_BLOCK_NEEDS_RESERVATION 0x0 + +/***********************************************************************************/ +// Structure used in VRAM_UsageByFirmwareTable +// Note1: This table is filled by SetBiosReservationStartInFB in CoreCommSubs.asm +// at running time. +// note2: From RV770, the memory is more than 32bit addressable, so we will change +// ucTableFormatRevision=1,ucTableContentRevision=4, the strcuture remains +// exactly same as 1.1 and 1.2 (1.3 is never in use), but ulStartAddrUsedByFirmware +// (in offset to start of memory address) is KB aligned instead of byte aligend. +/***********************************************************************************/ +#define ATOM_MAX_FIRMWARE_VRAM_USAGE_INFO 1 + +typedef struct _ATOM_FIRMWARE_VRAM_RESERVE_INFO +{ + ULONG ulStartAddrUsedByFirmware; + USHORT usFirmwareUseInKb; + USHORT usReserved; +}ATOM_FIRMWARE_VRAM_RESERVE_INFO; + +typedef struct _ATOM_VRAM_USAGE_BY_FIRMWARE +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_FIRMWARE_VRAM_RESERVE_INFO asFirmwareVramReserveInfo[ATOM_MAX_FIRMWARE_VRAM_USAGE_INFO]; +}ATOM_VRAM_USAGE_BY_FIRMWARE; + +/****************************************************************************/ +// Structure used in GPIO_Pin_LUTTable +/****************************************************************************/ +typedef struct _ATOM_GPIO_PIN_ASSIGNMENT +{ + USHORT usGpioPin_AIndex; + UCHAR ucGpioPinBitShift; + UCHAR ucGPIO_ID; +}ATOM_GPIO_PIN_ASSIGNMENT; + +typedef struct _ATOM_GPIO_PIN_LUT +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_GPIO_PIN_ASSIGNMENT asGPIO_Pin[1]; +}ATOM_GPIO_PIN_LUT; + +/****************************************************************************/ +// Structure used in ComponentVideoInfoTable +/****************************************************************************/ +#define GPIO_PIN_ACTIVE_HIGH 0x1 + +#define MAX_SUPPORTED_CV_STANDARDS 5 + +// definitions for ATOM_D_INFO.ucSettings +#define ATOM_GPIO_SETTINGS_BITSHIFT_MASK 0x1F // [4:0] +#define ATOM_GPIO_SETTINGS_RESERVED_MASK 0x60 // [6:5] = must be zeroed out +#define ATOM_GPIO_SETTINGS_ACTIVE_MASK 0x80 // [7] + +typedef struct _ATOM_GPIO_INFO +{ + USHORT usAOffset; + UCHAR ucSettings; + UCHAR ucReserved; +}ATOM_GPIO_INFO; + +// definitions for ATOM_COMPONENT_VIDEO_INFO.ucMiscInfo (bit vector) +#define ATOM_CV_RESTRICT_FORMAT_SELECTION 0x2 + +// definitions for ATOM_COMPONENT_VIDEO_INFO.uc480i/uc480p/uc720p/uc1080i +#define ATOM_GPIO_DEFAULT_MODE_EN 0x80 //[7]; +#define ATOM_GPIO_SETTING_PERMODE_MASK 0x7F //[6:0] + +// definitions for ATOM_COMPONENT_VIDEO_INFO.ucLetterBoxMode +//Line 3 out put 5V. +#define ATOM_CV_LINE3_ASPECTRATIO_16_9_GPIO_A 0x01 //represent gpio 3 state for 16:9 +#define ATOM_CV_LINE3_ASPECTRATIO_16_9_GPIO_B 0x02 //represent gpio 4 state for 16:9 +#define ATOM_CV_LINE3_ASPECTRATIO_16_9_GPIO_SHIFT 0x0 + +//Line 3 out put 2.2V +#define ATOM_CV_LINE3_ASPECTRATIO_4_3_LETBOX_GPIO_A 0x04 //represent gpio 3 state for 4:3 Letter box +#define ATOM_CV_LINE3_ASPECTRATIO_4_3_LETBOX_GPIO_B 0x08 //represent gpio 4 state for 4:3 Letter box +#define ATOM_CV_LINE3_ASPECTRATIO_4_3_LETBOX_GPIO_SHIFT 0x2 + +//Line 3 out put 0V +#define ATOM_CV_LINE3_ASPECTRATIO_4_3_GPIO_A 0x10 //represent gpio 3 state for 4:3 +#define ATOM_CV_LINE3_ASPECTRATIO_4_3_GPIO_B 0x20 //represent gpio 4 state for 4:3 +#define ATOM_CV_LINE3_ASPECTRATIO_4_3_GPIO_SHIFT 0x4 + +#define ATOM_CV_LINE3_ASPECTRATIO_MASK 0x3F // bit [5:0] + +#define ATOM_CV_LINE3_ASPECTRATIO_EXIST 0x80 //bit 7 + +//GPIO bit index in gpio setting per mode value, also represend the block no. in gpio blocks. +#define ATOM_GPIO_INDEX_LINE3_ASPECRATIO_GPIO_A 3 //bit 3 in uc480i/uc480p/uc720p/uc1080i, which represend the default gpio bit setting for the mode. +#define ATOM_GPIO_INDEX_LINE3_ASPECRATIO_GPIO_B 4 //bit 4 in uc480i/uc480p/uc720p/uc1080i, which represend the default gpio bit setting for the mode. + + +typedef struct _ATOM_COMPONENT_VIDEO_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usMask_PinRegisterIndex; + USHORT usEN_PinRegisterIndex; + USHORT usY_PinRegisterIndex; + USHORT usA_PinRegisterIndex; + UCHAR ucBitShift; + UCHAR ucPinActiveState; //ucPinActiveState: Bit0=1 active high, =0 active low + ATOM_DTD_FORMAT sReserved; // must be zeroed out + UCHAR ucMiscInfo; + UCHAR uc480i; + UCHAR uc480p; + UCHAR uc720p; + UCHAR uc1080i; + UCHAR ucLetterBoxMode; + UCHAR ucReserved[3]; + UCHAR ucNumOfWbGpioBlocks; //For Component video D-Connector support. If zere, NTSC type connector + ATOM_GPIO_INFO aWbGpioStateBlock[MAX_SUPPORTED_CV_STANDARDS]; + ATOM_DTD_FORMAT aModeTimings[MAX_SUPPORTED_CV_STANDARDS]; +}ATOM_COMPONENT_VIDEO_INFO; + +//ucTableFormatRevision=2 +//ucTableContentRevision=1 +typedef struct _ATOM_COMPONENT_VIDEO_INFO_V21 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + UCHAR ucMiscInfo; + UCHAR uc480i; + UCHAR uc480p; + UCHAR uc720p; + UCHAR uc1080i; + UCHAR ucReserved; + UCHAR ucLetterBoxMode; + UCHAR ucNumOfWbGpioBlocks; //For Component video D-Connector support. If zere, NTSC type connector + ATOM_GPIO_INFO aWbGpioStateBlock[MAX_SUPPORTED_CV_STANDARDS]; + ATOM_DTD_FORMAT aModeTimings[MAX_SUPPORTED_CV_STANDARDS]; +}ATOM_COMPONENT_VIDEO_INFO_V21; + +#define ATOM_COMPONENT_VIDEO_INFO_LAST ATOM_COMPONENT_VIDEO_INFO_V21 + +/****************************************************************************/ +// Structure used in object_InfoTable +/****************************************************************************/ +typedef struct _ATOM_OBJECT_HEADER +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usDeviceSupport; + USHORT usConnectorObjectTableOffset; + USHORT usRouterObjectTableOffset; + USHORT usEncoderObjectTableOffset; + USHORT usProtectionObjectTableOffset; //only available when Protection block is independent. + USHORT usDisplayPathTableOffset; +}ATOM_OBJECT_HEADER; + + +typedef struct _ATOM_DISPLAY_OBJECT_PATH +{ + USHORT usDeviceTag; //supported device + USHORT usSize; //the size of ATOM_DISPLAY_OBJECT_PATH + USHORT usConnObjectId; //Connector Object ID + USHORT usGPUObjectId; //GPU ID + USHORT usGraphicObjIds[1]; //1st Encoder Obj source from GPU to last Graphic Obj destinate to connector. +}ATOM_DISPLAY_OBJECT_PATH; + +typedef struct _ATOM_DISPLAY_OBJECT_PATH_TABLE +{ + UCHAR ucNumOfDispPath; + UCHAR ucVersion; + UCHAR ucPadding[2]; + ATOM_DISPLAY_OBJECT_PATH asDispPath[1]; +}ATOM_DISPLAY_OBJECT_PATH_TABLE; + + +typedef struct _ATOM_OBJECT //each object has this structure +{ + USHORT usObjectID; + USHORT usSrcDstTableOffset; + USHORT usRecordOffset; //this pointing to a bunch of records defined below + USHORT usReserved; +}ATOM_OBJECT; + +typedef struct _ATOM_OBJECT_TABLE //Above 4 object table offset pointing to a bunch of objects all have this structure +{ + UCHAR ucNumberOfObjects; + UCHAR ucPadding[3]; + ATOM_OBJECT asObjects[1]; +}ATOM_OBJECT_TABLE; + +typedef struct _ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT //usSrcDstTableOffset pointing to this structure +{ + UCHAR ucNumberOfSrc; + USHORT usSrcObjectID[1]; + UCHAR ucNumberOfDst; + USHORT usDstObjectID[1]; +}ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT; + + +//Related definitions, all records are differnt but they have a commond header +typedef struct _ATOM_COMMON_RECORD_HEADER +{ + UCHAR ucRecordType; //An emun to indicate the record type + UCHAR ucRecordSize; //The size of the whole record in byte +}ATOM_COMMON_RECORD_HEADER; + + +#define ATOM_I2C_RECORD_TYPE 1 +#define ATOM_HPD_INT_RECORD_TYPE 2 +#define ATOM_OUTPUT_PROTECTION_RECORD_TYPE 3 +#define ATOM_CONNECTOR_DEVICE_TAG_RECORD_TYPE 4 +#define ATOM_CONNECTOR_DVI_EXT_INPUT_RECORD_TYPE 5 //Obsolete, switch to use GPIO_CNTL_RECORD_TYPE +#define ATOM_ENCODER_FPGA_CONTROL_RECORD_TYPE 6 //Obsolete, switch to use GPIO_CNTL_RECORD_TYPE +#define ATOM_CONNECTOR_CVTV_SHARE_DIN_RECORD_TYPE 7 +#define ATOM_JTAG_RECORD_TYPE 8 //Obsolete, switch to use GPIO_CNTL_RECORD_TYPE +#define ATOM_OBJECT_GPIO_CNTL_RECORD_TYPE 9 +#define ATOM_ENCODER_DVO_CF_RECORD_TYPE 10 +#define ATOM_CONNECTOR_CF_RECORD_TYPE 11 +#define ATOM_CONNECTOR_HARDCODE_DTD_RECORD_TYPE 12 +#define ATOM_CONNECTOR_PCIE_SUBCONNECTOR_RECORD_TYPE 13 +#define ATOM_ROUTER_DDC_PATH_SELECT_RECORD_TYPE 14 +#define ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD_TYPE 15 + +//Must be updated when new record type is added,equal to that record definition! +#define ATOM_MAX_OBJECT_RECORD_NUMBER ATOM_CONNECTOR_CF_RECORD_TYPE + +typedef struct _ATOM_I2C_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + ATOM_I2C_ID_CONFIG sucI2cId; + UCHAR ucI2CAddr; //The slave address, it's 0 when the record is attached to connector for DDC +}ATOM_I2C_RECORD; + +typedef struct _ATOM_HPD_INT_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + UCHAR ucHPDIntGPIOID; //Corresponding block in GPIO_PIN_INFO table gives the pin info + UCHAR ucPluggged_PinState; +}ATOM_HPD_INT_RECORD; + + +typedef struct _ATOM_OUTPUT_PROTECTION_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + UCHAR ucProtectionFlag; + UCHAR ucReserved; +}ATOM_OUTPUT_PROTECTION_RECORD; + +typedef struct _ATOM_CONNECTOR_DEVICE_TAG +{ + ULONG ulACPIDeviceEnum; //Reserved for now + USHORT usDeviceID; //This Id is same as "ATOM_DEVICE_XXX_SUPPORT" + USHORT usPadding; +}ATOM_CONNECTOR_DEVICE_TAG; + +typedef struct _ATOM_CONNECTOR_DEVICE_TAG_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + UCHAR ucNumberOfDevice; + UCHAR ucReserved; + ATOM_CONNECTOR_DEVICE_TAG asDeviceTag[1]; //This Id is same as "ATOM_DEVICE_XXX_SUPPORT", 1 is only for allocation +}ATOM_CONNECTOR_DEVICE_TAG_RECORD; + + +typedef struct _ATOM_CONNECTOR_DVI_EXT_INPUT_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + UCHAR ucConfigGPIOID; + UCHAR ucConfigGPIOState; //Set to 1 when it's active high to enable external flow in + UCHAR ucFlowinGPIPID; + UCHAR ucExtInGPIPID; +}ATOM_CONNECTOR_DVI_EXT_INPUT_RECORD; + +typedef struct _ATOM_ENCODER_FPGA_CONTROL_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + UCHAR ucCTL1GPIO_ID; + UCHAR ucCTL1GPIOState; //Set to 1 when it's active high + UCHAR ucCTL2GPIO_ID; + UCHAR ucCTL2GPIOState; //Set to 1 when it's active high + UCHAR ucCTL3GPIO_ID; + UCHAR ucCTL3GPIOState; //Set to 1 when it's active high + UCHAR ucCTLFPGA_IN_ID; + UCHAR ucPadding[3]; +}ATOM_ENCODER_FPGA_CONTROL_RECORD; + +typedef struct _ATOM_CONNECTOR_CVTV_SHARE_DIN_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + UCHAR ucGPIOID; //Corresponding block in GPIO_PIN_INFO table gives the pin info + UCHAR ucTVActiveState; //Indicating when the pin==0 or 1 when TV is connected +}ATOM_CONNECTOR_CVTV_SHARE_DIN_RECORD; + +typedef struct _ATOM_JTAG_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + UCHAR ucTMSGPIO_ID; + UCHAR ucTMSGPIOState; //Set to 1 when it's active high + UCHAR ucTCKGPIO_ID; + UCHAR ucTCKGPIOState; //Set to 1 when it's active high + UCHAR ucTDOGPIO_ID; + UCHAR ucTDOGPIOState; //Set to 1 when it's active high + UCHAR ucTDIGPIO_ID; + UCHAR ucTDIGPIOState; //Set to 1 when it's active high + UCHAR ucPadding[2]; +}ATOM_JTAG_RECORD; + + +//The following generic object gpio pin control record type will replace JTAG_RECORD/FPGA_CONTROL_RECORD/DVI_EXT_INPUT_RECORD above gradually +typedef struct _ATOM_GPIO_PIN_CONTROL_PAIR +{ + UCHAR ucGPIOID; // GPIO_ID, find the corresponding ID in GPIO_LUT table + UCHAR ucGPIO_PinState; // Pin state showing how to set-up the pin +}ATOM_GPIO_PIN_CONTROL_PAIR; + +typedef struct _ATOM_OBJECT_GPIO_CNTL_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + UCHAR ucFlags; // Future expnadibility + UCHAR ucNumberOfPins; // Number of GPIO pins used to control the object + ATOM_GPIO_PIN_CONTROL_PAIR asGpio[1]; // the real gpio pin pair determined by number of pins ucNumberOfPins +}ATOM_OBJECT_GPIO_CNTL_RECORD; + +//Definitions for GPIO pin state +#define GPIO_PIN_TYPE_INPUT 0x00 +#define GPIO_PIN_TYPE_OUTPUT 0x10 +#define GPIO_PIN_TYPE_HW_CONTROL 0x20 + +//For GPIO_PIN_TYPE_OUTPUT the following is defined +#define GPIO_PIN_OUTPUT_STATE_MASK 0x01 +#define GPIO_PIN_OUTPUT_STATE_SHIFT 0 +#define GPIO_PIN_STATE_ACTIVE_LOW 0x0 +#define GPIO_PIN_STATE_ACTIVE_HIGH 0x1 + +typedef struct _ATOM_ENCODER_DVO_CF_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + ULONG ulStrengthControl; // DVOA strength control for CF + UCHAR ucPadding[2]; +}ATOM_ENCODER_DVO_CF_RECORD; + +// value for ATOM_CONNECTOR_CF_RECORD.ucConnectedDvoBundle +#define ATOM_CONNECTOR_CF_RECORD_CONNECTED_UPPER12BITBUNDLEA 1 +#define ATOM_CONNECTOR_CF_RECORD_CONNECTED_LOWER12BITBUNDLEB 2 + +typedef struct _ATOM_CONNECTOR_CF_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + USHORT usMaxPixClk; + UCHAR ucFlowCntlGpioId; + UCHAR ucSwapCntlGpioId; + UCHAR ucConnectedDvoBundle; + UCHAR ucPadding; +}ATOM_CONNECTOR_CF_RECORD; + +typedef struct _ATOM_CONNECTOR_HARDCODE_DTD_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + ATOM_DTD_FORMAT asTiming; +}ATOM_CONNECTOR_HARDCODE_DTD_RECORD; + +typedef struct _ATOM_CONNECTOR_PCIE_SUBCONNECTOR_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; //ATOM_CONNECTOR_PCIE_SUBCONNECTOR_RECORD_TYPE + UCHAR ucSubConnectorType; //CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D|X_ID_DUAL_LINK_DVI_D|HDMI_TYPE_A + UCHAR ucReserved; +}ATOM_CONNECTOR_PCIE_SUBCONNECTOR_RECORD; + + +typedef struct _ATOM_ROUTER_DDC_PATH_SELECT_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + UCHAR ucMuxType; //decide the number of ucMuxState, =0, no pin state, =1: single state with complement, >1: multiple state + UCHAR ucMuxControlPin; + UCHAR ucMuxState[2]; //for alligment purpose +}ATOM_ROUTER_DDC_PATH_SELECT_RECORD; + +typedef struct _ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + UCHAR ucMuxType; + UCHAR ucMuxControlPin; + UCHAR ucMuxState[2]; //for alligment purpose +}ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD; + +// define ucMuxType +#define ATOM_ROUTER_MUX_PIN_STATE_MASK 0x0f +#define ATOM_ROUTER_MUX_PIN_SINGLE_STATE_COMPLEMENT 0x01 + +/****************************************************************************/ +// ASIC voltage data table +/****************************************************************************/ +typedef struct _ATOM_VOLTAGE_INFO_HEADER +{ + USHORT usVDDCBaseLevel; //In number of 50mv unit + USHORT usReserved; //For possible extension table offset + UCHAR ucNumOfVoltageEntries; + UCHAR ucBytesPerVoltageEntry; + UCHAR ucVoltageStep; //Indicating in how many mv increament is one step, 0.5mv unit + UCHAR ucDefaultVoltageEntry; + UCHAR ucVoltageControlI2cLine; + UCHAR ucVoltageControlAddress; + UCHAR ucVoltageControlOffset; +}ATOM_VOLTAGE_INFO_HEADER; + +typedef struct _ATOM_VOLTAGE_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_VOLTAGE_INFO_HEADER viHeader; + UCHAR ucVoltageEntries[64]; //64 is for allocation, the actual number of entry is present at ucNumOfVoltageEntries*ucBytesPerVoltageEntry +}ATOM_VOLTAGE_INFO; + + +typedef struct _ATOM_VOLTAGE_FORMULA +{ + USHORT usVoltageBaseLevel; // In number of 1mv unit + USHORT usVoltageStep; // Indicating in how many mv increament is one step, 1mv unit + UCHAR ucNumOfVoltageEntries; // Number of Voltage Entry, which indicate max Voltage + UCHAR ucFlag; // bit0=0 :step is 1mv =1 0.5mv + UCHAR ucBaseVID; // if there is no lookup table, VID= BaseVID + ( Vol - BaseLevle ) /VoltageStep + UCHAR ucReserved; + UCHAR ucVIDAdjustEntries[32]; // 32 is for allocation, the actual number of entry is present at ucNumOfVoltageEntries +}ATOM_VOLTAGE_FORMULA; + +typedef struct _ATOM_VOLTAGE_CONTROL +{ + UCHAR ucVoltageControlId; //Indicate it is controlled by I2C or GPIO or HW state machine + UCHAR ucVoltageControlI2cLine; + UCHAR ucVoltageControlAddress; + UCHAR ucVoltageControlOffset; + USHORT usGpioPin_AIndex; //GPIO_PAD register index + UCHAR ucGpioPinBitShift[9]; //at most 8 pin support 255 VIDs, termintate with 0xff + UCHAR ucReserved; +}ATOM_VOLTAGE_CONTROL; + +// Define ucVoltageControlId +#define VOLTAGE_CONTROLLED_BY_HW 0x00 +#define VOLTAGE_CONTROLLED_BY_I2C_MASK 0x7F +#define VOLTAGE_CONTROLLED_BY_GPIO 0x80 +#define VOLTAGE_CONTROL_ID_LM64 0x01 //I2C control, used for R5xx Core Voltage +#define VOLTAGE_CONTROL_ID_DAC 0x02 //I2C control, used for R5xx/R6xx MVDDC,MVDDQ or VDDCI +#define VOLTAGE_CONTROL_ID_VT116xM 0x03 //I2C control, used for R6xx Core Voltage +#define VOLTAGE_CONTROL_ID_DS4402 0x04 + +typedef struct _ATOM_VOLTAGE_OBJECT +{ + UCHAR ucVoltageType; //Indicate Voltage Source: VDDC, MVDDC, MVDDQ or MVDDCI + UCHAR ucSize; //Size of Object + ATOM_VOLTAGE_CONTROL asControl; //describ how to control + ATOM_VOLTAGE_FORMULA asFormula; //Indicate How to convert real Voltage to VID +}ATOM_VOLTAGE_OBJECT; + +typedef struct _ATOM_VOLTAGE_OBJECT_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_VOLTAGE_OBJECT asVoltageObj[3]; //Info for Voltage control +}ATOM_VOLTAGE_OBJECT_INFO; + +typedef struct _ATOM_LEAKID_VOLTAGE +{ + UCHAR ucLeakageId; + UCHAR ucReserved; + USHORT usVoltage; +}ATOM_LEAKID_VOLTAGE; + +typedef struct _ATOM_ASIC_PROFILE_VOLTAGE +{ + UCHAR ucProfileId; + UCHAR ucReserved; + USHORT usSize; + USHORT usEfuseSpareStartAddr; + USHORT usFuseIndex[8]; //from LSB to MSB, Max 8bit,end of 0xffff if less than 8 efuse id, + ATOM_LEAKID_VOLTAGE asLeakVol[2]; //Leakid and relatd voltage +}ATOM_ASIC_PROFILE_VOLTAGE; + +//ucProfileId +#define ATOM_ASIC_PROFILE_ID_EFUSE_VOLTAGE 1 +#define ATOM_ASIC_PROFILE_ID_EFUSE_PERFORMANCE_VOLTAGE 1 +#define ATOM_ASIC_PROFILE_ID_EFUSE_THERMAL_VOLTAGE 2 + +typedef struct _ATOM_ASIC_PROFILING_INFO +{ + ATOM_COMMON_TABLE_HEADER asHeader; + ATOM_ASIC_PROFILE_VOLTAGE asVoltage; +}ATOM_ASIC_PROFILING_INFO; + +typedef struct _ATOM_POWER_SOURCE_OBJECT +{ + UCHAR ucPwrSrcId; // Power source + UCHAR ucPwrSensorType; // GPIO, I2C or none + UCHAR ucPwrSensId; // if GPIO detect, it is GPIO id, if I2C detect, it is I2C id + UCHAR ucPwrSensSlaveAddr; // Slave address if I2C detect + UCHAR ucPwrSensRegIndex; // I2C register Index if I2C detect + UCHAR ucPwrSensRegBitMask; // detect which bit is used if I2C detect + UCHAR ucPwrSensActiveState; // high active or low active + UCHAR ucReserve[3]; // reserve + USHORT usSensPwr; // in unit of watt +}ATOM_POWER_SOURCE_OBJECT; + +typedef struct _ATOM_POWER_SOURCE_INFO +{ + ATOM_COMMON_TABLE_HEADER asHeader; + UCHAR asPwrbehave[16]; + ATOM_POWER_SOURCE_OBJECT asPwrObj[1]; +}ATOM_POWER_SOURCE_INFO; + + +//Define ucPwrSrcId +#define POWERSOURCE_PCIE_ID1 0x00 +#define POWERSOURCE_6PIN_CONNECTOR_ID1 0x01 +#define POWERSOURCE_8PIN_CONNECTOR_ID1 0x02 +#define POWERSOURCE_6PIN_CONNECTOR_ID2 0x04 +#define POWERSOURCE_8PIN_CONNECTOR_ID2 0x08 + +//define ucPwrSensorId +#define POWER_SENSOR_ALWAYS 0x00 +#define POWER_SENSOR_GPIO 0x01 +#define POWER_SENSOR_I2C 0x02 + +/**************************************************************************/ +// This portion is only used when ext thermal chip or engine/memory clock SS chip is populated on a design +//Memory SS Info Table +//Define Memory Clock SS chip ID +#define ICS91719 1 +#define ICS91720 2 + +//Define one structure to inform SW a "block of data" writing to external SS chip via I2C protocol +typedef struct _ATOM_I2C_DATA_RECORD +{ + UCHAR ucNunberOfBytes; //Indicates how many bytes SW needs to write to the external ASIC for one block, besides to "Start" and "Stop" + UCHAR ucI2CData[1]; //I2C data in bytes, should be less than 16 bytes usually +}ATOM_I2C_DATA_RECORD; + + +//Define one structure to inform SW how many blocks of data writing to external SS chip via I2C protocol, in addition to other information +typedef struct _ATOM_I2C_DEVICE_SETUP_INFO +{ + ATOM_I2C_ID_CONFIG_ACCESS sucI2cId; //I2C line and HW/SW assisted cap. + UCHAR ucSSChipID; //SS chip being used + UCHAR ucSSChipSlaveAddr; //Slave Address to set up this SS chip + UCHAR ucNumOfI2CDataRecords; //number of data block + ATOM_I2C_DATA_RECORD asI2CData[1]; +}ATOM_I2C_DEVICE_SETUP_INFO; + +//========================================================================================== +typedef struct _ATOM_ASIC_MVDD_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_I2C_DEVICE_SETUP_INFO asI2CSetup[1]; +}ATOM_ASIC_MVDD_INFO; + +//========================================================================================== +#define ATOM_MCLK_SS_INFO ATOM_ASIC_MVDD_INFO + +//========================================================================================== +/**************************************************************************/ + +typedef struct _ATOM_ASIC_SS_ASSIGNMENT +{ + ULONG ulTargetClockRange; //Clock Out frequence (VCO ), in unit of 10Khz + USHORT usSpreadSpectrumPercentage; //in unit of 0.01% + USHORT usSpreadRateInKhz; //in unit of kHz, modulation freq + UCHAR ucClockIndication; //Indicate which clock source needs SS + UCHAR ucSpreadSpectrumMode; //Bit1=0 Down Spread,=1 Center Spread. + UCHAR ucReserved[2]; +}ATOM_ASIC_SS_ASSIGNMENT; + +//Define ucSpreadSpectrumType +#define ASIC_INTERNAL_MEMORY_SS 1 +#define ASIC_INTERNAL_ENGINE_SS 2 +#define ASIC_INTERNAL_UVD_SS 3 + +typedef struct _ATOM_ASIC_INTERNAL_SS_INFO{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_ASIC_SS_ASSIGNMENT asSpreadSpectrum[4]; +}ATOM_ASIC_INTERNAL_SS_INFO; + +//==============================Scratch Pad Definition Portion=============================== +#define ATOM_DEVICE_CONNECT_INFO_DEF 0 +#define ATOM_ROM_LOCATION_DEF 1 +#define ATOM_TV_STANDARD_DEF 2 +#define ATOM_ACTIVE_INFO_DEF 3 +#define ATOM_LCD_INFO_DEF 4 +#define ATOM_DOS_REQ_INFO_DEF 5 +#define ATOM_ACC_CHANGE_INFO_DEF 6 +#define ATOM_DOS_MODE_INFO_DEF 7 +#define ATOM_I2C_CHANNEL_STATUS_DEF 8 +#define ATOM_I2C_CHANNEL_STATUS1_DEF 9 + + +// BIOS_0_SCRATCH Definition +#define ATOM_S0_CRT1_MONO 0x00000001L +#define ATOM_S0_CRT1_COLOR 0x00000002L +#define ATOM_S0_CRT1_MASK (ATOM_S0_CRT1_MONO+ATOM_S0_CRT1_COLOR) + +#define ATOM_S0_TV1_COMPOSITE_A 0x00000004L +#define ATOM_S0_TV1_SVIDEO_A 0x00000008L +#define ATOM_S0_TV1_MASK_A (ATOM_S0_TV1_COMPOSITE_A+ATOM_S0_TV1_SVIDEO_A) + +#define ATOM_S0_CV_A 0x00000010L +#define ATOM_S0_CV_DIN_A 0x00000020L +#define ATOM_S0_CV_MASK_A (ATOM_S0_CV_A+ATOM_S0_CV_DIN_A) + + +#define ATOM_S0_CRT2_MONO 0x00000100L +#define ATOM_S0_CRT2_COLOR 0x00000200L +#define ATOM_S0_CRT2_MASK (ATOM_S0_CRT2_MONO+ATOM_S0_CRT2_COLOR) + +#define ATOM_S0_TV1_COMPOSITE 0x00000400L +#define ATOM_S0_TV1_SVIDEO 0x00000800L +#define ATOM_S0_TV1_SCART 0x00004000L +#define ATOM_S0_TV1_MASK (ATOM_S0_TV1_COMPOSITE+ATOM_S0_TV1_SVIDEO+ATOM_S0_TV1_SCART) + +#define ATOM_S0_CV 0x00001000L +#define ATOM_S0_CV_DIN 0x00002000L +#define ATOM_S0_CV_MASK (ATOM_S0_CV+ATOM_S0_CV_DIN) + +#define ATOM_S0_DFP1 0x00010000L +#define ATOM_S0_DFP2 0x00020000L +#define ATOM_S0_LCD1 0x00040000L +#define ATOM_S0_LCD2 0x00080000L +#define ATOM_S0_TV2 0x00100000L +#define ATOM_S0_DFP3 0x00200000L +#define ATOM_S0_DFP4 0x00400000L +#define ATOM_S0_DFP5 0x00800000L + +#define ATOM_S0_DFP_MASK ATOM_S0_DFP1 | ATOM_S0_DFP2 | ATOM_S0_DFP3 | ATOM_S0_DFP4 | ATOM_S0_DFP5 + +#define ATOM_S0_FAD_REGISTER_BUG 0x02000000L // If set, indicates we are running a PCIE asic with + // the FAD/HDP reg access bug. Bit is read by DAL + +#define ATOM_S0_THERMAL_STATE_MASK 0x1C000000L +#define ATOM_S0_THERMAL_STATE_SHIFT 26 + +#define ATOM_S0_SYSTEM_POWER_STATE_MASK 0xE0000000L +#define ATOM_S0_SYSTEM_POWER_STATE_SHIFT 29 + +#define ATOM_S0_SYSTEM_POWER_STATE_VALUE_AC 1 +#define ATOM_S0_SYSTEM_POWER_STATE_VALUE_DC 2 +#define ATOM_S0_SYSTEM_POWER_STATE_VALUE_LITEAC 3 + +//Byte aligned defintion for BIOS usage +#define ATOM_S0_CRT1_MONOb0 0x01 +#define ATOM_S0_CRT1_COLORb0 0x02 +#define ATOM_S0_CRT1_MASKb0 (ATOM_S0_CRT1_MONOb0+ATOM_S0_CRT1_COLORb0) + +#define ATOM_S0_TV1_COMPOSITEb0 0x04 +#define ATOM_S0_TV1_SVIDEOb0 0x08 +#define ATOM_S0_TV1_MASKb0 (ATOM_S0_TV1_COMPOSITEb0+ATOM_S0_TV1_SVIDEOb0) + +#define ATOM_S0_CVb0 0x10 +#define ATOM_S0_CV_DINb0 0x20 +#define ATOM_S0_CV_MASKb0 (ATOM_S0_CVb0+ATOM_S0_CV_DINb0) + +#define ATOM_S0_CRT2_MONOb1 0x01 +#define ATOM_S0_CRT2_COLORb1 0x02 +#define ATOM_S0_CRT2_MASKb1 (ATOM_S0_CRT2_MONOb1+ATOM_S0_CRT2_COLORb1) + +#define ATOM_S0_TV1_COMPOSITEb1 0x04 +#define ATOM_S0_TV1_SVIDEOb1 0x08 +#define ATOM_S0_TV1_SCARTb1 0x40 +#define ATOM_S0_TV1_MASKb1 (ATOM_S0_TV1_COMPOSITEb1+ATOM_S0_TV1_SVIDEOb1+ATOM_S0_TV1_SCARTb1) + +#define ATOM_S0_CVb1 0x10 +#define ATOM_S0_CV_DINb1 0x20 +#define ATOM_S0_CV_MASKb1 (ATOM_S0_CVb1+ATOM_S0_CV_DINb1) + +#define ATOM_S0_DFP1b2 0x01 +#define ATOM_S0_DFP2b2 0x02 +#define ATOM_S0_LCD1b2 0x04 +#define ATOM_S0_LCD2b2 0x08 +#define ATOM_S0_TV2b2 0x10 +#define ATOM_S0_DFP3b2 0x20 + +#define ATOM_S0_THERMAL_STATE_MASKb3 0x1C +#define ATOM_S0_THERMAL_STATE_SHIFTb3 2 + +#define ATOM_S0_SYSTEM_POWER_STATE_MASKb3 0xE0 +#define ATOM_S0_LCD1_SHIFT 18 + +// BIOS_1_SCRATCH Definition +#define ATOM_S1_ROM_LOCATION_MASK 0x0000FFFFL +#define ATOM_S1_PCI_BUS_DEV_MASK 0xFFFF0000L + +// BIOS_2_SCRATCH Definition +#define ATOM_S2_TV1_STANDARD_MASK 0x0000000FL +#define ATOM_S2_CURRENT_BL_LEVEL_MASK 0x0000FF00L +#define ATOM_S2_CURRENT_BL_LEVEL_SHIFT 8 + +#define ATOM_S2_CRT1_DPMS_STATE 0x00010000L +#define ATOM_S2_LCD1_DPMS_STATE 0x00020000L +#define ATOM_S2_TV1_DPMS_STATE 0x00040000L +#define ATOM_S2_DFP1_DPMS_STATE 0x00080000L +#define ATOM_S2_CRT2_DPMS_STATE 0x00100000L +#define ATOM_S2_LCD2_DPMS_STATE 0x00200000L +#define ATOM_S2_TV2_DPMS_STATE 0x00400000L +#define ATOM_S2_DFP2_DPMS_STATE 0x00800000L +#define ATOM_S2_CV_DPMS_STATE 0x01000000L +#define ATOM_S2_DFP3_DPMS_STATE 0x02000000L +#define ATOM_S2_DFP4_DPMS_STATE 0x04000000L +#define ATOM_S2_DFP5_DPMS_STATE 0x08000000L + +#define ATOM_S2_DFP_DPM_STATE ATOM_S2_DFP1_DPMS_STATE | ATOM_S2_DFP2_DPMS_STATE | ATOM_S2_DFP3_DPMS_STATE | ATOM_S2_DFP4_DPMS_STATE | ATOM_S2_DFP5_DPMS_STATE + +#define ATOM_S2_DEVICE_DPMS_STATE (ATOM_S2_CRT1_DPMS_STATE+ATOM_S2_LCD1_DPMS_STATE+ATOM_S2_TV1_DPMS_STATE+\ + ATOM_S2_DFP_DPMS_STATE+ATOM_S2_CRT2_DPMS_STATE+ATOM_S2_LCD2_DPMS_STATE+\ + ATOM_S2_TV2_DPMS_STATE+ATOM_S2_CV_DPMS_STATE + +#define ATOM_S2_FORCEDLOWPWRMODE_STATE_MASK 0x0C000000L +#define ATOM_S2_FORCEDLOWPWRMODE_STATE_MASK_SHIFT 26 +#define ATOM_S2_FORCEDLOWPWRMODE_STATE_CHANGE 0x10000000L + +#define ATOM_S2_VRI_BRIGHT_ENABLE 0x20000000L + +#define ATOM_S2_DISPLAY_ROTATION_0_DEGREE 0x0 +#define ATOM_S2_DISPLAY_ROTATION_90_DEGREE 0x1 +#define ATOM_S2_DISPLAY_ROTATION_180_DEGREE 0x2 +#define ATOM_S2_DISPLAY_ROTATION_270_DEGREE 0x3 +#define ATOM_S2_DISPLAY_ROTATION_DEGREE_SHIFT 30 +#define ATOM_S2_DISPLAY_ROTATION_ANGLE_MASK 0xC0000000L + + +//Byte aligned defintion for BIOS usage +#define ATOM_S2_TV1_STANDARD_MASKb0 0x0F +#define ATOM_S2_CURRENT_BL_LEVEL_MASKb1 0xFF +#define ATOM_S2_CRT1_DPMS_STATEb2 0x01 +#define ATOM_S2_LCD1_DPMS_STATEb2 0x02 +#define ATOM_S2_TV1_DPMS_STATEb2 0x04 +#define ATOM_S2_DFP1_DPMS_STATEb2 0x08 +#define ATOM_S2_CRT2_DPMS_STATEb2 0x10 +#define ATOM_S2_LCD2_DPMS_STATEb2 0x20 +#define ATOM_S2_TV2_DPMS_STATEb2 0x40 +#define ATOM_S2_DFP2_DPMS_STATEb2 0x80 +#define ATOM_S2_CV_DPMS_STATEb3 0x01 +#define ATOM_S2_DFP3_DPMS_STATEb3 0x02 +#define ATOM_S2_DFP4_DPMS_STATEb3 0x04 +#define ATOM_S2_DFP5_DPMS_STATEb3 0x08 + +#define ATOM_S2_DEVICE_DPMS_MASKw1 0x3FF +#define ATOM_S2_FORCEDLOWPWRMODE_STATE_MASKb3 0x0C +#define ATOM_S2_FORCEDLOWPWRMODE_STATE_CHANGEb3 0x10 +#define ATOM_S2_VRI_BRIGHT_ENABLEb3 0x20 +#define ATOM_S2_ROTATION_STATE_MASKb3 0xC0 + + +// BIOS_3_SCRATCH Definition +#define ATOM_S3_CRT1_ACTIVE 0x00000001L +#define ATOM_S3_LCD1_ACTIVE 0x00000002L +#define ATOM_S3_TV1_ACTIVE 0x00000004L +#define ATOM_S3_DFP1_ACTIVE 0x00000008L +#define ATOM_S3_CRT2_ACTIVE 0x00000010L +#define ATOM_S3_LCD2_ACTIVE 0x00000020L +#define ATOM_S3_TV2_ACTIVE 0x00000040L +#define ATOM_S3_DFP2_ACTIVE 0x00000080L +#define ATOM_S3_CV_ACTIVE 0x00000100L +#define ATOM_S3_DFP3_ACTIVE 0x00000200L +#define ATOM_S3_DFP4_ACTIVE 0x00000400L +#define ATOM_S3_DFP5_ACTIVE 0x00000800L + +#define ATOM_S3_DEVICE_ACTIVE_MASK 0x000003FFL + +#define ATOM_S3_LCD_FULLEXPANSION_ACTIVE 0x00001000L +#define ATOM_S3_LCD_EXPANSION_ASPEC_RATIO_ACTIVE 0x00002000L + +#define ATOM_S3_CRT1_CRTC_ACTIVE 0x00010000L +#define ATOM_S3_LCD1_CRTC_ACTIVE 0x00020000L +#define ATOM_S3_TV1_CRTC_ACTIVE 0x00040000L +#define ATOM_S3_DFP1_CRTC_ACTIVE 0x00080000L +#define ATOM_S3_CRT2_CRTC_ACTIVE 0x00100000L +#define ATOM_S3_LCD2_CRTC_ACTIVE 0x00200000L +#define ATOM_S3_TV2_CRTC_ACTIVE 0x00400000L +#define ATOM_S3_DFP2_CRTC_ACTIVE 0x00800000L +#define ATOM_S3_CV_CRTC_ACTIVE 0x01000000L +#define ATOM_S3_DFP3_CRTC_ACTIVE 0x02000000L +#define ATOM_S3_DFP4_CRTC_ACTIVE 0x04000000L +#define ATOM_S3_DFP5_CRTC_ACTIVE 0x08000000L + +#define ATOM_S3_DEVICE_CRTC_ACTIVE_MASK 0x0FFF0000L +#define ATOM_S3_ASIC_GUI_ENGINE_HUNG 0x20000000L +#define ATOM_S3_ALLOW_FAST_PWR_SWITCH 0x40000000L +#define ATOM_S3_RQST_GPU_USE_MIN_PWR 0x80000000L + +//Byte aligned defintion for BIOS usage +#define ATOM_S3_CRT1_ACTIVEb0 0x01 +#define ATOM_S3_LCD1_ACTIVEb0 0x02 +#define ATOM_S3_TV1_ACTIVEb0 0x04 +#define ATOM_S3_DFP1_ACTIVEb0 0x08 +#define ATOM_S3_CRT2_ACTIVEb0 0x10 +#define ATOM_S3_LCD2_ACTIVEb0 0x20 +#define ATOM_S3_TV2_ACTIVEb0 0x40 +#define ATOM_S3_DFP2_ACTIVEb0 0x80 +#define ATOM_S3_CV_ACTIVEb1 0x01 +#define ATOM_S3_DFP3_ACTIVEb1 0x02 +#define ATOM_S3_DFP4_ACTIVEb1 0x04 +#define ATOM_S3_DFP5_ACTIVEb1 0x08 + +#define ATOM_S3_ACTIVE_CRTC1w0 0xFFF + +#define ATOM_S3_CRT1_CRTC_ACTIVEb2 0x01 +#define ATOM_S3_LCD1_CRTC_ACTIVEb2 0x02 +#define ATOM_S3_TV1_CRTC_ACTIVEb2 0x04 +#define ATOM_S3_DFP1_CRTC_ACTIVEb2 0x08 +#define ATOM_S3_CRT2_CRTC_ACTIVEb2 0x10 +#define ATOM_S3_LCD2_CRTC_ACTIVEb2 0x20 +#define ATOM_S3_TV2_CRTC_ACTIVEb2 0x40 +#define ATOM_S3_DFP2_CRTC_ACTIVEb2 0x80 +#define ATOM_S3_CV_CRTC_ACTIVEb3 0x01 +#define ATOM_S3_DFP3_CRTC_ACTIVEb3 0x02 +#define ATOM_S3_DFP4_CRTC_ACTIVEb3 0x04 +#define ATOM_S3_DFP5_CRTC_ACTIVEb3 0x08 + +#define ATOM_S3_ACTIVE_CRTC2w1 0xFFF + +#define ATOM_S3_ASIC_GUI_ENGINE_HUNGb3 0x20 +#define ATOM_S3_ALLOW_FAST_PWR_SWITCHb3 0x40 +#define ATOM_S3_RQST_GPU_USE_MIN_PWRb3 0x80 + +// BIOS_4_SCRATCH Definition +#define ATOM_S4_LCD1_PANEL_ID_MASK 0x000000FFL +#define ATOM_S4_LCD1_REFRESH_MASK 0x0000FF00L +#define ATOM_S4_LCD1_REFRESH_SHIFT 8 + +//Byte aligned defintion for BIOS usage +#define ATOM_S4_LCD1_PANEL_ID_MASKb0 0x0FF +#define ATOM_S4_LCD1_REFRESH_MASKb1 ATOM_S4_LCD1_PANEL_ID_MASKb0 +#define ATOM_S4_VRAM_INFO_MASKb2 ATOM_S4_LCD1_PANEL_ID_MASKb0 + +// BIOS_5_SCRATCH Definition, BIOS_5_SCRATCH is used by Firmware only !!!! +#define ATOM_S5_DOS_REQ_CRT1b0 0x01 +#define ATOM_S5_DOS_REQ_LCD1b0 0x02 +#define ATOM_S5_DOS_REQ_TV1b0 0x04 +#define ATOM_S5_DOS_REQ_DFP1b0 0x08 +#define ATOM_S5_DOS_REQ_CRT2b0 0x10 +#define ATOM_S5_DOS_REQ_LCD2b0 0x20 +#define ATOM_S5_DOS_REQ_TV2b0 0x40 +#define ATOM_S5_DOS_REQ_DFP2b0 0x80 +#define ATOM_S5_DOS_REQ_CVb1 0x01 +#define ATOM_S5_DOS_REQ_DFP3b1 0x02 +#define ATOM_S5_DOS_REQ_DFP4b1 0x04 +#define ATOM_S5_DOS_REQ_DFP5b1 0x08 + +#define ATOM_S5_DOS_REQ_DEVICEw0 0x03FF + +#define ATOM_S5_DOS_REQ_CRT1 0x0001 +#define ATOM_S5_DOS_REQ_LCD1 0x0002 +#define ATOM_S5_DOS_REQ_TV1 0x0004 +#define ATOM_S5_DOS_REQ_DFP1 0x0008 +#define ATOM_S5_DOS_REQ_CRT2 0x0010 +#define ATOM_S5_DOS_REQ_LCD2 0x0020 +#define ATOM_S5_DOS_REQ_TV2 0x0040 +#define ATOM_S5_DOS_REQ_DFP2 0x0080 +#define ATOM_S5_DOS_REQ_CV 0x0100 +#define ATOM_S5_DOS_REQ_DFP3 0x0200 +#define ATOM_S5_DOS_REQ_DFP4 0x0400 +#define ATOM_S5_DOS_REQ_DFP5 0x0800 + +#define ATOM_S5_DOS_FORCE_CRT1b2 ATOM_S5_DOS_REQ_CRT1b0 +#define ATOM_S5_DOS_FORCE_TV1b2 ATOM_S5_DOS_REQ_TV1b0 +#define ATOM_S5_DOS_FORCE_CRT2b2 ATOM_S5_DOS_REQ_CRT2b0 +#define ATOM_S5_DOS_FORCE_CVb3 ATOM_S5_DOS_REQ_CVb1 +#define ATOM_S5_DOS_FORCE_DEVICEw1 (ATOM_S5_DOS_FORCE_CRT1b2+ATOM_S5_DOS_FORCE_TV1b2+ATOM_S5_DOS_FORCE_CRT2b2+\ + (ATOM_S5_DOS_FORCE_CVb3<<8)) + +// BIOS_6_SCRATCH Definition +#define ATOM_S6_DEVICE_CHANGE 0x00000001L +#define ATOM_S6_SCALER_CHANGE 0x00000002L +#define ATOM_S6_LID_CHANGE 0x00000004L +#define ATOM_S6_DOCKING_CHANGE 0x00000008L +#define ATOM_S6_ACC_MODE 0x00000010L +#define ATOM_S6_EXT_DESKTOP_MODE 0x00000020L +#define ATOM_S6_LID_STATE 0x00000040L +#define ATOM_S6_DOCK_STATE 0x00000080L +#define ATOM_S6_CRITICAL_STATE 0x00000100L +#define ATOM_S6_HW_I2C_BUSY_STATE 0x00000200L +#define ATOM_S6_THERMAL_STATE_CHANGE 0x00000400L +#define ATOM_S6_INTERRUPT_SET_BY_BIOS 0x00000800L +#define ATOM_S6_REQ_LCD_EXPANSION_FULL 0x00001000L //Normal expansion Request bit for LCD +#define ATOM_S6_REQ_LCD_EXPANSION_ASPEC_RATIO 0x00002000L //Aspect ratio expansion Request bit for LCD + +#define ATOM_S6_DISPLAY_STATE_CHANGE 0x00004000L //This bit is recycled when ATOM_BIOS_INFO_BIOS_SCRATCH6_SCL2_REDEFINE is set,previously it's SCL2_H_expansion +#define ATOM_S6_I2C_STATE_CHANGE 0x00008000L //This bit is recycled,when ATOM_BIOS_INFO_BIOS_SCRATCH6_SCL2_REDEFINE is set,previously it's SCL2_V_expansion + +#define ATOM_S6_ACC_REQ_CRT1 0x00010000L +#define ATOM_S6_ACC_REQ_LCD1 0x00020000L +#define ATOM_S6_ACC_REQ_TV1 0x00040000L +#define ATOM_S6_ACC_REQ_DFP1 0x00080000L +#define ATOM_S6_ACC_REQ_CRT2 0x00100000L +#define ATOM_S6_ACC_REQ_LCD2 0x00200000L +#define ATOM_S6_ACC_REQ_TV2 0x00400000L +#define ATOM_S6_ACC_REQ_DFP2 0x00800000L +#define ATOM_S6_ACC_REQ_CV 0x01000000L +#define ATOM_S6_ACC_REQ_DFP3 0x02000000L +#define ATOM_S6_ACC_REQ_DFP4 0x04000000L +#define ATOM_S6_ACC_REQ_DFP5 0x08000000L + +#define ATOM_S6_ACC_REQ_MASK 0x0FFF0000L +#define ATOM_S6_SYSTEM_POWER_MODE_CHANGE 0x10000000L +#define ATOM_S6_ACC_BLOCK_DISPLAY_SWITCH 0x20000000L +#define ATOM_S6_VRI_BRIGHTNESS_CHANGE 0x40000000L +#define ATOM_S6_CONFIG_DISPLAY_CHANGE_MASK 0x80000000L + +//Byte aligned defintion for BIOS usage +#define ATOM_S6_DEVICE_CHANGEb0 0x01 +#define ATOM_S6_SCALER_CHANGEb0 0x02 +#define ATOM_S6_LID_CHANGEb0 0x04 +#define ATOM_S6_DOCKING_CHANGEb0 0x08 +#define ATOM_S6_ACC_MODEb0 0x10 +#define ATOM_S6_EXT_DESKTOP_MODEb0 0x20 +#define ATOM_S6_LID_STATEb0 0x40 +#define ATOM_S6_DOCK_STATEb0 0x80 +#define ATOM_S6_CRITICAL_STATEb1 0x01 +#define ATOM_S6_HW_I2C_BUSY_STATEb1 0x02 +#define ATOM_S6_THERMAL_STATE_CHANGEb1 0x04 +#define ATOM_S6_INTERRUPT_SET_BY_BIOSb1 0x08 +#define ATOM_S6_REQ_LCD_EXPANSION_FULLb1 0x10 +#define ATOM_S6_REQ_LCD_EXPANSION_ASPEC_RATIOb1 0x20 + +#define ATOM_S6_ACC_REQ_CRT1b2 0x01 +#define ATOM_S6_ACC_REQ_LCD1b2 0x02 +#define ATOM_S6_ACC_REQ_TV1b2 0x04 +#define ATOM_S6_ACC_REQ_DFP1b2 0x08 +#define ATOM_S6_ACC_REQ_CRT2b2 0x10 +#define ATOM_S6_ACC_REQ_LCD2b2 0x20 +#define ATOM_S6_ACC_REQ_TV2b2 0x40 +#define ATOM_S6_ACC_REQ_DFP2b2 0x80 +#define ATOM_S6_ACC_REQ_CVb3 0x01 +#define ATOM_S6_ACC_REQ_DFP3b3 0x02 +#define ATOM_S6_ACC_REQ_DFP4b3 0x04 +#define ATOM_S6_ACC_REQ_DFP5b3 0x08 + +#define ATOM_S6_ACC_REQ_DEVICEw1 ATOM_S5_DOS_REQ_DEVICEw0 +#define ATOM_S6_SYSTEM_POWER_MODE_CHANGEb3 0x10 +#define ATOM_S6_ACC_BLOCK_DISPLAY_SWITCHb3 0x20 +#define ATOM_S6_VRI_BRIGHTNESS_CHANGEb3 0x40 +#define ATOM_S6_CONFIG_DISPLAY_CHANGEb3 0x80 + +#define ATOM_S6_DEVICE_CHANGE_SHIFT 0 +#define ATOM_S6_SCALER_CHANGE_SHIFT 1 +#define ATOM_S6_LID_CHANGE_SHIFT 2 +#define ATOM_S6_DOCKING_CHANGE_SHIFT 3 +#define ATOM_S6_ACC_MODE_SHIFT 4 +#define ATOM_S6_EXT_DESKTOP_MODE_SHIFT 5 +#define ATOM_S6_LID_STATE_SHIFT 6 +#define ATOM_S6_DOCK_STATE_SHIFT 7 +#define ATOM_S6_CRITICAL_STATE_SHIFT 8 +#define ATOM_S6_HW_I2C_BUSY_STATE_SHIFT 9 +#define ATOM_S6_THERMAL_STATE_CHANGE_SHIFT 10 +#define ATOM_S6_INTERRUPT_SET_BY_BIOS_SHIFT 11 +#define ATOM_S6_REQ_SCALER_SHIFT 12 +#define ATOM_S6_REQ_SCALER_ARATIO_SHIFT 13 +#define ATOM_S6_DISPLAY_STATE_CHANGE_SHIFT 14 +#define ATOM_S6_I2C_STATE_CHANGE_SHIFT 15 +#define ATOM_S6_SYSTEM_POWER_MODE_CHANGE_SHIFT 28 +#define ATOM_S6_ACC_BLOCK_DISPLAY_SWITCH_SHIFT 29 +#define ATOM_S6_VRI_BRIGHTNESS_CHANGE_SHIFT 30 +#define ATOM_S6_CONFIG_DISPLAY_CHANGE_SHIFT 31 + +// BIOS_7_SCRATCH Definition, BIOS_7_SCRATCH is used by Firmware only !!!! +#define ATOM_S7_DOS_MODE_TYPEb0 0x03 +#define ATOM_S7_DOS_MODE_VGAb0 0x00 +#define ATOM_S7_DOS_MODE_VESAb0 0x01 +#define ATOM_S7_DOS_MODE_EXTb0 0x02 +#define ATOM_S7_DOS_MODE_PIXEL_DEPTHb0 0x0C +#define ATOM_S7_DOS_MODE_PIXEL_FORMATb0 0xF0 +#define ATOM_S7_DOS_8BIT_DAC_ENb1 0x01 +#define ATOM_S7_DOS_MODE_NUMBERw1 0x0FFFF + +#define ATOM_S7_DOS_8BIT_DAC_EN_SHIFT 8 + +// BIOS_8_SCRATCH Definition +#define ATOM_S8_I2C_CHANNEL_BUSY_MASK 0x00000FFFF +#define ATOM_S8_I2C_HW_ENGINE_BUSY_MASK 0x0FFFF0000 + +#define ATOM_S8_I2C_CHANNEL_BUSY_SHIFT 0 +#define ATOM_S8_I2C_ENGINE_BUSY_SHIFT 16 + +// BIOS_9_SCRATCH Definition +#ifndef ATOM_S9_I2C_CHANNEL_COMPLETED_MASK +#define ATOM_S9_I2C_CHANNEL_COMPLETED_MASK 0x0000FFFF +#endif +#ifndef ATOM_S9_I2C_CHANNEL_ABORTED_MASK +#define ATOM_S9_I2C_CHANNEL_ABORTED_MASK 0xFFFF0000 +#endif +#ifndef ATOM_S9_I2C_CHANNEL_COMPLETED_SHIFT +#define ATOM_S9_I2C_CHANNEL_COMPLETED_SHIFT 0 +#endif +#ifndef ATOM_S9_I2C_CHANNEL_ABORTED_SHIFT +#define ATOM_S9_I2C_CHANNEL_ABORTED_SHIFT 16 +#endif + + +#define ATOM_FLAG_SET 0x20 +#define ATOM_FLAG_CLEAR 0 +#define CLEAR_ATOM_S6_ACC_MODE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_ACC_MODE_SHIFT | ATOM_FLAG_CLEAR) +#define SET_ATOM_S6_DEVICE_CHANGE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_DEVICE_CHANGE_SHIFT | ATOM_FLAG_SET) +#define SET_ATOM_S6_VRI_BRIGHTNESS_CHANGE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_VRI_BRIGHTNESS_CHANGE_SHIFT | ATOM_FLAG_SET) +#define SET_ATOM_S6_SCALER_CHANGE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_SCALER_CHANGE_SHIFT | ATOM_FLAG_SET) +#define SET_ATOM_S6_LID_CHANGE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_LID_CHANGE_SHIFT | ATOM_FLAG_SET) + +#define SET_ATOM_S6_LID_STATE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_LID_STATE_SHIFT | ATOM_FLAG_SET) +#define CLEAR_ATOM_S6_LID_STATE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_LID_STATE_SHIFT | ATOM_FLAG_CLEAR) + +#define SET_ATOM_S6_DOCK_CHANGE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_DOCKING_CHANGE_SHIFT | ATOM_FLAG_SET) +#define SET_ATOM_S6_DOCK_STATE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_DOCK_STATE_SHIFT | ATOM_FLAG_SET) +#define CLEAR_ATOM_S6_DOCK_STATE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_DOCK_STATE_SHIFT | ATOM_FLAG_CLEAR) + +#define SET_ATOM_S6_THERMAL_STATE_CHANGE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_THERMAL_STATE_CHANGE_SHIFT | ATOM_FLAG_SET) +#define SET_ATOM_S6_SYSTEM_POWER_MODE_CHANGE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_SYSTEM_POWER_MODE_CHANGE_SHIFT | ATOM_FLAG_SET) +#define SET_ATOM_S6_INTERRUPT_SET_BY_BIOS ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_INTERRUPT_SET_BY_BIOS_SHIFT | ATOM_FLAG_SET) + +#define SET_ATOM_S6_CRITICAL_STATE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_CRITICAL_STATE_SHIFT | ATOM_FLAG_SET) +#define CLEAR_ATOM_S6_CRITICAL_STATE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_CRITICAL_STATE_SHIFT | ATOM_FLAG_CLEAR) + +#define SET_ATOM_S6_REQ_SCALER ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_REQ_SCALER_SHIFT | ATOM_FLAG_SET) +#define CLEAR_ATOM_S6_REQ_SCALER ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_REQ_SCALER_SHIFT | ATOM_FLAG_CLEAR ) + +#define SET_ATOM_S6_REQ_SCALER_ARATIO ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_REQ_SCALER_ARATIO_SHIFT | ATOM_FLAG_SET ) +#define CLEAR_ATOM_S6_REQ_SCALER_ARATIO ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_REQ_SCALER_ARATIO_SHIFT | ATOM_FLAG_CLEAR ) + +#define SET_ATOM_S6_I2C_STATE_CHANGE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_I2C_STATE_CHANGE_SHIFT | ATOM_FLAG_SET ) + +#define SET_ATOM_S6_DISPLAY_STATE_CHANGE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_DISPLAY_STATE_CHANGE_SHIFT | ATOM_FLAG_SET ) + +#define SET_ATOM_S6_DEVICE_RECONFIG ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_CONFIG_DISPLAY_CHANGE_SHIFT | ATOM_FLAG_SET) +#define CLEAR_ATOM_S0_LCD1 ((ATOM_DEVICE_CONNECT_INFO_DEF << 8 )| ATOM_S0_LCD1_SHIFT | ATOM_FLAG_CLEAR ) +#define SET_ATOM_S7_DOS_8BIT_DAC_EN ((ATOM_DOS_MODE_INFO_DEF << 8 )|ATOM_S7_DOS_8BIT_DAC_EN_SHIFT | ATOM_FLAG_SET ) +#define CLEAR_ATOM_S7_DOS_8BIT_DAC_EN ((ATOM_DOS_MODE_INFO_DEF << 8 )|ATOM_S7_DOS_8BIT_DAC_EN_SHIFT | ATOM_FLAG_CLEAR ) + +/****************************************************************************/ +//Portion II: Definitinos only used in Driver +/****************************************************************************/ + +// Macros used by driver + +#define GetIndexIntoMasterTable(MasterOrData, FieldName) (((char*)(&((ATOM_MASTER_LIST_OF_##MasterOrData##_TABLES*)0)->FieldName)-(char*)0)/sizeof(USHORT)) + +#define GET_COMMAND_TABLE_COMMANDSET_REVISION(TABLE_HEADER_OFFSET) ((((ATOM_COMMON_TABLE_HEADER*)TABLE_HEADER_OFFSET)->ucTableFormatRevision)&0x3F) +#define GET_COMMAND_TABLE_PARAMETER_REVISION(TABLE_HEADER_OFFSET) ((((ATOM_COMMON_TABLE_HEADER*)TABLE_HEADER_OFFSET)->ucTableContentRevision)&0x3F) + +#define GET_DATA_TABLE_MAJOR_REVISION GET_COMMAND_TABLE_COMMANDSET_REVISION +#define GET_DATA_TABLE_MINOR_REVISION GET_COMMAND_TABLE_PARAMETER_REVISION + +/****************************************************************************/ +//Portion III: Definitinos only used in VBIOS +/****************************************************************************/ +#define ATOM_DAC_SRC 0x80 +#define ATOM_SRC_DAC1 0 +#define ATOM_SRC_DAC2 0x80 + + +#ifdef UEFI_BUILD + #define USHORT UTEMP +#endif + +typedef struct _MEMORY_PLLINIT_PARAMETERS +{ + ULONG ulTargetMemoryClock; //In 10Khz unit + UCHAR ucAction; //not define yet + UCHAR ucFbDiv_Hi; //Fbdiv Hi byte + UCHAR ucFbDiv; //FB value + UCHAR ucPostDiv; //Post div +}MEMORY_PLLINIT_PARAMETERS; + +#define MEMORY_PLLINIT_PS_ALLOCATION MEMORY_PLLINIT_PARAMETERS + + +#define GPIO_PIN_WRITE 0x01 +#define GPIO_PIN_READ 0x00 + +typedef struct _GPIO_PIN_CONTROL_PARAMETERS +{ + UCHAR ucGPIO_ID; //return value, read from GPIO pins + UCHAR ucGPIOBitShift; //define which bit in uGPIOBitVal need to be update + UCHAR ucGPIOBitVal; //Set/Reset corresponding bit defined in ucGPIOBitMask + UCHAR ucAction; //=GPIO_PIN_WRITE: Read; =GPIO_PIN_READ: Write +}GPIO_PIN_CONTROL_PARAMETERS; + +typedef struct _ENABLE_SCALER_PARAMETERS +{ + UCHAR ucScaler; // ATOM_SCALER1, ATOM_SCALER2 + UCHAR ucEnable; // ATOM_SCALER_DISABLE or ATOM_SCALER_CENTER or ATOM_SCALER_EXPANSION + UCHAR ucTVStandard; // + UCHAR ucPadding[1]; +}ENABLE_SCALER_PARAMETERS; +#define ENABLE_SCALER_PS_ALLOCATION ENABLE_SCALER_PARAMETERS + +//ucEnable: +#define SCALER_BYPASS_AUTO_CENTER_NO_REPLICATION 0 +#define SCALER_BYPASS_AUTO_CENTER_AUTO_REPLICATION 1 +#define SCALER_ENABLE_2TAP_ALPHA_MODE 2 +#define SCALER_ENABLE_MULTITAP_MODE 3 + +typedef struct _ENABLE_HARDWARE_ICON_CURSOR_PARAMETERS +{ + ULONG usHWIconHorzVertPosn; // Hardware Icon Vertical position + UCHAR ucHWIconVertOffset; // Hardware Icon Vertical offset + UCHAR ucHWIconHorzOffset; // Hardware Icon Horizontal offset + UCHAR ucSelection; // ATOM_CURSOR1 or ATOM_ICON1 or ATOM_CURSOR2 or ATOM_ICON2 + UCHAR ucEnable; // ATOM_ENABLE or ATOM_DISABLE +}ENABLE_HARDWARE_ICON_CURSOR_PARAMETERS; + +typedef struct _ENABLE_HARDWARE_ICON_CURSOR_PS_ALLOCATION +{ + ENABLE_HARDWARE_ICON_CURSOR_PARAMETERS sEnableIcon; + ENABLE_CRTC_PARAMETERS sReserved; +}ENABLE_HARDWARE_ICON_CURSOR_PS_ALLOCATION; + +typedef struct _ENABLE_GRAPH_SURFACE_PARAMETERS +{ + USHORT usHight; // Image Hight + USHORT usWidth; // Image Width + UCHAR ucSurface; // Surface 1 or 2 + UCHAR ucPadding[3]; +}ENABLE_GRAPH_SURFACE_PARAMETERS; + +typedef struct _ENABLE_GRAPH_SURFACE_PARAMETERS_V1_2 +{ + USHORT usHight; // Image Hight + USHORT usWidth; // Image Width + UCHAR ucSurface; // Surface 1 or 2 + UCHAR ucEnable; // ATOM_ENABLE or ATOM_DISABLE + UCHAR ucPadding[2]; +}ENABLE_GRAPH_SURFACE_PARAMETERS_V1_2; + +typedef struct _ENABLE_GRAPH_SURFACE_PS_ALLOCATION +{ + ENABLE_GRAPH_SURFACE_PARAMETERS sSetSurface; + ENABLE_YUV_PS_ALLOCATION sReserved; // Don't set this one +}ENABLE_GRAPH_SURFACE_PS_ALLOCATION; + +typedef struct _MEMORY_CLEAN_UP_PARAMETERS +{ + USHORT usMemoryStart; //in 8Kb boundry, offset from memory base address + USHORT usMemorySize; //8Kb blocks aligned +}MEMORY_CLEAN_UP_PARAMETERS; +#define MEMORY_CLEAN_UP_PS_ALLOCATION MEMORY_CLEAN_UP_PARAMETERS + +typedef struct _GET_DISPLAY_SURFACE_SIZE_PARAMETERS +{ + USHORT usX_Size; //When use as input parameter, usX_Size indicates which CRTC + USHORT usY_Size; +}GET_DISPLAY_SURFACE_SIZE_PARAMETERS; + +typedef struct _INDIRECT_IO_ACCESS +{ + ATOM_COMMON_TABLE_HEADER sHeader; + UCHAR IOAccessSequence[256]; +} INDIRECT_IO_ACCESS; + +#define INDIRECT_READ 0x00 +#define INDIRECT_WRITE 0x80 + +#define INDIRECT_IO_MM 0 +#define INDIRECT_IO_PLL 1 +#define INDIRECT_IO_MC 2 +#define INDIRECT_IO_PCIE 3 +#define INDIRECT_IO_PCIEP 4 +#define INDIRECT_IO_NBMISC 5 + +#define INDIRECT_IO_PLL_READ INDIRECT_IO_PLL | INDIRECT_READ +#define INDIRECT_IO_PLL_WRITE INDIRECT_IO_PLL | INDIRECT_WRITE +#define INDIRECT_IO_MC_READ INDIRECT_IO_MC | INDIRECT_READ +#define INDIRECT_IO_MC_WRITE INDIRECT_IO_MC | INDIRECT_WRITE +#define INDIRECT_IO_PCIE_READ INDIRECT_IO_PCIE | INDIRECT_READ +#define INDIRECT_IO_PCIE_WRITE INDIRECT_IO_PCIE | INDIRECT_WRITE +#define INDIRECT_IO_PCIEP_READ INDIRECT_IO_PCIEP | INDIRECT_READ +#define INDIRECT_IO_PCIEP_WRITE INDIRECT_IO_PCIEP | INDIRECT_WRITE +#define INDIRECT_IO_NBMISC_READ INDIRECT_IO_NBMISC | INDIRECT_READ +#define INDIRECT_IO_NBMISC_WRITE INDIRECT_IO_NBMISC | INDIRECT_WRITE + +typedef struct _ATOM_OEM_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_I2C_ID_CONFIG_ACCESS sucI2cId; +}ATOM_OEM_INFO; + +typedef struct _ATOM_TV_MODE +{ + UCHAR ucVMode_Num; //Video mode number + UCHAR ucTV_Mode_Num; //Internal TV mode number +}ATOM_TV_MODE; + +typedef struct _ATOM_BIOS_INT_TVSTD_MODE +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usTV_Mode_LUT_Offset; // Pointer to standard to internal number conversion table + USHORT usTV_FIFO_Offset; // Pointer to FIFO entry table + USHORT usNTSC_Tbl_Offset; // Pointer to SDTV_Mode_NTSC table + USHORT usPAL_Tbl_Offset; // Pointer to SDTV_Mode_PAL table + USHORT usCV_Tbl_Offset; // Pointer to SDTV_Mode_PAL table +}ATOM_BIOS_INT_TVSTD_MODE; + + +typedef struct _ATOM_TV_MODE_SCALER_PTR +{ + USHORT ucFilter0_Offset; //Pointer to filter format 0 coefficients + USHORT usFilter1_Offset; //Pointer to filter format 0 coefficients + UCHAR ucTV_Mode_Num; +}ATOM_TV_MODE_SCALER_PTR; + +typedef struct _ATOM_STANDARD_VESA_TIMING +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_DTD_FORMAT aModeTimings[16]; // 16 is not the real array number, just for initial allocation +}ATOM_STANDARD_VESA_TIMING; + + +typedef struct _ATOM_STD_FORMAT +{ + USHORT usSTD_HDisp; + USHORT usSTD_VDisp; + USHORT usSTD_RefreshRate; + USHORT usReserved; +}ATOM_STD_FORMAT; + +typedef struct _ATOM_VESA_TO_EXTENDED_MODE +{ + USHORT usVESA_ModeNumber; + USHORT usExtendedModeNumber; +}ATOM_VESA_TO_EXTENDED_MODE; + +typedef struct _ATOM_VESA_TO_INTENAL_MODE_LUT +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_VESA_TO_EXTENDED_MODE asVESA_ToExtendedModeInfo[76]; +}ATOM_VESA_TO_INTENAL_MODE_LUT; + +/*************** ATOM Memory Related Data Structure ***********************/ +typedef struct _ATOM_MEMORY_VENDOR_BLOCK{ + UCHAR ucMemoryType; + UCHAR ucMemoryVendor; + UCHAR ucAdjMCId; + UCHAR ucDynClkId; + ULONG ulDllResetClkRange; +}ATOM_MEMORY_VENDOR_BLOCK; + + +typedef struct _ATOM_MEMORY_SETTING_ID_CONFIG{ +#if ATOM_BIG_ENDIAN + ULONG ucMemBlkId:8; + ULONG ulMemClockRange:24; +#else + ULONG ulMemClockRange:24; + ULONG ucMemBlkId:8; +#endif +}ATOM_MEMORY_SETTING_ID_CONFIG; + +typedef union _ATOM_MEMORY_SETTING_ID_CONFIG_ACCESS +{ + ATOM_MEMORY_SETTING_ID_CONFIG slAccess; + ULONG ulAccess; +}ATOM_MEMORY_SETTING_ID_CONFIG_ACCESS; + + +typedef struct _ATOM_MEMORY_SETTING_DATA_BLOCK{ + ATOM_MEMORY_SETTING_ID_CONFIG_ACCESS ulMemoryID; + ULONG aulMemData[1]; +}ATOM_MEMORY_SETTING_DATA_BLOCK; + + +typedef struct _ATOM_INIT_REG_INDEX_FORMAT{ + USHORT usRegIndex; // MC register index + UCHAR ucPreRegDataLength; // offset in ATOM_INIT_REG_DATA_BLOCK.saRegDataBuf +}ATOM_INIT_REG_INDEX_FORMAT; + + +typedef struct _ATOM_INIT_REG_BLOCK{ + USHORT usRegIndexTblSize; //size of asRegIndexBuf + USHORT usRegDataBlkSize; //size of ATOM_MEMORY_SETTING_DATA_BLOCK + ATOM_INIT_REG_INDEX_FORMAT asRegIndexBuf[1]; + ATOM_MEMORY_SETTING_DATA_BLOCK asRegDataBuf[1]; +}ATOM_INIT_REG_BLOCK; + +#define END_OF_REG_INDEX_BLOCK 0x0ffff +#define END_OF_REG_DATA_BLOCK 0x00000000 +#define ATOM_INIT_REG_MASK_FLAG 0x80 +#define CLOCK_RANGE_HIGHEST 0x00ffffff + +#define VALUE_DWORD SIZEOF ULONG +#define VALUE_SAME_AS_ABOVE 0 +#define VALUE_MASK_DWORD 0x84 + +#define INDEX_ACCESS_RANGE_BEGIN (VALUE_DWORD + 1) +#define INDEX_ACCESS_RANGE_END (INDEX_ACCESS_RANGE_BEGIN + 1) +#define VALUE_INDEX_ACCESS_SINGLE (INDEX_ACCESS_RANGE_END + 1) + + +typedef struct _ATOM_MC_INIT_PARAM_TABLE +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usAdjustARB_SEQDataOffset; + USHORT usMCInitMemTypeTblOffset; + USHORT usMCInitCommonTblOffset; + USHORT usMCInitPowerDownTblOffset; + ULONG ulARB_SEQDataBuf[32]; + ATOM_INIT_REG_BLOCK asMCInitMemType; + ATOM_INIT_REG_BLOCK asMCInitCommon; +}ATOM_MC_INIT_PARAM_TABLE; + + +#define _4Mx16 0x2 +#define _4Mx32 0x3 +#define _8Mx16 0x12 +#define _8Mx32 0x13 +#define _16Mx16 0x22 +#define _16Mx32 0x23 +#define _32Mx16 0x32 +#define _32Mx32 0x33 +#define _64Mx8 0x41 +#define _64Mx16 0x42 + +#define SAMSUNG 0x1 +#define INFINEON 0x2 +#define ELPIDA 0x3 +#define ETRON 0x4 +#define NANYA 0x5 +#define HYNIX 0x6 +#define MOSEL 0x7 +#define WINBOND 0x8 +#define ESMT 0x9 +#define MICRON 0xF + +#define QIMONDA INFINEON +#define PROMOS MOSEL + +/////////////Support for GDDR5 MC uCode to reside in upper 64K of ROM///////////// + +#define UCODE_ROM_START_ADDRESS 0x1c000 +#define UCODE_SIGNATURE 0x4375434d // 'MCuC' - MC uCode + +//uCode block header for reference + +typedef struct _MCuCodeHeader +{ + ULONG ulSignature; + UCHAR ucRevision; + UCHAR ucChecksum; + UCHAR ucReserved1; + UCHAR ucReserved2; + USHORT usParametersLength; + USHORT usUCodeLength; + USHORT usReserved1; + USHORT usReserved2; +} MCuCodeHeader; + +////////////////////////////////////////////////////////////////////////////////// + +#define ATOM_MAX_NUMBER_OF_VRAM_MODULE 16 + +#define ATOM_VRAM_MODULE_MEMORY_VENDOR_ID_MASK 0xF +typedef struct _ATOM_VRAM_MODULE_V1 +{ + ULONG ulReserved; + USHORT usEMRSValue; + USHORT usMRSValue; + USHORT usReserved; + UCHAR ucExtMemoryID; // An external indicator (by hardcode, callback or pin) to tell what is the current memory module + UCHAR ucMemoryType; // [7:4]=0x1:DDR1;=0x2:DDR2;=0x3:DDR3;=0x4:DDR4;[3:0] reserved; + UCHAR ucMemoryVenderID; // Predefined,never change across designs or memory type/vender + UCHAR ucMemoryDeviceCfg; // [7:4]=0x0:4M;=0x1:8M;=0x2:16M;0x3:32M....[3:0]=0x0:x4;=0x1:x8;=0x2:x16;=0x3:x32... + UCHAR ucRow; // Number of Row,in power of 2; + UCHAR ucColumn; // Number of Column,in power of 2; + UCHAR ucBank; // Nunber of Bank; + UCHAR ucRank; // Number of Rank, in power of 2 + UCHAR ucChannelNum; // Number of channel; + UCHAR ucChannelConfig; // [3:0]=Indication of what channel combination;[4:7]=Channel bit width, in number of 2 + UCHAR ucDefaultMVDDQ_ID; // Default MVDDQ setting for this memory block, ID linking to MVDDQ info table to find real set-up data; + UCHAR ucDefaultMVDDC_ID; // Default MVDDC setting for this memory block, ID linking to MVDDC info table to find real set-up data; + UCHAR ucReserved[2]; +}ATOM_VRAM_MODULE_V1; + + +typedef struct _ATOM_VRAM_MODULE_V2 +{ + ULONG ulReserved; + ULONG ulFlags; // To enable/disable functionalities based on memory type + ULONG ulEngineClock; // Override of default engine clock for particular memory type + ULONG ulMemoryClock; // Override of default memory clock for particular memory type + USHORT usEMRS2Value; // EMRS2 Value is used for GDDR2 and GDDR4 memory type + USHORT usEMRS3Value; // EMRS3 Value is used for GDDR2 and GDDR4 memory type + USHORT usEMRSValue; + USHORT usMRSValue; + USHORT usReserved; + UCHAR ucExtMemoryID; // An external indicator (by hardcode, callback or pin) to tell what is the current memory module + UCHAR ucMemoryType; // [7:4]=0x1:DDR1;=0x2:DDR2;=0x3:DDR3;=0x4:DDR4;[3:0] - must not be used for now; + UCHAR ucMemoryVenderID; // Predefined,never change across designs or memory type/vender. If not predefined, vendor detection table gets executed + UCHAR ucMemoryDeviceCfg; // [7:4]=0x0:4M;=0x1:8M;=0x2:16M;0x3:32M....[3:0]=0x0:x4;=0x1:x8;=0x2:x16;=0x3:x32... + UCHAR ucRow; // Number of Row,in power of 2; + UCHAR ucColumn; // Number of Column,in power of 2; + UCHAR ucBank; // Nunber of Bank; + UCHAR ucRank; // Number of Rank, in power of 2 + UCHAR ucChannelNum; // Number of channel; + UCHAR ucChannelConfig; // [3:0]=Indication of what channel combination;[4:7]=Channel bit width, in number of 2 + UCHAR ucDefaultMVDDQ_ID; // Default MVDDQ setting for this memory block, ID linking to MVDDQ info table to find real set-up data; + UCHAR ucDefaultMVDDC_ID; // Default MVDDC setting for this memory block, ID linking to MVDDC info table to find real set-up data; + UCHAR ucRefreshRateFactor; + UCHAR ucReserved[3]; +}ATOM_VRAM_MODULE_V2; + + +typedef struct _ATOM_MEMORY_TIMING_FORMAT +{ + ULONG ulClkRange; // memory clock in 10kHz unit, when target memory clock is below this clock, use this memory timing + union{ + USHORT usMRS; // mode register + USHORT usDDR3_MR0; + }; + union{ + USHORT usEMRS; // extended mode register + USHORT usDDR3_MR1; + }; + UCHAR ucCL; // CAS latency + UCHAR ucWL; // WRITE Latency + UCHAR uctRAS; // tRAS + UCHAR uctRC; // tRC + UCHAR uctRFC; // tRFC + UCHAR uctRCDR; // tRCDR + UCHAR uctRCDW; // tRCDW + UCHAR uctRP; // tRP + UCHAR uctRRD; // tRRD + UCHAR uctWR; // tWR + UCHAR uctWTR; // tWTR + UCHAR uctPDIX; // tPDIX + UCHAR uctFAW; // tFAW + UCHAR uctAOND; // tAOND + union + { + struct { + UCHAR ucflag; // flag to control memory timing calculation. bit0= control EMRS2 Infineon + UCHAR ucReserved; + }; + USHORT usDDR3_MR2; + }; +}ATOM_MEMORY_TIMING_FORMAT; + + +typedef struct _ATOM_MEMORY_TIMING_FORMAT_V1 +{ + ULONG ulClkRange; // memory clock in 10kHz unit, when target memory clock is below this clock, use this memory timing + USHORT usMRS; // mode register + USHORT usEMRS; // extended mode register + UCHAR ucCL; // CAS latency + UCHAR ucWL; // WRITE Latency + UCHAR uctRAS; // tRAS + UCHAR uctRC; // tRC + UCHAR uctRFC; // tRFC + UCHAR uctRCDR; // tRCDR + UCHAR uctRCDW; // tRCDW + UCHAR uctRP; // tRP + UCHAR uctRRD; // tRRD + UCHAR uctWR; // tWR + UCHAR uctWTR; // tWTR + UCHAR uctPDIX; // tPDIX + UCHAR uctFAW; // tFAW + UCHAR uctAOND; // tAOND + UCHAR ucflag; // flag to control memory timing calculation. bit0= control EMRS2 Infineon +////////////////////////////////////GDDR parameters/////////////////////////////////// + UCHAR uctCCDL; // + UCHAR uctCRCRL; // + UCHAR uctCRCWL; // + UCHAR uctCKE; // + UCHAR uctCKRSE; // + UCHAR uctCKRSX; // + UCHAR uctFAW32; // + UCHAR ucReserved1; // + UCHAR ucReserved2; // + UCHAR ucTerminator; +}ATOM_MEMORY_TIMING_FORMAT_V1; + + +typedef struct _ATOM_MEMORY_FORMAT +{ + ULONG ulDllDisClock; // memory DLL will be disable when target memory clock is below this clock + union{ + USHORT usEMRS2Value; // EMRS2 Value is used for GDDR2 and GDDR4 memory type + USHORT usDDR3_Reserved; // Not used for DDR3 memory + }; + union{ + USHORT usEMRS3Value; // EMRS3 Value is used for GDDR2 and GDDR4 memory type + USHORT usDDR3_MR3; // Used for DDR3 memory + }; + UCHAR ucMemoryType; // [7:4]=0x1:DDR1;=0x2:DDR2;=0x3:DDR3;=0x4:DDR4;[3:0] - must not be used for now; + UCHAR ucMemoryVenderID; // Predefined,never change across designs or memory type/vender. If not predefined, vendor detection table gets executed + UCHAR ucRow; // Number of Row,in power of 2; + UCHAR ucColumn; // Number of Column,in power of 2; + UCHAR ucBank; // Nunber of Bank; + UCHAR ucRank; // Number of Rank, in power of 2 + UCHAR ucBurstSize; // burst size, 0= burst size=4 1= burst size=8 + UCHAR ucDllDisBit; // position of DLL Enable/Disable bit in EMRS ( Extended Mode Register ) + UCHAR ucRefreshRateFactor; // memory refresh rate in unit of ms + UCHAR ucDensity; // _8Mx32, _16Mx32, _16Mx16, _32Mx16 + UCHAR ucPreamble; //[7:4] Write Preamble, [3:0] Read Preamble + UCHAR ucMemAttrib; // Memory Device Addribute, like RDBI/WDBI etc + ATOM_MEMORY_TIMING_FORMAT asMemTiming[5]; //Memory Timing block sort from lower clock to higher clock +}ATOM_MEMORY_FORMAT; + + +typedef struct _ATOM_VRAM_MODULE_V3 +{ + ULONG ulChannelMapCfg; // board dependent paramenter:Channel combination + USHORT usSize; // size of ATOM_VRAM_MODULE_V3 + USHORT usDefaultMVDDQ; // board dependent parameter:Default Memory Core Voltage + USHORT usDefaultMVDDC; // board dependent parameter:Default Memory IO Voltage + UCHAR ucExtMemoryID; // An external indicator (by hardcode, callback or pin) to tell what is the current memory module + UCHAR ucChannelNum; // board dependent parameter:Number of channel; + UCHAR ucChannelSize; // board dependent parameter:32bit or 64bit + UCHAR ucVREFI; // board dependnt parameter: EXT or INT +160mv to -140mv + UCHAR ucNPL_RT; // board dependent parameter:NPL round trip delay, used for calculate memory timing parameters + UCHAR ucFlag; // To enable/disable functionalities based on memory type + ATOM_MEMORY_FORMAT asMemory; // describ all of video memory parameters from memory spec +}ATOM_VRAM_MODULE_V3; + + +//ATOM_VRAM_MODULE_V3.ucNPL_RT +#define NPL_RT_MASK 0x0f +#define BATTERY_ODT_MASK 0xc0 + +#define ATOM_VRAM_MODULE ATOM_VRAM_MODULE_V3 + +typedef struct _ATOM_VRAM_MODULE_V4 +{ + ULONG ulChannelMapCfg; // board dependent parameter: Channel combination + USHORT usModuleSize; // size of ATOM_VRAM_MODULE_V4, make it easy for VBIOS to look for next entry of VRAM_MODULE + USHORT usPrivateReserved; // BIOS internal reserved space to optimize code size, updated by the compiler, shouldn't be modified manually!! + // MC_ARB_RAMCFG (includes NOOFBANK,NOOFRANKS,NOOFROWS,NOOFCOLS) + USHORT usReserved; + UCHAR ucExtMemoryID; // An external indicator (by hardcode, callback or pin) to tell what is the current memory module + UCHAR ucMemoryType; // [7:4]=0x1:DDR1;=0x2:DDR2;=0x3:DDR3;=0x4:DDR4; 0x5:DDR5 [3:0] - Must be 0x0 for now; + UCHAR ucChannelNum; // Number of channels present in this module config + UCHAR ucChannelWidth; // 0 - 32 bits; 1 - 64 bits + UCHAR ucDensity; // _8Mx32, _16Mx32, _16Mx16, _32Mx16 + UCHAR ucFlag; // To enable/disable functionalities based on memory type + UCHAR ucMisc; // bit0: 0 - single rank; 1 - dual rank; bit2: 0 - burstlength 4, 1 - burstlength 8 + UCHAR ucVREFI; // board dependent parameter + UCHAR ucNPL_RT; // board dependent parameter:NPL round trip delay, used for calculate memory timing parameters + UCHAR ucPreamble; // [7:4] Write Preamble, [3:0] Read Preamble + UCHAR ucMemorySize; // BIOS internal reserved space to optimize code size, updated by the compiler, shouldn't be modified manually!! + // Total memory size in unit of 16MB for CONFIG_MEMSIZE - bit[23:0] zeros + UCHAR ucReserved[3]; + +//compare with V3, we flat the struct by merging ATOM_MEMORY_FORMAT (as is) into V4 as the same level + union{ + USHORT usEMRS2Value; // EMRS2 Value is used for GDDR2 and GDDR4 memory type + USHORT usDDR3_Reserved; + }; + union{ + USHORT usEMRS3Value; // EMRS3 Value is used for GDDR2 and GDDR4 memory type + USHORT usDDR3_MR3; // Used for DDR3 memory + }; + UCHAR ucMemoryVenderID; // Predefined, If not predefined, vendor detection table gets executed + UCHAR ucRefreshRateFactor; // [1:0]=RefreshFactor (00=8ms, 01=16ms, 10=32ms,11=64ms) + UCHAR ucReserved2[2]; + ATOM_MEMORY_TIMING_FORMAT asMemTiming[5];//Memory Timing block sort from lower clock to higher clock +}ATOM_VRAM_MODULE_V4; + +#define VRAM_MODULE_V4_MISC_RANK_MASK 0x3 +#define VRAM_MODULE_V4_MISC_DUAL_RANK 0x1 +#define VRAM_MODULE_V4_MISC_BL_MASK 0x4 +#define VRAM_MODULE_V4_MISC_BL8 0x4 +#define VRAM_MODULE_V4_MISC_DUAL_CS 0x10 + +typedef struct _ATOM_VRAM_MODULE_V5 +{ + ULONG ulChannelMapCfg; // board dependent parameter: Channel combination + USHORT usModuleSize; // size of ATOM_VRAM_MODULE_V4, make it easy for VBIOS to look for next entry of VRAM_MODULE + USHORT usPrivateReserved; // BIOS internal reserved space to optimize code size, updated by the compiler, shouldn't be modified manually!! + // MC_ARB_RAMCFG (includes NOOFBANK,NOOFRANKS,NOOFROWS,NOOFCOLS) + USHORT usReserved; + UCHAR ucExtMemoryID; // An external indicator (by hardcode, callback or pin) to tell what is the current memory module + UCHAR ucMemoryType; // [7:4]=0x1:DDR1;=0x2:DDR2;=0x3:DDR3;=0x4:DDR4; 0x5:DDR5 [3:0] - Must be 0x0 for now; + UCHAR ucChannelNum; // Number of channels present in this module config + UCHAR ucChannelWidth; // 0 - 32 bits; 1 - 64 bits + UCHAR ucDensity; // _8Mx32, _16Mx32, _16Mx16, _32Mx16 + UCHAR ucFlag; // To enable/disable functionalities based on memory type + UCHAR ucMisc; // bit0: 0 - single rank; 1 - dual rank; bit2: 0 - burstlength 4, 1 - burstlength 8 + UCHAR ucVREFI; // board dependent parameter + UCHAR ucNPL_RT; // board dependent parameter:NPL round trip delay, used for calculate memory timing parameters + UCHAR ucPreamble; // [7:4] Write Preamble, [3:0] Read Preamble + UCHAR ucMemorySize; // BIOS internal reserved space to optimize code size, updated by the compiler, shouldn't be modified manually!! + // Total memory size in unit of 16MB for CONFIG_MEMSIZE - bit[23:0] zeros + UCHAR ucReserved[3]; + +//compare with V3, we flat the struct by merging ATOM_MEMORY_FORMAT (as is) into V4 as the same level + USHORT usEMRS2Value; // EMRS2 Value is used for GDDR2 and GDDR4 memory type + USHORT usEMRS3Value; // EMRS3 Value is used for GDDR2 and GDDR4 memory type + UCHAR ucMemoryVenderID; // Predefined, If not predefined, vendor detection table gets executed + UCHAR ucRefreshRateFactor; // [1:0]=RefreshFactor (00=8ms, 01=16ms, 10=32ms,11=64ms) + UCHAR ucFIFODepth; // FIFO depth supposes to be detected during vendor detection, but if we dont do vendor detection we have to hardcode FIFO Depth + UCHAR ucCDR_Bandwidth; // [0:3]=Read CDR bandwidth, [4:7] - Write CDR Bandwidth + ATOM_MEMORY_TIMING_FORMAT_V1 asMemTiming[5];//Memory Timing block sort from lower clock to higher clock +}ATOM_VRAM_MODULE_V5; + +typedef struct _ATOM_VRAM_INFO_V2 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + UCHAR ucNumOfVRAMModule; + ATOM_VRAM_MODULE aVramInfo[ATOM_MAX_NUMBER_OF_VRAM_MODULE]; // just for allocation, real number of blocks is in ucNumOfVRAMModule; +}ATOM_VRAM_INFO_V2; + +typedef struct _ATOM_VRAM_INFO_V3 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usMemAdjustTblOffset; // offset of ATOM_INIT_REG_BLOCK structure for memory vendor specific MC adjust setting + USHORT usMemClkPatchTblOffset; // offset of ATOM_INIT_REG_BLOCK structure for memory clock specific MC setting + USHORT usRerseved; + UCHAR aVID_PinsShift[9]; // 8 bit strap maximum+terminator + UCHAR ucNumOfVRAMModule; + ATOM_VRAM_MODULE aVramInfo[ATOM_MAX_NUMBER_OF_VRAM_MODULE]; // just for allocation, real number of blocks is in ucNumOfVRAMModule; + ATOM_INIT_REG_BLOCK asMemPatch; // for allocation + // ATOM_INIT_REG_BLOCK aMemAdjust; +}ATOM_VRAM_INFO_V3; + +#define ATOM_VRAM_INFO_LAST ATOM_VRAM_INFO_V3 + +typedef struct _ATOM_VRAM_INFO_V4 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usMemAdjustTblOffset; // offset of ATOM_INIT_REG_BLOCK structure for memory vendor specific MC adjust setting + USHORT usMemClkPatchTblOffset; // offset of ATOM_INIT_REG_BLOCK structure for memory clock specific MC setting + USHORT usRerseved; + UCHAR ucMemDQ7_0ByteRemap; // DQ line byte remap, =0: Memory Data line BYTE0, =1: BYTE1, =2: BYTE2, =3: BYTE3 + ULONG ulMemDQ7_0BitRemap; // each DQ line ( 7~0) use 3bits, like: DQ0=Bit[2:0], DQ1:[5:3], ... DQ7:[23:21] + UCHAR ucReservde[4]; + UCHAR ucNumOfVRAMModule; + ATOM_VRAM_MODULE_V4 aVramInfo[ATOM_MAX_NUMBER_OF_VRAM_MODULE]; // just for allocation, real number of blocks is in ucNumOfVRAMModule; + ATOM_INIT_REG_BLOCK asMemPatch; // for allocation + // ATOM_INIT_REG_BLOCK aMemAdjust; +}ATOM_VRAM_INFO_V4; + +typedef struct _ATOM_VRAM_GPIO_DETECTION_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + UCHAR aVID_PinsShift[9]; //8 bit strap maximum+terminator +}ATOM_VRAM_GPIO_DETECTION_INFO; + + +typedef struct _ATOM_MEMORY_TRAINING_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + UCHAR ucTrainingLoop; + UCHAR ucReserved[3]; + ATOM_INIT_REG_BLOCK asMemTrainingSetting; +}ATOM_MEMORY_TRAINING_INFO; + + +typedef struct SW_I2C_CNTL_DATA_PARAMETERS +{ + UCHAR ucControl; + UCHAR ucData; + UCHAR ucSatus; + UCHAR ucTemp; +} SW_I2C_CNTL_DATA_PARAMETERS; + +#define SW_I2C_CNTL_DATA_PS_ALLOCATION SW_I2C_CNTL_DATA_PARAMETERS + +typedef struct _SW_I2C_IO_DATA_PARAMETERS +{ + USHORT GPIO_Info; + UCHAR ucAct; + UCHAR ucData; + } SW_I2C_IO_DATA_PARAMETERS; + +#define SW_I2C_IO_DATA_PS_ALLOCATION SW_I2C_IO_DATA_PARAMETERS + +/****************************SW I2C CNTL DEFINITIONS**********************/ +#define SW_I2C_IO_RESET 0 +#define SW_I2C_IO_GET 1 +#define SW_I2C_IO_DRIVE 2 +#define SW_I2C_IO_SET 3 +#define SW_I2C_IO_START 4 + +#define SW_I2C_IO_CLOCK 0 +#define SW_I2C_IO_DATA 0x80 + +#define SW_I2C_IO_ZERO 0 +#define SW_I2C_IO_ONE 0x100 + +#define SW_I2C_CNTL_READ 0 +#define SW_I2C_CNTL_WRITE 1 +#define SW_I2C_CNTL_START 2 +#define SW_I2C_CNTL_STOP 3 +#define SW_I2C_CNTL_OPEN 4 +#define SW_I2C_CNTL_CLOSE 5 +#define SW_I2C_CNTL_WRITE1BIT 6 + +//==============================VESA definition Portion=============================== +#define VESA_OEM_PRODUCT_REV '01.00' +#define VESA_MODE_ATTRIBUTE_MODE_SUPPORT 0xBB //refer to VBE spec p.32, no TTY support +#define VESA_MODE_WIN_ATTRIBUTE 7 +#define VESA_WIN_SIZE 64 + +typedef struct _PTR_32_BIT_STRUCTURE +{ + USHORT Offset16; + USHORT Segment16; +} PTR_32_BIT_STRUCTURE; + +typedef union _PTR_32_BIT_UNION +{ + PTR_32_BIT_STRUCTURE SegmentOffset; + ULONG Ptr32_Bit; +} PTR_32_BIT_UNION; + +typedef struct _VBE_1_2_INFO_BLOCK_UPDATABLE +{ + UCHAR VbeSignature[4]; + USHORT VbeVersion; + PTR_32_BIT_UNION OemStringPtr; + UCHAR Capabilities[4]; + PTR_32_BIT_UNION VideoModePtr; + USHORT TotalMemory; +} VBE_1_2_INFO_BLOCK_UPDATABLE; + + +typedef struct _VBE_2_0_INFO_BLOCK_UPDATABLE +{ + VBE_1_2_INFO_BLOCK_UPDATABLE CommonBlock; + USHORT OemSoftRev; + PTR_32_BIT_UNION OemVendorNamePtr; + PTR_32_BIT_UNION OemProductNamePtr; + PTR_32_BIT_UNION OemProductRevPtr; +} VBE_2_0_INFO_BLOCK_UPDATABLE; + +typedef union _VBE_VERSION_UNION +{ + VBE_2_0_INFO_BLOCK_UPDATABLE VBE_2_0_InfoBlock; + VBE_1_2_INFO_BLOCK_UPDATABLE VBE_1_2_InfoBlock; +} VBE_VERSION_UNION; + +typedef struct _VBE_INFO_BLOCK +{ + VBE_VERSION_UNION UpdatableVBE_Info; + UCHAR Reserved[222]; + UCHAR OemData[256]; +} VBE_INFO_BLOCK; + +typedef struct _VBE_FP_INFO +{ + USHORT HSize; + USHORT VSize; + USHORT FPType; + UCHAR RedBPP; + UCHAR GreenBPP; + UCHAR BlueBPP; + UCHAR ReservedBPP; + ULONG RsvdOffScrnMemSize; + ULONG RsvdOffScrnMEmPtr; + UCHAR Reserved[14]; +} VBE_FP_INFO; + +typedef struct _VESA_MODE_INFO_BLOCK +{ +// Mandatory information for all VBE revisions + USHORT ModeAttributes; // dw ? ; mode attributes + UCHAR WinAAttributes; // db ? ; window A attributes + UCHAR WinBAttributes; // db ? ; window B attributes + USHORT WinGranularity; // dw ? ; window granularity + USHORT WinSize; // dw ? ; window size + USHORT WinASegment; // dw ? ; window A start segment + USHORT WinBSegment; // dw ? ; window B start segment + ULONG WinFuncPtr; // dd ? ; real mode pointer to window function + USHORT BytesPerScanLine;// dw ? ; bytes per scan line + +//; Mandatory information for VBE 1.2 and above + USHORT XResolution; // dw ? ; horizontal resolution in pixels or characters + USHORT YResolution; // dw ? ; vertical resolution in pixels or characters + UCHAR XCharSize; // db ? ; character cell width in pixels + UCHAR YCharSize; // db ? ; character cell height in pixels + UCHAR NumberOfPlanes; // db ? ; number of memory planes + UCHAR BitsPerPixel; // db ? ; bits per pixel + UCHAR NumberOfBanks; // db ? ; number of banks + UCHAR MemoryModel; // db ? ; memory model type + UCHAR BankSize; // db ? ; bank size in KB + UCHAR NumberOfImagePages;// db ? ; number of images + UCHAR ReservedForPageFunction;//db 1 ; reserved for page function + +//; Direct Color fields(required for direct/6 and YUV/7 memory models) + UCHAR RedMaskSize; // db ? ; size of direct color red mask in bits + UCHAR RedFieldPosition; // db ? ; bit position of lsb of red mask + UCHAR GreenMaskSize; // db ? ; size of direct color green mask in bits + UCHAR GreenFieldPosition; // db ? ; bit position of lsb of green mask + UCHAR BlueMaskSize; // db ? ; size of direct color blue mask in bits + UCHAR BlueFieldPosition; // db ? ; bit position of lsb of blue mask + UCHAR RsvdMaskSize; // db ? ; size of direct color reserved mask in bits + UCHAR RsvdFieldPosition; // db ? ; bit position of lsb of reserved mask + UCHAR DirectColorModeInfo;// db ? ; direct color mode attributes + +//; Mandatory information for VBE 2.0 and above + ULONG PhysBasePtr; // dd ? ; physical address for flat memory frame buffer + ULONG Reserved_1; // dd 0 ; reserved - always set to 0 + USHORT Reserved_2; // dw 0 ; reserved - always set to 0 + +//; Mandatory information for VBE 3.0 and above + USHORT LinBytesPerScanLine; // dw ? ; bytes per scan line for linear modes + UCHAR BnkNumberOfImagePages;// db ? ; number of images for banked modes + UCHAR LinNumberOfImagPages; // db ? ; number of images for linear modes + UCHAR LinRedMaskSize; // db ? ; size of direct color red mask(linear modes) + UCHAR LinRedFieldPosition; // db ? ; bit position of lsb of red mask(linear modes) + UCHAR LinGreenMaskSize; // db ? ; size of direct color green mask(linear modes) + UCHAR LinGreenFieldPosition;// db ? ; bit position of lsb of green mask(linear modes) + UCHAR LinBlueMaskSize; // db ? ; size of direct color blue mask(linear modes) + UCHAR LinBlueFieldPosition; // db ? ; bit position of lsb of blue mask(linear modes) + UCHAR LinRsvdMaskSize; // db ? ; size of direct color reserved mask(linear modes) + UCHAR LinRsvdFieldPosition; // db ? ; bit position of lsb of reserved mask(linear modes) + ULONG MaxPixelClock; // dd ? ; maximum pixel clock(in Hz) for graphics mode + UCHAR Reserved; // db 190 dup (0) +} VESA_MODE_INFO_BLOCK; + +// BIOS function CALLS +#define ATOM_BIOS_EXTENDED_FUNCTION_CODE 0xA0 // ATI Extended Function code +#define ATOM_BIOS_FUNCTION_COP_MODE 0x00 +#define ATOM_BIOS_FUNCTION_SHORT_QUERY1 0x04 +#define ATOM_BIOS_FUNCTION_SHORT_QUERY2 0x05 +#define ATOM_BIOS_FUNCTION_SHORT_QUERY3 0x06 +#define ATOM_BIOS_FUNCTION_GET_DDC 0x0B +#define ATOM_BIOS_FUNCTION_ASIC_DSTATE 0x0E +#define ATOM_BIOS_FUNCTION_DEBUG_PLAY 0x0F +#define ATOM_BIOS_FUNCTION_STV_STD 0x16 +#define ATOM_BIOS_FUNCTION_DEVICE_DET 0x17 +#define ATOM_BIOS_FUNCTION_DEVICE_SWITCH 0x18 + +#define ATOM_BIOS_FUNCTION_PANEL_CONTROL 0x82 +#define ATOM_BIOS_FUNCTION_OLD_DEVICE_DET 0x83 +#define ATOM_BIOS_FUNCTION_OLD_DEVICE_SWITCH 0x84 +#define ATOM_BIOS_FUNCTION_HW_ICON 0x8A +#define ATOM_BIOS_FUNCTION_SET_CMOS 0x8B +#define SUB_FUNCTION_UPDATE_DISPLAY_INFO 0x8000 // Sub function 80 +#define SUB_FUNCTION_UPDATE_EXPANSION_INFO 0x8100 // Sub function 80 + +#define ATOM_BIOS_FUNCTION_DISPLAY_INFO 0x8D +#define ATOM_BIOS_FUNCTION_DEVICE_ON_OFF 0x8E +#define ATOM_BIOS_FUNCTION_VIDEO_STATE 0x8F +#define ATOM_SUB_FUNCTION_GET_CRITICAL_STATE 0x0300 // Sub function 03 +#define ATOM_SUB_FUNCTION_GET_LIDSTATE 0x0700 // Sub function 7 +#define ATOM_SUB_FUNCTION_THERMAL_STATE_NOTICE 0x1400 // Notify caller the current thermal state +#define ATOM_SUB_FUNCTION_CRITICAL_STATE_NOTICE 0x8300 // Notify caller the current critical state +#define ATOM_SUB_FUNCTION_SET_LIDSTATE 0x8500 // Sub function 85 +#define ATOM_SUB_FUNCTION_GET_REQ_DISPLAY_FROM_SBIOS_MODE 0x8900// Sub function 89 +#define ATOM_SUB_FUNCTION_INFORM_ADC_SUPPORT 0x9400 // Notify caller that ADC is supported + + +#define ATOM_BIOS_FUNCTION_VESA_DPMS 0x4F10 // Set DPMS +#define ATOM_SUB_FUNCTION_SET_DPMS 0x0001 // BL: Sub function 01 +#define ATOM_SUB_FUNCTION_GET_DPMS 0x0002 // BL: Sub function 02 +#define ATOM_PARAMETER_VESA_DPMS_ON 0x0000 // BH Parameter for DPMS ON. +#define ATOM_PARAMETER_VESA_DPMS_STANDBY 0x0100 // BH Parameter for DPMS STANDBY +#define ATOM_PARAMETER_VESA_DPMS_SUSPEND 0x0200 // BH Parameter for DPMS SUSPEND +#define ATOM_PARAMETER_VESA_DPMS_OFF 0x0400 // BH Parameter for DPMS OFF +#define ATOM_PARAMETER_VESA_DPMS_REDUCE_ON 0x0800 // BH Parameter for DPMS REDUCE ON (NOT SUPPORTED) + +#define ATOM_BIOS_RETURN_CODE_MASK 0x0000FF00L +#define ATOM_BIOS_REG_HIGH_MASK 0x0000FF00L +#define ATOM_BIOS_REG_LOW_MASK 0x000000FFL + +// structure used for VBIOS only + +//DispOutInfoTable +typedef struct _ASIC_TRANSMITTER_INFO +{ + USHORT usTransmitterObjId; + USHORT usSupportDevice; + UCHAR ucTransmitterCmdTblId; + UCHAR ucConfig; + UCHAR ucEncoderID; //available 1st encoder ( default ) + UCHAR ucOptionEncoderID; //available 2nd encoder ( optional ) + UCHAR uc2ndEncoderID; + UCHAR ucReserved; +}ASIC_TRANSMITTER_INFO; + +typedef struct _ASIC_ENCODER_INFO +{ + UCHAR ucEncoderID; + UCHAR ucEncoderConfig; + USHORT usEncoderCmdTblId; +}ASIC_ENCODER_INFO; + +typedef struct _ATOM_DISP_OUT_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT ptrTransmitterInfo; + USHORT ptrEncoderInfo; + ASIC_TRANSMITTER_INFO asTransmitterInfo[1]; + ASIC_ENCODER_INFO asEncoderInfo[1]; +}ATOM_DISP_OUT_INFO; + +// DispDevicePriorityInfo +typedef struct _ATOM_DISPLAY_DEVICE_PRIORITY_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT asDevicePriority[16]; +}ATOM_DISPLAY_DEVICE_PRIORITY_INFO; + +//ProcessAuxChannelTransactionTable +typedef struct _PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS +{ + USHORT lpAuxRequest; + USHORT lpDataOut; + UCHAR ucChannelID; + union + { + UCHAR ucReplyStatus; + UCHAR ucDelay; + }; + UCHAR ucDataOutLen; + UCHAR ucReserved; +}PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS; + +#define PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS + +//GetSinkType + +typedef struct _DP_ENCODER_SERVICE_PARAMETERS +{ + USHORT ucLinkClock; + union + { + UCHAR ucConfig; // for DP training command + UCHAR ucI2cId; // use for GET_SINK_TYPE command + }; + UCHAR ucAction; + UCHAR ucStatus; + UCHAR ucLaneNum; + UCHAR ucReserved[2]; +}DP_ENCODER_SERVICE_PARAMETERS; + +// ucAction +#define ATOM_DP_ACTION_GET_SINK_TYPE 0x01 +#define ATOM_DP_ACTION_TRAINING_START 0x02 +#define ATOM_DP_ACTION_TRAINING_COMPLETE 0x03 +#define ATOM_DP_ACTION_TRAINING_PATTERN_SEL 0x04 +#define ATOM_DP_ACTION_SET_VSWING_PREEMP 0x05 +#define ATOM_DP_ACTION_GET_VSWING_PREEMP 0x06 +#define ATOM_DP_ACTION_BLANKING 0x07 + +// ucConfig +#define ATOM_DP_CONFIG_ENCODER_SEL_MASK 0x03 +#define ATOM_DP_CONFIG_DIG1_ENCODER 0x00 +#define ATOM_DP_CONFIG_DIG2_ENCODER 0x01 +#define ATOM_DP_CONFIG_EXTERNAL_ENCODER 0x02 +#define ATOM_DP_CONFIG_LINK_SEL_MASK 0x04 +#define ATOM_DP_CONFIG_LINK_A 0x00 +#define ATOM_DP_CONFIG_LINK_B 0x04 + +#define DP_ENCODER_SERVICE_PS_ALLOCATION WRITE_ONE_BYTE_HW_I2C_DATA_PARAMETERS + +// DP_TRAINING_TABLE +#define DPCD_SET_LINKRATE_LANENUM_PATTERN1_TBL_ADDR ATOM_DP_TRAINING_TBL_ADDR +#define DPCD_SET_SS_CNTL_TBL_ADDR (ATOM_DP_TRAINING_TBL_ADDR + 8 ) +#define DPCD_SET_LANE_VSWING_PREEMP_TBL_ADDR (ATOM_DP_TRAINING_TBL_ADDR + 16 ) +#define DPCD_SET_TRAINING_PATTERN0_TBL_ADDR (ATOM_DP_TRAINING_TBL_ADDR + 24 ) +#define DPCD_SET_TRAINING_PATTERN2_TBL_ADDR (ATOM_DP_TRAINING_TBL_ADDR + 32) +#define DPCD_GET_LINKRATE_LANENUM_SS_TBL_ADDR (ATOM_DP_TRAINING_TBL_ADDR + 40) +#define DPCD_GET_LANE_STATUS_ADJUST_TBL_ADDR (ATOM_DP_TRAINING_TBL_ADDR + 48) +#define DP_I2C_AUX_DDC_WRITE_START_TBL_ADDR (ATOM_DP_TRAINING_TBL_ADDR + 60) +#define DP_I2C_AUX_DDC_WRITE_TBL_ADDR (ATOM_DP_TRAINING_TBL_ADDR + 64) +#define DP_I2C_AUX_DDC_READ_START_TBL_ADDR (ATOM_DP_TRAINING_TBL_ADDR + 72) +#define DP_I2C_AUX_DDC_READ_TBL_ADDR (ATOM_DP_TRAINING_TBL_ADDR + 76) +#define DP_I2C_AUX_DDC_READ_END_TBL_ADDR (ATOM_DP_TRAINING_TBL_ADDR + 80) + + +typedef struct _PROCESS_I2C_CHANNEL_TRANSACTION_PARAMETERS +{ + UCHAR ucI2CSpeed; + union + { + UCHAR ucRegIndex; + UCHAR ucStatus; + }; + USHORT lpI2CDataOut; + UCHAR ucFlag; + UCHAR ucTransBytes; + UCHAR ucSlaveAddr; + UCHAR ucLineNumber; +}PROCESS_I2C_CHANNEL_TRANSACTION_PARAMETERS; + +#define PROCESS_I2C_CHANNEL_TRANSACTION_PS_ALLOCATION PROCESS_I2C_CHANNEL_TRANSACTION_PARAMETERS + +//ucFlag +#define HW_I2C_WRITE 1 +#define HW_I2C_READ 0 + + +/****************************************************************************/ +//Portion VI: Definitinos being oboselete +/****************************************************************************/ + +//========================================================================================== +//Remove the definitions below when driver is ready! +typedef struct _ATOM_DAC_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usMaxFrequency; // in 10kHz unit + USHORT usReserved; +}ATOM_DAC_INFO; + + +typedef struct _COMPASSIONATE_DATA +{ + ATOM_COMMON_TABLE_HEADER sHeader; + + //============================== DAC1 portion + UCHAR ucDAC1_BG_Adjustment; + UCHAR ucDAC1_DAC_Adjustment; + USHORT usDAC1_FORCE_Data; + //============================== DAC2 portion + UCHAR ucDAC2_CRT2_BG_Adjustment; + UCHAR ucDAC2_CRT2_DAC_Adjustment; + USHORT usDAC2_CRT2_FORCE_Data; + USHORT usDAC2_CRT2_MUX_RegisterIndex; + UCHAR ucDAC2_CRT2_MUX_RegisterInfo; //Bit[4:0]=Bit position,Bit[7]=1:Active High;=0 Active Low + UCHAR ucDAC2_NTSC_BG_Adjustment; + UCHAR ucDAC2_NTSC_DAC_Adjustment; + USHORT usDAC2_TV1_FORCE_Data; + USHORT usDAC2_TV1_MUX_RegisterIndex; + UCHAR ucDAC2_TV1_MUX_RegisterInfo; //Bit[4:0]=Bit position,Bit[7]=1:Active High;=0 Active Low + UCHAR ucDAC2_CV_BG_Adjustment; + UCHAR ucDAC2_CV_DAC_Adjustment; + USHORT usDAC2_CV_FORCE_Data; + USHORT usDAC2_CV_MUX_RegisterIndex; + UCHAR ucDAC2_CV_MUX_RegisterInfo; //Bit[4:0]=Bit position,Bit[7]=1:Active High;=0 Active Low + UCHAR ucDAC2_PAL_BG_Adjustment; + UCHAR ucDAC2_PAL_DAC_Adjustment; + USHORT usDAC2_TV2_FORCE_Data; +}COMPASSIONATE_DATA; + +/****************************Supported Device Info Table Definitions**********************/ +// ucConnectInfo: +// [7:4] - connector type +// = 1 - VGA connector +// = 2 - DVI-I +// = 3 - DVI-D +// = 4 - DVI-A +// = 5 - SVIDEO +// = 6 - COMPOSITE +// = 7 - LVDS +// = 8 - DIGITAL LINK +// = 9 - SCART +// = 0xA - HDMI_type A +// = 0xB - HDMI_type B +// = 0xE - Special case1 (DVI+DIN) +// Others=TBD +// [3:0] - DAC Associated +// = 0 - no DAC +// = 1 - DACA +// = 2 - DACB +// = 3 - External DAC +// Others=TBD +// + +typedef struct _ATOM_CONNECTOR_INFO +{ +#if ATOM_BIG_ENDIAN + UCHAR bfConnectorType:4; + UCHAR bfAssociatedDAC:4; +#else + UCHAR bfAssociatedDAC:4; + UCHAR bfConnectorType:4; +#endif +}ATOM_CONNECTOR_INFO; + +typedef union _ATOM_CONNECTOR_INFO_ACCESS +{ + ATOM_CONNECTOR_INFO sbfAccess; + UCHAR ucAccess; +}ATOM_CONNECTOR_INFO_ACCESS; + +typedef struct _ATOM_CONNECTOR_INFO_I2C +{ + ATOM_CONNECTOR_INFO_ACCESS sucConnectorInfo; + ATOM_I2C_ID_CONFIG_ACCESS sucI2cId; +}ATOM_CONNECTOR_INFO_I2C; + + +typedef struct _ATOM_SUPPORTED_DEVICES_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usDeviceSupport; + ATOM_CONNECTOR_INFO_I2C asConnInfo[ATOM_MAX_SUPPORTED_DEVICE_INFO]; +}ATOM_SUPPORTED_DEVICES_INFO; + +#define NO_INT_SRC_MAPPED 0xFF + +typedef struct _ATOM_CONNECTOR_INC_SRC_BITMAP +{ + UCHAR ucIntSrcBitmap; +}ATOM_CONNECTOR_INC_SRC_BITMAP; + +typedef struct _ATOM_SUPPORTED_DEVICES_INFO_2 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usDeviceSupport; + ATOM_CONNECTOR_INFO_I2C asConnInfo[ATOM_MAX_SUPPORTED_DEVICE_INFO_2]; + ATOM_CONNECTOR_INC_SRC_BITMAP asIntSrcInfo[ATOM_MAX_SUPPORTED_DEVICE_INFO_2]; +}ATOM_SUPPORTED_DEVICES_INFO_2; + +typedef struct _ATOM_SUPPORTED_DEVICES_INFO_2d1 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usDeviceSupport; + ATOM_CONNECTOR_INFO_I2C asConnInfo[ATOM_MAX_SUPPORTED_DEVICE]; + ATOM_CONNECTOR_INC_SRC_BITMAP asIntSrcInfo[ATOM_MAX_SUPPORTED_DEVICE]; +}ATOM_SUPPORTED_DEVICES_INFO_2d1; + +#define ATOM_SUPPORTED_DEVICES_INFO_LAST ATOM_SUPPORTED_DEVICES_INFO_2d1 + + + +typedef struct _ATOM_MISC_CONTROL_INFO +{ + USHORT usFrequency; + UCHAR ucPLL_ChargePump; // PLL charge-pump gain control + UCHAR ucPLL_DutyCycle; // PLL duty cycle control + UCHAR ucPLL_VCO_Gain; // PLL VCO gain control + UCHAR ucPLL_VoltageSwing; // PLL driver voltage swing control +}ATOM_MISC_CONTROL_INFO; + + +#define ATOM_MAX_MISC_INFO 4 + +typedef struct _ATOM_TMDS_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usMaxFrequency; // in 10Khz + ATOM_MISC_CONTROL_INFO asMiscInfo[ATOM_MAX_MISC_INFO]; +}ATOM_TMDS_INFO; + + +typedef struct _ATOM_ENCODER_ANALOG_ATTRIBUTE +{ + UCHAR ucTVStandard; //Same as TV standards defined above, + UCHAR ucPadding[1]; +}ATOM_ENCODER_ANALOG_ATTRIBUTE; + +typedef struct _ATOM_ENCODER_DIGITAL_ATTRIBUTE +{ + UCHAR ucAttribute; //Same as other digital encoder attributes defined above + UCHAR ucPadding[1]; +}ATOM_ENCODER_DIGITAL_ATTRIBUTE; + +typedef union _ATOM_ENCODER_ATTRIBUTE +{ + ATOM_ENCODER_ANALOG_ATTRIBUTE sAlgAttrib; + ATOM_ENCODER_DIGITAL_ATTRIBUTE sDigAttrib; +}ATOM_ENCODER_ATTRIBUTE; + + +typedef struct _DVO_ENCODER_CONTROL_PARAMETERS +{ + USHORT usPixelClock; + USHORT usEncoderID; + UCHAR ucDeviceType; //Use ATOM_DEVICE_xxx1_Index to indicate device type only. + UCHAR ucAction; //ATOM_ENABLE/ATOM_DISABLE/ATOM_HPD_INIT + ATOM_ENCODER_ATTRIBUTE usDevAttr; +}DVO_ENCODER_CONTROL_PARAMETERS; + +typedef struct _DVO_ENCODER_CONTROL_PS_ALLOCATION +{ + DVO_ENCODER_CONTROL_PARAMETERS sDVOEncoder; + WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION sReserved; //Caller doesn't need to init this portion +}DVO_ENCODER_CONTROL_PS_ALLOCATION; + + +#define ATOM_XTMDS_ASIC_SI164_ID 1 +#define ATOM_XTMDS_ASIC_SI178_ID 2 +#define ATOM_XTMDS_ASIC_TFP513_ID 3 +#define ATOM_XTMDS_SUPPORTED_SINGLELINK 0x00000001 +#define ATOM_XTMDS_SUPPORTED_DUALLINK 0x00000002 +#define ATOM_XTMDS_MVPU_FPGA 0x00000004 + + +typedef struct _ATOM_XTMDS_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usSingleLinkMaxFrequency; + ATOM_I2C_ID_CONFIG_ACCESS sucI2cId; //Point the ID on which I2C is used to control external chip + UCHAR ucXtransimitterID; + UCHAR ucSupportedLink; // Bit field, bit0=1, single link supported;bit1=1,dual link supported + UCHAR ucSequnceAlterID; // Even with the same external TMDS asic, it's possible that the program seqence alters + // due to design. This ID is used to alert driver that the sequence is not "standard"! + UCHAR ucMasterAddress; // Address to control Master xTMDS Chip + UCHAR ucSlaveAddress; // Address to control Slave xTMDS Chip +}ATOM_XTMDS_INFO; + +typedef struct _DFP_DPMS_STATUS_CHANGE_PARAMETERS +{ + UCHAR ucEnable; // ATOM_ENABLE=On or ATOM_DISABLE=Off + UCHAR ucDevice; // ATOM_DEVICE_DFP1_INDEX.... + UCHAR ucPadding[2]; +}DFP_DPMS_STATUS_CHANGE_PARAMETERS; + +/****************************Legacy Power Play Table Definitions **********************/ + +//Definitions for ulPowerPlayMiscInfo +#define ATOM_PM_MISCINFO_SPLIT_CLOCK 0x00000000L +#define ATOM_PM_MISCINFO_USING_MCLK_SRC 0x00000001L +#define ATOM_PM_MISCINFO_USING_SCLK_SRC 0x00000002L + +#define ATOM_PM_MISCINFO_VOLTAGE_DROP_SUPPORT 0x00000004L +#define ATOM_PM_MISCINFO_VOLTAGE_DROP_ACTIVE_HIGH 0x00000008L + +#define ATOM_PM_MISCINFO_LOAD_PERFORMANCE_EN 0x00000010L + +#define ATOM_PM_MISCINFO_ENGINE_CLOCK_CONTRL_EN 0x00000020L +#define ATOM_PM_MISCINFO_MEMORY_CLOCK_CONTRL_EN 0x00000040L +#define ATOM_PM_MISCINFO_PROGRAM_VOLTAGE 0x00000080L //When this bit set, ucVoltageDropIndex is not an index for GPIO pin, but a voltage ID that SW needs program + +#define ATOM_PM_MISCINFO_ASIC_REDUCED_SPEED_SCLK_EN 0x00000100L +#define ATOM_PM_MISCINFO_ASIC_DYNAMIC_VOLTAGE_EN 0x00000200L +#define ATOM_PM_MISCINFO_ASIC_SLEEP_MODE_EN 0x00000400L +#define ATOM_PM_MISCINFO_LOAD_BALANCE_EN 0x00000800L +#define ATOM_PM_MISCINFO_DEFAULT_DC_STATE_ENTRY_TRUE 0x00001000L +#define ATOM_PM_MISCINFO_DEFAULT_LOW_DC_STATE_ENTRY_TRUE 0x00002000L +#define ATOM_PM_MISCINFO_LOW_LCD_REFRESH_RATE 0x00004000L + +#define ATOM_PM_MISCINFO_DRIVER_DEFAULT_MODE 0x00008000L +#define ATOM_PM_MISCINFO_OVER_CLOCK_MODE 0x00010000L +#define ATOM_PM_MISCINFO_OVER_DRIVE_MODE 0x00020000L +#define ATOM_PM_MISCINFO_POWER_SAVING_MODE 0x00040000L +#define ATOM_PM_MISCINFO_THERMAL_DIODE_MODE 0x00080000L + +#define ATOM_PM_MISCINFO_FRAME_MODULATION_MASK 0x00300000L //0-FM Disable, 1-2 level FM, 2-4 level FM, 3-Reserved +#define ATOM_PM_MISCINFO_FRAME_MODULATION_SHIFT 20 + +#define ATOM_PM_MISCINFO_DYN_CLK_3D_IDLE 0x00400000L +#define ATOM_PM_MISCINFO_DYNAMIC_CLOCK_DIVIDER_BY_2 0x00800000L +#define ATOM_PM_MISCINFO_DYNAMIC_CLOCK_DIVIDER_BY_4 0x01000000L +#define ATOM_PM_MISCINFO_DYNAMIC_HDP_BLOCK_EN 0x02000000L //When set, Dynamic +#define ATOM_PM_MISCINFO_DYNAMIC_MC_HOST_BLOCK_EN 0x04000000L //When set, Dynamic +#define ATOM_PM_MISCINFO_3D_ACCELERATION_EN 0x08000000L //When set, This mode is for acceleated 3D mode + +#define ATOM_PM_MISCINFO_POWERPLAY_SETTINGS_GROUP_MASK 0x70000000L //1-Optimal Battery Life Group, 2-High Battery, 3-Balanced, 4-High Performance, 5- Optimal Performance (Default state with Default clocks) +#define ATOM_PM_MISCINFO_POWERPLAY_SETTINGS_GROUP_SHIFT 28 +#define ATOM_PM_MISCINFO_ENABLE_BACK_BIAS 0x80000000L + +#define ATOM_PM_MISCINFO2_SYSTEM_AC_LITE_MODE 0x00000001L +#define ATOM_PM_MISCINFO2_MULTI_DISPLAY_SUPPORT 0x00000002L +#define ATOM_PM_MISCINFO2_DYNAMIC_BACK_BIAS_EN 0x00000004L +#define ATOM_PM_MISCINFO2_FS3D_OVERDRIVE_INFO 0x00000008L +#define ATOM_PM_MISCINFO2_FORCEDLOWPWR_MODE 0x00000010L +#define ATOM_PM_MISCINFO2_VDDCI_DYNAMIC_VOLTAGE_EN 0x00000020L +#define ATOM_PM_MISCINFO2_VIDEO_PLAYBACK_CAPABLE 0x00000040L //If this bit is set in multi-pp mode, then driver will pack up one with the minior power consumption. + //If it's not set in any pp mode, driver will use its default logic to pick a pp mode in video playback +#define ATOM_PM_MISCINFO2_NOT_VALID_ON_DC 0x00000080L +#define ATOM_PM_MISCINFO2_STUTTER_MODE_EN 0x00000100L +#define ATOM_PM_MISCINFO2_UVD_SUPPORT_MODE 0x00000200L + +//ucTableFormatRevision=1 +//ucTableContentRevision=1 +typedef struct _ATOM_POWERMODE_INFO +{ + ULONG ulMiscInfo; //The power level should be arranged in ascending order + ULONG ulReserved1; // must set to 0 + ULONG ulReserved2; // must set to 0 + USHORT usEngineClock; + USHORT usMemoryClock; + UCHAR ucVoltageDropIndex; // index to GPIO table + UCHAR ucSelectedPanel_RefreshRate;// panel refresh rate + UCHAR ucMinTemperature; + UCHAR ucMaxTemperature; + UCHAR ucNumPciELanes; // number of PCIE lanes +}ATOM_POWERMODE_INFO; + +//ucTableFormatRevision=2 +//ucTableContentRevision=1 +typedef struct _ATOM_POWERMODE_INFO_V2 +{ + ULONG ulMiscInfo; //The power level should be arranged in ascending order + ULONG ulMiscInfo2; + ULONG ulEngineClock; + ULONG ulMemoryClock; + UCHAR ucVoltageDropIndex; // index to GPIO table + UCHAR ucSelectedPanel_RefreshRate;// panel refresh rate + UCHAR ucMinTemperature; + UCHAR ucMaxTemperature; + UCHAR ucNumPciELanes; // number of PCIE lanes +}ATOM_POWERMODE_INFO_V2; + +//ucTableFormatRevision=2 +//ucTableContentRevision=2 +typedef struct _ATOM_POWERMODE_INFO_V3 +{ + ULONG ulMiscInfo; //The power level should be arranged in ascending order + ULONG ulMiscInfo2; + ULONG ulEngineClock; + ULONG ulMemoryClock; + UCHAR ucVoltageDropIndex; // index to Core (VDDC) votage table + UCHAR ucSelectedPanel_RefreshRate;// panel refresh rate + UCHAR ucMinTemperature; + UCHAR ucMaxTemperature; + UCHAR ucNumPciELanes; // number of PCIE lanes + UCHAR ucVDDCI_VoltageDropIndex; // index to VDDCI votage table +}ATOM_POWERMODE_INFO_V3; + + +#define ATOM_MAX_NUMBEROF_POWER_BLOCK 8 + +#define ATOM_PP_OVERDRIVE_INTBITMAP_AUXWIN 0x01 +#define ATOM_PP_OVERDRIVE_INTBITMAP_OVERDRIVE 0x02 + +#define ATOM_PP_OVERDRIVE_THERMALCONTROLLER_LM63 0x01 +#define ATOM_PP_OVERDRIVE_THERMALCONTROLLER_ADM1032 0x02 +#define ATOM_PP_OVERDRIVE_THERMALCONTROLLER_ADM1030 0x03 +#define ATOM_PP_OVERDRIVE_THERMALCONTROLLER_MUA6649 0x04 +#define ATOM_PP_OVERDRIVE_THERMALCONTROLLER_LM64 0x05 +#define ATOM_PP_OVERDRIVE_THERMALCONTROLLER_F75375 0x06 +#define ATOM_PP_OVERDRIVE_THERMALCONTROLLER_ASC7512 0x07 // Andigilog + + +typedef struct _ATOM_POWERPLAY_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + UCHAR ucOverdriveThermalController; + UCHAR ucOverdriveI2cLine; + UCHAR ucOverdriveIntBitmap; + UCHAR ucOverdriveControllerAddress; + UCHAR ucSizeOfPowerModeEntry; + UCHAR ucNumOfPowerModeEntries; + ATOM_POWERMODE_INFO asPowerPlayInfo[ATOM_MAX_NUMBEROF_POWER_BLOCK]; +}ATOM_POWERPLAY_INFO; + +typedef struct _ATOM_POWERPLAY_INFO_V2 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + UCHAR ucOverdriveThermalController; + UCHAR ucOverdriveI2cLine; + UCHAR ucOverdriveIntBitmap; + UCHAR ucOverdriveControllerAddress; + UCHAR ucSizeOfPowerModeEntry; + UCHAR ucNumOfPowerModeEntries; + ATOM_POWERMODE_INFO_V2 asPowerPlayInfo[ATOM_MAX_NUMBEROF_POWER_BLOCK]; +}ATOM_POWERPLAY_INFO_V2; + +typedef struct _ATOM_POWERPLAY_INFO_V3 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + UCHAR ucOverdriveThermalController; + UCHAR ucOverdriveI2cLine; + UCHAR ucOverdriveIntBitmap; + UCHAR ucOverdriveControllerAddress; + UCHAR ucSizeOfPowerModeEntry; + UCHAR ucNumOfPowerModeEntries; + ATOM_POWERMODE_INFO_V3 asPowerPlayInfo[ATOM_MAX_NUMBEROF_POWER_BLOCK]; +}ATOM_POWERPLAY_INFO_V3; + + + +/**************************************************************************/ + + +// Following definitions are for compatiblity issue in different SW components. +#define ATOM_MASTER_DATA_TABLE_REVISION 0x01 +#define Object_Info Object_Header +#define AdjustARB_SEQ MC_InitParameter +#define VRAM_GPIO_DetectionInfo VoltageObjectInfo +#define ASIC_VDDCI_Info ASIC_ProfilingInfo +#define ASIC_MVDDQ_Info MemoryTrainingInfo +#define SS_Info PPLL_SS_Info +#define ASIC_MVDDC_Info ASIC_InternalSS_Info +#define DispDevicePriorityInfo SaveRestoreInfo +#define DispOutInfo TV_VideoMode + + +#define ATOM_ENCODER_OBJECT_TABLE ATOM_OBJECT_TABLE +#define ATOM_CONNECTOR_OBJECT_TABLE ATOM_OBJECT_TABLE + +//New device naming, remove them when both DAL/VBIOS is ready +#define DFP2I_OUTPUT_CONTROL_PARAMETERS CRT1_OUTPUT_CONTROL_PARAMETERS +#define DFP2I_OUTPUT_CONTROL_PS_ALLOCATION DFP2I_OUTPUT_CONTROL_PARAMETERS + +#define DFP1X_OUTPUT_CONTROL_PARAMETERS CRT1_OUTPUT_CONTROL_PARAMETERS +#define DFP1X_OUTPUT_CONTROL_PS_ALLOCATION DFP1X_OUTPUT_CONTROL_PARAMETERS + +#define DFP1I_OUTPUT_CONTROL_PARAMETERS DFP1_OUTPUT_CONTROL_PARAMETERS +#define DFP1I_OUTPUT_CONTROL_PS_ALLOCATION DFP1_OUTPUT_CONTROL_PS_ALLOCATION + +#define ATOM_DEVICE_DFP1I_SUPPORT ATOM_DEVICE_DFP1_SUPPORT +#define ATOM_DEVICE_DFP1X_SUPPORT ATOM_DEVICE_DFP2_SUPPORT + +#define ATOM_DEVICE_DFP1I_INDEX ATOM_DEVICE_DFP1_INDEX +#define ATOM_DEVICE_DFP1X_INDEX ATOM_DEVICE_DFP2_INDEX + +#define ATOM_DEVICE_DFP2I_INDEX 0x00000009 +#define ATOM_DEVICE_DFP2I_SUPPORT (0x1L << ATOM_DEVICE_DFP2I_INDEX) + +#define ATOM_S0_DFP1I ATOM_S0_DFP1 +#define ATOM_S0_DFP1X ATOM_S0_DFP2 + +#define ATOM_S0_DFP2I 0x00200000L +#define ATOM_S0_DFP2Ib2 0x20 + +#define ATOM_S2_DFP1I_DPMS_STATE ATOM_S2_DFP1_DPMS_STATE +#define ATOM_S2_DFP1X_DPMS_STATE ATOM_S2_DFP2_DPMS_STATE + +#define ATOM_S2_DFP2I_DPMS_STATE 0x02000000L +#define ATOM_S2_DFP2I_DPMS_STATEb3 0x02 + +#define ATOM_S3_DFP2I_ACTIVEb1 0x02 + +#define ATOM_S3_DFP1I_ACTIVE ATOM_S3_DFP1_ACTIVE +#define ATOM_S3_DFP1X_ACTIVE ATOM_S3_DFP2_ACTIVE + +#define ATOM_S3_DFP2I_ACTIVE 0x00000200L + +#define ATOM_S3_DFP1I_CRTC_ACTIVE ATOM_S3_DFP1_CRTC_ACTIVE +#define ATOM_S3_DFP1X_CRTC_ACTIVE ATOM_S3_DFP2_CRTC_ACTIVE +#define ATOM_S3_DFP2I_CRTC_ACTIVE 0x02000000L + +#define ATOM_S3_DFP2I_CRTC_ACTIVEb3 0x02 +#define ATOM_S5_DOS_REQ_DFP2Ib1 0x02 + +#define ATOM_S5_DOS_REQ_DFP2I 0x0200 +#define ATOM_S6_ACC_REQ_DFP1I ATOM_S6_ACC_REQ_DFP1 +#define ATOM_S6_ACC_REQ_DFP1X ATOM_S6_ACC_REQ_DFP2 + +#define ATOM_S6_ACC_REQ_DFP2Ib3 0x02 +#define ATOM_S6_ACC_REQ_DFP2I 0x02000000L + +#define TMDS1XEncoderControl DVOEncoderControl +#define DFP1XOutputControl DVOOutputControl + +#define ExternalDFPOutputControl DFP1XOutputControl +#define EnableExternalTMDS_Encoder TMDS1XEncoderControl + +#define DFP1IOutputControl TMDSAOutputControl +#define DFP2IOutputControl LVTMAOutputControl + +#define DAC1_ENCODER_CONTROL_PARAMETERS DAC_ENCODER_CONTROL_PARAMETERS +#define DAC1_ENCODER_CONTROL_PS_ALLOCATION DAC_ENCODER_CONTROL_PS_ALLOCATION + +#define DAC2_ENCODER_CONTROL_PARAMETERS DAC_ENCODER_CONTROL_PARAMETERS +#define DAC2_ENCODER_CONTROL_PS_ALLOCATION DAC_ENCODER_CONTROL_PS_ALLOCATION + +#define ucDac1Standard ucDacStandard +#define ucDac2Standard ucDacStandard + +#define TMDS1EncoderControl TMDSAEncoderControl +#define TMDS2EncoderControl LVTMAEncoderControl + +#define DFP1OutputControl TMDSAOutputControl +#define DFP2OutputControl LVTMAOutputControl +#define CRT1OutputControl DAC1OutputControl +#define CRT2OutputControl DAC2OutputControl + +//These two lines will be removed for sure in a few days, will follow up with Michael V. +#define EnableLVDS_SS EnableSpreadSpectrumOnPPLL +#define ENABLE_LVDS_SS_PARAMETERS_V3 ENABLE_SPREAD_SPECTRUM_ON_PPLL + +/*********************************************************************************/ + +#pragma pack() // BIOS data must use byte aligment + +#endif /* _ATOMBIOS_H */ diff --git a/linux-core/atombios_crtc.c b/linux-core/atombios_crtc.c new file mode 100644 index 00000000..2e144c90 --- /dev/null +++ b/linux-core/atombios_crtc.c @@ -0,0 +1,462 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat 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. + * + * Authors: Dave Airlie + * Alex Deucher + */ +#include "drmP.h" +#include "radeon_drm.h" +#include "radeon_drv.h" + +#include "drm_crtc_helper.h" +#include "atom.h" +#include "atom-bits.h" + +static void atombios_lock_crtc(struct drm_crtc *crtc, int lock) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + int index = GetIndexIntoMasterTable(COMMAND, UpdateCRTC_DoubleBufferRegisters); + ENABLE_CRTC_PS_ALLOCATION args; + + memset(&args, 0, sizeof(args)); + + args.ucCRTC = radeon_crtc->crtc_id; + args.ucEnable = lock; + + atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); +} + +static void atombios_enable_crtc(struct drm_crtc *crtc, int state) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + int index = GetIndexIntoMasterTable(COMMAND, EnableCRTC); + ENABLE_CRTC_PS_ALLOCATION args; + + memset(&args, 0, sizeof(args)); + + args.ucCRTC = radeon_crtc->crtc_id; + args.ucEnable = state; + + atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); +} + +static void atombios_enable_crtc_memreq(struct drm_crtc *crtc, int state) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + int index = GetIndexIntoMasterTable(COMMAND, EnableCRTCMemReq); + ENABLE_CRTC_PS_ALLOCATION args; + + memset(&args, 0, sizeof(args)); + + args.ucCRTC = radeon_crtc->crtc_id; + args.ucEnable = state; + + atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); +} + +static void atombios_blank_crtc(struct drm_crtc *crtc, int state) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + int index = GetIndexIntoMasterTable(COMMAND, BlankCRTC); + BLANK_CRTC_PS_ALLOCATION args; + + memset(&args, 0, sizeof(args)); + + args.ucCRTC = radeon_crtc->crtc_id; + args.ucBlanking = state; + + atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); +} + +void atombios_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + + switch(mode) { + case DRM_MODE_DPMS_ON: + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + if (radeon_is_dce3(dev_priv)) + atombios_enable_crtc_memreq(crtc, 1); + atombios_enable_crtc(crtc, 1); + atombios_blank_crtc(crtc, 0); + + radeon_crtc_load_lut(crtc); + break; + case DRM_MODE_DPMS_OFF: + atombios_blank_crtc(crtc, 1); + atombios_enable_crtc(crtc, 0); + if (radeon_is_dce3(dev_priv)) + atombios_enable_crtc_memreq(crtc, 0); + break; + } +} + +static void +atombios_set_crtc_dtd_timing(struct drm_crtc *crtc, SET_CRTC_USING_DTD_TIMING_PARAMETERS *crtc_param) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + SET_CRTC_USING_DTD_TIMING_PARAMETERS conv_param; + int index = GetIndexIntoMasterTable(COMMAND, SetCRTC_UsingDTDTiming); + + conv_param.usH_Size = cpu_to_le16(crtc_param->usH_Size); + conv_param.usH_Blanking_Time = cpu_to_le16(crtc_param->usH_Blanking_Time); + conv_param.usV_Size = cpu_to_le16(crtc_param->usV_Size); + conv_param.usV_Blanking_Time = cpu_to_le16(crtc_param->usV_Blanking_Time); + conv_param.usH_SyncOffset = cpu_to_le16(crtc_param->usH_SyncOffset); + conv_param.usH_SyncWidth = cpu_to_le16(crtc_param->usH_SyncWidth); + conv_param.usV_SyncOffset = cpu_to_le16(crtc_param->usV_SyncOffset); + conv_param.usV_SyncWidth = cpu_to_le16(crtc_param->usV_SyncWidth); + conv_param.susModeMiscInfo.usAccess = cpu_to_le16(crtc_param->susModeMiscInfo.usAccess); + conv_param.ucCRTC = crtc_param->ucCRTC; + + printk("executing set crtc dtd timing\n"); + atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&conv_param); +} + +void atombios_crtc_set_timing(struct drm_crtc *crtc, SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION *crtc_param) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION conv_param; + int index = GetIndexIntoMasterTable(COMMAND, SetCRTC_Timing); + + conv_param.usH_Total = cpu_to_le16(crtc_param->usH_Total); + conv_param.usH_Disp = cpu_to_le16(crtc_param->usH_Disp); + conv_param.usH_SyncStart = cpu_to_le16(crtc_param->usH_SyncStart); + conv_param.usH_SyncWidth = cpu_to_le16(crtc_param->usH_SyncWidth); + conv_param.usV_Total = cpu_to_le16(crtc_param->usV_Total); + conv_param.usV_Disp = cpu_to_le16(crtc_param->usV_Disp); + conv_param.usV_SyncStart = cpu_to_le16(crtc_param->usV_SyncStart); + conv_param.usV_SyncWidth = cpu_to_le16(crtc_param->usV_SyncWidth); + conv_param.susModeMiscInfo.usAccess = cpu_to_le16(crtc_param->susModeMiscInfo.usAccess); + conv_param.ucCRTC = crtc_param->ucCRTC; + conv_param.ucOverscanRight = crtc_param->ucOverscanRight; + conv_param.ucOverscanLeft = crtc_param->ucOverscanLeft; + conv_param.ucOverscanBottom = crtc_param->ucOverscanBottom; + conv_param.ucOverscanTop = crtc_param->ucOverscanTop; + conv_param.ucReserved = crtc_param->ucReserved; + + printk("executing set crtc timing\n"); + atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&conv_param); +} + +void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + uint8_t frev, crev; + int index = GetIndexIntoMasterTable(COMMAND, SetPixelClock); + SET_PIXEL_CLOCK_PS_ALLOCATION spc_param; + PIXEL_CLOCK_PARAMETERS_V2 *spc2_ptr; + PIXEL_CLOCK_PARAMETERS_V3 *spc3_ptr; + uint32_t sclock = mode->clock; + uint32_t ref_div = 0, fb_div = 0, post_div = 0; + struct radeon_pll *pll; + int pll_flags = 0; + + memset(&spc_param, 0, sizeof(SET_PIXEL_CLOCK_PS_ALLOCATION)); + + if (!radeon_is_avivo(dev_priv)) + pll_flags |= RADEON_PLL_LEGACY; + + if (mode->clock > 200000) /* range limits??? */ + pll_flags |= RADEON_PLL_PREFER_HIGH_FB_DIV; + else + pll_flags |= RADEON_PLL_PREFER_LOW_REF_DIV; + + if (radeon_crtc->crtc_id == 0) + pll = &dev_priv->mode_info.p1pll; + else + pll = &dev_priv->mode_info.p2pll; + + radeon_compute_pll(pll, mode->clock, &sclock, + &fb_div, &ref_div, &post_div, pll_flags); + + if (radeon_is_avivo(dev_priv)) { + uint32_t ss_cntl; + + if (radeon_crtc->crtc_id == 0) { + ss_cntl = RADEON_READ(AVIVO_P1PLL_INT_SS_CNTL); + RADEON_WRITE(AVIVO_P1PLL_INT_SS_CNTL, ss_cntl & ~1); + } else { + ss_cntl = RADEON_READ(AVIVO_P2PLL_INT_SS_CNTL); + RADEON_WRITE(AVIVO_P2PLL_INT_SS_CNTL, ss_cntl & ~1); + } + } + + /* */ + + atom_parse_cmd_header(dev_priv->mode_info.atom_context, index, &frev, &crev); + + switch(frev) { + case 1: + switch(crev) { + case 1: + case 2: + spc2_ptr = (PIXEL_CLOCK_PARAMETERS_V2*)&spc_param.sPCLKInput; + spc2_ptr->usPixelClock = cpu_to_le16(sclock); + spc2_ptr->usRefDiv = cpu_to_le16(ref_div); + spc2_ptr->usFbDiv = cpu_to_le16(fb_div); + spc2_ptr->ucPostDiv = post_div; + spc2_ptr->ucPpll = radeon_crtc->crtc_id ? ATOM_PPLL2 : ATOM_PPLL1; + spc2_ptr->ucCRTC = radeon_crtc->crtc_id; + spc2_ptr->ucRefDivSrc = 1; + break; + case 3: + spc3_ptr = (PIXEL_CLOCK_PARAMETERS_V3*)&spc_param.sPCLKInput; + spc3_ptr->usPixelClock = cpu_to_le16(sclock); + spc3_ptr->usRefDiv = cpu_to_le16(ref_div); + spc3_ptr->usFbDiv = cpu_to_le16(fb_div); + spc3_ptr->ucPostDiv = post_div; + spc3_ptr->ucPpll = radeon_crtc->crtc_id ? ATOM_PPLL2 : ATOM_PPLL1; + spc3_ptr->ucMiscInfo = (radeon_crtc->crtc_id << 2); + + /* TODO insert output encoder object stuff herre for r600 */ + break; + default: + DRM_ERROR("Unknown table version %d %d\n", frev, crev); + return; + } + break; + default: + DRM_ERROR("Unknown table version %d %d\n", frev, crev); + return; + } + + printk("executing set pll\n"); + atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&spc_param); +} + +void atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_framebuffer *radeon_fb; + struct drm_gem_object *obj; + struct drm_radeon_gem_object *obj_priv; + uint32_t fb_location, fb_format, fb_pitch_pixels; + + if (!crtc->fb) + return; + + radeon_fb = to_radeon_framebuffer(crtc->fb); + + obj = radeon_fb->obj; + obj_priv = obj->driver_private; + + fb_location = obj_priv->bo->offset + dev_priv->fb_location; + + switch(crtc->fb->bits_per_pixel) { + case 15: + fb_format = AVIVO_D1GRPH_CONTROL_DEPTH_16BPP | AVIVO_D1GRPH_CONTROL_16BPP_ARGB1555; + break; + case 16: + fb_format = AVIVO_D1GRPH_CONTROL_DEPTH_16BPP | AVIVO_D1GRPH_CONTROL_16BPP_RGB565; + break; + case 24: + case 32: + fb_format = AVIVO_D1GRPH_CONTROL_DEPTH_32BPP | AVIVO_D1GRPH_CONTROL_32BPP_ARGB8888; + break; + default: + DRM_ERROR("Unsupported screen depth %d\n", crtc->fb->bits_per_pixel); + return; + } + + /* TODO tiling */ + if (radeon_crtc->crtc_id == 0) + RADEON_WRITE(AVIVO_D1VGA_CONTROL, 0); + else + RADEON_WRITE(AVIVO_D2VGA_CONTROL, 0); + + RADEON_WRITE(AVIVO_D1GRPH_PRIMARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, fb_location); + RADEON_WRITE(AVIVO_D1GRPH_SECONDARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, fb_location); + RADEON_WRITE(AVIVO_D1GRPH_CONTROL + radeon_crtc->crtc_offset, fb_format); + + RADEON_WRITE(AVIVO_D1GRPH_SURFACE_OFFSET_X + radeon_crtc->crtc_offset, 0); + RADEON_WRITE(AVIVO_D1GRPH_SURFACE_OFFSET_Y + radeon_crtc->crtc_offset, 0); + RADEON_WRITE(AVIVO_D1GRPH_X_START + radeon_crtc->crtc_offset, x); + RADEON_WRITE(AVIVO_D1GRPH_Y_START + radeon_crtc->crtc_offset, y); + RADEON_WRITE(AVIVO_D1GRPH_X_END + radeon_crtc->crtc_offset, x + crtc->mode.hdisplay); + RADEON_WRITE(AVIVO_D1GRPH_Y_END + radeon_crtc->crtc_offset, y + crtc->mode.vdisplay); + + fb_pitch_pixels = crtc->fb->pitch / (crtc->fb->bits_per_pixel / 8); + RADEON_WRITE(AVIVO_D1GRPH_PITCH + radeon_crtc->crtc_offset, fb_pitch_pixels); + RADEON_WRITE(AVIVO_D1GRPH_ENABLE + radeon_crtc->crtc_offset, 1); + + RADEON_WRITE(AVIVO_D1MODE_DESKTOP_HEIGHT + radeon_crtc->crtc_offset, + crtc->mode.vdisplay); + RADEON_WRITE(AVIVO_D1MODE_VIEWPORT_START + radeon_crtc->crtc_offset, (x << 16) | y); + RADEON_WRITE(AVIVO_D1MODE_VIEWPORT_SIZE + radeon_crtc->crtc_offset, + (crtc->mode.hdisplay << 16) | crtc->mode.vdisplay); + + if (crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) + RADEON_WRITE(AVIVO_D1MODE_DATA_FORMAT + radeon_crtc->crtc_offset, + AVIVO_D1MODE_INTERLEAVE_EN); + else + RADEON_WRITE(AVIVO_D1MODE_DATA_FORMAT + radeon_crtc->crtc_offset, + 0); +} + +void atombios_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct drm_encoder *encoder; + SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION crtc_timing; + + /* TODO color tiling */ + memset(&crtc_timing, 0, sizeof(crtc_timing)); + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + + + + } + + crtc_timing.ucCRTC = radeon_crtc->crtc_id; + crtc_timing.usH_Total = adjusted_mode->crtc_htotal; + crtc_timing.usH_Disp = adjusted_mode->crtc_hdisplay; + crtc_timing.usH_SyncStart = adjusted_mode->crtc_hsync_start; + crtc_timing.usH_SyncWidth = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start; + + crtc_timing.usV_Total = adjusted_mode->crtc_vtotal; + crtc_timing.usV_Disp = adjusted_mode->crtc_vdisplay; + crtc_timing.usV_SyncStart = adjusted_mode->crtc_vsync_start; + crtc_timing.usV_SyncWidth = adjusted_mode->crtc_vsync_end - adjusted_mode->crtc_vsync_start; + + if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) + crtc_timing.susModeMiscInfo.usAccess |= ATOM_VSYNC_POLARITY; + + if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) + crtc_timing.susModeMiscInfo.usAccess |= ATOM_HSYNC_POLARITY; + + if (adjusted_mode->flags & DRM_MODE_FLAG_CSYNC) + crtc_timing.susModeMiscInfo.usAccess |= ATOM_COMPOSITESYNC; + + if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) + crtc_timing.susModeMiscInfo.usAccess |= ATOM_INTERLACE; + + if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) + crtc_timing.susModeMiscInfo.usAccess |= ATOM_DOUBLE_CLOCK_MODE; + + atombios_crtc_set_pll(crtc, adjusted_mode); + atombios_crtc_set_timing(crtc, &crtc_timing); + + if (radeon_is_avivo(dev_priv)) + atombios_crtc_set_base(crtc, x, y); + else { + if (radeon_crtc->crtc_id == 0) { + SET_CRTC_USING_DTD_TIMING_PARAMETERS crtc_dtd_timing; + memset(&crtc_dtd_timing, 0, sizeof(crtc_dtd_timing)); + + /* setup FP shadow regs on R4xx */ + crtc_dtd_timing.ucCRTC = radeon_crtc->crtc_id; + crtc_dtd_timing.usH_Size = adjusted_mode->crtc_hdisplay; + crtc_dtd_timing.usV_Size = adjusted_mode->crtc_vdisplay; + crtc_dtd_timing.usH_Blanking_Time = adjusted_mode->crtc_hblank_end - adjusted_mode->crtc_hdisplay; + crtc_dtd_timing.usV_Blanking_Time = adjusted_mode->crtc_vblank_end - adjusted_mode->crtc_vdisplay; + crtc_dtd_timing.usH_SyncOffset = adjusted_mode->crtc_hsync_start - adjusted_mode->crtc_hdisplay; + crtc_dtd_timing.usV_SyncOffset = adjusted_mode->crtc_vsync_start - adjusted_mode->crtc_vdisplay; + crtc_dtd_timing.usH_SyncWidth = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start; + crtc_dtd_timing.usV_SyncWidth = adjusted_mode->crtc_vsync_end - adjusted_mode->crtc_vsync_start; + //crtc_dtd_timing.ucH_Border = adjusted_mode->crtc_hborder; + //crtc_dtd_timing.ucV_Border = adjusted_mode->crtc_vborder; + + if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) + crtc_dtd_timing.susModeMiscInfo.usAccess |= ATOM_VSYNC_POLARITY; + + if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) + crtc_dtd_timing.susModeMiscInfo.usAccess |= ATOM_HSYNC_POLARITY; + + if (adjusted_mode->flags & DRM_MODE_FLAG_CSYNC) + crtc_dtd_timing.susModeMiscInfo.usAccess |= ATOM_COMPOSITESYNC; + + if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) + crtc_dtd_timing.susModeMiscInfo.usAccess |= ATOM_INTERLACE; + + if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) + crtc_dtd_timing.susModeMiscInfo.usAccess |= ATOM_DOUBLE_CLOCK_MODE; + + atombios_set_crtc_dtd_timing(crtc, &crtc_dtd_timing); + } + radeon_crtc_set_base(crtc, x, y); + radeon_legacy_atom_set_surface(crtc); + } + +} + +static bool atombios_crtc_mode_fixup(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + + +static void atombios_crtc_prepare(struct drm_crtc *crtc) +{ + atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); + atombios_lock_crtc(crtc, 1); +} + +static void atombios_crtc_commit(struct drm_crtc *crtc) +{ + atombios_crtc_dpms(crtc, DRM_MODE_DPMS_ON); + atombios_lock_crtc(crtc, 0); +} + +static const struct drm_crtc_helper_funcs atombios_helper_funcs = { + .dpms = atombios_crtc_dpms, + .mode_fixup = atombios_crtc_mode_fixup, + .mode_set = atombios_crtc_mode_set, + .mode_set_base = atombios_crtc_set_base, + .prepare = atombios_crtc_prepare, + .commit = atombios_crtc_commit, +}; + +void radeon_atombios_init_crtc(struct drm_device *dev, + struct radeon_crtc *radeon_crtc) +{ + if (radeon_crtc->crtc_id == 1) + radeon_crtc->crtc_offset = AVIVO_D2CRTC_H_TOTAL - AVIVO_D1CRTC_H_TOTAL; + drm_crtc_helper_add(&radeon_crtc->base, &atombios_helper_funcs); +} diff --git a/linux-core/drmP.h b/linux-core/drmP.h index 36a3d898..1b71b45d 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -54,6 +54,7 @@ #include <linux/smp_lock.h> /* For (un)lock_kernel */ #include <linux/dma-mapping.h> #include <linux/mm.h> +#include <linux/swap.h> #include <linux/kref.h> #include <linux/pagemap.h> #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) @@ -112,7 +113,8 @@ typedef unsigned long uintptr_t; #define DRIVER_IRQ_SHARED 0x80 #define DRIVER_DMA_QUEUE 0x100 #define DRIVER_FB_DMA 0x200 -#define DRIVER_GEM 0x400 +#define DRIVER_MODESET 0x400 +#define DRIVER_GEM 0x800 /*@}*/ @@ -164,7 +166,6 @@ typedef unsigned long uintptr_t; #define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8) #define DRM_MAP_HASH_OFFSET 0x10000000 #define DRM_MAP_HASH_ORDER 12 -#define DRM_OBJECT_HASH_ORDER 12 #define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFFUL >> PAGE_SHIFT) + 1) #define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFFUL >> PAGE_SHIFT) * 16) /* @@ -266,11 +267,11 @@ typedef unsigned long uintptr_t; */ #define LOCK_TEST_WITH_RETURN( dev, file_priv ) \ do { \ - if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || \ - dev->lock.file_priv != file_priv ) { \ + if ( !_DRM_LOCK_IS_HELD( file_priv->master->lock.hw_lock->lock ) || \ + file_priv->master->lock.file_priv != file_priv ) { \ DRM_ERROR( "%s called without lock held, held %d owner %p %p\n",\ - __FUNCTION__, _DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ),\ - dev->lock.file_priv, file_priv ); \ + __FUNCTION__, _DRM_LOCK_IS_HELD( file_priv->master->lock.hw_lock->lock ),\ + file_priv->master->lock.file_priv, file_priv ); \ return -EINVAL; \ } \ } while (0) @@ -304,6 +305,7 @@ typedef int drm_ioctl_compat_t(struct file *filp, unsigned int cmd, #define DRM_AUTH 0x1 #define DRM_MASTER 0x2 #define DRM_ROOT_ONLY 0x4 +#define DRM_CONTROL_ALLOW 0x8 // allow ioctl to operate on control node struct drm_ioctl_desc { unsigned int cmd; @@ -402,44 +404,29 @@ struct drm_buf_entry { struct drm_freelist freelist; }; - -enum drm_ref_type { - _DRM_REF_USE = 0, - _DRM_REF_TYPE1, - _DRM_NO_REF_TYPES -}; - - /** File private data */ struct drm_file { int authenticated; - int master; pid_t pid; uid_t uid; drm_magic_t magic; unsigned long ioctl_count; struct list_head lhead; struct drm_minor *minor; - int remove_auth_on_close; unsigned long lock_count; - /* - * The user object hash table is global and resides in the - * drm_device structure. We protect the lists and hash tables with the - * device struct_mutex. A bit coarse-grained but probably the best - * option. - */ - - struct list_head refd_objects; - /** Mapping of mm object handles to object pointers. */ struct idr object_idr; /** Lock for synchronization of access to object_idr. */ spinlock_t table_lock; - struct drm_open_hash refd_object_hash[_DRM_NO_REF_TYPES]; struct file *filp; void *driver_priv; + + int is_master; /* this file private is a master for a minor */ + struct drm_master *master; /* master this node is currently associated with + N.B. not always minor->master */ + struct list_head fbs; }; /** Wait queue */ @@ -573,6 +560,8 @@ struct drm_map_list { struct drm_hash_item hash; struct drm_map *map; /**< mapping */ uint64_t user_token; + struct drm_master *master; /** if this map is associated with a specific + master */ struct drm_mm_node *file_offset_node; }; @@ -594,6 +583,13 @@ struct drm_vbl_sig { struct task_struct *task; }; +struct drm_hotplug_sig { + struct list_head head; + unsigned int counter; + struct siginfo info; + struct task_struct *task; +}; + /* location of GART table */ #define DRM_ATI_GART_MAIN 1 #define DRM_ATI_GART_FB 2 @@ -665,6 +661,31 @@ struct drm_gem_object { }; #include "drm_objects.h" +#include "drm_crtc.h" + +/* per-master structure */ +struct drm_master { + + struct kref refcount; /* refcount for this master */ + + struct list_head head; /**< each minor contains a list of masters */ + struct drm_minor *minor; /**< link back to minor we are a master for */ + + char *unique; /**< Unique identifier: e.g., busid */ + int unique_len; /**< Length of unique field */ + + int blocked; /**< Blocked due to VC switch? */ + + /** \name Authentication */ + /*@{ */ + struct drm_open_hash magiclist; + struct list_head magicfree; + /*@} */ + + struct drm_lock_data lock; /**< Information on hardware lock */ + + void *driver_priv; /**< Private structure for driver to use */ +}; /** * DRM driver structure. This structure represent the common code for @@ -765,6 +786,10 @@ struct drm_driver { void (*set_version) (struct drm_device *dev, struct drm_set_version *sv); + /* Master routines */ + int (*master_create)(struct drm_device *dev, struct drm_master *master); + void (*master_destroy)(struct drm_device *dev, struct drm_master *master); + int (*proc_init)(struct drm_minor *minor); void (*proc_cleanup)(struct drm_minor *minor); @@ -798,6 +823,8 @@ struct drm_driver { #define DRM_MINOR_UNASSIGNED 0 #define DRM_MINOR_LEGACY 1 +#define DRM_MINOR_CONTROL 2 +#define DRM_MINOR_RENDER 3 /** * DRM minor structure. This structure represents a drm minor number. @@ -808,8 +835,15 @@ struct drm_minor { dev_t device; /**< Device number for mknod */ struct device kdev; /**< Linux device */ struct drm_device *dev; + /* for render nodes */ struct proc_dir_entry *dev_root; /**< proc directory entry */ struct class_device *dev_class; + + /* for control nodes - a pointer to the current master for this control node */ + struct drm_master *master; /* currently active master for this node */ + struct list_head master_list; + + struct drm_mode_group mode_group; }; @@ -818,13 +852,9 @@ struct drm_minor { * may contain multiple heads. */ struct drm_device { - char *unique; /**< Unique identifier: e.g., busid */ - int unique_len; /**< Length of unique field */ char *devname; /**< For /proc/interrupts */ int if_version; /**< Highest interface version set */ - int blocked; /**< Blocked due to VC switch? */ - /** \name Locks */ /*@{ */ spinlock_t count_lock; /**< For inuse, drm_device::open_count, drm_device::buf_use */ @@ -847,12 +877,6 @@ struct drm_device { atomic_t counts[15]; /*@} */ - /** \name Authentication */ - /*@{ */ - struct list_head filelist; - struct drm_open_hash magiclist; - struct list_head magicfree; - /*@} */ /** \name Memory management */ /*@{ */ @@ -860,7 +884,6 @@ struct drm_device { int map_count; /**< Number of mappable regions */ struct drm_open_hash map_hash; /**< User token hash table for maps */ struct drm_mm offset_manager; /**< User token manager */ - struct drm_open_hash object_hash; /**< User token hash table for objects */ struct address_space *dev_mapping; /**< For unmap_mapping_range() */ struct page *ttm_dummy_page; @@ -873,7 +896,9 @@ struct drm_device { struct idr ctx_idr; struct list_head vmalist; /**< List of vmas (for debugging) */ - struct drm_lock_data lock; /**< Information on hardware lock */ + + struct list_head filelist; + /*@} */ /** \name DMA queues (contexts) */ @@ -885,6 +910,7 @@ struct drm_device { struct drm_device_dma *dma; /**< Optional pointer for DMA support */ /*@} */ + /** \name Context support */ /*@{ */ int irq; /**< Interrupt used by board */ @@ -901,6 +927,15 @@ struct drm_device { struct work_struct work; + /** \name HOTPLUG IRQ support */ + /*@{ */ + wait_queue_head_t hotplug_queue; /**< HOTPLUG wait queue */ + spinlock_t hotplug_lock; + struct list_head *hotplug_sigs; /**< signal list to send on HOTPLUG */ + atomic_t hotplug_signal_pending; /* number of signals pending on all crtcs*/ + + /*@} */ + /** \name VBLANK IRQ support */ /*@{ */ @@ -954,6 +989,9 @@ struct drm_device { struct drm_driver *driver; drm_local_map_t *agp_buffer_map; unsigned int agp_buffer_token; + + /* minor number for control node */ + struct drm_minor *control; struct drm_minor *primary; /**< render type primary screen head */ struct drm_fence_manager fm; @@ -965,6 +1003,9 @@ struct drm_device { struct idr drw_idr; /*@} */ + /* DRM mode setting */ + struct drm_mode_config mode_config; + /** \name GEM information */ /*@{ */ spinlock_t object_name_lock; @@ -989,7 +1030,18 @@ struct drm_agp_ttm_backend { int populated; }; #endif - +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; +}; +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)); static __inline__ int drm_core_check_feature(struct drm_device *dev, int feature) @@ -1238,12 +1290,16 @@ extern void drm_driver_irq_preinstall(struct drm_device *dev); extern void drm_driver_irq_postinstall(struct drm_device *dev); extern void drm_driver_irq_uninstall(struct drm_device *dev); +extern int drm_hotplug_init(struct drm_device *dev); +extern int drm_wait_hotplug(struct drm_device *dev, void *data, struct drm_file *filp); extern int drm_vblank_init(struct drm_device *dev, int num_crtcs); extern int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *filp); +extern int drm_wait_hotplug(struct drm_device *dev, void *data, struct drm_file *filp); extern int drm_vblank_wait(struct drm_device * dev, unsigned int *vbl_seq); extern void drm_locked_tasklet(struct drm_device *dev, void(*func)(struct drm_device*)); extern u32 drm_vblank_count(struct drm_device *dev, int crtc); extern void drm_handle_vblank(struct drm_device *dev, int crtc); +extern void drm_handle_hotplug(struct drm_device *dev); extern int drm_vblank_get(struct drm_device *dev, int crtc); extern void drm_vblank_put(struct drm_device *dev, int crtc); @@ -1288,10 +1344,17 @@ extern int drm_agp_unbind_memory(DRM_AGP_MEM * handle); extern struct drm_ttm_backend *drm_agp_init_ttm(struct drm_device *dev); extern void drm_agp_chipset_flush(struct drm_device *dev); /* Stub support (drm_stub.h) */ +extern int drm_setmaster_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int drm_dropmaster_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +struct drm_master *drm_master_create(struct drm_minor *minor); +extern struct drm_master *drm_master_get(struct drm_master *master); +extern void drm_master_put(struct drm_master **master); extern int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent, - struct drm_driver *driver); + struct drm_driver *driver); extern int drm_put_dev(struct drm_device *dev); -extern int drm_put_minor(struct drm_device *dev); +extern int drm_put_minor(struct drm_minor **minor_p); extern unsigned int drm_debug; /* 1 to enable debug output */ extern struct class *drm_class; @@ -1317,6 +1380,8 @@ 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 int drm_ati_alloc_pcigart_table(struct drm_device *dev, + struct drm_ati_pcigart_info *gart_info); extern drm_dma_handle_t *drm_pci_alloc(struct drm_device *dev, size_t size, size_t align, dma_addr_t maxaddr); @@ -1328,7 +1393,11 @@ struct drm_sysfs_class; extern struct class *drm_sysfs_create(struct module *owner, char *name); extern void drm_sysfs_destroy(void); extern int drm_sysfs_device_add(struct drm_minor *minor); +extern void drm_sysfs_hotplug_event(struct drm_device *dev); extern void drm_sysfs_device_remove(struct drm_minor *minor); +extern char *drm_get_connector_status_name(enum drm_connector_status status); +extern int drm_sysfs_connector_add(struct drm_connector *connector); +extern void drm_sysfs_connector_remove(struct drm_connector *connector); /* * Basic memory manager support (drm_mm.c) diff --git a/linux-core/drm_auth.c b/linux-core/drm_auth.c index c904a91d..20c8a634 100644 --- a/linux-core/drm_auth.c +++ b/linux-core/drm_auth.c @@ -45,14 +45,15 @@ * the one with matching magic number, while holding the drm_device::struct_mutex * lock. */ -static struct drm_file *drm_find_file(struct drm_device * dev, drm_magic_t magic) +static struct drm_file *drm_find_file(struct drm_master *master , drm_magic_t magic) { struct drm_file *retval = NULL; struct drm_magic_entry *pt; struct drm_hash_item *hash; + struct drm_device *dev = master->minor->dev; mutex_lock(&dev->struct_mutex); - if (!drm_ht_find_item(&dev->magiclist, (unsigned long)magic, &hash)) { + if (!drm_ht_find_item(&master->magiclist, (unsigned long)magic, &hash)) { pt = drm_hash_entry(hash, struct drm_magic_entry, hash_item); retval = pt->priv; } @@ -71,11 +72,11 @@ static struct drm_file *drm_find_file(struct drm_device * dev, drm_magic_t magic * associated the magic number hash key in drm_device::magiclist, while holding * the drm_device::struct_mutex lock. */ -static int drm_add_magic(struct drm_device * dev, struct drm_file * priv, +static int drm_add_magic(struct drm_master *master, struct drm_file * priv, drm_magic_t magic) { struct drm_magic_entry *entry; - + struct drm_device *dev = master->minor->dev; DRM_DEBUG("%d\n", magic); entry = drm_alloc(sizeof(*entry), DRM_MEM_MAGIC); @@ -85,8 +86,8 @@ static int drm_add_magic(struct drm_device * dev, struct drm_file * priv, entry->priv = priv; entry->hash_item.key = (unsigned long)magic; mutex_lock(&dev->struct_mutex); - drm_ht_insert_item(&dev->magiclist, &entry->hash_item); - list_add_tail(&entry->head, &dev->magicfree); + drm_ht_insert_item(&master->magiclist, &entry->hash_item); + list_add_tail(&entry->head, &master->magicfree); mutex_unlock(&dev->struct_mutex); return 0; @@ -101,20 +102,21 @@ static int drm_add_magic(struct drm_device * dev, struct drm_file * priv, * Searches and unlinks the entry in drm_device::magiclist with the magic * number hash key, while holding the drm_device::struct_mutex lock. */ -static int drm_remove_magic(struct drm_device * dev, drm_magic_t magic) +static int drm_remove_magic(struct drm_master *master, drm_magic_t magic) { struct drm_magic_entry *pt; struct drm_hash_item *hash; + struct drm_device *dev = master->minor->dev; DRM_DEBUG("%d\n", magic); mutex_lock(&dev->struct_mutex); - if (drm_ht_find_item(&dev->magiclist, (unsigned long)magic, &hash)) { + if (drm_ht_find_item(&master->magiclist, (unsigned long)magic, &hash)) { mutex_unlock(&dev->struct_mutex); return -EINVAL; } pt = drm_hash_entry(hash, struct drm_magic_entry, hash_item); - drm_ht_remove_item(&dev->magiclist, hash); + drm_ht_remove_item(&master->magiclist, hash); list_del(&pt->head); mutex_unlock(&dev->struct_mutex); @@ -152,9 +154,9 @@ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) ++sequence; /* reserve 0 */ auth->magic = sequence++; spin_unlock(&lock); - } while (drm_find_file(dev, auth->magic)); + } while (drm_find_file(file_priv->master, auth->magic)); file_priv->magic = auth->magic; - drm_add_magic(dev, file_priv, auth->magic); + drm_add_magic(file_priv->master, file_priv, auth->magic); } DRM_DEBUG("%u\n", auth->magic); @@ -180,9 +182,9 @@ int drm_authmagic(struct drm_device *dev, void *data, struct drm_file *file; DRM_DEBUG("%u\n", auth->magic); - if ((file = drm_find_file(dev, auth->magic))) { + if ((file = drm_find_file(file_priv->master, auth->magic))) { file->authenticated = 1; - drm_remove_magic(dev, auth->magic); + drm_remove_magic(file_priv->master, auth->magic); return 0; } return -EINVAL; diff --git a/linux-core/drm_bo.c b/linux-core/drm_bo.c index 88b2ee66..a1a8098e 100644 --- a/linux-core/drm_bo.c +++ b/linux-core/drm_bo.c @@ -51,7 +51,6 @@ static void drm_bo_destroy_locked(struct drm_buffer_object *bo); static int drm_bo_setup_vm_locked(struct drm_buffer_object *bo); -static void drm_bo_takedown_vm_locked(struct drm_buffer_object *bo); static void drm_bo_unmap_virtual(struct drm_buffer_object *bo); static inline uint64_t drm_bo_type_flags(unsigned type) @@ -133,7 +132,7 @@ static void drm_bo_vm_post_move(struct drm_buffer_object *bo) * Call bo->mutex locked. */ -static int drm_bo_add_ttm(struct drm_buffer_object *bo) +int drm_bo_add_ttm(struct drm_buffer_object *bo) { struct drm_device *dev = bo->dev; int ret = 0; @@ -175,6 +174,7 @@ static int drm_bo_add_ttm(struct drm_buffer_object *bo) return ret; } +EXPORT_SYMBOL(drm_bo_add_ttm); static int drm_bo_handle_move_mem(struct drm_buffer_object *bo, struct drm_bo_mem_reg *mem, @@ -458,6 +458,7 @@ static void drm_bo_destroy_locked(struct drm_buffer_object *bo) DRM_ASSERT_LOCKED(&dev->struct_mutex); + DRM_DEBUG("freeing %p\n", bo); if (list_empty(&bo->lru) && bo->mem.mm_node == NULL && list_empty(&bo->pinned_lru) && bo->pinned_node == NULL && list_empty(&bo->ddestroy) && atomic_read(&bo->usage) == 0) { @@ -510,6 +511,7 @@ static void drm_bo_delayed_delete(struct drm_device *dev, int remove_all) entry = list_entry(list, struct drm_buffer_object, ddestroy); nentry = NULL; + DRM_DEBUG("bo is %p, %d\n", entry, entry->num_pages); if (next != &bm->ddestroy) { nentry = list_entry(next, struct drm_buffer_object, ddestroy); @@ -565,18 +567,6 @@ void drm_bo_usage_deref_locked(struct drm_buffer_object **bo) } EXPORT_SYMBOL(drm_bo_usage_deref_locked); -static void drm_bo_base_deref_locked(struct drm_file *file_priv, - struct drm_user_object *uo) -{ - struct drm_buffer_object *bo = - drm_user_object_entry(uo, struct drm_buffer_object, base); - - DRM_ASSERT_LOCKED(&bo->dev->struct_mutex); - - drm_bo_takedown_vm_locked(bo); - drm_bo_usage_deref_locked(&bo); -} - void drm_bo_usage_deref_unlocked(struct drm_buffer_object **bo) { struct drm_buffer_object *tmp_bo = *bo; @@ -624,7 +614,6 @@ void drm_putback_buffer_objects(struct drm_device *dev) } EXPORT_SYMBOL(drm_putback_buffer_objects); - /* * Note. The caller has to register (if applicable) * and deregister fence object usage. @@ -796,8 +785,8 @@ out: } drm_bo_add_to_lru(bo); BUG_ON(bo->priv_flags & _DRM_BO_FLAG_UNLOCKED); -out_unlock: mutex_unlock(&dev->struct_mutex); +out_unlock: return ret; } @@ -1069,40 +1058,12 @@ static int drm_bo_modify_proposed_flags (struct drm_buffer_object *bo, } /* - * Call dev->struct_mutex locked. - */ - -struct drm_buffer_object *drm_lookup_buffer_object(struct drm_file *file_priv, - uint32_t handle, int check_owner) -{ - struct drm_user_object *uo; - struct drm_buffer_object *bo; - - uo = drm_lookup_user_object(file_priv, handle); - - if (!uo || (uo->type != drm_buffer_type)) { - DRM_ERROR("Could not find buffer object 0x%08x\n", handle); - return NULL; - } - - if (check_owner && file_priv != uo->owner) { - if (!drm_lookup_ref_object(file_priv, uo, _DRM_REF_USE)) - return NULL; - } - - bo = drm_user_object_entry(uo, struct drm_buffer_object, base); - atomic_inc(&bo->usage); - return bo; -} -EXPORT_SYMBOL(drm_lookup_buffer_object); - -/* * Call bo->mutex locked. * Returns -EBUSY if the buffer is currently rendered to or from. 0 otherwise. * Doesn't do any fence flushing as opposed to the drm_bo_busy function. */ -static int drm_bo_quick_busy(struct drm_buffer_object *bo, int check_unfenced) +int drm_bo_quick_busy(struct drm_buffer_object *bo, int check_unfenced) { struct drm_fence_object *fence = bo->fence; @@ -1159,149 +1120,6 @@ static int drm_bo_wait_unmapped(struct drm_buffer_object *bo, int no_wait) } /* - * Fill in the ioctl reply argument with buffer info. - * Bo locked. - */ - -void drm_bo_fill_rep_arg(struct drm_buffer_object *bo, - struct drm_bo_info_rep *rep) -{ - if (!rep) - return; - - rep->handle = bo->base.hash.key; - rep->flags = bo->mem.flags; - rep->size = bo->num_pages * PAGE_SIZE; - rep->offset = bo->offset; - - /* - * drm_bo_type_device buffers have user-visible - * handles which can be used to share across - * processes. Hand that back to the application - */ - if (bo->type == drm_bo_type_device) - rep->arg_handle = bo->map_list.user_token; - else - rep->arg_handle = 0; - - rep->proposed_flags = bo->mem.proposed_flags; - rep->buffer_start = bo->buffer_start; - rep->fence_flags = bo->fence_type; - rep->rep_flags = 0; - rep->page_alignment = bo->mem.page_alignment; - - if ((bo->priv_flags & _DRM_BO_FLAG_UNFENCED) || drm_bo_quick_busy(bo, 1)) { - DRM_FLAG_MASKED(rep->rep_flags, DRM_BO_REP_BUSY, - DRM_BO_REP_BUSY); - } -} -EXPORT_SYMBOL(drm_bo_fill_rep_arg); - -/* - * Wait for buffer idle and register that we've mapped the buffer. - * Mapping is registered as a drm_ref_object with type _DRM_REF_TYPE1, - * so that if the client dies, the mapping is automatically - * unregistered. - */ - -static int drm_buffer_object_map(struct drm_file *file_priv, uint32_t handle, - uint32_t map_flags, unsigned hint, - struct drm_bo_info_rep *rep) -{ - struct drm_buffer_object *bo; - struct drm_device *dev = file_priv->minor->dev; - int ret = 0; - int no_wait = hint & DRM_BO_HINT_DONT_BLOCK; - - mutex_lock(&dev->struct_mutex); - bo = drm_lookup_buffer_object(file_priv, handle, 1); - mutex_unlock(&dev->struct_mutex); - - if (!bo) - return -EINVAL; - - mutex_lock(&bo->mutex); - do { - bo->priv_flags &= ~_DRM_BO_FLAG_UNLOCKED; - - ret = drm_bo_wait(bo, 0, 1, no_wait, 1); - if (unlikely(ret)) - goto out; - - if (bo->mem.flags & DRM_BO_FLAG_CACHED_MAPPED) - drm_bo_evict_cached(bo); - - } while (unlikely(bo->priv_flags & _DRM_BO_FLAG_UNLOCKED)); - - atomic_inc(&bo->mapped); - mutex_lock(&dev->struct_mutex); - ret = drm_add_ref_object(file_priv, &bo->base, _DRM_REF_TYPE1); - mutex_unlock(&dev->struct_mutex); - if (ret) { - if (atomic_dec_and_test(&bo->mapped)) - wake_up_all(&bo->event_queue); - - } else - drm_bo_fill_rep_arg(bo, rep); - - out: - mutex_unlock(&bo->mutex); - drm_bo_usage_deref_unlocked(&bo); - - return ret; -} - -static int drm_buffer_object_unmap(struct drm_file *file_priv, uint32_t handle) -{ - struct drm_device *dev = file_priv->minor->dev; - struct drm_buffer_object *bo; - struct drm_ref_object *ro; - int ret = 0; - - mutex_lock(&dev->struct_mutex); - - bo = drm_lookup_buffer_object(file_priv, handle, 1); - if (!bo) { - ret = -EINVAL; - goto out; - } - - ro = drm_lookup_ref_object(file_priv, &bo->base, _DRM_REF_TYPE1); - if (!ro) { - ret = -EINVAL; - goto out; - } - - drm_remove_ref_object(file_priv, ro); - drm_bo_usage_deref_locked(&bo); -out: - mutex_unlock(&dev->struct_mutex); - return ret; -} - -/* - * Call struct-sem locked. - */ - -static void drm_buffer_user_object_unmap(struct drm_file *file_priv, - struct drm_user_object *uo, - enum drm_ref_type action) -{ - struct drm_buffer_object *bo = - drm_user_object_entry(uo, struct drm_buffer_object, base); - - /* - * We DON'T want to take the bo->lock here, because we want to - * hold it when we wait for unmapped buffer. - */ - - BUG_ON(action != _DRM_REF_TYPE1); - - if (atomic_dec_and_test(&bo->mapped)) - wake_up_all(&bo->event_queue); -} - -/* * bo->mutex locked. * Note that new_mem_flags are NOT transferred to the bo->mem.proposed_flags. */ @@ -1354,6 +1172,10 @@ out_unlock: DRM_FLAG_MASKED(bo->priv_flags, _DRM_BO_FLAG_UNFENCED, _DRM_BO_FLAG_UNFENCED); } + /* clear the clean flags */ + bo->mem.flags &= ~DRM_BO_FLAG_CLEAN; + bo->mem.proposed_flags &= ~DRM_BO_FLAG_CLEAN; + mutex_unlock(&dev->struct_mutex); mutex_unlock(&bm->evict_mutex); return ret; @@ -1510,14 +1332,15 @@ static int drm_bo_prepare_for_validate(struct drm_buffer_object *bo, int ret; - DRM_DEBUG("Proposed flags 0x%016llx, Old flags 0x%016llx\n", - (unsigned long long) bo->mem.proposed_flags, - (unsigned long long) bo->mem.flags); ret = drm_bo_modify_proposed_flags (bo, flags, mask); if (ret) return ret; + DRM_DEBUG("Proposed flags 0x%016llx, Old flags 0x%016llx\n", + (unsigned long long) bo->mem.proposed_flags, + (unsigned long long) bo->mem.flags); + ret = drm_bo_wait_unmapped(bo, no_wait); if (ret) return ret; @@ -1595,8 +1418,7 @@ static int drm_bo_prepare_for_validate(struct drm_buffer_object *bo, int drm_bo_do_validate(struct drm_buffer_object *bo, uint64_t flags, uint64_t mask, uint32_t hint, - uint32_t fence_class, - struct drm_bo_info_rep *rep) + uint32_t fence_class) { int ret; int no_wait = (hint & DRM_BO_HINT_DONT_BLOCK) != 0; @@ -1623,132 +1445,12 @@ int drm_bo_do_validate(struct drm_buffer_object *bo, BUG_ON(bo->priv_flags & _DRM_BO_FLAG_UNLOCKED); out: - if (rep) - drm_bo_fill_rep_arg(bo, rep); - mutex_unlock(&bo->mutex); return ret; } EXPORT_SYMBOL(drm_bo_do_validate); -/** - * drm_bo_handle_validate - * - * @file_priv: the drm file private, used to get a handle to the user context - * - * @handle: the buffer object handle - * - * @flags: access rights, mapping parameters and cacheability. See - * the DRM_BO_FLAG_* values in drm.h - * - * @mask: Which flag values to change; this allows callers to modify - * things without knowing the current state of other flags. - * - * @hint: changes the proceedure for this operation, see the DRM_BO_HINT_* - * values in drm.h. - * - * @fence_class: a driver-specific way of doing fences. Presumably, - * this would be used if the driver had more than one submission and - * fencing mechanism. At this point, there isn't any use of this - * from the user mode code. - * - * @rep: To be stuffed with the reply from validation - * - * @bp_rep: To be stuffed with the buffer object pointer - * - * Perform drm_bo_do_validate on a buffer referenced by a user-space handle instead - * of a pointer to a buffer object. Optionally return a pointer to the buffer object. - * This is a convenience wrapper only. - */ - -int drm_bo_handle_validate(struct drm_file *file_priv, uint32_t handle, - uint64_t flags, uint64_t mask, - uint32_t hint, - uint32_t fence_class, - struct drm_bo_info_rep *rep, - struct drm_buffer_object **bo_rep) -{ - struct drm_device *dev = file_priv->minor->dev; - struct drm_buffer_object *bo; - int ret; - - mutex_lock(&dev->struct_mutex); - bo = drm_lookup_buffer_object(file_priv, handle, 1); - mutex_unlock(&dev->struct_mutex); - - if (!bo) - return -EINVAL; - - if (bo->base.owner != file_priv) - mask &= ~(DRM_BO_FLAG_NO_EVICT | DRM_BO_FLAG_NO_MOVE); - - ret = drm_bo_do_validate(bo, flags, mask, hint, fence_class, rep); - - if (!ret && bo_rep) - *bo_rep = bo; - else - drm_bo_usage_deref_unlocked(&bo); - - return ret; -} -EXPORT_SYMBOL(drm_bo_handle_validate); - - -static int drm_bo_handle_info(struct drm_file *file_priv, uint32_t handle, - struct drm_bo_info_rep *rep) -{ - struct drm_device *dev = file_priv->minor->dev; - struct drm_buffer_object *bo; - - mutex_lock(&dev->struct_mutex); - bo = drm_lookup_buffer_object(file_priv, handle, 1); - mutex_unlock(&dev->struct_mutex); - - if (!bo) - return -EINVAL; - - mutex_lock(&bo->mutex); - - /* - * FIXME: Quick busy here? - */ - - drm_bo_busy(bo, 1); - drm_bo_fill_rep_arg(bo, rep); - mutex_unlock(&bo->mutex); - drm_bo_usage_deref_unlocked(&bo); - return 0; -} - -static int drm_bo_handle_wait(struct drm_file *file_priv, uint32_t handle, - uint32_t hint, - struct drm_bo_info_rep *rep) -{ - struct drm_device *dev = file_priv->minor->dev; - struct drm_buffer_object *bo; - int no_wait = hint & DRM_BO_HINT_DONT_BLOCK; - int ret; - - mutex_lock(&dev->struct_mutex); - bo = drm_lookup_buffer_object(file_priv, handle, 1); - mutex_unlock(&dev->struct_mutex); - - if (!bo) - return -EINVAL; - - mutex_lock(&bo->mutex); - ret = drm_bo_wait(bo, hint & DRM_BO_HINT_WAIT_LAZY, 1, no_wait, 1); - if (ret) - goto out; - - drm_bo_fill_rep_arg(bo, rep); -out: - mutex_unlock(&bo->mutex); - drm_bo_usage_deref_unlocked(&bo); - return ret; -} - int drm_buffer_object_create(struct drm_device *dev, unsigned long size, enum drm_bo_type type, @@ -1766,7 +1468,7 @@ int drm_buffer_object_create(struct drm_device *dev, size += buffer_start & ~PAGE_MASK; num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; if (num_pages == 0) { - DRM_ERROR("Illegal buffer object size.\n"); + DRM_ERROR("Illegal buffer object size %ld.\n", size); return -EINVAL; } @@ -1798,12 +1500,14 @@ int drm_buffer_object_create(struct drm_device *dev, bo->buffer_start = buffer_start & PAGE_MASK; bo->priv_flags = 0; bo->mem.flags = (DRM_BO_FLAG_MEM_LOCAL | DRM_BO_FLAG_CACHED | - DRM_BO_FLAG_MAPPABLE); + DRM_BO_FLAG_MAPPABLE | DRM_BO_FLAG_CLEAN); bo->mem.proposed_flags = 0; atomic_inc(&bm->count); /* * Use drm_bo_modify_proposed_flags to error-check the proposed flags */ + flags |= DRM_BO_FLAG_CLEAN; /* or in the clean flag */ + ret = drm_bo_modify_proposed_flags (bo, flags, flags); if (ret) goto out_err; @@ -1823,7 +1527,7 @@ int drm_buffer_object_create(struct drm_device *dev, mutex_unlock(&bo->mutex); ret = drm_bo_do_validate(bo, 0, 0, hint | DRM_BO_HINT_DONT_FENCE, - 0, NULL); + 0); if (ret) goto out_err_unlocked; @@ -1838,229 +1542,6 @@ out_err_unlocked: } EXPORT_SYMBOL(drm_buffer_object_create); - -static int drm_bo_add_user_object(struct drm_file *file_priv, - struct drm_buffer_object *bo, int shareable) -{ - struct drm_device *dev = file_priv->minor->dev; - int ret; - - mutex_lock(&dev->struct_mutex); - ret = drm_add_user_object(file_priv, &bo->base, shareable); - if (ret) - goto out; - - bo->base.remove = drm_bo_base_deref_locked; - bo->base.type = drm_buffer_type; - bo->base.ref_struct_locked = NULL; - bo->base.unref = drm_buffer_user_object_unmap; - -out: - mutex_unlock(&dev->struct_mutex); - return ret; -} - -int drm_bo_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - struct drm_bo_create_arg *arg = data; - struct drm_bo_create_req *req = &arg->d.req; - struct drm_bo_info_rep *rep = &arg->d.rep; - struct drm_buffer_object *entry; - enum drm_bo_type bo_type; - int ret = 0; - - DRM_DEBUG("drm_bo_create_ioctl: %dkb, %dkb align\n", - (int)(req->size / 1024), req->page_alignment * 4); - - if (!dev->bm.initialized) { - DRM_ERROR("Buffer object manager is not initialized.\n"); - return -EINVAL; - } - - /* - * If the buffer creation request comes in with a starting address, - * that points at the desired user pages to map. Otherwise, create - * a drm_bo_type_device buffer, which uses pages allocated from the kernel - */ - bo_type = (req->buffer_start) ? drm_bo_type_user : drm_bo_type_device; - - /* - * User buffers cannot be shared - */ - if (bo_type == drm_bo_type_user) - req->flags &= ~DRM_BO_FLAG_SHAREABLE; - - ret = drm_buffer_object_create(file_priv->minor->dev, - req->size, bo_type, req->flags, - req->hint, req->page_alignment, - req->buffer_start, &entry); - if (ret) - goto out; - - ret = drm_bo_add_user_object(file_priv, entry, - req->flags & DRM_BO_FLAG_SHAREABLE); - if (ret) { - drm_bo_usage_deref_unlocked(&entry); - goto out; - } - - mutex_lock(&entry->mutex); - drm_bo_fill_rep_arg(entry, rep); - mutex_unlock(&entry->mutex); - -out: - return ret; -} - -int drm_bo_setstatus_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_bo_map_wait_idle_arg *arg = data; - struct drm_bo_info_req *req = &arg->d.req; - struct drm_bo_info_rep *rep = &arg->d.rep; - struct drm_buffer_object *bo; - int ret; - - if (!dev->bm.initialized) { - DRM_ERROR("Buffer object manager is not initialized.\n"); - return -EINVAL; - } - - ret = drm_bo_read_lock(&dev->bm.bm_lock, 1); - if (ret) - return ret; - - mutex_lock(&dev->struct_mutex); - bo = drm_lookup_buffer_object(file_priv, req->handle, 1); - mutex_unlock(&dev->struct_mutex); - - if (!bo) - return -EINVAL; - - if (bo->base.owner != file_priv) - req->mask &= ~(DRM_BO_FLAG_NO_EVICT | DRM_BO_FLAG_NO_MOVE); - - ret = drm_bo_do_validate(bo, req->flags, req->mask, - req->hint | DRM_BO_HINT_DONT_FENCE, - bo->fence_class, rep); - - drm_bo_usage_deref_unlocked(&bo); - - (void) drm_bo_read_unlock(&dev->bm.bm_lock); - - return ret; -} - -int drm_bo_map_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - struct drm_bo_map_wait_idle_arg *arg = data; - struct drm_bo_info_req *req = &arg->d.req; - struct drm_bo_info_rep *rep = &arg->d.rep; - int ret; - if (!dev->bm.initialized) { - DRM_ERROR("Buffer object manager is not initialized.\n"); - return -EINVAL; - } - - ret = drm_buffer_object_map(file_priv, req->handle, req->mask, - req->hint, rep); - if (ret) - return ret; - - return 0; -} - -int drm_bo_unmap_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - struct drm_bo_handle_arg *arg = data; - int ret; - if (!dev->bm.initialized) { - DRM_ERROR("Buffer object manager is not initialized.\n"); - return -EINVAL; - } - - ret = drm_buffer_object_unmap(file_priv, arg->handle); - return ret; -} - - -int drm_bo_reference_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - struct drm_bo_reference_info_arg *arg = data; - struct drm_bo_handle_arg *req = &arg->d.req; - struct drm_bo_info_rep *rep = &arg->d.rep; - struct drm_user_object *uo; - int ret; - - if (!dev->bm.initialized) { - DRM_ERROR("Buffer object manager is not initialized.\n"); - return -EINVAL; - } - - ret = drm_user_object_ref(file_priv, req->handle, - drm_buffer_type, &uo); - if (ret) - return ret; - - ret = drm_bo_handle_info(file_priv, req->handle, rep); - if (ret) - return ret; - - return 0; -} - -int drm_bo_unreference_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - struct drm_bo_handle_arg *arg = data; - int ret = 0; - - if (!dev->bm.initialized) { - DRM_ERROR("Buffer object manager is not initialized.\n"); - return -EINVAL; - } - - ret = drm_user_object_unref(file_priv, arg->handle, drm_buffer_type); - return ret; -} - -int drm_bo_info_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - struct drm_bo_reference_info_arg *arg = data; - struct drm_bo_handle_arg *req = &arg->d.req; - struct drm_bo_info_rep *rep = &arg->d.rep; - int ret; - - if (!dev->bm.initialized) { - DRM_ERROR("Buffer object manager is not initialized.\n"); - return -EINVAL; - } - - ret = drm_bo_handle_info(file_priv, req->handle, rep); - if (ret) - return ret; - - return 0; -} - -int drm_bo_wait_idle_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - struct drm_bo_map_wait_idle_arg *arg = data; - struct drm_bo_info_req *req = &arg->d.req; - struct drm_bo_info_rep *rep = &arg->d.rep; - int ret; - if (!dev->bm.initialized) { - DRM_ERROR("Buffer object manager is not initialized.\n"); - return -EINVAL; - } - - ret = drm_bo_handle_wait(file_priv, req->handle, - req->hint, rep); - if (ret) - return ret; - - return 0; -} - static int drm_bo_leave_list(struct drm_buffer_object *bo, uint32_t mem_type, int free_pinned, @@ -2240,7 +1721,7 @@ EXPORT_SYMBOL(drm_bo_clean_mm); *point since we have the hardware lock. */ -static int drm_bo_lock_mm(struct drm_device *dev, unsigned mem_type) +int drm_bo_lock_mm(struct drm_device *dev, unsigned mem_type) { int ret; struct drm_buffer_manager *bm = &dev->bm; @@ -2362,15 +1843,19 @@ int drm_bo_driver_finish(struct drm_device *dev) if (list_empty(&bm->unfenced)) DRM_DEBUG("Unfenced list was clean\n"); + if (bm->dummy_read_page) { #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) - ClearPageReserved(bm->dummy_read_page); + ClearPageReserved(bm->dummy_read_page); #endif - __free_page(bm->dummy_read_page); + __free_page(bm->dummy_read_page); + } + drm_uncached_fini(); 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. @@ -2385,8 +1870,9 @@ int drm_bo_driver_init(struct drm_device *dev) struct drm_buffer_manager *bm = &dev->bm; int ret = -EINVAL; + drm_uncached_init(); + bm->dummy_read_page = NULL; - drm_bo_init_lock(&bm->bm_lock); mutex_lock(&dev->struct_mutex); if (!driver) goto out_unlock; @@ -2406,8 +1892,14 @@ int drm_bo_driver_init(struct drm_device *dev) * Other types need to be driver / IOCTL initialized. */ ret = drm_bo_init_mm(dev, DRM_BO_MEM_LOCAL, 0, 0, 1); - if (ret) + if (ret) { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) + ClearPageReserved(bm->dummy_read_page); +#endif + __free_page(bm->dummy_read_page); + bm->dummy_read_page = NULL; goto out_unlock; + } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) INIT_WORK(&bm->wq, &drm_bo_delayed_workqueue, dev); @@ -2426,191 +1918,6 @@ out_unlock: } EXPORT_SYMBOL(drm_bo_driver_init); -int drm_mm_init_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - struct drm_mm_init_arg *arg = data; - struct drm_buffer_manager *bm = &dev->bm; - struct drm_bo_driver *driver = dev->driver->bo_driver; - int ret; - - if (!driver) { - DRM_ERROR("Buffer objects are not supported by this driver\n"); - return -EINVAL; - } - - ret = drm_bo_write_lock(&bm->bm_lock, 1, file_priv); - if (ret) - return ret; - - ret = -EINVAL; - if (arg->magic != DRM_BO_INIT_MAGIC) { - DRM_ERROR("You are using an old libdrm that is not compatible with\n" - "\tthe kernel DRM module. Please upgrade your libdrm.\n"); - return -EINVAL; - } - if (arg->major != DRM_BO_INIT_MAJOR) { - DRM_ERROR("libdrm and kernel DRM buffer object interface major\n" - "\tversion don't match. Got %d, expected %d.\n", - arg->major, DRM_BO_INIT_MAJOR); - return -EINVAL; - } - - mutex_lock(&dev->struct_mutex); - if (!bm->initialized) { - DRM_ERROR("DRM memory manager was not initialized.\n"); - goto out; - } - if (arg->mem_type == 0) { - DRM_ERROR("System memory buffers already initialized.\n"); - goto out; - } - ret = drm_bo_init_mm(dev, arg->mem_type, - arg->p_offset, arg->p_size, 0); - -out: - mutex_unlock(&dev->struct_mutex); - (void) drm_bo_write_unlock(&bm->bm_lock, file_priv); - - if (ret) - return ret; - - return 0; -} - -int drm_mm_takedown_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - struct drm_mm_type_arg *arg = data; - struct drm_buffer_manager *bm = &dev->bm; - struct drm_bo_driver *driver = dev->driver->bo_driver; - int ret; - - if (!driver) { - DRM_ERROR("Buffer objects are not supported by this driver\n"); - return -EINVAL; - } - - ret = drm_bo_write_lock(&bm->bm_lock, 0, file_priv); - if (ret) - return ret; - - mutex_lock(&dev->struct_mutex); - ret = -EINVAL; - if (!bm->initialized) { - DRM_ERROR("DRM memory manager was not initialized\n"); - goto out; - } - if (arg->mem_type == 0) { - DRM_ERROR("No takedown for System memory buffers.\n"); - goto out; - } - ret = 0; - if ((ret = drm_bo_clean_mm(dev, arg->mem_type, 0))) { - if (ret == -EINVAL) - DRM_ERROR("Memory manager type %d not clean. " - "Delaying takedown\n", arg->mem_type); - ret = 0; - } -out: - mutex_unlock(&dev->struct_mutex); - (void) drm_bo_write_unlock(&bm->bm_lock, file_priv); - - if (ret) - return ret; - - return 0; -} - -int drm_mm_lock_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - struct drm_mm_type_arg *arg = data; - struct drm_bo_driver *driver = dev->driver->bo_driver; - int ret; - - if (!driver) { - DRM_ERROR("Buffer objects are not supported by this driver\n"); - return -EINVAL; - } - - if (arg->lock_flags & DRM_BO_LOCK_IGNORE_NO_EVICT) { - DRM_ERROR("Lock flag DRM_BO_LOCK_IGNORE_NO_EVICT not supported yet.\n"); - return -EINVAL; - } - - if (arg->lock_flags & DRM_BO_LOCK_UNLOCK_BM) { - ret = drm_bo_write_lock(&dev->bm.bm_lock, 1, file_priv); - if (ret) - return ret; - } - - mutex_lock(&dev->struct_mutex); - ret = drm_bo_lock_mm(dev, arg->mem_type); - mutex_unlock(&dev->struct_mutex); - if (ret) { - (void) drm_bo_write_unlock(&dev->bm.bm_lock, file_priv); - return ret; - } - - return 0; -} - -int drm_mm_unlock_ioctl(struct drm_device *dev, - void *data, - struct drm_file *file_priv) -{ - struct drm_mm_type_arg *arg = data; - struct drm_bo_driver *driver = dev->driver->bo_driver; - int ret; - - if (!driver) { - DRM_ERROR("Buffer objects are not supported by this driver\n"); - return -EINVAL; - } - - if (arg->lock_flags & DRM_BO_LOCK_UNLOCK_BM) { - ret = drm_bo_write_unlock(&dev->bm.bm_lock, file_priv); - if (ret) - return ret; - } - - return 0; -} - -int drm_mm_info_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - struct drm_mm_info_arg *arg = data; - struct drm_buffer_manager *bm = &dev->bm; - struct drm_bo_driver *driver = dev->driver->bo_driver; - struct drm_mem_type_manager *man; - int ret = 0; - int mem_type = arg->mem_type; - - if (!driver) { - DRM_ERROR("Buffer objects are not supported by this driver\n"); - return -EINVAL; - } - - if (mem_type >= DRM_BO_MEM_TYPES) { - DRM_ERROR("Illegal memory type %d\n", arg->mem_type); - return -EINVAL; - } - - mutex_lock(&dev->struct_mutex); - if (!bm->initialized) { - DRM_ERROR("DRM memory manager was not initialized\n"); - ret = -EINVAL; - goto out; - } - - - man = &bm->man[arg->mem_type]; - - arg->p_size = man->size; - -out: - mutex_unlock(&dev->struct_mutex); - - return ret; -} /* * buffer object vm functions. */ @@ -2697,7 +2004,7 @@ void drm_bo_unmap_virtual(struct drm_buffer_object *bo) * Remove any associated vm mapping on the drm device node that * would have been created for a drm_bo_type_device buffer */ -static void drm_bo_takedown_vm_locked(struct drm_buffer_object *bo) +void drm_bo_takedown_vm_locked(struct drm_buffer_object *bo) { struct drm_map_list *list; drm_local_map_t *map; @@ -2726,6 +2033,7 @@ static void drm_bo_takedown_vm_locked(struct drm_buffer_object *bo) list->user_token = 0ULL; drm_bo_usage_deref_locked(&bo); } +EXPORT_SYMBOL(drm_bo_takedown_vm_locked); /** * drm_bo_setup_vm_locked: @@ -2783,14 +2091,53 @@ static int drm_bo_setup_vm_locked(struct drm_buffer_object *bo) return 0; } -int drm_bo_version_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) +/* used to EVICT VRAM lru at suspend time */ +void drm_bo_evict_mm(struct drm_device *dev, int mem_type, int no_wait) { - struct drm_bo_version_arg *arg = (struct drm_bo_version_arg *)data; + struct drm_buffer_manager *bm = &dev->bm; + struct drm_mem_type_manager *man = &bm->man[mem_type]; + struct drm_buffer_object *entry; + /* we need to migrate all objects in VRAM */ + struct list_head *lru; + int ret; + /* evict all buffers on the LRU - won't evict pinned buffers */ + + mutex_lock(&dev->struct_mutex); + do { + lru = &man->lru; - arg->major = DRM_BO_INIT_MAJOR; - arg->minor = DRM_BO_INIT_MINOR; - arg->patchlevel = DRM_BO_INIT_PATCH; +redo: + if (lru->next == &man->lru) { + DRM_ERROR("lru empty\n"); + break; + } + + entry = list_entry(lru->next, struct drm_buffer_object, lru); + + if (entry->mem.flags & DRM_BO_FLAG_DISCARDABLE) { + lru = lru->next; + goto redo; + } + + atomic_inc(&entry->usage); + mutex_unlock(&dev->struct_mutex); + mutex_lock(&entry->mutex); + + ret = drm_bo_evict(entry, mem_type, no_wait); + mutex_unlock(&entry->mutex); + + if (ret) + DRM_ERROR("Evict failed for BO\n"); + + mutex_lock(&entry->mutex); + (void)drm_bo_expire_fence(entry, 0); + mutex_unlock(&entry->mutex); + drm_bo_usage_deref_unlocked(&entry); + + mutex_lock(&dev->struct_mutex); + } while(1); + + mutex_unlock(&dev->struct_mutex); - return 0; } +EXPORT_SYMBOL(drm_bo_evict_mm); diff --git a/linux-core/drm_bo_lock.c b/linux-core/drm_bo_lock.c deleted file mode 100644 index 08b1c6be..00000000 --- a/linux-core/drm_bo_lock.c +++ /dev/null @@ -1,189 +0,0 @@ -/************************************************************************** - * - * Copyright (c) 2007 Tungsten Graphics, Inc., Cedar Park, TX., 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 above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE 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. - * - **************************************************************************/ -/* - * Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com> - */ - -/* - * This file implements a simple replacement for the buffer manager use - * of the heavyweight hardware lock. - * The lock is a read-write lock. Taking it in read mode is fast, and - * intended for in-kernel use only. - * Taking it in write mode is slow. - * - * The write mode is used only when there is a need to block all - * user-space processes from allocating a - * new memory area. - * Typical use in write mode is X server VT switching, and it's allowed - * to leave kernel space with the write lock held. If a user-space process - * dies while having the write-lock, it will be released during the file - * descriptor release. - * - * The read lock is typically placed at the start of an IOCTL- or - * user-space callable function that may end up allocating a memory area. - * This includes setstatus, super-ioctls and no_pfn; the latter may move - * unmappable regions to mappable. It's a bug to leave kernel space with the - * read lock held. - * - * Both read- and write lock taking may be interruptible for low signal-delivery - * latency. The locking functions will return -EAGAIN if interrupted by a - * signal. - * - * Locking order: The lock should be taken BEFORE any kernel mutexes - * or spinlocks. - */ - -#include "drmP.h" - -void drm_bo_init_lock(struct drm_bo_lock *lock) -{ - DRM_INIT_WAITQUEUE(&lock->queue); - atomic_set(&lock->write_lock_pending, 0); - atomic_set(&lock->readers, 0); -} - -void drm_bo_read_unlock(struct drm_bo_lock *lock) -{ - if (atomic_dec_and_test(&lock->readers)) - wake_up_all(&lock->queue); -} -EXPORT_SYMBOL(drm_bo_read_unlock); - -int drm_bo_read_lock(struct drm_bo_lock *lock, int interruptible) -{ - while (unlikely(atomic_read(&lock->write_lock_pending) != 0)) { - int ret; - - if (!interruptible) { - wait_event(lock->queue, - atomic_read(&lock->write_lock_pending) == 0); - continue; - } - ret = wait_event_interruptible - (lock->queue, atomic_read(&lock->write_lock_pending) == 0); - if (ret) - return -EAGAIN; - } - - while (unlikely(!atomic_add_unless(&lock->readers, 1, -1))) { - int ret; - if (!interruptible) { - wait_event(lock->queue, - atomic_read(&lock->readers) != -1); - continue; - } - ret = wait_event_interruptible - (lock->queue, atomic_read(&lock->readers) != -1); - if (ret) - return -EAGAIN; - } - return 0; -} -EXPORT_SYMBOL(drm_bo_read_lock); - -static int __drm_bo_write_unlock(struct drm_bo_lock *lock) -{ - if (unlikely(atomic_cmpxchg(&lock->readers, -1, 0) != -1)) - return -EINVAL; - wake_up_all(&lock->queue); - return 0; -} - -static void drm_bo_write_lock_remove(struct drm_file *file_priv, - struct drm_user_object *item) -{ - struct drm_bo_lock *lock = container_of(item, struct drm_bo_lock, base); - int ret; - - ret = __drm_bo_write_unlock(lock); - BUG_ON(ret); -} - -int drm_bo_write_lock(struct drm_bo_lock *lock, int interruptible, - struct drm_file *file_priv) -{ - int ret = 0; - struct drm_device *dev; - - atomic_inc(&lock->write_lock_pending); - - while (unlikely(atomic_cmpxchg(&lock->readers, 0, -1) != 0)) { - if (!interruptible) { - wait_event(lock->queue, - atomic_read(&lock->readers) == 0); - continue; - } - ret = wait_event_interruptible - (lock->queue, atomic_read(&lock->readers) == 0); - - if (ret) { - atomic_dec(&lock->write_lock_pending); - wake_up_all(&lock->queue); - return -EAGAIN; - } - } - - /* - * Add a dummy user-object, the destructor of which will - * make sure the lock is released if the client dies - * while holding it. - */ - - if (atomic_dec_and_test(&lock->write_lock_pending)) - wake_up_all(&lock->queue); - dev = file_priv->minor->dev; - mutex_lock(&dev->struct_mutex); - ret = drm_add_user_object(file_priv, &lock->base, 0); - lock->base.remove = &drm_bo_write_lock_remove; - lock->base.type = drm_lock_type; - if (ret) - (void)__drm_bo_write_unlock(lock); - - mutex_unlock(&dev->struct_mutex); - - return ret; -} - -int drm_bo_write_unlock(struct drm_bo_lock *lock, struct drm_file *file_priv) -{ - struct drm_device *dev = file_priv->minor->dev; - struct drm_ref_object *ro; - - mutex_lock(&dev->struct_mutex); - - if (lock->base.owner != file_priv) { - mutex_unlock(&dev->struct_mutex); - return -EINVAL; - } - ro = drm_lookup_ref_object(file_priv, &lock->base, _DRM_REF_USE); - BUG_ON(!ro); - drm_remove_ref_object(file_priv, ro); - lock->base.owner = NULL; - - mutex_unlock(&dev->struct_mutex); - return 0; -} diff --git a/linux-core/drm_bo_move.c b/linux-core/drm_bo_move.c index bf0e1b74..7fe12f45 100644 --- a/linux-core/drm_bo_move.c +++ b/linux-core/drm_bo_move.c @@ -146,6 +146,7 @@ void drm_mem_reg_iounmap(struct drm_device *dev, struct drm_bo_mem_reg *mem, if (virtual && (man->flags & _DRM_FLAG_NEEDS_IOREMAP)) iounmap(virtual); } +EXPORT_SYMBOL(drm_mem_reg_iounmap); static int drm_copy_io_page(void *dst, void *src, unsigned long page) { @@ -271,6 +272,77 @@ out: } EXPORT_SYMBOL(drm_bo_move_memcpy); +static int drm_memset_io_page(void *dst, unsigned long page) +{ + dst = (void *)((unsigned long)dst + (page << PAGE_SHIFT)); + memset_io(dst, 0, PAGE_SIZE); + return 0; +} + +static int drm_memset_ttm_page(struct drm_ttm *ttm, unsigned long page) +{ + struct page *d = drm_ttm_get_page(ttm, page); + void *dst; + + dst = kmap(d); + if (!dst) + return -ENOMEM; + + memset_io(dst, 0, PAGE_SIZE); + kunmap(d); + return 0; +} + +int drm_bo_move_zero(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_mem_type_manager *man = &dev->bm.man[new_mem->mem_type]; + struct drm_ttm *ttm = bo->ttm; + void *new_iomap; + int ret; + struct drm_bo_mem_reg *old_mem = &bo->mem; + uint64_t save_flags = old_mem->flags; + uint64_t save_proposed_flags = old_mem->proposed_flags; + unsigned long i; + unsigned long page; + + ret = drm_mem_reg_ioremap(dev, new_mem, &new_iomap); + if (ret) + goto out; + + if (new_iomap == NULL && ttm == NULL) + goto out2; + + for (i = 0; i < new_mem->num_pages; ++i) { + if (new_iomap == NULL) + ret = drm_memset_ttm_page(ttm, i); + else + ret = drm_memset_io_page(new_iomap, i); + if (ret) + goto out1; + } + mb(); +out2: + drm_bo_free_old_node(bo); + + *old_mem = *new_mem; + new_mem->mm_node = NULL; + old_mem->proposed_flags = save_proposed_flags; + DRM_FLAG_MASKED(save_flags, new_mem->flags, DRM_BO_MASK_MEMTYPE); + + if ((man->flags & _DRM_FLAG_MEMTYPE_FIXED) && (ttm != NULL)) { + drm_ttm_unbind(ttm); + drm_ttm_destroy(ttm); + bo->ttm = NULL; + } +out1: + drm_mem_reg_iounmap(dev, new_mem, new_iomap); +out: + return ret; +} +EXPORT_SYMBOL(drm_bo_move_zero); + /* * Transfer a buffer object's memory and LRU status to a newly * created object. User-space references remains with the old @@ -561,6 +633,10 @@ int drm_bo_kmap(struct drm_buffer_object *bo, unsigned long start_page, if (ret) return ret; + /* clear the clean flags */ + bo->mem.flags &= ~DRM_BO_FLAG_CLEAN; + bo->mem.proposed_flags &= ~DRM_BO_FLAG_CLEAN; + if (bus_size == 0) { return drm_bo_kmap_ttm(bo, start_page, num_pages, map); } else { diff --git a/linux-core/drm_bufs.c b/linux-core/drm_bufs.c index 75c75c2f..15c426cb 100644 --- a/linux-core/drm_bufs.c +++ b/linux-core/drm_bufs.c @@ -52,9 +52,9 @@ struct drm_map_list *drm_find_matching_map(struct drm_device *dev, drm_local_map { struct drm_map_list *entry; list_for_each_entry(entry, &dev->maplist, head) { - if (entry->map && map->type == entry->map->type && - ((entry->map->offset == map->offset) || - (map->type == _DRM_SHM && map->flags==_DRM_CONTAINS_LOCK))) { + if (entry->map && (entry->master == dev->primary->master) && (map->type == entry->map->type) && + ((entry->map->offset == map->offset) || + ((map->type == _DRM_SHM) && (map->flags&_DRM_CONTAINS_LOCK)))) { return entry; } } @@ -209,12 +209,12 @@ static int drm_addmap_core(struct drm_device *dev, unsigned int offset, map->offset = (unsigned long)map->handle; if (map->flags & _DRM_CONTAINS_LOCK) { /* Prevent a 2nd X Server from creating a 2nd lock */ - if (dev->lock.hw_lock != NULL) { + if (dev->primary->master->lock.hw_lock != NULL) { vfree(map->handle); drm_free(map, sizeof(*map), DRM_MEM_MAPS); return -EBUSY; } - dev->sigdata.lock = dev->lock.hw_lock = map->handle; /* Pointer to lock */ + dev->sigdata.lock = dev->primary->master->lock.hw_lock = map->handle; /* Pointer to lock */ } break; case _DRM_AGP: { @@ -318,6 +318,7 @@ static int drm_addmap_core(struct drm_device *dev, unsigned int offset, list->user_token = list->hash.key << PAGE_SHIFT; mutex_unlock(&dev->struct_mutex); + list->master = dev->primary->master; *maplist = list; return 0; } @@ -344,7 +345,7 @@ int drm_addmap_ioctl(struct drm_device *dev, void *data, struct drm_map_list *maplist; int err; - if (!(capable(CAP_SYS_ADMIN) || map->type == _DRM_AGP)) + if (!(capable(CAP_SYS_ADMIN) || map->type == _DRM_AGP || map->type == _DRM_SHM)) return -EPERM; err = drm_addmap_core(dev, map->offset, map->size, map->type, @@ -379,10 +380,12 @@ int drm_rmmap_locked(struct drm_device *dev, drm_local_map_t *map) struct drm_map_list *r_list = NULL, *list_t; drm_dma_handle_t dmah; int found = 0; + struct drm_master *master; /* Find the list entry for the map and remove it */ list_for_each_entry_safe(r_list, list_t, &dev->maplist, head) { if (r_list->map == map) { + master = r_list->master; list_del(&r_list->head); drm_ht_remove_key(&dev->map_hash, r_list->user_token >> PAGE_SHIFT); @@ -412,6 +415,13 @@ int drm_rmmap_locked(struct drm_device *dev, drm_local_map_t *map) break; case _DRM_SHM: vfree(map->handle); + if (master) { + if (dev->sigdata.lock == master->lock.hw_lock) + dev->sigdata.lock = NULL; + master->lock.hw_lock = NULL; /* SHM removed */ + master->lock.file_priv = NULL; + wake_up_interruptible(&master->lock.lock_queue); + } break; case _DRM_AGP: case _DRM_SCATTER_GATHER: @@ -1518,6 +1528,7 @@ int drm_mapbufs(struct drm_device *dev, void *data, dev->buf_use++; /* Can't allocate more after this call */ spin_unlock(&dev->count_lock); + DRM_DEBUG("dma buf count %d, req count %d\n", request->count, dma->buf_count); if (request->count >= dma->buf_count) { if ((drm_core_has_AGP(dev) && (dma->flags & _DRM_DMA_USE_AGP)) || (drm_core_check_feature(dev, DRIVER_SG) @@ -1528,6 +1539,7 @@ int drm_mapbufs(struct drm_device *dev, void *data, unsigned long token = dev->agp_buffer_token; if (!map) { + DRM_DEBUG("No map\n"); retcode = -EINVAL; goto done; } @@ -1545,6 +1557,7 @@ int drm_mapbufs(struct drm_device *dev, void *data, up_write(¤t->mm->mmap_sem); } if (virtual > -1024UL) { + DRM_DEBUG("mmap failed\n"); /* Real error */ retcode = (signed long)virtual; goto done; diff --git a/linux-core/drm_compat.c b/linux-core/drm_compat.c index c4ebc2fa..d06b9d24 100644 --- a/linux-core/drm_compat.c +++ b/linux-core/drm_compat.c @@ -217,7 +217,7 @@ static struct page *drm_bo_vm_fault(struct vm_area_struct *vma, mutex_lock(&bo->mutex); - err = drm_bo_wait(bo, 0, 1, 0); + err = drm_bo_wait(bo, 0, 1, 0, 1); if (err) { data->type = (err == -EAGAIN) ? VM_FAULT_MINOR : VM_FAULT_SIGBUS; @@ -730,20 +730,68 @@ void *idr_replace(struct idr *idp, void *ptr, int id) EXPORT_SYMBOL(idr_replace); #endif -#if defined(DRM_KMAP_ATOMIC_PROT_PFN) -#define drm_kmap_get_fixmap_pte(vaddr) \ - pte_offset_kernel(pmd_offset(pud_offset(pgd_offset_k(vaddr), vaddr), (vaddr)), (vaddr)) +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) +static __inline__ unsigned long __round_jiffies(unsigned long j, int cpu) +{ + int rem; + unsigned long original = j; + + j += cpu * 3; + + rem = j % HZ; + + if (rem < HZ/4) /* round down */ + j = j - rem; + else /* round up */ + j = j - rem + HZ; + + /* now that we have rounded, subtract the extra skew again */ + j -= cpu * 3; + + if (j <= jiffies) /* rounding ate our timeout entirely; */ + return original; + return j; +} + +static __inline__ unsigned long __round_jiffies_relative(unsigned long j, int cpu) +{ + return __round_jiffies(j + jiffies, cpu) - jiffies; +} + +unsigned long round_jiffies_relative(unsigned long j) +{ + return __round_jiffies_relative(j, raw_smp_processor_id()); +} +EXPORT_SYMBOL(round_jiffies_relative); +#endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) +struct pci_dev * pci_get_bus_and_slot(unsigned int bus, unsigned int devfn) +{ + struct pci_dev *dev = NULL; + + while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { + if (pci_domain_nr(dev->bus) == 0 && + (dev->bus->number == bus && dev->devfn == devfn)) + return dev; + } + return NULL; +} +EXPORT_SYMBOL(pci_get_bus_and_slot); +#endif + +#if defined(DRM_KMAP_ATOMIC_PROT_PFN) void *kmap_atomic_prot_pfn(unsigned long pfn, enum km_type type, pgprot_t protection) { enum fixed_addresses idx; unsigned long vaddr; static pte_t *km_pte; + int level; static int initialized = 0; if (unlikely(!initialized)) { - km_pte = drm_kmap_get_fixmap_pte(__fix_to_virt(FIX_KMAP_BEGIN)); + km_pte = lookup_address(__fix_to_virt(FIX_KMAP_BEGIN), &level); initialized = 1; } @@ -756,7 +804,6 @@ void *kmap_atomic_prot_pfn(unsigned long pfn, enum km_type type, } EXPORT_SYMBOL(kmap_atomic_prot_pfn); - #endif #ifdef DRM_FULL_MM_COMPAT diff --git a/linux-core/drm_compat.h b/linux-core/drm_compat.h index bc4d2e58..585350f4 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 @@ -328,7 +335,24 @@ void *idr_replace(struct idr *idp, void *ptr, int id); #endif #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) -typedef _Bool bool; +extern unsigned long round_jiffies_relative(unsigned long j); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) +extern struct pci_dev * pci_get_bus_and_slot(unsigned int bus, unsigned int devfn); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) +static inline int kobject_uevent_env(struct kobject *kobj, + enum kobject_action action, + char *envp[]) +{ + return 0; +} +#endif + +#ifndef PM_EVENT_PRETHAW +#define PM_EVENT_PRETHAW 3 #endif @@ -380,6 +404,14 @@ extern struct page *drm_vm_sg_nopage(struct vm_area_struct *vma, unsigned long address, int *type); #endif +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,26) +#define drm_on_each_cpu(handler, data, wait) \ + on_each_cpu(handler, data, wait) +#else +#define drm_on_each_cpu(handler, data, wait) \ + on_each_cpu(handler, data, wait, 1) +#endif + #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)) #define drm_core_ioremap_wc drm_core_ioremap #endif diff --git a/linux-core/drm_context.c b/linux-core/drm_context.c index 83ad291e..f3d69ff7 100644 --- a/linux-core/drm_context.c +++ b/linux-core/drm_context.c @@ -257,12 +257,13 @@ static int drm_context_switch(struct drm_device *dev, int old, int new) * hardware lock is held, clears the drm_device::context_flag and wakes up * drm_device::context_wait. */ -static int drm_context_switch_complete(struct drm_device *dev, int new) +static int drm_context_switch_complete(struct drm_device *dev, + struct drm_file *file_priv, int new) { dev->last_context = new; /* PRE/POST: This is the _only_ writer. */ dev->last_switch = jiffies; - if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + if (!_DRM_LOCK_IS_HELD(file_priv->master->lock.hw_lock->lock)) { DRM_ERROR("Lock isn't held after context switch\n"); } @@ -421,7 +422,7 @@ int drm_newctx(struct drm_device *dev, void *data, struct drm_ctx *ctx = data; DRM_DEBUG("%d\n", ctx->handle); - drm_context_switch_complete(dev, ctx->handle); + drm_context_switch_complete(dev, file_priv, ctx->handle); return 0; } @@ -443,9 +444,6 @@ int drm_rmctx(struct drm_device *dev, void *data, struct drm_ctx *ctx = data; DRM_DEBUG("%d\n", ctx->handle); - if (ctx->handle == DRM_KERNEL_CONTEXT + 1) { - file_priv->remove_auth_on_close = 1; - } if (ctx->handle != DRM_KERNEL_CONTEXT) { if (dev->driver->context_dtor) dev->driver->context_dtor(dev, ctx->handle); diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c new file mode 100644 index 00000000..aa0c4a95 --- /dev/null +++ b/linux-core/drm_crtc.c @@ -0,0 +1,2383 @@ +/* + * Copyright (c) 2006-2007 Intel Corporation + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> + * Copyright (c) 2008 Red Hat Inc. + * + * DRM core CRTC related functions + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + * + * Authors: + * Keith Packard + * Eric Anholt <eric@anholt.net> + * Dave Airlie <airlied@linux.ie> + * Jesse Barnes <jesse.barnes@intel.com> + */ +#include <linux/list.h> +#include "drm.h" +#include "drmP.h" +#include "drm_crtc.h" + +struct drm_prop_enum_list { + int type; + char *name; +}; + +/* + * Global properties + */ +static struct drm_prop_enum_list drm_dpms_enum_list[] = +{ { DRM_MODE_DPMS_ON, "On" }, + { DRM_MODE_DPMS_STANDBY, "Standby" }, + { DRM_MODE_DPMS_SUSPEND, "Suspend" }, + { DRM_MODE_DPMS_OFF, "Off" } +}; + +char *drm_get_dpms_name(int val) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(drm_dpms_enum_list); i++) + if (drm_dpms_enum_list[i].type == val) + return drm_dpms_enum_list[i].name; + + return "unknown"; +} + +/* + * Optional properties + */ +static struct drm_prop_enum_list drm_scaling_mode_enum_list[] = +{ + { DRM_MODE_SCALE_NON_GPU, "Non-GPU" }, + { DRM_MODE_SCALE_FULLSCREEN, "Fullscreen" }, + { DRM_MODE_SCALE_NO_SCALE, "No scale" }, + { DRM_MODE_SCALE_ASPECT, "Aspect" }, +}; + +static struct drm_prop_enum_list drm_dithering_mode_enum_list[] = +{ + { DRM_MODE_DITHERING_OFF, "Off" }, + { DRM_MODE_DITHERING_ON, "On" }, +}; + +/* + * Non-global properties, but "required" for certain connectors. + */ +static struct drm_prop_enum_list drm_select_subconnector_enum_list[] = +{ + { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ + { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ + { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ + { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ + { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ + { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ +}; + +char *drm_get_select_subconnector_name(int val) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(drm_select_subconnector_enum_list); i++) + if (drm_select_subconnector_enum_list[i].type == val) + return drm_select_subconnector_enum_list[i].name; + + return "unknown"; +} + +static struct drm_prop_enum_list drm_subconnector_enum_list[] = +{ + { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ + { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ + { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ + { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ + { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ + { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ +}; + +char *drm_get_subconnector_name(int val) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(drm_subconnector_enum_list); i++) + if (drm_subconnector_enum_list[i].type == val) + return drm_subconnector_enum_list[i].name; + + return "unknown"; +} + +struct drm_conn_prop_enum_list { + int type; + char *name; + int count; +}; + +/* + * Connector and encoder types. + */ +static struct drm_conn_prop_enum_list drm_connector_enum_list[] = +{ { DRM_MODE_CONNECTOR_Unknown, "Unknown", 0 }, + { DRM_MODE_CONNECTOR_VGA, "VGA", 0 }, + { DRM_MODE_CONNECTOR_DVII, "DVI-I", 0 }, + { DRM_MODE_CONNECTOR_DVID, "DVI-D", 0 }, + { DRM_MODE_CONNECTOR_DVIA, "DVI-A", 0 }, + { DRM_MODE_CONNECTOR_Composite, "Composite", 0 }, + { DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO", 0 }, + { DRM_MODE_CONNECTOR_LVDS, "LVDS", 0 }, + { DRM_MODE_CONNECTOR_Component, "Component", 0 }, + { DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN", 0 }, + { DRM_MODE_CONNECTOR_DisplayPort, "DisplayPort", 0 }, + { DRM_MODE_CONNECTOR_HDMIA, "HDMI Type A", 0 }, + { DRM_MODE_CONNECTOR_HDMIB, "HDMI Type B", 0 }, +}; + +static struct drm_prop_enum_list drm_encoder_enum_list[] = +{ { DRM_MODE_ENCODER_NONE, "None" }, + { DRM_MODE_ENCODER_DAC, "DAC" }, + { DRM_MODE_ENCODER_TMDS, "TMDS" }, + { DRM_MODE_ENCODER_LVDS, "LVDS" }, + { DRM_MODE_ENCODER_TVDAC, "TV" }, +}; + +char *drm_get_encoder_name(struct drm_encoder *encoder) +{ + static char buf[32]; + + snprintf(buf, 32, "%s-%d", drm_encoder_enum_list[encoder->encoder_type].name, + encoder->base.id); + return buf; +} + +char *drm_get_connector_name(struct drm_connector *connector) +{ + static char buf[32]; + + snprintf(buf, 32, "%s-%d", drm_connector_enum_list[connector->connector_type].name, + connector->connector_type_id); + return buf; +} +EXPORT_SYMBOL(drm_get_connector_name); + +char *drm_get_connector_status_name(enum drm_connector_status status) +{ + if (status == connector_status_connected) + return "connected"; + else if (status == connector_status_disconnected) + return "disconnected"; + else + return "unknown"; +} + +/** + * drm_idr_get - allocate a new identifier + * @dev: DRM device + * @ptr: object pointer, used to generate unique ID + * + * LOCKING: + * Caller must hold DRM mode_config lock. + * + * Create a unique identifier based on @ptr in @dev's identifier space. Used + * for tracking modes, CRTCs and connectors. + * + * RETURNS: + * New unique (relative to other objects in @dev) integer identifier for the + * object. + */ +static int drm_mode_object_get(struct drm_device *dev, struct drm_mode_object *obj, uint32_t obj_type) +{ + int new_id = 0; + int ret; +again: + if (idr_pre_get(&dev->mode_config.crtc_idr, GFP_KERNEL) == 0) { + DRM_ERROR("Ran out memory getting a mode number\n"); + return -EINVAL; + } + + ret = idr_get_new_above(&dev->mode_config.crtc_idr, obj, 1, &new_id); + if (ret == -EAGAIN) + goto again; + + obj->id = new_id; + obj->type = obj_type; + return 0; +} + +/** + * drm_mode_object_put - free an identifer + * @dev: DRM device + * @id: ID to free + * + * LOCKING: + * Caller must hold DRM mode_config lock. + * + * Free @id from @dev's unique identifier pool. + */ +static void drm_mode_object_put(struct drm_device *dev, struct drm_mode_object *object) +{ + idr_remove(&dev->mode_config.crtc_idr, object->id); +} + +void *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type) +{ + struct drm_mode_object *obj; + + obj = idr_find(&dev->mode_config.crtc_idr, id); + if (!obj || (obj->type != type) || (obj->id != id)) + return NULL; + + return obj; +} +EXPORT_SYMBOL(drm_mode_object_find); + +/** + * drm_crtc_from_fb - find the CRTC structure associated with an fb + * @dev: DRM device + * @fb: framebuffer in question + * + * LOCKING: + * Caller must hold mode_config lock. + * + * Find CRTC in the mode_config structure that matches @fb. + * + * RETURNS: + * Pointer to the CRTC or NULL if it wasn't found. + */ +struct drm_crtc *drm_crtc_from_fb(struct drm_device *dev, + struct drm_framebuffer *fb) +{ + struct drm_crtc *crtc; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (crtc->fb == fb) + return crtc; + } + return NULL; +} + +/** + * drm_framebuffer_create - create a new framebuffer object + * @dev: DRM device + * + * LOCKING: + * Caller must hold mode config lock. + * + * Creates a new framebuffer objects and adds it to @dev's DRM mode_config. + * + * RETURNS: + * Pointer to new framebuffer or NULL on error. + */ +struct drm_framebuffer *drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, + const struct drm_framebuffer_funcs *funcs) +{ + if(drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB)) + return NULL; + + fb->dev = dev; + fb->funcs = funcs; + dev->mode_config.num_fb++; + list_add(&fb->head, &dev->mode_config.fb_list); + + return fb; +} +EXPORT_SYMBOL(drm_framebuffer_init); + +/** + * drm_framebuffer_cleanup - remove a framebuffer object + * @fb: framebuffer to remove + * + * LOCKING: + * Caller must hold mode config lock. + * + * Scans all the CRTCs in @dev's mode_config. If they're using @fb, removes + * it, setting it to NULL. + */ +void drm_framebuffer_cleanup(struct drm_framebuffer *fb) +{ + struct drm_device *dev = fb->dev; + struct drm_crtc *crtc; + + /* remove from any CRTC */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (crtc->fb == fb) + crtc->fb = NULL; + } + + drm_mode_object_put(dev, &fb->base); + list_del(&fb->head); + dev->mode_config.num_fb--; +} +EXPORT_SYMBOL(drm_framebuffer_cleanup); + +/** + * drm_crtc_init - Initialise a new CRTC object + * @dev: DRM device + * @crtc: CRTC object to init + * @funcs: callbacks for the new CRTC + * + * LOCKING: + * Caller must hold mode config lock. + * + * Inits a new object created as base part of an driver crtc object. + */ +void drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, + const struct drm_crtc_funcs *funcs) +{ + crtc->dev = dev; + crtc->funcs = funcs; + + drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); + + list_add_tail(&crtc->head, &dev->mode_config.crtc_list); + dev->mode_config.num_crtc++; +} +EXPORT_SYMBOL(drm_crtc_init); + +/** + * drm_crtc_cleanup - Cleans up the core crtc usage. + * @crtc: CRTC to cleanup + * + * LOCKING: + * Caller must hold mode config lock. + * + * Cleanup @crtc. Removes from drm modesetting space + * does NOT free object, caller does that. + */ +void drm_crtc_cleanup(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + + if (crtc->gamma_store) { + kfree(crtc->gamma_store); + crtc->gamma_store = NULL; + } + + drm_mode_object_put(dev, &crtc->base); + list_del(&crtc->head); + dev->mode_config.num_crtc--; +} +EXPORT_SYMBOL(drm_crtc_cleanup); + +/** + * drm_mode_probed_add - add a mode to the specified connector's probed mode list + * @connector: connector the new mode + * @mode: mode data + * + * LOCKING: + * Caller must hold mode config lock. + * + * Add @mode to @connector's mode list for later use. + */ +void drm_mode_probed_add(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + list_add(&mode->head, &connector->probed_modes); +} +EXPORT_SYMBOL(drm_mode_probed_add); + +/** + * drm_mode_remove - remove and free a mode + * @connector: connector list to modify + * @mode: mode to remove + * + * LOCKING: + * Caller must hold mode config lock. + * + * Remove @mode from @connector's mode list, then free it. + */ +void drm_mode_remove(struct drm_connector *connector, struct drm_display_mode *mode) +{ + list_del(&mode->head); + kfree(mode); +} +EXPORT_SYMBOL(drm_mode_remove); + +/** + * drm_connector_init - Init a preallocated connector + * @dev: DRM device + * @connector: the connector to init + * @funcs: callbacks for this connector + * @name: user visible name of the connector + * + * LOCKING: + * Caller must hold @dev's mode_config lock. + * + * Initialises a preallocated connector. Connectors should be + * subclassed as part of driver connector objects. + */ +void drm_connector_init(struct drm_device *dev, + struct drm_connector *connector, + const struct drm_connector_funcs *funcs, + int connector_type) +{ + connector->dev = dev; + connector->funcs = funcs; + drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR); + connector->connector_type = connector_type; + connector->connector_type_id = ++drm_connector_enum_list[connector_type].count; /* TODO */ + INIT_LIST_HEAD(&connector->user_modes); + INIT_LIST_HEAD(&connector->probed_modes); + INIT_LIST_HEAD(&connector->modes); + connector->edid_blob_ptr = NULL; + /* randr_connector? */ + /* connector_set_monitor(connector)? */ + /* check for connector_ignored(connector)? */ + + mutex_lock(&dev->mode_config.mutex); + list_add_tail(&connector->head, &dev->mode_config.connector_list); + dev->mode_config.num_connector++; + + drm_connector_attach_property(connector, dev->mode_config.edid_property, 0); + + drm_connector_attach_property(connector, dev->mode_config.dpms_property, 0); + + mutex_unlock(&dev->mode_config.mutex); +} +EXPORT_SYMBOL(drm_connector_init); + +/** + * drm_connector_cleanup - cleans up an initialised connector + * @connector: connector to cleanup + * + * LOCKING: + * Caller must hold @dev's mode_config lock. + * + * Cleans up the connector but doesn't free the object. + */ +void drm_connector_cleanup(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_display_mode *mode, *t; + + list_for_each_entry_safe(mode, t, &connector->probed_modes, head) + drm_mode_remove(connector, mode); + + list_for_each_entry_safe(mode, t, &connector->modes, head) + drm_mode_remove(connector, mode); + + list_for_each_entry_safe(mode, t, &connector->user_modes, head) + drm_mode_remove(connector, mode); + + mutex_lock(&dev->mode_config.mutex); + drm_mode_object_put(dev, &connector->base); + list_del(&connector->head); + mutex_unlock(&dev->mode_config.mutex); +} +EXPORT_SYMBOL(drm_connector_cleanup); + +void drm_encoder_init(struct drm_device *dev, + struct drm_encoder *encoder, + const struct drm_encoder_funcs *funcs, + int encoder_type) +{ + encoder->dev = dev; + + drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER); + encoder->encoder_type = encoder_type; + encoder->funcs = funcs; + + mutex_lock(&dev->mode_config.mutex); + list_add_tail(&encoder->head, &dev->mode_config.encoder_list); + dev->mode_config.num_encoder++; + + mutex_unlock(&dev->mode_config.mutex); +} +EXPORT_SYMBOL(drm_encoder_init); + +void drm_encoder_cleanup(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + mutex_lock(&dev->mode_config.mutex); + drm_mode_object_put(dev, &encoder->base); + list_del(&encoder->head); + mutex_unlock(&dev->mode_config.mutex); +} +EXPORT_SYMBOL(drm_encoder_cleanup); + +/** + * drm_mode_create - create a new display mode + * @dev: DRM device + * + * LOCKING: + * None. + * + * Create a new drm_display_mode, give it an ID, and return it. + * + * RETURNS: + * Pointer to new mode on success, NULL on error. + */ +struct drm_display_mode *drm_mode_create(struct drm_device *dev) +{ + struct drm_display_mode *nmode; + + nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL); + if (!nmode) + return NULL; + + drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE); + return nmode; +} +EXPORT_SYMBOL(drm_mode_create); + +/** + * drm_mode_destroy - remove a mode + * @dev: DRM device + * @mode: mode to remove + * + * LOCKING: + * Caller must hold mode config lock. + * + * Free @mode's unique identifier, then free it. + */ +void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode) +{ + drm_mode_object_put(dev, &mode->base); + + kfree(mode); +} +EXPORT_SYMBOL(drm_mode_destroy); + +static int drm_mode_create_standard_connector_properties(struct drm_device *dev) +{ + int i; + + /* + * Standard properties (apply to all connectors) + */ + dev->mode_config.edid_property = + drm_property_create(dev, DRM_MODE_PROP_BLOB | DRM_MODE_PROP_IMMUTABLE, + "EDID", 0); + + dev->mode_config.dpms_property = + drm_property_create(dev, DRM_MODE_PROP_ENUM, + "DPMS", ARRAY_SIZE(drm_dpms_enum_list)); + for (i = 0; i < ARRAY_SIZE(drm_dpms_enum_list); i++) + drm_property_add_enum(dev->mode_config.dpms_property, i, drm_dpms_enum_list[i].type, drm_dpms_enum_list[i].name); + + return 0; +} + +/** + * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties + * @dev: DRM device + * + * Called by a driver the first time a DVI-I connector is made. + */ +int drm_mode_create_dvi_i_properties(struct drm_device *dev) +{ + int i; + + if (dev->mode_config.dvi_i_select_subconnector_property) + return 0; + + dev->mode_config.dvi_i_select_subconnector_property = drm_property_create(dev, DRM_MODE_PROP_ENUM, + "select subconnector", 3); + /* add enum element 0-2 */ + for (i = 0; i < 3; i++) + drm_property_add_enum(dev->mode_config.dvi_i_select_subconnector_property, i, drm_select_subconnector_enum_list[i].type, + drm_select_subconnector_enum_list[i].name); + + /* This is a property which indicates the most likely thing to be connected. */ + dev->mode_config.dvi_i_subconnector_property = drm_property_create(dev, DRM_MODE_PROP_ENUM | DRM_MODE_PROP_IMMUTABLE, + "subconnector", 3); + /* add enum element 0-2 */ + for (i = 0; i < 3; i++) + drm_property_add_enum(dev->mode_config.dvi_i_subconnector_property, i, drm_subconnector_enum_list[i].type, + drm_subconnector_enum_list[i].name); + + return 0; +} +EXPORT_SYMBOL(drm_mode_create_dvi_i_properties); + +/** + * drm_create_tv_properties - create TV specific connector properties + * @dev: DRM device + * @num_modes: number of different TV formats (modes) supported + * @modes: array of pointers to strings containing name of each format + * + * Called by a driver's TV initialization routine, this function creates + * the TV specific connector properties for a given device. Caller is + * responsible for allocating a list of format names and passing them to + * this routine. + */ +int drm_mode_create_tv_properties(struct drm_device *dev, int num_modes, + char *modes[]) +{ + int i; + + if (dev->mode_config.tv_select_subconnector_property) /* already done */ + return 0; + + dev->mode_config.tv_select_subconnector_property = drm_property_create(dev, DRM_MODE_PROP_ENUM, + "select subconnector", 4); + /* add enum element 0 */ + drm_property_add_enum(dev->mode_config.tv_select_subconnector_property, 0, drm_select_subconnector_enum_list[0].type, + drm_select_subconnector_enum_list[0].name); + /* add enum element 3-5 */ + for (i = 1; i < 4; i++) + drm_property_add_enum(dev->mode_config.tv_select_subconnector_property, i, drm_select_subconnector_enum_list[i + 2].type, + drm_select_subconnector_enum_list[i + 2].name); + + /* This is a property which indicates the most likely thing to be connected. */ + dev->mode_config.tv_subconnector_property = drm_property_create(dev, DRM_MODE_PROP_ENUM | DRM_MODE_PROP_IMMUTABLE, + "subconnector", 4); + /* add enum element 0 */ + drm_property_add_enum(dev->mode_config.tv_subconnector_property, 0, drm_subconnector_enum_list[0].type, + drm_subconnector_enum_list[0].name); + /* add enum element 3-5 */ + for (i = 1; i < 4; i++) + drm_property_add_enum(dev->mode_config.tv_subconnector_property, i, drm_subconnector_enum_list[i + 2].type, + drm_subconnector_enum_list[i + 2].name); + + dev->mode_config.tv_left_margin_property = + drm_property_create(dev, DRM_MODE_PROP_RANGE, + "left margin", 2); + dev->mode_config.tv_left_margin_property->values[0] = 0; + dev->mode_config.tv_left_margin_property->values[1] = 100; + + dev->mode_config.tv_right_margin_property = + drm_property_create(dev, DRM_MODE_PROP_RANGE, + "right margin", 2); + dev->mode_config.tv_right_margin_property->values[0] = 0; + dev->mode_config.tv_right_margin_property->values[1] = 100; + + dev->mode_config.tv_top_margin_property = + drm_property_create(dev, DRM_MODE_PROP_RANGE, + "top margin", 2); + dev->mode_config.tv_top_margin_property->values[0] = 0; + dev->mode_config.tv_top_margin_property->values[1] = 100; + + dev->mode_config.tv_bottom_margin_property = + drm_property_create(dev, DRM_MODE_PROP_RANGE, + "bottom margin", 2); + dev->mode_config.tv_bottom_margin_property->values[0] = 0; + dev->mode_config.tv_bottom_margin_property->values[1] = 100; + + dev->mode_config.tv_mode_property = + drm_property_create(dev, DRM_MODE_PROP_ENUM, + "mode", num_modes); + for (i = 0; i < num_modes; i++) + drm_property_add_enum(dev->mode_config.tv_mode_property, i, + i, modes[i]); + + return 0; +} +EXPORT_SYMBOL(drm_mode_create_tv_properties); + +/** + * drm_mode_create_scaling_mode_property - create scaling mode property + * @dev: DRM device + * + * Called by a driver the first time it's needed, must be attached to desired connectors. + */ +int drm_mode_create_scaling_mode_property(struct drm_device *dev) +{ + int i; + + if (dev->mode_config.scaling_mode_property) /* already done */ + return 0; + + dev->mode_config.scaling_mode_property = + drm_property_create(dev, DRM_MODE_PROP_ENUM, + "scaling mode", ARRAY_SIZE(drm_scaling_mode_enum_list)); + for (i = 0; i < ARRAY_SIZE(drm_scaling_mode_enum_list); i++) + drm_property_add_enum(dev->mode_config.scaling_mode_property, i, drm_scaling_mode_enum_list[i].type, drm_scaling_mode_enum_list[i].name); + + return 0; +} +EXPORT_SYMBOL(drm_mode_create_scaling_mode_property); + +/** + * drm_mode_create_dithering_property - create dithering property + * @dev: DRM device + * + * Called by a driver the first time it's needed, must be attached to desired connectors. + */ +int drm_mode_create_dithering_property(struct drm_device *dev) +{ + int i; + + if (dev->mode_config.dithering_mode_property) /* already done */ + return 0; + + dev->mode_config.dithering_mode_property = + drm_property_create(dev, DRM_MODE_PROP_ENUM, + "dithering", ARRAY_SIZE(drm_dithering_mode_enum_list)); + for (i = 0; i < ARRAY_SIZE(drm_dithering_mode_enum_list); i++) + drm_property_add_enum(dev->mode_config.dithering_mode_property, i, drm_dithering_mode_enum_list[i].type, drm_dithering_mode_enum_list[i].name); + + return 0; +} +EXPORT_SYMBOL(drm_mode_create_dithering_property); + +/** + * drm_mode_config_init - initialize DRM mode_configuration structure + * @dev: DRM device + * + * LOCKING: + * None, should happen single threaded at init time. + * + * Initialize @dev's mode_config structure, used for tracking the graphics + * configuration of @dev. + */ +void drm_mode_config_init(struct drm_device *dev) +{ + mutex_init(&dev->mode_config.mutex); + INIT_LIST_HEAD(&dev->mode_config.fb_list); + INIT_LIST_HEAD(&dev->mode_config.fb_kernel_list); + INIT_LIST_HEAD(&dev->mode_config.crtc_list); + INIT_LIST_HEAD(&dev->mode_config.connector_list); + INIT_LIST_HEAD(&dev->mode_config.encoder_list); + INIT_LIST_HEAD(&dev->mode_config.property_list); + INIT_LIST_HEAD(&dev->mode_config.property_blob_list); + idr_init(&dev->mode_config.crtc_idr); + + drm_mode_create_standard_connector_properties(dev); + + /* Just to be sure */ + dev->mode_config.num_fb = 0; + dev->mode_config.num_connector = 0; + dev->mode_config.num_crtc = 0; + dev->mode_config.num_encoder = 0; + dev->mode_config.hotplug_counter = 0; +} +EXPORT_SYMBOL(drm_mode_config_init); + +int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *group) +{ + uint32_t total_objects = 0; + + total_objects += dev->mode_config.num_crtc; + total_objects += dev->mode_config.num_connector; + total_objects += dev->mode_config.num_encoder; + + if (total_objects == 0) + return -EINVAL; + + group->id_list = kzalloc(total_objects * sizeof(uint32_t), GFP_KERNEL); + if (!group->id_list) + return -ENOMEM; + + group->num_crtcs = 0; + group->num_connectors = 0; + group->num_encoders = 0; + return 0; +} + +int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group) +{ + struct drm_crtc *crtc; + struct drm_encoder *encoder; + struct drm_connector *connector; + int ret; + + if ((ret = drm_mode_group_init(dev, group))) + return ret; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + group->id_list[group->num_crtcs++] = crtc->base.id; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) + group->id_list[group->num_crtcs + group->num_encoders++] = encoder->base.id; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) + group->id_list[group->num_crtcs + group->num_encoders + group->num_connectors++] = connector->base.id; + + return 0; +} + +/** + * drm_mode_config_cleanup - free up DRM mode_config info + * @dev: DRM device + * + * LOCKING: + * Caller must hold mode config lock. + * + * Free up all the connectors and CRTCs associated with this DRM device, then + * free up the framebuffers and associated buffer objects. + * + * FIXME: cleanup any dangling user buffer objects too + */ +void drm_mode_config_cleanup(struct drm_device *dev) +{ + struct drm_connector *connector, *ot; + struct drm_crtc *crtc, *ct; + struct drm_encoder *encoder, *enct; + struct drm_framebuffer *fb, *fbt; + struct drm_property *property, *pt; + + list_for_each_entry_safe(encoder, enct, &dev->mode_config.encoder_list, head) { + encoder->funcs->destroy(encoder); + } + + list_for_each_entry_safe(connector, ot, &dev->mode_config.connector_list, head) { + connector->funcs->destroy(connector); + } + + list_for_each_entry_safe(property, pt, &dev->mode_config.property_list, head) { + drm_property_destroy(dev, property); + } + + list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { + fb->funcs->destroy(fb); + } + + list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) { + crtc->funcs->destroy(crtc); + } + +} +EXPORT_SYMBOL(drm_mode_config_cleanup); + +/** + * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo + * @out: drm_mode_modeinfo struct to return to the user + * @in: drm_display_mode to use + * + * LOCKING: + * None. + * + * Convert a drm_display_mode into a drm_mode_modeinfo structure to return to + * the user. + */ +void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, struct drm_display_mode *in) +{ + out->clock = in->clock; + out->hdisplay = in->hdisplay; + out->hsync_start = in->hsync_start; + out->hsync_end = in->hsync_end; + out->htotal = in->htotal; + out->hskew = in->hskew; + out->vdisplay = in->vdisplay; + out->vsync_start = in->vsync_start; + out->vsync_end = in->vsync_end; + out->vtotal = in->vtotal; + out->vscan = in->vscan; + out->vrefresh = in->vrefresh; + out->flags = in->flags; + out->type = in->type; + strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); + out->name[DRM_DISPLAY_MODE_LEN-1] = 0; +} + +/** + * drm_crtc_convert_to_umode - convert a modeinfo into a drm_display_mode + * @out: drm_display_mode to return to the user + * @in: drm_mode_modeinfo to use + * + * LOCKING: + * None. + * + * Convert a drmo_mode_modeinfo into a drm_display_mode structure to return to + * the caller. + */ +void drm_crtc_convert_umode(struct drm_display_mode *out, struct drm_mode_modeinfo *in) +{ + out->clock = in->clock; + out->hdisplay = in->hdisplay; + out->hsync_start = in->hsync_start; + out->hsync_end = in->hsync_end; + out->htotal = in->htotal; + out->hskew = in->hskew; + out->vdisplay = in->vdisplay; + out->vsync_start = in->vsync_start; + out->vsync_end = in->vsync_end; + out->vtotal = in->vtotal; + out->vscan = in->vscan; + out->vrefresh = in->vrefresh; + out->flags = in->flags; + out->type = in->type; + strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); + out->name[DRM_DISPLAY_MODE_LEN-1] = 0; +} + +/** + * drm_mode_getresources - get graphics configuration + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * LOCKING: + * Takes mode config lock. + * + * Construct a set of configuration description structures and return + * them to the user, including CRTC, connector and framebuffer configuration. + * + * Called by the user via ioctl. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_mode_getresources(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_card_res *card_res = data; + struct list_head *lh; + struct drm_framebuffer *fb; + struct drm_connector *connector; + struct drm_crtc *crtc; + struct drm_encoder *encoder; + int ret = 0; + int connector_count = 0; + int crtc_count = 0; + int fb_count = 0; + int encoder_count = 0; + int copied = 0, i; + uint32_t __user *fb_id; + uint32_t __user *crtc_id; + uint32_t __user *connector_id; + uint32_t __user *encoder_id; + struct drm_mode_group *mode_group; + + mutex_lock(&dev->mode_config.mutex); + + /* for the non-control nodes we need to limit the list of resources by IDs in the + group list for this node */ + list_for_each(lh, &file_priv->fbs) + fb_count++; + + mode_group = &file_priv->master->minor->mode_group; + if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { + + list_for_each(lh, &dev->mode_config.crtc_list) + crtc_count++; + + list_for_each(lh, &dev->mode_config.connector_list) + connector_count++; + + list_for_each(lh, &dev->mode_config.encoder_list) + encoder_count++; + } else { + + crtc_count = mode_group->num_crtcs; + connector_count = mode_group->num_connectors; + encoder_count = mode_group->num_encoders; + } + + card_res->max_height = dev->mode_config.max_height; + card_res->min_height = dev->mode_config.min_height; + card_res->max_width = dev->mode_config.max_width; + card_res->min_width = dev->mode_config.min_width; + + /* handle this in 4 parts */ + /* FBs */ + if (card_res->count_fbs >= fb_count) { + copied = 0; + fb_id = (uint32_t *)(unsigned long)card_res->fb_id_ptr; + list_for_each_entry(fb, &file_priv->fbs, head) { + if (put_user(fb->base.id, fb_id + copied)) { + ret = -EFAULT; + goto out; + } + copied++; + } + } + card_res->count_fbs = fb_count; + + /* CRTCs */ + if (card_res->count_crtcs >= crtc_count) { + copied = 0; + crtc_id = (uint32_t *)(unsigned long)card_res->crtc_id_ptr; + if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + DRM_DEBUG("CRTC ID is %d\n", crtc->base.id); + if (put_user(crtc->base.id, crtc_id + copied)) { + ret = -EFAULT; + goto out; + } + copied++; + } + } else { + for (i = 0; i < mode_group->num_crtcs; i++) { + if (put_user(mode_group->id_list[i], crtc_id + copied)) { + ret = -EFAULT; + goto out; + } + copied++; + } + } + } + card_res->count_crtcs = crtc_count; + + /* Encoders */ + if (card_res->count_encoders >= encoder_count) { + copied = 0; + encoder_id = (uint32_t *)(unsigned long)card_res->encoder_id_ptr; + if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { + list_for_each_entry(encoder, &dev->mode_config.encoder_list, + head) { + DRM_DEBUG("ENCODER ID is %d\n", encoder->base.id); + if (put_user(encoder->base.id, encoder_id + copied)) { + ret = -EFAULT; + goto out; + } + copied++; + } + } else { + for (i = mode_group->num_crtcs; i < mode_group->num_crtcs + mode_group->num_encoders; i++) { + if (put_user(mode_group->id_list[i], encoder_id + copied)) { + ret = -EFAULT; + goto out; + } + copied++; + } + + } + } + card_res->count_encoders = encoder_count; + + /* Connectors */ + if (card_res->count_connectors >= connector_count) { + copied = 0; + connector_id = (uint32_t *)(unsigned long)card_res->connector_id_ptr; + if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { + list_for_each_entry(connector, &dev->mode_config.connector_list, + head) { + DRM_DEBUG("CONNECTOR ID is %d\n", connector->base.id); + if (put_user(connector->base.id, connector_id + copied)) { + ret = -EFAULT; + goto out; + } + copied++; + } + } else { + int start = mode_group->num_crtcs + mode_group->num_encoders; + for (i = start; i < start + mode_group->num_connectors; i++) { + if (put_user(mode_group->id_list[i], connector_id + copied)) { + ret = -EFAULT; + goto out; + } + copied++; + } + } + } + card_res->count_connectors = connector_count; + + DRM_DEBUG("Counted %d %d %d\n", card_res->count_crtcs, + card_res->count_connectors, card_res->count_encoders); + +out: + mutex_unlock(&dev->mode_config.mutex); + return ret; +} + +/** + * drm_mode_getcrtc - get CRTC configuration + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * LOCKING: + * Caller? (FIXME) + * + * Construct a CRTC configuration structure to return to the user. + * + * Called by the user via ioctl. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_mode_getcrtc(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_crtc *crtc_resp = data; + struct drm_crtc *crtc; + struct drm_mode_object *obj; + int ret = 0; + + mutex_lock(&dev->mode_config.mutex); + + obj = drm_mode_object_find(dev, crtc_resp->crtc_id, DRM_MODE_OBJECT_CRTC); + if (!obj) { + ret = -EINVAL; + goto out; + } + crtc = obj_to_crtc(obj); + + crtc_resp->x = crtc->x; + crtc_resp->y = crtc->y; + crtc_resp->gamma_size = crtc->gamma_size; + if (crtc->fb) + crtc_resp->fb_id = crtc->fb->base.id; + else + crtc_resp->fb_id = 0; + + if (crtc->enabled) { + + drm_crtc_convert_to_umode(&crtc_resp->mode, &crtc->mode); + crtc_resp->mode_valid = 1; + + } else { + crtc_resp->mode_valid = 0; + } + +out: + mutex_unlock(&dev->mode_config.mutex); + return ret; +} + +/** + * drm_mode_getconnector - get connector configuration + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * LOCKING: + * Caller? (FIXME) + * + * Construct a connector configuration structure to return to the user. + * + * Called by the user via ioctl. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_mode_getconnector(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_get_connector *out_resp = data; + struct drm_mode_object *obj; + struct drm_connector *connector; + struct drm_display_mode *mode; + int mode_count = 0; + int props_count = 0; + int encoders_count = 0; + int ret = 0; + int copied = 0; + int i; + struct drm_mode_modeinfo u_mode; + struct drm_mode_modeinfo __user *mode_ptr; + uint32_t __user *prop_ptr; + uint64_t __user *prop_values; + uint32_t __user *encoder_ptr; + + memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); + + DRM_DEBUG("connector id %d:\n", out_resp->connector_id); + + mutex_lock(&dev->mode_config.mutex); + + obj = drm_mode_object_find(dev, out_resp->connector_id, DRM_MODE_OBJECT_CONNECTOR); + if (!obj) { + ret = -EINVAL; + goto out; + } + connector = obj_to_connector(obj); + + for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { + if (connector->property_ids[i] != 0) { + props_count++; + } + } + + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { + if (connector->encoder_ids[i] != 0) { + encoders_count++; + } + } + + if (out_resp->count_modes == 0) { + connector->funcs->fill_modes(connector, dev->mode_config.max_width, dev->mode_config.max_height); + } + + /* delayed so we get modes regardless of pre-fill_modes state */ + list_for_each_entry(mode, &connector->modes, head) + mode_count++; + + out_resp->connector_id = connector->base.id; + out_resp->connector_type = connector->connector_type; + out_resp->connector_type_id = connector->connector_type_id; + out_resp->mm_width = connector->display_info.width_mm; + out_resp->mm_height = connector->display_info.height_mm; + out_resp->subpixel = connector->display_info.subpixel_order; + out_resp->connection = connector->status; + if (connector->encoder) + out_resp->encoder_id = connector->encoder->base.id; + else + out_resp->encoder_id = 0; + + /* this ioctl is called twice, once to determine how much space is needed, and the 2nd time to fill it */ + if ((out_resp->count_modes >= mode_count) && mode_count) { + copied = 0; + mode_ptr = (struct drm_mode_modeinfo *)(unsigned long)out_resp->modes_ptr; + list_for_each_entry(mode, &connector->modes, head) { + drm_crtc_convert_to_umode(&u_mode, mode); + if (copy_to_user(mode_ptr + copied, + &u_mode, sizeof(u_mode))) { + ret = -EFAULT; + goto out; + } + copied++; + } + } + out_resp->count_modes = mode_count; + + if ((out_resp->count_props >= props_count) && props_count) { + copied = 0; + prop_ptr = (uint32_t *)(unsigned long)(out_resp->props_ptr); + prop_values = (uint64_t *)(unsigned long)(out_resp->prop_values_ptr); + for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { + if (connector->property_ids[i] != 0) { + if (put_user(connector->property_ids[i], prop_ptr + copied)) { + ret = -EFAULT; + goto out; + } + + if (put_user(connector->property_values[i], prop_values + copied)) { + ret = -EFAULT; + goto out; + } + copied++; + } + } + } + out_resp->count_props = props_count; + + if ((out_resp->count_encoders >= encoders_count) && encoders_count) { + copied = 0; + encoder_ptr = (uint32_t *)(unsigned long)(out_resp->encoders_ptr); + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { + if (connector->encoder_ids[i] != 0) { + if (put_user(connector->encoder_ids[i], encoder_ptr + copied)) { + ret = -EFAULT; + goto out; + } + copied++; + } + } + } + out_resp->count_encoders = encoders_count; + +out: + mutex_unlock(&dev->mode_config.mutex); + return ret; +} + +int drm_mode_getencoder(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_get_encoder *enc_resp = data; + struct drm_mode_object *obj; + struct drm_encoder *encoder; + int ret = 0; + + mutex_lock(&dev->mode_config.mutex); + obj = drm_mode_object_find(dev, enc_resp->encoder_id, DRM_MODE_OBJECT_ENCODER); + if (!obj) { + ret = -EINVAL; + goto out; + } + encoder = obj_to_encoder(obj); + + if (encoder->crtc) + enc_resp->crtc_id = encoder->crtc->base.id; + else + enc_resp->crtc_id = 0; + enc_resp->encoder_type = encoder->encoder_type; + enc_resp->encoder_id = encoder->base.id; + enc_resp->possible_crtcs = encoder->possible_crtcs; + enc_resp->possible_clones = encoder->possible_clones; + +out: + mutex_unlock(&dev->mode_config.mutex); + return ret; +} + +/** + * drm_mode_setcrtc - set CRTC configuration + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * LOCKING: + * Caller? (FIXME) + * + * Build a new CRTC configuration based on user request. + * + * Called by the user via ioctl. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_mode_setcrtc(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_crtc *crtc_req = data; + struct drm_mode_object *obj; + struct drm_crtc *crtc, *crtcfb; + struct drm_connector **connector_set = NULL, *connector; + struct drm_framebuffer *fb = NULL; + struct drm_display_mode *mode = NULL; + struct drm_mode_set set; + uint32_t __user *set_connectors_ptr; + int ret = 0; + int i; + + mutex_lock(&dev->mode_config.mutex); + obj = drm_mode_object_find(dev, crtc_req->crtc_id, DRM_MODE_OBJECT_CRTC); + if (!obj) { + DRM_DEBUG("Unknown CRTC ID %d\n", crtc_req->crtc_id); + ret = -EINVAL; + goto out; + } + crtc = obj_to_crtc(obj); + + if (crtc_req->mode_valid) { + /* If we have a mode we need a framebuffer. */ + /* If we pass -1, set the mode with the currently bound fb */ + if (crtc_req->fb_id == -1) { + list_for_each_entry(crtcfb, &dev->mode_config.crtc_list, head) { + if (crtcfb == crtc) { + DRM_DEBUG("Using current fb for setmode\n"); + fb = crtc->fb; + } + } + } else { + obj = drm_mode_object_find(dev, crtc_req->fb_id, DRM_MODE_OBJECT_FB); + if (!obj) { + DRM_DEBUG("Unknown FB ID%d\n", crtc_req->fb_id); + ret = -EINVAL; + goto out; + } + fb = obj_to_fb(obj); + } + + mode = drm_mode_create(dev); + drm_crtc_convert_umode(mode, &crtc_req->mode); + drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); + } + + if (crtc_req->count_connectors == 0 && mode) { + DRM_DEBUG("Count connectors is 0 but mode set\n"); + ret = -EINVAL; + goto out; + } + + if (crtc_req->count_connectors > 0 && !mode && !fb) { + DRM_DEBUG("Count connectors is %d but no mode or fb set\n", crtc_req->count_connectors); + ret = -EINVAL; + goto out; + } + + if (crtc_req->count_connectors > 0) { + u32 out_id; + /* Maybe we should check that count_connectors is a sensible value. */ + connector_set = kmalloc(crtc_req->count_connectors * + sizeof(struct drm_connector *), GFP_KERNEL); + if (!connector_set) { + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < crtc_req->count_connectors; i++) { + set_connectors_ptr = (uint32_t *)(unsigned long)crtc_req->set_connectors_ptr; + if (get_user(out_id, &set_connectors_ptr[i])) { + ret = -EFAULT; + goto out; + } + + obj = drm_mode_object_find(dev, out_id, DRM_MODE_OBJECT_CONNECTOR); + if (!obj) { + DRM_DEBUG("Connector id %d unknown\n", out_id); + ret = -EINVAL; + goto out; + } + connector = obj_to_connector(obj); + + connector_set[i] = connector; + } + } + + set.crtc = crtc; + set.x = crtc_req->x; + set.y = crtc_req->y; + set.mode = mode; + set.connectors = connector_set; + set.num_connectors = crtc_req->count_connectors; + set.fb = fb; + ret = crtc->funcs->set_config(&set); + +out: + kfree(connector_set); + mutex_unlock(&dev->mode_config.mutex); + return ret; +} + +int drm_mode_cursor_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_cursor *req = data; + struct drm_mode_object *obj; + struct drm_crtc *crtc; + int ret = 0; + + DRM_DEBUG("\n"); + + if (!req->flags) { + DRM_ERROR("no operation set\n"); + return -EINVAL; + } + + mutex_lock(&dev->mode_config.mutex); + obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC); + if (!obj) { + DRM_DEBUG("Unknown CRTC ID %d\n", req->crtc_id); + ret = -EINVAL; + goto out; + } + crtc = obj_to_crtc(obj); + + if (req->flags & DRM_MODE_CURSOR_BO) { + /* Turn of the cursor if handle is 0 */ + if (crtc->funcs->cursor_set) { + ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle, req->width, req->height); + } else { + DRM_ERROR("crtc does not support cursor\n"); + ret = -EFAULT; + goto out; + } + } + + if (req->flags & DRM_MODE_CURSOR_MOVE) { + if (crtc->funcs->cursor_move) { + ret = crtc->funcs->cursor_move(crtc, req->x, req->y); + } else { + DRM_ERROR("crtc does not support cursor\n"); + ret = -EFAULT; + goto out; + } + } +out: + mutex_unlock(&dev->mode_config.mutex); + return ret; +} + +/** + * drm_mode_addfb - add an FB to the graphics configuration + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * LOCKING: + * Takes mode config lock. + * + * Add a new FB to the specified CRTC, given a user request. + * + * Called by the user via ioctl. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_mode_addfb(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_fb_cmd *r = data; + struct drm_mode_config *config = &dev->mode_config; + struct drm_framebuffer *fb; + int ret = 0; + + if ((config->min_width > r->width) || (r->width > config->max_width)) { + DRM_ERROR("mode new framebuffer width not within limits\n"); + return -EINVAL; + } + if ((config->min_height > r->height) || (r->height > config->max_height)) { + DRM_ERROR("mode new framebuffer height not within limits\n"); + return -EINVAL; + } + + mutex_lock(&dev->mode_config.mutex); + + /* TODO check buffer is sufficently large */ + /* TODO setup destructor callback */ + + fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); + if (!fb) { + DRM_ERROR("could not create framebuffer\n"); + ret = -EINVAL; + goto out; + } + + r->fb_id = fb->base.id; + list_add(&fb->filp_head, &file_priv->fbs); + +out: + mutex_unlock(&dev->mode_config.mutex); + return ret; +} + +/** + * drm_mode_rmfb - remove an FB from the configuration + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * LOCKING: + * Takes mode config lock. + * + * Remove the FB specified by the user. + * + * Called by the user via ioctl. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_mode_rmfb(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_object *obj; + struct drm_framebuffer *fb = NULL; + struct drm_framebuffer *fbl = NULL; + uint32_t *id = data; + int ret = 0; + int found = 0; + + mutex_lock(&dev->mode_config.mutex); + obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB); + /* TODO check that we realy get a framebuffer back. */ + if (!obj) { + DRM_ERROR("mode invalid framebuffer id\n"); + ret = -EINVAL; + goto out; + } + fb = obj_to_fb(obj); + + list_for_each_entry(fbl, &file_priv->fbs, filp_head) + if (fb == fbl) + found = 1; + + if (!found) { + DRM_ERROR("tried to remove a fb that we didn't own\n"); + ret = -EINVAL; + goto out; + } + + /* TODO release all crtc connected to the framebuffer */ + /* TODO unhock the destructor from the buffer object */ + + list_del(&fb->filp_head); + fb->funcs->destroy(fb); + +out: + mutex_unlock(&dev->mode_config.mutex); + return ret; +} + +/** + * drm_mode_getfb - get FB info + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * LOCKING: + * Caller? (FIXME) + * + * Lookup the FB given its ID and return info about it. + * + * Called by the user via ioctl. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_mode_getfb(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_fb_cmd *r = data; + struct drm_mode_object *obj; + struct drm_framebuffer *fb; + int ret = 0; + + mutex_lock(&dev->mode_config.mutex); + obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); + if (!obj) { + DRM_ERROR("invalid framebuffer id\n"); + ret = -EINVAL; + goto out; + } + fb = obj_to_fb(obj); + + r->height = fb->height; + r->width = fb->width; + r->depth = fb->depth; + r->bpp = fb->bits_per_pixel; + r->pitch = fb->pitch; + fb->funcs->create_handle(fb, file_priv, &r->handle); + +out: + mutex_unlock(&dev->mode_config.mutex); + return ret; +} + +/** + * drm_fb_release - remove and free the FBs on this file + * @filp: file * from the ioctl + * + * LOCKING: + * Takes mode config lock. + * + * Destroy all the FBs associated with @filp. + * + * Called by the user via ioctl. + * + * RETURNS: + * Zero on success, errno on failure. + */ +void drm_fb_release(struct file *filp) +{ + struct drm_file *priv = filp->private_data; + struct drm_device *dev = priv->minor->dev; + struct drm_framebuffer *fb, *tfb; + + mutex_lock(&dev->mode_config.mutex); + list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { + list_del(&fb->filp_head); + fb->funcs->destroy(fb); + } + mutex_unlock(&dev->mode_config.mutex); +} + +/* + * + */ + +static int drm_mode_attachmode(struct drm_device *dev, + struct drm_connector *connector, + struct drm_display_mode *mode) +{ + int ret = 0; + + list_add_tail(&mode->head, &connector->user_modes); + return ret; +} + +int drm_mode_attachmode_crtc(struct drm_device *dev, struct drm_crtc *crtc, + struct drm_display_mode *mode) +{ + struct drm_connector *connector; + int ret = 0; + struct drm_display_mode *dup_mode; + int need_dup = 0; + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (!connector->encoder) + break; + if (connector->encoder->crtc == crtc) { + if (need_dup) + dup_mode = drm_mode_duplicate(dev, mode); + else + dup_mode = mode; + ret = drm_mode_attachmode(dev, connector, dup_mode); + if (ret) + return ret; + need_dup = 1; + } + } + return 0; +} +EXPORT_SYMBOL(drm_mode_attachmode_crtc); + +static int drm_mode_detachmode(struct drm_device *dev, + struct drm_connector *connector, + struct drm_display_mode *mode) +{ + int found = 0; + int ret = 0; + struct drm_display_mode *match_mode, *t; + + list_for_each_entry_safe(match_mode, t, &connector->user_modes, head) { + if (drm_mode_equal(match_mode, mode)) { + list_del(&match_mode->head); + drm_mode_destroy(dev, match_mode); + found = 1; + break; + } + } + + if (!found) + ret = -EINVAL; + + return ret; +} + +int drm_mode_detachmode_crtc(struct drm_device *dev, struct drm_display_mode *mode) +{ + struct drm_connector *connector; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + drm_mode_detachmode(dev, connector, mode); + } + return 0; +} +EXPORT_SYMBOL(drm_mode_detachmode_crtc); + +/** + * drm_fb_attachmode - Attach a user mode to an connector + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * This attaches a user specified mode to an connector. + * Called by the user via ioctl. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_mode_attachmode_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_mode_cmd *mode_cmd = data; + struct drm_connector *connector; + struct drm_display_mode *mode; + struct drm_mode_object *obj; + struct drm_mode_modeinfo *umode = &mode_cmd->mode; + int ret = 0; + + mutex_lock(&dev->mode_config.mutex); + + obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR); + if (!obj) { + ret = -EINVAL; + goto out; + } + connector = obj_to_connector(obj); + + mode = drm_mode_create(dev); + if (!mode) { + ret = -ENOMEM; + goto out; + } + + drm_crtc_convert_umode(mode, umode); + + ret = drm_mode_attachmode(dev, connector, mode); +out: + mutex_unlock(&dev->mode_config.mutex); + return ret; +} + + +/** + * drm_fb_detachmode - Detach a user specified mode from an connector + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * Called by the user via ioctl. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_mode_detachmode_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_object *obj; + struct drm_mode_mode_cmd *mode_cmd = data; + struct drm_connector *connector; + struct drm_display_mode mode; + struct drm_mode_modeinfo *umode = &mode_cmd->mode; + int ret = 0; + + mutex_lock(&dev->mode_config.mutex); + + obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR); + if (!obj) { + ret = -EINVAL; + goto out; + } + connector = obj_to_connector(obj); + + drm_crtc_convert_umode(&mode, umode); + ret = drm_mode_detachmode(dev, connector, &mode); +out: + mutex_unlock(&dev->mode_config.mutex); + return ret; +} + +struct drm_property *drm_property_create(struct drm_device *dev, int flags, + const char *name, int num_values) +{ + struct drm_property *property = NULL; + + property = kzalloc(sizeof(struct drm_property), GFP_KERNEL); + if (!property) + return NULL; + + if (num_values) { + property->values = kzalloc(sizeof(uint64_t)*num_values, GFP_KERNEL); + if (!property->values) + goto fail; + } + + drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY); + property->flags = flags; + property->num_values = num_values; + INIT_LIST_HEAD(&property->enum_blob_list); + + if (name) + strncpy(property->name, name, DRM_PROP_NAME_LEN); + + list_add_tail(&property->head, &dev->mode_config.property_list); + return property; +fail: + kfree(property); + return NULL; +} +EXPORT_SYMBOL(drm_property_create); + +int drm_property_add_enum(struct drm_property *property, int index, + uint64_t value, const char *name) +{ + struct drm_property_enum *prop_enum; + + if (!(property->flags & DRM_MODE_PROP_ENUM)) + return -EINVAL; + + if (!list_empty(&property->enum_blob_list)) { + list_for_each_entry(prop_enum, &property->enum_blob_list, head) { + if (prop_enum->value == value) { + strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); + prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; + return 0; + } + } + } + + prop_enum = kzalloc(sizeof(struct drm_property_enum), GFP_KERNEL); + if (!prop_enum) + return -ENOMEM; + + strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); + prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; + prop_enum->value = value; + + property->values[index] = value; + list_add_tail(&prop_enum->head, &property->enum_blob_list); + return 0; +} +EXPORT_SYMBOL(drm_property_add_enum); + +void drm_property_destroy(struct drm_device *dev, struct drm_property *property) +{ + struct drm_property_enum *prop_enum, *pt; + + list_for_each_entry_safe(prop_enum, pt, &property->enum_blob_list, head) { + list_del(&prop_enum->head); + kfree(prop_enum); + } + + if (property->num_values) + kfree(property->values); + drm_mode_object_put(dev, &property->base); + list_del(&property->head); + kfree(property); +} +EXPORT_SYMBOL(drm_property_destroy); + +int drm_connector_attach_property(struct drm_connector *connector, + struct drm_property *property, uint64_t init_val) +{ + int i; + + for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { + if (connector->property_ids[i] == 0) { + connector->property_ids[i] = property->base.id; + connector->property_values[i] = init_val; + break; + } + } + + if (i == DRM_CONNECTOR_MAX_PROPERTY) + return -EINVAL; + return 0; +} +EXPORT_SYMBOL(drm_connector_attach_property); + +int drm_connector_property_set_value(struct drm_connector *connector, + struct drm_property *property, uint64_t value) +{ + int i; + + for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { + if (connector->property_ids[i] == property->base.id) { + connector->property_values[i] = value; + break; + } + } + + if (i == DRM_CONNECTOR_MAX_PROPERTY) + return -EINVAL; + return 0; +} +EXPORT_SYMBOL(drm_connector_property_set_value); + +int drm_connector_property_get_value(struct drm_connector *connector, + struct drm_property *property, uint64_t *val) +{ + int i; + + for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { + if (connector->property_ids[i] == property->base.id) { + *val = connector->property_values[i]; + break; + } + } + + if (i == DRM_CONNECTOR_MAX_PROPERTY) + return -EINVAL; + return 0; +} +EXPORT_SYMBOL(drm_connector_property_get_value); + +int drm_mode_getproperty_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_object *obj; + struct drm_mode_get_property *out_resp = data; + struct drm_property *property; + int enum_count = 0; + int blob_count = 0; + int value_count = 0; + int ret = 0, i; + int copied; + struct drm_property_enum *prop_enum; + struct drm_mode_property_enum __user *enum_ptr; + struct drm_property_blob *prop_blob; + uint32_t *blob_id_ptr; + uint64_t __user *values_ptr; + uint32_t __user *blob_length_ptr; + + mutex_lock(&dev->mode_config.mutex); + obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY); + if (!obj) { + ret = -EINVAL; + goto done; + } + property = obj_to_property(obj); + + if (property->flags & DRM_MODE_PROP_ENUM) { + list_for_each_entry(prop_enum, &property->enum_blob_list, head) + enum_count++; + } else if (property->flags & DRM_MODE_PROP_BLOB) { + list_for_each_entry(prop_blob, &property->enum_blob_list, head) + blob_count++; + } + + value_count = property->num_values; + + strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN); + out_resp->name[DRM_PROP_NAME_LEN-1] = 0; + out_resp->flags = property->flags; + + if ((out_resp->count_values >= value_count) && value_count) { + values_ptr = (uint64_t *)(unsigned long)out_resp->values_ptr; + for (i = 0; i < value_count; i++) { + if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) { + ret = -EFAULT; + goto done; + } + } + } + out_resp->count_values = value_count; + + if (property->flags & DRM_MODE_PROP_ENUM) { + if ((out_resp->count_enum_blobs >= enum_count) && enum_count) { + copied = 0; + enum_ptr = (struct drm_mode_property_enum *)(unsigned long)out_resp->enum_blob_ptr; + list_for_each_entry(prop_enum, &property->enum_blob_list, head) { + + if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) { + ret = -EFAULT; + goto done; + } + + if (copy_to_user(&enum_ptr[copied].name, + &prop_enum->name, DRM_PROP_NAME_LEN)) { + ret = -EFAULT; + goto done; + } + copied++; + } + } + out_resp->count_enum_blobs = enum_count; + } + + if (property->flags & DRM_MODE_PROP_BLOB) { + if ((out_resp->count_enum_blobs >= blob_count) && blob_count) { + copied = 0; + blob_id_ptr = (uint32_t *)(unsigned long)out_resp->enum_blob_ptr; + blob_length_ptr = (uint32_t *)(unsigned long)out_resp->values_ptr; + + list_for_each_entry(prop_blob, &property->enum_blob_list, head) { + if (put_user(prop_blob->base.id, blob_id_ptr + copied)) { + ret = -EFAULT; + goto done; + } + + if (put_user(prop_blob->length, blob_length_ptr + copied)) { + ret = -EFAULT; + goto done; + } + + copied++; + } + } + out_resp->count_enum_blobs = blob_count; + } +done: + mutex_unlock(&dev->mode_config.mutex); + return ret; +} + +static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, int length, + void *data) +{ + struct drm_property_blob *blob; + + if (!length || !data) + return NULL; + + blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL); + if (!blob) + return NULL; + + blob->data = (void *)((char *)blob + sizeof(struct drm_property_blob)); + blob->length = length; + + memcpy(blob->data, data, length); + + drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB); + + list_add_tail(&blob->head, &dev->mode_config.property_blob_list); + return blob; +} + +static void drm_property_destroy_blob(struct drm_device *dev, + struct drm_property_blob *blob) +{ + drm_mode_object_put(dev, &blob->base); + list_del(&blob->head); + kfree(blob); +} + +int drm_mode_getblob_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_object *obj; + struct drm_mode_get_blob *out_resp = data; + struct drm_property_blob *blob; + int ret = 0; + void *blob_ptr; + + mutex_lock(&dev->mode_config.mutex); + obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB); + if (!obj) { + ret = -EINVAL; + goto done; + } + blob = obj_to_blob(obj); + + if (out_resp->length == blob->length) { + blob_ptr = (void *)(unsigned long)out_resp->data; + if (copy_to_user(blob_ptr, blob->data, blob->length)){ + ret = -EFAULT; + goto done; + } + } + out_resp->length = blob->length; + +done: + mutex_unlock(&dev->mode_config.mutex); + return ret; +} + +int drm_mode_connector_update_edid_property(struct drm_connector *connector, struct edid *edid) +{ + struct drm_device *dev = connector->dev; + int ret = 0; + if (connector->edid_blob_ptr) + drm_property_destroy_blob(dev, connector->edid_blob_ptr); + + /* Delete edid, when there is none. */ + if (!edid) { + connector->edid_blob_ptr = NULL; + ret = drm_connector_property_set_value(connector, dev->mode_config.edid_property, 0); + return ret; + } + + connector->edid_blob_ptr = drm_property_create_blob(connector->dev, 128, edid); + + ret = drm_connector_property_set_value(connector, dev->mode_config.edid_property, connector->edid_blob_ptr->base.id); + return ret; +} +EXPORT_SYMBOL(drm_mode_connector_update_edid_property); + +int drm_mode_connector_property_set_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_connector_set_property *out_resp = data; + struct drm_mode_object *obj; + struct drm_property *property; + struct drm_connector *connector; + int ret = -EINVAL; + int i; + + mutex_lock(&dev->mode_config.mutex); + + obj = drm_mode_object_find(dev, out_resp->connector_id, DRM_MODE_OBJECT_CONNECTOR); + if (!obj) { + goto out; + } + connector = obj_to_connector(obj); + + for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { + if (connector->property_ids[i] == out_resp->prop_id) + break; + } + + if (i == DRM_CONNECTOR_MAX_PROPERTY) { + goto out; + } + + obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY); + if (!obj) { + goto out; + } + property = obj_to_property(obj); + + if (property->flags & DRM_MODE_PROP_IMMUTABLE) + goto out; + + if (property->flags & DRM_MODE_PROP_RANGE) { + if (out_resp->value < property->values[0]) + goto out; + + if (out_resp->value > property->values[1]) + goto out; + } else { + int found = 0; + for (i = 0; i < property->num_values; i++) { + if (property->values[i] == out_resp->value) { + found = 1; + break; + } + } + if (!found) { + goto out; + } + } + + if (connector->funcs->set_property) + ret = connector->funcs->set_property(connector, property, out_resp->value); + + /* store the property value if succesful */ + if (!ret) + drm_connector_property_set_value(connector, property, out_resp->value); +out: + mutex_unlock(&dev->mode_config.mutex); + return ret; +} + + +int drm_mode_replacefb(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_fb_cmd *r = data; + struct drm_mode_object *obj; + struct drm_framebuffer *fb; + int found = 0; + struct drm_framebuffer *fbl = NULL; + int ret = 0; + + /* right replace the current bo attached to this fb with a new bo */ + mutex_lock(&dev->mode_config.mutex); + obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); + if (!obj) { + ret = -EINVAL; + goto out; + } + fb = obj_to_fb(obj); + + list_for_each_entry(fbl, &file_priv->fbs, filp_head) + if (fb == fbl) + found = 1; + + if (!found) { + DRM_ERROR("tried to replace an fb we didn't own\n"); + ret = -EINVAL; + goto out; + } + + if (dev->mode_config.funcs->resize_fb) + ret = dev->mode_config.funcs->resize_fb(dev, file_priv, fb, r); + else + ret = -EINVAL; +out: + mutex_unlock(&dev->mode_config.mutex); + return ret; + +} + +int drm_mode_connector_attach_encoder(struct drm_connector *connector, + struct drm_encoder *encoder) +{ + int i; + + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { + if (connector->encoder_ids[i] == 0) { + connector->encoder_ids[i] = encoder->base.id; + return 0; + } + } + return -ENOMEM; +} +EXPORT_SYMBOL(drm_mode_connector_attach_encoder); + +void drm_mode_connector_detach_encoder(struct drm_connector *connector, + struct drm_encoder *encoder) +{ + int i; + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { + if (connector->encoder_ids[i] == encoder->base.id) { + connector->encoder_ids[i] = 0; + if (connector->encoder == encoder) + connector->encoder = NULL; + break; + } + } +} +EXPORT_SYMBOL(drm_mode_connector_detach_encoder); + +bool drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, + int gamma_size) +{ + crtc->gamma_size = gamma_size; + + crtc->gamma_store = kzalloc(gamma_size * sizeof(uint16_t) * 3, GFP_KERNEL); + if (!crtc->gamma_store) { + crtc->gamma_size = 0; + return false; + } + + return true; +} +EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size); + +int drm_mode_gamma_set_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_crtc_lut *crtc_lut = data; + struct drm_mode_object *obj; + struct drm_crtc *crtc; + void *r_base, *g_base, *b_base; + int size; + int ret = 0; + + mutex_lock(&dev->mode_config.mutex); + obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); + if (!obj) { + ret = -EINVAL; + goto out; + } + crtc = obj_to_crtc(obj); + + /* memcpy into gamma store */ + if (crtc_lut->gamma_size != crtc->gamma_size) { + ret = -EINVAL; + goto out; + } + + size = crtc_lut->gamma_size * (sizeof(uint16_t)); + r_base = crtc->gamma_store; + if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) { + ret = -EFAULT; + goto out; + } + + g_base = r_base + size; + if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) { + ret = -EFAULT; + goto out; + } + + b_base = g_base + size; + if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) { + ret = -EFAULT; + goto out; + } + + crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, crtc->gamma_size); + +out: + mutex_unlock(&dev->mode_config.mutex); + return ret; + +} + +int drm_mode_gamma_get_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_crtc_lut *crtc_lut = data; + struct drm_mode_object *obj; + struct drm_crtc *crtc; + void *r_base, *g_base, *b_base; + int size; + int ret = 0; + + mutex_lock(&dev->mode_config.mutex); + obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); + if (!obj) { + ret = -EINVAL; + goto out; + } + crtc = obj_to_crtc(obj); + + /* memcpy into gamma store */ + if (crtc_lut->gamma_size != crtc->gamma_size) { + ret = -EINVAL; + goto out; + } + + size = crtc_lut->gamma_size * (sizeof(uint16_t)); + r_base = crtc->gamma_store; + if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) { + ret = -EFAULT; + goto out; + } + + g_base = r_base + size; + if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) { + ret = -EFAULT; + goto out; + } + + b_base = g_base + size; + if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) { + ret = -EFAULT; + goto out; + } +out: + mutex_unlock(&dev->mode_config.mutex); + return ret; +} diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h new file mode 100644 index 00000000..13fba4f2 --- /dev/null +++ b/linux-core/drm_crtc.h @@ -0,0 +1,716 @@ +/* + * Copyright © 2006 Keith Packard + * Copyright © 2007 Intel Corporation + * Jesse Barnes <jesse.barnes@intel.com> + */ +#ifndef __DRM_CRTC_H__ +#define __DRM_CRTC_H__ + +#include <linux/i2c.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <linux/idr.h> + +#include <linux/fb.h> + +struct drm_device; +struct drm_mode_set; +struct drm_framebuffer; + + +#define DRM_MODE_OBJECT_CRTC 0xcccccccc +#define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0 +#define DRM_MODE_OBJECT_ENCODER 0xe0e0e0e0 +#define DRM_MODE_OBJECT_MODE 0xdededede +#define DRM_MODE_OBJECT_PROPERTY 0xb0b0b0b0 +#define DRM_MODE_OBJECT_FB 0xfbfbfbfb +#define DRM_MODE_OBJECT_BLOB 0xbbbbbbbb + +struct drm_mode_object { + uint32_t id; + uint32_t type; +}; + +/* + * Note on terminology: here, for brevity and convenience, we refer to connector + * control chips as 'CRTCs'. They can control any type of connector, VGA, LVDS, + * DVI, etc. And 'screen' refers to the whole of the visible display, which + * may span multiple monitors (and therefore multiple CRTC and connector + * structures). + */ + +enum drm_mode_status { + MODE_OK = 0, /* Mode OK */ + MODE_HSYNC, /* hsync out of range */ + MODE_VSYNC, /* vsync out of range */ + MODE_H_ILLEGAL, /* mode has illegal horizontal timings */ + MODE_V_ILLEGAL, /* mode has illegal horizontal timings */ + MODE_BAD_WIDTH, /* requires an unsupported linepitch */ + MODE_NOMODE, /* no mode with a maching name */ + MODE_NO_INTERLACE, /* interlaced mode not supported */ + MODE_NO_DBLESCAN, /* doublescan mode not supported */ + MODE_NO_VSCAN, /* multiscan mode not supported */ + MODE_MEM, /* insufficient video memory */ + MODE_VIRTUAL_X, /* mode width too large for specified virtual size */ + MODE_VIRTUAL_Y, /* mode height too large for specified virtual size */ + MODE_MEM_VIRT, /* insufficient video memory given virtual size */ + MODE_NOCLOCK, /* no fixed clock available */ + MODE_CLOCK_HIGH, /* clock required is too high */ + MODE_CLOCK_LOW, /* clock required is too low */ + MODE_CLOCK_RANGE, /* clock/mode isn't in a ClockRange */ + MODE_BAD_HVALUE, /* horizontal timing was out of range */ + MODE_BAD_VVALUE, /* vertical timing was out of range */ + MODE_BAD_VSCAN, /* VScan value out of range */ + MODE_HSYNC_NARROW, /* horizontal sync too narrow */ + MODE_HSYNC_WIDE, /* horizontal sync too wide */ + MODE_HBLANK_NARROW, /* horizontal blanking too narrow */ + MODE_HBLANK_WIDE, /* horizontal blanking too wide */ + MODE_VSYNC_NARROW, /* vertical sync too narrow */ + MODE_VSYNC_WIDE, /* vertical sync too wide */ + MODE_VBLANK_NARROW, /* vertical blanking too narrow */ + MODE_VBLANK_WIDE, /* vertical blanking too wide */ + MODE_PANEL, /* exceeds panel dimensions */ + MODE_INTERLACE_WIDTH, /* width too large for interlaced mode */ + MODE_ONE_WIDTH, /* only one width is supported */ + MODE_ONE_HEIGHT, /* only one height is supported */ + MODE_ONE_SIZE, /* only one resolution is supported */ + MODE_NO_REDUCED, /* monitor doesn't accept reduced blanking */ + MODE_UNVERIFIED = -3, /* mode needs to reverified */ + MODE_BAD = -2, /* unspecified reason */ + MODE_ERROR = -1 /* error condition */ +}; + +#define DRM_MODE_TYPE_CLOCK_CRTC_C (DRM_MODE_TYPE_CLOCK_C | \ + DRM_MODE_TYPE_CRTC_C) + +#define DRM_MODE(nm, t, c, hd, hss, hse, ht, hsk, vd, vss, vse, vt, vs, f) \ + .name = nm, .status = 0, .type = (t), .clock = (c), \ + .hdisplay = (hd), .hsync_start = (hss), .hsync_end = (hse), \ + .htotal = (ht), .hskew = (hsk), .vdisplay = (vd), \ + .vsync_start = (vss), .vsync_end = (vse), .vtotal = (vt), \ + .vscan = (vs), .flags = (f), .vrefresh = 0 + +#define CRTC_INTERLACE_HALVE_V 0x1 /* halve V values for interlacing */ + +struct drm_display_mode { + /* Header */ + struct list_head head; + struct drm_mode_object base; + + char name[DRM_DISPLAY_MODE_LEN]; + + int connector_count; + enum drm_mode_status status; + int type; + + /* Proposed mode values */ + int clock; + int hdisplay; + int hsync_start; + int hsync_end; + int htotal; + int hskew; + int vdisplay; + int vsync_start; + int vsync_end; + int vtotal; + int vscan; + unsigned int flags; + + /* Addressable image size (may be 0 for projectors, etc.) */ + int width_mm; + int height_mm; + + /* Actual mode we give to hw */ + int clock_index; + int synth_clock; + int crtc_hdisplay; + int crtc_hblank_start; + int crtc_hblank_end; + int crtc_hsync_start; + int crtc_hsync_end; + int crtc_htotal; + int crtc_hskew; + int crtc_vdisplay; + int crtc_vblank_start; + int crtc_vblank_end; + int crtc_vsync_start; + int crtc_vsync_end; + int crtc_vtotal; + int crtc_hadjusted; + int crtc_vadjusted; + + /* Driver private mode info */ + int private_size; + int *private; + int private_flags; + + int vrefresh; + float hsync; +}; + +enum drm_connector_status { + connector_status_connected = 1, + connector_status_disconnected = 2, + connector_status_unknown = 3, +}; + +enum subpixel_order { + SubPixelUnknown = 0, + SubPixelHorizontalRGB, + SubPixelHorizontalBGR, + SubPixelVerticalRGB, + SubPixelVerticalBGR, + SubPixelNone, +}; + + +/* + * Describes a given display (e.g. CRT or flat panel) and its limitations. + */ +struct drm_display_info { + char name[DRM_DISPLAY_INFO_LEN]; + /* Input info */ + bool serration_vsync; + bool sync_on_green; + bool composite_sync; + bool separate_syncs; + bool blank_to_black; + unsigned char video_level; + bool digital; + /* Physical size */ + unsigned int width_mm; + unsigned int height_mm; + + /* Display parameters */ + unsigned char gamma; /* FIXME: storage format */ + bool gtf_supported; + bool standard_color; + enum { + monochrome = 0, + rgb, + other, + unknown, + } display_type; + bool active_off_supported; + bool suspend_supported; + bool standby_supported; + + /* Color info FIXME: storage format */ + unsigned short redx, redy; + unsigned short greenx, greeny; + unsigned short bluex, bluey; + unsigned short whitex, whitey; + + /* Clock limits FIXME: storage format */ + unsigned int min_vfreq, max_vfreq; + unsigned int min_hfreq, max_hfreq; + unsigned int pixel_clock; + + /* White point indices FIXME: storage format */ + unsigned int wpx1, wpy1; + unsigned int wpgamma1; + unsigned int wpx2, wpy2; + unsigned int wpgamma2; + + enum subpixel_order subpixel_order; + + char *raw_edid; /* if any */ +}; + +struct drm_framebuffer_funcs { + void (*destroy)(struct drm_framebuffer *framebuffer); + int (*create_handle)(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned int *handle); +}; + +struct drm_framebuffer { + struct drm_device *dev; + struct list_head head; + struct drm_mode_object base; + const struct drm_framebuffer_funcs *funcs; + unsigned int pitch; + unsigned int width; + unsigned int height; + /* depth can be 15 or 16 */ + unsigned int depth; + int bits_per_pixel; + int flags; + void *fbdev; + u32 pseudo_palette[17]; + struct list_head filp_head; +}; + +struct drm_property_blob { + struct drm_mode_object base; + struct list_head head; + unsigned int length; + void *data; +}; + +struct drm_property_enum { + uint64_t value; + struct list_head head; + char name[DRM_PROP_NAME_LEN]; +}; + +struct drm_property { + struct list_head head; + struct drm_mode_object base; + uint32_t flags; + char name[DRM_PROP_NAME_LEN]; + uint32_t num_values; + uint64_t *values; + + struct list_head enum_blob_list; +}; + +struct drm_crtc; +struct drm_connector; +struct drm_encoder; + +/** + * drm_crtc_funcs - control CRTCs for a given device + * @dpms: control display power levels + * @save: save CRTC state + * @resore: restore CRTC state + * @lock: lock the CRTC + * @unlock: unlock the CRTC + * @shadow_allocate: allocate shadow pixmap + * @shadow_create: create shadow pixmap for rotation support + * @shadow_destroy: free shadow pixmap + * @mode_fixup: fixup proposed mode + * @mode_set: set the desired mode on the CRTC + * @gamma_set: specify color ramp for CRTC + * @destroy: deinit and free object. + * + * The drm_crtc_funcs structure is the central CRTC management structure + * in the DRM. Each CRTC controls one or more connectors (note that the name + * CRTC is simply historical, a CRTC may control LVDS, VGA, DVI, TV out, etc. + * connectors, not just CRTs). + * + * Each driver is responsible for filling out this structure at startup time, + * in addition to providing other modesetting features, like i2c and DDC + * bus accessors. + */ +struct drm_crtc_funcs { + /* Save CRTC state */ + void (*save)(struct drm_crtc *crtc); /* suspend? */ + /* Restore CRTC state */ + void (*restore)(struct drm_crtc *crtc); /* resume? */ + + /* cursor controls */ + int (*cursor_set)(struct drm_crtc *crtc, struct drm_file *file_priv, + uint32_t handle, uint32_t width, uint32_t height); + int (*cursor_move)(struct drm_crtc *crtc, int x, int y); + + /* Set gamma on the CRTC */ + void (*gamma_set)(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, + uint32_t size); + /* Object destroy routine */ + void (*destroy)(struct drm_crtc *crtc); + + int (*set_config)(struct drm_mode_set *set); +}; + +/** + * drm_crtc - central CRTC control structure + * @enabled: is this CRTC enabled? + * @x: x position on screen + * @y: y position on screen + * @desired_mode: new desired mode + * @desired_x: desired x for desired_mode + * @desired_y: desired y for desired_mode + * @funcs: CRTC control functions + * + * Each CRTC may have one or more connectors associated with it. This structure + * allows the CRTC to be controlled. + */ +struct drm_crtc { + struct drm_device *dev; + struct list_head head; + + struct drm_mode_object base; + + /* framebuffer the connector is currently bound to */ + struct drm_framebuffer *fb; + + bool enabled; + + struct drm_display_mode mode; + + int x, y; + struct drm_display_mode *desired_mode; + int desired_x, desired_y; + const struct drm_crtc_funcs *funcs; + + /* CRTC gamma size for reporting to userspace */ + uint32_t gamma_size; + uint16_t *gamma_store; + + /* if you are using the helper */ + void *helper_private; +}; + + +/** + * drm_connector_funcs - control connectors on a given device + * @dpms: set power state (see drm_crtc_funcs above) + * @save: save connector state + * @restore: restore connector state + * @mode_valid: is this mode valid on the given connector? + * @mode_fixup: try to fixup proposed mode for this connector + * @mode_set: set this mode + * @detect: is this connector active? + * @get_modes: get mode list for this connector + * @set_property: property for this connector may need update + * @destroy: make object go away + * + * Each CRTC may have one or more connectors attached to it. The functions + * below allow the core DRM code to control connectors, enumerate available modes, + * etc. + */ +struct drm_connector_funcs { + void (*dpms)(struct drm_connector *connector, int mode); + void (*save)(struct drm_connector *connector); + void (*restore)(struct drm_connector *connector); + enum drm_connector_status (*detect)(struct drm_connector *connector); + void (*fill_modes)(struct drm_connector *connector, uint32_t max_width, uint32_t max_height); + int (*set_property)(struct drm_connector *connector, struct drm_property *property, + uint64_t val); + void (*destroy)(struct drm_connector *connector); +}; + +struct drm_encoder_funcs { + void (*destroy)(struct drm_encoder *encoder); +}; + +#define DRM_CONNECTOR_MAX_UMODES 16 +#define DRM_CONNECTOR_MAX_PROPERTY 16 +#define DRM_CONNECTOR_LEN 32 +#define DRM_CONNECTOR_MAX_ENCODER 2 + +/** + * drm_encoder - central DRM encoder structure + */ +struct drm_encoder { + struct drm_device *dev; + struct list_head head; + + struct drm_mode_object base; + int encoder_type; + uint32_t possible_crtcs; + uint32_t possible_clones; + + struct drm_crtc *crtc; + const struct drm_encoder_funcs *funcs; + void *helper_private; +}; + +/** + * drm_connector - central DRM connector control structure + * @crtc: CRTC this connector is currently connected to, NULL if none + * @interlace_allowed: can this connector handle interlaced modes? + * @doublescan_allowed: can this connector handle doublescan? + * @available_modes: modes available on this connector (from get_modes() + user) + * @initial_x: initial x position for this connector + * @initial_y: initial y position for this connector + * @status: connector connected? + * @funcs: connector control functions + * + * Each connector may be connected to one or more CRTCs, or may be clonable by + * another connector if they can share a CRTC. Each connector also has a specific + * position in the broader display (referred to as a 'screen' though it could + * span multiple monitors). + */ +struct drm_connector { + struct drm_device *dev; + struct device kdev; + struct device_attribute *attr; + struct list_head head; + + struct drm_mode_object base; + + int connector_type; + int connector_type_id; + bool interlace_allowed; + bool doublescan_allowed; + struct list_head modes; /* list of modes on this connector */ + + int initial_x, initial_y; + enum drm_connector_status status; + + /* these are modes added by probing with DDC or the BIOS */ + struct list_head probed_modes; + + struct drm_display_info display_info; + const struct drm_connector_funcs *funcs; + + struct list_head user_modes; + struct drm_property_blob *edid_blob_ptr; + u32 property_ids[DRM_CONNECTOR_MAX_PROPERTY]; + uint64_t property_values[DRM_CONNECTOR_MAX_PROPERTY]; + + void *helper_private; + + uint32_t encoder_ids[DRM_CONNECTOR_MAX_ENCODER]; + uint32_t force_encoder_id; + struct drm_encoder *encoder; /* currently active encoder */ +}; + +/** + * struct drm_mode_set + * + * Represents a single crtc the connectors that it drives with what mode + * and from which framebuffer it scans out from. + * + * This is used to set modes. + */ +struct drm_mode_set { + struct list_head head; + + struct drm_framebuffer *fb; + struct drm_crtc *crtc; + struct drm_display_mode *mode; + + uint32_t x; + uint32_t y; + + struct drm_connector **connectors; + size_t num_connectors; +}; + +/** + * struct drm_mode_config_funcs - configure CRTCs for a given screen layout + * @resize: adjust CRTCs as necessary for the proposed layout + * + * Currently only a resize hook is available. DRM will call back into the + * driver with a new screen width and height. If the driver can't support + * the proposed size, it can return false. Otherwise it should adjust + * the CRTC<->connector mappings as needed and update its view of the screen. + */ +struct drm_mode_config_funcs { + int (*resize_fb)(struct drm_device *dev, struct drm_file *file_priv, struct drm_framebuffer *fb, struct drm_mode_fb_cmd *mode_cmd); + struct drm_framebuffer *(*fb_create)(struct drm_device *dev, struct drm_file *file_priv, struct drm_mode_fb_cmd *mode_cmd); + int (*fb_changed)(struct drm_device *dev); +}; + +struct drm_mode_group { + uint32_t num_crtcs; + uint32_t num_encoders; + uint32_t num_connectors; + + /* list of object IDs for this group */ + uint32_t *id_list; +}; + +/** + * drm_mode_config - Mode configuration control structure + * + */ +struct drm_mode_config { + struct mutex mutex; /* protects configuration and IDR */ + struct idr crtc_idr; /* use this idr for all IDs, fb, crtc, connector, modes - just makes life easier */ + /* this is limited to one for now */ + int num_fb; + struct list_head fb_list; + int num_connector; + struct list_head connector_list; + int num_encoder; + struct list_head encoder_list; + + int num_crtc; + struct list_head crtc_list; + + struct list_head property_list; + + /* in-kernel framebuffers - hung of filp_head in drm_framebuffer */ + struct list_head fb_kernel_list; + + int min_width, min_height; + int max_width, max_height; + struct drm_mode_config_funcs *funcs; + unsigned long fb_base; + + /* pointers to standard properties */ + struct list_head property_blob_list; + struct drm_property *edid_property; + struct drm_property *dpms_property; + + /* DVI-I properties */ + struct drm_property *dvi_i_subconnector_property; + struct drm_property *dvi_i_select_subconnector_property; + + /* TV properties */ + struct drm_property *tv_subconnector_property; + struct drm_property *tv_select_subconnector_property; + struct drm_property *tv_mode_property; + struct drm_property *tv_left_margin_property; + struct drm_property *tv_right_margin_property; + struct drm_property *tv_top_margin_property; + struct drm_property *tv_bottom_margin_property; + + /* Optional properties */ + struct drm_property *scaling_mode_property; + struct drm_property *dithering_mode_property; + + /* hotplug */ + uint32_t hotplug_counter; +}; + +#define obj_to_crtc(x) container_of(x, struct drm_crtc, base) +#define obj_to_connector(x) container_of(x, struct drm_connector, base) +#define obj_to_encoder(x) container_of(x, struct drm_encoder, base) +#define obj_to_mode(x) container_of(x, struct drm_display_mode, base) +#define obj_to_fb(x) container_of(x, struct drm_framebuffer, base) +#define obj_to_property(x) container_of(x, struct drm_property, base) +#define obj_to_blob(x) container_of(x, struct drm_property_blob, base) + + +extern void drm_crtc_init(struct drm_device *dev, + struct drm_crtc *crtc, + const struct drm_crtc_funcs *funcs); +extern void drm_crtc_cleanup(struct drm_crtc *crtc); + +extern void drm_connector_init(struct drm_device *dev, + struct drm_connector *connector, + const struct drm_connector_funcs *funcs, + int connector_type); + +extern void drm_connector_cleanup(struct drm_connector *connector); + +extern void drm_encoder_init(struct drm_device *dev, + struct drm_encoder *encoder, + const struct drm_encoder_funcs *funcs, + int encoder_type); + +extern void drm_encoder_cleanup(struct drm_encoder *encoder); + +extern char *drm_get_connector_name(struct drm_connector *connector); +extern char *drm_get_dpms_name(int val); +extern char *drm_get_select_subconnector_name(int val); +extern char *drm_get_subconnector_name(int val); +extern void drm_fb_release(struct file *filp); +extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group); +extern struct edid *drm_get_edid(struct drm_connector *connector, + struct i2c_adapter *adapter); +extern unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter *adapter); +extern int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid); +extern void drm_mode_probed_add(struct drm_connector *connector, struct drm_display_mode *mode); +extern void drm_mode_remove(struct drm_connector *connector, struct drm_display_mode *mode); +extern struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, + struct drm_display_mode *mode); +extern void drm_mode_debug_printmodeline(struct drm_display_mode *mode); +extern void drm_mode_config_init(struct drm_device *dev); +extern void drm_mode_config_cleanup(struct drm_device *dev); +extern void drm_mode_set_name(struct drm_display_mode *mode); +extern bool drm_mode_equal(struct drm_display_mode *mode1, struct drm_display_mode *mode2); +extern int drm_mode_width(struct drm_display_mode *mode); +extern int drm_mode_height(struct drm_display_mode *mode); + +/* for us by fb module */ +extern int drm_mode_attachmode_crtc(struct drm_device *dev, + struct drm_crtc *crtc, + struct drm_display_mode *mode); +extern int drm_mode_detachmode_crtc(struct drm_device *dev, struct drm_display_mode *mode); + +extern struct drm_display_mode *drm_mode_create(struct drm_device *dev); +extern void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode); +extern void drm_mode_list_concat(struct list_head *head, + struct list_head *new); +extern void drm_mode_validate_size(struct drm_device *dev, + struct list_head *mode_list, + int maxX, int maxY, int maxPitch); +extern void drm_mode_prune_invalid(struct drm_device *dev, + struct list_head *mode_list, bool verbose); +extern void drm_mode_sort(struct list_head *mode_list); +extern int drm_mode_vrefresh(struct drm_display_mode *mode); +extern void drm_mode_set_crtcinfo(struct drm_display_mode *p, + int adjust_flags); +extern void drm_mode_connector_list_update(struct drm_connector *connector); +extern int drm_mode_connector_update_edid_property(struct drm_connector *connector, + struct edid *edid); +extern int drm_connector_property_set_value(struct drm_connector *connector, + struct drm_property *property, + uint64_t value); +extern int drm_connector_property_get_value(struct drm_connector *connector, + struct drm_property *property, + uint64_t *value); +extern struct drm_display_mode *drm_crtc_mode_create(struct drm_device *dev); +extern void drm_framebuffer_set_object(struct drm_device *dev, + unsigned long handle); +extern struct drm_framebuffer *drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, + const struct drm_framebuffer_funcs *funcs); +extern void drm_framebuffer_cleanup(struct drm_framebuffer *fb); +extern int drmfb_probe(struct drm_device *dev, struct drm_crtc *crtc); +extern int drmfb_remove(struct drm_device *dev, struct drm_framebuffer *fb); +extern void drm_crtc_probe_connector_modes(struct drm_device *dev, int maxX, int maxY); +extern bool drm_crtc_in_use(struct drm_crtc *crtc); + +extern int drm_connector_attach_property(struct drm_connector *connector, + struct drm_property *property, uint64_t init_val); +extern struct drm_property *drm_property_create(struct drm_device *dev, int flags, + const char *name, int num_values); +extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property); +extern int drm_property_add_enum(struct drm_property *property, int index, + uint64_t value, const char *name); +extern int drm_mode_create_dvi_i_properties(struct drm_device *dev); +extern int drm_mode_create_tv_properties(struct drm_device *dev, int num_formats, + char *formats[]); +extern int drm_mode_create_scaling_mode_property(struct drm_device *dev); +extern int drm_mode_create_dithering_property(struct drm_device *dev); +extern char *drm_get_encoder_name(struct drm_encoder *encoder); + +extern int drm_mode_connector_attach_encoder(struct drm_connector *connector, + struct drm_encoder *encoder); +extern void drm_mode_connector_detach_encoder(struct drm_connector *connector, + struct drm_encoder *encoder); +extern bool drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, + int gamma_size); +extern void *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type); +/* IOCTLs */ +extern int drm_mode_getresources(struct drm_device *dev, + void *data, struct drm_file *file_priv); + +extern int drm_mode_getcrtc(struct drm_device *dev, + void *data, struct drm_file *file_priv); +extern int drm_mode_getconnector(struct drm_device *dev, + void *data, struct drm_file *file_priv); +extern int drm_mode_setcrtc(struct drm_device *dev, + void *data, struct drm_file *file_priv); +extern int drm_mode_cursor_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +extern int drm_mode_addfb(struct drm_device *dev, + void *data, struct drm_file *file_priv); +extern int drm_mode_rmfb(struct drm_device *dev, + void *data, struct drm_file *file_priv); +extern int drm_mode_getfb(struct drm_device *dev, + void *data, struct drm_file *file_priv); +extern int drm_mode_addmode_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +extern int drm_mode_rmmode_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +extern int drm_mode_attachmode_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +extern int drm_mode_detachmode_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); + +extern int drm_mode_getproperty_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +extern int drm_mode_getblob_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +extern int drm_mode_connector_property_set_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +extern int drm_mode_hotplug_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +extern int drm_mode_replacefb(struct drm_device *dev, + void *data, struct drm_file *file_priv); +extern int drm_mode_getencoder(struct drm_device *dev, + void *data, struct drm_file *file_priv); +extern int drm_mode_gamma_get_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +extern int drm_mode_gamma_set_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +#endif /* __DRM_CRTC_H__ */ + diff --git a/linux-core/drm_crtc_helper.c b/linux-core/drm_crtc_helper.c new file mode 100644 index 00000000..ebb44794 --- /dev/null +++ b/linux-core/drm_crtc_helper.c @@ -0,0 +1,833 @@ +/* (c) 2006-2007 Intel Corporation + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> + * + * DRM core CRTC related functions + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + * + * Authors: + * Keith Packard + * Eric Anholt <eric@anholt.net> + * Dave Airlie <airlied@linux.ie> + * Jesse Barnes <jesse.barnes@intel.com> + */ + +#include "drmP.h" +#include "drm_crtc.h" +#include "drm_crtc_helper.h" + +/* + * Detailed mode info for a standard 640x480@60Hz monitor + */ +static struct drm_display_mode std_mode[] = { + { DRM_MODE("640x480", DRM_MODE_TYPE_DEFAULT, 25200, 640, 656, + 752, 800, 0, 480, 490, 492, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@60Hz */ +}; + +/** + * drm_helper_probe_connector_modes - get complete set of display modes + * @dev: DRM device + * @maxX: max width for modes + * @maxY: max height for modes + * + * LOCKING: + * Caller must hold mode config lock. + * + * Based on @dev's mode_config layout, scan all the connectors and try to detect + * modes on them. Modes will first be added to the connector's probed_modes + * list, then culled (based on validity and the @maxX, @maxY parameters) and + * put into the normal modes list. + * + * Intended to be used either at bootup time or when major configuration + * changes have occurred. + * + * FIXME: take into account monitor limits + */ +void drm_helper_probe_single_connector_modes(struct drm_connector *connector, uint32_t maxX, uint32_t maxY) +{ + struct drm_device *dev = connector->dev; + struct drm_display_mode *mode, *t; + struct drm_connector_helper_funcs *connector_funcs = connector->helper_private; + int ret; + + DRM_DEBUG("%s\n", drm_get_connector_name(connector)); + /* set all modes to the unverified state */ + list_for_each_entry_safe(mode, t, &connector->modes, head) + mode->status = MODE_UNVERIFIED; + + connector->status = (*connector->funcs->detect)(connector); + + if (connector->status == connector_status_disconnected) { + DRM_DEBUG("%s is disconnected\n", drm_get_connector_name(connector)); + /* TODO set EDID to NULL */ + return; + } + + ret = (*connector_funcs->get_modes)(connector); + + if (ret) { + drm_mode_connector_list_update(connector); + } + + if (maxX && maxY) + drm_mode_validate_size(dev, &connector->modes, maxX, + maxY, 0); + list_for_each_entry_safe(mode, t, &connector->modes, head) { + if (mode->status == MODE_OK) + mode->status = (*connector_funcs->mode_valid)(connector,mode); + } + + + drm_mode_prune_invalid(dev, &connector->modes, true); + + if (list_empty(&connector->modes)) { + struct drm_display_mode *stdmode; + + DRM_DEBUG("No valid modes on %s\n", drm_get_connector_name(connector)); + + /* Should we do this here ??? + * When no valid EDID modes are available we end up + * here and bailed in the past, now we add a standard + * 640x480@60Hz mode and carry on. + */ + stdmode = drm_mode_duplicate(dev, &std_mode[0]); + drm_mode_probed_add(connector, stdmode); + drm_mode_list_concat(&connector->probed_modes, + &connector->modes); + + DRM_DEBUG("Adding standard 640x480 @ 60Hz to %s\n", + drm_get_connector_name(connector)); + } + + drm_mode_sort(&connector->modes); + + DRM_DEBUG("Probed modes for %s\n", drm_get_connector_name(connector)); + list_for_each_entry_safe(mode, t, &connector->modes, head) { + mode->vrefresh = drm_mode_vrefresh(mode); + + drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); + drm_mode_debug_printmodeline(mode); + } +} +EXPORT_SYMBOL(drm_helper_probe_single_connector_modes); + +void drm_helper_probe_connector_modes(struct drm_device *dev, uint32_t maxX, uint32_t maxY) +{ + struct drm_connector *connector; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + drm_helper_probe_single_connector_modes(connector, maxX, maxY); + } +} +EXPORT_SYMBOL(drm_helper_probe_connector_modes); + + +/** + * drm_helper_crtc_in_use - check if a given CRTC is in a mode_config + * @crtc: CRTC to check + * + * LOCKING: + * Caller must hold mode config lock. + * + * Walk @crtc's DRM device's mode_config and see if it's in use. + * + * RETURNS: + * True if @crtc is part of the mode_config, false otherwise. + */ +bool drm_helper_crtc_in_use(struct drm_crtc *crtc) +{ + struct drm_encoder *encoder; + struct drm_device *dev = crtc->dev; + /* FIXME: Locking around list access? */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) + if (encoder->crtc == crtc) + return true; + return false; +} +EXPORT_SYMBOL(drm_helper_crtc_in_use); + +/** + * drm_disable_unused_functions - disable unused objects + * @dev: DRM device + * + * LOCKING: + * Caller must hold mode config lock. + * + * If an connector or CRTC isn't part of @dev's mode_config, it can be disabled + * by calling its dpms function, which should power it off. + */ +void drm_helper_disable_unused_functions(struct drm_device *dev) +{ + struct drm_encoder *encoder; + struct drm_encoder_helper_funcs *encoder_funcs; + struct drm_crtc *crtc; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + encoder_funcs = encoder->helper_private; + if (!encoder->crtc) + (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); + } + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + crtc->enabled = drm_helper_crtc_in_use(crtc); + if (!crtc->enabled) { + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); + crtc->fb = NULL; + } + } +} +EXPORT_SYMBOL(drm_helper_disable_unused_functions); + +static struct drm_display_mode *drm_has_preferred_mode(struct drm_connector *connector, int width, int height) +{ + struct drm_display_mode *mode; + + list_for_each_entry(mode, &connector->modes, head) { + if (drm_mode_width(mode) > width || + drm_mode_height(mode) > height) + continue; + if (mode->type & DRM_MODE_TYPE_PREFERRED) + return mode; + } + return NULL; +} + +static bool drm_connector_enabled(struct drm_connector *connector, bool strict) +{ + bool enable; + + if (strict) { + enable = connector->status == connector_status_connected; + } else { + enable = connector->status != connector_status_disconnected; + } + return enable; +} + +static void drm_enable_connectors(struct drm_device *dev, bool *enabled) +{ + bool any_enabled = false; + struct drm_connector *connector; + int i = 0; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + any_enabled |= enabled[i] = drm_connector_enabled(connector, true); + i++; + } + + if (!any_enabled) { + i = 0; + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + enabled[i] = drm_connector_enabled(connector, false); + i++; + } + } +} + +static bool drm_target_preferred(struct drm_device *dev, struct drm_display_mode **modes, + bool *enabled, int width, int height) +{ + struct drm_connector *connector; + struct drm_display_mode *preferred; + int i = 0; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + + if (enabled[i] == false) { + i++; + continue; + } + + modes[i] = drm_has_preferred_mode(connector, width, height); + if (!modes[i]) { + list_for_each_entry(modes[i], &connector->modes, head) + break; + } + i++; + } + return true; +} + +static int drm_pick_crtcs(struct drm_device *dev, + struct drm_crtc **best_crtcs, + struct drm_display_mode **modes, + int n, int width, int height) +{ + int c, o; + struct drm_connector *connector; + struct drm_connector_helper_funcs *connector_funcs; + struct drm_encoder *encoder; + struct drm_crtc *best_crtc; + int my_score, best_score, score; + struct drm_crtc **crtcs, *crtc; + + if (n == dev->mode_config.num_connector) + return 0; + c = 0; + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (c == n) + break; + c++; + } + + best_crtcs[n] = NULL; + best_crtc = NULL; + best_score = drm_pick_crtcs(dev, best_crtcs, modes, n+1, width, height); + if (modes[n] == NULL) + return best_score; + + crtcs = kmalloc(dev->mode_config.num_connector * sizeof(struct drm_crtc *), GFP_KERNEL); + if (!crtcs) + return best_score; + + my_score = 1; + if (connector->status == connector_status_connected) + my_score++; + if (drm_has_preferred_mode(connector, width, height)) + my_score++; + + connector_funcs = connector->helper_private; + encoder = connector_funcs->best_encoder(connector); + if (!encoder) + goto out; + + connector->encoder = encoder; + + /* select a crtc for this connector and then attempt to configure + remaining connectors */ + c = 0; + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + + if ((connector->encoder->possible_crtcs & (1 << c)) == 0) { + c++; + continue; + } + + for (o = 0; o < n; o++) + if (best_crtcs[o] == crtc) + break; + + if (o < n) { + /* ignore cloning for now */ + c++; + continue; + } + + crtcs[n] = crtc; + memcpy(crtcs, best_crtcs, n * sizeof(struct drm_crtc *)); + score = my_score + drm_pick_crtcs(dev, crtcs, modes, n + 1, width, height); + if (score > best_score) { + best_crtc = crtc; + best_score = score; + memcpy(best_crtcs, crtcs, dev->mode_config.num_connector * sizeof(struct drm_crtc *)); + } + c++; + } +out: + kfree(crtcs); + return best_score; +} + +static void drm_setup_crtcs(struct drm_device *dev) +{ + struct drm_crtc **crtcs; + struct drm_display_mode **modes; + struct drm_encoder *encoder; + struct drm_connector *connector; + bool *enabled; + int width, height; + int i, ret; + + width = dev->mode_config.max_width; + height = dev->mode_config.max_height; + + /* clean out all the encoder/crtc combos */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + encoder->crtc = NULL; + } + + crtcs = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_crtc *), GFP_KERNEL); + modes = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_display_mode *), GFP_KERNEL); + enabled = kcalloc(dev->mode_config.num_connector, sizeof(bool), GFP_KERNEL); + + drm_enable_connectors(dev, enabled); + + ret = drm_target_preferred(dev, modes, enabled, width, height); + if (!ret) + DRM_ERROR("Unable to find initial modes\n"); + + drm_pick_crtcs(dev, crtcs, modes, 0, width, height); + + i = 0; + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + struct drm_display_mode *mode = modes[i]; + struct drm_crtc *crtc = crtcs[i]; + + if (connector->encoder == NULL) { + i++; + continue; + } + + if (mode && crtc) { + crtc->desired_mode = mode; + connector->encoder->crtc = crtc; + } else + connector->encoder->crtc = NULL; + i++; + } + + kfree(crtcs); + kfree(modes); + kfree(enabled); +} +/** + * drm_crtc_set_mode - set a mode + * @crtc: CRTC to program + * @mode: mode to use + * @x: width of mode + * @y: height of mode + * + * LOCKING: + * Caller must hold mode config lock. + * + * Try to set @mode on @crtc. Give @crtc and its associated connectors a chance + * to fixup or reject the mode prior to trying to set it. + * + * RETURNS: + * True if the mode was set successfully, or false otherwise. + */ +bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, + int x, int y) +{ + struct drm_device *dev = crtc->dev; + struct drm_display_mode *adjusted_mode, saved_mode; + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + struct drm_encoder_helper_funcs *encoder_funcs; + int saved_x, saved_y; + struct drm_encoder *encoder; + bool ret = true; + + adjusted_mode = drm_mode_duplicate(dev, mode); + + crtc->enabled = drm_helper_crtc_in_use(crtc); + + if (!crtc->enabled) + return true; + + saved_mode = crtc->mode; + saved_x = crtc->x; + saved_y = crtc->y; + + /* Update crtc values up front so the driver can rely on them for mode + * setting. + */ + crtc->mode = *mode; + crtc->x = x; + crtc->y = y; + + if (drm_mode_equal(&saved_mode, &crtc->mode)) { + if (saved_x != crtc->x || saved_y != crtc->y) { + crtc_funcs->mode_set_base(crtc, crtc->x, crtc->y); + goto done; + } + } + + /* Pass our mode to the connectors and the CRTC to give them a chance to + * adjust it according to limitations or connector properties, and also + * a chance to reject the mode entirely. + */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + + if (encoder->crtc != crtc) + continue; + encoder_funcs = encoder->helper_private; + if (!(ret = encoder_funcs->mode_fixup(encoder, mode, adjusted_mode))) { + goto done; + } + } + + if (!(ret = crtc_funcs->mode_fixup(crtc, mode, adjusted_mode))) { + goto done; + } + + /* Prepare the encoders and CRTCs before setting the mode. */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + + if (encoder->crtc != crtc) + continue; + encoder_funcs = encoder->helper_private; + /* Disable the encoders as the first thing we do. */ + encoder_funcs->prepare(encoder); + } + + crtc_funcs->prepare(crtc); + + /* Set up the DPLL and any encoders state that needs to adjust or depend + * on the DPLL. + */ + crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y); + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + + if (encoder->crtc != crtc) + continue; + + DRM_INFO("%s: set mode %s %x\n", drm_get_encoder_name(encoder), mode->name, mode->base.id); + encoder_funcs = encoder->helper_private; + encoder_funcs->mode_set(encoder, mode, adjusted_mode); + } + + /* Now, enable the clocks, plane, pipe, and connectors that we set up. */ + crtc_funcs->commit(crtc); + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + + if (encoder->crtc != crtc) + continue; + + encoder_funcs = encoder->helper_private; + encoder_funcs->commit(encoder); + + } + + /* XXX free adjustedmode */ + drm_mode_destroy(dev, adjusted_mode); + /* TODO */ +// if (scrn->pScreen) +// drm_crtc_set_screen_sub_pixel_order(dev); + +done: + if (!ret) { + crtc->mode = saved_mode; + crtc->x = saved_x; + crtc->y = saved_y; + } + + return ret; +} +EXPORT_SYMBOL(drm_crtc_helper_set_mode); + + +/** + * drm_crtc_helper_set_config - set a new config from userspace + * @crtc: CRTC to setup + * @crtc_info: user provided configuration + * @new_mode: new mode to set + * @connector_set: set of connectors for the new config + * @fb: new framebuffer + * + * LOCKING: + * Caller must hold mode config lock. + * + * Setup a new configuration, provided by the user in @crtc_info, and enable + * it. + * + * RETURNS: + * Zero. (FIXME) + */ +int drm_crtc_helper_set_config(struct drm_mode_set *set) +{ + struct drm_device *dev; + struct drm_crtc **save_crtcs, *new_crtc; + struct drm_encoder **save_encoders, *new_encoder; + bool save_enabled; + bool changed = false; + bool flip_or_move = false; + struct drm_connector *connector; + int count = 0, ro, fail = 0; + struct drm_crtc_helper_funcs *crtc_funcs; + int ret = 0; + + DRM_DEBUG("\n"); + + if (!set) + return -EINVAL; + + if (!set->crtc) + return -EINVAL; + + if (!set->crtc->helper_private) + return -EINVAL; + + crtc_funcs = set->crtc->helper_private; + + DRM_DEBUG("crtc: %p %d fb: %p connectors: %p num_connectors: %i (x, y) (%i, %i)\n", set->crtc, set->crtc->base.id, set->fb, set->connectors, set->num_connectors, set->x, set->y); + + dev = set->crtc->dev; + + /* save previous config */ + save_enabled = set->crtc->enabled; + + /* this is meant to be num_connector not num_crtc */ + save_crtcs = kzalloc(dev->mode_config.num_connector * sizeof(struct drm_crtc *), GFP_KERNEL); + if (!save_crtcs) + return -ENOMEM; + + save_encoders = kzalloc(dev->mode_config.num_connector * sizeof(struct drm_encoders *), GFP_KERNEL); + if (!save_encoders) { + kfree(save_crtcs); + return -ENOMEM; + } + + /* We should be able to check here if the fb has the same properties + * and then just flip_or_move it */ + if (set->crtc->fb != set->fb) { + /* if we have no fb then its a change not a flip */ + if (set->crtc->fb == NULL) + changed = true; + else + flip_or_move = true; + } + + if (set->x != set->crtc->x || set->y != set->crtc->y) + flip_or_move = true; + + if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) { + DRM_DEBUG("modes are different\n"); + drm_mode_debug_printmodeline(&set->crtc->mode); + drm_mode_debug_printmodeline(set->mode); + changed = true; + } + + /* a) traverse passed in connector list and get encoders for them */ + count = 0; + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + struct drm_connector_helper_funcs *connector_funcs = connector->helper_private; + save_encoders[count++] = connector->encoder; + new_encoder = connector->encoder; + for (ro = 0; ro < set->num_connectors; ro++) { + if (set->connectors[ro] == connector) { + new_encoder = connector_funcs->best_encoder(connector); + /* if we can't get an encoder for a connector + we are setting now - then fail */ + if (new_encoder == NULL) + /* don't break so fail path works correct */ + fail = 1; + break; + } + } + + if (new_encoder != connector->encoder) { + changed = true; + connector->encoder = new_encoder; + } + } + + if (fail) { + ret = -EINVAL; + goto fail_no_encoder; + } + + count = 0; + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (!connector->encoder) + continue; + + save_crtcs[count++] = connector->encoder->crtc; + + if (connector->encoder->crtc == set->crtc) + new_crtc = NULL; + else + new_crtc = connector->encoder->crtc; + + for (ro = 0; ro < set->num_connectors; ro++) { + if (set->connectors[ro] == connector) + new_crtc = set->crtc; + } + if (new_crtc != connector->encoder->crtc) { + changed = true; + connector->encoder->crtc = new_crtc; + } + } + + /* mode_set_base is not a required function */ + if (flip_or_move && !crtc_funcs->mode_set_base) + changed = true; + + if (changed) { + set->crtc->fb = set->fb; + set->crtc->enabled = (set->mode != NULL); + if (set->mode != NULL) { + DRM_DEBUG("attempting to set mode from userspace\n"); + drm_mode_debug_printmodeline(set->mode); + if (!drm_crtc_helper_set_mode(set->crtc, set->mode, set->x, + set->y)) { + ret = -EINVAL; + goto fail_set_mode; + } + /* TODO are these needed? */ + set->crtc->desired_x = set->x; + set->crtc->desired_y = set->y; + set->crtc->desired_mode = set->mode; + } + drm_helper_disable_unused_functions(dev); + } else if (flip_or_move) { + if (set->crtc->fb != set->fb) + set->crtc->fb = set->fb; + crtc_funcs->mode_set_base(set->crtc, set->x, set->y); + set->crtc->x = set->x; + set->crtc->y = set->y; + } + + kfree(save_encoders); + kfree(save_crtcs); + return 0; + +fail_set_mode: + set->crtc->enabled = save_enabled; + count = 0; + list_for_each_entry(connector, &dev->mode_config.connector_list, head) + connector->encoder->crtc = save_crtcs[count++]; +fail_no_encoder: + kfree(save_crtcs); + count = 0; + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + connector->encoder = save_encoders[count++]; + } + kfree(save_encoders); + return ret; + + +} +EXPORT_SYMBOL(drm_crtc_helper_set_config); + +bool drm_helper_plugged_event(struct drm_device *dev) +{ + DRM_DEBUG("\n"); + + drm_helper_probe_connector_modes(dev, dev->mode_config.max_width, dev->mode_config.max_height); + + drm_setup_crtcs(dev); + + /* alert the driver fb layer */ + dev->mode_config.funcs->fb_changed(dev); + + drm_helper_disable_unused_functions(dev); + + drm_sysfs_hotplug_event(dev); + return true; +} +/** + * drm_initial_config - setup a sane initial connector configuration + * @dev: DRM device + * @can_grow: this configuration is growable + * + * LOCKING: + * Called at init time, must take mode config lock. + * + * Scan the CRTCs and connectors and try to put together an initial setup. + * At the moment, this is a cloned configuration across all heads with + * a new framebuffer object as the backing store. + * + * RETURNS: + * Zero if everything went ok, nonzero otherwise. + */ +bool drm_helper_initial_config(struct drm_device *dev, bool can_grow) +{ + int ret = false; + + drm_helper_plugged_event(dev); + return ret; +} +EXPORT_SYMBOL(drm_helper_initial_config); + +/** + * drm_hotplug_stage_two + * @dev DRM device + * @connector hotpluged connector + * + * LOCKING. + * Caller must hold mode config lock, function might grab struct lock. + * + * Stage two of a hotplug. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_helper_hotplug_stage_two(struct drm_device *dev) +{ + dev->mode_config.hotplug_counter++; + + drm_helper_plugged_event(dev); + + return 0; +} +EXPORT_SYMBOL(drm_helper_hotplug_stage_two); + +int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, + struct drm_mode_fb_cmd *mode_cmd) +{ + fb->width = mode_cmd->width; + fb->height = mode_cmd->height; + fb->pitch = mode_cmd->pitch; + fb->bits_per_pixel = mode_cmd->bpp; + fb->depth = mode_cmd->depth; + + return 0; +} +EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct); + +int drm_helper_resume_force_mode(struct drm_device *dev) +{ + struct drm_crtc *crtc; + int ret; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + + if (!crtc->enabled) + continue; + + ret = drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x, + crtc->y); + + if (ret == false) + DRM_ERROR("failed to set mode on crtc %p\n", crtc); + } + return 0; +} +EXPORT_SYMBOL(drm_helper_resume_force_mode); + +void drm_helper_set_connector_dpms(struct drm_connector *connector, + int dpms_mode) +{ + int i = 0; + struct drm_encoder *encoder; + struct drm_encoder_helper_funcs *encoder_funcs; + struct drm_mode_object *obj; + + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { + if (connector->encoder_ids[i] == 0) + break; + + obj = drm_mode_object_find(connector->dev, + connector->encoder_ids[i], + DRM_MODE_OBJECT_ENCODER); + if (!obj) + continue; + + encoder = obj_to_encoder(obj); + encoder_funcs = encoder->helper_private; + if (encoder_funcs->dpms) + encoder_funcs->dpms(encoder, dpms_mode); + + } +} +EXPORT_SYMBOL(drm_helper_set_connector_dpms); diff --git a/linux-core/drm_crtc_helper.h b/linux-core/drm_crtc_helper.h new file mode 100644 index 00000000..a0dd6675 --- /dev/null +++ b/linux-core/drm_crtc_helper.h @@ -0,0 +1,98 @@ +/* + * Copyright © 2006 Keith Packard + * Copyright © 2007 Intel Corporation + * Jesse Barnes <jesse.barnes@intel.com> + */ + +/* + * The DRM mode setting helper functions are common code for drivers to use if they wish. + * Drivers are not forced to use this code in their implementations but it would be useful + * if they code they do use at least provides a consistent interface and operation to userspace + */ + +#ifndef __DRM_CRTC_HELPER_H__ +#define __DRM_CRTC_HELPER_H__ + +#include <linux/i2c.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <linux/idr.h> + +#include <linux/fb.h> + +struct drm_crtc_helper_funcs { + /* + * Control power levels on the CRTC. If the mode passed in is + * unsupported, the provider must use the next lowest power level. + */ + void (*dpms)(struct drm_crtc *crtc, int mode); + void (*prepare)(struct drm_crtc *crtc); + void (*commit)(struct drm_crtc *crtc); + + /* Provider can fixup or change mode timings before modeset occurs */ + bool (*mode_fixup)(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + /* Actually set the mode */ + void (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, int x, int y); + + /* Move the crtc on the current fb to the given position *optional* */ + void (*mode_set_base)(struct drm_crtc *crtc, int x, int y); +}; + +struct drm_encoder_helper_funcs { + void (*dpms)(struct drm_encoder *encoder, int mode); + void (*save)(struct drm_encoder *encoder); + void (*restore)(struct drm_encoder *encoder); + + bool (*mode_fixup)(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + void (*prepare)(struct drm_encoder *encoder); + void (*commit)(struct drm_encoder *encoder); + void (*mode_set)(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + /* detect for DAC style encoders */ + enum drm_connector_status (*detect)(struct drm_encoder *encoder, struct drm_connector *connector); +}; + +struct drm_connector_helper_funcs { + int (*get_modes)(struct drm_connector *connector); + int (*mode_valid)(struct drm_connector *connector, + struct drm_display_mode *mode); + struct drm_encoder *(*best_encoder)(struct drm_connector *connector); +}; + +extern void drm_helper_probe_single_connector_modes(struct drm_connector *connector, uint32_t maxX, uint32_t maxY); +extern void drm_helper_disable_unused_functions(struct drm_device *dev); +extern int drm_helper_hotplug_stage_two(struct drm_device *dev); +extern bool drm_helper_initial_config(struct drm_device *dev, bool can_grow); +extern int drm_crtc_helper_set_config(struct drm_mode_set *set); +extern bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, + int x, int y); +extern bool drm_helper_crtc_in_use(struct drm_crtc *crtc); + +extern int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, + struct drm_mode_fb_cmd *mode_cmd); + +static inline void drm_crtc_helper_add(struct drm_crtc *crtc, const struct drm_crtc_helper_funcs *funcs) +{ + crtc->helper_private = (void *)funcs; +} + +static inline void drm_encoder_helper_add(struct drm_encoder *encoder, const struct drm_encoder_helper_funcs *funcs) +{ + encoder->helper_private = (void *)funcs; +} + +static inline void drm_connector_helper_add(struct drm_connector *connector, const struct drm_connector_helper_funcs *funcs) +{ + connector->helper_private = (void *)funcs; +} + +extern int drm_helper_resume_force_mode(struct drm_device *dev); +extern void drm_helper_set_connector_dpms(struct drm_connector *connector, + int dpms_mode); +#endif diff --git a/linux-core/drm_dma.c b/linux-core/drm_dma.c index f7bff0ac..1e6aeefd 100644 --- a/linux-core/drm_dma.c +++ b/linux-core/drm_dma.c @@ -58,6 +58,7 @@ int drm_dma_setup(struct drm_device *dev) return 0; } +EXPORT_SYMBOL(drm_dma_setup); /** * Cleanup the DMA resources. @@ -120,6 +121,7 @@ void drm_dma_takedown(struct drm_device *dev) drm_free(dev->dma, sizeof(*dev->dma), DRM_MEM_DRIVER); dev->dma = NULL; } +EXPORT_SYMBOL(drm_dma_takedown); /** * Free a buffer. diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c index 31a98fe8..a8b17f1d 100644 --- a/linux-core/drm_drv.c +++ b/linux-core/drm_drv.c @@ -56,26 +56,29 @@ static int drm_version(struct drm_device *dev, void *data, /** Ioctl table */ static struct drm_ioctl_desc drm_ioctls[] = { - DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, 0), + DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, DRM_CONTROL_ALLOW), DRM_IOCTL_DEF(DRM_IOCTL_GET_UNIQUE, drm_getunique, 0), DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, 0), - DRM_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_irq_by_busid, DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_irq_by_busid, DRM_MASTER), DRM_IOCTL_DEF(DRM_IOCTL_GET_MAP, drm_getmap, 0), DRM_IOCTL_DEF(DRM_IOCTL_GET_CLIENT, drm_getclient, 0), DRM_IOCTL_DEF(DRM_IOCTL_GET_STATS, drm_getstats, 0), - DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_MASTER), - DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_setunique, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_setunique, DRM_AUTH|DRM_MASTER), DRM_IOCTL_DEF(DRM_IOCTL_BLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_UNBLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_AUTH_MAGIC, drm_authmagic, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_AUTH_MAGIC, drm_authmagic, DRM_AUTH|DRM_MASTER), - DRM_IOCTL_DEF(DRM_IOCTL_ADD_MAP, drm_addmap_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_ADD_MAP, drm_addmap_ioctl, DRM_AUTH|DRM_MASTER), DRM_IOCTL_DEF(DRM_IOCTL_RM_MAP, drm_rmmap_ioctl, DRM_AUTH), DRM_IOCTL_DEF(DRM_IOCTL_SET_SAREA_CTX, drm_setsareactx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_GET_SAREA_CTX, drm_getsareactx, DRM_AUTH), + DRM_IOCTL_DEF(DRM_IOCTL_SET_MASTER, drm_setmaster_ioctl, DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_DROP_MASTER, drm_dropmaster_ioctl, DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_ADD_CTX, drm_addctx, DRM_AUTH|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_RM_CTX, drm_rmctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_MOD_CTX, drm_modctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), @@ -100,7 +103,7 @@ static struct drm_ioctl_desc drm_ioctls[] = { /* The DRM_IOCTL_DMA ioctl should be defined by the driver. */ DRM_IOCTL_DEF(DRM_IOCTL_DMA, NULL, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_CONTROL, drm_control, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_CONTROL, drm_control, DRM_AUTH|DRM_MASTER), #if __OS_HAS_AGP DRM_IOCTL_DEF(DRM_IOCTL_AGP_ACQUIRE, drm_agp_acquire_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), @@ -121,35 +124,25 @@ static struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_UPDATE_DRAW, drm_update_drawable_info, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_MM_INIT, drm_mm_init_ioctl, - DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_MM_TAKEDOWN, drm_mm_takedown_ioctl, - DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_MM_LOCK, drm_mm_lock_ioctl, - DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_MM_UNLOCK, drm_mm_unlock_ioctl, - DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - - DRM_IOCTL_DEF(DRM_IOCTL_FENCE_CREATE, drm_fence_create_ioctl, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_FENCE_REFERENCE, drm_fence_reference_ioctl, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_FENCE_UNREFERENCE, drm_fence_unreference_ioctl, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_FENCE_SIGNALED, drm_fence_signaled_ioctl, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_FENCE_FLUSH, drm_fence_flush_ioctl, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_FENCE_WAIT, drm_fence_wait_ioctl, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_FENCE_EMIT, drm_fence_emit_ioctl, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_FENCE_BUFFERS, drm_fence_buffers_ioctl, DRM_AUTH), - - DRM_IOCTL_DEF(DRM_IOCTL_BO_CREATE, drm_bo_create_ioctl, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_BO_MAP, drm_bo_map_ioctl, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_BO_UNMAP, drm_bo_unmap_ioctl, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_BO_REFERENCE, drm_bo_reference_ioctl, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_BO_UNREFERENCE, drm_bo_unreference_ioctl, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_BO_SETSTATUS, drm_bo_setstatus_ioctl, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_BO_INFO, drm_bo_info_ioctl, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_BO_WAIT_IDLE, drm_bo_wait_idle_ioctl, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_BO_VERSION, drm_bo_version_ioctl, 0), - - DRM_IOCTL_DEF(DRM_IOCTL_MM_INFO, drm_mm_info_ioctl, 0), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, DRM_MASTER|DRM_CONTROL_ALLOW), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_MASTER|DRM_CONTROL_ALLOW), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, DRM_MASTER|DRM_CONTROL_ALLOW), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_CONTROL_ALLOW), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_MASTER|DRM_CONTROL_ALLOW), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_MASTER|DRM_CONTROL_ALLOW), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_MASTER|DRM_CONTROL_ALLOW), + + DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPROPERTY, drm_mode_connector_property_set_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATTACHMODE, drm_mode_attachmode_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_DETACHMODE, drm_mode_detachmode_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_MASTER | DRM_CONTROL_ALLOW), + + DRM_IOCTL_DEF(DRM_IOCTL_MODE_REPLACEFB, drm_mode_replacefb, DRM_MASTER|DRM_ROOT_ONLY|DRM_CONTROL_ALLOW), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETENCODER, drm_mode_getencoder, DRM_MASTER|DRM_CONTROL_ALLOW), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, DRM_MASTER), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETGAMMA, drm_mode_gamma_set_ioctl, DRM_MASTER), #if OS_HAS_GEM DRM_IOCTL_DEF(DRM_IOCTL_GEM_CLOSE, drm_gem_close_ioctl, 0), @@ -172,30 +165,24 @@ static struct drm_ioctl_desc drm_ioctls[] = { */ int drm_lastclose(struct drm_device * dev) { - struct drm_magic_entry *pt, *next; - struct drm_map_list *r_list, *list_t; struct drm_vma_entry *vma, *vma_temp; int i; DRM_DEBUG("\n"); + /* * We can't do much about this function failing. */ - drm_bo_driver_finish(dev); - if (dev->driver->lastclose) dev->driver->lastclose(dev); DRM_DEBUG("driver lastclose completed\n"); - if (dev->unique) { - drm_free(dev->unique, strlen(dev->unique) + 1, DRM_MEM_DRIVER); - dev->unique = NULL; - dev->unique_len = 0; - } + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + drm_bo_driver_finish(dev); - if (dev->irq_enabled) + if (dev->irq_enabled && !drm_core_check_feature(dev, DRIVER_MODESET)) drm_irq_uninstall(dev); /* Free drawable information memory */ @@ -204,24 +191,8 @@ int drm_lastclose(struct drm_device * dev) drm_drawable_free_all(dev); del_timer(&dev->timer); - if (dev->unique) { - drm_free(dev->unique, strlen(dev->unique) + 1, DRM_MEM_DRIVER); - dev->unique = NULL; - dev->unique_len = 0; - } - - if (dev->magicfree.next) { - list_for_each_entry_safe(pt, next, &dev->magicfree, head) { - list_del(&pt->head); - drm_ht_remove_item(&dev->magiclist, &pt->hash_item); - drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); - } - drm_ht_remove(&dev->magiclist); - } - - /* Clear AGP information */ - if (drm_core_has_AGP(dev) && dev->agp) { + if (drm_core_has_AGP(dev) && dev->agp && !drm_core_check_feature(dev, DRIVER_MODESET)) { struct drm_agp_mem *entry, *tempe; /* Remove AGP resources, but leave dev->agp @@ -240,7 +211,10 @@ int drm_lastclose(struct drm_device * dev) dev->agp->acquired = 0; dev->agp->enabled = 0; } - if (drm_core_check_feature(dev, DRIVER_SG) && dev->sg) { + + /* You're supposed to have a real memory manager for modesetting, but this'll suffice as a temporary workaround. */ + /* This assumes sgdma is inited at load time. */ + if (drm_core_check_feature(dev, DRIVER_SG) && !drm_core_check_feature(dev, DRIVER_MODESET) && dev->sg) { drm_sg_cleanup(dev->sg); dev->sg = NULL; } @@ -251,12 +225,13 @@ int drm_lastclose(struct drm_device * dev) drm_ctl_free(vma, sizeof(*vma), DRM_MEM_VMAS); } + /* list_for_each_entry_safe(r_list, list_t, &dev->maplist, head) { if (!(r_list->map->flags & _DRM_DRIVER)) { drm_rmmap_locked(dev, r_list->map); r_list = NULL; } - } + }*/ if (drm_core_check_feature(dev, DRIVER_DMA_QUEUE) && dev->queuelist) { for (i = 0; i < dev->queue_count; i++) { @@ -275,14 +250,9 @@ int drm_lastclose(struct drm_device * dev) } dev->queue_count = 0; - if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) + if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) && !drm_core_check_feature(dev, DRIVER_MODESET)) 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); @@ -409,12 +379,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); @@ -422,9 +394,12 @@ static void drm_cleanup(struct drm_device * dev) drm_ctxbitmap_cleanup(dev); drm_ht_remove(&dev->map_hash); drm_mm_takedown(&dev->offset_manager); - drm_ht_remove(&dev->object_hash); - drm_put_minor(dev); + if (drm_core_check_feature(dev, DRIVER_MODESET)) + drm_put_minor(&dev->control); + + drm_put_minor(&dev->primary); + if (drm_put_dev(dev)) DRM_ERROR("Cannot unload module\n"); } @@ -439,8 +414,14 @@ int drm_minors_cleanup(int id, void *ptr, void *data) if (minor->dev->driver != driver) return 0; - if (minor->type != DRM_MINOR_LEGACY) - return 0; + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + if (minor->type != DRM_MINOR_CONTROL) + return 0; + } else { + if (minor->type != DRM_MINOR_LEGACY) + return 0; + } + if (dev) pci_dev_put(dev->pdev); @@ -626,6 +607,7 @@ long drm_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) retcode = -EINVAL; goto err_i1; } + #if 0 /* * This check is disabled, because driver private ioctl->cmd @@ -647,7 +629,7 @@ long drm_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (cmd & IOC_IN) { if (copy_from_user(kdata, (void __user *)arg, _IOC_SIZE(cmd)) != 0) { - retcode = -EACCES; + retcode = -EFAULT; goto err_i1; } } @@ -657,7 +639,8 @@ long drm_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) retcode = -EINVAL; } else if (((ioctl->flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN)) || ((ioctl->flags & DRM_AUTH) && !file_priv->authenticated) || - ((ioctl->flags & DRM_MASTER) && !file_priv->master)) { + ((ioctl->flags & DRM_MASTER) && !file_priv->master) || + ((!(ioctl->flags & DRM_CONTROL_ALLOW)) && (file_priv->minor->type == DRM_MINOR_CONTROL)) ) { retcode = -EACCES; } else { retcode = func(dev, kdata, file_priv); @@ -666,13 +649,13 @@ long drm_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (cmd & IOC_OUT) { if (copy_to_user((void __user *)arg, kdata, _IOC_SIZE(cmd)) != 0) - retcode = -EACCES; + retcode = -EFAULT; } err_i1: atomic_dec(&dev->ioctl_count); if (retcode) - DRM_DEBUG("ret = %d\n", retcode); + DRM_ERROR("ret = %x %d\n", nr, retcode); return retcode; } EXPORT_SYMBOL(drm_unlocked_ioctl); diff --git a/linux-core/drm_edid.c b/linux-core/drm_edid.c new file mode 100644 index 00000000..07894720 --- /dev/null +++ b/linux-core/drm_edid.c @@ -0,0 +1,737 @@ +/* + * Copyright (c) 2006 Luc Verhaegen (quirks list) + * Copyright (c) 2007 Intel Corporation + * Jesse Barnes <jesse.barnes@intel.com> + * + * DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid) originally from + * FB layer. + * Copyright (C) 2006 Dennis Munsie <dmunsie@cecropia.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sub license, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include <linux/kernel.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include "drmP.h" +#include "drm_edid.h" + +/* + * TODO: + * - support EDID 1.4 + * - port quirks from X server code + */ + +/* + * EDID blocks out in the wild have a variety of bugs, try to collect + * them here (note that userspace may work around broken monitors first, + * but fixes should make their way here so that the kernel "just works" + * on as many displays as possible). + */ + +/* First detailed mode wrong, use largest 60Hz mode */ +#define EDID_QUIRK_PREFER_LARGE_60 (1 << 0) +/* Reported 135MHz pixel clock is too high, needs adjustment */ +#define EDID_QUIRK_135_CLOCK_TOO_HIGH (1 << 1) +/* Prefer the largest mode at 75 Hz */ +#define EDID_QUIRK_PREFER_LARGE_75 (1 << 2) +/* Detail timing is in cm not mm */ +#define EDID_QUIRK_DETAILED_IN_CM (1 << 3) +/* Detailed timing descriptors have bogus size values, so just take the + * maximum size and use that. + */ +#define EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE (1 << 4) +/* Monitor forgot to set the first detailed is preferred bit. */ +#define EDID_QUIRK_FIRST_DETAILED_PREFERRED (1 << 5) +/* use +hsync +vsync for detailed mode */ +#define EDID_QUIRK_DETAILED_SYNC_PP (1 << 6) + +static struct edid_quirk { + char *vendor; + int product_id; + u32 quirks; +} edid_quirk_list[] = { + /* Acer AL1706 */ + { "ACR", 44358, EDID_QUIRK_PREFER_LARGE_60 }, + /* Acer F51 */ + { "API", 0x7602, EDID_QUIRK_PREFER_LARGE_60 }, + /* Unknown Acer */ + { "ACR", 2423, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, + + /* Belinea 10 15 55 */ + { "MAX", 1516, EDID_QUIRK_PREFER_LARGE_60 }, + { "MAX", 0x77e, EDID_QUIRK_PREFER_LARGE_60 }, + + /* Envision Peripherals, Inc. EN-7100e */ + { "EPI", 59264, EDID_QUIRK_135_CLOCK_TOO_HIGH }, + + /* Funai Electronics PM36B */ + { "FCM", 13600, EDID_QUIRK_PREFER_LARGE_75 | + EDID_QUIRK_DETAILED_IN_CM }, + + /* LG Philips LCD LP154W01-A5 */ + { "LPL", 0, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE }, + { "LPL", 0x2a00, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE }, + + /* Philips 107p5 CRT */ + { "PHL", 57364, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, + + /* Proview AY765C */ + { "PTS", 765, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, + + /* Samsung SyncMaster 205BW. Note: irony */ + { "SAM", 541, EDID_QUIRK_DETAILED_SYNC_PP }, + /* Samsung SyncMaster 22[5-6]BW */ + { "SAM", 596, EDID_QUIRK_PREFER_LARGE_60 }, + { "SAM", 638, EDID_QUIRK_PREFER_LARGE_60 }, +}; + + +/* Valid EDID header has these bytes */ +static u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; + +/** + * edid_is_valid - sanity check EDID data + * @edid: EDID data + * + * Sanity check the EDID block by looking at the header, the version number + * and the checksum. Return 0 if the EDID doesn't check out, or 1 if it's + * valid. + */ +static bool edid_is_valid(struct edid *edid) +{ + int i; + u8 csum = 0; + u8 *raw_edid = (u8 *)edid; + + if (memcmp(edid->header, edid_header, sizeof(edid_header))) + goto bad; + if (edid->version != 1) { + DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version); + goto bad; + } + if (edid->revision <= 0 || edid->revision > 3) { + DRM_ERROR("EDID has minor version %d, which is not between 0-3\n", edid->revision); + goto bad; + } + + for (i = 0; i < EDID_LENGTH; i++) + csum += raw_edid[i]; + if (csum) { + DRM_ERROR("EDID checksum is invalid, remainder is %d\n", csum); + goto bad; + } + + return 1; + +bad: + if (raw_edid) { + DRM_ERROR("Raw EDID:\n"); + print_hex_dump_bytes(KERN_ERR, DUMP_PREFIX_NONE, raw_edid, EDID_LENGTH); + printk("\n"); + } + return 0; +} + +/** + * edid_vendor - match a string against EDID's obfuscated vendor field + * @edid: EDID to match + * @vendor: vendor string + * + * Returns true if @vendor is in @edid, false otherwise + */ +static bool edid_vendor(struct edid *edid, char *vendor) +{ + char edid_vendor[3]; + + edid_vendor[0] = ((edid->mfg_id[0] & 0x7c) >> 2) + '@'; + edid_vendor[1] = (((edid->mfg_id[0] & 0x3) << 3) | + ((edid->mfg_id[1] & 0xe0) >> 5)) + '@'; + edid_vendor[2] = (edid->mfg_id[2] & 0x1f) + '@'; + + return !strncmp(edid_vendor, vendor, 3); +} + +/** + * edid_get_quirks - return quirk flags for a given EDID + * @edid: EDID to process + * + * This tells subsequent routines what fixes they need to apply. + */ +static u32 edid_get_quirks(struct edid *edid) +{ + struct edid_quirk *quirk; + int i; + + for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) { + quirk = &edid_quirk_list[i]; + + if (edid_vendor(edid, quirk->vendor) && + (EDID_PRODUCT_ID(edid) == quirk->product_id)) + return quirk->quirks; + } + + return 0; +} + +#define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay) +#define MODE_REFRESH_DIFF(m,r) (abs((m)->vrefresh - target_refresh)) + + +/** + * edid_fixup_preferred - set preferred modes based on quirk list + * @connector: has mode list to fix up + * @quirks: quirks list + * + * Walk the mode list for @connector, clearing the preferred status + * on existing modes and setting it anew for the right mode ala @quirks. + */ +static void edid_fixup_preferred(struct drm_connector *connector, + u32 quirks) +{ + struct drm_display_mode *t, *cur_mode, *preferred_mode; + int target_refresh; + + if (list_empty(&connector->probed_modes)) + return; + + if (quirks & EDID_QUIRK_PREFER_LARGE_60) + target_refresh = 60; + if (quirks & EDID_QUIRK_PREFER_LARGE_75) + target_refresh = 75; + + preferred_mode = list_first_entry(&connector->probed_modes, + struct drm_display_mode, head); + + list_for_each_entry_safe(cur_mode, t, &connector->probed_modes, head) { + cur_mode->type &= ~DRM_MODE_TYPE_PREFERRED; + + if (cur_mode == preferred_mode) + continue; + + /* Largest mode is preferred */ + if (MODE_SIZE(cur_mode) > MODE_SIZE(preferred_mode)) + preferred_mode = cur_mode; + + /* At a given size, try to get closest to target refresh */ + if ((MODE_SIZE(cur_mode) == MODE_SIZE(preferred_mode)) && + MODE_REFRESH_DIFF(cur_mode, target_refresh) < + MODE_REFRESH_DIFF(preferred_mode, target_refresh)) { + preferred_mode = cur_mode; + } + } + + preferred_mode->type |= DRM_MODE_TYPE_PREFERRED; +} + +/** + * drm_mode_std - convert standard mode info (width, height, refresh) into mode + * @t: standard timing params + * + * Take the standard timing params (in this case width, aspect, and refresh) + * and convert them into a real mode using CVT. + * + * Punts for now, but should eventually use the FB layer's CVT based mode + * generation code. + */ +struct drm_display_mode *drm_mode_std(struct drm_device *dev, + struct std_timing *t) +{ +// struct fb_videomode mode; + +// fb_find_mode_cvt(&mode, 0, 0); + /* JJJ: convert to drm_display_mode */ + struct drm_display_mode *mode; + int hsize = t->hsize * 8 + 248, vsize; + + mode = drm_mode_create(dev); + if (!mode) + return NULL; + + if (t->aspect_ratio == 0) + vsize = (hsize * 10) / 16; + else if (t->aspect_ratio == 1) + vsize = (hsize * 3) / 4; + else if (t->aspect_ratio == 2) + vsize = (hsize * 4) / 5; + else + vsize = (hsize * 9) / 16; + + drm_mode_set_name(mode); + + return mode; +} + +/** + * drm_mode_detailed - create a new mode from an EDID detailed timing section + * @dev: DRM device (needed to create new mode) + * @edid: EDID block + * @timing: EDID detailed timing info + * @quirks: quirks to apply + * + * An EDID detailed timing block contains enough info for us to create and + * return a new struct drm_display_mode. + */ +static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, + struct edid *edid, + struct detailed_timing *timing, + u32 quirks) +{ + struct drm_display_mode *mode; + struct detailed_pixel_timing *pt = &timing->data.pixel_data; + + if (pt->stereo) { + printk(KERN_WARNING "stereo mode not supported\n"); + return NULL; + } + if (!pt->separate_sync) { + printk(KERN_WARNING "integrated sync not supported\n"); + return NULL; + } + + mode = drm_mode_create(dev); + if (!mode) + return NULL; + + mode->type = DRM_MODE_TYPE_DRIVER; + + if (quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH) + timing->pixel_clock = 1088; + + mode->clock = timing->pixel_clock * 10; + + mode->hdisplay = (pt->hactive_hi << 8) | pt->hactive_lo; + mode->hsync_start = mode->hdisplay + ((pt->hsync_offset_hi << 8) | + pt->hsync_offset_lo); + mode->hsync_end = mode->hsync_start + + ((pt->hsync_pulse_width_hi << 8) | + pt->hsync_pulse_width_lo); + mode->htotal = mode->hdisplay + ((pt->hblank_hi << 8) | pt->hblank_lo); + + mode->vdisplay = (pt->vactive_hi << 8) | pt->vactive_lo; + mode->vsync_start = mode->vdisplay + ((pt->vsync_offset_hi << 8) | + pt->vsync_offset_lo); + mode->vsync_end = mode->vsync_start + + ((pt->vsync_pulse_width_hi << 8) | + pt->vsync_pulse_width_lo); + mode->vtotal = mode->vdisplay + ((pt->vblank_hi << 8) | pt->vblank_lo); + + drm_mode_set_name(mode); + + if (pt->interlaced) + mode->flags |= DRM_MODE_FLAG_INTERLACE; + + if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) { + pt->hsync_positive = 1; + pt->vsync_positive = 1; + } + + mode->flags |= pt->hsync_positive ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; + mode->flags |= pt->vsync_positive ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; + + mode->width_mm = pt->width_mm_lo | (pt->width_mm_hi << 8); + mode->height_mm = pt->height_mm_lo | (pt->height_mm_hi << 8); + + if (quirks & EDID_QUIRK_DETAILED_IN_CM) { + mode->width_mm *= 10; + mode->height_mm *= 10; + } + + if (quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) { + mode->width_mm = edid->width_cm * 10; + mode->height_mm = edid->height_cm * 10; + } + + return mode; +} + +/* + * Detailed mode info for the EDID "established modes" data to use. + */ +static struct drm_display_mode edid_est_modes[] = { + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, + 968, 1056, 0, 600, 601, 605, 628, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@60Hz */ + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824, + 896, 1024, 0, 600, 601, 603, 625, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@56Hz */ + { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656, + 720, 840, 0, 480, 481, 484, 500, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@75Hz */ + { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664, + 704, 832, 0, 480, 489, 491, 520, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@72Hz */ + { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 30240, 640, 704, + 768, 864, 0, 480, 483, 486, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@67Hz */ + { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25200, 640, 656, + 752, 800, 0, 480, 490, 492, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@60Hz */ + { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 738, + 846, 900, 0, 400, 421, 423, 449, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 720x400@88Hz */ + { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738, + 846, 900, 0, 400, 412, 414, 449, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 720x400@70Hz */ + { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296, + 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1280x1024@75Hz */ + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78800, 1024, 1040, + 1136, 1312, 0, 768, 769, 772, 800, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1024x768@75Hz */ + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048, + 1184, 1328, 0, 768, 771, 777, 806, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@70Hz */ + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, + 1184, 1344, 0, 768, 771, 777, 806, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@60Hz */ + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032, + 1208, 1264, 0, 768, 768, 776, 817, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 1024x768@43Hz */ + { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864, + 928, 1152, 0, 624, 625, 628, 667, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 832x624@75Hz */ + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816, + 896, 1056, 0, 600, 601, 604, 625, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@75Hz */ + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856, + 976, 1040, 0, 600, 637, 643, 666, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@72Hz */ + { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, + 1344, 1600, 0, 864, 865, 868, 900, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1152x864@75Hz */ +}; + +#define EDID_EST_TIMINGS 16 +#define EDID_STD_TIMINGS 8 +#define EDID_DETAILED_TIMINGS 4 + +/** + * add_established_modes - get est. modes from EDID and add them + * @edid: EDID block to scan + * + * Each EDID block contains a bitmap of the supported "established modes" list + * (defined above). Tease them out and add them to the global modes list. + */ +static int add_established_modes(struct drm_connector *connector, struct edid *edid) +{ + struct drm_device *dev = connector->dev; + unsigned long est_bits = edid->established_timings.t1 | + (edid->established_timings.t2 << 8) | + ((edid->established_timings.mfg_rsvd & 0x80) << 9); + int i, modes = 0; + + for (i = 0; i <= EDID_EST_TIMINGS; i++) + if (est_bits & (1<<i)) { + struct drm_display_mode *newmode; + newmode = drm_mode_duplicate(dev, &edid_est_modes[i]); + if (newmode) { + drm_mode_probed_add(connector, newmode); + modes++; + } + } + + return modes; +} + +/** + * add_standard_modes - get std. modes from EDID and add them + * @edid: EDID block to scan + * + * Standard modes can be calculated using the CVT standard. Grab them from + * @edid, calculate them, and add them to the list. + */ +static int add_standard_modes(struct drm_connector *connector, struct edid *edid) +{ + struct drm_device *dev = connector->dev; + int i, modes = 0; + + for (i = 0; i < EDID_STD_TIMINGS; i++) { + struct std_timing *t = &edid->standard_timings[i]; + struct drm_display_mode *newmode; + + /* If std timings bytes are 1, 1 it's empty */ + if (t->hsize == 1 && (t->aspect_ratio | t->vfreq) == 1) + continue; + + newmode = drm_mode_std(dev, &edid->standard_timings[i]); + if (newmode) { + drm_mode_probed_add(connector, newmode); + modes++; + } + } + + return modes; +} + +/** + * add_detailed_modes - get detailed mode info from EDID data + * @connector: attached connector + * @edid: EDID block to scan + * @quirks: quirks to apply + * + * Some of the detailed timing sections may contain mode information. Grab + * it and add it to the list. + */ +static int add_detailed_info(struct drm_connector *connector, + struct edid *edid, u32 quirks) +{ + struct drm_device *dev = connector->dev; + int i, j, modes = 0; + + for (i = 0; i < EDID_DETAILED_TIMINGS; i++) { + struct detailed_timing *timing = &edid->detailed_timings[i]; + struct detailed_non_pixel *data = &timing->data.other_data; + struct drm_display_mode *newmode; + + /* EDID up to and including 1.2 may put monitor info here */ + if (edid->version == 1 && edid->revision < 3) + continue; + + /* Detailed mode timing */ + if (timing->pixel_clock) { + newmode = drm_mode_detailed(dev, edid, timing, quirks); + if (!newmode) + continue; + + /* First detailed mode is preferred */ + if (i == 0 && edid->preferred_timing) + newmode->type |= DRM_MODE_TYPE_PREFERRED; + drm_mode_probed_add(connector, newmode); + + modes++; + continue; + } + + /* Other timing or info */ + switch (data->type) { + case EDID_DETAIL_MONITOR_SERIAL: + break; + case EDID_DETAIL_MONITOR_STRING: + break; + case EDID_DETAIL_MONITOR_RANGE: + /* Get monitor range data */ + break; + case EDID_DETAIL_MONITOR_NAME: + break; + case EDID_DETAIL_MONITOR_CPDATA: + break; + case EDID_DETAIL_STD_MODES: + /* Five modes per detailed section */ + for (j = 0; j < 5; i++) { + struct std_timing *std; + struct drm_display_mode *newmode; + + std = &data->data.timings[j]; + newmode = drm_mode_std(dev, std); + if (newmode) { + drm_mode_probed_add(connector, newmode); + modes++; + } + } + break; + default: + break; + } + } + + return modes; +} + +#define DDC_ADDR 0x50 + +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; +} +EXPORT_SYMBOL(drm_do_probe_ddc_edid); + +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; + + algo_data->setscl(algo_data->data, 1); + + 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, 1); + 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); + algo_data->setsda(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, 1); + algo_data->setscl(algo_data->data, 1); + + return edid; +} + +/** + * drm_get_edid - get EDID data, if available + * @connector: connector we're probing + * @adapter: i2c adapter to use for DDC + * + * Poke the given connector's i2c channel to grab EDID data if possible. + * + * Return edid data or NULL if we couldn't find any. + */ +struct edid *drm_get_edid(struct drm_connector *connector, + struct i2c_adapter *adapter) +{ + struct edid *edid; + + edid = (struct edid *)drm_ddc_read(adapter); + if (!edid) { + dev_warn(&connector->dev->pdev->dev, "%s: no EDID data\n", + drm_get_connector_name(connector)); + return NULL; + } + if (!edid_is_valid(edid)) { + dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n", + drm_get_connector_name(connector)); + kfree(edid); + return NULL; + } + + connector->display_info.raw_edid = (char *)edid; + + return edid; +} +EXPORT_SYMBOL(drm_get_edid); + +/** + * drm_add_edid_modes - add modes from EDID data, if available + * @connector: connector we're probing + * @edid: edid data + * + * Add the specified modes to the connector's mode list. + * + * Return number of modes added or 0 if we couldn't find any. + */ +int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) +{ + int num_modes = 0; + u32 quirks; + + if (edid == NULL) { + return 0; + } + if (!edid_is_valid(edid)) { + dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n", + drm_get_connector_name(connector)); + return 0; + } + + quirks = edid_get_quirks(edid); + + num_modes += add_established_modes(connector, edid); + num_modes += add_standard_modes(connector, edid); + num_modes += add_detailed_info(connector, edid, quirks); + + if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75)) + edid_fixup_preferred(connector, quirks); + + connector->display_info.serration_vsync = edid->serration_vsync; + connector->display_info.sync_on_green = edid->sync_on_green; + connector->display_info.composite_sync = edid->composite_sync; + connector->display_info.separate_syncs = edid->separate_syncs; + connector->display_info.blank_to_black = edid->blank_to_black; + connector->display_info.video_level = edid->video_level; + connector->display_info.digital = edid->digital; + connector->display_info.width_mm = edid->width_cm * 10; + connector->display_info.height_mm = edid->height_cm * 10; + connector->display_info.gamma = edid->gamma; + connector->display_info.gtf_supported = edid->default_gtf; + connector->display_info.standard_color = edid->standard_color; + connector->display_info.display_type = edid->display_type; + connector->display_info.active_off_supported = edid->pm_active_off; + connector->display_info.suspend_supported = edid->pm_suspend; + connector->display_info.standby_supported = edid->pm_standby; + connector->display_info.gamma = edid->gamma; + + return num_modes; +} +EXPORT_SYMBOL(drm_add_edid_modes); diff --git a/linux-core/drm_edid.h b/linux-core/drm_edid.h new file mode 100644 index 00000000..dccfba91 --- /dev/null +++ b/linux-core/drm_edid.h @@ -0,0 +1,178 @@ +#ifndef __DRM_EDID_H__ +#define __DRM_EDID_H__ + +#include <linux/types.h> + +#define EDID_LENGTH 128 +#define DDC_ADDR 0x50 + +#ifdef BIG_ENDIAN +#error "EDID structure is little endian, need big endian versions" +#endif + +struct est_timings { + u8 t1; + u8 t2; + u8 mfg_rsvd; +} __attribute__((packed)); + +struct std_timing { + u8 hsize; /* need to multiply by 8 then add 248 */ + u8 vfreq:6; /* need to add 60 */ + u8 aspect_ratio:2; /* 00=16:10, 01=4:3, 10=5:4, 11=16:9 */ +} __attribute__((packed)); + +/* If detailed data is pixel timing */ +struct detailed_pixel_timing { + u8 hactive_lo; + u8 hblank_lo; + u8 hblank_hi:4; + u8 hactive_hi:4; + u8 vactive_lo; + u8 vblank_lo; + u8 vblank_hi:4; + u8 vactive_hi:4; + u8 hsync_offset_lo; + u8 hsync_pulse_width_lo; + u8 vsync_pulse_width_lo:4; + u8 vsync_offset_lo:4; + u8 hsync_pulse_width_hi:2; + u8 hsync_offset_hi:2; + u8 vsync_pulse_width_hi:2; + u8 vsync_offset_hi:2; + u8 width_mm_lo; + u8 height_mm_lo; + u8 height_mm_hi:4; + u8 width_mm_hi:4; + u8 hborder; + u8 vborder; + u8 unknown0:1; + u8 vsync_positive:1; + u8 hsync_positive:1; + u8 separate_sync:2; + u8 stereo:1; + u8 unknown6:1; + u8 interlaced:1; +} __attribute__((packed)); + +/* If it's not pixel timing, it'll be one of the below */ +struct detailed_data_string { + u8 str[13]; +} __attribute__((packed)); + +struct detailed_data_monitor_range { + u8 min_vfreq; + u8 max_vfreq; + u8 min_hfreq_khz; + u8 max_hfreq_khz; + u8 pixel_clock_mhz; /* need to multiply by 10 */ + u16 sec_gtf_toggle; /* A000=use above, 20=use below */ /* FIXME: byte order */ + u8 hfreq_start_khz; /* need to multiply by 2 */ + u8 c; /* need to divide by 2 */ + u16 m; /* FIXME: byte order */ + u8 k; + u8 j; /* need to divide by 2 */ +} __attribute__((packed)); + +struct detailed_data_wpindex { + u8 white_y_lo:2; + u8 white_x_lo:2; + u8 pad:4; + u8 white_x_hi; + u8 white_y_hi; + u8 gamma; /* need to divide by 100 then add 1 */ +} __attribute__((packed)); + +struct detailed_data_color_point { + u8 windex1; + u8 wpindex1[3]; + u8 windex2; + u8 wpindex2[3]; +} __attribute__((packed)); + +struct detailed_non_pixel { + u8 pad1; + u8 type; /* ff=serial, fe=string, fd=monitor range, fc=monitor name + fb=color point data, fa=standard timing data, + f9=undefined, f8=mfg. reserved */ + u8 pad2; + union { + struct detailed_data_string str; + struct detailed_data_monitor_range range; + struct detailed_data_wpindex color; + struct std_timing timings[5]; + } data; +} __attribute__((packed)); + +#define EDID_DETAIL_STD_MODES 0xfa +#define EDID_DETAIL_MONITOR_CPDATA 0xfb +#define EDID_DETAIL_MONITOR_NAME 0xfc +#define EDID_DETAIL_MONITOR_RANGE 0xfd +#define EDID_DETAIL_MONITOR_STRING 0xfe +#define EDID_DETAIL_MONITOR_SERIAL 0xff + +struct detailed_timing { + u16 pixel_clock; /* need to multiply by 10 KHz */ /* FIXME: byte order */ + union { + struct detailed_pixel_timing pixel_data; + struct detailed_non_pixel other_data; + } data; +} __attribute__((packed)); + +struct edid { + u8 header[8]; + /* Vendor & product info */ + u8 mfg_id[2]; + u8 prod_code[2]; + u32 serial; /* FIXME: byte order */ + u8 mfg_week; + u8 mfg_year; + /* EDID version */ + u8 version; + u8 revision; + /* Display info: */ + /* input definition */ + u8 serration_vsync:1; + u8 sync_on_green:1; + u8 composite_sync:1; + u8 separate_syncs:1; + u8 blank_to_black:1; + u8 video_level:2; + u8 digital:1; /* bits below must be zero if set */ + u8 width_cm; + u8 height_cm; + u8 gamma; + /* feature support */ + u8 default_gtf:1; + u8 preferred_timing:1; + u8 standard_color:1; + u8 display_type:2; /* 00=mono, 01=rgb, 10=non-rgb, 11=unknown */ + u8 pm_active_off:1; + u8 pm_suspend:1; + u8 pm_standby:1; + /* Color characteristics */ + u8 red_green_lo; + u8 black_white_lo; + u8 red_x; + u8 red_y; + u8 green_x; + u8 green_y; + u8 blue_x; + u8 blue_y; + u8 white_x; + u8 white_y; + /* Est. timings and mfg rsvd timings*/ + struct est_timings established_timings; + /* Standard timings 1-8*/ + struct std_timing standard_timings[8]; + /* Detailing timings 1-4 */ + struct detailed_timing detailed_timings[4]; + /* Number of 128 byte ext. blocks */ + u8 extensions; + /* Checksum */ + u8 checksum; +} __attribute__((packed)); + +#define EDID_PRODUCT_ID(e) ((e)->prod_code[0] | ((e)->prod_code[1] << 8)) + +#endif /* __DRM_EDID_H__ */ diff --git a/linux-core/drm_fb.c b/linux-core/drm_fb.c new file mode 100644 index 00000000..74a4394a --- /dev/null +++ b/linux-core/drm_fb.c @@ -0,0 +1,432 @@ +/* + * Copyright © 2007 David Airlie + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * David Airlie + */ + /* + * Modularization + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/init.h> + +#include "drmP.h" +#include "drm_crtc.h" + +struct drmfb_par { + struct drm_device *dev; + struct drm_crtc *crtc; +}; + +static int drmfb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) +{ + struct drmfb_par *par = info->par; + struct drm_framebuffer *fb = par->crtc->fb; + struct drm_crtc *crtc = par->crtc; + + if (regno > 255) + return 1; + + if (fb->depth == 8) { + return 0; + } + + if (regno < 16) { + switch (fb->depth) { + case 15: + fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) | + ((green & 0xf800) >> 6) | + ((blue & 0xf800) >> 11); + break; + case 16: + fb->pseudo_palette[regno] = (red & 0xf800) | + ((green & 0xfc00) >> 5) | + ((blue & 0xf800) >> 11); + break; + case 24: + case 32: + fb->pseudo_palette[regno] = ((red & 0xff00) << 8) | + (green & 0xff00) | + ((blue & 0xff00) >> 8); + break; + } + } + + return 0; +} + +static int drmfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct drmfb_par *par = info->par; + struct drm_device *dev = par->dev; + struct drm_framebuffer *fb = par->crtc->fb; + struct drm_display_mode *drm_mode; + struct drm_output *output; + int depth; + + if (!var->pixclock) + return -EINVAL; + + /* Need to resize the fb object !!! */ + if (var->xres > fb->width || var->yres > fb->height) { + DRM_ERROR("Requested width/height is greater than current fb object %dx%d > %dx%d\n",var->xres,var->yres,fb->width,fb->height); + DRM_ERROR("Need resizing code.\n"); + return -EINVAL; + } + + switch (var->bits_per_pixel) { + case 16: + depth = (var->green.length == 6) ? 16 : 15; + break; + case 32: + depth = (var->transp.length > 0) ? 32 : 24; + break; + default: + depth = var->bits_per_pixel; + break; + } + + switch (depth) { + case 8: + var->red.offset = 0; + var->green.offset = 0; + var->blue.offset = 0; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 0; + var->transp.offset = 0; + break; + case 15: + var->red.offset = 10; + var->green.offset = 5; + var->blue.offset = 0; + var->red.length = 5; + var->green.length = 5; + var->blue.length = 5; + var->transp.length = 1; + var->transp.offset = 15; + break; + case 16: + var->red.offset = 11; + var->green.offset = 6; + var->blue.offset = 0; + var->red.length = 5; + var->green.length = 6; + var->blue.length = 5; + var->transp.length = 0; + var->transp.offset = 0; + break; + case 24: + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 0; + var->transp.offset = 0; + break; + case 32: + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 8; + var->transp.offset = 24; + break; + default: + return -EINVAL; + } + +#if 0 + /* Here we walk the output mode list and look for modes. If we haven't + * got it, then bail. Not very nice, so this is disabled. + * In the set_par code, we create our mode based on the incoming + * parameters. Nicer, but may not be desired by some. + */ + list_for_each_entry(output, &dev->mode_config.output_list, head) { + if (output->crtc == par->crtc) + break; + } + + list_for_each_entry(drm_mode, &output->modes, head) { + if (drm_mode->hdisplay == var->xres && + drm_mode->vdisplay == var->yres && + drm_mode->clock != 0) + break; + } + + if (!drm_mode) + return -EINVAL; +#endif + + return 0; +} + +/* this will let fbcon do the mode init */ +static int drmfb_set_par(struct fb_info *info) +{ + struct drmfb_par *par = info->par; + struct drm_framebuffer *fb = par->crtc->fb; + struct drm_device *dev = par->dev; + struct drm_display_mode *drm_mode; + struct fb_var_screeninfo *var = &info->var; + struct drm_output *output; + + switch (var->bits_per_pixel) { + case 16: + fb->depth = (var->green.length == 6) ? 16 : 15; + break; + case 32: + fb->depth = (var->transp.length > 0) ? 32 : 24; + break; + default: + fb->depth = var->bits_per_pixel; + break; + } + + fb->bits_per_pixel = var->bits_per_pixel; + + info->fix.line_length = fb->pitch; + info->fix.smem_len = info->fix.line_length * fb->height; + info->fix.visual = (fb->depth == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; + + info->screen_size = info->fix.smem_len; /* ??? */ + + /* Should we walk the output's modelist or just create our own ??? + * For now, we create and destroy a mode based on the incoming + * parameters. But there's commented out code below which scans + * the output list too. + */ +#if 0 + list_for_each_entry(output, &dev->mode_config.output_list, head) { + if (output->crtc == par->crtc) + break; + } + + list_for_each_entry(drm_mode, &output->modes, head) { + if (drm_mode->hdisplay == var->xres && + drm_mode->vdisplay == var->yres && + drm_mode->clock != 0) + break; + } +#else + drm_mode = drm_mode_create(dev); + drm_mode->hdisplay = var->xres; + drm_mode->hsync_start = drm_mode->hdisplay + var->right_margin; + drm_mode->hsync_end = drm_mode->hsync_start + var->hsync_len; + drm_mode->htotal = drm_mode->hsync_end + var->left_margin; + drm_mode->vdisplay = var->yres; + drm_mode->vsync_start = drm_mode->vdisplay + var->lower_margin; + drm_mode->vsync_end = drm_mode->vsync_start + var->vsync_len; + drm_mode->vtotal = drm_mode->vsync_end + var->upper_margin; + drm_mode->clock = PICOS2KHZ(var->pixclock); + drm_mode->vrefresh = drm_mode_vrefresh(drm_mode); + drm_mode_set_name(drm_mode); + drm_mode_set_crtcinfo(drm_mode, CRTC_INTERLACE_HALVE_V); +#endif + + if (!drm_crtc_set_mode(par->crtc, drm_mode, 0, 0)) + return -EINVAL; + + /* Have to destroy our created mode if we're not searching the mode + * list for it. + */ +#if 1 + drm_mode_destroy(dev, drm_mode); +#endif + + return 0; +} + +static struct fb_ops drmfb_ops = { + .owner = THIS_MODULE, + // .fb_open = drmfb_open, + // .fb_read = drmfb_read, + // .fb_write = drmfb_write, + // .fb_release = drmfb_release, + // .fb_ioctl = drmfb_ioctl, + .fb_check_var = drmfb_check_var, + .fb_set_par = drmfb_set_par, + .fb_setcolreg = drmfb_setcolreg, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +int drmfb_probe(struct drm_device *dev, struct drm_crtc *crtc) +{ + struct fb_info *info; + struct drm_framebuffer *fb = crtc->fb; + struct drmfb_par *par; + struct device *device = &dev->pdev->dev; + struct drm_display_mode *mode = crtc->desired_mode; + int ret; + + info = framebuffer_alloc(sizeof(struct drmfb_par), device); + if (!info) + return -ENOMEM; + + fb->fbdev = info; + + par = info->par; + + par->dev = dev; + par->crtc = crtc; + + info->fbops = &drmfb_ops; + + strcpy(info->fix.id, "drmfb"); + info->fix.type = FB_TYPE_PACKED_PIXELS; + info->fix.visual = FB_VISUAL_TRUECOLOR; + info->fix.accel = FB_ACCEL_NONE; + info->fix.type_aux = 0; + info->fix.mmio_start = 0; + info->fix.mmio_len = 0; + info->fix.line_length = fb->pitch; + info->fix.smem_start = fb->offset + dev->mode_config.fb_base; + info->fix.smem_len = info->fix.line_length * fb->height; + + info->flags = FBINFO_DEFAULT; + + ret = drm_mem_reg_ioremap(dev, &fb->bo->mem, &fb->virtual_base); + if (ret) + DRM_ERROR("error mapping fb: %d\n", ret); + + info->screen_base = fb->virtual_base; + info->screen_size = info->fix.smem_len; /* ??? */ + info->pseudo_palette = fb->pseudo_palette; + info->var.xres_virtual = fb->width; + info->var.yres_virtual = fb->height; + info->var.bits_per_pixel = fb->bits_per_pixel; + info->var.xoffset = 0; + info->var.yoffset = 0; + info->var.activate = FB_ACTIVATE_NOW; + info->var.height = -1; + info->var.width = -1; + info->var.vmode = FB_VMODE_NONINTERLACED; + + info->var.xres = mode->hdisplay; + info->var.right_margin = mode->hsync_start - mode->hdisplay; + info->var.hsync_len = mode->hsync_end - mode->hsync_start; + info->var.left_margin = mode->htotal - mode->hsync_end; + info->var.yres = mode->vdisplay; + info->var.lower_margin = mode->vsync_start - mode->vdisplay; + info->var.vsync_len = mode->vsync_end - mode->vsync_start; + info->var.upper_margin = mode->vtotal - mode->vsync_end; + info->var.pixclock = 10000000 / mode->htotal * 1000 / + mode->vtotal * 100; + /* avoid overflow */ + info->var.pixclock = info->var.pixclock * 1000 / mode->vrefresh; + + DRM_DEBUG("fb depth is %d\n", fb->depth); + switch(fb->depth) { + case 8: + info->var.red.offset = 0; + info->var.green.offset = 0; + info->var.blue.offset = 0; + info->var.red.length = 8; /* 8bit DAC */ + info->var.green.length = 8; + info->var.blue.length = 8; + info->var.transp.offset = 0; + info->var.transp.length = 0; + break; + case 15: + info->var.red.offset = 10; + info->var.green.offset = 5; + info->var.blue.offset = 0; + info->var.red.length = info->var.green.length = + info->var.blue.length = 5; + info->var.transp.offset = 15; + info->var.transp.length = 1; + break; + case 16: + info->var.red.offset = 11; + info->var.green.offset = 5; + info->var.blue.offset = 0; + info->var.red.length = 5; + info->var.green.length = 6; + info->var.blue.length = 5; + info->var.transp.offset = 0; + info->var.transp.length = 0; + break; + case 24: + info->var.red.offset = 16; + info->var.green.offset = 8; + info->var.blue.offset = 0; + info->var.red.length = info->var.green.length = + info->var.blue.length = 8; + info->var.transp.offset = 0; + info->var.transp.length = 0; + break; + case 32: + info->var.red.offset = 16; + info->var.green.offset = 8; + info->var.blue.offset = 0; + info->var.red.length = info->var.green.length = + info->var.blue.length = 8; + info->var.transp.offset = 24; + info->var.transp.length = 8; + break; + default: + break; + } + + if (register_framebuffer(info) < 0) { + unregister_framebuffer(info); + return -EINVAL; + } + + printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, + info->fix.id); + return 0; +} +EXPORT_SYMBOL(drmfb_probe); + +int drmfb_remove(struct drm_device *dev, struct drm_crtc *crtc) +{ + struct fb_info *info = fb->fbdev; + struct drm_framebuffer *fb = crtc->fb; + + if (info) { + drm_mem_reg_iounmap(dev, &fb->bo->mem, fb->virtual_base); + unregister_framebuffer(info); + framebuffer_release(info); + } + return 0; +} +EXPORT_SYMBOL(drmfb_remove); +MODULE_LICENSE("GPL"); diff --git a/linux-core/drm_fence.c b/linux-core/drm_fence.c index 7c78e09f..f1c386c4 100644 --- a/linux-core/drm_fence.c +++ b/linux-core/drm_fence.c @@ -134,8 +134,8 @@ void drm_fence_handler(struct drm_device *dev, uint32_t fence_class, if (new_type) { fence->signaled_types |= new_type; - DRM_DEBUG("Fence 0x%08lx signaled 0x%08x\n", - fence->base.hash.key, fence->signaled_types); + DRM_DEBUG("Fence %p signaled 0x%08x\n", + fence, fence->signaled_types); if (driver->needed_flush) fc->pending_flush |= driver->needed_flush(fence); @@ -147,8 +147,8 @@ void drm_fence_handler(struct drm_device *dev, uint32_t fence_class, fc->waiting_types |= fence->waiting_types & ~fence->signaled_types; if (!(fence->type & ~fence->signaled_types)) { - DRM_DEBUG("Fence completely signaled 0x%08lx\n", - fence->base.hash.key); + DRM_DEBUG("Fence completely signaled %p\n", + fence); list_del_init(&fence->ring); } } @@ -196,10 +196,9 @@ void drm_fence_usage_deref_locked(struct drm_fence_object **fence) *fence = NULL; if (atomic_dec_and_test(&tmp_fence->usage)) { drm_fence_unring(dev, &tmp_fence->ring); - DRM_DEBUG("Destroyed a fence object 0x%08lx\n", - tmp_fence->base.hash.key); + DRM_DEBUG("Destroyed a fence object %p\n", + tmp_fence); atomic_dec(&fm->count); - BUG_ON(!list_empty(&tmp_fence->base.list)); drm_ctl_free(tmp_fence, sizeof(*tmp_fence), DRM_MEM_FENCE); } } @@ -217,7 +216,6 @@ void drm_fence_usage_deref_unlocked(struct drm_fence_object **fence) if (atomic_read(&tmp_fence->usage) == 0) { drm_fence_unring(dev, &tmp_fence->ring); atomic_dec(&fm->count); - BUG_ON(!list_empty(&tmp_fence->base.list)); drm_ctl_free(tmp_fence, sizeof(*tmp_fence), DRM_MEM_FENCE); } mutex_unlock(&dev->struct_mutex); @@ -244,15 +242,6 @@ void drm_fence_reference_unlocked(struct drm_fence_object **dst, } EXPORT_SYMBOL(drm_fence_reference_unlocked); -static void drm_fence_object_destroy(struct drm_file *priv, - struct drm_user_object *base) -{ - struct drm_fence_object *fence = - drm_user_object_entry(base, struct drm_fence_object, base); - - drm_fence_usage_deref_locked(&fence); -} - int drm_fence_object_signaled(struct drm_fence_object *fence, uint32_t mask) { unsigned long flags; @@ -392,7 +381,6 @@ int drm_fence_object_wait(struct drm_fence_object *fence, if (driver->wait) return driver->wait(fence, lazy, !ignore_signals, mask); - drm_fence_object_flush(fence, mask); if (driver->has_irq(dev, fence->fence_class, mask)) { if (!ignore_signals) @@ -420,8 +408,6 @@ int drm_fence_object_wait(struct drm_fence_object *fence, } EXPORT_SYMBOL(drm_fence_object_wait); - - int drm_fence_object_emit(struct drm_fence_object *fence, uint32_t fence_flags, uint32_t fence_class, uint32_t type) { @@ -477,7 +463,6 @@ static int drm_fence_object_init(struct drm_device *dev, uint32_t fence_class, * Avoid hitting BUG() for kernel-only fence objects. */ - INIT_LIST_HEAD(&fence->base.list); fence->fence_class = fence_class; fence->type = type; fence->signaled_types = 0; @@ -493,26 +478,6 @@ static int drm_fence_object_init(struct drm_device *dev, uint32_t fence_class, return ret; } -int drm_fence_add_user_object(struct drm_file *priv, - struct drm_fence_object *fence, int shareable) -{ - struct drm_device *dev = priv->minor->dev; - int ret; - - mutex_lock(&dev->struct_mutex); - ret = drm_add_user_object(priv, &fence->base, shareable); - if (ret) - goto out; - atomic_inc(&fence->usage); - fence->base.type = drm_fence_type; - fence->base.remove = &drm_fence_object_destroy; - DRM_DEBUG("Fence 0x%08lx created\n", fence->base.hash.key); -out: - mutex_unlock(&dev->struct_mutex); - return ret; -} -EXPORT_SYMBOL(drm_fence_add_user_object); - int drm_fence_object_create(struct drm_device *dev, uint32_t fence_class, uint32_t type, unsigned flags, struct drm_fence_object **c_fence) @@ -569,261 +534,7 @@ void drm_fence_manager_init(struct drm_device *dev) write_unlock_irqrestore(&fm->lock, flags); } -void drm_fence_fill_arg(struct drm_fence_object *fence, - struct drm_fence_arg *arg) -{ - struct drm_device *dev = fence->dev; - struct drm_fence_manager *fm = &dev->fm; - unsigned long irq_flags; - - read_lock_irqsave(&fm->lock, irq_flags); - arg->handle = fence->base.hash.key; - arg->fence_class = fence->fence_class; - arg->type = fence->type; - arg->signaled = fence->signaled_types; - arg->error = fence->error; - arg->sequence = fence->sequence; - read_unlock_irqrestore(&fm->lock, irq_flags); -} -EXPORT_SYMBOL(drm_fence_fill_arg); - void drm_fence_manager_takedown(struct drm_device *dev) { } -struct drm_fence_object *drm_lookup_fence_object(struct drm_file *priv, - uint32_t handle) -{ - struct drm_device *dev = priv->minor->dev; - struct drm_user_object *uo; - struct drm_fence_object *fence; - - mutex_lock(&dev->struct_mutex); - uo = drm_lookup_user_object(priv, handle); - if (!uo || (uo->type != drm_fence_type)) { - mutex_unlock(&dev->struct_mutex); - return NULL; - } - fence = drm_fence_reference_locked(drm_user_object_entry(uo, struct drm_fence_object, base)); - mutex_unlock(&dev->struct_mutex); - return fence; -} - -int drm_fence_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - int ret; - struct drm_fence_manager *fm = &dev->fm; - struct drm_fence_arg *arg = data; - struct drm_fence_object *fence; - ret = 0; - - if (!fm->initialized) { - DRM_ERROR("The DRM driver does not support fencing.\n"); - return -EINVAL; - } - - if (arg->flags & DRM_FENCE_FLAG_EMIT) - LOCK_TEST_WITH_RETURN(dev, file_priv); - ret = drm_fence_object_create(dev, arg->fence_class, - arg->type, arg->flags, &fence); - if (ret) - return ret; - ret = drm_fence_add_user_object(file_priv, fence, - arg->flags & - DRM_FENCE_FLAG_SHAREABLE); - if (ret) { - drm_fence_usage_deref_unlocked(&fence); - return ret; - } - - /* - * usage > 0. No need to lock dev->struct_mutex; - */ - - arg->handle = fence->base.hash.key; - - drm_fence_fill_arg(fence, arg); - drm_fence_usage_deref_unlocked(&fence); - - return ret; -} - -int drm_fence_reference_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - int ret; - struct drm_fence_manager *fm = &dev->fm; - struct drm_fence_arg *arg = data; - struct drm_fence_object *fence; - struct drm_user_object *uo; - ret = 0; - - if (!fm->initialized) { - DRM_ERROR("The DRM driver does not support fencing.\n"); - return -EINVAL; - } - - ret = drm_user_object_ref(file_priv, arg->handle, drm_fence_type, &uo); - if (ret) - return ret; - fence = drm_lookup_fence_object(file_priv, arg->handle); - drm_fence_fill_arg(fence, arg); - drm_fence_usage_deref_unlocked(&fence); - - return ret; -} - - -int drm_fence_unreference_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - int ret; - struct drm_fence_manager *fm = &dev->fm; - struct drm_fence_arg *arg = data; - ret = 0; - - if (!fm->initialized) { - DRM_ERROR("The DRM driver does not support fencing.\n"); - return -EINVAL; - } - - return drm_user_object_unref(file_priv, arg->handle, drm_fence_type); -} - -int drm_fence_signaled_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - int ret; - struct drm_fence_manager *fm = &dev->fm; - struct drm_fence_arg *arg = data; - struct drm_fence_object *fence; - ret = 0; - - if (!fm->initialized) { - DRM_ERROR("The DRM driver does not support fencing.\n"); - return -EINVAL; - } - - fence = drm_lookup_fence_object(file_priv, arg->handle); - if (!fence) - return -EINVAL; - - drm_fence_fill_arg(fence, arg); - drm_fence_usage_deref_unlocked(&fence); - - return ret; -} - -int drm_fence_flush_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - int ret; - struct drm_fence_manager *fm = &dev->fm; - struct drm_fence_arg *arg = data; - struct drm_fence_object *fence; - ret = 0; - - if (!fm->initialized) { - DRM_ERROR("The DRM driver does not support fencing.\n"); - return -EINVAL; - } - - fence = drm_lookup_fence_object(file_priv, arg->handle); - if (!fence) - return -EINVAL; - ret = drm_fence_object_flush(fence, arg->type); - - drm_fence_fill_arg(fence, arg); - drm_fence_usage_deref_unlocked(&fence); - - return ret; -} - - -int drm_fence_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - int ret; - struct drm_fence_manager *fm = &dev->fm; - struct drm_fence_arg *arg = data; - struct drm_fence_object *fence; - ret = 0; - - if (!fm->initialized) { - DRM_ERROR("The DRM driver does not support fencing.\n"); - return -EINVAL; - } - - fence = drm_lookup_fence_object(file_priv, arg->handle); - if (!fence) - return -EINVAL; - ret = drm_fence_object_wait(fence, - arg->flags & DRM_FENCE_FLAG_WAIT_LAZY, - 0, arg->type); - - drm_fence_fill_arg(fence, arg); - drm_fence_usage_deref_unlocked(&fence); - - return ret; -} - - -int drm_fence_emit_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - int ret; - struct drm_fence_manager *fm = &dev->fm; - struct drm_fence_arg *arg = data; - struct drm_fence_object *fence; - ret = 0; - - if (!fm->initialized) { - DRM_ERROR("The DRM driver does not support fencing.\n"); - return -EINVAL; - } - - LOCK_TEST_WITH_RETURN(dev, file_priv); - fence = drm_lookup_fence_object(file_priv, arg->handle); - if (!fence) - return -EINVAL; - ret = drm_fence_object_emit(fence, arg->flags, arg->fence_class, - arg->type); - - drm_fence_fill_arg(fence, arg); - drm_fence_usage_deref_unlocked(&fence); - - return ret; -} - -int drm_fence_buffers_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - int ret; - struct drm_fence_manager *fm = &dev->fm; - struct drm_fence_arg *arg = data; - struct drm_fence_object *fence; - ret = 0; - - if (!fm->initialized) { - DRM_ERROR("The DRM driver does not support fencing.\n"); - return -EINVAL; - } - - if (!dev->bm.initialized) { - DRM_ERROR("Buffer object manager is not initialized\n"); - return -EINVAL; - } - LOCK_TEST_WITH_RETURN(dev, file_priv); - ret = drm_fence_buffer_objects(dev, NULL, arg->flags, - NULL, &fence); - if (ret) - return ret; - - if (!(arg->flags & DRM_FENCE_FLAG_NO_USER)) { - ret = drm_fence_add_user_object(file_priv, fence, - arg->flags & - DRM_FENCE_FLAG_SHAREABLE); - if (ret) - return ret; - } - - arg->handle = fence->base.hash.key; - - drm_fence_fill_arg(fence, arg); - drm_fence_usage_deref_unlocked(&fence); - - return ret; -} diff --git a/linux-core/drm_fops.c b/linux-core/drm_fops.c index ec521101..7bc73d26 100644 --- a/linux-core/drm_fops.c +++ b/linux-core/drm_fops.c @@ -43,10 +43,8 @@ static int drm_open_helper(struct inode *inode, struct file *filp, static int drm_setup(struct drm_device * dev) { - drm_local_map_t *map; int i; int ret; - int sareapage; if (dev->driver->firstopen) { ret = dev->driver->firstopen(dev); @@ -54,20 +52,13 @@ static int drm_setup(struct drm_device * dev) return ret; } - dev->magicfree.next = NULL; - - /* prebuild the SAREA */ - sareapage = max(SAREA_MAX, PAGE_SIZE); - i = drm_addmap(dev, 0, sareapage, _DRM_SHM, _DRM_CONTAINS_LOCK, &map); - if (i != 0) - return i; - atomic_set(&dev->ioctl_count, 0); atomic_set(&dev->vma_count, 0); - dev->buf_use = 0; - atomic_set(&dev->buf_alloc, 0); - if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) { + if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) && !drm_core_check_feature(dev, DRIVER_MODESET)) { + dev->buf_use = 0; + atomic_set(&dev->buf_alloc, 0); + i = drm_dma_setup(dev); if (i < 0) return i; @@ -76,11 +67,8 @@ static int drm_setup(struct drm_device * dev) for (i = 0; i < ARRAY_SIZE(dev->counts); i++) atomic_set(&dev->counts[i], 0); - drm_ht_create(&dev->magiclist, DRM_MAGIC_HASH_ORDER); - INIT_LIST_HEAD(&dev->magicfree); - dev->sigdata.lock = NULL; - init_waitqueue_head(&dev->lock.lock_queue); + dev->queue_count = 0; dev->queue_reserved = 0; dev->queue_slots = 0; @@ -234,7 +222,6 @@ static int drm_open_helper(struct inode *inode, struct file *filp, int minor_id = iminor(inode); struct drm_file *priv; int ret; - int i, j; if (filp->f_flags & O_EXCL) return -EBUSY; /* No exclusive opens */ @@ -259,20 +246,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp, priv->lock_count = 0; INIT_LIST_HEAD(&priv->lhead); - INIT_LIST_HEAD(&priv->refd_objects); - - for (i = 0; i < _DRM_NO_REF_TYPES; ++i) { - ret = drm_ht_create(&priv->refd_object_hash[i], - DRM_FILE_HASH_ORDER); - if (ret) - break; - } - - if (ret) { - for (j = 0; j < i; ++j) - drm_ht_remove(&priv->refd_object_hash[j]); - goto out_free; - } + INIT_LIST_HEAD(&priv->fbs); if (dev->driver->driver_features & DRIVER_GEM) drm_gem_open(dev, priv); @@ -283,10 +257,42 @@ static int drm_open_helper(struct inode *inode, struct file *filp, goto out_free; } + + /* if there is no current master make this fd it */ mutex_lock(&dev->struct_mutex); - if (list_empty(&dev->filelist)) - priv->master = 1; + if (!priv->minor->master) { + /* create a new master */ + priv->minor->master = drm_master_create(priv->minor); + if (!priv->minor->master) { + ret = -ENOMEM; + goto out_free; + } + priv->is_master = 1; + /* take another reference for the copy in the local file priv */ + priv->master = drm_master_get(priv->minor->master); + + priv->authenticated = 1; + + mutex_unlock(&dev->struct_mutex); + if (dev->driver->master_create) { + ret = dev->driver->master_create(dev, priv->master); + if (ret) { + mutex_lock(&dev->struct_mutex); + /* drop both references if this fails */ + drm_master_put(&priv->minor->master); + drm_master_put(&priv->master); + mutex_unlock(&dev->struct_mutex); + goto out_free; + } + } + } else { + /* get a reference to the master */ + priv->master = drm_master_get(priv->minor->master); + mutex_unlock(&dev->struct_mutex); + } + + mutex_lock(&dev->struct_mutex); list_add(&priv->lhead, &dev->filelist); mutex_unlock(&dev->struct_mutex); @@ -332,33 +338,6 @@ int drm_fasync(int fd, struct file *filp, int on) } EXPORT_SYMBOL(drm_fasync); -static void drm_object_release(struct file *filp) -{ - struct drm_file *priv = filp->private_data; - struct list_head *head; - struct drm_ref_object *ref_object; - int i; - - /* - * Free leftover ref objects created by me. Note that we cannot use - * list_for_each() here, as the struct_mutex may be temporarily - * released by the remove_() functions, and thus the lists may be - * altered. - * Also, a drm_remove_ref_object() will not remove it - * from the list unless its refcount is 1. - */ - - head = &priv->refd_objects; - while (head->next != head) { - ref_object = list_entry(head->next, struct drm_ref_object, list); - drm_remove_ref_object(priv, ref_object); - head = &priv->refd_objects; - } - - for (i = 0; i < _DRM_NO_REF_TYPES; ++i) - drm_ht_remove(&priv->refd_object_hash[i]); -} - /** * Release file. * @@ -392,59 +371,63 @@ int drm_release(struct inode *inode, struct file *filp) current->pid, (long)old_encode_dev(file_priv->minor->device), dev->open_count); - if (dev->driver->reclaim_buffers_locked && dev->lock.hw_lock) { - if (drm_i_have_hw_lock(dev, file_priv)) { - dev->driver->reclaim_buffers_locked(dev, file_priv); - } else { - unsigned long _end=jiffies + 3*DRM_HZ; - int locked = 0; - - drm_idlelock_take(&dev->lock); - - /* - * Wait for a while. - */ - - do{ - spin_lock_bh(&dev->lock.spinlock); - locked = dev->lock.idle_has_lock; - spin_unlock_bh(&dev->lock.spinlock); - if (locked) - break; - schedule(); - } while (!time_after_eq(jiffies, _end)); - - if (!locked) { - DRM_ERROR("reclaim_buffers_locked() deadlock. Please rework this\n" - "\tdriver to use reclaim_buffers_idlelocked() instead.\n" - "\tI will go on reclaiming the buffers anyway.\n"); + /* if the master has gone away we can't do anything with the lock */ + if (file_priv->minor->master) { + if (dev->driver->reclaim_buffers_locked && file_priv->master->lock.hw_lock) { + if (drm_i_have_hw_lock(dev, file_priv)) { + dev->driver->reclaim_buffers_locked(dev, file_priv); + } else { + unsigned long _end=jiffies + 3*DRM_HZ; + int locked = 0; + + drm_idlelock_take(&file_priv->master->lock); + + /* + * Wait for a while. + */ + + do{ + spin_lock_bh(&file_priv->master->lock.spinlock); + locked = file_priv->master->lock.idle_has_lock; + spin_unlock_bh(&file_priv->master->lock.spinlock); + if (locked) + break; + schedule(); + } while (!time_after_eq(jiffies, _end)); + + if (!locked) { + DRM_ERROR("reclaim_buffers_locked() deadlock. Please rework this\n" + "\tdriver to use reclaim_buffers_idlelocked() instead.\n" + "\tI will go on reclaiming the buffers anyway.\n"); + } + + dev->driver->reclaim_buffers_locked(dev, file_priv); + drm_idlelock_release(&file_priv->master->lock); } - - dev->driver->reclaim_buffers_locked(dev, file_priv); - drm_idlelock_release(&dev->lock); } - } - - if (dev->driver->reclaim_buffers_idlelocked && dev->lock.hw_lock) { - - drm_idlelock_take(&dev->lock); - dev->driver->reclaim_buffers_idlelocked(dev, file_priv); - drm_idlelock_release(&dev->lock); - - } - if (drm_i_have_hw_lock(dev, file_priv)) { - DRM_DEBUG("File %p released, freeing lock for context %d\n", - filp, _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + if (dev->driver->reclaim_buffers_idlelocked && file_priv->master->lock.hw_lock) { + + drm_idlelock_take(&file_priv->master->lock); + dev->driver->reclaim_buffers_idlelocked(dev, file_priv); + drm_idlelock_release(&file_priv->master->lock); + + } - drm_lock_free(&dev->lock, - _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); - } + if (drm_i_have_hw_lock(dev, file_priv)) { + DRM_DEBUG("File %p released, freeing lock for context %d\n", + filp, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock)); + + drm_lock_free(&file_priv->master->lock, + _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock)); + } + - if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) && - !dev->driver->reclaim_buffers_locked) { - dev->driver->reclaim_buffers(dev, file_priv); + if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) && + !dev->driver->reclaim_buffers_locked) { + dev->driver->reclaim_buffers(dev, file_priv); + } } if (dev->driver->driver_features & DRIVER_GEM) @@ -474,14 +457,29 @@ int drm_release(struct inode *inode, struct file *filp) } mutex_unlock(&dev->ctxlist_mutex); + if (drm_core_check_feature(dev, DRIVER_MODESET)) + drm_fb_release(filp); + mutex_lock(&dev->struct_mutex); - drm_object_release(filp); - if (file_priv->remove_auth_on_close == 1) { + + if (file_priv->is_master) { struct drm_file *temp; + list_for_each_entry(temp, &dev->filelist, lhead) { + if ((temp->master == file_priv->master) && + (temp != file_priv)) + temp->authenticated = 0; + } - list_for_each_entry(temp, &dev->filelist, lhead) - temp->authenticated = 0; + if (file_priv->minor->master == file_priv->master) { + /* drop the reference held my the minor */ + drm_master_put(&file_priv->minor->master); + } } + + /* drop the reference held my the file priv */ + drm_master_put(&file_priv->master); + file_priv->is_master = 0; + list_del(&file_priv->lhead); mutex_unlock(&dev->struct_mutex); @@ -496,9 +494,9 @@ int drm_release(struct inode *inode, struct file *filp) atomic_inc(&dev->counts[_DRM_STAT_CLOSES]); spin_lock(&dev->count_lock); if (!--dev->open_count) { - if (atomic_read(&dev->ioctl_count) || dev->blocked) { - DRM_ERROR("Device busy: %d %d\n", - atomic_read(&dev->ioctl_count), dev->blocked); + if (atomic_read(&dev->ioctl_count)) { + DRM_ERROR("Device busy: %d\n", + atomic_read(&dev->ioctl_count)); spin_unlock(&dev->count_lock); unlock_kernel(); return -EBUSY; diff --git a/linux-core/drm_gem.c b/linux-core/drm_gem.c index ce930265..607c8b67 100644 --- a/linux-core/drm_gem.c +++ b/linux-core/drm_gem.c @@ -105,7 +105,6 @@ drm_gem_object_alloc(struct drm_device *dev, size_t size) kfree(obj); return NULL; } - kref_init(&obj->refcount); kref_init(&obj->handlecount); obj->size = size; @@ -264,8 +263,9 @@ again: spin_lock(&dev->object_name_lock); if (obj->name) { + args->name = (uint64_t) obj->name; spin_unlock(&dev->object_name_lock); - return -EEXIST; + return 0; } ret = idr_get_new_above(&dev->object_name_idr, obj, 1, &obj->name); diff --git a/linux-core/drm_ioctl.c b/linux-core/drm_ioctl.c index 3df163db..e35126a3 100644 --- a/linux-core/drm_ioctl.c +++ b/linux-core/drm_ioctl.c @@ -53,12 +53,13 @@ int drm_getunique(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_unique *u = data; + struct drm_master *master = file_priv->master; - if (u->unique_len >= dev->unique_len) { - if (copy_to_user(u->unique, dev->unique, dev->unique_len)) + if (u->unique_len >= master->unique_len) { + if (copy_to_user(u->unique, master->unique, master->unique_len)) return -EFAULT; } - u->unique_len = dev->unique_len; + u->unique_len = master->unique_len; return 0; } @@ -81,36 +82,37 @@ int drm_setunique(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_unique *u = data; + struct drm_master *master = file_priv->master; int domain, bus, slot, func, ret; - if (dev->unique_len || dev->unique) + if (master->unique_len || master->unique) return -EBUSY; if (!u->unique_len || u->unique_len > 1024) return -EINVAL; - dev->unique_len = u->unique_len; - dev->unique = drm_alloc(u->unique_len + 1, DRM_MEM_DRIVER); - if (!dev->unique) + master->unique_len = u->unique_len; + master->unique = drm_alloc(u->unique_len + 1, DRM_MEM_DRIVER); + if (!master->unique) return -ENOMEM; - if (copy_from_user(dev->unique, u->unique, dev->unique_len)) + if (copy_from_user(master->unique, u->unique, master->unique_len)) return -EFAULT; - dev->unique[dev->unique_len] = '\0'; + master->unique[master->unique_len] = '\0'; dev->devname = drm_alloc(strlen(dev->driver->pci_driver.name) + - strlen(dev->unique) + 2, DRM_MEM_DRIVER); + strlen(master->unique) + 2, DRM_MEM_DRIVER); if (!dev->devname) return -ENOMEM; sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name, - dev->unique); + master->unique); /* Return error if the busid submitted doesn't match the device's actual * busid. */ - ret = sscanf(dev->unique, "PCI:%d:%d:%d", &bus, &slot, &func); + ret = sscanf(master->unique, "PCI:%d:%d:%d", &bus, &slot, &func); if (ret != 3) return -EINVAL; domain = bus >> 8; @@ -125,33 +127,35 @@ int drm_setunique(struct drm_device *dev, void *data, return 0; } -static int drm_set_busid(struct drm_device * dev) +static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv) { + struct drm_master *master = file_priv->master; int len; - if (dev->unique != NULL) + + if (master->unique != NULL) return -EBUSY; - dev->unique_len = 40; - dev->unique = drm_alloc(dev->unique_len + 1, DRM_MEM_DRIVER); - if (dev->unique == NULL) + master->unique_len = 40; + master->unique = drm_alloc(master->unique_len + 1, DRM_MEM_DRIVER); + if (master->unique == NULL) return -ENOMEM; - len = snprintf(dev->unique, dev->unique_len, "pci:%04x:%02x:%02x.%d", + len = snprintf(master->unique, master->unique_len, "pci:%04x:%02x:%02x.%d", drm_get_pci_domain(dev), dev->pdev->bus->number, PCI_SLOT(dev->pdev->devfn), PCI_FUNC(dev->pdev->devfn)); - if (len > dev->unique_len) + if (len > master->unique_len) DRM_ERROR("buffer overflow"); dev->devname = - drm_alloc(strlen(dev->driver->pci_driver.name) + dev->unique_len + + drm_alloc(strlen(dev->driver->pci_driver.name) + master->unique_len + 2, DRM_MEM_DRIVER); if (dev->devname == NULL) return -ENOMEM; sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name, - dev->unique); + master->unique); return 0; } @@ -275,7 +279,7 @@ int drm_getstats(struct drm_device *dev, void *data, for (i = 0; i < dev->counters; i++) { if (dev->types[i] == _DRM_STAT_LOCK) stats->data[i].value = - (dev->lock.hw_lock ? dev->lock.hw_lock->lock : 0); + (file_priv->master->lock.hw_lock ? file_priv->master->lock.hw_lock->lock : 0); else stats->data[i].value = atomic_read(&dev->counts[i]); stats->data[i].type = dev->types[i]; @@ -317,7 +321,7 @@ int drm_setversion(struct drm_device *dev, void *data, struct drm_file *file_pri /* * Version 1.1 includes tying of DRM to specific device */ - drm_set_busid(dev); + drm_set_busid(dev, file_priv); } } diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index 57419ca1..103f3e2b 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -96,30 +96,37 @@ static void vblank_disable_fn(unsigned long arg) static void drm_vblank_cleanup(struct drm_device *dev) { - /* Bail if the driver didn't call drm_vblank_init() */ - if (dev->num_crtcs == 0) - return; - del_timer(&dev->vblank_disable_timer); vblank_disable_fn((unsigned long)dev); - drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) * dev->num_crtcs, + if (dev->vbl_queue) + drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) * dev->num_crtcs, DRM_MEM_DRIVER); - drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * dev->num_crtcs, + + if (dev->vbl_sigs) + drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * dev->num_crtcs, DRM_MEM_DRIVER); - drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) * + + if (dev->_vblank_count) + drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) * dev->num_crtcs, DRM_MEM_DRIVER); - drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) * + + if (dev->vblank_refcount) + drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) * dev->num_crtcs, DRM_MEM_DRIVER); - drm_free(dev->vblank_enabled, sizeof(*dev->vblank_enabled) * + + if (dev->vblank_enabled) + drm_free(dev->vblank_enabled, sizeof(*dev->vblank_enabled) * dev->num_crtcs, DRM_MEM_DRIVER); - drm_free(dev->last_vblank, sizeof(*dev->last_vblank) * dev->num_crtcs, + + if (dev->last_vblank) + drm_free(dev->last_vblank, sizeof(*dev->last_vblank) * dev->num_crtcs, DRM_MEM_DRIVER); - drm_free(dev->vblank_inmodeset, sizeof(*dev->vblank_inmodeset) * - dev->num_crtcs, DRM_MEM_DRIVER); - dev->num_crtcs = 0; + if (dev->vblank_inmodeset) + drm_free(dev->vblank_inmodeset, sizeof(*dev->vblank_inmodeset) * + dev->num_crtcs, DRM_MEM_DRIVER); } int drm_vblank_init(struct drm_device *dev, int num_crtcs) @@ -185,6 +192,30 @@ err: } EXPORT_SYMBOL(drm_vblank_init); +static void drm_hotplug_cleanup(struct drm_device *dev) +{ + if (dev->hotplug_sigs) + drm_free(dev->hotplug_sigs, sizeof(*dev->hotplug_sigs), + DRM_MEM_DRIVER); +} +EXPORT_SYMBOL(drm_hotplug_cleanup); + +int drm_hotplug_init(struct drm_device *dev) +{ + spin_lock_init(&dev->hotplug_lock); + atomic_set(&dev->hotplug_signal_pending, 0); + + dev->hotplug_sigs = drm_alloc(sizeof(struct list_head), DRM_MEM_DRIVER); + if (!dev->hotplug_sigs) + return -ENOMEM; + + INIT_LIST_HEAD(dev->hotplug_sigs); + init_waitqueue_head(&dev->hotplug_queue); + + return 0; +} +EXPORT_SYMBOL(drm_hotplug_init); + /** * Install IRQ handler. * @@ -215,7 +246,7 @@ int drm_irq_install(struct drm_device * dev) if (dev->irq_enabled) { mutex_unlock(&dev->struct_mutex); - return -EBUSY; + return 0; } dev->irq_enabled = 1; mutex_unlock(&dev->struct_mutex); @@ -284,6 +315,8 @@ int drm_irq_uninstall(struct drm_device * dev) drm_vblank_cleanup(dev); + drm_hotplug_cleanup(dev); + dev->locked_tasklet_func = NULL; return 0; @@ -313,6 +346,8 @@ int drm_control(struct drm_device *dev, void *data, case DRM_INST_HANDLER: if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) return 0; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return 0; if (dev->if_version < DRM_IF_VERSION(1, 2) && ctl->irq != dev->pdev->irq) return -EINVAL; @@ -320,6 +355,8 @@ int drm_control(struct drm_device *dev, void *data, case DRM_UNINST_HANDLER: if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) return 0; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return 0; return drm_irq_uninstall(dev); default: return -EINVAL; @@ -560,7 +597,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data, if (flags & _DRM_VBLANK_SIGNAL) { unsigned long irqflags; struct list_head *vbl_sigs = &dev->vbl_sigs[crtc]; - struct drm_vbl_sig *vbl_sig; + struct drm_vbl_sig *vbl_sig, *tmp; spin_lock_irqsave(&dev->vbl_lock, irqflags); @@ -568,7 +605,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data, * for the same vblank sequence number; nothing to be done in * that case */ - list_for_each_entry(vbl_sig, vbl_sigs, head) { + list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) { if (vbl_sig->sequence == vblwait->request.sequence && vbl_sig->info.si_signo == vblwait->request.signal @@ -693,6 +730,53 @@ void drm_handle_vblank(struct drm_device *dev, int crtc) EXPORT_SYMBOL(drm_handle_vblank); /** + * Send the HOTPLUG signals. + * + * \param dev DRM device. + * + * Sends a signal for each task in drm_device::hotplug_sigs and empties the list. + */ +static void drm_hotplug_send_signals(struct drm_device * dev) +{ + struct drm_hotplug_sig *hotplug_sig, *tmp; + struct list_head *hotplug_sigs; + unsigned long flags; + + spin_lock_irqsave(&dev->hotplug_lock, flags); + + hotplug_sigs = dev->hotplug_sigs; + + list_for_each_entry_safe(hotplug_sig, tmp, hotplug_sigs, head) { + hotplug_sig->info.si_code = hotplug_sig->counter; + + send_sig_info(hotplug_sig->info.si_signo, + &hotplug_sig->info, hotplug_sig->task); + + list_del(&hotplug_sig->head); + + drm_free(hotplug_sig, sizeof(*hotplug_sig), + DRM_MEM_DRIVER); + atomic_dec(&dev->hotplug_signal_pending); + } + + spin_unlock_irqrestore(&dev->hotplug_lock, flags); +} + +/** + * drm_handle_hotplug - handle a hotplug event + * @dev: DRM device + * @crtc: where this event occurred + * + * Drivers should call this routine in their hotplug interrupt handlers. + */ +void drm_handle_hotplug(struct drm_device *dev) +{ + DRM_WAKEUP(&dev->hotplug_queue); + drm_hotplug_send_signals(dev); +} +EXPORT_SYMBOL(drm_handle_hotplug); + +/** * Tasklet wrapper function. * * \param data DRM device in disguise. @@ -712,12 +796,12 @@ static void drm_locked_tasklet_func(unsigned long data) spin_unlock_irqrestore(&dev->tasklet_lock, irqflags); if (!tasklet_func || - !drm_lock_take(&dev->lock, + !drm_lock_take(&dev->primary->master->lock, DRM_KERNEL_CONTEXT)) { return; } - dev->lock.lock_time = jiffies; + dev->primary->master->lock.lock_time = jiffies; atomic_inc(&dev->counts[_DRM_STAT_LOCKS]); spin_lock_irqsave(&dev->tasklet_lock, irqflags); @@ -728,7 +812,7 @@ static void drm_locked_tasklet_func(unsigned long data) if (tasklet_func != NULL) tasklet_func(dev); - drm_lock_free(&dev->lock, + drm_lock_free(&dev->primary->master->lock, DRM_KERNEL_CONTEXT); } diff --git a/linux-core/drm_lock.c b/linux-core/drm_lock.c index cad2e44d..8dd2e5ee 100644 --- a/linux-core/drm_lock.c +++ b/linux-core/drm_lock.c @@ -52,6 +52,7 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv) { DECLARE_WAITQUEUE(entry, current); struct drm_lock *lock = data; + struct drm_master *master = file_priv->master; int ret = 0; ++file_priv->lock_count; @@ -64,26 +65,27 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv) DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n", lock->context, current->pid, - dev->lock.hw_lock->lock, lock->flags); + master->lock.hw_lock->lock, lock->flags); if (drm_core_check_feature(dev, DRIVER_DMA_QUEUE)) if (lock->context < 0) return -EINVAL; - add_wait_queue(&dev->lock.lock_queue, &entry); - spin_lock_bh(&dev->lock.spinlock); - dev->lock.user_waiters++; - spin_unlock_bh(&dev->lock.spinlock); + add_wait_queue(&master->lock.lock_queue, &entry); + spin_lock_bh(&master->lock.spinlock); + master->lock.user_waiters++; + spin_unlock_bh(&master->lock.spinlock); + for (;;) { __set_current_state(TASK_INTERRUPTIBLE); - if (!dev->lock.hw_lock) { + if (!master->lock.hw_lock) { /* Device has been unregistered */ ret = -EINTR; break; } - if (drm_lock_take(&dev->lock, lock->context)) { - dev->lock.file_priv = file_priv; - dev->lock.lock_time = jiffies; + if (drm_lock_take(&master->lock, lock->context)) { + master->lock.file_priv = file_priv; + master->lock.lock_time = jiffies; atomic_inc(&dev->counts[_DRM_STAT_LOCKS]); break; /* Got lock */ } @@ -95,11 +97,11 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv) break; } } - spin_lock_bh(&dev->lock.spinlock); - dev->lock.user_waiters--; - spin_unlock_bh(&dev->lock.spinlock); + spin_lock_bh(&master->lock.spinlock); + master->lock.user_waiters--; + spin_unlock_bh(&master->lock.spinlock); __set_current_state(TASK_RUNNING); - remove_wait_queue(&dev->lock.lock_queue, &entry); + remove_wait_queue(&master->lock.lock_queue, &entry); DRM_DEBUG("%d %s\n", lock->context, ret ? "interrupted" : "has lock"); @@ -108,14 +110,14 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv) /* don't set the block all signals on the master process for now * really probably not the correct answer but lets us debug xkb * xserver for now */ - if (!file_priv->master) { + if (!file_priv->is_master) { sigemptyset(&dev->sigmask); sigaddset(&dev->sigmask, SIGSTOP); sigaddset(&dev->sigmask, SIGTSTP); sigaddset(&dev->sigmask, SIGTTIN); sigaddset(&dev->sigmask, SIGTTOU); dev->sigdata.context = lock->context; - dev->sigdata.lock = dev->lock.hw_lock; + dev->sigdata.lock = master->lock.hw_lock; block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask); } @@ -154,6 +156,7 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv) int drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_lock *lock = data; + struct drm_master *master = file_priv->master; unsigned long irqflags; void (*tasklet_func)(struct drm_device *); @@ -178,7 +181,7 @@ int drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv) if (dev->driver->kernel_context_switch_unlock) dev->driver->kernel_context_switch_unlock(dev); else { - if (drm_lock_free(&dev->lock,lock->context)) { + if (drm_lock_free(&master->lock,lock->context)) { /* FIXME: Should really bail out here. */ } } @@ -216,16 +219,22 @@ int drm_lock_take(struct drm_lock_data *lock_data, } while (prev != old); spin_unlock_bh(&lock_data->spinlock); - /* Warn on recursive locking of user contexts. */ - if (_DRM_LOCKING_CONTEXT(old) == context && _DRM_LOCK_IS_HELD(old)) { - if (context != DRM_KERNEL_CONTEXT) { - DRM_ERROR("%d holds heavyweight lock\n", - context); + if (_DRM_LOCKING_CONTEXT(old) == context) { + if (old & _DRM_LOCK_HELD) { + if (context != DRM_KERNEL_CONTEXT) { + DRM_ERROR("%d holds heavyweight lock\n", + context); + } + return 0; } - return 0; } - return !_DRM_LOCK_IS_HELD(old); + if ((_DRM_LOCKING_CONTEXT(new)) == context && (new & _DRM_LOCK_HELD)) { + /* Have lock */ + + return 1; + } + return 0; } /** @@ -380,10 +389,10 @@ EXPORT_SYMBOL(drm_idlelock_release); int drm_i_have_hw_lock(struct drm_device *dev, struct drm_file *file_priv) { - - return (file_priv->lock_count && dev->lock.hw_lock && - _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) && - dev->lock.file_priv == file_priv); + struct drm_master *master = file_priv->master; + return (file_priv->lock_count && master->lock.hw_lock && + _DRM_LOCK_IS_HELD(master->lock.hw_lock->lock) && + master->lock.file_priv == file_priv); } EXPORT_SYMBOL(drm_i_have_hw_lock); diff --git a/linux-core/drm_memory.c b/linux-core/drm_memory.c index d1a88c88..38b93bca 100644 --- a/linux-core/drm_memory.c +++ b/linux-core/drm_memory.c @@ -188,6 +188,7 @@ void *drm_realloc(void *oldpt, size_t oldsize, size_t size, int area) } return pt; } +EXPORT_SYMBOL(drm_realloc); /** * Allocate pages. diff --git a/linux-core/drm_mm.c b/linux-core/drm_mm.c index 1de7059a..8d7390d4 100644 --- a/linux-core/drm_mm.c +++ b/linux-core/drm_mm.c @@ -294,5 +294,4 @@ void drm_mm_takedown(struct drm_mm * mm) list_del(&entry->ml_entry); drm_ctl_free(entry, sizeof(*entry), DRM_MEM_MM); } - EXPORT_SYMBOL(drm_mm_takedown); diff --git a/linux-core/drm_mode.h b/linux-core/drm_mode.h new file mode 120000 index 00000000..a43f1385 --- /dev/null +++ b/linux-core/drm_mode.h @@ -0,0 +1 @@ +../shared-core/drm_mode.h
\ No newline at end of file diff --git a/linux-core/drm_modes.c b/linux-core/drm_modes.c new file mode 100644 index 00000000..67f278eb --- /dev/null +++ b/linux-core/drm_modes.c @@ -0,0 +1,570 @@ +/* + * Copyright © 1997-2003 by The XFree86 Project, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of the copyright holder(s) + * and author(s) shall not be used in advertising or otherwise to promote + * the sale, use or other dealings in this Software without prior written + * authorization from the copyright holder(s) and author(s). + */ +/* + * Copyright © 2007 Dave Airlie + */ + +#include <linux/list.h> +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" + +/** + * drm_mode_debug_printmodeline - debug print a mode + * @dev: DRM device + * @mode: mode to print + * + * LOCKING: + * None. + * + * Describe @mode using DRM_DEBUG. + */ +void drm_mode_debug_printmodeline(struct drm_display_mode *mode) +{ + DRM_DEBUG("Modeline %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x\n", + mode->base.id, mode->name, mode->vrefresh, mode->clock, + mode->hdisplay, mode->hsync_start, + mode->hsync_end, mode->htotal, + mode->vdisplay, mode->vsync_start, + mode->vsync_end, mode->vtotal, mode->type, mode->flags); +} +EXPORT_SYMBOL(drm_mode_debug_printmodeline); + +/** + * drm_mode_set_name - set the name on a mode + * @mode: name will be set in this mode + * + * LOCKING: + * None. + * + * Set the name of @mode to a standard format. + */ +void drm_mode_set_name(struct drm_display_mode *mode) +{ + snprintf(mode->name, DRM_DISPLAY_MODE_LEN, "%dx%d", mode->hdisplay, + mode->vdisplay); +} +EXPORT_SYMBOL(drm_mode_set_name); + +/** + * drm_mode_list_concat - move modes from one list to another + * @head: source list + * @new: dst list + * + * LOCKING: + * Caller must ensure both lists are locked. + * + * Move all the modes from @head to @new. + */ +void drm_mode_list_concat(struct list_head *head, struct list_head *new) +{ + + struct list_head *entry, *tmp; + + list_for_each_safe(entry, tmp, head) { + list_move_tail(entry, new); + } +} +EXPORT_SYMBOL(drm_mode_list_concat); + +/** + * 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 & DRM_MODE_FLAG_INTERLACE) + refresh *= 2; + if (mode->flags & DRM_MODE_FLAG_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 & DRM_MODE_FLAG_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 & DRM_MODE_FLAG_DBLSCAN) { + p->crtc_vdisplay *= 2; + p->crtc_vsync_start *= 2; + p->crtc_vsync_end *= 2; + p->crtc_vtotal *= 2; + } + + if (p->vscan > 1) { + p->crtc_vdisplay *= p->vscan; + p->crtc_vsync_start *= p->vscan; + p->crtc_vsync_end *= p->vscan; + p->crtc_vtotal *= p->vscan; + } + + p->crtc_vblank_start = min(p->crtc_vsync_start, p->crtc_vdisplay); + p->crtc_vblank_end = max(p->crtc_vsync_end, p->crtc_vtotal); + p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay); + p->crtc_hblank_end = max(p->crtc_hsync_end, p->crtc_htotal); + + p->crtc_hadjusted = false; + p->crtc_vadjusted = false; +} +EXPORT_SYMBOL(drm_mode_set_crtcinfo); + + +/** + * drm_mode_duplicate - allocate and duplicate an existing mode + * @m: mode to duplicate + * + * LOCKING: + * None. + * + * Just allocate a new mode, copy the existing mode into it, and return + * a pointer to it. Used to create new instances of established modes. + */ +struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, + struct drm_display_mode *mode) +{ + struct drm_display_mode *nmode; + int new_id; + + nmode = drm_mode_create(dev); + if (!nmode) + return NULL; + + new_id = nmode->base.id; + *nmode = *mode; + nmode->base.id = new_id; + INIT_LIST_HEAD(&nmode->head); + return nmode; +} +EXPORT_SYMBOL(drm_mode_duplicate); + +/** + * drm_mode_equal - test modes for equality + * @mode1: first mode + * @mode2: second mode + * + * LOCKING: + * None. + * + * Check to see if @mode1 and @mode2 are equivalent. + * + * RETURNS: + * True if the modes are equal, false otherwise. + */ +bool drm_mode_equal(struct drm_display_mode *mode1, struct drm_display_mode *mode2) +{ + /* do clock check convert to PICOS so fb modes get matched + * the same */ + if (mode1->clock && mode2->clock) { + if (KHZ2PICOS(mode1->clock) != KHZ2PICOS(mode2->clock)) + return false; + } else if (mode1->clock != mode2->clock) + return false; + + if (mode1->hdisplay == mode2->hdisplay && + mode1->hsync_start == mode2->hsync_start && + mode1->hsync_end == mode2->hsync_end && + mode1->htotal == mode2->htotal && + mode1->hskew == mode2->hskew && + mode1->vdisplay == mode2->vdisplay && + mode1->vsync_start == mode2->vsync_start && + mode1->vsync_end == mode2->vsync_end && + mode1->vtotal == mode2->vtotal && + mode1->vscan == mode2->vscan && + mode1->flags == mode2->flags) + return true; + + return false; +} +EXPORT_SYMBOL(drm_mode_equal); + +/** + * drm_mode_validate_size - make sure modes adhere to size constraints + * @dev: DRM device + * @mode_list: list of modes to check + * @maxX: maximum width + * @maxY: maximum height + * @maxPitch: max pitch + * + * LOCKING: + * Caller must hold a lock protecting @mode_list. + * + * The DRM device (@dev) has size and pitch limits. Here we validate the + * modes we probed for @dev against those limits and set their status as + * necessary. + */ +void drm_mode_validate_size(struct drm_device *dev, + struct list_head *mode_list, + int maxX, int maxY, int maxPitch) +{ + struct drm_display_mode *mode; + + list_for_each_entry(mode, mode_list, head) { + if (maxPitch > 0 && mode->hdisplay > maxPitch) + mode->status = MODE_BAD_WIDTH; + + if (maxX > 0 && mode->hdisplay > maxX) + mode->status = MODE_VIRTUAL_X; + + if (maxY > 0 && mode->vdisplay > maxY) + mode->status = MODE_VIRTUAL_Y; + } +} +EXPORT_SYMBOL(drm_mode_validate_size); + +/** + * drm_mode_validate_clocks - validate modes against clock limits + * @dev: DRM device + * @mode_list: list of modes to check + * @min: minimum clock rate array + * @max: maximum clock rate array + * @n_ranges: number of clock ranges (size of arrays) + * + * LOCKING: + * Caller must hold a lock protecting @mode_list. + * + * Some code may need to check a mode list against the clock limits of the + * device in question. This function walks the mode list, testing to make + * sure each mode falls within a given range (defined by @min and @max + * arrays) and sets @mode->status as needed. + */ +void drm_mode_validate_clocks(struct drm_device *dev, + struct list_head *mode_list, + int *min, int *max, int n_ranges) +{ + struct drm_display_mode *mode; + int i; + + list_for_each_entry(mode, mode_list, head) { + bool good = false; + for (i = 0; i < n_ranges; i++) { + if (mode->clock >= min[i] && mode->clock <= max[i]) { + good = true; + break; + } + } + if (!good) + mode->status = MODE_CLOCK_RANGE; + } +} +EXPORT_SYMBOL(drm_mode_validate_clocks); + +/** + * drm_mode_prune_invalid - remove invalid modes from mode list + * @dev: DRM device + * @mode_list: list of modes to check + * @verbose: be verbose about it + * + * LOCKING: + * Caller must hold a lock protecting @mode_list. + * + * Once mode list generation is complete, a caller can use this routine to + * remove invalid modes from a mode list. If any of the modes have a + * status other than %MODE_OK, they are removed from @mode_list and freed. + */ +void drm_mode_prune_invalid(struct drm_device *dev, + struct list_head *mode_list, bool verbose) +{ + struct drm_display_mode *mode, *t; + + list_for_each_entry_safe(mode, t, mode_list, head) { + if (mode->status != MODE_OK) { + list_del(&mode->head); + if (verbose) { + drm_mode_debug_printmodeline(mode); + DRM_DEBUG("Not using %s mode %d\n", mode->name, mode->status); + } + drm_mode_destroy(dev, mode); + } + } +} +EXPORT_SYMBOL(drm_mode_prune_invalid); + +/** + * 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); +} +EXPORT_SYMBOL(drm_mode_sort); + +/** + * drm_mode_connector_list_update - update the mode list for the connector + * @connector: the connector to update + * + * LOCKING: + * Caller must hold a lock protecting @mode_list. + * + * This moves the modes from the @connector probed_modes list + * to the actual mode list. It compares the probed mode against the current + * list and only adds different modes. All modes unverified after this point + * will be removed by the prune invalid modes. + */ +void drm_mode_connector_list_update(struct drm_connector *connector) +{ + struct drm_display_mode *mode; + struct drm_display_mode *pmode, *pt; + int found_it; + + list_for_each_entry_safe(pmode, pt, &connector->probed_modes, + head) { + found_it = 0; + /* go through current modes checking for the new probed mode */ + list_for_each_entry(mode, &connector->modes, head) { + if (drm_mode_equal(pmode, mode)) { + found_it = 1; + /* if equal delete the probed mode */ + mode->status = pmode->status; + list_del(&pmode->head); + drm_mode_destroy(connector->dev, pmode); + break; + } + } + + if (!found_it) { + list_move_tail(&pmode->head, &connector->modes); + } + } +} +EXPORT_SYMBOL(drm_mode_connector_list_update); diff --git a/linux-core/drm_object.c b/linux-core/drm_object.c deleted file mode 100644 index 2994b716..00000000 --- a/linux-core/drm_object.c +++ /dev/null @@ -1,294 +0,0 @@ -/************************************************************************** - * - * Copyright (c) 2006-2007 Tungsten Graphics, Inc., Cedar Park, TX., 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 above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE 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. - * - **************************************************************************/ -/* - * Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com> - */ - -#include "drmP.h" - -int drm_add_user_object(struct drm_file *priv, struct drm_user_object *item, - int shareable) -{ - struct drm_device *dev = priv->minor->dev; - int ret; - - DRM_ASSERT_LOCKED(&dev->struct_mutex); - - /* The refcount will be bumped to 1 when we add the ref object below. */ - atomic_set(&item->refcount, 0); - item->shareable = shareable; - item->owner = priv; - - ret = drm_ht_just_insert_please(&dev->object_hash, &item->hash, - (unsigned long)item, 31, 0, 0); - if (ret) - return ret; - - ret = drm_add_ref_object(priv, item, _DRM_REF_USE); - if (ret) - ret = drm_ht_remove_item(&dev->object_hash, &item->hash); - - return ret; -} -EXPORT_SYMBOL(drm_add_user_object); - -struct drm_user_object *drm_lookup_user_object(struct drm_file *priv, uint32_t key) -{ - struct drm_device *dev = priv->minor->dev; - struct drm_hash_item *hash; - int ret; - struct drm_user_object *item; - - DRM_ASSERT_LOCKED(&dev->struct_mutex); - - ret = drm_ht_find_item(&dev->object_hash, key, &hash); - if (ret) - return NULL; - - item = drm_hash_entry(hash, struct drm_user_object, hash); - - if (priv != item->owner) { - struct drm_open_hash *ht = &priv->refd_object_hash[_DRM_REF_USE]; - ret = drm_ht_find_item(ht, (unsigned long)item, &hash); - if (ret) { - DRM_ERROR("Object not registered for usage\n"); - return NULL; - } - } - return item; -} -EXPORT_SYMBOL(drm_lookup_user_object); - -static void drm_deref_user_object(struct drm_file *priv, struct drm_user_object *item) -{ - struct drm_device *dev = priv->minor->dev; - int ret; - - if (atomic_dec_and_test(&item->refcount)) { - ret = drm_ht_remove_item(&dev->object_hash, &item->hash); - BUG_ON(ret); - item->remove(priv, item); - } -} - -static int drm_object_ref_action(struct drm_file *priv, struct drm_user_object *ro, - enum drm_ref_type action) -{ - int ret = 0; - - switch (action) { - case _DRM_REF_USE: - atomic_inc(&ro->refcount); - break; - default: - if (!ro->ref_struct_locked) { - break; - } else { - ro->ref_struct_locked(priv, ro, action); - } - } - return ret; -} - -int drm_add_ref_object(struct drm_file *priv, struct drm_user_object *referenced_object, - enum drm_ref_type ref_action) -{ - int ret = 0; - struct drm_ref_object *item; - struct drm_open_hash *ht = &priv->refd_object_hash[ref_action]; - - DRM_ASSERT_LOCKED(&priv->minor->dev->struct_mutex); - if (!referenced_object->shareable && priv != referenced_object->owner) { - DRM_ERROR("Not allowed to reference this object\n"); - return -EINVAL; - } - - /* - * If this is not a usage reference, Check that usage has been registered - * first. Otherwise strange things may happen on destruction. - */ - - if ((ref_action != _DRM_REF_USE) && priv != referenced_object->owner) { - item = - drm_lookup_ref_object(priv, referenced_object, - _DRM_REF_USE); - if (!item) { - DRM_ERROR - ("Object not registered for usage by this client\n"); - return -EINVAL; - } - } - - if (NULL != - (item = - drm_lookup_ref_object(priv, referenced_object, ref_action))) { - atomic_inc(&item->refcount); - return drm_object_ref_action(priv, referenced_object, - ref_action); - } - - item = drm_ctl_calloc(1, sizeof(*item), DRM_MEM_OBJECTS); - if (item == NULL) { - DRM_ERROR("Could not allocate reference object\n"); - return -ENOMEM; - } - - atomic_set(&item->refcount, 1); - item->hash.key = (unsigned long)referenced_object; - ret = drm_ht_insert_item(ht, &item->hash); - item->unref_action = ref_action; - - if (ret) - goto out; - - list_add(&item->list, &priv->refd_objects); - ret = drm_object_ref_action(priv, referenced_object, ref_action); -out: - return ret; -} - -struct drm_ref_object *drm_lookup_ref_object(struct drm_file *priv, - struct drm_user_object *referenced_object, - enum drm_ref_type ref_action) -{ - struct drm_hash_item *hash; - int ret; - - DRM_ASSERT_LOCKED(&priv->minor->dev->struct_mutex); - ret = drm_ht_find_item(&priv->refd_object_hash[ref_action], - (unsigned long)referenced_object, &hash); - if (ret) - return NULL; - - return drm_hash_entry(hash, struct drm_ref_object, hash); -} -EXPORT_SYMBOL(drm_lookup_ref_object); - -static void drm_remove_other_references(struct drm_file *priv, - struct drm_user_object *ro) -{ - int i; - struct drm_open_hash *ht; - struct drm_hash_item *hash; - struct drm_ref_object *item; - - for (i = _DRM_REF_USE + 1; i < _DRM_NO_REF_TYPES; ++i) { - ht = &priv->refd_object_hash[i]; - while (!drm_ht_find_item(ht, (unsigned long)ro, &hash)) { - item = drm_hash_entry(hash, struct drm_ref_object, hash); - drm_remove_ref_object(priv, item); - } - } -} - -void drm_remove_ref_object(struct drm_file *priv, struct drm_ref_object *item) -{ - int ret; - struct drm_user_object *user_object = (struct drm_user_object *) item->hash.key; - struct drm_open_hash *ht = &priv->refd_object_hash[item->unref_action]; - enum drm_ref_type unref_action; - - DRM_ASSERT_LOCKED(&priv->minor->dev->struct_mutex); - unref_action = item->unref_action; - if (atomic_dec_and_test(&item->refcount)) { - ret = drm_ht_remove_item(ht, &item->hash); - BUG_ON(ret); - list_del_init(&item->list); - if (unref_action == _DRM_REF_USE) - drm_remove_other_references(priv, user_object); - drm_ctl_free(item, sizeof(*item), DRM_MEM_OBJECTS); - } - - switch (unref_action) { - case _DRM_REF_USE: - drm_deref_user_object(priv, user_object); - break; - default: - BUG_ON(!user_object->unref); - user_object->unref(priv, user_object, unref_action); - break; - } - -} -EXPORT_SYMBOL(drm_remove_ref_object); - -int drm_user_object_ref(struct drm_file *priv, uint32_t user_token, - enum drm_object_type type, struct drm_user_object **object) -{ - struct drm_device *dev = priv->minor->dev; - struct drm_user_object *uo; - struct drm_hash_item *hash; - int ret; - - mutex_lock(&dev->struct_mutex); - ret = drm_ht_find_item(&dev->object_hash, user_token, &hash); - if (ret) { - DRM_ERROR("Could not find user object to reference.\n"); - goto out_err; - } - uo = drm_hash_entry(hash, struct drm_user_object, hash); - if (uo->type != type) { - ret = -EINVAL; - goto out_err; - } - ret = drm_add_ref_object(priv, uo, _DRM_REF_USE); - if (ret) - goto out_err; - mutex_unlock(&dev->struct_mutex); - *object = uo; - return 0; -out_err: - mutex_unlock(&dev->struct_mutex); - return ret; -} - -int drm_user_object_unref(struct drm_file *priv, uint32_t user_token, - enum drm_object_type type) -{ - struct drm_device *dev = priv->minor->dev; - struct drm_user_object *uo; - struct drm_ref_object *ro; - int ret; - - mutex_lock(&dev->struct_mutex); - uo = drm_lookup_user_object(priv, user_token); - if (!uo || (uo->type != type)) { - ret = -EINVAL; - goto out_err; - } - ro = drm_lookup_ref_object(priv, uo, _DRM_REF_USE); - if (!ro) { - ret = -EINVAL; - goto out_err; - } - drm_remove_ref_object(priv, ro); - mutex_unlock(&dev->struct_mutex); - return 0; -out_err: - mutex_unlock(&dev->struct_mutex); - return ret; -} diff --git a/linux-core/drm_objects.h b/linux-core/drm_objects.h index 6a900612..012123bf 100644 --- a/linux-core/drm_objects.h +++ b/linux-core/drm_objects.h @@ -34,110 +34,212 @@ struct drm_device; struct drm_bo_mem_reg; -/*************************************************** - * User space objects. (drm_object.c) - */ - -#define drm_user_object_entry(_ptr, _type, _member) container_of(_ptr, _type, _member) - -enum drm_object_type { - drm_fence_type, - drm_buffer_type, - drm_lock_type, - /* - * Add other user space object types here. - */ - drm_driver_type0 = 256, - drm_driver_type1, - drm_driver_type2, - drm_driver_type3, - drm_driver_type4 +#define DRM_FENCE_FLAG_EMIT 0x00000001 +#define DRM_FENCE_FLAG_SHAREABLE 0x00000002 +/** + * On hardware with no interrupt events for operation completion, + * indicates that the kernel should sleep while waiting for any blocking + * operation to complete rather than spinning. + * + * Has no effect otherwise. + */ +#define DRM_FENCE_FLAG_WAIT_LAZY 0x00000004 +#define DRM_FENCE_FLAG_NO_USER 0x00000010 + +/* Reserved for driver use */ +#define DRM_FENCE_MASK_DRIVER 0xFF000000 + +#define DRM_FENCE_TYPE_EXE 0x00000001 + +struct drm_fence_arg { + unsigned int handle; + unsigned int fence_class; + unsigned int type; + unsigned int flags; + unsigned int signaled; + unsigned int error; + unsigned int sequence; + unsigned int pad64; + uint64_t expand_pad[2]; /*Future expansion */ }; +/* Buffer permissions, referring to how the GPU uses the buffers. + * these translate to fence types used for the buffers. + * Typically a texture buffer is read, A destination buffer is write and + * a command (batch-) buffer is exe. Can be or-ed together. + */ + +#define DRM_BO_FLAG_READ (1ULL << 0) +#define DRM_BO_FLAG_WRITE (1ULL << 1) +#define DRM_BO_FLAG_EXE (1ULL << 2) + /* - * A user object is a structure that helps the drm give out user handles - * to kernel internal objects and to keep track of these objects so that - * they can be destroyed, for example when the user space process exits. - * Designed to be accessible using a user space 32-bit handle. - */ - -struct drm_user_object { - struct drm_hash_item hash; - struct list_head list; - enum drm_object_type type; - atomic_t refcount; - int shareable; - struct drm_file *owner; - void (*ref_struct_locked) (struct drm_file *priv, - struct drm_user_object *obj, - enum drm_ref_type ref_action); - void (*unref) (struct drm_file *priv, struct drm_user_object *obj, - enum drm_ref_type unref_action); - void (*remove) (struct drm_file *priv, struct drm_user_object *obj); -}; + * All of the bits related to access mode + */ +#define DRM_BO_MASK_ACCESS (DRM_BO_FLAG_READ | DRM_BO_FLAG_WRITE | DRM_BO_FLAG_EXE) +/* + * Status flags. Can be read to determine the actual state of a buffer. + * Can also be set in the buffer mask before validation. + */ /* - * A ref object is a structure which is used to - * keep track of references to user objects and to keep track of these - * references so that they can be destroyed for example when the user space - * process exits. Designed to be accessible using a pointer to the _user_ object. + * Mask: Never evict this buffer. Not even with force. This type of buffer is only + * available to root and must be manually removed before buffer manager shutdown + * or lock. + * Flags: Acknowledge */ +#define DRM_BO_FLAG_NO_EVICT (1ULL << 4) -struct drm_ref_object { - struct drm_hash_item hash; - struct list_head list; - atomic_t refcount; - enum drm_ref_type unref_action; -}; +/* + * Mask: Require that the buffer is placed in mappable memory when validated. + * If not set the buffer may or may not be in mappable memory when validated. + * Flags: If set, the buffer is in mappable memory. + */ +#define DRM_BO_FLAG_MAPPABLE (1ULL << 5) -/** - * Must be called with the struct_mutex held. +/* Mask: The buffer should be shareable with other processes. + * Flags: The buffer is shareable with other processes. */ +#define DRM_BO_FLAG_SHAREABLE (1ULL << 6) -extern int drm_add_user_object(struct drm_file *priv, struct drm_user_object *item, - int shareable); -/** - * Must be called with the struct_mutex held. +/* Mask: If set, place the buffer in cache-coherent memory if available. + * If clear, never place the buffer in cache coherent memory if validated. + * Flags: The buffer is currently in cache-coherent memory. + */ +#define DRM_BO_FLAG_CACHED (1ULL << 7) + +/* Mask: Make sure that every time this buffer is validated, + * it ends up on the same location provided that the memory mask is the same. + * The buffer will also not be evicted when claiming space for + * other buffers. Basically a pinned buffer but it may be thrown out as + * part of buffer manager shutdown or locking. + * Flags: Acknowledge. + */ +#define DRM_BO_FLAG_NO_MOVE (1ULL << 8) + +/* + * Mask: if set the note the buffer contents are discardable + * Flags: if set the buffer contents are discardable on migration + */ +#define DRM_BO_FLAG_DISCARDABLE (1ULL << 9) + +/* Mask: Make sure the buffer is in cached memory when mapped. In conjunction + * with DRM_BO_FLAG_CACHED it also allows the buffer to be bound into the GART + * with unsnooped PTEs instead of snooped, by using chipset-specific cache + * flushing at bind time. A better name might be DRM_BO_FLAG_TT_UNSNOOPED, + * as the eviction to local memory (TTM unbind) on map is just a side effect + * to prevent aggressive cache prefetch from the GPU disturbing the cache + * management that the DRM is doing. + * + * Flags: Acknowledge. + * Buffers allocated with this flag should not be used for suballocators + * This type may have issues on CPUs with over-aggressive caching + * http://marc.info/?l=linux-kernel&m=102376926732464&w=2 */ +#define DRM_BO_FLAG_CACHED_MAPPED (1ULL << 19) -extern struct drm_user_object *drm_lookup_user_object(struct drm_file *priv, - uint32_t key); + +/* Mask: Force DRM_BO_FLAG_CACHED flag strictly also if it is set. + * Flags: Acknowledge. + */ +#define DRM_BO_FLAG_FORCE_CACHING (1ULL << 13) /* - * Must be called with the struct_mutex held. May temporarily release it. + * Mask: Force DRM_BO_FLAG_MAPPABLE flag strictly also if it is clear. + * Flags: Acknowledge. */ +#define DRM_BO_FLAG_FORCE_MAPPABLE (1ULL << 14) +#define DRM_BO_FLAG_TILE (1ULL << 15) -extern int drm_add_ref_object(struct drm_file *priv, - struct drm_user_object *referenced_object, - enum drm_ref_type ref_action); +/* + * Buffer has been mapped or touched since creation + * for VRAM we don't need to migrate, just fill with 0s for non-dirty + */ +#define DRM_BO_FLAG_CLEAN (1ULL << 16) + +/* + * Memory type flags that can be or'ed together in the mask, but only + * one appears in flags. + */ + +/* System memory */ +#define DRM_BO_FLAG_MEM_LOCAL (1ULL << 24) +/* Translation table memory */ +#define DRM_BO_FLAG_MEM_TT (1ULL << 25) +/* Vram memory */ +#define DRM_BO_FLAG_MEM_VRAM (1ULL << 26) +/* Up to the driver to define. */ +#define DRM_BO_FLAG_MEM_PRIV0 (1ULL << 27) +#define DRM_BO_FLAG_MEM_PRIV1 (1ULL << 28) +#define DRM_BO_FLAG_MEM_PRIV2 (1ULL << 29) +#define DRM_BO_FLAG_MEM_PRIV3 (1ULL << 30) +#define DRM_BO_FLAG_MEM_PRIV4 (1ULL << 31) +/* We can add more of these now with a 64-bit flag type */ /* - * Must be called with the struct_mutex held. + * This is a mask covering all of the memory type flags; easier to just + * use a single constant than a bunch of | values. It covers + * DRM_BO_FLAG_MEM_LOCAL through DRM_BO_FLAG_MEM_PRIV4 + */ +#define DRM_BO_MASK_MEM 0x00000000FF000000ULL +/* + * This adds all of the CPU-mapping options in with the memory + * type to label all bits which change how the page gets mapped */ +#define DRM_BO_MASK_MEMTYPE (DRM_BO_MASK_MEM | \ + DRM_BO_FLAG_CACHED_MAPPED | \ + DRM_BO_FLAG_CACHED | \ + DRM_BO_FLAG_MAPPABLE) + +/* Driver-private flags */ +#define DRM_BO_MASK_DRIVER 0xFFFF000000000000ULL -struct drm_ref_object *drm_lookup_ref_object(struct drm_file *priv, - struct drm_user_object *referenced_object, - enum drm_ref_type ref_action); /* - * Must be called with the struct_mutex held. - * If "item" has been obtained by a call to drm_lookup_ref_object. You may not - * release the struct_mutex before calling drm_remove_ref_object. - * This function may temporarily release the struct_mutex. + * Don't block on validate and map. Instead, return EBUSY. + */ +#define DRM_BO_HINT_DONT_BLOCK 0x00000002 +/* + * Don't place this buffer on the unfenced list. This means + * that the buffer will not end up having a fence associated + * with it as a result of this operation + */ +#define DRM_BO_HINT_DONT_FENCE 0x00000004 +/** + * On hardware with no interrupt events for operation completion, + * indicates that the kernel should sleep while waiting for any blocking + * operation to complete rather than spinning. + * + * Has no effect otherwise. + */ +#define DRM_BO_HINT_WAIT_LAZY 0x00000008 +/* + * The client has compute relocations refering to this buffer using the + * offset in the presumed_offset field. If that offset ends up matching + * where this buffer lands, the kernel is free to skip executing those + * relocations */ +#define DRM_BO_HINT_PRESUMED_OFFSET 0x00000010 + +#define DRM_BO_MEM_LOCAL 0 +#define DRM_BO_MEM_TT 1 +#define DRM_BO_MEM_VRAM 2 +#define DRM_BO_MEM_PRIV0 3 +#define DRM_BO_MEM_PRIV1 4 +#define DRM_BO_MEM_PRIV2 5 +#define DRM_BO_MEM_PRIV3 6 +#define DRM_BO_MEM_PRIV4 7 + +#define DRM_BO_MEM_TYPES 8 /* For now. */ + +#define DRM_BO_LOCK_UNLOCK_BM (1 << 0) +#define DRM_BO_LOCK_IGNORE_NO_EVICT (1 << 1) -extern void drm_remove_ref_object(struct drm_file *priv, struct drm_ref_object *item); -extern int drm_user_object_ref(struct drm_file *priv, uint32_t user_token, - enum drm_object_type type, - struct drm_user_object **object); -extern int drm_user_object_unref(struct drm_file *priv, uint32_t user_token, - enum drm_object_type type); /*************************************************** * Fence objects. (drm_fence.c) */ struct drm_fence_object { - struct drm_user_object base; struct drm_device *dev; atomic_t usage; @@ -156,7 +258,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; @@ -471,7 +572,6 @@ enum drm_bo_type { struct drm_buffer_object { struct drm_device *dev; - struct drm_user_object base; /* * If there is a possibility that the usage variable is zero, @@ -547,7 +647,7 @@ struct drm_mem_type_manager { }; struct drm_bo_lock { - struct drm_user_object base; + // struct drm_user_object base; wait_queue_head_t queue; atomic_t write_lock_pending; atomic_t readers; @@ -561,6 +661,9 @@ struct drm_bo_lock { #define _DRM_FLAG_MEMTYPE_CMA 0x00000010 /* Can't map aperture */ #define _DRM_FLAG_MEMTYPE_CSELECT 0x00000020 /* Select caching */ +#define _DRM_BM_ALLOCATOR_CACHED 0x0 +#define _DRM_BM_ALLOCATOR_UNCACHED 0x1 + struct drm_buffer_manager { struct drm_bo_lock bm_lock; struct mutex evict_mutex; @@ -579,6 +682,7 @@ struct drm_buffer_manager { unsigned long cur_pages; atomic_t count; struct page *dummy_read_page; + int allocator_type; }; struct drm_bo_driver { @@ -656,22 +760,10 @@ 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_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); -extern int drm_bo_setstatus_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern int drm_mm_init_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern int drm_mm_takedown_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern int drm_mm_lock_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern int drm_mm_unlock_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern int drm_mm_info_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern int drm_bo_version_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +int drm_bo_do_validate(struct drm_buffer_object *bo, + uint64_t flags, uint64_t mask, uint32_t hint, + uint32_t fence_class); +extern int drm_bo_set_pin(struct drm_device *dev, struct drm_buffer_object *bo, int pin); extern int drm_bo_driver_finish(struct drm_device *dev); extern int drm_bo_driver_init(struct drm_device *dev); extern int drm_bo_pci_offset(struct drm_device *dev, @@ -681,6 +773,8 @@ extern int drm_bo_pci_offset(struct drm_device *dev, unsigned long *bus_size); extern int drm_mem_reg_is_pci(struct drm_device *dev, struct drm_bo_mem_reg *mem); +extern int drm_bo_add_user_object(struct drm_file *file_priv, + struct drm_buffer_object *bo, int shareable); extern void drm_bo_usage_deref_locked(struct drm_buffer_object **bo); extern void drm_bo_usage_deref_unlocked(struct drm_buffer_object **bo); extern void drm_putback_buffer_objects(struct drm_device *dev); @@ -706,30 +800,27 @@ extern int drm_bo_clean_mm(struct drm_device *dev, unsigned mem_type, int kern_c extern int drm_bo_init_mm(struct drm_device *dev, unsigned type, unsigned long p_offset, unsigned long p_size, int kern_init); -extern int drm_bo_handle_validate(struct drm_file *file_priv, uint32_t handle, - uint64_t flags, uint64_t mask, uint32_t hint, - uint32_t fence_class, - struct drm_bo_info_rep *rep, - struct drm_buffer_object **bo_rep); extern struct drm_buffer_object *drm_lookup_buffer_object(struct drm_file *file_priv, uint32_t handle, int check_owner); -extern int drm_bo_do_validate(struct drm_buffer_object *bo, - uint64_t flags, uint64_t mask, uint32_t hint, - uint32_t fence_class, - struct drm_bo_info_rep *rep); extern int drm_bo_evict_cached(struct drm_buffer_object *bo); + +extern void drm_bo_takedown_vm_locked(struct drm_buffer_object *bo); +extern void drm_bo_evict_mm(struct drm_device *dev, int mem_type, int no_wait); /* * Buffer object memory move- and map helpers. * drm_bo_move.c */ +extern int drm_bo_add_ttm(struct drm_buffer_object *bo); extern int drm_bo_move_ttm(struct drm_buffer_object *bo, int evict, int no_wait, struct drm_bo_mem_reg *new_mem); extern int drm_bo_move_memcpy(struct drm_buffer_object *bo, int evict, int no_wait, struct drm_bo_mem_reg *new_mem); +extern int drm_bo_move_zero(struct drm_buffer_object *bo, + int evict, int no_wait, struct drm_bo_mem_reg *new_mem); extern int drm_bo_move_accel_cleanup(struct drm_buffer_object *bo, int evict, int no_wait, uint32_t fence_class, uint32_t fence_type, @@ -763,8 +854,6 @@ extern int drm_bo_pfn_prot(struct drm_buffer_object *bo, unsigned long dst_offset, unsigned long *pfn, pgprot_t *prot); -extern void drm_bo_fill_rep_arg(struct drm_buffer_object *bo, - struct drm_bo_info_rep *rep); /* @@ -805,22 +894,18 @@ 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. + * drm_uncached.c */ - - -extern void drm_bo_init_lock(struct drm_bo_lock *lock); -extern void drm_bo_read_unlock(struct drm_bo_lock *lock); -extern int drm_bo_read_lock(struct drm_bo_lock *lock, - int interruptible); -extern int drm_bo_write_lock(struct drm_bo_lock *lock, - int interruptible, - struct drm_file *file_priv); - -extern int drm_bo_write_unlock(struct drm_bo_lock *lock, - struct drm_file *file_priv); +extern int drm_uncached_init(void); +extern void drm_uncached_fini(void); +extern struct page *drm_get_uncached_page(void); +extern void drm_put_uncached_page(struct page *page); #ifdef CONFIG_DEBUG_MUTEXES #define DRM_ASSERT_LOCKED(_mutex) \ diff --git a/linux-core/drm_proc.c b/linux-core/drm_proc.c index d3845bb3..e66edfb3 100644 --- a/linux-core/drm_proc.c +++ b/linux-core/drm_proc.c @@ -172,6 +172,7 @@ static int drm_name_info(char *buf, char **start, off_t offset, int request, int *eof, void *data) { struct drm_minor *minor = (struct drm_minor *) data; + struct drm_master *master = minor->master; struct drm_device *dev = minor->dev; int len = 0; @@ -180,13 +181,16 @@ static int drm_name_info(char *buf, char **start, off_t offset, int request, return 0; } + if (!master) + return 0; + *start = &buf[offset]; *eof = 0; - if (dev->unique) { + if (master->unique) { DRM_PROC_PRINT("%s %s %s\n", dev->driver->pci_driver.name, - pci_name(dev->pdev), dev->unique); + pci_name(dev->pdev), master->unique); } else { DRM_PROC_PRINT("%s %s\n", dev->driver->pci_driver.name, pci_name(dev->pdev)); diff --git a/linux-core/drm_stub.c b/linux-core/drm_stub.c index 1518bc6d..cd6cd4b5 100644 --- a/linux-core/drm_stub.c +++ b/linux-core/drm_stub.c @@ -58,6 +58,14 @@ static int drm_minor_get_id(struct drm_device *dev, int type) int ret; int base = 0, limit = 63; + if (type == DRM_MINOR_CONTROL) { + base += 64; + limit = base + 127; + } else if (type == DRM_MINOR_RENDER) { + base += 128; + limit = base + 255; + } + again: if (idr_pre_get(&drm_minors_idr, GFP_KERNEL) == 0) { DRM_ERROR("Out of memory expanding drawable idr\n"); @@ -80,6 +88,103 @@ again: return new_id; } +struct drm_master *drm_master_create(struct drm_minor *minor) +{ + struct drm_master *master; + + master = drm_calloc(1, sizeof(*master), DRM_MEM_DRIVER); + if (!master) + return NULL; + + kref_init(&master->refcount); + spin_lock_init(&master->lock.spinlock); + init_waitqueue_head(&master->lock.lock_queue); + drm_ht_create(&master->magiclist, DRM_MAGIC_HASH_ORDER); + INIT_LIST_HEAD(&master->magicfree); + master->minor = minor; + + list_add_tail(&master->head, &minor->master_list); + + return master; +} + +struct drm_master *drm_master_get(struct drm_master *master) +{ + kref_get(&master->refcount); + return master; +} + +static void drm_master_destroy(struct kref *kref) +{ + struct drm_master *master = container_of(kref, struct drm_master, refcount); + struct drm_magic_entry *pt, *next; + struct drm_device *dev = master->minor->dev; + + list_del(&master->head); + + if (dev->driver->master_destroy) + dev->driver->master_destroy(dev, master); + + if (master->unique) { + drm_free(master->unique, strlen(master->unique) + 1, DRM_MEM_DRIVER); + master->unique = NULL; + master->unique_len = 0; + } + + list_for_each_entry_safe(pt, next, &master->magicfree, head) { + list_del(&pt->head); + drm_ht_remove_item(&master->magiclist, &pt->hash_item); + drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); + } + + drm_ht_remove(&master->magiclist); + + if (master->lock.hw_lock) { + if (dev->sigdata.lock == master->lock.hw_lock) + dev->sigdata.lock = NULL; + master->lock.hw_lock = NULL; /* SHM removed */ + master->lock.file_priv = NULL; + wake_up_interruptible(&master->lock.lock_queue); + } + + drm_free(master, sizeof(*master), DRM_MEM_DRIVER); +} + +void drm_master_put(struct drm_master **master) +{ + kref_put(&(*master)->refcount, drm_master_destroy); + *master = NULL; +} + +int drm_setmaster_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + if (file_priv->minor->master && file_priv->minor->master != file_priv->master) + return -EINVAL; + + if (!file_priv->master) + return -EINVAL; + + if (!file_priv->minor->master && file_priv->minor->master != file_priv->master) { + mutex_lock(&dev->struct_mutex); + file_priv->minor->master = drm_master_get(file_priv->master); + mutex_unlock(&dev->struct_mutex); + } + + return 0; +} + +int drm_dropmaster_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + if (!file_priv->master) + return -EINVAL; + mutex_lock(&dev->struct_mutex); + drm_master_put(&file_priv->minor->master); + mutex_unlock(&dev->struct_mutex); + return 0; +} + static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev, const struct pci_device_id *ent, struct drm_driver *driver) @@ -94,7 +199,7 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev, spin_lock_init(&dev->count_lock); spin_lock_init(&dev->drw_lock); spin_lock_init(&dev->tasklet_lock); - spin_lock_init(&dev->lock.spinlock); + init_timer(&dev->timer); mutex_init(&dev->struct_mutex); mutex_init(&dev->ctxlist_mutex); @@ -112,21 +217,14 @@ 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); return -ENOMEM; } - if (drm_ht_create(&dev->object_hash, DRM_OBJECT_HASH_ORDER)) { - drm_ht_remove(&dev->map_hash); - drm_mm_takedown(&dev->offset_manager); - return -ENOMEM; - } - /* the DRM has 6 counters */ dev->counters = 6; dev->types[0] = _DRM_STAT_LOCK; @@ -213,6 +311,7 @@ static int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int t new_minor->device = MKDEV(DRM_MAJOR, minor_id); new_minor->dev = dev; new_minor->index = minor_id; + INIT_LIST_HEAD(&new_minor->master_list); idr_replace(&drm_minors_idr, new_minor, minor_id); @@ -299,20 +398,32 @@ int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent, } /* only add the control node on a modesetting platform */ + if (drm_core_check_feature(dev, DRIVER_MODESET)) + if ((ret = drm_get_minor(dev, &dev->control, DRM_MINOR_CONTROL))) + goto err_g3; + if ((ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY))) - goto err_g3; + goto err_g4; if (dev->driver->load) if ((ret = dev->driver->load(dev, ent->driver_data))) - goto err_g4; + goto err_g5; + + /* setup the grouping for the legacy output */ + if (drm_core_check_feature(dev, DRIVER_MODESET)) + if (drm_mode_group_init_legacy_group(dev, &dev->primary->mode_group)) + goto err_g5; DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", driver->name, driver->major, driver->minor, driver->patchlevel, driver->date, dev->primary->index); return 0; +err_g5: + drm_put_minor(&dev->primary); err_g4: - drm_put_minor(dev); + if (drm_core_check_feature(dev, DRIVER_MODESET)) + drm_put_minor(&dev->control); err_g3: if (!drm_fb_loaded) pci_disable_device(pdev); @@ -344,11 +455,6 @@ int drm_put_dev(struct drm_device * dev) { DRM_DEBUG("release primary %s\n", dev->driver->pci_driver.name); - if (dev->unique) { - drm_free(dev->unique, strlen(dev->unique) + 1, DRM_MEM_DRIVER); - dev->unique = NULL; - dev->unique_len = 0; - } if (dev->devname) { drm_free(dev->devname, strlen(dev->devname) + 1, DRM_MEM_DRIVER); @@ -368,15 +474,14 @@ int drm_put_dev(struct drm_device * dev) * last minor released. * */ -int drm_put_minor(struct drm_device *dev) +int drm_put_minor(struct drm_minor **minor_p) { - struct drm_minor **minor_p = &dev->primary; struct drm_minor *minor = *minor_p; DRM_DEBUG("release secondary minor %d\n", minor->index); if (minor->type == DRM_MINOR_LEGACY) { - if (dev->driver->proc_cleanup) - dev->driver->proc_cleanup(minor); + if (minor->dev->driver->proc_cleanup) + minor->dev->driver->proc_cleanup(minor); drm_proc_cleanup(minor, drm_proc_root); } drm_sysfs_device_remove(minor); @@ -387,3 +492,5 @@ int drm_put_minor(struct drm_device *dev) *minor_p = NULL; return 0; } + +EXPORT_SYMBOL(drm_put_minor); diff --git a/linux-core/drm_sysfs.c b/linux-core/drm_sysfs.c index 6de93679..f5366e8c 100644 --- a/linux-core/drm_sysfs.c +++ b/linux-core/drm_sysfs.c @@ -36,8 +36,9 @@ static int drm_sysfs_suspend(struct device *dev, pm_message_t state) printk(KERN_ERR "%s\n", __FUNCTION__); - if (drm_dev->driver->suspend) - return drm_dev->driver->suspend(drm_dev, state); + if (drm_minor->type == DRM_MINOR_CONTROL) + if (drm_dev->driver->suspend) + return drm_dev->driver->suspend(drm_dev, state); return 0; } @@ -54,8 +55,9 @@ static int drm_sysfs_resume(struct device *dev) struct drm_minor *drm_minor = to_drm_minor(dev); struct drm_device *drm_dev = drm_minor->dev; - if (drm_dev->driver->resume) - return drm_dev->driver->resume(drm_dev); + if (drm_minor->type == DRM_MINOR_CONTROL) + if (drm_dev->driver->resume) + return drm_dev->driver->resume(drm_dev); return 0; } @@ -131,10 +133,6 @@ static ssize_t show_dri(struct device *device, struct device_attribute *attr, return snprintf(buf, PAGE_SIZE, "%s\n", drm_dev->driver->pci_driver.name); } -static struct device_attribute device_attrs[] = { - __ATTR(dri_library_name, S_IRUGO, show_dri, NULL), -}; - /** * drm_sysfs_device_release - do nothing * @dev: Linux device @@ -148,6 +146,316 @@ static void drm_sysfs_device_release(struct device *dev) return; } +/* + * Connector properties + */ +static ssize_t status_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct drm_connector *connector = container_of(device, struct drm_connector, kdev); + return snprintf(buf, PAGE_SIZE, "%s", + drm_get_connector_status_name(connector->funcs->detect(connector))); +} + +static ssize_t dpms_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct drm_connector *connector = container_of(device, struct drm_connector, kdev); + struct drm_device *dev = connector->dev; + uint64_t dpms_status; + int ret; + + ret = drm_connector_property_get_value(connector, + dev->mode_config.dpms_property, + &dpms_status); + if (ret) + return 0; + + return snprintf(buf, PAGE_SIZE, "%s", drm_get_dpms_name((int)dpms_status)); +} + +static ssize_t enabled_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct drm_connector *connector = container_of(device, struct drm_connector, kdev); + + if (connector->encoder) + return snprintf(buf, PAGE_SIZE, "enabled"); + else + return snprintf(buf, PAGE_SIZE, "disabled"); +} + +static ssize_t edid_show(struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct device *connector_dev = container_of(kobj, struct device, kobj); + struct drm_connector *connector = container_of(connector_dev, struct drm_connector, + kdev); + unsigned char *edid; + size_t size; + + if (!connector->edid_blob_ptr) + return 0; + + edid = connector->edid_blob_ptr->data; + size = connector->edid_blob_ptr->length; + if (!edid) + return 0; + + if (off >= size) + return 0; + + if (off + count > size) + count = size - off; + memcpy(buf, edid + off, count); + + return count; +} + +static ssize_t modes_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct drm_connector *connector = container_of(device, struct drm_connector, kdev); + struct drm_display_mode *mode; + int written = 0; + + list_for_each_entry(mode, &connector->modes, head) { + written += snprintf(buf + written, PAGE_SIZE - written, "%s\n", + mode->name); + } + + return written; +} + +static ssize_t subconnector_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct drm_connector *connector = container_of(device, struct drm_connector, kdev); + struct drm_device *dev = connector->dev; + struct drm_property *prop = NULL; + uint64_t subconnector; + int ret; + + switch (connector->connector_type) { + case DRM_MODE_CONNECTOR_DVII: + prop = dev->mode_config.dvi_i_subconnector_property; + break; + case DRM_MODE_CONNECTOR_Composite: + case DRM_MODE_CONNECTOR_SVIDEO: + case DRM_MODE_CONNECTOR_Component: + prop = dev->mode_config.tv_subconnector_property; + break; + default: + DRM_ERROR("Wrong connector type for this property\n"); + return 0; + } + + if (!prop) { + DRM_ERROR("Unable to find subconnector property\n"); + return 0; + } + + ret = drm_connector_property_get_value(connector, prop, &subconnector); + if (ret) + return 0; + + return snprintf(buf, PAGE_SIZE, "%s", drm_get_subconnector_name((int)subconnector)); +} + +static ssize_t select_subconnector_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct drm_connector *connector = container_of(device, struct drm_connector, kdev); + struct drm_device *dev = connector->dev; + struct drm_property *prop = NULL; + uint64_t subconnector; + int ret; + + switch (connector->connector_type) { + case DRM_MODE_CONNECTOR_DVII: + prop = dev->mode_config.dvi_i_select_subconnector_property; + break; + case DRM_MODE_CONNECTOR_Composite: + case DRM_MODE_CONNECTOR_SVIDEO: + case DRM_MODE_CONNECTOR_Component: + prop = dev->mode_config.tv_select_subconnector_property; + break; + default: + DRM_ERROR("Wrong connector type for this property\n"); + return 0; + } + + if (!prop) { + DRM_ERROR("Unable to find select subconnector property\n"); + return 0; + } + + ret = drm_connector_property_get_value(connector, prop, &subconnector); + if (ret) + return 0; + + return snprintf(buf, PAGE_SIZE, "%s", drm_get_select_subconnector_name((int)subconnector)); +} + +static struct device_attribute connector_attrs[] = { + __ATTR_RO(status), + __ATTR_RO(enabled), + __ATTR_RO(dpms), + __ATTR_RO(modes), +}; + +/* These attributes are for both DVI-I connectors and all types of tv-out. */ +static struct device_attribute connector_attrs_opt1[] = { + __ATTR_RO(subconnector), + __ATTR_RO(select_subconnector), +}; + +static struct bin_attribute edid_attr = { + .attr.name = "edid", + .size = 128, + .read = edid_show, +}; + +/** + * drm_sysfs_connector_add - add an connector to sysfs + * @connector: connector to add + * + * Create an connector device in sysfs, along with its associated connector + * properties (so far, connection status, dpms, mode list & edid) and + * generate a hotplug event so userspace knows there's a new connector + * available. + * + * Note: + * This routine should only be called *once* for each DRM minor registered. + * A second call for an already registered device will trigger the BUG_ON + * below. + */ +int drm_sysfs_connector_add(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + int ret = 0, i, j; + + /* We shouldn't get called more than once for the same connector */ + BUG_ON(device_is_registered(&connector->kdev)); + + connector->kdev.parent = &dev->primary->kdev; + connector->kdev.class = drm_class; + connector->kdev.release = drm_sysfs_device_release; + + DRM_DEBUG("adding \"%s\" to sysfs\n", + drm_get_connector_name(connector)); + + snprintf(connector->kdev.bus_id, BUS_ID_SIZE, "card%d-%s", + dev->primary->index, drm_get_connector_name(connector)); + ret = device_register(&connector->kdev); + + if (ret) { + DRM_ERROR("failed to register connector device: %d\n", ret); + goto out; + } + + /* Standard attributes */ + + for (i = 0; i < ARRAY_SIZE(connector_attrs); i++) { + ret = device_create_file(&connector->kdev, &connector_attrs[i]); + if (ret) + goto err_out_files; + } + + /* Optional attributes */ + /* On the long run it maybe a good idea to make one set of optionals per connector type. */ + + switch (connector->connector_type) { + case DRM_MODE_CONNECTOR_DVII: + case DRM_MODE_CONNECTOR_Composite: + case DRM_MODE_CONNECTOR_SVIDEO: + case DRM_MODE_CONNECTOR_Component: + for (i = 0; i < ARRAY_SIZE(connector_attrs_opt1); i++) { + ret = device_create_file(&connector->kdev, &connector_attrs_opt1[i]); + if (ret) + goto err_out_files; + } + break; + default: + break; + } + + ret = sysfs_create_bin_file(&connector->kdev.kobj, &edid_attr); + if (ret) + goto err_out_files; + + /* Let userspace know we have a new connector */ + drm_sysfs_hotplug_event(dev); + + return 0; + +err_out_files: + if (i > 0) + for (j = 0; j < i; j++) + device_remove_file(&connector->kdev, &connector_attrs[i]); + device_unregister(&connector->kdev); + +out: + return ret; +} +EXPORT_SYMBOL(drm_sysfs_connector_add); + +/** + * drm_sysfs_connector_remove - remove an connector device from sysfs + * @connector: connector to remove + * + * Remove @connector and its associated attributes from sysfs. Note that + * the device model core will take care of sending the "remove" uevent + * at this time, so we don't need to do it. + * + * Note: + * This routine should only be called if the connector was previously + * successfully registered. If @connector hasn't been registered yet, + * you'll likely see a panic somewhere deep in sysfs code when called. + */ +void drm_sysfs_connector_remove(struct drm_connector *connector) +{ + int i; + + DRM_DEBUG("removing \"%s\" from sysfs\n", + drm_get_connector_name(connector)); + + for (i = 0; i < ARRAY_SIZE(connector_attrs); i++) + device_remove_file(&connector->kdev, &connector_attrs[i]); + sysfs_remove_bin_file(&connector->kdev.kobj, &edid_attr); + device_unregister(&connector->kdev); +} +EXPORT_SYMBOL(drm_sysfs_connector_remove); + +/** + * drm_sysfs_hotplug_event - generate a DRM uevent + * @dev: DRM device + * + * Send a uevent for the DRM device specified by @dev. Currently we only + * set HOTPLUG=1 in the uevent environment, but this could be expanded to + * deal with other types of events. + */ +void drm_sysfs_hotplug_event(struct drm_device *dev) +{ + char *event_string = "HOTPLUG=1"; + char *envp[] = { event_string, NULL }; + + DRM_DEBUG("generating hotplug event\n"); + + kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, envp); +} +EXPORT_SYMBOL(drm_sysfs_hotplug_event); + +static struct device_attribute dri_attrs[] = { + __ATTR(dri_library_name, S_IRUGO, show_dri, NULL), +}; + /** * drm_sysfs_device_add - adds a class device to sysfs for a character driver * @dev: DRM device to be added @@ -156,6 +464,11 @@ static void drm_sysfs_device_release(struct device *dev) * Add a DRM device to the DRM's device model class. We use @dev's PCI device * as the parent for the Linux device, and make sure it has a file containing * the driver we're using (for userspace compatibility). + * + * Note: + * This routine should only be called *once* for each DRM minor registered. + * A second call for an already registered device will trigger the BUG_ON + * below. */ int drm_sysfs_device_add(struct drm_minor *minor) { @@ -167,18 +480,28 @@ int drm_sysfs_device_add(struct drm_minor *minor) minor->kdev.class = drm_class; minor->kdev.release = drm_sysfs_device_release; minor->kdev.devt = minor->device; - minor_str = "card%d"; + if (minor->type == DRM_MINOR_CONTROL) + minor_str = "controlD%d"; + else if (minor->type == DRM_MINOR_RENDER) + minor_str = "renderD%d"; + else + minor_str = "card%d"; snprintf(minor->kdev.bus_id, BUS_ID_SIZE, minor_str, minor->index); + /* Shouldn't register more than once */ + BUG_ON(device_is_registered(&minor->kdev)); + + DRM_DEBUG("registering DRM device \"%s\"\n", minor->kdev.bus_id); + err = device_register(&minor->kdev); if (err) { DRM_ERROR("device add failed: %d\n", err); goto err_out; } - for (i = 0; i < ARRAY_SIZE(device_attrs); i++) { - err = device_create_file(&minor->kdev, &device_attrs[i]); + for (i = 0; i < ARRAY_SIZE(dri_attrs); i++) { + err = device_create_file(&minor->kdev, &dri_attrs[i]); if (err) goto err_out_files; } @@ -188,7 +511,7 @@ int drm_sysfs_device_add(struct drm_minor *minor) err_out_files: if (i > 0) for (j = 0; j < i; j++) - device_remove_file(&minor->kdev, &device_attrs[j]); + device_remove_file(&minor->kdev, &dri_attrs[j]); device_unregister(&minor->kdev); err_out: @@ -206,7 +529,7 @@ void drm_sysfs_device_remove(struct drm_minor *minor) { int i; - for (i = 0; i < ARRAY_SIZE(device_attrs); i++) - device_remove_file(&minor->kdev, &device_attrs[i]); + for (i = 0; i < ARRAY_SIZE(dri_attrs); i++) + device_remove_file(&minor->kdev, &dri_attrs[i]); device_unregister(&minor->kdev); } diff --git a/linux-core/drm_ttm.c b/linux-core/drm_ttm.c index 054a7ce8..4067b9e1 100644 --- a/linux-core/drm_ttm.c +++ b/linux-core/drm_ttm.c @@ -120,14 +120,18 @@ static void drm_ttm_free_page_directory(struct drm_ttm *ttm) ttm->pages = NULL; } -static struct page *drm_ttm_alloc_page(void) +static struct page *drm_ttm_alloc_page(struct drm_ttm *ttm) { struct page *page; if (drm_alloc_memctl(PAGE_SIZE)) return NULL; - page = alloc_page(GFP_KERNEL | __GFP_ZERO | GFP_DMA32); + if (ttm->dev->bm.allocator_type == _DRM_BM_ALLOCATOR_UNCACHED) + page = drm_get_uncached_page(); + else + page = alloc_page(GFP_KERNEL | __GFP_ZERO | GFP_DMA32); + if (!page) { drm_free_memctl(PAGE_SIZE); return NULL; @@ -149,6 +153,9 @@ static int drm_ttm_set_caching(struct drm_ttm *ttm, int noncached) struct page **cur_page; int do_tlbflush = 0; + if (ttm->dev->bm.allocator_type == _DRM_BM_ALLOCATOR_UNCACHED) + return 0; + if ((ttm->page_flags & DRM_TTM_PAGE_UNCACHED) == noncached) return 0; @@ -215,14 +222,18 @@ static void drm_ttm_free_alloced_pages(struct drm_ttm *ttm) for (i = 0; i < ttm->num_pages; ++i) { cur_page = ttm->pages + i; if (*cur_page) { + if (ttm->dev->bm.allocator_type == _DRM_BM_ALLOCATOR_UNCACHED) + drm_put_uncached_page(*cur_page); + else { #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) - ClearPageReserved(*cur_page); + ClearPageReserved(*cur_page); #endif - if (page_count(*cur_page) != 1) - DRM_ERROR("Erroneous page count. Leaking pages.\n"); - if (page_mapped(*cur_page)) - DRM_ERROR("Erroneous map count. Leaking page mappings.\n"); - __free_page(*cur_page); + if (page_count(*cur_page) != 1) + DRM_ERROR("Erroneous page count. Leaking pages.\n"); + if (page_mapped(*cur_page)) + DRM_ERROR("Erroneous map count. Leaking page mappings.\n"); + __free_page(*cur_page); + } drm_free_memctl(PAGE_SIZE); --bm->cur_pages; } @@ -268,7 +279,7 @@ struct page *drm_ttm_get_page(struct drm_ttm *ttm, int index) struct drm_buffer_manager *bm = &ttm->dev->bm; while(NULL == (p = ttm->pages[index])) { - p = drm_ttm_alloc_page(); + p = drm_ttm_alloc_page(ttm); if (!p) return NULL; diff --git a/linux-core/drm_uncached.c b/linux-core/drm_uncached.c new file mode 100644 index 00000000..9c7183b0 --- /dev/null +++ b/linux-core/drm_uncached.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) Red Hat 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, sub license, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Dave Airlie <airlied@redhat.com> + */ + +/* simple list based uncached page allocator + * - Add chunks of 1MB to the allocator at a time. + * - Use page->lru to keep a free list + * - doesn't track currently in use pages + * + * TODO: Add shrinker support + */ + +#include "drmP.h" +#include <asm/agp.h> + +static struct list_head uncached_free_list; + +static struct mutex uncached_mutex; +static int uncached_inited; +static int total_uncached_pages; + +/* add 1MB at a time */ +#define NUM_PAGES_TO_ADD 256 + +static void drm_uncached_page_put(struct page *page) +{ + unmap_page_from_agp(page); + put_page(page); + __free_page(page); +} + +int drm_uncached_add_pages_locked(int num_pages) +{ + struct page *page; + int i; + + DRM_DEBUG("adding uncached memory %ld\n", num_pages * PAGE_SIZE); + for (i = 0; i < num_pages; i++) { + + page = alloc_page(GFP_KERNEL | __GFP_ZERO | GFP_DMA32); + if (!page) { + DRM_ERROR("unable to get page %d\n", i); + return i; + } + + get_page(page); +#ifdef CONFIG_X86 + set_memory_wc((unsigned long)page_address(page), 1); +#else + map_page_into_agp(page); +#endif + + list_add(&page->lru, &uncached_free_list); + total_uncached_pages++; + } + return i; +} + +struct page *drm_get_uncached_page(void) +{ + struct page *page = NULL; + int ret; + + mutex_lock(&uncached_mutex); + if (list_empty(&uncached_free_list)) { + ret = drm_uncached_add_pages_locked(NUM_PAGES_TO_ADD); + if (ret == 0) + return NULL; + } + + page = list_first_entry(&uncached_free_list, struct page, lru); + list_del(&page->lru); + + mutex_unlock(&uncached_mutex); + return page; +} + +void drm_put_uncached_page(struct page *page) +{ + mutex_lock(&uncached_mutex); + list_add(&page->lru, &uncached_free_list); + mutex_unlock(&uncached_mutex); +} + +void drm_uncached_release_all_pages(void) +{ + struct page *page, *tmp; + + list_for_each_entry_safe(page, tmp, &uncached_free_list, lru) { + list_del(&page->lru); + drm_uncached_page_put(page); + } +} + +int drm_uncached_init(void) +{ + + if (uncached_inited) + return 0; + + INIT_LIST_HEAD(&uncached_free_list); + + mutex_init(&uncached_mutex); + uncached_inited = 1; + return 0; + +} + +void drm_uncached_fini(void) +{ + if (!uncached_inited) + return; + + uncached_inited = 0; + drm_uncached_release_all_pages(); +} + diff --git a/linux-core/drm_vm.c b/linux-core/drm_vm.c index d4d97a4d..228ea6cd 100644 --- a/linux-core/drm_vm.c +++ b/linux-core/drm_vm.c @@ -715,13 +715,9 @@ static int drm_bo_vm_fault(struct vm_area_struct *vma, unsigned long ret = VM_FAULT_NOPAGE; dev = bo->dev; - err = drm_bo_read_lock(&dev->bm.bm_lock, 1); - if (err) - return VM_FAULT_NOPAGE; err = mutex_lock_interruptible(&bo->mutex); if (err) { - drm_bo_read_unlock(&dev->bm.bm_lock); return VM_FAULT_NOPAGE; } @@ -788,7 +784,6 @@ static int drm_bo_vm_fault(struct vm_area_struct *vma, out_unlock: BUG_ON(bo->priv_flags & _DRM_BO_FLAG_UNLOCKED); mutex_unlock(&bo->mutex); - drm_bo_read_unlock(&dev->bm.bm_lock); return ret; } #endif @@ -797,6 +792,10 @@ static void drm_bo_vm_open_locked(struct vm_area_struct *vma) { struct drm_buffer_object *bo = (struct drm_buffer_object *) vma->vm_private_data; + /* clear the clean flags */ + bo->mem.flags &= ~DRM_BO_FLAG_CLEAN; + bo->mem.proposed_flags &= ~DRM_BO_FLAG_CLEAN; + drm_vm_open_locked(vma); atomic_inc(&bo->usage); #ifdef DRM_ODD_MM_COMPAT diff --git a/linux-core/dvo.h b/linux-core/dvo.h new file mode 100644 index 00000000..b122ea1f --- /dev/null +++ b/linux-core/dvo.h @@ -0,0 +1,159 @@ +/* + * Copyright © 2006 Eric Anholt + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _INTEL_DVO_H +#define _INTEL_DVO_H + +#include <linux/i2c.h> +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "intel_drv.h" + +struct intel_dvo_device { + char *name; + int type; + /* DVOA/B/C output register */ + u32 dvo_reg; + /* GPIO register used for i2c bus to control this device */ + u32 gpio; + int slave_addr; + struct intel_i2c_chan *i2c_bus; + + const struct intel_dvo_dev_ops *dev_ops; + void *dev_priv; + + struct drm_display_mode *panel_fixed_mode; + bool panel_wants_dither; +}; + +struct intel_dvo_dev_ops { + /* + * Initialize the device at startup time. + * Returns NULL if the device does not exist. + */ + bool (*init)(struct intel_dvo_device *dvo, + struct intel_i2c_chan *i2cbus); + + /* + * Called to allow the output a chance to create properties after the + * RandR objects have been created. + */ + void (*create_resources)(struct intel_dvo_device *dvo); + + /* + * Turn on/off output or set intermediate power levels if available. + * + * Unsupported intermediate modes drop to the lower power setting. + * If the mode is DPMSModeOff, the output must be disabled, + * as the DPLL may be disabled afterwards. + */ + void (*dpms)(struct intel_dvo_device *dvo, int mode); + + /* + * Saves the output's state for restoration on VT switch. + */ + void (*save)(struct intel_dvo_device *dvo); + + /* + * Restore's the output's state at VT switch. + */ + void (*restore)(struct intel_dvo_device *dvo); + + /* + * Callback for testing a video mode for a given output. + * + * This function should only check for cases where a mode can't + * be supported on the output specifically, and not represent + * generic CRTC limitations. + * + * \return MODE_OK if the mode is valid, or another MODE_* otherwise. + */ + int (*mode_valid)(struct intel_dvo_device *dvo, + struct drm_display_mode *mode); + + /* + * Callback to adjust the mode to be set in the CRTC. + * + * This allows an output to adjust the clock or even the entire set of + * timings, which is used for panels with fixed timings or for + * buses with clock limitations. + */ + bool (*mode_fixup)(struct intel_dvo_device *dvo, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + + /* + * Callback for preparing mode changes on an output + */ + void (*prepare)(struct intel_dvo_device *dvo); + + /* + * Callback for committing mode changes on an output + */ + void (*commit)(struct intel_dvo_device *dvo); + + /* + * Callback for setting up a video mode after fixups have been made. + * + * This is only called while the output is disabled. The dpms callback + * must be all that's necessary for the output, to turn the output on + * after this function is called. + */ + void (*mode_set)(struct intel_dvo_device *dvo, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + + /* + * Probe for a connected output, and return detect_status. + */ + enum drm_connector_status (*detect)(struct intel_dvo_device *dvo); + + /** + * Query the device for the modes it provides. + * + * This function may also update MonInfo, mm_width, and mm_height. + * + * \return singly-linked list of modes or NULL if no modes found. + */ + struct drm_display_mode *(*get_modes)(struct intel_dvo_device *dvo); + +#ifdef RANDR_12_INTERFACE + /** + * Callback when an output's property has changed. + */ + bool (*set_property)(struct intel_dvo_device *dvo, + struct drm_property *property, uint64_t val); +#endif + + /** + * Clean up driver-specific bits of the output + */ + void (*destroy) (struct intel_dvo_device *dvo); + + /** + * Debugging hook to dump device registers to log file + */ + void (*dump_regs)(struct intel_dvo_device *dvo); +}; + +#endif /* _INTEL_DVO_H */ diff --git a/linux-core/dvo_ch7017.c b/linux-core/dvo_ch7017.c new file mode 100644 index 00000000..b10e0388 --- /dev/null +++ b/linux-core/dvo_ch7017.c @@ -0,0 +1,454 @@ +/* + * Copyright © 2006 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * + */ + +#include "dvo.h" + +#define CH7017_TV_DISPLAY_MODE 0x00 +#define CH7017_FLICKER_FILTER 0x01 +#define CH7017_VIDEO_BANDWIDTH 0x02 +#define CH7017_TEXT_ENHANCEMENT 0x03 +#define CH7017_START_ACTIVE_VIDEO 0x04 +#define CH7017_HORIZONTAL_POSITION 0x05 +#define CH7017_VERTICAL_POSITION 0x06 +#define CH7017_BLACK_LEVEL 0x07 +#define CH7017_CONTRAST_ENHANCEMENT 0x08 +#define CH7017_TV_PLL 0x09 +#define CH7017_TV_PLL_M 0x0a +#define CH7017_TV_PLL_N 0x0b +#define CH7017_SUB_CARRIER_0 0x0c +#define CH7017_CIV_CONTROL 0x10 +#define CH7017_CIV_0 0x11 +#define CH7017_CHROMA_BOOST 0x14 +#define CH7017_CLOCK_MODE 0x1c +#define CH7017_INPUT_CLOCK 0x1d +#define CH7017_GPIO_CONTROL 0x1e +#define CH7017_INPUT_DATA_FORMAT 0x1f +#define CH7017_CONNECTION_DETECT 0x20 +#define CH7017_DAC_CONTROL 0x21 +#define CH7017_BUFFERED_CLOCK_OUTPUT 0x22 +#define CH7017_DEFEAT_VSYNC 0x47 +#define CH7017_TEST_PATTERN 0x48 + +#define CH7017_POWER_MANAGEMENT 0x49 +/** Enables the TV output path. */ +#define CH7017_TV_EN (1 << 0) +#define CH7017_DAC0_POWER_DOWN (1 << 1) +#define CH7017_DAC1_POWER_DOWN (1 << 2) +#define CH7017_DAC2_POWER_DOWN (1 << 3) +#define CH7017_DAC3_POWER_DOWN (1 << 4) +/** Powers down the TV out block, and DAC0-3 */ +#define CH7017_TV_POWER_DOWN_EN (1 << 5) + +#define CH7017_VERSION_ID 0x4a + +#define CH7017_DEVICE_ID 0x4b +#define CH7017_DEVICE_ID_VALUE 0x1b +#define CH7018_DEVICE_ID_VALUE 0x1a +#define CH7019_DEVICE_ID_VALUE 0x19 + +#define CH7017_XCLK_D2_ADJUST 0x53 +#define CH7017_UP_SCALER_COEFF_0 0x55 +#define CH7017_UP_SCALER_COEFF_1 0x56 +#define CH7017_UP_SCALER_COEFF_2 0x57 +#define CH7017_UP_SCALER_COEFF_3 0x58 +#define CH7017_UP_SCALER_COEFF_4 0x59 +#define CH7017_UP_SCALER_VERTICAL_INC_0 0x5a +#define CH7017_UP_SCALER_VERTICAL_INC_1 0x5b +#define CH7017_GPIO_INVERT 0x5c +#define CH7017_UP_SCALER_HORIZONTAL_INC_0 0x5d +#define CH7017_UP_SCALER_HORIZONTAL_INC_1 0x5e + +#define CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT 0x5f +/**< Low bits of horizontal active pixel input */ + +#define CH7017_ACTIVE_INPUT_LINE_OUTPUT 0x60 +/** High bits of horizontal active pixel input */ +#define CH7017_LVDS_HAP_INPUT_MASK (0x7 << 0) +/** High bits of vertical active line output */ +#define CH7017_LVDS_VAL_HIGH_MASK (0x7 << 3) + +#define CH7017_VERTICAL_ACTIVE_LINE_OUTPUT 0x61 +/**< Low bits of vertical active line output */ + +#define CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT 0x62 +/**< Low bits of horizontal active pixel output */ + +#define CH7017_LVDS_POWER_DOWN 0x63 +/** High bits of horizontal active pixel output */ +#define CH7017_LVDS_HAP_HIGH_MASK (0x7 << 0) +/** Enables the LVDS power down state transition */ +#define CH7017_LVDS_POWER_DOWN_EN (1 << 6) +/** Enables the LVDS upscaler */ +#define CH7017_LVDS_UPSCALER_EN (1 << 7) +#define CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED 0x08 + +#define CH7017_LVDS_ENCODING 0x64 +#define CH7017_LVDS_DITHER_2D (1 << 2) +#define CH7017_LVDS_DITHER_DIS (1 << 3) +#define CH7017_LVDS_DUAL_CHANNEL_EN (1 << 4) +#define CH7017_LVDS_24_BIT (1 << 5) + +#define CH7017_LVDS_ENCODING_2 0x65 + +#define CH7017_LVDS_PLL_CONTROL 0x66 +/** Enables the LVDS panel output path */ +#define CH7017_LVDS_PANEN (1 << 0) +/** Enables the LVDS panel backlight */ +#define CH7017_LVDS_BKLEN (1 << 3) + +#define CH7017_POWER_SEQUENCING_T1 0x67 +#define CH7017_POWER_SEQUENCING_T2 0x68 +#define CH7017_POWER_SEQUENCING_T3 0x69 +#define CH7017_POWER_SEQUENCING_T4 0x6a +#define CH7017_POWER_SEQUENCING_T5 0x6b +#define CH7017_GPIO_DRIVER_TYPE 0x6c +#define CH7017_GPIO_DATA 0x6d +#define CH7017_GPIO_DIRECTION_CONTROL 0x6e + +#define CH7017_LVDS_PLL_FEEDBACK_DIV 0x71 +# define CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT 4 +# define CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT 0 +# define CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED 0x80 + +#define CH7017_LVDS_PLL_VCO_CONTROL 0x72 +# define CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED 0x80 +# define CH7017_LVDS_PLL_VCO_SHIFT 4 +# define CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT 0 + +#define CH7017_OUTPUTS_ENABLE 0x73 +# define CH7017_CHARGE_PUMP_LOW 0x0 +# define CH7017_CHARGE_PUMP_HIGH 0x3 +# define CH7017_LVDS_CHANNEL_A (1 << 3) +# define CH7017_LVDS_CHANNEL_B (1 << 4) +# define CH7017_TV_DAC_A (1 << 5) +# define CH7017_TV_DAC_B (1 << 6) +# define CH7017_DDC_SELECT_DC2 (1 << 7) + +#define CH7017_LVDS_OUTPUT_AMPLITUDE 0x74 +#define CH7017_LVDS_PLL_EMI_REDUCTION 0x75 +#define CH7017_LVDS_POWER_DOWN_FLICKER 0x76 + +#define CH7017_LVDS_CONTROL_2 0x78 +# define CH7017_LOOP_FILTER_SHIFT 5 +# define CH7017_PHASE_DETECTOR_SHIFT 0 + +#define CH7017_BANG_LIMIT_CONTROL 0x7f + +struct ch7017_priv { + uint8_t save_hapi; + uint8_t save_vali; + uint8_t save_valo; + uint8_t save_ailo; + uint8_t save_lvds_pll_vco; + uint8_t save_feedback_div; + uint8_t save_lvds_control_2; + uint8_t save_outputs_enable; + uint8_t save_lvds_power_down; + uint8_t save_power_management; +}; + +static void ch7017_dump_regs(struct intel_dvo_device *dvo); +static void ch7017_dpms(struct intel_dvo_device *dvo, int mode); + +static bool ch7017_read(struct intel_dvo_device *dvo, int addr, uint8_t *val) +{ + struct intel_i2c_chan *i2cbus = dvo->i2c_bus; + u8 out_buf[2]; + u8 in_buf[2]; + + struct i2c_msg msgs[] = { + { + .addr = i2cbus->slave_addr, + .flags = 0, + .len = 1, + .buf = out_buf, + }, + { + .addr = i2cbus->slave_addr, + .flags = I2C_M_RD, + .len = 1, + .buf = in_buf, + } + }; + + out_buf[0] = addr; + out_buf[1] = 0; + + if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) { + *val= in_buf[0]; + return true; + }; + + return false; +} + +static bool ch7017_write(struct intel_dvo_device *dvo, int addr, uint8_t val) +{ + struct intel_i2c_chan *i2cbus = dvo->i2c_bus; + uint8_t out_buf[2]; + struct i2c_msg msg = { + .addr = i2cbus->slave_addr, + .flags = 0, + .len = 2, + .buf = out_buf, + }; + + out_buf[0] = addr; + out_buf[1] = val; + + if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1) + return true; + + return false; +} + +/** Probes for a CH7017 on the given bus and slave address. */ +static bool ch7017_init(struct intel_dvo_device *dvo, + struct intel_i2c_chan *i2cbus) +{ + struct ch7017_priv *priv; + uint8_t val; + + priv = kzalloc(sizeof(struct ch7017_priv), GFP_KERNEL); + if (priv == NULL) + return false; + + dvo->i2c_bus = i2cbus; + dvo->i2c_bus->slave_addr = dvo->slave_addr; + dvo->dev_priv = priv; + + if (!ch7017_read(dvo, CH7017_DEVICE_ID, &val)) + goto fail; + + if (val != CH7017_DEVICE_ID_VALUE && + val != CH7018_DEVICE_ID_VALUE && + val != CH7019_DEVICE_ID_VALUE) { + DRM_DEBUG("ch701x not detected, got %d: from %s Slave %d.\n", + val, i2cbus->adapter.name,i2cbus->slave_addr); + goto fail; + } + + return true; +fail: + kfree(priv); + return false; +} + +static enum drm_connector_status ch7017_detect(struct intel_dvo_device *dvo) +{ + return connector_status_unknown; +} + +static enum drm_mode_status ch7017_mode_valid(struct intel_dvo_device *dvo, + struct drm_display_mode *mode) +{ + if (mode->clock > 160000) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + +static void ch7017_mode_set(struct intel_dvo_device *dvo, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + uint8_t lvds_pll_feedback_div, lvds_pll_vco_control; + uint8_t outputs_enable, lvds_control_2, lvds_power_down; + uint8_t horizontal_active_pixel_input; + uint8_t horizontal_active_pixel_output, vertical_active_line_output; + uint8_t active_input_line_output; + + DRM_DEBUG("Registers before mode setting\n"); + ch7017_dump_regs(dvo); + + /* LVDS PLL settings from page 75 of 7017-7017ds.pdf*/ + if (mode->clock < 100000) { + outputs_enable = CH7017_LVDS_CHANNEL_A | CH7017_CHARGE_PUMP_LOW; + lvds_pll_feedback_div = CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED | + (2 << CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT) | + (13 << CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT); + lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED | + (2 << CH7017_LVDS_PLL_VCO_SHIFT) | + (3 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT); + lvds_control_2 = (1 << CH7017_LOOP_FILTER_SHIFT) | + (0 << CH7017_PHASE_DETECTOR_SHIFT); + } else { + outputs_enable = CH7017_LVDS_CHANNEL_A | CH7017_CHARGE_PUMP_HIGH; + lvds_pll_feedback_div = CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED | + (2 << CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT) | + (3 << CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT); + lvds_pll_feedback_div = 35; + lvds_control_2 = (3 << CH7017_LOOP_FILTER_SHIFT) | + (0 << CH7017_PHASE_DETECTOR_SHIFT); + if (1) { /* XXX: dual channel panel detection. Assume yes for now. */ + outputs_enable |= CH7017_LVDS_CHANNEL_B; + lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED | + (2 << CH7017_LVDS_PLL_VCO_SHIFT) | + (13 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT); + } else { + lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED | + (1 << CH7017_LVDS_PLL_VCO_SHIFT) | + (13 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT); + } + } + + horizontal_active_pixel_input = mode->hdisplay & 0x00ff; + + vertical_active_line_output = mode->vdisplay & 0x00ff; + horizontal_active_pixel_output = mode->hdisplay & 0x00ff; + + active_input_line_output = ((mode->hdisplay & 0x0700) >> 8) | + (((mode->vdisplay & 0x0700) >> 8) << 3); + + lvds_power_down = CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED | + (mode->hdisplay & 0x0700) >> 8; + + ch7017_dpms(dvo, DRM_MODE_DPMS_OFF); + ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT, + horizontal_active_pixel_input); + ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT, + horizontal_active_pixel_output); + ch7017_write(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT, + vertical_active_line_output); + ch7017_write(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT, + active_input_line_output); + ch7017_write(dvo, CH7017_LVDS_PLL_VCO_CONTROL, lvds_pll_vco_control); + ch7017_write(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, lvds_pll_feedback_div); + ch7017_write(dvo, CH7017_LVDS_CONTROL_2, lvds_control_2); + ch7017_write(dvo, CH7017_OUTPUTS_ENABLE, outputs_enable); + + /* Turn the LVDS back on with new settings. */ + ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, lvds_power_down); + + DRM_DEBUG("Registers after mode setting\n"); + ch7017_dump_regs(dvo); +} + +/* set the CH7017 power state */ +static void ch7017_dpms(struct intel_dvo_device *dvo, int mode) +{ + uint8_t val; + + ch7017_read(dvo, CH7017_LVDS_POWER_DOWN, &val); + + /* Turn off TV/VGA, and never turn it on since we don't support it. */ + ch7017_write(dvo, CH7017_POWER_MANAGEMENT, + CH7017_DAC0_POWER_DOWN | + CH7017_DAC1_POWER_DOWN | + CH7017_DAC2_POWER_DOWN | + CH7017_DAC3_POWER_DOWN | + CH7017_TV_POWER_DOWN_EN); + + if (mode == DRM_MODE_DPMS_ON) { + /* Turn on the LVDS */ + ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, + val & ~CH7017_LVDS_POWER_DOWN_EN); + } else { + /* Turn off the LVDS */ + ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, + val | CH7017_LVDS_POWER_DOWN_EN); + } + + /* XXX: Should actually wait for update power status somehow */ + udelay(20000); +} + +static void ch7017_dump_regs(struct intel_dvo_device *dvo) +{ + uint8_t val; + +#define DUMP(reg) \ +do { \ + ch7017_read(dvo, reg, &val); \ + DRM_DEBUG(#reg ": %02x\n", val); \ +} while (0) + + DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT); + DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT); + DUMP(CH7017_VERTICAL_ACTIVE_LINE_OUTPUT); + DUMP(CH7017_ACTIVE_INPUT_LINE_OUTPUT); + DUMP(CH7017_LVDS_PLL_VCO_CONTROL); + DUMP(CH7017_LVDS_PLL_FEEDBACK_DIV); + DUMP(CH7017_LVDS_CONTROL_2); + DUMP(CH7017_OUTPUTS_ENABLE); + DUMP(CH7017_LVDS_POWER_DOWN); +} + +static void ch7017_save(struct intel_dvo_device *dvo) +{ + struct ch7017_priv *priv = dvo->dev_priv; + + ch7017_read(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT, &priv->save_hapi); + ch7017_read(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT, &priv->save_valo); + ch7017_read(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT, &priv->save_ailo); + ch7017_read(dvo, CH7017_LVDS_PLL_VCO_CONTROL, &priv->save_lvds_pll_vco); + ch7017_read(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, &priv->save_feedback_div); + ch7017_read(dvo, CH7017_LVDS_CONTROL_2, &priv->save_lvds_control_2); + ch7017_read(dvo, CH7017_OUTPUTS_ENABLE, &priv->save_outputs_enable); + ch7017_read(dvo, CH7017_LVDS_POWER_DOWN, &priv->save_lvds_power_down); + ch7017_read(dvo, CH7017_POWER_MANAGEMENT, &priv->save_power_management); +} + +static void ch7017_restore(struct intel_dvo_device *dvo) +{ + struct ch7017_priv *priv = dvo->dev_priv; + + /* Power down before changing mode */ + ch7017_dpms(dvo, DRM_MODE_DPMS_OFF); + + ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT, priv->save_hapi); + ch7017_write(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT, priv->save_valo); + ch7017_write(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT, priv->save_ailo); + ch7017_write(dvo, CH7017_LVDS_PLL_VCO_CONTROL, priv->save_lvds_pll_vco); + ch7017_write(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, priv->save_feedback_div); + ch7017_write(dvo, CH7017_LVDS_CONTROL_2, priv->save_lvds_control_2); + ch7017_write(dvo, CH7017_OUTPUTS_ENABLE, priv->save_outputs_enable); + ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, priv->save_lvds_power_down); + ch7017_write(dvo, CH7017_POWER_MANAGEMENT, priv->save_power_management); +} + +static void ch7017_destroy(struct intel_dvo_device *dvo) +{ + struct ch7017_priv *priv = dvo->dev_priv; + + if (priv) { + kfree(priv); + dvo->dev_priv = NULL; + } +} + +struct intel_dvo_dev_ops ch7017_ops = { + .init = ch7017_init, + .detect = ch7017_detect, + .mode_valid = ch7017_mode_valid, + .mode_set = ch7017_mode_set, + .dpms = ch7017_dpms, + .dump_regs = ch7017_dump_regs, + .save = ch7017_save, + .restore = ch7017_restore, + .destroy = ch7017_destroy, +}; diff --git a/linux-core/dvo_ch7xxx.c b/linux-core/dvo_ch7xxx.c new file mode 100644 index 00000000..77c86395 --- /dev/null +++ b/linux-core/dvo_ch7xxx.c @@ -0,0 +1,368 @@ +/************************************************************************** + +Copyright © 2006 Dave Airlie + +All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sub license, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice (including the +next paragraph) shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +**************************************************************************/ + +#include "dvo.h" + +#define CH7xxx_REG_VID 0x4a +#define CH7xxx_REG_DID 0x4b + +#define CH7011_VID 0x83 /* 7010 as well */ +#define CH7009A_VID 0x84 +#define CH7009B_VID 0x85 +#define CH7301_VID 0x95 + +#define CH7xxx_VID 0x84 +#define CH7xxx_DID 0x17 + +#define CH7xxx_NUM_REGS 0x4c + +#define CH7xxx_CM 0x1c +#define CH7xxx_CM_XCM (1<<0) +#define CH7xxx_CM_MCP (1<<2) +#define CH7xxx_INPUT_CLOCK 0x1d +#define CH7xxx_GPIO 0x1e +#define CH7xxx_GPIO_HPIR (1<<3) +#define CH7xxx_IDF 0x1f + +#define CH7xxx_IDF_HSP (1<<3) +#define CH7xxx_IDF_VSP (1<<4) + +#define CH7xxx_CONNECTION_DETECT 0x20 +#define CH7xxx_CDET_DVI (1<<5) + +#define CH7301_DAC_CNTL 0x21 +#define CH7301_HOTPLUG 0x23 +#define CH7xxx_TCTL 0x31 +#define CH7xxx_TVCO 0x32 +#define CH7xxx_TPCP 0x33 +#define CH7xxx_TPD 0x34 +#define CH7xxx_TPVT 0x35 +#define CH7xxx_TLPF 0x36 +#define CH7xxx_TCT 0x37 +#define CH7301_TEST_PATTERN 0x48 + +#define CH7xxx_PM 0x49 +#define CH7xxx_PM_FPD (1<<0) +#define CH7301_PM_DACPD0 (1<<1) +#define CH7301_PM_DACPD1 (1<<2) +#define CH7301_PM_DACPD2 (1<<3) +#define CH7xxx_PM_DVIL (1<<6) +#define CH7xxx_PM_DVIP (1<<7) + +#define CH7301_SYNC_POLARITY 0x56 +#define CH7301_SYNC_RGB_YUV (1<<0) +#define CH7301_SYNC_POL_DVI (1<<5) + +/** @file + * driver for the Chrontel 7xxx DVI chip over DVO. + */ + +static struct ch7xxx_id_struct { + uint8_t vid; + char *name; +} ch7xxx_ids[] = { + { CH7011_VID, "CH7011" }, + { CH7009A_VID, "CH7009A" }, + { CH7009B_VID, "CH7009B" }, + { CH7301_VID, "CH7301" }, +}; + +struct ch7xxx_reg_state { + uint8_t regs[CH7xxx_NUM_REGS]; +}; + +struct ch7xxx_priv { + bool quiet; + + struct ch7xxx_reg_state save_reg; + struct ch7xxx_reg_state mode_reg; + uint8_t save_TCTL, save_TPCP, save_TPD, save_TPVT; + uint8_t save_TLPF, save_TCT, save_PM, save_IDF; +}; + +static void ch7xxx_save(struct intel_dvo_device *dvo); + +static char *ch7xxx_get_id(uint8_t vid) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ch7xxx_ids); i++) { + if (ch7xxx_ids[i].vid == vid) + return ch7xxx_ids[i].name; + } + + return NULL; +} + +/** Reads an 8 bit register */ +static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch) +{ + struct ch7xxx_priv *ch7xxx= dvo->dev_priv; + struct intel_i2c_chan *i2cbus = dvo->i2c_bus; + u8 out_buf[2]; + u8 in_buf[2]; + + struct i2c_msg msgs[] = { + { + .addr = i2cbus->slave_addr, + .flags = 0, + .len = 1, + .buf = out_buf, + }, + { + .addr = i2cbus->slave_addr, + .flags = I2C_M_RD, + .len = 1, + .buf = in_buf, + } + }; + + out_buf[0] = addr; + out_buf[1] = 0; + + if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) { + *ch = in_buf[0]; + return true; + }; + + if (!ch7xxx->quiet) { + DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n", + addr, i2cbus->adapter.name, i2cbus->slave_addr); + } + return false; +} + +/** Writes an 8 bit register */ +static bool ch7xxx_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch) +{ + struct ch7xxx_priv *ch7xxx = dvo->dev_priv; + struct intel_i2c_chan *i2cbus = dvo->i2c_bus; + uint8_t out_buf[2]; + struct i2c_msg msg = { + .addr = i2cbus->slave_addr, + .flags = 0, + .len = 2, + .buf = out_buf, + }; + + out_buf[0] = addr; + out_buf[1] = ch; + + if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1) + return true; + + if (!ch7xxx->quiet) { + DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n", + addr, i2cbus->adapter.name, i2cbus->slave_addr); + } + + return false; +} + +static bool ch7xxx_init(struct intel_dvo_device *dvo, + struct intel_i2c_chan *i2cbus) +{ + /* this will detect the CH7xxx chip on the specified i2c bus */ + struct ch7xxx_priv *ch7xxx; + uint8_t vendor, device; + char *name; + + ch7xxx = kzalloc(sizeof(struct ch7xxx_priv), GFP_KERNEL); + if (ch7xxx == NULL) + return false; + + dvo->i2c_bus = i2cbus; + dvo->i2c_bus->slave_addr = dvo->slave_addr; + dvo->dev_priv = ch7xxx; + ch7xxx->quiet = true; + + if (!ch7xxx_readb(dvo, CH7xxx_REG_VID, &vendor)) + goto out; + + name = ch7xxx_get_id(vendor); + if (!name) { + DRM_DEBUG("ch7xxx not detected; got 0x%02x from %s slave %d.\n", + vendor, i2cbus->adapter.name, i2cbus->slave_addr); + goto out; + } + + + if (!ch7xxx_readb(dvo, CH7xxx_REG_DID, &device)) + goto out; + + if (device != CH7xxx_DID) { + DRM_DEBUG("ch7xxx not detected; got 0x%02x from %s slave %d.\n", + vendor, i2cbus->adapter.name, i2cbus->slave_addr); + goto out; + } + + ch7xxx->quiet = false; + DRM_DEBUG("Detected %s chipset, vendor/device ID 0x%02x/0x%02x\n", + name, vendor, device); + return true; +out: + kfree(ch7xxx); + return false; +} + +static enum drm_connector_status ch7xxx_detect(struct intel_dvo_device *dvo) +{ + uint8_t cdet, orig_pm, pm; + + ch7xxx_readb(dvo, CH7xxx_PM, &orig_pm); + + pm = orig_pm; + pm &= ~CH7xxx_PM_FPD; + pm |= CH7xxx_PM_DVIL | CH7xxx_PM_DVIP; + + ch7xxx_writeb(dvo, CH7xxx_PM, pm); + + ch7xxx_readb(dvo, CH7xxx_CONNECTION_DETECT, &cdet); + + ch7xxx_writeb(dvo, CH7xxx_PM, orig_pm); + + if (cdet & CH7xxx_CDET_DVI) + return connector_status_connected; + return connector_status_disconnected; +} + +static enum drm_mode_status ch7xxx_mode_valid(struct intel_dvo_device *dvo, + struct drm_display_mode *mode) +{ + if (mode->clock > 165000) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + +static void ch7xxx_mode_set(struct intel_dvo_device *dvo, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + uint8_t tvco, tpcp, tpd, tlpf, idf; + + if (mode->clock <= 65000) { + tvco = 0x23; + tpcp = 0x08; + tpd = 0x16; + tlpf = 0x60; + } else { + tvco = 0x2d; + tpcp = 0x06; + tpd = 0x26; + tlpf = 0xa0; + } + + ch7xxx_writeb(dvo, CH7xxx_TCTL, 0x00); + ch7xxx_writeb(dvo, CH7xxx_TVCO, tvco); + ch7xxx_writeb(dvo, CH7xxx_TPCP, tpcp); + ch7xxx_writeb(dvo, CH7xxx_TPD, tpd); + ch7xxx_writeb(dvo, CH7xxx_TPVT, 0x30); + ch7xxx_writeb(dvo, CH7xxx_TLPF, tlpf); + ch7xxx_writeb(dvo, CH7xxx_TCT, 0x00); + + ch7xxx_readb(dvo, CH7xxx_IDF, &idf); + + idf &= ~(CH7xxx_IDF_HSP | CH7xxx_IDF_VSP); + if (mode->flags & DRM_MODE_FLAG_PHSYNC) + idf |= CH7xxx_IDF_HSP; + + if (mode->flags & DRM_MODE_FLAG_PVSYNC) + idf |= CH7xxx_IDF_HSP; + + ch7xxx_writeb(dvo, CH7xxx_IDF, idf); +} + +/* set the CH7xxx power state */ +static void ch7xxx_dpms(struct intel_dvo_device *dvo, int mode) +{ + if (mode == DRM_MODE_DPMS_ON) + ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_DVIL | CH7xxx_PM_DVIP); + else + ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_FPD); +} + +static void ch7xxx_dump_regs(struct intel_dvo_device *dvo) +{ + struct ch7xxx_priv *ch7xxx = dvo->dev_priv; + int i; + + for (i = 0; i < CH7xxx_NUM_REGS; i++) { + if ((i % 8) == 0 ) + DRM_DEBUG("\n %02X: ", i); + DRM_DEBUG("%02X ", ch7xxx->mode_reg.regs[i]); + } +} + +static void ch7xxx_save(struct intel_dvo_device *dvo) +{ + struct ch7xxx_priv *ch7xxx= dvo->dev_priv; + + ch7xxx_readb(dvo, CH7xxx_TCTL, &ch7xxx->save_TCTL); + ch7xxx_readb(dvo, CH7xxx_TPCP, &ch7xxx->save_TPCP); + ch7xxx_readb(dvo, CH7xxx_TPD, &ch7xxx->save_TPD); + ch7xxx_readb(dvo, CH7xxx_TPVT, &ch7xxx->save_TPVT); + ch7xxx_readb(dvo, CH7xxx_TLPF, &ch7xxx->save_TLPF); + ch7xxx_readb(dvo, CH7xxx_PM, &ch7xxx->save_PM); + ch7xxx_readb(dvo, CH7xxx_IDF, &ch7xxx->save_IDF); +} + +static void ch7xxx_restore(struct intel_dvo_device *dvo) +{ + struct ch7xxx_priv *ch7xxx = dvo->dev_priv; + + ch7xxx_writeb(dvo, CH7xxx_TCTL, ch7xxx->save_TCTL); + ch7xxx_writeb(dvo, CH7xxx_TPCP, ch7xxx->save_TPCP); + ch7xxx_writeb(dvo, CH7xxx_TPD, ch7xxx->save_TPD); + ch7xxx_writeb(dvo, CH7xxx_TPVT, ch7xxx->save_TPVT); + ch7xxx_writeb(dvo, CH7xxx_TLPF, ch7xxx->save_TLPF); + ch7xxx_writeb(dvo, CH7xxx_IDF, ch7xxx->save_IDF); + ch7xxx_writeb(dvo, CH7xxx_PM, ch7xxx->save_PM); +} + +static void ch7xxx_destroy(struct intel_dvo_device *dvo) +{ + struct ch7xxx_priv *ch7xxx = dvo->dev_priv; + + if (ch7xxx) { + kfree(ch7xxx); + dvo->dev_priv = NULL; + } +} + +struct intel_dvo_dev_ops ch7xxx_ops = { + .init = ch7xxx_init, + .detect = ch7xxx_detect, + .mode_valid = ch7xxx_mode_valid, + .mode_set = ch7xxx_mode_set, + .dpms = ch7xxx_dpms, + .dump_regs = ch7xxx_dump_regs, + .save = ch7xxx_save, + .restore = ch7xxx_restore, + .destroy = ch7xxx_destroy, +}; diff --git a/linux-core/dvo_ivch.c b/linux-core/dvo_ivch.c new file mode 100644 index 00000000..788b0721 --- /dev/null +++ b/linux-core/dvo_ivch.c @@ -0,0 +1,463 @@ +/* + * Copyright © 2006 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * + */ + +#include "dvo.h" + +/* + * register definitions for the i82807aa. + * + * Documentation on this chipset can be found in datasheet #29069001 at + * intel.com. + */ + +/* + * VCH Revision & GMBus Base Addr + */ +#define VR00 0x00 +# define VR00_BASE_ADDRESS_MASK 0x007f + +/* + * Functionality Enable + */ +#define VR01 0x01 + +/* + * Enable the panel fitter + */ +# define VR01_PANEL_FIT_ENABLE (1 << 3) +/* + * Enables the LCD display. + * + * This must not be set while VR01_DVO_BYPASS_ENABLE is set. + */ +# define VR01_LCD_ENABLE (1 << 2) +/** Enables the DVO repeater. */ +# define VR01_DVO_BYPASS_ENABLE (1 << 1) +/** Enables the DVO clock */ +# define VR01_DVO_ENABLE (1 << 0) + +/* + * LCD Interface Format + */ +#define VR10 0x10 +/** Enables LVDS output instead of CMOS */ +# define VR10_LVDS_ENABLE (1 << 4) +/** Enables 18-bit LVDS output. */ +# define VR10_INTERFACE_1X18 (0 << 2) +/** Enables 24-bit LVDS or CMOS output */ +# define VR10_INTERFACE_1X24 (1 << 2) +/** Enables 2x18-bit LVDS or CMOS output. */ +# define VR10_INTERFACE_2X18 (2 << 2) +/** Enables 2x24-bit LVDS output */ +# define VR10_INTERFACE_2X24 (3 << 2) + +/* + * VR20 LCD Horizontal Display Size + */ +#define VR20 0x20 + +/* + * LCD Vertical Display Size + */ +#define VR21 0x20 + +/* + * Panel power down status + */ +#define VR30 0x30 +/** Read only bit indicating that the panel is not in a safe poweroff state. */ +# define VR30_PANEL_ON (1 << 15) + +#define VR40 0x40 +# define VR40_STALL_ENABLE (1 << 13) +# define VR40_VERTICAL_INTERP_ENABLE (1 << 12) +# define VR40_ENHANCED_PANEL_FITTING (1 << 11) +# define VR40_HORIZONTAL_INTERP_ENABLE (1 << 10) +# define VR40_AUTO_RATIO_ENABLE (1 << 9) +# define VR40_CLOCK_GATING_ENABLE (1 << 8) + +/* + * Panel Fitting Vertical Ratio + * (((image_height - 1) << 16) / ((panel_height - 1))) >> 2 + */ +#define VR41 0x41 + +/* + * Panel Fitting Horizontal Ratio + * (((image_width - 1) << 16) / ((panel_width - 1))) >> 2 + */ +#define VR42 0x42 + +/* + * Horizontal Image Size + */ +#define VR43 0x43 + +/* VR80 GPIO 0 + */ +#define VR80 0x80 +#define VR81 0x81 +#define VR82 0x82 +#define VR83 0x83 +#define VR84 0x84 +#define VR85 0x85 +#define VR86 0x86 +#define VR87 0x87 + +/* VR88 GPIO 8 + */ +#define VR88 0x88 + +/* Graphics BIOS scratch 0 + */ +#define VR8E 0x8E +# define VR8E_PANEL_TYPE_MASK (0xf << 0) +# define VR8E_PANEL_INTERFACE_CMOS (0 << 4) +# define VR8E_PANEL_INTERFACE_LVDS (1 << 4) +# define VR8E_FORCE_DEFAULT_PANEL (1 << 5) + +/* Graphics BIOS scratch 1 + */ +#define VR8F 0x8F +# define VR8F_VCH_PRESENT (1 << 0) +# define VR8F_DISPLAY_CONN (1 << 1) +# define VR8F_POWER_MASK (0x3c) +# define VR8F_POWER_POS (2) + + +struct ivch_priv { + bool quiet; + + uint16_t width, height; + + uint16_t save_VR01; + uint16_t save_VR40; +}; + +#if 0 +struct vch_capabilities { + struct aimdb_block aimdb_block; + uint8_t panel_type; + uint8_t set_panel_type; + uint8_t slave_address; + uint8_t capabilities; +#define VCH_PANEL_FITTING_SUPPORT (0x3 << 0) +#define VCH_PANEL_FITTING_TEXT (1 << 2) +#define VCH_PANEL_FITTING_GRAPHICS (1 << 3) +#define VCH_PANEL_FITTING_RATIO (1 << 4) +#define VCH_DITHERING (1 << 5) + uint8_t backlight_gpio; + uint8_t set_panel_type_us_gpios; +} __attribute__ ((packed)); +#endif + +static void ivch_dump_regs(struct intel_dvo_device *dvo); + +/** + * Reads a register on the ivch. + * + * Each of the 256 registers are 16 bits long. + */ +static bool ivch_read(struct intel_dvo_device *dvo, int addr, uint16_t *data) +{ + struct ivch_priv *priv = dvo->dev_priv; + struct intel_i2c_chan *i2cbus = dvo->i2c_bus; + u8 out_buf[1]; + u8 in_buf[2]; + + struct i2c_msg msgs[] = { + { + .addr = i2cbus->slave_addr, + .flags = I2C_M_RD, + .len = 0, + }, + { + .addr = 0, + .flags = I2C_M_NOSTART, + .len = 1, + .buf = out_buf, + }, + { + .addr = i2cbus->slave_addr, + .flags = I2C_M_RD | I2C_M_NOSTART, + .len = 2, + .buf = in_buf, + } + }; + + out_buf[0] = addr; + + if (i2c_transfer(&i2cbus->adapter, msgs, 3) == 3) { + *data = (in_buf[1] << 8) | in_buf[0]; + return true; + }; + + if (!priv->quiet) { + DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n", + addr, i2cbus->adapter.name, i2cbus->slave_addr); + } + return false; +} + +/** Writes a 16-bit register on the ivch */ +static bool ivch_write(struct intel_dvo_device *dvo, int addr, uint16_t data) +{ + struct ivch_priv *priv = dvo->dev_priv; + struct intel_i2c_chan *i2cbus = dvo->i2c_bus; + u8 out_buf[3]; + struct i2c_msg msg = { + .addr = i2cbus->slave_addr, + .flags = 0, + .len = 3, + .buf = out_buf, + }; + + out_buf[0] = addr; + out_buf[1] = data & 0xff; + out_buf[2] = data >> 8; + + if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1) + return true; + + if (!priv->quiet) { + DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n", + addr, i2cbus->adapter.name, i2cbus->slave_addr); + } + + return false; +} + +/** Probes the given bus and slave address for an ivch */ +static bool ivch_init(struct intel_dvo_device *dvo, + struct intel_i2c_chan *i2cbus) +{ + struct ivch_priv *priv; + uint16_t temp; + + priv = kzalloc(sizeof(struct ivch_priv), GFP_KERNEL); + if (priv == NULL) + return false; + + dvo->i2c_bus = i2cbus; + dvo->i2c_bus->slave_addr = dvo->slave_addr; + dvo->dev_priv = priv; + priv->quiet = true; + + if (!ivch_read(dvo, VR00, &temp)) + goto out; + priv->quiet = false; + + /* Since the identification bits are probably zeroes, which doesn't seem + * very unique, check that the value in the base address field matches + * the address it's responding on. + */ + if ((temp & VR00_BASE_ADDRESS_MASK) != dvo->slave_addr) { + DRM_DEBUG("ivch detect failed due to address mismatch " + "(%d vs %d)\n", + (temp & VR00_BASE_ADDRESS_MASK), dvo->slave_addr); + goto out; + } + +#if 0 + if (!xf86I2CDevInit(&priv->d)) { + goto out; + } +#endif + ivch_read(dvo, VR20, &priv->width); + ivch_read(dvo, VR21, &priv->height); + + return true; + +out: + kfree(priv); + return false; +} + +static enum drm_connector_status ivch_detect(struct intel_dvo_device *dvo) +{ + return connector_status_connected; +} + +static enum drm_mode_status ivch_mode_valid(struct intel_dvo_device *dvo, + struct drm_display_mode *mode) +{ + if (mode->clock > 112000) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + +/** Sets the power state of the panel connected to the ivch */ +static void ivch_dpms(struct intel_dvo_device *dvo, int mode) +{ + int i; + uint16_t vr01, vr30, backlight; + + /* Set the new power state of the panel. */ + if (!ivch_read(dvo, VR01, &vr01)) + return; + + if (mode == DRM_MODE_DPMS_ON) + backlight = 1; + else + backlight = 0; + ivch_write(dvo, VR80, backlight); + + if (mode == DRM_MODE_DPMS_ON) + vr01 |= VR01_LCD_ENABLE | VR01_DVO_ENABLE; + else + vr01 &= ~(VR01_LCD_ENABLE | VR01_DVO_ENABLE); + + ivch_write(dvo, VR01, vr01); + + /* Wait for the panel to make its state transition */ + for (i = 0; i < 100; i++) { + if (!ivch_read(dvo, VR30, &vr30)) + break; + + if (((vr30 & VR30_PANEL_ON) != 0) == (mode == DRM_MODE_DPMS_ON)) + break; + udelay(1000); + } + /* wait some more; vch may fail to resync sometimes without this */ + udelay(16 * 1000); +} + +static void ivch_mode_set(struct intel_dvo_device *dvo, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + uint16_t vr40 = 0; + uint16_t vr01; + + vr01 = 0; + vr40 = (VR40_STALL_ENABLE | VR40_VERTICAL_INTERP_ENABLE | + VR40_HORIZONTAL_INTERP_ENABLE); + + if (mode->hdisplay != adjusted_mode->hdisplay || + mode->vdisplay != adjusted_mode->vdisplay) { + uint16_t x_ratio, y_ratio; + + vr01 |= VR01_PANEL_FIT_ENABLE; + vr40 |= VR40_CLOCK_GATING_ENABLE; + x_ratio = (((mode->hdisplay - 1) << 16) / + (adjusted_mode->hdisplay - 1)) >> 2; + y_ratio = (((mode->vdisplay - 1) << 16) / + (adjusted_mode->vdisplay - 1)) >> 2; + ivch_write (dvo, VR42, x_ratio); + ivch_write (dvo, VR41, y_ratio); + } else { + vr01 &= ~VR01_PANEL_FIT_ENABLE; + vr40 &= ~VR40_CLOCK_GATING_ENABLE; + } + vr40 &= ~VR40_AUTO_RATIO_ENABLE; + + ivch_write(dvo, VR01, vr01); + ivch_write(dvo, VR40, vr40); + + ivch_dump_regs(dvo); +} + +static void ivch_dump_regs(struct intel_dvo_device *dvo) +{ + uint16_t val; + + ivch_read(dvo, VR00, &val); + DRM_DEBUG("VR00: 0x%04x\n", val); + ivch_read(dvo, VR01, &val); + DRM_DEBUG("VR01: 0x%04x\n", val); + ivch_read(dvo, VR30, &val); + DRM_DEBUG("VR30: 0x%04x\n", val); + ivch_read(dvo, VR40, &val); + DRM_DEBUG("VR40: 0x%04x\n", val); + + /* GPIO registers */ + ivch_read(dvo, VR80, &val); + DRM_DEBUG("VR80: 0x%04x\n", val); + ivch_read(dvo, VR81, &val); + DRM_DEBUG("VR81: 0x%04x\n", val); + ivch_read(dvo, VR82, &val); + DRM_DEBUG("VR82: 0x%04x\n", val); + ivch_read(dvo, VR83, &val); + DRM_DEBUG("VR83: 0x%04x\n", val); + ivch_read(dvo, VR84, &val); + DRM_DEBUG("VR84: 0x%04x\n", val); + ivch_read(dvo, VR85, &val); + DRM_DEBUG("VR85: 0x%04x\n", val); + ivch_read(dvo, VR86, &val); + DRM_DEBUG("VR86: 0x%04x\n", val); + ivch_read(dvo, VR87, &val); + DRM_DEBUG("VR87: 0x%04x\n", val); + ivch_read(dvo, VR88, &val); + DRM_DEBUG("VR88: 0x%04x\n", val); + + /* Scratch register 0 - AIM Panel type */ + ivch_read(dvo, VR8E, &val); + DRM_DEBUG("VR8E: 0x%04x\n", val); + + /* Scratch register 1 - Status register */ + ivch_read(dvo, VR8F, &val); + DRM_DEBUG("VR8F: 0x%04x\n", val); +} + +static void ivch_save(struct intel_dvo_device *dvo) +{ + struct ivch_priv *priv = dvo->dev_priv; + + ivch_read(dvo, VR01, &priv->save_VR01); + ivch_read(dvo, VR40, &priv->save_VR40); +} + +static void ivch_restore(struct intel_dvo_device *dvo) +{ + struct ivch_priv *priv = dvo->dev_priv; + + ivch_write(dvo, VR01, priv->save_VR01); + ivch_write(dvo, VR40, priv->save_VR40); +} + +static void ivch_destroy(struct intel_dvo_device *dvo) +{ + struct ivch_priv *priv = dvo->dev_priv; + + if (priv) { + kfree(priv); + dvo->dev_priv = NULL; + } +} + +struct intel_dvo_dev_ops ivch_ops= { + .init = ivch_init, + .dpms = ivch_dpms, + .save = ivch_save, + .restore = ivch_restore, + .mode_valid = ivch_mode_valid, + .mode_set = ivch_mode_set, + .detect = ivch_detect, + .dump_regs = ivch_dump_regs, + .destroy = ivch_destroy, +}; diff --git a/linux-core/dvo_sil164.c b/linux-core/dvo_sil164.c new file mode 100644 index 00000000..033a4bb0 --- /dev/null +++ b/linux-core/dvo_sil164.c @@ -0,0 +1,302 @@ +/************************************************************************** + +Copyright © 2006 Dave Airlie + +All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sub license, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice (including the +next paragraph) shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +**************************************************************************/ + +#include "dvo.h" + +#define SIL164_VID 0x0001 +#define SIL164_DID 0x0006 + +#define SIL164_VID_LO 0x00 +#define SIL164_VID_HI 0x01 +#define SIL164_DID_LO 0x02 +#define SIL164_DID_HI 0x03 +#define SIL164_REV 0x04 +#define SIL164_RSVD 0x05 +#define SIL164_FREQ_LO 0x06 +#define SIL164_FREQ_HI 0x07 + +#define SIL164_REG8 0x08 +#define SIL164_8_VEN (1<<5) +#define SIL164_8_HEN (1<<4) +#define SIL164_8_DSEL (1<<3) +#define SIL164_8_BSEL (1<<2) +#define SIL164_8_EDGE (1<<1) +#define SIL164_8_PD (1<<0) + +#define SIL164_REG9 0x09 +#define SIL164_9_VLOW (1<<7) +#define SIL164_9_MSEL_MASK (0x7<<4) +#define SIL164_9_TSEL (1<<3) +#define SIL164_9_RSEN (1<<2) +#define SIL164_9_HTPLG (1<<1) +#define SIL164_9_MDI (1<<0) + +#define SIL164_REGC 0x0c + +struct sil164_save_rec { + uint8_t reg8; + uint8_t reg9; + uint8_t regc; +}; + +struct sil164_priv { + //I2CDevRec d; + bool quiet; + struct sil164_save_rec save_regs; + struct sil164_save_rec mode_regs; +}; + +#define SILPTR(d) ((SIL164Ptr)(d->DriverPrivate.ptr)) + +static bool sil164_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch) +{ + struct sil164_priv *sil = dvo->dev_priv; + struct intel_i2c_chan *i2cbus = dvo->i2c_bus; + u8 out_buf[2]; + u8 in_buf[2]; + + struct i2c_msg msgs[] = { + { + .addr = i2cbus->slave_addr, + .flags = 0, + .len = 1, + .buf = out_buf, + }, + { + .addr = i2cbus->slave_addr, + .flags = I2C_M_RD, + .len = 1, + .buf = in_buf, + } + }; + + out_buf[0] = addr; + out_buf[1] = 0; + + if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) { + *ch = in_buf[0]; + return true; + }; + + if (!sil->quiet) { + DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n", + addr, i2cbus->adapter.name, i2cbus->slave_addr); + } + return false; +} + +static bool sil164_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch) +{ + struct sil164_priv *sil= dvo->dev_priv; + struct intel_i2c_chan *i2cbus = dvo->i2c_bus; + uint8_t out_buf[2]; + struct i2c_msg msg = { + .addr = i2cbus->slave_addr, + .flags = 0, + .len = 2, + .buf = out_buf, + }; + + out_buf[0] = addr; + out_buf[1] = ch; + + if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1) + return true; + + if (!sil->quiet) { + DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n", + addr, i2cbus->adapter.name, i2cbus->slave_addr); + } + + return false; +} + +/* Silicon Image 164 driver for chip on i2c bus */ +static bool sil164_init(struct intel_dvo_device *dvo, + struct intel_i2c_chan *i2cbus) +{ + /* this will detect the SIL164 chip on the specified i2c bus */ + struct sil164_priv *sil; + unsigned char ch; + + sil = kzalloc(sizeof(struct sil164_priv), GFP_KERNEL); + if (sil == NULL) + return false; + + dvo->i2c_bus = i2cbus; + dvo->i2c_bus->slave_addr = dvo->slave_addr; + dvo->dev_priv = sil; + sil->quiet = true; + + if (!sil164_readb(dvo, SIL164_VID_LO, &ch)) + goto out; + + if (ch != (SIL164_VID & 0xff)) { + DRM_DEBUG("sil164 not detected got %d: from %s Slave %d.\n", + ch, i2cbus->adapter.name, i2cbus->slave_addr); + goto out; + } + + if (!sil164_readb(dvo, SIL164_DID_LO, &ch)) + goto out; + + if (ch != (SIL164_DID & 0xff)) { + DRM_DEBUG("sil164 not detected got %d: from %s Slave %d.\n", + ch, i2cbus->adapter.name, i2cbus->slave_addr); + goto out; + } + sil->quiet = false; + + DRM_DEBUG("init sil164 dvo controller successfully!\n"); + return true; + +out: + kfree(sil); + return false; +} + +static enum drm_connector_status sil164_detect(struct intel_dvo_device *dvo) +{ + uint8_t reg9; + + sil164_readb(dvo, SIL164_REG9, ®9); + + if (reg9 & SIL164_9_HTPLG) + return connector_status_connected; + else + return connector_status_disconnected; +} + +static enum drm_mode_status sil164_mode_valid(struct intel_dvo_device *dvo, + struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static void sil164_mode_set(struct intel_dvo_device *dvo, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + /* As long as the basics are set up, since we don't have clock + * dependencies in the mode setup, we can just leave the + * registers alone and everything will work fine. + */ + /* recommended programming sequence from doc */ + /*sil164_writeb(sil, 0x08, 0x30); + sil164_writeb(sil, 0x09, 0x00); + sil164_writeb(sil, 0x0a, 0x90); + sil164_writeb(sil, 0x0c, 0x89); + sil164_writeb(sil, 0x08, 0x31);*/ + /* don't do much */ + return; +} + +/* set the SIL164 power state */ +static void sil164_dpms(struct intel_dvo_device *dvo, int mode) +{ + int ret; + unsigned char ch; + + ret = sil164_readb(dvo, SIL164_REG8, &ch); + if (ret == false) + return; + + if (mode == DRM_MODE_DPMS_ON) + ch |= SIL164_8_PD; + else + ch &= ~SIL164_8_PD; + + sil164_writeb(dvo, SIL164_REG8, ch); + return; +} + +static void sil164_dump_regs(struct intel_dvo_device *dvo) +{ + uint8_t val; + + sil164_readb(dvo, SIL164_FREQ_LO, &val); + DRM_DEBUG("SIL164_FREQ_LO: 0x%02x\n", val); + sil164_readb(dvo, SIL164_FREQ_HI, &val); + DRM_DEBUG("SIL164_FREQ_HI: 0x%02x\n", val); + sil164_readb(dvo, SIL164_REG8, &val); + DRM_DEBUG("SIL164_REG8: 0x%02x\n", val); + sil164_readb(dvo, SIL164_REG9, &val); + DRM_DEBUG("SIL164_REG9: 0x%02x\n", val); + sil164_readb(dvo, SIL164_REGC, &val); + DRM_DEBUG("SIL164_REGC: 0x%02x\n", val); +} + +static void sil164_save(struct intel_dvo_device *dvo) +{ + struct sil164_priv *sil= dvo->dev_priv; + + if (!sil164_readb(dvo, SIL164_REG8, &sil->save_regs.reg8)) + return; + + if (!sil164_readb(dvo, SIL164_REG9, &sil->save_regs.reg9)) + return; + + if (!sil164_readb(dvo, SIL164_REGC, &sil->save_regs.regc)) + return; + + return; +} + +static void sil164_restore(struct intel_dvo_device *dvo) +{ + struct sil164_priv *sil = dvo->dev_priv; + + /* Restore it powered down initially */ + sil164_writeb(dvo, SIL164_REG8, sil->save_regs.reg8 & ~0x1); + + sil164_writeb(dvo, SIL164_REG9, sil->save_regs.reg9); + sil164_writeb(dvo, SIL164_REGC, sil->save_regs.regc); + sil164_writeb(dvo, SIL164_REG8, sil->save_regs.reg8); +} + +static void sil164_destroy(struct intel_dvo_device *dvo) +{ + struct sil164_priv *sil = dvo->dev_priv; + + if (sil) { + kfree(sil); + dvo->dev_priv = NULL; + } +} + +struct intel_dvo_dev_ops sil164_ops = { + .init = sil164_init, + .detect = sil164_detect, + .mode_valid = sil164_mode_valid, + .mode_set = sil164_mode_set, + .dpms = sil164_dpms, + .dump_regs = sil164_dump_regs, + .save = sil164_save, + .restore = sil164_restore, + .destroy = sil164_destroy, +}; diff --git a/linux-core/dvo_tfp410.c b/linux-core/dvo_tfp410.c new file mode 100644 index 00000000..207fda80 --- /dev/null +++ b/linux-core/dvo_tfp410.c @@ -0,0 +1,335 @@ +/* + * Copyright © 2007 Dave Mueller + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dave Mueller <dave.mueller@gmx.ch> + * + */ + +#include "dvo.h" + +/* register definitions according to the TFP410 data sheet */ +#define TFP410_VID 0x014C +#define TFP410_DID 0x0410 + +#define TFP410_VID_LO 0x00 +#define TFP410_VID_HI 0x01 +#define TFP410_DID_LO 0x02 +#define TFP410_DID_HI 0x03 +#define TFP410_REV 0x04 + +#define TFP410_CTL_1 0x08 +#define TFP410_CTL_1_TDIS (1<<6) +#define TFP410_CTL_1_VEN (1<<5) +#define TFP410_CTL_1_HEN (1<<4) +#define TFP410_CTL_1_DSEL (1<<3) +#define TFP410_CTL_1_BSEL (1<<2) +#define TFP410_CTL_1_EDGE (1<<1) +#define TFP410_CTL_1_PD (1<<0) + +#define TFP410_CTL_2 0x09 +#define TFP410_CTL_2_VLOW (1<<7) +#define TFP410_CTL_2_MSEL_MASK (0x7<<4) +#define TFP410_CTL_2_MSEL (1<<4) +#define TFP410_CTL_2_TSEL (1<<3) +#define TFP410_CTL_2_RSEN (1<<2) +#define TFP410_CTL_2_HTPLG (1<<1) +#define TFP410_CTL_2_MDI (1<<0) + +#define TFP410_CTL_3 0x0A +#define TFP410_CTL_3_DK_MASK (0x7<<5) +#define TFP410_CTL_3_DK (1<<5) +#define TFP410_CTL_3_DKEN (1<<4) +#define TFP410_CTL_3_CTL_MASK (0x7<<1) +#define TFP410_CTL_3_CTL (1<<1) + +#define TFP410_USERCFG 0x0B + +#define TFP410_DE_DLY 0x32 + +#define TFP410_DE_CTL 0x33 +#define TFP410_DE_CTL_DEGEN (1<<6) +#define TFP410_DE_CTL_VSPOL (1<<5) +#define TFP410_DE_CTL_HSPOL (1<<4) +#define TFP410_DE_CTL_DEDLY8 (1<<0) + +#define TFP410_DE_TOP 0x34 + +#define TFP410_DE_CNT_LO 0x36 +#define TFP410_DE_CNT_HI 0x37 + +#define TFP410_DE_LIN_LO 0x38 +#define TFP410_DE_LIN_HI 0x39 + +#define TFP410_H_RES_LO 0x3A +#define TFP410_H_RES_HI 0x3B + +#define TFP410_V_RES_LO 0x3C +#define TFP410_V_RES_HI 0x3D + +struct tfp410_save_rec { + uint8_t ctl1; + uint8_t ctl2; +}; + +struct tfp410_priv { + bool quiet; + + struct tfp410_save_rec saved_reg; + struct tfp410_save_rec mode_reg; +}; + +static bool tfp410_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch) +{ + struct tfp410_priv *tfp = dvo->dev_priv; + struct intel_i2c_chan *i2cbus = dvo->i2c_bus; + u8 out_buf[2]; + u8 in_buf[2]; + + struct i2c_msg msgs[] = { + { + .addr = i2cbus->slave_addr, + .flags = 0, + .len = 1, + .buf = out_buf, + }, + { + .addr = i2cbus->slave_addr, + .flags = I2C_M_RD, + .len = 1, + .buf = in_buf, + } + }; + + out_buf[0] = addr; + out_buf[1] = 0; + + if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) { + *ch = in_buf[0]; + return true; + }; + + if (!tfp->quiet) { + DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n", + addr, i2cbus->adapter.name, i2cbus->slave_addr); + } + return false; +} + +static bool tfp410_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch) +{ + struct tfp410_priv *tfp = dvo->dev_priv; + struct intel_i2c_chan *i2cbus = dvo->i2c_bus; + uint8_t out_buf[2]; + struct i2c_msg msg = { + .addr = i2cbus->slave_addr, + .flags = 0, + .len = 2, + .buf = out_buf, + }; + + out_buf[0] = addr; + out_buf[1] = ch; + + if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1) + return true; + + if (!tfp->quiet) { + DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n", + addr, i2cbus->adapter.name, i2cbus->slave_addr); + } + + return false; +} + +static int tfp410_getid(struct intel_dvo_device *dvo, int addr) +{ + uint8_t ch1, ch2; + + if (tfp410_readb(dvo, addr+0, &ch1) && + tfp410_readb(dvo, addr+1, &ch2)) + return ((ch2 << 8) & 0xFF00) | (ch1 & 0x00FF); + + return -1; +} + +/* Ti TFP410 driver for chip on i2c bus */ +static bool tfp410_init(struct intel_dvo_device *dvo, + struct intel_i2c_chan *i2cbus) +{ + /* this will detect the tfp410 chip on the specified i2c bus */ + struct tfp410_priv *tfp; + int id; + + tfp = kzalloc(sizeof(struct tfp410_priv), GFP_KERNEL); + if (tfp == NULL) + return false; + + dvo->i2c_bus = i2cbus; + dvo->i2c_bus->slave_addr = dvo->slave_addr; + dvo->dev_priv = tfp; + tfp->quiet = true; + + if ((id = tfp410_getid(dvo, TFP410_VID_LO)) != TFP410_VID) { + DRM_DEBUG("tfp410 not detected got VID %X: from %s Slave %d.\n", + id, i2cbus->adapter.name, i2cbus->slave_addr); + goto out; + } + + if ((id = tfp410_getid(dvo, TFP410_DID_LO)) != TFP410_DID) { + DRM_DEBUG("tfp410 not detected got DID %X: from %s Slave %d.\n", + id, i2cbus->adapter.name, i2cbus->slave_addr); + goto out; + } + tfp->quiet = false; + return true; +out: + kfree(tfp); + return false; +} + +static enum drm_connector_status tfp410_detect(struct intel_dvo_device *dvo) +{ + enum drm_connector_status ret = connector_status_disconnected; + uint8_t ctl2; + + if (tfp410_readb(dvo, TFP410_CTL_2, &ctl2)) { + if (ctl2 & TFP410_CTL_2_HTPLG) + ret = connector_status_connected; + else + ret = connector_status_disconnected; + } + + return ret; +} + +static enum drm_mode_status tfp410_mode_valid(struct intel_dvo_device *dvo, + struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static void tfp410_mode_set(struct intel_dvo_device *dvo, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + /* As long as the basics are set up, since we don't have clock dependencies + * in the mode setup, we can just leave the registers alone and everything + * will work fine. + */ + /* don't do much */ + return; +} + +/* set the tfp410 power state */ +static void tfp410_dpms(struct intel_dvo_device *dvo, int mode) +{ + uint8_t ctl1; + + if (!tfp410_readb(dvo, TFP410_CTL_1, &ctl1)) + return; + + if (mode == DRM_MODE_DPMS_ON) + ctl1 |= TFP410_CTL_1_PD; + else + ctl1 &= ~TFP410_CTL_1_PD; + + tfp410_writeb(dvo, TFP410_CTL_1, ctl1); +} + +static void tfp410_dump_regs(struct intel_dvo_device *dvo) +{ + uint8_t val, val2; + + tfp410_readb(dvo, TFP410_REV, &val); + DRM_DEBUG("TFP410_REV: 0x%02X\n", val); + tfp410_readb(dvo, TFP410_CTL_1, &val); + DRM_DEBUG("TFP410_CTL1: 0x%02X\n", val); + tfp410_readb(dvo, TFP410_CTL_2, &val); + DRM_DEBUG("TFP410_CTL2: 0x%02X\n", val); + tfp410_readb(dvo, TFP410_CTL_3, &val); + DRM_DEBUG("TFP410_CTL3: 0x%02X\n", val); + tfp410_readb(dvo, TFP410_USERCFG, &val); + DRM_DEBUG("TFP410_USERCFG: 0x%02X\n", val); + tfp410_readb(dvo, TFP410_DE_DLY, &val); + DRM_DEBUG("TFP410_DE_DLY: 0x%02X\n", val); + tfp410_readb(dvo, TFP410_DE_CTL, &val); + DRM_DEBUG("TFP410_DE_CTL: 0x%02X\n", val); + tfp410_readb(dvo, TFP410_DE_TOP, &val); + DRM_DEBUG("TFP410_DE_TOP: 0x%02X\n", val); + tfp410_readb(dvo, TFP410_DE_CNT_LO, &val); + tfp410_readb(dvo, TFP410_DE_CNT_HI, &val2); + DRM_DEBUG("TFP410_DE_CNT: 0x%02X%02X\n", val2, val); + tfp410_readb(dvo, TFP410_DE_LIN_LO, &val); + tfp410_readb(dvo, TFP410_DE_LIN_HI, &val2); + DRM_DEBUG("TFP410_DE_LIN: 0x%02X%02X\n", val2, val); + tfp410_readb(dvo, TFP410_H_RES_LO, &val); + tfp410_readb(dvo, TFP410_H_RES_HI, &val2); + DRM_DEBUG("TFP410_H_RES: 0x%02X%02X\n", val2, val); + tfp410_readb(dvo, TFP410_V_RES_LO, &val); + tfp410_readb(dvo, TFP410_V_RES_HI, &val2); + DRM_DEBUG("TFP410_V_RES: 0x%02X%02X\n", val2, val); +} + +static void tfp410_save(struct intel_dvo_device *dvo) +{ + struct tfp410_priv *tfp = dvo->dev_priv; + + if (!tfp410_readb(dvo, TFP410_CTL_1, &tfp->saved_reg.ctl1)) + return; + + if (!tfp410_readb(dvo, TFP410_CTL_2, &tfp->saved_reg.ctl2)) + return; +} + +static void tfp410_restore(struct intel_dvo_device *dvo) +{ + struct tfp410_priv *tfp = dvo->dev_priv; + + /* Restore it powered down initially */ + tfp410_writeb(dvo, TFP410_CTL_1, tfp->saved_reg.ctl1 & ~0x1); + + tfp410_writeb(dvo, TFP410_CTL_2, tfp->saved_reg.ctl2); + tfp410_writeb(dvo, TFP410_CTL_1, tfp->saved_reg.ctl1); +} + +static void tfp410_destroy(struct intel_dvo_device *dvo) +{ + struct tfp410_priv *tfp = dvo->dev_priv; + + if (tfp) { + kfree(tfp); + dvo->dev_priv = NULL; + } +} + +struct intel_dvo_dev_ops tfp410_ops = { + .init = tfp410_init, + .detect = tfp410_detect, + .mode_valid = tfp410_mode_valid, + .mode_set = tfp410_mode_set, + .dpms = tfp410_dpms, + .dump_regs = tfp410_dump_regs, + .save = tfp410_save, + .restore = tfp410_restore, + .destroy = tfp410_destroy, +}; diff --git a/linux-core/i915_buffer.c b/linux-core/i915_buffer.c deleted file mode 100644 index 8d991c42..00000000 --- a/linux-core/i915_buffer.c +++ /dev/null @@ -1,303 +0,0 @@ -/************************************************************************** - * - * 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 "i915_drm.h" -#include "i915_drv.h" - -struct drm_ttm_backend *i915_create_ttm_backend_entry(struct drm_device *dev) -{ - return drm_agp_init_ttm(dev); -} - -int i915_fence_type(struct drm_buffer_object *bo, - uint32_t *fclass, - uint32_t *type) -{ - if (bo->mem.proposed_flags & (DRM_BO_FLAG_READ | DRM_BO_FLAG_WRITE)) - *type = 3; - else - *type = 1; - return 0; -} - -int i915_invalidate_caches(struct drm_device *dev, uint64_t flags) -{ - /* - * FIXME: Only emit once per batchbuffer submission. - */ - - uint32_t flush_cmd = MI_NO_WRITE_FLUSH; - - if (flags & DRM_BO_FLAG_READ) - flush_cmd |= MI_READ_FLUSH; - if (flags & DRM_BO_FLAG_EXE) - flush_cmd |= MI_EXE_FLUSH; - - return i915_emit_mi_flush(dev, flush_cmd); -} - -int i915_init_mem_type(struct drm_device *dev, uint32_t type, - struct drm_mem_type_manager *man) -{ - switch (type) { - case DRM_BO_MEM_LOCAL: - man->flags = _DRM_FLAG_MEMTYPE_MAPPABLE | - _DRM_FLAG_MEMTYPE_CACHED; - man->drm_bus_maptype = 0; - man->gpu_offset = 0; - break; - case DRM_BO_MEM_TT: - 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; - case DRM_BO_MEM_PRIV0: - 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_FIXED | _DRM_FLAG_NEEDS_IOREMAP; - man->drm_bus_maptype = _DRM_AGP; - man->gpu_offset = 0; - break; - default: - DRM_ERROR("Unsupported memory type %u\n", (unsigned)type); - return -EINVAL; - } - return 0; -} - -/* - * i915_evict_flags: - * - * @bo: the buffer object to be evicted - * - * Return the bo flags for a buffer which is not mapped to the hardware. - * These will be placed in proposed_flags so that when the move is - * finished, they'll end up in bo->mem.flags - */ -uint64_t i915_evict_flags(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; - default: - return DRM_BO_FLAG_MEM_TT | DRM_BO_FLAG_CACHED; - } -} - -#if 0 /* See comment below */ - -static void i915_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_i915_private_t *dev_priv = dev->dev_private; - RING_LOCALS; - - if (!dev_priv) - return; - - i915_kernel_lost_context(dev); - while (pages > 0) { - cur_pages = pages; - if (cur_pages > 2048) - cur_pages = 2048; - pages -= cur_pages; - - BEGIN_LP_RING(6); - OUT_RING(SRC_COPY_BLT_CMD | XY_SRC_COPY_BLT_WRITE_ALPHA | - XY_SRC_COPY_BLT_WRITE_RGB); - OUT_RING((stride & 0xffff) | (0xcc << 16) | (1 << 24) | - (1 << 25) | (direction ? (1 << 30) : 0)); - OUT_RING((cur_pages << 16) | PAGE_SIZE); - OUT_RING(dst_offset); - OUT_RING(stride & 0xffff); - OUT_RING(src_offset); - ADVANCE_LP_RING(); - } - return; -} - -static int i915_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; - } - - i915_emit_copy_blit(bo->dev, - old_mem->mm_node->start << PAGE_SHIFT, - new_mem->mm_node->start << PAGE_SHIFT, - new_mem->num_pages, dir); - - i915_emit_mi_flush(bo->dev, MI_READ_FLUSH | MI_EXE_FLUSH); - - return drm_bo_move_accel_cleanup(bo, evict, no_wait, 0, - DRM_FENCE_TYPE_EXE | - DRM_I915_FENCE_TYPE_RW, - DRM_I915_FENCE_FLAG_FLUSHED, new_mem); -} - -/* - * Flip destination ttm into cached-coherent AGP, - * then blit and subsequently move out again. - */ - -static int i915_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 = i915_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; -} - -#endif - -/* - * Disable i915_move_flip for now, since we can't guarantee that the hardware - * lock is held here. To re-enable we need to make sure either - * a) The X server is using DRM to submit commands to the ring, or - * b) DRM can use the HP ring for these blits. This means i915 needs to - * implement a new ring submission mechanism and fence class. - */ -int i915_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 (1) /*i915_move_flip(bo, evict, no_wait, new_mem)*/ - return drm_bo_move_memcpy(bo, evict, no_wait, new_mem); - } else { - if (1) /*i915_move_blit(bo, evict, no_wait, new_mem)*/ - return drm_bo_move_memcpy(bo, evict, no_wait, new_mem); - } - return 0; -} - -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)) -static inline void clflush(volatile void *__p) -{ - asm volatile("clflush %0" : "+m" (*(char __force *)__p)); -} -#endif - -static inline void drm_cache_flush_addr(void *virt) -{ - int i; - - for (i = 0; i < PAGE_SIZE; i += boot_cpu_data.x86_clflush_size) - clflush(virt+i); -} - -static inline void drm_cache_flush_page(struct page *p) -{ - drm_cache_flush_addr(page_address(p)); -} - -void i915_flush_ttm(struct drm_ttm *ttm) -{ - int i; - - if (!ttm) - return; - - DRM_MEMORYBARRIER(); - -#ifdef CONFIG_X86_32 - /* Hopefully nobody has built an x86-64 processor without clflush */ - if (!cpu_has_clflush) { - wbinvd(); - DRM_MEMORYBARRIER(); - return; - } -#endif - - for (i = ttm->num_pages - 1; i >= 0; i--) - drm_cache_flush_page(drm_ttm_get_page(ttm, i)); - - DRM_MEMORYBARRIER(); -} diff --git a/linux-core/i915_drv.c b/linux-core/i915_drv.c index 28923201..38859474 100644 --- a/linux-core/i915_drv.c +++ b/linux-core/i915_drv.c @@ -30,6 +30,7 @@ #include "drmP.h" #include "drm.h" #include "i915_drm.h" +#include "intel_drv.h" #include "i915_drv.h" #include "drm_pciids.h" @@ -38,30 +39,14 @@ static struct pci_device_id pciidlist[] = { i915_PCI_IDS }; -#ifdef I915_HAVE_FENCE -extern struct drm_fence_driver i915_fence_driver; -#endif +unsigned int i915_modeset = 0; +module_param_named(modeset, i915_modeset, int, 0400); -#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 struct drm_bo_driver i915_bo_driver = { - .mem_type_prio = i915_mem_prios, - .mem_busy_prio = i915_busy_prios, - .num_mem_type_prio = sizeof(i915_mem_prios)/sizeof(uint32_t), - .num_mem_busy_prio = sizeof(i915_busy_prios)/sizeof(uint32_t), - .create_ttm_backend_entry = i915_create_ttm_backend_entry, - .fence_type = i915_fence_type, - .invalidate_caches = i915_invalidate_caches, - .init_mem_type = i915_init_mem_type, - .evict_flags = i915_evict_flags, - .move = i915_move, - .ttm_cache_flush = i915_flush_ttm, - .command_stream_barrier = NULL, -}; -#endif +unsigned int i915_fbpercrtc = 0; +module_param_named(fbpercrtc, i915_fbpercrtc, int, 0400); + +unsigned int i915_rightof = 1; +module_param_named(i915_rightof, i915_rightof, int, 0400); static int i915_suspend(struct drm_device *dev, pm_message_t state) { @@ -140,6 +125,8 @@ static struct drm_driver driver = { .reclaim_buffers = drm_core_reclaim_buffers, .get_map_ofs = drm_core_get_map_ofs, .get_reg_ofs = drm_core_get_reg_ofs, + .master_create = i915_master_create, + .master_destroy = i915_master_destroy, .proc_init = i915_gem_proc_init, .proc_cleanup = i915_gem_proc_cleanup, .ioctls = i915_ioctls, @@ -163,12 +150,6 @@ static struct drm_driver driver = { .probe = probe, .remove = remove, }, -#ifdef I915_HAVE_FENCE - .fence_driver = &i915_fence_driver, -#endif -#ifdef I915_HAVE_BUFFER - .bo_driver = &i915_bo_driver, -#endif .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = DRIVER_DATE, @@ -198,14 +179,17 @@ static int probe(struct pci_dev *pdev, const struct pci_device_id *ent) } static void remove(struct pci_dev *pdev) { + drm_cleanup_pci(pdev); if (pdev->msi_enabled) pci_disable_msi(pdev); - drm_cleanup_pci(pdev); } static int __init i915_init(void) { driver.num_ioctls = i915_max_ioctl; + if (i915_modeset == 1) + driver.driver_features |= DRIVER_MODESET; + return drm_init(&driver, pciidlist); } diff --git a/linux-core/i915_execbuf.c b/linux-core/i915_execbuf.c deleted file mode 100644 index 804f3ac1..00000000 --- a/linux-core/i915_execbuf.c +++ /dev/null @@ -1,917 +0,0 @@ -/* - * Copyright 2003-2008 Tungsten Graphics, Inc., Cedar Park, Texas. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sub license, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. - * IN NO EVENT SHALL TUNGSTEN GRAPHICS 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: - * Thomas Hellstrom <thomas-at-tungstengraphics-dot-com> - * Dave Airlie - * Keith Packard - * ... ? - */ - -#include "drmP.h" -#include "drm.h" -#include "i915_drm.h" -#include "i915_drv.h" - -#if DRM_DEBUG_CODE -#define DRM_DEBUG_RELOCATION (drm_debug != 0) -#else -#define DRM_DEBUG_RELOCATION 0 -#endif - -enum i915_buf_idle { - I915_RELOC_UNCHECKED, - I915_RELOC_IDLE, - I915_RELOC_BUSY -}; - -struct i915_relocatee_info { - struct drm_buffer_object *buf; - unsigned long offset; - uint32_t *data_page; - unsigned page_offset; - struct drm_bo_kmap_obj kmap; - int is_iomem; - int dst; - int idle; - int performed_ring_relocs; -#ifdef DRM_KMAP_ATOMIC_PROT_PFN - unsigned long pfn; - pgprot_t pg_prot; -#endif -}; - -struct drm_i915_validate_buffer { - struct drm_buffer_object *buffer; - int presumed_offset_correct; - void __user *data; - int ret; - enum i915_buf_idle idle; -}; - -/* - * I'd like to use MI_STORE_DATA_IMM here, but I can't make - * it work. Seems like GART writes are broken with that - * instruction. Also I'm not sure that MI_FLUSH will - * act as a memory barrier for that instruction. It will - * for this single dword 2D blit. - */ - -static void i915_emit_ring_reloc(struct drm_device *dev, uint32_t offset, - uint32_t value) -{ - struct drm_i915_private *dev_priv = - (struct drm_i915_private *)dev->dev_private; - - RING_LOCALS; - i915_kernel_lost_context(dev); - BEGIN_LP_RING(6); - OUT_RING((0x02 << 29) | (0x40 << 22) | (0x3 << 20) | (0x3)); - OUT_RING((0x3 << 24) | (0xF0 << 16) | (0x40)); - OUT_RING((0x1 << 16) | (0x4)); - OUT_RING(offset); - OUT_RING(value); - OUT_RING(0); - ADVANCE_LP_RING(); -} - -static void i915_dereference_buffers_locked(struct drm_i915_validate_buffer - *buffers, unsigned num_buffers) -{ - while (num_buffers--) - drm_bo_usage_deref_locked(&buffers[num_buffers].buffer); -} - -int i915_apply_reloc(struct drm_file *file_priv, int num_buffers, - struct drm_i915_validate_buffer *buffers, - struct i915_relocatee_info *relocatee, uint32_t * reloc) -{ - unsigned index; - unsigned long new_cmd_offset; - u32 val; - int ret, i; - int buf_index = -1; - - /* - * FIXME: O(relocs * buffers) complexity. - */ - - for (i = 0; i <= num_buffers; i++) - if (buffers[i].buffer) - if (reloc[2] == buffers[i].buffer->base.hash.key) - buf_index = i; - - if (buf_index == -1) { - DRM_ERROR("Illegal relocation buffer %08X\n", reloc[2]); - return -EINVAL; - } - - /* - * Short-circuit relocations that were correctly - * guessed by the client - */ - if (buffers[buf_index].presumed_offset_correct && !DRM_DEBUG_RELOCATION) - return 0; - - new_cmd_offset = reloc[0]; - if (!relocatee->data_page || - !drm_bo_same_page(relocatee->offset, new_cmd_offset)) { - struct drm_bo_mem_reg *mem = &relocatee->buf->mem; - - drm_bo_kunmap(&relocatee->kmap); - relocatee->data_page = NULL; - relocatee->offset = new_cmd_offset; - - if (unlikely(relocatee->idle == I915_RELOC_UNCHECKED)) { - ret = drm_bo_wait(relocatee->buf, 0, 1, 0, 0); - if (ret) - return ret; - relocatee->idle = I915_RELOC_IDLE; - } - - if (unlikely((mem->mem_type != DRM_BO_MEM_LOCAL) && - (mem->flags & DRM_BO_FLAG_CACHED_MAPPED))) - drm_bo_evict_cached(relocatee->buf); - - ret = drm_bo_kmap(relocatee->buf, new_cmd_offset >> PAGE_SHIFT, - 1, &relocatee->kmap); - if (ret) { - DRM_ERROR - ("Could not map command buffer to apply relocs\n %08lx", - new_cmd_offset); - return ret; - } - relocatee->data_page = drm_bmo_virtual(&relocatee->kmap, - &relocatee->is_iomem); - relocatee->page_offset = (relocatee->offset & PAGE_MASK); - } - - val = buffers[buf_index].buffer->offset; - index = (reloc[0] - relocatee->page_offset) >> 2; - - /* add in validate */ - val = val + reloc[1]; - - if (DRM_DEBUG_RELOCATION) { - if (buffers[buf_index].presumed_offset_correct && - relocatee->data_page[index] != val) { - DRM_DEBUG - ("Relocation mismatch source %d target %d buffer %d user %08x kernel %08x\n", - reloc[0], reloc[1], buf_index, - relocatee->data_page[index], val); - } - } - - if (relocatee->is_iomem) - iowrite32(val, relocatee->data_page + index); - else - relocatee->data_page[index] = val; - return 0; -} - -int i915_process_relocs(struct drm_file *file_priv, - uint32_t buf_handle, - uint32_t __user ** reloc_user_ptr, - struct i915_relocatee_info *relocatee, - struct drm_i915_validate_buffer *buffers, - uint32_t num_buffers) -{ - int ret, reloc_stride; - uint32_t cur_offset; - uint32_t reloc_count; - uint32_t reloc_type; - uint32_t reloc_buf_size; - uint32_t *reloc_buf = NULL; - int i; - - /* do a copy from user from the user ptr */ - ret = get_user(reloc_count, *reloc_user_ptr); - if (ret) { - DRM_ERROR("Could not map relocation buffer.\n"); - goto out; - } - - ret = get_user(reloc_type, (*reloc_user_ptr) + 1); - if (ret) { - DRM_ERROR("Could not map relocation buffer.\n"); - goto out; - } - - if (reloc_type != 0) { - DRM_ERROR("Unsupported relocation type requested\n"); - ret = -EINVAL; - goto out; - } - - reloc_buf_size = - (I915_RELOC_HEADER + - (reloc_count * I915_RELOC0_STRIDE)) * sizeof(uint32_t); - reloc_buf = kmalloc(reloc_buf_size, GFP_KERNEL); - if (!reloc_buf) { - DRM_ERROR("Out of memory for reloc buffer\n"); - ret = -ENOMEM; - goto out; - } - - if (copy_from_user(reloc_buf, *reloc_user_ptr, reloc_buf_size)) { - ret = -EFAULT; - goto out; - } - - /* get next relocate buffer handle */ - *reloc_user_ptr = (uint32_t *) * (unsigned long *)&reloc_buf[2]; - - reloc_stride = I915_RELOC0_STRIDE * sizeof(uint32_t); /* may be different for other types of relocs */ - - DRM_DEBUG("num relocs is %d, next is %p\n", reloc_count, - *reloc_user_ptr); - - for (i = 0; i < reloc_count; i++) { - cur_offset = I915_RELOC_HEADER + (i * I915_RELOC0_STRIDE); - - ret = i915_apply_reloc(file_priv, num_buffers, buffers, - relocatee, reloc_buf + cur_offset); - if (ret) - goto out; - } - - out: - if (reloc_buf) - kfree(reloc_buf); - - if (relocatee->data_page) { - drm_bo_kunmap(&relocatee->kmap); - relocatee->data_page = NULL; - } - - return ret; -} - -static int i915_exec_reloc(struct drm_file *file_priv, drm_handle_t buf_handle, - uint32_t __user * reloc_user_ptr, - struct drm_i915_validate_buffer *buffers, - uint32_t buf_count) -{ - struct drm_device *dev = file_priv->minor->dev; - struct i915_relocatee_info relocatee; - int ret = 0; - int b; - - /* - * Short circuit relocations when all previous - * buffers offsets were correctly guessed by - * the client - */ - if (!DRM_DEBUG_RELOCATION) { - for (b = 0; b < buf_count; b++) - if (!buffers[b].presumed_offset_correct) - break; - - if (b == buf_count) - return 0; - } - - memset(&relocatee, 0, sizeof(relocatee)); - relocatee.idle = I915_RELOC_UNCHECKED; - - mutex_lock(&dev->struct_mutex); - relocatee.buf = drm_lookup_buffer_object(file_priv, buf_handle, 1); - mutex_unlock(&dev->struct_mutex); - if (!relocatee.buf) { - DRM_DEBUG("relocatee buffer invalid %08x\n", buf_handle); - ret = -EINVAL; - goto out_err; - } - - mutex_lock(&relocatee.buf->mutex); - while (reloc_user_ptr) { - ret = - i915_process_relocs(file_priv, buf_handle, &reloc_user_ptr, - &relocatee, buffers, buf_count); - if (ret) { - DRM_ERROR("process relocs failed\n"); - goto out_err1; - } - } - - out_err1: - mutex_unlock(&relocatee.buf->mutex); - drm_bo_usage_deref_unlocked(&relocatee.buf); - out_err: - return ret; -} - -static void i915_clear_relocatee(struct i915_relocatee_info *relocatee) -{ - if (relocatee->data_page) { -#ifndef DRM_KMAP_ATOMIC_PROT_PFN - drm_bo_kunmap(&relocatee->kmap); -#else - kunmap_atomic(relocatee->data_page, KM_USER0); -#endif - relocatee->data_page = NULL; - } - relocatee->buf = NULL; - relocatee->dst = ~0; -} - -static int i915_update_relocatee(struct i915_relocatee_info *relocatee, - struct drm_i915_validate_buffer *buffers, - unsigned int dst, unsigned long dst_offset) -{ - int ret; - - if (unlikely(dst != relocatee->dst || NULL == relocatee->buf)) { - i915_clear_relocatee(relocatee); - relocatee->dst = dst; - relocatee->buf = buffers[dst].buffer; - relocatee->idle = buffers[dst].idle; - - /* - * Check for buffer idle. If the buffer is busy, revert to - * ring relocations. - */ - - if (relocatee->idle == I915_RELOC_UNCHECKED) { - preempt_enable(); - mutex_lock(&relocatee->buf->mutex); - - ret = drm_bo_wait(relocatee->buf, 0, 1, 1, 0); - if (ret == 0) - relocatee->idle = I915_RELOC_IDLE; - else { - relocatee->idle = I915_RELOC_BUSY; - relocatee->performed_ring_relocs = 1; - } - mutex_unlock(&relocatee->buf->mutex); - preempt_disable(); - buffers[dst].idle = relocatee->idle; - } - } - - if (relocatee->idle == I915_RELOC_BUSY) - return 0; - - if (unlikely(dst_offset > relocatee->buf->num_pages * PAGE_SIZE)) { - DRM_ERROR("Relocation destination out of bounds.\n"); - return -EINVAL; - } - if (unlikely(!drm_bo_same_page(relocatee->page_offset, dst_offset) || - NULL == relocatee->data_page)) { -#ifdef DRM_KMAP_ATOMIC_PROT_PFN - if (NULL != relocatee->data_page) { - kunmap_atomic(relocatee->data_page, KM_USER0); - relocatee->data_page = NULL; - } - ret = drm_bo_pfn_prot(relocatee->buf, dst_offset, - &relocatee->pfn, &relocatee->pg_prot); - if (ret) { - DRM_ERROR("Can't map relocation destination.\n"); - return -EINVAL; - } - relocatee->data_page = - kmap_atomic_prot_pfn(relocatee->pfn, KM_USER0, - relocatee->pg_prot); -#else - if (NULL != relocatee->data_page) { - drm_bo_kunmap(&relocatee->kmap); - relocatee->data_page = NULL; - } - - ret = drm_bo_kmap(relocatee->buf, dst_offset >> PAGE_SHIFT, - 1, &relocatee->kmap); - if (ret) { - DRM_ERROR("Can't map relocation destination.\n"); - return ret; - } - - relocatee->data_page = drm_bmo_virtual(&relocatee->kmap, - &relocatee->is_iomem); -#endif - relocatee->page_offset = dst_offset & PAGE_MASK; - } - return 0; -} - -static int i915_apply_post_reloc(uint32_t reloc[], - struct drm_i915_validate_buffer *buffers, - uint32_t num_buffers, - struct i915_relocatee_info *relocatee) -{ - uint32_t reloc_buffer = reloc[2]; - uint32_t dst_buffer = reloc[3]; - uint32_t val; - uint32_t index; - int ret; - - if (likely(buffers[reloc_buffer].presumed_offset_correct)) - return 0; - if (unlikely(reloc_buffer >= num_buffers)) { - DRM_ERROR("Invalid reloc buffer index.\n"); - return -EINVAL; - } - if (unlikely(dst_buffer >= num_buffers)) { - DRM_ERROR("Invalid dest buffer index.\n"); - return -EINVAL; - } - - ret = i915_update_relocatee(relocatee, buffers, dst_buffer, reloc[0]); - if (unlikely(ret)) - return ret; - - val = buffers[reloc_buffer].buffer->offset; - index = (reloc[0] - relocatee->page_offset) >> 2; - val = val + reloc[1]; - - if (relocatee->idle == I915_RELOC_BUSY) { - i915_emit_ring_reloc(relocatee->buf->dev, - relocatee->buf->offset + reloc[0], val); - return 0; - } -#ifdef DRM_KMAP_ATOMIC_PROT_PFN - relocatee->data_page[index] = val; -#else - if (likely(relocatee->is_iomem)) - iowrite32(val, relocatee->data_page + index); - else - relocatee->data_page[index] = val; -#endif - - return 0; -} - -static int i915_post_relocs(struct drm_file *file_priv, - uint32_t __user * new_reloc_ptr, - struct drm_i915_validate_buffer *buffers, - unsigned int num_buffers) -{ - uint32_t *reloc; - uint32_t reloc_stride = I915_RELOC0_STRIDE * sizeof(uint32_t); - uint32_t header_size = I915_RELOC_HEADER * sizeof(uint32_t); - struct i915_relocatee_info relocatee; - uint32_t reloc_type; - uint32_t num_relocs; - uint32_t count; - int ret = 0; - int i; - int short_circuit = 1; - uint32_t __user *reloc_ptr; - uint64_t new_reloc_data; - uint32_t reloc_buf_size; - uint32_t *reloc_buf; - - for (i = 0; i < num_buffers; ++i) { - if (unlikely(!buffers[i].presumed_offset_correct)) { - short_circuit = 0; - break; - } - } - - if (likely(short_circuit)) - return 0; - - memset(&relocatee, 0, sizeof(relocatee)); - - while (new_reloc_ptr) { - reloc_ptr = new_reloc_ptr; - - ret = get_user(num_relocs, reloc_ptr); - if (unlikely(ret)) - goto out; - if (unlikely(!access_ok(VERIFY_READ, reloc_ptr, - header_size + - num_relocs * reloc_stride))) - return -EFAULT; - - ret = __get_user(reloc_type, reloc_ptr + 1); - if (unlikely(ret)) - goto out; - - if (unlikely(reloc_type != 1)) { - DRM_ERROR("Unsupported relocation type requested.\n"); - ret = -EINVAL; - goto out; - } - - ret = __get_user(new_reloc_data, reloc_ptr + 2); - new_reloc_ptr = (uint32_t __user *) (unsigned long) - new_reloc_data; - - reloc_ptr += I915_RELOC_HEADER; - - if (num_relocs == 0) - goto out; - - reloc_buf_size = - (num_relocs * I915_RELOC0_STRIDE) * sizeof(uint32_t); - reloc_buf = kmalloc(reloc_buf_size, GFP_KERNEL); - if (!reloc_buf) { - DRM_ERROR("Out of memory for reloc buffer\n"); - ret = -ENOMEM; - goto out; - } - - if (__copy_from_user(reloc_buf, reloc_ptr, reloc_buf_size)) { - ret = -EFAULT; - goto out; - } - reloc = reloc_buf; - preempt_disable(); - for (count = 0; count < num_relocs; ++count) { - ret = i915_apply_post_reloc(reloc, buffers, - num_buffers, &relocatee); - if (unlikely(ret)) { - preempt_enable(); - goto out; - } - reloc += I915_RELOC0_STRIDE; - } - preempt_enable(); - - if (reloc_buf) { - kfree(reloc_buf); - reloc_buf = NULL; - } - i915_clear_relocatee(&relocatee); - } - - out: - /* - * Flush ring relocs so the command parser will pick them up. - */ - - if (relocatee.performed_ring_relocs) - (void)i915_emit_mi_flush(file_priv->minor->dev, 0); - - i915_clear_relocatee(&relocatee); - if (reloc_buf) { - kfree(reloc_buf); - reloc_buf = NULL; - } - - return ret; -} - -static int i915_check_presumed(struct drm_i915_op_arg *arg, - struct drm_buffer_object *bo, - uint32_t __user * data, int *presumed_ok) -{ - struct drm_bo_op_req *req = &arg->d.req; - uint32_t hint_offset; - uint32_t hint = req->bo_req.hint; - - *presumed_ok = 0; - - if (!(hint & DRM_BO_HINT_PRESUMED_OFFSET)) - return 0; - if (bo->offset == req->bo_req.presumed_offset) { - *presumed_ok = 1; - return 0; - } - - /* - * We need to turn off the HINT_PRESUMED_OFFSET for this buffer in - * the user-space IOCTL argument list, since the buffer has moved, - * we're about to apply relocations and we might subsequently - * hit an -EAGAIN. In that case the argument list will be reused by - * user-space, but the presumed offset is no longer valid. - * - * Needless to say, this is a bit ugly. - */ - - hint_offset = (uint32_t *) & req->bo_req.hint - (uint32_t *) arg; - hint &= ~DRM_BO_HINT_PRESUMED_OFFSET; - return __put_user(hint, data + hint_offset); -} - -/* - * Validate, add fence and relocate a block of bos from a userspace list - */ -int i915_validate_buffer_list(struct drm_file *file_priv, - unsigned int fence_class, uint64_t data, - struct drm_i915_validate_buffer *buffers, - uint32_t * num_buffers, - uint32_t __user ** post_relocs) -{ - struct drm_i915_op_arg arg; - struct drm_bo_op_req *req = &arg.d.req; - int ret = 0; - unsigned buf_count = 0; - uint32_t buf_handle; - uint32_t __user *reloc_user_ptr; - struct drm_i915_validate_buffer *item = buffers; - *post_relocs = NULL; - - do { - if (buf_count >= *num_buffers) { - DRM_ERROR("Buffer count exceeded %d\n.", *num_buffers); - ret = -EINVAL; - goto out_err; - } - item = buffers + buf_count; - item->buffer = NULL; - item->presumed_offset_correct = 0; - item->idle = I915_RELOC_UNCHECKED; - - if (copy_from_user - (&arg, (void __user *)(unsigned long)data, sizeof(arg))) { - ret = -EFAULT; - goto out_err; - } - - ret = 0; - if (req->op != drm_bo_validate) { - DRM_ERROR - ("Buffer object operation wasn't \"validate\".\n"); - ret = -EINVAL; - goto out_err; - } - item->ret = 0; - item->data = (void __user *)(unsigned long)data; - - buf_handle = req->bo_req.handle; - reloc_user_ptr = (uint32_t *) (unsigned long)arg.reloc_ptr; - - /* - * Switch mode to post-validation relocations? - */ - - if (unlikely((buf_count == 0) && (*post_relocs == NULL) && - (reloc_user_ptr != NULL))) { - uint32_t reloc_type; - - ret = get_user(reloc_type, reloc_user_ptr + 1); - if (ret) - goto out_err; - - if (reloc_type == 1) - *post_relocs = reloc_user_ptr; - - } - - if ((*post_relocs == NULL) && (reloc_user_ptr != NULL)) { - ret = - i915_exec_reloc(file_priv, buf_handle, - reloc_user_ptr, buffers, buf_count); - if (ret) - goto out_err; - DRM_MEMORYBARRIER(); - } - - ret = drm_bo_handle_validate(file_priv, req->bo_req.handle, - req->bo_req.flags, - req->bo_req.mask, req->bo_req.hint, - req->bo_req.fence_class, - NULL, &item->buffer); - if (ret) { - DRM_ERROR("error on handle validate %d\n", ret); - goto out_err; - } - - buf_count++; - - ret = i915_check_presumed(&arg, item->buffer, - (uint32_t __user *) - (unsigned long)data, - &item->presumed_offset_correct); - if (ret) - goto out_err; - - data = arg.next; - } while (data != 0); - out_err: - *num_buffers = buf_count; - item->ret = (ret != -EAGAIN) ? ret : 0; - return ret; -} - -/* - * Remove all buffers from the unfenced list. - * If the execbuffer operation was aborted, for example due to a signal, - * this also make sure that buffers retain their original state and - * fence pointers. - * Copy back buffer information to user-space unless we were interrupted - * by a signal. In which case the IOCTL must be rerun. - */ - -static int i915_handle_copyback(struct drm_device *dev, - struct drm_i915_validate_buffer *buffers, - unsigned int num_buffers, int ret) -{ - int err = ret; - int i; - struct drm_i915_op_arg arg; - struct drm_buffer_object *bo; - - if (ret) - drm_putback_buffer_objects(dev); - - if (ret != -EAGAIN) { - for (i = 0; i < num_buffers; ++i) { - arg.handled = 1; - arg.d.rep.ret = buffers->ret; - bo = buffers->buffer; - mutex_lock(&bo->mutex); - drm_bo_fill_rep_arg(bo, &arg.d.rep.bo_info); - mutex_unlock(&bo->mutex); - if (__copy_to_user(buffers->data, &arg, sizeof(arg))) - err = -EFAULT; - buffers++; - } - } - - return err; -} - -/* - * Create a fence object, and if that fails, pretend that everything is - * OK and just idle the GPU. - */ - -void i915_fence_or_sync(struct drm_file *file_priv, - uint32_t fence_flags, - struct drm_fence_arg *fence_arg, - struct drm_fence_object **fence_p) -{ - struct drm_device *dev = file_priv->minor->dev; - int ret; - struct drm_fence_object *fence; - - ret = drm_fence_buffer_objects(dev, NULL, fence_flags, NULL, &fence); - - if (ret) { - - /* - * Fence creation failed. - * Fall back to synchronous operation and idle the engine. - */ - - (void)i915_emit_mi_flush(dev, MI_READ_FLUSH); - (void)i915_quiescent(dev); - - if (!(fence_flags & DRM_FENCE_FLAG_NO_USER)) { - - /* - * Communicate to user-space that - * fence creation has failed and that - * the engine is idle. - */ - - fence_arg->handle = ~0; - fence_arg->error = ret; - } - drm_putback_buffer_objects(dev); - if (fence_p) - *fence_p = NULL; - return; - } - - if (!(fence_flags & DRM_FENCE_FLAG_NO_USER)) { - - ret = drm_fence_add_user_object(file_priv, fence, - fence_flags & - DRM_FENCE_FLAG_SHAREABLE); - if (!ret) - drm_fence_fill_arg(fence, fence_arg); - else { - /* - * Fence user object creation failed. - * We must idle the engine here as well, as user- - * space expects a fence object to wait on. Since we - * have a fence object we wait for it to signal - * to indicate engine "sufficiently" idle. - */ - - (void)drm_fence_object_wait(fence, 0, 1, fence->type); - drm_fence_usage_deref_unlocked(&fence); - fence_arg->handle = ~0; - fence_arg->error = ret; - } - } - - if (fence_p) - *fence_p = fence; - else if (fence) - drm_fence_usage_deref_unlocked(&fence); -} - -int i915_execbuffer(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *) - dev_priv->sarea_priv; - struct drm_i915_execbuffer *exec_buf = data; - struct drm_i915_batchbuffer *batch = &exec_buf->batch; - struct drm_fence_arg *fence_arg = &exec_buf->fence_arg; - int num_buffers; - int ret; - uint32_t __user *post_relocs; - - if (!dev_priv->allow_batchbuffer) { - DRM_ERROR("Batchbuffer ioctl disabled\n"); - return -EINVAL; - } - - if (batch->num_cliprects && DRM_VERIFYAREA_READ(batch->cliprects, - batch->num_cliprects * - sizeof(struct - drm_clip_rect))) - return -EFAULT; - - if (exec_buf->num_buffers > dev_priv->max_validate_buffers) - return -EINVAL; - - ret = drm_bo_read_lock(&dev->bm.bm_lock, 1); - if (ret) - return ret; - - /* - * The cmdbuf_mutex makes sure the validate-submit-fence - * operation is atomic. - */ - - ret = mutex_lock_interruptible(&dev_priv->cmdbuf_mutex); - if (ret) { - drm_bo_read_unlock(&dev->bm.bm_lock); - return -EAGAIN; - } - - num_buffers = exec_buf->num_buffers; - - if (!dev_priv->val_bufs) { - dev_priv->val_bufs = - vmalloc(sizeof(struct drm_i915_validate_buffer) * - dev_priv->max_validate_buffers); - } - if (!dev_priv->val_bufs) { - drm_bo_read_unlock(&dev->bm.bm_lock); - mutex_unlock(&dev_priv->cmdbuf_mutex); - return -ENOMEM; - } - - /* validate buffer list + fixup relocations */ - ret = i915_validate_buffer_list(file_priv, 0, exec_buf->ops_list, - dev_priv->val_bufs, &num_buffers, - &post_relocs); - if (ret) - goto out_err0; - - if (post_relocs) { - ret = i915_post_relocs(file_priv, post_relocs, - dev_priv->val_bufs, num_buffers); - if (ret) - goto out_err0; - } - - /* make sure all previous memory operations have passed */ - DRM_MEMORYBARRIER(); - - if (!post_relocs) { - drm_agp_chipset_flush(dev); - batch->start = - dev_priv->val_bufs[num_buffers - 1].buffer->offset; - } else { - batch->start += dev_priv->val_bufs[0].buffer->offset; - } - - DRM_DEBUG("i915 exec batchbuffer, start %x used %d cliprects %d\n", - batch->start, batch->used, batch->num_cliprects); - - ret = i915_dispatch_batchbuffer(dev, batch); - if (ret) - goto out_err0; - if (sarea_priv) - sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); - i915_fence_or_sync(file_priv, fence_arg->flags, fence_arg, NULL); - - out_err0: - ret = i915_handle_copyback(dev, dev_priv->val_bufs, num_buffers, ret); - mutex_lock(&dev->struct_mutex); - i915_dereference_buffers_locked(dev_priv->val_bufs, num_buffers); - mutex_unlock(&dev->struct_mutex); - mutex_unlock(&dev_priv->cmdbuf_mutex); - drm_bo_read_unlock(&dev->bm.bm_lock); - return ret; -} diff --git a/linux-core/i915_fence.c b/linux-core/i915_fence.c deleted file mode 100644 index 45613c3a..00000000 --- a/linux-core/i915_fence.c +++ /dev/null @@ -1,273 +0,0 @@ -/************************************************************************** - * - * 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 "i915_drm.h" -#include "i915_drv.h" - -/* - * Initiate a sync flush if it's not already pending. - */ - -static inline void i915_initiate_rwflush(struct drm_i915_private *dev_priv, - struct drm_fence_class_manager *fc) -{ - if ((fc->pending_flush & DRM_I915_FENCE_TYPE_RW) && - !dev_priv->flush_pending) { - dev_priv->flush_sequence = (uint32_t) READ_BREADCRUMB(dev_priv); - dev_priv->flush_flags = fc->pending_flush; - dev_priv->saved_flush_status = READ_HWSP(dev_priv, 0); - I915_WRITE(INSTPM, (1 << 5) | (1 << 21)); - dev_priv->flush_pending = 1; - fc->pending_flush &= ~DRM_I915_FENCE_TYPE_RW; - } -} - -static inline void i915_report_rwflush(struct drm_device *dev, - struct drm_i915_private *dev_priv) -{ - if (unlikely(dev_priv->flush_pending)) { - - uint32_t flush_flags; - uint32_t i_status; - uint32_t flush_sequence; - - i_status = READ_HWSP(dev_priv, 0); - if ((i_status & (1 << 12)) != - (dev_priv->saved_flush_status & (1 << 12))) { - flush_flags = dev_priv->flush_flags; - flush_sequence = dev_priv->flush_sequence; - dev_priv->flush_pending = 0; - drm_fence_handler(dev, 0, flush_sequence, - flush_flags, 0); - } - } -} - -static void i915_fence_flush(struct drm_device *dev, - uint32_t fence_class) -{ - 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]; - unsigned long irq_flags; - - if (unlikely(!dev_priv)) - return; - - write_lock_irqsave(&fm->lock, irq_flags); - i915_initiate_rwflush(dev_priv, fc); - write_unlock_irqrestore(&fm->lock, irq_flags); -} - - -static void i915_fence_poll(struct drm_device *dev, uint32_t fence_class, - uint32_t waiting_types) -{ - drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - struct drm_fence_manager *fm = &dev->fm; - struct drm_fence_class_manager *fc = &fm->fence_class[0]; - uint32_t sequence; - - if (unlikely(!dev_priv)) - return; - - /* - * First, report any executed sync flush: - */ - - i915_report_rwflush(dev, dev_priv); - - /* - * Report A new breadcrumb, and adjust IRQs. - */ - - if (waiting_types & DRM_FENCE_TYPE_EXE) { - - sequence = READ_BREADCRUMB(dev_priv); - drm_fence_handler(dev, 0, sequence, - DRM_FENCE_TYPE_EXE, 0); - - if (dev_priv->fence_irq_on && - !(fc->waiting_types & DRM_FENCE_TYPE_EXE)) { - i915_user_irq_off(dev_priv); - dev_priv->fence_irq_on = 0; - } else if (!dev_priv->fence_irq_on && - (fc->waiting_types & DRM_FENCE_TYPE_EXE)) { - i915_user_irq_on(dev_priv); - dev_priv->fence_irq_on = 1; - } - } - - /* - * There may be new RW flushes pending. Start them. - */ - - i915_initiate_rwflush(dev_priv, fc); - - /* - * And possibly, but unlikely, they finish immediately. - */ - - i915_report_rwflush(dev, dev_priv); - -} - -static int i915_fence_emit_sequence(struct drm_device *dev, uint32_t class, - uint32_t flags, uint32_t *sequence, - uint32_t *native_type) -{ - drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - if (unlikely(!dev_priv)) - return -EINVAL; - - i915_emit_irq(dev); - *sequence = (uint32_t) dev_priv->counter; - *native_type = DRM_FENCE_TYPE_EXE; - if (flags & DRM_I915_FENCE_FLAG_FLUSHED) - *native_type |= DRM_I915_FENCE_TYPE_RW; - - return 0; -} - -void i915_fence_handler(struct drm_device *dev) -{ - 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]; - - write_lock(&fm->lock); - if (likely(dev_priv->fence_irq_on)) - i915_fence_poll(dev, 0, fc->waiting_types); - write_unlock(&fm->lock); -} - -/* - * We need a separate wait function since we need to poll for - * sync flushes. - */ - -static int i915_fence_wait(struct drm_fence_object *fence, - int lazy, int interruptible, uint32_t mask) -{ - struct drm_device *dev = fence->dev; - drm_i915_private_t *dev_priv = (struct drm_i915_private *) dev->dev_private; - struct drm_fence_manager *fm = &dev->fm; - struct drm_fence_class_manager *fc = &fm->fence_class[0]; - int ret; - unsigned long _end = jiffies + 3 * DRM_HZ; - - drm_fence_object_flush(fence, mask); - if (likely(interruptible)) - ret = wait_event_interruptible_timeout - (fc->fence_queue, drm_fence_object_signaled(fence, DRM_FENCE_TYPE_EXE), - 3 * DRM_HZ); - else - ret = wait_event_timeout - (fc->fence_queue, drm_fence_object_signaled(fence, DRM_FENCE_TYPE_EXE), - 3 * DRM_HZ); - - if (unlikely(ret == -ERESTARTSYS)) - return -EAGAIN; - - if (unlikely(ret == 0)) - return -EBUSY; - - if (likely(mask == DRM_FENCE_TYPE_EXE || - drm_fence_object_signaled(fence, mask))) - return 0; - - /* - * Remove this code snippet when fixed. HWSTAM doesn't let - * flush info through... - */ - - if (unlikely(dev_priv && !dev_priv->irq_enabled)) { - unsigned long irq_flags; - - DRM_ERROR("X server disabled IRQs before releasing frame buffer.\n"); - msleep(100); - dev_priv->flush_pending = 0; - write_lock_irqsave(&fm->lock, irq_flags); - drm_fence_handler(dev, fence->fence_class, - fence->sequence, fence->type, 0); - write_unlock_irqrestore(&fm->lock, irq_flags); - } - - /* - * Poll for sync flush completion. - */ - - return drm_fence_wait_polling(fence, lazy, interruptible, mask, _end); -} - -static uint32_t i915_fence_needed_flush(struct drm_fence_object *fence) -{ - uint32_t flush_flags = fence->waiting_types & - ~(DRM_FENCE_TYPE_EXE | fence->signaled_types); - - if (likely(flush_flags == 0 || - ((flush_flags & ~fence->native_types) == 0) || - (fence->signaled_types != DRM_FENCE_TYPE_EXE))) - return 0; - else { - struct drm_device *dev = fence->dev; - struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; - struct drm_fence_driver *driver = dev->driver->fence_driver; - - if (unlikely(!dev_priv)) - return 0; - - if (dev_priv->flush_pending) { - uint32_t diff = (dev_priv->flush_sequence - fence->sequence) & - driver->sequence_mask; - - if (diff < driver->wrap_diff) - return 0; - } - } - return flush_flags; -} - -struct drm_fence_driver i915_fence_driver = { - .num_classes = 1, - .wrap_diff = (1U << (BREADCRUMB_BITS - 1)), - .flush_diff = (1U << (BREADCRUMB_BITS - 2)), - .sequence_mask = BREADCRUMB_MASK, - .has_irq = NULL, - .emit = i915_fence_emit_sequence, - .flush = i915_fence_flush, - .poll = i915_fence_poll, - .needed_flush = i915_fence_needed_flush, - .wait = i915_fence_wait, -}; diff --git a/linux-core/i915_gem.c b/linux-core/i915_gem.c index 35dc5bd7..6966c84f 100644 --- a/linux-core/i915_gem.c +++ b/linux-core/i915_gem.c @@ -33,10 +33,6 @@ #include <linux/swap.h> static int -i915_gem_object_set_domain(struct drm_gem_object *obj, - uint32_t read_domains, - uint32_t write_domain); -static int i915_gem_object_set_domain_range(struct drm_gem_object *obj, uint64_t offset, uint64_t size, @@ -51,30 +47,37 @@ static int i915_gem_object_get_page_list(struct drm_gem_object *obj); static void i915_gem_object_free_page_list(struct drm_gem_object *obj); static int i915_gem_object_wait_rendering(struct drm_gem_object *obj); -int -i915_gem_init_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) +int i915_gem_do_init(struct drm_device *dev, unsigned long start, + unsigned long end) { - drm_i915_private_t *dev_priv = dev->dev_private; - struct drm_i915_gem_init *args = data; - - mutex_lock(&dev->struct_mutex); + struct drm_i915_private *dev_priv = dev->dev_private; - if (args->gtt_start >= args->gtt_end || - (args->gtt_start & (PAGE_SIZE - 1)) != 0 || - (args->gtt_end & (PAGE_SIZE - 1)) != 0) { - mutex_unlock(&dev->struct_mutex); + if (start >= end || + (start & (PAGE_SIZE - 1)) != 0 || + (end & (PAGE_SIZE - 1)) != 0) { return -EINVAL; } - drm_mm_init(&dev_priv->mm.gtt_space, args->gtt_start, - args->gtt_end - args->gtt_start); + drm_mm_init(&dev_priv->mm.gtt_space, start, + end - start); + + dev->gtt_total = (uint32_t) (end - start); - dev->gtt_total = (uint32_t) (args->gtt_end - args->gtt_start); + return 0; +} +int +i915_gem_init_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_i915_gem_init *args = data; + int ret; + + mutex_lock(&dev->struct_mutex); + ret = i915_gem_do_init(dev, args->gtt_start, args->gtt_end); mutex_unlock(&dev->struct_mutex); - return 0; + return ret; } @@ -501,7 +504,7 @@ static void i915_gem_object_move_to_active(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv = obj->driver_private; /* Add a reference if we're newly entering the active list. */ @@ -519,7 +522,7 @@ static void i915_gem_object_move_to_inactive(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv = obj->driver_private; i915_verify_inactive(dev, __FILE__, __LINE__); @@ -546,7 +549,7 @@ i915_gem_object_move_to_inactive(struct drm_gem_object *obj) static uint32_t i915_add_request(struct drm_device *dev, uint32_t flush_domains) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_request *request; uint32_t seqno; int was_empty; @@ -594,7 +597,7 @@ i915_add_request(struct drm_device *dev, uint32_t flush_domains) uint32_t i915_retire_commands(struct drm_device *dev) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; uint32_t cmd = MI_FLUSH | MI_NO_WRITE_FLUSH; uint32_t flush_domains = 0; RING_LOCALS; @@ -617,7 +620,7 @@ static void i915_gem_retire_request(struct drm_device *dev, struct drm_i915_gem_request *request) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; if (request->flush_domains != 0) { struct drm_i915_gem_object *obj_priv, *next; @@ -688,7 +691,7 @@ i915_seqno_passed(uint32_t seq1, uint32_t seq2) uint32_t i915_get_gem_seqno(struct drm_device *dev) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; return READ_HWSP(dev_priv, I915_GEM_HWS_INDEX); } @@ -699,7 +702,7 @@ i915_get_gem_seqno(struct drm_device *dev) void i915_gem_retire_requests(struct drm_device *dev) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; uint32_t seqno; seqno = i915_get_gem_seqno(dev); @@ -727,10 +730,10 @@ i915_gem_retire_requests(struct drm_device *dev) void i915_gem_retire_work_handler(struct work_struct *work) { - drm_i915_private_t *dev_priv; + struct drm_i915_private *dev_priv; struct drm_device *dev; - dev_priv = container_of(work, drm_i915_private_t, + dev_priv = container_of(work, struct drm_i915_private, mm.retire_work.work); dev = dev_priv->dev; @@ -748,19 +751,19 @@ i915_gem_retire_work_handler(struct work_struct *work) int i915_wait_request(struct drm_device *dev, uint32_t seqno) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; int ret = 0; BUG_ON(seqno == 0); if (!i915_seqno_passed(i915_get_gem_seqno(dev), seqno)) { dev_priv->mm.waiting_gem_seqno = seqno; - i915_user_irq_on(dev_priv); + i915_user_irq_on(dev); ret = wait_event_interruptible(dev_priv->irq_queue, i915_seqno_passed(i915_get_gem_seqno(dev), seqno) || dev_priv->mm.wedged); - i915_user_irq_off(dev_priv); + i915_user_irq_off(dev); dev_priv->mm.waiting_gem_seqno = 0; } if (dev_priv->mm.wedged) @@ -786,7 +789,7 @@ i915_gem_flush(struct drm_device *dev, uint32_t invalidate_domains, uint32_t flush_domains) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; uint32_t cmd; RING_LOCALS; @@ -972,7 +975,7 @@ i915_gem_object_unbind(struct drm_gem_object *obj) static int i915_gem_evict_something(struct drm_device *dev) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_gem_object *obj; struct drm_i915_gem_object *obj_priv; int ret = 0; @@ -1102,7 +1105,7 @@ static int i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment) { struct drm_device *dev = obj->dev; - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv = obj->driver_private; struct drm_mm_node *free_space; int page_count, ret; @@ -1311,7 +1314,7 @@ i915_gem_clflush_object(struct drm_gem_object *obj) * MI_FLUSH * drm_agp_chipset_flush */ -static int +int i915_gem_object_set_domain(struct drm_gem_object *obj, uint32_t read_domains, uint32_t write_domain) @@ -1675,7 +1678,7 @@ i915_dispatch_gem_execbuffer(struct drm_device *dev, struct drm_i915_gem_execbuffer *exec, uint64_t exec_offset) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_clip_rect __user *boxes = (struct drm_clip_rect __user *) (uintptr_t) exec->cliprects_ptr; int nbox = exec->num_cliprects; @@ -1758,7 +1761,7 @@ int i915_gem_execbuffer(struct drm_device *dev, void *data, struct drm_file *file_priv) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_file_private *i915_file_priv = file_priv->driver_priv; struct drm_i915_gem_execbuffer *args = data; struct drm_i915_gem_exec_object *exec_list = NULL; @@ -2000,7 +2003,7 @@ void i915_gem_object_unpin(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv = obj->driver_private; i915_verify_inactive(dev, __FILE__, __LINE__); @@ -2217,7 +2220,7 @@ i915_gem_evict_from_list(struct drm_device *dev, struct list_head *head) static int i915_gem_idle(struct drm_device *dev) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; uint32_t seqno, cur_seqno, last_seqno; int stuck; @@ -2289,7 +2292,7 @@ i915_gem_idle(struct drm_device *dev) static int i915_gem_init_hws(struct drm_device *dev) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_gem_object *obj; struct drm_i915_gem_object *obj_priv; int ret; @@ -2336,10 +2339,10 @@ i915_gem_init_hws(struct drm_device *dev) return 0; } -static int +int i915_gem_init_ringbuffer(struct drm_device *dev) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_gem_object *obj; struct drm_i915_gem_object *obj_priv; int ret; @@ -2400,10 +2403,10 @@ i915_gem_init_ringbuffer(struct drm_device *dev) return 0; } -static void +void i915_gem_cleanup_ringbuffer(struct drm_device *dev) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; if (dev_priv->ring.ring_obj == NULL) return; @@ -2430,9 +2433,12 @@ int i915_gem_entervt_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; int ret; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return 0; + if (dev_priv->mm.wedged) { DRM_ERROR("Reenabling wedged hardware, good luck\n"); dev_priv->mm.wedged = 0; @@ -2458,6 +2464,9 @@ i915_gem_leavevt_ioctl(struct drm_device *dev, void *data, { int ret; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return 0; + mutex_lock(&dev->struct_mutex); ret = i915_gem_idle(dev); if (ret == 0) @@ -2471,7 +2480,7 @@ void i915_gem_lastclose(struct drm_device *dev) { int ret; - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; mutex_lock(&dev->struct_mutex); @@ -2488,7 +2497,7 @@ i915_gem_lastclose(struct drm_device *dev) void i915_gem_load(struct drm_device *dev) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; INIT_LIST_HEAD(&dev_priv->mm.active_list); INIT_LIST_HEAD(&dev_priv->mm.flushing_list); diff --git a/linux-core/i915_gem_debug.c b/linux-core/i915_gem_debug.c index a2d6f289..6d50e5bb 100644 --- a/linux-core/i915_gem_debug.c +++ b/linux-core/i915_gem_debug.c @@ -35,7 +35,7 @@ void i915_verify_inactive(struct drm_device *dev, char *file, int line) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_gem_object *obj; struct drm_i915_gem_object *obj_priv; @@ -102,7 +102,7 @@ i915_gem_dump_object(struct drm_gem_object *obj, int len, void i915_dump_lru(struct drm_device *dev, const char *where) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv; DRM_INFO("active list %s {\n", where); diff --git a/linux-core/i915_gem_proc.c b/linux-core/i915_gem_proc.c index 132eb3d1..2704f922 100644 --- a/linux-core/i915_gem_proc.c +++ b/linux-core/i915_gem_proc.c @@ -37,7 +37,7 @@ static int i915_gem_active_info(char *buf, char **start, off_t offset, { struct drm_minor *minor = (struct drm_minor *) data; struct drm_device *dev = minor->dev; - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv; int len = 0; @@ -76,7 +76,7 @@ static int i915_gem_flushing_info(char *buf, char **start, off_t offset, { struct drm_minor *minor = (struct drm_minor *) data; struct drm_device *dev = minor->dev; - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv; int len = 0; @@ -114,7 +114,7 @@ static int i915_gem_inactive_info(char *buf, char **start, off_t offset, { struct drm_minor *minor = (struct drm_minor *) data; struct drm_device *dev = minor->dev; - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv; int len = 0; @@ -152,7 +152,7 @@ static int i915_gem_request_info(char *buf, char **start, off_t offset, { struct drm_minor *minor = (struct drm_minor *) data; struct drm_device *dev = minor->dev; - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_request *gem_request; int len = 0; @@ -183,7 +183,7 @@ static int i915_gem_seqno_info(char *buf, char **start, off_t offset, { struct drm_minor *minor = (struct drm_minor *) data; struct drm_device *dev = minor->dev; - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; int len = 0; if (offset > DRM_PROC_LIMIT) { @@ -209,7 +209,7 @@ static int i915_interrupt_info(char *buf, char **start, off_t offset, { struct drm_minor *minor = (struct drm_minor *) data; struct drm_device *dev = minor->dev; - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; int len = 0; if (offset > DRM_PROC_LIMIT) { diff --git a/linux-core/i915_gem_tiling.c b/linux-core/i915_gem_tiling.c index a4ff736f..3ac1f353 100644 --- a/linux-core/i915_gem_tiling.c +++ b/linux-core/i915_gem_tiling.c @@ -86,7 +86,7 @@ void i915_gem_detect_bit_6_swizzle(struct drm_device *dev) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; struct pci_dev *bridge; uint32_t swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN; uint32_t swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN; @@ -115,7 +115,8 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev) * since the bridge would only ever use standard BARs 0-1 (though it * doesn't anyway) */ - ret = pci_read_base(bridge, mchbar_offset, &bridge->resource[2]); + ret = pci_read_base(bridge, pci_bar_mem64, &bridge->resource[2], + mchbar_offset); if (ret != 0) { DRM_ERROR("pci_read_base failed: %d\n", ret); return; @@ -233,7 +234,7 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_i915_gem_set_tiling *args = data; - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_gem_object *obj; struct drm_i915_gem_object *obj_priv; @@ -275,7 +276,7 @@ i915_gem_get_tiling(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_i915_gem_get_tiling *args = data; - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_gem_object *obj; struct drm_i915_gem_object *obj_priv; 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/i915_ioc32.c b/linux-core/i915_ioc32.c index 0b8fff19..d2d69a0b 100644 --- a/linux-core/i915_ioc32.c +++ b/linux-core/i915_ioc32.c @@ -183,73 +183,12 @@ static int compat_i915_alloc(struct file *file, unsigned int cmd, DRM_IOCTL_I915_ALLOC, (unsigned long) request); } -typedef struct drm_i915_execbuffer32 { - uint64_t ops_list; - uint32_t num_buffers; - struct _drm_i915_batchbuffer32 batch; - drm_context_t context; - struct drm_fence_arg fence_arg; -} drm_i915_execbuffer32_t; - -static int compat_i915_execbuffer(struct file *file, unsigned int cmd, - unsigned long arg) -{ - drm_i915_execbuffer32_t req32; - struct drm_i915_execbuffer __user *request; - int err; - - if (copy_from_user(&req32, (void __user *) arg, sizeof(req32))) - return -EFAULT; - - request = compat_alloc_user_space(sizeof(*request)); - - if (!access_ok(VERIFY_WRITE, request, sizeof(*request)) - || __put_user(req32.ops_list, &request->ops_list) - || __put_user(req32.num_buffers, &request->num_buffers) - || __put_user(req32.context, &request->context) - || __copy_to_user(&request->fence_arg, &req32.fence_arg, - sizeof(req32.fence_arg)) - || __put_user(req32.batch.start, &request->batch.start) - || __put_user(req32.batch.used, &request->batch.used) - || __put_user(req32.batch.DR1, &request->batch.DR1) - || __put_user(req32.batch.DR4, &request->batch.DR4) - || __put_user(req32.batch.num_cliprects, - &request->batch.num_cliprects) - || __put_user((int __user *)(unsigned long)req32.batch.cliprects, - &request->batch.cliprects)) - return -EFAULT; - - err = drm_ioctl(file->f_dentry->d_inode, file, - DRM_IOCTL_I915_EXECBUFFER, (unsigned long)request); - - if (err) - return err; - - if (__get_user(req32.fence_arg.handle, &request->fence_arg.handle) - || __get_user(req32.fence_arg.fence_class, &request->fence_arg.fence_class) - || __get_user(req32.fence_arg.type, &request->fence_arg.type) - || __get_user(req32.fence_arg.flags, &request->fence_arg.flags) - || __get_user(req32.fence_arg.signaled, &request->fence_arg.signaled) - || __get_user(req32.fence_arg.error, &request->fence_arg.error) - || __get_user(req32.fence_arg.sequence, &request->fence_arg.sequence)) - return -EFAULT; - - if (copy_to_user((void __user *)arg, &req32, sizeof(req32))) - return -EFAULT; - - return 0; -} - - drm_ioctl_compat_t *i915_compat_ioctls[] = { [DRM_I915_BATCHBUFFER] = compat_i915_batchbuffer, [DRM_I915_CMDBUFFER] = compat_i915_cmdbuffer, [DRM_I915_GETPARAM] = compat_i915_getparam, [DRM_I915_IRQ_EMIT] = compat_i915_irq_emit, [DRM_I915_ALLOC] = compat_i915_alloc, -#ifdef I915_HAVE_BUFFER - [DRM_I915_EXECBUFFER] = compat_i915_execbuffer, -#endif }; /** diff --git a/linux-core/intel_bios.c b/linux-core/intel_bios.c new file mode 100644 index 00000000..0cdc915c --- /dev/null +++ b/linux-core/intel_bios.c @@ -0,0 +1,242 @@ +/* + * Copyright © 2006 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * + */ +#include "drmP.h" +#include "drm.h" +#include "i915_drm.h" +#include "i915_drv.h" +#include "intel_bios.h" + +#if 0 +unsigned char * +i830_bios_get_aim_data_block (ScrnInfoPtr pScrn, int aim, int data_block) +{ + unsigned char *bios; + int bdb_off; + int vbt_off; + int aim_off; + struct vbt_header *vbt; + struct aimdb_header *aimdb; + struct aimdb_block *aimdb_block; + + bios = i830_bios_get (pScrn); + if (!bios) + return NULL; + + vbt_off = INTEL_BIOS_16(0x1a); + vbt = (struct vbt_header *)(bios + vbt_off); + + aim_off = vbt->aim_offset[aim]; + if (!aim_off) + { + free (bios); + return NULL; + } + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "aim_off %d\n", aim_off); + aimdb = (struct aimdb_header *) (bios + vbt_off + aim_off); + bdb_off = aimdb->aimdb_header_size; + while (bdb_off < aimdb->aimdb_size) + { + aimdb_block = (struct aimdb_block *) (bios + vbt_off + aim_off + bdb_off); + if (aimdb_block->aimdb_id == data_block) + { + unsigned char *aim = malloc (aimdb_block->aimdb_size + sizeof (struct aimdb_block)); + if (!aim) + { + free (bios); + return NULL; + } + memcpy (aim, aimdb_block, aimdb_block->aimdb_size + sizeof (struct aimdb_block)); + free (bios); + return aim; + } + bdb_off += aimdb_block->aimdb_size + sizeof (struct aimdb_block); + } + free (bios); + return NULL; +} +#endif + +static void * +find_section(struct bdb_header *bdb, int section_id) +{ + u8 *base = (u8 *)bdb; + int index = 0; + u16 total, current_size; + u8 current_id; + + /* skip to first section */ + index += bdb->header_size; + total = bdb->bdb_size; + + /* walk the sections looking for section_id */ + while (index < total) { + current_id = *(base + index); + index++; + current_size = *((u16 *)(base + index)); + index += 2; + if (current_id == section_id) + return base + index; + index += current_size; + } + + return NULL; +} + +/* Try to find panel data */ +static void +parse_panel_data(struct drm_i915_private *dev_priv, struct bdb_header *bdb) +{ + struct bdb_lvds_options *lvds_options; + struct bdb_lvds_lfp_data *lvds_lfp_data; + struct bdb_lvds_lfp_data_entry *entry; + struct lvds_dvo_timing *dvo_timing; + struct drm_display_mode *panel_fixed_mode; + + /* Defaults if we can't find VBT info */ + dev_priv->lvds_dither = 0; + dev_priv->lvds_vbt = 0; + + lvds_options = find_section(bdb, BDB_LVDS_OPTIONS); + if (!lvds_options) + return; + + dev_priv->lvds_dither = lvds_options->pixel_dither; + if (lvds_options->panel_type == 0xff) + return; + + lvds_lfp_data = find_section(bdb, BDB_LVDS_LFP_DATA); + if (!lvds_lfp_data) + return; + + dev_priv->lvds_vbt = 1; + + entry = &lvds_lfp_data->data[lvds_options->panel_type]; + dvo_timing = &entry->dvo_timing; + + panel_fixed_mode = drm_calloc(1, sizeof(*panel_fixed_mode), + DRM_MEM_DRIVER); + + panel_fixed_mode->hdisplay = (dvo_timing->hactive_hi << 8) | + dvo_timing->hactive_lo; + panel_fixed_mode->hsync_start = panel_fixed_mode->hdisplay + + ((dvo_timing->hsync_off_hi << 8) | dvo_timing->hsync_off_lo); + panel_fixed_mode->hsync_end = panel_fixed_mode->hsync_start + + dvo_timing->hsync_pulse_width; + panel_fixed_mode->htotal = panel_fixed_mode->hdisplay + + ((dvo_timing->hblank_hi << 8) | dvo_timing->hblank_lo); + + panel_fixed_mode->vdisplay = (dvo_timing->vactive_hi << 8) | + dvo_timing->vactive_lo; + panel_fixed_mode->vsync_start = panel_fixed_mode->vdisplay + + dvo_timing->vsync_off; + panel_fixed_mode->vsync_end = panel_fixed_mode->vsync_start + + dvo_timing->vsync_pulse_width; + panel_fixed_mode->vtotal = panel_fixed_mode->vdisplay + + ((dvo_timing->vblank_hi << 8) | dvo_timing->vblank_lo); + panel_fixed_mode->clock = dvo_timing->clock * 10; + panel_fixed_mode->type = DRM_MODE_TYPE_PREFERRED; + + drm_mode_set_name(panel_fixed_mode); + + dev_priv->vbt_mode = panel_fixed_mode; + + DRM_DEBUG("Found panel mode in BIOS VBT tables:\n"); + drm_mode_debug_printmodeline(panel_fixed_mode); + + return; +} + +static void +parse_general_features(struct drm_i915_private *dev_priv, + struct bdb_header *bdb) +{ + struct bdb_general_features *general; + + /* Set sensible defaults in case we can't find the general block */ + dev_priv->int_tv_support = 1; + dev_priv->int_crt_support = 1; + + general = find_section(bdb, BDB_GENERAL_FEATURES); + if (general) { + dev_priv->int_tv_support = general->int_tv_support; + dev_priv->int_crt_support = general->int_crt_support; + } +} + +/** + * intel_init_bios - initialize VBIOS settings & find VBT + * @dev: DRM device + * + * Loads the Video BIOS and checks that the VBT exists. Sets scratch registers + * to appropriate values. + * + * VBT existence is a sanity check that is relied on by other i830_bios.c code. + * Note that it would be better to use a BIOS call to get the VBT, as BIOSes may + * feed an updated VBT back through that, compared to what we'll fetch using + * this method of groping around in the BIOS data. + * + * Returns 0 on success, nonzero on failure. + */ +bool +intel_init_bios(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct pci_dev *pdev = dev->pdev; + struct vbt_header *vbt = NULL; + struct bdb_header *bdb; + u8 __iomem *bios; + size_t size; + int i; + + bios = pci_map_rom(pdev, &size); + if (!bios) + return -1; + + /* Scour memory looking for the VBT signature */ + for (i = 0; i + 4 < size; i++) { + if (!memcmp(bios + i, "$VBT", 4)) { + vbt = (struct vbt_header *)(bios + i); + break; + } + } + + if (!vbt) { + DRM_ERROR("VBT signature missing\n"); + pci_unmap_rom(pdev, bios); + return -1; + } + + bdb = (struct bdb_header *)(bios + i + vbt->bdb_offset); + + /* Grab useful general definitions */ + parse_general_features(dev_priv, bdb); + parse_panel_data(dev_priv, bdb); + + pci_unmap_rom(pdev, bios); + + return 0; +} diff --git a/linux-core/intel_bios.h b/linux-core/intel_bios.h new file mode 100644 index 00000000..5ea715ac --- /dev/null +++ b/linux-core/intel_bios.h @@ -0,0 +1,405 @@ +/* + * Copyright © 2006 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * + */ + +#ifndef _I830_BIOS_H_ +#define _I830_BIOS_H_ + +#include "drmP.h" + +struct vbt_header { + u8 signature[20]; /**< Always starts with 'VBT$' */ + u16 version; /**< decimal */ + u16 header_size; /**< in bytes */ + u16 vbt_size; /**< in bytes */ + u8 vbt_checksum; + u8 reserved0; + u32 bdb_offset; /**< from beginning of VBT */ + u32 aim_offset[4]; /**< from beginning of VBT */ +} __attribute__((packed)); + +struct bdb_header { + u8 signature[16]; /**< Always 'BIOS_DATA_BLOCK' */ + u16 version; /**< decimal */ + u16 header_size; /**< in bytes */ + u16 bdb_size; /**< in bytes */ +}; + +/* strictly speaking, this is a "skip" block, but it has interesting info */ +struct vbios_data { + u8 type; /* 0 == desktop, 1 == mobile */ + u8 relstage; + u8 chipset; + u8 lvds_present:1; + u8 tv_present:1; + u8 rsvd2:6; /* finish byte */ + u8 rsvd3[4]; + u8 signon[155]; + u8 copyright[61]; + u16 code_segment; + u8 dos_boot_mode; + u8 bandwidth_percent; + u8 rsvd4; /* popup memory size */ + u8 resize_pci_bios; + u8 rsvd5; /* is crt already on ddc2 */ +} __attribute__((packed)); + +/* + * There are several types of BIOS data blocks (BDBs), each block has + * an ID and size in the first 3 bytes (ID in first, size in next 2). + * Known types are listed below. + */ +#define BDB_GENERAL_FEATURES 1 +#define BDB_GENERAL_DEFINITIONS 2 +#define BDB_OLD_TOGGLE_LIST 3 +#define BDB_MODE_SUPPORT_LIST 4 +#define BDB_GENERIC_MODE_TABLE 5 +#define BDB_EXT_MMIO_REGS 6 +#define BDB_SWF_IO 7 +#define BDB_SWF_MMIO 8 +#define BDB_DOT_CLOCK_TABLE 9 +#define BDB_MODE_REMOVAL_TABLE 10 +#define BDB_CHILD_DEVICE_TABLE 11 +#define BDB_DRIVER_FEATURES 12 +#define BDB_DRIVER_PERSISTENCE 13 +#define BDB_EXT_TABLE_PTRS 14 +#define BDB_DOT_CLOCK_OVERRIDE 15 +#define BDB_DISPLAY_SELECT 16 +/* 17 rsvd */ +#define BDB_DRIVER_ROTATION 18 +#define BDB_DISPLAY_REMOVE 19 +#define BDB_OEM_CUSTOM 20 +#define BDB_EFP_LIST 21 /* workarounds for VGA hsync/vsync */ +#define BDB_SDVO_LVDS_OPTIONS 22 +#define BDB_SDVO_PANEL_DTDS 23 +#define BDB_SDVO_LVDS_PNP_IDS 24 +#define BDB_SDVO_LVDS_POWER_SEQ 25 +#define BDB_TV_OPTIONS 26 +#define BDB_LVDS_OPTIONS 40 +#define BDB_LVDS_LFP_DATA_PTRS 41 +#define BDB_LVDS_LFP_DATA 42 +#define BDB_LVDS_BACKLIGHT 43 +#define BDB_LVDS_POWER 44 +#define BDB_SKIP 254 /* VBIOS private block, ignore */ + +struct bdb_general_features { + /* bits 1 */ + u8 panel_fitting:2; + u8 flexaim:1; + u8 msg_enable:1; + u8 clear_screen:3; + u8 color_flip:1; + + /* bits 2 */ + u8 download_ext_vbt:1; + u8 enable_ssc:1; + u8 ssc_freq:1; + u8 enable_lfp_on_override:1; + u8 disable_ssc_ddt:1; + u8 rsvd8:3; /* finish byte */ + + /* bits 3 */ + u8 disable_smooth_vision:1; + u8 single_dvi:1; + u8 rsvd9:6; /* finish byte */ + + /* bits 4 */ + u8 legacy_monitor_detect; + + /* bits 5 */ + u8 int_crt_support:1; + u8 int_tv_support:1; + u8 rsvd11:6; /* finish byte */ +} __attribute__((packed)); + +struct bdb_general_definitions { + /* DDC GPIO */ + u8 crt_ddc_gmbus_pin; + + /* DPMS bits */ + u8 dpms_acpi:1; + u8 skip_boot_crt_detect:1; + u8 dpms_aim:1; + u8 rsvd1:5; /* finish byte */ + + /* boot device bits */ + u8 boot_display[2]; + u8 child_dev_size; + + /* device info */ + u8 tv_or_lvds_info[33]; + u8 dev1[33]; + u8 dev2[33]; + u8 dev3[33]; + u8 dev4[33]; + /* may be another device block here on some platforms */ +}; + +struct bdb_lvds_options { + u8 panel_type; + u8 rsvd1; + /* LVDS capabilities, stored in a dword */ + u8 rsvd2:1; + u8 lvds_edid:1; + u8 pixel_dither:1; + u8 pfit_ratio_auto:1; + u8 pfit_gfx_mode_enhanced:1; + u8 pfit_text_mode_enhanced:1; + u8 pfit_mode:2; + u8 rsvd4; +} __attribute__((packed)); + +/* LFP pointer table contains entries to the struct below */ +struct bdb_lvds_lfp_data_ptr { + u16 fp_timing_offset; /* offsets are from start of bdb */ + u8 fp_table_size; + u16 dvo_timing_offset; + u8 dvo_table_size; + u16 panel_pnp_id_offset; + u8 pnp_table_size; +} __attribute__((packed)); + +struct bdb_lvds_lfp_data_ptrs { + u8 lvds_entries; /* followed by one or more lvds_data_ptr structs */ + struct bdb_lvds_lfp_data_ptr ptr[16]; +} __attribute__((packed)); + +/* LFP data has 3 blocks per entry */ +struct lvds_fp_timing { + u16 x_res; + u16 y_res; + u32 lvds_reg; + u32 lvds_reg_val; + u32 pp_on_reg; + u32 pp_on_reg_val; + u32 pp_off_reg; + u32 pp_off_reg_val; + u32 pp_cycle_reg; + u32 pp_cycle_reg_val; + u32 pfit_reg; + u32 pfit_reg_val; + u16 terminator; +} __attribute__((packed)); + +struct lvds_dvo_timing { + u16 clock; /**< In 10khz */ + u8 hactive_lo; + u8 hblank_lo; + u8 hblank_hi:4; + u8 hactive_hi:4; + u8 vactive_lo; + u8 vblank_lo; + u8 vblank_hi:4; + u8 vactive_hi:4; + u8 hsync_off_lo; + u8 hsync_pulse_width; + u8 vsync_pulse_width:4; + u8 vsync_off:4; + u8 rsvd0:6; + u8 hsync_off_hi:2; + u8 h_image; + u8 v_image; + u8 max_hv; + u8 h_border; + u8 v_border; + u8 rsvd1:3; + u8 digital:2; + u8 vsync_positive:1; + u8 hsync_positive:1; + u8 rsvd2:1; +} __attribute__((packed)); + +struct lvds_pnp_id { + u16 mfg_name; + u16 product_code; + u32 serial; + u8 mfg_week; + u8 mfg_year; +} __attribute__((packed)); + +struct bdb_lvds_lfp_data_entry { + struct lvds_fp_timing fp_timing; + struct lvds_dvo_timing dvo_timing; + struct lvds_pnp_id pnp_id; +} __attribute__((packed)); + +struct bdb_lvds_lfp_data { + struct bdb_lvds_lfp_data_entry data[16]; +} __attribute__((packed)); + +struct aimdb_header { + char signature[16]; + char oem_device[20]; + u16 aimdb_version; + u16 aimdb_header_size; + u16 aimdb_size; +} __attribute__((packed)); + +struct aimdb_block { + u8 aimdb_id; + u16 aimdb_size; +} __attribute__((packed)); + +struct vch_panel_data { + u16 fp_timing_offset; + u8 fp_timing_size; + u16 dvo_timing_offset; + u8 dvo_timing_size; + u16 text_fitting_offset; + u8 text_fitting_size; + u16 graphics_fitting_offset; + u8 graphics_fitting_size; +} __attribute__((packed)); + +struct vch_bdb_22 { + struct aimdb_block aimdb_block; + struct vch_panel_data panels[16]; +} __attribute__((packed)); + +bool intel_init_bios(struct drm_device *dev); + +/* + * Driver<->VBIOS interaction occurs through scratch bits in + * GR18 & SWF*. + */ + +/* GR18 bits are set on display switch and hotkey events */ +#define GR18_DRIVER_SWITCH_EN (1<<7) /* 0: VBIOS control, 1: driver control */ +#define GR18_HOTKEY_MASK 0x78 /* See also SWF4 15:0 */ +#define GR18_HK_NONE (0x0<<3) +#define GR18_HK_LFP_STRETCH (0x1<<3) +#define GR18_HK_TOGGLE_DISP (0x2<<3) +#define GR18_HK_DISP_SWITCH (0x4<<3) /* see SWF14 15:0 for what to enable */ +#define GR18_HK_POPUP_DISABLED (0x6<<3) +#define GR18_HK_POPUP_ENABLED (0x7<<3) +#define GR18_HK_PFIT (0x8<<3) +#define GR18_HK_APM_CHANGE (0xa<<3) +#define GR18_HK_MULTIPLE (0xc<<3) +#define GR18_USER_INT_EN (1<<2) +#define GR18_A0000_FLUSH_EN (1<<1) +#define GR18_SMM_EN (1<<0) + +/* Set by driver, cleared by VBIOS */ +#define SWF00_YRES_SHIFT 16 +#define SWF00_XRES_SHIFT 0 +#define SWF00_RES_MASK 0xffff + +/* Set by VBIOS at boot time and driver at runtime */ +#define SWF01_TV2_FORMAT_SHIFT 8 +#define SWF01_TV1_FORMAT_SHIFT 0 +#define SWF01_TV_FORMAT_MASK 0xffff + +#define SWF10_VBIOS_BLC_I2C_EN (1<<29) +#define SWF10_GTT_OVERRIDE_EN (1<<28) +#define SWF10_LFP_DPMS_OVR (1<<27) /* override DPMS on display switch */ +#define SWF10_ACTIVE_TOGGLE_LIST_MASK (7<<24) +#define SWF10_OLD_TOGGLE 0x0 +#define SWF10_TOGGLE_LIST_1 0x1 +#define SWF10_TOGGLE_LIST_2 0x2 +#define SWF10_TOGGLE_LIST_3 0x3 +#define SWF10_TOGGLE_LIST_4 0x4 +#define SWF10_PANNING_EN (1<<23) +#define SWF10_DRIVER_LOADED (1<<22) +#define SWF10_EXTENDED_DESKTOP (1<<21) +#define SWF10_EXCLUSIVE_MODE (1<<20) +#define SWF10_OVERLAY_EN (1<<19) +#define SWF10_PLANEB_HOLDOFF (1<<18) +#define SWF10_PLANEA_HOLDOFF (1<<17) +#define SWF10_VGA_HOLDOFF (1<<16) +#define SWF10_ACTIVE_DISP_MASK 0xffff +#define SWF10_PIPEB_LFP2 (1<<15) +#define SWF10_PIPEB_EFP2 (1<<14) +#define SWF10_PIPEB_TV2 (1<<13) +#define SWF10_PIPEB_CRT2 (1<<12) +#define SWF10_PIPEB_LFP (1<<11) +#define SWF10_PIPEB_EFP (1<<10) +#define SWF10_PIPEB_TV (1<<9) +#define SWF10_PIPEB_CRT (1<<8) +#define SWF10_PIPEA_LFP2 (1<<7) +#define SWF10_PIPEA_EFP2 (1<<6) +#define SWF10_PIPEA_TV2 (1<<5) +#define SWF10_PIPEA_CRT2 (1<<4) +#define SWF10_PIPEA_LFP (1<<3) +#define SWF10_PIPEA_EFP (1<<2) +#define SWF10_PIPEA_TV (1<<1) +#define SWF10_PIPEA_CRT (1<<0) + +#define SWF11_MEMORY_SIZE_SHIFT 16 +#define SWF11_SV_TEST_EN (1<<15) +#define SWF11_IS_AGP (1<<14) +#define SWF11_DISPLAY_HOLDOFF (1<<13) +#define SWF11_DPMS_REDUCED (1<<12) +#define SWF11_IS_VBE_MODE (1<<11) +#define SWF11_PIPEB_ACCESS (1<<10) /* 0 here means pipe a */ +#define SWF11_DPMS_MASK 0x07 +#define SWF11_DPMS_OFF (1<<2) +#define SWF11_DPMS_SUSPEND (1<<1) +#define SWF11_DPMS_STANDBY (1<<0) +#define SWF11_DPMS_ON 0 + +#define SWF14_GFX_PFIT_EN (1<<31) +#define SWF14_TEXT_PFIT_EN (1<<30) +#define SWF14_LID_STATUS_CLOSED (1<<29) /* 0 here means open */ +#define SWF14_POPUP_EN (1<<28) +#define SWF14_DISPLAY_HOLDOFF (1<<27) +#define SWF14_DISP_DETECT_EN (1<<26) +#define SWF14_DOCKING_STATUS_DOCKED (1<<25) /* 0 here means undocked */ +#define SWF14_DRIVER_STATUS (1<<24) +#define SWF14_OS_TYPE_WIN9X (1<<23) +#define SWF14_OS_TYPE_WINNT (1<<22) +/* 21:19 rsvd */ +#define SWF14_PM_TYPE_MASK 0x00070000 +#define SWF14_PM_ACPI_VIDEO (0x4 << 16) +#define SWF14_PM_ACPI (0x3 << 16) +#define SWF14_PM_APM_12 (0x2 << 16) +#define SWF14_PM_APM_11 (0x1 << 16) +#define SWF14_HK_REQUEST_MASK 0x0000ffff /* see GR18 6:3 for event type */ + /* if GR18 indicates a display switch */ +#define SWF14_DS_PIPEB_LFP2_EN (1<<15) +#define SWF14_DS_PIPEB_EFP2_EN (1<<14) +#define SWF14_DS_PIPEB_TV2_EN (1<<13) +#define SWF14_DS_PIPEB_CRT2_EN (1<<12) +#define SWF14_DS_PIPEB_LFP_EN (1<<11) +#define SWF14_DS_PIPEB_EFP_EN (1<<10) +#define SWF14_DS_PIPEB_TV_EN (1<<9) +#define SWF14_DS_PIPEB_CRT_EN (1<<8) +#define SWF14_DS_PIPEA_LFP2_EN (1<<7) +#define SWF14_DS_PIPEA_EFP2_EN (1<<6) +#define SWF14_DS_PIPEA_TV2_EN (1<<5) +#define SWF14_DS_PIPEA_CRT2_EN (1<<4) +#define SWF14_DS_PIPEA_LFP_EN (1<<3) +#define SWF14_DS_PIPEA_EFP_EN (1<<2) +#define SWF14_DS_PIPEA_TV_EN (1<<1) +#define SWF14_DS_PIPEA_CRT_EN (1<<0) + /* if GR18 indicates a panel fitting request */ +#define SWF14_PFIT_EN (1<<0) /* 0 means disable */ + /* if GR18 indicates an APM change request */ +#define SWF14_APM_HIBERNATE 0x4 +#define SWF14_APM_SUSPEND 0x3 +#define SWF14_APM_STANDBY 0x1 +#define SWF14_APM_RESTORE 0x0 + +#endif /* _I830_BIOS_H_ */ diff --git a/linux-core/intel_crt.c b/linux-core/intel_crt.c new file mode 100644 index 00000000..2505d983 --- /dev/null +++ b/linux-core/intel_crt.c @@ -0,0 +1,296 @@ +/* + * Copyright © 2006-2007 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Eric Anholt <eric@anholt.net> + */ + +#include <linux/i2c.h> +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "drm_crtc_helper.h" +#include "intel_drv.h" +#include "i915_drm.h" +#include "i915_drv.h" + +static void intel_crt_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 temp; + + temp = I915_READ(ADPA); + temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE); + temp &= ~ADPA_DAC_ENABLE; + + switch(mode) { + case DRM_MODE_DPMS_ON: + temp |= ADPA_DAC_ENABLE; + break; + case DRM_MODE_DPMS_STANDBY: + temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE; + break; + case DRM_MODE_DPMS_SUSPEND: + temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE; + break; + case DRM_MODE_DPMS_OFF: + temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE; + break; + } + + I915_WRITE(ADPA, temp); +} + +static void intel_crt_save(struct drm_connector *connector) +{ + +} + +static void intel_crt_restore(struct drm_connector *connector) +{ + +} + +static int intel_crt_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + return MODE_NO_DBLESCAN; + + if (mode->clock > 400000 || mode->clock < 25000) + return MODE_CLOCK_RANGE; + + return MODE_OK; +} + +static bool intel_crt_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void intel_crt_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + + struct drm_device *dev = encoder->dev; + struct drm_crtc *crtc = encoder->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct drm_i915_private *dev_priv = dev->dev_private; + int dpll_md_reg; + u32 adpa, dpll_md; + + if (intel_crtc->pipe == 0) + dpll_md_reg = DPLL_A_MD; + else + dpll_md_reg = DPLL_B_MD; + + /* + * Disable separate mode multiplier used when cloning SDVO to CRT + * XXX this needs to be adjusted when we really are cloning + */ + if (IS_I965G(dev)) { + dpll_md = I915_READ(dpll_md_reg); + I915_WRITE(dpll_md_reg, + dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK); + } + + adpa = 0; + if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) + adpa |= ADPA_HSYNC_ACTIVE_HIGH; + if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) + adpa |= ADPA_VSYNC_ACTIVE_HIGH; + + if (intel_crtc->pipe == 0) + adpa |= ADPA_PIPE_A_SELECT; + else + adpa |= ADPA_PIPE_B_SELECT; + + I915_WRITE(ADPA, adpa); +} + +/** + * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence. + * + * Not for i915G/i915GM + * + * \return true if CRT is connected. + * \return false if CRT is disconnected. + */ +static bool intel_crt_detect_hotplug(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 temp; + + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + + temp = I915_READ(PORT_HOTPLUG_EN); + + I915_WRITE(PORT_HOTPLUG_EN, + temp | CRT_HOTPLUG_FORCE_DETECT | (1 << 5)); + + do { + if (!(I915_READ(PORT_HOTPLUG_EN) & CRT_HOTPLUG_FORCE_DETECT)) + break; + msleep(1); + } while (time_after(timeout, jiffies)); + + if ((I915_READ(PORT_HOTPLUG_STAT) & CRT_HOTPLUG_MONITOR_MASK) == + CRT_HOTPLUG_MONITOR_COLOR) + return true; + + return false; +} + +static bool intel_crt_detect_ddc(struct drm_connector *connector) +{ + struct intel_output *intel_output = to_intel_output(connector); + + /* CRT should always be at 0, but check anyway */ + if (intel_output->type != INTEL_OUTPUT_ANALOG) + return false; + + return intel_ddc_probe(intel_output); +} + +static enum drm_connector_status intel_crt_detect(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + + if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) { + if (intel_crt_detect_hotplug(connector)) + return connector_status_connected; + else + return connector_status_disconnected; + } + + if (intel_crt_detect_ddc(connector)) + return connector_status_connected; + + /* TODO use load detect */ + return connector_status_unknown; +} + +static void intel_crt_destroy(struct drm_connector *connector) +{ + struct intel_output *intel_output = to_intel_output(connector); + + intel_i2c_destroy(intel_output->ddc_bus); + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); + kfree(connector); +} + +static int intel_crt_get_modes(struct drm_connector *connector) +{ + struct intel_output *intel_output = to_intel_output(connector); + return intel_ddc_get_modes(intel_output); +} + +static int intel_crt_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t value) +{ + struct drm_device *dev = connector->dev; + + if (property == dev->mode_config.dpms_property && connector->encoder) + intel_crt_dpms(connector->encoder, (uint32_t)(value & 0xf)); + + return 0; +} + +/* + * Routines for controlling stuff on the analog port + */ + +static const struct drm_encoder_helper_funcs intel_crt_helper_funcs = { + .dpms = intel_crt_dpms, + .mode_fixup = intel_crt_mode_fixup, + .prepare = intel_encoder_prepare, + .commit = intel_encoder_commit, + .mode_set = intel_crt_mode_set, +}; + +static const struct drm_connector_funcs intel_crt_connector_funcs = { + .save = intel_crt_save, + .restore = intel_crt_restore, + .detect = intel_crt_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = intel_crt_destroy, + .set_property = intel_crt_set_property, +}; + +static const struct drm_connector_helper_funcs intel_crt_connector_helper_funcs = { + .mode_valid = intel_crt_mode_valid, + .get_modes = intel_crt_get_modes, + .best_encoder = intel_best_encoder, +}; + +void intel_crt_enc_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_funcs intel_crt_enc_funcs = { + .destroy = intel_crt_enc_destroy, +}; + +void intel_crt_init(struct drm_device *dev) +{ + struct drm_connector *connector; + struct intel_output *intel_output; + + intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL); + if (!intel_output) + return; + + connector = &intel_output->base; + drm_connector_init(dev, &intel_output->base, + &intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA); + + drm_encoder_init(dev, &intel_output->enc, &intel_crt_enc_funcs, + DRM_MODE_ENCODER_DAC); + + drm_mode_connector_attach_encoder(&intel_output->base, + &intel_output->enc); + + /* Set up the DDC bus. */ + intel_output->ddc_bus = intel_i2c_create(dev, GPIOA, "CRTDDC_A"); + if (!intel_output->ddc_bus) { + dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration " + "failed.\n"); + return; + } + + intel_output->type = INTEL_OUTPUT_ANALOG; + connector->interlace_allowed = 0; + connector->doublescan_allowed = 0; + + drm_encoder_helper_add(&intel_output->enc, &intel_crt_helper_funcs); + drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs); + + drm_sysfs_connector_add(connector); +} diff --git a/linux-core/intel_display.c b/linux-core/intel_display.c new file mode 100644 index 00000000..5a275c6c --- /dev/null +++ b/linux-core/intel_display.c @@ -0,0 +1,1636 @@ +/* + * Copyright © 2006-2007 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Eric Anholt <eric@anholt.net> + */ + +#include <linux/i2c.h> +#include "drmP.h" +#include "intel_drv.h" +#include "i915_drm.h" +#include "i915_drv.h" + +#include "drm_crtc_helper.h" + +bool intel_pipe_has_type (struct drm_crtc *crtc, int type); + +typedef struct { + /* given values */ + int n; + int m1, m2; + int p1, p2; + /* derived values */ + int dot; + int vco; + int m; + int p; +} intel_clock_t; + +typedef struct { + int min, max; +} intel_range_t; + +typedef struct { + int dot_limit; + int p2_slow, p2_fast; +} intel_p2_t; + +#define INTEL_P2_NUM 2 + +typedef struct { + intel_range_t dot, vco, n, m, m1, m2, p, p1; + intel_p2_t p2; +} intel_limit_t; + +#define I8XX_DOT_MIN 25000 +#define I8XX_DOT_MAX 350000 +#define I8XX_VCO_MIN 930000 +#define I8XX_VCO_MAX 1400000 +#define I8XX_N_MIN 3 +#define I8XX_N_MAX 16 +#define I8XX_M_MIN 96 +#define I8XX_M_MAX 140 +#define I8XX_M1_MIN 18 +#define I8XX_M1_MAX 26 +#define I8XX_M2_MIN 6 +#define I8XX_M2_MAX 16 +#define I8XX_P_MIN 4 +#define I8XX_P_MAX 128 +#define I8XX_P1_MIN 2 +#define I8XX_P1_MAX 33 +#define I8XX_P1_LVDS_MIN 1 +#define I8XX_P1_LVDS_MAX 6 +#define I8XX_P2_SLOW 4 +#define I8XX_P2_FAST 2 +#define I8XX_P2_LVDS_SLOW 14 +#define I8XX_P2_LVDS_FAST 14 /* No fast option */ +#define I8XX_P2_SLOW_LIMIT 165000 + +#define I9XX_DOT_MIN 20000 +#define I9XX_DOT_MAX 400000 +#define I9XX_VCO_MIN 1400000 +#define I9XX_VCO_MAX 2800000 +#define I9XX_N_MIN 3 +#define I9XX_N_MAX 8 +#define I9XX_M_MIN 70 +#define I9XX_M_MAX 120 +#define I9XX_M1_MIN 10 +#define I9XX_M1_MAX 20 +#define I9XX_M2_MIN 5 +#define I9XX_M2_MAX 9 +#define I9XX_P_SDVO_DAC_MIN 5 +#define I9XX_P_SDVO_DAC_MAX 80 +#define I9XX_P_LVDS_MIN 7 +#define I9XX_P_LVDS_MAX 98 +#define I9XX_P1_MIN 1 +#define I9XX_P1_MAX 8 +#define I9XX_P2_SDVO_DAC_SLOW 10 +#define I9XX_P2_SDVO_DAC_FAST 5 +#define I9XX_P2_SDVO_DAC_SLOW_LIMIT 200000 +#define I9XX_P2_LVDS_SLOW 14 +#define I9XX_P2_LVDS_FAST 7 +#define I9XX_P2_LVDS_SLOW_LIMIT 112000 + +#define INTEL_LIMIT_I8XX_DVO_DAC 0 +#define INTEL_LIMIT_I8XX_LVDS 1 +#define INTEL_LIMIT_I9XX_SDVO_DAC 2 +#define INTEL_LIMIT_I9XX_LVDS 3 + +static const intel_limit_t intel_limits[] = { + { /* INTEL_LIMIT_I8XX_DVO_DAC */ + .dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX }, + .vco = { .min = I8XX_VCO_MIN, .max = I8XX_VCO_MAX }, + .n = { .min = I8XX_N_MIN, .max = I8XX_N_MAX }, + .m = { .min = I8XX_M_MIN, .max = I8XX_M_MAX }, + .m1 = { .min = I8XX_M1_MIN, .max = I8XX_M1_MAX }, + .m2 = { .min = I8XX_M2_MIN, .max = I8XX_M2_MAX }, + .p = { .min = I8XX_P_MIN, .max = I8XX_P_MAX }, + .p1 = { .min = I8XX_P1_MIN, .max = I8XX_P1_MAX }, + .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT, + .p2_slow = I8XX_P2_SLOW, .p2_fast = I8XX_P2_FAST }, + }, + { /* INTEL_LIMIT_I8XX_LVDS */ + .dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX }, + .vco = { .min = I8XX_VCO_MIN, .max = I8XX_VCO_MAX }, + .n = { .min = I8XX_N_MIN, .max = I8XX_N_MAX }, + .m = { .min = I8XX_M_MIN, .max = I8XX_M_MAX }, + .m1 = { .min = I8XX_M1_MIN, .max = I8XX_M1_MAX }, + .m2 = { .min = I8XX_M2_MIN, .max = I8XX_M2_MAX }, + .p = { .min = I8XX_P_MIN, .max = I8XX_P_MAX }, + .p1 = { .min = I8XX_P1_LVDS_MIN, .max = I8XX_P1_LVDS_MAX }, + .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT, + .p2_slow = I8XX_P2_LVDS_SLOW, .p2_fast = I8XX_P2_LVDS_FAST }, + }, + { /* INTEL_LIMIT_I9XX_SDVO_DAC */ + .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX }, + .vco = { .min = I9XX_VCO_MIN, .max = I9XX_VCO_MAX }, + .n = { .min = I9XX_N_MIN, .max = I9XX_N_MAX }, + .m = { .min = I9XX_M_MIN, .max = I9XX_M_MAX }, + .m1 = { .min = I9XX_M1_MIN, .max = I9XX_M1_MAX }, + .m2 = { .min = I9XX_M2_MIN, .max = I9XX_M2_MAX }, + .p = { .min = I9XX_P_SDVO_DAC_MIN, .max = I9XX_P_SDVO_DAC_MAX }, + .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX }, + .p2 = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT, + .p2_slow = I9XX_P2_SDVO_DAC_SLOW, .p2_fast = I9XX_P2_SDVO_DAC_FAST }, + }, + { /* INTEL_LIMIT_I9XX_LVDS */ + .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX }, + .vco = { .min = I9XX_VCO_MIN, .max = I9XX_VCO_MAX }, + .n = { .min = I9XX_N_MIN, .max = I9XX_N_MAX }, + .m = { .min = I9XX_M_MIN, .max = I9XX_M_MAX }, + .m1 = { .min = I9XX_M1_MIN, .max = I9XX_M1_MAX }, + .m2 = { .min = I9XX_M2_MIN, .max = I9XX_M2_MAX }, + .p = { .min = I9XX_P_LVDS_MIN, .max = I9XX_P_LVDS_MAX }, + .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX }, + /* The single-channel range is 25-112Mhz, and dual-channel + * is 80-224Mhz. Prefer single channel as much as possible. + */ + .p2 = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT, + .p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_FAST }, + }, +}; + +static const intel_limit_t *intel_limit(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + const intel_limit_t *limit; + + if (IS_I9XX(dev)) { + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) + limit = &intel_limits[INTEL_LIMIT_I9XX_LVDS]; + else + limit = &intel_limits[INTEL_LIMIT_I9XX_SDVO_DAC]; + } else { + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) + limit = &intel_limits[INTEL_LIMIT_I8XX_LVDS]; + else + limit = &intel_limits[INTEL_LIMIT_I8XX_DVO_DAC]; + } + return limit; +} + +/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */ + +static void i8xx_clock(int refclk, intel_clock_t *clock) +{ + clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2); + clock->p = clock->p1 * clock->p2; + clock->vco = refclk * clock->m / (clock->n + 2); + clock->dot = clock->vco / clock->p; +} + +/** Derive the pixel clock for the given refclk and divisors for 9xx chips. */ + +static void i9xx_clock(int refclk, intel_clock_t *clock) +{ + clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2); + clock->p = clock->p1 * clock->p2; + clock->vco = refclk * clock->m / (clock->n + 2); + clock->dot = clock->vco / clock->p; +} + +static void intel_clock(struct drm_device *dev, int refclk, + intel_clock_t *clock) +{ + if (IS_I9XX(dev)) + return i9xx_clock (refclk, clock); + else + return i8xx_clock (refclk, clock); +} + +/** + * Returns whether any output on the specified pipe is of the specified type + */ +bool intel_pipe_has_type (struct drm_crtc *crtc, int type) +{ + struct drm_device *dev = crtc->dev; + struct drm_mode_config *mode_config = &dev->mode_config; + struct drm_connector *l_entry; + + list_for_each_entry(l_entry, &mode_config->connector_list, head) { + if (l_entry->encoder && + l_entry->encoder->crtc == crtc) { + struct intel_output *intel_output = to_intel_output(l_entry); + if (intel_output->type == type) + return true; + } + } + return false; +} + +#define INTELPllInvalid(s) { /* ErrorF (s) */; return false; } +/** + * Returns whether the given set of divisors are valid for a given refclk with + * the given connectors. + */ + +static bool intel_PLL_is_valid(struct drm_crtc *crtc, intel_clock_t *clock) +{ + const intel_limit_t *limit = intel_limit (crtc); + + if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1) + INTELPllInvalid ("p1 out of range\n"); + if (clock->p < limit->p.min || limit->p.max < clock->p) + INTELPllInvalid ("p out of range\n"); + if (clock->m2 < limit->m2.min || limit->m2.max < clock->m2) + INTELPllInvalid ("m2 out of range\n"); + if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1) + INTELPllInvalid ("m1 out of range\n"); + if (clock->m1 <= clock->m2) + INTELPllInvalid ("m1 <= m2\n"); + if (clock->m < limit->m.min || limit->m.max < clock->m) + INTELPllInvalid ("m out of range\n"); + if (clock->n < limit->n.min || limit->n.max < clock->n) + INTELPllInvalid ("n out of range\n"); + if (clock->vco < limit->vco.min || limit->vco.max < clock->vco) + INTELPllInvalid ("vco out of range\n"); + /* XXX: We may need to be checking "Dot clock" depending on the multiplier, + * connector, etc., rather than just a single range. + */ + if (clock->dot < limit->dot.min || limit->dot.max < clock->dot) + INTELPllInvalid ("dot out of range\n"); + + return true; +} + +/** + * Returns a set of divisors for the desired target clock with the given + * refclk, or FALSE. The returned values represent the clock equation: + * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. + */ +static bool intel_find_best_PLL(struct drm_crtc *crtc, int target, + int refclk, intel_clock_t *best_clock) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + intel_clock_t clock; + const intel_limit_t *limit = intel_limit(crtc); + int err = target; + + if (IS_I9XX(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && + (I915_READ(LVDS) & LVDS_PORT_EN) != 0) { + /* + * For LVDS, if the panel is on, just rely on its current + * settings for dual-channel. We haven't figured out how to + * reliably set up different single/dual channel state, if we + * even can. + */ + if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) == + LVDS_CLKB_POWER_UP) + clock.p2 = limit->p2.p2_fast; + else + clock.p2 = limit->p2.p2_slow; + } else { + if (target < limit->p2.dot_limit) + clock.p2 = limit->p2.p2_slow; + else + clock.p2 = limit->p2.p2_fast; + } + + memset (best_clock, 0, sizeof (*best_clock)); + + for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) { + for (clock.m2 = limit->m2.min; clock.m2 < clock.m1 && + clock.m2 <= limit->m2.max; clock.m2++) { + for (clock.n = limit->n.min; clock.n <= limit->n.max; + clock.n++) { + for (clock.p1 = limit->p1.min; + clock.p1 <= limit->p1.max; clock.p1++) { + int this_err; + + intel_clock(dev, refclk, &clock); + + if (!intel_PLL_is_valid(crtc, &clock)) + continue; + + this_err = abs(clock.dot - target); + if (this_err < err) { + *best_clock = clock; + err = this_err; + } + } + } + } + } + + return (err != target); +} + +void +intel_set_vblank(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_crtc *crtc; + struct intel_crtc *intel_crtc; + int vbl_pipe = 0; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + intel_crtc = to_intel_crtc(crtc); + + if (crtc->enabled) + vbl_pipe |= (1<<intel_crtc->pipe); + } + + dev_priv->vblank_pipe = vbl_pipe; + i915_enable_interrupt(dev); +} +void +intel_wait_for_vblank(struct drm_device *dev) +{ + /* Wait for 20ms, i.e. one cycle at 50hz. */ + udelay(20000); +} + +void +intel_pipe_set_base(struct drm_crtc *crtc, int x, int y) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_master_private *master_priv; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_framebuffer *intel_fb; + struct drm_i915_gem_object *obj_priv; + struct drm_gem_object *obj; + int pipe = intel_crtc->pipe; + unsigned long Start, Offset; + int dspbase = (pipe == 0 ? DSPAADDR : DSPBADDR); + int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF); + int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE; + int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; + u32 dspcntr; + + /* no fb bound */ + if (!crtc->fb) { + DRM_DEBUG("No FB bound\n"); + return; + } + + intel_fb = to_intel_framebuffer(crtc->fb); + + obj = intel_fb->base.mm_private; + obj_priv = obj->driver_private; + + Start = obj_priv->gtt_offset; + Offset = y * crtc->fb->pitch + x * (crtc->fb->bits_per_pixel / 8); + + I915_WRITE(dspstride, crtc->fb->pitch); + + dspcntr = I915_READ(dspcntr_reg); + switch (crtc->fb->bits_per_pixel) { + case 8: + dspcntr |= DISPPLANE_8BPP; + break; + case 16: + if (crtc->fb->depth == 15) + dspcntr |= DISPPLANE_15_16BPP; + else + dspcntr |= DISPPLANE_16BPP; + break; + case 24: + case 32: + dspcntr |= DISPPLANE_32BPP_NO_ALPHA; + break; + default: + DRM_ERROR("Unknown color depth\n"); + return; + } + I915_WRITE(dspcntr_reg, dspcntr); + + DRM_DEBUG("Writing base %08lX %08lX %d %d\n", Start, Offset, x, y); + if (IS_I965G(dev)) { + I915_WRITE(dspbase, Offset); + I915_READ(dspbase); + I915_WRITE(dspsurf, Start); + I915_READ(dspsurf); + } else { + I915_WRITE(dspbase, Start + Offset); + I915_READ(dspbase); + } + + + if (!dev->primary->master) + return; + + master_priv = dev->primary->master->driver_priv; + if (!master_priv->sarea_priv) + return; + + switch (pipe) { + case 0: + master_priv->sarea_priv->planeA_x = x; + master_priv->sarea_priv->planeA_y = y; + break; + case 1: + master_priv->sarea_priv->planeB_x = x; + master_priv->sarea_priv->planeB_y = y; + break; + default: + DRM_ERROR("Can't update pipe %d in SAREA\n", pipe); + break; + } +} + + + +/** + * Sets the power management mode of the pipe and plane. + * + * This code should probably grow support for turning the cursor off and back + * on appropriately at the same time as we're turning the pipe off/on. + */ +static void intel_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_master_private *master_priv; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe; + int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; + int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; + int dspbase_reg = (pipe == 0) ? DSPAADDR : DSPBADDR; + int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; + u32 temp; + bool enabled; + + /* XXX: When our outputs are all unaware of DPMS modes other than off + * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC. + */ + switch (mode) { + case DRM_MODE_DPMS_ON: + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + /* 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 DRM_MODE_DPMS_OFF: + /* Give the overlay scaler a chance to disable if it's on this pipe */ + //intel_crtc_dpms_video(crtc, FALSE); TODO + + /* Disable the VGA plane that we never use */ + I915_WRITE(VGACNTRL, VGA_DISP_DISABLE); + + /* Disable display plane */ + temp = I915_READ(dspcntr_reg); + if ((temp & DISPLAY_PLANE_ENABLE) != 0) { + I915_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); + I915_READ(dspbase_reg); + } + + if (!IS_I9XX(dev)) { + /* Wait for vblank for the disable to take effect */ + intel_wait_for_vblank(dev); + } + + /* Next, disable display pipes */ + temp = I915_READ(pipeconf_reg); + if ((temp & PIPEACONF_ENABLE) != 0) { + I915_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE); + I915_READ(pipeconf_reg); + } + + /* Wait for vblank for the disable to take effect. */ + intel_wait_for_vblank(dev); + + temp = I915_READ(dpll_reg); + if ((temp & DPLL_VCO_ENABLE) != 0) { + I915_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE); + I915_READ(dpll_reg); + } + + /* Wait for the clocks to turn off. */ + udelay(150); + break; + } + + if (!dev->primary->master) + return; + + master_priv = dev->primary->master->driver_priv; + if (!master_priv->sarea_priv) + return; + + enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF; + + switch (pipe) { + case 0: + master_priv->sarea_priv->planeA_w = enabled ? crtc->mode.hdisplay : 0; + master_priv->sarea_priv->planeA_h = enabled ? crtc->mode.vdisplay : 0; + break; + case 1: + master_priv->sarea_priv->planeB_w = enabled ? crtc->mode.hdisplay : 0; + master_priv->sarea_priv->planeB_h = enabled ? crtc->mode.vdisplay : 0; + break; + default: + DRM_ERROR("Can't update pipe %d in SAREA\n", pipe); + break; + } + + intel_crtc->dpms_mode = mode; +} + +static void intel_crtc_prepare (struct drm_crtc *crtc) +{ + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); +} + +static void intel_crtc_commit (struct drm_crtc *crtc) +{ + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); +} + +void intel_encoder_prepare (struct drm_encoder *encoder) +{ + struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; + /* lvds has its own version of prepare see intel_lvds_prepare */ + encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF); +} + +void intel_encoder_commit (struct drm_encoder *encoder) +{ + struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; + /* lvds has its own version of commit see intel_lvds_commit */ + encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); +} + +static bool intel_crtc_mode_fixup(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + + +/** Returns the core display clock speed for i830 - i945 */ +static int intel_get_core_clock_speed(struct drm_device *dev) +{ + + /* Core clock values taken from the published datasheets. + * The 830 may go up to 166 Mhz, which we should check. + */ + if (IS_I945G(dev)) + return 400000; + else if (IS_I915G(dev)) + return 333000; + else if (IS_I945GM(dev) || IS_845G(dev)) + return 200000; + else if (IS_I915GM(dev)) { + u16 gcfgc = 0; + + pci_read_config_word(dev->pdev, GCFGC, &gcfgc); + + if (gcfgc & GC_LOW_FREQUENCY_ENABLE) + return 133000; + else { + switch (gcfgc & GC_DISPLAY_CLOCK_MASK) { + case GC_DISPLAY_CLOCK_333_MHZ: + return 333000; + default: + case GC_DISPLAY_CLOCK_190_200_MHZ: + return 190000; + } + } + } else if (IS_I865G(dev)) + return 266000; + else if (IS_I855(dev)) { +#if 0 + PCITAG bridge = pciTag(0, 0, 0); /* This is always the host bridge */ + u16 hpllcc = pciReadWord(bridge, HPLLCC); + +#endif + u16 hpllcc = 0; + /* Assume that the hardware is in the high speed state. This + * should be the default. + */ + switch (hpllcc & GC_CLOCK_CONTROL_MASK) { + case GC_CLOCK_133_200: + case GC_CLOCK_100_200: + return 200000; + case GC_CLOCK_166_250: + return 250000; + case GC_CLOCK_100_133: + return 133000; + } + } else /* 852, 830 */ + return 133000; + + return 0; /* Silence gcc warning */ +} + + +/** + * Return the pipe currently connected to the panel fitter, + * or -1 if the panel fitter is not present or not in use + */ +static int intel_panel_fitter_pipe (struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 pfit_control; + + /* i830 doesn't have a panel fitter */ + if (IS_I830(dev)) + return -1; + + pfit_control = I915_READ(PFIT_CONTROL); + + /* See if the panel fitter is in use */ + if ((pfit_control & PFIT_ENABLE) == 0) + return -1; + + /* 965 can place panel fitter on either pipe */ + if (IS_I965G(dev)) + return (pfit_control >> 29) & 0x3; + + /* older chips can only use pipe 1 */ + return 1; +} + +static void intel_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe; + int fp_reg = (pipe == 0) ? FPA0 : FPB0; + int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; + int dpll_md_reg = (intel_crtc->pipe == 0) ? DPLL_A_MD : DPLL_B_MD; + int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; + int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; + int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B; + int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B; + int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B; + int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B; + int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B; + int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B; + int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE; + int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS; + int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; + int refclk; + intel_clock_t clock; + u32 dpll = 0, fp = 0, dspcntr, pipeconf; + bool ok, is_sdvo = false, is_dvo = false; + bool is_crt = false, is_lvds = false, is_tv = false; + struct drm_mode_config *mode_config = &dev->mode_config; + struct drm_connector *connector; + + list_for_each_entry(connector, &mode_config->connector_list, head) { + struct intel_output *intel_output = to_intel_output(connector); + + if (!connector->encoder || connector->encoder->crtc != crtc) + continue; + + switch (intel_output->type) { + case INTEL_OUTPUT_LVDS: + is_lvds = true; + break; + case INTEL_OUTPUT_SDVO: + is_sdvo = true; + break; + case INTEL_OUTPUT_DVO: + is_dvo = true; + break; + case INTEL_OUTPUT_TVOUT: + is_tv = true; + break; + case INTEL_OUTPUT_ANALOG: + is_crt = true; + break; + } + } + + if (IS_I9XX(dev)) { + refclk = 96000; + } else { + refclk = 48000; + } + + ok = intel_find_best_PLL(crtc, adjusted_mode->clock, refclk, &clock); + if (!ok) { + DRM_ERROR("Couldn't find PLL settings for mode!\n"); + return; + } + + fp = clock.n << 16 | clock.m1 << 8 | clock.m2; + + dpll = DPLL_VGA_MODE_DIS; + if (IS_I9XX(dev)) { + if (is_lvds) + dpll |= DPLLB_MODE_LVDS; + else + dpll |= DPLLB_MODE_DAC_SERIAL; + if (is_sdvo) { + dpll |= DPLL_DVO_HIGH_SPEED; + if (IS_I945G(dev) || IS_I945GM(dev)) { + int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock; + dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES; + } + } + + /* compute bitmask from p1 value */ + dpll |= (1 << (clock.p1 - 1)) << 16; + switch (clock.p2) { + case 5: + dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5; + break; + case 7: + dpll |= DPLLB_LVDS_P2_CLOCK_DIV_7; + break; + case 10: + dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_10; + break; + case 14: + dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14; + break; + } + if (IS_I965G(dev)) + dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT); + } else { + if (is_lvds) { + dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; + } else { + if (clock.p1 == 2) + dpll |= PLL_P1_DIVIDE_BY_TWO; + else + dpll |= (clock.p1 - 2) << DPLL_FPA01_P1_POST_DIV_SHIFT; + if (clock.p2 == 4) + dpll |= PLL_P2_DIVIDE_BY_4; + } + } + + if (is_tv) { + /* XXX: just matching BIOS for now */ +/* dpll |= PLL_REF_INPUT_TVCLKINBC; */ + dpll |= 3; + } +#if 0 + else if (is_lvds) + dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; +#endif + else + dpll |= PLL_REF_INPUT_DREFCLK; + + /* setup pipeconf */ + pipeconf = I915_READ(pipeconf_reg); + + /* Set up the display plane register */ + dspcntr = DISPPLANE_GAMMA_ENABLE; + + if (pipe == 0) + dspcntr |= DISPPLANE_SEL_PIPE_A; + else + dspcntr |= DISPPLANE_SEL_PIPE_B; + + if (pipe == 0 && !IS_I965G(dev)) { + /* Enable pixel doubling when the dot clock is > 90% of the (display) + * core speed. + * + * XXX: No double-wide on 915GM pipe B. Is that the only reason for the + * pipe == 0 check? + */ + if (mode->clock > intel_get_core_clock_speed(dev) * 9 / 10) + pipeconf |= PIPEACONF_DOUBLE_WIDE; + else + pipeconf &= ~PIPEACONF_DOUBLE_WIDE; + } + + dspcntr |= DISPLAY_PLANE_ENABLE; + pipeconf |= PIPEACONF_ENABLE; + dpll |= DPLL_VCO_ENABLE; + + + /* Disable the panel fitter if it was on our pipe */ + if (intel_panel_fitter_pipe(dev) == pipe) + I915_WRITE(PFIT_CONTROL, 0); + + DRM_DEBUG("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B'); + drm_mode_debug_printmodeline(mode); + +#if 0 + if (!xf86ModesEqual(mode, adjusted_mode)) { + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "Adjusted mode for pipe %c:\n", pipe == 0 ? 'A' : 'B'); + xf86PrintModeline(pScrn->scrnIndex, mode); + } + i830PrintPll("chosen", &clock); +#endif + + if (dpll & DPLL_VCO_ENABLE) { + I915_WRITE(fp_reg, fp); + I915_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE); + I915_READ(dpll_reg); + udelay(150); + } + + /* The LVDS pin pair needs to be on before the DPLLs are enabled. + * This is an exception to the general rule that mode_set doesn't turn + * things on. + */ + if (is_lvds) { + u32 lvds = I915_READ(LVDS); + + lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP | LVDS_PIPEB_SELECT; + /* Set the B0-B3 data pairs corresponding to whether we're going to + * set the DPLLs for dual-channel mode or not. + */ + if (clock.p2 == 7) + lvds |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; + else + lvds &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP); + + /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP) + * appropriately here, but we need to look more thoroughly into how + * panels behave in the two modes. + */ + + I915_WRITE(LVDS, lvds); + I915_READ(LVDS); + } + + I915_WRITE(fp_reg, fp); + I915_WRITE(dpll_reg, dpll); + I915_READ(dpll_reg); + /* Wait for the clocks to stabilize. */ + udelay(150); + + if (IS_I965G(dev)) { + int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock; + I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) | + ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT)); + } else { + /* write it again -- the BIOS does, after all */ + I915_WRITE(dpll_reg, dpll); + } + I915_READ(dpll_reg); + /* Wait for the clocks to stabilize. */ + udelay(150); + + I915_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) | + ((adjusted_mode->crtc_htotal - 1) << 16)); + I915_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | + ((adjusted_mode->crtc_hblank_end - 1) << 16)); + I915_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | + ((adjusted_mode->crtc_hsync_end - 1) << 16)); + I915_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | + ((adjusted_mode->crtc_vtotal - 1) << 16)); + I915_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | + ((adjusted_mode->crtc_vblank_end - 1) << 16)); + I915_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | + ((adjusted_mode->crtc_vsync_end - 1) << 16)); + /* pipesrc and dspsize control the size that is scaled from, which should + * always be the user's requested size. + */ + I915_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1)); + I915_WRITE(dsppos_reg, 0); + I915_WRITE(pipesrc_reg, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1)); + I915_WRITE(pipeconf_reg, pipeconf); + I915_READ(pipeconf_reg); + + intel_wait_for_vblank(dev); + + I915_WRITE(dspcntr_reg, dspcntr); + + /* Flush the plane changes */ + intel_pipe_set_base(crtc, x, y); + + intel_set_vblank(dev); + + intel_wait_for_vblank(dev); +} + +/** Loads the palette/gamma unit for the CRTC with the prepared values */ +void intel_crtc_load_lut(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int palreg = (intel_crtc->pipe == 0) ? PALETTE_A : PALETTE_B; + int i; + + /* The clocks have to be on to load the palette. */ + if (!crtc->enabled) + return; + + for (i = 0; i < 256; i++) { + I915_WRITE(palreg + 4 * i, + (intel_crtc->lut_r[i] << 16) | + (intel_crtc->lut_g[i] << 8) | + intel_crtc->lut_b[i]); + } +} + +static int intel_crtc_cursor_set(struct drm_crtc *crtc, + struct drm_file *file_priv, + uint32_t handle, + uint32_t width, uint32_t height) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct drm_gem_object *bo; + struct drm_i915_gem_object *obj_priv; + int pipe = intel_crtc->pipe; + uint32_t control = (pipe == 0) ? CURACNTR : CURBCNTR; + uint32_t base = (pipe == 0) ? CURABASE : CURBBASE; + uint32_t temp; + size_t addr; + + DRM_DEBUG("\n"); + + /* if we want to turn of the cursor ignore width and height */ + if (!handle) { + DRM_DEBUG("cursor off\n"); + /* turn of the cursor */ + temp = 0; + temp |= CURSOR_MODE_DISABLE; + + I915_WRITE(control, temp); + I915_WRITE(base, 0); + return 0; + } + + /* Currently we only support 64x64 cursors */ + if (width != 64 || height != 64) { + DRM_ERROR("we currently only support 64x64 cursors\n"); + return -EINVAL; + } + + bo = drm_gem_object_lookup(dev, file_priv, handle); + if (!bo) + return -ENOENT; + + obj_priv = bo->driver_private; + + if (bo->size < width * height * 4) { + DRM_ERROR("buffer is to small\n"); + return -ENOMEM; + } + + if (dev_priv->cursor_needs_physical) { + addr = dev->agp->base + obj_priv->gtt_offset; + } else { + addr = obj_priv->gtt_offset; + } + + intel_crtc->cursor_addr = addr; + temp = 0; + /* set the pipe for the cursor */ + temp |= (pipe << 28); + temp |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE; + + I915_WRITE(control, temp); + I915_WRITE(base, addr); + + return 0; +} + +static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe; + uint32_t temp = 0; + uint32_t adder; + + if (x < 0) { + temp |= (CURSOR_POS_SIGN << CURSOR_X_SHIFT); + x = -x; + } + if (y < 0) { + temp |= (CURSOR_POS_SIGN << CURSOR_Y_SHIFT); + y = -y; + } + + temp |= ((x & CURSOR_POS_MASK) << CURSOR_X_SHIFT); + temp |= ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT); + + adder = intel_crtc->cursor_addr; + I915_WRITE((pipe == 0) ? CURAPOS : CURBPOS, temp); + I915_WRITE((pipe == 0) ? CURABASE : CURBBASE, adder); + + return 0; +} + +/** Sets the color ramps on behalf of RandR */ +void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, + u16 blue, int regno) +{ + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + intel_crtc->lut_r[regno] = red >> 8; + intel_crtc->lut_g[regno] = green >> 8; + intel_crtc->lut_b[regno] = blue >> 8; +} + +static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, uint32_t size) +{ + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int i; + + if (size != 256) + return; + + for (i = 0; i < 256; i++) { + intel_crtc->lut_r[i] = red[i] >> 8; + intel_crtc->lut_g[i] = green[i] >> 8; + intel_crtc->lut_b[i] = blue[i] >> 8; + } + + intel_crtc_load_lut(crtc); +} + +/** + * Get a pipe with a simple mode set on it for doing load-based monitor + * detection. + * + * It will be up to the load-detect code to adjust the pipe as appropriate for + * its requirements. The pipe will be connected to no other outputs. + * + * Currently this code will only succeed if there is a pipe with no outputs + * configured for it. In the future, it could choose to temporarily disable + * some outputs to free up a pipe for its use. + * + * \return crtc, or NULL if no pipes are available. + */ + +/* VESA 640x480x72Hz mode to set on the pipe */ +static struct drm_display_mode load_detect_mode = { + DRM_MODE("640x480", DRM_MODE_TYPE_DEFAULT, 31500, 640, 664, + 704, 832, 0, 480, 489, 491, 520, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), +}; + +struct drm_crtc *intel_get_load_detect_pipe(struct intel_output *intel_output, + struct drm_display_mode *mode, + int *dpms_mode) +{ + struct intel_crtc *intel_crtc; + struct drm_crtc *possible_crtc; + struct drm_crtc *supported_crtc =NULL; + struct drm_encoder *encoder = &intel_output->enc; + struct drm_crtc *crtc = NULL; + struct drm_device *dev = encoder->dev; + struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; + struct drm_crtc_helper_funcs *crtc_funcs; + int i = -1; + + /* + * Algorithm gets a little messy: + * - if the connector already has an assigned crtc, use it (but make + * sure it's on first) + * - try to find the first unused crtc that can drive this connector, + * and use that if we find one + * - if there are no unused crtcs available, try to use the first + * one we found that supports the connector + */ + + /* See if we already have a CRTC for this connector */ + if (encoder->crtc) { + crtc = encoder->crtc; + /* Make sure the crtc and connector are running */ + intel_crtc = to_intel_crtc(crtc); + *dpms_mode = intel_crtc->dpms_mode; + if (intel_crtc->dpms_mode != DRM_MODE_DPMS_ON) { + crtc_funcs = crtc->helper_private; + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); + encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); + } + return crtc; + } + + /* Find an unused one (if possible) */ + list_for_each_entry(possible_crtc, &dev->mode_config.crtc_list, head) { + i++; + if (!(encoder->possible_crtcs & (1 << i))) + continue; + if (!possible_crtc->enabled) { + crtc = possible_crtc; + break; + } + if (!supported_crtc) + supported_crtc = possible_crtc; + } + + /* + * If we didn't find an unused CRTC, don't use any. + */ + if (!crtc) { + return NULL; + } + + encoder->crtc = crtc; + intel_output->load_detect_temp = true; + + intel_crtc = to_intel_crtc(crtc); + *dpms_mode = intel_crtc->dpms_mode; + + if (!crtc->enabled) { + if (!mode) + mode = &load_detect_mode; + drm_crtc_helper_set_mode(crtc, mode, 0, 0); + } else { + if (intel_crtc->dpms_mode != DRM_MODE_DPMS_ON) { + crtc_funcs = crtc->helper_private; + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); + } + + /* Add this connector to the crtc */ + encoder_funcs->mode_set(encoder, &crtc->mode, &crtc->mode); + encoder_funcs->commit(encoder); + } + /* let the connector get through one full cycle before testing */ + intel_wait_for_vblank(dev); + + return crtc; +} + +void intel_release_load_detect_pipe(struct intel_output *intel_output, int dpms_mode) +{ + struct drm_encoder *encoder = &intel_output->enc; + struct drm_device *dev = encoder->dev; + struct drm_crtc *crtc = encoder->crtc; + struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + + if (intel_output->load_detect_temp) { + encoder->crtc = NULL; + intel_output->load_detect_temp = false; + crtc->enabled = drm_helper_crtc_in_use(crtc); + drm_helper_disable_unused_functions(dev); + } + + /* Switch crtc and output back off if necessary */ + if (crtc->enabled && dpms_mode != DRM_MODE_DPMS_ON) { + if (encoder->crtc == crtc) + encoder_funcs->dpms(encoder, dpms_mode); + crtc_funcs->dpms(crtc, dpms_mode); + } +} + +/* Returns the clock of the currently programmed mode of the given pipe. */ +static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe; + u32 dpll = I915_READ((pipe == 0) ? DPLL_A : DPLL_B); + u32 fp; + intel_clock_t clock; + + if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) + fp = I915_READ((pipe == 0) ? FPA0 : FPB0); + else + fp = I915_READ((pipe == 0) ? FPA1 : FPB1); + + clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT; + clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT; + clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT; + if (IS_I9XX(dev)) { + clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK) >> + DPLL_FPA01_P1_POST_DIV_SHIFT); + + switch (dpll & DPLL_MODE_MASK) { + case DPLLB_MODE_DAC_SERIAL: + clock.p2 = dpll & DPLL_DAC_SERIAL_P2_CLOCK_DIV_5 ? + 5 : 10; + break; + case DPLLB_MODE_LVDS: + clock.p2 = dpll & DPLLB_LVDS_P2_CLOCK_DIV_7 ? + 7 : 14; + break; + default: + DRM_DEBUG("Unknown DPLL mode %08x in programmed " + "mode\n", (int)(dpll & DPLL_MODE_MASK)); + return 0; + } + + /* XXX: Handle the 100Mhz refclk */ + i9xx_clock(96000, &clock); + } else { + bool is_lvds = (pipe == 1) && (I915_READ(LVDS) & LVDS_PORT_EN); + + if (is_lvds) { + clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS) >> + DPLL_FPA01_P1_POST_DIV_SHIFT); + clock.p2 = 14; + + if ((dpll & PLL_REF_INPUT_MASK) == + PLLB_REF_INPUT_SPREADSPECTRUMIN) { + /* XXX: might not be 66MHz */ + i8xx_clock(66000, &clock); + } else + i8xx_clock(48000, &clock); + } else { + if (dpll & PLL_P1_DIVIDE_BY_TWO) + clock.p1 = 2; + else { + clock.p1 = ((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830) >> + DPLL_FPA01_P1_POST_DIV_SHIFT) + 2; + } + if (dpll & PLL_P2_DIVIDE_BY_4) + clock.p2 = 4; + else + clock.p2 = 2; + + i8xx_clock(48000, &clock); + } + } + + /* XXX: It would be nice to validate the clocks, but we can't reuse + * i830PllIsValid() because it relies on the xf86_config connector + * configuration being accurate, which it isn't necessarily. + */ + + return clock.dot; +} + +/** Returns the currently programmed mode of the given pipe. */ +struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev, + struct drm_crtc *crtc) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe; + struct drm_display_mode *mode; + int htot = I915_READ((pipe == 0) ? HTOTAL_A : HTOTAL_B); + int hsync = I915_READ((pipe == 0) ? HSYNC_A : HSYNC_B); + int vtot = I915_READ((pipe == 0) ? VTOTAL_A : VTOTAL_B); + int vsync = I915_READ((pipe == 0) ? VSYNC_A : VSYNC_B); + + mode = kzalloc(sizeof(*mode), GFP_KERNEL); + if (!mode) + return NULL; + + mode->clock = intel_crtc_clock_get(dev, crtc); + mode->hdisplay = (htot & 0xffff) + 1; + mode->htotal = ((htot & 0xffff0000) >> 16) + 1; + mode->hsync_start = (hsync & 0xffff) + 1; + mode->hsync_end = ((hsync & 0xffff0000) >> 16) + 1; + mode->vdisplay = (vtot & 0xffff) + 1; + mode->vtotal = ((vtot & 0xffff0000) >> 16) + 1; + mode->vsync_start = (vsync & 0xffff) + 1; + mode->vsync_end = ((vsync & 0xffff0000) >> 16) + 1; + + drm_mode_set_name(mode); + drm_mode_set_crtcinfo(mode, 0); + + return mode; +} + +static void intel_crtc_destroy(struct drm_crtc *crtc) +{ + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + drm_crtc_cleanup(crtc); + kfree(intel_crtc); +} + +static const struct drm_crtc_helper_funcs intel_helper_funcs = { + .dpms = intel_crtc_dpms, + .mode_fixup = intel_crtc_mode_fixup, + .mode_set = intel_crtc_mode_set, + .mode_set_base = intel_pipe_set_base, + .prepare = intel_crtc_prepare, + .commit = intel_crtc_commit, +}; + +static const struct drm_crtc_funcs intel_crtc_funcs = { + .cursor_set = intel_crtc_cursor_set, + .cursor_move = intel_crtc_cursor_move, + .gamma_set = intel_crtc_gamma_set, + .set_config = drm_crtc_helper_set_config, + .destroy = intel_crtc_destroy, +}; + + +void intel_crtc_init(struct drm_device *dev, int pipe) +{ + struct intel_crtc *intel_crtc; + int i; + + intel_crtc = kzalloc(sizeof(struct intel_crtc) + (INTELFB_CONN_LIMIT * sizeof(struct drm_connector *)), GFP_KERNEL); + if (intel_crtc == NULL) + return; + + drm_crtc_init(dev, &intel_crtc->base, &intel_crtc_funcs); + + drm_mode_crtc_set_gamma_size(&intel_crtc->base, 256); + intel_crtc->pipe = pipe; + for (i = 0; i < 256; i++) { + intel_crtc->lut_r[i] = i; + intel_crtc->lut_g[i] = i; + intel_crtc->lut_b[i] = i; + } + + intel_crtc->cursor_addr = 0; + intel_crtc->dpms_mode = DRM_MODE_DPMS_OFF; + drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs); + + intel_crtc->mode_set.crtc = &intel_crtc->base; + intel_crtc->mode_set.connectors = (struct drm_connector **)(intel_crtc + 1); + intel_crtc->mode_set.num_connectors = 0; + + if (i915_fbpercrtc) { + + + + } +} + +struct drm_crtc *intel_get_crtc_from_pipe(struct drm_device *dev, int pipe) +{ + struct drm_crtc *crtc = NULL; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + if (intel_crtc->pipe == pipe) + break; + } + return crtc; +} + +int intel_connector_clones(struct drm_device *dev, int type_mask) +{ + int index_mask = 0; + struct drm_connector *connector; + int entry = 0; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + struct intel_output *intel_output = to_intel_output(connector); + if (type_mask & (1 << intel_output->type)) + index_mask |= (1 << entry); + entry++; + } + return index_mask; +} + + +static void intel_setup_outputs(struct drm_device *dev) +{ + struct drm_connector *connector; + + intel_crt_init(dev); + + /* Set up integrated LVDS */ + if (IS_MOBILE(dev) && !IS_I830(dev)) + intel_lvds_init(dev); + + if (IS_I9XX(dev)) { + intel_sdvo_init(dev, SDVOB); + intel_sdvo_init(dev, SDVOC); + } else + intel_dvo_init(dev); + + if (IS_I9XX(dev) && !IS_I915G(dev)) + intel_tv_init(dev); + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + struct intel_output *intel_output = to_intel_output(connector); + struct drm_encoder *encoder = &intel_output->enc; + int crtc_mask = 0, clone_mask = 0; + + /* valid crtcs */ + switch(intel_output->type) { + case INTEL_OUTPUT_DVO: + case INTEL_OUTPUT_SDVO: + crtc_mask = ((1 << 0)| + (1 << 1)); + clone_mask = ((1 << INTEL_OUTPUT_ANALOG) | + (1 << INTEL_OUTPUT_DVO) | + (1 << INTEL_OUTPUT_SDVO)); + break; + case INTEL_OUTPUT_ANALOG: + crtc_mask = ((1 << 0)| + (1 << 1)); + clone_mask = ((1 << INTEL_OUTPUT_ANALOG) | + (1 << INTEL_OUTPUT_DVO) | + (1 << INTEL_OUTPUT_SDVO)); + break; + case INTEL_OUTPUT_LVDS: + crtc_mask = (1 << 1); + clone_mask = (1 << INTEL_OUTPUT_LVDS); + break; + case INTEL_OUTPUT_TVOUT: + crtc_mask = ((1 << 0) | + (1 << 1)); + clone_mask = (1 << INTEL_OUTPUT_TVOUT); + break; + } + encoder->possible_crtcs = crtc_mask; + encoder->possible_clones = intel_connector_clones(dev, clone_mask); + } +} + +static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb) +{ + struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); + struct drm_device *dev = fb->dev; + + if (fb->fbdev) + intelfb_remove(dev, fb); + + drm_framebuffer_cleanup(fb); + drm_gem_object_unreference(fb->mm_private); + + kfree(intel_fb); +} + +static int intel_user_framebuffer_create_handle(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned int *handle) +{ + struct drm_gem_object *object = fb->mm_private; + + return drm_gem_handle_create(file_priv, object, handle); +} + +static const struct drm_framebuffer_funcs intel_fb_funcs = { + .destroy = intel_user_framebuffer_destroy, + .create_handle = intel_user_framebuffer_create_handle, +}; + +struct drm_framebuffer * +intel_framebuffer_create(struct drm_device *dev, + struct drm_mode_fb_cmd *mode_cmd, + void *mm_private) +{ + struct intel_framebuffer *intel_fb; + + intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL); + if (!intel_fb) + return NULL; + + if (!drm_framebuffer_init(dev, &intel_fb->base, &intel_fb_funcs)) + return NULL; + + drm_helper_mode_fill_fb_struct(&intel_fb->base, mode_cmd, mm_private); + + return &intel_fb->base; +} + + +static struct drm_framebuffer * +intel_user_framebuffer_create(struct drm_device *dev, + struct drm_file *filp, + struct drm_mode_fb_cmd *mode_cmd) +{ + struct drm_gem_object *obj; + + obj = drm_gem_object_lookup(dev, filp, mode_cmd->handle); + if (!obj) + return NULL; + + return intel_framebuffer_create(dev, mode_cmd, obj); +} + +static int intel_insert_new_fb(struct drm_device *dev, struct drm_file *file_priv, + struct drm_framebuffer *fb, struct drm_mode_fb_cmd *mode_cmd) +{ + struct intel_framebuffer *intel_fb; + struct drm_gem_object *obj; + struct drm_crtc *crtc; + + intel_fb = to_intel_framebuffer(fb); + + mutex_lock(&dev->struct_mutex); + obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle); + + if (!obj) { + mutex_unlock(&dev->struct_mutex); + return -EINVAL; + } + drm_gem_object_unreference(intel_fb->base.mm_private); + drm_helper_mode_fill_fb_struct(fb, mode_cmd, obj); + mutex_unlock(&dev->struct_mutex); + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (crtc->fb == fb) { + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + crtc_funcs->mode_set_base(crtc, crtc->x, crtc->y); + } + } + return 0; +} + +static const struct drm_mode_config_funcs intel_mode_funcs = { + .resize_fb = intel_insert_new_fb, + .fb_create = intel_user_framebuffer_create, + .fb_changed = intelfb_probe, +}; + +void intel_modeset_init(struct drm_device *dev) +{ + int num_pipe; + int i; + + drm_mode_config_init(dev); + + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + + dev->mode_config.funcs = (void *)&intel_mode_funcs; + + if (IS_I965G(dev)) { + dev->mode_config.max_width = 8192; + dev->mode_config.max_height = 8192; + } else { + dev->mode_config.max_width = 2048; + dev->mode_config.max_height = 2048; + } + + /* set memory base */ + if (IS_I9XX(dev)) + dev->mode_config.fb_base = pci_resource_start(dev->pdev, 2); + else + dev->mode_config.fb_base = pci_resource_start(dev->pdev, 0); + + if (IS_MOBILE(dev) || IS_I9XX(dev)) + num_pipe = 2; + else + num_pipe = 1; + DRM_DEBUG("%d display pipe%s available.\n", + num_pipe, num_pipe > 1 ? "s" : ""); + + for (i = 0; i < num_pipe; i++) { + intel_crtc_init(dev, i); + } + + intel_setup_outputs(dev); + + /* setup fbs */ + //drm_initial_config(dev, false); +} + +void intel_modeset_cleanup(struct drm_device *dev) +{ + drm_mode_config_cleanup(dev); +} + + +/* current intel driver doesn't take advantage of encoders + always give back the encoder for the connector +*/ +struct drm_encoder *intel_best_encoder(struct drm_connector *connector) +{ + struct intel_output *intel_output = to_intel_output(connector); + + return &intel_output->enc; +} diff --git a/linux-core/intel_drv.h b/linux-core/intel_drv.h new file mode 100644 index 00000000..256deac7 --- /dev/null +++ b/linux-core/intel_drv.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2006 Dave Airlie <airlied@linux.ie> + * Copyright (c) 2007 Intel Corporation + * Jesse Barnes <jesse.barnes@intel.com> + */ +#ifndef __INTEL_DRV_H__ +#define __INTEL_DRV_H__ + +#include <linux/i2c.h> +#include <linux/i2c-id.h> +#include <linux/i2c-algo-bit.h> +#include "drm_crtc.h" + +#include "drm_crtc_helper.h" +/* + * Display related stuff + */ + +/* store information about an Ixxx DVO */ +/* The i830->i865 use multiple DVOs with multiple i2cs */ +/* the i915, i945 have a single sDVO i2c bus - which is different */ +#define MAX_OUTPUTS 6 +/* maximum connectors per crtcs in the mode set */ +#define INTELFB_CONN_LIMIT 4 + +#define INTEL_I2C_BUS_DVO 1 +#define INTEL_I2C_BUS_SDVO 2 + +/* these are outputs from the chip - integrated only + external chips are via DVO or SDVO output */ +#define INTEL_OUTPUT_UNUSED 0 +#define INTEL_OUTPUT_ANALOG 1 +#define INTEL_OUTPUT_DVO 2 +#define INTEL_OUTPUT_SDVO 3 +#define INTEL_OUTPUT_LVDS 4 +#define INTEL_OUTPUT_TVOUT 5 + +#define INTEL_DVO_CHIP_NONE 0 +#define INTEL_DVO_CHIP_LVDS 1 +#define INTEL_DVO_CHIP_TMDS 2 +#define INTEL_DVO_CHIP_TVOUT 4 + +struct intel_i2c_chan { + struct drm_device *drm_dev; /* for getting at dev. private (mmio etc.) */ + u32 reg; /* GPIO reg */ + struct i2c_adapter adapter; + struct i2c_algo_bit_data algo; + u8 slave_addr; +}; + +struct intel_framebuffer { + struct drm_framebuffer base; +}; + + +struct intel_output { + struct drm_connector base; + + struct drm_encoder enc; + int type; + struct intel_i2c_chan *i2c_bus; /* for control functions */ + struct intel_i2c_chan *ddc_bus; /* for DDC only stuff */ + bool load_detect_temp; + void *dev_priv; +}; + +struct intel_crtc { + struct drm_crtc base; + int pipe; + int plane; + uint32_t cursor_addr; + u8 lut_r[256], lut_g[256], lut_b[256]; + int dpms_mode; + struct intel_framebuffer *fbdev_fb; + /* a mode_set for fbdev users on this crtc */ + struct drm_mode_set mode_set; +}; + +#define to_intel_crtc(x) container_of(x, struct intel_crtc, base) +#define to_intel_output(x) container_of(x, struct intel_output, base) +#define enc_to_intel_output(x) container_of(x, struct intel_output, enc) +#define to_intel_framebuffer(x) container_of(x, struct intel_framebuffer, base) + +struct intel_i2c_chan *intel_i2c_create(struct drm_device *dev, const u32 reg, + const char *name); +void intel_i2c_destroy(struct intel_i2c_chan *chan); +int intel_ddc_get_modes(struct intel_output *intel_output); +extern bool intel_ddc_probe(struct intel_output *intel_output); + +extern void intel_crt_init(struct drm_device *dev); +extern void intel_sdvo_init(struct drm_device *dev, int output_device); +extern void intel_dvo_init(struct drm_device *dev); +extern void intel_tv_init(struct drm_device *dev); +extern void intel_lvds_init(struct drm_device *dev); + +extern void intel_crtc_load_lut(struct drm_crtc *crtc); +extern void intel_encoder_prepare (struct drm_encoder *encoder); +extern void intel_encoder_commit (struct drm_encoder *encoder); + +extern struct drm_encoder *intel_best_encoder(struct drm_connector *connector); + +extern struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev, + struct drm_crtc *crtc); +extern void intel_wait_for_vblank(struct drm_device *dev); +extern struct drm_crtc *intel_get_crtc_from_pipe(struct drm_device *dev, int pipe); +extern struct drm_crtc *intel_get_load_detect_pipe(struct intel_output *intel_output, + struct drm_display_mode *mode, + int *dpms_mode); +extern void intel_release_load_detect_pipe(struct intel_output *intel_output, + int dpms_mode); + +extern struct drm_connector* intel_sdvo_find(struct drm_device *dev, int sdvoB); +extern int intel_sdvo_supports_hotplug(struct drm_connector *connector); +extern void intel_sdvo_set_hotplug(struct drm_connector *connector, int enable); +extern int intelfb_probe(struct drm_device *dev); +extern int intelfb_remove(struct drm_device *dev, struct drm_framebuffer *fb); +extern int intelfb_resize(struct drm_device *dev, struct drm_crtc *crtc); +extern void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, + u16 blue, int regno); + +extern struct drm_framebuffer * +intel_framebuffer_create(struct drm_device *dev, + struct drm_mode_fb_cmd *mode_cmd, + void *mm_private); +#endif /* __INTEL_DRV_H__ */ diff --git a/linux-core/intel_dvo.c b/linux-core/intel_dvo.c new file mode 100644 index 00000000..39ec65d2 --- /dev/null +++ b/linux-core/intel_dvo.c @@ -0,0 +1,507 @@ +/* + * Copyright © 2006-2007 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Eric Anholt <eric@anholt.net> + */ +/* + * Copyright 2006 Dave Airlie <airlied@linux.ie> + */ + +#include <linux/i2c.h> +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "intel_drv.h" +#include "i915_drm.h" +#include "i915_drv.h" +#include "dvo.h" + +#define SIL164_ADDR 0x38 +#define CH7xxx_ADDR 0x76 +#define TFP410_ADDR 0x38 + +extern struct intel_dvo_dev_ops sil164_ops; +extern struct intel_dvo_dev_ops ch7xxx_ops; +extern struct intel_dvo_dev_ops ivch_ops; +extern struct intel_dvo_dev_ops tfp410_ops; +extern struct intel_dvo_dev_ops ch7017_ops; + +struct intel_dvo_device intel_dvo_devices[] = { + { + .type = INTEL_DVO_CHIP_TMDS, + .name = "sil164", + .dvo_reg = DVOC, + .slave_addr = SIL164_ADDR, + .dev_ops = &sil164_ops, + }, + { + .type = INTEL_DVO_CHIP_TMDS, + .name = "ch7xxx", + .dvo_reg = DVOC, + .slave_addr = CH7xxx_ADDR, + .dev_ops = &ch7xxx_ops, + }, + { + .type = INTEL_DVO_CHIP_LVDS, + .name = "ivch", + .dvo_reg = DVOA, + .slave_addr = 0x02, /* Might also be 0x44, 0x84, 0xc4 */ + .dev_ops = &ivch_ops, + }, + { + .type = INTEL_DVO_CHIP_TMDS, + .name = "tfp410", + .dvo_reg = DVOC, + .slave_addr = TFP410_ADDR, + .dev_ops = &tfp410_ops, + }, + { + .type = INTEL_DVO_CHIP_LVDS, + .name = "ch7017", + .dvo_reg = DVOC, + .slave_addr = 0x75, + .gpio = GPIOE, + .dev_ops = &ch7017_ops, + } +}; + +static void intel_dvo_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_i915_private *dev_priv = encoder->dev->dev_private; + struct intel_output *intel_output = enc_to_intel_output(encoder); + struct intel_dvo_device *dvo = intel_output->dev_priv; + u32 dvo_reg = dvo->dvo_reg; + u32 temp = I915_READ(dvo_reg); + + if (mode == DRM_MODE_DPMS_ON) { + I915_WRITE(dvo_reg, temp | DVO_ENABLE); + I915_READ(dvo_reg); + dvo->dev_ops->dpms(dvo, mode); + } else { + dvo->dev_ops->dpms(dvo, mode); + I915_WRITE(dvo_reg, temp & ~DVO_ENABLE); + I915_READ(dvo_reg); + } +} + +static void intel_dvo_save(struct drm_connector *connector) +{ + struct drm_i915_private *dev_priv = connector->dev->dev_private; + struct intel_output *intel_output = to_intel_output(connector); + struct intel_dvo_device *dvo = intel_output->dev_priv; + + /* Each output should probably just save the registers it touches, + * but for now, use more overkill. + */ + dev_priv->saveDVOA = I915_READ(DVOA); + dev_priv->saveDVOB = I915_READ(DVOB); + dev_priv->saveDVOC = I915_READ(DVOC); + + dvo->dev_ops->save(dvo); +} + +static void intel_dvo_restore(struct drm_connector *connector) +{ + struct drm_i915_private *dev_priv = connector->dev->dev_private; + struct intel_output *intel_output = to_intel_output(connector); + struct intel_dvo_device *dvo = intel_output->dev_priv; + + dvo->dev_ops->restore(dvo); + + I915_WRITE(DVOA, dev_priv->saveDVOA); + I915_WRITE(DVOB, dev_priv->saveDVOB); + I915_WRITE(DVOC, dev_priv->saveDVOC); +} + +static int intel_dvo_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct intel_output *intel_output = to_intel_output(connector); + struct intel_dvo_device *dvo = intel_output->dev_priv; + + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + return MODE_NO_DBLESCAN; + + /* XXX: Validate clock range */ + + if (dvo->panel_fixed_mode) { + if (mode->hdisplay > dvo->panel_fixed_mode->hdisplay) + return MODE_PANEL; + if (mode->vdisplay > dvo->panel_fixed_mode->vdisplay) + return MODE_PANEL; + } + + return dvo->dev_ops->mode_valid(dvo, mode); +} + +static bool intel_dvo_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct intel_output *intel_output = enc_to_intel_output(encoder); + struct intel_dvo_device *dvo = intel_output->dev_priv; + + /* If we have timings from the BIOS for the panel, put them in + * to the adjusted mode. The CRTC will be set up for this mode, + * with the panel scaling set up to source from the H/VDisplay + * of the original mode. + */ + if (dvo->panel_fixed_mode != NULL) { +#define C(x) adjusted_mode->x = dvo->panel_fixed_mode->x + C(hdisplay); + C(hsync_start); + C(hsync_end); + C(htotal); + C(vdisplay); + C(vsync_start); + C(vsync_end); + C(vtotal); + C(clock); + drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); +#undef C + } + + if (dvo->dev_ops->mode_fixup) + return dvo->dev_ops->mode_fixup(dvo, mode, adjusted_mode); + + return true; +} + +static void intel_dvo_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); + struct intel_output *intel_output = enc_to_intel_output(encoder); + struct intel_dvo_device *dvo = intel_output->dev_priv; + int pipe = intel_crtc->pipe; + u32 dvo_val; + u32 dvo_reg = dvo->dvo_reg, dvo_srcdim_reg; + int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; + + switch (dvo_reg) { + case DVOA: + default: + dvo_srcdim_reg = DVOA_SRCDIM; + break; + case DVOB: + dvo_srcdim_reg = DVOB_SRCDIM; + break; + case DVOC: + dvo_srcdim_reg = DVOC_SRCDIM; + break; + } + + dvo->dev_ops->mode_set(dvo, mode, adjusted_mode); + + /* Save the data order, since I don't know what it should be set to. */ + dvo_val = I915_READ(dvo_reg) & + (DVO_PRESERVE_MASK | DVO_DATA_ORDER_GBRG); + dvo_val |= DVO_DATA_ORDER_FP | DVO_BORDER_ENABLE | + DVO_BLANK_ACTIVE_HIGH; + + if (pipe == 1) + dvo_val |= DVO_PIPE_B_SELECT; + dvo_val |= DVO_PIPE_STALL; + if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) + dvo_val |= DVO_HSYNC_ACTIVE_HIGH; + if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) + dvo_val |= DVO_VSYNC_ACTIVE_HIGH; + + I915_WRITE(dpll_reg, I915_READ(dpll_reg) | DPLL_DVO_HIGH_SPEED); + + /*I915_WRITE(DVOB_SRCDIM, + (adjusted_mode->hdisplay << DVO_SRCDIM_HORIZONTAL_SHIFT) | + (adjusted_mode->VDisplay << DVO_SRCDIM_VERTICAL_SHIFT));*/ + I915_WRITE(dvo_srcdim_reg, + (adjusted_mode->hdisplay << DVO_SRCDIM_HORIZONTAL_SHIFT) | + (adjusted_mode->vdisplay << DVO_SRCDIM_VERTICAL_SHIFT)); + /*I915_WRITE(DVOB, dvo_val);*/ + I915_WRITE(dvo_reg, dvo_val); +} + +/** + * Detect the output connection on our DVO device. + * + * Unimplemented. + */ +static enum drm_connector_status intel_dvo_detect(struct drm_connector *connector) +{ + struct intel_output *intel_output = to_intel_output(connector); + struct intel_dvo_device *dvo = intel_output->dev_priv; + + return dvo->dev_ops->detect(dvo); +} + +static int intel_dvo_get_modes(struct drm_connector *connector) +{ + struct intel_output *intel_output = to_intel_output(connector); + struct intel_dvo_device *dvo = intel_output->dev_priv; + + /* We should probably have an i2c driver get_modes function for those + * devices which will have a fixed set of modes determined by the chip + * (TV-out, for example), but for now with just TMDS and LVDS, + * that's not the case. + */ + intel_ddc_get_modes(intel_output); + if (!list_empty(&connector->probed_modes)) + return 1; + +#if 0 + if (intel_output->i2c_drv->vid_rec->get_modes) + { + modes = intel_output->i2c_drv->vid_rec->get_modes (intel_output->i2c_drv->dev_priv); + if (modes != NULL) + return modes; + } +#endif + + if (dvo->panel_fixed_mode != NULL) { + struct drm_display_mode *mode; + mode = drm_mode_duplicate(connector->dev, dvo->panel_fixed_mode); + if (mode) { + drm_mode_probed_add(connector, mode); + return 1; + } + } + return 0; +} + +static void intel_dvo_destroy (struct drm_connector *connector) +{ + struct intel_output *intel_output = to_intel_output(connector); + struct intel_dvo_device *dvo = intel_output->dev_priv; + + if (dvo) { + if (dvo->dev_ops->destroy) + dvo->dev_ops->destroy(dvo); + if (dvo->panel_fixed_mode) + kfree(dvo->panel_fixed_mode); + /* no need, in i830_dvoices[] now */ + //kfree(dvo); + } + if (intel_output->i2c_bus) + intel_i2c_destroy(intel_output->i2c_bus); + if (intel_output->ddc_bus) + intel_i2c_destroy(intel_output->ddc_bus); + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); + kfree(intel_output); +} + +#ifdef RANDR_GET_CRTC_INTERFACE +static struct drm_crtc *intel_dvo_get_crtc(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_output *intel_output = to_intel_output(connector); + struct intel_dvo_device *dvo = intel_output->dev_priv; + int pipe = !!(I915_READ(dvo->dvo_reg) & SDVO_PIPE_B_SELECT); + + return intel_pipe_to_crtc(pScrn, pipe); +} +#endif + +static const struct drm_encoder_helper_funcs intel_dvo_helper_funcs = { + .dpms = intel_dvo_dpms, + .mode_fixup = intel_dvo_mode_fixup, + .prepare = intel_encoder_prepare, + .mode_set = intel_dvo_mode_set, + .commit = intel_encoder_commit, +}; + +static const struct drm_connector_funcs intel_dvo_connector_funcs = { + .save = intel_dvo_save, + .restore = intel_dvo_restore, + .detect = intel_dvo_detect, + .destroy = intel_dvo_destroy, + .fill_modes = drm_helper_probe_single_connector_modes, +}; + +static const struct drm_connector_helper_funcs intel_dvo_connector_helper_funcs = { + .mode_valid = intel_dvo_mode_valid, + .get_modes = intel_dvo_get_modes, + .best_encoder = intel_best_encoder, +}; + +void intel_dvo_enc_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_funcs intel_dvo_enc_funcs = { + .destroy = intel_dvo_enc_destroy, +}; + + +/** + * Attempts to get a fixed panel timing for LVDS (currently only the i830). + * + * Other chips with DVO LVDS will need to extend this to deal with the LVDS + * chip being on DVOB/C and having multiple pipes. + */ +static struct drm_display_mode * +intel_dvo_get_current_mode (struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_output *intel_output = to_intel_output(connector); + struct intel_dvo_device *dvo = intel_output->dev_priv; + uint32_t dvo_reg = dvo->dvo_reg; + uint32_t dvo_val = I915_READ(dvo_reg); + struct drm_display_mode *mode = NULL; + + /* If the DVO port is active, that'll be the LVDS, so we can pull out + * its timings to get how the BIOS set up the panel. + */ + if (dvo_val & DVO_ENABLE) { + struct drm_crtc *crtc; + int pipe = (dvo_val & DVO_PIPE_B_SELECT) ? 1 : 0; + + crtc = intel_get_crtc_from_pipe(dev, pipe); + if (crtc) { + mode = intel_crtc_mode_get(dev, crtc); + + if (mode) { + mode->type |= DRM_MODE_TYPE_PREFERRED; + if (dvo_val & DVO_HSYNC_ACTIVE_HIGH) + mode->flags |= DRM_MODE_FLAG_PHSYNC; + if (dvo_val & DVO_VSYNC_ACTIVE_HIGH) + mode->flags |= DRM_MODE_FLAG_PVSYNC; + } + } + } + return mode; +} + +void intel_dvo_init(struct drm_device *dev) +{ + struct intel_output *intel_output; + struct intel_dvo_device *dvo; + struct intel_i2c_chan *i2cbus = NULL; + int ret = 0; + int i; + int gpio_inited = 0; + int encoder_type = DRM_MODE_ENCODER_NONE; + intel_output = kzalloc (sizeof(struct intel_output), GFP_KERNEL); + if (!intel_output) + return; + + /* Set up the DDC bus */ + intel_output->ddc_bus = intel_i2c_create(dev, GPIOD, "DVODDC_D"); + if (!intel_output->ddc_bus) + goto free_intel; + + /* Now, try to find a controller */ + for (i = 0; i < ARRAY_SIZE(intel_dvo_devices); i++) { + struct drm_connector *connector = &intel_output->base; + int gpio; + + dvo = &intel_dvo_devices[i]; + + /* Allow the I2C driver info to specify the GPIO to be used in + * special cases, but otherwise default to what's defined + * in the spec. + */ + if (dvo->gpio != 0) + gpio = dvo->gpio; + else if (dvo->type == INTEL_DVO_CHIP_LVDS) + gpio = GPIOB; + else + gpio = GPIOE; + + /* Set up the I2C bus necessary for the chip we're probing. + * It appears that everything is on GPIOE except for panels + * on i830 laptops, which are on GPIOB (DVOA). + */ + if (gpio_inited != gpio) { + if (i2cbus != NULL) + intel_i2c_destroy(i2cbus); + if (!(i2cbus = intel_i2c_create(dev, gpio, + gpio == GPIOB ? "DVOI2C_B" : "DVOI2C_E"))) { + continue; + } + gpio_inited = gpio; + } + + if (dvo->dev_ops!= NULL) + ret = dvo->dev_ops->init(dvo, i2cbus); + else + ret = false; + + if (!ret) + continue; + + intel_output->type = INTEL_OUTPUT_DVO; + switch (dvo->type) { + case INTEL_DVO_CHIP_TMDS: + // connector = DRM_MODE_CONNECTOR_DVID; + drm_connector_init(dev, connector, &intel_dvo_connector_funcs, + DRM_MODE_CONNECTOR_DVII); + encoder_type = DRM_MODE_ENCODER_TMDS; + break; + case INTEL_DVO_CHIP_LVDS: + // connector = DRM_MODE_CONNECTOR_LVDS; + drm_connector_init(dev, connector, &intel_dvo_connector_funcs, + DRM_MODE_CONNECTOR_LVDS); + encoder_type = DRM_MODE_ENCODER_LVDS; + break; + } + + drm_connector_helper_add(connector, &intel_dvo_connector_helper_funcs); + connector->display_info.subpixel_order = SubPixelHorizontalRGB; + connector->interlace_allowed = false; + connector->doublescan_allowed = false; + + intel_output->dev_priv = dvo; + intel_output->i2c_bus = i2cbus; + + drm_encoder_init(dev, &intel_output->enc, &intel_dvo_enc_funcs, encoder_type); + drm_encoder_helper_add(&intel_output->enc, &intel_dvo_helper_funcs); + + drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc); + if (dvo->type == INTEL_DVO_CHIP_LVDS) { + /* For our LVDS chipsets, we should hopefully be able + * to dig the fixed panel mode out of the BIOS data. + * However, it's in a different format from the BIOS + * data on chipsets with integrated LVDS (stored in AIM + * headers, likely), so for now, just get the current + * mode being output through DVO. + */ + dvo->panel_fixed_mode = intel_dvo_get_current_mode(connector); + dvo->panel_wants_dither = true; + } + + drm_sysfs_connector_add(connector); + return; + } + + intel_i2c_destroy(intel_output->ddc_bus); + /* Didn't find a chip, so tear down. */ + if (i2cbus != NULL) + intel_i2c_destroy(i2cbus); +free_intel: + kfree(intel_output); +} diff --git a/linux-core/intel_fb.c b/linux-core/intel_fb.c new file mode 100644 index 00000000..3d786ea3 --- /dev/null +++ b/linux-core/intel_fb.c @@ -0,0 +1,1148 @@ +/* + * Copyright © 2007 David Airlie + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * David Airlie + */ + /* + * Modularization + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/init.h> + +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "intel_drv.h" +#include "i915_drm.h" +#include "i915_drv.h" + +struct intelfb_par { + struct drm_device *dev; + struct drm_display_mode *our_mode; + struct intel_framebuffer *intel_fb; + int crtc_count; + /* crtc currently bound to this */ + uint32_t crtc_ids[2]; +}; +/* +static int +var_to_refresh(const struct fb_var_screeninfo *var) +{ + int xtot = var->xres + var->left_margin + var->right_margin + + var->hsync_len; + int ytot = var->yres + var->upper_margin + var->lower_margin + + var->vsync_len; + + return (1000000000 / var->pixclock * 1000 + 500) / xtot / ytot; +}*/ + +static int intelfb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) +{ + struct intelfb_par *par = info->par; + struct drm_device *dev = par->dev; + struct drm_crtc *crtc; + int i; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct drm_mode_set *modeset = &intel_crtc->mode_set; + struct drm_framebuffer *fb = modeset->fb; + + for (i = 0; i < par->crtc_count; i++) + if (crtc->base.id == par->crtc_ids[i]) + break; + + if (i == par->crtc_count) + continue; + + + if (regno > 255) + return 1; + + if (fb->depth == 8) { + intel_crtc_fb_gamma_set(crtc, red, green, blue, regno); + return 0; + } + + if (regno < 16) { + switch (fb->depth) { + case 15: + fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) | + ((green & 0xf800) >> 6) | + ((blue & 0xf800) >> 11); + break; + case 16: + fb->pseudo_palette[regno] = (red & 0xf800) | + ((green & 0xfc00) >> 5) | + ((blue & 0xf800) >> 11); + break; + case 24: + case 32: + fb->pseudo_palette[regno] = ((red & 0xff00) << 8) | + (green & 0xff00) | + ((blue & 0xff00) >> 8); + break; + } + } + } + return 0; +} + +static int intelfb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct intelfb_par *par = info->par; + struct intel_framebuffer *intel_fb = par->intel_fb; + struct drm_framebuffer *fb = &intel_fb->base; + int depth; + + if (var->pixclock == -1 || !var->pixclock) + return -EINVAL; + + /* Need to resize the fb object !!! */ + if (var->xres > fb->width || var->yres > fb->height) { + DRM_ERROR("Requested width/height is greater than current fb object %dx%d > %dx%d\n",var->xres,var->yres,fb->width,fb->height); + DRM_ERROR("Need resizing code.\n"); + return -EINVAL; + } + + switch (var->bits_per_pixel) { + case 16: + depth = (var->green.length == 6) ? 16 : 15; + break; + case 32: + depth = (var->transp.length > 0) ? 32 : 24; + break; + default: + depth = var->bits_per_pixel; + break; + } + + switch (depth) { + case 8: + var->red.offset = 0; + var->green.offset = 0; + var->blue.offset = 0; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 0; + var->transp.offset = 0; + break; + case 15: + var->red.offset = 10; + var->green.offset = 5; + var->blue.offset = 0; + var->red.length = 5; + var->green.length = 5; + var->blue.length = 5; + var->transp.length = 1; + var->transp.offset = 15; + break; + case 16: + var->red.offset = 11; + var->green.offset = 5; + var->blue.offset = 0; + var->red.length = 5; + var->green.length = 6; + var->blue.length = 5; + var->transp.length = 0; + var->transp.offset = 0; + break; + case 24: + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 0; + var->transp.offset = 0; + break; + case 32: + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 8; + var->transp.offset = 24; + break; + default: + return -EINVAL; + } + + return 0; +} + +/* this will let fbcon do the mode init */ +/* FIXME: take mode config lock? */ +static int intelfb_set_par(struct fb_info *info) +{ + struct intelfb_par *par = info->par; + struct drm_device *dev = par->dev; + struct fb_var_screeninfo *var = &info->var; + int i; + + DRM_DEBUG("%d %d\n", var->xres, var->pixclock); + + if (var->pixclock != -1) { + + DRM_ERROR("PIXEL CLCOK SET\n"); +#if 0 + struct intel_framebuffer *intel_fb = par->intel_fb; + struct drm_framebuffer *fb = &intel_fb->base; + struct drm_display_mode *drm_mode, *search_mode; + struct drm_connector *connector = NULL; + struct drm_device *dev = par->dev; + + int found = 0; + + switch (var->bits_per_pixel) { + case 16: + fb->depth = (var->green.length == 6) ? 16 : 15; + break; + case 32: + fb->depth = (var->transp.length > 0) ? 32 : 24; + break; + default: + fb->depth = var->bits_per_pixel; + break; + } + + fb->bits_per_pixel = var->bits_per_pixel; + + info->fix.line_length = fb->pitch; + info->fix.smem_len = info->fix.line_length * fb->height; + info->fix.visual = (fb->depth == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; + + info->screen_size = info->fix.smem_len; /* ??? */ + /* reuse desired mode if possible */ + /* create a drm mode */ + drm_mode = drm_mode_create(dev); + drm_mode->hdisplay = var->xres; + drm_mode->hsync_start = drm_mode->hdisplay + var->right_margin; + drm_mode->hsync_end = drm_mode->hsync_start + var->hsync_len; + drm_mode->htotal = drm_mode->hsync_end + var->left_margin; + drm_mode->vdisplay = var->yres; + drm_mode->vsync_start = drm_mode->vdisplay + var->lower_margin; + drm_mode->vsync_end = drm_mode->vsync_start + var->vsync_len; + drm_mode->vtotal = drm_mode->vsync_end + var->upper_margin; + drm_mode->clock = PICOS2KHZ(var->pixclock); + drm_mode->vrefresh = drm_mode_vrefresh(drm_mode); + drm_mode->flags = 0; + drm_mode->flags |= var->sync & FB_SYNC_HOR_HIGH_ACT ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; + drm_mode->flags |= var->sync & FB_SYNC_VERT_HIGH_ACT ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; + + drm_mode_set_name(drm_mode); + drm_mode_set_crtcinfo(drm_mode, CRTC_INTERLACE_HALVE_V); + + found = 0; + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->encoder && + connector->encoder->crtc == par->set.crtc){ + found = 1; + break; + } + } + + /* no connector bound, bail */ + if (!found) + return -EINVAL; + + found = 0; + drm_mode_debug_printmodeline(drm_mode); + list_for_each_entry(search_mode, &connector->modes, head) { + drm_mode_debug_printmodeline(search_mode); + if (drm_mode_equal(drm_mode, search_mode)) { + drm_mode_destroy(dev, drm_mode); + drm_mode = search_mode; + found = 1; + break; + } + } + + /* If we didn't find a matching mode that exists on our connector, + * create a new attachment for the incoming user specified mode + */ + if (!found) { + if (par->our_mode) { + /* this also destroys the mode */ + drm_mode_detachmode_crtc(dev, par->our_mode); + } + + par->set.mode = drm_mode; + par->our_mode = drm_mode; + drm_mode_debug_printmodeline(drm_mode); + /* attach mode */ + drm_mode_attachmode_crtc(dev, par->set.crtc, par->set.mode); + } else { + par->set.mode = drm_mode; + if (par->our_mode) + drm_mode_detachmode_crtc(dev, par->our_mode); + par->our_mode = NULL; + } + return par->set.crtc->funcs->set_config(&par->set); +#endif + return -EINVAL; + } else { + struct drm_crtc *crtc; + int ret; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + for (i = 0; i < par->crtc_count; i++) + if (crtc->base.id == par->crtc_ids[i]) + break; + + if (i == par->crtc_count) + continue; + + if (crtc->fb == intel_crtc->mode_set.fb) { + ret = crtc->funcs->set_config(&intel_crtc->mode_set); + if (ret) + return ret; + } + } + return 0; + } +} + +#if 0 +static void intelfb_copyarea(struct fb_info *info, + const struct fb_copyarea *region) +{ + struct intelfb_par *par = info->par; + struct drm_device *dev = par->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 src_x1, src_y1, dst_x1, dst_y1, dst_x2, dst_y2, offset; + u32 cmd, rop_depth_pitch, src_pitch; + RING_LOCALS; + + cmd = XY_SRC_COPY_BLT_CMD; + src_x1 = region->sx; + src_y1 = region->sy; + dst_x1 = region->dx; + dst_y1 = region->dy; + dst_x2 = region->dx + region->width; + dst_y2 = region->dy + region->height; + offset = par->fb->offset; + rop_depth_pitch = BLT_ROP_GXCOPY | par->fb->pitch; + src_pitch = par->fb->pitch; + + switch (par->fb->bits_per_pixel) { + case 16: + rop_depth_pitch |= BLT_DEPTH_16_565; + break; + case 32: + rop_depth_pitch |= BLT_DEPTH_32; + cmd |= XY_SRC_COPY_BLT_WRITE_ALPHA | XY_SRC_COPY_BLT_WRITE_RGB; + break; + } + + BEGIN_LP_RING(8); + OUT_RING(cmd); + OUT_RING(rop_depth_pitch); + OUT_RING((dst_y1 << 16) | (dst_x1 & 0xffff)); + OUT_RING((dst_y2 << 16) | (dst_x2 & 0xffff)); + OUT_RING(offset); + OUT_RING((src_y1 << 16) | (src_x1 & 0xffff)); + OUT_RING(src_pitch); + OUT_RING(offset); + ADVANCE_LP_RING(); +} + +#define ROUND_UP_TO(x, y) (((x) + (y) - 1) / (y) * (y)) +#define ROUND_DOWN_TO(x, y) ((x) / (y) * (y)) + +void intelfb_imageblit(struct fb_info *info, const struct fb_image *image) +{ + struct intelfb_par *par = info->par; + struct drm_device *dev = par->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 cmd, rop_pitch_depth, tmp; + int nbytes, ndwords, pad; + u32 dst_x1, dst_y1, dst_x2, dst_y2, offset, bg, fg; + int dat, ix, iy, iw; + int i, j; + RING_LOCALS; + + /* size in bytes of a padded scanline */ + nbytes = ROUND_UP_TO(image->width, 16) / 8; + + /* Total bytes of padded scanline data to write out. */ + nbytes *= image->height; + + /* + * Check if the glyph data exceeds the immediate mode limit. + * It would take a large font (1K pixels) to hit this limit. + */ + if (nbytes > 128 || image->depth != 1) + return cfb_imageblit(info, image); + + /* Src data is packaged a dword (32-bit) at a time. */ + ndwords = ROUND_UP_TO(nbytes, 4) / 4; + + /* + * Ring has to be padded to a quad word. But because the command starts + with 7 bytes, pad only if there is an even number of ndwords + */ + pad = !(ndwords % 2); + + DRM_DEBUG("imageblit %dx%dx%d to (%d,%d)\n", image->width, + image->height, image->depth, image->dx, image->dy); + DRM_DEBUG("nbytes: %d, ndwords: %d, pad: %d\n", nbytes, ndwords, pad); + + tmp = (XY_MONO_SRC_COPY_IMM_BLT & 0xff) + ndwords; + cmd = (XY_MONO_SRC_COPY_IMM_BLT & ~0xff) | tmp; + offset = par->fb->offset; + dst_x1 = image->dx; + dst_y1 = image->dy; + dst_x2 = image->dx + image->width; + dst_y2 = image->dy + image->height; + rop_pitch_depth = BLT_ROP_GXCOPY | par->fb->pitch; + + switch (par->fb->bits_per_pixel) { + case 8: + rop_pitch_depth |= BLT_DEPTH_8; + fg = image->fg_color; + bg = image->bg_color; + break; + case 16: + rop_pitch_depth |= BLT_DEPTH_16_565; + fg = par->fb->pseudo_palette[image->fg_color]; + bg = par->fb->pseudo_palette[image->bg_color]; + break; + case 32: + rop_pitch_depth |= BLT_DEPTH_32; + cmd |= XY_SRC_COPY_BLT_WRITE_ALPHA | XY_SRC_COPY_BLT_WRITE_RGB; + fg = par->fb->pseudo_palette[image->fg_color]; + bg = par->fb->pseudo_palette[image->bg_color]; + break; + default: + DRM_ERROR("unknown depth %d\n", par->fb->bits_per_pixel); + break; + } + + BEGIN_LP_RING(8 + ndwords); + OUT_RING(cmd); + OUT_RING(rop_pitch_depth); + OUT_RING((dst_y1 << 16) | (dst_x1 & 0xffff)); + OUT_RING((dst_y2 << 16) | (dst_x2 & 0xffff)); + OUT_RING(offset); + OUT_RING(bg); + OUT_RING(fg); + ix = iy = 0; + iw = ROUND_UP_TO(image->width, 8) / 8; + while (ndwords--) { + dat = 0; + for (j = 0; j < 2; ++j) { + for (i = 0; i < 2; ++i) { + if (ix != iw || i == 0) + dat |= image->data[iy*iw + ix++] << (i+j*2)*8; + } + if (ix == iw && iy != (image->height - 1)) { + ix = 0; + ++iy; + } + } + OUT_RING(dat); + } + if (pad) + OUT_RING(MI_NOOP); + ADVANCE_LP_RING(); +} +#endif +static int intelfb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct intelfb_par *par = info->par; + struct drm_device *dev = par->dev; + struct drm_mode_set *modeset; + struct drm_crtc *crtc; + struct intel_crtc *intel_crtc; + int ret = 0; + int i; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + + for (i = 0; i < par->crtc_count; i++) + if (crtc->base.id == par->crtc_ids[i]) + break; + + if (i == par->crtc_count) + continue; + + intel_crtc = to_intel_crtc(crtc); + modeset = &intel_crtc->mode_set; + + modeset->x = var->xoffset; + modeset->y = var->yoffset; + + if (modeset->num_connectors) { + ret = crtc->funcs->set_config(modeset); + + if (!ret) { + info->var.xoffset = var->xoffset; + info->var.yoffset = var->yoffset; + } + } + } + + return ret; +} + +static void intelfb_on(struct fb_info *info) +{ + struct intelfb_par *par = info->par; + struct drm_device *dev = par->dev; + struct drm_crtc *crtc; + struct drm_encoder *encoder; + int i; + + /* + * For each CRTC in this fb, find all associated encoders + * and turn them off, then turn off the CRTC. + */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + + for (i = 0; i < par->crtc_count; i++) + if (crtc->base.id == par->crtc_ids[i]) + break; + + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); + + /* Found a CRTC on this fb, now find encoders */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + if (encoder->crtc == crtc) { + struct drm_encoder_helper_funcs *encoder_funcs; + encoder_funcs = encoder->helper_private; + encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); + } + } + } +} + +static void intelfb_off(struct fb_info *info, int dpms_mode) +{ + struct intelfb_par *par = info->par; + struct drm_device *dev = par->dev; + struct drm_crtc *crtc; + struct drm_encoder *encoder; + int i; + + /* + * For each CRTC in this fb, find all associated encoders + * and turn them off, then turn off the CRTC. + */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + + for (i = 0; i < par->crtc_count; i++) + if (crtc->base.id == par->crtc_ids[i]) + break; + + /* Found a CRTC on this fb, now find encoders */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + if (encoder->crtc == crtc) { + struct drm_encoder_helper_funcs *encoder_funcs; + encoder_funcs = encoder->helper_private; + encoder_funcs->dpms(encoder, dpms_mode); + } + } + if (dpms_mode == DRM_MODE_DPMS_OFF) + crtc_funcs->dpms(crtc, dpms_mode); + } +} + +int intelfb_blank(int blank, struct fb_info *info) +{ + switch (blank) { + case FB_BLANK_UNBLANK: + intelfb_on(info); + break; + case FB_BLANK_NORMAL: + intelfb_off(info, DRM_MODE_DPMS_STANDBY); + break; + case FB_BLANK_HSYNC_SUSPEND: + intelfb_off(info, DRM_MODE_DPMS_STANDBY); + break; + case FB_BLANK_VSYNC_SUSPEND: + intelfb_off(info, DRM_MODE_DPMS_SUSPEND); + break; + case FB_BLANK_POWERDOWN: + intelfb_off(info, DRM_MODE_DPMS_OFF); + break; + } + return 0; +} + +static struct fb_ops intelfb_ops = { + .owner = THIS_MODULE, + //.fb_open = intelfb_open, + //.fb_read = intelfb_read, + //.fb_write = intelfb_write, + //.fb_release = intelfb_release, + //.fb_ioctl = intelfb_ioctl, + .fb_check_var = intelfb_check_var, + .fb_set_par = intelfb_set_par, + .fb_setcolreg = intelfb_setcolreg, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, //intelfb_copyarea, + .fb_imageblit = cfb_imageblit, //intelfb_imageblit, + .fb_pan_display = intelfb_pan_display, + .fb_blank = intelfb_blank, +}; + +/** + * Curretly it is assumed that the old framebuffer is reused. + * + * LOCKING + * caller should hold the mode config lock. + * + */ +int intelfb_resize(struct drm_device *dev, struct drm_crtc *crtc) +{ + struct fb_info *info; + struct drm_framebuffer *fb; + struct drm_display_mode *mode = crtc->desired_mode; + + fb = crtc->fb; + if (!fb) + return 1; + + info = fb->fbdev; + if (!info) + return 1; + + if (!mode) + return 1; + + info->var.xres = mode->hdisplay; + info->var.right_margin = mode->hsync_start - mode->hdisplay; + info->var.hsync_len = mode->hsync_end - mode->hsync_start; + info->var.left_margin = mode->htotal - mode->hsync_end; + info->var.yres = mode->vdisplay; + info->var.lower_margin = mode->vsync_start - mode->vdisplay; + info->var.vsync_len = mode->vsync_end - mode->vsync_start; + info->var.upper_margin = mode->vtotal - mode->vsync_end; + info->var.pixclock = 10000000 / mode->htotal * 1000 / mode->vtotal * 100; + /* avoid overflow */ + info->var.pixclock = info->var.pixclock * 1000 / mode->vrefresh; + + return 0; +} +EXPORT_SYMBOL(intelfb_resize); + +static struct drm_mode_set panic_mode; + +int intelfb_panic(struct notifier_block *n, unsigned long ununsed, + void *panic_str) +{ + DRM_ERROR("panic occurred, switching back to text console\n"); + drm_crtc_helper_set_config(&panic_mode); + + return 0; +} +EXPORT_SYMBOL(intelfb_panic); + +static struct notifier_block paniced = { + .notifier_call = intelfb_panic, +}; + +int intelfb_create(struct drm_device *dev, uint32_t fb_width, uint32_t fb_height, + uint32_t surface_width, uint32_t surface_height, + struct intel_framebuffer **intel_fb_p) +{ + struct fb_info *info; + struct intelfb_par *par; + struct drm_framebuffer *fb; + struct intel_framebuffer *intel_fb; + struct drm_mode_fb_cmd mode_cmd; + struct drm_gem_object *fbo = NULL; + struct drm_i915_gem_object *obj_priv; + struct device *device = &dev->pdev->dev; + int size, aligned_size, ret; + + mode_cmd.width = surface_width;/* crtc->desired_mode->hdisplay; */ + mode_cmd.height = surface_height;/* crtc->desired_mode->vdisplay; */ + + mode_cmd.bpp = 32; + mode_cmd.pitch = mode_cmd.width * ((mode_cmd.bpp + 1) / 8); + mode_cmd.depth = 24; + + size = mode_cmd.pitch * mode_cmd.height; + aligned_size = ALIGN(size, PAGE_SIZE); + fbo = drm_gem_object_alloc(dev, aligned_size); + if (!fbo) { + printk(KERN_ERR "failed to allocate framebuffer\n"); + ret = -ENOMEM; + goto out; + } + obj_priv = fbo->driver_private; + + mutex_lock(&dev->struct_mutex); + /* Flush everything out, we'll be doing GTT only from now on */ + i915_gem_object_set_domain(fbo, I915_GEM_DOMAIN_GTT, + I915_GEM_DOMAIN_GTT); + + ret = i915_gem_object_pin(fbo, PAGE_SIZE); + if (ret) { + DRM_ERROR("failed to pin fb: %d\n", ret); + goto out_unref; + } + + i915_gem_clflush_object(fbo); + + fb = intel_framebuffer_create(dev, &mode_cmd, fbo); + if (!fb) { + DRM_ERROR("failed to allocate fb.\n"); + ret = -ENOMEM; + goto out_unref; + } + + list_add(&fb->filp_head, &dev->mode_config.fb_kernel_list); + + intel_fb = to_intel_framebuffer(fb); + *intel_fb_p = intel_fb; + + info = framebuffer_alloc(sizeof(struct intelfb_par), device); + if (!info) { + ret = -ENOMEM; + goto out_unref; + } + + par = info->par; + + strcpy(info->fix.id, "inteldrmfb"); + info->fix.type = FB_TYPE_PACKED_PIXELS; + info->fix.visual = FB_VISUAL_TRUECOLOR; + info->fix.type_aux = 0; + info->fix.xpanstep = 1; /* doing it in hw */ + info->fix.ypanstep = 1; /* doing it in hw */ + info->fix.ywrapstep = 0; + info->fix.accel = FB_ACCEL_I830; + info->fix.type_aux = 0; + + info->flags = FBINFO_DEFAULT; + + info->fbops = &intelfb_ops; + + info->fix.line_length = fb->pitch; + info->fix.smem_start = dev->mode_config.fb_base + obj_priv->gtt_offset; + info->fix.smem_len = size; + + info->flags = FBINFO_DEFAULT; + + info->screen_base = ioremap_wc(dev->agp->base + obj_priv->gtt_offset, + size); + if (!info->screen_base) { + ret = -ENOSPC; + goto out_unref; + } + info->screen_size = size; + + memset(info->screen_base, 0, size); + + info->pseudo_palette = fb->pseudo_palette; + info->var.xres_virtual = fb->width; + info->var.yres_virtual = fb->height; + info->var.bits_per_pixel = fb->bits_per_pixel; + info->var.xoffset = 0; + info->var.yoffset = 0; + info->var.activate = FB_ACTIVATE_NOW; + info->var.height = -1; + info->var.width = -1; + + info->var.xres = fb_width; + info->var.yres = fb_height; + + if (IS_I9XX(dev)) { + info->fix.mmio_start = pci_resource_start(dev->pdev, 0); + info->fix.mmio_len = pci_resource_len(dev->pdev, 0); + } else { + info->fix.mmio_start = pci_resource_start(dev->pdev, 1); + info->fix.mmio_len = pci_resource_len(dev->pdev, 1); + } + + info->pixmap.size = 64*1024; + info->pixmap.buf_align = 8; + info->pixmap.access_align = 32; + info->pixmap.flags = FB_PIXMAP_SYSTEM; + info->pixmap.scan_align = 1; + + DRM_DEBUG("fb depth is %d\n", fb->depth); + DRM_DEBUG(" pitch is %d\n", fb->pitch); + switch(fb->depth) { + case 8: + info->var.red.offset = 0; + info->var.green.offset = 0; + info->var.blue.offset = 0; + info->var.red.length = 8; /* 8bit DAC */ + info->var.green.length = 8; + info->var.blue.length = 8; + info->var.transp.offset = 0; + info->var.transp.length = 0; + break; + case 15: + info->var.red.offset = 10; + info->var.green.offset = 5; + info->var.blue.offset = 0; + info->var.red.length = 5; + info->var.green.length = 5; + info->var.blue.length = 5; + info->var.transp.offset = 15; + info->var.transp.length = 1; + break; + case 16: + info->var.red.offset = 11; + info->var.green.offset = 5; + info->var.blue.offset = 0; + info->var.red.length = 5; + info->var.green.length = 6; + info->var.blue.length = 5; + info->var.transp.offset = 0; + break; + case 24: + info->var.red.offset = 16; + info->var.green.offset = 8; + info->var.blue.offset = 0; + info->var.red.length = 8; + info->var.green.length = 8; + info->var.blue.length = 8; + info->var.transp.offset = 0; + info->var.transp.length = 0; + break; + case 32: + info->var.red.offset = 16; + info->var.green.offset = 8; + info->var.blue.offset = 0; + info->var.red.length = 8; + info->var.green.length = 8; + info->var.blue.length = 8; + info->var.transp.offset = 24; + info->var.transp.length = 8; + break; + default: + break; + } + + fb->fbdev = info; + + par->intel_fb = intel_fb; + par->dev = dev; + + /* To allow resizeing without swapping buffers */ + printk("allocated %dx%d fb: 0x%08x, bo %p\n", intel_fb->base.width, + intel_fb->base.height, obj_priv->gtt_offset, fbo); + + mutex_unlock(&dev->struct_mutex); + return 0; + +out_unref: + drm_gem_object_unreference(fbo); + mutex_unlock(&dev->struct_mutex); +out: + return ret; +} + +static int intelfb_multi_fb_probe_crtc(struct drm_device *dev, struct drm_crtc *crtc) +{ + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_framebuffer *intel_fb; + struct drm_framebuffer *fb; + struct drm_connector *connector; + struct fb_info *info; + struct intelfb_par *par; + struct drm_mode_set *modeset; + unsigned int width, height; + int new_fb = 0; + int ret, i, conn_count; + + if (!drm_helper_crtc_in_use(crtc)) + return 0; + + if (!crtc->desired_mode) + return 0; + + width = crtc->desired_mode->hdisplay; + height = crtc->desired_mode->vdisplay; + + /* is there an fb bound to this crtc already */ + if (!intel_crtc->mode_set.fb) { + ret = intelfb_create(dev, width, height, width, height, &intel_fb); + if (ret) + return -EINVAL; + new_fb = 1; + } else { + fb = intel_crtc->mode_set.fb; + intel_fb = to_intel_framebuffer(fb); + if ((intel_fb->base.width < width) || (intel_fb->base.height < height)) + return -EINVAL; + } + + info = intel_fb->base.fbdev; + par = info->par; + + modeset = &intel_crtc->mode_set; + modeset->fb = &intel_fb->base; + conn_count = 0; + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->encoder) + if (connector->encoder->crtc == modeset->crtc) { + modeset->connectors[conn_count] = connector; + conn_count++; + if (conn_count > INTELFB_CONN_LIMIT) + BUG(); + } + } + + for (i = conn_count; i < INTELFB_CONN_LIMIT; i++) + modeset->connectors[i] = NULL; + + par->crtc_ids[0] = crtc->base.id; + + modeset->num_connectors = conn_count; + if (modeset->mode != modeset->crtc->desired_mode) + modeset->mode = modeset->crtc->desired_mode; + + par->crtc_count = 1; + + if (new_fb) { + info->var.pixclock = -1; + if (register_framebuffer(info) < 0) + return -EINVAL; + } else + intelfb_set_par(info); + + printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, + info->fix.id); + + /* Switch back to kernel console on panic */ + panic_mode = *modeset; + atomic_notifier_chain_register(&panic_notifier_list, &paniced); + printk(KERN_INFO "registered panic notifier\n"); + + return 0; +} + +static int intelfb_multi_fb_probe(struct drm_device *dev) +{ + + struct drm_crtc *crtc; + int ret = 0; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + ret = intelfb_multi_fb_probe_crtc(dev, crtc); + if (ret) + return ret; + } + return ret; +} + +static int intelfb_single_fb_probe(struct drm_device *dev) +{ + struct drm_crtc *crtc; + struct drm_connector *connector; + unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1; + unsigned int surface_width = 0, surface_height = 0; + int new_fb = 0; + int crtc_count = 0; + int ret, i, conn_count = 0; + struct intel_framebuffer *intel_fb; + struct fb_info *info; + struct intelfb_par *par; + struct drm_mode_set *modeset = NULL; + + DRM_DEBUG("\n"); + /* first up get a count of crtcs now in use and new min/maxes width/heights */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (drm_helper_crtc_in_use(crtc)) { + if (crtc->desired_mode) { + if (crtc->desired_mode->hdisplay < fb_width) + fb_width = crtc->desired_mode->hdisplay; + + if (crtc->desired_mode->vdisplay < fb_height) + fb_height = crtc->desired_mode->vdisplay; + + if (crtc->desired_mode->hdisplay > surface_width) + surface_width = crtc->desired_mode->hdisplay; + + if (crtc->desired_mode->vdisplay > surface_height) + surface_height = crtc->desired_mode->vdisplay; + + } + crtc_count++; + } + } + + if (crtc_count == 0 || fb_width == -1 || fb_height == -1) { + /* hmm everyone went away - assume VGA cable just fell out + and will come back later. */ + return 0; + } + + /* do we have an fb already? */ + if (list_empty(&dev->mode_config.fb_kernel_list)) { + /* create an fb if we don't have one */ + ret = intelfb_create(dev, fb_width, fb_height, surface_width, surface_height, &intel_fb); + if (ret) + return -EINVAL; + new_fb = 1; + } else { + struct drm_framebuffer *fb; + fb = list_first_entry(&dev->mode_config.fb_kernel_list, struct drm_framebuffer, filp_head); + intel_fb = to_intel_framebuffer(fb); + + /* if someone hotplugs something bigger than we have already allocated, we are pwned. + As really we can't resize an fbdev that is in the wild currently due to fbdev + not really being designed for the lower layers moving stuff around under it. + - so in the grand style of things - punt. */ + if ((fb->width < surface_width) || (fb->height < surface_height)) { + DRM_ERROR("Framebuffer not large enough to scale console onto.\n"); + return -EINVAL; + } + } + + info = intel_fb->base.fbdev; + par = info->par; + + crtc_count = 0; + /* okay we need to setup new connector sets in the crtcs */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + modeset = &intel_crtc->mode_set; + modeset->fb = &intel_fb->base; + conn_count = 0; + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->encoder) + if(connector->encoder->crtc == modeset->crtc) { + modeset->connectors[conn_count] = connector; + conn_count++; + if (conn_count > INTELFB_CONN_LIMIT) + BUG(); + } + } + + for (i = conn_count; i < INTELFB_CONN_LIMIT; i++) + modeset->connectors[i] = NULL; + + par->crtc_ids[crtc_count++] = crtc->base.id; + + modeset->num_connectors = conn_count; + if (modeset->mode != modeset->crtc->desired_mode) + modeset->mode = modeset->crtc->desired_mode; + } + par->crtc_count = crtc_count; + + if (new_fb) { + info->var.pixclock = -1; + if (register_framebuffer(info) < 0) + return -EINVAL; + } else + intelfb_set_par(info); + + printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, + info->fix.id); + + /* Switch back to kernel console on panic */ + panic_mode = *modeset; + atomic_notifier_chain_register(&panic_notifier_list, &paniced); + printk(KERN_INFO "registered panic notifier\n"); + + return 0; +} + +int intelfb_probe(struct drm_device *dev) +{ + int ret; + + DRM_DEBUG("\n"); + + /* something has changed in the lower levels of hell - deal with it + here */ + + /* two modes : a) 1 fb to rule all crtcs. + b) one fb per crtc. + two actions 1) new connected device + 2) device removed. + case a/1 : if the fb surface isn't big enough - resize the surface fb. + if the fb size isn't big enough - resize fb into surface. + if everything big enough configure the new crtc/etc. + case a/2 : undo the configuration + possibly resize down the fb to fit the new configuration. + case b/1 : see if it is on a new crtc - setup a new fb and add it. + case b/2 : teardown the new fb. + */ + + /* mode a first */ + /* search for an fb */ + if (i915_fbpercrtc == 1) { + ret = intelfb_multi_fb_probe(dev); + } else { + ret = intelfb_single_fb_probe(dev); + } + + return ret; +} +EXPORT_SYMBOL(intelfb_probe); + +int intelfb_remove(struct drm_device *dev, struct drm_framebuffer *fb) +{ + struct fb_info *info; + struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); + + if (!fb) + return -EINVAL; + + info = fb->fbdev; + + if (info) { + unregister_framebuffer(info); + iounmap(info->screen_base); + mutex_lock(&dev->struct_mutex); + drm_gem_object_unreference(intel_fb->base.mm_private); + mutex_unlock(&dev->struct_mutex); + framebuffer_release(info); + } + + atomic_notifier_chain_unregister(&panic_notifier_list, &paniced); + memset(&panic_mode, 0, sizeof(struct drm_mode_set)); + 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..fa8209c7 --- /dev/null +++ b/linux-core/intel_lvds.c @@ -0,0 +1,515 @@ +/* + * Copyright © 2006-2007 Intel Corporation + * Copyright (c) 2006 Dave Airlie <airlied@linux.ie> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * Dave Airlie <airlied@linux.ie> + * Jesse Barnes <jesse.barnes@intel.com> + */ + +#include <linux/i2c.h> +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "drm_edid.h" +#include "intel_drv.h" +#include "i915_drm.h" +#include "i915_drv.h" + +/** + * Sets the backlight level. + * + * \param level backlight level, from 0 to intel_lvds_get_max_backlight(). + */ +static void intel_lvds_set_backlight(struct drm_device *dev, int level) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 blc_pwm_ctl; + + blc_pwm_ctl = I915_READ(BLC_PWM_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK; + I915_WRITE(BLC_PWM_CTL, (blc_pwm_ctl | + (level << BACKLIGHT_DUTY_CYCLE_SHIFT))); +} + +/** + * Returns the maximum level of the backlight duty cycle field. + */ +static u32 intel_lvds_get_max_backlight(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + return ((I915_READ(BLC_PWM_CTL) & BACKLIGHT_MODULATION_FREQ_MASK) >> + BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; +} + +/** + * Sets the power state for the panel. + */ +static void intel_lvds_set_power(struct drm_device *dev, bool on) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 pp_status; + + if (on) { + I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) | + POWER_TARGET_ON); + do { + pp_status = I915_READ(PP_STATUS); + } while ((pp_status & PP_ON) == 0); + + intel_lvds_set_backlight(dev, dev_priv->backlight_duty_cycle); + } else { + intel_lvds_set_backlight(dev, 0); + + I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) & + ~POWER_TARGET_ON); + do { + pp_status = I915_READ(PP_STATUS); + } while (pp_status & PP_ON); + } +} + +static void intel_lvds_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + + if (mode == DRM_MODE_DPMS_ON) + intel_lvds_set_power(dev, true); + else + intel_lvds_set_power(dev, false); + + /* XXX: We never power down the LVDS pairs. */ +} + +static void intel_lvds_save(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + dev_priv->savePP_ON = I915_READ(PP_ON_DELAYS); + dev_priv->savePP_OFF = I915_READ(PP_OFF_DELAYS); + dev_priv->savePP_CONTROL = I915_READ(PP_CONTROL); + dev_priv->savePP_DIVISOR = I915_READ(PP_DIVISOR); + dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL); + dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL & + BACKLIGHT_DUTY_CYCLE_MASK); + + /* + * If the light is off at server startup, just make it full brightness + */ + if (dev_priv->backlight_duty_cycle == 0) + dev_priv->backlight_duty_cycle = + intel_lvds_get_max_backlight(dev); +} + +static void intel_lvds_restore(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + I915_WRITE(BLC_PWM_CTL, dev_priv->saveBLC_PWM_CTL); + I915_WRITE(PP_ON_DELAYS, dev_priv->savePP_ON); + I915_WRITE(PP_OFF_DELAYS, dev_priv->savePP_OFF); + I915_WRITE(PP_DIVISOR, dev_priv->savePP_DIVISOR); + I915_WRITE(PP_CONTROL, dev_priv->savePP_CONTROL); + if (dev_priv->savePP_CONTROL & POWER_TARGET_ON) + intel_lvds_set_power(dev, true); + else + intel_lvds_set_power(dev, false); +} + +static int intel_lvds_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct drm_device *dev = connector->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_display_mode *fixed_mode = dev_priv->panel_fixed_mode; + + if (fixed_mode) { + if (mode->hdisplay > fixed_mode->hdisplay) + return MODE_PANEL; + if (mode->vdisplay > fixed_mode->vdisplay) + return MODE_PANEL; + } + + return MODE_OK; +} + +static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); + struct drm_encoder *tmp_encoder; + + /* Should never happen!! */ + if (!IS_I965G(dev) && intel_crtc->pipe == 0) { + printk(KERN_ERR "Can't support LVDS on pipe A\n"); + return false; + } + + /* Should never happen!! */ + list_for_each_entry(tmp_encoder, &dev->mode_config.encoder_list, head) { + if (tmp_encoder != encoder && tmp_encoder->crtc == encoder->crtc) { + printk(KERN_ERR "Can't enable LVDS and another " + "encoder on the same pipe\n"); + return false; + } + } + + /* + * If we have timings from the BIOS for the panel, put them in + * to the adjusted mode. The CRTC will be set up for this mode, + * with the panel scaling set up to source from the H/VDisplay + * of the original mode. + */ + if (dev_priv->panel_fixed_mode != NULL) { + adjusted_mode->hdisplay = dev_priv->panel_fixed_mode->hdisplay; + adjusted_mode->hsync_start = + dev_priv->panel_fixed_mode->hsync_start; + adjusted_mode->hsync_end = + dev_priv->panel_fixed_mode->hsync_end; + adjusted_mode->htotal = dev_priv->panel_fixed_mode->htotal; + adjusted_mode->vdisplay = dev_priv->panel_fixed_mode->vdisplay; + adjusted_mode->vsync_start = + dev_priv->panel_fixed_mode->vsync_start; + adjusted_mode->vsync_end = + dev_priv->panel_fixed_mode->vsync_end; + adjusted_mode->vtotal = dev_priv->panel_fixed_mode->vtotal; + adjusted_mode->clock = dev_priv->panel_fixed_mode->clock; + drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); + } + + /* + * XXX: It would be nice to support lower refresh rates on the + * panels to reduce power consumption, and perhaps match the + * user's requested refresh rate. + */ + + return true; +} + +static void intel_lvds_prepare(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL); + dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL & + BACKLIGHT_DUTY_CYCLE_MASK); + + intel_lvds_set_power(dev, false); +} + +static void intel_lvds_commit( struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + if (dev_priv->backlight_duty_cycle == 0) + dev_priv->backlight_duty_cycle = + intel_lvds_get_max_backlight(dev); + + intel_lvds_set_power(dev, true); +} + +static void intel_lvds_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); + u32 pfit_control; + + /* + * The LVDS pin pair will already have been turned on in the + * intel_crtc_mode_set since it has a large impact on the DPLL + * settings. + */ + + /* + * Enable automatic panel scaling so that non-native modes fill the + * screen. Should be enabled before the pipe is enabled, according to + * register description and PRM. + */ + if (mode->hdisplay != adjusted_mode->hdisplay || + mode->vdisplay != adjusted_mode->vdisplay) + pfit_control = (PFIT_ENABLE | VERT_AUTO_SCALE | + HORIZ_AUTO_SCALE | VERT_INTERP_BILINEAR | + HORIZ_INTERP_BILINEAR); + else + pfit_control = 0; + + if (!IS_I965G(dev)) { + if (dev_priv->panel_wants_dither) + pfit_control |= PANEL_8TO6_DITHER_ENABLE; + } + else + pfit_control |= intel_crtc->pipe << PFIT_PIPE_SHIFT; + + I915_WRITE(PFIT_CONTROL, pfit_control); +} + +/** + * Detect the LVDS connection. + * + * This always returns CONNECTOR_STATUS_CONNECTED. This connector should only have + * been set up if the LVDS was actually connected anyway. + */ +static enum drm_connector_status intel_lvds_detect(struct drm_connector *connector) +{ + return connector_status_connected; +} + +/** + * Return the list of DDC modes if available, or the BIOS fixed mode otherwise. + */ +static int intel_lvds_get_modes(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct intel_output *intel_output = to_intel_output(connector); + struct drm_i915_private *dev_priv = dev->dev_private; + int ret = 0; + + ret = intel_ddc_get_modes(intel_output); + + if (ret) + return ret; + + /* Didn't get an EDID, so + * Set wide sync ranges so we get all modes + * handed to valid_mode for checking + */ + connector->display_info.min_vfreq = 0; + connector->display_info.max_vfreq = 200; + connector->display_info.min_hfreq = 0; + connector->display_info.max_hfreq = 200; + + if (dev_priv->panel_fixed_mode != NULL) { + struct drm_display_mode *mode = + drm_mode_duplicate(dev, dev_priv->panel_fixed_mode); + drm_mode_probed_add(connector, mode); + return 1; + } + + return 0; +} + +/** + * intel_lvds_destroy - unregister and free LVDS structures + * @connector: connector to free + * + * Unregister the DDC bus for this connector then free the driver private + * structure. + */ +static void intel_lvds_destroy(struct drm_connector *connector) +{ + struct intel_output *intel_output = to_intel_output(connector); + + if (intel_output->ddc_bus) + intel_i2c_destroy(intel_output->ddc_bus); + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); + kfree(connector); +} + +static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = { + .dpms = intel_lvds_dpms, + .mode_fixup = intel_lvds_mode_fixup, + .prepare = intel_lvds_prepare, + .mode_set = intel_lvds_mode_set, + .commit = intel_lvds_commit, +}; + +static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = { + .get_modes = intel_lvds_get_modes, + .mode_valid = intel_lvds_mode_valid, + .best_encoder = intel_best_encoder, +}; + +static const struct drm_connector_funcs intel_lvds_connector_funcs = { + .save = intel_lvds_save, + .restore = intel_lvds_restore, + .detect = intel_lvds_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = intel_lvds_destroy, +}; + + +static void intel_lvds_enc_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_funcs intel_lvds_enc_funcs = { + .destroy = intel_lvds_enc_destroy, +}; + + + +/** + * intel_lvds_init - setup LVDS connectors on this device + * @dev: drm device + * + * Create the connector, register the LVDS DDC bus, and try to figure out what + * modes we can display on the LVDS panel (if present). + */ +void intel_lvds_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_output *intel_output; + struct drm_connector *connector; + struct drm_encoder *encoder; + struct drm_display_mode *scan; /* *modes, *bios_mode; */ + struct drm_crtc *crtc; + u32 lvds; + int pipe; + + intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL); + if (!intel_output) { + return; + } + + connector = &intel_output->base; + encoder = &intel_output->enc; + drm_connector_init(dev, &intel_output->base, &intel_lvds_connector_funcs, + DRM_MODE_CONNECTOR_LVDS); + + drm_encoder_init(dev, &intel_output->enc, &intel_lvds_enc_funcs, + DRM_MODE_ENCODER_LVDS); + + drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc); + intel_output->type = INTEL_OUTPUT_LVDS; + + drm_encoder_helper_add(encoder, &intel_lvds_helper_funcs); + drm_connector_helper_add(connector, &intel_lvds_connector_helper_funcs); + connector->display_info.subpixel_order = SubPixelHorizontalRGB; + connector->interlace_allowed = false; + connector->doublescan_allowed = false; + + + /* + * LVDS discovery: + * 1) check for EDID on DDC + * 2) check for VBT data + * 3) check to see if LVDS is already on + * if none of the above, no panel + * 4) make sure lid is open + * if closed, act like it's not there for now + */ + + /* Set up the DDC bus. */ + intel_output->ddc_bus = intel_i2c_create(dev, GPIOC, "LVDSDDC_C"); + if (!intel_output->ddc_bus) { + dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration " + "failed.\n"); + goto failed; + } + + /* + * Attempt to get the fixed panel mode from DDC. Assume that the + * preferred mode is the right one. + */ + intel_ddc_get_modes(intel_output); + + list_for_each_entry(scan, &connector->probed_modes, head) { + if (scan->type & DRM_MODE_TYPE_PREFERRED) { + dev_priv->panel_fixed_mode = + drm_mode_duplicate(dev, scan); + goto out; /* FIXME: check for quirks */ + } + } + + /* Failed to get EDID, what about VBT? */ + if (dev_priv->vbt_mode) + dev_priv->panel_fixed_mode = + drm_mode_duplicate(dev, dev_priv->vbt_mode); + + /* + * If we didn't get EDID, try checking if the panel is already turned + * on. If so, assume that whatever is currently programmed is the + * correct mode. + */ + lvds = I915_READ(LVDS); + pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0; + crtc = intel_get_crtc_from_pipe(dev, pipe); + + if (crtc && (lvds & LVDS_PORT_EN)) { + dev_priv->panel_fixed_mode = intel_crtc_mode_get(dev, crtc); + if (dev_priv->panel_fixed_mode) { + dev_priv->panel_fixed_mode->type |= + DRM_MODE_TYPE_PREFERRED; + goto out; /* FIXME: check for quirks */ + } + } + + /* If we still don't have a mode after all that, give up. */ + if (!dev_priv->panel_fixed_mode) + goto failed; + + /* FIXME: detect aopen & mac mini type stuff automatically? */ + /* + * Blacklist machines with BIOSes that list an LVDS panel without + * actually having one. + */ + if (IS_I945GM(dev)) { + /* aopen mini pc */ + if (dev->pdev->subsystem_vendor == 0xa0a0) + goto failed; + + if ((dev->pdev->subsystem_vendor == 0x8086) && + (dev->pdev->subsystem_device == 0x7270)) { + /* It's a Mac Mini or Macbook Pro. + * + * Apple hardware is out to get us. The macbook pro + * has a real LVDS panel, but the mac mini does not, + * and they have the same device IDs. We'll + * distinguish by panel size, on the assumption + * that Apple isn't about to make any machines with an + * 800x600 display. + */ + + if (dev_priv->panel_fixed_mode != NULL && + dev_priv->panel_fixed_mode->hdisplay == 800 && + dev_priv->panel_fixed_mode->vdisplay == 600) { + DRM_DEBUG("Suspected Mac Mini, ignoring the LVDS\n"); + goto failed; + } + } + } + + +out: + drm_sysfs_connector_add(connector); + return; + +failed: + DRM_DEBUG("No LVDS modes found, disabling.\n"); + if (intel_output->ddc_bus) + intel_i2c_destroy(intel_output->ddc_bus); + drm_connector_cleanup(connector); + kfree(connector); +} diff --git a/linux-core/intel_modes.c b/linux-core/intel_modes.c new file mode 100644 index 00000000..79be3575 --- /dev/null +++ b/linux-core/intel_modes.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> + * Copyright (c) 2007 Intel Corporation + * Jesse Barnes <jesse.barnes@intel.com> + */ + +#include <linux/i2c.h> +#include <linux/fb.h> +#include "drmP.h" +#include "intel_drv.h" + +/** + * intel_ddc_probe + * + */ +bool intel_ddc_probe(struct intel_output *intel_output) +{ + u8 out_buf[] = { 0x0, 0x0}; + u8 buf[2]; + int ret; + struct i2c_msg msgs[] = { + { + .addr = 0x50, + .flags = 0, + .len = 1, + .buf = out_buf, + }, + { + .addr = 0x50, + .flags = I2C_M_RD, + .len = 1, + .buf = buf, + } + }; + + ret = i2c_transfer(&intel_output->ddc_bus->adapter, msgs, 2); + if (ret == 2) + return true; + + return false; +} + +/** + * intel_ddc_get_modes - get modelist from monitor + * @connector: DRM connector device to use + * + * Fetch the EDID information from @connector using the DDC bus. + */ +int intel_ddc_get_modes(struct intel_output *intel_output) +{ + struct edid *edid; + int ret = 0; + + edid = drm_get_edid(&intel_output->base, &intel_output->ddc_bus->adapter); + if (edid) { + drm_mode_connector_update_edid_property(&intel_output->base, edid); + ret = drm_add_edid_modes(&intel_output->base, edid); + kfree(edid); + } + return ret; +} diff --git a/linux-core/intel_sdvo.c b/linux-core/intel_sdvo.c new file mode 100644 index 00000000..6624bde2 --- /dev/null +++ b/linux-core/intel_sdvo.c @@ -0,0 +1,1153 @@ +/* + * Copyright © 2006-2007 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Eric Anholt <eric@anholt.net> + */ +/* + * Copyright 2006 Dave Airlie <airlied@linux.ie> + * Jesse Barnes <jesse.barnes@intel.com> + */ + +#include <linux/i2c.h> +#include <linux/delay.h> +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "intel_drv.h" +#include "i915_drm.h" +#include "i915_drv.h" +#include "intel_sdvo_regs.h" + +struct intel_sdvo_priv { + struct intel_i2c_chan *i2c_bus; + int slaveaddr; + int output_device; + + u16 active_outputs; + + struct intel_sdvo_caps caps; + int pixel_clock_min, pixel_clock_max; + + int save_sdvo_mult; + u16 save_active_outputs; + struct intel_sdvo_dtd save_input_dtd_1, save_input_dtd_2; + struct intel_sdvo_dtd save_output_dtd[16]; + u32 save_SDVOX; +}; + +/** + * Writes the SDVOB or SDVOC with the given value, but always writes both + * SDVOB and SDVOC to work around apparent hardware issues (according to + * comments in the BIOS). + */ +void intel_sdvo_write_sdvox(struct intel_output *intel_output, u32 val) +{ + struct drm_device *dev = intel_output->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; + u32 bval = val, cval = val; + int i; + + if (sdvo_priv->output_device == SDVOB) { + cval = I915_READ(SDVOC); + } else { + bval = I915_READ(SDVOB); + } + /* + * Write the registers twice for luck. Sometimes, + * writing them only once doesn't appear to 'stick'. + * The BIOS does this too. Yay, magic + */ + for (i = 0; i < 2; i++) + { + I915_WRITE(SDVOB, bval); + I915_READ(SDVOB); + I915_WRITE(SDVOC, cval); + I915_READ(SDVOC); + } +} + +static bool intel_sdvo_read_byte(struct intel_output *intel_output, u8 addr, + u8 *ch) +{ + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; + u8 out_buf[2]; + u8 buf[2]; + int ret; + + struct i2c_msg msgs[] = { + { + .addr = sdvo_priv->i2c_bus->slave_addr, + .flags = 0, + .len = 1, + .buf = out_buf, + }, + { + .addr = sdvo_priv->i2c_bus->slave_addr, + .flags = I2C_M_RD, + .len = 1, + .buf = buf, + } + }; + + out_buf[0] = addr; + out_buf[1] = 0; + + if ((ret = i2c_transfer(&sdvo_priv->i2c_bus->adapter, msgs, 2)) == 2) + { +// DRM_DEBUG("got back from addr %02X = %02x\n", out_buf[0], buf[0]); + *ch = buf[0]; + return true; + } + + DRM_DEBUG("i2c transfer returned %d\n", ret); + return false; +} + +static bool intel_sdvo_write_byte(struct intel_output *intel_output, int addr, + u8 ch) +{ + u8 out_buf[2]; + struct i2c_msg msgs[] = { + { + .addr = intel_output->i2c_bus->slave_addr, + .flags = 0, + .len = 2, + .buf = out_buf, + } + }; + + out_buf[0] = addr; + out_buf[1] = ch; + + if (i2c_transfer(&intel_output->i2c_bus->adapter, msgs, 1) == 1) + { + return true; + } + return false; +} + +#define SDVO_CMD_NAME_ENTRY(cmd) {cmd, #cmd} +/** Mapping of command numbers to names, for debug output */ +const static struct _sdvo_cmd_name { + u8 cmd; + char *name; +} sdvo_cmd_names[] = { + SDVO_CMD_NAME_ENTRY(SDVO_CMD_RESET), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DEVICE_CAPS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FIRMWARE_REV), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TRAINED_INPUTS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_OUTPUTS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_OUTPUTS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_IN_OUT_MAP), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_IN_OUT_MAP), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ATTACHED_DISPLAYS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HOT_PLUG_SUPPORT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_HOT_PLUG), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_HOT_PLUG), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_INPUT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_OUTPUT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART2), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART2), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART2), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART2), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CLOCK_RATE_MULT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CLOCK_RATE_MULT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_TV_FORMATS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_FORMAT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_FORMAT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_RESOLUTION_SUPPORT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTROL_BUS_SWITCH), +}; + +#define SDVO_NAME(dev_priv) ((dev_priv)->output_device == SDVOB ? "SDVOB" : "SDVOC") +#define SDVO_PRIV(output) ((struct intel_sdvo_priv *) (output)->dev_priv) + +static void intel_sdvo_write_cmd(struct intel_output *intel_output, u8 cmd, + void *args, int args_len) +{ + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; + int i; + + if (1) { + DRM_DEBUG("%s: W: %02X ", SDVO_NAME(sdvo_priv), cmd); + for (i = 0; i < args_len; i++) + printk("%02X ", ((u8 *)args)[i]); + for (; i < 8; i++) + printk(" "); + for (i = 0; i < sizeof(sdvo_cmd_names) / sizeof(sdvo_cmd_names[0]); i++) { + if (cmd == sdvo_cmd_names[i].cmd) { + printk("(%s)", sdvo_cmd_names[i].name); + break; + } + } + if (i == sizeof(sdvo_cmd_names)/ sizeof(sdvo_cmd_names[0])) + printk("(%02X)",cmd); + printk("\n"); + } + + for (i = 0; i < args_len; i++) { + intel_sdvo_write_byte(intel_output, SDVO_I2C_ARG_0 - i, ((u8*)args)[i]); + } + + intel_sdvo_write_byte(intel_output, SDVO_I2C_OPCODE, cmd); +} + +static const char *cmd_status_names[] = { + "Power on", + "Success", + "Not supported", + "Invalid arg", + "Pending", + "Target not specified", + "Scaling not supported" +}; + +static u8 intel_sdvo_read_response(struct intel_output *intel_output, void *response, + int response_len) +{ + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; + int i; + u8 status; + u8 retry = 50; + + while (retry--) { + /* Read the command response */ + for (i = 0; i < response_len; i++) { + intel_sdvo_read_byte(intel_output, SDVO_I2C_RETURN_0 + i, + &((u8 *)response)[i]); + } + + /* read the return status */ + intel_sdvo_read_byte(intel_output, SDVO_I2C_CMD_STATUS, &status); + + if (1) { + DRM_DEBUG("%s: R: ", SDVO_NAME(sdvo_priv)); + for (i = 0; i < response_len; i++) + printk("%02X ", ((u8 *)response)[i]); + for (; i < 8; i++) + printk(" "); + if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP) + printk("(%s)", cmd_status_names[status]); + else + printk("(??? %d)", status); + printk("\n"); + } + + if (status != SDVO_CMD_STATUS_PENDING) + return status; + + mdelay(50); + } + + return status; +} + +int intel_sdvo_get_pixel_multiplier(struct drm_display_mode *mode) +{ + if (mode->clock >= 100000) + return 1; + else if (mode->clock >= 50000) + return 2; + else + return 4; +} + +/** + * Don't check status code from this as it switches the bus back to the + * SDVO chips which defeats the purpose of doing a bus switch in the first + * place. + */ +void intel_sdvo_set_control_bus_switch(struct intel_output *intel_output, u8 target) +{ + intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_CONTROL_BUS_SWITCH, &target, 1); +} + +static bool intel_sdvo_set_target_input(struct intel_output *intel_output, bool target_0, bool target_1) +{ + struct intel_sdvo_set_target_input_args targets = {0}; + u8 status; + + if (target_0 && target_1) + return SDVO_CMD_STATUS_NOTSUPP; + + if (target_1) + targets.target_1 = 1; + + intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_TARGET_INPUT, &targets, + sizeof(targets)); + + status = intel_sdvo_read_response(intel_output, NULL, 0); + + return (status == SDVO_CMD_STATUS_SUCCESS); +} + +/** + * Return whether each input is trained. + * + * This function is making an assumption about the layout of the response, + * which should be checked against the docs. + */ +static bool intel_sdvo_get_trained_inputs(struct intel_output *intel_output, bool *input_1, bool *input_2) +{ + struct intel_sdvo_get_trained_inputs_response response; + u8 status; + + intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_TRAINED_INPUTS, NULL, 0); + status = intel_sdvo_read_response(intel_output, &response, sizeof(response)); + if (status != SDVO_CMD_STATUS_SUCCESS) + return false; + + *input_1 = response.input0_trained; + *input_2 = response.input1_trained; + return true; +} + +static bool intel_sdvo_get_active_outputs(struct intel_output *intel_output, + u16 *outputs) +{ + u8 status; + + intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_ACTIVE_OUTPUTS, NULL, 0); + status = intel_sdvo_read_response(intel_output, outputs, sizeof(*outputs)); + + return (status == SDVO_CMD_STATUS_SUCCESS); +} + +static bool intel_sdvo_set_active_outputs(struct intel_output *intel_output, + u16 outputs) +{ + u8 status; + + intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_ACTIVE_OUTPUTS, &outputs, + sizeof(outputs)); + status = intel_sdvo_read_response(intel_output, NULL, 0); + return (status == SDVO_CMD_STATUS_SUCCESS); +} + +static bool intel_sdvo_set_encoder_power_state(struct intel_output *intel_output, + int mode) +{ + u8 status, state = SDVO_ENCODER_STATE_ON; + + switch (mode) { + case DRM_MODE_DPMS_ON: + state = SDVO_ENCODER_STATE_ON; + break; + case DRM_MODE_DPMS_STANDBY: + state = SDVO_ENCODER_STATE_STANDBY; + break; + case DRM_MODE_DPMS_SUSPEND: + state = SDVO_ENCODER_STATE_SUSPEND; + break; + case DRM_MODE_DPMS_OFF: + state = SDVO_ENCODER_STATE_OFF; + break; + } + + intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_ENCODER_POWER_STATE, &state, + sizeof(state)); + status = intel_sdvo_read_response(intel_output, NULL, 0); + + return (status == SDVO_CMD_STATUS_SUCCESS); +} + +static bool intel_sdvo_get_input_pixel_clock_range(struct intel_output *intel_output, + int *clock_min, + int *clock_max) +{ + struct intel_sdvo_pixel_clock_range clocks; + u8 status; + + intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE, + NULL, 0); + + status = intel_sdvo_read_response(intel_output, &clocks, sizeof(clocks)); + + if (status != SDVO_CMD_STATUS_SUCCESS) + return false; + + /* Convert the values from units of 10 kHz to kHz. */ + *clock_min = clocks.min * 10; + *clock_max = clocks.max * 10; + + return true; +} + +static bool intel_sdvo_set_target_output(struct intel_output *intel_output, + u16 outputs) +{ + u8 status; + + intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_TARGET_OUTPUT, &outputs, + sizeof(outputs)); + + status = intel_sdvo_read_response(intel_output, NULL, 0); + return (status == SDVO_CMD_STATUS_SUCCESS); +} + +static bool intel_sdvo_get_timing(struct intel_output *intel_output, u8 cmd, + struct intel_sdvo_dtd *dtd) +{ + u8 status; + + intel_sdvo_write_cmd(intel_output, cmd, NULL, 0); + status = intel_sdvo_read_response(intel_output, &dtd->part1, + sizeof(dtd->part1)); + if (status != SDVO_CMD_STATUS_SUCCESS) + return false; + + intel_sdvo_write_cmd(intel_output, cmd + 1, NULL, 0); + status = intel_sdvo_read_response(intel_output, &dtd->part2, + sizeof(dtd->part2)); + if (status != SDVO_CMD_STATUS_SUCCESS) + return false; + + return true; +} + +static bool intel_sdvo_get_input_timing(struct intel_output *intel_output, + struct intel_sdvo_dtd *dtd) +{ + return intel_sdvo_get_timing(intel_output, + SDVO_CMD_GET_INPUT_TIMINGS_PART1, dtd); +} + +static bool intel_sdvo_get_output_timing(struct intel_output *intel_output, + struct intel_sdvo_dtd *dtd) +{ + return intel_sdvo_get_timing(intel_output, + SDVO_CMD_GET_OUTPUT_TIMINGS_PART1, dtd); +} + +static bool intel_sdvo_set_timing(struct intel_output *intel_output, u8 cmd, + struct intel_sdvo_dtd *dtd) +{ + u8 status; + + intel_sdvo_write_cmd(intel_output, cmd, &dtd->part1, sizeof(dtd->part1)); + status = intel_sdvo_read_response(intel_output, NULL, 0); + if (status != SDVO_CMD_STATUS_SUCCESS) + return false; + + intel_sdvo_write_cmd(intel_output, cmd + 1, &dtd->part2, sizeof(dtd->part2)); + status = intel_sdvo_read_response(intel_output, NULL, 0); + if (status != SDVO_CMD_STATUS_SUCCESS) + return false; + + return true; +} + +static bool intel_sdvo_set_input_timing(struct intel_output *intel_output, + struct intel_sdvo_dtd *dtd) +{ + return intel_sdvo_set_timing(intel_output, + SDVO_CMD_SET_INPUT_TIMINGS_PART1, dtd); +} + +static bool intel_sdvo_set_output_timing(struct intel_output *intel_output, + struct intel_sdvo_dtd *dtd) +{ + return intel_sdvo_set_timing(intel_output, + SDVO_CMD_SET_OUTPUT_TIMINGS_PART1, dtd); +} + +#if 0 +static bool intel_sdvo_get_preferred_input_timing(struct intel_output *intel_output, + struct intel_sdvo_dtd *dtd) +{ + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; + u8 status; + + intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1, + NULL, 0); + + status = intel_sdvo_read_response(intel_output, &dtd->part1, + sizeof(dtd->part1)); + if (status != SDVO_CMD_STATUS_SUCCESS) + return false; + + intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2, + NULL, 0); + status = intel_sdvo_read_response(intel_output, &dtd->part2, + sizeof(dtd->part2)); + if (status != SDVO_CMD_STATUS_SUCCESS) + return false; + + return true; +} +#endif + +static int intel_sdvo_get_clock_rate_mult(struct intel_output *intel_output) +{ + u8 response, status; + + intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_CLOCK_RATE_MULT, NULL, 0); + status = intel_sdvo_read_response(intel_output, &response, 1); + + if (status != SDVO_CMD_STATUS_SUCCESS) { + DRM_DEBUG("Couldn't get SDVO clock rate multiplier\n"); + return SDVO_CLOCK_RATE_MULT_1X; + } else { + DRM_DEBUG("Current clock rate multiplier: %d\n", response); + } + + return response; +} + +static bool intel_sdvo_set_clock_rate_mult(struct intel_output *intel_output, u8 val) +{ + u8 status; + + intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_CLOCK_RATE_MULT, &val, 1); + status = intel_sdvo_read_response(intel_output, NULL, 0); + if (status != SDVO_CMD_STATUS_SUCCESS) + return false; + + return true; +} + +static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + /* Make the CRTC code factor in the SDVO pixel multiplier. The SDVO + * device will be told of the multiplier during mode_set. + */ + adjusted_mode->clock *= intel_sdvo_get_pixel_multiplier(mode); + return true; +} + +static void intel_sdvo_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_crtc *crtc = encoder->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_output *intel_output = enc_to_intel_output(encoder); + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; + u16 width, height; + u16 h_blank_len, h_sync_len, v_blank_len, v_sync_len; + u16 h_sync_offset, v_sync_offset; + u32 sdvox; + struct intel_sdvo_dtd output_dtd; + int sdvo_pixel_multiply; + + if (!mode) + return; + + width = mode->crtc_hdisplay; + height = mode->crtc_vdisplay; + + /* do some mode translations */ + h_blank_len = mode->crtc_hblank_end - mode->crtc_hblank_start; + h_sync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; + + v_blank_len = mode->crtc_vblank_end - mode->crtc_vblank_start; + v_sync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; + + h_sync_offset = mode->crtc_hsync_start - mode->crtc_hblank_start; + v_sync_offset = mode->crtc_vsync_start - mode->crtc_vblank_start; + + output_dtd.part1.clock = mode->clock / 10; + output_dtd.part1.h_active = width & 0xff; + output_dtd.part1.h_blank = h_blank_len & 0xff; + output_dtd.part1.h_high = (((width >> 8) & 0xf) << 4) | + ((h_blank_len >> 8) & 0xf); + output_dtd.part1.v_active = height & 0xff; + output_dtd.part1.v_blank = v_blank_len & 0xff; + output_dtd.part1.v_high = (((height >> 8) & 0xf) << 4) | + ((v_blank_len >> 8) & 0xf); + + output_dtd.part2.h_sync_off = h_sync_offset; + output_dtd.part2.h_sync_width = h_sync_len & 0xff; + output_dtd.part2.v_sync_off_width = (v_sync_offset & 0xf) << 4 | + (v_sync_len & 0xf); + output_dtd.part2.sync_off_width_high = ((h_sync_offset & 0x300) >> 2) | + ((h_sync_len & 0x300) >> 4) | ((v_sync_offset & 0x30) >> 2) | + ((v_sync_len & 0x30) >> 4); + + output_dtd.part2.dtd_flags = 0x18; + if (mode->flags & DRM_MODE_FLAG_PHSYNC) + output_dtd.part2.dtd_flags |= 0x2; + if (mode->flags & DRM_MODE_FLAG_PVSYNC) + output_dtd.part2.dtd_flags |= 0x4; + + output_dtd.part2.sdvo_flags = 0; + output_dtd.part2.v_sync_off_high = v_sync_offset & 0xc0; + output_dtd.part2.reserved = 0; + + /* Set the output timing to the screen */ + intel_sdvo_set_target_output(intel_output, sdvo_priv->active_outputs); + intel_sdvo_set_output_timing(intel_output, &output_dtd); + + /* Set the input timing to the screen. Assume always input 0. */ + intel_sdvo_set_target_input(intel_output, true, false); + + /* We would like to use i830_sdvo_create_preferred_input_timing() to + * provide the device with a timing it can support, if it supports that + * feature. However, presumably we would need to adjust the CRTC to + * output the preferred timing, and we don't support that currently. + */ +#if 0 + success = intel_sdvo_create_preferred_input_timing(intel_output, clock, + width, height); + if (success) { + struct intel_sdvo_dtd *input_dtd; + + intel_sdvo_get_preferred_input_timing(intel_output, &input_dtd); + intel_sdvo_set_input_timing(intel_output, &input_dtd); + } +#else + intel_sdvo_set_input_timing(intel_output, &output_dtd); +#endif + + switch (intel_sdvo_get_pixel_multiplier(mode)) { + case 1: + intel_sdvo_set_clock_rate_mult(intel_output, + SDVO_CLOCK_RATE_MULT_1X); + break; + case 2: + intel_sdvo_set_clock_rate_mult(intel_output, + SDVO_CLOCK_RATE_MULT_2X); + break; + case 4: + intel_sdvo_set_clock_rate_mult(intel_output, + SDVO_CLOCK_RATE_MULT_4X); + break; + } + + /* Set the SDVO control regs. */ + if (0/*IS_I965GM(dev)*/) { + sdvox = SDVO_BORDER_ENABLE; + } else { + sdvox = I915_READ(sdvo_priv->output_device); + switch (sdvo_priv->output_device) { + case SDVOB: + sdvox &= SDVOB_PRESERVE_MASK; + break; + case SDVOC: + sdvox &= SDVOC_PRESERVE_MASK; + break; + } + sdvox |= (9 << 19) | SDVO_BORDER_ENABLE; + } + if (intel_crtc->pipe == 1) + sdvox |= SDVO_PIPE_B_SELECT; + + sdvo_pixel_multiply = intel_sdvo_get_pixel_multiplier(mode); + if (IS_I965G(dev)) { + /* done in crtc_mode_set as the dpll_md reg must be written + early */ + } else if (IS_I945G(dev) || IS_I945GM(dev)) { + /* done in crtc_mode_set as it lives inside the + dpll register */ + } else { + sdvox |= (sdvo_pixel_multiply - 1) << SDVO_PORT_MULTIPLY_SHIFT; + } + + intel_sdvo_write_sdvox(intel_output, sdvox); +} + +static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_output *intel_output = enc_to_intel_output(encoder); + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; + u32 temp; + + if (mode != DRM_MODE_DPMS_ON) { + intel_sdvo_set_active_outputs(intel_output, 0); + if (0) + intel_sdvo_set_encoder_power_state(intel_output, mode); + + if (mode == DRM_MODE_DPMS_OFF) { + temp = I915_READ(sdvo_priv->output_device); + if ((temp & SDVO_ENABLE) != 0) { + intel_sdvo_write_sdvox(intel_output, temp & ~SDVO_ENABLE); + } + } + } else { + bool input1, input2; + int i; + u8 status; + + temp = I915_READ(sdvo_priv->output_device); + if ((temp & SDVO_ENABLE) == 0) + intel_sdvo_write_sdvox(intel_output, temp | SDVO_ENABLE); + for (i = 0; i < 2; i++) + intel_wait_for_vblank(dev); + + status = intel_sdvo_get_trained_inputs(intel_output, &input1, + &input2); + + + /* Warn if the device reported failure to sync. + * A lot of SDVO devices fail to notify of sync, but it's + * a given it the status is a success, we succeeded. + */ + if (status == SDVO_CMD_STATUS_SUCCESS && !input1) { + DRM_DEBUG("First %s output reported failure to sync\n", + SDVO_NAME(sdvo_priv)); + } + + if (0) + intel_sdvo_set_encoder_power_state(intel_output, mode); + intel_sdvo_set_active_outputs(intel_output, sdvo_priv->active_outputs); + } + return; +} + +static void intel_sdvo_save(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_output *intel_output = to_intel_output(connector); + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; + int o; + + sdvo_priv->save_sdvo_mult = intel_sdvo_get_clock_rate_mult(intel_output); + intel_sdvo_get_active_outputs(intel_output, &sdvo_priv->save_active_outputs); + + if (sdvo_priv->caps.sdvo_inputs_mask & 0x1) { + intel_sdvo_set_target_input(intel_output, true, false); + intel_sdvo_get_input_timing(intel_output, + &sdvo_priv->save_input_dtd_1); + } + + if (sdvo_priv->caps.sdvo_inputs_mask & 0x2) { + intel_sdvo_set_target_input(intel_output, false, true); + intel_sdvo_get_input_timing(intel_output, + &sdvo_priv->save_input_dtd_2); + } + + for (o = SDVO_OUTPUT_FIRST; o <= SDVO_OUTPUT_LAST; o++) + { + u16 this_output = (1 << o); + if (sdvo_priv->caps.output_flags & this_output) + { + intel_sdvo_set_target_output(intel_output, this_output); + intel_sdvo_get_output_timing(intel_output, + &sdvo_priv->save_output_dtd[o]); + } + } + + sdvo_priv->save_SDVOX = I915_READ(sdvo_priv->output_device); +} + +static void intel_sdvo_restore(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_output *intel_output = to_intel_output(connector); + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; + int o; + int i; + bool input1, input2; + u8 status; + + intel_sdvo_set_active_outputs(intel_output, 0); + + for (o = SDVO_OUTPUT_FIRST; o <= SDVO_OUTPUT_LAST; o++) + { + u16 this_output = (1 << o); + if (sdvo_priv->caps.output_flags & this_output) { + intel_sdvo_set_target_output(intel_output, this_output); + intel_sdvo_set_output_timing(intel_output, &sdvo_priv->save_output_dtd[o]); + } + } + + if (sdvo_priv->caps.sdvo_inputs_mask & 0x1) { + intel_sdvo_set_target_input(intel_output, true, false); + intel_sdvo_set_input_timing(intel_output, &sdvo_priv->save_input_dtd_1); + } + + if (sdvo_priv->caps.sdvo_inputs_mask & 0x2) { + intel_sdvo_set_target_input(intel_output, false, true); + intel_sdvo_set_input_timing(intel_output, &sdvo_priv->save_input_dtd_2); + } + + intel_sdvo_set_clock_rate_mult(intel_output, sdvo_priv->save_sdvo_mult); + + I915_WRITE(sdvo_priv->output_device, sdvo_priv->save_SDVOX); + + if (sdvo_priv->save_SDVOX & SDVO_ENABLE) + { + for (i = 0; i < 2; i++) + intel_wait_for_vblank(dev); + status = intel_sdvo_get_trained_inputs(intel_output, &input1, &input2); + if (status == SDVO_CMD_STATUS_SUCCESS && !input1) + DRM_DEBUG("First %s output reported failure to sync\n", + SDVO_NAME(sdvo_priv)); + } + + intel_sdvo_set_active_outputs(intel_output, sdvo_priv->save_active_outputs); +} + +static int intel_sdvo_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct intel_output *intel_output = to_intel_output(connector); + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; + + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + return MODE_NO_DBLESCAN; + + if (sdvo_priv->pixel_clock_min > mode->clock) + return MODE_CLOCK_LOW; + + if (sdvo_priv->pixel_clock_max < mode->clock) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + +static bool intel_sdvo_get_capabilities(struct intel_output *intel_output, struct intel_sdvo_caps *caps) +{ + u8 status; + + intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_DEVICE_CAPS, NULL, 0); + status = intel_sdvo_read_response(intel_output, caps, sizeof(*caps)); + if (status != SDVO_CMD_STATUS_SUCCESS) + return false; + + return true; +} + +struct drm_connector* intel_sdvo_find(struct drm_device *dev, int sdvoB) +{ + struct drm_connector *connector = NULL; + struct intel_output *iout = NULL; + struct intel_sdvo_priv *sdvo; + + /* find the sdvo connector */ + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + iout = to_intel_output(connector); + + if (iout->type != INTEL_OUTPUT_SDVO) + continue; + + sdvo = iout->dev_priv; + + if (sdvo->output_device == SDVOB && sdvoB) + return connector; + + if (sdvo->output_device == SDVOC && !sdvoB) + return connector; + + } + + return NULL; +} + +int intel_sdvo_supports_hotplug(struct drm_connector *connector) +{ + u8 response[2]; + u8 status; + struct intel_output *intel_output; + DRM_DEBUG("\n"); + + if (!connector) + return 0; + + intel_output = to_intel_output(connector); + + intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_HOT_PLUG_SUPPORT, NULL, 0); + status = intel_sdvo_read_response(intel_output, &response, 2); + + if (response[0] !=0) + return 1; + + return 0; +} + +void intel_sdvo_set_hotplug(struct drm_connector *connector, int on) +{ + u8 response[2]; + u8 status; + struct intel_output *intel_output = to_intel_output(connector); + + intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_ACTIVE_HOT_PLUG, NULL, 0); + intel_sdvo_read_response(intel_output, &response, 2); + + if (on) { + intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_HOT_PLUG_SUPPORT, NULL, 0); + status = intel_sdvo_read_response(intel_output, &response, 2); + + intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_ACTIVE_HOT_PLUG, &response, 2); + } else { + response[0] = 0; + response[1] = 0; + intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_ACTIVE_HOT_PLUG, &response, 2); + } + + intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_ACTIVE_HOT_PLUG, NULL, 0); + intel_sdvo_read_response(intel_output, &response, 2); +} + +static enum drm_connector_status intel_sdvo_detect(struct drm_connector *connector) +{ + u8 response[2]; + u8 status; + struct intel_output *intel_output = to_intel_output(connector); + + intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_ATTACHED_DISPLAYS, NULL, 0); + status = intel_sdvo_read_response(intel_output, &response, 2); + + DRM_DEBUG("SDVO response %d %d\n", response[0], response[1]); + if ((response[0] != 0) || (response[1] != 0)) + return connector_status_connected; + else + return connector_status_disconnected; +} + +static int intel_sdvo_get_modes(struct drm_connector *connector) +{ + struct intel_output *intel_output = to_intel_output(connector); + + /* set the bus switch and get the modes */ + intel_sdvo_set_control_bus_switch(intel_output, SDVO_CONTROL_BUS_DDC2); + intel_ddc_get_modes(intel_output); + + if (list_empty(&connector->probed_modes)) + return 0; + return 1; +#if 0 + /* Mac mini hack. On this device, I get DDC through the analog, which + * load-detects as disconnected. I fail to DDC through the SDVO DDC, + * but it does load-detect as connected. So, just steal the DDC bits + * from analog when we fail at finding it the right way. + */ + /* TODO */ + return NULL; + + return NULL; +#endif +} + +static void intel_sdvo_destroy(struct drm_connector *connector) +{ + struct intel_output *intel_output = to_intel_output(connector); + + if (intel_output->i2c_bus) + intel_i2c_destroy(intel_output->i2c_bus); + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); + kfree(intel_output); +} + +static const struct drm_encoder_helper_funcs intel_sdvo_helper_funcs = { + .dpms = intel_sdvo_dpms, + .mode_fixup = intel_sdvo_mode_fixup, + .prepare = intel_encoder_prepare, + .mode_set = intel_sdvo_mode_set, + .commit = intel_encoder_commit, +}; + +static const struct drm_connector_funcs intel_sdvo_connector_funcs = { + .save = intel_sdvo_save, + .restore = intel_sdvo_restore, + .detect = intel_sdvo_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = intel_sdvo_destroy, +}; + +static const struct drm_connector_helper_funcs intel_sdvo_connector_helper_funcs = { + .get_modes = intel_sdvo_get_modes, + .mode_valid = intel_sdvo_mode_valid, + .best_encoder = intel_best_encoder, +}; + +void intel_sdvo_enc_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_funcs intel_sdvo_enc_funcs = { + .destroy = intel_sdvo_enc_destroy, +}; + + +void intel_sdvo_init(struct drm_device *dev, int output_device) +{ + struct drm_connector *connector; + struct intel_output *intel_output; + struct intel_sdvo_priv *sdvo_priv; + struct intel_i2c_chan *i2cbus = NULL; + int connector_type; + u8 ch[0x40]; + int i; + int encoder_type, output_id; + + intel_output = kcalloc(sizeof(struct intel_output)+sizeof(struct intel_sdvo_priv), 1, GFP_KERNEL); + if (!intel_output) { + return; + } + + connector = &intel_output->base; + + drm_connector_init(dev, connector, &intel_sdvo_connector_funcs, + DRM_MODE_CONNECTOR_Unknown); + drm_connector_helper_add(connector, &intel_sdvo_connector_helper_funcs); + sdvo_priv = (struct intel_sdvo_priv *)(intel_output + 1); + intel_output->type = INTEL_OUTPUT_SDVO; + + connector->interlace_allowed = 0; + connector->doublescan_allowed = 0; + + /* setup the DDC bus. */ + if (output_device == SDVOB) + i2cbus = intel_i2c_create(dev, GPIOE, "SDVOCTRL_E for SDVOB"); + else + i2cbus = intel_i2c_create(dev, GPIOE, "SDVOCTRL_E for SDVOC"); + + if (!i2cbus) + goto err_connector; + + sdvo_priv->i2c_bus = i2cbus; + + if (output_device == SDVOB) { + output_id = 1; + sdvo_priv->i2c_bus->slave_addr = 0x38; + } else { + output_id = 2; + sdvo_priv->i2c_bus->slave_addr = 0x39; + } + + sdvo_priv->output_device = output_device; + intel_output->i2c_bus = i2cbus; + intel_output->dev_priv = sdvo_priv; + + + /* Read the regs to test if we can talk to the device */ + for (i = 0; i < 0x40; i++) { + if (!intel_sdvo_read_byte(intel_output, i, &ch[i])) { + DRM_DEBUG("No SDVO device found on SDVO%c\n", + output_device == SDVOB ? 'B' : 'C'); + goto err_i2c; + } + } + + intel_sdvo_get_capabilities(intel_output, &sdvo_priv->caps); + + memset(&sdvo_priv->active_outputs, 0, sizeof(sdvo_priv->active_outputs)); + + /* TODO, CVBS, SVID, YPRPB & SCART outputs. */ + if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB0) + { + sdvo_priv->active_outputs = SDVO_OUTPUT_RGB0; + connector->display_info.subpixel_order = SubPixelHorizontalRGB; + encoder_type = DRM_MODE_ENCODER_DAC; + connector_type = DRM_MODE_CONNECTOR_VGA; + } + else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB1) + { + sdvo_priv->active_outputs = SDVO_OUTPUT_RGB1; + connector->display_info.subpixel_order = SubPixelHorizontalRGB; + encoder_type = DRM_MODE_ENCODER_DAC; + connector_type = DRM_MODE_CONNECTOR_VGA; + } + else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0) + { + sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS0; + connector->display_info.subpixel_order = SubPixelHorizontalRGB; + encoder_type = DRM_MODE_ENCODER_TMDS; + connector_type = DRM_MODE_CONNECTOR_DVID; + } + else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS1) + { + sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS1; + connector->display_info.subpixel_order = SubPixelHorizontalRGB; + encoder_type = DRM_MODE_ENCODER_TMDS; + connector_type = DRM_MODE_CONNECTOR_DVID; + } + else + { + unsigned char bytes[2]; + + memcpy (bytes, &sdvo_priv->caps.output_flags, 2); + DRM_DEBUG("%s: No active RGB or TMDS outputs (0x%02x%02x)\n", + SDVO_NAME(sdvo_priv), + bytes[0], bytes[1]); + goto err_i2c; + } + + drm_encoder_init(dev, &intel_output->enc, &intel_sdvo_enc_funcs, encoder_type); + drm_encoder_helper_add(&intel_output->enc, &intel_sdvo_helper_funcs); + connector->connector_type = connector_type; + + drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc); + drm_sysfs_connector_add(connector); + + /* Set the input timing to the screen. Assume always input 0. */ + intel_sdvo_set_target_input(intel_output, true, false); + + intel_sdvo_get_input_pixel_clock_range(intel_output, + &sdvo_priv->pixel_clock_min, + &sdvo_priv->pixel_clock_max); + + + DRM_DEBUG("%s device VID/DID: %02X:%02X.%02X, " + "clock range %dMHz - %dMHz, " + "input 1: %c, input 2: %c, " + "output 1: %c, output 2: %c\n", + SDVO_NAME(sdvo_priv), + sdvo_priv->caps.vendor_id, sdvo_priv->caps.device_id, + sdvo_priv->caps.device_rev_id, + sdvo_priv->pixel_clock_min / 1000, + sdvo_priv->pixel_clock_max / 1000, + (sdvo_priv->caps.sdvo_inputs_mask & 0x1) ? 'Y' : 'N', + (sdvo_priv->caps.sdvo_inputs_mask & 0x2) ? 'Y' : 'N', + /* check currently supported outputs */ + sdvo_priv->caps.output_flags & + (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_RGB0) ? 'Y' : 'N', + sdvo_priv->caps.output_flags & + (SDVO_OUTPUT_TMDS1 | SDVO_OUTPUT_RGB1) ? 'Y' : 'N'); + + intel_output->ddc_bus = i2cbus; + + return; + +err_i2c: + intel_i2c_destroy(intel_output->i2c_bus); +err_connector: + drm_connector_cleanup(connector); + kfree(intel_output); + + return; +} diff --git a/linux-core/intel_sdvo_regs.h b/linux-core/intel_sdvo_regs.h new file mode 100644 index 00000000..a9d16711 --- /dev/null +++ b/linux-core/intel_sdvo_regs.h @@ -0,0 +1,328 @@ +/* + * Copyright © 2006-2007 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Eric Anholt <eric@anholt.net> + */ + +/** + * @file SDVO command definitions and structures. + */ + +#define SDVO_OUTPUT_FIRST (0) +#define SDVO_OUTPUT_TMDS0 (1 << 0) +#define SDVO_OUTPUT_RGB0 (1 << 1) +#define SDVO_OUTPUT_CVBS0 (1 << 2) +#define SDVO_OUTPUT_SVID0 (1 << 3) +#define SDVO_OUTPUT_YPRPB0 (1 << 4) +#define SDVO_OUTPUT_SCART0 (1 << 5) +#define SDVO_OUTPUT_LVDS0 (1 << 6) +#define SDVO_OUTPUT_TMDS1 (1 << 8) +#define SDVO_OUTPUT_RGB1 (1 << 9) +#define SDVO_OUTPUT_CVBS1 (1 << 10) +#define SDVO_OUTPUT_SVID1 (1 << 11) +#define SDVO_OUTPUT_YPRPB1 (1 << 12) +#define SDVO_OUTPUT_SCART1 (1 << 13) +#define SDVO_OUTPUT_LVDS1 (1 << 14) +#define SDVO_OUTPUT_LAST (14) + +struct intel_sdvo_caps { + u8 vendor_id; + u8 device_id; + u8 device_rev_id; + u8 sdvo_version_major; + u8 sdvo_version_minor; + unsigned int sdvo_inputs_mask:2; + unsigned int smooth_scaling:1; + unsigned int sharp_scaling:1; + unsigned int up_scaling:1; + unsigned int down_scaling:1; + unsigned int stall_support:1; + unsigned int pad:1; + u16 output_flags; +} __attribute__((packed)); + +/** This matches the EDID DTD structure, more or less */ +struct intel_sdvo_dtd { + struct { + u16 clock; /**< pixel clock, in 10kHz units */ + u8 h_active; /**< lower 8 bits (pixels) */ + u8 h_blank; /**< lower 8 bits (pixels) */ + u8 h_high; /**< upper 4 bits each h_active, h_blank */ + u8 v_active; /**< lower 8 bits (lines) */ + u8 v_blank; /**< lower 8 bits (lines) */ + u8 v_high; /**< upper 4 bits each v_active, v_blank */ + } part1; + + struct { + u8 h_sync_off; /**< lower 8 bits, from hblank start */ + u8 h_sync_width; /**< lower 8 bits (pixels) */ + /** lower 4 bits each vsync offset, vsync width */ + u8 v_sync_off_width; + /** + * 2 high bits of hsync offset, 2 high bits of hsync width, + * bits 4-5 of vsync offset, and 2 high bits of vsync width. + */ + u8 sync_off_width_high; + u8 dtd_flags; + u8 sdvo_flags; + /** bits 6-7 of vsync offset at bits 6-7 */ + u8 v_sync_off_high; + u8 reserved; + } part2; +} __attribute__((packed)); + +struct intel_sdvo_pixel_clock_range { + u16 min; /**< pixel clock, in 10kHz units */ + u16 max; /**< pixel clock, in 10kHz units */ +} __attribute__((packed)); + +struct intel_sdvo_preferred_input_timing_args { + u16 clock; + u16 width; + u16 height; +} __attribute__((packed)); + +/* I2C registers for SDVO */ +#define SDVO_I2C_ARG_0 0x07 +#define SDVO_I2C_ARG_1 0x06 +#define SDVO_I2C_ARG_2 0x05 +#define SDVO_I2C_ARG_3 0x04 +#define SDVO_I2C_ARG_4 0x03 +#define SDVO_I2C_ARG_5 0x02 +#define SDVO_I2C_ARG_6 0x01 +#define SDVO_I2C_ARG_7 0x00 +#define SDVO_I2C_OPCODE 0x08 +#define SDVO_I2C_CMD_STATUS 0x09 +#define SDVO_I2C_RETURN_0 0x0a +#define SDVO_I2C_RETURN_1 0x0b +#define SDVO_I2C_RETURN_2 0x0c +#define SDVO_I2C_RETURN_3 0x0d +#define SDVO_I2C_RETURN_4 0x0e +#define SDVO_I2C_RETURN_5 0x0f +#define SDVO_I2C_RETURN_6 0x10 +#define SDVO_I2C_RETURN_7 0x11 +#define SDVO_I2C_VENDOR_BEGIN 0x20 + +/* Status results */ +#define SDVO_CMD_STATUS_POWER_ON 0x0 +#define SDVO_CMD_STATUS_SUCCESS 0x1 +#define SDVO_CMD_STATUS_NOTSUPP 0x2 +#define SDVO_CMD_STATUS_INVALID_ARG 0x3 +#define SDVO_CMD_STATUS_PENDING 0x4 +#define SDVO_CMD_STATUS_TARGET_NOT_SPECIFIED 0x5 +#define SDVO_CMD_STATUS_SCALING_NOT_SUPP 0x6 + +/* SDVO commands, argument/result registers */ + +#define SDVO_CMD_RESET 0x01 + +/** Returns a struct intel_sdvo_caps */ +#define SDVO_CMD_GET_DEVICE_CAPS 0x02 + +#define SDVO_CMD_GET_FIRMWARE_REV 0x86 +# define SDVO_DEVICE_FIRMWARE_MINOR SDVO_I2C_RETURN_0 +# define SDVO_DEVICE_FIRMWARE_MAJOR SDVO_I2C_RETURN_1 +# define SDVO_DEVICE_FIRMWARE_PATCH SDVO_I2C_RETURN_2 + +/** + * Reports which inputs are trained (managed to sync). + * + * Devices must have trained within 2 vsyncs of a mode change. + */ +#define SDVO_CMD_GET_TRAINED_INPUTS 0x03 +struct intel_sdvo_get_trained_inputs_response { + unsigned int input0_trained:1; + unsigned int input1_trained:1; + unsigned int pad:6; +} __attribute__((packed)); + +/** Returns a struct intel_sdvo_output_flags of active outputs. */ +#define SDVO_CMD_GET_ACTIVE_OUTPUTS 0x04 + +/** + * Sets the current set of active outputs. + * + * Takes a struct intel_sdvo_output_flags. Must be preceded by a SET_IN_OUT_MAP + * on multi-output devices. + */ +#define SDVO_CMD_SET_ACTIVE_OUTPUTS 0x05 + +/** + * Returns the current mapping of SDVO inputs to outputs on the device. + * + * Returns two struct intel_sdvo_output_flags structures. + */ +#define SDVO_CMD_GET_IN_OUT_MAP 0x06 + +/** + * Sets the current mapping of SDVO inputs to outputs on the device. + * + * Takes two struct i380_sdvo_output_flags structures. + */ +#define SDVO_CMD_SET_IN_OUT_MAP 0x07 + +/** + * Returns a struct intel_sdvo_output_flags of attached displays. + */ +#define SDVO_CMD_GET_ATTACHED_DISPLAYS 0x0b + +/** + * Returns a struct intel_sdvo_ouptut_flags of displays supporting hot plugging. + */ +#define SDVO_CMD_GET_HOT_PLUG_SUPPORT 0x0c + +/** + * Takes a struct intel_sdvo_output_flags. + */ +#define SDVO_CMD_SET_ACTIVE_HOT_PLUG 0x0d + +/** + * Returns a struct intel_sdvo_output_flags of displays with hot plug + * interrupts enabled. + */ +#define SDVO_CMD_GET_ACTIVE_HOT_PLUG 0x0e + +#define SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE 0x0f +struct intel_sdvo_get_interrupt_event_source_response { + u16 interrupt_status; + unsigned int ambient_light_interrupt:1; + unsigned int pad:7; +} __attribute__((packed)); + +/** + * Selects which input is affected by future input commands. + * + * Commands affected include SET_INPUT_TIMINGS_PART[12], + * GET_INPUT_TIMINGS_PART[12], GET_PREFERRED_INPUT_TIMINGS_PART[12], + * GET_INPUT_PIXEL_CLOCK_RANGE, and CREATE_PREFERRED_INPUT_TIMINGS. + */ +#define SDVO_CMD_SET_TARGET_INPUT 0x10 +struct intel_sdvo_set_target_input_args { + unsigned int target_1:1; + unsigned int pad:7; +} __attribute__((packed)); + +/** + * Takes a struct intel_sdvo_output_flags of which outputs are targetted by + * future output commands. + * + * Affected commands inclue SET_OUTPUT_TIMINGS_PART[12], + * GET_OUTPUT_TIMINGS_PART[12], and GET_OUTPUT_PIXEL_CLOCK_RANGE. + */ +#define SDVO_CMD_SET_TARGET_OUTPUT 0x11 + +#define SDVO_CMD_GET_INPUT_TIMINGS_PART1 0x12 +#define SDVO_CMD_GET_INPUT_TIMINGS_PART2 0x13 +#define SDVO_CMD_SET_INPUT_TIMINGS_PART1 0x14 +#define SDVO_CMD_SET_INPUT_TIMINGS_PART2 0x15 +#define SDVO_CMD_SET_OUTPUT_TIMINGS_PART1 0x16 +#define SDVO_CMD_SET_OUTPUT_TIMINGS_PART2 0x17 +#define SDVO_CMD_GET_OUTPUT_TIMINGS_PART1 0x18 +#define SDVO_CMD_GET_OUTPUT_TIMINGS_PART2 0x19 +/* Part 1 */ +# define SDVO_DTD_CLOCK_LOW SDVO_I2C_ARG_0 +# define SDVO_DTD_CLOCK_HIGH SDVO_I2C_ARG_1 +# define SDVO_DTD_H_ACTIVE SDVO_I2C_ARG_2 +# define SDVO_DTD_H_BLANK SDVO_I2C_ARG_3 +# define SDVO_DTD_H_HIGH SDVO_I2C_ARG_4 +# define SDVO_DTD_V_ACTIVE SDVO_I2C_ARG_5 +# define SDVO_DTD_V_BLANK SDVO_I2C_ARG_6 +# define SDVO_DTD_V_HIGH SDVO_I2C_ARG_7 +/* Part 2 */ +# define SDVO_DTD_HSYNC_OFF SDVO_I2C_ARG_0 +# define SDVO_DTD_HSYNC_WIDTH SDVO_I2C_ARG_1 +# define SDVO_DTD_VSYNC_OFF_WIDTH SDVO_I2C_ARG_2 +# define SDVO_DTD_SYNC_OFF_WIDTH_HIGH SDVO_I2C_ARG_3 +# define SDVO_DTD_DTD_FLAGS SDVO_I2C_ARG_4 +# define SDVO_DTD_DTD_FLAG_INTERLACED (1 << 7) +# define SDVO_DTD_DTD_FLAG_STEREO_MASK (3 << 5) +# define SDVO_DTD_DTD_FLAG_INPUT_MASK (3 << 3) +# define SDVO_DTD_DTD_FLAG_SYNC_MASK (3 << 1) +# define SDVO_DTD_SDVO_FLAS SDVO_I2C_ARG_5 +# define SDVO_DTD_SDVO_FLAG_STALL (1 << 7) +# define SDVO_DTD_SDVO_FLAG_CENTERED (0 << 6) +# define SDVO_DTD_SDVO_FLAG_UPPER_LEFT (1 << 6) +# define SDVO_DTD_SDVO_FLAG_SCALING_MASK (3 << 4) +# define SDVO_DTD_SDVO_FLAG_SCALING_NONE (0 << 4) +# define SDVO_DTD_SDVO_FLAG_SCALING_SHARP (1 << 4) +# define SDVO_DTD_SDVO_FLAG_SCALING_SMOOTH (2 << 4) +# define SDVO_DTD_VSYNC_OFF_HIGH SDVO_I2C_ARG_6 + +/** + * Generates a DTD based on the given width, height, and flags. + * + * This will be supported by any device supporting scaling or interlaced + * modes. + */ +#define SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING 0x1a +# define SDVO_PREFERRED_INPUT_TIMING_CLOCK_LOW SDVO_I2C_ARG_0 +# define SDVO_PREFERRED_INPUT_TIMING_CLOCK_HIGH SDVO_I2C_ARG_1 +# define SDVO_PREFERRED_INPUT_TIMING_WIDTH_LOW SDVO_I2C_ARG_2 +# define SDVO_PREFERRED_INPUT_TIMING_WIDTH_HIGH SDVO_I2C_ARG_3 +# define SDVO_PREFERRED_INPUT_TIMING_HEIGHT_LOW SDVO_I2C_ARG_4 +# define SDVO_PREFERRED_INPUT_TIMING_HEIGHT_HIGH SDVO_I2C_ARG_5 +# define SDVO_PREFERRED_INPUT_TIMING_FLAGS SDVO_I2C_ARG_6 +# define SDVO_PREFERRED_INPUT_TIMING_FLAGS_INTERLACED (1 << 0) +# define SDVO_PREFERRED_INPUT_TIMING_FLAGS_SCALED (1 << 1) + +#define SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1 0x1b +#define SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2 0x1c + +/** Returns a struct intel_sdvo_pixel_clock_range */ +#define SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE 0x1d +/** Returns a struct intel_sdvo_pixel_clock_range */ +#define SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE 0x1e + +/** Returns a byte bitfield containing SDVO_CLOCK_RATE_MULT_* flags */ +#define SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS 0x1f + +/** Returns a byte containing a SDVO_CLOCK_RATE_MULT_* flag */ +#define SDVO_CMD_GET_CLOCK_RATE_MULT 0x20 +/** Takes a byte containing a SDVO_CLOCK_RATE_MULT_* flag */ +#define SDVO_CMD_SET_CLOCK_RATE_MULT 0x21 +# define SDVO_CLOCK_RATE_MULT_1X (1 << 0) +# define SDVO_CLOCK_RATE_MULT_2X (1 << 1) +# define SDVO_CLOCK_RATE_MULT_4X (1 << 3) + +#define SDVO_CMD_GET_SUPPORTED_TV_FORMATS 0x27 + +#define SDVO_CMD_GET_TV_FORMAT 0x28 + +#define SDVO_CMD_SET_TV_FORMAT 0x29 + +#define SDVO_CMD_GET_SUPPORTED_POWER_STATES 0x2a +#define SDVO_CMD_GET_ENCODER_POWER_STATE 0x2b +#define SDVO_CMD_SET_ENCODER_POWER_STATE 0x2c +# define SDVO_ENCODER_STATE_ON (1 << 0) +# define SDVO_ENCODER_STATE_STANDBY (1 << 1) +# define SDVO_ENCODER_STATE_SUSPEND (1 << 2) +# define SDVO_ENCODER_STATE_OFF (1 << 3) + +#define SDVO_CMD_SET_TV_RESOLUTION_SUPPORT 0x93 + +#define SDVO_CMD_SET_CONTROL_BUS_SWITCH 0x7a +# define SDVO_CONTROL_BUS_PROM 0x0 +# define SDVO_CONTROL_BUS_DDC1 0x1 +# define SDVO_CONTROL_BUS_DDC2 0x2 +# define SDVO_CONTROL_BUS_DDC3 0x3 + diff --git a/linux-core/intel_tv.c b/linux-core/intel_tv.c new file mode 100644 index 00000000..3efa9d2d --- /dev/null +++ b/linux-core/intel_tv.c @@ -0,0 +1,1734 @@ +/* + * Copyright © 2006 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * + */ + +/** @file + * Integrated TV-out support for the 915GM and 945GM. + */ + +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "drm_edid.h" +#include "intel_drv.h" +#include "i915_drm.h" +#include "i915_drv.h" + +enum tv_margin { + TV_MARGIN_LEFT, TV_MARGIN_TOP, + TV_MARGIN_RIGHT, TV_MARGIN_BOTTOM +}; + +/** Private structure for the integrated TV support */ +struct intel_tv_priv { + int type; + char *tv_format; + int margin[4]; + u32 save_TV_H_CTL_1; + u32 save_TV_H_CTL_2; + u32 save_TV_H_CTL_3; + u32 save_TV_V_CTL_1; + u32 save_TV_V_CTL_2; + u32 save_TV_V_CTL_3; + u32 save_TV_V_CTL_4; + u32 save_TV_V_CTL_5; + u32 save_TV_V_CTL_6; + u32 save_TV_V_CTL_7; + u32 save_TV_SC_CTL_1, save_TV_SC_CTL_2, save_TV_SC_CTL_3; + + u32 save_TV_CSC_Y; + u32 save_TV_CSC_Y2; + u32 save_TV_CSC_U; + u32 save_TV_CSC_U2; + u32 save_TV_CSC_V; + u32 save_TV_CSC_V2; + u32 save_TV_CLR_KNOBS; + u32 save_TV_CLR_LEVEL; + u32 save_TV_WIN_POS; + u32 save_TV_WIN_SIZE; + u32 save_TV_FILTER_CTL_1; + u32 save_TV_FILTER_CTL_2; + u32 save_TV_FILTER_CTL_3; + + u32 save_TV_H_LUMA[60]; + u32 save_TV_H_CHROMA[60]; + u32 save_TV_V_LUMA[43]; + u32 save_TV_V_CHROMA[43]; + + u32 save_TV_DAC; + u32 save_TV_CTL; +}; + +struct video_levels { + int blank, black, burst; +}; + +struct color_conversion { + u16 ry, gy, by, ay; + u16 ru, gu, bu, au; + u16 rv, gv, bv, av; +}; + +static const u32 filter_table[] = { + 0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140, + 0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000, + 0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160, + 0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780, + 0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50, + 0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20, + 0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0, + 0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0, + 0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020, + 0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140, + 0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20, + 0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848, + 0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900, + 0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080, + 0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060, + 0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140, + 0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000, + 0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160, + 0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780, + 0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50, + 0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20, + 0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0, + 0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0, + 0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020, + 0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140, + 0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20, + 0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848, + 0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900, + 0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080, + 0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060, + 0x36403000, 0x2D002CC0, 0x30003640, 0x2D0036C0, + 0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540, + 0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00, + 0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000, + 0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00, + 0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40, + 0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240, + 0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00, + 0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0, + 0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840, + 0x28003100, 0x28002F00, 0x00003100, 0x36403000, + 0x2D002CC0, 0x30003640, 0x2D0036C0, + 0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540, + 0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00, + 0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000, + 0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00, + 0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40, + 0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240, + 0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00, + 0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0, + 0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840, + 0x28003100, 0x28002F00, 0x00003100, +}; + +/* + * Color conversion values have 3 separate fixed point formats: + * + * 10 bit fields (ay, au) + * 1.9 fixed point (b.bbbbbbbbb) + * 11 bit fields (ry, by, ru, gu, gv) + * exp.mantissa (ee.mmmmmmmmm) + * ee = 00 = 10^-1 (0.mmmmmmmmm) + * ee = 01 = 10^-2 (0.0mmmmmmmmm) + * ee = 10 = 10^-3 (0.00mmmmmmmmm) + * ee = 11 = 10^-4 (0.000mmmmmmmmm) + * 12 bit fields (gy, rv, bu) + * exp.mantissa (eee.mmmmmmmmm) + * eee = 000 = 10^-1 (0.mmmmmmmmm) + * eee = 001 = 10^-2 (0.0mmmmmmmmm) + * eee = 010 = 10^-3 (0.00mmmmmmmmm) + * eee = 011 = 10^-4 (0.000mmmmmmmmm) + * eee = 100 = reserved + * eee = 101 = reserved + * eee = 110 = reserved + * eee = 111 = 10^0 (m.mmmmmmmm) (only usable for 1.0 representation) + * + * Saturation and contrast are 8 bits, with their own representation: + * 8 bit field (saturation, contrast) + * exp.mantissa (ee.mmmmmm) + * ee = 00 = 10^-1 (0.mmmmmm) + * ee = 01 = 10^0 (m.mmmmm) + * ee = 10 = 10^1 (mm.mmmm) + * ee = 11 = 10^2 (mmm.mmm) + * + * Simple conversion function: + * + * static u32 + * float_to_csc_11(float f) + * { + * u32 exp; + * u32 mant; + * u32 ret; + * + * if (f < 0) + * f = -f; + * + * if (f >= 1) { + * exp = 0x7; + * mant = 1 << 8; + * } else { + * for (exp = 0; exp < 3 && f < 0.5; exp++) + * f *= 2.0; + * mant = (f * (1 << 9) + 0.5); + * if (mant >= (1 << 9)) + * mant = (1 << 9) - 1; + * } + * ret = (exp << 9) | mant; + * return ret; + * } + */ + +/* + * Behold, magic numbers! If we plant them they might grow a big + * s-video cable to the sky... or something. + * + * Pre-converted to appropriate hex value. + */ + +/* + * PAL & NTSC values for composite & s-video connections + */ +static const struct color_conversion ntsc_m_csc_composite = { + .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104, + .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0f00, + .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0f00, +}; + +static const struct video_levels ntsc_m_levels_composite = { + .blank = 225, .black = 267, .burst = 113, +}; + +static const struct color_conversion ntsc_m_csc_svideo = { + .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0134, + .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0f00, + .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0f00, +}; + +static const struct video_levels ntsc_m_levels_svideo = { + .blank = 266, .black = 316, .burst = 133, +}; + +static const struct color_conversion ntsc_j_csc_composite = { + .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0119, + .ru = 0x074c, .gu = 0x0546, .bu = 0x05ec, .au = 0x0f00, + .rv = 0x035a, .gv = 0x0322, .bv = 0x06e1, .av = 0x0f00, +}; + +static const struct video_levels ntsc_j_levels_composite = { + .blank = 225, .black = 225, .burst = 113, +}; + +static const struct color_conversion ntsc_j_csc_svideo = { + .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x014c, + .ru = 0x0788, .gu = 0x0581, .bu = 0x0322, .au = 0x0f00, + .rv = 0x0399, .gv = 0x0356, .bv = 0x070a, .av = 0x0f00, +}; + +static const struct video_levels ntsc_j_levels_svideo = { + .blank = 266, .black = 266, .burst = 133, +}; + +static const struct color_conversion pal_csc_composite = { + .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0113, + .ru = 0x0745, .gu = 0x053f, .bu = 0x05e1, .au = 0x0f00, + .rv = 0x0353, .gv = 0x031c, .bv = 0x06dc, .av = 0x0f00, +}; + +static const struct video_levels pal_levels_composite = { + .blank = 237, .black = 237, .burst = 118, +}; + +static const struct color_conversion pal_csc_svideo = { + .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145, + .ru = 0x0780, .gu = 0x0579, .bu = 0x031c, .au = 0x0f00, + .rv = 0x0390, .gv = 0x034f, .bv = 0x0705, .av = 0x0f00, +}; + +static const struct video_levels pal_levels_svideo = { + .blank = 280, .black = 280, .burst = 139, +}; + +static const struct color_conversion pal_m_csc_composite = { + .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104, + .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0f00, + .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0f00, +}; + +static const struct video_levels pal_m_levels_composite = { + .blank = 225, .black = 267, .burst = 113, +}; + +static const struct color_conversion pal_m_csc_svideo = { + .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0134, + .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0f00, + .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0f00, +}; + +static const struct video_levels pal_m_levels_svideo = { + .blank = 266, .black = 316, .burst = 133, +}; + +static const struct color_conversion pal_n_csc_composite = { + .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104, + .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0f00, + .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0f00, +}; + +static const struct video_levels pal_n_levels_composite = { + .blank = 225, .black = 267, .burst = 118, +}; + +static const struct color_conversion pal_n_csc_svideo = { + .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0134, + .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0f00, + .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0f00, +}; + +static const struct video_levels pal_n_levels_svideo = { + .blank = 266, .black = 316, .burst = 139, +}; + +/* + * Component connections + */ +static const struct color_conversion sdtv_csc_yprpb = { + .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0146, + .ru = 0x0559, .gu = 0x0353, .bu = 0x0100, .au = 0x0f00, + .rv = 0x0100, .gv = 0x03ad, .bv = 0x074d, .av = 0x0f00, +}; + +static const struct color_conversion sdtv_csc_rgb = { + .ry = 0x0000, .gy = 0x0f00, .by = 0x0000, .ay = 0x0166, + .ru = 0x0000, .gu = 0x0000, .bu = 0x0f00, .au = 0x0166, + .rv = 0x0f00, .gv = 0x0000, .bv = 0x0000, .av = 0x0166, +}; + +static const struct color_conversion hdtv_csc_yprpb = { + .ry = 0x05b3, .gy = 0x016e, .by = 0x0728, .ay = 0x0146, + .ru = 0x07d5, .gu = 0x038b, .bu = 0x0100, .au = 0x0f00, + .rv = 0x0100, .gv = 0x03d1, .bv = 0x06bc, .av = 0x0f00, +}; + +static const struct color_conversion hdtv_csc_rgb = { + .ry = 0x0000, .gy = 0x0f00, .by = 0x0000, .ay = 0x0166, + .ru = 0x0000, .gu = 0x0000, .bu = 0x0f00, .au = 0x0166, + .rv = 0x0f00, .gv = 0x0000, .bv = 0x0000, .av = 0x0166, +}; + +static const struct video_levels component_levels = { + .blank = 279, .black = 279, .burst = 0, +}; + + +struct tv_mode { + char *name; + int clock; + int refresh; /* in millihertz (for precision) */ + u32 oversample; + int hsync_end, hblank_start, hblank_end, htotal; + bool progressive, trilevel_sync, component_only; + int vsync_start_f1, vsync_start_f2, vsync_len; + bool veq_ena; + int veq_start_f1, veq_start_f2, veq_len; + int vi_end_f1, vi_end_f2, nbr_end; + bool burst_ena; + int hburst_start, hburst_len; + int vburst_start_f1, vburst_end_f1; + int vburst_start_f2, vburst_end_f2; + int vburst_start_f3, vburst_end_f3; + int vburst_start_f4, vburst_end_f4; + /* + * subcarrier programming + */ + int dda2_size, dda3_size, dda1_inc, dda2_inc, dda3_inc; + u32 sc_reset; + bool pal_burst; + /* + * blank/black levels + */ + const struct video_levels *composite_levels, *svideo_levels; + const struct color_conversion *composite_color, *svideo_color; + const u32 *filter_table; + int max_srcw; +}; + + +/* + * Sub carrier DDA + * + * I think this works as follows: + * + * subcarrier freq = pixel_clock * (dda1_inc + dda2_inc / dda2_size) / 4096 + * + * Presumably, when dda3 is added in, it gets to adjust the dda2_inc value + * + * So, + * dda1_ideal = subcarrier/pixel * 4096 + * dda1_inc = floor (dda1_ideal) + * dda2 = dda1_ideal - dda1_inc + * + * then pick a ratio for dda2 that gives the closest approximation. If + * you can't get close enough, you can play with dda3 as well. This + * seems likely to happen when dda2 is small as the jumps would be larger + * + * To invert this, + * + * pixel_clock = subcarrier * 4096 / (dda1_inc + dda2_inc / dda2_size) + * + * The constants below were all computed using a 107.520MHz clock + */ + +/** + * Register programming values for TV modes. + * + * These values account for -1s required. + */ + +const static struct tv_mode tv_modes[] = { + { + .name = "NTSC-M", + .clock = 107520, + .refresh = 29970, + .oversample = TV_OVERSAMPLE_8X, + .component_only = 0, + /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */ + + .hsync_end = 64, .hblank_end = 124, + .hblank_start = 836, .htotal = 857, + + .progressive = false, .trilevel_sync = false, + + .vsync_start_f1 = 6, .vsync_start_f2 = 7, + .vsync_len = 6, + + .veq_ena = true, .veq_start_f1 = 0, + .veq_start_f2 = 1, .veq_len = 18, + + .vi_end_f1 = 20, .vi_end_f2 = 21, + .nbr_end = 240, + + .burst_ena = true, + .hburst_start = 72, .hburst_len = 34, + .vburst_start_f1 = 9, .vburst_end_f1 = 240, + .vburst_start_f2 = 10, .vburst_end_f2 = 240, + .vburst_start_f3 = 9, .vburst_end_f3 = 240, + .vburst_start_f4 = 10, .vburst_end_f4 = 240, + + /* desired 3.5800000 actual 3.5800000 clock 107.52 */ + .dda1_inc = 136, + .dda2_inc = 7624, .dda2_size = 20013, + .dda3_inc = 0, .dda3_size = 0, + .sc_reset = TV_SC_RESET_EVERY_4, + .pal_burst = false, + + .composite_levels = &ntsc_m_levels_composite, + .composite_color = &ntsc_m_csc_composite, + .svideo_levels = &ntsc_m_levels_svideo, + .svideo_color = &ntsc_m_csc_svideo, + + .filter_table = filter_table, + }, + { + .name = "NTSC-443", + .clock = 107520, + .refresh = 29970, + .oversample = TV_OVERSAMPLE_8X, + .component_only = 0, + /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 4.43MHz */ + .hsync_end = 64, .hblank_end = 124, + .hblank_start = 836, .htotal = 857, + + .progressive = false, .trilevel_sync = false, + + .vsync_start_f1 = 6, .vsync_start_f2 = 7, + .vsync_len = 6, + + .veq_ena = true, .veq_start_f1 = 0, + .veq_start_f2 = 1, .veq_len = 18, + + .vi_end_f1 = 20, .vi_end_f2 = 21, + .nbr_end = 240, + + .burst_ena = 8, + .hburst_start = 72, .hburst_len = 34, + .vburst_start_f1 = 9, .vburst_end_f1 = 240, + .vburst_start_f2 = 10, .vburst_end_f2 = 240, + .vburst_start_f3 = 9, .vburst_end_f3 = 240, + .vburst_start_f4 = 10, .vburst_end_f4 = 240, + + /* desired 4.4336180 actual 4.4336180 clock 107.52 */ + .dda1_inc = 168, + .dda2_inc = 18557, .dda2_size = 20625, + .dda3_inc = 0, .dda3_size = 0, + .sc_reset = TV_SC_RESET_EVERY_8, + .pal_burst = true, + + .composite_levels = &ntsc_m_levels_composite, + .composite_color = &ntsc_m_csc_composite, + .svideo_levels = &ntsc_m_levels_svideo, + .svideo_color = &ntsc_m_csc_svideo, + + .filter_table = filter_table, + }, + { + .name = "NTSC-J", + .clock = 107520, + .refresh = 29970, + .oversample = TV_OVERSAMPLE_8X, + .component_only = 0, + + /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */ + .hsync_end = 64, .hblank_end = 124, + .hblank_start = 836, .htotal = 857, + + .progressive = false, .trilevel_sync = false, + + .vsync_start_f1 = 6, .vsync_start_f2 = 7, + .vsync_len = 6, + + .veq_ena = true, .veq_start_f1 = 0, + .veq_start_f2 = 1, .veq_len = 18, + + .vi_end_f1 = 20, .vi_end_f2 = 21, + .nbr_end = 240, + + .burst_ena = true, + .hburst_start = 72, .hburst_len = 34, + .vburst_start_f1 = 9, .vburst_end_f1 = 240, + .vburst_start_f2 = 10, .vburst_end_f2 = 240, + .vburst_start_f3 = 9, .vburst_end_f3 = 240, + .vburst_start_f4 = 10, .vburst_end_f4 = 240, + + /* desired 3.5800000 actual 3.5800000 clock 107.52 */ + .dda1_inc = 136, + .dda2_inc = 7624, .dda2_size = 20013, + .dda3_inc = 0, .dda3_size = 0, + .sc_reset = TV_SC_RESET_EVERY_4, + .pal_burst = false, + + .composite_levels = &ntsc_j_levels_composite, + .composite_color = &ntsc_j_csc_composite, + .svideo_levels = &ntsc_j_levels_svideo, + .svideo_color = &ntsc_j_csc_svideo, + + .filter_table = filter_table, + }, + { + .name = "PAL-M", + .clock = 107520, + .refresh = 29970, + .oversample = TV_OVERSAMPLE_8X, + .component_only = 0, + + /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */ + .hsync_end = 64, .hblank_end = 124, + .hblank_start = 836, .htotal = 857, + + .progressive = false, .trilevel_sync = false, + + .vsync_start_f1 = 6, .vsync_start_f2 = 7, + .vsync_len = 6, + + .veq_ena = true, .veq_start_f1 = 0, + .veq_start_f2 = 1, .veq_len = 18, + + .vi_end_f1 = 20, .vi_end_f2 = 21, + .nbr_end = 240, + + .burst_ena = true, + .hburst_start = 72, .hburst_len = 34, + .vburst_start_f1 = 9, .vburst_end_f1 = 240, + .vburst_start_f2 = 10, .vburst_end_f2 = 240, + .vburst_start_f3 = 9, .vburst_end_f3 = 240, + .vburst_start_f4 = 10, .vburst_end_f4 = 240, + + /* desired 3.5800000 actual 3.5800000 clock 107.52 */ + .dda1_inc = 136, + .dda2_inc = 7624, .dda2_size = 20013, + .dda3_inc = 0, .dda3_size = 0, + .sc_reset = TV_SC_RESET_EVERY_4, + .pal_burst = false, + + .composite_levels = &pal_m_levels_composite, + .composite_color = &pal_m_csc_composite, + .svideo_levels = &pal_m_levels_svideo, + .svideo_color = &pal_m_csc_svideo, + + .filter_table = filter_table, + }, + { + /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */ + .name = "PAL-N", + .clock = 107520, + .refresh = 25000, + .oversample = TV_OVERSAMPLE_8X, + .component_only = 0, + + .hsync_end = 64, .hblank_end = 128, + .hblank_start = 844, .htotal = 863, + + .progressive = false, .trilevel_sync = false, + + + .vsync_start_f1 = 6, .vsync_start_f2 = 7, + .vsync_len = 6, + + .veq_ena = true, .veq_start_f1 = 0, + .veq_start_f2 = 1, .veq_len = 18, + + .vi_end_f1 = 24, .vi_end_f2 = 25, + .nbr_end = 286, + + .burst_ena = true, + .hburst_start = 73, .hburst_len = 34, + .vburst_start_f1 = 8, .vburst_end_f1 = 285, + .vburst_start_f2 = 8, .vburst_end_f2 = 286, + .vburst_start_f3 = 9, .vburst_end_f3 = 286, + .vburst_start_f4 = 9, .vburst_end_f4 = 285, + + + /* desired 4.4336180 actual 4.4336180 clock 107.52 */ + .dda1_inc = 168, + .dda2_inc = 18557, .dda2_size = 20625, + .dda3_inc = 0, .dda3_size = 0, + .sc_reset = TV_SC_RESET_EVERY_8, + .pal_burst = true, + + .composite_levels = &pal_n_levels_composite, + .composite_color = &pal_n_csc_composite, + .svideo_levels = &pal_n_levels_svideo, + .svideo_color = &pal_n_csc_svideo, + + .filter_table = filter_table, + }, + { + /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */ + .name = "PAL", + .clock = 107520, + .refresh = 25000, + .oversample = TV_OVERSAMPLE_8X, + .component_only = 0, + + .hsync_end = 64, .hblank_end = 128, + .hblank_start = 844, .htotal = 863, + + .progressive = false, .trilevel_sync = false, + + .vsync_start_f1 = 5, .vsync_start_f2 = 6, + .vsync_len = 5, + + .veq_ena = true, .veq_start_f1 = 0, + .veq_start_f2 = 1, .veq_len = 15, + + .vi_end_f1 = 24, .vi_end_f2 = 25, + .nbr_end = 286, + + .burst_ena = true, + .hburst_start = 73, .hburst_len = 32, + .vburst_start_f1 = 8, .vburst_end_f1 = 285, + .vburst_start_f2 = 8, .vburst_end_f2 = 286, + .vburst_start_f3 = 9, .vburst_end_f3 = 286, + .vburst_start_f4 = 9, .vburst_end_f4 = 285, + + /* desired 4.4336180 actual 4.4336180 clock 107.52 */ + .dda1_inc = 168, + .dda2_inc = 18557, .dda2_size = 20625, + .dda3_inc = 0, .dda3_size = 0, + .sc_reset = TV_SC_RESET_EVERY_8, + .pal_burst = true, + + .composite_levels = &pal_levels_composite, + .composite_color = &pal_csc_composite, + .svideo_levels = &pal_levels_svideo, + .svideo_color = &pal_csc_svideo, + + .filter_table = filter_table, + }, + { + .name = "480p@59.94Hz", + .clock = 107520, + .refresh = 59940, + .oversample = TV_OVERSAMPLE_4X, + .component_only = 1, + + .hsync_end = 64, .hblank_end = 122, + .hblank_start = 842, .htotal = 857, + + .progressive = true,.trilevel_sync = false, + + .vsync_start_f1 = 12, .vsync_start_f2 = 12, + .vsync_len = 12, + + .veq_ena = false, + + .vi_end_f1 = 44, .vi_end_f2 = 44, + .nbr_end = 496, + + .burst_ena = false, + + .filter_table = filter_table, + }, + { + .name = "480p@60Hz", + .clock = 107520, + .refresh = 60000, + .oversample = TV_OVERSAMPLE_4X, + .component_only = 1, + + .hsync_end = 64, .hblank_end = 122, + .hblank_start = 842, .htotal = 856, + + .progressive = true,.trilevel_sync = false, + + .vsync_start_f1 = 12, .vsync_start_f2 = 12, + .vsync_len = 12, + + .veq_ena = false, + + .vi_end_f1 = 44, .vi_end_f2 = 44, + .nbr_end = 496, + + .burst_ena = false, + + .filter_table = filter_table, + }, + { + .name = "576p", + .clock = 107520, + .refresh = 50000, + .oversample = TV_OVERSAMPLE_4X, + .component_only = 1, + + .hsync_end = 64, .hblank_end = 139, + .hblank_start = 859, .htotal = 863, + + .progressive = true, .trilevel_sync = false, + + .vsync_start_f1 = 10, .vsync_start_f2 = 10, + .vsync_len = 10, + + .veq_ena = false, + + .vi_end_f1 = 48, .vi_end_f2 = 48, + .nbr_end = 575, + + .burst_ena = false, + + .filter_table = filter_table, + }, + { + .name = "720p@60Hz", + .clock = 148800, + .refresh = 60000, + .oversample = TV_OVERSAMPLE_2X, + .component_only = 1, + + .hsync_end = 80, .hblank_end = 300, + .hblank_start = 1580, .htotal = 1649, + + .progressive = true, .trilevel_sync = true, + + .vsync_start_f1 = 10, .vsync_start_f2 = 10, + .vsync_len = 10, + + .veq_ena = false, + + .vi_end_f1 = 29, .vi_end_f2 = 29, + .nbr_end = 719, + + .burst_ena = false, + + .filter_table = filter_table, + }, + { + .name = "720p@59.94Hz", + .clock = 148800, + .refresh = 59940, + .oversample = TV_OVERSAMPLE_2X, + .component_only = 1, + + .hsync_end = 80, .hblank_end = 300, + .hblank_start = 1580, .htotal = 1651, + + .progressive = true, .trilevel_sync = true, + + .vsync_start_f1 = 10, .vsync_start_f2 = 10, + .vsync_len = 10, + + .veq_ena = false, + + .vi_end_f1 = 29, .vi_end_f2 = 29, + .nbr_end = 719, + + .burst_ena = false, + + .filter_table = filter_table, + }, + { + .name = "720p@50Hz", + .clock = 148800, + .refresh = 50000, + .oversample = TV_OVERSAMPLE_2X, + .component_only = 1, + + .hsync_end = 80, .hblank_end = 300, + .hblank_start = 1580, .htotal = 1979, + + .progressive = true, .trilevel_sync = true, + + .vsync_start_f1 = 10, .vsync_start_f2 = 10, + .vsync_len = 10, + + .veq_ena = false, + + .vi_end_f1 = 29, .vi_end_f2 = 29, + .nbr_end = 719, + + .burst_ena = false, + + .filter_table = filter_table, + .max_srcw = 800 + }, + { + .name = "1080i@50Hz", + .clock = 148800, + .refresh = 25000, + .oversample = TV_OVERSAMPLE_2X, + .component_only = 1, + + .hsync_end = 88, .hblank_end = 235, + .hblank_start = 2155, .htotal = 2639, + + .progressive = false, .trilevel_sync = true, + + .vsync_start_f1 = 4, .vsync_start_f2 = 5, + .vsync_len = 10, + + .veq_ena = true, .veq_start_f1 = 4, + .veq_start_f2 = 4, .veq_len = 10, + + + .vi_end_f1 = 21, .vi_end_f2 = 22, + .nbr_end = 539, + + .burst_ena = false, + + .filter_table = filter_table, + }, + { + .name = "1080i@60Hz", + .clock = 148800, + .refresh = 30000, + .oversample = TV_OVERSAMPLE_2X, + .component_only = 1, + + .hsync_end = 88, .hblank_end = 235, + .hblank_start = 2155, .htotal = 2199, + + .progressive = false, .trilevel_sync = true, + + .vsync_start_f1 = 4, .vsync_start_f2 = 5, + .vsync_len = 10, + + .veq_ena = true, .veq_start_f1 = 4, + .veq_start_f2 = 4, .veq_len = 10, + + + .vi_end_f1 = 21, .vi_end_f2 = 22, + .nbr_end = 539, + + .burst_ena = false, + + .filter_table = filter_table, + }, + { + .name = "1080i@59.94Hz", + .clock = 148800, + .refresh = 29970, + .oversample = TV_OVERSAMPLE_2X, + .component_only = 1, + + .hsync_end = 88, .hblank_end = 235, + .hblank_start = 2155, .htotal = 2200, + + .progressive = false, .trilevel_sync = true, + + .vsync_start_f1 = 4, .vsync_start_f2 = 5, + .vsync_len = 10, + + .veq_ena = true, .veq_start_f1 = 4, + .veq_start_f2 = 4, .veq_len = 10, + + + .vi_end_f1 = 21, .vi_end_f2 = 22, + .nbr_end = 539, + + .burst_ena = false, + + .filter_table = filter_table, + }, +}; + +#define NUM_TV_MODES sizeof(tv_modes) / sizeof (tv_modes[0]) + +static void +intel_tv_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + switch(mode) { + case DRM_MODE_DPMS_ON: + I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE); + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + I915_WRITE(TV_CTL, I915_READ(TV_CTL) & ~TV_ENC_ENABLE); + break; + } +} + +static void +intel_tv_save(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_output *intel_output = to_intel_output(connector); + struct intel_tv_priv *tv_priv = intel_output->dev_priv; + int i; + + tv_priv->save_TV_H_CTL_1 = I915_READ(TV_H_CTL_1); + tv_priv->save_TV_H_CTL_2 = I915_READ(TV_H_CTL_2); + tv_priv->save_TV_H_CTL_3 = I915_READ(TV_H_CTL_3); + tv_priv->save_TV_V_CTL_1 = I915_READ(TV_V_CTL_1); + tv_priv->save_TV_V_CTL_2 = I915_READ(TV_V_CTL_2); + tv_priv->save_TV_V_CTL_3 = I915_READ(TV_V_CTL_3); + tv_priv->save_TV_V_CTL_4 = I915_READ(TV_V_CTL_4); + tv_priv->save_TV_V_CTL_5 = I915_READ(TV_V_CTL_5); + tv_priv->save_TV_V_CTL_6 = I915_READ(TV_V_CTL_6); + tv_priv->save_TV_V_CTL_7 = I915_READ(TV_V_CTL_7); + tv_priv->save_TV_SC_CTL_1 = I915_READ(TV_SC_CTL_1); + tv_priv->save_TV_SC_CTL_2 = I915_READ(TV_SC_CTL_2); + tv_priv->save_TV_SC_CTL_3 = I915_READ(TV_SC_CTL_3); + + tv_priv->save_TV_CSC_Y = I915_READ(TV_CSC_Y); + tv_priv->save_TV_CSC_Y2 = I915_READ(TV_CSC_Y2); + tv_priv->save_TV_CSC_U = I915_READ(TV_CSC_U); + tv_priv->save_TV_CSC_U2 = I915_READ(TV_CSC_U2); + tv_priv->save_TV_CSC_V = I915_READ(TV_CSC_V); + tv_priv->save_TV_CSC_V2 = I915_READ(TV_CSC_V2); + tv_priv->save_TV_CLR_KNOBS = I915_READ(TV_CLR_KNOBS); + tv_priv->save_TV_CLR_LEVEL = I915_READ(TV_CLR_LEVEL); + tv_priv->save_TV_WIN_POS = I915_READ(TV_WIN_POS); + tv_priv->save_TV_WIN_SIZE = I915_READ(TV_WIN_SIZE); + tv_priv->save_TV_FILTER_CTL_1 = I915_READ(TV_FILTER_CTL_1); + tv_priv->save_TV_FILTER_CTL_2 = I915_READ(TV_FILTER_CTL_2); + tv_priv->save_TV_FILTER_CTL_3 = I915_READ(TV_FILTER_CTL_3); + + for (i = 0; i < 60; i++) + tv_priv->save_TV_H_LUMA[i] = I915_READ(TV_H_LUMA_0 + (i <<2)); + for (i = 0; i < 60; i++) + tv_priv->save_TV_H_CHROMA[i] = I915_READ(TV_H_CHROMA_0 + (i <<2)); + for (i = 0; i < 43; i++) + tv_priv->save_TV_V_LUMA[i] = I915_READ(TV_V_LUMA_0 + (i <<2)); + for (i = 0; i < 43; i++) + tv_priv->save_TV_V_CHROMA[i] = I915_READ(TV_V_CHROMA_0 + (i <<2)); + + tv_priv->save_TV_DAC = I915_READ(TV_DAC); + tv_priv->save_TV_CTL = I915_READ(TV_CTL); +} + +static void +intel_tv_restore(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_output *intel_output = to_intel_output(connector); + struct intel_tv_priv *tv_priv = intel_output->dev_priv; + struct drm_crtc *crtc = connector->encoder->crtc; + struct intel_crtc *intel_crtc; + int i; + + /* FIXME: No CRTC? */ + if (!crtc) + return; + + intel_crtc = to_intel_crtc(crtc); + I915_WRITE(TV_H_CTL_1, tv_priv->save_TV_H_CTL_1); + I915_WRITE(TV_H_CTL_2, tv_priv->save_TV_H_CTL_2); + I915_WRITE(TV_H_CTL_3, tv_priv->save_TV_H_CTL_3); + I915_WRITE(TV_V_CTL_1, tv_priv->save_TV_V_CTL_1); + I915_WRITE(TV_V_CTL_2, tv_priv->save_TV_V_CTL_2); + I915_WRITE(TV_V_CTL_3, tv_priv->save_TV_V_CTL_3); + I915_WRITE(TV_V_CTL_4, tv_priv->save_TV_V_CTL_4); + I915_WRITE(TV_V_CTL_5, tv_priv->save_TV_V_CTL_5); + I915_WRITE(TV_V_CTL_6, tv_priv->save_TV_V_CTL_6); + I915_WRITE(TV_V_CTL_7, tv_priv->save_TV_V_CTL_7); + I915_WRITE(TV_SC_CTL_1, tv_priv->save_TV_SC_CTL_1); + I915_WRITE(TV_SC_CTL_2, tv_priv->save_TV_SC_CTL_2); + I915_WRITE(TV_SC_CTL_3, tv_priv->save_TV_SC_CTL_3); + + I915_WRITE(TV_CSC_Y, tv_priv->save_TV_CSC_Y); + I915_WRITE(TV_CSC_Y2, tv_priv->save_TV_CSC_Y2); + I915_WRITE(TV_CSC_U, tv_priv->save_TV_CSC_U); + I915_WRITE(TV_CSC_U2, tv_priv->save_TV_CSC_U2); + I915_WRITE(TV_CSC_V, tv_priv->save_TV_CSC_V); + I915_WRITE(TV_CSC_V2, tv_priv->save_TV_CSC_V2); + I915_WRITE(TV_CLR_KNOBS, tv_priv->save_TV_CLR_KNOBS); + I915_WRITE(TV_CLR_LEVEL, tv_priv->save_TV_CLR_LEVEL); + + { + int pipeconf_reg = (intel_crtc->pipe == 0) ? + PIPEACONF : PIPEBCONF; + int dspcntr_reg = (intel_crtc->plane == 0) ? + DSPACNTR : DSPBCNTR; + int pipeconf = I915_READ(pipeconf_reg); + int dspcntr = I915_READ(dspcntr_reg); + int dspbase_reg = (intel_crtc->plane == 0) ? + DSPAADDR : DSPBADDR; + /* Pipe must be off here */ + I915_WRITE(dspcntr_reg, dspcntr & ~DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); + + if (!IS_I9XX(dev)) { + /* Wait for vblank for the disable to take effect */ + intel_wait_for_vblank(dev); + } + + I915_WRITE(pipeconf_reg, pipeconf & ~PIPEACONF_ENABLE); + /* Wait for vblank for the disable to take effect. */ + intel_wait_for_vblank(dev); + + /* Filter ctl must be set before TV_WIN_SIZE */ + I915_WRITE(TV_FILTER_CTL_1, tv_priv->save_TV_FILTER_CTL_1); + I915_WRITE(TV_FILTER_CTL_2, tv_priv->save_TV_FILTER_CTL_2); + I915_WRITE(TV_FILTER_CTL_3, tv_priv->save_TV_FILTER_CTL_3); + I915_WRITE(TV_WIN_POS, tv_priv->save_TV_WIN_POS); + I915_WRITE(TV_WIN_SIZE, tv_priv->save_TV_WIN_SIZE); + I915_WRITE(pipeconf_reg, pipeconf); + I915_WRITE(dspcntr_reg, dspcntr); + /* Flush the plane changes */ + I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); + } + + for (i = 0; i < 60; i++) + I915_WRITE(TV_H_LUMA_0 + (i <<2), tv_priv->save_TV_H_LUMA[i]); + for (i = 0; i < 60; i++) + I915_WRITE(TV_H_CHROMA_0 + (i <<2), tv_priv->save_TV_H_CHROMA[i]); + for (i = 0; i < 43; i++) + I915_WRITE(TV_V_LUMA_0 + (i <<2), tv_priv->save_TV_V_LUMA[i]); + for (i = 0; i < 43; i++) + I915_WRITE(TV_V_CHROMA_0 + (i <<2), tv_priv->save_TV_V_CHROMA[i]); + + I915_WRITE(TV_DAC, tv_priv->save_TV_DAC); + I915_WRITE(TV_CTL, tv_priv->save_TV_CTL); +} + +static const struct tv_mode * +intel_tv_mode_lookup (char *tv_format) +{ + int i; + + for (i = 0; i < sizeof(tv_modes) / sizeof (tv_modes[0]); i++) { + const struct tv_mode *tv_mode = &tv_modes[i]; + + if (!strcmp(tv_format, tv_mode->name)) + return tv_mode; + } + return NULL; +} + +static const struct tv_mode * +intel_tv_mode_find (struct intel_output *intel_output) +{ + struct intel_tv_priv *tv_priv = intel_output->dev_priv; + + return intel_tv_mode_lookup(tv_priv->tv_format); +} + +static enum drm_mode_status +intel_tv_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) +{ + struct intel_output *intel_output = to_intel_output(connector); + const struct tv_mode *tv_mode = intel_tv_mode_find(intel_output); + + /* Ensure TV refresh is close to desired refresh */ + if (tv_mode && abs(tv_mode->refresh - drm_mode_vrefresh(mode)) < 1) + return MODE_OK; + return MODE_CLOCK_RANGE; +} + + +static bool +intel_tv_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_mode_config *drm_config = &dev->mode_config; + struct intel_output *intel_output = enc_to_intel_output(encoder); + const struct tv_mode *tv_mode = intel_tv_mode_find (intel_output); + struct drm_encoder *other_encoder; + + if (!tv_mode) + return false; + + /* FIXME: lock encoder list */ + list_for_each_entry(other_encoder, &drm_config->encoder_list, head) { + if (other_encoder != encoder && + other_encoder->crtc == encoder->crtc) + return false; + } + + adjusted_mode->clock = tv_mode->clock; + return true; +} + +static void +intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_crtc *crtc = encoder->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_output *intel_output = enc_to_intel_output(encoder); + struct intel_tv_priv *tv_priv = intel_output->dev_priv; + const struct tv_mode *tv_mode = intel_tv_mode_find(intel_output); + u32 tv_ctl; + u32 hctl1, hctl2, hctl3; + u32 vctl1, vctl2, vctl3, vctl4, vctl5, vctl6, vctl7; + u32 scctl1, scctl2, scctl3; + int i, j; + const struct video_levels *video_levels; + const struct color_conversion *color_conversion; + bool burst_ena; + + if (!tv_mode) + return; /* can't happen (mode_prepare prevents this) */ + + tv_ctl = 0; + + switch (tv_priv->type) { + default: + case DRM_MODE_CONNECTOR_Unknown: + case DRM_MODE_CONNECTOR_Composite: + tv_ctl |= TV_ENC_OUTPUT_COMPOSITE; + video_levels = tv_mode->composite_levels; + color_conversion = tv_mode->composite_color; + burst_ena = tv_mode->burst_ena; + break; + case DRM_MODE_CONNECTOR_Component: + tv_ctl |= TV_ENC_OUTPUT_COMPONENT; + video_levels = &component_levels; + if (tv_mode->burst_ena) + color_conversion = &sdtv_csc_yprpb; + else + color_conversion = &hdtv_csc_yprpb; + burst_ena = false; + break; + case DRM_MODE_CONNECTOR_SVIDEO: + tv_ctl |= TV_ENC_OUTPUT_SVIDEO; + video_levels = tv_mode->svideo_levels; + color_conversion = tv_mode->svideo_color; + burst_ena = tv_mode->burst_ena; + break; + } + hctl1 = (tv_mode->hsync_end << TV_HSYNC_END_SHIFT) | + (tv_mode->htotal << TV_HTOTAL_SHIFT); + + hctl2 = (tv_mode->hburst_start << 16) | + (tv_mode->hburst_len << TV_HBURST_LEN_SHIFT); + + if (burst_ena) + hctl2 |= TV_BURST_ENA; + + hctl3 = (tv_mode->hblank_start << TV_HBLANK_START_SHIFT) | + (tv_mode->hblank_end << TV_HBLANK_END_SHIFT); + + vctl1 = (tv_mode->nbr_end << TV_NBR_END_SHIFT) | + (tv_mode->vi_end_f1 << TV_VI_END_F1_SHIFT) | + (tv_mode->vi_end_f2 << TV_VI_END_F2_SHIFT); + + vctl2 = (tv_mode->vsync_len << TV_VSYNC_LEN_SHIFT) | + (tv_mode->vsync_start_f1 << TV_VSYNC_START_F1_SHIFT) | + (tv_mode->vsync_start_f2 << TV_VSYNC_START_F2_SHIFT); + + vctl3 = (tv_mode->veq_len << TV_VEQ_LEN_SHIFT) | + (tv_mode->veq_start_f1 << TV_VEQ_START_F1_SHIFT) | + (tv_mode->veq_start_f2 << TV_VEQ_START_F2_SHIFT); + + if (tv_mode->veq_ena) + vctl3 |= TV_EQUAL_ENA; + + vctl4 = (tv_mode->vburst_start_f1 << TV_VBURST_START_F1_SHIFT) | + (tv_mode->vburst_end_f1 << TV_VBURST_END_F1_SHIFT); + + vctl5 = (tv_mode->vburst_start_f2 << TV_VBURST_START_F2_SHIFT) | + (tv_mode->vburst_end_f2 << TV_VBURST_END_F2_SHIFT); + + vctl6 = (tv_mode->vburst_start_f3 << TV_VBURST_START_F3_SHIFT) | + (tv_mode->vburst_end_f3 << TV_VBURST_END_F3_SHIFT); + + vctl7 = (tv_mode->vburst_start_f4 << TV_VBURST_START_F4_SHIFT) | + (tv_mode->vburst_end_f4 << TV_VBURST_END_F4_SHIFT); + + if (intel_crtc->pipe == 1) + tv_ctl |= TV_ENC_PIPEB_SELECT; + tv_ctl |= tv_mode->oversample; + + if (tv_mode->progressive) + tv_ctl |= TV_PROGRESSIVE; + if (tv_mode->trilevel_sync) + tv_ctl |= TV_TRILEVEL_SYNC; + if (tv_mode->pal_burst) + tv_ctl |= TV_PAL_BURST; + scctl1 = 0; + /* dda1 implies valid video levels */ + if (tv_mode->dda1_inc) { + scctl1 |= TV_SC_DDA1_EN; + scctl1 |= video_levels->burst << TV_BURST_LEVEL_SHIFT; + } + + if (tv_mode->dda2_inc) + scctl1 |= TV_SC_DDA2_EN; + + if (tv_mode->dda3_inc) + scctl1 |= TV_SC_DDA3_EN; + + scctl1 |= tv_mode->sc_reset; + scctl1 |= tv_mode->dda1_inc << TV_SCDDA1_INC_SHIFT; + + scctl2 = tv_mode->dda2_size << TV_SCDDA2_SIZE_SHIFT | + tv_mode->dda2_inc << TV_SCDDA2_INC_SHIFT; + + scctl3 = tv_mode->dda3_size << TV_SCDDA3_SIZE_SHIFT | + tv_mode->dda3_inc << TV_SCDDA3_INC_SHIFT; + + /* Enable two fixes for the chips that need them. */ + if (dev->pci_device < 0x2772) + tv_ctl |= TV_ENC_C0_FIX | TV_ENC_SDP_FIX; + + I915_WRITE(TV_H_CTL_1, hctl1); + I915_WRITE(TV_H_CTL_2, hctl2); + I915_WRITE(TV_H_CTL_3, hctl3); + I915_WRITE(TV_V_CTL_1, vctl1); + I915_WRITE(TV_V_CTL_2, vctl2); + I915_WRITE(TV_V_CTL_3, vctl3); + I915_WRITE(TV_V_CTL_4, vctl4); + I915_WRITE(TV_V_CTL_5, vctl5); + I915_WRITE(TV_V_CTL_6, vctl6); + I915_WRITE(TV_V_CTL_7, vctl7); + I915_WRITE(TV_SC_CTL_1, scctl1); + I915_WRITE(TV_SC_CTL_2, scctl2); + I915_WRITE(TV_SC_CTL_3, scctl3); + + if (color_conversion) { + I915_WRITE(TV_CSC_Y, (color_conversion->ry << 16) | + color_conversion->gy); + I915_WRITE(TV_CSC_Y2,(color_conversion->by << 16) | + color_conversion->ay); + I915_WRITE(TV_CSC_U, (color_conversion->ru << 16) | + color_conversion->gu); + I915_WRITE(TV_CSC_U2, (color_conversion->bu << 16) | + color_conversion->au); + I915_WRITE(TV_CSC_V, (color_conversion->rv << 16) | + color_conversion->gv); + I915_WRITE(TV_CSC_V2, (color_conversion->bv << 16) | + color_conversion->av); + } + + I915_WRITE(TV_CLR_KNOBS, 0x00606000); + if (video_levels) + I915_WRITE(TV_CLR_LEVEL, + ((video_levels->black << TV_BLACK_LEVEL_SHIFT) | + (video_levels->blank << TV_BLANK_LEVEL_SHIFT))); + { + int pipeconf_reg = (intel_crtc->pipe == 0) ? + PIPEACONF : PIPEBCONF; + int dspcntr_reg = (intel_crtc->plane == 0) ? + DSPACNTR : DSPBCNTR; + int pipeconf = I915_READ(pipeconf_reg); + int dspcntr = I915_READ(dspcntr_reg); + int dspbase_reg = (intel_crtc->plane == 0) ? + DSPAADDR : DSPBADDR; + int xpos = 0x0, ypos = 0x0; + unsigned int xsize, ysize; + /* Pipe must be off here */ + I915_WRITE(dspcntr_reg, dspcntr & ~DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); + + /* Wait for vblank for the disable to take effect */ + if (!IS_I9XX(dev)) + intel_wait_for_vblank(dev); + + I915_WRITE(pipeconf_reg, pipeconf & ~PIPEACONF_ENABLE); + /* Wait for vblank for the disable to take effect. */ + intel_wait_for_vblank(dev); + + /* Filter ctl must be set before TV_WIN_SIZE */ + I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE); + xsize = tv_mode->hblank_start - tv_mode->hblank_end; + if (tv_mode->progressive) + ysize = tv_mode->nbr_end + 1; + else + ysize = 2*tv_mode->nbr_end + 1; + + xpos += tv_priv->margin[TV_MARGIN_LEFT]; + ypos += tv_priv->margin[TV_MARGIN_TOP]; + xsize -= (tv_priv->margin[TV_MARGIN_LEFT] + + tv_priv->margin[TV_MARGIN_RIGHT]); + ysize -= (tv_priv->margin[TV_MARGIN_TOP] + + tv_priv->margin[TV_MARGIN_BOTTOM]); + I915_WRITE(TV_WIN_POS, (xpos<<16)|ypos); + I915_WRITE(TV_WIN_SIZE, (xsize<<16)|ysize); + + I915_WRITE(pipeconf_reg, pipeconf); + I915_WRITE(dspcntr_reg, dspcntr); + /* Flush the plane changes */ + I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); + } + + j = 0; + for (i = 0; i < 60; i++) + I915_WRITE(TV_H_LUMA_0 + (i<<2), tv_mode->filter_table[j++]); + for (i = 0; i < 60; i++) + I915_WRITE(TV_H_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]); + for (i = 0; i < 43; i++) + I915_WRITE(TV_V_LUMA_0 + (i<<2), tv_mode->filter_table[j++]); + for (i = 0; i < 43; i++) + I915_WRITE(TV_V_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]); + I915_WRITE(TV_DAC, 0); + I915_WRITE(TV_CTL, tv_ctl); +} + +static const struct drm_display_mode reported_modes[] = { + { + .name = "NTSC 480i", + .clock = 107520, + .hdisplay = 1280, + .hsync_start = 1368, + .hsync_end = 1496, + .htotal = 1712, + + .vdisplay = 1024, + .vsync_start = 1027, + .vsync_end = 1034, + .vtotal = 1104, + .type = DRM_MODE_TYPE_DRIVER, + }, +}; + +/** + * Detects TV presence by checking for load. + * + * Requires that the current pipe's DPLL is active. + + * \return true if TV is connected. + * \return false if TV is disconnected. + */ +static int +intel_tv_detect_type (struct drm_crtc *crtc, struct intel_output *intel_output) +{ + struct drm_encoder *encoder = &intel_output->enc; + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 pipeastat, pipeastat_save; + u32 tv_ctl, save_tv_ctl; + u32 tv_dac, save_tv_dac; + int type = DRM_MODE_CONNECTOR_Unknown; + + tv_dac = I915_READ(TV_DAC); + + /* Disable TV interrupts around load detect or we'll recurse */ + pipeastat = I915_READ(PIPEASTAT); + pipeastat_save = pipeastat; + pipeastat &= ~PIPE_HOTPLUG_INTERRUPT_ENABLE; + pipeastat &= ~PIPE_HOTPLUG_TV_INTERRUPT_ENABLE; + I915_WRITE(PIPEASTAT, pipeastat | PIPE_HOTPLUG_TV_INTERRUPT_STATUS | + PIPE_HOTPLUG_INTERRUPT_STATUS); + + /* + * Detect TV by polling) + */ + if (intel_output->load_detect_temp) { + /* TV not currently running, prod it with destructive detect */ + save_tv_dac = tv_dac; + tv_ctl = I915_READ(TV_CTL); + save_tv_ctl = tv_ctl; + tv_ctl &= ~TV_ENC_ENABLE; + tv_ctl &= ~TV_TEST_MODE_MASK; + tv_ctl |= TV_TEST_MODE_MONITOR_DETECT; + tv_dac &= ~TVDAC_SENSE_MASK; + tv_dac |= (TVDAC_STATE_CHG_EN | + TVDAC_A_SENSE_CTL | + TVDAC_B_SENSE_CTL | + TVDAC_C_SENSE_CTL | + DAC_CTL_OVERRIDE | + DAC_A_0_7_V | + DAC_B_0_7_V | + DAC_C_0_7_V); + I915_WRITE(TV_CTL, tv_ctl); + I915_WRITE(TV_DAC, tv_dac); + intel_wait_for_vblank(dev); + tv_dac = I915_READ(TV_DAC); + I915_WRITE(TV_DAC, save_tv_dac); + I915_WRITE(TV_CTL, save_tv_ctl); + } + /* + * A B C + * 0 1 1 Composite + * 1 0 X svideo + * 0 0 0 Component + */ + if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) { + DRM_DEBUG("Detected Composite TV connection\n"); + type = DRM_MODE_CONNECTOR_Composite; + } else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) { + DRM_DEBUG("Detected S-Video TV connection\n"); + type = DRM_MODE_CONNECTOR_SVIDEO; + } else if ((tv_dac & TVDAC_SENSE_MASK) == 0) { + DRM_DEBUG("Detected Component TV connection\n"); + type = DRM_MODE_CONNECTOR_Component; + } else { + DRM_DEBUG("No TV connection detected\n"); + type = -1; + } + + /* Restore interrupt config */ + I915_WRITE(PIPEASTAT, pipeastat_save | PIPE_HOTPLUG_TV_INTERRUPT_STATUS | + PIPE_HOTPLUG_INTERRUPT_STATUS); + + return type; +} + +/** + * Detect the TV connection. + * + * Currently this always returns CONNECTOR_STATUS_UNKNOWN, as we need to be sure + * we have a pipe programmed in order to probe the TV. + */ +static enum drm_connector_status +intel_tv_detect(struct drm_connector *connector) +{ + struct drm_crtc *crtc; + struct drm_display_mode mode; + struct intel_output *intel_output = to_intel_output(connector); + struct intel_tv_priv *tv_priv = intel_output->dev_priv; + struct drm_encoder *encoder = &intel_output->enc; + int dpms_mode; + int type = tv_priv->type; + + mode = reported_modes[0]; + drm_mode_set_crtcinfo(&mode, CRTC_INTERLACE_HALVE_V); + + if (encoder->crtc) { + type = intel_tv_detect_type(encoder->crtc, intel_output); + } else { + crtc = intel_get_load_detect_pipe(intel_output, &mode, &dpms_mode); + if (crtc) { + type = intel_tv_detect_type(crtc, intel_output); + intel_release_load_detect_pipe(intel_output, dpms_mode); + } else + type = -1; + } + +#if 0 + if (type != tv_priv->type) { + struct drm_property *connector_property = + connector->dev->mode_config.connector_type_property; + + tv_priv->type = type; + drm_connector_property_set_value(connector, connector_property, + type); + } +#endif + if (type < 0) + return connector_status_disconnected; + + return connector_status_connected; +} + +static struct input_res { + char *name; + int w, h; +} input_res_table[] = +{ + {"640x480", 640, 480}, + {"800x600", 800, 600}, + {"1024x768", 1024, 768}, + {"1280x1024", 1280, 1024}, + {"848x480", 848, 480}, + {"1280x720", 1280, 720}, + {"1920x1080", 1920, 1080}, +}; + +/** + * Stub get_modes function. + * + * This should probably return a set of fixed modes, unless we can figure out + * how to probe modes off of TV connections. + */ + +static int +intel_tv_get_modes(struct drm_connector *connector) +{ + struct drm_display_mode *mode_ptr; + struct intel_output *intel_output = to_intel_output(connector); + const struct tv_mode *tv_mode = intel_tv_mode_find(intel_output); + int j; + + for (j = 0; j < sizeof(input_res_table) / sizeof(input_res_table[0]); + j++) { + struct input_res *input = &input_res_table[j]; + unsigned int hactive_s = input->w; + unsigned int vactive_s = input->h; + + if (tv_mode->max_srcw && input->w > tv_mode->max_srcw) + continue; + + if (input->w > 1024 && (!tv_mode->progressive + && !tv_mode->component_only)) + continue; + + mode_ptr = drm_calloc(1, sizeof(struct drm_display_mode), + DRM_MEM_DRIVER); + strncpy(mode_ptr->name, input->name, DRM_DISPLAY_MODE_LEN); + + mode_ptr->hdisplay = hactive_s; + mode_ptr->hsync_start = hactive_s + 1; + mode_ptr->hsync_end = hactive_s + 64; + if (mode_ptr->hsync_end <= mode_ptr->hsync_start) + mode_ptr->hsync_end = mode_ptr->hsync_start + 1; + mode_ptr->htotal = hactive_s + 96; + + mode_ptr->vdisplay = vactive_s; + mode_ptr->vsync_start = vactive_s + 1; + mode_ptr->vsync_end = vactive_s + 32; + if (mode_ptr->vsync_end <= mode_ptr->vsync_start) + mode_ptr->vsync_end = mode_ptr->vsync_start + 1; + mode_ptr->vtotal = vactive_s + 33; + + mode_ptr->clock = (int) (tv_mode->refresh * + mode_ptr->vtotal * + mode_ptr->htotal / 1000) / 1000; + + mode_ptr->type = DRM_MODE_TYPE_DRIVER; + drm_mode_probed_add(connector, mode_ptr); + } + + return 0; +} + +static void +intel_tv_destroy (struct drm_connector *connector) +{ + struct intel_output *intel_output = to_intel_output(connector); + + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); + drm_free(intel_output, sizeof(struct intel_output) + sizeof(struct intel_tv_priv), + DRM_MEM_DRIVER); +} + + +static int +intel_tv_set_property(struct drm_connector *connector, struct drm_property *property, + uint64_t val) +{ + struct drm_device *dev = connector->dev; + struct intel_output *intel_output = to_intel_output(connector); + struct intel_tv_priv *tv_priv = intel_output->dev_priv; + int ret = 0; + + ret = drm_connector_property_set_value(connector, property, val); + if (ret < 0) + goto out; + + if (property == dev->mode_config.tv_left_margin_property) + tv_priv->margin[TV_MARGIN_LEFT] = val; + else if (property == dev->mode_config.tv_right_margin_property) + tv_priv->margin[TV_MARGIN_RIGHT] = val; + else if (property == dev->mode_config.tv_top_margin_property) + tv_priv->margin[TV_MARGIN_TOP] = val; + else if (property == dev->mode_config.tv_bottom_margin_property) + tv_priv->margin[TV_MARGIN_BOTTOM] = val; + else if (property == dev->mode_config.tv_mode_property) { + if (val >= NUM_TV_MODES) { + ret = -EINVAL; + goto out; + } + tv_priv->tv_format = tv_modes[val].name; + intel_tv_mode_set(&intel_output->enc, NULL, NULL); + } else { + ret = -EINVAL; + goto out; + } + + intel_tv_mode_set(&intel_output->enc, NULL, NULL); +out: + return ret; +} + +static const struct drm_encoder_helper_funcs intel_tv_helper_funcs = { + .dpms = intel_tv_dpms, + .mode_fixup = intel_tv_mode_fixup, + .prepare = intel_encoder_prepare, + .mode_set = intel_tv_mode_set, + .commit = intel_encoder_commit, +}; + +static const struct drm_connector_funcs intel_tv_connector_funcs = { + .save = intel_tv_save, + .restore = intel_tv_restore, + .detect = intel_tv_detect, + .destroy = intel_tv_destroy, + .set_property = intel_tv_set_property, + .fill_modes = drm_helper_probe_single_connector_modes, +}; + +static const struct drm_connector_helper_funcs intel_tv_connector_helper_funcs = { + .mode_valid = intel_tv_mode_valid, + .get_modes = intel_tv_get_modes, + .best_encoder = intel_best_encoder, +}; + +void intel_tv_enc_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_funcs intel_tv_enc_funcs = { + .destroy = intel_tv_enc_destroy, +}; + + +void +intel_tv_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_connector *connector; + struct intel_output *intel_output; + struct intel_tv_priv *tv_priv; + u32 tv_dac_on, tv_dac_off, save_tv_dac; + char **tv_format_names; + int i, initial_mode = 0; + + if ((I915_READ(TV_CTL) & TV_FUSE_STATE_MASK) == TV_FUSE_STATE_DISABLED) + return; + + /* Even if we have an encoder we may not have a connector */ + if (!dev_priv->int_tv_support) + return; + + /* + * Sanity check the TV output by checking to see if the + * DAC register holds a value + */ + save_tv_dac = I915_READ(TV_DAC); + + I915_WRITE(TV_DAC, save_tv_dac | TVDAC_STATE_CHG_EN); + tv_dac_on = I915_READ(TV_DAC); + + I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN); + tv_dac_off = I915_READ(TV_DAC); + + I915_WRITE(TV_DAC, save_tv_dac); + + /* + * If the register does not hold the state change enable + * bit, (either as a 0 or a 1), assume it doesn't really + * exist + */ + if ((tv_dac_on & TVDAC_STATE_CHG_EN) == 0 || + (tv_dac_off & TVDAC_STATE_CHG_EN) != 0) + return; + + intel_output = drm_calloc(1, sizeof(struct intel_output) + + sizeof(struct intel_tv_priv), DRM_MEM_DRIVER); + if (!intel_output) { + return; + } + connector = &intel_output->base; + + drm_connector_init(dev, connector, &intel_tv_connector_funcs, + DRM_MODE_CONNECTOR_SVIDEO); + + drm_encoder_init(dev, &intel_output->enc, &intel_tv_enc_funcs, + DRM_MODE_ENCODER_TVDAC); + + drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc); + tv_priv = (struct intel_tv_priv *)(intel_output + 1); + intel_output->type = INTEL_OUTPUT_TVOUT; + intel_output->enc.possible_crtcs = ((1 << 0) | (1 << 1)); + intel_output->enc.possible_clones = (1 << INTEL_OUTPUT_TVOUT); + intel_output->dev_priv = tv_priv; + tv_priv->type = DRM_MODE_CONNECTOR_Unknown; + + /* BIOS margin values */ + tv_priv->margin[TV_MARGIN_LEFT] = 54; + tv_priv->margin[TV_MARGIN_TOP] = 36; + tv_priv->margin[TV_MARGIN_RIGHT] = 46; + tv_priv->margin[TV_MARGIN_BOTTOM] = 37; + + tv_priv->tv_format = kstrdup(tv_modes[initial_mode].name, GFP_KERNEL); + + drm_encoder_helper_add(&intel_output->enc, &intel_tv_helper_funcs); + drm_connector_helper_add(connector, &intel_tv_connector_helper_funcs); + connector->interlace_allowed = false; + connector->doublescan_allowed = false; + + /* Create TV properties then attach current values */ + tv_format_names = drm_alloc(sizeof(char *) * NUM_TV_MODES, + DRM_MEM_DRIVER); + if (!tv_format_names) + goto out; + for (i = 0; i < NUM_TV_MODES; i++) + tv_format_names[i] = tv_modes[i].name; + drm_mode_create_tv_properties(dev, NUM_TV_MODES, tv_format_names); + + drm_connector_attach_property(connector, dev->mode_config.tv_mode_property, + initial_mode); + drm_connector_attach_property(connector, + dev->mode_config.tv_left_margin_property, + tv_priv->margin[TV_MARGIN_LEFT]); + drm_connector_attach_property(connector, + dev->mode_config.tv_top_margin_property, + tv_priv->margin[TV_MARGIN_TOP]); + drm_connector_attach_property(connector, + dev->mode_config.tv_right_margin_property, + tv_priv->margin[TV_MARGIN_RIGHT]); + drm_connector_attach_property(connector, + dev->mode_config.tv_bottom_margin_property, + tv_priv->margin[TV_MARGIN_BOTTOM]); +out: + drm_sysfs_connector_add(connector); +} diff --git a/linux-core/nouveau_bios.c b/linux-core/nouveau_bios.c new file mode 100644 index 00000000..faa2b2b0 --- /dev/null +++ b/linux-core/nouveau_bios.c @@ -0,0 +1,854 @@ +/* + * Copyright (C) 2005-2006 Erik Waling + * Copyright (C) 2006 Stephane Marchesin + * Copyright (C) 2007-2008 Stuart Bennett + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +#include <asm/byteorder.h> +#include "nouveau_bios.h" +#include "nouveau_drv.h" + +/* returns true if it mismatches */ +static bool nv_checksum(const uint8_t *data, unsigned int length) +{ + /* there's a few checksums in the BIOS, so here's a generic checking function */ + int i; + uint8_t sum = 0; + + for (i = 0; i < length; i++) + sum += data[i]; + + if (sum) + return true; + + return false; +} + +static int nv_valid_bios(struct drm_device *dev, uint8_t *data) +{ + /* check for BIOS signature */ + if (!(data[0] == 0x55 && data[1] == 0xAA)) { + DRM_ERROR("BIOS signature not found.\n"); + return 0; + } + + if (nv_checksum(data, data[2] * 512)) { + DRM_ERROR("BIOS checksum invalid.\n"); + return 1; + } + + return 2; +} + +static void nv_shadow_bios_rom(struct drm_device *dev, uint8_t *data) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + int i; + + /* enable access to rom */ + NV_WRITE(NV04_PBUS_PCI_NV_20, NV04_PBUS_PCI_NV_20_ROM_SHADOW_DISABLED); + + /* This is also valid for pre-NV50, it just happened to be the only define already present. */ + for (i=0; i < NV50_PROM__ESIZE; i++) { + /* Appearantly needed for a 6600GT/6800LE bug. */ + data[i] = DRM_READ8(dev_priv->mmio, NV50_PROM + i); + data[i] = DRM_READ8(dev_priv->mmio, NV50_PROM + i); + data[i] = DRM_READ8(dev_priv->mmio, NV50_PROM + i); + data[i] = DRM_READ8(dev_priv->mmio, NV50_PROM + i); + data[i] = DRM_READ8(dev_priv->mmio, NV50_PROM + i); + } + + /* disable access to rom */ + NV_WRITE(NV04_PBUS_PCI_NV_20, NV04_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED); +} + +static void nv_shadow_bios_ramin(struct drm_device *dev, uint8_t *data) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + uint32_t old_bar0_pramin = 0; + int i; + + /* Move the bios copy to the start of ramin? */ + if (dev_priv->card_type >= NV_50) { + uint32_t vbios_vram = (NV_READ(0x619f04) & ~0xff) << 8; + + if (!vbios_vram) + vbios_vram = (NV_READ(0x1700) << 16) + 0xf0000; + + old_bar0_pramin = NV_READ(0x1700); + NV_WRITE(0x1700, vbios_vram >> 16); + } + + for (i=0; i < NV50_PROM__ESIZE; i++) + data[i] = DRM_READ8(dev_priv->mmio, NV04_PRAMIN + i); + + if (dev_priv->card_type >= NV_50) + NV_WRITE(0x1700, old_bar0_pramin); +} + +static bool nv_shadow_bios(struct drm_device *dev, uint8_t *data) +{ + nv_shadow_bios_rom(dev, data); + if (nv_valid_bios(dev, data) == 2) + return true; + + nv_shadow_bios_ramin(dev, data); + if (nv_valid_bios(dev, data)) + return true; + + return false; +} + +struct bit_entry { + uint8_t id[2]; + uint16_t length; + uint16_t offset; +}; + +static int parse_bit_A_tbl_entry(struct drm_device *dev, struct bios *bios, struct bit_entry *bitentry) +{ + /* Parses the load detect value table. + * + * Starting at bitentry->offset: + * + * offset + 0 (16 bits): table pointer + */ + + uint16_t load_table_pointer; + + if (bitentry->length != 3) { + DRM_ERROR("Do not understand BIT loadval table\n"); + return 0; + } + + load_table_pointer = le16_to_cpu(*((uint16_t *)(&bios->data[bitentry->offset]))); + + if (load_table_pointer == 0x0) { + DRM_ERROR("Pointer to loadval table invalid\n"); + return 0; + } + + /* Some kind of signature */ + if (bios->data[load_table_pointer] != 16 || bios->data[load_table_pointer + 1] != 4 || + bios->data[load_table_pointer + 2] != 4 || bios->data[load_table_pointer + 3] != 2) + return 0; + + bios->dactestval = le32_to_cpu(*((uint32_t *)&bios->data[load_table_pointer + 4])) & 0x3FF; + + return 1; +} + +static int parse_bit_C_tbl_entry(struct drm_device *dev, struct bios *bios, struct bit_entry *bitentry) +{ + /* offset + 8 (16 bits): PLL limits table pointer + * + * There's more in here, but that's unknown. + */ + + if (bitentry->length < 10) { + DRM_ERROR("Do not understand BIT C table\n"); + return 0; + } + + bios->pll_limit_tbl_ptr = le16_to_cpu(*((uint16_t *)(&bios->data[bitentry->offset + 8]))); + + return 1; +} + +static void parse_bit_structure(struct drm_device *dev, struct bios *bios, const uint16_t bitoffset) +{ + int entries = bios->data[bitoffset + 4]; + /* parse i first, I next (which needs C & M before it), and L before D */ + char parseorder[] = "iCMILDTA"; + struct bit_entry bitentry; + int i, j, offset; + + for (i = 0; i < sizeof(parseorder); i++) { + for (j = 0, offset = bitoffset + 6; j < entries; j++, offset += 6) { + bitentry.id[0] = bios->data[offset]; + bitentry.id[1] = bios->data[offset + 1]; + bitentry.length = le16_to_cpu(*((uint16_t *)&bios->data[offset + 2])); + bitentry.offset = le16_to_cpu(*((uint16_t *)&bios->data[offset + 4])); + + if (bitentry.id[0] != parseorder[i]) + continue; + + switch (bitentry.id[0]) { + case 'A': + parse_bit_A_tbl_entry(dev, bios, &bitentry); + break; + case 'C': + parse_bit_C_tbl_entry(dev, bios, &bitentry); + break; + } + } + } +} + +static uint16_t findstr(uint8_t *data, int n, const uint8_t *str, int len) +{ + int i, j; + + for (i = 0; i <= (n - len); i++) { + for (j = 0; j < len; j++) + if (data[i + j] != str[j]) + break; + if (j == len) + return i; + } + + return 0; +} + +static void +read_dcb_i2c_entry(struct drm_device *dev, uint8_t dcb_version, uint16_t i2ctabptr, int index) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct bios *bios = &dev_priv->bios; + uint8_t *i2ctable = &bios->data[i2ctabptr]; + uint8_t headerlen = 0; + int i2c_entries = MAX_NUM_DCB_ENTRIES; + int recordoffset = 0, rdofs = 1, wrofs = 0; + + if (!i2ctabptr) + return; + + if (dcb_version >= 0x30) { + if (i2ctable[0] != dcb_version) /* necessary? */ + DRM_ERROR( + "DCB I2C table version mismatch (%02X vs %02X)\n", + i2ctable[0], dcb_version); + headerlen = i2ctable[1]; + i2c_entries = i2ctable[2]; + + /* same address offset used for read and write for C51 and G80 */ + if (bios->chip_version == 0x51) + rdofs = wrofs = 1; + if (i2ctable[0] >= 0x40) + rdofs = wrofs = 0; + } + /* it's your own fault if you call this function on a DCB 1.1 BIOS -- + * the test below is for DCB 1.2 + */ + if (dcb_version < 0x14) { + recordoffset = 2; + rdofs = 0; + wrofs = 1; + } + + if (index == 0xf) + return; + if (index > i2c_entries) { + DRM_ERROR( + "DCB I2C index too big (%d > %d)\n", + index, i2ctable[2]); + return; + } + if (i2ctable[headerlen + 4 * index + 3] == 0xff) { + DRM_ERROR( + "DCB I2C entry invalid\n"); + return; + } + + if (bios->chip_version == 0x51) { + int port_type = i2ctable[headerlen + 4 * index + 3]; + + if (port_type != 4) + DRM_ERROR( + "DCB I2C table has port type %d\n", port_type); + } + if (i2ctable[0] >= 0x40) { + int port_type = i2ctable[headerlen + 4 * index + 3]; + + if (port_type != 5) + DRM_ERROR( + "DCB I2C table has port type %d\n", port_type); + } + + dev_priv->dcb_table.i2c_read[index] = i2ctable[headerlen + recordoffset + rdofs + 4 * index]; + dev_priv->dcb_table.i2c_write[index] = i2ctable[headerlen + recordoffset + wrofs + 4 * index]; +} + +static bool +parse_dcb_entry(struct drm_device *dev, int index, uint8_t dcb_version, uint16_t i2ctabptr, uint32_t conn, uint32_t conf) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct dcb_entry *entry = &dev_priv->dcb_table.entry[index]; + + memset(entry, 0, sizeof (struct dcb_entry)); + + entry->index = index; + /* safe defaults for a crt */ + entry->type = 0; + entry->i2c_index = 0; + entry->heads = 1; + entry->bus = 0; + entry->location = LOC_ON_CHIP; + entry->or = 1; + entry->duallink_possible = false; + + if (dcb_version >= 0x20) { + entry->type = conn & 0xf; + entry->i2c_index = (conn >> 4) & 0xf; + entry->heads = (conn >> 8) & 0xf; + entry->bus = (conn >> 16) & 0xf; + entry->location = (conn >> 20) & 0xf; + entry->or = (conn >> 24) & 0xf; + /* Normal entries consist of a single bit, but dual link has the + * adjacent more significant bit set too + */ + if ((1 << (ffs(entry->or) - 1)) * 3 == entry->or) + entry->duallink_possible = true; + + switch (entry->type) { + case DCB_OUTPUT_LVDS: + { + uint32_t mask; + if (conf & 0x1) + entry->lvdsconf.use_straps_for_mode = true; + if (dcb_version < 0x22) { + mask = ~0xd; + /* both 0x4 and 0x8 show up in v2.0 tables; assume they mean + * the same thing, which is probably wrong, but might work */ + if (conf & 0x4 || conf & 0x8) + entry->lvdsconf.use_power_scripts = true; + } else { + mask = ~0x5; + if (conf & 0x4) + entry->lvdsconf.use_power_scripts = true; + } + if (conf & mask) { + if (dcb_version < 0x40) { /* we know g80 cards have unknown bits */ + DRM_ERROR("Unknown LVDS configuration bits, please report\n"); + /* cause output setting to fail, so message is seen */ + dev_priv->dcb_table.entries = 0; + return false; + } + } + break; + } + case 0xe: + /* weird type that appears on g80 mobile bios; nv driver treats it as a terminator */ + return false; + } + read_dcb_i2c_entry(dev, dcb_version, i2ctabptr, entry->i2c_index); + } else if (dcb_version >= 0x14 ) { + if (conn != 0xf0003f00 && conn != 0xf2247f10 && conn != 0xf2204001 && conn != 0xf2204301 && conn != 0xf2244311 && conn != 0xf2045f14 && conn != 0xf2205004 && conn != 0xf2208001 && conn != 0xf4204011 && conn != 0xf4208011 && conn != 0xf4248011) { + DRM_ERROR( + "Unknown DCB 1.4 / 1.5 entry, please report\n"); + /* cause output setting to fail, so message is seen */ + dev_priv->dcb_table.entries = 0; + return false; + } + /* most of the below is a "best guess" atm */ + entry->type = conn & 0xf; + if (entry->type == 4) { /* digital */ + if (conn & 0x10) + entry->type = DCB_OUTPUT_LVDS; + else + entry->type = DCB_OUTPUT_TMDS; + } + /* what's in bits 5-13? could be some brooktree/chrontel/philips thing, in tv case */ + entry->i2c_index = (conn >> 14) & 0xf; + /* raw heads field is in range 0-1, so move to 1-2 */ + entry->heads = ((conn >> 18) & 0x7) + 1; + entry->location = (conn >> 21) & 0xf; + entry->bus = (conn >> 25) & 0x7; + /* set or to be same as heads -- hopefully safe enough */ + entry->or = entry->heads; + + switch (entry->type) { + case DCB_OUTPUT_LVDS: + /* this is probably buried in conn's unknown bits */ + entry->lvdsconf.use_power_scripts = true; + break; + case DCB_OUTPUT_TMDS: + /* invent a DVI-A output, by copying the fields of the DVI-D output + * reported to work by math_b on an NV20(!) */ + memcpy(&entry[1], &entry[0], sizeof(struct dcb_entry)); + entry[1].type = DCB_OUTPUT_ANALOG; + dev_priv->dcb_table.entries++; + } + read_dcb_i2c_entry(dev, dcb_version, i2ctabptr, entry->i2c_index); + } else if (dcb_version >= 0x12) { + /* v1.2 tables normally have the same 5 entries, which are not + * specific to the card, so use the defaults for a crt */ + /* DCB v1.2 does have an I2C table that read_dcb_i2c_table can handle, but cards + * exist (seen on nv11) where the pointer to the table points to the wrong + * place, so for now, we rely on the indices parsed in parse_bmp_structure + */ + entry->i2c_index = dev_priv->bios.legacy.i2c_indices.crt; + } else { /* pre DCB / v1.1 - use the safe defaults for a crt */ + DRM_ERROR( + "No information in BIOS output table; assuming a CRT output exists\n"); + entry->i2c_index = dev_priv->bios.legacy.i2c_indices.crt; + } + + if (entry->type == DCB_OUTPUT_LVDS && dev_priv->bios.fp.strapping != 0xff) + entry->lvdsconf.use_straps_for_mode = true; + + dev_priv->dcb_table.entries++; + + return true; +} + +static void merge_like_dcb_entries(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + + /* DCB v2.0 lists each output combination separately. + * Here we merge compatible entries to have fewer outputs, with more options + */ + int i, newentries = 0; + + for (i = 0; i < dev_priv->dcb_table.entries; i++) { + struct dcb_entry *ient = &dev_priv->dcb_table.entry[i]; + int j; + + for (j = i + 1; j < dev_priv->dcb_table.entries; j++) { + struct dcb_entry *jent = &dev_priv->dcb_table.entry[j]; + + if (jent->type == 100) /* already merged entry */ + continue; + + /* merge heads field when all other fields the same */ + if (jent->i2c_index == ient->i2c_index && jent->type == ient->type && jent->location == ient->location && jent->or == ient->or) { + DRM_INFO( + "Merging DCB entries %d and %d\n", i, j); + ient->heads |= jent->heads; + jent->type = 100; /* dummy value */ + } + } + } + + /* Compact entries merged into others out of dcb_table */ + for (i = 0; i < dev_priv->dcb_table.entries; i++) { + if ( dev_priv->dcb_table.entry[i].type == 100 ) + continue; + + if (newentries != i) + memcpy(&dev_priv->dcb_table.entry[newentries], &dev_priv->dcb_table.entry[i], sizeof(struct dcb_entry)); + newentries++; + } + + dev_priv->dcb_table.entries = newentries; +} + +static unsigned int parse_dcb_table(struct drm_device *dev, struct bios *bios) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + uint16_t dcbptr, i2ctabptr = 0; + uint8_t *dcbtable; + uint8_t dcb_version, headerlen = 0x4, entries = MAX_NUM_DCB_ENTRIES; + bool configblock = true; + int recordlength = 8, confofs = 4; + int i; + + dev_priv->dcb_table.entries = 0; + + /* get the offset from 0x36 */ + dcbptr = le16_to_cpu(*(uint16_t *)&bios->data[0x36]); + + if (dcbptr == 0x0) { + DRM_ERROR( + "No Display Configuration Block pointer found\n"); + /* this situation likely means a really old card, pre DCB, so we'll add the safe CRT entry */ + parse_dcb_entry(dev, 0, 0, 0, 0, 0); + return 1; + } + + dcbtable = &bios->data[dcbptr]; + + /* get DCB version */ + dcb_version = dcbtable[0]; + DRM_INFO( + "Display Configuration Block version %d.%d found\n", + dcb_version >> 4, dcb_version & 0xf); + + if (dcb_version >= 0x20) { /* NV17+ */ + uint32_t sig; + + if (dcb_version >= 0x30) { /* NV40+ */ + headerlen = dcbtable[1]; + entries = dcbtable[2]; + recordlength = dcbtable[3]; + i2ctabptr = le16_to_cpu(*(uint16_t *)&dcbtable[4]); + sig = le32_to_cpu(*(uint32_t *)&dcbtable[6]); + + DRM_INFO( + "DCB header length %d, with %d possible entries\n", + headerlen, entries); + } else { + i2ctabptr = le16_to_cpu(*(uint16_t *)&dcbtable[2]); + sig = le32_to_cpu(*(uint32_t *)&dcbtable[4]); + headerlen = 8; + } + + if (sig != 0x4edcbdcb) { + DRM_ERROR( + "Bad Display Configuration Block signature (%08X)\n", sig); + return 0; + } + } else if (dcb_version >= 0x14) { /* some NV15/16, and NV11+ */ + char sig[8]; + + memset(sig, 0, 8); + strncpy(sig, (char *)&dcbtable[-7], 7); + i2ctabptr = le16_to_cpu(*(uint16_t *)&dcbtable[2]); + recordlength = 10; + confofs = 6; + + if (strcmp(sig, "DEV_REC")) { + DRM_ERROR( + "Bad Display Configuration Block signature (%s)\n", sig); + return 0; + } + } else if (dcb_version >= 0x12) { /* some NV6/10, and NV15+ */ + i2ctabptr = le16_to_cpu(*(uint16_t *)&dcbtable[2]); + configblock = false; + } else { /* NV5+, maybe NV4 */ + /* DCB 1.1 seems to be quite unhelpful - we'll just add the safe CRT entry */ + parse_dcb_entry(dev, 0, dcb_version, 0, 0, 0); + return 1; + } + + if (entries >= MAX_NUM_DCB_ENTRIES) + entries = MAX_NUM_DCB_ENTRIES; + + for (i = 0; i < entries; i++) { + uint32_t connection, config = 0; + + connection = le32_to_cpu(*(uint32_t *)&dcbtable[headerlen + recordlength * i]); + if (configblock) + config = le32_to_cpu(*(uint32_t *)&dcbtable[headerlen + confofs + recordlength * i]); + + /* Should we allow discontinuous DCBs? Certainly DCB I2C tables can be discontinuous */ + if ((connection & 0x0000000f) == 0x0000000f) /* end of records */ + break; + if (connection == 0x00000000) /* seen on an NV11 with DCB v1.5 */ + break; + + DRM_INFO("Raw DCB entry %d: %08x %08x\n", i, connection, config); + if (!parse_dcb_entry(dev, dev_priv->dcb_table.entries, dcb_version, i2ctabptr, connection, config)) + break; + } + + merge_like_dcb_entries(dev); + + return dev_priv->dcb_table.entries; +} + +int nouveau_parse_bios(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + + const uint8_t bit_signature[] = { 'B', 'I', 'T' }; + int offset; + + dev_priv->bios.data = kzalloc(NV50_PROM__ESIZE, GFP_KERNEL); + if (!dev_priv->bios.data) + return -ENOMEM; + + if (!nv_shadow_bios(dev, dev_priv->bios.data)) + return -EINVAL; + + dev_priv->bios.length = dev_priv->bios.data[2] * 512; + if (dev_priv->bios.length > NV50_PROM__ESIZE) + dev_priv->bios.length = NV50_PROM__ESIZE; + + if ((offset = findstr(dev_priv->bios.data, dev_priv->bios.length, bit_signature, sizeof(bit_signature)))) { + DRM_INFO("BIT BIOS found\n"); + parse_bit_structure(dev, &dev_priv->bios, offset + 4); + } else { + DRM_ERROR("BIT BIOS not found\n"); + return -EINVAL; + } + + if (parse_dcb_table(dev, &dev_priv->bios)) + DRM_INFO("Found %d entries in DCB\n", dev_priv->dcb_table.entries); + + return 0; +} + +/* temporary */ +#define NV_RAMDAC_NVPLL 0x00680500 +#define NV_RAMDAC_MPLL 0x00680504 +#define NV_RAMDAC_VPLL 0x00680508 +# define NV_RAMDAC_PLL_COEFF_MDIV 0x000000FF +# define NV_RAMDAC_PLL_COEFF_NDIV 0x0000FF00 +# define NV_RAMDAC_PLL_COEFF_PDIV 0x00070000 +# define NV30_RAMDAC_ENABLE_VCO2 (1 << 7) +#define NV_RAMDAC_VPLL2 0x00680520 + +bool get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims *pll_lim) +{ + /* PLL limits table + * + * Version 0x10: NV31 + * One byte header (version), one record of 24 bytes + * Version 0x11: NV36 - Not implemented + * Seems to have same record style as 0x10, but 3 records rather than 1 + * Version 0x20: Found on Geforce 6 cards + * Trivial 4 byte BIT header. 31 (0x1f) byte record length + * Version 0x21: Found on Geforce 7, 8 and some Geforce 6 cards + * 5 byte header, fifth byte of unknown purpose. 35 (0x23) byte record + * length in general, some (integrated) have an extra configuration byte + */ + + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct bios *bios = &dev_priv->bios; + uint8_t pll_lim_ver = 0, headerlen = 0, recordlen = 0, entries = 0; + int pllindex = 0; + uint32_t crystal_strap_mask, crystal_straps; + + if (!bios->pll_limit_tbl_ptr) { + if (bios->chip_version >= 0x40 || bios->chip_version == 0x31 || bios->chip_version == 0x36) { + DRM_ERROR("Pointer to PLL limits table invalid\n"); + return false; + } + } else { + pll_lim_ver = bios->data[bios->pll_limit_tbl_ptr]; + + DRM_INFO("Found PLL limits table version 0x%X\n", pll_lim_ver); + } + + crystal_strap_mask = 1 << 6; + /* open coded pNv->twoHeads test */ + if (bios->chip_version > 0x10 && bios->chip_version != 0x15 && + bios->chip_version != 0x1a && bios->chip_version != 0x20) + crystal_strap_mask |= 1 << 22; + crystal_straps = NV_READ(NV50_PEXTDEV + 0x0) & crystal_strap_mask; + + switch (pll_lim_ver) { + /* we use version 0 to indicate a pre limit table bios (single stage pll) + * and load the hard coded limits instead */ + case 0: + break; + case 0x10: + case 0x11: /* strictly v0x11 has 3 entries, but the last two don't seem to get used */ + headerlen = 1; + recordlen = 0x18; + entries = 1; + pllindex = 0; + break; + case 0x20: + case 0x21: + headerlen = bios->data[bios->pll_limit_tbl_ptr + 1]; + recordlen = bios->data[bios->pll_limit_tbl_ptr + 2]; + entries = bios->data[bios->pll_limit_tbl_ptr + 3]; + break; + default: + DRM_ERROR("PLL limits table revision not currently supported\n"); + return false; + } + + /* initialize all members to zero */ + memset(pll_lim, 0, sizeof(struct pll_lims)); + + if (pll_lim_ver == 0x10 || pll_lim_ver == 0x11) { + uint16_t plloffs = bios->pll_limit_tbl_ptr + headerlen + recordlen * pllindex; + + pll_lim->vco1.minfreq = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs]))); + pll_lim->vco1.maxfreq = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs + 4]))); + pll_lim->vco2.minfreq = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs + 8]))); + pll_lim->vco2.maxfreq = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs + 12]))); + pll_lim->vco1.min_inputfreq = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs + 16]))); + pll_lim->vco2.min_inputfreq = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs + 20]))); + pll_lim->vco1.max_inputfreq = pll_lim->vco2.max_inputfreq = INT_MAX; + + /* these values taken from nv30/31/36 */ + pll_lim->vco1.min_n = 0x1; + if (bios->chip_version == 0x36) + pll_lim->vco1.min_n = 0x5; + pll_lim->vco1.max_n = 0xff; + pll_lim->vco1.min_m = 0x1; + pll_lim->vco1.max_m = 0xd; + pll_lim->vco2.min_n = 0x4; + /* on nv30, 31, 36 (i.e. all cards with two stage PLLs with this + * table version (apart from nv35)), N2 is compared to + * maxN2 (0x46) and 10 * maxM2 (0x4), so set maxN2 to 0x28 and + * save a comparison + */ + pll_lim->vco2.max_n = 0x28; + if (bios->chip_version == 0x30 || bios->chip_version == 0x35) + /* only 5 bits available for N2 on nv30/35 */ + pll_lim->vco2.max_n = 0x1f; + pll_lim->vco2.min_m = 0x1; + pll_lim->vco2.max_m = 0x4; + } else if (pll_lim_ver) { /* ver 0x20, 0x21 */ + uint16_t plloffs = bios->pll_limit_tbl_ptr + headerlen; + uint32_t reg = 0; /* default match */ + int i; + + /* first entry is default match, if nothing better. warn if reg field nonzero */ + if (le32_to_cpu(*((uint32_t *)&bios->data[plloffs]))) + DRM_ERROR("Default PLL limit entry has non-zero register field\n"); + + if (limit_match > MAX_PLL_TYPES) + /* we've been passed a reg as the match */ + reg = limit_match; + else /* limit match is a pll type */ + for (i = 1; i < entries && !reg; i++) { + uint32_t cmpreg = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs + recordlen * i]))); + + if (limit_match == NVPLL && (cmpreg == NV_RAMDAC_NVPLL || cmpreg == 0x4000)) + reg = cmpreg; + if (limit_match == MPLL && (cmpreg == NV_RAMDAC_MPLL || cmpreg == 0x4020)) + reg = cmpreg; + if (limit_match == VPLL1 && (cmpreg == NV_RAMDAC_VPLL || cmpreg == 0x4010)) + reg = cmpreg; + if (limit_match == VPLL2 && (cmpreg == NV_RAMDAC_VPLL2 || cmpreg == 0x4018)) + reg = cmpreg; + } + + for (i = 1; i < entries; i++) + if (le32_to_cpu(*((uint32_t *)&bios->data[plloffs + recordlen * i])) == reg) { + pllindex = i; + break; + } + + plloffs += recordlen * pllindex; + + DRM_INFO("Loading PLL limits for reg 0x%08x\n", pllindex ? reg : 0); + + /* frequencies are stored in tables in MHz, kHz are more useful, so we convert */ + + /* What output frequencies can each VCO generate? */ + pll_lim->vco1.minfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 4]))) * 1000; + pll_lim->vco1.maxfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 6]))) * 1000; + pll_lim->vco2.minfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 8]))) * 1000; + pll_lim->vco2.maxfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 10]))) * 1000; + + /* What input frequencies do they accept (past the m-divider)? */ + pll_lim->vco1.min_inputfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 12]))) * 1000; + pll_lim->vco2.min_inputfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 14]))) * 1000; + pll_lim->vco1.max_inputfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 16]))) * 1000; + pll_lim->vco2.max_inputfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 18]))) * 1000; + + /* What values are accepted as multiplier and divider? */ + pll_lim->vco1.min_n = bios->data[plloffs + 20]; + pll_lim->vco1.max_n = bios->data[plloffs + 21]; + pll_lim->vco1.min_m = bios->data[plloffs + 22]; + pll_lim->vco1.max_m = bios->data[plloffs + 23]; + pll_lim->vco2.min_n = bios->data[plloffs + 24]; + pll_lim->vco2.max_n = bios->data[plloffs + 25]; + pll_lim->vco2.min_m = bios->data[plloffs + 26]; + pll_lim->vco2.max_m = bios->data[plloffs + 27]; + + pll_lim->unk1c = bios->data[plloffs + 28]; + pll_lim->max_log2p_bias = bios->data[plloffs + 29]; + pll_lim->log2p_bias = bios->data[plloffs + 30]; + + if (recordlen > 0x22) + pll_lim->refclk = le32_to_cpu(*((uint32_t *)&bios->data[plloffs + 31])); + + if (recordlen > 0x23) + if (bios->data[plloffs + 35]) + DRM_ERROR("Bits set in PLL configuration byte (%x)\n", bios->data[plloffs + 35]); + + /* C51 special not seen elsewhere */ + /*if (bios->chip_version == 0x51 && !pll_lim->refclk) { + uint32_t sel_clk = nv32_rd(pScrn, NV_RAMDAC_SEL_CLK); + + if (((limit_match == NV_RAMDAC_VPLL || limit_match == VPLL1) && sel_clk & 0x20) || + ((limit_match == NV_RAMDAC_VPLL2 || limit_match == VPLL2) && sel_clk & 0x80)) { + if (nv_idx_port_rd(pScrn, CRTC_INDEX_COLOR, NV_VGA_CRTCX_REVISION) < 0xa3) + pll_lim->refclk = 200000; + else + pll_lim->refclk = 25000; + } + }*/ + } + + /* By now any valid limit table ought to have set a max frequency for + * vco1, so if it's zero it's either a pre limit table bios, or one + * with an empty limit table (seen on nv18) + */ + if (!pll_lim->vco1.maxfreq) { + pll_lim->vco1.minfreq = bios->fminvco; + pll_lim->vco1.maxfreq = bios->fmaxvco; + pll_lim->vco1.min_inputfreq = 0; + pll_lim->vco1.max_inputfreq = INT_MAX; + pll_lim->vco1.min_n = 0x1; + pll_lim->vco1.max_n = 0xff; + pll_lim->vco1.min_m = 0x1; + if (crystal_straps == 0) { + /* nv05 does this, nv11 doesn't, nv10 unknown */ + if (bios->chip_version < 0x11) + pll_lim->vco1.min_m = 0x7; + pll_lim->vco1.max_m = 0xd; + } else { + if (bios->chip_version < 0x11) + pll_lim->vco1.min_m = 0x8; + pll_lim->vco1.max_m = 0xe; + } + } + + if (!pll_lim->refclk) + switch (crystal_straps) { + case 0: + pll_lim->refclk = 13500; + break; + case (1 << 6): + pll_lim->refclk = 14318; + break; + case (1 << 22): + pll_lim->refclk = 27000; + break; + case (1 << 22 | 1 << 6): + pll_lim->refclk = 25000; + break; + } + +#if 1 /* for easy debugging */ + DRM_INFO("pll.vco1.minfreq: %d\n", pll_lim->vco1.minfreq); + DRM_INFO("pll.vco1.maxfreq: %d\n", pll_lim->vco1.maxfreq); + DRM_INFO("pll.vco2.minfreq: %d\n", pll_lim->vco2.minfreq); + DRM_INFO("pll.vco2.maxfreq: %d\n", pll_lim->vco2.maxfreq); + + DRM_INFO("pll.vco1.min_inputfreq: %d\n", pll_lim->vco1.min_inputfreq); + DRM_INFO("pll.vco1.max_inputfreq: %d\n", pll_lim->vco1.max_inputfreq); + DRM_INFO("pll.vco2.min_inputfreq: %d\n", pll_lim->vco2.min_inputfreq); + DRM_INFO("pll.vco2.max_inputfreq: %d\n", pll_lim->vco2.max_inputfreq); + + DRM_INFO("pll.vco1.min_n: %d\n", pll_lim->vco1.min_n); + DRM_INFO("pll.vco1.max_n: %d\n", pll_lim->vco1.max_n); + DRM_INFO("pll.vco1.min_m: %d\n", pll_lim->vco1.min_m); + DRM_INFO("pll.vco1.max_m: %d\n", pll_lim->vco1.max_m); + DRM_INFO("pll.vco2.min_n: %d\n", pll_lim->vco2.min_n); + DRM_INFO("pll.vco2.max_n: %d\n", pll_lim->vco2.max_n); + DRM_INFO("pll.vco2.min_m: %d\n", pll_lim->vco2.min_m); + DRM_INFO("pll.vco2.max_m: %d\n", pll_lim->vco2.max_m); + + DRM_INFO("pll.unk1c: %d\n", pll_lim->unk1c); + DRM_INFO("pll.max_log2p_bias: %d\n", pll_lim->max_log2p_bias); + DRM_INFO("pll.log2p_bias: %d\n", pll_lim->log2p_bias); + + DRM_INFO("pll.refclk: %d\n", pll_lim->refclk); +#endif + + return true; +} diff --git a/linux-core/nouveau_bios.h b/linux-core/nouveau_bios.h new file mode 100644 index 00000000..e33ecd0f --- /dev/null +++ b/linux-core/nouveau_bios.h @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2005-2006 Erik Waling + * Copyright (C) 2006 Stephane Marchesin + * Copyright (C) 2007-2008 Stuart Bennett + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +#ifndef __NOUVEAU_BIOS_H__ +#define __NOUVEAU_BIOS_H__ + +#include "drmP.h" +#include "drm.h" + +#define LOC_ON_CHIP 0 + +enum dcb_output_type {/* matches DCB types */ + DCB_OUTPUT_NONE = 4, + DCB_OUTPUT_ANALOG = 0, + DCB_OUTPUT_TMDS = 2, + DCB_OUTPUT_LVDS = 3, + DCB_OUTPUT_TV = 1, +}; + +struct bios { + uint8_t *data; + unsigned int length; + bool execute; + + uint8_t major_version, chip_version; + uint8_t feature_byte; + + uint32_t fmaxvco, fminvco; + + uint32_t dactestval; + + uint16_t init_script_tbls_ptr; + uint16_t extra_init_script_tbl_ptr; + uint16_t macro_index_tbl_ptr; + uint16_t macro_tbl_ptr; + uint16_t condition_tbl_ptr; + uint16_t io_condition_tbl_ptr; + uint16_t io_flag_condition_tbl_ptr; + uint16_t init_function_tbl_ptr; + + uint16_t pll_limit_tbl_ptr; + uint16_t ram_restrict_tbl_ptr; + + struct { + struct nouveau_hw_mode *native_mode; + uint8_t *edid; + uint16_t lvdsmanufacturerpointer; + uint16_t xlated_entry; + bool power_off_for_reset; + bool reset_after_pclk_change; + bool dual_link; + bool link_c_increment; + bool if_is_24bit; + bool BITbit1; + int duallink_transition_clk; + /* lower nibble stores PEXTDEV_BOOT_0 strap + * upper nibble stores xlated display strap */ + uint8_t strapping; + } fp; + + struct { + uint16_t output0_script_ptr; + uint16_t output1_script_ptr; + } tmds; + + struct { + uint16_t mem_init_tbl_ptr; + uint16_t sdr_seq_tbl_ptr; + uint16_t ddr_seq_tbl_ptr; + + struct { + uint8_t crt, tv, panel; + } i2c_indices; + } legacy; +}; + +struct dcb_entry { + int index; + uint8_t type; + uint8_t i2c_index; + uint8_t heads; + uint8_t bus; + uint8_t location; + uint8_t or; + bool duallink_possible; + union { + struct { + bool use_straps_for_mode; + bool use_power_scripts; + } lvdsconf; + }; +}; + +/* changing these requires matching changes to reg tables in nv_get_clock */ +#define MAX_PLL_TYPES 4 +enum pll_types { + NVPLL, + MPLL, + VPLL1, + VPLL2 +}; + +struct pll_lims { + struct { + int minfreq; + int maxfreq; + int min_inputfreq; + int max_inputfreq; + + uint8_t min_m; + uint8_t max_m; + uint8_t min_n; + uint8_t max_n; + } vco1, vco2; + + uint8_t unk1c; + uint8_t max_log2p_bias; + uint8_t log2p_bias; + int refclk; +}; + +bool get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims *pll_lim); +int nouveau_parse_bios(struct drm_device *dev); + +#endif /* __NOUVEAU_BIOS_H__ */ diff --git a/linux-core/nouveau_drv.c b/linux-core/nouveau_drv.c index c8f57dff..04f002f2 100644 --- a/linux-core/nouveau_drv.c +++ b/linux-core/nouveau_drv.c @@ -28,6 +28,9 @@ #include "drm_pciids.h" +unsigned int nouveau_modeset = 0; /* kms */ +module_param_named(modeset, nouveau_modeset, int, 0400); + static struct pci_device_id pciidlist[] = { { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID), @@ -104,6 +107,10 @@ static int probe(struct pci_dev *pdev, const struct pci_device_id *ent) static int __init nouveau_init(void) { driver.num_ioctls = nouveau_max_ioctl; + + if (nouveau_modeset == 1) + driver.driver_features |= DRIVER_MODESET; + return drm_init(&driver, pciidlist); } diff --git a/linux-core/nv50_connector.c b/linux-core/nv50_connector.c new file mode 100644 index 00000000..34706bae --- /dev/null +++ b/linux-core/nv50_connector.c @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +#include "nv50_connector.h" + +static struct nv50_output *nv50_connector_to_output(struct nv50_connector *connector, bool digital) +{ + struct nv50_display *display = nv50_get_display(connector->dev); + struct nv50_output *output = NULL; + bool digital_possible = false; + bool analog_possible = false; + + switch (connector->type) { + case CONNECTOR_VGA: + case CONNECTOR_TV: + analog_possible = true; + break; + case CONNECTOR_DVI_I: + analog_possible = true; + digital_possible = true; + break; + case CONNECTOR_DVI_D: + case CONNECTOR_LVDS: + digital_possible = true; + break; + default: + break; + } + + /* Return early on bad situations. */ + if (!analog_possible && !digital_possible) + return NULL; + + if (!analog_possible && !digital) + return NULL; + + if (!digital_possible && digital) + return NULL; + + list_for_each_entry(output, &display->outputs, item) { + if (connector->bus != output->bus) + continue; + if (digital && output->type == OUTPUT_TMDS) + return output; + if (digital && output->type == OUTPUT_LVDS) + return output; + if (!digital && output->type == OUTPUT_DAC) + return output; + if (!digital && output->type == OUTPUT_TV) + return output; + } + + return NULL; +} + +static int nv50_connector_hpd_detect(struct nv50_connector *connector) +{ + struct drm_nouveau_private *dev_priv = connector->dev->dev_private; + bool present = 0; + uint32_t reg = 0; + + /* Assume connected for the moment. */ + if (connector->type == CONNECTOR_LVDS) { + NV50_DEBUG("LVDS is defaulting to connected for the moment.\n"); + return 1; + } + + /* No i2c port, no idea what to do for hotplug. */ + if (connector->i2c_chan->index == 15) { + DRM_ERROR("You have a non-LVDS SOR with no i2c port, please report\n"); + return -EINVAL; + } + + if (connector->i2c_chan->index > 3) { + DRM_ERROR("You have an unusual configuration, index is %d\n", connector->i2c_chan->index); + DRM_ERROR("Please report.\n"); + return -EINVAL; + } + + /* Check hotplug pins. */ + reg = NV_READ(NV50_PCONNECTOR_HOTPLUG_STATE); + if (reg & (NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C0 << (4 * connector->i2c_chan->index))) + present = 1; + + if (present) + NV50_DEBUG("Hotplug detect returned positive for bus %d\n", connector->bus); + else + NV50_DEBUG("Hotplug detect returned negative for bus %d\n", connector->bus); + + return present; +} + +static int nv50_connector_i2c_detect(struct nv50_connector *connector) +{ + /* kindly borrrowed from the intel driver, hope it works. */ + uint8_t out_buf[] = { 0x0, 0x0}; + uint8_t 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, + } + }; + + if (!connector->i2c_chan) + return -EINVAL; + + ret = i2c_transfer(&connector->i2c_chan->adapter, msgs, 2); + NV50_DEBUG("I2C detect returned %d\n", ret); + + if (ret == 2) + return true; + + return false; +} + +static int nv50_connector_destroy(struct nv50_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv50_display *display = nv50_get_display(dev); + + NV50_DEBUG("\n"); + + if (!display || !connector) + return -EINVAL; + + list_del(&connector->item); + + if (connector->i2c_chan) + nv50_i2c_channel_destroy(connector->i2c_chan); + + if (dev_priv->free_connector) + dev_priv->free_connector(connector); + + return 0; +} + +int nv50_connector_create(struct drm_device *dev, int bus, int i2c_index, int type) +{ + struct nv50_connector *connector = NULL; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv50_display *display = NULL; + + NV50_DEBUG("\n"); + + /* This allows the public layer to do it's thing. */ + if (dev_priv->alloc_connector) + connector = dev_priv->alloc_connector(dev); + + if (!connector) + return -ENOMEM; + + connector->dev = dev; + + display = nv50_get_display(dev); + if (!display) + goto out; + + if (type == CONNECTOR_UNKNOWN) + goto out; + + list_add_tail(&connector->item, &display->connectors); + + connector->bus = bus; + connector->type = type; + + switch (type) { + case CONNECTOR_VGA: + DRM_INFO("Detected a VGA connector\n"); + break; + case CONNECTOR_DVI_D: + DRM_INFO("Detected a DVI-D connector\n"); + break; + case CONNECTOR_DVI_I: + DRM_INFO("Detected a DVI-I connector\n"); + break; + case CONNECTOR_LVDS: + DRM_INFO("Detected a LVDS connector\n"); + break; + case CONNECTOR_TV: + DRM_INFO("Detected a TV connector\n"); + break; + default: + DRM_ERROR("Unknown connector, this is not good.\n"); + break; + } + + /* some reasonable defaults */ + if (type == CONNECTOR_DVI_D || type == CONNECTOR_DVI_I || type == CONNECTOR_LVDS) + connector->requested_scaling_mode = SCALE_FULLSCREEN; + else + connector->requested_scaling_mode = SCALE_NON_GPU; + + connector->use_dithering = false; + + if (i2c_index < 0xf) + connector->i2c_chan = nv50_i2c_channel_create(dev, i2c_index); + + /* set function pointers */ + connector->hpd_detect = nv50_connector_hpd_detect; + connector->i2c_detect = nv50_connector_i2c_detect; + connector->destroy = nv50_connector_destroy; + connector->to_output = nv50_connector_to_output; + + return 0; + +out: + if (dev_priv->free_connector) + dev_priv->free_connector(connector); + + return -EINVAL; +} diff --git a/linux-core/nv50_connector.h b/linux-core/nv50_connector.h new file mode 100644 index 00000000..fa7316e2 --- /dev/null +++ b/linux-core/nv50_connector.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +#ifndef __NV50_CONNECTOR_H__ +#define __NV50_CONNECTOR_H__ + +#include "nv50_output.h" +#include "nv50_i2c.h" + +#define CONNECTOR_UNKNOWN 0 +#define CONNECTOR_VGA 1 +#define CONNECTOR_DVI_D 2 +#define CONNECTOR_DVI_I 3 +#define CONNECTOR_LVDS 4 +#define CONNECTOR_TV 5 + +struct nv50_connector { + struct list_head item; + + struct drm_device *dev; + int type; + + int bus; + struct nv50_i2c_channel *i2c_chan; + struct nv50_output *output; + + int requested_scaling_mode; + + bool use_dithering; + + int (*hpd_detect) (struct nv50_connector *connector); + int (*i2c_detect) (struct nv50_connector *connector); + int (*destroy) (struct nv50_connector *connector); + struct nv50_output *(*to_output) (struct nv50_connector *connector, bool digital); +}; + +int nv50_connector_create(struct drm_device *dev, int bus, int i2c_index, int type); + +#endif /* __NV50_CONNECTOR_H__ */ diff --git a/linux-core/nv50_crtc.c b/linux-core/nv50_crtc.c new file mode 100644 index 00000000..f56aa339 --- /dev/null +++ b/linux-core/nv50_crtc.c @@ -0,0 +1,533 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +#include "nv50_crtc.h" +#include "nv50_cursor.h" +#include "nv50_lut.h" +#include "nv50_fb.h" + +static int nv50_crtc_validate_mode(struct nv50_crtc *crtc, struct nouveau_hw_mode *mode) +{ + NV50_DEBUG("\n"); + + if (mode->clock > 400000) + return MODE_CLOCK_HIGH; + + if (mode->clock < 25000) + return MODE_CLOCK_LOW; + + return MODE_OK; +} + +static int nv50_crtc_set_mode(struct nv50_crtc *crtc, struct nouveau_hw_mode *mode) +{ + struct nouveau_hw_mode *hw_mode = crtc->mode; + uint8_t rval; + + NV50_DEBUG("index %d\n", crtc->index); + + if (!mode) { + DRM_ERROR("No mode\n"); + return MODE_NOMODE; + } + + if ((rval = crtc->validate_mode(crtc, mode))) { + DRM_ERROR("Mode invalid\n"); + return rval; + } + + /* copy values to mode */ + *hw_mode = *mode; + + return 0; +} + +static int nv50_crtc_execute_mode(struct nv50_crtc *crtc) +{ + struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + struct nouveau_hw_mode *hw_mode; + uint32_t hsync_dur, vsync_dur, hsync_start_to_end, vsync_start_to_end; + uint32_t hunk1, vunk1, vunk2a, vunk2b; + uint32_t offset = crtc->index * 0x400; + + NV50_DEBUG("index %d\n", crtc->index); + NV50_DEBUG("%s native mode\n", crtc->use_native_mode ? "using" : "not using"); + + if (crtc->use_native_mode) + hw_mode = crtc->native_mode; + else + hw_mode = crtc->mode; + + hsync_dur = hw_mode->hsync_end - hw_mode->hsync_start; + vsync_dur = hw_mode->vsync_end - hw_mode->vsync_start; + hsync_start_to_end = hw_mode->hblank_end - hw_mode->hsync_start; + vsync_start_to_end = hw_mode->vblank_end - hw_mode->vsync_start; + /* I can't give this a proper name, anyone else can? */ + hunk1 = hw_mode->htotal - hw_mode->hsync_start + hw_mode->hblank_start; + vunk1 = hw_mode->vtotal - hw_mode->vsync_start + hw_mode->vblank_start; + /* Another strange value, this time only for interlaced modes. */ + vunk2a = 2*hw_mode->vtotal - hw_mode->vsync_start + hw_mode->vblank_start; + vunk2b = hw_mode->vtotal - hw_mode->vsync_start + hw_mode->vblank_end; + + if (hw_mode->flags & DRM_MODE_FLAG_INTERLACE) { + vsync_dur /= 2; + vsync_start_to_end /= 2; + vunk1 /= 2; + vunk2a /= 2; + vunk2b /= 2; + /* magic */ + if (hw_mode->flags & DRM_MODE_FLAG_DBLSCAN) { + vsync_start_to_end -= 1; + vunk1 -= 1; + vunk2a -= 1; + vunk2b -= 1; + } + } + + OUT_MODE(NV50_CRTC0_CLOCK + offset, hw_mode->clock | 0x800000); + OUT_MODE(NV50_CRTC0_INTERLACE + offset, (hw_mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 0); + OUT_MODE(NV50_CRTC0_DISPLAY_START + offset, 0); + OUT_MODE(NV50_CRTC0_UNK82C + offset, 0); + OUT_MODE(NV50_CRTC0_DISPLAY_TOTAL + offset, hw_mode->vtotal << 16 | hw_mode->htotal); + OUT_MODE(NV50_CRTC0_SYNC_DURATION + offset, (vsync_dur - 1) << 16 | (hsync_dur - 1)); + OUT_MODE(NV50_CRTC0_SYNC_START_TO_BLANK_END + offset, (vsync_start_to_end - 1) << 16 | (hsync_start_to_end - 1)); + OUT_MODE(NV50_CRTC0_MODE_UNK1 + offset, (vunk1 - 1) << 16 | (hunk1 - 1)); + if (hw_mode->flags & DRM_MODE_FLAG_INTERLACE) { + OUT_MODE(NV50_CRTC0_MODE_UNK2 + offset, (vunk2b - 1) << 16 | (vunk2a - 1)); + } + + crtc->set_fb(crtc); + crtc->set_dither(crtc); + + /* This is the actual resolution of the mode. */ + OUT_MODE(NV50_CRTC0_REAL_RES + offset, (crtc->mode->vdisplay << 16) | crtc->mode->hdisplay); + OUT_MODE(NV50_CRTC0_SCALE_CENTER_OFFSET + offset, NV50_CRTC_SCALE_CENTER_OFFSET_VAL(0,0)); + + /* Maybe move this as well? */ + crtc->blank(crtc, false); + + return 0; +} + +static int nv50_crtc_set_fb(struct nv50_crtc *crtc) +{ + struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + uint32_t offset = crtc->index * 0x400; + + NV50_DEBUG("\n"); + + OUT_MODE(NV50_CRTC0_FB_SIZE + offset, crtc->fb->height << 16 | crtc->fb->width); + + /* I suspect this flag indicates a linear fb. */ + OUT_MODE(NV50_CRTC0_FB_PITCH + offset, crtc->fb->pitch | 0x100000); + + switch (crtc->fb->depth) { + case 8: + OUT_MODE(NV50_CRTC0_DEPTH + offset, NV50_CRTC0_DEPTH_8BPP); + break; + case 15: + OUT_MODE(NV50_CRTC0_DEPTH + offset, NV50_CRTC0_DEPTH_15BPP); + break; + case 16: + OUT_MODE(NV50_CRTC0_DEPTH + offset, NV50_CRTC0_DEPTH_16BPP); + break; + case 24: + OUT_MODE(NV50_CRTC0_DEPTH + offset, NV50_CRTC0_DEPTH_24BPP); + break; + } + + OUT_MODE(NV50_CRTC0_COLOR_CTRL + offset, NV50_CRTC_COLOR_CTRL_MODE_COLOR); + OUT_MODE(NV50_CRTC0_FB_POS + offset, (crtc->fb->y << 16) | (crtc->fb->x)); + + return 0; +} + +static int nv50_crtc_blank(struct nv50_crtc *crtc, bool blanked) +{ + struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + uint32_t offset = crtc->index * 0x400; + + NV50_DEBUG("index %d\n", crtc->index); + NV50_DEBUG("%s\n", blanked ? "blanked" : "unblanked"); + + /* We really need a framebuffer. */ + if (!crtc->fb->block && !blanked) { + DRM_ERROR("No framebuffer available on crtc %d\n", crtc->index); + return -EINVAL; + } + + if (blanked) { + crtc->cursor->hide(crtc); + + OUT_MODE(NV50_CRTC0_CLUT_MODE + offset, NV50_CRTC0_CLUT_MODE_BLANK); + OUT_MODE(NV50_CRTC0_CLUT_OFFSET + offset, 0); + if (dev_priv->chipset != 0x50) + OUT_MODE(NV84_CRTC0_BLANK_UNK1 + offset, NV84_CRTC0_BLANK_UNK1_BLANK); + OUT_MODE(NV50_CRTC0_BLANK_CTRL + offset, NV50_CRTC0_BLANK_CTRL_BLANK); + if (dev_priv->chipset != 0x50) + OUT_MODE(NV84_CRTC0_BLANK_UNK2 + offset, NV84_CRTC0_BLANK_UNK2_BLANK); + } else { + OUT_MODE(NV50_CRTC0_FB_OFFSET + offset, crtc->fb->block->start >> 8); + OUT_MODE(0x864 + offset, 0); + + crtc->cursor->set_offset(crtc); + + if (dev_priv->chipset != 0x50) + OUT_MODE(NV84_CRTC0_BLANK_UNK2 + offset, NV84_CRTC0_BLANK_UNK2_UNBLANK); + + if (crtc->cursor->visible) + crtc->cursor->show(crtc); + else + crtc->cursor->hide(crtc); + + OUT_MODE(NV50_CRTC0_CLUT_MODE + offset, + crtc->fb->depth == 8 ? NV50_CRTC0_CLUT_MODE_OFF : NV50_CRTC0_CLUT_MODE_ON); + OUT_MODE(NV50_CRTC0_CLUT_OFFSET + offset, crtc->lut->block->start >> 8); + if (dev_priv->chipset != 0x50) + OUT_MODE(NV84_CRTC0_BLANK_UNK1 + offset, NV84_CRTC0_BLANK_UNK1_UNBLANK); + OUT_MODE(NV50_CRTC0_BLANK_CTRL + offset, NV50_CRTC0_BLANK_CTRL_UNBLANK); + } + + /* sometimes you need to know if a screen is already blanked. */ + crtc->blanked = blanked; + + return 0; +} + +static int nv50_crtc_set_dither(struct nv50_crtc *crtc) +{ + struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + uint32_t offset = crtc->index * 0x400; + + NV50_DEBUG("\n"); + + OUT_MODE(NV50_CRTC0_DITHERING_CTRL + offset, crtc->use_dithering ? + NV50_CRTC0_DITHERING_CTRL_ON : NV50_CRTC0_DITHERING_CTRL_OFF); + + return 0; +} + +static void nv50_crtc_calc_scale(struct nv50_crtc *crtc, uint32_t *outX, uint32_t *outY) +{ + uint32_t hor_scale, ver_scale; + + /* max res is 8192, which is 2^13, which leaves 19 bits */ + hor_scale = (crtc->native_mode->hdisplay << 19)/crtc->mode->hdisplay; + ver_scale = (crtc->native_mode->vdisplay << 19)/crtc->mode->vdisplay; + + if (ver_scale > hor_scale) { + *outX = (crtc->mode->hdisplay * hor_scale) >> 19; + *outY = (crtc->mode->vdisplay * hor_scale) >> 19; + } else { + *outX = (crtc->mode->hdisplay * ver_scale) >> 19; + *outY = (crtc->mode->vdisplay * ver_scale) >> 19; + } +} + +static int nv50_crtc_set_scale(struct nv50_crtc *crtc) +{ + struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + uint32_t offset = crtc->index * 0x400; + uint32_t outX, outY; + + NV50_DEBUG("\n"); + + switch (crtc->requested_scaling_mode) { + case SCALE_ASPECT: + nv50_crtc_calc_scale(crtc, &outX, &outY); + break; + case SCALE_FULLSCREEN: + outX = crtc->native_mode->hdisplay; + outY = crtc->native_mode->vdisplay; + break; + case SCALE_NOSCALE: + case SCALE_NON_GPU: + default: + outX = crtc->mode->hdisplay; + outY = crtc->mode->vdisplay; + break; + } + + /* Got a better name for SCALER_ACTIVE? */ + /* One day i've got to really figure out why this is needed. */ + if ((crtc->mode->flags & DRM_MODE_FLAG_DBLSCAN) || (crtc->mode->flags & DRM_MODE_FLAG_INTERLACE) || + crtc->mode->hdisplay != outX || crtc->mode->vdisplay != outY) { + OUT_MODE(NV50_CRTC0_SCALE_CTRL + offset, NV50_CRTC0_SCALE_CTRL_SCALER_ACTIVE); + } else { + OUT_MODE(NV50_CRTC0_SCALE_CTRL + offset, NV50_CRTC0_SCALE_CTRL_SCALER_INACTIVE); + } + + OUT_MODE(NV50_CRTC0_SCALE_RES1 + offset, outY << 16 | outX); + OUT_MODE(NV50_CRTC0_SCALE_RES2 + offset, outY << 16 | outX); + + /* processed */ + crtc->scaling_mode = crtc->requested_scaling_mode; + + return 0; +} + +static int nv50_crtc_calc_clock(struct nv50_crtc *crtc, + uint32_t *bestN1, uint32_t *bestN2, uint32_t *bestM1, uint32_t *bestM2, uint32_t *bestlog2P) +{ + struct nouveau_hw_mode *hw_mode; + struct pll_lims limits; + int clk, vco2, crystal; + int minvco1, minvco2, minU1, maxU1, minU2, maxU2, minM1, maxM1; + int maxvco1, maxvco2, minN1, maxN1, minM2, maxM2, minN2, maxN2; + bool fixedgain2; + int M1, N1, M2, N2, log2P; + int clkP, calcclk1, calcclk2, calcclkout; + int delta, bestdelta = INT_MAX; + int bestclk = 0; + + NV50_DEBUG("\n"); + + if (crtc->use_native_mode) + hw_mode = crtc->native_mode; + else + hw_mode = crtc->mode; + + clk = hw_mode->clock; + + /* These are in the g80 bios tables, at least in mine. */ + if (!get_pll_limits(crtc->dev, NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1(crtc->index), &limits)) + return -EINVAL; + + minvco1 = limits.vco1.minfreq, maxvco1 = limits.vco1.maxfreq; + minvco2 = limits.vco2.minfreq, maxvco2 = limits.vco2.maxfreq; + minU1 = limits.vco1.min_inputfreq, minU2 = limits.vco2.min_inputfreq; + maxU1 = limits.vco1.max_inputfreq, maxU2 = limits.vco2.max_inputfreq; + minM1 = limits.vco1.min_m, maxM1 = limits.vco1.max_m; + minN1 = limits.vco1.min_n, maxN1 = limits.vco1.max_n; + minM2 = limits.vco2.min_m, maxM2 = limits.vco2.max_m; + minN2 = limits.vco2.min_n, maxN2 = limits.vco2.max_n; + crystal = limits.refclk; + fixedgain2 = (minM2 == maxM2 && minN2 == maxN2); + + vco2 = (maxvco2 - maxvco2/200) / 2; + for (log2P = 0; clk && log2P < 6 && clk <= (vco2 >> log2P); log2P++) /* log2P is maximum of 6 */ + ; + clkP = clk << log2P; + + if (maxvco2 < clk + clk/200) /* +0.5% */ + maxvco2 = clk + clk/200; + + for (M1 = minM1; M1 <= maxM1; M1++) { + if (crystal/M1 < minU1) + return bestclk; + if (crystal/M1 > maxU1) + continue; + + for (N1 = minN1; N1 <= maxN1; N1++) { + calcclk1 = crystal * N1 / M1; + if (calcclk1 < minvco1) + continue; + if (calcclk1 > maxvco1) + break; + + for (M2 = minM2; M2 <= maxM2; M2++) { + if (calcclk1/M2 < minU2) + break; + if (calcclk1/M2 > maxU2) + continue; + + /* add calcclk1/2 to round better */ + N2 = (clkP * M2 + calcclk1/2) / calcclk1; + if (N2 < minN2) + continue; + if (N2 > maxN2) + break; + + if (!fixedgain2) { + calcclk2 = calcclk1 * N2 / M2; + if (calcclk2 < minvco2) + break; + if (calcclk2 > maxvco2) + continue; + } else + calcclk2 = calcclk1; + + calcclkout = calcclk2 >> log2P; + delta = abs(calcclkout - clk); + /* we do an exhaustive search rather than terminating + * on an optimality condition... + */ + if (delta < bestdelta) { + bestdelta = delta; + bestclk = calcclkout; + *bestN1 = N1; + *bestN2 = N2; + *bestM1 = M1; + *bestM2 = M2; + *bestlog2P = log2P; + if (delta == 0) /* except this one */ + return bestclk; + } + } + } + } + + return bestclk; +} + +static int nv50_crtc_set_clock(struct nv50_crtc *crtc) +{ + struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + + uint32_t pll_reg = NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1(crtc->index); + + uint32_t N1 = 0, N2 = 0, M1 = 0, M2 = 0, log2P = 0; + + uint32_t reg1 = NV_READ(pll_reg + 4); + uint32_t reg2 = NV_READ(pll_reg + 8); + + NV50_DEBUG("\n"); + + NV_WRITE(pll_reg, NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1_CONNECTED | 0x10000011); + + /* The other bits are typically empty, but let's be on the safe side. */ + reg1 &= 0xff00ff00; + reg2 &= 0x8000ff00; + + if (!nv50_crtc_calc_clock(crtc, &N1, &N2, &M1, &M2, &log2P)) + return -EINVAL; + + NV50_DEBUG("N1 %d N2 %d M1 %d M2 %d log2P %d\n", N1, N2, M1, M2, log2P); + + reg1 |= (M1 << 16) | N1; + reg2 |= (log2P << 28) | (M2 << 16) | N2; + + NV_WRITE(pll_reg + 4, reg1); + NV_WRITE(pll_reg + 8, reg2); + + return 0; +} + +static int nv50_crtc_set_clock_mode(struct nv50_crtc *crtc) +{ + struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + + NV50_DEBUG("\n"); + + /* This acknowledges a clock request. */ + NV_WRITE(NV50_PDISPLAY_CRTC_CLK_CLK_CTRL2(crtc->index), 0); + + return 0; +} + +static int nv50_crtc_destroy(struct nv50_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv50_display *display = nv50_get_display(dev); + + NV50_DEBUG("\n"); + + if (!display || !crtc) + return -EINVAL; + + list_del(&crtc->item); + + nv50_fb_destroy(crtc); + nv50_lut_destroy(crtc); + nv50_cursor_destroy(crtc); + + kfree(crtc->mode); + kfree(crtc->native_mode); + + if (dev_priv->free_crtc) + dev_priv->free_crtc(crtc); + + return 0; +} + +int nv50_crtc_create(struct drm_device *dev, int index) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv50_crtc *crtc = NULL; + struct nv50_display *display = NULL; + int rval = 0; + + NV50_DEBUG("\n"); + + /* This allows the public layer to do it's thing. */ + if (dev_priv->alloc_crtc) + crtc = dev_priv->alloc_crtc(dev); + + if (!crtc) + return -ENOMEM; + + crtc->dev = dev; + + display = nv50_get_display(dev); + if (!display) { + rval = -EINVAL; + goto out; + } + + list_add_tail(&crtc->item, &display->crtcs); + + crtc->index = index; + + crtc->mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL); + crtc->native_mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL); + + crtc->requested_scaling_mode = SCALE_INVALID; + crtc->scaling_mode = SCALE_INVALID; + + if (!crtc->mode || !crtc->native_mode) { + rval = -ENOMEM; + goto out; + } + + nv50_fb_create(crtc); + nv50_lut_create(crtc); + nv50_cursor_create(crtc); + + /* set function pointers */ + crtc->validate_mode = nv50_crtc_validate_mode; + crtc->set_mode = nv50_crtc_set_mode; + crtc->execute_mode = nv50_crtc_execute_mode; + crtc->set_fb = nv50_crtc_set_fb; + crtc->blank = nv50_crtc_blank; + crtc->set_dither = nv50_crtc_set_dither; + crtc->set_scale = nv50_crtc_set_scale; + crtc->set_clock = nv50_crtc_set_clock; + crtc->set_clock_mode = nv50_crtc_set_clock_mode; + crtc->destroy = nv50_crtc_destroy; + + return 0; + +out: + if (crtc->mode) + kfree(crtc->mode); + if (crtc->native_mode) + kfree(crtc->native_mode); + if (dev_priv->free_crtc) + dev_priv->free_crtc(crtc); + + return rval; +} diff --git a/linux-core/nv50_crtc.h b/linux-core/nv50_crtc.h new file mode 100644 index 00000000..b4b83584 --- /dev/null +++ b/linux-core/nv50_crtc.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +#ifndef __NV50_CRTC_H__ +#define __NV50_CRTC_H__ + +#include "nv50_display.h" + +struct nv50_cursor; +struct nv50_lut; +struct nv50_fb; + +struct nv50_crtc { + struct list_head item; + + struct drm_device *dev; + int index; + bool enabled; + bool blanked; + + struct nouveau_hw_mode *mode; + struct nouveau_hw_mode *native_mode; + + bool use_native_mode; + bool use_dithering; + + /* Changing scaling modes requires a modeset sometimes. */ + /* We need to know the currently active hw mode, as well as the requested one by the user. */ + int requested_scaling_mode; + int scaling_mode; + + struct nv50_cursor *cursor; + struct nv50_lut *lut; + struct nv50_fb *fb; + + int (*validate_mode) (struct nv50_crtc *crtc, struct nouveau_hw_mode *mode); + int (*set_mode) (struct nv50_crtc *crtc, struct nouveau_hw_mode *mode); + int (*execute_mode) (struct nv50_crtc *crtc); + int (*set_fb) (struct nv50_crtc *crtc); + int (*blank) (struct nv50_crtc *crtc, bool blanked); + int (*set_dither) (struct nv50_crtc *crtc); + int (*set_scale) (struct nv50_crtc *crtc); + int (*set_clock) (struct nv50_crtc *crtc); + int (*set_clock_mode) (struct nv50_crtc *crtc); + int (*destroy) (struct nv50_crtc *crtc); +}; + +int nv50_crtc_create(struct drm_device *dev, int index); + +#endif /* __NV50_CRTC_H__ */ diff --git a/linux-core/nv50_cursor.c b/linux-core/nv50_cursor.c new file mode 100644 index 00000000..091c94fe --- /dev/null +++ b/linux-core/nv50_cursor.c @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +#include "nv50_cursor.h" +#include "nv50_crtc.h" +#include "nv50_display.h" + +static int nv50_cursor_enable(struct nv50_crtc *crtc) +{ + struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + + NV50_DEBUG("\n"); + + NV_WRITE(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(crtc->index), 0x2000); + while(NV_READ(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(crtc->index)) & NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_MASK); + + NV_WRITE(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(crtc->index), NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_ON); + while((NV_READ(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(crtc->index)) & NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_MASK) + != NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_ACTIVE); + + crtc->cursor->enabled = true; + + return 0; +} + +static int nv50_cursor_disable(struct nv50_crtc *crtc) +{ + struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + + NV50_DEBUG("\n"); + + NV_WRITE(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(crtc->index), 0); + while(NV_READ(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(crtc->index)) & NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_MASK); + + crtc->cursor->enabled = false; + + return 0; +} + +/* Calling update or changing the stored cursor state is left to the higher level ioctl's. */ +static int nv50_cursor_show(struct nv50_crtc *crtc) +{ + struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + uint32_t offset = crtc->index * 0x400; + + NV50_DEBUG("\n"); + + /* Better not show the cursor when we have none. */ + /* TODO: is cursor offset actually set? */ + if (!crtc->cursor->block) { + DRM_ERROR("No cursor available on crtc %d\n", crtc->index); + return -EINVAL; + } + + OUT_MODE(NV50_CRTC0_CURSOR_CTRL + offset, NV50_CRTC0_CURSOR_CTRL_SHOW); + + return 0; +} + +static int nv50_cursor_hide(struct nv50_crtc *crtc) +{ + struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + uint32_t offset = crtc->index * 0x400; + + NV50_DEBUG("\n"); + + OUT_MODE(NV50_CRTC0_CURSOR_CTRL + offset, NV50_CRTC0_CURSOR_CTRL_HIDE); + + return 0; +} + +static int nv50_cursor_set_pos(struct nv50_crtc *crtc, int x, int y) +{ + struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + + NV_WRITE(NV50_HW_CURSOR_POS(crtc->index), ((y & 0xFFFF) << 16) | (x & 0xFFFF)); + /* Needed to make the cursor move. */ + NV_WRITE(NV50_HW_CURSOR_POS_CTRL(crtc->index), 0); + + return 0; +} + +static int nv50_cursor_set_offset(struct nv50_crtc *crtc) +{ + struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + + NV50_DEBUG("\n"); + + if (crtc->cursor->block) { + OUT_MODE(NV50_CRTC0_CURSOR_OFFSET + crtc->index * 0x400, crtc->cursor->block->start >> 8); + } else { + OUT_MODE(NV50_CRTC0_CURSOR_OFFSET + crtc->index * 0x400, 0); + } + + return 0; +} + +static int nv50_cursor_set_bo(struct nv50_crtc *crtc, drm_handle_t handle) +{ + struct mem_block *block = NULL; + struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + + NV50_DEBUG("\n"); + + block = find_block_by_handle(dev_priv->fb_heap, handle); + + if (block) { + bool first_time = false; + if (!crtc->cursor->block) + first_time = true; + + crtc->cursor->block = block; + + /* set the cursor offset cursor */ + if (first_time) { + crtc->cursor->set_offset(crtc); + if (crtc->cursor->visible) + crtc->cursor->show(crtc); + } + } else { + DRM_ERROR("Unable to find cursor bo with handle 0x%X\n", handle); + return -EINVAL; + } + + return 0; +} + +int nv50_cursor_create(struct nv50_crtc *crtc) +{ + NV50_DEBUG("\n"); + + if (!crtc) + return -EINVAL; + + crtc->cursor = kzalloc(sizeof(struct nv50_cursor), GFP_KERNEL); + if (!crtc->cursor) + return -ENOMEM; + + /* function pointers */ + crtc->cursor->show = nv50_cursor_show; + crtc->cursor->hide = nv50_cursor_hide; + crtc->cursor->set_pos = nv50_cursor_set_pos; + crtc->cursor->set_offset = nv50_cursor_set_offset; + crtc->cursor->set_bo = nv50_cursor_set_bo; + crtc->cursor->enable = nv50_cursor_enable; + crtc->cursor->disable = nv50_cursor_disable; + + return 0; +} + +int nv50_cursor_destroy(struct nv50_crtc *crtc) +{ + int rval = 0; + + NV50_DEBUG("\n"); + + if (!crtc) + return -EINVAL; + + if (crtc->cursor->enabled) { + rval = crtc->cursor->disable(crtc); + if (rval != 0) + return rval; + } + + kfree(crtc->cursor); + crtc->cursor = NULL; + + return 0; +} diff --git a/linux-core/nv50_cursor.h b/linux-core/nv50_cursor.h new file mode 100644 index 00000000..4fd0d39a --- /dev/null +++ b/linux-core/nv50_cursor.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +#ifndef __NV50_CURSOR_H__ +#define __NV50_CURSOR_H__ + +#include "nv50_display.h" + +struct nv50_crtc; + +struct nv50_cursor { + struct mem_block *block; + int x, y; + bool visible; + bool enabled; + + int (*show) (struct nv50_crtc *crtc); + int (*hide) (struct nv50_crtc *crtc); + int (*set_pos) (struct nv50_crtc *crtc, int x, int y); + int (*set_offset) (struct nv50_crtc *crtc); + int (*set_bo) (struct nv50_crtc *crtc, drm_handle_t handle); + int (*enable) (struct nv50_crtc *crtc); + int (*disable) (struct nv50_crtc *crtc); +}; + +int nv50_cursor_create(struct nv50_crtc *crtc); +int nv50_cursor_destroy(struct nv50_crtc *crtc); + +#endif /* __NV50_CURSOR_H__ */ diff --git a/linux-core/nv50_dac.c b/linux-core/nv50_dac.c new file mode 100644 index 00000000..5dddb469 --- /dev/null +++ b/linux-core/nv50_dac.c @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +#include "nv50_output.h" + +static int nv50_dac_validate_mode(struct nv50_output *output, struct nouveau_hw_mode *mode) +{ + NV50_DEBUG("\n"); + + if (mode->clock > 400000) + return MODE_CLOCK_HIGH; + + if (mode->clock < 25000) + return MODE_CLOCK_LOW; + + return MODE_OK; +} + +static int nv50_dac_execute_mode(struct nv50_output *output, bool disconnect) +{ + struct drm_nouveau_private *dev_priv = output->dev->dev_private; + struct nv50_crtc *crtc = output->crtc; + struct nouveau_hw_mode *desired_mode = NULL; + + uint32_t offset = nv50_output_or_offset(output) * 0x80; + + uint32_t mode_ctl = NV50_DAC_MODE_CTRL_OFF; + uint32_t mode_ctl2 = 0; + + NV50_DEBUG("or %d\n", nv50_output_or_offset(output)); + + if (disconnect) { + NV50_DEBUG("Disconnecting DAC\n"); + OUT_MODE(NV50_DAC0_MODE_CTRL + offset, mode_ctl); + return 0; + } + + desired_mode = (crtc->use_native_mode ? crtc->native_mode : + crtc->mode); + + if (crtc->index == 1) + mode_ctl |= NV50_DAC_MODE_CTRL_CRTC1; + else + mode_ctl |= NV50_DAC_MODE_CTRL_CRTC0; + + /* Lacking a working tv-out, this is not a 100% sure. */ + if (output->type == OUTPUT_DAC) { + mode_ctl |= 0x40; + } else if (output->type == OUTPUT_TV) { + mode_ctl |= 0x100; + } + + if (desired_mode->flags & DRM_MODE_FLAG_NHSYNC) + mode_ctl2 |= NV50_DAC_MODE_CTRL2_NHSYNC; + + if (desired_mode->flags & DRM_MODE_FLAG_NVSYNC) + mode_ctl2 |= NV50_DAC_MODE_CTRL2_NVSYNC; + + OUT_MODE(NV50_DAC0_MODE_CTRL + offset, mode_ctl); + OUT_MODE(NV50_DAC0_MODE_CTRL2 + offset, mode_ctl2); + + return 0; +} + +static int nv50_dac_set_clock_mode(struct nv50_output *output) +{ + struct drm_nouveau_private *dev_priv = output->dev->dev_private; + + NV50_DEBUG("or %d\n", nv50_output_or_offset(output)); + + NV_WRITE(NV50_PDISPLAY_DAC_CLK_CLK_CTRL2(nv50_output_or_offset(output)), 0); + + return 0; +} + +static int nv50_dac_set_power_mode(struct nv50_output *output, int mode) +{ + struct drm_nouveau_private *dev_priv = output->dev->dev_private; + uint32_t val; + int or = nv50_output_or_offset(output); + + NV50_DEBUG("or %d\n", or); + + /* wait for it to be done */ + while (NV_READ(NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(or)) & NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_PENDING); + + val = NV_READ(NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(or)) & ~0x7F; + + if (mode != DRM_MODE_DPMS_ON) + val |= NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_BLANKED; + + switch (mode) { + case DRM_MODE_DPMS_STANDBY: + val |= NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_HSYNC_OFF; + break; + case DRM_MODE_DPMS_SUSPEND: + val |= NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_VSYNC_OFF; + break; + case DRM_MODE_DPMS_OFF: + val |= NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_OFF; + val |= NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_HSYNC_OFF; + val |= NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_VSYNC_OFF; + break; + default: + break; + } + + NV_WRITE(NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(or), val | NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_PENDING); + + return 0; +} + +static int nv50_dac_detect(struct nv50_output *output) +{ + struct drm_nouveau_private *dev_priv = output->dev->dev_private; + int or = nv50_output_or_offset(output); + bool present = 0; + uint32_t dpms_state, load_pattern, load_state; + + NV_WRITE(NV50_PDISPLAY_DAC_REGS_CLK_CTRL1(or), 0x00000001); + dpms_state = NV_READ(NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(or)); + + NV_WRITE(NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(or), 0x00150000 | NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_PENDING); + while (NV_READ(NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(or)) & NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_PENDING); + + /* Use bios provided value if possible. */ + if (dev_priv->bios.dactestval) { + load_pattern = dev_priv->bios.dactestval; + NV50_DEBUG("Using bios provided load_pattern of %d\n", load_pattern); + } else { + load_pattern = 340; + NV50_DEBUG("Using default load_pattern of %d\n", load_pattern); + } + + NV_WRITE(NV50_PDISPLAY_DAC_REGS_LOAD_CTRL(or), NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_ACTIVE | load_pattern); + udelay(10000); /* give it some time to process */ + load_state = NV_READ(NV50_PDISPLAY_DAC_REGS_LOAD_CTRL(or)); + + NV_WRITE(NV50_PDISPLAY_DAC_REGS_LOAD_CTRL(or), 0); + NV_WRITE(NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(or), dpms_state); + + if ((load_state & NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_PRESENT) == NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_PRESENT) + present = 1; + + if (present) + NV50_DEBUG("Load was detected on output with or %d\n", or); + else + NV50_DEBUG("Load was not detected on output with or %d\n", or); + + return present; +} + +static int nv50_dac_destroy(struct nv50_output *output) +{ + struct drm_device *dev = output->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv50_display *display = nv50_get_display(dev); + + NV50_DEBUG("\n"); + + if (!display || !output) + return -EINVAL; + + list_del(&output->item); + + kfree(output->native_mode); + if (dev_priv->free_output) + dev_priv->free_output(output); + + return 0; +} + +int nv50_dac_create(struct drm_device *dev, int dcb_entry) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv50_output *output = NULL; + struct nv50_display *display = NULL; + struct dcb_entry *entry = NULL; + int rval = 0; + + NV50_DEBUG("\n"); + + /* This allows the public layer to do it's thing. */ + if (dev_priv->alloc_output) + output = dev_priv->alloc_output(dev); + + if (!output) + return -ENOMEM; + + output->dev = dev; + + display = nv50_get_display(dev); + if (!display) { + rval = -EINVAL; + goto out; + } + + entry = &dev_priv->dcb_table.entry[dcb_entry]; + if (!entry) { + rval = -EINVAL; + goto out; + } + + switch (entry->type) { + case DCB_OUTPUT_ANALOG: + output->type = OUTPUT_DAC; + DRM_INFO("Detected a DAC output\n"); + break; + default: + rval = -EINVAL; + goto out; + } + + output->dcb_entry = dcb_entry; + output->bus = entry->bus; + + list_add_tail(&output->item, &display->outputs); + + output->native_mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL); + if (!output->native_mode) { + rval = -ENOMEM; + goto out; + } + + /* Set function pointers. */ + output->validate_mode = nv50_dac_validate_mode; + output->execute_mode = nv50_dac_execute_mode; + output->set_clock_mode = nv50_dac_set_clock_mode; + output->set_power_mode = nv50_dac_set_power_mode; + output->detect = nv50_dac_detect; + output->destroy = nv50_dac_destroy; + + return 0; + +out: + if (output->native_mode) + kfree(output->native_mode); + if (dev_priv->free_output) + dev_priv->free_output(output); + return rval; +} + diff --git a/linux-core/nv50_display.c b/linux-core/nv50_display.c new file mode 100644 index 00000000..6665a32f --- /dev/null +++ b/linux-core/nv50_display.c @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +#include "nv50_display.h" +#include "nv50_crtc.h" +#include "nv50_output.h" +#include "nv50_connector.h" + +static int nv50_display_pre_init(struct nv50_display *display) +{ + struct drm_device *dev = display->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + int i; + uint32_t ram_amount; + + NV50_DEBUG("\n"); + + NV_WRITE(0x00610184, NV_READ(0x00614004)); + /* + * I think the 0x006101XX range is some kind of main control area that enables things. + */ + /* CRTC? */ + NV_WRITE(0x00610190 + 0 * 0x10, NV_READ(0x00616100 + 0 * 0x800)); + NV_WRITE(0x00610190 + 1 * 0x10, NV_READ(0x00616100 + 1 * 0x800)); + NV_WRITE(0x00610194 + 0 * 0x10, NV_READ(0x00616104 + 0 * 0x800)); + NV_WRITE(0x00610194 + 1 * 0x10, NV_READ(0x00616104 + 1 * 0x800)); + NV_WRITE(0x00610198 + 0 * 0x10, NV_READ(0x00616108 + 0 * 0x800)); + NV_WRITE(0x00610198 + 1 * 0x10, NV_READ(0x00616108 + 1 * 0x800)); + NV_WRITE(0x0061019c + 0 * 0x10, NV_READ(0x0061610c + 0 * 0x800)); + NV_WRITE(0x0061019c + 1 * 0x10, NV_READ(0x0061610c + 1 * 0x800)); + /* DAC */ + NV_WRITE(0x006101d0 + 0 * 0x4, NV_READ(0x0061a000 + 0 * 0x800)); + NV_WRITE(0x006101d0 + 1 * 0x4, NV_READ(0x0061a000 + 1 * 0x800)); + NV_WRITE(0x006101d0 + 2 * 0x4, NV_READ(0x0061a000 + 2 * 0x800)); + /* SOR */ + NV_WRITE(0x006101e0 + 0 * 0x4, NV_READ(0x0061c000 + 0 * 0x800)); + NV_WRITE(0x006101e0 + 1 * 0x4, NV_READ(0x0061c000 + 1 * 0x800)); + /* Something not yet in use, tv-out maybe. */ + NV_WRITE(0x006101f0 + 0 * 0x4, NV_READ(0x0061e000 + 0 * 0x800)); + NV_WRITE(0x006101f0 + 1 * 0x4, NV_READ(0x0061e000 + 1 * 0x800)); + NV_WRITE(0x006101f0 + 2 * 0x4, NV_READ(0x0061e000 + 2 * 0x800)); + + for (i = 0; i < 3; i++) { + NV_WRITE(NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(i), 0x00550000 | NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_PENDING); + NV_WRITE(NV50_PDISPLAY_DAC_REGS_CLK_CTRL1(i), 0x00000001); + } + + /* This used to be in crtc unblank, but seems out of place there. */ + NV_WRITE(NV50_PDISPLAY_UNK_380, 0); + /* RAM is clamped to 256 MiB. */ + ram_amount = nouveau_mem_fb_amount(display->dev); + NV50_DEBUG("ram_amount %d\n", ram_amount); + if (ram_amount > 256*1024*1024) + ram_amount = 256*1024*1024; + NV_WRITE(NV50_PDISPLAY_RAM_AMOUNT, ram_amount - 1); + NV_WRITE(NV50_PDISPLAY_UNK_388, 0x150000); + NV_WRITE(NV50_PDISPLAY_UNK_38C, 0); + + display->preinit_done = true; + + return 0; +} + +static int nv50_display_init(struct nv50_display *display) +{ + struct drm_device *dev = display->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + uint32_t val; + + NV50_DEBUG("\n"); + + /* The precise purpose is unknown, i suspect it has something to do with text mode. */ + if (NV_READ(NV50_PDISPLAY_SUPERVISOR) & 0x100) { + NV_WRITE(NV50_PDISPLAY_SUPERVISOR, 0x100); + NV_WRITE(0x006194e8, NV_READ(0x006194e8) & ~1); + while (NV_READ(0x006194e8) & 2); + } + + /* taken from nv bug #12637 */ + NV_WRITE(NV50_PDISPLAY_UNK200_CTRL, 0x2b00); + do { + val = NV_READ(NV50_PDISPLAY_UNK200_CTRL); + if ((val & 0x9f0000) == 0x20000) + NV_WRITE(NV50_PDISPLAY_UNK200_CTRL, val | 0x800000); + + if ((val & 0x3f0000) == 0x30000) + NV_WRITE(NV50_PDISPLAY_UNK200_CTRL, val | 0x200000); + } while (val & 0x1e0000); + + NV_WRITE(NV50_PDISPLAY_CTRL_STATE, NV50_PDISPLAY_CTRL_STATE_ENABLE); + NV_WRITE(NV50_PDISPLAY_UNK200_CTRL, 0x1000b03); + while (!(NV_READ(NV50_PDISPLAY_UNK200_CTRL) & 0x40000000)); + + /* For the moment this is just a wrapper, which should be replaced with a real fifo at some point. */ + OUT_MODE(NV50_UNK84, 0); + OUT_MODE(NV50_UNK88, 0); + OUT_MODE(NV50_CRTC0_BLANK_CTRL, NV50_CRTC0_BLANK_CTRL_BLANK); + OUT_MODE(NV50_CRTC0_UNK800, 0); + OUT_MODE(NV50_CRTC0_DISPLAY_START, 0); + OUT_MODE(NV50_CRTC0_UNK82C, 0); + + /* enable clock change interrupts. */ + NV_WRITE(NV50_PDISPLAY_SUPERVISOR_INTR, NV_READ(NV50_PDISPLAY_SUPERVISOR_INTR) | 0x70); + + /* enable hotplug interrupts */ + NV_WRITE(NV50_PCONNECTOR_HOTPLUG_INTR, 0x7FFF7FFF); + + display->init_done = true; + + return 0; +} + +static int nv50_display_disable(struct nv50_display *display) +{ + struct drm_device *dev = display->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv50_crtc *crtc = NULL; + int i; + + NV50_DEBUG("\n"); + + list_for_each_entry(crtc, &display->crtcs, item) { + crtc->blank(crtc, true); + } + + display->update(display); + + /* Almost like ack'ing a vblank interrupt, maybe in the spirit of cleaning up? */ + list_for_each_entry(crtc, &display->crtcs, item) { + if (crtc->enabled) { + uint32_t mask; + + if (crtc->index == 1) + mask = NV50_PDISPLAY_SUPERVISOR_CRTC1; + else + mask = NV50_PDISPLAY_SUPERVISOR_CRTC0; + + NV_WRITE(NV50_PDISPLAY_SUPERVISOR, mask); + while (!(NV_READ(NV50_PDISPLAY_SUPERVISOR) & mask)); + } + } + + NV_WRITE(NV50_PDISPLAY_UNK200_CTRL, 0); + NV_WRITE(NV50_PDISPLAY_CTRL_STATE, 0); + while ((NV_READ(NV50_PDISPLAY_UNK200_CTRL) & 0x1e0000) != 0); + + for (i = 0; i < 2; i++) { + while (NV_READ(NV50_PDISPLAY_SOR_REGS_DPMS_STATE(i)) & NV50_PDISPLAY_SOR_REGS_DPMS_STATE_WAIT); + } + + /* disable clock change interrupts. */ + NV_WRITE(NV50_PDISPLAY_SUPERVISOR_INTR, NV_READ(NV50_PDISPLAY_SUPERVISOR_INTR) & ~0x70); + + /* disable hotplug interrupts */ + NV_WRITE(NV50_PCONNECTOR_HOTPLUG_INTR, 0); + + display->init_done = false; + + return 0; +} + +static int nv50_display_update(struct nv50_display *display) +{ + struct drm_device *dev = display->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + + NV50_DEBUG("\n"); + + OUT_MODE(NV50_UPDATE_DISPLAY, 0); + + return 0; +} + +int nv50_display_create(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv50_display *display = kzalloc(sizeof(struct nv50_display), GFP_KERNEL); + int i, type, output_index, bus; + /* DAC0, DAC1, DAC2, SOR0, SOR1*/ + int or_counter[5] = {0, 0, 0, 0, 0}; + int i2c_index[5] = {0, 0, 0, 0, 0}; + uint32_t bus_mask = 0; + uint32_t bus_digital = 0, bus_analog = 0; + + NV50_DEBUG("\n"); + + if (!display) + return -ENOMEM; + + INIT_LIST_HEAD(&display->crtcs); + INIT_LIST_HEAD(&display->outputs); + INIT_LIST_HEAD(&display->connectors); + + dev_priv->display_priv = display; + + for (i = 0; i < 2; i++) { + nv50_crtc_create(dev, i); + } + + /* we setup the outputs up from the BIOS table */ + for (i = 0 ; i < dev_priv->dcb_table.entries; i++) { + type = dev_priv->dcb_table.entry[i].type; + output_index = ffs(dev_priv->dcb_table.entry[i].or) - 1; + bus = dev_priv->dcb_table.entry[i].bus; + + switch (type) { + case DCB_OUTPUT_TMDS: + case DCB_OUTPUT_LVDS: + or_counter[output_index + 3] += 1; + i2c_index[output_index + 3] = dev_priv->dcb_table.entry[i].i2c_index; + bus_digital |= (1 << bus); + nv50_sor_create(dev, i); + break; + case DCB_OUTPUT_ANALOG: + or_counter[output_index] += 1; + i2c_index[output_index] = dev_priv->dcb_table.entry[i].i2c_index; + bus_analog |= (1 << bus); + nv50_dac_create(dev, i); + break; + default: + break; + } + + } + + /* setup the connectors based on the output tables. */ + for (i = 0 ; i < dev_priv->dcb_table.entries; i++) { + int connector_type = 0; + type = dev_priv->dcb_table.entry[i].type; + bus = dev_priv->dcb_table.entry[i].bus; + + /* already done? */ + if (bus_mask & (1 << bus)) + continue; + + /* only do it for supported outputs */ + if (type != DCB_OUTPUT_ANALOG && type != DCB_OUTPUT_TMDS + && type != DCB_OUTPUT_LVDS) + continue; + + switch (type) { + case DCB_OUTPUT_TMDS: + case DCB_OUTPUT_ANALOG: + if ((bus_digital & (1 << bus)) && (bus_analog & (1 << bus))) + connector_type = CONNECTOR_DVI_I; + else if (bus_digital & (1 << bus)) + connector_type = CONNECTOR_DVI_D; + else if (bus_analog & (1 << bus)) + connector_type = CONNECTOR_VGA; + break; + case DCB_OUTPUT_LVDS: + connector_type = CONNECTOR_LVDS; + break; + default: + connector_type = CONNECTOR_UNKNOWN; + break; + } + + if (connector_type == CONNECTOR_UNKNOWN) + continue; + + nv50_connector_create(dev, bus, dev_priv->dcb_table.entry[i].i2c_index, connector_type); + + bus_mask |= (1 << bus); + } + + display->dev = dev; + + /* function pointers */ + display->init = nv50_display_init; + display->pre_init = nv50_display_pre_init; + display->disable = nv50_display_disable; + display->update = nv50_display_update; + + return 0; +} + +int nv50_display_destroy(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv50_display *display = nv50_get_display(dev); + struct nv50_crtc *crtc = NULL; + struct nv50_output *output = NULL; + struct nv50_connector *connector = NULL; + + NV50_DEBUG("\n"); + + if (display->init_done) + display->disable(display); + + list_for_each_entry(connector, &display->connectors, item) { + connector->destroy(connector); + } + + list_for_each_entry(output, &display->outputs, item) { + output->destroy(output); + } + + list_for_each_entry(crtc, &display->crtcs, item) { + crtc->destroy(crtc); + } + + kfree(display); + dev_priv->display_priv = NULL; + + return 0; +} + +/* This can be replaced with a real fifo in the future. */ +void nv50_display_command(struct drm_nouveau_private *dev_priv, uint32_t mthd, uint32_t val) +{ + uint32_t counter = 0; + +#if 1 + DRM_INFO("mthd 0x%03X val 0x%08X\n", mthd, val); +#endif + + NV_WRITE(NV50_PDISPLAY_CTRL_VAL, val); + NV_WRITE(NV50_PDISPLAY_CTRL_STATE, NV50_PDISPLAY_CTRL_STATE_PENDING | 0x10000 | mthd | NV50_PDISPLAY_CTRL_STATE_ENABLE); + + while (NV_READ(NV50_PDISPLAY_CTRL_STATE) & NV50_PDISPLAY_CTRL_STATE_PENDING) { + counter++; + if (counter > 1000000) { + DRM_ERROR("You probably need a reboot now\n"); + break; + } + udelay(1); + } +} + +struct nv50_display *nv50_get_display(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + + return (struct nv50_display *) dev_priv->display_priv; +} diff --git a/linux-core/nv50_display.h b/linux-core/nv50_display.h new file mode 100644 index 00000000..3c2ee1c9 --- /dev/null +++ b/linux-core/nv50_display.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +#ifndef __NV50_DISPLAY_H__ +#define __NV50_DISPLAY_H__ + +#include "drmP.h" +#include "drm.h" +#include "nouveau_dma.h" +#include "nouveau_drv.h" +#include "nouveau_reg.h" +#include "nv50_display_commands.h" + +/* for convience, so you can see through the trees. */ +#define NV50_DEBUG DRM_ERROR + +struct nouveau_hw_mode { + unsigned int clock; + unsigned short hdisplay, hblank_start, hsync_start, hsync_end, hblank_end, htotal; + unsigned short vdisplay, vblank_start, vsync_start, vsync_end, vblank_end, vtotal; + + unsigned int flags; +}; + +struct nv50_crtc; +struct nv50_output; +struct nv50_connector; + +struct nv50_display { + struct drm_device *dev; + + bool preinit_done; + bool init_done; + + int last_crtc; /* crtc used for last mode set */ + + int (*pre_init) (struct nv50_display *display); + int (*init) (struct nv50_display *display); + int (*disable) (struct nv50_display *display); + int (*update) (struct nv50_display *display); + + struct list_head crtcs; + struct list_head outputs; + struct list_head connectors; +}; + +enum scaling_modes { + SCALE_NON_GPU, + SCALE_FULLSCREEN, + SCALE_ASPECT, + SCALE_NOSCALE, + SCALE_INVALID +}; + +void nv50_display_command(struct drm_nouveau_private *dev_priv, uint32_t mthd, uint32_t val); +struct nv50_display *nv50_get_display(struct drm_device *dev); +int nv50_display_create(struct drm_device *dev); +int nv50_display_destroy(struct drm_device *dev); + +#endif /* __NV50_DISPLAY_H__ */ diff --git a/linux-core/nv50_display_commands.h b/linux-core/nv50_display_commands.h new file mode 100644 index 00000000..97b3d3c1 --- /dev/null +++ b/linux-core/nv50_display_commands.h @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +/* copied from ddx definitions, until rules-ng can handle this */ + +#define NV50_UPDATE_DISPLAY 0x80 +#define NV50_UNK84 0x84 +#define NV50_UNK88 0x88 + +#define NV50_DAC0_MODE_CTRL 0x400 + #define NV50_DAC_MODE_CTRL_OFF (0 << 0) + #define NV50_DAC_MODE_CTRL_CRTC0 (1 << 0) + #define NV50_DAC_MODE_CTRL_CRTC1 (1 << 1) +#define NV50_DAC1_MODE_CTRL 0x480 +#define NV50_DAC2_MODE_CTRL 0x500 + +#define NV50_DAC0_MODE_CTRL2 0x404 + #define NV50_DAC_MODE_CTRL2_NHSYNC (1 << 0) + #define NV50_DAC_MODE_CTRL2_NVSYNC (2 << 0) +#define NV50_DAC1_MODE_CTRL2 0x484 +#define NV50_DAC2_MODE_CTRL2 0x504 + +#define NV50_SOR0_MODE_CTRL 0x600 + #define NV50_SOR_MODE_CTRL_OFF (0 << 0) + #define NV50_SOR_MODE_CTRL_CRTC0 (1 << 0) + #define NV50_SOR_MODE_CTRL_CRTC1 (1 << 1) + #define NV50_SOR_MODE_CTRL_LVDS (0 << 8) + #define NV50_SOR_MODE_CTRL_TMDS (1 << 8) + #define NV50_SOR_MODE_CTRL_TMDS_DUAL_LINK (4 << 8) + #define NV50_SOR_MODE_CTRL_NHSYNC (1 << 12) + #define NV50_SOR_MODE_CTRL_NVSYNC (2 << 12) +#define NV50_SOR1_MODE_CTRL 0x640 + +#define NV50_CRTC0_UNK800 0x800 +#define NV50_CRTC0_CLOCK 0x804 +#define NV50_CRTC0_INTERLACE 0x808 + +/* 0x810 is a reasonable guess, nothing more. */ +#define NV50_CRTC0_DISPLAY_START 0x810 +#define NV50_CRTC0_DISPLAY_TOTAL 0x814 +#define NV50_CRTC0_SYNC_DURATION 0x818 +#define NV50_CRTC0_SYNC_START_TO_BLANK_END 0x81C +#define NV50_CRTC0_MODE_UNK1 0x820 +#define NV50_CRTC0_MODE_UNK2 0x824 + +#define NV50_CRTC0_UNK82C 0x82C + +/* You can't have a palette in 8 bit mode (=OFF) */ +#define NV50_CRTC0_CLUT_MODE 0x840 + #define NV50_CRTC0_CLUT_MODE_BLANK 0x00000000 + #define NV50_CRTC0_CLUT_MODE_OFF 0x80000000 + #define NV50_CRTC0_CLUT_MODE_ON 0xC0000000 +#define NV50_CRTC0_CLUT_OFFSET 0x844 + +/* Anyone know what part of the chip is triggered here precisely? */ +#define NV84_CRTC0_BLANK_UNK1 0x85C + #define NV84_CRTC0_BLANK_UNK1_BLANK 0x0 + #define NV84_CRTC0_BLANK_UNK1_UNBLANK 0x1 + +#define NV50_CRTC0_FB_OFFSET 0x860 + +#define NV50_CRTC0_FB_SIZE 0x868 +#define NV50_CRTC0_FB_PITCH 0x86C + +#define NV50_CRTC0_DEPTH 0x870 + #define NV50_CRTC0_DEPTH_8BPP 0x1E00 + #define NV50_CRTC0_DEPTH_15BPP 0xE900 + #define NV50_CRTC0_DEPTH_16BPP 0xE800 + #define NV50_CRTC0_DEPTH_24BPP 0xCF00 + +/* I'm openminded to better interpretations. */ +/* This is an educated guess. */ +/* NV50 has RAMDAC and TMDS offchip, so it's unlikely to be that. */ +#define NV50_CRTC0_BLANK_CTRL 0x874 + #define NV50_CRTC0_BLANK_CTRL_BLANK 0x0 + #define NV50_CRTC0_BLANK_CTRL_UNBLANK 0x1 + +#define NV50_CRTC0_CURSOR_CTRL 0x880 + #define NV50_CRTC0_CURSOR_CTRL_SHOW 0x85000000 + #define NV50_CRTC0_CURSOR_CTRL_HIDE 0x05000000 + +#define NV50_CRTC0_CURSOR_OFFSET 0x884 + +/* Anyone know what part of the chip is triggered here precisely? */ +#define NV84_CRTC0_BLANK_UNK2 0x89C + #define NV84_CRTC0_BLANK_UNK2_BLANK 0x0 + #define NV84_CRTC0_BLANK_UNK2_UNBLANK 0x1 + +#define NV50_CRTC0_DITHERING_CTRL 0x8A0 + #define NV50_CRTC0_DITHERING_CTRL_ON 0x11 + #define NV50_CRTC0_DITHERING_CTRL_OFF 0x0 + +#define NV50_CRTC0_SCALE_CTRL 0x8A4 + #define NV50_CRTC0_SCALE_CTRL_SCALER_INACTIVE (0 << 0) + /* It doesn't seem to be needed, hence i wonder what it does precisely. */ + #define NV50_CRTC0_SCALE_CTRL_SCALER_ACTIVE (9 << 0) +#define NV50_CRTC0_COLOR_CTRL 0x8A8 + #define NV50_CRTC_COLOR_CTRL_MODE_COLOR (4 << 16) + +#define NV50_CRTC0_FB_POS 0x8C0 +#define NV50_CRTC0_REAL_RES 0x8C8 + +/* Added a macro, because the signed stuff can cause you problems very quickly. */ +#define NV50_CRTC0_SCALE_CENTER_OFFSET 0x8D4 + #define NV50_CRTC_SCALE_CENTER_OFFSET_VAL(x, y) ((((unsigned)y << 16) & 0xFFFF0000) | (((unsigned)x) & 0x0000FFFF)) +/* Both of these are needed, otherwise nothing happens. */ +#define NV50_CRTC0_SCALE_RES1 0x8D8 +#define NV50_CRTC0_SCALE_RES2 0x8DC + +#define NV50_CRTC1_UNK800 0xC00 +#define NV50_CRTC1_CLOCK 0xC04 +#define NV50_CRTC1_INTERLACE 0xC08 + +/* 0xC10 is a reasonable guess, nothing more. */ +#define NV50_CRTC1_DISPLAY_START 0xC10 +#define NV50_CRTC1_DISPLAY_TOTAL 0xC14 +#define NV50_CRTC1_SYNC_DURATION 0xC18 +#define NV50_CRTC1_SYNC_START_TO_BLANK_END 0xC1C +#define NV50_CRTC1_MODE_UNK1 0xC20 +#define NV50_CRTC1_MODE_UNK2 0xC24 + +#define NV50_CRTC1_CLUT_MODE 0xC40 + #define NV50_CRTC1_CLUT_MODE_BLANK 0x00000000 + #define NV50_CRTC1_CLUT_MODE_OFF 0x80000000 + #define NV50_CRTC1_CLUT_MODE_ON 0xC0000000 +#define NV50_CRTC1_CLUT_OFFSET 0xC44 + +/* Anyone know what part of the chip is triggered here precisely? */ +#define NV84_CRTC1_BLANK_UNK1 0xC5C + #define NV84_CRTC1_BLANK_UNK1_BLANK 0x0 + #define NV84_CRTC1_BLANK_UNK1_UNBLANK 0x1 + +#define NV50_CRTC1_FB_OFFSET 0xC60 + +#define NV50_CRTC1_FB_SIZE 0xC68 +#define NV50_CRTC1_FB_PITCH 0xC6C + +#define NV50_CRTC1_DEPTH 0xC70 + #define NV50_CRTC1_DEPTH_8BPP 0x1E00 + #define NV50_CRTC1_DEPTH_15BPP 0xE900 + #define NV50_CRTC1_DEPTH_16BPP 0xE800 + #define NV50_CRTC1_DEPTH_24BPP 0xCF00 + +/* I'm openminded to better interpretations. */ +#define NV50_CRTC1_BLANK_CTRL 0xC74 + #define NV50_CRTC1_BLANK_CTRL_BLANK 0x0 + #define NV50_CRTC1_BLANK_CTRL_UNBLANK 0x1 + +#define NV50_CRTC1_CURSOR_CTRL 0xC80 + #define NV50_CRTC1_CURSOR_CTRL_SHOW 0x85000000 + #define NV50_CRTC1_CURSOR_CTRL_HIDE 0x05000000 + +#define NV50_CRTC1_CURSOR_OFFSET 0xC84 + +/* Anyone know what part of the chip is triggered here precisely? */ +#define NV84_CRTC1_BLANK_UNK2 0xC9C + #define NV84_CRTC1_BLANK_UNK2_BLANK 0x0 + #define NV84_CRTC1_BLANK_UNK2_UNBLANK 0x1 + +#define NV50_CRTC1_DITHERING_CTRL 0xCA0 + #define NV50_CRTC1_DITHERING_CTRL_ON 0x11 + #define NV50_CRTC1_DITHERING_CTRL_OFF 0x0 + +#define NV50_CRTC1_SCALE_CTRL 0xCA4 +#define NV50_CRTC1_COLOR_CTRL 0xCA8 + +#define NV50_CRTC1_FB_POS 0xCC0 +#define NV50_CRTC1_REAL_RES 0xCC8 + +#define NV50_CRTC1_SCALE_CENTER_OFFSET 0xCD4 +/* Both of these are needed, otherwise nothing happens. */ +#define NV50_CRTC1_SCALE_RES1 0xCD8 +#define NV50_CRTC1_SCALE_RES2 0xCDC diff --git a/linux-core/nv50_fb.c b/linux-core/nv50_fb.c new file mode 100644 index 00000000..153899da --- /dev/null +++ b/linux-core/nv50_fb.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +#include "nv50_fb.h" +#include "nv50_lut.h" +#include "nv50_crtc.h" +#include "nv50_display.h" + +static int nv50_fb_bind(struct nv50_crtc *crtc, struct nv50_fb_info *info) +{ + int rval = 0; + + NV50_DEBUG("\n"); + + if (!crtc || !info) { + DRM_ERROR("crtc %p info %p\n",crtc, info); + return -EINVAL; + } + + if (!info->block || !info->width || !info->height || !info->depth || !info->bpp || !info->pitch) { + DRM_ERROR("block %p width %d height %d depth %d bpp %d pitch %d\n", info->block, info->width, + info->height, info->depth, info->bpp, info->pitch); + return -EINVAL; + } + + crtc->fb->block = info->block; + crtc->fb->width = info->width; + crtc->fb->height = info->height; + + crtc->fb->y = info->x; + crtc->fb->x = info->y; + + crtc->fb->depth = info->depth; + crtc->fb->bpp = info->bpp; + + crtc->fb->pitch = info->pitch; + + /* update lut if needed */ + if (crtc->fb->depth != crtc->lut->depth) { + int r_size = 0, g_size = 0, b_size = 0; + uint16_t *r_val, *g_val, *b_val; + int i; + + switch (crtc->fb->depth) { + case 15: + r_size = 32; + g_size = 32; + b_size = 32; + break; + case 16: + r_size = 32; + g_size = 64; + b_size = 32; + break; + case 24: + default: + r_size = 256; + g_size = 256; + b_size = 256; + break; + } + + r_val = kmalloc(r_size * sizeof(uint16_t), GFP_KERNEL); + g_val = kmalloc(g_size * sizeof(uint16_t), GFP_KERNEL); + b_val = kmalloc(b_size * sizeof(uint16_t), GFP_KERNEL); + + if (!r_val || !g_val || !b_val) + return -ENOMEM; + + /* Set the color indices. */ + for (i = 0; i < r_size; i++) { + r_val[i] = i << 8; + } + for (i = 0; i < g_size; i++) { + g_val[i] = i << 8; + } + for (i = 0; i < b_size; i++) { + b_val[i] = i << 8; + } + + rval = crtc->lut->set(crtc, r_val, g_val, b_val); + + /* free before returning */ + kfree(r_val); + kfree(g_val); + kfree(b_val); + + if (rval != 0) + return rval; + } + + return 0; +} + +int nv50_fb_create(struct nv50_crtc *crtc) +{ + if (!crtc) + return -EINVAL; + + crtc->fb = kzalloc(sizeof(struct nv50_fb), GFP_KERNEL); + + if (!crtc->fb) + return -ENOMEM; + + crtc->fb->bind = nv50_fb_bind; + + return 0; +} + +int nv50_fb_destroy(struct nv50_crtc *crtc) +{ + if (!crtc) + return -EINVAL; + + kfree(crtc->fb); + crtc->fb = NULL; + + return 0; +} diff --git a/linux-core/nv50_fb.h b/linux-core/nv50_fb.h new file mode 100644 index 00000000..3051dc5c --- /dev/null +++ b/linux-core/nv50_fb.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +#ifndef __NV50_FB_H__ +#define __NV50_FB_H__ + +#include "nv50_display.h" + +struct nv50_crtc; + +struct nv50_fb_info { + struct mem_block *block; + int width, height; + int bpp, depth; + int pitch; + int x,y; +}; + +struct nv50_fb { + struct mem_block *block; + int width, height; + int bpp, depth; + int pitch; + + int x,y; + + /* function points */ + int (*bind) (struct nv50_crtc *crtc, struct nv50_fb_info *info); +}; + +int nv50_fb_create(struct nv50_crtc *crtc); +int nv50_fb_destroy(struct nv50_crtc *crtc); + +#endif /* __NV50_FB_H__ */ diff --git a/linux-core/nv50_fbcon.c b/linux-core/nv50_fbcon.c new file mode 100644 index 00000000..3dd73062 --- /dev/null +++ b/linux-core/nv50_fbcon.c @@ -0,0 +1,627 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +#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 "nv50_fbcon.h" + +static int nv50_fbcon_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) +{ + struct nv50_fbcon_par *par = info->par; + struct drm_device *dev = par->dev; + struct drm_framebuffer *drm_fb; + + list_for_each_entry(drm_fb, &dev->mode_config.fb_kernel_list, filp_head) { + if (regno > 255) + return 1; + + /* TODO: 8 bit support */ + if (regno < 16) { + switch (drm_fb->depth) { + case 15: + drm_fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) | + ((green & 0xf800) >> 6) | + ((blue & 0xf800) >> 11); + break; + case 16: + drm_fb->pseudo_palette[regno] = (red & 0xf800) | + ((green & 0xfc00) >> 5) | + ((blue & 0xf800) >> 11); + break; + case 24: + case 32: + drm_fb->pseudo_palette[regno] = ((red & 0xff00) << 8) | + (green & 0xff00) | + ((blue & 0xff00) >> 8); + break; + } + } + } + return 0; +} + +static int nv50_fbcon_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct nv50_fbcon_par *par = info->par; + struct drm_framebuffer *drm_fb = par->fb; + int depth; + + NV50_DEBUG("\n"); + + if (!var || !drm_fb || !info) { + DRM_ERROR("No var, drm_fb or info\n"); + } + + par->use_preferred_mode = false; + + if (var->pixclock == -1 || !var->pixclock) { + DRM_INFO("Using preferred mode.\n"); + par->use_preferred_mode = true; + } + + /* Need to resize the fb object !!! */ + if (var->xres > drm_fb->width || var->yres > drm_fb->height) { + DRM_ERROR("Requested width/height is greater than current fb object %dx%d > %dx%d\n", var->xres,var->yres, drm_fb->width, drm_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 15: + var->red.offset = 10; + var->green.offset = 5; + var->blue.offset = 0; + var->red.length = 5; + var->green.length = 5; + var->blue.length = 5; + var->transp.length = 1; + var->transp.offset = 15; + break; + case 16: + var->red.offset = 11; + var->green.offset = 5; + var->blue.offset = 0; + var->red.length = 5; + var->green.length = 6; + var->blue.length = 5; + var->transp.length = 0; + var->transp.offset = 0; + break; + case 24: + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 0; + var->transp.offset = 0; + break; + case 32: + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 8; + var->transp.offset = 24; + break; + default: + DRM_ERROR("Invalid depth %d\n", depth); + return -EINVAL; + } + + return 0; +} + +static int nv50_fbcon_set_par(struct fb_info *info) +{ + struct nv50_fbcon_par *par; + struct drm_framebuffer *drm_fb; + struct drm_connector *drm_connector; + struct drm_crtc *drm_crtc; + struct fb_var_screeninfo *var; + struct drm_display_mode *drm_mode = NULL, *t; + struct drm_device *dev; + int rval; + bool crtc_used[2] = {false, false}; + + NV50_DEBUG("\n"); + + if (!info) { + DRM_ERROR("No fb_info\n"); + return -EINVAL; + } + + par = info->par; + + if (!par) { + DRM_ERROR("No nv50_fbcon_par\n"); + return -EINVAL; + } + + drm_fb = par->fb; + var = &info->var; + dev = par->dev; + + if (!drm_fb || !var || !dev) { + DRM_ERROR("No drm_fb, var or dev\n"); + return -EINVAL; + } + + par->use_preferred_mode = false; + + if (var->pixclock == -1 || !var->pixclock) { + DRM_INFO("Using preferred mode.\n"); + par->use_preferred_mode = true; + } + + /* FB setup */ + switch (var->bits_per_pixel) { + case 16: + drm_fb->depth = (var->green.length == 6) ? 16 : 15; + break; + case 32: + drm_fb->depth = (var->transp.length > 0) ? 32 : 24; + break; + default: + drm_fb->depth = var->bits_per_pixel; + break; + } + + drm_fb->bits_per_pixel = var->bits_per_pixel; + + info->fix.line_length = drm_fb->pitch; + info->fix.smem_len = info->fix.line_length * drm_fb->height; + /* ignoring 8bpp for the moment */ + info->fix.visual = FB_VISUAL_TRUECOLOR; + + info->screen_size = info->fix.smem_len; /* ??? */ + + /* create a drm mode */ + if (!par->use_preferred_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 ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; + drm_mode->flags |= var->sync & FB_SYNC_VERT_HIGH_ACT ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; + + drm_mode_set_name(drm_mode); + drm_mode_set_crtcinfo(drm_mode, CRTC_INTERLACE_HALVE_V); + } + + /* hook up crtc's */ + list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) { + enum drm_connector_status status; + struct drm_mode_set mode_set; + int crtc_count = 0; + + status = drm_connector->funcs->detect(drm_connector); + + if (status != connector_status_connected) + continue; + + memset(&mode_set, 0, sizeof(struct drm_mode_set)); + + /* set connector */ + mode_set.num_connectors = 1; + mode_set.connectors = kzalloc(sizeof(struct drm_connector *), GFP_KERNEL); + if (!mode_set.connectors) { + rval = -ENOMEM; + goto out; + } + mode_set.connectors[0] = drm_connector; + + /* set fb */ + list_for_each_entry(drm_fb, &dev->mode_config.fb_kernel_list, filp_head) { + break; /* first entry is the only entry */ + } + mode_set.fb = drm_fb; + + /* set mode */ + if (par->use_preferred_mode) { + /* find preferred mode */ + list_for_each_entry_safe(drm_mode, t, &drm_connector->modes, head) { + if (drm_mode->type & DRM_MODE_TYPE_PREFERRED) + break; + } + } + mode_set.mode = drm_mode; + + /* choose crtc it already has, if possible */ + if (drm_connector->encoder) { + struct drm_encoder *drm_encoder = drm_connector->encoder; + + if (drm_encoder->crtc) { + list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) { + if (drm_crtc == drm_encoder->crtc) { + if (!crtc_used[crtc_count]) /* still available? */ + mode_set.crtc = drm_crtc; + break; + } + + crtc_count++; + } + } + } + + /* proceed as planned */ + if (mode_set.crtc) { + mode_set.crtc->funcs->set_config(&mode_set); + crtc_used[crtc_count] = true; + } + + if (!mode_set.crtc) { + crtc_count = 0; /* reset */ + + /* choose a "random" crtc */ + list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) { + if (crtc_used[crtc_count]) { + crtc_count++; + continue; + } + + /* found a crtc */ + mode_set.crtc = drm_crtc; + + break; + } + + /* proceed as planned */ + if (mode_set.crtc) { + mode_set.crtc->funcs->set_config(&mode_set); + crtc_used[crtc_count] = true; + } + } + + kfree(mode_set.connectors); + } + + return 0; + +out: + return rval; +} + +static struct fb_ops nv50_fb_ops = { + .owner = THIS_MODULE, + //.fb_open = nv50_fb_open, + //.fb_read = nv50_fb_read, + //.fb_write = nv50_fb_write, + //.fb_release = nv50_fb_release, + //.fb_ioctl = nv50_fb_ioctl, + .fb_check_var = nv50_fbcon_check_var, + .fb_set_par = nv50_fbcon_set_par, + .fb_setcolreg = nv50_fbcon_setcolreg, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + //.fb_pan_display = nv50_fb_pan_display, +}; + +static int nv50_fbcon_initial_config(struct drm_device *dev) +{ + struct drm_connector *drm_connector; + + struct drm_framebuffer *drm_fb = NULL; + struct drm_mode_fb_cmd drm_fb_cmd; + enum drm_connector_status status; + uint32_t max_width = 0, max_height = 0, pitch = 0; + struct mem_block *block; + struct drm_file *file_priv; + uint32_t flags; + int rval = 0; + + list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) { + status = drm_connector->funcs->detect(drm_connector); + + /* find the framebuffer size */ + if (status == connector_status_connected) { + struct drm_display_mode *mode, *t; + list_for_each_entry_safe(mode, t, &drm_connector->modes, head) { + if (mode->type & DRM_MODE_TYPE_PREFERRED) { + if (mode->hdisplay > max_width) + max_width = mode->hdisplay; + if (mode->vdisplay > max_height) + max_height = mode->vdisplay; + } + } + } + } + + /* allocate framebuffer */ + file_priv = kzalloc(sizeof(struct drm_file), GFP_KERNEL); + if (!file_priv) { + rval = -ENOMEM; + goto out; + } + + pitch = (max_width + 63) & ~63; + pitch *= 4; /* TODO */ + + flags = NOUVEAU_MEM_FB | NOUVEAU_MEM_MAPPED; + + /* Any file_priv should do as it's pointer is used as identification. */ + block = nouveau_mem_alloc(dev, 0, pitch * max_height, flags, file_priv); + if (!block) { + rval = -ENOMEM; + goto out; + } + + memset(&drm_fb_cmd, 0, sizeof(struct drm_mode_fb_cmd)); + + drm_fb_cmd.width = max_width; + drm_fb_cmd.height = max_height; + drm_fb_cmd.pitch = pitch; + drm_fb_cmd.bpp = 32; /* TODO */ + drm_fb_cmd.handle = block->map_handle; + drm_fb_cmd.depth = 24; /* TODO */ + + drm_fb = dev->mode_config.funcs->fb_create(dev, file_priv, &drm_fb_cmd); + if (!drm_fb) { + rval = -EINVAL; + goto out; + } + + list_add(&drm_fb->filp_head, &dev->mode_config.fb_kernel_list); + + return 0; + +out: + if (file_priv) + kfree(file_priv); + if (drm_fb) + drm_fb->funcs->destroy(drm_fb); + + return rval; +} + +/* + * Single framebuffer, ideally operating in clone mode across various connectors. + */ +int nv50_fbcon_init(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct fb_info *info; + struct nv50_fbcon_par *par; + struct device *device = &dev->pdev->dev; + struct drm_framebuffer *drm_fb; + struct mem_block *block; + void __iomem *fb = NULL; + int rval; + + rval = nv50_fbcon_initial_config(dev); + if (rval != 0) { + DRM_ERROR("nv50_fbcon_initial_config failed\n"); + return rval; + } + + list_for_each_entry(drm_fb, &dev->mode_config.fb_kernel_list, filp_head) { + break; /* first entry is the only entry */ + } + + if (!drm_fb) { + DRM_ERROR("no drm_fb found\n"); + return -EINVAL; + } + + block = find_block_by_handle(dev_priv->fb_heap, drm_fb->mm_handle); + if (!block) { + DRM_ERROR("can't find mem_block\n"); + return -EINVAL; + } + + info = framebuffer_alloc(sizeof(struct nv50_fbcon_par), device); + if (!info) { + DRM_ERROR("framebuffer_alloc failed\n"); + return -EINVAL; + } + + par = info->par; + + strcpy(info->fix.id, "nv50drmfb"); + info->fix.type = FB_TYPE_PACKED_PIXELS; + info->fix.visual = FB_VISUAL_TRUECOLOR; + info->fix.type_aux = 0; + info->fix.xpanstep = 0; /* 1 is doing it in hw */ + info->fix.ypanstep = 0; + info->fix.ywrapstep = 0; + info->fix.accel = FB_ACCEL_NONE; + info->fix.type_aux = 0; + + info->flags = FBINFO_DEFAULT; + + info->fbops = &nv50_fb_ops; + + info->fix.line_length = drm_fb->pitch; + info->fix.smem_start = dev_priv->fb_phys + block->start; + info->fix.smem_len = info->fix.line_length * drm_fb->height; + + info->flags = FBINFO_DEFAULT; + + fb = ioremap(dev_priv->fb_phys + block->start, block->size); + if (!fb) { + DRM_ERROR("Unable to ioremap framebuffer\n"); + return -EINVAL; + } + + info->screen_base = fb; + info->screen_size = info->fix.smem_len; /* FIXME */ + + info->pseudo_palette = drm_fb->pseudo_palette; + info->var.xres_virtual = drm_fb->width; + info->var.yres_virtual = drm_fb->height; + info->var.bits_per_pixel = drm_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; + + /* TODO: improve this */ + info->var.xres = drm_fb->width; + info->var.yres = drm_fb->height; + + info->fix.mmio_start = drm_get_resource_start(dev, 0); + info->fix.mmio_len = drm_get_resource_len(dev, 0); + + DRM_DEBUG("fb depth is %d\n", drm_fb->depth); + DRM_DEBUG(" pitch is %d\n", drm_fb->pitch); + + switch(drm_fb->depth) { + case 15: + info->var.red.offset = 10; + info->var.green.offset = 5; + info->var.blue.offset = 0; + info->var.red.length = 5; + info->var.green.length = 5; + info->var.blue.length = 5; + info->var.transp.offset = 15; + info->var.transp.length = 1; + break; + case 16: + info->var.red.offset = 11; + info->var.green.offset = 5; + info->var.blue.offset = 0; + info->var.red.length = 5; + info->var.green.length = 6; + info->var.blue.length = 5; + info->var.transp.offset = 0; + break; + case 24: + info->var.red.offset = 16; + info->var.green.offset = 8; + info->var.blue.offset = 0; + info->var.red.length = 8; + info->var.green.length = 8; + info->var.blue.length = 8; + info->var.transp.offset = 0; + info->var.transp.length = 0; + break; + case 32: + info->var.red.offset = 16; + info->var.green.offset = 8; + info->var.blue.offset = 0; + info->var.red.length = 8; + info->var.green.length = 8; + info->var.blue.length = 8; + info->var.transp.offset = 24; + info->var.transp.length = 8; + break; + default: + break; + } + + drm_fb->fbdev = info; + par->dev = dev; + par->fb = drm_fb; + + register_framebuffer(info); + + DRM_INFO("nv50drmfb initialised\n"); + + return 0; +} + +int nv50_fbcon_destroy(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct drm_framebuffer *drm_fb; + struct fb_info *info; + struct mem_block *block; + struct drm_file *file_priv; + + list_for_each_entry(drm_fb, &dev->mode_config.fb_kernel_list, filp_head) { + break; /* first entry is the only entry */ + } + + if (!drm_fb) { + DRM_ERROR("No framebuffer to destroy\n"); + return -EINVAL; + } + + info = drm_fb->fbdev; + if (!info) { + DRM_ERROR("No fb_info\n"); + return -EINVAL; + } + + unregister_framebuffer(info); + + block = find_block_by_handle(dev_priv->fb_heap, drm_fb->mm_handle); + if (!block) { + DRM_ERROR("can't find mem_block\n"); + return -EINVAL; + } + + /* we need to free this after memory is freed */ + file_priv = block->file_priv; + + /* free memory */ + nouveau_mem_free(dev, block); + + if (file_priv) { + kfree(file_priv); + file_priv = NULL; + } + + framebuffer_release(info); + + return 0; +} diff --git a/linux-core/nv50_fbcon.h b/linux-core/nv50_fbcon.h new file mode 100644 index 00000000..98e7101a --- /dev/null +++ b/linux-core/nv50_fbcon.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +#ifndef __NV50_FBCON_H__ +#define __NV50_FBCON_H__ + +#include "nv50_kms_wrapper.h" + +struct nv50_fbcon_par { + struct drm_framebuffer *fb; + struct drm_device *dev; + bool use_preferred_mode; +}; + +int nv50_fbcon_init(struct drm_device *dev); +int nv50_fbcon_destroy(struct drm_device *dev); + +#endif /* __NV50_FBCON_H__ */
\ No newline at end of file diff --git a/linux-core/nv50_i2c.c b/linux-core/nv50_i2c.c new file mode 100644 index 00000000..30e317c5 --- /dev/null +++ b/linux-core/nv50_i2c.c @@ -0,0 +1,400 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +/* This is largely a clone from xorg i2c functions, as i had serious trouble getting an i2c_bit_algo adaptor running. */ + +#include "nv50_i2c.h" + +static uint32_t nv50_i2c_port(int index) +{ + uint32_t port = 0; + + switch (index) { + case 0: + port = NV50_PCONNECTOR_I2C_PORT_0; + break; + case 1: + port = NV50_PCONNECTOR_I2C_PORT_1; + break; + case 2: + port = NV50_PCONNECTOR_I2C_PORT_2; + break; + case 3: + port = NV50_PCONNECTOR_I2C_PORT_3; + break; + case 4: + port = NV50_PCONNECTOR_I2C_PORT_4; + break; + case 5: + port = NV50_PCONNECTOR_I2C_PORT_5; + break; + default: + break; + } + + if (!port) { + DRM_ERROR("Invalid i2c port, returning 0.\n"); + BUG(); + } + + return port; +} + +static void nv50_i2c_set_bits(struct nv50_i2c_channel *chan, int clock_high, int data_high) +{ + struct drm_nouveau_private *dev_priv = chan->dev->dev_private; + uint32_t port = nv50_i2c_port(chan->index); + + if (!port) + return; + + NV_WRITE(port, 4 | (data_high << 1) | clock_high); +} + +static void nv50_i2c_get_bits(struct nv50_i2c_channel *chan, int *clock_high, int *data_high) +{ + struct drm_nouveau_private *dev_priv = chan->dev->dev_private; + uint32_t port = nv50_i2c_port(chan->index); + uint32_t val; + + if (!port) + return; + + val = NV_READ(port); + + if (val & 1) + *clock_high = 1; + else + *clock_high = 0; + + if (val & 2) + *data_high = 1; + else + *data_high = 0; +} + +static bool nv50_i2c_raise_clock(struct nv50_i2c_channel *chan, int data) +{ + int i, clock; + + nv50_i2c_set_bits(chan, 1, data); + udelay(2); + + for (i = 2200; i > 0; i -= 2) { + nv50_i2c_get_bits(chan, &clock, &data); + if (clock) + return true; + udelay(2); + } + + printk("a timeout occured in nv50_i2c_raise_clock\n"); + + return false; +} + +static bool nv50_i2c_start(struct nv50_i2c_channel *chan) +{ + if (!nv50_i2c_raise_clock(chan, 1)) + return false; + + nv50_i2c_set_bits(chan, 1, 0); + udelay(5); + + nv50_i2c_set_bits(chan, 0, 0); + udelay(5); + + return true; +} + +static void nv50_i2c_stop(struct nv50_i2c_channel *chan) +{ + nv50_i2c_set_bits(chan, 0, 0); + udelay(2); + + nv50_i2c_set_bits(chan, 1, 0); + udelay(5); + + nv50_i2c_set_bits(chan, 1, 1); + udelay(5); +} + +static bool nv50_i2c_write_bit(struct nv50_i2c_channel *chan, int data) +{ + bool rval; + + nv50_i2c_set_bits(chan, 0, data); + udelay(2); + + rval = nv50_i2c_raise_clock(chan, data); + udelay(5); + + nv50_i2c_set_bits(chan, 0, data); + udelay(5); + + return rval; +} + +static bool nv50_i2c_read_bit(struct nv50_i2c_channel *chan, int *data) +{ + bool rval; + int clock; + + rval = nv50_i2c_raise_clock(chan, 1); + udelay(5); + + nv50_i2c_get_bits(chan, &clock, data); + udelay(5); + + nv50_i2c_set_bits(chan, 0, 1); + udelay(5); + + return rval; +} + +static bool nv50_i2c_write_byte(struct nv50_i2c_channel *chan, uint8_t byte) +{ + bool rval; + int i, clock, data; + + for (i = 7; i >= 0; i--) + if (!nv50_i2c_write_bit(chan, (byte >> i) & 1)) + return false; + + nv50_i2c_set_bits(chan, 0, 1); + udelay(5); + + rval = nv50_i2c_raise_clock(chan, 1); + + if (rval) { + for (i = 40; i > 0; i -= 2) { + udelay(2); + nv50_i2c_get_bits(chan, &clock, &data); + if (data == 0) + break; + } + + if (i <= 0) { + printk("a timeout occured in nv50_i2c_write_byte\n"); + rval = false; + } + } + + nv50_i2c_set_bits(chan, 0, 1); + udelay(5); + + return rval; +} + +static bool nv50_i2c_read_byte(struct nv50_i2c_channel *chan, uint8_t *byte, bool last) +{ + int i, bit; + + nv50_i2c_set_bits(chan, 0, 1); + udelay(5); + + *byte = 0; + + for (i = 7; i >= 0; i--) { + if (nv50_i2c_read_bit(chan, &bit)) { + if (bit) + *byte |= (1 << i); + } else { + return false; + } + } + + if (!nv50_i2c_write_bit(chan, last ? 1 : 0)) + return false; + + return true; +} + +/* only 7 bits addresses. */ +static bool nv50_i2c_address(struct nv50_i2c_channel *chan, uint8_t address, bool write) +{ + if (nv50_i2c_start(chan)) { + uint8_t real_addr = (address << 1); + if (!write) + real_addr |= 1; + + if (nv50_i2c_write_byte(chan, real_addr)) + return true; + + /* failure, so issue stop */ + nv50_i2c_stop(chan); + } + + return false; +} + +static bool nv50_i2c_read(struct nv50_i2c_channel *chan, uint8_t address, uint8_t *buffer, uint32_t length) +{ + int i, j; + bool rval, last; + + /* retries */ + for (i = 0; i < 4; i++) { + rval = nv50_i2c_address(chan, address, false); + if (!rval) + return false; + + for (j = 0; j < length; j++) { + last = false; + if (j == (length - 1)) + last = true; + rval = nv50_i2c_read_byte(chan, &buffer[j], last); + if (!rval) { + nv50_i2c_stop(chan); + break; + } + } + + nv50_i2c_stop(chan); + + /* done */ + if (rval) + break; + } + + if (!rval) + printk("nv50_i2c_read failed\n"); + + return rval; +} + +static bool nv50_i2c_write(struct nv50_i2c_channel *chan, uint8_t address, uint8_t *buffer, uint32_t length) +{ + int i, j; + bool rval; + + /* retries */ + for (i = 0; i < 4; i++) { + rval = nv50_i2c_address(chan, address, true); + if (!rval) + return false; + + for (j = 0; j < length; j++) { + rval = nv50_i2c_write_byte(chan, buffer[j]); + if (!rval) { + break; + } + } + + nv50_i2c_stop(chan); + + /* done */ + if (rval) + break; + } + + if (!rval) + printk("nv50_i2c_write failed\n"); + + return rval; +} + +static int nv50_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) +{ + struct nv50_i2c_channel *chan = i2c_get_adapdata(i2c_adap); + bool rval; + int i; + + for (i = 0; i < num; i++) { + if (msgs[i].flags & I2C_M_RD) { /* read */ + rval = nv50_i2c_read(chan, msgs[i].addr, msgs[i].buf, msgs[i].len); + } else { /* write */ + rval = nv50_i2c_write(chan, msgs[i].addr, msgs[i].buf, msgs[i].len); + } + + if (!rval) + break; + } + + if (rval) + return i; + else + return -EINVAL; +} + +static u32 nv50_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C; +} + +static const struct i2c_algorithm nv50_i2c_algo = { + .master_xfer = nv50_i2c_xfer, + .functionality = nv50_i2c_functionality, +}; + +static int nv50_i2c_register_bus(struct i2c_adapter *adap) +{ + adap->algo = &nv50_i2c_algo; + + adap->timeout = 40; + adap->retries = 4; + + return i2c_add_adapter(adap); +} + +#define I2C_HW_B_NOUVEAU 0x010030 +struct nv50_i2c_channel *nv50_i2c_channel_create(struct drm_device *dev, uint32_t index) +{ + struct nv50_i2c_channel *chan; + + chan = kzalloc(sizeof(struct nv50_i2c_channel), GFP_KERNEL); + + if (!chan) + goto out; + + DRM_INFO("Creating i2c bus with index %d\n", index); + + chan->dev = dev; + chan->index = index; + snprintf(chan->adapter.name, I2C_NAME_SIZE, "nv50 i2c %d", index); + chan->adapter.owner = THIS_MODULE; + chan->adapter.id = I2C_HW_B_NOUVEAU; + chan->adapter.dev.parent = &dev->pdev->dev; + + i2c_set_adapdata(&chan->adapter, chan); + + if (nv50_i2c_register_bus(&chan->adapter)) + goto out; + + return chan; + +out: + kfree(chan); + return NULL; +} + +void nv50_i2c_channel_destroy(struct nv50_i2c_channel *chan) +{ + if (!chan) + return; + + i2c_del_adapter(&chan->adapter); + kfree(chan); +} diff --git a/linux-core/nv50_i2c.h b/linux-core/nv50_i2c.h new file mode 100644 index 00000000..1740f8ec --- /dev/null +++ b/linux-core/nv50_i2c.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +#ifndef __NV50_I2C_H__ +#define __NV50_I2C_H__ + +#include <linux/i2c.h> +#include <linux/i2c-id.h> +#include <linux/i2c-algo-bit.h> +#include "drmP.h" +#include "drm.h" +#include "nv50_display.h" + +struct nv50_i2c_channel { + struct drm_device *dev; + + uint32_t index; + struct i2c_adapter adapter; +}; + +struct nv50_i2c_channel *nv50_i2c_channel_create(struct drm_device *dev, uint32_t index); +void nv50_i2c_channel_destroy(struct nv50_i2c_channel *chan); + +#endif /* __NV50_I2C_H__ */ diff --git a/linux-core/nv50_kms_wrapper.c b/linux-core/nv50_kms_wrapper.c new file mode 100644 index 00000000..1320e5ed --- /dev/null +++ b/linux-core/nv50_kms_wrapper.c @@ -0,0 +1,1454 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +#include "nv50_kms_wrapper.h" +#include "drm_crtc_helper.h" /* be careful what you use from this */ + +/* This file serves as the interface between the common kernel modesetting code and the device dependent implementation. */ + +/* + * Get private functions. + */ + +struct nv50_kms_priv *nv50_get_kms_priv(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + return dev_priv->kms_priv; +} + +/* + * Allocation functions. + */ + +static void *nv50_kms_alloc_crtc(struct drm_device *dev) +{ + struct nv50_kms_priv *kms_priv = nv50_get_kms_priv(dev); + struct nv50_kms_crtc *crtc = kzalloc(sizeof(struct nv50_kms_crtc), GFP_KERNEL); + + if (!crtc) + return NULL; + + list_add_tail(&crtc->item, &kms_priv->crtcs); + + return &(crtc->priv); +} + +static void *nv50_kms_alloc_output(struct drm_device *dev) +{ + struct nv50_kms_priv *kms_priv = nv50_get_kms_priv(dev); + struct nv50_kms_encoder *encoder = kzalloc(sizeof(struct nv50_kms_encoder), GFP_KERNEL); + + if (!encoder) + return NULL; + + list_add_tail(&encoder->item, &kms_priv->encoders); + + return &(encoder->priv); +} + +static void *nv50_kms_alloc_connector(struct drm_device *dev) +{ + struct nv50_kms_priv *kms_priv = nv50_get_kms_priv(dev); + struct nv50_kms_connector *connector = kzalloc(sizeof(struct nv50_kms_connector), GFP_KERNEL); + + if (!connector) + return NULL; + + list_add_tail(&connector->item, &kms_priv->connectors); + + return &(connector->priv); +} + +static void nv50_kms_free_crtc(void *crtc) +{ + struct nv50_kms_crtc *kms_crtc = from_nv50_crtc(crtc); + + list_del(&kms_crtc->item); + + kfree(kms_crtc); +} + +static void nv50_kms_free_output(void *output) +{ + struct nv50_kms_encoder *kms_encoder = from_nv50_output(output); + + list_del(&kms_encoder->item); + + kfree(kms_encoder); +} + +static void nv50_kms_free_connector(void *connector) +{ + struct nv50_kms_connector *kms_connector = from_nv50_connector(connector); + + list_del(&kms_connector->item); + + kfree(kms_connector); +} + +/* + * Mode conversion functions. + */ + +static struct nouveau_hw_mode *nv50_kms_to_hw_mode(struct drm_display_mode *mode) +{ + struct nouveau_hw_mode *hw_mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL); + if (!hw_mode) + return NULL; + + /* create hw values. */ + hw_mode->clock = mode->clock; + hw_mode->flags = hw_mode->flags; + + hw_mode->hdisplay = mode->hdisplay; + hw_mode->hsync_start = mode->hsync_start; + hw_mode->hsync_end = mode->hsync_end; + hw_mode->htotal = mode->htotal; + + hw_mode->hblank_start = mode->hdisplay + 1; + hw_mode->hblank_end = mode->htotal; + + hw_mode->vdisplay = mode->vdisplay; + hw_mode->vsync_start = mode->vsync_start; + hw_mode->vsync_end = mode->vsync_end; + hw_mode->vtotal = mode->vtotal; + + hw_mode->vblank_start = mode->vdisplay + 1; + hw_mode->vblank_end = mode->vtotal; + + return hw_mode; +} + +/* + * State mirroring functions. + */ + +static void nv50_kms_mirror_routing(struct drm_device *dev) +{ + struct nv50_display *display = nv50_get_display(dev); + struct nv50_crtc *crtc = NULL; + struct nv50_output *output = NULL; + struct nv50_connector *connector = NULL; + struct drm_connector *drm_connector = NULL; + struct drm_crtc *drm_crtc = NULL; + + /* Wipe all previous connections. */ + list_for_each_entry(connector, &display->connectors, item) { + connector->output = NULL; + } + + list_for_each_entry(output, &display->outputs, item) { + output->crtc = NULL; + } + + list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) { + if (drm_connector->encoder) { + output = to_nv50_output(drm_connector->encoder); + connector = to_nv50_connector(drm_connector); + + /* hook up output to connector. */ + connector->output = output; + + if (drm_connector->encoder->crtc) { + crtc = to_nv50_crtc(drm_connector->encoder->crtc); + + /* hook up output to crtc. */ + output->crtc = crtc; + } + } + } + + /* mirror crtc active state */ + list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) { + crtc = to_nv50_crtc(drm_crtc); + + crtc->enabled = drm_crtc->enabled; + } +} + +/* + * FB functions. + */ + +static void nv50_kms_framebuffer_destroy(struct drm_framebuffer *drm_framebuffer) +{ + drm_framebuffer_cleanup(drm_framebuffer); + + kfree(drm_framebuffer); +} + +static const struct drm_framebuffer_funcs nv50_kms_fb_funcs = { + .destroy = nv50_kms_framebuffer_destroy, +}; + +/* + * Mode config functions. + */ + +static struct drm_framebuffer *nv50_kms_framebuffer_create(struct drm_device *dev, + struct drm_file *file_priv, struct drm_mode_fb_cmd *mode_cmd) +{ + struct drm_framebuffer *drm_framebuffer = kzalloc(sizeof(struct drm_framebuffer), GFP_KERNEL); + if (!drm_framebuffer) + return NULL; + + drm_framebuffer_init(dev, drm_framebuffer, &nv50_kms_fb_funcs); + drm_helper_mode_fill_fb_struct(drm_framebuffer, mode_cmd); + + return drm_framebuffer; +} + +static int nv50_kms_fb_changed(struct drm_device *dev) +{ + return 0; /* not needed until nouveaufb? */ +} + +static const struct drm_mode_config_funcs nv50_kms_mode_funcs = { + .resize_fb = NULL, + .fb_create = nv50_kms_framebuffer_create, + .fb_changed = nv50_kms_fb_changed, +}; + +/* + * CRTC functions. + */ + +static int nv50_kms_crtc_cursor_set(struct drm_crtc *drm_crtc, + struct drm_file *file_priv, + uint32_t buffer_handle, + uint32_t width, uint32_t height) +{ + struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc); + struct nv50_display *display = nv50_get_display(crtc->dev); + int rval = 0; + + if (width != 64 || height != 64) + return -EINVAL; + + /* set bo before doing show cursor */ + if (buffer_handle) { + rval = crtc->cursor->set_bo(crtc, (drm_handle_t) buffer_handle); + if (rval != 0) + goto out; + } + + crtc->cursor->visible = buffer_handle ? true : false; + + if (buffer_handle) { + rval = crtc->cursor->show(crtc); + if (rval != 0) + goto out; + } else { /* no handle implies hiding the cursor */ + rval = crtc->cursor->hide(crtc); + goto out; + } + + if (rval != 0) + return rval; + +out: + /* in case this triggers any other cursor changes */ + display->update(display); + + return rval; +} + +static int nv50_kms_crtc_cursor_move(struct drm_crtc *drm_crtc, int x, int y) +{ + struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc); + + return crtc->cursor->set_pos(crtc, x, y); +} + +void nv50_kms_crtc_gamma_set(struct drm_crtc *drm_crtc, u16 *r, u16 *g, u16 *b, + uint32_t size) +{ + struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc); + + if (size != 256) + return; + + crtc->lut->set(crtc, (uint16_t *)r, (uint16_t *)g, (uint16_t *)b); +} + +int nv50_kms_crtc_set_config(struct drm_mode_set *set) +{ + int rval = 0, i; + uint32_t crtc_mask = 0; + struct drm_device *dev = NULL; + struct drm_nouveau_private *dev_priv = NULL; + struct nv50_display *display = NULL; + struct drm_connector *drm_connector = NULL; + struct drm_encoder *drm_encoder = NULL; + struct drm_crtc *drm_crtc = NULL; + + struct nv50_crtc *crtc = NULL; + struct nv50_output *output = NULL; + struct nv50_connector *connector = NULL; + struct nouveau_hw_mode *hw_mode = NULL; + struct nv50_fb_info fb_info; + + bool blank = false; + bool switch_fb = false; + bool modeset = false; + + NV50_DEBUG("\n"); + + /* + * Supported operations: + * - Switch mode. + * - Switch framebuffer. + * - Blank screen. + */ + + /* Sanity checking */ + if (!set) { + DRM_ERROR("Sanity check failed\n"); + goto out; + } + + if (!set->crtc) { + DRM_ERROR("Sanity check failed\n"); + goto out; + } + + if (set->mode) { + if (set->fb) { + if (!drm_mode_equal(set->mode, &set->crtc->mode)) + modeset = true; + + if (set->fb != set->crtc->fb) + switch_fb = true; + + if (set->x != set->crtc->x || set->y != set->crtc->y) + switch_fb = true; + } + } else { + blank = true; + } + + if (!set->connectors && !blank) { + DRM_ERROR("Sanity check failed\n"); + goto out; + } + + /* Basic variable setting */ + dev = set->crtc->dev; + dev_priv = dev->dev_private; + display = nv50_get_display(dev); + crtc = to_nv50_crtc(set->crtc); + + /** + * Wiring up the encoders and connectors. + */ + + /* for switch_fb we verify if any important changes happened */ + if (!blank) { + /* Mode validation */ + hw_mode = nv50_kms_to_hw_mode(set->mode); + + rval = crtc->validate_mode(crtc, hw_mode); + + if (rval != MODE_OK) { + DRM_ERROR("Mode not ok\n"); + goto out; + } + + for (i = 0; i < set->num_connectors; i++) { + drm_connector = set->connectors[i]; + if (!drm_connector) { + DRM_ERROR("No connector\n"); + goto out; + } + connector = to_nv50_connector(drm_connector); + + /* This is to ensure it knows the connector subtype. */ + drm_connector->funcs->fill_modes(drm_connector, 0, 0); + + output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector)); + if (!output) { + DRM_ERROR("No output\n"); + goto out; + } + + rval = output->validate_mode(output, hw_mode); + if (rval != MODE_OK) { + DRM_ERROR("Mode not ok\n"); + goto out; + } + + /* verify if any "sneaky" changes happened */ + if (output != connector->output) + modeset = true; + + if (output->crtc != crtc) + modeset = true; + } + } + + /* Now we verified if anything changed, fail if nothing has. */ + if (!modeset && !switch_fb && !blank) + DRM_INFO("A seemingly empty modeset encountered, this could be a bug.\n"); + + /* Validation done, move on to cleaning of existing structures. */ + if (modeset) { + /* find encoders that use this crtc. */ + list_for_each_entry(drm_encoder, &dev->mode_config.encoder_list, head) { + if (drm_encoder->crtc == set->crtc) { + /* find the connector that goes with it */ + list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) { + if (drm_connector->encoder == drm_encoder) { + drm_connector->encoder = NULL; + break; + } + } + drm_encoder->crtc = NULL; + } + } + + /* now find if our desired encoders or connectors are in use already. */ + for (i = 0; i < set->num_connectors; i++) { + drm_connector = set->connectors[i]; + if (!drm_connector) { + DRM_ERROR("No connector\n"); + goto out; + } + + if (!drm_connector->encoder) + continue; + + drm_encoder = drm_connector->encoder; + drm_connector->encoder = NULL; + + if (!drm_encoder->crtc) + continue; + + drm_crtc = drm_encoder->crtc; + drm_encoder->crtc = NULL; + + drm_crtc->enabled = false; + } + + /* Time to wire up the public encoder, the private one will be handled later. */ + for (i = 0; i < set->num_connectors; i++) { + drm_connector = set->connectors[i]; + if (!drm_connector) { + DRM_ERROR("No connector\n"); + goto out; + } + + output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector)); + if (!output) { + DRM_ERROR("No output\n"); + goto out; + } + + /* find the encoder public structure that matches out output structure. */ + drm_encoder = to_nv50_kms_encoder(output); + + if (!drm_encoder) { + DRM_ERROR("No encoder\n"); + goto out; + } + + drm_encoder->crtc = set->crtc; + set->crtc->enabled = true; + drm_connector->encoder = drm_encoder; + } + } + + /** + * Disable crtc. + */ + + if (blank) { + crtc = to_nv50_crtc(set->crtc); + + set->crtc->enabled = false; + + /* disconnect encoders and connectors */ + for (i = 0; i < set->num_connectors; i++) { + drm_connector = set->connectors[i]; + + if (!drm_connector->encoder) + continue; + + drm_connector->encoder->crtc = NULL; + drm_connector->encoder = NULL; + } + } + + /** + * All state should now be updated, now onto the real work. + */ + + /* mirror everything to the private structs */ + nv50_kms_mirror_routing(dev); + + /** + * Bind framebuffer. + */ + + if (switch_fb) { + crtc = to_nv50_crtc(set->crtc); + + /* set framebuffer */ + set->crtc->fb = set->fb; + + /* set private framebuffer */ + crtc = to_nv50_crtc(set->crtc); + fb_info.block = find_block_by_handle(dev_priv->fb_heap, set->fb->mm_handle); + fb_info.width = set->fb->width; + fb_info.height = set->fb->height; + fb_info.depth = set->fb->depth; + fb_info.bpp = set->fb->bits_per_pixel; + fb_info.pitch = set->fb->pitch; + fb_info.x = set->x; + fb_info.y = set->y; + + rval = crtc->fb->bind(crtc, &fb_info); + if (rval != 0) { + DRM_ERROR("fb_bind failed\n"); + goto out; + } + } + + /* this is !cursor_show */ + if (!crtc->cursor->enabled) { + rval = crtc->cursor->enable(crtc); + if (rval != 0) { + DRM_ERROR("cursor_enable failed\n"); + goto out; + } + } + + /** + * Blanking. + */ + + if (blank) { + crtc = to_nv50_crtc(set->crtc); + + rval = crtc->blank(crtc, true); + if (rval != 0) { + DRM_ERROR("blanking failed\n"); + goto out; + } + + /* detach any outputs that are currently unused */ + list_for_each_entry(drm_encoder, &dev->mode_config.encoder_list, head) { + if (!drm_encoder->crtc) { + output = to_nv50_output(drm_encoder); + + rval = output->execute_mode(output, true); + if (rval != 0) { + DRM_ERROR("detaching output failed\n"); + goto out; + } + } + } + } + + /** + * Change framebuffer, without changing mode. + */ + + if (switch_fb && !modeset && !blank) { + crtc = to_nv50_crtc(set->crtc); + + rval = crtc->set_fb(crtc); + if (rval != 0) { + DRM_ERROR("set_fb failed\n"); + goto out; + } + + /* this also sets the fb offset */ + rval = crtc->blank(crtc, false); + if (rval != 0) { + DRM_ERROR("unblanking failed\n"); + goto out; + } + } + + /** + * Normal modesetting. + */ + + if (modeset) { + crtc = to_nv50_crtc(set->crtc); + + /* disconnect unused outputs */ + list_for_each_entry(output, &display->outputs, item) { + if (output->crtc) { + crtc_mask |= 1 << output->crtc->index; + } else { + rval = output->execute_mode(output, true); + if (rval != 0) { + DRM_ERROR("detaching output failed\n"); + goto out; + } + } + } + + /* blank any unused crtcs */ + list_for_each_entry(crtc, &display->crtcs, item) { + if (!(crtc_mask & (1 << crtc->index))) + crtc->blank(crtc, true); + } + + crtc = to_nv50_crtc(set->crtc); + + rval = crtc->set_mode(crtc, hw_mode); + if (rval != 0) { + DRM_ERROR("crtc mode set failed\n"); + goto out; + } + + /* find native mode. */ + list_for_each_entry(output, &display->outputs, item) { + if (output->crtc != crtc) + continue; + + *crtc->native_mode = *output->native_mode; + list_for_each_entry(connector, &display->connectors, item) { + if (connector->output != output) + continue; + + crtc->requested_scaling_mode = connector->requested_scaling_mode; + crtc->use_dithering = connector->use_dithering; + break; + } + + if (crtc->requested_scaling_mode == SCALE_NON_GPU) + crtc->use_native_mode = false; + else + crtc->use_native_mode = true; + + break; /* no use in finding more than one mode */ + } + + rval = crtc->execute_mode(crtc); + if (rval != 0) { + DRM_ERROR("crtc execute mode failed\n"); + goto out; + } + + list_for_each_entry(output, &display->outputs, item) { + if (output->crtc != crtc) + continue; + + rval = output->execute_mode(output, false); + if (rval != 0) { + DRM_ERROR("output execute mode failed\n"); + goto out; + } + } + + rval = crtc->set_scale(crtc); + if (rval != 0) { + DRM_ERROR("crtc set scale failed\n"); + goto out; + } + + /* next line changes crtc, so putting it here is important */ + display->last_crtc = crtc->index; + } + + /* always reset dpms, regardless if any other modesetting is done. */ + if (!blank) { + /* this is executed immediately */ + list_for_each_entry(output, &display->outputs, item) { + if (output->crtc != crtc) + continue; + + rval = output->set_power_mode(output, DRM_MODE_DPMS_ON); + if (rval != 0) { + DRM_ERROR("output set power mode failed\n"); + goto out; + } + } + + /* update dpms state to DPMSModeOn */ + for (i = 0; i < set->num_connectors; i++) { + drm_connector = set->connectors[i]; + if (!drm_connector) { + DRM_ERROR("No connector\n"); + goto out; + } + + rval = drm_connector_property_set_value(drm_connector, + dev->mode_config.dpms_property, + DRM_MODE_DPMS_ON); + if (rval != 0) { + DRM_ERROR("failed to update dpms state\n"); + goto out; + } + } + } + + display->update(display); + + /* Update the current mode, now that all has gone well. */ + if (modeset) { + set->crtc->mode = *(set->mode); + set->crtc->x = set->x; + set->crtc->y = set->y; + } + + kfree(hw_mode); + + return 0; + +out: + kfree(hw_mode); + + if (rval != 0) + return rval; + else + return -EINVAL; +} + +static void nv50_kms_crtc_destroy(struct drm_crtc *drm_crtc) +{ + struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc); + + drm_crtc_cleanup(drm_crtc); + + /* this will even destroy the public structure. */ + crtc->destroy(crtc); +} + +static const struct drm_crtc_funcs nv50_kms_crtc_funcs = { + .save = NULL, + .restore = NULL, + .cursor_set = nv50_kms_crtc_cursor_set, + .cursor_move = nv50_kms_crtc_cursor_move, + .gamma_set = nv50_kms_crtc_gamma_set, + .set_config = nv50_kms_crtc_set_config, + .destroy = nv50_kms_crtc_destroy, +}; + +static int nv50_kms_crtcs_init(struct drm_device *dev) +{ + struct nv50_display *display = nv50_get_display(dev); + struct nv50_crtc *crtc = NULL; + + /* + * This may look a bit confusing, but: + * The internal structure is already allocated and so is the public one. + * Just a matter of getting to the memory and register it. + */ + list_for_each_entry(crtc, &display->crtcs, item) { + struct drm_crtc *drm_crtc = to_nv50_kms_crtc(crtc); + + drm_crtc_init(dev, drm_crtc, &nv50_kms_crtc_funcs); + + /* init lut storage */ + drm_mode_crtc_set_gamma_size(drm_crtc, 256); + } + + return 0; +} + +/* + * Encoder functions + */ + +static void nv50_kms_encoder_destroy(struct drm_encoder *drm_encoder) +{ + struct nv50_output *output = to_nv50_output(drm_encoder); + + drm_encoder_cleanup(drm_encoder); + + /* this will even destroy the public structure. */ + output->destroy(output); +} + +static const struct drm_encoder_funcs nv50_kms_encoder_funcs = { + .destroy = nv50_kms_encoder_destroy, +}; + +static int nv50_kms_encoders_init(struct drm_device *dev) +{ + struct nv50_display *display = nv50_get_display(dev); + struct nv50_output *output = NULL; + + list_for_each_entry(output, &display->outputs, item) { + struct drm_encoder *drm_encoder = to_nv50_kms_encoder(output); + uint32_t type = DRM_MODE_ENCODER_NONE; + + switch (output->type) { + case OUTPUT_DAC: + type = DRM_MODE_ENCODER_DAC; + break; + case OUTPUT_TMDS: + type = DRM_MODE_ENCODER_TMDS; + break; + case OUTPUT_LVDS: + type = DRM_MODE_ENCODER_LVDS; + break; + case OUTPUT_TV: + type = DRM_MODE_ENCODER_TVDAC; + break; + default: + type = DRM_MODE_ENCODER_NONE; + break; + } + + if (type == DRM_MODE_ENCODER_NONE) { + DRM_ERROR("DRM_MODE_ENCODER_NONE encountered\n"); + continue; + } + + drm_encoder_init(dev, drm_encoder, &nv50_kms_encoder_funcs, type); + + /* I've never seen possible crtc's restricted. */ + drm_encoder->possible_crtcs = 3; + drm_encoder->possible_clones = 0; + } + + return 0; +} + +/* + * Connector functions + */ + + +/* These 2 functions wrap the connector properties that deal with multiple encoders per connector. */ +bool nv50_kms_connector_get_digital(struct drm_connector *drm_connector) +{ + struct drm_device *dev = drm_connector->dev; + + switch (drm_connector->connector_type) { + case DRM_MODE_CONNECTOR_VGA: + case DRM_MODE_CONNECTOR_SVIDEO: + return false; + case DRM_MODE_CONNECTOR_DVID: + case DRM_MODE_CONNECTOR_LVDS: + return true; + default: + break; + } + + if (drm_connector->connector_type == DRM_MODE_CONNECTOR_DVII) { + int rval; + uint64_t prop_val; + + rval = drm_connector_property_get_value(drm_connector, dev->mode_config.dvi_i_select_subconnector_property, &prop_val); + if (rval) { + DRM_ERROR("Unable to find select subconnector property, defaulting to DVI-D\n"); + return true; + } + + /* Is a subconnector explicitly selected? */ + switch (prop_val) { + case DRM_MODE_SUBCONNECTOR_DVID: + return true; + case DRM_MODE_SUBCONNECTOR_DVIA: + return false; + default: + break; + } + + rval = drm_connector_property_get_value(drm_connector, dev->mode_config.dvi_i_subconnector_property, &prop_val); + if (rval) { + DRM_ERROR("Unable to find subconnector property, defaulting to DVI-D\n"); + return true; + } + + /* Do we know what subconnector we currently have connected? */ + switch (prop_val) { + case DRM_MODE_SUBCONNECTOR_DVID: + return true; + case DRM_MODE_SUBCONNECTOR_DVIA: + return false; + default: + DRM_ERROR("Unknown subconnector value, defaulting to DVI-D\n"); + return true; + } + } + + DRM_ERROR("Unknown connector type, defaulting to analog\n"); + return false; +} + +static void nv50_kms_connector_set_digital(struct drm_connector *drm_connector, int digital, bool force) +{ + struct drm_device *dev = drm_connector->dev; + + if (drm_connector->connector_type == DRM_MODE_CONNECTOR_DVII) { + uint64_t cur_value, new_value; + + int rval = drm_connector_property_get_value(drm_connector, dev->mode_config.dvi_i_subconnector_property, &cur_value); + if (rval) { + DRM_ERROR("Unable to find subconnector property\n"); + return; + } + + /* Only set when unknown or when forced to do so. */ + if (cur_value != DRM_MODE_SUBCONNECTOR_Unknown && !force) + return; + + if (digital == 1) + new_value = DRM_MODE_SUBCONNECTOR_DVID; + else if (digital == 0) + new_value = DRM_MODE_SUBCONNECTOR_DVIA; + else + new_value = DRM_MODE_SUBCONNECTOR_Unknown; + drm_connector_property_set_value(drm_connector, dev->mode_config.dvi_i_subconnector_property, new_value); + } +} + +void nv50_kms_connector_detect_all(struct drm_device *dev) +{ + struct drm_connector *drm_connector = NULL; + + list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) { + drm_connector->funcs->detect(drm_connector); + } +} + +static enum drm_connector_status nv50_kms_connector_detect(struct drm_connector *drm_connector) +{ + struct drm_device *dev = drm_connector->dev; + struct nv50_connector *connector = to_nv50_connector(drm_connector); + struct nv50_output *output = NULL; + int hpd_detect = 0, load_detect = 0, i2c_detect = 0; + int old_status = drm_connector->status; + + /* hotplug detect */ + hpd_detect = connector->hpd_detect(connector); + + /* load detect */ + output = connector->to_output(connector, false); /* analog */ + if (output && output->detect) + load_detect = output->detect(output); + + if (hpd_detect < 0 || load_detect < 0) /* did an error occur? */ + i2c_detect = connector->i2c_detect(connector); + + if (load_detect == 1) { + nv50_kms_connector_set_digital(drm_connector, 0, true); /* analog, forced */ + } else if (hpd_detect == 1 && load_detect == 0) { + nv50_kms_connector_set_digital(drm_connector, 1, true); /* digital, forced */ + } else { + nv50_kms_connector_set_digital(drm_connector, -1, true); /* unknown, forced */ + } + + if (hpd_detect == 1 || load_detect == 1 || i2c_detect == 1) + drm_connector->status = connector_status_connected; + else + drm_connector->status = connector_status_disconnected; + + /* update our modes whenever there is reason to */ + if (old_status != drm_connector->status) { + drm_connector->funcs->fill_modes(drm_connector, 0, 0); + + /* notify fb of changes */ + dev->mode_config.funcs->fb_changed(dev); + + /* sent a hotplug event when appropriate. */ + drm_sysfs_hotplug_event(dev); + } + + return drm_connector->status; +} + +static void nv50_kms_connector_destroy(struct drm_connector *drm_connector) +{ + struct nv50_connector *connector = to_nv50_connector(drm_connector); + + drm_sysfs_connector_remove(drm_connector); + drm_connector_cleanup(drm_connector); + + /* this will even destroy the public structure. */ + connector->destroy(connector); +} + +/* + * 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, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },*/ /* 640x480@60Hz */ + { DRM_MODE("1280x1024", DRM_MODE_TYPE_DEFAULT, 135000, 1280, 1296, + 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1280x1024@75Hz */ +}; + +static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, uint32_t maxX, uint32_t maxY) +{ + struct nv50_connector *connector = to_nv50_connector(drm_connector); + struct drm_device *dev = drm_connector->dev; + int rval = 0; + bool connected = false; + struct drm_display_mode *mode, *t; + struct edid *edid = NULL; + + NV50_DEBUG("%s\n", drm_get_connector_name(drm_connector)); + /* set all modes to the unverified state */ + list_for_each_entry_safe(mode, t, &drm_connector->modes, head) + mode->status = MODE_UNVERIFIED; + + if (nv50_kms_connector_detect(drm_connector) == connector_status_connected) + connected = true; + + if (connected) + NV50_DEBUG("%s is connected\n", drm_get_connector_name(drm_connector)); + else + NV50_DEBUG("%s is disconnected\n", drm_get_connector_name(drm_connector)); + + /* Not all connnectors have an i2c channel. */ + if (connected && connector->i2c_chan) + edid = (struct edid *) drm_do_probe_ddc_edid(&connector->i2c_chan->adapter); + + /* This will remove edid if needed. */ + drm_mode_connector_update_edid_property(drm_connector, edid); + + if (edid) { + rval = drm_add_edid_modes(drm_connector, edid); + + /* Only update when relevant and when detect couldn't determine type. */ + nv50_kms_connector_set_digital(drm_connector, edid->digital ? 1 : 0, false); + + kfree(edid); + } + + if (rval) /* number of modes > 1 */ + drm_mode_connector_list_update(drm_connector); + + if (maxX && maxY) + drm_mode_validate_size(dev, &drm_connector->modes, maxX, maxY, 0); + + list_for_each_entry_safe(mode, t, &drm_connector->modes, head) { + if (mode->status == MODE_OK) { + struct nouveau_hw_mode *hw_mode = nv50_kms_to_hw_mode(mode); + struct nv50_output *output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector)); + + mode->status = output->validate_mode(output, hw_mode); + /* find native mode, TODO: also check if we actually found one */ + if (mode->status == MODE_OK) { + if (mode->type & DRM_MODE_TYPE_PREFERRED) + *output->native_mode = *hw_mode; + } + kfree(hw_mode); + } + } + + /* revalidate now that we have native mode */ + list_for_each_entry_safe(mode, t, &drm_connector->modes, head) { + if (mode->status == MODE_OK) { + struct nouveau_hw_mode *hw_mode = nv50_kms_to_hw_mode(mode); + struct nv50_output *output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector)); + + mode->status = output->validate_mode(output, hw_mode); + kfree(hw_mode); + } + } + + drm_mode_prune_invalid(dev, &drm_connector->modes, true); + + /* pruning is done, so bail out. */ + if (!connected) + return; + + if (list_empty(&drm_connector->modes)) { + struct drm_display_mode *stdmode; + struct nouveau_hw_mode *hw_mode; + struct nv50_output *output; + + NV50_DEBUG("No valid modes on %s\n", drm_get_connector_name(drm_connector)); + + /* Making up native modes for LVDS is a bad idea. */ + if (drm_connector->connector_type == DRM_MODE_CONNECTOR_LVDS) + return; + + /* 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(drm_connector, stdmode); + drm_mode_list_concat(&drm_connector->probed_modes, + &drm_connector->modes); + + /* also add it as native mode */ + hw_mode = nv50_kms_to_hw_mode(mode); + output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector)); + + if (hw_mode) + *output->native_mode = *hw_mode; + + DRM_DEBUG("Adding standard 640x480 @ 60Hz to %s\n", + drm_get_connector_name(drm_connector)); + } + + drm_mode_sort(&drm_connector->modes); + + NV50_DEBUG("Probed modes for %s\n", drm_get_connector_name(drm_connector)); + + list_for_each_entry_safe(mode, t, &drm_connector->modes, head) { + mode->vrefresh = drm_mode_vrefresh(mode); + + /* is this needed, as it's unused by the driver? */ + drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); + drm_mode_debug_printmodeline(mode); + } +} + +static int nv50_kms_connector_set_property(struct drm_connector *drm_connector, + struct drm_property *property, + uint64_t value) +{ + struct drm_device *dev = drm_connector->dev; + struct nv50_connector *connector = to_nv50_connector(drm_connector); + int rval = 0; + bool delay_change = false; + + /* DPMS */ + if (property == dev->mode_config.dpms_property && drm_connector->encoder) { + struct nv50_output *output = to_nv50_output(drm_connector->encoder); + + rval = output->set_power_mode(output, (int) value); + + return rval; + } + + /* Scaling mode */ + if (property == dev->mode_config.scaling_mode_property) { + struct nv50_crtc *crtc = NULL; + struct nv50_display *display = nv50_get_display(dev); + int internal_value = 0; + + switch (value) { + case DRM_MODE_SCALE_NON_GPU: + internal_value = SCALE_NON_GPU; + break; + case DRM_MODE_SCALE_FULLSCREEN: + internal_value = SCALE_FULLSCREEN; + break; + case DRM_MODE_SCALE_NO_SCALE: + internal_value = SCALE_NOSCALE; + break; + case DRM_MODE_SCALE_ASPECT: + internal_value = SCALE_ASPECT; + break; + default: + break; + } + + /* LVDS always needs gpu scaling */ + if (connector->type == CONNECTOR_LVDS && internal_value == SCALE_NON_GPU) + return -EINVAL; + + connector->requested_scaling_mode = internal_value; + + if (drm_connector->encoder && drm_connector->encoder->crtc) + crtc = to_nv50_crtc(drm_connector->encoder->crtc); + + if (!crtc) + return 0; + + crtc->requested_scaling_mode = connector->requested_scaling_mode; + + /* going from and to a gpu scaled regime requires a modesetting, so wait until next modeset */ + if (crtc->scaling_mode == SCALE_NON_GPU || internal_value == SCALE_NON_GPU) { + DRM_INFO("Moving from or to a non-gpu scaled mode, this will be processed upon next modeset."); + delay_change = true; + } + + if (delay_change) + return 0; + + rval = crtc->set_scale(crtc); + if (rval) + return rval; + + /* process command buffer */ + display->update(display); + + return 0; + } + + /* Dithering */ + if (property == dev->mode_config.dithering_mode_property) { + struct nv50_crtc *crtc = NULL; + struct nv50_display *display = nv50_get_display(dev); + + if (value == DRM_MODE_DITHERING_ON) + connector->use_dithering = true; + else + connector->use_dithering = false; + + if (drm_connector->encoder && drm_connector->encoder->crtc) + crtc = to_nv50_crtc(drm_connector->encoder->crtc); + + if (!crtc) + return 0; + + /* update hw state */ + crtc->use_dithering = connector->use_dithering; + rval = crtc->set_dither(crtc); + if (rval) + return rval; + + /* process command buffer */ + display->update(display); + + return 0; + } + + return -EINVAL; +} + +static const struct drm_connector_funcs nv50_kms_connector_funcs = { + .save = NULL, + .restore = NULL, + .detect = nv50_kms_connector_detect, + .destroy = nv50_kms_connector_destroy, + .fill_modes = nv50_kms_connector_fill_modes, + .set_property = nv50_kms_connector_set_property +}; + +static int nv50_kms_get_scaling_mode(struct drm_connector *drm_connector) +{ + struct nv50_connector *connector = NULL; + int drm_mode = 0; + + if (!drm_connector) { + DRM_ERROR("drm_connector is NULL\n"); + return 0; + } + + connector = to_nv50_connector(drm_connector); + + switch (connector->requested_scaling_mode) { + case SCALE_NON_GPU: + drm_mode = DRM_MODE_SCALE_NON_GPU; + break; + case SCALE_FULLSCREEN: + drm_mode = DRM_MODE_SCALE_FULLSCREEN; + break; + case SCALE_NOSCALE: + drm_mode = DRM_MODE_SCALE_NO_SCALE; + break; + case SCALE_ASPECT: + drm_mode = DRM_MODE_SCALE_ASPECT; + break; + default: + break; + } + + return drm_mode; +} + +static int nv50_kms_connectors_init(struct drm_device *dev) +{ + struct nv50_display *display = nv50_get_display(dev); + struct nv50_connector *connector = NULL; + int i; + + /* Initialise some optional connector properties. */ + drm_mode_create_scaling_mode_property(dev); + drm_mode_create_dithering_property(dev); + + list_for_each_entry(connector, &display->connectors, item) { + struct drm_connector *drm_connector = to_nv50_kms_connector(connector); + uint32_t type = DRM_MODE_CONNECTOR_Unknown; + + switch (connector->type) { + case CONNECTOR_VGA: + type = DRM_MODE_CONNECTOR_VGA; + break; + case CONNECTOR_DVI_D: + type = DRM_MODE_CONNECTOR_DVID; + break; + case CONNECTOR_DVI_I: + type = DRM_MODE_CONNECTOR_DVII; + break; + case CONNECTOR_LVDS: + type = DRM_MODE_CONNECTOR_LVDS; + break; + case CONNECTOR_TV: + type = DRM_MODE_CONNECTOR_SVIDEO; + break; + default: + type = DRM_MODE_CONNECTOR_Unknown; + break; + } + + if (type == DRM_MODE_CONNECTOR_Unknown) { + DRM_ERROR("DRM_MODE_CONNECTOR_Unknown encountered\n"); + continue; + } + + /* It should be allowed sometimes, but let's be safe for the moment. */ + drm_connector->interlace_allowed = false; + drm_connector->doublescan_allowed = false; + + drm_connector_init(dev, drm_connector, &nv50_kms_connector_funcs, type); + + /* Init DVI-I specific properties */ + if (type == DRM_MODE_CONNECTOR_DVII) { + drm_mode_create_dvi_i_properties(dev); + drm_connector_attach_property(drm_connector, dev->mode_config.dvi_i_subconnector_property, 0); + drm_connector_attach_property(drm_connector, dev->mode_config.dvi_i_select_subconnector_property, 0); + } + + /* If supported in the future, it will have to use the scalers internally and not expose them. */ + if (type != DRM_MODE_CONNECTOR_SVIDEO) { + drm_connector_attach_property(drm_connector, dev->mode_config.scaling_mode_property, nv50_kms_get_scaling_mode(drm_connector)); + } + + drm_connector_attach_property(drm_connector, dev->mode_config.dithering_mode_property, connector->use_dithering ? DRM_MODE_DITHERING_ON : DRM_MODE_DITHERING_OFF); + + /* attach encoders, possibilities are analog + digital */ + for (i = 0; i < 2; i++) { + struct drm_encoder *drm_encoder = NULL; + struct nv50_output *output = connector->to_output(connector, i); + if (!output) + continue; + + drm_encoder = to_nv50_kms_encoder(output); + if (!drm_encoder) { + DRM_ERROR("No struct drm_connector to match struct nv50_output\n"); + continue; + } + + drm_mode_connector_attach_encoder(drm_connector, drm_encoder); + } + + drm_sysfs_connector_add(drm_connector); + } + + return 0; +} + +/* + * Main functions + */ + +int nv50_kms_init(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv50_kms_priv *kms_priv = kzalloc(sizeof(struct nv50_kms_priv), GFP_KERNEL); + struct nv50_display *display = NULL; + int rval = 0; + + if (!kms_priv) + return -ENOMEM; + + dev_priv->kms_priv = kms_priv; + + /* function pointers */ + /* an allocation interface that deals with the outside world, without polluting the core. */ + dev_priv->alloc_crtc = nv50_kms_alloc_crtc; + dev_priv->alloc_output = nv50_kms_alloc_output; + dev_priv->alloc_connector = nv50_kms_alloc_connector; + + dev_priv->free_crtc = nv50_kms_free_crtc; + dev_priv->free_output = nv50_kms_free_output; + dev_priv->free_connector = nv50_kms_free_connector; + + /* bios is needed for tables. */ + rval = nouveau_parse_bios(dev); + if (rval != 0) + goto out; + + /* init basic kernel modesetting */ + drm_mode_config_init(dev); + + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + + dev->mode_config.funcs = (void *)&nv50_kms_mode_funcs; + + dev->mode_config.max_width = 8192; + dev->mode_config.max_height = 8192; + + dev->mode_config.fb_base = dev_priv->fb_phys; + + /* init kms lists */ + INIT_LIST_HEAD(&kms_priv->crtcs); + INIT_LIST_HEAD(&kms_priv->encoders); + INIT_LIST_HEAD(&kms_priv->connectors); + + /* init the internal core, must be done first. */ + rval = nv50_display_create(dev); + if (rval != 0) + goto out; + + display = nv50_get_display(dev); + if (!display) { + rval = -EINVAL; + goto out; + } + + /* pre-init now */ + rval = display->pre_init(display); + if (rval != 0) + goto out; + + /* init external layer */ + rval = nv50_kms_crtcs_init(dev); + if (rval != 0) + goto out; + + rval = nv50_kms_encoders_init(dev); + if (rval != 0) + goto out; + + rval = nv50_kms_connectors_init(dev); + if (rval != 0) + goto out; + + /* init now, this'll kill the textmode */ + rval = display->init(display); + if (rval != 0) + goto out; + + /* process cmdbuffer */ + display->update(display); + + return 0; + +out: + kfree(kms_priv); + dev_priv->kms_priv = NULL; + + return rval; +} + +int nv50_kms_destroy(struct drm_device *dev) +{ + drm_mode_config_cleanup(dev); + + return 0; +} + diff --git a/linux-core/nv50_kms_wrapper.h b/linux-core/nv50_kms_wrapper.h new file mode 100644 index 00000000..60384804 --- /dev/null +++ b/linux-core/nv50_kms_wrapper.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +#ifndef __NV50_KMS_WRAPPER_H__ +#define __NV50_KMS_WRAPPER_H__ + +#include "drmP.h" +#include "drm.h" + +#include "nv50_display.h" +#include "nv50_crtc.h" +#include "nv50_cursor.h" +#include "nv50_lut.h" +#include "nv50_fb.h" +#include "nv50_output.h" +#include "nv50_connector.h" + +#include "drm_crtc.h" +#include "drm_edid.h" + +/* Link internal modesetting structure to interface. */ + +struct nv50_kms_crtc { + struct list_head item; + + struct nv50_crtc priv; + struct drm_crtc pub; +}; + +struct nv50_kms_encoder { + struct list_head item; + + struct nv50_output priv; + struct drm_encoder pub; +}; + +struct nv50_kms_connector { + struct list_head item; + + struct nv50_connector priv; + struct drm_connector pub; +}; + +struct nv50_kms_priv { + struct list_head crtcs; + struct list_head encoders; + struct list_head connectors; +}; + +/* Get private functions. */ +#define from_nv50_kms_crtc(x) container_of(x, struct nv50_kms_crtc, pub) +#define from_nv50_crtc(x) container_of(x, struct nv50_kms_crtc, priv) +#define from_nv50_kms_encoder(x) container_of(x, struct nv50_kms_encoder, pub) +#define from_nv50_output(x) container_of(x, struct nv50_kms_encoder, priv) +#define from_nv50_kms_connector(x) container_of(x, struct nv50_kms_connector, pub) +#define from_nv50_connector(x) container_of(x, struct nv50_kms_connector, priv) + +#define to_nv50_crtc(x) (&(from_nv50_kms_crtc(x)->priv)) +#define to_nv50_kms_crtc(x) (&(from_nv50_crtc(x)->pub)) +#define to_nv50_output(x) (&(from_nv50_kms_encoder(x)->priv)) +#define to_nv50_kms_encoder(x) (&(from_nv50_output(x)->pub)) +#define to_nv50_connector(x) (&(from_nv50_kms_connector(x)->priv)) +#define to_nv50_kms_connector(x) (&(from_nv50_connector(x)->pub)) + +struct nv50_kms_priv *nv50_get_kms_priv(struct drm_device *dev); +void nv50_kms_connector_detect_all(struct drm_device *dev); +bool nv50_kms_connector_get_digital(struct drm_connector *drm_connector); + +int nv50_kms_init(struct drm_device *dev); +int nv50_kms_destroy(struct drm_device *dev); + +#endif /* __NV50_KMS_WRAPPER_H__ */ diff --git a/linux-core/nv50_lut.c b/linux-core/nv50_lut.c new file mode 100644 index 00000000..7982a926 --- /dev/null +++ b/linux-core/nv50_lut.c @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +#include "nv50_lut.h" +#include "nv50_fb.h" +#include "nv50_crtc.h" +#include "nv50_display.h" + +static int nv50_lut_alloc(struct nv50_crtc *crtc) +{ + struct mem_block *block; + struct drm_file *file_priv = kzalloc(sizeof(struct drm_file), GFP_KERNEL); + uint32_t flags = NOUVEAU_MEM_FB | NOUVEAU_MEM_MAPPED; + int rval = 0; + + NV50_DEBUG("\n"); + + if (!file_priv) + return -ENOMEM; + + /* Any file_priv should do as it's pointer is used as identification. */ + block = nouveau_mem_alloc(crtc->dev, 0, 4096, flags, file_priv); + + if (!block) { + rval = -ENOMEM; + goto out; + } + + crtc->lut->block = block; + + return 0; + +out: + if (file_priv) + kfree(file_priv); + + return rval; +} + +static int nv50_lut_free(struct nv50_crtc *crtc) +{ + struct drm_file *file_priv = crtc->lut->block->file_priv; + + NV50_DEBUG("\n"); + + nouveau_mem_free(crtc->dev, crtc->lut->block); + + kfree(file_priv); + + return 0; +} + +#define NV50_LUT_INDEX(val, w) ((val << (8 - w)) | (val >> ((w << 1) - 8))) +static int nv50_lut_set(struct nv50_crtc *crtc, uint16_t *red, uint16_t *green, uint16_t *blue) +{ + uint32_t index = 0, i; + struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + void __iomem *lut = NULL; + + NV50_DEBUG("\n"); + + if (!crtc->lut || !crtc->lut->block) { + DRM_ERROR("Something wrong with the LUT\n"); + return -EINVAL; + } + + /* 16 bits, red, green, blue, unused, total of 64 bits per index */ + /* maybe switch to ioremap_wc once it becomes available. */ + lut = ioremap(dev_priv->fb_phys + crtc->lut->block->start, crtc->lut->block->size); + if (!lut) { + DRM_ERROR("ioremap failed on LUT\n"); + return -EINVAL; + } + + /* 10 bits lut, with 14 bits values. */ + switch (crtc->fb->depth) { + case 15: + /* R5G5B5 */ + for (i = 0; i < 32; i++) { + index = NV50_LUT_INDEX(i, 5); + writew(red[i] >> 2, lut + 8*index + 0); + writew(green[i] >> 2, lut + 8*index + 2); + writew(blue[i] >> 2, lut + 8*index + 4); + } + break; + case 16: + /* R5G6B5 */ + for (i = 0; i < 32; i++) { + index = NV50_LUT_INDEX(i, 5); + writew(red[i] >> 2, lut + 8*index + 0); + writew(blue[i] >> 2, lut + 8*index + 4); + } + + /* Green has an extra bit. */ + for (i = 0; i < 64; i++) { + index = NV50_LUT_INDEX(i, 6); + writew(green[i] >> 2, lut + 8*index + 2); + } + break; + default: + /* R8G8B8 */ + for (i = 0; i < 256; i++) { + writew(red[i] >> 2, lut + 8*i + 0); + writew(green[i] >> 2, lut + 8*i + 2); + writew(blue[i] >> 2, lut + 8*i + 4); + } + break; + } + + crtc->lut->depth = crtc->fb->depth; + + /* Cleaning time. */ + iounmap(lut); + + return 0; +} + +int nv50_lut_create(struct nv50_crtc *crtc) +{ + int rval = 0; + + NV50_DEBUG("\n"); + + if (!crtc) + return -EINVAL; + + crtc->lut = kzalloc(sizeof(struct nv50_lut), GFP_KERNEL); + + if (!crtc->lut) + return -ENOMEM; + + rval = nv50_lut_alloc(crtc); + if (rval != 0) { + goto out; + } + + /* lut will be inited when fb is bound */ + crtc->lut->depth = 0; + + /* function pointers */ + crtc->lut->set = nv50_lut_set; + + return 0; + +out: + if (crtc->lut) + kfree(crtc->lut); + + return rval; +} + +int nv50_lut_destroy(struct nv50_crtc *crtc) +{ + int rval = 0; + + NV50_DEBUG("\n"); + + if (!crtc) + return -EINVAL; + + rval = nv50_lut_free(crtc); + + kfree(crtc->lut); + crtc->lut = NULL; + + if (rval != 0) + return rval; + + return 0; +} diff --git a/linux-core/nv50_lut.h b/linux-core/nv50_lut.h new file mode 100644 index 00000000..06704830 --- /dev/null +++ b/linux-core/nv50_lut.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +#ifndef __NV50_LUT_H__ +#define __NV50_LUT_H__ + +#include "nv50_display.h" + +struct nv50_crtc; + +struct nv50_lut { + struct mem_block *block; + + int depth; /* check against fb to see if it needs to be reuploaded */ + + int (*set) (struct nv50_crtc *crtc, uint16_t *red, uint16_t *green, uint16_t *blue); +}; + +int nv50_lut_create(struct nv50_crtc *crtc); +int nv50_lut_destroy(struct nv50_crtc *crtc); + +#endif /* __NV50_LUT_H__ */ diff --git a/linux-core/nv50_output.c b/linux-core/nv50_output.c new file mode 100644 index 00000000..de0017b9 --- /dev/null +++ b/linux-core/nv50_output.c @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +#include "nv50_output.h" + +int nv50_output_or_offset(struct nv50_output *output) +{ + struct drm_nouveau_private *dev_priv = output->dev->dev_private; + return ffs(dev_priv->dcb_table.entry[output->dcb_entry].or) - 1; +} diff --git a/linux-core/nv50_output.h b/linux-core/nv50_output.h new file mode 100644 index 00000000..ac6d714f --- /dev/null +++ b/linux-core/nv50_output.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +#ifndef __NV50_OUTPUT_H__ +#define __NV50_OUTPUT_H__ + +#include "nv50_crtc.h" + +#define OUTPUT_UNKNOWN 0 +#define OUTPUT_DAC 1 +#define OUTPUT_TMDS 2 +#define OUTPUT_LVDS 3 +#define OUTPUT_TV 4 + +struct nv50_output { + struct list_head item; + + struct drm_device *dev; + int bus; + int dcb_entry; + int type; + + struct nv50_crtc *crtc; + struct nouveau_hw_mode *native_mode; + + int (*validate_mode) (struct nv50_output *output, struct nouveau_hw_mode *mode); + int (*execute_mode) (struct nv50_output *output, bool disconnect); + int (*set_clock_mode) (struct nv50_output *output); + /* this is not a normal modeset call, it is a direct register write, so it's executed immediately */ + int (*set_power_mode) (struct nv50_output *output, int mode); + int (*detect) (struct nv50_output *output); + int (*destroy) (struct nv50_output *output); +}; + +int nv50_output_or_offset(struct nv50_output *output); +int nv50_sor_create(struct drm_device *dev, int dcb_entry); +int nv50_dac_create(struct drm_device *dev, int dcb_entry); + +#endif /* __NV50_OUTPUT_H__ */ diff --git a/linux-core/nv50_sor.c b/linux-core/nv50_sor.c new file mode 100644 index 00000000..41116923 --- /dev/null +++ b/linux-core/nv50_sor.c @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +#include "nv50_output.h" + +static int nv50_sor_validate_mode(struct nv50_output *output, struct nouveau_hw_mode *mode) +{ + NV50_DEBUG("\n"); + + if (mode->clock > 165000) /* no dual link until we figure it out completely */ + return MODE_CLOCK_HIGH; + + if (mode->clock < 25000) + return MODE_CLOCK_LOW; + + if (output->native_mode->hdisplay > 0 && output->native_mode->vdisplay > 0) { + if (mode->hdisplay > output->native_mode->hdisplay || mode->vdisplay > output->native_mode->vdisplay) + return MODE_PANEL; + } + + return MODE_OK; +} + +static int nv50_sor_execute_mode(struct nv50_output *output, bool disconnect) +{ + struct drm_nouveau_private *dev_priv = output->dev->dev_private; + struct nv50_crtc *crtc = output->crtc; + struct nouveau_hw_mode *desired_mode = NULL; + + uint32_t offset = nv50_output_or_offset(output) * 0x40; + + uint32_t mode_ctl = NV50_SOR_MODE_CTRL_OFF; + + NV50_DEBUG("or %d\n", nv50_output_or_offset(output)); + + if (disconnect) { + NV50_DEBUG("Disconnecting SOR\n"); + OUT_MODE(NV50_SOR0_MODE_CTRL + offset, mode_ctl); + return 0; + } + + desired_mode = (crtc->use_native_mode ? crtc->native_mode : crtc->mode); + + if (output->type == OUTPUT_LVDS) { + mode_ctl |= NV50_SOR_MODE_CTRL_LVDS; + } else { + mode_ctl |= NV50_SOR_MODE_CTRL_TMDS; + if (desired_mode->clock > 165000) + mode_ctl |= NV50_SOR_MODE_CTRL_TMDS_DUAL_LINK; + } + + if (crtc->index == 1) + mode_ctl |= NV50_SOR_MODE_CTRL_CRTC1; + else + mode_ctl |= NV50_SOR_MODE_CTRL_CRTC0; + + if (desired_mode->flags & DRM_MODE_FLAG_NHSYNC) + mode_ctl |= NV50_SOR_MODE_CTRL_NHSYNC; + + if (desired_mode->flags & DRM_MODE_FLAG_NVSYNC) + mode_ctl |= NV50_SOR_MODE_CTRL_NVSYNC; + + OUT_MODE(NV50_SOR0_MODE_CTRL + offset, mode_ctl); + + return 0; +} + +static int nv50_sor_set_clock_mode(struct nv50_output *output) +{ + struct drm_nouveau_private *dev_priv = output->dev->dev_private; + struct nv50_crtc *crtc = output->crtc; + + uint32_t limit = 165000; + struct nouveau_hw_mode *hw_mode; + + NV50_DEBUG("or %d\n", nv50_output_or_offset(output)); + + /* We don't yet know what to do, if anything at all. */ + if (output->type == OUTPUT_LVDS) + return 0; + + if (crtc->use_native_mode) + hw_mode = crtc->native_mode; + else + hw_mode = crtc->mode; + + /* 0x70000 was a late addition to nv, mentioned as fixing tmds initialisation on certain gpu's. */ + /* I presume it's some kind of clock setting, but what precisely i do not know. */ + NV_WRITE(NV50_PDISPLAY_SOR_CLK_CLK_CTRL2(nv50_output_or_offset(output)), 0x70000 | ((hw_mode->clock > limit) ? 0x101 : 0)); + + return 0; +} + +static int nv50_sor_set_power_mode(struct nv50_output *output, int mode) +{ + struct drm_nouveau_private *dev_priv = output->dev->dev_private; + uint32_t val; + int or = nv50_output_or_offset(output); + + NV50_DEBUG("or %d\n", nv50_output_or_offset(output)); + + /* wait for it to be done */ + while (NV_READ(NV50_PDISPLAY_SOR_REGS_DPMS_CTRL(or)) & NV50_PDISPLAY_SOR_REGS_DPMS_CTRL_PENDING); + + val = NV_READ(NV50_PDISPLAY_SOR_REGS_DPMS_CTRL(or)); + + if (mode == DRM_MODE_DPMS_ON) + val |= NV50_PDISPLAY_SOR_REGS_DPMS_CTRL_ON; + else + val &= ~NV50_PDISPLAY_SOR_REGS_DPMS_CTRL_ON; + + NV_WRITE(NV50_PDISPLAY_SOR_REGS_DPMS_CTRL(or), val | NV50_PDISPLAY_SOR_REGS_DPMS_CTRL_PENDING); + + return 0; +} + +static int nv50_sor_destroy(struct nv50_output *output) +{ + struct drm_device *dev = output->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv50_display *display = nv50_get_display(dev); + + NV50_DEBUG("\n"); + + if (!display || !output) + return -EINVAL; + + list_del(&output->item); + + kfree(output->native_mode); + if (dev_priv->free_output) + dev_priv->free_output(output); + + return 0; +} + +int nv50_sor_create(struct drm_device *dev, int dcb_entry) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv50_output *output = NULL; + struct nv50_display *display = NULL; + struct dcb_entry *entry = NULL; + int rval = 0; + + NV50_DEBUG("\n"); + + /* This allows the public layer to do it's thing. */ + if (dev_priv->alloc_output) + output = dev_priv->alloc_output(dev); + + if (!output) + return -ENOMEM; + + output->dev = dev; + + display = nv50_get_display(dev); + if (!display) { + rval = -EINVAL; + goto out; + } + + entry = &dev_priv->dcb_table.entry[dcb_entry]; + if (!entry) { + rval = -EINVAL; + goto out; + } + + switch (entry->type) { + case DCB_OUTPUT_TMDS: + output->type = OUTPUT_TMDS; + DRM_INFO("Detected a TMDS output\n"); + break; + case DCB_OUTPUT_LVDS: + output->type = OUTPUT_LVDS; + DRM_INFO("Detected a LVDS output\n"); + break; + default: + rval = -EINVAL; + goto out; + } + + output->dcb_entry = dcb_entry; + output->bus = entry->bus; + + list_add_tail(&output->item, &display->outputs); + + output->native_mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL); + if (!output->native_mode) { + rval = -ENOMEM; + goto out; + } + + /* Set function pointers. */ + output->validate_mode = nv50_sor_validate_mode; + output->execute_mode = nv50_sor_execute_mode; + output->set_clock_mode = nv50_sor_set_clock_mode; + output->set_power_mode = nv50_sor_set_power_mode; + output->detect = NULL; + output->destroy = nv50_sor_destroy; + + /* Some default state, unknown what it precisely means. */ + if (output->type == OUTPUT_TMDS) { + NV_WRITE(NV50_PDISPLAY_SOR_REGS_UNK_00C(nv50_output_or_offset(output)), 0x03010700); + NV_WRITE(NV50_PDISPLAY_SOR_REGS_UNK_010(nv50_output_or_offset(output)), 0x0000152f); + NV_WRITE(NV50_PDISPLAY_SOR_REGS_UNK_014(nv50_output_or_offset(output)), 0x00000000); + NV_WRITE(NV50_PDISPLAY_SOR_REGS_UNK_018(nv50_output_or_offset(output)), 0x00245af8); + } + + return 0; + +out: + if (output->native_mode) + kfree(output->native_mode); + if (dev_priv->free_output) + dev_priv->free_output(output); + return rval; +} diff --git a/linux-core/radeon_atombios.c b/linux-core/radeon_atombios.c new file mode 100644 index 00000000..61732274 --- /dev/null +++ b/linux-core/radeon_atombios.c @@ -0,0 +1,757 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat 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. + * + * Authors: Dave Airlie + * Alex Deucher + */ +#include "drmP.h" +#include "radeon_drm.h" +#include "radeon_drv.h" + +#include "atom.h" +#include "atom-bits.h" + + +union atom_supported_devices { + struct _ATOM_SUPPORTED_DEVICES_INFO info; + struct _ATOM_SUPPORTED_DEVICES_INFO_2 info_2; + struct _ATOM_SUPPORTED_DEVICES_INFO_2d1 info_2d1; +}; + +static inline struct radeon_i2c_bus_rec radeon_lookup_gpio(struct drm_device *dev, uint8_t id) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct atom_context *ctx = dev_priv->mode_info.atom_context; + ATOM_GPIO_I2C_ASSIGMENT gpio; + struct radeon_i2c_bus_rec i2c; + int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info); + struct _ATOM_GPIO_I2C_INFO *i2c_info; + uint16_t data_offset; + + memset(&i2c, 0, sizeof(struct radeon_i2c_bus_rec)); + i2c.valid = false; + + atom_parse_data_header(ctx, index, NULL, NULL, NULL, &data_offset); + + i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset); + + gpio = i2c_info->asGPIO_Info[id]; + + i2c.mask_clk_reg = le16_to_cpu(gpio.usClkMaskRegisterIndex) * 4; + i2c.mask_data_reg = le16_to_cpu(gpio.usDataMaskRegisterIndex) * 4; + i2c.put_clk_reg = le16_to_cpu(gpio.usClkEnRegisterIndex) * 4; + i2c.put_data_reg = le16_to_cpu(gpio.usDataEnRegisterIndex) * 4; + i2c.get_clk_reg = le16_to_cpu(gpio.usClkY_RegisterIndex) * 4; + i2c.get_data_reg = le16_to_cpu(gpio.usDataY_RegisterIndex) * 4; + i2c.a_clk_reg = le16_to_cpu(gpio.usClkA_RegisterIndex) * 4; + i2c.a_data_reg = le16_to_cpu(gpio.usDataA_RegisterIndex) * 4; + i2c.mask_clk_mask = (1 << gpio.ucClkMaskShift); + i2c.mask_data_mask = (1 << gpio.ucDataMaskShift); + i2c.put_clk_mask = (1 << gpio.ucClkEnShift); + i2c.put_data_mask = (1 << gpio.ucDataEnShift); + i2c.get_clk_mask = (1 << gpio.ucClkY_Shift); + i2c.get_data_mask = (1 << gpio.ucDataY_Shift); + i2c.a_clk_mask = (1 << gpio.ucClkA_Shift); + i2c.a_data_mask = (1 << gpio.ucDataA_Shift); + i2c.hw_line = gpio.sucI2cId.sbfAccess.bfI2C_LineMux; + i2c.hw_capable = gpio.sucI2cId.sbfAccess.bfHW_Capable; + i2c.valid = true; + + return i2c; +} + +#if 0 +// TODO fix atom FB/scratch space access +struct edid *radeon_atom_get_edid(struct drm_connector *connector) +{ + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct drm_radeon_private *dev_priv = radeon_connector->base.dev->dev_private; + struct radeon_mode_info *mode_info = &dev_priv->mode_info; + READ_EDID_FROM_HW_I2C_DATA_PS_ALLOCATION edid_param; + int i2c_clock = 50; + int prescale; + unsigned char *raw_edid; + struct edid *edid = NULL; + int index = GetIndexIntoMasterTable(COMMAND, ReadEDIDFromHWAssistedI2C); + + if (!radeon_connector->ddc_bus) + return edid; + + if (!radeon_connector->ddc_bus->rec.hw_capable) + return edid; + + if (info->atomBIOS->fbBase) + raw_edid = (unsigned char *)info->FB + info->atomBIOS->fbBase; + else if (info->atomBIOS->scratchBase) + raw_edid = (unsigned char *)info->atomBIOS->scratchBase; + else + return edid; + + memset(raw_edid, 0, ATOM_EDID_RAW_DATASIZE); + + if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R520) + prescale = (127 << 8) + (mode_info->sclk * 10) / (4 * 127 * i2c_clock); + else if ((dev_priv->flags & RADEON_FAMILY_MASK) < CHIP_R600) + prescale = (((mode_info->sclk * 10)/(4 * 128 * 100) + 1) << 8) + 128; + else + prescale = (mode_info->spll.reference_freq * 10) / i2c_clock; + + edid_param.usPrescale = prescale; + edid_param.usVRAMAddress = 0; + edid_param.ucSlaveAddr = 0xa0; + edid_param.ucLineNumber = radeon_connector->ddc_bus->rec.hw_line; + + atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&edid_param); + + if (raw_edid[1] == 0xff) { + edid = kmalloc(size_of(struct edid), GFP_KERNEL); + *edid = raw_edid; + } + + return edid; + +} +#endif + +static struct radeon_i2c_bus_rec radeon_parse_i2c_record(struct drm_device *dev, ATOM_I2C_RECORD *record) +{ + return radeon_lookup_gpio(dev, record->sucI2cId.bfI2C_LineMux); +} + +static void radeon_atom_apply_quirks(struct drm_device *dev, int index) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_mode_info *mode_info = &dev_priv->mode_info; + + if ((dev->pdev->device == 0x791e) && + (dev->pdev->subsystem_vendor == 0x1043) && + (dev->pdev->subsystem_device == 0x826d)) { + if ((mode_info->bios_connector[index].connector_type == CONNECTOR_HDMI_TYPE_A) && + (mode_info->bios_connector[index].tmds_type == TMDS_LVTMA)) { + mode_info->bios_connector[index].connector_type = CONNECTOR_DVI_D; + } + } + + if ((dev->pdev->device == 0x5653) && + (dev->pdev->subsystem_vendor == 0x1462) && + (dev->pdev->subsystem_device == 0x0291)) { + if (mode_info->bios_connector[index].connector_type == CONNECTOR_LVDS) { + mode_info->bios_connector[index].ddc_i2c.valid = false; + } + } +} + +const int object_connector_convert[] = +{ CONNECTOR_NONE, + CONNECTOR_DVI_I, + CONNECTOR_DVI_I, + CONNECTOR_DVI_D, + CONNECTOR_DVI_D, + CONNECTOR_VGA, + CONNECTOR_CTV, + CONNECTOR_STV, + CONNECTOR_NONE, + CONNECTOR_DIN, + CONNECTOR_SCART, + CONNECTOR_HDMI_TYPE_A, + CONNECTOR_HDMI_TYPE_B, + CONNECTOR_HDMI_TYPE_B, + CONNECTOR_LVDS, + CONNECTOR_DIN, + CONNECTOR_NONE, + CONNECTOR_NONE, + CONNECTOR_NONE, + CONNECTOR_DISPLAY_PORT, +}; + +bool radeon_get_atom_connector_info_from_bios_object_table(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_mode_info *mode_info = &dev_priv->mode_info; + struct atom_context *ctx = mode_info->atom_context; + int index = GetIndexIntoMasterTable(DATA, Object_Header); + uint16_t size, data_offset; + uint8_t frev, crev; + ATOM_CONNECTOR_OBJECT_TABLE *con_obj; + ATOM_INTEGRATED_SYSTEM_INFO_V2 *igp_obj = NULL; + ATOM_OBJECT_HEADER *obj_header; + int i, j; + + atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset); + + if (crev < 2) + return false; + + + obj_header = (ATOM_OBJECT_HEADER *)(ctx->bios + data_offset); + + con_obj = (ATOM_CONNECTOR_OBJECT_TABLE *)(ctx->bios + data_offset + obj_header->usConnectorObjectTableOffset); + DRM_ERROR("Num of objects %d\n", con_obj->ucNumberOfObjects); + + for (i = 0; i < con_obj->ucNumberOfObjects; i++) { + ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *src_dst_table; + ATOM_COMMON_RECORD_HEADER *record; + uint8_t obj_id, num, obj_type; + int record_base; + uint16_t con_obj_id = le16_to_cpu(con_obj->asObjects[i].usObjectID); + + obj_id = (con_obj_id & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; + num = (con_obj_id & ENUM_ID_MASK) >> ENUM_ID_SHIFT; + obj_type = (con_obj_id & OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT; + if (obj_type != GRAPH_OBJECT_TYPE_CONNECTOR) + continue; + + DRM_ERROR("offset is %04x\n", le16_to_cpu(con_obj->asObjects[i].usSrcDstTableOffset)); + src_dst_table = (ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *) + (ctx->bios + data_offset + le16_to_cpu(con_obj->asObjects[i].usSrcDstTableOffset)); + + DRM_ERROR("object id %04x %02x\n", obj_id, src_dst_table->ucNumberOfSrc); + + if ((dev_priv->chip_family == CHIP_RS780) && + (obj_id == CONNECTOR_OBJECT_ID_PCIE_CONNECTOR)) { + uint32_t slot_config, ct; + + // TODO + } else + mode_info->bios_connector[i].connector_type = object_connector_convert[obj_id]; + + if (mode_info->bios_connector[i].connector_type == CONNECTOR_NONE) + mode_info->bios_connector[i].valid = false; + else + mode_info->bios_connector[i].valid = true; + mode_info->bios_connector[i].devices = 0; + + for (j = 0; j < src_dst_table->ucNumberOfSrc; j++) { + uint8_t sobj_id; + + sobj_id = (src_dst_table->usSrcObjectID[j] & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; + DRM_ERROR("src object id %04x %d\n", src_dst_table->usSrcObjectID[j], sobj_id); + + switch(sobj_id) { + case ENCODER_OBJECT_ID_INTERNAL_LVDS: + mode_info->bios_connector[i].devices |= (1 << ATOM_DEVICE_LCD1_INDEX); + break; + case ENCODER_OBJECT_ID_INTERNAL_TMDS1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: + mode_info->bios_connector[i].devices |= (1 << ATOM_DEVICE_DFP1_INDEX); + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: + if (num == 1) + mode_info->bios_connector[i].devices |= (1 << ATOM_DEVICE_DFP1_INDEX); + else + mode_info->bios_connector[i].devices |= (1 << ATOM_DEVICE_DFP2_INDEX); + mode_info->bios_connector[i].tmds_type = TMDS_UNIPHY; + break; + case ENCODER_OBJECT_ID_INTERNAL_TMDS2: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: + mode_info->bios_connector[i].devices |= (1 << ATOM_DEVICE_DFP2_INDEX); + mode_info->bios_connector[i].tmds_type = TMDS_EXT; + break; + case ENCODER_OBJECT_ID_INTERNAL_LVTM1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: + mode_info->bios_connector[i].devices |= (1 << ATOM_DEVICE_DFP3_INDEX); + mode_info->bios_connector[i].tmds_type = TMDS_LVTMA; + break; + case ENCODER_OBJECT_ID_INTERNAL_DAC1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: + if (mode_info->bios_connector[i].connector_type == CONNECTOR_DIN || + mode_info->bios_connector[i].connector_type == CONNECTOR_STV || + mode_info->bios_connector[i].connector_type == CONNECTOR_CTV) + mode_info->bios_connector[i].valid = false; + else + mode_info->bios_connector[i].devices |= (1 << ATOM_DEVICE_CRT1_INDEX); + mode_info->bios_connector[i].dac_type = DAC_PRIMARY; + break; + case ENCODER_OBJECT_ID_INTERNAL_DAC2: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: + if (mode_info->bios_connector[i].connector_type == CONNECTOR_DIN || + mode_info->bios_connector[i].connector_type == CONNECTOR_STV || + mode_info->bios_connector[i].connector_type == CONNECTOR_CTV) + mode_info->bios_connector[i].valid = false; + else + mode_info->bios_connector[i].devices |= (1 << ATOM_DEVICE_CRT2_INDEX); + mode_info->bios_connector[i].dac_type = DAC_TVDAC; + break; + } + } + + record = (ATOM_COMMON_RECORD_HEADER *) + (ctx->bios + data_offset + le16_to_cpu(con_obj->asObjects[i].usRecordOffset)); + record_base = le16_to_cpu(con_obj->asObjects[i].usRecordOffset); + + while (record->ucRecordType > 0 && + record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { + DRM_ERROR("record type %d\n", record->ucRecordType); + + switch(record->ucRecordType) { + case ATOM_I2C_RECORD_TYPE: + mode_info->bios_connector[i].ddc_i2c = radeon_parse_i2c_record(dev, (ATOM_I2C_RECORD *)record); + break; + case ATOM_HPD_INT_RECORD_TYPE: + break; + case ATOM_CONNECTOR_DEVICE_TAG_RECORD_TYPE: + break; + } + record = (ATOM_COMMON_RECORD_HEADER *)((char *)record + record->ucRecordSize); + } + + } + return true; +} + + +bool radeon_get_atom_connector_info_from_bios_connector_table(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_mode_info *mode_info = &dev_priv->mode_info; + struct atom_context *ctx = mode_info->atom_context; + int index = GetIndexIntoMasterTable(DATA, SupportedDevicesInfo); + uint16_t size, data_offset; + uint8_t frev, crev; + uint16_t device_support; + + union atom_supported_devices *supported_devices; + int i,j; + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + // FIXME this should return false for pre-r6xx chips + if (radeon_get_atom_connector_info_from_bios_object_table(dev)) + return true; + + atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset); + + supported_devices = (union atom_supported_devices *)(ctx->bios + data_offset); + + device_support = le16_to_cpu(supported_devices->info.usDeviceSupport); + + for (i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) { + + ATOM_CONNECTOR_INFO_I2C ci = supported_devices->info.asConnInfo[i]; + + if (!(device_support & (1 << i))) { + mode_info->bios_connector[i].valid = false; + continue; + } + + if (i == ATOM_DEVICE_CV_INDEX) { + DRM_DEBUG("Skipping Component Video\n"); + mode_info->bios_connector[i].valid = false; + continue; + } + + if (i == ATOM_DEVICE_TV1_INDEX) { + DRM_DEBUG("Skipping TV Out\n"); + mode_info->bios_connector[i].valid = false; + continue; + } + + mode_info->bios_connector[i].valid = true; + mode_info->bios_connector[i].output_id = ci.sucI2cId.sbfAccess.bfI2C_LineMux; + mode_info->bios_connector[i].devices = 1 << i; + mode_info->bios_connector[i].connector_type = ci.sucConnectorInfo.sbfAccess.bfConnectorType; + + if (mode_info->bios_connector[i].connector_type == CONNECTOR_NONE) { + mode_info->bios_connector[i].valid = false; + continue; + } + + mode_info->bios_connector[i].dac_type = ci.sucConnectorInfo.sbfAccess.bfAssociatedDAC; + + if ((i == ATOM_DEVICE_TV1_INDEX) || + (i == ATOM_DEVICE_TV2_INDEX) || + (i == ATOM_DEVICE_TV1_INDEX)) + mode_info->bios_connector[i].ddc_i2c.valid = false; + else if ((dev_priv->chip_family == CHIP_RS600) || + (dev_priv->chip_family == CHIP_RS690) || + (dev_priv->chip_family == CHIP_RS740)) { + if ((i == ATOM_DEVICE_DFP2_INDEX) || (i == ATOM_DEVICE_DFP3_INDEX)) + mode_info->bios_connector[i].ddc_i2c = + radeon_lookup_gpio(dev, ci.sucI2cId.sbfAccess.bfI2C_LineMux + 1); + else + mode_info->bios_connector[i].ddc_i2c = + radeon_lookup_gpio(dev, ci.sucI2cId.sbfAccess.bfI2C_LineMux); + } else + mode_info->bios_connector[i].ddc_i2c = + radeon_lookup_gpio(dev, ci.sucI2cId.sbfAccess.bfI2C_LineMux); + + if (i == ATOM_DEVICE_DFP1_INDEX) + mode_info->bios_connector[i].tmds_type = TMDS_INT; + else if (i == ATOM_DEVICE_DFP2_INDEX) { + if ((dev_priv->chip_family == CHIP_RS600) || + (dev_priv->chip_family == CHIP_RS690) || + (dev_priv->chip_family == CHIP_RS740)) + mode_info->bios_connector[i].tmds_type = TMDS_DDIA; + else + mode_info->bios_connector[i].tmds_type = TMDS_EXT; + } else if (i == ATOM_DEVICE_DFP3_INDEX) + mode_info->bios_connector[i].tmds_type = TMDS_LVTMA; + else + mode_info->bios_connector[i].tmds_type = TMDS_NONE; + + /* Always set the connector type to VGA for CRT1/CRT2. if they are + * shared with a DVI port, we'll pick up the DVI connector below when we + * merge the outputs + */ + if ((i == ATOM_DEVICE_CRT1_INDEX || i == ATOM_DEVICE_CRT2_INDEX) && + (mode_info->bios_connector[i].connector_type == CONNECTOR_DVI_I || + mode_info->bios_connector[i].connector_type == CONNECTOR_DVI_D || + mode_info->bios_connector[i].connector_type == CONNECTOR_DVI_A)) { + mode_info->bios_connector[i].connector_type = CONNECTOR_VGA; + } + + if (crev > 1) { + ATOM_CONNECTOR_INC_SRC_BITMAP isb = supported_devices->info_2.asIntSrcInfo[i]; + + switch(isb.ucIntSrcBitmap) { + case 0x4: + mode_info->bios_connector[i].hpd_mask = 0x1; + break; + case 0xa: + mode_info->bios_connector[i].hpd_mask = 0x100; + break; + default: + mode_info->bios_connector[i].hpd_mask = 0; + break; + } + } else { + mode_info->bios_connector[i].hpd_mask = 0; + } + + radeon_atom_apply_quirks(dev, i); + } + + /* CRTs/DFPs may share a port */ + for (i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) { + if (!mode_info->bios_connector[i].valid) + continue; + + for (j = 0; j < ATOM_MAX_SUPPORTED_DEVICE; j++) { + if (mode_info->bios_connector[j].valid && (i != j)) { + if (mode_info->bios_connector[i].output_id == + mode_info->bios_connector[j].output_id) { + if (((i == ATOM_DEVICE_DFP1_INDEX) || + (i == ATOM_DEVICE_DFP2_INDEX) || + (i == ATOM_DEVICE_DFP3_INDEX)) && + ((j == ATOM_DEVICE_CRT1_INDEX) || + (j == ATOM_DEVICE_CRT2_INDEX))) { + mode_info->bios_connector[i].dac_type = mode_info->bios_connector[j].dac_type; + mode_info->bios_connector[i].devices |= mode_info->bios_connector[j].devices; + mode_info->bios_connector[i].hpd_mask = mode_info->bios_connector[j].hpd_mask; + mode_info->bios_connector[j].valid = false; + } else if (((j == ATOM_DEVICE_DFP1_INDEX) || + (j == ATOM_DEVICE_DFP2_INDEX) || + (j == ATOM_DEVICE_DFP3_INDEX)) && + ((i == ATOM_DEVICE_CRT1_INDEX) || + (i == ATOM_DEVICE_CRT2_INDEX))) { + mode_info->bios_connector[j].dac_type = mode_info->bios_connector[i].dac_type; + mode_info->bios_connector[j].devices |= mode_info->bios_connector[i].devices; + mode_info->bios_connector[j].hpd_mask = mode_info->bios_connector[i].hpd_mask; + mode_info->bios_connector[i].valid = false; + } + } + } + } + } + + + DRM_DEBUG("BIOS Connector table\n"); + for (i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) { + if (!mode_info->bios_connector[i].valid) + continue; + + DRM_DEBUG("Port %d: ddc_type 0x%x, dac_type %d, tmds_type %d, connector type %d, hpd_mask %d\n", + i, mode_info->bios_connector[i].ddc_i2c.mask_clk_reg, + mode_info->bios_connector[i].dac_type, + mode_info->bios_connector[i].tmds_type, + mode_info->bios_connector[i].connector_type, + mode_info->bios_connector[i].hpd_mask); + } + return true; +} + +union firmware_info { + ATOM_FIRMWARE_INFO info; + ATOM_FIRMWARE_INFO_V1_2 info_12; + ATOM_FIRMWARE_INFO_V1_3 info_13; + ATOM_FIRMWARE_INFO_V1_4 info_14; +}; + +bool radeon_atom_get_clock_info(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_mode_info *mode_info = &dev_priv->mode_info; + int index = GetIndexIntoMasterTable(DATA, FirmwareInfo); + union firmware_info *firmware_info; + uint8_t frev, crev; + struct radeon_pll *p1pll = &mode_info->p1pll; + struct radeon_pll *p2pll = &mode_info->p2pll; + struct radeon_pll *spll = &mode_info->spll; + struct radeon_pll *mpll = &mode_info->mpll; + uint16_t data_offset; + + atom_parse_data_header(mode_info->atom_context, index, NULL, &frev, &crev, &data_offset); + + firmware_info = (union firmware_info *)(mode_info->atom_context->bios + data_offset); + + if (firmware_info) { + /* pixel clocks */ + p1pll->reference_freq = le16_to_cpu(firmware_info->info.usReferenceClock); + p1pll->reference_div = 0; + + p1pll->pll_out_min = le16_to_cpu(firmware_info->info.usMinPixelClockPLL_Output); + p1pll->pll_out_max = le32_to_cpu(firmware_info->info.ulMaxPixelClockPLL_Output); + + if (p1pll->pll_out_min == 0) { + if (radeon_is_avivo(dev_priv)) + p1pll->pll_out_min = 64800; + else + p1pll->pll_out_min = 20000; + } + + p1pll->pll_in_min = le16_to_cpu(firmware_info->info.usMinPixelClockPLL_Input); + p1pll->pll_in_max = le16_to_cpu(firmware_info->info.usMaxPixelClockPLL_Input); + + *p2pll = *p1pll; + + /* system clock */ + spll->reference_freq = le16_to_cpu(firmware_info->info.usReferenceClock); + spll->reference_div = 0; + + spll->pll_out_min = le16_to_cpu(firmware_info->info.usMinEngineClockPLL_Output); + spll->pll_out_max = le32_to_cpu(firmware_info->info.ulMaxEngineClockPLL_Output); + + /* ??? */ + if (spll->pll_out_min == 0) { + if (radeon_is_avivo(dev_priv)) + spll->pll_out_min = 64800; + else + spll->pll_out_min = 20000; + } + + spll->pll_in_min = le16_to_cpu(firmware_info->info.usMinEngineClockPLL_Input); + spll->pll_in_max = le16_to_cpu(firmware_info->info.usMaxEngineClockPLL_Input); + + + /* memory clock */ + mpll->reference_freq = le16_to_cpu(firmware_info->info.usReferenceClock); + mpll->reference_div = 0; + + mpll->pll_out_min = le16_to_cpu(firmware_info->info.usMinMemoryClockPLL_Output); + mpll->pll_out_max = le32_to_cpu(firmware_info->info.ulMaxMemoryClockPLL_Output); + + /* ??? */ + if (mpll->pll_out_min == 0) { + if (radeon_is_avivo(dev_priv)) + mpll->pll_out_min = 64800; + else + mpll->pll_out_min = 20000; + } + + mpll->pll_in_min = le16_to_cpu(firmware_info->info.usMinMemoryClockPLL_Input); + mpll->pll_in_max = le16_to_cpu(firmware_info->info.usMaxMemoryClockPLL_Input); + + mode_info->sclk = le32_to_cpu(firmware_info->info.ulDefaultEngineClock); + mode_info->mclk = le32_to_cpu(firmware_info->info.ulDefaultMemoryClock); + + return true; + } + return false; +} + + +void radeon_atombios_get_tmds_info(struct radeon_encoder *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_mode_info *mode_info = &dev_priv->mode_info; + int index = GetIndexIntoMasterTable(DATA, TMDS_Info); + uint16_t data_offset; + struct _ATOM_TMDS_INFO *tmds_info; + uint8_t frev, crev; + uint16_t maxfreq; + int i; + + atom_parse_data_header(mode_info->atom_context, index, NULL, &frev, &crev, &data_offset); + + tmds_info = (struct _ATOM_TMDS_INFO *)(mode_info->atom_context->bios + data_offset); + + if (tmds_info) { + maxfreq = le16_to_cpu(tmds_info->usMaxFrequency); + for (i = 0; i < 4; i++) { + encoder->tmds_pll[i].freq = le16_to_cpu(tmds_info->asMiscInfo[i].usFrequency); + encoder->tmds_pll[i].value = tmds_info->asMiscInfo[i].ucPLL_ChargePump & 0x3f; + encoder->tmds_pll[i].value |= ((tmds_info->asMiscInfo[i].ucPLL_VCO_Gain & 0x3f) << 6); + encoder->tmds_pll[i].value |= ((tmds_info->asMiscInfo[i].ucPLL_DutyCycle & 0xf) << 12); + encoder->tmds_pll[i].value |= ((tmds_info->asMiscInfo[i].ucPLL_VoltageSwing & 0xf) << 16); + + DRM_DEBUG("TMDS PLL From BIOS %u %x\n", + encoder->tmds_pll[i].freq, + encoder->tmds_pll[i].value); + + if (maxfreq == encoder->tmds_pll[i].freq) { + encoder->tmds_pll[i].freq = 0xffffffff; + break; + } + } + } +} + +union lvds_info { + struct _ATOM_LVDS_INFO info; + struct _ATOM_LVDS_INFO_V12 info_12; +}; + +void radeon_atombios_get_lvds_info(struct radeon_encoder *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_mode_info *mode_info = &dev_priv->mode_info; + int index = GetIndexIntoMasterTable(DATA, LVDS_Info); + uint16_t data_offset; + union lvds_info *lvds_info; + uint8_t frev, crev; + + atom_parse_data_header(mode_info->atom_context, index, NULL, &frev, &crev, &data_offset); + + lvds_info = (union lvds_info *)(mode_info->atom_context->bios + data_offset); + + if (lvds_info) { + encoder->dotclock = le16_to_cpu(lvds_info->info.sLCDTiming.usPixClk) * 10; + encoder->panel_xres = le16_to_cpu(lvds_info->info.sLCDTiming.usHActive); + encoder->panel_yres = le16_to_cpu(lvds_info->info.sLCDTiming.usVActive); + encoder->hblank = le16_to_cpu(lvds_info->info.sLCDTiming.usHBlanking_Time); + encoder->hoverplus = le16_to_cpu(lvds_info->info.sLCDTiming.usHSyncOffset); + encoder->hsync_width = le16_to_cpu(lvds_info->info.sLCDTiming.usHSyncWidth); + + encoder->vblank = le16_to_cpu(lvds_info->info.sLCDTiming.usVBlanking_Time); + encoder->voverplus = le16_to_cpu(lvds_info->info.sLCDTiming.usVSyncOffset); + encoder->vsync_width = le16_to_cpu(lvds_info->info.sLCDTiming.usVSyncWidth); + encoder->panel_pwr_delay = le16_to_cpu(lvds_info->info.usOffDelayInMs); + encoder->lvds_misc = lvds_info->info.ucLVDS_Misc; + encoder->lvds_ss_id = lvds_info->info.ucSS_Id; + } +} + +void radeon_atom_dyn_clk_setup(struct drm_device *dev, int enable) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_mode_info *mode_info = &dev_priv->mode_info; + struct atom_context *ctx = mode_info->atom_context; + DYNAMIC_CLOCK_GATING_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, DynamicClockGating); + + args.ucEnable = enable; + + atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); +} + +void radeon_atom_static_pwrmgt_setup(struct drm_device *dev, int enable) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_mode_info *mode_info = &dev_priv->mode_info; + struct atom_context *ctx = mode_info->atom_context; + ENABLE_ASIC_STATIC_PWR_MGT_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, EnableASIC_StaticPwrMgt); + + args.ucEnable = enable; + + atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); +} + +void radeon_atom_set_engine_clock(struct drm_device *dev, int eng_clock) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_mode_info *mode_info = &dev_priv->mode_info; + struct atom_context *ctx = mode_info->atom_context; + SET_ENGINE_CLOCK_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, SetEngineClock); + + args.ulTargetEngineClock = eng_clock; /* 10 khz */ + + atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); +} + +void radeon_atom_set_memory_clock(struct drm_device *dev, int mem_clock) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_mode_info *mode_info = &dev_priv->mode_info; + struct atom_context *ctx = mode_info->atom_context; + SET_MEMORY_CLOCK_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, SetMemoryClock); + + args.ulTargetMemoryClock = mem_clock; /* 10 khz */ + + atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); +} + +void radeon_atom_initialize_bios_scratch_regs(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t bios_2_scratch, bios_6_scratch; + + if (dev_priv->chip_family >= CHIP_R600) { + bios_2_scratch = RADEON_READ(RADEON_BIOS_0_SCRATCH); + bios_6_scratch = RADEON_READ(RADEON_BIOS_6_SCRATCH); + } else { + bios_2_scratch = RADEON_READ(RADEON_BIOS_0_SCRATCH); + bios_6_scratch = RADEON_READ(RADEON_BIOS_6_SCRATCH); + } + + /* let the bios control the backlight */ + bios_2_scratch &= ~ATOM_S2_VRI_BRIGHT_ENABLE; + + /* tell the bios not to handle mode switching */ + bios_6_scratch |= (ATOM_S6_ACC_BLOCK_DISPLAY_SWITCH | + ATOM_S6_ACC_MODE); + + if (dev_priv->chip_family >= CHIP_R600) { + RADEON_WRITE(R600_BIOS_2_SCRATCH, bios_2_scratch); + RADEON_WRITE(R600_BIOS_6_SCRATCH, bios_6_scratch); + } else { + RADEON_WRITE(RADEON_BIOS_2_SCRATCH, bios_2_scratch); + RADEON_WRITE(RADEON_BIOS_6_SCRATCH, bios_6_scratch); + } + +} + +void +radeon_atom_output_lock(struct drm_encoder *encoder, bool lock) +{ + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t bios_6_scratch; + + if (dev_priv->chip_family >= CHIP_R600) + bios_6_scratch = RADEON_READ(R600_BIOS_6_SCRATCH); + else + bios_6_scratch = RADEON_READ(RADEON_BIOS_6_SCRATCH); + + if (lock) + bios_6_scratch |= ATOM_S6_CRITICAL_STATE; + else + bios_6_scratch &= ~ATOM_S6_CRITICAL_STATE; + + if (dev_priv->chip_family >= CHIP_R600) + RADEON_WRITE(R600_BIOS_6_SCRATCH, bios_6_scratch); + else + RADEON_WRITE(RADEON_BIOS_6_SCRATCH, bios_6_scratch); +} diff --git a/linux-core/radeon_buffer.c b/linux-core/radeon_buffer.c new file mode 100644 index 00000000..f047b1ac --- /dev/null +++ b/linux-core/radeon_buffer.c @@ -0,0 +1,453 @@ +/************************************************************************** + * + * 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; + *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; + + if (!dev_priv->cp_running) + return 0; + + BEGIN_RING(6); + RADEON_PURGE_CACHE(); + RADEON_PURGE_ZCACHE(); + RADEON_WAIT_UNTIL_3D_IDLE(); + ADVANCE_RING(); + COMMIT_RING(); + return 0; +} + +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; +} + +void radeon_emit_copy_blit(struct drm_device * dev, + uint32_t src_offset, + uint32_t dst_offset, + uint32_t pages) +{ + uint32_t cur_pages; + uint32_t stride_bytes = PAGE_SIZE; + drm_radeon_private_t *dev_priv = dev->dev_private; + uint32_t format, pitch; + const uint32_t clip = (0x1fff) | (0x1fff << 16); + uint32_t stride_pixels; + RING_LOCALS; + + if (!dev_priv) + return; + + /* 32-bit copy format */ + format = RADEON_COLOR_FORMAT_ARGB8888; + + /* radeon limited to 16k stride */ + stride_bytes &= 0x3fff; + /* radeon pitch is /64 */ + pitch = stride_bytes / 64; + + stride_pixels = stride_bytes / 4; + + while(pages > 0) { + cur_pages = pages; + if (cur_pages > 8191) + cur_pages = 8191; + pages -= cur_pages; + + /* pages are in Y direction - height + page width in X direction - width */ + BEGIN_RING(10); + OUT_RING(CP_PACKET3(RADEON_CNTL_BITBLT_MULTI, 8)); + OUT_RING(RADEON_GMC_SRC_PITCH_OFFSET_CNTL | + RADEON_GMC_DST_PITCH_OFFSET_CNTL | + RADEON_GMC_SRC_CLIPPING | RADEON_GMC_DST_CLIPPING | + 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); + OUT_RING((pitch << 22) | (src_offset >> 10)); + OUT_RING((pitch << 22) | (dst_offset >> 10)); + OUT_RING(clip); // SRC _SC BOT_RITE + OUT_RING(0); // SC_TOP_LEFT + OUT_RING(clip); // SC_BOT_RITE + + OUT_RING(pages); + OUT_RING(pages); /* x - y */ + OUT_RING(cur_pages | (stride_pixels << 16)); + ADVANCE_RING(); + } + + BEGIN_RING(6); + OUT_RING(CP_PACKET0(RADEON_RB2D_DSTCACHE_CTLSTAT, 0)); + OUT_RING(RADEON_RB2D_DC_FLUSH_ALL); + RADEON_WAIT_UNTIL_2D_IDLE(); + OUT_RING(CP_PACKET2()); + OUT_RING(CP_PACKET2()); + ADVANCE_RING(); + + COMMIT_RING(); + return; +} + +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) +{ + struct drm_device *dev = bo->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + uint32_t old_start, new_start; + + old_start = old_mem->mm_node->start << PAGE_SHIFT; + new_start = new_mem->mm_node->start << PAGE_SHIFT; + + if (old_mem->mem_type == DRM_BO_MEM_VRAM) + old_start += dev_priv->fb_location; + if (old_mem->mem_type == DRM_BO_MEM_TT) + old_start += dev_priv->gart_vm_start; + + if (new_mem->mem_type == DRM_BO_MEM_VRAM) + new_start += dev_priv->fb_location; + if (new_mem->mem_type == DRM_BO_MEM_TT) + new_start += dev_priv->gart_vm_start; + + radeon_emit_copy_blit(bo->dev, + old_start, + new_start, + new_mem->num_pages); + + /* invalidate the chip caches */ + + return drm_bo_move_accel_cleanup(bo, evict, no_wait, 0, + DRM_FENCE_TYPE_EXE, 0, + new_mem); +} + +void radeon_emit_solid_fill(struct drm_device * dev, + uint32_t dst_offset, + uint32_t pages, uint8_t value) +{ + uint32_t cur_pages; + uint32_t stride_bytes = PAGE_SIZE; + drm_radeon_private_t *dev_priv = dev->dev_private; + uint32_t format, pitch; + const uint32_t clip = (0x1fff) | (0x1fff << 16); + uint32_t stride_pixels; + RING_LOCALS; + + if (!dev_priv) + return; + + /* 32-bit copy format */ + format = RADEON_COLOR_FORMAT_ARGB8888; + + /* radeon limited to 16k stride */ + stride_bytes &= 0x3fff; + /* radeon pitch is /64 */ + pitch = stride_bytes / 64; + + stride_pixels = stride_bytes / 4; + + while(pages > 0) { + cur_pages = pages; + if (cur_pages > 8191) + cur_pages = 8191; + pages -= cur_pages; + + /* pages are in Y direction - height + page width in X direction - width */ + BEGIN_RING(8); + OUT_RING(CP_PACKET3(RADEON_CNTL_PAINT_MULTI, 6)); + OUT_RING(RADEON_GMC_DST_PITCH_OFFSET_CNTL | + RADEON_GMC_DST_CLIPPING | + RADEON_GMC_BRUSH_SOLID_COLOR | + (format << 8) | + RADEON_ROP3_P | + RADEON_CLR_CMP_SRC_SOURCE | + RADEON_GMC_CLR_CMP_CNTL_DIS | RADEON_GMC_WR_MSK_DIS); + OUT_RING((pitch << 22) | (dst_offset >> 10)); // PITCH + OUT_RING(0); // SC_TOP_LEFT // DST CLIPPING + OUT_RING(clip); // SC_BOT_RITE + + OUT_RING(0); // COLOR + + OUT_RING(pages); /* x - y */ + OUT_RING(cur_pages | (stride_pixels << 16)); + ADVANCE_RING(); + } + + BEGIN_RING(8); + OUT_RING(CP_PACKET0(RADEON_RB2D_DSTCACHE_CTLSTAT, 0)); + OUT_RING(RADEON_RB2D_DC_FLUSH_ALL); + RADEON_WAIT_UNTIL_2D_IDLE(); + OUT_RING(CP_PACKET2()); + OUT_RING(CP_PACKET2()); + OUT_RING(CP_PACKET2()); + OUT_RING(CP_PACKET2()); + ADVANCE_RING(); + + COMMIT_RING(); + return; +} + +int radeon_move_zero_fill(struct drm_buffer_object * bo, + int evict, int no_wait, struct drm_bo_mem_reg *new_mem) +{ + struct drm_device *dev = bo->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + uint32_t new_start; + + new_start = new_mem->mm_node->start << PAGE_SHIFT; + + if (new_mem->mem_type == DRM_BO_MEM_VRAM) + new_start += dev_priv->fb_location; + + radeon_emit_solid_fill(bo->dev, + new_start, + new_mem->num_pages, 0); + + /* invalidate the chip caches */ + + return drm_bo_move_accel_cleanup(bo, 1, no_wait, 0, + DRM_FENCE_TYPE_EXE, 0, + 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; + + /* if we are flipping into LOCAL memory we have no TTM so create one */ + if (new_mem->mem_type == DRM_BO_MEM_LOCAL) { + tmp_mem.mm_node = NULL; + tmp_mem.proposed_flags = DRM_BO_FLAG_MEM_TT; + + ret = drm_bo_mem_space(bo, &tmp_mem, no_wait); + if (ret) + return ret; + + ret = drm_ttm_bind(bo->ttm, &tmp_mem); + if (ret) + goto out_cleanup; + } + + ret = radeon_move_blit(bo, 1, no_wait, &tmp_mem, &bo->mem); + if (ret) + goto out_cleanup; + + if (new_mem->mem_type == DRM_BO_MEM_LOCAL) { + ret = drm_bo_move_ttm(bo, evict, no_wait, new_mem); + } else { + tmp_mem.mm_node = NULL; + new_mem->mm_node = NULL; + } + +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; +} + +static int radeon_move_vram(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; + struct drm_bo_mem_reg *old_mem = &bo->mem; + int ret; + + /* old - LOCAL memory node bo->mem + tmp - TT type memory node + new - VRAM memory node */ + + tmp_mem = *old_mem; + tmp_mem.mm_node = NULL; + + if (old_mem->mem_type == DRM_BO_MEM_LOCAL) { + tmp_mem.proposed_flags = DRM_BO_FLAG_MEM_TT; + + ret = drm_bo_mem_space(bo, &tmp_mem, no_wait); + if (ret) + return ret; + } + + if (!bo->ttm) { + ret = drm_bo_add_ttm(bo); + if (ret) + goto out_cleanup; + } + + if (old_mem->mem_type == DRM_BO_MEM_LOCAL) { + ret = drm_bo_move_ttm(bo, evict, no_wait, &tmp_mem); + if (ret) + return ret; + } + + ret = radeon_move_blit(bo, 1, no_wait, new_mem, &bo->mem); + if (ret) + goto out_cleanup; + +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_device *dev = bo->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + + if (!dev_priv->cp_running) + goto fallback; + + if (bo->mem.flags & DRM_BO_FLAG_CLEAN) /* need to implement solid fill */ + { + if (radeon_move_zero_fill(bo, evict, no_wait, new_mem)) + return drm_bo_move_zero(bo, evict, no_wait, new_mem); + return 0; + } + + if (new_mem->mem_type == DRM_BO_MEM_VRAM) { + if (radeon_move_vram(bo, evict, no_wait, new_mem)) + goto fallback; + } else { + if (radeon_move_flip(bo, evict, no_wait, new_mem)) + goto fallback; + } + return 0; +fallback: + if (bo->mem.flags & DRM_BO_FLAG_CLEAN) + return drm_bo_move_zero(bo, evict, no_wait, new_mem); + else + return drm_bo_move_memcpy(bo, evict, no_wait, new_mem); +} + + +/* + * i915_evict_flags: + * + * @bo: the buffer object to be evicted + * + * Return the bo flags for a buffer which is not mapped to the hardware. + * These will be placed in proposed_flags so that when the move is + * finished, they'll end up in bo->mem.flags + */ +uint64_t radeon_evict_flags(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; + default: + return DRM_BO_FLAG_MEM_TT | DRM_BO_FLAG_MEM_LOCAL; + } +} diff --git a/linux-core/radeon_combios.c b/linux-core/radeon_combios.c new file mode 100644 index 00000000..f89bf88a --- /dev/null +++ b/linux-core/radeon_combios.c @@ -0,0 +1,1400 @@ +/* + * Copyright 2004 ATI Technologies Inc., Markham, Ontario + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat 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. + * + * Authors: Dave Airlie + * Alex Deucher + */ +#include "drmP.h" +#include "radeon_drm.h" +#include "radeon_drv.h" + +/* old legacy ATI BIOS routines */ + +/* COMBIOS table offsets */ +enum radeon_combios_table_offset +{ + /* absolute offset tables */ + COMBIOS_ASIC_INIT_1_TABLE, + COMBIOS_BIOS_SUPPORT_TABLE, + COMBIOS_DAC_PROGRAMMING_TABLE, + COMBIOS_MAX_COLOR_DEPTH_TABLE, + COMBIOS_CRTC_INFO_TABLE, + COMBIOS_PLL_INFO_TABLE, + COMBIOS_TV_INFO_TABLE, + COMBIOS_DFP_INFO_TABLE, + COMBIOS_HW_CONFIG_INFO_TABLE, + COMBIOS_MULTIMEDIA_INFO_TABLE, + COMBIOS_TV_STD_PATCH_TABLE, + COMBIOS_LCD_INFO_TABLE, + COMBIOS_MOBILE_INFO_TABLE, + COMBIOS_PLL_INIT_TABLE, + COMBIOS_MEM_CONFIG_TABLE, + COMBIOS_SAVE_MASK_TABLE, + COMBIOS_HARDCODED_EDID_TABLE, + COMBIOS_ASIC_INIT_2_TABLE, + COMBIOS_CONNECTOR_INFO_TABLE, + COMBIOS_DYN_CLK_1_TABLE, + COMBIOS_RESERVED_MEM_TABLE, + COMBIOS_EXT_TMDS_INFO_TABLE, + COMBIOS_MEM_CLK_INFO_TABLE, + COMBIOS_EXT_DAC_INFO_TABLE, + COMBIOS_MISC_INFO_TABLE, + COMBIOS_CRT_INFO_TABLE, + COMBIOS_INTEGRATED_SYSTEM_INFO_TABLE, + COMBIOS_COMPONENT_VIDEO_INFO_TABLE, + COMBIOS_FAN_SPEED_INFO_TABLE, + COMBIOS_OVERDRIVE_INFO_TABLE, + COMBIOS_OEM_INFO_TABLE, + COMBIOS_DYN_CLK_2_TABLE, + COMBIOS_POWER_CONNECTOR_INFO_TABLE, + COMBIOS_I2C_INFO_TABLE, + /* relative offset tables */ + COMBIOS_ASIC_INIT_3_TABLE, /* offset from misc info */ + COMBIOS_ASIC_INIT_4_TABLE, /* offset from misc info */ + COMBIOS_ASIC_INIT_5_TABLE, /* offset from misc info */ + COMBIOS_RAM_RESET_TABLE, /* offset from mem config */ + COMBIOS_POWERPLAY_INFO_TABLE, /* offset from mobile info */ + COMBIOS_GPIO_INFO_TABLE, /* offset from mobile info */ + COMBIOS_LCD_DDC_INFO_TABLE, /* offset from mobile info */ + COMBIOS_TMDS_POWER_TABLE, /* offset from mobile info */ + COMBIOS_TMDS_POWER_ON_TABLE, /* offset from tmds power */ + COMBIOS_TMDS_POWER_OFF_TABLE, /* offset from tmds power */ +}; + +enum radeon_combios_ddc +{ + DDC_NONE_DETECTED, + DDC_MONID, + DDC_DVI, + DDC_VGA, + DDC_CRT2, + DDC_LCD, + DDC_GPIO, +}; + +enum radeon_combios_connector +{ + CONNECTOR_NONE_LEGACY, + CONNECTOR_PROPRIETARY_LEGACY, + CONNECTOR_CRT_LEGACY, + CONNECTOR_DVI_I_LEGACY, + CONNECTOR_DVI_D_LEGACY, + CONNECTOR_CTV_LEGACY, + CONNECTOR_STV_LEGACY, + CONNECTOR_UNSUPPORTED_LEGACY +}; + +static uint16_t combios_get_table_offset(struct drm_device *dev, enum radeon_combios_table_offset table) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + int rev; + uint16_t offset = 0, check_offset; + + switch (table) { + /* absolute offset tables */ + case COMBIOS_ASIC_INIT_1_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0xc); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_BIOS_SUPPORT_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x14); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_DAC_PROGRAMMING_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x2a); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_MAX_COLOR_DEPTH_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x2c); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_CRTC_INFO_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x2e); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_PLL_INFO_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x30); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_TV_INFO_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x32); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_DFP_INFO_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x34); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_HW_CONFIG_INFO_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x36); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_MULTIMEDIA_INFO_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x38); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_TV_STD_PATCH_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x3e); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_LCD_INFO_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x40); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_MOBILE_INFO_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x42); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_PLL_INIT_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x46); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_MEM_CONFIG_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x48); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_SAVE_MASK_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x4a); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_HARDCODED_EDID_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x4c); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_ASIC_INIT_2_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x4e); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_CONNECTOR_INFO_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x50); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_DYN_CLK_1_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x52); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_RESERVED_MEM_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x54); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_EXT_TMDS_INFO_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x58); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_MEM_CLK_INFO_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x5a); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_EXT_DAC_INFO_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x5c); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_MISC_INFO_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x5e); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_CRT_INFO_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x60); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_INTEGRATED_SYSTEM_INFO_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x62); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_COMPONENT_VIDEO_INFO_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x64); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_FAN_SPEED_INFO_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x66); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_OVERDRIVE_INFO_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x68); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_OEM_INFO_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x6a); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_DYN_CLK_2_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x6c); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_POWER_CONNECTOR_INFO_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x6e); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_I2C_INFO_TABLE: + check_offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x70); + if (check_offset) + offset = check_offset; + break; + /* relative offset tables */ + case COMBIOS_ASIC_INIT_3_TABLE: /* offset from misc info */ + check_offset = combios_get_table_offset(dev, COMBIOS_MISC_INFO_TABLE); + if (check_offset) { + rev = radeon_bios8(dev_priv, check_offset); + if (rev > 0) { + check_offset = radeon_bios16(dev_priv, check_offset + 0x3); + if (check_offset) + offset = check_offset; + } + } + break; + case COMBIOS_ASIC_INIT_4_TABLE: /* offset from misc info */ + check_offset = combios_get_table_offset(dev, COMBIOS_MISC_INFO_TABLE); + if (check_offset) { + rev = radeon_bios8(dev_priv, check_offset); + if (rev > 0) { + check_offset = radeon_bios16(dev_priv, check_offset + 0x5); + if (check_offset) + offset = check_offset; + } + } + break; + case COMBIOS_ASIC_INIT_5_TABLE: /* offset from misc info */ + check_offset = combios_get_table_offset(dev, COMBIOS_MISC_INFO_TABLE); + if (check_offset) { + rev = radeon_bios8(dev_priv, check_offset); + if (rev == 2) { + check_offset = radeon_bios16(dev_priv, check_offset + 0x9); + if (check_offset) + offset = check_offset; + } + } + break; + case COMBIOS_RAM_RESET_TABLE: /* offset from mem config */ + check_offset = combios_get_table_offset(dev, COMBIOS_MEM_CONFIG_TABLE); + if (check_offset) { + while (radeon_bios8(dev_priv, check_offset++)); + check_offset += 2; + if (check_offset) + offset = check_offset; + } + break; + case COMBIOS_POWERPLAY_INFO_TABLE: /* offset from mobile info */ + check_offset = combios_get_table_offset(dev, COMBIOS_MOBILE_INFO_TABLE); + if (check_offset) { + check_offset = radeon_bios16(dev_priv, check_offset + 0x11); + if (check_offset) + offset = check_offset; + } + break; + case COMBIOS_GPIO_INFO_TABLE: /* offset from mobile info */ + check_offset = combios_get_table_offset(dev, COMBIOS_MOBILE_INFO_TABLE); + if (check_offset) { + check_offset = radeon_bios16(dev_priv, check_offset + 0x13); + if (check_offset) + offset = check_offset; + } + break; + case COMBIOS_LCD_DDC_INFO_TABLE: /* offset from mobile info */ + check_offset = combios_get_table_offset(dev, COMBIOS_MOBILE_INFO_TABLE); + if (check_offset) { + check_offset = radeon_bios16(dev_priv, check_offset + 0x15); + if (check_offset) + offset = check_offset; + } + break; + case COMBIOS_TMDS_POWER_TABLE: /* offset from mobile info */ + check_offset = combios_get_table_offset(dev, COMBIOS_MOBILE_INFO_TABLE); + if (check_offset) { + check_offset = radeon_bios16(dev_priv, check_offset + 0x17); + if (check_offset) + offset = check_offset; + } + break; + case COMBIOS_TMDS_POWER_ON_TABLE: /* offset from tmds power */ + check_offset = combios_get_table_offset(dev, COMBIOS_TMDS_POWER_TABLE); + if (check_offset) { + check_offset = radeon_bios16(dev_priv, check_offset + 0x2); + if (check_offset) + offset = check_offset; + } + break; + case COMBIOS_TMDS_POWER_OFF_TABLE: /* offset from tmds power */ + check_offset = combios_get_table_offset(dev, COMBIOS_TMDS_POWER_TABLE); + if (check_offset) { + check_offset = radeon_bios16(dev_priv, check_offset + 0x4); + if (check_offset) + offset = check_offset; + } + break; + default: + break; + } + + return offset; + +} + +struct radeon_i2c_bus_rec combios_setup_i2c_bus(int ddc_line) +{ + struct radeon_i2c_bus_rec i2c; + + i2c.hw_line = 0; + i2c.hw_capable = false; // actually depends on chip + i2c.mask_clk_mask = RADEON_GPIO_EN_1; + i2c.mask_data_mask = RADEON_GPIO_EN_0; + i2c.a_clk_mask = RADEON_GPIO_A_1; + i2c.a_data_mask = RADEON_GPIO_A_0; + i2c.put_clk_mask = RADEON_GPIO_EN_1; + i2c.put_data_mask = RADEON_GPIO_EN_0; + i2c.get_clk_mask = RADEON_GPIO_Y_1; + i2c.get_data_mask = RADEON_GPIO_Y_0; + if ((ddc_line == RADEON_LCD_GPIO_MASK) || + (ddc_line == RADEON_MDGPIO_EN_REG)) { + i2c.mask_clk_reg = ddc_line; + i2c.mask_data_reg = ddc_line; + i2c.a_clk_reg = ddc_line; + i2c.a_data_reg = ddc_line; + i2c.put_clk_reg = ddc_line; + i2c.put_data_reg = ddc_line; + i2c.get_clk_reg = ddc_line + 4; + i2c.get_data_reg = ddc_line + 4; + } else { + i2c.mask_clk_reg = ddc_line; + i2c.mask_data_reg = ddc_line; + i2c.a_clk_reg = ddc_line; + i2c.a_data_reg = ddc_line; + i2c.put_clk_reg = ddc_line; + i2c.put_data_reg = ddc_line; + i2c.get_clk_reg = ddc_line; + i2c.get_data_reg = ddc_line; + } + + if (ddc_line) + i2c.valid = true; + else + i2c.valid = false; + + return i2c; +} + +bool radeon_combios_get_clock_info(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_mode_info *mode_info = &dev_priv->mode_info; + uint16_t pll_info; + struct radeon_pll *p1pll = &mode_info->p1pll; + struct radeon_pll *p2pll = &mode_info->p2pll; + struct radeon_pll *spll = &mode_info->spll; + struct radeon_pll *mpll = &mode_info->mpll; + int8_t rev; + uint16_t sclk, mclk; + + pll_info = combios_get_table_offset(dev, COMBIOS_PLL_INFO_TABLE); + if (pll_info) { + rev = radeon_bios8(dev_priv, pll_info); + + /* pixel clocks */ + p1pll->reference_freq = radeon_bios16(dev_priv, pll_info + 0xe); + p1pll->reference_div = radeon_bios16(dev_priv, pll_info + 0x10); + p1pll->pll_out_min = radeon_bios32(dev_priv, pll_info + 0x12); + p1pll->pll_out_max = radeon_bios32(dev_priv, pll_info + 0x16); + + if (rev > 9) { + p1pll->pll_in_min = radeon_bios32(dev_priv, pll_info + 0x36); + p1pll->pll_in_max = radeon_bios32(dev_priv, pll_info + 0x3a); + } else { + p1pll->pll_in_min = 40; + p1pll->pll_in_max = 500; + } + *p2pll = *p1pll; + + /* system clock */ + spll->reference_freq = radeon_bios16(dev_priv, pll_info + 0x1a); + spll->reference_div = radeon_bios16(dev_priv, pll_info + 0x1c); + spll->pll_out_min = radeon_bios32(dev_priv, pll_info + 0x1e); + spll->pll_out_max = radeon_bios32(dev_priv, pll_info + 0x22); + + if (rev > 10) { + spll->pll_in_min = radeon_bios32(dev_priv, pll_info + 0x48); + spll->pll_in_max = radeon_bios32(dev_priv, pll_info + 0x4c); + } else { + /* ??? */ + spll->pll_in_min = 40; + spll->pll_in_max = 500; + } + + /* memory clock */ + mpll->reference_freq = radeon_bios16(dev_priv, pll_info + 0x26); + mpll->reference_div = radeon_bios16(dev_priv, pll_info + 0x28); + mpll->pll_out_min = radeon_bios32(dev_priv, pll_info + 0x2a); + mpll->pll_out_max = radeon_bios32(dev_priv, pll_info + 0x2e); + + if (rev > 10) { + mpll->pll_in_min = radeon_bios32(dev_priv, pll_info + 0x5a); + mpll->pll_in_max = radeon_bios32(dev_priv, pll_info + 0x5e); + } else { + /* ??? */ + mpll->pll_in_min = 40; + mpll->pll_in_max = 500; + } + + /* default sclk/mclk */ + sclk = radeon_bios16(dev_priv, pll_info + 0x8); + mclk = radeon_bios16(dev_priv, pll_info + 0xa); + if (sclk == 0) + sclk = 200; + if (mclk == 0) + mclk = 200; + + mode_info->sclk = sclk; + mode_info->mclk = mclk; + + return true; + } + return false; +} + +bool radeon_combios_get_primary_dac_info(struct radeon_encoder *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + uint16_t dac_info; + uint8_t rev, bg, dac; + + /* check CRT table */ + dac_info = combios_get_table_offset(dev, COMBIOS_CRT_INFO_TABLE); + if (dac_info) { + rev = radeon_bios8(dev_priv, dac_info) & 0x3; + if (rev < 2) { + bg = radeon_bios8(dev_priv, dac_info + 0x2) & 0xf; + dac = (radeon_bios8(dev_priv, dac_info + 0x2) >> 4) & 0xf; + encoder->ps2_pdac_adj = (bg << 8) | (dac); + + return true; + } else { + bg = radeon_bios8(dev_priv, dac_info + 0x2) & 0xf; + dac = radeon_bios8(dev_priv, dac_info + 0x3) & 0xf; + encoder->ps2_pdac_adj = (bg << 8) | (dac); + + return true; + } + + } + + return false; +} + +bool radeon_combios_get_tv_dac_info(struct radeon_encoder *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + uint16_t dac_info; + uint8_t rev, bg, dac; + + /* first check TV table */ + dac_info = combios_get_table_offset(dev, COMBIOS_TV_INFO_TABLE); + if (dac_info) { + rev = radeon_bios8(dev_priv, dac_info + 0x3); + if (rev > 4) { + bg = radeon_bios8(dev_priv, dac_info + 0xc) & 0xf; + dac = radeon_bios8(dev_priv, dac_info + 0xd) & 0xf; + encoder->ps2_tvdac_adj = (bg << 16) | (dac << 20); + + bg = radeon_bios8(dev_priv, dac_info + 0xe) & 0xf; + dac = radeon_bios8(dev_priv, dac_info + 0xf) & 0xf; + encoder->pal_tvdac_adj = (bg << 16) | (dac << 20); + + bg = radeon_bios8(dev_priv, dac_info + 0x10) & 0xf; + dac = radeon_bios8(dev_priv, dac_info + 0x11) & 0xf; + encoder->ntsc_tvdac_adj = (bg << 16) | (dac << 20); + + return true; + } else if (rev > 1) { + bg = radeon_bios8(dev_priv, dac_info + 0xc) & 0xf; + dac = (radeon_bios8(dev_priv, dac_info + 0xc) >> 4) & 0xf; + encoder->ps2_tvdac_adj = (bg << 16) | (dac << 20); + + bg = radeon_bios8(dev_priv, dac_info + 0xd) & 0xf; + dac = (radeon_bios8(dev_priv, dac_info + 0xd) >> 4) & 0xf; + encoder->pal_tvdac_adj = (bg << 16) | (dac << 20); + + bg = radeon_bios8(dev_priv, dac_info + 0xe) & 0xf; + dac = (radeon_bios8(dev_priv, dac_info + 0xe) >> 4) & 0xf; + encoder->ntsc_tvdac_adj = (bg << 16) | (dac << 20); + + return true; + } + } + + /* then check CRT table */ + dac_info = combios_get_table_offset(dev, COMBIOS_CRT_INFO_TABLE); + if (dac_info) { + rev = radeon_bios8(dev_priv, dac_info) & 0x3; + if (rev < 2) { + bg = radeon_bios8(dev_priv, dac_info + 0x3) & 0xf; + dac = (radeon_bios8(dev_priv, dac_info + 0x3) >> 4) & 0xf; + encoder->ps2_tvdac_adj = (bg << 16) | (dac << 20); + encoder->pal_tvdac_adj = encoder->ps2_tvdac_adj; + encoder->ntsc_tvdac_adj = encoder->ps2_tvdac_adj; + + return true; + } else { + bg = radeon_bios8(dev_priv, dac_info + 0x4) & 0xf; + dac = radeon_bios8(dev_priv, dac_info + 0x5) & 0xf; + encoder->ps2_tvdac_adj = (bg << 16) | (dac << 20); + encoder->pal_tvdac_adj = encoder->ps2_tvdac_adj; + encoder->ntsc_tvdac_adj = encoder->ps2_tvdac_adj; + + return true; + } + + } + + return false; +} + +bool radeon_combios_get_tv_info(struct radeon_encoder *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + uint16_t tv_info; + + tv_info = combios_get_table_offset(dev, COMBIOS_TV_INFO_TABLE); + if (tv_info) { + if (radeon_bios8(dev_priv, tv_info + 6) == 'T') { + switch (radeon_bios8(dev_priv, tv_info + 7) & 0xf) { + case 1: + encoder->tv_std = TV_STD_NTSC; + DRM_INFO("Default TV standard: NTSC\n"); + break; + case 2: + encoder->tv_std = TV_STD_PAL; + DRM_INFO("Default TV standard: PAL\n"); + break; + case 3: + encoder->tv_std = TV_STD_PAL_M; + DRM_INFO("Default TV standard: PAL-M\n"); + break; + case 4: + encoder->tv_std = TV_STD_PAL_60; + DRM_INFO("Default TV standard: PAL-60\n"); + break; + case 5: + encoder->tv_std = TV_STD_NTSC_J; + DRM_INFO("Default TV standard: NTSC-J\n"); + break; + case 6: + encoder->tv_std = TV_STD_SCART_PAL; + DRM_INFO("Default TV standard: SCART-PAL\n"); + break; + default: + encoder->tv_std = TV_STD_NTSC; + DRM_INFO("Unknown TV standard; defaulting to NTSC\n"); + break; + } + + switch ((radeon_bios8(dev_priv, tv_info + 9) >> 2) & 0x3) { + case 0: + DRM_INFO("29.498928713 MHz TV ref clk\n"); + break; + case 1: + DRM_INFO("28.636360000 MHz TV ref clk\n"); + break; + case 2: + DRM_INFO("14.318180000 MHz TV ref clk\n"); + break; + case 3: + DRM_INFO("27.000000000 MHz TV ref clk\n"); + break; + default: + break; + } + return true; + } + } + return false; +} + +bool radeon_combios_get_lvds_info(struct radeon_encoder *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + uint16_t lcd_info; + uint32_t panel_setup; + char stmp[30]; + int tmp, i; + + lcd_info = combios_get_table_offset(dev, COMBIOS_LCD_INFO_TABLE); + + if (lcd_info) { + for (i = 0; i < 24; i++) + stmp[i] = radeon_bios8(dev_priv, lcd_info + i + 1); + stmp[24] = 0; + + DRM_INFO("Panel ID String: %s\n", stmp); + + encoder->panel_xres = radeon_bios16(dev_priv, lcd_info + 25); + encoder->panel_yres = radeon_bios16(dev_priv, lcd_info + 27); + + DRM_INFO("Panel Size %dx%d\n", encoder->panel_xres, encoder->panel_yres); + + encoder->panel_vcc_delay = radeon_bios16(dev_priv, lcd_info + 44); + if (encoder->panel_vcc_delay > 2000 || encoder->panel_vcc_delay < 0) + encoder->panel_vcc_delay = 2000; + + encoder->panel_pwr_delay = radeon_bios16(dev_priv, lcd_info + 0x24); + encoder->panel_digon_delay = radeon_bios16(dev_priv, lcd_info + 0x38) & 0xf; + encoder->panel_blon_delay = (radeon_bios16(dev_priv, lcd_info + 0x38) >> 4) & 0xf; + + encoder->panel_ref_divider = radeon_bios16(dev_priv, lcd_info + 46); + encoder->panel_post_divider = radeon_bios8(dev_priv, lcd_info + 48); + encoder->panel_fb_divider = radeon_bios16(dev_priv, lcd_info + 49); + if ((encoder->panel_ref_divider != 0) && + (encoder->panel_fb_divider > 3)) + encoder->use_bios_dividers = true; + + panel_setup = radeon_bios32(dev_priv, lcd_info + 0x39); + encoder->lvds_gen_cntl = 0xff00; + if (panel_setup & 0x1) + encoder->lvds_gen_cntl |= RADEON_LVDS_PANEL_FORMAT; + + if ((panel_setup >> 4) & 0x1) + encoder->lvds_gen_cntl |= RADEON_LVDS_PANEL_TYPE; + + switch ((panel_setup >> 8) & 0x7) { + case 0: + encoder->lvds_gen_cntl |= RADEON_LVDS_NO_FM; + break; + case 1: + encoder->lvds_gen_cntl |= RADEON_LVDS_2_GREY; + break; + case 2: + encoder->lvds_gen_cntl |= RADEON_LVDS_4_GREY; + break; + default: + break; + } + + if ((panel_setup >> 16) & 0x1) + encoder->lvds_gen_cntl |= RADEON_LVDS_FP_POL_LOW; + + if ((panel_setup >> 17) & 0x1) + encoder->lvds_gen_cntl |= RADEON_LVDS_LP_POL_LOW; + + if ((panel_setup >> 18) & 0x1) + encoder->lvds_gen_cntl |= RADEON_LVDS_DTM_POL_LOW; + + if ((panel_setup >> 23) & 0x1) + encoder->lvds_gen_cntl |= RADEON_LVDS_BL_CLK_SEL; + + encoder->lvds_gen_cntl |= (panel_setup & 0xf0000000); + + + for (i = 0; i < 32; i++) { + tmp = radeon_bios16(dev_priv, lcd_info + 64 + i * 2); + if (tmp == 0) break; + + if ((radeon_bios16(dev_priv, tmp) == encoder->panel_xres) && + (radeon_bios16(dev_priv, tmp + 2) == encoder->panel_yres)) { + encoder->hblank = (radeon_bios16(dev_priv, tmp + 17) - + radeon_bios16(dev_priv, tmp + 19)) * 8; + encoder->hoverplus = (radeon_bios16(dev_priv, tmp + 21) - + radeon_bios16(dev_priv, tmp + 19) - 1) * 8; + encoder->hsync_width = radeon_bios8(dev_priv, tmp + 23) * 8; + + encoder->vblank = (radeon_bios16(dev_priv, tmp + 24) - + radeon_bios16(dev_priv, tmp + 26)); + encoder->voverplus = ((radeon_bios16(dev_priv, tmp + 28) & 0x7ff) - + radeon_bios16(dev_priv, tmp + 26)); + encoder->vsync_width = ((radeon_bios16(dev_priv, tmp + 28) & 0xf800) >> 11); + encoder->dotclock = radeon_bios16(dev_priv, tmp + 9) * 10; + encoder->flags = 0; + } + } + return true; + } + DRM_INFO("No panel info found in BIOS\n"); + return false; + +} + +bool radeon_combios_get_tmds_info(struct radeon_encoder *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + uint16_t tmds_info; + int i, n; + uint8_t ver; + + tmds_info = combios_get_table_offset(dev, COMBIOS_DFP_INFO_TABLE); + + if (tmds_info) { + ver = radeon_bios8(dev_priv, tmds_info); + DRM_INFO("DFP table revision: %d\n", ver); + if (ver == 3) { + n = radeon_bios8(dev_priv, tmds_info + 5) + 1; + if (n > 4) + n = 4; + for (i = 0; i < n; i++) { + encoder->tmds_pll[i].value = radeon_bios32(dev_priv, tmds_info + i * 10 + 0x08); + encoder->tmds_pll[i].freq = radeon_bios16(dev_priv, tmds_info + i * 10 + 0x10); + } + return true; + } else if (ver == 4) { + int stride = 0; + n = radeon_bios8(dev_priv, tmds_info + 5) + 1; + if (n > 4) + n = 4; + for (i = 0; i < n; i++) { + encoder->tmds_pll[i].value = radeon_bios32(dev_priv, tmds_info + stride + 0x08); + encoder->tmds_pll[i].freq = radeon_bios16(dev_priv, tmds_info + stride + 0x10); + if (i == 0) + stride += 10; + else + stride += 6; + } + return true; + } + } + + DRM_INFO("No TMDS info found in BIOS\n"); + return false; +} + +void radeon_combios_get_ext_tmds_info(struct radeon_encoder *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + uint16_t ext_tmds_info; + uint8_t ver; + + ext_tmds_info = combios_get_table_offset(dev, COMBIOS_EXT_TMDS_INFO_TABLE); + if (ext_tmds_info) { + ver = radeon_bios8(dev_priv, ext_tmds_info); + DRM_INFO("External TMDS Table revision: %d\n", ver); + // TODO + } +} + +static void radeon_apply_legacy_quirks(struct drm_device *dev, int bios_index) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_mode_info *mode_info = &dev_priv->mode_info; + + /* XPRESS DDC quirks */ + if ((dev_priv->chip_family == CHIP_RS400 || + dev_priv->chip_family == CHIP_RS480) && + mode_info->bios_connector[bios_index].ddc_i2c.mask_clk_reg == RADEON_GPIO_CRT2_DDC) { + mode_info->bios_connector[bios_index].ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_MONID); + } else if ((dev_priv->chip_family == CHIP_RS400 || + dev_priv->chip_family == CHIP_RS480) && + mode_info->bios_connector[bios_index].ddc_i2c.mask_clk_reg == RADEON_GPIO_MONID) { + mode_info->bios_connector[bios_index].ddc_i2c.valid = true; + mode_info->bios_connector[bios_index].ddc_i2c.mask_clk_mask = (0x20 << 8); + mode_info->bios_connector[bios_index].ddc_i2c.mask_data_mask = 0x80; + mode_info->bios_connector[bios_index].ddc_i2c.a_clk_mask = (0x20 << 8); + mode_info->bios_connector[bios_index].ddc_i2c.a_data_mask = 0x80; + mode_info->bios_connector[bios_index].ddc_i2c.put_clk_mask = (0x20 << 8); + mode_info->bios_connector[bios_index].ddc_i2c.put_data_mask = 0x80; + mode_info->bios_connector[bios_index].ddc_i2c.get_clk_mask = (0x20 << 8); + mode_info->bios_connector[bios_index].ddc_i2c.get_data_mask = 0x80; + mode_info->bios_connector[bios_index].ddc_i2c.mask_clk_reg = RADEON_GPIOPAD_MASK; + mode_info->bios_connector[bios_index].ddc_i2c.mask_data_reg = RADEON_GPIOPAD_MASK; + mode_info->bios_connector[bios_index].ddc_i2c.a_clk_reg = RADEON_GPIOPAD_A; + mode_info->bios_connector[bios_index].ddc_i2c.a_data_reg = RADEON_GPIOPAD_A; + mode_info->bios_connector[bios_index].ddc_i2c.put_clk_reg = RADEON_GPIOPAD_EN; + mode_info->bios_connector[bios_index].ddc_i2c.put_data_reg = RADEON_GPIOPAD_EN; + mode_info->bios_connector[bios_index].ddc_i2c.get_clk_reg = RADEON_LCD_GPIO_Y_REG; + mode_info->bios_connector[bios_index].ddc_i2c.get_data_reg = RADEON_LCD_GPIO_Y_REG; + } + + /* Certain IBM chipset RN50s have a BIOS reporting two VGAs, + one with VGA DDC and one with CRT2 DDC. - kill the CRT2 DDC one */ + if (dev->pdev->device == 0x515e && + dev->pdev->subsystem_vendor == 0x1014) { + if (mode_info->bios_connector[bios_index].connector_type == CONNECTOR_VGA && + mode_info->bios_connector[bios_index].ddc_i2c.mask_clk_reg == RADEON_GPIO_CRT2_DDC) { + mode_info->bios_connector[bios_index].valid = false; + } + } + + /* Some RV100 cards with 2 VGA ports show up with DVI+VGA */ + if (dev->pdev->device == 0x5159 && + dev->pdev->subsystem_vendor == 0x1002 && + dev->pdev->subsystem_device == 0x013a) { + if (mode_info->bios_connector[bios_index].connector_type == CONNECTOR_DVI_I) + mode_info->bios_connector[bios_index].connector_type = CONNECTOR_VGA; + + } + + /* X300 card with extra non-existent DVI port */ + if (dev->pdev->device == 0x5B60 && + dev->pdev->subsystem_vendor == 0x17af && + dev->pdev->subsystem_device == 0x201e && + bios_index == 2) { + if (mode_info->bios_connector[bios_index].connector_type == CONNECTOR_DVI_I) + mode_info->bios_connector[bios_index].valid = false; + } + +} + +bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_mode_info *mode_info = &dev_priv->mode_info; + uint32_t conn_info, entry; + uint16_t tmp; + enum radeon_combios_ddc ddc_type; + enum radeon_combios_connector connector_type; + int i; + + DRM_DEBUG("\n"); + conn_info = combios_get_table_offset(dev, COMBIOS_CONNECTOR_INFO_TABLE); + if (conn_info) { + for (i = 0; i < 4; i++) { + entry = conn_info + 2 + i * 2; + + if (!radeon_bios16(dev_priv, entry)) + break; + + mode_info->bios_connector[i].valid = true; + + tmp = radeon_bios16(dev_priv, entry); + + connector_type = (tmp >> 12) & 0xf; + mode_info->bios_connector[i].connector_type = connector_type; + + switch(connector_type) { + case CONNECTOR_PROPRIETARY_LEGACY: + mode_info->bios_connector[i].connector_type = CONNECTOR_DVI_D; + break; + case CONNECTOR_CRT_LEGACY: + mode_info->bios_connector[i].connector_type = CONNECTOR_VGA; + break; + case CONNECTOR_DVI_I_LEGACY: + mode_info->bios_connector[i].connector_type = CONNECTOR_DVI_I; + break; + case CONNECTOR_DVI_D_LEGACY: + mode_info->bios_connector[i].connector_type = CONNECTOR_DVI_D; + break; + case CONNECTOR_CTV_LEGACY: + mode_info->bios_connector[i].connector_type = CONNECTOR_CTV; + break; + case CONNECTOR_STV_LEGACY: + mode_info->bios_connector[i].connector_type = CONNECTOR_STV; + break; + default: + DRM_ERROR("Unknown connector type: %d\n", connector_type); + mode_info->bios_connector[i].valid = false; + break; + } + + mode_info->bios_connector[i].ddc_i2c.valid = false; + + ddc_type = (tmp >> 8) & 0xf; + switch (ddc_type) { + case DDC_MONID: + mode_info->bios_connector[i].ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_MONID); + break; + case DDC_DVI: + mode_info->bios_connector[i].ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC); + break; + case DDC_VGA: + mode_info->bios_connector[i].ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC); + break; + case DDC_CRT2: + mode_info->bios_connector[i].ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_CRT2_DDC); + break; + default: + break; + } + + if (tmp & 0x1) + mode_info->bios_connector[i].dac_type = DAC_TVDAC; + else + mode_info->bios_connector[i].dac_type = DAC_PRIMARY; + + if ((dev_priv->chip_family == CHIP_RS300) || + (dev_priv->chip_family == CHIP_RS400) || + (dev_priv->chip_family == CHIP_RS480)) + mode_info->bios_connector[i].dac_type = DAC_TVDAC; + + if ((tmp >> 4) & 0x1) + mode_info->bios_connector[i].tmds_type = TMDS_EXT; + else + mode_info->bios_connector[i].tmds_type = TMDS_INT; + + radeon_apply_legacy_quirks(dev, i); + } + } else { + uint16_t tmds_info = combios_get_table_offset(dev, COMBIOS_DFP_INFO_TABLE); + if (tmds_info) { + DRM_DEBUG("Found DFP table, assuming DVI connector\n"); + + mode_info->bios_connector[0].valid = true; + mode_info->bios_connector[0].connector_type = CONNECTOR_DVI_I; + mode_info->bios_connector[0].dac_type = DAC_PRIMARY; + mode_info->bios_connector[0].tmds_type = TMDS_INT; + mode_info->bios_connector[0].ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC); + } else { + DRM_DEBUG("No connector info found\n"); + return false; + } + } + + if (dev_priv->flags & RADEON_IS_MOBILITY || + dev_priv->chip_family == CHIP_RS400 || + dev_priv->chip_family == CHIP_RS480) { + uint16_t lcd_info = combios_get_table_offset(dev, COMBIOS_LCD_INFO_TABLE); + if (lcd_info) { + uint16_t lcd_ddc_info = combios_get_table_offset(dev, COMBIOS_LCD_DDC_INFO_TABLE); + + mode_info->bios_connector[4].valid = true; + mode_info->bios_connector[4].connector_type = CONNECTOR_LVDS; + mode_info->bios_connector[4].dac_type = DAC_NONE; + mode_info->bios_connector[4].tmds_type = TMDS_NONE; + mode_info->bios_connector[4].ddc_i2c.valid = false; + + if (lcd_ddc_info) { + ddc_type = radeon_bios8(dev_priv, lcd_ddc_info + 2); + switch(ddc_type) { + case DDC_MONID: + mode_info->bios_connector[4].ddc_i2c = + combios_setup_i2c_bus(RADEON_GPIO_MONID); + break; + case DDC_DVI: + mode_info->bios_connector[4].ddc_i2c = + combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC); + break; + case DDC_VGA: + mode_info->bios_connector[4].ddc_i2c = + combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC); + break; + case DDC_CRT2: + mode_info->bios_connector[4].ddc_i2c = + combios_setup_i2c_bus(RADEON_GPIO_CRT2_DDC); + break; + case DDC_LCD: + mode_info->bios_connector[4].ddc_i2c = + combios_setup_i2c_bus(RADEON_LCD_GPIO_MASK); + mode_info->bios_connector[4].ddc_i2c.mask_clk_mask = + radeon_bios32(dev_priv, lcd_ddc_info + 3); + mode_info->bios_connector[4].ddc_i2c.mask_data_mask = + radeon_bios32(dev_priv, lcd_ddc_info + 7); + mode_info->bios_connector[4].ddc_i2c.a_clk_mask = + radeon_bios32(dev_priv, lcd_ddc_info + 3); + mode_info->bios_connector[4].ddc_i2c.a_data_mask = + radeon_bios32(dev_priv, lcd_ddc_info + 7); + mode_info->bios_connector[4].ddc_i2c.put_clk_mask = + radeon_bios32(dev_priv, lcd_ddc_info + 3); + mode_info->bios_connector[4].ddc_i2c.put_data_mask = + radeon_bios32(dev_priv, lcd_ddc_info + 7); + mode_info->bios_connector[4].ddc_i2c.get_clk_mask = + radeon_bios32(dev_priv, lcd_ddc_info + 3); + mode_info->bios_connector[4].ddc_i2c.get_data_mask = + radeon_bios32(dev_priv, lcd_ddc_info + 7); + break; + case DDC_GPIO: + mode_info->bios_connector[4].ddc_i2c = + combios_setup_i2c_bus(RADEON_MDGPIO_EN_REG); + mode_info->bios_connector[4].ddc_i2c.mask_clk_mask = + radeon_bios32(dev_priv, lcd_ddc_info + 3); + mode_info->bios_connector[4].ddc_i2c.mask_data_mask = + radeon_bios32(dev_priv, lcd_ddc_info + 7); + mode_info->bios_connector[4].ddc_i2c.a_clk_mask = + radeon_bios32(dev_priv, lcd_ddc_info + 3); + mode_info->bios_connector[4].ddc_i2c.a_data_mask = + radeon_bios32(dev_priv, lcd_ddc_info + 7); + mode_info->bios_connector[4].ddc_i2c.put_clk_mask = + radeon_bios32(dev_priv, lcd_ddc_info + 3); + mode_info->bios_connector[4].ddc_i2c.put_data_mask = + radeon_bios32(dev_priv, lcd_ddc_info + 7); + mode_info->bios_connector[4].ddc_i2c.get_clk_mask = + radeon_bios32(dev_priv, lcd_ddc_info + 3); + mode_info->bios_connector[4].ddc_i2c.get_data_mask = + radeon_bios32(dev_priv, lcd_ddc_info + 7); + break; + default: + break; + } + DRM_DEBUG("LCD DDC Info Table found!\n"); + } + } else + mode_info->bios_connector[4].ddc_i2c.valid = false; + } + + /* check TV table */ + if (dev_priv->chip_family != CHIP_R100 && + dev_priv->chip_family != CHIP_R200) { + uint32_t tv_info = combios_get_table_offset(dev, COMBIOS_TV_INFO_TABLE); + if (tv_info) { + if (radeon_bios8(dev_priv, tv_info + 6) == 'T') { + mode_info->bios_connector[5].valid = true; + mode_info->bios_connector[5].connector_type = CONNECTOR_DIN; + mode_info->bios_connector[5].dac_type = DAC_TVDAC; + mode_info->bios_connector[5].tmds_type = TMDS_NONE; + mode_info->bios_connector[5].ddc_i2c.valid = false; + } + } + } + + + DRM_DEBUG("BIOS Connector table\n"); + for (i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) { + if (!mode_info->bios_connector[i].valid) + continue; + + DRM_DEBUG("Port %d: ddc_type 0x%x, dac_type %d, tmds_type %d, connector type %d, hpd_mask %d\n", + i, mode_info->bios_connector[i].ddc_i2c.mask_clk_reg, + mode_info->bios_connector[i].dac_type, + mode_info->bios_connector[i].tmds_type, + mode_info->bios_connector[i].connector_type, + mode_info->bios_connector[i].hpd_mask); + } + + return true; +} + +static void combios_parse_mmio_table(struct drm_device *dev, uint16_t offset) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + if (offset) { + while (radeon_bios16(dev_priv, offset)) { + uint16_t cmd = ((radeon_bios16(dev_priv, offset) & 0xe000) >> 13); + uint32_t addr = (radeon_bios16(dev_priv, offset) & 0x1fff); + uint32_t val, and_mask, or_mask; + uint32_t tmp; + + offset += 2; + switch (cmd) { + case 0: + val = radeon_bios32(dev_priv, offset); + offset += 4; + RADEON_WRITE(addr, val); + break; + case 1: + val = radeon_bios32(dev_priv, offset); + offset += 4; + RADEON_WRITE(addr, val); + break; + case 2: + and_mask = radeon_bios32(dev_priv, offset); + offset += 4; + or_mask = radeon_bios32(dev_priv, offset); + offset += 4; + tmp = RADEON_READ(addr); + tmp &= and_mask; + tmp |= or_mask; + RADEON_WRITE(addr, tmp); + break; + case 3: + and_mask = radeon_bios32(dev_priv, offset); + offset += 4; + or_mask = radeon_bios32(dev_priv, offset); + offset += 4; + tmp = RADEON_READ(addr); + tmp &= and_mask; + tmp |= or_mask; + RADEON_WRITE(addr, tmp); + break; + case 4: + val = radeon_bios16(dev_priv, offset); + offset += 2; + udelay(val); + break; + case 5: + val = radeon_bios16(dev_priv, offset); + offset += 2; + switch (addr) { + case 8: + while (val--) { + if (!(RADEON_READ_PLL(dev_priv, RADEON_CLK_PWRMGT_CNTL) & + RADEON_MC_BUSY)) + break; + } + break; + case 9: + while (val--) { + if ((RADEON_READ(RADEON_MC_STATUS) & + RADEON_MC_IDLE)) + break; + } + break; + default: + break; + } + break; + default: + break; + } + } + } +} + +static void combios_parse_pll_table(struct drm_device *dev, uint16_t offset) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + if (offset) { + while (radeon_bios8(dev_priv, offset)) { + uint8_t cmd = ((radeon_bios8(dev_priv, offset) & 0xc0) >> 6); + uint8_t addr = (radeon_bios8(dev_priv, offset) & 0x3f); + uint32_t val, shift, tmp; + uint32_t and_mask, or_mask; + + offset++; + switch (cmd) { + case 0: + val = radeon_bios32(dev_priv, offset); + offset += 4; + RADEON_WRITE_PLL(dev_priv, addr, val); + break; + case 1: + shift = radeon_bios8(dev_priv, offset) * 8; + offset++; + and_mask = radeon_bios8(dev_priv, offset) << shift; + and_mask |= ~(0xff << shift); + offset++; + or_mask = radeon_bios8(dev_priv, offset) << shift; + offset++; + tmp = RADEON_READ_PLL(dev_priv, addr); + tmp &= and_mask; + tmp |= or_mask; + RADEON_WRITE_PLL(dev_priv, addr, tmp); + break; + case 2: + case 3: + tmp = 1000; + switch (addr) { + case 1: + udelay(150); + break; + case 2: + udelay(1000); + break; + case 3: + while (tmp--) { + if (!(RADEON_READ_PLL(dev_priv, RADEON_CLK_PWRMGT_CNTL) & + RADEON_MC_BUSY)) + break; + } + break; + case 4: + while (tmp--) { + if (RADEON_READ_PLL(dev_priv, RADEON_CLK_PWRMGT_CNTL) & + RADEON_DLL_READY) + break; + } + break; + case 5: + tmp = RADEON_READ_PLL(dev_priv, RADEON_CLK_PWRMGT_CNTL); + if (tmp & RADEON_CG_NO1_DEBUG_0) { +#if 0 + uint32_t mclk_cntl = RADEON_READ_PLL(RADEON_MCLK_CNTL); + mclk_cntl &= 0xffff0000; + //mclk_cntl |= 0x00001111; /* ??? */ + RADEON_WRITE_PLL(dev_priv, RADEON_MCLK_CNTL, mclk_cntl); + udelay(10000); +#endif + RADEON_WRITE_PLL(dev_priv, RADEON_CLK_PWRMGT_CNTL, + tmp & ~RADEON_CG_NO1_DEBUG_0); + udelay(10000); + } + break; + default: + break; + } + break; + default: + break; + } + } + } +} + +static void combios_parse_ram_reset_table(struct drm_device *dev, uint16_t offset) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t tmp; + + if (offset) { + uint8_t val = radeon_bios8(dev_priv, offset); + while (val != 0xff) { + offset++; + + if (val == 0x0f) { + uint32_t channel_complete_mask; + + if (radeon_is_r300(dev_priv)) + channel_complete_mask = R300_MEM_PWRUP_COMPLETE; + else + channel_complete_mask = RADEON_MEM_PWRUP_COMPLETE; + tmp = 20000; + while (tmp--) { + if ((RADEON_READ(RADEON_MEM_STR_CNTL) & + channel_complete_mask) == + channel_complete_mask) + break; + } + } else { + uint32_t or_mask = radeon_bios16(dev_priv, offset); + offset += 2; + + tmp = RADEON_READ(RADEON_MEM_SDRAM_MODE_REG); + tmp &= RADEON_SDRAM_MODE_MASK; + tmp |= or_mask; + RADEON_WRITE(RADEON_MEM_SDRAM_MODE_REG, tmp); + + or_mask = val << 24; + tmp = RADEON_READ(RADEON_MEM_SDRAM_MODE_REG); + tmp &= RADEON_B3MEM_RESET_MASK; + tmp |= or_mask; + RADEON_WRITE(RADEON_MEM_SDRAM_MODE_REG, tmp); + } + val = radeon_bios8(dev_priv, offset); + } + } +} + +void radeon_combios_dyn_clk_setup(struct drm_device *dev, int enable) +{ + uint16_t dyn_clk_info = combios_get_table_offset(dev, COMBIOS_DYN_CLK_1_TABLE); + + if (dyn_clk_info) + combios_parse_pll_table(dev, dyn_clk_info); +} + +void radeon_combios_asic_init(struct drm_device *dev) +{ + uint16_t table; + + /* ASIC INIT 1 */ + table = combios_get_table_offset(dev, COMBIOS_ASIC_INIT_1_TABLE); + if (table) + combios_parse_mmio_table(dev, table); + + /* PLL INIT */ + table = combios_get_table_offset(dev, COMBIOS_PLL_INIT_TABLE); + if (table) + combios_parse_pll_table(dev, table); + + /* ASIC INIT 2 */ + table = combios_get_table_offset(dev, COMBIOS_ASIC_INIT_2_TABLE); + if (table) + combios_parse_mmio_table(dev, table); + + /* ASIC INIT 4 */ + table = combios_get_table_offset(dev, COMBIOS_ASIC_INIT_4_TABLE); + if (table) + combios_parse_mmio_table(dev, table); + + /* RAM RESET */ + table = combios_get_table_offset(dev, COMBIOS_RAM_RESET_TABLE); + if (table) + combios_parse_ram_reset_table(dev, table); + + /* ASIC INIT 3 */ + table = combios_get_table_offset(dev, COMBIOS_ASIC_INIT_3_TABLE); + if (table) + combios_parse_mmio_table(dev, table); + + /* DYN CLK 1 */ + table = combios_get_table_offset(dev, COMBIOS_DYN_CLK_1_TABLE); + if (table) + combios_parse_pll_table(dev, table); + + /* ASIC INIT 5 */ + table = combios_get_table_offset(dev, COMBIOS_ASIC_INIT_5_TABLE); + if (table) + combios_parse_mmio_table(dev, table); + +} + +void radeon_combios_initialize_bios_scratch_regs(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t bios_0_scratch, bios_6_scratch, bios_7_scratch; + + bios_0_scratch = RADEON_READ(RADEON_BIOS_0_SCRATCH); + bios_6_scratch = RADEON_READ(RADEON_BIOS_6_SCRATCH); + //bios_7_scratch = RADEON_READ(RADEON_BIOS_7_SCRATCH); + + /* let the bios control the backlight */ + bios_0_scratch &= ~RADEON_DRIVER_BRIGHTNESS_EN; + + /* tell the bios not to handle mode switching */ + bios_6_scratch |= (RADEON_DISPLAY_SWITCHING_DIS | + RADEON_ACC_MODE_CHANGE); + + /* tell the bios a driver is loaded */ + //bios_7_scratch |= RADEON_DRV_LOADED; + + RADEON_WRITE(RADEON_BIOS_0_SCRATCH, bios_0_scratch); + RADEON_WRITE(RADEON_BIOS_6_SCRATCH, bios_6_scratch); + //RADEON_WRITE(RADEON_BIOS_7_SCRATCH, bios_7_scratch); +} + +void +radeon_combios_output_lock(struct drm_encoder *encoder, bool lock) +{ + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t bios_6_scratch; + + bios_6_scratch = RADEON_READ(RADEON_BIOS_6_SCRATCH); + + if (lock) + bios_6_scratch |= RADEON_DRIVER_CRITICAL; + else + bios_6_scratch &= ~RADEON_DRIVER_CRITICAL; + + RADEON_WRITE(RADEON_BIOS_6_SCRATCH, bios_6_scratch); +} diff --git a/linux-core/radeon_connectors.c b/linux-core/radeon_connectors.c new file mode 100644 index 00000000..be1dbae1 --- /dev/null +++ b/linux-core/radeon_connectors.c @@ -0,0 +1,392 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat 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. + * + * Authors: Dave Airlie + * Alex Deucher + */ +#include "drmP.h" +#include "drm_edid.h" +#include "drm_crtc_helper.h" +#include "radeon_drm.h" +#include "radeon_drv.h" + +struct drm_encoder *radeon_best_single_encoder(struct drm_connector *connector) +{ + int enc_id = connector->encoder_ids[0]; + struct drm_mode_object *obj; + struct drm_encoder *encoder; + + /* pick the encoder ids */ + if (enc_id) { + obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER); + if (!obj) + return NULL; + encoder = obj_to_encoder(obj); + return encoder; + } + return NULL; +} + +static struct drm_display_mode *radeon_fp_native_mode(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_display_mode *mode = NULL; + + if (radeon_encoder->panel_xres != 0 && + radeon_encoder->panel_yres != 0 && + radeon_encoder->dotclock != 0) { + mode = drm_mode_create(dev); + + mode->hdisplay = radeon_encoder->panel_xres; + mode->vdisplay = radeon_encoder->panel_yres; + + mode->htotal = mode->hdisplay + radeon_encoder->hblank; + mode->hsync_start = mode->hdisplay + radeon_encoder->hoverplus; + mode->hsync_end = mode->hsync_start + radeon_encoder->hsync_width; + mode->vtotal = mode->vdisplay + radeon_encoder->vblank; + mode->vsync_start = mode->vdisplay + radeon_encoder->voverplus; + mode->vsync_end = mode->vsync_start + radeon_encoder->vsync_width; + mode->clock = radeon_encoder->dotclock; + mode->flags = 0; + + mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER; + + DRM_DEBUG("Adding native panel mode %dx%d\n", + radeon_encoder->panel_xres, radeon_encoder->panel_yres); + } + return mode; +} + +int radeon_connector_set_property(struct drm_connector *connector, struct drm_property *property, + uint64_t val) +{ + struct drm_device *dev = connector->dev; + + if (property == dev->mode_config.dpms_property) { + if (val > 3) + return -EINVAL; + + drm_helper_set_connector_dpms(connector, val); + + } + return 0; +} + + +static int radeon_lvds_get_modes(struct drm_connector *connector) +{ + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct drm_encoder *encoder; + int ret = 0; + struct edid *edid; + struct drm_display_mode *mode; + + if (radeon_connector->ddc_bus) { + radeon_i2c_do_lock(radeon_connector, 1); + edid = drm_get_edid(connector, &radeon_connector->ddc_bus->adapter); + radeon_i2c_do_lock(radeon_connector, 0); + if (edid) { + drm_mode_connector_update_edid_property(&radeon_connector->base, edid); + ret = drm_add_edid_modes(&radeon_connector->base, edid); + kfree(edid); + if (ret == 0) + goto native; + return ret; + } + } + +native: + encoder = radeon_best_single_encoder(connector); + if (!encoder) + return 0; + + /* we have no EDID modes */ + mode = radeon_fp_native_mode(encoder); + if (mode) { + ret = 1; + drm_mode_probed_add(connector, mode); + } + return ret; +} + +static int radeon_lvds_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static enum drm_connector_status radeon_lvds_detect(struct drm_connector *connector) +{ + // check acpi lid status ??? + return connector_status_connected; +} + +static void radeon_connector_destroy(struct drm_connector *connector) +{ + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + + if (radeon_connector->ddc_bus) + radeon_i2c_destroy(radeon_connector->ddc_bus); + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); + kfree(connector); +} + +struct drm_connector_helper_funcs radeon_lvds_connector_helper_funcs = { + .get_modes = radeon_lvds_get_modes, + .mode_valid = radeon_lvds_mode_valid, + .best_encoder = radeon_best_single_encoder, +}; + +struct drm_connector_funcs radeon_lvds_connector_funcs = { + .detect = radeon_lvds_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = radeon_connector_destroy, + .set_property = radeon_connector_set_property, +}; + +static int radeon_vga_get_modes(struct drm_connector *connector) +{ + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + int ret; + + ret = radeon_ddc_get_modes(radeon_connector); + + return ret; +} + +static int radeon_vga_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + + return MODE_OK; +} + +static enum drm_connector_status radeon_vga_detect(struct drm_connector *connector) +{ + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct drm_encoder *encoder; + struct drm_encoder_helper_funcs *encoder_funcs; + bool ret; + + radeon_i2c_do_lock(radeon_connector, 1); + ret = radeon_ddc_probe(radeon_connector); + radeon_i2c_do_lock(radeon_connector, 0); + if (ret) + return connector_status_connected; + + /* if EDID fails to a load detect */ + encoder = radeon_best_single_encoder(connector); + if (!encoder) + return connector_status_disconnected; + + encoder_funcs = encoder->helper_private; + return encoder_funcs->detect(encoder, connector); +} + +struct drm_connector_helper_funcs radeon_vga_connector_helper_funcs = { + .get_modes = radeon_vga_get_modes, + .mode_valid = radeon_vga_mode_valid, + .best_encoder = radeon_best_single_encoder, +}; + +struct drm_connector_funcs radeon_vga_connector_funcs = { + .detect = radeon_vga_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = radeon_connector_destroy, + .set_property = radeon_connector_set_property, +}; + + +static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connector) +{ + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct drm_encoder *encoder; + struct drm_encoder_helper_funcs *encoder_funcs; + struct drm_mode_object *obj; + int i; + enum drm_connector_status ret; + bool dret; + + radeon_i2c_do_lock(radeon_connector, 1); + dret = radeon_ddc_probe(radeon_connector); + radeon_i2c_do_lock(radeon_connector, 0); + if (dret) + return connector_status_connected; + + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { + if (connector->encoder_ids[i] == 0) + break; + + obj = drm_mode_object_find(connector->dev, connector->encoder_ids[i], DRM_MODE_OBJECT_ENCODER); + if (!obj) + continue; + + encoder = obj_to_encoder(obj); + + encoder_funcs = encoder->helper_private; + if (encoder_funcs->detect) { + ret = encoder_funcs->detect(encoder, connector); + if (ret == connector_status_connected) { + radeon_connector->use_digital = 0; + return ret; + } + } + } + return connector_status_disconnected; +} + +/* okay need to be smart in here about which encoder to pick */ +struct drm_encoder *radeon_dvi_encoder(struct drm_connector *connector) +{ + int enc_id = connector->encoder_ids[0]; + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct drm_mode_object *obj; + struct drm_encoder *encoder; + int i; + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { + if (connector->encoder_ids[i] == 0) + break; + + obj = drm_mode_object_find(connector->dev, connector->encoder_ids[i], DRM_MODE_OBJECT_ENCODER); + if (!obj) + continue; + + encoder = obj_to_encoder(obj); + + if (radeon_connector->use_digital) { + if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS) + return encoder; + } else { + if (encoder->encoder_type == DRM_MODE_ENCODER_DAC || + encoder->encoder_type == DRM_MODE_ENCODER_TVDAC) + return encoder; + } + } + + /* see if we have a default encoder TODO */ + + /* then check use digitial */ + /* pick the first one */ + if (enc_id) { + obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER); + if (!obj) + return NULL; + encoder = obj_to_encoder(obj); + return encoder; + } + return NULL; +} + +struct drm_connector_helper_funcs radeon_dvi_connector_helper_funcs = { + .get_modes = radeon_vga_get_modes, + .mode_valid = radeon_vga_mode_valid, + .best_encoder = radeon_dvi_encoder, +}; + +struct drm_connector_funcs radeon_dvi_connector_funcs = { + .detect = radeon_dvi_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = radeon_connector_set_property, + .destroy = radeon_connector_destroy, +}; + + +static struct connector_funcs { + int conn_id; + struct drm_connector_funcs *connector_funcs; + struct drm_connector_helper_funcs *helper_funcs; + int conn_type; + char *i2c_id; +} connector_fns[] = { + { CONNECTOR_NONE, NULL, NULL, DRM_MODE_CONNECTOR_Unknown }, + { CONNECTOR_VGA, &radeon_vga_connector_funcs, &radeon_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_VGA , "VGA"}, + { CONNECTOR_LVDS, &radeon_lvds_connector_funcs, &radeon_lvds_connector_helper_funcs, DRM_MODE_CONNECTOR_LVDS, "LVDS" }, + { CONNECTOR_DVI_A, &radeon_vga_connector_funcs, &radeon_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_DVIA, "DVI" }, + { CONNECTOR_DVI_I, &radeon_dvi_connector_funcs, &radeon_dvi_connector_helper_funcs, DRM_MODE_CONNECTOR_DVII, "DVI" }, + { CONNECTOR_DVI_D, &radeon_dvi_connector_funcs, &radeon_dvi_connector_helper_funcs, DRM_MODE_CONNECTOR_DVID, "DVI" }, + { CONNECTOR_HDMI_TYPE_A, &radeon_dvi_connector_funcs, &radeon_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_HDMIA, "HDMI" }, + { CONNECTOR_HDMI_TYPE_B, &radeon_dvi_connector_funcs, &radeon_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_HDMIB, "HDMI" }, +#if 0 + { CONNECTOR_HDMI_TYPE_A, radeon_vga_connector_funcs, radeon_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_VGA }, + { CONNECTOR_DVI_D, radeon_vga_connector_funcs, radeon_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_VGA }, + + { CONNECTOR_STV, radeon_vga_connector_funcs, radeon_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_VGA }, + { CONNECTOR_CTV, radeon_vga_connector_funcs, radeon_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_VGA }, + { CONNECTOR_DIGITAL, radeon_vga_connector_funcs, radeon_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_VGA }, + { CONNECTOR_SCART, radeon_vga_connector_funcs, radeon_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_VGA }, + + { CONNECTOR_HDMI_TYPE_B, radeon_vga_connector_funcs, radeon_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_VGA }, + { CONNECTOR_HDMI_TYPE_B, radeon_vga_connector_funcs, radeon_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_VGA }, + { CONNECTOR_HDMI_TYPE_B, radeon_vga_connector_funcs, radeon_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_VGA }, + { CONNECTOR_DIN, radeon_vga_connector_funcs, radeon_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_VGA }, + { CONNECTOR_DISPLAY_PORT, radeon_vga_connector_funcs, radeon_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_VGA }, +#endif +}; + +struct drm_connector *radeon_connector_add(struct drm_device *dev, int bios_index) +{ + struct radeon_connector *radeon_connector; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_mode_info *mode_info = &dev_priv->mode_info; + struct drm_connector *connector; + int table_idx; + + for (table_idx = 0; table_idx < ARRAY_SIZE(connector_fns); table_idx++) { + if (connector_fns[table_idx].conn_id == mode_info->bios_connector[bios_index].connector_type) + break; + } + + if (table_idx == ARRAY_SIZE(connector_fns)) + return NULL; + + radeon_connector = kzalloc(sizeof(struct radeon_connector), GFP_KERNEL); + if (!radeon_connector) { + return NULL; + } + + connector = &radeon_connector->base; + + drm_connector_init(dev, &radeon_connector->base, connector_fns[table_idx].connector_funcs, + connector_fns[table_idx].conn_type); + + drm_connector_helper_add(&radeon_connector->base, connector_fns[table_idx].helper_funcs); + + if (mode_info->bios_connector[bios_index].ddc_i2c.valid) { + radeon_connector->ddc_bus = radeon_i2c_create(dev, &mode_info->bios_connector[bios_index].ddc_i2c, + connector_fns[table_idx].i2c_id); + if (!radeon_connector->ddc_bus) + goto failed; + } + + drm_sysfs_connector_add(connector); + return connector; + + +failed: + if (radeon_connector->ddc_bus) + radeon_i2c_destroy(radeon_connector->ddc_bus); + drm_connector_cleanup(connector); + kfree(connector); + return NULL; +} diff --git a/linux-core/radeon_cs.c b/linux-core/radeon_cs.c new file mode 120000 index 00000000..0e3fa8ee --- /dev/null +++ b/linux-core/radeon_cs.c @@ -0,0 +1 @@ +../shared-core/radeon_cs.c
\ No newline at end of file diff --git a/linux-core/radeon_cursor.c b/linux-core/radeon_cursor.c new file mode 100644 index 00000000..fbd4143c --- /dev/null +++ b/linux-core/radeon_cursor.c @@ -0,0 +1,247 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat 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. + * + * Authors: Dave Airlie + * Alex Deucher + */ +#include "drmP.h" +#include "radeon_drm.h" +#include "radeon_drv.h" + +#define CURSOR_WIDTH 64 +#define CURSOR_HEIGHT 64 + +static void radeon_lock_cursor(struct drm_crtc *crtc, bool lock) +{ + struct drm_radeon_private *dev_priv = crtc->dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + uint32_t cur_lock; + + if (radeon_is_avivo(dev_priv)) { + cur_lock = RADEON_READ(AVIVO_D1CUR_UPDATE + radeon_crtc->crtc_offset); + if (lock) + cur_lock |= AVIVO_D1CURSOR_UPDATE_LOCK; + else + cur_lock &= ~AVIVO_D1CURSOR_UPDATE_LOCK; + RADEON_WRITE(AVIVO_D1CUR_UPDATE + radeon_crtc->crtc_offset, cur_lock); + } else { + switch(radeon_crtc->crtc_id) { + case 0: + cur_lock = RADEON_READ(RADEON_CUR_OFFSET); + if (lock) + cur_lock |= RADEON_CUR_LOCK; + else + cur_lock &= ~RADEON_CUR_LOCK; + RADEON_WRITE(RADEON_CUR_OFFSET, cur_lock); + break; + case 1: + cur_lock = RADEON_READ(RADEON_CUR2_OFFSET); + if (lock) + cur_lock |= RADEON_CUR2_LOCK; + else + cur_lock &= ~RADEON_CUR2_LOCK; + RADEON_WRITE(RADEON_CUR2_OFFSET, cur_lock); + break; + default: + break; + } + } +} + +static void radeon_hide_cursor(struct drm_crtc *crtc) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_radeon_private *dev_priv = crtc->dev->dev_private; + + if (radeon_is_avivo(dev_priv)) { + RADEON_WRITE(RADEON_MM_INDEX, AVIVO_D1CUR_CONTROL + radeon_crtc->crtc_offset); + RADEON_WRITE_P(RADEON_MM_DATA, 0, ~AVIVO_D1CURSOR_EN); + } else { + switch(radeon_crtc->crtc_id) { + case 0: + RADEON_WRITE(RADEON_MM_INDEX, RADEON_CRTC_GEN_CNTL); + break; + case 1: + RADEON_WRITE(RADEON_MM_INDEX, RADEON_CRTC2_GEN_CNTL); + break; + default: + return; + } + RADEON_WRITE_P(RADEON_MM_DATA, 0, ~RADEON_CRTC_CUR_EN); + } +} + +static void radeon_show_cursor(struct drm_crtc *crtc) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_radeon_private *dev_priv = crtc->dev->dev_private; + + if (radeon_is_avivo(dev_priv)) { + RADEON_WRITE(RADEON_MM_INDEX, AVIVO_D1CUR_CONTROL + radeon_crtc->crtc_offset); + RADEON_WRITE(RADEON_MM_DATA, AVIVO_D1CURSOR_EN | + (AVIVO_D1CURSOR_MODE_24BPP << AVIVO_D1CURSOR_MODE_SHIFT)); + } else { + switch(radeon_crtc->crtc_id) { + case 0: + RADEON_WRITE(RADEON_MM_INDEX, RADEON_CRTC_GEN_CNTL); + break; + case 1: + RADEON_WRITE(RADEON_MM_INDEX, RADEON_CRTC2_GEN_CNTL); + break; + default: + return; + } + + RADEON_WRITE_P(RADEON_MM_DATA, (RADEON_CRTC_CUR_EN | + (RADEON_CRTC_CUR_MODE_24BPP << RADEON_CRTC_CUR_MODE_SHIFT)), + ~(RADEON_CRTC_CUR_EN | RADEON_CRTC_CUR_MODE_MASK)); + } +} + +static void radeon_set_cursor(struct drm_crtc *crtc, struct drm_gem_object *obj, + uint32_t width, uint32_t height) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_radeon_private *dev_priv = crtc->dev->dev_private; + struct drm_radeon_gem_object *obj_priv; + + obj_priv = obj->driver_private; + + if (radeon_is_avivo(dev_priv)) { + RADEON_WRITE(AVIVO_D1CUR_SURFACE_ADDRESS + radeon_crtc->crtc_offset, + dev_priv->fb_location + obj_priv->bo->offset); + RADEON_WRITE(AVIVO_D1CUR_SIZE + radeon_crtc->crtc_offset, + (width - 1) << 16 | (height - 1)); + } else { + switch(radeon_crtc->crtc_id) { + case 0: + /* offset is from DISP_BASE_ADDRESS */ + RADEON_WRITE(RADEON_CUR_OFFSET, obj_priv->bo->offset); + break; + case 1: + /* offset is from DISP2_BASE_ADDRESS */ + RADEON_WRITE(RADEON_CUR2_OFFSET, obj_priv->bo->offset); + break; + default: + break; + } + } +} + +int radeon_crtc_cursor_set(struct drm_crtc *crtc, + struct drm_file *file_priv, + uint32_t handle, + uint32_t width, + uint32_t height) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_gem_object *obj; + + if (!handle) { + /* turn off cursor */ + radeon_hide_cursor(crtc); + return 0; + } + + obj = drm_gem_object_lookup(crtc->dev, file_priv, handle); + if (!obj) { + DRM_ERROR("Cannot find cursor object %x for crtc %d\n", handle, radeon_crtc->crtc_id); + return -EINVAL; + } + + if ((width > CURSOR_WIDTH) || (height > CURSOR_HEIGHT)) { + DRM_ERROR("bad cursor width or height %d x %d\n", width, height); + return -EINVAL; + } + + radeon_lock_cursor(crtc, true); + // XXX only 27 bit offset for legacy cursor + radeon_set_cursor(crtc, obj, width, height); + radeon_show_cursor(crtc); + radeon_lock_cursor(crtc, false); + + mutex_lock(&crtc->dev->struct_mutex); + drm_gem_object_unreference(obj); + mutex_unlock(&crtc->dev->struct_mutex); + + return 0; +} + +int radeon_crtc_cursor_move(struct drm_crtc *crtc, + int x, int y) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_radeon_private *dev_priv = crtc->dev->dev_private; + int xorigin = 0, yorigin = 0; + + if (x < 0) + xorigin = -x + 1; + if (y < 0) + yorigin = -y + 1; + if (xorigin >= CURSOR_WIDTH) + xorigin = CURSOR_WIDTH - 1; + if (yorigin >= CURSOR_HEIGHT) + yorigin = CURSOR_HEIGHT - 1; + + radeon_lock_cursor(crtc, true); + if (radeon_is_avivo(dev_priv)) { + /* avivo cursor are offset into the total surface */ + x += crtc->x; + y += crtc->y; + DRM_DEBUG("x %d y %d c->x %d c->y %d\n", x, y, crtc->x, crtc->y); + RADEON_WRITE(AVIVO_D1CUR_POSITION + radeon_crtc->crtc_offset, + ((xorigin ? 0: x) << 16) | + (yorigin ? 0 : y)); + RADEON_WRITE(AVIVO_D1CUR_HOT_SPOT + radeon_crtc->crtc_offset, (xorigin << 16) | yorigin); + } else { + if (crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) + y /= 2; + else if (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN) + y *= 2; + + switch(radeon_crtc->crtc_id) { + case 0: + RADEON_WRITE(RADEON_CUR_HORZ_VERT_OFF, (RADEON_CUR_LOCK + | (xorigin << 16) + | yorigin)); + RADEON_WRITE(RADEON_CUR_HORZ_VERT_POSN, (RADEON_CUR_LOCK + | ((xorigin ? 0 : x) << 16) + | (yorigin ? 0 : y))); + break; + case 1: + RADEON_WRITE(RADEON_CUR2_HORZ_VERT_OFF, (RADEON_CUR2_LOCK + | (xorigin << 16) + | yorigin)); + RADEON_WRITE(RADEON_CUR2_HORZ_VERT_POSN, (RADEON_CUR2_LOCK + | ((xorigin ? 0 : x) << 16) + | (yorigin ? 0 : y))); + break; + default: + break; + } + + } + radeon_lock_cursor(crtc, false); + + return 0; +} + diff --git a/linux-core/radeon_display.c b/linux-core/radeon_display.c new file mode 100644 index 00000000..0b9467fd --- /dev/null +++ b/linux-core/radeon_display.c @@ -0,0 +1,718 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat 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. + * + * Authors: Dave Airlie + * Alex Deucher + */ +#include "drmP.h" +#include "radeon_drm.h" +#include "radeon_drv.h" + +#include "atom.h" +#include <asm/div64.h> + +#include "drm_crtc_helper.h" +#include "drm_edid.h" + +int radeon_ddc_dump(struct drm_connector *connector); + + + +static void avivo_crtc_load_lut(struct drm_crtc *crtc) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + int i; + + DRM_DEBUG("%d\n", radeon_crtc->crtc_id); + RADEON_WRITE(AVIVO_DC_LUTA_CONTROL + radeon_crtc->crtc_offset, 0); + + RADEON_WRITE(AVIVO_DC_LUTA_BLACK_OFFSET_BLUE + radeon_crtc->crtc_offset, 0); + RADEON_WRITE(AVIVO_DC_LUTA_BLACK_OFFSET_GREEN + radeon_crtc->crtc_offset, 0); + RADEON_WRITE(AVIVO_DC_LUTA_BLACK_OFFSET_RED + radeon_crtc->crtc_offset, 0); + + RADEON_WRITE(AVIVO_DC_LUTA_WHITE_OFFSET_BLUE + radeon_crtc->crtc_offset, 0xffff); + RADEON_WRITE(AVIVO_DC_LUTA_WHITE_OFFSET_GREEN + radeon_crtc->crtc_offset, 0xffff); + RADEON_WRITE(AVIVO_DC_LUTA_WHITE_OFFSET_RED + radeon_crtc->crtc_offset, 0xffff); + + RADEON_WRITE(AVIVO_DC_LUT_RW_SELECT, radeon_crtc->crtc_id); + RADEON_WRITE(AVIVO_DC_LUT_RW_MODE, 0); + RADEON_WRITE(AVIVO_DC_LUT_WRITE_EN_MASK, 0x0000003f); + + for (i = 0; i < 256; i++) { + RADEON_WRITE8(AVIVO_DC_LUT_RW_INDEX, i); + RADEON_WRITE(AVIVO_DC_LUT_30_COLOR, + (radeon_crtc->lut_r[i] << 22) | + (radeon_crtc->lut_g[i] << 12) | + (radeon_crtc->lut_b[i] << 2)); + } + + RADEON_WRITE(AVIVO_D1GRPH_LUT_SEL + radeon_crtc->crtc_offset, radeon_crtc->crtc_id); +} + +static void legacy_crtc_load_lut(struct drm_crtc *crtc) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + int i; + uint32_t dac2_cntl; + + dac2_cntl = RADEON_READ(RADEON_DAC_CNTL2); + if (radeon_crtc->crtc_id == 0) + dac2_cntl &= (uint32_t)~RADEON_DAC2_PALETTE_ACC_CTL; + else + dac2_cntl |= RADEON_DAC2_PALETTE_ACC_CTL; + RADEON_WRITE(RADEON_DAC_CNTL2, dac2_cntl); + + for (i = 0; i < 256; i++) { + RADEON_WRITE8(RADEON_PALETTE_INDEX, i); + RADEON_WRITE(RADEON_PALETTE_DATA, + (radeon_crtc->lut_r[i] << 16) | + (radeon_crtc->lut_g[i] << 8) | + (radeon_crtc->lut_b[i] << 0)); + } +} + +void radeon_crtc_load_lut(struct drm_crtc *crtc) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + + if (!crtc->enabled) + return; + + if (radeon_is_avivo(dev_priv)) + avivo_crtc_load_lut(crtc); + else + legacy_crtc_load_lut(crtc); +} + +/** Sets the color ramps on behalf of RandR */ +void radeon_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, + u16 blue, int regno) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + + if (regno==0) + DRM_DEBUG("gamma set %d\n", radeon_crtc->crtc_id); + radeon_crtc->lut_r[regno] = red >> 8; + radeon_crtc->lut_g[regno] = green >> 8; + radeon_crtc->lut_b[regno] = blue >> 8; +} + +static void radeon_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, uint32_t size) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + int i, j; + + if (size != 256) + return; + + if (crtc->fb->depth == 16) { + for (i = 0; i < 64; i++) { + if (i <= 31) { + for (j = 0; j < 8; j++) { + radeon_crtc->lut_r[i * 8 + j] = red[i] >> 8; + radeon_crtc->lut_b[i * 8 + j] = blue[i] >> 8; + } + } + for (j = 0; j < 4; j++) + radeon_crtc->lut_g[i * 4 + j] = green[i] >> 8; + } + } else { + for (i = 0; i < 256; i++) { + radeon_crtc->lut_r[i] = red[i] >> 8; + radeon_crtc->lut_g[i] = green[i] >> 8; + radeon_crtc->lut_b[i] = blue[i] >> 8; + } + } + + radeon_crtc_load_lut(crtc); +} + +static void radeon_crtc_destroy(struct drm_crtc *crtc) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + + drm_crtc_cleanup(crtc); + kfree(radeon_crtc); +} + +static const struct drm_crtc_funcs radeon_crtc_funcs = { + .cursor_set = radeon_crtc_cursor_set, + .cursor_move = radeon_crtc_cursor_move, + .gamma_set = radeon_crtc_gamma_set, + .set_config = drm_crtc_helper_set_config, + .destroy = radeon_crtc_destroy, +}; + +static void radeon_crtc_init(struct drm_device *dev, int index) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_crtc *radeon_crtc; + int i; + + radeon_crtc = kzalloc(sizeof(struct radeon_crtc) + (RADEONFB_CONN_LIMIT * sizeof(struct drm_connector *)), GFP_KERNEL); + // radeon_crtc = kzalloc(sizeof(struct radeon_crtc), GFP_KERNEL); + if (radeon_crtc == NULL) + return; + + drm_crtc_init(dev, &radeon_crtc->base, &radeon_crtc_funcs); + + drm_mode_crtc_set_gamma_size(&radeon_crtc->base, 256); + radeon_crtc->crtc_id = index; + + radeon_crtc->mode_set.crtc = &radeon_crtc->base; + radeon_crtc->mode_set.connectors = (struct drm_connector **)(radeon_crtc + 1); + radeon_crtc->mode_set.num_connectors = 0; + + for (i = 0; i < 256; i++) { + radeon_crtc->lut_r[i] = i; + radeon_crtc->lut_g[i] = i; + radeon_crtc->lut_b[i] = i; + } + + if (dev_priv->is_atom_bios && (radeon_is_avivo(dev_priv) || radeon_r4xx_atom)) + radeon_atombios_init_crtc(dev, radeon_crtc); + else + radeon_legacy_init_crtc(dev, radeon_crtc); +} + +bool radeon_legacy_setup_enc_conn(struct drm_device *dev) +{ + + radeon_get_legacy_connector_info_from_bios(dev); + return false; +} + +bool radeon_setup_enc_conn(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_mode_info *mode_info = &dev_priv->mode_info; + /* do all the mac and stuff */ + struct drm_connector *connector; + struct drm_encoder *encoder; + int i; + + if (dev_priv->is_atom_bios) + radeon_get_atom_connector_info_from_bios_connector_table(dev); + else + radeon_get_legacy_connector_info_from_bios(dev); + + for (i = 0; i < RADEON_MAX_BIOS_CONNECTOR; i++) { + if (!mode_info->bios_connector[i].valid) + continue; + + /* add a connector for this */ + if (mode_info->bios_connector[i].connector_type == CONNECTOR_NONE) + continue; + + connector = radeon_connector_add(dev, i); + if (!connector) + continue; + + encoder = NULL; + /* if we find an LVDS connector */ + if (mode_info->bios_connector[i].connector_type == CONNECTOR_LVDS) { + if (radeon_is_avivo(dev_priv) || radeon_r4xx_atom) + encoder = radeon_encoder_lvtma_add(dev, i); + else + encoder = radeon_encoder_legacy_lvds_add(dev, i); + if (encoder) + drm_mode_connector_attach_encoder(connector, encoder); + } + + /* DAC on DVI or VGA */ + if ((mode_info->bios_connector[i].connector_type == CONNECTOR_DVI_I) || + (mode_info->bios_connector[i].connector_type == CONNECTOR_DVI_A) || + (mode_info->bios_connector[i].connector_type == CONNECTOR_VGA)) { + if (radeon_is_avivo(dev_priv) || radeon_r4xx_atom) + encoder = radeon_encoder_atom_dac_add(dev, i, mode_info->bios_connector[i].dac_type, 0); + else { + if (mode_info->bios_connector[i].dac_type == DAC_PRIMARY) + encoder = radeon_encoder_legacy_primary_dac_add(dev, i, 0); + else if (mode_info->bios_connector[i].dac_type == DAC_TVDAC) + encoder = radeon_encoder_legacy_tv_dac_add(dev, i, 0); + } + if (encoder) + drm_mode_connector_attach_encoder(connector, encoder); + } + + /* TMDS on DVI */ + if ((mode_info->bios_connector[i].connector_type == CONNECTOR_DVI_I) || + (mode_info->bios_connector[i].connector_type == CONNECTOR_DVI_D) || + (mode_info->bios_connector[i].connector_type == CONNECTOR_HDMI_TYPE_A) || + (mode_info->bios_connector[i].connector_type == CONNECTOR_HDMI_TYPE_B)) { + if (radeon_is_avivo(dev_priv) || radeon_r4xx_atom) + encoder = radeon_encoder_atom_tmds_add(dev, i, mode_info->bios_connector[i].tmds_type); + else { + if (mode_info->bios_connector[i].tmds_type == TMDS_INT) + encoder = radeon_encoder_legacy_tmds_int_add(dev, i); + else if (mode_info->bios_connector[i].tmds_type == TMDS_EXT) + encoder = radeon_encoder_legacy_tmds_ext_add(dev, i); + } + if (encoder) + drm_mode_connector_attach_encoder(connector, encoder); + } + + /* TVDAC on DIN */ + if (mode_info->bios_connector[i].connector_type == CONNECTOR_DIN) { + if (radeon_is_avivo(dev_priv) || radeon_r4xx_atom) + encoder = radeon_encoder_atom_dac_add(dev, i, mode_info->bios_connector[i].dac_type, 1); + else { + if (mode_info->bios_connector[i].dac_type == DAC_TVDAC) + encoder = radeon_encoder_legacy_tv_dac_add(dev, i, 0); + } + if (encoder) + drm_mode_connector_attach_encoder(connector, encoder); + } + } + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) + radeon_ddc_dump(connector); + return true; +} + +int radeon_ddc_get_modes(struct radeon_connector *radeon_connector) +{ + struct drm_radeon_private *dev_priv = radeon_connector->base.dev->dev_private; + struct edid *edid; + int ret = 0; + + if (!radeon_connector->ddc_bus) + return -1; + radeon_i2c_do_lock(radeon_connector, 1); + edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter); + radeon_i2c_do_lock(radeon_connector, 0); + if (edid) { + /* update digital bits here */ + if (edid->digital) + radeon_connector->use_digital = 1; + else + radeon_connector->use_digital = 0; + drm_mode_connector_update_edid_property(&radeon_connector->base, edid); + ret = drm_add_edid_modes(&radeon_connector->base, edid); + kfree(edid); + return ret; + } + return -1; +} + +int radeon_ddc_dump(struct drm_connector *connector) +{ + struct edid *edid; + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + int ret = 0; + + if (!radeon_connector->ddc_bus) + return -1; + radeon_i2c_do_lock(radeon_connector, 1); + edid = drm_get_edid(connector, &radeon_connector->ddc_bus->adapter); + radeon_i2c_do_lock(radeon_connector, 0); + if (edid) { + kfree(edid); + } + return ret; +} + +static inline uint32_t radeon_div(uint64_t n, uint32_t d) +{ + uint64_t x, y, result; + uint64_t mod; + + n += d / 2; + + mod = do_div(n, d); + return n; +} + +void radeon_compute_pll(struct radeon_pll *pll, + uint64_t freq, + uint32_t *dot_clock_p, + uint32_t *fb_div_p, + uint32_t *ref_div_p, + uint32_t *post_div_p, + int flags) +{ + uint32_t min_ref_div = pll->min_ref_div; + uint32_t max_ref_div = pll->max_ref_div; + uint32_t best_vco = pll->best_vco; + uint32_t best_post_div = 1; + uint32_t best_ref_div = 1; + uint32_t best_feedback_div = 1; + uint32_t best_freq = -1; + uint32_t best_error = 0xffffffff; + uint32_t best_vco_diff = 1; + uint32_t post_div; + + DRM_DEBUG("PLL freq %llu\n", freq); + freq = freq * 1000; + + if (flags & RADEON_PLL_USE_REF_DIV) + min_ref_div = max_ref_div = pll->reference_div; + else { + while (min_ref_div < max_ref_div-1) { + uint32_t mid=(min_ref_div+max_ref_div)/2; + uint32_t pll_in = pll->reference_freq / mid; + if (pll_in < pll->pll_in_min) + max_ref_div = mid; + else if (pll_in > pll->pll_in_max) + min_ref_div = mid; + else + break; + } + } + + for (post_div = pll->min_post_div; post_div <= pll->max_post_div; ++post_div) { + uint32_t ref_div; + + if ((flags & RADEON_PLL_NO_ODD_POST_DIV) && (post_div & 1)) + continue; + + /* legacy radeons only have a few post_divs */ + if (flags & RADEON_PLL_LEGACY) { + if ((post_div == 5) || + (post_div == 7) || + (post_div == 9) || + (post_div == 10) || + (post_div == 11) || + (post_div == 13) || + (post_div == 14) || + (post_div == 15)) + continue; + } + + for (ref_div = min_ref_div; ref_div <= max_ref_div; ++ref_div) { + uint32_t feedback_div, current_freq, error, vco_diff; + uint32_t pll_in = pll->reference_freq / ref_div; + uint32_t min_feed_div = pll->min_feedback_div; + uint32_t max_feed_div = pll->max_feedback_div+1; + + if (pll_in < pll->pll_in_min || pll_in > pll->pll_in_max) + continue; + + while (min_feed_div < max_feed_div) { + uint32_t vco; + feedback_div = (min_feed_div+max_feed_div)/2; + + vco = radeon_div((uint64_t)pll->reference_freq * feedback_div, + ref_div); + + if (vco < pll->pll_out_min) { + min_feed_div = feedback_div+1; + continue; + } else if(vco > pll->pll_out_max) { + max_feed_div = feedback_div; + continue; + } + + current_freq = radeon_div((uint64_t)pll->reference_freq * 10000 * feedback_div, + ref_div * post_div); + + error = abs(current_freq - freq); + vco_diff = abs(vco - best_vco); + + if ((best_vco == 0 && error < best_error) || + (best_vco != 0 && + (error < best_error - 100 || + (abs(error - best_error) < 100 && vco_diff < best_vco_diff )))) { + best_post_div = post_div; + best_ref_div = ref_div; + best_feedback_div = feedback_div; + best_freq = current_freq; + best_error = error; + best_vco_diff = vco_diff; + } else if (current_freq == freq) { + if (best_freq == -1) { + best_post_div = post_div; + best_ref_div = ref_div; + best_feedback_div = feedback_div; + best_freq = current_freq; + best_error = error; + best_vco_diff = vco_diff; + } else if (((flags & RADEON_PLL_PREFER_LOW_REF_DIV) && (ref_div < best_ref_div)) || + ((flags & RADEON_PLL_PREFER_HIGH_REF_DIV) && (ref_div > best_ref_div)) || + ((flags & RADEON_PLL_PREFER_LOW_FB_DIV) && (feedback_div < best_feedback_div)) || + ((flags & RADEON_PLL_PREFER_HIGH_FB_DIV) && (feedback_div > best_feedback_div)) || + ((flags & RADEON_PLL_PREFER_LOW_POST_DIV) && (post_div < best_post_div)) || + ((flags & RADEON_PLL_PREFER_HIGH_POST_DIV) && (post_div > best_post_div))) { + best_post_div = post_div; + best_ref_div = ref_div; + best_feedback_div = feedback_div; + best_freq = current_freq; + best_error = error; + best_vco_diff = vco_diff; + } + } + + if (current_freq < freq) + min_feed_div = feedback_div+1; + else + max_feed_div = feedback_div; + } + } + } + + *dot_clock_p = best_freq / 10000; + *fb_div_p = best_feedback_div; + *ref_div_p = best_ref_div; + *post_div_p = best_post_div; +} + +void radeon_get_clock_info(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct radeon_pll *p1pll = &dev_priv->mode_info.p1pll; + struct radeon_pll *p2pll = &dev_priv->mode_info.p2pll; + struct radeon_pll *spll = &dev_priv->mode_info.spll; + struct radeon_pll *mpll = &dev_priv->mode_info.mpll; + int ret; + + if (dev_priv->is_atom_bios) + ret = radeon_atom_get_clock_info(dev); + else + ret = radeon_combios_get_clock_info(dev); + + if (ret) { + if (p1pll->reference_div < 2) + p1pll->reference_div = 12; + if (p2pll->reference_div < 2) + p2pll->reference_div = 12; + } else { + // TODO FALLBACK + } + + /* pixel clocks */ + if (radeon_is_avivo(dev_priv)) { + p1pll->min_post_div = 2; + p1pll->max_post_div = 0x7f; + p2pll->min_post_div = 2; + p2pll->max_post_div = 0x7f; + } else { + p1pll->min_post_div = 1; + p1pll->max_post_div = 16; + p2pll->min_post_div = 1; + p2pll->max_post_div = 12; + } + + p1pll->min_ref_div = 2; + p1pll->max_ref_div = 0x3ff; + p1pll->min_feedback_div = 4; + p1pll->max_feedback_div = 0x7ff; + p1pll->best_vco = 0; + + p2pll->min_ref_div = 2; + p2pll->max_ref_div = 0x3ff; + p2pll->min_feedback_div = 4; + p2pll->max_feedback_div = 0x7ff; + p2pll->best_vco = 0; + + /* system clock */ + spll->min_post_div = 1; + spll->max_post_div = 1; + spll->min_ref_div = 2; + spll->max_ref_div = 0xff; + spll->min_feedback_div = 4; + spll->max_feedback_div = 0xff; + spll->best_vco = 0; + + /* memory clock */ + mpll->min_post_div = 1; + mpll->max_post_div = 1; + mpll->min_ref_div = 2; + mpll->max_ref_div = 0xff; + mpll->min_feedback_div = 4; + mpll->max_feedback_div = 0xff; + mpll->best_vco = 0; + +} + +/* not sure of the best place for these */ +/* 10 khz */ +void radeon_legacy_set_engine_clock(struct drm_device *dev, int eng_clock) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_mode_info *mode_info = &dev_priv->mode_info; + struct radeon_pll *spll = &mode_info->spll; + uint32_t ref_div, fb_div; + uint32_t m_spll_ref_fb_div; + + /* FIXME wait for idle */ + + m_spll_ref_fb_div = RADEON_READ_PLL(dev_priv, RADEON_M_SPLL_REF_FB_DIV); + m_spll_ref_fb_div &= ((RADEON_M_SPLL_REF_DIV_MASK << RADEON_M_SPLL_REF_DIV_SHIFT) | + (RADEON_MPLL_FB_DIV_MASK << RADEON_MPLL_FB_DIV_SHIFT)); + ref_div = m_spll_ref_fb_div & RADEON_M_SPLL_REF_DIV_MASK; + + fb_div = radeon_div(eng_clock * ref_div, spll->reference_freq); + m_spll_ref_fb_div |= (fb_div & RADEON_SPLL_FB_DIV_MASK) << RADEON_SPLL_FB_DIV_SHIFT; + RADEON_WRITE_PLL(dev_priv, RADEON_M_SPLL_REF_FB_DIV, m_spll_ref_fb_div); + +} + +/* 10 khz */ +void radeon_legacy_set_memory_clock(struct drm_device *dev, int mem_clock) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_mode_info *mode_info = &dev_priv->mode_info; + struct radeon_pll *mpll = &mode_info->mpll; + uint32_t ref_div, fb_div; + uint32_t m_spll_ref_fb_div; + + /* FIXME wait for idle */ + + m_spll_ref_fb_div = RADEON_READ_PLL(dev_priv, RADEON_M_SPLL_REF_FB_DIV); + m_spll_ref_fb_div &= ((RADEON_M_SPLL_REF_DIV_MASK << RADEON_M_SPLL_REF_DIV_SHIFT) | + (RADEON_SPLL_FB_DIV_MASK << RADEON_SPLL_FB_DIV_SHIFT)); + ref_div = m_spll_ref_fb_div & RADEON_M_SPLL_REF_DIV_MASK; + + fb_div = radeon_div(mem_clock * ref_div, mpll->reference_freq); + m_spll_ref_fb_div |= (fb_div & RADEON_MPLL_FB_DIV_MASK) << RADEON_MPLL_FB_DIV_SHIFT; + RADEON_WRITE_PLL(dev_priv, RADEON_M_SPLL_REF_FB_DIV, m_spll_ref_fb_div); + +} + +static void radeon_user_framebuffer_destroy(struct drm_framebuffer *fb) +{ + struct radeon_framebuffer *radeon_fb = to_radeon_framebuffer(fb); + struct drm_device *dev = fb->dev; + + if (fb->fbdev) + radeonfb_remove(dev, fb); + + if (radeon_fb->obj) { + mutex_lock(&dev->struct_mutex); + drm_gem_object_unreference(radeon_fb->obj); + mutex_unlock(&dev->struct_mutex); + } + drm_framebuffer_cleanup(fb); + kfree(radeon_fb); +} + +static int radeon_user_framebuffer_create_handle(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned int *handle) +{ + struct radeon_framebuffer *radeon_fb = to_radeon_framebuffer(fb); + + return drm_gem_handle_create(file_priv, radeon_fb->obj, handle); +} + +static const struct drm_framebuffer_funcs radeon_fb_funcs = { + .destroy = radeon_user_framebuffer_destroy, + .create_handle = radeon_user_framebuffer_create_handle, +}; + +struct drm_framebuffer * +radeon_framebuffer_create(struct drm_device *dev, + struct drm_mode_fb_cmd *mode_cmd, + struct drm_gem_object *obj) +{ + struct radeon_framebuffer *radeon_fb; + + radeon_fb = kzalloc(sizeof(*radeon_fb), GFP_KERNEL); + if (!radeon_fb) + return NULL; + + drm_framebuffer_init(dev, &radeon_fb->base, &radeon_fb_funcs); + drm_helper_mode_fill_fb_struct(&radeon_fb->base, mode_cmd); + + radeon_fb->obj = obj; + + return &radeon_fb->base; +} + +static struct drm_framebuffer * +radeon_user_framebuffer_create(struct drm_device *dev, + struct drm_file *file_priv, + struct drm_mode_fb_cmd *mode_cmd) +{ + struct radeon_framebuffer *radeon_fb; + struct drm_gem_object *obj; + + obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle); + + return radeon_framebuffer_create(dev, mode_cmd, obj); +} + +static const struct drm_mode_config_funcs radeon_mode_funcs = { + .fb_create = radeon_user_framebuffer_create, + .fb_changed = radeonfb_probe, +}; + + +int radeon_modeset_init(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + static struct card_info card; + size_t size; + int num_crtc = 2, i; + int ret; + + drm_mode_config_init(dev); + + dev->mode_config.funcs = (void *)&radeon_mode_funcs; + + if (radeon_is_avivo(dev_priv)) { + dev->mode_config.max_width = 8192; + dev->mode_config.max_height = 8192; + } else { + dev->mode_config.max_width = 4096; + dev->mode_config.max_height = 4096; + } + + dev->mode_config.fb_base = dev_priv->fb_aper_offset; + + /* allocate crtcs - TODO single crtc */ + for (i = 0; i < num_crtc; i++) { + radeon_crtc_init(dev, i); + } + + /* okay we should have all the bios connectors */ + + ret = radeon_setup_enc_conn(dev); + + if (!ret) + return ret; + + drm_helper_initial_config(dev, false); + + return 0; +} + + +int radeon_load_modeset_init(struct drm_device *dev) +{ + int ret; + ret = radeon_modeset_init(dev); + + return ret; +} + +void radeon_modeset_cleanup(struct drm_device *dev) +{ + drm_mode_config_cleanup(dev); +} diff --git a/linux-core/radeon_drv.c b/linux-core/radeon_drv.c index 934fa0b9..f4194688 100644 --- a/linux-core/radeon_drv.c +++ b/linux-core/radeon_drv.c @@ -37,52 +37,68 @@ #include "drm_pciids.h" int radeon_no_wb; +int radeon_dynclks = -1; +int radeon_r4xx_atom = 0; +int radeon_agpmode = 0; MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers\n"); module_param_named(no_wb, radeon_no_wb, int, 0444); +int radeon_modeset = 0; +module_param_named(modeset, radeon_modeset, int, 0400); + +MODULE_PARM_DESC(dynclks, "Disable/Enable dynamic clocks"); +module_param_named(dynclks, radeon_dynclks, int, 0444); + +MODULE_PARM_DESC(r4xx_atom, "Enable ATOMBIOS modesetting for R4xx"); +module_param_named(r4xx_atom, radeon_r4xx_atom, int, 0444); + +MODULE_PARM_DESC(agpmode, "AGP Mode (-1 == PCI)"); +module_param_named(agpmode, radeon_agpmode, int, 0444); + + static int dri_library_name(struct drm_device * dev, char * buf) { drm_radeon_private_t *dev_priv = dev->dev_private; - int family = dev_priv->flags & RADEON_FAMILY_MASK; + int family; + + if (!dev_priv) + return 0; + family = dev_priv->flags & RADEON_FAMILY_MASK; return snprintf(buf, PAGE_SIZE, "%s\n", (family < CHIP_R200) ? "radeon" : ((family < CHIP_R300) ? "r200" : "r300")); } -static int radeon_suspend(struct drm_device *dev, pm_message_t state) -{ - drm_radeon_private_t *dev_priv = dev->dev_private; - - /* Disable *all* interrupts */ - if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS690) - RADEON_WRITE(R500_DxMODE_INT_MASK, 0); - RADEON_WRITE(RADEON_GEN_INT_CNTL, 0); - return 0; -} - -static int radeon_resume(struct drm_device *dev) -{ - drm_radeon_private_t *dev_priv = dev->dev_private; - - /* Restore interrupt registers */ - if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS690) - RADEON_WRITE(R500_DxMODE_INT_MASK, dev_priv->r500_disp_irq_reg); - RADEON_WRITE(RADEON_GEN_INT_CNTL, dev_priv->irq_enable_reg); - return 0; -} - static struct pci_device_id pciidlist[] = { radeon_PCI_IDS }; +extern struct drm_fence_driver radeon_fence_driver; + +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, + .move = radeon_move, + .evict_flags = radeon_evict_flags, +}; + static int probe(struct pci_dev *pdev, const struct pci_device_id *ent); static struct drm_driver driver = { .driver_features = DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG | - DRIVER_HAVE_IRQ | DRIVER_HAVE_DMA | DRIVER_IRQ_SHARED, + DRIVER_HAVE_IRQ | DRIVER_HAVE_DMA | DRIVER_IRQ_SHARED | DRIVER_GEM, .dev_priv_size = sizeof(drm_radeon_buf_priv_t), .load = radeon_driver_load, .firstopen = radeon_driver_firstopen, @@ -105,7 +121,13 @@ static struct drm_driver driver = { .get_map_ofs = drm_core_get_map_ofs, .get_reg_ofs = drm_core_get_reg_ofs, .ioctls = radeon_ioctls, + .gem_init_object = radeon_gem_init_object, + .gem_free_object = radeon_gem_free_object, .dma_ioctl = radeon_cp_buffers, + .master_create = radeon_master_create, + .master_destroy = radeon_master_destroy, + .proc_init = radeon_gem_proc_init, + .proc_cleanup = radeon_gem_proc_cleanup, .fops = { .owner = THIS_MODULE, .open = drm_open, @@ -125,6 +147,9 @@ static struct drm_driver driver = { .remove = __devexit_p(drm_cleanup_pci), }, + .fence_driver = &radeon_fence_driver, + .bo_driver = &radeon_bo_driver, + .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = DRIVER_DATE, @@ -141,6 +166,10 @@ static int probe(struct pci_dev *pdev, const struct pci_device_id *ent) static int __init radeon_init(void) { driver.num_ioctls = radeon_max_ioctl; + + if (radeon_modeset == 1) + driver.driver_features |= DRIVER_MODESET; + return drm_init(&driver, pciidlist); } diff --git a/linux-core/radeon_encoders.c b/linux-core/radeon_encoders.c new file mode 100644 index 00000000..87d9184f --- /dev/null +++ b/linux-core/radeon_encoders.c @@ -0,0 +1,1103 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat 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. + * + * Authors: Dave Airlie + * Alex Deucher + */ +#include "drmP.h" +#include "drm_crtc_helper.h" +#include "radeon_drm.h" +#include "radeon_drv.h" + +extern int atom_debug; + +void radeon_rmx_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + + if (mode->hdisplay < radeon_encoder->panel_xres || + mode->vdisplay < radeon_encoder->panel_yres) { + radeon_encoder->flags |= RADEON_USE_RMX; + if (radeon_is_avivo(dev_priv)) { + adjusted_mode->hdisplay = radeon_encoder->panel_xres; + adjusted_mode->vdisplay = radeon_encoder->panel_yres; + adjusted_mode->htotal = radeon_encoder->panel_xres + radeon_encoder->hblank; + adjusted_mode->hsync_start = radeon_encoder->panel_xres + radeon_encoder->hoverplus; + adjusted_mode->hsync_end = adjusted_mode->hsync_start + radeon_encoder->hsync_width; + adjusted_mode->vtotal = radeon_encoder->panel_yres + radeon_encoder->vblank; + adjusted_mode->vsync_start = radeon_encoder->panel_yres + radeon_encoder->voverplus; + adjusted_mode->vsync_end = adjusted_mode->vsync_start + radeon_encoder->vsync_width; + /* update crtc values */ + drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); + /* adjust crtc values */ + adjusted_mode->crtc_hdisplay = radeon_encoder->panel_xres; + adjusted_mode->crtc_vdisplay = radeon_encoder->panel_yres; + adjusted_mode->crtc_htotal = adjusted_mode->crtc_hdisplay + radeon_encoder->hblank; + adjusted_mode->crtc_hsync_start = adjusted_mode->crtc_hdisplay + radeon_encoder->hoverplus; + adjusted_mode->crtc_hsync_end = adjusted_mode->crtc_hsync_start + radeon_encoder->hsync_width; + adjusted_mode->crtc_vtotal = adjusted_mode->crtc_vdisplay + radeon_encoder->vblank; + adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + radeon_encoder->voverplus; + adjusted_mode->crtc_vsync_end = adjusted_mode->crtc_vsync_start + radeon_encoder->vsync_width; + } else { + adjusted_mode->htotal = radeon_encoder->panel_xres + radeon_encoder->hblank; + adjusted_mode->hsync_start = radeon_encoder->panel_xres + radeon_encoder->hoverplus; + adjusted_mode->hsync_end = adjusted_mode->hsync_start + radeon_encoder->hsync_width; + adjusted_mode->vtotal = radeon_encoder->panel_yres + radeon_encoder->vblank; + adjusted_mode->vsync_start = radeon_encoder->panel_yres + radeon_encoder->voverplus; + adjusted_mode->vsync_end = adjusted_mode->vsync_start + radeon_encoder->vsync_width; + /* update crtc values */ + drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); + /* adjust crtc values */ + adjusted_mode->crtc_htotal = adjusted_mode->crtc_hdisplay + radeon_encoder->hblank; + adjusted_mode->crtc_hsync_start = adjusted_mode->crtc_hdisplay + radeon_encoder->hoverplus; + adjusted_mode->crtc_hsync_end = adjusted_mode->crtc_hsync_start + radeon_encoder->hsync_width; + adjusted_mode->crtc_vtotal = adjusted_mode->crtc_vdisplay + radeon_encoder->vblank; + adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + radeon_encoder->voverplus; + adjusted_mode->crtc_vsync_end = adjusted_mode->crtc_vsync_start + radeon_encoder->vsync_width; + } + } +} + + +static int atom_dac_find_atom_type(struct radeon_encoder *radeon_encoder, struct drm_connector *connector) +{ + struct drm_device *dev = radeon_encoder->base.dev; + struct drm_connector *connector_find; + int atom_type = -1; + + if (!connector) { + list_for_each_entry(connector_find, &dev->mode_config.connector_list, head) { + if (connector_find->encoder == &radeon_encoder->base) + connector = connector_find; + } + } + if (connector) { + /* look for the encoder in the connector list - + check if we the DAC is enabled on a VGA or STV/CTV or CV connector */ + /* work out the ATOM_DEVICE bits */ + switch (connector->connector_type) { + case CONNECTOR_VGA: + case CONNECTOR_DVI_I: + case CONNECTOR_DVI_A: + if (radeon_encoder->atom_device & ATOM_DEVICE_CRT1_SUPPORT) + atom_type = ATOM_DEVICE_CRT1_INDEX; + else if (radeon_encoder->atom_device & ATOM_DEVICE_CRT2_SUPPORT) + atom_type = ATOM_DEVICE_CRT2_INDEX; + break; + case CONNECTOR_STV: + case CONNECTOR_CTV: + if (radeon_encoder->atom_device & ATOM_DEVICE_TV1_SUPPORT) + atom_type = ATOM_DEVICE_TV1_INDEX; + break; + case CONNECTOR_DIN: + if (radeon_encoder->atom_device & ATOM_DEVICE_TV1_SUPPORT) + atom_type = ATOM_DEVICE_TV1_INDEX; + if (radeon_encoder->atom_device & ATOM_DEVICE_CV_SUPPORT) + atom_type = ATOM_DEVICE_CV_INDEX; + break; + } + } + + return atom_type; +} + +/* LVTMA encoder for LVDS usage */ +static void atombios_display_device_control(struct drm_encoder *encoder, int index, uint8_t state) +{ + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION args; + + memset(&args, 0, sizeof(args)); + args.ucAction = state; + + atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); +} + +static void atombios_scaler_setup(struct drm_encoder *encoder, struct drm_display_mode *mode) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); + ENABLE_SCALER_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, EnableScaler); + + /* pre-avivo chips only have 1 scaler */ + if (!radeon_is_avivo(dev_priv) && radeon_crtc->crtc_id) + return; + + memset(&args, 0, sizeof(args)); + args.ucScaler = radeon_crtc->crtc_id; + + if (radeon_encoder->flags & RADEON_USE_RMX) { + if (radeon_encoder->rmx_type == RMX_FULL) + args.ucEnable = ATOM_SCALER_EXPANSION; + else if (radeon_encoder->rmx_type == RMX_CENTER) + args.ucEnable = ATOM_SCALER_CENTER; + } else { + if (radeon_is_avivo(dev_priv)) + args.ucEnable = ATOM_SCALER_DISABLE; + else + args.ucEnable = ATOM_SCALER_CENTER; + } + + atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); +} + +void atombios_set_crtc_source(struct drm_encoder *encoder, int source) +{ + int index = GetIndexIntoMasterTable(COMMAND, SelectCRTC_Source); + struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); + struct drm_radeon_private *dev_priv = encoder->dev->dev_private; + uint8_t frev, crev; + SELECT_CRTC_SOURCE_PS_ALLOCATION crtc_src_param; + SELECT_CRTC_SOURCE_PARAMETERS_V2 crtc_src_param2; + uint32_t *param = NULL; + + atom_parse_cmd_header(dev_priv->mode_info.atom_context, index, &frev, &crev); + switch (frev) { + case 1: { + switch (crev) { + case 0: + case 1: + default: + memset(&crtc_src_param, 0, sizeof(crtc_src_param)); + crtc_src_param.ucCRTC = radeon_crtc->crtc_id; + crtc_src_param.ucDevice = source; + param = (uint32_t *)&crtc_src_param; + break; + case 2: + memset(&crtc_src_param2, 0, sizeof(crtc_src_param2)); + crtc_src_param2.ucCRTC = radeon_crtc->crtc_id; + crtc_src_param2.ucEncoderID = source; + switch (source) { + case ATOM_DEVICE_CRT1_INDEX: + case ATOM_DEVICE_CRT2_INDEX: + crtc_src_param2.ucEncodeMode = ATOM_ENCODER_MODE_CRT; + break; + case ATOM_DEVICE_DFP1_INDEX: + case ATOM_DEVICE_DFP2_INDEX: + case ATOM_DEVICE_DFP3_INDEX: + crtc_src_param2.ucEncodeMode = ATOM_ENCODER_MODE_DVI; + // TODO ENCODER MODE + break; + case ATOM_DEVICE_LCD1_INDEX: + crtc_src_param2.ucEncodeMode = ATOM_ENCODER_MODE_LVDS; + break; + case ATOM_DEVICE_TV1_INDEX: + crtc_src_param2.ucEncodeMode = ATOM_ENCODER_MODE_TV; + break; + case ATOM_DEVICE_CV_INDEX: + crtc_src_param2.ucEncodeMode = ATOM_ENCODER_MODE_CV; + break; + } + param = (uint32_t *)&crtc_src_param2; + break; + } + } + break; + default: + return; + } + + atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)param); + +} + +static void atombios_output_digital_mode_set(struct drm_encoder *encoder, + int device, + struct drm_display_mode *mode) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_radeon_private *dev_priv = encoder->dev->dev_private; + uint8_t frev, crev; + int index; + LVDS_ENCODER_CONTROL_PS_ALLOCATION args; + LVDS_ENCODER_CONTROL_PS_ALLOCATION_V2 args2; + uint32_t *param = NULL; + + switch (device) { + case ATOM_DEVICE_DFP1_INDEX: + index = GetIndexIntoMasterTable(COMMAND, TMDS1EncoderControl); + break; + case ATOM_DEVICE_LCD1_INDEX: + index = GetIndexIntoMasterTable(COMMAND, LVDSEncoderControl); + break; + case ATOM_DEVICE_DFP3_INDEX: + index = GetIndexIntoMasterTable(COMMAND, TMDS2EncoderControl); + break; + default: + return; + } + + atom_parse_cmd_header(dev_priv->mode_info.atom_context, index, &frev, &crev); + + switch (frev) { + case 0: + case 1: + switch (crev) { + case 0: + case 1: + memset(&args, 0, sizeof(args)); + args.ucAction = PANEL_ENCODER_ACTION_ENABLE; + // TODO HDMI + //if (radeon_encoder->type == OUTPUT_HDMI) + //disp_data.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE; + args.usPixelClock = cpu_to_le16(mode->clock / 10); + if (device == ATOM_DEVICE_LCD1_INDEX) { + if (radeon_encoder->lvds_misc & (1 << 0)) + args.ucMisc |= PANEL_ENCODER_MISC_DUAL; + if (radeon_encoder->lvds_misc & (1 << 1)) + args.ucMisc |= (1 << 1); + } else { + if (mode->clock > 165000) + args.ucMisc |= PANEL_ENCODER_MISC_DUAL; + // TODO 6-bit DAC + args.ucMisc |= (1 << 1); + } + param = (uint32_t *)&args; + break; + case 2: + case 3: + memset(&args2, 0, sizeof(args2)); + args2.ucAction = PANEL_ENCODER_ACTION_ENABLE; + if (crev == 3) { + // TODO coherent mode + //if (encoder->coherent_mode) + //args2.ucMisc |= PANEL_ENCODER_MISC_COHERENT; + } + // TODO HDMI + //if (radeon_encoder->type == OUTPUT_HDMI) + //args2.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE; + if (device == ATOM_DEVICE_LCD1_INDEX) { + if (radeon_encoder->lvds_misc & (1 << 0)) + args2.ucMisc |= PANEL_ENCODER_MISC_DUAL; + if (radeon_encoder->lvds_misc & (1 << 5)) { + args2.ucSpatial = PANEL_ENCODER_SPATIAL_DITHER_EN; + if (radeon_encoder->lvds_misc & (1 << 1)) + args2.ucSpatial |= PANEL_ENCODER_SPATIAL_DITHER_DEPTH; + } + if (radeon_encoder->lvds_misc & (1 << 6)) { + args2.ucTemporal = PANEL_ENCODER_TEMPORAL_DITHER_EN; + if (radeon_encoder->lvds_misc & (1 << 1)) + args2.ucTemporal |= PANEL_ENCODER_TEMPORAL_DITHER_DEPTH; + } + } else { + if (mode->clock > 165000) + args2.ucMisc |= PANEL_ENCODER_MISC_DUAL; + } + param = (uint32_t *)&args2; + break; + } + break; + } + + atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)param); +} + +static void radeon_lvtma_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + + atombios_scaler_setup(encoder, mode); + atombios_set_crtc_source(encoder, ATOM_DEVICE_LCD1_INDEX); + atombios_output_digital_mode_set(encoder, ATOM_DEVICE_LCD1_INDEX, adjusted_mode); +} + + +static void radeon_lvtma_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + int index = GetIndexIntoMasterTable(COMMAND, LCD1OutputControl); + uint32_t bios_2_scratch, bios_3_scratch; + int crtc_id = 0; + + if (encoder->crtc) { + struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); + crtc_id = radeon_crtc->crtc_id; + } + + if (dev_priv->chip_family >= CHIP_R600) { + bios_2_scratch = RADEON_READ(R600_BIOS_2_SCRATCH); + bios_3_scratch = RADEON_READ(R600_BIOS_3_SCRATCH); + } else { + bios_2_scratch = RADEON_READ(RADEON_BIOS_2_SCRATCH); + bios_3_scratch = RADEON_READ(RADEON_BIOS_3_SCRATCH); + } + + bios_2_scratch &= ~ATOM_S3_LCD1_CRTC_ACTIVE; + bios_3_scratch |= (crtc_id << 17); + + switch(mode) { + case DRM_MODE_DPMS_ON: + atombios_display_device_control(encoder, index, ATOM_ENABLE); + bios_2_scratch &= ~ATOM_S2_LCD1_DPMS_STATE; + bios_3_scratch |= ATOM_S3_LCD1_ACTIVE; + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + atombios_display_device_control(encoder, index, ATOM_DISABLE); + bios_2_scratch |= ATOM_S2_LCD1_DPMS_STATE; + bios_3_scratch &= ~ATOM_S3_LCD1_ACTIVE; + break; + } + + if (dev_priv->chip_family >= CHIP_R600) { + RADEON_WRITE(R600_BIOS_2_SCRATCH, bios_2_scratch); + RADEON_WRITE(R600_BIOS_3_SCRATCH, bios_3_scratch); + } else { + RADEON_WRITE(RADEON_BIOS_2_SCRATCH, bios_2_scratch); + RADEON_WRITE(RADEON_BIOS_3_SCRATCH, bios_3_scratch); + } +} + +static bool radeon_lvtma_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + + radeon_encoder->flags &= ~RADEON_USE_RMX; + + if (radeon_encoder->rmx_type != RMX_OFF) + radeon_rmx_mode_fixup(encoder, mode, adjusted_mode); + + return true; +} + +static void radeon_lvtma_prepare(struct drm_encoder *encoder) +{ + radeon_atom_output_lock(encoder, true); + radeon_lvtma_dpms(encoder, DRM_MODE_DPMS_OFF); +} + +static void radeon_lvtma_commit(struct drm_encoder *encoder) +{ + radeon_lvtma_dpms(encoder, DRM_MODE_DPMS_ON); + radeon_atom_output_lock(encoder, false); +} + +static const struct drm_encoder_helper_funcs radeon_atom_lvtma_helper_funcs = { + .dpms = radeon_lvtma_dpms, + .mode_fixup = radeon_lvtma_mode_fixup, + .prepare = radeon_lvtma_prepare, + .mode_set = radeon_lvtma_mode_set, + .commit = radeon_lvtma_commit, +}; + +void radeon_enc_destroy(struct drm_encoder *encoder) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + drm_encoder_cleanup(encoder); + kfree(radeon_encoder); +} + +static const struct drm_encoder_funcs radeon_atom_lvtma_enc_funcs = { + .destroy = radeon_enc_destroy, +}; + +struct drm_encoder *radeon_encoder_lvtma_add(struct drm_device *dev, int bios_index) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_mode_info *mode_info = &dev_priv->mode_info; + struct radeon_encoder *radeon_encoder; + struct drm_encoder *encoder; + radeon_encoder = kzalloc(sizeof(struct radeon_encoder), GFP_KERNEL); + if (!radeon_encoder) { + return NULL; + } + + encoder = &radeon_encoder->base; + + /* Set LVTMA to only use crtc 0 */ + encoder->possible_crtcs = 0x1; + encoder->possible_clones = 0; + drm_encoder_init(dev, encoder, &radeon_atom_lvtma_enc_funcs, + DRM_MODE_ENCODER_LVDS); + + drm_encoder_helper_add(encoder, &radeon_atom_lvtma_helper_funcs); + radeon_encoder->atom_device = mode_info->bios_connector[bios_index].devices; + + /* TODO get the LVDS info from the BIOS for panel size etc. */ + /* get the lvds info from the bios */ + radeon_atombios_get_lvds_info(radeon_encoder); + + /* LVDS gets default RMX full scaling */ + radeon_encoder->rmx_type = RMX_FULL; + + return encoder; +} + +static void radeon_atom_dac_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_crtc *radeon_crtc; + int atom_type = -1; + int index; + uint32_t bios_2_scratch, bios_3_scratch; + int crtc_id = 0; + + if (encoder->crtc) { + radeon_crtc = to_radeon_crtc(encoder->crtc); + crtc_id = radeon_crtc->crtc_id; + } + + atom_type = atom_dac_find_atom_type(radeon_encoder, NULL); + if (atom_type == -1) + return; + + if (dev_priv->chip_family >= CHIP_R600) { + bios_2_scratch = RADEON_READ(R600_BIOS_2_SCRATCH); + bios_3_scratch = RADEON_READ(R600_BIOS_3_SCRATCH); + } else { + bios_2_scratch = RADEON_READ(RADEON_BIOS_2_SCRATCH); + bios_3_scratch = RADEON_READ(RADEON_BIOS_3_SCRATCH); + } + + switch(atom_type) { + case ATOM_DEVICE_CRT1_INDEX: + index = GetIndexIntoMasterTable(COMMAND, DAC1OutputControl); + bios_2_scratch &= ~ATOM_S3_CRT1_CRTC_ACTIVE; + bios_3_scratch |= (crtc_id << 16); + switch(mode) { + case DRM_MODE_DPMS_ON: + bios_2_scratch &= ~ATOM_S2_CRT1_DPMS_STATE; + bios_3_scratch |= ATOM_S3_CRT1_ACTIVE; + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + bios_2_scratch |= ATOM_S2_CRT1_DPMS_STATE; + bios_3_scratch &= ~ATOM_S3_CRT1_ACTIVE; + break; + } + break; + case ATOM_DEVICE_CRT2_INDEX: + index = GetIndexIntoMasterTable(COMMAND, DAC2OutputControl); + bios_2_scratch &= ~ATOM_S3_CRT2_CRTC_ACTIVE; + bios_3_scratch |= (crtc_id << 20); + switch(mode) { + case DRM_MODE_DPMS_ON: + bios_2_scratch &= ~ATOM_S2_CRT2_DPMS_STATE; + bios_3_scratch |= ATOM_S3_CRT2_ACTIVE; + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + bios_2_scratch |= ATOM_S2_CRT2_DPMS_STATE; + bios_3_scratch &= ~ATOM_S3_CRT2_ACTIVE; + break; + } + break; + case ATOM_DEVICE_TV1_INDEX: + index = GetIndexIntoMasterTable(COMMAND, TV1OutputControl); + bios_3_scratch &= ~ATOM_S3_TV1_CRTC_ACTIVE; + bios_3_scratch |= (crtc_id << 18); + switch(mode) { + case DRM_MODE_DPMS_ON: + bios_2_scratch &= ~ATOM_S2_TV1_DPMS_STATE; + bios_3_scratch |= ATOM_S3_TV1_ACTIVE; + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + bios_2_scratch |= ATOM_S2_TV1_DPMS_STATE; + bios_3_scratch &= ~ATOM_S3_TV1_ACTIVE; + break; + } + break; + case ATOM_DEVICE_CV_INDEX: + index = GetIndexIntoMasterTable(COMMAND, CV1OutputControl); + bios_2_scratch &= ~ATOM_S3_CV_CRTC_ACTIVE; + bios_3_scratch |= (crtc_id << 24); + switch(mode) { + case DRM_MODE_DPMS_ON: + bios_2_scratch &= ~ATOM_S2_CV_DPMS_STATE; + bios_3_scratch |= ATOM_S3_CV_ACTIVE; + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + bios_2_scratch |= ATOM_S2_CV_DPMS_STATE; + bios_3_scratch &= ~ATOM_S3_CV_ACTIVE; + break; + } + break; + default: + return; + } + + switch(mode) { + case DRM_MODE_DPMS_ON: + atombios_display_device_control(encoder, index, ATOM_ENABLE); + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + atombios_display_device_control(encoder, index, ATOM_DISABLE); + break; + } + + if (dev_priv->chip_family >= CHIP_R600) { + RADEON_WRITE(R600_BIOS_2_SCRATCH, bios_2_scratch); + RADEON_WRITE(R600_BIOS_3_SCRATCH, bios_3_scratch); + } else { + RADEON_WRITE(RADEON_BIOS_2_SCRATCH, bios_2_scratch); + RADEON_WRITE(RADEON_BIOS_3_SCRATCH, bios_3_scratch); + } +} + +static bool radeon_atom_dac_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + + /* hw bug */ + if ((mode->flags & DRM_MODE_FLAG_INTERLACE) + && (mode->crtc_vsync_start < (mode->crtc_vdisplay + 2))) + adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + 2; + + return true; +} + +static void radeon_atom_dac_prepare(struct drm_encoder *encoder) +{ + radeon_atom_output_lock(encoder, true); + radeon_atom_dac_dpms(encoder, DRM_MODE_DPMS_OFF); +} + +static void radeon_atom_dac_commit(struct drm_encoder *encoder) +{ + radeon_atom_dac_dpms(encoder, DRM_MODE_DPMS_ON); + radeon_atom_output_lock(encoder, false); +} + +static int atombios_dac_setup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + int atom_type) +{ + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + DAC_ENCODER_CONTROL_PS_ALLOCATION args; + int id = (radeon_encoder->type.dac == DAC_TVDAC); + int index; + + memset(&args, 0, sizeof(args)); + if (id == 0) + index = GetIndexIntoMasterTable(COMMAND, DAC1EncoderControl); + else + index = GetIndexIntoMasterTable(COMMAND, DAC2EncoderControl); + + args.ucAction = 1; + args.usPixelClock = cpu_to_le16(mode->clock / 10); + if ((atom_type == ATOM_DEVICE_CRT1_INDEX) || + (atom_type == ATOM_DEVICE_CRT2_INDEX)) + args.ucDacStandard = id ? ATOM_DAC2_PS2 : ATOM_DAC1_PS2; + else if (atom_type == ATOM_DEVICE_CV_INDEX) + args.ucDacStandard = id ? ATOM_DAC2_CV : ATOM_DAC1_CV; + else if (atom_type == ATOM_DEVICE_TV1_INDEX) + args.ucDacStandard = id ? ATOM_DAC2_NTSC : ATOM_DAC1_NTSC; + /* TODO PAL */ + atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); + + return 0; +} + +static int atombios_tv1_setup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + int atom_type) +{ + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + TV_ENCODER_CONTROL_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, TVEncoderControl); + + memset(&args, 0, sizeof(args)); + args.sTVEncoder.ucAction = 1; + if (atom_type == ATOM_DEVICE_CV_INDEX) + args.sTVEncoder.ucTvStandard = ATOM_TV_CV; + else { + // TODO PAL + args.sTVEncoder.ucTvStandard = ATOM_TV_NTSC; + } + + args.sTVEncoder.usPixelClock = cpu_to_le16(mode->clock / 10); + + atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); + return 0; +} + +static void radeon_atom_dac_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + int atom_type = -1; + + atom_type = atom_dac_find_atom_type(radeon_encoder, NULL); + if (atom_type == -1) + return; + + atombios_scaler_setup(encoder, mode); + atombios_set_crtc_source(encoder, atom_type); + + atombios_dac_setup(encoder, adjusted_mode, atom_type); + if ((atom_type == ATOM_DEVICE_TV1_INDEX) || + (atom_type == ATOM_DEVICE_CV_INDEX)) + atombios_tv1_setup(encoder, adjusted_mode, atom_type); + +} + +static bool atom_dac_load_detect(struct drm_encoder *encoder, int atom_devices) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + DAC_LOAD_DETECTION_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, DAC_LoadDetection); + + memset(&args, 0, sizeof(args)); + args.sDacload.ucMisc = 0; + args.sDacload.ucDacType = (radeon_encoder->type.dac == DAC_PRIMARY) ? ATOM_DAC_A : ATOM_DAC_B; + + if (atom_devices & ATOM_DEVICE_CRT1_SUPPORT) + args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CRT1_SUPPORT); + else if (atom_devices & ATOM_DEVICE_CRT2_SUPPORT) + args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CRT2_SUPPORT); + else if (atom_devices & ATOM_DEVICE_CV_SUPPORT) { + args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CV_SUPPORT); + if (radeon_is_dce3(dev_priv)) + args.sDacload.ucMisc = 1; + } else if (atom_devices & ATOM_DEVICE_TV1_SUPPORT) { + args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_TV1_SUPPORT); + if (radeon_is_dce3(dev_priv)) + args.sDacload.ucMisc = 1; + } else + return false; + + DRM_DEBUG("writing %x %x\n", args.sDacload.usDeviceID, args.sDacload.ucDacType); + atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); + return true; +} + +static enum drm_connector_status radeon_atom_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) +{ + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + int atom_type = -1; + uint32_t bios_0_scratch; + + atom_type = atom_dac_find_atom_type(radeon_encoder, connector); + if (atom_type == -1) { + DRM_DEBUG("exit after find \n"); + return connector_status_unknown; + } + + if(!atom_dac_load_detect(encoder, (1 << atom_type))) { + DRM_DEBUG("detect returned false \n"); + return connector_status_unknown; + } + + + if (dev_priv->chip_family >= CHIP_R600) + bios_0_scratch = RADEON_READ(R600_BIOS_0_SCRATCH); + else + bios_0_scratch = RADEON_READ(RADEON_BIOS_0_SCRATCH); + + DRM_DEBUG("Bios 0 scratch %x\n", bios_0_scratch); + if (radeon_encoder->atom_device & ATOM_DEVICE_CRT1_SUPPORT) { + if (bios_0_scratch & ATOM_S0_CRT1_MASK) + return connector_status_connected; + } else if (radeon_encoder->atom_device & ATOM_DEVICE_CRT2_SUPPORT) { + if (bios_0_scratch & ATOM_S0_CRT2_MASK) + return connector_status_connected; + } else if (radeon_encoder->atom_device & ATOM_DEVICE_CV_SUPPORT) { + if (bios_0_scratch & (ATOM_S0_CV_MASK|ATOM_S0_CV_MASK_A)) + return connector_status_connected; + } else if (radeon_encoder->atom_device & ATOM_DEVICE_TV1_SUPPORT) { + if (bios_0_scratch & (ATOM_S0_TV1_COMPOSITE | ATOM_S0_TV1_COMPOSITE_A)) + return connector_status_connected; // CTV + else if (bios_0_scratch & (ATOM_S0_TV1_SVIDEO | ATOM_S0_TV1_SVIDEO_A)) + return connector_status_connected; // STV + } + return connector_status_disconnected; +} + +static const struct drm_encoder_helper_funcs radeon_atom_dac_helper_funcs = { + .dpms = radeon_atom_dac_dpms, + .mode_fixup = radeon_atom_dac_mode_fixup, + .prepare = radeon_atom_dac_prepare, + .mode_set = radeon_atom_dac_mode_set, + .commit = radeon_atom_dac_commit, + .detect = radeon_atom_dac_detect, +}; + +static const struct drm_encoder_funcs radeon_atom_dac_enc_funcs = { + . destroy = radeon_enc_destroy, +}; + +void atombios_ext_tmds_setup(struct drm_encoder *encoder, + struct drm_display_mode *mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, DVOEncoderControl); + + memset(&args, 0, sizeof(args)); + args.sXTmdsEncoder.ucEnable = PANEL_ENCODER_ACTION_ENABLE; + + if (mode->clock > 165000) + args.sXTmdsEncoder.ucMisc = PANEL_ENCODER_MISC_DUAL; + + // TODO 6-bit DAC + args.sXTmdsEncoder.ucMisc |= (1 << 1); + + atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); +} + +static void atombios_dig1_setup(struct drm_encoder *encoder, + struct drm_display_mode *mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + DIG_ENCODER_CONTROL_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, DIG1EncoderControl); + + memset(&args, 0, sizeof(args)); + args.ucAction = ATOM_ENABLE; + args.usPixelClock = mode->clock / 10; + args.ucConfig = ATOM_ENCODER_CONFIG_TRANSMITTER1; + + // TODO coherent mode +// if (encoder->coherent_mode) +// args.ucConfig |= ATOM_TRANSMITTER_CONFIG_COHERENT; + + if (mode->clock > 165000) { + args.ucConfig |= ATOM_ENCODER_CONFIG_LINKA_B; + args.ucLaneNum = 8; + } else { + args.ucConfig |= ATOM_ENCODER_CONFIG_LINKA; + args.ucLaneNum = 4; + } + + // TODO Encoder MODE + atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); +} + +static void atombios_ddia_setup(struct drm_encoder *encoder, + struct drm_display_mode *mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + DVO_ENCODER_CONTROL_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, DVOEncoderControl); + + memset(&args, 0, sizeof(args)); + args.sDVOEncoder.ucAction = PANEL_ENCODER_ACTION_ENABLE; + args.sDVOEncoder.usPixelClock = cpu_to_le16(mode->clock / 10); + + if (mode->clock > 165000) + args.sDVOEncoder.usDevAttr.sDigAttrib.ucAttribute = PANEL_ENCODER_MISC_DUAL; + + atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); +} + +struct drm_encoder *radeon_encoder_atom_dac_add(struct drm_device *dev, int bios_index, int dac_type, int with_tv) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_mode_info *mode_info = &dev_priv->mode_info; + struct radeon_encoder *radeon_encoder = NULL; + struct drm_encoder *encoder; + int type = with_tv ? DRM_MODE_ENCODER_TVDAC : DRM_MODE_ENCODER_DAC; + int found = 0; + int digital_enc_mask = ~(ATOM_DEVICE_DFP1_SUPPORT | + ATOM_DEVICE_DFP2_SUPPORT | + ATOM_DEVICE_DFP3_SUPPORT | + ATOM_DEVICE_LCD1_SUPPORT); + /* we may already have added this encoder */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + if (encoder->encoder_type != DRM_MODE_ENCODER_DAC || + encoder->encoder_type != DRM_MODE_ENCODER_TVDAC) + continue; + + radeon_encoder = to_radeon_encoder(encoder); + if (radeon_encoder->type.dac == dac_type) { + found = 1; + break; + } + } + + if (found) { + /* upgrade to a TV controlling DAC */ + if (type == DRM_MODE_ENCODER_TVDAC) + encoder->encoder_type = type; + radeon_encoder->atom_device |= mode_info->bios_connector[bios_index].devices; + radeon_encoder->atom_device &= digital_enc_mask; + return encoder; + } + + radeon_encoder = kzalloc(sizeof(struct radeon_encoder), GFP_KERNEL); + if (!radeon_encoder) { + return NULL; + } + + encoder = &radeon_encoder->base; + + encoder->possible_crtcs = 0x3; + encoder->possible_clones = 0; + drm_encoder_init(dev, encoder, &radeon_atom_dac_enc_funcs, + type); + + drm_encoder_helper_add(encoder, &radeon_atom_dac_helper_funcs); + radeon_encoder->type.dac = dac_type; + radeon_encoder->atom_device = mode_info->bios_connector[bios_index].devices; + + /* mask off any digital encoders */ + radeon_encoder->atom_device &= digital_enc_mask; + return encoder; +} + +static void radeon_atom_tmds_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_crtc *radeon_crtc = NULL; + int crtc_id = 0; + int atom_type = -1; + int index = -1; + uint32_t bios_2_scratch, bios_3_scratch; + + if (encoder->crtc) { + radeon_crtc = to_radeon_crtc(encoder->crtc); + crtc_id = radeon_crtc->crtc_id; + } else if (mode == DRM_MODE_DPMS_ON) + return; + + if (radeon_encoder->atom_device & ATOM_DEVICE_DFP1_SUPPORT) + atom_type = ATOM_DEVICE_DFP1_INDEX; + if (radeon_encoder->atom_device & ATOM_DEVICE_DFP2_SUPPORT) + atom_type = ATOM_DEVICE_DFP2_INDEX; + if (radeon_encoder->atom_device & ATOM_DEVICE_DFP3_SUPPORT) + atom_type = ATOM_DEVICE_DFP3_INDEX; + + if (atom_type == -1) + return; + + if (dev_priv->chip_family >= CHIP_R600) { + bios_2_scratch = RADEON_READ(R600_BIOS_2_SCRATCH); + bios_3_scratch = RADEON_READ(R600_BIOS_3_SCRATCH); + } else { + bios_2_scratch = RADEON_READ(RADEON_BIOS_2_SCRATCH); + bios_3_scratch = RADEON_READ(RADEON_BIOS_3_SCRATCH); + } + + switch(atom_type) { + case ATOM_DEVICE_DFP1_INDEX: + index = GetIndexIntoMasterTable(COMMAND, TMDSAOutputControl); + bios_2_scratch &= ~ATOM_S3_DFP1_CRTC_ACTIVE; + bios_3_scratch |= (crtc_id << 19); + switch(mode) { + case DRM_MODE_DPMS_ON: + bios_2_scratch &= ~ATOM_S2_DFP1_DPMS_STATE; + bios_3_scratch |= ATOM_S3_DFP1_ACTIVE; + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + bios_2_scratch |= ATOM_S2_DFP1_DPMS_STATE; + bios_3_scratch &= ~ATOM_S3_DFP1_ACTIVE; + break; + } + break; + case ATOM_DEVICE_DFP2_INDEX: + index = GetIndexIntoMasterTable(COMMAND, DVOOutputControl); + bios_2_scratch &= ~ATOM_S3_DFP2_CRTC_ACTIVE; + bios_3_scratch |= (crtc_id << 23); + switch(mode) { + case DRM_MODE_DPMS_ON: + bios_2_scratch &= ~ATOM_S2_DFP2_DPMS_STATE; + bios_3_scratch |= ATOM_S3_DFP2_ACTIVE; + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + bios_2_scratch |= ATOM_S2_DFP2_DPMS_STATE; + bios_3_scratch &= ~ATOM_S3_DFP2_ACTIVE; + break; + } + break; + case ATOM_DEVICE_DFP3_INDEX: + index = GetIndexIntoMasterTable(COMMAND, LVTMAOutputControl); + bios_2_scratch &= ~ATOM_S3_DFP3_CRTC_ACTIVE; + bios_3_scratch |= (crtc_id << 25); + switch(mode) { + case DRM_MODE_DPMS_ON: + bios_2_scratch &= ~ATOM_S2_DFP3_DPMS_STATE; + bios_3_scratch |= ATOM_S3_DFP3_ACTIVE; + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + bios_2_scratch |= ATOM_S2_DFP3_DPMS_STATE; + bios_3_scratch &= ~ATOM_S3_DFP3_ACTIVE; + break; + } + break; + } + + if (index == -1) + return; + + switch(mode) { + case DRM_MODE_DPMS_ON: + atombios_display_device_control(encoder, index, ATOM_ENABLE); + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + atombios_display_device_control(encoder, index, ATOM_DISABLE); + break; + } + + if (dev_priv->chip_family >= CHIP_R600) { + RADEON_WRITE(R600_BIOS_2_SCRATCH, bios_2_scratch); + RADEON_WRITE(R600_BIOS_3_SCRATCH, bios_3_scratch); + } else { + RADEON_WRITE(RADEON_BIOS_2_SCRATCH, bios_2_scratch); + RADEON_WRITE(RADEON_BIOS_3_SCRATCH, bios_3_scratch); + } +} + +static bool radeon_atom_tmds_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + + /* hw bug */ + if ((mode->flags & DRM_MODE_FLAG_INTERLACE) + && (mode->crtc_vsync_start < (mode->crtc_vdisplay + 2))) + adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + 2; + + return true; +} + +static void radeon_atom_tmds_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + int atom_type; + + if (radeon_encoder->atom_device & ATOM_DEVICE_DFP1_SUPPORT) + atom_type = ATOM_DEVICE_DFP1_INDEX; + if (radeon_encoder->atom_device & ATOM_DEVICE_DFP2_SUPPORT) + atom_type = ATOM_DEVICE_DFP2_INDEX; + if (radeon_encoder->atom_device & ATOM_DEVICE_DFP3_SUPPORT) + atom_type = ATOM_DEVICE_DFP3_INDEX; + + atombios_scaler_setup(encoder, mode); + atombios_set_crtc_source(encoder, atom_type); + + if (atom_type == ATOM_DEVICE_DFP1_INDEX) + atombios_output_digital_mode_set(encoder, ATOM_DEVICE_DFP1_INDEX, adjusted_mode); + if (atom_type == ATOM_DEVICE_DFP2_INDEX) { + if ((dev_priv->chip_family == CHIP_RS600) || + (dev_priv->chip_family == CHIP_RS690) || + (dev_priv->chip_family == CHIP_RS740)) + atombios_ddia_setup(encoder, adjusted_mode); + else + atombios_ext_tmds_setup(encoder, adjusted_mode); + } + if (atom_type == ATOM_DEVICE_DFP3_INDEX) + atombios_output_digital_mode_set(encoder, ATOM_DEVICE_DFP3_INDEX, adjusted_mode); +} + +static void radeon_atom_tmds_prepare(struct drm_encoder *encoder) +{ + radeon_atom_output_lock(encoder, true); + radeon_atom_tmds_dpms(encoder, DRM_MODE_DPMS_OFF); +} + +static void radeon_atom_tmds_commit(struct drm_encoder *encoder) +{ + radeon_atom_tmds_dpms(encoder, DRM_MODE_DPMS_ON); + radeon_atom_output_lock(encoder, false); +} + +static const struct drm_encoder_helper_funcs radeon_atom_tmds_helper_funcs = { + .dpms = radeon_atom_tmds_dpms, + .mode_fixup = radeon_atom_tmds_mode_fixup, + .prepare = radeon_atom_tmds_prepare, + .mode_set = radeon_atom_tmds_mode_set, + .commit = radeon_atom_tmds_commit, + /* no detect for TMDS */ +}; + +static const struct drm_encoder_funcs radeon_atom_tmds_enc_funcs = { + . destroy = radeon_enc_destroy, +}; + +struct drm_encoder *radeon_encoder_atom_tmds_add(struct drm_device *dev, int bios_index, int tmds_type) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_mode_info *mode_info = &dev_priv->mode_info; + struct radeon_encoder *radeon_encoder = NULL; + struct drm_encoder *encoder; + int analog_enc_mask = ~(ATOM_DEVICE_CRT1_SUPPORT | ATOM_DEVICE_CRT2_SUPPORT); + + radeon_encoder = kzalloc(sizeof(struct radeon_encoder), GFP_KERNEL); + if (!radeon_encoder) { + return NULL; + } + + encoder = &radeon_encoder->base; + + encoder->possible_crtcs = 0x3; + encoder->possible_clones = 0; + drm_encoder_init(dev, encoder, &radeon_atom_tmds_enc_funcs, + DRM_MODE_ENCODER_TMDS); + + drm_encoder_helper_add(encoder, &radeon_atom_tmds_helper_funcs); + + radeon_encoder->atom_device = mode_info->bios_connector[bios_index].devices; + + /* mask off any analog encoders */ + radeon_encoder->atom_device &= analog_enc_mask; + return encoder; +} diff --git a/linux-core/radeon_fb.c b/linux-core/radeon_fb.c new file mode 100644 index 00000000..9d30d1f7 --- /dev/null +++ b/linux-core/radeon_fb.c @@ -0,0 +1,1167 @@ +/* + * 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 "drm_crtc_helper.h" +#include "radeon_drm.h" +#include "radeon_drv.h" + +struct radeonfb_par { + struct drm_device *dev; + struct drm_display_mode *our_mode; + struct radeon_framebuffer *radeon_fb; + int crtc_count; + /* crtc currently bound to this */ + uint32_t crtc_ids[2]; +}; +/* +static int +var_to_refresh(const struct fb_var_screeninfo *var) +{ + int xtot = var->xres + var->left_margin + var->right_margin + + var->hsync_len; + int ytot = var->yres + var->upper_margin + var->lower_margin + + var->vsync_len; + + return (1000000000 / var->pixclock * 1000 + 500) / xtot / ytot; +}*/ + +static int radeonfb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) +{ + struct radeonfb_par *par = info->par; + struct drm_device *dev = par->dev; + struct drm_crtc *crtc; + int i; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_mode_set *modeset = &radeon_crtc->mode_set; + struct drm_framebuffer *fb = modeset->fb; + + for (i = 0; i < par->crtc_count; i++) + if (crtc->base.id == par->crtc_ids[i]) + break; + + if (i == par->crtc_count) + continue; + + + if (regno > 255) + return 1; + + if (fb->depth == 8) { + radeon_crtc_fb_gamma_set(crtc, red, green, blue, regno); + return 0; + } + + if (regno < 16) { + switch (fb->depth) { + case 15: + fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) | + ((green & 0xf800) >> 6) | + ((blue & 0xf800) >> 11); + break; + case 16: + fb->pseudo_palette[regno] = (red & 0xf800) | + ((green & 0xfc00) >> 5) | + ((blue & 0xf800) >> 11); + break; + case 24: + case 32: + fb->pseudo_palette[regno] = ((red & 0xff00) << 8) | + (green & 0xff00) | + ((blue & 0xff00) >> 8); + break; + } + } + } + return 0; +} + +static int radeonfb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct radeonfb_par *par = info->par; + struct radeon_framebuffer *radeon_fb = par->radeon_fb; + struct drm_framebuffer *fb = &radeon_fb->base; + int depth; + + if (var->pixclock == -1 || !var->pixclock) + return -EINVAL; + + /* Need to resize the fb object !!! */ + if (var->xres > fb->width || var->yres > fb->height) { + DRM_ERROR("Requested width/height is greater than current fb object %dx%d > %dx%d\n",var->xres,var->yres,fb->width,fb->height); + DRM_ERROR("Need resizing code.\n"); + return -EINVAL; + } + + switch (var->bits_per_pixel) { + case 16: + depth = (var->green.length == 6) ? 16 : 15; + break; + case 32: + depth = (var->transp.length > 0) ? 32 : 24; + break; + default: + depth = var->bits_per_pixel; + break; + } + + switch (depth) { + case 8: + var->red.offset = 0; + var->green.offset = 0; + var->blue.offset = 0; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 0; + var->transp.offset = 0; + break; + case 15: + var->red.offset = 10; + var->green.offset = 5; + var->blue.offset = 0; + var->red.length = 5; + var->green.length = 5; + var->blue.length = 5; + var->transp.length = 1; + var->transp.offset = 15; + break; + case 16: + var->red.offset = 11; + var->green.offset = 5; + var->blue.offset = 0; + var->red.length = 5; + var->green.length = 6; + var->blue.length = 5; + var->transp.length = 0; + var->transp.offset = 0; + break; + case 24: + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 0; + var->transp.offset = 0; + break; + case 32: + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 8; + var->transp.offset = 24; + break; + default: + return -EINVAL; + } + + return 0; +} + +/* this will let fbcon do the mode init */ +/* FIXME: take mode config lock? */ +static int radeonfb_set_par(struct fb_info *info) +{ + struct radeonfb_par *par = info->par; + struct drm_device *dev = par->dev; + struct fb_var_screeninfo *var = &info->var; + int i; + + DRM_DEBUG("%d %d\n", var->xres, var->pixclock); + + if (var->pixclock != -1) { + + DRM_ERROR("PIXEL CLCOK SET\n"); +#if 0 + struct radeon_framebuffer *radeon_fb = par->radeon_fb; + struct drm_framebuffer *fb = &radeon_fb->base; + struct drm_display_mode *drm_mode, *search_mode; + struct drm_connector *connector = NULL; + struct drm_device *dev = par->dev; + + int found = 0; + + switch (var->bits_per_pixel) { + case 16: + fb->depth = (var->green.length == 6) ? 16 : 15; + break; + case 32: + fb->depth = (var->transp.length > 0) ? 32 : 24; + break; + default: + fb->depth = var->bits_per_pixel; + break; + } + + fb->bits_per_pixel = var->bits_per_pixel; + + info->fix.line_length = fb->pitch; + info->fix.smem_len = info->fix.line_length * fb->height; + info->fix.visual = (fb->depth == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; + + info->screen_size = info->fix.smem_len; /* ??? */ + /* reuse desired mode if possible */ + /* create a drm mode */ + drm_mode = drm_mode_create(dev); + drm_mode->hdisplay = var->xres; + drm_mode->hsync_start = drm_mode->hdisplay + var->right_margin; + drm_mode->hsync_end = drm_mode->hsync_start + var->hsync_len; + drm_mode->htotal = drm_mode->hsync_end + var->left_margin; + drm_mode->vdisplay = var->yres; + drm_mode->vsync_start = drm_mode->vdisplay + var->lower_margin; + drm_mode->vsync_end = drm_mode->vsync_start + var->vsync_len; + drm_mode->vtotal = drm_mode->vsync_end + var->upper_margin; + drm_mode->clock = PICOS2KHZ(var->pixclock); + drm_mode->vrefresh = drm_mode_vrefresh(drm_mode); + drm_mode->flags = 0; + drm_mode->flags |= var->sync & FB_SYNC_HOR_HIGH_ACT ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; + drm_mode->flags |= var->sync & FB_SYNC_VERT_HIGH_ACT ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; + + drm_mode_set_name(drm_mode); + drm_mode_set_crtcinfo(drm_mode, CRTC_INTERLACE_HALVE_V); + + found = 0; + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->encoder && + connector->encoder->crtc == par->set.crtc){ + found = 1; + break; + } + } + + /* no connector bound, bail */ + if (!found) + return -EINVAL; + + found = 0; + drm_mode_debug_printmodeline(drm_mode); + list_for_each_entry(search_mode, &connector->modes, head) { + drm_mode_debug_printmodeline(search_mode); + if (drm_mode_equal(drm_mode, search_mode)) { + drm_mode_destroy(dev, drm_mode); + drm_mode = search_mode; + found = 1; + break; + } + } + + /* If we didn't find a matching mode that exists on our connector, + * create a new attachment for the incoming user specified mode + */ + if (!found) { + if (par->our_mode) { + /* this also destroys the mode */ + drm_mode_detachmode_crtc(dev, par->our_mode); + } + + par->set.mode = drm_mode; + par->our_mode = drm_mode; + drm_mode_debug_printmodeline(drm_mode); + /* attach mode */ + drm_mode_attachmode_crtc(dev, par->set.crtc, par->set.mode); + } else { + par->set.mode = drm_mode; + if (par->our_mode) + drm_mode_detachmode_crtc(dev, par->our_mode); + par->our_mode = NULL; + } + return par->set.crtc->funcs->set_config(&par->set); +#endif + return -EINVAL; + } else { + struct drm_crtc *crtc; + int ret; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + + for (i = 0; i < par->crtc_count; i++) + if (crtc->base.id == par->crtc_ids[i]) + break; + + if (i == par->crtc_count) + continue; + + if (crtc->fb == radeon_crtc->mode_set.fb) { + ret = crtc->funcs->set_config(&radeon_crtc->mode_set); + if (ret) + return ret; + } + } + return 0; + } +} + +#if 0 +static void radeonfb_copyarea(struct fb_info *info, + const struct fb_copyarea *region) +{ + struct radeonfb_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 radeonfb_imageblit(struct fb_info *info, const struct fb_image *image) +{ + struct radeonfb_par *par = info->par; + struct drm_device *dev = par->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 cmd, rop_pitch_depth, tmp; + int nbytes, ndwords, pad; + u32 dst_x1, dst_y1, dst_x2, dst_y2, offset, bg, fg; + int dat, ix, iy, iw; + int i, j; + RING_LOCALS; + + /* size in bytes of a padded scanline */ + nbytes = ROUND_UP_TO(image->width, 16) / 8; + + /* Total bytes of padded scanline data to write out. */ + nbytes *= image->height; + + /* + * Check if the glyph data exceeds the immediate mode limit. + * It would take a large font (1K pixels) to hit this limit. + */ + if (nbytes > 128 || image->depth != 1) + return cfb_imageblit(info, image); + + /* Src data is packaged a dword (32-bit) at a time. */ + ndwords = ROUND_UP_TO(nbytes, 4) / 4; + + /* + * Ring has to be padded to a quad word. But because the command starts + with 7 bytes, pad only if there is an even number of ndwords + */ + pad = !(ndwords % 2); + + DRM_DEBUG("imageblit %dx%dx%d to (%d,%d)\n", image->width, + image->height, image->depth, image->dx, image->dy); + DRM_DEBUG("nbytes: %d, ndwords: %d, pad: %d\n", nbytes, ndwords, pad); + + tmp = (XY_MONO_SRC_COPY_IMM_BLT & 0xff) + ndwords; + cmd = (XY_MONO_SRC_COPY_IMM_BLT & ~0xff) | tmp; + offset = par->fb->offset; + dst_x1 = image->dx; + dst_y1 = image->dy; + dst_x2 = image->dx + image->width; + dst_y2 = image->dy + image->height; + rop_pitch_depth = BLT_ROP_GXCOPY | par->fb->pitch; + + switch (par->fb->bits_per_pixel) { + case 8: + rop_pitch_depth |= BLT_DEPTH_8; + fg = image->fg_color; + bg = image->bg_color; + break; + case 16: + rop_pitch_depth |= BLT_DEPTH_16_565; + fg = par->fb->pseudo_palette[image->fg_color]; + bg = par->fb->pseudo_palette[image->bg_color]; + break; + case 32: + rop_pitch_depth |= BLT_DEPTH_32; + cmd |= XY_SRC_COPY_BLT_WRITE_ALPHA | XY_SRC_COPY_BLT_WRITE_RGB; + fg = par->fb->pseudo_palette[image->fg_color]; + bg = par->fb->pseudo_palette[image->bg_color]; + break; + default: + DRM_ERROR("unknown depth %d\n", par->fb->bits_per_pixel); + break; + } + + BEGIN_LP_RING(8 + ndwords); + OUT_RING(cmd); + OUT_RING(rop_pitch_depth); + OUT_RING((dst_y1 << 16) | (dst_x1 & 0xffff)); + OUT_RING((dst_y2 << 16) | (dst_x2 & 0xffff)); + OUT_RING(offset); + OUT_RING(bg); + OUT_RING(fg); + ix = iy = 0; + iw = ROUND_UP_TO(image->width, 8) / 8; + while (ndwords--) { + dat = 0; + for (j = 0; j < 2; ++j) { + for (i = 0; i < 2; ++i) { + if (ix != iw || i == 0) + dat |= image->data[iy*iw + ix++] << (i+j*2)*8; + } + if (ix == iw && iy != (image->height - 1)) { + ix = 0; + ++iy; + } + } + OUT_RING(dat); + } + if (pad) + OUT_RING(MI_NOOP); + ADVANCE_LP_RING(); +} +#endif +static int radeonfb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct radeonfb_par *par = info->par; + struct drm_device *dev = par->dev; + struct drm_mode_set *modeset; + struct drm_crtc *crtc; + struct radeon_crtc *radeon_crtc; + int ret = 0; + int i; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + + for (i = 0; i < par->crtc_count; i++) + if (crtc->base.id == par->crtc_ids[i]) + break; + + if (i == par->crtc_count) + continue; + + radeon_crtc = to_radeon_crtc(crtc); + modeset = &radeon_crtc->mode_set; + + modeset->x = var->xoffset; + modeset->y = var->yoffset; + + if (modeset->num_connectors) { + ret = crtc->funcs->set_config(modeset); + + if (!ret) { + info->var.xoffset = var->xoffset; + info->var.yoffset = var->yoffset; + } + } + } + + return ret; +} + +static void radeonfb_on(struct fb_info *info) +{ + struct radeonfb_par *par = info->par; + struct drm_device *dev = par->dev; + struct drm_crtc *crtc; + struct drm_encoder *encoder; + int i; + + /* + * For each CRTC in this fb, find all associated encoders + * and turn them off, then turn off the CRTC. + */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + + for (i = 0; i < par->crtc_count; i++) + if (crtc->base.id == par->crtc_ids[i]) + break; + + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); + + /* Found a CRTC on this fb, now find encoders */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + if (encoder->crtc == crtc) { + struct drm_encoder_helper_funcs *encoder_funcs; + encoder_funcs = encoder->helper_private; + encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); + } + } + } +} + +static void radeonfb_off(struct fb_info *info, int dpms_mode) +{ + struct radeonfb_par *par = info->par; + struct drm_device *dev = par->dev; + struct drm_crtc *crtc; + struct drm_encoder *encoder; + int i; + + /* + * For each CRTC in this fb, find all associated encoders + * and turn them off, then turn off the CRTC. + */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + + for (i = 0; i < par->crtc_count; i++) + if (crtc->base.id == par->crtc_ids[i]) + break; + + /* Found a CRTC on this fb, now find encoders */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + if (encoder->crtc == crtc) { + struct drm_encoder_helper_funcs *encoder_funcs; + encoder_funcs = encoder->helper_private; + encoder_funcs->dpms(encoder, dpms_mode); + } + } + if (dpms_mode == DRM_MODE_DPMS_OFF) + crtc_funcs->dpms(crtc, dpms_mode); + } +} + +int radeonfb_blank(int blank, struct fb_info *info) +{ + switch (blank) { + case FB_BLANK_UNBLANK: + radeonfb_on(info); + break; + case FB_BLANK_NORMAL: + radeonfb_off(info, DRM_MODE_DPMS_STANDBY); + break; + case FB_BLANK_HSYNC_SUSPEND: + radeonfb_off(info, DRM_MODE_DPMS_STANDBY); + break; + case FB_BLANK_VSYNC_SUSPEND: + radeonfb_off(info, DRM_MODE_DPMS_SUSPEND); + break; + case FB_BLANK_POWERDOWN: + radeonfb_off(info, DRM_MODE_DPMS_OFF); + break; + } + 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, //radeonfb_copyarea, + .fb_imageblit = cfb_imageblit, //radeonfb_imageblit, + .fb_pan_display = radeonfb_pan_display, + .fb_blank = radeonfb_blank, +}; + +/** + * Curretly it is assumed that the old framebuffer is reused. + * + * LOCKING + * caller should hold the mode config lock. + * + */ +int radeonfb_resize(struct drm_device *dev, struct drm_crtc *crtc) +{ + struct fb_info *info; + struct drm_framebuffer *fb; + struct drm_display_mode *mode = crtc->desired_mode; + + fb = crtc->fb; + if (!fb) + return 1; + + info = fb->fbdev; + if (!info) + return 1; + + if (!mode) + return 1; + + info->var.xres = mode->hdisplay; + info->var.right_margin = mode->hsync_start - mode->hdisplay; + info->var.hsync_len = mode->hsync_end - mode->hsync_start; + info->var.left_margin = mode->htotal - mode->hsync_end; + info->var.yres = mode->vdisplay; + info->var.lower_margin = mode->vsync_start - mode->vdisplay; + info->var.vsync_len = mode->vsync_end - mode->vsync_start; + info->var.upper_margin = mode->vtotal - mode->vsync_end; + info->var.pixclock = 10000000 / mode->htotal * 1000 / mode->vtotal * 100; + /* avoid overflow */ + info->var.pixclock = info->var.pixclock * 1000 / mode->vrefresh; + + return 0; +} +EXPORT_SYMBOL(radeonfb_resize); + +static struct drm_mode_set panic_mode; + +int radeonfb_panic(struct notifier_block *n, unsigned long ununsed, + void *panic_str) +{ + DRM_ERROR("panic occurred, switching back to text console\n"); + drm_crtc_helper_set_config(&panic_mode); + + return 0; +} +EXPORT_SYMBOL(radeonfb_panic); + +static struct notifier_block paniced = { + .notifier_call = radeonfb_panic, +}; + +static int radeon_align_pitch(struct drm_device *dev, int width, int bpp) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + int aligned = width; + int align_large = (radeon_is_avivo(dev_priv)); + int pitch_mask = 0; + + switch(bpp / 8) { + case 1: pitch_mask = align_large ? 255 : 127; break; + case 2: pitch_mask = align_large ? 127 : 31; break; + case 3: + case 4: pitch_mask = align_large ? 63 : 15; break; + } + + aligned += pitch_mask; + aligned &= ~pitch_mask; + return aligned; +} + +int radeonfb_create(struct drm_device *dev, uint32_t fb_width, uint32_t fb_height, + uint32_t surface_width, uint32_t surface_height, + struct radeon_framebuffer **radeon_fb_p) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct fb_info *info; + struct radeonfb_par *par; + struct drm_framebuffer *fb; + struct radeon_framebuffer *radeon_fb; + struct drm_mode_fb_cmd mode_cmd; + struct drm_gem_object *fbo = NULL; + struct drm_radeon_gem_object *obj_priv; + struct device *device = &dev->pdev->dev; + int size, aligned_size, ret; + + mode_cmd.width = surface_width;/* crtc->desired_mode->hdisplay; */ + mode_cmd.height = surface_height;/* crtc->desired_mode->vdisplay; */ + + mode_cmd.bpp = 32; + /* need to align pitch with crtc limits */ + mode_cmd.pitch = radeon_align_pitch(dev, mode_cmd.width, mode_cmd.bpp) * ((mode_cmd.bpp + 1) / 8); + mode_cmd.depth = 24; + + size = mode_cmd.pitch * mode_cmd.height; + aligned_size = ALIGN(size, PAGE_SIZE); + + fbo = radeon_gem_object_alloc(dev, aligned_size, 1, RADEON_GEM_DOMAIN_VRAM, 0); + if (!fbo) { + printk(KERN_ERR "failed to allocate framebuffer\n"); + ret = -ENOMEM; + goto out; + } + obj_priv = fbo->driver_private; + + ret = radeon_gem_object_pin(fbo, PAGE_SIZE, RADEON_GEM_DOMAIN_VRAM); + if (ret) { + DRM_ERROR("failed to pin fb: %d\n", ret); + mutex_lock(&dev->struct_mutex); + goto out_unref; + } + + dev_priv->mm.vram_visible -= aligned_size; + + mutex_lock(&dev->struct_mutex); + fb = radeon_framebuffer_create(dev, &mode_cmd, fbo); + if (!fb) { + DRM_ERROR("failed to allocate fb.\n"); + ret = -ENOMEM; + goto out_unref; + } + + list_add(&fb->filp_head, &dev->mode_config.fb_kernel_list); + + radeon_fb = to_radeon_framebuffer(fb); + *radeon_fb_p = radeon_fb; + + info = framebuffer_alloc(sizeof(struct radeonfb_par), device); + if (!info) { + ret = -ENOMEM; + goto out_unref; + } + + par = info->par; + + strcpy(info->fix.id, "radeondrmfb"); + info->fix.type = FB_TYPE_PACKED_PIXELS; + info->fix.visual = FB_VISUAL_TRUECOLOR; + info->fix.type_aux = 0; + info->fix.xpanstep = 1; /* doing it in hw */ + info->fix.ypanstep = 1; /* doing it in hw */ + info->fix.ywrapstep = 0; + info->fix.accel = FB_ACCEL_I830; + info->fix.type_aux = 0; + + info->flags = FBINFO_DEFAULT; + + info->fbops = &radeonfb_ops; + + info->fix.line_length = fb->pitch; + info->fix.smem_start = dev->mode_config.fb_base + obj_priv->bo->offset; + info->fix.smem_len = size; + + info->flags = FBINFO_DEFAULT; + + ret = drm_bo_kmap(obj_priv->bo, 0, PAGE_ALIGN(size) >> PAGE_SHIFT, + &radeon_fb->kmap_obj); + info->screen_base = radeon_fb->kmap_obj.virtual; + if (!info->screen_base) { + ret = -ENOSPC; + goto out_unref; + } + info->screen_size = size; + + memset(info->screen_base, 0, size); + + info->pseudo_palette = fb->pseudo_palette; + info->var.xres_virtual = fb->width; + info->var.yres_virtual = fb->height; + info->var.bits_per_pixel = fb->bits_per_pixel; + info->var.xoffset = 0; + info->var.yoffset = 0; + info->var.activate = FB_ACTIVATE_NOW; + info->var.height = -1; + info->var.width = -1; + + info->var.xres = fb_width; + info->var.yres = fb_height; + + info->fix.mmio_start = pci_resource_start(dev->pdev, 2); + info->fix.mmio_len = pci_resource_len(dev->pdev, 2); + + info->pixmap.size = 64*1024; + info->pixmap.buf_align = 8; + info->pixmap.access_align = 32; + info->pixmap.flags = FB_PIXMAP_SYSTEM; + info->pixmap.scan_align = 1; + + DRM_DEBUG("fb depth is %d\n", fb->depth); + DRM_DEBUG(" pitch is %d\n", fb->pitch); + switch(fb->depth) { + case 8: + info->var.red.offset = 0; + info->var.green.offset = 0; + info->var.blue.offset = 0; + info->var.red.length = 8; /* 8bit DAC */ + info->var.green.length = 8; + info->var.blue.length = 8; + info->var.transp.offset = 0; + info->var.transp.length = 0; + break; + case 15: + info->var.red.offset = 10; + info->var.green.offset = 5; + info->var.blue.offset = 0; + info->var.red.length = 5; + info->var.green.length = 5; + info->var.blue.length = 5; + info->var.transp.offset = 15; + info->var.transp.length = 1; + break; + case 16: + info->var.red.offset = 11; + info->var.green.offset = 5; + info->var.blue.offset = 0; + info->var.red.length = 5; + info->var.green.length = 6; + info->var.blue.length = 5; + info->var.transp.offset = 0; + break; + case 24: + info->var.red.offset = 16; + info->var.green.offset = 8; + info->var.blue.offset = 0; + info->var.red.length = 8; + info->var.green.length = 8; + info->var.blue.length = 8; + info->var.transp.offset = 0; + info->var.transp.length = 0; + break; + case 32: + info->var.red.offset = 16; + info->var.green.offset = 8; + info->var.blue.offset = 0; + info->var.red.length = 8; + info->var.green.length = 8; + info->var.blue.length = 8; + info->var.transp.offset = 24; + info->var.transp.length = 8; + break; + default: + break; + } + + fb->fbdev = info; + + par->radeon_fb = radeon_fb; + par->dev = dev; + + /* To allow resizeing without swapping buffers */ + printk("allocated %p %dx%d fb: 0x%08x, bo %p\n", dev, radeon_fb->base.width, + radeon_fb->base.height, obj_priv->bo->offset, fbo); + + mutex_unlock(&dev->struct_mutex); + return 0; + +out_unref: + drm_gem_object_unreference(fbo); + mutex_unlock(&dev->struct_mutex); +out: + return ret; +} + +static int radeonfb_multi_fb_probe_crtc(struct drm_device *dev, struct drm_crtc *crtc) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct radeon_framebuffer *radeon_fb; + struct drm_framebuffer *fb; + struct drm_connector *connector; + struct fb_info *info; + struct radeonfb_par *par; + struct drm_mode_set *modeset; + unsigned int width, height; + int new_fb = 0; + int ret, i, conn_count; + + if (!drm_helper_crtc_in_use(crtc)) + return 0; + + if (!crtc->desired_mode) + return 0; + + width = crtc->desired_mode->hdisplay; + height = crtc->desired_mode->vdisplay; + + /* is there an fb bound to this crtc already */ + if (!radeon_crtc->mode_set.fb) { + ret = radeonfb_create(dev, width, height, width, height, &radeon_fb); + if (ret) + return -EINVAL; + new_fb = 1; + } else { + fb = radeon_crtc->mode_set.fb; + radeon_fb = to_radeon_framebuffer(fb); + if ((radeon_fb->base.width < width) || (radeon_fb->base.height < height)) + return -EINVAL; + } + + info = radeon_fb->base.fbdev; + par = info->par; + + modeset = &radeon_crtc->mode_set; + modeset->fb = &radeon_fb->base; + conn_count = 0; + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->encoder) + if (connector->encoder->crtc == modeset->crtc) { + modeset->connectors[conn_count] = connector; + conn_count++; + if (conn_count > RADEONFB_CONN_LIMIT) + BUG(); + } + } + + for (i = conn_count; i < RADEONFB_CONN_LIMIT; i++) + modeset->connectors[i] = NULL; + + par->crtc_ids[0] = crtc->base.id; + + modeset->num_connectors = conn_count; + if (modeset->mode != modeset->crtc->desired_mode) + modeset->mode = modeset->crtc->desired_mode; + + par->crtc_count = 1; + + if (new_fb) { + info->var.pixclock = -1; + if (register_framebuffer(info) < 0) + return -EINVAL; + } else + radeonfb_set_par(info); + + printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, + info->fix.id); + + /* Switch back to kernel console on panic */ + panic_mode = *modeset; + atomic_notifier_chain_register(&panic_notifier_list, &paniced); + printk(KERN_INFO "registered panic notifier\n"); + + return 0; +} + +static int radeonfb_multi_fb_probe(struct drm_device *dev) +{ + + struct drm_crtc *crtc; + int ret = 0; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + ret = radeonfb_multi_fb_probe_crtc(dev, crtc); + if (ret) + return ret; + } + return ret; +} + +static int radeonfb_single_fb_probe(struct drm_device *dev) +{ + struct drm_crtc *crtc; + struct drm_connector *connector; + unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1; + unsigned int surface_width = 0, surface_height = 0; + int new_fb = 0; + int crtc_count = 0; + int ret, i, conn_count = 0; + struct radeon_framebuffer *radeon_fb; + struct fb_info *info; + struct radeonfb_par *par; + struct drm_mode_set *modeset = NULL; + + DRM_DEBUG("\n"); + /* first up get a count of crtcs now in use and new min/maxes width/heights */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (drm_helper_crtc_in_use(crtc)) { + if (crtc->desired_mode) { + if (crtc->desired_mode->hdisplay < fb_width) + fb_width = crtc->desired_mode->hdisplay; + + if (crtc->desired_mode->vdisplay < fb_height) + fb_height = crtc->desired_mode->vdisplay; + + if (crtc->desired_mode->hdisplay > surface_width) + surface_width = crtc->desired_mode->hdisplay; + + if (crtc->desired_mode->vdisplay > surface_height) + surface_height = crtc->desired_mode->vdisplay; + + } + crtc_count++; + } + } + + if (crtc_count == 0 || fb_width == -1 || fb_height == -1) { + /* hmm everyone went away - assume VGA cable just fell out + and will come back later. */ + return 0; + } + + /* do we have an fb already? */ + if (list_empty(&dev->mode_config.fb_kernel_list)) { + /* create an fb if we don't have one */ + ret = radeonfb_create(dev, fb_width, fb_height, surface_width, surface_height, &radeon_fb); + if (ret) + return -EINVAL; + new_fb = 1; + } else { + struct drm_framebuffer *fb; + fb = list_first_entry(&dev->mode_config.fb_kernel_list, struct drm_framebuffer, filp_head); + radeon_fb = to_radeon_framebuffer(fb); + + /* if someone hotplugs something bigger than we have already allocated, we are pwned. + As really we can't resize an fbdev that is in the wild currently due to fbdev + not really being designed for the lower layers moving stuff around under it. + - so in the grand style of things - punt. */ + if ((fb->width < surface_width) || (fb->height < surface_height)) { + DRM_ERROR("Framebuffer not large enough to scale console onto.\n"); + return -EINVAL; + } + } + + info = radeon_fb->base.fbdev; + par = info->par; + + crtc_count = 0; + /* okay we need to setup new connector sets in the crtcs */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + modeset = &radeon_crtc->mode_set; + modeset->fb = &radeon_fb->base; + conn_count = 0; + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->encoder) + if(connector->encoder->crtc == modeset->crtc) { + modeset->connectors[conn_count] = connector; + conn_count++; + if (conn_count > RADEONFB_CONN_LIMIT) + BUG(); + } + } + + for (i = conn_count; i < RADEONFB_CONN_LIMIT; i++) + modeset->connectors[i] = NULL; + + + par->crtc_ids[crtc_count++] = crtc->base.id; + + modeset->num_connectors = conn_count; + if (modeset->mode != modeset->crtc->desired_mode) + modeset->mode = modeset->crtc->desired_mode; + } + par->crtc_count = crtc_count; + + if (new_fb) { + info->var.pixclock = -1; + if (register_framebuffer(info) < 0) + return -EINVAL; + } else + radeonfb_set_par(info); + + printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, + info->fix.id); + + /* Switch back to kernel console on panic */ + panic_mode = *modeset; + atomic_notifier_chain_register(&panic_notifier_list, &paniced); + printk(KERN_INFO "registered panic notifier\n"); + + return 0; +} + +int radeonfb_probe(struct drm_device *dev) +{ + int ret; + + DRM_DEBUG("\n"); + + /* something has changed in the lower levels of hell - deal with it + here */ + + /* two modes : a) 1 fb to rule all crtcs. + b) one fb per crtc. + two actions 1) new connected device + 2) device removed. + case a/1 : if the fb surface isn't big enough - resize the surface fb. + if the fb size isn't big enough - resize fb into surface. + if everything big enough configure the new crtc/etc. + case a/2 : undo the configuration + possibly resize down the fb to fit the new configuration. + case b/1 : see if it is on a new crtc - setup a new fb and add it. + case b/2 : teardown the new fb. + */ + + /* mode a first */ + /* search for an fb */ + // if (radeon_fbpercrtc == 1) { + // ret = radeonfb_multi_fb_probe(dev); + // } else { + ret = radeonfb_single_fb_probe(dev); + // } + + return ret; +} +EXPORT_SYMBOL(radeonfb_probe); + +int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *fb) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct fb_info *info; + struct radeon_framebuffer *radeon_fb = to_radeon_framebuffer(fb); + + if (!fb) + return -EINVAL; + + info = fb->fbdev; + + if (info) { + unregister_framebuffer(info); + drm_bo_kunmap(&radeon_fb->kmap_obj); + dev_priv->mm.vram_visible += radeon_fb->obj->size; + mutex_lock(&dev->struct_mutex); + drm_gem_object_unreference(radeon_fb->obj); + radeon_fb->obj = NULL; + mutex_unlock(&dev->struct_mutex); + framebuffer_release(info); + } + + atomic_notifier_chain_unregister(&panic_notifier_list, &paniced); + memset(&panic_mode, 0, sizeof(struct drm_mode_set)); + return 0; +} +EXPORT_SYMBOL(radeonfb_remove); +MODULE_LICENSE("GPL"); diff --git a/linux-core/radeon_fence.c b/linux-core/radeon_fence.c new file mode 100644 index 00000000..13af804b --- /dev/null +++ b/linux-core/radeon_fence.c @@ -0,0 +1,99 @@ +/************************************************************************** + * + * 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" + +int radeon_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 = (struct drm_radeon_private *) dev->dev_private; + + if (!dev_priv) + return -EINVAL; + + radeon_emit_irq(dev); + + DRM_DEBUG("emitting %d\n", dev_priv->counter); + *sequence = (uint32_t) dev_priv->counter; + *native_type = DRM_FENCE_TYPE_EXE; + + return 0; +} + +static void radeon_fence_poll(struct drm_device *dev, uint32_t fence_class, + uint32_t waiting_types) +{ + struct drm_radeon_private *dev_priv = (struct drm_radeon_private *) dev->dev_private; + uint32_t sequence; + + sequence = RADEON_READ(RADEON_SCRATCH_REG3); + /* this used to be READ_BREADCRUMB(dev_priv); but it caused + * a race somewhere in the fencing irq + */ + + DRM_DEBUG("polling %d\n", sequence); + drm_fence_handler(dev, 0, sequence, + DRM_FENCE_TYPE_EXE, 0); +} + +void radeon_fence_handler(struct drm_device * dev) +{ + struct drm_fence_manager *fm = &dev->fm; + struct drm_fence_class_manager *fc = &fm->fence_class[0]; + + write_lock(&fm->lock); + radeon_fence_poll(dev, 0, fc->waiting_types); + 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. + */ + return 1; +} + + +struct drm_fence_driver radeon_fence_driver = { + .num_classes = 1, + .wrap_diff = (1U << (BREADCRUMB_BITS -1)), + .flush_diff = (1U << (BREADCRUMB_BITS - 2)), + .sequence_mask = BREADCRUMB_MASK, + .emit = radeon_fence_emit_sequence, + .has_irq = radeon_fence_has_irq, + .poll = radeon_fence_poll, +}; + diff --git a/linux-core/radeon_gem.c b/linux-core/radeon_gem.c new file mode 100644 index 00000000..b2e1d7fe --- /dev/null +++ b/linux-core/radeon_gem.c @@ -0,0 +1,1608 @@ +/* + * Copyright 2008 Red Hat 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. + * + * Author: Dave Airlie + */ +#include "drmP.h" +#include "drm.h" + +#include "radeon_drm.h" +#include "radeon_drv.h" + +static int radeon_gem_ib_init(struct drm_device *dev); +static int radeon_gem_ib_destroy(struct drm_device *dev); +static int radeon_gem_dma_bufs_init(struct drm_device *dev); +static void radeon_gem_dma_bufs_destroy(struct drm_device *dev); + +int radeon_gem_init_object(struct drm_gem_object *obj) +{ + struct drm_radeon_gem_object *obj_priv; + + obj_priv = drm_calloc(1, sizeof(*obj_priv), DRM_MEM_DRIVER); + if (!obj_priv) { + return -ENOMEM; + } + + obj->driver_private = obj_priv; + obj_priv->obj = obj; + return 0; +} + +void radeon_gem_free_object(struct drm_gem_object *obj) +{ + + struct drm_radeon_gem_object *obj_priv = obj->driver_private; + + /* tear down the buffer object - gem holds struct mutex */ + drm_bo_takedown_vm_locked(obj_priv->bo); + drm_bo_usage_deref_locked(&obj_priv->bo); + drm_free(obj->driver_private, 1, DRM_MEM_DRIVER); +} + +int radeon_gem_info_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct drm_radeon_gem_info *args = data; + + args->vram_start = dev_priv->mm.vram_offset; + args->vram_size = dev_priv->mm.vram_size; + args->vram_visible = dev_priv->mm.vram_visible; + + args->gart_start = dev_priv->mm.gart_start; + args->gart_size = dev_priv->mm.gart_useable; + + return 0; +} + +struct drm_gem_object *radeon_gem_object_alloc(struct drm_device *dev, int size, int alignment, + int initial_domain, bool discardable) +{ + struct drm_gem_object *obj; + struct drm_radeon_gem_object *obj_priv; + int ret; + uint32_t flags; + uint32_t page_align; + + obj = drm_gem_object_alloc(dev, size); + if (!obj) + return NULL; + + obj_priv = obj->driver_private; + flags = DRM_BO_FLAG_MAPPABLE; + if (initial_domain == RADEON_GEM_DOMAIN_VRAM) + flags |= DRM_BO_FLAG_MEM_VRAM; + else if (initial_domain == RADEON_GEM_DOMAIN_GTT) + flags |= DRM_BO_FLAG_MEM_TT; + else + flags |= DRM_BO_FLAG_MEM_LOCAL | DRM_BO_FLAG_CACHED; + + flags |= DRM_BO_FLAG_READ | DRM_BO_FLAG_WRITE | DRM_BO_FLAG_EXE; + + if (discardable) + flags |= DRM_BO_FLAG_DISCARDABLE; + + if (alignment == 0) + alignment = PAGE_SIZE; + + page_align = alignment >> PAGE_SHIFT; + /* create a TTM BO */ + ret = drm_buffer_object_create(dev, + size, drm_bo_type_device, + flags, 0, page_align, + 0, &obj_priv->bo); + if (ret) + goto fail; + + DRM_DEBUG("%p : size 0x%x, alignment %d, initial_domain %d\n", obj_priv->bo, size, alignment, initial_domain); + return obj; +fail: + + return NULL; +} + +int radeon_gem_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_radeon_gem_create *args = data; + struct drm_radeon_gem_object *obj_priv; + struct drm_gem_object *obj; + int ret = 0; + int handle; + + /* create a gem object to contain this object in */ + args->size = roundup(args->size, PAGE_SIZE); + + obj = radeon_gem_object_alloc(dev, args->size, args->alignment, args->initial_domain, args->no_backing_store); + if (!obj) + return -EINVAL; + + obj_priv = obj->driver_private; + DRM_DEBUG("obj is %p bo is %p, %d\n", obj, obj_priv->bo, obj_priv->bo->num_pages); + ret = drm_gem_handle_create(file_priv, obj, &handle); + mutex_lock(&dev->struct_mutex); + drm_gem_object_handle_unreference(obj); + mutex_unlock(&dev->struct_mutex); + + if (ret) + goto fail; + + args->handle = handle; + + return 0; +fail: + drm_gem_object_unreference(obj); + + return ret; +} + +int radeon_gem_set_domain(struct drm_gem_object *obj, uint32_t read_domains, uint32_t write_domain, uint32_t *flags_p, bool unfenced) +{ + struct drm_radeon_gem_object *obj_priv; + uint32_t flags = 0; + int ret; + + obj_priv = obj->driver_private; + + /* work out where to validate the buffer to */ + if (write_domain) { /* write domains always win */ + if (write_domain == RADEON_GEM_DOMAIN_VRAM) + flags = DRM_BO_FLAG_MEM_VRAM; + else if (write_domain == RADEON_GEM_DOMAIN_GTT) + flags = DRM_BO_FLAG_MEM_TT; // need a can write gart check + else + return -EINVAL; // we can't write to system RAM + } else { + /* okay for a read domain - prefer wherever the object is now or close enough */ + if (read_domains == 0) + return -EINVAL; + + /* if its already a local memory and CPU is valid do nothing */ + if (read_domains & RADEON_GEM_DOMAIN_CPU) { + if (obj_priv->bo->mem.mem_type == DRM_BO_MEM_LOCAL) + return 0; + if (read_domains == RADEON_GEM_DOMAIN_CPU) + return -EINVAL; + } + + /* simple case no choice in domains */ + if (read_domains == RADEON_GEM_DOMAIN_VRAM) + flags = DRM_BO_FLAG_MEM_VRAM; + else if (read_domains == RADEON_GEM_DOMAIN_GTT) + flags = DRM_BO_FLAG_MEM_TT; + else if ((obj_priv->bo->mem.mem_type == DRM_BO_MEM_VRAM) && (read_domains & RADEON_GEM_DOMAIN_VRAM)) + flags = DRM_BO_FLAG_MEM_VRAM; + else if ((obj_priv->bo->mem.mem_type == DRM_BO_MEM_TT) && (read_domains & RADEON_GEM_DOMAIN_GTT)) + flags = DRM_BO_FLAG_MEM_TT; + else if ((obj_priv->bo->mem.mem_type == DRM_BO_MEM_LOCAL) && (read_domains & RADEON_GEM_DOMAIN_GTT)) + flags = DRM_BO_FLAG_MEM_TT; + + /* no idea here just set whatever we are input */ + if (flags == 0) { + if (read_domains & RADEON_GEM_DOMAIN_VRAM) + flags |= DRM_BO_FLAG_MEM_VRAM; + if (read_domains & RADEON_GEM_DOMAIN_GTT) + flags |= DRM_BO_FLAG_MEM_TT; + } + } + + /* if this BO is pinned then we ain't moving it anywhere */ + if (obj_priv->bo->pinned_mem_type && unfenced) + return 0; + + DRM_DEBUG("validating %p from %d into %x %d %d\n", obj_priv->bo, obj_priv->bo->mem.mem_type, flags, read_domains, write_domain); + ret = drm_bo_do_validate(obj_priv->bo, flags, DRM_BO_MASK_MEM | DRM_BO_FLAG_CACHED, + unfenced ? DRM_BO_HINT_DONT_FENCE : 0, 0); + if (ret) + return ret; + + if (flags_p) + *flags_p = flags; + return 0; + +} + +int radeon_gem_set_domain_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + /* transition the BO to a domain - just validate the BO into a certain domain */ + struct drm_radeon_gem_set_domain *args = data; + struct drm_gem_object *obj; + struct drm_radeon_gem_object *obj_priv; + int ret; + + /* for now if someone requests domain CPU - just make sure the buffer is finished with */ + + /* just do a BO wait for now */ + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (obj == NULL) + return -EINVAL; + + obj_priv = obj->driver_private; + + ret = radeon_gem_set_domain(obj, args->read_domains, args->write_domain, NULL, true); + + mutex_lock(&dev->struct_mutex); + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + return ret; +} + +int radeon_gem_pread_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return -ENOSYS; +} + +int radeon_gem_pwrite_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_radeon_gem_pwrite *args = data; + struct drm_gem_object *obj; + struct drm_radeon_gem_object *obj_priv; + int ret; + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (obj == NULL) + return -EINVAL; + + obj_priv = obj->driver_private; + + /* check where the buffer is first - if not in VRAM + fallback to userspace copying for now */ + mutex_lock(&obj_priv->bo->mutex); + if (obj_priv->bo->mem.mem_type != DRM_BO_MEM_VRAM) { + ret = -EINVAL; + goto out_unlock; + } + + DRM_ERROR("pwriting data->size %lld %llx\n", args->size, args->offset); + ret = -EINVAL; + +#if 0 + /* so need to grab an IB, copy the data into it in a loop + and send them to VRAM using HDB */ + while ((buf = radeon_host_data_blit(dev, cpp, w, dst_pitch_off, &buf_pitch, + x, &y, (unsigned int*)&h, &hpass)) != 0) { + radeon_host_data_blit_copy_pass(dev, cpp, buf, (uint8_t *)src, + hpass, buf_pitch, src_pitch); + src += hpass * src_pitch; + } +#endif +out_unlock: + mutex_unlock(&obj_priv->bo->mutex); + mutex_lock(&dev->struct_mutex); + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + return ret; +} + +int radeon_gem_mmap_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_radeon_gem_mmap *args = data; + struct drm_gem_object *obj; + struct drm_radeon_gem_object *obj_priv; + loff_t offset; + unsigned long addr; + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (obj == NULL) + return -EINVAL; + + offset = args->offset; + + DRM_DEBUG("got here %p\n", obj); + obj_priv = obj->driver_private; + + DRM_DEBUG("got here %p %p %lld %ld\n", obj, obj_priv->bo, args->size, obj_priv->bo->num_pages); + if (!obj_priv->bo) { + mutex_lock(&dev->struct_mutex); + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + return -EINVAL; + } + + down_write(¤t->mm->mmap_sem); + addr = do_mmap_pgoff(file_priv->filp, 0, args->size, + PROT_READ | PROT_WRITE, MAP_SHARED, + obj_priv->bo->map_list.hash.key); + up_write(¤t->mm->mmap_sem); + + DRM_DEBUG("got here %p %d\n", obj, obj_priv->bo->mem.mem_type); + mutex_lock(&dev->struct_mutex); + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + if (IS_ERR((void *)addr)) + return addr; + + args->addr_ptr = (uint64_t) addr; + + return 0; + +} + +int radeon_gem_pin_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_radeon_gem_pin *args = data; + struct drm_gem_object *obj; + struct drm_radeon_gem_object *obj_priv; + int ret; + int flags = DRM_BO_FLAG_NO_EVICT; + int mask = DRM_BO_FLAG_NO_EVICT; + + /* check for valid args */ + if (args->pin_domain) { + mask |= DRM_BO_MASK_MEM; + if (args->pin_domain == RADEON_GEM_DOMAIN_GTT) + flags |= DRM_BO_FLAG_MEM_TT; + else if (args->pin_domain == RADEON_GEM_DOMAIN_VRAM) + flags |= DRM_BO_FLAG_MEM_VRAM; + else /* hand back the offset we currently have if no args supplied + - this is to allow old mesa to work - its a hack */ + flags = 0; + } + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (obj == NULL) + return -EINVAL; + + obj_priv = obj->driver_private; + + /* validate into a pin with no fence */ + DRM_DEBUG("got here %p %p %d\n", obj, obj_priv->bo, atomic_read(&obj_priv->bo->usage)); + if (flags && !(obj_priv->bo->type != drm_bo_type_kernel && !DRM_SUSER(DRM_CURPROC))) { + ret = drm_bo_do_validate(obj_priv->bo, flags, mask, + DRM_BO_HINT_DONT_FENCE, 0); + } else + ret = 0; + + args->offset = obj_priv->bo->offset; + DRM_DEBUG("got here %p %p %x\n", obj, obj_priv->bo, obj_priv->bo->offset); + + mutex_lock(&dev->struct_mutex); + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + return ret; +} + +int radeon_gem_unpin_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_radeon_gem_unpin *args = data; + struct drm_gem_object *obj; + struct drm_radeon_gem_object *obj_priv; + int ret; + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (obj == NULL) + return -EINVAL; + + obj_priv = obj->driver_private; + + /* validate into a pin with no fence */ + + ret = drm_bo_do_validate(obj_priv->bo, 0, DRM_BO_FLAG_NO_EVICT, + DRM_BO_HINT_DONT_FENCE, 0); + + mutex_lock(&dev->struct_mutex); + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + return ret; +} + +int radeon_gem_busy(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return 0; +} + +int radeon_gem_wait_rendering(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_radeon_gem_wait_rendering *args = data; + struct drm_gem_object *obj; + struct drm_radeon_gem_object *obj_priv; + int ret; + + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (obj == NULL) + return -EINVAL; + + obj_priv = obj->driver_private; + + mutex_lock(&obj_priv->bo->mutex); + ret = drm_bo_wait(obj_priv->bo, 0, 1, 1, 0); + mutex_unlock(&obj_priv->bo->mutex); + + mutex_lock(&dev->struct_mutex); + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + return ret; +} + + + +/* + * Depending on card genertation, chipset bugs, etc... the amount of vram + * accessible to the CPU can vary. This function is our best shot at figuring + * it out. Returns a value in KB. + */ +static uint32_t radeon_get_accessible_vram(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + uint32_t aper_size; + u8 byte; + + if (dev_priv->chip_family >= CHIP_R600) + aper_size = RADEON_READ(R600_CONFIG_APER_SIZE) / 1024; + else + aper_size = RADEON_READ(RADEON_CONFIG_APER_SIZE) / 1024; + + /* Set HDP_APER_CNTL only on cards that are known not to be broken, + * that is has the 2nd generation multifunction PCI interface + */ + if (dev_priv->chip_family == CHIP_RV280 || + dev_priv->chip_family == CHIP_RV350 || + dev_priv->chip_family == CHIP_RV380 || + dev_priv->chip_family == CHIP_R420 || + dev_priv->chip_family == CHIP_R423 || + dev_priv->chip_family == CHIP_RV410 || + radeon_is_avivo(dev_priv)) { + uint32_t temp = RADEON_READ(RADEON_HOST_PATH_CNTL); + temp |= RADEON_HDP_APER_CNTL; + RADEON_WRITE(RADEON_HOST_PATH_CNTL, temp); + return aper_size * 2; + } + + /* Older cards have all sorts of funny issues to deal with. First + * check if it's a multifunction card by reading the PCI config + * header type... Limit those to one aperture size + */ + pci_read_config_byte(dev->pdev, 0xe, &byte); + if (byte & 0x80) + return aper_size; + + /* Single function older card. We read HDP_APER_CNTL to see how the BIOS + * have set it up. We don't write this as it's broken on some ASICs but + * we expect the BIOS to have done the right thing (might be too optimistic...) + */ + if (RADEON_READ(RADEON_HOST_PATH_CNTL) & RADEON_HDP_APER_CNTL) + return aper_size * 2; + + return aper_size; +} + +/* code from the DDX - do memory sizing */ +void radeon_vram_setup(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + uint32_t vram; + uint32_t accessible, bar_size; + + if (!radeon_is_avivo(dev_priv) && (dev_priv->flags & RADEON_IS_IGP)) { + uint32_t tom = RADEON_READ(RADEON_NB_TOM); + + vram = (((tom >> 16) - (tom & 0xffff) + 1) << 6); + RADEON_WRITE(RADEON_CONFIG_MEMSIZE, vram * 1024); + } else { + if (dev_priv->chip_family >= CHIP_R600) + vram = RADEON_READ(R600_CONFIG_MEMSIZE) / 1024; + else { + vram = RADEON_READ(RADEON_CONFIG_MEMSIZE) / 1024; + + /* Some production boards of m6 will return 0 if it's 8 MB */ + if (vram == 0) { + vram = 8192; + RADEON_WRITE(RADEON_CONFIG_MEMSIZE, 0x800000); + } + } + } + + accessible = radeon_get_accessible_vram(dev); + + bar_size = drm_get_resource_len(dev, 0) / 1024; + if (bar_size == 0) + bar_size = 0x20000; + if (accessible > bar_size) + accessible = bar_size; + + if (accessible > vram) + accessible = vram; + + DRM_INFO("Detected VRAM RAM=%dK, accessible=%uK, BAR=%uK\n", + vram, accessible, bar_size); + + dev_priv->mm.vram_offset = dev_priv->fb_aper_offset; + dev_priv->mm.vram_size = vram * 1024; + dev_priv->mm.vram_visible = accessible * 1024; + + +} + +static int radeon_gart_init(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + int ret; + u32 base = 0; + + /* setup a 32MB GART */ + dev_priv->gart_size = dev_priv->mm.gart_size; + + dev_priv->gart_info.table_size = RADEON_PCIGART_TABLE_SIZE; + +#if __OS_HAS_AGP + /* setup VRAM vs GART here */ + if (dev_priv->flags & RADEON_IS_AGP) { + base = dev->agp->base; + if ((base + dev_priv->gart_size - 1) >= dev_priv->fb_location && + base < (dev_priv->fb_location + dev_priv->fb_size - 1)) { + DRM_INFO("Can't use agp base @0x%08lx, won't fit\n", + dev->agp->base); + base = 0; + } + } +#endif + + if (base == 0) { + base = dev_priv->fb_location + dev_priv->fb_size; + if (base < dev_priv->fb_location || + ((base + dev_priv->gart_size) & 0xfffffffful) < base) + base = dev_priv->fb_location + - dev_priv->gart_size; + } + /* start on the card */ + dev_priv->gart_vm_start = base & 0xffc00000u; + if (dev_priv->gart_vm_start != base) + DRM_INFO("GART aligned down from 0x%08x to 0x%08x\n", + base, dev_priv->gart_vm_start); + + /* if on PCIE we need to allocate an fb object for the PCIE GART table */ + if (dev_priv->flags & RADEON_IS_PCIE) { + ret = drm_buffer_object_create(dev, RADEON_PCIGART_TABLE_SIZE, + drm_bo_type_kernel, + DRM_BO_FLAG_READ | DRM_BO_FLAG_MEM_VRAM | DRM_BO_FLAG_MAPPABLE | DRM_BO_FLAG_NO_EVICT, + 0, 1, 0, &dev_priv->mm.pcie_table.bo); + if (ret) + return -EINVAL; + + /* subtract from VRAM value reporting to userspace */ + dev_priv->mm.vram_visible -= RADEON_PCIGART_TABLE_SIZE; + + dev_priv->mm.pcie_table_backup = kzalloc(RADEON_PCIGART_TABLE_SIZE, GFP_KERNEL); + if (!dev_priv->mm.pcie_table_backup) + return -EINVAL; + + ret = drm_bo_kmap(dev_priv->mm.pcie_table.bo, 0, RADEON_PCIGART_TABLE_SIZE >> PAGE_SHIFT, + &dev_priv->mm.pcie_table.kmap); + if (ret) + return -EINVAL; + + dev_priv->pcigart_offset_set = 2; + dev_priv->gart_info.bus_addr = dev_priv->fb_location + dev_priv->mm.pcie_table.bo->offset; + dev_priv->gart_info.addr = dev_priv->mm.pcie_table.kmap.virtual; + dev_priv->gart_info.gart_reg_if = DRM_ATI_GART_PCIE; + dev_priv->gart_info.gart_table_location = DRM_ATI_GART_FB; + memset(dev_priv->gart_info.addr, 0, RADEON_PCIGART_TABLE_SIZE); + } else if (!(dev_priv->flags & RADEON_IS_AGP)) { + /* allocate PCI GART table */ + dev_priv->gart_info.table_mask = DMA_BIT_MASK(32); + dev_priv->gart_info.gart_table_location = DRM_ATI_GART_MAIN; + if (dev_priv->flags & RADEON_IS_IGPGART) + dev_priv->gart_info.gart_reg_if = DRM_ATI_GART_IGP; + else + dev_priv->gart_info.gart_reg_if = DRM_ATI_GART_PCI; + + ret = drm_ati_alloc_pcigart_table(dev, &dev_priv->gart_info); + if (ret) { + DRM_ERROR("cannot allocate PCI GART page!\n"); + return -EINVAL; + } + + dev_priv->gart_info.addr = dev_priv->gart_info.table_handle->vaddr; + dev_priv->gart_info.bus_addr = dev_priv->gart_info.table_handle->busaddr; + } + + /* gart values setup - start the GART */ + if (dev_priv->flags & RADEON_IS_AGP) { + radeon_set_pcigart(dev_priv, 0); + } else { + radeon_set_pcigart(dev_priv, 1); + } + + return 0; +} + +int radeon_alloc_gart_objects(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + int ret; + + ret = drm_buffer_object_create(dev, RADEON_DEFAULT_RING_SIZE, + drm_bo_type_kernel, + DRM_BO_FLAG_READ | DRM_BO_FLAG_MEM_TT | + DRM_BO_FLAG_MAPPABLE | DRM_BO_FLAG_NO_EVICT, + 0, 1, 0, &dev_priv->mm.ring.bo); + if (ret) { + if (dev_priv->flags & RADEON_IS_AGP) + DRM_ERROR("failed to allocate ring - most likely an AGP driver bug\n"); + else + DRM_ERROR("failed to allocate ring\n"); + return -EINVAL; + } + + ret = drm_bo_kmap(dev_priv->mm.ring.bo, 0, RADEON_DEFAULT_RING_SIZE >> PAGE_SHIFT, + &dev_priv->mm.ring.kmap); + if (ret) { + DRM_ERROR("failed to map ring\n"); + return -EINVAL; + } + + ret = drm_buffer_object_create(dev, PAGE_SIZE, + drm_bo_type_kernel, + DRM_BO_FLAG_WRITE |DRM_BO_FLAG_READ | DRM_BO_FLAG_MEM_TT | + DRM_BO_FLAG_MAPPABLE | DRM_BO_FLAG_NO_EVICT, + 0, 1, 0, &dev_priv->mm.ring_read.bo); + if (ret) { + DRM_ERROR("failed to allocate ring read\n"); + return -EINVAL; + } + + ret = drm_bo_kmap(dev_priv->mm.ring_read.bo, 0, + PAGE_SIZE >> PAGE_SHIFT, + &dev_priv->mm.ring_read.kmap); + if (ret) { + DRM_ERROR("failed to map ring read\n"); + return -EINVAL; + } + + DRM_DEBUG("Ring ptr %p mapped at %ld %p, read ptr %p maped at %ld %p\n", + dev_priv->mm.ring.bo, dev_priv->mm.ring.bo->offset, dev_priv->mm.ring.kmap.virtual, + dev_priv->mm.ring_read.bo, dev_priv->mm.ring_read.bo->offset, dev_priv->mm.ring_read.kmap.virtual); + + dev_priv->mm.gart_useable -= RADEON_DEFAULT_RING_SIZE + PAGE_SIZE; + + /* init the indirect buffers */ + radeon_gem_ib_init(dev); + radeon_gem_dma_bufs_init(dev); + return 0; + +} + +static bool avivo_get_mc_idle(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + if (dev_priv->chip_family >= CHIP_R600) { + /* no idea where this is on r600 yet */ + return true; + } else if (dev_priv->chip_family == CHIP_RV515) { + if (radeon_read_mc_reg(dev_priv, RV515_MC_STATUS) & RV515_MC_STATUS_IDLE) + return true; + else + return false; + } else if (dev_priv->chip_family == CHIP_RS600) { + if (radeon_read_mc_reg(dev_priv, RS600_MC_STATUS) & RS600_MC_STATUS_IDLE) + return true; + else + return false; + } else if ((dev_priv->chip_family == CHIP_RS690) || + (dev_priv->chip_family == CHIP_RS740)) { + if (radeon_read_mc_reg(dev_priv, RS690_MC_STATUS) & RS690_MC_STATUS_IDLE) + return true; + else + return false; + } else { + if (radeon_read_mc_reg(dev_priv, R520_MC_STATUS) & R520_MC_STATUS_IDLE) + return true; + else + return false; + } +} + + +static void avivo_disable_mc_clients(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + uint32_t tmp; + int timeout; + + radeon_do_wait_for_idle(dev_priv); + + RADEON_WRITE(AVIVO_D1VGA_CONTROL, RADEON_READ(AVIVO_D1VGA_CONTROL) & ~AVIVO_DVGA_CONTROL_MODE_ENABLE); + RADEON_WRITE(AVIVO_D2VGA_CONTROL, RADEON_READ(AVIVO_D2VGA_CONTROL) & ~AVIVO_DVGA_CONTROL_MODE_ENABLE); + + tmp = RADEON_READ(AVIVO_D1CRTC_CONTROL); + RADEON_WRITE(AVIVO_D1CRTC_CONTROL, tmp & ~AVIVO_CRTC_EN); + + tmp = RADEON_READ(AVIVO_D2CRTC_CONTROL); + RADEON_WRITE(AVIVO_D2CRTC_CONTROL, tmp & ~AVIVO_CRTC_EN); + + tmp = RADEON_READ(AVIVO_D2CRTC_CONTROL); + + udelay(1000); + + timeout = 0; + while (!(avivo_get_mc_idle(dev))) { + if (++timeout > 100000) { + DRM_ERROR("Timeout waiting for memory controller to update settings\n"); + DRM_ERROR("Bad things may or may not happen\n"); + } + udelay(10); + } +} + +static inline u32 radeon_busy_wait(struct drm_device *dev, uint32_t reg, uint32_t bits, + unsigned int timeout) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + u32 status; + + do { + udelay(10); + status = RADEON_READ(reg); + timeout--; + } while(status != 0xffffffff && (status & bits) && (timeout > 0)); + + if (timeout == 0) + status = 0xffffffff; + + return status; +} + +/* Wait for vertical sync on primary CRTC */ +static void radeon_wait_for_vsync(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + uint32_t crtc_gen_cntl; + + crtc_gen_cntl = RADEON_READ(RADEON_CRTC_GEN_CNTL); + if ((crtc_gen_cntl & RADEON_CRTC_DISP_REQ_EN_B) || + !(crtc_gen_cntl & RADEON_CRTC_EN)) + return; + + /* Clear the CRTC_VBLANK_SAVE bit */ + RADEON_WRITE(RADEON_CRTC_STATUS, RADEON_CRTC_VBLANK_SAVE_CLEAR); + + radeon_busy_wait(dev, RADEON_CRTC_STATUS, RADEON_CRTC_VBLANK_SAVE, 2000); + +} + +/* Wait for vertical sync on primary CRTC */ +static void radeon_wait_for_vsync2(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + uint32_t crtc2_gen_cntl; + + crtc2_gen_cntl = RADEON_READ(RADEON_CRTC2_GEN_CNTL); + if ((crtc2_gen_cntl & RADEON_CRTC2_DISP_REQ_EN_B) || + !(crtc2_gen_cntl & RADEON_CRTC2_EN)) + return; + + /* Clear the CRTC_VBLANK_SAVE bit */ + RADEON_WRITE(RADEON_CRTC2_STATUS, RADEON_CRTC2_VBLANK_SAVE_CLEAR); + + radeon_busy_wait(dev, RADEON_CRTC2_STATUS, RADEON_CRTC2_VBLANK_SAVE, 2000); +} + +static void legacy_disable_mc_clients(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + uint32_t old_mc_status, status_idle; + uint32_t ov0_scale_cntl, crtc_ext_cntl, crtc_gen_cntl, crtc2_gen_cntl; + uint32_t status; + + radeon_do_wait_for_idle(dev_priv); + + if (dev_priv->flags & RADEON_IS_IGP) + return; + + old_mc_status = RADEON_READ(RADEON_MC_STATUS); + + /* stop display and memory access */ + ov0_scale_cntl = RADEON_READ(RADEON_OV0_SCALE_CNTL); + RADEON_WRITE(RADEON_OV0_SCALE_CNTL, ov0_scale_cntl & ~RADEON_SCALER_ENABLE); + crtc_ext_cntl = RADEON_READ(RADEON_CRTC_EXT_CNTL); + RADEON_WRITE(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl | RADEON_CRTC_DISPLAY_DIS); + crtc_gen_cntl = RADEON_READ(RADEON_CRTC_GEN_CNTL); + + radeon_wait_for_vsync(dev); + + RADEON_WRITE(RADEON_CRTC_GEN_CNTL, + (crtc_gen_cntl & ~(RADEON_CRTC_CUR_EN | RADEON_CRTC_ICON_EN)) | + RADEON_CRTC_DISP_REQ_EN_B | RADEON_CRTC_EXT_DISP_EN); + + if (!(dev_priv->flags & RADEON_SINGLE_CRTC)) { + crtc2_gen_cntl = RADEON_READ(RADEON_CRTC2_GEN_CNTL); + + radeon_wait_for_vsync2(dev); + RADEON_WRITE(RADEON_CRTC2_GEN_CNTL, + (crtc2_gen_cntl & + ~(RADEON_CRTC2_CUR_EN | RADEON_CRTC2_ICON_EN)) | + RADEON_CRTC2_DISP_REQ_EN_B); + } + + udelay(500); + + if (radeon_is_r300(dev_priv)) + status_idle = R300_MC_IDLE; + else + status_idle = RADEON_MC_IDLE; + + status = radeon_busy_wait(dev, RADEON_MC_STATUS, status_idle, 200000); + if (status == 0xffffffff) { + DRM_ERROR("Timeout waiting for memory controller to update settings\n"); + DRM_ERROR("Bad things may or may not happen\n"); + } +} + + +void radeon_init_memory_map(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + u32 mem_size, aper_size; + + dev_priv->mc_fb_location = radeon_read_fb_location(dev_priv); + radeon_read_agp_location(dev_priv, &dev_priv->mc_agp_loc_lo, &dev_priv->mc_agp_loc_hi); + + if (dev_priv->chip_family >= CHIP_R600) { + mem_size = RADEON_READ(R600_CONFIG_MEMSIZE); + aper_size = RADEON_READ(R600_CONFIG_APER_SIZE); + } else { + mem_size = RADEON_READ(RADEON_CONFIG_MEMSIZE); + aper_size = RADEON_READ(RADEON_CONFIG_APER_SIZE); + } + + /* M6s report illegal memory size */ + if (mem_size == 0) + mem_size = 8 * 1024 * 1024; + + /* for RN50/M6/M7 - Novell bug 204882 */ + if (aper_size > mem_size) + mem_size = aper_size; + + if ((dev_priv->chip_family != CHIP_RS600) && + (dev_priv->chip_family != CHIP_RS690) && + (dev_priv->chip_family != CHIP_RS740)) { + if (dev_priv->flags & RADEON_IS_IGP) + dev_priv->mc_fb_location = RADEON_READ(RADEON_NB_TOM); + else { + uint32_t aper0_base; + + if (dev_priv->chip_family >= CHIP_R600) + aper0_base = RADEON_READ(R600_CONFIG_F0_BASE); + else + aper0_base = RADEON_READ(RADEON_CONFIG_APER_0_BASE); + + + /* Some chips have an "issue" with the memory controller, the + * location must be aligned to the size. We just align it down, + * too bad if we walk over the top of system memory, we don't + * use DMA without a remapped anyway. + * Affected chips are rv280, all r3xx, and all r4xx, but not IGP + */ + if (dev_priv->chip_family == CHIP_RV280 || + dev_priv->chip_family == CHIP_R300 || + dev_priv->chip_family == CHIP_R350 || + dev_priv->chip_family == CHIP_RV350 || + dev_priv->chip_family == CHIP_RV380 || + dev_priv->chip_family == CHIP_R420 || + dev_priv->chip_family == CHIP_R423 || + dev_priv->chip_family == CHIP_RV410) + aper0_base &= ~(mem_size - 1); + + if (dev_priv->chip_family >= CHIP_R600) { + dev_priv->mc_fb_location = (aper0_base >> 24) | + (((aper0_base + mem_size - 1) & 0xff000000U) >> 8); + } else { + dev_priv->mc_fb_location = (aper0_base >> 16) | + ((aper0_base + mem_size - 1) & 0xffff0000U); + } + } + } + + if (dev_priv->chip_family >= CHIP_R600) + dev_priv->fb_location = (dev_priv->mc_fb_location & 0xffff) << 24; + else + dev_priv->fb_location = (dev_priv->mc_fb_location & 0xffff) << 16; + + /* updating mc regs here */ + if (radeon_is_avivo(dev_priv)) + avivo_disable_mc_clients(dev); + else + legacy_disable_mc_clients(dev); + + radeon_write_fb_location(dev_priv, dev_priv->mc_fb_location); + + if (radeon_is_avivo(dev_priv)) { + if (dev_priv->chip_family >= CHIP_R600) + RADEON_WRITE(R600_HDP_NONSURFACE_BASE, (dev_priv->mc_fb_location << 16) & 0xff0000); + else + RADEON_WRITE(AVIVO_HDP_FB_LOCATION, dev_priv->mc_fb_location); + } + + if (dev_priv->chip_family >= CHIP_R600) { + dev_priv->fb_location = (radeon_read_fb_location(dev_priv) & 0xffffff) << 24; + dev_priv->fb_size = ((radeon_read_fb_location(dev_priv) & 0xff000000u) + 0x1000000) + - dev_priv->fb_location; + } else { + dev_priv->fb_location = (radeon_read_fb_location(dev_priv) & 0xffff) << 16; + dev_priv->fb_size = + ((radeon_read_fb_location(dev_priv) & 0xffff0000u) + 0x10000) + - dev_priv->fb_location; + } + + /* add an MTRR for the VRAM */ + dev_priv->aper_size = aper_size; + dev_priv->vram_mtrr = mtrr_add(dev_priv->fb_aper_offset, dev_priv->aper_size, MTRR_TYPE_WRCOMB, 1); + +} + +/* init memory manager - start with all of VRAM and a 32MB GART aperture for now */ +int radeon_gem_mm_init(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + int ret; + u32 pg_offset; + + /* init TTM underneath */ + drm_bo_driver_init(dev); + + /* use the uncached allocator */ + dev->bm.allocator_type = _DRM_BM_ALLOCATOR_UNCACHED; + + /* size the mappable VRAM memory for now */ + radeon_vram_setup(dev); + + radeon_init_memory_map(dev); + +#define VRAM_RESERVE_TEXT (256*1024) /* need to reserve 256 for text mode for now */ + dev_priv->mm.vram_visible -= VRAM_RESERVE_TEXT; + pg_offset = VRAM_RESERVE_TEXT >> PAGE_SHIFT; + drm_bo_init_mm(dev, DRM_BO_MEM_VRAM, pg_offset, /*dev_priv->mm.vram_offset >> PAGE_SHIFT,*/ + ((dev_priv->mm.vram_visible) >> PAGE_SHIFT) - 16, + 0); + + + if (dev_priv->chip_family > CHIP_R600) { + dev_priv->mm_enabled = true; + return 0; + } + + dev_priv->mm.gart_size = (32 * 1024 * 1024); + dev_priv->mm.gart_start = 0; + dev_priv->mm.gart_useable = dev_priv->mm.gart_size; + ret = radeon_gart_init(dev); + if (ret) + return -EINVAL; + + drm_bo_init_mm(dev, DRM_BO_MEM_TT, 0, + dev_priv->mm.gart_size >> PAGE_SHIFT, + 0); + + /* need to allocate some objects in the GART */ + /* ring + ring read ptr */ + ret = radeon_alloc_gart_objects(dev); + if (ret) { + radeon_gem_mm_fini(dev); + return -EINVAL; + } + + dev_priv->mm_enabled = true; + return 0; +} + +void radeon_gem_mm_fini(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + radeon_gem_dma_bufs_destroy(dev); + radeon_gem_ib_destroy(dev); + + mutex_lock(&dev->struct_mutex); + + if (dev_priv->mm.ring_read.bo) { + drm_bo_kunmap(&dev_priv->mm.ring_read.kmap); + drm_bo_usage_deref_locked(&dev_priv->mm.ring_read.bo); + } + + if (dev_priv->mm.ring.bo) { + drm_bo_kunmap(&dev_priv->mm.ring.kmap); + drm_bo_usage_deref_locked(&dev_priv->mm.ring.bo); + } + + if (drm_bo_clean_mm(dev, DRM_BO_MEM_TT, 1)) { + DRM_DEBUG("delaying takedown of TTM memory\n"); + } + + if (dev_priv->flags & RADEON_IS_PCIE) { + if (dev_priv->mm.pcie_table_backup) { + kfree(dev_priv->mm.pcie_table_backup); + dev_priv->mm.pcie_table_backup = NULL; + } + if (dev_priv->mm.pcie_table.bo) { + drm_bo_kunmap(&dev_priv->mm.pcie_table.kmap); + drm_bo_usage_deref_locked(&dev_priv->mm.pcie_table.bo); + } + } + + if (drm_bo_clean_mm(dev, DRM_BO_MEM_VRAM, 1)) { + DRM_DEBUG("delaying takedown of VRAM memory\n"); + } + + if (dev_priv->vram_mtrr) + mtrr_del(dev_priv->vram_mtrr, dev_priv->fb_aper_offset, dev_priv->aper_size); + mutex_unlock(&dev->struct_mutex); + + drm_bo_driver_finish(dev); + dev_priv->mm_enabled = false; +} + +int radeon_gem_object_pin(struct drm_gem_object *obj, + uint32_t alignment, uint32_t pin_domain) +{ + struct drm_radeon_gem_object *obj_priv; + int ret; + uint32_t flags = DRM_BO_FLAG_NO_EVICT; + uint32_t mask = DRM_BO_FLAG_NO_EVICT; + + obj_priv = obj->driver_private; + + if (pin_domain) { + mask |= DRM_BO_MASK_MEM; + if (pin_domain == RADEON_GEM_DOMAIN_GTT) + flags |= DRM_BO_FLAG_MEM_TT; + else if (pin_domain == RADEON_GEM_DOMAIN_VRAM) + flags |= DRM_BO_FLAG_MEM_VRAM; + else + return -EINVAL; + } + ret = drm_bo_do_validate(obj_priv->bo, flags, mask, + DRM_BO_HINT_DONT_FENCE, 0); + + return ret; +} + +int radeon_gem_object_unpin(struct drm_gem_object *obj) +{ + struct drm_radeon_gem_object *obj_priv; + int ret; + + obj_priv = obj->driver_private; + + ret = drm_bo_do_validate(obj_priv->bo, 0, DRM_BO_FLAG_NO_EVICT, + DRM_BO_HINT_DONT_FENCE, 0); + + return ret; +} + +#define RADEON_IB_MEMORY (1*1024*1024) +#define RADEON_IB_SIZE (65536) + +#define RADEON_NUM_IB (RADEON_IB_MEMORY / RADEON_IB_SIZE) + +int radeon_gem_ib_get(struct drm_radeon_cs_parser *parser) +{ + int i, index = -1; + int ret; + drm_radeon_private_t *dev_priv = parser->dev->dev_private; + + for (i = 0; i < RADEON_NUM_IB; i++) { + if (!(dev_priv->ib_alloc_bitmap & (1 << i))){ + index = i; + break; + } + } + + /* if all in use we need to wait */ + if (index == -1) { + for (i = 0; i < RADEON_NUM_IB; i++) { + if (dev_priv->ib_alloc_bitmap & (1 << i)) { + mutex_lock(&dev_priv->ib_objs[i]->bo->mutex); + ret = drm_bo_wait(dev_priv->ib_objs[i]->bo, 0, 1, 0, 0); + mutex_unlock(&dev_priv->ib_objs[i]->bo->mutex); + if (ret) + continue; + dev_priv->ib_alloc_bitmap &= ~(1 << i); + index = i; + break; + } + } + } + + if (index == -1) { + DRM_ERROR("Major case fail to allocate IB from freelist %llx\n", dev_priv->ib_alloc_bitmap); + return -EINVAL; + } + + + if (parser->chunks[parser->ib_index].length_dw > RADEON_IB_SIZE / sizeof(uint32_t)) + return -EINVAL; + + ret = drm_bo_do_validate(dev_priv->ib_objs[index]->bo, 0, + DRM_BO_FLAG_NO_EVICT, + 0, 0); + if (ret) { + DRM_ERROR("Failed to validate IB %d\n", index); + return -EINVAL; + } + + parser->ib = dev_priv->ib_objs[index]->kmap.virtual; + parser->card_offset = dev_priv->gart_vm_start + dev_priv->ib_objs[index]->bo->offset; + dev_priv->ib_alloc_bitmap |= (1 << i); + return 0; +} + +static void radeon_gem_ib_free(struct drm_radeon_cs_parser *parser) +{ + struct drm_device *dev = parser->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_fence_object *fence; + int ret; + int i; + + for (i = 0; i < RADEON_NUM_IB; i++) { + if (dev_priv->ib_objs[i]->kmap.virtual == parser->ib) { + /* emit a fence object */ + ret = drm_fence_buffer_objects(dev, NULL, 0, NULL, &fence); + dev_priv->irq_emitted = 0; + if (ret) { + drm_putback_buffer_objects(dev); + } + /* dereference the fence object */ + if (fence) + drm_fence_usage_deref_unlocked(&fence); + } + } + +} + +static int radeon_gem_ib_destroy(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + int i; + + if (dev_priv->ib_objs) { + for (i = 0; i < RADEON_NUM_IB; i++) { + if (dev_priv->ib_objs[i]) { + drm_bo_kunmap(&dev_priv->ib_objs[i]->kmap); + drm_bo_usage_deref_unlocked(&dev_priv->ib_objs[i]->bo); + } + drm_free(dev_priv->ib_objs[i], sizeof(struct radeon_mm_obj), DRM_MEM_DRIVER); + } + drm_free(dev_priv->ib_objs, RADEON_NUM_IB*sizeof(struct radeon_mm_obj *), DRM_MEM_DRIVER); + } + dev_priv->ib_objs = NULL; + return 0; +} + +static int radeon_gem_find_reloc(struct drm_radeon_cs_parser *parser, + uint32_t offset, uint32_t *handle, + uint32_t *read_domains, uint32_t *write_domain) +{ + struct drm_device *dev = parser->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_kernel_chunk *reloc_chunk = &parser->chunks[parser->reloc_index]; + + if (!reloc_chunk->kdata) + return -EINVAL; + + if (offset > reloc_chunk->length_dw){ + DRM_ERROR("Offset larger than chunk %d %d\n", offset, reloc_chunk->length_dw); + return -EINVAL; + } + + *handle = reloc_chunk->kdata[offset]; + *read_domains = reloc_chunk->kdata[offset + 3]; + *write_domain = reloc_chunk->kdata[offset + 4]; + return 0; +} + +static int radeon_gem_relocate(struct drm_radeon_cs_parser *parser, + uint32_t *reloc, uint32_t *offset) +{ + struct drm_device *dev = parser->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + /* relocate the handle */ + uint32_t read_domains, write_domain; + struct drm_gem_object *obj; + int flags = 0; + int ret; + struct drm_radeon_gem_object *obj_priv; + + if (parser->reloc_index == -1) { + obj = drm_gem_object_lookup(dev, parser->file_priv, reloc[1]); + if (!obj) + return -EINVAL; + read_domains = reloc[2]; + write_domain = reloc[3]; + } else { + uint32_t handle; + + /* have to lookup handle in other chunk */ + ret = radeon_gem_find_reloc(parser, reloc[1], &handle, &read_domains, &write_domain); + if (ret < 0) + return ret; + + obj = drm_gem_object_lookup(dev, parser->file_priv, handle); + if (!obj) + return -EINVAL; + } + + obj_priv = obj->driver_private; + radeon_gem_set_domain(obj, read_domains, write_domain, &flags, false); + + obj_priv->bo->mem.flags &= ~DRM_BO_FLAG_CLEAN; + obj_priv->bo->mem.proposed_flags &= ~DRM_BO_FLAG_CLEAN; + + if (flags == DRM_BO_FLAG_MEM_VRAM) + *offset = obj_priv->bo->offset + dev_priv->fb_location; + else if (flags == DRM_BO_FLAG_MEM_TT) + *offset = obj_priv->bo->offset + dev_priv->gart_vm_start; + + /* BAD BAD BAD - LINKED LIST THE OBJS and UNREF ONCE IB is SUBMITTED */ + drm_gem_object_unreference(obj); + return 0; +} + +/* allocate 1MB of 64k IBs the the kernel can keep mapped */ +static int radeon_gem_ib_init(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + int i; + int ret; + + dev_priv->ib_objs = drm_calloc(RADEON_NUM_IB, sizeof(struct radeon_mm_obj *), DRM_MEM_DRIVER); + if (!dev_priv->ib_objs) + goto free_all; + + for (i = 0; i < RADEON_NUM_IB; i++) { + dev_priv->ib_objs[i] = drm_calloc(1, sizeof(struct radeon_mm_obj), DRM_MEM_DRIVER); + if (!dev_priv->ib_objs[i]) + goto free_all; + + ret = drm_buffer_object_create(dev, RADEON_IB_SIZE, + drm_bo_type_kernel, + DRM_BO_FLAG_READ | DRM_BO_FLAG_MEM_TT | + DRM_BO_FLAG_MAPPABLE, 0, + 0, 0, &dev_priv->ib_objs[i]->bo); + if (ret) + goto free_all; + + ret = drm_bo_kmap(dev_priv->ib_objs[i]->bo, 0, RADEON_IB_SIZE >> PAGE_SHIFT, + &dev_priv->ib_objs[i]->kmap); + + if (ret) + goto free_all; + } + + dev_priv->mm.gart_useable -= RADEON_IB_SIZE * RADEON_NUM_IB; + dev_priv->ib_alloc_bitmap = 0; + + dev_priv->cs.ib_get = radeon_gem_ib_get; + dev_priv->cs.ib_free = radeon_gem_ib_free; + + radeon_cs_init(dev); + dev_priv->cs.relocate = radeon_gem_relocate; + return 0; + +free_all: + radeon_gem_ib_destroy(dev); + return -ENOMEM; +} + +#define RADEON_DMA_BUFFER_SIZE (64 * 1024) +#define RADEON_DMA_BUFFER_COUNT (16) + + +/** + * Cleanup after an error on one of the addbufs() functions. + * + * \param dev DRM device. + * \param entry buffer entry where the error occurred. + * + * Frees any pages and buffers associated with the given entry. + */ +static void drm_cleanup_buf_error(struct drm_device * dev, + struct drm_buf_entry * entry) +{ + int i; + + if (entry->seg_count) { + for (i = 0; i < entry->seg_count; i++) { + if (entry->seglist[i]) { + drm_pci_free(dev, entry->seglist[i]); + } + } + drm_free(entry->seglist, + entry->seg_count * + sizeof(*entry->seglist), DRM_MEM_SEGS); + + entry->seg_count = 0; + } + + if (entry->buf_count) { + for (i = 0; i < entry->buf_count; i++) { + if (entry->buflist[i].dev_private) { + drm_free(entry->buflist[i].dev_private, + entry->buflist[i].dev_priv_size, + DRM_MEM_BUFS); + } + } + drm_free(entry->buflist, + entry->buf_count * + sizeof(*entry->buflist), DRM_MEM_BUFS); + + entry->buf_count = 0; + } +} + +static int radeon_gem_addbufs(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct drm_device_dma *dma = dev->dma; + struct drm_buf_entry *entry; + struct drm_buf *buf; + unsigned long offset; + unsigned long agp_offset; + int count; + int order; + int size; + int alignment; + int page_order; + int total; + int byte_count; + int i; + struct drm_buf **temp_buflist; + + if (!dma) + return -EINVAL; + + count = RADEON_DMA_BUFFER_COUNT; + order = drm_order(RADEON_DMA_BUFFER_SIZE); + size = 1 << order; + + alignment = PAGE_ALIGN(size); + page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; + total = PAGE_SIZE << page_order; + + byte_count = 0; + agp_offset = dev_priv->mm.dma_bufs.bo->offset; + + DRM_DEBUG("count: %d\n", count); + DRM_DEBUG("order: %d\n", order); + DRM_DEBUG("size: %d\n", size); + DRM_DEBUG("agp_offset: %lu\n", agp_offset); + DRM_DEBUG("alignment: %d\n", alignment); + DRM_DEBUG("page_order: %d\n", page_order); + DRM_DEBUG("total: %d\n", total); + + if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) + return -EINVAL; + if (dev->queue_count) + return -EBUSY; /* Not while in use */ + + spin_lock(&dev->count_lock); + if (dev->buf_use) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + atomic_inc(&dev->buf_alloc); + spin_unlock(&dev->count_lock); + + mutex_lock(&dev->struct_mutex); + entry = &dma->bufs[order]; + if (entry->buf_count) { + mutex_unlock(&dev->struct_mutex); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; /* May only call once for each order */ + } + + if (count < 0 || count > 4096) { + mutex_unlock(&dev->struct_mutex); + atomic_dec(&dev->buf_alloc); + return -EINVAL; + } + + entry->buflist = drm_alloc(count * sizeof(*entry->buflist), + DRM_MEM_BUFS); + if (!entry->buflist) { + mutex_unlock(&dev->struct_mutex); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; + } + memset(entry->buflist, 0, count * sizeof(*entry->buflist)); + + entry->buf_size = size; + entry->page_order = page_order; + + offset = 0; + + while (entry->buf_count < count) { + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->order = order; + buf->used = 0; + + buf->offset = (dma->byte_count + offset); + buf->bus_address = dev_priv->gart_vm_start + agp_offset + offset; + buf->address = (void *)(agp_offset + offset); + buf->next = NULL; + buf->waiting = 0; + buf->pending = 0; + init_waitqueue_head(&buf->dma_wait); + buf->file_priv = NULL; + + buf->dev_priv_size = dev->driver->dev_priv_size; + buf->dev_private = drm_alloc(buf->dev_priv_size, DRM_MEM_BUFS); + if (!buf->dev_private) { + /* Set count correctly so we free the proper amount. */ + entry->buf_count = count; + drm_cleanup_buf_error(dev, entry); + mutex_unlock(&dev->struct_mutex); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; + } + + memset(buf->dev_private, 0, buf->dev_priv_size); + + DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address); + + offset += alignment; + entry->buf_count++; + byte_count += PAGE_SIZE << page_order; + } + + DRM_DEBUG("byte_count: %d\n", byte_count); + + temp_buflist = drm_realloc(dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + (dma->buf_count + entry->buf_count) + * sizeof(*dma->buflist), DRM_MEM_BUFS); + if (!temp_buflist) { + /* Free the entry because it isn't valid */ + drm_cleanup_buf_error(dev, entry); + mutex_unlock(&dev->struct_mutex); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; + } + dma->buflist = temp_buflist; + + for (i = 0; i < entry->buf_count; i++) { + dma->buflist[i + dma->buf_count] = &entry->buflist[i]; + } + + dma->buf_count += entry->buf_count; + dma->seg_count += entry->seg_count; + dma->page_count += byte_count >> PAGE_SHIFT; + dma->byte_count += byte_count; + + DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count); + DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count); + + mutex_unlock(&dev->struct_mutex); + + dma->flags = _DRM_DMA_USE_SG; + atomic_dec(&dev->buf_alloc); + return 0; +} + +static int radeon_gem_dma_bufs_init(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + int size = RADEON_DMA_BUFFER_SIZE * RADEON_DMA_BUFFER_COUNT; + int ret; + + ret = drm_dma_setup(dev); + if (ret < 0) + return ret; + + ret = drm_buffer_object_create(dev, size, drm_bo_type_kernel, + DRM_BO_FLAG_READ | DRM_BO_FLAG_WRITE | DRM_BO_FLAG_NO_EVICT | + DRM_BO_FLAG_MEM_TT | DRM_BO_FLAG_MAPPABLE, 0, + 0, 0, &dev_priv->mm.dma_bufs.bo); + if (ret) { + DRM_ERROR("Failed to create DMA bufs\n"); + return -ENOMEM; + } + + ret = drm_bo_kmap(dev_priv->mm.dma_bufs.bo, 0, size >> PAGE_SHIFT, + &dev_priv->mm.dma_bufs.kmap); + if (ret) { + DRM_ERROR("Failed to mmap DMA buffers\n"); + return -ENOMEM; + } + dev_priv->mm.gart_useable -= size; + DRM_DEBUG("\n"); + radeon_gem_addbufs(dev); + + DRM_DEBUG("%lx %d\n", dev_priv->mm.dma_bufs.bo->map_list.hash.key, size); + dev->agp_buffer_token = dev_priv->mm.dma_bufs.bo->map_list.hash.key << PAGE_SHIFT; + dev_priv->mm.fake_agp_map.handle = dev_priv->mm.dma_bufs.kmap.virtual; + dev_priv->mm.fake_agp_map.size = size; + + dev->agp_buffer_map = &dev_priv->mm.fake_agp_map; + dev_priv->gart_buffers_offset = dev_priv->mm.dma_bufs.bo->offset + dev_priv->gart_vm_start; + return 0; +} + +static void radeon_gem_dma_bufs_destroy(struct drm_device *dev) +{ + + struct drm_radeon_private *dev_priv = dev->dev_private; + drm_dma_takedown(dev); + + if (dev_priv->mm.dma_bufs.bo) { + drm_bo_kunmap(&dev_priv->mm.dma_bufs.kmap); + drm_bo_usage_deref_unlocked(&dev_priv->mm.dma_bufs.bo); + } +} + + +static struct drm_gem_object *gem_object_get(struct drm_device *dev, uint32_t name) +{ + struct drm_gem_object *obj; + + spin_lock(&dev->object_name_lock); + obj = idr_find(&dev->object_name_idr, name); + if (obj) + drm_gem_object_reference(obj); + spin_unlock(&dev->object_name_lock); + return obj; +} + +void radeon_gem_update_offsets(struct drm_device *dev, struct drm_master *master) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_master_private *master_priv = master->driver_priv; + drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv; + struct drm_gem_object *obj; + struct drm_radeon_gem_object *obj_priv; + + /* update front_pitch_offset and back_pitch_offset */ + obj = gem_object_get(dev, sarea_priv->front_handle); + if (obj) { + obj_priv = obj->driver_private; + + dev_priv->front_offset = obj_priv->bo->offset; + dev_priv->front_pitch_offset = (((sarea_priv->front_pitch / 64) << 22) | + ((obj_priv->bo->offset + + dev_priv->fb_location) >> 10)); + drm_gem_object_unreference(obj); + } + + obj = gem_object_get(dev, sarea_priv->back_handle); + if (obj) { + obj_priv = obj->driver_private; + dev_priv->back_offset = obj_priv->bo->offset; + dev_priv->back_pitch_offset = (((sarea_priv->back_pitch / 64) << 22) | + ((obj_priv->bo->offset + + dev_priv->fb_location) >> 10)); + drm_gem_object_unreference(obj); + } + dev_priv->color_fmt = RADEON_COLOR_FORMAT_ARGB8888; + +} + + diff --git a/linux-core/radeon_gem_proc.c b/linux-core/radeon_gem_proc.c new file mode 100644 index 00000000..04f5a5fc --- /dev/null +++ b/linux-core/radeon_gem_proc.c @@ -0,0 +1,146 @@ +/* + * Copyright © 2008 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> + * Keith Packard <keithp@keithp.com> + * + */ + +#include "drmP.h" +#include "drm.h" +#include "radeon_drm.h" +#include "radeon_drv.h" + + +static int radeon_ring_info(char *buf, char **start, off_t offset, + int request, int *eof, void *data) +{ + struct drm_minor *minor = (struct drm_minor *) data; + struct drm_device *dev = minor->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + int len = 0; + + if (offset > DRM_PROC_LIMIT) { + *eof = 1; + return 0; + } + + *start = &buf[offset]; + *eof = 0; + DRM_PROC_PRINT("RADEON_CP_RB_WPTR %08x\n", + RADEON_READ(RADEON_CP_RB_WPTR)); + + DRM_PROC_PRINT("RADEON_CP_RB_RPTR %08x\n", + RADEON_READ(RADEON_CP_RB_RPTR)); + + + if (len > request + offset) + return request; + *eof = 1; + return len - offset; +} + +static int radeon_interrupt_info(char *buf, char **start, off_t offset, + int request, int *eof, void *data) +{ + struct drm_minor *minor = (struct drm_minor *) data; + struct drm_device *dev = minor->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + int len = 0; + + if (offset > DRM_PROC_LIMIT) { + *eof = 1; + return 0; + } + + *start = &buf[offset]; + *eof = 0; + DRM_PROC_PRINT("Interrupt enable: %08x\n", + RADEON_READ(RADEON_GEN_INT_CNTL)); + + if (dev_priv->chip_family >= CHIP_RS690) { + DRM_PROC_PRINT("DxMODE_INT_MASK: %08x\n", + RADEON_READ(R500_DxMODE_INT_MASK)); + } + DRM_PROC_PRINT("Interrupts received: %d\n", + atomic_read(&dev_priv->irq_received)); + DRM_PROC_PRINT("Current sequence: %d %d\n", + READ_BREADCRUMB(dev_priv), RADEON_READ(RADEON_SCRATCH_REG3)); + DRM_PROC_PRINT("Counter sequence: %d\n", + dev_priv->counter); + if (dev_priv->chip_family >= CHIP_R300) + DRM_PROC_PRINT("CS: %d\n", + GET_SCRATCH(6)); + + + if (len > request + offset) + return request; + *eof = 1; + return len - offset; +} + +static struct drm_proc_list { + /** file name */ + const char *name; + /** proc callback*/ + int (*f) (char *, char **, off_t, int, int *, void *); +} radeon_gem_proc_list[] = { + {"radeon_gem_interrupt", radeon_interrupt_info}, + {"radeon_gem_ring", radeon_ring_info}, +}; + + +#define RADEON_GEM_PROC_ENTRIES ARRAY_SIZE(radeon_gem_proc_list) + +int radeon_gem_proc_init(struct drm_minor *minor) +{ + struct proc_dir_entry *ent; + int i, j; + + for (i = 0; i < RADEON_GEM_PROC_ENTRIES; i++) { + ent = create_proc_entry(radeon_gem_proc_list[i].name, + S_IFREG | S_IRUGO, minor->dev_root); + if (!ent) { + DRM_ERROR("Cannot create /proc/dri/.../%s\n", + radeon_gem_proc_list[i].name); + for (j = 0; j < i; j++) + remove_proc_entry(radeon_gem_proc_list[i].name, + minor->dev_root); + return -1; + } + ent->read_proc = radeon_gem_proc_list[i].f; + ent->data = minor; + } + return 0; +} + +void radeon_gem_proc_cleanup(struct drm_minor *minor) +{ + int i; + + if (!minor->dev_root) + return; + + for (i = 0; i < RADEON_GEM_PROC_ENTRIES; i++) + remove_proc_entry(radeon_gem_proc_list[i].name, minor->dev_root); +} diff --git a/linux-core/radeon_i2c.c b/linux-core/radeon_i2c.c new file mode 100644 index 00000000..94a485ba --- /dev/null +++ b/linux-core/radeon_i2c.c @@ -0,0 +1,196 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat 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. + * + * Authors: Dave Airlie + * Alex Deucher + */ +#include "drmP.h" +#include "radeon_drm.h" +#include "radeon_drv.h" + +/** + * radeon_ddc_probe + * + */ +bool radeon_ddc_probe(struct radeon_connector *radeon_connector) +{ + 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(&radeon_connector->ddc_bus->adapter, msgs, 2); + if (ret == 2) + return true; + + return false; +} + + +void radeon_i2c_do_lock(struct radeon_connector *radeon_connector, int lock_state) +{ + struct drm_radeon_private *dev_priv = radeon_connector->base.dev->dev_private; + uint32_t temp; + struct radeon_i2c_bus_rec *rec = &radeon_connector->ddc_bus->rec; + + if (lock_state) { + temp = RADEON_READ(rec->a_clk_reg); + temp &= ~(rec->a_clk_mask); + RADEON_WRITE(rec->a_clk_reg, temp); + + temp = RADEON_READ(rec->a_data_reg); + temp &= ~(rec->a_data_mask); + RADEON_WRITE(rec->a_data_reg, temp); + } + + temp = RADEON_READ(rec->mask_clk_reg); + if (lock_state) + temp |= rec->mask_clk_mask; + else + temp &= ~rec->mask_clk_mask; + RADEON_WRITE(rec->mask_clk_reg, temp); + temp = RADEON_READ(rec->mask_clk_reg); + + temp = RADEON_READ(rec->mask_data_reg); + if (lock_state) + temp |= rec->mask_data_mask; + else + temp &= ~rec->mask_data_mask; + RADEON_WRITE(rec->mask_data_reg, temp); + temp = RADEON_READ(rec->mask_data_reg); +} + +static int get_clock(void *i2c_priv) +{ + struct radeon_i2c_chan *i2c = i2c_priv; + struct drm_radeon_private *dev_priv = i2c->dev->dev_private; + struct radeon_i2c_bus_rec *rec = &i2c->rec; + uint32_t val; + + val = RADEON_READ(rec->get_clk_reg); + val &= rec->get_clk_mask; + + return (val != 0); +} + + +static int get_data(void *i2c_priv) +{ + struct radeon_i2c_chan *i2c = i2c_priv; + struct drm_radeon_private *dev_priv = i2c->dev->dev_private; + struct radeon_i2c_bus_rec *rec = &i2c->rec; + uint32_t val; + + val = RADEON_READ(rec->get_data_reg); + val &= rec->get_data_mask; + return (val != 0); +} + +static void set_clock(void *i2c_priv, int clock) +{ + struct radeon_i2c_chan *i2c = i2c_priv; + struct drm_radeon_private *dev_priv = i2c->dev->dev_private; + struct radeon_i2c_bus_rec *rec = &i2c->rec; + uint32_t val; + + val = RADEON_READ(rec->put_clk_reg) & (uint32_t)~(rec->put_clk_mask); + val |= clock ? 0 : rec->put_clk_mask; + RADEON_WRITE(rec->put_clk_reg, val); +} + +static void set_data(void *i2c_priv, int data) +{ + struct radeon_i2c_chan *i2c = i2c_priv; + struct drm_radeon_private *dev_priv = i2c->dev->dev_private; + struct radeon_i2c_bus_rec *rec = &i2c->rec; + uint32_t val; + + val = RADEON_READ(rec->put_data_reg) & (uint32_t)~(rec->put_data_mask); + val |= data ? 0 : rec->put_data_mask; + RADEON_WRITE(rec->put_data_reg, val); +} + +struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev, + struct radeon_i2c_bus_rec *rec, + const char *name) +{ + struct radeon_i2c_chan *i2c; + int ret; + + i2c = drm_calloc(1, sizeof(struct radeon_i2c_chan), DRM_MEM_DRIVER); + if (i2c == NULL) + return NULL; + + i2c->adapter.owner = THIS_MODULE; + i2c->adapter.id = I2C_HW_B_RADEON; + i2c->adapter.algo_data = &i2c->algo; + i2c->dev = 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->rec = *rec; + i2c_set_adapdata(&i2c->adapter, i2c); + + ret = i2c_bit_add_bus(&i2c->adapter); + if (ret) { + DRM_INFO("Failed to register i2c %s\n", name); + goto out_free; + } + + return i2c; +out_free: + drm_free(i2c, sizeof(struct radeon_i2c_chan), DRM_MEM_DRIVER); + return NULL; + +} + +void radeon_i2c_destroy(struct radeon_i2c_chan *i2c) +{ + if (!i2c) + return; + + i2c_del_adapter(&i2c->adapter); + drm_free(i2c, sizeof(struct radeon_i2c_chan), DRM_MEM_DRIVER); +} + +struct drm_encoder *radeon_best_encoder(struct drm_connector *connector) +{ + return NULL; +} + diff --git a/linux-core/radeon_legacy_crtc.c b/linux-core/radeon_legacy_crtc.c new file mode 100644 index 00000000..c0a3c0fa --- /dev/null +++ b/linux-core/radeon_legacy_crtc.c @@ -0,0 +1,1075 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat 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. + * + * Authors: Dave Airlie + * Alex Deucher + */ +#include "drmP.h" +#include "radeon_drm.h" +#include "radeon_drv.h" + +#include "drm_crtc_helper.h" + +void radeon_restore_common_regs(struct drm_device *dev) +{ + /* don't need this yet */ +} + +static void radeon_pll_wait_for_read_update_complete(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + int i = 0; + + /* FIXME: Certain revisions of R300 can't recover here. Not sure of + the cause yet, but this workaround will mask the problem for now. + Other chips usually will pass at the very first test, so the + workaround shouldn't have any effect on them. */ + for (i = 0; + (i < 10000 && + RADEON_READ_PLL(dev_priv, RADEON_PPLL_REF_DIV) & RADEON_PPLL_ATOMIC_UPDATE_R); + i++); +} + +static void radeon_pll_write_update(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + while (RADEON_READ_PLL(dev_priv, RADEON_PPLL_REF_DIV) & RADEON_PPLL_ATOMIC_UPDATE_R); + + RADEON_WRITE_PLL_P(dev_priv, RADEON_PPLL_REF_DIV, + RADEON_PPLL_ATOMIC_UPDATE_W, + ~(RADEON_PPLL_ATOMIC_UPDATE_W)); +} + +static void radeon_pll2_wait_for_read_update_complete(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + int i = 0; + + + /* FIXME: Certain revisions of R300 can't recover here. Not sure of + the cause yet, but this workaround will mask the problem for now. + Other chips usually will pass at the very first test, so the + workaround shouldn't have any effect on them. */ + for (i = 0; + (i < 10000 && + RADEON_READ_PLL(dev_priv, RADEON_P2PLL_REF_DIV) & RADEON_P2PLL_ATOMIC_UPDATE_R); + i++); +} + +static void radeon_pll2_write_update(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + while (RADEON_READ_PLL(dev_priv, RADEON_P2PLL_REF_DIV) & RADEON_P2PLL_ATOMIC_UPDATE_R); + + RADEON_WRITE_PLL_P(dev_priv, RADEON_P2PLL_REF_DIV, + RADEON_P2PLL_ATOMIC_UPDATE_W, + ~(RADEON_P2PLL_ATOMIC_UPDATE_W)); +} + +static uint8_t radeon_compute_pll_gain(uint16_t ref_freq, uint16_t ref_div, + uint16_t fb_div) +{ + unsigned int vcoFreq; + + if (!ref_div) + return 1; + + vcoFreq = ((unsigned)ref_freq & fb_div) / ref_div; + + /* + * This is horribly crude: the VCO frequency range is divided into + * 3 parts, each part having a fixed PLL gain value. + */ + if (vcoFreq >= 30000) + /* + * [300..max] MHz : 7 + */ + return 7; + else if (vcoFreq >= 18000) + /* + * [180..300) MHz : 4 + */ + return 4; + else + /* + * [0..180) MHz : 1 + */ + return 1; +} + +void radeon_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t mask; + + DRM_DEBUG("\n"); + + mask = radeon_crtc->crtc_id ? + (RADEON_CRTC2_DISP_DIS | RADEON_CRTC2_VSYNC_DIS | RADEON_CRTC2_HSYNC_DIS | RADEON_CRTC2_DISP_REQ_EN_B) : + (RADEON_CRTC_DISPLAY_DIS | RADEON_CRTC_VSYNC_DIS | RADEON_CRTC_HSYNC_DIS); + + switch(mode) { + case DRM_MODE_DPMS_ON: + if (radeon_crtc->crtc_id) + RADEON_WRITE_P(RADEON_CRTC2_GEN_CNTL, 0, ~mask); + else { + RADEON_WRITE_P(RADEON_CRTC_GEN_CNTL, 0, ~RADEON_CRTC_DISP_REQ_EN_B); + RADEON_WRITE_P(RADEON_CRTC_EXT_CNTL, 0, ~mask); + } + break; + case DRM_MODE_DPMS_STANDBY: + if (radeon_crtc->crtc_id) + RADEON_WRITE_P(RADEON_CRTC2_GEN_CNTL, (RADEON_CRTC2_DISP_DIS | RADEON_CRTC2_HSYNC_DIS), ~mask); + else { + RADEON_WRITE_P(RADEON_CRTC_GEN_CNTL, 0, ~RADEON_CRTC_DISP_REQ_EN_B); + RADEON_WRITE_P(RADEON_CRTC_EXT_CNTL, (RADEON_CRTC_DISPLAY_DIS | RADEON_CRTC_HSYNC_DIS), ~mask); + } + break; + case DRM_MODE_DPMS_SUSPEND: + if (radeon_crtc->crtc_id) + RADEON_WRITE_P(RADEON_CRTC2_GEN_CNTL, (RADEON_CRTC2_DISP_DIS | RADEON_CRTC2_VSYNC_DIS), ~mask); + else { + RADEON_WRITE_P(RADEON_CRTC_GEN_CNTL, 0, ~RADEON_CRTC_DISP_REQ_EN_B); + RADEON_WRITE_P(RADEON_CRTC_EXT_CNTL, (RADEON_CRTC_DISPLAY_DIS | RADEON_CRTC_VSYNC_DIS), ~mask); + } + break; + case DRM_MODE_DPMS_OFF: + if (radeon_crtc->crtc_id) + RADEON_WRITE_P(RADEON_CRTC2_GEN_CNTL, mask, ~mask); + else { + RADEON_WRITE_P(RADEON_CRTC_GEN_CNTL, RADEON_CRTC_DISP_REQ_EN_B, ~RADEON_CRTC_DISP_REQ_EN_B); + RADEON_WRITE_P(RADEON_CRTC_EXT_CNTL, mask, ~mask); + } + break; + } + + if (mode != DRM_MODE_DPMS_OFF) { + radeon_crtc_load_lut(crtc); + } +} + +/* properly set crtc bpp when using atombios */ +void radeon_legacy_atom_set_surface(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + int format; + uint32_t crtc_gen_cntl, crtc2_gen_cntl; + + switch (crtc->fb->bits_per_pixel) { + case 15: /* 555 */ + format = 3; + break; + case 16: /* 565 */ + format = 4; + break; + case 24: /* RGB */ + format = 5; + break; + case 32: /* xRGB */ + format = 6; + break; + default: + return; + } + + switch (radeon_crtc->crtc_id) { + case 0: + crtc_gen_cntl = RADEON_READ(RADEON_CRTC_GEN_CNTL) & 0xfffff0ff; + crtc_gen_cntl |= (format << 8); + crtc_gen_cntl |= RADEON_CRTC_EXT_DISP_EN; + RADEON_WRITE(RADEON_CRTC_GEN_CNTL, crtc_gen_cntl); + break; + case 1: + crtc2_gen_cntl = RADEON_READ(RADEON_CRTC2_GEN_CNTL) & 0xfffff0ff; + crtc2_gen_cntl |= (format << 8); + RADEON_WRITE(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl); + // not sure we need these... + RADEON_WRITE(RADEON_FP_H2_SYNC_STRT_WID, RADEON_READ(RADEON_CRTC2_H_SYNC_STRT_WID)); + RADEON_WRITE(RADEON_FP_V2_SYNC_STRT_WID, RADEON_READ(RADEON_CRTC2_V_SYNC_STRT_WID)); + break; + } +} + +static bool radeon_set_crtc1_base(struct drm_crtc *crtc, int x, int y) +{ + struct drm_device *dev = crtc->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_framebuffer *radeon_fb; + struct drm_gem_object *obj; + struct drm_radeon_gem_object *obj_priv; + uint32_t base; + uint32_t crtc_offset, crtc_offset_cntl, crtc_tile_x0_y0 = 0; + uint32_t crtc_pitch; + uint32_t disp_merge_cntl; + + DRM_DEBUG("\n"); + + radeon_fb = to_radeon_framebuffer(crtc->fb); + + obj = radeon_fb->obj; + obj_priv = obj->driver_private; + + crtc_offset = obj_priv->bo->offset; + + crtc_offset_cntl = 0; + + /* TODO tiling */ + if (0) { + if (radeon_is_r300(dev_priv)) + crtc_offset_cntl |= (R300_CRTC_X_Y_MODE_EN | + R300_CRTC_MICRO_TILE_BUFFER_DIS | + R300_CRTC_MACRO_TILE_EN); + else + crtc_offset_cntl |= RADEON_CRTC_TILE_EN; + } else { + if (radeon_is_r300(dev_priv)) + crtc_offset_cntl &= ~(R300_CRTC_X_Y_MODE_EN | + R300_CRTC_MICRO_TILE_BUFFER_DIS | + R300_CRTC_MACRO_TILE_EN); + else + crtc_offset_cntl &= ~RADEON_CRTC_TILE_EN; + } + + base = obj_priv->bo->offset; + + /* TODO more tiling */ + if (0) { + if (radeon_is_r300(dev_priv)) { + crtc_tile_x0_y0 = x | (y << 16); + base &= ~0x7ff; + } else { + int byteshift = crtc->fb->bits_per_pixel >> 4; + int tile_addr = (((y >> 3) * crtc->fb->width + x) >> (8 - byteshift)) << 11; + base += tile_addr + ((x << byteshift) % 256) + ((y % 8) << 8); + crtc_offset_cntl |= (y % 16); + } + } else { + int offset = y * crtc->fb->pitch + x; + switch (crtc->fb->bits_per_pixel) { + case 15: + case 16: + offset *= 2; + break; + case 24: + offset *= 3; + break; + case 32: + offset *= 4; + break; + default: + return false; + } + base += offset; + } + + base &= ~7; + + /* update sarea TODO */ + + crtc_offset = base; + + crtc_pitch = ((((crtc->fb->pitch / (crtc->fb->bits_per_pixel / 8)) * crtc->fb->bits_per_pixel) + + ((crtc->fb->bits_per_pixel * 8) - 1)) / + (crtc->fb->bits_per_pixel * 8)); + crtc_pitch |= crtc_pitch << 16; + + DRM_DEBUG("mc_fb_location: 0x%x\n", dev_priv->fb_location); + + RADEON_WRITE(RADEON_DISPLAY_BASE_ADDR, dev_priv->fb_location); + + if (radeon_is_r300(dev_priv)) + RADEON_WRITE(R300_CRTC_TILE_X0_Y0, crtc_tile_x0_y0); + RADEON_WRITE(RADEON_CRTC_OFFSET_CNTL, crtc_offset_cntl); + RADEON_WRITE(RADEON_CRTC_OFFSET, crtc_offset); + RADEON_WRITE(RADEON_CRTC_PITCH, crtc_pitch); + + disp_merge_cntl = RADEON_READ(RADEON_DISP_MERGE_CNTL); + disp_merge_cntl &= ~RADEON_DISP_RGB_OFFSET_EN; + RADEON_WRITE(RADEON_DISP_MERGE_CNTL, disp_merge_cntl); + + return true; +} + +static bool radeon_set_crtc1_timing(struct drm_crtc *crtc, struct drm_display_mode *mode) +{ + struct drm_device *dev = crtc->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + int format; + int hsync_start; + int hsync_wid; + int vsync_wid; + 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; + + DRM_DEBUG("\n"); + + switch (crtc->fb->bits_per_pixel) { + case 15: /* 555 */ + format = 3; + break; + case 16: /* 565 */ + format = 4; + break; + case 24: /* RGB */ + format = 5; + break; + case 32: /* xRGB */ + format = 6; + break; + default: + return false; + } + + crtc_gen_cntl = (RADEON_CRTC_EXT_DISP_EN + | RADEON_CRTC_EN + | (format << 8) + | ((mode->flags & DRM_MODE_FLAG_DBLSCAN) + ? RADEON_CRTC_DBL_SCAN_EN + : 0) + | ((mode->flags & DRM_MODE_FLAG_CSYNC) + ? RADEON_CRTC_CSYNC_EN + : 0) + | ((mode->flags & DRM_MODE_FLAG_INTERLACE) + ? RADEON_CRTC_INTERLACE_EN + : 0)); + + crtc_ext_cntl = RADEON_READ(RADEON_CRTC_EXT_CNTL); + crtc_ext_cntl |= (RADEON_XCRT_CNT_EN | + RADEON_CRTC_VSYNC_DIS | + RADEON_CRTC_HSYNC_DIS | + RADEON_CRTC_DISPLAY_DIS); + + crtc_h_total_disp = ((((mode->crtc_htotal / 8) - 1) & 0x3ff) + | ((((mode->crtc_hdisplay / 8) - 1) & 0x1ff) << 16)); + + hsync_wid = (mode->crtc_hsync_end - mode->crtc_hsync_start) / 8; + if (!hsync_wid) + hsync_wid = 1; + hsync_start = mode->crtc_hsync_start - 8; + + crtc_h_sync_strt_wid = ((hsync_start & 0x1fff) + | ((hsync_wid & 0x3f) << 16) + | ((mode->flags & DRM_MODE_FLAG_NHSYNC) + ? RADEON_CRTC_H_SYNC_POL + : 0)); + + /* This works for double scan mode. */ + crtc_v_total_disp = (((mode->crtc_vtotal - 1) & 0xffff) + | ((mode->crtc_vdisplay - 1) << 16)); + + vsync_wid = mode->crtc_vsync_end - mode->crtc_vsync_start; + if (!vsync_wid) + vsync_wid = 1; + + crtc_v_sync_strt_wid = (((mode->crtc_vsync_start - 1) & 0xfff) + | ((vsync_wid & 0x1f) << 16) + | ((mode->flags & DRM_MODE_FLAG_NVSYNC) + ? RADEON_CRTC_V_SYNC_POL + : 0)); + + /* TODO -> Dell Server */ + if (0) { + uint32_t disp_hw_debug = RADEON_READ(RADEON_DISP_HW_DEBUG); + uint32_t tv_dac_cntl = RADEON_READ(RADEON_TV_DAC_CNTL); + uint32_t dac2_cntl = RADEON_READ(RADEON_DAC_CNTL2); + uint32_t crtc2_gen_cntl = RADEON_READ(RADEON_CRTC2_GEN_CNTL); + + dac2_cntl &= ~RADEON_DAC2_DAC_CLK_SEL; + dac2_cntl |= RADEON_DAC2_DAC2_CLK_SEL; + + /* For CRT on DAC2, don't turn it on if BIOS didn't + enable it, even it's detected. + */ + disp_hw_debug |= RADEON_CRT2_DISP1_SEL; + tv_dac_cntl &= ~((1<<2) | (3<<8) | (7<<24) | (0xff<<16)); + tv_dac_cntl |= (0x03 | (2<<8) | (0x58<<16)); + + RADEON_WRITE(RADEON_TV_DAC_CNTL, tv_dac_cntl); + RADEON_WRITE(RADEON_DISP_HW_DEBUG, disp_hw_debug); + RADEON_WRITE(RADEON_DAC_CNTL2, dac2_cntl); + RADEON_WRITE(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl); + } + + RADEON_WRITE(RADEON_CRTC_GEN_CNTL, crtc_gen_cntl | + RADEON_CRTC_DISP_REQ_EN_B); + + RADEON_WRITE_P(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl, + RADEON_CRTC_VSYNC_DIS | RADEON_CRTC_HSYNC_DIS | RADEON_CRTC_DISPLAY_DIS); + + RADEON_WRITE(RADEON_CRTC_H_TOTAL_DISP, crtc_h_total_disp); + RADEON_WRITE(RADEON_CRTC_H_SYNC_STRT_WID, crtc_h_sync_strt_wid); + RADEON_WRITE(RADEON_CRTC_V_TOTAL_DISP, crtc_v_total_disp); + RADEON_WRITE(RADEON_CRTC_V_SYNC_STRT_WID, crtc_v_sync_strt_wid); + + RADEON_WRITE(RADEON_CRTC_GEN_CNTL, crtc_gen_cntl); + + return true; +} + +static void radeon_set_pll1(struct drm_crtc *crtc, struct drm_display_mode *mode) +{ + struct drm_device *dev = crtc->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct drm_encoder *encoder; + uint32_t feedback_div = 0; + uint32_t reference_div = 0; + uint32_t post_divider = 0; + uint32_t freq = 0; + uint8_t pll_gain; + int pll_flags = RADEON_PLL_LEGACY; + bool use_bios_divs = false; + /* PLL registers */ + uint32_t ppll_ref_div = 0; + uint32_t ppll_div_3 = 0; + uint32_t htotal_cntl = 0; + uint32_t vclk_ecp_cntl; + + struct radeon_pll *pll = &dev_priv->mode_info.p1pll; + + struct { + int divider; + int bitvalue; + } *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 } + }; + + if (mode->clock > 200000) /* range limits??? */ + pll_flags |= RADEON_PLL_PREFER_HIGH_FB_DIV; + else + pll_flags |= RADEON_PLL_PREFER_LOW_REF_DIV; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + if (encoder->crtc == crtc) { + if (encoder->encoder_type != DRM_MODE_ENCODER_DAC) + pll_flags |= RADEON_PLL_NO_ODD_POST_DIV; + if (encoder->encoder_type == DRM_MODE_ENCODER_LVDS) { + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + + if (radeon_encoder->use_bios_dividers) { + ppll_ref_div = radeon_encoder->panel_ref_divider; + ppll_div_3 = (radeon_encoder->panel_fb_divider | + (radeon_encoder->panel_post_divider << 16)); + htotal_cntl = 0; + use_bios_divs = true; + } else + pll_flags |= RADEON_PLL_USE_REF_DIV; + } + } + } + + DRM_DEBUG("\n"); + + if (!use_bios_divs) { + radeon_compute_pll(pll, mode->clock, &freq, &feedback_div, &reference_div, &post_divider, pll_flags); + + for (post_div = &post_divs[0]; post_div->divider; ++post_div) { + if (post_div->divider == post_divider) + break; + } + + if (!post_div->divider) { + post_div = &post_divs[0]; + } + + DRM_DEBUG("dc=%u, fd=%d, rd=%d, pd=%d\n", + (unsigned)freq, + feedback_div, + reference_div, + post_divider); + + ppll_ref_div = reference_div; +#if defined(__powerpc__) && (0) /* TODO */ + /* apparently programming this otherwise causes a hang??? */ + if (info->MacModel == RADEON_MAC_IBOOK) + state->ppll_div_3 = 0x000600ad; + else +#endif + ppll_div_3 = (feedback_div | (post_div->bitvalue << 16)); + htotal_cntl = mode->htotal & 0x7; + + } + + vclk_ecp_cntl = (RADEON_READ_PLL(dev_priv, RADEON_VCLK_ECP_CNTL) & + ~RADEON_VCLK_SRC_SEL_MASK) | RADEON_VCLK_SRC_SEL_PPLLCLK; + + pll_gain = radeon_compute_pll_gain(dev_priv->mode_info.p1pll.reference_freq, + ppll_ref_div & RADEON_PPLL_REF_DIV_MASK, + ppll_div_3 & RADEON_PPLL_FB3_DIV_MASK); + + if (dev_priv->flags & RADEON_IS_MOBILITY) { + /* A temporal workaround for the occational blanking on certain laptop panels. + This appears to related to the PLL divider registers (fail to lock?). + It occurs even when all dividers are the same with their old settings. + In this case we really don't need to fiddle with PLL registers. + By doing this we can avoid the blanking problem with some panels. + */ + if ((ppll_ref_div == (RADEON_READ_PLL(dev_priv, RADEON_PPLL_REF_DIV) & RADEON_PPLL_REF_DIV_MASK)) && + (ppll_div_3 == (RADEON_READ_PLL(dev_priv, RADEON_PPLL_DIV_3) & + (RADEON_PPLL_POST3_DIV_MASK | RADEON_PPLL_FB3_DIV_MASK)))) { + RADEON_WRITE_P(RADEON_CLOCK_CNTL_INDEX, + RADEON_PLL_DIV_SEL, + ~(RADEON_PLL_DIV_SEL)); + radeon_pll_errata_after_index(dev_priv); + return; + } + } + + RADEON_WRITE_PLL_P(dev_priv, RADEON_VCLK_ECP_CNTL, + RADEON_VCLK_SRC_SEL_CPUCLK, + ~(RADEON_VCLK_SRC_SEL_MASK)); + RADEON_WRITE_PLL_P(dev_priv, + RADEON_PPLL_CNTL, + RADEON_PPLL_RESET + | RADEON_PPLL_ATOMIC_UPDATE_EN + | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN + | ((uint32_t)pll_gain << RADEON_PPLL_PVG_SHIFT), + ~(RADEON_PPLL_RESET + | RADEON_PPLL_ATOMIC_UPDATE_EN + | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN + | RADEON_PPLL_PVG_MASK)); + + RADEON_WRITE_P(RADEON_CLOCK_CNTL_INDEX, + RADEON_PLL_DIV_SEL, + ~(RADEON_PLL_DIV_SEL)); + radeon_pll_errata_after_index(dev_priv); + + if (radeon_is_r300(dev_priv) || + (dev_priv->chip_family == CHIP_RS300) || + (dev_priv->chip_family == CHIP_RS400) || + (dev_priv->chip_family == CHIP_RS480)) { + if (ppll_ref_div & R300_PPLL_REF_DIV_ACC_MASK) { + /* When restoring console mode, use saved PPLL_REF_DIV + * setting. + */ + RADEON_WRITE_PLL_P(dev_priv, RADEON_PPLL_REF_DIV, + ppll_ref_div, + 0); + } else { + /* R300 uses ref_div_acc field as real ref divider */ + RADEON_WRITE_PLL_P(dev_priv, RADEON_PPLL_REF_DIV, + (ppll_ref_div << R300_PPLL_REF_DIV_ACC_SHIFT), + ~R300_PPLL_REF_DIV_ACC_MASK); + } + } else { + RADEON_WRITE_PLL_P(dev_priv, RADEON_PPLL_REF_DIV, + ppll_ref_div, + ~RADEON_PPLL_REF_DIV_MASK); + } + + RADEON_WRITE_PLL_P(dev_priv, RADEON_PPLL_DIV_3, + ppll_div_3, + ~RADEON_PPLL_FB3_DIV_MASK); + + RADEON_WRITE_PLL_P(dev_priv, RADEON_PPLL_DIV_3, + ppll_div_3, + ~RADEON_PPLL_POST3_DIV_MASK); + + radeon_pll_write_update(dev); + radeon_pll_wait_for_read_update_complete(dev); + + RADEON_WRITE_PLL(dev_priv, RADEON_HTOTAL_CNTL, htotal_cntl); + + RADEON_WRITE_PLL_P(dev_priv, RADEON_PPLL_CNTL, + 0, + ~(RADEON_PPLL_RESET + | RADEON_PPLL_SLEEP + | RADEON_PPLL_ATOMIC_UPDATE_EN + | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN)); + + DRM_DEBUG("Wrote: 0x%08x 0x%08x 0x%08x (0x%08x)\n", + ppll_ref_div, + ppll_div_3, + (unsigned)htotal_cntl, + RADEON_READ_PLL(dev_priv, RADEON_PPLL_CNTL)); + DRM_DEBUG("Wrote: rd=%d, fd=%d, pd=%d\n", + ppll_ref_div & RADEON_PPLL_REF_DIV_MASK, + ppll_div_3 & RADEON_PPLL_FB3_DIV_MASK, + (ppll_div_3 & RADEON_PPLL_POST3_DIV_MASK) >> 16); + + mdelay(50); /* Let the clock to lock */ + + RADEON_WRITE_PLL_P(dev_priv, RADEON_VCLK_ECP_CNTL, + RADEON_VCLK_SRC_SEL_PPLLCLK, + ~(RADEON_VCLK_SRC_SEL_MASK)); + + /*RADEON_WRITE_PLL(dev_priv, RADEON_VCLK_ECP_CNTL, vclk_ecp_cntl);*/ + +} + +static bool radeon_set_crtc2_base(struct drm_crtc *crtc, int x, int y) +{ + struct drm_device *dev = crtc->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_framebuffer *radeon_fb; + struct drm_gem_object *obj; + struct drm_radeon_gem_object *obj_priv; + uint32_t base; + uint32_t crtc2_offset, crtc2_offset_cntl, crtc2_tile_x0_y0 = 0; + uint32_t crtc2_pitch; + uint32_t disp2_merge_cntl; + + DRM_DEBUG("\n"); + + radeon_fb = to_radeon_framebuffer(crtc->fb); + + obj = radeon_fb->obj; + obj_priv = obj->driver_private; + + crtc2_offset = obj_priv->bo->offset; + + crtc2_offset_cntl = 0; + + /* TODO tiling */ + if (0) { + if (radeon_is_r300(dev_priv)) + crtc2_offset_cntl |= (R300_CRTC_X_Y_MODE_EN | + R300_CRTC_MICRO_TILE_BUFFER_DIS | + R300_CRTC_MACRO_TILE_EN); + else + crtc2_offset_cntl |= RADEON_CRTC_TILE_EN; + } else { + if (radeon_is_r300(dev_priv)) + crtc2_offset_cntl &= ~(R300_CRTC_X_Y_MODE_EN | + R300_CRTC_MICRO_TILE_BUFFER_DIS | + R300_CRTC_MACRO_TILE_EN); + else + crtc2_offset_cntl &= ~RADEON_CRTC_TILE_EN; + } + + base = obj_priv->bo->offset; + + /* TODO more tiling */ + if (0) { + if (radeon_is_r300(dev_priv)) { + crtc2_tile_x0_y0 = x | (y << 16); + base &= ~0x7ff; + } else { + int byteshift = crtc->fb->bits_per_pixel >> 4; + int tile_addr = (((y >> 3) * crtc->fb->width + x) >> (8 - byteshift)) << 11; + base += tile_addr + ((x << byteshift) % 256) + ((y % 8) << 8); + crtc2_offset_cntl |= (y % 16); + } + } else { + int offset = y * crtc->fb->pitch + x; + switch (crtc->fb->bits_per_pixel) { + case 15: + case 16: + offset *= 2; + break; + case 24: + offset *= 3; + break; + case 32: + offset *= 4; + break; + default: + return false; + } + base += offset; + } + + base &= ~7; + + /* update sarea TODO */ + + crtc2_offset = base; + + crtc2_pitch = ((((crtc->fb->pitch / (crtc->fb->bits_per_pixel / 8)) * crtc->fb->bits_per_pixel) + + ((crtc->fb->bits_per_pixel * 8) - 1)) / + (crtc->fb->bits_per_pixel * 8)); + crtc2_pitch |= crtc2_pitch << 16; + + RADEON_WRITE(RADEON_DISPLAY2_BASE_ADDR, dev_priv->fb_location); + + if (radeon_is_r300(dev_priv)) + RADEON_WRITE(R300_CRTC2_TILE_X0_Y0, crtc2_tile_x0_y0); + RADEON_WRITE(RADEON_CRTC2_OFFSET_CNTL, crtc2_offset_cntl); + RADEON_WRITE(RADEON_CRTC2_OFFSET, crtc2_offset); + RADEON_WRITE(RADEON_CRTC2_PITCH, crtc2_pitch); + + disp2_merge_cntl = RADEON_READ(RADEON_DISP2_MERGE_CNTL); + disp2_merge_cntl &= ~RADEON_DISP2_RGB_OFFSET_EN; + RADEON_WRITE(RADEON_DISP2_MERGE_CNTL, disp2_merge_cntl); + + return true; +} + +static bool radeon_set_crtc2_timing(struct drm_crtc *crtc, struct drm_display_mode *mode) +{ + struct drm_device *dev = crtc->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + int format; + int hsync_start; + int hsync_wid; + int vsync_wid; + uint32_t crtc2_gen_cntl; + uint32_t crtc2_h_total_disp; + uint32_t crtc2_h_sync_strt_wid; + uint32_t crtc2_v_total_disp; + uint32_t crtc2_v_sync_strt_wid; + uint32_t fp_h2_sync_strt_wid; + uint32_t fp_v2_sync_strt_wid; + + DRM_DEBUG("\n"); + + switch (crtc->fb->bits_per_pixel) { + + case 15: /* 555 */ + format = 3; + break; + case 16: /* 565 */ + format = 4; + break; + case 24: /* RGB */ + format = 5; + break; + case 32: /* xRGB */ + format = 6; + break; + default: + return false; + } + + crtc2_h_total_disp = + ((((mode->crtc_htotal / 8) - 1) & 0x3ff) + | ((((mode->crtc_hdisplay / 8) - 1) & 0x1ff) << 16)); + + hsync_wid = (mode->crtc_hsync_end - mode->crtc_hsync_start) / 8; + if (!hsync_wid) + hsync_wid = 1; + hsync_start = mode->crtc_hsync_start - 8; + + crtc2_h_sync_strt_wid = ((hsync_start & 0x1fff) + | ((hsync_wid & 0x3f) << 16) + | ((mode->flags & DRM_MODE_FLAG_NHSYNC) + ? RADEON_CRTC_H_SYNC_POL + : 0)); + + /* This works for double scan mode. */ + crtc2_v_total_disp = (((mode->crtc_vtotal - 1) & 0xffff) + | ((mode->crtc_vdisplay - 1) << 16)); + + vsync_wid = mode->crtc_vsync_end - mode->crtc_vsync_start; + if (!vsync_wid) + vsync_wid = 1; + + crtc2_v_sync_strt_wid = (((mode->crtc_vsync_start - 1) & 0xfff) + | ((vsync_wid & 0x1f) << 16) + | ((mode->flags & DRM_MODE_FLAG_NVSYNC) + ? RADEON_CRTC2_V_SYNC_POL + : 0)); + + /* check to see if TV DAC is enabled for another crtc and keep it enabled */ + if (RADEON_READ(RADEON_CRTC2_GEN_CNTL) & RADEON_CRTC2_CRT2_ON) + crtc2_gen_cntl = RADEON_CRTC2_CRT2_ON; + else + crtc2_gen_cntl = 0; + + crtc2_gen_cntl |= (RADEON_CRTC2_EN + | (format << 8) + | RADEON_CRTC2_VSYNC_DIS + | RADEON_CRTC2_HSYNC_DIS + | RADEON_CRTC2_DISP_DIS + | ((mode->flags & DRM_MODE_FLAG_DBLSCAN) + ? RADEON_CRTC2_DBL_SCAN_EN + : 0) + | ((mode->flags & DRM_MODE_FLAG_CSYNC) + ? RADEON_CRTC2_CSYNC_EN + : 0) + | ((mode->flags & DRM_MODE_FLAG_INTERLACE) + ? RADEON_CRTC2_INTERLACE_EN + : 0)); + + fp_h2_sync_strt_wid = crtc2_h_sync_strt_wid; + fp_v2_sync_strt_wid = crtc2_v_sync_strt_wid; + + RADEON_WRITE(RADEON_CRTC2_GEN_CNTL, + crtc2_gen_cntl | RADEON_CRTC2_VSYNC_DIS | + RADEON_CRTC2_HSYNC_DIS | RADEON_CRTC2_DISP_DIS | + RADEON_CRTC2_DISP_REQ_EN_B); + + RADEON_WRITE(RADEON_CRTC2_H_TOTAL_DISP, crtc2_h_total_disp); + RADEON_WRITE(RADEON_CRTC2_H_SYNC_STRT_WID, crtc2_h_sync_strt_wid); + RADEON_WRITE(RADEON_CRTC2_V_TOTAL_DISP, crtc2_v_total_disp); + RADEON_WRITE(RADEON_CRTC2_V_SYNC_STRT_WID, crtc2_v_sync_strt_wid); + + RADEON_WRITE(RADEON_FP_H2_SYNC_STRT_WID, fp_h2_sync_strt_wid); + RADEON_WRITE(RADEON_FP_V2_SYNC_STRT_WID, fp_v2_sync_strt_wid); + + RADEON_WRITE(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl); + + return true; + +} + +static void radeon_set_pll2(struct drm_crtc *crtc, struct drm_display_mode *mode) +{ + struct drm_device *dev = crtc->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct drm_encoder *encoder; + uint32_t feedback_div = 0; + uint32_t reference_div = 0; + uint32_t post_divider = 0; + uint32_t freq = 0; + uint8_t pll_gain; + int pll_flags = RADEON_PLL_LEGACY; + bool use_bios_divs = false; + /* PLL2 registers */ + uint32_t p2pll_ref_div = 0; + uint32_t p2pll_div_0 = 0; + uint32_t htotal_cntl2 = 0; + uint32_t pixclks_cntl; + + struct radeon_pll *pll = &dev_priv->mode_info.p2pll; + + struct { + int divider; + int bitvalue; + } *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 */ + { 6, 6 }, /* VCLK_SRC/6 */ + { 12, 7 }, /* VCLK_SRC/12 */ + { 0, 0 } + }; + + if (mode->clock > 200000) /* range limits??? */ + pll_flags |= RADEON_PLL_PREFER_HIGH_FB_DIV; + else + pll_flags |= RADEON_PLL_PREFER_LOW_REF_DIV; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + if (encoder->crtc == crtc) { + if (encoder->encoder_type != DRM_MODE_ENCODER_DAC) + pll_flags |= RADEON_PLL_NO_ODD_POST_DIV; + if (encoder->encoder_type == DRM_MODE_ENCODER_LVDS) { + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + + if (radeon_encoder->use_bios_dividers) { + p2pll_ref_div = radeon_encoder->panel_ref_divider; + p2pll_div_0 = (radeon_encoder->panel_fb_divider | + (radeon_encoder->panel_post_divider << 16)); + htotal_cntl2 = 0; + use_bios_divs = true; + } else + pll_flags |= RADEON_PLL_USE_REF_DIV; + } + } + } + + DRM_DEBUG("\n"); + + if (!use_bios_divs) { + radeon_compute_pll(pll, mode->clock, &freq, &feedback_div, &reference_div, &post_divider, pll_flags); + + for (post_div = &post_divs[0]; post_div->divider; ++post_div) { + if (post_div->divider == post_divider) + break; + } + + if (!post_div->divider) { + post_div = &post_divs[0]; + } + + DRM_DEBUG("dc=%u, fd=%d, rd=%d, pd=%d\n", + (unsigned)freq, + feedback_div, + reference_div, + post_divider); + + p2pll_ref_div = reference_div; + p2pll_div_0 = (feedback_div | (post_div->bitvalue << 16)); + htotal_cntl2 = mode->htotal & 0x7; + + } + + pixclks_cntl = ((RADEON_READ_PLL(dev_priv, RADEON_PIXCLKS_CNTL) & + ~(RADEON_PIX2CLK_SRC_SEL_MASK)) | + RADEON_PIX2CLK_SRC_SEL_P2PLLCLK); + + pll_gain = radeon_compute_pll_gain(dev_priv->mode_info.p2pll.reference_freq, + p2pll_ref_div & RADEON_P2PLL_REF_DIV_MASK, + p2pll_div_0 & RADEON_P2PLL_FB0_DIV_MASK); + + + RADEON_WRITE_PLL_P(dev_priv, RADEON_PIXCLKS_CNTL, + RADEON_PIX2CLK_SRC_SEL_CPUCLK, + ~(RADEON_PIX2CLK_SRC_SEL_MASK)); + + RADEON_WRITE_PLL_P(dev_priv, + RADEON_P2PLL_CNTL, + RADEON_P2PLL_RESET + | RADEON_P2PLL_ATOMIC_UPDATE_EN + | ((uint32_t)pll_gain << RADEON_P2PLL_PVG_SHIFT), + ~(RADEON_P2PLL_RESET + | RADEON_P2PLL_ATOMIC_UPDATE_EN + | RADEON_P2PLL_PVG_MASK)); + + + RADEON_WRITE_PLL_P(dev_priv, RADEON_P2PLL_REF_DIV, + p2pll_ref_div, + ~RADEON_P2PLL_REF_DIV_MASK); + + RADEON_WRITE_PLL_P(dev_priv, RADEON_P2PLL_DIV_0, + p2pll_div_0, + ~RADEON_P2PLL_FB0_DIV_MASK); + + RADEON_WRITE_PLL_P(dev_priv, RADEON_P2PLL_DIV_0, + p2pll_div_0, + ~RADEON_P2PLL_POST0_DIV_MASK); + + radeon_pll2_write_update(dev); + radeon_pll2_wait_for_read_update_complete(dev); + + RADEON_WRITE_PLL(dev_priv, RADEON_HTOTAL2_CNTL, htotal_cntl2); + + RADEON_WRITE_PLL_P(dev_priv, RADEON_P2PLL_CNTL, + 0, + ~(RADEON_P2PLL_RESET + | RADEON_P2PLL_SLEEP + | RADEON_P2PLL_ATOMIC_UPDATE_EN)); + + DRM_DEBUG("Wrote2: 0x%08x 0x%08x 0x%08x (0x%08x)\n", + (unsigned)p2pll_ref_div, + (unsigned)p2pll_div_0, + (unsigned)htotal_cntl2, + RADEON_READ_PLL(dev_priv, RADEON_P2PLL_CNTL)); + DRM_DEBUG("Wrote2: rd=%u, fd=%u, pd=%u\n", + (unsigned)p2pll_ref_div & RADEON_P2PLL_REF_DIV_MASK, + (unsigned)p2pll_div_0 & RADEON_P2PLL_FB0_DIV_MASK, + (unsigned)((p2pll_div_0 & + RADEON_P2PLL_POST0_DIV_MASK) >>16)); + + mdelay(50); /* Let the clock to lock */ + + RADEON_WRITE_PLL_P(dev_priv, RADEON_PIXCLKS_CNTL, + RADEON_PIX2CLK_SRC_SEL_P2PLLCLK, + ~(RADEON_PIX2CLK_SRC_SEL_MASK)); + + RADEON_WRITE_PLL(dev_priv, RADEON_PIXCLKS_CNTL, pixclks_cntl); + +} + +static bool radeon_crtc_mode_fixup(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +void radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + + switch(radeon_crtc->crtc_id) { + case 0: + radeon_set_crtc1_base(crtc, x, y); + break; + case 1: + radeon_set_crtc2_base(crtc, x, y); + break; + + } +} + +static void radeon_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + + DRM_DEBUG("\n"); + + /* TODO TV */ + + radeon_crtc_set_base(crtc, x, y); + + switch(radeon_crtc->crtc_id) { + case 0: + radeon_set_crtc1_timing(crtc, adjusted_mode); + radeon_set_pll1(crtc, adjusted_mode); + break; + case 1: + radeon_set_crtc2_timing(crtc, adjusted_mode); + radeon_set_pll2(crtc, adjusted_mode); + break; + + } +} + +static void radeon_crtc_prepare(struct drm_crtc *crtc) +{ + radeon_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); +} + +static void radeon_crtc_commit(struct drm_crtc *crtc) +{ + radeon_crtc_dpms(crtc, DRM_MODE_DPMS_ON); +} + +static const struct drm_crtc_helper_funcs legacy_helper_funcs = { + .dpms = radeon_crtc_dpms, + .mode_fixup = radeon_crtc_mode_fixup, + .mode_set = radeon_crtc_mode_set, + .mode_set_base = radeon_crtc_set_base, + .prepare = radeon_crtc_prepare, + .commit = radeon_crtc_commit, +}; + + +void radeon_legacy_init_crtc(struct drm_device *dev, + struct radeon_crtc *radeon_crtc) +{ + drm_crtc_helper_add(&radeon_crtc->base, &legacy_helper_funcs); +} diff --git a/linux-core/radeon_legacy_encoders.c b/linux-core/radeon_legacy_encoders.c new file mode 100644 index 00000000..3df89d30 --- /dev/null +++ b/linux-core/radeon_legacy_encoders.c @@ -0,0 +1,1371 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat 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. + * + * Authors: Dave Airlie + * Alex Deucher + */ +#include "drmP.h" +#include "drm_crtc_helper.h" +#include "radeon_drm.h" +#include "radeon_drv.h" + + +static void radeon_legacy_rmx_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + int xres = mode->hdisplay; + int yres = mode->vdisplay; + bool hscale = true, vscale = true; + int hsync_wid; + int vsync_wid; + int hsync_start; + uint32_t scale, inc; + uint32_t fp_horz_stretch, fp_vert_stretch, crtc_more_cntl, fp_horz_vert_active; + uint32_t fp_h_sync_strt_wid, fp_v_sync_strt_wid, fp_crtc_h_total_disp, fp_crtc_v_total_disp; + + DRM_DEBUG("\n"); + + fp_vert_stretch = RADEON_READ(RADEON_FP_VERT_STRETCH) & + (RADEON_VERT_STRETCH_RESERVED | + RADEON_VERT_AUTO_RATIO_INC); + fp_horz_stretch = RADEON_READ(RADEON_FP_HORZ_STRETCH) & + (RADEON_HORZ_FP_LOOP_STRETCH | + RADEON_HORZ_AUTO_RATIO_INC); + + crtc_more_cntl = 0; + if ((dev_priv->chip_family == CHIP_RS100) || + (dev_priv->chip_family == CHIP_RS200)) { + /* This is to workaround the asic bug for RMX, some versions + of BIOS dosen't have this register initialized correctly. */ + crtc_more_cntl |= RADEON_CRTC_H_CUTOFF_ACTIVE_EN; + } + + + fp_crtc_h_total_disp = ((((mode->crtc_htotal / 8) - 1) & 0x3ff) + | ((((mode->crtc_hdisplay / 8) - 1) & 0x1ff) << 16)); + + hsync_wid = (mode->crtc_hsync_end - mode->crtc_hsync_start) / 8; + if (!hsync_wid) + hsync_wid = 1; + hsync_start = mode->crtc_hsync_start - 8; + + fp_h_sync_strt_wid = ((hsync_start & 0x1fff) + | ((hsync_wid & 0x3f) << 16) + | ((mode->flags & DRM_MODE_FLAG_NHSYNC) + ? RADEON_CRTC_H_SYNC_POL + : 0)); + + fp_crtc_v_total_disp = (((mode->crtc_vtotal - 1) & 0xffff) + | ((mode->crtc_vdisplay - 1) << 16)); + + vsync_wid = mode->crtc_vsync_end - mode->crtc_vsync_start; + if (!vsync_wid) + vsync_wid = 1; + + fp_v_sync_strt_wid = (((mode->crtc_vsync_start - 1) & 0xfff) + | ((vsync_wid & 0x1f) << 16) + | ((mode->flags & DRM_MODE_FLAG_NVSYNC) + ? RADEON_CRTC_V_SYNC_POL + : 0)); + + fp_horz_vert_active = 0; + + if (radeon_encoder->panel_xres == 0 || + radeon_encoder->panel_yres == 0) { + hscale = false; + vscale = false; + } else { + if (xres > radeon_encoder->panel_xres) + xres = radeon_encoder->panel_xres; + if (yres > radeon_encoder->panel_yres) + yres = radeon_encoder->panel_yres; + + if (xres == radeon_encoder->panel_xres) + hscale = false; + if (yres == radeon_encoder->panel_yres) + vscale = false; + } + + if (radeon_encoder->flags & RADEON_USE_RMX) { + if (radeon_encoder->rmx_type != RMX_CENTER) { + if (!hscale) + fp_horz_stretch |= ((xres/8-1) << 16); + else { + inc = (fp_horz_stretch & RADEON_HORZ_AUTO_RATIO_INC) ? 1 : 0; + scale = ((xres + inc) * RADEON_HORZ_STRETCH_RATIO_MAX) + / radeon_encoder->panel_xres + 1; + fp_horz_stretch |= (((scale) & RADEON_HORZ_STRETCH_RATIO_MASK) | + RADEON_HORZ_STRETCH_BLEND | + RADEON_HORZ_STRETCH_ENABLE | + ((radeon_encoder->panel_xres/8-1) << 16)); + } + + if (!vscale) + fp_vert_stretch |= ((yres-1) << 12); + else { + inc = (fp_vert_stretch & RADEON_VERT_AUTO_RATIO_INC) ? 1 : 0; + scale = ((yres + inc) * RADEON_VERT_STRETCH_RATIO_MAX) + / radeon_encoder->panel_yres + 1; + fp_vert_stretch |= (((scale) & RADEON_VERT_STRETCH_RATIO_MASK) | + RADEON_VERT_STRETCH_ENABLE | + RADEON_VERT_STRETCH_BLEND | + ((radeon_encoder->panel_yres-1) << 12)); + } + } else if (radeon_encoder->rmx_type == RMX_CENTER) { + int blank_width; + + fp_horz_stretch |= ((xres/8-1) << 16); + fp_vert_stretch |= ((yres-1) << 12); + + crtc_more_cntl |= (RADEON_CRTC_AUTO_HORZ_CENTER_EN | + RADEON_CRTC_AUTO_VERT_CENTER_EN); + + blank_width = (mode->crtc_hblank_end - mode->crtc_hblank_start) / 8; + if (blank_width > 110) + blank_width = 110; + + fp_crtc_h_total_disp = (((blank_width) & 0x3ff) + | ((((mode->crtc_hdisplay / 8) - 1) & 0x1ff) << 16)); + + hsync_wid = (mode->crtc_hsync_end - mode->crtc_hsync_start) / 8; + if (!hsync_wid) + hsync_wid = 1; + + fp_h_sync_strt_wid = ((((mode->crtc_hsync_start - mode->crtc_hblank_start) / 8) & 0x1fff) + | ((hsync_wid & 0x3f) << 16) + | ((mode->flags & DRM_MODE_FLAG_NHSYNC) + ? RADEON_CRTC_H_SYNC_POL + : 0)); + + fp_crtc_v_total_disp = (((mode->crtc_vblank_end - mode->crtc_vblank_start) & 0xffff) + | ((mode->crtc_vdisplay - 1) << 16)); + + vsync_wid = mode->crtc_vsync_end - mode->crtc_vsync_start; + if (!vsync_wid) + vsync_wid = 1; + + fp_v_sync_strt_wid = ((((mode->crtc_vsync_start - mode->crtc_vblank_start) & 0xfff) + | ((vsync_wid & 0x1f) << 16) + | ((mode->flags & DRM_MODE_FLAG_NVSYNC) + ? RADEON_CRTC_V_SYNC_POL + : 0))); + + fp_horz_vert_active = (((radeon_encoder->panel_yres) & 0xfff) | + (((radeon_encoder->panel_xres / 8) & 0x1ff) << 16)); + } + } else { + fp_horz_stretch |= ((xres/8-1) << 16); + fp_vert_stretch |= ((yres-1) << 12); + } + + RADEON_WRITE(RADEON_FP_HORZ_STRETCH, fp_horz_stretch); + RADEON_WRITE(RADEON_FP_VERT_STRETCH, fp_vert_stretch); + RADEON_WRITE(RADEON_CRTC_MORE_CNTL, crtc_more_cntl); + RADEON_WRITE(RADEON_FP_HORZ_VERT_ACTIVE, fp_horz_vert_active); + RADEON_WRITE(RADEON_FP_H_SYNC_STRT_WID, fp_h_sync_strt_wid); + RADEON_WRITE(RADEON_FP_V_SYNC_STRT_WID, fp_v_sync_strt_wid); + RADEON_WRITE(RADEON_FP_CRTC_H_TOTAL_DISP, fp_crtc_h_total_disp); + RADEON_WRITE(RADEON_FP_CRTC_V_TOTAL_DISP, fp_crtc_v_total_disp); + +} + +static void radeon_legacy_lvds_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_crtc *radeon_crtc; + uint32_t lvds_gen_cntl, lvds_pll_cntl, pixclks_cntl, disp_pwr_man; + uint32_t bios_5_scratch, bios_6_scratch; + int crtc_id = 0; + DRM_DEBUG("\n"); + + if (encoder->crtc) { + radeon_crtc = to_radeon_crtc(encoder->crtc); + crtc_id = radeon_crtc->crtc_id; + } + + // FIXME atom/legacy cards like r4xx + bios_5_scratch = RADEON_READ(RADEON_BIOS_5_SCRATCH); + bios_6_scratch = RADEON_READ(RADEON_BIOS_6_SCRATCH); + + bios_5_scratch &= ~RADEON_LCD1_CRTC_MASK; + bios_5_scratch |= (crtc_id << RADEON_LCD1_CRTC_SHIFT); + + switch (mode) { + case DRM_MODE_DPMS_ON: + disp_pwr_man = RADEON_READ(RADEON_DISP_PWR_MAN); + disp_pwr_man |= RADEON_AUTO_PWRUP_EN; + RADEON_WRITE(RADEON_DISP_PWR_MAN, disp_pwr_man); + lvds_pll_cntl = RADEON_READ(RADEON_LVDS_PLL_CNTL); + lvds_pll_cntl |= RADEON_LVDS_PLL_EN; + RADEON_WRITE(RADEON_LVDS_PLL_CNTL, lvds_pll_cntl); + udelay(1000); + + lvds_pll_cntl = RADEON_READ(RADEON_LVDS_PLL_CNTL); + lvds_pll_cntl &= ~RADEON_LVDS_PLL_RESET; + RADEON_WRITE(RADEON_LVDS_PLL_CNTL, lvds_pll_cntl); + + lvds_gen_cntl = RADEON_READ(RADEON_LVDS_GEN_CNTL); + lvds_gen_cntl |= (RADEON_LVDS_ON | RADEON_LVDS_EN | RADEON_LVDS_DIGON | RADEON_LVDS_BLON); + lvds_gen_cntl &= ~(RADEON_LVDS_DISPLAY_DIS); + udelay(radeon_encoder->panel_pwr_delay * 1000); + RADEON_WRITE(RADEON_LVDS_GEN_CNTL, lvds_gen_cntl); + + /* update bios scratch regs */ + bios_5_scratch |= RADEON_LCD1_ON; + bios_6_scratch |= RADEON_LCD_DPMS_ON; + + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + pixclks_cntl = RADEON_READ_PLL(dev_priv, RADEON_PIXCLKS_CNTL); + RADEON_WRITE_PLL_P(dev_priv, RADEON_PIXCLKS_CNTL, 0, ~RADEON_PIXCLK_LVDS_ALWAYS_ONb); + lvds_gen_cntl = RADEON_READ(RADEON_LVDS_GEN_CNTL); + lvds_gen_cntl |= RADEON_LVDS_DISPLAY_DIS; + lvds_gen_cntl &= ~(RADEON_LVDS_ON | RADEON_LVDS_BLON | RADEON_LVDS_EN | RADEON_LVDS_DIGON); + udelay(radeon_encoder->panel_pwr_delay * 1000); + RADEON_WRITE(RADEON_LVDS_GEN_CNTL, lvds_gen_cntl); + RADEON_WRITE_PLL(dev_priv, RADEON_PIXCLKS_CNTL, pixclks_cntl); + + bios_5_scratch &= ~RADEON_LCD1_ON; + bios_6_scratch &= ~RADEON_LCD_DPMS_ON; + break; + } + RADEON_WRITE(RADEON_BIOS_5_SCRATCH, bios_5_scratch); + RADEON_WRITE(RADEON_BIOS_6_SCRATCH, bios_6_scratch); +} + +static void radeon_legacy_lvds_prepare(struct drm_encoder *encoder) +{ + struct drm_radeon_private *dev_priv = encoder->dev->dev_private; + // fix me: atom/legacy r4xx + if (!dev_priv->is_atom_bios) + radeon_combios_output_lock(encoder, true); + radeon_legacy_lvds_dpms(encoder, DRM_MODE_DPMS_OFF); +} + +static void radeon_legacy_lvds_commit(struct drm_encoder *encoder) +{ + struct drm_radeon_private *dev_priv = encoder->dev->dev_private; + radeon_legacy_lvds_dpms(encoder, DRM_MODE_DPMS_ON); + // fix me: atom/legacy r4xx + if (!dev_priv->is_atom_bios) + radeon_combios_output_lock(encoder, false); +} + +static void radeon_legacy_lvds_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + uint32_t lvds_pll_cntl, lvds_gen_cntl, lvds_ss_gen_cntl; + + DRM_DEBUG("\n"); + + if (radeon_crtc->crtc_id == 0) + radeon_legacy_rmx_mode_set(encoder, mode, adjusted_mode); + + lvds_pll_cntl = RADEON_READ(RADEON_LVDS_PLL_CNTL); + lvds_pll_cntl &= ~RADEON_LVDS_PLL_EN; + if (radeon_encoder->lvds_gen_cntl) + lvds_gen_cntl = radeon_encoder->lvds_gen_cntl; + else + lvds_gen_cntl = RADEON_READ(RADEON_LVDS_GEN_CNTL); + lvds_gen_cntl |= RADEON_LVDS_DISPLAY_DIS; + lvds_gen_cntl &= ~(RADEON_LVDS_ON | + RADEON_LVDS_BLON | + RADEON_LVDS_EN | + RADEON_LVDS_RST_FM); + + DRM_INFO("bios LVDS_GEN_CNTL: 0x%x\n", radeon_encoder->lvds_gen_cntl); + + if (radeon_is_r300(dev_priv)) + lvds_pll_cntl &= ~(R300_LVDS_SRC_SEL_MASK); + + if (radeon_crtc->crtc_id == 0) { + if (radeon_is_r300(dev_priv)) { + if (radeon_encoder->flags & RADEON_USE_RMX) + lvds_pll_cntl |= R300_LVDS_SRC_SEL_RMX; + } else + lvds_gen_cntl &= ~RADEON_LVDS_SEL_CRTC2; + } else { + if (radeon_is_r300(dev_priv)) + lvds_pll_cntl |= R300_LVDS_SRC_SEL_CRTC2; + else + lvds_gen_cntl |= RADEON_LVDS_SEL_CRTC2; + } + + RADEON_WRITE(RADEON_LVDS_GEN_CNTL, lvds_gen_cntl); + RADEON_WRITE(RADEON_LVDS_PLL_CNTL, lvds_pll_cntl); + + lvds_ss_gen_cntl = RADEON_READ(RADEON_LVDS_SS_GEN_CNTL); + if (radeon_encoder->panel_digon_delay && + radeon_encoder->panel_blon_delay) { + lvds_ss_gen_cntl &= ~((0xf << RADEON_LVDS_PWRSEQ_DELAY1_SHIFT) | + (0xf << RADEON_LVDS_PWRSEQ_DELAY2_SHIFT)); + lvds_ss_gen_cntl |= ((radeon_encoder->panel_digon_delay << RADEON_LVDS_PWRSEQ_DELAY1_SHIFT) | + (radeon_encoder->panel_blon_delay << RADEON_LVDS_PWRSEQ_DELAY2_SHIFT)); + RADEON_WRITE(RADEON_LVDS_SS_GEN_CNTL, lvds_ss_gen_cntl); + } + + if (dev_priv->chip_family == CHIP_RV410) + RADEON_WRITE(RADEON_CLOCK_CNTL_INDEX, 0); +} + +static bool radeon_legacy_lvds_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + + radeon_encoder->flags &= ~RADEON_USE_RMX; + + if (radeon_encoder->rmx_type != RMX_OFF) + radeon_rmx_mode_fixup(encoder, mode, adjusted_mode); + + return true; +} + +static const struct drm_encoder_helper_funcs radeon_legacy_lvds_helper_funcs = { + .dpms = radeon_legacy_lvds_dpms, + .mode_fixup = radeon_legacy_lvds_mode_fixup, + .prepare = radeon_legacy_lvds_prepare, + .mode_set = radeon_legacy_lvds_mode_set, + .commit = radeon_legacy_lvds_commit, +}; + + +static const struct drm_encoder_funcs radeon_legacy_lvds_enc_funcs = { + .destroy = radeon_enc_destroy, +}; + + +struct drm_encoder *radeon_encoder_legacy_lvds_add(struct drm_device *dev, int bios_index) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_encoder *radeon_encoder; + struct drm_encoder *encoder; + + DRM_DEBUG("\n"); + + radeon_encoder = kzalloc(sizeof(struct radeon_encoder), GFP_KERNEL); + if (!radeon_encoder) { + return NULL; + } + + encoder = &radeon_encoder->base; + + /* Limit LVDS to crtc 0 for RMX */ + encoder->possible_crtcs = 0x1; + encoder->possible_clones = 0; + drm_encoder_init(dev, encoder, &radeon_legacy_lvds_enc_funcs, + DRM_MODE_ENCODER_LVDS); + + drm_encoder_helper_add(encoder, &radeon_legacy_lvds_helper_funcs); + + /* get the lvds info from the bios */ + if (dev_priv->is_atom_bios) + radeon_atombios_get_lvds_info(radeon_encoder); + else + radeon_combios_get_lvds_info(radeon_encoder); + + /* LVDS gets default RMX full scaling */ + radeon_encoder->rmx_type = RMX_FULL; + + return encoder; +} + +static bool radeon_legacy_primary_dac_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void radeon_legacy_primary_dac_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_crtc *radeon_crtc; + uint32_t crtc_ext_cntl = RADEON_READ(RADEON_CRTC_EXT_CNTL); + uint32_t dac_cntl = RADEON_READ(RADEON_DAC_CNTL); + uint32_t dac_macro_cntl = RADEON_READ(RADEON_DAC_MACRO_CNTL); + uint32_t bios_5_scratch, bios_6_scratch; + int crtc_id = 0; + + DRM_DEBUG("\n"); + + if (encoder->crtc) { + radeon_crtc = to_radeon_crtc(encoder->crtc); + crtc_id = radeon_crtc->crtc_id; + } + + // FIXME atom/legacy cards like r4xx + bios_5_scratch = RADEON_READ(RADEON_BIOS_5_SCRATCH); + bios_6_scratch = RADEON_READ(RADEON_BIOS_6_SCRATCH); + + bios_5_scratch &= ~RADEON_CRT1_CRTC_MASK; + bios_5_scratch |= (crtc_id << RADEON_CRT1_CRTC_SHIFT); + + DRM_DEBUG("\n"); + + switch(mode) { + case DRM_MODE_DPMS_ON: + crtc_ext_cntl |= RADEON_CRTC_CRT_ON; + dac_cntl &= ~RADEON_DAC_PDWN; + dac_macro_cntl &= ~(RADEON_DAC_PDWN_R | + RADEON_DAC_PDWN_G | + RADEON_DAC_PDWN_B); + bios_5_scratch |= RADEON_CRT1_ON; + bios_6_scratch |= RADEON_CRT_DPMS_ON; + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + crtc_ext_cntl &= ~RADEON_CRTC_CRT_ON; + dac_cntl |= RADEON_DAC_PDWN; + dac_macro_cntl |= (RADEON_DAC_PDWN_R | + RADEON_DAC_PDWN_G | + RADEON_DAC_PDWN_B); + bios_5_scratch &= ~RADEON_CRT1_ON; + bios_6_scratch &= ~RADEON_CRT_DPMS_ON; + break; + } + + RADEON_WRITE(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl); + RADEON_WRITE(RADEON_DAC_CNTL, dac_cntl); + RADEON_WRITE(RADEON_DAC_MACRO_CNTL, dac_macro_cntl); + + RADEON_WRITE(RADEON_BIOS_5_SCRATCH, bios_5_scratch); + RADEON_WRITE(RADEON_BIOS_6_SCRATCH, bios_6_scratch); +} + +static void radeon_legacy_primary_dac_prepare(struct drm_encoder *encoder) +{ + struct drm_radeon_private *dev_priv = encoder->dev->dev_private; + // fix me: atom/legacy r4xx + if (!dev_priv->is_atom_bios) + radeon_combios_output_lock(encoder, true); + radeon_legacy_primary_dac_dpms(encoder, DRM_MODE_DPMS_OFF); +} + +static void radeon_legacy_primary_dac_commit(struct drm_encoder *encoder) +{ + struct drm_radeon_private *dev_priv = encoder->dev->dev_private; + radeon_legacy_primary_dac_dpms(encoder, DRM_MODE_DPMS_ON); + // fix me: atom/legacy r4xx + if (!dev_priv->is_atom_bios) + radeon_combios_output_lock(encoder, false); +} + +static void radeon_legacy_primary_dac_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + uint32_t disp_output_cntl, dac_cntl, dac2_cntl, dac_macro_cntl; + + DRM_DEBUG("\n"); + + if (radeon_crtc->crtc_id == 0) + radeon_legacy_rmx_mode_set(encoder, mode, adjusted_mode); + + if (radeon_crtc->crtc_id == 0) { + if (dev_priv->chip_family == CHIP_R200 || radeon_is_r300(dev_priv)) { + disp_output_cntl = RADEON_READ(RADEON_DISP_OUTPUT_CNTL) & + ~(RADEON_DISP_DAC_SOURCE_MASK); + RADEON_WRITE(RADEON_DISP_OUTPUT_CNTL, disp_output_cntl); + } else { + dac2_cntl = RADEON_READ(RADEON_DAC_CNTL2) & ~(RADEON_DAC2_DAC_CLK_SEL); + RADEON_WRITE(RADEON_DAC_CNTL2, dac2_cntl); + } + } else { + if (dev_priv->chip_family == CHIP_R200 || radeon_is_r300(dev_priv)) { + disp_output_cntl = RADEON_READ(RADEON_DISP_OUTPUT_CNTL) & + ~(RADEON_DISP_DAC_SOURCE_MASK); + disp_output_cntl |= RADEON_DISP_DAC_SOURCE_CRTC2; + RADEON_WRITE(RADEON_DISP_OUTPUT_CNTL, disp_output_cntl); + } else { + dac2_cntl = RADEON_READ(RADEON_DAC_CNTL2) | RADEON_DAC2_DAC_CLK_SEL; + RADEON_WRITE(RADEON_DAC_CNTL2, dac2_cntl); + } + } + + dac_cntl = (RADEON_DAC_MASK_ALL | + RADEON_DAC_VGA_ADR_EN | + /* TODO 6-bits */ + RADEON_DAC_8BIT_EN); + + RADEON_WRITE_P(RADEON_DAC_CNTL, + dac_cntl, + RADEON_DAC_RANGE_CNTL | + RADEON_DAC_BLANKING); + + if (radeon_encoder->ps2_pdac_adj) + dac_macro_cntl = radeon_encoder->ps2_pdac_adj; + else + dac_macro_cntl = RADEON_READ(RADEON_DAC_MACRO_CNTL); + dac_macro_cntl |= RADEON_DAC_PDWN_R | RADEON_DAC_PDWN_G | RADEON_DAC_PDWN_B; + RADEON_WRITE(RADEON_DAC_MACRO_CNTL, dac_macro_cntl); +} + +static enum drm_connector_status radeon_legacy_primary_dac_detect(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t vclk_ecp_cntl, crtc_ext_cntl; + uint32_t dac_ext_cntl, dac_cntl, dac_macro_cntl, tmp; + enum drm_connector_status found = connector_status_disconnected; + bool color = true; + + /* save the regs we need */ + vclk_ecp_cntl = RADEON_READ_PLL(dev_priv, RADEON_VCLK_ECP_CNTL); + crtc_ext_cntl = RADEON_READ(RADEON_CRTC_EXT_CNTL); + dac_ext_cntl = RADEON_READ(RADEON_DAC_EXT_CNTL); + dac_cntl = RADEON_READ(RADEON_DAC_CNTL); + dac_macro_cntl = RADEON_READ(RADEON_DAC_MACRO_CNTL); + + tmp = vclk_ecp_cntl & + ~(RADEON_PIXCLK_ALWAYS_ONb | RADEON_PIXCLK_DAC_ALWAYS_ONb); + RADEON_WRITE_PLL(dev_priv, RADEON_VCLK_ECP_CNTL, tmp); + + tmp = crtc_ext_cntl | RADEON_CRTC_CRT_ON; + RADEON_WRITE(RADEON_CRTC_EXT_CNTL, tmp); + + tmp = RADEON_DAC_FORCE_BLANK_OFF_EN | + RADEON_DAC_FORCE_DATA_EN; + + if (color) + tmp |= RADEON_DAC_FORCE_DATA_SEL_RGB; + else + tmp |= RADEON_DAC_FORCE_DATA_SEL_G; + + if (radeon_is_r300(dev_priv)) + tmp |= (0x1b6 << RADEON_DAC_FORCE_DATA_SHIFT); + else + tmp |= (0x180 << RADEON_DAC_FORCE_DATA_SHIFT); + + RADEON_WRITE(RADEON_DAC_EXT_CNTL, tmp); + + tmp = dac_cntl & ~(RADEON_DAC_RANGE_CNTL_MASK | RADEON_DAC_PDWN); + tmp |= RADEON_DAC_RANGE_CNTL_PS2 | RADEON_DAC_CMP_EN; + RADEON_WRITE(RADEON_DAC_CNTL, tmp); + + tmp &= ~(RADEON_DAC_PDWN_R | + RADEON_DAC_PDWN_G | + RADEON_DAC_PDWN_B); + + RADEON_WRITE(RADEON_DAC_MACRO_CNTL, tmp); + + udelay(2000); + + if (RADEON_READ(RADEON_DAC_CNTL) & RADEON_DAC_CMP_OUTPUT) + found = connector_status_connected; + + /* restore the regs we used */ + RADEON_WRITE(RADEON_DAC_CNTL, dac_cntl); + RADEON_WRITE(RADEON_DAC_MACRO_CNTL, dac_macro_cntl); + RADEON_WRITE(RADEON_DAC_EXT_CNTL, dac_ext_cntl); + RADEON_WRITE(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl); + RADEON_WRITE_PLL(dev_priv, RADEON_VCLK_ECP_CNTL, vclk_ecp_cntl); + + return found; +} + +static const struct drm_encoder_helper_funcs radeon_legacy_primary_dac_helper_funcs = { + .dpms = radeon_legacy_primary_dac_dpms, + .mode_fixup = radeon_legacy_primary_dac_mode_fixup, + .prepare = radeon_legacy_primary_dac_prepare, + .mode_set = radeon_legacy_primary_dac_mode_set, + .commit = radeon_legacy_primary_dac_commit, + .detect = radeon_legacy_primary_dac_detect, +}; + + +static const struct drm_encoder_funcs radeon_legacy_primary_dac_enc_funcs = { + .destroy = radeon_enc_destroy, +}; + +struct drm_encoder *radeon_encoder_legacy_primary_dac_add(struct drm_device *dev, int bios_index, int has_tv) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_encoder *radeon_encoder; + struct drm_encoder *encoder; + + DRM_DEBUG("\n"); + + radeon_encoder = kzalloc(sizeof(struct radeon_encoder), GFP_KERNEL); + if (!radeon_encoder) { + return NULL; + } + + encoder = &radeon_encoder->base; + + encoder->possible_crtcs = 0x3; + encoder->possible_clones = 0; + drm_encoder_init(dev, encoder, &radeon_legacy_primary_dac_enc_funcs, + DRM_MODE_ENCODER_DAC); + + drm_encoder_helper_add(encoder, &radeon_legacy_primary_dac_helper_funcs); + + /* get the primary dac bg/adj vals from bios tables */ + if (!dev_priv->is_atom_bios) + radeon_combios_get_primary_dac_info(radeon_encoder); + + return encoder; +} + + +static bool radeon_legacy_tmds_int_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void radeon_legacy_tmds_int_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_crtc *radeon_crtc; + uint32_t fp_gen_cntl = RADEON_READ(RADEON_FP_GEN_CNTL); + uint32_t bios_5_scratch, bios_6_scratch; + int crtc_id = 0; + DRM_DEBUG("\n"); + + if (encoder->crtc) { + radeon_crtc = to_radeon_crtc(encoder->crtc); + crtc_id = radeon_crtc->crtc_id; + } + + // FIXME atom/legacy cards like r4xx + bios_5_scratch = RADEON_READ(RADEON_BIOS_5_SCRATCH); + bios_6_scratch = RADEON_READ(RADEON_BIOS_6_SCRATCH); + + bios_5_scratch &= ~RADEON_DFP1_CRTC_MASK; + bios_5_scratch |= (crtc_id << RADEON_DFP1_CRTC_SHIFT); + + switch(mode) { + case DRM_MODE_DPMS_ON: + fp_gen_cntl |= (RADEON_FP_FPON | RADEON_FP_TMDS_EN); + bios_5_scratch |= RADEON_DFP1_ON; + bios_6_scratch |= RADEON_DFP_DPMS_ON; + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + fp_gen_cntl &= ~(RADEON_FP_FPON | RADEON_FP_TMDS_EN); + bios_5_scratch &= ~RADEON_DFP1_ON; + bios_6_scratch &= ~RADEON_DFP_DPMS_ON; + break; + } + + RADEON_WRITE(RADEON_FP_GEN_CNTL, fp_gen_cntl); + + RADEON_WRITE(RADEON_BIOS_5_SCRATCH, bios_5_scratch); + RADEON_WRITE(RADEON_BIOS_6_SCRATCH, bios_6_scratch); +} + +static void radeon_legacy_tmds_int_prepare(struct drm_encoder *encoder) +{ + struct drm_radeon_private *dev_priv = encoder->dev->dev_private; + // fix me: atom/legacy r4xx + if (!dev_priv->is_atom_bios) + radeon_combios_output_lock(encoder, true); + radeon_legacy_tmds_int_dpms(encoder, DRM_MODE_DPMS_OFF); +} + +static void radeon_legacy_tmds_int_commit(struct drm_encoder *encoder) +{ + struct drm_radeon_private *dev_priv = encoder->dev->dev_private; + radeon_legacy_tmds_int_dpms(encoder, DRM_MODE_DPMS_ON); + // fix me: atom/legacy r4xx + if (!dev_priv->is_atom_bios) + radeon_combios_output_lock(encoder, true); +} + +static void radeon_legacy_tmds_int_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + uint32_t tmp, tmds_pll_cntl, tmds_transmitter_cntl, fp_gen_cntl; + int i; + + DRM_DEBUG("\n"); + + if (radeon_crtc->crtc_id == 0) + radeon_legacy_rmx_mode_set(encoder, mode, adjusted_mode); + + tmp = tmds_pll_cntl = RADEON_READ(RADEON_TMDS_PLL_CNTL); + tmp &= 0xfffff; + if (dev_priv->chip_family == CHIP_RV280) { + /* bit 22 of TMDS_PLL_CNTL is read-back inverted */ + tmp ^= (1 << 22); + tmds_pll_cntl ^= (1 << 22); + } + + for (i = 0; i < 4; i++) { + if (radeon_encoder->tmds_pll[i].freq == 0) + break; + if ((uint32_t)(mode->clock / 10) < radeon_encoder->tmds_pll[i].freq) { + tmp = radeon_encoder->tmds_pll[i].value ; + break; + } + } + + if (radeon_is_r300(dev_priv) || (dev_priv->chip_family == CHIP_RV280)) { + if (tmp & 0xfff00000) + tmds_pll_cntl = tmp; + else { + tmds_pll_cntl &= 0xfff00000; + tmds_pll_cntl |= tmp; + } + } else + tmds_pll_cntl = tmp; + + tmds_transmitter_cntl = RADEON_READ(RADEON_TMDS_TRANSMITTER_CNTL) & + ~(RADEON_TMDS_TRANSMITTER_PLLRST); + + if (dev_priv->chip_family == CHIP_R200 || + dev_priv->chip_family == CHIP_R100 || + radeon_is_r300(dev_priv)) + tmds_transmitter_cntl &= ~(RADEON_TMDS_TRANSMITTER_PLLEN); + else /* RV chips got this bit reversed */ + tmds_transmitter_cntl |= RADEON_TMDS_TRANSMITTER_PLLEN; + + fp_gen_cntl = (RADEON_READ(RADEON_FP_GEN_CNTL) | + (RADEON_FP_CRTC_DONT_SHADOW_VPAR | + RADEON_FP_CRTC_DONT_SHADOW_HEND)); + + fp_gen_cntl &= ~(RADEON_FP_FPON | RADEON_FP_TMDS_EN); + + if (1) // FIXME rgbBits == 8 + fp_gen_cntl |= RADEON_FP_PANEL_FORMAT; /* 24 bit format */ + else + fp_gen_cntl &= ~RADEON_FP_PANEL_FORMAT;/* 18 bit format */ + + if (radeon_crtc->crtc_id == 0) { + if (radeon_is_r300(dev_priv) || dev_priv->chip_family == CHIP_R200) { + fp_gen_cntl &= ~R200_FP_SOURCE_SEL_MASK; + if (radeon_encoder->flags & RADEON_USE_RMX) + fp_gen_cntl |= R200_FP_SOURCE_SEL_RMX; + else + fp_gen_cntl |= R200_FP_SOURCE_SEL_CRTC1; + } else + fp_gen_cntl |= RADEON_FP_SEL_CRTC1; + } else { + if (radeon_is_r300(dev_priv) || dev_priv->chip_family == CHIP_R200) { + fp_gen_cntl &= ~R200_FP_SOURCE_SEL_MASK; + fp_gen_cntl |= R200_FP_SOURCE_SEL_CRTC2; + } else + fp_gen_cntl |= RADEON_FP_SEL_CRTC2; + } + + RADEON_WRITE(RADEON_TMDS_PLL_CNTL, tmds_pll_cntl); + RADEON_WRITE(RADEON_TMDS_TRANSMITTER_CNTL, tmds_transmitter_cntl); + RADEON_WRITE(RADEON_FP_GEN_CNTL, fp_gen_cntl); +} + +static const struct drm_encoder_helper_funcs radeon_legacy_tmds_int_helper_funcs = { + .dpms = radeon_legacy_tmds_int_dpms, + .mode_fixup = radeon_legacy_tmds_int_mode_fixup, + .prepare = radeon_legacy_tmds_int_prepare, + .mode_set = radeon_legacy_tmds_int_mode_set, + .commit = radeon_legacy_tmds_int_commit, +}; + + +static const struct drm_encoder_funcs radeon_legacy_tmds_int_enc_funcs = { + .destroy = radeon_enc_destroy, +}; + +struct drm_encoder *radeon_encoder_legacy_tmds_int_add(struct drm_device *dev, int bios_index) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_encoder *radeon_encoder; + struct drm_encoder *encoder; + + DRM_DEBUG("\n"); + + radeon_encoder = kzalloc(sizeof(struct radeon_encoder), GFP_KERNEL); + if (!radeon_encoder) { + return NULL; + } + + encoder = &radeon_encoder->base; + + encoder->possible_crtcs = 0x3; + encoder->possible_clones = 0; + drm_encoder_init(dev, encoder, &radeon_legacy_tmds_int_enc_funcs, + DRM_MODE_ENCODER_TMDS); + + drm_encoder_helper_add(encoder, &radeon_legacy_tmds_int_helper_funcs); + + if (dev_priv->is_atom_bios) + radeon_atombios_get_tmds_info(radeon_encoder); + else + radeon_combios_get_tmds_info(radeon_encoder); + + return encoder; +} + +static bool radeon_legacy_tmds_ext_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void radeon_legacy_tmds_ext_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_crtc *radeon_crtc; + uint32_t fp2_gen_cntl = RADEON_READ(RADEON_FP2_GEN_CNTL); + uint32_t bios_5_scratch, bios_6_scratch; + int crtc_id = 0; + DRM_DEBUG("\n"); + + if (encoder->crtc) { + radeon_crtc = to_radeon_crtc(encoder->crtc); + crtc_id = radeon_crtc->crtc_id; + } + + // FIXME atom/legacy cards like r4xx + bios_5_scratch = RADEON_READ(RADEON_BIOS_5_SCRATCH); + bios_6_scratch = RADEON_READ(RADEON_BIOS_6_SCRATCH); + + bios_5_scratch &= ~RADEON_DFP2_CRTC_MASK; + bios_5_scratch |= (crtc_id << RADEON_DFP2_CRTC_SHIFT); + + switch(mode) { + case DRM_MODE_DPMS_ON: + fp2_gen_cntl &= ~RADEON_FP2_BLANK_EN; + fp2_gen_cntl |= (RADEON_FP2_ON | RADEON_FP2_DVO_EN); + bios_5_scratch |= RADEON_DFP2_ON; + bios_6_scratch |= RADEON_DFP_DPMS_ON; + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + fp2_gen_cntl |= RADEON_FP2_BLANK_EN; + fp2_gen_cntl &= ~(RADEON_FP2_ON | RADEON_FP2_DVO_EN); + bios_5_scratch &= ~RADEON_DFP2_ON; + bios_6_scratch &= ~RADEON_DFP_DPMS_ON; + break; + } + + RADEON_WRITE(RADEON_FP2_GEN_CNTL, fp2_gen_cntl); + + RADEON_WRITE(RADEON_BIOS_5_SCRATCH, bios_5_scratch); + RADEON_WRITE(RADEON_BIOS_6_SCRATCH, bios_6_scratch); +} + +static void radeon_legacy_tmds_ext_prepare(struct drm_encoder *encoder) +{ + struct drm_radeon_private *dev_priv = encoder->dev->dev_private; + // fix me: atom/legacy r4xx + if (!dev_priv->is_atom_bios) + radeon_combios_output_lock(encoder, true); + radeon_legacy_tmds_ext_dpms(encoder, DRM_MODE_DPMS_OFF); +} + +static void radeon_legacy_tmds_ext_commit(struct drm_encoder *encoder) +{ + struct drm_radeon_private *dev_priv = encoder->dev->dev_private; + radeon_legacy_tmds_ext_dpms(encoder, DRM_MODE_DPMS_ON); + // fix me: atom/legacy r4xx + if (!dev_priv->is_atom_bios) + radeon_combios_output_lock(encoder, false); +} + +static void radeon_legacy_tmds_ext_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + uint32_t fp2_gen_cntl; + + DRM_DEBUG("\n"); + + if (radeon_crtc->crtc_id == 0) + radeon_legacy_rmx_mode_set(encoder, mode, adjusted_mode); + + if (dev_priv->is_atom_bios) { + atombios_ext_tmds_setup(encoder, adjusted_mode); + fp2_gen_cntl = RADEON_READ(RADEON_FP2_GEN_CNTL); + } else { + fp2_gen_cntl = RADEON_READ(RADEON_FP2_GEN_CNTL); + + if (1) // FIXME rgbBits == 8 + fp2_gen_cntl |= RADEON_FP2_PANEL_FORMAT; /* 24 bit format, */ + else + fp2_gen_cntl &= ~RADEON_FP2_PANEL_FORMAT;/* 18 bit format, */ + + fp2_gen_cntl &= ~(RADEON_FP2_ON | + RADEON_FP2_DVO_EN | + RADEON_FP2_DVO_RATE_SEL_SDR); + + /* XXX: these are oem specific */ + if (radeon_is_r300(dev_priv)) { + if ((dev->pdev->device == 0x4850) && + (dev->pdev->subsystem_vendor == 0x1028) && + (dev->pdev->subsystem_device == 0x2001)) /* Dell Inspiron 8600 */ + fp2_gen_cntl |= R300_FP2_DVO_CLOCK_MODE_SINGLE; + else + fp2_gen_cntl |= RADEON_FP2_PAD_FLOP_EN | R300_FP2_DVO_CLOCK_MODE_SINGLE; + + /*if (mode->clock > 165000) + fp2_gen_cntl |= R300_FP2_DVO_DUAL_CHANNEL_EN;*/ + } + } + + if (radeon_crtc->crtc_id == 0) { + if ((dev_priv->chip_family == CHIP_R200) || radeon_is_r300(dev_priv)) { + fp2_gen_cntl &= ~R200_FP2_SOURCE_SEL_MASK; + if (radeon_encoder->flags & RADEON_USE_RMX) + fp2_gen_cntl |= R200_FP2_SOURCE_SEL_RMX; + else + fp2_gen_cntl |= R200_FP2_SOURCE_SEL_CRTC1; + } else + fp2_gen_cntl &= ~RADEON_FP2_SRC_SEL_CRTC2; + } else { + if ((dev_priv->chip_family == CHIP_R200) || radeon_is_r300(dev_priv)) { + fp2_gen_cntl &= ~R200_FP2_SOURCE_SEL_MASK; + fp2_gen_cntl |= R200_FP2_SOURCE_SEL_CRTC2; + } else + fp2_gen_cntl |= RADEON_FP2_SRC_SEL_CRTC2; + } + + RADEON_WRITE(RADEON_FP2_GEN_CNTL, fp2_gen_cntl); +} + +static const struct drm_encoder_helper_funcs radeon_legacy_tmds_ext_helper_funcs = { + .dpms = radeon_legacy_tmds_ext_dpms, + .mode_fixup = radeon_legacy_tmds_ext_mode_fixup, + .prepare = radeon_legacy_tmds_ext_prepare, + .mode_set = radeon_legacy_tmds_ext_mode_set, + .commit = radeon_legacy_tmds_ext_commit, +}; + + +static const struct drm_encoder_funcs radeon_legacy_tmds_ext_enc_funcs = { + .destroy = radeon_enc_destroy, +}; + +struct drm_encoder *radeon_encoder_legacy_tmds_ext_add(struct drm_device *dev, int bios_index) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_encoder *radeon_encoder; + struct drm_encoder *encoder; + + DRM_DEBUG("\n"); + + radeon_encoder = kzalloc(sizeof(struct radeon_encoder), GFP_KERNEL); + if (!radeon_encoder) { + return NULL; + } + + encoder = &radeon_encoder->base; + + encoder->possible_crtcs = 0x3; + encoder->possible_clones = 0; + drm_encoder_init(dev, encoder, &radeon_legacy_tmds_ext_enc_funcs, + DRM_MODE_ENCODER_TMDS); + + drm_encoder_helper_add(encoder, &radeon_legacy_tmds_ext_helper_funcs); + + if (!dev_priv->is_atom_bios) + radeon_combios_get_ext_tmds_info(radeon_encoder); + return encoder; +} + +static bool radeon_legacy_tv_dac_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void radeon_legacy_tv_dac_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_crtc *radeon_crtc; + uint32_t fp2_gen_cntl = 0, crtc2_gen_cntl = 0, tv_dac_cntl = 0; + //uint32_t tv_master_cntl = 0; + uint32_t bios_5_scratch, bios_6_scratch; + int crtc_id = 0; + + DRM_DEBUG("\n"); + + if (encoder->crtc) { + radeon_crtc = to_radeon_crtc(encoder->crtc); + crtc_id = radeon_crtc->crtc_id; + } + + // FIXME atom/legacy cards like r4xx + bios_5_scratch = RADEON_READ(RADEON_BIOS_5_SCRATCH); + bios_6_scratch = RADEON_READ(RADEON_BIOS_6_SCRATCH); + + bios_5_scratch &= ~RADEON_CRT2_CRTC_MASK; + bios_5_scratch |= (crtc_id << RADEON_CRT2_CRTC_SHIFT); + // FIXME TV + //bios_5_scratch &= ~RADEON_TV1_CRTC_MASK; + //bios_5_scratch |= (crtc_id << RADEON_TV1_CRTC_SHIFT); + + if (dev_priv->chip_family == CHIP_R200) + fp2_gen_cntl = RADEON_READ(RADEON_FP2_GEN_CNTL); + else { + crtc2_gen_cntl = RADEON_READ(RADEON_CRTC2_GEN_CNTL); + // FIXME TV + //tv_master_cntl = RADEON_READ(RADEON_TV_MASTER_CNTL); + tv_dac_cntl = RADEON_READ(RADEON_TV_DAC_CNTL); + } + + switch(mode) { + case DRM_MODE_DPMS_ON: + if (dev_priv->chip_family == CHIP_R200) + fp2_gen_cntl |= (RADEON_FP2_ON | RADEON_FP2_DVO_EN); + else { + crtc2_gen_cntl |= RADEON_CRTC2_CRT2_ON; + //tv_master_cntl |= RADEON_TV_ON; + if (dev_priv->chip_family == CHIP_R420 || + dev_priv->chip_family == CHIP_R423 || + dev_priv->chip_family == CHIP_RV410) + tv_dac_cntl &= ~(R420_TV_DAC_RDACPD | + R420_TV_DAC_GDACPD | + R420_TV_DAC_BDACPD | + RADEON_TV_DAC_BGSLEEP); + else + tv_dac_cntl &= ~(RADEON_TV_DAC_RDACPD | + RADEON_TV_DAC_GDACPD | + RADEON_TV_DAC_BDACPD | + RADEON_TV_DAC_BGSLEEP); + } + //bios_5_scratch |= RADEON_TV1_ON; + //bios_6_scratch |= RADEON_TV_DPMS_ON; + bios_5_scratch |= RADEON_CRT2_ON; + bios_6_scratch |= RADEON_CRT_DPMS_ON; + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + if (dev_priv->chip_family == CHIP_R200) + fp2_gen_cntl &= ~(RADEON_FP2_ON | RADEON_FP2_DVO_EN); + else { + crtc2_gen_cntl &= ~RADEON_CRTC2_CRT2_ON; + //tv_master_cntl &= ~RADEON_TV_ON; + if (dev_priv->chip_family == CHIP_R420 || + dev_priv->chip_family == CHIP_R423 || + dev_priv->chip_family == CHIP_RV410) + tv_dac_cntl |= (R420_TV_DAC_RDACPD | + R420_TV_DAC_GDACPD | + R420_TV_DAC_BDACPD | + RADEON_TV_DAC_BGSLEEP); + else + tv_dac_cntl |= (RADEON_TV_DAC_RDACPD | + RADEON_TV_DAC_GDACPD | + RADEON_TV_DAC_BDACPD | + RADEON_TV_DAC_BGSLEEP); + } + //bios_5_scratch &= ~RADEON_TV1_ON; + //bios_6_scratch &= ~RADEON_TV_DPMS_ON; + bios_5_scratch &= ~RADEON_CRT2_ON; + bios_6_scratch &= ~RADEON_CRT_DPMS_ON; + break; + } + + if (dev_priv->chip_family == CHIP_R200) + RADEON_WRITE(RADEON_FP2_GEN_CNTL, fp2_gen_cntl); + else { + RADEON_WRITE(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl); + //RADEON_WRITE(RADEON_TV_MASTER_CNTL, tv_master_cntl); + RADEON_WRITE(RADEON_TV_DAC_CNTL, tv_dac_cntl); + } + + RADEON_WRITE(RADEON_BIOS_5_SCRATCH, bios_5_scratch); + RADEON_WRITE(RADEON_BIOS_6_SCRATCH, bios_6_scratch); +} + +static void radeon_legacy_tv_dac_prepare(struct drm_encoder *encoder) +{ + // fix me: atom/legacy r4xx + radeon_combios_output_lock(encoder, true); + radeon_legacy_tv_dac_dpms(encoder, DRM_MODE_DPMS_OFF); +} + +static void radeon_legacy_tv_dac_commit(struct drm_encoder *encoder) +{ + radeon_legacy_tv_dac_dpms(encoder, DRM_MODE_DPMS_ON); + // fix me: atom/legacy r4xx + radeon_combios_output_lock(encoder, false); +} + +static void radeon_legacy_tv_dac_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + uint32_t tv_dac_cntl, gpiopad_a = 0, dac2_cntl, disp_output_cntl = 0; + uint32_t disp_hw_debug = 0, fp2_gen_cntl = 0; + + DRM_DEBUG("\n"); + + if (radeon_crtc->crtc_id == 0) + radeon_legacy_rmx_mode_set(encoder, mode, adjusted_mode); + + if (dev_priv->chip_family != CHIP_R200) { + tv_dac_cntl = RADEON_READ(RADEON_TV_DAC_CNTL); + if (dev_priv->chip_family == CHIP_R420 || + dev_priv->chip_family == CHIP_R423 || + dev_priv->chip_family == CHIP_RV410) { + tv_dac_cntl &= ~(RADEON_TV_DAC_STD_MASK | + RADEON_TV_DAC_BGADJ_MASK | + R420_TV_DAC_DACADJ_MASK | + R420_TV_DAC_RDACPD | + R420_TV_DAC_GDACPD | + R420_TV_DAC_GDACPD | + R420_TV_DAC_TVENABLE); + } else { + tv_dac_cntl &= ~(RADEON_TV_DAC_STD_MASK | + RADEON_TV_DAC_BGADJ_MASK | + RADEON_TV_DAC_DACADJ_MASK | + RADEON_TV_DAC_RDACPD | + RADEON_TV_DAC_GDACPD | + RADEON_TV_DAC_GDACPD); + } + + // FIXME TV + tv_dac_cntl |= (RADEON_TV_DAC_NBLANK | + RADEON_TV_DAC_NHOLD | + RADEON_TV_DAC_STD_PS2 | + radeon_encoder->ps2_tvdac_adj); + + RADEON_WRITE(RADEON_TV_DAC_CNTL, tv_dac_cntl); + } + + if (radeon_is_r300(dev_priv)) { + gpiopad_a = RADEON_READ(RADEON_GPIOPAD_A) | 1; + disp_output_cntl = RADEON_READ(RADEON_DISP_OUTPUT_CNTL); + } else if (dev_priv->chip_family == CHIP_R200) + fp2_gen_cntl = RADEON_READ(RADEON_FP2_GEN_CNTL); + else + disp_hw_debug = RADEON_READ(RADEON_DISP_HW_DEBUG); + + dac2_cntl = RADEON_READ(RADEON_DAC_CNTL2) | RADEON_DAC2_DAC2_CLK_SEL; + + if (radeon_crtc->crtc_id == 0) { + if (radeon_is_r300(dev_priv)) { + disp_output_cntl &= ~RADEON_DISP_TVDAC_SOURCE_MASK; + disp_output_cntl |= RADEON_DISP_TVDAC_SOURCE_CRTC; + } else if (dev_priv->chip_family == CHIP_R200) { + fp2_gen_cntl &= ~(R200_FP2_SOURCE_SEL_MASK | + RADEON_FP2_DVO_RATE_SEL_SDR); + } else + disp_hw_debug |= RADEON_CRT2_DISP1_SEL; + } else { + if (radeon_is_r300(dev_priv)) { + disp_output_cntl &= ~RADEON_DISP_TVDAC_SOURCE_MASK; + disp_output_cntl |= RADEON_DISP_TVDAC_SOURCE_CRTC2; + } else if (dev_priv->chip_family == CHIP_R200) { + fp2_gen_cntl &= ~(R200_FP2_SOURCE_SEL_MASK | + RADEON_FP2_DVO_RATE_SEL_SDR); + fp2_gen_cntl |= R200_FP2_SOURCE_SEL_CRTC2; + } else + disp_hw_debug &= ~RADEON_CRT2_DISP1_SEL; + } + + RADEON_WRITE(RADEON_DAC_CNTL2, dac2_cntl); + + if (radeon_is_r300(dev_priv)) { + RADEON_WRITE_P(RADEON_GPIOPAD_A, gpiopad_a, ~1); + RADEON_WRITE(RADEON_DISP_TV_OUT_CNTL, disp_output_cntl); + } else if (dev_priv->chip_family == CHIP_R200) + RADEON_WRITE(RADEON_FP2_GEN_CNTL, fp2_gen_cntl); + else + RADEON_WRITE(RADEON_DISP_HW_DEBUG, disp_hw_debug); + +} + +static enum drm_connector_status radeon_legacy_tv_dac_detect(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct drm_device *dev = encoder->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t crtc2_gen_cntl, tv_dac_cntl, dac_cntl2, dac_ext_cntl; + uint32_t disp_hw_debug, disp_output_cntl, gpiopad_a, pixclks_cntl, tmp; + enum drm_connector_status found = connector_status_disconnected; + bool color = true; + + // FIXME tv + + /* save the regs we need */ + pixclks_cntl = RADEON_READ_PLL(dev_priv, RADEON_PIXCLKS_CNTL); + gpiopad_a = radeon_is_r300(dev_priv) ? RADEON_READ(RADEON_GPIOPAD_A) : 0; + disp_output_cntl = radeon_is_r300(dev_priv) ? RADEON_READ(RADEON_DISP_OUTPUT_CNTL) : 0; + disp_hw_debug = radeon_is_r300(dev_priv) ? 0 : RADEON_READ(RADEON_DISP_HW_DEBUG); + crtc2_gen_cntl = RADEON_READ(RADEON_CRTC2_GEN_CNTL); + tv_dac_cntl = RADEON_READ(RADEON_TV_DAC_CNTL); + dac_ext_cntl = RADEON_READ(RADEON_DAC_EXT_CNTL); + dac_cntl2 = RADEON_READ(RADEON_DAC_CNTL2); + + tmp = pixclks_cntl & ~(RADEON_PIX2CLK_ALWAYS_ONb + | RADEON_PIX2CLK_DAC_ALWAYS_ONb); + RADEON_WRITE_PLL(dev_priv, RADEON_PIXCLKS_CNTL, tmp); + + if (radeon_is_r300(dev_priv)) + RADEON_WRITE_P(RADEON_GPIOPAD_A, 1, ~1); + + tmp = crtc2_gen_cntl & ~RADEON_CRTC2_PIX_WIDTH_MASK; + tmp |= RADEON_CRTC2_CRT2_ON | + (2 << RADEON_CRTC2_PIX_WIDTH_SHIFT); + + RADEON_WRITE(RADEON_CRTC2_GEN_CNTL, tmp); + + if (radeon_is_r300(dev_priv)) { + tmp = disp_output_cntl & ~RADEON_DISP_TVDAC_SOURCE_MASK; + tmp |= RADEON_DISP_TVDAC_SOURCE_CRTC2; + RADEON_WRITE(RADEON_DISP_OUTPUT_CNTL, tmp); + } else { + tmp = disp_hw_debug & ~RADEON_CRT2_DISP1_SEL; + RADEON_WRITE(RADEON_DISP_HW_DEBUG, tmp); + } + + tmp = RADEON_TV_DAC_NBLANK | + RADEON_TV_DAC_NHOLD | + RADEON_TV_MONITOR_DETECT_EN | + RADEON_TV_DAC_STD_PS2; + + RADEON_WRITE(RADEON_TV_DAC_CNTL, tmp); + + tmp = RADEON_DAC2_FORCE_BLANK_OFF_EN | + RADEON_DAC2_FORCE_DATA_EN; + + if (color) + tmp |= RADEON_DAC_FORCE_DATA_SEL_RGB; + else + tmp |= RADEON_DAC_FORCE_DATA_SEL_G; + + if (radeon_is_r300(dev_priv)) + tmp |= (0x1b6 << RADEON_DAC_FORCE_DATA_SHIFT); + else + tmp |= (0x180 << RADEON_DAC_FORCE_DATA_SHIFT); + + RADEON_WRITE(RADEON_DAC_EXT_CNTL, tmp); + + tmp = dac_cntl2 | RADEON_DAC2_DAC2_CLK_SEL | RADEON_DAC2_CMP_EN; + RADEON_WRITE(RADEON_DAC_CNTL2, tmp); + + udelay(10000); + + if (radeon_is_r300(dev_priv)) { + if (RADEON_READ(RADEON_DAC_CNTL2) & RADEON_DAC2_CMP_OUT_B) + found = connector_status_connected; + } else { + if (RADEON_READ(RADEON_DAC_CNTL2) & RADEON_DAC2_CMP_OUTPUT) + found = connector_status_connected; + } + + /* restore regs we used */ + RADEON_WRITE(RADEON_DAC_CNTL2, dac_cntl2); + RADEON_WRITE(RADEON_DAC_EXT_CNTL, dac_ext_cntl); + RADEON_WRITE(RADEON_TV_DAC_CNTL, tv_dac_cntl); + RADEON_WRITE(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl); + + if (radeon_is_r300(dev_priv)) { + RADEON_WRITE(RADEON_DISP_OUTPUT_CNTL, disp_output_cntl); + RADEON_WRITE_P(RADEON_GPIOPAD_A, gpiopad_a, ~1 ); + } else { + RADEON_WRITE(RADEON_DISP_HW_DEBUG, disp_hw_debug); + } + RADEON_WRITE_PLL(dev_priv, RADEON_PIXCLKS_CNTL, pixclks_cntl); + + //return found; + return connector_status_disconnected; + +} + +static const struct drm_encoder_helper_funcs radeon_legacy_tv_dac_helper_funcs = { + .dpms = radeon_legacy_tv_dac_dpms, + .mode_fixup = radeon_legacy_tv_dac_mode_fixup, + .prepare = radeon_legacy_tv_dac_prepare, + .mode_set = radeon_legacy_tv_dac_mode_set, + .commit = radeon_legacy_tv_dac_commit, + .detect = radeon_legacy_tv_dac_detect, +}; + + +static const struct drm_encoder_funcs radeon_legacy_tv_dac_enc_funcs = { + .destroy = radeon_enc_destroy, +}; + +struct drm_encoder *radeon_encoder_legacy_tv_dac_add(struct drm_device *dev, int bios_index, int has_tv) +{ + struct radeon_encoder *radeon_encoder; + struct drm_encoder *encoder; + + DRM_DEBUG("\n"); + + radeon_encoder = kzalloc(sizeof(struct radeon_encoder), GFP_KERNEL); + if (!radeon_encoder) { + return NULL; + } + + encoder = &radeon_encoder->base; + + encoder->possible_crtcs = 0x3; + encoder->possible_clones = 0; + drm_encoder_init(dev, encoder, &radeon_legacy_tv_dac_enc_funcs, + DRM_MODE_ENCODER_DAC); + + drm_encoder_helper_add(encoder, &radeon_legacy_tv_dac_helper_funcs); + + /* get the tv dac vals from bios tables */ + radeon_combios_get_tv_info(radeon_encoder); + radeon_combios_get_tv_dac_info(radeon_encoder); + + return encoder; +} diff --git a/linux-core/radeon_mode.h b/linux-core/radeon_mode.h new file mode 100644 index 00000000..23de1088 --- /dev/null +++ b/linux-core/radeon_mode.h @@ -0,0 +1,357 @@ +/* + * Copyright 2000 ATI Technologies Inc., Markham, Ontario, and + * VA Linux Systems Inc., Fremont, California. + * Copyright 2008 Red Hat 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. + * + * Original Authors: + * Kevin E. Martin, Rickard E. Faith, Alan Hourihane + * + * Kernel port Author: Dave Airlie + */ + +#ifndef RADEON_MODE_H +#define RADEON_MODE_H + +#include <linux/i2c.h> +#include <linux/i2c-id.h> +#include <linux/i2c-algo-bit.h> + +#define to_radeon_crtc(x) container_of(x, struct radeon_crtc, base) +#define to_radeon_connector(x) container_of(x, struct radeon_connector, base) +#define to_radeon_encoder(x) container_of(x, struct radeon_encoder, base) +#define to_radeon_framebuffer(x) container_of(x, struct radeon_framebuffer, base) + +enum radeon_connector_type { + CONNECTOR_NONE, + CONNECTOR_VGA, + CONNECTOR_DVI_I, + CONNECTOR_DVI_D, + CONNECTOR_DVI_A, + CONNECTOR_STV, + CONNECTOR_CTV, + CONNECTOR_LVDS, + CONNECTOR_DIGITAL, + CONNECTOR_SCART, + CONNECTOR_HDMI_TYPE_A, + CONNECTOR_HDMI_TYPE_B, + CONNECTOR_0XC, + CONNECTOR_0XD, + CONNECTOR_DIN, + CONNECTOR_DISPLAY_PORT, + CONNECTOR_UNSUPPORTED +}; + +enum radeon_dac_type { + DAC_NONE = 0, + DAC_PRIMARY = 1, + DAC_TVDAC = 2, + DAC_EXT = 3 +}; + +enum radeon_tmds_type { + TMDS_NONE = 0, + TMDS_INT = 1, + TMDS_EXT = 2, + TMDS_LVTMA = 3, + TMDS_DDIA = 4, + TMDS_UNIPHY = 5 +}; + +enum radeon_dvi_type { + DVI_AUTO, + DVI_DIGITAL, + DVI_ANALOG +}; + +enum radeon_rmx_type { + RMX_OFF, + RMX_FULL, + RMX_CENTER, +}; + +enum radeon_tv_std { + TV_STD_NTSC, + TV_STD_PAL, + TV_STD_PAL_M, + TV_STD_PAL_60, + TV_STD_NTSC_J, + TV_STD_SCART_PAL, + TV_STD_SECAM, + TV_STD_PAL_CN, +}; + +struct radeon_i2c_bus_rec { + bool valid; + uint32_t mask_clk_reg; + uint32_t mask_data_reg; + uint32_t a_clk_reg; + uint32_t a_data_reg; + uint32_t put_clk_reg; + uint32_t put_data_reg; + uint32_t get_clk_reg; + uint32_t get_data_reg; + uint32_t mask_clk_mask; + uint32_t mask_data_mask; + uint32_t put_clk_mask; + uint32_t put_data_mask; + uint32_t get_clk_mask; + uint32_t get_data_mask; + uint32_t a_clk_mask; + uint32_t a_data_mask; + int hw_line; + bool hw_capable; +}; + +struct radeon_bios_connector { + enum radeon_dac_type dac_type; + enum radeon_tmds_type tmds_type; + enum radeon_connector_type connector_type; + bool valid; + int output_id; + int devices; + int hpd_mask; + struct radeon_i2c_bus_rec ddc_i2c; + int igp_lane_info; +}; + +struct radeon_tmds_pll { + uint32_t freq; + uint32_t value; +}; + +#define RADEON_MAX_BIOS_CONNECTOR 16 + +#define RADEON_PLL_USE_BIOS_DIVS (1 << 0) +#define RADEON_PLL_NO_ODD_POST_DIV (1 << 1) +#define RADEON_PLL_USE_REF_DIV (1 << 2) +#define RADEON_PLL_LEGACY (1 << 3) +#define RADEON_PLL_PREFER_LOW_REF_DIV (1 << 4) +#define RADEON_PLL_PREFER_HIGH_REF_DIV (1 << 5) +#define RADEON_PLL_PREFER_LOW_FB_DIV (1 << 6) +#define RADEON_PLL_PREFER_HIGH_FB_DIV (1 << 7) +#define RADEON_PLL_PREFER_LOW_POST_DIV (1 << 8) +#define RADEON_PLL_PREFER_HIGH_POST_DIV (1 << 9) + +struct radeon_pll { + uint16_t reference_freq; + uint16_t reference_div; + uint32_t pll_in_min; + uint32_t pll_in_max; + uint32_t pll_out_min; + uint32_t pll_out_max; + uint16_t xclk; + + uint32_t min_ref_div; + uint32_t max_ref_div; + uint32_t min_post_div; + uint32_t max_post_div; + uint32_t min_feedback_div; + uint32_t max_feedback_div; + uint32_t best_vco; +}; + +struct radeon_i2c_chan { + struct drm_device *dev; + struct i2c_adapter adapter; + struct i2c_algo_bit_data algo; + struct radeon_i2c_bus_rec rec; +}; + +struct radeon_mode_info { + struct atom_context *atom_context; + struct radeon_bios_connector bios_connector[RADEON_MAX_BIOS_CONNECTOR]; + struct radeon_pll p1pll; + struct radeon_pll p2pll; + struct radeon_pll spll; + struct radeon_pll mpll; + uint32_t mclk; + uint32_t sclk; +}; + +struct radeon_crtc { + struct drm_crtc base; + int crtc_id; + u8 lut_r[256], lut_g[256], lut_b[256]; + bool enabled; + bool can_tile; + uint32_t crtc_offset; + struct radeon_framebuffer *fbdev_fb; + struct drm_mode_set mode_set; +}; + +#define RADEON_USE_RMX 1 + +struct radeon_encoder { + struct drm_encoder base; + uint32_t encoder_mode; + uint32_t flags; + enum radeon_rmx_type rmx_type; + union { + enum radeon_dac_type dac; + enum radeon_tmds_type tmds; + } type; + int atom_device; /* atom devices */ + + /* preferred mode */ + uint32_t panel_xres, panel_yres; + uint32_t hoverplus, hsync_width; + uint32_t hblank; + uint32_t voverplus, vsync_width; + uint32_t vblank; + uint32_t dotclock; + + /* legacy lvds */ + uint16_t panel_vcc_delay; + uint16_t panel_pwr_delay; + uint16_t panel_digon_delay; + uint16_t panel_blon_delay; + uint32_t panel_ref_divider; + uint32_t panel_post_divider; + uint32_t panel_fb_divider; + bool use_bios_dividers; + uint32_t lvds_gen_cntl; + + /* atom lvds */ + int lvds_misc; + int lvds_ss_id; + + /* legacy primary dac */ + uint32_t ps2_pdac_adj; + + /* legacy tv dac */ + uint32_t ps2_tvdac_adj; + uint32_t ntsc_tvdac_adj; + uint32_t pal_tvdac_adj; + enum radeon_tv_std tv_std; + + /* legacy int tmds */ + struct radeon_tmds_pll tmds_pll[4]; +}; + +struct radeon_connector { + struct drm_connector base; + struct radeon_i2c_chan *ddc_bus; + int use_digital; +}; + +struct radeon_framebuffer { + struct drm_framebuffer base; + struct drm_bo_kmap_obj kmap_obj; + struct drm_gem_object *obj; +}; + +extern struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev, + struct radeon_i2c_bus_rec *rec, + const char *name); +extern void radeon_i2c_destroy(struct radeon_i2c_chan *i2c); +extern bool radeon_ddc_probe(struct radeon_connector *radeon_connector); +extern int radeon_ddc_get_modes(struct radeon_connector *radeon_connector); +extern struct drm_connector *radeon_connector_add(struct drm_device *dev, int bios_index); + +extern struct drm_encoder *radeon_best_encoder(struct drm_connector *connector); + +extern void radeon_compute_pll(struct radeon_pll *pll, + uint64_t freq, + uint32_t *dot_clock_p, + uint32_t *fb_div_p, + uint32_t *ref_div_p, + uint32_t *post_div_p, + int flags); + +struct drm_encoder *radeon_encoder_lvtma_add(struct drm_device *dev, int bios_index); +struct drm_encoder *radeon_encoder_atom_dac_add(struct drm_device *dev, int bios_index, int dac_id, int with_tv); +struct drm_encoder *radeon_encoder_atom_tmds_add(struct drm_device *dev, int bios_index, int tmds_type); +struct drm_encoder *radeon_encoder_legacy_lvds_add(struct drm_device *dev, int bios_index); +struct drm_encoder *radeon_encoder_legacy_primary_dac_add(struct drm_device *dev, int bios_index, int with_tv); +struct drm_encoder *radeon_encoder_legacy_tv_dac_add(struct drm_device *dev, int bios_index, int with_tv); +struct drm_encoder *radeon_encoder_legacy_tmds_int_add(struct drm_device *dev, int bios_index); +struct drm_encoder *radeon_encoder_legacy_tmds_ext_add(struct drm_device *dev, int bios_index); +extern void atombios_ext_tmds_setup(struct drm_encoder *encoder, + struct drm_display_mode *mode); + +extern void radeon_crtc_load_lut(struct drm_crtc *crtc); +extern void atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y); +extern void atombios_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y); +extern void atombios_crtc_dpms(struct drm_crtc *crtc, int mode); + +extern void radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y); +extern void radeon_legacy_atom_set_surface(struct drm_crtc *crtc); + +extern int radeon_crtc_cursor_set(struct drm_crtc *crtc, + struct drm_file *file_priv, + uint32_t handle, + uint32_t width, + uint32_t height); +extern int radeon_crtc_cursor_move(struct drm_crtc *crtc, + int x, int y); + +extern bool radeon_atom_get_clock_info(struct drm_device *dev); +extern bool radeon_combios_get_clock_info(struct drm_device *dev); +extern void radeon_atombios_get_lvds_info(struct radeon_encoder *encoder); +extern void radeon_atombios_get_tmds_info(struct radeon_encoder *encoder); +extern bool radeon_combios_get_lvds_info(struct radeon_encoder *encoder); +extern bool radeon_combios_get_tmds_info(struct radeon_encoder *encoder); +extern void radeon_combios_get_ext_tmds_info(struct radeon_encoder *encoder); +extern bool radeon_combios_get_tv_info(struct radeon_encoder *encoder); +extern bool radeon_combios_get_tv_dac_info(struct radeon_encoder *encoder); +extern bool radeon_combios_get_primary_dac_info(struct radeon_encoder *encoder); +extern void radeon_combios_output_lock(struct drm_encoder *encoder, bool lock); +extern void radeon_combios_initialize_bios_scratch_regs(struct drm_device *dev); +extern void radeon_atom_output_lock(struct drm_encoder *encoder, bool lock); +extern void radeon_atom_initialize_bios_scratch_regs(struct drm_device *dev); +extern void radeon_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, + u16 blue, int regno); +struct drm_framebuffer *radeon_framebuffer_create(struct drm_device *dev, + struct drm_mode_fb_cmd *mode_cmd, + struct drm_gem_object *obj); + +int radeonfb_probe(struct drm_device *dev); + +int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *fb); +bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev); +void radeon_atombios_init_crtc(struct drm_device *dev, + struct radeon_crtc *radeon_crtc); +void radeon_legacy_init_crtc(struct drm_device *dev, + struct radeon_crtc *radeon_crtc); +void radeon_i2c_do_lock(struct radeon_connector *radeon_connector, int lock_state); + +void radeon_atom_static_pwrmgt_setup(struct drm_device *dev, int enable); +void radeon_atom_dyn_clk_setup(struct drm_device *dev, int enable); +void radeon_combios_dyn_clk_setup(struct drm_device *dev, int enable); +void radeon_get_clock_info(struct drm_device *dev); +extern bool radeon_get_atom_connector_info_from_bios_connector_table(struct drm_device *dev); + +void radeon_rmx_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); +void radeon_enc_destroy(struct drm_encoder *encoder); +void radeon_emit_copy_blit(struct drm_device * dev, + uint32_t src_offset, + uint32_t dst_offset, + uint32_t pages); +void radeon_copy_fb(struct drm_device *dev, struct drm_gem_object *dst_obj); +void radeon_combios_asic_init(struct drm_device *dev); +extern int radeon_static_clocks_init(struct drm_device *dev); + +#endif diff --git a/linux-core/radeon_ms.h b/linux-core/radeon_ms.h new file mode 120000 index 00000000..da340c5d --- /dev/null +++ b/linux-core/radeon_ms.h @@ -0,0 +1 @@ +../shared-core/radeon_ms.h
\ No newline at end of file diff --git a/linux-core/radeon_ms_bo.c b/linux-core/radeon_ms_bo.c new file mode 120000 index 00000000..d05df59f --- /dev/null +++ b/linux-core/radeon_ms_bo.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_bo.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_bus.c b/linux-core/radeon_ms_bus.c new file mode 120000 index 00000000..50f649d0 --- /dev/null +++ b/linux-core/radeon_ms_bus.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_bus.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_combios.c b/linux-core/radeon_ms_combios.c new file mode 120000 index 00000000..d7b99958 --- /dev/null +++ b/linux-core/radeon_ms_combios.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_combios.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_combios.h b/linux-core/radeon_ms_combios.h new file mode 120000 index 00000000..5b19c708 --- /dev/null +++ b/linux-core/radeon_ms_combios.h @@ -0,0 +1 @@ +../shared-core/radeon_ms_combios.h
\ No newline at end of file diff --git a/linux-core/radeon_ms_compat.c b/linux-core/radeon_ms_compat.c new file mode 100644 index 00000000..6efdc786 --- /dev/null +++ b/linux-core/radeon_ms_compat.c @@ -0,0 +1,67 @@ +/* + * Copyright 2007 Dave Airlie + * Copyright 2007 Alex Deucher + * Copyright 2007 Michel Dänzer + * Copyright 2007 Roland Scheidegger + * Copyright 2007 Vladimir Dergachev + * Copyright 2007 Nicolai Haehnle + * Copyright 2007 Aapo Tahkola + * Copyright 2007 Ben Skeggs + * Copyright 2007 Jérôme Glisse + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/* + * Authors: + * Jerome Glisse <glisse@freedesktop.org> + */ +#include "drmP.h" +#include "drm.h" +#include "radeon_ms.h" + +/** + * Called whenever a 32-bit process running under a 64-bit kernel + * performs an ioctl on /dev/dri/card<n>. + + * \param filp file pointer. + * \param cmd command. + * \param arg user argument. + * \return zero on success or negative number on failure. + */ +long radeon_ms_compat_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + unsigned int nr = DRM_IOCTL_NR(cmd); + drm_ioctl_compat_t *fn = NULL; + int ret; + + if (nr < DRM_COMMAND_BASE) + return drm_compat_ioctl(filp, cmd, arg); + + lock_kernel(); + if (fn != NULL) + ret = (*fn)(filp, cmd, arg); + else + ret = drm_ioctl(filp->f_dentry->d_inode, filp, cmd, arg); + unlock_kernel(); + + return ret; +} diff --git a/linux-core/radeon_ms_cp.c b/linux-core/radeon_ms_cp.c new file mode 120000 index 00000000..6aee3e6e --- /dev/null +++ b/linux-core/radeon_ms_cp.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_cp.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_cp_mc.c b/linux-core/radeon_ms_cp_mc.c new file mode 120000 index 00000000..0ae1a647 --- /dev/null +++ b/linux-core/radeon_ms_cp_mc.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_cp_mc.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_crtc.c b/linux-core/radeon_ms_crtc.c new file mode 120000 index 00000000..31f11447 --- /dev/null +++ b/linux-core/radeon_ms_crtc.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_crtc.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_dac.c b/linux-core/radeon_ms_dac.c new file mode 120000 index 00000000..cb523cfb --- /dev/null +++ b/linux-core/radeon_ms_dac.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_dac.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_drm.c b/linux-core/radeon_ms_drm.c new file mode 120000 index 00000000..8bbf19a5 --- /dev/null +++ b/linux-core/radeon_ms_drm.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_drm.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_drm.h b/linux-core/radeon_ms_drm.h new file mode 120000 index 00000000..5d9d7319 --- /dev/null +++ b/linux-core/radeon_ms_drm.h @@ -0,0 +1 @@ +../shared-core/radeon_ms_drm.h
\ No newline at end of file diff --git a/linux-core/radeon_ms_drv.c b/linux-core/radeon_ms_drv.c new file mode 100644 index 00000000..fc3c106a --- /dev/null +++ b/linux-core/radeon_ms_drv.c @@ -0,0 +1,144 @@ +/* + * Copyright 2007 Jerome Glisse. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/* + * Authors: + * Jerome Glisse <glisse@freedesktop.org> + */ +#include "drm_pciids.h" +#include "radeon_ms.h" + +extern struct drm_fence_driver r3xx_fence_driver; +extern struct drm_bo_driver radeon_ms_bo_driver; +extern struct drm_ioctl_desc radeon_ms_ioctls[]; +extern int radeon_ms_num_ioctls; + +static int radeon_ms_driver_dri_library_name(struct drm_device * dev, + char * buf); +static int radeon_ms_driver_probe(struct pci_dev *pdev, + const struct pci_device_id *ent); + +static struct pci_device_id pciidlist[] = { + radeon_ms_PCI_IDS +}; + +static struct drm_driver driver = { + .load = radeon_ms_driver_load, + .firstopen = NULL, + .open = radeon_ms_driver_open, + .preclose = NULL, + .postclose = NULL, + .lastclose = radeon_ms_driver_lastclose, + .unload = radeon_ms_driver_unload, + .dma_ioctl = radeon_ms_driver_dma_ioctl, + .dma_ready = NULL, + .dma_quiescent = NULL, + .context_ctor = NULL, + .context_dtor = NULL, + .kernel_context_switch = NULL, + .kernel_context_switch_unlock = NULL, + .dri_library_name = radeon_ms_driver_dri_library_name, + .device_is_agp = NULL, + .irq_handler = radeon_ms_irq_handler, + .irq_preinstall = radeon_ms_irq_preinstall, + .irq_postinstall = radeon_ms_irq_postinstall, + .irq_uninstall = radeon_ms_irq_uninstall, + .reclaim_buffers = drm_core_reclaim_buffers, + .reclaim_buffers_locked = NULL, + .reclaim_buffers_idlelocked = NULL, + .get_map_ofs = drm_core_get_map_ofs, + .get_reg_ofs = drm_core_get_reg_ofs, + .set_version = NULL, + .fb_probe = radeonfb_probe, + .fb_remove = radeonfb_remove, + .fence_driver = &r3xx_fence_driver, + .bo_driver = &radeon_ms_bo_driver, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .driver_features = + DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG | + DRIVER_HAVE_IRQ | DRIVER_HAVE_DMA | DRIVER_IRQ_SHARED, + .dev_priv_size = 0, + .ioctls = radeon_ms_ioctls, + .num_ioctls = 0, + .fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .ioctl = drm_ioctl, + .mmap = drm_mmap, + .poll = drm_poll, + .fasync = drm_fasync, +#if defined(CONFIG_COMPAT) && LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) + .compat_ioctl = radeon_ms_compat_ioctl, +#endif + }, + .pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, + .probe = radeon_ms_driver_probe, + .remove = __devexit_p(drm_cleanup_pci), + }, +}; + +static int radeon_ms_driver_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + return drm_get_dev(pdev, ent, &driver); +} + +static int radeon_ms_driver_dri_library_name(struct drm_device * dev, + char * buf) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + int ret; + + switch (dev_priv->family) { + default: + ret = snprintf(buf, PAGE_SIZE, "\n"); + } + return ret; +} + +static void __exit radeon_ms_driver_exit(void) +{ + drm_exit(&driver); +} + +static int __init radeon_ms_driver_init(void) +{ + driver.num_ioctls = radeon_ms_num_ioctls; + driver.driver_features |= DRIVER_MODESET; + return drm_init(&driver, pciidlist); +} + +module_init(radeon_ms_driver_init); +module_exit(radeon_ms_driver_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL and additional rights"); diff --git a/linux-core/radeon_ms_drv.h b/linux-core/radeon_ms_drv.h new file mode 100644 index 00000000..b80ed442 --- /dev/null +++ b/linux-core/radeon_ms_drv.h @@ -0,0 +1,46 @@ +/* + * Copyright 2007 Dave Airlie + * Copyright 2007 Alex Deucher + * Copyright 2007 Michel Dänzer + * Copyright 2007 Roland Scheidegger + * Copyright 2007 Vladimir Dergachev + * Copyright 2007 Nicolai Haehnle + * Copyright 2007 Aapo Tahkola + * Copyright 2007 Ben Skeggs + * Copyright 2007 Jérôme Glisse + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/* + * Authors: + * Jérôme Glisse <glisse@freedesktop.org> + */ +#ifndef __RADEON_MS_DRV_H__ +#define __RADEON_MS_DRV_H__ + +#include <linux/i2c.h> +#include <linux/i2c-id.h> +#include <linux/i2c-algo-bit.h> +#include <linux/pci.h> +#include "drm.h" +#include "drmP.h" + +#endif diff --git a/linux-core/radeon_ms_exec.c b/linux-core/radeon_ms_exec.c new file mode 120000 index 00000000..cb397fbf --- /dev/null +++ b/linux-core/radeon_ms_exec.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_exec.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_family.c b/linux-core/radeon_ms_family.c new file mode 120000 index 00000000..1f12e092 --- /dev/null +++ b/linux-core/radeon_ms_family.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_family.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_fb.c b/linux-core/radeon_ms_fb.c new file mode 100644 index 00000000..0ac9207c --- /dev/null +++ b/linux-core/radeon_ms_fb.c @@ -0,0 +1,462 @@ +/* + * Copyright © 2007 David Airlie + * Copyright © 2007 Jerome Glisse + * + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation on the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT. IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR + * THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/init.h> + +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "radeon_ms.h" +#include "amd.h" + + +static int radeonfb_setcolreg(unsigned regno, unsigned red, + unsigned green, unsigned blue, + unsigned transp, struct fb_info *info) +{ + struct amd_fb *par = info->par; + struct drm_framebuffer *fb = par->fb; + struct drm_crtc *crtc = par->crtc; + + if (regno > 255) { + return 1; + } + // if (crtc->funcs->gamma_set) { + // crtc->funcs->gamma_set(crtc, red, green, blue, regno); + // } + if (regno < 16) { + switch (fb->depth) { + case 15: + fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) | + ((green & 0xf800) >> 6) | + ((blue & 0xf800) >> 11); + break; + case 16: + fb->pseudo_palette[regno] = (red & 0xf800) | + ((green & 0xfc00) >> 5) | + ((blue & 0xf800) >> 11); + break; + case 24: + case 32: + fb->pseudo_palette[regno] = ((red & 0xff00) << 8) | + (green & 0xff00) | + ((blue & 0xff00) >> 8); + break; + } + } + return 0; +} + +static int radeonfb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct amd_fb *par = info->par; + struct drm_framebuffer *fb = par->fb; + + if (!var->pixclock) + return -EINVAL; + + /* Need to resize the fb object !!! */ + if (var->xres > fb->width || var->yres > fb->height) { + DRM_ERROR("Requested width/height is greater than " + "current fb object %dx%d > %dx%d\n", + var->xres, var->yres, fb->width, fb->height); + DRM_ERROR("Need resizing code.\n"); + return -EINVAL; + } + + switch (var->bits_per_pixel) { + case 16: + if (var->green.length == 5) { + var->red.offset = 10; + var->green.offset = 5; + var->blue.offset = 0; + var->red.length = 5; + var->green.length = 5; + var->blue.length = 5; + var->transp.length = 0; + var->transp.offset = 0; + } else { + var->red.offset = 11; + var->green.offset = 6; + var->blue.offset = 0; + var->red.length = 5; + var->green.length = 6; + var->blue.length = 5; + var->transp.length = 0; + var->transp.offset = 0; + } + break; + case 32: + if (var->transp.length) { + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 8; + var->transp.offset = 24; + } else { + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 0; + var->transp.offset = 0; + } + break; + default: + return -EINVAL; + } + return 0; +} + +static bool radeonfb_mode_equal(struct drm_display_mode *mode1, + struct drm_display_mode *mode2) +{ + if (mode1->hdisplay == mode2->hdisplay && + mode1->hsync_start == mode2->hsync_start && + mode1->hsync_end == mode2->hsync_end && + mode1->htotal == mode2->htotal && + mode1->hskew == mode2->hskew && + mode1->vdisplay == mode2->vdisplay && + mode1->vsync_start == mode2->vsync_start && + mode1->vsync_end == mode2->vsync_end && + mode1->vtotal == mode2->vtotal && + mode1->vscan == mode2->vscan && + mode1->flags == mode2->flags) { + /* FIXME: what about adding a margin for clock ? */ + if (mode1->clock == mode2->clock) + return true; + return false; + } + + return false; +} + +static int radeonfb_set_par(struct fb_info *info) +{ + struct amd_fb *par = info->par; + struct drm_framebuffer *fb = par->fb; + struct drm_device *dev = par->dev; + struct drm_display_mode *drm_mode, *search_mode; + struct drm_output *output; + struct fb_var_screeninfo *var = &info->var; + int found = 0; + + switch (var->bits_per_pixel) { + case 16: + fb->depth = (var->green.length == 6) ? 16 : 15; + break; + case 32: + fb->depth = (var->transp.length > 0) ? 32 : 24; + break; + default: + return -EINVAL; + } + fb->bits_per_pixel = var->bits_per_pixel; + + info->fix.line_length = fb->pitch; + info->fix.smem_len = info->fix.line_length * fb->height; + info->fix.visual = FB_VISUAL_TRUECOLOR; + info->screen_size = info->fix.smem_len; /* ??? */ + + /* Should we walk the output's modelist or just create our own ??? + * For now, we create and destroy a mode based on the incoming + * parameters. But there's commented out code below which scans + * the output list too. + */ + drm_mode = drm_mode_create(dev); + drm_mode->hdisplay = var->xres; + drm_mode->hsync_start = drm_mode->hdisplay + var->right_margin; + drm_mode->hsync_end = drm_mode->hsync_start + var->hsync_len; + drm_mode->htotal = drm_mode->hsync_end + var->left_margin; + drm_mode->vdisplay = var->yres; + drm_mode->vsync_start = drm_mode->vdisplay + var->lower_margin; + drm_mode->vsync_end = drm_mode->vsync_start + var->vsync_len; + drm_mode->vtotal = drm_mode->vsync_end + var->upper_margin; + drm_mode->clock = PICOS2KHZ(var->pixclock); + drm_mode->vrefresh = drm_mode_vrefresh(drm_mode); + drm_mode_set_name(drm_mode); + drm_mode_set_crtcinfo(drm_mode, CRTC_INTERLACE_HALVE_V); + + list_for_each_entry(output, &dev->mode_config.output_list, head) { + if (output->crtc == par->crtc) + break; + } + + drm_mode_debug_printmodeline(drm_mode); + list_for_each_entry(search_mode, &output->modes, head) { + drm_mode_debug_printmodeline(search_mode); + if (radeonfb_mode_equal(drm_mode, search_mode)) { + drm_mode_destroy(dev, drm_mode); + drm_mode = search_mode; + found = 1; + break; + } + } + + if (!found) { + if (par->fb_mode) { + drm_mode_detachmode_crtc(dev, par->fb_mode); + } + par->fb_mode = drm_mode; + drm_mode_debug_printmodeline(drm_mode); + /* attach mode */ + drm_mode_attachmode_crtc(dev, par->crtc, par->fb_mode); + } + + if (par->crtc->enabled) { + if (!drm_mode_equal(&par->crtc->mode, drm_mode) || + par->crtc->fb != par->fb) { + par->crtc->fb = par->fb; + if (!drm_crtc_set_mode(par->crtc, drm_mode, 0, 0)) { + return -EINVAL; + } + } + } + + return 0; +} + +static struct fb_ops radeonfb_ops = { + .owner = THIS_MODULE, + // .fb_open = radeonfb_open, + // .fb_read = radeonfb_read, + // .fb_write = radeonfb_write, + // .fb_release = radeonfb_release, + // .fb_ioctl = radeonfb_ioctl, + .fb_check_var = radeonfb_check_var, + .fb_set_par = radeonfb_set_par, + .fb_setcolreg = radeonfb_setcolreg, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +int radeonfb_probe(struct drm_device *dev, struct drm_crtc *crtc, struct drm_output *output) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct fb_info *info; + struct amd_fb *par; + struct device *device = &dev->pdev->dev; + struct drm_framebuffer *fb; + struct drm_display_mode *mode = crtc->desired_mode; + int ret; + + info = framebuffer_alloc(sizeof(struct amd_fb), device); + if (!info){ + DRM_INFO("[radeon_ms] framebuffer_alloc failed\n"); + return -EINVAL; + } + + fb = drm_framebuffer_create(dev); + if (!fb) { + framebuffer_release(info); + DRM_ERROR("[radeon_ms] failed to allocate fb.\n"); + return -EINVAL; + } + crtc->fb = fb; + + fb->width = crtc->desired_mode->hdisplay; + fb->height = crtc->desired_mode->vdisplay; + fb->bits_per_pixel = 32; + fb->pitch = fb->width * ((fb->bits_per_pixel + 1) / 8); + fb->depth = 24; + /* one page alignment should be fine for constraint (micro|macro tiling, + * bit depth, color buffer offset, ...) */ + ret = drm_buffer_object_create(dev, fb->width * fb->height * 4, + drm_bo_type_kernel, + DRM_BO_FLAG_READ | + DRM_BO_FLAG_WRITE | + DRM_BO_FLAG_NO_EVICT | + DRM_BO_FLAG_MEM_VRAM, + DRM_BO_HINT_DONT_FENCE, + 1, + 0, + &fb->bo); + if (ret || fb->bo == NULL) { + DRM_ERROR("[radeon_ms] failed to allocate framebuffer\n"); + drm_framebuffer_destroy(fb); + framebuffer_release(info); + return -EINVAL; + } + + DRM_INFO("[radeon_ms] framebuffer %dx%d at 0x%08lX\n", + fb->width, fb->height, fb->bo->offset); + + fb->fbdev = info; + par = info->par; + dev_priv->fb = par; + par->dev = dev; + par->crtc = crtc; + par->fb = fb; + info->fbops = &radeonfb_ops; + strcpy(info->fix.id, "radeonfb"); + info->fix.type = FB_TYPE_PACKED_PIXELS; + info->fix.visual = FB_VISUAL_TRUECOLOR; + info->fix.type_aux = 0; + info->fix.xpanstep = 8; + info->fix.ypanstep = 1; + info->fix.ywrapstep = 0; + info->fix.accel = FB_ACCEL_ATI_RADEON; + info->fix.type_aux = 0; + info->fix.mmio_start = 0; + info->fix.mmio_len = 0; + info->fix.line_length = fb->pitch; + info->fix.smem_start = fb->bo->offset + dev->mode_config.fb_base; + info->fix.smem_len = info->fix.line_length * fb->height; + info->flags = FBINFO_DEFAULT; + DRM_INFO("[radeon_ms] fb physical start : 0x%lX\n", info->fix.smem_start); + DRM_INFO("[radeon_ms] fb physical size : %d\n", info->fix.smem_len); + + ret = drm_bo_kmap(fb->bo, 0, fb->bo->num_pages, &fb->kmap); + if (ret) { + DRM_ERROR("error mapping fb: %d\n", ret); + } + info->screen_base = fb->kmap.virtual; + info->screen_size = info->fix.smem_len; /* FIXME */ + info->pseudo_palette = fb->pseudo_palette; + info->var.xres_virtual = fb->width; + info->var.yres_virtual = fb->height; + info->var.bits_per_pixel = fb->bits_per_pixel; + info->var.xoffset = 0; + info->var.yoffset = 0; + info->var.activate = FB_ACTIVATE_NOW; + info->var.height = -1; + info->var.width = -1; + info->var.vmode = FB_VMODE_NONINTERLACED; + + info->var.xres = mode->hdisplay; + info->var.right_margin = mode->hsync_start - mode->hdisplay; + info->var.hsync_len = mode->hsync_end - mode->hsync_start; + info->var.left_margin = mode->htotal - mode->hsync_end; + info->var.yres = mode->vdisplay; + info->var.lower_margin = mode->vsync_start - mode->vdisplay; + info->var.vsync_len = mode->vsync_end - mode->vsync_start; + info->var.upper_margin = mode->vtotal - mode->vsync_end; + info->var.pixclock = 10000000 / mode->htotal * 1000 / + mode->vtotal * 100; + /* avoid overflow */ + info->var.pixclock = info->var.pixclock * 1000 / mode->vrefresh; + + info->pixmap.size = 64*1024; + info->pixmap.buf_align = 8; + info->pixmap.access_align = 32; + info->pixmap.flags = FB_PIXMAP_SYSTEM; + info->pixmap.scan_align = 1; + + DRM_DEBUG("fb depth is %d\n", fb->depth); + DRM_DEBUG(" pitch is %d\n", fb->pitch); + switch(fb->depth) { + case 15: + info->var.red.offset = 10; + info->var.green.offset = 5; + info->var.blue.offset = 0; + info->var.red.length = info->var.green.length = + info->var.blue.length = 5; + info->var.transp.offset = 15; + info->var.transp.length = 1; + break; + case 16: + info->var.red.offset = 11; + info->var.green.offset = 5; + info->var.blue.offset = 0; + info->var.red.length = 5; + info->var.green.length = 6; + info->var.blue.length = 5; + info->var.transp.offset = 0; + break; + case 24: + info->var.red.offset = 16; + info->var.green.offset = 8; + info->var.blue.offset = 0; + info->var.red.length = info->var.green.length = + info->var.blue.length = 8; + info->var.transp.offset = 0; + info->var.transp.length = 0; + break; + case 32: + info->var.red.offset = 16; + info->var.green.offset = 8; + info->var.blue.offset = 0; + info->var.red.length = info->var.green.length = + info->var.blue.length = 8; + info->var.transp.offset = 24; + info->var.transp.length = 8; + break; + default: + DRM_ERROR("only support 15, 16, 24 or 32bits per pixel " + "got %d\n", fb->depth); + return -EINVAL; + break; + } + + if (register_framebuffer(info) < 0) { + return -EINVAL; + } + + DRM_INFO("[radeon_ms] fb%d: %s frame buffer device\n", info->node, + info->fix.id); + return 0; +} +EXPORT_SYMBOL(radeonfb_probe); + +int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *kern_fb) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct amd_fb *fb = dev_priv->fb; + struct fb_info *info; + + if (fb == NULL || fb->fb == NULL || fb->fb->fbdev == NULL) { + DRM_INFO("[radeon_ms] %s: no crtc, or fb or fbdev\n", + __func__); + return 0; + } + info = fb->fb->fbdev; + unregister_framebuffer(info); + drm_bo_kunmap(&fb->fb->kmap); + drm_bo_usage_deref_unlocked(&fb->fb->bo); + drm_framebuffer_destroy(fb->fb); + framebuffer_release(info); + dev_priv->fb = NULL; + return 0; +} +EXPORT_SYMBOL(radeonfb_remove); +MODULE_LICENSE("GPL"); diff --git a/linux-core/radeon_ms_fence.c b/linux-core/radeon_ms_fence.c new file mode 120000 index 00000000..383cc070 --- /dev/null +++ b/linux-core/radeon_ms_fence.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_fence.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_gpu.c b/linux-core/radeon_ms_gpu.c new file mode 120000 index 00000000..fa5e05bf --- /dev/null +++ b/linux-core/radeon_ms_gpu.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_gpu.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_i2c.c b/linux-core/radeon_ms_i2c.c new file mode 120000 index 00000000..1863e6d6 --- /dev/null +++ b/linux-core/radeon_ms_i2c.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_i2c.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_irq.c b/linux-core/radeon_ms_irq.c new file mode 120000 index 00000000..c4e60ba6 --- /dev/null +++ b/linux-core/radeon_ms_irq.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_irq.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_output.c b/linux-core/radeon_ms_output.c new file mode 120000 index 00000000..6a38b671 --- /dev/null +++ b/linux-core/radeon_ms_output.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_output.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_properties.c b/linux-core/radeon_ms_properties.c new file mode 120000 index 00000000..e2e0dc0b --- /dev/null +++ b/linux-core/radeon_ms_properties.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_properties.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_properties.h b/linux-core/radeon_ms_properties.h new file mode 120000 index 00000000..59783e89 --- /dev/null +++ b/linux-core/radeon_ms_properties.h @@ -0,0 +1 @@ +../shared-core/radeon_ms_properties.h
\ No newline at end of file diff --git a/linux-core/radeon_ms_reg.h b/linux-core/radeon_ms_reg.h new file mode 120000 index 00000000..24b01b42 --- /dev/null +++ b/linux-core/radeon_ms_reg.h @@ -0,0 +1 @@ +../shared-core/radeon_ms_reg.h
\ No newline at end of file diff --git a/linux-core/radeon_ms_rom.c b/linux-core/radeon_ms_rom.c new file mode 120000 index 00000000..80f5f606 --- /dev/null +++ b/linux-core/radeon_ms_rom.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_rom.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_rom.h b/linux-core/radeon_ms_rom.h new file mode 120000 index 00000000..f20e42be --- /dev/null +++ b/linux-core/radeon_ms_rom.h @@ -0,0 +1 @@ +../shared-core/radeon_ms_rom.h
\ No newline at end of file diff --git a/linux-core/radeon_ms_state.c b/linux-core/radeon_ms_state.c new file mode 120000 index 00000000..2d2e2ef7 --- /dev/null +++ b/linux-core/radeon_ms_state.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_state.c
\ No newline at end of file diff --git a/linux-core/radeon_pm.c b/linux-core/radeon_pm.c new file mode 100644 index 00000000..259d42da --- /dev/null +++ b/linux-core/radeon_pm.c @@ -0,0 +1,248 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat 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. + * + * Authors: Dave Airlie + * Alex Deucher + */ +#include "drmP.h" +#include "radeon_drm.h" +#include "radeon_drv.h" + +#include "atom.h" + +#include "drm_crtc_helper.h" + +int radeon_suspend(struct drm_device *dev, pm_message_t state) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct drm_framebuffer *fb; + int i; + + if (!dev || !dev_priv) { + return -ENODEV; + } + + if (state.event == PM_EVENT_PRETHAW) + return 0; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return 0; + + /* unpin the front buffers */ + list_for_each_entry(fb, &dev->mode_config.fb_kernel_list, filp_head) { + struct radeon_framebuffer *radeon_fb = to_radeon_framebuffer(fb); + + if (!radeon_fb) + continue; + + if (!radeon_fb->obj) + continue; + + radeon_gem_object_unpin(radeon_fb->obj); + } + + if (!(dev_priv->flags & RADEON_IS_IGP)) + drm_bo_evict_mm(dev, DRM_BO_MEM_VRAM, 0); + + dev_priv->pmregs.crtc_ext_cntl = RADEON_READ(RADEON_CRTC_EXT_CNTL); + for (i = 0; i < 8; i++) + dev_priv->pmregs.bios_scratch[i] = RADEON_READ(RADEON_BIOS_0_SCRATCH + (i * 4)); + + radeon_modeset_cp_suspend(dev); + + /* Disable *all* interrupts */ + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS690) + RADEON_WRITE(R500_DxMODE_INT_MASK, 0); + RADEON_WRITE(RADEON_GEN_INT_CNTL, 0); + + if (dev_priv->flags & RADEON_IS_PCIE) { + memcpy_fromio(dev_priv->mm.pcie_table_backup, dev_priv->mm.pcie_table.kmap.virtual, RADEON_PCIGART_TABLE_SIZE); + } + + pci_save_state(dev->pdev); + + if (state.event == PM_EVENT_SUSPEND) { + /* Shut down the device */ + pci_disable_device(dev->pdev); + pci_set_power_state(dev->pdev, PCI_D3hot); + } + return 0; +} + +int radeon_resume(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct drm_framebuffer *fb; + int i; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return 0; + + pci_set_power_state(dev->pdev, PCI_D0); + pci_restore_state(dev->pdev); + if (pci_enable_device(dev->pdev)) + return -1; + + /* Turn on bus mastering -todo fix properly */ + radeon_enable_bm(dev_priv); + + DRM_ERROR("\n"); + /* on atom cards re init the whole card + and set the modes again */ + + if (dev_priv->is_atom_bios) { + struct atom_context *ctx = dev_priv->mode_info.atom_context; + atom_asic_init(ctx); + } else { + radeon_combios_asic_init(dev); + } + + pci_set_master(dev->pdev); + + for (i = 0; i < 8; i++) + RADEON_WRITE(RADEON_BIOS_0_SCRATCH + (i * 4), dev_priv->pmregs.bios_scratch[i]); + + /* VGA render mayhaps */ + if (dev_priv->chip_family >= CHIP_RS600) { + uint32_t tmp; + + RADEON_WRITE(AVIVO_D1VGA_CONTROL, 0); + RADEON_WRITE(AVIVO_D2VGA_CONTROL, 0); + tmp = RADEON_READ(0x300); + tmp &= ~(3 << 16); + RADEON_WRITE(0x300, tmp); + RADEON_WRITE(0x308, (1 << 8)); + RADEON_WRITE(0x310, dev_priv->fb_location); + RADEON_WRITE(0x594, 0); + } + + RADEON_WRITE(RADEON_CRTC_EXT_CNTL, dev_priv->pmregs.crtc_ext_cntl); + + radeon_static_clocks_init(dev); + + radeon_init_memory_map(dev); + + if (dev_priv->flags & RADEON_IS_PCIE) { + memcpy_toio(dev_priv->mm.pcie_table.kmap.virtual, dev_priv->mm.pcie_table_backup, RADEON_PCIGART_TABLE_SIZE); + } + + if (dev_priv->mm.ring.kmap.virtual) + memset(dev_priv->mm.ring.kmap.virtual, 0, RADEON_DEFAULT_RING_SIZE); + + if (dev_priv->mm.ring_read.kmap.virtual) + memset(dev_priv->mm.ring_read.kmap.virtual, 0, PAGE_SIZE); + + radeon_modeset_cp_resume(dev); + + /* reset swi reg */ + RADEON_WRITE(RADEON_LAST_SWI_REG, dev_priv->counter); + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS690) + RADEON_WRITE(R500_DxMODE_INT_MASK, dev_priv->r500_disp_irq_reg); + RADEON_WRITE(RADEON_GEN_INT_CNTL, dev_priv->irq_enable_reg); + + /* reset the context for userspace */ + if (dev->primary->master) { + struct drm_radeon_master_private *master_priv = dev->primary->master->driver_priv; + if (master_priv->sarea_priv) + master_priv->sarea_priv->ctx_owner = 0; + } + + /* pin the front buffers */ + list_for_each_entry(fb, &dev->mode_config.fb_kernel_list, filp_head) { + + struct radeon_framebuffer *radeon_fb = to_radeon_framebuffer(fb); + + if (!radeon_fb) + continue; + + if (!radeon_fb->obj) + continue; + + radeon_gem_object_pin(radeon_fb->obj, + PAGE_SIZE, RADEON_GEM_DOMAIN_VRAM); + } + /* blat the mode back in */ + drm_helper_resume_force_mode(dev); + + return 0; +} + +bool radeon_set_pcie_lanes(struct drm_device *dev, int lanes) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + uint32_t link_width_cntl, mask; + + /* FIXME wait for idle */ + + + switch (lanes) { + case 0: + mask = RADEON_PCIE_LC_LINK_WIDTH_X0; + break; + case 1: + mask = RADEON_PCIE_LC_LINK_WIDTH_X1; + break; + case 2: + mask = RADEON_PCIE_LC_LINK_WIDTH_X2; + break; + case 4: + mask = RADEON_PCIE_LC_LINK_WIDTH_X4; + break; + case 8: + mask = RADEON_PCIE_LC_LINK_WIDTH_X8; + break; + case 12: + mask = RADEON_PCIE_LC_LINK_WIDTH_X12; + break; + case 16: + default: + mask = RADEON_PCIE_LC_LINK_WIDTH_X16; + break; + } + + link_width_cntl = RADEON_READ_PCIE(dev_priv, RADEON_PCIE_LC_LINK_WIDTH_CNTL); + + if ((link_width_cntl & RADEON_PCIE_LC_LINK_WIDTH_RD_MASK) == + (mask << RADEON_PCIE_LC_LINK_WIDTH_RD_SHIFT)) + return true; + + link_width_cntl &= ~(RADEON_PCIE_LC_LINK_WIDTH_MASK | + RADEON_PCIE_LC_RECONFIG_NOW | + RADEON_PCIE_LC_RECONFIG_LATER | + RADEON_PCIE_LC_SHORT_RECONFIG_EN); + link_width_cntl |= mask; + RADEON_WRITE_PCIE(RADEON_PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); + RADEON_WRITE_PCIE(RADEON_PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl | RADEON_PCIE_LC_RECONFIG_NOW); + + /* wait for lane set to complete */ + link_width_cntl = RADEON_READ_PCIE(dev_priv, RADEON_PCIE_LC_LINK_WIDTH_CNTL); + while (link_width_cntl == 0xffffffff) + link_width_cntl = RADEON_READ_PCIE(dev_priv, RADEON_PCIE_LC_LINK_WIDTH_CNTL); + + if ((link_width_cntl & RADEON_PCIE_LC_LINK_WIDTH_RD_MASK) == + (mask << RADEON_PCIE_LC_LINK_WIDTH_RD_SHIFT)) + return true; + else + return false; +} + diff --git a/linux-core/radeon_reg.h b/linux-core/radeon_reg.h new file mode 100644 index 00000000..8698e861 --- /dev/null +++ b/linux-core/radeon_reg.h @@ -0,0 +1,5331 @@ +/* + * Copyright 2000 ATI Technologies Inc., Markham, Ontario, and + * VA Linux Systems Inc., Fremont, California. + * + * 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. + */ + +/* + * Authors: + * Kevin E. Martin <martin@xfree86.org> + * Rickard E. Faith <faith@valinux.com> + * Alan Hourihane <alanh@fairlite.demon.co.uk> + * + * References: + * + * !!!! FIXME !!!! + * RAGE 128 VR/ RAGE 128 GL Register Reference Manual (Technical + * Reference Manual P/N RRG-G04100-C Rev. 0.04), ATI Technologies: April + * 1999. + * + * !!!! FIXME !!!! + * RAGE 128 Software Development Manual (Technical Reference Manual P/N + * SDK-G04000 Rev. 0.01), ATI Technologies: June 1999. + * + */ + +/* !!!! FIXME !!!! NOTE: THIS FILE HAS BEEN CONVERTED FROM r128_reg.h + * AND CONTAINS REGISTERS AND REGISTER DEFINITIONS THAT ARE NOT CORRECT + * ON THE RADEON. A FULL AUDIT OF THIS CODE IS NEEDED! */ + +#ifndef _RADEON_REG_H_ +#define _RADEON_REG_H_ + +#define ATI_DATATYPE_VQ 0 +#define ATI_DATATYPE_CI4 1 +#define ATI_DATATYPE_CI8 2 +#define ATI_DATATYPE_ARGB1555 3 +#define ATI_DATATYPE_RGB565 4 +#define ATI_DATATYPE_RGB888 5 +#define ATI_DATATYPE_ARGB8888 6 +#define ATI_DATATYPE_RGB332 7 +#define ATI_DATATYPE_Y8 8 +#define ATI_DATATYPE_RGB8 9 +#define ATI_DATATYPE_CI16 10 +#define ATI_DATATYPE_VYUY_422 11 +#define ATI_DATATYPE_YVYU_422 12 +#define ATI_DATATYPE_AYUV_444 14 +#define ATI_DATATYPE_ARGB4444 15 + + /* Registers for 2D/Video/Overlay */ +#define RADEON_ADAPTER_ID 0x0f2c /* PCI */ +#define RADEON_AGP_BASE 0x0170 +#define RADEON_AGP_CNTL 0x0174 +# define RADEON_AGP_APER_SIZE_256MB (0x00 << 0) +# define RADEON_AGP_APER_SIZE_128MB (0x20 << 0) +# define RADEON_AGP_APER_SIZE_64MB (0x30 << 0) +# define RADEON_AGP_APER_SIZE_32MB (0x38 << 0) +# define RADEON_AGP_APER_SIZE_16MB (0x3c << 0) +# define RADEON_AGP_APER_SIZE_8MB (0x3e << 0) +# define RADEON_AGP_APER_SIZE_4MB (0x3f << 0) +# define RADEON_AGP_APER_SIZE_MASK (0x3f << 0) +#define RADEON_STATUS_PCI_CONFIG 0x06 +# define RADEON_CAP_LIST 0x100000 +#define RADEON_CAPABILITIES_PTR_PCI_CONFIG 0x34 /* offset in PCI config*/ +# define RADEON_CAP_PTR_MASK 0xfc /* mask off reserved bits of CAP_PTR */ +# define RADEON_CAP_ID_NULL 0x00 /* End of capability list */ +# define RADEON_CAP_ID_AGP 0x02 /* AGP capability ID */ +# define RADEON_CAP_ID_EXP 0x10 /* PCI Express */ +#define RADEON_AGP_COMMAND 0x0f60 /* PCI */ +#define RADEON_AGP_COMMAND_PCI_CONFIG 0x0060 /* offset in PCI config*/ +# define RADEON_AGP_ENABLE (1<<8) +#define RADEON_AGP_PLL_CNTL 0x000b /* PLL */ +#define RADEON_AGP_STATUS 0x0f5c /* PCI */ +# define RADEON_AGP_1X_MODE 0x01 +# define RADEON_AGP_2X_MODE 0x02 +# define RADEON_AGP_4X_MODE 0x04 +# define RADEON_AGP_FW_MODE 0x10 +# define RADEON_AGP_MODE_MASK 0x17 +# define RADEON_AGPv3_MODE 0x08 +# define RADEON_AGPv3_4X_MODE 0x01 +# define RADEON_AGPv3_8X_MODE 0x02 +#define RADEON_ATTRDR 0x03c1 /* VGA */ +#define RADEON_ATTRDW 0x03c0 /* VGA */ +#define RADEON_ATTRX 0x03c0 /* VGA */ +#define RADEON_AUX_SC_CNTL 0x1660 +# define RADEON_AUX1_SC_EN (1 << 0) +# define RADEON_AUX1_SC_MODE_OR (0 << 1) +# define RADEON_AUX1_SC_MODE_NAND (1 << 1) +# define RADEON_AUX2_SC_EN (1 << 2) +# define RADEON_AUX2_SC_MODE_OR (0 << 3) +# define RADEON_AUX2_SC_MODE_NAND (1 << 3) +# define RADEON_AUX3_SC_EN (1 << 4) +# define RADEON_AUX3_SC_MODE_OR (0 << 5) +# define RADEON_AUX3_SC_MODE_NAND (1 << 5) +#define RADEON_AUX1_SC_BOTTOM 0x1670 +#define RADEON_AUX1_SC_LEFT 0x1664 +#define RADEON_AUX1_SC_RIGHT 0x1668 +#define RADEON_AUX1_SC_TOP 0x166c +#define RADEON_AUX2_SC_BOTTOM 0x1680 +#define RADEON_AUX2_SC_LEFT 0x1674 +#define RADEON_AUX2_SC_RIGHT 0x1678 +#define RADEON_AUX2_SC_TOP 0x167c +#define RADEON_AUX3_SC_BOTTOM 0x1690 +#define RADEON_AUX3_SC_LEFT 0x1684 +#define RADEON_AUX3_SC_RIGHT 0x1688 +#define RADEON_AUX3_SC_TOP 0x168c +#define RADEON_AUX_WINDOW_HORZ_CNTL 0x02d8 +#define RADEON_AUX_WINDOW_VERT_CNTL 0x02dc + +#define RADEON_BASE_CODE 0x0f0b +#define RADEON_BIOS_0_SCRATCH 0x0010 +# define RADEON_FP_PANEL_SCALABLE (1 << 16) +# define RADEON_FP_PANEL_SCALE_EN (1 << 17) +# define RADEON_FP_CHIP_SCALE_EN (1 << 18) +# define RADEON_DRIVER_BRIGHTNESS_EN (1 << 26) +# define RADEON_DISPLAY_ROT_MASK (3 << 28) +# define RADEON_DISPLAY_ROT_00 (0 << 28) +# define RADEON_DISPLAY_ROT_90 (1 << 28) +# define RADEON_DISPLAY_ROT_180 (2 << 28) +# define RADEON_DISPLAY_ROT_270 (3 << 28) +#define RADEON_BIOS_1_SCRATCH 0x0014 +#define RADEON_BIOS_2_SCRATCH 0x0018 +#define RADEON_BIOS_3_SCRATCH 0x001c +#define RADEON_BIOS_4_SCRATCH 0x0020 +# define RADEON_CRT1_ATTACHED_MASK (3 << 0) +# define RADEON_CRT1_ATTACHED_MONO (1 << 0) +# define RADEON_CRT1_ATTACHED_COLOR (2 << 0) +# define RADEON_LCD1_ATTACHED (1 << 2) +# define RADEON_DFP1_ATTACHED (1 << 3) +# define RADEON_TV1_ATTACHED_MASK (3 << 4) +# define RADEON_TV1_ATTACHED_COMP (1 << 4) +# define RADEON_TV1_ATTACHED_SVIDEO (2 << 4) +# define RADEON_CRT2_ATTACHED_MASK (3 << 8) +# define RADEON_CRT2_ATTACHED_MONO (1 << 8) +# define RADEON_CRT2_ATTACHED_COLOR (2 << 8) +# define RADEON_DFP2_ATTACHED (1 << 11) +#define RADEON_BIOS_5_SCRATCH 0x0024 +# define RADEON_LCD1_ON (1 << 0) +# define RADEON_CRT1_ON (1 << 1) +# define RADEON_TV1_ON (1 << 2) +# define RADEON_DFP1_ON (1 << 3) +# define RADEON_CRT2_ON (1 << 5) +# define RADEON_CV1_ON (1 << 6) +# define RADEON_DFP2_ON (1 << 7) +# define RADEON_LCD1_CRTC_MASK (1 << 8) +# define RADEON_LCD1_CRTC_SHIFT 8 +# define RADEON_CRT1_CRTC_MASK (1 << 9) +# define RADEON_CRT1_CRTC_SHIFT 9 +# define RADEON_TV1_CRTC_MASK (1 << 10) +# define RADEON_TV1_CRTC_SHIFT 10 +# define RADEON_DFP1_CRTC_MASK (1 << 11) +# define RADEON_DFP1_CRTC_SHIFT 11 +# define RADEON_CRT2_CRTC_MASK (1 << 12) +# define RADEON_CRT2_CRTC_SHIFT 12 +# define RADEON_CV1_CRTC_MASK (1 << 13) +# define RADEON_CV1_CRTC_SHIFT 13 +# define RADEON_DFP2_CRTC_MASK (1 << 14) +# define RADEON_DFP2_CRTC_SHIFT 14 +#define RADEON_BIOS_6_SCRATCH 0x0028 +# define RADEON_ACC_MODE_CHANGE (1 << 2) +# define RADEON_EXT_DESKTOP_MODE (1 << 3) +# define RADEON_LCD_DPMS_ON (1 << 20) +# define RADEON_CRT_DPMS_ON (1 << 21) +# define RADEON_TV_DPMS_ON (1 << 22) +# define RADEON_DFP_DPMS_ON (1 << 23) +# define RADEON_DPMS_MASK (3 << 24) +# define RADEON_DPMS_ON (0 << 24) +# define RADEON_DPMS_STANDBY (1 << 24) +# define RADEON_DPMS_SUSPEND (2 << 24) +# define RADEON_DPMS_OFF (3 << 24) +# define RADEON_SCREEN_BLANKING (1 << 26) +# define RADEON_DRIVER_CRITICAL (1 << 27) +# define RADEON_DISPLAY_SWITCHING_DIS (1 << 30) +#define RADEON_BIOS_7_SCRATCH 0x002c +# define RADEON_SYS_HOTKEY (1 << 10) +# define RADEON_DRV_LOADED (1 << 12) +#define RADEON_BIOS_ROM 0x0f30 /* PCI */ +#define RADEON_BIST 0x0f0f /* PCI */ +#define RADEON_BRUSH_DATA0 0x1480 +#define RADEON_BRUSH_DATA1 0x1484 +#define RADEON_BRUSH_DATA10 0x14a8 +#define RADEON_BRUSH_DATA11 0x14ac +#define RADEON_BRUSH_DATA12 0x14b0 +#define RADEON_BRUSH_DATA13 0x14b4 +#define RADEON_BRUSH_DATA14 0x14b8 +#define RADEON_BRUSH_DATA15 0x14bc +#define RADEON_BRUSH_DATA16 0x14c0 +#define RADEON_BRUSH_DATA17 0x14c4 +#define RADEON_BRUSH_DATA18 0x14c8 +#define RADEON_BRUSH_DATA19 0x14cc +#define RADEON_BRUSH_DATA2 0x1488 +#define RADEON_BRUSH_DATA20 0x14d0 +#define RADEON_BRUSH_DATA21 0x14d4 +#define RADEON_BRUSH_DATA22 0x14d8 +#define RADEON_BRUSH_DATA23 0x14dc +#define RADEON_BRUSH_DATA24 0x14e0 +#define RADEON_BRUSH_DATA25 0x14e4 +#define RADEON_BRUSH_DATA26 0x14e8 +#define RADEON_BRUSH_DATA27 0x14ec +#define RADEON_BRUSH_DATA28 0x14f0 +#define RADEON_BRUSH_DATA29 0x14f4 +#define RADEON_BRUSH_DATA3 0x148c +#define RADEON_BRUSH_DATA30 0x14f8 +#define RADEON_BRUSH_DATA31 0x14fc +#define RADEON_BRUSH_DATA32 0x1500 +#define RADEON_BRUSH_DATA33 0x1504 +#define RADEON_BRUSH_DATA34 0x1508 +#define RADEON_BRUSH_DATA35 0x150c +#define RADEON_BRUSH_DATA36 0x1510 +#define RADEON_BRUSH_DATA37 0x1514 +#define RADEON_BRUSH_DATA38 0x1518 +#define RADEON_BRUSH_DATA39 0x151c +#define RADEON_BRUSH_DATA4 0x1490 +#define RADEON_BRUSH_DATA40 0x1520 +#define RADEON_BRUSH_DATA41 0x1524 +#define RADEON_BRUSH_DATA42 0x1528 +#define RADEON_BRUSH_DATA43 0x152c +#define RADEON_BRUSH_DATA44 0x1530 +#define RADEON_BRUSH_DATA45 0x1534 +#define RADEON_BRUSH_DATA46 0x1538 +#define RADEON_BRUSH_DATA47 0x153c +#define RADEON_BRUSH_DATA48 0x1540 +#define RADEON_BRUSH_DATA49 0x1544 +#define RADEON_BRUSH_DATA5 0x1494 +#define RADEON_BRUSH_DATA50 0x1548 +#define RADEON_BRUSH_DATA51 0x154c +#define RADEON_BRUSH_DATA52 0x1550 +#define RADEON_BRUSH_DATA53 0x1554 +#define RADEON_BRUSH_DATA54 0x1558 +#define RADEON_BRUSH_DATA55 0x155c +#define RADEON_BRUSH_DATA56 0x1560 +#define RADEON_BRUSH_DATA57 0x1564 +#define RADEON_BRUSH_DATA58 0x1568 +#define RADEON_BRUSH_DATA59 0x156c +#define RADEON_BRUSH_DATA6 0x1498 +#define RADEON_BRUSH_DATA60 0x1570 +#define RADEON_BRUSH_DATA61 0x1574 +#define RADEON_BRUSH_DATA62 0x1578 +#define RADEON_BRUSH_DATA63 0x157c +#define RADEON_BRUSH_DATA7 0x149c +#define RADEON_BRUSH_DATA8 0x14a0 +#define RADEON_BRUSH_DATA9 0x14a4 +#define RADEON_BRUSH_SCALE 0x1470 +#define RADEON_BRUSH_Y_X 0x1474 +#define RADEON_BUS_CNTL 0x0030 +# define RADEON_BUS_MASTER_DIS (1 << 6) +# define RADEON_BUS_BIOS_DIS_ROM (1 << 12) +# define RADEON_BUS_RD_DISCARD_EN (1 << 24) +# define RADEON_BUS_RD_ABORT_EN (1 << 25) +# define RADEON_BUS_MSTR_DISCONNECT_EN (1 << 28) +# define RADEON_BUS_WRT_BURST (1 << 29) +# define RADEON_BUS_READ_BURST (1 << 30) +#define RADEON_BUS_CNTL1 0x0034 +# define RADEON_BUS_WAIT_ON_LOCK_EN (1 << 4) + +//#define RADEON_PCIE_INDEX 0x0030 +//#define RADEON_PCIE_DATA 0x0034 +#define RADEON_PCIE_LC_LINK_WIDTH_CNTL 0xa2 /* PCIE */ +# define RADEON_PCIE_LC_LINK_WIDTH_SHIFT 0 +# define RADEON_PCIE_LC_LINK_WIDTH_MASK 0x7 +# define RADEON_PCIE_LC_LINK_WIDTH_X0 0 +# define RADEON_PCIE_LC_LINK_WIDTH_X1 1 +# define RADEON_PCIE_LC_LINK_WIDTH_X2 2 +# define RADEON_PCIE_LC_LINK_WIDTH_X4 3 +# define RADEON_PCIE_LC_LINK_WIDTH_X8 4 +# define RADEON_PCIE_LC_LINK_WIDTH_X12 5 +# define RADEON_PCIE_LC_LINK_WIDTH_X16 6 +# define RADEON_PCIE_LC_LINK_WIDTH_RD_SHIFT 4 +# define RADEON_PCIE_LC_LINK_WIDTH_RD_MASK 0x70 +# define RADEON_PCIE_LC_RECONFIG_NOW (1 << 8) +# define RADEON_PCIE_LC_RECONFIG_LATER (1 << 9) +# define RADEON_PCIE_LC_SHORT_RECONFIG_EN (1 << 10) + +#define RADEON_CACHE_CNTL 0x1724 +#define RADEON_CACHE_LINE 0x0f0c /* PCI */ +#define RADEON_CAPABILITIES_ID 0x0f50 /* PCI */ +#define RADEON_CAPABILITIES_PTR 0x0f34 /* PCI */ +#define RADEON_CLK_PIN_CNTL 0x0001 /* PLL */ +# define RADEON_SCLK_DYN_START_CNTL (1 << 15) +#define RADEON_CLOCK_CNTL_DATA 0x000c +#define RADEON_CLOCK_CNTL_INDEX 0x0008 +# define RADEON_PLL_WR_EN (1 << 7) +# define RADEON_PLL_DIV_SEL (3 << 8) +# define RADEON_PLL2_DIV_SEL_MASK ~(3 << 8) +#define RADEON_CLK_PWRMGT_CNTL 0x0014 +# define RADEON_ENGIN_DYNCLK_MODE (1 << 12) +# define RADEON_ACTIVE_HILO_LAT_MASK (3 << 13) +# define RADEON_ACTIVE_HILO_LAT_SHIFT 13 +# define RADEON_DISP_DYN_STOP_LAT_MASK (1 << 12) +# define RADEON_MC_BUSY (1 << 16) +# define RADEON_DLL_READY (1 << 19) +# define RADEON_CG_NO1_DEBUG_0 (1 << 24) +# define RADEON_CG_NO1_DEBUG_MASK (0x1f << 24) +# define RADEON_DYN_STOP_MODE_MASK (7 << 21) +# define RADEON_TVPLL_PWRMGT_OFF (1 << 30) +# define RADEON_TVCLK_TURNOFF (1 << 31) +#define RADEON_PLL_PWRMGT_CNTL 0x0015 +# define RADEON_TCL_BYPASS_DISABLE (1 << 20) +#define RADEON_CLR_CMP_CLR_3D 0x1a24 +#define RADEON_CLR_CMP_CLR_DST 0x15c8 +#define RADEON_CLR_CMP_CLR_SRC 0x15c4 +#define RADEON_CLR_CMP_CNTL 0x15c0 +# define RADEON_SRC_CMP_EQ_COLOR (4 << 0) +# define RADEON_SRC_CMP_NEQ_COLOR (5 << 0) +# define RADEON_CLR_CMP_SRC_SOURCE (1 << 24) +#define RADEON_CLR_CMP_MASK 0x15cc +# define RADEON_CLR_CMP_MSK 0xffffffff +#define RADEON_CLR_CMP_MASK_3D 0x1A28 +#define RADEON_COMMAND 0x0f04 /* PCI */ +#define RADEON_COMPOSITE_SHADOW_ID 0x1a0c +#define RADEON_CONFIG_APER_0_BASE 0x0100 +#define RADEON_CONFIG_APER_1_BASE 0x0104 +#define RADEON_CONFIG_APER_SIZE 0x0108 +#define RADEON_CONFIG_BONDS 0x00e8 +#define RADEON_CONFIG_CNTL 0x00e0 +# define RADEON_CFG_ATI_REV_A11 (0 << 16) +# define RADEON_CFG_ATI_REV_A12 (1 << 16) +# define RADEON_CFG_ATI_REV_A13 (2 << 16) +# define RADEON_CFG_ATI_REV_ID_MASK (0xf << 16) +#define RADEON_CONFIG_MEMSIZE 0x00f8 +#define RADEON_CONFIG_MEMSIZE_EMBEDDED 0x0114 +#define RADEON_CONFIG_REG_1_BASE 0x010c +#define RADEON_CONFIG_REG_APER_SIZE 0x0110 +#define RADEON_CONFIG_XSTRAP 0x00e4 +#define RADEON_CONSTANT_COLOR_C 0x1d34 +# define RADEON_CONSTANT_COLOR_MASK 0x00ffffff +# define RADEON_CONSTANT_COLOR_ONE 0x00ffffff +# define RADEON_CONSTANT_COLOR_ZERO 0x00000000 +#define RADEON_CRC_CMDFIFO_ADDR 0x0740 +#define RADEON_CRC_CMDFIFO_DOUT 0x0744 +#define RADEON_GRPH_BUFFER_CNTL 0x02f0 +# define RADEON_GRPH_START_REQ_MASK (0x7f) +# define RADEON_GRPH_START_REQ_SHIFT 0 +# define RADEON_GRPH_STOP_REQ_MASK (0x7f<<8) +# define RADEON_GRPH_STOP_REQ_SHIFT 8 +# define RADEON_GRPH_CRITICAL_POINT_MASK (0x7f<<16) +# define RADEON_GRPH_CRITICAL_POINT_SHIFT 16 +# define RADEON_GRPH_CRITICAL_CNTL (1<<28) +# define RADEON_GRPH_BUFFER_SIZE (1<<29) +# define RADEON_GRPH_CRITICAL_AT_SOF (1<<30) +# define RADEON_GRPH_STOP_CNTL (1<<31) +#define RADEON_GRPH2_BUFFER_CNTL 0x03f0 +# define RADEON_GRPH2_START_REQ_MASK (0x7f) +# define RADEON_GRPH2_START_REQ_SHIFT 0 +# define RADEON_GRPH2_STOP_REQ_MASK (0x7f<<8) +# define RADEON_GRPH2_STOP_REQ_SHIFT 8 +# define RADEON_GRPH2_CRITICAL_POINT_MASK (0x7f<<16) +# define RADEON_GRPH2_CRITICAL_POINT_SHIFT 16 +# define RADEON_GRPH2_CRITICAL_CNTL (1<<28) +# define RADEON_GRPH2_BUFFER_SIZE (1<<29) +# define RADEON_GRPH2_CRITICAL_AT_SOF (1<<30) +# define RADEON_GRPH2_STOP_CNTL (1<<31) +#define RADEON_CRTC_CRNT_FRAME 0x0214 +#define RADEON_CRTC_EXT_CNTL 0x0054 +# define RADEON_CRTC_VGA_XOVERSCAN (1 << 0) +# define RADEON_VGA_ATI_LINEAR (1 << 3) +# define RADEON_XCRT_CNT_EN (1 << 6) +# define RADEON_CRTC_HSYNC_DIS (1 << 8) +# define RADEON_CRTC_VSYNC_DIS (1 << 9) +# define RADEON_CRTC_DISPLAY_DIS (1 << 10) +# define RADEON_CRTC_SYNC_TRISTAT (1 << 11) +# define RADEON_CRTC_CRT_ON (1 << 15) +#define RADEON_CRTC_EXT_CNTL_DPMS_BYTE 0x0055 +# define RADEON_CRTC_HSYNC_DIS_BYTE (1 << 0) +# define RADEON_CRTC_VSYNC_DIS_BYTE (1 << 1) +# define RADEON_CRTC_DISPLAY_DIS_BYTE (1 << 2) +#define RADEON_CRTC_GEN_CNTL 0x0050 +# define RADEON_CRTC_DBL_SCAN_EN (1 << 0) +# define RADEON_CRTC_INTERLACE_EN (1 << 1) +# define RADEON_CRTC_CSYNC_EN (1 << 4) +# define RADEON_CRTC_ICON_EN (1 << 15) +# define RADEON_CRTC_CUR_EN (1 << 16) +# define RADEON_CRTC_CUR_MODE_MASK (7 << 20) +# define RADEON_CRTC_CUR_MODE_SHIFT 20 +# define RADEON_CRTC_CUR_MODE_MONO 0 +# define RADEON_CRTC_CUR_MODE_24BPP 2 +# define RADEON_CRTC_EXT_DISP_EN (1 << 24) +# define RADEON_CRTC_EN (1 << 25) +# define RADEON_CRTC_DISP_REQ_EN_B (1 << 26) +#define RADEON_CRTC2_GEN_CNTL 0x03f8 +# define RADEON_CRTC2_DBL_SCAN_EN (1 << 0) +# define RADEON_CRTC2_INTERLACE_EN (1 << 1) +# define RADEON_CRTC2_SYNC_TRISTAT (1 << 4) +# define RADEON_CRTC2_HSYNC_TRISTAT (1 << 5) +# define RADEON_CRTC2_VSYNC_TRISTAT (1 << 6) +# define RADEON_CRTC2_CRT2_ON (1 << 7) +# define RADEON_CRTC2_PIX_WIDTH_SHIFT 8 +# define RADEON_CRTC2_PIX_WIDTH_MASK (0xf << 8) +# define RADEON_CRTC2_ICON_EN (1 << 15) +# define RADEON_CRTC2_CUR_EN (1 << 16) +# define RADEON_CRTC2_CUR_MODE_MASK (7 << 20) +# define RADEON_CRTC2_DISP_DIS (1 << 23) +# define RADEON_CRTC2_EN (1 << 25) +# define RADEON_CRTC2_DISP_REQ_EN_B (1 << 26) +# define RADEON_CRTC2_CSYNC_EN (1 << 27) +# define RADEON_CRTC2_HSYNC_DIS (1 << 28) +# define RADEON_CRTC2_VSYNC_DIS (1 << 29) +#define RADEON_CRTC_MORE_CNTL 0x27c +# define RADEON_CRTC_AUTO_HORZ_CENTER_EN (1<<2) +# define RADEON_CRTC_AUTO_VERT_CENTER_EN (1<<3) +# define RADEON_CRTC_H_CUTOFF_ACTIVE_EN (1<<4) +# define RADEON_CRTC_V_CUTOFF_ACTIVE_EN (1<<5) +#define RADEON_CRTC_GUI_TRIG_VLINE 0x0218 +#define RADEON_CRTC_H_SYNC_STRT_WID 0x0204 +# define RADEON_CRTC_H_SYNC_STRT_PIX (0x07 << 0) +# define RADEON_CRTC_H_SYNC_STRT_CHAR (0x3ff << 3) +# define RADEON_CRTC_H_SYNC_STRT_CHAR_SHIFT 3 +# define RADEON_CRTC_H_SYNC_WID (0x3f << 16) +# define RADEON_CRTC_H_SYNC_WID_SHIFT 16 +# define RADEON_CRTC_H_SYNC_POL (1 << 23) +#define RADEON_CRTC2_H_SYNC_STRT_WID 0x0304 +# define RADEON_CRTC2_H_SYNC_STRT_PIX (0x07 << 0) +# define RADEON_CRTC2_H_SYNC_STRT_CHAR (0x3ff << 3) +# define RADEON_CRTC2_H_SYNC_STRT_CHAR_SHIFT 3 +# define RADEON_CRTC2_H_SYNC_WID (0x3f << 16) +# define RADEON_CRTC2_H_SYNC_WID_SHIFT 16 +# define RADEON_CRTC2_H_SYNC_POL (1 << 23) +#define RADEON_CRTC_H_TOTAL_DISP 0x0200 +# define RADEON_CRTC_H_TOTAL (0x03ff << 0) +# define RADEON_CRTC_H_TOTAL_SHIFT 0 +# define RADEON_CRTC_H_DISP (0x01ff << 16) +# define RADEON_CRTC_H_DISP_SHIFT 16 +#define RADEON_CRTC2_H_TOTAL_DISP 0x0300 +# define RADEON_CRTC2_H_TOTAL (0x03ff << 0) +# define RADEON_CRTC2_H_TOTAL_SHIFT 0 +# define RADEON_CRTC2_H_DISP (0x01ff << 16) +# define RADEON_CRTC2_H_DISP_SHIFT 16 + +#define RADEON_CRTC_OFFSET_RIGHT 0x0220 +#define RADEON_CRTC_OFFSET 0x0224 +# define RADEON_CRTC_OFFSET__GUI_TRIG_OFFSET (1<<30) +# define RADEON_CRTC_OFFSET__OFFSET_LOCK (1<<31) + +#define RADEON_CRTC2_OFFSET 0x0324 +# define RADEON_CRTC2_OFFSET__GUI_TRIG_OFFSET (1<<30) +# define RADEON_CRTC2_OFFSET__OFFSET_LOCK (1<<31) +#define RADEON_CRTC_OFFSET_CNTL 0x0228 +# define RADEON_CRTC_TILE_LINE_SHIFT 0 +# define RADEON_CRTC_TILE_LINE_RIGHT_SHIFT 4 +# define R300_CRTC_X_Y_MODE_EN_RIGHT (1 << 6) +# define R300_CRTC_MICRO_TILE_BUFFER_RIGHT_MASK (3 << 7) +# define R300_CRTC_MICRO_TILE_BUFFER_RIGHT_AUTO (0 << 7) +# define R300_CRTC_MICRO_TILE_BUFFER_RIGHT_SINGLE (1 << 7) +# define R300_CRTC_MICRO_TILE_BUFFER_RIGHT_DOUBLE (2 << 7) +# define R300_CRTC_MICRO_TILE_BUFFER_RIGHT_DIS (3 << 7) +# define R300_CRTC_X_Y_MODE_EN (1 << 9) +# define R300_CRTC_MICRO_TILE_BUFFER_MASK (3 << 10) +# define R300_CRTC_MICRO_TILE_BUFFER_AUTO (0 << 10) +# define R300_CRTC_MICRO_TILE_BUFFER_SINGLE (1 << 10) +# define R300_CRTC_MICRO_TILE_BUFFER_DOUBLE (2 << 10) +# define R300_CRTC_MICRO_TILE_BUFFER_DIS (3 << 10) +# define R300_CRTC_MICRO_TILE_EN_RIGHT (1 << 12) +# define R300_CRTC_MICRO_TILE_EN (1 << 13) +# define R300_CRTC_MACRO_TILE_EN_RIGHT (1 << 14) +# define R300_CRTC_MACRO_TILE_EN (1 << 15) +# define RADEON_CRTC_TILE_EN_RIGHT (1 << 14) +# define RADEON_CRTC_TILE_EN (1 << 15) +# define RADEON_CRTC_OFFSET_FLIP_CNTL (1 << 16) +# define RADEON_CRTC_STEREO_OFFSET_EN (1 << 17) + +#define R300_CRTC_TILE_X0_Y0 0x0350 +#define R300_CRTC2_TILE_X0_Y0 0x0358 + +#define RADEON_CRTC2_OFFSET_CNTL 0x0328 +# define RADEON_CRTC2_OFFSET_FLIP_CNTL (1 << 16) +# define RADEON_CRTC2_TILE_EN (1 << 15) +#define RADEON_CRTC_PITCH 0x022c +# define RADEON_CRTC_PITCH__SHIFT 0 +# define RADEON_CRTC_PITCH__RIGHT_SHIFT 16 + +#define RADEON_CRTC2_PITCH 0x032c +#define RADEON_CRTC_STATUS 0x005c +# define RADEON_CRTC_VBLANK_SAVE (1 << 1) +# define RADEON_CRTC_VBLANK_SAVE_CLEAR (1 << 1) +#define RADEON_CRTC2_STATUS 0x03fc +# define RADEON_CRTC2_VBLANK_SAVE (1 << 1) +# define RADEON_CRTC2_VBLANK_SAVE_CLEAR (1 << 1) +#define RADEON_CRTC_V_SYNC_STRT_WID 0x020c +# define RADEON_CRTC_V_SYNC_STRT (0x7ff << 0) +# define RADEON_CRTC_V_SYNC_STRT_SHIFT 0 +# define RADEON_CRTC_V_SYNC_WID (0x1f << 16) +# define RADEON_CRTC_V_SYNC_WID_SHIFT 16 +# define RADEON_CRTC_V_SYNC_POL (1 << 23) +#define RADEON_CRTC2_V_SYNC_STRT_WID 0x030c +# define RADEON_CRTC2_V_SYNC_STRT (0x7ff << 0) +# define RADEON_CRTC2_V_SYNC_STRT_SHIFT 0 +# define RADEON_CRTC2_V_SYNC_WID (0x1f << 16) +# define RADEON_CRTC2_V_SYNC_WID_SHIFT 16 +# define RADEON_CRTC2_V_SYNC_POL (1 << 23) +#define RADEON_CRTC_V_TOTAL_DISP 0x0208 +# define RADEON_CRTC_V_TOTAL (0x07ff << 0) +# define RADEON_CRTC_V_TOTAL_SHIFT 0 +# define RADEON_CRTC_V_DISP (0x07ff << 16) +# define RADEON_CRTC_V_DISP_SHIFT 16 +#define RADEON_CRTC2_V_TOTAL_DISP 0x0308 +# define RADEON_CRTC2_V_TOTAL (0x07ff << 0) +# define RADEON_CRTC2_V_TOTAL_SHIFT 0 +# define RADEON_CRTC2_V_DISP (0x07ff << 16) +# define RADEON_CRTC2_V_DISP_SHIFT 16 +#define RADEON_CRTC_VLINE_CRNT_VLINE 0x0210 +# define RADEON_CRTC_CRNT_VLINE_MASK (0x7ff << 16) +#define RADEON_CRTC2_CRNT_FRAME 0x0314 +#define RADEON_CRTC2_GUI_TRIG_VLINE 0x0318 +#define RADEON_CRTC2_STATUS 0x03fc +#define RADEON_CRTC2_VLINE_CRNT_VLINE 0x0310 +#define RADEON_CRTC8_DATA 0x03d5 /* VGA, 0x3b5 */ +#define RADEON_CRTC8_IDX 0x03d4 /* VGA, 0x3b4 */ +#define RADEON_CUR_CLR0 0x026c +#define RADEON_CUR_CLR1 0x0270 +#define RADEON_CUR_HORZ_VERT_OFF 0x0268 +#define RADEON_CUR_HORZ_VERT_POSN 0x0264 +#define RADEON_CUR_OFFSET 0x0260 +# define RADEON_CUR_LOCK (1 << 31) +#define RADEON_CUR2_CLR0 0x036c +#define RADEON_CUR2_CLR1 0x0370 +#define RADEON_CUR2_HORZ_VERT_OFF 0x0368 +#define RADEON_CUR2_HORZ_VERT_POSN 0x0364 +#define RADEON_CUR2_OFFSET 0x0360 +# define RADEON_CUR2_LOCK (1 << 31) + +#define RADEON_DAC_CNTL 0x0058 +# define RADEON_DAC_RANGE_CNTL (3 << 0) +# define RADEON_DAC_RANGE_CNTL_PS2 (2 << 0) +# define RADEON_DAC_RANGE_CNTL_MASK 0x03 +# define RADEON_DAC_BLANKING (1 << 2) +# define RADEON_DAC_CMP_EN (1 << 3) +# define RADEON_DAC_CMP_OUTPUT (1 << 7) +# define RADEON_DAC_8BIT_EN (1 << 8) +# define RADEON_DAC_TVO_EN (1 << 10) +# define RADEON_DAC_VGA_ADR_EN (1 << 13) +# define RADEON_DAC_PDWN (1 << 15) +# define RADEON_DAC_MASK_ALL (0xff << 24) +#define RADEON_DAC_CNTL2 0x007c +# define RADEON_DAC2_TV_CLK_SEL (0 << 1) +# define RADEON_DAC2_DAC_CLK_SEL (1 << 0) +# define RADEON_DAC2_DAC2_CLK_SEL (1 << 1) +# define RADEON_DAC2_PALETTE_ACC_CTL (1 << 5) +# define RADEON_DAC2_CMP_EN (1 << 7) +# define RADEON_DAC2_CMP_OUT_R (1 << 8) +# define RADEON_DAC2_CMP_OUT_G (1 << 9) +# define RADEON_DAC2_CMP_OUT_B (1 << 10) +# define RADEON_DAC2_CMP_OUTPUT (1 << 11) +#define RADEON_DAC_EXT_CNTL 0x0280 +# define RADEON_DAC2_FORCE_BLANK_OFF_EN (1 << 0) +# define RADEON_DAC2_FORCE_DATA_EN (1 << 1) +# define RADEON_DAC_FORCE_BLANK_OFF_EN (1 << 4) +# define RADEON_DAC_FORCE_DATA_EN (1 << 5) +# define RADEON_DAC_FORCE_DATA_SEL_MASK (3 << 6) +# define RADEON_DAC_FORCE_DATA_SEL_R (0 << 6) +# define RADEON_DAC_FORCE_DATA_SEL_G (1 << 6) +# define RADEON_DAC_FORCE_DATA_SEL_B (2 << 6) +# define RADEON_DAC_FORCE_DATA_SEL_RGB (3 << 6) +# define RADEON_DAC_FORCE_DATA_MASK 0x0003ff00 +# define RADEON_DAC_FORCE_DATA_SHIFT 8 +#define RADEON_DAC_MACRO_CNTL 0x0d04 +# define RADEON_DAC_PDWN_R (1 << 16) +# define RADEON_DAC_PDWN_G (1 << 17) +# define RADEON_DAC_PDWN_B (1 << 18) +#define RADEON_DISP_PWR_MAN 0x0d08 +# define RADEON_DISP_PWR_MAN_D3_CRTC_EN (1 << 0) +# define RADEON_DISP_PWR_MAN_D3_CRTC2_EN (1 << 4) +# define RADEON_DISP_PWR_MAN_DPMS_ON (0 << 8) +# define RADEON_DISP_PWR_MAN_DPMS_STANDBY (1 << 8) +# define RADEON_DISP_PWR_MAN_DPMS_SUSPEND (2 << 8) +# define RADEON_DISP_PWR_MAN_DPMS_OFF (3 << 8) +# define RADEON_DISP_D3_RST (1 << 16) +# define RADEON_DISP_D3_REG_RST (1 << 17) +# define RADEON_DISP_D3_GRPH_RST (1 << 18) +# define RADEON_DISP_D3_SUBPIC_RST (1 << 19) +# define RADEON_DISP_D3_OV0_RST (1 << 20) +# define RADEON_DISP_D1D2_GRPH_RST (1 << 21) +# define RADEON_DISP_D1D2_SUBPIC_RST (1 << 22) +# define RADEON_DISP_D1D2_OV0_RST (1 << 23) +# define RADEON_DIG_TMDS_ENABLE_RST (1 << 24) +# define RADEON_TV_ENABLE_RST (1 << 25) +# define RADEON_AUTO_PWRUP_EN (1 << 26) +#define RADEON_TV_DAC_CNTL 0x088c +# define RADEON_TV_DAC_NBLANK (1 << 0) +# define RADEON_TV_DAC_NHOLD (1 << 1) +# define RADEON_TV_DAC_PEDESTAL (1 << 2) +# define RADEON_TV_MONITOR_DETECT_EN (1 << 4) +# define RADEON_TV_DAC_CMPOUT (1 << 5) +# define RADEON_TV_DAC_STD_MASK (3 << 8) +# define RADEON_TV_DAC_STD_PAL (0 << 8) +# define RADEON_TV_DAC_STD_NTSC (1 << 8) +# define RADEON_TV_DAC_STD_PS2 (2 << 8) +# define RADEON_TV_DAC_STD_RS343 (3 << 8) +# define RADEON_TV_DAC_BGSLEEP (1 << 6) +# define RADEON_TV_DAC_BGADJ_MASK (0xf << 16) +# define RADEON_TV_DAC_BGADJ_SHIFT 16 +# define RADEON_TV_DAC_DACADJ_MASK (0xf << 20) +# define RADEON_TV_DAC_DACADJ_SHIFT 20 +# define RADEON_TV_DAC_RDACPD (1 << 24) +# define RADEON_TV_DAC_GDACPD (1 << 25) +# define RADEON_TV_DAC_BDACPD (1 << 26) +# define RADEON_TV_DAC_RDACDET (1 << 29) +# define RADEON_TV_DAC_GDACDET (1 << 30) +# define RADEON_TV_DAC_BDACDET (1 << 31) +# define R420_TV_DAC_DACADJ_MASK (0x1f << 20) +# define R420_TV_DAC_RDACPD (1 << 25) +# define R420_TV_DAC_GDACPD (1 << 26) +# define R420_TV_DAC_BDACPD (1 << 27) +# define R420_TV_DAC_TVENABLE (1 << 28) +#define RADEON_DISP_HW_DEBUG 0x0d14 +# define RADEON_CRT2_DISP1_SEL (1 << 5) +#define RADEON_DISP_OUTPUT_CNTL 0x0d64 +# define RADEON_DISP_DAC_SOURCE_MASK 0x03 +# define RADEON_DISP_DAC2_SOURCE_MASK 0x0c +# define RADEON_DISP_DAC_SOURCE_CRTC2 0x01 +# define RADEON_DISP_DAC_SOURCE_RMX 0x02 +# define RADEON_DISP_DAC_SOURCE_LTU 0x03 +# define RADEON_DISP_DAC2_SOURCE_CRTC2 0x04 +# define RADEON_DISP_TVDAC_SOURCE_MASK (0x03 << 2) +# define RADEON_DISP_TVDAC_SOURCE_CRTC 0x0 +# define RADEON_DISP_TVDAC_SOURCE_CRTC2 (0x01 << 2) +# define RADEON_DISP_TVDAC_SOURCE_RMX (0x02 << 2) +# define RADEON_DISP_TVDAC_SOURCE_LTU (0x03 << 2) +# define RADEON_DISP_TRANS_MATRIX_MASK (0x03 << 4) +# define RADEON_DISP_TRANS_MATRIX_ALPHA_MSB (0x00 << 4) +# define RADEON_DISP_TRANS_MATRIX_GRAPHICS (0x01 << 4) +# define RADEON_DISP_TRANS_MATRIX_VIDEO (0x02 << 4) +# define RADEON_DISP_TV_SOURCE_CRTC (1 << 16) /* crtc1 or crtc2 */ +# define RADEON_DISP_TV_SOURCE_LTU (0 << 16) /* linear transform unit */ +#define RADEON_DISP_TV_OUT_CNTL 0x0d6c +# define RADEON_DISP_TV_PATH_SRC_CRTC2 (1 << 16) +# define RADEON_DISP_TV_PATH_SRC_CRTC1 (0 << 16) +#define RADEON_DAC_CRC_SIG 0x02cc +#define RADEON_DAC_DATA 0x03c9 /* VGA */ +#define RADEON_DAC_MASK 0x03c6 /* VGA */ +#define RADEON_DAC_R_INDEX 0x03c7 /* VGA */ +#define RADEON_DAC_W_INDEX 0x03c8 /* VGA */ +#define RADEON_DDA_CONFIG 0x02e0 +#define RADEON_DDA_ON_OFF 0x02e4 +#define RADEON_DEFAULT_OFFSET 0x16e0 +#define RADEON_DEFAULT_PITCH 0x16e4 +#define RADEON_DEFAULT_SC_BOTTOM_RIGHT 0x16e8 +# define RADEON_DEFAULT_SC_RIGHT_MAX (0x1fff << 0) +# define RADEON_DEFAULT_SC_BOTTOM_MAX (0x1fff << 16) +#define RADEON_DESTINATION_3D_CLR_CMP_VAL 0x1820 +#define RADEON_DESTINATION_3D_CLR_CMP_MSK 0x1824 +#define RADEON_DEVICE_ID 0x0f02 /* PCI */ +#define RADEON_DISP_MISC_CNTL 0x0d00 +# define RADEON_SOFT_RESET_GRPH_PP (1 << 0) +#define RADEON_DISP_MERGE_CNTL 0x0d60 +# define RADEON_DISP_ALPHA_MODE_MASK 0x03 +# define RADEON_DISP_ALPHA_MODE_KEY 0 +# define RADEON_DISP_ALPHA_MODE_PER_PIXEL 1 +# define RADEON_DISP_ALPHA_MODE_GLOBAL 2 +# define RADEON_DISP_RGB_OFFSET_EN (1 << 8) +# define RADEON_DISP_GRPH_ALPHA_MASK (0xff << 16) +# define RADEON_DISP_OV0_ALPHA_MASK (0xff << 24) +# define RADEON_DISP_LIN_TRANS_BYPASS (0x01 << 9) +#define RADEON_DISP2_MERGE_CNTL 0x0d68 +# define RADEON_DISP2_RGB_OFFSET_EN (1 << 8) +#define RADEON_DISP_LIN_TRANS_GRPH_A 0x0d80 +#define RADEON_DISP_LIN_TRANS_GRPH_B 0x0d84 +#define RADEON_DISP_LIN_TRANS_GRPH_C 0x0d88 +#define RADEON_DISP_LIN_TRANS_GRPH_D 0x0d8c +#define RADEON_DISP_LIN_TRANS_GRPH_E 0x0d90 +#define RADEON_DISP_LIN_TRANS_GRPH_F 0x0d98 +#define RADEON_DP_BRUSH_BKGD_CLR 0x1478 +#define RADEON_DP_BRUSH_FRGD_CLR 0x147c +#define RADEON_DP_CNTL 0x16c0 +# define RADEON_DST_X_LEFT_TO_RIGHT (1 << 0) +# define RADEON_DST_Y_TOP_TO_BOTTOM (1 << 1) +# define RADEON_DP_DST_TILE_LINEAR (0 << 3) +# define RADEON_DP_DST_TILE_MACRO (1 << 3) +# define RADEON_DP_DST_TILE_MICRO (2 << 3) +# define RADEON_DP_DST_TILE_BOTH (3 << 3) +#define RADEON_DP_CNTL_XDIR_YDIR_YMAJOR 0x16d0 +# define RADEON_DST_Y_MAJOR (1 << 2) +# define RADEON_DST_Y_DIR_TOP_TO_BOTTOM (1 << 15) +# define RADEON_DST_X_DIR_LEFT_TO_RIGHT (1 << 31) +#define RADEON_DP_DATATYPE 0x16c4 +# define RADEON_HOST_BIG_ENDIAN_EN (1 << 29) +#define RADEON_DP_GUI_MASTER_CNTL 0x146c +# define RADEON_GMC_SRC_PITCH_OFFSET_CNTL (1 << 0) +# define RADEON_GMC_DST_PITCH_OFFSET_CNTL (1 << 1) +# define RADEON_GMC_SRC_CLIPPING (1 << 2) +# define RADEON_GMC_DST_CLIPPING (1 << 3) +# define RADEON_GMC_BRUSH_DATATYPE_MASK (0x0f << 4) +# define RADEON_GMC_BRUSH_8X8_MONO_FG_BG (0 << 4) +# define RADEON_GMC_BRUSH_8X8_MONO_FG_LA (1 << 4) +# define RADEON_GMC_BRUSH_1X8_MONO_FG_BG (4 << 4) +# define RADEON_GMC_BRUSH_1X8_MONO_FG_LA (5 << 4) +# define RADEON_GMC_BRUSH_32x1_MONO_FG_BG (6 << 4) +# define RADEON_GMC_BRUSH_32x1_MONO_FG_LA (7 << 4) +# define RADEON_GMC_BRUSH_32x32_MONO_FG_BG (8 << 4) +# define RADEON_GMC_BRUSH_32x32_MONO_FG_LA (9 << 4) +# define RADEON_GMC_BRUSH_8x8_COLOR (10 << 4) +# define RADEON_GMC_BRUSH_1X8_COLOR (12 << 4) +# define RADEON_GMC_BRUSH_SOLID_COLOR (13 << 4) +# define RADEON_GMC_BRUSH_NONE (15 << 4) +# define RADEON_GMC_DST_8BPP_CI (2 << 8) +# define RADEON_GMC_DST_15BPP (3 << 8) +# define RADEON_GMC_DST_16BPP (4 << 8) +# define RADEON_GMC_DST_24BPP (5 << 8) +# define RADEON_GMC_DST_32BPP (6 << 8) +# define RADEON_GMC_DST_8BPP_RGB (7 << 8) +# define RADEON_GMC_DST_Y8 (8 << 8) +# define RADEON_GMC_DST_RGB8 (9 << 8) +# define RADEON_GMC_DST_VYUY (11 << 8) +# define RADEON_GMC_DST_YVYU (12 << 8) +# define RADEON_GMC_DST_AYUV444 (14 << 8) +# define RADEON_GMC_DST_ARGB4444 (15 << 8) +# define RADEON_GMC_DST_DATATYPE_MASK (0x0f << 8) +# define RADEON_GMC_DST_DATATYPE_SHIFT 8 +# define RADEON_GMC_SRC_DATATYPE_MASK (3 << 12) +# define RADEON_GMC_SRC_DATATYPE_MONO_FG_BG (0 << 12) +# define RADEON_GMC_SRC_DATATYPE_MONO_FG_LA (1 << 12) +# define RADEON_GMC_SRC_DATATYPE_COLOR (3 << 12) +# define RADEON_GMC_BYTE_PIX_ORDER (1 << 14) +# define RADEON_GMC_BYTE_MSB_TO_LSB (0 << 14) +# define RADEON_GMC_BYTE_LSB_TO_MSB (1 << 14) +# define RADEON_GMC_CONVERSION_TEMP (1 << 15) +# define RADEON_GMC_CONVERSION_TEMP_6500 (0 << 15) +# define RADEON_GMC_CONVERSION_TEMP_9300 (1 << 15) +# define RADEON_GMC_ROP3_MASK (0xff << 16) +# define RADEON_DP_SRC_SOURCE_MASK (7 << 24) +# define RADEON_DP_SRC_SOURCE_MEMORY (2 << 24) +# define RADEON_DP_SRC_SOURCE_HOST_DATA (3 << 24) +# define RADEON_GMC_3D_FCN_EN (1 << 27) +# define RADEON_GMC_CLR_CMP_CNTL_DIS (1 << 28) +# define RADEON_GMC_AUX_CLIP_DIS (1 << 29) +# define RADEON_GMC_WR_MSK_DIS (1 << 30) +# define RADEON_GMC_LD_BRUSH_Y_X (1 << 31) +# define RADEON_ROP3_ZERO 0x00000000 +# define RADEON_ROP3_DSa 0x00880000 +# define RADEON_ROP3_SDna 0x00440000 +# define RADEON_ROP3_S 0x00cc0000 +# define RADEON_ROP3_DSna 0x00220000 +# define RADEON_ROP3_D 0x00aa0000 +# define RADEON_ROP3_DSx 0x00660000 +# define RADEON_ROP3_DSo 0x00ee0000 +# define RADEON_ROP3_DSon 0x00110000 +# define RADEON_ROP3_DSxn 0x00990000 +# define RADEON_ROP3_Dn 0x00550000 +# define RADEON_ROP3_SDno 0x00dd0000 +# define RADEON_ROP3_Sn 0x00330000 +# define RADEON_ROP3_DSno 0x00bb0000 +# define RADEON_ROP3_DSan 0x00770000 +# define RADEON_ROP3_ONE 0x00ff0000 +# define RADEON_ROP3_DPa 0x00a00000 +# define RADEON_ROP3_PDna 0x00500000 +# define RADEON_ROP3_P 0x00f00000 +# define RADEON_ROP3_DPna 0x000a0000 +# define RADEON_ROP3_D 0x00aa0000 +# define RADEON_ROP3_DPx 0x005a0000 +# define RADEON_ROP3_DPo 0x00fa0000 +# define RADEON_ROP3_DPon 0x00050000 +# define RADEON_ROP3_PDxn 0x00a50000 +# define RADEON_ROP3_PDno 0x00f50000 +# define RADEON_ROP3_Pn 0x000f0000 +# define RADEON_ROP3_DPno 0x00af0000 +# define RADEON_ROP3_DPan 0x005f0000 +#define RADEON_DP_GUI_MASTER_CNTL_C 0x1c84 +#define RADEON_DP_MIX 0x16c8 +#define RADEON_DP_SRC_BKGD_CLR 0x15dc +#define RADEON_DP_SRC_FRGD_CLR 0x15d8 +#define RADEON_DP_WRITE_MASK 0x16cc +#define RADEON_DST_BRES_DEC 0x1630 +#define RADEON_DST_BRES_ERR 0x1628 +#define RADEON_DST_BRES_INC 0x162c +#define RADEON_DST_BRES_LNTH 0x1634 +#define RADEON_DST_BRES_LNTH_SUB 0x1638 +#define RADEON_DST_HEIGHT 0x1410 +#define RADEON_DST_HEIGHT_WIDTH 0x143c +#define RADEON_DST_HEIGHT_WIDTH_8 0x158c +#define RADEON_DST_HEIGHT_WIDTH_BW 0x15b4 +#define RADEON_DST_HEIGHT_Y 0x15a0 +#define RADEON_DST_LINE_START 0x1600 +#define RADEON_DST_LINE_END 0x1604 +#define RADEON_DST_LINE_PATCOUNT 0x1608 +# define RADEON_BRES_CNTL_SHIFT 8 +#define RADEON_DST_OFFSET 0x1404 +#define RADEON_DST_PITCH 0x1408 +#define RADEON_DST_PITCH_OFFSET 0x142c +#define RADEON_DST_PITCH_OFFSET_C 0x1c80 +# define RADEON_PITCH_SHIFT 21 +# define RADEON_DST_TILE_LINEAR (0 << 30) +# define RADEON_DST_TILE_MACRO (1 << 30) +# define RADEON_DST_TILE_MICRO (2 << 30) +# define RADEON_DST_TILE_BOTH (3 << 30) +#define RADEON_DST_WIDTH 0x140c +#define RADEON_DST_WIDTH_HEIGHT 0x1598 +#define RADEON_DST_WIDTH_X 0x1588 +#define RADEON_DST_WIDTH_X_INCY 0x159c +#define RADEON_DST_X 0x141c +#define RADEON_DST_X_SUB 0x15a4 +#define RADEON_DST_X_Y 0x1594 +#define RADEON_DST_Y 0x1420 +#define RADEON_DST_Y_SUB 0x15a8 +#define RADEON_DST_Y_X 0x1438 + +#define RADEON_FCP_CNTL 0x0910 +# define RADEON_FCP0_SRC_PCICLK 0 +# define RADEON_FCP0_SRC_PCLK 1 +# define RADEON_FCP0_SRC_PCLKb 2 +# define RADEON_FCP0_SRC_HREF 3 +# define RADEON_FCP0_SRC_GND 4 +# define RADEON_FCP0_SRC_HREFb 5 +#define RADEON_FLUSH_1 0x1704 +#define RADEON_FLUSH_2 0x1708 +#define RADEON_FLUSH_3 0x170c +#define RADEON_FLUSH_4 0x1710 +#define RADEON_FLUSH_5 0x1714 +#define RADEON_FLUSH_6 0x1718 +#define RADEON_FLUSH_7 0x171c +#define RADEON_FOG_3D_TABLE_START 0x1810 +#define RADEON_FOG_3D_TABLE_END 0x1814 +#define RADEON_FOG_3D_TABLE_DENSITY 0x181c +#define RADEON_FOG_TABLE_INDEX 0x1a14 +#define RADEON_FOG_TABLE_DATA 0x1a18 +#define RADEON_FP_CRTC_H_TOTAL_DISP 0x0250 +#define RADEON_FP_CRTC_V_TOTAL_DISP 0x0254 +# define RADEON_FP_CRTC_H_TOTAL_MASK 0x000003ff +# define RADEON_FP_CRTC_H_DISP_MASK 0x01ff0000 +# define RADEON_FP_CRTC_V_TOTAL_MASK 0x00000fff +# define RADEON_FP_CRTC_V_DISP_MASK 0x0fff0000 +# define RADEON_FP_H_SYNC_STRT_CHAR_MASK 0x00001ff8 +# define RADEON_FP_H_SYNC_WID_MASK 0x003f0000 +# define RADEON_FP_V_SYNC_STRT_MASK 0x00000fff +# define RADEON_FP_V_SYNC_WID_MASK 0x001f0000 +# define RADEON_FP_CRTC_H_TOTAL_SHIFT 0x00000000 +# define RADEON_FP_CRTC_H_DISP_SHIFT 0x00000010 +# define RADEON_FP_CRTC_V_TOTAL_SHIFT 0x00000000 +# define RADEON_FP_CRTC_V_DISP_SHIFT 0x00000010 +# define RADEON_FP_H_SYNC_STRT_CHAR_SHIFT 0x00000003 +# define RADEON_FP_H_SYNC_WID_SHIFT 0x00000010 +# define RADEON_FP_V_SYNC_STRT_SHIFT 0x00000000 +# define RADEON_FP_V_SYNC_WID_SHIFT 0x00000010 +#define RADEON_FP_GEN_CNTL 0x0284 +# define RADEON_FP_FPON (1 << 0) +# define RADEON_FP_BLANK_EN (1 << 1) +# define RADEON_FP_TMDS_EN (1 << 2) +# define RADEON_FP_PANEL_FORMAT (1 << 3) +# define RADEON_FP_EN_TMDS (1 << 7) +# define RADEON_FP_DETECT_SENSE (1 << 8) +# define R200_FP_SOURCE_SEL_MASK (3 << 10) +# define R200_FP_SOURCE_SEL_CRTC1 (0 << 10) +# define R200_FP_SOURCE_SEL_CRTC2 (1 << 10) +# define R200_FP_SOURCE_SEL_RMX (2 << 10) +# define R200_FP_SOURCE_SEL_TRANS (3 << 10) +# define RADEON_FP_SEL_CRTC1 (0 << 13) +# define RADEON_FP_SEL_CRTC2 (1 << 13) +# define RADEON_FP_CRTC_DONT_SHADOW_HPAR (1 << 15) +# define RADEON_FP_CRTC_DONT_SHADOW_VPAR (1 << 16) +# define RADEON_FP_CRTC_DONT_SHADOW_HEND (1 << 17) +# define RADEON_FP_CRTC_USE_SHADOW_VEND (1 << 18) +# define RADEON_FP_RMX_HVSYNC_CONTROL_EN (1 << 20) +# define RADEON_FP_DFP_SYNC_SEL (1 << 21) +# define RADEON_FP_CRTC_LOCK_8DOT (1 << 22) +# define RADEON_FP_CRT_SYNC_SEL (1 << 23) +# define RADEON_FP_USE_SHADOW_EN (1 << 24) +# define RADEON_FP_CRT_SYNC_ALT (1 << 26) +#define RADEON_FP2_GEN_CNTL 0x0288 +# define RADEON_FP2_BLANK_EN (1 << 1) +# define RADEON_FP2_ON (1 << 2) +# define RADEON_FP2_PANEL_FORMAT (1 << 3) +# define RADEON_FP2_DETECT_SENSE (1 << 8) +# define R200_FP2_SOURCE_SEL_MASK (3 << 10) +# define R200_FP2_SOURCE_SEL_CRTC1 (0 << 10) +# define R200_FP2_SOURCE_SEL_CRTC2 (1 << 10) +# define R200_FP2_SOURCE_SEL_RMX (2 << 10) +# define R200_FP2_SOURCE_SEL_TRANS_UNIT (3 << 10) +# define RADEON_FP2_SRC_SEL_MASK (3 << 13) +# define RADEON_FP2_SRC_SEL_CRTC2 (1 << 13) +# define RADEON_FP2_FP_POL (1 << 16) +# define RADEON_FP2_LP_POL (1 << 17) +# define RADEON_FP2_SCK_POL (1 << 18) +# define RADEON_FP2_LCD_CNTL_MASK (7 << 19) +# define RADEON_FP2_PAD_FLOP_EN (1 << 22) +# define RADEON_FP2_CRC_EN (1 << 23) +# define RADEON_FP2_CRC_READ_EN (1 << 24) +# define RADEON_FP2_DVO_EN (1 << 25) +# define RADEON_FP2_DVO_RATE_SEL_SDR (1 << 26) +# define R200_FP2_DVO_RATE_SEL_SDR (1 << 27) +# define R300_FP2_DVO_CLOCK_MODE_SINGLE (1 << 28) +# define R300_FP2_DVO_DUAL_CHANNEL_EN (1 << 29) +#define RADEON_FP_H_SYNC_STRT_WID 0x02c4 +#define RADEON_FP_H2_SYNC_STRT_WID 0x03c4 +#define RADEON_FP_HORZ_STRETCH 0x028c +#define RADEON_FP_HORZ2_STRETCH 0x038c +# define RADEON_HORZ_STRETCH_RATIO_MASK 0xffff +# define RADEON_HORZ_STRETCH_RATIO_MAX 4096 +# define RADEON_HORZ_PANEL_SIZE (0x1ff << 16) +# define RADEON_HORZ_PANEL_SHIFT 16 +# define RADEON_HORZ_STRETCH_PIXREP (0 << 25) +# define RADEON_HORZ_STRETCH_BLEND (1 << 26) +# define RADEON_HORZ_STRETCH_ENABLE (1 << 25) +# define RADEON_HORZ_AUTO_RATIO (1 << 27) +# define RADEON_HORZ_FP_LOOP_STRETCH (0x7 << 28) +# define RADEON_HORZ_AUTO_RATIO_INC (1 << 31) +#define RADEON_FP_HORZ_VERT_ACTIVE 0x0278 +#define RADEON_FP_V_SYNC_STRT_WID 0x02c8 +#define RADEON_FP_VERT_STRETCH 0x0290 +#define RADEON_FP_V2_SYNC_STRT_WID 0x03c8 +#define RADEON_FP_VERT2_STRETCH 0x0390 +# define RADEON_VERT_PANEL_SIZE (0xfff << 12) +# define RADEON_VERT_PANEL_SHIFT 12 +# define RADEON_VERT_STRETCH_RATIO_MASK 0xfff +# define RADEON_VERT_STRETCH_RATIO_SHIFT 0 +# define RADEON_VERT_STRETCH_RATIO_MAX 4096 +# define RADEON_VERT_STRETCH_ENABLE (1 << 25) +# define RADEON_VERT_STRETCH_LINEREP (0 << 26) +# define RADEON_VERT_STRETCH_BLEND (1 << 26) +# define RADEON_VERT_AUTO_RATIO_EN (1 << 27) +# define RADEON_VERT_AUTO_RATIO_INC (1 << 31) +# define RADEON_VERT_STRETCH_RESERVED 0x71000000 +#define RS400_FP_2ND_GEN_CNTL 0x0384 +# define RS400_FP_2ND_ON (1 << 0) +# define RS400_FP_2ND_BLANK_EN (1 << 1) +# define RS400_TMDS_2ND_EN (1 << 2) +# define RS400_PANEL_FORMAT_2ND (1 << 3) +# define RS400_FP_2ND_EN_TMDS (1 << 7) +# define RS400_FP_2ND_DETECT_SENSE (1 << 8) +# define RS400_FP_2ND_SOURCE_SEL_MASK (3 << 10) +# define RS400_FP_2ND_SOURCE_SEL_CRTC1 (0 << 10) +# define RS400_FP_2ND_SOURCE_SEL_CRTC2 (1 << 10) +# define RS400_FP_2ND_SOURCE_SEL_RMX (2 << 10) +# define RS400_FP_2ND_DETECT_EN (1 << 12) +# define RS400_HPD_2ND_SEL (1 << 13) +#define RS400_FP2_2_GEN_CNTL 0x0388 +# define RS400_FP2_2_BLANK_EN (1 << 1) +# define RS400_FP2_2_ON (1 << 2) +# define RS400_FP2_2_PANEL_FORMAT (1 << 3) +# define RS400_FP2_2_DETECT_SENSE (1 << 8) +# define RS400_FP2_2_SOURCE_SEL_MASK (3 << 10) +# define RS400_FP2_2_SOURCE_SEL_CRTC1 (0 << 10) +# define RS400_FP2_2_SOURCE_SEL_CRTC2 (1 << 10) +# define RS400_FP2_2_SOURCE_SEL_RMX (2 << 10) +# define RS400_FP2_2_DVO2_EN (1 << 25) +#define RS400_TMDS2_CNTL 0x0394 +#define RS400_TMDS2_TRANSMITTER_CNTL 0x03a4 +# define RS400_TMDS2_PLLEN (1 << 0) +# define RS400_TMDS2_PLLRST (1 << 1) + +#define RADEON_GEN_INT_CNTL 0x0040 +#define RADEON_GEN_INT_STATUS 0x0044 +# define RADEON_VSYNC_INT_AK (1 << 2) +# define RADEON_VSYNC_INT (1 << 2) +# define RADEON_VSYNC2_INT_AK (1 << 6) +# define RADEON_VSYNC2_INT (1 << 6) +#define RADEON_GENENB 0x03c3 /* VGA */ +#define RADEON_GENFC_RD 0x03ca /* VGA */ +#define RADEON_GENFC_WT 0x03da /* VGA, 0x03ba */ +#define RADEON_GENMO_RD 0x03cc /* VGA */ +#define RADEON_GENMO_WT 0x03c2 /* VGA */ +#define RADEON_GENS0 0x03c2 /* VGA */ +#define RADEON_GENS1 0x03da /* VGA, 0x03ba */ +#define RADEON_GPIO_MONID 0x0068 /* DDC interface via I2C */ +#define RADEON_GPIO_MONIDB 0x006c +#define RADEON_GPIO_CRT2_DDC 0x006c +#define RADEON_GPIO_DVI_DDC 0x0064 +#define RADEON_GPIO_VGA_DDC 0x0060 +# define RADEON_GPIO_A_0 (1 << 0) +# define RADEON_GPIO_A_1 (1 << 1) +# define RADEON_GPIO_Y_0 (1 << 8) +# define RADEON_GPIO_Y_1 (1 << 9) +# define RADEON_GPIO_Y_SHIFT_0 8 +# define RADEON_GPIO_Y_SHIFT_1 9 +# define RADEON_GPIO_EN_0 (1 << 16) +# define RADEON_GPIO_EN_1 (1 << 17) +# define RADEON_GPIO_MASK_0 (1 << 24) /*??*/ +# define RADEON_GPIO_MASK_1 (1 << 25) /*??*/ +#define RADEON_GRPH8_DATA 0x03cf /* VGA */ +#define RADEON_GRPH8_IDX 0x03ce /* VGA */ +#define RADEON_GUI_SCRATCH_REG0 0x15e0 +#define RADEON_GUI_SCRATCH_REG1 0x15e4 +#define RADEON_GUI_SCRATCH_REG2 0x15e8 +#define RADEON_GUI_SCRATCH_REG3 0x15ec +#define RADEON_GUI_SCRATCH_REG4 0x15f0 +#define RADEON_GUI_SCRATCH_REG5 0x15f4 + +#define RADEON_HEADER 0x0f0e /* PCI */ +#define RADEON_HOST_DATA0 0x17c0 +#define RADEON_HOST_DATA1 0x17c4 +#define RADEON_HOST_DATA2 0x17c8 +#define RADEON_HOST_DATA3 0x17cc +#define RADEON_HOST_DATA4 0x17d0 +#define RADEON_HOST_DATA5 0x17d4 +#define RADEON_HOST_DATA6 0x17d8 +#define RADEON_HOST_DATA7 0x17dc +#define RADEON_HOST_DATA_LAST 0x17e0 +#define RADEON_HOST_PATH_CNTL 0x0130 +# define RADEON_HDP_SOFT_RESET (1 << 26) +# define RADEON_HDP_APER_CNTL (1 << 23) +#define RADEON_HTOTAL_CNTL 0x0009 /* PLL */ +# define RADEON_HTOT_CNTL_VGA_EN (1 << 28) +#define RADEON_HTOTAL2_CNTL 0x002e /* PLL */ + + /* Multimedia I2C bus */ +#define RADEON_I2C_CNTL_0 0x0090 +#define RADEON_I2C_DONE (1<<0) +#define RADEON_I2C_NACK (1<<1) +#define RADEON_I2C_HALT (1<<2) +#define RADEON_I2C_SOFT_RST (1<<5) +#define RADEON_I2C_DRIVE_EN (1<<6) +#define RADEON_I2C_DRIVE_SEL (1<<7) +#define RADEON_I2C_START (1<<8) +#define RADEON_I2C_STOP (1<<9) +#define RADEON_I2C_RECEIVE (1<<10) +#define RADEON_I2C_ABORT (1<<11) +#define RADEON_I2C_GO (1<<12) +#define RADEON_I2C_CNTL_1 0x0094 +#define RADEON_I2C_SEL (1<<16) +#define RADEON_I2C_EN (1<<17) +#define RADEON_I2C_DATA 0x0098 + +#define RADEON_DVI_I2C_CNTL_0 0x02e0 +#define RADEON_DVI_I2C_CNTL_1 0x02e4 /* ? */ +#define RADEON_DVI_I2C_DATA 0x02e8 + +#define RADEON_INTERRUPT_LINE 0x0f3c /* PCI */ +#define RADEON_INTERRUPT_PIN 0x0f3d /* PCI */ +#define RADEON_IO_BASE 0x0f14 /* PCI */ + +#define RADEON_LATENCY 0x0f0d /* PCI */ +#define RADEON_LEAD_BRES_DEC 0x1608 +#define RADEON_LEAD_BRES_LNTH 0x161c +#define RADEON_LEAD_BRES_LNTH_SUB 0x1624 +#define RADEON_LVDS_GEN_CNTL 0x02d0 +# define RADEON_LVDS_ON (1 << 0) +# define RADEON_LVDS_DISPLAY_DIS (1 << 1) +# define RADEON_LVDS_PANEL_TYPE (1 << 2) +# define RADEON_LVDS_PANEL_FORMAT (1 << 3) +# define RADEON_LVDS_NO_FM (0 << 4) +# define RADEON_LVDS_2_GREY (1 << 4) +# define RADEON_LVDS_4_GREY (2 << 4) +# define RADEON_LVDS_RST_FM (1 << 6) +# define RADEON_LVDS_EN (1 << 7) +# define RADEON_LVDS_BL_MOD_LEVEL_SHIFT 8 +# define RADEON_LVDS_BL_MOD_LEVEL_MASK (0xff << 8) +# define RADEON_LVDS_BL_MOD_EN (1 << 16) +# define RADEON_LVDS_BL_CLK_SEL (1 << 17) +# define RADEON_LVDS_DIGON (1 << 18) +# define RADEON_LVDS_BLON (1 << 19) +# define RADEON_LVDS_FP_POL_LOW (1 << 20) +# define RADEON_LVDS_LP_POL_LOW (1 << 21) +# define RADEON_LVDS_DTM_POL_LOW (1 << 22) +# define RADEON_LVDS_SEL_CRTC2 (1 << 23) +# define RADEON_LVDS_FPDI_EN (1 << 27) +# define RADEON_LVDS_HSYNC_DELAY_SHIFT 28 +#define RADEON_LVDS_PLL_CNTL 0x02d4 +# define RADEON_HSYNC_DELAY_SHIFT 28 +# define RADEON_HSYNC_DELAY_MASK (0xf << 28) +# define RADEON_LVDS_PLL_EN (1 << 16) +# define RADEON_LVDS_PLL_RESET (1 << 17) +# define R300_LVDS_SRC_SEL_MASK (3 << 18) +# define R300_LVDS_SRC_SEL_CRTC1 (0 << 18) +# define R300_LVDS_SRC_SEL_CRTC2 (1 << 18) +# define R300_LVDS_SRC_SEL_RMX (2 << 18) +#define RADEON_LVDS_SS_GEN_CNTL 0x02ec +# define RADEON_LVDS_PWRSEQ_DELAY1_SHIFT 16 +# define RADEON_LVDS_PWRSEQ_DELAY2_SHIFT 20 + +#define RADEON_MAX_LATENCY 0x0f3f /* PCI */ +#define RADEON_MC_AGP_LOCATION 0x014c +#define RADEON_MC_FB_LOCATION 0x0148 +#define RADEON_DISPLAY_BASE_ADDR 0x23c +#define RADEON_DISPLAY2_BASE_ADDR 0x33c +#define RADEON_OV0_BASE_ADDR 0x43c +#define RADEON_NB_TOM 0x15c +#define R300_MC_INIT_MISC_LAT_TIMER 0x180 +#define RADEON_MCLK_CNTL 0x0012 /* PLL */ +# define RADEON_FORCEON_MCLKA (1 << 16) +# define RADEON_FORCEON_MCLKB (1 << 17) +# define RADEON_FORCEON_YCLKA (1 << 18) +# define RADEON_FORCEON_YCLKB (1 << 19) +# define RADEON_FORCEON_MC (1 << 20) +# define RADEON_FORCEON_AIC (1 << 21) +# define R300_DISABLE_MC_MCLKA (1 << 21) +# define R300_DISABLE_MC_MCLKB (1 << 21) +#define RADEON_MCLK_MISC 0x001f /* PLL */ +# define RADEON_MC_MCLK_MAX_DYN_STOP_LAT (1 << 12) +# define RADEON_IO_MCLK_MAX_DYN_STOP_LAT (1 << 13) +# define RADEON_MC_MCLK_DYN_ENABLE (1 << 14) +# define RADEON_IO_MCLK_DYN_ENABLE (1 << 15) +#define RADEON_LCD_GPIO_MASK 0x01a0 +#define RADEON_GPIOPAD_EN 0x01a0 +#define RADEON_LCD_GPIO_Y_REG 0x01a4 +#define RADEON_MDGPIO_A_REG 0x01ac +#define RADEON_MDGPIO_EN_REG 0x01b0 +#define RADEON_MDGPIO_MASK 0x0198 +#define RADEON_GPIOPAD_MASK 0x0198 +#define RADEON_GPIOPAD_A 0x019c +#define RADEON_MDGPIO_Y_REG 0x01b4 +#define RADEON_MEM_ADDR_CONFIG 0x0148 +#define RADEON_MEM_BASE 0x0f10 /* PCI */ +#define RADEON_MEM_CNTL 0x0140 +# define RADEON_MEM_NUM_CHANNELS_MASK 0x01 +# define RADEON_MEM_USE_B_CH_ONLY (1 << 1) +# define RV100_HALF_MODE (1 << 3) +# define R300_MEM_NUM_CHANNELS_MASK 0x03 +# define R300_MEM_USE_CD_CH_ONLY (1 << 2) +#define RADEON_MEM_TIMING_CNTL 0x0144 /* EXT_MEM_CNTL */ +#define RADEON_MEM_INIT_LAT_TIMER 0x0154 +#define RADEON_MEM_INTF_CNTL 0x014c +#define RADEON_MEM_SDRAM_MODE_REG 0x0158 +# define RADEON_SDRAM_MODE_MASK 0xffff0000 +# define RADEON_B3MEM_RESET_MASK 0x6fffffff +# define RADEON_MEM_CFG_TYPE_DDR (1 << 30) +#define RADEON_MEM_STR_CNTL 0x0150 +# define RADEON_MEM_PWRUP_COMPL_A (1 << 0) +# define RADEON_MEM_PWRUP_COMPL_B (1 << 1) +# define R300_MEM_PWRUP_COMPL_C (1 << 2) +# define R300_MEM_PWRUP_COMPL_D (1 << 3) +# define RADEON_MEM_PWRUP_COMPLETE 0x03 +# define R300_MEM_PWRUP_COMPLETE 0x0f +#define RADEON_MC_STATUS 0x0150 +# define RADEON_MC_IDLE (1 << 2) +# define R300_MC_IDLE (1 << 4) +#define RADEON_MEM_VGA_RP_SEL 0x003c +#define RADEON_MEM_VGA_WP_SEL 0x0038 +#define RADEON_MIN_GRANT 0x0f3e /* PCI */ +#define RADEON_MM_DATA 0x0004 +#define RADEON_MM_INDEX 0x0000 +#define RADEON_MPLL_CNTL 0x000e /* PLL */ +#define RADEON_MPP_TB_CONFIG 0x01c0 /* ? */ +#define RADEON_MPP_GP_CONFIG 0x01c8 /* ? */ +#define RADEON_SEPROM_CNTL1 0x01c0 +# define RADEON_SCK_PRESCALE_SHIFT 24 +# define RADEON_SCK_PRESCALE_MASK (0xff << 24) +#define R300_MC_IND_INDEX 0x01f8 +# define R300_MC_IND_ADDR_MASK 0x3f +# define R300_MC_IND_WR_EN (1 << 8) +#define R300_MC_IND_DATA 0x01fc +#define R300_MC_READ_CNTL_AB 0x017c +# define R300_MEM_RBS_POSITION_A_MASK 0x03 +#define R300_MC_READ_CNTL_CD_mcind 0x24 +# define R300_MEM_RBS_POSITION_C_MASK 0x03 + +#define RADEON_N_VIF_COUNT 0x0248 + +#define RADEON_OV0_AUTO_FLIP_CNTL 0x0470 +# define RADEON_OV0_AUTO_FLIP_CNTL_SOFT_BUF_NUM 0x00000007 +# define RADEON_OV0_AUTO_FLIP_CNTL_SOFT_REPEAT_FIELD 0x00000008 +# define RADEON_OV0_AUTO_FLIP_CNTL_SOFT_BUF_ODD 0x00000010 +# define RADEON_OV0_AUTO_FLIP_CNTL_IGNORE_REPEAT_FIELD 0x00000020 +# define RADEON_OV0_AUTO_FLIP_CNTL_SOFT_EOF_TOGGLE 0x00000040 +# define RADEON_OV0_AUTO_FLIP_CNTL_VID_PORT_SELECT 0x00000300 +# define RADEON_OV0_AUTO_FLIP_CNTL_P1_FIRST_LINE_EVEN 0x00010000 +# define RADEON_OV0_AUTO_FLIP_CNTL_SHIFT_EVEN_DOWN 0x00040000 +# define RADEON_OV0_AUTO_FLIP_CNTL_SHIFT_ODD_DOWN 0x00080000 +# define RADEON_OV0_AUTO_FLIP_CNTL_FIELD_POL_SOURCE 0x00800000 + +#define RADEON_OV0_COLOUR_CNTL 0x04E0 +#define RADEON_OV0_DEINTERLACE_PATTERN 0x0474 +#define RADEON_OV0_EXCLUSIVE_HORZ 0x0408 +# define RADEON_EXCL_HORZ_START_MASK 0x000000ff +# define RADEON_EXCL_HORZ_END_MASK 0x0000ff00 +# define RADEON_EXCL_HORZ_BACK_PORCH_MASK 0x00ff0000 +# define RADEON_EXCL_HORZ_EXCLUSIVE_EN 0x80000000 +#define RADEON_OV0_EXCLUSIVE_VERT 0x040C +# define RADEON_EXCL_VERT_START_MASK 0x000003ff +# define RADEON_EXCL_VERT_END_MASK 0x03ff0000 +#define RADEON_OV0_FILTER_CNTL 0x04A0 +# define RADEON_FILTER_PROGRAMMABLE_COEF 0x0 +# define RADEON_FILTER_HC_COEF_HORZ_Y 0x1 +# define RADEON_FILTER_HC_COEF_HORZ_UV 0x2 +# define RADEON_FILTER_HC_COEF_VERT_Y 0x4 +# define RADEON_FILTER_HC_COEF_VERT_UV 0x8 +# define RADEON_FILTER_HARDCODED_COEF 0xf +# define RADEON_FILTER_COEF_MASK 0xf + +#define RADEON_OV0_FOUR_TAP_COEF_0 0x04B0 +#define RADEON_OV0_FOUR_TAP_COEF_1 0x04B4 +#define RADEON_OV0_FOUR_TAP_COEF_2 0x04B8 +#define RADEON_OV0_FOUR_TAP_COEF_3 0x04BC +#define RADEON_OV0_FOUR_TAP_COEF_4 0x04C0 +#define RADEON_OV0_FLAG_CNTL 0x04DC +#define RADEON_OV0_GAMMA_000_00F 0x0d40 +#define RADEON_OV0_GAMMA_010_01F 0x0d44 +#define RADEON_OV0_GAMMA_020_03F 0x0d48 +#define RADEON_OV0_GAMMA_040_07F 0x0d4c +#define RADEON_OV0_GAMMA_080_0BF 0x0e00 +#define RADEON_OV0_GAMMA_0C0_0FF 0x0e04 +#define RADEON_OV0_GAMMA_100_13F 0x0e08 +#define RADEON_OV0_GAMMA_140_17F 0x0e0c +#define RADEON_OV0_GAMMA_180_1BF 0x0e10 +#define RADEON_OV0_GAMMA_1C0_1FF 0x0e14 +#define RADEON_OV0_GAMMA_200_23F 0x0e18 +#define RADEON_OV0_GAMMA_240_27F 0x0e1c +#define RADEON_OV0_GAMMA_280_2BF 0x0e20 +#define RADEON_OV0_GAMMA_2C0_2FF 0x0e24 +#define RADEON_OV0_GAMMA_300_33F 0x0e28 +#define RADEON_OV0_GAMMA_340_37F 0x0e2c +#define RADEON_OV0_GAMMA_380_3BF 0x0d50 +#define RADEON_OV0_GAMMA_3C0_3FF 0x0d54 +#define RADEON_OV0_GRAPHICS_KEY_CLR_LOW 0x04EC +#define RADEON_OV0_GRAPHICS_KEY_CLR_HIGH 0x04F0 +#define RADEON_OV0_H_INC 0x0480 +#define RADEON_OV0_KEY_CNTL 0x04F4 +# define RADEON_VIDEO_KEY_FN_MASK 0x00000003L +# define RADEON_VIDEO_KEY_FN_FALSE 0x00000000L +# define RADEON_VIDEO_KEY_FN_TRUE 0x00000001L +# define RADEON_VIDEO_KEY_FN_EQ 0x00000002L +# define RADEON_VIDEO_KEY_FN_NE 0x00000003L +# define RADEON_GRAPHIC_KEY_FN_MASK 0x00000030L +# define RADEON_GRAPHIC_KEY_FN_FALSE 0x00000000L +# define RADEON_GRAPHIC_KEY_FN_TRUE 0x00000010L +# define RADEON_GRAPHIC_KEY_FN_EQ 0x00000020L +# define RADEON_GRAPHIC_KEY_FN_NE 0x00000030L +# define RADEON_CMP_MIX_MASK 0x00000100L +# define RADEON_CMP_MIX_OR 0x00000000L +# define RADEON_CMP_MIX_AND 0x00000100L +#define RADEON_OV0_LIN_TRANS_A 0x0d20 +#define RADEON_OV0_LIN_TRANS_B 0x0d24 +#define RADEON_OV0_LIN_TRANS_C 0x0d28 +#define RADEON_OV0_LIN_TRANS_D 0x0d2c +#define RADEON_OV0_LIN_TRANS_E 0x0d30 +#define RADEON_OV0_LIN_TRANS_F 0x0d34 +#define RADEON_OV0_P1_BLANK_LINES_AT_TOP 0x0430 +# define RADEON_P1_BLNK_LN_AT_TOP_M1_MASK 0x00000fffL +# define RADEON_P1_ACTIVE_LINES_M1 0x0fff0000L +#define RADEON_OV0_P1_H_ACCUM_INIT 0x0488 +#define RADEON_OV0_P1_V_ACCUM_INIT 0x0428 +# define RADEON_OV0_P1_MAX_LN_IN_PER_LN_OUT 0x00000003L +# define RADEON_OV0_P1_V_ACCUM_INIT_MASK 0x01ff8000L +#define RADEON_OV0_P1_X_START_END 0x0494 +#define RADEON_OV0_P2_X_START_END 0x0498 +#define RADEON_OV0_P23_BLANK_LINES_AT_TOP 0x0434 +# define RADEON_P23_BLNK_LN_AT_TOP_M1_MASK 0x000007ffL +# define RADEON_P23_ACTIVE_LINES_M1 0x07ff0000L +#define RADEON_OV0_P23_H_ACCUM_INIT 0x048C +#define RADEON_OV0_P23_V_ACCUM_INIT 0x042C +#define RADEON_OV0_P3_X_START_END 0x049C +#define RADEON_OV0_REG_LOAD_CNTL 0x0410 +# define RADEON_REG_LD_CTL_LOCK 0x00000001L +# define RADEON_REG_LD_CTL_VBLANK_DURING_LOCK 0x00000002L +# define RADEON_REG_LD_CTL_STALL_GUI_UNTIL_FLIP 0x00000004L +# define RADEON_REG_LD_CTL_LOCK_READBACK 0x00000008L +# define RADEON_REG_LD_CTL_FLIP_READBACK 0x00000010L +#define RADEON_OV0_SCALE_CNTL 0x0420 +# define RADEON_SCALER_HORZ_PICK_NEAREST 0x00000004L +# define RADEON_SCALER_VERT_PICK_NEAREST 0x00000008L +# define RADEON_SCALER_SIGNED_UV 0x00000010L +# define RADEON_SCALER_GAMMA_SEL_MASK 0x00000060L +# define RADEON_SCALER_GAMMA_SEL_BRIGHT 0x00000000L +# define RADEON_SCALER_GAMMA_SEL_G22 0x00000020L +# define RADEON_SCALER_GAMMA_SEL_G18 0x00000040L +# define RADEON_SCALER_GAMMA_SEL_G14 0x00000060L +# define RADEON_SCALER_COMCORE_SHIFT_UP_ONE 0x00000080L +# define RADEON_SCALER_SURFAC_FORMAT 0x00000f00L +# define RADEON_SCALER_SOURCE_15BPP 0x00000300L +# define RADEON_SCALER_SOURCE_16BPP 0x00000400L +# define RADEON_SCALER_SOURCE_32BPP 0x00000600L +# define RADEON_SCALER_SOURCE_YUV9 0x00000900L +# define RADEON_SCALER_SOURCE_YUV12 0x00000A00L +# define RADEON_SCALER_SOURCE_VYUY422 0x00000B00L +# define RADEON_SCALER_SOURCE_YVYU422 0x00000C00L +# define RADEON_SCALER_ADAPTIVE_DEINT 0x00001000L +# define RADEON_SCALER_TEMPORAL_DEINT 0x00002000L +# define RADEON_SCALER_CRTC_SEL 0x00004000L +# define RADEON_SCALER_SMART_SWITCH 0x00008000L +# define RADEON_SCALER_BURST_PER_PLANE 0x007F0000L +# define RADEON_SCALER_DOUBLE_BUFFER 0x01000000L +# define RADEON_SCALER_DIS_LIMIT 0x08000000L +# define RADEON_SCALER_LIN_TRANS_BYPASS 0x10000000L +# define RADEON_SCALER_INT_EMU 0x20000000L +# define RADEON_SCALER_ENABLE 0x40000000L +# define RADEON_SCALER_SOFT_RESET 0x80000000L +#define RADEON_OV0_STEP_BY 0x0484 +#define RADEON_OV0_TEST 0x04F8 +#define RADEON_OV0_V_INC 0x0424 +#define RADEON_OV0_VID_BUF_PITCH0_VALUE 0x0460 +#define RADEON_OV0_VID_BUF_PITCH1_VALUE 0x0464 +#define RADEON_OV0_VID_BUF0_BASE_ADRS 0x0440 +# define RADEON_VIF_BUF0_PITCH_SEL 0x00000001L +# define RADEON_VIF_BUF0_TILE_ADRS 0x00000002L +# define RADEON_VIF_BUF0_BASE_ADRS_MASK 0x03fffff0L +# define RADEON_VIF_BUF0_1ST_LINE_LSBS_MASK 0x48000000L +#define RADEON_OV0_VID_BUF1_BASE_ADRS 0x0444 +# define RADEON_VIF_BUF1_PITCH_SEL 0x00000001L +# define RADEON_VIF_BUF1_TILE_ADRS 0x00000002L +# define RADEON_VIF_BUF1_BASE_ADRS_MASK 0x03fffff0L +# define RADEON_VIF_BUF1_1ST_LINE_LSBS_MASK 0x48000000L +#define RADEON_OV0_VID_BUF2_BASE_ADRS 0x0448 +# define RADEON_VIF_BUF2_PITCH_SEL 0x00000001L +# define RADEON_VIF_BUF2_TILE_ADRS 0x00000002L +# define RADEON_VIF_BUF2_BASE_ADRS_MASK 0x03fffff0L +# define RADEON_VIF_BUF2_1ST_LINE_LSBS_MASK 0x48000000L +#define RADEON_OV0_VID_BUF3_BASE_ADRS 0x044C +#define RADEON_OV0_VID_BUF4_BASE_ADRS 0x0450 +#define RADEON_OV0_VID_BUF5_BASE_ADRS 0x0454 +#define RADEON_OV0_VIDEO_KEY_CLR_HIGH 0x04E8 +#define RADEON_OV0_VIDEO_KEY_CLR_LOW 0x04E4 +#define RADEON_OV0_Y_X_START 0x0400 +#define RADEON_OV0_Y_X_END 0x0404 +#define RADEON_OV1_Y_X_START 0x0600 +#define RADEON_OV1_Y_X_END 0x0604 +#define RADEON_OVR_CLR 0x0230 +#define RADEON_OVR_WID_LEFT_RIGHT 0x0234 +#define RADEON_OVR_WID_TOP_BOTTOM 0x0238 + +/* first capture unit */ + +#define RADEON_CAP0_BUF0_OFFSET 0x0920 +#define RADEON_CAP0_BUF1_OFFSET 0x0924 +#define RADEON_CAP0_BUF0_EVEN_OFFSET 0x0928 +#define RADEON_CAP0_BUF1_EVEN_OFFSET 0x092C + +#define RADEON_CAP0_BUF_PITCH 0x0930 +#define RADEON_CAP0_V_WINDOW 0x0934 +#define RADEON_CAP0_H_WINDOW 0x0938 +#define RADEON_CAP0_VBI0_OFFSET 0x093C +#define RADEON_CAP0_VBI1_OFFSET 0x0940 +#define RADEON_CAP0_VBI_V_WINDOW 0x0944 +#define RADEON_CAP0_VBI_H_WINDOW 0x0948 +#define RADEON_CAP0_PORT_MODE_CNTL 0x094C +#define RADEON_CAP0_TRIG_CNTL 0x0950 +#define RADEON_CAP0_DEBUG 0x0954 +#define RADEON_CAP0_CONFIG 0x0958 +# define RADEON_CAP0_CONFIG_CONTINUOS 0x00000001 +# define RADEON_CAP0_CONFIG_START_FIELD_EVEN 0x00000002 +# define RADEON_CAP0_CONFIG_START_BUF_GET 0x00000004 +# define RADEON_CAP0_CONFIG_START_BUF_SET 0x00000008 +# define RADEON_CAP0_CONFIG_BUF_TYPE_ALT 0x00000010 +# define RADEON_CAP0_CONFIG_BUF_TYPE_FRAME 0x00000020 +# define RADEON_CAP0_CONFIG_ONESHOT_MODE_FRAME 0x00000040 +# define RADEON_CAP0_CONFIG_BUF_MODE_DOUBLE 0x00000080 +# define RADEON_CAP0_CONFIG_BUF_MODE_TRIPLE 0x00000100 +# define RADEON_CAP0_CONFIG_MIRROR_EN 0x00000200 +# define RADEON_CAP0_CONFIG_ONESHOT_MIRROR_EN 0x00000400 +# define RADEON_CAP0_CONFIG_VIDEO_SIGNED_UV 0x00000800 +# define RADEON_CAP0_CONFIG_ANC_DECODE_EN 0x00001000 +# define RADEON_CAP0_CONFIG_VBI_EN 0x00002000 +# define RADEON_CAP0_CONFIG_SOFT_PULL_DOWN_EN 0x00004000 +# define RADEON_CAP0_CONFIG_VIP_EXTEND_FLAG_EN 0x00008000 +# define RADEON_CAP0_CONFIG_FAKE_FIELD_EN 0x00010000 +# define RADEON_CAP0_CONFIG_ODD_ONE_MORE_LINE 0x00020000 +# define RADEON_CAP0_CONFIG_EVEN_ONE_MORE_LINE 0x00040000 +# define RADEON_CAP0_CONFIG_HORZ_DIVIDE_2 0x00080000 +# define RADEON_CAP0_CONFIG_HORZ_DIVIDE_4 0x00100000 +# define RADEON_CAP0_CONFIG_VERT_DIVIDE_2 0x00200000 +# define RADEON_CAP0_CONFIG_VERT_DIVIDE_4 0x00400000 +# define RADEON_CAP0_CONFIG_FORMAT_BROOKTREE 0x00000000 +# define RADEON_CAP0_CONFIG_FORMAT_CCIR656 0x00800000 +# define RADEON_CAP0_CONFIG_FORMAT_ZV 0x01000000 +# define RADEON_CAP0_CONFIG_FORMAT_VIP 0x01800000 +# define RADEON_CAP0_CONFIG_FORMAT_TRANSPORT 0x02000000 +# define RADEON_CAP0_CONFIG_HORZ_DECIMATOR 0x04000000 +# define RADEON_CAP0_CONFIG_VIDEO_IN_YVYU422 0x00000000 +# define RADEON_CAP0_CONFIG_VIDEO_IN_VYUY422 0x20000000 +# define RADEON_CAP0_CONFIG_VBI_DIVIDE_2 0x40000000 +# define RADEON_CAP0_CONFIG_VBI_DIVIDE_4 0x80000000 +#define RADEON_CAP0_ANC_ODD_OFFSET 0x095C +#define RADEON_CAP0_ANC_EVEN_OFFSET 0x0960 +#define RADEON_CAP0_ANC_H_WINDOW 0x0964 +#define RADEON_CAP0_VIDEO_SYNC_TEST 0x0968 +#define RADEON_CAP0_ONESHOT_BUF_OFFSET 0x096C +#define RADEON_CAP0_BUF_STATUS 0x0970 +/* #define RADEON_CAP0_DWNSC_XRATIO 0x0978 */ +/* #define RADEON_CAP0_XSHARPNESS 0x097C */ +#define RADEON_CAP0_VBI2_OFFSET 0x0980 +#define RADEON_CAP0_VBI3_OFFSET 0x0984 +#define RADEON_CAP0_ANC2_OFFSET 0x0988 +#define RADEON_CAP0_ANC3_OFFSET 0x098C +#define RADEON_VID_BUFFER_CONTROL 0x0900 + +/* second capture unit */ + +#define RADEON_CAP1_BUF0_OFFSET 0x0990 +#define RADEON_CAP1_BUF1_OFFSET 0x0994 +#define RADEON_CAP1_BUF0_EVEN_OFFSET 0x0998 +#define RADEON_CAP1_BUF1_EVEN_OFFSET 0x099C + +#define RADEON_CAP1_BUF_PITCH 0x09A0 +#define RADEON_CAP1_V_WINDOW 0x09A4 +#define RADEON_CAP1_H_WINDOW 0x09A8 +#define RADEON_CAP1_VBI_ODD_OFFSET 0x09AC +#define RADEON_CAP1_VBI_EVEN_OFFSET 0x09B0 +#define RADEON_CAP1_VBI_V_WINDOW 0x09B4 +#define RADEON_CAP1_VBI_H_WINDOW 0x09B8 +#define RADEON_CAP1_PORT_MODE_CNTL 0x09BC +#define RADEON_CAP1_TRIG_CNTL 0x09C0 +#define RADEON_CAP1_DEBUG 0x09C4 +#define RADEON_CAP1_CONFIG 0x09C8 +#define RADEON_CAP1_ANC_ODD_OFFSET 0x09CC +#define RADEON_CAP1_ANC_EVEN_OFFSET 0x09D0 +#define RADEON_CAP1_ANC_H_WINDOW 0x09D4 +#define RADEON_CAP1_VIDEO_SYNC_TEST 0x09D8 +#define RADEON_CAP1_ONESHOT_BUF_OFFSET 0x09DC +#define RADEON_CAP1_BUF_STATUS 0x09E0 +#define RADEON_CAP1_DWNSC_XRATIO 0x09E8 +#define RADEON_CAP1_XSHARPNESS 0x09EC + +/* misc multimedia registers */ + +#define RADEON_IDCT_RUNS 0x1F80 +#define RADEON_IDCT_LEVELS 0x1F84 +#define RADEON_IDCT_CONTROL 0x1FBC +#define RADEON_IDCT_AUTH_CONTROL 0x1F88 +#define RADEON_IDCT_AUTH 0x1F8C + +#define RADEON_P2PLL_CNTL 0x002a /* P2PLL */ +# define RADEON_P2PLL_RESET (1 << 0) +# define RADEON_P2PLL_SLEEP (1 << 1) +# define RADEON_P2PLL_PVG_MASK (7 << 11) +# define RADEON_P2PLL_PVG_SHIFT 11 +# define RADEON_P2PLL_ATOMIC_UPDATE_EN (1 << 16) +# define RADEON_P2PLL_VGA_ATOMIC_UPDATE_EN (1 << 17) +# define RADEON_P2PLL_ATOMIC_UPDATE_VSYNC (1 << 18) +#define RADEON_P2PLL_DIV_0 0x002c +# define RADEON_P2PLL_FB0_DIV_MASK 0x07ff +# define RADEON_P2PLL_POST0_DIV_MASK 0x00070000 +#define RADEON_P2PLL_REF_DIV 0x002B /* PLL */ +# define RADEON_P2PLL_REF_DIV_MASK 0x03ff +# define RADEON_P2PLL_ATOMIC_UPDATE_R (1 << 15) /* same as _W */ +# define RADEON_P2PLL_ATOMIC_UPDATE_W (1 << 15) /* same as _R */ +# define R300_PPLL_REF_DIV_ACC_MASK (0x3ff << 18) +# define R300_PPLL_REF_DIV_ACC_SHIFT 18 +#define RADEON_PALETTE_DATA 0x00b4 +#define RADEON_PALETTE_30_DATA 0x00b8 +#define RADEON_PALETTE_INDEX 0x00b0 +#define RADEON_PCI_GART_PAGE 0x017c +#define RADEON_PIXCLKS_CNTL 0x002d +# define RADEON_PIX2CLK_SRC_SEL_MASK 0x03 +# define RADEON_PIX2CLK_SRC_SEL_CPUCLK 0x00 +# define RADEON_PIX2CLK_SRC_SEL_PSCANCLK 0x01 +# define RADEON_PIX2CLK_SRC_SEL_BYTECLK 0x02 +# define RADEON_PIX2CLK_SRC_SEL_P2PLLCLK 0x03 +# define RADEON_PIX2CLK_ALWAYS_ONb (1<<6) +# define RADEON_PIX2CLK_DAC_ALWAYS_ONb (1<<7) +# define RADEON_PIXCLK_TV_SRC_SEL (1 << 8) +# define RADEON_DISP_TVOUT_PIXCLK_TV_ALWAYS_ONb (1 << 9) +# define R300_DVOCLK_ALWAYS_ONb (1 << 10) +# define RADEON_PIXCLK_BLEND_ALWAYS_ONb (1 << 11) +# define RADEON_PIXCLK_GV_ALWAYS_ONb (1 << 12) +# define RADEON_PIXCLK_DIG_TMDS_ALWAYS_ONb (1 << 13) +# define R300_PIXCLK_DVO_ALWAYS_ONb (1 << 13) +# define RADEON_PIXCLK_LVDS_ALWAYS_ONb (1 << 14) +# define RADEON_PIXCLK_TMDS_ALWAYS_ONb (1 << 15) +# define R300_PIXCLK_TRANS_ALWAYS_ONb (1 << 16) +# define R300_PIXCLK_TVO_ALWAYS_ONb (1 << 17) +# define R300_P2G2CLK_ALWAYS_ONb (1 << 18) +# define R300_P2G2CLK_DAC_ALWAYS_ONb (1 << 19) +# define R300_DISP_DAC_PIXCLK_DAC2_BLANK_OFF (1 << 23) +#define RADEON_PLANE_3D_MASK_C 0x1d44 +#define RADEON_PLL_TEST_CNTL 0x0013 /* PLL */ +# define RADEON_PLL_MASK_READ_B (1 << 9) +#define RADEON_PMI_CAP_ID 0x0f5c /* PCI */ +#define RADEON_PMI_DATA 0x0f63 /* PCI */ +#define RADEON_PMI_NXT_CAP_PTR 0x0f5d /* PCI */ +#define RADEON_PMI_PMC_REG 0x0f5e /* PCI */ +#define RADEON_PMI_PMCSR_REG 0x0f60 /* PCI */ +#define RADEON_PMI_REGISTER 0x0f5c /* PCI */ +#define RADEON_PPLL_CNTL 0x0002 /* PLL */ +# define RADEON_PPLL_RESET (1 << 0) +# define RADEON_PPLL_SLEEP (1 << 1) +# define RADEON_PPLL_PVG_MASK (7 << 11) +# define RADEON_PPLL_PVG_SHIFT 11 +# define RADEON_PPLL_ATOMIC_UPDATE_EN (1 << 16) +# define RADEON_PPLL_VGA_ATOMIC_UPDATE_EN (1 << 17) +# define RADEON_PPLL_ATOMIC_UPDATE_VSYNC (1 << 18) +#define RADEON_PPLL_DIV_0 0x0004 /* PLL */ +#define RADEON_PPLL_DIV_1 0x0005 /* PLL */ +#define RADEON_PPLL_DIV_2 0x0006 /* PLL */ +#define RADEON_PPLL_DIV_3 0x0007 /* PLL */ +# define RADEON_PPLL_FB3_DIV_MASK 0x07ff +# define RADEON_PPLL_POST3_DIV_MASK 0x00070000 +#define RADEON_PPLL_REF_DIV 0x0003 /* PLL */ +# define RADEON_PPLL_REF_DIV_MASK 0x03ff +# define RADEON_PPLL_ATOMIC_UPDATE_R (1 << 15) /* same as _W */ +# define RADEON_PPLL_ATOMIC_UPDATE_W (1 << 15) /* same as _R */ +#define RADEON_PWR_MNGMT_CNTL_STATUS 0x0f60 /* PCI */ + +#define RADEON_RBBM_GUICNTL 0x172c +# define RADEON_HOST_DATA_SWAP_NONE (0 << 0) +# define RADEON_HOST_DATA_SWAP_16BIT (1 << 0) +# define RADEON_HOST_DATA_SWAP_32BIT (2 << 0) +# define RADEON_HOST_DATA_SWAP_HDW (3 << 0) +#define RADEON_RBBM_SOFT_RESET 0x00f0 +# define RADEON_SOFT_RESET_CP (1 << 0) +# define RADEON_SOFT_RESET_HI (1 << 1) +# define RADEON_SOFT_RESET_SE (1 << 2) +# define RADEON_SOFT_RESET_RE (1 << 3) +# define RADEON_SOFT_RESET_PP (1 << 4) +# define RADEON_SOFT_RESET_E2 (1 << 5) +# define RADEON_SOFT_RESET_RB (1 << 6) +# define RADEON_SOFT_RESET_HDP (1 << 7) +#define RADEON_RBBM_STATUS 0x0e40 +# define RADEON_RBBM_FIFOCNT_MASK 0x007f +# define RADEON_RBBM_ACTIVE (1 << 31) +#define RADEON_RB2D_DSTCACHE_CTLSTAT 0x342c +# define RADEON_RB2D_DC_FLUSH (3 << 0) +# define RADEON_RB2D_DC_FREE (3 << 2) +# define RADEON_RB2D_DC_FLUSH_ALL 0xf +# define RADEON_RB2D_DC_BUSY (1 << 31) +#define RADEON_RB2D_DSTCACHE_MODE 0x3428 +#define RADEON_DSTCACHE_CTLSTAT 0x1714 + +#define RADEON_RB3D_ZCACHE_MODE 0x3250 +#define RADEON_RB3D_ZCACHE_CTLSTAT 0x3254 +# define RADEON_RB3D_ZC_FLUSH_ALL 0x5 +#define RADEON_RB3D_DSTCACHE_MODE 0x3258 +# define RADEON_RB3D_DC_CACHE_ENABLE (0) +# define RADEON_RB3D_DC_2D_CACHE_DISABLE (1) +# define RADEON_RB3D_DC_3D_CACHE_DISABLE (2) +# define RADEON_RB3D_DC_CACHE_DISABLE (3) +# define RADEON_RB3D_DC_2D_CACHE_LINESIZE_128 (1 << 2) +# define RADEON_RB3D_DC_3D_CACHE_LINESIZE_128 (2 << 2) +# define RADEON_RB3D_DC_2D_CACHE_AUTOFLUSH (1 << 8) +# define RADEON_RB3D_DC_3D_CACHE_AUTOFLUSH (2 << 8) +# define R200_RB3D_DC_2D_CACHE_AUTOFREE (1 << 10) +# define R200_RB3D_DC_3D_CACHE_AUTOFREE (2 << 10) +# define RADEON_RB3D_DC_FORCE_RMW (1 << 16) +# define RADEON_RB3D_DC_DISABLE_RI_FILL (1 << 24) +# define RADEON_RB3D_DC_DISABLE_RI_READ (1 << 25) + +#define RADEON_RB3D_DSTCACHE_CTLSTAT 0x325C +# define RADEON_RB3D_DC_FLUSH (3 << 0) +# define RADEON_RB3D_DC_FREE (3 << 2) +# define RADEON_RB3D_DC_FLUSH_ALL 0xf +# define RADEON_RB3D_DC_BUSY (1 << 31) + +#define RADEON_REG_BASE 0x0f18 /* PCI */ +#define RADEON_REGPROG_INF 0x0f09 /* PCI */ +#define RADEON_REVISION_ID 0x0f08 /* PCI */ + +#define RADEON_SC_BOTTOM 0x164c +#define RADEON_SC_BOTTOM_RIGHT 0x16f0 +#define RADEON_SC_BOTTOM_RIGHT_C 0x1c8c +#define RADEON_SC_LEFT 0x1640 +#define RADEON_SC_RIGHT 0x1644 +#define RADEON_SC_TOP 0x1648 +#define RADEON_SC_TOP_LEFT 0x16ec +#define RADEON_SC_TOP_LEFT_C 0x1c88 +# define RADEON_SC_SIGN_MASK_LO 0x8000 +# define RADEON_SC_SIGN_MASK_HI 0x80000000 +#define RADEON_M_SPLL_REF_FB_DIV 0x000a /* PLL */ +# define RADEON_M_SPLL_REF_DIV_SHIFT 0 +# define RADEON_M_SPLL_REF_DIV_MASK 0xff +# define RADEON_MPLL_FB_DIV_SHIFT 8 +# define RADEON_MPLL_FB_DIV_MASK 0xff +# define RADEON_SPLL_FB_DIV_SHIFT 16 +# define RADEON_SPLL_FB_DIV_MASK 0xff +#define RADEON_SCLK_CNTL 0x000d /* PLL */ +# define RADEON_SCLK_SRC_SEL_MASK 0x0007 +# define RADEON_DYN_STOP_LAT_MASK 0x00007ff8 +# define RADEON_CP_MAX_DYN_STOP_LAT 0x0008 +# define RADEON_SCLK_FORCEON_MASK 0xffff8000 +# define RADEON_SCLK_FORCE_DISP2 (1<<15) +# define RADEON_SCLK_FORCE_CP (1<<16) +# define RADEON_SCLK_FORCE_HDP (1<<17) +# define RADEON_SCLK_FORCE_DISP1 (1<<18) +# define RADEON_SCLK_FORCE_TOP (1<<19) +# define RADEON_SCLK_FORCE_E2 (1<<20) +# define RADEON_SCLK_FORCE_SE (1<<21) +# define RADEON_SCLK_FORCE_IDCT (1<<22) +# define RADEON_SCLK_FORCE_VIP (1<<23) +# define RADEON_SCLK_FORCE_RE (1<<24) +# define RADEON_SCLK_FORCE_PB (1<<25) +# define RADEON_SCLK_FORCE_TAM (1<<26) +# define RADEON_SCLK_FORCE_TDM (1<<27) +# define RADEON_SCLK_FORCE_RB (1<<28) +# define RADEON_SCLK_FORCE_TV_SCLK (1<<29) +# define RADEON_SCLK_FORCE_SUBPIC (1<<30) +# define RADEON_SCLK_FORCE_OV0 (1<<31) +# define R300_SCLK_FORCE_VAP (1<<21) +# define R300_SCLK_FORCE_SR (1<<25) +# define R300_SCLK_FORCE_PX (1<<26) +# define R300_SCLK_FORCE_TX (1<<27) +# define R300_SCLK_FORCE_US (1<<28) +# define R300_SCLK_FORCE_SU (1<<30) +#define R300_SCLK_CNTL2 0x1e /* PLL */ +# define R300_SCLK_TCL_MAX_DYN_STOP_LAT (1<<10) +# define R300_SCLK_GA_MAX_DYN_STOP_LAT (1<<11) +# define R300_SCLK_CBA_MAX_DYN_STOP_LAT (1<<12) +# define R300_SCLK_FORCE_TCL (1<<13) +# define R300_SCLK_FORCE_CBA (1<<14) +# define R300_SCLK_FORCE_GA (1<<15) +#define RADEON_SCLK_MORE_CNTL 0x0035 /* PLL */ +# define RADEON_SCLK_MORE_MAX_DYN_STOP_LAT 0x0007 +# define RADEON_SCLK_MORE_FORCEON 0x0700 +#define RADEON_SDRAM_MODE_REG 0x0158 +#define RADEON_SEQ8_DATA 0x03c5 /* VGA */ +#define RADEON_SEQ8_IDX 0x03c4 /* VGA */ +#define RADEON_SNAPSHOT_F_COUNT 0x0244 +#define RADEON_SNAPSHOT_VH_COUNTS 0x0240 +#define RADEON_SNAPSHOT_VIF_COUNT 0x024c +#define RADEON_SRC_OFFSET 0x15ac +#define RADEON_SRC_PITCH 0x15b0 +#define RADEON_SRC_PITCH_OFFSET 0x1428 +#define RADEON_SRC_SC_BOTTOM 0x165c +#define RADEON_SRC_SC_BOTTOM_RIGHT 0x16f4 +#define RADEON_SRC_SC_RIGHT 0x1654 +#define RADEON_SRC_X 0x1414 +#define RADEON_SRC_X_Y 0x1590 +#define RADEON_SRC_Y 0x1418 +#define RADEON_SRC_Y_X 0x1434 +#define RADEON_STATUS 0x0f06 /* PCI */ +#define RADEON_SUBPIC_CNTL 0x0540 /* ? */ +#define RADEON_SUB_CLASS 0x0f0a /* PCI */ +#define RADEON_SURFACE_CNTL 0x0b00 +# define RADEON_SURF_TRANSLATION_DIS (1 << 8) +# define RADEON_NONSURF_AP0_SWP_16BPP (1 << 20) +# define RADEON_NONSURF_AP0_SWP_32BPP (1 << 21) +# define RADEON_NONSURF_AP1_SWP_16BPP (1 << 22) +# define RADEON_NONSURF_AP1_SWP_32BPP (1 << 23) +#define RADEON_SURFACE0_INFO 0x0b0c +# define RADEON_SURF_TILE_COLOR_MACRO (0 << 16) +# define RADEON_SURF_TILE_COLOR_BOTH (1 << 16) +# define RADEON_SURF_TILE_DEPTH_32BPP (2 << 16) +# define RADEON_SURF_TILE_DEPTH_16BPP (3 << 16) +# define R200_SURF_TILE_NONE (0 << 16) +# define R200_SURF_TILE_COLOR_MACRO (1 << 16) +# define R200_SURF_TILE_COLOR_MICRO (2 << 16) +# define R200_SURF_TILE_COLOR_BOTH (3 << 16) +# define R200_SURF_TILE_DEPTH_32BPP (4 << 16) +# define R200_SURF_TILE_DEPTH_16BPP (5 << 16) +# define R300_SURF_TILE_NONE (0 << 16) +# define R300_SURF_TILE_COLOR_MACRO (1 << 16) +# define R300_SURF_TILE_DEPTH_32BPP (2 << 16) +# define RADEON_SURF_AP0_SWP_16BPP (1 << 20) +# define RADEON_SURF_AP0_SWP_32BPP (1 << 21) +# define RADEON_SURF_AP1_SWP_16BPP (1 << 22) +# define RADEON_SURF_AP1_SWP_32BPP (1 << 23) +#define RADEON_SURFACE0_LOWER_BOUND 0x0b04 +#define RADEON_SURFACE0_UPPER_BOUND 0x0b08 +#define RADEON_SURFACE1_INFO 0x0b1c +#define RADEON_SURFACE1_LOWER_BOUND 0x0b14 +#define RADEON_SURFACE1_UPPER_BOUND 0x0b18 +#define RADEON_SURFACE2_INFO 0x0b2c +#define RADEON_SURFACE2_LOWER_BOUND 0x0b24 +#define RADEON_SURFACE2_UPPER_BOUND 0x0b28 +#define RADEON_SURFACE3_INFO 0x0b3c +#define RADEON_SURFACE3_LOWER_BOUND 0x0b34 +#define RADEON_SURFACE3_UPPER_BOUND 0x0b38 +#define RADEON_SURFACE4_INFO 0x0b4c +#define RADEON_SURFACE4_LOWER_BOUND 0x0b44 +#define RADEON_SURFACE4_UPPER_BOUND 0x0b48 +#define RADEON_SURFACE5_INFO 0x0b5c +#define RADEON_SURFACE5_LOWER_BOUND 0x0b54 +#define RADEON_SURFACE5_UPPER_BOUND 0x0b58 +#define RADEON_SURFACE6_INFO 0x0b6c +#define RADEON_SURFACE6_LOWER_BOUND 0x0b64 +#define RADEON_SURFACE6_UPPER_BOUND 0x0b68 +#define RADEON_SURFACE7_INFO 0x0b7c +#define RADEON_SURFACE7_LOWER_BOUND 0x0b74 +#define RADEON_SURFACE7_UPPER_BOUND 0x0b78 +#define RADEON_SW_SEMAPHORE 0x013c + +#define RADEON_TEST_DEBUG_CNTL 0x0120 +#define RADEON_TEST_DEBUG_CNTL__TEST_DEBUG_OUT_EN 0x00000001 + +#define RADEON_TEST_DEBUG_MUX 0x0124 +#define RADEON_TEST_DEBUG_OUT 0x012c +#define RADEON_TMDS_PLL_CNTL 0x02a8 +#define RADEON_TMDS_TRANSMITTER_CNTL 0x02a4 +# define RADEON_TMDS_TRANSMITTER_PLLEN 1 +# define RADEON_TMDS_TRANSMITTER_PLLRST 2 +#define RADEON_TRAIL_BRES_DEC 0x1614 +#define RADEON_TRAIL_BRES_ERR 0x160c +#define RADEON_TRAIL_BRES_INC 0x1610 +#define RADEON_TRAIL_X 0x1618 +#define RADEON_TRAIL_X_SUB 0x1620 + +#define RADEON_VCLK_ECP_CNTL 0x0008 /* PLL */ +# define RADEON_VCLK_SRC_SEL_MASK 0x03 +# define RADEON_VCLK_SRC_SEL_CPUCLK 0x00 +# define RADEON_VCLK_SRC_SEL_PSCANCLK 0x01 +# define RADEON_VCLK_SRC_SEL_BYTECLK 0x02 +# define RADEON_VCLK_SRC_SEL_PPLLCLK 0x03 +# define RADEON_PIXCLK_ALWAYS_ONb (1<<6) +# define RADEON_PIXCLK_DAC_ALWAYS_ONb (1<<7) +# define R300_DISP_DAC_PIXCLK_DAC_BLANK_OFF (1<<23) + +#define RADEON_VENDOR_ID 0x0f00 /* PCI */ +#define RADEON_VGA_DDA_CONFIG 0x02e8 +#define RADEON_VGA_DDA_ON_OFF 0x02ec +#define RADEON_VID_BUFFER_CONTROL 0x0900 +#define RADEON_VIDEOMUX_CNTL 0x0190 + + /* VIP bus */ +#define RADEON_VIPH_CH0_DATA 0x0c00 +#define RADEON_VIPH_CH1_DATA 0x0c04 +#define RADEON_VIPH_CH2_DATA 0x0c08 +#define RADEON_VIPH_CH3_DATA 0x0c0c +#define RADEON_VIPH_CH0_ADDR 0x0c10 +#define RADEON_VIPH_CH1_ADDR 0x0c14 +#define RADEON_VIPH_CH2_ADDR 0x0c18 +#define RADEON_VIPH_CH3_ADDR 0x0c1c +#define RADEON_VIPH_CH0_SBCNT 0x0c20 +#define RADEON_VIPH_CH1_SBCNT 0x0c24 +#define RADEON_VIPH_CH2_SBCNT 0x0c28 +#define RADEON_VIPH_CH3_SBCNT 0x0c2c +#define RADEON_VIPH_CH0_ABCNT 0x0c30 +#define RADEON_VIPH_CH1_ABCNT 0x0c34 +#define RADEON_VIPH_CH2_ABCNT 0x0c38 +#define RADEON_VIPH_CH3_ABCNT 0x0c3c +#define RADEON_VIPH_CONTROL 0x0c40 +# define RADEON_VIP_BUSY 0 +# define RADEON_VIP_IDLE 1 +# define RADEON_VIP_RESET 2 +# define RADEON_VIPH_EN (1 << 21) +#define RADEON_VIPH_DV_LAT 0x0c44 +#define RADEON_VIPH_BM_CHUNK 0x0c48 +#define RADEON_VIPH_DV_INT 0x0c4c +#define RADEON_VIPH_TIMEOUT_STAT 0x0c50 +#define RADEON_VIPH_TIMEOUT_STAT__VIPH_REG_STAT 0x00000010 +#define RADEON_VIPH_TIMEOUT_STAT__VIPH_REG_AK 0x00000010 +#define RADEON_VIPH_TIMEOUT_STAT__VIPH_REGR_DIS 0x01000000 + +#define RADEON_VIPH_REG_DATA 0x0084 +#define RADEON_VIPH_REG_ADDR 0x0080 + + +#define RADEON_WAIT_UNTIL 0x1720 +# define RADEON_WAIT_CRTC_PFLIP (1 << 0) +# define RADEON_WAIT_RE_CRTC_VLINE (1 << 1) +# define RADEON_WAIT_FE_CRTC_VLINE (1 << 2) +# define RADEON_WAIT_CRTC_VLINE (1 << 3) +# define RADEON_WAIT_DMA_VID_IDLE (1 << 8) +# define RADEON_WAIT_DMA_GUI_IDLE (1 << 9) +# define RADEON_WAIT_CMDFIFO (1 << 10) /* wait for CMDFIFO_ENTRIES */ +# define RADEON_WAIT_OV0_FLIP (1 << 11) +# define RADEON_WAIT_AGP_FLUSH (1 << 13) +# define RADEON_WAIT_2D_IDLE (1 << 14) +# define RADEON_WAIT_3D_IDLE (1 << 15) +# define RADEON_WAIT_2D_IDLECLEAN (1 << 16) +# define RADEON_WAIT_3D_IDLECLEAN (1 << 17) +# define RADEON_WAIT_HOST_IDLECLEAN (1 << 18) +# define RADEON_CMDFIFO_ENTRIES_SHIFT 10 +# define RADEON_CMDFIFO_ENTRIES_MASK 0x7f +# define RADEON_WAIT_VAP_IDLE (1 << 28) +# define RADEON_WAIT_BOTH_CRTC_PFLIP (1 << 30) +# define RADEON_ENG_DISPLAY_SELECT_CRTC0 (0 << 31) +# define RADEON_ENG_DISPLAY_SELECT_CRTC1 (1 << 31) + +#define RADEON_X_MPLL_REF_FB_DIV 0x000a /* PLL */ +#define RADEON_XCLK_CNTL 0x000d /* PLL */ +#define RADEON_XDLL_CNTL 0x000c /* PLL */ +#define RADEON_XPLL_CNTL 0x000b /* PLL */ + + + + /* Registers for 3D/TCL */ +#define RADEON_PP_BORDER_COLOR_0 0x1d40 +#define RADEON_PP_BORDER_COLOR_1 0x1d44 +#define RADEON_PP_BORDER_COLOR_2 0x1d48 +#define RADEON_PP_CNTL 0x1c38 +# define RADEON_STIPPLE_ENABLE (1 << 0) +# define RADEON_SCISSOR_ENABLE (1 << 1) +# define RADEON_PATTERN_ENABLE (1 << 2) +# define RADEON_SHADOW_ENABLE (1 << 3) +# define RADEON_TEX_ENABLE_MASK (0xf << 4) +# define RADEON_TEX_0_ENABLE (1 << 4) +# define RADEON_TEX_1_ENABLE (1 << 5) +# define RADEON_TEX_2_ENABLE (1 << 6) +# define RADEON_TEX_3_ENABLE (1 << 7) +# define RADEON_TEX_BLEND_ENABLE_MASK (0xf << 12) +# define RADEON_TEX_BLEND_0_ENABLE (1 << 12) +# define RADEON_TEX_BLEND_1_ENABLE (1 << 13) +# define RADEON_TEX_BLEND_2_ENABLE (1 << 14) +# define RADEON_TEX_BLEND_3_ENABLE (1 << 15) +# define RADEON_PLANAR_YUV_ENABLE (1 << 20) +# define RADEON_SPECULAR_ENABLE (1 << 21) +# define RADEON_FOG_ENABLE (1 << 22) +# define RADEON_ALPHA_TEST_ENABLE (1 << 23) +# define RADEON_ANTI_ALIAS_NONE (0 << 24) +# define RADEON_ANTI_ALIAS_LINE (1 << 24) +# define RADEON_ANTI_ALIAS_POLY (2 << 24) +# define RADEON_ANTI_ALIAS_LINE_POLY (3 << 24) +# define RADEON_BUMP_MAP_ENABLE (1 << 26) +# define RADEON_BUMPED_MAP_T0 (0 << 27) +# define RADEON_BUMPED_MAP_T1 (1 << 27) +# define RADEON_BUMPED_MAP_T2 (2 << 27) +# define RADEON_TEX_3D_ENABLE_0 (1 << 29) +# define RADEON_TEX_3D_ENABLE_1 (1 << 30) +# define RADEON_MC_ENABLE (1 << 31) +#define RADEON_PP_FOG_COLOR 0x1c18 +# define RADEON_FOG_COLOR_MASK 0x00ffffff +# define RADEON_FOG_VERTEX (0 << 24) +# define RADEON_FOG_TABLE (1 << 24) +# define RADEON_FOG_USE_DEPTH (0 << 25) +# define RADEON_FOG_USE_DIFFUSE_ALPHA (2 << 25) +# define RADEON_FOG_USE_SPEC_ALPHA (3 << 25) +#define RADEON_PP_LUM_MATRIX 0x1d00 +#define RADEON_PP_MISC 0x1c14 +# define RADEON_REF_ALPHA_MASK 0x000000ff +# define RADEON_ALPHA_TEST_FAIL (0 << 8) +# define RADEON_ALPHA_TEST_LESS (1 << 8) +# define RADEON_ALPHA_TEST_LEQUAL (2 << 8) +# define RADEON_ALPHA_TEST_EQUAL (3 << 8) +# define RADEON_ALPHA_TEST_GEQUAL (4 << 8) +# define RADEON_ALPHA_TEST_GREATER (5 << 8) +# define RADEON_ALPHA_TEST_NEQUAL (6 << 8) +# define RADEON_ALPHA_TEST_PASS (7 << 8) +# define RADEON_ALPHA_TEST_OP_MASK (7 << 8) +# define RADEON_CHROMA_FUNC_FAIL (0 << 16) +# define RADEON_CHROMA_FUNC_PASS (1 << 16) +# define RADEON_CHROMA_FUNC_NEQUAL (2 << 16) +# define RADEON_CHROMA_FUNC_EQUAL (3 << 16) +# define RADEON_CHROMA_KEY_NEAREST (0 << 18) +# define RADEON_CHROMA_KEY_ZERO (1 << 18) +# define RADEON_SHADOW_ID_AUTO_INC (1 << 20) +# define RADEON_SHADOW_FUNC_EQUAL (0 << 21) +# define RADEON_SHADOW_FUNC_NEQUAL (1 << 21) +# define RADEON_SHADOW_PASS_1 (0 << 22) +# define RADEON_SHADOW_PASS_2 (1 << 22) +# define RADEON_RIGHT_HAND_CUBE_D3D (0 << 24) +# define RADEON_RIGHT_HAND_CUBE_OGL (1 << 24) +#define RADEON_PP_ROT_MATRIX_0 0x1d58 +#define RADEON_PP_ROT_MATRIX_1 0x1d5c +#define RADEON_PP_TXFILTER_0 0x1c54 +#define RADEON_PP_TXFILTER_1 0x1c6c +#define RADEON_PP_TXFILTER_2 0x1c84 +# define RADEON_MAG_FILTER_NEAREST (0 << 0) +# define RADEON_MAG_FILTER_LINEAR (1 << 0) +# define RADEON_MAG_FILTER_MASK (1 << 0) +# define RADEON_MIN_FILTER_NEAREST (0 << 1) +# define RADEON_MIN_FILTER_LINEAR (1 << 1) +# define RADEON_MIN_FILTER_NEAREST_MIP_NEAREST (2 << 1) +# define RADEON_MIN_FILTER_NEAREST_MIP_LINEAR (3 << 1) +# define RADEON_MIN_FILTER_LINEAR_MIP_NEAREST (6 << 1) +# define RADEON_MIN_FILTER_LINEAR_MIP_LINEAR (7 << 1) +# define RADEON_MIN_FILTER_ANISO_NEAREST (8 << 1) +# define RADEON_MIN_FILTER_ANISO_LINEAR (9 << 1) +# define RADEON_MIN_FILTER_ANISO_NEAREST_MIP_NEAREST (10 << 1) +# define RADEON_MIN_FILTER_ANISO_NEAREST_MIP_LINEAR (11 << 1) +# define RADEON_MIN_FILTER_MASK (15 << 1) +# define RADEON_MAX_ANISO_1_TO_1 (0 << 5) +# define RADEON_MAX_ANISO_2_TO_1 (1 << 5) +# define RADEON_MAX_ANISO_4_TO_1 (2 << 5) +# define RADEON_MAX_ANISO_8_TO_1 (3 << 5) +# define RADEON_MAX_ANISO_16_TO_1 (4 << 5) +# define RADEON_MAX_ANISO_MASK (7 << 5) +# define RADEON_LOD_BIAS_MASK (0xff << 8) +# define RADEON_LOD_BIAS_SHIFT 8 +# define RADEON_MAX_MIP_LEVEL_MASK (0x0f << 16) +# define RADEON_MAX_MIP_LEVEL_SHIFT 16 +# define RADEON_YUV_TO_RGB (1 << 20) +# define RADEON_YUV_TEMPERATURE_COOL (0 << 21) +# define RADEON_YUV_TEMPERATURE_HOT (1 << 21) +# define RADEON_YUV_TEMPERATURE_MASK (1 << 21) +# define RADEON_WRAPEN_S (1 << 22) +# define RADEON_CLAMP_S_WRAP (0 << 23) +# define RADEON_CLAMP_S_MIRROR (1 << 23) +# define RADEON_CLAMP_S_CLAMP_LAST (2 << 23) +# define RADEON_CLAMP_S_MIRROR_CLAMP_LAST (3 << 23) +# define RADEON_CLAMP_S_CLAMP_BORDER (4 << 23) +# define RADEON_CLAMP_S_MIRROR_CLAMP_BORDER (5 << 23) +# define RADEON_CLAMP_S_CLAMP_GL (6 << 23) +# define RADEON_CLAMP_S_MIRROR_CLAMP_GL (7 << 23) +# define RADEON_CLAMP_S_MASK (7 << 23) +# define RADEON_WRAPEN_T (1 << 26) +# define RADEON_CLAMP_T_WRAP (0 << 27) +# define RADEON_CLAMP_T_MIRROR (1 << 27) +# define RADEON_CLAMP_T_CLAMP_LAST (2 << 27) +# define RADEON_CLAMP_T_MIRROR_CLAMP_LAST (3 << 27) +# define RADEON_CLAMP_T_CLAMP_BORDER (4 << 27) +# define RADEON_CLAMP_T_MIRROR_CLAMP_BORDER (5 << 27) +# define RADEON_CLAMP_T_CLAMP_GL (6 << 27) +# define RADEON_CLAMP_T_MIRROR_CLAMP_GL (7 << 27) +# define RADEON_CLAMP_T_MASK (7 << 27) +# define RADEON_BORDER_MODE_OGL (0 << 31) +# define RADEON_BORDER_MODE_D3D (1 << 31) +#define RADEON_PP_TXFORMAT_0 0x1c58 +#define RADEON_PP_TXFORMAT_1 0x1c70 +#define RADEON_PP_TXFORMAT_2 0x1c88 +# define RADEON_TXFORMAT_I8 (0 << 0) +# define RADEON_TXFORMAT_AI88 (1 << 0) +# define RADEON_TXFORMAT_RGB332 (2 << 0) +# define RADEON_TXFORMAT_ARGB1555 (3 << 0) +# define RADEON_TXFORMAT_RGB565 (4 << 0) +# define RADEON_TXFORMAT_ARGB4444 (5 << 0) +# define RADEON_TXFORMAT_ARGB8888 (6 << 0) +# define RADEON_TXFORMAT_RGBA8888 (7 << 0) +# define RADEON_TXFORMAT_Y8 (8 << 0) +# define RADEON_TXFORMAT_VYUY422 (10 << 0) +# define RADEON_TXFORMAT_YVYU422 (11 << 0) +# define RADEON_TXFORMAT_DXT1 (12 << 0) +# define RADEON_TXFORMAT_DXT23 (14 << 0) +# define RADEON_TXFORMAT_DXT45 (15 << 0) +# define RADEON_TXFORMAT_FORMAT_MASK (31 << 0) +# define RADEON_TXFORMAT_FORMAT_SHIFT 0 +# define RADEON_TXFORMAT_APPLE_YUV_MODE (1 << 5) +# define RADEON_TXFORMAT_ALPHA_IN_MAP (1 << 6) +# define RADEON_TXFORMAT_NON_POWER2 (1 << 7) +# define RADEON_TXFORMAT_WIDTH_MASK (15 << 8) +# define RADEON_TXFORMAT_WIDTH_SHIFT 8 +# define RADEON_TXFORMAT_HEIGHT_MASK (15 << 12) +# define RADEON_TXFORMAT_HEIGHT_SHIFT 12 +# define RADEON_TXFORMAT_F5_WIDTH_MASK (15 << 16) +# define RADEON_TXFORMAT_F5_WIDTH_SHIFT 16 +# define RADEON_TXFORMAT_F5_HEIGHT_MASK (15 << 20) +# define RADEON_TXFORMAT_F5_HEIGHT_SHIFT 20 +# define RADEON_TXFORMAT_ST_ROUTE_STQ0 (0 << 24) +# define RADEON_TXFORMAT_ST_ROUTE_MASK (3 << 24) +# define RADEON_TXFORMAT_ST_ROUTE_STQ1 (1 << 24) +# define RADEON_TXFORMAT_ST_ROUTE_STQ2 (2 << 24) +# define RADEON_TXFORMAT_ENDIAN_NO_SWAP (0 << 26) +# define RADEON_TXFORMAT_ENDIAN_16BPP_SWAP (1 << 26) +# define RADEON_TXFORMAT_ENDIAN_32BPP_SWAP (2 << 26) +# define RADEON_TXFORMAT_ENDIAN_HALFDW_SWAP (3 << 26) +# define RADEON_TXFORMAT_ALPHA_MASK_ENABLE (1 << 28) +# define RADEON_TXFORMAT_CHROMA_KEY_ENABLE (1 << 29) +# define RADEON_TXFORMAT_CUBIC_MAP_ENABLE (1 << 30) +# define RADEON_TXFORMAT_PERSPECTIVE_ENABLE (1 << 31) +#define RADEON_PP_CUBIC_FACES_0 0x1d24 +#define RADEON_PP_CUBIC_FACES_1 0x1d28 +#define RADEON_PP_CUBIC_FACES_2 0x1d2c +# define RADEON_FACE_WIDTH_1_SHIFT 0 +# define RADEON_FACE_HEIGHT_1_SHIFT 4 +# define RADEON_FACE_WIDTH_1_MASK (0xf << 0) +# define RADEON_FACE_HEIGHT_1_MASK (0xf << 4) +# define RADEON_FACE_WIDTH_2_SHIFT 8 +# define RADEON_FACE_HEIGHT_2_SHIFT 12 +# define RADEON_FACE_WIDTH_2_MASK (0xf << 8) +# define RADEON_FACE_HEIGHT_2_MASK (0xf << 12) +# define RADEON_FACE_WIDTH_3_SHIFT 16 +# define RADEON_FACE_HEIGHT_3_SHIFT 20 +# define RADEON_FACE_WIDTH_3_MASK (0xf << 16) +# define RADEON_FACE_HEIGHT_3_MASK (0xf << 20) +# define RADEON_FACE_WIDTH_4_SHIFT 24 +# define RADEON_FACE_HEIGHT_4_SHIFT 28 +# define RADEON_FACE_WIDTH_4_MASK (0xf << 24) +# define RADEON_FACE_HEIGHT_4_MASK (0xf << 28) + +#define RADEON_PP_TXOFFSET_0 0x1c5c +#define RADEON_PP_TXOFFSET_1 0x1c74 +#define RADEON_PP_TXOFFSET_2 0x1c8c +# define RADEON_TXO_ENDIAN_NO_SWAP (0 << 0) +# define RADEON_TXO_ENDIAN_BYTE_SWAP (1 << 0) +# define RADEON_TXO_ENDIAN_WORD_SWAP (2 << 0) +# define RADEON_TXO_ENDIAN_HALFDW_SWAP (3 << 0) +# define RADEON_TXO_MACRO_LINEAR (0 << 2) +# define RADEON_TXO_MACRO_TILE (1 << 2) +# define RADEON_TXO_MICRO_LINEAR (0 << 3) +# define RADEON_TXO_MICRO_TILE_X2 (1 << 3) +# define RADEON_TXO_MICRO_TILE_OPT (2 << 3) +# define RADEON_TXO_OFFSET_MASK 0xffffffe0 +# define RADEON_TXO_OFFSET_SHIFT 5 + +#define RADEON_PP_CUBIC_OFFSET_T0_0 0x1dd0 /* bits [31:5] */ +#define RADEON_PP_CUBIC_OFFSET_T0_1 0x1dd4 +#define RADEON_PP_CUBIC_OFFSET_T0_2 0x1dd8 +#define RADEON_PP_CUBIC_OFFSET_T0_3 0x1ddc +#define RADEON_PP_CUBIC_OFFSET_T0_4 0x1de0 +#define RADEON_PP_CUBIC_OFFSET_T1_0 0x1e00 +#define RADEON_PP_CUBIC_OFFSET_T1_1 0x1e04 +#define RADEON_PP_CUBIC_OFFSET_T1_2 0x1e08 +#define RADEON_PP_CUBIC_OFFSET_T1_3 0x1e0c +#define RADEON_PP_CUBIC_OFFSET_T1_4 0x1e10 +#define RADEON_PP_CUBIC_OFFSET_T2_0 0x1e14 +#define RADEON_PP_CUBIC_OFFSET_T2_1 0x1e18 +#define RADEON_PP_CUBIC_OFFSET_T2_2 0x1e1c +#define RADEON_PP_CUBIC_OFFSET_T2_3 0x1e20 +#define RADEON_PP_CUBIC_OFFSET_T2_4 0x1e24 + +#define RADEON_PP_TEX_SIZE_0 0x1d04 /* NPOT */ +#define RADEON_PP_TEX_SIZE_1 0x1d0c +#define RADEON_PP_TEX_SIZE_2 0x1d14 +# define RADEON_TEX_USIZE_MASK (0x7ff << 0) +# define RADEON_TEX_USIZE_SHIFT 0 +# define RADEON_TEX_VSIZE_MASK (0x7ff << 16) +# define RADEON_TEX_VSIZE_SHIFT 16 +# define RADEON_SIGNED_RGB_MASK (1 << 30) +# define RADEON_SIGNED_RGB_SHIFT 30 +# define RADEON_SIGNED_ALPHA_MASK (1 << 31) +# define RADEON_SIGNED_ALPHA_SHIFT 31 +#define RADEON_PP_TEX_PITCH_0 0x1d08 /* NPOT */ +#define RADEON_PP_TEX_PITCH_1 0x1d10 /* NPOT */ +#define RADEON_PP_TEX_PITCH_2 0x1d18 /* NPOT */ +/* note: bits 13-5: 32 byte aligned stride of texture map */ + +#define RADEON_PP_TXCBLEND_0 0x1c60 +#define RADEON_PP_TXCBLEND_1 0x1c78 +#define RADEON_PP_TXCBLEND_2 0x1c90 +# define RADEON_COLOR_ARG_A_SHIFT 0 +# define RADEON_COLOR_ARG_A_MASK (0x1f << 0) +# define RADEON_COLOR_ARG_A_ZERO (0 << 0) +# define RADEON_COLOR_ARG_A_CURRENT_COLOR (2 << 0) +# define RADEON_COLOR_ARG_A_CURRENT_ALPHA (3 << 0) +# define RADEON_COLOR_ARG_A_DIFFUSE_COLOR (4 << 0) +# define RADEON_COLOR_ARG_A_DIFFUSE_ALPHA (5 << 0) +# define RADEON_COLOR_ARG_A_SPECULAR_COLOR (6 << 0) +# define RADEON_COLOR_ARG_A_SPECULAR_ALPHA (7 << 0) +# define RADEON_COLOR_ARG_A_TFACTOR_COLOR (8 << 0) +# define RADEON_COLOR_ARG_A_TFACTOR_ALPHA (9 << 0) +# define RADEON_COLOR_ARG_A_T0_COLOR (10 << 0) +# define RADEON_COLOR_ARG_A_T0_ALPHA (11 << 0) +# define RADEON_COLOR_ARG_A_T1_COLOR (12 << 0) +# define RADEON_COLOR_ARG_A_T1_ALPHA (13 << 0) +# define RADEON_COLOR_ARG_A_T2_COLOR (14 << 0) +# define RADEON_COLOR_ARG_A_T2_ALPHA (15 << 0) +# define RADEON_COLOR_ARG_A_T3_COLOR (16 << 0) +# define RADEON_COLOR_ARG_A_T3_ALPHA (17 << 0) +# define RADEON_COLOR_ARG_B_SHIFT 5 +# define RADEON_COLOR_ARG_B_MASK (0x1f << 5) +# define RADEON_COLOR_ARG_B_ZERO (0 << 5) +# define RADEON_COLOR_ARG_B_CURRENT_COLOR (2 << 5) +# define RADEON_COLOR_ARG_B_CURRENT_ALPHA (3 << 5) +# define RADEON_COLOR_ARG_B_DIFFUSE_COLOR (4 << 5) +# define RADEON_COLOR_ARG_B_DIFFUSE_ALPHA (5 << 5) +# define RADEON_COLOR_ARG_B_SPECULAR_COLOR (6 << 5) +# define RADEON_COLOR_ARG_B_SPECULAR_ALPHA (7 << 5) +# define RADEON_COLOR_ARG_B_TFACTOR_COLOR (8 << 5) +# define RADEON_COLOR_ARG_B_TFACTOR_ALPHA (9 << 5) +# define RADEON_COLOR_ARG_B_T0_COLOR (10 << 5) +# define RADEON_COLOR_ARG_B_T0_ALPHA (11 << 5) +# define RADEON_COLOR_ARG_B_T1_COLOR (12 << 5) +# define RADEON_COLOR_ARG_B_T1_ALPHA (13 << 5) +# define RADEON_COLOR_ARG_B_T2_COLOR (14 << 5) +# define RADEON_COLOR_ARG_B_T2_ALPHA (15 << 5) +# define RADEON_COLOR_ARG_B_T3_COLOR (16 << 5) +# define RADEON_COLOR_ARG_B_T3_ALPHA (17 << 5) +# define RADEON_COLOR_ARG_C_SHIFT 10 +# define RADEON_COLOR_ARG_C_MASK (0x1f << 10) +# define RADEON_COLOR_ARG_C_ZERO (0 << 10) +# define RADEON_COLOR_ARG_C_CURRENT_COLOR (2 << 10) +# define RADEON_COLOR_ARG_C_CURRENT_ALPHA (3 << 10) +# define RADEON_COLOR_ARG_C_DIFFUSE_COLOR (4 << 10) +# define RADEON_COLOR_ARG_C_DIFFUSE_ALPHA (5 << 10) +# define RADEON_COLOR_ARG_C_SPECULAR_COLOR (6 << 10) +# define RADEON_COLOR_ARG_C_SPECULAR_ALPHA (7 << 10) +# define RADEON_COLOR_ARG_C_TFACTOR_COLOR (8 << 10) +# define RADEON_COLOR_ARG_C_TFACTOR_ALPHA (9 << 10) +# define RADEON_COLOR_ARG_C_T0_COLOR (10 << 10) +# define RADEON_COLOR_ARG_C_T0_ALPHA (11 << 10) +# define RADEON_COLOR_ARG_C_T1_COLOR (12 << 10) +# define RADEON_COLOR_ARG_C_T1_ALPHA (13 << 10) +# define RADEON_COLOR_ARG_C_T2_COLOR (14 << 10) +# define RADEON_COLOR_ARG_C_T2_ALPHA (15 << 10) +# define RADEON_COLOR_ARG_C_T3_COLOR (16 << 10) +# define RADEON_COLOR_ARG_C_T3_ALPHA (17 << 10) +# define RADEON_COMP_ARG_A (1 << 15) +# define RADEON_COMP_ARG_A_SHIFT 15 +# define RADEON_COMP_ARG_B (1 << 16) +# define RADEON_COMP_ARG_B_SHIFT 16 +# define RADEON_COMP_ARG_C (1 << 17) +# define RADEON_COMP_ARG_C_SHIFT 17 +# define RADEON_BLEND_CTL_MASK (7 << 18) +# define RADEON_BLEND_CTL_ADD (0 << 18) +# define RADEON_BLEND_CTL_SUBTRACT (1 << 18) +# define RADEON_BLEND_CTL_ADDSIGNED (2 << 18) +# define RADEON_BLEND_CTL_BLEND (3 << 18) +# define RADEON_BLEND_CTL_DOT3 (4 << 18) +# define RADEON_SCALE_SHIFT 21 +# define RADEON_SCALE_MASK (3 << 21) +# define RADEON_SCALE_1X (0 << 21) +# define RADEON_SCALE_2X (1 << 21) +# define RADEON_SCALE_4X (2 << 21) +# define RADEON_CLAMP_TX (1 << 23) +# define RADEON_T0_EQ_TCUR (1 << 24) +# define RADEON_T1_EQ_TCUR (1 << 25) +# define RADEON_T2_EQ_TCUR (1 << 26) +# define RADEON_T3_EQ_TCUR (1 << 27) +# define RADEON_COLOR_ARG_MASK 0x1f +# define RADEON_COMP_ARG_SHIFT 15 +#define RADEON_PP_TXABLEND_0 0x1c64 +#define RADEON_PP_TXABLEND_1 0x1c7c +#define RADEON_PP_TXABLEND_2 0x1c94 +# define RADEON_ALPHA_ARG_A_SHIFT 0 +# define RADEON_ALPHA_ARG_A_MASK (0xf << 0) +# define RADEON_ALPHA_ARG_A_ZERO (0 << 0) +# define RADEON_ALPHA_ARG_A_CURRENT_ALPHA (1 << 0) +# define RADEON_ALPHA_ARG_A_DIFFUSE_ALPHA (2 << 0) +# define RADEON_ALPHA_ARG_A_SPECULAR_ALPHA (3 << 0) +# define RADEON_ALPHA_ARG_A_TFACTOR_ALPHA (4 << 0) +# define RADEON_ALPHA_ARG_A_T0_ALPHA (5 << 0) +# define RADEON_ALPHA_ARG_A_T1_ALPHA (6 << 0) +# define RADEON_ALPHA_ARG_A_T2_ALPHA (7 << 0) +# define RADEON_ALPHA_ARG_A_T3_ALPHA (8 << 0) +# define RADEON_ALPHA_ARG_B_SHIFT 4 +# define RADEON_ALPHA_ARG_B_MASK (0xf << 4) +# define RADEON_ALPHA_ARG_B_ZERO (0 << 4) +# define RADEON_ALPHA_ARG_B_CURRENT_ALPHA (1 << 4) +# define RADEON_ALPHA_ARG_B_DIFFUSE_ALPHA (2 << 4) +# define RADEON_ALPHA_ARG_B_SPECULAR_ALPHA (3 << 4) +# define RADEON_ALPHA_ARG_B_TFACTOR_ALPHA (4 << 4) +# define RADEON_ALPHA_ARG_B_T0_ALPHA (5 << 4) +# define RADEON_ALPHA_ARG_B_T1_ALPHA (6 << 4) +# define RADEON_ALPHA_ARG_B_T2_ALPHA (7 << 4) +# define RADEON_ALPHA_ARG_B_T3_ALPHA (8 << 4) +# define RADEON_ALPHA_ARG_C_SHIFT 8 +# define RADEON_ALPHA_ARG_C_MASK (0xf << 8) +# define RADEON_ALPHA_ARG_C_ZERO (0 << 8) +# define RADEON_ALPHA_ARG_C_CURRENT_ALPHA (1 << 8) +# define RADEON_ALPHA_ARG_C_DIFFUSE_ALPHA (2 << 8) +# define RADEON_ALPHA_ARG_C_SPECULAR_ALPHA (3 << 8) +# define RADEON_ALPHA_ARG_C_TFACTOR_ALPHA (4 << 8) +# define RADEON_ALPHA_ARG_C_T0_ALPHA (5 << 8) +# define RADEON_ALPHA_ARG_C_T1_ALPHA (6 << 8) +# define RADEON_ALPHA_ARG_C_T2_ALPHA (7 << 8) +# define RADEON_ALPHA_ARG_C_T3_ALPHA (8 << 8) +# define RADEON_DOT_ALPHA_DONT_REPLICATE (1 << 9) +# define RADEON_ALPHA_ARG_MASK 0xf + +#define RADEON_PP_TFACTOR_0 0x1c68 +#define RADEON_PP_TFACTOR_1 0x1c80 +#define RADEON_PP_TFACTOR_2 0x1c98 + +#define RADEON_RB3D_BLENDCNTL 0x1c20 +# define RADEON_COMB_FCN_MASK (3 << 12) +# define RADEON_COMB_FCN_ADD_CLAMP (0 << 12) +# define RADEON_COMB_FCN_ADD_NOCLAMP (1 << 12) +# define RADEON_COMB_FCN_SUB_CLAMP (2 << 12) +# define RADEON_COMB_FCN_SUB_NOCLAMP (3 << 12) +# define RADEON_SRC_BLEND_GL_ZERO (32 << 16) +# define RADEON_SRC_BLEND_GL_ONE (33 << 16) +# define RADEON_SRC_BLEND_GL_SRC_COLOR (34 << 16) +# define RADEON_SRC_BLEND_GL_ONE_MINUS_SRC_COLOR (35 << 16) +# define RADEON_SRC_BLEND_GL_DST_COLOR (36 << 16) +# define RADEON_SRC_BLEND_GL_ONE_MINUS_DST_COLOR (37 << 16) +# define RADEON_SRC_BLEND_GL_SRC_ALPHA (38 << 16) +# define RADEON_SRC_BLEND_GL_ONE_MINUS_SRC_ALPHA (39 << 16) +# define RADEON_SRC_BLEND_GL_DST_ALPHA (40 << 16) +# define RADEON_SRC_BLEND_GL_ONE_MINUS_DST_ALPHA (41 << 16) +# define RADEON_SRC_BLEND_GL_SRC_ALPHA_SATURATE (42 << 16) +# define RADEON_SRC_BLEND_MASK (63 << 16) +# define RADEON_DST_BLEND_GL_ZERO (32 << 24) +# define RADEON_DST_BLEND_GL_ONE (33 << 24) +# define RADEON_DST_BLEND_GL_SRC_COLOR (34 << 24) +# define RADEON_DST_BLEND_GL_ONE_MINUS_SRC_COLOR (35 << 24) +# define RADEON_DST_BLEND_GL_DST_COLOR (36 << 24) +# define RADEON_DST_BLEND_GL_ONE_MINUS_DST_COLOR (37 << 24) +# define RADEON_DST_BLEND_GL_SRC_ALPHA (38 << 24) +# define RADEON_DST_BLEND_GL_ONE_MINUS_SRC_ALPHA (39 << 24) +# define RADEON_DST_BLEND_GL_DST_ALPHA (40 << 24) +# define RADEON_DST_BLEND_GL_ONE_MINUS_DST_ALPHA (41 << 24) +# define RADEON_DST_BLEND_MASK (63 << 24) +#define RADEON_RB3D_CNTL 0x1c3c +# define RADEON_ALPHA_BLEND_ENABLE (1 << 0) +# define RADEON_PLANE_MASK_ENABLE (1 << 1) +# define RADEON_DITHER_ENABLE (1 << 2) +# define RADEON_ROUND_ENABLE (1 << 3) +# define RADEON_SCALE_DITHER_ENABLE (1 << 4) +# define RADEON_DITHER_INIT (1 << 5) +# define RADEON_ROP_ENABLE (1 << 6) +# define RADEON_STENCIL_ENABLE (1 << 7) +# define RADEON_Z_ENABLE (1 << 8) +# define RADEON_DEPTH_XZ_OFFEST_ENABLE (1 << 9) +# define RADEON_RB3D_COLOR_FORMAT_SHIFT 10 + +# define RADEON_COLOR_FORMAT_ARGB1555 3 +# define RADEON_COLOR_FORMAT_RGB565 4 +# define RADEON_COLOR_FORMAT_ARGB8888 6 +# define RADEON_COLOR_FORMAT_RGB332 7 +# define RADEON_COLOR_FORMAT_Y8 8 +# define RADEON_COLOR_FORMAT_RGB8 9 +# define RADEON_COLOR_FORMAT_YUV422_VYUY 11 +# define RADEON_COLOR_FORMAT_YUV422_YVYU 12 +# define RADEON_COLOR_FORMAT_aYUV444 14 +# define RADEON_COLOR_FORMAT_ARGB4444 15 + +# define RADEON_CLRCMP_FLIP_ENABLE (1 << 14) +#define RADEON_RB3D_COLOROFFSET 0x1c40 +# define RADEON_COLOROFFSET_MASK 0xfffffff0 +#define RADEON_RB3D_COLORPITCH 0x1c48 +# define RADEON_COLORPITCH_MASK 0x000001ff8 +# define RADEON_COLOR_TILE_ENABLE (1 << 16) +# define RADEON_COLOR_MICROTILE_ENABLE (1 << 17) +# define RADEON_COLOR_ENDIAN_NO_SWAP (0 << 18) +# define RADEON_COLOR_ENDIAN_WORD_SWAP (1 << 18) +# define RADEON_COLOR_ENDIAN_DWORD_SWAP (2 << 18) +#define RADEON_RB3D_DEPTHOFFSET 0x1c24 +#define RADEON_RB3D_DEPTHPITCH 0x1c28 +# define RADEON_DEPTHPITCH_MASK 0x00001ff8 +# define RADEON_DEPTH_ENDIAN_NO_SWAP (0 << 18) +# define RADEON_DEPTH_ENDIAN_WORD_SWAP (1 << 18) +# define RADEON_DEPTH_ENDIAN_DWORD_SWAP (2 << 18) +#define RADEON_RB3D_PLANEMASK 0x1d84 +#define RADEON_RB3D_ROPCNTL 0x1d80 +# define RADEON_ROP_MASK (15 << 8) +# define RADEON_ROP_CLEAR (0 << 8) +# define RADEON_ROP_NOR (1 << 8) +# define RADEON_ROP_AND_INVERTED (2 << 8) +# define RADEON_ROP_COPY_INVERTED (3 << 8) +# define RADEON_ROP_AND_REVERSE (4 << 8) +# define RADEON_ROP_INVERT (5 << 8) +# define RADEON_ROP_XOR (6 << 8) +# define RADEON_ROP_NAND (7 << 8) +# define RADEON_ROP_AND (8 << 8) +# define RADEON_ROP_EQUIV (9 << 8) +# define RADEON_ROP_NOOP (10 << 8) +# define RADEON_ROP_OR_INVERTED (11 << 8) +# define RADEON_ROP_COPY (12 << 8) +# define RADEON_ROP_OR_REVERSE (13 << 8) +# define RADEON_ROP_OR (14 << 8) +# define RADEON_ROP_SET (15 << 8) +#define RADEON_RB3D_STENCILREFMASK 0x1d7c +# define RADEON_STENCIL_REF_SHIFT 0 +# define RADEON_STENCIL_REF_MASK (0xff << 0) +# define RADEON_STENCIL_MASK_SHIFT 16 +# define RADEON_STENCIL_VALUE_MASK (0xff << 16) +# define RADEON_STENCIL_WRITEMASK_SHIFT 24 +# define RADEON_STENCIL_WRITE_MASK (0xff << 24) +#define RADEON_RB3D_ZSTENCILCNTL 0x1c2c +# define RADEON_DEPTH_FORMAT_MASK (0xf << 0) +# define RADEON_DEPTH_FORMAT_16BIT_INT_Z (0 << 0) +# define RADEON_DEPTH_FORMAT_24BIT_INT_Z (2 << 0) +# define RADEON_DEPTH_FORMAT_24BIT_FLOAT_Z (3 << 0) +# define RADEON_DEPTH_FORMAT_32BIT_INT_Z (4 << 0) +# define RADEON_DEPTH_FORMAT_32BIT_FLOAT_Z (5 << 0) +# define RADEON_DEPTH_FORMAT_16BIT_FLOAT_W (7 << 0) +# define RADEON_DEPTH_FORMAT_24BIT_FLOAT_W (9 << 0) +# define RADEON_DEPTH_FORMAT_32BIT_FLOAT_W (11 << 0) +# define RADEON_Z_TEST_NEVER (0 << 4) +# define RADEON_Z_TEST_LESS (1 << 4) +# define RADEON_Z_TEST_LEQUAL (2 << 4) +# define RADEON_Z_TEST_EQUAL (3 << 4) +# define RADEON_Z_TEST_GEQUAL (4 << 4) +# define RADEON_Z_TEST_GREATER (5 << 4) +# define RADEON_Z_TEST_NEQUAL (6 << 4) +# define RADEON_Z_TEST_ALWAYS (7 << 4) +# define RADEON_Z_TEST_MASK (7 << 4) +# define RADEON_STENCIL_TEST_NEVER (0 << 12) +# define RADEON_STENCIL_TEST_LESS (1 << 12) +# define RADEON_STENCIL_TEST_LEQUAL (2 << 12) +# define RADEON_STENCIL_TEST_EQUAL (3 << 12) +# define RADEON_STENCIL_TEST_GEQUAL (4 << 12) +# define RADEON_STENCIL_TEST_GREATER (5 << 12) +# define RADEON_STENCIL_TEST_NEQUAL (6 << 12) +# define RADEON_STENCIL_TEST_ALWAYS (7 << 12) +# define RADEON_STENCIL_TEST_MASK (0x7 << 12) +# define RADEON_STENCIL_FAIL_KEEP (0 << 16) +# define RADEON_STENCIL_FAIL_ZERO (1 << 16) +# define RADEON_STENCIL_FAIL_REPLACE (2 << 16) +# define RADEON_STENCIL_FAIL_INC (3 << 16) +# define RADEON_STENCIL_FAIL_DEC (4 << 16) +# define RADEON_STENCIL_FAIL_INVERT (5 << 16) +# define RADEON_STENCIL_FAIL_MASK (0x7 << 16) +# define RADEON_STENCIL_ZPASS_KEEP (0 << 20) +# define RADEON_STENCIL_ZPASS_ZERO (1 << 20) +# define RADEON_STENCIL_ZPASS_REPLACE (2 << 20) +# define RADEON_STENCIL_ZPASS_INC (3 << 20) +# define RADEON_STENCIL_ZPASS_DEC (4 << 20) +# define RADEON_STENCIL_ZPASS_INVERT (5 << 20) +# define RADEON_STENCIL_ZPASS_MASK (0x7 << 20) +# define RADEON_STENCIL_ZFAIL_KEEP (0 << 24) +# define RADEON_STENCIL_ZFAIL_ZERO (1 << 24) +# define RADEON_STENCIL_ZFAIL_REPLACE (2 << 24) +# define RADEON_STENCIL_ZFAIL_INC (3 << 24) +# define RADEON_STENCIL_ZFAIL_DEC (4 << 24) +# define RADEON_STENCIL_ZFAIL_INVERT (5 << 24) +# define RADEON_STENCIL_ZFAIL_MASK (0x7 << 24) +# define RADEON_Z_COMPRESSION_ENABLE (1 << 28) +# define RADEON_FORCE_Z_DIRTY (1 << 29) +# define RADEON_Z_WRITE_ENABLE (1 << 30) +#define RADEON_RE_LINE_PATTERN 0x1cd0 +# define RADEON_LINE_PATTERN_MASK 0x0000ffff +# define RADEON_LINE_REPEAT_COUNT_SHIFT 16 +# define RADEON_LINE_PATTERN_START_SHIFT 24 +# define RADEON_LINE_PATTERN_LITTLE_BIT_ORDER (0 << 28) +# define RADEON_LINE_PATTERN_BIG_BIT_ORDER (1 << 28) +# define RADEON_LINE_PATTERN_AUTO_RESET (1 << 29) +#define RADEON_RE_LINE_STATE 0x1cd4 +# define RADEON_LINE_CURRENT_PTR_SHIFT 0 +# define RADEON_LINE_CURRENT_COUNT_SHIFT 8 +#define RADEON_RE_MISC 0x26c4 +# define RADEON_STIPPLE_COORD_MASK 0x1f +# define RADEON_STIPPLE_X_OFFSET_SHIFT 0 +# define RADEON_STIPPLE_X_OFFSET_MASK (0x1f << 0) +# define RADEON_STIPPLE_Y_OFFSET_SHIFT 8 +# define RADEON_STIPPLE_Y_OFFSET_MASK (0x1f << 8) +# define RADEON_STIPPLE_LITTLE_BIT_ORDER (0 << 16) +# define RADEON_STIPPLE_BIG_BIT_ORDER (1 << 16) +#define RADEON_RE_SOLID_COLOR 0x1c1c +#define RADEON_RE_TOP_LEFT 0x26c0 +# define RADEON_RE_LEFT_SHIFT 0 +# define RADEON_RE_TOP_SHIFT 16 +#define RADEON_RE_WIDTH_HEIGHT 0x1c44 +# define RADEON_RE_WIDTH_SHIFT 0 +# define RADEON_RE_HEIGHT_SHIFT 16 + +#define RADEON_SE_CNTL 0x1c4c +# define RADEON_FFACE_CULL_CW (0 << 0) +# define RADEON_FFACE_CULL_CCW (1 << 0) +# define RADEON_FFACE_CULL_DIR_MASK (1 << 0) +# define RADEON_BFACE_CULL (0 << 1) +# define RADEON_BFACE_SOLID (3 << 1) +# define RADEON_FFACE_CULL (0 << 3) +# define RADEON_FFACE_SOLID (3 << 3) +# define RADEON_FFACE_CULL_MASK (3 << 3) +# define RADEON_BADVTX_CULL_DISABLE (1 << 5) +# define RADEON_FLAT_SHADE_VTX_0 (0 << 6) +# define RADEON_FLAT_SHADE_VTX_1 (1 << 6) +# define RADEON_FLAT_SHADE_VTX_2 (2 << 6) +# define RADEON_FLAT_SHADE_VTX_LAST (3 << 6) +# define RADEON_DIFFUSE_SHADE_SOLID (0 << 8) +# define RADEON_DIFFUSE_SHADE_FLAT (1 << 8) +# define RADEON_DIFFUSE_SHADE_GOURAUD (2 << 8) +# define RADEON_DIFFUSE_SHADE_MASK (3 << 8) +# define RADEON_ALPHA_SHADE_SOLID (0 << 10) +# define RADEON_ALPHA_SHADE_FLAT (1 << 10) +# define RADEON_ALPHA_SHADE_GOURAUD (2 << 10) +# define RADEON_ALPHA_SHADE_MASK (3 << 10) +# define RADEON_SPECULAR_SHADE_SOLID (0 << 12) +# define RADEON_SPECULAR_SHADE_FLAT (1 << 12) +# define RADEON_SPECULAR_SHADE_GOURAUD (2 << 12) +# define RADEON_SPECULAR_SHADE_MASK (3 << 12) +# define RADEON_FOG_SHADE_SOLID (0 << 14) +# define RADEON_FOG_SHADE_FLAT (1 << 14) +# define RADEON_FOG_SHADE_GOURAUD (2 << 14) +# define RADEON_FOG_SHADE_MASK (3 << 14) +# define RADEON_ZBIAS_ENABLE_POINT (1 << 16) +# define RADEON_ZBIAS_ENABLE_LINE (1 << 17) +# define RADEON_ZBIAS_ENABLE_TRI (1 << 18) +# define RADEON_WIDELINE_ENABLE (1 << 20) +# define RADEON_VPORT_XY_XFORM_ENABLE (1 << 24) +# define RADEON_VPORT_Z_XFORM_ENABLE (1 << 25) +# define RADEON_VTX_PIX_CENTER_D3D (0 << 27) +# define RADEON_VTX_PIX_CENTER_OGL (1 << 27) +# define RADEON_ROUND_MODE_TRUNC (0 << 28) +# define RADEON_ROUND_MODE_ROUND (1 << 28) +# define RADEON_ROUND_MODE_ROUND_EVEN (2 << 28) +# define RADEON_ROUND_MODE_ROUND_ODD (3 << 28) +# define RADEON_ROUND_PREC_16TH_PIX (0 << 30) +# define RADEON_ROUND_PREC_8TH_PIX (1 << 30) +# define RADEON_ROUND_PREC_4TH_PIX (2 << 30) +# define RADEON_ROUND_PREC_HALF_PIX (3 << 30) +#define R200_RE_CNTL 0x1c50 +# define R200_STIPPLE_ENABLE 0x1 +# define R200_SCISSOR_ENABLE 0x2 +# define R200_PATTERN_ENABLE 0x4 +# define R200_PERSPECTIVE_ENABLE 0x8 +# define R200_POINT_SMOOTH 0x20 +# define R200_VTX_STQ0_D3D 0x00010000 +# define R200_VTX_STQ1_D3D 0x00040000 +# define R200_VTX_STQ2_D3D 0x00100000 +# define R200_VTX_STQ3_D3D 0x00400000 +# define R200_VTX_STQ4_D3D 0x01000000 +# define R200_VTX_STQ5_D3D 0x04000000 +#define RADEON_SE_CNTL_STATUS 0x2140 +# define RADEON_VC_NO_SWAP (0 << 0) +# define RADEON_VC_16BIT_SWAP (1 << 0) +# define RADEON_VC_32BIT_SWAP (2 << 0) +# define RADEON_VC_HALF_DWORD_SWAP (3 << 0) +# define RADEON_TCL_BYPASS (1 << 8) +#define RADEON_SE_COORD_FMT 0x1c50 +# define RADEON_VTX_XY_PRE_MULT_1_OVER_W0 (1 << 0) +# define RADEON_VTX_Z_PRE_MULT_1_OVER_W0 (1 << 1) +# define RADEON_VTX_ST0_NONPARAMETRIC (1 << 8) +# define RADEON_VTX_ST1_NONPARAMETRIC (1 << 9) +# define RADEON_VTX_ST2_NONPARAMETRIC (1 << 10) +# define RADEON_VTX_ST3_NONPARAMETRIC (1 << 11) +# define RADEON_VTX_W0_NORMALIZE (1 << 12) +# define RADEON_VTX_W0_IS_NOT_1_OVER_W0 (1 << 16) +# define RADEON_VTX_ST0_PRE_MULT_1_OVER_W0 (1 << 17) +# define RADEON_VTX_ST1_PRE_MULT_1_OVER_W0 (1 << 19) +# define RADEON_VTX_ST2_PRE_MULT_1_OVER_W0 (1 << 21) +# define RADEON_VTX_ST3_PRE_MULT_1_OVER_W0 (1 << 23) +# define RADEON_TEX1_W_ROUTING_USE_W0 (0 << 26) +# define RADEON_TEX1_W_ROUTING_USE_Q1 (1 << 26) +#define RADEON_SE_LINE_WIDTH 0x1db8 +#define RADEON_SE_TCL_LIGHT_MODEL_CTL 0x226c +# define RADEON_LIGHTING_ENABLE (1 << 0) +# define RADEON_LIGHT_IN_MODELSPACE (1 << 1) +# define RADEON_LOCAL_VIEWER (1 << 2) +# define RADEON_NORMALIZE_NORMALS (1 << 3) +# define RADEON_RESCALE_NORMALS (1 << 4) +# define RADEON_SPECULAR_LIGHTS (1 << 5) +# define RADEON_DIFFUSE_SPECULAR_COMBINE (1 << 6) +# define RADEON_LIGHT_ALPHA (1 << 7) +# define RADEON_LOCAL_LIGHT_VEC_GL (1 << 8) +# define RADEON_LIGHT_NO_NORMAL_AMBIENT_ONLY (1 << 9) +# define RADEON_LM_SOURCE_STATE_PREMULT 0 +# define RADEON_LM_SOURCE_STATE_MULT 1 +# define RADEON_LM_SOURCE_VERTEX_DIFFUSE 2 +# define RADEON_LM_SOURCE_VERTEX_SPECULAR 3 +# define RADEON_EMISSIVE_SOURCE_SHIFT 16 +# define RADEON_AMBIENT_SOURCE_SHIFT 18 +# define RADEON_DIFFUSE_SOURCE_SHIFT 20 +# define RADEON_SPECULAR_SOURCE_SHIFT 22 +#define RADEON_SE_TCL_MATERIAL_AMBIENT_RED 0x2220 +#define RADEON_SE_TCL_MATERIAL_AMBIENT_GREEN 0x2224 +#define RADEON_SE_TCL_MATERIAL_AMBIENT_BLUE 0x2228 +#define RADEON_SE_TCL_MATERIAL_AMBIENT_ALPHA 0x222c +#define RADEON_SE_TCL_MATERIAL_DIFFUSE_RED 0x2230 +#define RADEON_SE_TCL_MATERIAL_DIFFUSE_GREEN 0x2234 +#define RADEON_SE_TCL_MATERIAL_DIFFUSE_BLUE 0x2238 +#define RADEON_SE_TCL_MATERIAL_DIFFUSE_ALPHA 0x223c +#define RADEON_SE_TCL_MATERIAL_EMMISSIVE_RED 0x2210 +#define RADEON_SE_TCL_MATERIAL_EMMISSIVE_GREEN 0x2214 +#define RADEON_SE_TCL_MATERIAL_EMMISSIVE_BLUE 0x2218 +#define RADEON_SE_TCL_MATERIAL_EMMISSIVE_ALPHA 0x221c +#define RADEON_SE_TCL_MATERIAL_SPECULAR_RED 0x2240 +#define RADEON_SE_TCL_MATERIAL_SPECULAR_GREEN 0x2244 +#define RADEON_SE_TCL_MATERIAL_SPECULAR_BLUE 0x2248 +#define RADEON_SE_TCL_MATERIAL_SPECULAR_ALPHA 0x224c +#define RADEON_SE_TCL_MATRIX_SELECT_0 0x225c +# define RADEON_MODELVIEW_0_SHIFT 0 +# define RADEON_MODELVIEW_1_SHIFT 4 +# define RADEON_MODELVIEW_2_SHIFT 8 +# define RADEON_MODELVIEW_3_SHIFT 12 +# define RADEON_IT_MODELVIEW_0_SHIFT 16 +# define RADEON_IT_MODELVIEW_1_SHIFT 20 +# define RADEON_IT_MODELVIEW_2_SHIFT 24 +# define RADEON_IT_MODELVIEW_3_SHIFT 28 +#define RADEON_SE_TCL_MATRIX_SELECT_1 0x2260 +# define RADEON_MODELPROJECT_0_SHIFT 0 +# define RADEON_MODELPROJECT_1_SHIFT 4 +# define RADEON_MODELPROJECT_2_SHIFT 8 +# define RADEON_MODELPROJECT_3_SHIFT 12 +# define RADEON_TEXMAT_0_SHIFT 16 +# define RADEON_TEXMAT_1_SHIFT 20 +# define RADEON_TEXMAT_2_SHIFT 24 +# define RADEON_TEXMAT_3_SHIFT 28 + + +#define RADEON_SE_TCL_OUTPUT_VTX_FMT 0x2254 +# define RADEON_TCL_VTX_W0 (1 << 0) +# define RADEON_TCL_VTX_FP_DIFFUSE (1 << 1) +# define RADEON_TCL_VTX_FP_ALPHA (1 << 2) +# define RADEON_TCL_VTX_PK_DIFFUSE (1 << 3) +# define RADEON_TCL_VTX_FP_SPEC (1 << 4) +# define RADEON_TCL_VTX_FP_FOG (1 << 5) +# define RADEON_TCL_VTX_PK_SPEC (1 << 6) +# define RADEON_TCL_VTX_ST0 (1 << 7) +# define RADEON_TCL_VTX_ST1 (1 << 8) +# define RADEON_TCL_VTX_Q1 (1 << 9) +# define RADEON_TCL_VTX_ST2 (1 << 10) +# define RADEON_TCL_VTX_Q2 (1 << 11) +# define RADEON_TCL_VTX_ST3 (1 << 12) +# define RADEON_TCL_VTX_Q3 (1 << 13) +# define RADEON_TCL_VTX_Q0 (1 << 14) +# define RADEON_TCL_VTX_WEIGHT_COUNT_SHIFT 15 +# define RADEON_TCL_VTX_NORM0 (1 << 18) +# define RADEON_TCL_VTX_XY1 (1 << 27) +# define RADEON_TCL_VTX_Z1 (1 << 28) +# define RADEON_TCL_VTX_W1 (1 << 29) +# define RADEON_TCL_VTX_NORM1 (1 << 30) +# define RADEON_TCL_VTX_Z0 (1 << 31) + +#define RADEON_SE_TCL_OUTPUT_VTX_SEL 0x2258 +# define RADEON_TCL_COMPUTE_XYZW (1 << 0) +# define RADEON_TCL_COMPUTE_DIFFUSE (1 << 1) +# define RADEON_TCL_COMPUTE_SPECULAR (1 << 2) +# define RADEON_TCL_FORCE_NAN_IF_COLOR_NAN (1 << 3) +# define RADEON_TCL_FORCE_INORDER_PROC (1 << 4) +# define RADEON_TCL_TEX_INPUT_TEX_0 0 +# define RADEON_TCL_TEX_INPUT_TEX_1 1 +# define RADEON_TCL_TEX_INPUT_TEX_2 2 +# define RADEON_TCL_TEX_INPUT_TEX_3 3 +# define RADEON_TCL_TEX_COMPUTED_TEX_0 8 +# define RADEON_TCL_TEX_COMPUTED_TEX_1 9 +# define RADEON_TCL_TEX_COMPUTED_TEX_2 10 +# define RADEON_TCL_TEX_COMPUTED_TEX_3 11 +# define RADEON_TCL_TEX_0_OUTPUT_SHIFT 16 +# define RADEON_TCL_TEX_1_OUTPUT_SHIFT 20 +# define RADEON_TCL_TEX_2_OUTPUT_SHIFT 24 +# define RADEON_TCL_TEX_3_OUTPUT_SHIFT 28 + +#define RADEON_SE_TCL_PER_LIGHT_CTL_0 0x2270 +# define RADEON_LIGHT_0_ENABLE (1 << 0) +# define RADEON_LIGHT_0_ENABLE_AMBIENT (1 << 1) +# define RADEON_LIGHT_0_ENABLE_SPECULAR (1 << 2) +# define RADEON_LIGHT_0_IS_LOCAL (1 << 3) +# define RADEON_LIGHT_0_IS_SPOT (1 << 4) +# define RADEON_LIGHT_0_DUAL_CONE (1 << 5) +# define RADEON_LIGHT_0_ENABLE_RANGE_ATTEN (1 << 6) +# define RADEON_LIGHT_0_CONSTANT_RANGE_ATTEN (1 << 7) +# define RADEON_LIGHT_0_SHIFT 0 +# define RADEON_LIGHT_1_ENABLE (1 << 16) +# define RADEON_LIGHT_1_ENABLE_AMBIENT (1 << 17) +# define RADEON_LIGHT_1_ENABLE_SPECULAR (1 << 18) +# define RADEON_LIGHT_1_IS_LOCAL (1 << 19) +# define RADEON_LIGHT_1_IS_SPOT (1 << 20) +# define RADEON_LIGHT_1_DUAL_CONE (1 << 21) +# define RADEON_LIGHT_1_ENABLE_RANGE_ATTEN (1 << 22) +# define RADEON_LIGHT_1_CONSTANT_RANGE_ATTEN (1 << 23) +# define RADEON_LIGHT_1_SHIFT 16 +#define RADEON_SE_TCL_PER_LIGHT_CTL_1 0x2274 +# define RADEON_LIGHT_2_SHIFT 0 +# define RADEON_LIGHT_3_SHIFT 16 +#define RADEON_SE_TCL_PER_LIGHT_CTL_2 0x2278 +# define RADEON_LIGHT_4_SHIFT 0 +# define RADEON_LIGHT_5_SHIFT 16 +#define RADEON_SE_TCL_PER_LIGHT_CTL_3 0x227c +# define RADEON_LIGHT_6_SHIFT 0 +# define RADEON_LIGHT_7_SHIFT 16 + +#define RADEON_SE_TCL_SHININESS 0x2250 + +#define RADEON_SE_TCL_TEXTURE_PROC_CTL 0x2268 +# define RADEON_TEXGEN_TEXMAT_0_ENABLE (1 << 0) +# define RADEON_TEXGEN_TEXMAT_1_ENABLE (1 << 1) +# define RADEON_TEXGEN_TEXMAT_2_ENABLE (1 << 2) +# define RADEON_TEXGEN_TEXMAT_3_ENABLE (1 << 3) +# define RADEON_TEXMAT_0_ENABLE (1 << 4) +# define RADEON_TEXMAT_1_ENABLE (1 << 5) +# define RADEON_TEXMAT_2_ENABLE (1 << 6) +# define RADEON_TEXMAT_3_ENABLE (1 << 7) +# define RADEON_TEXGEN_INPUT_MASK 0xf +# define RADEON_TEXGEN_INPUT_TEXCOORD_0 0 +# define RADEON_TEXGEN_INPUT_TEXCOORD_1 1 +# define RADEON_TEXGEN_INPUT_TEXCOORD_2 2 +# define RADEON_TEXGEN_INPUT_TEXCOORD_3 3 +# define RADEON_TEXGEN_INPUT_OBJ 4 +# define RADEON_TEXGEN_INPUT_EYE 5 +# define RADEON_TEXGEN_INPUT_EYE_NORMAL 6 +# define RADEON_TEXGEN_INPUT_EYE_REFLECT 7 +# define RADEON_TEXGEN_INPUT_EYE_NORMALIZED 8 +# define RADEON_TEXGEN_0_INPUT_SHIFT 16 +# define RADEON_TEXGEN_1_INPUT_SHIFT 20 +# define RADEON_TEXGEN_2_INPUT_SHIFT 24 +# define RADEON_TEXGEN_3_INPUT_SHIFT 28 + +#define RADEON_SE_TCL_UCP_VERT_BLEND_CTL 0x2264 +# define RADEON_UCP_IN_CLIP_SPACE (1 << 0) +# define RADEON_UCP_IN_MODEL_SPACE (1 << 1) +# define RADEON_UCP_ENABLE_0 (1 << 2) +# define RADEON_UCP_ENABLE_1 (1 << 3) +# define RADEON_UCP_ENABLE_2 (1 << 4) +# define RADEON_UCP_ENABLE_3 (1 << 5) +# define RADEON_UCP_ENABLE_4 (1 << 6) +# define RADEON_UCP_ENABLE_5 (1 << 7) +# define RADEON_TCL_FOG_MASK (3 << 8) +# define RADEON_TCL_FOG_DISABLE (0 << 8) +# define RADEON_TCL_FOG_EXP (1 << 8) +# define RADEON_TCL_FOG_EXP2 (2 << 8) +# define RADEON_TCL_FOG_LINEAR (3 << 8) +# define RADEON_RNG_BASED_FOG (1 << 10) +# define RADEON_LIGHT_TWOSIDE (1 << 11) +# define RADEON_BLEND_OP_COUNT_MASK (7 << 12) +# define RADEON_BLEND_OP_COUNT_SHIFT 12 +# define RADEON_POSITION_BLEND_OP_ENABLE (1 << 16) +# define RADEON_NORMAL_BLEND_OP_ENABLE (1 << 17) +# define RADEON_VERTEX_BLEND_SRC_0_PRIMARY (1 << 18) +# define RADEON_VERTEX_BLEND_SRC_0_SECONDARY (1 << 18) +# define RADEON_VERTEX_BLEND_SRC_1_PRIMARY (1 << 19) +# define RADEON_VERTEX_BLEND_SRC_1_SECONDARY (1 << 19) +# define RADEON_VERTEX_BLEND_SRC_2_PRIMARY (1 << 20) +# define RADEON_VERTEX_BLEND_SRC_2_SECONDARY (1 << 20) +# define RADEON_VERTEX_BLEND_SRC_3_PRIMARY (1 << 21) +# define RADEON_VERTEX_BLEND_SRC_3_SECONDARY (1 << 21) +# define RADEON_VERTEX_BLEND_WGT_MINUS_ONE (1 << 22) +# define RADEON_CULL_FRONT_IS_CW (0 << 28) +# define RADEON_CULL_FRONT_IS_CCW (1 << 28) +# define RADEON_CULL_FRONT (1 << 29) +# define RADEON_CULL_BACK (1 << 30) +# define RADEON_FORCE_W_TO_ONE (1 << 31) + +#define RADEON_SE_VPORT_XSCALE 0x1d98 +#define RADEON_SE_VPORT_XOFFSET 0x1d9c +#define RADEON_SE_VPORT_YSCALE 0x1da0 +#define RADEON_SE_VPORT_YOFFSET 0x1da4 +#define RADEON_SE_VPORT_ZSCALE 0x1da8 +#define RADEON_SE_VPORT_ZOFFSET 0x1dac +#define RADEON_SE_ZBIAS_FACTOR 0x1db0 +#define RADEON_SE_ZBIAS_CONSTANT 0x1db4 + +#define RADEON_SE_VTX_FMT 0x2080 +# define RADEON_SE_VTX_FMT_XY 0x00000000 +# define RADEON_SE_VTX_FMT_W0 0x00000001 +# define RADEON_SE_VTX_FMT_FPCOLOR 0x00000002 +# define RADEON_SE_VTX_FMT_FPALPHA 0x00000004 +# define RADEON_SE_VTX_FMT_PKCOLOR 0x00000008 +# define RADEON_SE_VTX_FMT_FPSPEC 0x00000010 +# define RADEON_SE_VTX_FMT_FPFOG 0x00000020 +# define RADEON_SE_VTX_FMT_PKSPEC 0x00000040 +# define RADEON_SE_VTX_FMT_ST0 0x00000080 +# define RADEON_SE_VTX_FMT_ST1 0x00000100 +# define RADEON_SE_VTX_FMT_Q1 0x00000200 +# define RADEON_SE_VTX_FMT_ST2 0x00000400 +# define RADEON_SE_VTX_FMT_Q2 0x00000800 +# define RADEON_SE_VTX_FMT_ST3 0x00001000 +# define RADEON_SE_VTX_FMT_Q3 0x00002000 +# define RADEON_SE_VTX_FMT_Q0 0x00004000 +# define RADEON_SE_VTX_FMT_BLND_WEIGHT_CNT_MASK 0x00038000 +# define RADEON_SE_VTX_FMT_N0 0x00040000 +# define RADEON_SE_VTX_FMT_XY1 0x08000000 +# define RADEON_SE_VTX_FMT_Z1 0x10000000 +# define RADEON_SE_VTX_FMT_W1 0x20000000 +# define RADEON_SE_VTX_FMT_N1 0x40000000 +# define RADEON_SE_VTX_FMT_Z 0x80000000 + +#define RADEON_SE_VF_CNTL 0x2084 +# define RADEON_VF_PRIM_TYPE_POINT_LIST 1 +# define RADEON_VF_PRIM_TYPE_LINE_LIST 2 +# define RADEON_VF_PRIM_TYPE_LINE_STRIP 3 +# define RADEON_VF_PRIM_TYPE_TRIANGLE_LIST 4 +# define RADEON_VF_PRIM_TYPE_TRIANGLE_FAN 5 +# define RADEON_VF_PRIM_TYPE_TRIANGLE_STRIP 6 +# define RADEON_VF_PRIM_TYPE_TRIANGLE_FLAG 7 +# define RADEON_VF_PRIM_TYPE_RECTANGLE_LIST 8 +# define RADEON_VF_PRIM_TYPE_POINT_LIST_3 9 +# define RADEON_VF_PRIM_TYPE_LINE_LIST_3 10 +# define RADEON_VF_PRIM_TYPE_SPIRIT_LIST 11 +# define RADEON_VF_PRIM_TYPE_LINE_LOOP 12 +# define RADEON_VF_PRIM_TYPE_QUAD_LIST 13 +# define RADEON_VF_PRIM_TYPE_QUAD_STRIP 14 +# define RADEON_VF_PRIM_TYPE_POLYGON 15 +# define RADEON_VF_PRIM_WALK_STATE (0<<4) +# define RADEON_VF_PRIM_WALK_INDEX (1<<4) +# define RADEON_VF_PRIM_WALK_LIST (2<<4) +# define RADEON_VF_PRIM_WALK_DATA (3<<4) +# define RADEON_VF_COLOR_ORDER_RGBA (1<<6) +# define RADEON_VF_RADEON_MODE (1<<8) +# define RADEON_VF_TCL_OUTPUT_CTL_ENA (1<<9) +# define RADEON_VF_PROG_STREAM_ENA (1<<10) +# define RADEON_VF_INDEX_SIZE_SHIFT 11 +# define RADEON_VF_NUM_VERTICES_SHIFT 16 + +#define RADEON_SE_PORT_DATA0 0x2000 + +#define R200_SE_VAP_CNTL 0x2080 +# define R200_VAP_TCL_ENABLE 0x00000001 +# define R200_VAP_SINGLE_BUF_STATE_ENABLE 0x00000010 +# define R200_VAP_FORCE_W_TO_ONE 0x00010000 +# define R200_VAP_D3D_TEX_DEFAULT 0x00020000 +# define R200_VAP_VF_MAX_VTX_NUM__SHIFT 18 +# define R200_VAP_VF_MAX_VTX_NUM (9 << 18) +# define R200_VAP_DX_CLIP_SPACE_DEF 0x00400000 +#define R200_VF_MAX_VTX_INDX 0x210c +#define R200_VF_MIN_VTX_INDX 0x2110 +#define R200_SE_VTE_CNTL 0x20b0 +# define R200_VPORT_X_SCALE_ENA 0x00000001 +# define R200_VPORT_X_OFFSET_ENA 0x00000002 +# define R200_VPORT_Y_SCALE_ENA 0x00000004 +# define R200_VPORT_Y_OFFSET_ENA 0x00000008 +# define R200_VPORT_Z_SCALE_ENA 0x00000010 +# define R200_VPORT_Z_OFFSET_ENA 0x00000020 +# define R200_VTX_XY_FMT 0x00000100 +# define R200_VTX_Z_FMT 0x00000200 +# define R200_VTX_W0_FMT 0x00000400 +# define R200_VTX_W0_NORMALIZE 0x00000800 +# define R200_VTX_ST_DENORMALIZED 0x00001000 +#define R200_SE_VAP_CNTL_STATUS 0x2140 +# define R200_VC_NO_SWAP (0 << 0) +# define R200_VC_16BIT_SWAP (1 << 0) +# define R200_VC_32BIT_SWAP (2 << 0) +#define R200_PP_TXFILTER_0 0x2c00 +#define R200_PP_TXFILTER_1 0x2c20 +#define R200_PP_TXFILTER_2 0x2c40 +#define R200_PP_TXFILTER_3 0x2c60 +#define R200_PP_TXFILTER_4 0x2c80 +#define R200_PP_TXFILTER_5 0x2ca0 +# define R200_MAG_FILTER_NEAREST (0 << 0) +# define R200_MAG_FILTER_LINEAR (1 << 0) +# define R200_MAG_FILTER_MASK (1 << 0) +# define R200_MIN_FILTER_NEAREST (0 << 1) +# define R200_MIN_FILTER_LINEAR (1 << 1) +# define R200_MIN_FILTER_NEAREST_MIP_NEAREST (2 << 1) +# define R200_MIN_FILTER_NEAREST_MIP_LINEAR (3 << 1) +# define R200_MIN_FILTER_LINEAR_MIP_NEAREST (6 << 1) +# define R200_MIN_FILTER_LINEAR_MIP_LINEAR (7 << 1) +# define R200_MIN_FILTER_ANISO_NEAREST (8 << 1) +# define R200_MIN_FILTER_ANISO_LINEAR (9 << 1) +# define R200_MIN_FILTER_ANISO_NEAREST_MIP_NEAREST (10 << 1) +# define R200_MIN_FILTER_ANISO_NEAREST_MIP_LINEAR (11 << 1) +# define R200_MIN_FILTER_MASK (15 << 1) +# define R200_MAX_ANISO_1_TO_1 (0 << 5) +# define R200_MAX_ANISO_2_TO_1 (1 << 5) +# define R200_MAX_ANISO_4_TO_1 (2 << 5) +# define R200_MAX_ANISO_8_TO_1 (3 << 5) +# define R200_MAX_ANISO_16_TO_1 (4 << 5) +# define R200_MAX_ANISO_MASK (7 << 5) +# define R200_MAX_MIP_LEVEL_MASK (0x0f << 16) +# define R200_MAX_MIP_LEVEL_SHIFT 16 +# define R200_YUV_TO_RGB (1 << 20) +# define R200_YUV_TEMPERATURE_COOL (0 << 21) +# define R200_YUV_TEMPERATURE_HOT (1 << 21) +# define R200_YUV_TEMPERATURE_MASK (1 << 21) +# define R200_WRAPEN_S (1 << 22) +# define R200_CLAMP_S_WRAP (0 << 23) +# define R200_CLAMP_S_MIRROR (1 << 23) +# define R200_CLAMP_S_CLAMP_LAST (2 << 23) +# define R200_CLAMP_S_MIRROR_CLAMP_LAST (3 << 23) +# define R200_CLAMP_S_CLAMP_BORDER (4 << 23) +# define R200_CLAMP_S_MIRROR_CLAMP_BORDER (5 << 23) +# define R200_CLAMP_S_CLAMP_GL (6 << 23) +# define R200_CLAMP_S_MIRROR_CLAMP_GL (7 << 23) +# define R200_CLAMP_S_MASK (7 << 23) +# define R200_WRAPEN_T (1 << 26) +# define R200_CLAMP_T_WRAP (0 << 27) +# define R200_CLAMP_T_MIRROR (1 << 27) +# define R200_CLAMP_T_CLAMP_LAST (2 << 27) +# define R200_CLAMP_T_MIRROR_CLAMP_LAST (3 << 27) +# define R200_CLAMP_T_CLAMP_BORDER (4 << 27) +# define R200_CLAMP_T_MIRROR_CLAMP_BORDER (5 << 27) +# define R200_CLAMP_T_CLAMP_GL (6 << 27) +# define R200_CLAMP_T_MIRROR_CLAMP_GL (7 << 27) +# define R200_CLAMP_T_MASK (7 << 27) +# define R200_KILL_LT_ZERO (1 << 30) +# define R200_BORDER_MODE_OGL (0 << 31) +# define R200_BORDER_MODE_D3D (1 << 31) +#define R200_PP_TXFORMAT_0 0x2c04 +#define R200_PP_TXFORMAT_1 0x2c24 +#define R200_PP_TXFORMAT_2 0x2c44 +#define R200_PP_TXFORMAT_3 0x2c64 +#define R200_PP_TXFORMAT_4 0x2c84 +#define R200_PP_TXFORMAT_5 0x2ca4 +# define R200_TXFORMAT_I8 (0 << 0) +# define R200_TXFORMAT_AI88 (1 << 0) +# define R200_TXFORMAT_RGB332 (2 << 0) +# define R200_TXFORMAT_ARGB1555 (3 << 0) +# define R200_TXFORMAT_RGB565 (4 << 0) +# define R200_TXFORMAT_ARGB4444 (5 << 0) +# define R200_TXFORMAT_ARGB8888 (6 << 0) +# define R200_TXFORMAT_RGBA8888 (7 << 0) +# define R200_TXFORMAT_Y8 (8 << 0) +# define R200_TXFORMAT_AVYU4444 (9 << 0) +# define R200_TXFORMAT_VYUY422 (10 << 0) +# define R200_TXFORMAT_YVYU422 (11 << 0) +# define R200_TXFORMAT_DXT1 (12 << 0) +# define R200_TXFORMAT_DXT23 (14 << 0) +# define R200_TXFORMAT_DXT45 (15 << 0) +# define R200_TXFORMAT_ABGR8888 (22 << 0) +# define R200_TXFORMAT_FORMAT_MASK (31 << 0) +# define R200_TXFORMAT_FORMAT_SHIFT 0 +# define R200_TXFORMAT_ALPHA_IN_MAP (1 << 6) +# define R200_TXFORMAT_NON_POWER2 (1 << 7) +# define R200_TXFORMAT_WIDTH_MASK (15 << 8) +# define R200_TXFORMAT_WIDTH_SHIFT 8 +# define R200_TXFORMAT_HEIGHT_MASK (15 << 12) +# define R200_TXFORMAT_HEIGHT_SHIFT 12 +# define R200_TXFORMAT_F5_WIDTH_MASK (15 << 16) /* cube face 5 */ +# define R200_TXFORMAT_F5_WIDTH_SHIFT 16 +# define R200_TXFORMAT_F5_HEIGHT_MASK (15 << 20) +# define R200_TXFORMAT_F5_HEIGHT_SHIFT 20 +# define R200_TXFORMAT_ST_ROUTE_STQ0 (0 << 24) +# define R200_TXFORMAT_ST_ROUTE_STQ1 (1 << 24) +# define R200_TXFORMAT_ST_ROUTE_STQ2 (2 << 24) +# define R200_TXFORMAT_ST_ROUTE_STQ3 (3 << 24) +# define R200_TXFORMAT_ST_ROUTE_STQ4 (4 << 24) +# define R200_TXFORMAT_ST_ROUTE_STQ5 (5 << 24) +# define R200_TXFORMAT_ST_ROUTE_MASK (7 << 24) +# define R200_TXFORMAT_ST_ROUTE_SHIFT 24 +# define R200_TXFORMAT_ALPHA_MASK_ENABLE (1 << 28) +# define R200_TXFORMAT_CHROMA_KEY_ENABLE (1 << 29) +# define R200_TXFORMAT_CUBIC_MAP_ENABLE (1 << 30) +#define R200_PP_TXFORMAT_X_0 0x2c08 +#define R200_PP_TXFORMAT_X_1 0x2c28 +#define R200_PP_TXFORMAT_X_2 0x2c48 +#define R200_PP_TXFORMAT_X_3 0x2c68 +#define R200_PP_TXFORMAT_X_4 0x2c88 +#define R200_PP_TXFORMAT_X_5 0x2ca8 + +#define R200_PP_TXSIZE_0 0x2c0c /* NPOT only */ +#define R200_PP_TXSIZE_1 0x2c2c /* NPOT only */ +#define R200_PP_TXSIZE_2 0x2c4c /* NPOT only */ +#define R200_PP_TXSIZE_3 0x2c6c /* NPOT only */ +#define R200_PP_TXSIZE_4 0x2c8c /* NPOT only */ +#define R200_PP_TXSIZE_5 0x2cac /* NPOT only */ + +#define R200_PP_TXPITCH_0 0x2c10 /* NPOT only */ +#define R200_PP_TXPITCH_1 0x2c30 /* NPOT only */ +#define R200_PP_TXPITCH_2 0x2c50 /* NPOT only */ +#define R200_PP_TXPITCH_3 0x2c70 /* NPOT only */ +#define R200_PP_TXPITCH_4 0x2c90 /* NPOT only */ +#define R200_PP_TXPITCH_5 0x2cb0 /* NPOT only */ + +#define R200_PP_TXOFFSET_0 0x2d00 +# define R200_TXO_ENDIAN_NO_SWAP (0 << 0) +# define R200_TXO_ENDIAN_BYTE_SWAP (1 << 0) +# define R200_TXO_ENDIAN_WORD_SWAP (2 << 0) +# define R200_TXO_ENDIAN_HALFDW_SWAP (3 << 0) +# define R200_TXO_MACRO_LINEAR (0 << 2) +# define R200_TXO_MACRO_TILE (1 << 2) +# define R200_TXO_MICRO_LINEAR (0 << 3) +# define R200_TXO_MICRO_TILE (1 << 3) +# define R200_TXO_OFFSET_MASK 0xffffffe0 +# define R200_TXO_OFFSET_SHIFT 5 +#define R200_PP_TXOFFSET_1 0x2d18 +#define R200_PP_TXOFFSET_2 0x2d30 +#define R200_PP_TXOFFSET_3 0x2d48 +#define R200_PP_TXOFFSET_4 0x2d60 +#define R200_PP_TXOFFSET_5 0x2d78 + +#define R200_PP_TFACTOR_0 0x2ee0 +#define R200_PP_TFACTOR_1 0x2ee4 +#define R200_PP_TFACTOR_2 0x2ee8 +#define R200_PP_TFACTOR_3 0x2eec +#define R200_PP_TFACTOR_4 0x2ef0 +#define R200_PP_TFACTOR_5 0x2ef4 + +#define R200_PP_TXCBLEND_0 0x2f00 +# define R200_TXC_ARG_A_ZERO (0) +# define R200_TXC_ARG_A_CURRENT_COLOR (2) +# define R200_TXC_ARG_A_CURRENT_ALPHA (3) +# define R200_TXC_ARG_A_DIFFUSE_COLOR (4) +# define R200_TXC_ARG_A_DIFFUSE_ALPHA (5) +# define R200_TXC_ARG_A_SPECULAR_COLOR (6) +# define R200_TXC_ARG_A_SPECULAR_ALPHA (7) +# define R200_TXC_ARG_A_TFACTOR_COLOR (8) +# define R200_TXC_ARG_A_TFACTOR_ALPHA (9) +# define R200_TXC_ARG_A_R0_COLOR (10) +# define R200_TXC_ARG_A_R0_ALPHA (11) +# define R200_TXC_ARG_A_R1_COLOR (12) +# define R200_TXC_ARG_A_R1_ALPHA (13) +# define R200_TXC_ARG_A_R2_COLOR (14) +# define R200_TXC_ARG_A_R2_ALPHA (15) +# define R200_TXC_ARG_A_R3_COLOR (16) +# define R200_TXC_ARG_A_R3_ALPHA (17) +# define R200_TXC_ARG_A_R4_COLOR (18) +# define R200_TXC_ARG_A_R4_ALPHA (19) +# define R200_TXC_ARG_A_R5_COLOR (20) +# define R200_TXC_ARG_A_R5_ALPHA (21) +# define R200_TXC_ARG_A_TFACTOR1_COLOR (26) +# define R200_TXC_ARG_A_TFACTOR1_ALPHA (27) +# define R200_TXC_ARG_A_MASK (31 << 0) +# define R200_TXC_ARG_A_SHIFT 0 +# define R200_TXC_ARG_B_ZERO (0 << 5) +# define R200_TXC_ARG_B_CURRENT_COLOR (2 << 5) +# define R200_TXC_ARG_B_CURRENT_ALPHA (3 << 5) +# define R200_TXC_ARG_B_DIFFUSE_COLOR (4 << 5) +# define R200_TXC_ARG_B_DIFFUSE_ALPHA (5 << 5) +# define R200_TXC_ARG_B_SPECULAR_COLOR (6 << 5) +# define R200_TXC_ARG_B_SPECULAR_ALPHA (7 << 5) +# define R200_TXC_ARG_B_TFACTOR_COLOR (8 << 5) +# define R200_TXC_ARG_B_TFACTOR_ALPHA (9 << 5) +# define R200_TXC_ARG_B_R0_COLOR (10 << 5) +# define R200_TXC_ARG_B_R0_ALPHA (11 << 5) +# define R200_TXC_ARG_B_R1_COLOR (12 << 5) +# define R200_TXC_ARG_B_R1_ALPHA (13 << 5) +# define R200_TXC_ARG_B_R2_COLOR (14 << 5) +# define R200_TXC_ARG_B_R2_ALPHA (15 << 5) +# define R200_TXC_ARG_B_R3_COLOR (16 << 5) +# define R200_TXC_ARG_B_R3_ALPHA (17 << 5) +# define R200_TXC_ARG_B_R4_COLOR (18 << 5) +# define R200_TXC_ARG_B_R4_ALPHA (19 << 5) +# define R200_TXC_ARG_B_R5_COLOR (20 << 5) +# define R200_TXC_ARG_B_R5_ALPHA (21 << 5) +# define R200_TXC_ARG_B_TFACTOR1_COLOR (26 << 5) +# define R200_TXC_ARG_B_TFACTOR1_ALPHA (27 << 5) +# define R200_TXC_ARG_B_MASK (31 << 5) +# define R200_TXC_ARG_B_SHIFT 5 +# define R200_TXC_ARG_C_ZERO (0 << 10) +# define R200_TXC_ARG_C_CURRENT_COLOR (2 << 10) +# define R200_TXC_ARG_C_CURRENT_ALPHA (3 << 10) +# define R200_TXC_ARG_C_DIFFUSE_COLOR (4 << 10) +# define R200_TXC_ARG_C_DIFFUSE_ALPHA (5 << 10) +# define R200_TXC_ARG_C_SPECULAR_COLOR (6 << 10) +# define R200_TXC_ARG_C_SPECULAR_ALPHA (7 << 10) +# define R200_TXC_ARG_C_TFACTOR_COLOR (8 << 10) +# define R200_TXC_ARG_C_TFACTOR_ALPHA (9 << 10) +# define R200_TXC_ARG_C_R0_COLOR (10 << 10) +# define R200_TXC_ARG_C_R0_ALPHA (11 << 10) +# define R200_TXC_ARG_C_R1_COLOR (12 << 10) +# define R200_TXC_ARG_C_R1_ALPHA (13 << 10) +# define R200_TXC_ARG_C_R2_COLOR (14 << 10) +# define R200_TXC_ARG_C_R2_ALPHA (15 << 10) +# define R200_TXC_ARG_C_R3_COLOR (16 << 10) +# define R200_TXC_ARG_C_R3_ALPHA (17 << 10) +# define R200_TXC_ARG_C_R4_COLOR (18 << 10) +# define R200_TXC_ARG_C_R4_ALPHA (19 << 10) +# define R200_TXC_ARG_C_R5_COLOR (20 << 10) +# define R200_TXC_ARG_C_R5_ALPHA (21 << 10) +# define R200_TXC_ARG_C_TFACTOR1_COLOR (26 << 10) +# define R200_TXC_ARG_C_TFACTOR1_ALPHA (27 << 10) +# define R200_TXC_ARG_C_MASK (31 << 10) +# define R200_TXC_ARG_C_SHIFT 10 +# define R200_TXC_COMP_ARG_A (1 << 16) +# define R200_TXC_COMP_ARG_A_SHIFT (16) +# define R200_TXC_BIAS_ARG_A (1 << 17) +# define R200_TXC_SCALE_ARG_A (1 << 18) +# define R200_TXC_NEG_ARG_A (1 << 19) +# define R200_TXC_COMP_ARG_B (1 << 20) +# define R200_TXC_COMP_ARG_B_SHIFT (20) +# define R200_TXC_BIAS_ARG_B (1 << 21) +# define R200_TXC_SCALE_ARG_B (1 << 22) +# define R200_TXC_NEG_ARG_B (1 << 23) +# define R200_TXC_COMP_ARG_C (1 << 24) +# define R200_TXC_COMP_ARG_C_SHIFT (24) +# define R200_TXC_BIAS_ARG_C (1 << 25) +# define R200_TXC_SCALE_ARG_C (1 << 26) +# define R200_TXC_NEG_ARG_C (1 << 27) +# define R200_TXC_OP_MADD (0 << 28) +# define R200_TXC_OP_CND0 (2 << 28) +# define R200_TXC_OP_LERP (3 << 28) +# define R200_TXC_OP_DOT3 (4 << 28) +# define R200_TXC_OP_DOT4 (5 << 28) +# define R200_TXC_OP_CONDITIONAL (6 << 28) +# define R200_TXC_OP_DOT2_ADD (7 << 28) +# define R200_TXC_OP_MASK (7 << 28) +#define R200_PP_TXCBLEND2_0 0x2f04 +# define R200_TXC_TFACTOR_SEL_SHIFT 0 +# define R200_TXC_TFACTOR_SEL_MASK 0x7 +# define R200_TXC_TFACTOR1_SEL_SHIFT 4 +# define R200_TXC_TFACTOR1_SEL_MASK (0x7 << 4) +# define R200_TXC_SCALE_SHIFT 8 +# define R200_TXC_SCALE_MASK (7 << 8) +# define R200_TXC_SCALE_1X (0 << 8) +# define R200_TXC_SCALE_2X (1 << 8) +# define R200_TXC_SCALE_4X (2 << 8) +# define R200_TXC_SCALE_8X (3 << 8) +# define R200_TXC_SCALE_INV2 (5 << 8) +# define R200_TXC_SCALE_INV4 (6 << 8) +# define R200_TXC_SCALE_INV8 (7 << 8) +# define R200_TXC_CLAMP_SHIFT 12 +# define R200_TXC_CLAMP_MASK (3 << 12) +# define R200_TXC_CLAMP_WRAP (0 << 12) +# define R200_TXC_CLAMP_0_1 (1 << 12) +# define R200_TXC_CLAMP_8_8 (2 << 12) +# define R200_TXC_OUTPUT_REG_MASK (7 << 16) +# define R200_TXC_OUTPUT_REG_NONE (0 << 16) +# define R200_TXC_OUTPUT_REG_R0 (1 << 16) +# define R200_TXC_OUTPUT_REG_R1 (2 << 16) +# define R200_TXC_OUTPUT_REG_R2 (3 << 16) +# define R200_TXC_OUTPUT_REG_R3 (4 << 16) +# define R200_TXC_OUTPUT_REG_R4 (5 << 16) +# define R200_TXC_OUTPUT_REG_R5 (6 << 16) +# define R200_TXC_OUTPUT_MASK_MASK (7 << 20) +# define R200_TXC_OUTPUT_MASK_RGB (0 << 20) +# define R200_TXC_OUTPUT_MASK_RG (1 << 20) +# define R200_TXC_OUTPUT_MASK_RB (2 << 20) +# define R200_TXC_OUTPUT_MASK_R (3 << 20) +# define R200_TXC_OUTPUT_MASK_GB (4 << 20) +# define R200_TXC_OUTPUT_MASK_G (5 << 20) +# define R200_TXC_OUTPUT_MASK_B (6 << 20) +# define R200_TXC_OUTPUT_MASK_NONE (7 << 20) +# define R200_TXC_REPL_NORMAL 0 +# define R200_TXC_REPL_RED 1 +# define R200_TXC_REPL_GREEN 2 +# define R200_TXC_REPL_BLUE 3 +# define R200_TXC_REPL_ARG_A_SHIFT 26 +# define R200_TXC_REPL_ARG_A_MASK (3 << 26) +# define R200_TXC_REPL_ARG_B_SHIFT 28 +# define R200_TXC_REPL_ARG_B_MASK (3 << 28) +# define R200_TXC_REPL_ARG_C_SHIFT 30 +# define R200_TXC_REPL_ARG_C_MASK (3 << 30) +#define R200_PP_TXABLEND_0 0x2f08 +# define R200_TXA_ARG_A_ZERO (0) +# define R200_TXA_ARG_A_CURRENT_ALPHA (2) /* guess */ +# define R200_TXA_ARG_A_CURRENT_BLUE (3) /* guess */ +# define R200_TXA_ARG_A_DIFFUSE_ALPHA (4) +# define R200_TXA_ARG_A_DIFFUSE_BLUE (5) +# define R200_TXA_ARG_A_SPECULAR_ALPHA (6) +# define R200_TXA_ARG_A_SPECULAR_BLUE (7) +# define R200_TXA_ARG_A_TFACTOR_ALPHA (8) +# define R200_TXA_ARG_A_TFACTOR_BLUE (9) +# define R200_TXA_ARG_A_R0_ALPHA (10) +# define R200_TXA_ARG_A_R0_BLUE (11) +# define R200_TXA_ARG_A_R1_ALPHA (12) +# define R200_TXA_ARG_A_R1_BLUE (13) +# define R200_TXA_ARG_A_R2_ALPHA (14) +# define R200_TXA_ARG_A_R2_BLUE (15) +# define R200_TXA_ARG_A_R3_ALPHA (16) +# define R200_TXA_ARG_A_R3_BLUE (17) +# define R200_TXA_ARG_A_R4_ALPHA (18) +# define R200_TXA_ARG_A_R4_BLUE (19) +# define R200_TXA_ARG_A_R5_ALPHA (20) +# define R200_TXA_ARG_A_R5_BLUE (21) +# define R200_TXA_ARG_A_TFACTOR1_ALPHA (26) +# define R200_TXA_ARG_A_TFACTOR1_BLUE (27) +# define R200_TXA_ARG_A_MASK (31 << 0) +# define R200_TXA_ARG_A_SHIFT 0 +# define R200_TXA_ARG_B_ZERO (0 << 5) +# define R200_TXA_ARG_B_CURRENT_ALPHA (2 << 5) /* guess */ +# define R200_TXA_ARG_B_CURRENT_BLUE (3 << 5) /* guess */ +# define R200_TXA_ARG_B_DIFFUSE_ALPHA (4 << 5) +# define R200_TXA_ARG_B_DIFFUSE_BLUE (5 << 5) +# define R200_TXA_ARG_B_SPECULAR_ALPHA (6 << 5) +# define R200_TXA_ARG_B_SPECULAR_BLUE (7 << 5) +# define R200_TXA_ARG_B_TFACTOR_ALPHA (8 << 5) +# define R200_TXA_ARG_B_TFACTOR_BLUE (9 << 5) +# define R200_TXA_ARG_B_R0_ALPHA (10 << 5) +# define R200_TXA_ARG_B_R0_BLUE (11 << 5) +# define R200_TXA_ARG_B_R1_ALPHA (12 << 5) +# define R200_TXA_ARG_B_R1_BLUE (13 << 5) +# define R200_TXA_ARG_B_R2_ALPHA (14 << 5) +# define R200_TXA_ARG_B_R2_BLUE (15 << 5) +# define R200_TXA_ARG_B_R3_ALPHA (16 << 5) +# define R200_TXA_ARG_B_R3_BLUE (17 << 5) +# define R200_TXA_ARG_B_R4_ALPHA (18 << 5) +# define R200_TXA_ARG_B_R4_BLUE (19 << 5) +# define R200_TXA_ARG_B_R5_ALPHA (20 << 5) +# define R200_TXA_ARG_B_R5_BLUE (21 << 5) +# define R200_TXA_ARG_B_TFACTOR1_ALPHA (26 << 5) +# define R200_TXA_ARG_B_TFACTOR1_BLUE (27 << 5) +# define R200_TXA_ARG_B_MASK (31 << 5) +# define R200_TXA_ARG_B_SHIFT 5 +# define R200_TXA_ARG_C_ZERO (0 << 10) +# define R200_TXA_ARG_C_CURRENT_ALPHA (2 << 10) /* guess */ +# define R200_TXA_ARG_C_CURRENT_BLUE (3 << 10) /* guess */ +# define R200_TXA_ARG_C_DIFFUSE_ALPHA (4 << 10) +# define R200_TXA_ARG_C_DIFFUSE_BLUE (5 << 10) +# define R200_TXA_ARG_C_SPECULAR_ALPHA (6 << 10) +# define R200_TXA_ARG_C_SPECULAR_BLUE (7 << 10) +# define R200_TXA_ARG_C_TFACTOR_ALPHA (8 << 10) +# define R200_TXA_ARG_C_TFACTOR_BLUE (9 << 10) +# define R200_TXA_ARG_C_R0_ALPHA (10 << 10) +# define R200_TXA_ARG_C_R0_BLUE (11 << 10) +# define R200_TXA_ARG_C_R1_ALPHA (12 << 10) +# define R200_TXA_ARG_C_R1_BLUE (13 << 10) +# define R200_TXA_ARG_C_R2_ALPHA (14 << 10) +# define R200_TXA_ARG_C_R2_BLUE (15 << 10) +# define R200_TXA_ARG_C_R3_ALPHA (16 << 10) +# define R200_TXA_ARG_C_R3_BLUE (17 << 10) +# define R200_TXA_ARG_C_R4_ALPHA (18 << 10) +# define R200_TXA_ARG_C_R4_BLUE (19 << 10) +# define R200_TXA_ARG_C_R5_ALPHA (20 << 10) +# define R200_TXA_ARG_C_R5_BLUE (21 << 10) +# define R200_TXA_ARG_C_TFACTOR1_ALPHA (26 << 10) +# define R200_TXA_ARG_C_TFACTOR1_BLUE (27 << 10) +# define R200_TXA_ARG_C_MASK (31 << 10) +# define R200_TXA_ARG_C_SHIFT 10 +# define R200_TXA_COMP_ARG_A (1 << 16) +# define R200_TXA_COMP_ARG_A_SHIFT (16) +# define R200_TXA_BIAS_ARG_A (1 << 17) +# define R200_TXA_SCALE_ARG_A (1 << 18) +# define R200_TXA_NEG_ARG_A (1 << 19) +# define R200_TXA_COMP_ARG_B (1 << 20) +# define R200_TXA_COMP_ARG_B_SHIFT (20) +# define R200_TXA_BIAS_ARG_B (1 << 21) +# define R200_TXA_SCALE_ARG_B (1 << 22) +# define R200_TXA_NEG_ARG_B (1 << 23) +# define R200_TXA_COMP_ARG_C (1 << 24) +# define R200_TXA_COMP_ARG_C_SHIFT (24) +# define R200_TXA_BIAS_ARG_C (1 << 25) +# define R200_TXA_SCALE_ARG_C (1 << 26) +# define R200_TXA_NEG_ARG_C (1 << 27) +# define R200_TXA_OP_MADD (0 << 28) +# define R200_TXA_OP_CND0 (2 << 28) +# define R200_TXA_OP_LERP (3 << 28) +# define R200_TXA_OP_CONDITIONAL (6 << 28) +# define R200_TXA_OP_MASK (7 << 28) +#define R200_PP_TXABLEND2_0 0x2f0c +# define R200_TXA_TFACTOR_SEL_SHIFT 0 +# define R200_TXA_TFACTOR_SEL_MASK 0x7 +# define R200_TXA_TFACTOR1_SEL_SHIFT 4 +# define R200_TXA_TFACTOR1_SEL_MASK (0x7 << 4) +# define R200_TXA_SCALE_SHIFT 8 +# define R200_TXA_SCALE_MASK (7 << 8) +# define R200_TXA_SCALE_1X (0 << 8) +# define R200_TXA_SCALE_2X (1 << 8) +# define R200_TXA_SCALE_4X (2 << 8) +# define R200_TXA_SCALE_8X (3 << 8) +# define R200_TXA_SCALE_INV2 (5 << 8) +# define R200_TXA_SCALE_INV4 (6 << 8) +# define R200_TXA_SCALE_INV8 (7 << 8) +# define R200_TXA_CLAMP_SHIFT 12 +# define R200_TXA_CLAMP_MASK (3 << 12) +# define R200_TXA_CLAMP_WRAP (0 << 12) +# define R200_TXA_CLAMP_0_1 (1 << 12) +# define R200_TXA_CLAMP_8_8 (2 << 12) +# define R200_TXA_OUTPUT_REG_MASK (7 << 16) +# define R200_TXA_OUTPUT_REG_NONE (0 << 16) +# define R200_TXA_OUTPUT_REG_R0 (1 << 16) +# define R200_TXA_OUTPUT_REG_R1 (2 << 16) +# define R200_TXA_OUTPUT_REG_R2 (3 << 16) +# define R200_TXA_OUTPUT_REG_R3 (4 << 16) +# define R200_TXA_OUTPUT_REG_R4 (5 << 16) +# define R200_TXA_OUTPUT_REG_R5 (6 << 16) +# define R200_TXA_DOT_ALPHA (1 << 20) +# define R200_TXA_REPL_NORMAL 0 +# define R200_TXA_REPL_RED 1 +# define R200_TXA_REPL_GREEN 2 +# define R200_TXA_REPL_ARG_A_SHIFT 26 +# define R200_TXA_REPL_ARG_A_MASK (3 << 26) +# define R200_TXA_REPL_ARG_B_SHIFT 28 +# define R200_TXA_REPL_ARG_B_MASK (3 << 28) +# define R200_TXA_REPL_ARG_C_SHIFT 30 +# define R200_TXA_REPL_ARG_C_MASK (3 << 30) + +#define R200_SE_VTX_FMT_0 0x2088 +# define R200_VTX_XY 0 /* always have xy */ +# define R200_VTX_Z0 (1<<0) +# define R200_VTX_W0 (1<<1) +# define R200_VTX_WEIGHT_COUNT_SHIFT (2) +# define R200_VTX_PV_MATRIX_SEL (1<<5) +# define R200_VTX_N0 (1<<6) +# define R200_VTX_POINT_SIZE (1<<7) +# define R200_VTX_DISCRETE_FOG (1<<8) +# define R200_VTX_SHININESS_0 (1<<9) +# define R200_VTX_SHININESS_1 (1<<10) +# define R200_VTX_COLOR_NOT_PRESENT 0 +# define R200_VTX_PK_RGBA 1 +# define R200_VTX_FP_RGB 2 +# define R200_VTX_FP_RGBA 3 +# define R200_VTX_COLOR_MASK 3 +# define R200_VTX_COLOR_0_SHIFT 11 +# define R200_VTX_COLOR_1_SHIFT 13 +# define R200_VTX_COLOR_2_SHIFT 15 +# define R200_VTX_COLOR_3_SHIFT 17 +# define R200_VTX_COLOR_4_SHIFT 19 +# define R200_VTX_COLOR_5_SHIFT 21 +# define R200_VTX_COLOR_6_SHIFT 23 +# define R200_VTX_COLOR_7_SHIFT 25 +# define R200_VTX_XY1 (1<<28) +# define R200_VTX_Z1 (1<<29) +# define R200_VTX_W1 (1<<30) +# define R200_VTX_N1 (1<<31) +#define R200_SE_VTX_FMT_1 0x208c +# define R200_VTX_TEX0_COMP_CNT_SHIFT 0 +# define R200_VTX_TEX1_COMP_CNT_SHIFT 3 +# define R200_VTX_TEX2_COMP_CNT_SHIFT 6 +# define R200_VTX_TEX3_COMP_CNT_SHIFT 9 +# define R200_VTX_TEX4_COMP_CNT_SHIFT 12 +# define R200_VTX_TEX5_COMP_CNT_SHIFT 15 + +#define R200_SE_TCL_OUTPUT_VTX_FMT_0 0x2090 +#define R200_SE_TCL_OUTPUT_VTX_FMT_1 0x2094 +#define R200_SE_TCL_OUTPUT_VTX_COMP_SEL 0x2250 +# define R200_OUTPUT_XYZW (1<<0) +# define R200_OUTPUT_COLOR_0 (1<<8) +# define R200_OUTPUT_COLOR_1 (1<<9) +# define R200_OUTPUT_TEX_0 (1<<16) +# define R200_OUTPUT_TEX_1 (1<<17) +# define R200_OUTPUT_TEX_2 (1<<18) +# define R200_OUTPUT_TEX_3 (1<<19) +# define R200_OUTPUT_TEX_4 (1<<20) +# define R200_OUTPUT_TEX_5 (1<<21) +# define R200_OUTPUT_TEX_MASK (0x3f<<16) +# define R200_OUTPUT_DISCRETE_FOG (1<<24) +# define R200_OUTPUT_PT_SIZE (1<<25) +# define R200_FORCE_INORDER_PROC (1<<31) +#define R200_PP_CNTL_X 0x2cc4 +#define R200_PP_TXMULTI_CTL_0 0x2c1c +#define R200_SE_VTX_STATE_CNTL 0x2180 +# define R200_UPDATE_USER_COLOR_0_ENA_MASK (1<<16) + + /* Registers for CP and Microcode Engine */ +#define RADEON_CP_ME_RAM_ADDR 0x07d4 +#define RADEON_CP_ME_RAM_RADDR 0x07d8 +#define RADEON_CP_ME_RAM_DATAH 0x07dc +#define RADEON_CP_ME_RAM_DATAL 0x07e0 + +#define RADEON_CP_RB_BASE 0x0700 +#define RADEON_CP_RB_CNTL 0x0704 +#define RADEON_CP_RB_RPTR_ADDR 0x070c +#define RADEON_CP_RB_RPTR 0x0710 +#define RADEON_CP_RB_WPTR 0x0714 + +#define RADEON_CP_IB_BASE 0x0738 +#define RADEON_CP_IB_BUFSZ 0x073c + +#define RADEON_CP_CSQ_CNTL 0x0740 +# define RADEON_CSQ_CNT_PRIMARY_MASK (0xff << 0) +# define RADEON_CSQ_PRIDIS_INDDIS (0 << 28) +# define RADEON_CSQ_PRIPIO_INDDIS (1 << 28) +# define RADEON_CSQ_PRIBM_INDDIS (2 << 28) +# define RADEON_CSQ_PRIPIO_INDBM (3 << 28) +# define RADEON_CSQ_PRIBM_INDBM (4 << 28) +# define RADEON_CSQ_PRIPIO_INDPIO (15 << 28) + +#define R300_CP_RESYNC_ADDR 0x778 +#define R300_CP_RESYNC_DATA 0x77c + +#define RADEON_CP_CSQ_STAT 0x07f8 +# define RADEON_CSQ_RPTR_PRIMARY_MASK (0xff << 0) +# define RADEON_CSQ_WPTR_PRIMARY_MASK (0xff << 8) +# define RADEON_CSQ_RPTR_INDIRECT_MASK (0xff << 16) +# define RADEON_CSQ_WPTR_INDIRECT_MASK (0xff << 24) +#define RADEON_CP_CSQ_ADDR 0x07f0 +#define RADEON_CP_CSQ_DATA 0x07f4 +#define RADEON_CP_CSQ_APER_PRIMARY 0x1000 +#define RADEON_CP_CSQ_APER_INDIRECT 0x1300 + +#define RADEON_CP_RB_WPTR_DELAY 0x0718 +# define RADEON_PRE_WRITE_TIMER_SHIFT 0 +# define RADEON_PRE_WRITE_LIMIT_SHIFT 23 + +#define RADEON_AIC_CNTL 0x01d0 +# define RADEON_PCIGART_TRANSLATE_EN (1 << 0) +#define RADEON_AIC_LO_ADDR 0x01dc + + + + /* Constants */ +//#define RADEON_LAST_FRAME_REG RADEON_GUI_SCRATCH_REG0 +//efine RADEON_LAST_CLEAR_REG RADEON_GUI_SCRATCH_REG2 + + + + /* CP packet types */ +#define RADEON_CP_PACKET0 0x00000000 +#define RADEON_CP_PACKET1 0x40000000 +#define RADEON_CP_PACKET2 0x80000000 +#define RADEON_CP_PACKET3 0xC0000000 +# define RADEON_CP_PACKET_MASK 0xC0000000 +# define RADEON_CP_PACKET_COUNT_MASK 0x3fff0000 +# define RADEON_CP_PACKET_MAX_DWORDS (1 << 12) +# define RADEON_CP_PACKET0_REG_MASK 0x000007ff +# define R300_CP_PACKET0_REG_MASK 0x00001fff +# define RADEON_CP_PACKET1_REG0_MASK 0x000007ff +# define RADEON_CP_PACKET1_REG1_MASK 0x003ff800 + +#define RADEON_CP_PACKET0_ONE_REG_WR 0x00008000 + +#define RADEON_CP_PACKET3_NOP 0xC0001000 +#define RADEON_CP_PACKET3_NEXT_CHAR 0xC0001900 +#define RADEON_CP_PACKET3_PLY_NEXTSCAN 0xC0001D00 +#define RADEON_CP_PACKET3_SET_SCISSORS 0xC0001E00 +#define RADEON_CP_PACKET3_3D_RNDR_GEN_INDX_PRIM 0xC0002300 +#define RADEON_CP_PACKET3_LOAD_MICROCODE 0xC0002400 +#define RADEON_CP_PACKET3_WAIT_FOR_IDLE 0xC0002600 +#define RADEON_CP_PACKET3_3D_DRAW_VBUF 0xC0002800 +#define RADEON_CP_PACKET3_3D_DRAW_IMMD 0xC0002900 +#define RADEON_CP_PACKET3_3D_DRAW_INDX 0xC0002A00 +#define RADEON_CP_PACKET3_LOAD_PALETTE 0xC0002C00 +#define R200_CP_PACKET3_3D_DRAW_IMMD_2 0xc0003500 +#define RADEON_CP_PACKET3_3D_LOAD_VBPNTR 0xC0002F00 +#define RADEON_CP_PACKET3_CNTL_PAINT 0xC0009100 +#define RADEON_CP_PACKET3_CNTL_BITBLT 0xC0009200 +#define RADEON_CP_PACKET3_CNTL_SMALLTEXT 0xC0009300 +#define RADEON_CP_PACKET3_CNTL_HOSTDATA_BLT 0xC0009400 +#define RADEON_CP_PACKET3_CNTL_POLYLINE 0xC0009500 +#define RADEON_CP_PACKET3_CNTL_POLYSCANLINES 0xC0009800 +#define RADEON_CP_PACKET3_CNTL_PAINT_MULTI 0xC0009A00 +#define RADEON_CP_PACKET3_CNTL_BITBLT_MULTI 0xC0009B00 +#define RADEON_CP_PACKET3_CNTL_TRANS_BITBLT 0xC0009C00 + + +#define RADEON_CP_VC_FRMT_XY 0x00000000 +#define RADEON_CP_VC_FRMT_W0 0x00000001 +#define RADEON_CP_VC_FRMT_FPCOLOR 0x00000002 +#define RADEON_CP_VC_FRMT_FPALPHA 0x00000004 +#define RADEON_CP_VC_FRMT_PKCOLOR 0x00000008 +#define RADEON_CP_VC_FRMT_FPSPEC 0x00000010 +#define RADEON_CP_VC_FRMT_FPFOG 0x00000020 +#define RADEON_CP_VC_FRMT_PKSPEC 0x00000040 +#define RADEON_CP_VC_FRMT_ST0 0x00000080 +#define RADEON_CP_VC_FRMT_ST1 0x00000100 +#define RADEON_CP_VC_FRMT_Q1 0x00000200 +#define RADEON_CP_VC_FRMT_ST2 0x00000400 +#define RADEON_CP_VC_FRMT_Q2 0x00000800 +#define RADEON_CP_VC_FRMT_ST3 0x00001000 +#define RADEON_CP_VC_FRMT_Q3 0x00002000 +#define RADEON_CP_VC_FRMT_Q0 0x00004000 +#define RADEON_CP_VC_FRMT_BLND_WEIGHT_CNT_MASK 0x00038000 +#define RADEON_CP_VC_FRMT_N0 0x00040000 +#define RADEON_CP_VC_FRMT_XY1 0x08000000 +#define RADEON_CP_VC_FRMT_Z1 0x10000000 +#define RADEON_CP_VC_FRMT_W1 0x20000000 +#define RADEON_CP_VC_FRMT_N1 0x40000000 +#define RADEON_CP_VC_FRMT_Z 0x80000000 + +#define RADEON_CP_VC_CNTL_PRIM_TYPE_NONE 0x00000000 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_POINT 0x00000001 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_LINE 0x00000002 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_LINE_STRIP 0x00000003 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_TRI_LIST 0x00000004 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_TRI_FAN 0x00000005 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_TRI_STRIP 0x00000006 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_TRI_TYPE_2 0x00000007 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_RECT_LIST 0x00000008 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_3VRT_POINT_LIST 0x00000009 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_3VRT_LINE_LIST 0x0000000a +#define RADEON_CP_VC_CNTL_PRIM_WALK_IND 0x00000010 +#define RADEON_CP_VC_CNTL_PRIM_WALK_LIST 0x00000020 +#define RADEON_CP_VC_CNTL_PRIM_WALK_RING 0x00000030 +#define RADEON_CP_VC_CNTL_COLOR_ORDER_BGRA 0x00000000 +#define RADEON_CP_VC_CNTL_COLOR_ORDER_RGBA 0x00000040 +#define RADEON_CP_VC_CNTL_MAOS_ENABLE 0x00000080 +#define RADEON_CP_VC_CNTL_VTX_FMT_NON_RADEON_MODE 0x00000000 +#define RADEON_CP_VC_CNTL_VTX_FMT_RADEON_MODE 0x00000100 +#define RADEON_CP_VC_CNTL_TCL_DISABLE 0x00000000 +#define RADEON_CP_VC_CNTL_TCL_ENABLE 0x00000200 +#define RADEON_CP_VC_CNTL_NUM_SHIFT 16 + +#define RADEON_VS_MATRIX_0_ADDR 0 +#define RADEON_VS_MATRIX_1_ADDR 4 +#define RADEON_VS_MATRIX_2_ADDR 8 +#define RADEON_VS_MATRIX_3_ADDR 12 +#define RADEON_VS_MATRIX_4_ADDR 16 +#define RADEON_VS_MATRIX_5_ADDR 20 +#define RADEON_VS_MATRIX_6_ADDR 24 +#define RADEON_VS_MATRIX_7_ADDR 28 +#define RADEON_VS_MATRIX_8_ADDR 32 +#define RADEON_VS_MATRIX_9_ADDR 36 +#define RADEON_VS_MATRIX_10_ADDR 40 +#define RADEON_VS_MATRIX_11_ADDR 44 +#define RADEON_VS_MATRIX_12_ADDR 48 +#define RADEON_VS_MATRIX_13_ADDR 52 +#define RADEON_VS_MATRIX_14_ADDR 56 +#define RADEON_VS_MATRIX_15_ADDR 60 +#define RADEON_VS_LIGHT_AMBIENT_ADDR 64 +#define RADEON_VS_LIGHT_DIFFUSE_ADDR 72 +#define RADEON_VS_LIGHT_SPECULAR_ADDR 80 +#define RADEON_VS_LIGHT_DIRPOS_ADDR 88 +#define RADEON_VS_LIGHT_HWVSPOT_ADDR 96 +#define RADEON_VS_LIGHT_ATTENUATION_ADDR 104 +#define RADEON_VS_MATRIX_EYE2CLIP_ADDR 112 +#define RADEON_VS_UCP_ADDR 116 +#define RADEON_VS_GLOBAL_AMBIENT_ADDR 122 +#define RADEON_VS_FOG_PARAM_ADDR 123 +#define RADEON_VS_EYE_VECTOR_ADDR 124 + +#define RADEON_SS_LIGHT_DCD_ADDR 0 +#define RADEON_SS_LIGHT_SPOT_EXPONENT_ADDR 8 +#define RADEON_SS_LIGHT_SPOT_CUTOFF_ADDR 16 +#define RADEON_SS_LIGHT_SPECULAR_THRESH_ADDR 24 +#define RADEON_SS_LIGHT_RANGE_CUTOFF_ADDR 32 +#define RADEON_SS_VERT_GUARD_CLIP_ADJ_ADDR 48 +#define RADEON_SS_VERT_GUARD_DISCARD_ADJ_ADDR 49 +#define RADEON_SS_HORZ_GUARD_CLIP_ADJ_ADDR 50 +#define RADEON_SS_HORZ_GUARD_DISCARD_ADJ_ADDR 51 +#define RADEON_SS_SHININESS 60 + +#define RADEON_TV_MASTER_CNTL 0x0800 +# define RADEON_TV_ASYNC_RST (1 << 0) +# define RADEON_CRT_ASYNC_RST (1 << 1) +# define RADEON_RESTART_PHASE_FIX (1 << 3) +# define RADEON_TV_FIFO_ASYNC_RST (1 << 4) +# define RADEON_VIN_ASYNC_RST (1 << 5) +# define RADEON_AUD_ASYNC_RST (1 << 6) +# define RADEON_DVS_ASYNC_RST (1 << 7) +# define RADEON_CRT_FIFO_CE_EN (1 << 9) +# define RADEON_TV_FIFO_CE_EN (1 << 10) +# define RADEON_RE_SYNC_NOW_SEL_MASK (3 << 14) +# define RADEON_TVCLK_ALWAYS_ONb (1 << 30) +# define RADEON_TV_ON (1 << 31) +#define RADEON_TV_PRE_DAC_MUX_CNTL 0x0888 +# define RADEON_Y_RED_EN (1 << 0) +# define RADEON_C_GRN_EN (1 << 1) +# define RADEON_CMP_BLU_EN (1 << 2) +# define RADEON_DAC_DITHER_EN (1 << 3) +# define RADEON_RED_MX_FORCE_DAC_DATA (6 << 4) +# define RADEON_GRN_MX_FORCE_DAC_DATA (6 << 8) +# define RADEON_BLU_MX_FORCE_DAC_DATA (6 << 12) +# define RADEON_TV_FORCE_DAC_DATA_SHIFT 16 +#define RADEON_TV_RGB_CNTL 0x0804 +# define RADEON_SWITCH_TO_BLUE (1 << 4) +# define RADEON_RGB_DITHER_EN (1 << 5) +# define RADEON_RGB_SRC_SEL_MASK (3 << 8) +# define RADEON_RGB_SRC_SEL_CRTC1 (0 << 8) +# define RADEON_RGB_SRC_SEL_RMX (1 << 8) +# define RADEON_RGB_SRC_SEL_CRTC2 (2 << 8) +# define RADEON_RGB_CONVERT_BY_PASS (1 << 10) +# define RADEON_UVRAM_READ_MARGIN_SHIFT 16 +# define RADEON_FIFORAM_FFMACRO_READ_MARGIN_SHIFT 20 +# define RADEON_TVOUT_SCALE_EN (1 << 26) +#define RADEON_TV_SYNC_CNTL 0x0808 +# define RADEON_SYNC_OE (1 << 0) +# define RADEON_SYNC_OUT (1 << 1) +# define RADEON_SYNC_IN (1 << 2) +# define RADEON_SYNC_PUB (1 << 3) +# define RADEON_SYNC_PD (1 << 4) +# define RADEON_TV_SYNC_IO_DRIVE (1 << 5) +#define RADEON_TV_HTOTAL 0x080c +#define RADEON_TV_HDISP 0x0810 +#define RADEON_TV_HSTART 0x0818 +#define RADEON_TV_HCOUNT 0x081C +#define RADEON_TV_VTOTAL 0x0820 +#define RADEON_TV_VDISP 0x0824 +#define RADEON_TV_VCOUNT 0x0828 +#define RADEON_TV_FTOTAL 0x082c +#define RADEON_TV_FCOUNT 0x0830 +#define RADEON_TV_FRESTART 0x0834 +#define RADEON_TV_HRESTART 0x0838 +#define RADEON_TV_VRESTART 0x083c +#define RADEON_TV_HOST_READ_DATA 0x0840 +#define RADEON_TV_HOST_WRITE_DATA 0x0844 +#define RADEON_TV_HOST_RD_WT_CNTL 0x0848 +# define RADEON_HOST_FIFO_RD (1 << 12) +# define RADEON_HOST_FIFO_RD_ACK (1 << 13) +# define RADEON_HOST_FIFO_WT (1 << 14) +# define RADEON_HOST_FIFO_WT_ACK (1 << 15) +#define RADEON_TV_VSCALER_CNTL1 0x084c +# define RADEON_UV_INC_MASK 0xffff +# define RADEON_UV_INC_SHIFT 0 +# define RADEON_Y_W_EN (1 << 24) +# define RADEON_RESTART_FIELD (1 << 29) /* restart on field 0 */ +# define RADEON_Y_DEL_W_SIG_SHIFT 26 +#define RADEON_TV_TIMING_CNTL 0x0850 +# define RADEON_H_INC_MASK 0xfff +# define RADEON_H_INC_SHIFT 0 +# define RADEON_REQ_Y_FIRST (1 << 19) +# define RADEON_FORCE_BURST_ALWAYS (1 << 21) +# define RADEON_UV_POST_SCALE_BYPASS (1 << 23) +# define RADEON_UV_OUTPUT_POST_SCALE_SHIFT 24 +#define RADEON_TV_VSCALER_CNTL2 0x0854 +# define RADEON_DITHER_MODE (1 << 0) +# define RADEON_Y_OUTPUT_DITHER_EN (1 << 1) +# define RADEON_UV_OUTPUT_DITHER_EN (1 << 2) +# define RADEON_UV_TO_BUF_DITHER_EN (1 << 3) +#define RADEON_TV_Y_FALL_CNTL 0x0858 +# define RADEON_Y_FALL_PING_PONG (1 << 16) +# define RADEON_Y_COEF_EN (1 << 17) +#define RADEON_TV_Y_RISE_CNTL 0x085c +# define RADEON_Y_RISE_PING_PONG (1 << 16) +#define RADEON_TV_Y_SAW_TOOTH_CNTL 0x0860 +#define RADEON_TV_UPSAMP_AND_GAIN_CNTL 0x0864 +# define RADEON_YUPSAMP_EN (1 << 0) +# define RADEON_UVUPSAMP_EN (1 << 2) +#define RADEON_TV_GAIN_LIMIT_SETTINGS 0x0868 +# define RADEON_Y_GAIN_LIMIT_SHIFT 0 +# define RADEON_UV_GAIN_LIMIT_SHIFT 16 +#define RADEON_TV_LINEAR_GAIN_SETTINGS 0x086c +# define RADEON_Y_GAIN_SHIFT 0 +# define RADEON_UV_GAIN_SHIFT 16 +#define RADEON_TV_MODULATOR_CNTL1 0x0870 +# define RADEON_YFLT_EN (1 << 2) +# define RADEON_UVFLT_EN (1 << 3) +# define RADEON_ALT_PHASE_EN (1 << 6) +# define RADEON_SYNC_TIP_LEVEL (1 << 7) +# define RADEON_BLANK_LEVEL_SHIFT 8 +# define RADEON_SET_UP_LEVEL_SHIFT 16 +# define RADEON_SLEW_RATE_LIMIT (1 << 23) +# define RADEON_CY_FILT_BLEND_SHIFT 28 +#define RADEON_TV_MODULATOR_CNTL2 0x0874 +# define RADEON_TV_U_BURST_LEVEL_MASK 0x1ff +# define RADEON_TV_V_BURST_LEVEL_MASK 0x1ff +# define RADEON_TV_V_BURST_LEVEL_SHIFT 16 +#define RADEON_TV_CRC_CNTL 0x0890 +#define RADEON_TV_UV_ADR 0x08ac +# define RADEON_MAX_UV_ADR_MASK 0x000000ff +# define RADEON_MAX_UV_ADR_SHIFT 0 +# define RADEON_TABLE1_BOT_ADR_MASK 0x0000ff00 +# define RADEON_TABLE1_BOT_ADR_SHIFT 8 +# define RADEON_TABLE3_TOP_ADR_MASK 0x00ff0000 +# define RADEON_TABLE3_TOP_ADR_SHIFT 16 +# define RADEON_HCODE_TABLE_SEL_MASK 0x06000000 +# define RADEON_HCODE_TABLE_SEL_SHIFT 25 +# define RADEON_VCODE_TABLE_SEL_MASK 0x18000000 +# define RADEON_VCODE_TABLE_SEL_SHIFT 27 +# define RADEON_TV_MAX_FIFO_ADDR 0x1a7 +# define RADEON_TV_MAX_FIFO_ADDR_INTERNAL 0x1ff +#define RADEON_TV_PLL_FINE_CNTL 0x0020 /* PLL */ +#define RADEON_TV_PLL_CNTL 0x0021 /* PLL */ +# define RADEON_TV_M0LO_MASK 0xff +# define RADEON_TV_M0HI_MASK 0x7 +# define RADEON_TV_M0HI_SHIFT 18 +# define RADEON_TV_N0LO_MASK 0x1ff +# define RADEON_TV_N0LO_SHIFT 8 +# define RADEON_TV_N0HI_MASK 0x3 +# define RADEON_TV_N0HI_SHIFT 21 +# define RADEON_TV_P_MASK 0xf +# define RADEON_TV_P_SHIFT 24 +# define RADEON_TV_SLIP_EN (1 << 23) +# define RADEON_TV_DTO_EN (1 << 28) +#define RADEON_TV_PLL_CNTL1 0x0022 /* PLL */ +# define RADEON_TVPLL_RESET (1 << 1) +# define RADEON_TVPLL_SLEEP (1 << 3) +# define RADEON_TVPLL_REFCLK_SEL (1 << 4) +# define RADEON_TVPCP_SHIFT 8 +# define RADEON_TVPCP_MASK (7 << 8) +# define RADEON_TVPVG_SHIFT 11 +# define RADEON_TVPVG_MASK (7 << 11) +# define RADEON_TVPDC_SHIFT 14 +# define RADEON_TVPDC_MASK (3 << 14) +# define RADEON_TVPLL_TEST_DIS (1 << 31) +# define RADEON_TVCLK_SRC_SEL_TVPLL (1 << 30) + +#define RS400_DISP2_REQ_CNTL1 0xe30 +# define RS400_DISP2_START_REQ_LEVEL_SHIFT 0 +# define RS400_DISP2_START_REQ_LEVEL_MASK 0x3ff +# define RS400_DISP2_STOP_REQ_LEVEL_SHIFT 12 +# define RS400_DISP2_STOP_REQ_LEVEL_MASK 0x3ff +# define RS400_DISP2_ALLOW_FID_LEVEL_SHIFT 22 +# define RS400_DISP2_ALLOW_FID_LEVEL_MASK 0x3ff +#define RS400_DISP2_REQ_CNTL2 0xe34 +# define RS400_DISP2_CRITICAL_POINT_START_SHIFT 12 +# define RS400_DISP2_CRITICAL_POINT_START_MASK 0x3ff +# define RS400_DISP2_CRITICAL_POINT_STOP_SHIFT 22 +# define RS400_DISP2_CRITICAL_POINT_STOP_MASK 0x3ff +#define RS400_DMIF_MEM_CNTL1 0xe38 +# define RS400_DISP2_START_ADR_SHIFT 0 +# define RS400_DISP2_START_ADR_MASK 0x3ff +# define RS400_DISP1_CRITICAL_POINT_START_SHIFT 12 +# define RS400_DISP1_CRITICAL_POINT_START_MASK 0x3ff +# define RS400_DISP1_CRITICAL_POINT_STOP_SHIFT 22 +# define RS400_DISP1_CRITICAL_POINT_STOP_MASK 0x3ff +#define RS400_DISP1_REQ_CNTL1 0xe3c +# define RS400_DISP1_START_REQ_LEVEL_SHIFT 0 +# define RS400_DISP1_START_REQ_LEVEL_MASK 0x3ff +# define RS400_DISP1_STOP_REQ_LEVEL_SHIFT 12 +# define RS400_DISP1_STOP_REQ_LEVEL_MASK 0x3ff +# define RS400_DISP1_ALLOW_FID_LEVEL_SHIFT 22 +# define RS400_DISP1_ALLOW_FID_LEVEL_MASK 0x3ff + +#define RS690_MC_INDEX 0x78 +# define RS690_MC_INDEX_MASK 0x1ff +# define RS690_MC_INDEX_WR_EN (1 << 9) +# define RS690_MC_INDEX_WR_ACK 0x7f +#define RS690_MC_DATA 0x7c + +#define RS690_MC_FB_LOCATION 0x100 +#define RS690_MC_AGP_LOCATION 0x101 +#define RS690_MC_AGP_BASE 0x102 +#define RS690_MC_AGP_BASE_2 0x103 +#define RS690_MC_STATUS 0x90 +#define RS690_MC_STATUS_IDLE (1 << 0) + +#define RS600_MC_INDEX 0x78 +# define RS600_MC_INDEX_MASK 0xff +# define RS600_MC_INDEX_WR_EN (1 << 8) +# define RS600_MC_INDEX_WR_ACK 0xff +#define RS600_MC_DATA 0x7c + +#define RS600_MC_FB_LOCATION 0xA +#define RS600_MC_STATUS 0x0 +#define RS600_MC_STATUS_IDLE (1 << 0) + +#define AVIVO_MC_INDEX 0x0070 +#define R520_MC_STATUS 0x00 +#define R520_MC_STATUS_IDLE (1<<1) +#define RV515_MC_STATUS 0x08 +#define RV515_MC_STATUS_IDLE (1<<4) +#define AVIVO_MC_DATA 0x0074 + +#define RV515_MC_FB_LOCATION 0x1 +#define RV515_MC_AGP_LOCATION 0x2 +#define RV515_MC_AGP_BASE 0x3 +#define RV515_MC_AGP_BASE_2 0x4 +#define RV515_MC_CNTL 0x5 +# define RV515_MEM_NUM_CHANNELS_MASK 0x3 +#define R520_MC_FB_LOCATION 0x4 +#define R520_MC_AGP_LOCATION 0x5 +#define R520_MC_AGP_BASE 0x6 +#define R520_MC_AGP_BASE_2 0x7 +#define R520_MC_CNTL0 0x8 +# define R520_MEM_NUM_CHANNELS_MASK (0x3 << 24) +# define R520_MEM_NUM_CHANNELS_SHIFT 24 +# define R520_MC_CHANNEL_SIZE (1 << 23) + +#define R600_RAMCFG 0x2408 +# define R600_CHANSIZE (1 << 7) +# define R600_CHANSIZE_OVERRIDE (1 << 10) + +#define AVIVO_HDP_FB_LOCATION 0x134 + +#define AVIVO_VGA_RENDER_CONTROL 0x0300 +# define AVIVO_VGA_VSTATUS_CNTL_MASK (3 << 16) +#define AVIVO_D1VGA_CONTROL 0x0330 +# define AVIVO_DVGA_CONTROL_MODE_ENABLE (1<<0) +# define AVIVO_DVGA_CONTROL_TIMING_SELECT (1<<8) +# define AVIVO_DVGA_CONTROL_SYNC_POLARITY_SELECT (1<<9) +# define AVIVO_DVGA_CONTROL_OVERSCAN_TIMING_SELECT (1<<10) +# define AVIVO_DVGA_CONTROL_OVERSCAN_COLOR_EN (1<<16) +# define AVIVO_DVGA_CONTROL_ROTATE (1<<24) +#define AVIVO_D2VGA_CONTROL 0x0338 + +#define AVIVO_EXT1_PPLL_REF_DIV_SRC 0x400 +#define AVIVO_EXT1_PPLL_REF_DIV 0x404 +#define AVIVO_EXT1_PPLL_UPDATE_LOCK 0x408 +#define AVIVO_EXT1_PPLL_UPDATE_CNTL 0x40c + +#define AVIVO_EXT2_PPLL_REF_DIV_SRC 0x410 +#define AVIVO_EXT2_PPLL_REF_DIV 0x414 +#define AVIVO_EXT2_PPLL_UPDATE_LOCK 0x418 +#define AVIVO_EXT2_PPLL_UPDATE_CNTL 0x41c + +#define AVIVO_EXT1_PPLL_FB_DIV 0x430 +#define AVIVO_EXT2_PPLL_FB_DIV 0x434 + +#define AVIVO_EXT1_PPLL_POST_DIV_SRC 0x438 +#define AVIVO_EXT1_PPLL_POST_DIV 0x43c + +#define AVIVO_EXT2_PPLL_POST_DIV_SRC 0x440 +#define AVIVO_EXT2_PPLL_POST_DIV 0x444 + +#define AVIVO_EXT1_PPLL_CNTL 0x448 +#define AVIVO_EXT2_PPLL_CNTL 0x44c + +#define AVIVO_P1PLL_CNTL 0x450 +#define AVIVO_P2PLL_CNTL 0x454 +#define AVIVO_P1PLL_INT_SS_CNTL 0x458 +#define AVIVO_P2PLL_INT_SS_CNTL 0x45c +#define AVIVO_P1PLL_TMDSA_CNTL 0x460 +#define AVIVO_P2PLL_LVTMA_CNTL 0x464 + +#define AVIVO_PCLK_CRTC1_CNTL 0x480 +#define AVIVO_PCLK_CRTC2_CNTL 0x484 + +#define AVIVO_D1CRTC_H_TOTAL 0x6000 +#define AVIVO_D1CRTC_H_BLANK_START_END 0x6004 +#define AVIVO_D1CRTC_H_SYNC_A 0x6008 +#define AVIVO_D1CRTC_H_SYNC_A_CNTL 0x600c +#define AVIVO_D1CRTC_H_SYNC_B 0x6010 +#define AVIVO_D1CRTC_H_SYNC_B_CNTL 0x6014 + +#define AVIVO_D1CRTC_V_TOTAL 0x6020 +#define AVIVO_D1CRTC_V_BLANK_START_END 0x6024 +#define AVIVO_D1CRTC_V_SYNC_A 0x6028 +#define AVIVO_D1CRTC_V_SYNC_A_CNTL 0x602c +#define AVIVO_D1CRTC_V_SYNC_B 0x6030 +#define AVIVO_D1CRTC_V_SYNC_B_CNTL 0x6034 + +#define AVIVO_D1CRTC_CONTROL 0x6080 +# define AVIVO_CRTC_EN (1 << 0) +#define AVIVO_D1CRTC_BLANK_CONTROL 0x6084 +#define AVIVO_D1CRTC_INTERLACE_CONTROL 0x6088 +#define AVIVO_D1CRTC_INTERLACE_STATUS 0x608c +#define AVIVO_D1CRTC_STEREO_CONTROL 0x60c4 + +/* master controls */ +#define AVIVO_DC_CRTC_MASTER_EN 0x60f8 +#define AVIVO_DC_CRTC_TV_CONTROL 0x60fc + +#define AVIVO_D1GRPH_ENABLE 0x6100 +#define AVIVO_D1GRPH_CONTROL 0x6104 +# define AVIVO_D1GRPH_CONTROL_DEPTH_8BPP (0 << 0) +# define AVIVO_D1GRPH_CONTROL_DEPTH_16BPP (1 << 0) +# define AVIVO_D1GRPH_CONTROL_DEPTH_32BPP (2 << 0) +# define AVIVO_D1GRPH_CONTROL_DEPTH_64BPP (3 << 0) + +# define AVIVO_D1GRPH_CONTROL_8BPP_INDEXED (0 << 8) + +# define AVIVO_D1GRPH_CONTROL_16BPP_ARGB1555 (0 << 8) +# define AVIVO_D1GRPH_CONTROL_16BPP_RGB565 (1 << 8) +# define AVIVO_D1GRPH_CONTROL_16BPP_ARGB4444 (2 << 8) +# define AVIVO_D1GRPH_CONTROL_16BPP_AI88 (3 << 8) +# define AVIVO_D1GRPH_CONTROL_16BPP_MONO16 (4 << 8) + +# define AVIVO_D1GRPH_CONTROL_32BPP_ARGB8888 (0 << 8) +# define AVIVO_D1GRPH_CONTROL_32BPP_ARGB2101010 (1 << 8) +# define AVIVO_D1GRPH_CONTROL_32BPP_DIGITAL (2 << 8) +# define AVIVO_D1GRPH_CONTROL_32BPP_8B_ARGB2101010 (3 << 8) + + +# define AVIVO_D1GRPH_CONTROL_64BPP_ARGB16161616 (0 << 8) + +# define AVIVO_D1GRPH_SWAP_RB (1 << 16) +# define AVIVO_D1GRPH_TILED (1 << 20) +# define AVIVO_D1GRPH_MACRO_ADDRESS_MODE (1 << 21) + +#define AVIVO_D1GRPH_LUT_SEL 0x6108 +#define AVIVO_D1GRPH_PRIMARY_SURFACE_ADDRESS 0x6110 +#define AVIVO_D1GRPH_SECONDARY_SURFACE_ADDRESS 0x6118 +#define AVIVO_D1GRPH_PITCH 0x6120 +#define AVIVO_D1GRPH_SURFACE_OFFSET_X 0x6124 +#define AVIVO_D1GRPH_SURFACE_OFFSET_Y 0x6128 +#define AVIVO_D1GRPH_X_START 0x612c +#define AVIVO_D1GRPH_Y_START 0x6130 +#define AVIVO_D1GRPH_X_END 0x6134 +#define AVIVO_D1GRPH_Y_END 0x6138 +#define AVIVO_D1GRPH_UPDATE 0x6144 +# define AVIVO_D1GRPH_UPDATE_LOCK (1 << 16) +#define AVIVO_D1GRPH_FLIP_CONTROL 0x6148 + +#define AVIVO_D1CUR_CONTROL 0x6400 +# define AVIVO_D1CURSOR_EN (1 << 0) +# define AVIVO_D1CURSOR_MODE_SHIFT 8 +# define AVIVO_D1CURSOR_MODE_MASK (3 << 8) +# define AVIVO_D1CURSOR_MODE_24BPP 2 +#define AVIVO_D1CUR_SURFACE_ADDRESS 0x6408 +#define AVIVO_D1CUR_SIZE 0x6410 +#define AVIVO_D1CUR_POSITION 0x6414 +#define AVIVO_D1CUR_HOT_SPOT 0x6418 +#define AVIVO_D1CUR_UPDATE 0x6424 +# define AVIVO_D1CURSOR_UPDATE_LOCK (1 << 16) + +#define AVIVO_DC_LUT_RW_SELECT 0x6480 +#define AVIVO_DC_LUT_RW_MODE 0x6484 +#define AVIVO_DC_LUT_RW_INDEX 0x6488 +#define AVIVO_DC_LUT_SEQ_COLOR 0x648c +#define AVIVO_DC_LUT_PWL_DATA 0x6490 +#define AVIVO_DC_LUT_30_COLOR 0x6494 +#define AVIVO_DC_LUT_READ_PIPE_SELECT 0x6498 +#define AVIVO_DC_LUT_WRITE_EN_MASK 0x649c +#define AVIVO_DC_LUT_AUTOFILL 0x64a0 + +#define AVIVO_DC_LUTA_CONTROL 0x64c0 +#define AVIVO_DC_LUTA_BLACK_OFFSET_BLUE 0x64c4 +#define AVIVO_DC_LUTA_BLACK_OFFSET_GREEN 0x64c8 +#define AVIVO_DC_LUTA_BLACK_OFFSET_RED 0x64cc +#define AVIVO_DC_LUTA_WHITE_OFFSET_BLUE 0x64d0 +#define AVIVO_DC_LUTA_WHITE_OFFSET_GREEN 0x64d4 +#define AVIVO_DC_LUTA_WHITE_OFFSET_RED 0x64d8 + +#define AVIVO_D1MODE_DATA_FORMAT 0x6528 +# define AVIVO_D1MODE_INTERLEAVE_EN (1 << 0) +#define AVIVO_D1MODE_DESKTOP_HEIGHT 0x652C +#define AVIVO_D1MODE_VIEWPORT_START 0x6580 +#define AVIVO_D1MODE_VIEWPORT_SIZE 0x6584 +#define AVIVO_D1MODE_EXT_OVERSCAN_LEFT_RIGHT 0x6588 +#define AVIVO_D1MODE_EXT_OVERSCAN_TOP_BOTTOM 0x658c + +#define AVIVO_D1SCL_SCALER_ENABLE 0x6590 +#define AVIVO_D1SCL_SCALER_TAP_CONTROL 0x6594 +#define AVIVO_D1SCL_UPDATE 0x65cc +# define AVIVO_D1SCL_UPDATE_LOCK (1 << 16) + +/* second crtc */ +#define AVIVO_D2CRTC_H_TOTAL 0x6800 +#define AVIVO_D2CRTC_H_BLANK_START_END 0x6804 +#define AVIVO_D2CRTC_H_SYNC_A 0x6808 +#define AVIVO_D2CRTC_H_SYNC_A_CNTL 0x680c +#define AVIVO_D2CRTC_H_SYNC_B 0x6810 +#define AVIVO_D2CRTC_H_SYNC_B_CNTL 0x6814 + +#define AVIVO_D2CRTC_V_TOTAL 0x6820 +#define AVIVO_D2CRTC_V_BLANK_START_END 0x6824 +#define AVIVO_D2CRTC_V_SYNC_A 0x6828 +#define AVIVO_D2CRTC_V_SYNC_A_CNTL 0x682c +#define AVIVO_D2CRTC_V_SYNC_B 0x6830 +#define AVIVO_D2CRTC_V_SYNC_B_CNTL 0x6834 + +#define AVIVO_D2CRTC_CONTROL 0x6880 +#define AVIVO_D2CRTC_BLANK_CONTROL 0x6884 +#define AVIVO_D2CRTC_INTERLACE_CONTROL 0x6888 +#define AVIVO_D2CRTC_INTERLACE_STATUS 0x688c +#define AVIVO_D2CRTC_STEREO_CONTROL 0x68c4 + +#define AVIVO_D2GRPH_ENABLE 0x6900 +#define AVIVO_D2GRPH_CONTROL 0x6904 +#define AVIVO_D2GRPH_LUT_SEL 0x6908 +#define AVIVO_D2GRPH_PRIMARY_SURFACE_ADDRESS 0x6910 +#define AVIVO_D2GRPH_SECONDARY_SURFACE_ADDRESS 0x6918 +#define AVIVO_D2GRPH_PITCH 0x6920 +#define AVIVO_D2GRPH_SURFACE_OFFSET_X 0x6924 +#define AVIVO_D2GRPH_SURFACE_OFFSET_Y 0x6928 +#define AVIVO_D2GRPH_X_START 0x692c +#define AVIVO_D2GRPH_Y_START 0x6930 +#define AVIVO_D2GRPH_X_END 0x6934 +#define AVIVO_D2GRPH_Y_END 0x6938 +#define AVIVO_D2GRPH_UPDATE 0x6944 +#define AVIVO_D2GRPH_FLIP_CONTROL 0x6948 + +#define AVIVO_D2CUR_CONTROL 0x6c00 +#define AVIVO_D2CUR_SURFACE_ADDRESS 0x6c08 +#define AVIVO_D2CUR_SIZE 0x6c10 +#define AVIVO_D2CUR_POSITION 0x6c14 + +#define AVIVO_D2MODE_VIEWPORT_START 0x6d80 +#define AVIVO_D2MODE_VIEWPORT_SIZE 0x6d84 +#define AVIVO_D2MODE_EXT_OVERSCAN_LEFT_RIGHT 0x6d88 +#define AVIVO_D2MODE_EXT_OVERSCAN_TOP_BOTTOM 0x6d8c + +#define AVIVO_D2SCL_SCALER_ENABLE 0x6d90 +#define AVIVO_D2SCL_SCALER_TAP_CONTROL 0x6d94 + +#define AVIVO_DDIA_BIT_DEPTH_CONTROL 0x7214 + +#define AVIVO_DACA_ENABLE 0x7800 +# define AVIVO_DAC_ENABLE (1 << 0) +#define AVIVO_DACA_SOURCE_SELECT 0x7804 +# define AVIVO_DAC_SOURCE_CRTC1 (0 << 0) +# define AVIVO_DAC_SOURCE_CRTC2 (1 << 0) +# define AVIVO_DAC_SOURCE_TV (2 << 0) + +#define AVIVO_DACA_FORCE_OUTPUT_CNTL 0x783c +# define AVIVO_DACA_FORCE_OUTPUT_CNTL_FORCE_DATA_EN (1 << 0) +# define AVIVO_DACA_FORCE_OUTPUT_CNTL_DATA_SEL_SHIFT (8) +# define AVIVO_DACA_FORCE_OUTPUT_CNTL_DATA_SEL_BLUE (1 << 0) +# define AVIVO_DACA_FORCE_OUTPUT_CNTL_DATA_SEL_GREEN (1 << 1) +# define AVIVO_DACA_FORCE_OUTPUT_CNTL_DATA_SEL_RED (1 << 2) +# define AVIVO_DACA_FORCE_OUTPUT_CNTL_DATA_ON_BLANKB_ONLY (1 << 24) +#define AVIVO_DACA_POWERDOWN 0x7850 +# define AVIVO_DACA_POWERDOWN_POWERDOWN (1 << 0) +# define AVIVO_DACA_POWERDOWN_BLUE (1 << 8) +# define AVIVO_DACA_POWERDOWN_GREEN (1 << 16) +# define AVIVO_DACA_POWERDOWN_RED (1 << 24) + +#define AVIVO_DACB_ENABLE 0x7a00 +#define AVIVO_DACB_SOURCE_SELECT 0x7a04 +#define AVIVO_DACB_FORCE_OUTPUT_CNTL 0x7a3c +# define AVIVO_DACB_FORCE_OUTPUT_CNTL_FORCE_DATA_EN (1 << 0) +# define AVIVO_DACB_FORCE_OUTPUT_CNTL_DATA_SEL_SHIFT (8) +# define AVIVO_DACB_FORCE_OUTPUT_CNTL_DATA_SEL_BLUE (1 << 0) +# define AVIVO_DACB_FORCE_OUTPUT_CNTL_DATA_SEL_GREEN (1 << 1) +# define AVIVO_DACB_FORCE_OUTPUT_CNTL_DATA_SEL_RED (1 << 2) +# define AVIVO_DACB_FORCE_OUTPUT_CNTL_DATA_ON_BLANKB_ONLY (1 << 24) +#define AVIVO_DACB_POWERDOWN 0x7a50 +# define AVIVO_DACB_POWERDOWN_POWERDOWN (1 << 0) +# define AVIVO_DACB_POWERDOWN_BLUE (1 << 8) +# define AVIVO_DACB_POWERDOWN_GREEN (1 << 16) +# define AVIVO_DACB_POWERDOWN_RED + +#define AVIVO_TMDSA_CNTL 0x7880 +# define AVIVO_TMDSA_CNTL_ENABLE (1 << 0) +# define AVIVO_TMDSA_CNTL_HPD_MASK (1 << 4) +# define AVIVO_TMDSA_CNTL_HPD_SELECT (1 << 8) +# define AVIVO_TMDSA_CNTL_SYNC_PHASE (1 << 12) +# define AVIVO_TMDSA_CNTL_PIXEL_ENCODING (1 << 16) +# define AVIVO_TMDSA_CNTL_DUAL_LINK_ENABLE (1 << 24) +# define AVIVO_TMDSA_CNTL_SWAP (1 << 28) +#define AVIVO_TMDSA_SOURCE_SELECT 0x7884 +/* 78a8 appears to be some kind of (reasonably tolerant) clock? + * 78d0 definitely hits the transmitter, definitely clock. */ +/* MYSTERY1 This appears to control dithering? */ +#define AVIVO_TMDSA_BIT_DEPTH_CONTROL 0x7894 +# define AVIVO_TMDS_BIT_DEPTH_CONTROL_TRUNCATE_EN (1 << 0) +# define AVIVO_TMDS_BIT_DEPTH_CONTROL_TRUNCATE_DEPTH (1 << 4) +# define AVIVO_TMDS_BIT_DEPTH_CONTROL_SPATIAL_DITHER_EN (1 << 8) +# define AVIVO_TMDS_BIT_DEPTH_CONTROL_SPATIAL_DITHER_DEPTH (1 << 12) +# define AVIVO_TMDS_BIT_DEPTH_CONTROL_TEMPORAL_DITHER_EN (1 << 16) +# define AVIVO_TMDS_BIT_DEPTH_CONTROL_TEMPORAL_DITHER_DEPTH (1 << 20) +# define AVIVO_TMDS_BIT_DEPTH_CONTROL_TEMPORAL_LEVEL (1 << 24) +# define AVIVO_TMDS_BIT_DEPTH_CONTROL_TEMPORAL_DITHER_RESET (1 << 26) +#define AVIVO_TMDSA_DCBALANCER_CONTROL 0x78d0 +# define AVIVO_TMDSA_DCBALANCER_CONTROL_EN (1 << 0) +# define AVIVO_TMDSA_DCBALANCER_CONTROL_TEST_EN (1 << 8) +# define AVIVO_TMDSA_DCBALANCER_CONTROL_TEST_IN_SHIFT (16) +# define AVIVO_TMDSA_DCBALANCER_CONTROL_FORCE (1 << 24) +#define AVIVO_TMDSA_DATA_SYNCHRONIZATION 0x78d8 +# define AVIVO_TMDSA_DATA_SYNCHRONIZATION_DSYNSEL (1 << 0) +# define AVIVO_TMDSA_DATA_SYNCHRONIZATION_PFREQCHG (1 << 8) +#define AVIVO_TMDSA_CLOCK_ENABLE 0x7900 +#define AVIVO_TMDSA_TRANSMITTER_ENABLE 0x7904 +# define AVIVO_TMDSA_TRANSMITTER_ENABLE_TX0_ENABLE (1 << 0) +# define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKC0EN (1 << 1) +# define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKD00EN (1 << 2) +# define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKD01EN (1 << 3) +# define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKD02EN (1 << 4) +# define AVIVO_TMDSA_TRANSMITTER_ENABLE_TX1_ENABLE (1 << 8) +# define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKD10EN (1 << 10) +# define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKD11EN (1 << 11) +# define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKD12EN (1 << 12) +# define AVIVO_TMDSA_TRANSMITTER_ENABLE_TX_ENABLE_HPD_MASK (1 << 16) +# define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKCEN_HPD_MASK (1 << 17) +# define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKDEN_HPD_MASK (1 << 18) + +#define AVIVO_TMDSA_TRANSMITTER_CONTROL 0x7910 +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_PLL_ENABLE (1 << 0) +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_PLL_RESET (1 << 1) +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_PLL_HPD_MASK_SHIFT (2) +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_IDSCKSEL (1 << 4) +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_BGSLEEP (1 << 5) +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_PLL_PWRUP_SEQ_EN (1 << 6) +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_TMCLK (1 << 8) +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_TMCLK_FROM_PADS (1 << 13) +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_TDCLK (1 << 14) +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_TDCLK_FROM_PADS (1 << 15) +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_CLK_PATTERN_SHIFT (16) +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_BYPASS_PLL (1 << 28) +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_USE_CLK_DATA (1 << 29) +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_INPUT_TEST_CLK_SEL (1 << 31) + +#define AVIVO_LVTMA_CNTL 0x7a80 +# define AVIVO_LVTMA_CNTL_ENABLE (1 << 0) +# define AVIVO_LVTMA_CNTL_HPD_MASK (1 << 4) +# define AVIVO_LVTMA_CNTL_HPD_SELECT (1 << 8) +# define AVIVO_LVTMA_CNTL_SYNC_PHASE (1 << 12) +# define AVIVO_LVTMA_CNTL_PIXEL_ENCODING (1 << 16) +# define AVIVO_LVTMA_CNTL_DUAL_LINK_ENABLE (1 << 24) +# define AVIVO_LVTMA_CNTL_SWAP (1 << 28) +#define AVIVO_LVTMA_SOURCE_SELECT 0x7a84 +#define AVIVO_LVTMA_COLOR_FORMAT 0x7a88 +#define AVIVO_LVTMA_BIT_DEPTH_CONTROL 0x7a94 +# define AVIVO_LVTMA_BIT_DEPTH_CONTROL_TRUNCATE_EN (1 << 0) +# define AVIVO_LVTMA_BIT_DEPTH_CONTROL_TRUNCATE_DEPTH (1 << 4) +# define AVIVO_LVTMA_BIT_DEPTH_CONTROL_SPATIAL_DITHER_EN (1 << 8) +# define AVIVO_LVTMA_BIT_DEPTH_CONTROL_SPATIAL_DITHER_DEPTH (1 << 12) +# define AVIVO_LVTMA_BIT_DEPTH_CONTROL_TEMPORAL_DITHER_EN (1 << 16) +# define AVIVO_LVTMA_BIT_DEPTH_CONTROL_TEMPORAL_DITHER_DEPTH (1 << 20) +# define AVIVO_LVTMA_BIT_DEPTH_CONTROL_TEMPORAL_LEVEL (1 << 24) +# define AVIVO_LVTMA_BIT_DEPTH_CONTROL_TEMPORAL_DITHER_RESET (1 << 26) + + + +#define AVIVO_LVTMA_DCBALANCER_CONTROL 0x7ad0 +# define AVIVO_LVTMA_DCBALANCER_CONTROL_EN (1 << 0) +# define AVIVO_LVTMA_DCBALANCER_CONTROL_TEST_EN (1 << 8) +# define AVIVO_LVTMA_DCBALANCER_CONTROL_TEST_IN_SHIFT (16) +# define AVIVO_LVTMA_DCBALANCER_CONTROL_FORCE (1 << 24) + +#define AVIVO_LVTMA_DATA_SYNCHRONIZATION 0x78d8 +# define AVIVO_LVTMA_DATA_SYNCHRONIZATION_DSYNSEL (1 << 0) +# define AVIVO_LVTMA_DATA_SYNCHRONIZATION_PFREQCHG (1 << 8) +#define R500_LVTMA_CLOCK_ENABLE 0x7b00 +#define R600_LVTMA_CLOCK_ENABLE 0x7b04 + +#define R500_LVTMA_TRANSMITTER_ENABLE 0x7b04 +#define R600_LVTMA_TRANSMITTER_ENABLE 0x7b08 +# define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKC0EN (1 << 1) +# define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKD00EN (1 << 2) +# define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKD01EN (1 << 3) +# define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKD02EN (1 << 4) +# define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKD03EN (1 << 5) +# define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKC1EN (1 << 9) +# define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKD10EN (1 << 10) +# define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKD11EN (1 << 11) +# define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKD12EN (1 << 12) +# define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKCEN_HPD_MASK (1 << 17) +# define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKDEN_HPD_MASK (1 << 18) + +#define R500_LVTMA_TRANSMITTER_CONTROL 0x7b10 +#define R600_LVTMA_TRANSMITTER_CONTROL 0x7b14 +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_PLL_ENABLE (1 << 0) +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_PLL_RESET (1 << 1) +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_PLL_HPD_MASK_SHIFT (2) +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_IDSCKSEL (1 << 4) +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_BGSLEEP (1 << 5) +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_PLL_PWRUP_SEQ_EN (1 << 6) +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_TMCLK (1 << 8) +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_TMCLK_FROM_PADS (1 << 13) +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_TDCLK (1 << 14) +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_TDCLK_FROM_PADS (1 << 15) +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_CLK_PATTERN_SHIFT (16) +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_BYPASS_PLL (1 << 28) +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_USE_CLK_DATA (1 << 29) +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_INPUT_TEST_CLK_SEL (1 << 31) + +#define R500_LVTMA_PWRSEQ_CNTL 0x7af0 +#define R600_LVTMA_PWRSEQ_CNTL 0x7af4 +# define AVIVO_LVTMA_PWRSEQ_EN (1 << 0) +# define AVIVO_LVTMA_PWRSEQ_PLL_ENABLE_MASK (1 << 2) +# define AVIVO_LVTMA_PWRSEQ_PLL_RESET_MASK (1 << 3) +# define AVIVO_LVTMA_PWRSEQ_TARGET_STATE (1 << 4) +# define AVIVO_LVTMA_SYNCEN (1 << 8) +# define AVIVO_LVTMA_SYNCEN_OVRD (1 << 9) +# define AVIVO_LVTMA_SYNCEN_POL (1 << 10) +# define AVIVO_LVTMA_DIGON (1 << 16) +# define AVIVO_LVTMA_DIGON_OVRD (1 << 17) +# define AVIVO_LVTMA_DIGON_POL (1 << 18) +# define AVIVO_LVTMA_BLON (1 << 24) +# define AVIVO_LVTMA_BLON_OVRD (1 << 25) +# define AVIVO_LVTMA_BLON_POL (1 << 26) + +#define R500_LVTMA_PWRSEQ_STATE 0x7af4 +#define R600_LVTMA_PWRSEQ_STATE 0x7af8 +# define AVIVO_LVTMA_PWRSEQ_STATE_TARGET_STATE_R (1 << 0) +# define AVIVO_LVTMA_PWRSEQ_STATE_DIGON (1 << 1) +# define AVIVO_LVTMA_PWRSEQ_STATE_SYNCEN (1 << 2) +# define AVIVO_LVTMA_PWRSEQ_STATE_BLON (1 << 3) +# define AVIVO_LVTMA_PWRSEQ_STATE_DONE (1 << 4) +# define AVIVO_LVTMA_PWRSEQ_STATE_STATUS_SHIFT (8) + +#define AVIVO_LVDS_BACKLIGHT_CNTL 0x7af8 +# define AVIVO_LVDS_BACKLIGHT_CNTL_EN (1 << 0) +# define AVIVO_LVDS_BACKLIGHT_LEVEL_MASK 0x0000ff00 +# define AVIVO_LVDS_BACKLIGHT_LEVEL_SHIFT 8 + +#define AVIVO_DVOA_BIT_DEPTH_CONTROL 0x7988 + +#define AVIVO_GPIO_0 0x7e30 +#define AVIVO_GPIO_1 0x7e40 +#define AVIVO_GPIO_2 0x7e50 +#define AVIVO_GPIO_3 0x7e60 + +#define AVIVO_DC_GPIO_HPD_Y 0x7e9c + +#define AVIVO_I2C_STATUS 0x7d30 +# define AVIVO_I2C_STATUS_DONE (1 << 0) +# define AVIVO_I2C_STATUS_NACK (1 << 1) +# define AVIVO_I2C_STATUS_HALT (1 << 2) +# define AVIVO_I2C_STATUS_GO (1 << 3) +# define AVIVO_I2C_STATUS_MASK 0x7 +/* If radeon_mm_i2c is to be believed, this is HALT, NACK, and maybe + * DONE? */ +# define AVIVO_I2C_STATUS_CMD_RESET 0x7 +# define AVIVO_I2C_STATUS_CMD_WAIT (1 << 3) +#define AVIVO_I2C_STOP 0x7d34 +#define AVIVO_I2C_START_CNTL 0x7d38 +# define AVIVO_I2C_START (1 << 8) +# define AVIVO_I2C_CONNECTOR0 (0 << 16) +# define AVIVO_I2C_CONNECTOR1 (1 << 16) +#define R520_I2C_START (1<<0) +#define R520_I2C_STOP (1<<1) +#define R520_I2C_RX (1<<2) +#define R520_I2C_EN (1<<8) +#define R520_I2C_DDC1 (0<<16) +#define R520_I2C_DDC2 (1<<16) +#define R520_I2C_DDC3 (2<<16) +#define R520_I2C_DDC_MASK (3<<16) +#define AVIVO_I2C_CONTROL2 0x7d3c +# define AVIVO_I2C_7D3C_SIZE_SHIFT 8 +# define AVIVO_I2C_7D3C_SIZE_MASK (0xf << 8) +#define AVIVO_I2C_CONTROL3 0x7d40 +/* Reading is done 4 bytes at a time: read the bottom 8 bits from + * 7d44, four times in a row. + * Writing is a little more complex. First write DATA with + * 0xnnnnnnzz, then 0xnnnnnnyy, where nnnnnn is some non-deterministic + * magic number, zz is, I think, the slave address, and yy is the byte + * you want to write. */ +#define AVIVO_I2C_DATA 0x7d44 +#define R520_I2C_ADDR_COUNT_MASK (0x7) +#define R520_I2C_DATA_COUNT_SHIFT (8) +#define R520_I2C_DATA_COUNT_MASK (0xF00) +#define AVIVO_I2C_CNTL 0x7d50 +# define AVIVO_I2C_EN (1 << 0) +# define AVIVO_I2C_RESET (1 << 8) + +#define R600_GENERAL_PWRMGT 0x618 +# define R600_OPEN_DRAIN_PADS (1 << 11) + +#define R600_LOWER_GPIO_ENABLE 0x710 +#define R600_CTXSW_VID_LOWER_GPIO_CNTL 0x718 +#define R600_HIGH_VID_LOWER_GPIO_CNTL 0x71c +#define R600_MEDIUM_VID_LOWER_GPIO_CNTL 0x720 +#define R600_LOW_VID_LOWER_GPIO_CNTL 0x724 + +#define R600_MC_VM_FB_LOCATION 0x2180 +#define R600_MC_VM_AGP_TOP 0x2184 +#define R600_MC_VM_AGP_BOT 0x2188 +#define R600_MC_VM_AGP_BASE 0x218c +#define R600_MC_VM_SYSTEM_APERTURE_LOW_ADDR 0x2190 +#define R600_MC_VM_SYSTEM_APERTURE_HIGH_ADDR 0x2194 +#define R600_MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR 0x2198 + +#define R700_MC_VM_FB_LOCATION 0x2024 + +#define R600_HDP_NONSURFACE_BASE 0x2c04 + +#define R600_BUS_CNTL 0x5420 +#define R600_CONFIG_CNTL 0x5424 +#define R600_CONFIG_MEMSIZE 0x5428 +#define R600_CONFIG_F0_BASE 0x542C +#define R600_CONFIG_APER_SIZE 0x5430 + +#define R600_ROM_CNTL 0x1600 +# define R600_SCK_OVERWRITE (1 << 1) +# define R600_SCK_PRESCALE_CRYSTAL_CLK_SHIFT 28 +# define R600_SCK_PRESCALE_CRYSTAL_CLK_MASK (0xf << 28) + +#define R600_BIOS_0_SCRATCH 0x1724 +#define R600_BIOS_1_SCRATCH 0x1728 +#define R600_BIOS_2_SCRATCH 0x172c +#define R600_BIOS_3_SCRATCH 0x1730 +#define R600_BIOS_4_SCRATCH 0x1734 +#define R600_BIOS_5_SCRATCH 0x1738 +#define R600_BIOS_6_SCRATCH 0x173c +#define R600_BIOS_7_SCRATCH 0x1740 + +#define R300_GB_TILE_CONFIG 0x4018 +# define R300_ENABLE_TILING (1 << 0) +# define R300_PIPE_COUNT_RV350 (0 << 1) +# define R300_PIPE_COUNT_R300 (3 << 1) +# define R300_PIPE_COUNT_R420_3P (6 << 1) +# define R300_PIPE_COUNT_R420 (7 << 1) +# define R300_TILE_SIZE_8 (0 << 4) +# define R300_TILE_SIZE_16 (1 << 4) +# define R300_TILE_SIZE_32 (2 << 4) +# define R300_SUBPIXEL_1_12 (0 << 16) +# define R300_SUBPIXEL_1_16 (1 << 16) +#define R300_GB_ENABLE 0x4008 +#define R300_GB_AA_CONFIG 0x4020 +#define R400_GB_PIPE_SELECT 0x402c +#define R300_GB_MSPOS0 0x4010 +# define R300_MS_X0_SHIFT 0 +# define R300_MS_Y0_SHIFT 4 +# define R300_MS_X1_SHIFT 8 +# define R300_MS_Y1_SHIFT 12 +# define R300_MS_X2_SHIFT 16 +# define R300_MS_Y2_SHIFT 20 +# define R300_MSBD0_Y_SHIFT 24 +# define R300_MSBD0_X_SHIFT 28 +#define R300_GB_MSPOS1 0x4014 +# define R300_MS_X3_SHIFT 0 +# define R300_MS_Y3_SHIFT 4 +# define R300_MS_X4_SHIFT 8 +# define R300_MS_Y4_SHIFT 12 +# define R300_MS_X5_SHIFT 16 +# define R300_MS_Y5_SHIFT 20 +# define R300_MSBD1_SHIFT 24 + +#define R300_GA_ENHANCE 0x4274 +# define R300_GA_DEADLOCK_CNTL (1 << 0) +# define R300_GA_FASTSYNC_CNTL (1 << 1) + +#define R300_GA_POLY_MODE 0x4288 +# define R300_FRONT_PTYPE_POINT (0 << 4) +# define R300_FRONT_PTYPE_LINE (1 << 4) +# define R300_FRONT_PTYPE_TRIANGE (2 << 4) +# define R300_BACK_PTYPE_POINT (0 << 7) +# define R300_BACK_PTYPE_LINE (1 << 7) +# define R300_BACK_PTYPE_TRIANGE (2 << 7) +#define R300_GA_ROUND_MODE 0x428c +# define R300_GEOMETRY_ROUND_TRUNC (0 << 0) +# define R300_GEOMETRY_ROUND_NEAREST (1 << 0) +# define R300_COLOR_ROUND_TRUNC (0 << 2) +# define R300_COLOR_ROUND_NEAREST (1 << 2) +#define R300_GA_COLOR_CONTROL 0x4278 +# define R300_RGB0_SHADING_SOLID (0 << 0) +# define R300_RGB0_SHADING_FLAT (1 << 0) +# define R300_RGB0_SHADING_GOURAUD (2 << 0) +# define R300_ALPHA0_SHADING_SOLID (0 << 2) +# define R300_ALPHA0_SHADING_FLAT (1 << 2) +# define R300_ALPHA0_SHADING_GOURAUD (2 << 2) +# define R300_RGB1_SHADING_SOLID (0 << 4) +# define R300_RGB1_SHADING_FLAT (1 << 4) +# define R300_RGB1_SHADING_GOURAUD (2 << 4) +# define R300_ALPHA1_SHADING_SOLID (0 << 6) +# define R300_ALPHA1_SHADING_FLAT (1 << 6) +# define R300_ALPHA1_SHADING_GOURAUD (2 << 6) +# define R300_RGB2_SHADING_SOLID (0 << 8) +# define R300_RGB2_SHADING_FLAT (1 << 8) +# define R300_RGB2_SHADING_GOURAUD (2 << 8) +# define R300_ALPHA2_SHADING_SOLID (0 << 10) +# define R300_ALPHA2_SHADING_FLAT (1 << 10) +# define R300_ALPHA2_SHADING_GOURAUD (2 << 10) +# define R300_RGB3_SHADING_SOLID (0 << 12) +# define R300_RGB3_SHADING_FLAT (1 << 12) +# define R300_RGB3_SHADING_GOURAUD (2 << 12) +# define R300_ALPHA3_SHADING_SOLID (0 << 14) +# define R300_ALPHA3_SHADING_FLAT (1 << 14) +# define R300_ALPHA3_SHADING_GOURAUD (2 << 14) +#define R300_GA_OFFSET 0x4290 + +#define R500_SU_REG_DEST 0x42c8 + +#define R300_VAP_CNTL_STATUS 0x2140 +# define R300_PVS_BYPASS (1 << 8) +#define R300_VAP_PVS_STATE_FLUSH_REG 0x2284 +#define R300_VAP_CNTL 0x2080 +# define R300_PVS_NUM_SLOTS_SHIFT 0 +# define R300_PVS_NUM_CNTLRS_SHIFT 4 +# define R300_PVS_NUM_FPUS_SHIFT 8 +# define R300_VF_MAX_VTX_NUM_SHIFT 18 +# define R300_GL_CLIP_SPACE_DEF (0 << 22) +# define R300_DX_CLIP_SPACE_DEF (1 << 22) +# define R500_TCL_STATE_OPTIMIZATION (1 << 23) +#define R300_VAP_PSC_SGN_NORM_CNTL 0x21DC +#define R300_VAP_PROG_STREAM_CNTL_0 0x2150 +# define R300_DATA_TYPE_0_SHIFT 0 +# define R300_DATA_TYPE_FLOAT_1 0 +# define R300_DATA_TYPE_FLOAT_2 1 +# define R300_DATA_TYPE_FLOAT_3 2 +# define R300_DATA_TYPE_FLOAT_4 3 +# define R300_DATA_TYPE_BYTE 4 +# define R300_DATA_TYPE_D3DCOLOR 5 +# define R300_DATA_TYPE_SHORT_2 6 +# define R300_DATA_TYPE_SHORT_4 7 +# define R300_DATA_TYPE_VECTOR_3_TTT 8 +# define R300_DATA_TYPE_VECTOR_3_EET 9 +# define R300_SKIP_DWORDS_0_SHIFT 4 +# define R300_DST_VEC_LOC_0_SHIFT 8 +# define R300_LAST_VEC_0 (1 << 13) +# define R300_SIGNED_0 (1 << 14) +# define R300_NORMALIZE_0 (1 << 15) +# define R300_DATA_TYPE_1_SHIFT 16 +# define R300_SKIP_DWORDS_1_SHIFT 20 +# define R300_DST_VEC_LOC_1_SHIFT 24 +# define R300_LAST_VEC_1 (1 << 29) +# define R300_SIGNED_1 (1 << 30) +# define R300_NORMALIZE_1 (1 << 31) +#define R300_VAP_PROG_STREAM_CNTL_1 0x2154 +# define R300_DATA_TYPE_2_SHIFT 0 +# define R300_SKIP_DWORDS_2_SHIFT 4 +# define R300_DST_VEC_LOC_2_SHIFT 8 +# define R300_LAST_VEC_2 (1 << 13) +# define R300_SIGNED_2 (1 << 14) +# define R300_NORMALIZE_2 (1 << 15) +# define R300_DATA_TYPE_3_SHIFT 16 +# define R300_SKIP_DWORDS_3_SHIFT 20 +# define R300_DST_VEC_LOC_3_SHIFT 24 +# define R300_LAST_VEC_3 (1 << 29) +# define R300_SIGNED_3 (1 << 30) +# define R300_NORMALIZE_3 (1 << 31) +#define R300_VAP_PROG_STREAM_CNTL_EXT_0 0x21e0 +# define R300_SWIZZLE_SELECT_X_0_SHIFT 0 +# define R300_SWIZZLE_SELECT_Y_0_SHIFT 3 +# define R300_SWIZZLE_SELECT_Z_0_SHIFT 6 +# define R300_SWIZZLE_SELECT_W_0_SHIFT 9 +# define R300_SWIZZLE_SELECT_X 0 +# define R300_SWIZZLE_SELECT_Y 1 +# define R300_SWIZZLE_SELECT_Z 2 +# define R300_SWIZZLE_SELECT_W 3 +# define R300_SWIZZLE_SELECT_FP_ZERO 4 +# define R300_SWIZZLE_SELECT_FP_ONE 5 +# define R300_WRITE_ENA_0_SHIFT 12 +# define R300_WRITE_ENA_X 1 +# define R300_WRITE_ENA_Y 2 +# define R300_WRITE_ENA_Z 4 +# define R300_WRITE_ENA_W 8 +# define R300_SWIZZLE_SELECT_X_1_SHIFT 16 +# define R300_SWIZZLE_SELECT_Y_1_SHIFT 19 +# define R300_SWIZZLE_SELECT_Z_1_SHIFT 22 +# define R300_SWIZZLE_SELECT_W_1_SHIFT 25 +# define R300_WRITE_ENA_1_SHIFT 28 +#define R300_VAP_PROG_STREAM_CNTL_EXT_1 0x21e4 +# define R300_SWIZZLE_SELECT_X_2_SHIFT 0 +# define R300_SWIZZLE_SELECT_Y_2_SHIFT 3 +# define R300_SWIZZLE_SELECT_Z_2_SHIFT 6 +# define R300_SWIZZLE_SELECT_W_2_SHIFT 9 +# define R300_WRITE_ENA_2_SHIFT 12 +# define R300_SWIZZLE_SELECT_X_3_SHIFT 16 +# define R300_SWIZZLE_SELECT_Y_3_SHIFT 19 +# define R300_SWIZZLE_SELECT_Z_3_SHIFT 22 +# define R300_SWIZZLE_SELECT_W_3_SHIFT 25 +# define R300_WRITE_ENA_3_SHIFT 28 +#define R300_VAP_PVS_CODE_CNTL_0 0x22D0 +# define R300_PVS_FIRST_INST_SHIFT 0 +# define R300_PVS_XYZW_VALID_INST_SHIFT 10 +# define R300_PVS_LAST_INST_SHIFT 20 +#define R300_VAP_PVS_CODE_CNTL_1 0x22D8 +# define R300_PVS_LAST_VTX_SRC_INST_SHIFT 0 +#define R300_VAP_PVS_VECTOR_INDX_REG 0x2200 +#define R300_VAP_PVS_VECTOR_DATA_REG 0x2204 +/* PVS instructions */ +/* Opcode and dst instruction */ +#define R300_PVS_DST_OPCODE(x) (x << 0) +/* Vector ops */ +# define R300_VECTOR_NO_OP 0 +# define R300_VE_DOT_PRODUCT 1 +# define R300_VE_MULTIPLY 2 +# define R300_VE_ADD 3 +# define R300_VE_MULTIPLY_ADD 4 +# define R300_VE_DISTANCE_VECTOR 5 +# define R300_VE_FRACTION 6 +# define R300_VE_MAXIMUM 7 +# define R300_VE_MINIMUM 8 +# define R300_VE_SET_GREATER_THAN_EQUAL 9 +# define R300_VE_SET_LESS_THAN 10 +# define R300_VE_MULTIPLYX2_ADD 11 +# define R300_VE_MULTIPLY_CLAMP 12 +# define R300_VE_FLT2FIX_DX 13 +# define R300_VE_FLT2FIX_DX_RND 14 +/* R500 additions */ +# define R500_VE_PRED_SET_EQ_PUSH 15 +# define R500_VE_PRED_SET_GT_PUSH 16 +# define R500_VE_PRED_SET_GTE_PUSH 17 +# define R500_VE_PRED_SET_NEQ_PUSH 18 +# define R500_VE_COND_WRITE_EQ 19 +# define R500_VE_COND_WRITE_GT 20 +# define R500_VE_COND_WRITE_GTE 21 +# define R500_VE_COND_WRITE_NEQ 22 +# define R500_VE_COND_MUX_EQ 23 +# define R500_VE_COND_MUX_GT 24 +# define R500_VE_COND_MUX_GTE 25 +# define R500_VE_SET_GREATER_THAN 26 +# define R500_VE_SET_EQUAL 27 +# define R500_VE_SET_NOT_EQUAL 28 +/* Math ops */ +# define R300_MATH_NO_OP 0 +# define R300_ME_EXP_BASE2_DX 1 +# define R300_ME_LOG_BASE2_DX 2 +# define R300_ME_EXP_BASEE_FF 3 +# define R300_ME_LIGHT_COEFF_DX 4 +# define R300_ME_POWER_FUNC_FF 5 +# define R300_ME_RECIP_DX 6 +# define R300_ME_RECIP_FF 7 +# define R300_ME_RECIP_SQRT_DX 8 +# define R300_ME_RECIP_SQRT_FF 9 +# define R300_ME_MULTIPLY 10 +# define R300_ME_EXP_BASE2_FULL_DX 11 +# define R300_ME_LOG_BASE2_FULL_DX 12 +# define R300_ME_POWER_FUNC_FF_CLAMP_B 13 +# define R300_ME_POWER_FUNC_FF_CLAMP_B1 14 +# define R300_ME_POWER_FUNC_FF_CLAMP_01 15 +# define R300_ME_SIN 16 +# define R300_ME_COS 17 +/* R500 additions */ +# define R500_ME_LOG_BASE2_IEEE 18 +# define R500_ME_RECIP_IEEE 19 +# define R500_ME_RECIP_SQRT_IEEE 20 +# define R500_ME_PRED_SET_EQ 21 +# define R500_ME_PRED_SET_GT 22 +# define R500_ME_PRED_SET_GTE 23 +# define R500_ME_PRED_SET_NEQ 24 +# define R500_ME_PRED_SET_CLR 25 +# define R500_ME_PRED_SET_INV 26 +# define R500_ME_PRED_SET_POP 27 +# define R500_ME_PRED_SET_RESTORE 28 +/* macro */ +# define R300_PVS_MACRO_OP_2CLK_MADD 0 +# define R300_PVS_MACRO_OP_2CLK_M2X_ADD 1 +#define R300_PVS_DST_MATH_INST (1 << 6) +#define R300_PVS_DST_MACRO_INST (1 << 7) +#define R300_PVS_DST_REG_TYPE(x) (x << 8) +# define R300_PVS_DST_REG_TEMPORARY 0 +# define R300_PVS_DST_REG_A0 1 +# define R300_PVS_DST_REG_OUT 2 +# define R500_PVS_DST_REG_OUT_REPL_X 3 +# define R300_PVS_DST_REG_ALT_TEMPORARY 4 +# define R300_PVS_DST_REG_INPUT 5 +#define R300_PVS_DST_ADDR_MODE_1 (1 << 12) +#define R300_PVS_DST_OFFSET(x) (x << 13) +#define R300_PVS_DST_WE_X (1 << 20) +#define R300_PVS_DST_WE_Y (1 << 21) +#define R300_PVS_DST_WE_Z (1 << 22) +#define R300_PVS_DST_WE_W (1 << 23) +#define R300_PVS_DST_VE_SAT (1 << 24) +#define R300_PVS_DST_ME_SAT (1 << 25) +#define R300_PVS_DST_PRED_ENABLE (1 << 26) +#define R300_PVS_DST_PRED_SENSE (1 << 27) +#define R300_PVS_DST_DUAL_MATH_OP (1 << 28) +#define R300_PVS_DST_ADDR_SEL(x) (x << 29) +#define R300_PVS_DST_ADDR_MODE_0 (1 << 31) +/* src operand instruction */ +#define R300_PVS_SRC_REG_TYPE(x) (x << 0) +# define R300_PVS_SRC_REG_TEMPORARY 0 +# define R300_PVS_SRC_REG_INPUT 1 +# define R300_PVS_SRC_REG_CONSTANT 2 +# define R300_PVS_SRC_REG_ALT_TEMPORARY 3 +#define R300_SPARE_0 (1 << 2) +#define R300_PVS_SRC_ABS_XYZW (1 << 3) +#define R300_PVS_SRC_ADDR_MODE_0 (1 << 4) +#define R300_PVS_SRC_OFFSET(x) (x << 5) +#define R300_PVS_SRC_SWIZZLE_X(x) (x << 13) +#define R300_PVS_SRC_SWIZZLE_Y(x) (x << 16) +#define R300_PVS_SRC_SWIZZLE_Z(x) (x << 19) +#define R300_PVS_SRC_SWIZZLE_W(x) (x << 22) +# define R300_PVS_SRC_SELECT_X 0 +# define R300_PVS_SRC_SELECT_Y 1 +# define R300_PVS_SRC_SELECT_Z 2 +# define R300_PVS_SRC_SELECT_W 3 +# define R300_PVS_SRC_SELECT_FORCE_0 4 +# define R300_PVS_SRC_SELECT_FORCE_1 5 +#define R300_PVS_SRC_NEG_X (1 << 25) +#define R300_PVS_SRC_NEG_Y (1 << 26) +#define R300_PVS_SRC_NEG_Z (1 << 27) +#define R300_PVS_SRC_NEG_W (1 << 28) +#define R300_PVS_SRC_ADDR_SEL(x) (x << 29) +#define R300_PVS_SRC_ADDR_MODE_1 (1 << 31) + +#define R300_VAP_PVS_FLOW_CNTL_OPC 0x22DC +#define R300_VAP_OUT_VTX_FMT_0 0x2090 +# define R300_VTX_POS_PRESENT (1 << 0) +# define R300_VTX_COLOR_0_PRESENT (1 << 1) +# define R300_VTX_COLOR_1_PRESENT (1 << 2) +# define R300_VTX_COLOR_2_PRESENT (1 << 3) +# define R300_VTX_COLOR_3_PRESENT (1 << 4) +# define R300_VTX_PT_SIZE_PRESENT (1 << 16) +#define R300_VAP_OUT_VTX_FMT_1 0x2094 +# define R300_TEX_0_COMP_CNT_SHIFT 0 +# define R300_TEX_1_COMP_CNT_SHIFT 3 +# define R300_TEX_2_COMP_CNT_SHIFT 6 +# define R300_TEX_3_COMP_CNT_SHIFT 9 +# define R300_TEX_4_COMP_CNT_SHIFT 12 +# define R300_TEX_5_COMP_CNT_SHIFT 15 +# define R300_TEX_6_COMP_CNT_SHIFT 18 +# define R300_TEX_7_COMP_CNT_SHIFT 21 +#define R300_VAP_VTX_SIZE 0x20b4 +#define R300_VAP_GB_VERT_CLIP_ADJ 0x2220 +#define R300_VAP_GB_VERT_DISC_ADJ 0x2224 +#define R300_VAP_GB_HORZ_CLIP_ADJ 0x2228 +#define R300_VAP_GB_HORZ_DISC_ADJ 0x222c +#define R300_VAP_CLIP_CNTL 0x221c +# define R300_UCP_ENA_0 (1 << 0) +# define R300_UCP_ENA_1 (1 << 1) +# define R300_UCP_ENA_2 (1 << 2) +# define R300_UCP_ENA_3 (1 << 3) +# define R300_UCP_ENA_4 (1 << 4) +# define R300_UCP_ENA_5 (1 << 5) +# define R300_PS_UCP_MODE_SHIFT 14 +# define R300_CLIP_DISABLE (1 << 16) +# define R300_UCP_CULL_ONLY_ENA (1 << 17) +# define R300_BOUNDARY_EDGE_FLAG_ENA (1 << 18) +#define R300_VAP_PVS_STATE_FLUSH_REG 0x2284 + +#define R500_VAP_INDEX_OFFSET 0x208c + +#define R300_SU_TEX_WRAP 0x42a0 +#define R300_SU_POLY_OFFSET_ENABLE 0x42b4 +#define R300_SU_CULL_MODE 0x42b8 +# define R300_CULL_FRONT (1 << 0) +# define R300_CULL_BACK (1 << 1) +# define R300_FACE_POS (0 << 2) +# define R300_FACE_NEG (1 << 2) +#define R300_SU_DEPTH_SCALE 0x42c0 +#define R300_SU_DEPTH_OFFSET 0x42c4 + +#define R300_RS_COUNT 0x4300 +# define R300_RS_COUNT_IT_COUNT_SHIFT 0 +# define R300_RS_COUNT_IC_COUNT_SHIFT 7 +# define R300_RS_COUNT_HIRES_EN (1 << 18) + +#define R300_RS_IP_0 0x4310 +#define R300_RS_IP_1 0x4314 +# define R300_RS_TEX_PTR(x) (x << 0) +# define R300_RS_COL_PTR(x) (x << 6) +# define R300_RS_COL_FMT(x) (x << 9) +# define R300_RS_COL_FMT_RGBA 0 +# define R300_RS_COL_FMT_RGB0 2 +# define R300_RS_COL_FMT_RGB1 3 +# define R300_RS_COL_FMT_000A 4 +# define R300_RS_COL_FMT_0000 5 +# define R300_RS_COL_FMT_0001 6 +# define R300_RS_COL_FMT_111A 8 +# define R300_RS_COL_FMT_1110 9 +# define R300_RS_COL_FMT_1111 10 +# define R300_RS_SEL_S(x) (x << 13) +# define R300_RS_SEL_T(x) (x << 16) +# define R300_RS_SEL_R(x) (x << 19) +# define R300_RS_SEL_Q(x) (x << 22) +# define R300_RS_SEL_C0 0 +# define R300_RS_SEL_C1 1 +# define R300_RS_SEL_C2 2 +# define R300_RS_SEL_C3 3 +# define R300_RS_SEL_K0 4 +# define R300_RS_SEL_K1 5 +#define R300_RS_INST_COUNT 0x4304 +# define R300_INST_COUNT_RS(x) (x << 0) +# define R300_RS_W_EN (1 << 4) +# define R300_TX_OFFSET_RS(x) (x << 5) +#define R300_RS_INST_0 0x4330 +#define R300_RS_INST_1 0x4334 +# define R300_INST_TEX_ID(x) (x << 0) +# define R300_RS_INST_TEX_CN_WRITE (1 << 3) +# define R300_INST_TEX_ADDR(x) (x << 6) + +#define R300_TX_INVALTAGS 0x4100 +#define R300_TX_FILTER0_0 0x4400 +# define R300_TX_CLAMP_S(x) (x << 0) +# define R300_TX_CLAMP_T(x) (x << 3) +# define R300_TX_CLAMP_R(x) (x << 6) +# define R300_TX_CLAMP_WRAP 0 +# define R300_TX_CLAMP_MIRROR 1 +# define R300_TX_CLAMP_CLAMP_LAST 2 +# define R300_TX_CLAMP_MIRROR_CLAMP_LAST 3 +# define R300_TX_CLAMP_CLAMP_BORDER 4 +# define R300_TX_CLAMP_MIRROR_CLAMP_BORDER 5 +# define R300_TX_CLAMP_CLAMP_GL 6 +# define R300_TX_CLAMP_MIRROR_CLAMP_GL 7 +# define R300_TX_MAG_FILTER_NEAREST (1 << 9) +# define R300_TX_MIN_FILTER_NEAREST (1 << 11) +# define R300_TX_MAG_FILTER_LINEAR (2 << 9) +# define R300_TX_MIN_FILTER_LINEAR (2 << 11) +# define R300_TX_ID_SHIFT 28 +#define R300_TX_FILTER1_0 0x4440 +#define R300_TX_FORMAT0_0 0x4480 +# define R300_TXWIDTH_SHIFT 0 +# define R300_TXHEIGHT_SHIFT 11 +# define R300_NUM_LEVELS_SHIFT 26 +# define R300_NUM_LEVELS_MASK 0x +# define R300_TXPROJECTED (1 << 30) +# define R300_TXPITCH_EN (1 << 31) +#define R300_TX_FORMAT1_0 0x44c0 +# define R300_TX_FORMAT_X8 0x0 +# define R300_TX_FORMAT_X16 0x1 +# define R300_TX_FORMAT_Y4X4 0x2 +# define R300_TX_FORMAT_Y8X8 0x3 +# define R300_TX_FORMAT_Y16X16 0x4 +# define R300_TX_FORMAT_Z3Y3X2 0x5 +# define R300_TX_FORMAT_Z5Y6X5 0x6 +# define R300_TX_FORMAT_Z6Y5X5 0x7 +# define R300_TX_FORMAT_Z11Y11X10 0x8 +# define R300_TX_FORMAT_Z10Y11X11 0x9 +# define R300_TX_FORMAT_W4Z4Y4X4 0xA +# define R300_TX_FORMAT_W1Z5Y5X5 0xB +# define R300_TX_FORMAT_W8Z8Y8X8 0xC +# define R300_TX_FORMAT_W2Z10Y10X10 0xD +# define R300_TX_FORMAT_W16Z16Y16X16 0xE +# define R300_TX_FORMAT_DXT1 0xF +# define R300_TX_FORMAT_DXT3 0x10 +# define R300_TX_FORMAT_DXT5 0x11 +# define R300_TX_FORMAT_D3DMFT_CxV8U8 0x12 /* no swizzle */ +# define R300_TX_FORMAT_A8R8G8B8 0x13 /* no swizzle */ +# define R300_TX_FORMAT_B8G8_B8G8 0x14 /* no swizzle */ +# define R300_TX_FORMAT_G8R8_G8B8 0x15 /* no swizzle */ +# define R300_TX_FORMAT_VYUY422 0x14 /* no swizzle */ +# define R300_TX_FORMAT_YVYU422 0x15 /* no swizzle */ +# define R300_TX_FORMAT_X24_Y8 0x1e +# define R300_TX_FORMAT_X32 0x1e + /* Floating point formats */ + /* Note - hardware supports both 16 and 32 bit floating point */ +# define R300_TX_FORMAT_FL_I16 0x18 +# define R300_TX_FORMAT_FL_I16A16 0x19 +# define R300_TX_FORMAT_FL_R16G16B16A16 0x1A +# define R300_TX_FORMAT_FL_I32 0x1B +# define R300_TX_FORMAT_FL_I32A32 0x1C +# define R300_TX_FORMAT_FL_R32G32B32A32 0x1D + /* alpha modes, convenience mostly */ + /* if you have alpha, pick constant appropriate to the + number of channels (1 for I8, 2 for I8A8, 4 for R8G8B8A8, etc */ +# define R300_TX_FORMAT_ALPHA_1CH 0x000 +# define R300_TX_FORMAT_ALPHA_2CH 0x200 +# define R300_TX_FORMAT_ALPHA_4CH 0x600 +# define R300_TX_FORMAT_ALPHA_NONE 0xA00 + /* Swizzling */ + /* constants */ +# define R300_TX_FORMAT_X 0 +# define R300_TX_FORMAT_Y 1 +# define R300_TX_FORMAT_Z 2 +# define R300_TX_FORMAT_W 3 +# define R300_TX_FORMAT_ZERO 4 +# define R300_TX_FORMAT_ONE 5 + /* 2.0*Z, everything above 1.0 is set to 0.0 */ +# define R300_TX_FORMAT_CUT_Z 6 + /* 2.0*W, everything above 1.0 is set to 0.0 */ +# define R300_TX_FORMAT_CUT_W 7 + +# define R300_TX_FORMAT_B_SHIFT 18 +# define R300_TX_FORMAT_G_SHIFT 15 +# define R300_TX_FORMAT_R_SHIFT 12 +# define R300_TX_FORMAT_A_SHIFT 9 + + /* Convenience macro to take care of layout and swizzling */ +# define R300_EASY_TX_FORMAT(B, G, R, A, FMT) ( \ + ((R300_TX_FORMAT_##B)<<R300_TX_FORMAT_B_SHIFT) \ + | ((R300_TX_FORMAT_##G)<<R300_TX_FORMAT_G_SHIFT) \ + | ((R300_TX_FORMAT_##R)<<R300_TX_FORMAT_R_SHIFT) \ + | ((R300_TX_FORMAT_##A)<<R300_TX_FORMAT_A_SHIFT) \ + | (R300_TX_FORMAT_##FMT) \ + ) + +# define R300_TX_FORMAT_YUV_TO_RGB_CLAMP (1 << 22) +# define R300_TX_FORMAT_YUV_TO_RGB_NO_CLAMP (2 << 22) +# define R300_TX_FORMAT_SWAP_YUV (1 << 24) + +#define R300_TX_FORMAT2_0 0x4500 +# define R500_TXWIDTH_11 (1 << 15) +# define R500_TXHEIGHT_11 (1 << 16) + +#define R300_TX_OFFSET_0 0x4540 +# define R300_ENDIAN_SWAP_16_BIT (1 << 0) +# define R300_ENDIAN_SWAP_32_BIT (2 << 0) +# define R300_ENDIAN_SWAP_HALF_DWORD (3 << 0) +# define R300_MACRO_TILE (1 << 2) + +#define R300_TX_BORDER_COLOR_0 0x45c0 + +#define R300_TX_ENABLE 0x4104 +# define R300_TEX_0_ENABLE (1 << 0) +# define R300_TEX_1_ENABLE (1 << 1) + +#define R300_US_W_FMT 0x46b4 +#define R300_US_OUT_FMT_1 0x46a8 +#define R300_US_OUT_FMT_2 0x46ac +#define R300_US_OUT_FMT_3 0x46b0 +#define R300_US_OUT_FMT_0 0x46a4 +# define R300_OUT_FMT_C4_8 (0 << 0) +# define R300_OUT_FMT_C4_10 (1 << 0) +# define R300_OUT_FMT_C4_10_GAMMA (2 << 0) +# define R300_OUT_FMT_C_16 (3 << 0) +# define R300_OUT_FMT_C2_16 (4 << 0) +# define R300_OUT_FMT_C4_16 (5 << 0) +# define R300_OUT_FMT_C_16_MPEG (6 << 0) +# define R300_OUT_FMT_C2_16_MPEG (7 << 0) +# define R300_OUT_FMT_C2_4 (8 << 0) +# define R300_OUT_FMT_C_3_3_2 (9 << 0) +# define R300_OUT_FMT_C_5_6_5 (10 << 0) +# define R300_OUT_FMT_C_11_11_10 (11 << 0) +# define R300_OUT_FMT_C_10_11_11 (12 << 0) +# define R300_OUT_FMT_C_2_10_10_10 (13 << 0) +# define R300_OUT_FMT_UNUSED (15 << 0) +# define R300_OUT_FMT_C_16_FP (16 << 0) +# define R300_OUT_FMT_C2_16_FP (17 << 0) +# define R300_OUT_FMT_C4_16_FP (18 << 0) +# define R300_OUT_FMT_C_32_FP (19 << 0) +# define R300_OUT_FMT_C2_32_FP (20 << 0) +# define R300_OUT_FMT_C4_32_FP (21 << 0) +# define R300_OUT_FMT_C0_SEL_ALPHA (0 << 8) +# define R300_OUT_FMT_C0_SEL_RED (1 << 8) +# define R300_OUT_FMT_C0_SEL_GREEN (2 << 8) +# define R300_OUT_FMT_C0_SEL_BLUE (3 << 8) +# define R300_OUT_FMT_C1_SEL_ALPHA (0 << 10) +# define R300_OUT_FMT_C1_SEL_RED (1 << 10) +# define R300_OUT_FMT_C1_SEL_GREEN (2 << 10) +# define R300_OUT_FMT_C1_SEL_BLUE (3 << 10) +# define R300_OUT_FMT_C2_SEL_ALPHA (0 << 12) +# define R300_OUT_FMT_C2_SEL_RED (1 << 12) +# define R300_OUT_FMT_C2_SEL_GREEN (2 << 12) +# define R300_OUT_FMT_C2_SEL_BLUE (3 << 12) +# define R300_OUT_FMT_C3_SEL_ALPHA (0 << 14) +# define R300_OUT_FMT_C3_SEL_RED (1 << 14) +# define R300_OUT_FMT_C3_SEL_GREEN (2 << 14) +# define R300_OUT_FMT_C3_SEL_BLUE (3 << 14) +#define R300_US_CONFIG 0x4600 +# define R300_NLEVEL_SHIFT 0 +# define R300_FIRST_TEX (1 << 3) +# define R500_ZERO_TIMES_ANYTHING_EQUALS_ZERO (1 << 1) +#define R300_US_PIXSIZE 0x4604 +#define R300_US_CODE_OFFSET 0x4608 +# define R300_ALU_CODE_OFFSET(x) (x << 0) +# define R300_ALU_CODE_SIZE(x) (x << 6) +# define R300_TEX_CODE_OFFSET(x) (x << 13) +# define R300_TEX_CODE_SIZE(x) (x << 18) +#define R300_US_CODE_ADDR_0 0x4610 +# define R300_ALU_START(x) (x << 0) +# define R300_ALU_SIZE(x) (x << 6) +# define R300_TEX_START(x) (x << 12) +# define R300_TEX_SIZE(x) (x << 17) +# define R300_RGBA_OUT (1 << 22) +# define R300_W_OUT (1 << 23) +#define R300_US_CODE_ADDR_1 0x4614 +#define R300_US_CODE_ADDR_2 0x4618 +#define R300_US_CODE_ADDR_3 0x461c +#define R300_US_TEX_INST_0 0x4620 +#define R300_US_TEX_INST_1 0x4624 +#define R300_US_TEX_INST_2 0x4628 +# define R300_TEX_SRC_ADDR(x) (x << 0) +# define R300_TEX_DST_ADDR(x) (x << 6) +# define R300_TEX_ID(x) (x << 11) +# define R300_TEX_INST(x) (x << 15) +# define R300_TEX_INST_NOP 0 +# define R300_TEX_INST_LD 1 +# define R300_TEX_INST_TEXKILL 2 +# define R300_TEX_INST_PROJ 3 +# define R300_TEX_INST_LODBIAS 4 +#define R300_US_ALU_RGB_ADDR_0 0x46c0 +#define R300_US_ALU_RGB_ADDR_1 0x46c4 +#define R300_US_ALU_RGB_ADDR_2 0x46c8 +/* for ADDR0-2, values 0-31 specify a location in the pixel stack, + values 32-63 specify a constant */ +# define R300_ALU_RGB_ADDR0(x) (x << 0) +# define R300_ALU_RGB_ADDR1(x) (x << 6) +# define R300_ALU_RGB_ADDR2(x) (x << 12) +/* ADDRD - where on the pixel stack the result of this instruction + will be written */ +# define R300_ALU_RGB_ADDRD(x) (x << 18) +# define R300_ALU_RGB_WMASK(x) (x << 23) +# define R300_ALU_RGB_OMASK(x) (x << 26) +# define R300_ALU_RGB_MASK_NONE 0 +# define R300_ALU_RGB_MASK_R 1 +# define R300_ALU_RGB_MASK_G 2 +# define R300_ALU_RGB_MASK_B 4 +# define R300_ALU_RGB_TARGET_A (0 << 29) +# define R300_ALU_RGB_TARGET_B (1 << 29) +# define R300_ALU_RGB_TARGET_C (2 << 29) +# define R300_ALU_RGB_TARGET_D (3 << 29) +#define R300_US_ALU_RGB_INST_0 0x48c0 +#define R300_US_ALU_RGB_INST_1 0x48c4 +#define R300_US_ALU_RGB_INST_2 0x48c8 +# define R300_ALU_RGB_SEL_A(x) (x << 0) +# define R300_ALU_RGB_SRC0_RGB 0 +# define R300_ALU_RGB_SRC0_RRR 1 +# define R300_ALU_RGB_SRC0_GGG 2 +# define R300_ALU_RGB_SRC0_BBB 3 +# define R300_ALU_RGB_SRC1_RGB 4 +# define R300_ALU_RGB_SRC1_RRR 5 +# define R300_ALU_RGB_SRC1_GGG 6 +# define R300_ALU_RGB_SRC1_BBB 7 +# define R300_ALU_RGB_SRC2_RGB 8 +# define R300_ALU_RGB_SRC2_RRR 9 +# define R300_ALU_RGB_SRC2_GGG 10 +# define R300_ALU_RGB_SRC2_BBB 11 +# define R300_ALU_RGB_SRC0_AAA 12 +# define R300_ALU_RGB_SRC1_AAA 13 +# define R300_ALU_RGB_SRC2_AAA 14 +# define R300_ALU_RGB_SRCP_RGB 15 +# define R300_ALU_RGB_SRCP_RRR 16 +# define R300_ALU_RGB_SRCP_GGG 17 +# define R300_ALU_RGB_SRCP_BBB 18 +# define R300_ALU_RGB_SRCP_AAA 19 +# define R300_ALU_RGB_0_0 20 +# define R300_ALU_RGB_1_0 21 +# define R300_ALU_RGB_0_5 22 +# define R300_ALU_RGB_SRC0_GBR 23 +# define R300_ALU_RGB_SRC1_GBR 24 +# define R300_ALU_RGB_SRC2_GBR 25 +# define R300_ALU_RGB_SRC0_BRG 26 +# define R300_ALU_RGB_SRC1_BRG 27 +# define R300_ALU_RGB_SRC2_BRG 28 +# define R300_ALU_RGB_SRC0_ABG 29 +# define R300_ALU_RGB_SRC1_ABG 30 +# define R300_ALU_RGB_SRC2_ABG 31 +# define R300_ALU_RGB_MOD_A(x) (x << 5) +# define R300_ALU_RGB_MOD_NOP 0 +# define R300_ALU_RGB_MOD_NEG 1 +# define R300_ALU_RGB_MOD_ABS 2 +# define R300_ALU_RGB_MOD_NAB 3 +# define R300_ALU_RGB_SEL_B(x) (x << 7) +# define R300_ALU_RGB_MOD_B(x) (x << 12) +# define R300_ALU_RGB_SEL_C(x) (x << 14) +# define R300_ALU_RGB_MOD_C(x) (x << 19) +# define R300_ALU_RGB_SRCP_OP(x) (x << 21) +# define R300_ALU_RGB_SRCP_OP_1_MINUS_2RGB0 0 +# define R300_ALU_RGB_SRCP_OP_RGB1_MINUS_RGB0 1 +# define R300_ALU_RGB_SRCP_OP_RGB1_PLUS_RGB0 2 +# define R300_ALU_RGB_SRCP_OP_1_MINUS_RGB0 3 +# define R300_ALU_RGB_OP(x) (x << 23) +# define R300_ALU_RGB_OP_MAD 0 +# define R300_ALU_RGB_OP_DP3 1 +# define R300_ALU_RGB_OP_DP4 2 +# define R300_ALU_RGB_OP_D2A 3 +# define R300_ALU_RGB_OP_MIN 4 +# define R300_ALU_RGB_OP_MAX 5 +# define R300_ALU_RGB_OP_CND 7 +# define R300_ALU_RGB_OP_CMP 8 +# define R300_ALU_RGB_OP_FRC 9 +# define R300_ALU_RGB_OP_SOP 10 +# define R300_ALU_RGB_OMOD(x) (x << 27) +# define R300_ALU_RGB_OMOD_NONE 0 +# define R300_ALU_RGB_OMOD_MUL_2 1 +# define R300_ALU_RGB_OMOD_MUL_4 2 +# define R300_ALU_RGB_OMOD_MUL_8 3 +# define R300_ALU_RGB_OMOD_DIV_2 4 +# define R300_ALU_RGB_OMOD_DIV_4 5 +# define R300_ALU_RGB_OMOD_DIV_8 6 +# define R300_ALU_RGB_CLAMP (1 << 30) +# define R300_ALU_RGB_INSERT_NOP (1 << 31) +#define R300_US_ALU_ALPHA_ADDR_0 0x47c0 +#define R300_US_ALU_ALPHA_ADDR_1 0x47c4 +#define R300_US_ALU_ALPHA_ADDR_2 0x47c8 +/* for ADDR0-2, values 0-31 specify a location in the pixel stack, + values 32-63 specify a constant */ +# define R300_ALU_ALPHA_ADDR0(x) (x << 0) +# define R300_ALU_ALPHA_ADDR1(x) (x << 6) +# define R300_ALU_ALPHA_ADDR2(x) (x << 12) +/* ADDRD - where on the pixel stack the result of this instruction + will be written */ +# define R300_ALU_ALPHA_ADDRD(x) (x << 18) +# define R300_ALU_ALPHA_WMASK(x) (x << 23) +# define R300_ALU_ALPHA_OMASK(x) (x << 24) +# define R300_ALU_ALPHA_OMASK_W(x) (x << 27) +# define R300_ALU_ALPHA_MASK_NONE 0 +# define R300_ALU_ALPHA_MASK_A 1 +# define R300_ALU_ALPHA_TARGET_A (0 << 25) +# define R300_ALU_ALPHA_TARGET_B (1 << 25) +# define R300_ALU_ALPHA_TARGET_C (2 << 25) +# define R300_ALU_ALPHA_TARGET_D (3 << 25) +#define R300_US_ALU_ALPHA_INST_0 0x49c0 +#define R300_US_ALU_ALPHA_INST_1 0x49c4 +#define R300_US_ALU_ALPHA_INST_2 0x49c8 +# define R300_ALU_ALPHA_SEL_A(x) (x << 0) +# define R300_ALU_ALPHA_SRC0_R 0 +# define R300_ALU_ALPHA_SRC0_G 1 +# define R300_ALU_ALPHA_SRC0_B 2 +# define R300_ALU_ALPHA_SRC1_R 3 +# define R300_ALU_ALPHA_SRC1_G 4 +# define R300_ALU_ALPHA_SRC1_B 5 +# define R300_ALU_ALPHA_SRC2_R 6 +# define R300_ALU_ALPHA_SRC2_G 7 +# define R300_ALU_ALPHA_SRC2_B 8 +# define R300_ALU_ALPHA_SRC0_A 9 +# define R300_ALU_ALPHA_SRC1_A 10 +# define R300_ALU_ALPHA_SRC2_A 11 +# define R300_ALU_ALPHA_SRCP_R 12 +# define R300_ALU_ALPHA_SRCP_G 13 +# define R300_ALU_ALPHA_SRCP_B 14 +# define R300_ALU_ALPHA_SRCP_A 15 +# define R300_ALU_ALPHA_0_0 16 +# define R300_ALU_ALPHA_1_0 17 +# define R300_ALU_ALPHA_0_5 18 +# define R300_ALU_ALPHA_MOD_A(x) (x << 5) +# define R300_ALU_ALPHA_MOD_NOP 0 +# define R300_ALU_ALPHA_MOD_NEG 1 +# define R300_ALU_ALPHA_MOD_ABS 2 +# define R300_ALU_ALPHA_MOD_NAB 3 +# define R300_ALU_ALPHA_SEL_B(x) (x << 7) +# define R300_ALU_ALPHA_MOD_B(x) (x << 12) +# define R300_ALU_ALPHA_SEL_C(x) (x << 14) +# define R300_ALU_ALPHA_MOD_C(x) (x << 19) +# define R300_ALU_ALPHA_SRCP_OP(x) (x << 21) +# define R300_ALU_ALPHA_SRCP_OP_1_MINUS_2RGB0 0 +# define R300_ALU_ALPHA_SRCP_OP_RGB1_MINUS_RGB0 1 +# define R300_ALU_ALPHA_SRCP_OP_RGB1_PLUS_RGB0 2 +# define R300_ALU_ALPHA_SRCP_OP_1_MINUS_RGB0 3 +# define R300_ALU_ALPHA_OP(x) (x << 23) +# define R300_ALU_ALPHA_OP_MAD 0 +# define R300_ALU_ALPHA_OP_DP 1 +# define R300_ALU_ALPHA_OP_MIN 2 +# define R300_ALU_ALPHA_OP_MAX 3 +# define R300_ALU_ALPHA_OP_CND 5 +# define R300_ALU_ALPHA_OP_CMP 6 +# define R300_ALU_ALPHA_OP_FRC 7 +# define R300_ALU_ALPHA_OP_EX2 8 +# define R300_ALU_ALPHA_OP_LN2 9 +# define R300_ALU_ALPHA_OP_RCP 10 +# define R300_ALU_ALPHA_OP_RSQ 11 +# define R300_ALU_ALPHA_OMOD(x) (x << 27) +# define R300_ALU_ALPHA_OMOD_NONE 0 +# define R300_ALU_ALPHA_OMOD_MUL_2 1 +# define R300_ALU_ALPHA_OMOD_MUL_4 2 +# define R300_ALU_ALPHA_OMOD_MUL_8 3 +# define R300_ALU_ALPHA_OMOD_DIV_2 4 +# define R300_ALU_ALPHA_OMOD_DIV_4 5 +# define R300_ALU_ALPHA_OMOD_DIV_8 6 +# define R300_ALU_ALPHA_CLAMP (1 << 30) + +#define R300_FG_DEPTH_SRC 0x4bd8 +#define R300_FG_FOG_BLEND 0x4bc0 +#define R300_FG_ALPHA_FUNC 0x4bd4 + +#define R300_DST_PIPE_CONFIG 0x170c +# define R300_PIPE_AUTO_CONFIG (1 << 31) +#define R300_RB2D_DSTCACHE_MODE 0x3428 +#define R300_RB2D_DSTCACHE_MODE 0x3428 +# define R300_DC_AUTOFLUSH_ENABLE (1 << 8) +# define R300_DC_DC_DISABLE_IGNORE_PE (1 << 17) +#define R300_RB2D_DSTCACHE_CTLSTAT 0x342c /* use DSTCACHE_CTLSTAT instead */ +#define R300_DSTCACHE_CTLSTAT 0x1714 +# define R300_DC_FLUSH_2D (1 << 0) +# define R300_DC_FREE_2D (1 << 2) +# define R300_RB2D_DC_FLUSH_ALL (R300_DC_FLUSH_2D | R300_DC_FREE_2D) +# define R300_RB2D_DC_BUSY (1 << 31) +#define R300_RB3D_DSTCACHE_CTLSTAT 0x4e4c +# define R300_DC_FLUSH_3D (2 << 0) +# define R300_DC_FREE_3D (2 << 2) +# define R300_RB3D_DC_FLUSH_ALL (R300_DC_FLUSH_3D | R300_DC_FREE_3D) +# define R300_DC_FINISH_3D (1 << 4) +#define R300_RB3D_ZCACHE_CTLSTAT 0x4f18 +# define R300_ZC_FLUSH (1 << 0) +# define R300_ZC_FREE (1 << 1) +# define R300_ZC_FLUSH_ALL 0x3 +#define R300_RB3D_ZSTENCILCNTL 0x4f04 +#define R300_RB3D_ZCACHE_CTLSTAT 0x4f18 +#define R300_RB3D_BW_CNTL 0x4f1c +#define R300_RB3D_ZCNTL 0x4f00 +#define R300_RB3D_ZTOP 0x4f14 +#define R300_RB3D_ROPCNTL 0x4e18 +#define R300_RB3D_BLENDCNTL 0x4e04 +# define R300_ALPHA_BLEND_ENABLE (1 << 0) +# define R300_SEPARATE_ALPHA_ENABLE (1 << 1) +# define R300_READ_ENABLE (1 << 2) +#define R300_RB3D_ABLENDCNTL 0x4e08 +#define R300_RB3D_DSTCACHE_CTLSTAT 0x4e4c +#define R300_RB3D_COLOROFFSET0 0x4e28 +#define R300_RB3D_COLORPITCH0 0x4e38 +# define R300_COLORTILE (1 << 16) +# define R300_COLORENDIAN_WORD (1 << 19) +# define R300_COLORENDIAN_DWORD (2 << 19) +# define R300_COLORENDIAN_HALF_DWORD (3 << 19) +# define R300_COLORFORMAT_ARGB1555 (3 << 21) +# define R300_COLORFORMAT_RGB565 (4 << 21) +# define R300_COLORFORMAT_ARGB8888 (6 << 21) +# define R300_COLORFORMAT_ARGB32323232 (7 << 21) +# define R300_COLORFORMAT_I8 (9 << 21) +# define R300_COLORFORMAT_ARGB16161616 (10 << 21) +# define R300_COLORFORMAT_VYUY (11 << 21) +# define R300_COLORFORMAT_YVYU (12 << 21) +# define R300_COLORFORMAT_UV88 (13 << 21) +# define R300_COLORFORMAT_ARGB4444 (15 << 21) + +#define R300_RB3D_AARESOLVE_CTL 0x4e88 +#define R300_RB3D_COLOR_CHANNEL_MASK 0x4e0c +# define R300_BLUE_MASK_EN (1 << 0) +# define R300_GREEN_MASK_EN (1 << 1) +# define R300_RED_MASK_EN (1 << 2) +# define R300_ALPHA_MASK_EN (1 << 3) +#define R300_RB3D_COLOR_CLEAR_VALUE 0x4e14 +#define R300_RB3D_DSTCACHE_CTLSTAT 0x4e4c +#define R300_RB3D_CCTL 0x4e00 +#define R300_RB3D_DITHER_CTL 0x4e50 + +#define R300_SC_EDGERULE 0x43a8 +#define R300_SC_SCISSOR0 0x43e0 +#define R300_SC_SCISSOR1 0x43e4 +# define R300_SCISSOR_X_SHIFT 0 +# define R300_SCISSOR_Y_SHIFT 13 +#define R300_SC_CLIP_0_A 0x43b0 +#define R300_SC_CLIP_0_B 0x43b4 +# define R300_CLIP_X_SHIFT 0 +# define R300_CLIP_Y_SHIFT 13 +#define R300_SC_CLIP_RULE 0x43d0 +#define R300_SC_SCREENDOOR 0x43e8 + +/* R500 US has to be loaded through an index/data pair */ +#define R500_GA_US_VECTOR_INDEX 0x4250 +# define R500_US_VECTOR_INDEX(x) (x << 0) +# define R500_US_VECTOR_TYPE_INST (0 << 16) +# define R500_US_VECTOR_TYPE_CONST (1 << 16) +# define R500_US_VECTOR_CLAMP (1 << 17) +#define R500_GA_US_VECTOR_DATA 0x4254 + +/* + * The R500 unified shader (US) registers come in banks of 512 each, one + * for each instruction slot in the shader. You can't touch them directly. + * R500_US_VECTOR_INDEX() sets the base instruction to modify; successive + * writes to R500_GA_US_VECTOR_DATA autoincrement the index after the + * instruction is fully specified. + */ +#define R500_US_ALU_ALPHA_INST_0 0xa800 +# define R500_ALPHA_OP_MAD 0 +# define R500_ALPHA_OP_DP 1 +# define R500_ALPHA_OP_MIN 2 +# define R500_ALPHA_OP_MAX 3 +/* #define R500_ALPHA_OP_RESERVED 4 */ +# define R500_ALPHA_OP_CND 5 +# define R500_ALPHA_OP_CMP 6 +# define R500_ALPHA_OP_FRC 7 +# define R500_ALPHA_OP_EX2 8 +# define R500_ALPHA_OP_LN2 9 +# define R500_ALPHA_OP_RCP 10 +# define R500_ALPHA_OP_RSQ 11 +# define R500_ALPHA_OP_SIN 12 +# define R500_ALPHA_OP_COS 13 +# define R500_ALPHA_OP_MDH 14 +# define R500_ALPHA_OP_MDV 15 +# define R500_ALPHA_ADDRD(x) (x << 4) +# define R500_ALPHA_ADDRD_REL (1 << 11) +# define R500_ALPHA_SEL_A_SRC0 (0 << 12) +# define R500_ALPHA_SEL_A_SRC1 (1 << 12) +# define R500_ALPHA_SEL_A_SRC2 (2 << 12) +# define R500_ALPHA_SEL_A_SRCP (3 << 12) +# define R500_ALPHA_SWIZ_A_R (0 << 14) +# define R500_ALPHA_SWIZ_A_G (1 << 14) +# define R500_ALPHA_SWIZ_A_B (2 << 14) +# define R500_ALPHA_SWIZ_A_A (3 << 14) +# define R500_ALPHA_SWIZ_A_0 (4 << 14) +# define R500_ALPHA_SWIZ_A_HALF (5 << 14) +# define R500_ALPHA_SWIZ_A_1 (6 << 14) +/* #define R500_ALPHA_SWIZ_A_UNUSED (7 << 14) */ +# define R500_ALPHA_MOD_A_NOP (0 << 17) +# define R500_ALPHA_MOD_A_NEG (1 << 17) +# define R500_ALPHA_MOD_A_ABS (2 << 17) +# define R500_ALPHA_MOD_A_NAB (3 << 17) +# define R500_ALPHA_SEL_B_SRC0 (0 << 19) +# define R500_ALPHA_SEL_B_SRC1 (1 << 19) +# define R500_ALPHA_SEL_B_SRC2 (2 << 19) +# define R500_ALPHA_SEL_B_SRCP (3 << 19) +# define R500_ALPHA_SWIZ_B_R (0 << 21) +# define R500_ALPHA_SWIZ_B_G (1 << 21) +# define R500_ALPHA_SWIZ_B_B (2 << 21) +# define R500_ALPHA_SWIZ_B_A (3 << 21) +# define R500_ALPHA_SWIZ_B_0 (4 << 21) +# define R500_ALPHA_SWIZ_B_HALF (5 << 21) +# define R500_ALPHA_SWIZ_B_1 (6 << 21) +/* #define R500_ALPHA_SWIZ_B_UNUSED (7 << 21) */ +# define R500_ALPHA_MOD_B_NOP (0 << 24) +# define R500_ALPHA_MOD_B_NEG (1 << 24) +# define R500_ALPHA_MOD_B_ABS (2 << 24) +# define R500_ALPHA_MOD_B_NAB (3 << 24) +# define R500_ALPHA_OMOD_IDENTITY (0 << 26) +# define R500_ALPHA_OMOD_MUL_2 (1 << 26) +# define R500_ALPHA_OMOD_MUL_4 (2 << 26) +# define R500_ALPHA_OMOD_MUL_8 (3 << 26) +# define R500_ALPHA_OMOD_DIV_2 (4 << 26) +# define R500_ALPHA_OMOD_DIV_4 (5 << 26) +# define R500_ALPHA_OMOD_DIV_8 (6 << 26) +# define R500_ALPHA_OMOD_DISABLE (7 << 26) +# define R500_ALPHA_TARGET(x) (x << 29) +# define R500_ALPHA_W_OMASK (1 << 31) +#define R500_US_ALU_ALPHA_ADDR_0 0x9800 +# define R500_ALPHA_ADDR0(x) (x << 0) +# define R500_ALPHA_ADDR0_CONST (1 << 8) +# define R500_ALPHA_ADDR0_REL (1 << 9) +# define R500_ALPHA_ADDR1(x) (x << 10) +# define R500_ALPHA_ADDR1_CONST (1 << 18) +# define R500_ALPHA_ADDR1_REL (1 << 19) +# define R500_ALPHA_ADDR2(x) (x << 20) +# define R500_ALPHA_ADDR2_CONST (1 << 28) +# define R500_ALPHA_ADDR2_REL (1 << 29) +# define R500_ALPHA_SRCP_OP_1_MINUS_2A0 (0 << 30) +# define R500_ALPHA_SRCP_OP_A1_MINUS_A0 (1 << 30) +# define R500_ALPHA_SRCP_OP_A1_PLUS_A0 (2 << 30) +# define R500_ALPHA_SRCP_OP_1_MINUS_A0 (3 << 30) +#define R500_US_ALU_RGBA_INST_0 0xb000 +# define R500_ALU_RGBA_OP_MAD (0 << 0) +# define R500_ALU_RGBA_OP_DP3 (1 << 0) +# define R500_ALU_RGBA_OP_DP4 (2 << 0) +# define R500_ALU_RGBA_OP_D2A (3 << 0) +# define R500_ALU_RGBA_OP_MIN (4 << 0) +# define R500_ALU_RGBA_OP_MAX (5 << 0) +/* #define R500_ALU_RGBA_OP_RESERVED (6 << 0) */ +# define R500_ALU_RGBA_OP_CND (7 << 0) +# define R500_ALU_RGBA_OP_CMP (8 << 0) +# define R500_ALU_RGBA_OP_FRC (9 << 0) +# define R500_ALU_RGBA_OP_SOP (10 << 0) +# define R500_ALU_RGBA_OP_MDH (11 << 0) +# define R500_ALU_RGBA_OP_MDV (12 << 0) +# define R500_ALU_RGBA_ADDRD(x) (x << 4) +# define R500_ALU_RGBA_ADDRD_REL (1 << 11) +# define R500_ALU_RGBA_SEL_C_SRC0 (0 << 12) +# define R500_ALU_RGBA_SEL_C_SRC1 (1 << 12) +# define R500_ALU_RGBA_SEL_C_SRC2 (2 << 12) +# define R500_ALU_RGBA_SEL_C_SRCP (3 << 12) +# define R500_ALU_RGBA_R_SWIZ_R (0 << 14) +# define R500_ALU_RGBA_R_SWIZ_G (1 << 14) +# define R500_ALU_RGBA_R_SWIZ_B (2 << 14) +# define R500_ALU_RGBA_R_SWIZ_A (3 << 14) +# define R500_ALU_RGBA_R_SWIZ_0 (4 << 14) +# define R500_ALU_RGBA_R_SWIZ_HALF (5 << 14) +# define R500_ALU_RGBA_R_SWIZ_1 (6 << 14) +/* #define R500_ALU_RGBA_R_SWIZ_UNUSED (7 << 14) */ +# define R500_ALU_RGBA_G_SWIZ_R (0 << 17) +# define R500_ALU_RGBA_G_SWIZ_G (1 << 17) +# define R500_ALU_RGBA_G_SWIZ_B (2 << 17) +# define R500_ALU_RGBA_G_SWIZ_A (3 << 17) +# define R500_ALU_RGBA_G_SWIZ_0 (4 << 17) +# define R500_ALU_RGBA_G_SWIZ_HALF (5 << 17) +# define R500_ALU_RGBA_G_SWIZ_1 (6 << 17) +/* #define R500_ALU_RGBA_G_SWIZ_UNUSED (7 << 17) */ +# define R500_ALU_RGBA_B_SWIZ_R (0 << 20) +# define R500_ALU_RGBA_B_SWIZ_G (1 << 20) +# define R500_ALU_RGBA_B_SWIZ_B (2 << 20) +# define R500_ALU_RGBA_B_SWIZ_A (3 << 20) +# define R500_ALU_RGBA_B_SWIZ_0 (4 << 20) +# define R500_ALU_RGBA_B_SWIZ_HALF (5 << 20) +# define R500_ALU_RGBA_B_SWIZ_1 (6 << 20) +/* #define R500_ALU_RGBA_B_SWIZ_UNUSED (7 << 20) */ +# define R500_ALU_RGBA_MOD_C_NOP (0 << 23) +# define R500_ALU_RGBA_MOD_C_NEG (1 << 23) +# define R500_ALU_RGBA_MOD_C_ABS (2 << 23) +# define R500_ALU_RGBA_MOD_C_NAB (3 << 23) +# define R500_ALU_RGBA_ALPHA_SEL_C_SRC0 (0 << 25) +# define R500_ALU_RGBA_ALPHA_SEL_C_SRC1 (1 << 25) +# define R500_ALU_RGBA_ALPHA_SEL_C_SRC2 (2 << 25) +# define R500_ALU_RGBA_ALPHA_SEL_C_SRCP (3 << 25) +# define R500_ALU_RGBA_A_SWIZ_R (0 << 27) +# define R500_ALU_RGBA_A_SWIZ_G (1 << 27) +# define R500_ALU_RGBA_A_SWIZ_B (2 << 27) +# define R500_ALU_RGBA_A_SWIZ_A (3 << 27) +# define R500_ALU_RGBA_A_SWIZ_0 (4 << 27) +# define R500_ALU_RGBA_A_SWIZ_HALF (5 << 27) +# define R500_ALU_RGBA_A_SWIZ_1 (6 << 27) +/* #define R500_ALU_RGBA_A_SWIZ_UNUSED (7 << 27) */ +# define R500_ALU_RGBA_ALPHA_MOD_C_NOP (0 << 30) +# define R500_ALU_RGBA_ALPHA_MOD_C_NEG (1 << 30) +# define R500_ALU_RGBA_ALPHA_MOD_C_ABS (2 << 30) +# define R500_ALU_RGBA_ALPHA_MOD_C_NAB (3 << 30) +#define R500_US_ALU_RGB_INST_0 0xa000 +# define R500_ALU_RGB_SEL_A_SRC0 (0 << 0) +# define R500_ALU_RGB_SEL_A_SRC1 (1 << 0) +# define R500_ALU_RGB_SEL_A_SRC2 (2 << 0) +# define R500_ALU_RGB_SEL_A_SRCP (3 << 0) +# define R500_ALU_RGB_R_SWIZ_A_R (0 << 2) +# define R500_ALU_RGB_R_SWIZ_A_G (1 << 2) +# define R500_ALU_RGB_R_SWIZ_A_B (2 << 2) +# define R500_ALU_RGB_R_SWIZ_A_A (3 << 2) +# define R500_ALU_RGB_R_SWIZ_A_0 (4 << 2) +# define R500_ALU_RGB_R_SWIZ_A_HALF (5 << 2) +# define R500_ALU_RGB_R_SWIZ_A_1 (6 << 2) +/* #define R500_ALU_RGB_R_SWIZ_A_UNUSED (7 << 2) */ +# define R500_ALU_RGB_G_SWIZ_A_R (0 << 5) +# define R500_ALU_RGB_G_SWIZ_A_G (1 << 5) +# define R500_ALU_RGB_G_SWIZ_A_B (2 << 5) +# define R500_ALU_RGB_G_SWIZ_A_A (3 << 5) +# define R500_ALU_RGB_G_SWIZ_A_0 (4 << 5) +# define R500_ALU_RGB_G_SWIZ_A_HALF (5 << 5) +# define R500_ALU_RGB_G_SWIZ_A_1 (6 << 5) +/* #define R500_ALU_RGB_G_SWIZ_A_UNUSED (7 << 5) */ +# define R500_ALU_RGB_B_SWIZ_A_R (0 << 8) +# define R500_ALU_RGB_B_SWIZ_A_G (1 << 8) +# define R500_ALU_RGB_B_SWIZ_A_B (2 << 8) +# define R500_ALU_RGB_B_SWIZ_A_A (3 << 8) +# define R500_ALU_RGB_B_SWIZ_A_0 (4 << 8) +# define R500_ALU_RGB_B_SWIZ_A_HALF (5 << 8) +# define R500_ALU_RGB_B_SWIZ_A_1 (6 << 8) +/* #define R500_ALU_RGB_B_SWIZ_A_UNUSED (7 << 8) */ +# define R500_ALU_RGB_MOD_A_NOP (0 << 11) +# define R500_ALU_RGB_MOD_A_NEG (1 << 11) +# define R500_ALU_RGB_MOD_A_ABS (2 << 11) +# define R500_ALU_RGB_MOD_A_NAB (3 << 11) +# define R500_ALU_RGB_SEL_B_SRC0 (0 << 13) +# define R500_ALU_RGB_SEL_B_SRC1 (1 << 13) +# define R500_ALU_RGB_SEL_B_SRC2 (2 << 13) +# define R500_ALU_RGB_SEL_B_SRCP (3 << 13) +# define R500_ALU_RGB_R_SWIZ_B_R (0 << 15) +# define R500_ALU_RGB_R_SWIZ_B_G (1 << 15) +# define R500_ALU_RGB_R_SWIZ_B_B (2 << 15) +# define R500_ALU_RGB_R_SWIZ_B_A (3 << 15) +# define R500_ALU_RGB_R_SWIZ_B_0 (4 << 15) +# define R500_ALU_RGB_R_SWIZ_B_HALF (5 << 15) +# define R500_ALU_RGB_R_SWIZ_B_1 (6 << 15) +/* #define R500_ALU_RGB_R_SWIZ_B_UNUSED (7 << 15) */ +# define R500_ALU_RGB_G_SWIZ_B_R (0 << 18) +# define R500_ALU_RGB_G_SWIZ_B_G (1 << 18) +# define R500_ALU_RGB_G_SWIZ_B_B (2 << 18) +# define R500_ALU_RGB_G_SWIZ_B_A (3 << 18) +# define R500_ALU_RGB_G_SWIZ_B_0 (4 << 18) +# define R500_ALU_RGB_G_SWIZ_B_HALF (5 << 18) +# define R500_ALU_RGB_G_SWIZ_B_1 (6 << 18) +/* #define R500_ALU_RGB_G_SWIZ_B_UNUSED (7 << 18) */ +# define R500_ALU_RGB_B_SWIZ_B_R (0 << 21) +# define R500_ALU_RGB_B_SWIZ_B_G (1 << 21) +# define R500_ALU_RGB_B_SWIZ_B_B (2 << 21) +# define R500_ALU_RGB_B_SWIZ_B_A (3 << 21) +# define R500_ALU_RGB_B_SWIZ_B_0 (4 << 21) +# define R500_ALU_RGB_B_SWIZ_B_HALF (5 << 21) +# define R500_ALU_RGB_B_SWIZ_B_1 (6 << 21) +/* #define R500_ALU_RGB_B_SWIZ_B_UNUSED (7 << 21) */ +# define R500_ALU_RGB_MOD_B_NOP (0 << 24) +# define R500_ALU_RGB_MOD_B_NEG (1 << 24) +# define R500_ALU_RGB_MOD_B_ABS (2 << 24) +# define R500_ALU_RGB_MOD_B_NAB (3 << 24) +# define R500_ALU_RGB_OMOD_IDENTITY (0 << 26) +# define R500_ALU_RGB_OMOD_MUL_2 (1 << 26) +# define R500_ALU_RGB_OMOD_MUL_4 (2 << 26) +# define R500_ALU_RGB_OMOD_MUL_8 (3 << 26) +# define R500_ALU_RGB_OMOD_DIV_2 (4 << 26) +# define R500_ALU_RGB_OMOD_DIV_4 (5 << 26) +# define R500_ALU_RGB_OMOD_DIV_8 (6 << 26) +# define R500_ALU_RGB_OMOD_DISABLE (7 << 26) +# define R500_ALU_RGB_TARGET(x) (x << 29) +# define R500_ALU_RGB_WMASK (1 << 31) +#define R500_US_ALU_RGB_ADDR_0 0x9000 +# define R500_RGB_ADDR0(x) (x << 0) +# define R500_RGB_ADDR0_CONST (1 << 8) +# define R500_RGB_ADDR0_REL (1 << 9) +# define R500_RGB_ADDR1(x) (x << 10) +# define R500_RGB_ADDR1_CONST (1 << 18) +# define R500_RGB_ADDR1_REL (1 << 19) +# define R500_RGB_ADDR2(x) (x << 20) +# define R500_RGB_ADDR2_CONST (1 << 28) +# define R500_RGB_ADDR2_REL (1 << 29) +# define R500_RGB_SRCP_OP_1_MINUS_2RGB0 (0 << 30) +# define R500_RGB_SRCP_OP_RGB1_MINUS_RGB0 (1 << 30) +# define R500_RGB_SRCP_OP_RGB1_PLUS_RGB0 (2 << 30) +# define R500_RGB_SRCP_OP_1_MINUS_RGB0 (3 << 30) +#define R500_US_CMN_INST_0 0xb800 +# define R500_INST_TYPE_ALU (0 << 0) +# define R500_INST_TYPE_OUT (1 << 0) +# define R500_INST_TYPE_FC (2 << 0) +# define R500_INST_TYPE_TEX (3 << 0) +# define R500_INST_TEX_SEM_WAIT (1 << 2) +# define R500_INST_RGB_PRED_SEL_NONE (0 << 3) +# define R500_INST_RGB_PRED_SEL_RGBA (1 << 3) +# define R500_INST_RGB_PRED_SEL_RRRR (2 << 3) +# define R500_INST_RGB_PRED_SEL_GGGG (3 << 3) +# define R500_INST_RGB_PRED_SEL_BBBB (4 << 3) +# define R500_INST_RGB_PRED_SEL_AAAA (5 << 3) +# define R500_INST_RGB_PRED_INV (1 << 6) +# define R500_INST_WRITE_INACTIVE (1 << 7) +# define R500_INST_LAST (1 << 8) +# define R500_INST_NOP (1 << 9) +# define R500_INST_ALU_WAIT (1 << 10) +# define R500_INST_RGB_WMASK_R (1 << 11) +# define R500_INST_RGB_WMASK_G (1 << 12) +# define R500_INST_RGB_WMASK_B (1 << 13) +# define R500_INST_ALPHA_WMASK (1 << 14) +# define R500_INST_RGB_OMASK_R (1 << 15) +# define R500_INST_RGB_OMASK_G (1 << 16) +# define R500_INST_RGB_OMASK_B (1 << 17) +# define R500_INST_ALPHA_OMASK (1 << 18) +# define R500_INST_RGB_CLAMP (1 << 19) +# define R500_INST_ALPHA_CLAMP (1 << 20) +# define R500_INST_ALU_RESULT_SEL (1 << 21) +# define R500_INST_ALPHA_PRED_INV (1 << 22) +# define R500_INST_ALU_RESULT_OP_EQ (0 << 23) +# define R500_INST_ALU_RESULT_OP_LT (1 << 23) +# define R500_INST_ALU_RESULT_OP_GE (2 << 23) +# define R500_INST_ALU_RESULT_OP_NE (3 << 23) +# define R500_INST_ALPHA_PRED_SEL_NONE (0 << 25) +# define R500_INST_ALPHA_PRED_SEL_RGBA (1 << 25) +# define R500_INST_ALPHA_PRED_SEL_RRRR (2 << 25) +# define R500_INST_ALPHA_PRED_SEL_GGGG (3 << 25) +# define R500_INST_ALPHA_PRED_SEL_BBBB (4 << 25) +# define R500_INST_ALPHA_PRED_SEL_AAAA (5 << 25) +/* XXX next four are kind of guessed */ +# define R500_INST_STAT_WE_R (1 << 28) +# define R500_INST_STAT_WE_G (1 << 29) +# define R500_INST_STAT_WE_B (1 << 30) +# define R500_INST_STAT_WE_A (1 << 31) +/* note that these are 8 bit lengths, despite the offsets, at least for R500 */ +#define R500_US_CODE_ADDR 0x4630 +# define R500_US_CODE_START_ADDR(x) (x << 0) +# define R500_US_CODE_END_ADDR(x) (x << 16) +#define R500_US_CODE_OFFSET 0x4638 +# define R500_US_CODE_OFFSET_ADDR(x) (x << 0) +#define R500_US_CODE_RANGE 0x4634 +# define R500_US_CODE_RANGE_ADDR(x) (x << 0) +# define R500_US_CODE_RANGE_SIZE(x) (x << 16) +#define R500_US_CONFIG 0x4600 +# define R500_ZERO_TIMES_ANYTHING_EQUALS_ZERO (1 << 1) +#define R500_US_FC_ADDR_0 0xa000 +# define R500_FC_BOOL_ADDR(x) (x << 0) +# define R500_FC_INT_ADDR(x) (x << 8) +# define R500_FC_JUMP_ADDR(x) (x << 16) +# define R500_FC_JUMP_GLOBAL (1 << 31) +#define R500_US_FC_BOOL_CONST 0x4620 +# define R500_FC_KBOOL(x) (x) +#define R500_US_FC_CTRL 0x4624 +# define R500_FC_TEST_EN (1 << 30) +# define R500_FC_FULL_FC_EN (1 << 31) +#define R500_US_FC_INST_0 0x9800 +# define R500_FC_OP_JUMP (0 << 0) +# define R500_FC_OP_LOOP (1 << 0) +# define R500_FC_OP_ENDLOOP (2 << 0) +# define R500_FC_OP_REP (3 << 0) +# define R500_FC_OP_ENDREP (4 << 0) +# define R500_FC_OP_BREAKLOOP (5 << 0) +# define R500_FC_OP_BREAKREP (6 << 0) +# define R500_FC_OP_CONTINUE (7 << 0) +# define R500_FC_B_ELSE (1 << 4) +# define R500_FC_JUMP_ANY (1 << 5) +# define R500_FC_A_OP_NONE (0 << 6) +# define R500_FC_A_OP_POP (1 << 6) +# define R500_FC_A_OP_PUSH (2 << 6) +# define R500_FC_JUMP_FUNC(x) (x << 8) +# define R500_FC_B_POP_CNT(x) (x << 16) +# define R500_FC_B_OP0_NONE (0 << 24) +# define R500_FC_B_OP0_DECR (1 << 24) +# define R500_FC_B_OP0_INCR (2 << 24) +# define R500_FC_B_OP1_DECR (0 << 26) +# define R500_FC_B_OP1_NONE (1 << 26) +# define R500_FC_B_OP1_INCR (2 << 26) +# define R500_FC_IGNORE_UNCOVERED (1 << 28) +#define R500_US_FC_INT_CONST_0 0x4c00 +# define R500_FC_INT_CONST_KR(x) (x << 0) +# define R500_FC_INT_CONST_KG(x) (x << 8) +# define R500_FC_INT_CONST_KB(x) (x << 16) +/* _0 through _15 */ +#define R500_US_FORMAT0_0 0x4640 +# define R500_FORMAT_TXWIDTH(x) (x << 0) +# define R500_FORMAT_TXHEIGHT(x) (x << 11) +# define R500_FORMAT_TXDEPTH(x) (x << 22) +/* _0 through _3 */ +#define R500_US_OUT_FMT_0 0x46a4 +# define R500_OUT_FMT_C4_8 (0 << 0) +# define R500_OUT_FMT_C4_10 (1 << 0) +# define R500_OUT_FMT_C4_10_GAMMA (2 << 0) +# define R500_OUT_FMT_C_16 (3 << 0) +# define R500_OUT_FMT_C2_16 (4 << 0) +# define R500_OUT_FMT_C4_16 (5 << 0) +# define R500_OUT_FMT_C_16_MPEG (6 << 0) +# define R500_OUT_FMT_C2_16_MPEG (7 << 0) +# define R500_OUT_FMT_C2_4 (8 << 0) +# define R500_OUT_FMT_C_3_3_2 (9 << 0) +# define R500_OUT_FMT_C_6_5_6 (10 << 0) +# define R500_OUT_FMT_C_11_11_10 (11 << 0) +# define R500_OUT_FMT_C_10_11_11 (12 << 0) +# define R500_OUT_FMT_C_2_10_10_10 (13 << 0) +/* #define R500_OUT_FMT_RESERVED (14 << 0) */ +# define R500_OUT_FMT_UNUSED (15 << 0) +# define R500_OUT_FMT_C_16_FP (16 << 0) +# define R500_OUT_FMT_C2_16_FP (17 << 0) +# define R500_OUT_FMT_C4_16_FP (18 << 0) +# define R500_OUT_FMT_C_32_FP (19 << 0) +# define R500_OUT_FMT_C2_32_FP (20 << 0) +# define R500_OUT_FMT_C4_32_FP (21 << 0) +# define R500_C0_SEL_A (0 << 8) +# define R500_C0_SEL_R (1 << 8) +# define R500_C0_SEL_G (2 << 8) +# define R500_C0_SEL_B (3 << 8) +# define R500_C1_SEL_A (0 << 10) +# define R500_C1_SEL_R (1 << 10) +# define R500_C1_SEL_G (2 << 10) +# define R500_C1_SEL_B (3 << 10) +# define R500_C2_SEL_A (0 << 12) +# define R500_C2_SEL_R (1 << 12) +# define R500_C2_SEL_G (2 << 12) +# define R500_C2_SEL_B (3 << 12) +# define R500_C3_SEL_A (0 << 14) +# define R500_C3_SEL_R (1 << 14) +# define R500_C3_SEL_G (2 << 14) +# define R500_C3_SEL_B (3 << 14) +# define R500_OUT_SIGN(x) (x << 16) +# define R500_ROUND_ADJ (1 << 20) +#define R500_US_PIXSIZE 0x4604 +# define R500_PIX_SIZE(x) (x) +#define R500_US_TEX_ADDR_0 0x9800 +# define R500_TEX_SRC_ADDR(x) (x << 0) +# define R500_TEX_SRC_ADDR_REL (1 << 7) +# define R500_TEX_SRC_S_SWIZ_R (0 << 8) +# define R500_TEX_SRC_S_SWIZ_G (1 << 8) +# define R500_TEX_SRC_S_SWIZ_B (2 << 8) +# define R500_TEX_SRC_S_SWIZ_A (3 << 8) +# define R500_TEX_SRC_T_SWIZ_R (0 << 10) +# define R500_TEX_SRC_T_SWIZ_G (1 << 10) +# define R500_TEX_SRC_T_SWIZ_B (2 << 10) +# define R500_TEX_SRC_T_SWIZ_A (3 << 10) +# define R500_TEX_SRC_R_SWIZ_R (0 << 12) +# define R500_TEX_SRC_R_SWIZ_G (1 << 12) +# define R500_TEX_SRC_R_SWIZ_B (2 << 12) +# define R500_TEX_SRC_R_SWIZ_A (3 << 12) +# define R500_TEX_SRC_Q_SWIZ_R (0 << 14) +# define R500_TEX_SRC_Q_SWIZ_G (1 << 14) +# define R500_TEX_SRC_Q_SWIZ_B (2 << 14) +# define R500_TEX_SRC_Q_SWIZ_A (3 << 14) +# define R500_TEX_DST_ADDR(x) (x << 16) +# define R500_TEX_DST_ADDR_REL (1 << 23) +# define R500_TEX_DST_R_SWIZ_R (0 << 24) +# define R500_TEX_DST_R_SWIZ_G (1 << 24) +# define R500_TEX_DST_R_SWIZ_B (2 << 24) +# define R500_TEX_DST_R_SWIZ_A (3 << 24) +# define R500_TEX_DST_G_SWIZ_R (0 << 26) +# define R500_TEX_DST_G_SWIZ_G (1 << 26) +# define R500_TEX_DST_G_SWIZ_B (2 << 26) +# define R500_TEX_DST_G_SWIZ_A (3 << 26) +# define R500_TEX_DST_B_SWIZ_R (0 << 28) +# define R500_TEX_DST_B_SWIZ_G (1 << 28) +# define R500_TEX_DST_B_SWIZ_B (2 << 28) +# define R500_TEX_DST_B_SWIZ_A (3 << 28) +# define R500_TEX_DST_A_SWIZ_R (0 << 30) +# define R500_TEX_DST_A_SWIZ_G (1 << 30) +# define R500_TEX_DST_A_SWIZ_B (2 << 30) +# define R500_TEX_DST_A_SWIZ_A (3 << 30) +#define R500_US_TEX_ADDR_DXDY_0 0xa000 +# define R500_DX_ADDR(x) (x << 0) +# define R500_DX_ADDR_REL (1 << 7) +# define R500_DX_S_SWIZ_R (0 << 8) +# define R500_DX_S_SWIZ_G (1 << 8) +# define R500_DX_S_SWIZ_B (2 << 8) +# define R500_DX_S_SWIZ_A (3 << 8) +# define R500_DX_T_SWIZ_R (0 << 10) +# define R500_DX_T_SWIZ_G (1 << 10) +# define R500_DX_T_SWIZ_B (2 << 10) +# define R500_DX_T_SWIZ_A (3 << 10) +# define R500_DX_R_SWIZ_R (0 << 12) +# define R500_DX_R_SWIZ_G (1 << 12) +# define R500_DX_R_SWIZ_B (2 << 12) +# define R500_DX_R_SWIZ_A (3 << 12) +# define R500_DX_Q_SWIZ_R (0 << 14) +# define R500_DX_Q_SWIZ_G (1 << 14) +# define R500_DX_Q_SWIZ_B (2 << 14) +# define R500_DX_Q_SWIZ_A (3 << 14) +# define R500_DY_ADDR(x) (x << 16) +# define R500_DY_ADDR_REL (1 << 17) +# define R500_DY_S_SWIZ_R (0 << 24) +# define R500_DY_S_SWIZ_G (1 << 24) +# define R500_DY_S_SWIZ_B (2 << 24) +# define R500_DY_S_SWIZ_A (3 << 24) +# define R500_DY_T_SWIZ_R (0 << 26) +# define R500_DY_T_SWIZ_G (1 << 26) +# define R500_DY_T_SWIZ_B (2 << 26) +# define R500_DY_T_SWIZ_A (3 << 26) +# define R500_DY_R_SWIZ_R (0 << 28) +# define R500_DY_R_SWIZ_G (1 << 28) +# define R500_DY_R_SWIZ_B (2 << 28) +# define R500_DY_R_SWIZ_A (3 << 28) +# define R500_DY_Q_SWIZ_R (0 << 30) +# define R500_DY_Q_SWIZ_G (1 << 30) +# define R500_DY_Q_SWIZ_B (2 << 30) +# define R500_DY_Q_SWIZ_A (3 << 30) +#define R500_US_TEX_INST_0 0x9000 +# define R500_TEX_ID(x) (x << 16) +# define R500_TEX_INST_NOP (0 << 22) +# define R500_TEX_INST_LD (1 << 22) +# define R500_TEX_INST_TEXKILL (2 << 22) +# define R500_TEX_INST_PROJ (3 << 22) +# define R500_TEX_INST_LODBIAS (4 << 22) +# define R500_TEX_INST_LOD (5 << 22) +# define R500_TEX_INST_DXDY (6 << 22) +# define R500_TEX_SEM_ACQUIRE (1 << 25) +# define R500_TEX_IGNORE_UNCOVERED (1 << 26) +# define R500_TEX_UNSCALED (1 << 27) +#define R500_US_W_FMT 0x46b4 +# define R500_W_FMT_W0 (0 << 0) +# define R500_W_FMT_W24 (1 << 0) +# define R500_W_FMT_W24FP (2 << 0) +# define R500_W_SRC_US (0 << 2) +# define R500_W_SRC_RAS (1 << 2) + +#define R500_GA_US_VECTOR_INDEX 0x4250 +#define R500_GA_US_VECTOR_DATA 0x4254 + +#define R500_RS_INST_0 0x4320 +#define R500_RS_INST_1 0x4324 +# define R500_RS_INST_TEX_ID_SHIFT 0 +# define R500_RS_INST_TEX_CN_WRITE (1 << 4) +# define R500_RS_INST_TEX_ADDR_SHIFT 5 +# define R500_RS_INST_COL_ID_SHIFT 12 +# define R500_RS_INST_COL_CN_NO_WRITE (0 << 16) +# define R500_RS_INST_COL_CN_WRITE (1 << 16) +# define R500_RS_INST_COL_CN_WRITE_FBUFFER (2 << 16) +# define R500_RS_INST_COL_CN_WRITE_BACKFACE (3 << 16) +# define R500_RS_INST_COL_COL_ADDR_SHIFT 18 +# define R500_RS_INST_TEX_ADJ (1 << 25) +# define R500_RS_INST_W_CN (1 << 26) + +#define R500_US_FC_CTRL 0x4624 +#define R500_US_CODE_ADDR 0x4630 +#define R500_US_CODE_RANGE 0x4634 +#define R500_US_CODE_OFFSET 0x4638 + +#define R500_RS_IP_0 0x4074 +#define R500_RS_IP_1 0x4078 +# define R500_RS_IP_PTR_K0 62 +# define R500_RS_IP_PTR_K1 63 +# define R500_RS_IP_TEX_PTR_S_SHIFT 0 +# define R500_RS_IP_TEX_PTR_T_SHIFT 6 +# define R500_RS_IP_TEX_PTR_R_SHIFT 12 +# define R500_RS_IP_TEX_PTR_Q_SHIFT 18 +# define R500_RS_IP_COL_PTR_SHIFT 24 +# define R500_RS_IP_COL_FMT_SHIFT 27 +# define R500_RS_IP_COL_FMT_RGBA (0 << 27) +# define R500_RS_IP_OFFSET_EN (1 << 31) + +#define R500_DYN_SCLK_PWMEM_PIPE 0x000d /* PLL */ + +#endif diff --git a/linux-core/via_fence.c b/linux-core/via_fence.c index 3a680a32..6473e701 100644 --- a/linux-core/via_fence.c +++ b/linux-core/via_fence.c @@ -63,7 +63,7 @@ static void via_fence_poll(struct drm_device *dev, uint32_t class, if (!dev_priv->have_idlelock) { - drm_idlelock_take(&dev->lock); + drm_idlelock_take(&dev->primary->master->lock); dev_priv->have_idlelock = 1; } @@ -89,7 +89,7 @@ static void via_fence_poll(struct drm_device *dev, uint32_t class, if (signaled_flush_types) { waiting_types &= ~signaled_flush_types; if (!waiting_types && dev_priv->have_idlelock) { - drm_idlelock_release(&dev->lock); + drm_idlelock_release(&dev->primary->master->lock); dev_priv->have_idlelock = 0; } drm_fence_handler(dev, 0, dev_priv->emit_0_sequence, diff --git a/shared-core/Makefile.am b/shared-core/Makefile.am index 7193e527..5b65bc67 100644 --- a/shared-core/Makefile.am +++ b/shared-core/Makefile.am @@ -25,6 +25,7 @@ klibdrmincludedir = ${includedir}/drm klibdrminclude_HEADERS = \ drm.h \ + drm_mode.h \ drm_sarea.h \ i915_drm.h \ mach64_drm.h \ @@ -32,6 +33,7 @@ klibdrminclude_HEADERS = \ nouveau_drm.h \ r128_drm.h \ radeon_drm.h \ + radeon_ms_drm.h \ savage_drm.h \ sis_drm.h \ via_drm.h \ diff --git a/shared-core/amd.h b/shared-core/amd.h new file mode 100644 index 00000000..f8096053 --- /dev/null +++ b/shared-core/amd.h @@ -0,0 +1,94 @@ +/* + * 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> + */ +#ifndef __AMD_H__ +#define __AMD_H__ + +/* struct amd_cbuffer are for command buffer, this is the structure passed + * around during command validation (ie check that user have the right to + * execute the given command). + */ +struct amd_cmd_bo +{ + struct list_head list; + uint64_t *offsets; + uint32_t *cdw_id; + struct drm_buffer_object *bo; + unsigned int handle; + uint64_t mask; + uint64_t flags; + uint32_t type; +}; + +struct amd_cmd +{ + uint32_t *cdw; + uint32_t cdw_count; + struct drm_bo_kmap_obj cdw_kmap; + size_t cdw_size; + struct amd_cmd_bo *cdw_bo; + struct amd_cmd_bo bo_unused; + struct amd_cmd_bo bo_used; + struct amd_cmd_bo *bo; + uint32_t bo_count; + void *driver; +}; + +struct amd_cmd_module +{ + uint32_t numof_p0_checkers; + uint32_t numof_p3_checkers; + int (*check)(struct drm_device *dev, struct amd_cmd *cmd); + int (**check_p0)(struct drm_device *dev, struct amd_cmd *cmd, + int cdw_id, int reg); + int (**check_p3)(struct drm_device *dev, struct amd_cmd *cmd, + int cdw_id, int op, int count); +}; + +int amd_cmd_check(struct drm_device *dev, struct amd_cmd *cmd); +int amd_ioctl_cmd(struct drm_device *dev, void *data, struct drm_file *file); + +static inline struct amd_cmd_bo *amd_cmd_get_bo(struct amd_cmd *cmd, int i) +{ + if (i < cmd->bo_count && cmd->bo[i].type == DRM_AMD_CMD_BO_TYPE_DATA) { + list_del(&cmd->bo[i].list); + list_add_tail(&cmd->bo[i].list, &cmd->bo_used.list); + return &cmd->bo[i]; + } + return NULL; +} + +/* struct amd_fb amd is for storing amd framebuffer informations + */ +struct amd_fb +{ + struct drm_device *dev; + struct drm_crtc *crtc; + struct drm_display_mode *fb_mode; + struct drm_framebuffer *fb; +}; + +#endif diff --git a/shared-core/amd_legacy.h b/shared-core/amd_legacy.h new file mode 100644 index 00000000..c1e5f5da --- /dev/null +++ b/shared-core/amd_legacy.h @@ -0,0 +1,33 @@ +/* + * 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> + */ +#ifndef __AMD_LEGACY_H__ +#define __AMD_LEGACY_H__ + +int amd_legacy_cmd_module_destroy(struct drm_device *dev); +int amd_legacy_cmd_module_initialize(struct drm_device *dev); + +#endif diff --git a/shared-core/amd_legacy_cbuffer.c b/shared-core/amd_legacy_cbuffer.c new file mode 100644 index 00000000..7971ee67 --- /dev/null +++ b/shared-core/amd_legacy_cbuffer.c @@ -0,0 +1,320 @@ +/* + * Copyright 2008 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" +#include "amd.h" + +#define RADEON_DST_Y_X 0x1438 +#define RADEON_SRC_Y_X 0x1434 +#define RADEON_DP_CNTL 0x16c0 +#define RADEON_DP_GUI_MASTER_CNTL 0x146c +#define RADEON_DP_BRUSH_BKGD_CLR 0x1478 +#define RADEON_DP_BRUSH_FRGD_CLR 0x147c +#define RADEON_DP_WRITE_MASK 0x16cc +#define RADEON_DST_PITCH_OFFSET 0x142c +#define RADEON_SRC_PITCH_OFFSET 0x1428 +#define RADEON_DST_HEIGHT_WIDTH 0x143c + +struct legacy_check +{ + /* for 2D operations */ + uint32_t dp_gui_master_cntl; + uint32_t dst_offset; + uint32_t dst_pitch; + struct amd_cmd_bo *dst; + uint32_t dst_x; + uint32_t dst_y; + uint32_t dst_h; + uint32_t dst_w; + struct amd_cmd_bo *src; + uint32_t src_pitch; + uint32_t src_x; + uint32_t src_y; +}; + +static int check_blit(struct drm_device *dev, struct amd_cmd *cmd) +{ + struct legacy_check *legacy_check; + long bpp, start, end; + + legacy_check = (struct legacy_check *)cmd->driver; + /* check that gui master cntl have been set */ + if (legacy_check->dp_gui_master_cntl == 0xffffffff) { + return -EINVAL; + } + switch ((legacy_check->dp_gui_master_cntl >> 8) & 0xf) { + case 2: + bpp = 1; + break; + case 3: + case 4: + bpp = 2; + break; + case 6: + bpp = 4; + break; + default: + return -EINVAL; + } + /* check that a destination have been set */ + if (legacy_check->dst == (void *)-1) { + return -EINVAL; + } + if (legacy_check->dst_pitch == 0xffffffff) { + return -EINVAL; + } + if (legacy_check->dst_x == 0xffffffff) { + return -EINVAL; + } + if (legacy_check->dst_y == 0xffffffff) { + return -EINVAL; + } + if (legacy_check->dst_w == 0xffffffff) { + return -EINVAL; + } + if (legacy_check->dst_h == 0xffffffff) { + return -EINVAL; + } + /* compute start offset of blit */ + start = legacy_check->dst_pitch * legacy_check->dst_y + + legacy_check->dst_x * bpp; + /* compute end offset of blit */ + end = legacy_check->dst_pitch * legacy_check->dst_h + + legacy_check->dst_w * bpp; + /* FIXME: check that end offset is inside dst bo */ + + /* check that a destination have been set */ + if (legacy_check->dp_gui_master_cntl & 1) { + if (legacy_check->src == (void *)-1) { + return -EINVAL; + } + if (legacy_check->src_pitch == 0xffffffff) { + return -EINVAL; + } + if (legacy_check->src_x == 0xffffffff) { + return -EINVAL; + } + if (legacy_check->src_y == 0xffffffff) { + return -EINVAL; + } + /* compute start offset of blit */ + start = legacy_check->src_pitch * legacy_check->src_y + + legacy_check->src_x * bpp; + /* compute end offset of blit */ + end = legacy_check->src_pitch * legacy_check->dst_h + + legacy_check->dst_w * bpp + start; + /* FIXME: check that end offset is inside src bo */ + } + return 0; +} + +static int p0_dp_gui_master_cntl(struct drm_device *dev, + struct amd_cmd *cmd, + int cdw_id, int reg) +{ + struct legacy_check *legacy_check; + + legacy_check = (struct legacy_check *)cmd->driver; + legacy_check->dp_gui_master_cntl = cmd->cdw[cdw_id]; + /* we only accept src data type to be same as dst */ + if (((legacy_check->dp_gui_master_cntl >> 12) & 0x3) != 3) { + return -EINVAL; + } + return 0; +} + +static int p0_dst_pitch_offset(struct drm_device *dev, + struct amd_cmd *cmd, + int cdw_id, int reg) +{ + struct legacy_check *legacy_check; + uint32_t tmp; + int ret; + + legacy_check = (struct legacy_check *)cmd->driver; + legacy_check->dst_pitch = ((cmd->cdw[cdw_id] >> 22) & 0xff) << 6; + tmp = cmd->cdw[cdw_id] & 0x003fffff; + legacy_check->dst = amd_cmd_get_bo(cmd, tmp); + if (legacy_check->dst == NULL) { + DRM_ERROR("invalid bo (%d) for DST_PITCH_OFFSET register.\n", + tmp); + return -EINVAL; + } + ret = radeon_ms_bo_get_gpu_addr(dev, &legacy_check->dst->bo->mem, &tmp); + if (ret) { + DRM_ERROR("failed to get GPU offset for bo 0x%x.\n", + legacy_check->dst->handle); + return -EINVAL; + } + if (tmp & 0x3fff) { + DRM_ERROR("bo 0x%x offset doesn't meet alignement 0x%x.\n", + legacy_check->dst->handle, tmp); + } + cmd->cdw[cdw_id] &= 0xffc00000; + cmd->cdw[cdw_id] |= (tmp >> 10); + return 0; +} + +static int p0_src_pitch_offset(struct drm_device *dev, + struct amd_cmd *cmd, + int cdw_id, int reg) +{ + struct legacy_check *legacy_check; + uint32_t tmp; + int ret; + + legacy_check = (struct legacy_check *)cmd->driver; + legacy_check->src_pitch = ((cmd->cdw[cdw_id] >> 22) & 0xff) << 6; + tmp = cmd->cdw[cdw_id] & 0x003fffff; + legacy_check->src = amd_cmd_get_bo(cmd, tmp); + if (legacy_check->src == NULL) { + DRM_ERROR("invalid bo (%d) for SRC_PITCH_OFFSET register.\n", + tmp); + return -EINVAL; + } + ret = radeon_ms_bo_get_gpu_addr(dev, &legacy_check->src->bo->mem, &tmp); + if (ret) { + DRM_ERROR("failed to get GPU offset for bo 0x%x.\n", + legacy_check->src->handle); + return -EINVAL; + } + if (tmp & 0x3fff) { + DRM_ERROR("bo 0x%x offset doesn't meet alignement 0x%x.\n", + legacy_check->src->handle, tmp); + } + cmd->cdw[cdw_id] &= 0xffc00000; + cmd->cdw[cdw_id] |= (tmp >> 10); + return 0; +} + +static int p0_dst_y_x(struct drm_device *dev, + struct amd_cmd *cmd, + int cdw_id, int reg) +{ + struct legacy_check *legacy_check; + + legacy_check = (struct legacy_check *)cmd->driver; + legacy_check->dst_x = cmd->cdw[cdw_id] & 0xffff; + legacy_check->dst_y = (cmd->cdw[cdw_id] >> 16) & 0xffff; + return 0; +} + +static int p0_src_y_x(struct drm_device *dev, + struct amd_cmd *cmd, + int cdw_id, int reg) +{ + struct legacy_check *legacy_check; + + legacy_check = (struct legacy_check *)cmd->driver; + legacy_check->src_x = cmd->cdw[cdw_id] & 0xffff; + legacy_check->src_y = (cmd->cdw[cdw_id] >> 16) & 0xffff; + return 0; +} + +static int p0_dst_h_w(struct drm_device *dev, + struct amd_cmd *cmd, + int cdw_id, int reg) +{ + struct legacy_check *legacy_check; + + legacy_check = (struct legacy_check *)cmd->driver; + legacy_check->dst_w = cmd->cdw[cdw_id] & 0xffff; + legacy_check->dst_h = (cmd->cdw[cdw_id] >> 16) & 0xffff; + return check_blit(dev, cmd); +} + +static int legacy_cmd_check(struct drm_device *dev, + struct amd_cmd *cmd) +{ + struct legacy_check legacy_check; + + memset(&legacy_check, 0xff, sizeof(struct legacy_check)); + cmd->driver = &legacy_check; + return amd_cmd_check(dev, cmd); +} + +int amd_legacy_cmd_module_destroy(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + dev_priv->cmd_module.check = NULL; + if (dev_priv->cmd_module.numof_p0_checkers) { + drm_free(dev_priv->cmd_module.check_p0, + dev_priv->cmd_module.numof_p0_checkers * + sizeof(void*), DRM_MEM_DRIVER); + dev_priv->cmd_module.numof_p0_checkers = 0; + } + if (dev_priv->cmd_module.numof_p3_checkers) { + drm_free(dev_priv->cmd_module.check_p3, + dev_priv->cmd_module.numof_p3_checkers * + sizeof(void*), DRM_MEM_DRIVER); + dev_priv->cmd_module.numof_p3_checkers = 0; + } + return 0; +} + +int amd_legacy_cmd_module_initialize(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct amd_cmd_module *checker = &dev_priv->cmd_module; + long size; + + /* packet 0 */ + checker->numof_p0_checkers = 0x5000 >> 2; + size = checker->numof_p0_checkers * sizeof(void*); + checker->check_p0 = drm_alloc(size, DRM_MEM_DRIVER); + if (checker->check_p0 == NULL) { + amd_legacy_cmd_module_destroy(dev); + return -ENOMEM; + } + /* initialize to -1 */ + memset(checker->check_p0, 0xff, size); + + /* packet 3 */ + checker->numof_p3_checkers = 20; + size = checker->numof_p3_checkers * sizeof(void*); + checker->check_p3 = drm_alloc(size, DRM_MEM_DRIVER); + if (checker->check_p3 == NULL) { + amd_legacy_cmd_module_destroy(dev); + return -ENOMEM; + } + /* initialize to -1 */ + memset(checker->check_p3, 0xff, size); + + /* initialize packet0 checker */ + checker->check_p0[RADEON_DST_Y_X >> 2] = p0_dst_y_x; + checker->check_p0[RADEON_SRC_Y_X >> 2] = p0_src_y_x; + checker->check_p0[RADEON_DST_HEIGHT_WIDTH >> 2] = p0_dst_h_w; + checker->check_p0[RADEON_DST_PITCH_OFFSET >> 2] = p0_dst_pitch_offset; + checker->check_p0[RADEON_SRC_PITCH_OFFSET >> 2] = p0_src_pitch_offset; + checker->check_p0[RADEON_DP_GUI_MASTER_CNTL>>2] = p0_dp_gui_master_cntl; + checker->check_p0[RADEON_DP_BRUSH_FRGD_CLR >> 2] = NULL; + checker->check_p0[RADEON_DP_WRITE_MASK >> 2] = NULL; + checker->check_p0[RADEON_DP_CNTL >> 2] = NULL; + + checker->check = legacy_cmd_check; + return 0; +} diff --git a/shared-core/amd_legacy_fence.h b/shared-core/amd_legacy_fence.h new file mode 100644 index 00000000..d82138ba --- /dev/null +++ b/shared-core/amd_legacy_fence.h @@ -0,0 +1,39 @@ +/* + * 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> + */ +#ifndef __AMD_LEGACY_FENCE_H__ +#define __AMD_LEGACY_FENCE_H__ + +struct legacy_fence +{ + uint32_t sequence; + uint32_t sequence_last_reported; + uint32_t sequence_last_flush; + uint32_t fence_reg; +}; + +#endif diff --git a/shared-core/drm.h b/shared-core/drm.h index 507f0478..218a469d 100644 --- a/shared-core/drm.h +++ b/shared-core/drm.h @@ -557,8 +557,11 @@ union drm_wait_vblank { }; -#define _DRM_PRE_MODESET 1 -#define _DRM_POST_MODESET 2 +enum drm_modeset_ctl_cmd { + _DRM_PRE_MODESET = 1, + _DRM_POST_MODESET = 2, +}; + /** * DRM_IOCTL_MODESET_CTL ioctl argument type @@ -642,324 +645,6 @@ struct drm_set_version { int drm_dd_minor; }; - -#define DRM_FENCE_FLAG_EMIT 0x00000001 -#define DRM_FENCE_FLAG_SHAREABLE 0x00000002 -/** - * On hardware with no interrupt events for operation completion, - * indicates that the kernel should sleep while waiting for any blocking - * operation to complete rather than spinning. - * - * Has no effect otherwise. - */ -#define DRM_FENCE_FLAG_WAIT_LAZY 0x00000004 -#define DRM_FENCE_FLAG_NO_USER 0x00000010 - -/* Reserved for driver use */ -#define DRM_FENCE_MASK_DRIVER 0xFF000000 - -#define DRM_FENCE_TYPE_EXE 0x00000001 - -struct drm_fence_arg { - unsigned int handle; - unsigned int fence_class; - unsigned int type; - unsigned int flags; - unsigned int signaled; - unsigned int error; - unsigned int sequence; - unsigned int pad64; - uint64_t expand_pad[2]; /*Future expansion */ -}; - -/* Buffer permissions, referring to how the GPU uses the buffers. - * these translate to fence types used for the buffers. - * Typically a texture buffer is read, A destination buffer is write and - * a command (batch-) buffer is exe. Can be or-ed together. - */ - -#define DRM_BO_FLAG_READ (1ULL << 0) -#define DRM_BO_FLAG_WRITE (1ULL << 1) -#define DRM_BO_FLAG_EXE (1ULL << 2) - -/* - * All of the bits related to access mode - */ -#define DRM_BO_MASK_ACCESS (DRM_BO_FLAG_READ | DRM_BO_FLAG_WRITE | DRM_BO_FLAG_EXE) -/* - * Status flags. Can be read to determine the actual state of a buffer. - * Can also be set in the buffer mask before validation. - */ - -/* - * Mask: Never evict this buffer. Not even with force. This type of buffer is only - * available to root and must be manually removed before buffer manager shutdown - * or lock. - * Flags: Acknowledge - */ -#define DRM_BO_FLAG_NO_EVICT (1ULL << 4) - -/* - * Mask: Require that the buffer is placed in mappable memory when validated. - * If not set the buffer may or may not be in mappable memory when validated. - * Flags: If set, the buffer is in mappable memory. - */ -#define DRM_BO_FLAG_MAPPABLE (1ULL << 5) - -/* Mask: The buffer should be shareable with other processes. - * Flags: The buffer is shareable with other processes. - */ -#define DRM_BO_FLAG_SHAREABLE (1ULL << 6) - -/* Mask: If set, place the buffer in cache-coherent memory if available. - * If clear, never place the buffer in cache coherent memory if validated. - * Flags: The buffer is currently in cache-coherent memory. - */ -#define DRM_BO_FLAG_CACHED (1ULL << 7) - -/* Mask: Make sure that every time this buffer is validated, - * it ends up on the same location provided that the memory mask is the same. - * The buffer will also not be evicted when claiming space for - * other buffers. Basically a pinned buffer but it may be thrown out as - * part of buffer manager shutdown or locking. - * Flags: Acknowledge. - */ -#define DRM_BO_FLAG_NO_MOVE (1ULL << 8) - -/* Mask: Make sure the buffer is in cached memory when mapped. In conjunction - * with DRM_BO_FLAG_CACHED it also allows the buffer to be bound into the GART - * with unsnooped PTEs instead of snooped, by using chipset-specific cache - * flushing at bind time. A better name might be DRM_BO_FLAG_TT_UNSNOOPED, - * as the eviction to local memory (TTM unbind) on map is just a side effect - * to prevent aggressive cache prefetch from the GPU disturbing the cache - * management that the DRM is doing. - * - * Flags: Acknowledge. - * Buffers allocated with this flag should not be used for suballocators - * This type may have issues on CPUs with over-aggressive caching - * http://marc.info/?l=linux-kernel&m=102376926732464&w=2 - */ -#define DRM_BO_FLAG_CACHED_MAPPED (1ULL << 19) - - -/* Mask: Force DRM_BO_FLAG_CACHED flag strictly also if it is set. - * Flags: Acknowledge. - */ -#define DRM_BO_FLAG_FORCE_CACHING (1ULL << 13) - -/* - * Mask: Force DRM_BO_FLAG_MAPPABLE flag strictly also if it is clear. - * Flags: Acknowledge. - */ -#define DRM_BO_FLAG_FORCE_MAPPABLE (1ULL << 14) -#define DRM_BO_FLAG_TILE (1ULL << 15) - -/* - * Memory type flags that can be or'ed together in the mask, but only - * one appears in flags. - */ - -/* System memory */ -#define DRM_BO_FLAG_MEM_LOCAL (1ULL << 24) -/* Translation table memory */ -#define DRM_BO_FLAG_MEM_TT (1ULL << 25) -/* Vram memory */ -#define DRM_BO_FLAG_MEM_VRAM (1ULL << 26) -/* Up to the driver to define. */ -#define DRM_BO_FLAG_MEM_PRIV0 (1ULL << 27) -#define DRM_BO_FLAG_MEM_PRIV1 (1ULL << 28) -#define DRM_BO_FLAG_MEM_PRIV2 (1ULL << 29) -#define DRM_BO_FLAG_MEM_PRIV3 (1ULL << 30) -#define DRM_BO_FLAG_MEM_PRIV4 (1ULL << 31) -/* We can add more of these now with a 64-bit flag type */ - -/* - * This is a mask covering all of the memory type flags; easier to just - * use a single constant than a bunch of | values. It covers - * DRM_BO_FLAG_MEM_LOCAL through DRM_BO_FLAG_MEM_PRIV4 - */ -#define DRM_BO_MASK_MEM 0x00000000FF000000ULL -/* - * This adds all of the CPU-mapping options in with the memory - * type to label all bits which change how the page gets mapped - */ -#define DRM_BO_MASK_MEMTYPE (DRM_BO_MASK_MEM | \ - DRM_BO_FLAG_CACHED_MAPPED | \ - DRM_BO_FLAG_CACHED | \ - DRM_BO_FLAG_MAPPABLE) - -/* Driver-private flags */ -#define DRM_BO_MASK_DRIVER 0xFFFF000000000000ULL - -/* - * Don't block on validate and map. Instead, return EBUSY. - */ -#define DRM_BO_HINT_DONT_BLOCK 0x00000002 -/* - * Don't place this buffer on the unfenced list. This means - * that the buffer will not end up having a fence associated - * with it as a result of this operation - */ -#define DRM_BO_HINT_DONT_FENCE 0x00000004 -/** - * On hardware with no interrupt events for operation completion, - * indicates that the kernel should sleep while waiting for any blocking - * operation to complete rather than spinning. - * - * Has no effect otherwise. - */ -#define DRM_BO_HINT_WAIT_LAZY 0x00000008 -/* - * The client has compute relocations refering to this buffer using the - * offset in the presumed_offset field. If that offset ends up matching - * where this buffer lands, the kernel is free to skip executing those - * relocations - */ -#define DRM_BO_HINT_PRESUMED_OFFSET 0x00000010 - -#define DRM_BO_INIT_MAGIC 0xfe769812 -#define DRM_BO_INIT_MAJOR 1 -#define DRM_BO_INIT_MINOR 0 -#define DRM_BO_INIT_PATCH 0 - - -struct drm_bo_info_req { - uint64_t mask; - uint64_t flags; - unsigned int handle; - unsigned int hint; - unsigned int fence_class; - unsigned int desired_tile_stride; - unsigned int tile_info; - unsigned int pad64; - uint64_t presumed_offset; -}; - -struct drm_bo_create_req { - uint64_t flags; - uint64_t size; - uint64_t buffer_start; - unsigned int hint; - unsigned int page_alignment; -}; - - -/* - * Reply flags - */ - -#define DRM_BO_REP_BUSY 0x00000001 - -struct drm_bo_info_rep { - uint64_t flags; - uint64_t proposed_flags; - uint64_t size; - uint64_t offset; - uint64_t arg_handle; - uint64_t buffer_start; - unsigned int handle; - unsigned int fence_flags; - unsigned int rep_flags; - unsigned int page_alignment; - unsigned int desired_tile_stride; - unsigned int hw_tile_stride; - unsigned int tile_info; - unsigned int pad64; - uint64_t expand_pad[4]; /*Future expansion */ -}; - -struct drm_bo_arg_rep { - struct drm_bo_info_rep bo_info; - int ret; - unsigned int pad64; -}; - -struct drm_bo_create_arg { - union { - struct drm_bo_create_req req; - struct drm_bo_info_rep rep; - } d; -}; - -struct drm_bo_handle_arg { - unsigned int handle; -}; - -struct drm_bo_reference_info_arg { - union { - struct drm_bo_handle_arg req; - struct drm_bo_info_rep rep; - } d; -}; - -struct drm_bo_map_wait_idle_arg { - union { - struct drm_bo_info_req req; - struct drm_bo_info_rep rep; - } d; -}; - -struct drm_bo_op_req { - enum { - drm_bo_validate, - drm_bo_fence, - drm_bo_ref_fence, - } op; - unsigned int arg_handle; - struct drm_bo_info_req bo_req; -}; - - -struct drm_bo_op_arg { - uint64_t next; - union { - struct drm_bo_op_req req; - struct drm_bo_arg_rep rep; - } d; - int handled; - unsigned int pad64; -}; - - -#define DRM_BO_MEM_LOCAL 0 -#define DRM_BO_MEM_TT 1 -#define DRM_BO_MEM_VRAM 2 -#define DRM_BO_MEM_PRIV0 3 -#define DRM_BO_MEM_PRIV1 4 -#define DRM_BO_MEM_PRIV2 5 -#define DRM_BO_MEM_PRIV3 6 -#define DRM_BO_MEM_PRIV4 7 - -#define DRM_BO_MEM_TYPES 8 /* For now. */ - -#define DRM_BO_LOCK_UNLOCK_BM (1 << 0) -#define DRM_BO_LOCK_IGNORE_NO_EVICT (1 << 1) - -struct drm_bo_version_arg { - uint32_t major; - uint32_t minor; - uint32_t patchlevel; -}; - -struct drm_mm_type_arg { - unsigned int mem_type; - unsigned int lock_flags; -}; - -struct drm_mm_init_arg { - unsigned int magic; - unsigned int major; - unsigned int minor; - unsigned int mem_type; - uint64_t p_offset; - uint64_t p_size; -}; - -struct drm_mm_info_arg { - unsigned int mem_type; - uint64_t p_size; -}; - struct drm_gem_close { /** Handle of the object to be closed. */ uint32_t handle; @@ -985,6 +670,8 @@ struct drm_gem_open { uint64_t size; }; +#include "drm_mode.h" + /** * \name Ioctls Definitions */ @@ -1027,6 +714,9 @@ struct drm_gem_open { #define DRM_IOCTL_SET_SAREA_CTX DRM_IOW( 0x1c, struct drm_ctx_priv_map) #define DRM_IOCTL_GET_SAREA_CTX DRM_IOWR(0x1d, struct drm_ctx_priv_map) +#define DRM_IOCTL_SET_MASTER DRM_IO(0x1e) +#define DRM_IOCTL_DROP_MASTER DRM_IO(0x1f) + #define DRM_IOCTL_ADD_CTX DRM_IOWR(0x20, struct drm_ctx) #define DRM_IOCTL_RM_CTX DRM_IOWR(0x21, struct drm_ctx) #define DRM_IOCTL_MOD_CTX DRM_IOW( 0x22, struct drm_ctx) @@ -1057,30 +747,27 @@ struct drm_gem_open { #define DRM_IOCTL_UPDATE_DRAW DRM_IOW(0x3f, struct drm_update_draw) -#define DRM_IOCTL_MM_INIT DRM_IOWR(0xc0, struct drm_mm_init_arg) -#define DRM_IOCTL_MM_TAKEDOWN DRM_IOWR(0xc1, struct drm_mm_type_arg) -#define DRM_IOCTL_MM_LOCK DRM_IOWR(0xc2, struct drm_mm_type_arg) -#define DRM_IOCTL_MM_UNLOCK DRM_IOWR(0xc3, struct drm_mm_type_arg) - -#define DRM_IOCTL_FENCE_CREATE DRM_IOWR(0xc4, struct drm_fence_arg) -#define DRM_IOCTL_FENCE_REFERENCE DRM_IOWR(0xc6, struct drm_fence_arg) -#define DRM_IOCTL_FENCE_UNREFERENCE DRM_IOWR(0xc7, struct drm_fence_arg) -#define DRM_IOCTL_FENCE_SIGNALED DRM_IOWR(0xc8, struct drm_fence_arg) -#define DRM_IOCTL_FENCE_FLUSH DRM_IOWR(0xc9, struct drm_fence_arg) -#define DRM_IOCTL_FENCE_WAIT DRM_IOWR(0xca, struct drm_fence_arg) -#define DRM_IOCTL_FENCE_EMIT DRM_IOWR(0xcb, struct drm_fence_arg) -#define DRM_IOCTL_FENCE_BUFFERS DRM_IOWR(0xcc, struct drm_fence_arg) - -#define DRM_IOCTL_BO_CREATE DRM_IOWR(0xcd, struct drm_bo_create_arg) -#define DRM_IOCTL_BO_MAP DRM_IOWR(0xcf, struct drm_bo_map_wait_idle_arg) -#define DRM_IOCTL_BO_UNMAP DRM_IOWR(0xd0, struct drm_bo_handle_arg) -#define DRM_IOCTL_BO_REFERENCE DRM_IOWR(0xd1, struct drm_bo_reference_info_arg) -#define DRM_IOCTL_BO_UNREFERENCE DRM_IOWR(0xd2, struct drm_bo_handle_arg) -#define DRM_IOCTL_BO_SETSTATUS DRM_IOWR(0xd3, struct drm_bo_map_wait_idle_arg) -#define DRM_IOCTL_BO_INFO DRM_IOWR(0xd4, struct drm_bo_reference_info_arg) -#define DRM_IOCTL_BO_WAIT_IDLE DRM_IOWR(0xd5, struct drm_bo_map_wait_idle_arg) -#define DRM_IOCTL_BO_VERSION DRM_IOR(0xd6, struct drm_bo_version_arg) -#define DRM_IOCTL_MM_INFO DRM_IOWR(0xd7, struct drm_mm_info_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_SETCRTC DRM_IOWR(0xA2, struct drm_mode_crtc) +#define DRM_IOCTL_MODE_CURSOR DRM_IOWR(0xA3, struct drm_mode_cursor) +#define DRM_IOCTL_MODE_GETGAMMA DRM_IOWR(0xA4, struct drm_mode_crtc_lut) +#define DRM_IOCTL_MODE_SETGAMMA DRM_IOWR(0xA5, struct drm_mode_crtc_lut) + +#define DRM_IOCTL_MODE_GETENCODER DRM_IOWR(0xA6, struct drm_mode_get_encoder) + +#define DRM_IOCTL_MODE_GETCONNECTOR DRM_IOWR(0xA7, struct drm_mode_get_connector) +#define DRM_IOCTL_MODE_ATTACHMODE DRM_IOWR(0xA8, struct drm_mode_mode_cmd) +#define DRM_IOCTL_MODE_DETACHMODE DRM_IOWR(0xA9, struct drm_mode_mode_cmd) +#define DRM_IOCTL_MODE_GETPROPERTY DRM_IOWR(0xAA, struct drm_mode_get_property) +#define DRM_IOCTL_MODE_SETPROPERTY DRM_IOWR(0xAB, struct drm_mode_connector_set_property) +#define DRM_IOCTL_MODE_GETPROPBLOB DRM_IOWR(0xAC, struct drm_mode_get_blob) + +#define DRM_IOCTL_MODE_GETFB DRM_IOWR(0xAD, struct drm_mode_fb_cmd) +#define DRM_IOCTL_MODE_ADDFB DRM_IOWR(0xAE, struct drm_mode_fb_cmd) +#define DRM_IOCTL_MODE_RMFB DRM_IOWR(0xAF, uint32_t) +#define DRM_IOCTL_MODE_REPLACEFB DRM_IOWR(0xB0, struct drm_mode_fb_cmd) /*@}*/ @@ -1136,11 +823,6 @@ typedef struct drm_agp_binding drm_agp_binding_t; typedef struct drm_agp_info drm_agp_info_t; typedef struct drm_scatter_gather drm_scatter_gather_t; typedef struct drm_set_version drm_set_version_t; - -typedef struct drm_fence_arg drm_fence_arg_t; -typedef struct drm_mm_type_arg drm_mm_type_arg_t; -typedef struct drm_mm_init_arg drm_mm_init_arg_t; -typedef enum drm_bo_type drm_bo_type_t; #endif #endif diff --git a/shared-core/drm_mode.h b/shared-core/drm_mode.h new file mode 100644 index 00000000..601d2bd8 --- /dev/null +++ b/shared-core/drm_mode.h @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> + * Copyright (c) 2007 Jakob Bornecrantz <wallbraker@gmail.com> + * Copyright (c) 2008 Red Hat Inc. + * Copyright (c) 2007-2008 Tungsten Graphics, Inc., Cedar Park, TX., USA + * Copyright (c) 2007-2008 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 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. + */ + +#ifndef _DRM_MODE_H +#define _DRM_MODE_H + +#if !defined(__KERNEL__) && !defined(_KERNEL) +#include <stdint.h> +#else +#include <linux/kernel.h> +#endif + +#define DRM_DISPLAY_INFO_LEN 32 +#define DRM_CONNECTOR_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) + +/* Video mode flags */ +/* bit compatible with the xorg definitions. */ +#define DRM_MODE_FLAG_PHSYNC (1<<0) +#define DRM_MODE_FLAG_NHSYNC (1<<1) +#define DRM_MODE_FLAG_PVSYNC (1<<2) +#define DRM_MODE_FLAG_NVSYNC (1<<3) +#define DRM_MODE_FLAG_INTERLACE (1<<4) +#define DRM_MODE_FLAG_DBLSCAN (1<<5) +#define DRM_MODE_FLAG_CSYNC (1<<6) +#define DRM_MODE_FLAG_PCSYNC (1<<7) +#define DRM_MODE_FLAG_NCSYNC (1<<8) +#define DRM_MODE_FLAG_HSKEW (1<<9) /* hskew provided */ +#define DRM_MODE_FLAG_BCAST (1<<10) +#define DRM_MODE_FLAG_PIXMUX (1<<11) +#define DRM_MODE_FLAG_DBLCLK (1<<12) +#define DRM_MODE_FLAG_CLKDIV2 (1<<13) + +/* DPMS flags */ +/* bit compatible with the xorg definitions. */ +#define DRM_MODE_DPMS_ON 0 +#define DRM_MODE_DPMS_STANDBY 1 +#define DRM_MODE_DPMS_SUSPEND 2 +#define DRM_MODE_DPMS_OFF 3 + +/* Scaling mode options */ +#define DRM_MODE_SCALE_NON_GPU 0 +#define DRM_MODE_SCALE_FULLSCREEN 1 +#define DRM_MODE_SCALE_NO_SCALE 2 +#define DRM_MODE_SCALE_ASPECT 3 + +/* Dithering mode options */ +#define DRM_MODE_DITHERING_OFF 0 +#define DRM_MODE_DITHERING_ON 1 + +struct drm_mode_modeinfo { + uint32_t clock; + uint16_t hdisplay, hsync_start, hsync_end, htotal, hskew; + uint16_t vdisplay, vsync_start, vsync_end, vtotal, vscan; + + uint32_t vrefresh; /* vertical refresh * 1000 */ + + uint32_t flags; + uint32_t type; + char name[DRM_DISPLAY_MODE_LEN]; +}; + +struct drm_mode_card_res { + uint64_t fb_id_ptr; + uint64_t crtc_id_ptr; + uint64_t connector_id_ptr; + uint64_t encoder_id_ptr; + uint32_t count_fbs; + uint32_t count_crtcs; + uint32_t count_connectors; + uint32_t count_encoders; + uint32_t min_width, max_width; + uint32_t min_height, max_height; +}; + +struct drm_mode_crtc { + uint64_t set_connectors_ptr; + uint32_t count_connectors; + + uint32_t crtc_id; /**< Id */ + uint32_t fb_id; /**< Id of framebuffer */ + + uint32_t x, y; /**< Position on the frameuffer */ + + uint32_t gamma_size; + uint32_t mode_valid; + struct drm_mode_modeinfo mode; +}; + +#define DRM_MODE_ENCODER_NONE 0 +#define DRM_MODE_ENCODER_DAC 1 +#define DRM_MODE_ENCODER_TMDS 2 +#define DRM_MODE_ENCODER_LVDS 3 +#define DRM_MODE_ENCODER_TVDAC 4 + +struct drm_mode_get_encoder { + uint32_t encoder_id; + uint32_t encoder_type; + + uint32_t crtc_id; /**< Id of crtc */ + + uint32_t possible_crtcs; + uint32_t possible_clones; +}; + +/* This is for connectors with multiple signal types. */ +/* Try to match DRM_MODE_CONNECTOR_X as closely as possible. */ +#define DRM_MODE_SUBCONNECTOR_Automatic 0 +#define DRM_MODE_SUBCONNECTOR_Unknown 0 +#define DRM_MODE_SUBCONNECTOR_DVID 3 +#define DRM_MODE_SUBCONNECTOR_DVIA 4 +#define DRM_MODE_SUBCONNECTOR_Composite 5 +#define DRM_MODE_SUBCONNECTOR_SVIDEO 6 +#define DRM_MODE_SUBCONNECTOR_Component 8 + +#define DRM_MODE_CONNECTOR_Unknown 0 +#define DRM_MODE_CONNECTOR_VGA 1 +#define DRM_MODE_CONNECTOR_DVII 2 +#define DRM_MODE_CONNECTOR_DVID 3 +#define DRM_MODE_CONNECTOR_DVIA 4 +#define DRM_MODE_CONNECTOR_Composite 5 +#define DRM_MODE_CONNECTOR_SVIDEO 6 +#define DRM_MODE_CONNECTOR_LVDS 7 +#define DRM_MODE_CONNECTOR_Component 8 +#define DRM_MODE_CONNECTOR_9PinDIN 9 +#define DRM_MODE_CONNECTOR_DisplayPort 10 +#define DRM_MODE_CONNECTOR_HDMIA 11 +#define DRM_MODE_CONNECTOR_HDMIB 12 + +struct drm_mode_get_connector { + + uint64_t encoders_ptr; + uint64_t modes_ptr; + uint64_t props_ptr; + uint64_t prop_values_ptr; + + uint32_t count_modes; + uint32_t count_props; + uint32_t count_encoders; + + uint32_t encoder_id; /**< Current Encoder */ + uint32_t connector_id; /**< Id */ + uint32_t connector_type; + uint32_t connector_type_id; + + uint32_t connection; + uint32_t mm_width, mm_height; /**< HxW in millimeters */ + uint32_t subpixel; +}; + +#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; + 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 */ + + uint32_t prop_id; + uint32_t flags; + char name[DRM_PROP_NAME_LEN]; + + uint32_t count_values; + uint32_t count_enum_blobs; +}; + +struct drm_mode_connector_set_property { + uint64_t value; + uint32_t prop_id; + uint32_t connector_id; +}; + +struct drm_mode_get_blob { + uint32_t blob_id; + uint32_t length; + uint64_t data; +}; + +struct drm_mode_fb_cmd { + uint32_t fb_id; + uint32_t width, height; + uint32_t pitch; + uint32_t bpp; + uint32_t depth; + /* driver specific handle */ + uint32_t handle; +}; + +struct drm_mode_mode_cmd { + uint32_t connector_id; + struct drm_mode_modeinfo mode; +}; + +#define DRM_MODE_CURSOR_BO (1<<0) +#define DRM_MODE_CURSOR_MOVE (1<<1) + +/* + * depending on the value in flags diffrent members are used. + * + * CURSOR_BO uses + * crtc + * width + * height + * handle - if 0 turns the cursor of + * + * CURSOR_MOVE uses + * crtc + * x + * y + */ +struct drm_mode_cursor { + uint32_t flags; + uint32_t crtc_id; + int32_t x; + int32_t y; + uint32_t width; + uint32_t height; + /* driver specific handle */ + uint32_t handle; +}; + +struct drm_mode_crtc_lut { + uint32_t crtc_id; + uint32_t gamma_size; + + /* pointers to arrays */ + uint64_t red; + uint64_t green; + uint64_t blue; +}; + +#endif diff --git a/shared-core/drm_pciids.txt b/shared-core/drm_pciids.txt index 5ca04160..aee3167b 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 29a0a1aa..6350d21a 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(PRB0_HEAD) & HEAD_ADDR; u32 acthd_reg = IS_I965G(dev) ? ACTHD_I965 : ACTHD; u32 last_acthd = I915_READ(acthd_reg); @@ -69,52 +69,6 @@ int i915_wait_ring(struct drm_device * dev, int n, const char *caller) return -EBUSY; } -int i915_init_hardware_status(struct drm_device *dev) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - drm_dma_handle_t *dmah; - - /* Program Hardware Status Page */ -#ifdef __FreeBSD__ - DRM_UNLOCK(); -#endif - dmah = drm_pci_alloc(dev, PAGE_SIZE, PAGE_SIZE, 0xffffffff); -#ifdef __FreeBSD__ - DRM_LOCK(); -#endif - if (!dmah) { - DRM_ERROR("Can not allocate hardware status page\n"); - return -ENOMEM; - } - - dev_priv->status_page_dmah = dmah; - dev_priv->hw_status_page = dmah->vaddr; - dev_priv->dma_status_page = dmah->busaddr; - - memset(dev_priv->hw_status_page, 0, PAGE_SIZE); - - I915_WRITE(0x02080, dev_priv->dma_status_page); - DRM_DEBUG("Enabled hardware status page\n"); - return 0; -} - -void i915_free_hardware_status(struct drm_device *dev) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - 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); - } -} - #if I915_RING_VALIDATE /** * Validate the cached ring tail value @@ -125,7 +79,7 @@ void i915_free_hardware_status(struct drm_device *dev) */ void i915_ring_validate(struct drm_device *dev, const char *func, int line) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; drm_i915_ring_buffer_t *ring = &(dev_priv->ring); u32 tail = I915_READ(PRB0_TAIL) & HEAD_ADDR; u32 head = I915_READ(PRB0_HEAD) & HEAD_ADDR; @@ -143,8 +97,13 @@ void i915_ring_validate(struct drm_device *dev, const char *func, int line) 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); + + /* we should never lose context on the ring with modesetting + * as we don't expose it to userspace */ + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return; ring->head = I915_READ(PRB0_HEAD) & HEAD_ADDR; ring->tail = I915_READ(PRB0_TAIL) & TAIL_ADDR; @@ -153,9 +112,13 @@ 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; + struct drm_i915_private *dev_priv = dev->dev_private; + + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return 0; + /* 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. @@ -163,20 +126,18 @@ static int i915_dma_cleanup(struct drm_device * dev) if (dev->irq_enabled) 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 (I915_NEED_GFX_HWS(dev)) - i915_free_hardware_status(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; + dev_priv->ring.Size = 0; + } return 0; } -#if defined(I915_HAVE_BUFFER) +#if defined(DRI2) #define DRI2_SAREA_BLOCK_TYPE(b) ((b) >> 16) #define DRI2_SAREA_BLOCK_SIZE(b) ((b) & 0xffff) #define DRI2_SAREA_BLOCK_NEXT(p) \ @@ -192,7 +153,7 @@ setup_dri2_sarea(struct drm_device * dev, struct drm_file *file_priv, drm_i915_init_t * init) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; int ret; unsigned int *p, *end, *next; @@ -220,8 +181,8 @@ setup_dri2_sarea(struct drm_device * dev, while (p < end && DRI2_SAREA_BLOCK_TYPE(*p) != DRI2_SAREA_BLOCK_END) { switch (DRI2_SAREA_BLOCK_TYPE(*p)) { case DRI2_SAREA_BLOCK_LOCK: - dev->lock.hw_lock = (void *) (p + 1); - dev->sigdata.lock = dev->lock.hw_lock; + dev->primary->master->lock.hw_lock = (void *) (p + 1); + dev->sigdata.lock = dev->primary->master->lock.hw_lock; break; } next = DRI2_SAREA_BLOCK_NEXT(p); @@ -241,40 +202,28 @@ static int i915_initialize(struct drm_device * dev, struct drm_file *file_priv, drm_i915_init_t * init) { - drm_i915_private_t *dev_priv = dev->dev_private; -#if defined(I915_HAVE_BUFFER) - int ret; -#endif - dev_priv->sarea = drm_getsarea(dev); - if (!dev_priv->sarea) { - DRM_ERROR("can not find sarea!\n"); - i915_dma_cleanup(dev); - return -EINVAL; - } - -#ifdef I915_HAVE_BUFFER - dev_priv->max_validate_buffers = I915_MAX_VALIDATE_BUFFERS; -#endif + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; - if (init->sarea_priv_offset) - dev_priv->sarea_priv = (drm_i915_sarea_t *) - ((u8 *) dev_priv->sarea->handle + - init->sarea_priv_offset); - else { - /* No sarea_priv for you! */ - dev_priv->sarea_priv = NULL; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) { + if (init->mmio_offset != 0) + dev_priv->mmio_map = drm_core_findmap(dev, init->mmio_offset); + if (!dev_priv->mmio_map) { + i915_dma_cleanup(dev); + DRM_ERROR("can not find mmio map!\n"); + return -EINVAL; + } } + if (init->ring_size != 0) { dev_priv->ring.Size = init->ring_size; dev_priv->ring.tail_mask = dev_priv->ring.Size - 1; - dev_priv->ring.map.offset = init->ring_start; dev_priv->ring.map.size = init->ring_size; dev_priv->ring.map.type = 0; dev_priv->ring.map.flags = 0; dev_priv->ring.map.mtrr = 0; - drm_core_ioremap(&dev_priv->ring.map, dev); if (dev_priv->ring.map.handle == NULL) { @@ -283,14 +232,11 @@ static int i915_initialize(struct drm_device * dev, " ring buffer\n"); return -ENOMEM; } - dev_priv->ring.virtual_start = dev_priv->ring.map.handle; } dev_priv->cpp = init->cpp; - - if (dev_priv->sarea_priv) - dev_priv->sarea_priv->pf_current_page = 0; + master_priv->sarea_priv->pf_current_page = 0; /* We are using separate values as placeholders for mechanisms for * private backbuffer/depthbuffer usage. @@ -304,33 +250,28 @@ static int i915_initialize(struct drm_device * dev, */ dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A; -#ifdef I915_HAVE_BUFFER - mutex_init(&dev_priv->cmdbuf_mutex); -#endif -#if defined(I915_HAVE_BUFFER) +#ifdef DRI2 if (init->func == I915_INIT_DMA2) { - ret = setup_dri2_sarea(dev, file_priv, init); + int ret = setup_dri2_sarea(dev, file_priv, init); if (ret) { i915_dma_cleanup(dev); DRM_ERROR("could not set up dri2 sarea\n"); return ret; } } -#endif +#endif /* DRI2 */ return 0; } 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"); - if (!dev_priv->sarea) { - DRM_ERROR("can not find sarea!\n"); - return -EINVAL; - } + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return 0; if (dev_priv->ring.map.handle == NULL) { DRM_ERROR("can not ioremap virtual address for" @@ -346,9 +287,9 @@ static int i915_dma_resume(struct drm_device * dev) DRM_DEBUG("hw status page @ %p\n", dev_priv->hw_status_page); if (dev_priv->status_gfx_addr != 0) - I915_WRITE(0x02080, dev_priv->status_gfx_addr); + I915_WRITE(HWS_PGA, dev_priv->status_gfx_addr); else - I915_WRITE(0x02080, dev_priv->dma_status_page); + I915_WRITE(HWS_PGA, dev_priv->dma_status_page); DRM_DEBUG("Enabled hardware status page\n"); return 0; @@ -357,7 +298,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) { @@ -458,7 +399,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; @@ -499,7 +440,7 @@ 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; @@ -540,7 +481,8 @@ 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; + struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; RING_LOCALS; if (++dev_priv->counter > BREADCRUMB_MASK) { @@ -548,8 +490,7 @@ void i915_emit_breadcrumb(struct drm_device *dev) DRM_DEBUG("Breadcrumb counter wrapped around\n"); } - if (dev_priv->sarea_priv) - dev_priv->sarea_priv->last_enqueue = dev_priv->counter; + master_priv->sarea_priv->last_enqueue = dev_priv->counter; BEGIN_LP_RING(4); OUT_RING(MI_STORE_DWORD_INDEX); @@ -562,7 +503,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 = MI_FLUSH; RING_LOCALS; @@ -582,11 +523,8 @@ 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; -#endif int nbox = cmd->num_cliprects; int i = 0, count, ret; @@ -613,17 +551,13 @@ static int i915_dispatch_cmdbuffer(struct drm_device * dev, } i915_emit_breadcrumb(dev); -#ifdef I915_HAVE_FENCE - if (unlikely((dev_priv->counter & 0xFF) == 0)) - drm_fence_flush_old(dev, 0, dev_priv->counter); -#endif return 0; } 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; @@ -667,47 +601,44 @@ int i915_dispatch_batchbuffer(struct drm_device * dev, } i915_emit_breadcrumb(dev); -#ifdef I915_HAVE_FENCE - if (unlikely((dev_priv->counter & 0xFF) == 0)) - drm_fence_flush_old(dev, 0, dev_priv->counter); -#endif return 0; } 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; + struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; u32 num_pages, current_page, next_page, dspbase; int shift = 2 * plane, x, y; RING_LOCALS; /* Calculate display base offset */ - num_pages = dev_priv->sarea_priv->third_handle ? 3 : 2; - current_page = (dev_priv->sarea_priv->pf_current_page >> shift) & 0x3; + num_pages = master_priv->sarea_priv->third_handle ? 3 : 2; + current_page = (master_priv->sarea_priv->pf_current_page >> shift) & 0x3; next_page = (current_page + 1) % num_pages; switch (next_page) { default: case 0: - dspbase = dev_priv->sarea_priv->front_offset; + dspbase = master_priv->sarea_priv->front_offset; break; case 1: - dspbase = dev_priv->sarea_priv->back_offset; + dspbase = master_priv->sarea_priv->back_offset; break; case 2: - dspbase = dev_priv->sarea_priv->third_offset; + dspbase = master_priv->sarea_priv->third_offset; break; } if (plane == 0) { - x = dev_priv->sarea_priv->planeA_x; - y = dev_priv->sarea_priv->planeA_y; + x = master_priv->sarea_priv->planeA_x; + y = master_priv->sarea_priv->planeA_y; } else { - x = dev_priv->sarea_priv->planeB_x; - y = dev_priv->sarea_priv->planeB_y; + x = master_priv->sarea_priv->planeB_x; + y = master_priv->sarea_priv->planeB_y; } - dspbase += (y * dev_priv->sarea_priv->pitch + x) * dev_priv->cpp; + dspbase += (y * master_priv->sarea_priv->pitch + x) * dev_priv->cpp; DRM_DEBUG("plane=%d current_page=%d dspbase=0x%x\n", plane, current_page, dspbase); @@ -718,21 +649,21 @@ static void i915_do_dispatch_flip(struct drm_device * dev, int plane, int sync) MI_WAIT_FOR_PLANE_A_FLIP))); OUT_RING(CMD_OP_DISPLAYBUFFER_INFO | (sync ? 0 : ASYNC_FLIP) | (plane ? DISPLAY_PLANE_B : DISPLAY_PLANE_A)); - OUT_RING(dev_priv->sarea_priv->pitch * dev_priv->cpp); + OUT_RING(master_priv->sarea_priv->pitch * dev_priv->cpp); OUT_RING(dspbase); ADVANCE_LP_RING(); - dev_priv->sarea_priv->pf_current_page &= ~(0x3 << shift); - dev_priv->sarea_priv->pf_current_page |= next_page << shift; + master_priv->sarea_priv->pf_current_page &= ~(0x3 << shift); + master_priv->sarea_priv->pf_current_page |= next_page << shift; } void i915_dispatch_flip(struct drm_device * dev, int planes, int sync) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; int i; DRM_DEBUG("planes=0x%x pfCurrentPage=%d\n", - planes, dev_priv->sarea_priv->pf_current_page); + planes, master_priv->sarea_priv->pf_current_page); i915_emit_mi_flush(dev, MI_READ_FLUSH | MI_EXE_FLUSH); @@ -741,15 +672,11 @@ void i915_dispatch_flip(struct drm_device * dev, int planes, int sync) i915_do_dispatch_flip(dev, i, sync); i915_emit_breadcrumb(dev); -#ifdef I915_HAVE_FENCE - if (unlikely(!sync && ((dev_priv->counter & 0xFF) == 0))) - drm_fence_flush_old(dev, 0, dev_priv->counter); -#endif } 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; int ret; i915_kernel_lost_context(dev); @@ -777,9 +704,10 @@ 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; + struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *) - dev_priv->sarea_priv; + master_priv->sarea_priv; drm_i915_batchbuffer_t *batch = data; int ret; @@ -807,10 +735,11 @@ 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 *) - dev_priv->sarea_priv; - drm_i915_cmdbuffer_t *cmdbuf = data; + struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; + struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; + struct drm_i915_sarea *sarea_priv = (struct drm_i915_sarea *) + master_priv->sarea_priv; + struct drm_i915_cmdbuffer *cmdbuf = data; int ret; DRM_DEBUG("i915 cmdbuffer, buf %p sz %d cliprects %d\n", @@ -842,21 +771,22 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data, #define DRM_DEBUG_RELOCATION 0 #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_master_private *master_priv = dev->primary->master->driver_priv; + int i, planes, num_pages; DRM_DEBUG("\n"); - - for (i = 0, planes = 0; i < 2; i++) - if (dev_priv->sarea_priv->pf_current_page & (0x3 << (2 * i))) { - dev_priv->sarea_priv->pf_current_page = - (dev_priv->sarea_priv->pf_current_page & + num_pages = master_priv->sarea_priv->third_handle ? 3 : 2; + for (i = 0, planes = 0; i < 2; i++) { + if (master_priv->sarea_priv->pf_current_page & (0x3 << (2 * i))) { + master_priv->sarea_priv->pf_current_page = + (master_priv->sarea_priv->pf_current_page & ~(0x3 << (2 * i))) | ((num_pages - 1) << (2 * i)); planes |= 1 << i; } + } if (planes) i915_dispatch_flip(dev, planes, 0); @@ -866,7 +796,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"); @@ -888,8 +818,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) { @@ -933,7 +863,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) { @@ -963,6 +893,26 @@ drm_i915_mmio_entry_t mmio_table[] = { I915_MMIO_MAY_READ|I915_MMIO_MAY_WRITE, 0x2350, 8 + }, + [MMIO_REGS_DOVSTA] = { + I915_MMIO_MAY_READ, + 0x30008, + 1 + }, + [MMIO_REGS_GAMMA] = { + I915_MMIO_MAY_READ|I915_MMIO_MAY_WRITE, + 0x30010, + 6 + }, + [MMIO_REGS_FENCE] = { + I915_MMIO_MAY_READ|I915_MMIO_MAY_WRITE, + 0x2000, + 8 + }, + [MMIO_REGS_FENCE_NEW] = { + I915_MMIO_MAY_READ|I915_MMIO_MAY_WRITE, + 0x3000, + 16 } }; @@ -972,8 +922,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; @@ -1018,7 +968,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 (!I915_NEED_GFX_HWS(dev)) @@ -1028,6 +978,10 @@ static int i915_set_status_page(struct drm_device *dev, void *data, DRM_ERROR("called with no initialization\n"); return -EINVAL; } + + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return 0; + DRM_DEBUG("set status page addr 0x%08x\n", (u32)hws->addr); dev_priv->status_gfx_addr = hws->addr & (0x1ffff<<12); @@ -1046,180 +1000,24 @@ static int i915_set_status_page(struct drm_device *dev, void *data, " G33 hw status page\n"); return -ENOMEM; } + dev_priv->hw_status_page = dev_priv->hws_map.handle; memset(dev_priv->hw_status_page, 0, PAGE_SIZE); I915_WRITE(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; - 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; - dev_priv->dev = dev; - - /* 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 I915_HAVE_GEM - i915_gem_load(dev); -#endif - DRM_SPININIT(&dev_priv->swaps_lock, "swap"); - DRM_SPININIT(&dev_priv->user_irq_lock, "userirq"); - -#ifdef __linux__ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) - intel_init_chipset_flush_compat(dev); -#endif -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25) - intel_opregion_init(dev); -#endif -#endif - - /* Init HWS */ - if (!I915_NEED_GFX_HWS(dev)) { - ret = i915_init_hardware_status(dev); - if(ret) - return ret; - } - - return ret; -} - -int i915_driver_unload(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - - i915_free_hardware_status(dev); - - drm_rmmap(dev, dev_priv->mmio_map); - - DRM_SPINUNINIT(&dev_priv->swaps_lock); - DRM_SPINUNINIT(&dev_priv->user_irq_lock); - -#ifdef __linux__ -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25) - intel_opregion_free(dev); -#endif -#endif - - drm_free(dev->dev_private, sizeof(drm_i915_private_t), - DRM_MEM_DRIVER); - dev->dev_private = NULL; - -#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; - - /* agp off can use this to get called before dev_priv */ - if (!dev_priv) - return; - -#ifdef I915_HAVE_BUFFER - if (dev_priv->val_bufs) { - vfree(dev_priv->val_bufs); - dev_priv->val_bufs = NULL; - } -#endif -#ifdef I915_HAVE_GEM - i915_gem_lastclose(dev); -#endif - if (drm_getsarea(dev) && dev_priv->sarea_priv) - i915_do_cleanup_pageflip(dev); - if (dev_priv->sarea_priv) - dev_priv->sarea_priv = NULL; - if (dev_priv->agp_heap) - i915_mem_takedown(&(dev_priv->agp_heap)); -#if defined(I915_HAVE_BUFFER) - if (dev_priv->sarea_kmap.virtual) { - drm_bo_kunmap(&dev_priv->sarea_kmap); - dev_priv->sarea_kmap.virtual = NULL; - dev->lock.hw_lock = NULL; - dev->sigdata.lock = NULL; - } - - if (dev_priv->sarea_bo) { - mutex_lock(&dev->struct_mutex); - drm_bo_usage_deref_locked(&dev_priv->sarea_bo); - mutex_unlock(&dev->struct_mutex); - dev_priv->sarea_bo = NULL; - } -#endif - i915_dma_cleanup(dev); -} - -int i915_driver_open(struct drm_device *dev, struct drm_file *file_priv) -{ - struct drm_i915_file_private *i915_file_priv; - - DRM_DEBUG("\n"); - i915_file_priv = (struct drm_i915_file_private *) - drm_alloc(sizeof(*i915_file_priv), DRM_MEM_FILES); - - if (!i915_file_priv) - return -ENOMEM; - - file_priv->driver_priv = i915_file_priv; - - i915_file_priv->mm.last_gem_seqno = 0; - i915_file_priv->mm.last_gem_throttle_seqno = 0; - - return 0; -} - -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); -} - -void i915_driver_postclose(struct drm_device *dev, struct drm_file *file_priv) -{ - struct drm_i915_file_private *i915_file_priv = file_priv->driver_priv; - - drm_free(i915_file_priv, sizeof(*i915_file_priv), DRM_MEM_FILES); -} - 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_INIT, i915_dma_init, DRM_AUTH|DRM_MASTER), DRM_IOCTL_DEF(DRM_I915_FLUSH, i915_flush_ioctl, DRM_AUTH), DRM_IOCTL_DEF(DRM_I915_FLIP, i915_flip_bufs, DRM_AUTH), DRM_IOCTL_DEF(DRM_I915_BATCHBUFFER, i915_batchbuffer, DRM_AUTH), DRM_IOCTL_DEF(DRM_I915_IRQ_EMIT, i915_irq_emit, DRM_AUTH), DRM_IOCTL_DEF(DRM_I915_IRQ_WAIT, i915_irq_wait, DRM_AUTH), DRM_IOCTL_DEF(DRM_I915_GETPARAM, i915_getparam, DRM_AUTH), - DRM_IOCTL_DEF(DRM_I915_SETPARAM, i915_setparam, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_I915_SETPARAM, i915_setparam, DRM_AUTH|DRM_MASTER), DRM_IOCTL_DEF(DRM_I915_ALLOC, i915_mem_alloc, DRM_AUTH), DRM_IOCTL_DEF(DRM_I915_FREE, i915_mem_free, DRM_AUTH), DRM_IOCTL_DEF(DRM_I915_INIT_HEAP, i915_mem_init_heap, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), @@ -1271,10 +1069,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 976ff187..b05fc737 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, @@ -259,7 +259,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 */ @@ -363,6 +363,10 @@ typedef struct drm_i915_vblank_swap { #define MMIO_REGS_CL_INVOCATION_COUNT 6 #define MMIO_REGS_PS_INVOCATION_COUNT 7 #define MMIO_REGS_PS_DEPTH_COUNT 8 +#define MMIO_REGS_DOVSTA 9 +#define MMIO_REGS_GAMMA 10 +#define MMIO_REGS_FENCE 11 +#define MMIO_REGS_FENCE_NEW 12 typedef struct drm_i915_mmio_entry { unsigned int flag; @@ -380,58 +384,6 @@ typedef struct drm_i915_hws_addr { uint64_t addr; } drm_i915_hws_addr_t; -/* - * Relocation header is 4 uint32_ts - * 0 - 32 bit reloc count - * 1 - 32-bit relocation type - * 2-3 - 64-bit user buffer handle ptr for another list of relocs. - */ -#define I915_RELOC_HEADER 4 - -/* - * type 0 relocation has 4-uint32_t stride - * 0 - offset into buffer - * 1 - delta to add in - * 2 - buffer handle - * 3 - reserved (for optimisations later). - */ -/* - * type 1 relocation has 4-uint32_t stride. - * Hangs off the first item in the op list. - * Performed after all valiations are done. - * Try to group relocs into the same relocatee together for - * performance reasons. - * 0 - offset into buffer - * 1 - delta to add in - * 2 - buffer index in op list. - * 3 - relocatee index in op list. - */ -#define I915_RELOC_TYPE_0 0 -#define I915_RELOC0_STRIDE 4 -#define I915_RELOC_TYPE_1 1 -#define I915_RELOC1_STRIDE 4 - - -struct drm_i915_op_arg { - uint64_t next; - uint64_t reloc_ptr; - int handled; - unsigned int pad64; - union { - struct drm_bo_op_req req; - struct drm_bo_arg_rep rep; - } d; - -}; - -struct drm_i915_execbuffer { - uint64_t ops_list; - uint32_t num_buffers; - struct drm_i915_batchbuffer batch; - drm_context_t context; /* for lockless use in the future */ - struct drm_fence_arg fence_arg; -}; - struct drm_i915_gem_init { /** * Beginning offset in the GTT to be managed by the DRM memory diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index 48fd30d7..c420f3c9 100644 --- a/shared-core/i915_drv.h +++ b/shared-core/i915_drv.h @@ -33,6 +33,8 @@ /* General customization: */ +#include "intel_bios.h" + #define DRIVER_AUTHOR "Tungsten Graphics, Inc." #define DRIVER_NAME "i915" @@ -40,7 +42,6 @@ #define DRIVER_DATE "20080730" #if defined(__linux__) -#define I915_HAVE_FENCE #define I915_HAVE_BUFFER #define I915_HAVE_GEM #endif @@ -61,7 +62,7 @@ * 1.12: TTM relocation optimization */ #define DRIVER_MAJOR 1 -#if defined(I915_HAVE_FENCE) && defined(I915_HAVE_BUFFER) +#if defined(I915_HAVE_BUFFER) #define DRIVER_MINOR 13 #else #define DRIVER_MINOR 6 @@ -86,7 +87,7 @@ struct drm_i915_validate_buffer; #define WATCH_INACTIVE 0 #define WATCH_PWRITE 0 -typedef struct _drm_i915_ring_buffer { +struct drm_i915_ring_buffer { int tail_mask; unsigned long Size; u8 *virtual_start; @@ -95,7 +96,7 @@ typedef struct _drm_i915_ring_buffer { int space; drm_local_map_t map; struct drm_gem_object *ring_obj; -} drm_i915_ring_buffer_t; +}; struct mem_block { struct mem_block *next; @@ -105,13 +106,15 @@ 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; + struct drm_minor *minor; +}; + #ifdef __linux__ struct opregion_header; @@ -129,20 +132,26 @@ struct intel_opregion { }; #endif -typedef struct drm_i915_private { - struct drm_device *dev; - +struct drm_i915_master_private { drm_local_map_t *sarea; + struct drm_i915_sarea *sarea_priv; +}; + +struct drm_i915_private { + struct drm_device *dev; + drm_local_map_t *mmio_map; - drm_i915_sarea_t *sarea_priv; - drm_i915_ring_buffer_t ring; + unsigned long mmiobase; + unsigned long mmiolen; - drm_dma_handle_t *status_page_dmah; + struct drm_i915_ring_buffer ring; + + struct drm_dma_handle *status_page_dmah; void *hw_status_page; dma_addr_t dma_status_page; uint32_t counter; - unsigned int status_gfx_addr; + uint32_t status_gfx_addr; drm_local_map_t hws_map; struct drm_gem_object *hws_obj; @@ -161,29 +170,44 @@ typedef struct drm_i915_private { int fence_irq_on; uint32_t irq_mask_reg; int irq_enabled; + struct workqueue_struct *wq; + + bool cursor_needs_physical; + + struct drm_mm vram; -#ifdef I915_HAVE_FENCE - uint32_t flush_sequence; - uint32_t flush_flags; - uint32_t flush_pending; - uint32_t saved_flush_status; -#endif #ifdef I915_HAVE_BUFFER void *agp_iomap; unsigned int max_validate_buffers; struct mutex cmdbuf_mutex; + u32 stolen_base; struct drm_i915_validate_buffer *val_bufs; #endif DRM_SPINTYPE swaps_lock; - drm_i915_vbl_swap_t vbl_swaps; + struct drm_i915_vbl_swap vbl_swaps; unsigned int swaps_pending; -#if defined(I915_HAVE_BUFFER) + + /* LVDS info */ + int backlight_duty_cycle; /* restore backlight to this value */ + bool panel_wants_dither; + struct drm_display_mode *panel_fixed_mode; + struct drm_display_mode *vbt_mode; /* if any */ + +#if defined(I915_HAVE_BUFFER) && defined(DRI2) /* DRI2 sarea */ - struct drm_buffer_object *sarea_bo; - struct drm_bo_kmap_obj sarea_kmap; + struct drm_gem_object *sarea_object; + struct drm_bo_kmap_obj sarea_kmap; #endif + /* Feature bits from the VBIOS */ + int int_tv_support:1; + int lvds_dither:1; + int lvds_vbt:1; + int int_crt_support:1; + + struct work_struct user_interrupt_task; + #ifdef __linux__ struct intel_opregion opregion; #endif @@ -359,7 +383,7 @@ typedef struct drm_i915_private { /** Bit 6 swizzling required for Y tiling */ uint32_t bit_6_swizzle_y; } mm; -} drm_i915_private_t; +}; struct drm_i915_file_private { struct { @@ -455,10 +479,12 @@ struct drm_i915_gem_request { extern struct drm_ioctl_desc i915_ioctls[]; extern int i915_max_ioctl; +extern int i915_master_create(struct drm_device *dev, struct drm_master *master); +extern void i915_master_destroy(struct drm_device *dev, struct drm_master *master); /* 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 int i915_driver_open(struct drm_device *dev, struct drm_file *file_priv); extern void i915_driver_preclose(struct drm_device *dev, @@ -472,10 +498,11 @@ 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); extern int i915_dispatch_batchbuffer(struct drm_device * dev, drm_i915_batchbuffer_t * batch); extern int i915_quiescent(struct drm_device *dev); -extern int i915_init_hardware_status(struct drm_device *dev); extern void i915_free_hardware_status(struct drm_device *dev); int i915_emit_box(struct drm_device * dev, @@ -497,14 +524,15 @@ extern int i915_vblank_pipe_set(struct drm_device *dev, void *data, extern int i915_vblank_pipe_get(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int i915_emit_irq(struct drm_device * dev); +extern void i915_enable_interrupt (struct drm_device *dev); extern int i915_wait_irq(struct drm_device * dev, int irq_nr); extern int i915_enable_vblank(struct drm_device *dev, int crtc); extern void i915_disable_vblank(struct drm_device *dev, int crtc); extern u32 i915_get_vblank_counter(struct drm_device *dev, int crtc); extern int i915_vblank_swap(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern void i915_user_irq_on(drm_i915_private_t *dev_priv); -extern void i915_user_irq_off(drm_i915_private_t *dev_priv); +extern void i915_user_irq_on(struct drm_device *dev); +extern void i915_user_irq_off(struct drm_device *dev); /* i915_mem.c */ extern int i915_mem_alloc(struct drm_device *dev, void *data, @@ -524,28 +552,7 @@ extern void i915_mem_release(struct drm_device * dev, extern int i915_save_state(struct drm_device *dev); extern int i915_restore_state(struct drm_device *dev); -#ifdef I915_HAVE_FENCE -/* i915_fence.c */ -extern void i915_fence_handler(struct drm_device *dev); -extern void i915_invalidate_reported_sequence(struct drm_device *dev); - -#endif - #ifdef I915_HAVE_BUFFER -/* i915_buffer.c */ -extern struct drm_ttm_backend *i915_create_ttm_backend_entry(struct drm_device *dev); -extern int i915_fence_type(struct drm_buffer_object *bo, uint32_t *fclass, - uint32_t *type); -extern int i915_invalidate_caches(struct drm_device *dev, uint64_t buffer_flags); -extern int i915_init_mem_type(struct drm_device *dev, uint32_t type, - struct drm_mem_type_manager *man); -extern uint64_t i915_evict_flags(struct drm_buffer_object *bo); -extern int i915_move(struct drm_buffer_object *bo, int evict, - int no_wait, struct drm_bo_mem_reg *new_mem); -void i915_flush_ttm(struct drm_ttm *ttm); -/* i915_execbuf.c */ -int i915_execbuffer(struct drm_device *dev, void *data, - struct drm_file *file_priv); /* i915_gem.c */ int i915_gem_init_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); @@ -589,10 +596,19 @@ void i915_gem_object_unpin(struct drm_gem_object *obj); void i915_gem_lastclose(struct drm_device *dev); uint32_t i915_get_gem_seqno(struct drm_device *dev); void i915_gem_retire_requests(struct drm_device *dev); +int i915_gem_init_ringbuffer(struct drm_device *dev); +void i915_gem_cleanup_ringbuffer(struct drm_device *dev); +int i915_gem_do_init(struct drm_device *dev, unsigned long start, + unsigned long end); +int i915_gem_object_set_domain(struct drm_gem_object *obj, + uint32_t read_domains, + uint32_t write_domain); void i915_gem_retire_work_handler(struct work_struct *work); void i915_gem_clflush_object(struct drm_gem_object *obj); #endif +extern unsigned int i915_fbpercrtc; + /* i915_gem_tiling.c */ void i915_gem_detect_bit_6_swizzle(struct drm_device *dev); @@ -622,6 +638,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)) @@ -638,6 +660,8 @@ typedef boolean_t bool; #define PRIMARY_RINGBUFFER_SIZE (128*1024) +#define PRIMARY_RINGBUFFER_SIZE (128*1024) + #define RING_LOCALS unsigned int outring, ringmask, outcount; \ volatile char *virt; @@ -824,6 +848,7 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); */ #define GFX_INSTR(opcode, flags) ((0x3 << 29) | ((opcode) << 24) | (flags)) +#define GFX_OP_USER_INTERRUPT ((0<<29)|(2<<23)) #define GFX_OP_RASTER_RULES ((0x3<<29)|(0x7<<24)) #define GFX_OP_SCISSOR ((0x3<<29)|(0x1c<<24)|(0x10<<19)) #define SC_UPDATE_SCISSOR (0x1<<1) @@ -1021,6 +1046,12 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); #define DPLLB_LVDS_P2_CLOCK_DIV_7 (1 << 24) /* i915 */ #define DPLL_P2_CLOCK_DIV_MASK 0x03000000 /* i915 */ #define DPLL_FPA01_P1_POST_DIV_MASK 0x00ff0000 /* i915 */ +/* + * Pixel within the current frame is counted in the PIPEAFRAMEPIXEL register + * and is 24 bits wide. + */ +#define PIPE_PIXEL_MASK 0x00ffffff +#define PIPE_PIXEL_SHIFT 0 #define I915_FIFO_UNDERRUN_STATUS (1UL<<31) #define I915_CRC_ERROR_ENABLE (1UL<<29) diff --git a/shared-core/i915_init.c b/shared-core/i915_init.c new file mode 100644 index 00000000..b513379a --- /dev/null +++ b/shared-core/i915_init.c @@ -0,0 +1,552 @@ +/* + * 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" +#include "intel_bios.h" +#include "intel_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; +} + +static int +i915_init_hardware_status(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + drm_dma_handle_t *dmah; + int ret = 0; + +#ifdef __FreeBSD__ + DRM_UNLOCK(); +#endif + dmah = drm_pci_alloc(dev, PAGE_SIZE, PAGE_SIZE, + 0xffffffff); +#ifdef __FreeBSD__ + DRM_LOCK(); +#endif + if (!dmah) { + DRM_ERROR("Can not allocate hardware status page\n"); + ret = -ENOMEM; + goto out; + } + dev_priv->status_page_dmah = dmah; + dev_priv->hw_status_page = dmah->vaddr; + dev_priv->dma_status_page = dmah->busaddr; + + memset(dev_priv->hw_status_page, 0, PAGE_SIZE); + + I915_WRITE(HWS_PGA, dev_priv->dma_status_page); + DRM_DEBUG("hws kernel virt: 0x%p\n", dev_priv->hw_status_page); + +out: + return ret; +} + +void i915_free_hardware_status(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + 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); + } +} + +static int i915_load_modeset_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long agp_size, prealloc_size; + int ret = 0; + + i915_probe_agp(dev->pdev, &agp_size, &prealloc_size); + + /* Basic memrange allocator for stolen space (aka vram) */ + drm_mm_init(&dev_priv->vram, 0, prealloc_size); + /* Let GEM Manage from end of prealloc space to end of aperture */ + i915_gem_do_init(dev, prealloc_size, agp_size); + + ret = i915_gem_init_ringbuffer(dev); + if (ret) + goto out; + + /* Allow hardware batchbuffers unless told otherwise. + */ + dev_priv->allow_batchbuffer = 1; + dev_priv->max_validate_buffers = I915_MAX_VALIDATE_BUFFERS; + mutex_init(&dev_priv->cmdbuf_mutex); + + dev_priv->wq = create_singlethread_workqueue("i915"); + if (dev_priv->wq == 0) { + DRM_DEBUG("Error\n"); + ret = -EINVAL; + goto destroy_ringbuffer; + } + + ret = intel_init_bios(dev); + if (ret) { + DRM_ERROR("failed to find VBIOS tables\n"); + ret = -ENODEV; + goto destroy_wq; + } + + intel_modeset_init(dev); + drm_helper_initial_config(dev, false); + + dev->devname = kstrdup(DRIVER_NAME, GFP_KERNEL); + if (!dev->devname) { + ret = -ENOMEM; + goto modeset_cleanup; + } + + ret = drm_irq_install(dev); + if (ret) { + kfree(dev->devname); + goto modeset_cleanup; + } + return 0; + +modeset_cleanup: + intel_modeset_cleanup(dev); +destroy_wq: + destroy_workqueue(dev_priv->wq); +destroy_ringbuffer: + i915_gem_cleanup_ringbuffer(dev); +out: + return ret; +} + +/** + * 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; + int ret = 0, num_pipes = 2; + u32 tmp; + + 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->dev = dev; + + /* 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_MOBILE(dev) || IS_I9XX(dev)) + dev_priv->cursor_needs_physical = true; + else + dev_priv->cursor_needs_physical = false; + + if (IS_I965G(dev) || IS_G33(dev)) + dev_priv->cursor_needs_physical = false; + + if (IS_I9XX(dev)) + pci_read_config_dword(dev->pdev, 0x5C, &dev_priv->stolen_base); + + 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"); + ret = -ENODEV; + goto free_priv; + } + + 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_KERNEL|_DRM_READ_ONLY|_DRM_DRIVER, + &dev_priv->mmio_map); + if (ret != 0) { + DRM_ERROR("Cannot add mapping for MMIO registers\n"); + goto free_priv; + } + + i915_gem_load(dev); + +#ifdef __linux__ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) + intel_init_chipset_flush_compat(dev); +#endif +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25) + intel_opregion_init(dev); +#endif +#endif + + tmp = I915_READ(PIPEASTAT); + I915_WRITE(PIPEASTAT, tmp); + tmp = I915_READ(PIPEBSTAT); + I915_WRITE(PIPEBSTAT, tmp); + + atomic_set(&dev_priv->irq_received, 0); + I915_WRITE(HWSTAM, 0xeffe); + I915_WRITE(IMR, 0x0); + I915_WRITE(IER, 0x0); + + DRM_SPININIT(&dev_priv->swaps_lock, "swap"); + INIT_LIST_HEAD(&dev_priv->vbl_swaps.head); + dev_priv->swaps_pending = 0; + + DRM_SPININIT(&dev_priv->user_irq_lock, "userirq"); + dev_priv->user_irq_refcount = 0; + dev_priv->irq_mask_reg = ~0; + + ret = drm_vblank_init(dev, num_pipes); + if (ret) + goto out_rmmap; + + ret = drm_hotplug_init(dev); + if (ret) + goto out_rmmap; + + dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; + dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ + + i915_enable_interrupt(dev); + DRM_INIT_WAITQUEUE(&dev_priv->irq_queue); + + /* + * Initialize the hardware status page IRQ location. + */ + + I915_WRITE(INSTPM, (1 << 5) | (1 << 21)); + + if (!I915_NEED_GFX_HWS(dev)) { + ret = i915_init_hardware_status(dev); + if (ret) + return ret; + } + + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + ret = i915_load_modeset_init(dev); + if (ret < 0) { + DRM_ERROR("failed to init modeset\n"); + goto out_rmmap; + } + } + + return 0; + +out_rmmap: + drm_rmmap(dev, dev_priv->mmio_map); +free_priv: + drm_free(dev_priv, sizeof(struct drm_i915_private), DRM_MEM_DRIVER); + return ret; +} + +int i915_driver_unload(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + u32 temp; + + i915_free_hardware_status(dev); + + dev_priv->vblank_pipe = 0; + + dev_priv->irq_enabled = 0; + + I915_WRITE(HWSTAM, 0xffffffff); + I915_WRITE(IMR, 0xffffffff); + I915_WRITE(IER, 0x0); + + temp = I915_READ(PIPEASTAT); + I915_WRITE(PIPEASTAT, temp); + temp = I915_READ(PIPEBSTAT); + I915_WRITE(PIPEBSTAT, temp); + temp = I915_READ(IIR); + I915_WRITE(IIR, temp); + + I915_WRITE(PRB0_CTL, 0); + + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + drm_irq_uninstall(dev); + intel_modeset_cleanup(dev); + destroy_workqueue(dev_priv->wq); + } + +#if 0 + if (dev_priv->ring.virtual_start) { + drm_core_ioremapfree(&dev_priv->ring.map, dev); + } +#endif + +#ifdef DRI2 + if (dev_priv->sarea_kmap.virtual) { + drm_bo_kunmap(&dev_priv->sarea_kmap); + dev_priv->sarea_kmap.virtual = NULL; + dev->sigdata.lock = NULL; + } + + if (dev_priv->sarea_bo) { + mutex_lock(&dev->struct_mutex); + drm_bo_usage_deref_locked(&dev_priv->sarea_bo); + mutex_unlock(&dev->struct_mutex); + dev_priv->sarea_bo = NULL; + } +#endif + + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + mutex_lock(&dev->struct_mutex); + i915_gem_cleanup_ringbuffer(dev); + mutex_unlock(&dev->struct_mutex); + drm_mm_takedown(&dev_priv->vram); + i915_gem_lastclose(dev); + if (!I915_NEED_GFX_HWS(dev)) + drm_pci_free(dev, dev_priv->status_page_dmah); + } + + drm_rmmap(dev, dev_priv->mmio_map); + +#ifdef __linux__ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25) + intel_opregion_free(dev); +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) + intel_fini_chipset_flush_compat(dev); +#endif +#endif + + drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER); + + dev->dev_private = NULL; + return 0; +} + +int i915_master_create(struct drm_device *dev, struct drm_master *master) +{ + struct drm_i915_master_private *master_priv; + unsigned long sareapage; + int ret; + + master_priv = drm_calloc(1, sizeof(*master_priv), DRM_MEM_DRIVER); + if (!master_priv) + return -ENOMEM; + + /* prebuild the SAREA */ + sareapage = max(SAREA_MAX, PAGE_SIZE); + ret = drm_addmap(dev, 0, sareapage, _DRM_SHM, _DRM_CONTAINS_LOCK|_DRM_DRIVER, + &master_priv->sarea); + if (ret) { + DRM_ERROR("SAREA setup failed\n"); + return ret; + } + master_priv->sarea_priv = master_priv->sarea->handle + sizeof(struct drm_sarea); + master_priv->sarea_priv->pf_current_page = 0; + + master->driver_priv = master_priv; + return 0; +} + +void i915_master_destroy(struct drm_device *dev, struct drm_master *master) +{ + struct drm_i915_master_private *master_priv = master->driver_priv; + + if (!master_priv) + return; + + if (master_priv->sarea) + drm_rmmap_locked(dev, master_priv->sarea); + + drm_free(master_priv, sizeof(*master_priv), DRM_MEM_DRIVER); + + master->driver_priv = NULL; +} + +int i915_driver_open(struct drm_device *dev, struct drm_file *file_priv) +{ + struct drm_i915_file_private *i915_file_priv; + + DRM_DEBUG("\n"); + i915_file_priv = (struct drm_i915_file_private *) + drm_alloc(sizeof(*i915_file_priv), DRM_MEM_FILES); + + if (!i915_file_priv) + return -ENOMEM; + + file_priv->driver_priv = i915_file_priv; + + i915_file_priv->mm.last_gem_seqno = 0; + i915_file_priv->mm.last_gem_throttle_seqno = 0; + + return 0; +} + +void i915_driver_postclose(struct drm_device *dev, struct drm_file *file_priv) +{ + struct drm_i915_file_private *i915_file_priv = file_priv->driver_priv; + + drm_free(i915_file_priv, sizeof(*i915_file_priv), DRM_MEM_FILES); +} + +void i915_driver_preclose(struct drm_device * dev, struct drm_file *file_priv) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + i915_mem_release(dev, file_priv, dev_priv->agp_heap); +} + +void i915_driver_lastclose(struct drm_device * dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return; + +#ifdef I915_HAVE_BUFFER + if (dev_priv->val_bufs) { + vfree(dev_priv->val_bufs); + dev_priv->val_bufs = NULL; + } +#endif + + i915_gem_lastclose(dev); + + if (dev_priv->agp_heap) + i915_mem_takedown(&(dev_priv->agp_heap)); + +#if defined(DRI2) + if (dev_priv->sarea_kmap.virtual) { + drm_bo_kunmap(&dev_priv->sarea_kmap); + dev_priv->sarea_kmap.virtual = NULL; + dev->control->master->lock.hw_lock = NULL; + dev->sigdata.lock = NULL; + } + + if (dev_priv->sarea_bo) { + mutex_lock(&dev->struct_mutex); + drm_bo_usage_deref_locked(&dev_priv->sarea_bo); + mutex_unlock(&dev->struct_mutex); + dev_priv->sarea_bo = NULL; + } +#endif + + i915_dma_cleanup(dev); +} + +int i915_driver_firstopen(struct drm_device *dev) +{ + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return 0; + return 0; +} diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index 8c2e0a30..0609dba7 100644 --- a/shared-core/i915_irq.c +++ b/shared-core/i915_irq.c @@ -30,6 +30,8 @@ #include "drm.h" #include "i915_drm.h" #include "i915_drv.h" +#include "intel_drv.h" +#include "drm_crtc_helper.h" #define MAX_NOPID ((u32)~0) @@ -41,7 +43,7 @@ I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) static inline void -i915_enable_irq(drm_i915_private_t *dev_priv, uint32_t mask) +i915_enable_irq(struct drm_i915_private *dev_priv, uint32_t mask) { if ((dev_priv->irq_mask_reg & mask) != 0) { dev_priv->irq_mask_reg &= ~mask; @@ -51,7 +53,7 @@ i915_enable_irq(drm_i915_private_t *dev_priv, uint32_t mask) } static inline void -i915_disable_irq(drm_i915_private_t *dev_priv, uint32_t mask) +i915_disable_irq(struct drm_i915_private *dev_priv, uint32_t mask) { if ((dev_priv->irq_mask_reg & mask) != mask) { dev_priv->irq_mask_reg |= mask; @@ -72,7 +74,7 @@ i915_disable_irq(drm_i915_private_t *dev_priv, uint32_t mask) 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); @@ -109,7 +111,7 @@ i915_get_plane(struct drm_device *dev, int pipe) static int i915_pipe_enabled(struct drm_device *dev, int pipe) { - 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; unsigned long pipeconf = pipe ? PIPEBCONF : PIPEACONF; if (I915_READ(pipeconf) & PIPEACONF_ENABLE) @@ -127,8 +129,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_master_private *master_priv = dev->primary->master->driver_priv; + struct drm_i915_sarea *sarea_priv = master_priv->sarea_priv; u16 x1, y1, x2, y2; int pf_planes = 1 << plane; @@ -172,12 +174,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 drm_i915_master_private *master_priv = dev->primary->master->driver_priv; struct list_head *list, *tmp, hits, *hit; int nhits, nrects, slice[2], upper[2], lower[2], i, num_pages; unsigned counter[2]; struct drm_drawable_info *drw; - drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv; + struct drm_i915_sarea *sarea_priv = master_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 | @@ -216,13 +219,16 @@ 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)) continue; + master_priv = vbl_swap->minor->master->driver_priv; + sarea_priv = master_priv->sarea_priv; + list_del(list); dev_priv->swaps_pending--; drm_vblank_put(dev, pipe); @@ -240,8 +246,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); @@ -298,8 +304,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; @@ -350,7 +356,7 @@ static void i915_vblank_tasklet(struct drm_device *dev) top = upper[plane]; bottom = lower[plane]; - front = (dev_priv->sarea_priv->pf_current_page >> + front = (master_priv->sarea_priv->pf_current_page >> (2 * plane)) & 0x3; back = (front + 1) % num_pages; @@ -380,8 +386,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); @@ -391,7 +397,7 @@ static void i915_vblank_tasklet(struct drm_device *dev) u32 i915_get_vblank_counter(struct drm_device *dev, int plane) { - drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; unsigned long high_frame; unsigned long low_frame; u32 high1, high2, low, count; @@ -425,21 +431,72 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int plane) return count; } +static struct drm_device *hotplug_dev; + +/** + * Handler for user interrupts in process context (able to sleep, do VFS + * operations, etc. + * + * If another IRQ comes in while we're in this handler, it will still get put + * on the queue again to be rerun when we finish. + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) +static void i915_hotplug_work_func(void *work) +#else +static void i915_hotplug_work_func(struct work_struct *work) +#endif +{ + struct drm_device *dev = hotplug_dev; + + drm_helper_hotplug_stage_two(dev); + drm_handle_hotplug(dev); +} + +static int i915_run_hotplug_tasklet(struct drm_device *dev, uint32_t stat) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) + static DECLARE_WORK(hotplug, i915_hotplug_work_func, NULL); +#else + static DECLARE_WORK(hotplug, i915_hotplug_work_func); +#endif + struct drm_i915_private *dev_priv = dev->dev_private; + + hotplug_dev = dev; + + if (stat & TV_HOTPLUG_INT_STATUS) { + DRM_DEBUG("TV event\n"); + } + + if (stat & CRT_HOTPLUG_INT_STATUS) { + DRM_DEBUG("CRT event\n"); + } + + if (stat & SDVOB_HOTPLUG_INT_STATUS) { + DRM_DEBUG("sDVOB event\n"); + } + + if (stat & SDVOC_HOTPLUG_INT_STATUS) { + DRM_DEBUG("sDVOC event\n"); + } + queue_work(dev_priv->wq, &hotplug); + + return 0; +} + 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_master_private *master_priv; + struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; u32 iir; - u32 pipea_stats = 0, pipeb_stats = 0; + u32 pipea_stats = 0, pipeb_stats = 0, tvdac; + int hotplug = 0; int vblank = 0; #ifdef __linux__ if (dev->pdev->msi_enabled) I915_WRITE(IMR, ~0); #endif iir = I915_READ(IIR); -#if 0 - DRM_DEBUG("flag=%08x\n", iir); -#endif atomic_inc(&dev_priv->irq_received); if (iir == 0) { #ifdef __linux__ @@ -457,7 +514,6 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) */ if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) { pipea_stats = I915_READ(PIPEASTAT); - /* The vblank interrupt gets enabled even if we didn't ask for it, so make sure it's shut down again */ if (!(dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A)) @@ -472,9 +528,10 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) I915_WRITE(PIPEASTAT, pipea_stats); } + if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) { pipeb_stats = I915_READ(PIPEBSTAT); - + I915_WRITE(PIPEBSTAT, pipeb_stats); /* The vblank interrupt gets enabled even if we didn't ask for it, so make sure it's shut down again */ if (!(dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)) @@ -486,41 +543,64 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) vblank++; drm_handle_vblank(dev, i915_get_plane(dev, 1)); } + } #ifdef __linux__ -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25) - if (pipeb_stats & I915_LEGACY_BLC_EVENT_ENABLE) - opregion_asle_intr(dev); -#endif + if (dev->pdev->msi_enabled) + I915_WRITE(IMR, dev_priv->irq_mask_reg); #endif - I915_WRITE(PIPEBSTAT, pipeb_stats); + + (void) I915_READ(IIR); /* Flush posted writes */ + + /* This is a global event, and not a pipe A event */ + if (pipea_stats & PIPE_HOTPLUG_INTERRUPT_STATUS) + hotplug = 1; + + if (pipea_stats & PIPE_HOTPLUG_TV_INTERRUPT_STATUS) { + hotplug = 1; + /* Toggle hotplug detection to clear hotplug status */ + tvdac = I915_READ(TV_DAC); + I915_WRITE(TV_DAC, tvdac & ~TVDAC_STATE_CHG_EN); + I915_WRITE(TV_DAC, tvdac | TVDAC_STATE_CHG_EN); + } + + if (dev->primary->master) { + master_priv = dev->primary->master->driver_priv; + master_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); } #ifdef __linux__ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25) + if ((iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) && + (pipeb_stats & I915_LEGACY_BLC_EVENT_ENABLE)) + opregion_asle_intr(dev); if (iir & I915_ASLE_INTERRUPT) opregion_asle_intr(dev); #endif #endif - if (dev_priv->sarea_priv) - dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); - - I915_WRITE(IIR, iir); -#ifdef __linux__ - if (dev->pdev->msi_enabled) - I915_WRITE(IMR, dev_priv->irq_mask_reg); -#endif - (void) I915_READ(IIR); /* Flush posted writes */ - if (iir & I915_USER_INTERRUPT) { #ifdef I915_HAVE_GEM dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev); #endif DRM_WAKEUP(&dev_priv->irq_queue); -#ifdef I915_HAVE_FENCE - i915_fence_handler(dev); -#endif + } + + if (pipea_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS| + PIPE_VBLANK_INTERRUPT_STATUS)) { + vblank++; + drm_handle_vblank(dev, i915_get_plane(dev, 0)); + } + + /* The vblank interrupt gets enabled even if we didn't ask for + it, so make sure it's shut down again */ + if (!(dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)) + pipeb_stats &= ~(I915_VBLANK_INTERRUPT_ENABLE); + + if (pipeb_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS| + PIPE_VBLANK_INTERRUPT_STATUS)) { + vblank++; + drm_handle_vblank(dev, i915_get_plane(dev, 1)); } if (vblank) { @@ -528,12 +608,31 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) drm_locked_tasklet(dev, i915_vblank_tasklet); } + if ((iir & I915_DISPLAY_PORT_INTERRUPT) || hotplug) { + u32 temp2 = 0; + + DRM_INFO("Hotplug event received\n"); + + if (!IS_I9XX(dev) || IS_I915G(dev) || IS_I915GM(dev)) { + if (pipea_stats & PIPE_HOTPLUG_INTERRUPT_STATUS) + temp2 |= SDVOB_HOTPLUG_INT_STATUS | + SDVOC_HOTPLUG_INT_STATUS; + if (pipea_stats & PIPE_HOTPLUG_TV_INTERRUPT_STATUS) + temp2 |= TV_HOTPLUG_INT_STATUS; + } else { + temp2 = I915_READ(PORT_HOTPLUG_STAT); + + I915_WRITE(PORT_HOTPLUG_STAT, temp2); + } + i915_run_hotplug_tasklet(dev, temp2); + } + return IRQ_HANDLED; } 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); @@ -550,16 +649,20 @@ 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_device *dev) { + struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; + DRM_SPINLOCK(&dev_priv->user_irq_lock); if (dev_priv->irq_enabled && (++dev_priv->user_irq_refcount == 1)) i915_enable_irq(dev_priv, I915_USER_INTERRUPT); 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_device *dev) { + struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; + DRM_SPINLOCK(&dev_priv->user_irq_lock); #ifdef __linux__ BUG_ON(dev_priv->irq_enabled && dev_priv->user_irq_refcount <= 0); @@ -572,7 +675,8 @@ void i915_user_irq_off(drm_i915_private_t *dev_priv) 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; + struct drm_i915_master_private *master_priv; int ret = 0; if (!dev_priv) { @@ -583,26 +687,31 @@ int i915_wait_irq(struct drm_device * dev, int irq_nr) DRM_DEBUG("irq_nr=%d breadcrumb=%d\n", irq_nr, READ_BREADCRUMB(dev_priv)); + master_priv = dev->primary->master->driver_priv; + + if (!master_priv) { + DRM_ERROR("no master priv?\n"); + return -EINVAL; + } + if (READ_BREADCRUMB(dev_priv) >= irq_nr) { - if (dev_priv->sarea_priv) - dev_priv->sarea_priv->last_dispatch = - READ_BREADCRUMB(dev_priv); + master_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); return 0; } - i915_user_irq_on(dev_priv); + i915_user_irq_on(dev); DRM_WAIT_ON(ret, dev_priv->irq_queue, 3 * DRM_HZ, READ_BREADCRUMB(dev_priv) >= irq_nr); - i915_user_irq_off(dev_priv); + i915_user_irq_off(dev); if (ret == -EBUSY) { DRM_ERROR("EBUSY -- rec: %d emitted: %d\n", READ_BREADCRUMB(dev_priv), (int)dev_priv->counter); } + + if (READ_BREADCRUMB(dev_priv) >= irq_nr) + master_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); - if (dev_priv->sarea_priv) - dev_priv->sarea_priv->last_dispatch = - READ_BREADCRUMB(dev_priv); return ret; } @@ -611,8 +720,8 @@ int i915_wait_irq(struct drm_device * dev, int irq_nr) 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); @@ -637,8 +746,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("called with no initialization\n"); @@ -650,7 +759,7 @@ int i915_irq_wait(struct drm_device *dev, void *data, int i915_enable_vblank(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; int pipe = i915_get_pipe(dev, plane); u32 pipestat_reg = 0; u32 mask_reg = 0; @@ -698,7 +807,7 @@ int i915_enable_vblank(struct drm_device *dev, int plane) void i915_disable_vblank(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; int pipe = i915_get_pipe(dev, plane); u32 pipestat_reg = 0; u32 mask_reg = 0; @@ -723,8 +832,7 @@ void i915_disable_vblank(struct drm_device *dev, int plane) i915_disable_irq(dev_priv, mask_reg); DRM_SPINUNLOCK(&dev_priv->user_irq_lock); - if (pipestat_reg) - { + if (pipestat_reg) { pipestat = I915_READ (pipestat_reg); pipestat &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE | PIPE_VBLANK_INTERRUPT_ENABLE); @@ -738,21 +846,78 @@ void i915_disable_vblank(struct drm_device *dev, int plane) } } -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; + struct drm_connector *o; + + dev_priv->irq_mask_reg &= ~0; - dev_priv->irq_mask_reg = ~0; - I915_WRITE(IMR, dev_priv->irq_mask_reg); - I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK); - (void) I915_READ (IER); + if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) { + if (dev->mode_config.num_connector) + dev_priv->irq_mask_reg &= ~I915_DISPLAY_PORT_INTERRUPT; + } else { + if (dev->mode_config.num_connector) + dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_A_EVENT_INTERRUPT; + + /* Enable global interrupts for hotplug - not a pipeA event */ + I915_WRITE(PIPEASTAT, I915_READ(PIPEASTAT) | + PIPE_HOTPLUG_INTERRUPT_ENABLE | + PIPE_HOTPLUG_TV_INTERRUPT_ENABLE | + PIPE_HOTPLUG_TV_INTERRUPT_STATUS | + PIPE_HOTPLUG_INTERRUPT_STATUS); + } + + if (!(dev_priv->irq_mask_reg & I915_DISPLAY_PORT_INTERRUPT) || + !(dev_priv->irq_mask_reg & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT)) { + u32 temp = 0; + + if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) { + temp = I915_READ(PORT_HOTPLUG_EN); + + /* Activate the CRT */ + temp |= CRT_HOTPLUG_INT_EN; + } + + if (IS_I9XX(dev)) { + /* SDVOB */ + o = intel_sdvo_find(dev, 1); + if (o && intel_sdvo_supports_hotplug(o)) { + intel_sdvo_set_hotplug(o, 1); + temp |= SDVOB_HOTPLUG_INT_EN; + } + + /* SDVOC */ + o = intel_sdvo_find(dev, 0); + if (o && intel_sdvo_supports_hotplug(o)) { + intel_sdvo_set_hotplug(o, 1); + temp |= SDVOC_HOTPLUG_INT_EN; + } + + I915_WRITE(SDVOB, I915_READ(SDVOB) | SDVO_INTERRUPT_ENABLE); + I915_WRITE(SDVOC, I915_READ(SDVOC) | SDVO_INTERRUPT_ENABLE); + + /* TV */ + I915_WRITE(TV_DAC, I915_READ(TV_DAC) | TVDAC_STATE_CHG_EN); + } else { + /* DVO ???? */ + } + + if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) { + I915_WRITE(PORT_HOTPLUG_EN, temp); + + DRM_DEBUG("HEN %08x\n",I915_READ(PORT_HOTPLUG_EN)); + DRM_DEBUG("HST %08x\n",I915_READ(PORT_HOTPLUG_STAT)); + + I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); + } + } #ifdef __linux__ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25) opregion_enable_asle(dev); #endif #endif - dev_priv->irq_enabled = 1; } @@ -761,7 +926,7 @@ 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; + struct drm_i915_private *dev_priv = dev->dev_private; if (!dev_priv) { DRM_ERROR("called with no initialization\n"); @@ -774,15 +939,23 @@ 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; + u32 flag = 0; if (!dev_priv) { DRM_ERROR("called with no initialization\n"); return -EINVAL; } - pipe->pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; + if (dev_priv->irq_enabled) + flag = ~dev_priv->irq_mask_reg; + + pipe->pipe = 0; + if (flag & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) + pipe->pipe |= DRM_I915_VBLANK_PIPE_A; + if (flag & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) + pipe->pipe |= DRM_I915_VBLANK_PIPE_B; return 0; } @@ -793,9 +966,10 @@ 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_master_private *master_priv; + 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; @@ -806,7 +980,12 @@ int i915_vblank_swap(struct drm_device *dev, void *data, return -EINVAL; } - if (!dev_priv->sarea_priv || dev_priv->sarea_priv->rotation) { + if (!dev->primary->master) + return -EINVAL; + + master_priv = dev->primary->master->driver_priv; + + if (master_priv->sarea_priv->rotation) { DRM_DEBUG("Rotation not supported\n"); return -EINVAL; } @@ -898,7 +1077,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 && @@ -932,6 +1111,7 @@ int i915_vblank_swap(struct drm_device *dev, void *data, vbl_swap->plane = plane; vbl_swap->sequence = swap->sequence; vbl_swap->flip = (swap->seqtype & _DRM_VBLANK_FLIP); + vbl_swap->minor = file_priv->minor; if (vbl_swap->flip) swap->sequence++; @@ -950,7 +1130,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 = dev->dev_private; I915_WRITE(HWSTAM, 0xeffe); I915_WRITE(IMR, 0xffffffff); @@ -959,7 +1139,7 @@ void i915_driver_irq_preinstall(struct drm_device * dev) int i915_driver_irq_postinstall(struct drm_device * dev) { - drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; int ret, num_pipes = 2; INIT_LIST_HEAD(&dev_priv->vbl_swaps.head); @@ -988,7 +1168,7 @@ int 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 = dev->dev_private; u32 temp; if (!dev_priv) diff --git a/shared-core/i915_mem.c b/shared-core/i915_mem.c index 6126a60d..15d63dec 100644 --- a/shared-core/i915_mem.c +++ b/shared-core/i915_mem.c @@ -45,8 +45,9 @@ */ 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_master_private *master_priv = dev->primary->master->driver_priv; + struct drm_i915_sarea *sarea_priv = master_priv->sarea_priv; struct drm_tex_region *list; unsigned shift, nr; unsigned start; @@ -256,7 +257,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 +272,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 +310,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 +338,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 +362,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/nouveau_dma.h b/shared-core/nouveau_dma.h index ce3c58cb..07652c2b 100644 --- a/shared-core/nouveau_dma.h +++ b/shared-core/nouveau_dma.h @@ -93,4 +93,9 @@ typedef enum { } \ } while(0) +/* This should allow easy switching to a real fifo in the future. */ +#define OUT_MODE(mthd, val) do { \ + nv50_display_command(dev_priv, mthd, val); \ +} while(0) + #endif diff --git a/shared-core/nouveau_drv.h b/shared-core/nouveau_drv.h index a97e3e99..cdb73bb3 100644 --- a/shared-core/nouveau_drv.h +++ b/shared-core/nouveau_drv.h @@ -41,6 +41,9 @@ #include "nouveau_drm.h" #include "nouveau_reg.h" +#include "nouveau_bios.h" + +#define MAX_NUM_DCB_ENTRIES 16 struct mem_block { struct mem_block *next; @@ -311,6 +314,27 @@ struct drm_nouveau_private { struct list_head gpuobj_list; + void *display_priv; /* internal modesetting */ + void *kms_priv; /* related to public interface */ + + /* Hook these up to the "public interface" to accomodate a certain allocation style. */ + /* This is to avoid polluting the internal interface. */ + void *(*alloc_crtc) (struct drm_device *dev); + void *(*alloc_output) (struct drm_device *dev); + void *(*alloc_connector) (struct drm_device *dev); + + void (*free_crtc) (void *crtc); + void (*free_output) (void *output); + void (*free_connector) (void *connector); + + struct bios bios; + + struct { + int entries; + struct dcb_entry entry[MAX_NUM_DCB_ENTRIES]; + unsigned char i2c_read[MAX_NUM_DCB_ENTRIES]; + unsigned char i2c_write[MAX_NUM_DCB_ENTRIES]; + } dcb_table; struct nouveau_suspend_resume { uint32_t fifo_mode; uint32_t graph_ctx_control; @@ -365,6 +389,7 @@ extern struct mem_block *nouveau_mem_alloc_block(struct mem_block *, struct drm_file *, int tail); extern void nouveau_mem_takedown(struct mem_block **heap); extern void nouveau_mem_free_block(struct mem_block *); +extern struct mem_block* find_block_by_handle(struct mem_block *heap, drm_handle_t handle); extern uint64_t nouveau_mem_fb_amount(struct drm_device *); extern void nouveau_mem_release(struct drm_file *, struct mem_block *heap); extern int nouveau_ioctl_mem_alloc(struct drm_device *, void *data, diff --git a/shared-core/nouveau_irq.c b/shared-core/nouveau_irq.c index 2a3d8a0b..48872622 100644 --- a/shared-core/nouveau_irq.c +++ b/shared-core/nouveau_irq.c @@ -37,6 +37,13 @@ #include "nouveau_reg.h" #include "nouveau_swmthd.h" +/* needed for interrupt based vpll changes */ +#include "nv50_display.h" +#include "nv50_crtc.h" +#include "nv50_output.h" +/* needed for hotplug irq */ +#include "nv50_kms_wrapper.h" + void nouveau_irq_preinstall(struct drm_device *dev) { @@ -503,11 +510,82 @@ static void nouveau_nv50_display_irq_handler(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - uint32_t val = NV_READ(NV50_DISPLAY_SUPERVISOR); + uint32_t val = NV_READ(NV50_PDISPLAY_SUPERVISOR); + + DRM_INFO("NV50_PDISPLAY_SUPERVISOR - 0x%08X\n", val); + + /* vblank interrupts */ + if (val & NV50_PDISPLAY_SUPERVISOR_CRTCn) { + NV_WRITE(NV50_PDISPLAY_SUPERVISOR, val & NV50_PDISPLAY_SUPERVISOR_CRTCn); + val &= ~NV50_PDISPLAY_SUPERVISOR_CRTCn; + } + + /* clock setting amongst other things. */ + if (val & NV50_PDISPLAY_SUPERVISOR_CLK_MASK) { + uint32_t state = (val & NV50_PDISPLAY_SUPERVISOR_CLK_MASK) >> NV50_PDISPLAY_SUPERVISOR_CLK_MASK__SHIFT; + + NV50_DEBUG("state %d\n", state); + + /* Set pll */ + if (state == 2) { + struct nv50_display *display = nv50_get_display(dev); + struct nv50_output *output = NULL; + struct nv50_crtc *crtc = NULL; + int crtc_index; + + uint32_t unk30 = NV_READ(NV50_PDISPLAY_UNK30_CTRL); + + for (crtc_index = 0; crtc_index < 2; crtc_index++) { + bool clock_change = false; + bool clock_ack = false; + + if (crtc_index == 0 && (unk30 & NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK0)) + clock_change = true; + + if (crtc_index == 1 && (unk30 & NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK1)) + clock_change = true; + + if (clock_change) + clock_ack = true; + + if (display->last_crtc == crtc_index) + clock_ack = true; - DRM_INFO("NV50_DISPLAY_INTR - 0x%08X\n", val); + list_for_each_entry(crtc, &display->crtcs, item) { + if (crtc->index == crtc_index) + break; + } - NV_WRITE(NV50_DISPLAY_SUPERVISOR, val); + if (clock_change) + crtc->set_clock(crtc); + + NV50_DEBUG("index %d clock_change %d clock_ack %d\n", crtc_index, clock_change, clock_ack); + + if (!clock_ack) + continue; + + crtc->set_clock_mode(crtc); + + list_for_each_entry(output, &display->outputs, item) { + if (!output->crtc) + continue; + + if (output->crtc == crtc) + output->set_clock_mode(output); + } + } + } + + NV_WRITE(NV50_PDISPLAY_UNK30_CTRL, NV50_PDISPLAY_UNK30_CTRL_PENDING); + NV_WRITE(NV50_PDISPLAY_SUPERVISOR, val & NV50_PDISPLAY_SUPERVISOR_CLK_MASK); + + val &= ~NV50_PDISPLAY_SUPERVISOR_CLK_MASK; + } + + if (val) + DRM_ERROR("unsupported NV50_DISPLAY_INTR - 0x%08X\n", val); + + NV_WRITE(NV50_PDISPLAY_SUPERVISOR, val); } static void @@ -515,10 +593,13 @@ nouveau_nv50_i2c_irq_handler(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - DRM_INFO("NV50_I2C_INTR - 0x%08X\n", NV_READ(NV50_I2C_CONTROLLER)); + DRM_INFO("NV50_PCONNECTOR_HOTPLUG_CTRL - 0x%08X\n", NV_READ(NV50_PCONNECTOR_HOTPLUG_CTRL)); /* This seems to be the way to acknowledge an interrupt. */ - NV_WRITE(NV50_I2C_CONTROLLER, 0x7FFF7FFF); + NV_WRITE(NV50_PCONNECTOR_HOTPLUG_CTRL, 0x7FFF7FFF); + + /* Do a "dumb" detect all */ + nv50_kms_connector_detect_all(dev); } irqreturn_t diff --git a/shared-core/nouveau_mem.c b/shared-core/nouveau_mem.c index d79c1a52..1078a9c5 100644 --- a/shared-core/nouveau_mem.c +++ b/shared-core/nouveau_mem.c @@ -34,6 +34,7 @@ #include "drm.h" #include "drm_sarea.h" #include "nouveau_drv.h" +#include "nv50_kms_wrapper.h" static struct mem_block * split_block(struct mem_block *p, uint64_t start, uint64_t size, @@ -120,6 +121,17 @@ static struct mem_block *find_block(struct mem_block *heap, uint64_t start) return NULL; } +struct mem_block *find_block_by_handle(struct mem_block *heap, drm_handle_t handle) +{ + struct mem_block *p; + + list_for_each(p, heap) + if (p->map_handle == handle) + return p; + + return NULL; +} + void nouveau_mem_free_block(struct mem_block *p) { p->file_priv = NULL; @@ -734,6 +746,30 @@ void nouveau_mem_free(struct drm_device* dev, struct mem_block* block) DRM_DEBUG("freeing 0x%llx type=0x%08x\n", block->start, block->flags); + /* Check if the deallocations cause problems for our modesetting system. */ + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + if (dev_priv->card_type >= NV_50) { + struct nv50_crtc *crtc = NULL; + struct nv50_display *display = nv50_get_display(dev); + + list_for_each_entry(crtc, &display->crtcs, item) { + if (crtc->fb->block == block) { + crtc->fb->block = NULL; + + if (!crtc->blanked) + crtc->blank(crtc, true); + } + + if (crtc->cursor->block == block) { + crtc->cursor->block = NULL; + + if (crtc->cursor->visible) + crtc->cursor->hide(crtc); + } + } + } + } + if (block->flags&NOUVEAU_MEM_MAPPED) drm_rmmap(dev, block->map); diff --git a/shared-core/nouveau_reg.h b/shared-core/nouveau_reg.h index 1ae0177c..091c22c4 100644 --- a/shared-core/nouveau_reg.h +++ b/shared-core/nouveau_reg.h @@ -116,6 +116,9 @@ #define NV04_PBUS_PCI_NV_1 0x00001804 #define NV04_PBUS_PCI_NV_19 0x0000184C +#define NV04_PBUS_PCI_NV_20 0x00001850 +# define NV04_PBUS_PCI_NV_20_ROM_SHADOW_DISABLED (0 << 0) +# define NV04_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED (1 << 0) #define NV04_PTIMER_INTR_0 0x00009100 #define NV04_PTIMER_INTR_EN_0 0x00009140 @@ -125,8 +128,6 @@ #define NV04_PTIMER_TIME_1 0x00009410 #define NV04_PTIMER_ALARM_0 0x00009420 -#define NV50_I2C_CONTROLLER 0x0000E054 - #define NV04_PFB_CFG0 0x00100200 #define NV04_PFB_CFG1 0x00100204 #define NV40_PFB_020C 0x0010020C @@ -542,6 +543,8 @@ /* This name is a partial guess. */ #define NV50_DISPLAY_SUPERVISOR 0x00610024 +#define NV04_PRAMIN 0x00700000 + /* Fifo commands. These are not regs, neither masks */ #define NV03_FIFO_CMD_JUMP 0x20000000 #define NV03_FIFO_CMD_JUMP_OFFSET_MASK 0x1ffffffc @@ -591,3 +594,236 @@ #define NV40_RAMFC_UNK_48 0x48 #define NV40_RAMFC_UNK_4C 0x4C #define NV40_RAMFC_UNK_50 0x50 + +/* This is a partial import from rules-ng, a few things may be duplicated. + * Eventually we should completely import everything from rules-ng. + * For the moment check rules-ng for docs. + */ + +#define NV50_PMC 0x00000000 +#define NV50_PMC__LEN 0x1 +#define NV50_PMC__ESIZE 0x2000 +# define NV50_PMC_BOOT_0 0x00000000 +# define NV50_PMC_BOOT_0_REVISION 0x000000ff +# define NV50_PMC_BOOT_0_REVISION__SHIFT 0 +# define NV50_PMC_BOOT_0_ARCH 0x0ff00000 +# define NV50_PMC_BOOT_0_ARCH__SHIFT 20 +# define NV50_PMC_INTR_0 0x00000100 +# define NV50_PMC_INTR_0_PFIFO (1<<8) +# define NV50_PMC_INTR_0_PGRAPH (1<<12) +# define NV50_PMC_INTR_0_PTIMER (1<<20) +# define NV50_PMC_INTR_0_HOTPLUG (1<<21) +# define NV50_PMC_INTR_0_DISPLAY (1<<26) +# define NV50_PMC_INTR_EN_0 0x00000140 +# define NV50_PMC_INTR_EN_0_MASTER (1<<0) +# define NV50_PMC_INTR_EN_0_MASTER_DISABLED (0<<0) +# define NV50_PMC_INTR_EN_0_MASTER_ENABLED (1<<0) +# define NV50_PMC_ENABLE 0x00000200 +# define NV50_PMC_ENABLE_PFIFO (1<<8) +# define NV50_PMC_ENABLE_PGRAPH (1<<12) + +#define NV50_PCONNECTOR 0x0000e000 +#define NV50_PCONNECTOR__LEN 0x1 +#define NV50_PCONNECTOR__ESIZE 0x1000 +# define NV50_PCONNECTOR_HOTPLUG_INTR 0x0000e050 +# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C0 (1<<0) +# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C1 (1<<1) +# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C2 (1<<2) +# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C3 (1<<3) +# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C0 (1<<16) +# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C1 (1<<17) +# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C2 (1<<18) +# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C3 (1<<19) +# define NV50_PCONNECTOR_HOTPLUG_CTRL 0x0000e054 +# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C0 (1<<0) +# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C1 (1<<1) +# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C2 (1<<2) +# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C3 (1<<3) +# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C0 (1<<16) +# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C1 (1<<17) +# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C2 (1<<18) +# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C3 (1<<19) +# define NV50_PCONNECTOR_HOTPLUG_STATE 0x0000e104 +# define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C0 (1<<2) +# define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C1 (1<<6) +# define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C2 (1<<10) +# define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C3 (1<<14) +# define NV50_PCONNECTOR_I2C_PORT_0 0x0000e138 +# define NV50_PCONNECTOR_I2C_PORT_1 0x0000e150 +# define NV50_PCONNECTOR_I2C_PORT_2 0x0000e168 +# define NV50_PCONNECTOR_I2C_PORT_3 0x0000e180 +# define NV50_PCONNECTOR_I2C_PORT_4 0x0000e240 +# define NV50_PCONNECTOR_I2C_PORT_5 0x0000e258 + +#define NV50_PBUS 0x00088000 +#define NV50_PBUS__LEN 0x1 +#define NV50_PBUS__ESIZE 0x1000 +# define NV50_PBUS_PCI_ID 0x00088000 +# define NV50_PBUS_PCI_ID_VENDOR_ID 0x0000ffff +# define NV50_PBUS_PCI_ID_VENDOR_ID__SHIFT 0 +# define NV50_PBUS_PCI_ID_DEVICE_ID 0xffff0000 +# define NV50_PBUS_PCI_ID_DEVICE_ID__SHIFT 16 + +#define NV50_PFB 0x00100000 +#define NV50_PFB__LEN 0x1 +#define NV50_PFB__ESIZE 0x1000 + +#define NV50_PEXTDEV 0x00101000 +#define NV50_PEXTDEV__LEN 0x1 +#define NV50_PEXTDEV__ESIZE 0x1000 + +#define NV50_PROM 0x00300000 +#define NV50_PROM__LEN 0x1 +#define NV50_PROM__ESIZE 0x10000 + +#define NV50_PGRAPH 0x00400000 +#define NV50_PGRAPH__LEN 0x1 +#define NV50_PGRAPH__ESIZE 0x10000 + +#define NV50_PDISPLAY 0x00610000 +#define NV50_PDISPLAY__LEN 0x1 +#define NV50_PDISPLAY__ESIZE 0x10000 +# define NV50_PDISPLAY_SUPERVISOR 0x00610024 +# define NV50_PDISPLAY_SUPERVISOR_CRTCn 0x0000000c +# define NV50_PDISPLAY_SUPERVISOR_CRTCn__SHIFT 2 +# define NV50_PDISPLAY_SUPERVISOR_CRTC0 (1<<2) +# define NV50_PDISPLAY_SUPERVISOR_CRTC1 (1<<3) +# define NV50_PDISPLAY_SUPERVISOR_CLK_MASK 0x00000070 +# define NV50_PDISPLAY_SUPERVISOR_CLK_MASK__SHIFT 4 +# define NV50_PDISPLAY_SUPERVISOR_CLK_UPDATE (1<<5) +# define NV50_PDISPLAY_SUPERVISOR_INTR 0x0061002c +# define NV50_PDISPLAY_SUPERVISOR_INTR_VBLANK_CRTC0 (1<<2) +# define NV50_PDISPLAY_SUPERVISOR_INTR_VBLANK_CRTC1 (1<<3) +# define NV50_PDISPLAY_SUPERVISOR_INTR_UNK1 (1<<4) +# define NV50_PDISPLAY_SUPERVISOR_INTR_CLK_UPDATE (1<<5) +# define NV50_PDISPLAY_SUPERVISOR_INTR_UNK4 (1<<6) +# define NV50_PDISPLAY_UNK30_CTRL 0x00610030 +# define NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK0 (1<<9) +# define NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK1 (1<<10) +# define NV50_PDISPLAY_UNK30_CTRL_PENDING (1<<31) +# define NV50_PDISPLAY_UNK50_CTRL 0x00610050 +# define NV50_PDISPLAY_UNK50_CTRL_CRTC0_ACTIVE (1<<1) +# define NV50_PDISPLAY_UNK50_CTRL_CRTC0_ACTIVE_MASK 0x00000003 +# define NV50_PDISPLAY_UNK50_CTRL_CRTC0_ACTIVE_MASK__SHIFT 0 +# define NV50_PDISPLAY_UNK50_CTRL_CRTC1_ACTIVE (1<<9) +# define NV50_PDISPLAY_UNK50_CTRL_CRTC1_ACTIVE_MASK 0x00000300 +# define NV50_PDISPLAY_UNK50_CTRL_CRTC1_ACTIVE_MASK__SHIFT 8 +# define NV50_PDISPLAY_UNK200_CTRL 0x00610200 +# define NV50_PDISPLAY_CURSOR 0x00610270 +# define NV50_PDISPLAY_CURSOR__LEN 0x2 +# define NV50_PDISPLAY_CURSOR__ESIZE 0x10 +# define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i) (0x00610270+(i)*0x10) +# define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_ON (1<<0) +# define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_MASK 0x00030000 +# define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_MASK__SHIFT 16 +# define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_ACTIVE (1<<16) + +# define NV50_PDISPLAY_CTRL_STATE 0x00610300 +# define NV50_PDISPLAY_CTRL_STATE_ENABLE (1<<0) +# define NV50_PDISPLAY_CTRL_STATE_PENDING (1<<31) +# define NV50_PDISPLAY_CTRL_VAL 0x00610304 +# define NV50_PDISPLAY_UNK_380 0x00610380 +# define NV50_PDISPLAY_RAM_AMOUNT 0x00610384 +# define NV50_PDISPLAY_UNK_388 0x00610388 +# define NV50_PDISPLAY_UNK_38C 0x0061038c +# define NV50_PDISPLAY_CRTC_VAL 0x00610a00 +# define NV50_PDISPLAY_CRTC_VAL__LEN 0x2 +# define NV50_PDISPLAY_CRTC_VAL_UNK_900(i,j) (0x00610a18+(i)*0x540+(j)*0x4) +# define NV50_PDISPLAY_CRTC_VAL_CLUT_MODE(i,j) (0x00610a24+(i)*0x540+(j)*0x4) +# define NV50_PDISPLAY_CRTC_VAL_INTERLACE(i,j) (0x00610a48+(i)*0x540+(j)*0x4) +# define NV50_PDISPLAY_CRTC_VAL_SCALE_CTRL(i,j) (0x00610a50+(i)*0x540+(j)*0x4) +# define NV50_PDISPLAY_CRTC_VAL_CURSOR_CTRL(i,j) (0x00610a58+(i)*0x540+(j)*0x4) +# define NV50_PDISPLAY_CRTC_VAL_UNK_904(i,j) (0x00610ab8+(i)*0x540+(j)*0x4) +# define NV50_PDISPLAY_CRTC_VAL_DEPTH(i,j) (0x00610ac8+(i)*0x540+(j)*0x4) +# define NV50_PDISPLAY_CRTC_VAL_CLOCK(i,j) (0x00610ad0+(i)*0x540+(j)*0x4) +# define NV50_PDISPLAY_CRTC_VAL_COLOR_CTRL(i,j) (0x00610ae0+(i)*0x540+(j)*0x4) +# define NV50_PDISPLAY_CRTC_VAL_SYNC_START_TO_BLANK_END(i,j) (0x00610ae8+(i)*0x540+(j)*0x4) +# define NV50_PDISPLAY_CRTC_VAL_MODE_UNK1(i,j) (0x00610af0+(i)*0x540+(j)*0x4) +# define NV50_PDISPLAY_CRTC_VAL_DISPLAY_TOTAL(i,j) (0x00610af8+(i)*0x540+(j)*0x4) +# define NV50_PDISPLAY_CRTC_VAL_SYNC_DURATION(i,j) (0x00610b00+(i)*0x540+(j)*0x4) +# define NV50_PDISPLAY_CRTC_VAL_MODE_UNK2(i,j) (0x00610b08+(i)*0x540+(j)*0x4) +# define NV50_PDISPLAY_CRTC_VAL_UNK_828(i,j) (0x00610b10+(i)*0x540+(j)*0x4) +# define NV50_PDISPLAY_CRTC_VAL_FB_SIZE(i,j) (0x00610b18+(i)*0x540+(j)*0x4) +# define NV50_PDISPLAY_CRTC_VAL_FB_PITCH(i,j) (0x00610b20+(i)*0x540+(j)*0x4) +# define NV50_PDISPLAY_CRTC_VAL_FB_PITCH_LINEAR_FB (1<<20) +# define NV50_PDISPLAY_CRTC_VAL_FB_POS(i,j) (0x00610b28+(i)*0x540+(j)*0x4) +# define NV50_PDISPLAY_CRTC_VAL_SCALE_CENTER_OFFSET(i,j) (0x00610b38+(i)*0x540+(j)*0x4) +# define NV50_PDISPLAY_CRTC_VAL_REAL_RES(i,j) (0x00610b40+(i)*0x540+(j)*0x4) +# define NV50_PDISPLAY_CRTC_VAL_SCALE_RES1(i,j) (0x00610b48+(i)*0x540+(j)*0x4) +# define NV50_PDISPLAY_CRTC_VAL_SCALE_RES2(i,j) (0x00610b50+(i)*0x540+(j)*0x4) + + +# define NV50_PDISPLAY_DAC_VAL_MODE_CTRL(i,j) (0x00610b58+(i)*0x8+(j)*0x4) + + +# define NV50_PDISPLAY_SOR_VAL_MODE_CTRL(i,j) (0x00610b70+(i)*0x8+(j)*0x4) + + +# define NV50_PDISPLAY_DAC_VAL_MODE_CTRL2(i,j) (0x00610bdc+(i)*0x8+(j)*0x4) + + +# define NV50_PDISPLAY_CRTC_CLK 0x00614000 +# define NV50_PDISPLAY_CRTC_CLK__LEN 0x2 +# define NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1(i) (0x00614100+(i)*0x800) +# define NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1_CONNECTED 0x00000600 +# define NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1_CONNECTED__SHIFT 9 +# define NV50_PDISPLAY_CRTC_CLK_VPLL_A(i) (0x00614104+(i)*0x800) +# define NV50_PDISPLAY_CRTC_CLK_VPLL_B(i) (0x00614108+(i)*0x800) +# define NV50_PDISPLAY_CRTC_CLK_CLK_CTRL2(i) (0x00614200+(i)*0x800) + +# define NV50_PDISPLAY_DAC_CLK 0x00614000 +# define NV50_PDISPLAY_DAC_CLK__LEN 0x3 +# define NV50_PDISPLAY_DAC_CLK_CLK_CTRL2(i) (0x00614280+(i)*0x800) + +# define NV50_PDISPLAY_SOR_CLK 0x00614000 +# define NV50_PDISPLAY_SOR_CLK__LEN 0x3 +# define NV50_PDISPLAY_SOR_CLK_CLK_CTRL2(i) (0x00614300+(i)*0x800) + +# define NV50_PDISPLAY_DAC_REGS 0x0061a000 +# define NV50_PDISPLAY_DAC_REGS__LEN 0x3 +# define NV50_PDISPLAY_DAC_REGS__ESIZE 0x800 +# define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(i) (0x0061a004+(i)*0x800) +# define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_HSYNC_OFF (1<<0) +# define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_VSYNC_OFF (1<<2) +# define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_BLANKED (1<<4) +# define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_OFF (1<<6) +# define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_PENDING (1<<31) +# define NV50_PDISPLAY_DAC_REGS_LOAD_CTRL(i) (0x0061a00c+(i)*0x800) +# define NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_ACTIVE (1<<20) +# define NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_PRESENT 0x38000000 +# define NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_PRESENT__SHIFT 29 +# define NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_DONE (1<<31) +# define NV50_PDISPLAY_DAC_REGS_CLK_CTRL1(i) (0x0061a010+(i)*0x800) +# define NV50_PDISPLAY_DAC_REGS_CLK_CTRL1_CONNECTED 0x00000600 +# define NV50_PDISPLAY_DAC_REGS_CLK_CTRL1_CONNECTED__SHIFT 9 + +# define NV50_PDISPLAY_SOR_REGS 0x0061c000 +# define NV50_PDISPLAY_SOR_REGS__LEN 0x2 +# define NV50_PDISPLAY_SOR_REGS__ESIZE 0x800 +# define NV50_PDISPLAY_SOR_REGS_DPMS_CTRL(i) (0x0061c004+(i)*0x800) +# define NV50_PDISPLAY_SOR_REGS_DPMS_CTRL_ON (1<<0) +# define NV50_PDISPLAY_SOR_REGS_DPMS_CTRL_PENDING (1<<31) +# define NV50_PDISPLAY_SOR_REGS_CLK_CTRL1(i) (0x0061c008+(i)*0x800) +# define NV50_PDISPLAY_SOR_REGS_CLK_CTRL1_CONNECTED 0x00000600 +# define NV50_PDISPLAY_SOR_REGS_CLK_CTRL1_CONNECTED__SHIFT 9 +# define NV50_PDISPLAY_SOR_REGS_UNK_00C(i) (0x0061c00c+(i)*0x800) +# define NV50_PDISPLAY_SOR_REGS_UNK_010(i) (0x0061c010+(i)*0x800) +# define NV50_PDISPLAY_SOR_REGS_UNK_014(i) (0x0061c014+(i)*0x800) +# define NV50_PDISPLAY_SOR_REGS_UNK_018(i) (0x0061c018+(i)*0x800) +# define NV50_PDISPLAY_SOR_REGS_DPMS_STATE(i) (0x0061c030+(i)*0x800) +# define NV50_PDISPLAY_SOR_REGS_DPMS_STATE_ACTIVE 0x00030000 +# define NV50_PDISPLAY_SOR_REGS_DPMS_STATE_ACTIVE__SHIFT 16 +# define NV50_PDISPLAY_SOR_REGS_DPMS_STATE_BLANKED (1<<19) +# define NV50_PDISPLAY_SOR_REGS_DPMS_STATE_WAIT (1<<28) + + +#define NV50_UNK640000 0x00640000 +#define NV50_UNK640000__LEN 0x6 +#define NV50_UNK640000__ESIZE 0x1000 +# define NV50_UNK640000_UNK_000(i) (0x00640000+(i)*0x1000) + +#define NV50_HW_CURSOR 0x00647000 +#define NV50_HW_CURSOR__LEN 0x2 +#define NV50_HW_CURSOR__ESIZE 0x1000 +# define NV50_HW_CURSOR_POS_CTRL(i) (0x00647080+(i)*0x1000) +# define NV50_HW_CURSOR_POS(i) (0x00647084+(i)*0x1000) diff --git a/shared-core/nouveau_state.c b/shared-core/nouveau_state.c index afddca0a..34a5197b 100644 --- a/shared-core/nouveau_state.c +++ b/shared-core/nouveau_state.c @@ -28,6 +28,8 @@ #include "drm_sarea.h" #include "nouveau_drv.h" #include "nouveau_drm.h" +#include "nv50_kms_wrapper.h" +#include "nv50_fbcon.h" static int nouveau_init_card_mappings(struct drm_device *dev) { @@ -364,6 +366,14 @@ nouveau_card_init(struct drm_device *dev) if (ret) return ret; dev_priv->init_state = NOUVEAU_CARD_INIT_DONE; + + if (drm_core_check_feature(dev, DRIVER_MODESET)) + if (dev_priv->card_type >= NV_50) { + nv50_kms_init(dev); + //nv50_kms_connector_detect_all(dev); + nv50_fbcon_init(dev); + } + return 0; } @@ -412,8 +422,7 @@ void nouveau_preclose(struct drm_device *dev, struct drm_file *file_priv) nouveau_mem_release(file_priv,dev_priv->pci_heap); } -/* first module load, setup the mmio/fb mapping */ -int nouveau_firstopen(struct drm_device *dev) +int nouveau_setup_mappings(struct drm_device *dev) { #if defined(__powerpc__) struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -459,6 +468,16 @@ int nouveau_firstopen(struct drm_device *dev) return 0; } +/* first module load, setup the mmio/fb mapping */ +/* KMS: we need mmio at load time, not when the first drm client opens. */ +int nouveau_firstopen(struct drm_device *dev) +{ + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return 0; + + return nouveau_setup_mappings(dev); +} + #define NV40_CHIPSET_MASK 0x00000baf #define NV44_CHIPSET_MASK 0x00005450 @@ -542,6 +561,10 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) return -EINVAL; } + /* For those who think they want to be funny. */ + if (dev_priv->card_type < NV_50) + dev->driver->driver_features &= ~DRIVER_MODESET; + /* Special flags */ if (dev->pci_device == 0x01a0) { dev_priv->flags |= NV_NFORCE; @@ -551,10 +574,23 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) dev->dev_private = (void *)dev_priv; + /* init card now, otherwise bad things happen */ + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + int rval = 0; + + rval = nouveau_setup_mappings(dev); + if (rval != 0) + return rval; + + rval = nouveau_card_init(dev); + if (rval != 0) + return rval; + } + return 0; } -void nouveau_lastclose(struct drm_device *dev) +void nouveau_close(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -570,8 +606,23 @@ void nouveau_lastclose(struct drm_device *dev) } } +/* KMS: we need mmio at load time, not when the first drm client opens. */ +void nouveau_lastclose(struct drm_device *dev) +{ + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return; + + nouveau_close(dev); +} + int nouveau_unload(struct drm_device *dev) { + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + nv50_kms_destroy(dev); + nv50_fbcon_destroy(dev); + nouveau_close(dev); + } + drm_free(dev->dev_private, sizeof(*dev->dev_private), DRM_MEM_DRIVER); dev->dev_private = NULL; return 0; diff --git a/shared-core/r300_cmdbuf.c b/shared-core/r300_cmdbuf.c index 7546e839..f39a7afd 100644 --- a/shared-core/r300_cmdbuf.c +++ b/shared-core/r300_cmdbuf.c @@ -35,6 +35,7 @@ #include "drm.h" #include "radeon_drm.h" #include "radeon_drv.h" +#include "radeon_reg.h" #include "r300_reg.h" #define R300_SIMULTANEOUS_CLIPRECTS 4 @@ -166,8 +167,6 @@ void r300_init_reg_flags(struct drm_device *dev) for(i=((reg)>>2);i<((reg)>>2)+(count);i++)\ r300_reg_flags[i]|=(mark); -#define MARK_SAFE 1 -#define MARK_CHECK_OFFSET 2 #define ADD_RANGE(reg, count) ADD_RANGE_MARK(reg, count, MARK_SAFE) @@ -247,6 +246,11 @@ void r300_init_reg_flags(struct drm_device *dev) ADD_RANGE(R300_VAP_INPUT_ROUTE_0_0, 8); ADD_RANGE(R300_VAP_INPUT_ROUTE_1_0, 8); + ADD_RANGE(R500_SU_REG_DEST, 1); + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV410) { + ADD_RANGE(R300_DST_PIPE_CONFIG, 1); + } + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV515) { ADD_RANGE(R500_VAP_INDEX_OFFSET, 1); ADD_RANGE(R500_US_CONFIG, 2); @@ -257,6 +261,7 @@ void r300_init_reg_flags(struct drm_device *dev) ADD_RANGE(R500_RB3D_COLOR_CLEAR_VALUE_AR, 2); ADD_RANGE(R500_RB3D_CONSTANT_COLOR_AR, 2); ADD_RANGE(R500_ZB_FIFO_SIZE, 2); + ADD_RANGE(R500_GA_US_VECTOR_INDEX, 2); } else { ADD_RANGE(R300_PFS_CNTL_0, 3); ADD_RANGE(R300_PFS_NODE_0, 4); @@ -269,9 +274,112 @@ void r300_init_reg_flags(struct drm_device *dev) ADD_RANGE(R300_RS_ROUTE_0, 8); } + + /* add 2d blit engine registers for DDX */ + ADD_RANGE(RADEON_SRC_Y_X, 3); /* 1434, 1438, 143c, + SRC_Y_X, DST_Y_X, DST_HEIGHT_WIDTH + */ + ADD_RANGE(RADEON_DP_GUI_MASTER_CNTL, 1); /* 146c */ + ADD_RANGE(RADEON_DP_BRUSH_BKGD_CLR, 2); /* 1478, 147c */ + ADD_RANGE(RADEON_DP_SRC_FRGD_CLR, 2); /* 15d8, 15dc */ + ADD_RANGE(RADEON_DP_CNTL, 1); /* 16c0 */ + ADD_RANGE(RADEON_DP_WRITE_MASK, 1); /* 16cc */ + ADD_RANGE(RADEON_DEFAULT_SC_BOTTOM_RIGHT, 1); /* 16e8 */ + + ADD_RANGE(RADEON_DSTCACHE_CTLSTAT, 1); + ADD_RANGE(RADEON_WAIT_UNTIL, 1); + + ADD_RANGE_MARK(RADEON_DST_OFFSET, 1, MARK_CHECK_OFFSET); + ADD_RANGE_MARK(RADEON_SRC_OFFSET, 1, MARK_CHECK_OFFSET); + + ADD_RANGE_MARK(RADEON_DST_PITCH_OFFSET, 1, MARK_CHECK_OFFSET); + ADD_RANGE_MARK(RADEON_SRC_PITCH_OFFSET, 1, MARK_CHECK_OFFSET); + + /* TODO SCISSOR */ + ADD_RANGE_MARK(R300_SC_SCISSOR0, 2, MARK_CHECK_SCISSOR); + + ADD_RANGE(R300_SC_CLIP_0_A, 2); + ADD_RANGE(R300_SC_CLIP_RULE, 1); + ADD_RANGE(R300_SC_SCREENDOOR, 1); + + ADD_RANGE(R300_VAP_PVS_CODE_CNTL_0, 4); + ADD_RANGE(R300_VAP_PVS_VECTOR_INDX_REG, 2); + + if (dev_priv->chip_family <= CHIP_RV280) { + ADD_RANGE(RADEON_RE_TOP_LEFT, 1); + ADD_RANGE(RADEON_RE_WIDTH_HEIGHT, 1); + ADD_RANGE(RADEON_AUX_SC_CNTL, 1); + ADD_RANGE(RADEON_RB3D_DSTCACHE_CTLSTAT, 1); + ADD_RANGE(RADEON_RB3D_PLANEMASK, 1); + ADD_RANGE(RADEON_SE_CNTL, 1); + ADD_RANGE(RADEON_PP_CNTL, 1); + ADD_RANGE(RADEON_RB3D_CNTL, 1); + ADD_RANGE_MARK(RADEON_RB3D_COLOROFFSET, 1, MARK_CHECK_OFFSET); + ADD_RANGE(RADEON_RB3D_COLORPITCH, 1); + ADD_RANGE(RADEON_RB3D_BLENDCNTL, 1); + + if (dev_priv->chip_family >= CHIP_R200) { + ADD_RANGE(R200_PP_CNTL_X, 1); + ADD_RANGE(R200_PP_TXMULTI_CTL_0, 1); + ADD_RANGE(R200_SE_VTX_STATE_CNTL, 1); + ADD_RANGE(R200_RE_CNTL, 1); + ADD_RANGE(R200_SE_VTE_CNTL, 1); + ADD_RANGE(R200_SE_VAP_CNTL, 1); + + ADD_RANGE(R200_PP_TXFILTER_0, 1); + ADD_RANGE(R200_PP_TXFORMAT_0, 1); + ADD_RANGE(R200_PP_TXFORMAT_X_0, 1); + ADD_RANGE(R200_PP_TXSIZE_0, 1); + ADD_RANGE(R200_PP_TXPITCH_0, 1); + ADD_RANGE(R200_PP_TFACTOR_0, 1); + + ADD_RANGE(R200_PP_TXFILTER_1, 1); + ADD_RANGE(R200_PP_TXFORMAT_1, 1); + ADD_RANGE(R200_PP_TXFORMAT_X_1, 1); + ADD_RANGE(R200_PP_TXSIZE_1, 1); + ADD_RANGE(R200_PP_TXPITCH_1, 1); + ADD_RANGE(R200_PP_TFACTOR_1, 1); + + ADD_RANGE_MARK(R200_PP_TXOFFSET_0, 1, MARK_CHECK_OFFSET); + ADD_RANGE_MARK(R200_PP_TXOFFSET_1, 1, MARK_CHECK_OFFSET); + ADD_RANGE_MARK(R200_PP_TXOFFSET_2, 1, MARK_CHECK_OFFSET); + ADD_RANGE_MARK(R200_PP_TXOFFSET_3, 1, MARK_CHECK_OFFSET); + ADD_RANGE_MARK(R200_PP_TXOFFSET_4, 1, MARK_CHECK_OFFSET); + ADD_RANGE_MARK(R200_PP_TXOFFSET_5, 1, MARK_CHECK_OFFSET); + + ADD_RANGE(R200_SE_VTX_FMT_0, 1); + ADD_RANGE(R200_SE_VTX_FMT_1, 1); + ADD_RANGE(R200_PP_TXCBLEND_0, 1); + ADD_RANGE(R200_PP_TXCBLEND2_0, 1); + ADD_RANGE(R200_PP_TXABLEND_0, 1); + ADD_RANGE(R200_PP_TXABLEND2_0, 1); + + } else { + + ADD_RANGE(RADEON_SE_COORD_FMT, 1); + ADD_RANGE(RADEON_SE_CNTL_STATUS, 1); + + ADD_RANGE(RADEON_PP_TXFILTER_0, 1); + ADD_RANGE(RADEON_PP_TXFORMAT_0, 1); + ADD_RANGE(RADEON_PP_TEX_SIZE_0, 1); + ADD_RANGE(RADEON_PP_TEX_PITCH_0, 1); + + ADD_RANGE(RADEON_PP_TXFILTER_1, 1); + ADD_RANGE(RADEON_PP_TXFORMAT_1, 1); + ADD_RANGE(RADEON_PP_TEX_SIZE_1, 1); + ADD_RANGE(RADEON_PP_TEX_PITCH_1, 1); + + ADD_RANGE(RADEON_PP_TXCBLEND_0, 1); + ADD_RANGE(RADEON_PP_TXABLEND_0, 1); + ADD_RANGE(RADEON_SE_VTX_FMT, 1); + ADD_RANGE_MARK(RADEON_PP_TXOFFSET_0, 1, MARK_CHECK_OFFSET); + ADD_RANGE_MARK(RADEON_PP_TXOFFSET_1, 1, MARK_CHECK_OFFSET); + ADD_RANGE_MARK(RADEON_PP_TXOFFSET_2, 1, MARK_CHECK_OFFSET); + } + } } -static __inline__ int r300_check_range(unsigned reg, int count) +int r300_check_range(unsigned reg, int count) { int i; if (reg & ~0xffff) @@ -282,6 +390,13 @@ static __inline__ int r300_check_range(unsigned reg, int count) return 0; } +int r300_get_reg_flags(unsigned reg) +{ + if (reg & ~0xffff) + return -1; + return r300_reg_flags[(reg >> 2)]; +} + static __inline__ int r300_emit_carefully_checked_packet0(drm_radeon_private_t * dev_priv, drm_radeon_kcmd_buffer_t @@ -811,13 +926,13 @@ static __inline__ void r300_pacify(drm_radeon_private_t *dev_priv) RING_LOCALS; cache_z = R300_ZC_FLUSH; - cache_2d = R300_RB2D_DC_FLUSH; - cache_3d = R300_RB3D_DC_FLUSH; + cache_2d = R300_DC_FLUSH_2D; + cache_3d = R300_DC_FLUSH_3D; if (!(dev_priv->track_flush & RADEON_PURGE_EMITED)) { /* we can purge, primitive where draw since last purge */ cache_z |= R300_ZC_FREE; - cache_2d |= R300_RB2D_DC_FREE; - cache_3d |= R300_RB3D_DC_FREE; + cache_2d |= R300_DC_FREE_2D; + cache_3d |= R300_DC_FREE_3D; } /* flush & purge zbuffer */ @@ -861,12 +976,12 @@ static __inline__ void r300_pacify(drm_radeon_private_t *dev_priv) * The actual age emit is done by r300_do_cp_cmdbuf, which is why you must * be careful about how this function is called. */ -static void r300_discard_buffer(struct drm_device * dev, struct drm_buf * buf) +static void r300_discard_buffer(struct drm_device * dev, struct drm_master *master, struct drm_buf * buf) { - drm_radeon_private_t *dev_priv = dev->dev_private; drm_radeon_buf_priv_t *buf_priv = buf->dev_private; + struct drm_radeon_master_private *master_priv = master->driver_priv; - buf_priv->age = ++dev_priv->sarea_priv->last_dispatch; + buf_priv->age = ++master_priv->sarea_priv->last_dispatch; buf->pending = 1; buf->used = 0; } @@ -1027,6 +1142,7 @@ int r300_do_cp_cmdbuf(struct drm_device *dev, drm_radeon_kcmd_buffer_t *cmdbuf) { drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv; struct drm_device_dma *dma = dev->dma; struct drm_buf *buf = NULL; int emit_dispatch_age = 0; @@ -1133,7 +1249,7 @@ int r300_do_cp_cmdbuf(struct drm_device *dev, } emit_dispatch_age = 1; - r300_discard_buffer(dev, buf); + r300_discard_buffer(dev, file_priv->master, buf); break; case R300_CMD_WAIT: @@ -1188,7 +1304,7 @@ int r300_do_cp_cmdbuf(struct drm_device *dev, /* Emit the vertex buffer age */ BEGIN_RING(2); - RADEON_DISPATCH_AGE(dev_priv->sarea_priv->last_dispatch); + RADEON_DISPATCH_AGE(master_priv->sarea_priv->last_dispatch); ADVANCE_RING(); } diff --git a/shared-core/r300_reg.h b/shared-core/r300_reg.h index d35dd39d..1e4631db 100644 --- a/shared-core/r300_reg.h +++ b/shared-core/r300_reg.h @@ -129,15 +129,6 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. /* END: Wild guesses */ #define R300_SE_VTE_CNTL 0x20b0 -# define R300_VPORT_X_SCALE_ENA 0x00000001 -# define R300_VPORT_X_OFFSET_ENA 0x00000002 -# define R300_VPORT_Y_SCALE_ENA 0x00000004 -# define R300_VPORT_Y_OFFSET_ENA 0x00000008 -# define R300_VPORT_Z_SCALE_ENA 0x00000010 -# define R300_VPORT_Z_OFFSET_ENA 0x00000020 -# define R300_VTX_XY_FMT 0x00000100 -# define R300_VTX_Z_FMT 0x00000200 -# define R300_VTX_W0_FMT 0x00000400 # define R300_VTX_W0_NORMALIZE 0x00000800 # define R300_VTX_ST_DENORMALIZED 0x00001000 @@ -493,7 +484,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. # define R300_OFIFO_HIGHWATER_SHIFT 22 /* two bits only */ # define R300_CUBE_FIFO_HIGHWATER_COL_SHIFT 24 -#define R300_GB_SELECT 0x401C + # define R300_GB_FOG_SELECT_C0A 0 # define R300_GB_FOG_SELECT_C1A 1 # define R300_GB_FOG_SELECT_C2A 2 @@ -955,7 +946,6 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. /* 32 bit chroma key */ #define R300_TX_CHROMA_KEY_0 0x4580 /* ff00ff00 == { 0, 1.0, 0, 1.0 } */ -#define R300_TX_BORDER_COLOR_0 0x45C0 /* END: Texture specification */ @@ -1340,7 +1330,6 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. /* gap */ -#define R300_RB3D_COLOROFFSET0 0x4E28 # define R300_COLOROFFSET_MASK 0xFFFFFFF0 /* GUESS */ #define R300_RB3D_COLOROFFSET1 0x4E2C /* GUESS */ #define R300_RB3D_COLOROFFSET2 0x4E30 /* GUESS */ @@ -1352,7 +1341,6 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. * Bit 17: 4x2 tiles * Bit 18: Extremely weird tile like, but some pixels duplicated? */ -#define R300_RB3D_COLORPITCH0 0x4E38 # define R300_COLORPITCH_MASK 0x00001FF8 /* GUESS */ # define R300_COLOR_TILE_ENABLE (1 << 16) /* GUESS */ # define R300_COLOR_MICROTILE_ENABLE (1 << 17) /* GUESS */ @@ -1365,7 +1353,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. #define R300_RB3D_COLORPITCH2 0x4E40 /* GUESS */ #define R300_RB3D_COLORPITCH3 0x4E44 /* GUESS */ -#define R300_RB3D_AARESOLVE_CTL 0x4E88 +//#define R300_RB3D_AARESOLVE_CTL 0x4E88 /* gap */ /* Guess by Vladimir. diff --git a/shared-core/radeon_cp.c b/shared-core/radeon_cp.c index c4035b89..6072d921 100644 --- a/shared-core/radeon_cp.c +++ b/shared-core/radeon_cp.c @@ -31,6 +31,7 @@ #include "drmP.h" #include "drm.h" +#include "drm_sarea.h" #include "radeon_drm.h" #include "radeon_drv.h" #include "r300_reg.h" @@ -77,6 +78,23 @@ static u32 IGP_READ_MCIND(drm_radeon_private_t *dev_priv, int addr) return RS480_READ_MCIND(dev_priv, addr); } +u32 radeon_read_mc_reg(drm_radeon_private_t *dev_priv, int addr) +{ + if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) + return IGP_READ_MCIND(dev_priv, addr); + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV515) + return R500_READ_MCIND(dev_priv, addr); + return 0; +} + +void radeon_write_mc_reg(drm_radeon_private_t *dev_priv, u32 addr, u32 val) +{ + if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) + IGP_WRITE_MCIND(addr, val); + else if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV515) + R500_WRITE_MCIND(addr, val); +} + u32 radeon_read_fb_location(drm_radeon_private_t *dev_priv) { @@ -85,33 +103,71 @@ u32 radeon_read_fb_location(drm_radeon_private_t *dev_priv) else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) || ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS740)) return RS690_READ_MCIND(dev_priv, RS690_MC_FB_LOCATION); + else if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV770) + return RADEON_READ(R700_MC_VM_FB_LOCATION); + else if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + return RADEON_READ(R600_MC_VM_FB_LOCATION); else if ((dev_priv->flags & RADEON_FAMILY_MASK) > CHIP_RV515) return R500_READ_MCIND(dev_priv, R520_MC_FB_LOCATION); else return RADEON_READ(RADEON_MC_FB_LOCATION); } -static void radeon_write_fb_location(drm_radeon_private_t *dev_priv, u32 fb_loc) +void radeon_read_agp_location(drm_radeon_private_t *dev_priv, u32 *agp_lo, u32 *agp_hi) +{ + if (dev_priv->chip_family == CHIP_RV770) { + *agp_lo = RADEON_READ(R600_MC_VM_AGP_BOT); + *agp_hi = RADEON_READ(R600_MC_VM_AGP_TOP); + } else if (dev_priv->chip_family == CHIP_R600) { + *agp_lo = RADEON_READ(R600_MC_VM_AGP_BOT); + *agp_hi = RADEON_READ(R600_MC_VM_AGP_TOP); + } else if (dev_priv->chip_family == CHIP_RV515) { + *agp_lo = radeon_read_mc_reg(dev_priv, RV515_MC_AGP_LOCATION); + *agp_hi = 0; + } else if (dev_priv->chip_family == CHIP_RS600) { + *agp_lo = 0; + *agp_hi = 0; + } else if (dev_priv->chip_family == CHIP_RS690 || + dev_priv->chip_family == CHIP_RS740) { + *agp_lo = radeon_read_mc_reg(dev_priv, RS690_MC_AGP_LOCATION); + *agp_hi = 0; + } else if (dev_priv->chip_family >= CHIP_R520) { + *agp_lo = radeon_read_mc_reg(dev_priv, R520_MC_AGP_LOCATION); + *agp_hi = 0; + } else { + *agp_lo = RADEON_READ(RADEON_MC_AGP_LOCATION); + *agp_hi = 0; + } +} + +void radeon_write_fb_location(drm_radeon_private_t *dev_priv, u32 fb_loc) { if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV515) R500_WRITE_MCIND(RV515_MC_FB_LOCATION, fb_loc); else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) || ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS740)) RS690_WRITE_MCIND(RS690_MC_FB_LOCATION, fb_loc); + else if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV770) + RADEON_WRITE(R700_MC_VM_FB_LOCATION, fb_loc); + else if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + RADEON_WRITE(R600_MC_VM_FB_LOCATION, fb_loc); else if ((dev_priv->flags & RADEON_FAMILY_MASK) > CHIP_RV515) R500_WRITE_MCIND(R520_MC_FB_LOCATION, fb_loc); else RADEON_WRITE(RADEON_MC_FB_LOCATION, fb_loc); } -static void radeon_write_agp_location(drm_radeon_private_t *dev_priv, u32 agp_loc) +static void radeon_write_agp_location(drm_radeon_private_t *dev_priv, u32 agp_loc, u32 agp_loc_hi) { if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV515) R500_WRITE_MCIND(RV515_MC_AGP_LOCATION, agp_loc); else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) || ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS740)) RS690_WRITE_MCIND(RS690_MC_AGP_LOCATION, agp_loc); - else if ((dev_priv->flags & RADEON_FAMILY_MASK) > CHIP_RV515) + else if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) { + RADEON_WRITE(R600_MC_VM_AGP_BOT, agp_loc); + RADEON_WRITE(R600_MC_VM_AGP_TOP, agp_loc_hi); + } else if ((dev_priv->flags & RADEON_FAMILY_MASK) > CHIP_RV515) R500_WRITE_MCIND(R520_MC_AGP_LOCATION, agp_loc); else RADEON_WRITE(RADEON_MC_AGP_LOCATION, agp_loc); @@ -143,20 +199,116 @@ static void radeon_write_agp_base(drm_radeon_private_t *dev_priv, u64 agp_base) } } -static int RADEON_READ_PLL(struct drm_device * dev, int addr) +void radeon_enable_bm(struct drm_radeon_private *dev_priv) { - drm_radeon_private_t *dev_priv = dev->dev_private; + u32 tmp; + /* Turn on bus mastering */ + if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS740)) { + /* rs600/rs690/rs740 */ + tmp = RADEON_READ(RADEON_BUS_CNTL) & ~RS600_BUS_MASTER_DIS; + RADEON_WRITE(RADEON_BUS_CNTL, tmp); + } else if (((dev_priv->flags & RADEON_FAMILY_MASK) <= CHIP_RV350) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R420) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS400) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS480)) { + /* r1xx, r2xx, r300, r(v)350, r420/r481, rs400/rs480 */ + tmp = RADEON_READ(RADEON_BUS_CNTL) & ~RADEON_BUS_MASTER_DIS; + RADEON_WRITE(RADEON_BUS_CNTL, tmp); + } /* PCIE cards appears to not need this */ +} + +void radeon_pll_errata_after_index(struct drm_radeon_private *dev_priv) +{ + if (!(dev_priv->pll_errata & CHIP_ERRATA_PLL_DUMMYREADS)) + return; + + (void)RADEON_READ(RADEON_CLOCK_CNTL_DATA); + (void)RADEON_READ(RADEON_CRTC_GEN_CNTL); +} + +void radeon_pll_errata_after_data(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->pll_errata & CHIP_ERRATA_PLL_DELAY) + udelay(5000); + + /* 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->pll_errata & CHIP_ERRATA_R300_CG) { + uint32_t save, tmp; + + save = RADEON_READ(RADEON_CLOCK_CNTL_INDEX); + tmp = save & ~(0x3f | RADEON_PLL_WR_EN); + RADEON_WRITE(RADEON_CLOCK_CNTL_INDEX, tmp); + tmp = RADEON_READ(RADEON_CLOCK_CNTL_DATA); + RADEON_WRITE(RADEON_CLOCK_CNTL_INDEX, save); + } +} + +u32 RADEON_READ_PLL(struct drm_radeon_private *dev_priv, int addr) +{ + uint32_t data; + + RADEON_WRITE8(RADEON_CLOCK_CNTL_INDEX, addr & 0x3f); + radeon_pll_errata_after_index(dev_priv); + data = RADEON_READ(RADEON_CLOCK_CNTL_DATA); + radeon_pll_errata_after_data(dev_priv); + return data; +} - RADEON_WRITE8(RADEON_CLOCK_CNTL_INDEX, addr & 0x1f); - return RADEON_READ(RADEON_CLOCK_CNTL_DATA); +void RADEON_WRITE_PLL(struct drm_radeon_private *dev_priv, int addr, uint32_t data) +{ + RADEON_WRITE8(RADEON_CLOCK_CNTL_INDEX, ((addr & 0x3f) | RADEON_PLL_WR_EN)); + radeon_pll_errata_after_index(dev_priv); + RADEON_WRITE(RADEON_CLOCK_CNTL_DATA, data); + radeon_pll_errata_after_data(dev_priv); } -static u32 RADEON_READ_PCIE(drm_radeon_private_t *dev_priv, int addr) +u32 RADEON_READ_PCIE(drm_radeon_private_t *dev_priv, int addr) { RADEON_WRITE8(RADEON_PCIE_INDEX, addr & 0xff); return RADEON_READ(RADEON_PCIE_DATA); } +/* ATOM accessor methods */ +static uint32_t cail_mc_read(struct card_info *info, uint32_t reg) +{ + uint32_t ret = radeon_read_mc_reg(info->dev->dev_private, reg); + + // DRM_DEBUG("(%x) = %x\n", reg, ret); + return ret; +} + +static void cail_mc_write(struct card_info *info, uint32_t reg, uint32_t val) +{ + // DRM_DEBUG("(%x, %x)\n", reg, val); + radeon_write_mc_reg(info->dev->dev_private, reg, val); +} + +static void cail_reg_write(struct card_info *info, uint32_t reg, uint32_t val) +{ + drm_radeon_private_t *dev_priv = info->dev->dev_private; + + // DRM_DEBUG("(%x, %x)\n", reg*4, val); + RADEON_WRITE(reg*4, val); +} + +static uint32_t cail_reg_read(struct card_info *info, uint32_t reg) +{ + uint32_t ret; + drm_radeon_private_t *dev_priv = info->dev->dev_private; + + ret = RADEON_READ(reg*4); + // DRM_DEBUG("(%x) = %x\n", reg*4, ret); + return ret; +} + #if RADEON_FIFO_DEBUG static void radeon_status(drm_radeon_private_t * dev_priv) { @@ -239,7 +391,7 @@ static int radeon_do_wait_for_fifo(drm_radeon_private_t * dev_priv, int entries) return -EBUSY; } -static int radeon_do_wait_for_idle(drm_radeon_private_t * dev_priv) +int radeon_do_wait_for_idle(drm_radeon_private_t * dev_priv) { int i, ret; @@ -299,7 +451,7 @@ static void radeon_init_pipes(drm_radeon_private_t * dev_priv) } if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV515) { - RADEON_WRITE_PLL(R500_DYN_SCLK_PWMEM_PIPE, (1 | ((gb_pipe_sel >> 8) & 0xf) << 4)); + RADEON_WRITE_PLL(dev_priv, R500_DYN_SCLK_PWMEM_PIPE, (1 | ((gb_pipe_sel >> 8) & 0xf) << 4)); RADEON_WRITE(R500_SU_REG_DEST, ((1 << dev_priv->num_gb_pipes) - 1)); } RADEON_WRITE(R300_GB_TILE_CONFIG, gb_tile_config); @@ -406,7 +558,6 @@ static void radeon_do_cp_flush(drm_radeon_private_t * dev_priv) DRM_DEBUG("\n"); #if 0 u32 tmp; - tmp = RADEON_READ(RADEON_CP_RB_WPTR) | (1 << 31); RADEON_WRITE(RADEON_CP_RB_WPTR, tmp); #endif @@ -447,10 +598,15 @@ static void radeon_do_cp_start(drm_radeon_private_t * dev_priv) BEGIN_RING(8); /* isync can only be written through cp on r5xx write it here */ OUT_RING(CP_PACKET0(RADEON_ISYNC_CNTL, 0)); + if (dev_priv->chip_family > CHIP_RV280) + OUT_RING(RADEON_ISYNC_ANY2D_IDLE3D | + RADEON_ISYNC_ANY3D_IDLE2D | + RADEON_ISYNC_WAIT_IDLEGUI | + dev_priv->mm_enabled ? 0 : RADEON_ISYNC_CPSCRATCH_IDLEGUI); + else OUT_RING(RADEON_ISYNC_ANY2D_IDLE3D | RADEON_ISYNC_ANY3D_IDLE2D | - RADEON_ISYNC_WAIT_IDLEGUI | - RADEON_ISYNC_CPSCRATCH_IDLEGUI); + RADEON_ISYNC_WAIT_IDLEGUI); RADEON_PURGE_CACHE(); RADEON_PURGE_ZCACHE(); RADEON_WAIT_UNTIL_IDLE(); @@ -501,15 +657,15 @@ static int radeon_do_engine_reset(struct drm_device * dev) if ((dev_priv->flags & RADEON_FAMILY_MASK) <= CHIP_RV410) { /* may need something similar for newer chips */ clock_cntl_index = RADEON_READ(RADEON_CLOCK_CNTL_INDEX); - mclk_cntl = RADEON_READ_PLL(dev, RADEON_MCLK_CNTL); - - RADEON_WRITE_PLL(RADEON_MCLK_CNTL, (mclk_cntl | - RADEON_FORCEON_MCLKA | - RADEON_FORCEON_MCLKB | - RADEON_FORCEON_YCLKA | - RADEON_FORCEON_YCLKB | - RADEON_FORCEON_MC | - RADEON_FORCEON_AIC)); + mclk_cntl = RADEON_READ_PLL(dev_priv, RADEON_MCLK_CNTL); + + RADEON_WRITE_PLL(dev_priv, RADEON_MCLK_CNTL, (mclk_cntl | + RADEON_FORCEON_MCLKA | + RADEON_FORCEON_MCLKB | + RADEON_FORCEON_YCLKA | + RADEON_FORCEON_YCLKB | + RADEON_FORCEON_MC | + RADEON_FORCEON_AIC)); } rbbm_soft_reset = RADEON_READ(RADEON_RBBM_SOFT_RESET); @@ -534,7 +690,7 @@ static int radeon_do_engine_reset(struct drm_device * dev) RADEON_READ(RADEON_RBBM_SOFT_RESET); if ((dev_priv->flags & RADEON_FAMILY_MASK) <= CHIP_RV410) { - RADEON_WRITE_PLL(RADEON_MCLK_CNTL, mclk_cntl); + RADEON_WRITE_PLL(dev_priv, RADEON_MCLK_CNTL, mclk_cntl); RADEON_WRITE(RADEON_CLOCK_CNTL_INDEX, clock_cntl_index); RADEON_WRITE(RADEON_RBBM_SOFT_RESET, rbbm_soft_reset); } @@ -550,7 +706,8 @@ static int radeon_do_engine_reset(struct drm_device * dev) dev_priv->cp_running = 0; /* Reset any pending vertex, indirect buffers */ - radeon_freelist_reset(dev); + if (dev->dma) + radeon_freelist_reset(dev); return 0; } @@ -559,7 +716,6 @@ static void radeon_cp_init_ring_buffer(struct drm_device * dev, drm_radeon_private_t * dev_priv) { u32 ring_start, cur_read_ptr; - u32 tmp; /* Initialize the memory controller. With new memory map, the fb location * is not changed, it should have been properly initialized already. Part @@ -568,9 +724,13 @@ static void radeon_cp_init_ring_buffer(struct drm_device * dev, */ if (!dev_priv->new_memmap) radeon_write_fb_location(dev_priv, - ((dev_priv->gart_vm_start - 1) & 0xffff0000) - | (dev_priv->fb_location >> 16)); - + ((dev_priv->gart_vm_start - 1) & 0xffff0000) + | (dev_priv->fb_location >> 16)); + + if (dev_priv->mm.ring.bo) { + ring_start = dev_priv->mm.ring.bo->offset + + dev_priv->gart_vm_start; + } else #if __OS_HAS_AGP if (dev_priv->flags & RADEON_IS_AGP) { radeon_write_agp_base(dev_priv, dev->agp->base); @@ -578,7 +738,7 @@ static void radeon_cp_init_ring_buffer(struct drm_device * dev, radeon_write_agp_location(dev_priv, (((dev_priv->gart_vm_start - 1 + dev_priv->gart_size) & 0xffff0000) | - (dev_priv->gart_vm_start >> 16))); + (dev_priv->gart_vm_start >> 16)), 0); ring_start = (dev_priv->cp_ring->offset - dev->agp->base @@ -600,6 +760,12 @@ static void radeon_cp_init_ring_buffer(struct drm_device * dev, SET_RING_HEAD(dev_priv, cur_read_ptr); dev_priv->ring.tail = cur_read_ptr; + + if (dev_priv->mm.ring_read.bo) { + RADEON_WRITE(RADEON_CP_RB_RPTR_ADDR, + dev_priv->mm.ring_read.bo->offset + + dev_priv->gart_vm_start); + } else #if __OS_HAS_AGP if (dev_priv->flags & RADEON_IS_AGP) { RADEON_WRITE(RADEON_CP_RB_RPTR_ADDR, @@ -645,60 +811,76 @@ static void radeon_cp_init_ring_buffer(struct drm_device * dev, RADEON_WRITE(RADEON_SCRATCH_ADDR, RADEON_READ(RADEON_CP_RB_RPTR_ADDR) + RADEON_SCRATCH_REG_OFFSET); - dev_priv->scratch = ((__volatile__ u32 *) - dev_priv->ring_rptr->handle + - (RADEON_SCRATCH_REG_OFFSET / sizeof(u32))); + if (dev_priv->mm.ring_read.bo) + dev_priv->scratch = ((__volatile__ u32 *) + dev_priv->mm.ring_read.kmap.virtual + + (RADEON_SCRATCH_REG_OFFSET / sizeof(u32))); + else + dev_priv->scratch = ((__volatile__ u32 *) + dev_priv->ring_rptr->handle + + (RADEON_SCRATCH_REG_OFFSET / sizeof(u32))); + + if (dev_priv->chip_family >= CHIP_R300) + RADEON_WRITE(RADEON_SCRATCH_UMSK, 0x7f); + else + RADEON_WRITE(RADEON_SCRATCH_UMSK, 0x1f); - RADEON_WRITE(RADEON_SCRATCH_UMSK, 0x7); + radeon_enable_bm(dev_priv); - /* Turn on bus mastering */ - if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) || - ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS740)) { - /* rs600/rs690/rs740 */ - tmp = RADEON_READ(RADEON_BUS_CNTL) & ~RS600_BUS_MASTER_DIS; - RADEON_WRITE(RADEON_BUS_CNTL, tmp); - } else if (((dev_priv->flags & RADEON_FAMILY_MASK) <= CHIP_RV350) || - ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R420) || - ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS400) || - ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS480)) { - /* r1xx, r2xx, r300, r(v)350, r420/r481, rs400/rs480 */ - tmp = RADEON_READ(RADEON_BUS_CNTL) & ~RADEON_BUS_MASTER_DIS; - RADEON_WRITE(RADEON_BUS_CNTL, tmp); - } /* PCIE cards appears to not need this */ + dev_priv->scratch[0] = 0; + RADEON_WRITE(RADEON_LAST_FRAME_REG, 0); + + dev_priv->scratch[1] = 0; + RADEON_WRITE(RADEON_LAST_DISPATCH_REG, 0); + + dev_priv->scratch[2] = 0; + RADEON_WRITE(RADEON_LAST_CLEAR_REG, 0); - dev_priv->sarea_priv->last_frame = dev_priv->scratch[0] = 0; - RADEON_WRITE(RADEON_LAST_FRAME_REG, dev_priv->sarea_priv->last_frame); + dev_priv->scratch[3] = 0; + RADEON_WRITE(RADEON_LAST_SWI_REG, 0); - dev_priv->sarea_priv->last_dispatch = dev_priv->scratch[1] = 0; - RADEON_WRITE(RADEON_LAST_DISPATCH_REG, - dev_priv->sarea_priv->last_dispatch); + dev_priv->scratch[4] = 0; + RADEON_WRITE(RADEON_SCRATCH_REG4, 0); - dev_priv->sarea_priv->last_clear = dev_priv->scratch[2] = 0; - RADEON_WRITE(RADEON_LAST_CLEAR_REG, dev_priv->sarea_priv->last_clear); + dev_priv->scratch[6] = 0; + RADEON_WRITE(RADEON_SCRATCH_REG6, 0); radeon_do_wait_for_idle(dev_priv); /* Sync everything up */ + if (dev_priv->chip_family > CHIP_RV280) { RADEON_WRITE(RADEON_ISYNC_CNTL, (RADEON_ISYNC_ANY2D_IDLE3D | RADEON_ISYNC_ANY3D_IDLE2D | RADEON_ISYNC_WAIT_IDLEGUI | RADEON_ISYNC_CPSCRATCH_IDLEGUI)); - + } else { + RADEON_WRITE(RADEON_ISYNC_CNTL, + (RADEON_ISYNC_ANY2D_IDLE3D | + RADEON_ISYNC_ANY3D_IDLE2D | + RADEON_ISYNC_WAIT_IDLEGUI)); + } } static void radeon_test_writeback(drm_radeon_private_t * dev_priv) { - u32 tmp; + u32 tmp, scratch1_store; + void *ring_read_ptr; + + if (dev_priv->mm.ring_read.bo) + ring_read_ptr = dev_priv->mm.ring_read.kmap.virtual; + else + ring_read_ptr = dev_priv->ring_rptr->handle; + scratch1_store = RADEON_READ(RADEON_SCRATCH_REG1); /* Writeback doesn't seem to work everywhere, test it here and possibly * enable it if it appears to work */ - DRM_WRITE32(dev_priv->ring_rptr, RADEON_SCRATCHOFF(1), 0); + writel(0, ring_read_ptr + RADEON_SCRATCHOFF(1)); RADEON_WRITE(RADEON_SCRATCH_REG1, 0xdeadbeef); for (tmp = 0; tmp < dev_priv->usec_timeout; tmp++) { - if (DRM_READ32(dev_priv->ring_rptr, RADEON_SCRATCHOFF(1)) == + if (readl(ring_read_ptr + RADEON_SCRATCHOFF(1)) == 0xdeadbeef) break; DRM_UDELAY(1); @@ -716,6 +898,9 @@ static void radeon_test_writeback(drm_radeon_private_t * dev_priv) DRM_INFO("writeback forced off\n"); } + /* write back previous value */ + RADEON_WRITE(RADEON_SCRATCH_REG1, scratch1_store); + if (!dev_priv->writeback_works) { /* Disable writeback to avoid unnecessary bus master transfers */ RADEON_WRITE(RADEON_CP_RB_CNTL, RADEON_READ(RADEON_CP_RB_CNTL) | RADEON_RB_NO_UPDATE); @@ -766,7 +951,7 @@ static void radeon_set_igpgart(drm_radeon_private_t * dev_priv, int on) temp = (((dev_priv->gart_vm_start - 1 + dev_priv->gart_size) & 0xffff0000) | (dev_priv->gart_vm_start >> 16)); - radeon_write_agp_location(dev_priv, temp); + radeon_write_agp_location(dev_priv, temp, 0); temp = IGP_READ_MCIND(dev_priv, RS480_AGP_ADDRESS_SPACE_SIZE); IGP_WRITE_MCIND(RS480_AGP_ADDRESS_SPACE_SIZE, (RS480_GART_EN | @@ -814,7 +999,7 @@ static void radeon_set_pciegart(drm_radeon_private_t * dev_priv, int on) dev_priv->gart_vm_start + dev_priv->gart_size - 1); - radeon_write_agp_location(dev_priv, 0xffffffc0); /* ?? */ + radeon_write_agp_location(dev_priv, 0xffffffc0, 0); /* ?? */ RADEON_WRITE_PCIE(RADEON_PCIE_TX_GART_CNTL, RADEON_PCIE_TX_GART_EN); @@ -825,7 +1010,7 @@ static void radeon_set_pciegart(drm_radeon_private_t * dev_priv, int on) } /* Enable or disable PCI GART on the chip */ -static void radeon_set_pcigart(drm_radeon_private_t * dev_priv, int on) +void radeon_set_pcigart(drm_radeon_private_t * dev_priv, int on) { u32 tmp; @@ -859,7 +1044,7 @@ static void radeon_set_pcigart(drm_radeon_private_t * dev_priv, int on) /* Turn off AGP aperture -- is this required for PCI GART? */ - radeon_write_agp_location(dev_priv, 0xffffffc0); + radeon_write_agp_location(dev_priv, 0xffffffc0, 0); RADEON_WRITE(RADEON_AGP_COMMAND, 0); /* clear AGP_COMMAND */ } else { RADEON_WRITE(RADEON_AIC_CNTL, @@ -867,9 +1052,11 @@ static void radeon_set_pcigart(drm_radeon_private_t * dev_priv, int on) } } -static int radeon_do_init_cp(struct drm_device * dev, drm_radeon_init_t * init) +static int radeon_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init, + struct drm_file *file_priv) { drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv; DRM_DEBUG("\n"); @@ -985,8 +1172,8 @@ static int radeon_do_init_cp(struct drm_device * dev, drm_radeon_init_t * init) dev_priv->buffers_offset = init->buffers_offset; dev_priv->gart_textures_offset = init->gart_textures_offset; - dev_priv->sarea = drm_getsarea(dev); - if (!dev_priv->sarea) { + master_priv->sarea = drm_getsarea(dev); + if (!master_priv->sarea) { DRM_ERROR("could not find sarea!\n"); radeon_do_cleanup_cp(dev); return -EINVAL; @@ -1022,10 +1209,6 @@ static int radeon_do_init_cp(struct drm_device * dev, drm_radeon_init_t * init) } } - dev_priv->sarea_priv = - (drm_radeon_sarea_t *) ((u8 *) dev_priv->sarea->handle + - init->sarea_priv_offset); - #if __OS_HAS_AGP if (dev_priv->flags & RADEON_IS_AGP) { drm_core_ioremap(dev_priv->cp_ring, dev); @@ -1139,9 +1322,7 @@ static int radeon_do_init_cp(struct drm_device * dev, drm_radeon_init_t * init) dev_priv->ring.rptr_update = /* init->rptr_update */ 4096; dev_priv->ring.rptr_update_l2qw = drm_order( /* init->rptr_update */ 4096 / 8); - dev_priv->ring.fetch_size = /* init->fetch_size */ 32; - dev_priv->ring.fetch_size_l2ow = drm_order( /* init->fetch_size */ 32 / 16); - + dev_priv->ring.fetch_size_l2ow = 2; dev_priv->ring.tail_mask = (dev_priv->ring.size / sizeof(u32)) - 1; dev_priv->ring.high_mark = RADEON_RING_HIGH_MARK; @@ -1156,28 +1337,40 @@ static int radeon_do_init_cp(struct drm_device * dev, drm_radeon_init_t * init) dev_priv->gart_info.table_mask = DMA_BIT_MASK(32); /* if we have an offset set from userspace */ if (dev_priv->pcigart_offset_set) { - dev_priv->gart_info.bus_addr = - dev_priv->pcigart_offset + dev_priv->fb_location; - dev_priv->gart_info.mapping.offset = - dev_priv->pcigart_offset + dev_priv->fb_aper_offset; - dev_priv->gart_info.mapping.size = - dev_priv->gart_info.table_size; - - drm_core_ioremap_wc(&dev_priv->gart_info.mapping, dev); - dev_priv->gart_info.addr = - dev_priv->gart_info.mapping.handle; - - if (dev_priv->flags & RADEON_IS_PCIE) - dev_priv->gart_info.gart_reg_if = DRM_ATI_GART_PCIE; - else - dev_priv->gart_info.gart_reg_if = DRM_ATI_GART_PCI; - dev_priv->gart_info.gart_table_location = - DRM_ATI_GART_FB; - - DRM_DEBUG("Setting phys_pci_gart to %p %08lX\n", - dev_priv->gart_info.addr, - dev_priv->pcigart_offset); + /* if it came from userspace - remap it */ + if (dev_priv->pcigart_offset_set == 1) { + dev_priv->gart_info.bus_addr = + dev_priv->pcigart_offset + dev_priv->fb_location; + dev_priv->gart_info.mapping.offset = + dev_priv->pcigart_offset + dev_priv->fb_aper_offset; + dev_priv->gart_info.mapping.size = + dev_priv->gart_info.table_size; + + /* this is done by the mm now */ + drm_core_ioremap(&dev_priv->gart_info.mapping, dev); + dev_priv->gart_info.addr = + dev_priv->gart_info.mapping.handle; + + memset(dev_priv->gart_info.addr, 0, dev_priv->gart_info.table_size); + if (dev_priv->flags & RADEON_IS_PCIE) + dev_priv->gart_info.gart_reg_if = DRM_ATI_GART_PCIE; + else + dev_priv->gart_info.gart_reg_if = DRM_ATI_GART_PCI; + dev_priv->gart_info.gart_table_location = + DRM_ATI_GART_FB; + + DRM_DEBUG("Setting phys_pci_gart to %p %08lX\n", + dev_priv->gart_info.addr, + dev_priv->pcigart_offset); + } } else { + + if (dev_priv->flags & RADEON_IS_PCIE) { + DRM_ERROR + ("Cannot use PCI Express without GART in FB memory\n"); + radeon_do_cleanup_cp(dev); + return -EINVAL; + } if (dev_priv->flags & RADEON_IS_IGPGART) dev_priv->gart_info.gart_reg_if = DRM_ATI_GART_IGP; else @@ -1186,12 +1379,7 @@ static int radeon_do_init_cp(struct drm_device * dev, drm_radeon_init_t * init) DRM_ATI_GART_MAIN; dev_priv->gart_info.addr = NULL; dev_priv->gart_info.bus_addr = 0; - if (dev_priv->flags & RADEON_IS_PCIE) { - DRM_ERROR - ("Cannot use PCI Express without GART in FB memory\n"); - radeon_do_cleanup_cp(dev); - return -EINVAL; - } + } if (!drm_ati_pcigart_init(dev, &dev_priv->gart_info)) { @@ -1251,14 +1439,16 @@ static int radeon_do_cleanup_cp(struct drm_device * dev) if (dev_priv->gart_info.bus_addr) { /* Turn off PCI GART */ radeon_set_pcigart(dev_priv, 0); - if (!drm_ati_pcigart_cleanup(dev, &dev_priv->gart_info)) - DRM_ERROR("failed to cleanup PCI GART!\n"); + drm_ati_pcigart_cleanup(dev, &dev_priv->gart_info); } if (dev_priv->gart_info.gart_table_location == DRM_ATI_GART_FB) { - drm_core_ioremapfree(&dev_priv->gart_info.mapping, dev); - dev_priv->gart_info.addr = 0; + if (dev_priv->pcigart_offset_set == 1) { + drm_core_ioremapfree(&dev_priv->gart_info.mapping, dev); + dev_priv->gart_info.addr = NULL; + dev_priv->pcigart_offset_set = 0; + } } } /* only clear to the start of flags */ @@ -1310,6 +1500,10 @@ static int radeon_do_resume_cp(struct drm_device * dev) int radeon_cp_init(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_radeon_init_t *init = data; + + /* on a modesetting driver ignore this stuff */ + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return 0; LOCK_TEST_WITH_RETURN(dev, file_priv); @@ -1320,7 +1514,7 @@ int radeon_cp_init(struct drm_device *dev, void *data, struct drm_file *file_pri case RADEON_INIT_CP: case RADEON_INIT_R200_CP: case RADEON_INIT_R300_CP: - return radeon_do_init_cp(dev, init); + return radeon_do_init_cp(dev, init, file_priv); case RADEON_CLEANUP_CP: return radeon_do_cleanup_cp(dev); } @@ -1333,6 +1527,9 @@ int radeon_cp_start(struct drm_device *dev, void *data, struct drm_file *file_pr drm_radeon_private_t *dev_priv = dev->dev_private; DRM_DEBUG("\n"); + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return 0; + LOCK_TEST_WITH_RETURN(dev, file_priv); if (dev_priv->cp_running) { @@ -1360,6 +1557,9 @@ int radeon_cp_stop(struct drm_device *dev, void *data, struct drm_file *file_pri int ret; DRM_DEBUG("\n"); + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return 0; + LOCK_TEST_WITH_RETURN(dev, file_priv); if (!dev_priv->cp_running) @@ -1398,6 +1598,9 @@ void radeon_do_release(struct drm_device * dev) drm_radeon_private_t *dev_priv = dev->dev_private; int i, ret; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return; + if (dev_priv) { if (dev_priv->cp_running) { /* Stop the cp */ @@ -1436,6 +1639,11 @@ void radeon_do_release(struct drm_device * dev) radeon_mem_takedown(&(dev_priv->gart_heap)); radeon_mem_takedown(&(dev_priv->fb_heap)); + if (dev_priv->user_mm_enable) { + radeon_gem_mm_fini(dev); + dev_priv->user_mm_enable = false; + } + /* deallocate kernel resources */ radeon_do_cleanup_cp(dev); } @@ -1448,6 +1656,9 @@ int radeon_cp_reset(struct drm_device *dev, void *data, struct drm_file *file_pr drm_radeon_private_t *dev_priv = dev->dev_private; DRM_DEBUG("\n"); + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return 0; + LOCK_TEST_WITH_RETURN(dev, file_priv); if (!dev_priv) { @@ -1468,7 +1679,9 @@ int radeon_cp_idle(struct drm_device *dev, void *data, struct drm_file *file_pri drm_radeon_private_t *dev_priv = dev->dev_private; DRM_DEBUG("\n"); - LOCK_TEST_WITH_RETURN(dev, file_priv); + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + LOCK_TEST_WITH_RETURN(dev, file_priv); return radeon_do_cp_idle(dev_priv); } @@ -1478,6 +1691,9 @@ int radeon_cp_idle(struct drm_device *dev, void *data, struct drm_file *file_pri int radeon_cp_resume(struct drm_device *dev, void *data, struct drm_file *file_priv) { + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return 0; + return radeon_do_resume_cp(dev); } @@ -1485,6 +1701,9 @@ int radeon_engine_reset(struct drm_device *dev, void *data, struct drm_file *fil { DRM_DEBUG("\n"); + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return 0; + LOCK_TEST_WITH_RETURN(dev, file_priv); return radeon_do_engine_reset(dev); @@ -1707,6 +1926,715 @@ int radeon_cp_buffers(struct drm_device *dev, void *data, struct drm_file *file_ return ret; } +static void radeon_get_vram_type(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t tmp; + + if (dev_priv->flags & RADEON_IS_IGP || (dev_priv->chip_family >= CHIP_R300)) + dev_priv->is_ddr = true; + else if (RADEON_READ(RADEON_MEM_SDRAM_MODE_REG) & RADEON_MEM_CFG_TYPE_DDR) + dev_priv->is_ddr = true; + else + dev_priv->is_ddr = false; + + if ((dev_priv->chip_family >= CHIP_R600) && + (dev_priv->chip_family <= CHIP_RV635)) { + int chansize; + + tmp = RADEON_READ(R600_RAMCFG); + if (tmp & R600_CHANSIZE_OVERRIDE) + chansize = 16; + else if (tmp & R600_CHANSIZE) + chansize = 64; + else + chansize = 32; + + if (dev_priv->chip_family == CHIP_R600) + dev_priv->ram_width = 8 * chansize; + else if (dev_priv->chip_family == CHIP_RV670) + dev_priv->ram_width = 4 * chansize; + else if ((dev_priv->chip_family == CHIP_RV610) || + (dev_priv->chip_family == CHIP_RV620)) + dev_priv->ram_width = chansize; + else if ((dev_priv->chip_family == CHIP_RV630) || + (dev_priv->chip_family == CHIP_RV635)) + dev_priv->ram_width = 2 * chansize; + } else if (dev_priv->chip_family == CHIP_RV515) { + tmp = radeon_read_mc_reg(dev_priv, RV515_MC_CNTL); + tmp &= RV515_MEM_NUM_CHANNELS_MASK; + switch (tmp) { + case 0: dev_priv->ram_width = 64; break; + case 1: dev_priv->ram_width = 128; break; + default: dev_priv->ram_width = 128; break; + } + } else if ((dev_priv->chip_family >= CHIP_R520) && + (dev_priv->chip_family <= CHIP_RV570)) { + tmp = radeon_read_mc_reg(dev_priv, R520_MC_CNTL0); + switch ((tmp & R520_MEM_NUM_CHANNELS_MASK) >> R520_MEM_NUM_CHANNELS_SHIFT) { + case 0: dev_priv->ram_width = 32; break; + case 1: dev_priv->ram_width = 64; break; + case 2: dev_priv->ram_width = 128; break; + case 3: dev_priv->ram_width = 256; break; + default: dev_priv->ram_width = 128; break; + } + } else if ((dev_priv->chip_family == CHIP_RV100) || + (dev_priv->chip_family == CHIP_RS100) || + (dev_priv->chip_family == CHIP_RS200)) { + tmp = RADEON_READ(RADEON_MEM_CNTL); + if (tmp & RV100_HALF_MODE) + dev_priv->ram_width = 32; + else + dev_priv->ram_width = 64; + + if (dev_priv->flags & RADEON_SINGLE_CRTC) { + dev_priv->ram_width /= 4; + dev_priv->is_ddr = true; + } + } else if (dev_priv->chip_family <= CHIP_RV280) { + tmp = RADEON_READ(RADEON_MEM_CNTL); + if (tmp & RADEON_MEM_NUM_CHANNELS_MASK) + dev_priv->ram_width = 128; + else + dev_priv->ram_width = 64; + } else { + /* newer IGPs */ + dev_priv->ram_width = 128; + } + DRM_DEBUG("RAM width %d bits %cDR\n", dev_priv->ram_width, dev_priv->is_ddr ? 'D' : 'S'); +} + +static void radeon_force_some_clocks(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t tmp; + + tmp = RADEON_READ_PLL(dev_priv, RADEON_SCLK_CNTL); + tmp |= RADEON_SCLK_FORCE_CP | RADEON_SCLK_FORCE_VIP; + RADEON_WRITE_PLL(dev_priv, RADEON_SCLK_CNTL, tmp); +} + +static void radeon_set_dynamic_clock(struct drm_device *dev, int mode) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t tmp; + + switch(mode) { + case 0: + if (dev_priv->flags & RADEON_SINGLE_CRTC) { + tmp = RADEON_READ_PLL(dev_priv, RADEON_SCLK_CNTL); + tmp |= (RADEON_SCLK_FORCE_CP | RADEON_SCLK_FORCE_HDP | + RADEON_SCLK_FORCE_DISP1 | RADEON_SCLK_FORCE_TOP | + RADEON_SCLK_FORCE_E2 | RADEON_SCLK_FORCE_SE | + RADEON_SCLK_FORCE_IDCT | RADEON_SCLK_FORCE_VIP | + RADEON_SCLK_FORCE_RE | RADEON_SCLK_FORCE_PB | + RADEON_SCLK_FORCE_TAM | RADEON_SCLK_FORCE_TDM | + RADEON_SCLK_FORCE_RB); + RADEON_WRITE_PLL(dev_priv, RADEON_SCLK_CNTL, tmp); + } else if (dev_priv->chip_family == CHIP_RV350) { + /* for RV350/M10, no delays are required. */ + tmp = RADEON_READ_PLL(dev_priv, R300_SCLK_CNTL2); + tmp |= (R300_SCLK_FORCE_TCL | + R300_SCLK_FORCE_GA | + R300_SCLK_FORCE_CBA); + RADEON_WRITE_PLL(dev_priv, R300_SCLK_CNTL2, tmp); + + tmp = RADEON_READ_PLL(dev_priv, RADEON_SCLK_CNTL); + tmp &= ~(RADEON_SCLK_FORCE_DISP2 | RADEON_SCLK_FORCE_CP | + RADEON_SCLK_FORCE_HDP | RADEON_SCLK_FORCE_DISP1 | + RADEON_SCLK_FORCE_TOP | RADEON_SCLK_FORCE_E2 | + R300_SCLK_FORCE_VAP | RADEON_SCLK_FORCE_IDCT | + RADEON_SCLK_FORCE_VIP | R300_SCLK_FORCE_SR | + R300_SCLK_FORCE_PX | R300_SCLK_FORCE_TX | + R300_SCLK_FORCE_US | RADEON_SCLK_FORCE_TV_SCLK | + R300_SCLK_FORCE_SU | RADEON_SCLK_FORCE_OV0); + tmp |= RADEON_DYN_STOP_LAT_MASK; + RADEON_WRITE_PLL(dev_priv, RADEON_SCLK_CNTL, tmp); + + tmp = RADEON_READ_PLL(dev_priv, RADEON_SCLK_MORE_CNTL); + tmp &= ~RADEON_SCLK_MORE_FORCEON; + tmp |= RADEON_SCLK_MORE_MAX_DYN_STOP_LAT; + RADEON_WRITE_PLL(dev_priv, RADEON_SCLK_MORE_CNTL, tmp); + + tmp = RADEON_READ_PLL(dev_priv, RADEON_VCLK_ECP_CNTL); + tmp |= (RADEON_PIXCLK_ALWAYS_ONb | + RADEON_PIXCLK_DAC_ALWAYS_ONb); + RADEON_WRITE_PLL(dev_priv, RADEON_VCLK_ECP_CNTL, tmp); + + tmp = RADEON_READ_PLL(dev_priv, RADEON_PIXCLKS_CNTL); + tmp |= (RADEON_PIX2CLK_ALWAYS_ONb | + RADEON_PIX2CLK_DAC_ALWAYS_ONb | + RADEON_DISP_TVOUT_PIXCLK_TV_ALWAYS_ONb | + R300_DVOCLK_ALWAYS_ONb | + RADEON_PIXCLK_BLEND_ALWAYS_ONb | + RADEON_PIXCLK_GV_ALWAYS_ONb | + R300_PIXCLK_DVO_ALWAYS_ONb | + RADEON_PIXCLK_LVDS_ALWAYS_ONb | + RADEON_PIXCLK_TMDS_ALWAYS_ONb | + R300_PIXCLK_TRANS_ALWAYS_ONb | + R300_PIXCLK_TVO_ALWAYS_ONb | + R300_P2G2CLK_ALWAYS_ONb | + R300_P2G2CLK_ALWAYS_ONb); + RADEON_WRITE_PLL(dev_priv, RADEON_PIXCLKS_CNTL, tmp); + } else { + tmp = RADEON_READ_PLL(dev_priv, RADEON_SCLK_CNTL); + tmp |= (RADEON_SCLK_FORCE_CP | RADEON_SCLK_FORCE_E2); + tmp |= RADEON_SCLK_FORCE_SE; + + if ( dev_priv->flags & RADEON_SINGLE_CRTC ) { + tmp |= ( RADEON_SCLK_FORCE_RB | + RADEON_SCLK_FORCE_TDM | + RADEON_SCLK_FORCE_TAM | + RADEON_SCLK_FORCE_PB | + RADEON_SCLK_FORCE_RE | + RADEON_SCLK_FORCE_VIP | + RADEON_SCLK_FORCE_IDCT | + RADEON_SCLK_FORCE_TOP | + RADEON_SCLK_FORCE_DISP1 | + RADEON_SCLK_FORCE_DISP2 | + RADEON_SCLK_FORCE_HDP ); + } else if ((dev_priv->chip_family == CHIP_R300) || + (dev_priv->chip_family == CHIP_R350)) { + tmp |= ( RADEON_SCLK_FORCE_HDP | + RADEON_SCLK_FORCE_DISP1 | + RADEON_SCLK_FORCE_DISP2 | + RADEON_SCLK_FORCE_TOP | + RADEON_SCLK_FORCE_IDCT | + RADEON_SCLK_FORCE_VIP); + } + + RADEON_WRITE_PLL(dev_priv, RADEON_SCLK_CNTL, tmp); + + udelay(16000); + + if ((dev_priv->chip_family == CHIP_R300) || + (dev_priv->chip_family == CHIP_R350)) { + tmp = RADEON_READ_PLL(dev_priv, R300_SCLK_CNTL2); + tmp |= ( R300_SCLK_FORCE_TCL | + R300_SCLK_FORCE_GA | + R300_SCLK_FORCE_CBA); + RADEON_WRITE_PLL(dev_priv, R300_SCLK_CNTL2, tmp); + udelay(16000); + } + + if (dev_priv->flags & RADEON_IS_IGP) { + tmp = RADEON_READ_PLL(dev_priv, RADEON_MCLK_CNTL); + tmp &= ~(RADEON_FORCEON_MCLKA | + RADEON_FORCEON_YCLKA); + RADEON_WRITE_PLL(dev_priv, RADEON_MCLK_CNTL, tmp); + udelay(16000); + } + + if ((dev_priv->chip_family == CHIP_RV200) || + (dev_priv->chip_family == CHIP_RV250) || + (dev_priv->chip_family == CHIP_RV280)) { + tmp = RADEON_READ_PLL(dev_priv, RADEON_SCLK_MORE_CNTL); + tmp |= RADEON_SCLK_MORE_FORCEON; + RADEON_WRITE_PLL(dev_priv, RADEON_SCLK_MORE_CNTL, tmp); + udelay(16000); + } + + tmp = RADEON_READ_PLL(dev_priv, RADEON_PIXCLKS_CNTL); + tmp &= ~(RADEON_PIX2CLK_ALWAYS_ONb | + RADEON_PIX2CLK_DAC_ALWAYS_ONb | + RADEON_PIXCLK_BLEND_ALWAYS_ONb | + RADEON_PIXCLK_GV_ALWAYS_ONb | + RADEON_PIXCLK_DIG_TMDS_ALWAYS_ONb | + RADEON_PIXCLK_LVDS_ALWAYS_ONb | + RADEON_PIXCLK_TMDS_ALWAYS_ONb); + + RADEON_WRITE_PLL(dev_priv, RADEON_PIXCLKS_CNTL, tmp); + udelay(16000); + + tmp = RADEON_READ_PLL(dev_priv, RADEON_VCLK_ECP_CNTL); + tmp &= ~(RADEON_PIXCLK_ALWAYS_ONb | + RADEON_PIXCLK_DAC_ALWAYS_ONb); + RADEON_WRITE_PLL(dev_priv, RADEON_VCLK_ECP_CNTL, tmp); + } + DRM_DEBUG("Dynamic Clock Scaling Disabled\n"); + break; + case 1: + if (dev_priv->flags & RADEON_SINGLE_CRTC) { + tmp = RADEON_READ_PLL(dev_priv, RADEON_SCLK_CNTL); + if ((RADEON_READ(RADEON_CONFIG_CNTL) & RADEON_CFG_ATI_REV_ID_MASK) > + RADEON_CFG_ATI_REV_A13) { + tmp &= ~(RADEON_SCLK_FORCE_CP | RADEON_SCLK_FORCE_RB); + } + tmp &= ~(RADEON_SCLK_FORCE_HDP | RADEON_SCLK_FORCE_DISP1 | + RADEON_SCLK_FORCE_TOP | RADEON_SCLK_FORCE_SE | + RADEON_SCLK_FORCE_IDCT | RADEON_SCLK_FORCE_RE | + RADEON_SCLK_FORCE_PB | RADEON_SCLK_FORCE_TAM | + RADEON_SCLK_FORCE_TDM); + RADEON_WRITE_PLL(dev_priv, RADEON_SCLK_CNTL, tmp); + } else if ((dev_priv->chip_family == CHIP_R300) || + (dev_priv->chip_family == CHIP_R350) || + (dev_priv->chip_family == CHIP_RV350)) { + if (dev_priv->chip_family == CHIP_RV350) { + tmp = RADEON_READ_PLL(dev_priv, R300_SCLK_CNTL2); + tmp &= ~(R300_SCLK_FORCE_TCL | + R300_SCLK_FORCE_GA | + R300_SCLK_FORCE_CBA); + tmp |= (R300_SCLK_TCL_MAX_DYN_STOP_LAT | + R300_SCLK_GA_MAX_DYN_STOP_LAT | + R300_SCLK_CBA_MAX_DYN_STOP_LAT); + RADEON_WRITE_PLL(dev_priv, R300_SCLK_CNTL2, tmp); + + tmp = RADEON_READ_PLL(dev_priv, RADEON_SCLK_CNTL); + tmp &= ~(RADEON_SCLK_FORCE_DISP2 | RADEON_SCLK_FORCE_CP | + RADEON_SCLK_FORCE_HDP | RADEON_SCLK_FORCE_DISP1 | + RADEON_SCLK_FORCE_TOP | RADEON_SCLK_FORCE_E2 | + R300_SCLK_FORCE_VAP | RADEON_SCLK_FORCE_IDCT | + RADEON_SCLK_FORCE_VIP | R300_SCLK_FORCE_SR | + R300_SCLK_FORCE_PX | R300_SCLK_FORCE_TX | + R300_SCLK_FORCE_US | RADEON_SCLK_FORCE_TV_SCLK | + R300_SCLK_FORCE_SU | RADEON_SCLK_FORCE_OV0); + tmp |= RADEON_DYN_STOP_LAT_MASK; + RADEON_WRITE_PLL(dev_priv, RADEON_SCLK_CNTL, tmp); + + tmp = RADEON_READ_PLL(dev_priv, RADEON_SCLK_MORE_CNTL); + tmp &= ~RADEON_SCLK_MORE_FORCEON; + tmp |= RADEON_SCLK_MORE_MAX_DYN_STOP_LAT; + RADEON_WRITE_PLL(dev_priv, RADEON_SCLK_MORE_CNTL, tmp); + + tmp = RADEON_READ_PLL(dev_priv, RADEON_VCLK_ECP_CNTL); + tmp |= (RADEON_PIXCLK_ALWAYS_ONb | + RADEON_PIXCLK_DAC_ALWAYS_ONb); + RADEON_WRITE_PLL(dev_priv, RADEON_VCLK_ECP_CNTL, tmp); + + tmp = RADEON_READ_PLL(dev_priv, RADEON_PIXCLKS_CNTL); + tmp |= (RADEON_PIX2CLK_ALWAYS_ONb | + RADEON_PIX2CLK_DAC_ALWAYS_ONb | + RADEON_DISP_TVOUT_PIXCLK_TV_ALWAYS_ONb | + R300_DVOCLK_ALWAYS_ONb | + RADEON_PIXCLK_BLEND_ALWAYS_ONb | + RADEON_PIXCLK_GV_ALWAYS_ONb | + R300_PIXCLK_DVO_ALWAYS_ONb | + RADEON_PIXCLK_LVDS_ALWAYS_ONb | + RADEON_PIXCLK_TMDS_ALWAYS_ONb | + R300_PIXCLK_TRANS_ALWAYS_ONb | + R300_PIXCLK_TVO_ALWAYS_ONb | + R300_P2G2CLK_ALWAYS_ONb | + R300_P2G2CLK_ALWAYS_ONb); + RADEON_WRITE_PLL(dev_priv, RADEON_PIXCLKS_CNTL, tmp); + + tmp = RADEON_READ_PLL(dev_priv, RADEON_MCLK_MISC); + tmp |= (RADEON_MC_MCLK_DYN_ENABLE | + RADEON_IO_MCLK_DYN_ENABLE); + RADEON_WRITE_PLL(dev_priv, RADEON_MCLK_MISC, tmp); + + tmp = RADEON_READ_PLL(dev_priv, RADEON_MCLK_CNTL); + tmp |= (RADEON_FORCEON_MCLKA | + RADEON_FORCEON_MCLKB); + + tmp &= ~(RADEON_FORCEON_YCLKA | + RADEON_FORCEON_YCLKB | + RADEON_FORCEON_MC); + + /* Some releases of vbios have set DISABLE_MC_MCLKA + and DISABLE_MC_MCLKB bits in the vbios table. Setting these + bits will cause H/W hang when reading video memory with dynamic clocking + enabled. */ + if ((tmp & R300_DISABLE_MC_MCLKA) && + (tmp & R300_DISABLE_MC_MCLKB)) { + /* If both bits are set, then check the active channels */ + tmp = RADEON_READ_PLL(dev_priv, RADEON_MCLK_CNTL); + if (dev_priv->ram_width == 64) { + if (RADEON_READ(RADEON_MEM_CNTL) & R300_MEM_USE_CD_CH_ONLY) + tmp &= ~R300_DISABLE_MC_MCLKB; + else + tmp &= ~R300_DISABLE_MC_MCLKA; + } else { + tmp &= ~(R300_DISABLE_MC_MCLKA | + R300_DISABLE_MC_MCLKB); + } + } + + RADEON_WRITE_PLL(dev_priv, RADEON_MCLK_CNTL, tmp); + } else { + tmp = RADEON_READ_PLL(dev_priv, RADEON_SCLK_CNTL); + tmp &= ~(R300_SCLK_FORCE_VAP); + tmp |= RADEON_SCLK_FORCE_CP; + RADEON_WRITE_PLL(dev_priv, RADEON_SCLK_CNTL, tmp); + udelay(15000); + + tmp = RADEON_READ_PLL(dev_priv, R300_SCLK_CNTL2); + tmp &= ~(R300_SCLK_FORCE_TCL | + R300_SCLK_FORCE_GA | + R300_SCLK_FORCE_CBA); + RADEON_WRITE_PLL(dev_priv, R300_SCLK_CNTL2, tmp); + } + } else { + tmp = RADEON_READ_PLL(dev_priv, RADEON_CLK_PWRMGT_CNTL); + tmp &= ~(RADEON_ACTIVE_HILO_LAT_MASK | + RADEON_DISP_DYN_STOP_LAT_MASK | + RADEON_DYN_STOP_MODE_MASK); + + tmp |= (RADEON_ENGIN_DYNCLK_MODE | + (0x01 << RADEON_ACTIVE_HILO_LAT_SHIFT)); + RADEON_WRITE_PLL(dev_priv, RADEON_CLK_PWRMGT_CNTL, tmp); + udelay(15000); + + tmp = RADEON_READ_PLL(dev_priv, RADEON_CLK_PIN_CNTL); + tmp |= RADEON_SCLK_DYN_START_CNTL; + RADEON_WRITE_PLL(dev_priv, RADEON_CLK_PIN_CNTL, tmp); + udelay(15000); + + /* When DRI is enabled, setting DYN_STOP_LAT to zero can cause some R200 + to lockup randomly, leave them as set by BIOS. + */ + tmp = RADEON_READ_PLL(dev_priv, RADEON_SCLK_CNTL); + /*tmp &= RADEON_SCLK_SRC_SEL_MASK;*/ + tmp &= ~RADEON_SCLK_FORCEON_MASK; + + /*RAGE_6::A11 A12 A12N1 A13, RV250::A11 A12, R300*/ + if (((dev_priv->chip_family == CHIP_RV250) && + ((RADEON_READ(RADEON_CONFIG_CNTL) & RADEON_CFG_ATI_REV_ID_MASK) < + RADEON_CFG_ATI_REV_A13)) || + ((dev_priv->chip_family == CHIP_RV100) && + ((RADEON_READ(RADEON_CONFIG_CNTL) & RADEON_CFG_ATI_REV_ID_MASK) <= + RADEON_CFG_ATI_REV_A13))){ + tmp |= RADEON_SCLK_FORCE_CP; + tmp |= RADEON_SCLK_FORCE_VIP; + } + + RADEON_WRITE_PLL(dev_priv, RADEON_SCLK_CNTL, tmp); + + if ((dev_priv->chip_family == CHIP_RV200) || + (dev_priv->chip_family == CHIP_RV250) || + (dev_priv->chip_family == CHIP_RV280)) { + tmp = RADEON_READ_PLL(dev_priv, RADEON_SCLK_MORE_CNTL); + tmp &= ~RADEON_SCLK_MORE_FORCEON; + + /* RV200::A11 A12 RV250::A11 A12 */ + if (((dev_priv->chip_family == CHIP_RV200) || + (dev_priv->chip_family == CHIP_RV250)) && + ((RADEON_READ(RADEON_CONFIG_CNTL) & RADEON_CFG_ATI_REV_ID_MASK) < + RADEON_CFG_ATI_REV_A13)) { + tmp |= RADEON_SCLK_MORE_FORCEON; + } + RADEON_WRITE_PLL(dev_priv, RADEON_SCLK_MORE_CNTL, tmp); + udelay(15000); + } + + /* RV200::A11 A12, RV250::A11 A12 */ + if (((dev_priv->chip_family == CHIP_RV200) || + (dev_priv->chip_family == CHIP_RV250)) && + ((RADEON_READ(RADEON_CONFIG_CNTL) & RADEON_CFG_ATI_REV_ID_MASK) < + RADEON_CFG_ATI_REV_A13)) { + tmp = RADEON_READ_PLL(dev_priv, RADEON_PLL_PWRMGT_CNTL); + tmp |= RADEON_TCL_BYPASS_DISABLE; + RADEON_WRITE_PLL(dev_priv, RADEON_PLL_PWRMGT_CNTL, tmp); + } + udelay(15000); + + /*enable dynamic mode for display clocks (PIXCLK and PIX2CLK)*/ + tmp = RADEON_READ_PLL(dev_priv, RADEON_PIXCLKS_CNTL); + tmp |= (RADEON_PIX2CLK_ALWAYS_ONb | + RADEON_PIX2CLK_DAC_ALWAYS_ONb | + RADEON_PIXCLK_BLEND_ALWAYS_ONb | + RADEON_PIXCLK_GV_ALWAYS_ONb | + RADEON_PIXCLK_DIG_TMDS_ALWAYS_ONb | + RADEON_PIXCLK_LVDS_ALWAYS_ONb | + RADEON_PIXCLK_TMDS_ALWAYS_ONb); + + RADEON_WRITE_PLL(dev_priv, RADEON_PIXCLKS_CNTL, tmp); + udelay(15000); + + tmp = RADEON_READ_PLL(dev_priv, RADEON_VCLK_ECP_CNTL); + tmp |= (RADEON_PIXCLK_ALWAYS_ONb | + RADEON_PIXCLK_DAC_ALWAYS_ONb); + + RADEON_WRITE_PLL(dev_priv, RADEON_VCLK_ECP_CNTL, tmp); + udelay(15000); + } + DRM_DEBUG("Dynamic Clock Scaling Enabled\n"); + break; + default: + break; + } + +} + +int radeon_modeset_cp_suspend(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + int ret; + + ret = radeon_do_cp_idle(dev_priv); + if (ret) + DRM_ERROR("failed to idle CP on suspend\n"); + + radeon_do_cp_stop(dev_priv); + radeon_do_engine_reset(dev); + if (dev_priv->flags & RADEON_IS_AGP) { + } else { + radeon_set_pcigart(dev_priv, 0); + } + + return 0; +} + +int radeon_modeset_cp_resume(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + radeon_do_wait_for_idle(dev_priv); +#if __OS_HAS_AGP + if (dev_priv->flags & RADEON_IS_AGP) { + /* Turn off PCI GART */ + radeon_set_pcigart(dev_priv, 0); + } else +#endif + { + /* Turn on PCI GART */ + radeon_set_pcigart(dev_priv, 1); + } + radeon_gart_flush(dev); + + radeon_cp_load_microcode(dev_priv); + radeon_cp_init_ring_buffer(dev, dev_priv); + + radeon_do_engine_reset(dev); + + radeon_test_writeback(dev_priv); + + radeon_do_cp_start(dev_priv); + return 0; +} + +#if __OS_HAS_AGP +int radeon_modeset_agp_init(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_agp_mode mode; + struct drm_agp_info info; + int ret; + int default_mode; + uint32_t agp_status; + bool is_v3; + + /* Acquire AGP. */ + ret = drm_agp_acquire(dev); + if (ret) { + DRM_ERROR("Unable to acquire AGP: %d\n", ret); + return ret; + } + + ret = drm_agp_info(dev, &info); + if (ret) { + DRM_ERROR("Unable to get AGP info: %d\n", ret); + return ret; + } + + mode.mode = info.mode; + + agp_status = (RADEON_READ(RADEON_AGP_STATUS) | RADEON_AGPv3_MODE) & mode.mode; + is_v3 = !!(agp_status & RADEON_AGPv3_MODE); + + if (is_v3) { + default_mode = (agp_status & RADEON_AGPv3_8X_MODE) ? 8 : 4; + } else { + if (agp_status & RADEON_AGP_4X_MODE) default_mode = 4; + else if (agp_status & RADEON_AGP_2X_MODE) default_mode = 2; + else default_mode = 1; + } + + if (radeon_agpmode > 0) { + if ((radeon_agpmode < (is_v3 ? 4 : 1)) || + (radeon_agpmode > (is_v3 ? 8 : 4)) || + (radeon_agpmode & (radeon_agpmode - 1))) { + DRM_ERROR("Illegal AGP Mode: %d (valid %s), leaving at %d\n", + radeon_agpmode, is_v3 ? "4, 8" : "1, 2, 4", + default_mode); + radeon_agpmode = default_mode; + } + else + DRM_INFO("AGP mode requested: %d\n", radeon_agpmode); + } else + radeon_agpmode = default_mode; + + mode.mode &= ~RADEON_AGP_MODE_MASK; + if (is_v3) { + switch(radeon_agpmode) { + case 8: + mode.mode |= RADEON_AGPv3_8X_MODE; + break; + case 4: + default: + mode.mode |= RADEON_AGPv3_4X_MODE; + break; + } + } else { + switch(radeon_agpmode) { + case 4: mode.mode |= RADEON_AGP_4X_MODE; + case 2: mode.mode |= RADEON_AGP_2X_MODE; + case 1: + default: + mode.mode |= RADEON_AGP_1X_MODE; + break; + } + } + + mode.mode &= ~RADEON_AGP_FW_MODE; /* disable fw */ + + ret = drm_agp_enable(dev, mode); + if (ret) { + DRM_ERROR("Unable to enable AGP (mode = 0x%lx)\n", mode.mode); + return ret; + } + + /* workaround some hw issues */ + if (dev_priv->chip_family < CHIP_R200) { + RADEON_WRITE(RADEON_AGP_CNTL, RADEON_READ(RADEON_AGP_CNTL) | 0x000e0000); + } + return 0; +} + +void radeon_modeset_agp_destroy(struct drm_device *dev) +{ + if (dev->agp->acquired) + drm_agp_release(dev); +} +#endif + +int radeon_modeset_cp_init(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + /* allocate a ring and ring rptr bits from GART space */ + /* these are allocated in GEM files */ + + /* Start with assuming that writeback doesn't work */ + dev_priv->writeback_works = 0; + + if (dev_priv->chip_family > CHIP_R600) + return 0; + + dev_priv->usec_timeout = RADEON_DEFAULT_CP_TIMEOUT; + dev_priv->ring.size = RADEON_DEFAULT_RING_SIZE; + dev_priv->cp_mode = RADEON_CSQ_PRIBM_INDBM; + + dev_priv->ring.start = (u32 *)(void *)(unsigned long)dev_priv->mm.ring.kmap.virtual; + dev_priv->ring.end = (u32 *)(void *)(unsigned long)dev_priv->mm.ring.kmap.virtual + + dev_priv->ring.size / sizeof(u32); + dev_priv->ring.size_l2qw = drm_order(dev_priv->ring.size / 8); + dev_priv->ring.rptr_update = 4096; + dev_priv->ring.rptr_update_l2qw = drm_order(4096 / 8); + dev_priv->ring.fetch_size_l2ow = 2; /* do what tcore does */ + dev_priv->ring.tail_mask = (dev_priv->ring.size / sizeof(u32)) - 1; + dev_priv->ring.high_mark = RADEON_RING_HIGH_MARK; + + dev_priv->new_memmap = true; + + r300_init_reg_flags(dev); + + /* turn off HDP read cache for now */ + RADEON_WRITE(RADEON_HOST_PATH_CNTL, RADEON_READ(RADEON_HOST_PATH_CNTL) | RADEON_HP_LIN_RD_CACHE_DIS); + +#if __OS_HAS_AGP + if (dev_priv->flags & RADEON_IS_AGP) + radeon_modeset_agp_init(dev); +#endif + + return radeon_modeset_cp_resume(dev); +} + +static bool radeon_get_bios(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + u8 __iomem *bios; + size_t size; + uint16_t tmp; + + bios = pci_map_rom(dev->pdev, &size); + if (!bios) + return -1; + + dev_priv->bios = kmalloc(size, GFP_KERNEL); + if (!dev_priv->bios) { + pci_unmap_rom(dev->pdev, bios); + return -1; + } + + memcpy(dev_priv->bios, bios, size); + + pci_unmap_rom(dev->pdev, bios); + + if (dev_priv->bios[0] != 0x55 || dev_priv->bios[1] != 0xaa) + goto free_bios; + + dev_priv->bios_header_start = radeon_bios16(dev_priv, 0x48); + + if (!dev_priv->bios_header_start) + goto free_bios; + + tmp = dev_priv->bios_header_start + 4; + + if (!memcmp(dev_priv->bios + tmp, "ATOM", 4) || + !memcmp(dev_priv->bios + tmp, "MOTA", 4)) + dev_priv->is_atom_bios = true; + else + dev_priv->is_atom_bios = false; + + DRM_DEBUG("%sBIOS detected\n", dev_priv->is_atom_bios ? "ATOM" : "COM"); + return true; +free_bios: + kfree(dev_priv->bios); + dev_priv->bios = NULL; + return false; +} + +int radeon_modeset_preinit(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + static struct card_info card; + int ret; + + card.dev = dev; + card.reg_read = cail_reg_read; + card.reg_write = cail_reg_write; + card.mc_read = cail_mc_read; + card.mc_write = cail_mc_write; + + ret = radeon_get_bios(dev); + if (!ret) + return -1; + + if (dev_priv->is_atom_bios) { + dev_priv->mode_info.atom_context = atom_parse(&card, dev_priv->bios); + radeon_atom_initialize_bios_scratch_regs(dev); + } else + radeon_combios_initialize_bios_scratch_regs(dev); + + radeon_get_clock_info(dev); + + return 0; +} + +int radeon_static_clocks_init(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + if (radeon_dynclks != -1) { + + if (dev_priv->chip_family == CHIP_RS400 || + dev_priv->chip_family == CHIP_RS480) + radeon_dynclks = 0; + + if ((dev_priv->flags & RADEON_IS_MOBILITY) && !radeon_is_avivo(dev_priv)) { + radeon_set_dynamic_clock(dev, radeon_dynclks); + } else if (radeon_is_avivo(dev_priv)) { + if (radeon_dynclks) { + radeon_atom_static_pwrmgt_setup(dev, 1); + radeon_atom_dyn_clk_setup(dev, 1); + } + } + } + if (radeon_is_r300(dev_priv) || radeon_is_rv100(dev_priv)) + radeon_force_some_clocks(dev); + return 0; +} + int radeon_driver_load(struct drm_device *dev, unsigned long flags) { drm_radeon_private_t *dev_priv; @@ -1750,33 +2678,131 @@ int radeon_driver_load(struct drm_device *dev, unsigned long flags) DRM_DEBUG("%s card detected\n", ((dev_priv->flags & RADEON_IS_AGP) ? "AGP" : (((dev_priv->flags & RADEON_IS_PCIE) ? "PCIE" : "PCI")))); + + if (dev_priv->flags & RADEON_IS_AGP) { + + /* disable AGP for any chips after RV280 if not specified */ + if ((dev_priv->chip_family > CHIP_RV280) && (radeon_agpmode == 0)) + radeon_agpmode = -1; + + if (radeon_agpmode == -1) { + DRM_INFO("Forcing AGP to PCI mode\n"); + dev_priv->flags &= ~RADEON_IS_AGP; + } + } + + + ret = drm_addmap(dev, drm_get_resource_start(dev, 2), + drm_get_resource_len(dev, 2), _DRM_REGISTERS, + _DRM_DRIVER | _DRM_READ_ONLY, &dev_priv->mmio); + if (ret != 0) + return ret; + + if (drm_core_check_feature(dev, DRIVER_MODESET)) + radeon_modeset_preinit(dev); + + radeon_get_vram_type(dev); + + dev_priv->pll_errata = 0; + + if (dev_priv->chip_family == CHIP_R300 && + (RADEON_READ(RADEON_CONFIG_CNTL) & RADEON_CFG_ATI_REV_ID_MASK) == RADEON_CFG_ATI_REV_A11) + dev_priv->pll_errata |= CHIP_ERRATA_R300_CG; + + if (dev_priv->chip_family == CHIP_RV200 || + dev_priv->chip_family == CHIP_RS200) + dev_priv->pll_errata |= CHIP_ERRATA_PLL_DUMMYREADS; + + + if (dev_priv->chip_family == CHIP_RV100 || + dev_priv->chip_family == CHIP_RS100 || + dev_priv->chip_family == CHIP_RS200) + dev_priv->pll_errata |= CHIP_ERRATA_PLL_DELAY; + + + if (drm_core_check_feature(dev, DRIVER_MODESET)) + radeon_static_clocks_init(dev); + + /* init memory manager - start with all of VRAM and a 32MB GART aperture for now */ + dev_priv->fb_aper_offset = drm_get_resource_start(dev, 0); + + drm_bo_driver_init(dev); + + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + + ret = radeon_gem_mm_init(dev); + if (ret) + goto modeset_fail; + radeon_modeset_init(dev); + + radeon_modeset_cp_init(dev); + dev->devname = kstrdup(DRIVER_NAME, GFP_KERNEL); + + drm_irq_install(dev); + } + + + return ret; +modeset_fail: + dev->driver->driver_features &= ~DRIVER_MODESET; + drm_put_minor(&dev->control); return ret; } + +int radeon_master_create(struct drm_device *dev, struct drm_master *master) +{ + struct drm_radeon_master_private *master_priv; + unsigned long sareapage; + int ret; + + master_priv = drm_calloc(1, sizeof(*master_priv), DRM_MEM_DRIVER); + if (!master_priv) + return -ENOMEM; + + /* prebuild the SAREA */ + sareapage = max(SAREA_MAX, PAGE_SIZE); + ret = drm_addmap(dev, 0, sareapage, _DRM_SHM, _DRM_CONTAINS_LOCK|_DRM_DRIVER, + &master_priv->sarea); + if (ret) { + DRM_ERROR("SAREA setup failed\n"); + return ret; + } + master_priv->sarea_priv = master_priv->sarea->handle + sizeof(struct drm_sarea); + master_priv->sarea_priv->pfCurrentPage = 0; + + master->driver_priv = master_priv; + return 0; +} + +void radeon_master_destroy(struct drm_device *dev, struct drm_master *master) +{ + struct drm_radeon_master_private *master_priv = master->driver_priv; + + if (!master_priv) + return; + + if (master_priv->sarea_priv && + master_priv->sarea_priv->pfCurrentPage != 0) + radeon_cp_dispatch_flip(dev, master); + + master_priv->sarea_priv = NULL; + if (master_priv->sarea) + drm_rmmap_locked(dev, master_priv->sarea); + + drm_free(master_priv, sizeof(*master_priv), DRM_MEM_DRIVER); + + master->driver_priv = NULL; +} /* Create mappings for registers and framebuffer so userland doesn't necessarily * have to find them. */ int radeon_driver_firstopen(struct drm_device *dev) { - int ret; - drm_local_map_t *map; drm_radeon_private_t *dev_priv = dev->dev_private; dev_priv->gart_info.table_size = RADEON_PCIGART_TABLE_SIZE; - ret = drm_addmap(dev, drm_get_resource_start(dev, 2), - drm_get_resource_len(dev, 2), _DRM_REGISTERS, - _DRM_READ_ONLY, &dev_priv->mmio); - if (ret != 0) - return ret; - - dev_priv->fb_aper_offset = drm_get_resource_start(dev, 0); - ret = drm_addmap(dev, dev_priv->fb_aper_offset, - drm_get_resource_len(dev, 0), _DRM_FRAME_BUFFER, - _DRM_WRITE_COMBINING, &map); - if (ret != 0) - return ret; - return 0; } @@ -1784,9 +2810,76 @@ int radeon_driver_unload(struct drm_device *dev) { drm_radeon_private_t *dev_priv = dev->dev_private; + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + drm_irq_uninstall(dev); + radeon_modeset_cleanup(dev); + radeon_gem_mm_fini(dev); +#if __OS_HAS_AGP + if (dev_priv->flags & RADEON_IS_AGP) + radeon_modeset_agp_destroy(dev); +#endif + } + + drm_bo_driver_finish(dev); + drm_rmmap(dev, dev_priv->mmio); + DRM_DEBUG("\n"); drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER); dev->dev_private = NULL; return 0; } + +void radeon_gart_flush(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + if (dev_priv->flags & RADEON_IS_IGPGART) { + IGP_READ_MCIND(dev_priv, RS480_GART_CACHE_CNTRL); + IGP_WRITE_MCIND(RS480_GART_CACHE_CNTRL, RS480_GART_CACHE_INVALIDATE); + IGP_READ_MCIND(dev_priv, RS480_GART_CACHE_CNTRL); + IGP_WRITE_MCIND(RS480_GART_CACHE_CNTRL, 0); + } 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 { + + + } + +} + +void radeon_commit_ring(drm_radeon_private_t *dev_priv) +{ + int i; + u32 *ring; + int tail_aligned; + + /* check if the ring is padded out to 16-dword alignment */ + + tail_aligned = dev_priv->ring.tail & 0xf; + if (tail_aligned) { + int num_p2 = 16 - tail_aligned; + + ring = dev_priv->ring.start; + /* pad with some CP_PACKET2 */ + for (i = 0; i < num_p2; i++) + ring[dev_priv->ring.tail + i] = CP_PACKET2(); + + dev_priv->ring.tail += i; + + dev_priv->ring.space -= num_p2 * sizeof(u32); + } + + dev_priv->ring.tail &= dev_priv->ring.tail_mask; + + DRM_MEMORYBARRIER(); + GET_RING_HEAD( dev_priv ); + + RADEON_WRITE( RADEON_CP_RB_WPTR, dev_priv->ring.tail ); + /* read from PCI bus to ensure correct posting */ + RADEON_READ( RADEON_CP_RB_RPTR ); +} diff --git a/shared-core/radeon_cs.c b/shared-core/radeon_cs.c new file mode 100644 index 00000000..9227a011 --- /dev/null +++ b/shared-core/radeon_cs.c @@ -0,0 +1,667 @@ +/* + * Copyright 2008 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 "drmP.h" +#include "radeon_drm.h" +#include "radeon_drv.h" +#include "r300_reg.h" + +int radeon_cs2_ioctl(struct drm_device *dev, void *data, struct drm_file *fpriv) +{ + struct drm_radeon_cs_parser parser; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct drm_radeon_cs2 *cs = data; + uint32_t cs_id; + struct drm_radeon_cs_chunk __user **chunk_ptr = NULL; + uint64_t *chunk_array; + uint64_t *chunk_array_ptr; + long size; + int r, i; + + mutex_lock(&dev_priv->cs.cs_mutex); + /* set command stream id to 0 which is fake id */ + cs_id = 0; + cs->cs_id = cs_id; + + if (dev_priv == NULL) { + DRM_ERROR("called with no initialization\n"); + mutex_unlock(&dev_priv->cs.cs_mutex); + return -EINVAL; + } + if (!cs->num_chunks) { + mutex_unlock(&dev_priv->cs.cs_mutex); + return 0; + } + + + chunk_array = drm_calloc(cs->num_chunks, sizeof(uint64_t), DRM_MEM_DRIVER); + if (!chunk_array) { + mutex_unlock(&dev_priv->cs.cs_mutex); + return -ENOMEM; + } + + chunk_array_ptr = (uint64_t *)(unsigned long)(cs->chunks); + + if (DRM_COPY_FROM_USER(chunk_array, chunk_array_ptr, sizeof(uint64_t)*cs->num_chunks)) { + r = -EFAULT; + goto out; + } + + parser.dev = dev; + parser.file_priv = fpriv; + parser.reloc_index = -1; + parser.ib_index = -1; + parser.num_chunks = cs->num_chunks; + /* copy out the chunk headers */ + parser.chunks = drm_calloc(parser.num_chunks, sizeof(struct drm_radeon_kernel_chunk), DRM_MEM_DRIVER); + if (!parser.chunks) { + return -ENOMEM; + } + + for (i = 0; i < parser.num_chunks; i++) { + struct drm_radeon_cs_chunk user_chunk; + + chunk_ptr = (void __user *)(unsigned long)chunk_array[i]; + + if (DRM_COPY_FROM_USER(&user_chunk, chunk_ptr, sizeof(struct drm_radeon_cs_chunk))){ + r = -EFAULT; + goto out; + } + parser.chunks[i].chunk_id = user_chunk.chunk_id; + + if (parser.chunks[i].chunk_id == RADEON_CHUNK_ID_RELOCS) + parser.reloc_index = i; + + if (parser.chunks[i].chunk_id == RADEON_CHUNK_ID_IB) + parser.ib_index = i; + + if (parser.chunks[i].chunk_id == RADEON_CHUNK_ID_OLD) { + parser.ib_index = i; + parser.reloc_index = -1; + } + + parser.chunks[i].length_dw = user_chunk.length_dw; + parser.chunks[i].chunk_data = (uint32_t *)(unsigned long)user_chunk.chunk_data; + + parser.chunks[i].kdata = NULL; + size = parser.chunks[i].length_dw * sizeof(uint32_t); + + switch(parser.chunks[i].chunk_id) { + case RADEON_CHUNK_ID_IB: + case RADEON_CHUNK_ID_OLD: + if (size == 0) { + r = -EINVAL; + goto out; + } + case RADEON_CHUNK_ID_RELOCS: + if (size) { + parser.chunks[i].kdata = drm_alloc(size, DRM_MEM_DRIVER); + if (!parser.chunks[i].kdata) { + r = -ENOMEM; + goto out; + } + + if (DRM_COPY_FROM_USER(parser.chunks[i].kdata, parser.chunks[i].chunk_data, size)) { + r = -EFAULT; + goto out; + } + } else + parser.chunks[i].kdata = NULL; + break; + default: + break; + } + DRM_DEBUG("chunk %d %d %d %p\n", i, parser.chunks[i].chunk_id, parser.chunks[i].length_dw, + parser.chunks[i].chunk_data); + } + + + if (parser.chunks[parser.ib_index].length_dw > (16 * 1024)) { + DRM_ERROR("cs->dwords too big: %d\n", parser.chunks[parser.ib_index].length_dw); + r = -EINVAL; + goto out; + } + + /* get ib */ + r = dev_priv->cs.ib_get(&parser); + if (r) { + DRM_ERROR("ib_get failed\n"); + goto out; + } + + /* now parse command stream */ + r = dev_priv->cs.parse(&parser); + if (r) { + goto out; + } + + /* emit cs id sequence */ + dev_priv->cs.id_emit(&parser, &cs_id); + + cs->cs_id = cs_id; + +out: + dev_priv->cs.ib_free(&parser); + mutex_unlock(&dev_priv->cs.cs_mutex); + + for (i = 0; i < parser.num_chunks; i++) { + if (parser.chunks[i].kdata) + drm_free(parser.chunks[i].kdata, parser.chunks[i].length_dw * sizeof(uint32_t), DRM_MEM_DRIVER); + } + + drm_free(parser.chunks, sizeof(struct drm_radeon_kernel_chunk)*parser.num_chunks, DRM_MEM_DRIVER); + drm_free(chunk_array, sizeof(uint64_t)*parser.num_chunks, DRM_MEM_DRIVER); + + return r; +} + +int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *fpriv) +{ + struct drm_radeon_cs_parser parser; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct drm_radeon_cs *cs = data; + uint32_t *packets = NULL; + uint32_t cs_id; + long size; + int r; + struct drm_radeon_kernel_chunk chunk_fake[1]; + + /* set command stream id to 0 which is fake id */ + cs_id = 0; + cs->cs_id = cs_id; + + if (dev_priv == NULL) { + DRM_ERROR("called with no initialization\n"); + return -EINVAL; + } + if (!cs->dwords) { + return 0; + } + /* limit cs to 64K ib */ + if (cs->dwords > (16 * 1024)) { + return -EINVAL; + } + /* copy cs from userspace maybe we should copy into ib to save + * one copy but ib will be mapped wc so not good for cmd checking + * somethings worth testing i guess (Jerome) + */ + size = cs->dwords * sizeof(uint32_t); + packets = drm_alloc(size, DRM_MEM_DRIVER); + if (packets == NULL) { + return -ENOMEM; + } + if (DRM_COPY_FROM_USER(packets, (void __user *)(unsigned long)cs->packets, size)) { + r = -EFAULT; + goto out; + } + + chunk_fake[0].chunk_id = RADEON_CHUNK_ID_OLD; + chunk_fake[0].length_dw = cs->dwords; + chunk_fake[0].kdata = packets; + + parser.dev = dev; + parser.file_priv = fpriv; + parser.num_chunks = 1; + parser.chunks = chunk_fake; + parser.ib_index = 0; + parser.reloc_index = -1; + + /* get ib */ + r = dev_priv->cs.ib_get(&parser); + if (r) { + goto out; + } + + /* now parse command stream */ + r = dev_priv->cs.parse(&parser); + if (r) { + goto out; + } + + /* emit cs id sequence */ + dev_priv->cs.id_emit(&parser, &cs_id); + COMMIT_RING(); + + cs->cs_id = cs_id; +out: + dev_priv->cs.ib_free(&parser); + drm_free(packets, size, DRM_MEM_DRIVER); + return r; +} + +/* for non-mm */ +static int radeon_nomm_relocate(struct drm_radeon_cs_parser *parser, uint32_t *reloc, uint32_t *offset) +{ + *offset = reloc[1]; + return 0; +} +#define RELOC_SIZE 2 +#define RELOC_SIZE_NEW 0 +#define RADEON_2D_OFFSET_MASK 0x3fffff + +static __inline__ int radeon_cs_relocate_packet0(struct drm_radeon_cs_parser *parser, uint32_t offset_dw) +{ + struct drm_device *dev = parser->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + uint32_t hdr, reg, val, packet3_hdr; + uint32_t tmp, offset; + struct drm_radeon_kernel_chunk *ib_chunk; + int ret; + + ib_chunk = &parser->chunks[parser->ib_index]; +// if (parser->reloc_index == -1) +// is_old = 1; + + hdr = ib_chunk->kdata[offset_dw]; + reg = (hdr & R300_CP_PACKET0_REG_MASK) << 2; + val = ib_chunk->kdata[offset_dw + 1]; + packet3_hdr = ib_chunk->kdata[offset_dw + 2]; + + /* this is too strict we may want to expand the length in the future and have + old kernels ignore it. */ + if (parser->reloc_index == -1) { + if (packet3_hdr != (RADEON_CP_PACKET3 | RADEON_CP_NOP | (RELOC_SIZE << 16))) { + DRM_ERROR("Packet 3 was %x should have been %x: reg is %x at %d\n", packet3_hdr, RADEON_CP_PACKET3 | RADEON_CP_NOP | (RELOC_SIZE << 16), reg, offset_dw); + return -EINVAL; + } + if (packet3_hdr != (RADEON_CP_PACKET3 | RADEON_CP_NOP | (RELOC_SIZE_NEW << 16))) { + DRM_ERROR("Packet 3 was %x should have been %x: reg is %x at %d\n", packet3_hdr, RADEON_CP_PACKET3 | RADEON_CP_NOP | (RELOC_SIZE_NEW << 16), reg, offset_dw); + return -EINVAL; + + } + } + + switch(reg) { + case RADEON_DST_PITCH_OFFSET: + case RADEON_SRC_PITCH_OFFSET: + /* pass in the start of the reloc */ + ret = dev_priv->cs.relocate(parser, + ib_chunk->kdata + offset_dw + 2, &offset); + if (ret) { + return ret; + } + tmp = (val & RADEON_2D_OFFSET_MASK) << 10; + val &= ~RADEON_2D_OFFSET_MASK; + offset += tmp; + offset >>= 10; + val |= offset; + break; + case RADEON_RB3D_COLOROFFSET: + case R300_RB3D_COLOROFFSET0: + case R300_ZB_DEPTHOFFSET: + case R300_TX_OFFSET_0: + case R300_TX_OFFSET_0+4: + case R200_PP_TXOFFSET_0: + case R200_PP_TXOFFSET_1: + case RADEON_PP_TXOFFSET_0: + case RADEON_PP_TXOFFSET_1: + ret = dev_priv->cs.relocate(parser, + ib_chunk->kdata + offset_dw + 2, &offset); + if (ret) { + DRM_ERROR("Failed to relocate %d\n", offset_dw); + return ret; + } + + offset &= 0xffffffe0; + val += offset; + break; + default: + break; + } + + ib_chunk->kdata[offset_dw + 1] = val; + return 0; +} + +static int radeon_cs_relocate_packet3(struct drm_radeon_cs_parser *parser, + uint32_t offset_dw) +{ + drm_radeon_private_t *dev_priv = parser->dev->dev_private; + uint32_t hdr, num_dw, reg, i; + uint32_t offset, val, tmp, nptr, cptr; + uint32_t *reloc; + int ret; + struct drm_radeon_kernel_chunk *ib_chunk; + + ib_chunk = &parser->chunks[parser->ib_index]; +// if (parser->reloc_index == -1) +// is_old = 1; + + hdr = ib_chunk->kdata[offset_dw]; + num_dw = (hdr & RADEON_CP_PACKET_COUNT_MASK) >> 16; + reg = hdr & 0xff00; + + switch(reg) { + case RADEON_CNTL_HOSTDATA_BLT: + val = ib_chunk->kdata[offset_dw + 2]; + ret = dev_priv->cs.relocate(parser, + ib_chunk->kdata + offset_dw + num_dw + 2, + &offset); + if (ret) { + return ret; + } + + tmp = (val & RADEON_2D_OFFSET_MASK) << 10; + val &= ~RADEON_2D_OFFSET_MASK; + offset += tmp; + offset >>= 10; + val |= offset; + + ib_chunk->kdata[offset_dw + 2] = val; + break; + case RADEON_3D_LOAD_VBPNTR: + nptr = ib_chunk->kdata[offset_dw + 1]; + cptr = offset_dw + 3; + for (i = 0; i < (nptr & ~1); i+= 2) { + reloc = ib_chunk->kdata + offset_dw + num_dw + 2; + reloc += ((i + 0) * 2); + ret = dev_priv->cs.relocate(parser, reloc, &offset); + if (ret) { + return ret; + } + ib_chunk->kdata[cptr] += offset; + cptr += 1; + reloc = ib_chunk->kdata + offset_dw + num_dw + 2; + reloc += ((i + 1) * 2); + ret = dev_priv->cs.relocate(parser, reloc, &offset); + if (ret) { + return ret; + } + ib_chunk->kdata[cptr] += offset; + cptr += 2; + } + if (nptr & 1) { + reloc = ib_chunk->kdata + offset_dw + num_dw + 2; + reloc += ((nptr - 1) * 2); + ret = dev_priv->cs.relocate(parser, reloc, &offset); + if (ret) { + return ret; + } + ib_chunk->kdata[cptr] += offset; + } + break; + case RADEON_CP_INDX_BUFFER: + reloc = ib_chunk->kdata + offset_dw + num_dw + 2; + ret = dev_priv->cs.relocate(parser, reloc, &offset); + if (ret) { + return ret; + } + ib_chunk->kdata[offset_dw + 2] += offset; + break; + default: + DRM_ERROR("Unknown packet3 0x%08X\n", hdr); + return -EINVAL; + } + return 0; +} + +int radeon_cs_packet0(struct drm_radeon_cs_parser *parser, uint32_t offset_dw) +{ + uint32_t hdr, num_dw, reg; + int count_dw = 1; + int ret; + + hdr = parser->chunks[parser->ib_index].kdata[offset_dw]; + num_dw = ((hdr & RADEON_CP_PACKET_COUNT_MASK) >> 16) + 2; + reg = (hdr & R300_CP_PACKET0_REG_MASK) << 2; + + if (hdr & (1 << 15)) { + if (reg == 0x2208) { + return 0; + } + } + + while (count_dw < num_dw) { + /* need to have something like the r300 validation here - + list of allowed registers */ + int flags; + + ret = r300_check_range(reg, 1); + switch(ret) { + case -1: + DRM_ERROR("Illegal register %x\n", reg); + break; + case 0: + break; + case 1: + flags = r300_get_reg_flags(reg); + if (flags == MARK_CHECK_OFFSET) { + if (num_dw > 2) { + DRM_ERROR("Cannot relocate inside type stream of reg0 packets\n"); + return -EINVAL; + } + + ret = radeon_cs_relocate_packet0(parser, offset_dw); + if (ret) + return ret; + DRM_DEBUG("need to relocate %x %d\n", reg, flags); + /* okay it should be followed by a NOP */ + } else if (flags == MARK_CHECK_SCISSOR) { + DRM_DEBUG("need to validate scissor %x %d\n", reg, flags); + } else { + + DRM_ERROR("illegal register 0x%x %d at %d\n", reg, flags, offset_dw); + return -EINVAL; + } + break; + } + count_dw++; + reg += 4; + } + return 0; +} + +int radeon_cs_parse(struct drm_radeon_cs_parser *parser) +{ + volatile int rb; + struct drm_radeon_kernel_chunk *ib_chunk; + /* scan the packet for various things */ + int count_dw = 0, size_dw; + int ret = 0; + + ib_chunk = &parser->chunks[parser->ib_index]; + size_dw = ib_chunk->length_dw; + + while (count_dw < size_dw && ret == 0) { + int hdr = ib_chunk->kdata[count_dw]; + int num_dw = (hdr & RADEON_CP_PACKET_COUNT_MASK) >> 16; + int reg; + + switch (hdr & RADEON_CP_PACKET_MASK) { + case RADEON_CP_PACKET0: + ret = radeon_cs_packet0(parser, count_dw); + if (ret) + return ret; + break; + case RADEON_CP_PACKET1: + case RADEON_CP_PACKET2: + reg = hdr & RADEON_CP_PACKET0_REG_MASK; + DRM_DEBUG("Packet 1/2: %d %x\n", num_dw, reg); + break; + + case RADEON_CP_PACKET3: + reg = hdr & 0xff00; + + switch(reg) { + case RADEON_3D_LOAD_VBPNTR: + case RADEON_CP_INDX_BUFFER: + case RADEON_CNTL_HOSTDATA_BLT: + ret =radeon_cs_relocate_packet3(parser, + count_dw); + if (ret) + return ret; + break; + + case RADEON_CNTL_BITBLT_MULTI: + DRM_ERROR("need relocate packet 3 for %x %d\n", reg, count_dw); + break; + + case RADEON_3D_DRAW_IMMD: /* triggers drawing using in-packet vertex data */ + case RADEON_CP_3D_DRAW_IMMD_2: /* triggers drawing using in-packet vertex data */ + case RADEON_CP_3D_DRAW_VBUF_2: /* triggers drawing of vertex buffers setup elsewhere */ + case RADEON_CP_3D_DRAW_INDX_2: /* triggers drawing using indices to vertex buffer */ + case RADEON_WAIT_FOR_IDLE: + case RADEON_CP_NOP: + break; + default: + DRM_ERROR("unknown packet 3 %x at %d\n", reg, count_dw); + ret = -EINVAL; + } + break; + } + count_dw += num_dw+2; + } + + if (ret) + return ret; + + + /* copy the packet into the IB */ + memcpy(parser->ib, ib_chunk->kdata, ib_chunk->length_dw * sizeof(uint32_t)); + + /* read back last byte to flush WC buffers */ + rb = readl((parser->ib + (ib_chunk->length_dw-1) * sizeof(uint32_t))); + + return 0; +} + +uint32_t radeon_cs_id_get(struct drm_radeon_private *radeon) +{ + /* FIXME: protect with a spinlock */ + /* FIXME: check if wrap affect last reported wrap & sequence */ + radeon->cs.id_scnt = (radeon->cs.id_scnt + 1) & 0x00FFFFFF; + if (!radeon->cs.id_scnt) { + /* increment wrap counter */ + radeon->cs.id_wcnt += 0x01000000; + /* valid sequence counter start at 1 */ + radeon->cs.id_scnt = 1; + } + return (radeon->cs.id_scnt | radeon->cs.id_wcnt); +} + +void r100_cs_id_emit(struct drm_radeon_cs_parser *parser, uint32_t *id) +{ + drm_radeon_private_t *dev_priv = parser->dev->dev_private; + RING_LOCALS; + + dev_priv->irq_emitted = radeon_update_breadcrumb(parser->dev); + /* ISYNC_CNTL should have CPSCRACTH bit set */ + *id = radeon_cs_id_get(dev_priv); + /* emit id in SCRATCH4 (not used yet in old drm) */ + BEGIN_RING(10); + OUT_RING(CP_PACKET0(RADEON_CP_IB_BASE, 1)); + OUT_RING(parser->card_offset); + OUT_RING(parser->chunks[parser->ib_index].length_dw); + OUT_RING(CP_PACKET2()); + OUT_RING(CP_PACKET0(RADEON_SCRATCH_REG4, 0)); + OUT_RING(*id); + OUT_RING_REG(RADEON_LAST_SWI_REG, dev_priv->irq_emitted); + OUT_RING_REG(RADEON_GEN_INT_STATUS, RADEON_SW_INT_FIRE); + ADVANCE_RING(); + COMMIT_RING(); + +} + +void r300_cs_id_emit(struct drm_radeon_cs_parser *parser, uint32_t *id) +{ + drm_radeon_private_t *dev_priv = parser->dev->dev_private; + int i; + RING_LOCALS; + + dev_priv->irq_emitted = radeon_update_breadcrumb(parser->dev); + + /* ISYNC_CNTL should not have CPSCRACTH bit set */ + *id = radeon_cs_id_get(dev_priv); + + /* emit id in SCRATCH6 */ + BEGIN_RING(16); + OUT_RING(CP_PACKET0(RADEON_CP_IB_BASE, 1)); + OUT_RING(parser->card_offset); + OUT_RING(parser->chunks[parser->ib_index].length_dw); + OUT_RING(CP_PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0)); + OUT_RING(0); + for (i = 0; i < 11; i++) /* emit fillers like fglrx */ + OUT_RING(CP_PACKET2()); + ADVANCE_RING(); + COMMIT_RING(); + + BEGIN_RING(16); + OUT_RING_REG(R300_RB3D_DSTCACHE_CTLSTAT, R300_RB3D_DC_FLUSH); + OUT_RING(CP_PACKET0(R300_CP_RESYNC_ADDR, 1)); + OUT_RING(6); + OUT_RING(*id); + OUT_RING_REG(R300_RB3D_DSTCACHE_CTLSTAT, R300_RB3D_DC_FINISH|R300_RB3D_DC_FLUSH); + /* emit inline breadcrumb for TTM fencing */ +#if 1 + RADEON_WAIT_UNTIL_3D_IDLE(); + OUT_RING_REG(RADEON_LAST_SWI_REG, dev_priv->irq_emitted); +#else + OUT_RING(CP_PACKET0(R300_CP_RESYNC_ADDR, 1)); + OUT_RING(3); /* breadcrumb register */ + OUT_RING(dev_priv->irq_emitted); + OUT_RING(CP_PACKET2()); +#endif + OUT_RING_REG(RADEON_GEN_INT_STATUS, RADEON_SW_INT_FIRE); + OUT_RING(CP_PACKET2()); + OUT_RING(CP_PACKET2()); + OUT_RING(CP_PACKET2()); + ADVANCE_RING(); + COMMIT_RING(); + +} + +uint32_t r100_cs_id_last_get(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + return GET_SCRATCH(4); +} + +uint32_t r300_cs_id_last_get(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + return GET_SCRATCH(6); +} + +int radeon_cs_init(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + mutex_init(&dev_priv->cs.cs_mutex); + if (dev_priv->chip_family < CHIP_RV280) { + dev_priv->cs.id_emit = r100_cs_id_emit; + dev_priv->cs.id_last_get = r100_cs_id_last_get; + } else if (dev_priv->chip_family < CHIP_R600) { + dev_priv->cs.id_emit = r300_cs_id_emit; + dev_priv->cs.id_last_get = r300_cs_id_last_get; + } + + dev_priv->cs.parse = radeon_cs_parse; + /* ib get depends on memory manager or not so memory manager */ + dev_priv->cs.relocate = radeon_nomm_relocate; + return 0; +} diff --git a/shared-core/radeon_drm.h b/shared-core/radeon_drm.h index ffaa46e4..002e4004 100644 --- a/shared-core/radeon_drm.h +++ b/shared-core/radeon_drm.h @@ -453,8 +453,18 @@ 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; + + uint32_t front_handle; + uint32_t back_handle; + uint32_t depth_handle; + uint32_t front_pitch; + uint32_t back_pitch; + uint32_t depth_pitch; } drm_radeon_sarea_t; + /* WARNING: If you change any of these defines, make sure to change the * defines in the Xserver file (xf86drmRadeon.h) * @@ -493,6 +503,19 @@ typedef struct { #define DRM_RADEON_SURF_ALLOC 0x1a #define DRM_RADEON_SURF_FREE 0x1b +#define DRM_RADEON_GEM_INFO 0x1c +#define DRM_RADEON_GEM_CREATE 0x1d +#define DRM_RADEON_GEM_MMAP 0x1e +#define DRM_RADEON_GEM_PIN 0x1f +#define DRM_RADEON_GEM_UNPIN 0x20 +#define DRM_RADEON_GEM_PREAD 0x21 +#define DRM_RADEON_GEM_PWRITE 0x22 +#define DRM_RADEON_GEM_SET_DOMAIN 0x23 +#define DRM_RADEON_GEM_WAIT_RENDERING 0x24 + +#define DRM_RADEON_CS 0x25 +#define DRM_RADEON_CS2 0x26 + #define DRM_IOCTL_RADEON_CP_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CP_INIT, drm_radeon_init_t) #define DRM_IOCTL_RADEON_CP_START DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_CP_START) #define DRM_IOCTL_RADEON_CP_STOP DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CP_STOP, drm_radeon_cp_stop_t) @@ -521,6 +544,19 @@ typedef struct { #define DRM_IOCTL_RADEON_SURF_ALLOC DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_SURF_ALLOC, drm_radeon_surface_alloc_t) #define DRM_IOCTL_RADEON_SURF_FREE DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_SURF_FREE, drm_radeon_surface_free_t) +#define DRM_IOCTL_RADEON_GEM_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_INFO, struct drm_radeon_gem_info) +#define DRM_IOCTL_RADEON_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_CREATE, struct drm_radeon_gem_create) +#define DRM_IOCTL_RADEON_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_MMAP, struct drm_radeon_gem_mmap) +#define DRM_IOCTL_RADEON_GEM_PIN DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_PIN, struct drm_radeon_gem_pin) +#define DRM_IOCTL_RADEON_GEM_UNPIN DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_UNPIN, struct drm_radeon_gem_unpin) +#define DRM_IOCTL_RADEON_GEM_PREAD DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_PREAD, struct drm_radeon_gem_pread) +#define DRM_IOCTL_RADEON_GEM_PWRITE DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_PWRITE, struct drm_radeon_gem_pwrite) +#define DRM_IOCTL_RADEON_GEM_SET_DOMAIN DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_SET_DOMAIN, struct drm_radeon_gem_set_domain) +#define DRM_IOCTL_RADEON_GEM_WAIT_RENDERING DRM_IOW(DRM_COMMAND_BASE + DRM_RADEON_GEM_WAIT_RENDERING, struct drm_radeon_gem_wait_rendering) +#define DRM_IOCTL_RADEON_CS DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_CS, struct drm_radeon_cs) +#define DRM_IOCTL_RADEON_CS2 DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_CS2, struct drm_radeon_cs2) + + typedef struct drm_radeon_init { enum { RADEON_INIT_CP = 0x01, @@ -677,6 +713,7 @@ typedef struct drm_radeon_indirect { #define RADEON_PARAM_VBLANK_CRTC 13 /* VBLANK CRTC */ #define RADEON_PARAM_FB_LOCATION 14 /* FB location */ #define RADEON_PARAM_NUM_GB_PIPES 15 /* num GB pipes */ +#define RADEON_PARAM_KERNEL_MM 16 typedef struct drm_radeon_getparam { int param; @@ -732,6 +769,7 @@ typedef struct drm_radeon_setparam { #define RADEON_SETPARAM_NEW_MEMMAP 4 /* Use new memory map */ #define RADEON_SETPARAM_PCIGART_TABLE_SIZE 5 /* PCI GART Table Size */ #define RADEON_SETPARAM_VBLANK_CRTC 6 /* VBLANK CRTC */ +#define RADEON_SETPARAM_MM_INIT 7 /* Initialise the mm */ /* 1.14: Clients can allocate/free a surface */ typedef struct drm_radeon_surface_alloc { @@ -747,4 +785,114 @@ typedef struct drm_radeon_surface_free { #define DRM_RADEON_VBLANK_CRTC1 1 #define DRM_RADEON_VBLANK_CRTC2 2 +#define RADEON_GEM_DOMAIN_CPU 0x1 // Cached CPU domain +#define RADEON_GEM_DOMAIN_GTT 0x2 // GTT or cache flushed +#define RADEON_GEM_DOMAIN_VRAM 0x4 // VRAM domain + +/* return to userspace start/size of gtt and vram apertures */ +struct drm_radeon_gem_info { + uint64_t gart_start; + uint64_t gart_size; + uint64_t vram_start; + uint64_t vram_size; + uint64_t vram_visible; +}; + +struct drm_radeon_gem_create { + uint64_t size; + uint64_t alignment; + uint32_t handle; + uint32_t initial_domain; // to allow VRAM to be created + uint32_t no_backing_store; // for VRAM objects - select whether they need backing store + // pretty much front/back/depth don't need it - other things do +}; + +struct drm_radeon_gem_mmap { + uint32_t handle; + uint32_t pad; + uint64_t offset; + uint64_t size; + uint64_t addr_ptr; +}; + +struct drm_radeon_gem_set_domain { + uint32_t handle; + uint32_t read_domains; + uint32_t write_domain; +}; + +struct drm_radeon_gem_wait_rendering { + uint32_t handle; +}; + +struct drm_radeon_gem_pin { + uint32_t handle; + uint32_t pin_domain; + uint64_t alignment; + uint64_t offset; +}; + +struct drm_radeon_gem_unpin { + uint32_t handle; + uint32_t pad; +}; + +struct drm_radeon_gem_busy { + uint32_t handle; + uint32_t busy; +}; + +struct drm_radeon_gem_pread { + /** Handle for the object being read. */ + uint32_t handle; + uint32_t pad; + /** Offset into the object to read from */ + uint64_t offset; + /** Length of data to read */ + uint64_t size; + /** Pointer to write the data into. */ + uint64_t data_ptr; /* void *, but pointers are not 32/64 compatible */ +}; + +struct drm_radeon_gem_pwrite { + /** Handle for the object being written to. */ + uint32_t handle; + uint32_t pad; + /** Offset into the object to write to */ + uint64_t offset; + /** Length of data to write */ + uint64_t size; + /** Pointer to read the data from. */ + uint64_t data_ptr; /* void *, but pointers are not 32/64 compatible */ +}; + + +/* New interface which obsolete all previous interface. + */ + + +struct drm_radeon_cs { + uint32_t dwords; + uint32_t cs_id; + uint64_t packets; +}; + +#define RADEON_CHUNK_ID_RELOCS 0x01 +#define RADEON_CHUNK_ID_IB 0x02 +#define RADEON_CHUNK_ID_OLD 0xff + +struct drm_radeon_cs_chunk { + uint32_t chunk_id; + uint32_t length_dw; + uint64_t chunk_data; +}; + +struct drm_radeon_cs2 { + uint32_t num_chunks; + uint32_t cs_id; + uint64_t chunks; /* this points to uint64_t * which point to + cs chunks */ +}; + + #endif diff --git a/shared-core/radeon_drv.h b/shared-core/radeon_drv.h index dd2b71ae..a87e125c 100644 --- a/shared-core/radeon_drv.h +++ b/shared-core/radeon_drv.h @@ -31,6 +31,7 @@ #ifndef __RADEON_DRV_H__ #define __RADEON_DRV_H__ +#include "atom.h" /* General customization: */ @@ -96,13 +97,13 @@ * 1.25- Add support for r200 vertex programs (R200_EMIT_VAP_PVS_CNTL, * new packet type) * 1.26- Add support for variable size PCI(E) gart aperture - * 1.27- Add support for IGP GART + * 1.27- Add support for IGPGART * 1.28- Add support for VBL on CRTC2 * 1.29- R500 3D cmd buffer support */ #define DRIVER_MAJOR 1 -#define DRIVER_MINOR 29 +#define DRIVER_MINOR 30 #define DRIVER_PATCHLEVEL 0 /* @@ -127,6 +128,7 @@ enum radeon_family { CHIP_RV410, CHIP_RS400, CHIP_RS480, + CHIP_RS600, CHIP_RS690, CHIP_RS740, CHIP_RV515, @@ -135,6 +137,15 @@ enum radeon_family { CHIP_RV560, CHIP_RV570, CHIP_R580, + CHIP_R600, + CHIP_R630, + CHIP_RV610, + CHIP_RV630, + CHIP_RV670, + CHIP_RV620, + CHIP_RV635, + CHIP_RS780, + CHIP_RV770, CHIP_LAST, }; @@ -155,9 +166,42 @@ enum radeon_chip_flags { RADEON_IS_IGPGART = 0x01000000UL, }; +/* + * Errata workarounds + */ +enum radeon_pll_errata { + CHIP_ERRATA_R300_CG = 0x00000001, + CHIP_ERRATA_PLL_DUMMYREADS = 0x00000002, + CHIP_ERRATA_PLL_DELAY = 0x00000004 +}; + +enum radeon_ext_tmds_chip { + RADEON_DVOCHIP_NONE, + RADEON_SIL_164, + RADEON_SIL_1178 +}; + +#if defined(__powerpc__) +enum radeon_mac_model { + RADEON_MAC_NONE, + RADEON_MAC_IBOOK, + RADEON_MAC_POWERBOOK_EXTERNAL, + RADEON_MAC_POWERBOOK_INTERNAL, + RADEON_MAC_POWERBOOK_VGA, + RADEON_MAC_MINI_EXTERNAL, + RADEON_MAC_MINI_INTERNAL, + RADEON_MAC_IMAC_G5_ISIGHT +}; +#endif + + #define GET_RING_HEAD(dev_priv) (dev_priv->writeback_works ? \ - DRM_READ32( (dev_priv)->ring_rptr, 0 ) : RADEON_READ(RADEON_CP_RB_RPTR)) -#define SET_RING_HEAD(dev_priv,val) DRM_WRITE32( (dev_priv)->ring_rptr, 0, (val) ) + (dev_priv->mm.ring_read.bo ? readl(dev_priv->mm.ring_read.kmap.virtual + 0) : DRM_READ32((dev_priv)->ring_rptr, 0 )) : \ + RADEON_READ(RADEON_CP_RB_RPTR)) + +#define SET_RING_HEAD(dev_priv,val) (dev_priv->mm.ring_read.bo ? \ + writel((val), dev_priv->mm.ring_read.kmap.virtual) : \ + DRM_WRITE32((dev_priv)->ring_rptr, 0, (val))) typedef struct drm_radeon_freelist { unsigned int age; @@ -175,7 +219,6 @@ typedef struct drm_radeon_ring_buffer { int rptr_update; /* Double Words */ int rptr_update_l2qw; /* log2 Quad Words */ - int fetch_size; /* Double Words */ int fetch_size_l2ow; /* log2 Oct Words */ u32 tail; @@ -218,17 +261,95 @@ struct radeon_virt_surface { struct drm_file *file_priv; }; +struct radeon_mm_obj { + struct drm_buffer_object *bo; + struct drm_bo_kmap_obj kmap; +}; + +struct radeon_mm_info { + uint64_t vram_offset; // Offset into GPU space + uint64_t vram_size; + uint64_t vram_visible; + + uint64_t gart_start; + uint64_t gart_size; + + uint64_t gart_useable; + + void *pcie_table_backup; + + struct radeon_mm_obj pcie_table; + struct radeon_mm_obj ring; + struct radeon_mm_obj ring_read; + + struct radeon_mm_obj dma_bufs; + struct drm_map fake_agp_map; +}; + +#include "radeon_mode.h" + +struct drm_radeon_master_private { + drm_local_map_t *sarea; + drm_radeon_sarea_t *sarea_priv; +}; + #define RADEON_FLUSH_EMITED (1 < 0) #define RADEON_PURGE_EMITED (1 < 1) +struct drm_radeon_kernel_chunk { + uint32_t chunk_id; + uint32_t length_dw; + uint32_t __user *chunk_data; + uint32_t *kdata; +}; + +struct drm_radeon_cs_parser { + struct drm_device *dev; + struct drm_file *file_priv; + uint32_t num_chunks; + struct drm_radeon_kernel_chunk *chunks; + int ib_index; + int reloc_index; + uint32_t card_offset; + void *ib; +}; + +/* command submission struct */ +struct drm_radeon_cs_priv { + struct mutex cs_mutex; + uint32_t id_wcnt; + uint32_t id_scnt; + uint32_t id_last_wcnt; + uint32_t id_last_scnt; + + int (*parse)(struct drm_radeon_cs_parser *parser); + void (*id_emit)(struct drm_radeon_cs_parser *parser, uint32_t *id); + uint32_t (*id_last_get)(struct drm_device *dev); + /* this ib handling callback are for hidding memory manager drm + * from memory manager less drm, free have to emit ib discard + * sequence into the ring */ + int (*ib_get)(struct drm_radeon_cs_parser *parser); + uint32_t (*ib_get_ptr)(struct drm_device *dev, void *ib); + void (*ib_free)(struct drm_radeon_cs_parser *parser); + /* do a relocation either MM or non-MM */ + int (*relocate)(struct drm_radeon_cs_parser *parser, + uint32_t *reloc, uint32_t *offset); +}; + + + +struct radeon_pm_regs { + uint32_t crtc_ext_cntl; + uint32_t bios_scratch[8]; +}; + typedef struct drm_radeon_private { drm_radeon_ring_buffer_t ring; - drm_radeon_sarea_t *sarea_priv; - u32 fb_location; - u32 fb_size; - int new_memmap; + bool new_memmap; + + bool user_mm_enable; int gart_size; u32 gart_vm_start; @@ -280,8 +401,6 @@ typedef struct drm_radeon_private { unsigned long buffers_offset; unsigned long gart_textures_offset; - drm_local_map_t *sarea; - drm_local_map_t *mmio; drm_local_map_t *cp_ring; drm_local_map_t *ring_rptr; drm_local_map_t *gart_textures; @@ -290,8 +409,8 @@ typedef struct drm_radeon_private { struct mem_block *fb_heap; /* SW interrupt */ + int counter; wait_queue_head_t swi_queue; - atomic_t swi_emitted; int vblank_crtc; uint32_t irq_enable_reg; int irq_enabled; @@ -300,10 +419,6 @@ typedef struct drm_radeon_private { struct radeon_surface surfaces[RADEON_MAX_SURFACES]; struct radeon_virt_surface virt_surfaces[2 * RADEON_MAX_SURFACES]; - unsigned long pcigart_offset; - unsigned int pcigart_offset_set; - struct drm_ati_pcigart_info gart_info; - u32 scratch_ages[5]; unsigned int crtc_last_cnt; @@ -313,9 +428,46 @@ typedef struct drm_radeon_private { uint32_t flags; /* see radeon_chip_flags */ unsigned long fb_aper_offset; + bool mm_enabled; + struct radeon_mm_info mm; + drm_local_map_t *mmio; + + + unsigned long pcigart_offset; + unsigned int pcigart_offset_set; + struct drm_ati_pcigart_info gart_info; + + struct radeon_mode_info mode_info; + + uint8_t *bios; /* copy of the BIOS image */ + bool is_atom_bios; + uint16_t bios_header_start; + u32 fb_location; + u32 fb_size; + bool is_ddr; + u32 ram_width; + + uint32_t mc_fb_location; + uint32_t mc_agp_loc_lo; + uint32_t mc_agp_loc_hi; + + enum radeon_pll_errata pll_errata; + int num_gb_pipes; int track_flush; uint32_t chip_family; /* extract from flags */ + + struct radeon_mm_obj **ib_objs; + /* ib bitmap */ + uint64_t ib_alloc_bitmap; // TO DO replace with a real bitmap + struct drm_radeon_cs_priv cs; + + struct radeon_pm_regs pmregs; + int irq_emitted; + atomic_t irq_received; + + uint32_t aper_size; + int vram_mtrr; } drm_radeon_private_t; typedef struct drm_radeon_buf_priv { @@ -330,8 +482,12 @@ typedef struct drm_radeon_kcmd_buffer { } drm_radeon_kcmd_buffer_t; extern int radeon_no_wb; +extern int radeon_dynclks; +extern int radeon_r4xx_atom; extern struct drm_ioctl_desc radeon_ioctls[]; extern int radeon_max_ioctl; +extern int radeon_agpmode; +extern int radeon_modeset; /* Check whether the given hardware address is inside the framebuffer or the * GART area. @@ -365,6 +521,7 @@ extern struct drm_buf *radeon_freelist_get(struct drm_device * dev); extern int radeon_wait_ring(drm_radeon_private_t * dev_priv, int n); +extern int radeon_do_wait_for_idle(drm_radeon_private_t * dev_priv); extern int radeon_do_cp_idle(drm_radeon_private_t * dev_priv); extern int radeon_mem_alloc(struct drm_device *dev, void *data, struct drm_file *file_priv); @@ -410,6 +567,11 @@ extern int r300_do_cp_cmdbuf(struct drm_device *dev, struct drm_file *file_priv, drm_radeon_kcmd_buffer_t *cmdbuf); +extern int radeon_modeset_cp_suspend(struct drm_device *dev); +extern int radeon_modeset_cp_resume(struct drm_device *dev); +/* radeon_pm.c */ +int radeon_suspend(struct drm_device *dev, pm_message_t state); +int radeon_resume(struct drm_device *dev); /* Flags for stats.boxes */ #define RADEON_BOX_DMA_IDLE 0x1 @@ -418,9 +580,14 @@ extern int r300_do_cp_cmdbuf(struct drm_device *dev, #define RADEON_BOX_WAIT_IDLE 0x8 #define RADEON_BOX_TEXTURE_LOAD 0x10 +#define R600_CONFIG_MEMSIZE 0x5428 +#define R600_CONFIG_APER_SIZE 0x5430 /* Register definitions, register access macros and drmAddMap constants * for Radeon kernel driver. */ + +#include "radeon_reg.h" + #define RADEON_AGP_COMMAND 0x0f60 #define RADEON_AGP_COMMAND_PCI_CONFIG 0x0060 /* offset in PCI config */ # define RADEON_AGP_ENABLE (1<<8) @@ -546,16 +713,6 @@ extern int r300_do_cp_cmdbuf(struct drm_device *dev, #define R520_MC_IND_WR_EN (1 << 24) #define R520_MC_IND_DATA 0x74 -#define RV515_MC_FB_LOCATION 0x01 -#define RV515_MC_AGP_LOCATION 0x02 -#define RV515_MC_AGP_BASE 0x03 -#define RV515_MC_AGP_BASE_2 0x04 - -#define R520_MC_FB_LOCATION 0x04 -#define R520_MC_AGP_LOCATION 0x05 -#define R520_MC_AGP_BASE 0x06 -#define R520_MC_AGP_BASE_2 0x07 - #define RADEON_MPP_TB_CONFIG 0x01c0 #define RADEON_MEM_CNTL 0x0140 #define RADEON_MEM_SDRAM_MODE_REG 0x0158 @@ -620,14 +777,17 @@ extern int r300_do_cp_cmdbuf(struct drm_device *dev, #define RADEON_SCRATCH_REG3 0x15ec #define RADEON_SCRATCH_REG4 0x15f0 #define RADEON_SCRATCH_REG5 0x15f4 +#define RADEON_SCRATCH_REG6 0x15f8 #define RADEON_SCRATCH_UMSK 0x0770 #define RADEON_SCRATCH_ADDR 0x0774 #define RADEON_SCRATCHOFF( x ) (RADEON_SCRATCH_REG_OFFSET + 4*(x)) -#define GET_SCRATCH( x ) (dev_priv->writeback_works \ - ? DRM_READ32( dev_priv->ring_rptr, RADEON_SCRATCHOFF(x) ) \ - : RADEON_READ( RADEON_SCRATCH_REG0 + 4*(x) ) ) +#define GET_SCRATCH( x ) (dev_priv->writeback_works ? \ + (dev_priv->mm.ring_read.bo ? \ + readl(dev_priv->mm.ring_read.kmap.virtual + RADEON_SCRATCHOFF(x)) : \ + DRM_READ32(dev_priv->ring_rptr, RADEON_SCRATCHOFF(x))) : \ + RADEON_READ( RADEON_SCRATCH_REG0 + 4*(x))) #define RADEON_CRTC_CRNT_FRAME 0x0214 #define RADEON_CRTC2_CRNT_FRAME 0x0314 @@ -652,11 +812,13 @@ extern int r300_do_cp_cmdbuf(struct drm_device *dev, # define RADEON_SW_INT_FIRE (1 << 26) # define R500_DISPLAY_INT_STATUS (1 << 0) +#define RADEON_HOST_PATH_CNTL 0x0130 +# define RADEON_HDP_APER_CNTL (1 << 23) +# define RADEON_HP_LIN_RD_CACHE_DIS (1 << 24) +# define RADEON_HDP_SOFT_RESET (1 << 26) +# define RADEON_HDP_READ_BUFFER_INVALIDATED (1 << 27) -#define RADEON_HOST_PATH_CNTL 0x0130 -# define RADEON_HDP_SOFT_RESET (1 << 26) -# define RADEON_HDP_WC_TIMEOUT_MASK (7 << 28) -# define RADEON_HDP_WC_TIMEOUT_28BCLK (7 << 28) +#define RADEON_NB_TOM 0x15c #define RADEON_ISYNC_CNTL 0x1724 # define RADEON_ISYNC_ANY2D_IDLE3D (1 << 0) @@ -695,12 +857,6 @@ extern int r300_do_cp_cmdbuf(struct drm_device *dev, #define RADEON_PP_TXFILTER_1 0x1c6c #define RADEON_PP_TXFILTER_2 0x1c84 -#define R300_RB2D_DSTCACHE_CTLSTAT 0x342c /* use R300_DSTCACHE_CTLSTAT */ -#define R300_DSTCACHE_CTLSTAT 0x1714 -# define R300_RB2D_DC_FLUSH (3 << 0) -# define R300_RB2D_DC_FREE (3 << 2) -# define R300_RB2D_DC_FLUSH_ALL 0xf -# define R300_RB2D_DC_BUSY (1 << 31) #define RADEON_RB3D_CNTL 0x1c3c # define RADEON_ALPHA_BLEND_ENABLE (1 << 0) # define RADEON_PLANE_MASK_ENABLE (1 << 1) @@ -727,11 +883,6 @@ extern int r300_do_cp_cmdbuf(struct drm_device *dev, # define R300_ZC_FLUSH (1 << 0) # define R300_ZC_FREE (1 << 1) # define R300_ZC_BUSY (1 << 31) -#define RADEON_RB3D_DSTCACHE_CTLSTAT 0x325c -# define RADEON_RB3D_DC_FLUSH (3 << 0) -# define RADEON_RB3D_DC_FREE (3 << 2) -# define RADEON_RB3D_DC_FLUSH_ALL 0xf -# define RADEON_RB3D_DC_BUSY (1 << 31) #define R300_RB3D_DSTCACHE_CTLSTAT 0x4e4c # define R300_RB3D_DC_FLUSH (2 << 0) # define R300_RB3D_DC_FREE (2 << 2) @@ -1008,27 +1159,6 @@ extern int r300_do_cp_cmdbuf(struct drm_device *dev, #define RADEON_NUM_VERTICES_SHIFT 16 #define RADEON_COLOR_FORMAT_CI8 2 -#define RADEON_COLOR_FORMAT_ARGB1555 3 -#define RADEON_COLOR_FORMAT_RGB565 4 -#define RADEON_COLOR_FORMAT_ARGB8888 6 -#define RADEON_COLOR_FORMAT_RGB332 7 -#define RADEON_COLOR_FORMAT_RGB8 9 -#define RADEON_COLOR_FORMAT_ARGB4444 15 - -#define RADEON_TXFORMAT_I8 0 -#define RADEON_TXFORMAT_AI88 1 -#define RADEON_TXFORMAT_RGB332 2 -#define RADEON_TXFORMAT_ARGB1555 3 -#define RADEON_TXFORMAT_RGB565 4 -#define RADEON_TXFORMAT_ARGB4444 5 -#define RADEON_TXFORMAT_ARGB8888 6 -#define RADEON_TXFORMAT_RGBA8888 7 -#define RADEON_TXFORMAT_Y8 8 -#define RADEON_TXFORMAT_VYUY422 10 -#define RADEON_TXFORMAT_YVYU422 11 -#define RADEON_TXFORMAT_DXT1 12 -#define RADEON_TXFORMAT_DXT23 14 -#define RADEON_TXFORMAT_DXT45 15 #define R200_PP_TXCBLEND_0 0x2f00 #define R200_PP_TXCBLEND_1 0x2f10 @@ -1214,49 +1344,66 @@ extern int r300_do_cp_cmdbuf(struct drm_device *dev, #define RADEON_RING_HIGH_MARK 128 #define RADEON_PCIGART_TABLE_SIZE (32*1024) +#define RADEON_DEFAULT_RING_SIZE (1024*1024) +#define RADEON_DEFAULT_CP_TIMEOUT 100000 /* usecs */ #define RADEON_READ(reg) DRM_READ32( dev_priv->mmio, (reg) ) #define RADEON_WRITE(reg,val) DRM_WRITE32( dev_priv->mmio, (reg), (val) ) #define RADEON_READ8(reg) DRM_READ8( dev_priv->mmio, (reg) ) #define RADEON_WRITE8(reg,val) DRM_WRITE8( dev_priv->mmio, (reg), (val) ) -#define RADEON_WRITE_PLL( addr, val ) \ -do { \ - RADEON_WRITE8( RADEON_CLOCK_CNTL_INDEX, \ - ((addr) & 0x1f) | RADEON_PLL_WR_EN ); \ - RADEON_WRITE( RADEON_CLOCK_CNTL_DATA, (val) ); \ +extern u32 RADEON_READ_PLL(struct drm_radeon_private *dev_priv, int addr); +extern void RADEON_WRITE_PLL(struct drm_radeon_private *dev_priv, int addr, uint32_t data); +extern u32 RADEON_READ_PCIE(drm_radeon_private_t *dev_priv, int addr); + +#define RADEON_WRITE_P(reg, val, mask) \ +do { \ + uint32_t tmp = RADEON_READ(reg); \ + tmp &= (mask); \ + tmp |= ((val) & ~(mask)); \ + RADEON_WRITE(reg, tmp); \ +} while(0) + +#define RADEON_WRITE_PLL_P(dev_priv, addr, val, mask) \ +do { \ + uint32_t tmp_ = RADEON_READ_PLL(dev_priv, addr); \ + tmp_ &= (mask); \ + tmp_ |= ((val) & ~(mask)); \ + RADEON_WRITE_PLL(dev_priv, addr, tmp_); \ } while (0) -#define RADEON_WRITE_PCIE( addr, val ) \ + + +#define RADEON_WRITE_PCIE(addr, val) \ do { \ - RADEON_WRITE8( RADEON_PCIE_INDEX, \ + RADEON_WRITE8(RADEON_PCIE_INDEX, \ ((addr) & 0xff)); \ - RADEON_WRITE( RADEON_PCIE_DATA, (val) ); \ + RADEON_WRITE(RADEON_PCIE_DATA, (val)); \ } while (0) -#define R500_WRITE_MCIND( addr, val ) \ +#define R500_WRITE_MCIND(addr, val) \ do { \ RADEON_WRITE(R520_MC_IND_INDEX, 0xff0000 | ((addr) & 0xff)); \ RADEON_WRITE(R520_MC_IND_DATA, (val)); \ RADEON_WRITE(R520_MC_IND_INDEX, 0); \ } while (0) -#define RS480_WRITE_MCIND( addr, val ) \ +#define RS480_WRITE_MCIND(addr, val) \ do { \ - RADEON_WRITE( RS480_NB_MC_INDEX, \ + RADEON_WRITE(RS480_NB_MC_INDEX, \ ((addr) & 0xff) | RS480_NB_MC_IND_WR_EN); \ - RADEON_WRITE( RS480_NB_MC_DATA, (val) ); \ - RADEON_WRITE( RS480_NB_MC_INDEX, 0xff ); \ + RADEON_WRITE(RS480_NB_MC_DATA, (val)); \ + RADEON_WRITE(RS480_NB_MC_INDEX, 0xff); \ } while (0) -#define RS690_WRITE_MCIND( addr, val ) \ +#define RS690_WRITE_MCIND(addr, val) \ do { \ RADEON_WRITE(RS690_MC_INDEX, RS690_MC_INDEX_WR_EN | ((addr) & RS690_MC_INDEX_MASK)); \ RADEON_WRITE(RS690_MC_DATA, val); \ RADEON_WRITE(RS690_MC_INDEX, RS690_MC_INDEX_WR_ACK); \ } while (0) -#define IGP_WRITE_MCIND( addr, val ) \ +#define IGP_WRITE_MCIND(addr, val) \ do { \ if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) || \ ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS740)) \ @@ -1306,42 +1453,42 @@ do { \ #define RADEON_FLUSH_CACHE() do { \ if ((dev_priv->flags & RADEON_FAMILY_MASK) <= CHIP_RV280) { \ - OUT_RING(CP_PACKET0(RADEON_RB3D_DSTCACHE_CTLSTAT, 0)); \ - OUT_RING(RADEON_RB3D_DC_FLUSH); \ + OUT_RING(CP_PACKET0(RADEON_RB3D_DSTCACHE_CTLSTAT, 0)); \ + OUT_RING(RADEON_RB3D_DC_FLUSH); \ } else { \ - OUT_RING(CP_PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0)); \ - OUT_RING(R300_RB3D_DC_FLUSH); \ - } \ + OUT_RING(CP_PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0)); \ + OUT_RING(RADEON_RB3D_DC_FLUSH); \ + } \ } while (0) #define RADEON_PURGE_CACHE() do { \ if ((dev_priv->flags & RADEON_FAMILY_MASK) <= CHIP_RV280) { \ - OUT_RING(CP_PACKET0( RADEON_RB3D_DSTCACHE_CTLSTAT, 0)); \ - OUT_RING(RADEON_RB3D_DC_FLUSH | RADEON_RB3D_DC_FREE); \ + OUT_RING(CP_PACKET0(RADEON_RB3D_DSTCACHE_CTLSTAT, 0)); \ + OUT_RING(RADEON_RB3D_DC_FLUSH | RADEON_RB3D_DC_FREE); \ } else { \ - OUT_RING(CP_PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0)); \ - OUT_RING(R300_RB3D_DC_FLUSH | R300_RB3D_DC_FREE ); \ - } \ + OUT_RING(CP_PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0)); \ + OUT_RING(R300_RB3D_DC_FLUSH | R300_RB3D_DC_FREE ); \ + } \ } while (0) #define RADEON_FLUSH_ZCACHE() do { \ if ((dev_priv->flags & RADEON_FAMILY_MASK) <= CHIP_RV280) { \ - OUT_RING( CP_PACKET0( RADEON_RB3D_ZCACHE_CTLSTAT, 0 ) ); \ - OUT_RING( RADEON_RB3D_ZC_FLUSH ); \ + OUT_RING(CP_PACKET0(RADEON_RB3D_ZCACHE_CTLSTAT, 0)); \ + OUT_RING(RADEON_RB3D_ZC_FLUSH); \ } else { \ - OUT_RING( CP_PACKET0( R300_ZB_ZCACHE_CTLSTAT, 0 ) ); \ - OUT_RING( R300_ZC_FLUSH ); \ - } \ + OUT_RING(CP_PACKET0(R300_ZB_ZCACHE_CTLSTAT, 0)); \ + OUT_RING(R300_ZC_FLUSH); \ + } \ } while (0) #define RADEON_PURGE_ZCACHE() do { \ if ((dev_priv->flags & RADEON_FAMILY_MASK) <= CHIP_RV280) { \ - OUT_RING(CP_PACKET0(RADEON_RB3D_ZCACHE_CTLSTAT, 0)); \ - OUT_RING(RADEON_RB3D_ZC_FLUSH | RADEON_RB3D_ZC_FREE); \ + OUT_RING(CP_PACKET0(RADEON_RB3D_ZCACHE_CTLSTAT, 0)); \ + OUT_RING(RADEON_RB3D_ZC_FLUSH | RADEON_RB3D_ZC_FREE); \ } else { \ - OUT_RING(CP_PACKET0(R300_ZB_ZCACHE_CTLSTAT, 0)); \ - OUT_RING(R300_ZC_FLUSH | R300_ZC_FREE); \ - } \ + OUT_RING(CP_PACKET0(R300_ZB_ZCACHE_CTLSTAT, 0)); \ + OUT_RING(R300_ZC_FLUSH | R300_ZC_FREE); \ + } \ } while (0) /* ================================================================ @@ -1361,7 +1508,8 @@ do { \ #define VB_AGE_TEST_WITH_RETURN( dev_priv ) \ do { \ - drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; \ + struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv; \ + drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv; \ if ( sarea_priv->last_dispatch >= RADEON_MAX_VB_AGE ) { \ int __ret = radeon_do_cp_idle( dev_priv ); \ if ( __ret ) return __ret; \ @@ -1391,15 +1539,16 @@ do { \ #define RADEON_VERBOSE 0 -#define RING_LOCALS int write, _nr; unsigned int mask; u32 *ring; +#define RING_LOCALS int write, _nr, _align_nr; unsigned int mask; u32 *ring; #define BEGIN_RING( n ) do { \ if ( RADEON_VERBOSE ) { \ DRM_INFO( "BEGIN_RING( %d )\n", (n)); \ } \ - if ( dev_priv->ring.space <= (n) * sizeof(u32) ) { \ + _align_nr = (n + 0xf) & ~0xf; \ + if (dev_priv->ring.space <= (_align_nr * sizeof(u32))) { \ COMMIT_RING(); \ - radeon_wait_ring( dev_priv, (n) * sizeof(u32) ); \ + radeon_wait_ring(dev_priv, _align_nr * sizeof(u32)); \ } \ _nr = n; dev_priv->ring.space -= (n) * sizeof(u32); \ ring = dev_priv->ring.start; \ @@ -1416,19 +1565,14 @@ do { \ DRM_ERROR( \ "ADVANCE_RING(): mismatch: nr: %x write: %x line: %d\n", \ ((dev_priv->ring.tail + _nr) & mask), \ - write, __LINE__); \ + write, __LINE__); \ } else \ dev_priv->ring.tail = write; \ } while (0) #define COMMIT_RING() do { \ - /* Flush writes to ring */ \ - DRM_MEMORYBARRIER(); \ - GET_RING_HEAD( dev_priv ); \ - RADEON_WRITE( RADEON_CP_RB_WPTR, dev_priv->ring.tail ); \ - /* read from PCI bus to ensure correct posting */ \ - RADEON_READ( RADEON_CP_RB_RPTR ); \ -} while (0) + radeon_commit_ring(dev_priv); \ + } while(0) #define OUT_RING( x ) do { \ if ( RADEON_VERBOSE ) { \ @@ -1467,4 +1611,148 @@ do { \ write &= mask; \ } while (0) +/* radeon GEM->TTM munger */ +struct drm_radeon_gem_object { + /* wrap a TTM bo */ + struct drm_buffer_object *bo; + struct drm_fence_object *fence; + struct drm_gem_object *obj; + +}; + +extern int radeon_gem_info_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + +extern int radeon_gem_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + +extern int radeon_gem_pwrite_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + +extern int radeon_gem_mmap_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int radeon_gem_pread_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + +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); + +/* 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 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); + +extern void radeon_gart_flush(struct drm_device *dev); +extern uint64_t radeon_evict_flags(struct drm_buffer_object *bo); + +#define BREADCRUMB_BITS 31 +#define BREADCRUMB_MASK ((1U << BREADCRUMB_BITS) - 1) + +/* Breadcrumb - swi irq */ +#define READ_BREADCRUMB(dev_priv) GET_SCRATCH(3) + +static inline int radeon_update_breadcrumb(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct drm_radeon_master_private *master_priv; + + ++dev_priv->counter; + if (dev_priv->counter > BREADCRUMB_MASK) + dev_priv->counter = 1; + + if (dev->primary->master) { + master_priv = dev->primary->master->driver_priv; + + if (master_priv->sarea_priv) + master_priv->sarea_priv->last_fence = dev_priv->counter; + } + return dev_priv->counter; +} + +#define radeon_is_avivo(dev_priv) ((dev_priv->chip_family >= CHIP_RS600)) + +#define radeon_is_dce3(dev_priv) ((dev_priv->chip_family >= CHIP_RV620)) + +#define radeon_is_rv100(dev_priv) ((dev_priv->chip_family == CHIP_RV100) || \ + (dev_priv->chip_family == CHIP_RV200) || \ + (dev_priv->chip_family == CHIP_RS100) || \ + (dev_priv->chip_family == CHIP_RS200) || \ + (dev_priv->chip_family == CHIP_RV250) || \ + (dev_priv->chip_family == CHIP_RV280) || \ + (dev_priv->chip_family == CHIP_RS300)) + +#define radeon_is_r300(dev_priv) ((dev_priv->chip_family == CHIP_R300) || \ + (dev_priv->chip_family == CHIP_RV350) || \ + (dev_priv->chip_family == CHIP_R350) || \ + (dev_priv->chip_family == CHIP_RV380) || \ + (dev_priv->chip_family == CHIP_R420) || \ + (dev_priv->chip_family == CHIP_R423) || \ + (dev_priv->chip_family == CHIP_RV410) || \ + (dev_priv->chip_family == CHIP_RS400) || \ + (dev_priv->chip_family == CHIP_RS480)) + +#define radeon_bios8(dev_priv, v) (dev_priv->bios[v]) +#define radeon_bios16(dev_priv, v) (dev_priv->bios[v] | (dev_priv->bios[(v) + 1] << 8)) +#define radeon_bios32(dev_priv, v) ((dev_priv->bios[v]) | \ + (dev_priv->bios[(v) + 1] << 8) | \ + (dev_priv->bios[(v) + 2] << 16) | \ + (dev_priv->bios[(v) + 3] << 24)) + +extern void radeon_pll_errata_after_index(struct drm_radeon_private *dev_priv); +extern int radeon_emit_irq(struct drm_device * dev); + +extern void radeon_gem_free_object(struct drm_gem_object *obj); +extern int radeon_gem_init_object(struct drm_gem_object *obj); +extern int radeon_gem_mm_init(struct drm_device *dev); +extern void radeon_gem_mm_fini(struct drm_device *dev); +extern int radeon_gem_pin_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int radeon_gem_unpin_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int radeon_gem_object_pin(struct drm_gem_object *obj, + uint32_t alignment, uint32_t pin_domain); +int radeon_gem_object_unpin(struct drm_gem_object *obj); +int radeon_gem_set_domain_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int radeon_gem_wait_rendering(struct drm_device *dev, void *data, + struct drm_file *file_priv); +struct drm_gem_object *radeon_gem_object_alloc(struct drm_device *dev, int size, int alignment, + int initial_domain, bool discardable); +int radeon_modeset_init(struct drm_device *dev); +void radeon_modeset_cleanup(struct drm_device *dev); +extern u32 radeon_read_mc_reg(drm_radeon_private_t *dev_priv, int addr); +extern void radeon_write_mc_reg(drm_radeon_private_t *dev_priv, u32 addr, u32 val); +void radeon_read_agp_location(drm_radeon_private_t *dev_priv, u32 *agp_lo, u32 *agp_hi); +void radeon_write_fb_location(drm_radeon_private_t *dev_priv, u32 fb_loc); +extern void radeon_set_pcigart(drm_radeon_private_t * dev_priv, int on); +#define RADEONFB_CONN_LIMIT 4 + +extern int radeon_master_create(struct drm_device *dev, struct drm_master *master); +extern void radeon_master_destroy(struct drm_device *dev, struct drm_master *master); +extern void radeon_cp_dispatch_flip(struct drm_device * dev, struct drm_master *master); +extern int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *fpriv); +extern int radeon_cs2_ioctl(struct drm_device *dev, void *data, struct drm_file *fpriv); +extern int radeon_cs_init(struct drm_device *dev); +void radeon_gem_update_offsets(struct drm_device *dev, struct drm_master *master); +void radeon_init_memory_map(struct drm_device *dev); +void radeon_enable_bm(struct drm_radeon_private *dev_priv); + +extern int radeon_gem_proc_init(struct drm_minor *minor); +extern void radeon_gem_proc_cleanup(struct drm_minor *minor); +#define MARK_SAFE 1 +#define MARK_CHECK_OFFSET 2 +#define MARK_CHECK_SCISSOR 3 + +extern void radeon_commit_ring(drm_radeon_private_t *dev_priv); + +extern int r300_check_range(unsigned reg, int count); +extern int r300_get_reg_flags(unsigned reg); #endif /* __RADEON_DRV_H__ */ diff --git a/shared-core/radeon_irq.c b/shared-core/radeon_irq.c index 69569961..46a27943 100644 --- a/shared-core/radeon_irq.c +++ b/shared-core/radeon_irq.c @@ -195,11 +195,14 @@ irqreturn_t radeon_driver_irq_handler(DRM_IRQ_ARGS) if (!stat) return IRQ_NONE; + atomic_inc(&dev_priv->irq_received); stat &= dev_priv->irq_enable_reg; /* SW interrupt */ - if (stat & RADEON_SW_INT_TEST) + if (stat & RADEON_SW_INT_TEST) { DRM_WAKEUP(&dev_priv->swi_queue); + radeon_fence_handler(dev); + } /* VBLANK interrupt */ if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS690) { @@ -216,20 +219,23 @@ 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; + int i; RING_LOCALS; - atomic_inc(&dev_priv->swi_emitted); - ret = atomic_read(&dev_priv->swi_emitted); + if (!dev_priv->irq_emitted) { + ret = radeon_update_breadcrumb(dev); - BEGIN_RING(4); - OUT_RING_REG(RADEON_LAST_SWI_REG, ret); - OUT_RING_REG(RADEON_GEN_INT_STATUS, RADEON_SW_INT_FIRE); - ADVANCE_RING(); - COMMIT_RING(); + BEGIN_RING(4); + OUT_RING_REG(RADEON_LAST_SWI_REG, ret); + OUT_RING_REG(RADEON_GEN_INT_STATUS, RADEON_SW_INT_FIRE); + ADVANCE_RING(); + COMMIT_RING(); + } else + ret = dev_priv->irq_emitted; return ret; } @@ -240,13 +246,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); + READ_BREADCRUMB(dev_priv) >= swi_nr); return ret; } @@ -341,7 +347,6 @@ int radeon_driver_irq_postinstall(struct drm_device * dev) (drm_radeon_private_t *) dev->dev_private; int ret; - atomic_set(&dev_priv->swi_emitted, 0); DRM_INIT_WAITQUEUE(&dev_priv->swi_queue); ret = drm_vblank_init(dev, 2); diff --git a/shared-core/radeon_mem.c b/shared-core/radeon_mem.c index dbd73c50..2622cf3c 100644 --- a/shared-core/radeon_mem.c +++ b/shared-core/radeon_mem.c @@ -294,7 +294,7 @@ int radeon_mem_init_heap(struct drm_device *dev, void *data, struct drm_file *fi return -EFAULT; if (*heap) { - DRM_ERROR("heap already initialized?"); + DRM_DEBUG("heap already initialized?"); return -EFAULT; } diff --git a/shared-core/radeon_ms.h b/shared-core/radeon_ms.h new file mode 100644 index 00000000..f3bbc9a3 --- /dev/null +++ b/shared-core/radeon_ms.h @@ -0,0 +1,591 @@ +/* + * 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" +#include "radeon_ms_rom.h" +#include "radeon_ms_properties.h" +#include "amd.h" +#include "amd_legacy.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 + +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_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; + /* framebuffer */ + struct amd_fb *fb; + /* card family */ + uint32_t usec_timeout; + uint32_t family; + 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; + /* command buffer informations */ + struct amd_cmd_module cmd_module; + /* abstract asic specific structures */ + struct radeon_ms_rom rom; + struct radeon_ms_properties properties; + void *fence; +}; + + +/* 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); +uint64_t radeon_ms_evict_flags(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_combios.c */ +int radeon_ms_combios_get_properties(struct drm_device *dev); +int radeon_ms_connectors_from_combios(struct drm_device *dev); +int radeon_ms_outputs_from_combios(struct drm_device *dev); + +/* 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); +int radeon_ms_resetcp(struct drm_device *dev, void *data, + struct drm_file *file_priv); + +/* 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_family.c */ +int radeon_ms_family_init(struct drm_device *dev); + +/* radeon_ms_fence.c */ +void r3xx_fence_handler(struct drm_device * dev); +int r3xx_fence_types(struct drm_buffer_object *bo, + uint32_t * class, uint32_t * type); + +/* radeon_ms_fb.c */ +int radeonfb_probe(struct drm_device *dev, struct drm_crtc *crtc, struct drm_output *output); +int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *fb); + +/* 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); +int 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); +int radeon_ms_connectors_from_rom(struct drm_device *dev); +void radeon_ms_outputs_destroy(struct drm_device *dev); +int radeon_ms_outputs_from_properties(struct drm_device *dev); +int radeon_ms_outputs_from_rom(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_properties.c */ +int radeon_ms_properties_init(struct drm_device *dev); + +/* radeon_ms_rom.c */ +int radeon_ms_rom_get_properties(struct drm_device *dev); +int radeon_ms_rom_init(struct drm_device *dev); + +/* 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); + +/* 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..b366ae5a --- /dev/null +++ b/shared-core/radeon_ms_bo.c @@ -0,0 +1,321 @@ +/* + * 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" + +#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) + +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(PACKET3_OPCODE_BITBLT, 5); + 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_AMD_FENCE_TYPE_R | + DRM_AMD_FENCE_TYPE_W, + DRM_AMD_FENCE_FLAG_FLUSH, + 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.flags = 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_ttm_bind(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; +} + +uint64_t radeon_ms_evict_flags(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..333fd558 --- /dev/null +++ b/shared-core/radeon_ms_bus.c @@ -0,0 +1,461 @@ +/* + * 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; + struct page *dummy_read_page; + 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, + struct page *dummy_read_page); +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]; + + if (!page) { + cur_page = pcie_gart->dummy_read_page; + } + /* 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 page *dummy_read_page) +{ + 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_INFO("[radeon_ms] release agp\n"); + 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 %d\n", ret); + 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_combios.c b/shared-core/radeon_ms_combios.c new file mode 100644 index 00000000..65609af9 --- /dev/null +++ b/shared-core/radeon_ms_combios.c @@ -0,0 +1,411 @@ +/* + * 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> + */ +#include "radeon_ms.h" + +extern struct radeon_ms_output radeon_ms_dac1; +extern struct radeon_ms_output radeon_ms_dac2; +extern const struct drm_output_funcs radeon_ms_output_funcs; + +static struct combios_connector_chip_info * +radeon_ms_combios_get_connector_chip_info(struct drm_device *dev, int chip_num) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_ms_rom *rom = &dev_priv->rom; + struct combios_header *header; + struct combios_connector_table *connector_table; + struct combios_connector_chip_info *connector_chip_info; + uint32_t offset; + int numof_chips, i; + + if (rom->type != ROM_COMBIOS || rom->rom_image == NULL) { + return NULL; + } + header = rom->rom.combios_header; + offset = header->usPointerToExtendedInitTable2; + if ((offset + sizeof(struct combios_connector_table)) > rom->rom_size) { + DRM_INFO("[radeon_ms] wrong COMBIOS connector offset\n"); + return NULL; + } + if (!offset) { + return NULL; + } + connector_table = (struct combios_connector_table *) + &rom->rom_image[offset]; + numof_chips = (connector_table->ucConnectorHeader & + BIOS_CONNECTOR_HEADER__NUMBER_OF_CHIPS__MASK) >> + BIOS_CONNECTOR_HEADER__NUMBER_OF_CHIPS__SHIFT; + DRM_INFO("[radeon_ms] COMBIOS number of chip: %d (table rev: %d)\n", + numof_chips, + (connector_table->ucConnectorHeader & + BIOS_CONNECTOR_HEADER__TABLE_REVISION__MASK) >> + BIOS_CONNECTOR_HEADER__TABLE_REVISION__SHIFT); + for (i = 0; i < numof_chips; i++) { + int chip; + + connector_chip_info = &connector_table->sChipConnectorInfo[i]; + chip = (connector_chip_info->ucChipHeader & + BIOS_CHIPINFO_HEADER__CHIP_NUMBER__MASK) >> + BIOS_CHIPINFO_HEADER__CHIP_NUMBER__SHIFT; + DRM_INFO("[radeon_ms] COMBIOS chip: %d (asked for: %d)\n", + chip, chip_num); + if (chip == chip_num) { + return connector_chip_info; + } + } + return NULL; +} + +static int radeon_combios_get_connector_infos(struct drm_device *dev, + int connector_info, + int *connector_type, + int *ddc_line, + int *tmds_type, + int *dac_type) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + *connector_type = (connector_info & BIOS_CONNECTOR_INFO__TYPE__MASK) >> + BIOS_CONNECTOR_INFO__TYPE__SHIFT; + *ddc_line = (connector_info & BIOS_CONNECTOR_INFO__DDC_LINE__MASK) >> + BIOS_CONNECTOR_INFO__DDC_LINE__SHIFT; + *tmds_type = (connector_info & BIOS_CONNECTOR_INFO__TMDS_TYPE__MASK) >> + BIOS_CONNECTOR_INFO__TMDS_TYPE__SHIFT; + *dac_type = (connector_info & BIOS_CONNECTOR_INFO__DAC_TYPE__MASK) >> + BIOS_CONNECTOR_INFO__DAC_TYPE__SHIFT; + + /* most XPRESS chips seem to specify DDC_CRT2 for their + * VGA DDC port, however DDC never seems to work on that + * port. Some have reported success on DDC_MONID, so + * lets see what happens with that. + */ + if (dev_priv->family == CHIP_RS400 && + *connector_type == BIOS_CONNECTOR_TYPE__CRT && + *ddc_line == BIOS_DDC_LINE__CRT2) { + *ddc_line = BIOS_DDC_LINE__MONID01; + } + /* XPRESS desktop chips seem to have a proprietary + * connector listed for DVI-D, try and do the right + * thing here. + */ + if (dev_priv->family == CHIP_RS400 && + *connector_type == BIOS_CONNECTOR_TYPE__PROPRIETARY) { + DRM_INFO("[radeon_ms] COMBIOS Proprietary connector " + "found, assuming DVI-D\n"); + *dac_type = 2; + *tmds_type = BIOS_TMDS_TYPE__EXTERNAL; + *connector_type = BIOS_CONNECTOR_TYPE__DVI_D; + } + return 0; +} + +static int radeon_ms_combios_connector_add(struct drm_device *dev, + int connector_number, + int connector_type, + uint32_t i2c_reg) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_ms_connector *connector = NULL; + struct drm_output *output = NULL; + + connector = drm_alloc(sizeof(struct radeon_ms_connector), + DRM_MEM_DRIVER); + if (connector == NULL) { + radeon_ms_connectors_destroy(dev); + return -ENOMEM; + } + memset(connector, 0, sizeof(struct radeon_ms_connector)); + connector->monitor_type = MT_NONE; + connector->type = connector_type; + connector->i2c_reg = i2c_reg; + + switch (connector->type) { + case CONNECTOR_VGA: + sprintf(connector->name, "VGA"); + break; + case CONNECTOR_DVI_I: + sprintf(connector->name, "DVI-I"); + break; + case CONNECTOR_DVI_D: + sprintf(connector->name, "DVI-D"); + break; + default: + sprintf(connector->name, "UNKNOWN-CONNECTOR"); + break; + } + + if (i2c_reg) { + connector->i2c = radeon_ms_i2c_create(dev, + connector->i2c_reg, + connector->name); + if (connector->i2c == NULL) { + radeon_ms_connectors_destroy(dev); + return -ENOMEM; + } + } else { + connector->i2c = NULL; + } + + output = drm_output_create(dev, &radeon_ms_output_funcs, + connector->type); + if (output == NULL) { + radeon_ms_connectors_destroy(dev); + return -EINVAL; + } + connector->output = output; + output->driver_private = connector; + output->possible_crtcs = 0x3; + dev_priv->connectors[connector_number] = connector; + return 0; +} + +int radeon_ms_combios_get_properties(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_ms_rom *rom = &dev_priv->rom; + struct combios_pll_block *pll_block; + struct combios_header *header; + uint32_t offset; + + if (rom->type != ROM_COMBIOS || rom->rom_image == NULL) { + return 0; + } + header = rom->rom.combios_header; + offset = header->usPointerToPllInfoBlock; + if ((offset + sizeof(struct combios_pll_block)) > rom->rom_size) { + DRM_INFO("[radeon_ms] wrong COMBIOS pll block offset\n"); + return 0; + } + if (!offset) { + return 0; + } + pll_block = (struct combios_pll_block *)&rom->rom_image[offset]; + dev_priv->properties.pll_reference_freq = pll_block->usDotClockRefFreq; + dev_priv->properties.pll_reference_div = pll_block->usDotClockRefDiv; + dev_priv->properties.pll_min_pll_freq = pll_block->ulDotClockMinFreq; + dev_priv->properties.pll_max_pll_freq = pll_block->ulDotClockMaxFreq; + dev_priv->properties.pll_reference_freq *= 10; + dev_priv->properties.pll_min_pll_freq *= 10; + dev_priv->properties.pll_max_pll_freq *= 10; + DRM_INFO("[radeon_ms] COMBIOS pll reference frequency : %d\n", + dev_priv->properties.pll_reference_freq); + DRM_INFO("[radeon_ms] COMBIOS pll reference divider : %d\n", + dev_priv->properties.pll_reference_div); + DRM_INFO("[radeon_ms] COMBIOS pll minimum frequency : %d\n", + dev_priv->properties.pll_min_pll_freq); + DRM_INFO("[radeon_ms] COMBIOS pll maximum frequency : %d\n", + dev_priv->properties.pll_max_pll_freq); + return 1; +} + +int radeon_ms_connectors_from_combios(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct combios_connector_chip_info *connector_chip_info; + int connector_type, ddc_line, tmds_type, dac_type; + int dac1, dac2, tmdsint, tmdsext; + int numof_connector, i, c = 0, added, j; + uint32_t i2c_reg; + int ret; + + dac1 = dac2 = tmdsint = tmdsext = -1; + connector_chip_info = radeon_ms_combios_get_connector_chip_info(dev, 1); + if (connector_chip_info == NULL) { + return -1; + } + numof_connector = (connector_chip_info->ucChipHeader & + BIOS_CHIPINFO_HEADER__NUMBER_OF_CONNECTORS__MASK) >> + BIOS_CHIPINFO_HEADER__NUMBER_OF_CONNECTORS__SHIFT; + DRM_INFO("[radeon_ms] COMBIOS number of connector: %d\n", + numof_connector); + for (i = 0; i < numof_connector; i++) { + int connector_info = connector_chip_info->sConnectorInfo[i]; + + ret = radeon_combios_get_connector_infos(dev, + connector_info, + &connector_type, + &ddc_line, + &tmds_type, + &dac_type); + + switch (ddc_line) { + case BIOS_DDC_LINE__MONID01: + i2c_reg = GPIO_MONID; + break; + case BIOS_DDC_LINE__DVI: + i2c_reg = GPIO_DVI_DDC; + break; + case BIOS_DDC_LINE__VGA: + i2c_reg = GPIO_DDC1; + break; + case BIOS_DDC_LINE__CRT2: + i2c_reg = GPIO_CRT2_DDC; + break; + case BIOS_DDC_LINE__GPIOPAD: + i2c_reg = VIPPAD_EN; + break; + case BIOS_DDC_LINE__ZV_LCDPAD: + i2c_reg = VIPPAD1_EN; + break; + default: + i2c_reg = 0; + break; + } + added = 0; + switch (connector_type) { + case BIOS_CONNECTOR_TYPE__CRT: + ret = radeon_ms_combios_connector_add(dev, c, + CONNECTOR_VGA, + i2c_reg); + if (ret) { + return ret; + } + added = 1; + break; + case BIOS_CONNECTOR_TYPE__DVI_I: + ret = radeon_ms_combios_connector_add(dev, c, + CONNECTOR_DVI_I, + i2c_reg); + if (ret) { + return ret; + } + added = 1; + break; + case BIOS_CONNECTOR_TYPE__DVI_D: + ret = radeon_ms_combios_connector_add(dev, c, + CONNECTOR_DVI_D, + i2c_reg); + if (ret) { + return ret; + } + added = 1; + break; + default: + break; + } + if (added) { + j = 0; + /* find to which output this connector is associated + * by following same algo as in: + * radeon_ms_outputs_from_combios*/ + switch (dac_type) { + case BIOS_DAC_TYPE__CRT: + if (dac1 == -1) { + dac1 = c; + } + dev_priv->connectors[c]->outputs[j++] = dac1; + break; + case BIOS_DAC_TYPE__NON_CRT: + if (dac2 == -1) { + dac2 = c; + } + dev_priv->connectors[c]->outputs[j++] = dac2; + break; + } +#if 0 + switch (tmds_type) { + case BIOS_TMDS_TYPE__INTERNAL: + if (tmdsint == -1) { + tmdsint = c; + } + dev_priv->connectors[c]->outputs[j++] = tmdsint; + break; + case BIOS_TMDS_TYPE__EXTERNAL: + if (tmdsext == -1) { + tmdsext = c; + } + dev_priv->connectors[c]->outputs[j++] = tmdsext; + break; + } +#endif + c++; + } + } + return c; +} + +int radeon_ms_outputs_from_combios(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct combios_connector_chip_info *connector_chip_info; + int connector_type, ddc_line, tmds_type, dac_type; + int numof_connector, i, dac1_present, dac2_present, c = 0; + int ret; + + dac1_present = dac2_present = 0; + connector_chip_info = radeon_ms_combios_get_connector_chip_info(dev, 1); + if (connector_chip_info == NULL) { + return -1; + } + numof_connector = (connector_chip_info->ucChipHeader & + BIOS_CHIPINFO_HEADER__NUMBER_OF_CONNECTORS__MASK) >> + BIOS_CHIPINFO_HEADER__NUMBER_OF_CONNECTORS__SHIFT; + DRM_INFO("[radeon_ms] COMBIOS number of connector: %d\n", + numof_connector); + for (i = 0; i < numof_connector; i++) { + int connector_info = connector_chip_info->sConnectorInfo[i]; + + ret = radeon_combios_get_connector_infos(dev, + connector_info, + &connector_type, + &ddc_line, + &tmds_type, + &dac_type); + + if (!dac1_present && dac_type == BIOS_DAC_TYPE__CRT) { + dev_priv->outputs[c] = + drm_alloc(sizeof(struct radeon_ms_output), + DRM_MEM_DRIVER); + if (dev_priv->outputs[c] == NULL) { + radeon_ms_outputs_destroy(dev); + return -ENOMEM; + } + memcpy(dev_priv->outputs[c], &radeon_ms_dac1, + sizeof(struct radeon_ms_output)); + dev_priv->outputs[c]->dev = dev; + dev_priv->outputs[c]->initialize(dev_priv->outputs[c]); + dac1_present = 1; + c++; + } + if (!dac2_present && dac_type == BIOS_DAC_TYPE__NON_CRT) { + dev_priv->outputs[c] = + drm_alloc(sizeof(struct radeon_ms_output), + DRM_MEM_DRIVER); + if (dev_priv->outputs[c] == NULL) { + radeon_ms_outputs_destroy(dev); + return -ENOMEM; + } + memcpy(dev_priv->outputs[c], &radeon_ms_dac2, + sizeof(struct radeon_ms_output)); + dev_priv->outputs[c]->dev = dev; + dev_priv->outputs[c]->initialize(dev_priv->outputs[c]); + dac1_present = 1; + c++; + } + } + return c; +} diff --git a/shared-core/radeon_ms_combios.h b/shared-core/radeon_ms_combios.h new file mode 100644 index 00000000..fbceadf2 --- /dev/null +++ b/shared-core/radeon_ms_combios.h @@ -0,0 +1,385 @@ +/* + * Copyright 2006-2007 Advanced Micro Devices, Inc. + * Copyright 2007 Jérôme Glisse + * + * 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. + */ +/* + * Authors: + * Jérôme Glisse <glisse@freedesktop.org> + */ +#ifndef __RADEON_MS_COMBIOS_H__ +#define __RADEON_MS_COMBIOS_H__ + +#pragma pack(1) + +#define ROM_HEADER 0x48 + +struct combios_header +{ + uint8_t ucTypeDefinition; + uint8_t ucExtFunctionCode; + uint8_t ucOemID1; + uint8_t ucOemID2; + uint8_t ucBiosMajorRev; + uint8_t ucBiosMinorRev; + uint16_t usStructureSize; + uint16_t usPointerToSmi; + uint16_t usPointerToPmid; + uint16_t usPointerToInitTable; + uint16_t usPointerToCrcChecksumBlock; + uint16_t usPointerToConfigFilename; + uint16_t usPointerToLogonMessage; + uint16_t usPointerToMiscInfo; + uint16_t usPciBusDevInitCode; + uint16_t usBiosRuntimeSegmentAddress; + uint16_t usIoBaseAddress; + uint16_t usSubsystemVendorID; + uint16_t usSubsystemID; + uint16_t usPostVendorID; + uint16_t usInt10Offset; + uint16_t usInt10Segment; + uint16_t usMonitorInfo; + uint16_t usPointerToConfigBlock; + uint16_t usPointerToDacDelayInfo; + uint16_t usPointerToCapDataStruct; + uint16_t usPointerToInternalCrtTables; + uint16_t usPointerToPllInfoBlock; + uint16_t usPointerToTVInfoTable; + uint16_t usPointerToDFPInfoTable; + uint16_t usPointerToHWConfigTable; + uint16_t usPointerToMMConfigTable; + uint32_t ulTVStdPatchTableSignature; + uint16_t usPointerToTVStdPatchTable; + uint16_t usPointerToPanelInfoTable; + uint16_t usPointerToAsicInfoTable; + uint16_t usPointerToAuroraInfoTable; + uint16_t usPointerToPllInitTable; + uint16_t usPointerToMemoryConfigTable; + uint16_t usPointerToSaveMaskTable; + uint16_t usPointerHardCodedEdid; + uint16_t usPointerToExtendedInitTable1; + uint16_t usPointerToExtendedInitTable2; + uint16_t usPointerToDynamicClkTable; + uint16_t usPointerToReservedMemoryTable; + uint16_t usPointerToBridgetInitTable; + uint16_t usPointerToExtTMDSInitTable; + uint16_t usPointerToMemClkInfoTable; + uint16_t usPointerToExtDACTable; + uint16_t usPointerToMiscInfoTable; +}; + +struct combios_pll_block +{ + /* Usually 6 */ + uint8_t ucPLLBiosVersion; + /* Size in bytes */ + uint8_t ucStructureSize; + /* Dot clock entry used for accelerated modes */ + uint8_t ucDotClockEntry; + /* Dot clock entry used for extended VGA modes */ + uint8_t ucDotClockEntryVga; + /* Offset into internal clock table used for by VGA parameter table */ + uint16_t usPointerToInternalClock; + /* Offset into actual programmed frequency table at POST */ + uint16_t usPointerToFreqTable; + /* XCLK setting, (memory clock in 10 KHz units) */ + uint16_t usXclkSetting; + /* MCLK setting, (engine clock in 10 KHz units) */ + uint16_t usMclkSetting; + /* Number of PLL information block to follow, currently value is 3 */ + uint8_t ucPllInfoBlockNumber; + /* Size of each PLL information block */ + uint8_t ucPllInfoBlockSize; + /* Reference frequency of the dot clock */ + uint16_t usDotClockRefFreq; + /* Reference Divider of the dot clock */ + uint16_t usDotClockRefDiv; + /* Min Frequency supported before post divider for the dot clock */ + uint32_t ulDotClockMinFreq; + /* Max Frequency can be supported for the dot clock */ + uint32_t ulDotClockMaxFreq; + /* Reference frequency of the MCLK, engine clock */ + uint16_t usMclkRefFreq; + /* Reference Divider of the MCLK, engine clock */ + uint16_t usMclkRefDiv; + /* Min Frequency supported before post divider for MCLK, engine clock */ + uint32_t ulMclkMinFreq; + /* Max Frequency can be supported for the MCLK, engine clock */ + uint32_t ulMclkMaxFreq; + /* Reference frequency of the XCLK, memory clock */ + uint16_t usXclkRefFreq; + /* Reference Divider of the XCLK, memory clock */ + uint16_t usXclkRefDiv; + /* Min Frequency supported before post divider for XCLK, memory clock */ + uint32_t ulXclkMinFreq; + /* Max Frequency can be supported for the XCLK, memory clock */ + uint32_t ulXclkMaxFreq; + + /*this is the PLL Information Table Extended structure version 10 */ + uint8_t ucNumberOfExtendedPllBlocks; + uint8_t ucSizePLLDefinition; + uint16_t ulCrystalFrequencyPixelClock_pll; + uint32_t ulMinInputPixelClockPLLFrequency; + uint32_t ulMaxInputPixelClockPLLFrequency; + uint32_t ulMinOutputPixelClockPLLFrequency; + uint32_t ulMaxOutputPixelClockPLLFrequency; + + /*version 11 */ + uint16_t ulCrystalFrequencyEngineClock_pll; + uint32_t ulMinInputFrequencyEngineClock_pll; + uint32_t ulMaxInputFrequencyEngineClock_pll; + uint32_t ulMinOutputFrequencyEngineClock_pll; + uint32_t ulMaxOutputFrequencyEngineClock_pll; + uint16_t ulCrystalFrequencyMemoryClock_pll; + uint32_t ulMinInputFrequencyMemoryClock_pll; + uint32_t ulMaxInputFrequencyMemoryClock_pll; + uint32_t ulMinOutputFrequencyMemoryClock_pll; + uint32_t ulMaxOutputFrequencyMemoryClock_pll; + uint32_t ulMaximumDACOutputFrequency; +}; + +#define MAX_NO_OF_LCD_RES_TIMING 25 + +struct panel_information_table +{ + uint8_t ucPanelIdentification; + uint8_t ucPanelIDString[24]; + uint16_t usHorizontalSize; + uint16_t usVerticalSize; + uint16_t usFlatPanelType; + uint8_t ucRedBitsPerPrimary; + uint8_t ucGreenBitsPerPrimary; + uint8_t ucBlueBitsPerPrimary; + uint8_t ucReservedBitsPerPrimary; + uint8_t ucPanelCaps; + uint8_t ucPowerSequenceDelayStepsInMS; + uint8_t ucSupportedRefreshRateExtended; + uint16_t usExtendedPanelInfoTable; + uint16_t usPtrToHalfFrameBufferInformationTable; + uint16_t usVccOntoBlOn; + uint16_t usOffDelay; + uint16_t usRefDiv; + uint8_t ucPostDiv; + uint16_t usFeedBackDiv; + uint8_t ucSpreadSpectrumType; + uint16_t usSpreadSpectrumPercentage; + uint8_t ucBackLightLevel; + uint8_t ucBiasLevel; + uint8_t ucPowerSequenceDelay; + uint32_t ulPanelData; + uint8_t ucPanelRefreshRateData; + uint16_t usSupportedRefreshRate; + uint16_t usModeTableOffset[MAX_NO_OF_LCD_RES_TIMING]; +}; + +struct extended_panel_info_table +{ + uint8_t ucExtendedPanelInfoTableVer; + uint8_t ucSSDelay; + uint8_t ucSSStepSizeIndex; +}; + +struct lcd_mode_table_center +{ + uint16_t usHorizontalRes; + uint16_t usVerticalRes; + uint8_t ucModeType; + uint16_t usOffset2ExpParamTable; + uint16_t usOffset2TvParamTable; + uint16_t usPixelClock; + uint16_t usPixelClockAdjustment; + uint16_t usFpPos; + uint8_t ucReserved; + uint8_t ucMiscBits; + uint16_t usCrtcHTotal; + uint16_t usCrtcHDisp; + uint16_t usCrtcHSyncStrt; + uint8_t ucCrtcHSyncWid; + uint16_t usCrtcVTotal; + uint16_t usCrtcVDisp; + uint16_t usCrtcVSyncStrt; + uint8_t ucOvrWidTop; +}; + +struct lcd_mode_table_exp +{ + uint16_t usPixelClock; + uint16_t usPixelClockAdjustment; + uint16_t usFpPos; + uint8_t ucReserved; + uint8_t ucMiscBits; + uint16_t usCrtcHTotal; + uint16_t usCrtcHDisp; + uint16_t usCrtcHSyncStrt; + uint8_t ucCrtcHSyncWid; + uint16_t usCrtcVTotal; + uint16_t usCrtcVDisp; + uint16_t usCrtcVSyncStrt; + uint8_t ucOvrWidTop; + uint16_t usHorizontalBlendRatio; + uint32_t ulVgaVertStretching; + uint16_t usCopVertStretching; + uint16_t usVgaExtVertStretching; +}; + +struct tmds_pll_cntl_block +{ + uint16_t usClockUpperRange; + uint32_t ulPllSetting; +}; + +#define MAX_PLL_CNTL_ENTRIES 8 + +struct combios_dfp_info_table +{ + uint8_t ucDFPInfoTableRev; + uint8_t ucDFPInfoTableSize; + uint16_t usOffsetDetailedTimingTable; + uint8_t ucReserved; + uint8_t ucNumberOfClockRanges; + uint16_t usMaxPixelClock; + uint32_t ulInitValueTmdsPllCntl; + uint32_t ulFinalValueTmdsPllCntl; + struct tmds_pll_cntl_block sTmdsPllCntlBlock[MAX_PLL_CNTL_ENTRIES]; +}; + +struct combios_exttmds_table_header +{ + uint8_t ucTableRev; + uint16_t usTableSize; + uint8_t ucNoBlocks; +}; + +struct combios_exttmds_block_header +{ + uint16_t usMaxFreq; + uint8_t ucI2CSlaveAddr; + uint8_t ucI2CLine; + uint8_t ucConnectorId; + uint8_t ucFlags; +}; + +/* Connector table - applicable from Piglet and later ASICs + byte 0 (embedded revision) + [7:4] = number of chips (valid number 1 - 15) + [3:0] = revision number of table (valid number 1 - 15) + + byte 1 (Chip info) + [7:4] = chip number, max. 15 (valid number 1 - 15) + [3:0] = number of connectors for that chip, (valid number 1 - 15) + (number of connectors = number of 'Connector info' entries + for that chip) + + byte 2,3 (Connector info) + [15:12] - connector type + = 0 - no connector + = 1 - proprietary + = 2 - CRT + = 3 - DVI-I + = 4 - DVI-D + = 5-15 - reserved for future expansion + [11:8] - DDC line pair used for that connector + = 0 - no DDC + = 1 - MONID 0/1 + = 2 - DVI_DDC + = 3 - VGA_DDC + = 4 - CRT2_DDC + = 5-15 - reserved for future expansion + [5] - bit indicating presence of multiplexer for TV,CRT2 + [7:6] - reserved for future expansion + [4] - TMDS type + = 0 - internal TMDS + = 1 - external TMDS + [3:1] - reserved for future expansion + [0] - DAC associated with that connector + = 0 - CRT DAC + = 1 - non-CRT DAC (e.g. TV DAC, external DAC ..) + + byte 4,5,6... - byte 4,5 can be another "Connector info" word + describing another connector + - or byte 5 is a "Chip info" byte for anther chip, + then start with byte 5,6 to describe connectors + for that chip + - or byte 5 = 0 if all connectors for all chips on + board have been described, no more connector left + to describe. +*/ +#define BIOS_CONNECTOR_INFO__TYPE__MASK 0xF000 +#define BIOS_CONNECTOR_INFO__TYPE__SHIFT 0x0000000C +#define BIOS_CONNECTOR_TYPE__NONE 0x00000000 +#define BIOS_CONNECTOR_TYPE__PROPRIETARY 0x00000001 +#define BIOS_CONNECTOR_TYPE__CRT 0x00000002 +#define BIOS_CONNECTOR_TYPE__DVI_I 0x00000003 +#define BIOS_CONNECTOR_TYPE__DVI_D 0x00000004 + +#define BIOS_CONNECTOR_INFO__DDC_LINE__MASK 0x0F00 +#define BIOS_CONNECTOR_INFO__DDC_LINE__SHIFT 0x00000008 +#define BIOS_DDC_LINE__NONE 0x00000000 +#define BIOS_DDC_LINE__MONID01 0x00000001 +#define BIOS_DDC_LINE__DVI 0x00000002 +#define BIOS_DDC_LINE__VGA 0x00000003 +#define BIOS_DDC_LINE__CRT2 0x00000004 +#define BIOS_DDC_LINE__GPIOPAD 0x00000005 +#define BIOS_DDC_LINE__ZV_LCDPAD 0x00000006 + +#define BIOS_CONNECTOR_INFO__TMDS_TYPE__MASK 0x0010 +#define BIOS_CONNECTOR_INFO__TMDS_TYPE__SHIFT 0x00000004 +#define BIOS_TMDS_TYPE__INTERNAL 0x00000000 +#define BIOS_TMDS_TYPE__EXTERNAL 0x00000001 + +#define BIOS_CONNECTOR_INFO__DAC_TYPE__MASK 0x0001 +#define BIOS_CONNECTOR_INFO__DAC_TYPE__SHIFT 0x00000000 +#define BIOS_DAC_TYPE__CRT 0x00000000 +#define BIOS_DAC_TYPE__NON_CRT 0x00000001 + +#define BIOS_CONNECTOR_INFO__MUX_MASK 0x00000020 +#define BIOS_CONNECTOR_INFO__MUX_SHIFT 0x00000005 + +#define BIOS_CHIPINFO_HEADER__CHIP_NUMBER__MASK 0xF0 +#define BIOS_CHIPINFO_HEADER__CHIP_NUMBER__SHIFT 0x00000004 + +#define BIOS_CHIPINFO_HEADER__NUMBER_OF_CONNECTORS__MASK 0x0F +#define BIOS_CHIPINFO_HEADER__NUMBER_OF_CONNECTORS__SHIFT 0x00000000 + +#define BIOS_CHIPINFO__MAX_NUMBER_OF_CONNECTORS 0x00000010 + +struct combios_connector_chip_info +{ + uint8_t ucChipHeader; + uint16_t sConnectorInfo[BIOS_CHIPINFO__MAX_NUMBER_OF_CONNECTORS]; +}; + +#define BIOS_CONNECTOR_HEADER__NUMBER_OF_CHIPS__MASK 0xF0 +#define BIOS_CONNECTOR_HEADER__NUMBER_OF_CHIPS__SHIFT 0x00000004 + +#define BIOS_CONNECTOR_HEADER__TABLE_REVISION__MASK 0x0F +#define BIOS_CONNECTOR_HEADER__TABLE_REVISION__SHIFT 0x00000000 + +struct combios_connector_table +{ + uint8_t ucConnectorHeader; + struct combios_connector_chip_info sChipConnectorInfo[0x10]; +}; + +#pragma pack() + +int combios_parse(unsigned char *rom, struct combios_header *header); + +#endif diff --git a/shared-core/radeon_ms_cp.c b/shared-core/radeon_ms_cp.c new file mode 100644 index 00000000..3ddeea47 --- /dev/null +++ b/shared-core/radeon_ms_cp.c @@ -0,0 +1,371 @@ +/* + * 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]; + DRM_DEBUG("ring[%d]=0x%08X\n", 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; +} + +int radeon_ms_resetcp(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + int i; + + DRM_INFO("[radeon_ms]--------------------------------------------\n"); + + /* reset VAP */ + DRM_INFO("[radeon_ms] status before VAP : RBBM_STATUS: 0x%08X\n", + MMIO_R(RBBM_STATUS)); + MMIO_W(RBBM_SOFT_RESET, RBBM_SOFT_RESET__SOFT_RESET_VAP); + MMIO_R(RBBM_SOFT_RESET); + MMIO_W(RBBM_SOFT_RESET, 0); + MMIO_R(RBBM_SOFT_RESET); + for (i = 0; i < 100; i++) { + DRM_UDELAY(100); + } + DRM_INFO("[radeon_ms] status after VAP : RBBM_STATUS: 0x%08X\n", + MMIO_R(RBBM_STATUS)); + + DRM_INFO("[radeon_ms]--------------------------------------------\n"); + 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..d73275bb --- /dev/null +++ b/shared-core/radeon_ms_crtc.c @@ -0,0 +1,765 @@ +/* + * 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(struct drm_device *dev, + int clock, int rdiv, + int fdiv, int pdiv, + int rfrq, int pfrq) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + int dfrq; + + if (rdiv < 2 || fdiv < 4) { + return 0; + } + dfrq = rfrq / rdiv; + if (dfrq < 2000 || dfrq > 3300) { + return 0; + } + if (pfrq < dev_priv->properties.pll_min_pll_freq || + pfrq > dev_priv->properties.pll_max_pll_freq) { + 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(crtc->dev, 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); + if (crtc->fb == NULL) { + DRM_INFO("[radeon_ms] no FB bound\n"); + return; + } + + /* 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 24: + case 32: + format = 6; + break; + default: + DRM_ERROR("Unknown color depth %d\n", crtc->fb->bits_per_pixel); + return; + } + radeon_pll1_compute(crtc, adjusted_mode); + + state->crtc_offset = REG_S(CRTC_OFFSET, CRTC_OFFSET, crtc->fb->bo->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_crtc1_mode_set_base(struct drm_crtc *crtc, 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; + + DRM_INFO("mode_set_base 0x%lX\n", crtc->fb->bo->offset); + state->crtc_offset = REG_S(CRTC_OFFSET, CRTC_OFFSET, crtc->fb->bo->offset); + 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; + + if (regno >= 256) { + return; + } + DRM_INFO("[radeon_ms] gamma[%d]=(%d, %d, %d)\n", regno, r, g, b); + 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); + 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); + MMIO_W(PALETTE_30_DATA, color); +} + +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 const struct drm_crtc_funcs radeon_ms_crtc1_funcs= { + .dpms = radeon_ms_crtc1_dpms, + .save = NULL, /* XXX */ + .restore = NULL, /* XXX */ + .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, + .mode_set_base = radeon_ms_crtc1_mode_set_base, + .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..e6312942 --- /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..9238de24 --- /dev/null +++ b/shared-core/radeon_ms_drm.c @@ -0,0 +1,341 @@ +/* + * 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" +#include "amd_legacy_fence.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_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 = r3xx_fence_types, + .invalidate_caches = radeon_ms_invalidate_caches, + .init_mem_type = radeon_ms_init_mem_type, + .evict_flags = radeon_ms_evict_flags, + .move = radeon_ms_bo_move, + .ttm_cache_flush = radeon_ms_ttm_flush, +}; + +struct drm_ioctl_desc radeon_ms_ioctls[] = { + DRM_IOCTL_DEF(DRM_AMD_CMD, amd_ioctl_cmd, DRM_AUTH), + DRM_IOCTL_DEF(DRM_AMD_RESETCP, radeon_ms_resetcp, 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; + } + + dev_priv->fence = drm_alloc(sizeof(struct legacy_fence), DRM_MEM_DRIVER); + if (dev_priv->fence == NULL) { + radeon_ms_driver_unload(dev); + return -ENOMEM; + } + memset(dev_priv->fence, 0, sizeof(struct legacy_fence)); + + /* 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; + ret = drm_bo_driver_init(dev); + if (ret != 0) { + DRM_INFO("[radeon_ms] failed to init bo driver %d.\n", ret); + radeon_ms_driver_unload(dev); + return ret; + } + DRM_INFO("[radeon_ms] bo driver succesfull %d.\n", dev->bm.initialized); + /* initialize vram */ + ret = drm_bo_init_mm(dev, DRM_BO_MEM_VRAM, 0, dev_priv->vram.size, 1); + 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, 1); + 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_rom(dev); + if (ret < 0) { + radeon_ms_driver_unload(dev); + return ret; + } else if (!ret) { + ret = radeon_ms_outputs_from_properties(dev); + if (ret < 0) { + radeon_ms_driver_unload(dev); + return ret; + } else if (ret == 0) { + DRM_INFO("[radeon_ms] no outputs !\n"); + } + } else { + DRM_INFO("[radeon_ms] added %d outputs from rom.\n", ret); + } + ret = radeon_ms_connectors_from_rom(dev); + if (ret < 0) { + radeon_ms_driver_unload(dev); + return ret; + } else if (!ret) { + ret = radeon_ms_connectors_from_properties(dev); + if (ret < 0) { + radeon_ms_driver_unload(dev); + return ret; + } else if (!ret) { + DRM_INFO("[radeon_ms] no connectors !\n"); + } + } else { + DRM_INFO("[radeon_ms] added %d connectors from rom.\n", 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; + } + + /* initialze driver specific */ + ret = amd_legacy_cmd_module_initialize(dev); + if (ret != 0) { + radeon_ms_driver_unload(dev); + return ret; + } + + if (dev->primary && dev->control) { + DRM_INFO("[radeon_ms] control 0x%lx, render 0x%lx\n", + (long)dev->primary->device, (long)dev->control->device); + } else { + DRM_INFO("[radeon_ms] error control 0x%lx, render 0x%lx\n", + (long)dev->primary, (long)dev->control); + } + 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 specific driver */ + amd_legacy_cmd_module_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, 1)) { + 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, 1)) { + 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->fence, sizeof(struct legacy_fence), DRM_MEM_DRIVER); + 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..d7fe6fab --- /dev/null +++ b/shared-core/radeon_ms_drm.h @@ -0,0 +1,80 @@ +/* + * 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 __AMD_DRM_H__ +#define __AMD_DRM_H__ + +/* Fence + * + * Set DRM_AND_FENCE_FLAG_FLUSH if you want a flush with + * emission of the fence + * + * For fence type we have the native DRM EXE type and the amd R & W type. + */ +#define DRM_AMD_FENCE_CLASS_2D 0 +#define DRM_AMD_FENCE_TYPE_R (1 << 1) +#define DRM_AMD_FENCE_TYPE_W (1 << 2) +#define DRM_AMD_FENCE_FLAG_FLUSH 0x01000000 + +/* ioctl */ +#define DRM_AMD_CMD 0x00 +#define DRM_AMD_RESETCP 0x01 + +/* cmd ioctl */ + +#define DRM_AMD_CMD_BO_TYPE_INVALID 0 +#define DRM_AMD_CMD_BO_TYPE_CMD_RING (1 << 0) +#define DRM_AMD_CMD_BO_TYPE_CMD_INDIRECT (1 << 1) +#define DRM_AMD_CMD_BO_TYPE_DATA (1 << 2) + +struct drm_amd_cmd_bo_offset +{ + uint64_t next; + uint64_t offset; + uint32_t cs_id; +}; + +struct drm_amd_cmd_bo +{ + uint32_t type; + uint64_t next; + uint64_t offset; + struct drm_bo_op_req op_req; + struct drm_bo_arg_rep op_rep; +}; + +struct drm_amd_cmd +{ + uint32_t cdw_count; + uint32_t bo_count; + uint64_t bo; + 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..bda9a846 --- /dev/null +++ b/shared-core/radeon_ms_exec.c @@ -0,0 +1,404 @@ +/* + * 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" +#include "amd.h" + +static inline void amd_cmd_bo_cleanup(struct drm_device *dev, + struct amd_cmd *cmd) +{ + struct amd_cmd_bo *bo; + + mutex_lock(&dev->struct_mutex); + list_for_each_entry(bo, &cmd->bo_unused.list, list) { + drm_bo_usage_deref_locked(&bo->bo); + } + list_for_each_entry(bo, &cmd->bo_used.list, list) { + drm_bo_usage_deref_locked(&bo->bo); + } + mutex_unlock(&dev->struct_mutex); +} + +static inline int amd_cmd_bo_validate(struct drm_device *dev, + struct drm_file *file, + struct amd_cmd_bo *cmd_bo, + struct drm_amd_cmd_bo *bo, + uint64_t data) +{ + int ret; + + /* validate only cmd indirect or data bo */ + switch (bo->type) { + case DRM_AMD_CMD_BO_TYPE_CMD_INDIRECT: + case DRM_AMD_CMD_BO_TYPE_DATA: + case DRM_AMD_CMD_BO_TYPE_CMD_RING: + /* FIXME: make sure userspace can no longer map the bo */ + break; + default: + return 0; + } + /* check that buffer operation is validate */ + if (bo->op_req.op != drm_bo_validate) { + DRM_ERROR("buffer 0x%x object operation is not validate.\n", + cmd_bo->handle); + return -EINVAL; + } + /* validate buffer */ + memset(&bo->op_rep, 0, sizeof(struct drm_bo_arg_rep)); + ret = drm_bo_handle_validate(file, + bo->op_req.bo_req.handle, + bo->op_req.bo_req.flags, + bo->op_req.bo_req.mask, + bo->op_req.bo_req.hint, + bo->op_req.bo_req.fence_class, + &bo->op_rep.bo_info, + &cmd_bo->bo); + if (ret) { + DRM_ERROR("validate error %d for 0x%08x\n", + ret, cmd_bo->handle); + return ret; + } + if (copy_to_user((void __user *)((unsigned)data), bo, + sizeof(struct drm_amd_cmd_bo))) { + DRM_ERROR("failed to copy to user validate result of 0x%08x\n", + cmd_bo->handle); + return -EFAULT; + } + return 0; +} + +static int amd_cmd_parse_cmd_bo(struct drm_device *dev, + struct drm_file *file, + struct drm_amd_cmd *drm_amd_cmd, + struct amd_cmd *cmd) +{ + struct drm_amd_cmd_bo drm_amd_cmd_bo; + struct amd_cmd_bo *cmd_bo; + uint32_t bo_count = 0; + uint64_t data = drm_amd_cmd->bo; + int ret = 0; + + do { + /* check we don't have more buffer than announced */ + if (bo_count >= drm_amd_cmd->bo_count) { + DRM_ERROR("cmd bo count exceeded got %d waited %d\n.", + bo_count, drm_amd_cmd->bo_count); + return -EINVAL; + } + /* initialize amd_cmd_bo */ + cmd_bo = &cmd->bo[bo_count]; + INIT_LIST_HEAD(&cmd_bo->list); + cmd_bo->bo = NULL; + /* copy from userspace */ + if (copy_from_user(&drm_amd_cmd_bo, + (void __user *)((unsigned)data), + sizeof(struct drm_amd_cmd_bo))) { + return -EFAULT; + } + /* collect informations */ + cmd_bo->type = drm_amd_cmd_bo.type; + cmd_bo->mask = drm_amd_cmd_bo.op_req.bo_req.mask; + cmd_bo->flags = drm_amd_cmd_bo.op_req.bo_req.flags; + cmd_bo->handle = drm_amd_cmd_bo.op_req.arg_handle; + /* get bo objects */ + mutex_lock(&dev->struct_mutex); + cmd_bo->bo = drm_lookup_buffer_object(file, cmd_bo->handle, 1); + mutex_unlock(&dev->struct_mutex); + if (cmd_bo->bo == NULL) { + DRM_ERROR("unknown bo handle 0x%x\n", cmd_bo->handle); + return -EINVAL; + } + /* validate buffer if necessary */ + ret = amd_cmd_bo_validate(dev, file, cmd_bo, + &drm_amd_cmd_bo, data); + if (ret) { + mutex_lock(&dev->struct_mutex); + drm_bo_usage_deref_locked(&cmd_bo->bo); + mutex_unlock(&dev->struct_mutex); + return ret; + } + /* inspect bo type */ + switch (cmd_bo->type) { + case DRM_AMD_CMD_BO_TYPE_CMD_INDIRECT: + /* add it so we properly unreference in case of error */ + list_add_tail(&cmd_bo->list, &cmd->bo_used.list); + return -EINVAL; + case DRM_AMD_CMD_BO_TYPE_DATA: + /* add to unused list */ + list_add_tail(&cmd_bo->list, &cmd->bo_unused.list); + break; + case DRM_AMD_CMD_BO_TYPE_CMD_RING: + /* set cdw_bo */ + list_add_tail(&cmd_bo->list, &cmd->bo_used.list); + cmd->cdw_bo = cmd_bo; + break; + default: + mutex_lock(&dev->struct_mutex); + drm_bo_usage_deref_locked(&cmd_bo->bo); + mutex_unlock(&dev->struct_mutex); + DRM_ERROR("unknow bo 0x%x unknown type 0x%x in cmd\n", + cmd_bo->handle, cmd_bo->type); + return -EINVAL; + } + /* ok next bo */ + data = drm_amd_cmd_bo.next; + bo_count++; + } while (data != 0); + if (bo_count != drm_amd_cmd->bo_count) { + DRM_ERROR("not enought buffer got %d expected %d\n.", + bo_count, drm_amd_cmd->bo_count); + return -EINVAL; + } + return 0; +} + +static int amd_cmd_packet0_check(struct drm_device *dev, + struct amd_cmd *cmd, + int *cdw_id) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t reg, count, r, i; + int ret; + + reg = cmd->cdw[*cdw_id] & PACKET0_REG_MASK; + count = (cmd->cdw[*cdw_id] & PACKET0_COUNT_MASK) >> PACKET0_COUNT_SHIFT; + if (reg + count > dev_priv->cmd_module.numof_p0_checkers) { + DRM_ERROR("0x%08X registers is above last accepted registers\n", + reg << 2); + return -EINVAL; + } + for (r = reg, i = 0; i <= count; i++, r++) { + if (dev_priv->cmd_module.check_p0[r] == NULL) { + continue; + } + if (dev_priv->cmd_module.check_p0[r] == (void *)-1) { + DRM_ERROR("register 0x%08X (at %d) is forbidden\n", + r << 2, (*cdw_id) + i + 1); + return -EINVAL; + } + ret = dev_priv->cmd_module.check_p0[r](dev, cmd, + (*cdw_id) + i + 1, r); + if (ret) { + return ret; + } + } + /* header + N + 1 dword passed test */ + (*cdw_id) += count + 2; + return 0; +} + +static int amd_cmd_packet3_check(struct drm_device *dev, + struct amd_cmd *cmd, + int *cdw_id) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + uint32_t opcode, count; + int ret; + + opcode = (cmd->cdw[*cdw_id] & PACKET3_OPCODE_MASK) >> + PACKET3_OPCODE_SHIFT; + if (opcode > dev_priv->cmd_module.numof_p3_checkers) { + DRM_ERROR("0x%08X opcode is above last accepted opcodes\n", + opcode); + return -EINVAL; + } + count = (cmd->cdw[*cdw_id] & PACKET3_COUNT_MASK) >> PACKET3_COUNT_SHIFT; + if (dev_priv->cmd_module.check_p3[opcode] == NULL) { + DRM_ERROR("0x%08X opcode is forbidden\n", opcode); + return -EINVAL; + } + ret = dev_priv->cmd_module.check_p3[opcode](dev, cmd, + (*cdw_id) + 1, opcode, + count); + if (ret) { + return ret; + } + /* header + N + 1 dword passed test */ + (*cdw_id) += count + 2; + return 0; +} + +int amd_cmd_check(struct drm_device *dev, struct amd_cmd *cmd) +{ + uint32_t i; + int ret; + + for (i = 0; i < cmd->cdw_count;) { + switch (PACKET_HEADER_GET(cmd->cdw[i])) { + case 0: + ret = amd_cmd_packet0_check(dev, cmd, &i); + if (ret) { + return ret; + } + break; + case 1: + /* we don't accept packet 1 */ + return -EINVAL; + case 2: + /* FIXME: accept packet 2 */ + return -EINVAL; + case 3: + ret = amd_cmd_packet3_check(dev, cmd, &i); + if (ret) { + return ret; + } + break; + } + } + return 0; +} + +static int amd_ioctl_cmd_cleanup(struct drm_device *dev, + struct drm_file *file, + struct amd_cmd *cmd, + int r) +{ + /* check if we need to unfence object */ + if (r && (!list_empty(&cmd->bo_unused.list) || + !list_empty(&cmd->bo_unused.list))) { + drm_putback_buffer_objects(dev); + } + if (cmd->cdw) { + drm_bo_kunmap(&cmd->cdw_kmap); + cmd->cdw = NULL; + } + /* derefence buffer as lookup reference them */ + amd_cmd_bo_cleanup(dev, cmd); + if (cmd->bo) { + drm_free(cmd->bo, + cmd->bo_count * sizeof(struct amd_cmd_bo), + DRM_MEM_DRIVER); + cmd->bo = NULL; + } + drm_bo_read_unlock(&dev->bm.bm_lock); + return r; +} + +int amd_ioctl_cmd(struct drm_device *dev, void *data, struct drm_file *file) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct drm_amd_cmd *drm_amd_cmd = data; + struct drm_fence_arg *fence_arg = &drm_amd_cmd->fence_arg; + struct drm_fence_object *fence; + struct amd_cmd cmd; + int tmp; + int ret; + + /* check that we have a command checker */ + if (dev_priv->cmd_module.check == NULL) { + DRM_ERROR("invalid command checker module.\n"); + return -EFAULT; + } + /* command dword count must be >= 0 */ + if (drm_amd_cmd->cdw_count == 0) { + DRM_ERROR("command dword count is 0.\n"); + return -EINVAL; + } + + /* FIXME: Lock buffer manager. This is needed so the X server can + * block DRI clients while VT switched. The X server will then + * take the lock in write mode + */ + + ret = drm_bo_read_lock(&dev->bm.bm_lock, 1); + if (ret) { + + /* FIXME: ret can be -EAGAIN here, + * which really isn't an error. + */ + + DRM_ERROR("bo read locking failed.\n"); + return ret; + } + /* cleanup & initialize amd cmd structure */ + memset(&cmd, 0, sizeof(struct amd_cmd)); + cmd.bo_count = drm_amd_cmd->bo_count; + INIT_LIST_HEAD(&cmd.bo_unused.list); + INIT_LIST_HEAD(&cmd.bo_used.list); + /* allocate structure for bo parsing */ + cmd.bo = drm_calloc(cmd.bo_count, sizeof(struct amd_cmd_bo), + DRM_MEM_DRIVER); + if (cmd.bo == NULL) { + return amd_ioctl_cmd_cleanup(dev, file, &cmd, -ENOMEM); + } + /* parse cmd bo */ + ret = amd_cmd_parse_cmd_bo(dev, file, drm_amd_cmd, &cmd); + if (ret) { + return amd_ioctl_cmd_cleanup(dev, file, &cmd, ret); + } + /* check that a command buffer have been found */ + if (cmd.cdw_bo == NULL) { + DRM_ERROR("no command buffer submited in cmd ioctl\n"); + return amd_ioctl_cmd_cleanup(dev, file, &cmd, -EINVAL); + } + /* map command buffer */ + cmd.cdw_count = drm_amd_cmd->cdw_count; + cmd.cdw_size = (cmd.cdw_bo->bo->mem.num_pages * PAGE_SIZE) >> 2; + if (cmd.cdw_size < cmd.cdw_count) { + DRM_ERROR("command buffer (%d) is smaller than expected (%d)\n", + cmd.cdw_size, cmd.cdw_count); + return amd_ioctl_cmd_cleanup(dev, file, &cmd, -EINVAL); + } + memset(&cmd.cdw_kmap, 0, sizeof(struct drm_bo_kmap_obj)); + ret = drm_bo_kmap(cmd.cdw_bo->bo, 0, + cmd.cdw_bo->bo->mem.num_pages, &cmd.cdw_kmap); + if (ret) { + DRM_ERROR("error mapping command buffer\n"); + return amd_ioctl_cmd_cleanup(dev, file, &cmd, ret); + } + cmd.cdw = drm_bmo_virtual(&cmd.cdw_kmap, &tmp); + /* do command checking */ + ret = dev_priv->cmd_module.check(dev, &cmd); + if (ret) { + return amd_ioctl_cmd_cleanup(dev, file, &cmd, ret); + } + /* copy command to ring */ + ret = radeon_ms_ring_emit(dev, cmd.cdw, cmd.cdw_count); + if (ret) { + return amd_ioctl_cmd_cleanup(dev, file, &cmd, ret); + } + /* fence */ + ret = drm_fence_buffer_objects(dev, NULL, 0, NULL, &fence); + if (ret) { + return amd_ioctl_cmd_cleanup(dev, file, &cmd, ret); + } + if (!(fence_arg->flags & DRM_FENCE_FLAG_NO_USER)) { + ret = drm_fence_add_user_object(file, 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_types; + fence_arg->sequence = fence->sequence; + } else { + DRM_ERROR("error add object fence, expect oddity !\n"); + } + } + drm_fence_usage_deref_unlocked(&fence); + return amd_ioctl_cmd_cleanup(dev, file, &cmd, 0); +} diff --git a/shared-core/radeon_ms_family.c b/shared-core/radeon_ms_family.c new file mode 100644 index 00000000..5e83766a --- /dev/null +++ b/shared-core/radeon_ms_family.c @@ -0,0 +1,142 @@ +/* + * 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" + +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 ret; + + 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_finish = radeon_ms_agp_finish; + 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; + } + ret = radeon_ms_rom_init(dev); + if (ret) { + return ret; + } + ret = radeon_ms_properties_init(dev); + return ret; +} diff --git a/shared-core/radeon_ms_fence.c b/shared-core/radeon_ms_fence.c new file mode 100644 index 00000000..08e53bd2 --- /dev/null +++ b/shared-core/radeon_ms_fence.c @@ -0,0 +1,275 @@ +/* + * 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" +#include "amd_legacy_fence.h" + +#define R3XX_FENCE_SEQUENCE_RW_FLUSH 0x80000000u + +static inline int r3xx_fence_emit_sequence(struct drm_device *dev, + struct drm_radeon_private *dev_priv, + uint32_t sequence) +{ + struct legacy_fence *r3xx_fence = dev_priv->fence; + uint32_t cmd[2]; + int i, r; + + if (sequence & R3XX_FENCE_SEQUENCE_RW_FLUSH) { + r3xx_fence->sequence_last_flush = + sequence & ~R3XX_FENCE_SEQUENCE_RW_FLUSH; + /* Ask flush for VERTEX & FRAGPROG pipeline + * have 3D idle */ + /* FIXME: proper flush */ +#if 0 + dev_priv->flush_cache(dev); +#endif + } + cmd[0] = CP_PACKET0(dev_priv->fence_reg, 0); + cmd[1] = sequence; + for (i = 0; i < dev_priv->usec_timeout; i++) { + r = radeon_ms_ring_emit(dev, cmd, 2); + if (!r) { + dev_priv->irq_emit(dev); + return 0; + } + } + return -EBUSY; +} + +static inline uint32_t r3xx_fence_sequence(struct legacy_fence *r3xx_fence) +{ + r3xx_fence->sequence += 1; + if (unlikely(r3xx_fence->sequence > 0x7fffffffu)) { + r3xx_fence->sequence = 1; + } + return r3xx_fence->sequence; +} + +static inline void r3xx_fence_report(struct drm_device *dev, + struct drm_radeon_private *dev_priv, + struct legacy_fence *r3xx_fence) +{ + uint32_t fence_types = DRM_FENCE_TYPE_EXE; + uint32_t sequence; + + if (dev_priv == NULL) { + return; + } + sequence = mmio_read(dev_priv, dev_priv->fence_reg); +DRM_INFO("%s pass fence 0x%08x\n", __func__, sequence); + if (sequence & R3XX_FENCE_SEQUENCE_RW_FLUSH) { + sequence &= ~R3XX_FENCE_SEQUENCE_RW_FLUSH; + fence_types |= DRM_AMD_FENCE_TYPE_R; + fence_types |= DRM_AMD_FENCE_TYPE_W; + if (sequence == r3xx_fence->sequence_last_flush) { + r3xx_fence->sequence_last_flush = 0; + } + } + drm_fence_handler(dev, 0, sequence, fence_types, 0); + r3xx_fence->sequence_last_reported = sequence; +} + +static void r3xx_fence_flush(struct drm_device *dev, uint32_t class) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct legacy_fence *r3xx_fence = dev_priv->fence; + uint32_t sequence; + + sequence = r3xx_fence_sequence(r3xx_fence); + sequence |= R3XX_FENCE_SEQUENCE_RW_FLUSH; + r3xx_fence_emit_sequence(dev, dev_priv, sequence); +} + +static void r3xx_fence_poll(struct drm_device *dev, uint32_t fence_class, + uint32_t waiting_types) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct drm_fence_manager *fm = &dev->fm; + struct drm_fence_class_manager *fc = &fm->fence_class[fence_class]; + struct legacy_fence *r3xx_fence = dev_priv->fence; + + if (unlikely(!dev_priv)) { + return; + } + /* if there is a RW flush pending then submit new sequence + * preceded by flush cmds */ + if (fc->pending_flush & (DRM_AMD_FENCE_TYPE_R | DRM_AMD_FENCE_TYPE_W)) { + r3xx_fence_flush(dev, 0); + fc->pending_flush &= ~DRM_AMD_FENCE_TYPE_R; + fc->pending_flush &= ~DRM_AMD_FENCE_TYPE_W; + } + r3xx_fence_report(dev, dev_priv, r3xx_fence); + return; +} + +static int r3xx_fence_emit(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; + struct legacy_fence *r3xx_fence = dev_priv->fence; + uint32_t tmp; + + if (!dev_priv || dev_priv->cp_ready != 1) { + return -EINVAL; + } + *sequence = tmp = r3xx_fence_sequence(r3xx_fence); + *native_type = DRM_FENCE_TYPE_EXE; + if (flags & DRM_AMD_FENCE_FLAG_FLUSH) { + *native_type |= DRM_AMD_FENCE_TYPE_R; + *native_type |= DRM_AMD_FENCE_TYPE_W; + tmp |= R3XX_FENCE_SEQUENCE_RW_FLUSH; + } +DRM_INFO("%s emit fence 0x%08x\n", __func__, tmp); + return r3xx_fence_emit_sequence(dev, dev_priv, tmp); +} + +static int r3xx_fence_has_irq(struct drm_device *dev, + uint32_t class, uint32_t type) +{ + const uint32_t type_irq_mask = DRM_FENCE_TYPE_EXE | + DRM_AMD_FENCE_TYPE_R | + DRM_AMD_FENCE_TYPE_W; + /* + * We have an irq for EXE & RW fence. + */ + if (class == 0 && (type & type_irq_mask)) { + return 1; + } + return 0; +} + +static uint32_t r3xx_fence_needed_flush(struct drm_fence_object *fence) +{ + struct drm_device *dev = fence->dev; + struct drm_radeon_private *dev_priv = dev->dev_private; + struct legacy_fence *r3xx_fence = dev_priv->fence; + struct drm_fence_driver *driver = dev->driver->fence_driver; + uint32_t flush_types, diff; + + flush_types = fence->waiting_types & + ~(DRM_FENCE_TYPE_EXE | fence->signaled_types); + + if (flush_types == 0 || ((flush_types & ~fence->native_types) == 0)) { + return 0; + } + if (unlikely(dev_priv == NULL)) { + return 0; + } + if (r3xx_fence->sequence_last_flush) { + diff = (r3xx_fence->sequence_last_flush - fence->sequence) & + driver->sequence_mask; + if (diff < driver->wrap_diff) { + return 0; + } + } + return flush_types; +} + +static int r3xx_fence_wait(struct drm_fence_object *fence, + int lazy, int interruptible, uint32_t mask) +{ + struct drm_device *dev = fence->dev; + struct drm_fence_manager *fm = &dev->fm; + struct drm_fence_class_manager *fc = &fm->fence_class[0]; + int r; + + drm_fence_object_flush(fence, mask); + if (likely(interruptible)) { + r = wait_event_interruptible_timeout( + fc->fence_queue, + drm_fence_object_signaled(fence, DRM_FENCE_TYPE_EXE), + 3 * DRM_HZ); + } else { + r = wait_event_timeout( + fc->fence_queue, + drm_fence_object_signaled(fence, DRM_FENCE_TYPE_EXE), + 3 * DRM_HZ); + } + if (unlikely(r == -ERESTARTSYS)) { + return -EAGAIN; + } + if (unlikely(r == 0)) { + return -EBUSY; + } + + if (likely(mask == DRM_FENCE_TYPE_EXE || + drm_fence_object_signaled(fence, mask))) { + return 0; + } + + /* + * Poll for sync flush completion. + */ + return drm_fence_wait_polling(fence, lazy, interruptible, + mask, 3 * DRM_HZ); +} + +struct drm_fence_driver r3xx_fence_driver = { + .num_classes = 1, + .wrap_diff = (1 << 29), + .flush_diff = (1 << 28), + .sequence_mask = 0x7fffffffU, + .has_irq = r3xx_fence_has_irq, + .emit = r3xx_fence_emit, + .flush = r3xx_fence_flush, + .poll = r3xx_fence_poll, + .needed_flush = r3xx_fence_needed_flush, + .wait = r3xx_fence_wait, +}; + +/* this are used by the buffer object code */ +int r3xx_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 = DRM_FENCE_TYPE_EXE | + DRM_AMD_FENCE_TYPE_R | + DRM_AMD_FENCE_TYPE_W; + } else { + *type = DRM_FENCE_TYPE_EXE; + } + return 0; +} + +/* this are used by the irq code */ +void r3xx_fence_handler(struct drm_device * dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct drm_fence_manager *fm = &dev->fm; + struct drm_fence_class_manager *fc = &fm->fence_class[0]; + + if (unlikely(dev_priv == NULL)) { + return; + } + + write_lock(&fm->lock); + r3xx_fence_poll(dev, 0, fc->waiting_types); + write_unlock(&fm->lock); +} diff --git a/shared-core/radeon_ms_gpu.c b/shared-core/radeon_ms_gpu.c new file mode 100644 index 00000000..5b03dc35 --- /dev/null +++ b/shared-core/radeon_ms_gpu.c @@ -0,0 +1,599 @@ +/* + * 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); + +#if 1 + cache_mode = MMIO_R(RB2D_DSTCACHE_MODE); + MMIO_W(RB2D_DSTCACHE_MODE, + cache_mode | RB2D_DSTCACHE_MODE__DC_DISABLE_IGNORE_PE); +#else + reset_mask = RBBM_SOFT_RESET__SOFT_RESET_CP | + RBBM_SOFT_RESET__SOFT_RESET_HI | + RBBM_SOFT_RESET__SOFT_RESET_E2; + MMIO_W(RBBM_SOFT_RESET, rbbm_soft_reset | reset_mask); + MMIO_R(RBBM_SOFT_RESET); + MMIO_W(RBBM_SOFT_RESET, 0); + cache_mode = MMIO_R(RB3D_DSTCACHE_CTLSTAT); + MMIO_W(RB3D_DSTCACHE_CTLSTAT, cache_mode | (0xf)); +#endif + + 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_INFO("[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_INFO("[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; + + /* 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_INFO("Unknown radeon family, aborting\n"); + return; + } + DRM_INFO("[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_INFO("[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_INFO("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_INFO("[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_INFO("[radeon_ms] no fifo for setting up dst & src gui\n"); + DRM_INFO("[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_INFO("[radeon_ms] no fifo for setting up dp data type\n"); + DRM_INFO("[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_INFO("[radeon_ms] no fifo for setting up surface cntl\n"); + DRM_INFO("[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_INFO("[radeon_ms] no fifo for setting scissor\n"); + DRM_INFO("[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_INFO("[radeon_ms] no fifo for setting up gui cntl\n"); + DRM_INFO("[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_INFO("[radeon_ms] no fifo for setting up clear color\n"); + DRM_INFO("[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_INFO("[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_INFO("[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_INFO("[radeon_ms] idle timed out: status=0x%08x\n", + MMIO_R(RBBM_STATUS)); + radeon_ms_gpu_reset(dev); + radeon_ms_gpu_resume(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..f4468c1e --- /dev/null +++ b/shared-core/radeon_ms_i2c.c @@ -0,0 +1,336 @@ +/* + * 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; + case GPIO_MONID: + v = MMIO_R(GPIO_MONID); + if ((GPIO_MONID__GPIO_MONID_1_INPUT & v)) { + v = 1; + } else { + v = 0; + } + break; + case GPIO_CRT2_DDC: + v = MMIO_R(GPIO_CRT2_DDC); + if ((GPIO_CRT2_DDC__CRT2_DDC_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; + case GPIO_MONID: + v = MMIO_R(GPIO_MONID); + if ((GPIO_MONID__GPIO_MONID_0_INPUT & v)) { + v = 1; + } else { + v = 0; + } + break; + case GPIO_CRT2_DDC: + v = MMIO_R(GPIO_CRT2_DDC); + if ((GPIO_CRT2_DDC__CRT2_DDC_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; + case GPIO_MONID: + v &= ~GPIO_MONID__GPIO_MONID_1_OUT_EN; + if (!clock) { + v |= GPIO_MONID__GPIO_MONID_1_OUT_EN; + } + break; + case GPIO_CRT2_DDC: + v &= ~GPIO_CRT2_DDC__CRT2_DDC_CLK_OUT_EN; + if (!clock) { + v |= GPIO_CRT2_DDC__CRT2_DDC_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; + case GPIO_MONID: + v &= ~GPIO_MONID__GPIO_MONID_0_OUT_EN; + if (!data) { + v |= GPIO_MONID__GPIO_MONID_0_OUT_EN; + } + break; + case GPIO_CRT2_DDC: + v &= ~GPIO_CRT2_DDC__CRT2_DDC_DATA_OUT_EN; + if (!data) { + v |= GPIO_CRT2_DDC__CRT2_DDC_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 (0x%X)\n", + i2c->adapter.name, reg); + goto out_free; + } + DRM_INFO("[radeon_ms] registered I2C '%s' bus (0x%X)\n", + i2c->adapter.name, reg); + 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..5c68c902 --- /dev/null +++ b/shared-core/radeon_ms_irq.c @@ -0,0 +1,161 @@ +/* + * 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) { + r3xx_fence_handler(dev); + } + r3xx_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); +} + +int radeon_ms_irq_postinstall(struct drm_device * dev) +{ + radeon_ms_irq_enable(dev); + return 0; +} + +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..bc174371 --- /dev/null +++ b/shared-core/radeon_ms_output.c @@ -0,0 +1,361 @@ +/* + * 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; +} + +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, c = 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->type); + if (output == NULL) { + radeon_ms_connectors_destroy(dev); + return -EINVAL; + } + connector->output = output; + output->driver_private = connector; + output->possible_crtcs = 0x3; + dev_priv->connectors[c++] = connector; + } + } + return c; +} + +int radeon_ms_connectors_from_rom(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + switch (dev_priv->rom.type) { + case ROM_COMBIOS: + return radeon_ms_connectors_from_combios(dev); + } + 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; + int c = 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]); + c++; + } + } + return c; +} + +int radeon_ms_outputs_from_rom(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + switch (dev_priv->rom.type) { + case ROM_COMBIOS: + return radeon_ms_outputs_from_combios(dev); + } + 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_properties.c b/shared-core/radeon_ms_properties.c new file mode 100644 index 00000000..baf2a7f2 --- /dev/null +++ b/shared-core/radeon_ms_properties.c @@ -0,0 +1,123 @@ +/* + * 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" + +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 +}; + +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 +}; + +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" +}; + +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" +}; + +struct radeon_ms_properties properties[] = { + /* default only one VGA connector */ + { + 0, 0, 27000, 12, 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 + } + } +}; + +int radeon_ms_properties_init(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + int i, ret; + + 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); + memcpy(&dev_priv->properties, &properties[i], + sizeof(struct radeon_ms_properties)); + } + } + if (dev_priv->properties.subvendor == 0) { + ret = radeon_ms_rom_get_properties(dev); + if (ret < 0) { + return ret; + } + if (!ret) { + memcpy(&dev_priv->properties, &properties[0], + sizeof(struct radeon_ms_properties)); + } else { + dev_priv->properties.pll_dummy_reads = 1; + dev_priv->properties.pll_delay = 1; + dev_priv->properties.pll_r300_errata = 1; + } + dev_priv->properties.subvendor = dev->pdev->subsystem_vendor; + dev_priv->properties.subdevice = dev->pdev->subsystem_device; + } + return 0; +} diff --git a/shared-core/radeon_ms_properties.h b/shared-core/radeon_ms_properties.h new file mode 100644 index 00000000..a02a84d5 --- /dev/null +++ b/shared-core/radeon_ms_properties.h @@ -0,0 +1,50 @@ +/* + * 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> + */ +#ifndef __RADEON_MS_PROPERTIES_H__ +#define __RADEON_MS_PROPERTIES_H__ + +#define RADEON_PAGE_SIZE 4096 +#define RADEON_MAX_CONNECTORS 8 +#define RADEON_MAX_OUTPUTS 8 + +struct radeon_ms_properties { + uint16_t subvendor; + uint16_t subdevice; + int16_t pll_reference_freq; + int16_t pll_reference_div; + 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]; +}; + +#endif diff --git a/shared-core/radeon_ms_reg.h b/shared-core/radeon_ms_reg.h new file mode 100644 index 00000000..25063990 --- /dev/null +++ b/shared-core/radeon_ms_reg.h @@ -0,0 +1,1787 @@ +/* + * 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 SC_SCISSOR0 0x000043E0 +#define SC_SCISSOR0__XS0__MASK 0x00001FFF +#define SC_SCISSOR0__XS0__SHIFT 0 +#define SC_SCISSOR0__YS0__MASK 0x03FFE000 +#define SC_SCISSOR0__YS0__SHIFT 13 +#define SC_SCISSOR1 0x000043E4 +#define SC_SCISSOR1__XS1__MASK 0x00001FFF +#define SC_SCISSOR1__XS1__SHIFT 0 +#define SC_SCISSOR1__YS1__MASK 0x03FFE000 +#define SC_SCISSOR1__YS1__SHIFT 13 +#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_CSQ_MODE 0x00000744 +#define CP_CSQ_MODE__INDIRECT2_START__MASK 0x0000007F +#define CP_CSQ_MODE__INDIRECT2_START__SHIFT 0 +#define CP_CSQ_MODE__INDIRECT1_START__MASK 0x00007F00 +#define CP_CSQ_MODE__INDIRECT1_START__SHIFT 8 +#define CP_CSQ_MODE__CSQ_INDIRECT2_MODE 0x04000000 +#define CP_CSQ_MODE__CSQ_INDIRECT2_ENABLE 0x08000000 +#define CP_CSQ_MODE__CSQ_INDIRECT1_MODE 0x10000000 +#define CP_CSQ_MODE__CSQ_INDIRECT1_ENABLE 0x20000000 +#define CP_CSQ_MODE__CSQ_PRIMARY_MODE 0x40000000 +#define CP_CSQ_MODE__CSQ_PRIMARY_ENABLE 0x80000000 +#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 GPIO_DVI_DDC 0x00000064 +#define GPIO_DVI_DDC__DVI_DDC_DATA_OUTPUT 0x00000001 +#define GPIO_DVI_DDC__DVI_DCC_DATA_OUTPUT 0x00000001 +#define GPIO_DVI_DDC__DVI_DDC_CLK_OUTPUT 0x00000002 +#define GPIO_DVI_DDC__DVI_DDC_DATA_INPUT 0x00000100 +#define GPIO_DVI_DDC__DVI_DDC_CLK_INPUT 0x00000200 +#define GPIO_DVI_DDC__DVI_DDC_DATA_OUT_EN 0x00010000 +#define GPIO_DVI_DDC__DVI_DDC_CLK_OUT_EN 0x00020000 +#define GPIO_DVI_DDC__SW_WANTS_TO_USE_DVI_I2C 0x00100000 +#define GPIO_DVI_DDC__SW_CAN_USE_DVI_I2C 0x00100000 +#define GPIO_DVI_DDC__SW_DONE_USING_DVI_I2C 0x00200000 +#define GPIO_DVI_DDC__HW_USING_DVI_I2C 0x00400000 +#define GPIO_MONID 0x00000068 +#define GPIO_MONID__GPIO_MONID_0_OUTPUT 0x00000001 +#define GPIO_MONID__GPIO_MONID_1_OUTPUT 0x00000002 +#define GPIO_MONID__GPIO_MONID_0_INPUT 0x00000100 +#define GPIO_MONID__GPIO_MONID_1_INPUT 0x00000200 +#define GPIO_MONID__GPIO_MONID_0_OUT_EN 0x00010000 +#define GPIO_MONID__GPIO_MONID_1_OUT_EN 0x00020000 +#define GPIO_CRT2_DDC 0x0000006C +#define GPIO_CRT2_DDC__CRT2_DDC_DATA_OUTPUT 0x00000001 +#define GPIO_CRT2_DDC__CRT2_DDC_CLK_OUTPUT 0x00000002 +#define GPIO_CRT2_DDC__CRT2_DDC_DATA_INPUT 0x00000100 +#define GPIO_CRT2_DDC__CRT2_DDC_CLK_INPUT 0x00000200 +#define GPIO_CRT2_DDC__CRT2_DDC_DATA_OUT_EN 0x00010000 +#define GPIO_CRT2_DDC__CRT2_DDC_CLK_OUT_EN 0x00020000 +#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 GA_SOFT_RESET 0x0000429C +#define GA_SOFT_RESET__SOFT_RESET_COUNT__MASK 0x0000FFFF +#define GA_SOFT_RESET__SOFT_RESET_COUNT__SHIFT 0 +#define RBBM_CNTL 0x000000EC +#define RBBM_CNTL__RB_SETTLE__MASK 0x0000000F +#define RBBM_CNTL__RB_SETTLE__SHIFT 0 +#define RBBM_CNTL__ABORTCLKS_HI__MASK 0x00000070 +#define RBBM_CNTL__ABORTCLKS_HI__SHIFT 4 +#define RBBM_CNTL__ABORTCLKS_CP__MASK 0x00000700 +#define RBBM_CNTL__ABORTCLKS_CP__SHIFT 8 +#define RBBM_CNTL__ABORTCLKS_CFIFO__MASK 0x00007000 +#define RBBM_CNTL__ABORTCLKS_CFIFO__SHIFT 12 +#define RBBM_CNTL__CPQ_DATA_SWAP 0x00020000 +#define RBBM_CNTL__NO_ABORT_IDCT 0x00200000 +#define RBBM_CNTL__NO_ABORT_BIOS 0x00400000 +#define RBBM_CNTL__NO_ABORT_FB 0x00800000 +#define RBBM_CNTL__NO_ABORT_CP 0x01000000 +#define RBBM_CNTL__NO_ABORT_HI 0x02000000 +#define RBBM_CNTL__NO_ABORT_HDP 0x04000000 +#define RBBM_CNTL__NO_ABORT_MC 0x08000000 +#define RBBM_CNTL__NO_ABORT_AIC 0x10000000 +#define RBBM_CNTL__NO_ABORT_VIP 0x20000000 +#define RBBM_CNTL__NO_ABORT_DISP 0x40000000 +#define RBBM_CNTL__NO_ABORT_CG 0x80000000 +#define RBBM_CNTL__NO_ABORT_VAP 0x00080000 +#define RBBM_CNTL__NO_ABORT_GA 0x00100000 +#define RBBM_CNTL__NO_ABORT_TVOUT 0x00800000 +#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 RBBM_CMDFIFO_ADDR 0x00000E70 +#define RBBM_CMDFIFO_ADDR__CMDFIFO_ADDR__MASK 0x0000003F +#define RBBM_CMDFIFO_ADDR__CMDFIFO_ADDR__SHIFT 0 +#define RBBM_CMDFIFO_ADDR__CMDFIFO_ADDR_R3__MASK 0x000001FF +#define RBBM_CMDFIFO_ADDR__CMDFIFO_ADDR_R3__SHIFT 0 +#define RBBM_CMDFIFO_DATA 0x00000E74 +#define RBBM_CMDFIFO_DATA__CMDFIFO_DATA__MASK 0xFFFFFFFF +#define RBBM_CMDFIFO_DATA__CMDFIFO_DATA__SHIFT 0 +#define RBBM_CMDFIFO_STAT 0x00000E7C +#define RBBM_CMDFIFO_STAT__CMDFIFO_RPTR__MASK 0x0000003F +#define RBBM_CMDFIFO_STAT__CMDFIFO_RPTR__SHIFT 0 +#define RBBM_CMDFIFO_STAT__CMDFIFO_WPTR__MASK 0x00003F00 +#define RBBM_CMDFIFO_STAT__CMDFIFO_WPTR__SHIFT 8 +#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 +#define US_CONFIG 0x00004600 +#define US_CONFIG__NLEVEL__MASK 0x00000007 +#define US_CONFIG__NLEVEL__SHIFT 0 +#define US_CONFIG__FIRST_TEX 0x00000008 +#define US_CONFIG__PERF0__MASK 0x000001F0 +#define US_CONFIG__PERF0__SHIFT 4 +#define US_CONFIG__PERF1__MASK 0x00003E00 +#define US_CONFIG__PERF1__SHIFT 9 +#define US_CONFIG__PERF2__MASK 0x0007C000 +#define US_CONFIG__PERF2__SHIFT 14 +#define US_CONFIG__PERF3__MASK 0x00F80000 +#define US_CONFIG__PERF3__SHIFT 19 +#define US_RESET 0x0000460C +#define VAP_PVS_STATE_FLUSH_REG 0x00002284 +#define VAP_PVS_STATE_FLUSH_REG__DATA_REGISTER__MASK 0xFFFFFFFF +#define VAP_PVS_STATE_FLUSH_REG__DATA_REGISTER__SHIFT 0 + +/* packet stuff **************************************************************/ +#define PACKET_HEADER_MASK 0xC0000000 +#define PACKET_HEADER_SHIFT 30 +#define PACKET_HEADER_GET(p) (((p) & PACKET_HEADER_MASK) >> PACKET_HEADER_SHIFT) +#define PACKET_HEADER_SET(p) (((p) << PACKET_HEADER_SHIFT) & PACKET_HEADER_MASK) + +#define PACKET0_HEADER 0x0 +# define PACKET0_REG_MASK 0x00001FFF +# define PACKET0_REG_SHIFT 0 +# define PACKET0_COUNT_MASK 0x3FFF0000 +# define PACKET0_COUNT_SHIFT 16 +#define PACKET1_HEADER 0x1 +#define PACKET2_HEADER 0x2 +#define PACKET3_HEADER 0x3 +# define PACKET3_OPCODE_MASK 0x0000FF00 +# define PACKET3_OPCODE_SHIFT 8 +# define PACKET3_OPCODE_NOP 0x10 +# define PACKET3_OPCODE_BITBLT 0x92 +# define PACKET3_OPCODE_BITBLT_MULTI 0x9B +# define PACKET3_COUNT_MASK 0x3FFF0000 +# define PACKET3_COUNT_SHIFT 16 + +#define CP_PACKET0(r, n) (PACKET_HEADER_SET(PACKET0_HEADER) |\ + ((((r)>>2)<<PACKET0_REG_SHIFT) & PACKET0_REG_MASK) |\ + (((n) << PACKET0_COUNT_SHIFT) & PACKET0_COUNT_MASK)) +#define CP_PACKET3(o, n) (PACKET_HEADER_SET(PACKET3_HEADER) |\ + (((o)<<PACKET3_OPCODE_SHIFT) & PACKET3_OPCODE_MASK) |\ + (((n)<<PACKET3_COUNT_SHIFT) & PACKET3_COUNT_MASK)) + +#endif diff --git a/shared-core/radeon_ms_rom.c b/shared-core/radeon_ms_rom.c new file mode 100644 index 00000000..b4db02be --- /dev/null +++ b/shared-core/radeon_ms_rom.c @@ -0,0 +1,91 @@ +/* + * 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" + +int radeon_ms_rom_get_properties(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + switch (dev_priv->rom.type) { + case ROM_COMBIOS: + return radeon_ms_combios_get_properties(dev); + } + return 0; +} + +int radeon_ms_rom_init(struct drm_device *dev) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct radeon_ms_rom *rom = &dev_priv->rom; + void *rom_mapped; + char atomstr[5] = {0, 0, 0, 0, 0}; + uint16_t *offset; + + dev_priv->rom.type = ROM_UNKNOWN; + /* copy rom if any */ + rom_mapped = pci_map_rom(dev->pdev, &rom->rom_size); + if (rom_mapped && rom->rom_size) { + rom->rom_image = drm_alloc(rom->rom_size, DRM_MEM_DRIVER); + if (rom->rom_image == NULL) { + return -1; + } + memcpy(rom->rom_image, rom_mapped, rom->rom_size); + DRM_INFO("[radeon_ms] ROM %d bytes copied\n", rom->rom_size); + } else { + DRM_INFO("[radeon_ms] no ROM\n"); + return 0; + } + pci_unmap_rom(dev->pdev, rom_mapped); + + if (rom->rom_image[0] != 0x55 || rom->rom_image[1] != 0xaa) { + DRM_INFO("[radeon_ms] no ROM\n"); + DRM_INFO("[radeon_ms] ROM signature 0x55 0xaa missing\n"); + return 0; + } + offset = (uint16_t *)&rom->rom_image[ROM_HEADER]; + memcpy(atomstr, &rom->rom_image[*offset + 4], 4); + if (!strcpy(atomstr, "ATOM") || !strcpy(atomstr, "MOTA")) { + DRM_INFO("[radeon_ms] ATOMBIOS ROM detected\n"); + return 0; + } else { + struct combios_header **header; + + header = &rom->rom.combios_header; + if ((*offset + sizeof(struct combios_header)) > rom->rom_size) { + DRM_INFO("[radeon_ms] wrong COMBIOS header offset\n"); + return -1; + } + dev_priv->rom.type = ROM_COMBIOS; + *header = (struct combios_header *)&rom->rom_image[*offset]; + DRM_INFO("[radeon_ms] COMBIOS type : %d\n", + (*header)->ucTypeDefinition); + DRM_INFO("[radeon_ms] COMBIOS OEM ID: %02x %02x\n", + (*header)->ucOemID1, (*header)->ucOemID2); + } + return 0; +} diff --git a/shared-core/radeon_ms_rom.h b/shared-core/radeon_ms_rom.h new file mode 100644 index 00000000..36a54cbb --- /dev/null +++ b/shared-core/radeon_ms_rom.h @@ -0,0 +1,51 @@ +/* + * 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> + */ +#ifndef __RADEON_MS_ROM_H__ +#define __RADEON_MS_ROM_H__ + +#include "radeon_ms_combios.h" + +enum radeon_rom_type { + ROM_COMBIOS, + ROM_ATOMBIOS, + ROM_UNKNOWN +}; + +union radeon_ms_rom_type { + struct combios_header *combios_header; +}; + +struct radeon_ms_rom { + uint8_t type; + size_t rom_size; + uint8_t *rom_image; + union radeon_ms_rom_type rom; +}; + +#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/shared-core/radeon_state.c b/shared-core/radeon_state.c index 57e3a3bb..e0a00f02 100644 --- a/shared-core/radeon_state.c +++ b/shared-core/radeon_state.c @@ -745,13 +745,14 @@ static struct { */ static void radeon_clear_box(drm_radeon_private_t * dev_priv, + struct drm_radeon_master_private *master_priv, int x, int y, int w, int h, int r, int g, int b) { u32 color; RING_LOCALS; - x += dev_priv->sarea_priv->boxes[0].x1; - y += dev_priv->sarea_priv->boxes[0].y1; + x += master_priv->sarea_priv->boxes[0].x1; + y += master_priv->sarea_priv->boxes[0].y1; switch (dev_priv->color_fmt) { case RADEON_COLOR_FORMAT_RGB565: @@ -779,7 +780,7 @@ static void radeon_clear_box(drm_radeon_private_t * dev_priv, RADEON_GMC_SRC_DATATYPE_COLOR | RADEON_ROP3_P | RADEON_GMC_CLR_CMP_CNTL_DIS); - if (dev_priv->sarea_priv->pfCurrentPage == 1) { + if (master_priv->sarea_priv->pfCurrentPage == 1) { OUT_RING(dev_priv->front_pitch_offset); } else { OUT_RING(dev_priv->back_pitch_offset); @@ -793,7 +794,7 @@ static void radeon_clear_box(drm_radeon_private_t * dev_priv, ADVANCE_RING(); } -static void radeon_cp_performance_boxes(drm_radeon_private_t * dev_priv) +static void radeon_cp_performance_boxes(drm_radeon_private_t * dev_priv, struct drm_radeon_master_private *master_priv) { /* Collapse various things into a wait flag -- trying to * guess if userspase slept -- better just to have them tell us. @@ -810,12 +811,12 @@ static void radeon_cp_performance_boxes(drm_radeon_private_t * dev_priv) /* Purple box for page flipping */ if (dev_priv->stats.boxes & RADEON_BOX_FLIP) - radeon_clear_box(dev_priv, 4, 4, 8, 8, 255, 0, 255); + radeon_clear_box(dev_priv, master_priv, 4, 4, 8, 8, 255, 0, 255); /* Red box if we have to wait for idle at any point */ if (dev_priv->stats.boxes & RADEON_BOX_WAIT_IDLE) - radeon_clear_box(dev_priv, 16, 4, 8, 8, 255, 0, 0); + radeon_clear_box(dev_priv, master_priv, 16, 4, 8, 8, 255, 0, 0); /* Blue box: lost context? */ @@ -823,12 +824,12 @@ static void radeon_cp_performance_boxes(drm_radeon_private_t * dev_priv) /* Yellow box for texture swaps */ if (dev_priv->stats.boxes & RADEON_BOX_TEXTURE_LOAD) - radeon_clear_box(dev_priv, 40, 4, 8, 8, 255, 255, 0); + radeon_clear_box(dev_priv, master_priv, 40, 4, 8, 8, 255, 255, 0); /* Green box if hardware never idles (as far as we can tell) */ if (!(dev_priv->stats.boxes & RADEON_BOX_DMA_IDLE)) - radeon_clear_box(dev_priv, 64, 4, 8, 8, 0, 255, 0); + radeon_clear_box(dev_priv, master_priv, 64, 4, 8, 8, 0, 255, 0); /* Draw bars indicating number of buffers allocated * (not a great measure, easily confused) @@ -837,7 +838,7 @@ static void radeon_cp_performance_boxes(drm_radeon_private_t * dev_priv) if (dev_priv->stats.requested_bufs > 100) dev_priv->stats.requested_bufs = 100; - radeon_clear_box(dev_priv, 4, 16, + radeon_clear_box(dev_priv, master_priv, 4, 16, dev_priv->stats.requested_bufs, 4, 196, 128, 128); } @@ -851,11 +852,13 @@ static void radeon_cp_performance_boxes(drm_radeon_private_t * dev_priv) */ static void radeon_cp_dispatch_clear(struct drm_device * dev, + struct drm_master *master, drm_radeon_clear_t * clear, drm_radeon_clear_rect_t * depth_boxes) { drm_radeon_private_t *dev_priv = dev->dev_private; - drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + struct drm_radeon_master_private *master_priv = master->driver_priv; + drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv; drm_radeon_depth_clear_t *depth_clear = &dev_priv->depth_clear; int nbox = sarea_priv->nbox; struct drm_clip_rect *pbox = sarea_priv->boxes; @@ -867,7 +870,7 @@ static void radeon_cp_dispatch_clear(struct drm_device * dev, dev_priv->stats.clears++; - if (dev_priv->sarea_priv->pfCurrentPage == 1) { + if (sarea_priv->pfCurrentPage == 1) { unsigned int tmp = flags; flags &= ~(RADEON_FRONT | RADEON_BACK); @@ -893,7 +896,7 @@ static void radeon_cp_dispatch_clear(struct drm_device * dev, /* Make sure we restore the 3D state next time. */ - dev_priv->sarea_priv->ctx_owner = 0; + sarea_priv->ctx_owner = 0; for (i = 0; i < nbox; i++) { int x = pbox[i].x1; @@ -970,7 +973,7 @@ static void radeon_cp_dispatch_clear(struct drm_device * dev, /* Make sure we restore the 3D state next time. * we haven't touched any "normal" state - still need this? */ - dev_priv->sarea_priv->ctx_owner = 0; + sarea_priv->ctx_owner = 0; if ((dev_priv->flags & RADEON_HAS_HIERZ) && (flags & RADEON_USE_HIERZ)) { @@ -1220,7 +1223,7 @@ static void radeon_cp_dispatch_clear(struct drm_device * dev, /* Make sure we restore the 3D state next time. */ - dev_priv->sarea_priv->ctx_owner = 0; + sarea_priv->ctx_owner = 0; for (i = 0; i < nbox; i++) { @@ -1291,7 +1294,7 @@ static void radeon_cp_dispatch_clear(struct drm_device * dev, /* Make sure we restore the 3D state next time. */ - dev_priv->sarea_priv->ctx_owner = 0; + sarea_priv->ctx_owner = 0; for (i = 0; i < nbox; i++) { @@ -1334,20 +1337,21 @@ static void radeon_cp_dispatch_clear(struct drm_device * dev, * wait on this value before performing the clear ioctl. We * need this because the card's so damned fast... */ - dev_priv->sarea_priv->last_clear++; + sarea_priv->last_clear++; BEGIN_RING(4); - RADEON_CLEAR_AGE(dev_priv->sarea_priv->last_clear); + RADEON_CLEAR_AGE(sarea_priv->last_clear); RADEON_WAIT_UNTIL_IDLE(); ADVANCE_RING(); } -static void radeon_cp_dispatch_swap(struct drm_device * dev) +static void radeon_cp_dispatch_swap(struct drm_device * dev, struct drm_master *master) { drm_radeon_private_t *dev_priv = dev->dev_private; - drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + struct drm_radeon_master_private *master_priv = master->driver_priv; + drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv; int nbox = sarea_priv->nbox; struct drm_clip_rect *pbox = sarea_priv->boxes; int i; @@ -1357,7 +1361,7 @@ static void radeon_cp_dispatch_swap(struct drm_device * dev) /* Do some trivial performance monitoring... */ if (dev_priv->do_boxes) - radeon_cp_performance_boxes(dev_priv); + radeon_cp_performance_boxes(dev_priv, master_priv); /* Wait for the 3D stream to idle before dispatching the bitblt. * This will prevent data corruption between the two streams. @@ -1391,7 +1395,7 @@ static void radeon_cp_dispatch_swap(struct drm_device * dev) /* Make this work even if front & back are flipped: */ OUT_RING(CP_PACKET0(RADEON_SRC_PITCH_OFFSET, 1)); - if (dev_priv->sarea_priv->pfCurrentPage == 0) { + if (sarea_priv->pfCurrentPage == 0) { OUT_RING(dev_priv->back_pitch_offset); OUT_RING(dev_priv->front_pitch_offset); } else { @@ -1411,31 +1415,32 @@ static void radeon_cp_dispatch_swap(struct drm_device * dev) * throttle the framerate by waiting for this value before * performing the swapbuffer ioctl. */ - dev_priv->sarea_priv->last_frame++; + sarea_priv->last_frame++; BEGIN_RING(4); - RADEON_FRAME_AGE(dev_priv->sarea_priv->last_frame); + RADEON_FRAME_AGE(sarea_priv->last_frame); RADEON_WAIT_UNTIL_2D_IDLE(); ADVANCE_RING(); } -static void radeon_cp_dispatch_flip(struct drm_device * dev) +void radeon_cp_dispatch_flip(struct drm_device * dev, struct drm_master *master) { drm_radeon_private_t *dev_priv = dev->dev_private; - struct drm_sarea *sarea = (struct drm_sarea *) dev_priv->sarea->handle; - int offset = (dev_priv->sarea_priv->pfCurrentPage == 1) + struct drm_radeon_master_private *master_priv = master->driver_priv; + struct drm_sarea *sarea = (struct drm_sarea *) master_priv->sarea->handle; + int offset = (master_priv->sarea_priv->pfCurrentPage == 1) ? dev_priv->front_offset : dev_priv->back_offset; RING_LOCALS; DRM_DEBUG("pfCurrentPage=%d\n", - dev_priv->sarea_priv->pfCurrentPage); + master_priv->sarea_priv->pfCurrentPage); /* Do some trivial performance monitoring... */ if (dev_priv->do_boxes) { dev_priv->stats.boxes |= RADEON_BOX_FLIP; - radeon_cp_performance_boxes(dev_priv); + radeon_cp_performance_boxes(dev_priv, master_priv); } /* Update the frame offsets for both CRTCs @@ -1447,7 +1452,7 @@ static void radeon_cp_dispatch_flip(struct drm_device * dev) ((sarea->frame.y * dev_priv->front_pitch + sarea->frame.x * (dev_priv->color_fmt - 2)) & ~7) + offset); - OUT_RING_REG(RADEON_CRTC2_OFFSET, dev_priv->sarea_priv->crtc2_base + OUT_RING_REG(RADEON_CRTC2_OFFSET, master_priv->sarea_priv->crtc2_base + offset); ADVANCE_RING(); @@ -1456,13 +1461,13 @@ static void radeon_cp_dispatch_flip(struct drm_device * dev) * throttle the framerate by waiting for this value before * performing the swapbuffer ioctl. */ - dev_priv->sarea_priv->last_frame++; - dev_priv->sarea_priv->pfCurrentPage = - 1 - dev_priv->sarea_priv->pfCurrentPage; + master_priv->sarea_priv->last_frame++; + master_priv->sarea_priv->pfCurrentPage = + 1 - master_priv->sarea_priv->pfCurrentPage; BEGIN_RING(2); - RADEON_FRAME_AGE(dev_priv->sarea_priv->last_frame); + RADEON_FRAME_AGE(master_priv->sarea_priv->last_frame); ADVANCE_RING(); } @@ -1500,11 +1505,13 @@ typedef struct { } drm_radeon_tcl_prim_t; static void radeon_cp_dispatch_vertex(struct drm_device * dev, + struct drm_file *file_priv, struct drm_buf * buf, drm_radeon_tcl_prim_t * prim) { drm_radeon_private_t *dev_priv = dev->dev_private; - drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv; + drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv; int offset = dev_priv->gart_buffers_offset + buf->offset + prim->start; int numverts = (int)prim->numverts; int nbox = sarea_priv->nbox; @@ -1545,13 +1552,14 @@ static void radeon_cp_dispatch_vertex(struct drm_device * dev, } while (i < nbox); } -static void radeon_cp_discard_buffer(struct drm_device * dev, struct drm_buf * buf) +static void radeon_cp_discard_buffer(struct drm_device * dev, struct drm_master *master, struct drm_buf * buf) { drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_master_private *master_priv = master->driver_priv; drm_radeon_buf_priv_t *buf_priv = buf->dev_private; RING_LOCALS; - buf_priv->age = ++dev_priv->sarea_priv->last_dispatch; + buf_priv->age = ++master_priv->sarea_priv->last_dispatch; /* Emit the vertex buffer age */ BEGIN_RING(2); @@ -1596,12 +1604,14 @@ static void radeon_cp_dispatch_indirect(struct drm_device * dev, } } -static void radeon_cp_dispatch_indices(struct drm_device * dev, +static void radeon_cp_dispatch_indices(struct drm_device *dev, + struct drm_master *master, struct drm_buf * elt_buf, drm_radeon_tcl_prim_t * prim) { drm_radeon_private_t *dev_priv = dev->dev_private; - drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + struct drm_radeon_master_private *master_priv = master->driver_priv; + drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv; int offset = dev_priv->gart_buffers_offset + prim->offset; u32 *data; int dwords; @@ -1874,9 +1884,10 @@ static int radeon_cp_dispatch_texture(struct drm_device * dev, OUT_RING((image->width << 16) | height); RADEON_WAIT_UNTIL_2D_IDLE(); ADVANCE_RING(); - COMMIT_RING(); - radeon_cp_discard_buffer(dev, buf); + radeon_cp_discard_buffer(dev, file_priv->master, buf); + + COMMIT_RING(); /* Update the input parameters for next time */ image->y += height; @@ -2126,7 +2137,8 @@ static int radeon_surface_free(struct drm_device *dev, void *data, struct drm_fi static int radeon_cp_clear(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_radeon_private_t *dev_priv = dev->dev_private; - drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv; + drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv; drm_radeon_clear_t *clear = data; drm_radeon_clear_rect_t depth_boxes[RADEON_NR_SAREA_CLIPRECTS]; DRM_DEBUG("\n"); @@ -2142,7 +2154,7 @@ static int radeon_cp_clear(struct drm_device *dev, void *data, struct drm_file * sarea_priv->nbox * sizeof(depth_boxes[0]))) return -EFAULT; - radeon_cp_dispatch_clear(dev, clear, depth_boxes); + radeon_cp_dispatch_clear(dev, file_priv->master, clear, depth_boxes); COMMIT_RING(); return 0; @@ -2150,9 +2162,10 @@ static int radeon_cp_clear(struct drm_device *dev, void *data, struct drm_file * /* Not sure why this isn't set all the time: */ -static int radeon_do_init_pageflip(struct drm_device * dev) +static int radeon_do_init_pageflip(struct drm_device * dev, struct drm_master *master) { drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_master_private *master_priv = master->driver_priv; RING_LOCALS; DRM_DEBUG("\n"); @@ -2169,8 +2182,8 @@ static int radeon_do_init_pageflip(struct drm_device * dev) dev_priv->page_flipping = 1; - if (dev_priv->sarea_priv->pfCurrentPage != 1) - dev_priv->sarea_priv->pfCurrentPage = 0; + if (master_priv->sarea_priv->pfCurrentPage != 1) + master_priv->sarea_priv->pfCurrentPage = 0; return 0; } @@ -2188,9 +2201,9 @@ static int radeon_cp_flip(struct drm_device *dev, void *data, struct drm_file *f RING_SPACE_TEST_WITH_RETURN(dev_priv); if (!dev_priv->page_flipping) - radeon_do_init_pageflip(dev); + radeon_do_init_pageflip(dev, file_priv->master); - radeon_cp_dispatch_flip(dev); + radeon_cp_dispatch_flip(dev, file_priv->master); COMMIT_RING(); return 0; @@ -2199,7 +2212,9 @@ static int radeon_cp_flip(struct drm_device *dev, void *data, struct drm_file *f static int radeon_cp_swap(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_radeon_private_t *dev_priv = dev->dev_private; - drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv; + drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv; + DRM_DEBUG("\n"); LOCK_TEST_WITH_RETURN(dev, file_priv); @@ -2209,8 +2224,11 @@ static int radeon_cp_swap(struct drm_device *dev, void *data, struct drm_file *f if (sarea_priv->nbox > RADEON_NR_SAREA_CLIPRECTS) sarea_priv->nbox = RADEON_NR_SAREA_CLIPRECTS; - radeon_cp_dispatch_swap(dev); - dev_priv->sarea_priv->ctx_owner = 0; + if (dev_priv->mm.vram_offset) + radeon_gem_update_offsets(dev, file_priv->master); + + radeon_cp_dispatch_swap(dev, file_priv->master); + sarea_priv->ctx_owner = 0; COMMIT_RING(); return 0; @@ -2219,6 +2237,7 @@ static int radeon_cp_swap(struct drm_device *dev, void *data, struct drm_file *f static int radeon_cp_vertex(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv; drm_radeon_sarea_t *sarea_priv; struct drm_device_dma *dma = dev->dma; struct drm_buf *buf; @@ -2232,7 +2251,7 @@ static int radeon_cp_vertex(struct drm_device *dev, void *data, struct drm_file return -EINVAL; } - sarea_priv = dev_priv->sarea_priv; + sarea_priv = master_priv->sarea_priv; DRM_DEBUG("pid=%d index=%d count=%d discard=%d\n", DRM_CURRENTPID, vertex->idx, vertex->count, vertex->discard); @@ -2286,13 +2305,13 @@ static int radeon_cp_vertex(struct drm_device *dev, void *data, struct drm_file prim.finish = vertex->count; /* unused */ prim.prim = vertex->prim; prim.numverts = vertex->count; - prim.vc_format = dev_priv->sarea_priv->vc_format; + prim.vc_format = sarea_priv->vc_format; - radeon_cp_dispatch_vertex(dev, buf, &prim); + radeon_cp_dispatch_vertex(dev, file_priv, buf, &prim); } if (vertex->discard) { - radeon_cp_discard_buffer(dev, buf); + radeon_cp_discard_buffer(dev, file_priv->master, buf); } COMMIT_RING(); @@ -2302,6 +2321,7 @@ static int radeon_cp_vertex(struct drm_device *dev, void *data, struct drm_file static int radeon_cp_indices(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv; drm_radeon_sarea_t *sarea_priv; struct drm_device_dma *dma = dev->dma; struct drm_buf *buf; @@ -2315,7 +2335,7 @@ static int radeon_cp_indices(struct drm_device *dev, void *data, struct drm_file DRM_ERROR("called with no initialization\n"); return -EINVAL; } - sarea_priv = dev_priv->sarea_priv; + sarea_priv = master_priv->sarea_priv; DRM_DEBUG("pid=%d index=%d start=%d end=%d discard=%d\n", DRM_CURRENTPID, elts->idx, elts->start, elts->end, @@ -2382,11 +2402,11 @@ static int radeon_cp_indices(struct drm_device *dev, void *data, struct drm_file prim.prim = elts->prim; prim.offset = 0; /* offset from start of dma buffers */ prim.numverts = RADEON_MAX_VB_VERTS; /* duh */ - prim.vc_format = dev_priv->sarea_priv->vc_format; + prim.vc_format = sarea_priv->vc_format; - radeon_cp_dispatch_indices(dev, buf, &prim); + radeon_cp_dispatch_indices(dev, file_priv->master, buf, &prim); if (elts->discard) { - radeon_cp_discard_buffer(dev, buf); + radeon_cp_discard_buffer(dev, file_priv->master, buf); } COMMIT_RING(); @@ -2502,7 +2522,7 @@ static int radeon_cp_indirect(struct drm_device *dev, void *data, struct drm_fil */ radeon_cp_dispatch_indirect(dev, buf, indirect->start, indirect->end); if (indirect->discard) { - radeon_cp_discard_buffer(dev, buf); + radeon_cp_discard_buffer(dev, file_priv->master, buf); } COMMIT_RING(); @@ -2512,6 +2532,7 @@ static int radeon_cp_indirect(struct drm_device *dev, void *data, struct drm_fil static int radeon_cp_vertex2(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv; drm_radeon_sarea_t *sarea_priv; struct drm_device_dma *dma = dev->dma; struct drm_buf *buf; @@ -2526,7 +2547,7 @@ static int radeon_cp_vertex2(struct drm_device *dev, void *data, struct drm_file return -EINVAL; } - sarea_priv = dev_priv->sarea_priv; + sarea_priv = master_priv->sarea_priv; DRM_DEBUG("pid=%d index=%d discard=%d\n", DRM_CURRENTPID, vertex->idx, vertex->discard); @@ -2588,12 +2609,12 @@ static int radeon_cp_vertex2(struct drm_device *dev, void *data, struct drm_file tclprim.offset = prim.numverts * 64; tclprim.numverts = RADEON_MAX_VB_VERTS; /* duh */ - radeon_cp_dispatch_indices(dev, buf, &tclprim); + radeon_cp_dispatch_indices(dev, file_priv->master, buf, &tclprim); } else { tclprim.numverts = prim.numverts; tclprim.offset = 0; /* not used */ - radeon_cp_dispatch_vertex(dev, buf, &tclprim); + radeon_cp_dispatch_vertex(dev, file_priv, buf, &tclprim); } if (sarea_priv->nbox == 1) @@ -2601,7 +2622,7 @@ static int radeon_cp_vertex2(struct drm_device *dev, void *data, struct drm_file } if (vertex->discard) { - radeon_cp_discard_buffer(dev, buf); + radeon_cp_discard_buffer(dev, file_priv->master, buf); } COMMIT_RING(); @@ -2955,7 +2976,7 @@ static int radeon_cp_cmdbuf(struct drm_device *dev, void *data, struct drm_file goto err; } - radeon_cp_discard_buffer(dev, buf); + radeon_cp_discard_buffer(dev, file_priv->master, buf); break; case RADEON_CMD_PACKET3: @@ -3071,7 +3092,7 @@ static int radeon_cp_getparam(struct drm_device *dev, void *data, struct drm_fil */ case RADEON_PARAM_SAREA_HANDLE: /* The lock is the first dword in the sarea. */ - value = (long)dev->lock.hw_lock; + value = (long)dev->primary->master->lock.hw_lock; break; #endif case RADEON_PARAM_GART_TEX_HANDLE: @@ -3100,6 +3121,9 @@ static int radeon_cp_getparam(struct drm_device *dev, void *data, struct drm_fil case RADEON_PARAM_NUM_GB_PIPES: value = dev_priv->num_gb_pipes; break; + case RADEON_PARAM_KERNEL_MM: + value = dev_priv->mm_enabled; + break; default: DRM_DEBUG( "Invalid parameter %d\n", param->param ); return -EINVAL; @@ -3116,6 +3140,7 @@ static int radeon_cp_getparam(struct drm_device *dev, void *data, struct drm_fil static int radeon_cp_setparam(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv; drm_radeon_setparam_t *sp = data; struct drm_radeon_driver_file_fields *radeon_priv; @@ -3135,14 +3160,14 @@ static int radeon_cp_setparam(struct drm_device *dev, void *data, struct drm_fil DRM_DEBUG("color tiling disabled\n"); dev_priv->front_pitch_offset &= ~RADEON_DST_TILE_MACRO; dev_priv->back_pitch_offset &= ~RADEON_DST_TILE_MACRO; - if (dev_priv->sarea_priv) - dev_priv->sarea_priv->tiling_enabled = 0; + if (master_priv->sarea_priv) + master_priv->sarea_priv->tiling_enabled = 0; } else if (sp->value == 1) { DRM_DEBUG("color tiling enabled\n"); dev_priv->front_pitch_offset |= RADEON_DST_TILE_MACRO; dev_priv->back_pitch_offset |= RADEON_DST_TILE_MACRO; - if (dev_priv->sarea_priv) - dev_priv->sarea_priv->tiling_enabled = 1; + if (master_priv->sarea_priv) + master_priv->sarea_priv->tiling_enabled = 1; } break; case RADEON_SETPARAM_PCIGART_LOCATION: @@ -3160,6 +3185,10 @@ static int radeon_cp_setparam(struct drm_device *dev, void *data, struct drm_fil case RADEON_SETPARAM_VBLANK_CRTC: return radeon_vblank_crtc_set(dev, sp->value); break; + case RADEON_SETPARAM_MM_INIT: + dev_priv->user_mm_enable = true; + dev_priv->new_memmap = true; + return radeon_gem_mm_init(dev); default: DRM_DEBUG("Invalid parameter %d\n", sp->param); return -EINVAL; @@ -3189,14 +3218,6 @@ void radeon_driver_preclose(struct drm_device *dev, void radeon_driver_lastclose(struct drm_device *dev) { - if (dev->dev_private) { - drm_radeon_private_t *dev_priv = dev->dev_private; - - if (dev_priv->sarea_priv && - dev_priv->sarea_priv->pfCurrentPage != 0) - radeon_cp_dispatch_flip(dev); - } - radeon_do_release(dev); } @@ -3257,7 +3278,20 @@ struct drm_ioctl_desc radeon_ioctls[] = { DRM_IOCTL_DEF(DRM_RADEON_IRQ_WAIT, radeon_irq_wait, DRM_AUTH), DRM_IOCTL_DEF(DRM_RADEON_SETPARAM, radeon_cp_setparam, DRM_AUTH), DRM_IOCTL_DEF(DRM_RADEON_SURF_ALLOC, radeon_surface_alloc, DRM_AUTH), - DRM_IOCTL_DEF(DRM_RADEON_SURF_FREE, radeon_surface_free, DRM_AUTH) + DRM_IOCTL_DEF(DRM_RADEON_SURF_FREE, radeon_surface_free, DRM_AUTH), + + DRM_IOCTL_DEF(DRM_RADEON_GEM_INFO, radeon_gem_info_ioctl, DRM_AUTH), + DRM_IOCTL_DEF(DRM_RADEON_GEM_CREATE, radeon_gem_create_ioctl, DRM_AUTH), + + DRM_IOCTL_DEF(DRM_RADEON_GEM_MMAP, radeon_gem_mmap_ioctl, DRM_AUTH), + DRM_IOCTL_DEF(DRM_RADEON_GEM_PIN, radeon_gem_pin_ioctl, DRM_AUTH), + DRM_IOCTL_DEF(DRM_RADEON_GEM_UNPIN, radeon_gem_unpin_ioctl, DRM_AUTH), + DRM_IOCTL_DEF(DRM_RADEON_GEM_PREAD, radeon_gem_pread_ioctl, DRM_AUTH), + DRM_IOCTL_DEF(DRM_RADEON_GEM_PWRITE, radeon_gem_pwrite_ioctl, DRM_AUTH), + DRM_IOCTL_DEF(DRM_RADEON_GEM_SET_DOMAIN, radeon_gem_set_domain_ioctl, DRM_AUTH), + DRM_IOCTL_DEF(DRM_RADEON_GEM_WAIT_RENDERING, radeon_gem_wait_rendering, DRM_AUTH), + DRM_IOCTL_DEF(DRM_RADEON_CS, radeon_cs_ioctl, DRM_AUTH), + DRM_IOCTL_DEF(DRM_RADEON_CS2, radeon_cs2_ioctl, DRM_AUTH), }; int radeon_max_ioctl = DRM_ARRAY_SIZE(radeon_ioctls); diff --git a/tests/Makefile.am b/tests/Makefile.am index 95f0f22e..805bf770 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -26,7 +26,9 @@ TESTS = auth \ gem_basic \ gem_flink \ gem_readwrite \ - gem_mmap + gem_mmap \ + radeon_gem_mmap \ + radeon_gem_basic EXTRA_PROGRAMS = $(TESTS) CLEANFILES = $(EXTRA_PROGRAMS) $(EXTRA_LTLIBRARIES) diff --git a/tests/dristat.c b/tests/dristat.c index 89853164..48c3b51b 100644 --- a/tests/dristat.c +++ b/tests/dristat.c @@ -263,7 +263,7 @@ int main(int argc, char **argv) for (i = 0; i < 16; i++) if (!minor || i == minor) { sprintf(buf, DRM_DEV_NAME, DRM_DIR_NAME, i); - fd = drmOpenMinor(i, 1); + fd = drmOpenMinor(i, 1, DRM_NODE_RENDER); if (fd >= 0) { printf("%s\n", buf); if (mask & DRM_BUSID) getbusid(fd); diff --git a/tests/modedemo/Makefile b/tests/modedemo/Makefile new file mode 100644 index 00000000..467fb11a --- /dev/null +++ b/tests/modedemo/Makefile @@ -0,0 +1,14 @@ + +all: app + +#CFLAGS = -g -ansi -pedantic -DPOSIX_C_SOURCE=199309L \ +# -D_POSIX_SOURCE -D_XOPEN_SOURCE -D_BSD_SOURCE -D_SVID_SOURCE \ + +app: demo.c + @gcc $(CFLAGS) -o app -Wall -I../../libdrm -I../../shared-core -L../../libdrm/.libs -ldrm demo.c + +clean: + @rm -f app + +run: app + sudo ./test diff --git a/tests/modedemo/demo.c b/tests/modedemo/demo.c new file mode 100644 index 00000000..72d69405 --- /dev/null +++ b/tests/modedemo/demo.c @@ -0,0 +1,634 @@ +/* + * Some defines to define the behavior of the program + */ + +#define CLEAN_FBDEV +#undef DEMO_CLONE + +#define SIZE_X 2048 +#define SIZE_Y 2048 +/* Pitch needs to be power of two */ +#define PITCH 2048 + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <unistd.h> +#include <string.h> +#ifdef CLEAN_FBDEV +#include <errno.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <linux/fb.h> +#endif +#include <signal.h> + +#include "xf86drm.h" +#include "xf86drmMode.h" + +/* old functions to be replaced */ +drmModeFBPtr createFB(int fd, drmModeResPtr res); +void testCursor(int fd, uint32_t crtc); +void prettyColors(int fd, unsigned int handle); +void prettyCursor(int fd, unsigned int handle, unsigned int color); + +#ifdef CLEAN_FBDEV +struct fb_var_screeninfo var; +struct fb_fix_screeninfo fix; +#endif + +/* structs for the demo_driver */ + +struct demo_driver; + +struct demo_screen +{ + /* drm stuff */ + drmBO buffer; + drmModeFBPtr fb; + drmModeCrtcPtr crtc; + + size_t num_outputs; + uint32_t outputs_id[8]; + drmModeOutputPtr outputs[8]; + + struct drm_mode_modeinfo *mode; + + /* virtual buffer */ + uint32_t virt_x; + uint32_t virt_y; + uint32_t pitch; + + /* parent */ + struct demo_driver *driver; +}; + +#define DEMO_MAX_SCREENS 4 +#define MAX_FIND_OUTPUTS 8 + +struct demo_driver +{ + /* drm stuff */ + int fd; + drmModeResPtr res; + + /* screens */ + size_t numScreens; + struct demo_screen screens[DEMO_MAX_SCREENS]; +}; + +struct demo_driver* demoCreateDriver(void); +void demoUpdateRes(struct demo_driver *driver); +int demoCreateScreens(struct demo_driver *driver); +int demoCreateScreenCloned(struct demo_driver *driver); +void demoTakeDownScreen(struct demo_screen *screen); +int demoFindConnectedOutputs(struct demo_driver *driver, drmModeOutputPtr *out, size_t max_out); +drmModeCrtcPtr demoFindFreeCrtc(struct demo_driver *driver, drmModeOutputPtr output); +void demoPanScreen(struct demo_screen *screen, uint16_t x, uint16_t y); +/* yet to be implemented */ +void demoMouseActivate(struct demo_screen *screen); +void demoMouseMove(struct demo_screen *screen, uint16_t x, uint16_t y); + +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 main(int argc, char **argv) +{ + struct demo_driver *driver; + int num; + int i; + +#ifdef CLEAN_FBDEV + int fbdev_fd; + + fbdev_fd = open("/dev/fb0", O_RDWR); + + memset(&var, 0, sizeof(struct fb_var_screeninfo)); + memset(&fix, 0, sizeof(struct fb_fix_screeninfo)); + + if (ioctl(fbdev_fd, FBIOGET_VSCREENINFO, &var)) + printf("var %s\n", strerror(errno)); + if (ioctl(fbdev_fd, FBIOGET_FSCREENINFO, &fix)) + printf("fix %s\n", strerror(errno)); +#endif + + printf("starting demo\n"); + + driver = demoCreateDriver(); + + if (!driver) { + printf("failed to create driver\n"); + return 1; + } + +#ifndef DEMO_CLONE + num = demoCreateScreens(driver); +#else + num = demoCreateScreenCloned(driver); +#endif + + if (num < 1) { + printf("no screens attached or an error occured\n"); + return 1; + } + printf("created %i screens\n", num); + + for (i = 0; i < num; i++) { + prettyColors(driver->fd, driver->screens[i].fb->handle); + } + sleep(1); + + for (i = 0; i < num; i++) { + printf("%i: 100 0\n", i); + demoPanScreen(&driver->screens[i], 100, 0); + sleep(1); + + printf("%i: 0 100\n", i); + demoPanScreen(&driver->screens[i], 0, 100); + sleep(1); + + printf("%i: 100 100\n", i); + demoPanScreen(&driver->screens[i], 100, 100); + sleep(1); + + printf("%i: 0 0\n", i); + demoPanScreen(&driver->screens[i], 0, 1); + sleep(1); + testCursor(driver->fd, driver->screens[i].crtc->crtc_id); + } + + sleep(2); + printf("taking down screens\n"); + for (i = 0; i < num; i++) { + demoTakeDownScreen(&driver->screens[i]); + } + +#ifdef CLEAN_FBDEV + if (ioctl(fbdev_fd, FBIOPUT_VSCREENINFO, &var)) + printf("var %s\n", strerror(errno)); + + close(fbdev_fd); +#endif + + printf("ok\n"); + return 0; +} + +int demoCreateScreens(struct demo_driver *driver) +{ + drmModeOutputPtr out[MAX_FIND_OUTPUTS]; + int num; + int num_screens = 0; + struct demo_screen *screen; + int ret = 0; + int i; + + num = demoFindConnectedOutputs(driver, out, MAX_FIND_OUTPUTS); + if (num < 0) + return 0; + + printf("found %i connected outputs\n", num); + + for (i = 0; i < num; i++) { + screen = &driver->screens[i]; + + screen->crtc = demoFindFreeCrtc(driver, out[i]); + if (!screen->crtc) { + printf("found no free crtc for output\n"); + drmModeFreeOutput(out[i]); + continue; + } + + screen->fb = createFB(driver->fd, driver->res); + if (!screen->fb) { + drmModeFreeOutput(out[i]); + drmModeFreeCrtc(screen->crtc); + screen->crtc = 0; + printf("could not create framebuffer\n"); + continue; + } + + screen->virt_x = SIZE_X; + screen->virt_y = SIZE_Y; + screen->pitch = PITCH; + + screen->outputs[0] = out[i]; + screen->outputs_id[0] = out[i]->output_id; + screen->num_outputs = 1; + + screen->mode = &mode; + screen->driver = driver; + + ret = drmModeSetCrtc( + driver->fd, + screen->crtc->crtc_id, + screen->fb->buffer_id, + 0, 0, + screen->outputs_id, screen->num_outputs, + screen->mode); + + if (ret) { + printf("failed to set mode\n"); + demoTakeDownScreen(screen); + } else { + num_screens++; + } + + demoUpdateRes(driver); + } + + return num_screens; +} + +int demoCreateScreenCloned(struct demo_driver *driver) +{ + drmModeOutputPtr out[MAX_FIND_OUTPUTS]; + int num; + struct demo_screen *screen; + int ret = 0; + int i; + + num = demoFindConnectedOutputs(driver, out, MAX_FIND_OUTPUTS); + if (num < 0) + return 0; + + printf("found %i connected outputs\n", num); + + screen = &driver->screens[0]; + + screen->fb = createFB(driver->fd, driver->res); + if (!screen->fb) { + printf("could not create framebuffer\n"); + return 0; + } + + screen->mode = &mode; + screen->driver = driver; + + screen->virt_x = SIZE_X; + screen->virt_y = SIZE_Y; + screen->pitch = PITCH; + + screen->num_outputs = 0; + for (i = 0; i < num; i++) { + screen->crtc = demoFindFreeCrtc(driver, out[i]); + if (!screen->crtc) { + printf("found no free crtc for output\n"); + drmModeFreeOutput(out[i]); + continue; + } + + screen->outputs[screen->num_outputs] = out[i]; + screen->outputs_id[screen->num_outputs] = out[i]->output_id; + screen->num_outputs++; + printf("%u, %u\n", out[i]->output_id, screen->num_outputs); + } + + ret = drmModeSetCrtc( + driver->fd, + screen->crtc->crtc_id, + screen->fb->buffer_id, + 0, 0, + screen->outputs_id, screen->num_outputs, + screen->mode); + + if (ret) { + printf("failed to set mode\n"); + demoTakeDownScreen(screen); + return 0; + } + + demoUpdateRes(driver); + + return 1; +} + +void demoTakeDownScreen(struct demo_screen *screen) +{ + int fd = screen->driver->fd; + int i; + drmBO bo; + +#if 0 + /* This can bust the fbdev arrangement as it basically unhooks + * the outputs and the fbdev backend doesn't know how to put things + * back on track. Realistically, it's up to the crtc owner to restore + * things..... + * + * So if you are mixing API's make sure the modesetting owner puts + * back the original CRTC arrangement so fbdev can continue... + * + * Ho-hum.. + */ + if (screen->crtc) + drmModeSetCrtc(fd, screen->crtc->crtc_id, 0, 0, 0, 0, 0, 0); +#endif + + if (screen->fb) + drmModeRmFB(fd, screen->fb->buffer_id); + + /* maybe we should keep a pointer to the bo on the screen */ + if (screen->fb && !drmBOReference(fd, screen->fb->handle, &bo)) { + drmBOUnreference(fd, &bo); + drmBOUnreference(fd, &bo); + } else { + printf("bo error\n"); + } + + for (i = 0; i < screen->num_outputs; i++) { + drmModeFreeOutput(screen->outputs[i]); + screen->outputs[i] = NULL; + } + + drmModeFreeCrtc(screen->crtc); + drmModeFreeFB(screen->fb); + + screen->crtc = NULL; + screen->fb = NULL; +} + +drmModeCrtcPtr demoFindFreeCrtc(struct demo_driver *driver, drmModeOutputPtr output) +{ + drmModeCrtcPtr crtc; + int i, j, used = 0; + drmModeResPtr res = driver->res; + + for (i = 0; i < res->count_crtcs; i++) { + used = 0; + for (j = 0; j < DEMO_MAX_SCREENS; j++) { + crtc = driver->screens[j].crtc; + + if (crtc && crtc->crtc_id == res->crtcs[i]) + used = 1; + } + + if (!used) { + crtc = drmModeGetCrtc(driver->fd, res->crtcs[i]); + break; + } else { + crtc = 0; + } + } + + return crtc; +} + +static int driverfd; + +static void +hotplugSIGNAL(int sig, siginfo_t *si, void *d) +{ + union drm_wait_hotplug hw; + int ret; + + printf("GOT HOTPLUG EVENT!\n"); + + /* ask for another hotplug event ! */ + memset(&hw, 0, sizeof(hw)); + hw.request.type = _DRM_HOTPLUG_SIGNAL; + hw.request.signal = SIGUSR1; + ret = ioctl(driverfd, DRM_IOCTL_WAIT_HOTPLUG, &hw); +} + +struct demo_driver* demoCreateDriver(void) +{ + struct demo_driver* driver = malloc(sizeof(struct demo_driver)); + union drm_wait_hotplug hw; + int ret = 0; + + memset(driver, 0, sizeof(struct demo_driver)); + + driver->fd = drmOpen("i915",NULL); + + if (driver->fd < 0) { + printf("Failed to open the card fb\n"); + goto err_driver; + } + +#if 0 + /* ioctl wait for hotplug */ + do { + memset(&hw, 0, sizeof(hw)); + ret = ioctl(driver->fd, DRM_IOCTL_WAIT_HOTPLUG, &hw); + printf("HOTPLUG %d %d %d\n",ret,errno,hw.reply.counter); + } while (ret && errno == EBUSY); +#else + /* signal for hotplug */ + { + struct sigaction sa; + struct sigaction osa; + + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = hotplugSIGNAL; + sigaction(SIGUSR1, &sa, &osa); + + driverfd = driver->fd; + + memset(&hw, 0, sizeof(hw)); + hw.request.type = _DRM_HOTPLUG_SIGNAL; + hw.request.signal = SIGUSR1; + ret = ioctl(driver->fd, DRM_IOCTL_WAIT_HOTPLUG, &hw); + } +#endif + + demoUpdateRes(driver); + if (!driver->res) { + printf("could not retrive resources\n"); + goto err_res; + } + + return driver; + +err_res: + drmClose(driver->fd); +err_driver: + free(driver); + return NULL; +} + +void demoUpdateRes(struct demo_driver *driver) +{ + if (driver->res) + drmModeFreeResources(driver->res); + + driver->res = drmModeGetResources(driver->fd); + + if (!driver->res) + printf("failed to get resources from kernel\n"); +} + +int demoFindConnectedOutputs(struct demo_driver *driver, drmModeOutputPtr *out, size_t max_out) +{ + int count = 0; + int i,j; + int fd = driver->fd; + drmModeResPtr res = driver->res; + + drmModeOutputPtr output; + + for (i = 0; i < res->count_outputs && count < max_out; i++) { + output = drmModeGetOutput(fd, res->outputs[i]); + + if (!output) + continue; + + if (output->connection == DRM_MODE_DISCONNECTED) { + drmModeFreeOutput(output); + continue; + } + + for (j = 0; j < output->count_props; j++) { + drmModePropertyPtr prop; + + prop = drmModeGetProperty(fd, output->props[j]); + + printf("Property: %s\n",prop->name); + if (prop->count_enums) + printf("%s\n",prop->enums[output->prop_values[j]].name); + } + + out[count++] = output; + } + + return count; +} + +void demoPanScreen(struct demo_screen *screen, uint16_t x, uint16_t y) +{ + drmModeSetCrtc( + screen->driver->fd, + screen->crtc->crtc_id, + screen->fb->buffer_id, + x, y, + screen->outputs_id, screen->num_outputs, + screen->mode); +} + +drmModeFBPtr createFB(int fd, drmModeResPtr res) +{ + drmModeFBPtr frame; + unsigned int fb = 0; + int ret = 0; + drmBO bo; + + ret = drmBOCreate(fd, SIZE_X * SIZE_Y * 4, 0, 0, + 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, &bo); + + if (ret) { + printf("failed to create framebuffer (ret %d)\n",ret); + goto err; + } + + ret = drmModeAddFB(fd, SIZE_X, SIZE_Y, 32, 32, PITCH * 4, bo.handle, &fb); + + if (ret) + goto err_bo; + + frame = drmModeGetFB(fd, fb); + + if (!frame) + goto err_bo; + + return frame; + +err_bo: + drmBOUnreference(fd, &bo); +err: + return 0; +} + +void draw(unsigned int x, unsigned int y, unsigned int w, unsigned int h, unsigned int v, unsigned int *ptr) +{ + int i, j; + + for (i = x; i < x + w; i++) + for(j = y; j < y + h; j++) + ptr[(i * PITCH) + j] = v; + +} + +void prettyColors(int fd, unsigned int handle) +{ + drmBO bo; + unsigned int *ptr; + int i; + + drmBOReference(fd, handle, &bo); + drmBOMap(fd, &bo, DRM_BO_FLAG_READ | DRM_BO_FLAG_WRITE, 0, (void**)&ptr); + + for (i = 0; i < (SIZE_X*SIZE_Y); i++) + ptr[i] = 0xFFFFFFFF; + + for (i = 0; i < 8; i++) + draw(i * 40, i * 40, 40, 40, 0, ptr); + + + draw(200, 100, 40, 40, 0xff00ff, ptr); + draw(100, 200, 40, 40, 0xff00ff, ptr); + + drmBOUnmap(fd, &bo); +} + +void testCursor(int fd, uint32_t crtc) +{ + drmBO bo; + int ret; + ret = drmBOCreate(fd, 64 * 64 * 4, 0, 0, + DRM_BO_FLAG_READ | + DRM_BO_FLAG_WRITE | + DRM_BO_FLAG_MEM_VRAM | + DRM_BO_FLAG_NO_EVICT, + DRM_BO_HINT_DONT_FENCE, &bo); + + prettyCursor(fd, bo.handle, 0xFFFF00FF); + printf("set cursor\n"); + drmModeSetCursor(fd, crtc, bo.handle, 64, 64); + printf("move cursor 0, 0\n"); + drmModeMoveCursor(fd, crtc, 0, 0); + sleep(1); + prettyCursor(fd, bo.handle, 0xFFFF0000); + printf("move cursor 40, 40\n"); + drmModeMoveCursor(fd, crtc, 40, 40); + sleep(1); + printf("move cursor 100, 100\n"); + drmModeMoveCursor(fd, crtc, 100, 100); + sleep(1); + drmModeSetCursor(fd, crtc, 0, 0, 0); +} + +void prettyCursor(int fd, unsigned int handle, unsigned int color) +{ + drmBO bo; + unsigned int *ptr; + int i; + + drmBOReference(fd, handle, &bo); + drmBOMap(fd, &bo, DRM_BO_FLAG_READ | DRM_BO_FLAG_WRITE, 0, (void**)&ptr); + + for (i = 0; i < (64 * 64); i++) + ptr[i] = color; + + drmBOUnmap(fd, &bo); + drmBOUnreference(fd, &bo); +} diff --git a/tests/modedemo/test b/tests/modedemo/test new file mode 100755 index 00000000..f98e3708 --- /dev/null +++ b/tests/modedemo/test @@ -0,0 +1 @@ +LD_PRELOAD=../../libdrm/.libs/libdrm.so ./app diff --git a/tests/modefb/Makefile b/tests/modefb/Makefile new file mode 100644 index 00000000..467fb11a --- /dev/null +++ b/tests/modefb/Makefile @@ -0,0 +1,14 @@ + +all: app + +#CFLAGS = -g -ansi -pedantic -DPOSIX_C_SOURCE=199309L \ +# -D_POSIX_SOURCE -D_XOPEN_SOURCE -D_BSD_SOURCE -D_SVID_SOURCE \ + +app: demo.c + @gcc $(CFLAGS) -o app -Wall -I../../libdrm -I../../shared-core -L../../libdrm/.libs -ldrm demo.c + +clean: + @rm -f app + +run: app + sudo ./test diff --git a/tests/modefb/demo.c b/tests/modefb/demo.c new file mode 100644 index 00000000..ead53334 --- /dev/null +++ b/tests/modefb/demo.c @@ -0,0 +1,230 @@ +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include "linux/fb.h" +#include <sys/mman.h> +#include "sys/ioctl.h" +#include "xf86drm.h" +#include "xf86drmMode.h" + +void pretty(int fd); +void setMode(struct fb_var_screeninfo *var); +void pan(int fd, struct fb_var_screeninfo *var, int x, int y); +void cursor(int fd, int drmfd); +void prettyColors(int fd); +void prettyCursor(int fd, unsigned int handle, unsigned int color); + +struct fb_var_screeninfo var; +struct fb_fix_screeninfo fix; + +int main(int argc, char **argv) +{ + char name[100]; + int i,d; + int fd; + int drmfd = drmOpen("i915", NULL); + + if (drmfd < 0) { + printf("drmOpenControl failed\n"); + return 1; + } + + /* try four devices */ + for (d = 0; d < 4; d++) { + snprintf(name, 100, "/dev/fb%d", d); + fd = open(name, O_RDWR); + + if (fd == -1) { + printf("open %s : %s\n", name, strerror(errno)); + return 1; + } + + memset(&var, 0, sizeof(struct fb_var_screeninfo)); + memset(&fix, 0, sizeof(struct fb_fix_screeninfo)); + + if (ioctl(fd, FBIOGET_VSCREENINFO, &var)) + printf("var %s\n", strerror(errno)); + if (ioctl(fd, FBIOGET_FSCREENINFO, &fix)) + printf("fix %s\n", strerror(errno)); + + setMode(&var); + + if (ioctl(fd, FBIOPUT_VSCREENINFO, &var)) + printf("var %s\n", strerror(errno)); + + for (i = 0; i < 1; i++) { + prettyColors(fd); + } + sleep(1); + + printf("pan: 0, 0\n"); + pan(fd, &var, 0, 0); + sleep(2); + printf("pan: 100, 0\n"); + pan(fd, &var, 100, 0); + sleep(2); + printf("pan: 0, 100\n"); + pan(fd, &var, 0, 100); + sleep(2); + printf("pan: 100, 100\n"); + pan(fd, &var, 100, 100); + sleep(2); + printf("pan: 0, 0\n"); + pan(fd, &var, 0, 0); + sleep(2); + + printf("cursor (may show up on wrong CRTC - fixme)\n"); + cursor(fd, drmfd); + + close(fd); + } + return 0; +} + +void pan(int fd, struct fb_var_screeninfo *var, int x, int y) +{ + var->xoffset = x; + var->yoffset = y; + + if (ioctl(fd, FBIOPAN_DISPLAY, var)) + printf("pan error: %s\n", strerror(errno)); +} + +void draw(unsigned int x, unsigned int y, unsigned int w, unsigned int h, unsigned int v, unsigned int *ptr) +{ + int i, j; + + for (i = x; i < x + w; i++) + for(j = y; j < y + h; j++) + ptr[(i * var.xres_virtual) + j] = v; +} + +void prettyColors(int fd) +{ + unsigned int *ptr; + int i; + int size = var.xres * var.yres * var.bits_per_pixel; + + ptr = (unsigned int *)mmap(NULL, size, + PROT_READ|PROT_WRITE, MAP_SHARED, fd, + 0); + if (ptr < 0) { + printf("FAILED MMAP %d\n",errno); + exit(1); + } + + memset(ptr, 0xFF, size); + + for (i = 0; i < 8; i++) + draw(i * 40, i * 40, 40, 40, 0, ptr); + + + draw(200, 100, 40, 40, 0xff00ff, ptr); + draw(100, 200, 40, 40, 0xff00ff, ptr); + + munmap(ptr, size); +} + +/* + * Cursor support removed from the fb kernel interface + * using drm instead. + */ +void cursor(int fd, int drmfd) +{ + drmModeResPtr res = drmModeGetResources(drmfd); + uint32_t crtc = res->crtcs[1]; /* select crtc here */ + drmBO bo; + int ret; + ret = drmBOCreate(drmfd, 64 * 64 * 4, 0, 0, + DRM_BO_FLAG_READ | + DRM_BO_FLAG_WRITE | + DRM_BO_FLAG_MEM_VRAM | + DRM_BO_FLAG_NO_EVICT, + DRM_BO_HINT_DONT_FENCE, &bo); + + if (ret) { + printf("failed to create buffer: %s\n", strerror(ret)); + return; + } + + prettyCursor(drmfd, bo.handle, 0xFFFF00FF); + drmModeSetCursor(drmfd, crtc, bo.handle, 64, 64); + drmModeMoveCursor(drmfd, crtc, 0, 0); + sleep(1); + prettyCursor(drmfd, bo.handle, 0xFFFF0000); + drmModeMoveCursor(drmfd, crtc, 40, 40); + sleep(1); + drmModeMoveCursor(drmfd, crtc, 100, 100); + sleep(1); + drmModeSetCursor(drmfd, crtc, 0, 0, 0); + drmBOUnreference(drmfd, &bo); +} + +void prettyCursor(int drmfd, unsigned int handle, unsigned int color) +{ + drmBO bo; + unsigned int *ptr; + int i; + + drmBOReference(drmfd, handle, &bo); + drmBOMap(drmfd, &bo, DRM_BO_FLAG_READ | DRM_BO_FLAG_WRITE, 0, (void**)&ptr); + + for (i = 0; i < (64 * 64); i++) + ptr[i] = color; + + drmBOUnmap(drmfd, &bo); + drmBOUnreference(drmfd, &bo); +} + +struct drm_mode +{ + 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; + int vrefresh; + int flags; +}; + +struct drm_mode 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, +}; + +void setMode(struct fb_var_screeninfo *var) { + var->activate = FB_ACTIVATE_NOW; + var->xres = mode.hdisplay; + var->right_margin = mode.hsync_start - mode.hdisplay; + var->hsync_len = mode.hsync_end - mode.hsync_start; + var->left_margin = mode.htotal - mode.hsync_end; + var->yres = mode.vdisplay; + var->lower_margin = mode.vsync_start - mode.vdisplay; + var->vsync_len = mode.vsync_end - mode.vsync_start; + var->upper_margin = mode.vtotal - mode.vsync_end; + var->pixclock = 10000000 / mode.htotal * 1000 / mode.vtotal * 100; + /* avoid overflow */ + var->pixclock = var->pixclock * 1000 / mode.vrefresh; + var->bits_per_pixel = 32; +} diff --git a/tests/modefb/test b/tests/modefb/test new file mode 100755 index 00000000..f98e3708 --- /dev/null +++ b/tests/modefb/test @@ -0,0 +1 @@ +LD_PRELOAD=../../libdrm/.libs/libdrm.so ./app diff --git a/tests/modehotplug/Makefile b/tests/modehotplug/Makefile new file mode 100644 index 00000000..467fb11a --- /dev/null +++ b/tests/modehotplug/Makefile @@ -0,0 +1,14 @@ + +all: app + +#CFLAGS = -g -ansi -pedantic -DPOSIX_C_SOURCE=199309L \ +# -D_POSIX_SOURCE -D_XOPEN_SOURCE -D_BSD_SOURCE -D_SVID_SOURCE \ + +app: demo.c + @gcc $(CFLAGS) -o app -Wall -I../../libdrm -I../../shared-core -L../../libdrm/.libs -ldrm demo.c + +clean: + @rm -f app + +run: app + sudo ./test diff --git a/tests/modehotplug/demo.c b/tests/modehotplug/demo.c new file mode 100644 index 00000000..4ef2e386 --- /dev/null +++ b/tests/modehotplug/demo.c @@ -0,0 +1,157 @@ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <unistd.h> +#include <string.h> + +#include "xf86drm.h" +#include "xf86drmMode.h" + +/* structs for the demo_driver */ + +#define DEMO_MAX_OUTPUTS 8 + +struct demo_driver +{ + /* drm stuff */ + int fd; + drmModeResPtr res; + uint32_t counter; + + drmModeOutputPtr outputs[DEMO_MAX_OUTPUTS]; +}; + +struct demo_driver* demoCreateDriver(void); +void demoUpdateRes(struct demo_driver *driver); + +void demoPopulateOutputs(struct demo_driver *driver); +void demoHotplug(struct demo_driver *driver); + +const char* demoGetStatus(drmModeOutputPtr out); + +int main(int argc, char **argv) +{ + struct demo_driver *driver; + uint32_t temp; + int i; + + printf("starting demo\n"); + + driver = demoCreateDriver(); + + if (!driver) { + printf("failed to create driver\n"); + return 1; + } + + driver->counter = drmModeGetHotplug(driver->fd); + demoPopulateOutputs(driver); + while (driver->counter != (temp = drmModeGetHotplug(driver->fd))) { + demoPopulateOutputs(driver); + driver->counter = temp; + } + + for (i = 0; i < driver->res->count_outputs && i < DEMO_MAX_OUTPUTS; i++) { + printf("Output %u is %s\n", + driver->outputs[i]->output_id, + demoGetStatus(driver->outputs[i])); + } + + while(1) { + usleep(100000); + temp = drmModeGetHotplug(driver->fd); + if (temp == driver->counter) + continue; + + demoHotplug(driver); + driver->counter = temp; + } + + return 0; +} + +const char* demoGetStatus(drmModeOutputPtr output) +{ + switch (output->connection) { + case DRM_MODE_CONNECTED: + return "connected"; + case DRM_MODE_DISCONNECTED: + return "disconnected"; + default: + return "unknown"; + } +} + +void demoHotplug(struct demo_driver *driver) +{ + drmModeResPtr res = driver->res; + int i; + drmModeOutputPtr temp, current; + + for (i = 0; i < res->count_outputs && i < DEMO_MAX_OUTPUTS; i++) { + temp = drmModeGetOutput(driver->fd, res->outputs[i]); + current = driver->outputs[i]; + + if (temp->connection != current->connection) { + printf("Output %u became %s was %s\n", + temp->output_id, + demoGetStatus(temp), + demoGetStatus(current)); + } + + drmModeFreeOutput(current); + driver->outputs[i] = temp; + } +} + +void demoPopulateOutputs(struct demo_driver *driver) +{ + drmModeResPtr res = driver->res; + int i; + + for (i = 0; i < res->count_outputs && i < DEMO_MAX_OUTPUTS; i++) { + drmModeFreeOutput(driver->outputs[i]); + driver->outputs[i] = drmModeGetOutput(driver->fd, res->outputs[i]); + } +} + +struct demo_driver* demoCreateDriver(void) +{ + struct demo_driver* driver = malloc(sizeof(struct demo_driver)); + + memset(driver, 0, sizeof(struct demo_driver)); + + driver->fd = drmOpen("i915", NULL); + + if (driver->fd < 0) { + printf("Failed to open the card fb\n"); + goto err_driver; + } + + demoUpdateRes(driver); + if (!driver->res) { + printf("could not retrive resources\n"); + goto err_res; + } + + return driver; + +err_res: + drmClose(driver->fd); +err_driver: + free(driver); + return NULL; +} + +void demoUpdateRes(struct demo_driver *driver) +{ + if (driver->res) + drmModeFreeResources(driver->res); + + driver->res = drmModeGetResources(driver->fd); + + if (!driver->res) + printf("failed to get resources from kernel\n"); +} diff --git a/tests/modehotplug/test b/tests/modehotplug/test new file mode 100755 index 00000000..f98e3708 --- /dev/null +++ b/tests/modehotplug/test @@ -0,0 +1 @@ +LD_PRELOAD=../../libdrm/.libs/libdrm.so ./app diff --git a/tests/modeprint/Makefile b/tests/modeprint/Makefile new file mode 100644 index 00000000..7a9c3c24 --- /dev/null +++ b/tests/modeprint/Makefile @@ -0,0 +1,14 @@ + +all: app + +#CFLAGS = -g -ansi -pedantic -DPOSIX_C_SOURCE=199309L \ +# -D_POSIX_SOURCE -D_XOPEN_SOURCE -D_BSD_SOURCE -D_SVID_SOURCE \ + +app: modetest.c + @gcc $(CFLAGS) -o app -Wall -I../../libdrm -I../../shared-core -L../../libdrm/.libs -ldrm modetest.c + +clean: + @rm -f app + +run: app + @sudo ./test diff --git a/tests/modeprint/modetest.c b/tests/modeprint/modetest.c new file mode 100644 index 00000000..cefa5262 --- /dev/null +++ b/tests/modeprint/modetest.c @@ -0,0 +1,371 @@ + +#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 connectors; +int full_props; +int edid; +int modes; +int full_modes; +int encoders; +int crtcs; +int fbs; +char *module_name; + +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 (full_modes) { + 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); + } + return 0; +} + +int printProperty(int fd, drmModeResPtr res, drmModePropertyPtr props, uint64_t value) +{ + const unsigned char *name = NULL; + int j; + + printf("Property: %s\n", props->name); + printf("\tid : %i\n", props->prop_id); + printf("\tflags : %i\n", props->flags); + printf("\tcount_values : %d\n", props->count_values); + + + if (props->count_values) { + printf("\tvalues :"); + for (j = 0; j < props->count_values; j++) + printf(" %lld", props->values[j]); + printf("\n"); + } + + + printf("\tcount_enums : %d\n", props->count_enums); + + if (props->flags & DRM_MODE_PROP_BLOB) { + drmModePropertyBlobPtr blob; + + blob = drmModeGetPropertyBlob(fd, value); + if (blob) { + printf("blob is %d length, %08X\n", blob->length, *(uint32_t *)blob->data); + drmModeFreePropertyBlob(blob); + } else { + printf("error getting blob %lld\n", value); + } + + } else { + if (!strncmp(props->name, "DPMS", 4)) + ; + + for (j = 0; j < props->count_enums; j++) { + printf("\t\t%lld = %s\n", props->enums[j].value, props->enums[j].name); + if (props->enums[j].value == value) + name = props->enums[j].name; + } + + if (props->count_enums && name) { + printf("\tcon_value : %s\n", name); + } else { + printf("\tcon_value : %lld\n", value); + } + } + + return 0; +} + +int printConnector(int fd, drmModeResPtr res, drmModeConnectorPtr connector, uint32_t id) +{ + int i = 0; + struct drm_mode_modeinfo *mode = NULL; + drmModePropertyPtr props; + + printf("Connector: %d-%d\n", connector->connector_type, connector->connector_type_id); + printf("\tid : %i\n", id); + printf("\tencoder id : %i\n", connector->encoder_id); + printf("\tconn : %s\n", getConnectionText(connector->connection)); + printf("\tsize : %ix%i (mm)\n", connector->mmWidth, connector->mmHeight); + printf("\tcount_modes : %i\n", connector->count_modes); + printf("\tcount_props : %i\n", connector->count_props); + if (connector->count_props) { + printf("\tprops :"); + for (i = 0; i < connector->count_props; i++) + printf(" %i", connector->props[i]); + printf("\n"); + } + + printf("\tcount_encoders : %i\n", connector->count_encoders); + if (connector->count_encoders) { + printf("\tencoders :"); + for (i = 0; i < connector->count_encoders; i++) + printf(" %i", connector->encoders[i]); + printf("\n"); + } + + if (modes) { + for (i = 0; i < connector->count_modes; i++) { + mode = &connector->modes[i]; + printMode(mode); + } + } + + if (full_props) { + for (i = 0; i < connector->count_props; i++) { + props = drmModeGetProperty(fd, connector->props[i]); + if (props) { + printProperty(fd, res, props, connector->prop_values[i]); + drmModeFreeProperty(props); + } + } + } + + return 0; +} + +int printEncoder(int fd, drmModeResPtr res, drmModeEncoderPtr encoder, uint32_t id) +{ + printf("Encoder\n"); + printf("\tid :%i\n", id); + printf("\tcrtc_id :%d\n", encoder->crtc_id); + printf("\ttype :%d\n", encoder->encoder_type); + printf("\tpossible_crtcs :%d\n", encoder->possible_crtcs); + printf("\tpossible_clones :%d\n", encoder->possible_clones); + 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("\tgamma size : %d\n", crtc->gamma_size); + + 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; + drmModeFBPtr fb; + drmModeCrtcPtr crtc; + drmModeEncoderPtr encoder; + drmModeConnectorPtr connector; + + printf("Resources\n\n"); + + printf("count_connectors : %i\n", res->count_connectors); + printf("count_encoders : %i\n", res->count_encoders); + printf("count_crtcs : %i\n", res->count_crtcs); + printf("count_fbs : %i\n", res->count_fbs); + + printf("\n"); + + if (connectors) { + for (i = 0; i < res->count_connectors; i++) { + connector = drmModeGetConnector(fd, res->connectors[i]); + + if (!connector) + printf("Could not get connector %i\n", res->connectors[i]); + else { + printConnector(fd, res, connector, res->connectors[i]); + drmModeFreeConnector(connector); + } + } + printf("\n"); + } + + + if (encoders) { + for (i = 0; i < res->count_encoders; i++) { + encoder = drmModeGetEncoder(fd, res->encoders[i]); + + if (!encoder) + printf("Could not get encoder %i\n", res->encoders[i]); + else { + printEncoder(fd, res, encoder, res->encoders[i]); + drmModeFreeEncoder(encoder); + } + } + printf("\n"); + } + + if (crtcs) { + for (i = 0; i < res->count_crtcs; i++) { + crtc = drmModeGetCrtc(fd, res->crtcs[i]); + + if (!crtc) + printf("Could not get crtc %i\n", res->crtcs[i]); + else { + printCrtc(fd, res, crtc, res->crtcs[i]); + drmModeFreeCrtc(crtc); + } + } + printf("\n"); + } + + if (fbs) { + 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; +} + +void args(int argc, char **argv) +{ + int i; + + fbs = 0; + edid = 0; + crtcs = 0; + modes = 0; + encoders = 0; + full_modes = 0; + full_props = 0; + connectors = 0; + + module_name = argv[1]; + + for (i = 2; i < argc; i++) { + if (strcmp(argv[i], "-fb") == 0) { + fbs = 1; + } else if (strcmp(argv[i], "-crtcs") == 0) { + crtcs = 1; + } else if (strcmp(argv[i], "-cons") == 0) { + connectors = 1; + modes = 1; + } else if (strcmp(argv[i], "-modes") == 0) { + connectors = 1; + modes = 1; + } else if (strcmp(argv[i], "-full") == 0) { + connectors = 1; + modes = 1; + full_modes = 1; + } else if (strcmp(argv[i], "-props") == 0) { + connectors = 1; + full_props = 1; + } else if (strcmp(argv[i], "-edids") == 0) { + connectors = 1; + edid = 1; + } else if (strcmp(argv[i], "-encoders") == 0) { + encoders = 1; + } else if (strcmp(argv[i], "-v") == 0) { + fbs = 1; + edid = 1; + crtcs = 1; + modes = 1; + encoders = 1; + full_modes = 1; + full_props = 1; + connectors = 1; + } + } + + if (argc == 2) { + fbs = 1; + edid = 1; + crtcs = 1; + modes = 1; + encoders = 1; + full_modes = 0; + full_props = 0; + connectors = 1; + } +} + +int main(int argc, char **argv) +{ + int fd; + drmModeResPtr res; + + if (argc == 1) { + printf("Please add modulename as first argument\n"); + return 1; + } + + args(argc, argv); + + printf("Starting test\n"); + + fd = drmOpen(module_name, NULL); + + if (fd < 0) { + printf("Failed to open the card fd (%d)\n",fd); + return 1; + } + + res = drmModeGetResources(fd); + if (res == 0) { + printf("Failed to get resources from card\n"); + drmClose(fd); + return 1; + } + + printRes(fd, res); + + drmModeFreeResources(res); + + printf("Ok\n"); + + return 0; +} diff --git a/tests/modeprint/test b/tests/modeprint/test new file mode 100755 index 00000000..bd1952cc --- /dev/null +++ b/tests/modeprint/test @@ -0,0 +1 @@ +LD_PRELOAD=../../libdrm/.libs/libdrm.so ./app $@ diff --git a/tests/modetest/Makefile b/tests/modetest/Makefile new file mode 100644 index 00000000..8583ae82 --- /dev/null +++ b/tests/modetest/Makefile @@ -0,0 +1,14 @@ + +all: app + +#CFLAGS = -g -ansi -pedantic -DPOSIX_C_SOURCE=199309L \ +# -D_POSIX_SOURCE -D_XOPEN_SOURCE -D_BSD_SOURCE -D_SVID_SOURCE \ + +app: modetest.c + gcc $(CFLAGS) -o app -Wall -I../../libdrm -I../../libdrm/intel -I../../shared-core -L../../libdrm/.libs -L../../libdrm/intel/.libs -ldrm -ldrm_intel modetest.c + +clean: + @rm -f app + +run: app + sudo ./test diff --git a/tests/modetest/modetest.c b/tests/modetest/modetest.c new file mode 100644 index 00000000..66c7f9cd --- /dev/null +++ b/tests/modetest/modetest.c @@ -0,0 +1,410 @@ +/* + * DRM based mode setting test program + * Copyright 2008 Tungsten Graphics + * Jakob Bornecrantz <jakob@tungstengraphics.com> + * Copyright 2008 Intel Corporation + * Jesse Barnes <jesse.barnes@intel.com> + */ +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include "xf86drm.h" +#include "xf86drmMode.h" +#include "intel_bufmgr.h" + +drmModeRes *resources; +int fd, modes; + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +struct type_name { + int type; + char *name; +}; + +#define type_name_fn(res) \ +char * res##_str(int type) { \ + int i; \ + for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \ + if (res##_names[i].type == type) \ + return res##_names[i].name; \ + } \ + return "(invalid)"; \ +} + +struct type_name encoder_type_names[] = { + { DRM_MODE_ENCODER_NONE, "none" }, + { DRM_MODE_ENCODER_DAC, "DAC" }, + { DRM_MODE_ENCODER_TMDS, "TMDS" }, + { DRM_MODE_ENCODER_LVDS, "LVDS" }, + { DRM_MODE_ENCODER_TVDAC, "TVDAC" }, +}; + +type_name_fn(encoder_type) + +struct type_name connector_status_names[] = { + { DRM_MODE_CONNECTED, "connected" }, + { DRM_MODE_DISCONNECTED, "disconnected" }, + { DRM_MODE_UNKNOWNCONNECTION, "unknown" }, +}; + +type_name_fn(connector_status) + +struct type_name connector_type_names[] = { + { DRM_MODE_CONNECTOR_Unknown, "unknown" }, + { DRM_MODE_CONNECTOR_VGA, "VGA" }, + { DRM_MODE_CONNECTOR_DVII, "DVI-I" }, + { DRM_MODE_CONNECTOR_DVID, "DVI-D" }, + { DRM_MODE_CONNECTOR_DVIA, "DVI-A" }, + { DRM_MODE_CONNECTOR_Composite, "composite" }, + { DRM_MODE_CONNECTOR_SVIDEO, "s-video" }, + { DRM_MODE_CONNECTOR_LVDS, "LVDS" }, + { DRM_MODE_CONNECTOR_Component, "component" }, + { DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN" }, + { DRM_MODE_CONNECTOR_DisplayPort, "displayport" }, + { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" }, + { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" }, +}; + +type_name_fn(connector_type) + +void dump_encoders(void) +{ + drmModeEncoder *encoder; + int i; + + printf("Encoders:\n"); + printf("id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n"); + for (i = 0; i < resources->count_encoders; i++) { + encoder = drmModeGetEncoder(fd, resources->encoders[i]); + + if (!encoder) { + fprintf(stderr, "could not get encoder %i: %s\n", + resources->encoders[i], strerror(errno)); + continue; + } + printf("%d\t%d\t%s\t0x%08x\t0x%08x\n", + encoder->encoder_id, + encoder->crtc_id, + encoder_type_str(encoder->encoder_type), + encoder->possible_crtcs, + encoder->possible_clones); + drmModeFreeEncoder(encoder); + } +} + +void dump_connectors(void) +{ + drmModeConnector *connector; + int i, j; + + printf("Connectors:\n"); + printf("id\tencoder\tstatus\t\ttype\tsize (mm)\tmodes\n"); + for (i = 0; i < resources->count_connectors; i++) { + connector = drmModeGetConnector(fd, resources->connectors[i]); + + if (!connector) { + fprintf(stderr, "could not get connector %i: %s\n", + resources->connectors[i], strerror(errno)); + continue; + } + + printf("%d\t%d\t%s\t%s\t%dx%d\t\t%d\n", + connector->connector_id, + connector->encoder_id, + connector_status_str(connector->connection), + connector_type_str(connector->connector_type), + connector->mmWidth, connector->mmHeight, + connector->count_modes); + + if (!connector->count_modes) + continue; + + printf(" modes:\n"); + printf(" name refresh (Hz) hdisp hss hse htot vdisp " + "vss vse vtot)\n"); + for (j = 0; j < connector->count_modes; j++) { + struct drm_mode_modeinfo *mode; + + mode = &connector->modes[j]; + printf(" %s %.02f %d %d %d %d %d %d %d %d\n", + mode->name, + (float)mode->vrefresh / 1000, + mode->hdisplay, + mode->hsync_start, + mode->hsync_end, + mode->htotal, + mode->vdisplay, + mode->vsync_start, + mode->vsync_end, + mode->vtotal); + } + drmModeFreeConnector(connector); + } +} + +void dump_crtcs(void) +{ + drmModeCrtc *crtc; + int i; + + for (i = 0; i < resources->count_crtcs; i++) { + crtc = drmModeGetCrtc(fd, resources->crtcs[i]); + + if (!crtc) { + fprintf(stderr, "could not get crtc %i: %s\n", + resources->crtcs[i], strerror(errno)); + continue; + } + drmModeFreeCrtc(crtc); + } +} + +void dump_framebuffers(void) +{ + drmModeFB *fb; + int i; + + for (i = 0; i < resources->count_fbs; i++) { + fb = drmModeGetFB(fd, resources->fbs[i]); + + if (!fb) { + fprintf(stderr, "could not get fb %i: %s\n", + resources->fbs[i], strerror(errno)); + continue; + } + drmModeFreeFB(fb); + } +} + +void set_mode(int connector_id, char *mode_str) +{ + drmModeConnector *connector; + drmModeEncoder *encoder = NULL; + struct drm_mode_modeinfo *mode = NULL; + drm_intel_bufmgr *bufmgr; + drm_intel_bo *bo; + unsigned int fb_id, *fb_ptr; + int i, j, size, ret, width, height; + + /* First, find the connector & mode */ + for (i = 0; i < resources->count_connectors; i++) { + connector = drmModeGetConnector(fd, resources->connectors[i]); + + if (!connector) { + fprintf(stderr, "could not get connector %i: %s\n", + resources->connectors[i], strerror(errno)); + drmModeFreeConnector(connector); + continue; + } + + if (!connector->count_modes) { + drmModeFreeConnector(connector); + continue; + } + + if (connector->connector_id != connector_id) { + drmModeFreeConnector(connector); + continue; + } + + for (j = 0; j < connector->count_modes; j++) { + mode = &connector->modes[j]; + if (!strcmp(mode->name, mode_str)) + break; + } + + /* Found it, break out */ + if (mode) + break; + + drmModeFreeConnector(connector); + } + + if (!mode) { + fprintf(stderr, "failed to find mode \"%s\"\n", mode_str); + return; + } + + width = mode->hdisplay; + height = mode->vdisplay; + + /* Now get the encoder */ + for (i = 0; i < resources->count_encoders; i++) { + encoder = drmModeGetEncoder(fd, resources->encoders[i]); + + if (!encoder) { + fprintf(stderr, "could not get encoder %i: %s\n", + resources->encoders[i], strerror(errno)); + drmModeFreeEncoder(encoder); + continue; + } + + if (encoder->encoder_id == connector->encoder_id) + break; + + drmModeFreeEncoder(encoder); + } + + bufmgr = drm_intel_bufmgr_gem_init(fd, 2<<20); + if (!bufmgr) { + fprintf(stderr, "failed to init bufmgr: %s\n", strerror(errno)); + return; + } + + /* Mode size at 32 bpp */ + size = width * height * 4; + + bo = drm_intel_bo_alloc(bufmgr, "frontbuffer", size, 4096); + if (!bo) { + fprintf(stderr, "failed to alloc buffer: %s\n", + strerror(errno)); + return; + } + + ret = drm_intel_bo_pin(bo, 4096); + if (ret) { + fprintf(stderr, "failed to pin buffer: %s\n", strerror(errno)); + return; + } + + ret = drm_intel_gem_bo_map_gtt(bo); + if (ret) { + fprintf(stderr, "failed to GTT map buffer: %s\n", + strerror(errno)); + return; + } + + fb_ptr = bo->virtual; + + /* paint the buffer blue */ + for (i = 0; i < width * height; i++) + fb_ptr[i] = 0xff; + + ret = drmModeAddFB(fd, width, height, 32, 32, width * 4, bo->handle, + &fb_id); + if (ret) { + fprintf(stderr, "failed to add fb: %s\n", strerror(errno)); + return; + } + + ret = drmModeSetCrtc(fd, encoder->crtc_id, fb_id, 0, 0, + &connector->connector_id, 1, mode); + if (ret) { + fprintf(stderr, "failed to set mode: %s\n", strerror(errno)); + return; + } +} + +extern char *optarg; +extern int optind, opterr, optopt; +static char optstr[] = "ecpmfs:"; + +void usage(char *name) +{ + fprintf(stderr, "usage: %s [-ecpmf]\n", name); + fprintf(stderr, "\t-e\tlist encoders\n"); + fprintf(stderr, "\t-c\tlist connectors\n"); + fprintf(stderr, "\t-p\tlist CRTCs (pipes)\n"); + fprintf(stderr, "\t-m\tlist modes\n"); + fprintf(stderr, "\t-f\tlist framebuffers\n"); + fprintf(stderr, "\t-s <connector_id>:<mode>\tset a mode\n"); + fprintf(stderr, "\n\tDefault is to dump all info.\n"); + exit(0); +} + +#define dump_resource(res) if (res) dump_##res() + +int main(int argc, char **argv) +{ + int c; + int encoders = 0, connectors = 0, crtcs = 0, framebuffers = 0; + char *modules[] = { "i915", "radeon" }; + char *modeset = NULL, *mode, *connector; + int i, connector_id; + + opterr = 0; + while ((c = getopt(argc, argv, optstr)) != -1) { + switch (c) { + case 'e': + encoders = 1; + break; + case 'c': + connectors = 1; + break; + case 'p': + crtcs = 1; + break; + case 'm': + modes = 1; + break; + case 'f': + framebuffers = 1; + break; + case 's': + modeset = strdup(optarg); + break; + default: + usage(argv[0]); + break; + } + } + + if (argc == 1) + encoders = connectors = crtcs = modes = framebuffers = 1; + + for (i = 0; i < ARRAY_SIZE(modules); i++) { + printf("trying to load module %s...", modules[i]); + fd = drmOpen(modules[i], NULL); + if (fd < 0) { + printf("failed.\n"); + } else { + printf("success.\n"); + break; + } + } + + if (i == ARRAY_SIZE(modules)) { + fprintf(stderr, "failed to load any modules, aborting.\n"); + return -1; + } + + resources = drmModeGetResources(fd); + if (!resources) { + fprintf(stderr, "drmModeGetResources failed: %s\n", + strerror(errno)); + drmClose(fd); + return 1; + } + + dump_resource(encoders); + dump_resource(connectors); + dump_resource(crtcs); + dump_resource(framebuffers); + + if (modeset) { + connector = strtok(modeset, ":"); + if (!connector) + usage(argv[0]); + connector_id = atoi(connector); + + mode = strtok(NULL, ":"); + if (!mode) + usage(argv[0]); + printf("setting connector %d to mode %s\n", connector_id, + mode); + set_mode(connector_id, mode); + sleep(3); + } + + sleep(3); + + drmModeFreeResources(resources); + + return 0; +} diff --git a/tests/modetest/test b/tests/modetest/test new file mode 100755 index 00000000..5bb552ef --- /dev/null +++ b/tests/modetest/test @@ -0,0 +1,2 @@ +export LD_LIBRARY_PATH=../../libdrm/.libs:../../libdrm/intel/.libs +LD_PRELOAD=../../libdrm/.libs/libdrm.so ./app $@ diff --git a/tests/radeon_gem_basic.c b/tests/radeon_gem_basic.c new file mode 100644 index 00000000..d8231879 --- /dev/null +++ b/tests/radeon_gem_basic.c @@ -0,0 +1,114 @@ +/* + * Copyright © 2008 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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <fcntl.h> +#include <inttypes.h> +#include <errno.h> +#include <sys/stat.h> +#include "drm.h" +#include "radeon_drm.h" + +static void +test_bad_close(int fd) +{ + struct drm_gem_close close; + int ret; + + printf("Testing error return on bad close ioctl.\n"); + + close.handle = 0x10101010; + ret = ioctl(fd, DRM_IOCTL_GEM_CLOSE, &close); + + assert(ret == -1 && errno == EINVAL); +} + +static void +test_create_close(int fd) +{ + struct drm_radeon_gem_create create; + struct drm_gem_close close; + int ret; + + printf("Testing creating and closing an object.\n"); + + memset(&create, 0, sizeof(create)); + create.size = 16 * 1024; + ret = ioctl(fd, DRM_IOCTL_RADEON_GEM_CREATE, &create); + assert(ret == 0); + + close.handle = create.handle; + ret = ioctl(fd, DRM_IOCTL_GEM_CLOSE, &close); +} + +static void +test_create_fd_close(int fd) +{ + struct drm_radeon_gem_create create; + int ret; + + printf("Testing closing with an object allocated.\n"); + + memset(&create, 0, sizeof(create)); + create.size = 16 * 1024; + ret = ioctl(fd, DRM_IOCTL_RADEON_GEM_CREATE, &create); + assert(ret == 0); + + close(fd); +} + +int test_gem_info(int fd) +{ + struct drm_radeon_gem_info info; + int ret; + + ret = ioctl(fd, DRM_IOCTL_RADEON_GEM_INFO, &info); + assert(ret == 0); + + fprintf(stderr,"%lld %lld %lld %lld %lld\n", + info.gtt_start, info.gtt_size, + info.vram_start, info.vram_size, + info.vram_visible); + +} + +int main(int argc, char **argv) +{ + int fd; + + fd = drm_open_any(); + + test_gem_info(fd); + test_bad_close(fd); + test_create_close(fd); + test_create_fd_close(fd); + + return 0; +} diff --git a/tests/radeon_gem_mmap.c b/tests/radeon_gem_mmap.c new file mode 100644 index 00000000..aa7b0196 --- /dev/null +++ b/tests/radeon_gem_mmap.c @@ -0,0 +1,132 @@ +/* + * Copyright © 2008 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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <fcntl.h> +#include <inttypes.h> +#include <errno.h> +#include <sys/stat.h> +#include "drm.h" +#include "radeon_drm.h" + +#define OBJECT_SIZE 16384 + +int do_read(int fd, int handle, void *buf, int offset, int size) +{ + struct drm_radeon_gem_pread read; + + /* Ensure that we don't have any convenient data in buf in case + * we fail. + */ + memset(buf, 0xd0, size); + + memset(&read, 0, sizeof(read)); + read.handle = handle; + read.data_ptr = (uintptr_t)buf; + read.size = size; + read.offset = offset; + + return ioctl(fd, DRM_IOCTL_RADEON_GEM_PREAD, &read); +} + +int do_write(int fd, int handle, void *buf, int offset, int size) +{ + struct drm_radeon_gem_pwrite write; + + memset(&write, 0, sizeof(write)); + write.handle = handle; + write.data_ptr = (uintptr_t)buf; + write.size = size; + write.offset = offset; + + return ioctl(fd, DRM_IOCTL_RADEON_GEM_PWRITE, &write); +} + +int main(int argc, char **argv) +{ + int fd; + struct drm_radeon_gem_create create; + struct drm_radeon_gem_mmap mmap; + struct drm_gem_close unref; + uint8_t expected[OBJECT_SIZE]; + uint8_t buf[OBJECT_SIZE]; + uint8_t *addr; + int ret; + int handle; + + fd = drm_open_any(); + + memset(&mmap, 0, sizeof(mmap)); + mmap.handle = 0x10101010; + mmap.offset = 0; + mmap.size = 4096; + printf("Testing mmaping of bad object.\n"); + ret = ioctl(fd, DRM_IOCTL_RADEON_GEM_MMAP, &mmap); + assert(ret == -1 && errno == EINVAL); + + memset(&create, 0, sizeof(create)); + create.size = OBJECT_SIZE; + ret = ioctl(fd, DRM_IOCTL_RADEON_GEM_CREATE, &create); + assert(ret == 0); + handle = create.handle; + + printf("Testing mmaping of newly created object.\n"); + mmap.handle = handle; + mmap.offset = 0; + mmap.size = OBJECT_SIZE; + ret = ioctl(fd, DRM_IOCTL_RADEON_GEM_MMAP, &mmap); + assert(ret == 0); + addr = (uint8_t *)(uintptr_t)mmap.addr_ptr; + + printf("Testing contents of newly created object.\n"); + memset(expected, 0, sizeof(expected)); + assert(memcmp(addr, expected, sizeof(expected)) == 0); + + printf("Testing coherency of writes and mmap reads.\n"); + memset(buf, 0, sizeof(buf)); + memset(buf + 1024, 0x01, 1024); + memset(expected + 1024, 0x01, 1024); + ret = do_write(fd, handle, buf, 0, OBJECT_SIZE); + assert(ret == 0); + assert(memcmp(buf, addr, sizeof(buf)) == 0); + + printf("Testing that mapping stays after close\n"); + unref.handle = handle; + ret = ioctl(fd, DRM_IOCTL_GEM_CLOSE, &unref); + assert(ret == 0); + assert(memcmp(buf, addr, sizeof(buf)) == 0); + + printf("Testing unmapping\n"); + munmap(addr, OBJECT_SIZE); + + close(fd); + + return 0; +} |