2007-05-17 21:24:04

by Jesse Barnes

[permalink] [raw]
Subject: [RFC] enhancing the kernel's graphics subsystem

Patches at http://www.kernel.org/pub/linux/kernel/people/jbarnes/patches
drm-modesetting-core.patch
drm-modesetting-i915.patch
console-unregister.patch
(Sorry the first two are slightly too big for lkml; they're against the
DRM tree at git://git.freedesktop.org/git/mesa/drm.)

In collaboration with the FB guys, we've been working on enhancing the
kernel's graphics subsystem in an attempt to bring some sanity to the
Linux graphics world and avoid the situation we have now where several
kernel and userspace drivers compete for control of graphics devices.

In the interest of getting some early feedback, I thought I'd post a
description of how we've structured things so far, along with some of
the early code, to get some feedback on the direction.

Why in the kernel?

There are several reasons to pull modesetting and proper multihead
support into the kernel:
- suspend/resume
- debugging (e.g. panic)
- non-X uses
- more reliable VT switch
Each of the above is covered in more detail below.

Suspend/resume

Currently, the kernel has to rely on an external application (X,
vbetool, etc.), or worse, ACPI, to reset video devices to the proper
state after resume. If one of these systems has trouble or crashes
during or shortly after resume, the system will become unusable, with
little indication as to why (see Debugging below). Putting code into
the kernel to perform low level modesetting (i.e. without video BIOS
support) will allow the kernel to resume to the correct mode
automatically and more quickly than would be possible otherwise.

Debugging

As mentioned above, if something goes wrong with modesetting during
resume, the user has very little indication of where things went wrong.
Likewise, if a panic or oops occurs while an application like X is
running, the user will experience a hard hang, rather than the much
more pleasant "blue penguin of death" (to be coded). With kernel
modesetting support, the kernel should be able to display panic and
oops messages directly on the console, even if a graphical application
is running, since it would have awareness of the current mode, display
depth, pitch, and other variables needed to display output. Another
possibility is multihead debugging: one display could run the user's
applications (i.e. a "normal" display) while a secondary display could
run a system level debugger, allowing the user to stop the machine,
investigate memory, step through programs, etc. (admittedly this is
somewhat far fetched).

Non-X uses

As it stands, non-X based applications wanting to use video devices have
two options: either take over the hardware themselves or use the
existing kernel fb layer. The former is obviously a tall order given
the complexity of current graphics devices, while the latter isn't
featureful enough to expose multiple outputs, perform per-device
locking so that multiple clients can share the device, etc. A kernel
based modsetting and multihead API would make developing such
applications much easier.

VT switch

Currently, the kernel relies on an external program to restore the
graphics state when a VT switch occurs. This doesn't always work, with
similar results to the suspend/resume case: an apparently hung or
unusable machine. Of course, the kernel can't unconditionally preempt
the graphics device to set a new mode, but having modesetting in the
kernel will give it a much better chance of coordinating with the DRM
command dispatch code to find a good time to set a new mode.

Interfaces

With the above patches, the kernel DRM layer manages the output devices,
available modes, and calls into the low level DRM drivers to set modes
and probe outputs devices for attached displays, much like the X
server's internal RandR 1.2 APIs. It also provides userspace with an
interface to these functions (Jakob based these APIs on the X server's
Randr extension, but there are differences).

DRM/FB cooperation

Another major factor to consider when enhancing modesetting in the
kernel is DRM and FB cooperation. Currently, FB isn't aware of DRM
drivers, and DRM is only minimally aware of FB (such that it can bind
to PCI devices even after FB drivers have already done so). As a
result, any modesetting done by either layer results in memory
allocation that may not be honored by the other side (and/or the X
server, which has its own idea of how memory is being used). To
properly address interoperability, both the FB and DRM layers need to
share a common memory manager, common suspend/resume code, and common
modesetting code. In addition, applications must use these layers in
some way to avoid conflicts (e.g. X should call into the DRM or FB
layers to do memory allocation).

What about the FB layer?

Today, the FB layer is really only well aware of a single head, and
doesn't do full EDID parsing, therefore its knowledge of available
modes is limited. On the plus side, it's able to fetch EDID data where
possible and generate modes using the VESA CVT specification.

In kernel APIs

The kernel APIs are broken up into several parts, as documented in
drm_crtc.h. There are two sets of callbacks that a driver must
implement to fully support the model, one for the driver's CRTC(s), and
one for its supported outputs.

Userland APIs

DRM_IOCTL_MODE_GETRESOURCES - get number of CRTCs, FBs
DRM_IOCTL_MODE_GETCRTC - get info about a given CRTC
DRM_IOCTL_MODE_GETOUTPUT - get info about a given output
DRM_IOCTL_MODE_SETCRTC - set CRTC parameters
DRM_IOCTL_MODE_ADDFB - add a new FB object
DRM_IOCTL_MODE_RMFB - remove an FB object
DRM_IOCTL_MODE_GETFB - get info about an FB object

Notes on the current codebase

The current codebase is still incomplete in many ways: locking needs to
be (re-)added around our various list manipulation paths, we need
better initial configuration logic, only the Intel driver has any
support (and it's still missing suspend/resume and accelerated FB
functions), we need to check modes against monitor limitations (which
come from EDID or the user), CVT and GTF based mode generation still
isn't used by the DRM modesetting code, and much more. I'm hoping that
by posting this now, we can get some ideas about what requirements
other people have for graphics on Linux so we can prioritize our work.

And of course, large chunks of this code came from X.Org's modesetting
and Intel driver code, but it should all be marked with the proper
copyrights and licenses if it wasn't written from scratch.

Comments, questions, suggestions?

Thanks,
Jesse


2007-05-17 22:32:53

by Jesse Barnes

[permalink] [raw]
Subject: [PATCH 1/3] allow console unregistration

Randy just informed me that the patch limits are bigger now, so here are the
actual patches.

This patch allows for proper console unregistration via the VT layer, and
updates the FB layer to use it. This makes debugging new console drivers
much easier, since you can properly clean them up before unloading.
Antonio already checked it out (and suggested a tweak for the fbcon side)
so I think it's on its way already via the FB tree.

Jesse

--- linux-2.6.21-rc4/drivers/video/fbmem.c 2007-03-15 17:20:01.000000000 -0700
+++ linux-2.6.21-rc4-modesetting/drivers/video/fbmem.c 2007-04-26 18:16:52.000000000 -0700
@@ -1349,6 +1349,8 @@
if (!registered_fb[i])
return -EINVAL;

+ event.info = fb_info;
+ fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event);
if (fb_info->pixmap.addr &&
(fb_info->pixmap.flags & FB_PIXMAP_DEFAULT))
kfree(fb_info->pixmap.addr);
--- linux-2.6.21-rc4/include/linux/fb.h 2007-03-15 17:20:01.000000000 -0700
+++ linux-2.6.21-rc4-modesetting/include/linux/fb.h 2007-04-26 17:33:07.000000000 -0700
@@ -525,6 +525,8 @@
#define FB_EVENT_MODE_CHANGE_ALL 0x0B
/* A software display blank change occured */
#define FB_EVENT_CONBLANK 0x0C
+/* Unbind from the console if possible */
+#define FB_EVENT_FB_UNBIND 0x0E

struct fb_event {
struct fb_info *info;
--- linux-2.6.21-rc4/include/linux/console.h 2007-03-15 17:20:01.000000000 -0700
+++ linux-2.6.21-rc4-modesetting/include/linux/console.h 2007-04-26 17:56:31.000000000 -0700
@@ -64,6 +64,7 @@
extern const struct consw newport_con; /* SGI Newport console */
extern const struct consw prom_con; /* SPARC PROM console */

+int unbind_con_driver(const struct consw *csw, int first, int last, int deflt);
int con_is_bound(const struct consw *csw);
int register_con_driver(const struct consw *csw, int first, int last);
int unregister_con_driver(const struct consw *csw);
--- linux-2.6.21-rc4/drivers/video/console/fbcon.c 2007-03-15 17:20:01.000000000 -0700
+++ linux-2.6.21-rc4-modesetting/drivers/video/console/fbcon.c 2007-04-26 18:15:49.000000000 -0700
@@ -563,8 +563,10 @@
for (i = first_fb_vc; i <= last_fb_vc; i++)
con2fb_map[i] = info_idx;

+ printk(KERN_ERR "calling take_over_console, return value: ");
err = take_over_console(&fb_con, first_fb_vc, last_fb_vc,
fbcon_is_default);
+ printk(KERN_ERR "%d\n", err);

if (err) {
for (i = first_fb_vc; i <= last_fb_vc; i++) {
@@ -2896,6 +2898,13 @@
return found;
}

+static int fbcon_fb_unbind(int idx)
+{
+ unbind_con_driver(&fb_con, 0, MAX_NR_CONSOLES - 1, 0);
+
+ return 0;
+}
+
static int fbcon_fb_unregistered(int idx)
{
int i;
@@ -2933,6 +2942,7 @@
{
int ret = 0, i;

+ printk(KERN_ERR "fbcon registration called\n");
if (info_idx == -1) {
for (i = first_fb_vc; i <= last_fb_vc; i++) {
if (con2fb_map_boot[i] == idx) {
@@ -2941,8 +2951,10 @@
}
}

- if (info_idx != -1)
+ if (info_idx != -1) {
+ printk(KERN_ERR "fb taking over console\n");
ret = fbcon_takeover(1);
+ }
} else {
for (i = first_fb_vc; i <= last_fb_vc; i++) {
if (con2fb_map_boot[i] == idx &&
@@ -3036,6 +3048,9 @@
mode = event->data;
ret = fbcon_mode_deleted(info, mode);
break;
+ case FB_EVENT_FB_UNBIND:
+ ret = fbcon_fb_unbind(info->node);
+ break;
case FB_EVENT_FB_REGISTERED:
ret = fbcon_fb_registered(info->node);
break;
--- linux-2.6.21-rc4/drivers/char/vt.c 2007-03-15 17:20:01.000000000 -0700
+++ linux-2.6.21-rc4-modesetting/drivers/char/vt.c 2007-04-26 18:22:42.000000000 -0700
@@ -2832,8 +2832,24 @@
return retval;
}

-static int unbind_con_driver(const struct consw *csw, int first, int last,
- int deflt)
+/**
+ * unbind_con_driver - unbind a console driver
+ * @csw: pointer to console driver to unregister
+ * @first: first in range of consoles that @csw should be unbound from
+ * @last: last in range of consoles that @csw should be unbound from
+ * @deflt: should next bound console driver be default after @csw is unbound?
+ *
+ * To unbind a driver from all possible consoles, pass 0 as @first and
+ * %MAX_NR_CONSOLES as @last.
+ *
+ * @deflt controls whether the console that ends up replacing @csw should be
+ * the default console.
+ *
+ * RETURNS:
+ * -ENODEV if @csw isn't a registered console driver or can't be unregistered
+ * or 0 on success.
+ */
+int unbind_con_driver(const struct consw *csw, int first, int last, int deflt)
{
struct module *owner = csw->owner;
const struct consw *defcsw = NULL;
@@ -2918,6 +2934,7 @@
return retval;

}
+EXPORT_SYMBOL(unbind_con_driver);

static int vt_bind(struct con_driver *con)
{

2007-05-17 22:38:16

by Jesse Barnes

[permalink] [raw]
Subject: [PATCH 2/3] drm modesetting core

This patch adds the core of the new DRM based modesetting system. It
creates several new structures in the DRM, the primary ones being the
CRTC, which controls all aspects of your device's CRTC(s), output,
which describes and controls the various outputs on your gfx chip (e.g.
TMDS, LVDS, VGA, etc.), and mode, which describes graphics modes in
enough detail for the output and CRTC callbacks to program them to
hardware.

It also contains the user level IOCTL interfaces for doing graphics
programming (getting CRTC, output and mode lists, setting up new
frame buffer objects, etc.).

It relies on the new TTM patch Dave posted recently.

Jesse

diff --git a/linux-core/Makefile.kernel b/linux-core/Makefile.kernel
index 6f5b021..b9684d6 100644
--- a/linux-core/Makefile.kernel
+++ b/linux-core/Makefile.kernel
@@ -13,13 +13,15 @@ drm-objs := drm_auth.o drm_bufs.o drm_context.o
drm_dma.o drm_drawable.o \
drm_sysfs.o drm_pci.o drm_agpsupport.o drm_scatter.o \
drm_memory_debug.o ati_pcigart.o drm_sman.o \
drm_hashtab.o drm_mm.o drm_object.o drm_compat.o \
- drm_fence.o drm_ttm.o drm_bo.o drm_bo_move.o
+ drm_fence.o drm_ttm.o drm_bo.o drm_bo_move.o drm_crtc.o \
+ drm_edid.o drm_modes.o drm_fb.o
tdfx-objs := tdfx_drv.o
r128-objs := r128_drv.o r128_cce.o r128_state.o r128_irq.o
mga-objs := mga_drv.o mga_dma.o mga_state.o mga_warp.o mga_irq.o
i810-objs := i810_drv.o i810_dma.o
i915-objs := i915_drv.o i915_dma.o i915_irq.o i915_mem.o i915_fence.o
\
- i915_buffer.o
+ i915_buffer.o intel_display.o intel_crt.o intel_lvds.o \
+ intel_sdvo.o intel_modes.o intel_i2c.o i915_init.o
nouveau-objs := nouveau_drv.o nouveau_state.o nouveau_fifo.o
nouveau_mem.o \
nouveau_object.o nouveau_irq.o \
nv04_timer.o \
diff --git a/linux-core/drmP.h b/linux-core/drmP.h
index a3f9ca8..377f447 100644
--- a/linux-core/drmP.h
+++ b/linux-core/drmP.h
@@ -164,6 +164,8 @@

#include "drm_compat.h"

+#include "drm_crtc.h"
+
/***********************************************************************/
/** \name Macros to make printk easier */
/*@{*/
@@ -429,6 +431,8 @@ typedef struct drm_file {

drm_open_hash_t refd_object_hash[_DRM_NO_REF_TYPES];
void *driver_priv;
+
+ struct list_head fbs;
} drm_file_t;

/** Wait queue */
@@ -831,6 +835,9 @@ typedef struct drm_device {
unsigned int drw_info_length;
drm_drawable_info_t **drw_info;
/*@} */
+
+ /* DRM mode setting */
+ struct drm_mode_config mode_config;
} drm_device_t;

#if __OS_HAS_AGP
@@ -1128,10 +1135,6 @@ extern drm_head_t **drm_heads;
extern struct drm_sysfs_class *drm_class;
extern struct proc_dir_entry *drm_proc_root;

-extern drm_local_map_t *drm_getsarea(struct drm_device *dev);
-extern int drm_wait_on(drm_device_t *dev, wait_queue_head_t *queue,
- int timeout, int (*fn)(drm_device_t *dev, void *priv),
- void *priv);
/* Proc support (drm_proc.h) */
extern int drm_proc_init(drm_device_t * dev,
int minor,
diff --git a/linux-core/drm_bo.c b/linux-core/drm_bo.c
index 1c7013b..27016d8 100644
--- a/linux-core/drm_bo.c
+++ b/linux-core/drm_bo.c
@@ -494,6 +494,7 @@ void drm_bo_usage_deref_locked(drm_buffer_object_t *
bo)
drm_bo_destroy_locked(bo);
}
}
+EXPORT_SYMBOL(drm_bo_usage_deref_locked);

static void drm_bo_base_deref_locked(drm_file_t * priv,
drm_user_object_t * uo)
{
@@ -1624,6 +1625,7 @@ int drm_buffer_object_create(drm_device_t *dev,
drm_bo_usage_deref_unlocked(bo);
return ret;
}
+EXPORT_SYMBOL(drm_buffer_object_create);

static int drm_bo_add_user_object(drm_file_t * priv,
drm_buffer_object_t * bo,
int shareable)
@@ -1732,7 +1734,6 @@ int drm_bo_ioctl(DRM_IOCTL_ARGS)
drm_buffer_type, &uo);
if (rep.ret)
break;
-
rep.ret = drm_bo_handle_info(priv, req->handle, &rep);
break;
case drm_bo_unreference:
@@ -1983,6 +1984,7 @@ int drm_bo_clean_mm(drm_device_t * dev, unsigned
mem_type)

return ret;
}
+EXPORT_SYMBOL(drm_bo_clean_mm);

/**
*Evict all buffers of a particular mem_type, but leave memory manager
@@ -2115,6 +2117,7 @@ int drm_bo_driver_finish(drm_device_t * dev)
mutex_unlock(&dev->bm.init_mutex);
return ret;
}
+EXPORT_SYMBOL(drm_bo_driver_finish);

int drm_bo_driver_init(drm_device_t * dev)
{
@@ -2306,9 +2309,6 @@ void drm_bo_unmap_virtual(drm_buffer_object_t *
bo)
loff_t offset = ((loff_t) bo->map_list.hash.key) << PAGE_SHIFT;
loff_t holelen = ((loff_t) bo->mem.num_pages) << PAGE_SHIFT;

- if (!dev->dev_mapping)
- return;
-
unmap_mapping_range(dev->dev_mapping, offset, holelen, 1);
}

diff --git a/linux-core/drm_bo_move.c b/linux-core/drm_bo_move.c
index 4f75206..eaeef1b 100644
--- a/linux-core/drm_bo_move.c
+++ b/linux-core/drm_bo_move.c
@@ -128,6 +128,7 @@ int drm_mem_reg_ioremap(drm_device_t * dev,
drm_bo_mem_reg_t * mem,
*virtual = addr;
return 0;
}
+EXPORT_SYMBOL(drm_mem_reg_ioremap);

/**
* \c Unmap mapping obtained using drm_bo_ioremap
@@ -150,6 +151,7 @@ void drm_mem_reg_iounmap(drm_device_t * dev,
drm_bo_mem_reg_t * mem,
iounmap(virtual);
}
}
+EXPORT_SYMBOL(drm_mem_reg_iounmap);

static int drm_copy_io_page(void *dst, void *src, unsigned long page)
{
diff --git a/linux-core/drm_bufs.c b/linux-core/drm_bufs.c
index 8793ba0..1ba53c8 100644
--- a/linux-core/drm_bufs.c
+++ b/linux-core/drm_bufs.c
@@ -57,7 +57,7 @@ static drm_map_list_t
*drm_find_matching_map(drm_device_t *dev,
drm_map_list_t *entry = list_entry(list, drm_map_list_t, head);
if (entry->map && map->type == entry->map->type &&
((entry->map->offset == map->offset) ||
- (map->type == _DRM_SHM && map->flags==_DRM_CONTAINS_LOCK))) {
+ ((map->type == _DRM_SHM) && (map->flags&_DRM_CONTAINS_LOCK)))) {
return entry;
}
}
@@ -417,6 +417,7 @@ int drm_rmmap_locked(drm_device_t *dev,
drm_local_map_t *map)
break;
case _DRM_SHM:
vfree(map->handle);
+ dev->sigdata.lock = dev->lock.hw_lock = NULL; /* SHM removed */
break;
case _DRM_AGP:
case _DRM_SCATTER_GATHER:
diff --git a/linux-core/drm_compat.h b/linux-core/drm_compat.h
index bc5fadc..bada1fd 100644
--- a/linux-core/drm_compat.h
+++ b/linux-core/drm_compat.h
@@ -60,6 +60,13 @@
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
#undef DRM_IRQ_ARGS
#define DRM_IRQ_ARGS int irq, void *arg, struct pt_regs *regs
+
+typedef _Bool bool;
+enum {
+ false = 0,
+ true = 1
+};
+
#endif

#ifndef list_for_each_safe
diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c
new file mode 100644
index 0000000..16cf62a
--- /dev/null
+++ b/linux-core/drm_crtc.c
@@ -0,0 +1,1652 @@
+/*
+ * Copyright (c) 2006-2007 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <[email protected]>
+ *
+ * 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 <[email protected]>
+ * Dave Airlie <[email protected]>
+ * Jesse Barnes <[email protected]>
+ */
+#include <linux/list.h>
+#include "drm.h"
+#include "drmP.h"
+#include "drm_crtc.h"
+
+/**
+ * drm_idr_get - allocate a new identifier
+ * @dev: DRM device
+ * @ptr: object pointer, used to generate unique ID
+ *
+ * LOCKING:
+ * Process context (either init or calling process). Must take DRM
mode_config
+ * lock around IDR allocation.
+ *
+ * Create a unique identifier based on @ptr in @dev's identifier space.
Used
+ * for tracking modes, CRTCs and outputs.
+ *
+ * RETURNS:
+ * New unique (relative to other objects in @dev) integer identifier
for the
+ * object.
+ */
+int drm_idr_get(struct drm_device *dev, void *ptr)
+{
+ int new_id = 0;
+ int ret;
+again:
+ if (idr_pre_get(&dev->mode_config.crtc_idr, GFP_KERNEL) == 0) {
+ DRM_ERROR("Ran out memory getting a mode number\n");
+ return 0;
+ }
+
+ spin_lock(&dev->mode_config.config_lock);
+
+ ret = idr_get_new_above(&dev->mode_config.crtc_idr, ptr, 1, &new_id);
+ if (ret == -EAGAIN) {
+ spin_unlock(&dev->mode_config.config_lock);
+ goto again;
+ }
+
+ spin_unlock(&dev->mode_config.config_lock);
+ return new_id;
+}
+
+/**
+ * drm_idr_put - free an identifer
+ * @dev: DRM device
+ * @id: ID to free
+ *
+ * LOCKING:
+ * Caller must hold DRM mode_config lock.
+ *
+ * Free @id from @dev's unique identifier pool.
+ */
+void drm_idr_put(struct drm_device *dev, int id)
+{
+ idr_remove(&dev->mode_config.crtc_idr, id);
+}
+
+/**
+ * drm_framebuffer_create - create a new framebuffer object
+ * @dev: DRM device
+ *
+ * LOCKING:
+ * Process context (either init or calling process). Must take DRM
mode_config
+ * lock around mode_config manipulation.
+ *
+ * Creates a new framebuffer objects and adds it to @dev's DRM
mode_config.
+ *
+ * RETURNS:
+ * Pointer to new framebuffer or NULL on error.
+ */
+struct drm_framebuffer *drm_framebuffer_create(drm_device_t *dev)
+{
+ struct drm_framebuffer *fb;
+
+ spin_lock(&dev->mode_config.config_lock);
+ /* Limit to single framebuffer for now */
+ if (dev->mode_config.num_fb > 1) {
+ spin_unlock(&dev->mode_config.config_lock);
+ DRM_ERROR("Attempt to add multiple framebuffers failed\n");
+ return NULL;
+ }
+ spin_unlock(&dev->mode_config.config_lock);
+
+ fb = kzalloc(sizeof(struct drm_framebuffer), GFP_KERNEL);
+ if (!fb) {
+
+ return NULL;
+ }
+
+ fb->id = drm_idr_get(dev, fb);
+ fb->dev = dev;
+ spin_lock(&dev->mode_config.config_lock);
+ dev->mode_config.num_fb++;
+ list_add(&fb->head, &dev->mode_config.fb_list);
+ spin_unlock(&dev->mode_config.config_lock);
+
+ return fb;
+}
+EXPORT_SYMBOL(drm_framebuffer_create);
+
+/**
+ * drm_framebuffer_destroy - remove a framebuffer object
+ * @fb: framebuffer to remove
+ *
+ * LOCKING:
+ * Process context (either init or calling process). Must take DRM
mode_config
+ * lock around mode_config manipulation.
+ *
+ * Scans all the CRTCs in @dev's mode_config. If they're using @fb,
removes
+ * it, setting it to NULL.
+ */
+void drm_framebuffer_destroy(struct drm_framebuffer *fb)
+{
+ drm_device_t *dev = fb->dev;
+ struct drm_crtc *crtc;
+
+ /* remove from any CRTC */
+ spin_lock(&dev->mode_config.config_lock);
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ if (crtc->fb == fb)
+ crtc->fb = NULL;
+ }
+
+ drm_idr_put(dev, fb->id);
+ list_del(&fb->head);
+ dev->mode_config.num_fb--;
+ spin_unlock(&dev->mode_config.config_lock);
+
+ kfree(fb);
+}
+
+/**
+ * drm_crtc_create - create a new CRTC object
+ * @dev: DRM device
+ * @funcs: callbacks for the new CRTC
+ *
+ * LOCKING:
+ * Process context (either init or calling process). Must take DRM
mode_config
+ * lock around mode_config manipulation.
+ *
+ * Creates a new CRTC object and adds it to @dev's mode_config
structure.
+ *
+ * RETURNS:
+ * Pointer to new CRTC object or NULL on error.
+ */
+struct drm_crtc *drm_crtc_create(drm_device_t *dev,
+ const struct drm_crtc_funcs *funcs)
+{
+ struct drm_crtc *crtc;
+
+ crtc = kzalloc(sizeof(struct drm_crtc), GFP_KERNEL);
+ if (!crtc)
+ return NULL;
+
+ crtc->dev = dev;
+ crtc->funcs = funcs;
+
+ crtc->id = drm_idr_get(dev, crtc);
+
+ spin_lock(&dev->mode_config.config_lock);
+ list_add_tail(&crtc->head, &dev->mode_config.crtc_list);
+ dev->mode_config.num_crtc++;
+ spin_unlock(&dev->mode_config.config_lock);
+
+ return crtc;
+}
+EXPORT_SYMBOL(drm_crtc_create);
+
+/**
+ * drm_crtc_destroy - remove a CRTC object
+ * @crtc: CRTC to remove
+ *
+ * LOCKING:
+ * Process context (either init or calling process). Must take DRM
mode_config
+ * lock around mode_config traversal.
+ *
+ * Cleanup @crtc. Calls @crtc's cleanup function, then removes @crtc
from
+ * its associated DRM device's mode_config. Frees it afterwards.
+ */
+void drm_crtc_destroy(struct drm_crtc *crtc)
+{
+ drm_device_t *dev = crtc->dev;
+
+ if (crtc->funcs->cleanup)
+ (*crtc->funcs->cleanup)(crtc);
+
+ spin_lock(&dev->mode_config.config_lock);
+ drm_idr_put(dev, crtc->id);
+ list_del(&crtc->head);
+ dev->mode_config.num_crtc--;
+ spin_unlock(&dev->mode_config.config_lock);
+ kfree(crtc);
+}
+EXPORT_SYMBOL(drm_crtc_destroy);
+
+/**
+ * drm_crtc_in_use - check if a given CRTC is in a mode_config
+ * @crtc: CRTC to check
+ *
+ * LOCKING:
+ * Caller? (FIXME)
+ *
+ * Walk @crtc's DRM device's mode_config and see if it's in use.
+ *
+ * RETURNS:
+ * True if @crtc is part of the mode_config, false otherwise.
+ */
+bool drm_crtc_in_use(struct drm_crtc *crtc)
+{
+ struct drm_output *output;
+ drm_device_t *dev = crtc->dev;
+ /* FIXME: Locking around list access? */
+ list_for_each_entry(output, &dev->mode_config.output_list, head)
+ if (output->crtc == crtc)
+ return true;
+ return false;
+}
+EXPORT_SYMBOL(drm_crtc_in_use);
+
+/*
+ * Detailed mode info for a standard 640x480@60Hz monitor
+ */
+static struct drm_display_mode std_mode[] = {
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25200, 640, 656,
+ 752, 800, 0, 480, 490, 492, 525, 0,
+ V_NHSYNC | V_NVSYNC) }, /* 640x480@60Hz */
+};
+
+/**
+ * drm_crtc_probe_output_modes - get complete set of display modes
+ * @dev: DRM device
+ * @maxX: max width for modes
+ * @maxY: max height for modes
+ *
+ * LOCKING:
+ * Caller? (FIXME)
+ *
+ * Based on @dev's mode_config layout, scan all the outputs and try to
detect
+ * modes on them. Modes will first be added to the output's
probed_modes
+ * list, then culled (based on validity and the @maxX, @maxY
parameters) and
+ * put into the normal modes list.
+ *
+ * Intended to be used either at bootup time or when major
configuration
+ * changes have occurred.
+ *
+ * FIXME: take into account monitor limits
+ */
+void drm_crtc_probe_output_modes(struct drm_device *dev, int maxX, int
maxY)
+{
+ struct drm_output *output;
+ struct drm_display_mode *mode, *t;
+ int ret;
+ //if (maxX == 0 || maxY == 0)
+ // TODO
+
+ list_for_each_entry(output, &dev->mode_config.output_list, head) {
+
+ /* set all modes to the unverified state */
+ list_for_each_entry_safe(mode, t, &output->modes, head)
+ mode->status = MODE_UNVERIFIED;
+
+ output->status = (*output->funcs->detect)(output);
+
+ if (output->status == output_status_disconnected) {
+ DRM_DEBUG("%s is disconnected\n", output->name);
+ /* TODO set EDID to NULL */
+ continue;
+ }
+
+ ret = (*output->funcs->get_modes)(output);
+
+ if (ret) {
+ drm_mode_output_list_update(output);
+ }
+
+ if (maxX && maxY)
+ drm_mode_validate_size(dev, &output->modes, maxX,
+ maxY, 0);
+ list_for_each_entry_safe(mode, t, &output->modes, head) {
+ if (mode->status == MODE_OK)
+ mode->status = (*output->funcs->mode_valid)(output,mode);
+ }
+
+
+ drm_mode_prune_invalid(dev, &output->modes, TRUE);
+
+ if (list_empty(&output->modes)) {
+ struct drm_display_mode *stdmode;
+
+ DRM_DEBUG("No valid modes on %s\n", output->name);
+
+ /* Should we do this here ???
+ * When no valid EDID modes are available we end up
+ * here and bailed in the past, now we add a standard
+ * 640x480@60Hz mode and carry on.
+ */
+ stdmode = drm_mode_duplicate(dev, &std_mode[0]);
+ drm_mode_probed_add(output, stdmode);
+ drm_mode_list_concat(&output->probed_modes,
+ &output->modes);
+
+ DRM_DEBUG("Adding standard 640x480 @ 60Hz to %s\n",
+ output->name);
+ }
+
+ drm_mode_sort(&output->modes);
+
+ DRM_DEBUG("Probed modes for %s\n", output->name);
+ list_for_each_entry_safe(mode, t, &output->modes, head) {
+ mode->vrefresh = drm_mode_vrefresh(mode);
+
+ drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
+ drm_mode_debug_printmodeline(dev, mode);
+ }
+ }
+}
+
+/**
+ * drm_crtc_set_mode - set a mode
+ * @crtc: CRTC to program
+ * @mode: mode to use
+ * @x: width of mode
+ * @y: height of mode
+ *
+ * LOCKING:
+ * Caller? (FIXME)
+ *
+ * Try to set @mode on @crtc. Give @crtc and its associated outputs a
chance
+ * to fixup or reject the mode prior to trying to set it.
+ *
+ * RETURNS:
+ * True if the mode was set successfully, or false otherwise.
+ */
+bool drm_crtc_set_mode(struct drm_crtc *crtc, struct drm_display_mode
*mode,
+ int x, int y)
+{
+ drm_device_t *dev = crtc->dev;
+ struct drm_display_mode *adjusted_mode, saved_mode;
+ int saved_x, saved_y;
+ bool didLock = false;
+ bool ret = false;
+ struct drm_output *output;
+
+ adjusted_mode = drm_mode_duplicate(dev, mode);
+
+ crtc->enabled = drm_crtc_in_use(crtc);
+
+ if (!crtc->enabled) {
+ return true;
+ }
+
+ didLock = crtc->funcs->lock(crtc);
+
+ saved_mode = crtc->mode;
+ saved_x = crtc->x;
+ saved_y = crtc->y;
+
+ /* Update crtc values up front so the driver can rely on them for mode
+ * setting.
+ */
+ crtc->mode = *mode;
+ crtc->x = x;
+ crtc->y = y;
+
+ /* XXX short-circuit changes to base location only */
+
+ /* Pass our mode to the outputs and the CRTC to give them a chance to
+ * adjust it according to limitations or output properties, and also
+ * a chance to reject the mode entirely.
+ */
+ list_for_each_entry(output, &dev->mode_config.output_list, head) {
+
+ if (output->crtc != crtc)
+ continue;
+
+ if (!output->funcs->mode_fixup(output, mode, adjusted_mode)) {
+ goto done;
+ }
+ }
+
+ if (!crtc->funcs->mode_fixup(crtc, mode, adjusted_mode)) {
+ goto done;
+ }
+
+ /* Prepare the outputs and CRTCs before setting the mode. */
+ list_for_each_entry(output, &dev->mode_config.output_list, head) {
+
+ if (output->crtc != crtc)
+ continue;
+
+ /* Disable the output as the first thing we do. */
+ output->funcs->prepare(output);
+ }
+
+ crtc->funcs->prepare(crtc);
+
+ /* Set up the DPLL and any output state that needs to adjust or depend
+ * on the DPLL.
+ */
+ crtc->funcs->mode_set(crtc, mode, adjusted_mode, x, y);
+ list_for_each_entry(output, &dev->mode_config.output_list, head) {
+ if (output->crtc == crtc)
+ output->funcs->mode_set(output, mode, adjusted_mode);
+ }
+
+ /* Now, enable the clocks, plane, pipe, and outputs that we set up. */
+ crtc->funcs->commit(crtc);
+ list_for_each_entry(output, &dev->mode_config.output_list, head) {
+ if (output->crtc == crtc) {
+ output->funcs->commit(output);
+#if 0 // TODO def RANDR_12_INTERFACE
+ if (output->randr_output)
+ RRPostPendingProperties (output->randr_output);
+#endif
+ }
+ }
+
+ /* XXX free adjustedmode */
+ drm_mode_destroy(dev, adjusted_mode);
+ ret = TRUE;
+ /* TODO */
+// if (scrn->pScreen)
+// drm_crtc_set_screen_sub_pixel_order(dev);
+
+done:
+ if (!ret) {
+ crtc->x = saved_x;
+ crtc->y = saved_y;
+ crtc->mode = saved_mode;
+ }
+
+ if (didLock)
+ crtc->funcs->unlock (crtc);
+
+ return ret;
+}
+
+/**
+ * drm_set_desired_modes - set a good mode on every CRTC & output
+ * @dev: DRM device
+ *
+ * LOCKING:
+ * Caller? (FIXME)
+ *
+ * Each CRTC may have a desired mode associated with it. This routine
simply
+ * walks @dev's mode_config and sets the desired mode on every CRTC.
Intended
+ * for use at startup time.
+ *
+ * RETURNS:
+ * True if modes were set, false otherwise.
+ */
+bool drm_set_desired_modes(struct drm_device *dev)
+{
+ struct drm_crtc *crtc;
+ struct drm_output *output, *list_output;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ output = NULL;
+
+ list_for_each_entry(list_output, &dev->mode_config.output_list,
+ head) {
+ if (list_output->crtc == crtc) {
+ output = list_output;
+ break;
+ }
+ }
+ /* Skip disabled crtcs */
+ if (!output) {
+ DRM_DEBUG("skipping disabled crtc\n");
+ continue;
+ }
+
+ if (!drm_crtc_set_mode(crtc, crtc->desired_mode,
+ crtc->desired_x, crtc->desired_y))
+ return false;
+ }
+
+ drm_disable_unused_functions(dev);
+ return true;
+}
+EXPORT_SYMBOL(drm_set_desired_modes);
+
+/**
+ * drm_disable_unused_functions - disable unused objects
+ * @dev: DRM device
+ *
+ * LOCKING:
+ * Caller? (FIXME)
+ *
+ * If an output or CRTC isn't part of @dev's mode_config, it can be
disabled
+ * by calling its dpms function, which should power it off.
+ */
+void drm_disable_unused_functions(struct drm_device *dev)
+{
+ struct drm_output *output;
+ struct drm_crtc *crtc;
+
+ list_for_each_entry(output, &dev->mode_config.output_list, head) {
+ if (!output->crtc)
+ (*output->funcs->dpms)(output, DPMSModeOff);
+ }
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ if (!crtc->enabled)
+ crtc->funcs->dpms(crtc, DPMSModeOff);
+ }
+}
+
+/**
+ * drm_mode_probed_add - add a mode to the specified output's probed
mode list
+ * @output: output the new mode
+ * @mode: mode data
+ *
+ * LOCKING:
+ * Process context (either init or calling process). Must take
@output's
+ * mode_lock around mode list manipulation.
+ *
+ * Add @mode to @output's mode list for later use.
+ */
+void drm_mode_probed_add(struct drm_output *output,
+ struct drm_display_mode *mode)
+{
+ spin_lock(&output->modes_lock);
+ list_add(&mode->head, &output->probed_modes);
+ spin_unlock(&output->modes_lock);
+}
+EXPORT_SYMBOL(drm_mode_probed_add);
+
+/**
+ * drm_mode_remove - remove and free a mode
+ * @output: output list to modify
+ * @mode: mode to remove
+ *
+ * LOCKING:
+ * Process context (either init or calling process). Must take
@output's
+ * mode_lock around mode list manipulation.
+ *
+ * Remove @mode from @output's mode list, then free it.
+ */
+void drm_mode_remove(struct drm_output *output, struct drm_display_mode
*mode)
+{
+ spin_lock(&output->modes_lock);
+ list_del(&mode->head);
+ spin_unlock(&output->modes_lock);
+ kfree(mode);
+}
+EXPORT_SYMBOL(drm_mode_remove);
+
+/**
+ * drm_output_create - create a new output
+ * @dev: DRM device
+ * @funcs: callbacks for this output
+ * @name: user visible name of the output
+ *
+ * LOCKING:
+ * Process context (either init or calling process). Must take @dev's
+ * mode_config lock around mode list manipulation.
+ *
+ * Creates a new drm_output structure and adds it to @dev's mode_config
+ * structure.
+ *
+ * RETURNS:
+ * Pointer to the new output or NULL on error.
+ */
+struct drm_output *drm_output_create(drm_device_t *dev,
+ const struct drm_output_funcs *funcs,
+ const char *name)
+{
+ struct drm_output *output = NULL;
+
+ output = kzalloc(sizeof(struct drm_output), GFP_KERNEL);
+ if (!output)
+ return NULL;
+
+ output->dev = dev;
+ output->funcs = funcs;
+ output->id = drm_idr_get(dev, output);
+ if (name)
+ strncpy(output->name, name, DRM_OUTPUT_LEN);
+ output->name[DRM_OUTPUT_LEN - 1] = 0;
+ output->subpixel_order = SubPixelUnknown;
+ INIT_LIST_HEAD(&output->probed_modes);
+ INIT_LIST_HEAD(&output->modes);
+ spin_lock_init(&output->modes_lock);
+ /* randr_output? */
+ /* output_set_monitor(output)? */
+ /* check for output_ignored(output)? */
+
+ spin_lock(&dev->mode_config.config_lock);
+ list_add_tail(&output->head, &dev->mode_config.output_list);
+ dev->mode_config.num_output++;
+
+ spin_unlock(&dev->mode_config.config_lock);
+
+ return output;
+
+}
+EXPORT_SYMBOL(drm_output_create);
+
+/**
+ * drm_output_destroy - remove an output
+ * @output: output to remove
+ *
+ * LOCKING:
+ * Process context (either init or calling process). Must take @dev's
+ * mode_config lock around mode list manipulation. Caller must hold
+ * modes lock? (FIXME)
+ *
+ * Call @output's cleanup function, then remove the output from the DRM
+ * mode_config after freeing @output's modes.
+ */
+void drm_output_destroy(struct drm_output *output)
+{
+ struct drm_device *dev = output->dev;
+ struct drm_display_mode *mode, *t;
+
+ if (*output->funcs->cleanup)
+ (*output->funcs->cleanup)(output);
+
+ list_for_each_entry_safe(mode, t, &output->probed_modes, head)
+ drm_mode_remove(output, mode);
+
+ list_for_each_entry_safe(mode, t, &output->modes, head)
+ drm_mode_remove(output, mode);
+
+ spin_lock(&dev->mode_config.config_lock);
+ drm_idr_put(dev, output->id);
+ list_del(&output->head);
+ spin_unlock(&dev->mode_config.config_lock);
+ kfree(output);
+}
+EXPORT_SYMBOL(drm_output_destroy);
+
+/**
+ * drm_output_rename - rename an output
+ * @output: output to rename
+ * @name: new user visible name
+ *
+ * LOCKING:
+ * None.
+ *
+ * Simply stuff a new name into @output's name field, based on @name.
+ *
+ * RETURNS:
+ * True if the name was changed, false otherwise.
+ */
+bool drm_output_rename(struct drm_output *output, const char *name)
+{
+ if (!name)
+ return false;
+
+ strncpy(output->name, name, DRM_OUTPUT_LEN);
+ output->name[DRM_OUTPUT_LEN - 1] = 0;
+
+ DRM_DEBUG("Changed name to %s\n", output->name);
+// drm_output_set_monitor(output);
+// if (drm_output_ignored(output))
+// return FALSE;
+
+ return TRUE;
+}
+EXPORT_SYMBOL(drm_output_rename);
+
+/**
+ * drm_mode_create - create a new display mode
+ * @dev: DRM device
+ *
+ * LOCKING:
+ * None.
+ *
+ * Create a new drm_display_mode, give it an ID, and return it.
+ *
+ * RETURNS:
+ * Pointer to new mode on success, NULL on error.
+ */
+struct drm_display_mode *drm_mode_create(struct drm_device *dev)
+{
+ struct drm_display_mode *nmode;
+
+ nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL);
+ if (!nmode)
+ return NULL;
+
+ nmode->mode_id = drm_idr_get(dev, nmode);
+ return nmode;
+}
+
+/**
+ * drm_mode_destroy - remove a mode
+ * @dev: DRM device
+ * @mode: mode to remove
+ *
+ * LOCKING:
+ * None.
+ *
+ * Free @mode's unique identifier, then free it.
+ */
+void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode
*mode)
+{
+ drm_idr_put(dev, mode->mode_id);
+
+ kfree(mode);
+}
+
+/**
+ * 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(drm_device_t *dev)
+{
+ spin_lock_init(&dev->mode_config.config_lock);
+ INIT_LIST_HEAD(&dev->mode_config.fb_list);
+ INIT_LIST_HEAD(&dev->mode_config.crtc_list);
+ INIT_LIST_HEAD(&dev->mode_config.output_list);
+ idr_init(&dev->mode_config.crtc_idr);
+}
+EXPORT_SYMBOL(drm_mode_config_init);
+
+/**
+ * drm_get_buffer_object - find the buffer object for a given handle
+ * @dev: DRM device
+ * @bo: pointer to caller's buffer_object pointer
+ * @handle: handle to lookup
+ *
+ * LOCKING:
+ * Must take @dev's struct_mutex to protect buffer object lookup.
+ *
+ * Given @handle, lookup the buffer object in @dev and put it in the
caller's
+ * @bo pointer.
+ *
+ * RETURNS:
+ * Zero on success, -EINVAL if the handle couldn't be found.
+ */
+static int drm_get_buffer_object(drm_device_t *dev, struct
drm_buffer_object **bo, unsigned long handle)
+{
+ drm_user_object_t *uo;
+ drm_hash_item_t *hash;
+ int ret;
+
+ *bo = NULL;
+
+ mutex_lock(&dev->struct_mutex);
+ ret = drm_ht_find_item(&dev->object_hash, handle, &hash);
+ if (ret) {
+ DRM_ERROR("Couldn't find handle.\n");
+ ret = -EINVAL;
+ goto out_err;
+ }
+
+ uo = drm_hash_entry(hash, drm_user_object_t, hash);
+ if (uo->type != drm_buffer_type) {
+ ret = -EINVAL;
+ goto out_err;
+ }
+
+ *bo = drm_user_object_entry(uo, drm_buffer_object_t, base);
+ ret = 0;
+out_err:
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
+
+/**
+ * drm_setup_output - setup an output structure
+ * @output: output to setup
+ * @crtc: CRTC this output belongs to
+ * @mode: desired mode for this output
+ *
+ * LOCKING:
+ * None.
+ *
+ * Setup @output with the parameters given, with its initial
coordinates set
+ * at the origin.
+ */
+static void drm_setup_output(struct drm_output *output, struct drm_crtc
*crtc,
+ struct drm_display_mode *mode)
+{
+ output->crtc = crtc;
+ output->crtc->desired_mode = mode;
+ output->initial_x = 0;
+ output->initial_y = 0;
+}
+
+/**
+ * drm_initial_config - setup a sane initial output configuration
+ * @dev: DRM device
+ * @can_grow: this configuration is growable
+ *
+ * LOCKING:
+ * Must take various locks. (FIXME)
+ *
+ * Scan the CRTCs and outputs and try to put together an initial setup.
+ * At the moment, this is a cloned configuration across all heads with
+ * a new framebuffer object as the backing store.
+ *
+ * FIXME: return value and better initial config.
+ *
+ * RETURNS:
+ * Zero if everything went ok, nonzero otherwise.
+ */
+bool drm_initial_config(drm_device_t *dev, bool can_grow)
+{
+ /* do a hardcoded initial configuration here */
+ struct drm_crtc *crtc, *vga_crtc = NULL, *tmds_crtc = NULL,
+ *lvds_crtc = NULL;
+ struct drm_output *output;
+ struct drm_framebuffer *fb;
+ drm_buffer_object_t *fbo;
+ unsigned long size, bytes_per_pixel;
+
+ fb = drm_framebuffer_create(dev);
+ if (!fb) {
+ DRM_ERROR("failed to allocate fb.\n");
+ return true;
+ }
+
+ /* bind both CRTCs to this fb */
+ /* only initialise one crtc to enabled state */
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ crtc->fb = fb;
+ if (!vga_crtc) {
+ vga_crtc = crtc;
+ crtc->enabled = 1;
+ crtc->desired_x = 0;
+ crtc->desired_y = 0;
+ } else {
+ if (!lvds_crtc) {
+ lvds_crtc = crtc;
+ crtc->enabled = 1;
+ crtc->desired_x = 0;
+ crtc->desired_y = 0;
+ }
+ if (!tmds_crtc) {
+ tmds_crtc = crtc;
+ crtc->enabled = 1;
+ crtc->desired_x = 0;
+ crtc->desired_y = 0;
+ }
+ }
+ }
+
+ drm_crtc_probe_output_modes(dev, 2048, 2048);
+
+ /* hard bind the CRTCS */
+
+ /* bind analog output to one crtc */
+ list_for_each_entry(output, &dev->mode_config.output_list, head) {
+ struct drm_display_mode *des_mode = NULL;
+
+ if (list_empty(&output->modes))
+ continue;
+
+ /* Get the first preferred moded */
+ list_for_each_entry(des_mode, &output->modes, head) {
+ if (des_mode->flags & DRM_MODE_TYPE_PREFERRED)
+ break;
+ }
+
+ if (!des_mode)
+ continue;
+
+ if (!strncmp(output->name, "VGA", 3)) {
+ DRM_DEBUG("VGA preferred mode: %s\n", des_mode->name);
+ drm_setup_output(output, vga_crtc, des_mode);
+ } else if (!strncmp(output->name, "TMDS", 4)) {
+ DRM_DEBUG("TMDS preferred mode: %s\n", des_mode->name);
+ drm_setup_output(output, tmds_crtc, des_mode);
+ } else if (!strncmp(output->name, "LVDS", 3)) {
+ DRM_DEBUG("LVDS preferred mode: %s\n", des_mode->name);
+ drm_setup_output(output, lvds_crtc, des_mode);
+ } else
+ output->crtc = NULL;
+
+ /* FB config is max of above desired resolutions */
+ /* FIXME: per-output FBs/CRTCs */
+ if (des_mode->hdisplay > fb->width) {
+ fb->width = des_mode->hdisplay;
+ fb->pitch = fb->width;
+ }
+ if (des_mode->vdisplay > fb->height)
+ fb->height = des_mode->vdisplay;
+ }
+
+ /* FIXME: multiple depths */
+ bytes_per_pixel = 4;
+ fb->bits_per_pixel = bytes_per_pixel * 8;
+ fb->depth = bytes_per_pixel * 8;
+ size = fb->width * fb->height * bytes_per_pixel;
+ drm_buffer_object_create(dev, size, drm_bo_type_kernel,
+ DRM_BO_FLAG_READ | DRM_BO_FLAG_WRITE |
+ DRM_BO_FLAG_MEM_PRIV0 | DRM_BO_FLAG_NO_MOVE,
+ 0, 0, 0,
+ &fbo);
+ DRM_DEBUG("allocated %dx%d fb: 0x%08lx, bo %p\n", fb->width,
+ fb->height, fbo->offset, fbo);
+ fb->offset = fbo->offset;
+ fb->bo = fbo;
+ drmfb_probe(dev, fb);
+
+ return false;
+}
+EXPORT_SYMBOL(drm_initial_config);
+
+/**
+ * drm_mode_config_cleanup - free up DRM mode_config info
+ * @dev: DRM device
+ *
+ * LOCKING:
+ * Caller? (FIXME)
+ *
+ * Free up all the outputs and CRTCs associated with this DRM device,
then
+ * free up the framebuffers and associated buffer objects.
+ *
+ * FIXME: cleanup any dangling user buffer objects too
+ */
+void drm_mode_config_cleanup(drm_device_t *dev)
+{
+ struct drm_output *output, *ot;
+ struct drm_crtc *crtc, *ct;
+ struct drm_framebuffer *fb, *fbt;
+ list_for_each_entry_safe(output, ot, &dev->mode_config.output_list,
head) {
+ drm_output_destroy(output);
+ }
+
+ list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head)
{
+ drm_crtc_destroy(crtc);
+ }
+
+ list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) {
+ drmfb_remove(dev, fb);
+ /* If this FB was the kernel one, free it */
+ if (fb->bo->type == drm_bo_type_kernel) {
+ mutex_lock(&dev->struct_mutex);
+ drm_bo_usage_deref_locked(fb->bo);
+ mutex_unlock(&dev->struct_mutex);
+ }
+ drm_framebuffer_destroy(fb);
+ }
+}
+EXPORT_SYMBOL(drm_mode_config_cleanup);
+
+/**
+ * drm_crtc_set_config - set a new config from userspace
+ * @crtc: CRTC to setup
+ * @crtc_info: user provided configuration
+ * @new_mode: new mode to set
+ * @output_set: set of outputs for the new config
+ * @fb: new framebuffer
+ *
+ * LOCKING:
+ * Caller? (FIXME)
+ *
+ * Setup a new configuration, provided by the user in @crtc_info, and
enable
+ * it.
+ *
+ * RETURNS:
+ * Zero. (FIXME)
+ */
+int drm_crtc_set_config(struct drm_crtc *crtc, struct drm_mode_crtc
*crtc_info, struct drm_display_mode *new_mode, struct drm_output
**output_set, struct drm_framebuffer *fb)
+{
+ drm_device_t *dev = crtc->dev;
+ struct drm_crtc **save_crtcs, *new_crtc;
+ bool save_enabled = crtc->enabled;
+ bool changed;
+ struct drm_output *output;
+ int count = 0, ro;
+
+ save_crtcs = kzalloc(dev->mode_config.num_crtc * sizeof(struct
drm_crtc *), GFP_KERNEL);
+ if (!save_crtcs)
+ return -ENOMEM;
+
+ if (crtc->fb != fb)
+ changed = true;
+
+ if (crtc_info->x != crtc->x || crtc_info->y != crtc->y)
+ changed = true;
+
+ if (new_mode && (crtc->mode.mode_id != new_mode->mode_id))
+ changed = true;
+
+ list_for_each_entry(output, &dev->mode_config.output_list, head) {
+ save_crtcs[count++] = output->crtc;
+
+ if (output->crtc == crtc)
+ new_crtc = NULL;
+ else
+ new_crtc = output->crtc;
+
+ for (ro = 0; ro < crtc_info->count_outputs; ro++) {
+ if (output_set[ro] == output)
+ new_crtc = crtc;
+ }
+ if (new_crtc != output->crtc) {
+ changed = true;
+ output->crtc = new_crtc;
+ }
+ }
+
+ if (changed) {
+ crtc->fb = fb;
+ crtc->enabled = (new_mode != NULL);
+ if (new_mode != NULL) {
+ DRM_DEBUG("attempting to set mode from userspace\n");
+ drm_mode_debug_printmodeline(dev, new_mode);
+ if (!drm_crtc_set_mode(crtc, new_mode, crtc_info->x,
+ crtc_info->y)) {
+ crtc->enabled = save_enabled;
+ count = 0;
+ list_for_each_entry(output, &dev->mode_config.output_list, head)
+ output->crtc = save_crtcs[count++];
+ kfree(save_crtcs);
+ return -EINVAL;
+ }
+ crtc->desired_x = crtc_info->x;
+ crtc->desired_y = crtc_info->y;
+ crtc->desired_mode = new_mode;
+ }
+ drm_disable_unused_functions(dev);
+ }
+ kfree(save_crtcs);
+ return 0;
+}
+
+/**
+ * drm_crtc_convert_to_umode - convert a drm_display_mode into a
modeinfo
+ * @out: drm_mode_modeinfo struct to return to the user
+ * @in: drm_display_mode to use
+ *
+ * LOCKING:
+ * None.
+ *
+ * Convert a drm_display_mode into a drm_mode_modeinfo structure to
return to
+ * the user.
+ */
+void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, struct
drm_display_mode *in)
+{
+
+ out->id = in->mode_id;
+ out->clock = in->clock;
+ out->hdisplay = in->hdisplay;
+ out->hsync_start = in->hsync_start;
+ out->hsync_end = in->hsync_end;
+ out->htotal = in->htotal;
+ out->hskew = in->hskew;
+ out->vdisplay = in->vdisplay;
+ out->vsync_start = in->vsync_start;
+ out->vsync_end = in->vsync_end;
+ out->vtotal = in->vtotal;
+ out->vscan = in->vscan;
+ out->vrefresh = in->vrefresh;
+ out->flags = in->flags;
+ strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN);
+ out->name[DRM_DISPLAY_MODE_LEN-1] = 0;
+}
+
+
+/**
+ * drm_mode_getresources - get graphics configuration
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * LOCKING:
+ * Caller? (FIXME)
+ *
+ * Construct a set of configuration description structures and return
+ * them to the user, including CRTC, output and framebuffer
configuration.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_getresources(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ drm_file_t *priv = filp->private_data;
+ drm_device_t *dev = priv->head->dev;
+ struct drm_mode_card_res __user *argp = (void __user *)arg;
+ struct drm_mode_card_res card_res;
+ struct list_head *lh;
+ struct drm_framebuffer *fb;
+ struct drm_output *output;
+ struct drm_crtc *crtc;
+ struct drm_mode_modeinfo u_mode;
+ struct drm_display_mode *mode;
+ int retcode = 0;
+ int mode_count= 0;
+ int output_count = 0;
+ int crtc_count = 0;
+ int fb_count = 0;
+ int copied = 0;
+
+ memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo));
+
+ list_for_each(lh, &dev->mode_config.fb_list)
+ fb_count++;
+
+ list_for_each(lh, &dev->mode_config.crtc_list)
+ crtc_count++;
+
+ list_for_each_entry(output, &dev->mode_config.output_list,
+ head) {
+ output_count++;
+ list_for_each(lh, &output->modes)
+ mode_count++;
+ }
+
+ if (copy_from_user(&card_res, argp, sizeof(card_res)))
+ return -EFAULT;
+
+ if (card_res.count_modes == 0) {
+ DRM_DEBUG("probing modes %dx%d\n", dev->mode_config.max_width,
dev->mode_config.max_height);
+ drm_crtc_probe_output_modes(dev, dev->mode_config.max_width,
dev->mode_config.max_height);
+ mode_count = 0;
+ list_for_each_entry(output, &dev->mode_config.output_list, head) {
+ list_for_each(lh, &output->modes)
+ mode_count++;
+ }
+ }
+
+ /* handle this in 4 parts */
+ /* FBs */
+ if (card_res.count_fbs >= fb_count) {
+ copied = 0;
+ list_for_each_entry(fb, &dev->mode_config.fb_list, head) {
+ if (put_user(fb->id, &card_res.fb_id[copied++])) {
+ retcode = -EFAULT;
+ goto done;
+ }
+ }
+ }
+ card_res.count_fbs = fb_count;
+
+ /* CRTCs */
+ if (card_res.count_crtcs >= crtc_count) {
+ copied = 0;
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head){
+ DRM_DEBUG("CRTC ID is %d\n", crtc->id);
+ if (put_user(crtc->id, &card_res.crtc_id[copied++])) {
+ retcode = -EFAULT;
+ goto done;
+ }
+ }
+ }
+ card_res.count_crtcs = crtc_count;
+
+
+ /* Outputs */
+ if (card_res.count_outputs >= output_count) {
+ copied = 0;
+ list_for_each_entry(output, &dev->mode_config.output_list,
+ head) {
+ DRM_DEBUG("OUTPUT ID is %d\n", output->id);
+ if (put_user(output->id, &card_res.output_id[copied++])) {
+ retcode = -EFAULT;
+ goto done;
+ }
+ }
+ }
+ card_res.count_outputs = output_count;
+
+ /* Modes */
+ if (card_res.count_modes >= mode_count) {
+ copied = 0;
+ list_for_each_entry(output, &dev->mode_config.output_list,
+ head) {
+ list_for_each_entry(mode, &output->modes, head) {
+ drm_crtc_convert_to_umode(&u_mode, mode);
+ if (copy_to_user(&card_res.modes[copied++], &u_mode, sizeof(struct
drm_mode_modeinfo))) {
+ retcode = -EFAULT;
+ goto done;
+ }
+ }
+ }
+ }
+ card_res.count_modes = mode_count;
+
+done:
+ DRM_DEBUG("Counted %d %d %d\n", card_res.count_crtcs,
+ card_res.count_outputs,
+ card_res.count_modes);
+
+ if (copy_to_user(argp, &card_res, sizeof(card_res)))
+ return -EFAULT;
+
+ return retcode;
+}
+
+/**
+ * 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 inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ drm_file_t *priv = filp->private_data;
+ drm_device_t *dev = priv->head->dev;
+ struct drm_mode_crtc __user *argp = (void __user *)arg;
+ struct drm_mode_crtc crtc_resp;
+ struct drm_crtc *crtc;
+ struct drm_output *output;
+ int ocount;
+ int retcode = 0;
+
+ if (copy_from_user(&crtc_resp, argp, sizeof(crtc_resp)))
+ return -EFAULT;
+
+ crtc = idr_find(&dev->mode_config.crtc_idr, crtc_resp.crtc_id);
+ if (!crtc || (crtc->id != crtc_resp.crtc_id))
+ return -EINVAL;
+ crtc_resp.x = crtc->x;
+ crtc_resp.y = crtc->y;
+ crtc_resp.fb_id = 1;
+
+ crtc_resp.outputs = 0;
+ if (crtc->enabled) {
+
+ crtc_resp.mode = crtc->mode.mode_id;
+ ocount = 0;
+ list_for_each_entry(output, &dev->mode_config.output_list, head) {
+ if (output->crtc == crtc)
+ crtc_resp.outputs |= 1 << (ocount++);
+ }
+ } else {
+ crtc_resp.mode = 0;
+ }
+
+ if (copy_to_user(argp, &crtc_resp, sizeof(crtc_resp)))
+ return -EFAULT;
+
+ return retcode;
+}
+
+/**
+ * drm_mode_getoutput - get output configuration
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * LOCKING:
+ * Caller? (FIXME)
+ *
+ * Construct a output configuration structure to return to the user.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_getoutput(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ drm_file_t *priv = filp->private_data;
+ drm_device_t *dev = priv->head->dev;
+ struct drm_mode_get_output __user *argp = (void __user *)arg;
+ struct drm_mode_get_output out_resp;
+ struct drm_output *output;
+ struct drm_display_mode *mode;
+ int mode_count = 0;
+ int retcode = 0;
+ int copied = 0;
+
+ if (copy_from_user(&out_resp, argp, sizeof(out_resp)))
+ return -EFAULT;
+
+ DRM_DEBUG("output id %d:\n", out_resp.output);
+ output= idr_find(&dev->mode_config.crtc_idr, out_resp.output);
+ if (!output || (output->id != out_resp.output))
+ return -EINVAL;
+
+ list_for_each_entry(mode, &output->modes, head)
+ mode_count++;
+
+ strncpy(out_resp.name, output->name, DRM_OUTPUT_NAME_LEN);
+ out_resp.name[DRM_OUTPUT_NAME_LEN-1] = 0;
+
+ out_resp.mm_width = output->mm_width;
+ out_resp.mm_height = output->mm_height;
+ out_resp.subpixel = output->subpixel_order;
+ out_resp.connection = output->status;
+ if (output->crtc)
+ out_resp.crtc = output->crtc->id;
+ else
+ out_resp.crtc = 0;
+
+ if ((out_resp.count_modes >= mode_count) && mode_count) {
+ copied = 0;
+ list_for_each_entry(mode, &output->modes, head) {
+ if (put_user(mode->mode_id, &out_resp.modes[copied++])) {
+ retcode = -EFAULT;
+ goto done;
+ }
+ }
+ }
+ out_resp.count_modes = mode_count;
+
+done:
+ if (copy_to_user(argp, &out_resp, sizeof(out_resp)))
+ return -EFAULT;
+
+ return retcode;
+}
+
+/**
+ * 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 inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ drm_file_t *priv = filp->private_data;
+ drm_device_t *dev = priv->head->dev;
+ struct drm_mode_crtc __user *argp = (void __user *)arg;
+ struct drm_mode_crtc crtc_req;
+ struct drm_crtc *crtc;
+ struct drm_output **output_set = NULL, *output;
+ struct drm_display_mode *mode;
+ struct drm_framebuffer *fb = NULL;
+ int retcode = 0;
+ int i;
+
+ if (copy_from_user(&crtc_req, argp, sizeof(crtc_req)))
+ return -EFAULT;
+
+ crtc = idr_find(&dev->mode_config.crtc_idr, crtc_req.crtc_id);
+ if (!crtc || (crtc->id != crtc_req.crtc_id)) {
+ DRM_DEBUG("Unknown CRTC ID %d\n", crtc_req.crtc_id);
+ return -EINVAL;
+ }
+
+ if (crtc_req.mode) {
+
+ /* if we have a mode we need a framebuffer */
+ if (crtc_req.fb_id) {
+ fb = idr_find(&dev->mode_config.crtc_idr, crtc_req.fb_id);
+ if (!fb || (fb->id != crtc_req.fb_id)) {
+ DRM_DEBUG("Unknown FB ID%d\n", crtc_req.fb_id);
+ return -EINVAL;
+ }
+ }
+ mode = idr_find(&dev->mode_config.crtc_idr, crtc_req.mode);
+ if (!mode || (mode->mode_id != crtc_req.mode)) {
+ struct drm_output *output;
+
+ list_for_each_entry(output, &dev->mode_config.output_list, head) {
+ list_for_each_entry(mode, &output->modes, head) {
+ drm_mode_debug_printmodeline(dev, mode);
+ }
+ }
+
+ DRM_DEBUG("Unknown mode id %d, %p\n", crtc_req.mode, mode);
+ return -EINVAL;
+ }
+ } else
+ mode = NULL;
+
+ if (crtc_req.count_outputs == 0 && mode) {
+ DRM_DEBUG("Count outputs is 0 but mode set\n");
+ return -EINVAL;
+ }
+
+ if (crtc_req.count_outputs > 0 && !mode && !fb) {
+ DRM_DEBUG("Count outputs is %d but no mode or fb set\n",
crtc_req.count_outputs);
+ return -EINVAL;
+ }
+
+ if (crtc_req.count_outputs > 0) {
+ u32 out_id;
+ output_set = kmalloc(crtc_req.count_outputs * sizeof(struct
drm_output *), GFP_KERNEL);
+ if (!output_set)
+ return -ENOMEM;
+
+ for (i = 0; i < crtc_req.count_outputs; i++) {
+ if (get_user(out_id, &crtc_req.set_outputs[i]))
+ return -EFAULT;
+
+ output = idr_find(&dev->mode_config.crtc_idr, out_id);
+ if (!output || (out_id != output->id)) {
+ DRM_DEBUG("Output id %d unknown\n", out_id);
+ return -EINVAL;
+ }
+
+ output_set[i] = output;
+ }
+ }
+
+ retcode = drm_crtc_set_config(crtc, &crtc_req, mode, output_set, fb);
+ return retcode;
+}
+
+/**
+ * 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:
+ * Caller? (FIXME)
+ *
+ * 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 inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct drm_file *priv = filp->private_data;
+ struct drm_device *dev = priv->head->dev;
+ struct drm_mode_fb_cmd __user *argp = (void __user *)arg;
+ struct drm_mode_fb_cmd r;
+ struct drm_mode_config *config = &dev->mode_config;
+ struct drm_framebuffer *fb;
+ struct drm_buffer_object *bo;
+ int ret;
+
+ if (copy_from_user(&r, argp, sizeof(r)))
+ return -EFAULT;
+
+ 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;
+ }
+
+ /* TODO check limits are okay */
+ ret = drm_get_buffer_object(dev, &bo, r.handle);
+ if (ret || !bo)
+ return -EINVAL;
+
+ /* TODO check buffer is sufficently large */
+ /* TODO setup destructor callback */
+
+ fb = drm_framebuffer_create(dev);
+ if(!fb)
+ return -EINVAL;;
+
+ fb->width = r.width;
+ fb->height = r.height;
+ fb->pitch = r.pitch;
+ fb->bits_per_pixel = r.bpp;
+ fb->depth = r.depth;
+ fb->offset = bo->offset;
+ fb->bo = bo;
+
+ r.buffer_id = fb->id;
+
+ list_add(&fb->filp_head, &priv->fbs);
+ /* bind the fb to the crtc for now */
+ {
+ struct drm_crtc *crtc;
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ crtc->fb = fb;
+ }
+ }
+ if (copy_to_user(argp, &r, sizeof(r)))
+ return -EFAULT;
+
+ drmfb_probe(dev, fb);
+ return 0;
+}
+
+/**
+ * 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:
+ * Caller? (FIXME)
+ *
+ * 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 inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ drm_file_t *priv = filp->private_data;
+ drm_device_t *dev = priv->head->dev;
+ struct drm_framebuffer *fb = 0;
+ uint32_t id = arg;
+
+ fb = idr_find(&dev->mode_config.crtc_idr, id);
+ /* TODO check that we realy get a framebuffer back. */
+ if (!fb || (id != fb->id)) {
+ DRM_ERROR("mode invalid framebuffer id\n");
+ return -EINVAL;
+ }
+
+ drmfb_remove(dev, fb);
+ /* TODO check if we own the buffer */
+ /* TODO release all crtc connected to the framebuffer */
+ /* bind the fb to the crtc for now */
+ /* TODO unhock the destructor from the buffer object */
+
+ drm_framebuffer_destroy(fb);
+
+ return 0;
+}
+
+/**
+ * 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 inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ drm_file_t *priv = filp->private_data;
+ drm_device_t *dev = priv->head->dev;
+ struct drm_mode_fb_cmd __user *argp = (void __user *)arg;
+ struct drm_mode_fb_cmd r;
+ struct drm_framebuffer *fb;
+
+ if (copy_from_user(&r, argp, sizeof(r)))
+ return -EFAULT;
+
+ fb = idr_find(&dev->mode_config.crtc_idr, r.buffer_id);
+ if (!fb || (r.buffer_id != fb->id)) {
+ DRM_ERROR("invalid framebuffer id\n");
+ return -EINVAL;
+ }
+
+ r.height = fb->height;
+ r.width = fb->width;
+ r.depth = fb->depth;
+ r.bpp = fb->bits_per_pixel;
+ r.handle = fb->bo->base.hash.key;
+ r.pitch = fb->pitch;
+
+ if (copy_to_user(argp, &r, sizeof(r)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/**
+ * drm_fb_release - remove and free the FBs on this file
+ * @filp: file * from the ioctl
+ *
+ * LOCKING:
+ * Caller? (FIXME)
+ *
+ * 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)
+{
+ drm_file_t *priv = filp->private_data;
+ drm_device_t *dev = priv->head->dev;
+ struct drm_framebuffer *fb, *tfb;
+
+ list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) {
+ list_del(&fb->filp_head);
+ drmfb_remove(dev, fb);
+ drm_framebuffer_destroy(fb);
+
+ }
+}
diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h
new file mode 100644
index 0000000..a15ce0c
--- /dev/null
+++ b/linux-core/drm_crtc.h
@@ -0,0 +1,535 @@
+/*
+ * Copyright © 2006 Keith Packard
+ * Copyright © 2007 Intel Corporation
+ * Jesse Barnes <[email protected]>
+ */
+#ifndef __DRM_CRTC_H__
+#define __DRM_CRTC_H__
+
+#include <linux/i2c.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/idr.h>
+
+#include <linux/fb.h>
+
+struct drm_device;
+
+/*
+ * Note on terminology: here, for brevity and convenience, we refer to
output
+ * control chips as 'CRTCs'. They can control any type of output, VGA,
LVDS,
+ * DVI, etc. And 'screen' refers to the whole of the visible display,
which
+ * may span multiple monitors (and therefore multiple CRTC and output
+ * structures).
+ */
+
+enum drm_mode_status {
+ MODE_OK = 0, /* Mode OK */
+ MODE_HSYNC, /* hsync out of range */
+ MODE_VSYNC, /* vsync out of range */
+ MODE_H_ILLEGAL, /* mode has illegal horizontal timings */
+ MODE_V_ILLEGAL, /* mode has illegal horizontal timings */
+ MODE_BAD_WIDTH, /* requires an unsupported linepitch */
+ MODE_NOMODE, /* no mode with a maching name */
+ MODE_NO_INTERLACE, /* interlaced mode not supported */
+ MODE_NO_DBLESCAN, /* doublescan mode not supported */
+ MODE_NO_VSCAN, /* multiscan mode not supported */
+ MODE_MEM, /* insufficient video memory */
+ MODE_VIRTUAL_X, /* mode width too large for specified virtual size
*/
+ MODE_VIRTUAL_Y, /* mode height too large for specified virtual size
*/
+ MODE_MEM_VIRT, /* insufficient video memory given virtual size */
+ MODE_NOCLOCK, /* no fixed clock available */
+ MODE_CLOCK_HIGH, /* clock required is too high */
+ MODE_CLOCK_LOW, /* clock required is too low */
+ MODE_CLOCK_RANGE, /* clock/mode isn't in a ClockRange */
+ MODE_BAD_HVALUE, /* horizontal timing was out of range */
+ MODE_BAD_VVALUE, /* vertical timing was out of range */
+ MODE_BAD_VSCAN, /* VScan value out of range */
+ MODE_HSYNC_NARROW, /* horizontal sync too narrow */
+ MODE_HSYNC_WIDE, /* horizontal sync too wide */
+ MODE_HBLANK_NARROW, /* horizontal blanking too narrow */
+ MODE_HBLANK_WIDE, /* horizontal blanking too wide */
+ MODE_VSYNC_NARROW, /* vertical sync too narrow */
+ MODE_VSYNC_WIDE, /* vertical sync too wide */
+ MODE_VBLANK_NARROW, /* vertical blanking too narrow */
+ MODE_VBLANK_WIDE, /* vertical blanking too wide */
+ MODE_PANEL, /* exceeds panel dimensions */
+ MODE_INTERLACE_WIDTH, /* width too large for interlaced mode */
+ MODE_ONE_WIDTH, /* only one width is supported */
+ MODE_ONE_HEIGHT, /* only one height is supported */
+ MODE_ONE_SIZE, /* only one resolution is supported */
+ MODE_NO_REDUCED, /* monitor doesn't accept reduced blanking */
+ MODE_UNVERIFIED = -3, /* mode needs to reverified */
+ MODE_BAD = -2, /* unspecified reason */
+ MODE_ERROR = -1 /* error condition */
+};
+
+#define DRM_MODE_TYPE_BUILTIN (1<<0)
+#define DRM_MODE_TYPE_CLOCK_C ((1<<1) | DRM_MODE_TYPE_BUILTIN)
+#define DRM_MODE_TYPE_CRTC_C ((1<<2) | DRM_MODE_TYPE_BUILTIN)
+#define DRM_MODE_TYPE_PREFERRED (1<<3)
+#define DRM_MODE_TYPE_DEFAULT (1<<4)
+#define DRM_MODE_TYPE_USERDEF (1<<5)
+#define DRM_MODE_TYPE_DRIVER (1<<6)
+
+#define DRM_MODE_TYPE_CLOCK_CRTC_C (DRM_MODE_TYPE_CLOCK_C | \
+ DRM_MODE_TYPE_CRTC_C)
+
+#define DRM_MODE(nm, t, c, hd, hss, hse, ht, hsk, vd, vss, vse, vt, vs,
f) \
+ .name = nm, .status = 0, .type = (t), .clock = (c), \
+ .hdisplay = (hd), .hsync_start = (hss), .hsync_end = (hse), \
+ .htotal = (ht), .hskew = (hsk), .vdisplay = (vd), \
+ .vsync_start = (vss), .vsync_end = (vse), .vtotal = (vt), \
+ .vscan = (vs), .flags = (f), .vrefresh = 0
+
+struct drm_display_mode {
+ /* Header */
+ struct list_head head;
+ char name[DRM_DISPLAY_MODE_LEN];
+ int mode_id;
+ enum drm_mode_status status;
+ int type;
+
+ /* Proposed mode values */
+ int clock;
+ int hdisplay;
+ int hsync_start;
+ int hsync_end;
+ int htotal;
+ int hskew;
+ int vdisplay;
+ int vsync_start;
+ int vsync_end;
+ int vtotal;
+ int vscan;
+ unsigned int flags;
+
+ /* Actual mode we give to hw */
+ int clock_index;
+ int synth_clock;
+ int crtc_hdisplay;
+ int crtc_hblank_start;
+ int crtc_hblank_end;
+ int crtc_hsync_start;
+ int crtc_hsync_end;
+ int crtc_htotal;
+ int crtc_hskew;
+ int crtc_vdisplay;
+ int crtc_vblank_start;
+ int crtc_vblank_end;
+ int crtc_vsync_start;
+ int crtc_vsync_end;
+ int crtc_vtotal;
+ int crtc_hadjusted;
+ int crtc_vadjusted;
+
+ /* Driver private mode info */
+ int private_size;
+ int *private;
+ int private_flags;
+
+ int vrefresh;
+ float hsync;
+};
+
+/* Video mode flags */
+#define V_PHSYNC (1<<0)
+#define V_NHSYNC (1<<1)
+#define V_PVSYNC (1<<2)
+#define V_NVSYNC (1<<3)
+#define V_INTERLACE (1<<4)
+#define V_DBLSCAN (1<<5)
+#define V_CSYNC (1<<6)
+#define V_PCSYNC (1<<7)
+#define V_NCSYNC (1<<8)
+#define V_HSKEW (1<<9) /* hskew provided */
+#define V_BCAST (1<<10)
+#define V_PIXMUX (1<<11)
+#define V_DBLCLK (1<<12)
+#define V_CLKDIV2 (1<<13)
+
+#define CRTC_INTERLACE_HALVE_V 0x1 /* halve V values for interlacing */
+#define DPMSModeOn 0
+#define DPMSModeStandby 1
+#define DPMSModeSuspend 2
+#define DPMSModeOff 3
+
+enum drm_output_status {
+ output_status_connected,
+ output_status_disconnected,
+ output_status_unknown,
+};
+
+enum subpixel_order {
+ SubPixelUnknown = 0,
+ SubPixelHorizontalRGB,
+ SubPixelHorizontalBGR,
+ SubPixelVerticalRGB,
+ SubPixelVerticalBGR,
+ SubPixelNone,
+};
+
+/*
+ * Describes a given display (e.g. CRT or flat panel) and its
limitations.
+ */
+struct drm_display_info {
+ char name[DRM_DISPLAY_INFO_LEN];
+ /* Input info */
+ bool serration_vsync;
+ bool sync_on_green;
+ bool composite_sync;
+ bool separate_syncs;
+ bool blank_to_black;
+ unsigned char video_level;
+ bool digital;
+ /* Physical size */
+ unsigned int width_mm;
+ unsigned int height_mm;
+
+ /* Display parameters */
+ unsigned char gamma; /* FIXME: storage format */
+ bool gtf_supported;
+ bool standard_color;
+ enum {
+ monochrome,
+ rgb,
+ other,
+ unknown,
+ } display_type;
+ bool active_off_supported;
+ bool suspend_supported;
+ bool standby_supported;
+
+ /* Color info FIXME: storage format */
+ unsigned short redx, redy;
+ unsigned short greenx, greeny;
+ unsigned short bluex, bluey;
+ unsigned short whitex, whitey;
+
+ /* Clock limits FIXME: storage format */
+ unsigned int min_vfreq, max_vfreq;
+ unsigned int min_hfreq, max_hfreq;
+ unsigned int pixel_clock;
+
+ /* White point indices FIXME: storage format */
+ unsigned int wpx1, wpy1;
+ unsigned int wpgamma1;
+ unsigned int wpx2, wpy2;
+ unsigned int wpgamma2;
+
+ /* Preferred mode (if any) */
+ struct drm_display_mode *preferred_mode;
+ char *raw_edid; /* if any */
+};
+
+struct drm_framebuffer {
+ struct drm_device *dev;
+ struct list_head head;
+ int id; /* idr assigned */
+ unsigned int pitch;
+ unsigned long offset;
+ unsigned int width;
+ unsigned int height;
+ /* depth can be 15 or 16 */
+ unsigned int depth;
+ int bits_per_pixel;
+ int flags;
+ struct drm_buffer_object *bo;
+ void *fbdev;
+ u32 pseudo_palette[17];
+ void *virtual_base;
+ struct list_head filp_head;
+};
+struct drm_crtc;
+struct drm_output;
+
+/**
+ * drm_crtc_funcs - control CRTCs for a given device
+ * @dpms: control display power levels
+ * @save: save CRTC state
+ * @resore: restore CRTC state
+ * @lock: lock the CRTC
+ * @unlock: unlock the CRTC
+ * @shadow_allocate: allocate shadow pixmap
+ * @shadow_create: create shadow pixmap for rotation support
+ * @shadow_destroy: free shadow pixmap
+ * @mode_fixup: fixup proposed mode
+ * @mode_set: set the desired mode on the CRTC
+ * @gamma_set: specify color ramp for CRTC
+ * @cleanup: cleanup driver private state prior to close
+ *
+ * The drm_crtc_funcs structure is the central CRTC management
structure
+ * in the DRM. Each CRTC controls one or more outputs (note that the
name
+ * CRTC is simply historical, a CRTC may control LVDS, VGA, DVI, TV
out, etc.
+ * outputs, not just CRTs).
+ *
+ * Each driver is responsible for filling out this structure at startup
time,
+ * in addition to providing other modesetting features, like i2c and
DDC
+ * bus accessors.
+ */
+struct drm_crtc_funcs {
+ /*
+ * Control power levels on the CRTC. If the mode passed in is
+ * unsupported, the provider must use the next lowest power level.
+ */
+ void (*dpms)(struct drm_crtc *crtc, int mode);
+
+ /* JJJ: Are these needed? */
+ /* Save CRTC state */
+ void (*save)(struct drm_crtc *crtc); /* suspend? */
+ /* Restore CRTC state */
+ void (*restore)(struct drm_crtc *crtc); /* resume? */
+ bool (*lock)(struct drm_crtc *crtc);
+ void (*unlock)(struct drm_crtc *crtc);
+
+ void (*prepare)(struct drm_crtc *crtc);
+ void (*commit)(struct drm_crtc *crtc);
+
+ /* Provider can fixup or change mode timings before modeset occurs */
+ bool (*mode_fixup)(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
+ /* Actually set the mode */
+ void (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode, int x, int y);
+ /* Set gamma on the CRTC */
+ void (*gamma_set)(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,
+ int size);
+ /* Driver cleanup routine */
+ void (*cleanup)(struct drm_crtc *crtc);
+};
+
+/**
+ * drm_crtc - central CRTC control structure
+ * @enabled: is this CRTC enabled?
+ * @x: x position on screen
+ * @y: y position on screen
+ * @desired_mode: new desired mode
+ * @desired_x: desired x for desired_mode
+ * @desired_y: desired y for desired_mode
+ * @funcs: CRTC control functions
+ * @driver_private: arbitrary driver data
+ *
+ * Each CRTC may have one or more outputs associated with it. This
structure
+ * allows the CRTC to be controlled.
+ */
+struct drm_crtc {
+ struct drm_device *dev;
+ struct list_head head;
+
+ int id; /* idr assigned */
+
+ /* framebuffer the CRTC is currently bound to */
+ struct drm_framebuffer *fb;
+
+ bool enabled;
+
+ /* JJJ: are these needed? */
+ bool cursor_in_range;
+ bool cursor_shown;
+
+ struct drm_display_mode mode;
+
+ int x, y;
+ struct drm_display_mode *desired_mode;
+ int desired_x, desired_y;
+ const struct drm_crtc_funcs *funcs;
+ void *driver_private;
+
+ /* RRCrtcPtr randr_crtc? */
+};
+
+extern struct drm_crtc *drm_crtc_create(struct drm_device *dev,
+ const struct drm_crtc_funcs *funcs);
+
+/**
+ * drm_output_funcs - control outputs on a given device
+ * @init: setup this output
+ * @dpms: set power state (see drm_crtc_funcs above)
+ * @save: save output state
+ * @restore: restore output state
+ * @mode_valid: is this mode valid on the given output?
+ * @mode_fixup: try to fixup proposed mode for this output
+ * @mode_set: set this mode
+ * @detect: is this output active?
+ * @get_modes: get mode list for this output
+ * @set_property: property for this output may need update
+ * @cleanup: output is going away, cleanup
+ *
+ * Each CRTC may have one or more outputs attached to it. The
functions
+ * below allow the core DRM code to control outputs, enumerate
available modes,
+ * etc.
+ */
+struct drm_output_funcs {
+ void (*init)(struct drm_output *output);
+ void (*dpms)(struct drm_output *output, int mode);
+ void (*save)(struct drm_output *output);
+ void (*restore)(struct drm_output *output);
+ int (*mode_valid)(struct drm_output *output,
+ struct drm_display_mode *mode);
+ bool (*mode_fixup)(struct drm_output *output,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
+ void (*prepare)(struct drm_output *output);
+ void (*commit)(struct drm_output *output);
+ void (*mode_set)(struct drm_output *output,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
+ enum drm_output_status (*detect)(struct drm_output *output);
+ int (*get_modes)(struct drm_output *output);
+ /* JJJ: type checking for properties via property value type */
+ bool (*set_property)(struct drm_output *output, int prop, void *val);
+ void (*cleanup)(struct drm_output *output);
+};
+
+#define DRM_OUTPUT_LEN 32
+/**
+ * drm_output - central DRM output control structure
+ * @crtc: CRTC this output is currently connected to, NULL if none
+ * @possible_crtcs: bitmap of CRTCS this output could be attached to
+ * @possible_clones: bitmap of possible outputs this output could clone
+ * @interlace_allowed: can this output handle interlaced modes?
+ * @doublescan_allowed: can this output handle doublescan?
+ * @available_modes: modes available on this output (from get_modes() +
user)
+ * @initial_x: initial x position for this output
+ * @initial_y: initial y position for this output
+ * @status: output connected?
+ * @subpixel_order: for this output
+ * @mm_width: displayable width of output in mm
+ * @mm_height: displayable height of output in mm
+ * @name: name of output (should be one of a few standard names)
+ * @funcs: output control functions
+ * @driver_private: private driver data
+ *
+ * Each output may be connected to one or more CRTCs, or may be
clonable by
+ * another output if they can share a CRTC. Each output also has a
specific
+ * position in the broader display (referred to as a 'screen' though it
could
+ * span multiple monitors).
+ */
+struct drm_output {
+ struct drm_device *dev;
+ struct list_head head;
+ struct drm_crtc *crtc;
+ int id; /* idr assigned */
+ unsigned long possible_crtcs;
+ unsigned long possible_clones;
+ bool interlace_allowed;
+ bool doublescan_allowed;
+ spinlock_t modes_lock; /* protects modes and probed_modes lists */
+ struct list_head modes; /* list of modes on this output */
+ /*
+ OptionInfoPtr options;
+ XF86ConfMonitorPtr conf_monitor;
+ */
+ int initial_x, initial_y;
+ enum drm_output_status status;
+
+ /* these are modes added by probing with DDC or the BIOS */
+ struct list_head probed_modes;
+
+ /* xf86MonPtr MonInfo; */
+ enum subpixel_order subpixel_order;
+ int mm_width, mm_height;
+ struct drm_display_info *monitor_info; /* if any */
+ char name[DRM_OUTPUT_LEN];
+ const struct drm_output_funcs *funcs;
+ void *driver_private;
+ /* RROutputPtr randr_output? */
+};
+
+/**
+ * struct drm_mode_config_funcs - configure CRTCs for a given screen
layout
+ * @resize: adjust CRTCs as necessary for the proposed layout
+ *
+ * Currently only a resize hook is available. DRM will call back into
the
+ * driver with a new screen width and height. If the driver can't
support
+ * the proposed size, it can return false. Otherwise it should adjust
+ * the CRTC<->output mappings as needed and update its view of the
screen.
+ */
+struct drm_mode_config_funcs {
+ bool (*resize)(struct drm_device *dev, int width, int height);
+};
+
+/**
+ * drm_mode_config - Mode configuration control structure
+ *
+ */
+struct drm_mode_config {
+ spinlock_t config_lock; /* protects configuration and IDR */
+ struct idr crtc_idr; /* use this idr for all IDs, fb, crtc, output,
modes - just makes life easier */
+ /* this is limited to one for now */
+ int num_fb;
+ struct list_head fb_list;
+ int num_output;
+ struct list_head output_list;
+
+ /* int compat_output? */
+ int num_crtc;
+ struct list_head crtc_list;
+
+ int min_width, min_height;
+ int max_width, max_height;
+ /* DamagePtr rotationDamage? */
+ /* DGA stuff? */
+ struct drm_mode_config_funcs *funcs;
+ unsigned long fb_base;
+};
+
+struct drm_output *drm_output_create(struct drm_device *dev,
+ const struct drm_output_funcs *funcs,
+ const char *name);
+extern void drm_output_destroy(struct drm_output *output);
+extern bool drm_output_rename(struct drm_output *output, const char
*name);
+
+extern int drm_add_edid_modes(struct drm_output *output,
+ struct i2c_adapter *adapter);
+extern void drm_mode_probed_add(struct drm_output *output, struct
drm_display_mode *mode);
+extern void drm_mode_remove(struct drm_output *output, struct
drm_display_mode *mode);
+extern struct drm_display_mode *drm_mode_duplicate(struct drm_device
*dev,
+ struct drm_display_mode *mode);
+extern void drm_mode_debug_printmodeline(struct drm_device *dev,
+ struct drm_display_mode *mode);
+extern void drm_mode_config_init(struct drm_device *dev);
+extern void drm_mode_config_cleanup(struct drm_device *dev);
+extern void drm_mode_set_name(struct drm_display_mode *mode);
+extern void drm_disable_unused_functions(struct drm_device *dev);
+
+extern struct drm_display_mode *drm_mode_create(struct drm_device
*dev);
+extern void drm_mode_destroy(struct drm_device *dev, struct
drm_display_mode *mode);
+extern void drm_mode_list_concat(struct list_head *head,
+ struct list_head *new);
+extern void drm_mode_validate_size(struct drm_device *dev,
+ struct list_head *mode_list,
+ int maxX, int maxY, int maxPitch);
+extern void drm_mode_prune_invalid(struct drm_device *dev,
+ struct list_head *mode_list, bool verbose);
+extern void drm_mode_sort(struct list_head *mode_list);
+extern int drm_mode_vrefresh(struct drm_display_mode *mode);
+extern void drm_mode_set_crtcinfo(struct drm_display_mode *p,
+ int adjust_flags);
+extern struct drm_display_mode *drm_crtc_mode_create(struct drm_device
*dev);
+extern bool drm_initial_config(struct drm_device *dev, bool cangrow);
+extern void drm_framebuffer_set_object(struct drm_device *dev,
+ unsigned long handle);
+extern bool drm_set_desired_modes(struct drm_device *dev);
+extern int drmfb_probe(struct drm_device *dev, struct drm_framebuffer
*fb);
+extern int drmfb_remove(struct drm_device *dev, struct drm_framebuffer
*fb);
+
+/* IOCTLs */
+extern int drm_mode_getresources(struct inode *inode, struct file
*filp,
+ unsigned int cmd, unsigned long arg);
+
+extern int drm_mode_getcrtc(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int drm_mode_getoutput(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int drm_mode_setcrtc(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int drm_mode_addfb(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int drm_mode_rmfb(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int drm_mode_getfb(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+#endif /* __DRM_CRTC_H__ */
+
diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c
index e5788d7..5aa7137 100644
--- a/linux-core/drm_drv.c
+++ b/linux-core/drm_drv.c
@@ -123,6 +123,13 @@ static drm_ioctl_desc_t drm_ioctls[] = {
DRM_AUTH },

[DRM_IOCTL_NR(DRM_IOCTL_UPDATE_DRAW)] = {drm_update_drawable_info,
DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY},
+ [DRM_IOCTL_NR(DRM_IOCTL_MODE_GETRESOURCES)] = {drm_mode_getresources,
DRM_MASTER|DRM_ROOT_ONLY},
+ [DRM_IOCTL_NR(DRM_IOCTL_MODE_GETCRTC)] = {drm_mode_getcrtc,
DRM_MASTER|DRM_ROOT_ONLY},
+ [DRM_IOCTL_NR(DRM_IOCTL_MODE_GETOUTPUT)] = {drm_mode_getoutput,
DRM_MASTER|DRM_ROOT_ONLY},
+ [DRM_IOCTL_NR(DRM_IOCTL_MODE_SETCRTC)] = {drm_mode_setcrtc,
DRM_MASTER|DRM_ROOT_ONLY},
+ [DRM_IOCTL_NR(DRM_IOCTL_MODE_ADDFB)] = {drm_mode_addfb, DRM_MASTER|
DRM_ROOT_ONLY},
+ [DRM_IOCTL_NR(DRM_IOCTL_MODE_RMFB)] = {drm_mode_rmfb, DRM_MASTER|
DRM_ROOT_ONLY},
+ [DRM_IOCTL_NR(DRM_IOCTL_MODE_GETFB)] = {drm_mode_getfb, DRM_MASTER|
DRM_ROOT_ONLY},
};

#define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls )
@@ -140,7 +147,7 @@ static drm_ioctl_desc_t drm_ioctls[] = {
int drm_lastclose(drm_device_t * dev)
{
drm_magic_entry_t *pt, *next;
- drm_map_list_t *r_list;
+ drm_map_list_t *r_list, *r_list_tmp;
drm_vma_entry_t *vma, *vma_next;
int i;

@@ -150,8 +157,6 @@ int drm_lastclose(drm_device_t * dev)
* We can't do much about this function failing.
*/

- drm_bo_driver_finish(dev);
-
if (dev->driver->lastclose)
dev->driver->lastclose(dev);
DRM_DEBUG("driver lastclose completed\n");
@@ -233,10 +238,9 @@ int drm_lastclose(drm_device_t * dev)
}

if (dev->maplist) {
- while (!list_empty(&dev->maplist->head)) {
- struct list_head *list = dev->maplist->head.next;
- r_list = list_entry(list, drm_map_list_t, head);
- drm_rmmap_locked(dev, r_list->map);
+ list_for_each_entry_safe(r_list, r_list_tmp,
&dev->maplist->head, head) {
+ if (!(r_list->map->flags & _DRM_DRIVER))
+ drm_rmmap_locked(dev, r_list->map);
}
}

@@ -260,8 +264,7 @@ int drm_lastclose(drm_device_t * dev)
if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))
drm_dma_takedown(dev);

- if (dev->lock.hw_lock) {
- dev->sigdata.lock = dev->lock.hw_lock = NULL; /* SHM removed */
+ if (dev->lock.filp) {
dev->lock.filp = NULL;
wake_up_interruptible(&dev->lock.lock_queue);
}
@@ -372,17 +375,6 @@ static void drm_cleanup(drm_device_t * dev)
drm_lastclose(dev);
drm_fence_manager_takedown(dev);

- if (dev->maplist) {
- drm_free(dev->maplist, sizeof(*dev->maplist), DRM_MEM_MAPS);
- dev->maplist = NULL;
- drm_ht_remove(&dev->map_hash);
- drm_mm_takedown(&dev->offset_manager);
- drm_ht_remove(&dev->object_hash);
- }
-
- if (!drm_fb_loaded)
- pci_disable_device(dev->pdev);
-
drm_ctxbitmap_cleanup(dev);

if (drm_core_has_MTRR(dev) && drm_core_has_AGP(dev) && dev->agp
@@ -394,12 +386,26 @@ static void drm_cleanup(drm_device_t * dev)
DRM_DEBUG("mtrr_del=%d\n", retval);
}

+ if (dev->driver->unload)
+ dev->driver->unload(dev);
+
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);
+
+
+ // drm_bo_driver_finish(dev);
+ if (dev->maplist) {
+ drm_free(dev->maplist, sizeof(*dev->maplist), DRM_MEM_MAPS);
+ dev->maplist = NULL;
+ drm_ht_remove(&dev->map_hash);
+ drm_mm_takedown(&dev->offset_manager);
+ drm_ht_remove(&dev->object_hash);
+ }
+
+ if (!drm_fb_loaded)
+ pci_disable_device(dev->pdev);

drm_put_head(&dev->primary);
if (drm_put_dev(dev))
@@ -622,45 +628,3 @@ err_i1:
return retcode;
}
EXPORT_SYMBOL(drm_ioctl);
-
-int drm_wait_on(drm_device_t *dev, wait_queue_head_t *queue, int
timeout,
- int (*fn)(drm_device_t *dev, void *priv), void *priv)
-{
- DECLARE_WAITQUEUE(entry, current);
- unsigned long end = jiffies + (timeout);
- int ret = 0;
- add_wait_queue(queue, &entry);
-
- for (;;) {
- __set_current_state(TASK_INTERRUPTIBLE);
- if ((*fn)(dev, priv))
- break;
- if (time_after_eq(jiffies, end)) {
- ret = -EBUSY;
- break;
- }
- schedule_timeout((HZ/100 > 1) ? HZ/100 : 1);
- if (signal_pending(current)) {
- ret = -EINTR;
- break;
- }
- }
- __set_current_state(TASK_RUNNING);
- remove_wait_queue(queue, &entry);
- return ret;
-}
-EXPORT_SYMBOL(drm_wait_on);
-
-drm_local_map_t *drm_getsarea(struct drm_device *dev)
-{
- drm_map_list_t *entry;
-
- list_for_each_entry(entry, &dev->maplist->head, head) {
- if (entry->map && entry->map->type == _DRM_SHM &&
- (entry->map->flags & _DRM_CONTAINS_LOCK)) {
- return entry->map;
- }
- }
- return NULL;
-}
-EXPORT_SYMBOL(drm_getsarea);
diff --git a/linux-core/drm_edid.c b/linux-core/drm_edid.c
new file mode 100644
index 0000000..0d06792
--- /dev/null
+++ b/linux-core/drm_edid.c
@@ -0,0 +1,467 @@
+/*
+ * Copyright (c) 2007 Intel Corporation
+ * Jesse Barnes <[email protected]>
+ *
+ * DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid)
originally from
+ * FB layer.
+ * Copyright (C) 2006 Dennis Munsie <[email protected]>
+ */
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include "drmP.h"
+#include "drm_edid.h"
+
+/* Valid EDID header has these bytes */
+static u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00 };
+
+/**
+ * edid_valid - sanity check EDID data
+ * @edid: EDID data
+ *
+ * Sanity check the EDID block by looking at the header, the version
number
+ * and the checksum. Return 0 if the EDID doesn't check out, or 1 if
it's
+ * valid.
+ */
+static bool edid_valid(struct edid *edid)
+{
+ int i;
+ u8 csum = 0;
+ u8 *raw_edid = (u8 *)edid;
+
+ if (memcmp(edid->header, edid_header, sizeof(edid_header)))
+ goto bad;
+ if (edid->version != 1)
+ goto bad;
+ if (edid->revision <= 0 || edid->revision > 3)
+ goto bad;
+
+ for (i = 0; i < EDID_LENGTH; i++)
+ csum += raw_edid[i];
+ if (csum)
+ goto bad;
+
+ return 1;
+
+bad:
+ return 0;
+}
+
+/**
+ * drm_mode_std - convert standard mode info (width, height, refresh)
into mode
+ * @t: standard timing params
+ *
+ * Take the standard timing params (in this case width, aspect, and
refresh)
+ * and convert them into a real mode using CVT.
+ *
+ * Punts for now, but should eventually use the FB layer's CVT based
mode
+ * generation code.
+ */
+struct drm_display_mode *drm_mode_std(struct drm_device *dev,
+ struct std_timing *t)
+{
+// struct fb_videomode mode;
+
+// fb_find_mode_cvt(&mode, 0, 0);
+ /* JJJ: convert to drm_display_mode */
+ struct drm_display_mode *mode;
+ int hsize = t->hsize * 8 + 248, vsize;
+
+ mode = drm_mode_create(dev);
+ if (!mode)
+ return NULL;
+
+ if (t->aspect_ratio == 0)
+ vsize = (hsize * 10) / 16;
+ else if (t->aspect_ratio == 1)
+ vsize = (hsize * 3) / 4;
+ else if (t->aspect_ratio == 2)
+ vsize = (hsize * 4) / 5;
+ else
+ vsize = (hsize * 9) / 16;
+
+ drm_mode_set_name(mode);
+
+ return mode;
+}
+
+/**
+ * drm_mode_detailed - create a new mode from an EDID detailed timing
section
+ * @timing: EDID detailed timing info
+ * @preferred: is this a preferred mode?
+ *
+ * An EDID detailed timing block contains enough info for us to create
and
+ * return a new struct drm_display_mode. The @preferred flag will be
set
+ * if this is the display's preferred timing, and we'll use it to
indicate
+ * to the other layers that this mode is desired.
+ */
+struct drm_display_mode *drm_mode_detailed(drm_device_t *dev,
+ struct detailed_timing *timing)
+{
+ struct drm_display_mode *mode;
+ struct detailed_pixel_timing *pt = &timing->data.pixel_data;
+
+ if (pt->stereo) {
+ printk(KERN_WARNING "stereo mode not supported\n");
+ return NULL;
+ }
+ if (!pt->separate_sync) {
+ printk(KERN_WARNING "integrated sync not supported\n");
+ return NULL;
+ }
+
+ mode = drm_mode_create(dev);
+ if (!mode)
+ return NULL;
+
+ mode->type = DRM_MODE_TYPE_DRIVER;
+ mode->clock = timing->pixel_clock * 10;
+
+ mode->hdisplay = (pt->hactive_hi << 8) | pt->hactive_lo;
+ mode->hsync_start = mode->hdisplay + ((pt->hsync_offset_hi << 8) |
+ pt->hsync_offset_lo);
+ mode->hsync_end = mode->hsync_start +
+ ((pt->hsync_pulse_width_hi << 8) |
+ pt->hsync_pulse_width_lo);
+ mode->htotal = mode->hdisplay + ((pt->hblank_hi << 8) |
pt->hblank_lo);
+
+ mode->vdisplay = (pt->vactive_hi << 8) | pt->vactive_lo;
+ mode->vsync_start = mode->vdisplay + ((pt->vsync_offset_hi << 8) |
+ pt->vsync_offset_lo);
+ mode->vsync_end = mode->vsync_start +
+ ((pt->vsync_pulse_width_hi << 8) |
+ pt->vsync_pulse_width_lo);
+ mode->vtotal = mode->vdisplay + ((pt->vblank_hi << 8) |
pt->vblank_lo);
+
+ drm_mode_set_name(mode);
+
+ if (pt->interlaced)
+ mode->flags |= V_INTERLACE;
+
+ mode->flags |= pt->hsync_positive ? V_PHSYNC : V_NHSYNC;
+ mode->flags |= pt->vsync_positive ? V_PVSYNC : V_NVSYNC;
+
+ return mode;
+}
+
+/*
+ * Detailed mode info for the EDID "established modes" data to use.
+ */
+static struct drm_display_mode edid_est_modes[] = {
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
+ 968, 1056, 0, 600, 601, 605, 628, 0,
+ V_PHSYNC | V_PVSYNC) }, /* 800x600@60Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824,
+ 896, 1024, 0, 600, 601, 603, 625, 0,
+ V_PHSYNC | V_PVSYNC) }, /* 800x600@56Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656,
+ 720, 840, 0, 480, 481, 484, 500, 0,
+ V_NHSYNC | V_NVSYNC) }, /* 640x480@75Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664,
+ 704, 832, 0, 480, 489, 491, 520, 0,
+ V_NHSYNC | V_NVSYNC) }, /* 640x480@72Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 30240, 640, 704,
+ 768, 864, 0, 480, 483, 486, 525, 0,
+ V_NHSYNC | V_NVSYNC) }, /* 640x480@67Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25200, 640, 656,
+ 752, 800, 0, 480, 490, 492, 525, 0,
+ V_NHSYNC | V_NVSYNC) }, /* 640x480@60Hz */
+ { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 738,
+ 846, 900, 0, 400, 421, 423, 449, 0,
+ V_NHSYNC | V_NVSYNC) }, /* 720x400@88Hz */
+ { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738,
+ 846, 900, 0, 400, 412, 414, 449, 0,
+ V_NHSYNC | V_PVSYNC) }, /* 720x400@70Hz */
+ { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296,
+ 1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
+ V_PHSYNC | V_PVSYNC) }, /* 1280x1024@75Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78800, 1024, 1040,
+ 1136, 1312, 0, 768, 769, 772, 800, 0,
+ V_PHSYNC | V_PVSYNC) }, /* 1024x768@75Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048,
+ 1184, 1328, 0, 768, 771, 777, 806, 0,
+ V_NHSYNC | V_NVSYNC) }, /* 1024x768@70Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
+ 1184, 1344, 0, 768, 771, 777, 806, 0,
+ V_NHSYNC | V_NVSYNC) }, /* 1024x768@60Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032,
+ 1208, 1264, 0, 768, 768, 776, 817, 0,
+ V_PHSYNC | V_PVSYNC | V_INTERLACE) }, /* 1024x768@43Hz */
+ { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864,
+ 928, 1152, 0, 624, 625, 628, 667, 0,
+ V_NHSYNC | V_NVSYNC) }, /* 832x624@75Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816,
+ 896, 1056, 0, 600, 601, 604, 625, 0,
+ V_PHSYNC | V_PVSYNC) }, /* 800x600@75Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856,
+ 976, 1040, 0, 600, 637, 643, 666, 0,
+ V_PHSYNC | V_PVSYNC) }, /* 800x600@72Hz */
+ { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
+ 1344, 1600, 0, 864, 865, 868, 900, 0,
+ V_PHSYNC | V_PVSYNC) }, /* 1152x864@75Hz */
+};
+
+#define EDID_EST_TIMINGS 16
+#define EDID_STD_TIMINGS 8
+#define EDID_DETAILED_TIMINGS 4
+
+/**
+ * add_established_modes - get est. modes from EDID and add them
+ * @edid: EDID block to scan
+ *
+ * Each EDID block contains a bitmap of the supported "established
modes" list
+ * (defined above). Tease them out and add them to the global modes
list.
+ */
+static int add_established_modes(struct drm_output *output, struct edid
*edid)
+{
+ struct drm_device *dev = output->dev;
+ unsigned long est_bits = edid->established_timings.t1 |
+ (edid->established_timings.t2 << 8) |
+ ((edid->established_timings.mfg_rsvd & 0x80) << 9);
+ int i, modes = 0;
+
+ for (i = 0; i <= EDID_EST_TIMINGS; i++)
+ if (est_bits & (1<<i)) {
+ struct drm_display_mode *newmode;
+ newmode = drm_mode_duplicate(dev, &edid_est_modes[i]);
+ drm_mode_probed_add(output, newmode);
+ modes++;
+ }
+
+ return modes;
+}
+
+/**
+ * add_standard_modes - get std. modes from EDID and add them
+ * @edid: EDID block to scan
+ *
+ * Standard modes can be calculated using the CVT standard. Grab them
from
+ * @edid, calculate them, and add them to the list.
+ */
+static int add_standard_modes(struct drm_output *output, struct edid
*edid)
+{
+ struct drm_device *dev = output->dev;
+ int i, modes = 0;
+
+ for (i = 0; i < EDID_STD_TIMINGS; i++) {
+ struct std_timing *t = &edid->standard_timings[i];
+ struct drm_display_mode *newmode;
+
+ /* If std timings bytes are 1, 1 it's empty */
+ if (t->hsize == 1 && (t->aspect_ratio | t->vfreq) == 1)
+ continue;
+
+ newmode = drm_mode_std(dev, &edid->standard_timings[i]);
+ drm_mode_probed_add(output, newmode);
+ modes++;
+ }
+
+ return modes;
+}
+
+/**
+ * add_detailed_modes - get detailed mode info from EDID data
+ * @edid: EDID block to scan
+ *
+ * Some of the detailed timing sections may contain mode information.
Grab
+ * it and add it to the list.
+ */
+static int add_detailed_info(struct drm_output *output, struct edid
*edid)
+{
+ struct drm_device *dev = output->dev;
+ int i, j, modes = 0;
+
+ for (i = 0; i < EDID_DETAILED_TIMINGS; i++) {
+ struct detailed_timing *timing = &edid->detailed_timings[i];
+ struct detailed_non_pixel *data = &timing->data.other_data;
+ struct drm_display_mode *newmode;
+
+ /* EDID up to and including 1.2 may put monitor info here */
+ if (edid->version == 1 && edid->revision < 3)
+ continue;
+
+ /* Detailed mode timing */
+ if (timing->pixel_clock) {
+ newmode = drm_mode_detailed(dev, timing);
+ /* First detailed mode is preferred */
+ if (i == 0 && edid->preferred_timing)
+ newmode->type |= DRM_MODE_TYPE_PREFERRED;
+ drm_mode_probed_add(output, newmode);
+
+ modes++;
+ continue;
+ }
+
+ /* Other timing or info */
+ switch (data->type) {
+ case EDID_DETAIL_MONITOR_SERIAL:
+ break;
+ case EDID_DETAIL_MONITOR_STRING:
+ break;
+ case EDID_DETAIL_MONITOR_RANGE:
+ /* Get monitor range data */
+ break;
+ case EDID_DETAIL_MONITOR_NAME:
+ break;
+ case EDID_DETAIL_MONITOR_CPDATA:
+ break;
+ case EDID_DETAIL_STD_MODES:
+ /* Five modes per detailed section */
+ for (j = 0; j < 5; i++) {
+ struct std_timing *std;
+ struct drm_display_mode *newmode;
+
+ std = &data->data.timings[j];
+ newmode = drm_mode_std(dev, std);
+ drm_mode_probed_add(output, newmode);
+ modes++;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return modes;
+}
+
+#define DDC_ADDR 0x50
+
+static unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter
*adapter)
+{
+ unsigned char start = 0x0;
+ unsigned char *buf = kmalloc(EDID_LENGTH, GFP_KERNEL);
+ struct i2c_msg msgs[] = {
+ {
+ .addr = DDC_ADDR,
+ .flags = 0,
+ .len = 1,
+ .buf = &start,
+ }, {
+ .addr = DDC_ADDR,
+ .flags = I2C_M_RD,
+ .len = EDID_LENGTH,
+ .buf = buf,
+ }
+ };
+
+ if (!buf) {
+ dev_warn(&adapter->dev, "unable to allocate memory for EDID "
+ "block.\n");
+ return NULL;
+ }
+
+ if (i2c_transfer(adapter, msgs, 2) == 2)
+ return buf;
+
+ dev_info(&adapter->dev, "unable to read EDID block.\n");
+ kfree(buf);
+ return NULL;
+}
+
+static unsigned char *drm_ddc_read(struct i2c_adapter *adapter)
+{
+ struct i2c_algo_bit_data *algo_data = adapter->algo_data;
+ unsigned char *edid = NULL;
+ int i, j;
+
+ /*
+ * Startup the bus:
+ * Set clock line high (but give it time to come up)
+ * Then set clock & data low
+ */
+ algo_data->setscl(algo_data->data, 1);
+ udelay(550); /* startup delay */
+ algo_data->setscl(algo_data->data, 0);
+ algo_data->setsda(algo_data->data, 0);
+
+ for (i = 0; i < 3; i++) {
+ /* For some old monitors we need the
+ * following process to initialize/stop DDC
+ */
+ algo_data->setsda(algo_data->data, 0);
+ msleep(13);
+
+ algo_data->setscl(algo_data->data, 1);
+ for (j = 0; j < 5; j++) {
+ msleep(10);
+ if (algo_data->getscl(algo_data->data))
+ break;
+ }
+ if (j == 5)
+ continue;
+
+ algo_data->setsda(algo_data->data, 0);
+ msleep(15);
+ algo_data->setscl(algo_data->data, 0);
+ msleep(15);
+ algo_data->setsda(algo_data->data, 1);
+ msleep(15);
+
+ /* Do the real work */
+ edid = drm_do_probe_ddc_edid(adapter);
+ algo_data->setsda(algo_data->data, 0);
+ algo_data->setscl(algo_data->data, 0);
+ msleep(15);
+
+ algo_data->setscl(algo_data->data, 1);
+ for (j = 0; j < 10; j++) {
+ msleep(10);
+ if (algo_data->getscl(algo_data->data))
+ break;
+ }
+
+ algo_data->setsda(algo_data->data, 1);
+ msleep(15);
+ algo_data->setscl(algo_data->data, 0);
+ if (edid)
+ break;
+ }
+ /* Release the DDC lines when done or the Apple Cinema HD display
+ * will switch off
+ */
+ algo_data->setsda(algo_data->data, 0);
+ algo_data->setscl(algo_data->data, 0);
+ algo_data->setscl(algo_data->data, 1);
+
+ return edid;
+}
+
+/**
+ * drm_add_edid_modes - add modes from EDID data, if available
+ * @output: output we're probing
+ * @adapter: i2c adapter to use for DDC
+ *
+ * Poke the given output's i2c channel to grab EDID data if possible.
If we
+ * get any, add the specified modes to the output's mode list.
+ *
+ * Return number of modes added or 0 if we couldn't find any.
+ */
+int drm_add_edid_modes(struct drm_output *output, struct i2c_adapter
*adapter)
+{
+ struct edid *edid;
+ int num_modes = 0;
+
+ edid = (struct edid *)drm_ddc_read(adapter);
+ if (!edid) {
+ dev_warn(&output->dev->pdev->dev, "%s: no EDID data\n",
+ output->name);
+ goto out_err;
+ }
+
+ if (!edid_valid(edid)) {
+ dev_warn(&output->dev->pdev->dev, "%s: EDID invalid.\n",
+ output->name);
+ goto out_err;
+ }
+
+ num_modes += add_established_modes(output, edid);
+ num_modes += add_standard_modes(output, edid);
+ num_modes += add_detailed_info(output, edid);
+
+ return num_modes;
+
+out_err:
+ kfree(edid);
+ return 0;
+}
+EXPORT_SYMBOL(drm_add_edid_modes);
diff --git a/linux-core/drm_edid.h b/linux-core/drm_edid.h
new file mode 100644
index 0000000..0d2eeaa
--- /dev/null
+++ b/linux-core/drm_edid.h
@@ -0,0 +1,176 @@
+#ifndef __DRM_EDID_H__
+#define __DRM_EDID_H__
+
+#include <linux/types.h>
+
+#define EDID_LENGTH 128
+#define DDC_ADDR 0x50
+
+#ifdef BIG_ENDIAN
+#error "EDID structure is little endian, need big endian versions"
+#endif
+
+struct est_timings {
+ u8 t1;
+ u8 t2;
+ u8 mfg_rsvd;
+} __attribute__((packed));
+
+struct std_timing {
+ u8 hsize; /* need to multiply by 8 then add 248 */
+ u8 vfreq:6; /* need to add 60 */
+ u8 aspect_ratio:2; /* 00=16:10, 01=4:3, 10=5:4, 11=16:9 */
+} __attribute__((packed));
+
+/* If detailed data is pixel timing */
+struct detailed_pixel_timing {
+ u8 hactive_lo;
+ u8 hblank_lo;
+ u8 hblank_hi:4;
+ u8 hactive_hi:4;
+ u8 vactive_lo;
+ u8 vblank_lo;
+ u8 vblank_hi:4;
+ u8 vactive_hi:4;
+ u8 hsync_offset_lo;
+ u8 hsync_pulse_width_lo;
+ u8 vsync_pulse_width_lo:4;
+ u8 vsync_offset_lo:4;
+ u8 hsync_pulse_width_hi:2;
+ u8 hsync_offset_hi:2;
+ u8 vsync_pulse_width_hi:2;
+ u8 vsync_offset_hi:2;
+ u8 width_mm_lo;
+ u8 height_mm_lo;
+ u8 height_mm_hi:4;
+ u8 width_mm_hi:4;
+ u8 hborder;
+ u8 vborder;
+ u8 unknown0:1;
+ u8 vsync_positive:1;
+ u8 hsync_positive:1;
+ u8 separate_sync:2;
+ u8 stereo:1;
+ u8 unknown6:1;
+ u8 interlaced:1;
+} __attribute__((packed));
+
+/* If it's not pixel timing, it'll be one of the below */
+struct detailed_data_string {
+ u8 str[13];
+} __attribute__((packed));
+
+struct detailed_data_monitor_range {
+ u8 min_vfreq;
+ u8 max_vfreq;
+ u8 min_hfreq_khz;
+ u8 max_hfreq_khz;
+ u8 pixel_clock_mhz; /* need to multiply by 10 */
+ u16 sec_gtf_toggle; /* A000=use above, 20=use below */ /* FIXME: byte
order */
+ u8 hfreq_start_khz; /* need to multiply by 2 */
+ u8 c; /* need to divide by 2 */
+ u16 m; /* FIXME: byte order */
+ u8 k;
+ u8 j; /* need to divide by 2 */
+} __attribute__((packed));
+
+struct detailed_data_wpindex {
+ u8 white_y_lo:2;
+ u8 white_x_lo:2;
+ u8 pad:4;
+ u8 white_x_hi;
+ u8 white_y_hi;
+ u8 gamma; /* need to divide by 100 then add 1 */
+} __attribute__((packed));
+
+struct detailed_data_color_point {
+ u8 windex1;
+ u8 wpindex1[3];
+ u8 windex2;
+ u8 wpindex2[3];
+} __attribute__((packed));
+
+struct detailed_non_pixel {
+ u8 pad1;
+ u8 type; /* ff=serial, fe=string, fd=monitor range, fc=monitor name
+ fb=color point data, fa=standard timing data,
+ f9=undefined, f8=mfg. reserved */
+ u8 pad2;
+ union {
+ struct detailed_data_string str;
+ struct detailed_data_monitor_range range;
+ struct detailed_data_wpindex color;
+ struct std_timing timings[5];
+ } data;
+} __attribute__((packed));
+
+#define EDID_DETAIL_STD_MODES 0xfa
+#define EDID_DETAIL_MONITOR_CPDATA 0xfb
+#define EDID_DETAIL_MONITOR_NAME 0xfc
+#define EDID_DETAIL_MONITOR_RANGE 0xfd
+#define EDID_DETAIL_MONITOR_STRING 0xfe
+#define EDID_DETAIL_MONITOR_SERIAL 0xff
+
+struct detailed_timing {
+ u16 pixel_clock; /* need to multiply by 10 KHz */ /* FIXME: byte order
*/
+ union {
+ struct detailed_pixel_timing pixel_data;
+ struct detailed_non_pixel other_data;
+ } data;
+} __attribute__((packed));
+
+struct edid {
+ u8 header[8];
+ /* Vendor & product info */
+ u16 mfg_id; /* FIXME: byte order */
+ u16 prod_code; /* FIXME: byte order */
+ u32 serial; /* FIXME: byte order */
+ u8 mfg_week;
+ u8 mfg_year;
+ /* EDID version */
+ u8 version;
+ u8 revision;
+ /* Display info: */
+ /* input definition */
+ u8 serration_vsync:1;
+ u8 sync_on_green:1;
+ u8 composite_sync:1;
+ u8 separate_syncs:1;
+ u8 blank_to_black:1;
+ u8 video_level:2;
+ u8 digital:1; /* bits below must be zero if set */
+ u8 width_cm;
+ u8 height_cm;
+ u8 gamma;
+ /* feature support */
+ u8 default_gtf:1;
+ u8 preferred_timing:1;
+ u8 standard_color:1;
+ u8 display_type:2; /* 00=mono, 01=rgb, 10=non-rgb, 11=unknown */
+ u8 pm_active_off:1;
+ u8 pm_suspend:1;
+ u8 pm_standby:1;
+ /* Color characteristics */
+ u8 red_green_lo;
+ u8 black_white_lo;
+ u8 red_x;
+ u8 red_y;
+ u8 green_x;
+ u8 green_y;
+ u8 blue_x;
+ u8 blue_y;
+ u8 white_x;
+ u8 white_y;
+ /* Est. timings and mfg rsvd timings*/
+ struct est_timings established_timings;
+ /* Standard timings 1-8*/
+ struct std_timing standard_timings[8];
+ /* Detailing timings 1-4 */
+ struct detailed_timing detailed_timings[4];
+ /* Number of 128 byte ext. blocks */
+ u8 extensions;
+ /* Checksum */
+ u8 checksum;
+} __attribute__((packed));
+
+#endif /* __DRM_EDID_H__ */
diff --git a/linux-core/drm_fb.c b/linux-core/drm_fb.c
new file mode 100644
index 0000000..ef05341
--- /dev/null
+++ b/linux-core/drm_fb.c
@@ -0,0 +1,201 @@
+/*
+ * 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"
+struct drmfb_par {
+ struct drm_device *dev;
+ struct drm_framebuffer *fb;
+};
+
+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->fb;
+ if (regno > 17)
+ return 1;
+
+ 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;
+}
+
+/* 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_device *dev = par->dev;
+
+ drm_set_desired_modes(dev);
+ 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_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_framebuffer *fb)
+{
+ struct fb_info *info;
+ struct drmfb_par *par;
+ struct device *device = &dev->pdev->dev;
+ struct fb_var_screeninfo *var_info;
+ unsigned long base, size;
+ int ret;
+
+ info = framebuffer_alloc(sizeof(struct drmfb_par), device);
+ if (!info){
+ return -EINVAL;
+ }
+
+ fb->fbdev = info;
+
+ par = info->par;
+
+ par->dev = dev;
+ par->fb = fb;
+
+ info->fbops = &drmfb_ops;
+
+ strcpy(info->fix.id, "drmfb");
+ info->fix.smem_start = fb->offset + dev->mode_config.fb_base;
+ info->fix.smem_len = (8*1024*1024);
+ info->fix.type = FB_TYPE_PACKED_PIXELS;
+ info->fix.visual = FB_VISUAL_DIRECTCOLOR;
+ 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 * ((fb->bits_per_pixel + 1) / 8);
+
+ 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 = size;
+ info->pseudo_palette = fb->pseudo_palette;
+ info->var.xres = fb->width;
+ info->var.xres_virtual = fb->pitch;
+ info->var.yres = fb->height;
+ 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;
+
+ DRM_DEBUG("fb depth is %d\n", fb->depth);
+ switch(fb->depth) {
+ case 8:
+ case 15:
+ case 16:
+ break;
+ default:
+ case 24:
+ 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;
+ if (fb->depth == 32) {
+ info->var.transp.offset = 24;
+ info->var.transp.length = 8;
+ }
+ break;
+ }
+
+ if (register_framebuffer(info) < 0)
+ return -EINVAL;
+
+ printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
+ info->fix.id);
+ return 0;
+}
+EXPORT_SYMBOL(drmfb_probe);
+
+int drmfb_remove(struct drm_device *dev, struct drm_framebuffer *fb)
+{
+ struct fb_info *info = fb->fbdev;
+
+ if (info) {
+ drm_mem_reg_iounmap(dev, &fb->bo->mem, fb->virtual_base);
+ unregister_framebuffer(info);
+ framebuffer_release(info);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(drmfb_remove);
+MODULE_LICENSE("GPL");
diff --git a/linux-core/drm_fops.c b/linux-core/drm_fops.c
index d400a4d..e474897 100644
--- a/linux-core/drm_fops.c
+++ b/linux-core/drm_fops.c
@@ -86,7 +86,7 @@ static int drm_setup(drm_device_t * dev)
INIT_LIST_HEAD(&dev->ctxlist->head);

dev->vmalist = NULL;
- dev->sigdata.lock = NULL;
+ // dev->sigdata.lock = NULL;
init_waitqueue_head(&dev->lock.lock_queue);
dev->queue_count = 0;
dev->queue_reserved = 0;
@@ -270,6 +270,7 @@ static int drm_open_helper(struct inode *inode,
struct file *filp,

INIT_LIST_HEAD(&priv->user_objects);
INIT_LIST_HEAD(&priv->refd_objects);
+ INIT_LIST_HEAD(&priv->fbs);

for (i=0; i<_DRM_NO_REF_TYPES; ++i) {
ret = drm_ht_create(&priv->refd_object_hash[i], DRM_FILE_HASH_ORDER);
@@ -501,6 +502,7 @@ int drm_release(struct inode *inode, struct file
*filp)
mutex_unlock(&dev->ctxlist_mutex);

mutex_lock(&dev->struct_mutex);
+ drm_fb_release(filp);
drm_object_release(filp);
if (priv->remove_auth_on_close == 1) {
drm_file_t *temp = dev->file_first;
diff --git a/linux-core/drm_modes.c b/linux-core/drm_modes.c
new file mode 100644
index 0000000..3293f91
--- /dev/null
+++ b/linux-core/drm_modes.c
@@ -0,0 +1,558 @@
+/*
+ * Copyright © 1997-2003 by The XFree86 Project, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
obtaining a
+ * copy of this software and associated documentation files
(the "Software"),
+ * to deal in the Software without restriction, including without
limitation
+ * the rights to use, copy, modify, merge, publish, distribute,
sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom
the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM,
DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of the copyright
holder(s)
+ * and author(s) shall not be used in advertising or otherwise to
promote
+ * the sale, use or other dealings in this Software without prior
written
+ * authorization from the copyright holder(s) and author(s).
+ */
+/*
+ * Copyright © 2007 Dave Airlie
+ */
+
+#include <linux/list.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+
+/**
+ * drm_mode_debug_printmodeline - debug print a mode
+ * @dev: DRM device
+ * @mode: mode to print
+ *
+ * LOCKING:
+ * None.
+ *
+ * Describe @mode using DRM_DEBUG.
+ */
+void drm_mode_debug_printmodeline(struct drm_device *dev,
+ struct drm_display_mode *mode)
+{
+ DRM_DEBUG("Modeline %d:\"%s\" %d %d %d %d %d %d %d %d %d %d\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);
+}
+EXPORT_SYMBOL(drm_mode_debug_printmodeline);
+
+/**
+ * drm_mode_set_name - set the name on a mode
+ * @mode: name will be set in this mode
+ *
+ * LOCKING:
+ * None.
+ *
+ * Set the name of @mode to a standard format.
+ */
+void drm_mode_set_name(struct drm_display_mode *mode)
+{
+ snprintf(mode->name, DRM_DISPLAY_MODE_LEN, "%dx%d", mode->hdisplay,
+ mode->vdisplay);
+}
+EXPORT_SYMBOL(drm_mode_set_name);
+
+/**
+ * drm_mode_list_concat - move modes from one list to another
+ * @head: source list
+ * @new: dst list
+ *
+ * LOCKING:
+ * Caller must ensure both lists are locked.
+ *
+ * Move all the modes from @head to @new.
+ */
+void drm_mode_list_concat(struct list_head *head, struct list_head
*new)
+{
+
+ struct list_head *entry, *tmp;
+
+ list_for_each_safe(entry, tmp, head) {
+ list_move_tail(entry, new);
+ }
+}
+
+/**
+ * drm_mode_width - get the width of a mode
+ * @mode: mode
+ *
+ * LOCKING:
+ * None.
+ *
+ * Return @mode's width (hdisplay) value.
+ *
+ * FIXME: is this needed?
+ *
+ * RETURNS:
+ * @mode->hdisplay
+ */
+int drm_mode_width(struct drm_display_mode *mode)
+{
+ return mode->hdisplay;
+
+}
+EXPORT_SYMBOL(drm_mode_width);
+
+/**
+ * drm_mode_height - get the height of a mode
+ * @mode: mode
+ *
+ * LOCKING:
+ * None.
+ *
+ * Return @mode's height (vdisplay) value.
+ *
+ * FIXME: is this needed?
+ *
+ * RETURNS:
+ * @mode->vdisplay
+ */
+int drm_mode_height(struct drm_display_mode *mode)
+{
+ return mode->vdisplay;
+}
+EXPORT_SYMBOL(drm_mode_height);
+
+/**
+ * drm_mode_vrefresh - get the vrefresh of a mode
+ * @mode: mode
+ *
+ * LOCKING:
+ * None.
+ *
+ * Return @mode's vrefresh rate or calculate it if necessary.
+ *
+ * FIXME: why is this needed?
+ *
+ * RETURNS:
+ * Vertical refresh rate of @mode x 1000. For precision reasons.
+ */
+int drm_mode_vrefresh(struct drm_display_mode *mode)
+{
+ int refresh = 0;
+ unsigned int calc_val;
+
+ if (mode->vrefresh > 0)
+ refresh = mode->vrefresh;
+ else if (mode->htotal > 0 && mode->vtotal > 0) {
+ /* work out vrefresh the value will be x1000 */
+ calc_val = (mode->clock * 1000);
+
+ calc_val /= mode->htotal;
+ calc_val *= 1000;
+ calc_val /= mode->vtotal;
+
+ refresh = calc_val;
+ if (mode->flags & V_INTERLACE)
+ refresh *= 2;
+ if (mode->flags & V_DBLSCAN)
+ refresh /= 2;
+ if (mode->vscan > 1)
+ refresh /= mode->vscan;
+ }
+ return refresh;
+}
+EXPORT_SYMBOL(drm_mode_vrefresh);
+
+/**
+ * drm_mode_set_crtcinfo - set CRTC modesetting parameters
+ * @p: mode
+ * @adjust_flags: unused? (FIXME)
+ *
+ * LOCKING:
+ * None.
+ *
+ * Setup the CRTC modesetting parameters for @p, adjusting if
necessary.
+ */
+void drm_mode_set_crtcinfo(struct drm_display_mode *p, int
adjust_flags)
+{
+ if ((p == NULL) || ((p->type & DRM_MODE_TYPE_CRTC_C) ==
DRM_MODE_TYPE_BUILTIN))
+ return;
+
+ p->crtc_hdisplay = p->hdisplay;
+ p->crtc_hsync_start = p->hsync_start;
+ p->crtc_hsync_end = p->hsync_end;
+ p->crtc_htotal = p->htotal;
+ p->crtc_hskew = p->hskew;
+ p->crtc_vdisplay = p->vdisplay;
+ p->crtc_vsync_start = p->vsync_start;
+ p->crtc_vsync_end = p->vsync_end;
+ p->crtc_vtotal = p->vtotal;
+
+ if (p->flags & V_INTERLACE) {
+ if (adjust_flags & CRTC_INTERLACE_HALVE_V) {
+ p->crtc_vdisplay /= 2;
+ p->crtc_vsync_start /= 2;
+ p->crtc_vsync_end /= 2;
+ p->crtc_vtotal /= 2;
+ }
+
+ p->crtc_vtotal |= 1;
+ }
+
+ if (p->flags & V_DBLSCAN) {
+ p->crtc_vdisplay *= 2;
+ p->crtc_vsync_start *= 2;
+ p->crtc_vsync_end *= 2;
+ p->crtc_vtotal *= 2;
+ }
+
+ if (p->vscan > 1) {
+ p->crtc_vdisplay *= p->vscan;
+ p->crtc_vsync_start *= p->vscan;
+ p->crtc_vsync_end *= p->vscan;
+ p->crtc_vtotal *= p->vscan;
+ }
+
+ p->crtc_vblank_start = min(p->crtc_vsync_start, p->crtc_vdisplay);
+ p->crtc_vblank_end = max(p->crtc_vsync_end, p->crtc_vtotal);
+ p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay);
+ p->crtc_hblank_end = max(p->crtc_hsync_end, p->crtc_htotal);
+
+ p->crtc_hadjusted = false;
+ p->crtc_vadjusted = false;
+}
+EXPORT_SYMBOL(drm_mode_set_crtcinfo);
+
+
+/**
+ * drm_mode_duplicate - allocate and duplicate an existing mode
+ * @m: mode to duplicate
+ *
+ * LOCKING:
+ * None.
+ *
+ * Just allocate a new mode, copy the existing mode into it, and return
+ * a pointer to it. Used to create new instances of established modes.
+ */
+struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev,
+ struct drm_display_mode *mode)
+{
+ struct drm_display_mode *nmode;
+ int new_id;
+
+ nmode = drm_mode_create(dev);
+ if (!nmode)
+ return NULL;
+
+ new_id = nmode->mode_id;
+ *nmode = *mode;
+ nmode->mode_id = new_id;
+ INIT_LIST_HEAD(&nmode->head);
+ return nmode;
+}
+EXPORT_SYMBOL(drm_mode_duplicate);
+
+/**
+ * drm_mode_equal - test modes for equality
+ * @mode1: first mode
+ * @mode2: second mode
+ *
+ * LOCKING:
+ * None.
+ *
+ * Check to see if @mode1 and @mode2 are equivalent.
+ *
+ * RETURNS:
+ * True if the modes are equal, false otherwise.
+ */
+bool drm_mode_equal(struct drm_display_mode *mode1, struct
drm_display_mode *mode2)
+{
+ if (mode1->clock == mode2->clock &&
+ mode1->hdisplay == mode2->hdisplay &&
+ mode1->hsync_start == mode2->hsync_start &&
+ mode1->hsync_end == mode2->hsync_end &&
+ mode1->htotal == mode2->htotal &&
+ mode1->hskew == mode2->hskew &&
+ mode1->vdisplay == mode2->vdisplay &&
+ mode1->vsync_start == mode2->vsync_start &&
+ mode1->vsync_end == mode2->vsync_end &&
+ mode1->vtotal == mode2->vtotal &&
+ mode1->vscan == mode2->vscan &&
+ mode1->flags == mode2->flags)
+ return true;
+
+ return false;
+}
+EXPORT_SYMBOL(drm_mode_equal);
+
+/**
+ * drm_mode_validate_size - make sure modes adhere to size constraints
+ * @dev: DRM device
+ * @mode_list: list of modes to check
+ * @maxX: maximum width
+ * @maxY: maximum height
+ * @maxPitch: max pitch
+ *
+ * LOCKING:
+ * Caller must hold a lock protecting @mode_list.
+ *
+ * The DRM device (@dev) has size and pitch limits. Here we validate
the
+ * modes we probed for @dev against those limits and set their status
as
+ * necessary.
+ */
+void drm_mode_validate_size(struct drm_device *dev,
+ struct list_head *mode_list,
+ int maxX, int maxY, int maxPitch)
+{
+ struct drm_display_mode *mode;
+
+ list_for_each_entry(mode, mode_list, head) {
+ if (maxPitch > 0 && mode->hdisplay > maxPitch)
+ mode->status = MODE_BAD_WIDTH;
+
+ if (maxX > 0 && mode->hdisplay > maxX)
+ mode->status = MODE_VIRTUAL_X;
+
+ if (maxY > 0 && mode->vdisplay > maxY)
+ mode->status = MODE_VIRTUAL_Y;
+ }
+}
+EXPORT_SYMBOL(drm_mode_validate_size);
+
+/**
+ * drm_mode_validate_clocks - validate modes against clock limits
+ * @dev: DRM device
+ * @mode_list: list of modes to check
+ * @min: minimum clock rate array
+ * @max: maximum clock rate array
+ * @n_ranges: number of clock ranges (size of arrays)
+ *
+ * LOCKING:
+ * Caller must hold a lock protecting @mode_list.
+ *
+ * Some code may need to check a mode list against the clock limits of
the
+ * device in question. This function walks the mode list, testing to
make
+ * sure each mode falls within a given range (defined by @min and @max
+ * arrays) and sets @mode->status as needed.
+ */
+void drm_mode_validate_clocks(struct drm_device *dev,
+ struct list_head *mode_list,
+ int *min, int *max, int n_ranges)
+{
+ struct drm_display_mode *mode;
+ int i;
+
+ list_for_each_entry(mode, mode_list, head) {
+ bool good = false;
+ for (i = 0; i < n_ranges; i++) {
+ if (mode->clock >= min[i] && mode->clock <= max[i]) {
+ good = true;
+ break;
+ }
+ }
+ if (!good)
+ mode->status = MODE_CLOCK_RANGE;
+ }
+}
+EXPORT_SYMBOL(drm_mode_validate_clocks);
+
+/**
+ * drm_mode_prune_invalid - remove invalid modes from mode list
+ * @dev: DRM device
+ * @mode_list: list of modes to check
+ * @verbose: be verbose about it
+ *
+ * LOCKING:
+ * Caller must hold a lock protecting @mode_list.
+ *
+ * Once mode list generation is complete, a caller can use this routine
to
+ * remove invalid modes from a mode list. If any of the modes have a
+ * status other than %MODE_OK, they are removed from @mode_list and
freed.
+ */
+void drm_mode_prune_invalid(struct drm_device *dev,
+ struct list_head *mode_list, bool verbose)
+{
+ struct drm_display_mode *mode, *t;
+
+ list_for_each_entry_safe(mode, t, mode_list, head) {
+ if (mode->status != MODE_OK) {
+ list_del(&mode->head);
+ if (verbose)
+ DRM_DEBUG("Not using %s mode %d\n", mode->name, mode->status);
+ kfree(mode);
+ }
+ }
+}
+
+/**
+ * drm_mode_compare - compare modes for favorability
+ * @lh_a: list_head for first mode
+ * @lh_b: list_head for second mode
+ *
+ * LOCKING:
+ * None.
+ *
+ * Compare two modes, given by @lh_a and @lh_b, returning a value
indicating
+ * which is better.
+ *
+ * RETURNS:
+ * Negative if @lh_a is better than @lh_b, zero if they're equivalent,
or
+ * positive if @lh_b is better than @lh_a.
+ */
+static int drm_mode_compare(struct list_head *lh_a, struct list_head
*lh_b)
+{
+ struct drm_display_mode *a = list_entry(lh_a, struct drm_display_mode,
head);
+ struct drm_display_mode *b = list_entry(lh_b, struct drm_display_mode,
head);
+ int diff;
+
+ diff = ((b->type & DRM_MODE_TYPE_PREFERRED) != 0) -
+ ((a->type & DRM_MODE_TYPE_PREFERRED) != 0);
+ if (diff)
+ return diff;
+ diff = b->hdisplay * b->vdisplay - a->hdisplay * a->vdisplay;
+ if (diff)
+ return diff;
+ diff = b->clock - a->clock;
+ return diff;
+}
+
+/* FIXME: what we don't have a list sort function? */
+/* list sort from Mark J Roberts ([email protected]) */
+void list_sort(struct list_head *head, int (*cmp)(struct list_head *a,
struct list_head *b))
+{
+ struct list_head *p, *q, *e, *list, *tail, *oldhead;
+ int insize, nmerges, psize, qsize, i;
+
+ list = head->next;
+ list_del(head);
+ insize = 1;
+ for (;;) {
+ p = oldhead = list;
+ list = tail = NULL;
+ nmerges = 0;
+
+ while (p) {
+ nmerges++;
+ q = p;
+ psize = 0;
+ for (i = 0; i < insize; i++) {
+ psize++;
+ q = q->next == oldhead ? NULL : q->next;
+ if (!q)
+ break;
+ }
+
+ qsize = insize;
+ while (psize > 0 || (qsize > 0 && q)) {
+ if (!psize) {
+ e = q;
+ q = q->next;
+ qsize--;
+ if (q == oldhead)
+ q = NULL;
+ } else if (!qsize || !q) {
+ e = p;
+ p = p->next;
+ psize--;
+ if (p == oldhead)
+ p = NULL;
+ } else if (cmp(p, q) <= 0) {
+ e = p;
+ p = p->next;
+ psize--;
+ if (p == oldhead)
+ p = NULL;
+ } else {
+ e = q;
+ q = q->next;
+ qsize--;
+ if (q == oldhead)
+ q = NULL;
+ }
+ if (tail)
+ tail->next = e;
+ else
+ list = e;
+ e->prev = tail;
+ tail = e;
+ }
+ p = q;
+ }
+
+ tail->next = list;
+ list->prev = tail;
+
+ if (nmerges <= 1)
+ break;
+
+ insize *= 2;
+ }
+
+ head->next = list;
+ head->prev = list->prev;
+ list->prev->next = head;
+ list->prev = head;
+}
+
+/**
+ * drm_mode_sort - sort mode list
+ * @mode_list: list to sort
+ *
+ * LOCKING:
+ * Caller must hold a lock protecting @mode_list.
+ *
+ * Sort @mode_list by favorability, putting good modes first.
+ */
+void drm_mode_sort(struct list_head *mode_list)
+{
+ list_sort(mode_list, drm_mode_compare);
+}
+
+
+/**
+ * drm_mode_output_list_update - update the mode list for the output
+ * @output: the output to update
+ *
+ * LOCKING:
+ * Caller must hold a lock protecting @mode_list.
+ *
+ * This moves the modes from the @output probed_modes list
+ * to the actual mode list. It compares the probed mode against the
current
+ * list and only adds different modes. All modes unverified after this
point
+ * will be removed by the prune invalid modes.
+ */
+void drm_mode_output_list_update(struct drm_output *output)
+{
+ struct drm_display_mode *mode, *t;
+ struct drm_display_mode *pmode, *pt;
+ int found_it;
+ list_for_each_entry_safe(pmode, pt, &output->probed_modes,
+ head) {
+ found_it = 0;
+ /* go through current modes checking for the new probed mode */
+ list_for_each_entry(mode, &output->modes, head) {
+ if (drm_mode_equal(pmode, mode)) {
+ found_it = 1;
+ /* if equal delete the probed mode */
+ mode->status = pmode->status;
+ list_del(&pmode->head);
+ kfree(pmode);
+ break;
+ }
+ }
+
+ if (!found_it) {
+ list_move_tail(&pmode->head, &output->modes);
+ }
+ }
+}
diff --git a/linux-core/drm_objects.h b/linux-core/drm_objects.h
index 03ea927..8a70c73 100644
--- a/linux-core/drm_objects.h
+++ b/linux-core/drm_objects.h
@@ -29,7 +29,8 @@
*/

#ifndef _DRM_OBJECTS_H
-#define _DRM_OBJECTS_H
+#define _DRM_OJBECTS_H
+#define DRM_HAS_TTM

struct drm_device;

@@ -247,9 +248,9 @@ typedef struct drm_ttm_backend_func {


typedef struct drm_ttm_backend {
- uint32_t flags;
- int mem_type;
- drm_ttm_backend_func_t *func;
+ uint32_t flags;
+ int mem_type;
+ drm_ttm_backend_func_t *func;
} drm_ttm_backend_t;

typedef struct drm_ttm {
@@ -427,7 +428,13 @@ typedef struct drm_bo_driver {
/*
* buffer objects (drm_bo.c)
*/
-
+extern int drm_bo_init_mm(struct drm_device * dev, unsigned type,
+ unsigned long p_offset, unsigned long p_size);
+extern int drm_buffer_object_create(struct drm_device *dev, unsigned
long size,
+ drm_bo_type_t type, uint32_t mask,
+ uint32_t hint, uint32_t page_alignment,
+ unsigned long buffer_start,
+ drm_buffer_object_t ** buf_obj);
extern int drm_bo_ioctl(DRM_IOCTL_ARGS);
extern int drm_mm_init_ioctl(DRM_IOCTL_ARGS);
extern int drm_bo_driver_finish(struct drm_device *dev);
@@ -471,4 +478,9 @@ extern int
drm_bo_move_accel_cleanup(drm_buffer_object_t * bo,
uint32_t fence_flags,
drm_bo_mem_reg_t * new_mem);

+extern int drm_mem_reg_ioremap(struct drm_device *dev, drm_bo_mem_reg_t
* mem,
+ void **virtual);
+extern void drm_mem_reg_iounmap(struct drm_device *dev,
drm_bo_mem_reg_t * mem,
+ void *virtual);
+
#endif
diff --git a/linux-core/drm_os_linux.h b/linux-core/drm_os_linux.h
index 2ea105c..816959e 100644
--- a/linux-core/drm_os_linux.h
+++ b/linux-core/drm_os_linux.h
@@ -119,6 +119,24 @@ static __inline__ int mtrr_del(int reg, unsigned
long base, unsigned long size)

#define DRM_GET_PRIV_WITH_RETURN(_priv, _filp) _priv =
_filp->private_data

+/**
+ * Get the pointer to the SAREA.
+ *
+ * Searches the SAREA on the mapping lists and points drm_device::sarea
to it.
+ */
+#define DRM_GETSAREA() \
+do { \
+ drm_map_list_t *entry; \
+ list_for_each_entry( entry, &dev->maplist->head, head ) { \
+ if ( entry->map && \
+ entry->map->type == _DRM_SHM && \
+ (entry->map->flags & _DRM_CONTAINS_LOCK) ) { \
+ dev_priv->sarea = entry->map; \
+ break; \
+ } \
+ } \
+} while (0)
+
#define DRM_HZ HZ

#define DRM_WAIT_ON( ret, queue, timeout, condition ) \
diff --git a/linux-core/drm_stub.c b/linux-core/drm_stub.c
index f4da7da..01ffe67 100644
--- a/linux-core/drm_stub.c
+++ b/linux-core/drm_stub.c
@@ -79,27 +79,28 @@ static int drm_fill_in_dev(drm_device_t * dev,
struct pci_dev *pdev,
#endif
dev->irq = pdev->irq;

- if (drm_ht_create(&dev->map_hash, DRM_MAP_HASH_ORDER)) {
- drm_free(dev->maplist, sizeof(*dev->maplist), DRM_MEM_MAPS);
+ 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_free(dev->maplist, sizeof(*dev->maplist), DRM_MEM_MAPS);
drm_ht_remove(&dev->map_hash);
return -ENOMEM;
}

if (drm_ht_create(&dev->object_hash, DRM_OBJECT_HASH_ORDER)) {
- drm_free(dev->maplist, sizeof(*dev->maplist),
DRM_MEM_MAPS);
drm_ht_remove(&dev->map_hash);
drm_mm_takedown(&dev->offset_manager);
return -ENOMEM;
}

dev->maplist = drm_calloc(1, sizeof(*dev->maplist), DRM_MEM_MAPS);
- if (dev->maplist == NULL)
+ if (dev->maplist == NULL) {
+ drm_ht_remove(&dev->object_hash);
+ drm_ht_remove(&dev->map_hash);
+ drm_mm_takedown(&dev->offset_manager);
return -ENOMEM;
+ }
INIT_LIST_HEAD(&dev->maplist->head);

/* the DRM has 6 counters */
@@ -113,10 +114,6 @@ static int drm_fill_in_dev(drm_device_t * dev,
struct pci_dev *pdev,

dev->driver = driver;

- if (dev->driver->load)
- if ((retcode = dev->driver->load(dev, ent->driver_data)))
- goto error_out_unreg;
-
if (drm_core_has_AGP(dev)) {
if (drm_device_is_agp(dev))
dev->agp = drm_agp_init(dev);
@@ -136,6 +133,10 @@ static int drm_fill_in_dev(drm_device_t * dev,
struct pci_dev *pdev,
}
}

+ if (dev->driver->load)
+ if ((retcode = dev->driver->load(dev, ent->driver_data)))
+ goto error_out_unreg;
+
retcode = drm_ctxbitmap_init(dev);
if (retcode) {
DRM_ERROR("Cannot allocate memory for context bitmap.\n");
diff --git a/linux-core/drm_vm.c b/linux-core/drm_vm.c
index b887153..1f905fb 100644
--- a/linux-core/drm_vm.c
+++ b/linux-core/drm_vm.c
@@ -516,7 +516,8 @@ static int drm_mmap_dma(struct file *filp, struct
vm_area_struct *vma)
return -EINVAL;
}

- if (!capable(CAP_SYS_ADMIN) && (dma->flags & _DRM_DMA_USE_PCI_RO)) {
+ if (!capable(CAP_SYS_ADMIN) &&
+ (dma->flags & _DRM_DMA_USE_PCI_RO)) {
vma->vm_flags &= ~(VM_WRITE | VM_MAYWRITE);
#if defined(__i386__) || defined(__x86_64__)
pgprot_val(vma->vm_page_prot) &= ~_PAGE_RW;
@@ -738,7 +739,7 @@ static unsigned long drm_bo_vm_nopfn(struct
vm_area_struct *vma,
unsigned long bus_base;
unsigned long bus_offset;
unsigned long bus_size;
- unsigned long ret = NOPFN_REFAULT;
+ int ret = NOPFN_REFAULT;

if (address > vma->vm_end)
return NOPFN_SIGBUS;

2007-05-17 22:40:53

by Jesse Barnes

[permalink] [raw]
Subject: [PATCH 3/3] Intel support for DRM modesetting

This patch adds support for DRM modesetting to the Intel DRM driver and
stubs out a simple FB driver to sit underneath. The code had to be
refactored a bit, since current DRM drivers tend to be fully initialized
by userspace via ioctls. This patch makes the driver load routine do
most of the heavy lifting, since it's necessary in order to fully bring up
a console driver.

It also relies on the TTM patch Dave posted recently for allocating the
initial framebuffer used by the FB layer.

Jesse

diff --git a/linux-core/i915_drv.c b/linux-core/i915_drv.c
index 7fdb083..50ff977 100644
--- a/linux-core/i915_drv.c
+++ b/linux-core/i915_drv.c
@@ -79,7 +79,7 @@ static struct drm_driver driver = {
DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_IRQ_VBL |
DRIVER_IRQ_VBL2,
.load = i915_driver_load,
- .firstopen = i915_driver_firstopen,
+ .unload = i915_driver_unload,
.lastclose = i915_driver_lastclose,
.preclose = i915_driver_preclose,
.device_is_agp = i915_driver_device_is_agp,
diff --git a/linux-core/i915_init.c b/linux-core/i915_init.c
new file mode 100644
index 0000000..0c9ef4d
--- /dev/null
+++ b/linux-core/i915_init.c
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2007 Intel Corporation
+ * Jesse Barnes <[email protected]>
+ *
+ * Copyright © 2002, 2003 David Dawes <[email protected]>
+ * 2004 Sylvain Meyer
+ *
+ * GPL/BSD dual license
+ */
+#include "drmP.h"
+#include "drm.h"
+#include "drm_sarea.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+/**
+ * i915_probe_agp - get AGP bootup configuration
+ * @pdev: PCI device
+ * @aperture_size: returns AGP aperture configured size
+ * @preallocated_size: returns size of BIOS preallocated AGP space
+ *
+ * Since Intel integrated graphics are UMA, the BIOS has to set aside
+ * some RAM for the framebuffer at early boot. This code figures out
+ * how much was set aside so we can use it for our own purposes.
+ */
+int i915_probe_agp(struct pci_dev *pdev, unsigned long *aperture_size,
+ unsigned long *preallocated_size)
+{
+ struct pci_dev *bridge_dev;
+ u16 tmp = 0;
+ unsigned long overhead;
+
+ bridge_dev = pci_get_bus_and_slot(0, PCI_DEVFN(0,0));
+ if (!bridge_dev) {
+ DRM_ERROR("bridge device not found\n");
+ return -1;
+ }
+
+ /* Get the fb aperture size and "stolen" memory amount. */
+ pci_read_config_word(bridge_dev, INTEL_GMCH_CTRL, &tmp);
+ pci_dev_put(bridge_dev);
+
+ *aperture_size = 1024 * 1024;
+ *preallocated_size = 1024 * 1024;
+
+ switch (pdev->device) {
+ case PCI_DEVICE_ID_INTEL_82830_CGC:
+ case PCI_DEVICE_ID_INTEL_82845G_IG:
+ case PCI_DEVICE_ID_INTEL_82855GM_IG:
+ case PCI_DEVICE_ID_INTEL_82865_IG:
+ if ((tmp & INTEL_GMCH_MEM_MASK) == INTEL_GMCH_MEM_64M)
+ *aperture_size *= 64;
+ else
+ *aperture_size *= 128;
+ break;
+ default:
+ /* 9xx supports large sizes, just look at the length */
+ *aperture_size = pci_resource_len(pdev, 2);
+ break;
+ }
+
+ /*
+ * Some of the preallocated space is taken by the GTT
+ * and popup. GTT is 1K per MB of aperture size, and popup is 4K.
+ */
+ overhead = (*aperture_size / 1024) + 4096;
+ switch (tmp & INTEL_855_GMCH_GMS_MASK) {
+ case INTEL_855_GMCH_GMS_STOLEN_1M:
+ break; /* 1M already */
+ case INTEL_855_GMCH_GMS_STOLEN_4M:
+ *preallocated_size *= 4;
+ break;
+ case INTEL_855_GMCH_GMS_STOLEN_8M:
+ *preallocated_size *= 8;
+ break;
+ case INTEL_855_GMCH_GMS_STOLEN_16M:
+ *preallocated_size *= 16;
+ break;
+ case INTEL_855_GMCH_GMS_STOLEN_32M:
+ *preallocated_size *= 32;
+ break;
+ case INTEL_915G_GMCH_GMS_STOLEN_48M:
+ *preallocated_size *= 48;
+ break;
+ case INTEL_915G_GMCH_GMS_STOLEN_64M:
+ *preallocated_size *= 64;
+ break;
+ case INTEL_855_GMCH_GMS_DISABLED:
+ DRM_ERROR("video memory is disabled\n");
+ return -1;
+ default:
+ DRM_ERROR("unexpected GMCH_GMS value: 0x%02x\n",
+ tmp & INTEL_855_GMCH_GMS_MASK);
+ return -1;
+ }
+ *preallocated_size -= overhead;
+
+ return 0;
+}
+
+/**
+ * i915_driver_load - setup chip and create an initial config
+ * @dev: DRM device
+ * @flags: startup flags
+ *
+ * The driver load routine has to do several things:
+ * - drive output discovery via intel_modeset_init()
+ * - initialize the memory manager
+ * - allocate initial config memory
+ * - setup the DRM framebuffer with the allocated memory
+ */
+int i915_driver_load(drm_device_t *dev, unsigned long flags)
+{
+ drm_i915_private_t *dev_priv;
+ unsigned long agp_size, prealloc_size;
+ unsigned long sareapage;
+ int size, ret;
+
+ dev_priv = drm_alloc(sizeof(drm_i915_private_t), DRM_MEM_DRIVER);
+ if (dev_priv == NULL)
+ return DRM_ERR(ENOMEM);
+
+ memset(dev_priv, 0, sizeof(drm_i915_private_t));
+ dev->dev_private = (void *)dev_priv;
+// dev_priv->flags = flags;
+
+ /* i915 has 4 more counters */
+ dev->counters += 4;
+ dev->types[6] = _DRM_STAT_IRQ;
+ dev->types[7] = _DRM_STAT_PRIMARY;
+ dev->types[8] = _DRM_STAT_SECONDARY;
+ dev->types[9] = _DRM_STAT_DMA;
+
+ if (IS_I9XX(dev)) {
+ dev_priv->mmiobase = drm_get_resource_start(dev, 0);
+ dev_priv->mmiolen = drm_get_resource_len(dev, 0);
+ dev->mode_config.fb_base =
+ drm_get_resource_start(dev, 2) & 0xff000000;
+ } else if (drm_get_resource_start(dev, 1)) {
+ dev_priv->mmiobase = drm_get_resource_start(dev, 1);
+ dev_priv->mmiolen = drm_get_resource_len(dev, 1);
+ dev->mode_config.fb_base =
+ drm_get_resource_start(dev, 0) & 0xff000000;
+ } else {
+ DRM_ERROR("Unable to find MMIO registers\n");
+ return -ENODEV;
+ }
+
+ DRM_DEBUG("fb_base: 0x%08lx\n", dev->mode_config.fb_base);
+
+ ret = drm_addmap(dev, dev_priv->mmiobase, dev_priv->mmiolen,
+ _DRM_REGISTERS, _DRM_READ_ONLY|_DRM_DRIVER, &dev_priv->mmio_map);
+ if (ret != 0) {
+ DRM_ERROR("Cannot add mapping for MMIO registers\n");
+ return ret;
+ }
+
+ /* prebuild the SAREA */
+ sareapage = max(SAREA_MAX, PAGE_SIZE);
+ ret = drm_addmap(dev, 0, sareapage, _DRM_SHM, _DRM_CONTAINS_LOCK|_DRM_DRIVER,
+ &dev_priv->sarea);
+ if (ret) {
+ DRM_ERROR("SAREA setup failed\n");
+ return ret;
+ }
+
+ init_waitqueue_head(&dev->lock.lock_queue);
+
+ /* FIXME: assume sarea_priv is right after SAREA */
+ dev_priv->sarea_priv = dev_priv->sarea->handle + sizeof(drm_sarea_t);
+
+ /*
+ * Initialize the memory manager for local and AGP space
+ */
+ drm_bo_driver_init(dev);
+
+ i915_probe_agp(dev->pdev, &agp_size, &prealloc_size);
+ DRM_DEBUG("setting up %ld bytes of PRIV0 space\n", prealloc_size);
+ drm_bo_init_mm(dev, DRM_BO_MEM_PRIV0, 0, prealloc_size >> PAGE_SHIFT);
+
+ I915_WRITE(LP_RING + RING_LEN, 0);
+ I915_WRITE(LP_RING + RING_HEAD, 0);
+ I915_WRITE(LP_RING + RING_TAIL, 0);
+
+ size = PRIMARY_RINGBUFFER_SIZE;
+ ret = drm_buffer_object_create(dev, size, drm_bo_type_kernel,
+ DRM_BO_FLAG_READ | DRM_BO_FLAG_WRITE |
+ DRM_BO_FLAG_MEM_PRIV0 |
+ DRM_BO_FLAG_NO_EVICT,
+ DRM_BO_HINT_DONT_FENCE, 0x1, 0,
+ &dev_priv->ring_buffer);
+ if (ret < 0) {
+ DRM_ERROR("Unable to allocate ring buffer\n");
+ return -EINVAL;
+ }
+
+ /* remap the buffer object properly */
+ dev_priv->ring.Start = dev_priv->ring_buffer->offset;
+ dev_priv->ring.End = dev_priv->ring.Start + size;
+ dev_priv->ring.Size = size;
+ dev_priv->ring.tail_mask = dev_priv->ring.Size - 1;
+
+ /* FIXME: need wrapper with PCI mem checks */
+ ret = drm_mem_reg_ioremap(dev, &dev_priv->ring_buffer->mem,
+ &dev_priv->ring.virtual_start);
+ if (ret)
+ DRM_ERROR("error mapping ring buffer: %d\n", ret);
+
+ DRM_DEBUG("ring start %08lX, %p, %08lX\n", dev_priv->ring.Start,
+ dev_priv->ring.virtual_start, dev_priv->ring.Size);
+
+ dev_priv->sarea_priv->pf_current_page = 0;
+
+ /* We are using separate values as placeholders for mechanisms for
+ * private backbuffer/depthbuffer usage.
+ */
+ dev_priv->use_mi_batchbuffer_start = 0;
+
+ /* Allow hardware batchbuffers unless told otherwise.
+ */
+ dev_priv->allow_batchbuffer = 1;
+
+ /* Program Hardware Status Page */
+ dev_priv->status_page_dmah = drm_pci_alloc(dev, PAGE_SIZE, PAGE_SIZE,
+ 0xffffffff);
+
+ if (!dev_priv->status_page_dmah) {
+ dev->dev_private = (void *)dev_priv;
+ i915_dma_cleanup(dev);
+ DRM_ERROR("Can not allocate hardware status page\n");
+ return DRM_ERR(ENOMEM);
+ }
+ dev_priv->hw_status_page = dev_priv->status_page_dmah->vaddr;
+ dev_priv->dma_status_page = dev_priv->status_page_dmah->busaddr;
+
+ memset(dev_priv->hw_status_page, 0, PAGE_SIZE);
+ DRM_DEBUG("hw status page @ %p\n", dev_priv->hw_status_page);
+
+ I915_WRITE(0x02080, dev_priv->dma_status_page);
+ DRM_DEBUG("Enabled hardware status page\n");
+
+ intel_modeset_init(dev);
+ drm_initial_config(dev, false);
+
+ return 0;
+}
+
+int i915_driver_unload(drm_device_t *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;
+ dev_priv->hw_status_page = NULL;
+ dev_priv->dma_status_page = 0;
+ /* Need to rewrite hardware status page */
+ I915_WRITE(0x02080, 0x1ffff000);
+ }
+
+ I915_WRITE(LP_RING + RING_LEN, 0);
+
+ intel_modeset_cleanup(dev);
+
+ drm_mem_reg_iounmap(dev, &dev_priv->ring_buffer->mem,
+ dev_priv->ring.virtual_start);
+
+ DRM_DEBUG("usage is %d\n", dev_priv->ring_buffer->usage);
+ mutex_lock(&dev->struct_mutex);
+ drm_bo_usage_deref_locked(dev_priv->ring_buffer);
+ mutex_unlock(&dev->struct_mutex);
+
+ if (drm_bo_clean_mm(dev, DRM_BO_MEM_PRIV0)) {
+ DRM_ERROR("Memory manager type 3 not clean. "
+ "Delaying takedown\n");
+ }
+
+ drm_bo_driver_finish(dev);
+
+ DRM_DEBUG("%p, %p\n", dev_priv->mmio_map, dev_priv->sarea);
+ drm_rmmap(dev, dev_priv->mmio_map);
+ drm_rmmap(dev, dev_priv->sarea);
+
+ drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER);
+
+ dev->dev_private = NULL;
+ return 0;
+}
+
+void i915_driver_lastclose(drm_device_t * dev)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+
+ i915_mem_takedown(&(dev_priv->agp_heap));
+
+ i915_dma_cleanup(dev);
+
+}
+
+void i915_driver_preclose(drm_device_t * dev, DRMFILE filp)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ i915_mem_release(dev, filp, dev_priv->agp_heap);
+}
+
diff --git a/linux-core/intel_crt.c b/linux-core/intel_crt.c
new file mode 100644
index 0000000..cdfb314
--- /dev/null
+++ b/linux-core/intel_crt.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright © 2006-2007 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <[email protected]>
+ */
+
+#include <linux/i2c.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+static void intel_crt_dpms(struct drm_output *output, int mode)
+{
+ drm_device_t *dev = output->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ u32 temp;
+
+ temp = I915_READ(ADPA);
+ temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
+ temp &= ~ADPA_DAC_ENABLE;
+
+ switch(mode) {
+ case DPMSModeOn:
+ temp |= ADPA_DAC_ENABLE;
+ break;
+ case DPMSModeStandby:
+ temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE;
+ break;
+ case DPMSModeSuspend:
+ temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE;
+ break;
+ case DPMSModeOff:
+ temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE;
+ break;
+ }
+
+ I915_WRITE(ADPA, temp);
+}
+
+static void intel_crt_save(struct drm_output *output)
+{
+
+}
+
+static void intel_crt_restore(struct drm_output *output)
+{
+
+}
+
+static int intel_crt_mode_valid(struct drm_output *output,
+ struct drm_display_mode *mode)
+{
+ if (mode->flags & V_DBLSCAN)
+ return MODE_NO_DBLESCAN;
+
+ if (mode->clock > 400000 || mode->clock < 25000)
+ return MODE_CLOCK_RANGE;
+
+ return MODE_OK;
+}
+
+static bool intel_crt_mode_fixup(struct drm_output *output,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+static void intel_crt_mode_set(struct drm_output *output,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ drm_device_t *dev = output->dev;
+ struct drm_crtc *crtc = output->crtc;
+ struct intel_crtc *intel_crtc = crtc->driver_private;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ int dpll_md_reg;
+ u32 adpa, dpll_md;
+
+ if (intel_crtc->pipe == 0)
+ dpll_md_reg = DPLL_A_MD;
+ else
+ dpll_md_reg = DPLL_B_MD;
+
+ /*
+ * Disable separate mode multiplier used when cloning SDVO to CRT
+ * XXX this needs to be adjusted when we really are cloning
+ */
+ if (IS_I965G(dev)) {
+ dpll_md = I915_READ(dpll_md_reg);
+ I915_WRITE(dpll_md_reg,
+ dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK);
+ }
+
+ adpa = 0;
+ if (adjusted_mode->flags & V_PHSYNC)
+ adpa |= ADPA_HSYNC_ACTIVE_HIGH;
+ if (adjusted_mode->flags & V_PVSYNC)
+ adpa |= ADPA_VSYNC_ACTIVE_HIGH;
+
+ if (intel_crtc->pipe == 0)
+ adpa |= ADPA_PIPE_A_SELECT;
+ else
+ adpa |= ADPA_PIPE_B_SELECT;
+
+ I915_WRITE(ADPA, adpa);
+}
+
+/**
+ * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence.
+ *
+ * Only for I945G/GM.
+ *
+ * \return TRUE if CRT is connected.
+ * \return FALSE if CRT is disconnected.
+ */
+static bool intel_crt_detect_hotplug(struct drm_output *output)
+{
+ drm_device_t *dev = output->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ u32 temp;
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+
+ temp = I915_READ(PORT_HOTPLUG_EN);
+
+ I915_WRITE(PORT_HOTPLUG_EN,
+ temp | CRT_HOTPLUG_FORCE_DETECT | (1 << 5));
+
+ do {
+ if (!(I915_READ(PORT_HOTPLUG_EN) & CRT_HOTPLUG_FORCE_DETECT))
+ break;
+ msleep(1);
+ } while (time_after(timeout, jiffies));
+
+ if ((I915_READ(PORT_HOTPLUG_STAT) & CRT_HOTPLUG_MONITOR_MASK) ==
+ CRT_HOTPLUG_MONITOR_COLOR)
+ return true;
+
+ return false;
+}
+
+static bool intel_crt_detect_ddc(struct drm_output *output)
+{
+ struct intel_output *intel_output = output->driver_private;
+
+ /* CRT should always be at 0, but check anyway */
+ if (intel_output->type != INTEL_OUTPUT_ANALOG)
+ return false;
+
+ return intel_ddc_probe(output);
+}
+
+static enum drm_output_status intel_crt_detect(struct drm_output *output)
+{
+ drm_device_t *dev = output->dev;
+
+ if (IS_I945G(dev)| IS_I945GM(dev) || IS_I965G(dev)) {
+ if (intel_crt_detect_hotplug(output))
+ return output_status_connected;
+ else
+ return output_status_disconnected;
+ }
+
+ if (intel_crt_detect_ddc(output))
+ return output_status_connected;
+
+ /* TODO use load detect */
+ return output_status_unknown;
+}
+
+static void intel_crt_destroy(struct drm_output *output)
+{
+ struct intel_output *intel_output = output->driver_private;
+
+ intel_i2c_destroy(intel_output->ddc_bus);
+ kfree(output->driver_private);
+}
+
+static int intel_crt_get_modes(struct drm_output *output)
+{
+ return intel_ddc_get_modes(output);
+}
+
+/*
+ * Routines for controlling stuff on the analog port
+ */
+static const struct drm_output_funcs intel_crt_output_funcs = {
+ .dpms = intel_crt_dpms,
+ .save = intel_crt_save,
+ .restore = intel_crt_restore,
+ .mode_valid = intel_crt_mode_valid,
+ .mode_fixup = intel_crt_mode_fixup,
+ .prepare = intel_output_prepare,
+ .mode_set = intel_crt_mode_set,
+ .commit = intel_output_commit,
+ .detect = intel_crt_detect,
+ .get_modes = intel_crt_get_modes,
+ .cleanup = intel_crt_destroy,
+};
+
+void intel_crt_init(drm_device_t *dev)
+{
+ struct drm_output *output;
+ struct intel_output *intel_output;
+
+ output = drm_output_create(dev, &intel_crt_output_funcs, "VGA");
+
+ intel_output = kmalloc(sizeof(struct intel_output), GFP_KERNEL);
+ if (!intel_output) {
+ drm_output_destroy(output);
+ return;
+ }
+ /* Set up the DDC bus. */
+ intel_output->ddc_bus = intel_i2c_create(dev, GPIOA, "CRTDDC_A");
+ if (!intel_output->ddc_bus) {
+ dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration "
+ "failed.\n");
+ return;
+ }
+
+ intel_output->type = INTEL_OUTPUT_ANALOG;
+ output->driver_private = intel_output;
+ output->interlace_allowed = 0;
+ output->doublescan_allowed = 0;
+}
diff --git a/linux-core/intel_display.c b/linux-core/intel_display.c
new file mode 100644
index 0000000..7d58117
--- /dev/null
+++ b/linux-core/intel_display.c
@@ -0,0 +1,1232 @@
+/*
+ * 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 <[email protected]>
+ */
+
+#include <linux/i2c.h>
+#include "drmP.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+bool intel_pipe_has_type (struct drm_crtc *crtc, int type);
+
+typedef struct {
+ /* given values */
+ int n;
+ int m1, m2;
+ int p1, p2;
+ /* derived values */
+ int dot;
+ int vco;
+ int m;
+ int p;
+} intel_clock_t;
+
+typedef struct {
+ int min, max;
+} intel_range_t;
+
+typedef struct {
+ int dot_limit;
+ int p2_slow, p2_fast;
+} intel_p2_t;
+
+#define INTEL_P2_NUM 2
+
+typedef struct {
+ intel_range_t dot, vco, n, m, m1, m2, p, p1;
+ intel_p2_t p2;
+} intel_limit_t;
+
+#define I8XX_DOT_MIN 25000
+#define I8XX_DOT_MAX 350000
+#define I8XX_VCO_MIN 930000
+#define I8XX_VCO_MAX 1400000
+#define I8XX_N_MIN 3
+#define I8XX_N_MAX 16
+#define I8XX_M_MIN 96
+#define I8XX_M_MAX 140
+#define I8XX_M1_MIN 18
+#define I8XX_M1_MAX 26
+#define I8XX_M2_MIN 6
+#define I8XX_M2_MAX 16
+#define I8XX_P_MIN 4
+#define I8XX_P_MAX 128
+#define I8XX_P1_MIN 2
+#define I8XX_P1_MAX 33
+#define I8XX_P1_LVDS_MIN 1
+#define I8XX_P1_LVDS_MAX 6
+#define I8XX_P2_SLOW 4
+#define I8XX_P2_FAST 2
+#define I8XX_P2_LVDS_SLOW 14
+#define I8XX_P2_LVDS_FAST 14 /* No fast option */
+#define I8XX_P2_SLOW_LIMIT 165000
+
+#define I9XX_DOT_MIN 20000
+#define I9XX_DOT_MAX 400000
+#define I9XX_VCO_MIN 1400000
+#define I9XX_VCO_MAX 2800000
+#define I9XX_N_MIN 3
+#define I9XX_N_MAX 8
+#define I9XX_M_MIN 70
+#define I9XX_M_MAX 120
+#define I9XX_M1_MIN 10
+#define I9XX_M1_MAX 20
+#define I9XX_M2_MIN 5
+#define I9XX_M2_MAX 9
+#define I9XX_P_SDVO_DAC_MIN 5
+#define I9XX_P_SDVO_DAC_MAX 80
+#define I9XX_P_LVDS_MIN 7
+#define I9XX_P_LVDS_MAX 98
+#define I9XX_P1_MIN 1
+#define I9XX_P1_MAX 8
+#define I9XX_P2_SDVO_DAC_SLOW 10
+#define I9XX_P2_SDVO_DAC_FAST 5
+#define I9XX_P2_SDVO_DAC_SLOW_LIMIT 200000
+#define I9XX_P2_LVDS_SLOW 14
+#define I9XX_P2_LVDS_FAST 7
+#define I9XX_P2_LVDS_SLOW_LIMIT 112000
+
+#define INTEL_LIMIT_I8XX_DVO_DAC 0
+#define INTEL_LIMIT_I8XX_LVDS 1
+#define INTEL_LIMIT_I9XX_SDVO_DAC 2
+#define INTEL_LIMIT_I9XX_LVDS 3
+
+static const intel_limit_t intel_limits[] = {
+ { /* INTEL_LIMIT_I8XX_DVO_DAC */
+ .dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX },
+ .vco = { .min = I8XX_VCO_MIN, .max = I8XX_VCO_MAX },
+ .n = { .min = I8XX_N_MIN, .max = I8XX_N_MAX },
+ .m = { .min = I8XX_M_MIN, .max = I8XX_M_MAX },
+ .m1 = { .min = I8XX_M1_MIN, .max = I8XX_M1_MAX },
+ .m2 = { .min = I8XX_M2_MIN, .max = I8XX_M2_MAX },
+ .p = { .min = I8XX_P_MIN, .max = I8XX_P_MAX },
+ .p1 = { .min = I8XX_P1_MIN, .max = I8XX_P1_MAX },
+ .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT,
+ .p2_slow = I8XX_P2_SLOW, .p2_fast = I8XX_P2_FAST },
+ },
+ { /* INTEL_LIMIT_I8XX_LVDS */
+ .dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX },
+ .vco = { .min = I8XX_VCO_MIN, .max = I8XX_VCO_MAX },
+ .n = { .min = I8XX_N_MIN, .max = I8XX_N_MAX },
+ .m = { .min = I8XX_M_MIN, .max = I8XX_M_MAX },
+ .m1 = { .min = I8XX_M1_MIN, .max = I8XX_M1_MAX },
+ .m2 = { .min = I8XX_M2_MIN, .max = I8XX_M2_MAX },
+ .p = { .min = I8XX_P_MIN, .max = I8XX_P_MAX },
+ .p1 = { .min = I8XX_P1_LVDS_MIN, .max = I8XX_P1_LVDS_MAX },
+ .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT,
+ .p2_slow = I8XX_P2_LVDS_SLOW, .p2_fast = I8XX_P2_LVDS_FAST },
+ },
+ { /* INTEL_LIMIT_I9XX_SDVO_DAC */
+ .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX },
+ .vco = { .min = I9XX_VCO_MIN, .max = I9XX_VCO_MAX },
+ .n = { .min = I9XX_N_MIN, .max = I9XX_N_MAX },
+ .m = { .min = I9XX_M_MIN, .max = I9XX_M_MAX },
+ .m1 = { .min = I9XX_M1_MIN, .max = I9XX_M1_MAX },
+ .m2 = { .min = I9XX_M2_MIN, .max = I9XX_M2_MAX },
+ .p = { .min = I9XX_P_SDVO_DAC_MIN, .max = I9XX_P_SDVO_DAC_MAX },
+ .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX },
+ .p2 = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT,
+ .p2_slow = I9XX_P2_SDVO_DAC_SLOW, .p2_fast = I9XX_P2_SDVO_DAC_FAST },
+ },
+ { /* INTEL_LIMIT_I9XX_LVDS */
+ .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX },
+ .vco = { .min = I9XX_VCO_MIN, .max = I9XX_VCO_MAX },
+ .n = { .min = I9XX_N_MIN, .max = I9XX_N_MAX },
+ .m = { .min = I9XX_M_MIN, .max = I9XX_M_MAX },
+ .m1 = { .min = I9XX_M1_MIN, .max = I9XX_M1_MAX },
+ .m2 = { .min = I9XX_M2_MIN, .max = I9XX_M2_MAX },
+ .p = { .min = I9XX_P_LVDS_MIN, .max = I9XX_P_LVDS_MAX },
+ .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX },
+ /* The single-channel range is 25-112Mhz, and dual-channel
+ * is 80-224Mhz. Prefer single channel as much as possible.
+ */
+ .p2 = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT,
+ .p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_FAST },
+ },
+};
+
+static const intel_limit_t *intel_limit(struct drm_crtc *crtc)
+{
+ drm_device_t *dev = crtc->dev;
+ const intel_limit_t *limit;
+
+ if (IS_I9XX(dev)) {
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+ limit = &intel_limits[INTEL_LIMIT_I9XX_LVDS];
+ else
+ limit = &intel_limits[INTEL_LIMIT_I9XX_SDVO_DAC];
+ } else {
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+ limit = &intel_limits[INTEL_LIMIT_I8XX_LVDS];
+ else
+ limit = &intel_limits[INTEL_LIMIT_I8XX_DVO_DAC];
+ }
+ return limit;
+}
+
+/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */
+
+static void i8xx_clock(int refclk, intel_clock_t *clock)
+{
+ clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2);
+ clock->p = clock->p1 * clock->p2;
+ clock->vco = refclk * clock->m / (clock->n + 2);
+ clock->dot = clock->vco / clock->p;
+}
+
+/** Derive the pixel clock for the given refclk and divisors for 9xx chips. */
+
+static void i9xx_clock(int refclk, intel_clock_t *clock)
+{
+ clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2);
+ clock->p = clock->p1 * clock->p2;
+ clock->vco = refclk * clock->m / (clock->n + 2);
+ clock->dot = clock->vco / clock->p;
+}
+
+static void intel_clock(struct drm_device *dev, int refclk,
+ intel_clock_t *clock)
+{
+ if (IS_I9XX(dev))
+ return i9xx_clock (refclk, clock);
+ else
+ return i8xx_clock (refclk, clock);
+}
+
+/**
+ * Returns whether any output on the specified pipe is of the specified type
+ */
+bool intel_pipe_has_type (struct drm_crtc *crtc, int type)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_mode_config *mode_config = &dev->mode_config;
+ struct drm_output *l_entry;
+
+ list_for_each_entry(l_entry, &mode_config->output_list, head) {
+ if (l_entry->crtc == crtc) {
+ struct intel_output *intel_output = l_entry->driver_private;
+ if (intel_output->type == type)
+ return true;
+ }
+ }
+ return false;
+}
+
+#define INTELPllInvalid(s) { /* ErrorF (s) */; return false; }
+/**
+ * Returns whether the given set of divisors are valid for a given refclk with
+ * the given outputs.
+ */
+
+static bool intel_PLL_is_valid(struct drm_crtc *crtc, intel_clock_t *clock)
+{
+ const intel_limit_t *limit = intel_limit (crtc);
+
+ if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1)
+ INTELPllInvalid ("p1 out of range\n");
+ if (clock->p < limit->p.min || limit->p.max < clock->p)
+ INTELPllInvalid ("p out of range\n");
+ if (clock->m2 < limit->m2.min || limit->m2.max < clock->m2)
+ INTELPllInvalid ("m2 out of range\n");
+ if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1)
+ INTELPllInvalid ("m1 out of range\n");
+ if (clock->m1 <= clock->m2)
+ INTELPllInvalid ("m1 <= m2\n");
+ if (clock->m < limit->m.min || limit->m.max < clock->m)
+ INTELPllInvalid ("m out of range\n");
+ if (clock->n < limit->n.min || limit->n.max < clock->n)
+ INTELPllInvalid ("n out of range\n");
+ if (clock->vco < limit->vco.min || limit->vco.max < clock->vco)
+ INTELPllInvalid ("vco out of range\n");
+ /* XXX: We may need to be checking "Dot clock" depending on the multiplier,
+ * output, etc., rather than just a single range.
+ */
+ if (clock->dot < limit->dot.min || limit->dot.max < clock->dot)
+ INTELPllInvalid ("dot out of range\n");
+
+ return true;
+}
+
+/**
+ * Returns a set of divisors for the desired target clock with the given
+ * refclk, or FALSE. The returned values represent the clock equation:
+ * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
+ */
+static bool intel_find_best_PLL(struct drm_crtc *crtc, int target,
+ int refclk, intel_clock_t *best_clock)
+{
+ drm_device_t *dev = crtc->dev;
+ drm_i915_private_t *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(drm_device_t *dev)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_crtc *crtc;
+ struct intel_crtc *intel_crtc;
+ int vbl_pipe = 0;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ intel_crtc = crtc->driver_private;
+
+ if (crtc->enabled)
+ vbl_pipe |= (1<<intel_crtc->pipe);
+ }
+
+ dev_priv->vblank_pipe = vbl_pipe;
+ i915_enable_interrupt(dev);
+}
+void
+intel_wait_for_vblank(drm_device_t *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)
+{
+ drm_device_t *dev = crtc->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = crtc->driver_private;
+ int pipe = intel_crtc->pipe;
+ unsigned long Start, Offset;
+ int dspbase = (pipe == 0 ? DSPABASE : DSPBBASE);
+ int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF);
+
+ Start = crtc->fb->offset;
+ Offset = ((y * crtc->fb->pitch + x) * (crtc->fb->bits_per_pixel / 8));
+
+ DRM_DEBUG("Writing base %08lX %08lX %d %d\n", Start, Offset, x, y);
+ if (IS_I965G(dev)) {
+ I915_WRITE(dspbase, Offset);
+ I915_READ(dspbase);
+ I915_WRITE(dspsurf, Start);
+ I915_READ(dspsurf);
+ } else {
+ I915_WRITE(dspbase, Start + Offset);
+ I915_READ(dspbase);
+ }
+
+
+ if (!dev_priv->sarea_priv)
+ return;
+
+ switch (pipe) {
+ case 0:
+ dev_priv->sarea_priv->pipeA_x = x;
+ dev_priv->sarea_priv->pipeA_y = y;
+ break;
+ case 1:
+ dev_priv->sarea_priv->pipeB_x = x;
+ dev_priv->sarea_priv->pipeB_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)
+{
+ drm_device_t *dev = crtc->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = crtc->driver_private;
+ int pipe = intel_crtc->pipe;
+ int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
+ int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
+ int dspbase_reg = (pipe == 0) ? DSPABASE : DSPBBASE;
+ int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
+ u32 temp;
+ bool enabled;
+
+ /* XXX: When our outputs are all unaware of DPMS modes other than off
+ * and on, we should map those modes to DPMSModeOff in the CRTC.
+ */
+ switch (mode) {
+ case DPMSModeOn:
+ case DPMSModeStandby:
+ case DPMSModeSuspend:
+ /* Enable the DPLL */
+ temp = I915_READ(dpll_reg);
+ if ((temp & DPLL_VCO_ENABLE) == 0) {
+ I915_WRITE(dpll_reg, temp);
+ I915_READ(dpll_reg);
+ /* Wait for the clocks to stabilize. */
+ udelay(150);
+ I915_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE);
+ I915_READ(dpll_reg);
+ /* Wait for the clocks to stabilize. */
+ udelay(150);
+ I915_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE);
+ I915_READ(dpll_reg);
+ /* Wait for the clocks to stabilize. */
+ udelay(150);
+ }
+
+ /* Enable the pipe */
+ temp = I915_READ(pipeconf_reg);
+ if ((temp & PIPEACONF_ENABLE) == 0)
+ I915_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE);
+
+ /* Enable the plane */
+ temp = I915_READ(dspcntr_reg);
+ if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
+ I915_WRITE(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE);
+ /* Flush the plane changes */
+ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+ }
+
+ intel_crtc_load_lut(crtc);
+
+ /* Give the overlay scaler a chance to enable if it's on this pipe */
+ //intel_crtc_dpms_video(crtc, TRUE); TODO
+ break;
+ case DPMSModeOff:
+ /* Give the overlay scaler a chance to disable if it's on this pipe */
+ //intel_crtc_dpms_video(crtc, FALSE); TODO
+
+ /* Disable the VGA plane that we never use */
+ I915_WRITE(VGACNTRL, VGA_DISP_DISABLE);
+
+ /* Disable display plane */
+ temp = I915_READ(dspcntr_reg);
+ if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
+ I915_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE);
+ /* Flush the plane changes */
+ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+ I915_READ(dspbase_reg);
+ }
+
+ if (!IS_I9XX(dev)) {
+ /* Wait for vblank for the disable to take effect */
+ intel_wait_for_vblank(dev);
+ }
+
+ /* Next, disable display pipes */
+ temp = I915_READ(pipeconf_reg);
+ if ((temp & PIPEACONF_ENABLE) != 0) {
+ I915_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE);
+ I915_READ(pipeconf_reg);
+ }
+
+ /* Wait for vblank for the disable to take effect. */
+ intel_wait_for_vblank(dev);
+
+ temp = I915_READ(dpll_reg);
+ if ((temp & DPLL_VCO_ENABLE) != 0) {
+ I915_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE);
+ I915_READ(dpll_reg);
+ }
+
+ /* Wait for the clocks to turn off. */
+ udelay(150);
+ break;
+ }
+
+
+ if (!dev_priv->sarea_priv)
+ return;
+
+ enabled = crtc->enabled && mode != DPMSModeOff;
+
+ switch (pipe) {
+ case 0:
+ dev_priv->sarea_priv->pipeA_w = enabled ? crtc->mode.hdisplay : 0;
+ dev_priv->sarea_priv->pipeA_h = enabled ? crtc->mode.vdisplay : 0;
+ break;
+ case 1:
+ dev_priv->sarea_priv->pipeB_w = enabled ? crtc->mode.hdisplay : 0;
+ dev_priv->sarea_priv->pipeB_h = enabled ? crtc->mode.vdisplay : 0;
+ break;
+ default:
+ DRM_ERROR("Can't update pipe %d in SAREA\n", pipe);
+ break;
+ }
+}
+
+static bool intel_crtc_lock(struct drm_crtc *crtc)
+{
+ /* Sync the engine before mode switch */
+// i830WaitSync(crtc->scrn);
+
+#if 0 // TODO def XF86DRI
+ return I830DRILock(crtc->scrn);
+#else
+ return FALSE;
+#endif
+}
+
+static void intel_crtc_unlock (struct drm_crtc *crtc)
+{
+#if 0 // TODO def XF86DRI
+ I830DRIUnlock (crtc->scrn);
+#endif
+}
+
+static void intel_crtc_prepare (struct drm_crtc *crtc)
+{
+ crtc->funcs->dpms(crtc, DPMSModeOff);
+}
+
+static void intel_crtc_commit (struct drm_crtc *crtc)
+{
+ crtc->funcs->dpms(crtc, DPMSModeOn);
+}
+
+void intel_output_prepare (struct drm_output *output)
+{
+ /* lvds has its own version of prepare see intel_lvds_prepare */
+ output->funcs->dpms(output, DPMSModeOff);
+}
+
+void intel_output_commit (struct drm_output *output)
+{
+ /* lvds has its own version of commit see intel_lvds_commit */
+ output->funcs->dpms(output, DPMSModeOn);
+}
+
+static bool intel_crtc_mode_fixup(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+
+/** Returns the core display clock speed for i830 - i945 */
+static int intel_get_core_clock_speed(drm_device_t *dev)
+{
+
+ /* Core clock values taken from the published datasheets.
+ * The 830 may go up to 166 Mhz, which we should check.
+ */
+ if (IS_I945G(dev))
+ return 400000;
+ else if (IS_I915G(dev))
+ return 333000;
+ else if (IS_I945GM(dev) || IS_845G(dev))
+ return 200000;
+ else if (IS_I915GM(dev)) {
+ u16 gcfgc = 0;
+
+ pci_read_config_word(dev->pdev, I915_GCFGC, &gcfgc);
+
+ if (gcfgc & I915_LOW_FREQUENCY_ENABLE)
+ return 133000;
+ else {
+ switch (gcfgc & I915_DISPLAY_CLOCK_MASK) {
+ case I915_DISPLAY_CLOCK_333_MHZ:
+ return 333000;
+ default:
+ case I915_DISPLAY_CLOCK_190_200_MHZ:
+ return 190000;
+ }
+ }
+ } else if (IS_I865G(dev))
+ return 266000;
+ else if (IS_I855(dev)) {
+#if 0
+ PCITAG bridge = pciTag(0, 0, 0); /* This is always the host bridge */
+ u16 hpllcc = pciReadWord(bridge, I855_HPLLCC);
+
+#endif
+ u16 hpllcc = 0;
+ /* Assume that the hardware is in the high speed state. This
+ * should be the default.
+ */
+ switch (hpllcc & I855_CLOCK_CONTROL_MASK) {
+ case I855_CLOCK_133_200:
+ case I855_CLOCK_100_200:
+ return 200000;
+ case I855_CLOCK_166_250:
+ return 250000;
+ case I855_CLOCK_100_133:
+ return 133000;
+ }
+ } else /* 852, 830 */
+ return 133000;
+
+ return 0; /* Silence gcc warning */
+}
+
+
+/**
+ * Return the pipe currently connected to the panel fitter,
+ * or -1 if the panel fitter is not present or not in use
+ */
+static int intel_panel_fitter_pipe (drm_device_t *dev)
+{
+ drm_i915_private_t *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)
+{
+ drm_device_t *dev = crtc->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = crtc->driver_private;
+ int pipe = intel_crtc->pipe;
+ int fp_reg = (pipe == 0) ? FPA0 : FPB0;
+ int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
+ int dpll_md_reg = (intel_crtc->pipe == 0) ? DPLL_A_MD : DPLL_B_MD;
+ int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
+ int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
+ int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B;
+ int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B;
+ int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B;
+ int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B;
+ int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B;
+ int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B;
+ int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE;
+ int dspstride_reg = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE;
+ int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS;
+ int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC;
+ int refclk;
+ intel_clock_t clock;
+ u32 dpll = 0, fp = 0, dspcntr, pipeconf;
+ bool ok, is_sdvo = false, is_dvo = false;
+ bool is_crt = false, is_lvds = false, is_tv = false;
+ struct drm_mode_config *mode_config = &dev->mode_config;
+ struct drm_output *output;
+
+ list_for_each_entry(output, &mode_config->output_list, head) {
+ struct intel_output *intel_output = output->driver_private;
+
+ if (output->crtc != crtc)
+ continue;
+
+ switch (intel_output->type) {
+ case INTEL_OUTPUT_LVDS:
+ is_lvds = TRUE;
+ break;
+ case INTEL_OUTPUT_SDVO:
+ is_sdvo = TRUE;
+ break;
+ case INTEL_OUTPUT_DVO:
+ is_dvo = TRUE;
+ break;
+ case INTEL_OUTPUT_TVOUT:
+ is_tv = TRUE;
+ break;
+ case INTEL_OUTPUT_ANALOG:
+ is_crt = TRUE;
+ break;
+ }
+ }
+
+ if (IS_I9XX(dev)) {
+ refclk = 96000;
+ } else {
+ refclk = 48000;
+ }
+
+ ok = intel_find_best_PLL(crtc, adjusted_mode->clock, refclk, &clock);
+ if (!ok) {
+ DRM_ERROR("Couldn't find PLL settings for mode!\n");
+ return;
+ }
+
+ fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
+
+ dpll = DPLL_VGA_MODE_DIS;
+ if (IS_I9XX(dev)) {
+ if (is_lvds)
+ dpll |= DPLLB_MODE_LVDS;
+ else
+ dpll |= DPLLB_MODE_DAC_SERIAL;
+ if (is_sdvo) {
+ dpll |= DPLL_DVO_HIGH_SPEED;
+ if (IS_I945G(dev) || IS_I945GM(dev)) {
+ int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
+ dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
+ }
+ }
+
+ /* compute bitmask from p1 value */
+ dpll |= (1 << (clock.p1 - 1)) << 16;
+ switch (clock.p2) {
+ case 5:
+ dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5;
+ break;
+ case 7:
+ dpll |= DPLLB_LVDS_P2_CLOCK_DIV_7;
+ break;
+ case 10:
+ dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_10;
+ break;
+ case 14:
+ dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14;
+ break;
+ }
+ if (IS_I965G(dev))
+ dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT);
+ } else {
+ if (is_lvds) {
+ dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT;
+ } else {
+ if (clock.p1 == 2)
+ dpll |= PLL_P1_DIVIDE_BY_TWO;
+ else
+ dpll |= (clock.p1 - 2) << DPLL_FPA01_P1_POST_DIV_SHIFT;
+ if (clock.p2 == 4)
+ dpll |= PLL_P2_DIVIDE_BY_4;
+ }
+ }
+
+ if (is_tv) {
+ /* XXX: just matching BIOS for now */
+/* dpll |= PLL_REF_INPUT_TVCLKINBC; */
+ dpll |= 3;
+ }
+#if 0
+ else if (is_lvds)
+ dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN;
+#endif
+ else
+ dpll |= PLL_REF_INPUT_DREFCLK;
+
+ /* Set up the display plane register */
+ dspcntr = DISPPLANE_GAMMA_ENABLE;
+
+ switch (crtc->fb->bits_per_pixel) {
+ case 8:
+ dspcntr |= DISPPLANE_8BPP;
+ break;
+ case 16:
+ if (crtc->fb->depth == 15)
+ dspcntr |= DISPPLANE_15_16BPP;
+ else
+ dspcntr |= DISPPLANE_16BPP;
+ break;
+ case 32:
+ dspcntr |= DISPPLANE_32BPP_NO_ALPHA;
+ break;
+ default:
+ DRM_ERROR("Unknown color depth\n");
+ return;
+ }
+
+
+ if (pipe == 0)
+ dspcntr |= DISPPLANE_SEL_PIPE_A;
+ else
+ dspcntr |= DISPPLANE_SEL_PIPE_B;
+
+ pipeconf = I915_READ(pipeconf_reg);
+ if (pipe == 0 && !IS_I965G(dev)) {
+ /* Enable pixel doubling when the dot clock is > 90% of the (display)
+ * core speed.
+ *
+ * XXX: No double-wide on 915GM pipe B. Is that the only reason for the
+ * pipe == 0 check?
+ */
+ if (mode->clock > intel_get_core_clock_speed(dev) * 9 / 10)
+ pipeconf |= PIPEACONF_DOUBLE_WIDE;
+ else
+ pipeconf &= ~PIPEACONF_DOUBLE_WIDE;
+ }
+
+ dspcntr |= DISPLAY_PLANE_ENABLE;
+ pipeconf |= PIPEACONF_ENABLE;
+ dpll |= DPLL_VCO_ENABLE;
+
+
+ /* Disable the panel fitter if it was on our pipe */
+ if (intel_panel_fitter_pipe(dev) == pipe)
+ I915_WRITE(PFIT_CONTROL, 0);
+
+ DRM_DEBUG("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
+ drm_mode_debug_printmodeline(dev, mode);
+
+#if 0
+ if (!xf86ModesEqual(mode, adjusted_mode)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Adjusted mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
+ xf86PrintModeline(pScrn->scrnIndex, mode);
+ }
+ i830PrintPll("chosen", &clock);
+#endif
+
+ if (dpll & DPLL_VCO_ENABLE) {
+ I915_WRITE(fp_reg, fp);
+ I915_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE);
+ I915_READ(dpll_reg);
+ udelay(150);
+ }
+
+ /* The LVDS pin pair needs to be on before the DPLLs are enabled.
+ * This is an exception to the general rule that mode_set doesn't turn
+ * things on.
+ */
+ if (is_lvds) {
+ u32 lvds = I915_READ(LVDS);
+
+ lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP | LVDS_PIPEB_SELECT;
+ /* Set the B0-B3 data pairs corresponding to whether we're going to
+ * set the DPLLs for dual-channel mode or not.
+ */
+ if (clock.p2 == 7)
+ lvds |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP;
+ else
+ lvds &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP);
+
+ /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP)
+ * appropriately here, but we need to look more thoroughly into how
+ * panels behave in the two modes.
+ */
+
+ I915_WRITE(LVDS, lvds);
+ I915_READ(LVDS);
+ }
+
+ I915_WRITE(fp_reg, fp);
+ I915_WRITE(dpll_reg, dpll);
+ I915_READ(dpll_reg);
+ /* Wait for the clocks to stabilize. */
+ udelay(150);
+
+ if (IS_I965G(dev)) {
+ int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
+ I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) |
+ ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT));
+ } else {
+ /* write it again -- the BIOS does, after all */
+ I915_WRITE(dpll_reg, dpll);
+ }
+ I915_READ(dpll_reg);
+ /* Wait for the clocks to stabilize. */
+ udelay(150);
+
+ I915_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) |
+ ((adjusted_mode->crtc_htotal - 1) << 16));
+ I915_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) |
+ ((adjusted_mode->crtc_hblank_end - 1) << 16));
+ I915_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) |
+ ((adjusted_mode->crtc_hsync_end - 1) << 16));
+ I915_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) |
+ ((adjusted_mode->crtc_vtotal - 1) << 16));
+ I915_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) |
+ ((adjusted_mode->crtc_vblank_end - 1) << 16));
+ I915_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) |
+ ((adjusted_mode->crtc_vsync_end - 1) << 16));
+ I915_WRITE(dspstride_reg, crtc->fb->pitch * (crtc->fb->bits_per_pixel / 8));
+ /* 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)
+{
+ drm_device_t *dev = crtc->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = crtc->driver_private;
+ int palreg = (intel_crtc->pipe == 0) ? PALETTE_A : PALETTE_B;
+ int i;
+
+ /* The clocks have to be on to load the palette. */
+ if (!crtc->enabled)
+ return;
+
+ for (i = 0; i < 256; i++) {
+ I915_WRITE(palreg + 4 * i,
+ (intel_crtc->lut_r[i] << 16) |
+ (intel_crtc->lut_g[i] << 8) |
+ intel_crtc->lut_b[i]);
+ }
+}
+
+/** Sets the color ramps on behalf of RandR */
+static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
+ u16 *blue, int size)
+{
+ struct intel_crtc *intel_crtc = crtc->driver_private;
+ int i;
+
+ 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);
+}
+
+/* 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)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = crtc->driver_private;
+ int pipe = intel_crtc->pipe;
+ u32 dpll = I915_READ((pipe == 0) ? DPLL_A : DPLL_B);
+ u32 fp;
+ intel_clock_t clock;
+
+ if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0)
+ fp = I915_READ((pipe == 0) ? FPA0 : FPB0);
+ else
+ fp = I915_READ((pipe == 0) ? FPA1 : FPB1);
+
+ clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT;
+ clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT;
+ clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT;
+ if (IS_I9XX(dev)) {
+ clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK) >>
+ DPLL_FPA01_P1_POST_DIV_SHIFT);
+
+ switch (dpll & DPLL_MODE_MASK) {
+ case DPLLB_MODE_DAC_SERIAL:
+ clock.p2 = dpll & DPLL_DAC_SERIAL_P2_CLOCK_DIV_5 ?
+ 5 : 10;
+ break;
+ case DPLLB_MODE_LVDS:
+ clock.p2 = dpll & DPLLB_LVDS_P2_CLOCK_DIV_7 ?
+ 7 : 14;
+ break;
+ default:
+ DRM_DEBUG("Unknown DPLL mode %08x in programmed "
+ "mode\n", (int)(dpll & DPLL_MODE_MASK));
+ return 0;
+ }
+
+ /* XXX: Handle the 100Mhz refclk */
+ i9xx_clock(96000, &clock);
+ } else {
+ bool is_lvds = (pipe == 1) && (I915_READ(LVDS) & LVDS_PORT_EN);
+
+ if (is_lvds) {
+ clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS) >>
+ DPLL_FPA01_P1_POST_DIV_SHIFT);
+ clock.p2 = 14;
+
+ if ((dpll & PLL_REF_INPUT_MASK) ==
+ PLLB_REF_INPUT_SPREADSPECTRUMIN) {
+ /* XXX: might not be 66MHz */
+ i8xx_clock(66000, &clock);
+ } else
+ i8xx_clock(48000, &clock);
+ } else {
+ if (dpll & PLL_P1_DIVIDE_BY_TWO)
+ clock.p1 = 2;
+ else {
+ clock.p1 = ((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830) >>
+ DPLL_FPA01_P1_POST_DIV_SHIFT) + 2;
+ }
+ if (dpll & PLL_P2_DIVIDE_BY_4)
+ clock.p2 = 4;
+ else
+ clock.p2 = 2;
+
+ i8xx_clock(48000, &clock);
+ }
+ }
+
+ /* XXX: It would be nice to validate the clocks, but we can't reuse
+ * i830PllIsValid() because it relies on the xf86_config output
+ * configuration being accurate, which it isn't necessarily.
+ */
+
+ return clock.dot;
+}
+
+/** Returns the currently programmed mode of the given pipe. */
+struct drm_display_mode *intel_crtc_mode_get(drm_device_t *dev,
+ struct drm_crtc *crtc)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = crtc->driver_private;
+ int pipe = intel_crtc->pipe;
+ struct drm_display_mode *mode;
+ int htot = I915_READ((pipe == 0) ? HTOTAL_A : HTOTAL_B);
+ int hsync = I915_READ((pipe == 0) ? HSYNC_A : HSYNC_B);
+ int vtot = I915_READ((pipe == 0) ? VTOTAL_A : VTOTAL_B);
+ int vsync = I915_READ((pipe == 0) ? VSYNC_A : VSYNC_B);
+
+ mode = kzalloc(sizeof(*mode), GFP_KERNEL);
+ if (!mode)
+ return NULL;
+
+ mode->clock = intel_crtc_clock_get(dev, crtc);
+ mode->hdisplay = (htot & 0xffff) + 1;
+ mode->htotal = ((htot & 0xffff0000) >> 16) + 1;
+ mode->hsync_start = (hsync & 0xffff) + 1;
+ mode->hsync_end = ((hsync & 0xffff0000) >> 16) + 1;
+ mode->vdisplay = (vtot & 0xffff) + 1;
+ mode->vtotal = ((vtot & 0xffff0000) >> 16) + 1;
+ mode->vsync_start = (vsync & 0xffff) + 1;
+ mode->vsync_end = ((vsync & 0xffff0000) >> 16) + 1;
+
+ drm_mode_set_name(mode);
+ drm_mode_set_crtcinfo(mode, 0);
+
+ return mode;
+}
+
+static const struct drm_crtc_funcs intel_crtc_funcs = {
+ .dpms = intel_crtc_dpms,
+ .lock = intel_crtc_lock,
+ .unlock = intel_crtc_unlock,
+ .mode_fixup = intel_crtc_mode_fixup,
+ .mode_set = intel_crtc_mode_set,
+ .gamma_set = intel_crtc_gamma_set,
+ .prepare = intel_crtc_prepare,
+ .commit = intel_crtc_commit,
+};
+
+
+void intel_crtc_init(drm_device_t *dev, int pipe)
+{
+ struct drm_crtc *crtc;
+ struct intel_crtc *intel_crtc;
+ int i;
+
+ crtc = drm_crtc_create(dev, &intel_crtc_funcs);
+ if (crtc == NULL)
+ return;
+
+ intel_crtc = kzalloc(sizeof(struct intel_crtc), GFP_KERNEL);
+ if (intel_crtc == NULL) {
+ kfree(crtc);
+ return;
+ }
+
+ intel_crtc->pipe = pipe;
+ for (i = 0; i < 256; i++) {
+ intel_crtc->lut_r[i] = i;
+ intel_crtc->lut_g[i] = i;
+ intel_crtc->lut_b[i] = i;
+ }
+
+ crtc->driver_private = intel_crtc;
+}
+
+struct drm_crtc *intel_get_crtc_from_pipe(drm_device_t *dev, int pipe)
+{
+ struct drm_crtc *crtc = NULL;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct intel_crtc *intel_crtc = crtc->driver_private;
+ if (intel_crtc->pipe == pipe)
+ break;
+ }
+ return crtc;
+}
+
+int intel_output_clones(drm_device_t *dev, int type_mask)
+{
+ int index_mask = 0;
+ struct drm_output *output;
+ int entry = 0;
+
+ list_for_each_entry(output, &dev->mode_config.output_list, head) {
+ struct intel_output *intel_output = output->driver_private;
+ if (type_mask & (1 << intel_output->type))
+ index_mask |= (1 << entry);
+ entry++;
+ }
+ return index_mask;
+}
+
+
+static void intel_setup_outputs(drm_device_t *dev)
+{
+ struct drm_output *output;
+
+ intel_crt_init(dev);
+
+ /* Set up integrated LVDS */
+ if (IS_MOBILE(dev) && !IS_I830(dev))
+ intel_lvds_init(dev);
+
+ if (IS_I9XX(dev)) {
+ intel_sdvo_init(dev, SDVOB);
+ intel_sdvo_init(dev, SDVOC);
+ }
+
+ list_for_each_entry(output, &dev->mode_config.output_list, head) {
+ struct intel_output *intel_output = output->driver_private;
+ int crtc_mask = 0, clone_mask = 0;
+
+ /* valid crtcs */
+ switch(intel_output->type) {
+ case INTEL_OUTPUT_DVO:
+ case INTEL_OUTPUT_SDVO:
+ crtc_mask = ((1 << 0)|
+ (1 << 1));
+ clone_mask = ((1 << INTEL_OUTPUT_ANALOG) |
+ (1 << INTEL_OUTPUT_DVO) |
+ (1 << INTEL_OUTPUT_SDVO));
+ break;
+ case INTEL_OUTPUT_ANALOG:
+ crtc_mask = ((1 << 0));
+ clone_mask = ((1 << INTEL_OUTPUT_ANALOG) |
+ (1 << INTEL_OUTPUT_DVO) |
+ (1 << INTEL_OUTPUT_SDVO));
+ break;
+ case INTEL_OUTPUT_LVDS:
+ crtc_mask = (1 << 1);
+ clone_mask = (1 << INTEL_OUTPUT_LVDS);
+ break;
+ case INTEL_OUTPUT_TVOUT:
+ crtc_mask = ((1 << 0) |
+ (1 << 1));
+ clone_mask = (1 << INTEL_OUTPUT_TVOUT);
+ break;
+ }
+ output->possible_crtcs = crtc_mask;
+ output->possible_clones = intel_output_clones(dev, clone_mask);
+ }
+}
+
+void intel_modeset_init(drm_device_t *dev)
+{
+ int num_pipe;
+ int i;
+
+ drm_mode_config_init(dev);
+
+ dev->mode_config.min_width = 0;
+ dev->mode_config.min_height = 0;
+
+ dev->mode_config.max_width = 4096;
+ dev->mode_config.max_height = 4096;
+
+ if (IS_MOBILE(dev) || IS_I9XX(dev))
+ num_pipe = 2;
+ else
+ num_pipe = 1;
+ DRM_DEBUG("%d display pipe%s available.\n",
+ num_pipe, num_pipe > 1 ? "s" : "");
+
+ for (i = 0; i < num_pipe; i++) {
+ intel_crtc_init(dev, i);
+ }
+
+ intel_setup_outputs(dev);
+
+ //drm_initial_config(dev, false);
+ //drm_set_desired_modes(dev);
+}
+
+void intel_modeset_cleanup(drm_device_t *dev)
+{
+ drm_mode_config_cleanup(dev);
+}
diff --git a/linux-core/intel_drv.h b/linux-core/intel_drv.h
new file mode 100644
index 0000000..aa33437
--- /dev/null
+++ b/linux-core/intel_drv.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2006 Dave Airlie <[email protected]>
+ * Copyright (c) 2007 Intel Corporation
+ * Jesse Barnes <[email protected]>
+ */
+#ifndef __INTEL_DRV_H__
+#define __INTEL_DRV_H__
+
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/i2c-algo-bit.h>
+#include "drm_crtc.h"
+
+/*
+ * Display related stuff
+ */
+
+/* store information about an Ixxx DVO */
+/* The i830->i865 use multiple DVOs with multiple i2cs */
+/* the i915, i945 have a single sDVO i2c bus - which is different */
+#define MAX_OUTPUTS 6
+
+#define INTEL_I2C_BUS_DVO 1
+#define INTEL_I2C_BUS_SDVO 2
+
+/* these are outputs from the chip - integrated only
+ external chips are via DVO or SDVO output */
+#define INTEL_OUTPUT_UNUSED 0
+#define INTEL_OUTPUT_ANALOG 1
+#define INTEL_OUTPUT_DVO 2
+#define INTEL_OUTPUT_SDVO 3
+#define INTEL_OUTPUT_LVDS 4
+#define INTEL_OUTPUT_TVOUT 5
+
+#define INTEL_DVO_CHIP_NONE 0
+#define INTEL_DVO_CHIP_LVDS 1
+#define INTEL_DVO_CHIP_TMDS 2
+#define INTEL_DVO_CHIP_TVOUT 4
+
+struct intel_i2c_chan {
+ drm_device_t *drm_dev; /* for getting at dev. private (mmio etc.) */
+ u32 reg; /* GPIO reg */
+ struct i2c_adapter adapter;
+ struct i2c_algo_bit_data algo;
+ u8 slave_addr;
+};
+
+struct intel_output {
+ int type;
+ struct intel_i2c_chan *i2c_bus; /* for control functions */
+ struct intel_i2c_chan *ddc_bus; /* for DDC only stuff */
+ bool load_detect_tmp;
+ void *dev_priv;
+};
+
+struct intel_crtc {
+ int pipe;
+ u8 lut_r[256], lut_g[256], lut_b[256];
+};
+
+struct intel_i2c_chan *intel_i2c_create(drm_device_t *dev, const u32 reg,
+ const char *name);
+void intel_i2c_destroy(struct intel_i2c_chan *chan);
+int intel_ddc_get_modes(struct drm_output *output);
+extern bool intel_ddc_probe(struct drm_output *output);
+
+extern void intel_crt_init(drm_device_t *dev);
+extern void intel_sdvo_init(drm_device_t *dev, int output_device);
+extern void intel_lvds_init(drm_device_t *dev);
+
+extern void intel_crtc_load_lut(struct drm_crtc *crtc);
+extern void intel_output_prepare (struct drm_output *output);
+extern void intel_output_commit (struct drm_output *output);
+extern struct drm_display_mode *intel_crtc_mode_get(drm_device_t *dev,
+ struct drm_crtc *crtc);
+extern void intel_wait_for_vblank(drm_device_t *dev);
+extern struct drm_crtc *intel_get_crtc_from_pipe(drm_device_t *dev, int pipe);
+
+#endif /* __INTEL_DRV_H__ */
diff --git a/linux-core/intel_i2c.c b/linux-core/intel_i2c.c
new file mode 100644
index 0000000..d4cf7ee
--- /dev/null
+++ b/linux-core/intel_i2c.c
@@ -0,0 +1,187 @@
+/*
+ * 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 <[email protected]>
+ */
+/*
+ * Copyright (c) 2006 Dave Airlie <[email protected]>
+ * Jesse Barnes <[email protected]>
+ */
+
+#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;
+ drm_i915_private_t *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;
+ drm_i915_private_t *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;
+ drm_device_t *dev = chan->drm_dev;
+ drm_i915_private_t *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;
+ drm_device_t *dev = chan->drm_dev;
+ drm_i915_private_t *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(drm_device_t *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;
+ 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 0000000..74b040b
--- /dev/null
+++ b/linux-core/intel_lvds.c
@@ -0,0 +1,500 @@
+/*
+ * Copyright © 2006-2007 Intel Corporation
+ * Copyright (c) 2006 Dave Airlie <[email protected]>
+ *
+ * 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 <[email protected]>
+ * Dave Airlie <[email protected]>
+ * Jesse Barnes <[email protected]>
+ */
+
+#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)
+{
+ drm_i915_private_t *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)
+{
+ drm_i915_private_t *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)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ u32 pp_status;
+
+ if (on) {
+ I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) |
+ POWER_TARGET_ON);
+ do {
+ pp_status = I915_READ(PP_STATUS);
+ } while ((pp_status & PP_ON) == 0);
+
+ intel_lvds_set_backlight(dev, dev_priv->backlight_duty_cycle);
+ } else {
+ intel_lvds_set_backlight(dev, 0);
+
+ I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) &
+ ~POWER_TARGET_ON);
+ do {
+ pp_status = I915_READ(PP_STATUS);
+ } while (pp_status & PP_ON);
+ }
+}
+
+static void intel_lvds_dpms(struct drm_output *output, int mode)
+{
+ struct drm_device *dev = output->dev;
+
+ if (mode == DPMSModeOn)
+ intel_lvds_set_power(dev, true);
+ else
+ intel_lvds_set_power(dev, false);
+
+ /* XXX: We never power down the LVDS pairs. */
+}
+
+static void intel_lvds_save(struct drm_output *output)
+{
+ struct drm_device *dev = output->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+
+ dev_priv->savePP_ON = I915_READ(LVDSPP_ON);
+ dev_priv->savePP_OFF = I915_READ(LVDSPP_OFF);
+ dev_priv->savePP_CONTROL = I915_READ(PP_CONTROL);
+ dev_priv->savePP_CYCLE = I915_READ(PP_CYCLE);
+ dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL);
+ dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL &
+ BACKLIGHT_DUTY_CYCLE_MASK);
+
+ /*
+ * If the light is off at server startup, just make it full brightness
+ */
+ if (dev_priv->backlight_duty_cycle == 0)
+ dev_priv->backlight_duty_cycle =
+ intel_lvds_get_max_backlight(dev);
+}
+
+static void intel_lvds_restore(struct drm_output *output)
+{
+ struct drm_device *dev = output->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+
+ I915_WRITE(BLC_PWM_CTL, dev_priv->saveBLC_PWM_CTL);
+ I915_WRITE(LVDSPP_ON, dev_priv->savePP_ON);
+ I915_WRITE(LVDSPP_OFF, dev_priv->savePP_OFF);
+ I915_WRITE(PP_CYCLE, dev_priv->savePP_CYCLE);
+ I915_WRITE(PP_CONTROL, dev_priv->savePP_CONTROL);
+ if (dev_priv->savePP_CONTROL & POWER_TARGET_ON)
+ intel_lvds_set_power(dev, true);
+ else
+ intel_lvds_set_power(dev, false);
+}
+
+static int intel_lvds_mode_valid(struct drm_output *output,
+ struct drm_display_mode *mode)
+{
+ struct drm_device *dev = output->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_display_mode *fixed_mode = dev_priv->panel_fixed_mode;
+
+ if (fixed_mode) {
+ if (mode->hdisplay > fixed_mode->hdisplay)
+ return MODE_PANEL;
+ if (mode->vdisplay > fixed_mode->vdisplay)
+ return MODE_PANEL;
+ }
+
+ return MODE_OK;
+}
+
+static bool intel_lvds_mode_fixup(struct drm_output *output,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = output->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = output->crtc->driver_private;
+ struct drm_output *tmp_output;
+
+ list_for_each_entry(tmp_output, &dev->mode_config.output_list, head) {
+ if (tmp_output != output && tmp_output->crtc == output->crtc) {
+ printk(KERN_ERR "Can't enable LVDS and another "
+ "output on the same pipe\n");
+ return false;
+ }
+ }
+
+ if (intel_crtc->pipe == 0) {
+ printk(KERN_ERR "Can't support LVDS on pipe A\n");
+ return false;
+ }
+
+ /*
+ * If we have timings from the BIOS for the panel, put them in
+ * to the adjusted mode. The CRTC will be set up for this mode,
+ * with the panel scaling set up to source from the H/VDisplay
+ * of the original mode.
+ */
+ if (dev_priv->panel_fixed_mode != NULL) {
+ adjusted_mode->hdisplay = dev_priv->panel_fixed_mode->hdisplay;
+ adjusted_mode->hsync_start =
+ dev_priv->panel_fixed_mode->hsync_start;
+ adjusted_mode->hsync_end =
+ dev_priv->panel_fixed_mode->hsync_end;
+ adjusted_mode->htotal = dev_priv->panel_fixed_mode->htotal;
+ adjusted_mode->vdisplay = dev_priv->panel_fixed_mode->vdisplay;
+ adjusted_mode->vsync_start =
+ dev_priv->panel_fixed_mode->vsync_start;
+ adjusted_mode->vsync_end =
+ dev_priv->panel_fixed_mode->vsync_end;
+ adjusted_mode->vtotal = dev_priv->panel_fixed_mode->vtotal;
+ adjusted_mode->clock = dev_priv->panel_fixed_mode->clock;
+ drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);
+ }
+
+ /*
+ * XXX: It would be nice to support lower refresh rates on the
+ * panels to reduce power consumption, and perhaps match the
+ * user's requested refresh rate.
+ */
+
+ return true;
+}
+
+static void intel_lvds_prepare(struct drm_output *output)
+{
+ struct drm_device *dev = output->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+
+ dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL);
+ dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL &
+ BACKLIGHT_DUTY_CYCLE_MASK);
+
+ intel_lvds_set_power(dev, false);
+}
+
+static void intel_lvds_commit( struct drm_output *output)
+{
+ struct drm_device *dev = output->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+
+ if (dev_priv->backlight_duty_cycle == 0)
+ dev_priv->backlight_duty_cycle =
+ intel_lvds_get_max_backlight(dev);
+
+ intel_lvds_set_power(dev, true);
+}
+
+static void intel_lvds_mode_set(struct drm_output *output,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = output->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = output->crtc->driver_private;
+ u32 pfit_control;
+
+ /*
+ * The LVDS pin pair will already have been turned on in the
+ * intel_crtc_mode_set since it has a large impact on the DPLL
+ * settings.
+ */
+
+ /*
+ * Enable automatic panel scaling so that non-native modes fill the
+ * screen. Should be enabled before the pipe is enabled, according to
+ * register description and PRM.
+ */
+ pfit_control = (PFIT_ENABLE | VERT_AUTO_SCALE | HORIZ_AUTO_SCALE |
+ VERT_INTERP_BILINEAR | HORIZ_INTERP_BILINEAR);
+
+ if (!IS_I965G(dev)) {
+ if (dev_priv->panel_wants_dither)
+ pfit_control |= PANEL_8TO6_DITHER_ENABLE;
+ }
+ else
+ pfit_control |= intel_crtc->pipe << PFIT_PIPE_SHIFT;
+
+ I915_WRITE(PFIT_CONTROL, pfit_control);
+}
+
+/**
+ * Detect the LVDS connection.
+ *
+ * This always returns OUTPUT_STATUS_CONNECTED. This output should only have
+ * been set up if the LVDS was actually connected anyway.
+ */
+static enum drm_output_status intel_lvds_detect(struct drm_output *output)
+{
+ return output_status_connected;
+}
+
+/**
+ * Return the list of DDC modes if available, or the BIOS fixed mode otherwise.
+ */
+static int intel_lvds_get_modes(struct drm_output *output)
+{
+ struct drm_device *dev = output->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ int ret = 0;
+
+ ret = intel_ddc_get_modes(output);
+
+ if (ret)
+ return ret;
+
+ /* Didn't get an EDID */
+ if (!output->monitor_info) {
+ struct drm_display_info *dspinfo;
+ dspinfo = kzalloc(sizeof(*output->monitor_info), GFP_KERNEL);
+ if (!dspinfo)
+ goto out;
+
+ /* Set wide sync ranges so we get all modes
+ * handed to valid_mode for checking
+ */
+ dspinfo->min_vfreq = 0;
+ dspinfo->max_vfreq = 200;
+ dspinfo->min_hfreq = 0;
+ dspinfo->max_hfreq = 200;
+ output->monitor_info = dspinfo;
+ }
+
+out:
+ if (dev_priv->panel_fixed_mode != NULL) {
+ struct drm_display_mode *mode =
+ drm_mode_duplicate(dev, dev_priv->panel_fixed_mode);
+ drm_mode_probed_add(output, mode);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * intel_lvds_destroy - unregister and free LVDS structures
+ * @output: output to free
+ *
+ * Unregister the DDC bus for this output then free the driver private
+ * structure.
+ */
+static void intel_lvds_destroy(struct drm_output *output)
+{
+ struct intel_output *intel_output = output->driver_private;
+
+ intel_i2c_destroy(intel_output->ddc_bus);
+ kfree(output->driver_private);
+}
+
+static const struct drm_output_funcs intel_lvds_output_funcs = {
+ .dpms = intel_lvds_dpms,
+ .save = intel_lvds_save,
+ .restore = intel_lvds_restore,
+ .mode_valid = intel_lvds_mode_valid,
+ .mode_fixup = intel_lvds_mode_fixup,
+ .prepare = intel_lvds_prepare,
+ .mode_set = intel_lvds_mode_set,
+ .commit = intel_lvds_commit,
+ .detect = intel_lvds_detect,
+ .get_modes = intel_lvds_get_modes,
+ .cleanup = intel_lvds_destroy
+};
+
+/**
+ * intel_lvds_init - setup LVDS outputs on this device
+ * @dev: drm device
+ *
+ * Create the output, register the LVDS DDC bus, and try to figure out what
+ * modes we can display on the LVDS panel (if present).
+ */
+void intel_lvds_init(struct drm_device *dev)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_output *output;
+ struct intel_output *intel_output;
+ struct drm_display_mode *scan; /* *modes, *bios_mode; */
+ struct drm_crtc *crtc;
+ u32 lvds;
+ int pipe;
+
+ output = drm_output_create(dev, &intel_lvds_output_funcs, "LVDS");
+ if (!output)
+ return;
+
+ intel_output = kmalloc(sizeof(struct intel_output), GFP_KERNEL);
+ if (!intel_output) {
+ drm_output_destroy(output);
+ return;
+ }
+
+ intel_output->type = INTEL_OUTPUT_LVDS;
+ output->driver_private = intel_output;
+ output->subpixel_order = SubPixelHorizontalRGB;
+ output->interlace_allowed = FALSE;
+ output->doublescan_allowed = FALSE;
+
+ /* Set up the DDC bus. */
+ intel_output->ddc_bus = intel_i2c_create(dev, GPIOC, "LVDSDDC_C");
+ if (!intel_output->ddc_bus) {
+ dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration "
+ "failed.\n");
+ return;
+ }
+
+ /*
+ * Attempt to get the fixed panel mode from DDC. Assume that the
+ * preferred mode is the right one.
+ */
+ intel_ddc_get_modes(output);
+
+ list_for_each_entry(scan, &output->probed_modes, head) {
+ if (scan->type & DRM_MODE_TYPE_PREFERRED) {
+ dev_priv->panel_fixed_mode =
+ drm_mode_duplicate(dev, scan);
+ goto out; /* FIXME: check for quirks */
+ }
+ }
+
+ /*
+ * If we didn't get EDID, try checking if the panel is already turned
+ * on. If so, assume that whatever is currently programmed is the
+ * correct mode.
+ */
+ lvds = I915_READ(LVDS);
+ pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0;
+ crtc = intel_get_crtc_from_pipe(dev, pipe);
+
+ if (crtc && (lvds & LVDS_PORT_EN)) {
+ dev_priv->panel_fixed_mode = intel_crtc_mode_get(dev, crtc);
+ if (dev_priv->panel_fixed_mode) {
+ dev_priv->panel_fixed_mode->type |=
+ DRM_MODE_TYPE_PREFERRED;
+ goto out; /* FIXME: check for quirks */
+ }
+ }
+
+ /* If we still don't have a mode after all that, give up. */
+ if (!dev_priv->panel_fixed_mode)
+ goto failed;
+
+ /* FIXME: probe the BIOS for modes and check for LVDS quirks */
+#if 0
+ /* Get the LVDS fixed mode out of the BIOS. We should support LVDS
+ * with the BIOS being unavailable or broken, but lack the
+ * configuration options for now.
+ */
+ bios_mode = intel_bios_get_panel_mode(pScrn);
+ if (bios_mode != NULL) {
+ if (dev_priv->panel_fixed_mode != NULL) {
+ if (dev_priv->debug_modes &&
+ !xf86ModesEqual(dev_priv->panel_fixed_mode,
+ bios_mode))
+ {
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "BIOS panel mode data doesn't match probed data, "
+ "continuing with probed.\n");
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "BIOS mode:\n");
+ xf86PrintModeline(pScrn->scrnIndex, bios_mode);
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "probed mode:\n");
+ xf86PrintModeline(pScrn->scrnIndex, dev_priv->panel_fixed_mode);
+ xfree(bios_mode->name);
+ xfree(bios_mode);
+ }
+ } else {
+ dev_priv->panel_fixed_mode = bios_mode;
+ }
+ } else {
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "Couldn't detect panel mode. Disabling panel\n");
+ goto disable_exit;
+ }
+
+ /*
+ * Blacklist machines with BIOSes that list an LVDS panel without
+ * actually having one.
+ */
+ if (dev_priv->PciInfo->chipType == PCI_CHIP_I945_GM) {
+ /* aopen mini pc */
+ if (dev_priv->PciInfo->subsysVendor == 0xa0a0)
+ goto disable_exit;
+
+ if ((dev_priv->PciInfo->subsysVendor == 0x8086) &&
+ (dev_priv->PciInfo->subsysCard == 0x7270)) {
+ /* It's a Mac Mini or Macbook Pro.
+ *
+ * Apple hardware is out to get us. The macbook pro
+ * has a real LVDS panel, but the mac mini does not,
+ * and they have the same device IDs. We'll
+ * distinguish by panel size, on the assumption
+ * that Apple isn't about to make any machines with an
+ * 800x600 display.
+ */
+
+ if (dev_priv->panel_fixed_mode != NULL &&
+ dev_priv->panel_fixed_mode->HDisplay == 800 &&
+ dev_priv->panel_fixed_mode->VDisplay == 600)
+ {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Suspected Mac Mini, ignoring the LVDS\n");
+ goto disable_exit;
+ }
+ }
+ }
+
+#endif
+
+out:
+ return;
+
+failed:
+ DRM_DEBUG("No LVDS modes found, disabling.\n");
+ drm_output_destroy(output); /* calls intel_lvds_destroy above */
+}
diff --git a/linux-core/intel_modes.c b/linux-core/intel_modes.c
new file mode 100644
index 0000000..601770e
--- /dev/null
+++ b/linux-core/intel_modes.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2007 Dave Airlie <[email protected]>
+ * Copyright (c) 2007 Intel Corporation
+ * Jesse Barnes <[email protected]>
+ */
+
+#include <linux/i2c.h>
+#include <linux/fb.h>
+#include "drmP.h"
+#include "intel_drv.h"
+
+/**
+ * intel_ddc_probe
+ *
+ */
+bool intel_ddc_probe(struct drm_output *output)
+{
+ struct intel_output *intel_output = output->driver_private;
+ u8 out_buf[] = { 0x0, 0x0};
+ u8 buf[2];
+ int ret;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = 0x50,
+ .flags = 0,
+ .len = 1,
+ .buf = out_buf,
+ },
+ {
+ .addr = 0x50,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = buf,
+ }
+ };
+
+ ret = i2c_transfer(&intel_output->ddc_bus->adapter, msgs, 2);
+ if (ret == 2)
+ return true;
+
+ return false;
+}
+
+/**
+ * intel_ddc_get_modes - get modelist from monitor
+ * @output: DRM output device to use
+ *
+ * Fetch the EDID information from @output using the DDC bus.
+ */
+int intel_ddc_get_modes(struct drm_output *output)
+{
+ struct intel_output *intel_output = output->driver_private;
+
+ return drm_add_edid_modes(output, &intel_output->ddc_bus->adapter);
+}
diff --git a/linux-core/intel_sdvo.c b/linux-core/intel_sdvo.c
new file mode 100644
index 0000000..98c4034
--- /dev/null
+++ b/linux-core/intel_sdvo.c
@@ -0,0 +1,1095 @@
+/*
+ * 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 <[email protected]>
+ */
+/*
+ * Copyright 2006 Dave Airlie <[email protected]>
+ * Jesse Barnes <[email protected]>
+ */
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+#include "intel_sdvo_regs.h"
+
+struct intel_sdvo_priv {
+ struct intel_i2c_chan *i2c_bus;
+ int slaveaddr;
+ int output_device;
+
+ u16 active_outputs;
+
+ struct intel_sdvo_caps caps;
+ int pixel_clock_min, pixel_clock_max;
+
+ int save_sdvo_mult;
+ u16 save_active_outputs;
+ struct intel_sdvo_dtd save_input_dtd_1, save_input_dtd_2;
+ struct intel_sdvo_dtd save_output_dtd[16];
+ u32 save_SDVOX;
+};
+
+/**
+ * Writes the SDVOB or SDVOC with the given value, but always writes both
+ * SDVOB and SDVOC to work around apparent hardware issues (according to
+ * comments in the BIOS).
+ */
+static void intel_sdvo_write_sdvox(struct drm_output *output, u32 val)
+{
+ drm_device_t *dev = output->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct intel_output *intel_output = output->driver_private;
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ u32 bval = val, cval = val;
+ int i;
+
+ if (sdvo_priv->output_device == SDVOB)
+ cval = I915_READ(SDVOC);
+ else
+ bval = I915_READ(SDVOB);
+ /*
+ * Write the registers twice for luck. Sometimes,
+ * writing them only once doesn't appear to 'stick'.
+ * The BIOS does this too. Yay, magic
+ */
+ for (i = 0; i < 2; i++)
+ {
+ I915_WRITE(SDVOB, bval);
+ I915_READ(SDVOB);
+ I915_WRITE(SDVOC, cval);
+ I915_READ(SDVOC);
+ }
+}
+
+static bool intel_sdvo_read_byte(struct drm_output *output, u8 addr,
+ u8 *ch)
+{
+ struct intel_output *intel_output = output->driver_private;
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ u8 out_buf[2];
+ u8 buf[2];
+ int ret;
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = sdvo_priv->i2c_bus->slave_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = out_buf,
+ },
+ {
+ .addr = sdvo_priv->i2c_bus->slave_addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = buf,
+ }
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = 0;
+
+ if ((ret = i2c_transfer(&sdvo_priv->i2c_bus->adapter, msgs, 2)) == 2)
+ {
+// DRM_DEBUG("got back from addr %02X = %02x\n", out_buf[0], buf[0]);
+ *ch = buf[0];
+ return true;
+ }
+
+ DRM_DEBUG("i2c transfer returned %d\n", ret);
+ return false;
+}
+
+
+static bool intel_sdvo_read_byte_quiet(struct drm_output *output, int addr,
+ u8 *ch)
+{
+ return true;
+
+}
+
+static bool intel_sdvo_write_byte(struct drm_output *output, int addr,
+ u8 ch)
+{
+ struct intel_output *intel_output = output->driver_private;
+ u8 out_buf[2];
+ struct i2c_msg msgs[] = {
+ {
+ .addr = intel_output->i2c_bus->slave_addr,
+ .flags = 0,
+ .len = 2,
+ .buf = out_buf,
+ }
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = ch;
+
+ if (i2c_transfer(&intel_output->i2c_bus->adapter, msgs, 1) == 1)
+ {
+ return true;
+ }
+ return false;
+}
+
+#define SDVO_CMD_NAME_ENTRY(cmd) {cmd, #cmd}
+/** Mapping of command numbers to names, for debug output */
+const static struct _sdvo_cmd_name {
+ u8 cmd;
+ char *name;
+} sdvo_cmd_names[] = {
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_RESET),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DEVICE_CAPS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FIRMWARE_REV),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TRAINED_INPUTS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_OUTPUTS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_OUTPUTS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_IN_OUT_MAP),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_IN_OUT_MAP),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ATTACHED_DISPLAYS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HOT_PLUG_SUPPORT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_HOT_PLUG),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_HOT_PLUG),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_INPUT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_OUTPUT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART2),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART2),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART2),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART2),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CLOCK_RATE_MULT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CLOCK_RATE_MULT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_TV_FORMATS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_FORMAT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_FORMAT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_RESOLUTION_SUPPORT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTROL_BUS_SWITCH),
+};
+
+#define SDVO_NAME(dev_priv) ((dev_priv)->output_device == SDVOB ? "SDVOB" : "SDVOC")
+#define SDVO_PRIV(output) ((struct intel_sdvo_priv *) (output)->dev_priv)
+
+static void intel_sdvo_write_cmd(struct drm_output *output, u8 cmd,
+ void *args, int args_len)
+{
+ struct intel_output *intel_output = output->driver_private;
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ int i;
+
+ if (1) {
+ printk("%s: W: %02X ", SDVO_NAME(sdvo_priv), cmd);
+ for (i = 0; i < args_len; i++)
+ printk("%02X ", ((u8 *)args)[i]);
+ for (; i < 8; i++)
+ printk(" ");
+ for (i = 0; i < sizeof(sdvo_cmd_names) / sizeof(sdvo_cmd_names[0]); i++) {
+ if (cmd == sdvo_cmd_names[i].cmd) {
+ printk("(%s)", sdvo_cmd_names[i].name);
+ break;
+ }
+ }
+ if (i == sizeof(sdvo_cmd_names)/ sizeof(sdvo_cmd_names[0]))
+ printk("(%02X)",cmd);
+ printk("\n");
+ }
+
+ for (i = 0; i < args_len; i++) {
+ intel_sdvo_write_byte(output, SDVO_I2C_ARG_0 - i, ((u8*)args)[i]);
+ }
+
+ intel_sdvo_write_byte(output, SDVO_I2C_OPCODE, cmd);
+}
+
+static const char *cmd_status_names[] = {
+ "Power on",
+ "Success",
+ "Not supported",
+ "Invalid arg",
+ "Pending",
+ "Target not specified",
+ "Scaling not supported"
+};
+
+static u8 intel_sdvo_read_response(struct drm_output *output, void *response,
+ int response_len)
+{
+ struct intel_output *intel_output = output->driver_private;
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ int i;
+ u8 status;
+ u8 retry = 50;
+
+ while (retry--) {
+ /* Read the command response */
+ for (i = 0; i < response_len; i++) {
+ intel_sdvo_read_byte(output, SDVO_I2C_RETURN_0 + i,
+ &((u8 *)response)[i]);
+ }
+
+ /* read the return status */
+ intel_sdvo_read_byte(output, SDVO_I2C_CMD_STATUS, &status);
+
+ if (1) {
+ printk("%s: R: ", SDVO_NAME(sdvo_priv));
+ for (i = 0; i < response_len; i++)
+ printk("%02X ", ((u8 *)response)[i]);
+ for (; i < 8; i++)
+ printk(" ");
+ if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP)
+ printk("(%s)", cmd_status_names[status]);
+ else
+ printk("(??? %d)", status);
+ printk("\n");
+ }
+
+ if (status != SDVO_CMD_STATUS_PENDING)
+ return status;
+
+ mdelay(50);
+ }
+
+ return status;
+}
+
+int intel_sdvo_get_pixel_multiplier(struct drm_display_mode *mode)
+{
+ if (mode->clock >= 100000)
+ return 1;
+ else if (mode->clock >= 50000)
+ return 2;
+ else
+ return 4;
+}
+
+/**
+ * Don't check status code from this as it switches the bus back to the
+ * SDVO chips which defeats the purpose of doing a bus switch in the first
+ * place.
+ */
+void intel_sdvo_set_control_bus_switch(struct drm_output *output, u8 target)
+{
+ intel_sdvo_write_cmd(output, SDVO_CMD_SET_CONTROL_BUS_SWITCH, &target, 1);
+}
+
+static bool intel_sdvo_set_target_input(struct drm_output *output, bool target_0, bool target_1)
+{
+ struct intel_sdvo_set_target_input_args targets = {0};
+ u8 status;
+
+ if (target_0 && target_1)
+ return SDVO_CMD_STATUS_NOTSUPP;
+
+ if (target_1)
+ targets.target_1 = 1;
+
+ intel_sdvo_write_cmd(output, SDVO_CMD_SET_TARGET_INPUT, &targets,
+ sizeof(targets));
+
+ status = intel_sdvo_read_response(output, NULL, 0);
+
+ return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+/**
+ * Return whether each input is trained.
+ *
+ * This function is making an assumption about the layout of the response,
+ * which should be checked against the docs.
+ */
+static bool intel_sdvo_get_trained_inputs(struct drm_output *output, bool *input_1, bool *input_2)
+{
+ struct intel_sdvo_get_trained_inputs_response response;
+ u8 status;
+
+ intel_sdvo_write_cmd(output, SDVO_CMD_GET_TRAINED_INPUTS, NULL, 0);
+ status = intel_sdvo_read_response(output, &response, sizeof(response));
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ *input_1 = response.input0_trained;
+ *input_2 = response.input1_trained;
+ return true;
+}
+
+static bool intel_sdvo_get_active_outputs(struct drm_output *output,
+ u16 *outputs)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(output, SDVO_CMD_GET_ACTIVE_OUTPUTS, NULL, 0);
+ status = intel_sdvo_read_response(output, outputs, sizeof(*outputs));
+
+ return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+static bool intel_sdvo_set_active_outputs(struct drm_output *output,
+ u16 outputs)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(output, SDVO_CMD_SET_ACTIVE_OUTPUTS, &outputs,
+ sizeof(outputs));
+ status = intel_sdvo_read_response(output, NULL, 0);
+ return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+static bool intel_sdvo_set_encoder_power_state(struct drm_output *output,
+ int mode)
+{
+ u8 status, state = SDVO_ENCODER_STATE_ON;
+
+ switch (mode) {
+ case DPMSModeOn:
+ state = SDVO_ENCODER_STATE_ON;
+ break;
+ case DPMSModeStandby:
+ state = SDVO_ENCODER_STATE_STANDBY;
+ break;
+ case DPMSModeSuspend:
+ state = SDVO_ENCODER_STATE_SUSPEND;
+ break;
+ case DPMSModeOff:
+ state = SDVO_ENCODER_STATE_OFF;
+ break;
+ }
+
+ intel_sdvo_write_cmd(output, SDVO_CMD_SET_ENCODER_POWER_STATE, &state,
+ sizeof(state));
+ status = intel_sdvo_read_response(output, NULL, 0);
+
+ return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+static bool intel_sdvo_get_input_pixel_clock_range(struct drm_output *output,
+ int *clock_min,
+ int *clock_max)
+{
+ struct intel_sdvo_pixel_clock_range clocks;
+ u8 status;
+
+ intel_sdvo_write_cmd(output, SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE,
+ NULL, 0);
+
+ status = intel_sdvo_read_response(output, &clocks, sizeof(clocks));
+
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ /* Convert the values from units of 10 kHz to kHz. */
+ *clock_min = clocks.min * 10;
+ *clock_max = clocks.max * 10;
+
+ return true;
+}
+
+static bool intel_sdvo_set_target_output(struct drm_output *output,
+ u16 outputs)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(output, SDVO_CMD_SET_TARGET_OUTPUT, &outputs,
+ sizeof(outputs));
+
+ status = intel_sdvo_read_response(output, NULL, 0);
+ return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+static bool intel_sdvo_get_timing(struct drm_output *output, u8 cmd,
+ struct intel_sdvo_dtd *dtd)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(output, cmd, NULL, 0);
+ status = intel_sdvo_read_response(output, &dtd->part1,
+ sizeof(dtd->part1));
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ intel_sdvo_write_cmd(output, cmd + 1, NULL, 0);
+ status = intel_sdvo_read_response(output, &dtd->part2,
+ sizeof(dtd->part2));
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ return true;
+}
+
+static bool intel_sdvo_get_input_timing(struct drm_output *output,
+ struct intel_sdvo_dtd *dtd)
+{
+ return intel_sdvo_get_timing(output,
+ SDVO_CMD_GET_INPUT_TIMINGS_PART1, dtd);
+}
+
+static bool intel_sdvo_get_output_timing(struct drm_output *output,
+ struct intel_sdvo_dtd *dtd)
+{
+ return intel_sdvo_get_timing(output,
+ SDVO_CMD_GET_OUTPUT_TIMINGS_PART1, dtd);
+}
+
+static bool intel_sdvo_set_timing(struct drm_output *output, u8 cmd,
+ struct intel_sdvo_dtd *dtd)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(output, cmd, &dtd->part1, sizeof(dtd->part1));
+ status = intel_sdvo_read_response(output, NULL, 0);
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ intel_sdvo_write_cmd(output, cmd + 1, &dtd->part2, sizeof(dtd->part2));
+ status = intel_sdvo_read_response(output, NULL, 0);
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ return true;
+}
+
+static bool intel_sdvo_set_input_timing(struct drm_output *output,
+ struct intel_sdvo_dtd *dtd)
+{
+ return intel_sdvo_set_timing(output,
+ SDVO_CMD_SET_INPUT_TIMINGS_PART1, dtd);
+}
+
+static bool intel_sdvo_set_output_timing(struct drm_output *output,
+ struct intel_sdvo_dtd *dtd)
+{
+ return intel_sdvo_set_timing(output,
+ SDVO_CMD_SET_OUTPUT_TIMINGS_PART1, dtd);
+}
+
+#if 0
+static bool intel_sdvo_get_preferred_input_timing(struct drm_output *output,
+ struct intel_sdvo_dtd *dtd)
+{
+ struct intel_output *intel_output = output->driver_private;
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ u8 status;
+
+ intel_sdvo_write_cmd(output, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1,
+ NULL, 0);
+
+ status = intel_sdvo_read_response(output, &dtd->part1,
+ sizeof(dtd->part1));
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ intel_sdvo_write_cmd(output, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2,
+ NULL, 0);
+ status = intel_sdvo_read_response(output, &dtd->part2,
+ sizeof(dtd->part2));
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ return true;
+}
+#endif
+
+static int intel_sdvo_get_clock_rate_mult(struct drm_output *output)
+{
+ struct intel_output *intel_output = output->driver_private;
+ u8 response, status;
+
+ intel_sdvo_write_cmd(output, SDVO_CMD_GET_CLOCK_RATE_MULT, NULL, 0);
+ status = intel_sdvo_read_response(output, &response, 1);
+
+ if (status != SDVO_CMD_STATUS_SUCCESS) {
+ DRM_DEBUG("Couldn't get SDVO clock rate multiplier\n");
+ return SDVO_CLOCK_RATE_MULT_1X;
+ } else {
+ DRM_DEBUG("Current clock rate multiplier: %d\n", response);
+ }
+
+ return response;
+}
+
+static bool intel_sdvo_set_clock_rate_mult(struct drm_output *output, u8 val)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(output, SDVO_CMD_SET_CLOCK_RATE_MULT, &val, 1);
+ status = intel_sdvo_read_response(output, NULL, 0);
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ return true;
+}
+
+static bool intel_sdvo_mode_fixup(struct drm_output *output,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ /* Make the CRTC code factor in the SDVO pixel multiplier. The SDVO
+ * device will be told of the multiplier during mode_set.
+ */
+ adjusted_mode->clock *= intel_sdvo_get_pixel_multiplier(mode);
+ return true;
+}
+
+static void intel_sdvo_mode_set(struct drm_output *output,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ drm_device_t *dev = output->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_crtc *crtc = output->crtc;
+ struct intel_crtc *intel_crtc = crtc->driver_private;
+ struct intel_output *intel_output = output->driver_private;
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ u16 width, height;
+ u16 h_blank_len, h_sync_len, v_blank_len, v_sync_len;
+ u16 h_sync_offset, v_sync_offset;
+ u32 sdvox;
+ struct intel_sdvo_dtd output_dtd;
+ int sdvo_pixel_multiply;
+
+ if (!mode)
+ return;
+
+ width = mode->crtc_hdisplay;
+ height = mode->crtc_vdisplay;
+
+ /* do some mode translations */
+ h_blank_len = mode->crtc_hblank_end - mode->crtc_hblank_start;
+ h_sync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
+
+ v_blank_len = mode->crtc_vblank_end - mode->crtc_vblank_start;
+ v_sync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
+
+ h_sync_offset = mode->crtc_hsync_start - mode->crtc_hblank_start;
+ v_sync_offset = mode->crtc_vsync_start - mode->crtc_vblank_start;
+
+ output_dtd.part1.clock = mode->clock / 10;
+ output_dtd.part1.h_active = width & 0xff;
+ output_dtd.part1.h_blank = h_blank_len & 0xff;
+ output_dtd.part1.h_high = (((width >> 8) & 0xf) << 4) |
+ ((h_blank_len >> 8) & 0xf);
+ output_dtd.part1.v_active = height & 0xff;
+ output_dtd.part1.v_blank = v_blank_len & 0xff;
+ output_dtd.part1.v_high = (((height >> 8) & 0xf) << 4) |
+ ((v_blank_len >> 8) & 0xf);
+
+ output_dtd.part2.h_sync_off = h_sync_offset;
+ output_dtd.part2.h_sync_width = h_sync_len & 0xff;
+ output_dtd.part2.v_sync_off_width = (v_sync_offset & 0xf) << 4 |
+ (v_sync_len & 0xf);
+ output_dtd.part2.sync_off_width_high = ((h_sync_offset & 0x300) >> 2) |
+ ((h_sync_len & 0x300) >> 4) | ((v_sync_offset & 0x30) >> 2) |
+ ((v_sync_len & 0x30) >> 4);
+
+ output_dtd.part2.dtd_flags = 0x18;
+ if (mode->flags & V_PHSYNC)
+ output_dtd.part2.dtd_flags |= 0x2;
+ if (mode->flags & V_PVSYNC)
+ output_dtd.part2.dtd_flags |= 0x4;
+
+ output_dtd.part2.sdvo_flags = 0;
+ output_dtd.part2.v_sync_off_high = v_sync_offset & 0xc0;
+ output_dtd.part2.reserved = 0;
+
+ /* Set the output timing to the screen */
+ intel_sdvo_set_target_output(output, sdvo_priv->active_outputs);
+ intel_sdvo_set_output_timing(output, &output_dtd);
+
+ /* Set the input timing to the screen. Assume always input 0. */
+ intel_sdvo_set_target_input(output, true, false);
+
+ /* We would like to use i830_sdvo_create_preferred_input_timing() to
+ * provide the device with a timing it can support, if it supports that
+ * feature. However, presumably we would need to adjust the CRTC to
+ * output the preferred timing, and we don't support that currently.
+ */
+#if 0
+ success = intel_sdvo_create_preferred_input_timing(output, clock,
+ width, height);
+ if (success) {
+ struct intel_sdvo_dtd *input_dtd;
+
+ intel_sdvo_get_preferred_input_timing(output, &input_dtd);
+ intel_sdvo_set_input_timing(output, &input_dtd);
+ }
+#else
+ intel_sdvo_set_input_timing(output, &output_dtd);
+#endif
+
+ switch (intel_sdvo_get_pixel_multiplier(mode)) {
+ case 1:
+ intel_sdvo_set_clock_rate_mult(output,
+ SDVO_CLOCK_RATE_MULT_1X);
+ break;
+ case 2:
+ intel_sdvo_set_clock_rate_mult(output,
+ SDVO_CLOCK_RATE_MULT_2X);
+ break;
+ case 4:
+ intel_sdvo_set_clock_rate_mult(output,
+ SDVO_CLOCK_RATE_MULT_4X);
+ break;
+ }
+
+ /* Set the SDVO control regs. */
+ if (0/*IS_I965GM(dev)*/) {
+ sdvox = SDVO_BORDER_ENABLE;
+ } else {
+ sdvox = I915_READ(sdvo_priv->output_device);
+ switch (sdvo_priv->output_device) {
+ case SDVOB:
+ sdvox &= SDVOB_PRESERVE_MASK;
+ break;
+ case SDVOC:
+ sdvox &= SDVOC_PRESERVE_MASK;
+ break;
+ }
+ sdvox |= (9 << 19) | SDVO_BORDER_ENABLE;
+ }
+ if (intel_crtc->pipe == 1)
+ sdvox |= SDVO_PIPE_B_SELECT;
+
+ sdvo_pixel_multiply = intel_sdvo_get_pixel_multiplier(mode);
+ if (IS_I965G(dev)) {
+ /* done in crtc_mode_set as the dpll_md reg must be written
+ early */
+ } else if (IS_I945G(dev) || IS_I945GM(dev)) {
+ /* done in crtc_mode_set as it lives inside the
+ dpll register */
+ } else {
+ sdvox |= (sdvo_pixel_multiply - 1) << SDVO_PORT_MULTIPLY_SHIFT;
+ }
+
+ intel_sdvo_write_sdvox(output, sdvox);
+}
+
+static void intel_sdvo_dpms(struct drm_output *output, int mode)
+{
+ drm_device_t *dev = output->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct intel_output *intel_output = output->driver_private;
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ u32 temp;
+
+ if (mode != DPMSModeOn) {
+ intel_sdvo_set_active_outputs(output, 0);
+ if (0)
+ intel_sdvo_set_encoder_power_state(output, mode);
+
+ if (mode == DPMSModeOff) {
+ temp = I915_READ(sdvo_priv->output_device);
+ if ((temp & SDVO_ENABLE) != 0) {
+ intel_sdvo_write_sdvox(output, temp & ~SDVO_ENABLE);
+ }
+ }
+ } else {
+ bool input1, input2;
+ int i;
+ u8 status;
+
+ temp = I915_READ(sdvo_priv->output_device);
+ if ((temp & SDVO_ENABLE) == 0)
+ intel_sdvo_write_sdvox(output, temp | SDVO_ENABLE);
+ for (i = 0; i < 2; i++)
+ intel_wait_for_vblank(dev);
+
+ status = intel_sdvo_get_trained_inputs(output, &input1,
+ &input2);
+
+
+ /* Warn if the device reported failure to sync. */
+ if (status == SDVO_CMD_STATUS_SUCCESS && !input1) {
+ DRM_ERROR("First %s output reported failure to sync\n",
+ SDVO_NAME(sdvo_priv));
+ }
+
+ if (0)
+ intel_sdvo_set_encoder_power_state(output, mode);
+ intel_sdvo_set_active_outputs(output, sdvo_priv->active_outputs);
+ }
+ return;
+}
+
+static void intel_sdvo_save(struct drm_output *output)
+{
+ drm_device_t *dev = output->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct intel_output *intel_output = output->driver_private;
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ int o;
+
+ sdvo_priv->save_sdvo_mult = intel_sdvo_get_clock_rate_mult(output);
+ intel_sdvo_get_active_outputs(output, &sdvo_priv->save_active_outputs);
+
+ if (sdvo_priv->caps.sdvo_inputs_mask & 0x1) {
+ intel_sdvo_set_target_input(output, true, false);
+ intel_sdvo_get_input_timing(output,
+ &sdvo_priv->save_input_dtd_1);
+ }
+
+ if (sdvo_priv->caps.sdvo_inputs_mask & 0x2) {
+ intel_sdvo_set_target_input(output, false, true);
+ intel_sdvo_get_input_timing(output,
+ &sdvo_priv->save_input_dtd_2);
+ }
+
+ for (o = SDVO_OUTPUT_FIRST; o <= SDVO_OUTPUT_LAST; o++)
+ {
+ u16 this_output = (1 << o);
+ if (sdvo_priv->caps.output_flags & this_output)
+ {
+ intel_sdvo_set_target_output(output, this_output);
+ intel_sdvo_get_output_timing(output,
+ &sdvo_priv->save_output_dtd[o]);
+ }
+ }
+
+ sdvo_priv->save_SDVOX = I915_READ(sdvo_priv->output_device);
+}
+
+static void intel_sdvo_restore(struct drm_output *output)
+{
+ drm_device_t *dev = output->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct intel_output *intel_output = output->driver_private;
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ int o;
+ int i;
+ bool input1, input2;
+ u8 status;
+
+ intel_sdvo_set_active_outputs(output, 0);
+
+ for (o = SDVO_OUTPUT_FIRST; o <= SDVO_OUTPUT_LAST; o++)
+ {
+ u16 this_output = (1 << o);
+ if (sdvo_priv->caps.output_flags & this_output) {
+ intel_sdvo_set_target_output(output, this_output);
+ intel_sdvo_set_output_timing(output, &sdvo_priv->save_output_dtd[o]);
+ }
+ }
+
+ if (sdvo_priv->caps.sdvo_inputs_mask & 0x1) {
+ intel_sdvo_set_target_input(output, true, false);
+ intel_sdvo_set_input_timing(output, &sdvo_priv->save_input_dtd_1);
+ }
+
+ if (sdvo_priv->caps.sdvo_inputs_mask & 0x2) {
+ intel_sdvo_set_target_input(output, false, true);
+ intel_sdvo_set_input_timing(output, &sdvo_priv->save_input_dtd_2);
+ }
+
+ intel_sdvo_set_clock_rate_mult(output, sdvo_priv->save_sdvo_mult);
+
+ I915_WRITE(sdvo_priv->output_device, sdvo_priv->save_SDVOX);
+
+ if (sdvo_priv->save_SDVOX & SDVO_ENABLE)
+ {
+ for (i = 0; i < 2; i++)
+ intel_wait_for_vblank(dev);
+ status = intel_sdvo_get_trained_inputs(output, &input1, &input2);
+ if (status == SDVO_CMD_STATUS_SUCCESS && !input1)
+ DRM_DEBUG("First %s output reported failure to sync\n",
+ SDVO_NAME(sdvo_priv));
+ }
+
+ intel_sdvo_set_active_outputs(output, sdvo_priv->save_active_outputs);
+}
+
+static int intel_sdvo_mode_valid(struct drm_output *output,
+ struct drm_display_mode *mode)
+{
+ struct intel_output *intel_output = output->driver_private;
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+
+ if (mode->flags & V_DBLSCAN)
+ return MODE_NO_DBLESCAN;
+
+ if (sdvo_priv->pixel_clock_min > mode->clock)
+ return MODE_CLOCK_HIGH;
+
+ if (sdvo_priv->pixel_clock_max < mode->clock)
+ return MODE_CLOCK_LOW;
+
+ return MODE_OK;
+}
+
+static bool intel_sdvo_get_capabilities(struct drm_output *output, struct intel_sdvo_caps *caps)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(output, SDVO_CMD_GET_DEVICE_CAPS, NULL, 0);
+ status = intel_sdvo_read_response(output, caps, sizeof(*caps));
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ return true;
+}
+
+
+static void intel_sdvo_dump_cmd(struct drm_output *output, int opcode)
+{
+
+
+}
+
+static void intel_sdvo_dump_device(struct drm_output *output)
+{
+
+}
+
+void intel_sdvo_dump(void)
+{
+
+}
+
+
+static enum drm_output_status intel_sdvo_detect(struct drm_output *output)
+{
+ u8 response[2];
+ u8 status;
+
+ intel_sdvo_write_cmd(output, SDVO_CMD_GET_ATTACHED_DISPLAYS, NULL, 0);
+ status = intel_sdvo_read_response(output, &response, 2);
+
+ DRM_DEBUG("SDVO response %d %d\n", response[0], response[1]);
+ if ((response[0] != 0) || (response[1] != 0))
+ return output_status_connected;
+ else
+ return output_status_disconnected;
+}
+
+static int intel_sdvo_get_modes(struct drm_output *output)
+{
+ struct drm_display_mode *modes;
+
+ /* set the bus switch and get the modes */
+ intel_sdvo_set_control_bus_switch(output, SDVO_CONTROL_BUS_DDC2);
+ intel_ddc_get_modes(output);
+
+ if (list_empty(&output->probed_modes))
+ return 0;
+ return 1;
+#if 0
+ /* Mac mini hack. On this device, I get DDC through the analog, which
+ * load-detects as disconnected. I fail to DDC through the SDVO DDC,
+ * but it does load-detect as connected. So, just steal the DDC bits
+ * from analog when we fail at finding it the right way.
+ */
+ /* TODO */
+ return NULL;
+
+ return NULL;
+#endif
+}
+
+static void intel_sdvo_destroy(struct drm_output *output)
+{
+ struct intel_output *intel_output = output->driver_private;
+
+ if (intel_output->i2c_bus)
+ intel_i2c_destroy(intel_output->i2c_bus);
+
+ if (intel_output) {
+ kfree(intel_output);
+ output->driver_private = NULL;
+ }
+}
+
+static const struct drm_output_funcs intel_sdvo_output_funcs = {
+ .dpms = intel_sdvo_dpms,
+ .save = intel_sdvo_save,
+ .restore = intel_sdvo_restore,
+ .mode_valid = intel_sdvo_mode_valid,
+ .mode_fixup = intel_sdvo_mode_fixup,
+ .prepare = intel_output_prepare,
+ .mode_set = intel_sdvo_mode_set,
+ .commit = intel_output_commit,
+ .detect = intel_sdvo_detect,
+ .get_modes = intel_sdvo_get_modes,
+ .cleanup = intel_sdvo_destroy
+};
+
+void intel_sdvo_init(drm_device_t *dev, int output_device)
+{
+ struct drm_output *output;
+ struct intel_output *intel_output;
+ struct intel_sdvo_priv *sdvo_priv;
+ struct intel_i2c_chan *i2cbus = NULL;
+ u8 ch[0x40];
+ int i;
+ char name[DRM_OUTPUT_LEN];
+ char *name_prefix;
+ char *name_suffix;
+
+
+ output = drm_output_create(dev, &intel_sdvo_output_funcs, NULL);
+ if (!output)
+ return;
+
+ intel_output = kcalloc(sizeof(struct intel_output)+sizeof(struct intel_sdvo_priv), 1, GFP_KERNEL);
+ if (!intel_output) {
+ drm_output_destroy(output);
+ return;
+ }
+
+ sdvo_priv = (struct intel_sdvo_priv *)(intel_output + 1);
+ intel_output->type = INTEL_OUTPUT_SDVO;
+ output->driver_private = intel_output;
+ output->interlace_allowed = 0;
+ output->doublescan_allowed = 0;
+
+ /* setup the DDC bus. */
+ if (output_device == SDVOB)
+ i2cbus = intel_i2c_create(dev, GPIOE, "SDVOCTRL_E for SDVOB");
+ else
+ i2cbus = intel_i2c_create(dev, GPIOE, "SDVOCTRL_E for SDVOC");
+
+ if (i2cbus == NULL) {
+ drm_output_destroy(output);
+ return;
+ }
+
+ sdvo_priv->i2c_bus = i2cbus;
+
+ if (output_device == SDVOB) {
+ name_suffix = "-1";
+ sdvo_priv->i2c_bus->slave_addr = 0x38;
+ } else {
+ name_suffix = "-2";
+ sdvo_priv->i2c_bus->slave_addr = 0x39;
+ }
+
+ sdvo_priv->output_device = output_device;
+ intel_output->i2c_bus = i2cbus;
+ intel_output->dev_priv = sdvo_priv;
+
+
+ /* Read the regs to test if we can talk to the device */
+ for (i = 0; i < 0x40; i++) {
+ if (!intel_sdvo_read_byte(output, i, &ch[i])) {
+ DRM_DEBUG("No SDVO device found on SDVO%c\n",
+ output_device == SDVOB ? 'B' : 'C');
+ drm_output_destroy(output);
+ return;
+ }
+ }
+
+ intel_sdvo_get_capabilities(output, &sdvo_priv->caps);
+
+ memset(&sdvo_priv->active_outputs, 0, sizeof(sdvo_priv->active_outputs));
+
+ /* TODO, CVBS, SVID, YPRPB & SCART outputs.
+ * drm_initial_config probably wants tweaking too to support the
+ * above. But has fixed VGA, TMDS and LVDS checking code. That should
+ * be dealt with.
+ */
+ if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB0)
+ {
+ sdvo_priv->active_outputs = SDVO_OUTPUT_RGB0;
+ output->subpixel_order = SubPixelHorizontalRGB;
+ /* drm_initial_config wants this name, but should be RGB */
+ /* Use this for now.... */
+ name_prefix="VGA";
+ }
+ else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB1)
+ {
+ sdvo_priv->active_outputs = SDVO_OUTPUT_RGB1;
+ output->subpixel_order = SubPixelHorizontalRGB;
+ /* drm_initial_config wants this name, but should be RGB */
+ /* Use this for now.... */
+ name_prefix="VGA";
+ }
+ else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0)
+ {
+ sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS0;
+ output->subpixel_order = SubPixelHorizontalRGB;
+ name_prefix="TMDS";
+ }
+ else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS1)
+ {
+ sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS1;
+ output->subpixel_order = SubPixelHorizontalRGB;
+ name_prefix="TMDS";
+ }
+ else
+ {
+ unsigned char bytes[2];
+
+ memcpy (bytes, &sdvo_priv->caps.output_flags, 2);
+ DRM_DEBUG("%s: No active RGB or TMDS outputs (0x%02x%02x)\n",
+ SDVO_NAME(sdvo_priv),
+ bytes[0], bytes[1]);
+ drm_output_destroy(output);
+ return;
+ }
+ strcpy (name, name_prefix);
+ strcat (name, name_suffix);
+ if (!drm_output_rename(output, name))
+ {
+ drm_output_destroy(output);
+ return;
+ }
+
+
+ /* Set the input timing to the screen. Assume always input 0. */
+ intel_sdvo_set_target_input(output, true, false);
+
+ intel_sdvo_get_input_pixel_clock_range(output,
+ &sdvo_priv->pixel_clock_min,
+ &sdvo_priv->pixel_clock_max);
+
+
+ DRM_DEBUG("%s device VID/DID: %02X:%02X.%02X, "
+ "clock range %dMHz - %dMHz, "
+ "input 1: %c, input 2: %c, "
+ "output 1: %c, output 2: %c\n",
+ SDVO_NAME(sdvo_priv),
+ sdvo_priv->caps.vendor_id, sdvo_priv->caps.device_id,
+ sdvo_priv->caps.device_rev_id,
+ sdvo_priv->pixel_clock_min / 1000,
+ sdvo_priv->pixel_clock_max / 1000,
+ (sdvo_priv->caps.sdvo_inputs_mask & 0x1) ? 'Y' : 'N',
+ (sdvo_priv->caps.sdvo_inputs_mask & 0x2) ? 'Y' : 'N',
+ /* check currently supported outputs */
+ sdvo_priv->caps.output_flags &
+ (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_RGB0) ? 'Y' : 'N',
+ sdvo_priv->caps.output_flags &
+ (SDVO_OUTPUT_TMDS1 | SDVO_OUTPUT_RGB1) ? 'Y' : 'N');
+
+ intel_output->ddc_bus = i2cbus;
+}
diff --git a/linux-core/intel_sdvo_regs.h b/linux-core/intel_sdvo_regs.h
new file mode 100644
index 0000000..a9d1671
--- /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 <[email protected]>
+ */
+
+/**
+ * @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
+

2007-05-17 22:47:51

by Jesse Barnes

[permalink] [raw]
Subject: Re: [PATCH 1/3] allow console unregistration

On Thursday, May 17, 2007 3:32 pm Jesse Barnes wrote:
> Randy just informed me that the patch limits are bigger now, so here
> are the actual patches.
>
> This patch allows for proper console unregistration via the VT layer,
> and updates the FB layer to use it. This makes debugging new console
> drivers much easier, since you can properly clean them up before
> unloading. Antonio already checked it out (and suggested a tweak for
> the fbcon side) so I think it's on its way already via the FB tree.

And before someone complains, here's the diffstat:
drivers/char/vt.c | 21 +++++++++++++++++++--
drivers/video/console/fbcon.c | 17 ++++++++++++++++-
drivers/video/fbmem.c | 2 ++
include/linux/console.h | 1 +
include/linux/fb.h | 2 ++
5 files changed, 40 insertions(+), 3 deletions(-)

2007-05-17 22:48:30

by Jesse Barnes

[permalink] [raw]
Subject: Re: [PATCH 2/3] drm modesetting core

On Thursday, May 17, 2007 3:37 pm Jesse Barnes wrote:
> This patch adds the core of the new DRM based modesetting system. It
> creates several new structures in the DRM, the primary ones being the
> CRTC, which controls all aspects of your device's CRTC(s), output,
> which describes and controls the various outputs on your gfx chip
> (e.g. TMDS, LVDS, VGA, etc.), and mode, which describes graphics
> modes in enough detail for the output and CRTC callbacks to program
> them to hardware.
>
> It also contains the user level IOCTL interfaces for doing graphics
> programming (getting CRTC, output and mode lists, setting up new
> frame buffer objects, etc.).
>
> It relies on the new TTM patch Dave posted recently.

Makefile.kernel | 6
drmP.h | 11
drm_bo.c | 8
drm_bo_move.c | 2
drm_bufs.c | 3
drm_compat.h | 7
drm_crtc.c | 1652 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
drm_crtc.h | 535 ++++++++++++++++++
drm_drv.c | 92 ---
drm_edid.c | 467 +++++++++++++++
drm_edid.h | 176 +++++
drm_fb.c | 201 ++++++
drm_fops.c | 4
drm_modes.c | 558 ++++++++++++++++++
drm_objects.h | 22
drm_os_linux.h | 18
drm_stub.c | 21
drm_vm.c | 5
18 files changed, 3695 insertions(+), 93 deletions(-)

2007-05-17 22:49:00

by Jesse Barnes

[permalink] [raw]
Subject: Re: [PATCH 3/3] Intel support for DRM modesetting

On Thursday, May 17, 2007 3:40 pm Jesse Barnes wrote:
> This patch adds support for DRM modesetting to the Intel DRM driver
> and stubs out a simple FB driver to sit underneath. The code had to
> be refactored a bit, since current DRM drivers tend to be fully
> initialized by userspace via ioctls. This patch makes the driver
> load routine do most of the heavy lifting, since it's necessary in
> order to fully bring up a console driver.
>
> It also relies on the TTM patch Dave posted recently for allocating
> the initial framebuffer used by the FB layer.

i915_drv.c | 2
i915_init.c | 305 +++++++++++++
intel_crt.c | 248 ++++++++++
intel_display.c | 1232 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
intel_drv.h | 79 +++
intel_i2c.c | 187 ++++++++
intel_lvds.c | 500 +++++++++++++++++++++
intel_modes.c | 55 ++
intel_sdvo.c | 1095 +++++++++++++++++++++++++++++++++++++++++++++++
intel_sdvo_regs.h | 328 ++++++++++++++
10 files changed, 4030 insertions(+), 1 deletion(-)

2007-05-17 23:23:26

by Antonino A. Daplas

[permalink] [raw]
Subject: Re: [PATCH 1/3] allow console unregistration

On Thu, 2007-05-17 at 15:32 -0700, Jesse Barnes wrote:
> Randy just informed me that the patch limits are bigger now, so here are the
> actual patches.
>
> This patch allows for proper console unregistration via the VT layer, and
> updates the FB layer to use it. This makes debugging new console drivers
> much easier, since you can properly clean them up before unloading.
> Antonio already checked it out (and suggested a tweak for the fbcon side)
> so I think it's on its way already via the FB tree.

Sorry, I was busy and got sidetracked so I wasn't able to work on this
for 2 weeks. Yes, this should work for now, at least for debugging
purposes. I'll work on refining on fbcon side, hopefully for
2.6.22-2.6.23 time frame.

Tony


2007-05-17 23:41:52

by Luca Tettamanti

[permalink] [raw]
Subject: Re: [PATCH 2/3] drm modesetting core

Il Thu, May 17, 2007 at 03:37:45PM -0700, Jesse Barnes ha scritto:
> This patch adds the core of the new DRM based modesetting system.

A couple of comments on drm_fb since I'm somewhat familiar with fb code:

> new file mode 100644
> index 0000000..0d06792
> --- /dev/null
> +++ b/linux-core/drm_edid.c
> @@ -0,0 +1,467 @@
> +/*
> + * Copyright (c) 2007 Intel Corporation
> + * Jesse Barnes <[email protected]>
> + *
> + * DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid)
> originally from
> + * FB layer.

Hum, why are you duplicating them here? fbmon.c has the
infrastructure for parsing and even fixing known-broken EDIDs.

> + * Copyright (C) 2006 Dennis Munsie <[email protected]>
> + */
> +#include <linux/i2c.h>
> +#include <linux/i2c-algo-bit.h>
> +#include "drmP.h"
> +#include "drm_edid.h"
> +
> +/* Valid EDID header has these bytes */
> +static u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> 0x00 };
> +
> +/**
> + * edid_valid - sanity check EDID data
> + * @edid: EDID data
> + *
> + * Sanity check the EDID block by looking at the header, the version
> number
> + * and the checksum. Return 0 if the EDID doesn't check out, or 1 if
> it's
> + * valid.
> + */
> +static bool edid_valid(struct edid *edid)
> +{
> + int i;
> + u8 csum = 0;
> + u8 *raw_edid = (u8 *)edid;
> +
> + if (memcmp(edid->header, edid_header, sizeof(edid_header)))
> + goto bad;
> + if (edid->version != 1)
> + goto bad;
> + if (edid->revision <= 0 || edid->revision > 3)
> + goto bad;
> +
> + for (i = 0; i < EDID_LENGTH; i++)
> + csum += raw_edid[i];
> + if (csum)
> + goto bad;
> +
> + return 1;
> +
> +bad:
> + return 0;
> +}

This is basically edid_check_header + edid_checksum.

> +
> +/**
> + * 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)
> +{

get_std_timing?

> +/**
> + * drm_mode_detailed - create a new mode from an EDID detailed timing
> section
> + * @timing: EDID detailed timing info
> + * @preferred: is this a preferred mode?
> + *
> + * An EDID detailed timing block contains enough info for us to create
> and
> + * return a new struct drm_display_mode. The @preferred flag will be
> set
> + * if this is the display's preferred timing, and we'll use it to
> indicate
> + * to the other layers that this mode is desired.
> + */
> +struct drm_display_mode *drm_mode_detailed(drm_device_t *dev,
> + struct detailed_timing *timing)
> +{

get_detailed_timing?

If you can't use 'struct fb_videomode' we may refactor code around a common
data structure instead of a copy&paste.

> +static unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter
> *adapter)
[...]
> +static unsigned char *drm_ddc_read(struct i2c_adapter *adapter)
[...]

Copy and paste from fb_dcc.c; furthermore a fix in drm_ddc_read hasn't
been backported to the original code.

> diff --git a/linux-core/drm_fb.c b/linux-core/drm_fb.c
> new file mode 100644
> index 0000000..ef05341
> --- /dev/null
> +++ b/linux-core/drm_fb.c
> @@ -0,0 +1,201 @@
> +/*
> + * 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"
> +struct drmfb_par {
> + struct drm_device *dev;
> + struct drm_framebuffer *fb;
> +};
> +
> +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->fb;
> + if (regno > 17)
> + return 1;
> +
> + 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;
> +}
> +
> +/* 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_device *dev = par->dev;
> +
> + drm_set_desired_modes(dev);
> + 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_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_framebuffer *fb)
> +{
> + struct fb_info *info;
> + struct drmfb_par *par;
> + struct device *device = &dev->pdev->dev;
> + struct fb_var_screeninfo *var_info;
> + unsigned long base, size;
> + int ret;
> +
> + info = framebuffer_alloc(sizeof(struct drmfb_par), device);
> + if (!info){
> + return -EINVAL;
> + }

-ENOMEM? Plus, spurious brackets.

> + if (register_framebuffer(info) < 0)
> + return -EINVAL;

You leak the fb_info structure on error path.

> +
> + printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
> + info->fix.id);
> + return 0;
> +}
> +EXPORT_SYMBOL(drmfb_probe);


Luca
--
Software is like sex; it's better when it's free.
Linus Torvalds

2007-05-18 00:57:15

by Jesse Barnes

[permalink] [raw]
Subject: Re: [PATCH 1/3] allow console unregistration

On Thursday, May 17, 2007, Antonino A. Daplas wrote:
> On Thu, 2007-05-17 at 15:32 -0700, Jesse Barnes wrote:
> > Randy just informed me that the patch limits are bigger now, so here
> > are the actual patches.
> >
> > This patch allows for proper console unregistration via the VT layer,
> > and updates the FB layer to use it. This makes debugging new console
> > drivers much easier, since you can properly clean them up before
> > unloading. Antonio already checked it out (and suggested a tweak for
> > the fbcon side) so I think it's on its way already via the FB tree.
>
> Sorry, I was busy and got sidetracked so I wasn't able to work on this
> for 2 weeks. Yes, this should work for now, at least for debugging
> purposes. I'll work on refining on fbcon side, hopefully for
> 2.6.22-2.6.23 time frame.

No problem, I don't expect the rest of the code to be ready for awhile...
This patch should be pretty close (modulo debugging statements and other
bogons) to what we discussed earlier. I think the main change that's
needed is to only unregister the specific console that was registered,
rather than all of them.

Thanks,
Jesse

2007-05-18 01:05:26

by Jesse Barnes

[permalink] [raw]
Subject: Re: [PATCH 2/3] drm modesetting core

On Thursday, May 17, 2007, Luca Tettamanti wrote:
> Il Thu, May 17, 2007 at 03:37:45PM -0700, Jesse Barnes ha scritto:
> > This patch adds the core of the new DRM based modesetting system.
>
> A couple of comments on drm_fb since I'm somewhat familiar with fb code:
> > new file mode 100644
> > index 0000000..0d06792
> > --- /dev/null
> > +++ b/linux-core/drm_edid.c
> > @@ -0,0 +1,467 @@
> > +/*
> > + * Copyright (c) 2007 Intel Corporation
> > + * Jesse Barnes <[email protected]>
> > + *
> > + * DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid)
> > originally from
> > + * FB layer.
>
> Hum, why are you duplicating them here? fbmon.c has the
> infrastructure for parsing and even fixing known-broken EDIDs.

Yeah, there's more sharing that could be done... though I don't think the
fb layer has the bits to actually grab EDIDs. Also, DRM is shared with
BSD...

> > +static bool edid_valid(struct edid *edid)
> > +{
> > + int i;
> > + u8 csum = 0;
> > + u8 *raw_edid = (u8 *)edid;
> > +
> > + if (memcmp(edid->header, edid_header, sizeof(edid_header)))
> > + goto bad;
> > + if (edid->version != 1)
> > + goto bad;
> > + if (edid->revision <= 0 || edid->revision > 3)
> > + goto bad;
> > +
> > + for (i = 0; i < EDID_LENGTH; i++)
> > + csum += raw_edid[i];
> > + if (csum)
> > + goto bad;
> > +
> > + return 1;
> > +
> > +bad:
> > + return 0;
> > +}
>
> This is basically edid_check_header + edid_checksum.

Yep, pretty trivial stuff.

> get_detailed_timing?
>
> If you can't use 'struct fb_videomode' we may refactor code around a
> common data structure instead of a copy&paste.

I agree that would be better. I'll see what I can do to unify the two.

> > +static unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter
> > *adapter)
>
> [...]
>
> > +static unsigned char *drm_ddc_read(struct i2c_adapter *adapter)
>
> [...]
>
> Copy and paste from fb_dcc.c; furthermore a fix in drm_ddc_read hasn't
> been backported to the original code.

I think the original tries a few times... but it's still buggy. I've got
an old EDID 1.1 monitor whose EDID block is fetched by X but not by this
code (or the original FB code) so I think we still have some timing bugs
to fix.

> > + info = framebuffer_alloc(sizeof(struct drmfb_par), device);
> > + if (!info){
> > + return -EINVAL;
> > + }
>
> -ENOMEM? Plus, spurious brackets.

Fixed, thanks.

> > + if (register_framebuffer(info) < 0)
> > + return -EINVAL;
>
> You leak the fb_info structure on error path.

Oops, I'll fix that too.

At this point though, the drm_fb driver isn't actually used. I recently
added the intel_fb driver (mostly using code from intelfb) so we could
have an accelerated DRM FB driver, hopefully that one's ok.

Thanks for looking at it.

Jesse

2007-05-18 19:33:10

by Luca Tettamanti

[permalink] [raw]
Subject: Re: [PATCH 2/3] drm modesetting core

Il Thu, May 17, 2007 at 06:04:54PM -0700, Jesse Barnes ha scritto:
> On Thursday, May 17, 2007, Luca Tettamanti wrote:
> > Il Thu, May 17, 2007 at 03:37:45PM -0700, Jesse Barnes ha scritto:
> > > This patch adds the core of the new DRM based modesetting system.
> >
> > A couple of comments on drm_fb since I'm somewhat familiar with fb code:
> > > new file mode 100644
> > > index 0000000..0d06792
> > > --- /dev/null
> > > +++ b/linux-core/drm_edid.c
> > > @@ -0,0 +1,467 @@
> > > +/*
> > > + * Copyright (c) 2007 Intel Corporation
> > > + * Jesse Barnes <[email protected]>
> > > + *
> > > + * DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid)
> > > originally from
> > > + * FB layer.
> >
> > Hum, why are you duplicating them here? fbmon.c has the
> > infrastructure for parsing and even fixing known-broken EDIDs.
>
> Yeah, there's more sharing that could be done... though I don't think the
> fb layer has the bits to actually grab EDIDs.

There are the I2C functions (fb_do_probe_ddc_edid, fb_ddc_read - I wrote
them for the radeon driver, but now are available for general use) which
will issue the read command; fbmon.c has the stuff for parsing the EDID;
you usualy build a DB of supported modes which is then used to validate
the mode requested by the user. Of course each driver has to implement
the I2C adapter.

> Also, DRM is shared with BSD...

Your patch already uses 'struct i2c_adapter' in drm_edid.c, is it
portable?

Luca
--
"Vorrei morire ucciso dagli agi. Vorrei che di me si dicesse: ``Com'?
morto?'' ``Gli ? scoppiato il portafogli''" -- Marcello Marchesi

2007-05-18 21:06:17

by Jesse Barnes

[permalink] [raw]
Subject: Re: [PATCH 2/3] drm modesetting core

On Friday, May 18, 2007 12:33 pm Luca Tettamanti wrote:
> > Yeah, there's more sharing that could be done... though I don't
> > think the fb layer has the bits to actually grab EDIDs.
>
> There are the I2C functions (fb_do_probe_ddc_edid, fb_ddc_read - I
> wrote them for the radeon driver, but now are available for general
> use) which will issue the read command; fbmon.c has the stuff for
> parsing the EDID; you usualy build a DB of supported modes which is
> then used to validate the mode requested by the user. Of course each
> driver has to implement the I2C adapter.

I'll take a look at fbmon... I've seen the fb_ddc_read stuff but didn't
see many drivers using it heavily. I think it makes sense to reuse
your code where possible (in fact some earlier versions of the code
made more use of FB stuff but was removed or rewritten for various
reasons).

> > Also, DRM is shared with BSD...
>
> Your patch already uses 'struct i2c_adapter' in drm_edid.c, is it
> portable?

I'm not sure how portable that will be. But regardless, if Linux has
some of this code already, I'd like to reuse it. I'll go head and see
what I can rip out.

In fact, I've received some comments pushing me towards moving the core
code (crtc, mode management) to drivers/video instead of DRM. That
might make sense, especially if we can just use/extend the FB layer's
mode tracking structures.

Thanks,
Jesse

2007-05-20 17:42:48

by Jon Smirl

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On Thu, 17 May 2007 14:23:45 -0700, Jesse Barnes wrote:

> In collaboration with the FB guys, we've been working on enhancing the
> kernel's graphics subsystem in an attempt to bring some sanity to the
> Linux graphics world and avoid the situation we have now where several
> kernel and userspace drivers compete for control of graphics devices.

How is supporting different users logged into each head going to work?
The original model for this was to give each head its own fbdev device.
It is important that each user be able to set their own mode without being
root.

Jon Smirl
[email protected]

2007-05-20 23:10:43

by Jesse Barnes

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On Sunday, May 20, 2007, Jon Smirl wrote:
> On Thu, 17 May 2007 14:23:45 -0700, Jesse Barnes wrote:
> > In collaboration with the FB guys, we've been working on enhancing the
> > kernel's graphics subsystem in an attempt to bring some sanity to the
> > Linux graphics world and avoid the situation we have now where several
> > kernel and userspace drivers compete for control of graphics devices.
>
> How is supporting different users logged into each head going to work?
> The original model for this was to give each head its own fbdev device.
> It is important that each user be able to set their own mode without
> being root.

With the interfaces implemented here, a userspace application can create a
multiseat environment either with a single graphics card with multiple
outputs or multiple cards. It could do this by creating several frame
buffer objects and associating them with whatever CRTCs were available,
and managing input via existing APIs. I don't know of anyone that's done
this yet though...

Jesse

2007-05-21 00:48:10

by [email protected]

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On 5/20/07, Jesse Barnes <[email protected]> wrote:
> With the interfaces implemented here, a userspace application can create a
> multiseat environment either with a single graphics card with multiple
> outputs or multiple cards. It could do this by creating several frame
> buffer objects and associating them with whatever CRTCs were available,
> and managing input via existing APIs. I don't know of anyone that's done
> this yet though...

This design still requires a global server app since the heads share a
single device.
I am always concerned that the root priv code in the X server is a
potential security hole. I would like to move away from a model where
there is a global controlling app. I don't think we need a global
controlling app at all.

By making one device per head it becomes possible to assign ownership
of the device to the specific user and let them do whatever they want
to it. It's then up to the device driver to sort everything out. fbdev
has already been designed with this in mind and I believe the Matrox
driver implements it. This model easily expands from one to N heads.

Merged-fb modes are handled by including them in the allowable modes
list on both heads. If you own both heads you can set a merged-fb mode
and the other device will just return EBUSY.

How are you reconciling the introduction of a new mode setting API
with the 90 existing fbdev drivers? We clearly don't want two
competing APIs in the kernel. What's the plan for converting all of
the existing drivers?

--
Jon Smirl
[email protected]

2007-05-21 01:29:37

by Jeff Garzik

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

Jon Smirl wrote:
> This design still requires a global server app since the heads share a
> single device.
> I am always concerned that the root priv code in the X server is a
> potential security hole. I would like to move away from a model where
> there is a global controlling app. I don't think we need a global
> controlling app at all.


If you have multiple "controlling" apps competing for a single device,
that either implies complexity in each app for sharing control, or
moving even more code into the kernel for 2D and 3D.

I don't think the kernel community is yet interested in moving
everything of consequence into the kernel.

Jeff


2007-05-21 08:28:15

by Dave Airlie

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On 5/21/07, Jon Smirl <[email protected]> wrote:
> On Thu, 17 May 2007 14:23:45 -0700, Jesse Barnes wrote:
>
> > In collaboration with the FB guys, we've been working on enhancing the
> > kernel's graphics subsystem in an attempt to bring some sanity to the
> > Linux graphics world and avoid the situation we have now where several
> > kernel and userspace drivers compete for control of graphics devices.
>
> How is supporting different users logged into each head going to work?
> The original model for this was to give each head its own fbdev device.
> It is important that each user be able to set their own mode without being
> root.

TThe problem with that is the concept of heads is flawed... there is
in reality no such
thing, you have crtcs and outputs, no heads. So any attempt to enforce
the head concept involves putting policy into the kernel, as if I have
3 outputs but 2 crtcs how do I decide the mappings without the admin
telling the kernel,

As for moving other card drivers they will move as people see fit to
move them there probably isn't much gain in moving single crtc/output
cards..

Dave.

>
> Jon Smirl
> [email protected]
>
> -
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
>
x.org/lkml/
>
x.org/lkml/
>

2007-05-21 09:19:00

by Helge Hafting

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

Dave Airlie wrote:
> On 5/21/07, Jon Smirl <[email protected]> wrote:
>> On Thu, 17 May 2007 14:23:45 -0700, Jesse Barnes wrote:
>>
>> > In collaboration with the FB guys, we've been working on enhancing the
>> > kernel's graphics subsystem in an attempt to bring some sanity to the
>> > Linux graphics world and avoid the situation we have now where several
>> > kernel and userspace drivers compete for control of graphics devices.
>>
>> How is supporting different users logged into each head going to work?
>> The original model for this was to give each head its own fbdev device.
>> It is important that each user be able to set their own mode without
>> being
>> root.
>
> TThe problem with that is the concept of heads is flawed... there is
> in reality no such
> thing, you have crtcs and outputs, no heads. So any attempt to enforce
> the head concept involves putting policy into the kernel, as if I have
> 3 outputs but 2 crtcs how do I decide the mappings without the admin
> telling the kernel,
>
Solution:
One device per crtc. You can then have two users, running consoles
or xservers on their crtcs, without having to involve root.

The crtc->output mapping must still be done by root of course.

This solution allow the useful case where the computer boots, the boot
scripts
set up a crtc->output mapping. Then users log in through the
various consoles (using getty or xdm or similiar) using their grahphical
devices in whatever way they want. A true multiseat setup.


And if one user needs to use all the screens for multi-display work?
Let root change the mappings, possibly through some sudo setup.

Helge Hafting

2007-05-21 09:28:01

by Dave Airlie

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On 5/21/07, Helge Hafting <[email protected]> wrote:
> Dave Airlie wrote:
> > On 5/21/07, Jon Smirl <[email protected]> wrote:
> >> On Thu, 17 May 2007 14:23:45 -0700, Jesse Barnes wrote:
> >>
> >> > In collaboration with the FB guys, we've been working on enhancing the
> >> > kernel's graphics subsystem in an attempt to bring some sanity to the
> >> > Linux graphics world and avoid the situation we have now where several
> >> > kernel and userspace drivers compete for control of graphics devices.
> >>
> >> How is supporting different users logged into each head going to work?
> >> The original model for this was to give each head its own fbdev device.
> >> It is important that each user be able to set their own mode without
> >> being
> >> root.
> >
> > TThe problem with that is the concept of heads is flawed... there is
> > in reality no such
> > thing, you have crtcs and outputs, no heads. So any attempt to enforce
> > the head concept involves putting policy into the kernel, as if I have
> > 3 outputs but 2 crtcs how do I decide the mappings without the admin
> > telling the kernel,
> >
> Solution:
> One device per crtc. You can then have two users, running consoles
> or xservers on their crtcs, without having to involve root.

Thats pretty much what the code does, but you still are putting a
certain amount of policy in the kernel...
>
> The crtc->output mapping must still be done by root of course.
>
> This solution allow the useful case where the computer boots, the boot
> scripts
> set up a crtc->output mapping. Then users log in through the
> various consoles (using getty or xdm or similiar) using their grahphical
> devices in whatever way they want. A true multiseat setup.
>
>
> And if one user needs to use all the screens for multi-display work?
> Let root change the mappings, possibly through some sudo setup.
>

Multiseat isn't what i would want as a default on any machine, so the
default setup should be to clone the single user onto as many screens
as possible, as this is what users expect.. the system startup scripts
can then reconfigure it, to suit the admins needs..

Dave.
>

2007-05-21 09:54:48

by Helge Hafting

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

Dave Airlie wrote:
> On 5/21/07, Helge Hafting <[email protected]> wrote:
>> Dave Airlie wrote:
>> > On 5/21/07, Jon Smirl <[email protected]> wrote:
>> >> On Thu, 17 May 2007 14:23:45 -0700, Jesse Barnes wrote:
>> >>
>> >> > In collaboration with the FB guys, we've been working on
>> enhancing the
>> >> > kernel's graphics subsystem in an attempt to bring some sanity
>> to the
>> >> > Linux graphics world and avoid the situation we have now where
>> several
>> >> > kernel and userspace drivers compete for control of graphics
>> devices.
>> >>
>> >> How is supporting different users logged into each head going to
>> work?
>> >> The original model for this was to give each head its own fbdev
>> device.
>> >> It is important that each user be able to set their own mode without
>> >> being
>> >> root.
>> >
>> > TThe problem with that is the concept of heads is flawed... there is
>> > in reality no such
>> > thing, you have crtcs and outputs, no heads. So any attempt to enforce
>> > the head concept involves putting policy into the kernel, as if I have
>> > 3 outputs but 2 crtcs how do I decide the mappings without the admin
>> > telling the kernel,
>> >
>> Solution:
>> One device per crtc. You can then have two users, running consoles
>> or xservers on their crtcs, without having to involve root.
>
> Thats pretty much what the code does, but you still are putting a
> certain amount of policy in the kernel...
What policy would that be?
The mapping is set by root from userspace, not by the kernel.
The same for ownership to crtc devices. The kernel may have to
provide some default so "init=/bin/sh" will work, that's all.

>>
>> The crtc->output mapping must still be done by root of course.
>>
>> This solution allow the useful case where the computer boots, the boot
>> scripts
>> set up a crtc->output mapping. Then users log in through the
>> various consoles (using getty or xdm or similiar) using their grahphical
>> devices in whatever way they want. A true multiseat setup.
>>
>>
>> And if one user needs to use all the screens for multi-display work?
>> Let root change the mappings, possibly through some sudo setup.
>>
>
> Multiseat isn't what i would want as a default on any machine, so the
Sure. Not multiseat by default, as the kernel can't know which output
goes with which keyboard. All I want it a system that allows
multiseat for those that care to set it up.
> default setup should be to clone the single user onto as many screens
> as possible, as this is what users expect.. the system startup scripts
> can then reconfigure it, to suit the admins needs..
Sure. Well, when using multiple screens as a single user, then I
don't want cloning. I want a multi-screen desktop. The laptop
user with a video projector is about the one case I know
where cloning is wanted.

Unfortunately the kernel can't always know which outputs are in
use, so I see how cloning may have to be the default. :-/

Helge Hafting


>
> Dave.
>>
> -
> To unsubscribe from this list: send the line "unsubscribe
> linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/

2007-05-21 15:09:47

by Jesse Barnes

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On Sunday, May 20, 2007, Jon Smirl wrote:
> On 5/20/07, Jesse Barnes <[email protected]> wrote:
> > With the interfaces implemented here, a userspace application can
> > create a multiseat environment either with a single graphics card with
> > multiple outputs or multiple cards. It could do this by creating
> > several frame buffer objects and associating them with whatever CRTCs
> > were available, and managing input via existing APIs. I don't know of
> > anyone that's done this yet though...
>
> This design still requires a global server app since the heads share a
> single device.
> I am always concerned that the root priv code in the X server is a
> potential security hole. I would like to move away from a model where
> there is a global controlling app. I don't think we need a global
> controlling app at all.

Even without a graphics server of some sort arbitrating access (and it
doesn't have to be a big as the current X server btw), you'd still need
your apps to take a card specific lock and/or coordinate so that they
don't clobber one another's rendering results. This could be done in the
kernel, but for many devices the complexity added is likely to be pretty
high.

OTOH, if you're just talking about mapping sections of VRAM to user level
processes to manage as indpendent heads, that's fairly trivial to do as
you say, but you'd almost certainly want acceleration for any sort of real
world application, which is where things would get tricky.

> How are you reconciling the introduction of a new mode setting API
> with the 90 existing fbdev drivers? We clearly don't want two
> competing APIs in the kernel. What's the plan for converting all of
> the existing drivers?

My initial plan was to only convert drivers to this new API if they had
hardware that justified a DRM driver (i.e. high performance devices with
command ring buffers, 3D, etc.), and leave the other FB drivers alone,
since the FB layer is quite suited to simple devices.

The other option of course is to port the existing drivers over to the new
modesetting interfaces, though I suspect in many cases that may not be
particularly useful. I'm open to suggestions.

Thanks,
Jesse

2007-05-21 15:34:17

by [email protected]

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On 5/20/07, Jeff Garzik <[email protected]> wrote:
> Jon Smirl wrote:
> > This design still requires a global server app since the heads share a
> > single device.
> > I am always concerned that the root priv code in the X server is a
> > potential security hole. I would like to move away from a model where
> > there is a global controlling app. I don't think we need a global
> > controlling app at all.
>
>
> If you have multiple "controlling" apps competing for a single device,
> that either implies complexity in each app for sharing control, or
> moving even more code into the kernel for 2D and 3D.
>
> I don't think the kernel community is yet interested in moving
> everything of consequence into the kernel.

Before dismissing this you might want to spend some time designing it.

When I said head I should have been more specific and said CRTC. CRTCs
are pretty much independent from each other and can be assigned one
per device. I believe this has already been done in the Matrox fbdev
driver. I have also prototyped this on the Radeon hardware and it
works. It is how fbdev was designed to work. As for 3D support, making
3D work on a per user basis is not very different from the way direct
rendering works today.

Supporting multiple users, one on each CRTC, has long been a feature
request from people building low end educational systems, kiosks,
Internet cafes, etc. If the entire graphics subsystem is going to get
rewritten this feature should be added.

After designing this I think you will find that the extra code that
needs to go into the kernel is minimal. You have to split the video
memory into multiple pools and keep people from accessing queues that
they don't own. Code like this is supposed to be in the kernel.

Having a multi-megabyte root priv graphics process always running is a
scary security exposure. This privileged process is not necessary and
through proper design it can be eliminated. After designing this I
think you'll find that less than 1% of the code ends up in the driver
and the rest can run unprivileged in user space. Put the root priv
code into the device driver where it will get the scrutiny it needs.

>
> Jeff
>
>
>


--
Jon Smirl
[email protected]

2007-05-21 15:58:22

by Jesse Barnes

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On Monday, May 21, 2007, Helge Hafting wrote:
> >> > TThe problem with that is the concept of heads is flawed... there
> >> > is in reality no such
> >> > thing, you have crtcs and outputs, no heads. So any attempt to
> >> > enforce the head concept involves putting policy into the kernel,
> >> > as if I have 3 outputs but 2 crtcs how do I decide the mappings
> >> > without the admin telling the kernel,
> >>
> >> Solution:
> >> One device per crtc. You can then have two users, running consoles
> >> or xservers on their crtcs, without having to involve root.
> >
> > Thats pretty much what the code does, but you still are putting a
> > certain amount of policy in the kernel...
>
> What policy would that be?
> The mapping is set by root from userspace, not by the kernel.
> The same for ownership to crtc devices. The kernel may have to
> provide some default so "init=/bin/sh" will work, that's all.

The policy of mapping outputs to CRTCs. Like Dave said, you may have
several outputs but only one or two CRTCs, with limitations on how they
can be routed. So doing "one device per CRTC" isn't quite enough, you
also have to choose an initial output setup. But like you say this could
be changed at boot time.

> Sure. Not multiseat by default, as the kernel can't know which output
> goes with which keyboard. All I want it a system that allows
> multiseat for those that care to set it up.

Sure, and like I mentioned to Jon in another mail, a decent multiseat setup
is possible with these interfaces, albeit with a userlevel graphics daemon
to arbitrate what the CRTC->output mappings are and to coordinate access
to the hw if needed.

So really, I think the interfaces posted here will do what you want. Maybe
you can take a closer look and make sure?

Thanks,
Jesse

2007-05-21 16:01:40

by [email protected]

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On 5/21/07, Jesse Barnes <[email protected]> wrote:
> On Sunday, May 20, 2007, Jon Smirl wrote:
> > On 5/20/07, Jesse Barnes <[email protected]> wrote:
> > > With the interfaces implemented here, a userspace application can
> > > create a multiseat environment either with a single graphics card with
> > > multiple outputs or multiple cards. It could do this by creating
> > > several frame buffer objects and associating them with whatever CRTCs
> > > were available, and managing input via existing APIs. I don't know of
> > > anyone that's done this yet though...
> >
> > This design still requires a global server app since the heads share a
> > single device.
> > I am always concerned that the root priv code in the X server is a
> > potential security hole. I would like to move away from a model where
> > there is a global controlling app. I don't think we need a global
> > controlling app at all.
>
> Even without a graphics server of some sort arbitrating access (and it
> doesn't have to be a big as the current X server btw), you'd still need
> your apps to take a card specific lock and/or coordinate so that they
> don't clobber one another's rendering results. This could be done in the
> kernel, but for many devices the complexity added is likely to be pretty
> high.

We have already have the card specific locks, it's how Direct Rendering works.

Besides, card locks are a much better design that the free for all
that happens on a VT switch.

> OTOH, if you're just talking about mapping sections of VRAM to user level
> processes to manage as indpendent heads, that's fairly trivial to do as
> you say, but you'd almost certainly want acceleration for any sort of real
> world application, which is where things would get tricky.
>
> > How are you reconciling the introduction of a new mode setting API
> > with the 90 existing fbdev drivers? We clearly don't want two
> > competing APIs in the kernel. What's the plan for converting all of
> > the existing drivers?
>
> My initial plan was to only convert drivers to this new API if they had
> hardware that justified a DRM driver (i.e. high performance devices with
> command ring buffers, 3D, etc.), and leave the other FB drivers alone,
> since the FB layer is quite suited to simple devices.
>
> The other option of course is to port the existing drivers over to the new
> modesetting interfaces, though I suspect in many cases that may not be
> particularly useful. I'm open to suggestions.

There is more to fbdev than mode setting. It is also how non-x86
platforms achieve their boot display. How will boot display be handled
with the your design? What about console display on non-x86 platforms?
Loading both fbdev and the new code puts us right back into the
multiple driver fight we have today.

If the kernel graphics system is going to get rewritten the rewrite
needs to address all of the existing problems.

--
Jon Smirl
[email protected]

2007-05-21 16:07:59

by [email protected]

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On 5/21/07, Jesse Barnes <[email protected]> wrote:
> On Monday, May 21, 2007, Helge Hafting wrote:
> > >> > TThe problem with that is the concept of heads is flawed... there
> > >> > is in reality no such
> > >> > thing, you have crtcs and outputs, no heads. So any attempt to
> > >> > enforce the head concept involves putting policy into the kernel,
> > >> > as if I have 3 outputs but 2 crtcs how do I decide the mappings
> > >> > without the admin telling the kernel,
> > >>
> > >> Solution:
> > >> One device per crtc. You can then have two users, running consoles
> > >> or xservers on their crtcs, without having to involve root.
> > >
> > > Thats pretty much what the code does, but you still are putting a
> > > certain amount of policy in the kernel...
> >
> > What policy would that be?
> > The mapping is set by root from userspace, not by the kernel.
> > The same for ownership to crtc devices. The kernel may have to
> > provide some default so "init=/bin/sh" will work, that's all.
>
> The policy of mapping outputs to CRTCs. Like Dave said, you may have
> several outputs but only one or two CRTCs, with limitations on how they
> can be routed. So doing "one device per CRTC" isn't quite enough, you
> also have to choose an initial output setup. But like you say this could
> be changed at boot time.
>
> > Sure. Not multiseat by default, as the kernel can't know which output
> > goes with which keyboard. All I want it a system that allows
> > multiseat for those that care to set it up.
>
> Sure, and like I mentioned to Jon in another mail, a decent multiseat setup
> is possible with these interfaces, albeit with a userlevel graphics daemon
> to arbitrate what the CRTC->output mappings are and to coordinate access
> to the hw if needed.
>
> So really, I think the interfaces posted here will do what you want. Maybe
> you can take a closer look and make sure?

What about modifying the existing fbdev API? You could start with one
fbdev device per CRTC and then add a new IOCTL to control the output
device. I haven't seen anything yet that justifies abandoning the
existing fbdev API.

Note that you could add fbdev API support to the radeon DRM driver.
Preserving the fbdev API doesn't mean that you have to preserve the
old driver code.

--
Jon Smirl
[email protected]

2007-05-21 16:14:37

by Jesse Barnes

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On Monday, May 21, 2007, Jon Smirl wrote:
> On 5/21/07, Jesse Barnes <[email protected]> wrote:
> > On Sunday, May 20, 2007, Jon Smirl wrote:
> > > On 5/20/07, Jesse Barnes <[email protected]> wrote:
> > > > With the interfaces implemented here, a userspace application can
> > > > create a multiseat environment either with a single graphics card
> > > > with multiple outputs or multiple cards. It could do this by
> > > > creating several frame buffer objects and associating them with
> > > > whatever CRTCs were available, and managing input via existing
> > > > APIs. I don't know of anyone that's done this yet though...
> > >
> > > This design still requires a global server app since the heads share
> > > a single device.
> > > I am always concerned that the root priv code in the X server is a
> > > potential security hole. I would like to move away from a model
> > > where there is a global controlling app. I don't think we need a
> > > global controlling app at all.
> >
> > Even without a graphics server of some sort arbitrating access (and it
> > doesn't have to be a big as the current X server btw), you'd still
> > need your apps to take a card specific lock and/or coordinate so that
> > they don't clobber one another's rendering results. This could be
> > done in the kernel, but for many devices the complexity added is
> > likely to be pretty high.
>
> We have already have the card specific locks, it's how Direct Rendering
> works.
>
> Besides, card locks are a much better design that the free for all
> that happens on a VT switch.

Right, I'm just saying there's much more to it than just having the
hardware locks (which of course we already have).

> There is more to fbdev than mode setting. It is also how non-x86
> platforms achieve their boot display. How will boot display be handled
> with the your design? What about console display on non-x86 platforms?
> Loading both fbdev and the new code puts us right back into the
> multiple driver fight we have today.

Maybe you should take a look at the patches. :) The code I posted actually
creates an fb device as a slave of the DRM device, and uses that for the
boot console. Once you've booted, you can use the new interfaces to do
whatever you want with the graphics device... though it doesn't
create /dev/fb* devices per-CRTC like you seem to want, it's really easy
to do the equivalent with a small userspace program (though I haven't
actually tested sharing of buffer objects, it should work).

> If the kernel graphics system is going to get rewritten the rewrite
> needs to address all of the existing problems.

Sure, I have no problem with that, but there are usually lots of ways to
solve a given problem.

Jesse

2007-05-21 16:16:33

by Dave Airlie

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

.
>
> There is more to fbdev than mode setting. It is also how non-x86
> platforms achieve their boot display. How will boot display be handled
> with the your design? What about console display on non-x86 platforms?
> Loading both fbdev and the new code puts us right back into the
> multiple driver fight we have today.
>
> If the kernel graphics system is going to get rewritten the rewrite
> needs to address all of the existing problems.
>

We already have fbcon working perfectly on top of this layer... so you
might want to actually look at the developed code..

Dave.

2007-05-21 16:17:16

by Arjan van de Ven

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On Mon, 2007-05-21 at 11:34 -0400, Jon Smirl wrote:
> Supporting multiple users, one on each CRTC, has long been a feature
> request from people building low end educational systems, kiosks,
> Internet cafes, etc. If the entire graphics subsystem is going to get
> rewritten this feature should be added.


are you going to write the code?

this topic has been on the table for YEARS and no code appeared. The
patch here is a great step forward for the common case, and seems to
allow more complex setups either already or with some minor future
modifications.... so how about we move forward with this code that
exists today, instead of doing more vaporware while holding the rest of
the world back...


2007-05-21 16:27:30

by Dave Airlie

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

>
> What about modifying the existing fbdev API? You could start with one
> fbdev device per CRTC and then add a new IOCTL to control the output
> device. I haven't seen anything yet that justifies abandoning the
> existing fbdev API.

Then you can't aribtrate properly output hooking is a root level
thing, you cannot allow the user in multiseat to just pick his own
outputs, if you claim to want a truly flexible interfaces, also the
crtc->output mappings aren't always simple, there are limitations on
most hw about which crtc can map to which output and when you can
clone etc.. putting policy for this stuff in-kernel would heavily
restrict what the user can do...

At the moment, you can with a small userspace app add two
framebuffers, link them to crtcs and link those to outputs, then users
can use /dev/fb/* to do what they need, we haven't linked up mode
setting so that fbset works yet I don't think, but if people really
wanted that we could probably do so... so far we just have enough
fbdev to support fbcon...

Dave.

2007-05-21 16:35:38

by Jesse Barnes

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On Monday, May 21, 2007, Jesse Barnes wrote:
> > There is more to fbdev than mode setting. It is also how non-x86
> > platforms achieve their boot display. How will boot display be handled
> > with the your design? What about console display on non-x86 platforms?
> > Loading both fbdev and the new code puts us right back into the
> > multiple driver fight we have today.
>
> Maybe you should take a look at the patches. :) The code I posted
> actually creates an fb device as a slave of the DRM device, and uses
> that for the boot console. Once you've booted, you can use the new
> interfaces to do whatever you want with the graphics device... though it
> doesn't create /dev/fb* devices per-CRTC like you seem to want, it's
> really easy to do the equivalent with a small userspace program (though
> I haven't actually tested sharing of buffer objects, it should work).

Actually, scratch that last bit. I forgot about a recent change alanh
committed that did just that: per-CRTC FB devices. So please take a
close look (modesetting-101 of mesa/drm git at freedesktop.org) and let me
know if you see any gaping holes.

Thanks,
Jesse

2007-05-21 17:05:31

by [email protected]

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On 5/21/07, Jesse Barnes <[email protected]> wrote:
> On Monday, May 21, 2007, Jesse Barnes wrote:
> > > There is more to fbdev than mode setting. It is also how non-x86
> > > platforms achieve their boot display. How will boot display be handled
> > > with the your design? What about console display on non-x86 platforms?
> > > Loading both fbdev and the new code puts us right back into the
> > > multiple driver fight we have today.
> >
> > Maybe you should take a look at the patches. :) The code I posted
> > actually creates an fb device as a slave of the DRM device, and uses
> > that for the boot console. Once you've booted, you can use the new
> > interfaces to do whatever you want with the graphics device... though it
> > doesn't create /dev/fb* devices per-CRTC like you seem to want, it's
> > really easy to do the equivalent with a small userspace program (though
> > I haven't actually tested sharing of buffer objects, it should work).
>
> Actually, scratch that last bit. I forgot about a recent change alanh
> committed that did just that: per-CRTC FB devices. So please take a
> close look (modesetting-101 of mesa/drm git at freedesktop.org) and let me
> know if you see any gaping holes.

Here are some of the goals that I believe a rewrite of the graphics
system should address:

1) Be upwards compatible with the existing fbdev drivers. This lets us
avoid rewriting the 90 existing drivers. New drivers shouldn't break
any old apps.

2) Address the long outstanding issue of multi-seat at the console
level. My solution to this is the one device per CRTC model.

3) Eliminate the need for a root priv controlling process. Get rid of
the potential for a security hole.

4) OOPS should always display even if in a graphics mode

5) Support Secure Attention Key (SAK).

6) Eliminate the existing VT swap driver free for all. I would compile
out the VT layer and replace it with a compatible API that enforces
some sanity.

7) Support Unicode on the console

8) Allow multiple user space graphics systems to run. These user space
systems should not touch the hardware, instead they ask the kernel
driver to manipulate the hardware on their behalf. Of course the
kernel driver is only the minimum code needed to arbitrate control of
the resources - it doesn't do things like implement drawing
algorithms.

9) Booting on non-VGA hardware still needs to work.

10) Support things like cloning and output device selection.

Of course a driver doesn't need to support all of this in its first
release. What's important is that the new architecture support all of
these features so that we don't end up rearchitecting it yet again.

Other people may add more things to this list. Let's get the design
right this time around and address all of the known problems.

--
Jon Smirl
[email protected]

2007-05-21 17:14:25

by Xavier Bestel

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On Mon, 2007-05-21 at 17:27 +0100, Dave Airlie wrote:
> > What about modifying the existing fbdev API? You could start with
> one
> > fbdev device per CRTC and then add a new IOCTL to control the output
> > device. I haven't seen anything yet that justifies abandoning the
> > existing fbdev API.
>
> Then you can't aribtrate properly output hooking is a root level
> thing, you cannot allow the user in multiseat to just pick his own
> outputs, if you claim to want a truly flexible interfaces, also the
> crtc->output mappings aren't always simple, there are limitations on
> most hw about which crtc can map to which output and when you can
> clone etc.. putting policy for this stuff in-kernel would heavily
> restrict what the user can do...

Policy for this kind of thing should just go in HAL's ConsoleKit:
http://lists.freedesktop.org/archives/hal/2007-January/007111.html

Xav


2007-05-21 17:14:39

by Dave Airlie

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

>
> 1) Be upwards compatible with the existing fbdev drivers. This lets us
> avoid rewriting the 90 existing drivers. New drivers shouldn't break
> any old apps.
>

done.

> 2) Address the long outstanding issue of multi-seat at the console
> level. My solution to this is the one device per CRTC model.

done.

>
> 3) Eliminate the need for a root priv controlling process. Get rid of
> the potential for a security hole.

Stupid idea, we need something to control policy, this isn't going in
the kernel, it can be a lot smaller than X and auditable.. sticking
the DRI protocol in the kernel is just pointless..

> 4) OOPS should always display even if in a graphics mode

possible to do.

>
> 5) Support Secure Attention Key (SAK).

possible to do.

> 6) Eliminate the existing VT swap driver free for all. I would compile
> out the VT layer and replace it with a compatible API that enforces
> some sanity.

I'm hoping to look into this but it is a parallel problem to what this
code does, the VT switch API sucks rocks, so providing something
compatible is going to suck rocks..

> 7) Support Unicode on the console

This just needs a userspace console again a parallel problem that
really isn't much to do with the problem set this work is trying to
solve... it should enable it...

>
> 8) Allow multiple user space graphics systems to run. These user space
> systems should not touch the hardware, instead they ask the kernel
> driver to manipulate the hardware on their behalf. Of course the
> kernel driver is only the minimum code needed to arbitrate control of
> the resources - it doesn't do things like implement drawing
> algorithms.

No problems.

>
> 9) Booting on non-VGA hardware still needs to work.
>
> 10) Support things like cloning and output device selection.
>
No problems.

Dave.

2007-05-21 17:29:42

by [email protected]

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On 5/21/07, Dave Airlie <[email protected]> wrote:
> > 3) Eliminate the need for a root priv controlling process. Get rid of
> > the potential for a security hole.
>
> Stupid idea, we need something to control policy, this isn't going in
> the kernel, it can be a lot smaller than X and auditable.. sticking
> the DRI protocol in the kernel is just pointless..

Try to be more flexible with your thinking. It is ok to have a
transient, privileged command line app that does something like assign
an output to be under the control of a specific CRTC. I have no
problem with some of the IOCTLs requiring root priv.

What is not ok is to require a permanently running root priv process.
If the code is going to always be running make it as small as possible
and put it in the device driver.

--
Jon Smirl
[email protected]

2007-05-21 17:33:17

by Jesse Barnes

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On Monday, May 21, 2007, Jon Smirl wrote:
> On 5/21/07, Jesse Barnes <[email protected]> wrote:
> > On Monday, May 21, 2007, Jesse Barnes wrote:
> > > > There is more to fbdev than mode setting. It is also how non-x86
> > > > platforms achieve their boot display. How will boot display be
> > > > handled with the your design? What about console display on
> > > > non-x86 platforms? Loading both fbdev and the new code puts us
> > > > right back into the multiple driver fight we have today.
> > >
> > > Maybe you should take a look at the patches. :) The code I posted
> > > actually creates an fb device as a slave of the DRM device, and uses
> > > that for the boot console. Once you've booted, you can use the new
> > > interfaces to do whatever you want with the graphics device...
> > > though it doesn't create /dev/fb* devices per-CRTC like you seem to
> > > want, it's really easy to do the equivalent with a small userspace
> > > program (though I haven't actually tested sharing of buffer objects,
> > > it should work).
> >
> > Actually, scratch that last bit. I forgot about a recent change alanh
> > committed that did just that: per-CRTC FB devices. So please take a
> > close look (modesetting-101 of mesa/drm git at freedesktop.org) and
> > let me know if you see any gaping holes.
>
> Here are some of the goals that I believe a rewrite of the graphics
> system should address:

I think we're talking past each other. I addressed many of the points
below in my initial post...

> 1) Be upwards compatible with the existing fbdev drivers. This lets us
> avoid rewriting the 90 existing drivers. New drivers shouldn't break
> any old apps.

That's there, see the patches.

> 2) Address the long outstanding issue of multi-seat at the console
> level. My solution to this is the one device per CRTC model.

Also covered.

> 3) Eliminate the need for a root priv controlling process. Get rid of
> the potential for a security hole.

This is an implementation detail. I can understand not wanting to run a
huge X server for this purpose, but I've already said that it can be done
with far less code for environments with different needs.

> 4) OOPS should always display even if in a graphics mode

This is definitely a goal, and something I spelled out in my initial post.
I think we'll need a new KD_* type to cover this case (i.e. not KD_TEXT
but not quite KD_GRAPHICS either). This should be fairly straightforward
once I have some good user level apps running on top of these interfaces.

> 5) Support Secure Attention Key (SAK).

This is more of a console issue, not really a graphics design issue I
think.

> 6) Eliminate the existing VT swap driver free for all. I would compile
> out the VT layer and replace it with a compatible API that enforces
> some sanity.

Again, called out in my initial post. Yes, the kernel should save/restore
state by itself and not rely on some userspace application. However, we
don't want to break the VT switch API either, so we'll still likely need a
heavyweight save/restore state mechanism somewhere.

> 7) Support Unicode on the console

A userspace console using these interfaces could do this, it's not really a
kernel graphics problem.

> 8) Allow multiple user space graphics systems to run. These user space
> systems should not touch the hardware, instead they ask the kernel
> driver to manipulate the hardware on their behalf. Of course the
> kernel driver is only the minimum code needed to arbitrate control of
> the resources - it doesn't do things like implement drawing
> algorithms.

This is already done.

> 9) Booting on non-VGA hardware still needs to work.

I don't see what this has to do with the overall design, it's a driver
problem (well aside from polluting the generic interfaces with VGA
knowledge, which I'm not planning on doing).

> 10) Support things like cloning and output device selection.

Already done.

Again, *please* take a look at the description in my initial post and the
patches themselves (or the modesetting-101 branch in the drm tree). I
think that'll lead to a more concrete discussion about what's
missing/needed, etc.

Thanks,
Jesse

2007-05-21 17:42:46

by [email protected]

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On 5/21/07, Dave Airlie <[email protected]> wrote:
> This just needs a userspace console again a parallel problem that
> really isn't much to do with the problem set this work is trying to
> solve... it should enable it...

When I went through the design process for all this I came to the same
conclusion about needing a user space console process.

User space console does impact on all of this because it implies that
the current console should be be defeatured down until it becomes only
a system recovery console and not a console for everyday use.

For example, one part of the defeaturing would be to remove the
drawing acceleration code in the existing fbdev console drivers and to
rework it to support accelerated drawing from the user space console
implementation. You want the system recovery console mode to be as
simple as possible so that it is always guaranteed to work. User space
console is also what leads to the idea of compiling VT out of the
kernel.

Once you decide that a user space console is needed then the per CRTC
device node becomes more obvious since different people can be logged
onto the different consoles.

All of the points in the list are interrelated and the architecture
needs to address everything as a unified whole.

--
Jon Smirl
[email protected]

2007-05-21 17:48:10

by Dave Airlie

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On 5/21/07, Jon Smirl <[email protected]> wrote:
> On 5/21/07, Dave Airlie <[email protected]> wrote:
> > This just needs a userspace console again a parallel problem that
> > really isn't much to do with the problem set this work is trying to
> > solve... it should enable it...
>
> When I went through the design process for all this I came to the same
> conclusion about needing a user space console process.
>
> User space console does impact on all of this because it implies that
> the current console should be be defeatured down until it becomes only
> a system recovery console and not a console for everyday use.
>
> For example, one part of the defeaturing would be to remove the
> drawing acceleration code in the existing fbdev console drivers and to
> rework it to support accelerated drawing from the user space console
> implementation. You want the system recovery console mode to be as
> simple as possible so that it is always guaranteed to work. User space
> console is also what leads to the idea of compiling VT out of the
> kernel.
>
> Once you decide that a user space console is needed then the per CRTC
> device node becomes more obvious since different people can be logged
> onto the different consoles.
>
> All of the points in the list are interrelated and the architecture
> needs to address everything as a unified whole.

you were doing fine up until the last point, they are interrelated but
not architecturally dependent, we can do a lot of work on this stuff
without that, we don't need to deprecate anything, just provide new
interfaces that new drivers can use to implement this stuff, then
people can pull over the old drivers at their own pace, like we can
just stick a flag in the console that says we can handle things like
dumping oopsen in KD_GRAPHICS etc.. drivers that can do it will do it,
drivers that can't won't.

You don't compile VT out you just disable it when you have a driver
that supports the model,

You also require a heavy weight state switch for suspend/resume in any
case so this could be utilised for other things, e.g. text mode on
many cards can't be done while acceleration is operational, but
certainly don't want to bar text mode for people who want it..

Dave.

2007-05-21 18:04:54

by [email protected]

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On 5/21/07, Dave Airlie <[email protected]> wrote:
> On 5/21/07, Jon Smirl <[email protected]> wrote:
> > On 5/21/07, Dave Airlie <[email protected]> wrote:
> > > This just needs a userspace console again a parallel problem that
> > > really isn't much to do with the problem set this work is trying to
> > > solve... it should enable it...
> >
> > When I went through the design process for all this I came to the same
> > conclusion about needing a user space console process.
> >
> > User space console does impact on all of this because it implies that
> > the current console should be be defeatured down until it becomes only
> > a system recovery console and not a console for everyday use.
> >
> > For example, one part of the defeaturing would be to remove the
> > drawing acceleration code in the existing fbdev console drivers and to
> > rework it to support accelerated drawing from the user space console
> > implementation. You want the system recovery console mode to be as
> > simple as possible so that it is always guaranteed to work. User space
> > console is also what leads to the idea of compiling VT out of the
> > kernel.
> >
> > Once you decide that a user space console is needed then the per CRTC
> > device node becomes more obvious since different people can be logged
> > onto the different consoles.
> >
> > All of the points in the list are interrelated and the architecture
> > needs to address everything as a unified whole.
>
> you were doing fine up until the last point, they are interrelated but
> not architecturally dependent, we can do a lot of work on this stuff
> without that, we don't need to deprecate anything, just provide new
> interfaces that new drivers can use to implement this stuff, then
> people can pull over the old drivers at their own pace, like we can
> just stick a flag in the console that says we can handle things like
> dumping oopsen in KD_GRAPHICS etc.. drivers that can do it will do it,
> drivers that can't won't.

You are describing a transition plan without knowing what the final
design is going to look like. We really need to hash out the final
design so that the right path is taken to get there.

For example I didn't have per CRTC device nodes or user space consoles
in my original design, but after talking to some of the people that
really wanted the multi-seat feature it led me down the user space
console path and to the per CRTC device node solution. I also got beat
up at OLS by people wanting full Unicode support on the console.

>
> You don't compile VT out you just disable it when you have a driver
> that supports the model,
>
> You also require a heavy weight state switch for suspend/resume in any
> case so this could be utilised for other things, e.g. text mode on
> many cards can't be done while acceleration is operational, but
> certainly don't want to bar text mode for people who want it..
>
> Dave.
>


--
Jon Smirl
[email protected]

2007-05-21 18:44:27

by Dave Airlie

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

>
> You are describing a transition plan without knowing what the final
> design is going to look like. We really need to hash out the final
> design so that the right path is taken to get there.
>
> For example I didn't have per CRTC device nodes or user space consoles
> in my original design, but after talking to some of the people that
> really wanted the multi-seat feature it led me down the user space
> console path and to the per CRTC device node solution. I also got beat
> up at OLS by people wanting full Unicode support on the console.
>

No we are defining steps towards improving the drivers on Linux, the
first step is the requirement to fix suspend/resume, and allow
modesetting on multiple crtc/output combinations, the other goals are
not directly within the scope of this work, you can take steps to do
get where we want, but we don't need to move all drivers at once to
get there... we also can't just merge something like that to the
kernel...

Your old ideas were mostly limited by the fact that you didn't get the
crtc/output distinction and persisted with the idea of heads which put
policy in the kernel, this was a major failing you never discovered,
and I didn't probably look enough at the time, since then Keith
Packard has done a lot of work on randr 1.2 to show the path to what
we actually wanted.

Dave.

2007-05-21 19:10:30

by [email protected]

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On 5/21/07, Dave Airlie <[email protected]> wrote:
> >
> > You are describing a transition plan without knowing what the final
> > design is going to look like. We really need to hash out the final
> > design so that the right path is taken to get there.
> >
> > For example I didn't have per CRTC device nodes or user space consoles
> > in my original design, but after talking to some of the people that
> > really wanted the multi-seat feature it led me down the user space
> > console path and to the per CRTC device node solution. I also got beat
> > up at OLS by people wanting full Unicode support on the console.
> >
>
> No we are defining steps towards improving the drivers on Linux, the
> first step is the requirement to fix suspend/resume, and allow
> modesetting on multiple crtc/output combinations, the other goals are
> not directly within the scope of this work, you can take steps to do
> get where we want, but we don't need to move all drivers at once to
> get there... we also can't just merge something like that to the
> kernel...
>
> Your old ideas were mostly limited by the fact that you didn't get the
> crtc/output distinction and persisted with the idea of heads which put
> policy in the kernel, this was a major failing you never discovered,
> and I didn't probably look enough at the time, since then Keith
> Packard has done a lot of work on randr 1.2 to show the path to what
> we actually wanted.

I thought Luc Verhaegen figured that out not Keith.

Call it whatever you want and I have wasted far too much time arguing
with you and Keith and I can never get agreement on anything. BTW,
should I search the LKML archives and find the messages where you call
me stupid and block my patches merging fbdev/DRM? That's effectively
what you are doing right now.

There is a significant group of Linux users who want to be able to
login separate users to each screen/head/crtc/output device. These
people are concentrated in the third world and don't show up at OLS to
argue their case.

There is another group that wants Unicode consoles. The people I
talked to were from India and Japan.

I am not a member of either group. So go ahead and ignore me, I'd just
like to see these two groups get features implemented that have been
ignored for a long time.


>
> Dave.
>


--
Jon Smirl
[email protected]

2007-05-21 19:20:56

by Dave Airlie

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On 5/21/07, Jon Smirl <[email protected]> wrote:
> On 5/21/07, Dave Airlie <[email protected]> wrote:
> > >
> > > You are describing a transition plan without knowing what the final
> > > design is going to look like. We really need to hash out the final
> > > design so that the right path is taken to get there.
> > >
> > > For example I didn't have per CRTC device nodes or user space consoles
> > > in my original design, but after talking to some of the people that
> > > really wanted the multi-seat feature it led me down the user space
> > > console path and to the per CRTC device node solution. I also got beat
> > > up at OLS by people wanting full Unicode support on the console.
> > >
> >
> > No we are defining steps towards improving the drivers on Linux, the
> > first step is the requirement to fix suspend/resume, and allow
> > modesetting on multiple crtc/output combinations, the other goals are
> > not directly within the scope of this work, you can take steps to do
> > get where we want, but we don't need to move all drivers at once to
> > get there... we also can't just merge something like that to the
> > kernel...
> >
> > Your old ideas were mostly limited by the fact that you didn't get the
> > crtc/output distinction and persisted with the idea of heads which put
> > policy in the kernel, this was a major failing you never discovered,
> > and I didn't probably look enough at the time, since then Keith
> > Packard has done a lot of work on randr 1.2 to show the path to what
> > we actually wanted.
>
> I thought Luc Verhaegen figured that out not Keith.
>

Luc figured it out then, Keith made it work for things that aren't
unichrome and spent the time getting the interfaces into the X
server..

> Call it whatever you want and I have wasted far too much time arguing
> with you and Keith and I can never get agreement on anything. BTW,
> should I search the LKML archives and find the messages where you call
> me stupid and block my patches merging fbdev/DRM? That's effectively
> what you are doing right now.
>

Yup please do, we are doing something seriously different to what you
were trying to do at the time, as we have the experience of the randr
work which demonstrated a path forward rather than trying to strive
for the end...

> There is a significant group of Linux users who want to be able to
> login separate users to each screen/head/crtc/output device. These
> people are concentrated in the third world and don't show up at OLS to
> argue their case.
>
> There is another group that wants Unicode consoles. The people I
> talked to were from India and Japan.
>
> I am not a member of either group. So go ahead and ignore me, I'd just
> like to see these two groups get features implemented that have been
> ignored for a long time.

These people are free to implement these features, we are doing
nothing to block any of these things from happening, but we are not
going to write a userspace console until such time as having a
userspace console becomes useful, whereas we are enabling someone else
to write one on top of the drm now....

We are not going to write a multi-head setup for someone but we are
enabling the setting up of one using the graphics drivers that support
it, we aren't going to enable every driver at once but allow others to
move things over as they like,

The reason this stuff is mainly ending up in the drm is separating the
modesetting from the memory management is definitely not a good plan
and drm has the memory management code now...

We also have VGA arbitration to worry about but again this is
"orthogonal" to the problem space we are currently trying to fix which
is just that we need to suspend/resume GPUs properly and set modes on
multiple output/crtc combos from the kernel.

Dave.

2007-05-21 23:19:18

by Jeff Garzik

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

Jon Smirl wrote:
> 2) Address the long outstanding issue of multi-seat at the console
> level. My solution to this is the one device per CRTC model.

This is very very low priority. Pretty much nobody besides you is
clamoring for it.


> 3) Eliminate the need for a root priv controlling process. Get rid of
> the potential for a security hole.

Agreed.


> 4) OOPS should always display even if in a graphics mode

Agreed, and this was in the list that Jesse(?) posted.


> 8) Allow multiple user space graphics systems to run. These user space

Another very very low priority item.

There are a lot more important things to work on. Linux is about what
people need -right now-, not what you think Linux might need in the
future; not what you think might be nice to have.

Jeff


2007-05-21 23:21:36

by Jeff Garzik

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

Dave Airlie wrote:
>> 3) Eliminate the need for a root priv controlling process. Get rid of
>> the potential for a security hole.
>
> Stupid idea, we need something to control policy, this isn't going in
> the kernel, it can be a lot smaller than X and auditable.. sticking
> the DRI protocol in the kernel is just pointless..

It is a quite sensible idea.

The userspace X server SHOULD be running under a non-root user, with
appropriate fine-grained privs granted to it.

"I need root to do graphics" is a myopic, antiquated view of the world.

Jeff


2007-05-21 23:24:57

by Jeff Garzik

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

Jon Smirl wrote:
> There is a significant group of Linux users who want to be able to
> login separate users to each screen/head/crtc/output device. These
> people are concentrated in the third world and don't show up at OLS to
> argue their case.
>
> There is another group that wants Unicode consoles. The people I
> talked to were from India and Japan.


If these "significant groups" do not bubble up to the surface, nothing
we can do about that.

Jeff


2007-05-22 00:08:16

by [email protected]

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On 5/21/07, Jeff Garzik <[email protected]> wrote:
> Jon Smirl wrote:
> > There is a significant group of Linux users who want to be able to
> > login separate users to each screen/head/crtc/output device. These
> > people are concentrated in the third world and don't show up at OLS to
> > argue their case.
> >
> > There is another group that wants Unicode consoles. The people I
> > talked to were from India and Japan.
>
> If these "significant groups" do not bubble up to the surface, nothing
> we can do about that.

You need to take into account that these features have been asked
about for years and we didn't respond. People interested in this have
turned to other solutions.

James Simmons maintained a multi-seat version of the 2.4 kernel for
years that had many happy users. I don't know the details about why it
never got merged. I believe two companies built special multi-head
video hardware for it (Appian and Matrox? it has been a while).

People needing Unicode console support can't speak English. How can
they complain on LKML? Linux is an OS for the world, we really should
make Unicode console work.

--
Jon Smirl
[email protected]

2007-05-22 00:11:26

by Benjamin Herrenschmidt

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem


> In collaboration with the FB guys, we've been working on enhancing
> the
> kernel's graphics subsystem in an attempt to bring some sanity to the
> Linux graphics world and avoid the situation we have now where
> several
> kernel and userspace drivers compete for control of graphics devices.

.../...

A little note about initial mode setting at boot...

I do stongly beleive that the decision of what mode to choose should not
be made in the kernel. At boot the kernel should either leave the HW in
whatever state the FW set it (and text mode is fine) or setup some sane
default (ie 640x480 has the most chances of working) if that's not an
option, maybe some very minimum EDID parsing in case you have a fixed
frequency weirdo panel, but that's it (unless it's a mac an OF tells you
what to use :-)

The kernel would provide userland with connector infos (presence load
detect, EDID, ...) and userland gets to decide what to do.

Some reasons to keep that policy completely out of the kernel even at
boot time are:

- User may want to configure his default gfx setup and have it up early

- EDID do lie, monitors routinely ship with windows .inf files
containing "updated" infos apple has that too in OS X (EDID overrides
afaik) etc.... and if we're going to do such a database of known
monitors, it should definitely not be in the kernel. Besides, we want to
add more infos that EDIDs don't provide most of the time to it like
subpixel ordering etc...

- It sounds better that way :-) (yeah, that's the best reason !)

So while I agree that the register frobbing, memory management, etc...
should be indeed moved to the kernel as you guys have been doing lately,
the policy of deciding what mode to set should totally stick to
userland.

IMHO, the best would be a lib (or daemon or both) for monitor detection
& mode setting that is separate from X :-) That could handle storing the
admin's default setup (including weird monitor info if any) and
restoring it at boot time, etc...

Cheers,
Ben.


2007-05-22 00:15:26

by Benjamin Herrenschmidt

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On Mon, 2007-05-21 at 18:14 +0100, Dave Airlie wrote:
>
> > 6) Eliminate the existing VT swap driver free for all. I would
> compile
> > out the VT layer and replace it with a compatible API that enforces
> > some sanity.
>
> I'm hoping to look into this but it is a parallel problem to what this
> code does, the VT switch API sucks rocks, so providing something
> compatible is going to suck rocks..

Yeah, please, don't even go near that until everything else is done &
merged or you'll never have anything finished :-) VT is a can of worms
that will take some time to sort out and has nothing to do with what we
are talking about right now anyway :-)

Ben.


2007-05-22 00:20:22

by Benjamin Herrenschmidt

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On Mon, 2007-05-21 at 13:42 -0400, Jon Smirl wrote:
>
> When I went through the design process for all this I came to the same
> conclusion about needing a user space console process.
>
> User space console does impact on all of this because it implies that
> the current console should be be defeatured down until it becomes only
> a system recovery console and not a console for everyday use.

I do agree (heh, for once) with that in the sense that in the long run,
we should strip the kernel console down to the bare minimum to boot,
display oopses, etc... and have all the fancy stuff, unicode, VT, and
more in a userspace console process.

However, I'm a little bit worried that we'll end up with 10 competing
incompatible and inconsistent userspace console projects and -that- will
be horrible.

But it's something separate from what Dave and Jesse are trying to
address. Let's first gets the fundation right and -then- we can do all
sort of crazy things. Or maybe you can start working on a user console
project in parallel using the new APIs that Jesse and Dave are
providing ? :-)

> For example, one part of the defeaturing would be to remove the
> drawing acceleration code in the existing fbdev console drivers and to
> rework it to support accelerated drawing from the user space console
> implementation. You want the system recovery console mode to be as
> simple as possible so that it is always guaranteed to work. User space
> console is also what leads to the idea of compiling VT out of the
> kernel.

I do agree on that in the long run, but again, let's look into this
-after- we have solved the more immediate issues. We can probably kill
most of fbdev, fbcon and current VT once we have a solid userland based
replacement that isn't completely bloated (maybe with a "slim" version
that does only VGA and non-utf8 for server type apps).

> Once you decide that a user space console is needed then the per CRTC
> device node becomes more obvious since different people can be logged
> onto the different consoles.

That's irrelevant. Implementation detail.

> All of the points in the list are interrelated and the architecture
> needs to address everything as a unified whole.

And that's bullshit :-)

Ben.


2007-05-22 00:26:52

by [email protected]

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On 5/21/07, Jeff Garzik <[email protected]> wrote:
> Jon Smirl wrote:
> > 2) Address the long outstanding issue of multi-seat at the console
> > level. My solution to this is the one device per CRTC model.
>
> This is very very low priority. Pretty much nobody besides you is
> clamoring for it.
>
>
> > 3) Eliminate the need for a root priv controlling process. Get rid of
> > the potential for a security hole.
>
> Agreed.
>
>
> > 4) OOPS should always display even if in a graphics mode
>
> Agreed, and this was in the list that Jesse(?) posted.
>
>
> > 8) Allow multiple user space graphics systems to run. These user space
>
> Another very very low priority item.
>
> There are a lot more important things to work on. Linux is about what
> people need -right now-, not what you think Linux might need in the
> future; not what you think might be nice to have.

I am not asking that these features be implemented today. I am asking
that enough planning go into the architecture today to make sure that
these features can be built in the future without tearing up the
graphics system for a third time.

This is the essence of my complaint about this patch. The patch
introduces a new low level graphics API to the kernel. Once we put an
API in it is basically impossible to get it back out. I am not
convinced that enough planning has gone into this API yet.

I'm also not convinced that there is a transition plan in place to
ensure that all drivers get updated to this new API. The last thing we
want is to maintain two parallel sets of video drivers forever into
the future. V4L2 did something similar to this and orphaned a lot of
drivers that the distributions were forced into updating later.

Mode setting is intimately intertwined with the console. VT swapping
adds another messy layer which can and should be eliminated in a
redesign. Multi-seat and unicode add more complexity. All of this
needs to be designed as a unified system. Satisfying the needs of the
X server is the easiest piece of the puzzle.

--
Jon Smirl
[email protected]

2007-05-22 00:31:19

by Alan

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

> > the kernel, it can be a lot smaller than X and auditable.. sticking
> > the DRI protocol in the kernel is just pointless..
>
> It is a quite sensible idea.
>
> The userspace X server SHOULD be running under a non-root user, with
> appropriate fine-grained privs granted to it.
>
> "I need root to do graphics" is a myopic, antiquated view of the world.

X server: priviledges below everything, pageable
kernel: priviledges as high as conceivable, non-pageable

So why do you want it in kernel.... security is not the sensible answer
here.

2007-05-22 00:33:22

by Jeff Garzik

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

Alan Cox wrote:
>>> the kernel, it can be a lot smaller than X and auditable.. sticking
>>> the DRI protocol in the kernel is just pointless..
>> It is a quite sensible idea.
>>
>> The userspace X server SHOULD be running under a non-root user, with
>> appropriate fine-grained privs granted to it.
>>
>> "I need root to do graphics" is a myopic, antiquated view of the world.
>
> X server: priviledges below everything, pageable
> kernel: priviledges as high as conceivable, non-pageable
>
> So why do you want it in kernel.... security is not the sensible answer
> here.

Replying/quoting mixup. I was responding to the root-privs userspace
aspect, not the "put it in the kernel" aspect.

I do not want it in the kernel (should have snipped that last quoted line).

Jeff



2007-05-22 00:45:25

by [email protected]

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On 5/21/07, Alan Cox <[email protected]> wrote:
> > > the kernel, it can be a lot smaller than X and auditable.. sticking
> > > the DRI protocol in the kernel is just pointless..
> >
> > It is a quite sensible idea.
> >
> > The userspace X server SHOULD be running under a non-root user, with
> > appropriate fine-grained privs granted to it.
> >
> > "I need root to do graphics" is a myopic, antiquated view of the world.
>
> X server: priviledges below everything, pageable
> kernel: priviledges as high as conceivable, non-pageable
>
> So why do you want it in kernel.... security is not the sensible answer
> here.

Have you inspected the multi-megabyte X server for security holes to
the same level the kernel has been inspected?

The only part that needs to be in the kernel driver is the code
controlling locking and code that plays with the hardware. Moving it
into the driver ensures that only the minimal amount of root priv code
possible is going to end up in the system. If someone tries to move
too much into the kernel I'm sure you'll let them know that it's a bad
idea.

The problem right now is that code that needs root priv is all
intertwined with code that doesn't need it and it all ends up getting
run as root.

BTW, when I prototyped this a couple of years ago by merging Radeon
DRM/fbdev I only needed to add about 10K more code to the device
driver. Most of that was associated with getting the VBIOS to run in
x86 mode when the driver was first loaded. That code can be marked
_init. We're not talking about a lot of code needing to go into the
kernel.

--
Jon Smirl
[email protected]

2007-05-22 00:51:47

by Keith Packard

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On Tue, 2007-05-22 at 10:09 +1000, Benjamin Herrenschmidt wrote:

> I do stongly beleive that the decision of what mode to choose should not
> be made in the kernel.

That's the plan; the kernel just provides mechanism. The architecture
used in the X server splits precisely at this point with the mechanism
in the driver and the configuration and policy up in the X server
proper. Quite a bit of that code could be broken out into a shared
library for fbdev-based apps and the X server to share, but that's down
the road a bit after the kernel APIs look a lot more solid.

With the goal of getting to a single-mode-set boot to avoid screen
flashing before login, the key here is to make any early user-mode
graphics apps share the same kernel graphics infrastructure as the X
server to identify the common cases where the startup and X modes are
the same and avoid resetting the configuration.

--
[email protected]


Attachments:
signature.asc (189.00 B)
This is a digitally signed message part

2007-05-22 00:56:35

by [email protected]

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

> > So why do you want it in kernel.... security is not the sensible answer
> > here.

I'm not proposing the KGI solution where every device driver presents
the same API. That model does require a lot of code in the kernel.

The existing DRM model where each driver provides it's own API is a
good one. The user space DRI driver then takes this API and turns it
into a standard one. Applying the DRM style model to fbdev may allow
parts of fbdev to be moved out to user space.

What I don't want is a permanent root priv process hanging around in
the system. It simply isn't needed and I have prototyped a system that
runs without root so I know it can be done. With minor mods DRI can
run without the need for root, with more major mods the X server can
run without the need for root. Most of the mods to the X server are to
remove things like PCI bus probing, mode setting and VBIOS support.

--
Jon Smirl
[email protected]

2007-05-22 01:57:18

by Jesse Barnes

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On Monday, May 21, 2007, Jon Smirl wrote:
> I am not asking that these features be implemented today. I am asking
> that enough planning go into the architecture today to make sure that
> these features can be built in the future without tearing up the
> graphics system for a third time.
>
> This is the essence of my complaint about this patch. The patch
> introduces a new low level graphics API to the kernel. Once we put an
> API in it is basically impossible to get it back out. I am not
> convinced that enough planning has gone into this API yet.

Jon, that's why I'm posting this stuff in the first place! :) Again, if
you have specific problems with the proposed interfaces (problems that
would preclude your wishlist from being fully implementable), please let
me know (preferably with specifics).

Thanks,
Jesse

2007-05-22 02:50:12

by Benjamin Herrenschmidt

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On Mon, 2007-05-21 at 17:51 -0700, Keith Packard wrote:
>
> That's the plan; the kernel just provides mechanism. The architecture
> used in the X server splits precisely at this point with the mechanism
> in the driver and the configuration and policy up in the X server
> proper. Quite a bit of that code could be broken out into a shared
> library for fbdev-based apps and the X server to share, but that's
> down the road a bit after the kernel APIs look a lot more solid.

Ok, good plan then.

> With the goal of getting to a single-mode-set boot to avoid screen
> flashing before login, the key here is to make any early user-mode
> graphics apps share the same kernel graphics infrastructure as the X
> server to identify the common cases where the startup and X modes are
> the same and avoid resetting the configuration.

Ok. Fair enough.

Cheers,
Ben.


2007-05-22 02:57:07

by l l

[permalink] [raw]

2007-05-22 08:13:09

by Dave Airlie

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

> It is a quite sensible idea.
>
> The userspace X server SHOULD be running under a non-root user, with
> appropriate fine-grained privs granted to it.
>
> "I need root to do graphics" is a myopic, antiquated view of the world.

Did I say the X server? There are policy decisions that are root only
also authorisation of processes to render etc..

I'm not sure we can punt all that in-kernel.

Dave.

> Jeff
>
>
>

2007-05-22 08:17:20

by Jeff Garzik

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

Dave Airlie wrote:
>> It is a quite sensible idea.
>>
>> The userspace X server SHOULD be running under a non-root user, with
>> appropriate fine-grained privs granted to it.
>>
>> "I need root to do graphics" is a myopic, antiquated view of the world.
>
> Did I say the X server? There are policy decisions that are root only
> also authorisation of processes to render etc..

Root only today, maybe, but this thread is talking about future
directions. Don't lock your design into a coarse-grained security model.


> I'm not sure we can punt all that in-kernel.

See my response to Alan.

Jeff



2007-05-22 08:22:13

by Dave Airlie

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

> What I don't want is a permanent root priv process hanging around in
> the system. It simply isn't needed and I have prototyped a system that
> runs without root so I know it can be done. With minor mods DRI can
> run without the need for root, with more major mods the X server can
> run without the need for root. Most of the mods to the X server are to
> remove things like PCI bus probing, mode setting and VBIOS support.
>

But you always lacked a transition plan and never showed a decent API if memory
serves, your methods disregarded back compat usually.
also there is no need to port all drivers there never has been we need
to just add config options and work with distros to upgrade to the
newer bits..

As Jesse said this is an RFC we are not merging it in the morning
however all your comments are hand waving so far we need technical
problems with this API.

Dave.

2007-05-22 08:27:59

by Dave Airlie

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

> > Did I say the X server? There are policy decisions that are root only
> > also authorisation of processes to render etc..
>
> Root only today, maybe, but this thread is talking about future
> directions. Don't lock your design into a coarse-grained security model.
>

We can add a new capability bit but there are certain operations that
need privs especially if multiple users are involved. binding outputs
to crtcs being one.

Again I can see little reason that this wouldn't be possible going
forward. But i'm seeing policy decisions we currently make in the X
server needing to be made somewhere.

Dave.

>
> > I'm not sure we can punt all that in-kernel.
>
> See my response to Alan.
>
> Jeff
>
>
>
>

2007-05-22 14:28:06

by [email protected]

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On 5/21/07, Jesse Barnes <[email protected]> wrote:
> Jon, that's why I'm posting this stuff in the first place! :) Again, if
> you have specific problems with the proposed interfaces (problems that
> would preclude your wishlist from being fully implementable), please let
> me know (preferably with specifics).

A simple place to start is OOPS display while in graphics mode. If we
going to tear up the kernel graphics system this is something that
needs to be fixed.

I don't think it is safe for the OOPS code to attempt a mode change to
text mode when the OOPS happens. The OOPS could have happened in a 3D
driver and left the GPU messed up. The safest thing to do is to
display the OOPS using the mode that is already set.

This implies that the kernel driver needs to track the dimensions and
location of the framebuffer and whether it is in text/graphics mode
(this hasn't been possible before because X never tells the kernel
what mode it is setting). You also need to bring in the bitmap copy
code and fonts over from fbdev. When the OOPS happens you use this
info to paint the OOPS onto the screen. The code from fbdev will let
you display text in graphics mode entirely in kernel context. The
driver should also attempt to stop the GPU to try and make sure it
doesn't erase the OOPS display.

Another simple thing that needs to be built is a mechanism to run the
VBIOS in x86 mode when the driver is first loaded. This can be
achieved by using call_usermode helper to trigger an external app. You
also need to get the x86 emulator working so that this will work on
non-x86 platforms (benh has already done this). I've aput the hooks
into place to give you access to the VBIOS from sysfs. This app is a
prime candidate for klibc. This app is strongly coupled to the problem
of VGA arbitration.

Could we try and work out a solution to these two problems? If we can
solve these they will provide a foundation for fixing the harder ones.

--
Jon Smirl
[email protected]

2007-05-22 14:35:22

by Dave Airlie

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On 5/22/07, Jon Smirl <[email protected]> wrote:
> On 5/21/07, Jesse Barnes <[email protected]> wrote:
> > Jon, that's why I'm posting this stuff in the first place! :) Again, if
> > you have specific problems with the proposed interfaces (problems that
> > would preclude your wishlist from being fully implementable), please let
> > me know (preferably with specifics).
>
> A simple place to start is OOPS display while in graphics mode. If we
> going to tear up the kernel graphics system this is something that
> needs to be fixed.
>
> I don't think it is safe for the OOPS code to attempt a mode change to
> text mode when the OOPS happens. The OOPS could have happened in a 3D
> driver and left the GPU messed up. The safest thing to do is to
> display the OOPS using the mode that is already set.
>
> This implies that the kernel driver needs to track the dimensions and
> location of the framebuffer and whether it is in text/graphics mode
> (this hasn't been possible before because X never tells the kernel
> what mode it is setting). You also need to bring in the bitmap copy
> code and fonts over from fbdev. When the OOPS happens you use this
> info to paint the OOPS onto the screen. The code from fbdev will let
> you display text in graphics mode entirely in kernel context. The
> driver should also attempt to stop the GPU to try and make sure it
> doesn't erase the OOPS display.

What does this have to do with Jesse's patches? this is a totally
orthogonal issue..

We will fix this once we have the basic modesetting code working,

> Another simple thing that needs to be built is a mechanism to run the
> VBIOS in x86 mode when the driver is first loaded. This can be
> achieved by using call_usermode helper to trigger an external app. You
> also need to get the x86 emulator working so that this will work on
> non-x86 platforms (benh has already done this). I've aput the hooks
> into place to give you access to the VBIOS from sysfs. This app is a
> prime candidate for klibc. This app is strongly coupled to the problem
> of VGA arbitration.
>

Again orthogonal problem, VGA arbitration isn't going to matter for
this stuff, and we can have initramfs sw do vbetool post on
non-initialised cards at startup even now (it may not always work as
some cards may not have BIOSes in the right place s and we need the
VGA arb to work to do it properly...)

These are nothing to do with the work we are doing, and I think a big
part of your problem with working on this previously is you have
linked together a group of orthogonal problems into one big problem,
when really there are 5-10 problems all of which can be solved
separately...

Dave.

2007-05-22 14:50:27

by Alan

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

> you display text in graphics mode entirely in kernel context. The
> driver should also attempt to stop the GPU to try and make sure it
> doesn't erase the OOPS display.

If we know how yes. Probably the X server needs to provide us with a
simple list of operations to perform or failing that write 128K of null
to the display then print the characters (the 128K should ensure any
running FIFO for commands is stuffed and we get back to the framebuffer)

> Another simple thing that needs to be built is a mechanism to run the
> VBIOS in x86 mode when the driver is first loaded. This can be

We don't want to touch the video bios for most cards, its not as good as
the X mode switcher code, nor for that matter does it work in a lot of
cases as they use IRQ and DMA functions sometimes.

Alan

2007-05-22 15:14:05

by [email protected]

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On 5/22/07, Dave Airlie <[email protected]> wrote:
> On 5/22/07, Jon Smirl <[email protected]> wrote:
> > On 5/21/07, Jesse Barnes <[email protected]> wrote:
> > > Jon, that's why I'm posting this stuff in the first place! :) Again, if
> > > you have specific problems with the proposed interfaces (problems that
> > > would preclude your wishlist from being fully implementable), please let
> > > me know (preferably with specifics).
> >
> > A simple place to start is OOPS display while in graphics mode. If we
> > going to tear up the kernel graphics system this is something that
> > needs to be fixed.
> >
> > I don't think it is safe for the OOPS code to attempt a mode change to
> > text mode when the OOPS happens. The OOPS could have happened in a 3D
> > driver and left the GPU messed up. The safest thing to do is to
> > display the OOPS using the mode that is already set.
> >
> > This implies that the kernel driver needs to track the dimensions and
> > location of the framebuffer and whether it is in text/graphics mode
> > (this hasn't been possible before because X never tells the kernel
> > what mode it is setting). You also need to bring in the bitmap copy
> > code and fonts over from fbdev. When the OOPS happens you use this
> > info to paint the OOPS onto the screen. The code from fbdev will let
> > you display text in graphics mode entirely in kernel context. The
> > driver should also attempt to stop the GPU to try and make sure it
> > doesn't erase the OOPS display.
>
> What does this have to do with Jesse's patches? this is a totally
> orthogonal issue..
>
> We will fix this once we have the basic modesetting code working,

These are not isolated problems. Linux needs a properly designed
graphics subsystem. One way to achieve that is to design it all on
paper first so that we can try and locate the interactions between
modules. For example the current mode setting design is definitely
broken for multi-seat support, that's because you didn't take that
feature into account when writing the code.

Putting a small module into the kernel first with a random API, then
try and build the next module is not a good development path. It is
better to design all of the modules on paper and then work backwards
to the API the first module needs. Even better would be to get the
whole subsystem working before including it in the kernel. Once these
exposed APIs go it, it is impossible to get them out. We need to try
and make sure that they are correct to begin with.

You need to take into account that you are proposing the replacement
of an existing subsystem, not the initial inclusion of a virgin
system. Putting your code in as is does make the X server happy, but
it is not solving all of the known problems with the existing graphics
subsystem. If you just want to make to X server happy it would be
better to extend the existing fbdev API and not try and replace the
subsystem.

--
Jon Smirl
[email protected]

2007-05-22 15:16:57

by [email protected]

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On 5/22/07, Alan Cox <[email protected]> wrote:
> > you display text in graphics mode entirely in kernel context. The
> > driver should also attempt to stop the GPU to try and make sure it
> > doesn't erase the OOPS display.
>
> If we know how yes. Probably the X server needs to provide us with a
> simple list of operations to perform or failing that write 128K of null
> to the display then print the characters (the 128K should ensure any
> running FIFO for commands is stuffed and we get back to the framebuffer)
>
> > Another simple thing that needs to be built is a mechanism to run the
> > VBIOS in x86 mode when the driver is first loaded. This can be
>
> We don't want to touch the video bios for most cards, its not as good as
> the X mode switcher code, nor for that matter does it work in a lot of
> cases as they use IRQ and DMA functions sometimes.

The only reason the VBIOS is run is to initially reset the card. Once
we get the card reset it isn't used any more. For almost all cards
this is the only way to reset them, we have insufficient information
to write this ourselves.


>
> Alan
>


--
Jon Smirl
[email protected]

2007-05-22 15:46:14

by Jesse Barnes

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On Monday, May 21, 2007, Benjamin Herrenschmidt wrote:
> > In collaboration with the FB guys, we've been working on enhancing
> > the
> > kernel's graphics subsystem in an attempt to bring some sanity to the
> > Linux graphics world and avoid the situation we have now where
> > several
> > kernel and userspace drivers compete for control of graphics devices.
>
> .../...
>
> A little note about initial mode setting at boot...
>
> I do stongly beleive that the decision of what mode to choose should not
> be made in the kernel. At boot the kernel should either leave the HW in
> whatever state the FW set it (and text mode is fine) or setup some sane
> default (ie 640x480 has the most chances of working) if that's not an
> option, maybe some very minimum EDID parsing in case you have a fixed
> frequency weirdo panel, but that's it (unless it's a mac an OF tells you
> what to use :-)
>
> The kernel would provide userland with connector infos (presence load
> detect, EDID, ...) and userland gets to decide what to do.

The current code does its best to figure out what modes are available and
tries to pick a good one for each display. It sounds like you're mainly
concerned with the actual mode picking, not the mode and output detection
and enumeration? If so, that's a pretty easy change to make. But if
you're also worried about the kernel building mode lists, then we'll have
bigger changes to make...

> Some reasons to keep that policy completely out of the kernel even at
> boot time are:
>
> - User may want to configure his default gfx setup and have it up early
>
> - EDID do lie, monitors routinely ship with windows .inf files
> containing "updated" infos apple has that too in OS X (EDID overrides
> afaik) etc.... and if we're going to do such a database of known
> monitors, it should definitely not be in the kernel. Besides, we want to
> add more infos that EDIDs don't provide most of the time to it like
> subpixel ordering etc...
>
> - It sounds better that way :-) (yeah, that's the best reason !)
>
> So while I agree that the register frobbing, memory management, etc...
> should be indeed moved to the kernel as you guys have been doing lately,
> the policy of deciding what mode to set should totally stick to
> userland.
>
> IMHO, the best would be a lib (or daemon or both) for monitor detection
> & mode setting that is separate from X :-) That could handle storing the
> admin's default setup (including weird monitor info if any) and
> restoring it at boot time, etc...

I'm not really sure how much of a problem broken EDIDs will be. The X
server only has a few quirks for broken EDIDs now, nothing major afaict,
and apparently the FB layer already has some code for handling EDID
quirks, so I don't think that'll be our biggest problem. So far, it looks
like handling laptop panels is a bit trickier (at least for Intel
chips)...

Jesse

2007-05-22 15:47:23

by Jesse Barnes

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On Tuesday, May 22, 2007, Jon Smirl wrote:
> On 5/22/07, Alan Cox <[email protected]> wrote:
> > > you display text in graphics mode entirely in kernel context. The
> > > driver should also attempt to stop the GPU to try and make sure it
> > > doesn't erase the OOPS display.
> >
> > If we know how yes. Probably the X server needs to provide us with a
> > simple list of operations to perform or failing that write 128K of
> > null to the display then print the characters (the 128K should ensure
> > any running FIFO for commands is stuffed and we get back to the
> > framebuffer)
> >
> > > Another simple thing that needs to be built is a mechanism to run
> > > the VBIOS in x86 mode when the driver is first loaded. This can be
> >
> > We don't want to touch the video bios for most cards, its not as good
> > as the X mode switcher code, nor for that matter does it work in a lot
> > of cases as they use IRQ and DMA functions sometimes.
>
> The only reason the VBIOS is run is to initially reset the card. Once
> we get the card reset it isn't used any more. For almost all cards
> this is the only way to reset them, we have insufficient information
> to write this ourselves.

We should aim to get away from this though. For Intel and VIA chips, we
have enough info to do full card setup ourselves. IIRC there's some early
code to do the same for ATI chips (not sure about other chips though). If
we can avoid using the VBIOS, our drivers will be far more portable and
featureful than they'd be otherwise. For example, current ATI drivers
simply can't do full multihead on many setups because it relies on the
BIOS to setup external DAC chips, and in many cases the BIOS doesn't. In
order to address that, we need to learn how to do it ourselves... So if
at all possible, I'd like to avoid baking any assumptions about VBIOS POST
into this design.

Jesse

2007-05-22 15:59:41

by Matthew Garrett

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On Tue, May 22, 2007 at 11:16:47AM -0400, Jon Smirl wrote:

> The only reason the VBIOS is run is to initially reset the card. Once
> we get the card reset it isn't used any more. For almost all cards
> this is the only way to reset them, we have insufficient information
> to write this ourselves.

For suspend/resume to ever work reliably, we need to be able to
reinitialise cards without BIOS support. It would be more useful to
spend time on that.

--
Matthew Garrett | [email protected]

2007-05-22 16:02:22

by [email protected]

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On 5/22/07, Jesse Barnes <[email protected]> wrote:
> On Tuesday, May 22, 2007, Jon Smirl wrote:
> > On 5/22/07, Alan Cox <[email protected]> wrote:
> > > > you display text in graphics mode entirely in kernel context. The
> > > > driver should also attempt to stop the GPU to try and make sure it
> > > > doesn't erase the OOPS display.
> > >
> > > If we know how yes. Probably the X server needs to provide us with a
> > > simple list of operations to perform or failing that write 128K of
> > > null to the display then print the characters (the 128K should ensure
> > > any running FIFO for commands is stuffed and we get back to the
> > > framebuffer)
> > >
> > > > Another simple thing that needs to be built is a mechanism to run
> > > > the VBIOS in x86 mode when the driver is first loaded. This can be
> > >
> > > We don't want to touch the video bios for most cards, its not as good
> > > as the X mode switcher code, nor for that matter does it work in a lot
> > > of cases as they use IRQ and DMA functions sometimes.
> >
> > The only reason the VBIOS is run is to initially reset the card. Once
> > we get the card reset it isn't used any more. For almost all cards
> > this is the only way to reset them, we have insufficient information
> > to write this ourselves.
>
> We should aim to get away from this though. For Intel and VIA chips, we
> have enough info to do full card setup ourselves. IIRC there's some early
> code to do the same for ATI chips (not sure about other chips though). If
> we can avoid using the VBIOS, our drivers will be far more portable and
> featureful than they'd be otherwise. For example, current ATI drivers
> simply can't do full multihead on many setups because it relies on the
> BIOS to setup external DAC chips, and in many cases the BIOS doesn't. In
> order to address that, we need to learn how to do it ourselves... So if
> at all possible, I'd like to avoid baking any assumptions about VBIOS POST
> into this design.

I've talked to an ATI engineer about VBIOS initialization. The chips
may have different steppings. They flash the right VBIOS that matches
the chip into the ROM on the card. Given all the various steppings
this is the only sane way to initialize the hardware. I don't believe
generic initialization code that can handle all of the various
steppings exists for any hardware.

--
Jon Smirl
[email protected]

2007-05-22 16:06:31

by [email protected]

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

Someone just pinged me privately asking about virtualization support
for the console and graphics hardware. What's the plan for handling
virtualization?

--
Jon Smirl
[email protected]

2007-05-22 16:10:32

by Alan

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

> I've talked to an ATI engineer about VBIOS initialization. The chips
> may have different steppings.

ATI won't even tell us how to do 2D mode setting or clear the code under
NDA that does so. So we don't support AMD (aka ATI) video hardware
anyway. Until they grow up its an irrelevant case.

The problem is how to support the hardware of people who will work sanely
with the free software community and how to do it well. If it makes life
worse for those who don't then that is their problem.

Alan

2007-05-22 16:15:10

by Alan

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On Tue, 22 May 2007 12:06:11 -0400
"Jon Smirl" <[email protected]> wrote:

> Someone just pinged me privately asking about virtualization support
> for the console and graphics hardware. What's the plan for handling
> virtualization?

qemu already emulates a fairly crappy PCI video card. Since X supports
remote display properly thats sufficient for most things.

Alan

2007-05-22 16:16:22

by Jesse Barnes

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On Tuesday, May 22, 2007, Jon Smirl wrote:
> I've talked to an ATI engineer about VBIOS initialization. The chips
> may have different steppings. They flash the right VBIOS that matches
> the chip into the ROM on the card. Given all the various steppings
> this is the only sane way to initialize the hardware. I don't believe
> generic initialization code that can handle all of the various
> steppings exists for any hardware.

I think you must have misunderstood. Last time I worked on ATI, there was
a single ROM image that supported several (maybe even all) of their addin
cards. There's nothing magical about them... But that's beside the
point.

They may have hardcoded information about how a particular board is wired
up--it's ok to use that (makes the DAC discovery much easier). We just
want to avoid relying on the VBIOS for DAC setup and output probing, since
we'll need to do that ourselves for suspend resume and to fully support
various multihead configurations.

Jesse

2007-05-22 16:29:48

by Philipp Klaus Krause

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

Benjamin Herrenschmidt schrieb:
>> In collaboration with the FB guys, we've been working on enhancing
>> the
>> kernel's graphics subsystem in an attempt to bring some sanity to the
>> Linux graphics world and avoid the situation we have now where
>> several
>> kernel and userspace drivers compete for control of graphics devices.
>

What's the difference between this and Jon Smirls' proposals from early
2005?

Philipp

2007-05-22 16:32:37

by [email protected]

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On 5/22/07, Jesse Barnes <[email protected]> wrote:
> On Tuesday, May 22, 2007, Jon Smirl wrote:
> > I've talked to an ATI engineer about VBIOS initialization. The chips
> > may have different steppings. They flash the right VBIOS that matches
> > the chip into the ROM on the card. Given all the various steppings
> > this is the only sane way to initialize the hardware. I don't believe
> > generic initialization code that can handle all of the various
> > steppings exists for any hardware.
>
> I think you must have misunderstood. Last time I worked on ATI, there was
> a single ROM image that supported several (maybe even all) of their addin
> cards. There's nothing magical about them... But that's beside the
> point.

What about the poke the obscure value into this undocumented IO port
class of problem where they are using the chip test interface to work
around bugs in the silicon.

He also mentioned that all of the OEMs change the standard ROM image
before loading it onto their cards to handle how they wired things up
and bugs in other chips.

Of course I don't have the source for the ROM so I don't know the
accuracy of what I was told.

>
> They may have hardcoded information about how a particular board is wired
> up--it's ok to use that (makes the DAC discovery much easier). We just
> want to avoid relying on the VBIOS for DAC setup and output probing, since
> we'll need to do that ourselves for suspend resume and to fully support
> various multihead configurations.
>
> Jesse
>


--
Jon Smirl
[email protected]

2007-05-22 16:34:26

by Jeff Garzik

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

Jon Smirl wrote:
> Someone just pinged me privately asking about virtualization support
> for the console and graphics hardware. What's the plan for handling
> virtualization?

Step one: avoid VBIOS, and drive the hardware with open source software
we have written :)

Jeff



2007-05-22 16:35:45

by Jeff Garzik

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

Jon Smirl wrote:
> On 5/22/07, Jesse Barnes <[email protected]> wrote:
>> On Tuesday, May 22, 2007, Jon Smirl wrote:
>> > I've talked to an ATI engineer about VBIOS initialization. The chips
>> > may have different steppings. They flash the right VBIOS that matches
>> > the chip into the ROM on the card. Given all the various steppings
>> > this is the only sane way to initialize the hardware. I don't believe
>> > generic initialization code that can handle all of the various
>> > steppings exists for any hardware.
>>
>> I think you must have misunderstood. Last time I worked on ATI, there
>> was
>> a single ROM image that supported several (maybe even all) of their addin
>> cards. There's nothing magical about them... But that's beside the
>> point.
>
> What about the poke the obscure value into this undocumented IO port
> class of problem where they are using the chip test interface to work
> around bugs in the silicon.
>
> He also mentioned that all of the OEMs change the standard ROM image
> before loading it onto their cards to handle how they wired things up
> and bugs in other chips.
>
> Of course I don't have the source for the ROM so I don't know the
> accuracy of what I was told.

There is nothing magic about a ROM. It's just software, or, often, data
tables.

Jeff



2007-05-22 16:51:43

by Jesse Barnes

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On Tuesday, May 22, 2007, Jon Smirl wrote:
> On 5/22/07, Jesse Barnes <[email protected]> wrote:
> > On Tuesday, May 22, 2007, Jon Smirl wrote:
> > > I've talked to an ATI engineer about VBIOS initialization. The chips
> > > may have different steppings. They flash the right VBIOS that
> > > matches the chip into the ROM on the card. Given all the various
> > > steppings this is the only sane way to initialize the hardware. I
> > > don't believe generic initialization code that can handle all of the
> > > various steppings exists for any hardware.
> >
> > I think you must have misunderstood. Last time I worked on ATI, there
> > was a single ROM image that supported several (maybe even all) of
> > their addin cards. There's nothing magical about them... But that's
> > beside the point.
>
> What about the poke the obscure value into this undocumented IO port
> class of problem where they are using the chip test interface to work
> around bugs in the silicon.
>
> He also mentioned that all of the OEMs change the standard ROM image
> before loading it onto their cards to handle how they wired things up
> and bugs in other chips.

Right, I think that's where most of the real differences are:
configuration tables. And Linux drivers will want to continue to make use
of those.

> Of course I don't have the source for the ROM so I don't know the
> accuracy of what I was told.

Yeah, it would be nice if we could get the ROM sources, would probably make
for some interesting (and maybe frightening) reading. :)

Jesse

2007-05-22 16:57:53

by Jesse Barnes

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On Tuesday, May 22, 2007, Philipp Klaus Krause wrote:
> Benjamin Herrenschmidt schrieb:
> >> In collaboration with the FB guys, we've been working on enhancing
> >> the
> >> kernel's graphics subsystem in an attempt to bring some sanity to the
> >> Linux graphics world and avoid the situation we have now where
> >> several
> >> kernel and userspace drivers compete for control of graphics devices.
>
> What's the difference between this and Jon Smirls' proposals from early
> 2005?

There are lots of differences. From memory, Jon tried to enhance the FB
layer to do modesetting with an eye toward multiseat support. Modes were
set by echoing values into sysfs, and iirc it didn't tie into the DRM
layer at all or use proper memory management.

These patches use ioctls on the DRM device for modesetting (at least for
now), and provide userspace with maximum flexibility. Framebuffer objects
are allocated using the new DRM memory manager, and any FB devices created
are slaves of the DRM layer, avoiding some of the fighting over
suspend/resume and graphics programming.

Many of the aims are the same though, Jon was trying to address many of the
problems outlined in my initial mail too, we just have different opinions
about how exactly those problems should be solved. :)

Jesse

2007-05-22 17:25:20

by Dave Airlie

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

> These are not isolated problems. Linux needs a properly designed
> graphics subsystem. One way to achieve that is to design it all on
> paper first so that we can try and locate the interactions between
> modules. For example the current mode setting design is definitely
> broken for multi-seat support, that's because you didn't take that
> feature into account when writing the code.

No it isn't the code Jesse posted can handle multi-seat fine in the
areas that it makes sense as we've pointed out to you you cannot just
divide a GPU up into two head and not have the users interfere with
each other, reprogramming modes on multiple crtcs/outputs and setting
up memory bandwidth calculations requires the driver to do things that
will potentially disrupt the other user, in most cases setting a mode
on a head requires turning off all devices on the card first and
switching them back on in a certain order, this is usually due to
clocking interactions,

We can provide two fb interfaces for users wanting to do what you
want, we then need to fix the VT subsystem on top of that, however for
most of our users (like 95% at least) we want to support X as best we
can, and that is where the energy will be placed initially, reducing
the number of mode changes on startup along with suspend/resume for
users is a major goal of this work.

> Putting a small module into the kernel first with a random API, then
> try and build the next module is not a good development path. It is
> better to design all of the modules on paper and then work backwards
> to the API the first module needs. Even better would be to get the
> whole subsystem working before including it in the kernel. Once these
> exposed APIs go it, it is impossible to get them out. We need to try
> and make sure that they are correct to begin with.

Fine, but nobody has succeded at this, because the effort of doing it
this way is not incrementally developed, we are not designing DX10, we
are improving Linux graphics one step at a time.

>
> You need to take into account that you are proposing the replacement
> of an existing subsystem, not the initial inclusion of a virgin
> system. Putting your code in as is does make the X server happy, but
> it is not solving all of the known problems with the existing graphics
> subsystem. If you just want to make to X server happy it would be
> better to extend the existing fbdev API and not try and replace the
> subsystem.

The thing is we need integration with memory management, the memory
management is in the drm as it is complicated and the work is done, I
know you aren't even reading this sentence at this point.

Dave.

2007-05-22 18:18:57

by Dave Airlie

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On 5/22/07, Philipp Klaus Krause <[email protected]> wrote:
> Benjamin Herrenschmidt schrieb:
> >> In collaboration with the FB guys, we've been working on enhancing
> >> the
> >> kernel's graphics subsystem in an attempt to bring some sanity to the
> >> Linux graphics world and avoid the situation we have now where
> >> several
> >> kernel and userspace drivers compete for control of graphics devices.
> >
>
> What's the difference between this and Jon Smirls' proposals from early
> 2005?
>

One of the biggest differences is we have the experience of the
randr-1.2 code in the X server and a chipset which is backed my the
manufacturer to design and test on. It also makes sure the design is
actually real-world useful, I've already deployed this modesetting
code in a standalone GL multi-head enviornment using miniglx code.

We also aim to maintain as much compatibility with the current system
as possible, whereas Jon really didn't care.

Also Jon proposed about 10 things, but never an actual API, the things
he proposed are still problems, but they are I believe as do many
others to be orthogonal problems, not the intertwined things that he
believes should be lumped under a single API ala DirectX.

Dave.

2007-05-22 19:59:13

by [email protected]

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On 5/22/07, Dave Airlie <[email protected]> wrote:
> > These are not isolated problems. Linux needs a properly designed
> > graphics subsystem. One way to achieve that is to design it all on
> > paper first so that we can try and locate the interactions between
> > modules. For example the current mode setting design is definitely
> > broken for multi-seat support, that's because you didn't take that
> > feature into account when writing the code.
>
> No it isn't the code Jesse posted can handle multi-seat fine in the
> areas that it makes sense as we've pointed out to you you cannot just

The code doesn't create one device per CRTC. Missing that feature
means that we need a persistent root priv app around that owns the
single device and then listens for messages from each seat asking it
to do things. That root priv app is not necessary, it is a security
risk and it should be eliminated.

> divide a GPU up into two head and not have the users interfere with
> each other, reprogramming modes on multiple crtcs/outputs and setting
> up memory bandwidth calculations requires the driver to do things that
> will potentially disrupt the other user, in most cases setting a mode
> on a head requires turning off all devices on the card first and
> switching them back on in a certain order, this is usually due to
> clocking interactions,

All this doesn't mean that it is impossible. I'm not asking you to
write this today, I just want an API that will allow it in the future.
The last graphics API was with us 25 years, I would hope that the
replacement can make it at least six months without needing changes. I
believe this feature will be much easier to implement on DX10 hardware
which will be wide spread in a few years.

> We can provide two fb interfaces for users wanting to do what you
> want, we then need to fix the VT subsystem on top of that, however for
> most of our users (like 95% at least) we want to support X as best we
> can, and that is where the energy will be placed initially, reducing
> the number of mode changes on startup along with suspend/resume for
> users is a major goal of this work.

Everyone runs X because we have built an environment where it is
pretty much impossible to create an alternative graphics system. If
the low level graphics system is going to get rewritten it is a crime
to do it to only serve the needs of X. Linux is about choice; it's
time we fixed the low level graphics system to make choice possible.
If you haven't noticed the other two major OSes have switched to
graphics systems based on 3D hardware. It is pretty much impossible to
build a graphics system like that for Linux given the current state of
low level graphics support.

> > Putting a small module into the kernel first with a random API, then
> > try and build the next module is not a good development path. It is
> > better to design all of the modules on paper and then work backwards
> > to the API the first module needs. Even better would be to get the
> > whole subsystem working before including it in the kernel. Once these
> > exposed APIs go it, it is impossible to get them out. We need to try
> > and make sure that they are correct to begin with.
>
> Fine, but nobody has succeeded at this, because the effort of doing it
> this way is not incrementally developed, we are not designing DX10, we
> are improving Linux graphics one step at a time.

And the right way to build a house is to build the kitchen first and
figure out the rest after the kitchen is finished.

> > You need to take into account that you are proposing the replacement
> > of an existing subsystem, not the initial inclusion of a virgin
> > system. Putting your code in as is does make the X server happy, but
> > it is not solving all of the known problems with the existing graphics
> > subsystem. If you just want to make to X server happy it would be
> > better to extend the existing fbdev API and not try and replace the
> > subsystem.
>
> The thing is we need integration with memory management, the memory
> management is in the drm as it is complicated and the work is done, I
> know you aren't even reading this sentence at this point.

A new memory manager for drm is a nice piece of work. It was something
that needed to get done. But right now it is being done in an X
specific manner without consideration of alternative graphics
environments.


>
> Dave.
>


--
Jon Smirl
[email protected]

2007-05-22 21:43:18

by Jesse Barnes

[permalink] [raw]
Subject: [PATCH 1/2] allow console unregistration

On Thursday, May 17, 2007, Antonino A. Daplas wrote:
> On Thu, 2007-05-17 at 15:32 -0700, Jesse Barnes wrote:
> > Randy just informed me that the patch limits are bigger now, so here
> > are the actual patches.
> >
> > This patch allows for proper console unregistration via the VT layer,
> > and updates the FB layer to use it. This makes debugging new console
> > drivers much easier, since you can properly clean them up before
> > unloading. Antonio already checked it out (and suggested a tweak for
> > the fbcon side) so I think it's on its way already via the FB tree.
>
> Sorry, I was busy and got sidetracked so I wasn't able to work on this
> for 2 weeks. Yes, this should work for now, at least for debugging
> purposes. I'll work on refining on fbcon side, hopefully for
> 2.6.22-2.6.23 time frame.

Btw, here's an updated console unregister patchset with signoff in the
off chance you like it right away. :)

When rmmod'd, some drivers might like to unregister the console they have
registered (e.g. the fbcon driver), so export unbind_con_driver. It
properly cleans up console references and takes care of switching to
one of the other registered drivers as needed.

Signed-off-by: Jesse Barnes <[email protected]>

diff -Napur -X /home/jbarnes/dontdiff --exclude=Makefile linux-2.6.22-rc2/drivers/char/vt.c linux-2.6.22-rc2-modesetting/drivers/char/vt.c
--- linux-2.6.22-rc2/drivers/char/vt.c 2007-05-18 21:06:17.000000000 -0700
+++ linux-2.6.22-rc2-modesetting/drivers/char/vt.c 2007-05-22 14:10:35.000000000 -0700
@@ -2981,8 +2981,24 @@ static int con_is_graphics(const struct
return retval;
}

-static int unbind_con_driver(const struct consw *csw, int first, int last,
- int deflt)
+/**
+ * unbind_con_driver - unbind a console driver
+ * @csw: pointer to console driver to unregister
+ * @first: first in range of consoles that @csw should be unbound from
+ * @last: last in range of consoles that @csw should be unbound from
+ * @deflt: should next bound console driver be default after @csw is unbound?
+ *
+ * To unbind a driver from all possible consoles, pass 0 as @first and
+ * %MAX_NR_CONSOLES as @last.
+ *
+ * @deflt controls whether the console that ends up replacing @csw should be
+ * the default console.
+ *
+ * RETURNS:
+ * -ENODEV if @csw isn't a registered console driver or can't be unregistered
+ * or 0 on success.
+ */
+int unbind_con_driver(const struct consw *csw, int first, int last, int deflt)
{
struct module *owner = csw->owner;
const struct consw *defcsw = NULL;
@@ -3067,6 +3083,7 @@ err:
return retval;

}
+EXPORT_SYMBOL(unbind_con_driver);

static int vt_bind(struct con_driver *con)
{
diff -Napur -X /home/jbarnes/dontdiff --exclude=Makefile linux-2.6.22-rc2/include/linux/console.h linux-2.6.22-rc2-modesetting/include/linux/console.h
--- linux-2.6.22-rc2/include/linux/console.h 2007-05-18 21:06:17.000000000 -0700
+++ linux-2.6.22-rc2-modesetting/include/linux/console.h 2007-05-22 14:10:35.000000000 -0700
@@ -64,6 +64,7 @@ extern const struct consw vga_con; /* VG
extern const struct consw newport_con; /* SGI Newport console */
extern const struct consw prom_con; /* SPARC PROM console */

+int unbind_con_driver(const struct consw *csw, int first, int last, int deflt);
int con_is_bound(const struct consw *csw);
int register_con_driver(const struct consw *csw, int first, int last);
int unregister_con_driver(const struct consw *csw);

2007-05-22 21:45:15

by Jesse Barnes

[permalink] [raw]
Subject: [PATCH 2/2] make fbcon unregister when unloaded

On Thursday, May 17, 2007, Antonino A. Daplas wrote:
> On Thu, 2007-05-17 at 15:32 -0700, Jesse Barnes wrote:
> > Randy just informed me that the patch limits are bigger now, so here
> > are the actual patches.
> >
> > This patch allows for proper console unregistration via the VT layer,
> > and updates the FB layer to use it. This makes debugging new console
> > drivers much easier, since you can properly clean them up before
> > unloading. Antonio already checked it out (and suggested a tweak for
> > the fbcon side) so I think it's on its way already via the FB tree.
>
> Sorry, I was busy and got sidetracked so I wasn't able to work on this
> for 2 weeks. Yes, this should work for now, at least for debugging
> purposes. I'll work on refining on fbcon side, hopefully for
> 2.6.22-2.6.23 time frame.

When unloaded, the fbcon driver should unregister itself from the VT
subsystem using unbind_con_driver. This patch makes it use the newly
exported function to do just that.

Signed-off-by: Jesse Barnes <[email protected]>

diff -Napur -X /home/jbarnes/dontdiff --exclude=Makefile linux-2.6.22-rc2/drivers/video/console/fbcon.c linux-2.6.22-rc2-modesetting/drivers/video/console/fbcon.c
--- linux-2.6.22-rc2/drivers/video/console/fbcon.c 2007-05-18 21:06:17.000000000 -0700
+++ linux-2.6.22-rc2-modesetting/drivers/video/console/fbcon.c 2007-05-22 14:26:20.000000000 -0700
@@ -2937,6 +2937,21 @@ static int fbcon_mode_deleted(struct fb_
return found;
}

+static int fbcon_fb_unbind(int idx)
+{
+ int i;
+
+ for (i = 0; i < MAX_NR_CONSOLES; i++) {
+ /* Assure we do not unbind other drivers */
+ if (idx == con2fb_map[i])
+ /* can be optimize to minimize multiple calls to
+ unbind_con_driver() */
+ unbind_con_driver(&fb_con, i, i, 0);
+ }
+
+ return 0;
+}
+
static int fbcon_fb_unregistered(int idx)
{
int i;
@@ -3114,6 +3129,9 @@ static int fbcon_event_notify(struct not
mode = event->data;
ret = fbcon_mode_deleted(info, mode);
break;
+ case FB_EVENT_FB_UNBIND:
+ ret = fbcon_fb_unbind(info->node);
+ break;
case FB_EVENT_FB_REGISTERED:
ret = fbcon_fb_registered(info->node);
break;
diff -Napur -X /home/jbarnes/dontdiff --exclude=Makefile linux-2.6.22-rc2/drivers/video/fbmem.c linux-2.6.22-rc2-modesetting/drivers/video/fbmem.c
--- linux-2.6.22-rc2/drivers/video/fbmem.c 2007-05-18 21:06:17.000000000 -0700
+++ linux-2.6.22-rc2-modesetting/drivers/video/fbmem.c 2007-05-22 14:10:35.000000000 -0700
@@ -1400,6 +1400,8 @@ unregister_framebuffer(struct fb_info *f
if (!registered_fb[i])
return -EINVAL;

+ event.info = fb_info;
+ fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event);
if (fb_info->pixmap.addr &&
(fb_info->pixmap.flags & FB_PIXMAP_DEFAULT))
kfree(fb_info->pixmap.addr);
diff -Napur -X /home/jbarnes/dontdiff --exclude=Makefile linux-2.6.22-rc2/include/linux/fb.h linux-2.6.22-rc2-modesetting/include/linux/fb.h
--- linux-2.6.22-rc2/include/linux/fb.h 2007-05-18 21:06:17.000000000 -0700
+++ linux-2.6.22-rc2-modesetting/include/linux/fb.h 2007-05-22 14:11:00.000000000 -0700
@@ -529,6 +529,9 @@ struct fb_cursor_user {
#define FB_EVENT_CONBLANK 0x0C
/* Get drawing requirements */
#define FB_EVENT_GET_REQ 0x0D
+/* Unbind from the console if possible */
+#define FB_EVENT_FB_UNBIND 0x0E
+

struct fb_event {
struct fb_info *info;

2007-05-22 22:00:54

by Randy Dunlap

[permalink] [raw]
Subject: Re: [PATCH 2/2] make fbcon unregister when unloaded

On Tue, 22 May 2007 14:44:52 -0700 Jesse Barnes wrote:

> When unloaded, the fbcon driver should unregister itself from the VT
> subsystem using unbind_con_driver. This patch makes it use the newly
> exported function to do just that.
>
> Signed-off-by: Jesse Barnes <[email protected]>
>
> diff -Napur -X /home/jbarnes/dontdiff --exclude=Makefile linux-2.6.22-rc2/drivers/video/console/fbcon.c linux-2.6.22-rc2-modesetting/drivers/video/console/fbcon.c
> --- linux-2.6.22-rc2/drivers/video/console/fbcon.c 2007-05-18 21:06:17.000000000 -0700
> +++ linux-2.6.22-rc2-modesetting/drivers/video/console/fbcon.c 2007-05-22 14:26:20.000000000 -0700
> @@ -2937,6 +2937,21 @@ static int fbcon_mode_deleted(struct fb_
> return found;
> }
>
> +static int fbcon_fb_unbind(int idx)
> +{
> + int i;
> +
> + for (i = 0; i < MAX_NR_CONSOLES; i++) {
> + /* Assure we do not unbind other drivers */
> + if (idx == con2fb_map[i])
> + /* can be optimize to minimize multiple calls to
> + unbind_con_driver() */

/*
* can be optimized to minimize multiple calls
* to unbind_con_driver()
*/

> + unbind_con_driver(&fb_con, i, i, 0);
> + }
> +
> + return 0;
> +}

Lots of whitespace mangling there (mostly spaces instead of tabs).

> +
> static int fbcon_fb_unregistered(int idx)
> {
> int i;

---
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***

2007-05-22 22:14:27

by Jesse Barnes

[permalink] [raw]
Subject: Re: [PATCH 2/2] make fbcon unregister when unloaded

On Tuesday, May 22, 2007 3:05 pm Randy Dunlap wrote:
> On Tue, 22 May 2007 14:44:52 -0700 Jesse Barnes wrote:
> > When unloaded, the fbcon driver should unregister itself from the
> > VT subsystem using unbind_con_driver. This patch makes it use the
> > newly exported function to do just that.
> >
> > Signed-off-by: Jesse Barnes <[email protected]>
> >
> > diff -Napur -X /home/jbarnes/dontdiff --exclude=Makefile
> > linux-2.6.22-rc2/drivers/video/console/fbcon.c
> > linux-2.6.22-rc2-modesetting/drivers/video/console/fbcon.c ---
> > linux-2.6.22-rc2/drivers/video/console/fbcon.c 2007-05-18
> > 21:06:17.000000000 -0700 +++
> > linux-2.6.22-rc2-modesetting/drivers/video/console/fbcon.c 2007-05-
> >22 14:26:20.000000000 -0700 @@ -2937,6 +2937,21 @@ static int
> > fbcon_mode_deleted(struct fb_ return found;
> > }
> >
> > +static int fbcon_fb_unbind(int idx)
> > +{
> > + int i;
> > +
> > + for (i = 0; i < MAX_NR_CONSOLES; i++) {
> > + /* Assure we do not unbind other drivers */
> > + if (idx == con2fb_map[i])
> > + /* can be optimize to minimize multiple
> > calls to + unbind_con_driver() */
>
> /*
> * can be optimized to minimize multiple calls
> * to unbind_con_driver()
> */
>
> > + unbind_con_driver(&fb_con, i, i, 0);
> > + }
> > +
> > + return 0;
> > +}
>
> Lots of whitespace mangling there (mostly spaces instead of tabs).

Oops, thanks for looking. Somehow my emacs configuration broke and I
don't see this as readily as I used to. If Antonio is ok with the
patch otherwise, I'll respin it with the cleanups.

Thanks,
Jesse

2007-05-22 23:28:37

by Benjamin Herrenschmidt

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On Tue, 2007-05-22 at 08:39 -0700, Jesse Barnes wrote:
>
> The current code does its best to figure out what modes are available and
> tries to pick a good one for each display. It sounds like you're mainly
> concerned with the actual mode picking, not the mode and output detection
> and enumeration? If so, that's a pretty easy change to make. But if
> you're also worried about the kernel building mode lists, then we'll have
> bigger changes to make...

I'm worried that the EDID we get from the monitor is bogus and needs to
be overriden.

Now, if the kernel builds a mode list, that's find if we have a call to
"feed" it with a replacement one later on from userland.

In addition, there are all those monitors that cannot be probed (no
DDC/EDID) and for which only userland can reasonably provide a mode or a
mode list.

So it's a bit of both :-) Building an "initial" mode list from the EDID
might be fair enough if we can replace it soon enough, but we still need
to be very conservative about whatever boot mode we choose.

> I'm not really sure how much of a problem broken EDIDs will be. The X
> server only has a few quirks for broken EDIDs now, nothing major afaict,
> and apparently the FB layer already has some code for handling EDID
> quirks, so I don't think that'll be our biggest problem. So far, it looks
> like handling laptop panels is a bit trickier (at least for Intel
> chips)...

Well, I've seen my share of broken EDID.. Last time I looked at Darwin,
I think I saw Apple maintaining a fairly huge database of EDID replacements
in userland...

Ben.

2007-05-22 23:36:41

by Jesse Barnes

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On Tuesday, May 22, 2007, Benjamin Herrenschmidt wrote:
> On Tue, 2007-05-22 at 08:39 -0700, Jesse Barnes wrote:
> > The current code does its best to figure out what modes are available
> > and tries to pick a good one for each display. It sounds like you're
> > mainly concerned with the actual mode picking, not the mode and output
> > detection and enumeration? If so, that's a pretty easy change to
> > make. But if you're also worried about the kernel building mode
> > lists, then we'll have bigger changes to make...
>
> I'm worried that the EDID we get from the monitor is bogus and needs to
> be overriden.
>
> Now, if the kernel builds a mode list, that's find if we have a call to
> "feed" it with a replacement one later on from userland.
>
> In addition, there are all those monitors that cannot be probed (no
> DDC/EDID) and for which only userland can reasonably provide a mode or a
> mode list.

Yeah, we already have a call to add modes to the kernel's list, so we
should be covered.

> So it's a bit of both :-) Building an "initial" mode list from the EDID
> might be fair enough if we can replace it soon enough, but we still need
> to be very conservative about whatever boot mode we choose.

Right.

> > I'm not really sure how much of a problem broken EDIDs will be. The X
> > server only has a few quirks for broken EDIDs now, nothing major
> > afaict, and apparently the FB layer already has some code for handling
> > EDID quirks, so I don't think that'll be our biggest problem. So far,
> > it looks like handling laptop panels is a bit trickier (at least for
> > Intel chips)...
>
> Well, I've seen my share of broken EDID.. Last time I looked at Darwin,
> I think I saw Apple maintaining a fairly huge database of EDID
> replacements in userland...

Interesting... I wonder how the distro monitor databases compare.

Jesse


2007-05-23 00:40:27

by Antonino A. Daplas

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On Tue, 2007-05-22 at 16:36 -0700, Jesse Barnes wrote:
> On Tuesday, May 22, 2007, Benjamin Herrenschmidt wrote:
> > On Tue, 2007-05-22 at 08:39 -0700, Jesse Barnes wrote:
> > > The current code does its best to figure out what modes are available
> > > and tries to pick a good one for each display. It sounds like you're
> > > mainly concerned with the actual mode picking, not the mode and output
> > > detection and enumeration? If so, that's a pretty easy change to
> > > make. But if you're also worried about the kernel building mode
> > > lists, then we'll have bigger changes to make...
> >
> > I'm worried that the EDID we get from the monitor is bogus and needs to
> > be overriden.
> >
> > Now, if the kernel builds a mode list, that's find if we have a call to
> > "feed" it with a replacement one later on from userland.
> >
> > In addition, there are all those monitors that cannot be probed (no
> > DDC/EDID) and for which only userland can reasonably provide a mode or a
> > mode list.
>
> Yeah, we already have a call to add modes to the kernel's list, so we
> should be covered.
>

We actually have a function that will replace the entire mode list with
a new one safely.

Tony


2007-05-23 00:47:41

by Antonino A. Daplas

[permalink] [raw]
Subject: Re: [PATCH 2/2] make fbcon unregister when unloaded

On Tue, 2007-05-22 at 15:14 -0700, Jesse Barnes wrote:
> On Tuesday, May 22, 2007 3:05 pm Randy Dunlap wrote:
> > On Tue, 22 May 2007 14:44:52 -0700 Jesse Barnes wrote:
> > > When unloaded, the fbcon driver should unregister itself from the
> > > VT subsystem using unbind_con_driver. This patch makes it use the
> > > newly exported function to do just that.
> > >
> > > Signed-off-by: Jesse Barnes <[email protected]>
> > >
> > > diff -Napur -X /home/jbarnes/dontdiff --exclude=Makefile
> > > linux-2.6.22-rc2/drivers/video/console/fbcon.c
> > > linux-2.6.22-rc2-modesetting/drivers/video/console/fbcon.c ---
> > > linux-2.6.22-rc2/drivers/video/console/fbcon.c 2007-05-18
> > > 21:06:17.000000000 -0700 +++
> > > linux-2.6.22-rc2-modesetting/drivers/video/console/fbcon.c 2007-05-
> > >22 14:26:20.000000000 -0700 @@ -2937,6 +2937,21 @@ static int
> > > fbcon_mode_deleted(struct fb_ return found;
> > > }
> > >
> > > +static int fbcon_fb_unbind(int idx)
> > > +{
> > > + int i;
> > > +
> > > + for (i = 0; i < MAX_NR_CONSOLES; i++) {
> > > + /* Assure we do not unbind other drivers */
> > > + if (idx == con2fb_map[i])
> > > + /* can be optimize to minimize multiple
> > > calls to + unbind_con_driver() */
> >
> > /*
> > * can be optimized to minimize multiple calls
> > * to unbind_con_driver()
> > */
> >
> > > + unbind_con_driver(&fb_con, i, i, 0);
> > > + }
> > > +
> > > + return 0;
> > > +}
> >
> > Lots of whitespace mangling there (mostly spaces instead of tabs).
>
> Oops, thanks for looking. Somehow my emacs configuration broke and I
> don't see this as readily as I used to. If Antonio is ok with the
> patch otherwise, I'll respin it with the cleanups.

This should be a lot safer than the previous patch. This is fine as long
as only a single driver is driving the console. For multiple drivers, it
will need the change that I promised weeks ago :-). (And I will work on
it soon).

Tony

2007-05-23 00:49:29

by Antonino A. Daplas

[permalink] [raw]
Subject: Re: [PATCH 1/2] allow console unregistration

On Tue, 2007-05-22 at 14:43 -0700, Jesse Barnes wrote:
> On Thursday, May 17, 2007, Antonino A. Daplas wrote:
> > On Thu, 2007-05-17 at 15:32 -0700, Jesse Barnes wrote:
> > > Randy just informed me that the patch limits are bigger now, so here
> > > are the actual patches.
> > >
> > > This patch allows for proper console unregistration via the VT layer,
> > > and updates the FB layer to use it. This makes debugging new console
> > > drivers much easier, since you can properly clean them up before
> > > unloading. Antonio already checked it out (and suggested a tweak for
> > > the fbcon side) so I think it's on its way already via the FB tree.
> >
> > Sorry, I was busy and got sidetracked so I wasn't able to work on this
> > for 2 weeks. Yes, this should work for now, at least for debugging
> > purposes. I'll work on refining on fbcon side, hopefully for
> > 2.6.22-2.6.23 time frame.
>
> Btw, here's an updated console unregister patchset with signoff in the
> off chance you like it right away. :)
>
> When rmmod'd, some drivers might like to unregister the console they have
> registered (e.g. the fbcon driver), so export unbind_con_driver. It
> properly cleans up console references and takes care of switching to
> one of the other registered drivers as needed.
>

I'm working on something that will need these functions
(unbind/bind_con_driver) to be exposed, so I'm fine with this.

Tony


2007-05-23 12:29:57

by Helge Hafting

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

Benjamin Herrenschmidt wrote:
> On Tue, 2007-05-22 at 08:39 -0700, Jesse Barnes wrote:
>
>> The current code does its best to figure out what modes are available and
>> tries to pick a good one for each display. It sounds like you're mainly
>> concerned with the actual mode picking, not the mode and output detection
>> and enumeration? If so, that's a pretty easy change to make. But if
>> you're also worried about the kernel building mode lists, then we'll have
>> bigger changes to make...
>>
>
> I'm worried that the EDID we get from the monitor is bogus and needs to
> be overriden.
>
How often do that happen?

There is the alternate solution of assuming that EDID is correct,
and provide an override when it isn't. So anyone with a good monitor
get a nice display by default. Those with a defective lying monitor
may have to add a "resolution=640x480" parameter to their
kernel command line to get out of the black screen modus.
This shifts the bother to those with a bad monitor, who then are
free to get pissed off at their monitor vendor . . .

Helge Hafting

2007-05-28 20:13:17

by Pavel Machek

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

Hi!

> >> These are not isolated problems. Linux needs a
> >properly designed
> >> graphics subsystem. One way to achieve that is to
> >design it all on
> >> paper first so that we can try and locate the
> >interactions between
> >> modules. For example the current mode setting design
> >is definitely
> >> broken for multi-seat support, that's because you
> >didn't take that
> >> feature into account when writing the code.
> >
> >No it isn't the code Jesse posted can handle multi-seat
> >fine in the
> >areas that it makes sense as we've pointed out to you
> >you cannot just
>
> The code doesn't create one device per CRTC. Missing
> that feature
> means that we need a persistent root priv app around
> that owns the
> single device and then listens for messages from each
> seat asking it
> to do things. That root priv app is not necessary, it is
> a security
> risk and it should be eliminated.

Fine, submit a patch. But don't block other people patches just
because they do not address your favourite problem.

> >Fine, but nobody has succeeded at this, because the
> >effort of doing it
> >this way is not incrementally developed, we are not
> >designing DX10, we
> >are improving Linux graphics one step at a time.
>
> And the right way to build a house is to build the
> kitchen first and
> figure out the rest after the kitchen is finished.

...and the right way to build a house is to do nothing, and chase away
anyone trying to do something?

> >The thing is we need integration with memory
> >management, the memory
> >management is in the drm as it is complicated and the
> >work is done, I
> >know you aren't even reading this sentence at this
> >point.
>
> A new memory manager for drm is a nice piece of work. It
> was something
> that needed to get done. But right now it is being done
> in an X
> specific manner without consideration of alternative
> graphics
> environments.

Feel free to post alternative patch that addresses that...

Fact is, everyone runs X these days. 'Your patches can't go in,
because you do not support alternative graphics systems (that don't
exist)' is pretty weak argument.

Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

2007-05-28 20:57:53

by [email protected]

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On 5/28/07, Pavel Machek <[email protected]> wrote:
> Hi!
>
> > >> These are not isolated problems. Linux needs a
> > >properly designed
> > >> graphics subsystem. One way to achieve that is to
> > >design it all on
> > >> paper first so that we can try and locate the
> > >interactions between
> > >> modules. For example the current mode setting design
> > >is definitely
> > >> broken for multi-seat support, that's because you
> > >didn't take that
> > >> feature into account when writing the code.
> > >
> > >No it isn't the code Jesse posted can handle multi-seat
> > >fine in the
> > >areas that it makes sense as we've pointed out to you
> > >you cannot just
> >
> > The code doesn't create one device per CRTC. Missing
> > that feature
> > means that we need a persistent root priv app around
> > that owns the
> > single device and then listens for messages from each
> > seat asking it
> > to do things. That root priv app is not necessary, it is
> > a security
> > risk and it should be eliminated.
>
> Fine, submit a patch. But don't block other people patches just
> because they do not address your favourite problem.

I have already implemented this once and submitted a patch.

Search in the paper for this paragraph:
http://jonsmirl.googlepages.com/graphics.html
DRM also implements the concept of a master user with more
capabilities than normal users. These extra capabilities allow the
graphics device to be initialized and the consumption of GPU resources
to be controlled. The current X server, which runs as root, functions
as the DRM master. There is actually no real requirement that the DRM
master be run as root, and there is a preliminary patch that removes
this requirement.

That paragraphs contains a link to the code that used to be hosted at
freedesktop.org. Maybe they can still find it.

--
Jon Smirl
[email protected]

2007-05-29 14:49:26

by Pavel Machek

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

Hi!

> >> >> These are not isolated problems. Linux needs a
> >> >properly designed
> >> >> graphics subsystem. One way to achieve that is to
> >> >design it all on
> >> >> paper first so that we can try and locate the
> >> >interactions between
> >> >> modules. For example the current mode setting
> >design
> >> >is definitely
> >> >> broken for multi-seat support, that's because you
> >> >didn't take that
> >> >> feature into account when writing the code.
> >> >
> >> >No it isn't the code Jesse posted can handle
> >multi-seat
> >> >fine in the
> >> >areas that it makes sense as we've pointed out to you
> >> >you cannot just
> >>
> >> The code doesn't create one device per CRTC. Missing
> >> that feature
> >> means that we need a persistent root priv app around
> >> that owns the
> >> single device and then listens for messages from each
> >> seat asking it
> >> to do things. That root priv app is not necessary, it
> >is
> >> a security
> >> risk and it should be eliminated.
> >
> >Fine, submit a patch. But don't block other people
> >patches just
> >because they do not address your favourite problem.
>
> I have already implemented this once and submitted a
> patch.

Seems you were not persistent enough. 'I submitted a patch long time
ago' is still no reason to block new work.

Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

2007-05-29 16:52:16

by [email protected]

[permalink] [raw]
Subject: Re: [RFC] enhancing the kernel's graphics subsystem

On 5/29/07, Pavel Machek <[email protected]> wrote:
> > I have already implemented this once and submitted a
> > patch.
>
> Seems you were not persistent enough. 'I submitted a patch long time
> ago' is still no reason to block new work.

This is a well researched area and the flaws are widely know. Adding
this new mode setting code to the kernel is not adding a new
subsystem, it is an attempt to replace an existing subsystem. In
order to replace a subsystem the general rule is that the new one must
be better than the old one. I have not seen sufficient arguments made
to justify replacing fbdev instead of extending it.

As for writing more patches, I have been burnt once by "show us the
code" being used as a way to getting rid of someone when the people
saying "show us the code" have no intention of agreeing to the changes
being proposed. I'm not going to fall for it again.

--
Jon Smirl
[email protected]

2007-05-30 00:00:50

by Antonino A. Daplas

[permalink] [raw]
Subject: Re: [PATCH 1/3] allow console unregistration

On Thu, 2007-05-17 at 15:32 -0700, Jesse Barnes wrote:
> Randy just informed me that the patch limits are bigger now, so here are the
> actual patches.
>
> This patch allows for proper console unregistration via the VT layer, and
> updates the FB layer to use it. This makes debugging new console drivers
> much easier, since you can properly clean them up before unloading.
> Antonio already checked it out (and suggested a tweak for the fbcon side)
> so I think it's on its way already via the FB tree.
>

Jesse,

I already implemented (and tested) selective framebuffer/console
unregistration in my tree using your patch as a base. I'll send this
patch to akpm soon.

Geert,

Is this something that you might need for ps3fb? It should address 2 of
your concerns. The only thing is that you will need
CONFIG_VT_HW_CONSOLE_BINDING=y. You can default this to 'y' in your
platform, or select it.

Tony

2007-05-30 06:26:41

by Geert Uytterhoeven

[permalink] [raw]
Subject: Re: [PATCH 1/3] allow console unregistration

Hi Tony,

On Wed, 30 May 2007, Antonino A. Daplas wrote:
> On Thu, 2007-05-17 at 15:32 -0700, Jesse Barnes wrote:
> > Randy just informed me that the patch limits are bigger now, so here are the
> > actual patches.
> >
> > This patch allows for proper console unregistration via the VT layer, and
> > updates the FB layer to use it. This makes debugging new console drivers
> > much easier, since you can properly clean them up before unloading.
> > Antonio already checked it out (and suggested a tweak for the fbcon side)
> > so I think it's on its way already via the FB tree.
> >
>
> Jesse,
>
> I already implemented (and tested) selective framebuffer/console
> unregistration in my tree using your patch as a base. I'll send this
> patch to akpm soon.
>
> Geert,
>
> Is this something that you might need for ps3fb? It should address 2 of
> your concerns. The only thing is that you will need
> CONFIG_VT_HW_CONSOLE_BINDING=y. You can default this to 'y' in your
> platform, or select it.

I was going to look at Jesse's patch, but it didn't happen yet.
So yes, I'm definitely interested in your patch! Can you please send it to
linux-fbdev-devel already?

Gr{oetje,eeting}s,

Geert

--
Geert Uytterhoeven -- Sony Network and Software Technology Center Europe (NSCE)
[email protected] ------- The Corporate Village, Da Vincilaan 7-D1
Voice +32-2-7008453 Fax +32-2-7008622 ---------------- B-1935 Zaventem, Belgium