aboutsummaryrefslogtreecommitdiff
path: root/drivers/gpu
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/Kconfig15
-rw-r--r--drivers/gpu/drm/drm_agpsupport.c3
-rw-r--r--drivers/gpu/drm/drm_bufs.c2
-rw-r--r--drivers/gpu/drm/drm_crtc.c17
-rw-r--r--drivers/gpu/drm/drm_crtc_helper.c270
-rw-r--r--drivers/gpu/drm/drm_drv.c4
-rw-r--r--drivers/gpu/drm/drm_edid.c8
-rw-r--r--drivers/gpu/drm/drm_fops.c23
-rw-r--r--drivers/gpu/drm/drm_gem.c81
-rw-r--r--drivers/gpu/drm/drm_irq.c188
-rw-r--r--drivers/gpu/drm/drm_lock.c3
-rw-r--r--drivers/gpu/drm/drm_memory.c7
-rw-r--r--drivers/gpu/drm/drm_stub.c18
-rw-r--r--drivers/gpu/drm/drm_sysfs.c4
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c77
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c30
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h41
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c548
-rw-r--r--drivers/gpu/drm/i915/i915_gem_tiling.c97
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c24
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h11
-rw-r--r--drivers/gpu/drm/i915/i915_suspend.c11
-rw-r--r--drivers/gpu/drm/i915/intel_bios.c14
-rw-r--r--drivers/gpu/drm/i915/intel_display.c216
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h1
-rw-r--r--drivers/gpu/drm/i915/intel_fb.c8
-rw-r--r--drivers/gpu/drm/i915/intel_i2c.c4
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c67
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo.c872
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo_regs.h404
-rw-r--r--drivers/gpu/drm/i915/intel_tv.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_cp.c27
32 files changed, 2440 insertions, 657 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 5130b72d593..3a22eb9be37 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -70,7 +70,7 @@ config DRM_I915
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
- depends on FB
+ select FB
tristate "i915 driver"
help
Choose this option if you have a system that has Intel 830M, 845G,
@@ -80,18 +80,17 @@ config DRM_I915
XFree86 4.4 and above. If unsure, build this and i830 as modules and
the X server will load the correct one.
-endchoice
-
config DRM_I915_KMS
bool "Enable modesetting on intel by default"
depends on DRM_I915
help
- Choose this option if you want kernel modesetting enabled by default,
- and you have a new enough userspace to support this. Running old
- userspaces with this enabled will cause pain. Note that this causes
- the driver to bind to PCI devices, which precludes loading things
- like intelfb.
+ Choose this option if you want kernel modesetting enabled by default,
+ and you have a new enough userspace to support this. Running old
+ userspaces with this enabled will cause pain. Note that this causes
+ the driver to bind to PCI devices, which precludes loading things
+ like intelfb.
+endchoice
config DRM_MGA
tristate "Matrox g200/g400"
diff --git a/drivers/gpu/drm/drm_agpsupport.c b/drivers/gpu/drm/drm_agpsupport.c
index 3d33b8252b5..14796594e5d 100644
--- a/drivers/gpu/drm/drm_agpsupport.c
+++ b/drivers/gpu/drm/drm_agpsupport.c
@@ -33,10 +33,11 @@
#include "drmP.h"
#include <linux/module.h>
-#include <asm/agp.h>
#if __OS_HAS_AGP
+#include <asm/agp.h>
+
/**
* Get AGP information.
*
diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c
index 72c667f9bee..12715d3c078 100644
--- a/drivers/gpu/drm/drm_bufs.c
+++ b/drivers/gpu/drm/drm_bufs.c
@@ -420,7 +420,7 @@ int drm_rmmap_locked(struct drm_device *dev, drm_local_map_t *map)
dev->sigdata.lock = NULL;
master->lock.hw_lock = NULL; /* SHM removed */
master->lock.file_priv = NULL;
- wake_up_interruptible(&master->lock.lock_queue);
+ wake_up_interruptible_all(&master->lock.lock_queue);
}
break;
case _DRM_AGP:
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 5b2cbb77816..94a76887173 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -194,7 +194,6 @@ char *drm_get_connector_status_name(enum drm_connector_status status)
* @type: object type
*
* LOCKING:
- * Caller must hold DRM mode_config lock.
*
* Create a unique identifier based on @ptr in @dev's identifier space. Used
* for tracking modes, CRTCs and connectors.
@@ -209,15 +208,15 @@ static int drm_mode_object_get(struct drm_device *dev,
int new_id = 0;
int ret;
- WARN(!mutex_is_locked(&dev->mode_config.mutex),
- "%s called w/o mode_config lock\n", __func__);
again:
if (idr_pre_get(&dev->mode_config.crtc_idr, GFP_KERNEL) == 0) {
DRM_ERROR("Ran out memory getting a mode number\n");
return -EINVAL;
}
+ mutex_lock(&dev->mode_config.idr_mutex);
ret = idr_get_new_above(&dev->mode_config.crtc_idr, obj, 1, &new_id);
+ mutex_unlock(&dev->mode_config.idr_mutex);
if (ret == -EAGAIN)
goto again;
@@ -239,16 +238,20 @@ again:
static void drm_mode_object_put(struct drm_device *dev,
struct drm_mode_object *object)
{
+ mutex_lock(&dev->mode_config.idr_mutex);
idr_remove(&dev->mode_config.crtc_idr, object->id);
+ mutex_unlock(&dev->mode_config.idr_mutex);
}
void *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type)
{
- struct drm_mode_object *obj;
+ struct drm_mode_object *obj = NULL;
+ mutex_lock(&dev->mode_config.idr_mutex);
obj = idr_find(&dev->mode_config.crtc_idr, id);
if (!obj || (obj->type != type) || (obj->id != id))
- return NULL;
+ obj = NULL;
+ mutex_unlock(&dev->mode_config.idr_mutex);
return obj;
}
@@ -786,6 +789,7 @@ EXPORT_SYMBOL(drm_mode_create_dithering_property);
void drm_mode_config_init(struct drm_device *dev)
{
mutex_init(&dev->mode_config.mutex);
+ mutex_init(&dev->mode_config.idr_mutex);
INIT_LIST_HEAD(&dev->mode_config.fb_list);
INIT_LIST_HEAD(&dev->mode_config.fb_kernel_list);
INIT_LIST_HEAD(&dev->mode_config.crtc_list);
@@ -1737,9 +1741,8 @@ out:
* RETURNS:
* Zero on success, errno on failure.
*/
-void drm_fb_release(struct file *filp)
+void drm_fb_release(struct drm_file *priv)
{
- struct drm_file *priv = filp->private_data;
struct drm_device *dev = priv->minor->dev;
struct drm_framebuffer *fb, *tfb;
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index d8a982b7129..1c3a8c55714 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -36,7 +36,7 @@
/*
* Detailed mode info for 800x600@60Hz
*/
-static struct drm_display_mode std_mode[] = {
+static struct drm_display_mode std_modes[] = {
{ DRM_MODE("800x600", DRM_MODE_TYPE_DEFAULT, 40000, 800, 840,
968, 1056, 0, 600, 601, 605, 628, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
@@ -60,15 +60,18 @@ static struct drm_display_mode std_mode[] = {
* changes have occurred.
*
* FIXME: take into account monitor limits
+ *
+ * RETURNS:
+ * Number of modes found on @connector.
*/
-void drm_helper_probe_single_connector_modes(struct drm_connector *connector,
- uint32_t maxX, uint32_t maxY)
+int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
+ uint32_t maxX, uint32_t maxY)
{
struct drm_device *dev = connector->dev;
struct drm_display_mode *mode, *t;
struct drm_connector_helper_funcs *connector_funcs =
connector->helper_private;
- int ret;
+ int count = 0;
DRM_DEBUG("%s\n", drm_get_connector_name(connector));
/* set all modes to the unverified state */
@@ -81,14 +84,14 @@ void drm_helper_probe_single_connector_modes(struct drm_connector *connector,
DRM_DEBUG("%s is disconnected\n",
drm_get_connector_name(connector));
/* TODO set EDID to NULL */
- return;
+ return 0;
}
- ret = (*connector_funcs->get_modes)(connector);
+ count = (*connector_funcs->get_modes)(connector);
+ if (!count)
+ return 0;
- if (ret) {
- drm_mode_connector_list_update(connector);
- }
+ drm_mode_connector_list_update(connector);
if (maxX && maxY)
drm_mode_validate_size(dev, &connector->modes, maxX,
@@ -102,25 +105,8 @@ void drm_helper_probe_single_connector_modes(struct drm_connector *connector,
drm_mode_prune_invalid(dev, &connector->modes, true);
- if (list_empty(&connector->modes)) {
- struct drm_display_mode *stdmode;
-
- DRM_DEBUG("No valid modes on %s\n",
- drm_get_connector_name(connector));
-
- /* Should we do this here ???
- * When no valid EDID modes are available we end up
- * here and bailed in the past, now we add a standard
- * 640x480@60Hz mode and carry on.
- */
- stdmode = drm_mode_duplicate(dev, &std_mode[0]);
- drm_mode_probed_add(connector, stdmode);
- drm_mode_list_concat(&connector->probed_modes,
- &connector->modes);
-
- DRM_DEBUG("Adding standard 640x480 @ 60Hz to %s\n",
- drm_get_connector_name(connector));
- }
+ if (list_empty(&connector->modes))
+ return 0;
drm_mode_sort(&connector->modes);
@@ -131,20 +117,58 @@ void drm_helper_probe_single_connector_modes(struct drm_connector *connector,
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
drm_mode_debug_printmodeline(mode);
}
+
+ return count;
}
EXPORT_SYMBOL(drm_helper_probe_single_connector_modes);
-void drm_helper_probe_connector_modes(struct drm_device *dev, uint32_t maxX,
+int drm_helper_probe_connector_modes(struct drm_device *dev, uint32_t maxX,
uint32_t maxY)
{
struct drm_connector *connector;
+ int count = 0;
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- drm_helper_probe_single_connector_modes(connector, maxX, maxY);
+ count += drm_helper_probe_single_connector_modes(connector,
+ maxX, maxY);
}
+
+ return count;
}
EXPORT_SYMBOL(drm_helper_probe_connector_modes);
+static void drm_helper_add_std_modes(struct drm_device *dev,
+ struct drm_connector *connector)
+{
+ struct drm_display_mode *mode, *t;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(std_modes); i++) {
+ struct drm_display_mode *stdmode;
+
+ /*
+ * When no valid EDID modes are available we end up
+ * here and bailed in the past, now we add some standard
+ * modes and move on.
+ */
+ stdmode = drm_mode_duplicate(dev, &std_modes[i]);
+ drm_mode_probed_add(connector, stdmode);
+ drm_mode_list_concat(&connector->probed_modes,
+ &connector->modes);
+
+ DRM_DEBUG("Adding mode %s to %s\n", stdmode->name,
+ drm_get_connector_name(connector));
+ }
+ drm_mode_sort(&connector->modes);
+
+ DRM_DEBUG("Added std modes on %s\n", drm_get_connector_name(connector));
+ list_for_each_entry_safe(mode, t, &connector->modes, head) {
+ mode->vrefresh = drm_mode_vrefresh(mode);
+
+ drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
+ drm_mode_debug_printmodeline(mode);
+ }
+}
/**
* drm_helper_crtc_in_use - check if a given CRTC is in a mode_config
@@ -237,6 +261,8 @@ static void drm_enable_connectors(struct drm_device *dev, bool *enabled)
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
enabled[i] = drm_connector_enabled(connector, true);
+ DRM_DEBUG("connector %d enabled? %s\n", connector->base.id,
+ enabled[i] ? "yes" : "no");
any_enabled |= enabled[i];
i++;
}
@@ -265,11 +291,17 @@ static bool drm_target_preferred(struct drm_device *dev,
continue;
}
+ DRM_DEBUG("looking for preferred mode on connector %d\n",
+ connector->base.id);
+
modes[i] = drm_has_preferred_mode(connector, width, height);
- if (!modes[i]) {
+ /* No preferred modes, pick one off the list */
+ if (!modes[i] && !list_empty(&connector->modes)) {
list_for_each_entry(modes[i], &connector->modes, head)
break;
}
+ DRM_DEBUG("found mode %s\n", modes[i] ? modes[i]->name :
+ "none");
i++;
}
return true;
@@ -369,6 +401,8 @@ static void drm_setup_crtcs(struct drm_device *dev)
int width, height;
int i, ret;
+ DRM_DEBUG("\n");
+
width = dev->mode_config.max_width;
height = dev->mode_config.max_height;
@@ -390,6 +424,8 @@ static void drm_setup_crtcs(struct drm_device *dev)
if (!ret)
DRM_ERROR("Unable to find initial modes\n");
+ DRM_DEBUG("picking CRTCs for %dx%d config\n", width, height);
+
drm_pick_crtcs(dev, crtcs, modes, 0, width, height);
i = 0;
@@ -403,6 +439,8 @@ static void drm_setup_crtcs(struct drm_device *dev)
}
if (mode && crtc) {
+ DRM_DEBUG("desired mode %s set on crtc %d\n",
+ mode->name, crtc->base.id);
crtc->desired_mode = mode;
connector->encoder->crtc = crtc;
} else
@@ -414,6 +452,59 @@ static void drm_setup_crtcs(struct drm_device *dev)
kfree(modes);
kfree(enabled);
}
+
+/**
+ * drm_encoder_crtc_ok - can a given crtc drive a given encoder?
+ * @encoder: encoder to test
+ * @crtc: crtc to test
+ *
+ * Return false if @encoder can't be driven by @crtc, true otherwise.
+ */
+static bool drm_encoder_crtc_ok(struct drm_encoder *encoder,
+ struct drm_crtc *crtc)
+{
+ struct drm_device *dev;
+ struct drm_crtc *tmp;
+ int crtc_mask = 1;
+
+ WARN(!crtc, "checking null crtc?");
+
+ dev = crtc->dev;
+
+ list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) {
+ if (tmp == crtc)
+ break;
+ crtc_mask <<= 1;
+ }
+
+ if (encoder->possible_crtcs & crtc_mask)
+ return true;
+ return false;
+}
+
+/*
+ * Check the CRTC we're going to map each output to vs. its current
+ * CRTC. If they don't match, we have to disable the output and the CRTC
+ * since the driver will have to re-route things.
+ */
+static void
+drm_crtc_prepare_encoders(struct drm_device *dev)
+{
+ struct drm_encoder_helper_funcs *encoder_funcs;
+ struct drm_encoder *encoder;
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ encoder_funcs = encoder->helper_private;
+ /* Disable unused encoders */
+ if (encoder->crtc == NULL)
+ (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF);
+ /* Disable encoders whose CRTC is about to change */
+ if (encoder_funcs->get_crtc &&
+ encoder->crtc != (*encoder_funcs->get_crtc)(encoder))
+ (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF);
+ }
+}
+
/**
* drm_crtc_set_mode - set a mode
* @crtc: CRTC to program
@@ -442,6 +533,7 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
int saved_x, saved_y;
struct drm_encoder *encoder;
bool ret = true;
+ bool depth_changed, bpp_changed;
adjusted_mode = drm_mode_duplicate(dev, mode);
@@ -450,6 +542,15 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
if (!crtc->enabled)
return true;
+ if (old_fb && crtc->fb) {
+ depth_changed = (old_fb->depth != crtc->fb->depth);
+ bpp_changed = (old_fb->bits_per_pixel !=
+ crtc->fb->bits_per_pixel);
+ } else {
+ depth_changed = true;
+ bpp_changed = true;
+ }
+
saved_mode = crtc->mode;
saved_x = crtc->x;
saved_y = crtc->y;
@@ -462,9 +563,10 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
crtc->y = y;
if (drm_mode_equal(&saved_mode, &crtc->mode)) {
- if (saved_x != crtc->x || saved_y != crtc->y) {
- crtc_funcs->mode_set_base(crtc, crtc->x, crtc->y,
- old_fb);
+ if (saved_x != crtc->x || saved_y != crtc->y ||
+ depth_changed || bpp_changed) {
+ ret = !crtc_funcs->mode_set_base(crtc, crtc->x, crtc->y,
+ old_fb);
goto done;
}
}
@@ -498,12 +600,16 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
encoder_funcs->prepare(encoder);
}
+ drm_crtc_prepare_encoders(dev);
+
crtc_funcs->prepare(crtc);
/* Set up the DPLL and any encoders state that needs to adjust or depend
* on the DPLL.
*/
- crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y, old_fb);
+ ret = !crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y, old_fb);
+ if (!ret)
+ goto done;
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
@@ -566,10 +672,10 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
struct drm_device *dev;
struct drm_crtc **save_crtcs, *new_crtc;
struct drm_encoder **save_encoders, *new_encoder;
- struct drm_framebuffer *old_fb;
+ struct drm_framebuffer *old_fb = NULL;
bool save_enabled;
- bool changed = false;
- bool flip_or_move = false;
+ bool mode_changed = false;
+ bool fb_changed = false;
struct drm_connector *connector;
int count = 0, ro, fail = 0;
struct drm_crtc_helper_funcs *crtc_funcs;
@@ -597,7 +703,10 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
/* save previous config */
save_enabled = set->crtc->enabled;
- /* this is meant to be num_connector not num_crtc */
+ /*
+ * We do mode_config.num_connectors here since we'll look at the
+ * CRTC and encoder associated with each connector later.
+ */
save_crtcs = kzalloc(dev->mode_config.num_connector *
sizeof(struct drm_crtc *), GFP_KERNEL);
if (!save_crtcs)
@@ -613,21 +722,26 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
/* We should be able to check here if the fb has the same properties
* and then just flip_or_move it */
if (set->crtc->fb != set->fb) {
- /* if we have no fb then its a change not a flip */
- if (set->crtc->fb == NULL)
- changed = true;
+ /* If we have no fb then treat it as a full mode set */
+ if (set->crtc->fb == NULL) {
+ DRM_DEBUG("crtc has no fb, full mode set\n");
+ mode_changed = true;
+ } else if ((set->fb->bits_per_pixel !=
+ set->crtc->fb->bits_per_pixel) ||
+ set->fb->depth != set->crtc->fb->depth)
+ fb_changed = true;
else
- flip_or_move = true;
+ fb_changed = true;
}
if (set->x != set->crtc->x || set->y != set->crtc->y)
- flip_or_move = true;
+ fb_changed = true;
if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) {
- DRM_DEBUG("modes are different\n");
+ DRM_DEBUG("modes are different, full mode set\n");
drm_mode_debug_printmodeline(&set->crtc->mode);
drm_mode_debug_printmodeline(set->mode);
- changed = true;
+ mode_changed = true;
}
/* a) traverse passed in connector list and get encoders for them */
@@ -650,7 +764,8 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
}
if (new_encoder != connector->encoder) {
- changed = true;
+ DRM_DEBUG("encoder changed, full mode switch\n");
+ mode_changed = true;
connector->encoder = new_encoder;
}
}
@@ -676,17 +791,27 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
if (set->connectors[ro] == connector)
new_crtc = set->crtc;
}
+
+ /* Make sure the new CRTC will work with the encoder */
+ if (new_crtc &&
+ !drm_encoder_crtc_ok(connector->encoder, new_crtc)) {
+ ret = -EINVAL;
+ goto fail_set_mode;
+ }
if (new_crtc != connector->encoder->crtc) {
- changed = true;
+ DRM_DEBUG("crtc changed, full mode switch\n");
+ mode_changed = true;
connector->encoder->crtc = new_crtc;
}
+ DRM_DEBUG("setting connector %d crtc to %p\n",
+ connector->base.id, new_crtc);
}
/* mode_set_base is not a required function */
- if (flip_or_move && !crtc_funcs->mode_set_base)
- changed = true;
+ if (fb_changed && !crtc_funcs->mode_set_base)
+ mode_changed = true;
- if (changed) {
+ if (mode_changed) {
old_fb = set->crtc->fb;
set->crtc->fb = set->fb;
set->crtc->enabled = (set->mode != NULL);
@@ -696,6 +821,8 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
if (!drm_crtc_helper_set_mode(set->crtc, set->mode,
set->x, set->y,
old_fb)) {
+ DRM_ERROR("failed to set mode on crtc %p\n",
+ set->crtc);
ret = -EINVAL;
goto fail_set_mode;
}
@@ -705,11 +832,14 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
set->crtc->desired_mode = set->mode;
}
drm_helper_disable_unused_functions(dev);
- } else if (flip_or_move) {
+ } else if (fb_changed) {
old_fb = set->crtc->fb;
if (set->crtc->fb != set->fb)
set->crtc->fb = set->fb;
- crtc_funcs->mode_set_base(set->crtc, set->x, set->y, old_fb);
+ ret = crtc_funcs->mode_set_base(set->crtc,
+ set->x, set->y, old_fb);
+ if (ret != 0)
+ goto fail_set_mode;
}
kfree(save_encoders);
@@ -718,9 +848,14 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
fail_set_mode:
set->crtc->enabled = save_enabled;
+ set->crtc->fb = old_fb;
count = 0;
- list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (!connector->encoder)
+ continue;
+
connector->encoder->crtc = save_crtcs[count++];
+ }
fail_no_encoder:
kfree(save_crtcs);
count = 0;
@@ -764,10 +899,31 @@ bool drm_helper_plugged_event(struct drm_device *dev)
*/
bool drm_helper_initial_config(struct drm_device *dev, bool can_grow)
{
- int ret = false;
+ struct drm_connector *connector;
+ int count = 0;
- drm_helper_plugged_event(dev);
- return ret;
+ count = drm_helper_probe_connector_modes(dev,
+ dev->mode_config.max_width,
+ dev->mode_config.max_height);
+
+ /*
+ * None of the available connectors had any modes, so add some
+ * and try to light them up anyway
+ */
+ if (!count) {
+ DRM_ERROR("connectors have no modes, using standard modes\n");
+ list_for_each_entry(connector,
+ &dev->mode_config.connector_list,
+ head)
+ drm_helper_add_std_modes(dev, connector);
+ }
+
+ drm_setup_crtcs(dev);
+
+ /* alert the driver fb layer */
+ dev->mode_config.funcs->fb_changed(dev);
+
+ return 0;
}
EXPORT_SYMBOL(drm_helper_initial_config);
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 5ff88d95222..14c7a23dc15 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -294,6 +294,7 @@ EXPORT_SYMBOL(drm_init);
*/
static void drm_cleanup(struct drm_device * dev)
{
+ struct drm_map_list *r_list, *list_temp;
DRM_DEBUG("\n");
if (!dev) {
@@ -325,6 +326,9 @@ static void drm_cleanup(struct drm_device * dev)
drm_ht_remove(&dev->map_hash);
drm_ctxbitmap_cleanup(dev);
+ list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head)
+ drm_rmmap(dev, r_list->map);
+
if (drm_core_check_feature(dev, DRIVER_MODESET))
drm_put_minor(&dev->control);
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 0fbb0da342c..a839a28d8ee 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -125,7 +125,7 @@ static bool edid_is_valid(struct edid *edid)
DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version);
goto bad;
}
- if (edid->revision <= 0 || edid->revision > 3) {
+ if (edid->revision > 3) {
DRM_ERROR("EDID has minor version %d, which is not between 0-3\n", edid->revision);
goto bad;
}
@@ -320,10 +320,10 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
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) |
+ mode->vsync_start = mode->vdisplay + ((pt->vsync_offset_hi << 4) |
pt->vsync_offset_lo);
mode->vsync_end = mode->vsync_start +
- ((pt->vsync_pulse_width_hi << 8) |
+ ((pt->vsync_pulse_width_hi << 4) |
pt->vsync_pulse_width_lo);
mode->vtotal = mode->vdisplay + ((pt->vblank_hi << 8) | pt->vblank_lo);
@@ -660,7 +660,7 @@ struct edid *drm_get_edid(struct drm_connector *connector,
edid = (struct edid *)drm_ddc_read(adapter);
if (!edid) {
- dev_warn(&connector->dev->pdev->dev, "%s: no EDID data\n",
+ dev_info(&connector->dev->pdev->dev, "%s: no EDID data\n",
drm_get_connector_name(connector));
return NULL;
}
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c
index b06a5371585..e13cb62bbae 100644
--- a/drivers/gpu/drm/drm_fops.c
+++ b/drivers/gpu/drm/drm_fops.c
@@ -337,14 +337,10 @@ int drm_fasync(int fd, struct file *filp, int on)
{
struct drm_file *priv = filp->private_data;
struct drm_device *dev = priv->minor->dev;
- int retcode;
DRM_DEBUG("fd = %d, device = 0x%lx\n", fd,
(long)old_encode_dev(priv->minor->device));
- retcode = fasync_helper(fd, filp, on, &dev->buf_async);
- if (retcode < 0)
- return retcode;
- return 0;
+ return fasync_helper(fd, filp, on, &dev->buf_async);
}
EXPORT_SYMBOL(drm_fasync);
@@ -457,6 +453,9 @@ int drm_release(struct inode *inode, struct file *filp)
if (dev->driver->driver_features & DRIVER_GEM)
drm_gem_release(dev, file_priv);
+ if (dev->driver->driver_features & DRIVER_MODESET)
+ drm_fb_release(file_priv);
+
mutex_lock(&dev->ctxlist_mutex);
if (!list_empty(&dev->ctxlist)) {
struct drm_ctx_list *pos, *n;
@@ -481,6 +480,7 @@ int drm_release(struct inode *inode, struct file *filp)
mutex_lock(&dev->struct_mutex);
if (file_priv->is_master) {
+ struct drm_master *master = file_priv->master;
struct drm_file *temp;
list_for_each_entry(temp, &dev->filelist, lhead) {
if ((temp->master == file_priv->master) &&
@@ -488,6 +488,19 @@ int drm_release(struct inode *inode, struct file *filp)
temp->authenticated = 0;
}
+ /**
+ * Since the master is disappearing, so is the
+ * possibility to lock.
+ */
+
+ if (master->lock.hw_lock) {
+ if (dev->sigdata.lock == master->lock.hw_lock)
+ dev->sigdata.lock = NULL;
+ master->lock.hw_lock = NULL;
+ master->lock.file_priv = NULL;
+ wake_up_interruptible_all(&master->lock.lock_queue);
+ }
+
if (file_priv->minor->master == file_priv->master) {
/* drop the reference held my the minor */
drm_master_put(&file_priv->minor->master);
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index 9da58145287..88d3368ffdd 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -104,8 +104,8 @@ drm_gem_init(struct drm_device *dev)
if (drm_mm_init(&mm->offset_manager, DRM_FILE_PAGE_OFFSET_START,
DRM_FILE_PAGE_OFFSET_SIZE)) {
- drm_free(mm, sizeof(struct drm_gem_mm), DRM_MEM_MM);
drm_ht_remove(&mm->offset_hash);
+ drm_free(mm, sizeof(struct drm_gem_mm), DRM_MEM_MM);
return -ENOMEM;
}
@@ -136,7 +136,7 @@ drm_gem_object_alloc(struct drm_device *dev, size_t size)
obj = kcalloc(1, sizeof(*obj), GFP_KERNEL);
obj->dev = dev;
- obj->filp = shmem_file_setup("drm mm object", size, 0);
+ obj->filp = shmem_file_setup("drm mm object", size, VM_NORESERVE);
if (IS_ERR(obj->filp)) {
kfree(obj);
return NULL;
@@ -295,35 +295,37 @@ drm_gem_flink_ioctl(struct drm_device *dev, void *data,
return -EBADF;
again:
- if (idr_pre_get(&dev->object_name_idr, GFP_KERNEL) == 0)
- return -ENOMEM;
+ if (idr_pre_get(&dev->object_name_idr, GFP_KERNEL) == 0) {
+ ret = -ENOMEM;
+ goto err;
+ }
spin_lock(&dev->object_name_lock);
- if (obj->name) {
- args->name = obj->name;
+ if (!obj->name) {
+ ret = idr_get_new_above(&dev->object_name_idr, obj, 1,
+ &obj->name);
+ args->name = (uint64_t) obj->name;
spin_unlock(&dev->object_name_lock);
- return 0;
- }
- ret = idr_get_new_above(&dev->object_name_idr, obj, 1,
- &obj->name);
- spin_unlock(&dev->object_name_lock);
- if (ret == -EAGAIN)
- goto again;
- if (ret != 0) {
- mutex_lock(&dev->struct_mutex);
- drm_gem_object_unreference(obj);
- mutex_unlock(&dev->struct_mutex);
- return ret;
- }
+ if (ret == -EAGAIN)
+ goto again;
- /*
- * Leave the reference from the lookup around as the
- * name table now holds one
- */
- args->name = (uint64_t) obj->name;
+ if (ret != 0)
+ goto err;
- return 0;
+ /* Allocate a reference for the name table. */
+ drm_gem_object_reference(obj);
+ } else {
+ args->name = (uint64_t) obj->name;
+ spin_unlock(&dev->object_name_lock);
+ ret = 0;
+ }
+
+err:
+ mutex_lock(&dev->struct_mutex);
+ drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
}
/**
@@ -448,6 +450,7 @@ drm_gem_object_handle_free(struct kref *kref)
spin_lock(&dev->object_name_lock);
if (obj->name) {
idr_remove(&dev->object_name_idr, obj->name);
+ obj->name = 0;
spin_unlock(&dev->object_name_lock);
/*
* The object name held a reference to this object, drop
@@ -460,6 +463,26 @@ drm_gem_object_handle_free(struct kref *kref)
}
EXPORT_SYMBOL(drm_gem_object_handle_free);
+void drm_gem_vm_open(struct vm_area_struct *vma)
+{
+ struct drm_gem_object *obj = vma->vm_private_data;
+
+ drm_gem_object_reference(obj);
+}
+EXPORT_SYMBOL(drm_gem_vm_open);
+
+void drm_gem_vm_close(struct vm_area_struct *vma)
+{
+ struct drm_gem_object *obj = vma->vm_private_data;
+ struct drm_device *dev = obj->dev;
+
+ mutex_lock(&dev->struct_mutex);
+ drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
+}
+EXPORT_SYMBOL(drm_gem_vm_close);
+
+
/**
* drm_gem_mmap - memory map routine for GEM objects
* @filp: DRM file pointer
@@ -521,6 +544,14 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
#endif
vma->vm_page_prot = __pgprot(prot);
+ /* Take a ref for this mapping of the object, so that the fault
+ * handler can dereference the mmap offset's pointer to the object.
+ * This reference is cleaned up by the corresponding vm_close
+ * (which should happen whether the vma was created by this call, or
+ * by a vm_open due to mremap or partial unmap or whatever).
+ */
+ drm_gem_object_reference(obj);
+
vma->vm_file = filp; /* Needed for drm_vm_open() */
drm_vm_open_locked(vma);
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index 724e505873c..93e677a481f 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -106,8 +106,6 @@ void drm_vblank_cleanup(struct drm_device *dev)
drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) * dev->num_crtcs,
DRM_MEM_DRIVER);
- drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * dev->num_crtcs,
- DRM_MEM_DRIVER);
drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) *
dev->num_crtcs, DRM_MEM_DRIVER);
drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) *
@@ -132,7 +130,6 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
setup_timer(&dev->vblank_disable_timer, vblank_disable_fn,
(unsigned long)dev);
spin_lock_init(&dev->vbl_lock);
- atomic_set(&dev->vbl_signal_pending, 0);
dev->num_crtcs = num_crtcs;
dev->vbl_queue = drm_alloc(sizeof(wait_queue_head_t) * num_crtcs,
@@ -140,11 +137,6 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
if (!dev->vbl_queue)
goto err;
- dev->vbl_sigs = drm_alloc(sizeof(struct list_head) * num_crtcs,
- DRM_MEM_DRIVER);
- if (!dev->vbl_sigs)
- goto err;
-
dev->_vblank_count = drm_alloc(sizeof(atomic_t) * num_crtcs,
DRM_MEM_DRIVER);
if (!dev->_vblank_count)
@@ -177,7 +169,6 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
/* Zero per-crtc vblank stuff */
for (i = 0; i < num_crtcs; i++) {
init_waitqueue_head(&dev->vbl_queue[i]);
- INIT_LIST_HEAD(&dev->vbl_sigs[i]);
atomic_set(&dev->_vblank_count[i], 0);
atomic_set(&dev->vblank_refcount[i], 0);
}
@@ -267,7 +258,8 @@ EXPORT_SYMBOL(drm_irq_install);
*/
int drm_irq_uninstall(struct drm_device * dev)
{
- int irq_enabled;
+ unsigned long irqflags;
+ int irq_enabled, i;
if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
return -EINVAL;
@@ -277,6 +269,17 @@ int drm_irq_uninstall(struct drm_device * dev)
dev->irq_enabled = 0;
mutex_unlock(&dev->struct_mutex);
+ /*
+ * Wake up any waiters so they don't hang.
+ */
+ spin_lock_irqsave(&dev->vbl_lock, irqflags);
+ for (i = 0; i < dev->num_crtcs; i++) {
+ DRM_WAKEUP(&dev->vbl_queue[i]);
+ dev->vblank_enabled[i] = 0;
+ dev->last_vblank[i] = dev->driver->get_vblank_counter(dev, i);
+ }
+ spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+
if (!irq_enabled)
return -EINVAL;
@@ -432,6 +435,8 @@ EXPORT_SYMBOL(drm_vblank_get);
*/
void drm_vblank_put(struct drm_device *dev, int crtc)
{
+ BUG_ON (atomic_read (&dev->vblank_refcount[crtc]) == 0);
+
/* Last user schedules interrupt disable */
if (atomic_dec_and_test(&dev->vblank_refcount[crtc]))
mod_timer(&dev->vblank_disable_timer, jiffies + 5*DRM_HZ);
@@ -457,8 +462,9 @@ void drm_vblank_pre_modeset(struct drm_device *dev, int crtc)
* so that interrupts remain enabled in the interim.
*/
if (!dev->vblank_inmodeset[crtc]) {
- dev->vblank_inmodeset[crtc] = 1;
- drm_vblank_get(dev, crtc);
+ dev->vblank_inmodeset[crtc] = 0x1;
+ if (drm_vblank_get(dev, crtc) == 0)
+ dev->vblank_inmodeset[crtc] |= 0x2;
}
}
EXPORT_SYMBOL(drm_vblank_pre_modeset);
@@ -470,9 +476,12 @@ void drm_vblank_post_modeset(struct drm_device *dev, int crtc)
if (dev->vblank_inmodeset[crtc]) {
spin_lock_irqsave(&dev->vbl_lock, irqflags);
dev->vblank_disable_allowed = 1;
- dev->vblank_inmodeset[crtc] = 0;
spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
- drm_vblank_put(dev, crtc);
+
+ if (dev->vblank_inmodeset[crtc] & 0x2)
+ drm_vblank_put(dev, crtc);
+
+ dev->vblank_inmodeset[crtc] = 0;
}
}
EXPORT_SYMBOL(drm_vblank_post_modeset);
@@ -529,15 +538,10 @@ out:
* \param data user argument, pointing to a drm_wait_vblank structure.
* \return zero on success or a negative number on failure.
*
- * Verifies the IRQ is installed.
- *
- * If a signal is requested checks if this task has already scheduled the same signal
- * for the same vblank sequence number - nothing to be done in
- * that case. If the number of tasks waiting for the interrupt exceeds 100 the
- * function fails. Otherwise adds a new entry to drm_device::vbl_sigs for this
- * task.
- *
- * If a signal is not requested, then calls vblank_wait().
+ * This function enables the vblank interrupt on the pipe requested, then
+ * sleeps waiting for the requested sequence number to occur, and drops
+ * the vblank interrupt refcount afterwards. (vblank irq disable follows that
+ * after a timeout with no further vblank waits scheduled).
*/
int drm_wait_vblank(struct drm_device *dev, void *data,
struct drm_file *file_priv)
@@ -549,6 +553,9 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
if ((!dev->pdev->irq) || (!dev->irq_enabled))
return -EINVAL;
+ if (vblwait->request.type & _DRM_VBLANK_SIGNAL)
+ return -EINVAL;
+
if (vblwait->request.type &
~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK)) {
DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n",
@@ -586,88 +593,26 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
vblwait->request.sequence = seq + 1;
}
- if (flags & _DRM_VBLANK_SIGNAL) {
- unsigned long irqflags;
- struct list_head *vbl_sigs = &dev->vbl_sigs[crtc];
- struct drm_vbl_sig *vbl_sig;
-
- spin_lock_irqsave(&dev->vbl_lock, irqflags);
-
- /* Check if this task has already scheduled the same signal
- * for the same vblank sequence number; nothing to be done in
- * that case
- */
- list_for_each_entry(vbl_sig, vbl_sigs, head) {
- if (vbl_sig->sequence == vblwait->request.sequence
- && vbl_sig->info.si_signo ==
- vblwait->request.signal
- && vbl_sig->task == current) {
- spin_unlock_irqrestore(&dev->vbl_lock,
- irqflags);
- vblwait->reply.sequence = seq;
- goto done;
- }
- }
-
- if (atomic_read(&dev->vbl_signal_pending) >= 100) {
- spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
- ret = -EBUSY;
- goto done;
- }
-
- spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
-
- vbl_sig = drm_calloc(1, sizeof(struct drm_vbl_sig),
- DRM_MEM_DRIVER);
- if (!vbl_sig) {
- ret = -ENOMEM;
- goto done;
- }
-
- /* Get a refcount on the vblank, which will be released by
- * drm_vbl_send_signals().
- */
- ret = drm_vblank_get(dev, crtc);
- if (ret) {
- drm_free(vbl_sig, sizeof(struct drm_vbl_sig),
- DRM_MEM_DRIVER);
- goto done;
- }
-
- atomic_inc(&dev->vbl_signal_pending);
-
- vbl_sig->sequence = vblwait->request.sequence;
- vbl_sig->info.si_signo = vblwait->request.signal;
- vbl_sig->task = current;
+ DRM_DEBUG("waiting on vblank count %d, crtc %d\n",
+ vblwait->request.sequence, crtc);
+ dev->last_vblank_wait[crtc] = vblwait->request.sequence;
+ DRM_WAIT_ON(ret, dev->vbl_queue[crtc], 3 * DRM_HZ,
+ (((drm_vblank_count(dev, crtc) -
+ vblwait->request.sequence) <= (1 << 23)) ||
+ !dev->irq_enabled));
- spin_lock_irqsave(&dev->vbl_lock, irqflags);
-
- list_add_tail(&vbl_sig->head, vbl_sigs);
+ if (ret != -EINTR) {
+ struct timeval now;
- spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+ do_gettimeofday(&now);
- vblwait->reply.sequence = seq;
+ vblwait->reply.tval_sec = now.tv_sec;
+ vblwait->reply.tval_usec = now.tv_usec;
+ vblwait->reply.sequence = drm_vblank_count(dev, crtc);
+ DRM_DEBUG("returning %d to client\n",
+ vblwait->reply.sequence);
} else {
- DRM_DEBUG("waiting on vblank count %d, crtc %d\n",
- vblwait->request.sequence, crtc);
- dev->last_vblank_wait[crtc] = vblwait->request.sequence;
- DRM_WAIT_ON(ret, dev->vbl_queue[crtc], 3 * DRM_HZ,
- ((drm_vblank_count(dev, crtc)
- - vblwait->request.sequence) <= (1 << 23)));
-
- if (ret != -EINTR) {
- struct timeval now;
-
- do_gettimeofday(&now);
-
- vblwait->reply.tval_sec = now.tv_sec;
- vblwait->reply.tval_usec = now.tv_usec;
- vblwait->reply.sequence = drm_vblank_count(dev, crtc);
- DRM_DEBUG("returning %d to client\n",
- vblwait->reply.sequence);
- } else {
- DRM_DEBUG("vblank wait interrupted by signal\n");
- }
+ DRM_DEBUG("vblank wait interrupted by signal\n");
}
done:
@@ -676,46 +621,6 @@ done:
}
/**
- * Send the VBLANK signals.
- *
- * \param dev DRM device.
- * \param crtc CRTC where the vblank event occurred
- *
- * Sends a signal for each task in drm_device::vbl_sigs and empties the list.
- *
- * If a signal is not requested, then calls vblank_wait().
- */
-static void drm_vbl_send_signals(struct drm_device *dev, int crtc)
-{
- struct drm_vbl_sig *vbl_sig, *tmp;
- struct list_head *vbl_sigs;
- unsigned int vbl_seq;
- unsigned long flags;
-
- spin_lock_irqsave(&dev->vbl_lock, flags);
-
- vbl_sigs = &dev->vbl_sigs[crtc];
- vbl_seq = drm_vblank_count(dev, crtc);
-
- list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) {
- if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) {
- vbl_sig->info.si_code = vbl_seq;
- send_sig_info(vbl_sig->info.si_signo,
- &vbl_sig->info, vbl_sig->task);
-
- list_del(&vbl_sig->head);
-
- drm_free(vbl_sig, sizeof(*vbl_sig),
- DRM_MEM_DRIVER);
- atomic_dec(&dev->vbl_signal_pending);
- drm_vblank_put(dev, crtc);
- }
- }
-
- spin_unlock_irqrestore(&dev->vbl_lock, flags);
-}
-
-/**
* drm_handle_vblank - handle a vblank event
* @dev: DRM device
* @crtc: where this event occurred
@@ -727,6 +632,5 @@ void drm_handle_vblank(struct drm_device *dev, int crtc)
{
atomic_inc(&dev->_vblank_count[crtc]);
DRM_WAKEUP(&dev->vbl_queue[crtc]);
- drm_vbl_send_signals(dev, crtc);
}
EXPORT_SYMBOL(drm_handle_vblank);
diff --git a/drivers/gpu/drm/drm_lock.c b/drivers/gpu/drm/drm_lock.c
index 46e7b28f070..e2f70a516c3 100644
--- a/drivers/gpu/drm/drm_lock.c
+++ b/drivers/gpu/drm/drm_lock.c
@@ -80,6 +80,7 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
__set_current_state(TASK_INTERRUPTIBLE);
if (!master->lock.hw_lock) {
/* Device has been unregistered */
+ send_sig(SIGTERM, current, 0);
ret = -EINTR;
break;
}
@@ -93,7 +94,7 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
/* Contention */
schedule();
if (signal_pending(current)) {
- ret = -ERESTARTSYS;
+ ret = -EINTR;
break;
}
}
diff --git a/drivers/gpu/drm/drm_memory.c b/drivers/gpu/drm/drm_memory.c
index 803bc9e7ce3..bcc869bc409 100644
--- a/drivers/gpu/drm/drm_memory.c
+++ b/drivers/gpu/drm/drm_memory.c
@@ -171,9 +171,14 @@ EXPORT_SYMBOL(drm_core_ioremap);
void drm_core_ioremap_wc(struct drm_map *map, struct drm_device *dev)
{
- map->handle = ioremap_wc(map->offset, map->size);
+ if (drm_core_has_AGP(dev) &&
+ dev->agp && dev->agp->cant_use_aperture && map->type == _DRM_AGP)
+ map->handle = agp_remap(map->offset, map->size, dev);
+ else
+ map->handle = ioremap_wc(map->offset, map->size);
}
EXPORT_SYMBOL(drm_core_ioremap_wc);
+
void drm_core_ioremapfree(struct drm_map *map, struct drm_device *dev)
{
if (!map->handle || !map->size)
diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c
index 5ca132afa4f..7c8b15b22bf 100644
--- a/drivers/gpu/drm/drm_stub.c
+++ b/drivers/gpu/drm/drm_stub.c
@@ -118,12 +118,20 @@ static void drm_master_destroy(struct kref *kref)
struct drm_master *master = container_of(kref, struct drm_master, refcount);
struct drm_magic_entry *pt, *next;
struct drm_device *dev = master->minor->dev;
+ struct drm_map_list *r_list, *list_temp;
list_del(&master->head);
if (dev->driver->master_destroy)
dev->driver->master_destroy(dev, master);
+ list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head) {
+ if (r_list->master == master) {
+ drm_rmmap_locked(dev, r_list->map);
+ r_list = NULL;
+ }
+ }
+
if (master->unique) {
drm_free(master->unique, master->unique_size, DRM_MEM_DRIVER);
master->unique = NULL;
@@ -138,14 +146,6 @@ static void drm_master_destroy(struct kref *kref)
drm_ht_remove(&master->magiclist);
- if (master->lock.hw_lock) {
- if (dev->sigdata.lock == master->lock.hw_lock)
- dev->sigdata.lock = NULL;
- master->lock.hw_lock = NULL;
- master->lock.file_priv = NULL;
- wake_up_interruptible(&master->lock.lock_queue);
- }
-
drm_free(master, sizeof(*master), DRM_MEM_DRIVER);
}
@@ -168,7 +168,7 @@ int drm_setmaster_ioctl(struct drm_device *dev, void *data,
file_priv->minor->master != file_priv->master) {
mutex_lock(&dev->struct_mutex);
file_priv->minor->master = drm_master_get(file_priv->master);
- mutex_lock(&dev->struct_mutex);
+ mutex_unlock(&dev->struct_mutex);
}
return 0;
diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c
index 5aa6780652a..186d08159d4 100644
--- a/drivers/gpu/drm/drm_sysfs.c
+++ b/drivers/gpu/drm/drm_sysfs.c
@@ -359,8 +359,8 @@ int drm_sysfs_connector_add(struct drm_connector *connector)
DRM_DEBUG("adding \"%s\" to sysfs\n",
drm_get_connector_name(connector));
- snprintf(connector->kdev.bus_id, BUS_ID_SIZE, "card%d-%s",
- dev->primary->index, drm_get_connector_name(connector));
+ dev_set_name(&connector->kdev, "card%d-%s",
+ dev->primary->index, drm_get_connector_name(connector));
ret = device_register(&connector->kdev);
if (ret) {
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 62a4bf7b49d..6d21b9e48b8 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -177,6 +177,14 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init)
drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
+ master_priv->sarea = drm_getsarea(dev);
+ if (master_priv->sarea) {
+ master_priv->sarea_priv = (drm_i915_sarea_t *)
+ ((u8 *)master_priv->sarea->handle + init->sarea_priv_offset);
+ } else {
+ DRM_DEBUG("sarea not found assuming DRI2 userspace\n");
+ }
+
if (init->ring_size != 0) {
if (dev_priv->ring.ring_obj != NULL) {
i915_dma_cleanup(dev);
@@ -194,7 +202,7 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init)
dev_priv->ring.map.flags = 0;
dev_priv->ring.map.mtrr = 0;
- drm_core_ioremap(&dev_priv->ring.map, dev);
+ drm_core_ioremap_wc(&dev_priv->ring.map, dev);
if (dev_priv->ring.map.handle == NULL) {
i915_dma_cleanup(dev);
@@ -723,8 +731,11 @@ static int i915_getparam(struct drm_device *dev, void *data,
case I915_PARAM_HAS_GEM:
value = dev_priv->has_gem;
break;
+ case I915_PARAM_NUM_FENCES_AVAIL:
+ value = dev_priv->num_fence_regs - dev_priv->fence_reg_start;
+ break;
default:
- DRM_ERROR("Unknown parameter %d\n", param->param);
+ DRM_DEBUG("Unknown parameter %d\n", param->param);
return -EINVAL;
}
@@ -756,8 +767,15 @@ static int i915_setparam(struct drm_device *dev, void *data,
case I915_SETPARAM_ALLOW_BATCHBUFFER:
dev_priv->allow_batchbuffer = param->value;
break;
+ case I915_SETPARAM_NUM_USED_FENCES:
+ if (param->value > dev_priv->num_fence_regs ||
+ param->value < 0)
+ return -EINVAL;
+ /* Userspace can use first N regs */
+ dev_priv->fence_reg_start = param->value;
+ break;
default:
- DRM_ERROR("unknown parameter %d\n", param->param);
+ DRM_DEBUG("unknown parameter %d\n", param->param);
return -EINVAL;
}
@@ -793,7 +811,7 @@ static int i915_set_status_page(struct drm_device *dev, void *data,
dev_priv->hws_map.flags = 0;
dev_priv->hws_map.mtrr = 0;
- drm_core_ioremap(&dev_priv->hws_map, dev);
+ drm_core_ioremap_wc(&dev_priv->hws_map, dev);
if (dev_priv->hws_map.handle == NULL) {
i915_dma_cleanup(dev);
dev_priv->status_gfx_addr = 0;
@@ -936,13 +954,14 @@ static int i915_load_modeset_init(struct drm_device *dev)
dev->mode_config.fb_base = drm_get_resource_start(dev, fb_bar) &
0xff000000;
- DRM_DEBUG("*** fb base 0x%08lx\n", dev->mode_config.fb_base);
-
- if (IS_MOBILE(dev) || (IS_I9XX(dev) && !IS_I965G(dev) && !IS_G33(dev)))
+ if (IS_MOBILE(dev) || IS_I9XX(dev))
dev_priv->cursor_needs_physical = true;
else
dev_priv->cursor_needs_physical = false;
+ if (IS_I965G(dev) || IS_G33(dev))
+ dev_priv->cursor_needs_physical = false;
+
ret = i915_probe_agp(dev, &agp_size, &prealloc_size);
if (ret)
goto kfree_devname;
@@ -957,10 +976,6 @@ static int i915_load_modeset_init(struct drm_device *dev)
if (ret)
goto kfree_devname;
- dev_priv->mm.gtt_mapping =
- io_mapping_create_wc(dev->agp->base,
- dev->agp->agp_info.aper_size * 1024*1024);
-
/* Allow hardware batchbuffers unless told otherwise.
*/
dev_priv->allow_batchbuffer = 1;
@@ -1072,6 +1087,28 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
goto free_priv;
}
+ dev_priv->mm.gtt_mapping =
+ io_mapping_create_wc(dev->agp->base,
+ dev->agp->agp_info.aper_size * 1024*1024);
+ if (dev_priv->mm.gtt_mapping == NULL) {
+ ret = -EIO;
+ goto out_rmmap;
+ }
+
+ /* Set up a WC MTRR for non-PAT systems. This is more common than
+ * one would think, because the kernel disables PAT on first
+ * generation Core chips because WC PAT gets overridden by a UC
+ * MTRR if present. Even if a UC MTRR isn't present.
+ */
+ dev_priv->mm.gtt_mtrr = mtrr_add(dev->agp->base,
+ dev->agp->agp_info.aper_size *
+ 1024 * 1024,
+ MTRR_TYPE_WRCOMB, 1);
+ if (dev_priv->mm.gtt_mtrr < 0) {
+ DRM_INFO("MTRR allocation failed. Graphics "
+ "performance may suffer.\n");
+ }
+
#ifdef CONFIG_HIGHMEM64G
/* don't enable GEM on PAE - needs agp + set_memory_* interface fixes */
dev_priv->has_gem = 0;
@@ -1080,13 +1117,17 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
dev_priv->has_gem = 1;
#endif
+ dev->driver->get_vblank_counter = i915_get_vblank_counter;
+ if (IS_GM45(dev))
+ dev->driver->get_vblank_counter = gm45_get_vblank_counter;
+
i915_gem_load(dev);
/* Init HWS */
if (!I915_NEED_GFX_HWS(dev)) {
ret = i915_init_phys_hws(dev);
if (ret != 0)
- goto out_rmmap;
+ goto out_iomapfree;
}
/* On the 945G/GM, the chipset reports the MSI capability on the
@@ -1125,6 +1166,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
return 0;
+out_iomapfree:
+ io_mapping_free(dev_priv->mm.gtt_mapping);
out_rmmap:
iounmap(dev_priv->regs);
free_priv:
@@ -1136,8 +1179,14 @@ int i915_driver_unload(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
+ io_mapping_free(dev_priv->mm.gtt_mapping);
+ if (dev_priv->mm.gtt_mtrr >= 0) {
+ mtrr_del(dev_priv->mm.gtt_mtrr, dev->agp->base,
+ dev->agp->agp_info.aper_size * 1024 * 1024);
+ dev_priv->mm.gtt_mtrr = -1;
+ }
+
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
- io_mapping_free(dev_priv->mm.gtt_mapping);
drm_irq_uninstall(dev);
}
@@ -1152,6 +1201,8 @@ int i915_driver_unload(struct drm_device *dev)
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
intel_modeset_cleanup(dev);
+ i915_gem_free_all_phys_object(dev);
+
mutex_lock(&dev->struct_mutex);
i915_gem_cleanup_ringbuffer(dev);
mutex_unlock(&dev->struct_mutex);
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index f8b3df0926c..b293ef0bae7 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -27,6 +27,7 @@
*
*/
+#include <linux/device.h>
#include "drmP.h"
#include "drm.h"
#include "i915_drm.h"
@@ -66,6 +67,14 @@ static int i915_suspend(struct drm_device *dev, pm_message_t state)
i915_save_state(dev);
+ /* If KMS is active, we do the leavevt stuff here */
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ if (i915_gem_idle(dev))
+ dev_err(&dev->pdev->dev,
+ "GEM idle failed, resume may fail\n");
+ drm_irq_uninstall(dev);
+ }
+
intel_opregion_free(dev);
if (state.event == PM_EVENT_SUSPEND) {
@@ -79,6 +88,9 @@ static int i915_suspend(struct drm_device *dev, pm_message_t state)
static int i915_resume(struct drm_device *dev)
{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret = 0;
+
pci_set_power_state(dev->pdev, PCI_D0);
pci_restore_state(dev->pdev);
if (pci_enable_device(dev->pdev))
@@ -89,11 +101,26 @@ static int i915_resume(struct drm_device *dev)
intel_opregion_init(dev);
- return 0;
+ /* KMS EnterVT equivalent */
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ mutex_lock(&dev->struct_mutex);
+ dev_priv->mm.suspended = 0;
+
+ ret = i915_gem_init_ringbuffer(dev);
+ if (ret != 0)
+ ret = -1;
+ mutex_unlock(&dev->struct_mutex);
+
+ drm_irq_install(dev);
+ }
+
+ return ret;
}
static struct vm_operations_struct i915_gem_vm_ops = {
.fault = i915_gem_fault,
+ .open = drm_gem_vm_open,
+ .close = drm_gem_vm_close,
};
static struct drm_driver driver = {
@@ -112,7 +139,6 @@ static struct drm_driver driver = {
.suspend = i915_suspend,
.resume = i915_resume,
.device_is_agp = i915_driver_device_is_agp,
- .get_vblank_counter = i915_get_vblank_counter,
.enable_vblank = i915_enable_vblank,
.disable_vblank = i915_disable_vblank,
.irq_preinstall = i915_driver_irq_preinstall,
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 563de18063f..d6cc9861e0a 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -72,6 +72,18 @@ enum pipe {
#define WATCH_INACTIVE 0
#define WATCH_PWRITE 0
+#define I915_GEM_PHYS_CURSOR_0 1
+#define I915_GEM_PHYS_CURSOR_1 2
+#define I915_GEM_PHYS_OVERLAY_REGS 3
+#define I915_MAX_PHYS_OBJECT (I915_GEM_PHYS_OVERLAY_REGS)
+
+struct drm_i915_gem_phys_object {
+ int id;
+ struct page **page_list;
+ drm_dma_handle_t *handle;
+ struct drm_gem_object *cur_obj;
+};
+
typedef struct _drm_i915_ring_buffer {
int tail_mask;
unsigned long Size;
@@ -172,6 +184,8 @@ typedef struct drm_i915_private {
unsigned int lvds_dither:1;
unsigned int lvds_vbt:1;
unsigned int int_crt_support:1;
+ unsigned int lvds_use_ssc:1;
+ int lvds_ssc_freq;
struct drm_i915_fence_reg fence_regs[16]; /* assume 965 */
int fence_reg_start; /* 4 if userland hasn't ioctl'd us yet */
@@ -265,13 +279,13 @@ typedef struct drm_i915_private {
u8 saveAR_INDEX;
u8 saveAR[21];
u8 saveDACMASK;
- u8 saveDACDATA[256*3]; /* 256 3-byte colors */
u8 saveCR[37];
struct {
struct drm_mm gtt_space;
struct io_mapping *gtt_mapping;
+ int gtt_mtrr;
/**
* List of objects currently involved in rendering from the
@@ -358,6 +372,9 @@ typedef struct drm_i915_private {
uint32_t bit_6_swizzle_x;
/** Bit 6 swizzling required for Y tiling */
uint32_t bit_6_swizzle_y;
+
+ /* storage for physical objects */
+ struct drm_i915_gem_phys_object *phys_objs[I915_MAX_PHYS_OBJECT];
} mm;
} drm_i915_private_t;
@@ -436,6 +453,15 @@ struct drm_i915_gem_object {
/** User space pin count and filp owning the pin */
uint32_t user_pin_count;
struct drm_file *pin_filp;
+
+ /** for phy allocated objects */
+ struct drm_i915_gem_phys_object *phys_obj;
+
+ /**
+ * Used for checking the object doesn't appear more than once
+ * in an execbuffer object list.
+ */
+ int in_execbuffer;
};
/**
@@ -516,6 +542,7 @@ extern int i915_vblank_pipe_get(struct drm_device *dev, void *data,
extern int i915_enable_vblank(struct drm_device *dev, int crtc);
extern void i915_disable_vblank(struct drm_device *dev, int crtc);
extern u32 i915_get_vblank_counter(struct drm_device *dev, int crtc);
+extern u32 gm45_get_vblank_counter(struct drm_device *dev, int crtc);
extern int i915_vblank_swap(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern void i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask);
@@ -583,6 +610,7 @@ int i915_gem_init_object(struct drm_gem_object *obj);
void i915_gem_free_object(struct drm_gem_object *obj);
int i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment);
void i915_gem_object_unpin(struct drm_gem_object *obj);
+int i915_gem_object_unbind(struct drm_gem_object *obj);
void i915_gem_lastclose(struct drm_device *dev);
uint32_t i915_get_gem_seqno(struct drm_device *dev);
void i915_gem_retire_requests(struct drm_device *dev);
@@ -595,9 +623,15 @@ int i915_gem_init_ringbuffer(struct drm_device *dev);
void i915_gem_cleanup_ringbuffer(struct drm_device *dev);
int i915_gem_do_init(struct drm_device *dev, unsigned long start,
unsigned long end);
+int i915_gem_idle(struct drm_device *dev);
int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
int i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj,
int write);
+int i915_gem_attach_phys_object(struct drm_device *dev,
+ struct drm_gem_object *obj, int id);
+void i915_gem_detach_phys_object(struct drm_device *dev,
+ struct drm_gem_object *obj);
+void i915_gem_free_all_phys_object(struct drm_device *dev);
/* i915_gem_tiling.c */
void i915_gem_detect_bit_6_swizzle(struct drm_device *dev);
@@ -761,6 +795,11 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
IS_I945GM(dev) || IS_I965GM(dev) || IS_GM45(dev))
#define I915_NEED_GFX_HWS(dev) (IS_G33(dev) || IS_GM45(dev) || IS_G4X(dev))
+/* With the 945 and later, Y tiling got adjusted so that it was 32 128-byte
+ * rows, which changed the alignment requirements and fence programming.
+ */
+#define HAS_128_BYTE_Y_TILING(dev) (IS_I9XX(dev) && !(IS_I915G(dev) || \
+ IS_I915GM(dev)))
#define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev))
#define PRIMARY_RINGBUFFER_SIZE (128*1024)
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 1384d668655..37427e4016c 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -34,10 +34,6 @@
#define I915_GEM_GPU_DOMAINS (~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT))
-static void
-i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj,
- uint32_t read_domains,
- uint32_t write_domain);
static void i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj);
static void i915_gem_object_flush_gtt_write_domain(struct drm_gem_object *obj);
static void i915_gem_object_flush_cpu_write_domain(struct drm_gem_object *obj);
@@ -52,9 +48,12 @@ static void i915_gem_object_free_page_list(struct drm_gem_object *obj);
static int i915_gem_object_wait_rendering(struct drm_gem_object *obj);
static int i915_gem_object_bind_to_gtt(struct drm_gem_object *obj,
unsigned alignment);
-static void i915_gem_object_get_fence_reg(struct drm_gem_object *obj);
+static int i915_gem_object_get_fence_reg(struct drm_gem_object *obj, bool write);
static void i915_gem_clear_fence_reg(struct drm_gem_object *obj);
static int i915_gem_evict_something(struct drm_device *dev);
+static int i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *obj,
+ struct drm_i915_gem_pwrite *args,
+ struct drm_file *file_priv);
int i915_gem_do_init(struct drm_device *dev, unsigned long start,
unsigned long end)
@@ -386,8 +385,10 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
* pread/pwrite currently are reading and writing from the CPU
* perspective, requiring manual detiling by the client.
*/
- if (obj_priv->tiling_mode == I915_TILING_NONE &&
- dev->gtt_total != 0)
+ if (obj_priv->phys_obj)
+ ret = i915_gem_phys_pwrite(dev, obj, args, file_priv);
+ else if (obj_priv->tiling_mode == I915_TILING_NONE &&
+ dev->gtt_total != 0)
ret = i915_gem_gtt_pwrite(dev, obj, args, file_priv);
else
ret = i915_gem_shmem_pwrite(dev, obj, args, file_priv);
@@ -562,6 +563,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
pgoff_t page_offset;
unsigned long pfn;
int ret = 0;
+ bool write = !!(vmf->flags & FAULT_FLAG_WRITE);
/* We don't use vmf->pgoff since that has the fake offset */
page_offset = ((unsigned long)vmf->virtual_address - vma->vm_start) >>
@@ -580,8 +582,13 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
/* Need a new fence register? */
if (obj_priv->fence_reg == I915_FENCE_REG_NONE &&
- obj_priv->tiling_mode != I915_TILING_NONE)
- i915_gem_object_get_fence_reg(obj);
+ obj_priv->tiling_mode != I915_TILING_NONE) {
+ ret = i915_gem_object_get_fence_reg(obj, write);
+ if (ret) {
+ mutex_unlock(&dev->struct_mutex);
+ return VM_FAULT_SIGBUS;
+ }
+ }
pfn = ((dev->agp->base + obj_priv->gtt_offset) >> PAGE_SHIFT) +
page_offset;
@@ -596,8 +603,6 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
case -EAGAIN:
return VM_FAULT_OOM;
case -EFAULT:
- case -EBUSY:
- DRM_ERROR("can't insert pfn?? fault or busy...\n");
return VM_FAULT_SIGBUS;
default:
return VM_FAULT_NOPAGE;
@@ -673,6 +678,30 @@ out_free_list:
return ret;
}
+static void
+i915_gem_free_mmap_offset(struct drm_gem_object *obj)
+{
+ struct drm_device *dev = obj->dev;
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ struct drm_gem_mm *mm = dev->mm_private;
+ struct drm_map_list *list;
+
+ list = &obj->map_list;
+ drm_ht_remove_item(&mm->offset_hash, &list->hash);
+
+ if (list->file_offset_node) {
+ drm_mm_put_block(list->file_offset_node);
+ list->file_offset_node = NULL;
+ }
+
+ if (list->map) {
+ drm_free(list->map, sizeof(struct drm_map), DRM_MEM_DRIVER);
+ list->map = NULL;
+ }
+
+ obj_priv->mmap_offset = 0;
+}
+
/**
* i915_gem_get_gtt_alignment - return required GTT alignment for an object
* @obj: object to check
@@ -747,8 +776,11 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data,
if (!obj_priv->mmap_offset) {
ret = i915_gem_create_mmap_offset(obj);
- if (ret)
+ if (ret) {
+ drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
return ret;
+ }
}
args->offset = obj_priv->mmap_offset;
@@ -1019,6 +1051,9 @@ i915_gem_retire_requests(struct drm_device *dev)
drm_i915_private_t *dev_priv = dev->dev_private;
uint32_t seqno;
+ if (!dev_priv->hw_status_page)
+ return;
+
seqno = i915_get_gem_seqno(dev);
while (!list_empty(&dev_priv->mm.request_list)) {
@@ -1206,7 +1241,7 @@ i915_gem_object_wait_rendering(struct drm_gem_object *obj)
/**
* Unbinds an object from the GTT aperture.
*/
-static int
+int
i915_gem_object_unbind(struct drm_gem_object *obj)
{
struct drm_device *dev = obj->dev;
@@ -1440,21 +1475,26 @@ static void i915_write_fence_reg(struct drm_i915_fence_reg *reg)
drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj_priv = obj->driver_private;
int regnum = obj_priv->fence_reg;
- uint32_t val;
+ int tile_width;
+ uint32_t fence_reg, val;
uint32_t pitch_val;
if ((obj_priv->gtt_offset & ~I915_FENCE_START_MASK) ||
(obj_priv->gtt_offset & (obj->size - 1))) {
- WARN(1, "%s: object not 1M or size aligned\n", __func__);
+ WARN(1, "%s: object 0x%08x not 1M or size (0x%zx) aligned\n",
+ __func__, obj_priv->gtt_offset, obj->size);
return;
}
- if (obj_priv->tiling_mode == I915_TILING_Y && (IS_I945G(dev) ||
- IS_I945GM(dev) ||
- IS_G33(dev)))
- pitch_val = (obj_priv->stride / 128) - 1;
+ if (obj_priv->tiling_mode == I915_TILING_Y &&
+ HAS_128_BYTE_Y_TILING(dev))
+ tile_width = 128;
else
- pitch_val = (obj_priv->stride / 512) - 1;
+ tile_width = 512;
+
+ /* Note: pitch better be a power of two tile widths */
+ pitch_val = obj_priv->stride / tile_width;
+ pitch_val = ffs(pitch_val) - 1;
val = obj_priv->gtt_offset;
if (obj_priv->tiling_mode == I915_TILING_Y)
@@ -1463,7 +1503,11 @@ static void i915_write_fence_reg(struct drm_i915_fence_reg *reg)
val |= pitch_val << I830_FENCE_PITCH_SHIFT;
val |= I830_FENCE_REG_VALID;
- I915_WRITE(FENCE_REG_830_0 + (regnum * 4), val);
+ if (regnum < 8)
+ fence_reg = FENCE_REG_830_0 + (regnum * 4);
+ else
+ fence_reg = FENCE_REG_945_8 + ((regnum - 8) * 4);
+ I915_WRITE(fence_reg, val);
}
static void i830_write_fence_reg(struct drm_i915_fence_reg *reg)
@@ -1478,7 +1522,8 @@ static void i830_write_fence_reg(struct drm_i915_fence_reg *reg)
if ((obj_priv->gtt_offset & ~I915_FENCE_START_MASK) ||
(obj_priv->gtt_offset & (obj->size - 1))) {
- WARN(1, "%s: object not 1M or size aligned\n", __func__);
+ WARN(1, "%s: object 0x%08x not 1M or size aligned\n",
+ __func__, obj_priv->gtt_offset);
return;
}
@@ -1498,6 +1543,7 @@ static void i830_write_fence_reg(struct drm_i915_fence_reg *reg)
/**
* i915_gem_object_get_fence_reg - set up a fence reg for an object
* @obj: object to map through a fence reg
+ * @write: object is about to be written
*
* When mapping objects through the GTT, userspace wants to be able to write
* to them without having to worry about swizzling if the object is tiled.
@@ -1508,49 +1554,77 @@ static void i830_write_fence_reg(struct drm_i915_fence_reg *reg)
* It then sets up the reg based on the object's properties: address, pitch
* and tiling format.
*/
-static void
-i915_gem_object_get_fence_reg(struct drm_gem_object *obj)
+static int
+i915_gem_object_get_fence_reg(struct drm_gem_object *obj, bool write)
{
struct drm_device *dev = obj->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj_priv = obj->driver_private;
struct drm_i915_fence_reg *reg = NULL;
- int i, ret;
+ struct drm_i915_gem_object *old_obj_priv = NULL;
+ int i, ret, avail;
switch (obj_priv->tiling_mode) {
case I915_TILING_NONE:
WARN(1, "allocating a fence for non-tiled object?\n");
break;
case I915_TILING_X:
- WARN(obj_priv->stride & (512 - 1),
- "object is X tiled but has non-512B pitch\n");
+ if (!obj_priv->stride)
+ return -EINVAL;
+ WARN((obj_priv->stride & (512 - 1)),
+ "object 0x%08x is X tiled but has non-512B pitch\n",
+ obj_priv->gtt_offset);
break;
case I915_TILING_Y:
- WARN(obj_priv->stride & (128 - 1),
- "object is Y tiled but has non-128B pitch\n");
+ if (!obj_priv->stride)
+ return -EINVAL;
+ WARN((obj_priv->stride & (128 - 1)),
+ "object 0x%08x is Y tiled but has non-128B pitch\n",
+ obj_priv->gtt_offset);
break;
}
/* First try to find a free reg */
+try_again:
+ avail = 0;
for (i = dev_priv->fence_reg_start; i < dev_priv->num_fence_regs; i++) {
reg = &dev_priv->fence_regs[i];
if (!reg->obj)
break;
+
+ old_obj_priv = reg->obj->driver_private;
+ if (!old_obj_priv->pin_count)
+ avail++;
}
/* None available, try to steal one or wait for a user to finish */
if (i == dev_priv->num_fence_regs) {
- struct drm_i915_gem_object *old_obj_priv = NULL;
+ uint32_t seqno = dev_priv->mm.next_gem_seqno;
loff_t offset;
-try_again:
- /* Could try to use LRU here instead... */
+ if (avail == 0)
+ return -ENOMEM;
+
for (i = dev_priv->fence_reg_start;
i < dev_priv->num_fence_regs; i++) {
+ uint32_t this_seqno;
+
reg = &dev_priv->fence_regs[i];
old_obj_priv = reg->obj->driver_private;
- if (!old_obj_priv->pin_count)
+
+ if (old_obj_priv->pin_count)
+ continue;
+
+ /* i915 uses fences for GPU access to tiled buffers */
+ if (IS_I965G(dev) || !old_obj_priv->active)
break;
+
+ /* find the seqno of the first available fence */
+ this_seqno = old_obj_priv->last_rendering_seqno;
+ if (this_seqno != 0 &&
+ reg->obj->write_domain == 0 &&
+ i915_seqno_passed(seqno, this_seqno))
+ seqno = this_seqno;
}
/*
@@ -1558,14 +1632,25 @@ try_again:
* objects to finish before trying again.
*/
if (i == dev_priv->num_fence_regs) {
- ret = i915_gem_object_wait_rendering(reg->obj);
- if (ret) {
- WARN(ret, "wait_rendering failed: %d\n", ret);
- return;
+ if (seqno == dev_priv->mm.next_gem_seqno) {
+ i915_gem_flush(dev,
+ I915_GEM_GPU_DOMAINS,
+ I915_GEM_GPU_DOMAINS);
+ seqno = i915_add_request(dev,
+ I915_GEM_GPU_DOMAINS);
+ if (seqno == 0)
+ return -ENOMEM;
}
+
+ ret = i915_wait_request(dev, seqno);
+ if (ret)
+ return ret;
goto try_again;
}
+ BUG_ON(old_obj_priv->active ||
+ (reg->obj->write_domain & I915_GEM_GPU_DOMAINS));
+
/*
* Zap this virtual mapping so we can set up a fence again
* for this object next time we need it.
@@ -1586,6 +1671,8 @@ try_again:
i915_write_fence_reg(reg);
else
i830_write_fence_reg(reg);
+
+ return 0;
}
/**
@@ -1604,8 +1691,17 @@ i915_gem_clear_fence_reg(struct drm_gem_object *obj)
if (IS_I965G(dev))
I915_WRITE64(FENCE_REG_965_0 + (obj_priv->fence_reg * 8), 0);
- else
- I915_WRITE(FENCE_REG_830_0 + (obj_priv->fence_reg * 4), 0);
+ else {
+ uint32_t fence_reg;
+
+ if (obj_priv->fence_reg < 8)
+ fence_reg = FENCE_REG_830_0 + obj_priv->fence_reg * 4;
+ else
+ fence_reg = FENCE_REG_945_8 + (obj_priv->fence_reg -
+ 8) * 4;
+
+ I915_WRITE(fence_reg, 0);
+ }
dev_priv->fence_regs[obj_priv->fence_reg].obj = NULL;
obj_priv->fence_reg = I915_FENCE_REG_NONE;
@@ -1626,7 +1722,7 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
if (dev_priv->mm.suspended)
return -EBUSY;
if (alignment == 0)
- alignment = PAGE_SIZE;
+ alignment = i915_gem_get_gtt_alignment(obj);
if (alignment & (PAGE_SIZE - 1)) {
DRM_ERROR("Invalid object alignment requested %u\n", alignment);
return -EINVAL;
@@ -1969,30 +2065,28 @@ i915_gem_object_set_to_cpu_domain(struct drm_gem_object *obj, int write)
* drm_agp_chipset_flush
*/
static void
-i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj,
- uint32_t read_domains,
- uint32_t write_domain)
+i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj)
{
struct drm_device *dev = obj->dev;
struct drm_i915_gem_object *obj_priv = obj->driver_private;
uint32_t invalidate_domains = 0;
uint32_t flush_domains = 0;
- BUG_ON(read_domains & I915_GEM_DOMAIN_CPU);
- BUG_ON(write_domain == I915_GEM_DOMAIN_CPU);
+ BUG_ON(obj->pending_read_domains & I915_GEM_DOMAIN_CPU);
+ BUG_ON(obj->pending_write_domain == I915_GEM_DOMAIN_CPU);
#if WATCH_BUF
DRM_INFO("%s: object %p read %08x -> %08x write %08x -> %08x\n",
__func__, obj,
- obj->read_domains, read_domains,
- obj->write_domain, write_domain);
+ obj->read_domains, obj->pending_read_domains,
+ obj->write_domain, obj->pending_write_domain);
#endif
/*
* If the object isn't moving to a new write domain,
* let the object stay in multiple read domains
*/
- if (write_domain == 0)
- read_domains |= obj->read_domains;
+ if (obj->pending_write_domain == 0)
+ obj->pending_read_domains |= obj->read_domains;
else
obj_priv->dirty = 1;
@@ -2002,15 +2096,17 @@ i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj,
* any read domains which differ from the old
* write domain
*/
- if (obj->write_domain && obj->write_domain != read_domains) {
+ if (obj->write_domain &&
+ obj->write_domain != obj->pending_read_domains) {
flush_domains |= obj->write_domain;
- invalidate_domains |= read_domains & ~obj->write_domain;
+ invalidate_domains |=
+ obj->pending_read_domains & ~obj->write_domain;
}
/*
* Invalidate any read caches which may have
* stale data. That is, any new read domains.
*/
- invalidate_domains |= read_domains & ~obj->read_domains;
+ invalidate_domains |= obj->pending_read_domains & ~obj->read_domains;
if ((flush_domains | invalidate_domains) & I915_GEM_DOMAIN_CPU) {
#if WATCH_BUF
DRM_INFO("%s: CPU domain flush %08x invalidate %08x\n",
@@ -2019,9 +2115,15 @@ i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj,
i915_gem_clflush_object(obj);
}
- if ((write_domain | flush_domains) != 0)
- obj->write_domain = write_domain;
- obj->read_domains = read_domains;
+ /* The actual obj->write_domain will be updated with
+ * pending_write_domain after we emit the accumulated flush for all
+ * of our domain changes in execbuffers (which clears objects'
+ * write_domains). So if we have a current write domain that we
+ * aren't changing, set pending_write_domain to that.
+ */
+ if (flush_domains == 0 && obj->pending_write_domain == 0)
+ obj->pending_write_domain = obj->write_domain;
+ obj->read_domains = obj->pending_read_domains;
dev->invalidate_domains |= invalidate_domains;
dev->flush_domains |= flush_domains;
@@ -2224,6 +2326,8 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
(int) reloc.offset,
reloc.read_domains,
reloc.write_domain);
+ drm_gem_object_unreference(target_obj);
+ i915_gem_object_unpin(obj);
return -EINVAL;
}
@@ -2410,6 +2514,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
struct drm_i915_gem_exec_object *exec_list = NULL;
struct drm_gem_object **object_list = NULL;
struct drm_gem_object *batch_obj;
+ struct drm_i915_gem_object *obj_priv;
int ret, i, pinned = 0;
uint64_t exec_offset;
uint32_t seqno, flush_domains;
@@ -2453,13 +2558,15 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
if (dev_priv->mm.wedged) {
DRM_ERROR("Execbuf while wedged\n");
mutex_unlock(&dev->struct_mutex);
- return -EIO;
+ ret = -EIO;
+ goto pre_mutex_err;
}
if (dev_priv->mm.suspended) {
DRM_ERROR("Execbuf while VT-switched.\n");
mutex_unlock(&dev->struct_mutex);
- return -EBUSY;
+ ret = -EBUSY;
+ goto pre_mutex_err;
}
/* Look up object handles */
@@ -2472,6 +2579,15 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
ret = -EBADF;
goto err;
}
+
+ obj_priv = object_list[i]->driver_private;
+ if (obj_priv->in_execbuffer) {
+ DRM_ERROR("Object %p appears more than once in object list\n",
+ object_list[i]);
+ ret = -EBADF;
+ goto err;
+ }
+ obj_priv->in_execbuffer = true;
}
/* Pin and relocate */
@@ -2527,9 +2643,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
struct drm_gem_object *obj = object_list[i];
/* Compute new gpu domains and update invalidate/flush */
- i915_gem_object_set_to_gpu_domain(obj,
- obj->pending_read_domains,
- obj->pending_write_domain);
+ i915_gem_object_set_to_gpu_domain(obj);
}
i915_verify_inactive(dev, __FILE__, __LINE__);
@@ -2548,6 +2662,12 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
(void)i915_add_request(dev, dev->flush_domains);
}
+ for (i = 0; i < args->buffer_count; i++) {
+ struct drm_gem_object *obj = object_list[i];
+
+ obj->write_domain = obj->pending_write_domain;
+ }
+
i915_verify_inactive(dev, __FILE__, __LINE__);
#if WATCH_COHERENCY
@@ -2605,24 +2725,32 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
i915_verify_inactive(dev, __FILE__, __LINE__);
- /* Copy the new buffer offsets back to the user's exec list. */
- ret = copy_to_user((struct drm_i915_relocation_entry __user *)
- (uintptr_t) args->buffers_ptr,
- exec_list,
- sizeof(*exec_list) * args->buffer_count);
- if (ret)
- DRM_ERROR("failed to copy %d exec entries "
- "back to user (%d)\n",
- args->buffer_count, ret);
err:
for (i = 0; i < pinned; i++)
i915_gem_object_unpin(object_list[i]);
- for (i = 0; i < args->buffer_count; i++)
+ for (i = 0; i < args->buffer_count; i++) {
+ if (object_list[i]) {
+ obj_priv = object_list[i]->driver_private;
+ obj_priv->in_execbuffer = false;
+ }
drm_gem_object_unreference(object_list[i]);
+ }
mutex_unlock(&dev->struct_mutex);
+ if (!ret) {
+ /* Copy the new buffer offsets back to the user's exec list. */
+ ret = copy_to_user((struct drm_i915_relocation_entry __user *)
+ (uintptr_t) args->buffers_ptr,
+ exec_list,
+ sizeof(*exec_list) * args->buffer_count);
+ if (ret)
+ DRM_ERROR("failed to copy %d exec entries "
+ "back to user (%d)\n",
+ args->buffer_count, ret);
+ }
+
pre_mutex_err:
drm_free(object_list, sizeof(*object_list) * args->buffer_count,
DRM_MEM_DRIVER);
@@ -2644,7 +2772,22 @@ i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment)
ret = i915_gem_object_bind_to_gtt(obj, alignment);
if (ret != 0) {
if (ret != -EBUSY && ret != -ERESTARTSYS)
- DRM_ERROR("Failure to bind: %d", ret);
+ DRM_ERROR("Failure to bind: %d\n", ret);
+ return ret;
+ }
+ }
+ /*
+ * Pre-965 chips need a fence register set up in order to
+ * properly handle tiled surfaces.
+ */
+ if (!IS_I965G(dev) &&
+ obj_priv->fence_reg == I915_FENCE_REG_NONE &&
+ obj_priv->tiling_mode != I915_TILING_NONE) {
+ ret = i915_gem_object_get_fence_reg(obj, true);
+ if (ret != 0) {
+ if (ret != -EBUSY && ret != -ERESTARTSYS)
+ DRM_ERROR("Failure to install fence: %d\n",
+ ret);
return ret;
}
}
@@ -2718,6 +2861,7 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data,
if (obj_priv->pin_filp != NULL && obj_priv->pin_filp != file_priv) {
DRM_ERROR("Already pinned in i915_gem_pin_ioctl(): %d\n",
args->handle);
+ drm_gem_object_unreference(obj);
mutex_unlock(&dev->struct_mutex);
return -EINVAL;
}
@@ -2798,6 +2942,13 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data,
return -EBADF;
}
+ /* Update the active list for the hardware's current position.
+ * Otherwise this only updates on a delayed timer or when irqs are
+ * actually unmasked, and our working set ends up being larger than
+ * required.
+ */
+ i915_gem_retire_requests(dev);
+
obj_priv = obj->driver_private;
/* Don't count being on the flushing list against the object being
* done. Otherwise, a buffer left on the flushing list but not getting
@@ -2850,29 +3001,17 @@ int i915_gem_init_object(struct drm_gem_object *obj)
void i915_gem_free_object(struct drm_gem_object *obj)
{
struct drm_device *dev = obj->dev;
- struct drm_gem_mm *mm = dev->mm_private;
- struct drm_map_list *list;
- struct drm_map *map;
struct drm_i915_gem_object *obj_priv = obj->driver_private;
while (obj_priv->pin_count > 0)
i915_gem_object_unpin(obj);
- i915_gem_object_unbind(obj);
-
- list = &obj->map_list;
- drm_ht_remove_item(&mm->offset_hash, &list->hash);
+ if (obj_priv->phys_obj)
+ i915_gem_detach_phys_object(dev, obj);
- if (list->file_offset_node) {
- drm_mm_put_block(list->file_offset_node);
- list->file_offset_node = NULL;
- }
+ i915_gem_object_unbind(obj);
- map = list->map;
- if (map) {
- drm_free(map, sizeof(*map), DRM_MEM_DRIVER);
- list->map = NULL;
- }
+ i915_gem_free_mmap_offset(obj);
drm_free(obj_priv->page_cpu_valid, 1, DRM_MEM_DRIVER);
drm_free(obj->driver_private, 1, DRM_MEM_DRIVER);
@@ -2911,7 +3050,7 @@ i915_gem_evict_from_list(struct drm_device *dev, struct list_head *head)
return 0;
}
-static int
+int
i915_gem_idle(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
@@ -3057,6 +3196,7 @@ i915_gem_init_hws(struct drm_device *dev)
if (dev_priv->hw_status_page == NULL) {
DRM_ERROR("Failed to map status page.\n");
memset(&dev_priv->hws_map, 0, sizeof(dev_priv->hws_map));
+ i915_gem_object_unpin(obj);
drm_gem_object_unreference(obj);
return -EINVAL;
}
@@ -3069,6 +3209,31 @@ i915_gem_init_hws(struct drm_device *dev)
return 0;
}
+static void
+i915_gem_cleanup_hws(struct drm_device *dev)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_gem_object *obj;
+ struct drm_i915_gem_object *obj_priv;
+
+ if (dev_priv->hws_obj == NULL)
+ return;
+
+ obj = dev_priv->hws_obj;
+ obj_priv = obj->driver_private;
+
+ kunmap(obj_priv->page_list[0]);
+ i915_gem_object_unpin(obj);
+ drm_gem_object_unreference(obj);
+ dev_priv->hws_obj = NULL;
+
+ memset(&dev_priv->hws_map, 0, sizeof(dev_priv->hws_map));
+ dev_priv->hw_status_page = NULL;
+
+ /* Write high address into HWS_PGA when disabling. */
+ I915_WRITE(HWS_PGA, 0x1ffff000);
+}
+
int
i915_gem_init_ringbuffer(struct drm_device *dev)
{
@@ -3086,6 +3251,7 @@ i915_gem_init_ringbuffer(struct drm_device *dev)
obj = drm_gem_object_alloc(dev, 128 * 1024);
if (obj == NULL) {
DRM_ERROR("Failed to allocate ringbuffer\n");
+ i915_gem_cleanup_hws(dev);
return -ENOMEM;
}
obj_priv = obj->driver_private;
@@ -3093,6 +3259,7 @@ i915_gem_init_ringbuffer(struct drm_device *dev)
ret = i915_gem_object_pin(obj, 4096);
if (ret != 0) {
drm_gem_object_unreference(obj);
+ i915_gem_cleanup_hws(dev);
return ret;
}
@@ -3110,7 +3277,9 @@ i915_gem_init_ringbuffer(struct drm_device *dev)
if (ring->map.handle == NULL) {
DRM_ERROR("Failed to map ringbuffer.\n");
memset(&dev_priv->ring, 0, sizeof(dev_priv->ring));
+ i915_gem_object_unpin(obj);
drm_gem_object_unreference(obj);
+ i915_gem_cleanup_hws(dev);
return -EINVAL;
}
ring->ring_obj = obj;
@@ -3190,20 +3359,7 @@ i915_gem_cleanup_ringbuffer(struct drm_device *dev)
dev_priv->ring.ring_obj = NULL;
memset(&dev_priv->ring, 0, sizeof(dev_priv->ring));
- if (dev_priv->hws_obj != NULL) {
- struct drm_gem_object *obj = dev_priv->hws_obj;
- struct drm_i915_gem_object *obj_priv = obj->driver_private;
-
- kunmap(obj_priv->page_list[0]);
- i915_gem_object_unpin(obj);
- drm_gem_object_unreference(obj);
- dev_priv->hws_obj = NULL;
- memset(&dev_priv->hws_map, 0, sizeof(dev_priv->hws_map));
- dev_priv->hw_status_page = NULL;
-
- /* Write high address into HWS_PGA when disabling. */
- I915_WRITE(HWS_PGA, 0x1ffff000);
- }
+ i915_gem_cleanup_hws(dev);
}
int
@@ -3221,10 +3377,6 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data,
dev_priv->mm.wedged = 0;
}
- dev_priv->mm.gtt_mapping = io_mapping_create_wc(dev->agp->base,
- dev->agp->agp_info.aper_size
- * 1024 * 1024);
-
mutex_lock(&dev->struct_mutex);
dev_priv->mm.suspended = 0;
@@ -3247,7 +3399,6 @@ int
i915_gem_leavevt_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
int ret;
if (drm_core_check_feature(dev, DRIVER_MODESET))
@@ -3256,7 +3407,6 @@ i915_gem_leavevt_ioctl(struct drm_device *dev, void *data,
ret = i915_gem_idle(dev);
drm_irq_uninstall(dev);
- io_mapping_free(dev_priv->mm.gtt_mapping);
return ret;
}
@@ -3265,6 +3415,9 @@ i915_gem_lastclose(struct drm_device *dev)
{
int ret;
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ return;
+
ret = i915_gem_idle(dev);
if (ret)
DRM_ERROR("failed to idle hardware: %d\n", ret);
@@ -3286,10 +3439,187 @@ i915_gem_load(struct drm_device *dev)
/* Old X drivers will take 0-2 for front, back, depth buffers */
dev_priv->fence_reg_start = 3;
- if (IS_I965G(dev))
+ if (IS_I965G(dev) || IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
dev_priv->num_fence_regs = 16;
else
dev_priv->num_fence_regs = 8;
i915_gem_detect_bit_6_swizzle(dev);
}
+
+/*
+ * Create a physically contiguous memory object for this object
+ * e.g. for cursor + overlay regs
+ */
+int i915_gem_init_phys_object(struct drm_device *dev,
+ int id, int size)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_gem_phys_object *phys_obj;
+ int ret;
+
+ if (dev_priv->mm.phys_objs[id - 1] || !size)
+ return 0;
+
+ phys_obj = drm_calloc(1, sizeof(struct drm_i915_gem_phys_object), DRM_MEM_DRIVER);
+ if (!phys_obj)
+ return -ENOMEM;
+
+ phys_obj->id = id;
+
+ phys_obj->handle = drm_pci_alloc(dev, size, 0, 0xffffffff);
+ if (!phys_obj->handle) {
+ ret = -ENOMEM;
+ goto kfree_obj;
+ }
+#ifdef CONFIG_X86
+ set_memory_wc((unsigned long)phys_obj->handle->vaddr, phys_obj->handle->size / PAGE_SIZE);
+#endif
+
+ dev_priv->mm.phys_objs[id - 1] = phys_obj;
+
+ return 0;
+kfree_obj:
+ drm_free(phys_obj, sizeof(struct drm_i915_gem_phys_object), DRM_MEM_DRIVER);
+ return ret;
+}
+
+void i915_gem_free_phys_object(struct drm_device *dev, int id)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_gem_phys_object *phys_obj;
+
+ if (!dev_priv->mm.phys_objs[id - 1])
+ return;
+
+ phys_obj = dev_priv->mm.phys_objs[id - 1];
+ if (phys_obj->cur_obj) {
+ i915_gem_detach_phys_object(dev, phys_obj->cur_obj);
+ }
+
+#ifdef CONFIG_X86
+ set_memory_wb((unsigned long)phys_obj->handle->vaddr, phys_obj->handle->size / PAGE_SIZE);
+#endif
+ drm_pci_free(dev, phys_obj->handle);
+ kfree(phys_obj);
+ dev_priv->mm.phys_objs[id - 1] = NULL;
+}
+
+void i915_gem_free_all_phys_object(struct drm_device *dev)
+{
+ int i;
+
+ for (i = I915_GEM_PHYS_CURSOR_0; i <= I915_MAX_PHYS_OBJECT; i++)
+ i915_gem_free_phys_object(dev, i);
+}
+
+void i915_gem_detach_phys_object(struct drm_device *dev,
+ struct drm_gem_object *obj)
+{
+ struct drm_i915_gem_object *obj_priv;
+ int i;
+ int ret;
+ int page_count;
+
+ obj_priv = obj->driver_private;
+ if (!obj_priv->phys_obj)
+ return;
+
+ ret = i915_gem_object_get_page_list(obj);
+ if (ret)
+ goto out;
+
+ page_count = obj->size / PAGE_SIZE;
+
+ for (i = 0; i < page_count; i++) {
+ char *dst = kmap_atomic(obj_priv->page_list[i], KM_USER0);
+ char *src = obj_priv->phys_obj->handle->vaddr + (i * PAGE_SIZE);
+
+ memcpy(dst, src, PAGE_SIZE);
+ kunmap_atomic(dst, KM_USER0);
+ }
+ drm_clflush_pages(obj_priv->page_list, page_count);
+ drm_agp_chipset_flush(dev);
+out:
+ obj_priv->phys_obj->cur_obj = NULL;
+ obj_priv->phys_obj = NULL;
+}
+
+int
+i915_gem_attach_phys_object(struct drm_device *dev,
+ struct drm_gem_object *obj, int id)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_gem_object *obj_priv;
+ int ret = 0;
+ int page_count;
+ int i;
+
+ if (id > I915_MAX_PHYS_OBJECT)
+ return -EINVAL;
+
+ obj_priv = obj->driver_private;
+
+ if (obj_priv->phys_obj) {
+ if (obj_priv->phys_obj->id == id)
+ return 0;
+ i915_gem_detach_phys_object(dev, obj);
+ }
+
+
+ /* create a new object */
+ if (!dev_priv->mm.phys_objs[id - 1]) {
+ ret = i915_gem_init_phys_object(dev, id,
+ obj->size);
+ if (ret) {
+ DRM_ERROR("failed to init phys object %d size: %zu\n", id, obj->size);
+ goto out;
+ }
+ }
+
+ /* bind to the object */
+ obj_priv->phys_obj = dev_priv->mm.phys_objs[id - 1];
+ obj_priv->phys_obj->cur_obj = obj;
+
+ ret = i915_gem_object_get_page_list(obj);
+ if (ret) {
+ DRM_ERROR("failed to get page list\n");
+ goto out;
+ }
+
+ page_count = obj->size / PAGE_SIZE;
+
+ for (i = 0; i < page_count; i++) {
+ char *src = kmap_atomic(obj_priv->page_list[i], KM_USER0);
+ char *dst = obj_priv->phys_obj->handle->vaddr + (i * PAGE_SIZE);
+
+ memcpy(dst, src, PAGE_SIZE);
+ kunmap_atomic(src, KM_USER0);
+ }
+
+ return 0;
+out:
+ return ret;
+}
+
+static int
+i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *obj,
+ struct drm_i915_gem_pwrite *args,
+ struct drm_file *file_priv)
+{
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ void *obj_addr;
+ int ret;
+ char __user *user_data;
+
+ user_data = (char __user *) (uintptr_t) args->data_ptr;
+ obj_addr = obj_priv->phys_obj->handle->vaddr + args->offset;
+
+ DRM_DEBUG("obj_addr %p, %lld\n", obj_addr, args->size);
+ ret = copy_from_user(obj_addr, user_data, args->size);
+ if (ret)
+ return -EFAULT;
+
+ drm_agp_chipset_flush(dev);
+ return 0;
+}
diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c
index 241f39b7f46..7fb4191ef93 100644
--- a/drivers/gpu/drm/i915/i915_gem_tiling.c
+++ b/drivers/gpu/drm/i915/i915_gem_tiling.c
@@ -173,6 +173,73 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
dev_priv->mm.bit_6_swizzle_y = swizzle_y;
}
+
+/**
+ * Returns the size of the fence for a tiled object of the given size.
+ */
+static int
+i915_get_fence_size(struct drm_device *dev, int size)
+{
+ int i;
+ int start;
+
+ if (IS_I965G(dev)) {
+ /* The 965 can have fences at any page boundary. */
+ return ALIGN(size, 4096);
+ } else {
+ /* Align the size to a power of two greater than the smallest
+ * fence size.
+ */
+ if (IS_I9XX(dev))
+ start = 1024 * 1024;
+ else
+ start = 512 * 1024;
+
+ for (i = start; i < size; i <<= 1)
+ ;
+
+ return i;
+ }
+}
+
+/* Check pitch constriants for all chips & tiling formats */
+static bool
+i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode)
+{
+ int tile_width;
+
+ /* Linear is always fine */
+ if (tiling_mode == I915_TILING_NONE)
+ return true;
+
+ if (tiling_mode == I915_TILING_Y && HAS_128_BYTE_Y_TILING(dev))
+ tile_width = 128;
+ else
+ tile_width = 512;
+
+ /* 965+ just needs multiples of tile width */
+ if (IS_I965G(dev)) {
+ if (stride & (tile_width - 1))
+ return false;
+ return true;
+ }
+
+ /* Pre-965 needs power of two tile widths */
+ if (stride < tile_width)
+ return false;
+
+ if (stride & (stride - 1))
+ return false;
+
+ /* We don't handle the aperture area covered by the fence being bigger
+ * than the object size.
+ */
+ if (i915_get_fence_size(dev, size) != size)
+ return false;
+
+ return true;
+}
+
/**
* Sets the tiling mode of an object, returning the required swizzling of
* bit 6 of addresses in the object.
@@ -191,6 +258,11 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
return -EINVAL;
obj_priv = obj->driver_private;
+ if (!i915_tiling_ok(dev, args->stride, obj->size, args->tiling_mode)) {
+ drm_gem_object_unreference(obj);
+ return -EINVAL;
+ }
+
mutex_lock(&dev->struct_mutex);
if (args->tiling_mode == I915_TILING_NONE) {
@@ -207,12 +279,28 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
args->swizzle_mode = I915_BIT_6_SWIZZLE_NONE;
}
}
- obj_priv->tiling_mode = args->tiling_mode;
- obj_priv->stride = args->stride;
+ if (args->tiling_mode != obj_priv->tiling_mode) {
+ int ret;
- mutex_unlock(&dev->struct_mutex);
+ /* Unbind the object, as switching tiling means we're
+ * switching the cache organization due to fencing, probably.
+ */
+ ret = i915_gem_object_unbind(obj);
+ if (ret != 0) {
+ WARN(ret != -ERESTARTSYS,
+ "failed to unbind object for tiling switch");
+ args->tiling_mode = obj_priv->tiling_mode;
+ mutex_unlock(&dev->struct_mutex);
+ drm_gem_object_unreference(obj);
+
+ return ret;
+ }
+ obj_priv->tiling_mode = args->tiling_mode;
+ }
+ obj_priv->stride = args->stride;
drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
return 0;
}
@@ -251,9 +339,8 @@ i915_gem_get_tiling(struct drm_device *dev, void *data,
DRM_ERROR("unknown tiling mode\n");
}
- mutex_unlock(&dev->struct_mutex);
-
drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
return 0;
}
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 0cadafbef41..87b6b603469 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -174,6 +174,19 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int pipe)
return count;
}
+u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe)
+{
+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ int reg = pipe ? PIPEB_FRMCOUNT_GM45 : PIPEA_FRMCOUNT_GM45;
+
+ if (!i915_pipe_enabled(dev, pipe)) {
+ DRM_ERROR("trying to get vblank count for disabled pipe %d\n", pipe);
+ return 0;
+ }
+
+ return I915_READ(reg);
+}
+
irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
{
struct drm_device *dev = (struct drm_device *) arg;
@@ -370,12 +383,13 @@ int i915_irq_emit(struct drm_device *dev, void *data,
drm_i915_irq_emit_t *emit = data;
int result;
- RING_LOCK_TEST_WITH_RETURN(dev, file_priv);
-
if (!dev_priv) {
DRM_ERROR("called with no initialization\n");
return -EINVAL;
}
+
+ RING_LOCK_TEST_WITH_RETURN(dev, file_priv);
+
mutex_lock(&dev->struct_mutex);
result = i915_emit_irq(dev);
mutex_unlock(&dev->struct_mutex);
@@ -411,6 +425,12 @@ int i915_enable_vblank(struct drm_device *dev, int pipe)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
unsigned long irqflags;
+ int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
+ u32 pipeconf;
+
+ pipeconf = I915_READ(pipeconf_reg);
+ if (!(pipeconf & PIPEACONF_ENABLE))
+ return -EINVAL;
spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
if (IS_I965G(dev))
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 273162579e1..90600d89941 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -184,14 +184,15 @@
* Fence registers
*/
#define FENCE_REG_830_0 0x2000
+#define FENCE_REG_945_8 0x3000
#define I830_FENCE_START_MASK 0x07f80000
#define I830_FENCE_TILING_Y_SHIFT 12
-#define I830_FENCE_SIZE_BITS(size) ((get_order(size >> 19) - 1) << 8)
+#define I830_FENCE_SIZE_BITS(size) ((ffs((size) >> 19) - 1) << 8)
#define I830_FENCE_PITCH_SHIFT 4
#define I830_FENCE_REG_VALID (1<<0)
#define I915_FENCE_START_MASK 0x0ff00000
-#define I915_FENCE_SIZE_BITS(size) ((get_order(size >> 20) - 1) << 8)
+#define I915_FENCE_SIZE_BITS(size) ((ffs((size) >> 20) - 1) << 8)
#define FENCE_REG_965_0 0x03000
#define I965_FENCE_PITCH_SHIFT 2
@@ -1371,6 +1372,9 @@
#define PIPE_FRAME_LOW_SHIFT 24
#define PIPE_PIXEL_MASK 0x00ffffff
#define PIPE_PIXEL_SHIFT 0
+/* GM45+ just has to be different */
+#define PIPEA_FRMCOUNT_GM45 0x70040
+#define PIPEA_FLIPCOUNT_GM45 0x70044
/* Cursor A & B regs */
#define CURACNTR 0x70080
@@ -1439,6 +1443,9 @@
#define PIPEBSTAT 0x71024
#define PIPEBFRAMEHIGH 0x71040
#define PIPEBFRAMEPIXEL 0x71044
+#define PIPEB_FRMCOUNT_GM45 0x71040
+#define PIPEB_FLIPCOUNT_GM45 0x71044
+
/* Display B control */
#define DSPBCNTR 0x71180
diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c
index 5d84027ee8f..d669cc2b42c 100644
--- a/drivers/gpu/drm/i915/i915_suspend.c
+++ b/drivers/gpu/drm/i915/i915_suspend.c
@@ -119,11 +119,6 @@ static void i915_save_vga(struct drm_device *dev)
/* VGA color palette registers */
dev_priv->saveDACMASK = I915_READ8(VGA_DACMASK);
- /* DACCRX automatically increments during read */
- I915_WRITE8(VGA_DACRX, 0);
- /* Read 3 bytes of color data from each index */
- for (i = 0; i < 256 * 3; i++)
- dev_priv->saveDACDATA[i] = I915_READ8(VGA_DACDATA);
/* MSR bits */
dev_priv->saveMSR = I915_READ8(VGA_MSR_READ);
@@ -225,12 +220,6 @@ static void i915_restore_vga(struct drm_device *dev)
/* VGA color palette registers */
I915_WRITE8(VGA_DACMASK, dev_priv->saveDACMASK);
- /* DACCRX automatically increments during read */
- I915_WRITE8(VGA_DACWX, 0);
- /* Read 3 bytes of color data from each index */
- for (i = 0; i < 256 * 3; i++)
- I915_WRITE8(VGA_DACDATA, dev_priv->saveDACDATA[i]);
-
}
int i915_save_state(struct drm_device *dev)
diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
index 4ca82a02552..fc28e2bbd54 100644
--- a/drivers/gpu/drm/i915/intel_bios.c
+++ b/drivers/gpu/drm/i915/intel_bios.c
@@ -111,6 +111,12 @@ parse_panel_data(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
panel_fixed_mode->clock = dvo_timing->clock * 10;
panel_fixed_mode->type = DRM_MODE_TYPE_PREFERRED;
+ /* Some VBTs have bogus h/vtotal values */
+ if (panel_fixed_mode->hsync_end > panel_fixed_mode->htotal)
+ panel_fixed_mode->htotal = panel_fixed_mode->hsync_end + 1;
+ if (panel_fixed_mode->vsync_end > panel_fixed_mode->vtotal)
+ panel_fixed_mode->vtotal = panel_fixed_mode->vsync_end + 1;
+
drm_mode_set_name(panel_fixed_mode);
dev_priv->vbt_mode = panel_fixed_mode;
@@ -135,6 +141,14 @@ parse_general_features(struct drm_i915_private *dev_priv,
if (general) {
dev_priv->int_tv_support = general->int_tv_support;
dev_priv->int_crt_support = general->int_crt_support;
+ dev_priv->lvds_use_ssc = general->enable_ssc;
+
+ if (dev_priv->lvds_use_ssc) {
+ if (IS_I855(dev_priv->dev))
+ dev_priv->lvds_ssc_freq = general->ssc_freq ? 66 : 48;
+ else
+ dev_priv->lvds_ssc_freq = general->ssc_freq ? 100 : 96;
+ }
}
}
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 8ccb9c3ab86..a2834276cb3 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -90,12 +90,12 @@ typedef struct {
#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_N_MIN 1
+#define I9XX_N_MAX 6
#define I9XX_M_MIN 70
#define I9XX_M_MAX 120
#define I9XX_M1_MIN 10
-#define I9XX_M1_MAX 20
+#define I9XX_M1_MAX 22
#define I9XX_M2_MIN 5
#define I9XX_M2_MAX 9
#define I9XX_P_SDVO_DAC_MIN 5
@@ -189,19 +189,7 @@ static const intel_limit_t *intel_limit(struct drm_crtc *crtc)
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)
+static void intel_clock(int refclk, intel_clock_t *clock)
{
clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2);
clock->p = clock->p1 * clock->p2;
@@ -209,15 +197,6 @@ static void i9xx_clock(int refclk, intel_clock_t *clock)
clock->dot = clock->vco / clock->p;
}
-static void intel_clock(struct drm_device *dev, int refclk,
- intel_clock_t *clock)
-{
- if (IS_I9XX(dev))
- i9xx_clock (refclk, clock);
- else
- i8xx_clock (refclk, clock);
-}
-
/**
* Returns whether any output on the specified pipe is of the specified type
*/
@@ -238,7 +217,7 @@ bool intel_pipe_has_type (struct drm_crtc *crtc, int type)
return false;
}
-#define INTELPllInvalid(s) { /* ErrorF (s) */; return false; }
+#define INTELPllInvalid(s) do { /* DRM_DEBUG(s); */ return false; } while (0)
/**
* Returns whether the given set of divisors are valid for a given refclk with
* the given connectors.
@@ -318,7 +297,7 @@ static bool intel_find_best_PLL(struct drm_crtc *crtc, int target,
clock.p1 <= limit->p1.max; clock.p1++) {
int this_err;
- intel_clock(dev, refclk, &clock);
+ intel_clock(refclk, &clock);
if (!intel_PLL_is_valid(crtc, &clock))
continue;
@@ -343,7 +322,7 @@ intel_wait_for_vblank(struct drm_device *dev)
udelay(20000);
}
-static void
+static int
intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_framebuffer *old_fb)
{
@@ -361,11 +340,21 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE;
int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
u32 dspcntr, alignment;
+ int ret;
/* no fb bound */
if (!crtc->fb) {
DRM_DEBUG("No FB bound\n");
- return;
+ return 0;
+ }
+
+ switch (pipe) {
+ case 0:
+ case 1:
+ break;
+ default:
+ DRM_ERROR("Can't update pipe %d in SAREA\n", pipe);
+ return -EINVAL;
}
intel_fb = to_intel_framebuffer(crtc->fb);
@@ -377,30 +366,34 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
alignment = 64 * 1024;
break;
case I915_TILING_X:
- if (IS_I9XX(dev))
- alignment = 1024 * 1024;
- else
- alignment = 512 * 1024;
+ /* pin() will align the object as required by fence */
+ alignment = 0;
break;
case I915_TILING_Y:
/* FIXME: Is this true? */
DRM_ERROR("Y tiled not allowed for scan out buffers\n");
- return;
+ return -EINVAL;
default:
BUG();
}
- if (i915_gem_object_pin(intel_fb->obj, alignment))
- return;
-
- i915_gem_object_set_to_gtt_domain(intel_fb->obj, 1);
-
- Start = obj_priv->gtt_offset;
- Offset = y * crtc->fb->pitch + x * (crtc->fb->bits_per_pixel / 8);
+ mutex_lock(&dev->struct_mutex);
+ ret = i915_gem_object_pin(intel_fb->obj, alignment);
+ if (ret != 0) {
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+ }
- I915_WRITE(dspstride, crtc->fb->pitch);
+ ret = i915_gem_object_set_to_gtt_domain(intel_fb->obj, 1);
+ if (ret != 0) {
+ i915_gem_object_unpin(intel_fb->obj);
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+ }
dspcntr = I915_READ(dspcntr_reg);
+ /* Mask out pixel format bits in case we change it */
+ dspcntr &= ~DISPPLANE_PIXFORMAT_MASK;
switch (crtc->fb->bits_per_pixel) {
case 8:
dspcntr |= DISPPLANE_8BPP;
@@ -417,11 +410,17 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
break;
default:
DRM_ERROR("Unknown color depth\n");
- return;
+ i915_gem_object_unpin(intel_fb->obj);
+ mutex_unlock(&dev->struct_mutex);
+ return -EINVAL;
}
I915_WRITE(dspcntr_reg, dspcntr);
+ Start = obj_priv->gtt_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);
+ I915_WRITE(dspstride, crtc->fb->pitch);
if (IS_I965G(dev)) {
I915_WRITE(dspbase, Offset);
I915_READ(dspbase);
@@ -438,27 +437,24 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
intel_fb = to_intel_framebuffer(old_fb);
i915_gem_object_unpin(intel_fb->obj);
}
+ mutex_unlock(&dev->struct_mutex);
if (!dev->primary->master)
- return;
+ return 0;
master_priv = dev->primary->master->driver_priv;
if (!master_priv->sarea_priv)
- return;
+ return 0;
- switch (pipe) {
- case 0:
- master_priv->sarea_priv->pipeA_x = x;
- master_priv->sarea_priv->pipeA_y = y;
- break;
- case 1:
+ if (pipe) {
master_priv->sarea_priv->pipeB_x = x;
master_priv->sarea_priv->pipeB_y = y;
- break;
- default:
- DRM_ERROR("Can't update pipe %d in SAREA\n", pipe);
- break;
+ } else {
+ master_priv->sarea_priv->pipeA_x = x;
+ master_priv->sarea_priv->pipeA_y = y;
}
+
+ return 0;
}
@@ -706,11 +702,11 @@ static int intel_panel_fitter_pipe (struct drm_device *dev)
return 1;
}
-static void intel_crtc_mode_set(struct drm_crtc *crtc,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode,
- int x, int y,
- struct drm_framebuffer *old_fb)
+static int intel_crtc_mode_set(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode,
+ int x, int y,
+ struct drm_framebuffer *old_fb)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -730,13 +726,14 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc,
int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE;
int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS;
int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC;
- int refclk;
+ int refclk, num_outputs = 0;
intel_clock_t clock;
u32 dpll = 0, fp = 0, dspcntr, pipeconf;
bool ok, is_sdvo = false, is_dvo = false;
bool is_crt = false, is_lvds = false, is_tv = false;
struct drm_mode_config *mode_config = &dev->mode_config;
struct drm_connector *connector;
+ int ret;
drm_vblank_pre_modeset(dev, pipe);
@@ -753,6 +750,8 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc,
case INTEL_OUTPUT_SDVO:
case INTEL_OUTPUT_HDMI:
is_sdvo = true;
+ if (intel_output->needs_tv_clock)
+ is_tv = true;
break;
case INTEL_OUTPUT_DVO:
is_dvo = true;
@@ -764,9 +763,14 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc,
is_crt = true;
break;
}
+
+ num_outputs++;
}
- if (IS_I9XX(dev)) {
+ if (is_lvds && dev_priv->lvds_use_ssc && num_outputs < 2) {
+ refclk = dev_priv->lvds_ssc_freq * 1000;
+ DRM_DEBUG("using SSC reference clock of %d MHz\n", refclk / 1000);
+ } else if (IS_I9XX(dev)) {
refclk = 96000;
} else {
refclk = 48000;
@@ -775,7 +779,7 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc,
ok = intel_find_best_PLL(crtc, adjusted_mode->clock, refclk, &clock);
if (!ok) {
DRM_ERROR("Couldn't find PLL settings for mode!\n");
- return;
+ return -EINVAL;
}
fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
@@ -825,11 +829,14 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc,
}
}
- if (is_tv) {
+ if (is_sdvo && is_tv)
+ dpll |= PLL_REF_INPUT_TVCLKINBC;
+ else if (is_tv)
/* XXX: just matching BIOS for now */
-/* dpll |= PLL_REF_INPUT_TVCLKINBC; */
+ /* dpll |= PLL_REF_INPUT_TVCLKINBC; */
dpll |= 3;
- }
+ else if (is_lvds && dev_priv->lvds_use_ssc && num_outputs < 2)
+ dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN;
else
dpll |= PLL_REF_INPUT_DREFCLK;
@@ -946,9 +953,13 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc,
I915_WRITE(dspcntr_reg, dspcntr);
/* Flush the plane changes */
- intel_pipe_set_base(crtc, x, y, old_fb);
+ ret = intel_pipe_set_base(crtc, x, y, old_fb);
+ if (ret != 0)
+ return ret;
drm_vblank_post_modeset(dev, pipe);
+
+ return 0;
}
/** Loads the palette/gamma unit for the CRTC with the prepared values */
@@ -997,6 +1008,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
temp = CURSOR_MODE_DISABLE;
addr = 0;
bo = NULL;
+ mutex_lock(&dev->struct_mutex);
goto finish;
}
@@ -1014,21 +1026,26 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
if (bo->size < width * height * 4) {
DRM_ERROR("buffer is to small\n");
- drm_gem_object_unreference(bo);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto fail;
}
- if (dev_priv->cursor_needs_physical) {
- addr = dev->agp->base + obj_priv->gtt_offset;
- } else {
+ /* we only need to pin inside GTT if cursor is non-phy */
+ mutex_lock(&dev->struct_mutex);
+ if (!dev_priv->cursor_needs_physical) {
+ ret = i915_gem_object_pin(bo, PAGE_SIZE);
+ if (ret) {
+ DRM_ERROR("failed to pin cursor bo\n");
+ goto fail_locked;
+ }
addr = obj_priv->gtt_offset;
- }
-
- ret = i915_gem_object_pin(bo, PAGE_SIZE);
- if (ret) {
- DRM_ERROR("failed to pin cursor bo\n");
- drm_gem_object_unreference(bo);
- return ret;
+ } else {
+ ret = i915_gem_attach_phys_object(dev, bo, (pipe == 0) ? I915_GEM_PHYS_CURSOR_0 : I915_GEM_PHYS_CURSOR_1);
+ if (ret) {
+ DRM_ERROR("failed to attach phys object\n");
+ goto fail_locked;
+ }
+ addr = obj_priv->phys_obj->handle->busaddr;
}
temp = 0;
@@ -1041,14 +1058,25 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
I915_WRITE(base, addr);
if (intel_crtc->cursor_bo) {
- i915_gem_object_unpin(intel_crtc->cursor_bo);
+ if (dev_priv->cursor_needs_physical) {
+ if (intel_crtc->cursor_bo != bo)
+ i915_gem_detach_phys_object(dev, intel_crtc->cursor_bo);
+ } else
+ i915_gem_object_unpin(intel_crtc->cursor_bo);
drm_gem_object_unreference(intel_crtc->cursor_bo);
}
+ mutex_unlock(&dev->struct_mutex);
intel_crtc->cursor_addr = addr;
intel_crtc->cursor_bo = bo;
return 0;
+fail:
+ mutex_lock(&dev->struct_mutex);
+fail_locked:
+ drm_gem_object_unreference(bo);
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
}
static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
@@ -1273,7 +1301,7 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
}
/* XXX: Handle the 100Mhz refclk */
- i9xx_clock(96000, &clock);
+ intel_clock(96000, &clock);
} else {
bool is_lvds = (pipe == 1) && (I915_READ(LVDS) & LVDS_PORT_EN);
@@ -1285,9 +1313,9 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
if ((dpll & PLL_REF_INPUT_MASK) ==
PLLB_REF_INPUT_SPREADSPECTRUMIN) {
/* XXX: might not be 66MHz */
- i8xx_clock(66000, &clock);
+ intel_clock(66000, &clock);
} else
- i8xx_clock(48000, &clock);
+ intel_clock(48000, &clock);
} else {
if (dpll & PLL_P1_DIVIDE_BY_TWO)
clock.p1 = 2;
@@ -1300,7 +1328,7 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
else
clock.p2 = 2;
- i8xx_clock(48000, &clock);
+ intel_clock(48000, &clock);
}
}
@@ -1435,6 +1463,7 @@ static int intel_connector_clones(struct drm_device *dev, int type_mask)
static void intel_setup_outputs(struct drm_device *dev)
{
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_connector *connector;
intel_crt_init(dev);
@@ -1446,13 +1475,16 @@ static void intel_setup_outputs(struct drm_device *dev)
if (IS_I9XX(dev)) {
int found;
- found = intel_sdvo_init(dev, SDVOB);
- if (!found && SUPPORTS_INTEGRATED_HDMI(dev))
- intel_hdmi_init(dev, SDVOB);
-
- found = intel_sdvo_init(dev, SDVOC);
- if (!found && SUPPORTS_INTEGRATED_HDMI(dev))
- intel_hdmi_init(dev, SDVOC);
+ if (I915_READ(SDVOB) & SDVO_DETECTED) {
+ found = intel_sdvo_init(dev, SDVOB);
+ if (!found && SUPPORTS_INTEGRATED_HDMI(dev))
+ intel_hdmi_init(dev, SDVOB);
+ }
+ if (!IS_G4X(dev) || (I915_READ(SDVOB) & SDVO_DETECTED)) {
+ found = intel_sdvo_init(dev, SDVOC);
+ if (!found && SUPPORTS_INTEGRATED_HDMI(dev))
+ intel_hdmi_init(dev, SDVOC);
+ }
} else
intel_dvo_init(dev);
@@ -1575,7 +1607,9 @@ intel_user_framebuffer_create(struct drm_device *dev,
ret = intel_framebuffer_create(dev, mode_cmd, &fb, obj);
if (ret) {
+ mutex_lock(&dev->struct_mutex);
drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
return NULL;
}
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 8a4cc50c5b4..957daef8edf 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -82,6 +82,7 @@ struct intel_output {
struct intel_i2c_chan *i2c_bus; /* for control functions */
struct intel_i2c_chan *ddc_bus; /* for DDC only stuff */
bool load_detect_temp;
+ bool needs_tv_clock;
void *dev_priv;
};
diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c
index afd1217b8a0..b7f0ebe9f81 100644
--- a/drivers/gpu/drm/i915/intel_fb.c
+++ b/drivers/gpu/drm/i915/intel_fb.c
@@ -473,7 +473,7 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width,
ret = intel_framebuffer_create(dev, &mode_cmd, &fb, fbo);
if (ret) {
DRM_ERROR("failed to allocate fb.\n");
- goto out_unref;
+ goto out_unpin;
}
list_add(&fb->filp_head, &dev->mode_config.fb_kernel_list);
@@ -484,7 +484,7 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width,
info = framebuffer_alloc(sizeof(struct intelfb_par), device);
if (!info) {
ret = -ENOMEM;
- goto out_unref;
+ goto out_unpin;
}
par = info->par;
@@ -513,7 +513,7 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width,
size);
if (!info->screen_base) {
ret = -ENOSPC;
- goto out_unref;
+ goto out_unpin;
}
info->screen_size = size;
@@ -608,6 +608,8 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width,
mutex_unlock(&dev->struct_mutex);
return 0;
+out_unpin:
+ i915_gem_object_unpin(fbo);
out_unref:
drm_gem_object_unreference(fbo);
mutex_unlock(&dev->struct_mutex);
diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
index a5a2f5339e9..5ee9d4c2575 100644
--- a/drivers/gpu/drm/i915/intel_i2c.c
+++ b/drivers/gpu/drm/i915/intel_i2c.c
@@ -137,10 +137,6 @@ struct intel_i2c_chan *intel_i2c_create(struct drm_device *dev, const u32 reg,
chan->reg = reg;
snprintf(chan->adapter.name, I2C_NAME_SIZE, "intel drm %s", name);
chan->adapter.owner = THIS_MODULE;
-#ifndef I2C_HW_B_INTELFB
-#define I2C_HW_B_INTELFB I2C_HW_B_I810
-#endif
- chan->adapter.id = I2C_HW_B_INTELFB;
chan->adapter.algo_data = &chan->algo;
chan->adapter.dev.parent = &dev->pdev->dev;
chan->algo.setsda = set_data;
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index ccecfaf6307..0d211af9885 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -27,6 +27,7 @@
* Jesse Barnes <jesse.barnes@intel.com>
*/
+#include <linux/dmi.h>
#include <linux/i2c.h>
#include "drmP.h"
#include "drm.h"
@@ -311,10 +312,8 @@ static int intel_lvds_get_modes(struct drm_connector *connector)
if (dev_priv->panel_fixed_mode != NULL) {
struct drm_display_mode *mode;
- mutex_unlock(&dev->mode_config.mutex);
mode = drm_mode_duplicate(dev, dev_priv->panel_fixed_mode);
drm_mode_probed_add(connector, mode);
- mutex_unlock(&dev->mode_config.mutex);
return 1;
}
@@ -340,6 +339,18 @@ static void intel_lvds_destroy(struct drm_connector *connector)
kfree(connector);
}
+static int intel_lvds_set_property(struct drm_connector *connector,
+ struct drm_property *property,
+ uint64_t value)
+{
+ struct drm_device *dev = connector->dev;
+
+ if (property == dev->mode_config.dpms_property && connector->encoder)
+ intel_lvds_dpms(connector->encoder, (uint32_t)(value & 0xf));
+
+ return 0;
+}
+
static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = {
.dpms = intel_lvds_dpms,
.mode_fixup = intel_lvds_mode_fixup,
@@ -359,6 +370,7 @@ static const struct drm_connector_funcs intel_lvds_connector_funcs = {
.restore = intel_lvds_restore,
.detect = intel_lvds_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
+ .set_property = intel_lvds_set_property,
.destroy = intel_lvds_destroy,
};
@@ -392,6 +404,16 @@ void intel_lvds_init(struct drm_device *dev)
u32 lvds;
int pipe;
+ /* Blacklist machines that we know falsely report LVDS. */
+ /* FIXME: add a check for the Aopen Mini PC */
+
+ /* Apple Mac Mini Core Duo and Mac Mini Core 2 Duo */
+ if(dmi_match(DMI_PRODUCT_NAME, "Macmini1,1") ||
+ dmi_match(DMI_PRODUCT_NAME, "Macmini2,1")) {
+ DRM_DEBUG("Skipping LVDS initialization for Apple Mac Mini\n");
+ return;
+ }
+
intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL);
if (!intel_output) {
return;
@@ -445,7 +467,7 @@ void intel_lvds_init(struct drm_device *dev)
dev_priv->panel_fixed_mode =
drm_mode_duplicate(dev, scan);
mutex_unlock(&dev->mode_config.mutex);
- goto out; /* FIXME: check for quirks */
+ goto out;
}
mutex_unlock(&dev->mode_config.mutex);
}
@@ -456,6 +478,11 @@ void intel_lvds_init(struct drm_device *dev)
dev_priv->panel_fixed_mode =
drm_mode_duplicate(dev, dev_priv->vbt_mode);
mutex_unlock(&dev->mode_config.mutex);
+ if (dev_priv->panel_fixed_mode) {
+ dev_priv->panel_fixed_mode->type |=
+ DRM_MODE_TYPE_PREFERRED;
+ goto out;
+ }
}
/*
@@ -472,7 +499,7 @@ void intel_lvds_init(struct drm_device *dev)
if (dev_priv->panel_fixed_mode) {
dev_priv->panel_fixed_mode->type |=
DRM_MODE_TYPE_PREFERRED;
- goto out; /* FIXME: check for quirks */
+ goto out;
}
}
@@ -480,38 +507,6 @@ void intel_lvds_init(struct drm_device *dev)
if (!dev_priv->panel_fixed_mode)
goto failed;
- /* FIXME: detect aopen & mac mini type stuff automatically? */
- /*
- * Blacklist machines with BIOSes that list an LVDS panel without
- * actually having one.
- */
- if (IS_I945GM(dev)) {
- /* aopen mini pc */
- if (dev->pdev->subsystem_vendor == 0xa0a0)
- goto failed;
-
- if ((dev->pdev->subsystem_vendor == 0x8086) &&
- (dev->pdev->subsystem_device == 0x7270)) {
- /* It's a Mac Mini or Macbook Pro.
- *
- * Apple hardware is out to get us. The macbook pro
- * has a real LVDS panel, but the mac mini does not,
- * and they have the same device IDs. We'll
- * distinguish by panel size, on the assumption
- * that Apple isn't about to make any machines with an
- * 800x600 display.
- */
-
- if (dev_priv->panel_fixed_mode != NULL &&
- dev_priv->panel_fixed_mode->hdisplay == 800 &&
- dev_priv->panel_fixed_mode->vdisplay == 600) {
- DRM_DEBUG("Suspected Mac Mini, ignoring the LVDS\n");
- goto failed;
- }
- }
- }
-
-
out:
drm_sysfs_connector_add(connector);
return;
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index 40721546910..fbe6f3931b1 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -40,13 +40,59 @@
struct intel_sdvo_priv {
struct intel_i2c_chan *i2c_bus;
int slaveaddr;
+
+ /* Register for the SDVO device: SDVOB or SDVOC */
int output_device;
- u16 active_outputs;
+ /* Active outputs controlled by this SDVO output */
+ uint16_t controlled_output;
+ /*
+ * Capabilities of the SDVO device returned by
+ * i830_sdvo_get_capabilities()
+ */
struct intel_sdvo_caps caps;
+
+ /* Pixel clock limitations reported by the SDVO device, in kHz */
int pixel_clock_min, pixel_clock_max;
+ /**
+ * This is set if we're going to treat the device as TV-out.
+ *
+ * While we have these nice friendly flags for output types that ought
+ * to decide this for us, the S-Video output on our HDMI+S-Video card
+ * shows up as RGB1 (VGA).
+ */
+ bool is_tv;
+
+ /**
+ * This is set if we treat the device as HDMI, instead of DVI.
+ */
+ bool is_hdmi;
+
+ /**
+ * Returned SDTV resolutions allowed for the current format, if the
+ * device reported it.
+ */
+ struct intel_sdvo_sdtv_resolution_reply sdtv_resolutions;
+
+ /**
+ * Current selected TV format.
+ *
+ * This is stored in the same structure that's passed to the device, for
+ * convenience.
+ */
+ struct intel_sdvo_tv_format tv_format;
+
+ /*
+ * supported encoding mode, used to determine whether HDMI is
+ * supported
+ */
+ struct intel_sdvo_encode encode;
+
+ /* DDC bus used by this SDVO output */
+ uint8_t ddc_bus;
+
int save_sdvo_mult;
u16 save_active_outputs;
struct intel_sdvo_dtd save_input_dtd_1, save_input_dtd_2;
@@ -147,9 +193,9 @@ static bool intel_sdvo_write_byte(struct intel_output *intel_output, int addr,
#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;
+static const 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),
@@ -186,8 +232,35 @@ const static struct _sdvo_cmd_name {
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_GET_SUPPORTED_POWER_STATES),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_POWER_STATE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ENCODER_POWER_STATE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_DISPLAY_POWER_STATE),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTROL_BUS_SWITCH),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SCALED_HDTV_RESOLUTION_SUPPORT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS),
+ /* HDMI op code */
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPP_ENCODE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ENCODE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ENCODE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_PIXEL_REPLI),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PIXEL_REPLI),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_COLORIMETRY_CAP),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_COLORIMETRY),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_COLORIMETRY),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_AUDIO_ENCRYPT_PREFER),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_AUDIO_STAT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_AUDIO_STAT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_INDEX),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_INDEX),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_INFO),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_AV_SPLIT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_AV_SPLIT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_TXRATE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_TXRATE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_DATA),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_DATA),
};
#define SDVO_NAME(dev_priv) ((dev_priv)->output_device == SDVOB ? "SDVOB" : "SDVOC")
@@ -506,6 +579,50 @@ static bool intel_sdvo_set_output_timing(struct intel_output *intel_output,
SDVO_CMD_SET_OUTPUT_TIMINGS_PART1, dtd);
}
+static bool
+intel_sdvo_create_preferred_input_timing(struct intel_output *output,
+ uint16_t clock,
+ uint16_t width,
+ uint16_t height)
+{
+ struct intel_sdvo_preferred_input_timing_args args;
+ uint8_t status;
+
+ args.clock = clock;
+ args.width = width;
+ args.height = height;
+ intel_sdvo_write_cmd(output, SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING,
+ &args, sizeof(args));
+ status = intel_sdvo_read_response(output, NULL, 0);
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ return true;
+}
+
+static bool intel_sdvo_get_preferred_input_timing(struct intel_output *output,
+ struct intel_sdvo_dtd *dtd)
+{
+ bool 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 false;
+}
static int intel_sdvo_get_clock_rate_mult(struct intel_output *intel_output)
{
@@ -536,36 +653,12 @@ static bool intel_sdvo_set_clock_rate_mult(struct intel_output *intel_output, u8
return true;
}
-static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
-{
- /* Make the CRTC code factor in the SDVO pixel multiplier. The SDVO
- * device will be told of the multiplier during mode_set.
- */
- adjusted_mode->clock *= intel_sdvo_get_pixel_multiplier(mode);
- return true;
-}
-
-static void intel_sdvo_mode_set(struct drm_encoder *encoder,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
+static void intel_sdvo_get_dtd_from_mode(struct intel_sdvo_dtd *dtd,
+ struct drm_display_mode *mode)
{
- struct drm_device *dev = encoder->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
- struct drm_crtc *crtc = encoder->crtc;
- struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- struct intel_output *intel_output = enc_to_intel_output(encoder);
- struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
- u16 width, height;
- u16 h_blank_len, h_sync_len, v_blank_len, v_sync_len;
- u16 h_sync_offset, v_sync_offset;
- u32 sdvox;
- struct intel_sdvo_dtd output_dtd;
- int sdvo_pixel_multiply;
-
- if (!mode)
- return;
+ uint16_t width, height;
+ uint16_t h_blank_len, h_sync_len, v_blank_len, v_sync_len;
+ uint16_t h_sync_offset, v_sync_offset;
width = mode->crtc_hdisplay;
height = mode->crtc_vdisplay;
@@ -580,93 +673,423 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder,
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) |
+ dtd->part1.clock = mode->clock / 10;
+ dtd->part1.h_active = width & 0xff;
+ dtd->part1.h_blank = h_blank_len & 0xff;
+ 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) |
+ dtd->part1.v_active = height & 0xff;
+ dtd->part1.v_blank = v_blank_len & 0xff;
+ 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 |
+ dtd->part2.h_sync_off = h_sync_offset;
+ dtd->part2.h_sync_width = h_sync_len & 0xff;
+ 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) |
+ 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;
+ dtd->part2.dtd_flags = 0x18;
if (mode->flags & DRM_MODE_FLAG_PHSYNC)
- output_dtd.part2.dtd_flags |= 0x2;
+ dtd->part2.dtd_flags |= 0x2;
if (mode->flags & DRM_MODE_FLAG_PVSYNC)
- output_dtd.part2.dtd_flags |= 0x4;
+ dtd->part2.dtd_flags |= 0x4;
+
+ dtd->part2.sdvo_flags = 0;
+ dtd->part2.v_sync_off_high = v_sync_offset & 0xc0;
+ dtd->part2.reserved = 0;
+}
+
+static void intel_sdvo_get_mode_from_dtd(struct drm_display_mode * mode,
+ struct intel_sdvo_dtd *dtd)
+{
+ uint16_t width, height;
+ uint16_t h_blank_len, h_sync_len, v_blank_len, v_sync_len;
+ uint16_t h_sync_offset, v_sync_offset;
+
+ 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;
+
+ mode->hdisplay = dtd->part1.h_active;
+ mode->hdisplay += ((dtd->part1.h_high >> 4) & 0x0f) << 8;
+ mode->hsync_start = mode->hdisplay + dtd->part2.h_sync_off;
+ mode->hsync_start += (dtd->part2.sync_off_width_high & 0xa0) << 2;
+ mode->hsync_end = mode->hsync_start + dtd->part2.h_sync_width;
+ mode->hsync_end += (dtd->part2.sync_off_width_high & 0x30) << 4;
+ mode->htotal = mode->hdisplay + dtd->part1.h_blank;
+ mode->htotal += (dtd->part1.h_high & 0xf) << 8;
+
+ mode->vdisplay = dtd->part1.v_active;
+ mode->vdisplay += ((dtd->part1.v_high >> 4) & 0x0f) << 8;
+ mode->vsync_start = mode->vdisplay;
+ mode->vsync_start += (dtd->part2.v_sync_off_width >> 4) & 0xf;
+ mode->vsync_start += (dtd->part2.sync_off_width_high & 0x0a) << 2;
+ mode->vsync_start += dtd->part2.v_sync_off_high & 0xc0;
+ mode->vsync_end = mode->vsync_start +
+ (dtd->part2.v_sync_off_width & 0xf);
+ mode->vsync_end += (dtd->part2.sync_off_width_high & 0x3) << 4;
+ mode->vtotal = mode->vdisplay + dtd->part1.v_blank;
+ mode->vtotal += (dtd->part1.v_high & 0xf) << 8;
+
+ mode->clock = dtd->part1.clock * 10;
+
+ mode->flags &= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
+ if (dtd->part2.dtd_flags & 0x2)
+ mode->flags |= DRM_MODE_FLAG_PHSYNC;
+ if (dtd->part2.dtd_flags & 0x4)
+ mode->flags |= DRM_MODE_FLAG_PVSYNC;
+}
+
+static bool intel_sdvo_get_supp_encode(struct intel_output *output,
+ struct intel_sdvo_encode *encode)
+{
+ uint8_t status;
+
+ intel_sdvo_write_cmd(output, SDVO_CMD_GET_SUPP_ENCODE, NULL, 0);
+ status = intel_sdvo_read_response(output, encode, sizeof(*encode));
+ if (status != SDVO_CMD_STATUS_SUCCESS) { /* non-support means DVI */
+ memset(encode, 0, sizeof(*encode));
+ return false;
+ }
+
+ return true;
+}
+
+static bool intel_sdvo_set_encode(struct intel_output *output, uint8_t mode)
+{
+ uint8_t status;
+
+ intel_sdvo_write_cmd(output, SDVO_CMD_SET_ENCODE, &mode, 1);
+ status = intel_sdvo_read_response(output, NULL, 0);
+
+ return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+static bool intel_sdvo_set_colorimetry(struct intel_output *output,
+ uint8_t mode)
+{
+ uint8_t status;
+
+ intel_sdvo_write_cmd(output, SDVO_CMD_SET_COLORIMETRY, &mode, 1);
+ status = intel_sdvo_read_response(output, NULL, 0);
+
+ return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+#if 0
+static void intel_sdvo_dump_hdmi_buf(struct intel_output *output)
+{
+ int i, j;
+ uint8_t set_buf_index[2];
+ uint8_t av_split;
+ uint8_t buf_size;
+ uint8_t buf[48];
+ uint8_t *pos;
+
+ intel_sdvo_write_cmd(output, SDVO_CMD_GET_HBUF_AV_SPLIT, NULL, 0);
+ intel_sdvo_read_response(output, &av_split, 1);
+
+ for (i = 0; i <= av_split; i++) {
+ set_buf_index[0] = i; set_buf_index[1] = 0;
+ intel_sdvo_write_cmd(output, SDVO_CMD_SET_HBUF_INDEX,
+ set_buf_index, 2);
+ intel_sdvo_write_cmd(output, SDVO_CMD_GET_HBUF_INFO, NULL, 0);
+ intel_sdvo_read_response(output, &buf_size, 1);
+
+ pos = buf;
+ for (j = 0; j <= buf_size; j += 8) {
+ intel_sdvo_write_cmd(output, SDVO_CMD_GET_HBUF_DATA,
+ NULL, 0);
+ intel_sdvo_read_response(output, pos, 8);
+ pos += 8;
+ }
+ }
+}
+#endif
+
+static void intel_sdvo_set_hdmi_buf(struct intel_output *output, int index,
+ uint8_t *data, int8_t size, uint8_t tx_rate)
+{
+ uint8_t set_buf_index[2];
+
+ set_buf_index[0] = index;
+ set_buf_index[1] = 0;
+
+ intel_sdvo_write_cmd(output, SDVO_CMD_SET_HBUF_INDEX, set_buf_index, 2);
+
+ for (; size > 0; size -= 8) {
+ intel_sdvo_write_cmd(output, SDVO_CMD_SET_HBUF_DATA, data, 8);
+ data += 8;
+ }
+
+ intel_sdvo_write_cmd(output, SDVO_CMD_SET_HBUF_TXRATE, &tx_rate, 1);
+}
+
+static uint8_t intel_sdvo_calc_hbuf_csum(uint8_t *data, uint8_t size)
+{
+ uint8_t csum = 0;
+ int i;
+
+ for (i = 0; i < size; i++)
+ csum += data[i];
+
+ return 0x100 - csum;
+}
+
+#define DIP_TYPE_AVI 0x82
+#define DIP_VERSION_AVI 0x2
+#define DIP_LEN_AVI 13
+
+struct dip_infoframe {
+ uint8_t type;
+ uint8_t version;
+ uint8_t len;
+ uint8_t checksum;
+ union {
+ struct {
+ /* Packet Byte #1 */
+ uint8_t S:2;
+ uint8_t B:2;
+ uint8_t A:1;
+ uint8_t Y:2;
+ uint8_t rsvd1:1;
+ /* Packet Byte #2 */
+ uint8_t R:4;
+ uint8_t M:2;
+ uint8_t C:2;
+ /* Packet Byte #3 */
+ uint8_t SC:2;
+ uint8_t Q:2;
+ uint8_t EC:3;
+ uint8_t ITC:1;
+ /* Packet Byte #4 */
+ uint8_t VIC:7;
+ uint8_t rsvd2:1;
+ /* Packet Byte #5 */
+ uint8_t PR:4;
+ uint8_t rsvd3:4;
+ /* Packet Byte #6~13 */
+ uint16_t top_bar_end;
+ uint16_t bottom_bar_start;
+ uint16_t left_bar_end;
+ uint16_t right_bar_start;
+ } avi;
+ struct {
+ /* Packet Byte #1 */
+ uint8_t channel_count:3;
+ uint8_t rsvd1:1;
+ uint8_t coding_type:4;
+ /* Packet Byte #2 */
+ uint8_t sample_size:2; /* SS0, SS1 */
+ uint8_t sample_frequency:3;
+ uint8_t rsvd2:3;
+ /* Packet Byte #3 */
+ uint8_t coding_type_private:5;
+ uint8_t rsvd3:3;
+ /* Packet Byte #4 */
+ uint8_t channel_allocation;
+ /* Packet Byte #5 */
+ uint8_t rsvd4:3;
+ uint8_t level_shift:4;
+ uint8_t downmix_inhibit:1;
+ } audio;
+ uint8_t payload[28];
+ } __attribute__ ((packed)) u;
+} __attribute__((packed));
+
+static void intel_sdvo_set_avi_infoframe(struct intel_output *output,
+ struct drm_display_mode * mode)
+{
+ struct dip_infoframe avi_if = {
+ .type = DIP_TYPE_AVI,
+ .version = DIP_VERSION_AVI,
+ .len = DIP_LEN_AVI,
+ };
+
+ avi_if.checksum = intel_sdvo_calc_hbuf_csum((uint8_t *)&avi_if,
+ 4 + avi_if.len);
+ intel_sdvo_set_hdmi_buf(output, 1, (uint8_t *)&avi_if, 4 + avi_if.len,
+ SDVO_HBUF_TX_VSYNC);
+}
+
+static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct intel_output *output = enc_to_intel_output(encoder);
+ struct intel_sdvo_priv *dev_priv = output->dev_priv;
- output_dtd.part2.sdvo_flags = 0;
- output_dtd.part2.v_sync_off_high = v_sync_offset & 0xc0;
- output_dtd.part2.reserved = 0;
+ if (!dev_priv->is_tv) {
+ /* 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);
+ } else {
+ struct intel_sdvo_dtd output_dtd;
+ bool success;
+
+ /* We need to construct preferred input timings based on our
+ * output timings. To do that, we have to set the output
+ * timings, even though this isn't really the right place in
+ * the sequence to do it. Oh well.
+ */
+
+
+ /* Set output timings */
+ intel_sdvo_get_dtd_from_mode(&output_dtd, mode);
+ intel_sdvo_set_target_output(output,
+ dev_priv->controlled_output);
+ 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);
+
+
+ success = intel_sdvo_create_preferred_input_timing(output,
+ mode->clock / 10,
+ mode->hdisplay,
+ mode->vdisplay);
+ if (success) {
+ struct intel_sdvo_dtd input_dtd;
- /* Set the output timing to the screen */
- intel_sdvo_set_target_output(intel_output, sdvo_priv->active_outputs);
- intel_sdvo_set_output_timing(intel_output, &output_dtd);
+ intel_sdvo_get_preferred_input_timing(output,
+ &input_dtd);
+ intel_sdvo_get_mode_from_dtd(adjusted_mode, &input_dtd);
+
+ } else {
+ return false;
+ }
+ }
+ return true;
+}
+
+static void intel_sdvo_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_crtc *crtc = encoder->crtc;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct intel_output *output = enc_to_intel_output(encoder);
+ struct intel_sdvo_priv *sdvo_priv = output->dev_priv;
+ u32 sdvox = 0;
+ int sdvo_pixel_multiply;
+ struct intel_sdvo_in_out_map in_out;
+ struct intel_sdvo_dtd input_dtd;
+ u8 status;
+
+ if (!mode)
+ return;
+
+ /* First, set the input mapping for the first input to our controlled
+ * output. This is only correct if we're a single-input device, in
+ * which case the first input is the output from the appropriate SDVO
+ * channel on the motherboard. In a two-input device, the first input
+ * will be SDVOB and the second SDVOC.
+ */
+ in_out.in0 = sdvo_priv->controlled_output;
+ in_out.in1 = 0;
+
+ intel_sdvo_write_cmd(output, SDVO_CMD_SET_IN_OUT_MAP,
+ &in_out, sizeof(in_out));
+ status = intel_sdvo_read_response(output, NULL, 0);
+
+ if (sdvo_priv->is_hdmi) {
+ intel_sdvo_set_avi_infoframe(output, mode);
+ sdvox |= SDVO_AUDIO_ENABLE;
+ }
+
+ intel_sdvo_get_dtd_from_mode(&input_dtd, mode);
+
+ /* If it's a TV, we already set the output timing in mode_fixup.
+ * Otherwise, the output timing is equal to the input timing.
+ */
+ if (!sdvo_priv->is_tv) {
+ /* Set the output timing to the screen */
+ intel_sdvo_set_target_output(output,
+ sdvo_priv->controlled_output);
+ intel_sdvo_set_output_timing(output, &input_dtd);
+ }
/* Set the input timing to the screen. Assume always input 0. */
- intel_sdvo_set_target_input(intel_output, true, false);
+ intel_sdvo_set_target_input(output, true, false);
- /* We would like to use i830_sdvo_create_preferred_input_timing() to
+ /* We would like to use intel_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.
*/
- intel_sdvo_set_input_timing(intel_output, &output_dtd);
+#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, &input_dtd);
+#endif
switch (intel_sdvo_get_pixel_multiplier(mode)) {
case 1:
- intel_sdvo_set_clock_rate_mult(intel_output,
+ intel_sdvo_set_clock_rate_mult(output,
SDVO_CLOCK_RATE_MULT_1X);
break;
case 2:
- intel_sdvo_set_clock_rate_mult(intel_output,
+ intel_sdvo_set_clock_rate_mult(output,
SDVO_CLOCK_RATE_MULT_2X);
break;
case 4:
- intel_sdvo_set_clock_rate_mult(intel_output,
+ 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 (IS_I965G(dev)) {
+ sdvox |= SDVO_BORDER_ENABLE |
+ SDVO_VSYNC_ACTIVE_HIGH |
+ SDVO_HSYNC_ACTIVE_HIGH;
+ } 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 */
+ /* done in crtc_mode_set as the dpll_md reg must be written early */
+ } else if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) {
+ /* done in crtc_mode_set as it lives inside the dpll register */
} else {
sdvox |= (sdvo_pixel_multiply - 1) << SDVO_PORT_MULTIPLY_SHIFT;
}
- intel_sdvo_write_sdvox(intel_output, sdvox);
+ intel_sdvo_write_sdvox(output, sdvox);
}
static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode)
@@ -714,7 +1137,7 @@ static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode)
if (0)
intel_sdvo_set_encoder_power_state(intel_output, mode);
- intel_sdvo_set_active_outputs(intel_output, sdvo_priv->active_outputs);
+ intel_sdvo_set_active_outputs(intel_output, sdvo_priv->controlled_output);
}
return;
}
@@ -752,6 +1175,9 @@ static void intel_sdvo_save(struct drm_connector *connector)
&sdvo_priv->save_output_dtd[o]);
}
}
+ if (sdvo_priv->is_tv) {
+ /* XXX: Save TV format/enhancements. */
+ }
sdvo_priv->save_SDVOX = I915_READ(sdvo_priv->output_device);
}
@@ -759,7 +1185,6 @@ static void intel_sdvo_save(struct drm_connector *connector)
static void intel_sdvo_restore(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_output *intel_output = to_intel_output(connector);
struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
int o;
@@ -790,7 +1215,11 @@ static void intel_sdvo_restore(struct drm_connector *connector)
intel_sdvo_set_clock_rate_mult(intel_output, sdvo_priv->save_sdvo_mult);
- I915_WRITE(sdvo_priv->output_device, sdvo_priv->save_SDVOX);
+ if (sdvo_priv->is_tv) {
+ /* XXX: Restore TV format/enhancements. */
+ }
+
+ intel_sdvo_write_sdvox(intel_output, sdvo_priv->save_SDVOX);
if (sdvo_priv->save_SDVOX & SDVO_ENABLE)
{
@@ -916,20 +1345,173 @@ static enum drm_connector_status intel_sdvo_detect(struct drm_connector *connect
status = intel_sdvo_read_response(intel_output, &response, 2);
DRM_DEBUG("SDVO response %d %d\n", response[0], response[1]);
+
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return connector_status_unknown;
+
if ((response[0] != 0) || (response[1] != 0))
return connector_status_connected;
else
return connector_status_disconnected;
}
-static int intel_sdvo_get_modes(struct drm_connector *connector)
+static void intel_sdvo_get_ddc_modes(struct drm_connector *connector)
{
struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
/* set the bus switch and get the modes */
- intel_sdvo_set_control_bus_switch(intel_output, SDVO_CONTROL_BUS_DDC2);
+ intel_sdvo_set_control_bus_switch(intel_output, sdvo_priv->ddc_bus);
intel_ddc_get_modes(intel_output);
+#if 0
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ /* 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.
+ */
+ crt = xf86_config->output[0];
+ intel_output = crt->driver_private;
+ if (intel_output->type == I830_OUTPUT_ANALOG &&
+ crt->funcs->detect(crt) == XF86OutputStatusDisconnected) {
+ I830I2CInit(pScrn, &intel_output->pDDCBus, GPIOA, "CRTDDC_A");
+ edid_mon = xf86OutputGetEDID(crt, intel_output->pDDCBus);
+ xf86DestroyI2CBusRec(intel_output->pDDCBus, true, true);
+ }
+ if (edid_mon) {
+ xf86OutputSetEDID(output, edid_mon);
+ modes = xf86OutputGetEDIDModes(output);
+ }
+#endif
+}
+
+/**
+ * This function checks the current TV format, and chooses a default if
+ * it hasn't been set.
+ */
+static void
+intel_sdvo_check_tv_format(struct intel_output *output)
+{
+ struct intel_sdvo_priv *dev_priv = output->dev_priv;
+ struct intel_sdvo_tv_format format, unset;
+ uint8_t status;
+
+ intel_sdvo_write_cmd(output, SDVO_CMD_GET_TV_FORMAT, NULL, 0);
+ status = intel_sdvo_read_response(output, &format, sizeof(format));
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return;
+
+ memset(&unset, 0, sizeof(unset));
+ if (memcmp(&format, &unset, sizeof(format))) {
+ DRM_DEBUG("%s: Choosing default TV format of NTSC-M\n",
+ SDVO_NAME(dev_priv));
+
+ format.ntsc_m = true;
+ intel_sdvo_write_cmd(output, SDVO_CMD_SET_TV_FORMAT, NULL, 0);
+ status = intel_sdvo_read_response(output, NULL, 0);
+ }
+}
+
+/*
+ * Set of SDVO TV modes.
+ * Note! This is in reply order (see loop in get_tv_modes).
+ * XXX: all 60Hz refresh?
+ */
+struct drm_display_mode sdvo_tv_modes[] = {
+ { DRM_MODE("320x200", DRM_MODE_TYPE_DRIVER, 5815680, 321, 384, 416,
+ 200, 0, 232, 201, 233, 4196112, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ { DRM_MODE("320x240", DRM_MODE_TYPE_DRIVER, 6814080, 321, 384, 416,
+ 240, 0, 272, 241, 273, 4196112, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ { DRM_MODE("400x300", DRM_MODE_TYPE_DRIVER, 9910080, 401, 464, 496,
+ 300, 0, 332, 301, 333, 4196112, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ { DRM_MODE("640x350", DRM_MODE_TYPE_DRIVER, 16913280, 641, 704, 736,
+ 350, 0, 382, 351, 383, 4196112, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ { DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 19121280, 641, 704, 736,
+ 400, 0, 432, 401, 433, 4196112, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ { DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 19121280, 641, 704, 736,
+ 400, 0, 432, 401, 433, 4196112, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ { DRM_MODE("704x480", DRM_MODE_TYPE_DRIVER, 24624000, 705, 768, 800,
+ 480, 0, 512, 481, 513, 4196112, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ { DRM_MODE("704x576", DRM_MODE_TYPE_DRIVER, 29232000, 705, 768, 800,
+ 576, 0, 608, 577, 609, 4196112, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ { DRM_MODE("720x350", DRM_MODE_TYPE_DRIVER, 18751680, 721, 784, 816,
+ 350, 0, 382, 351, 383, 4196112, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 21199680, 721, 784, 816,
+ 400, 0, 432, 401, 433, 4196112, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 25116480, 721, 784, 816,
+ 480, 0, 512, 481, 513, 4196112, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ { DRM_MODE("720x540", DRM_MODE_TYPE_DRIVER, 28054080, 721, 784, 816,
+ 540, 0, 572, 541, 573, 4196112, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 29816640, 721, 784, 816,
+ 576, 0, 608, 577, 609, 4196112, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ { DRM_MODE("768x576", DRM_MODE_TYPE_DRIVER, 31570560, 769, 832, 864,
+ 576, 0, 608, 577, 609, 4196112, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 34030080, 801, 864, 896,
+ 600, 0, 632, 601, 633, 4196112, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 36581760, 833, 896, 928,
+ 624, 0, 656, 625, 657, 4196112, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ { DRM_MODE("920x766", DRM_MODE_TYPE_DRIVER, 48707040, 921, 984, 1016,
+ 766, 0, 798, 767, 799, 4196112, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 53827200, 1025, 1088, 1120,
+ 768, 0, 800, 769, 801, 4196112, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 87265920, 1281, 1344, 1376,
+ 1024, 0, 1056, 1025, 1057, 4196112, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+};
+
+static void intel_sdvo_get_tv_modes(struct drm_connector *connector)
+{
+ struct intel_output *output = to_intel_output(connector);
+ uint32_t reply = 0;
+ uint8_t status;
+ int i = 0;
+
+ intel_sdvo_check_tv_format(output);
+
+ /* Read the list of supported input resolutions for the selected TV
+ * format.
+ */
+ intel_sdvo_write_cmd(output, SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT,
+ NULL, 0);
+ status = intel_sdvo_read_response(output, &reply, 3);
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(sdvo_tv_modes); i++)
+ if (reply & (1 << i))
+ drm_mode_probed_add(connector, &sdvo_tv_modes[i]);
+}
+
+static int intel_sdvo_get_modes(struct drm_connector *connector)
+{
+ struct intel_output *output = to_intel_output(connector);
+ struct intel_sdvo_priv *sdvo_priv = output->dev_priv;
+
+ if (sdvo_priv->is_tv)
+ intel_sdvo_get_tv_modes(connector);
+ else
+ intel_sdvo_get_ddc_modes(connector);
+
if (list_empty(&connector->probed_modes))
return 0;
return 1;
@@ -978,6 +1560,65 @@ static const struct drm_encoder_funcs intel_sdvo_enc_funcs = {
};
+/**
+ * Choose the appropriate DDC bus for control bus switch command for this
+ * SDVO output based on the controlled output.
+ *
+ * DDC bus number assignment is in a priority order of RGB outputs, then TMDS
+ * outputs, then LVDS outputs.
+ */
+static void
+intel_sdvo_select_ddc_bus(struct intel_sdvo_priv *dev_priv)
+{
+ uint16_t mask = 0;
+ unsigned int num_bits;
+
+ /* Make a mask of outputs less than or equal to our own priority in the
+ * list.
+ */
+ switch (dev_priv->controlled_output) {
+ case SDVO_OUTPUT_LVDS1:
+ mask |= SDVO_OUTPUT_LVDS1;
+ case SDVO_OUTPUT_LVDS0:
+ mask |= SDVO_OUTPUT_LVDS0;
+ case SDVO_OUTPUT_TMDS1:
+ mask |= SDVO_OUTPUT_TMDS1;
+ case SDVO_OUTPUT_TMDS0:
+ mask |= SDVO_OUTPUT_TMDS0;
+ case SDVO_OUTPUT_RGB1:
+ mask |= SDVO_OUTPUT_RGB1;
+ case SDVO_OUTPUT_RGB0:
+ mask |= SDVO_OUTPUT_RGB0;
+ break;
+ }
+
+ /* Count bits to find what number we are in the priority list. */
+ mask &= dev_priv->caps.output_flags;
+ num_bits = hweight16(mask);
+ if (num_bits > 3) {
+ /* if more than 3 outputs, default to DDC bus 3 for now */
+ num_bits = 3;
+ }
+
+ /* Corresponds to SDVO_CONTROL_BUS_DDCx */
+ dev_priv->ddc_bus = 1 << num_bits;
+}
+
+static bool
+intel_sdvo_get_digital_encoding_mode(struct intel_output *output)
+{
+ struct intel_sdvo_priv *sdvo_priv = output->dev_priv;
+ uint8_t status;
+
+ intel_sdvo_set_target_output(output, sdvo_priv->controlled_output);
+
+ intel_sdvo_write_cmd(output, SDVO_CMD_GET_ENCODE, NULL, 0);
+ status = intel_sdvo_read_response(output, &sdvo_priv->is_hdmi, 1);
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+ return true;
+}
+
bool intel_sdvo_init(struct drm_device *dev, int output_device)
{
struct drm_connector *connector;
@@ -1040,45 +1681,76 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)
intel_sdvo_get_capabilities(intel_output, &sdvo_priv->caps);
- memset(&sdvo_priv->active_outputs, 0, sizeof(sdvo_priv->active_outputs));
+ if (sdvo_priv->caps.output_flags &
+ (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1)) {
+ if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0)
+ sdvo_priv->controlled_output = SDVO_OUTPUT_TMDS0;
+ else
+ sdvo_priv->controlled_output = SDVO_OUTPUT_TMDS1;
+
+ connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+ encoder_type = DRM_MODE_ENCODER_TMDS;
+ connector_type = DRM_MODE_CONNECTOR_DVID;
- /* TODO, CVBS, SVID, YPRPB & SCART outputs. */
- if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB0)
+ if (intel_sdvo_get_supp_encode(intel_output,
+ &sdvo_priv->encode) &&
+ intel_sdvo_get_digital_encoding_mode(intel_output) &&
+ sdvo_priv->is_hdmi) {
+ /* enable hdmi encoding mode if supported */
+ intel_sdvo_set_encode(intel_output, SDVO_ENCODE_HDMI);
+ intel_sdvo_set_colorimetry(intel_output,
+ SDVO_COLORIMETRY_RGB256);
+ connector_type = DRM_MODE_CONNECTOR_HDMIA;
+ }
+ }
+ else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_SVID0)
{
- sdvo_priv->active_outputs = SDVO_OUTPUT_RGB0;
+ sdvo_priv->controlled_output = SDVO_OUTPUT_SVID0;
+ connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+ encoder_type = DRM_MODE_ENCODER_TVDAC;
+ connector_type = DRM_MODE_CONNECTOR_SVIDEO;
+ sdvo_priv->is_tv = true;
+ intel_output->needs_tv_clock = true;
+ }
+ else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB0)
+ {
+ sdvo_priv->controlled_output = SDVO_OUTPUT_RGB0;
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
encoder_type = DRM_MODE_ENCODER_DAC;
connector_type = DRM_MODE_CONNECTOR_VGA;
}
else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB1)
{
- sdvo_priv->active_outputs = SDVO_OUTPUT_RGB1;
+ sdvo_priv->controlled_output = SDVO_OUTPUT_RGB1;
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
encoder_type = DRM_MODE_ENCODER_DAC;
connector_type = DRM_MODE_CONNECTOR_VGA;
}
- else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0)
+ else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_LVDS0)
{
- sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS0;
+ sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS0;
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
- encoder_type = DRM_MODE_ENCODER_TMDS;
- connector_type = DRM_MODE_CONNECTOR_DVID;
+ encoder_type = DRM_MODE_ENCODER_LVDS;
+ connector_type = DRM_MODE_CONNECTOR_LVDS;
}
- else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS1)
+ else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_LVDS1)
{
- sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS1;
+ sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS1;
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
- encoder_type = DRM_MODE_ENCODER_TMDS;
- connector_type = DRM_MODE_CONNECTOR_DVID;
+ encoder_type = DRM_MODE_ENCODER_LVDS;
+ connector_type = DRM_MODE_CONNECTOR_LVDS;
}
else
{
unsigned char bytes[2];
+ sdvo_priv->controlled_output = 0;
memcpy (bytes, &sdvo_priv->caps.output_flags, 2);
- DRM_DEBUG("%s: No active RGB or TMDS outputs (0x%02x%02x)\n",
+ DRM_DEBUG("%s: Unknown SDVO output type (0x%02x%02x)\n",
SDVO_NAME(sdvo_priv),
bytes[0], bytes[1]);
+ encoder_type = DRM_MODE_ENCODER_NONE;
+ connector_type = DRM_MODE_CONNECTOR_Unknown;
goto err_i2c;
}
@@ -1089,6 +1761,8 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)
drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc);
drm_sysfs_connector_add(connector);
+ intel_sdvo_select_ddc_bus(sdvo_priv);
+
/* Set the input timing to the screen. Assume always input 0. */
intel_sdvo_set_target_input(intel_output, true, false);
diff --git a/drivers/gpu/drm/i915/intel_sdvo_regs.h b/drivers/gpu/drm/i915/intel_sdvo_regs.h
index 861a43f8693..1117b9c151a 100644
--- a/drivers/gpu/drm/i915/intel_sdvo_regs.h
+++ b/drivers/gpu/drm/i915/intel_sdvo_regs.h
@@ -173,6 +173,9 @@ struct intel_sdvo_get_trained_inputs_response {
* Returns two struct intel_sdvo_output_flags structures.
*/
#define SDVO_CMD_GET_IN_OUT_MAP 0x06
+struct intel_sdvo_in_out_map {
+ u16 in0, in1;
+};
/**
* Sets the current mapping of SDVO inputs to outputs on the device.
@@ -206,7 +209,8 @@ struct intel_sdvo_get_trained_inputs_response {
struct intel_sdvo_get_interrupt_event_source_response {
u16 interrupt_status;
unsigned int ambient_light_interrupt:1;
- unsigned int pad:7;
+ unsigned int hdmi_audio_encrypt_change:1;
+ unsigned int pad:6;
} __attribute__((packed));
/**
@@ -305,23 +309,411 @@ struct intel_sdvo_set_target_input_args {
# define SDVO_CLOCK_RATE_MULT_4X (1 << 3)
#define SDVO_CMD_GET_SUPPORTED_TV_FORMATS 0x27
+/** 5 bytes of bit flags for TV formats shared by all TV format functions */
+struct intel_sdvo_tv_format {
+ unsigned int ntsc_m:1;
+ unsigned int ntsc_j:1;
+ unsigned int ntsc_443:1;
+ unsigned int pal_b:1;
+ unsigned int pal_d:1;
+ unsigned int pal_g:1;
+ unsigned int pal_h:1;
+ unsigned int pal_i:1;
+
+ unsigned int pal_m:1;
+ unsigned int pal_n:1;
+ unsigned int pal_nc:1;
+ unsigned int pal_60:1;
+ unsigned int secam_b:1;
+ unsigned int secam_d:1;
+ unsigned int secam_g:1;
+ unsigned int secam_k:1;
+
+ unsigned int secam_k1:1;
+ unsigned int secam_l:1;
+ unsigned int secam_60:1;
+ unsigned int hdtv_std_smpte_240m_1080i_59:1;
+ unsigned int hdtv_std_smpte_240m_1080i_60:1;
+ unsigned int hdtv_std_smpte_260m_1080i_59:1;
+ unsigned int hdtv_std_smpte_260m_1080i_60:1;
+ unsigned int hdtv_std_smpte_274m_1080i_50:1;
+
+ unsigned int hdtv_std_smpte_274m_1080i_59:1;
+ unsigned int hdtv_std_smpte_274m_1080i_60:1;
+ unsigned int hdtv_std_smpte_274m_1080p_23:1;
+ unsigned int hdtv_std_smpte_274m_1080p_24:1;
+ unsigned int hdtv_std_smpte_274m_1080p_25:1;
+ unsigned int hdtv_std_smpte_274m_1080p_29:1;
+ unsigned int hdtv_std_smpte_274m_1080p_30:1;
+ unsigned int hdtv_std_smpte_274m_1080p_50:1;
+
+ unsigned int hdtv_std_smpte_274m_1080p_59:1;
+ unsigned int hdtv_std_smpte_274m_1080p_60:1;
+ unsigned int hdtv_std_smpte_295m_1080i_50:1;
+ unsigned int hdtv_std_smpte_295m_1080p_50:1;
+ unsigned int hdtv_std_smpte_296m_720p_59:1;
+ unsigned int hdtv_std_smpte_296m_720p_60:1;
+ unsigned int hdtv_std_smpte_296m_720p_50:1;
+ unsigned int hdtv_std_smpte_293m_480p_59:1;
+
+ unsigned int hdtv_std_smpte_170m_480i_59:1;
+ unsigned int hdtv_std_iturbt601_576i_50:1;
+ unsigned int hdtv_std_iturbt601_576p_50:1;
+ unsigned int hdtv_std_eia_7702a_480i_60:1;
+ unsigned int hdtv_std_eia_7702a_480p_60:1;
+ unsigned int pad:3;
+} __attribute__((packed));
#define SDVO_CMD_GET_TV_FORMAT 0x28
#define SDVO_CMD_SET_TV_FORMAT 0x29
+/** Returns the resolutiosn that can be used with the given TV format */
+#define SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT 0x83
+struct intel_sdvo_sdtv_resolution_request {
+ unsigned int ntsc_m:1;
+ unsigned int ntsc_j:1;
+ unsigned int ntsc_443:1;
+ unsigned int pal_b:1;
+ unsigned int pal_d:1;
+ unsigned int pal_g:1;
+ unsigned int pal_h:1;
+ unsigned int pal_i:1;
+
+ unsigned int pal_m:1;
+ unsigned int pal_n:1;
+ unsigned int pal_nc:1;
+ unsigned int pal_60:1;
+ unsigned int secam_b:1;
+ unsigned int secam_d:1;
+ unsigned int secam_g:1;
+ unsigned int secam_k:1;
+
+ unsigned int secam_k1:1;
+ unsigned int secam_l:1;
+ unsigned int secam_60:1;
+ unsigned int pad:5;
+} __attribute__((packed));
+
+struct intel_sdvo_sdtv_resolution_reply {
+ unsigned int res_320x200:1;
+ unsigned int res_320x240:1;
+ unsigned int res_400x300:1;
+ unsigned int res_640x350:1;
+ unsigned int res_640x400:1;
+ unsigned int res_640x480:1;
+ unsigned int res_704x480:1;
+ unsigned int res_704x576:1;
+
+ unsigned int res_720x350:1;
+ unsigned int res_720x400:1;
+ unsigned int res_720x480:1;
+ unsigned int res_720x540:1;
+ unsigned int res_720x576:1;
+ unsigned int res_768x576:1;
+ unsigned int res_800x600:1;
+ unsigned int res_832x624:1;
+
+ unsigned int res_920x766:1;
+ unsigned int res_1024x768:1;
+ unsigned int res_1280x1024:1;
+ unsigned int pad:5;
+} __attribute__((packed));
+
+/* Get supported resolution with squire pixel aspect ratio that can be
+ scaled for the requested HDTV format */
+#define SDVO_CMD_GET_SCALED_HDTV_RESOLUTION_SUPPORT 0x85
+
+struct intel_sdvo_hdtv_resolution_request {
+ unsigned int hdtv_std_smpte_240m_1080i_59:1;
+ unsigned int hdtv_std_smpte_240m_1080i_60:1;
+ unsigned int hdtv_std_smpte_260m_1080i_59:1;
+ unsigned int hdtv_std_smpte_260m_1080i_60:1;
+ unsigned int hdtv_std_smpte_274m_1080i_50:1;
+ unsigned int hdtv_std_smpte_274m_1080i_59:1;
+ unsigned int hdtv_std_smpte_274m_1080i_60:1;
+ unsigned int hdtv_std_smpte_274m_1080p_23:1;
+
+ unsigned int hdtv_std_smpte_274m_1080p_24:1;
+ unsigned int hdtv_std_smpte_274m_1080p_25:1;
+ unsigned int hdtv_std_smpte_274m_1080p_29:1;
+ unsigned int hdtv_std_smpte_274m_1080p_30:1;
+ unsigned int hdtv_std_smpte_274m_1080p_50:1;
+ unsigned int hdtv_std_smpte_274m_1080p_59:1;
+ unsigned int hdtv_std_smpte_274m_1080p_60:1;
+ unsigned int hdtv_std_smpte_295m_1080i_50:1;
+
+ unsigned int hdtv_std_smpte_295m_1080p_50:1;
+ unsigned int hdtv_std_smpte_296m_720p_59:1;
+ unsigned int hdtv_std_smpte_296m_720p_60:1;
+ unsigned int hdtv_std_smpte_296m_720p_50:1;
+ unsigned int hdtv_std_smpte_293m_480p_59:1;
+ unsigned int hdtv_std_smpte_170m_480i_59:1;
+ unsigned int hdtv_std_iturbt601_576i_50:1;
+ unsigned int hdtv_std_iturbt601_576p_50:1;
+
+ unsigned int hdtv_std_eia_7702a_480i_60:1;
+ unsigned int hdtv_std_eia_7702a_480p_60:1;
+ unsigned int pad:6;
+} __attribute__((packed));
+
+struct intel_sdvo_hdtv_resolution_reply {
+ unsigned int res_640x480:1;
+ unsigned int res_800x600:1;
+ unsigned int res_1024x768:1;
+ unsigned int res_1280x960:1;
+ unsigned int res_1400x1050:1;
+ unsigned int res_1600x1200:1;
+ unsigned int res_1920x1440:1;
+ unsigned int res_2048x1536:1;
+
+ unsigned int res_2560x1920:1;
+ unsigned int res_3200x2400:1;
+ unsigned int res_3840x2880:1;
+ unsigned int pad1:5;
+
+ unsigned int res_848x480:1;
+ unsigned int res_1064x600:1;
+ unsigned int res_1280x720:1;
+ unsigned int res_1360x768:1;
+ unsigned int res_1704x960:1;
+ unsigned int res_1864x1050:1;
+ unsigned int res_1920x1080:1;
+ unsigned int res_2128x1200:1;
+
+ unsigned int res_2560x1400:1;
+ unsigned int res_2728x1536:1;
+ unsigned int res_3408x1920:1;
+ unsigned int res_4264x2400:1;
+ unsigned int res_5120x2880:1;
+ unsigned int pad2:3;
+
+ unsigned int res_768x480:1;
+ unsigned int res_960x600:1;
+ unsigned int res_1152x720:1;
+ unsigned int res_1124x768:1;
+ unsigned int res_1536x960:1;
+ unsigned int res_1680x1050:1;
+ unsigned int res_1728x1080:1;
+ unsigned int res_1920x1200:1;
+
+ unsigned int res_2304x1440:1;
+ unsigned int res_2456x1536:1;
+ unsigned int res_3072x1920:1;
+ unsigned int res_3840x2400:1;
+ unsigned int res_4608x2880:1;
+ unsigned int pad3:3;
+
+ unsigned int res_1280x1024:1;
+ unsigned int pad4:7;
+
+ unsigned int res_1280x768:1;
+ unsigned int pad5:7;
+} __attribute__((packed));
+
+/* Get supported power state returns info for encoder and monitor, rely on
+ last SetTargetInput and SetTargetOutput calls */
#define SDVO_CMD_GET_SUPPORTED_POWER_STATES 0x2a
+/* Get power state returns info for encoder and monitor, rely on last
+ SetTargetInput and SetTargetOutput calls */
+#define SDVO_CMD_GET_POWER_STATE 0x2b
#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_MONITOR_STATE_ON (1 << 4)
+# define SDVO_MONITOR_STATE_STANDBY (1 << 5)
+# define SDVO_MONITOR_STATE_SUSPEND (1 << 6)
+# define SDVO_MONITOR_STATE_OFF (1 << 7)
+
+#define SDVO_CMD_GET_MAX_PANEL_POWER_SEQUENCING 0x2d
+#define SDVO_CMD_GET_PANEL_POWER_SEQUENCING 0x2e
+#define SDVO_CMD_SET_PANEL_POWER_SEQUENCING 0x2f
+/**
+ * The panel power sequencing parameters are in units of milliseconds.
+ * The high fields are bits 8:9 of the 10-bit values.
+ */
+struct sdvo_panel_power_sequencing {
+ u8 t0;
+ u8 t1;
+ u8 t2;
+ u8 t3;
+ u8 t4;
+
+ unsigned int t0_high:2;
+ unsigned int t1_high:2;
+ unsigned int t2_high:2;
+ unsigned int t3_high:2;
+
+ unsigned int t4_high:2;
+ unsigned int pad:6;
+} __attribute__((packed));
+
+#define SDVO_CMD_GET_MAX_BACKLIGHT_LEVEL 0x30
+struct sdvo_max_backlight_reply {
+ u8 max_value;
+ u8 default_value;
+} __attribute__((packed));
+
+#define SDVO_CMD_GET_BACKLIGHT_LEVEL 0x31
+#define SDVO_CMD_SET_BACKLIGHT_LEVEL 0x32
+
+#define SDVO_CMD_GET_AMBIENT_LIGHT 0x33
+struct sdvo_get_ambient_light_reply {
+ u16 trip_low;
+ u16 trip_high;
+ u16 value;
+} __attribute__((packed));
+#define SDVO_CMD_SET_AMBIENT_LIGHT 0x34
+struct sdvo_set_ambient_light_reply {
+ u16 trip_low;
+ u16 trip_high;
+ unsigned int enable:1;
+ unsigned int pad:7;
+} __attribute__((packed));
+
+/* Set display power state */
+#define SDVO_CMD_SET_DISPLAY_POWER_STATE 0x7d
+# define SDVO_DISPLAY_STATE_ON (1 << 0)
+# define SDVO_DISPLAY_STATE_STANDBY (1 << 1)
+# define SDVO_DISPLAY_STATE_SUSPEND (1 << 2)
+# define SDVO_DISPLAY_STATE_OFF (1 << 3)
+
+#define SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS 0x84
+struct intel_sdvo_enhancements_reply {
+ unsigned int flicker_filter:1;
+ unsigned int flicker_filter_adaptive:1;
+ unsigned int flicker_filter_2d:1;
+ unsigned int saturation:1;
+ unsigned int hue:1;
+ unsigned int brightness:1;
+ unsigned int contrast:1;
+ unsigned int overscan_h:1;
+
+ unsigned int overscan_v:1;
+ unsigned int position_h:1;
+ unsigned int position_v:1;
+ unsigned int sharpness:1;
+ unsigned int dot_crawl:1;
+ unsigned int dither:1;
+ unsigned int max_tv_chroma_filter:1;
+ unsigned int max_tv_luma_filter:1;
+} __attribute__((packed));
+
+/* Picture enhancement limits below are dependent on the current TV format,
+ * and thus need to be queried and set after it.
+ */
+#define SDVO_CMD_GET_MAX_FLICKER_FITER 0x4d
+#define SDVO_CMD_GET_MAX_ADAPTIVE_FLICKER_FITER 0x7b
+#define SDVO_CMD_GET_MAX_2D_FLICKER_FITER 0x52
+#define SDVO_CMD_GET_MAX_SATURATION 0x55
+#define SDVO_CMD_GET_MAX_HUE 0x58
+#define SDVO_CMD_GET_MAX_BRIGHTNESS 0x5b
+#define SDVO_CMD_GET_MAX_CONTRAST 0x5e
+#define SDVO_CMD_GET_MAX_OVERSCAN_H 0x61
+#define SDVO_CMD_GET_MAX_OVERSCAN_V 0x64
+#define SDVO_CMD_GET_MAX_POSITION_H 0x67
+#define SDVO_CMD_GET_MAX_POSITION_V 0x6a
+#define SDVO_CMD_GET_MAX_SHARPNESS_V 0x6d
+#define SDVO_CMD_GET_MAX_TV_CHROMA 0x74
+#define SDVO_CMD_GET_MAX_TV_LUMA 0x77
+struct intel_sdvo_enhancement_limits_reply {
+ u16 max_value;
+ u16 default_value;
+} __attribute__((packed));
-#define SDVO_CMD_SET_TV_RESOLUTION_SUPPORT 0x93
+#define SDVO_CMD_GET_LVDS_PANEL_INFORMATION 0x7f
+#define SDVO_CMD_SET_LVDS_PANEL_INFORMATION 0x80
+# define SDVO_LVDS_COLOR_DEPTH_18 (0 << 0)
+# define SDVO_LVDS_COLOR_DEPTH_24 (1 << 0)
+# define SDVO_LVDS_CONNECTOR_SPWG (0 << 2)
+# define SDVO_LVDS_CONNECTOR_OPENLDI (1 << 2)
+# define SDVO_LVDS_SINGLE_CHANNEL (0 << 4)
+# define SDVO_LVDS_DUAL_CHANNEL (1 << 4)
+
+#define SDVO_CMD_GET_FLICKER_FILTER 0x4e
+#define SDVO_CMD_SET_FLICKER_FILTER 0x4f
+#define SDVO_CMD_GET_ADAPTIVE_FLICKER_FITER 0x50
+#define SDVO_CMD_SET_ADAPTIVE_FLICKER_FITER 0x51
+#define SDVO_CMD_GET_2D_FLICKER_FITER 0x53
+#define SDVO_CMD_SET_2D_FLICKER_FITER 0x54
+#define SDVO_CMD_GET_SATURATION 0x56
+#define SDVO_CMD_SET_SATURATION 0x57
+#define SDVO_CMD_GET_HUE 0x59
+#define SDVO_CMD_SET_HUE 0x5a
+#define SDVO_CMD_GET_BRIGHTNESS 0x5c
+#define SDVO_CMD_SET_BRIGHTNESS 0x5d
+#define SDVO_CMD_GET_CONTRAST 0x5f
+#define SDVO_CMD_SET_CONTRAST 0x60
+#define SDVO_CMD_GET_OVERSCAN_H 0x62
+#define SDVO_CMD_SET_OVERSCAN_H 0x63
+#define SDVO_CMD_GET_OVERSCAN_V 0x65
+#define SDVO_CMD_SET_OVERSCAN_V 0x66
+#define SDVO_CMD_GET_POSITION_H 0x68
+#define SDVO_CMD_SET_POSITION_H 0x69
+#define SDVO_CMD_GET_POSITION_V 0x6b
+#define SDVO_CMD_SET_POSITION_V 0x6c
+#define SDVO_CMD_GET_SHARPNESS 0x6e
+#define SDVO_CMD_SET_SHARPNESS 0x6f
+#define SDVO_CMD_GET_TV_CHROMA 0x75
+#define SDVO_CMD_SET_TV_CHROMA 0x76
+#define SDVO_CMD_GET_TV_LUMA 0x78
+#define SDVO_CMD_SET_TV_LUMA 0x79
+struct intel_sdvo_enhancements_arg {
+ u16 value;
+}__attribute__((packed));
+
+#define SDVO_CMD_GET_DOT_CRAWL 0x70
+#define SDVO_CMD_SET_DOT_CRAWL 0x71
+# define SDVO_DOT_CRAWL_ON (1 << 0)
+# define SDVO_DOT_CRAWL_DEFAULT_ON (1 << 1)
+
+#define SDVO_CMD_GET_DITHER 0x72
+#define SDVO_CMD_SET_DITHER 0x73
+# define SDVO_DITHER_ON (1 << 0)
+# define SDVO_DITHER_DEFAULT_ON (1 << 1)
#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
+# define SDVO_CONTROL_BUS_PROM (1 << 0)
+# define SDVO_CONTROL_BUS_DDC1 (1 << 1)
+# define SDVO_CONTROL_BUS_DDC2 (1 << 2)
+# define SDVO_CONTROL_BUS_DDC3 (1 << 3)
+
+/* HDMI op codes */
+#define SDVO_CMD_GET_SUPP_ENCODE 0x9d
+#define SDVO_CMD_GET_ENCODE 0x9e
+#define SDVO_CMD_SET_ENCODE 0x9f
+ #define SDVO_ENCODE_DVI 0x0
+ #define SDVO_ENCODE_HDMI 0x1
+#define SDVO_CMD_SET_PIXEL_REPLI 0x8b
+#define SDVO_CMD_GET_PIXEL_REPLI 0x8c
+#define SDVO_CMD_GET_COLORIMETRY_CAP 0x8d
+#define SDVO_CMD_SET_COLORIMETRY 0x8e
+ #define SDVO_COLORIMETRY_RGB256 0x0
+ #define SDVO_COLORIMETRY_RGB220 0x1
+ #define SDVO_COLORIMETRY_YCrCb422 0x3
+ #define SDVO_COLORIMETRY_YCrCb444 0x4
+#define SDVO_CMD_GET_COLORIMETRY 0x8f
+#define SDVO_CMD_GET_AUDIO_ENCRYPT_PREFER 0x90
+#define SDVO_CMD_SET_AUDIO_STAT 0x91
+#define SDVO_CMD_GET_AUDIO_STAT 0x92
+#define SDVO_CMD_SET_HBUF_INDEX 0x93
+#define SDVO_CMD_GET_HBUF_INDEX 0x94
+#define SDVO_CMD_GET_HBUF_INFO 0x95
+#define SDVO_CMD_SET_HBUF_AV_SPLIT 0x96
+#define SDVO_CMD_GET_HBUF_AV_SPLIT 0x97
+#define SDVO_CMD_SET_HBUF_DATA 0x98
+#define SDVO_CMD_GET_HBUF_DATA 0x99
+#define SDVO_CMD_SET_HBUF_TXRATE 0x9a
+#define SDVO_CMD_GET_HBUF_TXRATE 0x9b
+ #define SDVO_HBUF_TX_DISABLED (0 << 6)
+ #define SDVO_HBUF_TX_ONCE (2 << 6)
+ #define SDVO_HBUF_TX_VSYNC (3 << 6)
+#define SDVO_CMD_GET_AUDIO_TX_INFO 0x9c
+
+struct intel_sdvo_encode{
+ u8 dvi_rev;
+ u8 hdmi_rev;
+} __attribute__ ((packed));
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
index fbb35dc56f5..56485d67369 100644
--- a/drivers/gpu/drm/i915/intel_tv.c
+++ b/drivers/gpu/drm/i915/intel_tv.c
@@ -411,7 +411,7 @@ struct tv_mode {
* These values account for -1s required.
*/
-const static struct tv_mode tv_modes[] = {
+static const struct tv_mode tv_modes[] = {
{
.name = "NTSC-M",
.clock = 107520,
diff --git a/drivers/gpu/drm/radeon/radeon_cp.c b/drivers/gpu/drm/radeon/radeon_cp.c
index 63212d7bbc2..92965dbb3c1 100644
--- a/drivers/gpu/drm/radeon/radeon_cp.c
+++ b/drivers/gpu/drm/radeon/radeon_cp.c
@@ -557,8 +557,10 @@ static int radeon_do_engine_reset(struct drm_device * dev)
}
static void radeon_cp_init_ring_buffer(struct drm_device * dev,
- drm_radeon_private_t * dev_priv)
+ drm_radeon_private_t *dev_priv,
+ struct drm_file *file_priv)
{
+ struct drm_radeon_master_private *master_priv;
u32 ring_start, cur_read_ptr;
u32 tmp;
@@ -677,6 +679,14 @@ static void radeon_cp_init_ring_buffer(struct drm_device * dev,
dev_priv->scratch[2] = 0;
RADEON_WRITE(RADEON_LAST_CLEAR_REG, 0);
+ /* reset sarea copies of these */
+ master_priv = file_priv->master->driver_priv;
+ if (master_priv->sarea_priv) {
+ master_priv->sarea_priv->last_frame = 0;
+ master_priv->sarea_priv->last_dispatch = 0;
+ master_priv->sarea_priv->last_clear = 0;
+ }
+
radeon_do_wait_for_idle(dev_priv);
/* Sync everything up */
@@ -1039,9 +1049,9 @@ static int radeon_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init,
#if __OS_HAS_AGP
if (dev_priv->flags & RADEON_IS_AGP) {
- drm_core_ioremap(dev_priv->cp_ring, dev);
- drm_core_ioremap(dev_priv->ring_rptr, dev);
- drm_core_ioremap(dev->agp_buffer_map, dev);
+ drm_core_ioremap_wc(dev_priv->cp_ring, dev);
+ drm_core_ioremap_wc(dev_priv->ring_rptr, dev);
+ drm_core_ioremap_wc(dev->agp_buffer_map, dev);
if (!dev_priv->cp_ring->handle ||
!dev_priv->ring_rptr->handle ||
!dev->agp_buffer_map->handle) {
@@ -1215,7 +1225,7 @@ static int radeon_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init,
}
radeon_cp_load_microcode(dev_priv);
- radeon_cp_init_ring_buffer(dev, dev_priv);
+ radeon_cp_init_ring_buffer(dev, dev_priv, file_priv);
dev_priv->last_buf = 0;
@@ -1281,7 +1291,7 @@ static int radeon_do_cleanup_cp(struct drm_device * dev)
*
* Charl P. Botha <http://cpbotha.net>
*/
-static int radeon_do_resume_cp(struct drm_device * dev)
+static int radeon_do_resume_cp(struct drm_device *dev, struct drm_file *file_priv)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
@@ -1304,7 +1314,7 @@ static int radeon_do_resume_cp(struct drm_device * dev)
}
radeon_cp_load_microcode(dev_priv);
- radeon_cp_init_ring_buffer(dev, dev_priv);
+ radeon_cp_init_ring_buffer(dev, dev_priv, file_priv);
radeon_do_engine_reset(dev);
radeon_irq_set_state(dev, RADEON_SW_INT_ENABLE, 1);
@@ -1479,8 +1489,7 @@ int radeon_cp_idle(struct drm_device *dev, void *data, struct drm_file *file_pri
*/
int radeon_cp_resume(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
-
- return radeon_do_resume_cp(dev);
+ return radeon_do_resume_cp(dev, file_priv);
}
int radeon_engine_reset(struct drm_device *dev, void *data, struct drm_file *file_priv)