Drop merged patches: 01-compat-make-compat_alloc_user_space-incorporate-the-access_ok-check.patch 02-compat-test-rax-for-the-system-call-number-not-eax.patch 03-compat-retruncate-rax-after-ia32-syscall-entry-tracing.patch hid-01-usbhid-initialize-interface-pointers-early-enough.patch hid-02-fix-suspend-crash-by-moving-initializations-earlier.patch alsa-seq-oss-fix-double-free-at-error-path-of-snd_seq_oss_open.patch tracing-do-not-allow-llseek-to-set_ftrace_filter.patch
85406 lines
2.6 MiB
85406 lines
2.6 MiB
commit 8f6aa16e3a85c732763458c07db65035e75e5906
|
|
Author: Jerome Glisse <jglisse@redhat.com>
|
|
Date: Mon Feb 15 21:36:33 2010 +0100
|
|
|
|
drm/radeon/kms: fix bo's fence association
|
|
|
|
Previous code did associate fence to bo before the fence was emited
|
|
and it also didn't lock protected access to ttm sync_obj member.
|
|
Both of this flaw leads to possible race between different code
|
|
path. This patch fix this by associating fence only once the fence
|
|
is emitted and properly lock protect access to sync_obj member of
|
|
ttm.
|
|
|
|
Fix:
|
|
https://bugs.freedesktop.org/show_bug.cgi?id=26438
|
|
and likely similar others bugs
|
|
Signed-off-by: Jerome Glisse <jglisse@redhat.com>
|
|
Signed-off-by: Dave Airlie <airlied@redhat.com>
|
|
|
|
commit 3a84c7bf19624ef6c5927eaf742adf346be1f34d
|
|
Author: Jerome Glisse <jglisse@redhat.com>
|
|
Date: Mon Feb 15 21:36:13 2010 +0100
|
|
|
|
drm/radeon/kms: fix indirect buffer management V2
|
|
|
|
There is 3 different distinct states for an indirect buffer (IB) :
|
|
1- free with no fence
|
|
2- free with a fence
|
|
3- non free (fence doesn't matter)
|
|
Previous code mixed case 2 & 3 in a single one leading to possible
|
|
catastrophique failure. This patch rework the handling and properly
|
|
separate each case. So when you get ib we set the ib as non free and
|
|
fence status doesn't matter. Fence become active (ie has a meaning
|
|
for the ib code) once the ib is scheduled or free. This patch also
|
|
get rid of the alloc bitmap as it was overkill, we know go through
|
|
IB pool list like in a ring buffer as the oldest IB is the first
|
|
one the will be free.
|
|
|
|
Fix :
|
|
https://bugs.freedesktop.org/show_bug.cgi?id=26438
|
|
and likely other bugs.
|
|
|
|
V2 remove the scheduled list, it's useless now, fix free ib scanning
|
|
|
|
Signed-off-by: Jerome Glisse <jglisse@redhat.com>
|
|
Signed-off-by: Dave Airlie <airlied@redhat.com>
|
|
|
|
commit c4716196dd78a7adfd400612d1c8465bf0bd3fb5
|
|
Author: Dave Airlie <airlied@redhat.com>
|
|
Date: Mon Feb 15 15:54:45 2010 +1000
|
|
|
|
drm/radeon/kms: flush HDP cache on GART table updates.
|
|
|
|
Suggested by Alex Deucher @ AMD
|
|
|
|
Signed-off-by: Dave Airlie <airlied@redhat.com>
|
|
|
|
commit 0f5980d95024ff0cf2e495d977db9c26e1a6765b
|
|
Author: Dave Airlie <airlied@redhat.com>
|
|
Date: Thu Feb 11 15:38:23 2010 +1000
|
|
|
|
drm/radeon/kms: check for valid PCI bios and not OF rom
|
|
|
|
stops us trying to treat a OF rom as a PCI rom.
|
|
|
|
Signed-off-by: Dave Airlie <airlied@redhat.com>
|
|
|
|
commit b1bd3bb9f2f5522415497a075313c8a2ba1cc16c
|
|
Author: Dave Airlie <airlied@redhat.com>
|
|
Date: Sun Jan 31 07:07:14 2010 +1000
|
|
|
|
drm/radeon/kms: use udelay for short delays
|
|
|
|
For usec delays use udelay instead of scheduling, this should
|
|
allow reclocking to happen faster.
|
|
|
|
Signed-off-by: Dave Airlie <airlied@redhat.com>
|
|
|
|
commit ce2440648005a532bffd3fbe8590a085d8869620
|
|
Author: Alex Deucher <alexdeucher@gmail.com>
|
|
Date: Thu Feb 4 11:01:52 2010 -0500
|
|
|
|
drm/radeon/kms/r600: reduce gpu cache flushing
|
|
|
|
Only flush the gpu caches before we submit a fence.
|
|
This leads to a small performance boost when we take
|
|
the extra gpu cache flushes out of the ddx and mesa.
|
|
Once this patch is in and the drm version is bumped,
|
|
we can remove the flushes from the ddx and drm.
|
|
|
|
Also, remove the extra cache flushes from the blit
|
|
routine.
|
|
|
|
Signed-off-by: Alex Deucher <alexdeucher@gmail.com>
|
|
Signed-off-by: Dave Airlie <airlied@redhat.com>
|
|
|
|
commit 03b6599497a283874236163c63f8f50045e5f8f1
|
|
Author: Alex Deucher <alexdeucher@gmail.com>
|
|
Date: Fri Feb 5 04:21:19 2010 -0500
|
|
|
|
drm/radeon/kms: add support for hardcoded edids in rom (v2)
|
|
|
|
Some servers hardcode an edid in rom so that they will
|
|
work properly with KVMs. This is a port of the relevant
|
|
code from the ddx.
|
|
|
|
[airlied: reworked to validate edid at boot stage - and
|
|
remove special quirk, if there is a valid EDID in the BIOS rom
|
|
we'll just try and use it.]
|
|
|
|
Signed-off-by: Alex Deucher <alexdeucher@gmail.com>
|
|
Signed-off-by: Dave Airlie <airlied@redhat.com>
|
|
|
|
commit 92d87a03788e038b3028653e736d4c51090317ac
|
|
Author: Alex Deucher <alexdeucher@gmail.com>
|
|
Date: Fri Feb 5 03:34:16 2010 -0500
|
|
|
|
drm/radeon/kms: add workaround for rn50/rv100 servers
|
|
|
|
Some servers have two VGA ports but only report
|
|
one in the bios connector tables. On these systems
|
|
always set up the TV DAC so that it displays properly
|
|
even if the bios is wrong.
|
|
|
|
Signed-off-by: Alex Deucher <alexdeucher@gmail.com>
|
|
Signed-off-by: Dave Airlie <airlied@redhat.com>
|
|
|
|
commit b3ba69acb361aa6396ad9e55c557fb9b47a0f43e
|
|
Author: Dave Airlie <airlied@redhat.com>
|
|
Date: Wed Feb 17 10:34:58 2010 +1000
|
|
|
|
drm: backport 2.6.33-rc8 drm
|
|
|
|
commit ffff5f5e2d9de68dc454ec85bafa9da94f10f204
|
|
Author: Dave Chinner <david@fromorbit.com>
|
|
Date: Tue Jan 12 17:39:16 2010 +1100
|
|
|
|
lib: Introduce generic list_sort function
|
|
|
|
There are two copies of list_sort() in the tree already, one in the DRM
|
|
code, another in ubifs. Now XFS needs this as well. Create a generic
|
|
list_sort() function from the ubifs version and convert existing users
|
|
to it so we don't end up with yet another copy in the tree.
|
|
|
|
Signed-off-by: Dave Chinner <david@fromorbit.com>
|
|
Acked-by: Dave Airlie <airlied@redhat.com>
|
|
Acked-by: Artem Bityutskiy <dedekind@infradead.org>
|
|
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
|
|
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
|
|
index 96eddd1..305c590 100644
|
|
--- a/drivers/gpu/drm/Kconfig
|
|
+++ b/drivers/gpu/drm/Kconfig
|
|
@@ -66,6 +66,8 @@ config DRM_RADEON
|
|
|
|
If M is selected, the module will be called radeon.
|
|
|
|
+source "drivers/gpu/drm/radeon/Kconfig"
|
|
+
|
|
config DRM_I810
|
|
tristate "Intel I810"
|
|
depends on DRM && AGP && AGP_INTEL
|
|
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
|
|
index 3c8827a..39c5aa7 100644
|
|
--- a/drivers/gpu/drm/Makefile
|
|
+++ b/drivers/gpu/drm/Makefile
|
|
@@ -15,7 +15,7 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \
|
|
|
|
drm-$(CONFIG_COMPAT) += drm_ioc32.o
|
|
|
|
-drm_kms_helper-y := drm_fb_helper.o drm_crtc_helper.o
|
|
+drm_kms_helper-y := drm_fb_helper.o drm_crtc_helper.o drm_dp_i2c_helper.o
|
|
|
|
obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
|
|
|
|
@@ -30,4 +30,7 @@ obj-$(CONFIG_DRM_I830) += i830/
|
|
obj-$(CONFIG_DRM_I915) += i915/
|
|
obj-$(CONFIG_DRM_SIS) += sis/
|
|
obj-$(CONFIG_DRM_SAVAGE)+= savage/
|
|
+obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
|
|
obj-$(CONFIG_DRM_VIA) +=via/
|
|
+obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
|
|
+obj-y += i2c/
|
|
diff --git a/drivers/gpu/drm/ati_pcigart.c b/drivers/gpu/drm/ati_pcigart.c
|
|
index a1fce68..17be051 100644
|
|
--- a/drivers/gpu/drm/ati_pcigart.c
|
|
+++ b/drivers/gpu/drm/ati_pcigart.c
|
|
@@ -113,7 +113,7 @@ int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *ga
|
|
|
|
if (pci_set_dma_mask(dev->pdev, gart_info->table_mask)) {
|
|
DRM_ERROR("fail to set dma mask to 0x%Lx\n",
|
|
- gart_info->table_mask);
|
|
+ (unsigned long long)gart_info->table_mask);
|
|
ret = 1;
|
|
goto done;
|
|
}
|
|
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
|
|
index 5cae0b3..d91fb8c 100644
|
|
--- a/drivers/gpu/drm/drm_crtc.c
|
|
+++ b/drivers/gpu/drm/drm_crtc.c
|
|
@@ -125,6 +125,15 @@ static struct drm_prop_enum_list drm_tv_subconnector_enum_list[] =
|
|
DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
|
|
drm_tv_subconnector_enum_list)
|
|
|
|
+static struct drm_prop_enum_list drm_dirty_info_enum_list[] = {
|
|
+ { DRM_MODE_DIRTY_OFF, "Off" },
|
|
+ { DRM_MODE_DIRTY_ON, "On" },
|
|
+ { DRM_MODE_DIRTY_ANNOTATE, "Annotate" },
|
|
+};
|
|
+
|
|
+DRM_ENUM_NAME_FN(drm_get_dirty_info_name,
|
|
+ drm_dirty_info_enum_list)
|
|
+
|
|
struct drm_conn_prop_enum_list {
|
|
int type;
|
|
char *name;
|
|
@@ -149,6 +158,7 @@ static struct drm_conn_prop_enum_list drm_connector_enum_list[] =
|
|
{ DRM_MODE_CONNECTOR_HDMIA, "HDMI Type A", 0 },
|
|
{ DRM_MODE_CONNECTOR_HDMIB, "HDMI Type B", 0 },
|
|
{ DRM_MODE_CONNECTOR_TV, "TV", 0 },
|
|
+ { DRM_MODE_CONNECTOR_eDP, "Embedded DisplayPort", 0 },
|
|
};
|
|
|
|
static struct drm_prop_enum_list drm_encoder_enum_list[] =
|
|
@@ -247,7 +257,8 @@ static void drm_mode_object_put(struct drm_device *dev,
|
|
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 *drm_mode_object_find(struct drm_device *dev,
|
|
+ uint32_t id, uint32_t type)
|
|
{
|
|
struct drm_mode_object *obj = NULL;
|
|
|
|
@@ -272,7 +283,7 @@ EXPORT_SYMBOL(drm_mode_object_find);
|
|
* functions & device file and adds it to the master fd list.
|
|
*
|
|
* RETURNS:
|
|
- * Zero on success, error code on falure.
|
|
+ * Zero on success, error code on failure.
|
|
*/
|
|
int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
|
|
const struct drm_framebuffer_funcs *funcs)
|
|
@@ -802,6 +813,36 @@ int drm_mode_create_dithering_property(struct drm_device *dev)
|
|
EXPORT_SYMBOL(drm_mode_create_dithering_property);
|
|
|
|
/**
|
|
+ * drm_mode_create_dirty_property - create dirty property
|
|
+ * @dev: DRM device
|
|
+ *
|
|
+ * Called by a driver the first time it's needed, must be attached to desired
|
|
+ * connectors.
|
|
+ */
|
|
+int drm_mode_create_dirty_info_property(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_property *dirty_info;
|
|
+ int i;
|
|
+
|
|
+ if (dev->mode_config.dirty_info_property)
|
|
+ return 0;
|
|
+
|
|
+ dirty_info =
|
|
+ drm_property_create(dev, DRM_MODE_PROP_ENUM |
|
|
+ DRM_MODE_PROP_IMMUTABLE,
|
|
+ "dirty",
|
|
+ ARRAY_SIZE(drm_dirty_info_enum_list));
|
|
+ for (i = 0; i < ARRAY_SIZE(drm_dirty_info_enum_list); i++)
|
|
+ drm_property_add_enum(dirty_info, i,
|
|
+ drm_dirty_info_enum_list[i].type,
|
|
+ drm_dirty_info_enum_list[i].name);
|
|
+ dev->mode_config.dirty_info_property = dirty_info;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL(drm_mode_create_dirty_info_property);
|
|
+
|
|
+/**
|
|
* drm_mode_config_init - initialize DRM mode_configuration structure
|
|
* @dev: DRM device
|
|
*
|
|
@@ -1753,6 +1794,71 @@ out:
|
|
return ret;
|
|
}
|
|
|
|
+int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
|
|
+ void *data, struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_clip_rect __user *clips_ptr;
|
|
+ struct drm_clip_rect *clips = NULL;
|
|
+ struct drm_mode_fb_dirty_cmd *r = data;
|
|
+ struct drm_mode_object *obj;
|
|
+ struct drm_framebuffer *fb;
|
|
+ unsigned flags;
|
|
+ int num_clips;
|
|
+ int ret = 0;
|
|
+
|
|
+ mutex_lock(&dev->mode_config.mutex);
|
|
+ obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB);
|
|
+ if (!obj) {
|
|
+ DRM_ERROR("invalid framebuffer id\n");
|
|
+ ret = -EINVAL;
|
|
+ goto out_err1;
|
|
+ }
|
|
+ fb = obj_to_fb(obj);
|
|
+
|
|
+ num_clips = r->num_clips;
|
|
+ clips_ptr = (struct drm_clip_rect *)(unsigned long)r->clips_ptr;
|
|
+
|
|
+ if (!num_clips != !clips_ptr) {
|
|
+ ret = -EINVAL;
|
|
+ goto out_err1;
|
|
+ }
|
|
+
|
|
+ flags = DRM_MODE_FB_DIRTY_FLAGS & r->flags;
|
|
+
|
|
+ /* If userspace annotates copy, clips must come in pairs */
|
|
+ if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY && (num_clips % 2)) {
|
|
+ ret = -EINVAL;
|
|
+ goto out_err1;
|
|
+ }
|
|
+
|
|
+ if (num_clips && clips_ptr) {
|
|
+ clips = kzalloc(num_clips * sizeof(*clips), GFP_KERNEL);
|
|
+ if (!clips) {
|
|
+ ret = -ENOMEM;
|
|
+ goto out_err1;
|
|
+ }
|
|
+
|
|
+ ret = copy_from_user(clips, clips_ptr,
|
|
+ num_clips * sizeof(*clips));
|
|
+ if (ret)
|
|
+ goto out_err2;
|
|
+ }
|
|
+
|
|
+ if (fb->funcs->dirty) {
|
|
+ ret = fb->funcs->dirty(fb, flags, r->color, clips, num_clips);
|
|
+ } else {
|
|
+ ret = -ENOSYS;
|
|
+ goto out_err2;
|
|
+ }
|
|
+
|
|
+out_err2:
|
|
+ kfree(clips);
|
|
+out_err1:
|
|
+ mutex_unlock(&dev->mode_config.mutex);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
/**
|
|
* drm_fb_release - remove and free the FBs on this file
|
|
* @filp: file * from the ioctl
|
|
@@ -2328,7 +2434,7 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
|
|
} else if (connector->funcs->set_property)
|
|
ret = connector->funcs->set_property(connector, property, out_resp->value);
|
|
|
|
- /* store the property value if succesful */
|
|
+ /* store the property value if successful */
|
|
if (!ret)
|
|
drm_connector_property_set_value(connector, property, out_resp->value);
|
|
out:
|
|
@@ -2478,3 +2584,72 @@ out:
|
|
mutex_unlock(&dev->mode_config.mutex);
|
|
return ret;
|
|
}
|
|
+
|
|
+int drm_mode_page_flip_ioctl(struct drm_device *dev,
|
|
+ void *data, struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_mode_crtc_page_flip *page_flip = data;
|
|
+ struct drm_mode_object *obj;
|
|
+ struct drm_crtc *crtc;
|
|
+ struct drm_framebuffer *fb;
|
|
+ struct drm_pending_vblank_event *e = NULL;
|
|
+ unsigned long flags;
|
|
+ int ret = -EINVAL;
|
|
+
|
|
+ if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS ||
|
|
+ page_flip->reserved != 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ mutex_lock(&dev->mode_config.mutex);
|
|
+ obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC);
|
|
+ if (!obj)
|
|
+ goto out;
|
|
+ crtc = obj_to_crtc(obj);
|
|
+
|
|
+ if (crtc->funcs->page_flip == NULL)
|
|
+ goto out;
|
|
+
|
|
+ obj = drm_mode_object_find(dev, page_flip->fb_id, DRM_MODE_OBJECT_FB);
|
|
+ if (!obj)
|
|
+ goto out;
|
|
+ fb = obj_to_fb(obj);
|
|
+
|
|
+ if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
|
|
+ ret = -ENOMEM;
|
|
+ spin_lock_irqsave(&dev->event_lock, flags);
|
|
+ if (file_priv->event_space < sizeof e->event) {
|
|
+ spin_unlock_irqrestore(&dev->event_lock, flags);
|
|
+ goto out;
|
|
+ }
|
|
+ file_priv->event_space -= sizeof e->event;
|
|
+ spin_unlock_irqrestore(&dev->event_lock, flags);
|
|
+
|
|
+ e = kzalloc(sizeof *e, GFP_KERNEL);
|
|
+ if (e == NULL) {
|
|
+ spin_lock_irqsave(&dev->event_lock, flags);
|
|
+ file_priv->event_space += sizeof e->event;
|
|
+ spin_unlock_irqrestore(&dev->event_lock, flags);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ e->event.base.type = DRM_EVENT_FLIP_COMPLETE;
|
|
+ e->event.base.length = sizeof e->event;
|
|
+ e->event.user_data = page_flip->user_data;
|
|
+ e->base.event = &e->event.base;
|
|
+ e->base.file_priv = file_priv;
|
|
+ e->base.destroy =
|
|
+ (void (*) (struct drm_pending_event *)) kfree;
|
|
+ }
|
|
+
|
|
+ ret = crtc->funcs->page_flip(crtc, fb, e);
|
|
+ if (ret) {
|
|
+ spin_lock_irqsave(&dev->event_lock, flags);
|
|
+ file_priv->event_space += sizeof e->event;
|
|
+ spin_unlock_irqrestore(&dev->event_lock, flags);
|
|
+ kfree(e);
|
|
+ }
|
|
+
|
|
+out:
|
|
+ mutex_unlock(&dev->mode_config.mutex);
|
|
+ return ret;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
|
|
index afed886..7d0f00a 100644
|
|
--- a/drivers/gpu/drm/drm_crtc_helper.c
|
|
+++ b/drivers/gpu/drm/drm_crtc_helper.c
|
|
@@ -109,7 +109,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
|
|
|
|
count = (*connector_funcs->get_modes)(connector);
|
|
if (!count) {
|
|
- count = drm_add_modes_noedid(connector, 800, 600);
|
|
+ count = drm_add_modes_noedid(connector, 1024, 768);
|
|
if (!count)
|
|
return 0;
|
|
}
|
|
@@ -216,7 +216,7 @@ bool drm_helper_crtc_in_use(struct drm_crtc *crtc)
|
|
EXPORT_SYMBOL(drm_helper_crtc_in_use);
|
|
|
|
/**
|
|
- * drm_disable_unused_functions - disable unused objects
|
|
+ * drm_helper_disable_unused_functions - disable unused objects
|
|
* @dev: DRM device
|
|
*
|
|
* LOCKING:
|
|
@@ -702,7 +702,7 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
|
|
if (encoder->crtc != crtc)
|
|
continue;
|
|
|
|
- DRM_INFO("%s: set mode %s %x\n", drm_get_encoder_name(encoder),
|
|
+ DRM_DEBUG("%s: set mode %s %x\n", drm_get_encoder_name(encoder),
|
|
mode->name, mode->base.id);
|
|
encoder_funcs = encoder->helper_private;
|
|
encoder_funcs->mode_set(encoder, mode, adjusted_mode);
|
|
@@ -1032,7 +1032,8 @@ bool drm_helper_initial_config(struct drm_device *dev)
|
|
/*
|
|
* we shouldn't end up with no modes here.
|
|
*/
|
|
- WARN(!count, "No connectors reported connected with modes\n");
|
|
+ if (count == 0)
|
|
+ printk(KERN_INFO "No connectors reported connected with modes\n");
|
|
|
|
drm_setup_crtcs(dev);
|
|
|
|
@@ -1162,6 +1163,9 @@ EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct);
|
|
int drm_helper_resume_force_mode(struct drm_device *dev)
|
|
{
|
|
struct drm_crtc *crtc;
|
|
+ struct drm_encoder *encoder;
|
|
+ struct drm_encoder_helper_funcs *encoder_funcs;
|
|
+ struct drm_crtc_helper_funcs *crtc_funcs;
|
|
int ret;
|
|
|
|
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
|
@@ -1174,6 +1178,25 @@ int drm_helper_resume_force_mode(struct drm_device *dev)
|
|
|
|
if (ret == false)
|
|
DRM_ERROR("failed to set mode on crtc %p\n", crtc);
|
|
+
|
|
+ /* Turn off outputs that were already powered off */
|
|
+ if (drm_helper_choose_crtc_dpms(crtc)) {
|
|
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
|
+
|
|
+ if(encoder->crtc != crtc)
|
|
+ continue;
|
|
+
|
|
+ encoder_funcs = encoder->helper_private;
|
|
+ if (encoder_funcs->dpms)
|
|
+ (*encoder_funcs->dpms) (encoder,
|
|
+ drm_helper_choose_encoder_dpms(encoder));
|
|
+
|
|
+ crtc_funcs = crtc->helper_private;
|
|
+ if (crtc_funcs->dpms)
|
|
+ (*crtc_funcs->dpms) (crtc,
|
|
+ drm_helper_choose_crtc_dpms(crtc));
|
|
+ }
|
|
+ }
|
|
}
|
|
/* disable the unused connectors while restoring the modesetting */
|
|
drm_helper_disable_unused_functions(dev);
|
|
diff --git a/drivers/gpu/drm/drm_dp_i2c_helper.c b/drivers/gpu/drm/drm_dp_i2c_helper.c
|
|
new file mode 100644
|
|
index 0000000..548887c
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/drm_dp_i2c_helper.c
|
|
@@ -0,0 +1,209 @@
|
|
+/*
|
|
+ * Copyright © 2009 Keith Packard
|
|
+ *
|
|
+ * Permission to use, copy, modify, distribute, and sell this software and its
|
|
+ * documentation for any purpose is hereby granted without fee, provided that
|
|
+ * the above copyright notice appear in all copies and that both that copyright
|
|
+ * notice and this permission notice appear in supporting documentation, and
|
|
+ * that the name of the copyright holders not be used in advertising or
|
|
+ * publicity pertaining to distribution of the software without specific,
|
|
+ * written prior permission. The copyright holders make no representations
|
|
+ * about the suitability of this software for any purpose. It is provided "as
|
|
+ * is" without express or implied warranty.
|
|
+ *
|
|
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
|
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
|
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
|
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
|
+ * OF THIS SOFTWARE.
|
|
+ */
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/errno.h>
|
|
+#include <linux/sched.h>
|
|
+#include <linux/i2c.h>
|
|
+#include "drm_dp_helper.h"
|
|
+#include "drmP.h"
|
|
+
|
|
+/* Run a single AUX_CH I2C transaction, writing/reading data as necessary */
|
|
+static int
|
|
+i2c_algo_dp_aux_transaction(struct i2c_adapter *adapter, int mode,
|
|
+ uint8_t write_byte, uint8_t *read_byte)
|
|
+{
|
|
+ struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
|
|
+ int ret;
|
|
+
|
|
+ ret = (*algo_data->aux_ch)(adapter, mode,
|
|
+ write_byte, read_byte);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * I2C over AUX CH
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * Send the address. If the I2C link is running, this 'restarts'
|
|
+ * the connection with the new address, this is used for doing
|
|
+ * a write followed by a read (as needed for DDC)
|
|
+ */
|
|
+static int
|
|
+i2c_algo_dp_aux_address(struct i2c_adapter *adapter, u16 address, bool reading)
|
|
+{
|
|
+ struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
|
|
+ int mode = MODE_I2C_START;
|
|
+ int ret;
|
|
+
|
|
+ if (reading)
|
|
+ mode |= MODE_I2C_READ;
|
|
+ else
|
|
+ mode |= MODE_I2C_WRITE;
|
|
+ algo_data->address = address;
|
|
+ algo_data->running = true;
|
|
+ ret = i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Stop the I2C transaction. This closes out the link, sending
|
|
+ * a bare address packet with the MOT bit turned off
|
|
+ */
|
|
+static void
|
|
+i2c_algo_dp_aux_stop(struct i2c_adapter *adapter, bool reading)
|
|
+{
|
|
+ struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
|
|
+ int mode = MODE_I2C_STOP;
|
|
+
|
|
+ if (reading)
|
|
+ mode |= MODE_I2C_READ;
|
|
+ else
|
|
+ mode |= MODE_I2C_WRITE;
|
|
+ if (algo_data->running) {
|
|
+ (void) i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL);
|
|
+ algo_data->running = false;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Write a single byte to the current I2C address, the
|
|
+ * the I2C link must be running or this returns -EIO
|
|
+ */
|
|
+static int
|
|
+i2c_algo_dp_aux_put_byte(struct i2c_adapter *adapter, u8 byte)
|
|
+{
|
|
+ struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
|
|
+ int ret;
|
|
+
|
|
+ if (!algo_data->running)
|
|
+ return -EIO;
|
|
+
|
|
+ ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_WRITE, byte, NULL);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read a single byte from the current I2C address, the
|
|
+ * I2C link must be running or this returns -EIO
|
|
+ */
|
|
+static int
|
|
+i2c_algo_dp_aux_get_byte(struct i2c_adapter *adapter, u8 *byte_ret)
|
|
+{
|
|
+ struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
|
|
+ int ret;
|
|
+
|
|
+ if (!algo_data->running)
|
|
+ return -EIO;
|
|
+
|
|
+ ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_READ, 0, byte_ret);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int
|
|
+i2c_algo_dp_aux_xfer(struct i2c_adapter *adapter,
|
|
+ struct i2c_msg *msgs,
|
|
+ int num)
|
|
+{
|
|
+ int ret = 0;
|
|
+ bool reading = false;
|
|
+ int m;
|
|
+ int b;
|
|
+
|
|
+ for (m = 0; m < num; m++) {
|
|
+ u16 len = msgs[m].len;
|
|
+ u8 *buf = msgs[m].buf;
|
|
+ reading = (msgs[m].flags & I2C_M_RD) != 0;
|
|
+ ret = i2c_algo_dp_aux_address(adapter, msgs[m].addr, reading);
|
|
+ if (ret < 0)
|
|
+ break;
|
|
+ if (reading) {
|
|
+ for (b = 0; b < len; b++) {
|
|
+ ret = i2c_algo_dp_aux_get_byte(adapter, &buf[b]);
|
|
+ if (ret < 0)
|
|
+ break;
|
|
+ }
|
|
+ } else {
|
|
+ for (b = 0; b < len; b++) {
|
|
+ ret = i2c_algo_dp_aux_put_byte(adapter, buf[b]);
|
|
+ if (ret < 0)
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (ret < 0)
|
|
+ break;
|
|
+ }
|
|
+ if (ret >= 0)
|
|
+ ret = num;
|
|
+ i2c_algo_dp_aux_stop(adapter, reading);
|
|
+ DRM_DEBUG_KMS("dp_aux_xfer return %d\n", ret);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static u32
|
|
+i2c_algo_dp_aux_functionality(struct i2c_adapter *adapter)
|
|
+{
|
|
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
|
|
+ I2C_FUNC_SMBUS_READ_BLOCK_DATA |
|
|
+ I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
|
|
+ I2C_FUNC_10BIT_ADDR;
|
|
+}
|
|
+
|
|
+static const struct i2c_algorithm i2c_dp_aux_algo = {
|
|
+ .master_xfer = i2c_algo_dp_aux_xfer,
|
|
+ .functionality = i2c_algo_dp_aux_functionality,
|
|
+};
|
|
+
|
|
+static void
|
|
+i2c_dp_aux_reset_bus(struct i2c_adapter *adapter)
|
|
+{
|
|
+ (void) i2c_algo_dp_aux_address(adapter, 0, false);
|
|
+ (void) i2c_algo_dp_aux_stop(adapter, false);
|
|
+
|
|
+}
|
|
+
|
|
+static int
|
|
+i2c_dp_aux_prepare_bus(struct i2c_adapter *adapter)
|
|
+{
|
|
+ adapter->algo = &i2c_dp_aux_algo;
|
|
+ adapter->retries = 3;
|
|
+ i2c_dp_aux_reset_bus(adapter);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+i2c_dp_aux_add_bus(struct i2c_adapter *adapter)
|
|
+{
|
|
+ int error;
|
|
+
|
|
+ error = i2c_dp_aux_prepare_bus(adapter);
|
|
+ if (error)
|
|
+ return error;
|
|
+ error = i2c_add_adapter(adapter);
|
|
+ return error;
|
|
+}
|
|
+EXPORT_SYMBOL(i2c_dp_aux_add_bus);
|
|
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
|
|
index a75ca63..766c468 100644
|
|
--- a/drivers/gpu/drm/drm_drv.c
|
|
+++ b/drivers/gpu/drm/drm_drv.c
|
|
@@ -145,6 +145,8 @@ static struct drm_ioctl_desc drm_ioctls[] = {
|
|
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_MASTER|DRM_CONTROL_ALLOW),
|
|
DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_MASTER|DRM_CONTROL_ALLOW),
|
|
DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_MASTER|DRM_CONTROL_ALLOW),
|
|
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW),
|
|
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW)
|
|
};
|
|
|
|
#define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls )
|
|
@@ -366,6 +368,29 @@ module_init(drm_core_init);
|
|
module_exit(drm_core_exit);
|
|
|
|
/**
|
|
+ * Copy and IOCTL return string to user space
|
|
+ */
|
|
+static int drm_copy_field(char *buf, size_t *buf_len, const char *value)
|
|
+{
|
|
+ int len;
|
|
+
|
|
+ /* don't overflow userbuf */
|
|
+ len = strlen(value);
|
|
+ if (len > *buf_len)
|
|
+ len = *buf_len;
|
|
+
|
|
+ /* let userspace know exact length of driver value (which could be
|
|
+ * larger than the userspace-supplied buffer) */
|
|
+ *buf_len = strlen(value);
|
|
+
|
|
+ /* finally, try filling in the userbuf */
|
|
+ if (len && buf)
|
|
+ if (copy_to_user(buf, value, len))
|
|
+ return -EFAULT;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
* Get version information
|
|
*
|
|
* \param inode device inode.
|
|
@@ -380,16 +405,21 @@ static int drm_version(struct drm_device *dev, void *data,
|
|
struct drm_file *file_priv)
|
|
{
|
|
struct drm_version *version = data;
|
|
- int len;
|
|
+ int err;
|
|
|
|
version->version_major = dev->driver->major;
|
|
version->version_minor = dev->driver->minor;
|
|
version->version_patchlevel = dev->driver->patchlevel;
|
|
- DRM_COPY(version->name, dev->driver->name);
|
|
- DRM_COPY(version->date, dev->driver->date);
|
|
- DRM_COPY(version->desc, dev->driver->desc);
|
|
-
|
|
- return 0;
|
|
+ err = drm_copy_field(version->name, &version->name_len,
|
|
+ dev->driver->name);
|
|
+ if (!err)
|
|
+ err = drm_copy_field(version->date, &version->date_len,
|
|
+ dev->driver->date);
|
|
+ if (!err)
|
|
+ err = drm_copy_field(version->desc, &version->desc_len,
|
|
+ dev->driver->desc);
|
|
+
|
|
+ return err;
|
|
}
|
|
|
|
/**
|
|
@@ -404,11 +434,11 @@ static int drm_version(struct drm_device *dev, void *data,
|
|
* Looks up the ioctl function in the ::ioctls table, checking for root
|
|
* previleges if so required, and dispatches to the respective function.
|
|
*/
|
|
-int drm_ioctl(struct inode *inode, struct file *filp,
|
|
+long drm_ioctl(struct file *filp,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
struct drm_file *file_priv = filp->private_data;
|
|
- struct drm_device *dev = file_priv->minor->dev;
|
|
+ struct drm_device *dev;
|
|
struct drm_ioctl_desc *ioctl;
|
|
drm_ioctl_t *func;
|
|
unsigned int nr = DRM_IOCTL_NR(cmd);
|
|
@@ -416,6 +446,7 @@ int drm_ioctl(struct inode *inode, struct file *filp,
|
|
char stack_kdata[128];
|
|
char *kdata = NULL;
|
|
|
|
+ dev = file_priv->minor->dev;
|
|
atomic_inc(&dev->ioctl_count);
|
|
atomic_inc(&dev->counts[_DRM_STAT_IOCTLS]);
|
|
++file_priv->ioctl_count;
|
|
@@ -471,7 +502,13 @@ int drm_ioctl(struct inode *inode, struct file *filp,
|
|
} else
|
|
memset(kdata, 0, _IOC_SIZE(cmd));
|
|
|
|
- retcode = func(dev, kdata, file_priv);
|
|
+ if (ioctl->flags & DRM_UNLOCKED)
|
|
+ retcode = func(dev, kdata, file_priv);
|
|
+ else {
|
|
+ lock_kernel();
|
|
+ retcode = func(dev, kdata, file_priv);
|
|
+ unlock_kernel();
|
|
+ }
|
|
|
|
if (cmd & IOC_OUT) {
|
|
if (copy_to_user((void __user *)arg, kdata,
|
|
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
|
|
index b54ba63..f41e91c 100644
|
|
--- a/drivers/gpu/drm/drm_edid.c
|
|
+++ b/drivers/gpu/drm/drm_edid.c
|
|
@@ -60,8 +60,7 @@
|
|
#define EDID_QUIRK_FIRST_DETAILED_PREFERRED (1 << 5)
|
|
/* use +hsync +vsync for detailed mode */
|
|
#define EDID_QUIRK_DETAILED_SYNC_PP (1 << 6)
|
|
-/* define the number of Extension EDID block */
|
|
-#define MAX_EDID_EXT_NUM 4
|
|
+
|
|
|
|
#define LEVEL_DMT 0
|
|
#define LEVEL_GTF 1
|
|
@@ -114,27 +113,29 @@ static const u8 edid_header[] = {
|
|
};
|
|
|
|
/**
|
|
- * edid_is_valid - sanity check EDID data
|
|
+ * drm_edid_is_valid - sanity check EDID data
|
|
* @edid: EDID data
|
|
*
|
|
* Sanity check the EDID block by looking at the header, the version number
|
|
* and the checksum. Return 0 if the EDID doesn't check out, or 1 if it's
|
|
* valid.
|
|
*/
|
|
-static bool edid_is_valid(struct edid *edid)
|
|
+bool drm_edid_is_valid(struct edid *edid)
|
|
{
|
|
- int i;
|
|
+ int i, score = 0;
|
|
u8 csum = 0;
|
|
u8 *raw_edid = (u8 *)edid;
|
|
|
|
- if (memcmp(edid->header, edid_header, sizeof(edid_header)))
|
|
- goto bad;
|
|
- if (edid->version != 1) {
|
|
- DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version);
|
|
+ for (i = 0; i < sizeof(edid_header); i++)
|
|
+ if (raw_edid[i] == edid_header[i])
|
|
+ score++;
|
|
+
|
|
+ if (score == 8) ;
|
|
+ else if (score >= 6) {
|
|
+ DRM_DEBUG("Fixing EDID header, your hardware may be failing\n");
|
|
+ memcpy(raw_edid, edid_header, sizeof(edid_header));
|
|
+ } else
|
|
goto bad;
|
|
- }
|
|
- if (edid->revision > 4)
|
|
- DRM_DEBUG("EDID minor > 4, assuming backward compatibility\n");
|
|
|
|
for (i = 0; i < EDID_LENGTH; i++)
|
|
csum += raw_edid[i];
|
|
@@ -143,6 +144,14 @@ static bool edid_is_valid(struct edid *edid)
|
|
goto bad;
|
|
}
|
|
|
|
+ if (edid->version != 1) {
|
|
+ DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version);
|
|
+ goto bad;
|
|
+ }
|
|
+
|
|
+ if (edid->revision > 4)
|
|
+ DRM_DEBUG("EDID minor > 4, assuming backward compatibility\n");
|
|
+
|
|
return 1;
|
|
|
|
bad:
|
|
@@ -153,6 +162,7 @@ bad:
|
|
}
|
|
return 0;
|
|
}
|
|
+EXPORT_SYMBOL(drm_edid_is_valid);
|
|
|
|
/**
|
|
* edid_vendor - match a string against EDID's obfuscated vendor field
|
|
@@ -481,16 +491,17 @@ static struct drm_display_mode drm_dmt_modes[] = {
|
|
3048, 3536, 0, 1600, 1603, 1609, 1682, 0,
|
|
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
};
|
|
+static const int drm_num_dmt_modes =
|
|
+ sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode);
|
|
|
|
static struct drm_display_mode *drm_find_dmt(struct drm_device *dev,
|
|
int hsize, int vsize, int fresh)
|
|
{
|
|
- int i, count;
|
|
+ int i;
|
|
struct drm_display_mode *ptr, *mode;
|
|
|
|
- count = sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode);
|
|
mode = NULL;
|
|
- for (i = 0; i < count; i++) {
|
|
+ for (i = 0; i < drm_num_dmt_modes; i++) {
|
|
ptr = &drm_dmt_modes[i];
|
|
if (hsize == ptr->hdisplay &&
|
|
vsize == ptr->vdisplay &&
|
|
@@ -622,8 +633,7 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
|
|
return NULL;
|
|
}
|
|
if (!(pt->misc & DRM_EDID_PT_SEPARATE_SYNC)) {
|
|
- printk(KERN_WARNING "integrated sync not supported\n");
|
|
- return NULL;
|
|
+ printk(KERN_WARNING "composite sync not supported\n");
|
|
}
|
|
|
|
/* it is incorrect if hsync/vsync width is zero */
|
|
@@ -834,8 +844,169 @@ static int add_standard_modes(struct drm_connector *connector, struct edid *edid
|
|
return modes;
|
|
}
|
|
|
|
+/*
|
|
+ * XXX fix this for:
|
|
+ * - GTF secondary curve formula
|
|
+ * - EDID 1.4 range offsets
|
|
+ * - CVT extended bits
|
|
+ */
|
|
+static bool
|
|
+mode_in_range(struct drm_display_mode *mode, struct detailed_timing *timing)
|
|
+{
|
|
+ struct detailed_data_monitor_range *range;
|
|
+ int hsync, vrefresh;
|
|
+
|
|
+ range = &timing->data.other_data.data.range;
|
|
+
|
|
+ hsync = drm_mode_hsync(mode);
|
|
+ vrefresh = drm_mode_vrefresh(mode);
|
|
+
|
|
+ if (hsync < range->min_hfreq_khz || hsync > range->max_hfreq_khz)
|
|
+ return false;
|
|
+
|
|
+ if (vrefresh < range->min_vfreq || vrefresh > range->max_vfreq)
|
|
+ return false;
|
|
+
|
|
+ if (range->pixel_clock_mhz && range->pixel_clock_mhz != 0xff) {
|
|
+ /* be forgiving since it's in units of 10MHz */
|
|
+ int max_clock = range->pixel_clock_mhz * 10 + 9;
|
|
+ max_clock *= 1000;
|
|
+ if (mode->clock > max_clock)
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * XXX If drm_dmt_modes ever regrows the CVT-R modes (and it will) this will
|
|
+ * need to account for them.
|
|
+ */
|
|
+static int drm_gtf_modes_for_range(struct drm_connector *connector,
|
|
+ struct detailed_timing *timing)
|
|
+{
|
|
+ int i, modes = 0;
|
|
+ struct drm_display_mode *newmode;
|
|
+ struct drm_device *dev = connector->dev;
|
|
+
|
|
+ for (i = 0; i < drm_num_dmt_modes; i++) {
|
|
+ if (mode_in_range(drm_dmt_modes + i, timing)) {
|
|
+ newmode = drm_mode_duplicate(dev, &drm_dmt_modes[i]);
|
|
+ if (newmode) {
|
|
+ drm_mode_probed_add(connector, newmode);
|
|
+ modes++;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return modes;
|
|
+}
|
|
+
|
|
+static int drm_cvt_modes(struct drm_connector *connector,
|
|
+ struct detailed_timing *timing)
|
|
+{
|
|
+ int i, j, modes = 0;
|
|
+ struct drm_display_mode *newmode;
|
|
+ struct drm_device *dev = connector->dev;
|
|
+ struct cvt_timing *cvt;
|
|
+ const int rates[] = { 60, 85, 75, 60, 50 };
|
|
+ const u8 empty[3] = { 0, 0, 0 };
|
|
+
|
|
+ for (i = 0; i < 4; i++) {
|
|
+ int uninitialized_var(width), height;
|
|
+ cvt = &(timing->data.other_data.data.cvt[i]);
|
|
+
|
|
+ if (!memcmp(cvt->code, empty, 3))
|
|
+ continue;
|
|
+
|
|
+ height = (cvt->code[0] + ((cvt->code[1] & 0xf0) << 4) + 1) * 2;
|
|
+ switch (cvt->code[1] & 0x0c) {
|
|
+ case 0x00:
|
|
+ width = height * 4 / 3;
|
|
+ break;
|
|
+ case 0x04:
|
|
+ width = height * 16 / 9;
|
|
+ break;
|
|
+ case 0x08:
|
|
+ width = height * 16 / 10;
|
|
+ break;
|
|
+ case 0x0c:
|
|
+ width = height * 15 / 9;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ for (j = 1; j < 5; j++) {
|
|
+ if (cvt->code[2] & (1 << j)) {
|
|
+ newmode = drm_cvt_mode(dev, width, height,
|
|
+ rates[j], j == 0,
|
|
+ false, false);
|
|
+ if (newmode) {
|
|
+ drm_mode_probed_add(connector, newmode);
|
|
+ modes++;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return modes;
|
|
+}
|
|
+
|
|
+static int add_detailed_modes(struct drm_connector *connector,
|
|
+ struct detailed_timing *timing,
|
|
+ struct edid *edid, u32 quirks, int preferred)
|
|
+{
|
|
+ int i, modes = 0;
|
|
+ struct detailed_non_pixel *data = &timing->data.other_data;
|
|
+ int timing_level = standard_timing_level(edid);
|
|
+ int gtf = (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF);
|
|
+ struct drm_display_mode *newmode;
|
|
+ struct drm_device *dev = connector->dev;
|
|
+
|
|
+ if (timing->pixel_clock) {
|
|
+ newmode = drm_mode_detailed(dev, edid, timing, quirks);
|
|
+ if (!newmode)
|
|
+ return 0;
|
|
+
|
|
+ if (preferred)
|
|
+ newmode->type |= DRM_MODE_TYPE_PREFERRED;
|
|
+
|
|
+ drm_mode_probed_add(connector, newmode);
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ /* other timing types */
|
|
+ switch (data->type) {
|
|
+ case EDID_DETAIL_MONITOR_RANGE:
|
|
+ if (gtf)
|
|
+ modes += drm_gtf_modes_for_range(connector, timing);
|
|
+ break;
|
|
+ case EDID_DETAIL_STD_MODES:
|
|
+ /* Six modes per detailed section */
|
|
+ for (i = 0; i < 6; i++) {
|
|
+ struct std_timing *std;
|
|
+ struct drm_display_mode *newmode;
|
|
+
|
|
+ std = &data->data.timings[i];
|
|
+ newmode = drm_mode_std(dev, std, edid->revision,
|
|
+ timing_level);
|
|
+ if (newmode) {
|
|
+ drm_mode_probed_add(connector, newmode);
|
|
+ modes++;
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ case EDID_DETAIL_CVT_3BYTE:
|
|
+ modes += drm_cvt_modes(connector, timing);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return modes;
|
|
+}
|
|
+
|
|
/**
|
|
- * add_detailed_modes - get detailed mode info from EDID data
|
|
+ * add_detailed_info - get detailed mode info from EDID data
|
|
* @connector: attached connector
|
|
* @edid: EDID block to scan
|
|
* @quirks: quirks to apply
|
|
@@ -846,67 +1017,24 @@ static int add_standard_modes(struct drm_connector *connector, struct edid *edid
|
|
static int add_detailed_info(struct drm_connector *connector,
|
|
struct edid *edid, u32 quirks)
|
|
{
|
|
- struct drm_device *dev = connector->dev;
|
|
- int i, j, modes = 0;
|
|
- int timing_level;
|
|
-
|
|
- timing_level = standard_timing_level(edid);
|
|
+ int i, modes = 0;
|
|
|
|
for (i = 0; i < EDID_DETAILED_TIMINGS; i++) {
|
|
struct detailed_timing *timing = &edid->detailed_timings[i];
|
|
- struct detailed_non_pixel *data = &timing->data.other_data;
|
|
- struct drm_display_mode *newmode;
|
|
+ int preferred = (i == 0) && (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING);
|
|
|
|
- /* X server check is version 1.1 or higher */
|
|
- if (edid->version == 1 && edid->revision >= 1 &&
|
|
- !timing->pixel_clock) {
|
|
- /* Other timing or info */
|
|
- switch (data->type) {
|
|
- case EDID_DETAIL_MONITOR_SERIAL:
|
|
- break;
|
|
- case EDID_DETAIL_MONITOR_STRING:
|
|
- break;
|
|
- case EDID_DETAIL_MONITOR_RANGE:
|
|
- /* Get monitor range data */
|
|
- break;
|
|
- case EDID_DETAIL_MONITOR_NAME:
|
|
- break;
|
|
- case EDID_DETAIL_MONITOR_CPDATA:
|
|
- break;
|
|
- case EDID_DETAIL_STD_MODES:
|
|
- for (j = 0; j < 6; i++) {
|
|
- struct std_timing *std;
|
|
- struct drm_display_mode *newmode;
|
|
-
|
|
- std = &data->data.timings[j];
|
|
- newmode = drm_mode_std(dev, std,
|
|
- edid->revision,
|
|
- timing_level);
|
|
- if (newmode) {
|
|
- drm_mode_probed_add(connector, newmode);
|
|
- modes++;
|
|
- }
|
|
- }
|
|
- break;
|
|
- default:
|
|
- break;
|
|
- }
|
|
- } else {
|
|
- newmode = drm_mode_detailed(dev, edid, timing, quirks);
|
|
- if (!newmode)
|
|
- continue;
|
|
-
|
|
- /* First detailed mode is preferred */
|
|
- if (i == 0 && (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING))
|
|
- newmode->type |= DRM_MODE_TYPE_PREFERRED;
|
|
- drm_mode_probed_add(connector, newmode);
|
|
+ /* In 1.0, only timings are allowed */
|
|
+ if (!timing->pixel_clock && edid->version == 1 &&
|
|
+ edid->revision == 0)
|
|
+ continue;
|
|
|
|
- modes++;
|
|
- }
|
|
+ modes += add_detailed_modes(connector, timing, edid, quirks,
|
|
+ preferred);
|
|
}
|
|
|
|
return modes;
|
|
}
|
|
+
|
|
/**
|
|
* add_detailed_mode_eedid - get detailed mode info from addtional timing
|
|
* EDID block
|
|
@@ -920,12 +1048,9 @@ static int add_detailed_info(struct drm_connector *connector,
|
|
static int add_detailed_info_eedid(struct drm_connector *connector,
|
|
struct edid *edid, u32 quirks)
|
|
{
|
|
- struct drm_device *dev = connector->dev;
|
|
- int i, j, modes = 0;
|
|
+ int i, modes = 0;
|
|
char *edid_ext = NULL;
|
|
struct detailed_timing *timing;
|
|
- struct detailed_non_pixel *data;
|
|
- struct drm_display_mode *newmode;
|
|
int edid_ext_num;
|
|
int start_offset, end_offset;
|
|
int timing_level;
|
|
@@ -944,8 +1069,8 @@ static int add_detailed_info_eedid(struct drm_connector *connector,
|
|
}
|
|
|
|
/* Chose real EDID extension number */
|
|
- edid_ext_num = edid->extensions > MAX_EDID_EXT_NUM ?
|
|
- MAX_EDID_EXT_NUM : edid->extensions;
|
|
+ edid_ext_num = edid->extensions > DRM_MAX_EDID_EXT_NUM ?
|
|
+ DRM_MAX_EDID_EXT_NUM : edid->extensions;
|
|
|
|
/* Find CEA extension */
|
|
for (i = 0; i < edid_ext_num; i++) {
|
|
@@ -976,51 +1101,7 @@ static int add_detailed_info_eedid(struct drm_connector *connector,
|
|
for (i = start_offset; i < end_offset;
|
|
i += sizeof(struct detailed_timing)) {
|
|
timing = (struct detailed_timing *)(edid_ext + i);
|
|
- data = &timing->data.other_data;
|
|
- /* Detailed mode timing */
|
|
- if (timing->pixel_clock) {
|
|
- newmode = drm_mode_detailed(dev, edid, timing, quirks);
|
|
- if (!newmode)
|
|
- continue;
|
|
-
|
|
- drm_mode_probed_add(connector, newmode);
|
|
-
|
|
- modes++;
|
|
- continue;
|
|
- }
|
|
-
|
|
- /* Other timing or info */
|
|
- switch (data->type) {
|
|
- case EDID_DETAIL_MONITOR_SERIAL:
|
|
- break;
|
|
- case EDID_DETAIL_MONITOR_STRING:
|
|
- break;
|
|
- case EDID_DETAIL_MONITOR_RANGE:
|
|
- /* Get monitor range data */
|
|
- break;
|
|
- case EDID_DETAIL_MONITOR_NAME:
|
|
- break;
|
|
- case EDID_DETAIL_MONITOR_CPDATA:
|
|
- break;
|
|
- case EDID_DETAIL_STD_MODES:
|
|
- /* Five modes per detailed section */
|
|
- for (j = 0; j < 5; i++) {
|
|
- struct std_timing *std;
|
|
- struct drm_display_mode *newmode;
|
|
-
|
|
- std = &data->data.timings[j];
|
|
- newmode = drm_mode_std(dev, std,
|
|
- edid->revision,
|
|
- timing_level);
|
|
- if (newmode) {
|
|
- drm_mode_probed_add(connector, newmode);
|
|
- modes++;
|
|
- }
|
|
- }
|
|
- break;
|
|
- default:
|
|
- break;
|
|
- }
|
|
+ modes += add_detailed_modes(connector, timing, edid, quirks, 0);
|
|
}
|
|
|
|
return modes;
|
|
@@ -1066,19 +1147,19 @@ static int drm_ddc_read_edid(struct drm_connector *connector,
|
|
struct i2c_adapter *adapter,
|
|
char *buf, int len)
|
|
{
|
|
- int ret;
|
|
+ int i;
|
|
|
|
- ret = drm_do_probe_ddc_edid(adapter, buf, len);
|
|
- if (ret != 0) {
|
|
- goto end;
|
|
- }
|
|
- if (!edid_is_valid((struct edid *)buf)) {
|
|
- dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n",
|
|
- drm_get_connector_name(connector));
|
|
- ret = -1;
|
|
+ for (i = 0; i < 4; i++) {
|
|
+ if (drm_do_probe_ddc_edid(adapter, buf, len))
|
|
+ return -1;
|
|
+ if (drm_edid_is_valid((struct edid *)buf))
|
|
+ return 0;
|
|
}
|
|
-end:
|
|
- return ret;
|
|
+
|
|
+ /* repeated checksum failures; warn, but carry on */
|
|
+ dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n",
|
|
+ drm_get_connector_name(connector));
|
|
+ return -1;
|
|
}
|
|
|
|
/**
|
|
@@ -1096,7 +1177,7 @@ struct edid *drm_get_edid(struct drm_connector *connector,
|
|
int ret;
|
|
struct edid *edid;
|
|
|
|
- edid = kmalloc(EDID_LENGTH * (MAX_EDID_EXT_NUM + 1),
|
|
+ edid = kmalloc(EDID_LENGTH * (DRM_MAX_EDID_EXT_NUM + 1),
|
|
GFP_KERNEL);
|
|
if (edid == NULL) {
|
|
dev_warn(&connector->dev->pdev->dev,
|
|
@@ -1114,14 +1195,14 @@ struct edid *drm_get_edid(struct drm_connector *connector,
|
|
if (edid->extensions != 0) {
|
|
int edid_ext_num = edid->extensions;
|
|
|
|
- if (edid_ext_num > MAX_EDID_EXT_NUM) {
|
|
+ if (edid_ext_num > DRM_MAX_EDID_EXT_NUM) {
|
|
dev_warn(&connector->dev->pdev->dev,
|
|
"The number of extension(%d) is "
|
|
"over max (%d), actually read number (%d)\n",
|
|
- edid_ext_num, MAX_EDID_EXT_NUM,
|
|
- MAX_EDID_EXT_NUM);
|
|
+ edid_ext_num, DRM_MAX_EDID_EXT_NUM,
|
|
+ DRM_MAX_EDID_EXT_NUM);
|
|
/* Reset EDID extension number to be read */
|
|
- edid_ext_num = MAX_EDID_EXT_NUM;
|
|
+ edid_ext_num = DRM_MAX_EDID_EXT_NUM;
|
|
}
|
|
/* Read EDID including extensions too */
|
|
ret = drm_ddc_read_edid(connector, adapter, (char *)edid,
|
|
@@ -1164,8 +1245,8 @@ bool drm_detect_hdmi_monitor(struct edid *edid)
|
|
goto end;
|
|
|
|
/* Chose real EDID extension number */
|
|
- edid_ext_num = edid->extensions > MAX_EDID_EXT_NUM ?
|
|
- MAX_EDID_EXT_NUM : edid->extensions;
|
|
+ edid_ext_num = edid->extensions > DRM_MAX_EDID_EXT_NUM ?
|
|
+ DRM_MAX_EDID_EXT_NUM : edid->extensions;
|
|
|
|
/* Find CEA extension */
|
|
for (i = 0; i < edid_ext_num; i++) {
|
|
@@ -1222,7 +1303,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
|
|
if (edid == NULL) {
|
|
return 0;
|
|
}
|
|
- if (!edid_is_valid(edid)) {
|
|
+ if (!drm_edid_is_valid(edid)) {
|
|
dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n",
|
|
drm_get_connector_name(connector));
|
|
return 0;
|
|
@@ -1296,6 +1377,8 @@ int drm_add_modes_noedid(struct drm_connector *connector,
|
|
ptr->vdisplay > vdisplay)
|
|
continue;
|
|
}
|
|
+ if (drm_mode_vrefresh(ptr) > 61)
|
|
+ continue;
|
|
mode = drm_mode_duplicate(dev, ptr);
|
|
if (mode) {
|
|
drm_mode_probed_add(connector, mode);
|
|
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
|
|
index 65ef011..0f9e905 100644
|
|
--- a/drivers/gpu/drm/drm_fb_helper.c
|
|
+++ b/drivers/gpu/drm/drm_fb_helper.c
|
|
@@ -156,7 +156,7 @@ static bool drm_fb_helper_connector_parse_command_line(struct drm_connector *con
|
|
force = DRM_FORCE_ON;
|
|
break;
|
|
case 'D':
|
|
- if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) ||
|
|
+ if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) &&
|
|
(connector->connector_type != DRM_MODE_CONNECTOR_HDMIB))
|
|
force = DRM_FORCE_ON;
|
|
else
|
|
@@ -373,11 +373,9 @@ static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
|
|
mutex_unlock(&dev->mode_config.mutex);
|
|
}
|
|
}
|
|
- if (dpms_mode == DRM_MODE_DPMS_OFF) {
|
|
- mutex_lock(&dev->mode_config.mutex);
|
|
- crtc_funcs->dpms(crtc, dpms_mode);
|
|
- mutex_unlock(&dev->mode_config.mutex);
|
|
- }
|
|
+ mutex_lock(&dev->mode_config.mutex);
|
|
+ crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
|
|
+ mutex_unlock(&dev->mode_config.mutex);
|
|
}
|
|
}
|
|
}
|
|
@@ -385,18 +383,23 @@ static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
|
|
int drm_fb_helper_blank(int blank, struct fb_info *info)
|
|
{
|
|
switch (blank) {
|
|
+ /* Display: On; HSync: On, VSync: On */
|
|
case FB_BLANK_UNBLANK:
|
|
drm_fb_helper_on(info);
|
|
break;
|
|
+ /* Display: Off; HSync: On, VSync: On */
|
|
case FB_BLANK_NORMAL:
|
|
drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
|
|
break;
|
|
+ /* Display: Off; HSync: Off, VSync: On */
|
|
case FB_BLANK_HSYNC_SUSPEND:
|
|
drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
|
|
break;
|
|
+ /* Display: Off; HSync: On, VSync: Off */
|
|
case FB_BLANK_VSYNC_SUSPEND:
|
|
drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND);
|
|
break;
|
|
+ /* Display: Off; HSync: Off, VSync: Off */
|
|
case FB_BLANK_POWERDOWN:
|
|
drm_fb_helper_off(info, DRM_MODE_DPMS_OFF);
|
|
break;
|
|
@@ -603,11 +606,10 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
|
|
return -EINVAL;
|
|
|
|
/* Need to resize the fb object !!! */
|
|
- if (var->xres > fb->width || var->yres > fb->height) {
|
|
- DRM_ERROR("Requested width/height is greater than current fb "
|
|
- "object %dx%d > %dx%d\n", var->xres, var->yres,
|
|
- fb->width, fb->height);
|
|
- DRM_ERROR("Need resizing code.\n");
|
|
+ if (var->bits_per_pixel > fb->bits_per_pixel || var->xres > fb->width || var->yres > fb->height) {
|
|
+ DRM_DEBUG("fb userspace requested width/height/bpp is greater than current fb "
|
|
+ "object %dx%d-%d > %dx%d-%d\n", var->xres, var->yres, var->bits_per_pixel,
|
|
+ fb->width, fb->height, fb->bits_per_pixel);
|
|
return -EINVAL;
|
|
}
|
|
|
|
@@ -905,8 +907,13 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev,
|
|
|
|
if (new_fb) {
|
|
info->var.pixclock = 0;
|
|
- if (register_framebuffer(info) < 0)
|
|
+ ret = fb_alloc_cmap(&info->cmap, modeset->crtc->gamma_size, 0);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ if (register_framebuffer(info) < 0) {
|
|
+ fb_dealloc_cmap(&info->cmap);
|
|
return -EINVAL;
|
|
+ }
|
|
} else {
|
|
drm_fb_helper_set_par(info);
|
|
}
|
|
@@ -936,6 +943,7 @@ void drm_fb_helper_free(struct drm_fb_helper *helper)
|
|
unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
|
|
}
|
|
drm_fb_helper_crtc_free(helper);
|
|
+ fb_dealloc_cmap(&helper->fb->fbdev->cmap);
|
|
}
|
|
EXPORT_SYMBOL(drm_fb_helper_free);
|
|
|
|
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c
|
|
index 251bc0e..08d14df 100644
|
|
--- a/drivers/gpu/drm/drm_fops.c
|
|
+++ b/drivers/gpu/drm/drm_fops.c
|
|
@@ -257,6 +257,9 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
|
|
|
|
INIT_LIST_HEAD(&priv->lhead);
|
|
INIT_LIST_HEAD(&priv->fbs);
|
|
+ INIT_LIST_HEAD(&priv->event_list);
|
|
+ init_waitqueue_head(&priv->event_wait);
|
|
+ priv->event_space = 4096; /* set aside 4k for event buffer */
|
|
|
|
if (dev->driver->driver_features & DRIVER_GEM)
|
|
drm_gem_open(dev, priv);
|
|
@@ -297,6 +300,18 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
|
|
goto out_free;
|
|
}
|
|
}
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
+ if (dev->driver->master_set) {
|
|
+ ret = dev->driver->master_set(dev, priv, true);
|
|
+ if (ret) {
|
|
+ /* drop both references if this fails */
|
|
+ drm_master_put(&priv->minor->master);
|
|
+ drm_master_put(&priv->master);
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+ goto out_free;
|
|
+ }
|
|
+ }
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
} else {
|
|
/* get a reference to the master */
|
|
priv->master = drm_master_get(priv->minor->master);
|
|
@@ -413,6 +428,30 @@ static void drm_master_release(struct drm_device *dev, struct file *filp)
|
|
}
|
|
}
|
|
|
|
+static void drm_events_release(struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_device *dev = file_priv->minor->dev;
|
|
+ struct drm_pending_event *e, *et;
|
|
+ struct drm_pending_vblank_event *v, *vt;
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&dev->event_lock, flags);
|
|
+
|
|
+ /* Remove pending flips */
|
|
+ list_for_each_entry_safe(v, vt, &dev->vblank_event_list, base.link)
|
|
+ if (v->base.file_priv == file_priv) {
|
|
+ list_del(&v->base.link);
|
|
+ drm_vblank_put(dev, v->pipe);
|
|
+ v->base.destroy(&v->base);
|
|
+ }
|
|
+
|
|
+ /* Remove unconsumed events */
|
|
+ list_for_each_entry_safe(e, et, &file_priv->event_list, link)
|
|
+ e->destroy(e);
|
|
+
|
|
+ spin_unlock_irqrestore(&dev->event_lock, flags);
|
|
+}
|
|
+
|
|
/**
|
|
* Release file.
|
|
*
|
|
@@ -451,6 +490,8 @@ int drm_release(struct inode *inode, struct file *filp)
|
|
if (file_priv->minor->master)
|
|
drm_master_release(dev, filp);
|
|
|
|
+ drm_events_release(file_priv);
|
|
+
|
|
if (dev->driver->driver_features & DRIVER_GEM)
|
|
drm_gem_release(dev, file_priv);
|
|
|
|
@@ -504,6 +545,8 @@ int drm_release(struct inode *inode, struct file *filp)
|
|
|
|
if (file_priv->minor->master == file_priv->master) {
|
|
/* drop the reference held my the minor */
|
|
+ if (dev->driver->master_drop)
|
|
+ dev->driver->master_drop(dev, file_priv, true);
|
|
drm_master_put(&file_priv->minor->master);
|
|
}
|
|
}
|
|
@@ -544,9 +587,74 @@ int drm_release(struct inode *inode, struct file *filp)
|
|
}
|
|
EXPORT_SYMBOL(drm_release);
|
|
|
|
-/** No-op. */
|
|
+static bool
|
|
+drm_dequeue_event(struct drm_file *file_priv,
|
|
+ size_t total, size_t max, struct drm_pending_event **out)
|
|
+{
|
|
+ struct drm_device *dev = file_priv->minor->dev;
|
|
+ struct drm_pending_event *e;
|
|
+ unsigned long flags;
|
|
+ bool ret = false;
|
|
+
|
|
+ spin_lock_irqsave(&dev->event_lock, flags);
|
|
+
|
|
+ *out = NULL;
|
|
+ if (list_empty(&file_priv->event_list))
|
|
+ goto out;
|
|
+ e = list_first_entry(&file_priv->event_list,
|
|
+ struct drm_pending_event, link);
|
|
+ if (e->event->length + total > max)
|
|
+ goto out;
|
|
+
|
|
+ file_priv->event_space += e->event->length;
|
|
+ list_del(&e->link);
|
|
+ *out = e;
|
|
+ ret = true;
|
|
+
|
|
+out:
|
|
+ spin_unlock_irqrestore(&dev->event_lock, flags);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+ssize_t drm_read(struct file *filp, char __user *buffer,
|
|
+ size_t count, loff_t *offset)
|
|
+{
|
|
+ struct drm_file *file_priv = filp->private_data;
|
|
+ struct drm_pending_event *e;
|
|
+ size_t total;
|
|
+ ssize_t ret;
|
|
+
|
|
+ ret = wait_event_interruptible(file_priv->event_wait,
|
|
+ !list_empty(&file_priv->event_list));
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ total = 0;
|
|
+ while (drm_dequeue_event(file_priv, total, count, &e)) {
|
|
+ if (copy_to_user(buffer + total,
|
|
+ e->event, e->event->length)) {
|
|
+ total = -EFAULT;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ total += e->event->length;
|
|
+ e->destroy(e);
|
|
+ }
|
|
+
|
|
+ return total;
|
|
+}
|
|
+EXPORT_SYMBOL(drm_read);
|
|
+
|
|
unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait)
|
|
{
|
|
- return 0;
|
|
+ struct drm_file *file_priv = filp->private_data;
|
|
+ unsigned int mask = 0;
|
|
+
|
|
+ poll_wait(filp, &file_priv->event_wait, wait);
|
|
+
|
|
+ if (!list_empty(&file_priv->event_list))
|
|
+ mask |= POLLIN | POLLRDNORM;
|
|
+
|
|
+ return mask;
|
|
}
|
|
EXPORT_SYMBOL(drm_poll);
|
|
diff --git a/drivers/gpu/drm/drm_ioc32.c b/drivers/gpu/drm/drm_ioc32.c
|
|
index 282d9fd..d61d185 100644
|
|
--- a/drivers/gpu/drm/drm_ioc32.c
|
|
+++ b/drivers/gpu/drm/drm_ioc32.c
|
|
@@ -104,7 +104,7 @@ static int compat_drm_version(struct file *file, unsigned int cmd,
|
|
&version->desc))
|
|
return -EFAULT;
|
|
|
|
- err = drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
+ err = drm_ioctl(file,
|
|
DRM_IOCTL_VERSION, (unsigned long)version);
|
|
if (err)
|
|
return err;
|
|
@@ -145,8 +145,7 @@ static int compat_drm_getunique(struct file *file, unsigned int cmd,
|
|
&u->unique))
|
|
return -EFAULT;
|
|
|
|
- err = drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_GET_UNIQUE, (unsigned long)u);
|
|
+ err = drm_ioctl(file, DRM_IOCTL_GET_UNIQUE, (unsigned long)u);
|
|
if (err)
|
|
return err;
|
|
|
|
@@ -174,8 +173,7 @@ static int compat_drm_setunique(struct file *file, unsigned int cmd,
|
|
&u->unique))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_SET_UNIQUE, (unsigned long)u);
|
|
+ return drm_ioctl(file, DRM_IOCTL_SET_UNIQUE, (unsigned long)u);
|
|
}
|
|
|
|
typedef struct drm_map32 {
|
|
@@ -205,8 +203,7 @@ static int compat_drm_getmap(struct file *file, unsigned int cmd,
|
|
if (__put_user(idx, &map->offset))
|
|
return -EFAULT;
|
|
|
|
- err = drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_GET_MAP, (unsigned long)map);
|
|
+ err = drm_ioctl(file, DRM_IOCTL_GET_MAP, (unsigned long)map);
|
|
if (err)
|
|
return err;
|
|
|
|
@@ -246,8 +243,7 @@ static int compat_drm_addmap(struct file *file, unsigned int cmd,
|
|
|| __put_user(m32.flags, &map->flags))
|
|
return -EFAULT;
|
|
|
|
- err = drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_ADD_MAP, (unsigned long)map);
|
|
+ err = drm_ioctl(file, DRM_IOCTL_ADD_MAP, (unsigned long)map);
|
|
if (err)
|
|
return err;
|
|
|
|
@@ -284,8 +280,7 @@ static int compat_drm_rmmap(struct file *file, unsigned int cmd,
|
|
if (__put_user((void *)(unsigned long)handle, &map->handle))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_RM_MAP, (unsigned long)map);
|
|
+ return drm_ioctl(file, DRM_IOCTL_RM_MAP, (unsigned long)map);
|
|
}
|
|
|
|
typedef struct drm_client32 {
|
|
@@ -314,8 +309,7 @@ static int compat_drm_getclient(struct file *file, unsigned int cmd,
|
|
if (__put_user(idx, &client->idx))
|
|
return -EFAULT;
|
|
|
|
- err = drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_GET_CLIENT, (unsigned long)client);
|
|
+ err = drm_ioctl(file, DRM_IOCTL_GET_CLIENT, (unsigned long)client);
|
|
if (err)
|
|
return err;
|
|
|
|
@@ -351,8 +345,7 @@ static int compat_drm_getstats(struct file *file, unsigned int cmd,
|
|
if (!access_ok(VERIFY_WRITE, stats, sizeof(*stats)))
|
|
return -EFAULT;
|
|
|
|
- err = drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_GET_STATS, (unsigned long)stats);
|
|
+ err = drm_ioctl(file, DRM_IOCTL_GET_STATS, (unsigned long)stats);
|
|
if (err)
|
|
return err;
|
|
|
|
@@ -395,8 +388,7 @@ static int compat_drm_addbufs(struct file *file, unsigned int cmd,
|
|
|| __put_user(agp_start, &buf->agp_start))
|
|
return -EFAULT;
|
|
|
|
- err = drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_ADD_BUFS, (unsigned long)buf);
|
|
+ err = drm_ioctl(file, DRM_IOCTL_ADD_BUFS, (unsigned long)buf);
|
|
if (err)
|
|
return err;
|
|
|
|
@@ -427,8 +419,7 @@ static int compat_drm_markbufs(struct file *file, unsigned int cmd,
|
|
|| __put_user(b32.high_mark, &buf->high_mark))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_MARK_BUFS, (unsigned long)buf);
|
|
+ return drm_ioctl(file, DRM_IOCTL_MARK_BUFS, (unsigned long)buf);
|
|
}
|
|
|
|
typedef struct drm_buf_info32 {
|
|
@@ -469,8 +460,7 @@ static int compat_drm_infobufs(struct file *file, unsigned int cmd,
|
|
|| __put_user(list, &request->list))
|
|
return -EFAULT;
|
|
|
|
- err = drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_INFO_BUFS, (unsigned long)request);
|
|
+ err = drm_ioctl(file, DRM_IOCTL_INFO_BUFS, (unsigned long)request);
|
|
if (err)
|
|
return err;
|
|
|
|
@@ -531,8 +521,7 @@ static int compat_drm_mapbufs(struct file *file, unsigned int cmd,
|
|
|| __put_user(list, &request->list))
|
|
return -EFAULT;
|
|
|
|
- err = drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_MAP_BUFS, (unsigned long)request);
|
|
+ err = drm_ioctl(file, DRM_IOCTL_MAP_BUFS, (unsigned long)request);
|
|
if (err)
|
|
return err;
|
|
|
|
@@ -578,8 +567,7 @@ static int compat_drm_freebufs(struct file *file, unsigned int cmd,
|
|
&request->list))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_FREE_BUFS, (unsigned long)request);
|
|
+ return drm_ioctl(file, DRM_IOCTL_FREE_BUFS, (unsigned long)request);
|
|
}
|
|
|
|
typedef struct drm_ctx_priv_map32 {
|
|
@@ -605,8 +593,7 @@ static int compat_drm_setsareactx(struct file *file, unsigned int cmd,
|
|
&request->handle))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_SET_SAREA_CTX, (unsigned long)request);
|
|
+ return drm_ioctl(file, DRM_IOCTL_SET_SAREA_CTX, (unsigned long)request);
|
|
}
|
|
|
|
static int compat_drm_getsareactx(struct file *file, unsigned int cmd,
|
|
@@ -628,8 +615,7 @@ static int compat_drm_getsareactx(struct file *file, unsigned int cmd,
|
|
if (__put_user(ctx_id, &request->ctx_id))
|
|
return -EFAULT;
|
|
|
|
- err = drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_GET_SAREA_CTX, (unsigned long)request);
|
|
+ err = drm_ioctl(file, DRM_IOCTL_GET_SAREA_CTX, (unsigned long)request);
|
|
if (err)
|
|
return err;
|
|
|
|
@@ -664,8 +650,7 @@ static int compat_drm_resctx(struct file *file, unsigned int cmd,
|
|
&res->contexts))
|
|
return -EFAULT;
|
|
|
|
- err = drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_RES_CTX, (unsigned long)res);
|
|
+ err = drm_ioctl(file, DRM_IOCTL_RES_CTX, (unsigned long)res);
|
|
if (err)
|
|
return err;
|
|
|
|
@@ -718,8 +703,7 @@ static int compat_drm_dma(struct file *file, unsigned int cmd,
|
|
&d->request_sizes))
|
|
return -EFAULT;
|
|
|
|
- err = drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_DMA, (unsigned long)d);
|
|
+ err = drm_ioctl(file, DRM_IOCTL_DMA, (unsigned long)d);
|
|
if (err)
|
|
return err;
|
|
|
|
@@ -751,8 +735,7 @@ static int compat_drm_agp_enable(struct file *file, unsigned int cmd,
|
|
if (put_user(m32.mode, &mode->mode))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_AGP_ENABLE, (unsigned long)mode);
|
|
+ return drm_ioctl(file, DRM_IOCTL_AGP_ENABLE, (unsigned long)mode);
|
|
}
|
|
|
|
typedef struct drm_agp_info32 {
|
|
@@ -781,8 +764,7 @@ static int compat_drm_agp_info(struct file *file, unsigned int cmd,
|
|
if (!access_ok(VERIFY_WRITE, info, sizeof(*info)))
|
|
return -EFAULT;
|
|
|
|
- err = drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_AGP_INFO, (unsigned long)info);
|
|
+ err = drm_ioctl(file, DRM_IOCTL_AGP_INFO, (unsigned long)info);
|
|
if (err)
|
|
return err;
|
|
|
|
@@ -827,16 +809,14 @@ static int compat_drm_agp_alloc(struct file *file, unsigned int cmd,
|
|
|| __put_user(req32.type, &request->type))
|
|
return -EFAULT;
|
|
|
|
- err = drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_AGP_ALLOC, (unsigned long)request);
|
|
+ err = drm_ioctl(file, DRM_IOCTL_AGP_ALLOC, (unsigned long)request);
|
|
if (err)
|
|
return err;
|
|
|
|
if (__get_user(req32.handle, &request->handle)
|
|
|| __get_user(req32.physical, &request->physical)
|
|
|| copy_to_user(argp, &req32, sizeof(req32))) {
|
|
- drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_AGP_FREE, (unsigned long)request);
|
|
+ drm_ioctl(file, DRM_IOCTL_AGP_FREE, (unsigned long)request);
|
|
return -EFAULT;
|
|
}
|
|
|
|
@@ -856,8 +836,7 @@ static int compat_drm_agp_free(struct file *file, unsigned int cmd,
|
|
|| __put_user(handle, &request->handle))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_AGP_FREE, (unsigned long)request);
|
|
+ return drm_ioctl(file, DRM_IOCTL_AGP_FREE, (unsigned long)request);
|
|
}
|
|
|
|
typedef struct drm_agp_binding32 {
|
|
@@ -881,8 +860,7 @@ static int compat_drm_agp_bind(struct file *file, unsigned int cmd,
|
|
|| __put_user(req32.offset, &request->offset))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_AGP_BIND, (unsigned long)request);
|
|
+ return drm_ioctl(file, DRM_IOCTL_AGP_BIND, (unsigned long)request);
|
|
}
|
|
|
|
static int compat_drm_agp_unbind(struct file *file, unsigned int cmd,
|
|
@@ -898,8 +876,7 @@ static int compat_drm_agp_unbind(struct file *file, unsigned int cmd,
|
|
|| __put_user(handle, &request->handle))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_AGP_UNBIND, (unsigned long)request);
|
|
+ return drm_ioctl(file, DRM_IOCTL_AGP_UNBIND, (unsigned long)request);
|
|
}
|
|
#endif /* __OS_HAS_AGP */
|
|
|
|
@@ -923,8 +900,7 @@ static int compat_drm_sg_alloc(struct file *file, unsigned int cmd,
|
|
|| __put_user(x, &request->size))
|
|
return -EFAULT;
|
|
|
|
- err = drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_SG_ALLOC, (unsigned long)request);
|
|
+ err = drm_ioctl(file, DRM_IOCTL_SG_ALLOC, (unsigned long)request);
|
|
if (err)
|
|
return err;
|
|
|
|
@@ -950,8 +926,7 @@ static int compat_drm_sg_free(struct file *file, unsigned int cmd,
|
|
|| __put_user(x << PAGE_SHIFT, &request->handle))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_SG_FREE, (unsigned long)request);
|
|
+ return drm_ioctl(file, DRM_IOCTL_SG_FREE, (unsigned long)request);
|
|
}
|
|
|
|
#if defined(CONFIG_X86) || defined(CONFIG_IA64)
|
|
@@ -981,8 +956,7 @@ static int compat_drm_update_draw(struct file *file, unsigned int cmd,
|
|
__put_user(update32.data, &request->data))
|
|
return -EFAULT;
|
|
|
|
- err = drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_UPDATE_DRAW, (unsigned long)request);
|
|
+ err = drm_ioctl(file, DRM_IOCTL_UPDATE_DRAW, (unsigned long)request);
|
|
return err;
|
|
}
|
|
#endif
|
|
@@ -1023,8 +997,7 @@ static int compat_drm_wait_vblank(struct file *file, unsigned int cmd,
|
|
|| __put_user(req32.request.signal, &request->request.signal))
|
|
return -EFAULT;
|
|
|
|
- err = drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_WAIT_VBLANK, (unsigned long)request);
|
|
+ err = drm_ioctl(file, DRM_IOCTL_WAIT_VBLANK, (unsigned long)request);
|
|
if (err)
|
|
return err;
|
|
|
|
@@ -1094,16 +1067,14 @@ long drm_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|
* than always failing.
|
|
*/
|
|
if (nr >= ARRAY_SIZE(drm_compat_ioctls))
|
|
- return drm_ioctl(filp->f_dentry->d_inode, filp, cmd, arg);
|
|
+ return drm_ioctl(filp, cmd, arg);
|
|
|
|
fn = drm_compat_ioctls[nr];
|
|
|
|
- lock_kernel(); /* XXX for now */
|
|
if (fn != NULL)
|
|
ret = (*fn) (filp, cmd, arg);
|
|
else
|
|
- ret = drm_ioctl(filp->f_path.dentry->d_inode, filp, cmd, arg);
|
|
- unlock_kernel();
|
|
+ ret = drm_ioctl(filp, cmd, arg);
|
|
|
|
return ret;
|
|
}
|
|
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
|
|
index 332d743..b98384d 100644
|
|
--- a/drivers/gpu/drm/drm_irq.c
|
|
+++ b/drivers/gpu/drm/drm_irq.c
|
|
@@ -115,6 +115,7 @@ void drm_vblank_cleanup(struct drm_device *dev)
|
|
|
|
dev->num_crtcs = 0;
|
|
}
|
|
+EXPORT_SYMBOL(drm_vblank_cleanup);
|
|
|
|
int drm_vblank_init(struct drm_device *dev, int num_crtcs)
|
|
{
|
|
@@ -163,7 +164,6 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
|
|
}
|
|
|
|
dev->vblank_disable_allowed = 0;
|
|
-
|
|
return 0;
|
|
|
|
err:
|
|
@@ -493,6 +493,9 @@ EXPORT_SYMBOL(drm_vblank_off);
|
|
*/
|
|
void drm_vblank_pre_modeset(struct drm_device *dev, int crtc)
|
|
{
|
|
+ /* vblank is not initialized (IRQ not installed ?) */
|
|
+ if (!dev->num_crtcs)
|
|
+ return;
|
|
/*
|
|
* To avoid all the problems that might happen if interrupts
|
|
* were enabled/disabled around or between these calls, we just
|
|
@@ -568,6 +571,63 @@ out:
|
|
return ret;
|
|
}
|
|
|
|
+static int drm_queue_vblank_event(struct drm_device *dev, int pipe,
|
|
+ union drm_wait_vblank *vblwait,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_pending_vblank_event *e;
|
|
+ struct timeval now;
|
|
+ unsigned long flags;
|
|
+ unsigned int seq;
|
|
+
|
|
+ e = kzalloc(sizeof *e, GFP_KERNEL);
|
|
+ if (e == NULL)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ e->pipe = pipe;
|
|
+ e->event.base.type = DRM_EVENT_VBLANK;
|
|
+ e->event.base.length = sizeof e->event;
|
|
+ e->event.user_data = vblwait->request.signal;
|
|
+ e->base.event = &e->event.base;
|
|
+ e->base.file_priv = file_priv;
|
|
+ e->base.destroy = (void (*) (struct drm_pending_event *)) kfree;
|
|
+
|
|
+ do_gettimeofday(&now);
|
|
+ spin_lock_irqsave(&dev->event_lock, flags);
|
|
+
|
|
+ if (file_priv->event_space < sizeof e->event) {
|
|
+ spin_unlock_irqrestore(&dev->event_lock, flags);
|
|
+ kfree(e);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ file_priv->event_space -= sizeof e->event;
|
|
+ seq = drm_vblank_count(dev, pipe);
|
|
+ if ((vblwait->request.type & _DRM_VBLANK_NEXTONMISS) &&
|
|
+ (seq - vblwait->request.sequence) <= (1 << 23)) {
|
|
+ vblwait->request.sequence = seq + 1;
|
|
+ vblwait->reply.sequence = vblwait->request.sequence;
|
|
+ }
|
|
+
|
|
+ DRM_DEBUG("event on vblank count %d, current %d, crtc %d\n",
|
|
+ vblwait->request.sequence, seq, pipe);
|
|
+
|
|
+ e->event.sequence = vblwait->request.sequence;
|
|
+ if ((seq - vblwait->request.sequence) <= (1 << 23)) {
|
|
+ e->event.tv_sec = now.tv_sec;
|
|
+ e->event.tv_usec = now.tv_usec;
|
|
+ drm_vblank_put(dev, e->pipe);
|
|
+ list_add_tail(&e->base.link, &e->base.file_priv->event_list);
|
|
+ wake_up_interruptible(&e->base.file_priv->event_wait);
|
|
+ } else {
|
|
+ list_add_tail(&e->base.link, &dev->vblank_event_list);
|
|
+ }
|
|
+
|
|
+ spin_unlock_irqrestore(&dev->event_lock, flags);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
/**
|
|
* Wait for VBLANK.
|
|
*
|
|
@@ -627,6 +687,9 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
|
|
goto done;
|
|
}
|
|
|
|
+ if (flags & _DRM_VBLANK_EVENT)
|
|
+ return drm_queue_vblank_event(dev, crtc, vblwait, file_priv);
|
|
+
|
|
if ((flags & _DRM_VBLANK_NEXTONMISS) &&
|
|
(seq - vblwait->request.sequence) <= (1<<23)) {
|
|
vblwait->request.sequence = seq + 1;
|
|
@@ -659,6 +722,38 @@ done:
|
|
return ret;
|
|
}
|
|
|
|
+void drm_handle_vblank_events(struct drm_device *dev, int crtc)
|
|
+{
|
|
+ struct drm_pending_vblank_event *e, *t;
|
|
+ struct timeval now;
|
|
+ unsigned long flags;
|
|
+ unsigned int seq;
|
|
+
|
|
+ do_gettimeofday(&now);
|
|
+ seq = drm_vblank_count(dev, crtc);
|
|
+
|
|
+ spin_lock_irqsave(&dev->event_lock, flags);
|
|
+
|
|
+ list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
|
|
+ if (e->pipe != crtc)
|
|
+ continue;
|
|
+ if ((seq - e->event.sequence) > (1<<23))
|
|
+ continue;
|
|
+
|
|
+ DRM_DEBUG("vblank event on %d, current %d\n",
|
|
+ e->event.sequence, seq);
|
|
+
|
|
+ e->event.sequence = seq;
|
|
+ e->event.tv_sec = now.tv_sec;
|
|
+ e->event.tv_usec = now.tv_usec;
|
|
+ drm_vblank_put(dev, e->pipe);
|
|
+ list_move_tail(&e->base.link, &e->base.file_priv->event_list);
|
|
+ wake_up_interruptible(&e->base.file_priv->event_wait);
|
|
+ }
|
|
+
|
|
+ spin_unlock_irqrestore(&dev->event_lock, flags);
|
|
+}
|
|
+
|
|
/**
|
|
* drm_handle_vblank - handle a vblank event
|
|
* @dev: DRM device
|
|
@@ -669,7 +764,11 @@ done:
|
|
*/
|
|
void drm_handle_vblank(struct drm_device *dev, int crtc)
|
|
{
|
|
+ if (!dev->num_crtcs)
|
|
+ return;
|
|
+
|
|
atomic_inc(&dev->_vblank_count[crtc]);
|
|
DRM_WAKEUP(&dev->vbl_queue[crtc]);
|
|
+ drm_handle_vblank_events(dev, crtc);
|
|
}
|
|
EXPORT_SYMBOL(drm_handle_vblank);
|
|
diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c
|
|
index 97dc5a4..2ac074c 100644
|
|
--- a/drivers/gpu/drm/drm_mm.c
|
|
+++ b/drivers/gpu/drm/drm_mm.c
|
|
@@ -226,6 +226,44 @@ struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *node,
|
|
}
|
|
EXPORT_SYMBOL(drm_mm_get_block_generic);
|
|
|
|
+struct drm_mm_node *drm_mm_get_block_range_generic(struct drm_mm_node *node,
|
|
+ unsigned long size,
|
|
+ unsigned alignment,
|
|
+ unsigned long start,
|
|
+ unsigned long end,
|
|
+ int atomic)
|
|
+{
|
|
+ struct drm_mm_node *align_splitoff = NULL;
|
|
+ unsigned tmp = 0;
|
|
+ unsigned wasted = 0;
|
|
+
|
|
+ if (node->start < start)
|
|
+ wasted += start - node->start;
|
|
+ if (alignment)
|
|
+ tmp = ((node->start + wasted) % alignment);
|
|
+
|
|
+ if (tmp)
|
|
+ wasted += alignment - tmp;
|
|
+ if (wasted) {
|
|
+ align_splitoff = drm_mm_split_at_start(node, wasted, atomic);
|
|
+ if (unlikely(align_splitoff == NULL))
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ if (node->size == size) {
|
|
+ list_del_init(&node->fl_entry);
|
|
+ node->free = 0;
|
|
+ } else {
|
|
+ node = drm_mm_split_at_start(node, size, atomic);
|
|
+ }
|
|
+
|
|
+ if (align_splitoff)
|
|
+ drm_mm_put_block(align_splitoff);
|
|
+
|
|
+ return node;
|
|
+}
|
|
+EXPORT_SYMBOL(drm_mm_get_block_range_generic);
|
|
+
|
|
/*
|
|
* Put a block. Merge with the previous and / or next block if they are free.
|
|
* Otherwise add to the free stack.
|
|
@@ -320,7 +358,7 @@ struct drm_mm_node *drm_mm_search_free(const struct drm_mm *mm,
|
|
if (entry->size >= size + wasted) {
|
|
if (!best_match)
|
|
return entry;
|
|
- if (size < best_size) {
|
|
+ if (entry->size < best_size) {
|
|
best = entry;
|
|
best_size = entry->size;
|
|
}
|
|
@@ -331,6 +369,57 @@ struct drm_mm_node *drm_mm_search_free(const struct drm_mm *mm,
|
|
}
|
|
EXPORT_SYMBOL(drm_mm_search_free);
|
|
|
|
+struct drm_mm_node *drm_mm_search_free_in_range(const struct drm_mm *mm,
|
|
+ unsigned long size,
|
|
+ unsigned alignment,
|
|
+ unsigned long start,
|
|
+ unsigned long end,
|
|
+ int best_match)
|
|
+{
|
|
+ struct list_head *list;
|
|
+ const struct list_head *free_stack = &mm->fl_entry;
|
|
+ struct drm_mm_node *entry;
|
|
+ struct drm_mm_node *best;
|
|
+ unsigned long best_size;
|
|
+ unsigned wasted;
|
|
+
|
|
+ best = NULL;
|
|
+ best_size = ~0UL;
|
|
+
|
|
+ list_for_each(list, free_stack) {
|
|
+ entry = list_entry(list, struct drm_mm_node, fl_entry);
|
|
+ wasted = 0;
|
|
+
|
|
+ if (entry->size < size)
|
|
+ continue;
|
|
+
|
|
+ if (entry->start > end || (entry->start+entry->size) < start)
|
|
+ continue;
|
|
+
|
|
+ if (entry->start < start)
|
|
+ wasted += start - entry->start;
|
|
+
|
|
+ if (alignment) {
|
|
+ register unsigned tmp = (entry->start + wasted) % alignment;
|
|
+ if (tmp)
|
|
+ wasted += alignment - tmp;
|
|
+ }
|
|
+
|
|
+ if (entry->size >= size + wasted &&
|
|
+ (entry->start + wasted + size) <= end) {
|
|
+ if (!best_match)
|
|
+ return entry;
|
|
+ if (entry->size < best_size) {
|
|
+ best = entry;
|
|
+ best_size = entry->size;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return best;
|
|
+}
|
|
+EXPORT_SYMBOL(drm_mm_search_free_in_range);
|
|
+
|
|
int drm_mm_clean(struct drm_mm * mm)
|
|
{
|
|
struct list_head *head = &mm->ml_entry;
|
|
@@ -381,6 +470,26 @@ void drm_mm_takedown(struct drm_mm * mm)
|
|
}
|
|
EXPORT_SYMBOL(drm_mm_takedown);
|
|
|
|
+void drm_mm_debug_table(struct drm_mm *mm, const char *prefix)
|
|
+{
|
|
+ struct drm_mm_node *entry;
|
|
+ int total_used = 0, total_free = 0, total = 0;
|
|
+
|
|
+ list_for_each_entry(entry, &mm->ml_entry, ml_entry) {
|
|
+ printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8ld: %s\n",
|
|
+ prefix, entry->start, entry->start + entry->size,
|
|
+ entry->size, entry->free ? "free" : "used");
|
|
+ total += entry->size;
|
|
+ if (entry->free)
|
|
+ total_free += entry->size;
|
|
+ else
|
|
+ total_used += entry->size;
|
|
+ }
|
|
+ printk(KERN_DEBUG "%s total: %d, used %d free %d\n", prefix, total,
|
|
+ total_used, total_free);
|
|
+}
|
|
+EXPORT_SYMBOL(drm_mm_debug_table);
|
|
+
|
|
#if defined(CONFIG_DEBUG_FS)
|
|
int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm)
|
|
{
|
|
@@ -395,7 +504,7 @@ int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm)
|
|
else
|
|
total_used += entry->size;
|
|
}
|
|
- seq_printf(m, "total: %d, used %d free %d\n", total, total_free, total_used);
|
|
+ seq_printf(m, "total: %d, used %d free %d\n", total, total_used, total_free);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(drm_mm_dump_table);
|
|
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
|
|
index 51f6772..76d6339 100644
|
|
--- a/drivers/gpu/drm/drm_modes.c
|
|
+++ b/drivers/gpu/drm/drm_modes.c
|
|
@@ -1,9 +1,4 @@
|
|
/*
|
|
- * The list_sort function is (presumably) licensed under the GPL (see the
|
|
- * top level "COPYING" file for details).
|
|
- *
|
|
- * The remainder of this file is:
|
|
- *
|
|
* Copyright © 1997-2003 by The XFree86 Project, Inc.
|
|
* Copyright © 2007 Dave Airlie
|
|
* Copyright © 2007-2008 Intel Corporation
|
|
@@ -36,6 +31,7 @@
|
|
*/
|
|
|
|
#include <linux/list.h>
|
|
+#include <linux/list_sort.h>
|
|
#include "drmP.h"
|
|
#include "drm.h"
|
|
#include "drm_crtc.h"
|
|
@@ -553,6 +549,32 @@ int drm_mode_height(struct drm_display_mode *mode)
|
|
}
|
|
EXPORT_SYMBOL(drm_mode_height);
|
|
|
|
+/** drm_mode_hsync - get the hsync of a mode
|
|
+ * @mode: mode
|
|
+ *
|
|
+ * LOCKING:
|
|
+ * None.
|
|
+ *
|
|
+ * Return @modes's hsync rate in kHz, rounded to the nearest int.
|
|
+ */
|
|
+int drm_mode_hsync(struct drm_display_mode *mode)
|
|
+{
|
|
+ unsigned int calc_val;
|
|
+
|
|
+ if (mode->hsync)
|
|
+ return mode->hsync;
|
|
+
|
|
+ if (mode->htotal < 0)
|
|
+ return 0;
|
|
+
|
|
+ calc_val = (mode->clock * 1000) / mode->htotal; /* hsync in Hz */
|
|
+ calc_val += 500; /* round to 1000Hz */
|
|
+ calc_val /= 1000; /* truncate to kHz */
|
|
+
|
|
+ return calc_val;
|
|
+}
|
|
+EXPORT_SYMBOL(drm_mode_hsync);
|
|
+
|
|
/**
|
|
* drm_mode_vrefresh - get the vrefresh of a mode
|
|
* @mode: mode
|
|
@@ -560,7 +582,7 @@ EXPORT_SYMBOL(drm_mode_height);
|
|
* LOCKING:
|
|
* None.
|
|
*
|
|
- * Return @mode's vrefresh rate or calculate it if necessary.
|
|
+ * Return @mode's vrefresh rate in Hz or calculate it if necessary.
|
|
*
|
|
* FIXME: why is this needed? shouldn't vrefresh be set already?
|
|
*
|
|
@@ -829,6 +851,7 @@ EXPORT_SYMBOL(drm_mode_prune_invalid);
|
|
|
|
/**
|
|
* drm_mode_compare - compare modes for favorability
|
|
+ * @priv: unused
|
|
* @lh_a: list_head for first mode
|
|
* @lh_b: list_head for second mode
|
|
*
|
|
@@ -842,7 +865,7 @@ EXPORT_SYMBOL(drm_mode_prune_invalid);
|
|
* Negative if @lh_a is better than @lh_b, zero if they're equivalent, or
|
|
* positive if @lh_b is better than @lh_a.
|
|
*/
|
|
-static int drm_mode_compare(struct list_head *lh_a, struct list_head *lh_b)
|
|
+static int drm_mode_compare(void *priv, struct list_head *lh_a, struct list_head *lh_b)
|
|
{
|
|
struct drm_display_mode *a = list_entry(lh_a, struct drm_display_mode, head);
|
|
struct drm_display_mode *b = list_entry(lh_b, struct drm_display_mode, head);
|
|
@@ -859,85 +882,6 @@ static int drm_mode_compare(struct list_head *lh_a, struct list_head *lh_b)
|
|
return diff;
|
|
}
|
|
|
|
-/* FIXME: what we don't have a list sort function? */
|
|
-/* list sort from Mark J Roberts (mjr@znex.org) */
|
|
-void list_sort(struct list_head *head,
|
|
- int (*cmp)(struct list_head *a, struct list_head *b))
|
|
-{
|
|
- struct list_head *p, *q, *e, *list, *tail, *oldhead;
|
|
- int insize, nmerges, psize, qsize, i;
|
|
-
|
|
- list = head->next;
|
|
- list_del(head);
|
|
- insize = 1;
|
|
- for (;;) {
|
|
- p = oldhead = list;
|
|
- list = tail = NULL;
|
|
- nmerges = 0;
|
|
-
|
|
- while (p) {
|
|
- nmerges++;
|
|
- q = p;
|
|
- psize = 0;
|
|
- for (i = 0; i < insize; i++) {
|
|
- psize++;
|
|
- q = q->next == oldhead ? NULL : q->next;
|
|
- if (!q)
|
|
- break;
|
|
- }
|
|
-
|
|
- qsize = insize;
|
|
- while (psize > 0 || (qsize > 0 && q)) {
|
|
- if (!psize) {
|
|
- e = q;
|
|
- q = q->next;
|
|
- qsize--;
|
|
- if (q == oldhead)
|
|
- q = NULL;
|
|
- } else if (!qsize || !q) {
|
|
- e = p;
|
|
- p = p->next;
|
|
- psize--;
|
|
- if (p == oldhead)
|
|
- p = NULL;
|
|
- } else if (cmp(p, q) <= 0) {
|
|
- e = p;
|
|
- p = p->next;
|
|
- psize--;
|
|
- if (p == oldhead)
|
|
- p = NULL;
|
|
- } else {
|
|
- e = q;
|
|
- q = q->next;
|
|
- qsize--;
|
|
- if (q == oldhead)
|
|
- q = NULL;
|
|
- }
|
|
- if (tail)
|
|
- tail->next = e;
|
|
- else
|
|
- list = e;
|
|
- e->prev = tail;
|
|
- tail = e;
|
|
- }
|
|
- p = q;
|
|
- }
|
|
-
|
|
- tail->next = list;
|
|
- list->prev = tail;
|
|
-
|
|
- if (nmerges <= 1)
|
|
- break;
|
|
-
|
|
- insize *= 2;
|
|
- }
|
|
-
|
|
- head->next = list;
|
|
- head->prev = list->prev;
|
|
- list->prev->next = head;
|
|
- list->prev = head;
|
|
-}
|
|
-
|
|
/**
|
|
* drm_mode_sort - sort mode list
|
|
* @mode_list: list to sort
|
|
@@ -949,7 +893,7 @@ void list_sort(struct list_head *head,
|
|
*/
|
|
void drm_mode_sort(struct list_head *mode_list)
|
|
{
|
|
- list_sort(mode_list, drm_mode_compare);
|
|
+ list_sort(NULL, mode_list, drm_mode_compare);
|
|
}
|
|
EXPORT_SYMBOL(drm_mode_sort);
|
|
|
|
diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c
|
|
index 55bb8a8..ad73e14 100644
|
|
--- a/drivers/gpu/drm/drm_stub.c
|
|
+++ b/drivers/gpu/drm/drm_stub.c
|
|
@@ -128,6 +128,7 @@ struct drm_master *drm_master_get(struct drm_master *master)
|
|
kref_get(&master->refcount);
|
|
return master;
|
|
}
|
|
+EXPORT_SYMBOL(drm_master_get);
|
|
|
|
static void drm_master_destroy(struct kref *kref)
|
|
{
|
|
@@ -170,10 +171,13 @@ void drm_master_put(struct drm_master **master)
|
|
kref_put(&(*master)->refcount, drm_master_destroy);
|
|
*master = NULL;
|
|
}
|
|
+EXPORT_SYMBOL(drm_master_put);
|
|
|
|
int drm_setmaster_ioctl(struct drm_device *dev, void *data,
|
|
struct drm_file *file_priv)
|
|
{
|
|
+ int ret = 0;
|
|
+
|
|
if (file_priv->is_master)
|
|
return 0;
|
|
|
|
@@ -188,6 +192,13 @@ int drm_setmaster_ioctl(struct drm_device *dev, void *data,
|
|
mutex_lock(&dev->struct_mutex);
|
|
file_priv->minor->master = drm_master_get(file_priv->master);
|
|
file_priv->is_master = 1;
|
|
+ if (dev->driver->master_set) {
|
|
+ ret = dev->driver->master_set(dev, file_priv, false);
|
|
+ if (unlikely(ret != 0)) {
|
|
+ file_priv->is_master = 0;
|
|
+ drm_master_put(&file_priv->minor->master);
|
|
+ }
|
|
+ }
|
|
mutex_unlock(&dev->struct_mutex);
|
|
}
|
|
|
|
@@ -204,6 +215,8 @@ int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&dev->struct_mutex);
|
|
+ if (dev->driver->master_drop)
|
|
+ dev->driver->master_drop(dev, file_priv, false);
|
|
drm_master_put(&file_priv->minor->master);
|
|
file_priv->is_master = 0;
|
|
mutex_unlock(&dev->struct_mutex);
|
|
@@ -220,9 +233,11 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,
|
|
INIT_LIST_HEAD(&dev->ctxlist);
|
|
INIT_LIST_HEAD(&dev->vmalist);
|
|
INIT_LIST_HEAD(&dev->maplist);
|
|
+ INIT_LIST_HEAD(&dev->vblank_event_list);
|
|
|
|
spin_lock_init(&dev->count_lock);
|
|
spin_lock_init(&dev->drw_lock);
|
|
+ spin_lock_init(&dev->event_lock);
|
|
init_timer(&dev->timer);
|
|
mutex_init(&dev->struct_mutex);
|
|
mutex_init(&dev->ctxlist_mutex);
|
|
diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile
|
|
new file mode 100644
|
|
index 0000000..6d2abaf
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/i2c/Makefile
|
|
@@ -0,0 +1,4 @@
|
|
+ccflags-y := -Iinclude/drm
|
|
+
|
|
+ch7006-y := ch7006_drv.o ch7006_mode.o
|
|
+obj-$(CONFIG_DRM_I2C_CH7006) += ch7006.o
|
|
diff --git a/drivers/gpu/drm/i2c/ch7006_drv.c b/drivers/gpu/drm/i2c/ch7006_drv.c
|
|
new file mode 100644
|
|
index 0000000..81681a0
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/i2c/ch7006_drv.c
|
|
@@ -0,0 +1,536 @@
|
|
+/*
|
|
+ * Copyright (C) 2009 Francisco Jerez.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "ch7006_priv.h"
|
|
+
|
|
+/* DRM encoder functions */
|
|
+
|
|
+static void ch7006_encoder_set_config(struct drm_encoder *encoder,
|
|
+ void *params)
|
|
+{
|
|
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
|
|
+
|
|
+ priv->params = params;
|
|
+}
|
|
+
|
|
+static void ch7006_encoder_destroy(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
|
|
+
|
|
+ drm_property_destroy(encoder->dev, priv->scale_property);
|
|
+
|
|
+ kfree(priv);
|
|
+ to_encoder_slave(encoder)->slave_priv = NULL;
|
|
+
|
|
+ drm_i2c_encoder_destroy(encoder);
|
|
+}
|
|
+
|
|
+static void ch7006_encoder_dpms(struct drm_encoder *encoder, int mode)
|
|
+{
|
|
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
|
|
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
|
|
+ struct ch7006_state *state = &priv->state;
|
|
+
|
|
+ ch7006_dbg(client, "\n");
|
|
+
|
|
+ if (mode == priv->last_dpms)
|
|
+ return;
|
|
+ priv->last_dpms = mode;
|
|
+
|
|
+ ch7006_setup_power_state(encoder);
|
|
+
|
|
+ ch7006_load_reg(client, state, CH7006_POWER);
|
|
+}
|
|
+
|
|
+static void ch7006_encoder_save(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
|
|
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
|
|
+
|
|
+ ch7006_dbg(client, "\n");
|
|
+
|
|
+ ch7006_state_save(client, &priv->saved_state);
|
|
+}
|
|
+
|
|
+static void ch7006_encoder_restore(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
|
|
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
|
|
+
|
|
+ ch7006_dbg(client, "\n");
|
|
+
|
|
+ ch7006_state_load(client, &priv->saved_state);
|
|
+}
|
|
+
|
|
+static bool ch7006_encoder_mode_fixup(struct drm_encoder *encoder,
|
|
+ struct drm_display_mode *mode,
|
|
+ struct drm_display_mode *adjusted_mode)
|
|
+{
|
|
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
|
|
+
|
|
+ /* The ch7006 is painfully picky with the input timings so no
|
|
+ * custom modes for now... */
|
|
+
|
|
+ priv->mode = ch7006_lookup_mode(encoder, mode);
|
|
+
|
|
+ return !!priv->mode;
|
|
+}
|
|
+
|
|
+static int ch7006_encoder_mode_valid(struct drm_encoder *encoder,
|
|
+ struct drm_display_mode *mode)
|
|
+{
|
|
+ if (ch7006_lookup_mode(encoder, mode))
|
|
+ return MODE_OK;
|
|
+ else
|
|
+ return MODE_BAD;
|
|
+}
|
|
+
|
|
+static void ch7006_encoder_mode_set(struct drm_encoder *encoder,
|
|
+ struct drm_display_mode *drm_mode,
|
|
+ struct drm_display_mode *adjusted_mode)
|
|
+{
|
|
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
|
|
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
|
|
+ struct ch7006_encoder_params *params = priv->params;
|
|
+ struct ch7006_state *state = &priv->state;
|
|
+ uint8_t *regs = state->regs;
|
|
+ struct ch7006_mode *mode = priv->mode;
|
|
+ struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
|
|
+ int start_active;
|
|
+
|
|
+ ch7006_dbg(client, "\n");
|
|
+
|
|
+ regs[CH7006_DISPMODE] = norm->dispmode | mode->dispmode;
|
|
+ regs[CH7006_BWIDTH] = 0;
|
|
+ regs[CH7006_INPUT_FORMAT] = bitf(CH7006_INPUT_FORMAT_FORMAT,
|
|
+ params->input_format);
|
|
+
|
|
+ regs[CH7006_CLKMODE] = CH7006_CLKMODE_SUBC_LOCK
|
|
+ | bitf(CH7006_CLKMODE_XCM, params->xcm)
|
|
+ | bitf(CH7006_CLKMODE_PCM, params->pcm);
|
|
+ if (params->clock_mode)
|
|
+ regs[CH7006_CLKMODE] |= CH7006_CLKMODE_MASTER;
|
|
+ if (params->clock_edge)
|
|
+ regs[CH7006_CLKMODE] |= CH7006_CLKMODE_POS_EDGE;
|
|
+
|
|
+ start_active = (drm_mode->htotal & ~0x7) - (drm_mode->hsync_start & ~0x7);
|
|
+ regs[CH7006_POV] = bitf(CH7006_POV_START_ACTIVE_8, start_active);
|
|
+ regs[CH7006_START_ACTIVE] = bitf(CH7006_START_ACTIVE_0, start_active);
|
|
+
|
|
+ regs[CH7006_INPUT_SYNC] = 0;
|
|
+ if (params->sync_direction)
|
|
+ regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_OUTPUT;
|
|
+ if (params->sync_encoding)
|
|
+ regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_EMBEDDED;
|
|
+ if (drm_mode->flags & DRM_MODE_FLAG_PVSYNC)
|
|
+ regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_PVSYNC;
|
|
+ if (drm_mode->flags & DRM_MODE_FLAG_PHSYNC)
|
|
+ regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_PHSYNC;
|
|
+
|
|
+ regs[CH7006_DETECT] = 0;
|
|
+ regs[CH7006_BCLKOUT] = 0;
|
|
+
|
|
+ regs[CH7006_SUBC_INC3] = 0;
|
|
+ if (params->pout_level)
|
|
+ regs[CH7006_SUBC_INC3] |= CH7006_SUBC_INC3_POUT_3_3V;
|
|
+
|
|
+ regs[CH7006_SUBC_INC4] = 0;
|
|
+ if (params->active_detect)
|
|
+ regs[CH7006_SUBC_INC4] |= CH7006_SUBC_INC4_DS_INPUT;
|
|
+
|
|
+ regs[CH7006_PLL_CONTROL] = priv->saved_state.regs[CH7006_PLL_CONTROL];
|
|
+
|
|
+ ch7006_setup_levels(encoder);
|
|
+ ch7006_setup_subcarrier(encoder);
|
|
+ ch7006_setup_pll(encoder);
|
|
+ ch7006_setup_power_state(encoder);
|
|
+ ch7006_setup_properties(encoder);
|
|
+
|
|
+ ch7006_state_load(client, state);
|
|
+}
|
|
+
|
|
+static enum drm_connector_status ch7006_encoder_detect(struct drm_encoder *encoder,
|
|
+ struct drm_connector *connector)
|
|
+{
|
|
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
|
|
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
|
|
+ struct ch7006_state *state = &priv->state;
|
|
+ int det;
|
|
+
|
|
+ ch7006_dbg(client, "\n");
|
|
+
|
|
+ ch7006_save_reg(client, state, CH7006_DETECT);
|
|
+ ch7006_save_reg(client, state, CH7006_POWER);
|
|
+ ch7006_save_reg(client, state, CH7006_CLKMODE);
|
|
+
|
|
+ ch7006_write(client, CH7006_POWER, CH7006_POWER_RESET |
|
|
+ bitfs(CH7006_POWER_LEVEL, NORMAL));
|
|
+ ch7006_write(client, CH7006_CLKMODE, CH7006_CLKMODE_MASTER);
|
|
+
|
|
+ ch7006_write(client, CH7006_DETECT, CH7006_DETECT_SENSE);
|
|
+
|
|
+ ch7006_write(client, CH7006_DETECT, 0);
|
|
+
|
|
+ det = ch7006_read(client, CH7006_DETECT);
|
|
+
|
|
+ ch7006_load_reg(client, state, CH7006_CLKMODE);
|
|
+ ch7006_load_reg(client, state, CH7006_POWER);
|
|
+ ch7006_load_reg(client, state, CH7006_DETECT);
|
|
+
|
|
+ if ((det & (CH7006_DETECT_SVIDEO_Y_TEST|
|
|
+ CH7006_DETECT_SVIDEO_C_TEST|
|
|
+ CH7006_DETECT_CVBS_TEST)) == 0)
|
|
+ priv->subconnector = DRM_MODE_SUBCONNECTOR_SCART;
|
|
+ else if ((det & (CH7006_DETECT_SVIDEO_Y_TEST|
|
|
+ CH7006_DETECT_SVIDEO_C_TEST)) == 0)
|
|
+ priv->subconnector = DRM_MODE_SUBCONNECTOR_SVIDEO;
|
|
+ else if ((det & CH7006_DETECT_CVBS_TEST) == 0)
|
|
+ priv->subconnector = DRM_MODE_SUBCONNECTOR_Composite;
|
|
+ else
|
|
+ priv->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
|
|
+
|
|
+ drm_connector_property_set_value(connector,
|
|
+ encoder->dev->mode_config.tv_subconnector_property,
|
|
+ priv->subconnector);
|
|
+
|
|
+ return priv->subconnector ? connector_status_connected :
|
|
+ connector_status_disconnected;
|
|
+}
|
|
+
|
|
+static int ch7006_encoder_get_modes(struct drm_encoder *encoder,
|
|
+ struct drm_connector *connector)
|
|
+{
|
|
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
|
|
+ struct ch7006_mode *mode;
|
|
+ int n = 0;
|
|
+
|
|
+ for (mode = ch7006_modes; mode->mode.clock; mode++) {
|
|
+ if (~mode->valid_scales & 1<<priv->scale ||
|
|
+ ~mode->valid_norms & 1<<priv->norm)
|
|
+ continue;
|
|
+
|
|
+ drm_mode_probed_add(connector,
|
|
+ drm_mode_duplicate(encoder->dev, &mode->mode));
|
|
+
|
|
+ n++;
|
|
+ }
|
|
+
|
|
+ return n;
|
|
+}
|
|
+
|
|
+static int ch7006_encoder_create_resources(struct drm_encoder *encoder,
|
|
+ struct drm_connector *connector)
|
|
+{
|
|
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct drm_mode_config *conf = &dev->mode_config;
|
|
+
|
|
+ drm_mode_create_tv_properties(dev, NUM_TV_NORMS, ch7006_tv_norm_names);
|
|
+
|
|
+ priv->scale_property = drm_property_create(dev, DRM_MODE_PROP_RANGE,
|
|
+ "scale", 2);
|
|
+ priv->scale_property->values[0] = 0;
|
|
+ priv->scale_property->values[1] = 2;
|
|
+
|
|
+ drm_connector_attach_property(connector, conf->tv_select_subconnector_property,
|
|
+ priv->select_subconnector);
|
|
+ drm_connector_attach_property(connector, conf->tv_subconnector_property,
|
|
+ priv->subconnector);
|
|
+ drm_connector_attach_property(connector, conf->tv_left_margin_property,
|
|
+ priv->hmargin);
|
|
+ drm_connector_attach_property(connector, conf->tv_bottom_margin_property,
|
|
+ priv->vmargin);
|
|
+ drm_connector_attach_property(connector, conf->tv_mode_property,
|
|
+ priv->norm);
|
|
+ drm_connector_attach_property(connector, conf->tv_brightness_property,
|
|
+ priv->brightness);
|
|
+ drm_connector_attach_property(connector, conf->tv_contrast_property,
|
|
+ priv->contrast);
|
|
+ drm_connector_attach_property(connector, conf->tv_flicker_reduction_property,
|
|
+ priv->flicker);
|
|
+ drm_connector_attach_property(connector, priv->scale_property,
|
|
+ priv->scale);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ch7006_encoder_set_property(struct drm_encoder *encoder,
|
|
+ struct drm_connector *connector,
|
|
+ struct drm_property *property,
|
|
+ uint64_t val)
|
|
+{
|
|
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
|
|
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
|
|
+ struct ch7006_state *state = &priv->state;
|
|
+ struct drm_mode_config *conf = &encoder->dev->mode_config;
|
|
+ struct drm_crtc *crtc = encoder->crtc;
|
|
+ bool modes_changed = false;
|
|
+
|
|
+ ch7006_dbg(client, "\n");
|
|
+
|
|
+ if (property == conf->tv_select_subconnector_property) {
|
|
+ priv->select_subconnector = val;
|
|
+
|
|
+ ch7006_setup_power_state(encoder);
|
|
+
|
|
+ ch7006_load_reg(client, state, CH7006_POWER);
|
|
+
|
|
+ } else if (property == conf->tv_left_margin_property) {
|
|
+ priv->hmargin = val;
|
|
+
|
|
+ ch7006_setup_properties(encoder);
|
|
+
|
|
+ ch7006_load_reg(client, state, CH7006_POV);
|
|
+ ch7006_load_reg(client, state, CH7006_HPOS);
|
|
+
|
|
+ } else if (property == conf->tv_bottom_margin_property) {
|
|
+ priv->vmargin = val;
|
|
+
|
|
+ ch7006_setup_properties(encoder);
|
|
+
|
|
+ ch7006_load_reg(client, state, CH7006_POV);
|
|
+ ch7006_load_reg(client, state, CH7006_VPOS);
|
|
+
|
|
+ } else if (property == conf->tv_mode_property) {
|
|
+ if (connector->dpms != DRM_MODE_DPMS_OFF)
|
|
+ return -EINVAL;
|
|
+
|
|
+ priv->norm = val;
|
|
+
|
|
+ modes_changed = true;
|
|
+
|
|
+ } else if (property == conf->tv_brightness_property) {
|
|
+ priv->brightness = val;
|
|
+
|
|
+ ch7006_setup_levels(encoder);
|
|
+
|
|
+ ch7006_load_reg(client, state, CH7006_BLACK_LEVEL);
|
|
+
|
|
+ } else if (property == conf->tv_contrast_property) {
|
|
+ priv->contrast = val;
|
|
+
|
|
+ ch7006_setup_properties(encoder);
|
|
+
|
|
+ ch7006_load_reg(client, state, CH7006_CONTRAST);
|
|
+
|
|
+ } else if (property == conf->tv_flicker_reduction_property) {
|
|
+ priv->flicker = val;
|
|
+
|
|
+ ch7006_setup_properties(encoder);
|
|
+
|
|
+ ch7006_load_reg(client, state, CH7006_FFILTER);
|
|
+
|
|
+ } else if (property == priv->scale_property) {
|
|
+ if (connector->dpms != DRM_MODE_DPMS_OFF)
|
|
+ return -EINVAL;
|
|
+
|
|
+ priv->scale = val;
|
|
+
|
|
+ modes_changed = true;
|
|
+
|
|
+ } else {
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (modes_changed) {
|
|
+ drm_helper_probe_single_connector_modes(connector, 0, 0);
|
|
+
|
|
+ /* Disable the crtc to ensure a full modeset is
|
|
+ * performed whenever it's turned on again. */
|
|
+ if (crtc) {
|
|
+ struct drm_mode_set modeset = {
|
|
+ .crtc = crtc,
|
|
+ };
|
|
+
|
|
+ crtc->funcs->set_config(&modeset);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct drm_encoder_slave_funcs ch7006_encoder_funcs = {
|
|
+ .set_config = ch7006_encoder_set_config,
|
|
+ .destroy = ch7006_encoder_destroy,
|
|
+ .dpms = ch7006_encoder_dpms,
|
|
+ .save = ch7006_encoder_save,
|
|
+ .restore = ch7006_encoder_restore,
|
|
+ .mode_fixup = ch7006_encoder_mode_fixup,
|
|
+ .mode_valid = ch7006_encoder_mode_valid,
|
|
+ .mode_set = ch7006_encoder_mode_set,
|
|
+ .detect = ch7006_encoder_detect,
|
|
+ .get_modes = ch7006_encoder_get_modes,
|
|
+ .create_resources = ch7006_encoder_create_resources,
|
|
+ .set_property = ch7006_encoder_set_property,
|
|
+};
|
|
+
|
|
+
|
|
+/* I2C driver functions */
|
|
+
|
|
+static int ch7006_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
|
+{
|
|
+ uint8_t addr = CH7006_VERSION_ID;
|
|
+ uint8_t val;
|
|
+ int ret;
|
|
+
|
|
+ ch7006_dbg(client, "\n");
|
|
+
|
|
+ ret = i2c_master_send(client, &addr, sizeof(addr));
|
|
+ if (ret < 0)
|
|
+ goto fail;
|
|
+
|
|
+ ret = i2c_master_recv(client, &val, sizeof(val));
|
|
+ if (ret < 0)
|
|
+ goto fail;
|
|
+
|
|
+ ch7006_info(client, "Detected version ID: %x\n", val);
|
|
+
|
|
+ /* I don't know what this is for, but otherwise I get no
|
|
+ * signal.
|
|
+ */
|
|
+ ch7006_write(client, 0x3d, 0x0);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+fail:
|
|
+ ch7006_err(client, "Error %d reading version ID\n", ret);
|
|
+
|
|
+ return -ENODEV;
|
|
+}
|
|
+
|
|
+static int ch7006_remove(struct i2c_client *client)
|
|
+{
|
|
+ ch7006_dbg(client, "\n");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ch7006_encoder_init(struct i2c_client *client,
|
|
+ struct drm_device *dev,
|
|
+ struct drm_encoder_slave *encoder)
|
|
+{
|
|
+ struct ch7006_priv *priv;
|
|
+ int i;
|
|
+
|
|
+ ch7006_dbg(client, "\n");
|
|
+
|
|
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
|
+ if (!priv)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ encoder->slave_priv = priv;
|
|
+ encoder->slave_funcs = &ch7006_encoder_funcs;
|
|
+
|
|
+ priv->norm = TV_NORM_PAL;
|
|
+ priv->select_subconnector = DRM_MODE_SUBCONNECTOR_Automatic;
|
|
+ priv->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
|
|
+ priv->scale = 1;
|
|
+ priv->contrast = 50;
|
|
+ priv->brightness = 50;
|
|
+ priv->flicker = 50;
|
|
+ priv->hmargin = 50;
|
|
+ priv->vmargin = 50;
|
|
+ priv->last_dpms = -1;
|
|
+
|
|
+ if (ch7006_tv_norm) {
|
|
+ for (i = 0; i < NUM_TV_NORMS; i++) {
|
|
+ if (!strcmp(ch7006_tv_norm_names[i], ch7006_tv_norm)) {
|
|
+ priv->norm = i;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (i == NUM_TV_NORMS)
|
|
+ ch7006_err(client, "Invalid TV norm setting \"%s\".\n",
|
|
+ ch7006_tv_norm);
|
|
+ }
|
|
+
|
|
+ if (ch7006_scale >= 0 && ch7006_scale <= 2)
|
|
+ priv->scale = ch7006_scale;
|
|
+ else
|
|
+ ch7006_err(client, "Invalid scale setting \"%d\".\n",
|
|
+ ch7006_scale);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct i2c_device_id ch7006_ids[] = {
|
|
+ { "ch7006", 0 },
|
|
+ { }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(i2c, ch7006_ids);
|
|
+
|
|
+static struct drm_i2c_encoder_driver ch7006_driver = {
|
|
+ .i2c_driver = {
|
|
+ .probe = ch7006_probe,
|
|
+ .remove = ch7006_remove,
|
|
+
|
|
+ .driver = {
|
|
+ .name = "ch7006",
|
|
+ },
|
|
+
|
|
+ .id_table = ch7006_ids,
|
|
+ },
|
|
+
|
|
+ .encoder_init = ch7006_encoder_init,
|
|
+};
|
|
+
|
|
+
|
|
+/* Module initialization */
|
|
+
|
|
+static int __init ch7006_init(void)
|
|
+{
|
|
+ return drm_i2c_encoder_register(THIS_MODULE, &ch7006_driver);
|
|
+}
|
|
+
|
|
+static void __exit ch7006_exit(void)
|
|
+{
|
|
+ drm_i2c_encoder_unregister(&ch7006_driver);
|
|
+}
|
|
+
|
|
+int ch7006_debug;
|
|
+module_param_named(debug, ch7006_debug, int, 0600);
|
|
+MODULE_PARM_DESC(debug, "Enable debug output.");
|
|
+
|
|
+char *ch7006_tv_norm;
|
|
+module_param_named(tv_norm, ch7006_tv_norm, charp, 0600);
|
|
+MODULE_PARM_DESC(tv_norm, "Default TV norm.\n"
|
|
+ "\t\tSupported: PAL, PAL-M, PAL-N, PAL-Nc, PAL-60, NTSC-M, NTSC-J.\n"
|
|
+ "\t\tDefault: PAL");
|
|
+
|
|
+int ch7006_scale = 1;
|
|
+module_param_named(scale, ch7006_scale, int, 0600);
|
|
+MODULE_PARM_DESC(scale, "Default scale.\n"
|
|
+ "\t\tSupported: 0 -> Select video modes with a higher blanking ratio.\n"
|
|
+ "\t\t\t1 -> Select default video modes.\n"
|
|
+ "\t\t\t2 -> Select video modes with a lower blanking ratio.");
|
|
+
|
|
+MODULE_AUTHOR("Francisco Jerez <currojerez@riseup.net>");
|
|
+MODULE_DESCRIPTION("Chrontel ch7006 TV encoder driver");
|
|
+MODULE_LICENSE("GPL and additional rights");
|
|
+
|
|
+module_init(ch7006_init);
|
|
+module_exit(ch7006_exit);
|
|
diff --git a/drivers/gpu/drm/i2c/ch7006_mode.c b/drivers/gpu/drm/i2c/ch7006_mode.c
|
|
new file mode 100644
|
|
index 0000000..e447dfb
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/i2c/ch7006_mode.c
|
|
@@ -0,0 +1,468 @@
|
|
+/*
|
|
+ * Copyright (C) 2009 Francisco Jerez.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "ch7006_priv.h"
|
|
+
|
|
+char *ch7006_tv_norm_names[] = {
|
|
+ [TV_NORM_PAL] = "PAL",
|
|
+ [TV_NORM_PAL_M] = "PAL-M",
|
|
+ [TV_NORM_PAL_N] = "PAL-N",
|
|
+ [TV_NORM_PAL_NC] = "PAL-Nc",
|
|
+ [TV_NORM_PAL_60] = "PAL-60",
|
|
+ [TV_NORM_NTSC_M] = "NTSC-M",
|
|
+ [TV_NORM_NTSC_J] = "NTSC-J",
|
|
+};
|
|
+
|
|
+#define NTSC_LIKE_TIMINGS .vrefresh = 60 * fixed1/1.001, \
|
|
+ .vdisplay = 480, \
|
|
+ .vtotal = 525, \
|
|
+ .hvirtual = 660
|
|
+
|
|
+#define PAL_LIKE_TIMINGS .vrefresh = 50 * fixed1, \
|
|
+ .vdisplay = 576, \
|
|
+ .vtotal = 625, \
|
|
+ .hvirtual = 810
|
|
+
|
|
+struct ch7006_tv_norm_info ch7006_tv_norms[] = {
|
|
+ [TV_NORM_NTSC_M] = {
|
|
+ NTSC_LIKE_TIMINGS,
|
|
+ .black_level = 0.339 * fixed1,
|
|
+ .subc_freq = 3579545 * fixed1,
|
|
+ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, NTSC),
|
|
+ .voffset = 0,
|
|
+ },
|
|
+ [TV_NORM_NTSC_J] = {
|
|
+ NTSC_LIKE_TIMINGS,
|
|
+ .black_level = 0.286 * fixed1,
|
|
+ .subc_freq = 3579545 * fixed1,
|
|
+ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, NTSC_J),
|
|
+ .voffset = 0,
|
|
+ },
|
|
+ [TV_NORM_PAL] = {
|
|
+ PAL_LIKE_TIMINGS,
|
|
+ .black_level = 0.3 * fixed1,
|
|
+ .subc_freq = 4433618.75 * fixed1,
|
|
+ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL),
|
|
+ .voffset = 0,
|
|
+ },
|
|
+ [TV_NORM_PAL_M] = {
|
|
+ NTSC_LIKE_TIMINGS,
|
|
+ .black_level = 0.339 * fixed1,
|
|
+ .subc_freq = 3575611.433 * fixed1,
|
|
+ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL_M),
|
|
+ .voffset = 16,
|
|
+ },
|
|
+
|
|
+ /* The following modes seem to work right but they're
|
|
+ * undocumented */
|
|
+
|
|
+ [TV_NORM_PAL_N] = {
|
|
+ PAL_LIKE_TIMINGS,
|
|
+ .black_level = 0.339 * fixed1,
|
|
+ .subc_freq = 4433618.75 * fixed1,
|
|
+ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL),
|
|
+ .voffset = 0,
|
|
+ },
|
|
+ [TV_NORM_PAL_NC] = {
|
|
+ PAL_LIKE_TIMINGS,
|
|
+ .black_level = 0.3 * fixed1,
|
|
+ .subc_freq = 3582056.25 * fixed1,
|
|
+ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL),
|
|
+ .voffset = 0,
|
|
+ },
|
|
+ [TV_NORM_PAL_60] = {
|
|
+ NTSC_LIKE_TIMINGS,
|
|
+ .black_level = 0.3 * fixed1,
|
|
+ .subc_freq = 4433618.75 * fixed1,
|
|
+ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL_M),
|
|
+ .voffset = 16,
|
|
+ },
|
|
+};
|
|
+
|
|
+#define __MODE(f, hd, vd, ht, vt, hsynp, vsynp, \
|
|
+ subc, scale, scale_mask, norm_mask, e_hd, e_vd) { \
|
|
+ .mode = { \
|
|
+ .name = #hd "x" #vd, \
|
|
+ .status = 0, \
|
|
+ .type = DRM_MODE_TYPE_DRIVER, \
|
|
+ .clock = f, \
|
|
+ .hdisplay = hd, \
|
|
+ .hsync_start = e_hd + 16, \
|
|
+ .hsync_end = e_hd + 80, \
|
|
+ .htotal = ht, \
|
|
+ .hskew = 0, \
|
|
+ .vdisplay = vd, \
|
|
+ .vsync_start = vd + 10, \
|
|
+ .vsync_end = vd + 26, \
|
|
+ .vtotal = vt, \
|
|
+ .vscan = 0, \
|
|
+ .flags = DRM_MODE_FLAG_##hsynp##HSYNC | \
|
|
+ DRM_MODE_FLAG_##vsynp##VSYNC, \
|
|
+ .vrefresh = 0, \
|
|
+ }, \
|
|
+ .enc_hdisp = e_hd, \
|
|
+ .enc_vdisp = e_vd, \
|
|
+ .subc_coeff = subc * fixed1, \
|
|
+ .dispmode = bitfs(CH7006_DISPMODE_SCALING_RATIO, scale) | \
|
|
+ bitfs(CH7006_DISPMODE_INPUT_RES, e_hd##x##e_vd), \
|
|
+ .valid_scales = scale_mask, \
|
|
+ .valid_norms = norm_mask \
|
|
+ }
|
|
+
|
|
+#define MODE(f, hd, vd, ht, vt, hsynp, vsynp, \
|
|
+ subc, scale, scale_mask, norm_mask) \
|
|
+ __MODE(f, hd, vd, ht, vt, hsynp, vsynp, subc, scale, \
|
|
+ scale_mask, norm_mask, hd, vd)
|
|
+
|
|
+#define NTSC_LIKE (1 << TV_NORM_NTSC_M | 1 << TV_NORM_NTSC_J | \
|
|
+ 1 << TV_NORM_PAL_M | 1 << TV_NORM_PAL_60)
|
|
+
|
|
+#define PAL_LIKE (1 << TV_NORM_PAL | 1 << TV_NORM_PAL_N | 1 << TV_NORM_PAL_NC)
|
|
+
|
|
+struct ch7006_mode ch7006_modes[] = {
|
|
+ MODE(21000, 512, 384, 840, 500, N, N, 181.797557582, 5_4, 0x6, PAL_LIKE),
|
|
+ MODE(26250, 512, 384, 840, 625, N, N, 145.438046066, 1_1, 0x1, PAL_LIKE),
|
|
+ MODE(20140, 512, 384, 800, 420, N, N, 213.257083791, 5_4, 0x4, NTSC_LIKE),
|
|
+ MODE(24671, 512, 384, 784, 525, N, N, 174.0874153, 1_1, 0x3, NTSC_LIKE),
|
|
+ MODE(28125, 720, 400, 1125, 500, N, N, 135.742176298, 5_4, 0x6, PAL_LIKE),
|
|
+ MODE(34875, 720, 400, 1116, 625, N, N, 109.469496898, 1_1, 0x1, PAL_LIKE),
|
|
+ MODE(23790, 720, 400, 945, 420, N, N, 160.475642016, 5_4, 0x4, NTSC_LIKE),
|
|
+ MODE(29455, 720, 400, 936, 525, N, N, 129.614941843, 1_1, 0x3, NTSC_LIKE),
|
|
+ MODE(25000, 640, 400, 1000, 500, N, N, 152.709948279, 5_4, 0x6, PAL_LIKE),
|
|
+ MODE(31500, 640, 400, 1008, 625, N, N, 121.198371646, 1_1, 0x1, PAL_LIKE),
|
|
+ MODE(21147, 640, 400, 840, 420, N, N, 180.535097338, 5_4, 0x4, NTSC_LIKE),
|
|
+ MODE(26434, 640, 400, 840, 525, N, N, 144.42807787, 1_1, 0x2, NTSC_LIKE),
|
|
+ MODE(30210, 640, 400, 840, 600, N, N, 126.374568276, 7_8, 0x1, NTSC_LIKE),
|
|
+ MODE(21000, 640, 480, 840, 500, N, N, 181.797557582, 5_4, 0x4, PAL_LIKE),
|
|
+ MODE(26250, 640, 480, 840, 625, N, N, 145.438046066, 1_1, 0x2, PAL_LIKE),
|
|
+ MODE(31500, 640, 480, 840, 750, N, N, 121.198371646, 5_6, 0x1, PAL_LIKE),
|
|
+ MODE(24671, 640, 480, 784, 525, N, N, 174.0874153, 1_1, 0x4, NTSC_LIKE),
|
|
+ MODE(28196, 640, 480, 784, 600, N, N, 152.326488422, 7_8, 0x2, NTSC_LIKE),
|
|
+ MODE(30210, 640, 480, 800, 630, N, N, 142.171389101, 5_6, 0x1, NTSC_LIKE),
|
|
+ __MODE(29500, 720, 576, 944, 625, P, P, 145.592111636, 1_1, 0x7, PAL_LIKE, 800, 600),
|
|
+ MODE(36000, 800, 600, 960, 750, P, P, 119.304647022, 5_6, 0x6, PAL_LIKE),
|
|
+ MODE(39000, 800, 600, 936, 836, P, P, 110.127366499, 3_4, 0x1, PAL_LIKE),
|
|
+ MODE(39273, 800, 600, 1040, 630, P, P, 145.816809399, 5_6, 0x4, NTSC_LIKE),
|
|
+ MODE(43636, 800, 600, 1040, 700, P, P, 131.235128487, 3_4, 0x2, NTSC_LIKE),
|
|
+ MODE(47832, 800, 600, 1064, 750, P, P, 119.723275165, 7_10, 0x1, NTSC_LIKE),
|
|
+ {}
|
|
+};
|
|
+
|
|
+struct ch7006_mode *ch7006_lookup_mode(struct drm_encoder *encoder,
|
|
+ struct drm_display_mode *drm_mode)
|
|
+{
|
|
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
|
|
+ struct ch7006_mode *mode;
|
|
+
|
|
+ for (mode = ch7006_modes; mode->mode.clock; mode++) {
|
|
+
|
|
+ if (~mode->valid_norms & 1<<priv->norm)
|
|
+ continue;
|
|
+
|
|
+ if (mode->mode.hdisplay != drm_mode->hdisplay ||
|
|
+ mode->mode.vdisplay != drm_mode->vdisplay ||
|
|
+ mode->mode.vtotal != drm_mode->vtotal ||
|
|
+ mode->mode.htotal != drm_mode->htotal ||
|
|
+ mode->mode.clock != drm_mode->clock)
|
|
+ continue;
|
|
+
|
|
+ return mode;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/* Some common HW state calculation code */
|
|
+
|
|
+void ch7006_setup_levels(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
|
|
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
|
|
+ uint8_t *regs = priv->state.regs;
|
|
+ struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
|
|
+ int gain;
|
|
+ int black_level;
|
|
+
|
|
+ /* Set DAC_GAIN if the voltage drop between white and black is
|
|
+ * high enough. */
|
|
+ if (norm->black_level < 339*fixed1/1000) {
|
|
+ gain = 76;
|
|
+
|
|
+ regs[CH7006_INPUT_FORMAT] |= CH7006_INPUT_FORMAT_DAC_GAIN;
|
|
+ } else {
|
|
+ gain = 71;
|
|
+
|
|
+ regs[CH7006_INPUT_FORMAT] &= ~CH7006_INPUT_FORMAT_DAC_GAIN;
|
|
+ }
|
|
+
|
|
+ black_level = round_fixed(norm->black_level*26625)/gain;
|
|
+
|
|
+ /* Correct it with the specified brightness. */
|
|
+ black_level = interpolate(90, black_level, 208, priv->brightness);
|
|
+
|
|
+ regs[CH7006_BLACK_LEVEL] = bitf(CH7006_BLACK_LEVEL_0, black_level);
|
|
+
|
|
+ ch7006_dbg(client, "black level: %d\n", black_level);
|
|
+}
|
|
+
|
|
+void ch7006_setup_subcarrier(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
|
|
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
|
|
+ struct ch7006_state *state = &priv->state;
|
|
+ struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
|
|
+ struct ch7006_mode *mode = priv->mode;
|
|
+ uint32_t subc_inc;
|
|
+
|
|
+ subc_inc = round_fixed((mode->subc_coeff >> 8)
|
|
+ * (norm->subc_freq >> 24));
|
|
+
|
|
+ setbitf(state, CH7006_SUBC_INC0, 28, subc_inc);
|
|
+ setbitf(state, CH7006_SUBC_INC1, 24, subc_inc);
|
|
+ setbitf(state, CH7006_SUBC_INC2, 20, subc_inc);
|
|
+ setbitf(state, CH7006_SUBC_INC3, 16, subc_inc);
|
|
+ setbitf(state, CH7006_SUBC_INC4, 12, subc_inc);
|
|
+ setbitf(state, CH7006_SUBC_INC5, 8, subc_inc);
|
|
+ setbitf(state, CH7006_SUBC_INC6, 4, subc_inc);
|
|
+ setbitf(state, CH7006_SUBC_INC7, 0, subc_inc);
|
|
+
|
|
+ ch7006_dbg(client, "subcarrier inc: %u\n", subc_inc);
|
|
+}
|
|
+
|
|
+void ch7006_setup_pll(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
|
|
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
|
|
+ uint8_t *regs = priv->state.regs;
|
|
+ struct ch7006_mode *mode = priv->mode;
|
|
+ int n, best_n = 0;
|
|
+ int m, best_m = 0;
|
|
+ int freq, best_freq = 0;
|
|
+
|
|
+ for (n = 0; n < CH7006_MAXN; n++) {
|
|
+ for (m = 0; m < CH7006_MAXM; m++) {
|
|
+ freq = CH7006_FREQ0*(n+2)/(m+2);
|
|
+
|
|
+ if (abs(freq - mode->mode.clock) <
|
|
+ abs(best_freq - mode->mode.clock)) {
|
|
+ best_freq = freq;
|
|
+ best_n = n;
|
|
+ best_m = m;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ regs[CH7006_PLLOV] = bitf(CH7006_PLLOV_N_8, best_n) |
|
|
+ bitf(CH7006_PLLOV_M_8, best_m);
|
|
+
|
|
+ regs[CH7006_PLLM] = bitf(CH7006_PLLM_0, best_m);
|
|
+ regs[CH7006_PLLN] = bitf(CH7006_PLLN_0, best_n);
|
|
+
|
|
+ if (best_n < 108)
|
|
+ regs[CH7006_PLL_CONTROL] |= CH7006_PLL_CONTROL_CAPACITOR;
|
|
+ else
|
|
+ regs[CH7006_PLL_CONTROL] &= ~CH7006_PLL_CONTROL_CAPACITOR;
|
|
+
|
|
+ ch7006_dbg(client, "n=%d m=%d f=%d c=%d\n",
|
|
+ best_n, best_m, best_freq, best_n < 108);
|
|
+}
|
|
+
|
|
+void ch7006_setup_power_state(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
|
|
+ uint8_t *power = &priv->state.regs[CH7006_POWER];
|
|
+ int subconnector;
|
|
+
|
|
+ subconnector = priv->select_subconnector ? priv->select_subconnector :
|
|
+ priv->subconnector;
|
|
+
|
|
+ *power = CH7006_POWER_RESET;
|
|
+
|
|
+ if (priv->last_dpms == DRM_MODE_DPMS_ON) {
|
|
+ switch (subconnector) {
|
|
+ case DRM_MODE_SUBCONNECTOR_SVIDEO:
|
|
+ *power |= bitfs(CH7006_POWER_LEVEL, CVBS_OFF);
|
|
+ break;
|
|
+ case DRM_MODE_SUBCONNECTOR_Composite:
|
|
+ *power |= bitfs(CH7006_POWER_LEVEL, SVIDEO_OFF);
|
|
+ break;
|
|
+ case DRM_MODE_SUBCONNECTOR_SCART:
|
|
+ *power |= bitfs(CH7006_POWER_LEVEL, NORMAL) |
|
|
+ CH7006_POWER_SCART;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ } else {
|
|
+ *power |= bitfs(CH7006_POWER_LEVEL, FULL_POWER_OFF);
|
|
+ }
|
|
+}
|
|
+
|
|
+void ch7006_setup_properties(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
|
|
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
|
|
+ struct ch7006_state *state = &priv->state;
|
|
+ struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
|
|
+ struct ch7006_mode *ch_mode = priv->mode;
|
|
+ struct drm_display_mode *mode = &ch_mode->mode;
|
|
+ uint8_t *regs = state->regs;
|
|
+ int flicker, contrast, hpos, vpos;
|
|
+ uint64_t scale, aspect;
|
|
+
|
|
+ flicker = interpolate(0, 2, 3, priv->flicker);
|
|
+ regs[CH7006_FFILTER] = bitf(CH7006_FFILTER_TEXT, flicker) |
|
|
+ bitf(CH7006_FFILTER_LUMA, flicker) |
|
|
+ bitf(CH7006_FFILTER_CHROMA, 1);
|
|
+
|
|
+ contrast = interpolate(0, 5, 7, priv->contrast);
|
|
+ regs[CH7006_CONTRAST] = bitf(CH7006_CONTRAST_0, contrast);
|
|
+
|
|
+ scale = norm->vtotal*fixed1;
|
|
+ do_div(scale, mode->vtotal);
|
|
+
|
|
+ aspect = ch_mode->enc_hdisp*fixed1;
|
|
+ do_div(aspect, ch_mode->enc_vdisp);
|
|
+
|
|
+ hpos = round_fixed((norm->hvirtual * aspect - mode->hdisplay * scale)
|
|
+ * priv->hmargin * mode->vtotal) / norm->vtotal / 100 / 4;
|
|
+
|
|
+ setbitf(state, CH7006_POV, HPOS_8, hpos);
|
|
+ setbitf(state, CH7006_HPOS, 0, hpos);
|
|
+
|
|
+ vpos = max(0, norm->vdisplay - round_fixed(mode->vdisplay*scale)
|
|
+ + norm->voffset) * priv->vmargin / 100 / 2;
|
|
+
|
|
+ setbitf(state, CH7006_POV, VPOS_8, vpos);
|
|
+ setbitf(state, CH7006_VPOS, 0, vpos);
|
|
+
|
|
+ ch7006_dbg(client, "hpos: %d, vpos: %d\n", hpos, vpos);
|
|
+}
|
|
+
|
|
+/* HW access functions */
|
|
+
|
|
+void ch7006_write(struct i2c_client *client, uint8_t addr, uint8_t val)
|
|
+{
|
|
+ uint8_t buf[] = {addr, val};
|
|
+ int ret;
|
|
+
|
|
+ ret = i2c_master_send(client, buf, ARRAY_SIZE(buf));
|
|
+ if (ret < 0)
|
|
+ ch7006_err(client, "Error %d writing to subaddress 0x%x\n",
|
|
+ ret, addr);
|
|
+}
|
|
+
|
|
+uint8_t ch7006_read(struct i2c_client *client, uint8_t addr)
|
|
+{
|
|
+ uint8_t val;
|
|
+ int ret;
|
|
+
|
|
+ ret = i2c_master_send(client, &addr, sizeof(addr));
|
|
+ if (ret < 0)
|
|
+ goto fail;
|
|
+
|
|
+ ret = i2c_master_recv(client, &val, sizeof(val));
|
|
+ if (ret < 0)
|
|
+ goto fail;
|
|
+
|
|
+ return val;
|
|
+
|
|
+fail:
|
|
+ ch7006_err(client, "Error %d reading from subaddress 0x%x\n",
|
|
+ ret, addr);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void ch7006_state_load(struct i2c_client *client,
|
|
+ struct ch7006_state *state)
|
|
+{
|
|
+ ch7006_load_reg(client, state, CH7006_POWER);
|
|
+
|
|
+ ch7006_load_reg(client, state, CH7006_DISPMODE);
|
|
+ ch7006_load_reg(client, state, CH7006_FFILTER);
|
|
+ ch7006_load_reg(client, state, CH7006_BWIDTH);
|
|
+ ch7006_load_reg(client, state, CH7006_INPUT_FORMAT);
|
|
+ ch7006_load_reg(client, state, CH7006_CLKMODE);
|
|
+ ch7006_load_reg(client, state, CH7006_START_ACTIVE);
|
|
+ ch7006_load_reg(client, state, CH7006_POV);
|
|
+ ch7006_load_reg(client, state, CH7006_BLACK_LEVEL);
|
|
+ ch7006_load_reg(client, state, CH7006_HPOS);
|
|
+ ch7006_load_reg(client, state, CH7006_VPOS);
|
|
+ ch7006_load_reg(client, state, CH7006_INPUT_SYNC);
|
|
+ ch7006_load_reg(client, state, CH7006_DETECT);
|
|
+ ch7006_load_reg(client, state, CH7006_CONTRAST);
|
|
+ ch7006_load_reg(client, state, CH7006_PLLOV);
|
|
+ ch7006_load_reg(client, state, CH7006_PLLM);
|
|
+ ch7006_load_reg(client, state, CH7006_PLLN);
|
|
+ ch7006_load_reg(client, state, CH7006_BCLKOUT);
|
|
+ ch7006_load_reg(client, state, CH7006_SUBC_INC0);
|
|
+ ch7006_load_reg(client, state, CH7006_SUBC_INC1);
|
|
+ ch7006_load_reg(client, state, CH7006_SUBC_INC2);
|
|
+ ch7006_load_reg(client, state, CH7006_SUBC_INC3);
|
|
+ ch7006_load_reg(client, state, CH7006_SUBC_INC4);
|
|
+ ch7006_load_reg(client, state, CH7006_SUBC_INC5);
|
|
+ ch7006_load_reg(client, state, CH7006_SUBC_INC6);
|
|
+ ch7006_load_reg(client, state, CH7006_SUBC_INC7);
|
|
+ ch7006_load_reg(client, state, CH7006_PLL_CONTROL);
|
|
+ ch7006_load_reg(client, state, CH7006_CALC_SUBC_INC0);
|
|
+}
|
|
+
|
|
+void ch7006_state_save(struct i2c_client *client,
|
|
+ struct ch7006_state *state)
|
|
+{
|
|
+ ch7006_save_reg(client, state, CH7006_POWER);
|
|
+
|
|
+ ch7006_save_reg(client, state, CH7006_DISPMODE);
|
|
+ ch7006_save_reg(client, state, CH7006_FFILTER);
|
|
+ ch7006_save_reg(client, state, CH7006_BWIDTH);
|
|
+ ch7006_save_reg(client, state, CH7006_INPUT_FORMAT);
|
|
+ ch7006_save_reg(client, state, CH7006_CLKMODE);
|
|
+ ch7006_save_reg(client, state, CH7006_START_ACTIVE);
|
|
+ ch7006_save_reg(client, state, CH7006_POV);
|
|
+ ch7006_save_reg(client, state, CH7006_BLACK_LEVEL);
|
|
+ ch7006_save_reg(client, state, CH7006_HPOS);
|
|
+ ch7006_save_reg(client, state, CH7006_VPOS);
|
|
+ ch7006_save_reg(client, state, CH7006_INPUT_SYNC);
|
|
+ ch7006_save_reg(client, state, CH7006_DETECT);
|
|
+ ch7006_save_reg(client, state, CH7006_CONTRAST);
|
|
+ ch7006_save_reg(client, state, CH7006_PLLOV);
|
|
+ ch7006_save_reg(client, state, CH7006_PLLM);
|
|
+ ch7006_save_reg(client, state, CH7006_PLLN);
|
|
+ ch7006_save_reg(client, state, CH7006_BCLKOUT);
|
|
+ ch7006_save_reg(client, state, CH7006_SUBC_INC0);
|
|
+ ch7006_save_reg(client, state, CH7006_SUBC_INC1);
|
|
+ ch7006_save_reg(client, state, CH7006_SUBC_INC2);
|
|
+ ch7006_save_reg(client, state, CH7006_SUBC_INC3);
|
|
+ ch7006_save_reg(client, state, CH7006_SUBC_INC4);
|
|
+ ch7006_save_reg(client, state, CH7006_SUBC_INC5);
|
|
+ ch7006_save_reg(client, state, CH7006_SUBC_INC6);
|
|
+ ch7006_save_reg(client, state, CH7006_SUBC_INC7);
|
|
+ ch7006_save_reg(client, state, CH7006_PLL_CONTROL);
|
|
+ ch7006_save_reg(client, state, CH7006_CALC_SUBC_INC0);
|
|
+
|
|
+ state->regs[CH7006_FFILTER] = (state->regs[CH7006_FFILTER] & 0xf0) |
|
|
+ (state->regs[CH7006_FFILTER] & 0x0c) >> 2 |
|
|
+ (state->regs[CH7006_FFILTER] & 0x03) << 2;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/i2c/ch7006_priv.h b/drivers/gpu/drm/i2c/ch7006_priv.h
|
|
new file mode 100644
|
|
index 0000000..b06d3d9
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/i2c/ch7006_priv.h
|
|
@@ -0,0 +1,344 @@
|
|
+/*
|
|
+ * Copyright (C) 2009 Francisco Jerez.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef __DRM_I2C_CH7006_PRIV_H__
|
|
+#define __DRM_I2C_CH7006_PRIV_H__
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm_crtc_helper.h"
|
|
+#include "drm_encoder_slave.h"
|
|
+#include "i2c/ch7006.h"
|
|
+
|
|
+typedef int64_t fixed;
|
|
+#define fixed1 (1LL << 32)
|
|
+
|
|
+enum ch7006_tv_norm {
|
|
+ TV_NORM_PAL,
|
|
+ TV_NORM_PAL_M,
|
|
+ TV_NORM_PAL_N,
|
|
+ TV_NORM_PAL_NC,
|
|
+ TV_NORM_PAL_60,
|
|
+ TV_NORM_NTSC_M,
|
|
+ TV_NORM_NTSC_J,
|
|
+ NUM_TV_NORMS
|
|
+};
|
|
+
|
|
+struct ch7006_tv_norm_info {
|
|
+ fixed vrefresh;
|
|
+ int vdisplay;
|
|
+ int vtotal;
|
|
+ int hvirtual;
|
|
+
|
|
+ fixed subc_freq;
|
|
+ fixed black_level;
|
|
+
|
|
+ uint32_t dispmode;
|
|
+ int voffset;
|
|
+};
|
|
+
|
|
+struct ch7006_mode {
|
|
+ struct drm_display_mode mode;
|
|
+
|
|
+ int enc_hdisp;
|
|
+ int enc_vdisp;
|
|
+
|
|
+ fixed subc_coeff;
|
|
+ uint32_t dispmode;
|
|
+
|
|
+ uint32_t valid_scales;
|
|
+ uint32_t valid_norms;
|
|
+};
|
|
+
|
|
+struct ch7006_state {
|
|
+ uint8_t regs[0x26];
|
|
+};
|
|
+
|
|
+struct ch7006_priv {
|
|
+ struct ch7006_encoder_params *params;
|
|
+ struct ch7006_mode *mode;
|
|
+
|
|
+ struct ch7006_state state;
|
|
+ struct ch7006_state saved_state;
|
|
+
|
|
+ struct drm_property *scale_property;
|
|
+
|
|
+ int select_subconnector;
|
|
+ int subconnector;
|
|
+ int hmargin;
|
|
+ int vmargin;
|
|
+ enum ch7006_tv_norm norm;
|
|
+ int brightness;
|
|
+ int contrast;
|
|
+ int flicker;
|
|
+ int scale;
|
|
+
|
|
+ int last_dpms;
|
|
+};
|
|
+
|
|
+#define to_ch7006_priv(x) \
|
|
+ ((struct ch7006_priv *)to_encoder_slave(x)->slave_priv)
|
|
+
|
|
+extern int ch7006_debug;
|
|
+extern char *ch7006_tv_norm;
|
|
+extern int ch7006_scale;
|
|
+
|
|
+extern char *ch7006_tv_norm_names[];
|
|
+extern struct ch7006_tv_norm_info ch7006_tv_norms[];
|
|
+extern struct ch7006_mode ch7006_modes[];
|
|
+
|
|
+struct ch7006_mode *ch7006_lookup_mode(struct drm_encoder *encoder,
|
|
+ struct drm_display_mode *drm_mode);
|
|
+
|
|
+void ch7006_setup_levels(struct drm_encoder *encoder);
|
|
+void ch7006_setup_subcarrier(struct drm_encoder *encoder);
|
|
+void ch7006_setup_pll(struct drm_encoder *encoder);
|
|
+void ch7006_setup_power_state(struct drm_encoder *encoder);
|
|
+void ch7006_setup_properties(struct drm_encoder *encoder);
|
|
+
|
|
+void ch7006_write(struct i2c_client *client, uint8_t addr, uint8_t val);
|
|
+uint8_t ch7006_read(struct i2c_client *client, uint8_t addr);
|
|
+
|
|
+void ch7006_state_load(struct i2c_client *client,
|
|
+ struct ch7006_state *state);
|
|
+void ch7006_state_save(struct i2c_client *client,
|
|
+ struct ch7006_state *state);
|
|
+
|
|
+/* Some helper macros */
|
|
+
|
|
+#define ch7006_dbg(client, format, ...) do { \
|
|
+ if (ch7006_debug) \
|
|
+ dev_printk(KERN_DEBUG, &client->dev, \
|
|
+ "%s: " format, __func__, ## __VA_ARGS__); \
|
|
+ } while (0)
|
|
+#define ch7006_info(client, format, ...) \
|
|
+ dev_info(&client->dev, format, __VA_ARGS__)
|
|
+#define ch7006_err(client, format, ...) \
|
|
+ dev_err(&client->dev, format, __VA_ARGS__)
|
|
+
|
|
+#define __mask(src, bitfield) \
|
|
+ (((2 << (1 ? bitfield)) - 1) & ~((1 << (0 ? bitfield)) - 1))
|
|
+#define mask(bitfield) __mask(bitfield)
|
|
+
|
|
+#define __bitf(src, bitfield, x) \
|
|
+ (((x) >> (src) << (0 ? bitfield)) & __mask(src, bitfield))
|
|
+#define bitf(bitfield, x) __bitf(bitfield, x)
|
|
+#define bitfs(bitfield, s) __bitf(bitfield, bitfield##_##s)
|
|
+#define setbitf(state, reg, bitfield, x) \
|
|
+ state->regs[reg] = (state->regs[reg] & ~mask(reg##_##bitfield)) \
|
|
+ | bitf(reg##_##bitfield, x)
|
|
+
|
|
+#define __unbitf(src, bitfield, x) \
|
|
+ ((x & __mask(src, bitfield)) >> (0 ? bitfield) << (src))
|
|
+#define unbitf(bitfield, x) __unbitf(bitfield, x)
|
|
+
|
|
+static inline int interpolate(int y0, int y1, int y2, int x)
|
|
+{
|
|
+ return y1 + (x < 50 ? y1 - y0 : y2 - y1) * (x - 50) / 50;
|
|
+}
|
|
+
|
|
+static inline int32_t round_fixed(fixed x)
|
|
+{
|
|
+ return (x + fixed1/2) >> 32;
|
|
+}
|
|
+
|
|
+#define ch7006_load_reg(client, state, reg) ch7006_write(client, reg, state->regs[reg])
|
|
+#define ch7006_save_reg(client, state, reg) state->regs[reg] = ch7006_read(client, reg)
|
|
+
|
|
+/* Fixed hardware specs */
|
|
+
|
|
+#define CH7006_FREQ0 14318
|
|
+#define CH7006_MAXN 650
|
|
+#define CH7006_MAXM 315
|
|
+
|
|
+/* Register definitions */
|
|
+
|
|
+#define CH7006_DISPMODE 0x00
|
|
+#define CH7006_DISPMODE_INPUT_RES 0, 7:5
|
|
+#define CH7006_DISPMODE_INPUT_RES_512x384 0x0
|
|
+#define CH7006_DISPMODE_INPUT_RES_720x400 0x1
|
|
+#define CH7006_DISPMODE_INPUT_RES_640x400 0x2
|
|
+#define CH7006_DISPMODE_INPUT_RES_640x480 0x3
|
|
+#define CH7006_DISPMODE_INPUT_RES_800x600 0x4
|
|
+#define CH7006_DISPMODE_INPUT_RES_NATIVE 0x5
|
|
+#define CH7006_DISPMODE_OUTPUT_STD 0, 4:3
|
|
+#define CH7006_DISPMODE_OUTPUT_STD_PAL 0x0
|
|
+#define CH7006_DISPMODE_OUTPUT_STD_NTSC 0x1
|
|
+#define CH7006_DISPMODE_OUTPUT_STD_PAL_M 0x2
|
|
+#define CH7006_DISPMODE_OUTPUT_STD_NTSC_J 0x3
|
|
+#define CH7006_DISPMODE_SCALING_RATIO 0, 2:0
|
|
+#define CH7006_DISPMODE_SCALING_RATIO_5_4 0x0
|
|
+#define CH7006_DISPMODE_SCALING_RATIO_1_1 0x1
|
|
+#define CH7006_DISPMODE_SCALING_RATIO_7_8 0x2
|
|
+#define CH7006_DISPMODE_SCALING_RATIO_5_6 0x3
|
|
+#define CH7006_DISPMODE_SCALING_RATIO_3_4 0x4
|
|
+#define CH7006_DISPMODE_SCALING_RATIO_7_10 0x5
|
|
+
|
|
+#define CH7006_FFILTER 0x01
|
|
+#define CH7006_FFILTER_TEXT 0, 5:4
|
|
+#define CH7006_FFILTER_LUMA 0, 3:2
|
|
+#define CH7006_FFILTER_CHROMA 0, 1:0
|
|
+#define CH7006_FFILTER_CHROMA_NO_DCRAWL 0x3
|
|
+
|
|
+#define CH7006_BWIDTH 0x03
|
|
+#define CH7006_BWIDTH_5L_FFILER (1 << 7)
|
|
+#define CH7006_BWIDTH_CVBS_NO_CHROMA (1 << 6)
|
|
+#define CH7006_BWIDTH_CHROMA 0, 5:4
|
|
+#define CH7006_BWIDTH_SVIDEO_YPEAK (1 << 3)
|
|
+#define CH7006_BWIDTH_SVIDEO_LUMA 0, 2:1
|
|
+#define CH7006_BWIDTH_CVBS_LUMA 0, 0:0
|
|
+
|
|
+#define CH7006_INPUT_FORMAT 0x04
|
|
+#define CH7006_INPUT_FORMAT_DAC_GAIN (1 << 6)
|
|
+#define CH7006_INPUT_FORMAT_RGB_PASS_THROUGH (1 << 5)
|
|
+#define CH7006_INPUT_FORMAT_FORMAT 0, 3:0
|
|
+#define CH7006_INPUT_FORMAT_FORMAT_RGB16 0x0
|
|
+#define CH7006_INPUT_FORMAT_FORMAT_YCrCb24m16 0x1
|
|
+#define CH7006_INPUT_FORMAT_FORMAT_RGB24m16 0x2
|
|
+#define CH7006_INPUT_FORMAT_FORMAT_RGB15 0x3
|
|
+#define CH7006_INPUT_FORMAT_FORMAT_RGB24m12C 0x4
|
|
+#define CH7006_INPUT_FORMAT_FORMAT_RGB24m12I 0x5
|
|
+#define CH7006_INPUT_FORMAT_FORMAT_RGB24m8 0x6
|
|
+#define CH7006_INPUT_FORMAT_FORMAT_RGB16m8 0x7
|
|
+#define CH7006_INPUT_FORMAT_FORMAT_RGB15m8 0x8
|
|
+#define CH7006_INPUT_FORMAT_FORMAT_YCrCb24m8 0x9
|
|
+
|
|
+#define CH7006_CLKMODE 0x06
|
|
+#define CH7006_CLKMODE_SUBC_LOCK (1 << 7)
|
|
+#define CH7006_CLKMODE_MASTER (1 << 6)
|
|
+#define CH7006_CLKMODE_POS_EDGE (1 << 4)
|
|
+#define CH7006_CLKMODE_XCM 0, 3:2
|
|
+#define CH7006_CLKMODE_PCM 0, 1:0
|
|
+
|
|
+#define CH7006_START_ACTIVE 0x07
|
|
+#define CH7006_START_ACTIVE_0 0, 7:0
|
|
+
|
|
+#define CH7006_POV 0x08
|
|
+#define CH7006_POV_START_ACTIVE_8 8, 2:2
|
|
+#define CH7006_POV_HPOS_8 8, 1:1
|
|
+#define CH7006_POV_VPOS_8 8, 0:0
|
|
+
|
|
+#define CH7006_BLACK_LEVEL 0x09
|
|
+#define CH7006_BLACK_LEVEL_0 0, 7:0
|
|
+
|
|
+#define CH7006_HPOS 0x0a
|
|
+#define CH7006_HPOS_0 0, 7:0
|
|
+
|
|
+#define CH7006_VPOS 0x0b
|
|
+#define CH7006_VPOS_0 0, 7:0
|
|
+
|
|
+#define CH7006_INPUT_SYNC 0x0d
|
|
+#define CH7006_INPUT_SYNC_EMBEDDED (1 << 3)
|
|
+#define CH7006_INPUT_SYNC_OUTPUT (1 << 2)
|
|
+#define CH7006_INPUT_SYNC_PVSYNC (1 << 1)
|
|
+#define CH7006_INPUT_SYNC_PHSYNC (1 << 0)
|
|
+
|
|
+#define CH7006_POWER 0x0e
|
|
+#define CH7006_POWER_SCART (1 << 4)
|
|
+#define CH7006_POWER_RESET (1 << 3)
|
|
+#define CH7006_POWER_LEVEL 0, 2:0
|
|
+#define CH7006_POWER_LEVEL_CVBS_OFF 0x0
|
|
+#define CH7006_POWER_LEVEL_POWER_OFF 0x1
|
|
+#define CH7006_POWER_LEVEL_SVIDEO_OFF 0x2
|
|
+#define CH7006_POWER_LEVEL_NORMAL 0x3
|
|
+#define CH7006_POWER_LEVEL_FULL_POWER_OFF 0x4
|
|
+
|
|
+#define CH7006_DETECT 0x10
|
|
+#define CH7006_DETECT_SVIDEO_Y_TEST (1 << 3)
|
|
+#define CH7006_DETECT_SVIDEO_C_TEST (1 << 2)
|
|
+#define CH7006_DETECT_CVBS_TEST (1 << 1)
|
|
+#define CH7006_DETECT_SENSE (1 << 0)
|
|
+
|
|
+#define CH7006_CONTRAST 0x11
|
|
+#define CH7006_CONTRAST_0 0, 2:0
|
|
+
|
|
+#define CH7006_PLLOV 0x13
|
|
+#define CH7006_PLLOV_N_8 8, 2:1
|
|
+#define CH7006_PLLOV_M_8 8, 0:0
|
|
+
|
|
+#define CH7006_PLLM 0x14
|
|
+#define CH7006_PLLM_0 0, 7:0
|
|
+
|
|
+#define CH7006_PLLN 0x15
|
|
+#define CH7006_PLLN_0 0, 7:0
|
|
+
|
|
+#define CH7006_BCLKOUT 0x17
|
|
+
|
|
+#define CH7006_SUBC_INC0 0x18
|
|
+#define CH7006_SUBC_INC0_28 28, 3:0
|
|
+
|
|
+#define CH7006_SUBC_INC1 0x19
|
|
+#define CH7006_SUBC_INC1_24 24, 3:0
|
|
+
|
|
+#define CH7006_SUBC_INC2 0x1a
|
|
+#define CH7006_SUBC_INC2_20 20, 3:0
|
|
+
|
|
+#define CH7006_SUBC_INC3 0x1b
|
|
+#define CH7006_SUBC_INC3_GPIO1_VAL (1 << 7)
|
|
+#define CH7006_SUBC_INC3_GPIO0_VAL (1 << 6)
|
|
+#define CH7006_SUBC_INC3_POUT_3_3V (1 << 5)
|
|
+#define CH7006_SUBC_INC3_POUT_INV (1 << 4)
|
|
+#define CH7006_SUBC_INC3_16 16, 3:0
|
|
+
|
|
+#define CH7006_SUBC_INC4 0x1c
|
|
+#define CH7006_SUBC_INC4_GPIO1_IN (1 << 7)
|
|
+#define CH7006_SUBC_INC4_GPIO0_IN (1 << 6)
|
|
+#define CH7006_SUBC_INC4_DS_INPUT (1 << 4)
|
|
+#define CH7006_SUBC_INC4_12 12, 3:0
|
|
+
|
|
+#define CH7006_SUBC_INC5 0x1d
|
|
+#define CH7006_SUBC_INC5_8 8, 3:0
|
|
+
|
|
+#define CH7006_SUBC_INC6 0x1e
|
|
+#define CH7006_SUBC_INC6_4 4, 3:0
|
|
+
|
|
+#define CH7006_SUBC_INC7 0x1f
|
|
+#define CH7006_SUBC_INC7_0 0, 3:0
|
|
+
|
|
+#define CH7006_PLL_CONTROL 0x20
|
|
+#define CH7006_PLL_CONTROL_CPI (1 << 5)
|
|
+#define CH7006_PLL_CONTROL_CAPACITOR (1 << 4)
|
|
+#define CH7006_PLL_CONTROL_7STAGES (1 << 3)
|
|
+#define CH7006_PLL_CONTROL_DIGITAL_5V (1 << 2)
|
|
+#define CH7006_PLL_CONTROL_ANALOG_5V (1 << 1)
|
|
+#define CH7006_PLL_CONTROL_MEMORY_5V (1 << 0)
|
|
+
|
|
+#define CH7006_CALC_SUBC_INC0 0x21
|
|
+#define CH7006_CALC_SUBC_INC0_24 24, 4:3
|
|
+#define CH7006_CALC_SUBC_INC0_HYST 0, 2:1
|
|
+#define CH7006_CALC_SUBC_INC0_AUTO (1 << 0)
|
|
+
|
|
+#define CH7006_CALC_SUBC_INC1 0x22
|
|
+#define CH7006_CALC_SUBC_INC1_16 16, 7:0
|
|
+
|
|
+#define CH7006_CALC_SUBC_INC2 0x23
|
|
+#define CH7006_CALC_SUBC_INC2_8 8, 7:0
|
|
+
|
|
+#define CH7006_CALC_SUBC_INC3 0x24
|
|
+#define CH7006_CALC_SUBC_INC3_0 0, 7:0
|
|
+
|
|
+#define CH7006_VERSION_ID 0x25
|
|
+
|
|
+#endif
|
|
diff --git a/drivers/gpu/drm/i810/i810_dma.c b/drivers/gpu/drm/i810/i810_dma.c
|
|
index 7d1d88c..de32d22 100644
|
|
--- a/drivers/gpu/drm/i810/i810_dma.c
|
|
+++ b/drivers/gpu/drm/i810/i810_dma.c
|
|
@@ -115,7 +115,7 @@ static int i810_mmap_buffers(struct file *filp, struct vm_area_struct *vma)
|
|
static const struct file_operations i810_buffer_fops = {
|
|
.open = drm_open,
|
|
.release = drm_release,
|
|
- .ioctl = drm_ioctl,
|
|
+ .unlocked_ioctl = drm_ioctl,
|
|
.mmap = i810_mmap_buffers,
|
|
.fasync = drm_fasync,
|
|
};
|
|
diff --git a/drivers/gpu/drm/i810/i810_drv.c b/drivers/gpu/drm/i810/i810_drv.c
|
|
index fabb9a8..c1e0275 100644
|
|
--- a/drivers/gpu/drm/i810/i810_drv.c
|
|
+++ b/drivers/gpu/drm/i810/i810_drv.c
|
|
@@ -59,7 +59,7 @@ static struct drm_driver driver = {
|
|
.owner = THIS_MODULE,
|
|
.open = drm_open,
|
|
.release = drm_release,
|
|
- .ioctl = drm_ioctl,
|
|
+ .unlocked_ioctl = drm_ioctl,
|
|
.mmap = drm_mmap,
|
|
.poll = drm_poll,
|
|
.fasync = drm_fasync,
|
|
diff --git a/drivers/gpu/drm/i830/i830_dma.c b/drivers/gpu/drm/i830/i830_dma.c
|
|
index 877bf6c..06bd732 100644
|
|
--- a/drivers/gpu/drm/i830/i830_dma.c
|
|
+++ b/drivers/gpu/drm/i830/i830_dma.c
|
|
@@ -117,7 +117,7 @@ static int i830_mmap_buffers(struct file *filp, struct vm_area_struct *vma)
|
|
static const struct file_operations i830_buffer_fops = {
|
|
.open = drm_open,
|
|
.release = drm_release,
|
|
- .ioctl = drm_ioctl,
|
|
+ .unlocked_ioctl = drm_ioctl,
|
|
.mmap = i830_mmap_buffers,
|
|
.fasync = drm_fasync,
|
|
};
|
|
diff --git a/drivers/gpu/drm/i830/i830_drv.c b/drivers/gpu/drm/i830/i830_drv.c
|
|
index 389597e..44f990b 100644
|
|
--- a/drivers/gpu/drm/i830/i830_drv.c
|
|
+++ b/drivers/gpu/drm/i830/i830_drv.c
|
|
@@ -70,7 +70,7 @@ static struct drm_driver driver = {
|
|
.owner = THIS_MODULE,
|
|
.open = drm_open,
|
|
.release = drm_release,
|
|
- .ioctl = drm_ioctl,
|
|
+ .unlocked_ioctl = drm_ioctl,
|
|
.mmap = drm_mmap,
|
|
.poll = drm_poll,
|
|
.fasync = drm_fasync,
|
|
diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
|
|
index fa7b9be..9929f84 100644
|
|
--- a/drivers/gpu/drm/i915/Makefile
|
|
+++ b/drivers/gpu/drm/i915/Makefile
|
|
@@ -15,7 +15,6 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \
|
|
intel_lvds.o \
|
|
intel_bios.o \
|
|
intel_dp.o \
|
|
- intel_dp_i2c.o \
|
|
intel_hdmi.o \
|
|
intel_sdvo.o \
|
|
intel_modes.o \
|
|
@@ -23,6 +22,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \
|
|
intel_fb.o \
|
|
intel_tv.o \
|
|
intel_dvo.o \
|
|
+ intel_overlay.o \
|
|
dvo_ch7xxx.o \
|
|
dvo_ch7017.o \
|
|
dvo_ivch.o \
|
|
diff --git a/drivers/gpu/drm/i915/dvo_ch7017.c b/drivers/gpu/drm/i915/dvo_ch7017.c
|
|
index 621815b..1184c14 100644
|
|
--- a/drivers/gpu/drm/i915/dvo_ch7017.c
|
|
+++ b/drivers/gpu/drm/i915/dvo_ch7017.c
|
|
@@ -249,7 +249,8 @@ static bool ch7017_init(struct intel_dvo_device *dvo,
|
|
if (val != CH7017_DEVICE_ID_VALUE &&
|
|
val != CH7018_DEVICE_ID_VALUE &&
|
|
val != CH7019_DEVICE_ID_VALUE) {
|
|
- DRM_DEBUG("ch701x not detected, got %d: from %s Slave %d.\n",
|
|
+ DRM_DEBUG_KMS("ch701x not detected, got %d: from %s "
|
|
+ "Slave %d.\n",
|
|
val, i2cbus->adapter.name,dvo->slave_addr);
|
|
goto fail;
|
|
}
|
|
@@ -284,7 +285,7 @@ static void ch7017_mode_set(struct intel_dvo_device *dvo,
|
|
uint8_t horizontal_active_pixel_output, vertical_active_line_output;
|
|
uint8_t active_input_line_output;
|
|
|
|
- DRM_DEBUG("Registers before mode setting\n");
|
|
+ DRM_DEBUG_KMS("Registers before mode setting\n");
|
|
ch7017_dump_regs(dvo);
|
|
|
|
/* LVDS PLL settings from page 75 of 7017-7017ds.pdf*/
|
|
@@ -346,7 +347,7 @@ static void ch7017_mode_set(struct intel_dvo_device *dvo,
|
|
/* Turn the LVDS back on with new settings. */
|
|
ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, lvds_power_down);
|
|
|
|
- DRM_DEBUG("Registers after mode setting\n");
|
|
+ DRM_DEBUG_KMS("Registers after mode setting\n");
|
|
ch7017_dump_regs(dvo);
|
|
}
|
|
|
|
@@ -386,7 +387,7 @@ static void ch7017_dump_regs(struct intel_dvo_device *dvo)
|
|
#define DUMP(reg) \
|
|
do { \
|
|
ch7017_read(dvo, reg, &val); \
|
|
- DRM_DEBUG(#reg ": %02x\n", val); \
|
|
+ DRM_DEBUG_KMS(#reg ": %02x\n", val); \
|
|
} while (0)
|
|
|
|
DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT);
|
|
diff --git a/drivers/gpu/drm/i915/dvo_ch7xxx.c b/drivers/gpu/drm/i915/dvo_ch7xxx.c
|
|
index a9b8962..d56ff5c 100644
|
|
--- a/drivers/gpu/drm/i915/dvo_ch7xxx.c
|
|
+++ b/drivers/gpu/drm/i915/dvo_ch7xxx.c
|
|
@@ -152,7 +152,7 @@ static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
|
|
};
|
|
|
|
if (!ch7xxx->quiet) {
|
|
- DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
|
|
+ DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
|
|
addr, i2cbus->adapter.name, dvo->slave_addr);
|
|
}
|
|
return false;
|
|
@@ -179,7 +179,7 @@ static bool ch7xxx_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
|
|
return true;
|
|
|
|
if (!ch7xxx->quiet) {
|
|
- DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
|
|
+ DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
|
|
addr, i2cbus->adapter.name, dvo->slave_addr);
|
|
}
|
|
|
|
@@ -207,7 +207,8 @@ static bool ch7xxx_init(struct intel_dvo_device *dvo,
|
|
|
|
name = ch7xxx_get_id(vendor);
|
|
if (!name) {
|
|
- DRM_DEBUG("ch7xxx not detected; got 0x%02x from %s slave %d.\n",
|
|
+ DRM_DEBUG_KMS("ch7xxx not detected; got 0x%02x from %s "
|
|
+ "slave %d.\n",
|
|
vendor, adapter->name, dvo->slave_addr);
|
|
goto out;
|
|
}
|
|
@@ -217,13 +218,14 @@ static bool ch7xxx_init(struct intel_dvo_device *dvo,
|
|
goto out;
|
|
|
|
if (device != CH7xxx_DID) {
|
|
- DRM_DEBUG("ch7xxx not detected; got 0x%02x from %s slave %d.\n",
|
|
+ DRM_DEBUG_KMS("ch7xxx not detected; got 0x%02x from %s "
|
|
+ "slave %d.\n",
|
|
vendor, adapter->name, dvo->slave_addr);
|
|
goto out;
|
|
}
|
|
|
|
ch7xxx->quiet = false;
|
|
- DRM_DEBUG("Detected %s chipset, vendor/device ID 0x%02x/0x%02x\n",
|
|
+ DRM_DEBUG_KMS("Detected %s chipset, vendor/device ID 0x%02x/0x%02x\n",
|
|
name, vendor, device);
|
|
return true;
|
|
out:
|
|
@@ -315,8 +317,8 @@ static void ch7xxx_dump_regs(struct intel_dvo_device *dvo)
|
|
|
|
for (i = 0; i < CH7xxx_NUM_REGS; i++) {
|
|
if ((i % 8) == 0 )
|
|
- DRM_DEBUG("\n %02X: ", i);
|
|
- DRM_DEBUG("%02X ", ch7xxx->mode_reg.regs[i]);
|
|
+ DRM_LOG_KMS("\n %02X: ", i);
|
|
+ DRM_LOG_KMS("%02X ", ch7xxx->mode_reg.regs[i]);
|
|
}
|
|
}
|
|
|
|
diff --git a/drivers/gpu/drm/i915/dvo_ivch.c b/drivers/gpu/drm/i915/dvo_ivch.c
|
|
index aa176f9..24169e5 100644
|
|
--- a/drivers/gpu/drm/i915/dvo_ivch.c
|
|
+++ b/drivers/gpu/drm/i915/dvo_ivch.c
|
|
@@ -202,7 +202,8 @@ static bool ivch_read(struct intel_dvo_device *dvo, int addr, uint16_t *data)
|
|
};
|
|
|
|
if (!priv->quiet) {
|
|
- DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
|
|
+ DRM_DEBUG_KMS("Unable to read register 0x%02x from "
|
|
+ "%s:%02x.\n",
|
|
addr, i2cbus->adapter.name, dvo->slave_addr);
|
|
}
|
|
return false;
|
|
@@ -230,7 +231,7 @@ static bool ivch_write(struct intel_dvo_device *dvo, int addr, uint16_t data)
|
|
return true;
|
|
|
|
if (!priv->quiet) {
|
|
- DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
|
|
+ DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
|
|
addr, i2cbus->adapter.name, dvo->slave_addr);
|
|
}
|
|
|
|
@@ -261,7 +262,7 @@ static bool ivch_init(struct intel_dvo_device *dvo,
|
|
* the address it's responding on.
|
|
*/
|
|
if ((temp & VR00_BASE_ADDRESS_MASK) != dvo->slave_addr) {
|
|
- DRM_DEBUG("ivch detect failed due to address mismatch "
|
|
+ DRM_DEBUG_KMS("ivch detect failed due to address mismatch "
|
|
"(%d vs %d)\n",
|
|
(temp & VR00_BASE_ADDRESS_MASK), dvo->slave_addr);
|
|
goto out;
|
|
@@ -367,41 +368,41 @@ static void ivch_dump_regs(struct intel_dvo_device *dvo)
|
|
uint16_t val;
|
|
|
|
ivch_read(dvo, VR00, &val);
|
|
- DRM_DEBUG("VR00: 0x%04x\n", val);
|
|
+ DRM_LOG_KMS("VR00: 0x%04x\n", val);
|
|
ivch_read(dvo, VR01, &val);
|
|
- DRM_DEBUG("VR01: 0x%04x\n", val);
|
|
+ DRM_LOG_KMS("VR01: 0x%04x\n", val);
|
|
ivch_read(dvo, VR30, &val);
|
|
- DRM_DEBUG("VR30: 0x%04x\n", val);
|
|
+ DRM_LOG_KMS("VR30: 0x%04x\n", val);
|
|
ivch_read(dvo, VR40, &val);
|
|
- DRM_DEBUG("VR40: 0x%04x\n", val);
|
|
+ DRM_LOG_KMS("VR40: 0x%04x\n", val);
|
|
|
|
/* GPIO registers */
|
|
ivch_read(dvo, VR80, &val);
|
|
- DRM_DEBUG("VR80: 0x%04x\n", val);
|
|
+ DRM_LOG_KMS("VR80: 0x%04x\n", val);
|
|
ivch_read(dvo, VR81, &val);
|
|
- DRM_DEBUG("VR81: 0x%04x\n", val);
|
|
+ DRM_LOG_KMS("VR81: 0x%04x\n", val);
|
|
ivch_read(dvo, VR82, &val);
|
|
- DRM_DEBUG("VR82: 0x%04x\n", val);
|
|
+ DRM_LOG_KMS("VR82: 0x%04x\n", val);
|
|
ivch_read(dvo, VR83, &val);
|
|
- DRM_DEBUG("VR83: 0x%04x\n", val);
|
|
+ DRM_LOG_KMS("VR83: 0x%04x\n", val);
|
|
ivch_read(dvo, VR84, &val);
|
|
- DRM_DEBUG("VR84: 0x%04x\n", val);
|
|
+ DRM_LOG_KMS("VR84: 0x%04x\n", val);
|
|
ivch_read(dvo, VR85, &val);
|
|
- DRM_DEBUG("VR85: 0x%04x\n", val);
|
|
+ DRM_LOG_KMS("VR85: 0x%04x\n", val);
|
|
ivch_read(dvo, VR86, &val);
|
|
- DRM_DEBUG("VR86: 0x%04x\n", val);
|
|
+ DRM_LOG_KMS("VR86: 0x%04x\n", val);
|
|
ivch_read(dvo, VR87, &val);
|
|
- DRM_DEBUG("VR87: 0x%04x\n", val);
|
|
+ DRM_LOG_KMS("VR87: 0x%04x\n", val);
|
|
ivch_read(dvo, VR88, &val);
|
|
- DRM_DEBUG("VR88: 0x%04x\n", val);
|
|
+ DRM_LOG_KMS("VR88: 0x%04x\n", val);
|
|
|
|
/* Scratch register 0 - AIM Panel type */
|
|
ivch_read(dvo, VR8E, &val);
|
|
- DRM_DEBUG("VR8E: 0x%04x\n", val);
|
|
+ DRM_LOG_KMS("VR8E: 0x%04x\n", val);
|
|
|
|
/* Scratch register 1 - Status register */
|
|
ivch_read(dvo, VR8F, &val);
|
|
- DRM_DEBUG("VR8F: 0x%04x\n", val);
|
|
+ DRM_LOG_KMS("VR8F: 0x%04x\n", val);
|
|
}
|
|
|
|
static void ivch_save(struct intel_dvo_device *dvo)
|
|
diff --git a/drivers/gpu/drm/i915/dvo_sil164.c b/drivers/gpu/drm/i915/dvo_sil164.c
|
|
index e1c1f73..0001c13 100644
|
|
--- a/drivers/gpu/drm/i915/dvo_sil164.c
|
|
+++ b/drivers/gpu/drm/i915/dvo_sil164.c
|
|
@@ -105,7 +105,7 @@ static bool sil164_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
|
|
};
|
|
|
|
if (!sil->quiet) {
|
|
- DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
|
|
+ DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
|
|
addr, i2cbus->adapter.name, dvo->slave_addr);
|
|
}
|
|
return false;
|
|
@@ -131,7 +131,7 @@ static bool sil164_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
|
|
return true;
|
|
|
|
if (!sil->quiet) {
|
|
- DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
|
|
+ DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
|
|
addr, i2cbus->adapter.name, dvo->slave_addr);
|
|
}
|
|
|
|
@@ -158,7 +158,7 @@ static bool sil164_init(struct intel_dvo_device *dvo,
|
|
goto out;
|
|
|
|
if (ch != (SIL164_VID & 0xff)) {
|
|
- DRM_DEBUG("sil164 not detected got %d: from %s Slave %d.\n",
|
|
+ DRM_DEBUG_KMS("sil164 not detected got %d: from %s Slave %d.\n",
|
|
ch, adapter->name, dvo->slave_addr);
|
|
goto out;
|
|
}
|
|
@@ -167,13 +167,13 @@ static bool sil164_init(struct intel_dvo_device *dvo,
|
|
goto out;
|
|
|
|
if (ch != (SIL164_DID & 0xff)) {
|
|
- DRM_DEBUG("sil164 not detected got %d: from %s Slave %d.\n",
|
|
+ DRM_DEBUG_KMS("sil164 not detected got %d: from %s Slave %d.\n",
|
|
ch, adapter->name, dvo->slave_addr);
|
|
goto out;
|
|
}
|
|
sil->quiet = false;
|
|
|
|
- DRM_DEBUG("init sil164 dvo controller successfully!\n");
|
|
+ DRM_DEBUG_KMS("init sil164 dvo controller successfully!\n");
|
|
return true;
|
|
|
|
out:
|
|
@@ -241,15 +241,15 @@ static void sil164_dump_regs(struct intel_dvo_device *dvo)
|
|
uint8_t val;
|
|
|
|
sil164_readb(dvo, SIL164_FREQ_LO, &val);
|
|
- DRM_DEBUG("SIL164_FREQ_LO: 0x%02x\n", val);
|
|
+ DRM_LOG_KMS("SIL164_FREQ_LO: 0x%02x\n", val);
|
|
sil164_readb(dvo, SIL164_FREQ_HI, &val);
|
|
- DRM_DEBUG("SIL164_FREQ_HI: 0x%02x\n", val);
|
|
+ DRM_LOG_KMS("SIL164_FREQ_HI: 0x%02x\n", val);
|
|
sil164_readb(dvo, SIL164_REG8, &val);
|
|
- DRM_DEBUG("SIL164_REG8: 0x%02x\n", val);
|
|
+ DRM_LOG_KMS("SIL164_REG8: 0x%02x\n", val);
|
|
sil164_readb(dvo, SIL164_REG9, &val);
|
|
- DRM_DEBUG("SIL164_REG9: 0x%02x\n", val);
|
|
+ DRM_LOG_KMS("SIL164_REG9: 0x%02x\n", val);
|
|
sil164_readb(dvo, SIL164_REGC, &val);
|
|
- DRM_DEBUG("SIL164_REGC: 0x%02x\n", val);
|
|
+ DRM_LOG_KMS("SIL164_REGC: 0x%02x\n", val);
|
|
}
|
|
|
|
static void sil164_save(struct intel_dvo_device *dvo)
|
|
diff --git a/drivers/gpu/drm/i915/dvo_tfp410.c b/drivers/gpu/drm/i915/dvo_tfp410.c
|
|
index 9ecc907..c7c391b 100644
|
|
--- a/drivers/gpu/drm/i915/dvo_tfp410.c
|
|
+++ b/drivers/gpu/drm/i915/dvo_tfp410.c
|
|
@@ -130,7 +130,7 @@ static bool tfp410_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
|
|
};
|
|
|
|
if (!tfp->quiet) {
|
|
- DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
|
|
+ DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
|
|
addr, i2cbus->adapter.name, dvo->slave_addr);
|
|
}
|
|
return false;
|
|
@@ -156,7 +156,7 @@ static bool tfp410_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
|
|
return true;
|
|
|
|
if (!tfp->quiet) {
|
|
- DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
|
|
+ DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
|
|
addr, i2cbus->adapter.name, dvo->slave_addr);
|
|
}
|
|
|
|
@@ -191,13 +191,15 @@ static bool tfp410_init(struct intel_dvo_device *dvo,
|
|
tfp->quiet = true;
|
|
|
|
if ((id = tfp410_getid(dvo, TFP410_VID_LO)) != TFP410_VID) {
|
|
- DRM_DEBUG("tfp410 not detected got VID %X: from %s Slave %d.\n",
|
|
+ DRM_DEBUG_KMS("tfp410 not detected got VID %X: from %s "
|
|
+ "Slave %d.\n",
|
|
id, adapter->name, dvo->slave_addr);
|
|
goto out;
|
|
}
|
|
|
|
if ((id = tfp410_getid(dvo, TFP410_DID_LO)) != TFP410_DID) {
|
|
- DRM_DEBUG("tfp410 not detected got DID %X: from %s Slave %d.\n",
|
|
+ DRM_DEBUG_KMS("tfp410 not detected got DID %X: from %s "
|
|
+ "Slave %d.\n",
|
|
id, adapter->name, dvo->slave_addr);
|
|
goto out;
|
|
}
|
|
@@ -262,33 +264,33 @@ static void tfp410_dump_regs(struct intel_dvo_device *dvo)
|
|
uint8_t val, val2;
|
|
|
|
tfp410_readb(dvo, TFP410_REV, &val);
|
|
- DRM_DEBUG("TFP410_REV: 0x%02X\n", val);
|
|
+ DRM_LOG_KMS("TFP410_REV: 0x%02X\n", val);
|
|
tfp410_readb(dvo, TFP410_CTL_1, &val);
|
|
- DRM_DEBUG("TFP410_CTL1: 0x%02X\n", val);
|
|
+ DRM_LOG_KMS("TFP410_CTL1: 0x%02X\n", val);
|
|
tfp410_readb(dvo, TFP410_CTL_2, &val);
|
|
- DRM_DEBUG("TFP410_CTL2: 0x%02X\n", val);
|
|
+ DRM_LOG_KMS("TFP410_CTL2: 0x%02X\n", val);
|
|
tfp410_readb(dvo, TFP410_CTL_3, &val);
|
|
- DRM_DEBUG("TFP410_CTL3: 0x%02X\n", val);
|
|
+ DRM_LOG_KMS("TFP410_CTL3: 0x%02X\n", val);
|
|
tfp410_readb(dvo, TFP410_USERCFG, &val);
|
|
- DRM_DEBUG("TFP410_USERCFG: 0x%02X\n", val);
|
|
+ DRM_LOG_KMS("TFP410_USERCFG: 0x%02X\n", val);
|
|
tfp410_readb(dvo, TFP410_DE_DLY, &val);
|
|
- DRM_DEBUG("TFP410_DE_DLY: 0x%02X\n", val);
|
|
+ DRM_LOG_KMS("TFP410_DE_DLY: 0x%02X\n", val);
|
|
tfp410_readb(dvo, TFP410_DE_CTL, &val);
|
|
- DRM_DEBUG("TFP410_DE_CTL: 0x%02X\n", val);
|
|
+ DRM_LOG_KMS("TFP410_DE_CTL: 0x%02X\n", val);
|
|
tfp410_readb(dvo, TFP410_DE_TOP, &val);
|
|
- DRM_DEBUG("TFP410_DE_TOP: 0x%02X\n", val);
|
|
+ DRM_LOG_KMS("TFP410_DE_TOP: 0x%02X\n", val);
|
|
tfp410_readb(dvo, TFP410_DE_CNT_LO, &val);
|
|
tfp410_readb(dvo, TFP410_DE_CNT_HI, &val2);
|
|
- DRM_DEBUG("TFP410_DE_CNT: 0x%02X%02X\n", val2, val);
|
|
+ DRM_LOG_KMS("TFP410_DE_CNT: 0x%02X%02X\n", val2, val);
|
|
tfp410_readb(dvo, TFP410_DE_LIN_LO, &val);
|
|
tfp410_readb(dvo, TFP410_DE_LIN_HI, &val2);
|
|
- DRM_DEBUG("TFP410_DE_LIN: 0x%02X%02X\n", val2, val);
|
|
+ DRM_LOG_KMS("TFP410_DE_LIN: 0x%02X%02X\n", val2, val);
|
|
tfp410_readb(dvo, TFP410_H_RES_LO, &val);
|
|
tfp410_readb(dvo, TFP410_H_RES_HI, &val2);
|
|
- DRM_DEBUG("TFP410_H_RES: 0x%02X%02X\n", val2, val);
|
|
+ DRM_LOG_KMS("TFP410_H_RES: 0x%02X%02X\n", val2, val);
|
|
tfp410_readb(dvo, TFP410_V_RES_LO, &val);
|
|
tfp410_readb(dvo, TFP410_V_RES_HI, &val2);
|
|
- DRM_DEBUG("TFP410_V_RES: 0x%02X%02X\n", val2, val);
|
|
+ DRM_LOG_KMS("TFP410_V_RES: 0x%02X%02X\n", val2, val);
|
|
}
|
|
|
|
static void tfp410_save(struct intel_dvo_device *dvo)
|
|
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
|
|
index af655e8..a894ade 100644
|
|
--- a/drivers/gpu/drm/i915/i915_debugfs.c
|
|
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
|
|
@@ -27,6 +27,7 @@
|
|
*/
|
|
|
|
#include <linux/seq_file.h>
|
|
+#include <linux/debugfs.h>
|
|
#include "drmP.h"
|
|
#include "drm.h"
|
|
#include "i915_drm.h"
|
|
@@ -96,13 +97,14 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data)
|
|
{
|
|
struct drm_gem_object *obj = obj_priv->obj;
|
|
|
|
- seq_printf(m, " %p: %s %8zd %08x %08x %d %s",
|
|
+ seq_printf(m, " %p: %s %8zd %08x %08x %d%s%s",
|
|
obj,
|
|
get_pin_flag(obj_priv),
|
|
obj->size,
|
|
obj->read_domains, obj->write_domain,
|
|
obj_priv->last_rendering_seqno,
|
|
- obj_priv->dirty ? "dirty" : "");
|
|
+ obj_priv->dirty ? " dirty" : "",
|
|
+ obj_priv->madv == I915_MADV_DONTNEED ? " purgeable" : "");
|
|
|
|
if (obj->name)
|
|
seq_printf(m, " (name: %d)", obj->name);
|
|
@@ -160,7 +162,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
|
|
struct drm_device *dev = node->minor->dev;
|
|
drm_i915_private_t *dev_priv = dev->dev_private;
|
|
|
|
- if (!IS_IGDNG(dev)) {
|
|
+ if (!IS_IRONLAKE(dev)) {
|
|
seq_printf(m, "Interrupt enable: %08x\n",
|
|
I915_READ(IER));
|
|
seq_printf(m, "Interrupt identity: %08x\n",
|
|
@@ -270,7 +272,7 @@ static void i915_dump_pages(struct seq_file *m, struct page **pages, int page_co
|
|
mem = kmap_atomic(pages[page], KM_USER0);
|
|
for (i = 0; i < PAGE_SIZE; i += 4)
|
|
seq_printf(m, "%08x : %08x\n", i, mem[i / 4]);
|
|
- kunmap_atomic(pages[page], KM_USER0);
|
|
+ kunmap_atomic(mem, KM_USER0);
|
|
}
|
|
}
|
|
|
|
@@ -384,37 +386,111 @@ out:
|
|
return 0;
|
|
}
|
|
|
|
-static int i915_registers_info(struct seq_file *m, void *data) {
|
|
- struct drm_info_node *node = (struct drm_info_node *) m->private;
|
|
- struct drm_device *dev = node->minor->dev;
|
|
+static int
|
|
+i915_wedged_open(struct inode *inode,
|
|
+ struct file *filp)
|
|
+{
|
|
+ filp->private_data = inode->i_private;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static ssize_t
|
|
+i915_wedged_read(struct file *filp,
|
|
+ char __user *ubuf,
|
|
+ size_t max,
|
|
+ loff_t *ppos)
|
|
+{
|
|
+ struct drm_device *dev = filp->private_data;
|
|
drm_i915_private_t *dev_priv = dev->dev_private;
|
|
- uint32_t reg;
|
|
-
|
|
-#define DUMP_RANGE(start, end) \
|
|
- for (reg=start; reg < end; reg += 4) \
|
|
- seq_printf(m, "%08x\t%08x\n", reg, I915_READ(reg));
|
|
-
|
|
- DUMP_RANGE(0x00000, 0x00fff); /* VGA registers */
|
|
- DUMP_RANGE(0x02000, 0x02fff); /* instruction, memory, interrupt control registers */
|
|
- DUMP_RANGE(0x03000, 0x031ff); /* FENCE and PPGTT control registers */
|
|
- DUMP_RANGE(0x03200, 0x03fff); /* frame buffer compression registers */
|
|
- DUMP_RANGE(0x05000, 0x05fff); /* I/O control registers */
|
|
- DUMP_RANGE(0x06000, 0x06fff); /* clock control registers */
|
|
- DUMP_RANGE(0x07000, 0x07fff); /* 3D internal debug registers */
|
|
- DUMP_RANGE(0x07400, 0x088ff); /* GPE debug registers */
|
|
- DUMP_RANGE(0x0a000, 0x0afff); /* display palette registers */
|
|
- DUMP_RANGE(0x10000, 0x13fff); /* MMIO MCHBAR */
|
|
- DUMP_RANGE(0x30000, 0x3ffff); /* overlay registers */
|
|
- DUMP_RANGE(0x60000, 0x6ffff); /* display engine pipeline registers */
|
|
- DUMP_RANGE(0x70000, 0x72fff); /* display and cursor registers */
|
|
- DUMP_RANGE(0x73000, 0x73fff); /* performance counters */
|
|
+ char buf[80];
|
|
+ int len;
|
|
+
|
|
+ len = snprintf(buf, sizeof (buf),
|
|
+ "wedged : %d\n",
|
|
+ atomic_read(&dev_priv->mm.wedged));
|
|
+
|
|
+ return simple_read_from_buffer(ubuf, max, ppos, buf, len);
|
|
+}
|
|
+
|
|
+static ssize_t
|
|
+i915_wedged_write(struct file *filp,
|
|
+ const char __user *ubuf,
|
|
+ size_t cnt,
|
|
+ loff_t *ppos)
|
|
+{
|
|
+ struct drm_device *dev = filp->private_data;
|
|
+ drm_i915_private_t *dev_priv = dev->dev_private;
|
|
+ char buf[20];
|
|
+ int val = 1;
|
|
+
|
|
+ if (cnt > 0) {
|
|
+ if (cnt > sizeof (buf) - 1)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (copy_from_user(buf, ubuf, cnt))
|
|
+ return -EFAULT;
|
|
+ buf[cnt] = 0;
|
|
+
|
|
+ val = simple_strtoul(buf, NULL, 0);
|
|
+ }
|
|
+
|
|
+ DRM_INFO("Manually setting wedged to %d\n", val);
|
|
+
|
|
+ atomic_set(&dev_priv->mm.wedged, val);
|
|
+ if (val) {
|
|
+ DRM_WAKEUP(&dev_priv->irq_queue);
|
|
+ queue_work(dev_priv->wq, &dev_priv->error_work);
|
|
+ }
|
|
+
|
|
+ return cnt;
|
|
+}
|
|
+
|
|
+static const struct file_operations i915_wedged_fops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .open = i915_wedged_open,
|
|
+ .read = i915_wedged_read,
|
|
+ .write = i915_wedged_write,
|
|
+};
|
|
+
|
|
+/* As the drm_debugfs_init() routines are called before dev->dev_private is
|
|
+ * allocated we need to hook into the minor for release. */
|
|
+static int
|
|
+drm_add_fake_info_node(struct drm_minor *minor,
|
|
+ struct dentry *ent,
|
|
+ const void *key)
|
|
+{
|
|
+ struct drm_info_node *node;
|
|
+
|
|
+ node = kmalloc(sizeof(struct drm_info_node), GFP_KERNEL);
|
|
+ if (node == NULL) {
|
|
+ debugfs_remove(ent);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ node->minor = minor;
|
|
+ node->dent = ent;
|
|
+ node->info_ent = (void *) key;
|
|
+ list_add(&node->list, &minor->debugfs_nodes.list);
|
|
|
|
return 0;
|
|
}
|
|
|
|
+static int i915_wedged_create(struct dentry *root, struct drm_minor *minor)
|
|
+{
|
|
+ struct drm_device *dev = minor->dev;
|
|
+ struct dentry *ent;
|
|
+
|
|
+ ent = debugfs_create_file("i915_wedged",
|
|
+ S_IRUGO | S_IWUSR,
|
|
+ root, dev,
|
|
+ &i915_wedged_fops);
|
|
+ if (IS_ERR(ent))
|
|
+ return PTR_ERR(ent);
|
|
+
|
|
+ return drm_add_fake_info_node(minor, ent, &i915_wedged_fops);
|
|
+}
|
|
|
|
static struct drm_info_list i915_debugfs_list[] = {
|
|
- {"i915_regs", i915_registers_info, 0},
|
|
{"i915_gem_active", i915_gem_object_list_info, 0, (void *) ACTIVE_LIST},
|
|
{"i915_gem_flushing", i915_gem_object_list_info, 0, (void *) FLUSHING_LIST},
|
|
{"i915_gem_inactive", i915_gem_object_list_info, 0, (void *) INACTIVE_LIST},
|
|
@@ -432,6 +508,12 @@ static struct drm_info_list i915_debugfs_list[] = {
|
|
|
|
int i915_debugfs_init(struct drm_minor *minor)
|
|
{
|
|
+ int ret;
|
|
+
|
|
+ ret = i915_wedged_create(minor->debugfs_root, minor);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
return drm_debugfs_create_files(i915_debugfs_list,
|
|
I915_DEBUGFS_ENTRIES,
|
|
minor->debugfs_root, minor);
|
|
@@ -441,7 +523,8 @@ void i915_debugfs_cleanup(struct drm_minor *minor)
|
|
{
|
|
drm_debugfs_remove_files(i915_debugfs_list,
|
|
I915_DEBUGFS_ENTRIES, minor);
|
|
+ drm_debugfs_remove_files((struct drm_info_list *) &i915_wedged_fops,
|
|
+ 1, minor);
|
|
}
|
|
|
|
#endif /* CONFIG_DEBUG_FS */
|
|
-
|
|
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
|
|
index eaa1893..2307f98 100644
|
|
--- a/drivers/gpu/drm/i915/i915_dma.c
|
|
+++ b/drivers/gpu/drm/i915/i915_dma.c
|
|
@@ -134,6 +134,10 @@ static int i915_init_phys_hws(struct drm_device *dev)
|
|
|
|
memset(dev_priv->hw_status_page, 0, PAGE_SIZE);
|
|
|
|
+ if (IS_I965G(dev))
|
|
+ dev_priv->dma_status_page |= (dev_priv->dma_status_page >> 28) &
|
|
+ 0xf0;
|
|
+
|
|
I915_WRITE(HWS_PGA, dev_priv->dma_status_page);
|
|
DRM_DEBUG_DRIVER("Enabled hardware status page\n");
|
|
return 0;
|
|
@@ -731,8 +735,10 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data,
|
|
if (cmdbuf->num_cliprects) {
|
|
cliprects = kcalloc(cmdbuf->num_cliprects,
|
|
sizeof(struct drm_clip_rect), GFP_KERNEL);
|
|
- if (cliprects == NULL)
|
|
+ if (cliprects == NULL) {
|
|
+ ret = -ENOMEM;
|
|
goto fail_batch_free;
|
|
+ }
|
|
|
|
ret = copy_from_user(cliprects, cmdbuf->cliprects,
|
|
cmdbuf->num_cliprects *
|
|
@@ -807,9 +813,19 @@ static int i915_getparam(struct drm_device *dev, void *data,
|
|
case I915_PARAM_NUM_FENCES_AVAIL:
|
|
value = dev_priv->num_fence_regs - dev_priv->fence_reg_start;
|
|
break;
|
|
+ case I915_PARAM_HAS_OVERLAY:
|
|
+ value = dev_priv->overlay ? 1 : 0;
|
|
+ break;
|
|
+ case I915_PARAM_HAS_PAGEFLIPPING:
|
|
+ value = 1;
|
|
+ break;
|
|
+ case I915_PARAM_HAS_EXECBUF2:
|
|
+ /* depends on GEM */
|
|
+ value = dev_priv->has_gem;
|
|
+ break;
|
|
default:
|
|
DRM_DEBUG_DRIVER("Unknown parameter %d\n",
|
|
- param->param);
|
|
+ param->param);
|
|
return -EINVAL;
|
|
}
|
|
|
|
@@ -962,7 +978,7 @@ static int i915_probe_agp(struct drm_device *dev, uint32_t *aperture_size,
|
|
* Some of the preallocated space is taken by the GTT
|
|
* and popup. GTT is 1K per MB of aperture size, and popup is 4K.
|
|
*/
|
|
- if (IS_G4X(dev) || IS_IGD(dev) || IS_IGDNG(dev))
|
|
+ if (IS_G4X(dev) || IS_PINEVIEW(dev) || IS_IRONLAKE(dev))
|
|
overhead = 4096;
|
|
else
|
|
overhead = (*aperture_size / 1024) + 4096;
|
|
@@ -1048,7 +1064,7 @@ static unsigned long i915_gtt_to_phys(struct drm_device *dev,
|
|
int gtt_offset, gtt_size;
|
|
|
|
if (IS_I965G(dev)) {
|
|
- if (IS_G4X(dev) || IS_IGDNG(dev)) {
|
|
+ if (IS_G4X(dev) || IS_IRONLAKE(dev)) {
|
|
gtt_offset = 2*1024*1024;
|
|
gtt_size = 2*1024*1024;
|
|
} else {
|
|
@@ -1070,7 +1086,7 @@ static unsigned long i915_gtt_to_phys(struct drm_device *dev,
|
|
|
|
entry = *(volatile u32 *)(gtt + (gtt_addr / 1024));
|
|
|
|
- DRM_DEBUG("GTT addr: 0x%08lx, PTE: 0x%08lx\n", gtt_addr, entry);
|
|
+ DRM_DEBUG_DRIVER("GTT addr: 0x%08lx, PTE: 0x%08lx\n", gtt_addr, entry);
|
|
|
|
/* Mask out these reserved bits on this hardware. */
|
|
if (!IS_I9XX(dev) || IS_I915G(dev) || IS_I915GM(dev) ||
|
|
@@ -1096,7 +1112,7 @@ static unsigned long i915_gtt_to_phys(struct drm_device *dev,
|
|
phys =(entry & PTE_ADDRESS_MASK) |
|
|
((uint64_t)(entry & PTE_ADDRESS_MASK_HIGH) << (32 - 4));
|
|
|
|
- DRM_DEBUG("GTT addr: 0x%08lx, phys addr: 0x%08lx\n", gtt_addr, phys);
|
|
+ DRM_DEBUG_DRIVER("GTT addr: 0x%08lx, phys addr: 0x%08lx\n", gtt_addr, phys);
|
|
|
|
return phys;
|
|
}
|
|
@@ -1195,14 +1211,6 @@ static int i915_load_modeset_init(struct drm_device *dev,
|
|
dev->mode_config.fb_base = drm_get_resource_start(dev, fb_bar) &
|
|
0xff000000;
|
|
|
|
- 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;
|
|
-
|
|
/* Basic memrange allocator for stolen space (aka vram) */
|
|
drm_mm_init(&dev_priv->vram, 0, prealloc_size);
|
|
DRM_INFO("set up %ldM of stolen space\n", prealloc_size / (1024*1024));
|
|
@@ -1307,7 +1315,7 @@ static void i915_get_mem_freq(struct drm_device *dev)
|
|
drm_i915_private_t *dev_priv = dev->dev_private;
|
|
u32 tmp;
|
|
|
|
- if (!IS_IGD(dev))
|
|
+ if (!IS_PINEVIEW(dev))
|
|
return;
|
|
|
|
tmp = I915_READ(CLKCFG);
|
|
@@ -1355,7 +1363,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
resource_size_t base, size;
|
|
- int ret = 0, mmio_bar = IS_I9XX(dev) ? 0 : 1;
|
|
+ int ret = 0, mmio_bar;
|
|
uint32_t agp_size, prealloc_size, prealloc_start;
|
|
|
|
/* i915 has 4 more counters */
|
|
@@ -1371,8 +1379,10 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
|
|
|
dev->dev_private = (void *)dev_priv;
|
|
dev_priv->dev = dev;
|
|
+ dev_priv->info = (struct intel_device_info *) flags;
|
|
|
|
/* Add register map (needed for suspend/resume) */
|
|
+ mmio_bar = IS_I9XX(dev) ? 0 : 1;
|
|
base = drm_get_resource_start(dev, mmio_bar);
|
|
size = drm_get_resource_len(dev, mmio_bar);
|
|
|
|
@@ -1414,7 +1424,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
|
if (ret)
|
|
goto out_iomapfree;
|
|
|
|
- dev_priv->wq = create_workqueue("i915");
|
|
+ dev_priv->wq = create_singlethread_workqueue("i915");
|
|
if (dev_priv->wq == NULL) {
|
|
DRM_ERROR("Failed to create our workqueue.\n");
|
|
ret = -ENOMEM;
|
|
@@ -1435,7 +1445,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
|
|
|
dev->driver->get_vblank_counter = i915_get_vblank_counter;
|
|
dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
|
|
- if (IS_G4X(dev) || IS_IGDNG(dev)) {
|
|
+ if (IS_G4X(dev) || IS_IRONLAKE(dev)) {
|
|
dev->max_vblank_count = 0xffffffff; /* full 32 bit counter */
|
|
dev->driver->get_vblank_counter = gm45_get_vblank_counter;
|
|
}
|
|
@@ -1490,9 +1500,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
|
}
|
|
|
|
/* Must be done after probing outputs */
|
|
- /* FIXME: verify on IGDNG */
|
|
- if (!IS_IGDNG(dev))
|
|
- intel_opregion_init(dev, 0);
|
|
+ intel_opregion_init(dev, 0);
|
|
|
|
setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed,
|
|
(unsigned long) dev);
|
|
@@ -1526,6 +1534,15 @@ int i915_driver_unload(struct drm_device *dev)
|
|
}
|
|
|
|
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
|
+ /*
|
|
+ * free the memory space allocated for the child device
|
|
+ * config parsed from VBT
|
|
+ */
|
|
+ if (dev_priv->child_dev && dev_priv->child_dev_num) {
|
|
+ kfree(dev_priv->child_dev);
|
|
+ dev_priv->child_dev = NULL;
|
|
+ dev_priv->child_dev_num = 0;
|
|
+ }
|
|
drm_irq_uninstall(dev);
|
|
vga_client_register(dev->pdev, NULL, NULL, NULL);
|
|
}
|
|
@@ -1536,8 +1553,7 @@ int i915_driver_unload(struct drm_device *dev)
|
|
if (dev_priv->regs != NULL)
|
|
iounmap(dev_priv->regs);
|
|
|
|
- if (!IS_IGDNG(dev))
|
|
- intel_opregion_free(dev, 0);
|
|
+ intel_opregion_free(dev, 0);
|
|
|
|
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
|
intel_modeset_cleanup(dev);
|
|
@@ -1549,6 +1565,8 @@ int i915_driver_unload(struct drm_device *dev)
|
|
mutex_unlock(&dev->struct_mutex);
|
|
drm_mm_takedown(&dev_priv->vram);
|
|
i915_gem_lastclose(dev);
|
|
+
|
|
+ intel_cleanup_overlay(dev);
|
|
}
|
|
|
|
pci_dev_put(dev_priv->bridge_dev);
|
|
@@ -1639,6 +1657,7 @@ struct drm_ioctl_desc i915_ioctls[] = {
|
|
DRM_IOCTL_DEF(DRM_I915_HWS_ADDR, i915_set_status_page, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
|
|
DRM_IOCTL_DEF(DRM_I915_GEM_INIT, i915_gem_init_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
|
|
DRM_IOCTL_DEF(DRM_I915_GEM_EXECBUFFER, i915_gem_execbuffer, DRM_AUTH),
|
|
+ DRM_IOCTL_DEF(DRM_I915_GEM_EXECBUFFER2, i915_gem_execbuffer2, DRM_AUTH),
|
|
DRM_IOCTL_DEF(DRM_I915_GEM_PIN, i915_gem_pin_ioctl, DRM_AUTH|DRM_ROOT_ONLY),
|
|
DRM_IOCTL_DEF(DRM_I915_GEM_UNPIN, i915_gem_unpin_ioctl, DRM_AUTH|DRM_ROOT_ONLY),
|
|
DRM_IOCTL_DEF(DRM_I915_GEM_BUSY, i915_gem_busy_ioctl, DRM_AUTH),
|
|
@@ -1657,6 +1676,8 @@ struct drm_ioctl_desc i915_ioctls[] = {
|
|
DRM_IOCTL_DEF(DRM_I915_GEM_GET_APERTURE, i915_gem_get_aperture_ioctl, 0),
|
|
DRM_IOCTL_DEF(DRM_I915_GET_PIPE_FROM_CRTC_ID, intel_get_pipe_from_crtc_id, 0),
|
|
DRM_IOCTL_DEF(DRM_I915_GEM_MADVISE, i915_gem_madvise_ioctl, 0),
|
|
+ DRM_IOCTL_DEF(DRM_I915_OVERLAY_PUT_IMAGE, intel_overlay_put_image, DRM_MASTER|DRM_CONTROL_ALLOW),
|
|
+ DRM_IOCTL_DEF(DRM_I915_OVERLAY_ATTRS, intel_overlay_attrs, DRM_MASTER|DRM_CONTROL_ALLOW),
|
|
};
|
|
|
|
int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls);
|
|
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
|
|
index 7f436ec..79beffc 100644
|
|
--- a/drivers/gpu/drm/i915/i915_drv.c
|
|
+++ b/drivers/gpu/drm/i915/i915_drv.c
|
|
@@ -33,7 +33,6 @@
|
|
#include "i915_drm.h"
|
|
#include "i915_drv.h"
|
|
|
|
-#include "drm_pciids.h"
|
|
#include <linux/console.h>
|
|
#include "drm_crtc_helper.h"
|
|
|
|
@@ -46,88 +45,229 @@ module_param_named(fbpercrtc, i915_fbpercrtc, int, 0400);
|
|
unsigned int i915_powersave = 1;
|
|
module_param_named(powersave, i915_powersave, int, 0400);
|
|
|
|
+unsigned int i915_lvds_downclock = 0;
|
|
+module_param_named(lvds_downclock, i915_lvds_downclock, int, 0400);
|
|
+
|
|
static struct drm_driver driver;
|
|
|
|
-static struct pci_device_id pciidlist[] = {
|
|
- i915_PCI_IDS
|
|
+#define INTEL_VGA_DEVICE(id, info) { \
|
|
+ .class = PCI_CLASS_DISPLAY_VGA << 8, \
|
|
+ .class_mask = 0xffff00, \
|
|
+ .vendor = 0x8086, \
|
|
+ .device = id, \
|
|
+ .subvendor = PCI_ANY_ID, \
|
|
+ .subdevice = PCI_ANY_ID, \
|
|
+ .driver_data = (unsigned long) info }
|
|
+
|
|
+const static struct intel_device_info intel_i830_info = {
|
|
+ .is_i8xx = 1, .is_mobile = 1, .cursor_needs_physical = 1,
|
|
+};
|
|
+
|
|
+const static struct intel_device_info intel_845g_info = {
|
|
+ .is_i8xx = 1,
|
|
+};
|
|
+
|
|
+const static struct intel_device_info intel_i85x_info = {
|
|
+ .is_i8xx = 1, .is_mobile = 1, .cursor_needs_physical = 1,
|
|
+};
|
|
+
|
|
+const static struct intel_device_info intel_i865g_info = {
|
|
+ .is_i8xx = 1,
|
|
+};
|
|
+
|
|
+const static struct intel_device_info intel_i915g_info = {
|
|
+ .is_i915g = 1, .is_i9xx = 1, .cursor_needs_physical = 1,
|
|
+};
|
|
+const static struct intel_device_info intel_i915gm_info = {
|
|
+ .is_i9xx = 1, .is_mobile = 1, .has_fbc = 1,
|
|
+ .cursor_needs_physical = 1,
|
|
+};
|
|
+const static struct intel_device_info intel_i945g_info = {
|
|
+ .is_i9xx = 1, .has_hotplug = 1, .cursor_needs_physical = 1,
|
|
+};
|
|
+const static struct intel_device_info intel_i945gm_info = {
|
|
+ .is_i945gm = 1, .is_i9xx = 1, .is_mobile = 1, .has_fbc = 1,
|
|
+ .has_hotplug = 1, .cursor_needs_physical = 1,
|
|
+};
|
|
+
|
|
+const static struct intel_device_info intel_i965g_info = {
|
|
+ .is_i965g = 1, .is_i9xx = 1, .has_hotplug = 1,
|
|
+};
|
|
+
|
|
+const static struct intel_device_info intel_i965gm_info = {
|
|
+ .is_i965g = 1, .is_mobile = 1, .is_i965gm = 1, .is_i9xx = 1,
|
|
+ .is_mobile = 1, .has_fbc = 1, .has_rc6 = 1,
|
|
+ .has_hotplug = 1,
|
|
+};
|
|
+
|
|
+const static struct intel_device_info intel_g33_info = {
|
|
+ .is_g33 = 1, .is_i9xx = 1, .need_gfx_hws = 1,
|
|
+ .has_hotplug = 1,
|
|
+};
|
|
+
|
|
+const static struct intel_device_info intel_g45_info = {
|
|
+ .is_i965g = 1, .is_g4x = 1, .is_i9xx = 1, .need_gfx_hws = 1,
|
|
+ .has_pipe_cxsr = 1,
|
|
+ .has_hotplug = 1,
|
|
+};
|
|
+
|
|
+const static struct intel_device_info intel_gm45_info = {
|
|
+ .is_i965g = 1, .is_mobile = 1, .is_g4x = 1, .is_i9xx = 1,
|
|
+ .is_mobile = 1, .need_gfx_hws = 1, .has_fbc = 1, .has_rc6 = 1,
|
|
+ .has_pipe_cxsr = 1,
|
|
+ .has_hotplug = 1,
|
|
+};
|
|
+
|
|
+const static struct intel_device_info intel_pineview_info = {
|
|
+ .is_g33 = 1, .is_pineview = 1, .is_mobile = 1, .is_i9xx = 1,
|
|
+ .need_gfx_hws = 1,
|
|
+ .has_hotplug = 1,
|
|
+};
|
|
+
|
|
+const static struct intel_device_info intel_ironlake_d_info = {
|
|
+ .is_ironlake = 1, .is_i965g = 1, .is_i9xx = 1, .need_gfx_hws = 1,
|
|
+ .has_pipe_cxsr = 1,
|
|
+ .has_hotplug = 1,
|
|
+};
|
|
+
|
|
+const static struct intel_device_info intel_ironlake_m_info = {
|
|
+ .is_ironlake = 1, .is_mobile = 1, .is_i965g = 1, .is_i9xx = 1,
|
|
+ .need_gfx_hws = 1, .has_rc6 = 1,
|
|
+ .has_hotplug = 1,
|
|
+};
|
|
+
|
|
+const static struct pci_device_id pciidlist[] = {
|
|
+ INTEL_VGA_DEVICE(0x3577, &intel_i830_info),
|
|
+ INTEL_VGA_DEVICE(0x2562, &intel_845g_info),
|
|
+ INTEL_VGA_DEVICE(0x3582, &intel_i85x_info),
|
|
+ INTEL_VGA_DEVICE(0x35e8, &intel_i85x_info),
|
|
+ INTEL_VGA_DEVICE(0x2572, &intel_i865g_info),
|
|
+ INTEL_VGA_DEVICE(0x2582, &intel_i915g_info),
|
|
+ INTEL_VGA_DEVICE(0x258a, &intel_i915g_info),
|
|
+ INTEL_VGA_DEVICE(0x2592, &intel_i915gm_info),
|
|
+ INTEL_VGA_DEVICE(0x2772, &intel_i945g_info),
|
|
+ INTEL_VGA_DEVICE(0x27a2, &intel_i945gm_info),
|
|
+ INTEL_VGA_DEVICE(0x27ae, &intel_i945gm_info),
|
|
+ INTEL_VGA_DEVICE(0x2972, &intel_i965g_info),
|
|
+ INTEL_VGA_DEVICE(0x2982, &intel_i965g_info),
|
|
+ INTEL_VGA_DEVICE(0x2992, &intel_i965g_info),
|
|
+ INTEL_VGA_DEVICE(0x29a2, &intel_i965g_info),
|
|
+ INTEL_VGA_DEVICE(0x29b2, &intel_g33_info),
|
|
+ INTEL_VGA_DEVICE(0x29c2, &intel_g33_info),
|
|
+ INTEL_VGA_DEVICE(0x29d2, &intel_g33_info),
|
|
+ INTEL_VGA_DEVICE(0x2a02, &intel_i965gm_info),
|
|
+ INTEL_VGA_DEVICE(0x2a12, &intel_i965gm_info),
|
|
+ INTEL_VGA_DEVICE(0x2a42, &intel_gm45_info),
|
|
+ INTEL_VGA_DEVICE(0x2e02, &intel_g45_info),
|
|
+ INTEL_VGA_DEVICE(0x2e12, &intel_g45_info),
|
|
+ INTEL_VGA_DEVICE(0x2e22, &intel_g45_info),
|
|
+ INTEL_VGA_DEVICE(0x2e32, &intel_g45_info),
|
|
+ INTEL_VGA_DEVICE(0x2e42, &intel_g45_info),
|
|
+ INTEL_VGA_DEVICE(0xa001, &intel_pineview_info),
|
|
+ INTEL_VGA_DEVICE(0xa011, &intel_pineview_info),
|
|
+ INTEL_VGA_DEVICE(0x0042, &intel_ironlake_d_info),
|
|
+ INTEL_VGA_DEVICE(0x0046, &intel_ironlake_m_info),
|
|
+ {0, 0, 0}
|
|
};
|
|
|
|
#if defined(CONFIG_DRM_I915_KMS)
|
|
MODULE_DEVICE_TABLE(pci, pciidlist);
|
|
#endif
|
|
|
|
-static int i915_suspend(struct drm_device *dev, pm_message_t state)
|
|
+static int i915_drm_freeze(struct drm_device *dev)
|
|
{
|
|
- struct drm_i915_private *dev_priv = dev->dev_private;
|
|
-
|
|
- if (!dev || !dev_priv) {
|
|
- DRM_ERROR("dev: %p, dev_priv: %p\n", dev, dev_priv);
|
|
- DRM_ERROR("DRM not initialized, aborting suspend.\n");
|
|
- return -ENODEV;
|
|
- }
|
|
-
|
|
- if (state.event == PM_EVENT_PRETHAW)
|
|
- return 0;
|
|
-
|
|
pci_save_state(dev->pdev);
|
|
|
|
/* If KMS is active, we do the leavevt stuff here */
|
|
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
|
- if (i915_gem_idle(dev))
|
|
+ int error = i915_gem_idle(dev);
|
|
+ if (error) {
|
|
dev_err(&dev->pdev->dev,
|
|
- "GEM idle failed, resume may fail\n");
|
|
+ "GEM idle failed, resume might fail\n");
|
|
+ return error;
|
|
+ }
|
|
drm_irq_uninstall(dev);
|
|
}
|
|
|
|
i915_save_state(dev);
|
|
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void i915_drm_suspend(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
+
|
|
intel_opregion_free(dev, 1);
|
|
|
|
+ /* Modeset on resume, not lid events */
|
|
+ dev_priv->modeset_on_lid = 0;
|
|
+}
|
|
+
|
|
+static int i915_suspend(struct drm_device *dev, pm_message_t state)
|
|
+{
|
|
+ int error;
|
|
+
|
|
+ if (!dev || !dev->dev_private) {
|
|
+ DRM_ERROR("dev: %p\n", dev);
|
|
+ DRM_ERROR("DRM not initialized, aborting suspend.\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ if (state.event == PM_EVENT_PRETHAW)
|
|
+ return 0;
|
|
+
|
|
+ error = i915_drm_freeze(dev);
|
|
+ if (error)
|
|
+ return error;
|
|
+
|
|
+ i915_drm_suspend(dev);
|
|
+
|
|
if (state.event == PM_EVENT_SUSPEND) {
|
|
/* Shut down the device */
|
|
pci_disable_device(dev->pdev);
|
|
pci_set_power_state(dev->pdev, PCI_D3hot);
|
|
}
|
|
|
|
- /* Modeset on resume, not lid events */
|
|
- dev_priv->modeset_on_lid = 0;
|
|
-
|
|
return 0;
|
|
}
|
|
|
|
-static int i915_resume(struct drm_device *dev)
|
|
+static int i915_drm_thaw(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
- int ret = 0;
|
|
-
|
|
- if (pci_enable_device(dev->pdev))
|
|
- return -1;
|
|
- pci_set_master(dev->pdev);
|
|
-
|
|
- i915_restore_state(dev);
|
|
-
|
|
- intel_opregion_init(dev, 1);
|
|
+ int error = 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;
|
|
+ error = i915_gem_init_ringbuffer(dev);
|
|
mutex_unlock(&dev->struct_mutex);
|
|
|
|
drm_irq_install(dev);
|
|
- }
|
|
- if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
|
+
|
|
/* Resume the modeset for every activated CRTC */
|
|
drm_helper_resume_force_mode(dev);
|
|
}
|
|
|
|
dev_priv->modeset_on_lid = 0;
|
|
|
|
- return ret;
|
|
+ return error;
|
|
+}
|
|
+
|
|
+static int i915_resume(struct drm_device *dev)
|
|
+{
|
|
+ if (pci_enable_device(dev->pdev))
|
|
+ return -EIO;
|
|
+
|
|
+ pci_set_master(dev->pdev);
|
|
+
|
|
+ i915_restore_state(dev);
|
|
+
|
|
+ intel_opregion_init(dev, 1);
|
|
+
|
|
+ return i915_drm_thaw(dev);
|
|
}
|
|
|
|
/**
|
|
@@ -268,22 +408,80 @@ i915_pci_remove(struct pci_dev *pdev)
|
|
drm_put_dev(dev);
|
|
}
|
|
|
|
-static int
|
|
-i915_pci_suspend(struct pci_dev *pdev, pm_message_t state)
|
|
+static int i915_pm_suspend(struct device *dev)
|
|
{
|
|
- struct drm_device *dev = pci_get_drvdata(pdev);
|
|
+ struct pci_dev *pdev = to_pci_dev(dev);
|
|
+ struct drm_device *drm_dev = pci_get_drvdata(pdev);
|
|
+ int error;
|
|
|
|
- return i915_suspend(dev, state);
|
|
+ if (!drm_dev || !drm_dev->dev_private) {
|
|
+ dev_err(dev, "DRM not initialized, aborting suspend.\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ error = i915_drm_freeze(drm_dev);
|
|
+ if (error)
|
|
+ return error;
|
|
+
|
|
+ i915_drm_suspend(drm_dev);
|
|
+
|
|
+ pci_disable_device(pdev);
|
|
+ pci_set_power_state(pdev, PCI_D3hot);
|
|
+
|
|
+ return 0;
|
|
}
|
|
|
|
-static int
|
|
-i915_pci_resume(struct pci_dev *pdev)
|
|
+static int i915_pm_resume(struct device *dev)
|
|
{
|
|
- struct drm_device *dev = pci_get_drvdata(pdev);
|
|
+ struct pci_dev *pdev = to_pci_dev(dev);
|
|
+ struct drm_device *drm_dev = pci_get_drvdata(pdev);
|
|
+
|
|
+ return i915_resume(drm_dev);
|
|
+}
|
|
+
|
|
+static int i915_pm_freeze(struct device *dev)
|
|
+{
|
|
+ struct pci_dev *pdev = to_pci_dev(dev);
|
|
+ struct drm_device *drm_dev = pci_get_drvdata(pdev);
|
|
+
|
|
+ if (!drm_dev || !drm_dev->dev_private) {
|
|
+ dev_err(dev, "DRM not initialized, aborting suspend.\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
|
|
- return i915_resume(dev);
|
|
+ return i915_drm_freeze(drm_dev);
|
|
}
|
|
|
|
+static int i915_pm_thaw(struct device *dev)
|
|
+{
|
|
+ struct pci_dev *pdev = to_pci_dev(dev);
|
|
+ struct drm_device *drm_dev = pci_get_drvdata(pdev);
|
|
+
|
|
+ return i915_drm_thaw(drm_dev);
|
|
+}
|
|
+
|
|
+static int i915_pm_poweroff(struct device *dev)
|
|
+{
|
|
+ struct pci_dev *pdev = to_pci_dev(dev);
|
|
+ struct drm_device *drm_dev = pci_get_drvdata(pdev);
|
|
+ int error;
|
|
+
|
|
+ error = i915_drm_freeze(drm_dev);
|
|
+ if (!error)
|
|
+ i915_drm_suspend(drm_dev);
|
|
+
|
|
+ return error;
|
|
+}
|
|
+
|
|
+const struct dev_pm_ops i915_pm_ops = {
|
|
+ .suspend = i915_pm_suspend,
|
|
+ .resume = i915_pm_resume,
|
|
+ .freeze = i915_pm_freeze,
|
|
+ .thaw = i915_pm_thaw,
|
|
+ .poweroff = i915_pm_poweroff,
|
|
+ .restore = i915_pm_resume,
|
|
+};
|
|
+
|
|
static struct vm_operations_struct i915_gem_vm_ops = {
|
|
.fault = i915_gem_fault,
|
|
.open = drm_gem_vm_open,
|
|
@@ -303,8 +501,11 @@ static struct drm_driver driver = {
|
|
.lastclose = i915_driver_lastclose,
|
|
.preclose = i915_driver_preclose,
|
|
.postclose = i915_driver_postclose,
|
|
+
|
|
+ /* Used in place of i915_pm_ops for non-DRIVER_MODESET */
|
|
.suspend = i915_suspend,
|
|
.resume = i915_resume,
|
|
+
|
|
.device_is_agp = i915_driver_device_is_agp,
|
|
.enable_vblank = i915_enable_vblank,
|
|
.disable_vblank = i915_disable_vblank,
|
|
@@ -329,10 +530,11 @@ static struct drm_driver driver = {
|
|
.owner = THIS_MODULE,
|
|
.open = drm_open,
|
|
.release = drm_release,
|
|
- .ioctl = drm_ioctl,
|
|
+ .unlocked_ioctl = drm_ioctl,
|
|
.mmap = drm_gem_mmap,
|
|
.poll = drm_poll,
|
|
.fasync = drm_fasync,
|
|
+ .read = drm_read,
|
|
#ifdef CONFIG_COMPAT
|
|
.compat_ioctl = i915_compat_ioctl,
|
|
#endif
|
|
@@ -343,10 +545,7 @@ static struct drm_driver driver = {
|
|
.id_table = pciidlist,
|
|
.probe = i915_pci_probe,
|
|
.remove = i915_pci_remove,
|
|
-#ifdef CONFIG_PM
|
|
- .resume = i915_pci_resume,
|
|
- .suspend = i915_pci_suspend,
|
|
-#endif
|
|
+ .driver.pm = &i915_pm_ops,
|
|
},
|
|
|
|
.name = DRIVER_NAME,
|
|
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
|
|
index 7277246..b99b6a8 100644
|
|
--- a/drivers/gpu/drm/i915/i915_drv.h
|
|
+++ b/drivers/gpu/drm/i915/i915_drv.h
|
|
@@ -170,9 +170,33 @@ struct drm_i915_display_funcs {
|
|
/* clock gating init */
|
|
};
|
|
|
|
+struct intel_overlay;
|
|
+
|
|
+struct intel_device_info {
|
|
+ u8 is_mobile : 1;
|
|
+ u8 is_i8xx : 1;
|
|
+ u8 is_i915g : 1;
|
|
+ u8 is_i9xx : 1;
|
|
+ u8 is_i945gm : 1;
|
|
+ u8 is_i965g : 1;
|
|
+ u8 is_i965gm : 1;
|
|
+ u8 is_g33 : 1;
|
|
+ u8 need_gfx_hws : 1;
|
|
+ u8 is_g4x : 1;
|
|
+ u8 is_pineview : 1;
|
|
+ u8 is_ironlake : 1;
|
|
+ u8 has_fbc : 1;
|
|
+ u8 has_rc6 : 1;
|
|
+ u8 has_pipe_cxsr : 1;
|
|
+ u8 has_hotplug : 1;
|
|
+ u8 cursor_needs_physical : 1;
|
|
+};
|
|
+
|
|
typedef struct drm_i915_private {
|
|
struct drm_device *dev;
|
|
|
|
+ const struct intel_device_info *info;
|
|
+
|
|
int has_gem;
|
|
|
|
void __iomem *regs;
|
|
@@ -187,6 +211,7 @@ typedef struct drm_i915_private {
|
|
unsigned int status_gfx_addr;
|
|
drm_local_map_t hws_map;
|
|
struct drm_gem_object *hws_obj;
|
|
+ struct drm_gem_object *pwrctx;
|
|
|
|
struct resource mch_res;
|
|
|
|
@@ -206,11 +231,13 @@ typedef struct drm_i915_private {
|
|
/** Cached value of IMR to avoid reads in updating the bitfield */
|
|
u32 irq_mask_reg;
|
|
u32 pipestat[2];
|
|
- /** splitted irq regs for graphics and display engine on IGDNG,
|
|
+ /** splitted irq regs for graphics and display engine on Ironlake,
|
|
irq_mask_reg is still used for display irq. */
|
|
u32 gt_irq_mask_reg;
|
|
u32 gt_irq_enable_reg;
|
|
u32 de_irq_enable_reg;
|
|
+ u32 pch_irq_mask_reg;
|
|
+ u32 pch_irq_enable_reg;
|
|
|
|
u32 hotplug_supported_mask;
|
|
struct work_struct hotplug_work;
|
|
@@ -227,8 +254,6 @@ typedef struct drm_i915_private {
|
|
int hangcheck_count;
|
|
uint32_t last_acthd;
|
|
|
|
- bool cursor_needs_physical;
|
|
-
|
|
struct drm_mm vram;
|
|
|
|
unsigned long cfb_size;
|
|
@@ -240,6 +265,9 @@ typedef struct drm_i915_private {
|
|
|
|
struct intel_opregion opregion;
|
|
|
|
+ /* overlay */
|
|
+ struct intel_overlay *overlay;
|
|
+
|
|
/* LVDS info */
|
|
int backlight_duty_cycle; /* restore backlight to this value */
|
|
bool panel_wants_dither;
|
|
@@ -255,10 +283,11 @@ typedef struct drm_i915_private {
|
|
unsigned int lvds_use_ssc:1;
|
|
unsigned int edp_support:1;
|
|
int lvds_ssc_freq;
|
|
+ int edp_bpp;
|
|
|
|
struct notifier_block lid_notifier;
|
|
|
|
- int crt_ddc_bus; /* -1 = unknown, else GPIO to use for CRT DDC */
|
|
+ int crt_ddc_bus; /* 0 = unknown, else GPIO to use for CRT DDC */
|
|
struct drm_i915_fence_reg fence_regs[16]; /* assume 965 */
|
|
int fence_reg_start; /* 4 if userland hasn't ioctl'd us yet */
|
|
int num_fence_regs; /* 8 on pre-965, 16 otherwise */
|
|
@@ -279,7 +308,6 @@ typedef struct drm_i915_private {
|
|
u32 saveDSPACNTR;
|
|
u32 saveDSPBCNTR;
|
|
u32 saveDSPARB;
|
|
- u32 saveRENDERSTANDBY;
|
|
u32 saveHWS;
|
|
u32 savePIPEACONF;
|
|
u32 savePIPEBCONF;
|
|
@@ -374,8 +402,6 @@ typedef struct drm_i915_private {
|
|
u32 saveFDI_RXA_IMR;
|
|
u32 saveFDI_RXB_IMR;
|
|
u32 saveCACHE_MODE_0;
|
|
- u32 saveD_STATE;
|
|
- u32 saveDSPCLK_GATE_D;
|
|
u32 saveMI_ARB_STATE;
|
|
u32 saveSWF0[16];
|
|
u32 saveSWF1[16];
|
|
@@ -467,6 +493,15 @@ typedef struct drm_i915_private {
|
|
struct list_head flushing_list;
|
|
|
|
/**
|
|
+ * List of objects currently pending a GPU write flush.
|
|
+ *
|
|
+ * All elements on this list will belong to either the
|
|
+ * active_list or flushing_list, last_rendering_seqno can
|
|
+ * be used to differentiate between the two elements.
|
|
+ */
|
|
+ struct list_head gpu_write_list;
|
|
+
|
|
+ /**
|
|
* LRU list of objects which are not in the ringbuffer and
|
|
* are ready to unbind, but are still in the GTT.
|
|
*
|
|
@@ -539,13 +574,21 @@ typedef struct drm_i915_private {
|
|
/* indicate whether the LVDS_BORDER should be enabled or not */
|
|
unsigned int lvds_border_bits;
|
|
|
|
+ struct drm_crtc *plane_to_crtc_mapping[2];
|
|
+ struct drm_crtc *pipe_to_crtc_mapping[2];
|
|
+ wait_queue_head_t pending_flip_queue;
|
|
+
|
|
/* Reclocking support */
|
|
bool render_reclock_avail;
|
|
bool lvds_downclock_avail;
|
|
+ /* indicates the reduced downclock for LVDS*/
|
|
+ int lvds_downclock;
|
|
struct work_struct idle_work;
|
|
struct timer_list idle_timer;
|
|
bool busy;
|
|
u16 orig_clock;
|
|
+ int child_dev_num;
|
|
+ struct child_device_config *child_dev;
|
|
struct drm_connector *int_lvds_connector;
|
|
} drm_i915_private_t;
|
|
|
|
@@ -558,6 +601,8 @@ struct drm_i915_gem_object {
|
|
|
|
/** This object's place on the active/flushing/inactive lists */
|
|
struct list_head list;
|
|
+ /** This object's place on GPU write list */
|
|
+ struct list_head gpu_write_list;
|
|
|
|
/** This object's place on the fenced object LRU */
|
|
struct list_head fence_list;
|
|
@@ -639,6 +684,13 @@ struct drm_i915_gem_object {
|
|
* Advice: are the backing pages purgeable?
|
|
*/
|
|
int madv;
|
|
+
|
|
+ /**
|
|
+ * Number of crtcs where this object is currently the fb, but
|
|
+ * will be page flipped away on the next vblank. When it
|
|
+ * reaches 0, dev_priv->pending_flip_queue will be woken up.
|
|
+ */
|
|
+ atomic_t pending_flip;
|
|
};
|
|
|
|
/**
|
|
@@ -682,6 +734,7 @@ extern struct drm_ioctl_desc i915_ioctls[];
|
|
extern int i915_max_ioctl;
|
|
extern unsigned int i915_fbpercrtc;
|
|
extern unsigned int i915_powersave;
|
|
+extern unsigned int i915_lvds_downclock;
|
|
|
|
extern void i915_save_display(struct drm_device *dev);
|
|
extern void i915_restore_display(struct drm_device *dev);
|
|
@@ -739,6 +792,8 @@ i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask);
|
|
void
|
|
i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask);
|
|
|
|
+void intel_enable_asle (struct drm_device *dev);
|
|
+
|
|
|
|
/* i915_mem.c */
|
|
extern int i915_mem_alloc(struct drm_device *dev, void *data,
|
|
@@ -771,6 +826,8 @@ int i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data,
|
|
struct drm_file *file_priv);
|
|
int i915_gem_execbuffer(struct drm_device *dev, void *data,
|
|
struct drm_file *file_priv);
|
|
+int i915_gem_execbuffer2(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv);
|
|
int i915_gem_pin_ioctl(struct drm_device *dev, void *data,
|
|
struct drm_file *file_priv);
|
|
int i915_gem_unpin_ioctl(struct drm_device *dev, void *data,
|
|
@@ -814,9 +871,13 @@ 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);
|
|
+uint32_t i915_add_request(struct drm_device *dev, struct drm_file *file_priv,
|
|
+ uint32_t flush_domains);
|
|
+int i915_do_wait_request(struct drm_device *dev, uint32_t seqno, int interruptible);
|
|
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_object_set_to_display_plane(struct drm_gem_object *obj);
|
|
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,
|
|
@@ -825,6 +886,7 @@ void i915_gem_free_all_phys_object(struct drm_device *dev);
|
|
int i915_gem_object_get_pages(struct drm_gem_object *obj, gfp_t gfpmask);
|
|
void i915_gem_object_put_pages(struct drm_gem_object *obj);
|
|
void i915_gem_release(struct drm_device * dev, struct drm_file *file_priv);
|
|
+void i915_gem_object_flush_write_domain(struct drm_gem_object *obj);
|
|
|
|
void i915_gem_shrinker_init(void);
|
|
void i915_gem_shrinker_exit(void);
|
|
@@ -833,6 +895,9 @@ void i915_gem_shrinker_exit(void);
|
|
void i915_gem_detect_bit_6_swizzle(struct drm_device *dev);
|
|
void i915_gem_object_do_bit_17_swizzle(struct drm_gem_object *obj);
|
|
void i915_gem_object_save_bit_17_swizzle(struct drm_gem_object *obj);
|
|
+bool i915_tiling_ok(struct drm_device *dev, int stride, int size,
|
|
+ int tiling_mode);
|
|
+bool i915_obj_fenceable(struct drm_device *dev, struct drm_gem_object *obj);
|
|
|
|
/* i915_gem_debug.c */
|
|
void i915_gem_dump_object(struct drm_gem_object *obj, int len,
|
|
@@ -864,11 +929,13 @@ extern int i915_restore_state(struct drm_device *dev);
|
|
extern int intel_opregion_init(struct drm_device *dev, int resume);
|
|
extern void intel_opregion_free(struct drm_device *dev, int suspend);
|
|
extern void opregion_asle_intr(struct drm_device *dev);
|
|
+extern void ironlake_opregion_gse_intr(struct drm_device *dev);
|
|
extern void opregion_enable_asle(struct drm_device *dev);
|
|
#else
|
|
static inline int intel_opregion_init(struct drm_device *dev, int resume) { return 0; }
|
|
static inline void intel_opregion_free(struct drm_device *dev, int suspend) { return; }
|
|
static inline void opregion_asle_intr(struct drm_device *dev) { return; }
|
|
+static inline void ironlake_opregion_gse_intr(struct drm_device *dev) { return; }
|
|
static inline void opregion_enable_asle(struct drm_device *dev) { return; }
|
|
#endif
|
|
|
|
@@ -953,89 +1020,52 @@ extern void g4x_disable_fbc(struct drm_device *dev);
|
|
extern int i915_wrap_ring(struct drm_device * dev);
|
|
extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
|
|
|
|
-#define IS_I830(dev) ((dev)->pci_device == 0x3577)
|
|
-#define IS_845G(dev) ((dev)->pci_device == 0x2562)
|
|
-#define IS_I85X(dev) ((dev)->pci_device == 0x3582)
|
|
-#define IS_I855(dev) ((dev)->pci_device == 0x3582)
|
|
-#define IS_I865G(dev) ((dev)->pci_device == 0x2572)
|
|
-#define IS_I8XX(dev) (IS_I830(dev) || IS_845G(dev) || IS_I85X(dev) || IS_I865G(dev))
|
|
-
|
|
-#define IS_I915G(dev) ((dev)->pci_device == 0x2582 || (dev)->pci_device == 0x258a)
|
|
-#define IS_I915GM(dev) ((dev)->pci_device == 0x2592)
|
|
-#define IS_I945G(dev) ((dev)->pci_device == 0x2772)
|
|
-#define IS_I945GM(dev) ((dev)->pci_device == 0x27A2 ||\
|
|
- (dev)->pci_device == 0x27AE)
|
|
-#define IS_I965G(dev) ((dev)->pci_device == 0x2972 || \
|
|
- (dev)->pci_device == 0x2982 || \
|
|
- (dev)->pci_device == 0x2992 || \
|
|
- (dev)->pci_device == 0x29A2 || \
|
|
- (dev)->pci_device == 0x2A02 || \
|
|
- (dev)->pci_device == 0x2A12 || \
|
|
- (dev)->pci_device == 0x2A42 || \
|
|
- (dev)->pci_device == 0x2E02 || \
|
|
- (dev)->pci_device == 0x2E12 || \
|
|
- (dev)->pci_device == 0x2E22 || \
|
|
- (dev)->pci_device == 0x2E32 || \
|
|
- (dev)->pci_device == 0x2E42 || \
|
|
- (dev)->pci_device == 0x0042 || \
|
|
- (dev)->pci_device == 0x0046)
|
|
-
|
|
-#define IS_I965GM(dev) ((dev)->pci_device == 0x2A02 || \
|
|
- (dev)->pci_device == 0x2A12)
|
|
-
|
|
-#define IS_GM45(dev) ((dev)->pci_device == 0x2A42)
|
|
-
|
|
-#define IS_G4X(dev) ((dev)->pci_device == 0x2E02 || \
|
|
- (dev)->pci_device == 0x2E12 || \
|
|
- (dev)->pci_device == 0x2E22 || \
|
|
- (dev)->pci_device == 0x2E32 || \
|
|
- (dev)->pci_device == 0x2E42 || \
|
|
- IS_GM45(dev))
|
|
-
|
|
-#define IS_IGDG(dev) ((dev)->pci_device == 0xa001)
|
|
-#define IS_IGDGM(dev) ((dev)->pci_device == 0xa011)
|
|
-#define IS_IGD(dev) (IS_IGDG(dev) || IS_IGDGM(dev))
|
|
-
|
|
-#define IS_G33(dev) ((dev)->pci_device == 0x29C2 || \
|
|
- (dev)->pci_device == 0x29B2 || \
|
|
- (dev)->pci_device == 0x29D2 || \
|
|
- (IS_IGD(dev)))
|
|
-
|
|
-#define IS_IGDNG_D(dev) ((dev)->pci_device == 0x0042)
|
|
-#define IS_IGDNG_M(dev) ((dev)->pci_device == 0x0046)
|
|
-#define IS_IGDNG(dev) (IS_IGDNG_D(dev) || IS_IGDNG_M(dev))
|
|
-
|
|
-#define IS_I9XX(dev) (IS_I915G(dev) || IS_I915GM(dev) || IS_I945G(dev) || \
|
|
- IS_I945GM(dev) || IS_I965G(dev) || IS_G33(dev) || \
|
|
- IS_IGDNG(dev))
|
|
-
|
|
-#define IS_MOBILE(dev) (IS_I830(dev) || IS_I85X(dev) || IS_I915GM(dev) || \
|
|
- IS_I945GM(dev) || IS_I965GM(dev) || IS_GM45(dev) || \
|
|
- IS_IGD(dev) || IS_IGDNG_M(dev))
|
|
-
|
|
-#define I915_NEED_GFX_HWS(dev) (IS_G33(dev) || IS_GM45(dev) || IS_G4X(dev) || \
|
|
- IS_IGDNG(dev))
|
|
+#define INTEL_INFO(dev) (((struct drm_i915_private *) (dev)->dev_private)->info)
|
|
+
|
|
+#define IS_I830(dev) ((dev)->pci_device == 0x3577)
|
|
+#define IS_845G(dev) ((dev)->pci_device == 0x2562)
|
|
+#define IS_I85X(dev) ((dev)->pci_device == 0x3582)
|
|
+#define IS_I865G(dev) ((dev)->pci_device == 0x2572)
|
|
+#define IS_I8XX(dev) (INTEL_INFO(dev)->is_i8xx)
|
|
+#define IS_I915G(dev) (INTEL_INFO(dev)->is_i915g)
|
|
+#define IS_I915GM(dev) ((dev)->pci_device == 0x2592)
|
|
+#define IS_I945G(dev) ((dev)->pci_device == 0x2772)
|
|
+#define IS_I945GM(dev) (INTEL_INFO(dev)->is_i945gm)
|
|
+#define IS_I965G(dev) (INTEL_INFO(dev)->is_i965g)
|
|
+#define IS_I965GM(dev) (INTEL_INFO(dev)->is_i965gm)
|
|
+#define IS_GM45(dev) ((dev)->pci_device == 0x2A42)
|
|
+#define IS_G4X(dev) (INTEL_INFO(dev)->is_g4x)
|
|
+#define IS_PINEVIEW_G(dev) ((dev)->pci_device == 0xa001)
|
|
+#define IS_PINEVIEW_M(dev) ((dev)->pci_device == 0xa011)
|
|
+#define IS_PINEVIEW(dev) (INTEL_INFO(dev)->is_pineview)
|
|
+#define IS_G33(dev) (INTEL_INFO(dev)->is_g33)
|
|
+#define IS_IRONLAKE_D(dev) ((dev)->pci_device == 0x0042)
|
|
+#define IS_IRONLAKE_M(dev) ((dev)->pci_device == 0x0046)
|
|
+#define IS_IRONLAKE(dev) (INTEL_INFO(dev)->is_ironlake)
|
|
+#define IS_I9XX(dev) (INTEL_INFO(dev)->is_i9xx)
|
|
+#define IS_MOBILE(dev) (INTEL_INFO(dev)->is_mobile)
|
|
+
|
|
+#define I915_NEED_GFX_HWS(dev) (INTEL_INFO(dev)->need_gfx_hws)
|
|
+
|
|
/* 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_DIGITAL_OUTPUTS(dev) (IS_I9XX(dev) && !IS_IGD(dev))
|
|
-#define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev) || IS_IGDNG(dev))
|
|
-#define SUPPORTS_INTEGRATED_DP(dev) (IS_G4X(dev) || IS_IGDNG(dev))
|
|
-#define SUPPORTS_EDP(dev) (IS_IGDNG_M(dev))
|
|
+#define SUPPORTS_DIGITAL_OUTPUTS(dev) (IS_I9XX(dev) && !IS_PINEVIEW(dev))
|
|
+#define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev) || IS_IRONLAKE(dev))
|
|
+#define SUPPORTS_INTEGRATED_DP(dev) (IS_G4X(dev) || IS_IRONLAKE(dev))
|
|
+#define SUPPORTS_EDP(dev) (IS_IRONLAKE_M(dev))
|
|
#define SUPPORTS_TV(dev) (IS_I9XX(dev) && IS_MOBILE(dev) && \
|
|
- !IS_IGDNG(dev) && !IS_IGD(dev))
|
|
-#define I915_HAS_HOTPLUG(dev) (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev) || IS_I965G(dev))
|
|
+ !IS_IRONLAKE(dev) && !IS_PINEVIEW(dev))
|
|
+#define I915_HAS_HOTPLUG(dev) (INTEL_INFO(dev)->has_hotplug)
|
|
/* dsparb controlled by hw only */
|
|
-#define DSPARB_HWCONTROL(dev) (IS_G4X(dev) || IS_IGDNG(dev))
|
|
-
|
|
-#define HAS_FW_BLC(dev) (IS_I9XX(dev) || IS_G4X(dev) || IS_IGDNG(dev))
|
|
-#define HAS_PIPE_CXSR(dev) (IS_G4X(dev) || IS_IGDNG(dev))
|
|
-#define I915_HAS_FBC(dev) (IS_MOBILE(dev) && \
|
|
- (IS_I9XX(dev) || IS_GM45(dev)) && \
|
|
- !IS_IGD(dev) && \
|
|
- !IS_IGDNG(dev))
|
|
+#define DSPARB_HWCONTROL(dev) (IS_G4X(dev) || IS_IRONLAKE(dev))
|
|
+
|
|
+#define HAS_FW_BLC(dev) (IS_I9XX(dev) || IS_G4X(dev) || IS_IRONLAKE(dev))
|
|
+#define HAS_PIPE_CXSR(dev) (INTEL_INFO(dev)->has_pipe_cxsr)
|
|
+#define I915_HAS_FBC(dev) (INTEL_INFO(dev)->has_fbc)
|
|
+#define I915_HAS_RC6(dev) (INTEL_INFO(dev)->has_rc6)
|
|
|
|
#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 8ad244a..ec8a0d7 100644
|
|
--- a/drivers/gpu/drm/i915/i915_gem.c
|
|
+++ b/drivers/gpu/drm/i915/i915_gem.c
|
|
@@ -1294,7 +1294,7 @@ out_free_list:
|
|
* i915_gem_release_mmap - remove physical page mappings
|
|
* @obj: obj in question
|
|
*
|
|
- * Preserve the reservation of the mmaping with the DRM core code, but
|
|
+ * Preserve the reservation of the mmapping with the DRM core code, but
|
|
* relinquish ownership of the pages back to the system.
|
|
*
|
|
* It is vital that we remove the page mapping if we have mapped a tiled
|
|
@@ -1552,6 +1552,8 @@ i915_gem_object_move_to_inactive(struct drm_gem_object *obj)
|
|
else
|
|
list_move_tail(&obj_priv->list, &dev_priv->mm.inactive_list);
|
|
|
|
+ BUG_ON(!list_empty(&obj_priv->gpu_write_list));
|
|
+
|
|
obj_priv->last_rendering_seqno = 0;
|
|
if (obj_priv->active) {
|
|
obj_priv->active = 0;
|
|
@@ -1568,7 +1570,7 @@ i915_gem_object_move_to_inactive(struct drm_gem_object *obj)
|
|
*
|
|
* Returned sequence numbers are nonzero on success.
|
|
*/
|
|
-static uint32_t
|
|
+uint32_t
|
|
i915_add_request(struct drm_device *dev, struct drm_file *file_priv,
|
|
uint32_t flush_domains)
|
|
{
|
|
@@ -1602,7 +1604,7 @@ i915_add_request(struct drm_device *dev, struct drm_file *file_priv,
|
|
OUT_RING(MI_USER_INTERRUPT);
|
|
ADVANCE_LP_RING();
|
|
|
|
- DRM_DEBUG("%d\n", seqno);
|
|
+ DRM_DEBUG_DRIVER("%d\n", seqno);
|
|
|
|
request->seqno = seqno;
|
|
request->emitted_jiffies = jiffies;
|
|
@@ -1622,7 +1624,8 @@ i915_add_request(struct drm_device *dev, struct drm_file *file_priv,
|
|
struct drm_i915_gem_object *obj_priv, *next;
|
|
|
|
list_for_each_entry_safe(obj_priv, next,
|
|
- &dev_priv->mm.flushing_list, list) {
|
|
+ &dev_priv->mm.gpu_write_list,
|
|
+ gpu_write_list) {
|
|
struct drm_gem_object *obj = obj_priv->obj;
|
|
|
|
if ((obj->write_domain & flush_domains) ==
|
|
@@ -1630,6 +1633,7 @@ i915_add_request(struct drm_device *dev, struct drm_file *file_priv,
|
|
uint32_t old_write_domain = obj->write_domain;
|
|
|
|
obj->write_domain = 0;
|
|
+ list_del_init(&obj_priv->gpu_write_list);
|
|
i915_gem_object_move_to_active(obj, seqno);
|
|
|
|
trace_i915_gem_object_change_domain(obj,
|
|
@@ -1805,12 +1809,8 @@ i915_gem_retire_work_handler(struct work_struct *work)
|
|
mutex_unlock(&dev->struct_mutex);
|
|
}
|
|
|
|
-/**
|
|
- * Waits for a sequence number to be signaled, and cleans up the
|
|
- * request and object lists appropriately for that event.
|
|
- */
|
|
-static int
|
|
-i915_wait_request(struct drm_device *dev, uint32_t seqno)
|
|
+int
|
|
+i915_do_wait_request(struct drm_device *dev, uint32_t seqno, int interruptible)
|
|
{
|
|
drm_i915_private_t *dev_priv = dev->dev_private;
|
|
u32 ier;
|
|
@@ -1822,7 +1822,7 @@ i915_wait_request(struct drm_device *dev, uint32_t seqno)
|
|
return -EIO;
|
|
|
|
if (!i915_seqno_passed(i915_get_gem_seqno(dev), seqno)) {
|
|
- if (IS_IGDNG(dev))
|
|
+ if (IS_IRONLAKE(dev))
|
|
ier = I915_READ(DEIER) | I915_READ(GTIER);
|
|
else
|
|
ier = I915_READ(IER);
|
|
@@ -1837,10 +1837,15 @@ i915_wait_request(struct drm_device *dev, uint32_t seqno)
|
|
|
|
dev_priv->mm.waiting_gem_seqno = seqno;
|
|
i915_user_irq_get(dev);
|
|
- ret = wait_event_interruptible(dev_priv->irq_queue,
|
|
- i915_seqno_passed(i915_get_gem_seqno(dev),
|
|
- seqno) ||
|
|
- atomic_read(&dev_priv->mm.wedged));
|
|
+ if (interruptible)
|
|
+ ret = wait_event_interruptible(dev_priv->irq_queue,
|
|
+ i915_seqno_passed(i915_get_gem_seqno(dev), seqno) ||
|
|
+ atomic_read(&dev_priv->mm.wedged));
|
|
+ else
|
|
+ wait_event(dev_priv->irq_queue,
|
|
+ i915_seqno_passed(i915_get_gem_seqno(dev), seqno) ||
|
|
+ atomic_read(&dev_priv->mm.wedged));
|
|
+
|
|
i915_user_irq_put(dev);
|
|
dev_priv->mm.waiting_gem_seqno = 0;
|
|
|
|
@@ -1864,6 +1869,16 @@ i915_wait_request(struct drm_device *dev, uint32_t seqno)
|
|
return ret;
|
|
}
|
|
|
|
+/**
|
|
+ * Waits for a sequence number to be signaled, and cleans up the
|
|
+ * request and object lists appropriately for that event.
|
|
+ */
|
|
+static int
|
|
+i915_wait_request(struct drm_device *dev, uint32_t seqno)
|
|
+{
|
|
+ return i915_do_wait_request(dev, seqno, 1);
|
|
+}
|
|
+
|
|
static void
|
|
i915_gem_flush(struct drm_device *dev,
|
|
uint32_t invalidate_domains,
|
|
@@ -1932,7 +1947,7 @@ i915_gem_flush(struct drm_device *dev,
|
|
#endif
|
|
BEGIN_LP_RING(2);
|
|
OUT_RING(cmd);
|
|
- OUT_RING(0); /* noop */
|
|
+ OUT_RING(MI_NOOP);
|
|
ADVANCE_LP_RING();
|
|
}
|
|
}
|
|
@@ -2073,8 +2088,8 @@ static int
|
|
i915_gem_evict_everything(struct drm_device *dev)
|
|
{
|
|
drm_i915_private_t *dev_priv = dev->dev_private;
|
|
- uint32_t seqno;
|
|
int ret;
|
|
+ uint32_t seqno;
|
|
bool lists_empty;
|
|
|
|
spin_lock(&dev_priv->mm.active_list_lock);
|
|
@@ -2096,6 +2111,8 @@ i915_gem_evict_everything(struct drm_device *dev)
|
|
if (ret)
|
|
return ret;
|
|
|
|
+ BUG_ON(!list_empty(&dev_priv->mm.flushing_list));
|
|
+
|
|
ret = i915_gem_evict_from_inactive_list(dev);
|
|
if (ret)
|
|
return ret;
|
|
@@ -2690,7 +2707,7 @@ i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj)
|
|
old_write_domain = obj->write_domain;
|
|
i915_gem_flush(dev, 0, obj->write_domain);
|
|
seqno = i915_add_request(dev, NULL, obj->write_domain);
|
|
- obj->write_domain = 0;
|
|
+ BUG_ON(obj->write_domain);
|
|
i915_gem_object_move_to_active(obj, seqno);
|
|
|
|
trace_i915_gem_object_change_domain(obj,
|
|
@@ -2739,6 +2756,22 @@ i915_gem_object_flush_cpu_write_domain(struct drm_gem_object *obj)
|
|
old_write_domain);
|
|
}
|
|
|
|
+void
|
|
+i915_gem_object_flush_write_domain(struct drm_gem_object *obj)
|
|
+{
|
|
+ switch (obj->write_domain) {
|
|
+ case I915_GEM_DOMAIN_GTT:
|
|
+ i915_gem_object_flush_gtt_write_domain(obj);
|
|
+ break;
|
|
+ case I915_GEM_DOMAIN_CPU:
|
|
+ i915_gem_object_flush_cpu_write_domain(obj);
|
|
+ break;
|
|
+ default:
|
|
+ i915_gem_object_flush_gpu_write_domain(obj);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
/**
|
|
* Moves a single object to the GTT read, and possibly write domain.
|
|
*
|
|
@@ -2790,6 +2823,57 @@ i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj, int write)
|
|
return 0;
|
|
}
|
|
|
|
+/*
|
|
+ * Prepare buffer for display plane. Use uninterruptible for possible flush
|
|
+ * wait, as in modesetting process we're not supposed to be interrupted.
|
|
+ */
|
|
+int
|
|
+i915_gem_object_set_to_display_plane(struct drm_gem_object *obj)
|
|
+{
|
|
+ struct drm_device *dev = obj->dev;
|
|
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
|
|
+ uint32_t old_write_domain, old_read_domains;
|
|
+ int ret;
|
|
+
|
|
+ /* Not valid to be called on unbound objects. */
|
|
+ if (obj_priv->gtt_space == NULL)
|
|
+ return -EINVAL;
|
|
+
|
|
+ i915_gem_object_flush_gpu_write_domain(obj);
|
|
+
|
|
+ /* Wait on any GPU rendering and flushing to occur. */
|
|
+ if (obj_priv->active) {
|
|
+#if WATCH_BUF
|
|
+ DRM_INFO("%s: object %p wait for seqno %08x\n",
|
|
+ __func__, obj, obj_priv->last_rendering_seqno);
|
|
+#endif
|
|
+ ret = i915_do_wait_request(dev, obj_priv->last_rendering_seqno, 0);
|
|
+ if (ret != 0)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ old_write_domain = obj->write_domain;
|
|
+ old_read_domains = obj->read_domains;
|
|
+
|
|
+ obj->read_domains &= I915_GEM_DOMAIN_GTT;
|
|
+
|
|
+ i915_gem_object_flush_cpu_write_domain(obj);
|
|
+
|
|
+ /* It should now be out of any other write domains, and we can update
|
|
+ * the domain values for our changes.
|
|
+ */
|
|
+ BUG_ON((obj->write_domain & ~I915_GEM_DOMAIN_GTT) != 0);
|
|
+ obj->read_domains |= I915_GEM_DOMAIN_GTT;
|
|
+ obj->write_domain = I915_GEM_DOMAIN_GTT;
|
|
+ obj_priv->dirty = 1;
|
|
+
|
|
+ trace_i915_gem_object_change_domain(obj,
|
|
+ old_read_domains,
|
|
+ old_write_domain);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
/**
|
|
* Moves a single object to the CPU read, and possibly write domain.
|
|
*
|
|
@@ -3149,7 +3233,7 @@ i915_gem_object_set_cpu_read_domain_range(struct drm_gem_object *obj,
|
|
static int
|
|
i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
|
|
struct drm_file *file_priv,
|
|
- struct drm_i915_gem_exec_object *entry,
|
|
+ struct drm_i915_gem_exec_object2 *entry,
|
|
struct drm_i915_gem_relocation_entry *relocs)
|
|
{
|
|
struct drm_device *dev = obj->dev;
|
|
@@ -3157,12 +3241,35 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
|
|
struct drm_i915_gem_object *obj_priv = obj->driver_private;
|
|
int i, ret;
|
|
void __iomem *reloc_page;
|
|
+ bool need_fence;
|
|
+
|
|
+ need_fence = entry->flags & EXEC_OBJECT_NEEDS_FENCE &&
|
|
+ obj_priv->tiling_mode != I915_TILING_NONE;
|
|
+
|
|
+ /* Check fence reg constraints and rebind if necessary */
|
|
+ if (need_fence && !i915_obj_fenceable(dev, obj))
|
|
+ i915_gem_object_unbind(obj);
|
|
|
|
/* Choose the GTT offset for our buffer and put it there. */
|
|
ret = i915_gem_object_pin(obj, (uint32_t) entry->alignment);
|
|
if (ret)
|
|
return ret;
|
|
|
|
+ /*
|
|
+ * Pre-965 chips need a fence register set up in order to
|
|
+ * properly handle blits to/from tiled surfaces.
|
|
+ */
|
|
+ if (need_fence) {
|
|
+ ret = i915_gem_object_get_fence_reg(obj);
|
|
+ if (ret != 0) {
|
|
+ if (ret != -EBUSY && ret != -ERESTARTSYS)
|
|
+ DRM_ERROR("Failure to install fence: %d\n",
|
|
+ ret);
|
|
+ i915_gem_object_unpin(obj);
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
entry->offset = obj_priv->gtt_offset;
|
|
|
|
/* Apply the relocations, using the GTT aperture to avoid cache
|
|
@@ -3324,7 +3431,7 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
|
|
*/
|
|
static int
|
|
i915_dispatch_gem_execbuffer(struct drm_device *dev,
|
|
- struct drm_i915_gem_execbuffer *exec,
|
|
+ struct drm_i915_gem_execbuffer2 *exec,
|
|
struct drm_clip_rect *cliprects,
|
|
uint64_t exec_offset)
|
|
{
|
|
@@ -3414,7 +3521,7 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file_priv)
|
|
}
|
|
|
|
static int
|
|
-i915_gem_get_relocs_from_user(struct drm_i915_gem_exec_object *exec_list,
|
|
+i915_gem_get_relocs_from_user(struct drm_i915_gem_exec_object2 *exec_list,
|
|
uint32_t buffer_count,
|
|
struct drm_i915_gem_relocation_entry **relocs)
|
|
{
|
|
@@ -3429,8 +3536,10 @@ i915_gem_get_relocs_from_user(struct drm_i915_gem_exec_object *exec_list,
|
|
}
|
|
|
|
*relocs = drm_calloc_large(reloc_count, sizeof(**relocs));
|
|
- if (*relocs == NULL)
|
|
+ if (*relocs == NULL) {
|
|
+ DRM_ERROR("failed to alloc relocs, count %d\n", reloc_count);
|
|
return -ENOMEM;
|
|
+ }
|
|
|
|
for (i = 0; i < buffer_count; i++) {
|
|
struct drm_i915_gem_relocation_entry __user *user_relocs;
|
|
@@ -3454,13 +3563,16 @@ i915_gem_get_relocs_from_user(struct drm_i915_gem_exec_object *exec_list,
|
|
}
|
|
|
|
static int
|
|
-i915_gem_put_relocs_to_user(struct drm_i915_gem_exec_object *exec_list,
|
|
+i915_gem_put_relocs_to_user(struct drm_i915_gem_exec_object2 *exec_list,
|
|
uint32_t buffer_count,
|
|
struct drm_i915_gem_relocation_entry *relocs)
|
|
{
|
|
uint32_t reloc_count = 0, i;
|
|
int ret = 0;
|
|
|
|
+ if (relocs == NULL)
|
|
+ return 0;
|
|
+
|
|
for (i = 0; i < buffer_count; i++) {
|
|
struct drm_i915_gem_relocation_entry __user *user_relocs;
|
|
int unwritten;
|
|
@@ -3487,7 +3599,7 @@ err:
|
|
}
|
|
|
|
static int
|
|
-i915_gem_check_execbuffer (struct drm_i915_gem_execbuffer *exec,
|
|
+i915_gem_check_execbuffer (struct drm_i915_gem_execbuffer2 *exec,
|
|
uint64_t exec_offset)
|
|
{
|
|
uint32_t exec_start, exec_len;
|
|
@@ -3504,22 +3616,57 @@ i915_gem_check_execbuffer (struct drm_i915_gem_execbuffer *exec,
|
|
return 0;
|
|
}
|
|
|
|
+static int
|
|
+i915_gem_wait_for_pending_flip(struct drm_device *dev,
|
|
+ struct drm_gem_object **object_list,
|
|
+ int count)
|
|
+{
|
|
+ drm_i915_private_t *dev_priv = dev->dev_private;
|
|
+ struct drm_i915_gem_object *obj_priv;
|
|
+ DEFINE_WAIT(wait);
|
|
+ int i, ret = 0;
|
|
+
|
|
+ for (;;) {
|
|
+ prepare_to_wait(&dev_priv->pending_flip_queue,
|
|
+ &wait, TASK_INTERRUPTIBLE);
|
|
+ for (i = 0; i < count; i++) {
|
|
+ obj_priv = object_list[i]->driver_private;
|
|
+ if (atomic_read(&obj_priv->pending_flip) > 0)
|
|
+ break;
|
|
+ }
|
|
+ if (i == count)
|
|
+ break;
|
|
+
|
|
+ if (!signal_pending(current)) {
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+ schedule();
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
+ continue;
|
|
+ }
|
|
+ ret = -ERESTARTSYS;
|
|
+ break;
|
|
+ }
|
|
+ finish_wait(&dev_priv->pending_flip_queue, &wait);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
int
|
|
-i915_gem_execbuffer(struct drm_device *dev, void *data,
|
|
- struct drm_file *file_priv)
|
|
+i915_gem_do_execbuffer(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv,
|
|
+ struct drm_i915_gem_execbuffer2 *args,
|
|
+ struct drm_i915_gem_exec_object2 *exec_list)
|
|
{
|
|
drm_i915_private_t *dev_priv = dev->dev_private;
|
|
- struct drm_i915_gem_execbuffer *args = 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;
|
|
struct drm_clip_rect *cliprects = NULL;
|
|
- struct drm_i915_gem_relocation_entry *relocs;
|
|
- int ret, ret2, i, pinned = 0;
|
|
+ struct drm_i915_gem_relocation_entry *relocs = NULL;
|
|
+ int ret = 0, ret2, i, pinned = 0;
|
|
uint64_t exec_offset;
|
|
uint32_t seqno, flush_domains, reloc_index;
|
|
- int pin_tries;
|
|
+ int pin_tries, flips;
|
|
|
|
#if WATCH_EXEC
|
|
DRM_INFO("buffers_ptr %d buffer_count %d len %08x\n",
|
|
@@ -3530,31 +3677,21 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
|
|
DRM_ERROR("execbuf with %d buffers\n", args->buffer_count);
|
|
return -EINVAL;
|
|
}
|
|
- /* Copy in the exec list from userland */
|
|
- exec_list = drm_calloc_large(sizeof(*exec_list), args->buffer_count);
|
|
- object_list = drm_calloc_large(sizeof(*object_list), args->buffer_count);
|
|
- if (exec_list == NULL || object_list == NULL) {
|
|
- DRM_ERROR("Failed to allocate exec or object list "
|
|
- "for %d buffers\n",
|
|
+ object_list = drm_malloc_ab(sizeof(*object_list), args->buffer_count);
|
|
+ if (object_list == NULL) {
|
|
+ DRM_ERROR("Failed to allocate object list for %d buffers\n",
|
|
args->buffer_count);
|
|
ret = -ENOMEM;
|
|
goto pre_mutex_err;
|
|
}
|
|
- ret = copy_from_user(exec_list,
|
|
- (struct drm_i915_relocation_entry __user *)
|
|
- (uintptr_t) args->buffers_ptr,
|
|
- sizeof(*exec_list) * args->buffer_count);
|
|
- if (ret != 0) {
|
|
- DRM_ERROR("copy %d exec entries failed %d\n",
|
|
- args->buffer_count, ret);
|
|
- goto pre_mutex_err;
|
|
- }
|
|
|
|
if (args->num_cliprects != 0) {
|
|
cliprects = kcalloc(args->num_cliprects, sizeof(*cliprects),
|
|
GFP_KERNEL);
|
|
- if (cliprects == NULL)
|
|
+ if (cliprects == NULL) {
|
|
+ ret = -ENOMEM;
|
|
goto pre_mutex_err;
|
|
+ }
|
|
|
|
ret = copy_from_user(cliprects,
|
|
(struct drm_clip_rect __user *)
|
|
@@ -3577,26 +3714,27 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
|
|
i915_verify_inactive(dev, __FILE__, __LINE__);
|
|
|
|
if (atomic_read(&dev_priv->mm.wedged)) {
|
|
- DRM_ERROR("Execbuf while wedged\n");
|
|
mutex_unlock(&dev->struct_mutex);
|
|
ret = -EIO;
|
|
goto pre_mutex_err;
|
|
}
|
|
|
|
if (dev_priv->mm.suspended) {
|
|
- DRM_ERROR("Execbuf while VT-switched.\n");
|
|
mutex_unlock(&dev->struct_mutex);
|
|
ret = -EBUSY;
|
|
goto pre_mutex_err;
|
|
}
|
|
|
|
/* Look up object handles */
|
|
+ flips = 0;
|
|
for (i = 0; i < args->buffer_count; i++) {
|
|
object_list[i] = drm_gem_object_lookup(dev, file_priv,
|
|
exec_list[i].handle);
|
|
if (object_list[i] == NULL) {
|
|
DRM_ERROR("Invalid object handle %d at index %d\n",
|
|
exec_list[i].handle, i);
|
|
+ /* prevent error path from reading uninitialized data */
|
|
+ args->buffer_count = i + 1;
|
|
ret = -EBADF;
|
|
goto err;
|
|
}
|
|
@@ -3605,10 +3743,20 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
|
|
if (obj_priv->in_execbuffer) {
|
|
DRM_ERROR("Object %p appears more than once in object list\n",
|
|
object_list[i]);
|
|
+ /* prevent error path from reading uninitialized data */
|
|
+ args->buffer_count = i + 1;
|
|
ret = -EBADF;
|
|
goto err;
|
|
}
|
|
obj_priv->in_execbuffer = true;
|
|
+ flips += atomic_read(&obj_priv->pending_flip);
|
|
+ }
|
|
+
|
|
+ if (flips > 0) {
|
|
+ ret = i915_gem_wait_for_pending_flip(dev, object_list,
|
|
+ args->buffer_count);
|
|
+ if (ret)
|
|
+ goto err;
|
|
}
|
|
|
|
/* Pin and relocate */
|
|
@@ -3710,16 +3858,23 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
|
|
i915_gem_flush(dev,
|
|
dev->invalidate_domains,
|
|
dev->flush_domains);
|
|
- if (dev->flush_domains)
|
|
+ if (dev->flush_domains & I915_GEM_GPU_DOMAINS)
|
|
(void)i915_add_request(dev, file_priv,
|
|
dev->flush_domains);
|
|
}
|
|
|
|
for (i = 0; i < args->buffer_count; i++) {
|
|
struct drm_gem_object *obj = object_list[i];
|
|
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
|
|
uint32_t old_write_domain = obj->write_domain;
|
|
|
|
obj->write_domain = obj->pending_write_domain;
|
|
+ if (obj->write_domain)
|
|
+ list_move_tail(&obj_priv->gpu_write_list,
|
|
+ &dev_priv->mm.gpu_write_list);
|
|
+ else
|
|
+ list_del_init(&obj_priv->gpu_write_list);
|
|
+
|
|
trace_i915_gem_object_change_domain(obj,
|
|
obj->read_domains,
|
|
old_write_domain);
|
|
@@ -3793,8 +3948,101 @@ err:
|
|
|
|
mutex_unlock(&dev->struct_mutex);
|
|
|
|
+pre_mutex_err:
|
|
+ /* Copy the updated relocations out regardless of current error
|
|
+ * state. Failure to update the relocs would mean that the next
|
|
+ * time userland calls execbuf, it would do so with presumed offset
|
|
+ * state that didn't match the actual object state.
|
|
+ */
|
|
+ ret2 = i915_gem_put_relocs_to_user(exec_list, args->buffer_count,
|
|
+ relocs);
|
|
+ if (ret2 != 0) {
|
|
+ DRM_ERROR("Failed to copy relocations back out: %d\n", ret2);
|
|
+
|
|
+ if (ret == 0)
|
|
+ ret = ret2;
|
|
+ }
|
|
+
|
|
+ drm_free_large(object_list);
|
|
+ kfree(cliprects);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Legacy execbuffer just creates an exec2 list from the original exec object
|
|
+ * list array and passes it to the real function.
|
|
+ */
|
|
+int
|
|
+i915_gem_execbuffer(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_i915_gem_execbuffer *args = data;
|
|
+ struct drm_i915_gem_execbuffer2 exec2;
|
|
+ struct drm_i915_gem_exec_object *exec_list = NULL;
|
|
+ struct drm_i915_gem_exec_object2 *exec2_list = NULL;
|
|
+ int ret, i;
|
|
+
|
|
+#if WATCH_EXEC
|
|
+ DRM_INFO("buffers_ptr %d buffer_count %d len %08x\n",
|
|
+ (int) args->buffers_ptr, args->buffer_count, args->batch_len);
|
|
+#endif
|
|
+
|
|
+ if (args->buffer_count < 1) {
|
|
+ DRM_ERROR("execbuf with %d buffers\n", args->buffer_count);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* Copy in the exec list from userland */
|
|
+ exec_list = drm_malloc_ab(sizeof(*exec_list), args->buffer_count);
|
|
+ exec2_list = drm_malloc_ab(sizeof(*exec2_list), args->buffer_count);
|
|
+ if (exec_list == NULL || exec2_list == NULL) {
|
|
+ DRM_ERROR("Failed to allocate exec list for %d buffers\n",
|
|
+ args->buffer_count);
|
|
+ drm_free_large(exec_list);
|
|
+ drm_free_large(exec2_list);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ ret = copy_from_user(exec_list,
|
|
+ (struct drm_i915_relocation_entry __user *)
|
|
+ (uintptr_t) args->buffers_ptr,
|
|
+ sizeof(*exec_list) * args->buffer_count);
|
|
+ if (ret != 0) {
|
|
+ DRM_ERROR("copy %d exec entries failed %d\n",
|
|
+ args->buffer_count, ret);
|
|
+ drm_free_large(exec_list);
|
|
+ drm_free_large(exec2_list);
|
|
+ return -EFAULT;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < args->buffer_count; i++) {
|
|
+ exec2_list[i].handle = exec_list[i].handle;
|
|
+ exec2_list[i].relocation_count = exec_list[i].relocation_count;
|
|
+ exec2_list[i].relocs_ptr = exec_list[i].relocs_ptr;
|
|
+ exec2_list[i].alignment = exec_list[i].alignment;
|
|
+ exec2_list[i].offset = exec_list[i].offset;
|
|
+ if (!IS_I965G(dev))
|
|
+ exec2_list[i].flags = EXEC_OBJECT_NEEDS_FENCE;
|
|
+ else
|
|
+ exec2_list[i].flags = 0;
|
|
+ }
|
|
+
|
|
+ exec2.buffers_ptr = args->buffers_ptr;
|
|
+ exec2.buffer_count = args->buffer_count;
|
|
+ exec2.batch_start_offset = args->batch_start_offset;
|
|
+ exec2.batch_len = args->batch_len;
|
|
+ exec2.DR1 = args->DR1;
|
|
+ exec2.DR4 = args->DR4;
|
|
+ exec2.num_cliprects = args->num_cliprects;
|
|
+ exec2.cliprects_ptr = args->cliprects_ptr;
|
|
+ exec2.flags = 0;
|
|
+
|
|
+ ret = i915_gem_do_execbuffer(dev, data, file_priv, &exec2, exec2_list);
|
|
if (!ret) {
|
|
/* Copy the new buffer offsets back to the user's exec list. */
|
|
+ for (i = 0; i < args->buffer_count; i++)
|
|
+ exec_list[i].offset = exec2_list[i].offset;
|
|
+ /* ... and back out to userspace */
|
|
ret = copy_to_user((struct drm_i915_relocation_entry __user *)
|
|
(uintptr_t) args->buffers_ptr,
|
|
exec_list,
|
|
@@ -3807,25 +4055,62 @@ err:
|
|
}
|
|
}
|
|
|
|
- /* Copy the updated relocations out regardless of current error
|
|
- * state. Failure to update the relocs would mean that the next
|
|
- * time userland calls execbuf, it would do so with presumed offset
|
|
- * state that didn't match the actual object state.
|
|
- */
|
|
- ret2 = i915_gem_put_relocs_to_user(exec_list, args->buffer_count,
|
|
- relocs);
|
|
- if (ret2 != 0) {
|
|
- DRM_ERROR("Failed to copy relocations back out: %d\n", ret2);
|
|
+ drm_free_large(exec_list);
|
|
+ drm_free_large(exec2_list);
|
|
+ return ret;
|
|
+}
|
|
|
|
- if (ret == 0)
|
|
- ret = ret2;
|
|
+int
|
|
+i915_gem_execbuffer2(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_i915_gem_execbuffer2 *args = data;
|
|
+ struct drm_i915_gem_exec_object2 *exec2_list = NULL;
|
|
+ int ret;
|
|
+
|
|
+#if WATCH_EXEC
|
|
+ DRM_INFO("buffers_ptr %d buffer_count %d len %08x\n",
|
|
+ (int) args->buffers_ptr, args->buffer_count, args->batch_len);
|
|
+#endif
|
|
+
|
|
+ if (args->buffer_count < 1) {
|
|
+ DRM_ERROR("execbuf2 with %d buffers\n", args->buffer_count);
|
|
+ return -EINVAL;
|
|
}
|
|
|
|
-pre_mutex_err:
|
|
- drm_free_large(object_list);
|
|
- drm_free_large(exec_list);
|
|
- kfree(cliprects);
|
|
+ exec2_list = drm_malloc_ab(sizeof(*exec2_list), args->buffer_count);
|
|
+ if (exec2_list == NULL) {
|
|
+ DRM_ERROR("Failed to allocate exec list for %d buffers\n",
|
|
+ args->buffer_count);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ ret = copy_from_user(exec2_list,
|
|
+ (struct drm_i915_relocation_entry __user *)
|
|
+ (uintptr_t) args->buffers_ptr,
|
|
+ sizeof(*exec2_list) * args->buffer_count);
|
|
+ if (ret != 0) {
|
|
+ DRM_ERROR("copy %d exec entries failed %d\n",
|
|
+ args->buffer_count, ret);
|
|
+ drm_free_large(exec2_list);
|
|
+ return -EFAULT;
|
|
+ }
|
|
|
|
+ ret = i915_gem_do_execbuffer(dev, data, file_priv, args, exec2_list);
|
|
+ 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,
|
|
+ exec2_list,
|
|
+ sizeof(*exec2_list) * args->buffer_count);
|
|
+ if (ret) {
|
|
+ ret = -EFAULT;
|
|
+ DRM_ERROR("failed to copy %d exec entries "
|
|
+ "back to user (%d)\n",
|
|
+ args->buffer_count, ret);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ drm_free_large(exec2_list);
|
|
return ret;
|
|
}
|
|
|
|
@@ -3842,19 +4127,7 @@ i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment)
|
|
if (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->tiling_mode != I915_TILING_NONE) {
|
|
- ret = i915_gem_object_get_fence_reg(obj);
|
|
- if (ret != 0) {
|
|
- if (ret != -EBUSY && ret != -ERESTARTSYS)
|
|
- DRM_ERROR("Failure to install fence: %d\n",
|
|
- ret);
|
|
- return ret;
|
|
- }
|
|
- }
|
|
+
|
|
obj_priv->pin_count++;
|
|
|
|
/* If the object is not active and not pending a flush,
|
|
@@ -4112,6 +4385,7 @@ int i915_gem_init_object(struct drm_gem_object *obj)
|
|
obj_priv->obj = obj;
|
|
obj_priv->fence_reg = I915_FENCE_REG_NONE;
|
|
INIT_LIST_HEAD(&obj_priv->list);
|
|
+ INIT_LIST_HEAD(&obj_priv->gpu_write_list);
|
|
INIT_LIST_HEAD(&obj_priv->fence_list);
|
|
obj_priv->madv = I915_MADV_WILLNEED;
|
|
|
|
@@ -4335,7 +4609,7 @@ i915_gem_init_hws(struct drm_device *dev)
|
|
memset(dev_priv->hw_status_page, 0, PAGE_SIZE);
|
|
I915_WRITE(HWS_PGA, dev_priv->status_gfx_addr);
|
|
I915_READ(HWS_PGA); /* posting read */
|
|
- DRM_DEBUG("hws offset: 0x%08x\n", dev_priv->status_gfx_addr);
|
|
+ DRM_DEBUG_DRIVER("hws offset: 0x%08x\n", dev_priv->status_gfx_addr);
|
|
|
|
return 0;
|
|
}
|
|
@@ -4563,6 +4837,7 @@ i915_gem_load(struct drm_device *dev)
|
|
spin_lock_init(&dev_priv->mm.active_list_lock);
|
|
INIT_LIST_HEAD(&dev_priv->mm.active_list);
|
|
INIT_LIST_HEAD(&dev_priv->mm.flushing_list);
|
|
+ INIT_LIST_HEAD(&dev_priv->mm.gpu_write_list);
|
|
INIT_LIST_HEAD(&dev_priv->mm.inactive_list);
|
|
INIT_LIST_HEAD(&dev_priv->mm.request_list);
|
|
INIT_LIST_HEAD(&dev_priv->mm.fence_list);
|
|
@@ -4593,8 +4868,8 @@ i915_gem_load(struct drm_device *dev)
|
|
for (i = 0; i < 8; i++)
|
|
I915_WRITE(FENCE_REG_945_8 + (i * 4), 0);
|
|
}
|
|
-
|
|
i915_gem_detect_bit_6_swizzle(dev);
|
|
+ init_waitqueue_head(&dev_priv->pending_flip_queue);
|
|
}
|
|
|
|
/*
|
|
@@ -4769,7 +5044,7 @@ i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *obj,
|
|
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);
|
|
+ DRM_DEBUG_DRIVER("obj_addr %p, %lld\n", obj_addr, args->size);
|
|
ret = copy_from_user(obj_addr, user_data, args->size);
|
|
if (ret)
|
|
return -EFAULT;
|
|
diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c
|
|
index 200e398..df278b2 100644
|
|
--- a/drivers/gpu/drm/i915/i915_gem_tiling.c
|
|
+++ b/drivers/gpu/drm/i915/i915_gem_tiling.c
|
|
@@ -121,7 +121,7 @@ intel_alloc_mchbar_resource(struct drm_device *dev)
|
|
0, pcibios_align_resource,
|
|
dev_priv->bridge_dev);
|
|
if (ret) {
|
|
- DRM_DEBUG("failed bus alloc: %d\n", ret);
|
|
+ DRM_DEBUG_DRIVER("failed bus alloc: %d\n", ret);
|
|
dev_priv->mch_res.start = 0;
|
|
goto out;
|
|
}
|
|
@@ -209,8 +209,8 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
|
|
uint32_t swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN;
|
|
bool need_disable;
|
|
|
|
- if (IS_IGDNG(dev)) {
|
|
- /* On IGDNG whatever DRAM config, GPU always do
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
+ /* On Ironlake whatever DRAM config, GPU always do
|
|
* same swizzling setup.
|
|
*/
|
|
swizzle_x = I915_BIT_6_SWIZZLE_9_10;
|
|
@@ -304,35 +304,39 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
|
|
|
|
|
|
/**
|
|
- * Returns the size of the fence for a tiled object of the given size.
|
|
+ * Returns whether an object is currently fenceable. If not, it may need
|
|
+ * to be unbound and have its pitch adjusted.
|
|
*/
|
|
-static int
|
|
-i915_get_fence_size(struct drm_device *dev, int size)
|
|
+bool
|
|
+i915_obj_fenceable(struct drm_device *dev, struct drm_gem_object *obj)
|
|
{
|
|
- int i;
|
|
- int start;
|
|
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
|
|
|
|
if (IS_I965G(dev)) {
|
|
/* The 965 can have fences at any page boundary. */
|
|
- return ALIGN(size, 4096);
|
|
+ if (obj->size & 4095)
|
|
+ return false;
|
|
+ return true;
|
|
+ } else if (IS_I9XX(dev)) {
|
|
+ if (obj_priv->gtt_offset & ~I915_FENCE_START_MASK)
|
|
+ return false;
|
|
} 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;
|
|
+ if (obj_priv->gtt_offset & ~I830_FENCE_START_MASK)
|
|
+ return false;
|
|
+ }
|
|
|
|
- for (i = start; i < size; i <<= 1)
|
|
- ;
|
|
+ /* Power of two sized... */
|
|
+ if (obj->size & (obj->size - 1))
|
|
+ return false;
|
|
|
|
- return i;
|
|
- }
|
|
+ /* Objects must be size aligned as well */
|
|
+ if (obj_priv->gtt_offset & (obj->size - 1))
|
|
+ return false;
|
|
+ return true;
|
|
}
|
|
|
|
/* Check pitch constriants for all chips & tiling formats */
|
|
-static bool
|
|
+bool
|
|
i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode)
|
|
{
|
|
int tile_width;
|
|
@@ -384,12 +388,6 @@ i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode)
|
|
if (stride & (stride - 1))
|
|
return false;
|
|
|
|
- /* We don't 0handle 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;
|
|
}
|
|
|
|
diff --git a/drivers/gpu/drm/i915/i915_ioc32.c b/drivers/gpu/drm/i915/i915_ioc32.c
|
|
index 1fe68a2..13b0289 100644
|
|
--- a/drivers/gpu/drm/i915/i915_ioc32.c
|
|
+++ b/drivers/gpu/drm/i915/i915_ioc32.c
|
|
@@ -66,8 +66,7 @@ static int compat_i915_batchbuffer(struct file *file, unsigned int cmd,
|
|
&batchbuffer->cliprects))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_I915_BATCHBUFFER,
|
|
+ return drm_ioctl(file, DRM_IOCTL_I915_BATCHBUFFER,
|
|
(unsigned long)batchbuffer);
|
|
}
|
|
|
|
@@ -102,8 +101,8 @@ static int compat_i915_cmdbuffer(struct file *file, unsigned int cmd,
|
|
&cmdbuffer->cliprects))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_I915_CMDBUFFER, (unsigned long)cmdbuffer);
|
|
+ return drm_ioctl(file, DRM_IOCTL_I915_CMDBUFFER,
|
|
+ (unsigned long)cmdbuffer);
|
|
}
|
|
|
|
typedef struct drm_i915_irq_emit32 {
|
|
@@ -125,8 +124,8 @@ static int compat_i915_irq_emit(struct file *file, unsigned int cmd,
|
|
&request->irq_seq))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_I915_IRQ_EMIT, (unsigned long)request);
|
|
+ return drm_ioctl(file, DRM_IOCTL_I915_IRQ_EMIT,
|
|
+ (unsigned long)request);
|
|
}
|
|
typedef struct drm_i915_getparam32 {
|
|
int param;
|
|
@@ -149,8 +148,8 @@ static int compat_i915_getparam(struct file *file, unsigned int cmd,
|
|
&request->value))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_I915_GETPARAM, (unsigned long)request);
|
|
+ return drm_ioctl(file, DRM_IOCTL_I915_GETPARAM,
|
|
+ (unsigned long)request);
|
|
}
|
|
|
|
typedef struct drm_i915_mem_alloc32 {
|
|
@@ -178,8 +177,8 @@ static int compat_i915_alloc(struct file *file, unsigned int cmd,
|
|
&request->region_offset))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_I915_ALLOC, (unsigned long)request);
|
|
+ return drm_ioctl(file, DRM_IOCTL_I915_ALLOC,
|
|
+ (unsigned long)request);
|
|
}
|
|
|
|
drm_ioctl_compat_t *i915_compat_ioctls[] = {
|
|
@@ -211,12 +210,10 @@ long i915_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|
if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(i915_compat_ioctls))
|
|
fn = i915_compat_ioctls[nr - DRM_COMMAND_BASE];
|
|
|
|
- lock_kernel(); /* XXX for now */
|
|
if (fn != NULL)
|
|
ret = (*fn) (filp, cmd, arg);
|
|
else
|
|
- ret = drm_ioctl(filp->f_path.dentry->d_inode, filp, cmd, arg);
|
|
- unlock_kernel();
|
|
+ ret = drm_ioctl(filp, cmd, arg);
|
|
|
|
return ret;
|
|
}
|
|
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
|
|
index 63f28ad..a17d6bd 100644
|
|
--- a/drivers/gpu/drm/i915/i915_irq.c
|
|
+++ b/drivers/gpu/drm/i915/i915_irq.c
|
|
@@ -43,10 +43,13 @@
|
|
* we leave them always unmasked in IMR and then control enabling them through
|
|
* PIPESTAT alone.
|
|
*/
|
|
-#define I915_INTERRUPT_ENABLE_FIX (I915_ASLE_INTERRUPT | \
|
|
- I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \
|
|
- I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | \
|
|
- I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
|
|
+#define I915_INTERRUPT_ENABLE_FIX \
|
|
+ (I915_ASLE_INTERRUPT | \
|
|
+ I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \
|
|
+ I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | \
|
|
+ I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | \
|
|
+ I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT | \
|
|
+ I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
|
|
|
|
/** Interrupts that we mask and unmask at runtime. */
|
|
#define I915_INTERRUPT_ENABLE_VAR (I915_USER_INTERRUPT)
|
|
@@ -61,7 +64,7 @@
|
|
DRM_I915_VBLANK_PIPE_B)
|
|
|
|
void
|
|
-igdng_enable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask)
|
|
+ironlake_enable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask)
|
|
{
|
|
if ((dev_priv->gt_irq_mask_reg & mask) != 0) {
|
|
dev_priv->gt_irq_mask_reg &= ~mask;
|
|
@@ -71,7 +74,7 @@ igdng_enable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask)
|
|
}
|
|
|
|
static inline void
|
|
-igdng_disable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask)
|
|
+ironlake_disable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask)
|
|
{
|
|
if ((dev_priv->gt_irq_mask_reg & mask) != mask) {
|
|
dev_priv->gt_irq_mask_reg |= mask;
|
|
@@ -82,7 +85,7 @@ igdng_disable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask)
|
|
|
|
/* For display hotplug interrupt */
|
|
void
|
|
-igdng_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
|
|
+ironlake_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
|
|
{
|
|
if ((dev_priv->irq_mask_reg & mask) != 0) {
|
|
dev_priv->irq_mask_reg &= ~mask;
|
|
@@ -92,7 +95,7 @@ igdng_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
|
|
}
|
|
|
|
static inline void
|
|
-igdng_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
|
|
+ironlake_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
|
|
{
|
|
if ((dev_priv->irq_mask_reg & mask) != mask) {
|
|
dev_priv->irq_mask_reg |= mask;
|
|
@@ -157,6 +160,20 @@ i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask)
|
|
}
|
|
|
|
/**
|
|
+ * intel_enable_asle - enable ASLE interrupt for OpRegion
|
|
+ */
|
|
+void intel_enable_asle (struct drm_device *dev)
|
|
+{
|
|
+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
|
+
|
|
+ if (IS_IRONLAKE(dev))
|
|
+ ironlake_enable_display_irq(dev_priv, DE_GSE);
|
|
+ else
|
|
+ i915_enable_pipestat(dev_priv, 1,
|
|
+ I915_LEGACY_BLC_EVENT_ENABLE);
|
|
+}
|
|
+
|
|
+/**
|
|
* i915_pipe_enabled - check if a pipe is enabled
|
|
* @dev: DRM device
|
|
* @pipe: pipe to check
|
|
@@ -191,7 +208,8 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int pipe)
|
|
low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL;
|
|
|
|
if (!i915_pipe_enabled(dev, pipe)) {
|
|
- DRM_DEBUG("trying to get vblank count for disabled pipe %d\n", pipe);
|
|
+ DRM_DEBUG_DRIVER("trying to get vblank count for disabled "
|
|
+ "pipe %d\n", pipe);
|
|
return 0;
|
|
}
|
|
|
|
@@ -220,7 +238,8 @@ u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe)
|
|
int reg = pipe ? PIPEB_FRMCOUNT_GM45 : PIPEA_FRMCOUNT_GM45;
|
|
|
|
if (!i915_pipe_enabled(dev, pipe)) {
|
|
- DRM_DEBUG("trying to get vblank count for disabled pipe %d\n", pipe);
|
|
+ DRM_DEBUG_DRIVER("trying to get vblank count for disabled "
|
|
+ "pipe %d\n", pipe);
|
|
return 0;
|
|
}
|
|
|
|
@@ -250,11 +269,11 @@ static void i915_hotplug_work_func(struct work_struct *work)
|
|
drm_sysfs_hotplug_event(dev);
|
|
}
|
|
|
|
-irqreturn_t igdng_irq_handler(struct drm_device *dev)
|
|
+irqreturn_t ironlake_irq_handler(struct drm_device *dev)
|
|
{
|
|
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
|
int ret = IRQ_NONE;
|
|
- u32 de_iir, gt_iir, de_ier;
|
|
+ u32 de_iir, gt_iir, de_ier, pch_iir;
|
|
struct drm_i915_master_private *master_priv;
|
|
|
|
/* disable master interrupt before clearing iir */
|
|
@@ -264,8 +283,9 @@ irqreturn_t igdng_irq_handler(struct drm_device *dev)
|
|
|
|
de_iir = I915_READ(DEIIR);
|
|
gt_iir = I915_READ(GTIIR);
|
|
+ pch_iir = I915_READ(SDEIIR);
|
|
|
|
- if (de_iir == 0 && gt_iir == 0)
|
|
+ if (de_iir == 0 && gt_iir == 0 && pch_iir == 0)
|
|
goto done;
|
|
|
|
ret = IRQ_HANDLED;
|
|
@@ -286,6 +306,33 @@ irqreturn_t igdng_irq_handler(struct drm_device *dev)
|
|
mod_timer(&dev_priv->hangcheck_timer, jiffies + DRM_I915_HANGCHECK_PERIOD);
|
|
}
|
|
|
|
+ if (de_iir & DE_GSE)
|
|
+ ironlake_opregion_gse_intr(dev);
|
|
+
|
|
+ if (de_iir & DE_PLANEA_FLIP_DONE) {
|
|
+ intel_prepare_page_flip(dev, 0);
|
|
+ intel_finish_page_flip(dev, 0);
|
|
+ }
|
|
+
|
|
+ if (de_iir & DE_PLANEB_FLIP_DONE) {
|
|
+ intel_prepare_page_flip(dev, 1);
|
|
+ intel_finish_page_flip(dev, 1);
|
|
+ }
|
|
+
|
|
+ if (de_iir & DE_PIPEA_VBLANK)
|
|
+ drm_handle_vblank(dev, 0);
|
|
+
|
|
+ if (de_iir & DE_PIPEB_VBLANK)
|
|
+ drm_handle_vblank(dev, 1);
|
|
+
|
|
+ /* check event from PCH */
|
|
+ if ((de_iir & DE_PCH_EVENT) &&
|
|
+ (pch_iir & SDE_HOTPLUG_MASK)) {
|
|
+ queue_work(dev_priv->wq, &dev_priv->hotplug_work);
|
|
+ }
|
|
+
|
|
+ /* should clear PCH hotplug event before clear CPU irq */
|
|
+ I915_WRITE(SDEIIR, pch_iir);
|
|
I915_WRITE(GTIIR, gt_iir);
|
|
I915_WRITE(DEIIR, de_iir);
|
|
|
|
@@ -312,19 +359,19 @@ static void i915_error_work_func(struct work_struct *work)
|
|
char *reset_event[] = { "RESET=1", NULL };
|
|
char *reset_done_event[] = { "ERROR=0", NULL };
|
|
|
|
- DRM_DEBUG("generating error event\n");
|
|
+ DRM_DEBUG_DRIVER("generating error event\n");
|
|
kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event);
|
|
|
|
if (atomic_read(&dev_priv->mm.wedged)) {
|
|
if (IS_I965G(dev)) {
|
|
- DRM_DEBUG("resetting chip\n");
|
|
+ DRM_DEBUG_DRIVER("resetting chip\n");
|
|
kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event);
|
|
if (!i965_reset(dev, GDRST_RENDER)) {
|
|
atomic_set(&dev_priv->mm.wedged, 0);
|
|
kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event);
|
|
}
|
|
} else {
|
|
- printk("reboot required\n");
|
|
+ DRM_DEBUG_DRIVER("reboot required\n");
|
|
}
|
|
}
|
|
}
|
|
@@ -350,7 +397,7 @@ static void i915_capture_error_state(struct drm_device *dev)
|
|
|
|
error = kmalloc(sizeof(*error), GFP_ATOMIC);
|
|
if (!error) {
|
|
- DRM_DEBUG("out ot memory, not capturing error state\n");
|
|
+ DRM_DEBUG_DRIVER("out ot memory, not capturing error state\n");
|
|
goto out;
|
|
}
|
|
|
|
@@ -507,7 +554,6 @@ static void i915_handle_error(struct drm_device *dev, bool wedged)
|
|
/*
|
|
* Wakeup waiting processes so they don't hang
|
|
*/
|
|
- printk("i915: Waking up sleeping processes\n");
|
|
DRM_WAKEUP(&dev_priv->irq_queue);
|
|
}
|
|
|
|
@@ -530,8 +576,8 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
|
|
|
|
atomic_inc(&dev_priv->irq_received);
|
|
|
|
- if (IS_IGDNG(dev))
|
|
- return igdng_irq_handler(dev);
|
|
+ if (IS_IRONLAKE(dev))
|
|
+ return ironlake_irq_handler(dev);
|
|
|
|
iir = I915_READ(IIR);
|
|
|
|
@@ -563,14 +609,14 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
|
|
*/
|
|
if (pipea_stats & 0x8000ffff) {
|
|
if (pipea_stats & PIPE_FIFO_UNDERRUN_STATUS)
|
|
- DRM_DEBUG("pipe a underrun\n");
|
|
+ DRM_DEBUG_DRIVER("pipe a underrun\n");
|
|
I915_WRITE(PIPEASTAT, pipea_stats);
|
|
irq_received = 1;
|
|
}
|
|
|
|
if (pipeb_stats & 0x8000ffff) {
|
|
if (pipeb_stats & PIPE_FIFO_UNDERRUN_STATUS)
|
|
- DRM_DEBUG("pipe b underrun\n");
|
|
+ DRM_DEBUG_DRIVER("pipe b underrun\n");
|
|
I915_WRITE(PIPEBSTAT, pipeb_stats);
|
|
irq_received = 1;
|
|
}
|
|
@@ -586,7 +632,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
|
|
(iir & I915_DISPLAY_PORT_INTERRUPT)) {
|
|
u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT);
|
|
|
|
- DRM_DEBUG("hotplug event received, stat 0x%08x\n",
|
|
+ DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n",
|
|
hotplug_status);
|
|
if (hotplug_status & dev_priv->hotplug_supported_mask)
|
|
queue_work(dev_priv->wq,
|
|
@@ -594,27 +640,6 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
|
|
|
|
I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
|
|
I915_READ(PORT_HOTPLUG_STAT);
|
|
-
|
|
- /* EOS interrupts occurs */
|
|
- if (IS_IGD(dev) &&
|
|
- (hotplug_status & CRT_EOS_INT_STATUS)) {
|
|
- u32 temp;
|
|
-
|
|
- DRM_DEBUG("EOS interrupt occurs\n");
|
|
- /* status is already cleared */
|
|
- temp = I915_READ(ADPA);
|
|
- temp &= ~ADPA_DAC_ENABLE;
|
|
- I915_WRITE(ADPA, temp);
|
|
-
|
|
- temp = I915_READ(PORT_HOTPLUG_EN);
|
|
- temp &= ~CRT_EOS_INT_EN;
|
|
- I915_WRITE(PORT_HOTPLUG_EN, temp);
|
|
-
|
|
- temp = I915_READ(PORT_HOTPLUG_STAT);
|
|
- if (temp & CRT_EOS_INT_STATUS)
|
|
- I915_WRITE(PORT_HOTPLUG_STAT,
|
|
- CRT_EOS_INT_STATUS);
|
|
- }
|
|
}
|
|
|
|
I915_WRITE(IIR, iir);
|
|
@@ -636,14 +661,22 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
|
|
mod_timer(&dev_priv->hangcheck_timer, jiffies + DRM_I915_HANGCHECK_PERIOD);
|
|
}
|
|
|
|
+ if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT)
|
|
+ intel_prepare_page_flip(dev, 0);
|
|
+
|
|
+ if (iir & I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT)
|
|
+ intel_prepare_page_flip(dev, 1);
|
|
+
|
|
if (pipea_stats & vblank_status) {
|
|
vblank++;
|
|
drm_handle_vblank(dev, 0);
|
|
+ intel_finish_page_flip(dev, 0);
|
|
}
|
|
|
|
if (pipeb_stats & vblank_status) {
|
|
vblank++;
|
|
drm_handle_vblank(dev, 1);
|
|
+ intel_finish_page_flip(dev, 1);
|
|
}
|
|
|
|
if ((pipeb_stats & I915_LEGACY_BLC_EVENT_STATUS) ||
|
|
@@ -679,7 +712,7 @@ static int i915_emit_irq(struct drm_device * dev)
|
|
|
|
i915_kernel_lost_context(dev);
|
|
|
|
- DRM_DEBUG("\n");
|
|
+ DRM_DEBUG_DRIVER("\n");
|
|
|
|
dev_priv->counter++;
|
|
if (dev_priv->counter > 0x7FFFFFFFUL)
|
|
@@ -704,8 +737,8 @@ void i915_user_irq_get(struct drm_device *dev)
|
|
|
|
spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
|
|
if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1)) {
|
|
- if (IS_IGDNG(dev))
|
|
- igdng_enable_graphics_irq(dev_priv, GT_USER_INTERRUPT);
|
|
+ if (IS_IRONLAKE(dev))
|
|
+ ironlake_enable_graphics_irq(dev_priv, GT_USER_INTERRUPT);
|
|
else
|
|
i915_enable_irq(dev_priv, I915_USER_INTERRUPT);
|
|
}
|
|
@@ -720,8 +753,8 @@ void i915_user_irq_put(struct drm_device *dev)
|
|
spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
|
|
BUG_ON(dev->irq_enabled && dev_priv->user_irq_refcount <= 0);
|
|
if (dev->irq_enabled && (--dev_priv->user_irq_refcount == 0)) {
|
|
- if (IS_IGDNG(dev))
|
|
- igdng_disable_graphics_irq(dev_priv, GT_USER_INTERRUPT);
|
|
+ if (IS_IRONLAKE(dev))
|
|
+ ironlake_disable_graphics_irq(dev_priv, GT_USER_INTERRUPT);
|
|
else
|
|
i915_disable_irq(dev_priv, I915_USER_INTERRUPT);
|
|
}
|
|
@@ -744,7 +777,7 @@ static int i915_wait_irq(struct drm_device * dev, int irq_nr)
|
|
struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
|
|
int ret = 0;
|
|
|
|
- DRM_DEBUG("irq_nr=%d breadcrumb=%d\n", irq_nr,
|
|
+ DRM_DEBUG_DRIVER("irq_nr=%d breadcrumb=%d\n", irq_nr,
|
|
READ_BREADCRUMB(dev_priv));
|
|
|
|
if (READ_BREADCRUMB(dev_priv) >= irq_nr) {
|
|
@@ -827,11 +860,11 @@ int i915_enable_vblank(struct drm_device *dev, int pipe)
|
|
if (!(pipeconf & PIPEACONF_ENABLE))
|
|
return -EINVAL;
|
|
|
|
- if (IS_IGDNG(dev))
|
|
- return 0;
|
|
-
|
|
spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
|
|
- if (IS_I965G(dev))
|
|
+ if (IS_IRONLAKE(dev))
|
|
+ ironlake_enable_display_irq(dev_priv, (pipe == 0) ?
|
|
+ DE_PIPEA_VBLANK: DE_PIPEB_VBLANK);
|
|
+ else if (IS_I965G(dev))
|
|
i915_enable_pipestat(dev_priv, pipe,
|
|
PIPE_START_VBLANK_INTERRUPT_ENABLE);
|
|
else
|
|
@@ -849,13 +882,14 @@ void i915_disable_vblank(struct drm_device *dev, int pipe)
|
|
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
|
unsigned long irqflags;
|
|
|
|
- if (IS_IGDNG(dev))
|
|
- return;
|
|
-
|
|
spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
|
|
- i915_disable_pipestat(dev_priv, pipe,
|
|
- PIPE_VBLANK_INTERRUPT_ENABLE |
|
|
- PIPE_START_VBLANK_INTERRUPT_ENABLE);
|
|
+ if (IS_IRONLAKE(dev))
|
|
+ ironlake_disable_display_irq(dev_priv, (pipe == 0) ?
|
|
+ DE_PIPEA_VBLANK: DE_PIPEB_VBLANK);
|
|
+ else
|
|
+ i915_disable_pipestat(dev_priv, pipe,
|
|
+ PIPE_VBLANK_INTERRUPT_ENABLE |
|
|
+ PIPE_START_VBLANK_INTERRUPT_ENABLE);
|
|
spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
|
|
}
|
|
|
|
@@ -863,7 +897,7 @@ void i915_enable_interrupt (struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
- if (!IS_IGDNG(dev))
|
|
+ if (!IS_IRONLAKE(dev))
|
|
opregion_enable_asle(dev);
|
|
dev_priv->irq_enabled = 1;
|
|
}
|
|
@@ -971,7 +1005,7 @@ void i915_hangcheck_elapsed(unsigned long data)
|
|
|
|
/* drm_dma.h hooks
|
|
*/
|
|
-static void igdng_irq_preinstall(struct drm_device *dev)
|
|
+static void ironlake_irq_preinstall(struct drm_device *dev)
|
|
{
|
|
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
|
|
|
@@ -987,17 +1021,25 @@ static void igdng_irq_preinstall(struct drm_device *dev)
|
|
I915_WRITE(GTIMR, 0xffffffff);
|
|
I915_WRITE(GTIER, 0x0);
|
|
(void) I915_READ(GTIER);
|
|
+
|
|
+ /* south display irq */
|
|
+ I915_WRITE(SDEIMR, 0xffffffff);
|
|
+ I915_WRITE(SDEIER, 0x0);
|
|
+ (void) I915_READ(SDEIER);
|
|
}
|
|
|
|
-static int igdng_irq_postinstall(struct drm_device *dev)
|
|
+static int ironlake_irq_postinstall(struct drm_device *dev)
|
|
{
|
|
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
|
/* enable kind of interrupts always enabled */
|
|
- u32 display_mask = DE_MASTER_IRQ_CONTROL /*| DE_PCH_EVENT */;
|
|
+ u32 display_mask = DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT |
|
|
+ DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE;
|
|
u32 render_mask = GT_USER_INTERRUPT;
|
|
+ u32 hotplug_mask = SDE_CRT_HOTPLUG | SDE_PORTB_HOTPLUG |
|
|
+ SDE_PORTC_HOTPLUG | SDE_PORTD_HOTPLUG;
|
|
|
|
dev_priv->irq_mask_reg = ~display_mask;
|
|
- dev_priv->de_irq_enable_reg = display_mask;
|
|
+ dev_priv->de_irq_enable_reg = display_mask | DE_PIPEA_VBLANK | DE_PIPEB_VBLANK;
|
|
|
|
/* should always can generate irq */
|
|
I915_WRITE(DEIIR, I915_READ(DEIIR));
|
|
@@ -1014,6 +1056,14 @@ static int igdng_irq_postinstall(struct drm_device *dev)
|
|
I915_WRITE(GTIER, dev_priv->gt_irq_enable_reg);
|
|
(void) I915_READ(GTIER);
|
|
|
|
+ dev_priv->pch_irq_mask_reg = ~hotplug_mask;
|
|
+ dev_priv->pch_irq_enable_reg = hotplug_mask;
|
|
+
|
|
+ I915_WRITE(SDEIIR, I915_READ(SDEIIR));
|
|
+ I915_WRITE(SDEIMR, dev_priv->pch_irq_mask_reg);
|
|
+ I915_WRITE(SDEIER, dev_priv->pch_irq_enable_reg);
|
|
+ (void) I915_READ(SDEIER);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
@@ -1026,8 +1076,8 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
|
|
INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
|
|
INIT_WORK(&dev_priv->error_work, i915_error_work_func);
|
|
|
|
- if (IS_IGDNG(dev)) {
|
|
- igdng_irq_preinstall(dev);
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
+ ironlake_irq_preinstall(dev);
|
|
return;
|
|
}
|
|
|
|
@@ -1058,8 +1108,8 @@ int i915_driver_irq_postinstall(struct drm_device *dev)
|
|
|
|
dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
|
|
|
|
- if (IS_IGDNG(dev))
|
|
- return igdng_irq_postinstall(dev);
|
|
+ if (IS_IRONLAKE(dev))
|
|
+ return ironlake_irq_postinstall(dev);
|
|
|
|
/* Unmask the interrupts that we always want on. */
|
|
dev_priv->irq_mask_reg = ~I915_INTERRUPT_ENABLE_FIX;
|
|
@@ -1123,7 +1173,7 @@ int i915_driver_irq_postinstall(struct drm_device *dev)
|
|
return 0;
|
|
}
|
|
|
|
-static void igdng_irq_uninstall(struct drm_device *dev)
|
|
+static void ironlake_irq_uninstall(struct drm_device *dev)
|
|
{
|
|
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
|
I915_WRITE(HWSTAM, 0xffffffff);
|
|
@@ -1146,8 +1196,8 @@ void i915_driver_irq_uninstall(struct drm_device * dev)
|
|
|
|
dev_priv->vblank_pipe = 0;
|
|
|
|
- if (IS_IGDNG(dev)) {
|
|
- igdng_irq_uninstall(dev);
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
+ ironlake_irq_uninstall(dev);
|
|
return;
|
|
}
|
|
|
|
diff --git a/drivers/gpu/drm/i915/i915_opregion.c b/drivers/gpu/drm/i915/i915_opregion.c
|
|
index 2d51935..7cc8410 100644
|
|
--- a/drivers/gpu/drm/i915/i915_opregion.c
|
|
+++ b/drivers/gpu/drm/i915/i915_opregion.c
|
|
@@ -118,6 +118,10 @@ struct opregion_asle {
|
|
#define ASLE_BACKLIGHT_FAIL (2<<12)
|
|
#define ASLE_PFIT_FAIL (2<<14)
|
|
#define ASLE_PWM_FREQ_FAIL (2<<16)
|
|
+#define ASLE_ALS_ILLUM_FAILED (1<<10)
|
|
+#define ASLE_BACKLIGHT_FAILED (1<<12)
|
|
+#define ASLE_PFIT_FAILED (1<<14)
|
|
+#define ASLE_PWM_FREQ_FAILED (1<<16)
|
|
|
|
/* ASLE backlight brightness to set */
|
|
#define ASLE_BCLP_VALID (1<<31)
|
|
@@ -163,7 +167,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
|
|
if (IS_I965G(dev) && (blc_pwm_ctl2 & BLM_COMBINATION_MODE))
|
|
pci_write_config_dword(dev->pdev, PCI_LBPC, bclp);
|
|
else {
|
|
- if (IS_IGD(dev)) {
|
|
+ if (IS_PINEVIEW(dev)) {
|
|
blc_pwm_ctl &= ~(BACKLIGHT_DUTY_CYCLE_MASK - 1);
|
|
max_backlight = (blc_pwm_ctl & BACKLIGHT_MODULATION_FREQ_MASK) >>
|
|
BACKLIGHT_MODULATION_FREQ_SHIFT;
|
|
@@ -224,7 +228,7 @@ void opregion_asle_intr(struct drm_device *dev)
|
|
asle_req = asle->aslc & ASLE_REQ_MSK;
|
|
|
|
if (!asle_req) {
|
|
- DRM_DEBUG("non asle set request??\n");
|
|
+ DRM_DEBUG_DRIVER("non asle set request??\n");
|
|
return;
|
|
}
|
|
|
|
@@ -243,6 +247,73 @@ void opregion_asle_intr(struct drm_device *dev)
|
|
asle->aslc = asle_stat;
|
|
}
|
|
|
|
+static u32 asle_set_backlight_ironlake(struct drm_device *dev, u32 bclp)
|
|
+{
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
+ struct opregion_asle *asle = dev_priv->opregion.asle;
|
|
+ u32 cpu_pwm_ctl, pch_pwm_ctl2;
|
|
+ u32 max_backlight, level;
|
|
+
|
|
+ if (!(bclp & ASLE_BCLP_VALID))
|
|
+ return ASLE_BACKLIGHT_FAILED;
|
|
+
|
|
+ bclp &= ASLE_BCLP_MSK;
|
|
+ if (bclp < 0 || bclp > 255)
|
|
+ return ASLE_BACKLIGHT_FAILED;
|
|
+
|
|
+ cpu_pwm_ctl = I915_READ(BLC_PWM_CPU_CTL);
|
|
+ pch_pwm_ctl2 = I915_READ(BLC_PWM_PCH_CTL2);
|
|
+ /* get the max PWM frequency */
|
|
+ max_backlight = (pch_pwm_ctl2 >> 16) & BACKLIGHT_DUTY_CYCLE_MASK;
|
|
+ /* calculate the expected PMW frequency */
|
|
+ level = (bclp * max_backlight) / 255;
|
|
+ /* reserve the high 16 bits */
|
|
+ cpu_pwm_ctl &= ~(BACKLIGHT_DUTY_CYCLE_MASK);
|
|
+ /* write the updated PWM frequency */
|
|
+ I915_WRITE(BLC_PWM_CPU_CTL, cpu_pwm_ctl | level);
|
|
+
|
|
+ asle->cblv = (bclp*0x64)/0xff | ASLE_CBLV_VALID;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void ironlake_opregion_gse_intr(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
+ struct opregion_asle *asle = dev_priv->opregion.asle;
|
|
+ u32 asle_stat = 0;
|
|
+ u32 asle_req;
|
|
+
|
|
+ if (!asle)
|
|
+ return;
|
|
+
|
|
+ asle_req = asle->aslc & ASLE_REQ_MSK;
|
|
+
|
|
+ if (!asle_req) {
|
|
+ DRM_DEBUG_DRIVER("non asle set request??\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (asle_req & ASLE_SET_ALS_ILLUM) {
|
|
+ DRM_DEBUG_DRIVER("Illum is not supported\n");
|
|
+ asle_stat |= ASLE_ALS_ILLUM_FAILED;
|
|
+ }
|
|
+
|
|
+ if (asle_req & ASLE_SET_BACKLIGHT)
|
|
+ asle_stat |= asle_set_backlight_ironlake(dev, asle->bclp);
|
|
+
|
|
+ if (asle_req & ASLE_SET_PFIT) {
|
|
+ DRM_DEBUG_DRIVER("Pfit is not supported\n");
|
|
+ asle_stat |= ASLE_PFIT_FAILED;
|
|
+ }
|
|
+
|
|
+ if (asle_req & ASLE_SET_PWM_FREQ) {
|
|
+ DRM_DEBUG_DRIVER("PWM freq is not supported\n");
|
|
+ asle_stat |= ASLE_PWM_FREQ_FAILED;
|
|
+ }
|
|
+
|
|
+ asle->aslc = asle_stat;
|
|
+}
|
|
#define ASLE_ALS_EN (1<<0)
|
|
#define ASLE_BLC_EN (1<<1)
|
|
#define ASLE_PFIT_EN (1<<2)
|
|
@@ -258,8 +329,7 @@ void opregion_enable_asle(struct drm_device *dev)
|
|
unsigned long irqflags;
|
|
|
|
spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
|
|
- i915_enable_pipestat(dev_priv, 1,
|
|
- I915_LEGACY_BLC_EVENT_ENABLE);
|
|
+ intel_enable_asle(dev);
|
|
spin_unlock_irqrestore(&dev_priv->user_irq_lock,
|
|
irqflags);
|
|
}
|
|
@@ -361,9 +431,9 @@ int intel_opregion_init(struct drm_device *dev, int resume)
|
|
int err = 0;
|
|
|
|
pci_read_config_dword(dev->pdev, PCI_ASLS, &asls);
|
|
- DRM_DEBUG("graphic opregion physical addr: 0x%x\n", asls);
|
|
+ DRM_DEBUG_DRIVER("graphic opregion physical addr: 0x%x\n", asls);
|
|
if (asls == 0) {
|
|
- DRM_DEBUG("ACPI OpRegion not supported!\n");
|
|
+ DRM_DEBUG_DRIVER("ACPI OpRegion not supported!\n");
|
|
return -ENOTSUPP;
|
|
}
|
|
|
|
@@ -373,30 +443,30 @@ int intel_opregion_init(struct drm_device *dev, int resume)
|
|
|
|
opregion->header = base;
|
|
if (memcmp(opregion->header->signature, OPREGION_SIGNATURE, 16)) {
|
|
- DRM_DEBUG("opregion signature mismatch\n");
|
|
+ DRM_DEBUG_DRIVER("opregion signature mismatch\n");
|
|
err = -EINVAL;
|
|
goto err_out;
|
|
}
|
|
|
|
mboxes = opregion->header->mboxes;
|
|
if (mboxes & MBOX_ACPI) {
|
|
- DRM_DEBUG("Public ACPI methods supported\n");
|
|
+ DRM_DEBUG_DRIVER("Public ACPI methods supported\n");
|
|
opregion->acpi = base + OPREGION_ACPI_OFFSET;
|
|
if (drm_core_check_feature(dev, DRIVER_MODESET))
|
|
intel_didl_outputs(dev);
|
|
} else {
|
|
- DRM_DEBUG("Public ACPI methods not supported\n");
|
|
+ DRM_DEBUG_DRIVER("Public ACPI methods not supported\n");
|
|
err = -ENOTSUPP;
|
|
goto err_out;
|
|
}
|
|
opregion->enabled = 1;
|
|
|
|
if (mboxes & MBOX_SWSCI) {
|
|
- DRM_DEBUG("SWSCI supported\n");
|
|
+ DRM_DEBUG_DRIVER("SWSCI supported\n");
|
|
opregion->swsci = base + OPREGION_SWSCI_OFFSET;
|
|
}
|
|
if (mboxes & MBOX_ASLE) {
|
|
- DRM_DEBUG("ASLE supported\n");
|
|
+ DRM_DEBUG_DRIVER("ASLE supported\n");
|
|
opregion->asle = base + OPREGION_ASLE_OFFSET;
|
|
opregion_enable_asle(dev);
|
|
}
|
|
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
|
|
index fd537f4..ab1bd2d 100644
|
|
--- a/drivers/gpu/drm/i915/i915_reg.h
|
|
+++ b/drivers/gpu/drm/i915/i915_reg.h
|
|
@@ -140,6 +140,7 @@
|
|
#define MI_NOOP MI_INSTR(0, 0)
|
|
#define MI_USER_INTERRUPT MI_INSTR(0x02, 0)
|
|
#define MI_WAIT_FOR_EVENT MI_INSTR(0x03, 0)
|
|
+#define MI_WAIT_FOR_OVERLAY_FLIP (1<<16)
|
|
#define MI_WAIT_FOR_PLANE_B_FLIP (1<<6)
|
|
#define MI_WAIT_FOR_PLANE_A_FLIP (1<<2)
|
|
#define MI_WAIT_FOR_PLANE_A_SCANLINES (1<<1)
|
|
@@ -151,7 +152,13 @@
|
|
#define MI_END_SCENE (1 << 4) /* flush binner and incr scene count */
|
|
#define MI_BATCH_BUFFER_END MI_INSTR(0x0a, 0)
|
|
#define MI_REPORT_HEAD MI_INSTR(0x07, 0)
|
|
+#define MI_OVERLAY_FLIP MI_INSTR(0x11,0)
|
|
+#define MI_OVERLAY_CONTINUE (0x0<<21)
|
|
+#define MI_OVERLAY_ON (0x1<<21)
|
|
+#define MI_OVERLAY_OFF (0x2<<21)
|
|
#define MI_LOAD_SCAN_LINES_INCL MI_INSTR(0x12, 0)
|
|
+#define MI_DISPLAY_FLIP MI_INSTR(0x14, 2)
|
|
+#define MI_DISPLAY_FLIP_PLANE(n) ((n) << 20)
|
|
#define MI_STORE_DWORD_IMM MI_INSTR(0x20, 1)
|
|
#define MI_MEM_VIRTUAL (1 << 22) /* 965+ only */
|
|
#define MI_STORE_DWORD_INDEX MI_INSTR(0x21, 1)
|
|
@@ -260,6 +267,8 @@
|
|
#define HWS_PGA 0x02080
|
|
#define HWS_ADDRESS_MASK 0xfffff000
|
|
#define HWS_START_ADDRESS_SHIFT 4
|
|
+#define PWRCTXA 0x2088 /* 965GM+ only */
|
|
+#define PWRCTX_EN (1<<0)
|
|
#define IPEIR 0x02088
|
|
#define IPEHR 0x0208c
|
|
#define INSTDONE 0x02090
|
|
@@ -329,6 +338,7 @@
|
|
#define FBC_CTL_PERIODIC (1<<30)
|
|
#define FBC_CTL_INTERVAL_SHIFT (16)
|
|
#define FBC_CTL_UNCOMPRESSIBLE (1<<14)
|
|
+#define FBC_C3_IDLE (1<<13)
|
|
#define FBC_CTL_STRIDE_SHIFT (5)
|
|
#define FBC_CTL_FENCENO (1<<0)
|
|
#define FBC_COMMAND 0x0320c
|
|
@@ -405,6 +415,13 @@
|
|
# define GPIO_DATA_VAL_IN (1 << 12)
|
|
# define GPIO_DATA_PULLUP_DISABLE (1 << 13)
|
|
|
|
+#define GMBUS0 0x5100
|
|
+#define GMBUS1 0x5104
|
|
+#define GMBUS2 0x5108
|
|
+#define GMBUS3 0x510c
|
|
+#define GMBUS4 0x5110
|
|
+#define GMBUS5 0x5120
|
|
+
|
|
/*
|
|
* Clock control & power management
|
|
*/
|
|
@@ -435,7 +452,7 @@
|
|
#define DPLLB_LVDS_P2_CLOCK_DIV_7 (1 << 24) /* i915 */
|
|
#define DPLL_P2_CLOCK_DIV_MASK 0x03000000 /* i915 */
|
|
#define DPLL_FPA01_P1_POST_DIV_MASK 0x00ff0000 /* i915 */
|
|
-#define DPLL_FPA01_P1_POST_DIV_MASK_IGD 0x00ff8000 /* IGD */
|
|
+#define DPLL_FPA01_P1_POST_DIV_MASK_PINEVIEW 0x00ff8000 /* Pineview */
|
|
|
|
#define I915_FIFO_UNDERRUN_STATUS (1UL<<31)
|
|
#define I915_CRC_ERROR_ENABLE (1UL<<29)
|
|
@@ -512,7 +529,7 @@
|
|
*/
|
|
#define DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS 0x003f0000
|
|
#define DPLL_FPA01_P1_POST_DIV_SHIFT 16
|
|
-#define DPLL_FPA01_P1_POST_DIV_SHIFT_IGD 15
|
|
+#define DPLL_FPA01_P1_POST_DIV_SHIFT_PINEVIEW 15
|
|
/* i830, required in DVO non-gang */
|
|
#define PLL_P2_DIVIDE_BY_4 (1 << 23)
|
|
#define PLL_P1_DIVIDE_BY_TWO (1 << 21) /* i830 */
|
|
@@ -522,7 +539,7 @@
|
|
#define PLLB_REF_INPUT_SPREADSPECTRUMIN (3 << 13)
|
|
#define PLL_REF_INPUT_MASK (3 << 13)
|
|
#define PLL_LOAD_PULSE_PHASE_SHIFT 9
|
|
-/* IGDNG */
|
|
+/* Ironlake */
|
|
# define PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT 9
|
|
# define PLL_REF_SDVO_HDMI_MULTIPLIER_MASK (7 << 9)
|
|
# define PLL_REF_SDVO_HDMI_MULTIPLIER(x) (((x)-1) << 9)
|
|
@@ -586,12 +603,12 @@
|
|
#define FPB0 0x06048
|
|
#define FPB1 0x0604c
|
|
#define FP_N_DIV_MASK 0x003f0000
|
|
-#define FP_N_IGD_DIV_MASK 0x00ff0000
|
|
+#define FP_N_PINEVIEW_DIV_MASK 0x00ff0000
|
|
#define FP_N_DIV_SHIFT 16
|
|
#define FP_M1_DIV_MASK 0x00003f00
|
|
#define FP_M1_DIV_SHIFT 8
|
|
#define FP_M2_DIV_MASK 0x0000003f
|
|
-#define FP_M2_IGD_DIV_MASK 0x000000ff
|
|
+#define FP_M2_PINEVIEW_DIV_MASK 0x000000ff
|
|
#define FP_M2_DIV_SHIFT 0
|
|
#define DPLL_TEST 0x606c
|
|
#define DPLLB_TEST_SDVO_DIV_1 (0 << 22)
|
|
@@ -769,7 +786,8 @@
|
|
|
|
/** GM965 GM45 render standby register */
|
|
#define MCHBAR_RENDER_STANDBY 0x111B8
|
|
-
|
|
+#define RCX_SW_EXIT (1<<23)
|
|
+#define RSX_STATUS_MASK 0x00700000
|
|
#define PEG_BAND_GAP_DATA 0x14d68
|
|
|
|
/*
|
|
@@ -844,7 +862,6 @@
|
|
#define SDVOB_HOTPLUG_INT_EN (1 << 26)
|
|
#define SDVOC_HOTPLUG_INT_EN (1 << 25)
|
|
#define TV_HOTPLUG_INT_EN (1 << 18)
|
|
-#define CRT_EOS_INT_EN (1 << 10)
|
|
#define CRT_HOTPLUG_INT_EN (1 << 9)
|
|
#define CRT_HOTPLUG_FORCE_DETECT (1 << 3)
|
|
#define CRT_HOTPLUG_ACTIVATION_PERIOD_32 (0 << 8)
|
|
@@ -871,7 +888,6 @@
|
|
#define DPC_HOTPLUG_INT_STATUS (1 << 28)
|
|
#define HDMID_HOTPLUG_INT_STATUS (1 << 27)
|
|
#define DPD_HOTPLUG_INT_STATUS (1 << 27)
|
|
-#define CRT_EOS_INT_STATUS (1 << 12)
|
|
#define CRT_HOTPLUG_INT_STATUS (1 << 11)
|
|
#define TV_HOTPLUG_INT_STATUS (1 << 10)
|
|
#define CRT_HOTPLUG_MONITOR_MASK (3 << 8)
|
|
@@ -1614,7 +1630,7 @@
|
|
#define DP_CLOCK_OUTPUT_ENABLE (1 << 13)
|
|
|
|
#define DP_SCRAMBLING_DISABLE (1 << 12)
|
|
-#define DP_SCRAMBLING_DISABLE_IGDNG (1 << 7)
|
|
+#define DP_SCRAMBLING_DISABLE_IRONLAKE (1 << 7)
|
|
|
|
/** limit RGB values to avoid confusing TVs */
|
|
#define DP_COLOR_RANGE_16_235 (1 << 8)
|
|
@@ -1800,11 +1816,11 @@
|
|
#define DSPFW_PLANEB_SHIFT 8
|
|
#define DSPFW2 0x70038
|
|
#define DSPFW_CURSORA_MASK 0x00003f00
|
|
-#define DSPFW_CURSORA_SHIFT 16
|
|
+#define DSPFW_CURSORA_SHIFT 8
|
|
#define DSPFW3 0x7003c
|
|
#define DSPFW_HPLL_SR_EN (1<<31)
|
|
#define DSPFW_CURSOR_SR_SHIFT 24
|
|
-#define IGD_SELF_REFRESH_EN (1<<30)
|
|
+#define PINEVIEW_SELF_REFRESH_EN (1<<30)
|
|
|
|
/* FIFO watermark sizes etc */
|
|
#define G4X_FIFO_LINE_SIZE 64
|
|
@@ -1820,16 +1836,16 @@
|
|
#define G4X_MAX_WM 0x3f
|
|
#define I915_MAX_WM 0x3f
|
|
|
|
-#define IGD_DISPLAY_FIFO 512 /* in 64byte unit */
|
|
-#define IGD_FIFO_LINE_SIZE 64
|
|
-#define IGD_MAX_WM 0x1ff
|
|
-#define IGD_DFT_WM 0x3f
|
|
-#define IGD_DFT_HPLLOFF_WM 0
|
|
-#define IGD_GUARD_WM 10
|
|
-#define IGD_CURSOR_FIFO 64
|
|
-#define IGD_CURSOR_MAX_WM 0x3f
|
|
-#define IGD_CURSOR_DFT_WM 0
|
|
-#define IGD_CURSOR_GUARD_WM 5
|
|
+#define PINEVIEW_DISPLAY_FIFO 512 /* in 64byte unit */
|
|
+#define PINEVIEW_FIFO_LINE_SIZE 64
|
|
+#define PINEVIEW_MAX_WM 0x1ff
|
|
+#define PINEVIEW_DFT_WM 0x3f
|
|
+#define PINEVIEW_DFT_HPLLOFF_WM 0
|
|
+#define PINEVIEW_GUARD_WM 10
|
|
+#define PINEVIEW_CURSOR_FIFO 64
|
|
+#define PINEVIEW_CURSOR_MAX_WM 0x3f
|
|
+#define PINEVIEW_CURSOR_DFT_WM 0
|
|
+#define PINEVIEW_CURSOR_GUARD_WM 5
|
|
|
|
/*
|
|
* The two pipe frame counter registers are not synchronized, so
|
|
@@ -1903,6 +1919,7 @@
|
|
#define DISPPLANE_16BPP (0x5<<26)
|
|
#define DISPPLANE_32BPP_NO_ALPHA (0x6<<26)
|
|
#define DISPPLANE_32BPP (0x7<<26)
|
|
+#define DISPPLANE_32BPP_30BIT_NO_ALPHA (0xa<<26)
|
|
#define DISPPLANE_STEREO_ENABLE (1<<25)
|
|
#define DISPPLANE_STEREO_DISABLE 0
|
|
#define DISPPLANE_SEL_PIPE_MASK (1<<24)
|
|
@@ -1914,7 +1931,7 @@
|
|
#define DISPPLANE_NO_LINE_DOUBLE 0
|
|
#define DISPPLANE_STEREO_POLARITY_FIRST 0
|
|
#define DISPPLANE_STEREO_POLARITY_SECOND (1<<18)
|
|
-#define DISPPLANE_TRICKLE_FEED_DISABLE (1<<14) /* IGDNG */
|
|
+#define DISPPLANE_TRICKLE_FEED_DISABLE (1<<14) /* Ironlake */
|
|
#define DISPPLANE_TILED (1<<10)
|
|
#define DSPAADDR 0x70184
|
|
#define DSPASTRIDE 0x70188
|
|
@@ -1967,7 +1984,7 @@
|
|
# define VGA_2X_MODE (1 << 30)
|
|
# define VGA_PIPE_B_SELECT (1 << 29)
|
|
|
|
-/* IGDNG */
|
|
+/* Ironlake */
|
|
|
|
#define CPU_VGACNTRL 0x41000
|
|
|
|
@@ -2113,6 +2130,7 @@
|
|
#define SDE_PORTC_HOTPLUG (1 << 9)
|
|
#define SDE_PORTB_HOTPLUG (1 << 8)
|
|
#define SDE_SDVOB_HOTPLUG (1 << 6)
|
|
+#define SDE_HOTPLUG_MASK (0xf << 8)
|
|
|
|
#define SDEISR 0xc4000
|
|
#define SDEIMR 0xc4004
|
|
@@ -2153,6 +2171,13 @@
|
|
#define PCH_GPIOE 0xc5020
|
|
#define PCH_GPIOF 0xc5024
|
|
|
|
+#define PCH_GMBUS0 0xc5100
|
|
+#define PCH_GMBUS1 0xc5104
|
|
+#define PCH_GMBUS2 0xc5108
|
|
+#define PCH_GMBUS3 0xc510c
|
|
+#define PCH_GMBUS4 0xc5110
|
|
+#define PCH_GMBUS5 0xc5120
|
|
+
|
|
#define PCH_DPLL_A 0xc6014
|
|
#define PCH_DPLL_B 0xc6018
|
|
|
|
@@ -2288,7 +2313,7 @@
|
|
#define FDI_DP_PORT_WIDTH_X3 (2<<19)
|
|
#define FDI_DP_PORT_WIDTH_X4 (3<<19)
|
|
#define FDI_TX_ENHANCE_FRAME_ENABLE (1<<18)
|
|
-/* IGDNG: hardwired to 1 */
|
|
+/* Ironlake: hardwired to 1 */
|
|
#define FDI_TX_PLL_ENABLE (1<<14)
|
|
/* both Tx and Rx */
|
|
#define FDI_SCRAMBLING_ENABLE (0<<7)
|
|
diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c
|
|
index 6eec817..a3b90c9 100644
|
|
--- a/drivers/gpu/drm/i915/i915_suspend.c
|
|
+++ b/drivers/gpu/drm/i915/i915_suspend.c
|
|
@@ -27,14 +27,14 @@
|
|
#include "drmP.h"
|
|
#include "drm.h"
|
|
#include "i915_drm.h"
|
|
-#include "i915_drv.h"
|
|
+#include "intel_drv.h"
|
|
|
|
static bool i915_pipe_enabled(struct drm_device *dev, enum pipe pipe)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
u32 dpll_reg;
|
|
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
dpll_reg = (pipe == PIPE_A) ? PCH_DPLL_A: PCH_DPLL_B;
|
|
} else {
|
|
dpll_reg = (pipe == PIPE_A) ? DPLL_A: DPLL_B;
|
|
@@ -53,7 +53,7 @@ static void i915_save_palette(struct drm_device *dev, enum pipe pipe)
|
|
if (!i915_pipe_enabled(dev, pipe))
|
|
return;
|
|
|
|
- if (IS_IGDNG(dev))
|
|
+ if (IS_IRONLAKE(dev))
|
|
reg = (pipe == PIPE_A) ? LGC_PALETTE_A : LGC_PALETTE_B;
|
|
|
|
if (pipe == PIPE_A)
|
|
@@ -75,7 +75,7 @@ static void i915_restore_palette(struct drm_device *dev, enum pipe pipe)
|
|
if (!i915_pipe_enabled(dev, pipe))
|
|
return;
|
|
|
|
- if (IS_IGDNG(dev))
|
|
+ if (IS_IRONLAKE(dev))
|
|
reg = (pipe == PIPE_A) ? LGC_PALETTE_A : LGC_PALETTE_B;
|
|
|
|
if (pipe == PIPE_A)
|
|
@@ -239,7 +239,7 @@ static void i915_save_modeset_reg(struct drm_device *dev)
|
|
if (drm_core_check_feature(dev, DRIVER_MODESET))
|
|
return;
|
|
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
dev_priv->savePCH_DREF_CONTROL = I915_READ(PCH_DREF_CONTROL);
|
|
dev_priv->saveDISP_ARB_CTL = I915_READ(DISP_ARB_CTL);
|
|
}
|
|
@@ -247,7 +247,7 @@ static void i915_save_modeset_reg(struct drm_device *dev)
|
|
/* Pipe & plane A info */
|
|
dev_priv->savePIPEACONF = I915_READ(PIPEACONF);
|
|
dev_priv->savePIPEASRC = I915_READ(PIPEASRC);
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
dev_priv->saveFPA0 = I915_READ(PCH_FPA0);
|
|
dev_priv->saveFPA1 = I915_READ(PCH_FPA1);
|
|
dev_priv->saveDPLL_A = I915_READ(PCH_DPLL_A);
|
|
@@ -256,7 +256,7 @@ static void i915_save_modeset_reg(struct drm_device *dev)
|
|
dev_priv->saveFPA1 = I915_READ(FPA1);
|
|
dev_priv->saveDPLL_A = I915_READ(DPLL_A);
|
|
}
|
|
- if (IS_I965G(dev) && !IS_IGDNG(dev))
|
|
+ if (IS_I965G(dev) && !IS_IRONLAKE(dev))
|
|
dev_priv->saveDPLL_A_MD = I915_READ(DPLL_A_MD);
|
|
dev_priv->saveHTOTAL_A = I915_READ(HTOTAL_A);
|
|
dev_priv->saveHBLANK_A = I915_READ(HBLANK_A);
|
|
@@ -264,10 +264,10 @@ static void i915_save_modeset_reg(struct drm_device *dev)
|
|
dev_priv->saveVTOTAL_A = I915_READ(VTOTAL_A);
|
|
dev_priv->saveVBLANK_A = I915_READ(VBLANK_A);
|
|
dev_priv->saveVSYNC_A = I915_READ(VSYNC_A);
|
|
- if (!IS_IGDNG(dev))
|
|
+ if (!IS_IRONLAKE(dev))
|
|
dev_priv->saveBCLRPAT_A = I915_READ(BCLRPAT_A);
|
|
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
dev_priv->savePIPEA_DATA_M1 = I915_READ(PIPEA_DATA_M1);
|
|
dev_priv->savePIPEA_DATA_N1 = I915_READ(PIPEA_DATA_N1);
|
|
dev_priv->savePIPEA_LINK_M1 = I915_READ(PIPEA_LINK_M1);
|
|
@@ -304,7 +304,7 @@ static void i915_save_modeset_reg(struct drm_device *dev)
|
|
/* Pipe & plane B info */
|
|
dev_priv->savePIPEBCONF = I915_READ(PIPEBCONF);
|
|
dev_priv->savePIPEBSRC = I915_READ(PIPEBSRC);
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
dev_priv->saveFPB0 = I915_READ(PCH_FPB0);
|
|
dev_priv->saveFPB1 = I915_READ(PCH_FPB1);
|
|
dev_priv->saveDPLL_B = I915_READ(PCH_DPLL_B);
|
|
@@ -313,7 +313,7 @@ static void i915_save_modeset_reg(struct drm_device *dev)
|
|
dev_priv->saveFPB1 = I915_READ(FPB1);
|
|
dev_priv->saveDPLL_B = I915_READ(DPLL_B);
|
|
}
|
|
- if (IS_I965G(dev) && !IS_IGDNG(dev))
|
|
+ if (IS_I965G(dev) && !IS_IRONLAKE(dev))
|
|
dev_priv->saveDPLL_B_MD = I915_READ(DPLL_B_MD);
|
|
dev_priv->saveHTOTAL_B = I915_READ(HTOTAL_B);
|
|
dev_priv->saveHBLANK_B = I915_READ(HBLANK_B);
|
|
@@ -321,10 +321,10 @@ static void i915_save_modeset_reg(struct drm_device *dev)
|
|
dev_priv->saveVTOTAL_B = I915_READ(VTOTAL_B);
|
|
dev_priv->saveVBLANK_B = I915_READ(VBLANK_B);
|
|
dev_priv->saveVSYNC_B = I915_READ(VSYNC_B);
|
|
- if (!IS_IGDNG(dev))
|
|
+ if (!IS_IRONLAKE(dev))
|
|
dev_priv->saveBCLRPAT_B = I915_READ(BCLRPAT_B);
|
|
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
dev_priv->savePIPEB_DATA_M1 = I915_READ(PIPEB_DATA_M1);
|
|
dev_priv->savePIPEB_DATA_N1 = I915_READ(PIPEB_DATA_N1);
|
|
dev_priv->savePIPEB_LINK_M1 = I915_READ(PIPEB_LINK_M1);
|
|
@@ -369,7 +369,7 @@ static void i915_restore_modeset_reg(struct drm_device *dev)
|
|
if (drm_core_check_feature(dev, DRIVER_MODESET))
|
|
return;
|
|
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
dpll_a_reg = PCH_DPLL_A;
|
|
dpll_b_reg = PCH_DPLL_B;
|
|
fpa0_reg = PCH_FPA0;
|
|
@@ -385,7 +385,7 @@ static void i915_restore_modeset_reg(struct drm_device *dev)
|
|
fpb1_reg = FPB1;
|
|
}
|
|
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
I915_WRITE(PCH_DREF_CONTROL, dev_priv->savePCH_DREF_CONTROL);
|
|
I915_WRITE(DISP_ARB_CTL, dev_priv->saveDISP_ARB_CTL);
|
|
}
|
|
@@ -402,7 +402,7 @@ static void i915_restore_modeset_reg(struct drm_device *dev)
|
|
/* Actually enable it */
|
|
I915_WRITE(dpll_a_reg, dev_priv->saveDPLL_A);
|
|
DRM_UDELAY(150);
|
|
- if (IS_I965G(dev) && !IS_IGDNG(dev))
|
|
+ if (IS_I965G(dev) && !IS_IRONLAKE(dev))
|
|
I915_WRITE(DPLL_A_MD, dev_priv->saveDPLL_A_MD);
|
|
DRM_UDELAY(150);
|
|
|
|
@@ -413,10 +413,10 @@ static void i915_restore_modeset_reg(struct drm_device *dev)
|
|
I915_WRITE(VTOTAL_A, dev_priv->saveVTOTAL_A);
|
|
I915_WRITE(VBLANK_A, dev_priv->saveVBLANK_A);
|
|
I915_WRITE(VSYNC_A, dev_priv->saveVSYNC_A);
|
|
- if (!IS_IGDNG(dev))
|
|
+ if (!IS_IRONLAKE(dev))
|
|
I915_WRITE(BCLRPAT_A, dev_priv->saveBCLRPAT_A);
|
|
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
I915_WRITE(PIPEA_DATA_M1, dev_priv->savePIPEA_DATA_M1);
|
|
I915_WRITE(PIPEA_DATA_N1, dev_priv->savePIPEA_DATA_N1);
|
|
I915_WRITE(PIPEA_LINK_M1, dev_priv->savePIPEA_LINK_M1);
|
|
@@ -467,7 +467,7 @@ static void i915_restore_modeset_reg(struct drm_device *dev)
|
|
/* Actually enable it */
|
|
I915_WRITE(dpll_b_reg, dev_priv->saveDPLL_B);
|
|
DRM_UDELAY(150);
|
|
- if (IS_I965G(dev) && !IS_IGDNG(dev))
|
|
+ if (IS_I965G(dev) && !IS_IRONLAKE(dev))
|
|
I915_WRITE(DPLL_B_MD, dev_priv->saveDPLL_B_MD);
|
|
DRM_UDELAY(150);
|
|
|
|
@@ -478,10 +478,10 @@ static void i915_restore_modeset_reg(struct drm_device *dev)
|
|
I915_WRITE(VTOTAL_B, dev_priv->saveVTOTAL_B);
|
|
I915_WRITE(VBLANK_B, dev_priv->saveVBLANK_B);
|
|
I915_WRITE(VSYNC_B, dev_priv->saveVSYNC_B);
|
|
- if (!IS_IGDNG(dev))
|
|
+ if (!IS_IRONLAKE(dev))
|
|
I915_WRITE(BCLRPAT_B, dev_priv->saveBCLRPAT_B);
|
|
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
I915_WRITE(PIPEB_DATA_M1, dev_priv->savePIPEB_DATA_M1);
|
|
I915_WRITE(PIPEB_DATA_N1, dev_priv->savePIPEB_DATA_N1);
|
|
I915_WRITE(PIPEB_LINK_M1, dev_priv->savePIPEB_LINK_M1);
|
|
@@ -546,14 +546,14 @@ void i915_save_display(struct drm_device *dev)
|
|
dev_priv->saveCURSIZE = I915_READ(CURSIZE);
|
|
|
|
/* CRT state */
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
dev_priv->saveADPA = I915_READ(PCH_ADPA);
|
|
} else {
|
|
dev_priv->saveADPA = I915_READ(ADPA);
|
|
}
|
|
|
|
/* LVDS state */
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
dev_priv->savePP_CONTROL = I915_READ(PCH_PP_CONTROL);
|
|
dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_PCH_CTL1);
|
|
dev_priv->saveBLC_PWM_CTL2 = I915_READ(BLC_PWM_PCH_CTL2);
|
|
@@ -571,10 +571,10 @@ void i915_save_display(struct drm_device *dev)
|
|
dev_priv->saveLVDS = I915_READ(LVDS);
|
|
}
|
|
|
|
- if (!IS_I830(dev) && !IS_845G(dev) && !IS_IGDNG(dev))
|
|
+ if (!IS_I830(dev) && !IS_845G(dev) && !IS_IRONLAKE(dev))
|
|
dev_priv->savePFIT_CONTROL = I915_READ(PFIT_CONTROL);
|
|
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
dev_priv->savePP_ON_DELAYS = I915_READ(PCH_PP_ON_DELAYS);
|
|
dev_priv->savePP_OFF_DELAYS = I915_READ(PCH_PP_OFF_DELAYS);
|
|
dev_priv->savePP_DIVISOR = I915_READ(PCH_PP_DIVISOR);
|
|
@@ -614,7 +614,7 @@ void i915_save_display(struct drm_device *dev)
|
|
dev_priv->saveVGA0 = I915_READ(VGA0);
|
|
dev_priv->saveVGA1 = I915_READ(VGA1);
|
|
dev_priv->saveVGA_PD = I915_READ(VGA_PD);
|
|
- if (IS_IGDNG(dev))
|
|
+ if (IS_IRONLAKE(dev))
|
|
dev_priv->saveVGACNTRL = I915_READ(CPU_VGACNTRL);
|
|
else
|
|
dev_priv->saveVGACNTRL = I915_READ(VGACNTRL);
|
|
@@ -656,24 +656,24 @@ void i915_restore_display(struct drm_device *dev)
|
|
I915_WRITE(CURSIZE, dev_priv->saveCURSIZE);
|
|
|
|
/* CRT state */
|
|
- if (IS_IGDNG(dev))
|
|
+ if (IS_IRONLAKE(dev))
|
|
I915_WRITE(PCH_ADPA, dev_priv->saveADPA);
|
|
else
|
|
I915_WRITE(ADPA, dev_priv->saveADPA);
|
|
|
|
/* LVDS state */
|
|
- if (IS_I965G(dev) && !IS_IGDNG(dev))
|
|
+ if (IS_I965G(dev) && !IS_IRONLAKE(dev))
|
|
I915_WRITE(BLC_PWM_CTL2, dev_priv->saveBLC_PWM_CTL2);
|
|
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
I915_WRITE(PCH_LVDS, dev_priv->saveLVDS);
|
|
} else if (IS_MOBILE(dev) && !IS_I830(dev))
|
|
I915_WRITE(LVDS, dev_priv->saveLVDS);
|
|
|
|
- if (!IS_I830(dev) && !IS_845G(dev) && !IS_IGDNG(dev))
|
|
+ if (!IS_I830(dev) && !IS_845G(dev) && !IS_IRONLAKE(dev))
|
|
I915_WRITE(PFIT_CONTROL, dev_priv->savePFIT_CONTROL);
|
|
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
I915_WRITE(BLC_PWM_PCH_CTL1, dev_priv->saveBLC_PWM_CTL);
|
|
I915_WRITE(BLC_PWM_PCH_CTL2, dev_priv->saveBLC_PWM_CTL2);
|
|
I915_WRITE(BLC_PWM_CPU_CTL, dev_priv->saveBLC_CPU_PWM_CTL);
|
|
@@ -713,7 +713,7 @@ void i915_restore_display(struct drm_device *dev)
|
|
}
|
|
|
|
/* VGA state */
|
|
- if (IS_IGDNG(dev))
|
|
+ if (IS_IRONLAKE(dev))
|
|
I915_WRITE(CPU_VGACNTRL, dev_priv->saveVGACNTRL);
|
|
else
|
|
I915_WRITE(VGACNTRL, dev_priv->saveVGACNTRL);
|
|
@@ -732,17 +732,13 @@ int i915_save_state(struct drm_device *dev)
|
|
|
|
pci_read_config_byte(dev->pdev, LBB, &dev_priv->saveLBB);
|
|
|
|
- /* Render Standby */
|
|
- if (IS_I965G(dev) && IS_MOBILE(dev))
|
|
- dev_priv->saveRENDERSTANDBY = I915_READ(MCHBAR_RENDER_STANDBY);
|
|
-
|
|
/* Hardware status page */
|
|
dev_priv->saveHWS = I915_READ(HWS_PGA);
|
|
|
|
i915_save_display(dev);
|
|
|
|
/* Interrupt state */
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
dev_priv->saveDEIER = I915_READ(DEIER);
|
|
dev_priv->saveDEIMR = I915_READ(DEIMR);
|
|
dev_priv->saveGTIER = I915_READ(GTIER);
|
|
@@ -754,10 +750,6 @@ int i915_save_state(struct drm_device *dev)
|
|
dev_priv->saveIMR = I915_READ(IMR);
|
|
}
|
|
|
|
- /* Clock gating state */
|
|
- dev_priv->saveD_STATE = I915_READ(D_STATE);
|
|
- dev_priv->saveDSPCLK_GATE_D = I915_READ(DSPCLK_GATE_D); /* Not sure about this */
|
|
-
|
|
/* Cache mode state */
|
|
dev_priv->saveCACHE_MODE_0 = I915_READ(CACHE_MODE_0);
|
|
|
|
@@ -795,10 +787,6 @@ int i915_restore_state(struct drm_device *dev)
|
|
|
|
pci_write_config_byte(dev->pdev, LBB, dev_priv->saveLBB);
|
|
|
|
- /* Render Standby */
|
|
- if (IS_I965G(dev) && IS_MOBILE(dev))
|
|
- I915_WRITE(MCHBAR_RENDER_STANDBY, dev_priv->saveRENDERSTANDBY);
|
|
-
|
|
/* Hardware status page */
|
|
I915_WRITE(HWS_PGA, dev_priv->saveHWS);
|
|
|
|
@@ -817,7 +805,7 @@ int i915_restore_state(struct drm_device *dev)
|
|
i915_restore_display(dev);
|
|
|
|
/* Interrupt state */
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
I915_WRITE(DEIER, dev_priv->saveDEIER);
|
|
I915_WRITE(DEIMR, dev_priv->saveDEIMR);
|
|
I915_WRITE(GTIER, dev_priv->saveGTIER);
|
|
@@ -830,8 +818,7 @@ int i915_restore_state(struct drm_device *dev)
|
|
}
|
|
|
|
/* Clock gating state */
|
|
- I915_WRITE (D_STATE, dev_priv->saveD_STATE);
|
|
- I915_WRITE (DSPCLK_GATE_D, dev_priv->saveDSPCLK_GATE_D);
|
|
+ intel_init_clock_gating(dev);
|
|
|
|
/* Cache mode state */
|
|
I915_WRITE (CACHE_MODE_0, dev_priv->saveCACHE_MODE_0 | 0xffff0000);
|
|
@@ -846,6 +833,9 @@ int i915_restore_state(struct drm_device *dev)
|
|
for (i = 0; i < 3; i++)
|
|
I915_WRITE(SWF30 + (i << 2), dev_priv->saveSWF2[i]);
|
|
|
|
+ /* I2C state */
|
|
+ intel_i2c_reset_gmbus(dev);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
|
|
index 96cd256..15fbc1b 100644
|
|
--- a/drivers/gpu/drm/i915/intel_bios.c
|
|
+++ b/drivers/gpu/drm/i915/intel_bios.c
|
|
@@ -33,6 +33,8 @@
|
|
#define SLAVE_ADDR1 0x70
|
|
#define SLAVE_ADDR2 0x72
|
|
|
|
+static int panel_type;
|
|
+
|
|
static void *
|
|
find_section(struct bdb_header *bdb, int section_id)
|
|
{
|
|
@@ -114,6 +116,8 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
|
|
struct lvds_dvo_timing *dvo_timing;
|
|
struct drm_display_mode *panel_fixed_mode;
|
|
int lfp_data_size, dvo_timing_offset;
|
|
+ int i, temp_downclock;
|
|
+ struct drm_display_mode *temp_mode;
|
|
|
|
/* Defaults if we can't find VBT info */
|
|
dev_priv->lvds_dither = 0;
|
|
@@ -126,6 +130,7 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
|
|
dev_priv->lvds_dither = lvds_options->pixel_dither;
|
|
if (lvds_options->panel_type == 0xff)
|
|
return;
|
|
+ panel_type = lvds_options->panel_type;
|
|
|
|
lvds_lfp_data = find_section(bdb, BDB_LVDS_LFP_DATA);
|
|
if (!lvds_lfp_data)
|
|
@@ -159,9 +164,50 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
|
|
|
|
dev_priv->lfp_lvds_vbt_mode = panel_fixed_mode;
|
|
|
|
- DRM_DEBUG("Found panel mode in BIOS VBT tables:\n");
|
|
+ DRM_DEBUG_KMS("Found panel mode in BIOS VBT tables:\n");
|
|
drm_mode_debug_printmodeline(panel_fixed_mode);
|
|
|
|
+ temp_mode = kzalloc(sizeof(*temp_mode), GFP_KERNEL);
|
|
+ temp_downclock = panel_fixed_mode->clock;
|
|
+ /*
|
|
+ * enumerate the LVDS panel timing info entry in VBT to check whether
|
|
+ * the LVDS downclock is found.
|
|
+ */
|
|
+ for (i = 0; i < 16; i++) {
|
|
+ entry = (struct bdb_lvds_lfp_data_entry *)
|
|
+ ((uint8_t *)lvds_lfp_data->data + (lfp_data_size * i));
|
|
+ dvo_timing = (struct lvds_dvo_timing *)
|
|
+ ((unsigned char *)entry + dvo_timing_offset);
|
|
+
|
|
+ fill_detail_timing_data(temp_mode, dvo_timing);
|
|
+
|
|
+ if (temp_mode->hdisplay == panel_fixed_mode->hdisplay &&
|
|
+ temp_mode->hsync_start == panel_fixed_mode->hsync_start &&
|
|
+ temp_mode->hsync_end == panel_fixed_mode->hsync_end &&
|
|
+ temp_mode->htotal == panel_fixed_mode->htotal &&
|
|
+ temp_mode->vdisplay == panel_fixed_mode->vdisplay &&
|
|
+ temp_mode->vsync_start == panel_fixed_mode->vsync_start &&
|
|
+ temp_mode->vsync_end == panel_fixed_mode->vsync_end &&
|
|
+ temp_mode->vtotal == panel_fixed_mode->vtotal &&
|
|
+ temp_mode->clock < temp_downclock) {
|
|
+ /*
|
|
+ * downclock is already found. But we expect
|
|
+ * to find the lower downclock.
|
|
+ */
|
|
+ temp_downclock = temp_mode->clock;
|
|
+ }
|
|
+ /* clear it to zero */
|
|
+ memset(temp_mode, 0, sizeof(*temp_mode));
|
|
+ }
|
|
+ kfree(temp_mode);
|
|
+ if (temp_downclock < panel_fixed_mode->clock &&
|
|
+ i915_lvds_downclock) {
|
|
+ dev_priv->lvds_downclock_avail = 1;
|
|
+ dev_priv->lvds_downclock = temp_downclock;
|
|
+ DRM_DEBUG_KMS("LVDS downclock is found in VBT. ",
|
|
+ "Normal Clock %dKHz, downclock %dKHz\n",
|
|
+ temp_downclock, panel_fixed_mode->clock);
|
|
+ }
|
|
return;
|
|
}
|
|
|
|
@@ -217,7 +263,7 @@ parse_general_features(struct drm_i915_private *dev_priv,
|
|
if (IS_I85X(dev_priv->dev))
|
|
dev_priv->lvds_ssc_freq =
|
|
general->ssc_freq ? 66 : 48;
|
|
- else if (IS_IGDNG(dev_priv->dev))
|
|
+ else if (IS_IRONLAKE(dev_priv->dev))
|
|
dev_priv->lvds_ssc_freq =
|
|
general->ssc_freq ? 100 : 120;
|
|
else
|
|
@@ -241,22 +287,18 @@ parse_general_definitions(struct drm_i915_private *dev_priv,
|
|
GPIOF,
|
|
};
|
|
|
|
- /* Set sensible defaults in case we can't find the general block
|
|
- or it is the wrong chipset */
|
|
- dev_priv->crt_ddc_bus = -1;
|
|
-
|
|
general = find_section(bdb, BDB_GENERAL_DEFINITIONS);
|
|
if (general) {
|
|
u16 block_size = get_blocksize(general);
|
|
if (block_size >= sizeof(*general)) {
|
|
int bus_pin = general->crt_ddc_gmbus_pin;
|
|
- DRM_DEBUG("crt_ddc_bus_pin: %d\n", bus_pin);
|
|
+ DRM_DEBUG_KMS("crt_ddc_bus_pin: %d\n", bus_pin);
|
|
if ((bus_pin >= 1) && (bus_pin <= 6)) {
|
|
dev_priv->crt_ddc_bus =
|
|
crt_bus_map_table[bus_pin-1];
|
|
}
|
|
} else {
|
|
- DRM_DEBUG("BDB_GD too small (%d). Invalid.\n",
|
|
+ DRM_DEBUG_KMS("BDB_GD too small (%d). Invalid.\n",
|
|
block_size);
|
|
}
|
|
}
|
|
@@ -274,7 +316,7 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
|
|
|
|
p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS);
|
|
if (!p_defs) {
|
|
- DRM_DEBUG("No general definition block is found\n");
|
|
+ DRM_DEBUG_KMS("No general definition block is found\n");
|
|
return;
|
|
}
|
|
/* judge whether the size of child device meets the requirements.
|
|
@@ -284,7 +326,7 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
|
|
*/
|
|
if (p_defs->child_dev_size != sizeof(*p_child)) {
|
|
/* different child dev size . Ignore it */
|
|
- DRM_DEBUG("different child size is found. Invalid.\n");
|
|
+ DRM_DEBUG_KMS("different child size is found. Invalid.\n");
|
|
return;
|
|
}
|
|
/* get the block size of general definitions */
|
|
@@ -310,11 +352,11 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
|
|
if (p_child->dvo_port != DEVICE_PORT_DVOB &&
|
|
p_child->dvo_port != DEVICE_PORT_DVOC) {
|
|
/* skip the incorrect SDVO port */
|
|
- DRM_DEBUG("Incorrect SDVO port. Skip it \n");
|
|
+ DRM_DEBUG_KMS("Incorrect SDVO port. Skip it \n");
|
|
continue;
|
|
}
|
|
- DRM_DEBUG("the SDVO device with slave addr %2x is found on "
|
|
- "%s port\n",
|
|
+ DRM_DEBUG_KMS("the SDVO device with slave addr %2x is found on"
|
|
+ " %s port\n",
|
|
p_child->slave_addr,
|
|
(p_child->dvo_port == DEVICE_PORT_DVOB) ?
|
|
"SDVOB" : "SDVOC");
|
|
@@ -325,21 +367,21 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
|
|
p_mapping->dvo_wiring = p_child->dvo_wiring;
|
|
p_mapping->initialized = 1;
|
|
} else {
|
|
- DRM_DEBUG("Maybe one SDVO port is shared by "
|
|
+ DRM_DEBUG_KMS("Maybe one SDVO port is shared by "
|
|
"two SDVO device.\n");
|
|
}
|
|
if (p_child->slave2_addr) {
|
|
/* Maybe this is a SDVO device with multiple inputs */
|
|
/* And the mapping info is not added */
|
|
- DRM_DEBUG("there exists the slave2_addr. Maybe this "
|
|
- "is a SDVO device with multiple inputs.\n");
|
|
+ DRM_DEBUG_KMS("there exists the slave2_addr. Maybe this"
|
|
+ " is a SDVO device with multiple inputs.\n");
|
|
}
|
|
count++;
|
|
}
|
|
|
|
if (!count) {
|
|
/* No SDVO device info is found */
|
|
- DRM_DEBUG("No SDVO device info is found in VBT\n");
|
|
+ DRM_DEBUG_KMS("No SDVO device info is found in VBT\n");
|
|
}
|
|
return;
|
|
}
|
|
@@ -366,6 +408,98 @@ parse_driver_features(struct drm_i915_private *dev_priv,
|
|
dev_priv->render_reclock_avail = true;
|
|
}
|
|
|
|
+static void
|
|
+parse_edp(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
|
|
+{
|
|
+ struct bdb_edp *edp;
|
|
+
|
|
+ edp = find_section(bdb, BDB_EDP);
|
|
+ if (!edp) {
|
|
+ if (SUPPORTS_EDP(dev_priv->dev) && dev_priv->edp_support) {
|
|
+ DRM_DEBUG_KMS("No eDP BDB found but eDP panel supported,\
|
|
+ assume 18bpp panel color depth.\n");
|
|
+ dev_priv->edp_bpp = 18;
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ switch ((edp->color_depth >> (panel_type * 2)) & 3) {
|
|
+ case EDP_18BPP:
|
|
+ dev_priv->edp_bpp = 18;
|
|
+ break;
|
|
+ case EDP_24BPP:
|
|
+ dev_priv->edp_bpp = 24;
|
|
+ break;
|
|
+ case EDP_30BPP:
|
|
+ dev_priv->edp_bpp = 30;
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+parse_device_mapping(struct drm_i915_private *dev_priv,
|
|
+ struct bdb_header *bdb)
|
|
+{
|
|
+ struct bdb_general_definitions *p_defs;
|
|
+ struct child_device_config *p_child, *child_dev_ptr;
|
|
+ int i, child_device_num, count;
|
|
+ u16 block_size;
|
|
+
|
|
+ p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS);
|
|
+ if (!p_defs) {
|
|
+ DRM_DEBUG_KMS("No general definition block is found\n");
|
|
+ return;
|
|
+ }
|
|
+ /* judge whether the size of child device meets the requirements.
|
|
+ * If the child device size obtained from general definition block
|
|
+ * is different with sizeof(struct child_device_config), skip the
|
|
+ * parsing of sdvo device info
|
|
+ */
|
|
+ if (p_defs->child_dev_size != sizeof(*p_child)) {
|
|
+ /* different child dev size . Ignore it */
|
|
+ DRM_DEBUG_KMS("different child size is found. Invalid.\n");
|
|
+ return;
|
|
+ }
|
|
+ /* get the block size of general definitions */
|
|
+ block_size = get_blocksize(p_defs);
|
|
+ /* get the number of child device */
|
|
+ child_device_num = (block_size - sizeof(*p_defs)) /
|
|
+ sizeof(*p_child);
|
|
+ count = 0;
|
|
+ /* get the number of child device that is present */
|
|
+ for (i = 0; i < child_device_num; i++) {
|
|
+ p_child = &(p_defs->devices[i]);
|
|
+ if (!p_child->device_type) {
|
|
+ /* skip the device block if device type is invalid */
|
|
+ continue;
|
|
+ }
|
|
+ count++;
|
|
+ }
|
|
+ if (!count) {
|
|
+ DRM_DEBUG_KMS("no child dev is parsed from VBT \n");
|
|
+ return;
|
|
+ }
|
|
+ dev_priv->child_dev = kzalloc(sizeof(*p_child) * count, GFP_KERNEL);
|
|
+ if (!dev_priv->child_dev) {
|
|
+ DRM_DEBUG_KMS("No memory space for child device\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ dev_priv->child_dev_num = count;
|
|
+ count = 0;
|
|
+ for (i = 0; i < child_device_num; i++) {
|
|
+ p_child = &(p_defs->devices[i]);
|
|
+ if (!p_child->device_type) {
|
|
+ /* skip the device block if device type is invalid */
|
|
+ continue;
|
|
+ }
|
|
+ child_dev_ptr = dev_priv->child_dev + count;
|
|
+ count++;
|
|
+ memcpy((void *)child_dev_ptr, (void *)p_child,
|
|
+ sizeof(*p_child));
|
|
+ }
|
|
+ return;
|
|
+}
|
|
/**
|
|
* intel_init_bios - initialize VBIOS settings & find VBT
|
|
* @dev: DRM device
|
|
@@ -417,7 +551,9 @@ intel_init_bios(struct drm_device *dev)
|
|
parse_lfp_panel_data(dev_priv, bdb);
|
|
parse_sdvo_panel_data(dev_priv, bdb);
|
|
parse_sdvo_device_mapping(dev_priv, bdb);
|
|
+ parse_device_mapping(dev_priv, bdb);
|
|
parse_driver_features(dev_priv, bdb);
|
|
+ parse_edp(dev_priv, bdb);
|
|
|
|
pci_unmap_rom(pdev, bios);
|
|
|
|
diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h
|
|
index 0f8e5f6..4c18514 100644
|
|
--- a/drivers/gpu/drm/i915/intel_bios.h
|
|
+++ b/drivers/gpu/drm/i915/intel_bios.h
|
|
@@ -98,6 +98,7 @@ struct vbios_data {
|
|
#define BDB_SDVO_LVDS_PNP_IDS 24
|
|
#define BDB_SDVO_LVDS_POWER_SEQ 25
|
|
#define BDB_TV_OPTIONS 26
|
|
+#define BDB_EDP 27
|
|
#define BDB_LVDS_OPTIONS 40
|
|
#define BDB_LVDS_LFP_DATA_PTRS 41
|
|
#define BDB_LVDS_LFP_DATA 42
|
|
@@ -426,6 +427,45 @@ struct bdb_driver_features {
|
|
u8 custom_vbt_version;
|
|
} __attribute__((packed));
|
|
|
|
+#define EDP_18BPP 0
|
|
+#define EDP_24BPP 1
|
|
+#define EDP_30BPP 2
|
|
+#define EDP_RATE_1_62 0
|
|
+#define EDP_RATE_2_7 1
|
|
+#define EDP_LANE_1 0
|
|
+#define EDP_LANE_2 1
|
|
+#define EDP_LANE_4 3
|
|
+#define EDP_PREEMPHASIS_NONE 0
|
|
+#define EDP_PREEMPHASIS_3_5dB 1
|
|
+#define EDP_PREEMPHASIS_6dB 2
|
|
+#define EDP_PREEMPHASIS_9_5dB 3
|
|
+#define EDP_VSWING_0_4V 0
|
|
+#define EDP_VSWING_0_6V 1
|
|
+#define EDP_VSWING_0_8V 2
|
|
+#define EDP_VSWING_1_2V 3
|
|
+
|
|
+struct edp_power_seq {
|
|
+ u16 t3;
|
|
+ u16 t7;
|
|
+ u16 t9;
|
|
+ u16 t10;
|
|
+ u16 t12;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct edp_link_params {
|
|
+ u8 rate:4;
|
|
+ u8 lanes:4;
|
|
+ u8 preemphasis:4;
|
|
+ u8 vswing:4;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct bdb_edp {
|
|
+ struct edp_power_seq power_seqs[16];
|
|
+ u32 color_depth;
|
|
+ u32 sdrrs_msa_timing_delay;
|
|
+ struct edp_link_params link_params[16];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
bool intel_init_bios(struct drm_device *dev);
|
|
|
|
/*
|
|
@@ -549,4 +589,21 @@ bool intel_init_bios(struct drm_device *dev);
|
|
#define SWF14_APM_STANDBY 0x1
|
|
#define SWF14_APM_RESTORE 0x0
|
|
|
|
+/* Add the device class for LFP, TV, HDMI */
|
|
+#define DEVICE_TYPE_INT_LFP 0x1022
|
|
+#define DEVICE_TYPE_INT_TV 0x1009
|
|
+#define DEVICE_TYPE_HDMI 0x60D2
|
|
+#define DEVICE_TYPE_DP 0x68C6
|
|
+#define DEVICE_TYPE_eDP 0x78C6
|
|
+
|
|
+/* define the DVO port for HDMI output type */
|
|
+#define DVO_B 1
|
|
+#define DVO_C 2
|
|
+#define DVO_D 3
|
|
+
|
|
+/* define the PORT for DP output type */
|
|
+#define PORT_IDPB 7
|
|
+#define PORT_IDPC 8
|
|
+#define PORT_IDPD 9
|
|
+
|
|
#endif /* _I830_BIOS_H_ */
|
|
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
|
|
index 6d3730f..79dd402 100644
|
|
--- a/drivers/gpu/drm/i915/intel_crt.c
|
|
+++ b/drivers/gpu/drm/i915/intel_crt.c
|
|
@@ -39,7 +39,7 @@ static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
u32 temp, reg;
|
|
|
|
- if (IS_IGDNG(dev))
|
|
+ if (IS_IRONLAKE(dev))
|
|
reg = PCH_ADPA;
|
|
else
|
|
reg = ADPA;
|
|
@@ -64,34 +64,6 @@ static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
|
|
}
|
|
|
|
I915_WRITE(reg, temp);
|
|
-
|
|
- if (IS_IGD(dev)) {
|
|
- if (mode == DRM_MODE_DPMS_OFF) {
|
|
- /* turn off DAC */
|
|
- temp = I915_READ(PORT_HOTPLUG_EN);
|
|
- temp &= ~CRT_EOS_INT_EN;
|
|
- I915_WRITE(PORT_HOTPLUG_EN, temp);
|
|
-
|
|
- temp = I915_READ(PORT_HOTPLUG_STAT);
|
|
- if (temp & CRT_EOS_INT_STATUS)
|
|
- I915_WRITE(PORT_HOTPLUG_STAT,
|
|
- CRT_EOS_INT_STATUS);
|
|
- } else {
|
|
- /* turn on DAC. EOS interrupt must be enabled after DAC
|
|
- * is enabled, so it sounds not good to enable it in
|
|
- * i915_driver_irq_postinstall()
|
|
- * wait 12.5ms after DAC is enabled
|
|
- */
|
|
- msleep(13);
|
|
- temp = I915_READ(PORT_HOTPLUG_STAT);
|
|
- if (temp & CRT_EOS_INT_STATUS)
|
|
- I915_WRITE(PORT_HOTPLUG_STAT,
|
|
- CRT_EOS_INT_STATUS);
|
|
- temp = I915_READ(PORT_HOTPLUG_EN);
|
|
- temp |= CRT_EOS_INT_EN;
|
|
- I915_WRITE(PORT_HOTPLUG_EN, temp);
|
|
- }
|
|
- }
|
|
}
|
|
|
|
static int intel_crt_mode_valid(struct drm_connector *connector,
|
|
@@ -141,7 +113,7 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
|
|
else
|
|
dpll_md_reg = DPLL_B_MD;
|
|
|
|
- if (IS_IGDNG(dev))
|
|
+ if (IS_IRONLAKE(dev))
|
|
adpa_reg = PCH_ADPA;
|
|
else
|
|
adpa_reg = ADPA;
|
|
@@ -150,7 +122,7 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
|
|
* Disable separate mode multiplier used when cloning SDVO to CRT
|
|
* XXX this needs to be adjusted when we really are cloning
|
|
*/
|
|
- if (IS_I965G(dev) && !IS_IGDNG(dev)) {
|
|
+ if (IS_I965G(dev) && !IS_IRONLAKE(dev)) {
|
|
dpll_md = I915_READ(dpll_md_reg);
|
|
I915_WRITE(dpll_md_reg,
|
|
dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK);
|
|
@@ -164,18 +136,18 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
|
|
|
|
if (intel_crtc->pipe == 0) {
|
|
adpa |= ADPA_PIPE_A_SELECT;
|
|
- if (!IS_IGDNG(dev))
|
|
+ if (!IS_IRONLAKE(dev))
|
|
I915_WRITE(BCLRPAT_A, 0);
|
|
} else {
|
|
adpa |= ADPA_PIPE_B_SELECT;
|
|
- if (!IS_IGDNG(dev))
|
|
+ if (!IS_IRONLAKE(dev))
|
|
I915_WRITE(BCLRPAT_B, 0);
|
|
}
|
|
|
|
I915_WRITE(adpa_reg, adpa);
|
|
}
|
|
|
|
-static bool intel_igdng_crt_detect_hotplug(struct drm_connector *connector)
|
|
+static bool intel_ironlake_crt_detect_hotplug(struct drm_connector *connector)
|
|
{
|
|
struct drm_device *dev = connector->dev;
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
@@ -185,6 +157,9 @@ static bool intel_igdng_crt_detect_hotplug(struct drm_connector *connector)
|
|
adpa = I915_READ(PCH_ADPA);
|
|
|
|
adpa &= ~ADPA_CRT_HOTPLUG_MASK;
|
|
+ /* disable HPD first */
|
|
+ I915_WRITE(PCH_ADPA, adpa);
|
|
+ (void)I915_READ(PCH_ADPA);
|
|
|
|
adpa |= (ADPA_CRT_HOTPLUG_PERIOD_128 |
|
|
ADPA_CRT_HOTPLUG_WARMUP_10MS |
|
|
@@ -194,7 +169,7 @@ static bool intel_igdng_crt_detect_hotplug(struct drm_connector *connector)
|
|
ADPA_CRT_HOTPLUG_ENABLE |
|
|
ADPA_CRT_HOTPLUG_FORCE_TRIGGER);
|
|
|
|
- DRM_DEBUG("pch crt adpa 0x%x", adpa);
|
|
+ DRM_DEBUG_KMS("pch crt adpa 0x%x", adpa);
|
|
I915_WRITE(PCH_ADPA, adpa);
|
|
|
|
while ((I915_READ(PCH_ADPA) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) != 0)
|
|
@@ -227,8 +202,8 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector)
|
|
u32 hotplug_en;
|
|
int i, tries = 0;
|
|
|
|
- if (IS_IGDNG(dev))
|
|
- return intel_igdng_crt_detect_hotplug(connector);
|
|
+ if (IS_IRONLAKE(dev))
|
|
+ return intel_ironlake_crt_detect_hotplug(connector);
|
|
|
|
/*
|
|
* On 4 series desktop, CRT detect sequence need to be done twice
|
|
@@ -549,12 +524,12 @@ void intel_crt_init(struct drm_device *dev)
|
|
&intel_output->enc);
|
|
|
|
/* Set up the DDC bus. */
|
|
- if (IS_IGDNG(dev))
|
|
+ if (IS_IRONLAKE(dev))
|
|
i2c_reg = PCH_GPIOA;
|
|
else {
|
|
i2c_reg = GPIOA;
|
|
/* Use VBT information for CRT DDC if available */
|
|
- if (dev_priv->crt_ddc_bus != -1)
|
|
+ if (dev_priv->crt_ddc_bus != 0)
|
|
i2c_reg = dev_priv->crt_ddc_bus;
|
|
}
|
|
intel_output->ddc_bus = intel_i2c_create(dev, i2c_reg, "CRTDDC_A");
|
|
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
|
|
index 601415d..b27202d 100644
|
|
--- a/drivers/gpu/drm/i915/intel_display.c
|
|
+++ b/drivers/gpu/drm/i915/intel_display.c
|
|
@@ -32,7 +32,7 @@
|
|
#include "intel_drv.h"
|
|
#include "i915_drm.h"
|
|
#include "i915_drv.h"
|
|
-#include "intel_dp.h"
|
|
+#include "drm_dp_helper.h"
|
|
|
|
#include "drm_crtc_helper.h"
|
|
|
|
@@ -70,8 +70,6 @@ struct intel_limit {
|
|
intel_p2_t p2;
|
|
bool (* find_pll)(const intel_limit_t *, struct drm_crtc *,
|
|
int, int, intel_clock_t *);
|
|
- bool (* find_reduced_pll)(const intel_limit_t *, struct drm_crtc *,
|
|
- int, int, intel_clock_t *);
|
|
};
|
|
|
|
#define I8XX_DOT_MIN 25000
|
|
@@ -102,32 +100,32 @@ struct intel_limit {
|
|
#define I9XX_DOT_MAX 400000
|
|
#define I9XX_VCO_MIN 1400000
|
|
#define I9XX_VCO_MAX 2800000
|
|
-#define IGD_VCO_MIN 1700000
|
|
-#define IGD_VCO_MAX 3500000
|
|
+#define PINEVIEW_VCO_MIN 1700000
|
|
+#define PINEVIEW_VCO_MAX 3500000
|
|
#define I9XX_N_MIN 1
|
|
#define I9XX_N_MAX 6
|
|
-/* IGD's Ncounter is a ring counter */
|
|
-#define IGD_N_MIN 3
|
|
-#define IGD_N_MAX 6
|
|
+/* Pineview's Ncounter is a ring counter */
|
|
+#define PINEVIEW_N_MIN 3
|
|
+#define PINEVIEW_N_MAX 6
|
|
#define I9XX_M_MIN 70
|
|
#define I9XX_M_MAX 120
|
|
-#define IGD_M_MIN 2
|
|
-#define IGD_M_MAX 256
|
|
+#define PINEVIEW_M_MIN 2
|
|
+#define PINEVIEW_M_MAX 256
|
|
#define I9XX_M1_MIN 10
|
|
#define I9XX_M1_MAX 22
|
|
#define I9XX_M2_MIN 5
|
|
#define I9XX_M2_MAX 9
|
|
-/* IGD M1 is reserved, and must be 0 */
|
|
-#define IGD_M1_MIN 0
|
|
-#define IGD_M1_MAX 0
|
|
-#define IGD_M2_MIN 0
|
|
-#define IGD_M2_MAX 254
|
|
+/* Pineview M1 is reserved, and must be 0 */
|
|
+#define PINEVIEW_M1_MIN 0
|
|
+#define PINEVIEW_M1_MAX 0
|
|
+#define PINEVIEW_M2_MIN 0
|
|
+#define PINEVIEW_M2_MAX 254
|
|
#define I9XX_P_SDVO_DAC_MIN 5
|
|
#define I9XX_P_SDVO_DAC_MAX 80
|
|
#define I9XX_P_LVDS_MIN 7
|
|
#define I9XX_P_LVDS_MAX 98
|
|
-#define IGD_P_LVDS_MIN 7
|
|
-#define IGD_P_LVDS_MAX 112
|
|
+#define PINEVIEW_P_LVDS_MIN 7
|
|
+#define PINEVIEW_P_LVDS_MAX 112
|
|
#define I9XX_P1_MIN 1
|
|
#define I9XX_P1_MAX 8
|
|
#define I9XX_P2_SDVO_DAC_SLOW 10
|
|
@@ -234,53 +232,108 @@ struct intel_limit {
|
|
#define G4X_P2_DISPLAY_PORT_FAST 10
|
|
#define G4X_P2_DISPLAY_PORT_LIMIT 0
|
|
|
|
-/* IGDNG */
|
|
+/* Ironlake */
|
|
/* as we calculate clock using (register_value + 2) for
|
|
N/M1/M2, so here the range value for them is (actual_value-2).
|
|
*/
|
|
-#define IGDNG_DOT_MIN 25000
|
|
-#define IGDNG_DOT_MAX 350000
|
|
-#define IGDNG_VCO_MIN 1760000
|
|
-#define IGDNG_VCO_MAX 3510000
|
|
-#define IGDNG_N_MIN 1
|
|
-#define IGDNG_N_MAX 5
|
|
-#define IGDNG_M_MIN 79
|
|
-#define IGDNG_M_MAX 118
|
|
-#define IGDNG_M1_MIN 12
|
|
-#define IGDNG_M1_MAX 23
|
|
-#define IGDNG_M2_MIN 5
|
|
-#define IGDNG_M2_MAX 9
|
|
-#define IGDNG_P_SDVO_DAC_MIN 5
|
|
-#define IGDNG_P_SDVO_DAC_MAX 80
|
|
-#define IGDNG_P_LVDS_MIN 28
|
|
-#define IGDNG_P_LVDS_MAX 112
|
|
-#define IGDNG_P1_MIN 1
|
|
-#define IGDNG_P1_MAX 8
|
|
-#define IGDNG_P2_SDVO_DAC_SLOW 10
|
|
-#define IGDNG_P2_SDVO_DAC_FAST 5
|
|
-#define IGDNG_P2_LVDS_SLOW 14 /* single channel */
|
|
-#define IGDNG_P2_LVDS_FAST 7 /* double channel */
|
|
-#define IGDNG_P2_DOT_LIMIT 225000 /* 225Mhz */
|
|
+#define IRONLAKE_DOT_MIN 25000
|
|
+#define IRONLAKE_DOT_MAX 350000
|
|
+#define IRONLAKE_VCO_MIN 1760000
|
|
+#define IRONLAKE_VCO_MAX 3510000
|
|
+#define IRONLAKE_M1_MIN 12
|
|
+#define IRONLAKE_M1_MAX 22
|
|
+#define IRONLAKE_M2_MIN 5
|
|
+#define IRONLAKE_M2_MAX 9
|
|
+#define IRONLAKE_P2_DOT_LIMIT 225000 /* 225Mhz */
|
|
+
|
|
+/* We have parameter ranges for different type of outputs. */
|
|
+
|
|
+/* DAC & HDMI Refclk 120Mhz */
|
|
+#define IRONLAKE_DAC_N_MIN 1
|
|
+#define IRONLAKE_DAC_N_MAX 5
|
|
+#define IRONLAKE_DAC_M_MIN 79
|
|
+#define IRONLAKE_DAC_M_MAX 127
|
|
+#define IRONLAKE_DAC_P_MIN 5
|
|
+#define IRONLAKE_DAC_P_MAX 80
|
|
+#define IRONLAKE_DAC_P1_MIN 1
|
|
+#define IRONLAKE_DAC_P1_MAX 8
|
|
+#define IRONLAKE_DAC_P2_SLOW 10
|
|
+#define IRONLAKE_DAC_P2_FAST 5
|
|
+
|
|
+/* LVDS single-channel 120Mhz refclk */
|
|
+#define IRONLAKE_LVDS_S_N_MIN 1
|
|
+#define IRONLAKE_LVDS_S_N_MAX 3
|
|
+#define IRONLAKE_LVDS_S_M_MIN 79
|
|
+#define IRONLAKE_LVDS_S_M_MAX 118
|
|
+#define IRONLAKE_LVDS_S_P_MIN 28
|
|
+#define IRONLAKE_LVDS_S_P_MAX 112
|
|
+#define IRONLAKE_LVDS_S_P1_MIN 2
|
|
+#define IRONLAKE_LVDS_S_P1_MAX 8
|
|
+#define IRONLAKE_LVDS_S_P2_SLOW 14
|
|
+#define IRONLAKE_LVDS_S_P2_FAST 14
|
|
+
|
|
+/* LVDS dual-channel 120Mhz refclk */
|
|
+#define IRONLAKE_LVDS_D_N_MIN 1
|
|
+#define IRONLAKE_LVDS_D_N_MAX 3
|
|
+#define IRONLAKE_LVDS_D_M_MIN 79
|
|
+#define IRONLAKE_LVDS_D_M_MAX 127
|
|
+#define IRONLAKE_LVDS_D_P_MIN 14
|
|
+#define IRONLAKE_LVDS_D_P_MAX 56
|
|
+#define IRONLAKE_LVDS_D_P1_MIN 2
|
|
+#define IRONLAKE_LVDS_D_P1_MAX 8
|
|
+#define IRONLAKE_LVDS_D_P2_SLOW 7
|
|
+#define IRONLAKE_LVDS_D_P2_FAST 7
|
|
+
|
|
+/* LVDS single-channel 100Mhz refclk */
|
|
+#define IRONLAKE_LVDS_S_SSC_N_MIN 1
|
|
+#define IRONLAKE_LVDS_S_SSC_N_MAX 2
|
|
+#define IRONLAKE_LVDS_S_SSC_M_MIN 79
|
|
+#define IRONLAKE_LVDS_S_SSC_M_MAX 126
|
|
+#define IRONLAKE_LVDS_S_SSC_P_MIN 28
|
|
+#define IRONLAKE_LVDS_S_SSC_P_MAX 112
|
|
+#define IRONLAKE_LVDS_S_SSC_P1_MIN 2
|
|
+#define IRONLAKE_LVDS_S_SSC_P1_MAX 8
|
|
+#define IRONLAKE_LVDS_S_SSC_P2_SLOW 14
|
|
+#define IRONLAKE_LVDS_S_SSC_P2_FAST 14
|
|
+
|
|
+/* LVDS dual-channel 100Mhz refclk */
|
|
+#define IRONLAKE_LVDS_D_SSC_N_MIN 1
|
|
+#define IRONLAKE_LVDS_D_SSC_N_MAX 3
|
|
+#define IRONLAKE_LVDS_D_SSC_M_MIN 79
|
|
+#define IRONLAKE_LVDS_D_SSC_M_MAX 126
|
|
+#define IRONLAKE_LVDS_D_SSC_P_MIN 14
|
|
+#define IRONLAKE_LVDS_D_SSC_P_MAX 42
|
|
+#define IRONLAKE_LVDS_D_SSC_P1_MIN 2
|
|
+#define IRONLAKE_LVDS_D_SSC_P1_MAX 6
|
|
+#define IRONLAKE_LVDS_D_SSC_P2_SLOW 7
|
|
+#define IRONLAKE_LVDS_D_SSC_P2_FAST 7
|
|
+
|
|
+/* DisplayPort */
|
|
+#define IRONLAKE_DP_N_MIN 1
|
|
+#define IRONLAKE_DP_N_MAX 2
|
|
+#define IRONLAKE_DP_M_MIN 81
|
|
+#define IRONLAKE_DP_M_MAX 90
|
|
+#define IRONLAKE_DP_P_MIN 10
|
|
+#define IRONLAKE_DP_P_MAX 20
|
|
+#define IRONLAKE_DP_P2_FAST 10
|
|
+#define IRONLAKE_DP_P2_SLOW 10
|
|
+#define IRONLAKE_DP_P2_LIMIT 0
|
|
+#define IRONLAKE_DP_P1_MIN 1
|
|
+#define IRONLAKE_DP_P1_MAX 2
|
|
|
|
static bool
|
|
intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
|
|
int target, int refclk, intel_clock_t *best_clock);
|
|
static bool
|
|
-intel_find_best_reduced_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
|
|
- int target, int refclk, intel_clock_t *best_clock);
|
|
-static bool
|
|
intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
|
|
int target, int refclk, intel_clock_t *best_clock);
|
|
-static bool
|
|
-intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
|
|
- int target, int refclk, intel_clock_t *best_clock);
|
|
|
|
static bool
|
|
intel_find_pll_g4x_dp(const intel_limit_t *, struct drm_crtc *crtc,
|
|
int target, int refclk, intel_clock_t *best_clock);
|
|
static bool
|
|
-intel_find_pll_igdng_dp(const intel_limit_t *, struct drm_crtc *crtc,
|
|
- int target, int refclk, intel_clock_t *best_clock);
|
|
+intel_find_pll_ironlake_dp(const intel_limit_t *, struct drm_crtc *crtc,
|
|
+ int target, int refclk, intel_clock_t *best_clock);
|
|
|
|
static const intel_limit_t intel_limits_i8xx_dvo = {
|
|
.dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX },
|
|
@@ -294,7 +347,6 @@ static const intel_limit_t intel_limits_i8xx_dvo = {
|
|
.p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT,
|
|
.p2_slow = I8XX_P2_SLOW, .p2_fast = I8XX_P2_FAST },
|
|
.find_pll = intel_find_best_PLL,
|
|
- .find_reduced_pll = intel_find_best_reduced_PLL,
|
|
};
|
|
|
|
static const intel_limit_t intel_limits_i8xx_lvds = {
|
|
@@ -309,7 +361,6 @@ static const intel_limit_t intel_limits_i8xx_lvds = {
|
|
.p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT,
|
|
.p2_slow = I8XX_P2_LVDS_SLOW, .p2_fast = I8XX_P2_LVDS_FAST },
|
|
.find_pll = intel_find_best_PLL,
|
|
- .find_reduced_pll = intel_find_best_reduced_PLL,
|
|
};
|
|
|
|
static const intel_limit_t intel_limits_i9xx_sdvo = {
|
|
@@ -324,7 +375,6 @@ static const intel_limit_t intel_limits_i9xx_sdvo = {
|
|
.p2 = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT,
|
|
.p2_slow = I9XX_P2_SDVO_DAC_SLOW, .p2_fast = I9XX_P2_SDVO_DAC_FAST },
|
|
.find_pll = intel_find_best_PLL,
|
|
- .find_reduced_pll = intel_find_best_reduced_PLL,
|
|
};
|
|
|
|
static const intel_limit_t intel_limits_i9xx_lvds = {
|
|
@@ -342,7 +392,6 @@ static const intel_limit_t intel_limits_i9xx_lvds = {
|
|
.p2 = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT,
|
|
.p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_FAST },
|
|
.find_pll = intel_find_best_PLL,
|
|
- .find_reduced_pll = intel_find_best_reduced_PLL,
|
|
};
|
|
|
|
/* below parameter and function is for G4X Chipset Family*/
|
|
@@ -360,7 +409,6 @@ static const intel_limit_t intel_limits_g4x_sdvo = {
|
|
.p2_fast = G4X_P2_SDVO_FAST
|
|
},
|
|
.find_pll = intel_g4x_find_best_PLL,
|
|
- .find_reduced_pll = intel_g4x_find_best_PLL,
|
|
};
|
|
|
|
static const intel_limit_t intel_limits_g4x_hdmi = {
|
|
@@ -377,7 +425,6 @@ static const intel_limit_t intel_limits_g4x_hdmi = {
|
|
.p2_fast = G4X_P2_HDMI_DAC_FAST
|
|
},
|
|
.find_pll = intel_g4x_find_best_PLL,
|
|
- .find_reduced_pll = intel_g4x_find_best_PLL,
|
|
};
|
|
|
|
static const intel_limit_t intel_limits_g4x_single_channel_lvds = {
|
|
@@ -402,7 +449,6 @@ static const intel_limit_t intel_limits_g4x_single_channel_lvds = {
|
|
.p2_fast = G4X_P2_SINGLE_CHANNEL_LVDS_FAST
|
|
},
|
|
.find_pll = intel_g4x_find_best_PLL,
|
|
- .find_reduced_pll = intel_g4x_find_best_PLL,
|
|
};
|
|
|
|
static const intel_limit_t intel_limits_g4x_dual_channel_lvds = {
|
|
@@ -427,7 +473,6 @@ static const intel_limit_t intel_limits_g4x_dual_channel_lvds = {
|
|
.p2_fast = G4X_P2_DUAL_CHANNEL_LVDS_FAST
|
|
},
|
|
.find_pll = intel_g4x_find_best_PLL,
|
|
- .find_reduced_pll = intel_g4x_find_best_PLL,
|
|
};
|
|
|
|
static const intel_limit_t intel_limits_g4x_display_port = {
|
|
@@ -453,74 +498,162 @@ static const intel_limit_t intel_limits_g4x_display_port = {
|
|
.find_pll = intel_find_pll_g4x_dp,
|
|
};
|
|
|
|
-static const intel_limit_t intel_limits_igd_sdvo = {
|
|
+static const intel_limit_t intel_limits_pineview_sdvo = {
|
|
.dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX},
|
|
- .vco = { .min = IGD_VCO_MIN, .max = IGD_VCO_MAX },
|
|
- .n = { .min = IGD_N_MIN, .max = IGD_N_MAX },
|
|
- .m = { .min = IGD_M_MIN, .max = IGD_M_MAX },
|
|
- .m1 = { .min = IGD_M1_MIN, .max = IGD_M1_MAX },
|
|
- .m2 = { .min = IGD_M2_MIN, .max = IGD_M2_MAX },
|
|
+ .vco = { .min = PINEVIEW_VCO_MIN, .max = PINEVIEW_VCO_MAX },
|
|
+ .n = { .min = PINEVIEW_N_MIN, .max = PINEVIEW_N_MAX },
|
|
+ .m = { .min = PINEVIEW_M_MIN, .max = PINEVIEW_M_MAX },
|
|
+ .m1 = { .min = PINEVIEW_M1_MIN, .max = PINEVIEW_M1_MAX },
|
|
+ .m2 = { .min = PINEVIEW_M2_MIN, .max = PINEVIEW_M2_MAX },
|
|
.p = { .min = I9XX_P_SDVO_DAC_MIN, .max = I9XX_P_SDVO_DAC_MAX },
|
|
.p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX },
|
|
.p2 = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT,
|
|
.p2_slow = I9XX_P2_SDVO_DAC_SLOW, .p2_fast = I9XX_P2_SDVO_DAC_FAST },
|
|
.find_pll = intel_find_best_PLL,
|
|
- .find_reduced_pll = intel_find_best_reduced_PLL,
|
|
};
|
|
|
|
-static const intel_limit_t intel_limits_igd_lvds = {
|
|
+static const intel_limit_t intel_limits_pineview_lvds = {
|
|
.dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX },
|
|
- .vco = { .min = IGD_VCO_MIN, .max = IGD_VCO_MAX },
|
|
- .n = { .min = IGD_N_MIN, .max = IGD_N_MAX },
|
|
- .m = { .min = IGD_M_MIN, .max = IGD_M_MAX },
|
|
- .m1 = { .min = IGD_M1_MIN, .max = IGD_M1_MAX },
|
|
- .m2 = { .min = IGD_M2_MIN, .max = IGD_M2_MAX },
|
|
- .p = { .min = IGD_P_LVDS_MIN, .max = IGD_P_LVDS_MAX },
|
|
+ .vco = { .min = PINEVIEW_VCO_MIN, .max = PINEVIEW_VCO_MAX },
|
|
+ .n = { .min = PINEVIEW_N_MIN, .max = PINEVIEW_N_MAX },
|
|
+ .m = { .min = PINEVIEW_M_MIN, .max = PINEVIEW_M_MAX },
|
|
+ .m1 = { .min = PINEVIEW_M1_MIN, .max = PINEVIEW_M1_MAX },
|
|
+ .m2 = { .min = PINEVIEW_M2_MIN, .max = PINEVIEW_M2_MAX },
|
|
+ .p = { .min = PINEVIEW_P_LVDS_MIN, .max = PINEVIEW_P_LVDS_MAX },
|
|
.p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX },
|
|
- /* IGD only supports single-channel mode. */
|
|
+ /* Pineview only supports single-channel mode. */
|
|
.p2 = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT,
|
|
.p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_SLOW },
|
|
.find_pll = intel_find_best_PLL,
|
|
- .find_reduced_pll = intel_find_best_reduced_PLL,
|
|
};
|
|
|
|
-static const intel_limit_t intel_limits_igdng_sdvo = {
|
|
- .dot = { .min = IGDNG_DOT_MIN, .max = IGDNG_DOT_MAX },
|
|
- .vco = { .min = IGDNG_VCO_MIN, .max = IGDNG_VCO_MAX },
|
|
- .n = { .min = IGDNG_N_MIN, .max = IGDNG_N_MAX },
|
|
- .m = { .min = IGDNG_M_MIN, .max = IGDNG_M_MAX },
|
|
- .m1 = { .min = IGDNG_M1_MIN, .max = IGDNG_M1_MAX },
|
|
- .m2 = { .min = IGDNG_M2_MIN, .max = IGDNG_M2_MAX },
|
|
- .p = { .min = IGDNG_P_SDVO_DAC_MIN, .max = IGDNG_P_SDVO_DAC_MAX },
|
|
- .p1 = { .min = IGDNG_P1_MIN, .max = IGDNG_P1_MAX },
|
|
- .p2 = { .dot_limit = IGDNG_P2_DOT_LIMIT,
|
|
- .p2_slow = IGDNG_P2_SDVO_DAC_SLOW,
|
|
- .p2_fast = IGDNG_P2_SDVO_DAC_FAST },
|
|
- .find_pll = intel_igdng_find_best_PLL,
|
|
+static const intel_limit_t intel_limits_ironlake_dac = {
|
|
+ .dot = { .min = IRONLAKE_DOT_MIN, .max = IRONLAKE_DOT_MAX },
|
|
+ .vco = { .min = IRONLAKE_VCO_MIN, .max = IRONLAKE_VCO_MAX },
|
|
+ .n = { .min = IRONLAKE_DAC_N_MIN, .max = IRONLAKE_DAC_N_MAX },
|
|
+ .m = { .min = IRONLAKE_DAC_M_MIN, .max = IRONLAKE_DAC_M_MAX },
|
|
+ .m1 = { .min = IRONLAKE_M1_MIN, .max = IRONLAKE_M1_MAX },
|
|
+ .m2 = { .min = IRONLAKE_M2_MIN, .max = IRONLAKE_M2_MAX },
|
|
+ .p = { .min = IRONLAKE_DAC_P_MIN, .max = IRONLAKE_DAC_P_MAX },
|
|
+ .p1 = { .min = IRONLAKE_DAC_P1_MIN, .max = IRONLAKE_DAC_P1_MAX },
|
|
+ .p2 = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
|
|
+ .p2_slow = IRONLAKE_DAC_P2_SLOW,
|
|
+ .p2_fast = IRONLAKE_DAC_P2_FAST },
|
|
+ .find_pll = intel_g4x_find_best_PLL,
|
|
};
|
|
|
|
-static const intel_limit_t intel_limits_igdng_lvds = {
|
|
- .dot = { .min = IGDNG_DOT_MIN, .max = IGDNG_DOT_MAX },
|
|
- .vco = { .min = IGDNG_VCO_MIN, .max = IGDNG_VCO_MAX },
|
|
- .n = { .min = IGDNG_N_MIN, .max = IGDNG_N_MAX },
|
|
- .m = { .min = IGDNG_M_MIN, .max = IGDNG_M_MAX },
|
|
- .m1 = { .min = IGDNG_M1_MIN, .max = IGDNG_M1_MAX },
|
|
- .m2 = { .min = IGDNG_M2_MIN, .max = IGDNG_M2_MAX },
|
|
- .p = { .min = IGDNG_P_LVDS_MIN, .max = IGDNG_P_LVDS_MAX },
|
|
- .p1 = { .min = IGDNG_P1_MIN, .max = IGDNG_P1_MAX },
|
|
- .p2 = { .dot_limit = IGDNG_P2_DOT_LIMIT,
|
|
- .p2_slow = IGDNG_P2_LVDS_SLOW,
|
|
- .p2_fast = IGDNG_P2_LVDS_FAST },
|
|
- .find_pll = intel_igdng_find_best_PLL,
|
|
+static const intel_limit_t intel_limits_ironlake_single_lvds = {
|
|
+ .dot = { .min = IRONLAKE_DOT_MIN, .max = IRONLAKE_DOT_MAX },
|
|
+ .vco = { .min = IRONLAKE_VCO_MIN, .max = IRONLAKE_VCO_MAX },
|
|
+ .n = { .min = IRONLAKE_LVDS_S_N_MIN, .max = IRONLAKE_LVDS_S_N_MAX },
|
|
+ .m = { .min = IRONLAKE_LVDS_S_M_MIN, .max = IRONLAKE_LVDS_S_M_MAX },
|
|
+ .m1 = { .min = IRONLAKE_M1_MIN, .max = IRONLAKE_M1_MAX },
|
|
+ .m2 = { .min = IRONLAKE_M2_MIN, .max = IRONLAKE_M2_MAX },
|
|
+ .p = { .min = IRONLAKE_LVDS_S_P_MIN, .max = IRONLAKE_LVDS_S_P_MAX },
|
|
+ .p1 = { .min = IRONLAKE_LVDS_S_P1_MIN, .max = IRONLAKE_LVDS_S_P1_MAX },
|
|
+ .p2 = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
|
|
+ .p2_slow = IRONLAKE_LVDS_S_P2_SLOW,
|
|
+ .p2_fast = IRONLAKE_LVDS_S_P2_FAST },
|
|
+ .find_pll = intel_g4x_find_best_PLL,
|
|
};
|
|
|
|
-static const intel_limit_t *intel_igdng_limit(struct drm_crtc *crtc)
|
|
+static const intel_limit_t intel_limits_ironlake_dual_lvds = {
|
|
+ .dot = { .min = IRONLAKE_DOT_MIN, .max = IRONLAKE_DOT_MAX },
|
|
+ .vco = { .min = IRONLAKE_VCO_MIN, .max = IRONLAKE_VCO_MAX },
|
|
+ .n = { .min = IRONLAKE_LVDS_D_N_MIN, .max = IRONLAKE_LVDS_D_N_MAX },
|
|
+ .m = { .min = IRONLAKE_LVDS_D_M_MIN, .max = IRONLAKE_LVDS_D_M_MAX },
|
|
+ .m1 = { .min = IRONLAKE_M1_MIN, .max = IRONLAKE_M1_MAX },
|
|
+ .m2 = { .min = IRONLAKE_M2_MIN, .max = IRONLAKE_M2_MAX },
|
|
+ .p = { .min = IRONLAKE_LVDS_D_P_MIN, .max = IRONLAKE_LVDS_D_P_MAX },
|
|
+ .p1 = { .min = IRONLAKE_LVDS_D_P1_MIN, .max = IRONLAKE_LVDS_D_P1_MAX },
|
|
+ .p2 = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
|
|
+ .p2_slow = IRONLAKE_LVDS_D_P2_SLOW,
|
|
+ .p2_fast = IRONLAKE_LVDS_D_P2_FAST },
|
|
+ .find_pll = intel_g4x_find_best_PLL,
|
|
+};
|
|
+
|
|
+static const intel_limit_t intel_limits_ironlake_single_lvds_100m = {
|
|
+ .dot = { .min = IRONLAKE_DOT_MIN, .max = IRONLAKE_DOT_MAX },
|
|
+ .vco = { .min = IRONLAKE_VCO_MIN, .max = IRONLAKE_VCO_MAX },
|
|
+ .n = { .min = IRONLAKE_LVDS_S_SSC_N_MIN, .max = IRONLAKE_LVDS_S_SSC_N_MAX },
|
|
+ .m = { .min = IRONLAKE_LVDS_S_SSC_M_MIN, .max = IRONLAKE_LVDS_S_SSC_M_MAX },
|
|
+ .m1 = { .min = IRONLAKE_M1_MIN, .max = IRONLAKE_M1_MAX },
|
|
+ .m2 = { .min = IRONLAKE_M2_MIN, .max = IRONLAKE_M2_MAX },
|
|
+ .p = { .min = IRONLAKE_LVDS_S_SSC_P_MIN, .max = IRONLAKE_LVDS_S_SSC_P_MAX },
|
|
+ .p1 = { .min = IRONLAKE_LVDS_S_SSC_P1_MIN,.max = IRONLAKE_LVDS_S_SSC_P1_MAX },
|
|
+ .p2 = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
|
|
+ .p2_slow = IRONLAKE_LVDS_S_SSC_P2_SLOW,
|
|
+ .p2_fast = IRONLAKE_LVDS_S_SSC_P2_FAST },
|
|
+ .find_pll = intel_g4x_find_best_PLL,
|
|
+};
|
|
+
|
|
+static const intel_limit_t intel_limits_ironlake_dual_lvds_100m = {
|
|
+ .dot = { .min = IRONLAKE_DOT_MIN, .max = IRONLAKE_DOT_MAX },
|
|
+ .vco = { .min = IRONLAKE_VCO_MIN, .max = IRONLAKE_VCO_MAX },
|
|
+ .n = { .min = IRONLAKE_LVDS_D_SSC_N_MIN, .max = IRONLAKE_LVDS_D_SSC_N_MAX },
|
|
+ .m = { .min = IRONLAKE_LVDS_D_SSC_M_MIN, .max = IRONLAKE_LVDS_D_SSC_M_MAX },
|
|
+ .m1 = { .min = IRONLAKE_M1_MIN, .max = IRONLAKE_M1_MAX },
|
|
+ .m2 = { .min = IRONLAKE_M2_MIN, .max = IRONLAKE_M2_MAX },
|
|
+ .p = { .min = IRONLAKE_LVDS_D_SSC_P_MIN, .max = IRONLAKE_LVDS_D_SSC_P_MAX },
|
|
+ .p1 = { .min = IRONLAKE_LVDS_D_SSC_P1_MIN,.max = IRONLAKE_LVDS_D_SSC_P1_MAX },
|
|
+ .p2 = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
|
|
+ .p2_slow = IRONLAKE_LVDS_D_SSC_P2_SLOW,
|
|
+ .p2_fast = IRONLAKE_LVDS_D_SSC_P2_FAST },
|
|
+ .find_pll = intel_g4x_find_best_PLL,
|
|
+};
|
|
+
|
|
+static const intel_limit_t intel_limits_ironlake_display_port = {
|
|
+ .dot = { .min = IRONLAKE_DOT_MIN,
|
|
+ .max = IRONLAKE_DOT_MAX },
|
|
+ .vco = { .min = IRONLAKE_VCO_MIN,
|
|
+ .max = IRONLAKE_VCO_MAX},
|
|
+ .n = { .min = IRONLAKE_DP_N_MIN,
|
|
+ .max = IRONLAKE_DP_N_MAX },
|
|
+ .m = { .min = IRONLAKE_DP_M_MIN,
|
|
+ .max = IRONLAKE_DP_M_MAX },
|
|
+ .m1 = { .min = IRONLAKE_M1_MIN,
|
|
+ .max = IRONLAKE_M1_MAX },
|
|
+ .m2 = { .min = IRONLAKE_M2_MIN,
|
|
+ .max = IRONLAKE_M2_MAX },
|
|
+ .p = { .min = IRONLAKE_DP_P_MIN,
|
|
+ .max = IRONLAKE_DP_P_MAX },
|
|
+ .p1 = { .min = IRONLAKE_DP_P1_MIN,
|
|
+ .max = IRONLAKE_DP_P1_MAX},
|
|
+ .p2 = { .dot_limit = IRONLAKE_DP_P2_LIMIT,
|
|
+ .p2_slow = IRONLAKE_DP_P2_SLOW,
|
|
+ .p2_fast = IRONLAKE_DP_P2_FAST },
|
|
+ .find_pll = intel_find_pll_ironlake_dp,
|
|
+};
|
|
+
|
|
+static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc)
|
|
{
|
|
+ struct drm_device *dev = crtc->dev;
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
const intel_limit_t *limit;
|
|
- if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
|
|
- limit = &intel_limits_igdng_lvds;
|
|
+ int refclk = 120;
|
|
+
|
|
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
|
|
+ if (dev_priv->lvds_use_ssc && dev_priv->lvds_ssc_freq == 100)
|
|
+ refclk = 100;
|
|
+
|
|
+ if ((I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) ==
|
|
+ LVDS_CLKB_POWER_UP) {
|
|
+ /* LVDS dual channel */
|
|
+ if (refclk == 100)
|
|
+ limit = &intel_limits_ironlake_dual_lvds_100m;
|
|
+ else
|
|
+ limit = &intel_limits_ironlake_dual_lvds;
|
|
+ } else {
|
|
+ if (refclk == 100)
|
|
+ limit = &intel_limits_ironlake_single_lvds_100m;
|
|
+ else
|
|
+ limit = &intel_limits_ironlake_single_lvds;
|
|
+ }
|
|
+ } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT) ||
|
|
+ HAS_eDP)
|
|
+ limit = &intel_limits_ironlake_display_port;
|
|
else
|
|
- limit = &intel_limits_igdng_sdvo;
|
|
+ limit = &intel_limits_ironlake_dac;
|
|
|
|
return limit;
|
|
}
|
|
@@ -557,20 +690,20 @@ static const intel_limit_t *intel_limit(struct drm_crtc *crtc)
|
|
struct drm_device *dev = crtc->dev;
|
|
const intel_limit_t *limit;
|
|
|
|
- if (IS_IGDNG(dev))
|
|
- limit = intel_igdng_limit(crtc);
|
|
+ if (IS_IRONLAKE(dev))
|
|
+ limit = intel_ironlake_limit(crtc);
|
|
else if (IS_G4X(dev)) {
|
|
limit = intel_g4x_limit(crtc);
|
|
- } else if (IS_I9XX(dev) && !IS_IGD(dev)) {
|
|
+ } else if (IS_I9XX(dev) && !IS_PINEVIEW(dev)) {
|
|
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
|
|
limit = &intel_limits_i9xx_lvds;
|
|
else
|
|
limit = &intel_limits_i9xx_sdvo;
|
|
- } else if (IS_IGD(dev)) {
|
|
+ } else if (IS_PINEVIEW(dev)) {
|
|
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
|
|
- limit = &intel_limits_igd_lvds;
|
|
+ limit = &intel_limits_pineview_lvds;
|
|
else
|
|
- limit = &intel_limits_igd_sdvo;
|
|
+ limit = &intel_limits_pineview_sdvo;
|
|
} else {
|
|
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
|
|
limit = &intel_limits_i8xx_lvds;
|
|
@@ -580,8 +713,8 @@ static const intel_limit_t *intel_limit(struct drm_crtc *crtc)
|
|
return limit;
|
|
}
|
|
|
|
-/* m1 is reserved as 0 in IGD, n is a ring counter */
|
|
-static void igd_clock(int refclk, intel_clock_t *clock)
|
|
+/* m1 is reserved as 0 in Pineview, n is a ring counter */
|
|
+static void pineview_clock(int refclk, intel_clock_t *clock)
|
|
{
|
|
clock->m = clock->m2 + 2;
|
|
clock->p = clock->p1 * clock->p2;
|
|
@@ -591,8 +724,8 @@ static void igd_clock(int refclk, intel_clock_t *clock)
|
|
|
|
static void intel_clock(struct drm_device *dev, int refclk, intel_clock_t *clock)
|
|
{
|
|
- if (IS_IGD(dev)) {
|
|
- igd_clock(refclk, clock);
|
|
+ if (IS_PINEVIEW(dev)) {
|
|
+ pineview_clock(refclk, clock);
|
|
return;
|
|
}
|
|
clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2);
|
|
@@ -657,7 +790,7 @@ static bool intel_PLL_is_valid(struct drm_crtc *crtc, intel_clock_t *clock)
|
|
INTELPllInvalid ("m2 out of range\n");
|
|
if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1)
|
|
INTELPllInvalid ("m1 out of range\n");
|
|
- if (clock->m1 <= clock->m2 && !IS_IGD(dev))
|
|
+ if (clock->m1 <= clock->m2 && !IS_PINEVIEW(dev))
|
|
INTELPllInvalid ("m1 <= m2\n");
|
|
if (clock->m < limit->m.min || limit->m.max < clock->m)
|
|
INTELPllInvalid ("m out of range\n");
|
|
@@ -706,16 +839,17 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
|
|
|
|
memset (best_clock, 0, sizeof (*best_clock));
|
|
|
|
- for (clock.p1 = limit->p1.max; clock.p1 >= limit->p1.min; clock.p1--) {
|
|
- for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max;
|
|
- clock.m1++) {
|
|
- for (clock.m2 = limit->m2.min;
|
|
- clock.m2 <= limit->m2.max; clock.m2++) {
|
|
- /* m1 is always 0 in IGD */
|
|
- if (clock.m2 >= clock.m1 && !IS_IGD(dev))
|
|
- break;
|
|
- for (clock.n = limit->n.min;
|
|
- clock.n <= limit->n.max; clock.n++) {
|
|
+ for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max;
|
|
+ clock.m1++) {
|
|
+ for (clock.m2 = limit->m2.min;
|
|
+ clock.m2 <= limit->m2.max; clock.m2++) {
|
|
+ /* m1 is always 0 in Pineview */
|
|
+ if (clock.m2 >= clock.m1 && !IS_PINEVIEW(dev))
|
|
+ break;
|
|
+ for (clock.n = limit->n.min;
|
|
+ clock.n <= limit->n.max; clock.n++) {
|
|
+ for (clock.p1 = limit->p1.min;
|
|
+ clock.p1 <= limit->p1.max; clock.p1++) {
|
|
int this_err;
|
|
|
|
intel_clock(dev, refclk, &clock);
|
|
@@ -736,46 +870,6 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
|
|
return (err != target);
|
|
}
|
|
|
|
-
|
|
-static bool
|
|
-intel_find_best_reduced_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
|
|
- int target, int refclk, intel_clock_t *best_clock)
|
|
-
|
|
-{
|
|
- struct drm_device *dev = crtc->dev;
|
|
- intel_clock_t clock;
|
|
- int err = target;
|
|
- bool found = false;
|
|
-
|
|
- memcpy(&clock, best_clock, sizeof(intel_clock_t));
|
|
-
|
|
- for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) {
|
|
- for (clock.m2 = limit->m2.min; clock.m2 <= limit->m2.max; clock.m2++) {
|
|
- /* m1 is always 0 in IGD */
|
|
- if (clock.m2 >= clock.m1 && !IS_IGD(dev))
|
|
- break;
|
|
- for (clock.n = limit->n.min; clock.n <= limit->n.max;
|
|
- clock.n++) {
|
|
- int this_err;
|
|
-
|
|
- intel_clock(dev, refclk, &clock);
|
|
-
|
|
- if (!intel_PLL_is_valid(crtc, &clock))
|
|
- continue;
|
|
-
|
|
- this_err = abs(clock.dot - target);
|
|
- if (this_err < err) {
|
|
- *best_clock = clock;
|
|
- err = this_err;
|
|
- found = true;
|
|
- }
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- return found;
|
|
-}
|
|
-
|
|
static bool
|
|
intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
|
|
int target, int refclk, intel_clock_t *best_clock)
|
|
@@ -790,7 +884,13 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
|
|
found = false;
|
|
|
|
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
|
|
- if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
|
|
+ int lvds_reg;
|
|
+
|
|
+ if (IS_IRONLAKE(dev))
|
|
+ lvds_reg = PCH_LVDS;
|
|
+ else
|
|
+ lvds_reg = LVDS;
|
|
+ if ((I915_READ(lvds_reg) & LVDS_CLKB_POWER_MASK) ==
|
|
LVDS_CLKB_POWER_UP)
|
|
clock.p2 = limit->p2.p2_fast;
|
|
else
|
|
@@ -833,11 +933,16 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
|
|
}
|
|
|
|
static bool
|
|
-intel_find_pll_igdng_dp(const intel_limit_t *limit, struct drm_crtc *crtc,
|
|
- int target, int refclk, intel_clock_t *best_clock)
|
|
+intel_find_pll_ironlake_dp(const intel_limit_t *limit, struct drm_crtc *crtc,
|
|
+ int target, int refclk, intel_clock_t *best_clock)
|
|
{
|
|
struct drm_device *dev = crtc->dev;
|
|
intel_clock_t clock;
|
|
+
|
|
+ /* return directly when it is eDP */
|
|
+ if (HAS_eDP)
|
|
+ return true;
|
|
+
|
|
if (target < 200000) {
|
|
clock.n = 1;
|
|
clock.p1 = 2;
|
|
@@ -856,68 +961,6 @@ intel_find_pll_igdng_dp(const intel_limit_t *limit, struct drm_crtc *crtc,
|
|
return true;
|
|
}
|
|
|
|
-static bool
|
|
-intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
|
|
- int target, int refclk, intel_clock_t *best_clock)
|
|
-{
|
|
- struct drm_device *dev = crtc->dev;
|
|
- struct drm_i915_private *dev_priv = dev->dev_private;
|
|
- intel_clock_t clock;
|
|
- int err_most = 47;
|
|
- int err_min = 10000;
|
|
-
|
|
- /* eDP has only 2 clock choice, no n/m/p setting */
|
|
- if (HAS_eDP)
|
|
- return true;
|
|
-
|
|
- if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT))
|
|
- return intel_find_pll_igdng_dp(limit, crtc, target,
|
|
- refclk, best_clock);
|
|
-
|
|
- if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
|
|
- if ((I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) ==
|
|
- LVDS_CLKB_POWER_UP)
|
|
- clock.p2 = limit->p2.p2_fast;
|
|
- else
|
|
- clock.p2 = limit->p2.p2_slow;
|
|
- } else {
|
|
- if (target < limit->p2.dot_limit)
|
|
- clock.p2 = limit->p2.p2_slow;
|
|
- else
|
|
- clock.p2 = limit->p2.p2_fast;
|
|
- }
|
|
-
|
|
- memset(best_clock, 0, sizeof(*best_clock));
|
|
- for (clock.p1 = limit->p1.max; clock.p1 >= limit->p1.min; clock.p1--) {
|
|
- /* based on hardware requriment prefer smaller n to precision */
|
|
- for (clock.n = limit->n.min; clock.n <= limit->n.max; clock.n++) {
|
|
- /* based on hardware requirment prefere larger m1,m2 */
|
|
- for (clock.m1 = limit->m1.max;
|
|
- clock.m1 >= limit->m1.min; clock.m1--) {
|
|
- for (clock.m2 = limit->m2.max;
|
|
- clock.m2 >= limit->m2.min; clock.m2--) {
|
|
- int this_err;
|
|
-
|
|
- intel_clock(dev, refclk, &clock);
|
|
- if (!intel_PLL_is_valid(crtc, &clock))
|
|
- continue;
|
|
- this_err = abs((10000 - (target*10000/clock.dot)));
|
|
- if (this_err < err_most) {
|
|
- *best_clock = clock;
|
|
- /* found on first matching */
|
|
- goto out;
|
|
- } else if (this_err < err_min) {
|
|
- *best_clock = clock;
|
|
- err_min = this_err;
|
|
- }
|
|
- }
|
|
- }
|
|
- }
|
|
- }
|
|
-out:
|
|
- return true;
|
|
-}
|
|
-
|
|
/* DisplayPort has only two frequencies, 162MHz and 270MHz */
|
|
static bool
|
|
intel_find_pll_g4x_dp(const intel_limit_t *limit, struct drm_crtc *crtc,
|
|
@@ -949,7 +992,7 @@ void
|
|
intel_wait_for_vblank(struct drm_device *dev)
|
|
{
|
|
/* Wait for 20ms, i.e. one cycle at 50hz. */
|
|
- mdelay(20);
|
|
+ msleep(20);
|
|
}
|
|
|
|
/* Parameters have changed, update FBC info */
|
|
@@ -988,13 +1031,15 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
|
|
|
|
/* enable it... */
|
|
fbc_ctl = FBC_CTL_EN | FBC_CTL_PERIODIC;
|
|
+ if (IS_I945GM(dev))
|
|
+ fbc_ctl |= FBC_C3_IDLE; /* 945 needs special SR handling */
|
|
fbc_ctl |= (dev_priv->cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT;
|
|
fbc_ctl |= (interval & 0x2fff) << FBC_CTL_INTERVAL_SHIFT;
|
|
if (obj_priv->tiling_mode != I915_TILING_NONE)
|
|
fbc_ctl |= dev_priv->cfb_fence;
|
|
I915_WRITE(FBC_CONTROL, fbc_ctl);
|
|
|
|
- DRM_DEBUG("enabled FBC, pitch %ld, yoff %d, plane %d, ",
|
|
+ DRM_DEBUG_KMS("enabled FBC, pitch %ld, yoff %d, plane %d, ",
|
|
dev_priv->cfb_pitch, crtc->y, dev_priv->cfb_plane);
|
|
}
|
|
|
|
@@ -1017,7 +1062,7 @@ void i8xx_disable_fbc(struct drm_device *dev)
|
|
|
|
intel_wait_for_vblank(dev);
|
|
|
|
- DRM_DEBUG("disabled FBC\n");
|
|
+ DRM_DEBUG_KMS("disabled FBC\n");
|
|
}
|
|
|
|
static bool i8xx_fbc_enabled(struct drm_crtc *crtc)
|
|
@@ -1062,7 +1107,7 @@ static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
|
|
/* enable it... */
|
|
I915_WRITE(DPFC_CONTROL, I915_READ(DPFC_CONTROL) | DPFC_CTL_EN);
|
|
|
|
- DRM_DEBUG("enabled fbc on plane %d\n", intel_crtc->plane);
|
|
+ DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane);
|
|
}
|
|
|
|
void g4x_disable_fbc(struct drm_device *dev)
|
|
@@ -1076,7 +1121,7 @@ void g4x_disable_fbc(struct drm_device *dev)
|
|
I915_WRITE(DPFC_CONTROL, dpfc_ctl);
|
|
intel_wait_for_vblank(dev);
|
|
|
|
- DRM_DEBUG("disabled FBC\n");
|
|
+ DRM_DEBUG_KMS("disabled FBC\n");
|
|
}
|
|
|
|
static bool g4x_fbc_enabled(struct drm_crtc *crtc)
|
|
@@ -1141,25 +1186,27 @@ static void intel_update_fbc(struct drm_crtc *crtc,
|
|
* - going to an unsupported config (interlace, pixel multiply, etc.)
|
|
*/
|
|
if (intel_fb->obj->size > dev_priv->cfb_size) {
|
|
- DRM_DEBUG("framebuffer too large, disabling compression\n");
|
|
+ DRM_DEBUG_KMS("framebuffer too large, disabling "
|
|
+ "compression\n");
|
|
goto out_disable;
|
|
}
|
|
if ((mode->flags & DRM_MODE_FLAG_INTERLACE) ||
|
|
(mode->flags & DRM_MODE_FLAG_DBLSCAN)) {
|
|
- DRM_DEBUG("mode incompatible with compression, disabling\n");
|
|
+ DRM_DEBUG_KMS("mode incompatible with compression, "
|
|
+ "disabling\n");
|
|
goto out_disable;
|
|
}
|
|
if ((mode->hdisplay > 2048) ||
|
|
(mode->vdisplay > 1536)) {
|
|
- DRM_DEBUG("mode too large for compression, disabling\n");
|
|
+ DRM_DEBUG_KMS("mode too large for compression, disabling\n");
|
|
goto out_disable;
|
|
}
|
|
if ((IS_I915GM(dev) || IS_I945GM(dev)) && plane != 0) {
|
|
- DRM_DEBUG("plane not 0, disabling compression\n");
|
|
+ DRM_DEBUG_KMS("plane not 0, disabling compression\n");
|
|
goto out_disable;
|
|
}
|
|
if (obj_priv->tiling_mode != I915_TILING_X) {
|
|
- DRM_DEBUG("framebuffer not tiled, disabling compression\n");
|
|
+ DRM_DEBUG_KMS("framebuffer not tiled, disabling compression\n");
|
|
goto out_disable;
|
|
}
|
|
|
|
@@ -1181,13 +1228,57 @@ static void intel_update_fbc(struct drm_crtc *crtc,
|
|
return;
|
|
|
|
out_disable:
|
|
- DRM_DEBUG("unsupported config, disabling FBC\n");
|
|
+ DRM_DEBUG_KMS("unsupported config, disabling FBC\n");
|
|
/* Multiple disables should be harmless */
|
|
if (dev_priv->display.fbc_enabled(crtc))
|
|
dev_priv->display.disable_fbc(dev);
|
|
}
|
|
|
|
static int
|
|
+intel_pin_and_fence_fb_obj(struct drm_device *dev, struct drm_gem_object *obj)
|
|
+{
|
|
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
|
|
+ u32 alignment;
|
|
+ int ret;
|
|
+
|
|
+ switch (obj_priv->tiling_mode) {
|
|
+ case I915_TILING_NONE:
|
|
+ alignment = 64 * 1024;
|
|
+ break;
|
|
+ case I915_TILING_X:
|
|
+ /* 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 -EINVAL;
|
|
+ default:
|
|
+ BUG();
|
|
+ }
|
|
+
|
|
+ ret = i915_gem_object_pin(obj, alignment);
|
|
+ if (ret != 0)
|
|
+ return ret;
|
|
+
|
|
+ /* Install a fence for tiled scan-out. Pre-i965 always needs a
|
|
+ * fence, whereas 965+ only requires a fence if using
|
|
+ * framebuffer compression. For simplicity, we always install
|
|
+ * a fence as the cost is not that onerous.
|
|
+ */
|
|
+ if (obj_priv->fence_reg == I915_FENCE_REG_NONE &&
|
|
+ obj_priv->tiling_mode != I915_TILING_NONE) {
|
|
+ ret = i915_gem_object_get_fence_reg(obj);
|
|
+ if (ret != 0) {
|
|
+ i915_gem_object_unpin(obj);
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
|
|
struct drm_framebuffer *old_fb)
|
|
{
|
|
@@ -1206,12 +1297,12 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
|
|
int dspstride = (plane == 0) ? DSPASTRIDE : DSPBSTRIDE;
|
|
int dsptileoff = (plane == 0 ? DSPATILEOFF : DSPBTILEOFF);
|
|
int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR;
|
|
- u32 dspcntr, alignment;
|
|
+ u32 dspcntr;
|
|
int ret;
|
|
|
|
/* no fb bound */
|
|
if (!crtc->fb) {
|
|
- DRM_DEBUG("No FB bound\n");
|
|
+ DRM_DEBUG_KMS("No FB bound\n");
|
|
return 0;
|
|
}
|
|
|
|
@@ -1228,50 +1319,20 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
|
|
obj = intel_fb->obj;
|
|
obj_priv = obj->driver_private;
|
|
|
|
- switch (obj_priv->tiling_mode) {
|
|
- case I915_TILING_NONE:
|
|
- alignment = 64 * 1024;
|
|
- break;
|
|
- case I915_TILING_X:
|
|
- /* 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 -EINVAL;
|
|
- default:
|
|
- BUG();
|
|
- }
|
|
-
|
|
mutex_lock(&dev->struct_mutex);
|
|
- ret = i915_gem_object_pin(obj, alignment);
|
|
+ ret = intel_pin_and_fence_fb_obj(dev, obj);
|
|
if (ret != 0) {
|
|
mutex_unlock(&dev->struct_mutex);
|
|
return ret;
|
|
}
|
|
|
|
- ret = i915_gem_object_set_to_gtt_domain(obj, 1);
|
|
+ ret = i915_gem_object_set_to_display_plane(obj);
|
|
if (ret != 0) {
|
|
i915_gem_object_unpin(obj);
|
|
mutex_unlock(&dev->struct_mutex);
|
|
return ret;
|
|
}
|
|
|
|
- /* Install a fence for tiled scan-out. Pre-i965 always needs a fence,
|
|
- * whereas 965+ only requires a fence if using framebuffer compression.
|
|
- * For simplicity, we always install a fence as the cost is not that onerous.
|
|
- */
|
|
- if (obj_priv->fence_reg == I915_FENCE_REG_NONE &&
|
|
- obj_priv->tiling_mode != I915_TILING_NONE) {
|
|
- ret = i915_gem_object_get_fence_reg(obj);
|
|
- if (ret != 0) {
|
|
- i915_gem_object_unpin(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;
|
|
@@ -1287,7 +1348,10 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
|
|
break;
|
|
case 24:
|
|
case 32:
|
|
- dspcntr |= DISPPLANE_32BPP_NO_ALPHA;
|
|
+ if (crtc->fb->depth == 30)
|
|
+ dspcntr |= DISPPLANE_32BPP_30BIT_NO_ALPHA;
|
|
+ else
|
|
+ dspcntr |= DISPPLANE_32BPP_NO_ALPHA;
|
|
break;
|
|
default:
|
|
DRM_ERROR("Unknown color depth\n");
|
|
@@ -1302,7 +1366,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
|
|
dspcntr &= ~DISPPLANE_TILED;
|
|
}
|
|
|
|
- if (IS_IGDNG(dev))
|
|
+ if (IS_IRONLAKE(dev))
|
|
/* must disable */
|
|
dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
|
|
|
|
@@ -1311,7 +1375,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
|
|
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);
|
|
+ DRM_DEBUG_KMS("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);
|
|
@@ -1363,7 +1427,7 @@ static void i915_disable_vga (struct drm_device *dev)
|
|
u8 sr1;
|
|
u32 vga_reg;
|
|
|
|
- if (IS_IGDNG(dev))
|
|
+ if (IS_IRONLAKE(dev))
|
|
vga_reg = CPU_VGACNTRL;
|
|
else
|
|
vga_reg = VGACNTRL;
|
|
@@ -1379,19 +1443,19 @@ static void i915_disable_vga (struct drm_device *dev)
|
|
I915_WRITE(vga_reg, VGA_DISP_DISABLE);
|
|
}
|
|
|
|
-static void igdng_disable_pll_edp (struct drm_crtc *crtc)
|
|
+static void ironlake_disable_pll_edp (struct drm_crtc *crtc)
|
|
{
|
|
struct drm_device *dev = crtc->dev;
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
u32 dpa_ctl;
|
|
|
|
- DRM_DEBUG("\n");
|
|
+ DRM_DEBUG_KMS("\n");
|
|
dpa_ctl = I915_READ(DP_A);
|
|
dpa_ctl &= ~DP_PLL_ENABLE;
|
|
I915_WRITE(DP_A, dpa_ctl);
|
|
}
|
|
|
|
-static void igdng_enable_pll_edp (struct drm_crtc *crtc)
|
|
+static void ironlake_enable_pll_edp (struct drm_crtc *crtc)
|
|
{
|
|
struct drm_device *dev = crtc->dev;
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
@@ -1404,13 +1468,13 @@ static void igdng_enable_pll_edp (struct drm_crtc *crtc)
|
|
}
|
|
|
|
|
|
-static void igdng_set_pll_edp (struct drm_crtc *crtc, int clock)
|
|
+static void ironlake_set_pll_edp (struct drm_crtc *crtc, int clock)
|
|
{
|
|
struct drm_device *dev = crtc->dev;
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
u32 dpa_ctl;
|
|
|
|
- DRM_DEBUG("eDP PLL enable for clock %d\n", clock);
|
|
+ DRM_DEBUG_KMS("eDP PLL enable for clock %d\n", clock);
|
|
dpa_ctl = I915_READ(DP_A);
|
|
dpa_ctl &= ~DP_PLL_FREQ_MASK;
|
|
|
|
@@ -1440,7 +1504,7 @@ static void igdng_set_pll_edp (struct drm_crtc *crtc, int clock)
|
|
udelay(500);
|
|
}
|
|
|
|
-static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|
+static void ironlake_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|
{
|
|
struct drm_device *dev = crtc->dev;
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
@@ -1485,7 +1549,7 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|
case DRM_MODE_DPMS_ON:
|
|
case DRM_MODE_DPMS_STANDBY:
|
|
case DRM_MODE_DPMS_SUSPEND:
|
|
- DRM_DEBUG("crtc %d dpms on\n", pipe);
|
|
+ DRM_DEBUG_KMS("crtc %d dpms on\n", pipe);
|
|
|
|
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
|
|
temp = I915_READ(PCH_LVDS);
|
|
@@ -1497,7 +1561,7 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|
|
|
if (HAS_eDP) {
|
|
/* enable eDP PLL */
|
|
- igdng_enable_pll_edp(crtc);
|
|
+ ironlake_enable_pll_edp(crtc);
|
|
} else {
|
|
/* enable PCH DPLL */
|
|
temp = I915_READ(pch_dpll_reg);
|
|
@@ -1520,7 +1584,7 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|
I915_READ(fdi_rx_reg);
|
|
udelay(200);
|
|
|
|
- /* Enable CPU FDI TX PLL, always on for IGDNG */
|
|
+ /* Enable CPU FDI TX PLL, always on for Ironlake */
|
|
temp = I915_READ(fdi_tx_reg);
|
|
if ((temp & FDI_TX_PLL_ENABLE) == 0) {
|
|
I915_WRITE(fdi_tx_reg, temp | FDI_TX_PLL_ENABLE);
|
|
@@ -1587,12 +1651,13 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|
udelay(150);
|
|
|
|
temp = I915_READ(fdi_rx_iir_reg);
|
|
- DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
|
|
+ DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n", temp);
|
|
|
|
if ((temp & FDI_RX_BIT_LOCK) == 0) {
|
|
for (j = 0; j < tries; j++) {
|
|
temp = I915_READ(fdi_rx_iir_reg);
|
|
- DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
|
|
+ DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n",
|
|
+ temp);
|
|
if (temp & FDI_RX_BIT_LOCK)
|
|
break;
|
|
udelay(200);
|
|
@@ -1601,11 +1666,11 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|
I915_WRITE(fdi_rx_iir_reg,
|
|
temp | FDI_RX_BIT_LOCK);
|
|
else
|
|
- DRM_DEBUG("train 1 fail\n");
|
|
+ DRM_DEBUG_KMS("train 1 fail\n");
|
|
} else {
|
|
I915_WRITE(fdi_rx_iir_reg,
|
|
temp | FDI_RX_BIT_LOCK);
|
|
- DRM_DEBUG("train 1 ok 2!\n");
|
|
+ DRM_DEBUG_KMS("train 1 ok 2!\n");
|
|
}
|
|
temp = I915_READ(fdi_tx_reg);
|
|
temp &= ~FDI_LINK_TRAIN_NONE;
|
|
@@ -1620,12 +1685,13 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|
udelay(150);
|
|
|
|
temp = I915_READ(fdi_rx_iir_reg);
|
|
- DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
|
|
+ DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n", temp);
|
|
|
|
if ((temp & FDI_RX_SYMBOL_LOCK) == 0) {
|
|
for (j = 0; j < tries; j++) {
|
|
temp = I915_READ(fdi_rx_iir_reg);
|
|
- DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
|
|
+ DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n",
|
|
+ temp);
|
|
if (temp & FDI_RX_SYMBOL_LOCK)
|
|
break;
|
|
udelay(200);
|
|
@@ -1633,15 +1699,15 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|
if (j != tries) {
|
|
I915_WRITE(fdi_rx_iir_reg,
|
|
temp | FDI_RX_SYMBOL_LOCK);
|
|
- DRM_DEBUG("train 2 ok 1!\n");
|
|
+ DRM_DEBUG_KMS("train 2 ok 1!\n");
|
|
} else
|
|
- DRM_DEBUG("train 2 fail\n");
|
|
+ DRM_DEBUG_KMS("train 2 fail\n");
|
|
} else {
|
|
I915_WRITE(fdi_rx_iir_reg,
|
|
temp | FDI_RX_SYMBOL_LOCK);
|
|
- DRM_DEBUG("train 2 ok 2!\n");
|
|
+ DRM_DEBUG_KMS("train 2 ok 2!\n");
|
|
}
|
|
- DRM_DEBUG("train done\n");
|
|
+ DRM_DEBUG_KMS("train done\n");
|
|
|
|
/* set transcoder timing */
|
|
I915_WRITE(trans_htot_reg, I915_READ(cpu_htot_reg));
|
|
@@ -1689,8 +1755,9 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|
|
|
break;
|
|
case DRM_MODE_DPMS_OFF:
|
|
- DRM_DEBUG("crtc %d dpms off\n", pipe);
|
|
+ DRM_DEBUG_KMS("crtc %d dpms off\n", pipe);
|
|
|
|
+ drm_vblank_off(dev, pipe);
|
|
/* Disable display plane */
|
|
temp = I915_READ(dspcntr_reg);
|
|
if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
|
|
@@ -1715,12 +1782,13 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|
udelay(500);
|
|
continue;
|
|
} else {
|
|
- DRM_DEBUG("pipe %d off delay\n", pipe);
|
|
+ DRM_DEBUG_KMS("pipe %d off delay\n",
|
|
+ pipe);
|
|
break;
|
|
}
|
|
}
|
|
} else
|
|
- DRM_DEBUG("crtc %d is disabled\n", pipe);
|
|
+ DRM_DEBUG_KMS("crtc %d is disabled\n", pipe);
|
|
|
|
udelay(100);
|
|
|
|
@@ -1779,7 +1847,8 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|
udelay(500);
|
|
continue;
|
|
} else {
|
|
- DRM_DEBUG("transcoder %d off delay\n", pipe);
|
|
+ DRM_DEBUG_KMS("transcoder %d off "
|
|
+ "delay\n", pipe);
|
|
break;
|
|
}
|
|
}
|
|
@@ -1800,7 +1869,7 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|
}
|
|
|
|
if (HAS_eDP) {
|
|
- igdng_disable_pll_edp(crtc);
|
|
+ ironlake_disable_pll_edp(crtc);
|
|
}
|
|
|
|
temp = I915_READ(fdi_rx_reg);
|
|
@@ -1827,6 +1896,37 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|
}
|
|
}
|
|
|
|
+static void intel_crtc_dpms_overlay(struct intel_crtc *intel_crtc, bool enable)
|
|
+{
|
|
+ struct intel_overlay *overlay;
|
|
+ int ret;
|
|
+
|
|
+ if (!enable && intel_crtc->overlay) {
|
|
+ overlay = intel_crtc->overlay;
|
|
+ mutex_lock(&overlay->dev->struct_mutex);
|
|
+ for (;;) {
|
|
+ ret = intel_overlay_switch_off(overlay);
|
|
+ if (ret == 0)
|
|
+ break;
|
|
+
|
|
+ ret = intel_overlay_recover_from_interrupt(overlay, 0);
|
|
+ if (ret != 0) {
|
|
+ /* overlay doesn't react anymore. Usually
|
|
+ * results in a black screen and an unkillable
|
|
+ * X server. */
|
|
+ BUG();
|
|
+ overlay->hw_wedged = HW_WEDGED;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ mutex_unlock(&overlay->dev->struct_mutex);
|
|
+ }
|
|
+ /* Let userspace switch the overlay on again. In most cases userspace
|
|
+ * has to recompute where to put it anyway. */
|
|
+
|
|
+ return;
|
|
+}
|
|
+
|
|
static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|
{
|
|
struct drm_device *dev = crtc->dev;
|
|
@@ -1885,12 +1985,13 @@ static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|
intel_update_fbc(crtc, &crtc->mode);
|
|
|
|
/* Give the overlay scaler a chance to enable if it's on this pipe */
|
|
- //intel_crtc_dpms_video(crtc, true); TODO
|
|
+ intel_crtc_dpms_overlay(intel_crtc, true);
|
|
break;
|
|
case DRM_MODE_DPMS_OFF:
|
|
intel_update_watermarks(dev);
|
|
+
|
|
/* Give the overlay scaler a chance to disable if it's on this pipe */
|
|
- //intel_crtc_dpms_video(crtc, FALSE); TODO
|
|
+ intel_crtc_dpms_overlay(intel_crtc, false);
|
|
drm_vblank_off(dev, pipe);
|
|
|
|
if (dev_priv->cfb_plane == plane &&
|
|
@@ -2010,7 +2111,7 @@ static bool intel_crtc_mode_fixup(struct drm_crtc *crtc,
|
|
struct drm_display_mode *adjusted_mode)
|
|
{
|
|
struct drm_device *dev = crtc->dev;
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
/* FDI link clock is fixed at 2.7G */
|
|
if (mode->clock * 3 > 27000 * 4)
|
|
return MODE_CLOCK_HIGH;
|
|
@@ -2086,7 +2187,7 @@ static int i830_get_display_clock_speed(struct drm_device *dev)
|
|
* Return the pipe currently connected to the panel fitter,
|
|
* or -1 if the panel fitter is not present or not in use
|
|
*/
|
|
-static int intel_panel_fitter_pipe (struct drm_device *dev)
|
|
+int intel_panel_fitter_pipe (struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
u32 pfit_control;
|
|
@@ -2130,9 +2231,8 @@ fdi_reduce_ratio(u32 *num, u32 *den)
|
|
#define LINK_N 0x80000
|
|
|
|
static void
|
|
-igdng_compute_m_n(int bits_per_pixel, int nlanes,
|
|
- int pixel_clock, int link_clock,
|
|
- struct fdi_m_n *m_n)
|
|
+ironlake_compute_m_n(int bits_per_pixel, int nlanes, int pixel_clock,
|
|
+ int link_clock, struct fdi_m_n *m_n)
|
|
{
|
|
u64 temp;
|
|
|
|
@@ -2160,34 +2260,34 @@ struct intel_watermark_params {
|
|
unsigned long cacheline_size;
|
|
};
|
|
|
|
-/* IGD has different values for various configs */
|
|
-static struct intel_watermark_params igd_display_wm = {
|
|
- IGD_DISPLAY_FIFO,
|
|
- IGD_MAX_WM,
|
|
- IGD_DFT_WM,
|
|
- IGD_GUARD_WM,
|
|
- IGD_FIFO_LINE_SIZE
|
|
+/* Pineview has different values for various configs */
|
|
+static struct intel_watermark_params pineview_display_wm = {
|
|
+ PINEVIEW_DISPLAY_FIFO,
|
|
+ PINEVIEW_MAX_WM,
|
|
+ PINEVIEW_DFT_WM,
|
|
+ PINEVIEW_GUARD_WM,
|
|
+ PINEVIEW_FIFO_LINE_SIZE
|
|
};
|
|
-static struct intel_watermark_params igd_display_hplloff_wm = {
|
|
- IGD_DISPLAY_FIFO,
|
|
- IGD_MAX_WM,
|
|
- IGD_DFT_HPLLOFF_WM,
|
|
- IGD_GUARD_WM,
|
|
- IGD_FIFO_LINE_SIZE
|
|
+static struct intel_watermark_params pineview_display_hplloff_wm = {
|
|
+ PINEVIEW_DISPLAY_FIFO,
|
|
+ PINEVIEW_MAX_WM,
|
|
+ PINEVIEW_DFT_HPLLOFF_WM,
|
|
+ PINEVIEW_GUARD_WM,
|
|
+ PINEVIEW_FIFO_LINE_SIZE
|
|
};
|
|
-static struct intel_watermark_params igd_cursor_wm = {
|
|
- IGD_CURSOR_FIFO,
|
|
- IGD_CURSOR_MAX_WM,
|
|
- IGD_CURSOR_DFT_WM,
|
|
- IGD_CURSOR_GUARD_WM,
|
|
- IGD_FIFO_LINE_SIZE,
|
|
+static struct intel_watermark_params pineview_cursor_wm = {
|
|
+ PINEVIEW_CURSOR_FIFO,
|
|
+ PINEVIEW_CURSOR_MAX_WM,
|
|
+ PINEVIEW_CURSOR_DFT_WM,
|
|
+ PINEVIEW_CURSOR_GUARD_WM,
|
|
+ PINEVIEW_FIFO_LINE_SIZE,
|
|
};
|
|
-static struct intel_watermark_params igd_cursor_hplloff_wm = {
|
|
- IGD_CURSOR_FIFO,
|
|
- IGD_CURSOR_MAX_WM,
|
|
- IGD_CURSOR_DFT_WM,
|
|
- IGD_CURSOR_GUARD_WM,
|
|
- IGD_FIFO_LINE_SIZE
|
|
+static struct intel_watermark_params pineview_cursor_hplloff_wm = {
|
|
+ PINEVIEW_CURSOR_FIFO,
|
|
+ PINEVIEW_CURSOR_MAX_WM,
|
|
+ PINEVIEW_CURSOR_DFT_WM,
|
|
+ PINEVIEW_CURSOR_GUARD_WM,
|
|
+ PINEVIEW_FIFO_LINE_SIZE
|
|
};
|
|
static struct intel_watermark_params g4x_wm_info = {
|
|
G4X_FIFO_SIZE,
|
|
@@ -2260,11 +2360,11 @@ static unsigned long intel_calculate_wm(unsigned long clock_in_khz,
|
|
1000;
|
|
entries_required /= wm->cacheline_size;
|
|
|
|
- DRM_DEBUG("FIFO entries required for mode: %d\n", entries_required);
|
|
+ DRM_DEBUG_KMS("FIFO entries required for mode: %d\n", entries_required);
|
|
|
|
wm_size = wm->fifo_size - (entries_required + wm->guard_size);
|
|
|
|
- DRM_DEBUG("FIFO watermark level: %d\n", wm_size);
|
|
+ DRM_DEBUG_KMS("FIFO watermark level: %d\n", wm_size);
|
|
|
|
/* Don't promote wm_size to unsigned... */
|
|
if (wm_size > (long)wm->max_wm)
|
|
@@ -2326,50 +2426,50 @@ static struct cxsr_latency *intel_get_cxsr_latency(int is_desktop, int fsb,
|
|
return latency;
|
|
}
|
|
|
|
- DRM_DEBUG("Unknown FSB/MEM found, disable CxSR\n");
|
|
+ DRM_DEBUG_KMS("Unknown FSB/MEM found, disable CxSR\n");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
-static void igd_disable_cxsr(struct drm_device *dev)
|
|
+static void pineview_disable_cxsr(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
u32 reg;
|
|
|
|
/* deactivate cxsr */
|
|
reg = I915_READ(DSPFW3);
|
|
- reg &= ~(IGD_SELF_REFRESH_EN);
|
|
+ reg &= ~(PINEVIEW_SELF_REFRESH_EN);
|
|
I915_WRITE(DSPFW3, reg);
|
|
DRM_INFO("Big FIFO is disabled\n");
|
|
}
|
|
|
|
-static void igd_enable_cxsr(struct drm_device *dev, unsigned long clock,
|
|
- int pixel_size)
|
|
+static void pineview_enable_cxsr(struct drm_device *dev, unsigned long clock,
|
|
+ int pixel_size)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
u32 reg;
|
|
unsigned long wm;
|
|
struct cxsr_latency *latency;
|
|
|
|
- latency = intel_get_cxsr_latency(IS_IGDG(dev), dev_priv->fsb_freq,
|
|
+ latency = intel_get_cxsr_latency(IS_PINEVIEW_G(dev), dev_priv->fsb_freq,
|
|
dev_priv->mem_freq);
|
|
if (!latency) {
|
|
- DRM_DEBUG("Unknown FSB/MEM found, disable CxSR\n");
|
|
- igd_disable_cxsr(dev);
|
|
+ DRM_DEBUG_KMS("Unknown FSB/MEM found, disable CxSR\n");
|
|
+ pineview_disable_cxsr(dev);
|
|
return;
|
|
}
|
|
|
|
/* Display SR */
|
|
- wm = intel_calculate_wm(clock, &igd_display_wm, pixel_size,
|
|
+ wm = intel_calculate_wm(clock, &pineview_display_wm, pixel_size,
|
|
latency->display_sr);
|
|
reg = I915_READ(DSPFW1);
|
|
reg &= 0x7fffff;
|
|
reg |= wm << 23;
|
|
I915_WRITE(DSPFW1, reg);
|
|
- DRM_DEBUG("DSPFW1 register is %x\n", reg);
|
|
+ DRM_DEBUG_KMS("DSPFW1 register is %x\n", reg);
|
|
|
|
/* cursor SR */
|
|
- wm = intel_calculate_wm(clock, &igd_cursor_wm, pixel_size,
|
|
+ wm = intel_calculate_wm(clock, &pineview_cursor_wm, pixel_size,
|
|
latency->cursor_sr);
|
|
reg = I915_READ(DSPFW3);
|
|
reg &= ~(0x3f << 24);
|
|
@@ -2377,7 +2477,7 @@ static void igd_enable_cxsr(struct drm_device *dev, unsigned long clock,
|
|
I915_WRITE(DSPFW3, reg);
|
|
|
|
/* Display HPLL off SR */
|
|
- wm = intel_calculate_wm(clock, &igd_display_hplloff_wm,
|
|
+ wm = intel_calculate_wm(clock, &pineview_display_hplloff_wm,
|
|
latency->display_hpll_disable, I915_FIFO_LINE_SIZE);
|
|
reg = I915_READ(DSPFW3);
|
|
reg &= 0xfffffe00;
|
|
@@ -2385,17 +2485,17 @@ static void igd_enable_cxsr(struct drm_device *dev, unsigned long clock,
|
|
I915_WRITE(DSPFW3, reg);
|
|
|
|
/* cursor HPLL off SR */
|
|
- wm = intel_calculate_wm(clock, &igd_cursor_hplloff_wm, pixel_size,
|
|
+ wm = intel_calculate_wm(clock, &pineview_cursor_hplloff_wm, pixel_size,
|
|
latency->cursor_hpll_disable);
|
|
reg = I915_READ(DSPFW3);
|
|
reg &= ~(0x3f << 16);
|
|
reg |= (wm & 0x3f) << 16;
|
|
I915_WRITE(DSPFW3, reg);
|
|
- DRM_DEBUG("DSPFW3 register is %x\n", reg);
|
|
+ DRM_DEBUG_KMS("DSPFW3 register is %x\n", reg);
|
|
|
|
/* activate cxsr */
|
|
reg = I915_READ(DSPFW3);
|
|
- reg |= IGD_SELF_REFRESH_EN;
|
|
+ reg |= PINEVIEW_SELF_REFRESH_EN;
|
|
I915_WRITE(DSPFW3, reg);
|
|
|
|
DRM_INFO("Big FIFO is enabled\n");
|
|
@@ -2417,7 +2517,7 @@ static void igd_enable_cxsr(struct drm_device *dev, unsigned long clock,
|
|
* A value of 5us seems to be a good balance; safe for very low end
|
|
* platforms but not overly aggressive on lower latency configs.
|
|
*/
|
|
-const static int latency_ns = 5000;
|
|
+static const int latency_ns = 5000;
|
|
|
|
static int i9xx_get_fifo_size(struct drm_device *dev, int plane)
|
|
{
|
|
@@ -2431,8 +2531,8 @@ static int i9xx_get_fifo_size(struct drm_device *dev, int plane)
|
|
size = ((dsparb >> DSPARB_CSTART_SHIFT) & 0x7f) -
|
|
(dsparb & 0x7f);
|
|
|
|
- DRM_DEBUG("FIFO size - (0x%08x) %s: %d\n", dsparb, plane ? "B" : "A",
|
|
- size);
|
|
+ DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb,
|
|
+ plane ? "B" : "A", size);
|
|
|
|
return size;
|
|
}
|
|
@@ -2450,8 +2550,8 @@ static int i85x_get_fifo_size(struct drm_device *dev, int plane)
|
|
(dsparb & 0x1ff);
|
|
size >>= 1; /* Convert to cachelines */
|
|
|
|
- DRM_DEBUG("FIFO size - (0x%08x) %s: %d\n", dsparb, plane ? "B" : "A",
|
|
- size);
|
|
+ DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb,
|
|
+ plane ? "B" : "A", size);
|
|
|
|
return size;
|
|
}
|
|
@@ -2465,7 +2565,8 @@ static int i845_get_fifo_size(struct drm_device *dev, int plane)
|
|
size = dsparb & 0x7f;
|
|
size >>= 2; /* Convert to cachelines */
|
|
|
|
- DRM_DEBUG("FIFO size - (0x%08x) %s: %d\n", dsparb, plane ? "B" : "A",
|
|
+ DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb,
|
|
+ plane ? "B" : "A",
|
|
size);
|
|
|
|
return size;
|
|
@@ -2480,8 +2581,8 @@ static int i830_get_fifo_size(struct drm_device *dev, int plane)
|
|
size = dsparb & 0x7f;
|
|
size >>= 1; /* Convert to cachelines */
|
|
|
|
- DRM_DEBUG("FIFO size - (0x%08x) %s: %d\n", dsparb, plane ? "B" : "A",
|
|
- size);
|
|
+ DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb,
|
|
+ plane ? "B" : "A", size);
|
|
|
|
return size;
|
|
}
|
|
@@ -2527,7 +2628,7 @@ static void g4x_update_wm(struct drm_device *dev, int planea_clock,
|
|
/* Calc sr entries for one plane configs */
|
|
if (sr_hdisplay && (!planea_clock || !planeb_clock)) {
|
|
/* self-refresh has much higher latency */
|
|
- const static int sr_latency_ns = 12000;
|
|
+ static const int sr_latency_ns = 12000;
|
|
|
|
sr_clock = planea_clock ? planea_clock : planeb_clock;
|
|
line_time_us = ((sr_hdisplay * 1000) / sr_clock);
|
|
@@ -2538,6 +2639,10 @@ static void g4x_update_wm(struct drm_device *dev, int planea_clock,
|
|
sr_entries = roundup(sr_entries / cacheline_size, 1);
|
|
DRM_DEBUG("self-refresh entries: %d\n", sr_entries);
|
|
I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN);
|
|
+ } else {
|
|
+ /* Turn off self refresh if both pipes are enabled */
|
|
+ I915_WRITE(FW_BLC_SELF, I915_READ(FW_BLC_SELF)
|
|
+ & ~FW_BLC_SELF_EN);
|
|
}
|
|
|
|
DRM_DEBUG("Setting FIFO watermarks - A: %d, B: %d, SR %d\n",
|
|
@@ -2556,15 +2661,43 @@ static void g4x_update_wm(struct drm_device *dev, int planea_clock,
|
|
(cursor_sr << DSPFW_CURSOR_SR_SHIFT));
|
|
}
|
|
|
|
-static void i965_update_wm(struct drm_device *dev, int unused, int unused2,
|
|
- int unused3, int unused4)
|
|
+static void i965_update_wm(struct drm_device *dev, int planea_clock,
|
|
+ int planeb_clock, int sr_hdisplay, int pixel_size)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
+ unsigned long line_time_us;
|
|
+ int sr_clock, sr_entries, srwm = 1;
|
|
+
|
|
+ /* Calc sr entries for one plane configs */
|
|
+ if (sr_hdisplay && (!planea_clock || !planeb_clock)) {
|
|
+ /* self-refresh has much higher latency */
|
|
+ static const int sr_latency_ns = 12000;
|
|
+
|
|
+ sr_clock = planea_clock ? planea_clock : planeb_clock;
|
|
+ line_time_us = ((sr_hdisplay * 1000) / sr_clock);
|
|
+
|
|
+ /* Use ns/us then divide to preserve precision */
|
|
+ sr_entries = (((sr_latency_ns / line_time_us) + 1) *
|
|
+ pixel_size * sr_hdisplay) / 1000;
|
|
+ sr_entries = roundup(sr_entries / I915_FIFO_LINE_SIZE, 1);
|
|
+ DRM_DEBUG("self-refresh entries: %d\n", sr_entries);
|
|
+ srwm = I945_FIFO_SIZE - sr_entries;
|
|
+ if (srwm < 0)
|
|
+ srwm = 1;
|
|
+ srwm &= 0x3f;
|
|
+ I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN);
|
|
+ } else {
|
|
+ /* Turn off self refresh if both pipes are enabled */
|
|
+ I915_WRITE(FW_BLC_SELF, I915_READ(FW_BLC_SELF)
|
|
+ & ~FW_BLC_SELF_EN);
|
|
+ }
|
|
|
|
- DRM_DEBUG("Setting FIFO watermarks - A: 8, B: 8, C: 8, SR 8\n");
|
|
+ DRM_DEBUG_KMS("Setting FIFO watermarks - A: 8, B: 8, C: 8, SR %d\n",
|
|
+ srwm);
|
|
|
|
/* 965 has limitations... */
|
|
- I915_WRITE(DSPFW1, (8 << 16) | (8 << 8) | (8 << 0));
|
|
+ I915_WRITE(DSPFW1, (srwm << DSPFW_SR_SHIFT) | (8 << 16) | (8 << 8) |
|
|
+ (8 << 0));
|
|
I915_WRITE(DSPFW2, (8 << 8) | (8 << 0));
|
|
}
|
|
|
|
@@ -2600,7 +2733,7 @@ static void i9xx_update_wm(struct drm_device *dev, int planea_clock,
|
|
pixel_size, latency_ns);
|
|
planeb_wm = intel_calculate_wm(planeb_clock, &planeb_params,
|
|
pixel_size, latency_ns);
|
|
- DRM_DEBUG("FIFO watermarks - A: %d, B: %d\n", planea_wm, planeb_wm);
|
|
+ DRM_DEBUG_KMS("FIFO watermarks - A: %d, B: %d\n", planea_wm, planeb_wm);
|
|
|
|
/*
|
|
* Overlay gets an aggressive default since video jitter is bad.
|
|
@@ -2611,7 +2744,7 @@ static void i9xx_update_wm(struct drm_device *dev, int planea_clock,
|
|
if (HAS_FW_BLC(dev) && sr_hdisplay &&
|
|
(!planea_clock || !planeb_clock)) {
|
|
/* self-refresh has much higher latency */
|
|
- const static int sr_latency_ns = 6000;
|
|
+ static const int sr_latency_ns = 6000;
|
|
|
|
sr_clock = planea_clock ? planea_clock : planeb_clock;
|
|
line_time_us = ((sr_hdisplay * 1000) / sr_clock);
|
|
@@ -2620,14 +2753,18 @@ static void i9xx_update_wm(struct drm_device *dev, int planea_clock,
|
|
sr_entries = (((sr_latency_ns / line_time_us) + 1) *
|
|
pixel_size * sr_hdisplay) / 1000;
|
|
sr_entries = roundup(sr_entries / cacheline_size, 1);
|
|
- DRM_DEBUG("self-refresh entries: %d\n", sr_entries);
|
|
+ DRM_DEBUG_KMS("self-refresh entries: %d\n", sr_entries);
|
|
srwm = total_size - sr_entries;
|
|
if (srwm < 0)
|
|
srwm = 1;
|
|
I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN | (srwm & 0x3f));
|
|
+ } else {
|
|
+ /* Turn off self refresh if both pipes are enabled */
|
|
+ I915_WRITE(FW_BLC_SELF, I915_READ(FW_BLC_SELF)
|
|
+ & ~FW_BLC_SELF_EN);
|
|
}
|
|
|
|
- DRM_DEBUG("Setting FIFO watermarks - A: %d, B: %d, C: %d, SR %d\n",
|
|
+ DRM_DEBUG_KMS("Setting FIFO watermarks - A: %d, B: %d, C: %d, SR %d\n",
|
|
planea_wm, planeb_wm, cwm, srwm);
|
|
|
|
fwater_lo = ((planeb_wm & 0x3f) << 16) | (planea_wm & 0x3f);
|
|
@@ -2654,7 +2791,7 @@ static void i830_update_wm(struct drm_device *dev, int planea_clock, int unused,
|
|
pixel_size, latency_ns);
|
|
fwater_lo |= (3<<8) | planea_wm;
|
|
|
|
- DRM_DEBUG("Setting FIFO watermarks - A: %d\n", planea_wm);
|
|
+ DRM_DEBUG_KMS("Setting FIFO watermarks - A: %d\n", planea_wm);
|
|
|
|
I915_WRITE(FW_BLC, fwater_lo);
|
|
}
|
|
@@ -2708,11 +2845,11 @@ static void intel_update_watermarks(struct drm_device *dev)
|
|
if (crtc->enabled) {
|
|
enabled++;
|
|
if (intel_crtc->plane == 0) {
|
|
- DRM_DEBUG("plane A (pipe %d) clock: %d\n",
|
|
+ DRM_DEBUG_KMS("plane A (pipe %d) clock: %d\n",
|
|
intel_crtc->pipe, crtc->mode.clock);
|
|
planea_clock = crtc->mode.clock;
|
|
} else {
|
|
- DRM_DEBUG("plane B (pipe %d) clock: %d\n",
|
|
+ DRM_DEBUG_KMS("plane B (pipe %d) clock: %d\n",
|
|
intel_crtc->pipe, crtc->mode.clock);
|
|
planeb_clock = crtc->mode.clock;
|
|
}
|
|
@@ -2729,10 +2866,10 @@ static void intel_update_watermarks(struct drm_device *dev)
|
|
return;
|
|
|
|
/* Single plane configs can enable self refresh */
|
|
- if (enabled == 1 && IS_IGD(dev))
|
|
- igd_enable_cxsr(dev, sr_clock, pixel_size);
|
|
- else if (IS_IGD(dev))
|
|
- igd_disable_cxsr(dev);
|
|
+ if (enabled == 1 && IS_PINEVIEW(dev))
|
|
+ pineview_enable_cxsr(dev, sr_clock, pixel_size);
|
|
+ else if (IS_PINEVIEW(dev))
|
|
+ pineview_disable_cxsr(dev);
|
|
|
|
dev_priv->display.update_wm(dev, planea_clock, planeb_clock,
|
|
sr_hdisplay, pixel_size);
|
|
@@ -2826,10 +2963,11 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
|
|
|
|
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);
|
|
+ DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n",
|
|
+ refclk / 1000);
|
|
} else if (IS_I9XX(dev)) {
|
|
refclk = 96000;
|
|
- if (IS_IGDNG(dev))
|
|
+ if (IS_IRONLAKE(dev))
|
|
refclk = 120000; /* 120Mhz refclk */
|
|
} else {
|
|
refclk = 48000;
|
|
@@ -2849,14 +2987,23 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
|
|
return -EINVAL;
|
|
}
|
|
|
|
- if (limit->find_reduced_pll && dev_priv->lvds_downclock_avail) {
|
|
- memcpy(&reduced_clock, &clock, sizeof(intel_clock_t));
|
|
- has_reduced_clock = limit->find_reduced_pll(limit, crtc,
|
|
- (adjusted_mode->clock*3/4),
|
|
+ if (is_lvds && dev_priv->lvds_downclock_avail) {
|
|
+ has_reduced_clock = limit->find_pll(limit, crtc,
|
|
+ dev_priv->lvds_downclock,
|
|
refclk,
|
|
&reduced_clock);
|
|
+ if (has_reduced_clock && (clock.p != reduced_clock.p)) {
|
|
+ /*
|
|
+ * If the different P is found, it means that we can't
|
|
+ * switch the display clock by using the FP0/FP1.
|
|
+ * In such case we will disable the LVDS downclock
|
|
+ * feature.
|
|
+ */
|
|
+ DRM_DEBUG_KMS("Different P is found for "
|
|
+ "LVDS clock/downclock\n");
|
|
+ has_reduced_clock = 0;
|
|
+ }
|
|
}
|
|
-
|
|
/* SDVO TV has fixed PLL values depend on its clock range,
|
|
this mirrors vbios setting. */
|
|
if (is_sdvo && is_tv) {
|
|
@@ -2878,7 +3025,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
|
|
}
|
|
|
|
/* FDI link */
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
int lane, link_bw, bpp;
|
|
/* eDP doesn't require FDI link, so just set DP M/N
|
|
according to current link config */
|
|
@@ -2909,6 +3056,21 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
|
|
temp |= PIPE_8BPC;
|
|
else
|
|
temp |= PIPE_6BPC;
|
|
+ } else if (is_edp) {
|
|
+ switch (dev_priv->edp_bpp/3) {
|
|
+ case 8:
|
|
+ temp |= PIPE_8BPC;
|
|
+ break;
|
|
+ case 10:
|
|
+ temp |= PIPE_10BPC;
|
|
+ break;
|
|
+ case 6:
|
|
+ temp |= PIPE_6BPC;
|
|
+ break;
|
|
+ case 12:
|
|
+ temp |= PIPE_12BPC;
|
|
+ break;
|
|
+ }
|
|
} else
|
|
temp |= PIPE_8BPC;
|
|
I915_WRITE(pipeconf_reg, temp);
|
|
@@ -2932,8 +3094,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
|
|
bpp = 24;
|
|
}
|
|
|
|
- igdng_compute_m_n(bpp, lane, target_clock,
|
|
- link_bw, &m_n);
|
|
+ ironlake_compute_m_n(bpp, lane, target_clock, link_bw, &m_n);
|
|
}
|
|
|
|
/* Ironlake: try to setup display ref clock before DPLL
|
|
@@ -2941,7 +3102,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
|
|
* PCH B stepping, previous chipset stepping should be
|
|
* ignoring this setting.
|
|
*/
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
temp = I915_READ(PCH_DREF_CONTROL);
|
|
/* Always enable nonspread source */
|
|
temp &= ~DREF_NONSPREAD_SOURCE_MASK;
|
|
@@ -2976,7 +3137,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
|
|
}
|
|
}
|
|
|
|
- if (IS_IGD(dev)) {
|
|
+ if (IS_PINEVIEW(dev)) {
|
|
fp = (1 << clock.n) << 16 | clock.m1 << 8 | clock.m2;
|
|
if (has_reduced_clock)
|
|
fp2 = (1 << reduced_clock.n) << 16 |
|
|
@@ -2988,7 +3149,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
|
|
reduced_clock.m2;
|
|
}
|
|
|
|
- if (!IS_IGDNG(dev))
|
|
+ if (!IS_IRONLAKE(dev))
|
|
dpll = DPLL_VGA_MODE_DIS;
|
|
|
|
if (IS_I9XX(dev)) {
|
|
@@ -3001,19 +3162,19 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
|
|
sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
|
|
if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
|
|
dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
|
|
- else if (IS_IGDNG(dev))
|
|
+ else if (IS_IRONLAKE(dev))
|
|
dpll |= (sdvo_pixel_multiply - 1) << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT;
|
|
}
|
|
if (is_dp)
|
|
dpll |= DPLL_DVO_HIGH_SPEED;
|
|
|
|
/* compute bitmask from p1 value */
|
|
- if (IS_IGD(dev))
|
|
- dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT_IGD;
|
|
+ if (IS_PINEVIEW(dev))
|
|
+ dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT_PINEVIEW;
|
|
else {
|
|
dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT;
|
|
/* also FPA1 */
|
|
- if (IS_IGDNG(dev))
|
|
+ if (IS_IRONLAKE(dev))
|
|
dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT;
|
|
if (IS_G4X(dev) && has_reduced_clock)
|
|
dpll |= (1 << (reduced_clock.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT;
|
|
@@ -3032,7 +3193,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
|
|
dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14;
|
|
break;
|
|
}
|
|
- if (IS_I965G(dev) && !IS_IGDNG(dev))
|
|
+ if (IS_I965G(dev) && !IS_IRONLAKE(dev))
|
|
dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT);
|
|
} else {
|
|
if (is_lvds) {
|
|
@@ -3064,9 +3225,9 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
|
|
/* Set up the display plane register */
|
|
dspcntr = DISPPLANE_GAMMA_ENABLE;
|
|
|
|
- /* IGDNG's plane is forced to pipe, bit 24 is to
|
|
+ /* Ironlake's plane is forced to pipe, bit 24 is to
|
|
enable color space conversion */
|
|
- if (!IS_IGDNG(dev)) {
|
|
+ if (!IS_IRONLAKE(dev)) {
|
|
if (pipe == 0)
|
|
dspcntr &= ~DISPPLANE_SEL_PIPE_MASK;
|
|
else
|
|
@@ -3093,20 +3254,20 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
|
|
|
|
|
|
/* Disable the panel fitter if it was on our pipe */
|
|
- if (!IS_IGDNG(dev) && intel_panel_fitter_pipe(dev) == pipe)
|
|
+ if (!IS_IRONLAKE(dev) && intel_panel_fitter_pipe(dev) == pipe)
|
|
I915_WRITE(PFIT_CONTROL, 0);
|
|
|
|
- DRM_DEBUG("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
|
|
+ DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
|
|
drm_mode_debug_printmodeline(mode);
|
|
|
|
- /* assign to IGDNG registers */
|
|
- if (IS_IGDNG(dev)) {
|
|
+ /* assign to Ironlake registers */
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
fp_reg = pch_fp_reg;
|
|
dpll_reg = pch_dpll_reg;
|
|
}
|
|
|
|
if (is_edp) {
|
|
- igdng_disable_pll_edp(crtc);
|
|
+ ironlake_disable_pll_edp(crtc);
|
|
} else if ((dpll & DPLL_VCO_ENABLE)) {
|
|
I915_WRITE(fp_reg, fp);
|
|
I915_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE);
|
|
@@ -3121,7 +3282,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
|
|
if (is_lvds) {
|
|
u32 lvds;
|
|
|
|
- if (IS_IGDNG(dev))
|
|
+ if (IS_IRONLAKE(dev))
|
|
lvds_reg = PCH_LVDS;
|
|
|
|
lvds = I915_READ(lvds_reg);
|
|
@@ -3143,12 +3304,12 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
|
|
/* set the dithering flag */
|
|
if (IS_I965G(dev)) {
|
|
if (dev_priv->lvds_dither) {
|
|
- if (IS_IGDNG(dev))
|
|
+ if (IS_IRONLAKE(dev))
|
|
pipeconf |= PIPE_ENABLE_DITHER;
|
|
else
|
|
lvds |= LVDS_ENABLE_DITHER;
|
|
} else {
|
|
- if (IS_IGDNG(dev))
|
|
+ if (IS_IRONLAKE(dev))
|
|
pipeconf &= ~PIPE_ENABLE_DITHER;
|
|
else
|
|
lvds &= ~LVDS_ENABLE_DITHER;
|
|
@@ -3167,7 +3328,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
|
|
/* Wait for the clocks to stabilize. */
|
|
udelay(150);
|
|
|
|
- if (IS_I965G(dev) && !IS_IGDNG(dev)) {
|
|
+ if (IS_I965G(dev) && !IS_IRONLAKE(dev)) {
|
|
if (is_sdvo) {
|
|
sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
|
|
I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) |
|
|
@@ -3187,14 +3348,14 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
|
|
I915_WRITE(fp_reg + 4, fp2);
|
|
intel_crtc->lowfreq_avail = true;
|
|
if (HAS_PIPE_CXSR(dev)) {
|
|
- DRM_DEBUG("enabling CxSR downclocking\n");
|
|
+ DRM_DEBUG_KMS("enabling CxSR downclocking\n");
|
|
pipeconf |= PIPECONF_CXSR_DOWNCLOCK;
|
|
}
|
|
} else {
|
|
I915_WRITE(fp_reg + 4, fp);
|
|
intel_crtc->lowfreq_avail = false;
|
|
if (HAS_PIPE_CXSR(dev)) {
|
|
- DRM_DEBUG("disabling CxSR downclocking\n");
|
|
+ DRM_DEBUG_KMS("disabling CxSR downclocking\n");
|
|
pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK;
|
|
}
|
|
}
|
|
@@ -3214,21 +3375,21 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
|
|
/* pipesrc and dspsize control the size that is scaled from, which should
|
|
* always be the user's requested size.
|
|
*/
|
|
- if (!IS_IGDNG(dev)) {
|
|
+ if (!IS_IRONLAKE(dev)) {
|
|
I915_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) |
|
|
(mode->hdisplay - 1));
|
|
I915_WRITE(dsppos_reg, 0);
|
|
}
|
|
I915_WRITE(pipesrc_reg, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
|
|
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
I915_WRITE(data_m1_reg, TU_SIZE(m_n.tu) | m_n.gmch_m);
|
|
I915_WRITE(data_n1_reg, TU_SIZE(m_n.tu) | m_n.gmch_n);
|
|
I915_WRITE(link_m1_reg, m_n.link_m);
|
|
I915_WRITE(link_n1_reg, m_n.link_n);
|
|
|
|
if (is_edp) {
|
|
- igdng_set_pll_edp(crtc, adjusted_mode->clock);
|
|
+ ironlake_set_pll_edp(crtc, adjusted_mode->clock);
|
|
} else {
|
|
/* enable FDI RX PLL too */
|
|
temp = I915_READ(fdi_rx_reg);
|
|
@@ -3242,7 +3403,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
|
|
|
|
intel_wait_for_vblank(dev);
|
|
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
/* enable address swizzle for tiling buffer */
|
|
temp = I915_READ(DISP_ARB_CTL);
|
|
I915_WRITE(DISP_ARB_CTL, temp | DISP_TILE_SURFACE_SWIZZLING);
|
|
@@ -3276,8 +3437,8 @@ void intel_crtc_load_lut(struct drm_crtc *crtc)
|
|
if (!crtc->enabled)
|
|
return;
|
|
|
|
- /* use legacy palette for IGDNG */
|
|
- if (IS_IGDNG(dev))
|
|
+ /* use legacy palette for Ironlake */
|
|
+ if (IS_IRONLAKE(dev))
|
|
palreg = (intel_crtc->pipe == 0) ? LGC_PALETTE_A :
|
|
LGC_PALETTE_B;
|
|
|
|
@@ -3306,11 +3467,11 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
|
|
size_t addr;
|
|
int ret;
|
|
|
|
- DRM_DEBUG("\n");
|
|
+ DRM_DEBUG_KMS("\n");
|
|
|
|
/* if we want to turn off the cursor ignore width and height */
|
|
if (!handle) {
|
|
- DRM_DEBUG("cursor off\n");
|
|
+ DRM_DEBUG_KMS("cursor off\n");
|
|
if (IS_MOBILE(dev) || IS_I9XX(dev)) {
|
|
temp &= ~(CURSOR_MODE | MCURSOR_GAMMA_ENABLE);
|
|
temp |= CURSOR_MODE_DISABLE;
|
|
@@ -3343,7 +3504,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
|
|
|
|
/* we only need to pin inside GTT if cursor is non-phy */
|
|
mutex_lock(&dev->struct_mutex);
|
|
- if (!dev_priv->cursor_needs_physical) {
|
|
+ if (!dev_priv->info->cursor_needs_physical) {
|
|
ret = i915_gem_object_pin(bo, PAGE_SIZE);
|
|
if (ret) {
|
|
DRM_ERROR("failed to pin cursor bo\n");
|
|
@@ -3378,7 +3539,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
|
|
I915_WRITE(base, addr);
|
|
|
|
if (intel_crtc->cursor_bo) {
|
|
- if (dev_priv->cursor_needs_physical) {
|
|
+ if (dev_priv->info->cursor_needs_physical) {
|
|
if (intel_crtc->cursor_bo != bo)
|
|
i915_gem_detach_phys_object(dev, intel_crtc->cursor_bo);
|
|
} else
|
|
@@ -3618,18 +3779,18 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
|
|
fp = I915_READ((pipe == 0) ? FPA1 : FPB1);
|
|
|
|
clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT;
|
|
- if (IS_IGD(dev)) {
|
|
- clock.n = ffs((fp & FP_N_IGD_DIV_MASK) >> FP_N_DIV_SHIFT) - 1;
|
|
- clock.m2 = (fp & FP_M2_IGD_DIV_MASK) >> FP_M2_DIV_SHIFT;
|
|
+ if (IS_PINEVIEW(dev)) {
|
|
+ clock.n = ffs((fp & FP_N_PINEVIEW_DIV_MASK) >> FP_N_DIV_SHIFT) - 1;
|
|
+ clock.m2 = (fp & FP_M2_PINEVIEW_DIV_MASK) >> FP_M2_DIV_SHIFT;
|
|
} else {
|
|
clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT;
|
|
clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT;
|
|
}
|
|
|
|
if (IS_I9XX(dev)) {
|
|
- if (IS_IGD(dev))
|
|
- clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_IGD) >>
|
|
- DPLL_FPA01_P1_POST_DIV_SHIFT_IGD);
|
|
+ if (IS_PINEVIEW(dev))
|
|
+ clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_PINEVIEW) >>
|
|
+ DPLL_FPA01_P1_POST_DIV_SHIFT_PINEVIEW);
|
|
else
|
|
clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK) >>
|
|
DPLL_FPA01_P1_POST_DIV_SHIFT);
|
|
@@ -3644,7 +3805,7 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
|
|
7 : 14;
|
|
break;
|
|
default:
|
|
- DRM_DEBUG("Unknown DPLL mode %08x in programmed "
|
|
+ DRM_DEBUG_KMS("Unknown DPLL mode %08x in programmed "
|
|
"mode\n", (int)(dpll & DPLL_MODE_MASK));
|
|
return 0;
|
|
}
|
|
@@ -3730,7 +3891,7 @@ static void intel_gpu_idle_timer(unsigned long arg)
|
|
struct drm_device *dev = (struct drm_device *)arg;
|
|
drm_i915_private_t *dev_priv = dev->dev_private;
|
|
|
|
- DRM_DEBUG("idle timer fired, downclocking\n");
|
|
+ DRM_DEBUG_DRIVER("idle timer fired, downclocking\n");
|
|
|
|
dev_priv->busy = false;
|
|
|
|
@@ -3745,7 +3906,7 @@ static void intel_crtc_idle_timer(unsigned long arg)
|
|
struct drm_crtc *crtc = &intel_crtc->base;
|
|
drm_i915_private_t *dev_priv = crtc->dev->dev_private;
|
|
|
|
- DRM_DEBUG("idle timer fired, downclocking\n");
|
|
+ DRM_DEBUG_DRIVER("idle timer fired, downclocking\n");
|
|
|
|
intel_crtc->busy = false;
|
|
|
|
@@ -3761,14 +3922,14 @@ static void intel_increase_pllclock(struct drm_crtc *crtc, bool schedule)
|
|
int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
|
|
int dpll = I915_READ(dpll_reg);
|
|
|
|
- if (IS_IGDNG(dev))
|
|
+ if (IS_IRONLAKE(dev))
|
|
return;
|
|
|
|
if (!dev_priv->lvds_downclock_avail)
|
|
return;
|
|
|
|
if (!HAS_PIPE_CXSR(dev) && (dpll & DISPLAY_RATE_SELECT_FPA1)) {
|
|
- DRM_DEBUG("upclocking LVDS\n");
|
|
+ DRM_DEBUG_DRIVER("upclocking LVDS\n");
|
|
|
|
/* Unlock panel regs */
|
|
I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) | (0xabcd << 16));
|
|
@@ -3779,7 +3940,7 @@ static void intel_increase_pllclock(struct drm_crtc *crtc, bool schedule)
|
|
intel_wait_for_vblank(dev);
|
|
dpll = I915_READ(dpll_reg);
|
|
if (dpll & DISPLAY_RATE_SELECT_FPA1)
|
|
- DRM_DEBUG("failed to upclock LVDS!\n");
|
|
+ DRM_DEBUG_DRIVER("failed to upclock LVDS!\n");
|
|
|
|
/* ...and lock them again */
|
|
I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) & 0x3);
|
|
@@ -3800,7 +3961,7 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc)
|
|
int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
|
|
int dpll = I915_READ(dpll_reg);
|
|
|
|
- if (IS_IGDNG(dev))
|
|
+ if (IS_IRONLAKE(dev))
|
|
return;
|
|
|
|
if (!dev_priv->lvds_downclock_avail)
|
|
@@ -3811,7 +3972,7 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc)
|
|
* the manual case.
|
|
*/
|
|
if (!HAS_PIPE_CXSR(dev) && intel_crtc->lowfreq_avail) {
|
|
- DRM_DEBUG("downclocking LVDS\n");
|
|
+ DRM_DEBUG_DRIVER("downclocking LVDS\n");
|
|
|
|
/* Unlock panel regs */
|
|
I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) | (0xabcd << 16));
|
|
@@ -3822,7 +3983,7 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc)
|
|
intel_wait_for_vblank(dev);
|
|
dpll = I915_READ(dpll_reg);
|
|
if (!(dpll & DISPLAY_RATE_SELECT_FPA1))
|
|
- DRM_DEBUG("failed to downclock LVDS!\n");
|
|
+ DRM_DEBUG_DRIVER("failed to downclock LVDS!\n");
|
|
|
|
/* ...and lock them again */
|
|
I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) & 0x3);
|
|
@@ -3883,7 +4044,11 @@ void intel_mark_busy(struct drm_device *dev, struct drm_gem_object *obj)
|
|
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
|
return;
|
|
|
|
- dev_priv->busy = true;
|
|
+ if (!dev_priv->busy)
|
|
+ dev_priv->busy = true;
|
|
+ else
|
|
+ mod_timer(&dev_priv->idle_timer, jiffies +
|
|
+ msecs_to_jiffies(GPU_IDLE_TIMEOUT));
|
|
|
|
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
|
if (!crtc->fb)
|
|
@@ -3913,6 +4078,180 @@ static void intel_crtc_destroy(struct drm_crtc *crtc)
|
|
kfree(intel_crtc);
|
|
}
|
|
|
|
+struct intel_unpin_work {
|
|
+ struct work_struct work;
|
|
+ struct drm_device *dev;
|
|
+ struct drm_gem_object *old_fb_obj;
|
|
+ struct drm_gem_object *pending_flip_obj;
|
|
+ struct drm_pending_vblank_event *event;
|
|
+ int pending;
|
|
+};
|
|
+
|
|
+static void intel_unpin_work_fn(struct work_struct *__work)
|
|
+{
|
|
+ struct intel_unpin_work *work =
|
|
+ container_of(__work, struct intel_unpin_work, work);
|
|
+
|
|
+ mutex_lock(&work->dev->struct_mutex);
|
|
+ i915_gem_object_unpin(work->old_fb_obj);
|
|
+ drm_gem_object_unreference(work->pending_flip_obj);
|
|
+ drm_gem_object_unreference(work->old_fb_obj);
|
|
+ mutex_unlock(&work->dev->struct_mutex);
|
|
+ kfree(work);
|
|
+}
|
|
+
|
|
+void intel_finish_page_flip(struct drm_device *dev, int pipe)
|
|
+{
|
|
+ drm_i915_private_t *dev_priv = dev->dev_private;
|
|
+ struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
|
|
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
|
|
+ struct intel_unpin_work *work;
|
|
+ struct drm_i915_gem_object *obj_priv;
|
|
+ struct drm_pending_vblank_event *e;
|
|
+ struct timeval now;
|
|
+ unsigned long flags;
|
|
+
|
|
+ /* Ignore early vblank irqs */
|
|
+ if (intel_crtc == NULL)
|
|
+ return;
|
|
+
|
|
+ spin_lock_irqsave(&dev->event_lock, flags);
|
|
+ work = intel_crtc->unpin_work;
|
|
+ if (work == NULL || !work->pending) {
|
|
+ if (work && !work->pending) {
|
|
+ obj_priv = work->pending_flip_obj->driver_private;
|
|
+ DRM_DEBUG_DRIVER("flip finish: %p (%d) not pending?\n",
|
|
+ obj_priv,
|
|
+ atomic_read(&obj_priv->pending_flip));
|
|
+ }
|
|
+ spin_unlock_irqrestore(&dev->event_lock, flags);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ intel_crtc->unpin_work = NULL;
|
|
+ drm_vblank_put(dev, intel_crtc->pipe);
|
|
+
|
|
+ if (work->event) {
|
|
+ e = work->event;
|
|
+ do_gettimeofday(&now);
|
|
+ e->event.sequence = drm_vblank_count(dev, intel_crtc->pipe);
|
|
+ e->event.tv_sec = now.tv_sec;
|
|
+ e->event.tv_usec = now.tv_usec;
|
|
+ list_add_tail(&e->base.link,
|
|
+ &e->base.file_priv->event_list);
|
|
+ wake_up_interruptible(&e->base.file_priv->event_wait);
|
|
+ }
|
|
+
|
|
+ spin_unlock_irqrestore(&dev->event_lock, flags);
|
|
+
|
|
+ obj_priv = work->pending_flip_obj->driver_private;
|
|
+
|
|
+ /* Initial scanout buffer will have a 0 pending flip count */
|
|
+ if ((atomic_read(&obj_priv->pending_flip) == 0) ||
|
|
+ atomic_dec_and_test(&obj_priv->pending_flip))
|
|
+ DRM_WAKEUP(&dev_priv->pending_flip_queue);
|
|
+ schedule_work(&work->work);
|
|
+}
|
|
+
|
|
+void intel_prepare_page_flip(struct drm_device *dev, int plane)
|
|
+{
|
|
+ drm_i915_private_t *dev_priv = dev->dev_private;
|
|
+ struct intel_crtc *intel_crtc =
|
|
+ to_intel_crtc(dev_priv->plane_to_crtc_mapping[plane]);
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&dev->event_lock, flags);
|
|
+ if (intel_crtc->unpin_work) {
|
|
+ intel_crtc->unpin_work->pending = 1;
|
|
+ } else {
|
|
+ DRM_DEBUG_DRIVER("preparing flip with no unpin work?\n");
|
|
+ }
|
|
+ spin_unlock_irqrestore(&dev->event_lock, flags);
|
|
+}
|
|
+
|
|
+static int intel_crtc_page_flip(struct drm_crtc *crtc,
|
|
+ struct drm_framebuffer *fb,
|
|
+ struct drm_pending_vblank_event *event)
|
|
+{
|
|
+ struct drm_device *dev = crtc->dev;
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
+ struct intel_framebuffer *intel_fb;
|
|
+ struct drm_i915_gem_object *obj_priv;
|
|
+ struct drm_gem_object *obj;
|
|
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
|
|
+ struct intel_unpin_work *work;
|
|
+ unsigned long flags;
|
|
+ int pipesrc_reg = (intel_crtc->pipe == 0) ? PIPEASRC : PIPEBSRC;
|
|
+ int ret, pipesrc;
|
|
+ RING_LOCALS;
|
|
+
|
|
+ work = kzalloc(sizeof *work, GFP_KERNEL);
|
|
+ if (work == NULL)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
+
|
|
+ work->event = event;
|
|
+ work->dev = crtc->dev;
|
|
+ intel_fb = to_intel_framebuffer(crtc->fb);
|
|
+ work->old_fb_obj = intel_fb->obj;
|
|
+ INIT_WORK(&work->work, intel_unpin_work_fn);
|
|
+
|
|
+ /* We borrow the event spin lock for protecting unpin_work */
|
|
+ spin_lock_irqsave(&dev->event_lock, flags);
|
|
+ if (intel_crtc->unpin_work) {
|
|
+ DRM_DEBUG_DRIVER("flip queue: crtc already busy\n");
|
|
+ spin_unlock_irqrestore(&dev->event_lock, flags);
|
|
+ kfree(work);
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+ return -EBUSY;
|
|
+ }
|
|
+ intel_crtc->unpin_work = work;
|
|
+ spin_unlock_irqrestore(&dev->event_lock, flags);
|
|
+
|
|
+ intel_fb = to_intel_framebuffer(fb);
|
|
+ obj = intel_fb->obj;
|
|
+
|
|
+ ret = intel_pin_and_fence_fb_obj(dev, obj);
|
|
+ if (ret != 0) {
|
|
+ DRM_DEBUG_DRIVER("flip queue: %p pin & fence failed\n",
|
|
+ obj->driver_private);
|
|
+ kfree(work);
|
|
+ intel_crtc->unpin_work = NULL;
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /* Reference the objects for the scheduled work. */
|
|
+ drm_gem_object_reference(work->old_fb_obj);
|
|
+ drm_gem_object_reference(obj);
|
|
+
|
|
+ crtc->fb = fb;
|
|
+ i915_gem_object_flush_write_domain(obj);
|
|
+ drm_vblank_get(dev, intel_crtc->pipe);
|
|
+ obj_priv = obj->driver_private;
|
|
+ atomic_inc(&obj_priv->pending_flip);
|
|
+ work->pending_flip_obj = obj;
|
|
+
|
|
+ BEGIN_LP_RING(4);
|
|
+ OUT_RING(MI_DISPLAY_FLIP |
|
|
+ MI_DISPLAY_FLIP_PLANE(intel_crtc->plane));
|
|
+ OUT_RING(fb->pitch);
|
|
+ if (IS_I965G(dev)) {
|
|
+ OUT_RING(obj_priv->gtt_offset | obj_priv->tiling_mode);
|
|
+ pipesrc = I915_READ(pipesrc_reg);
|
|
+ OUT_RING(pipesrc & 0x0fff0fff);
|
|
+ } else {
|
|
+ OUT_RING(obj_priv->gtt_offset);
|
|
+ OUT_RING(MI_NOOP);
|
|
+ }
|
|
+ ADVANCE_LP_RING();
|
|
+
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static const struct drm_crtc_helper_funcs intel_helper_funcs = {
|
|
.dpms = intel_crtc_dpms,
|
|
.mode_fixup = intel_crtc_mode_fixup,
|
|
@@ -3929,11 +4268,13 @@ static const struct drm_crtc_funcs intel_crtc_funcs = {
|
|
.gamma_set = intel_crtc_gamma_set,
|
|
.set_config = drm_crtc_helper_set_config,
|
|
.destroy = intel_crtc_destroy,
|
|
+ .page_flip = intel_crtc_page_flip,
|
|
};
|
|
|
|
|
|
static void intel_crtc_init(struct drm_device *dev, int pipe)
|
|
{
|
|
+ drm_i915_private_t *dev_priv = dev->dev_private;
|
|
struct intel_crtc *intel_crtc;
|
|
int i;
|
|
|
|
@@ -3956,10 +4297,15 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
|
|
intel_crtc->pipe = pipe;
|
|
intel_crtc->plane = pipe;
|
|
if (IS_MOBILE(dev) && (IS_I9XX(dev) && !IS_I965G(dev))) {
|
|
- DRM_DEBUG("swapping pipes & planes for FBC\n");
|
|
+ DRM_DEBUG_KMS("swapping pipes & planes for FBC\n");
|
|
intel_crtc->plane = ((pipe == 0) ? 1 : 0);
|
|
}
|
|
|
|
+ BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) ||
|
|
+ dev_priv->plane_to_crtc_mapping[intel_crtc->plane] != NULL);
|
|
+ dev_priv->plane_to_crtc_mapping[intel_crtc->plane] = &intel_crtc->base;
|
|
+ dev_priv->pipe_to_crtc_mapping[intel_crtc->pipe] = &intel_crtc->base;
|
|
+
|
|
intel_crtc->cursor_addr = 0;
|
|
intel_crtc->dpms_mode = -1;
|
|
drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs);
|
|
@@ -4036,7 +4382,7 @@ static void intel_setup_outputs(struct drm_device *dev)
|
|
if (IS_MOBILE(dev) && !IS_I830(dev))
|
|
intel_lvds_init(dev);
|
|
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
int found;
|
|
|
|
if (IS_MOBILE(dev) && (I915_READ(DP_A) & DP_DETECTED))
|
|
@@ -4209,6 +4555,42 @@ static const struct drm_mode_config_funcs intel_mode_funcs = {
|
|
.fb_changed = intelfb_probe,
|
|
};
|
|
|
|
+static struct drm_gem_object *
|
|
+intel_alloc_power_context(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_gem_object *pwrctx;
|
|
+ int ret;
|
|
+
|
|
+ pwrctx = drm_gem_object_alloc(dev, 4096);
|
|
+ if (!pwrctx) {
|
|
+ DRM_DEBUG("failed to alloc power context, RC6 disabled\n");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
+ ret = i915_gem_object_pin(pwrctx, 4096);
|
|
+ if (ret) {
|
|
+ DRM_ERROR("failed to pin power context: %d\n", ret);
|
|
+ goto err_unref;
|
|
+ }
|
|
+
|
|
+ ret = i915_gem_object_set_to_gtt_domain(pwrctx, 1);
|
|
+ if (ret) {
|
|
+ DRM_ERROR("failed to set-domain on power context: %d\n", ret);
|
|
+ goto err_unpin;
|
|
+ }
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+
|
|
+ return pwrctx;
|
|
+
|
|
+err_unpin:
|
|
+ i915_gem_object_unpin(pwrctx);
|
|
+err_unref:
|
|
+ drm_gem_object_unreference(pwrctx);
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
void intel_init_clock_gating(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
@@ -4217,7 +4599,7 @@ void intel_init_clock_gating(struct drm_device *dev)
|
|
* Disable clock gating reported to work incorrectly according to the
|
|
* specs, but enable as much else as we can.
|
|
*/
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
return;
|
|
} else if (IS_G4X(dev)) {
|
|
uint32_t dspclk_gate;
|
|
@@ -4251,11 +4633,37 @@ void intel_init_clock_gating(struct drm_device *dev)
|
|
dstate |= DSTATE_PLL_D3_OFF | DSTATE_GFX_CLOCK_GATING |
|
|
DSTATE_DOT_CLOCK_GATING;
|
|
I915_WRITE(D_STATE, dstate);
|
|
- } else if (IS_I855(dev) || IS_I865G(dev)) {
|
|
+ } else if (IS_I85X(dev) || IS_I865G(dev)) {
|
|
I915_WRITE(RENCLK_GATE_D1, SV_CLOCK_GATE_DISABLE);
|
|
} else if (IS_I830(dev)) {
|
|
I915_WRITE(DSPCLK_GATE_D, OVRUNIT_CLOCK_GATE_DISABLE);
|
|
}
|
|
+
|
|
+ /*
|
|
+ * GPU can automatically power down the render unit if given a page
|
|
+ * to save state.
|
|
+ */
|
|
+ if (I915_HAS_RC6(dev) && drm_core_check_feature(dev, DRIVER_MODESET)) {
|
|
+ struct drm_i915_gem_object *obj_priv = NULL;
|
|
+
|
|
+ if (dev_priv->pwrctx) {
|
|
+ obj_priv = dev_priv->pwrctx->driver_private;
|
|
+ } else {
|
|
+ struct drm_gem_object *pwrctx;
|
|
+
|
|
+ pwrctx = intel_alloc_power_context(dev);
|
|
+ if (pwrctx) {
|
|
+ dev_priv->pwrctx = pwrctx;
|
|
+ obj_priv = pwrctx->driver_private;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (obj_priv) {
|
|
+ I915_WRITE(PWRCTXA, obj_priv->gtt_offset | PWRCTX_EN);
|
|
+ I915_WRITE(MCHBAR_RENDER_STANDBY,
|
|
+ I915_READ(MCHBAR_RENDER_STANDBY) & ~RCX_SW_EXIT);
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
/* Set up chip specific display functions */
|
|
@@ -4264,8 +4672,8 @@ static void intel_init_display(struct drm_device *dev)
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
/* We always want a DPMS function */
|
|
- if (IS_IGDNG(dev))
|
|
- dev_priv->display.dpms = igdng_crtc_dpms;
|
|
+ if (IS_IRONLAKE(dev))
|
|
+ dev_priv->display.dpms = ironlake_crtc_dpms;
|
|
else
|
|
dev_priv->display.dpms = i9xx_crtc_dpms;
|
|
|
|
@@ -4284,13 +4692,13 @@ static void intel_init_display(struct drm_device *dev)
|
|
}
|
|
|
|
/* Returns the core display clock speed */
|
|
- if (IS_I945G(dev))
|
|
+ if (IS_I945G(dev) || (IS_G33(dev) && ! IS_PINEVIEW_M(dev)))
|
|
dev_priv->display.get_display_clock_speed =
|
|
i945_get_display_clock_speed;
|
|
else if (IS_I915G(dev))
|
|
dev_priv->display.get_display_clock_speed =
|
|
i915_get_display_clock_speed;
|
|
- else if (IS_I945GM(dev) || IS_845G(dev) || IS_IGDGM(dev))
|
|
+ else if (IS_I945GM(dev) || IS_845G(dev) || IS_PINEVIEW_M(dev))
|
|
dev_priv->display.get_display_clock_speed =
|
|
i9xx_misc_get_display_clock_speed;
|
|
else if (IS_I915GM(dev))
|
|
@@ -4299,7 +4707,7 @@ static void intel_init_display(struct drm_device *dev)
|
|
else if (IS_I865G(dev))
|
|
dev_priv->display.get_display_clock_speed =
|
|
i865_get_display_clock_speed;
|
|
- else if (IS_I855(dev))
|
|
+ else if (IS_I85X(dev))
|
|
dev_priv->display.get_display_clock_speed =
|
|
i855_get_display_clock_speed;
|
|
else /* 852, 830 */
|
|
@@ -4307,7 +4715,7 @@ static void intel_init_display(struct drm_device *dev)
|
|
i830_get_display_clock_speed;
|
|
|
|
/* For FIFO watermark updates */
|
|
- if (IS_IGDNG(dev))
|
|
+ if (IS_IRONLAKE(dev))
|
|
dev_priv->display.update_wm = NULL;
|
|
else if (IS_G4X(dev))
|
|
dev_priv->display.update_wm = g4x_update_wm;
|
|
@@ -4363,7 +4771,7 @@ void intel_modeset_init(struct drm_device *dev)
|
|
num_pipe = 2;
|
|
else
|
|
num_pipe = 1;
|
|
- DRM_DEBUG("%d display pipe%s available.\n",
|
|
+ DRM_DEBUG_KMS("%d display pipe%s available.\n",
|
|
num_pipe, num_pipe > 1 ? "s" : "");
|
|
|
|
if (IS_I85X(dev))
|
|
@@ -4382,6 +4790,15 @@ void intel_modeset_init(struct drm_device *dev)
|
|
INIT_WORK(&dev_priv->idle_work, intel_idle_update);
|
|
setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer,
|
|
(unsigned long)dev);
|
|
+
|
|
+ intel_setup_overlay(dev);
|
|
+
|
|
+ if (IS_PINEVIEW(dev) && !intel_get_cxsr_latency(IS_PINEVIEW_G(dev),
|
|
+ dev_priv->fsb_freq,
|
|
+ dev_priv->mem_freq))
|
|
+ DRM_INFO("failed to find known CxSR latency "
|
|
+ "(found fsb freq %d, mem freq %d), disabling CxSR\n",
|
|
+ dev_priv->fsb_freq, dev_priv->mem_freq);
|
|
}
|
|
|
|
void intel_modeset_cleanup(struct drm_device *dev)
|
|
@@ -4404,11 +4821,21 @@ void intel_modeset_cleanup(struct drm_device *dev)
|
|
|
|
del_timer_sync(&dev_priv->idle_timer);
|
|
|
|
- mutex_unlock(&dev->struct_mutex);
|
|
-
|
|
if (dev_priv->display.disable_fbc)
|
|
dev_priv->display.disable_fbc(dev);
|
|
|
|
+ if (dev_priv->pwrctx) {
|
|
+ struct drm_i915_gem_object *obj_priv;
|
|
+
|
|
+ obj_priv = dev_priv->pwrctx->driver_private;
|
|
+ I915_WRITE(PWRCTXA, obj_priv->gtt_offset &~ PWRCTX_EN);
|
|
+ I915_READ(PWRCTXA);
|
|
+ i915_gem_object_unpin(dev_priv->pwrctx);
|
|
+ drm_gem_object_unreference(dev_priv->pwrctx);
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+
|
|
drm_mode_config_cleanup(dev);
|
|
}
|
|
|
|
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
|
|
index d487771..439506c 100644
|
|
--- a/drivers/gpu/drm/i915/intel_dp.c
|
|
+++ b/drivers/gpu/drm/i915/intel_dp.c
|
|
@@ -33,7 +33,8 @@
|
|
#include "intel_drv.h"
|
|
#include "i915_drm.h"
|
|
#include "i915_drv.h"
|
|
-#include "intel_dp.h"
|
|
+#include "drm_dp_helper.h"
|
|
+
|
|
|
|
#define DP_LINK_STATUS_SIZE 6
|
|
#define DP_LINK_CHECK_TIMEOUT (10 * 1000)
|
|
@@ -124,9 +125,15 @@ intel_dp_link_clock(uint8_t link_bw)
|
|
|
|
/* I think this is a fiction */
|
|
static int
|
|
-intel_dp_link_required(int pixel_clock)
|
|
+intel_dp_link_required(struct drm_device *dev,
|
|
+ struct intel_output *intel_output, int pixel_clock)
|
|
{
|
|
- return pixel_clock * 3;
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ if (IS_eDP(intel_output))
|
|
+ return (pixel_clock * dev_priv->edp_bpp) / 8;
|
|
+ else
|
|
+ return pixel_clock * 3;
|
|
}
|
|
|
|
static int
|
|
@@ -137,7 +144,8 @@ intel_dp_mode_valid(struct drm_connector *connector,
|
|
int max_link_clock = intel_dp_link_clock(intel_dp_max_link_bw(intel_output));
|
|
int max_lanes = intel_dp_max_lane_count(intel_output);
|
|
|
|
- if (intel_dp_link_required(mode->clock) > max_link_clock * max_lanes)
|
|
+ if (intel_dp_link_required(connector->dev, intel_output, mode->clock)
|
|
+ > max_link_clock * max_lanes)
|
|
return MODE_CLOCK_HIGH;
|
|
|
|
if (mode->clock < 10000)
|
|
@@ -223,8 +231,8 @@ intel_dp_aux_ch(struct intel_output *intel_output,
|
|
*/
|
|
if (IS_eDP(intel_output))
|
|
aux_clock_divider = 225; /* eDP input clock at 450Mhz */
|
|
- else if (IS_IGDNG(dev))
|
|
- aux_clock_divider = 62; /* IGDNG: input clock fixed at 125Mhz */
|
|
+ else if (IS_IRONLAKE(dev))
|
|
+ aux_clock_divider = 62; /* IRL input clock fixed at 125Mhz */
|
|
else
|
|
aux_clock_divider = intel_hrawclk(dev) / 2;
|
|
|
|
@@ -282,7 +290,7 @@ intel_dp_aux_ch(struct intel_output *intel_output,
|
|
/* Timeouts occur when the device isn't connected, so they're
|
|
* "normal" -- don't fill the kernel log with these */
|
|
if (status & DP_AUX_CH_CTL_TIME_OUT_ERROR) {
|
|
- DRM_DEBUG("dp_aux_ch timeout status 0x%08x\n", status);
|
|
+ DRM_DEBUG_KMS("dp_aux_ch timeout status 0x%08x\n", status);
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
@@ -382,17 +390,77 @@ intel_dp_aux_native_read(struct intel_output *intel_output,
|
|
}
|
|
|
|
static int
|
|
-intel_dp_i2c_aux_ch(struct i2c_adapter *adapter,
|
|
- uint8_t *send, int send_bytes,
|
|
- uint8_t *recv, int recv_bytes)
|
|
+intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
|
|
+ uint8_t write_byte, uint8_t *read_byte)
|
|
{
|
|
+ struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
|
|
struct intel_dp_priv *dp_priv = container_of(adapter,
|
|
struct intel_dp_priv,
|
|
adapter);
|
|
struct intel_output *intel_output = dp_priv->intel_output;
|
|
+ uint16_t address = algo_data->address;
|
|
+ uint8_t msg[5];
|
|
+ uint8_t reply[2];
|
|
+ int msg_bytes;
|
|
+ int reply_bytes;
|
|
+ int ret;
|
|
+
|
|
+ /* Set up the command byte */
|
|
+ if (mode & MODE_I2C_READ)
|
|
+ msg[0] = AUX_I2C_READ << 4;
|
|
+ else
|
|
+ msg[0] = AUX_I2C_WRITE << 4;
|
|
+
|
|
+ if (!(mode & MODE_I2C_STOP))
|
|
+ msg[0] |= AUX_I2C_MOT << 4;
|
|
+
|
|
+ msg[1] = address >> 8;
|
|
+ msg[2] = address;
|
|
+
|
|
+ switch (mode) {
|
|
+ case MODE_I2C_WRITE:
|
|
+ msg[3] = 0;
|
|
+ msg[4] = write_byte;
|
|
+ msg_bytes = 5;
|
|
+ reply_bytes = 1;
|
|
+ break;
|
|
+ case MODE_I2C_READ:
|
|
+ msg[3] = 0;
|
|
+ msg_bytes = 4;
|
|
+ reply_bytes = 2;
|
|
+ break;
|
|
+ default:
|
|
+ msg_bytes = 3;
|
|
+ reply_bytes = 1;
|
|
+ break;
|
|
+ }
|
|
|
|
- return intel_dp_aux_ch(intel_output,
|
|
- send, send_bytes, recv, recv_bytes);
|
|
+ for (;;) {
|
|
+ ret = intel_dp_aux_ch(intel_output,
|
|
+ msg, msg_bytes,
|
|
+ reply, reply_bytes);
|
|
+ if (ret < 0) {
|
|
+ DRM_DEBUG_KMS("aux_ch failed %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+ switch (reply[0] & AUX_I2C_REPLY_MASK) {
|
|
+ case AUX_I2C_REPLY_ACK:
|
|
+ if (mode == MODE_I2C_READ) {
|
|
+ *read_byte = reply[1];
|
|
+ }
|
|
+ return reply_bytes - 1;
|
|
+ case AUX_I2C_REPLY_NACK:
|
|
+ DRM_DEBUG_KMS("aux_ch nack\n");
|
|
+ return -EREMOTEIO;
|
|
+ case AUX_I2C_REPLY_DEFER:
|
|
+ DRM_DEBUG_KMS("aux_ch defer\n");
|
|
+ udelay(100);
|
|
+ break;
|
|
+ default:
|
|
+ DRM_ERROR("aux_ch invalid reply 0x%02x\n", reply[0]);
|
|
+ return -EREMOTEIO;
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
static int
|
|
@@ -431,11 +499,13 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
|
|
for (clock = 0; clock <= max_clock; clock++) {
|
|
int link_avail = intel_dp_link_clock(bws[clock]) * lane_count;
|
|
|
|
- if (intel_dp_link_required(mode->clock) <= link_avail) {
|
|
+ if (intel_dp_link_required(encoder->dev, intel_output, mode->clock)
|
|
+ <= link_avail) {
|
|
dp_priv->link_bw = bws[clock];
|
|
dp_priv->lane_count = lane_count;
|
|
adjusted_mode->clock = intel_dp_link_clock(dp_priv->link_bw);
|
|
- DRM_DEBUG("Display port link bw %02x lane count %d clock %d\n",
|
|
+ DRM_DEBUG_KMS("Display port link bw %02x lane "
|
|
+ "count %d clock %d\n",
|
|
dp_priv->link_bw, dp_priv->lane_count,
|
|
adjusted_mode->clock);
|
|
return true;
|
|
@@ -514,7 +584,7 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
|
|
intel_dp_compute_m_n(3, lane_count,
|
|
mode->clock, adjusted_mode->clock, &m_n);
|
|
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
if (intel_crtc->pipe == 0) {
|
|
I915_WRITE(TRANSA_DATA_M1,
|
|
((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
|
|
@@ -606,23 +676,23 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
|
|
}
|
|
}
|
|
|
|
-static void igdng_edp_backlight_on (struct drm_device *dev)
|
|
+static void ironlake_edp_backlight_on (struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
u32 pp;
|
|
|
|
- DRM_DEBUG("\n");
|
|
+ DRM_DEBUG_KMS("\n");
|
|
pp = I915_READ(PCH_PP_CONTROL);
|
|
pp |= EDP_BLC_ENABLE;
|
|
I915_WRITE(PCH_PP_CONTROL, pp);
|
|
}
|
|
|
|
-static void igdng_edp_backlight_off (struct drm_device *dev)
|
|
+static void ironlake_edp_backlight_off (struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
u32 pp;
|
|
|
|
- DRM_DEBUG("\n");
|
|
+ DRM_DEBUG_KMS("\n");
|
|
pp = I915_READ(PCH_PP_CONTROL);
|
|
pp &= ~EDP_BLC_ENABLE;
|
|
I915_WRITE(PCH_PP_CONTROL, pp);
|
|
@@ -641,13 +711,13 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode)
|
|
if (dp_reg & DP_PORT_EN) {
|
|
intel_dp_link_down(intel_output, dp_priv->DP);
|
|
if (IS_eDP(intel_output))
|
|
- igdng_edp_backlight_off(dev);
|
|
+ ironlake_edp_backlight_off(dev);
|
|
}
|
|
} else {
|
|
if (!(dp_reg & DP_PORT_EN)) {
|
|
intel_dp_link_train(intel_output, dp_priv->DP, dp_priv->link_configuration);
|
|
if (IS_eDP(intel_output))
|
|
- igdng_edp_backlight_on(dev);
|
|
+ ironlake_edp_backlight_on(dev);
|
|
}
|
|
}
|
|
dp_priv->dpms_mode = mode;
|
|
@@ -1010,7 +1080,7 @@ intel_dp_link_down(struct intel_output *intel_output, uint32_t DP)
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
struct intel_dp_priv *dp_priv = intel_output->dev_priv;
|
|
|
|
- DRM_DEBUG("\n");
|
|
+ DRM_DEBUG_KMS("\n");
|
|
|
|
if (IS_eDP(intel_output)) {
|
|
DP &= ~DP_PLL_ENABLE;
|
|
@@ -1071,7 +1141,7 @@ intel_dp_check_link_status(struct intel_output *intel_output)
|
|
}
|
|
|
|
static enum drm_connector_status
|
|
-igdng_dp_detect(struct drm_connector *connector)
|
|
+ironlake_dp_detect(struct drm_connector *connector)
|
|
{
|
|
struct intel_output *intel_output = to_intel_output(connector);
|
|
struct intel_dp_priv *dp_priv = intel_output->dev_priv;
|
|
@@ -1106,8 +1176,8 @@ intel_dp_detect(struct drm_connector *connector)
|
|
|
|
dp_priv->has_audio = false;
|
|
|
|
- if (IS_IGDNG(dev))
|
|
- return igdng_dp_detect(connector);
|
|
+ if (IS_IRONLAKE(dev))
|
|
+ return ironlake_dp_detect(connector);
|
|
|
|
temp = I915_READ(PORT_HOTPLUG_EN);
|
|
|
|
@@ -1261,11 +1331,10 @@ intel_dp_init(struct drm_device *dev, int output_reg)
|
|
else if (output_reg == DP_D || output_reg == PCH_DP_D)
|
|
intel_output->clone_mask = (1 << INTEL_DP_D_CLONE_BIT);
|
|
|
|
- if (IS_eDP(intel_output)) {
|
|
- intel_output->crtc_mask = (1 << 1);
|
|
+ if (IS_eDP(intel_output))
|
|
intel_output->clone_mask = (1 << INTEL_EDP_CLONE_BIT);
|
|
- } else
|
|
- intel_output->crtc_mask = (1 << 0) | (1 << 1);
|
|
+
|
|
+ intel_output->crtc_mask = (1 << 0) | (1 << 1);
|
|
connector->interlace_allowed = true;
|
|
connector->doublescan_allowed = 0;
|
|
|
|
diff --git a/drivers/gpu/drm/i915/intel_dp.h b/drivers/gpu/drm/i915/intel_dp.h
|
|
deleted file mode 100644
|
|
index 2b38054..0000000
|
|
--- a/drivers/gpu/drm/i915/intel_dp.h
|
|
+++ /dev/null
|
|
@@ -1,144 +0,0 @@
|
|
-/*
|
|
- * Copyright © 2008 Keith Packard
|
|
- *
|
|
- * Permission to use, copy, modify, distribute, and sell this software and its
|
|
- * documentation for any purpose is hereby granted without fee, provided that
|
|
- * the above copyright notice appear in all copies and that both that copyright
|
|
- * notice and this permission notice appear in supporting documentation, and
|
|
- * that the name of the copyright holders not be used in advertising or
|
|
- * publicity pertaining to distribution of the software without specific,
|
|
- * written prior permission. The copyright holders make no representations
|
|
- * about the suitability of this software for any purpose. It is provided "as
|
|
- * is" without express or implied warranty.
|
|
- *
|
|
- * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
- * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
|
- * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
|
- * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
|
- * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
- * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
|
- * OF THIS SOFTWARE.
|
|
- */
|
|
-
|
|
-#ifndef _INTEL_DP_H_
|
|
-#define _INTEL_DP_H_
|
|
-
|
|
-/* From the VESA DisplayPort spec */
|
|
-
|
|
-#define AUX_NATIVE_WRITE 0x8
|
|
-#define AUX_NATIVE_READ 0x9
|
|
-#define AUX_I2C_WRITE 0x0
|
|
-#define AUX_I2C_READ 0x1
|
|
-#define AUX_I2C_STATUS 0x2
|
|
-#define AUX_I2C_MOT 0x4
|
|
-
|
|
-#define AUX_NATIVE_REPLY_ACK (0x0 << 4)
|
|
-#define AUX_NATIVE_REPLY_NACK (0x1 << 4)
|
|
-#define AUX_NATIVE_REPLY_DEFER (0x2 << 4)
|
|
-#define AUX_NATIVE_REPLY_MASK (0x3 << 4)
|
|
-
|
|
-#define AUX_I2C_REPLY_ACK (0x0 << 6)
|
|
-#define AUX_I2C_REPLY_NACK (0x1 << 6)
|
|
-#define AUX_I2C_REPLY_DEFER (0x2 << 6)
|
|
-#define AUX_I2C_REPLY_MASK (0x3 << 6)
|
|
-
|
|
-/* AUX CH addresses */
|
|
-#define DP_LINK_BW_SET 0x100
|
|
-# define DP_LINK_BW_1_62 0x06
|
|
-# define DP_LINK_BW_2_7 0x0a
|
|
-
|
|
-#define DP_LANE_COUNT_SET 0x101
|
|
-# define DP_LANE_COUNT_MASK 0x0f
|
|
-# define DP_LANE_COUNT_ENHANCED_FRAME_EN (1 << 7)
|
|
-
|
|
-#define DP_TRAINING_PATTERN_SET 0x102
|
|
-
|
|
-# define DP_TRAINING_PATTERN_DISABLE 0
|
|
-# define DP_TRAINING_PATTERN_1 1
|
|
-# define DP_TRAINING_PATTERN_2 2
|
|
-# define DP_TRAINING_PATTERN_MASK 0x3
|
|
-
|
|
-# define DP_LINK_QUAL_PATTERN_DISABLE (0 << 2)
|
|
-# define DP_LINK_QUAL_PATTERN_D10_2 (1 << 2)
|
|
-# define DP_LINK_QUAL_PATTERN_ERROR_RATE (2 << 2)
|
|
-# define DP_LINK_QUAL_PATTERN_PRBS7 (3 << 2)
|
|
-# define DP_LINK_QUAL_PATTERN_MASK (3 << 2)
|
|
-
|
|
-# define DP_RECOVERED_CLOCK_OUT_EN (1 << 4)
|
|
-# define DP_LINK_SCRAMBLING_DISABLE (1 << 5)
|
|
-
|
|
-# define DP_SYMBOL_ERROR_COUNT_BOTH (0 << 6)
|
|
-# define DP_SYMBOL_ERROR_COUNT_DISPARITY (1 << 6)
|
|
-# define DP_SYMBOL_ERROR_COUNT_SYMBOL (2 << 6)
|
|
-# define DP_SYMBOL_ERROR_COUNT_MASK (3 << 6)
|
|
-
|
|
-#define DP_TRAINING_LANE0_SET 0x103
|
|
-#define DP_TRAINING_LANE1_SET 0x104
|
|
-#define DP_TRAINING_LANE2_SET 0x105
|
|
-#define DP_TRAINING_LANE3_SET 0x106
|
|
-
|
|
-# define DP_TRAIN_VOLTAGE_SWING_MASK 0x3
|
|
-# define DP_TRAIN_VOLTAGE_SWING_SHIFT 0
|
|
-# define DP_TRAIN_MAX_SWING_REACHED (1 << 2)
|
|
-# define DP_TRAIN_VOLTAGE_SWING_400 (0 << 0)
|
|
-# define DP_TRAIN_VOLTAGE_SWING_600 (1 << 0)
|
|
-# define DP_TRAIN_VOLTAGE_SWING_800 (2 << 0)
|
|
-# define DP_TRAIN_VOLTAGE_SWING_1200 (3 << 0)
|
|
-
|
|
-# define DP_TRAIN_PRE_EMPHASIS_MASK (3 << 3)
|
|
-# define DP_TRAIN_PRE_EMPHASIS_0 (0 << 3)
|
|
-# define DP_TRAIN_PRE_EMPHASIS_3_5 (1 << 3)
|
|
-# define DP_TRAIN_PRE_EMPHASIS_6 (2 << 3)
|
|
-# define DP_TRAIN_PRE_EMPHASIS_9_5 (3 << 3)
|
|
-
|
|
-# define DP_TRAIN_PRE_EMPHASIS_SHIFT 3
|
|
-# define DP_TRAIN_MAX_PRE_EMPHASIS_REACHED (1 << 5)
|
|
-
|
|
-#define DP_DOWNSPREAD_CTRL 0x107
|
|
-# define DP_SPREAD_AMP_0_5 (1 << 4)
|
|
-
|
|
-#define DP_MAIN_LINK_CHANNEL_CODING_SET 0x108
|
|
-# define DP_SET_ANSI_8B10B (1 << 0)
|
|
-
|
|
-#define DP_LANE0_1_STATUS 0x202
|
|
-#define DP_LANE2_3_STATUS 0x203
|
|
-
|
|
-# define DP_LANE_CR_DONE (1 << 0)
|
|
-# define DP_LANE_CHANNEL_EQ_DONE (1 << 1)
|
|
-# define DP_LANE_SYMBOL_LOCKED (1 << 2)
|
|
-
|
|
-#define DP_LANE_ALIGN_STATUS_UPDATED 0x204
|
|
-
|
|
-#define DP_INTERLANE_ALIGN_DONE (1 << 0)
|
|
-#define DP_DOWNSTREAM_PORT_STATUS_CHANGED (1 << 6)
|
|
-#define DP_LINK_STATUS_UPDATED (1 << 7)
|
|
-
|
|
-#define DP_SINK_STATUS 0x205
|
|
-
|
|
-#define DP_RECEIVE_PORT_0_STATUS (1 << 0)
|
|
-#define DP_RECEIVE_PORT_1_STATUS (1 << 1)
|
|
-
|
|
-#define DP_ADJUST_REQUEST_LANE0_1 0x206
|
|
-#define DP_ADJUST_REQUEST_LANE2_3 0x207
|
|
-
|
|
-#define DP_ADJUST_VOLTAGE_SWING_LANE0_MASK 0x03
|
|
-#define DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT 0
|
|
-#define DP_ADJUST_PRE_EMPHASIS_LANE0_MASK 0x0c
|
|
-#define DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT 2
|
|
-#define DP_ADJUST_VOLTAGE_SWING_LANE1_MASK 0x30
|
|
-#define DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT 4
|
|
-#define DP_ADJUST_PRE_EMPHASIS_LANE1_MASK 0xc0
|
|
-#define DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT 6
|
|
-
|
|
-struct i2c_algo_dp_aux_data {
|
|
- bool running;
|
|
- u16 address;
|
|
- int (*aux_ch) (struct i2c_adapter *adapter,
|
|
- uint8_t *send, int send_bytes,
|
|
- uint8_t *recv, int recv_bytes);
|
|
-};
|
|
-
|
|
-int
|
|
-i2c_dp_aux_add_bus(struct i2c_adapter *adapter);
|
|
-
|
|
-#endif /* _INTEL_DP_H_ */
|
|
diff --git a/drivers/gpu/drm/i915/intel_dp_i2c.c b/drivers/gpu/drm/i915/intel_dp_i2c.c
|
|
deleted file mode 100644
|
|
index a63b6f5..0000000
|
|
--- a/drivers/gpu/drm/i915/intel_dp_i2c.c
|
|
+++ /dev/null
|
|
@@ -1,273 +0,0 @@
|
|
-/*
|
|
- * Copyright © 2009 Keith Packard
|
|
- *
|
|
- * Permission to use, copy, modify, distribute, and sell this software and its
|
|
- * documentation for any purpose is hereby granted without fee, provided that
|
|
- * the above copyright notice appear in all copies and that both that copyright
|
|
- * notice and this permission notice appear in supporting documentation, and
|
|
- * that the name of the copyright holders not be used in advertising or
|
|
- * publicity pertaining to distribution of the software without specific,
|
|
- * written prior permission. The copyright holders make no representations
|
|
- * about the suitability of this software for any purpose. It is provided "as
|
|
- * is" without express or implied warranty.
|
|
- *
|
|
- * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
- * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
|
- * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
|
- * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
|
- * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
- * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
|
- * OF THIS SOFTWARE.
|
|
- */
|
|
-
|
|
-#include <linux/kernel.h>
|
|
-#include <linux/module.h>
|
|
-#include <linux/delay.h>
|
|
-#include <linux/slab.h>
|
|
-#include <linux/init.h>
|
|
-#include <linux/errno.h>
|
|
-#include <linux/sched.h>
|
|
-#include <linux/i2c.h>
|
|
-#include "intel_dp.h"
|
|
-#include "drmP.h"
|
|
-
|
|
-/* Run a single AUX_CH I2C transaction, writing/reading data as necessary */
|
|
-
|
|
-#define MODE_I2C_START 1
|
|
-#define MODE_I2C_WRITE 2
|
|
-#define MODE_I2C_READ 4
|
|
-#define MODE_I2C_STOP 8
|
|
-
|
|
-static int
|
|
-i2c_algo_dp_aux_transaction(struct i2c_adapter *adapter, int mode,
|
|
- uint8_t write_byte, uint8_t *read_byte)
|
|
-{
|
|
- struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
|
|
- uint16_t address = algo_data->address;
|
|
- uint8_t msg[5];
|
|
- uint8_t reply[2];
|
|
- int msg_bytes;
|
|
- int reply_bytes;
|
|
- int ret;
|
|
-
|
|
- /* Set up the command byte */
|
|
- if (mode & MODE_I2C_READ)
|
|
- msg[0] = AUX_I2C_READ << 4;
|
|
- else
|
|
- msg[0] = AUX_I2C_WRITE << 4;
|
|
-
|
|
- if (!(mode & MODE_I2C_STOP))
|
|
- msg[0] |= AUX_I2C_MOT << 4;
|
|
-
|
|
- msg[1] = address >> 8;
|
|
- msg[2] = address;
|
|
-
|
|
- switch (mode) {
|
|
- case MODE_I2C_WRITE:
|
|
- msg[3] = 0;
|
|
- msg[4] = write_byte;
|
|
- msg_bytes = 5;
|
|
- reply_bytes = 1;
|
|
- break;
|
|
- case MODE_I2C_READ:
|
|
- msg[3] = 0;
|
|
- msg_bytes = 4;
|
|
- reply_bytes = 2;
|
|
- break;
|
|
- default:
|
|
- msg_bytes = 3;
|
|
- reply_bytes = 1;
|
|
- break;
|
|
- }
|
|
-
|
|
- for (;;) {
|
|
- ret = (*algo_data->aux_ch)(adapter,
|
|
- msg, msg_bytes,
|
|
- reply, reply_bytes);
|
|
- if (ret < 0) {
|
|
- DRM_DEBUG("aux_ch failed %d\n", ret);
|
|
- return ret;
|
|
- }
|
|
- switch (reply[0] & AUX_I2C_REPLY_MASK) {
|
|
- case AUX_I2C_REPLY_ACK:
|
|
- if (mode == MODE_I2C_READ) {
|
|
- *read_byte = reply[1];
|
|
- }
|
|
- return reply_bytes - 1;
|
|
- case AUX_I2C_REPLY_NACK:
|
|
- DRM_DEBUG("aux_ch nack\n");
|
|
- return -EREMOTEIO;
|
|
- case AUX_I2C_REPLY_DEFER:
|
|
- DRM_DEBUG("aux_ch defer\n");
|
|
- udelay(100);
|
|
- break;
|
|
- default:
|
|
- DRM_ERROR("aux_ch invalid reply 0x%02x\n", reply[0]);
|
|
- return -EREMOTEIO;
|
|
- }
|
|
- }
|
|
-}
|
|
-
|
|
-/*
|
|
- * I2C over AUX CH
|
|
- */
|
|
-
|
|
-/*
|
|
- * Send the address. If the I2C link is running, this 'restarts'
|
|
- * the connection with the new address, this is used for doing
|
|
- * a write followed by a read (as needed for DDC)
|
|
- */
|
|
-static int
|
|
-i2c_algo_dp_aux_address(struct i2c_adapter *adapter, u16 address, bool reading)
|
|
-{
|
|
- struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
|
|
- int mode = MODE_I2C_START;
|
|
- int ret;
|
|
-
|
|
- if (reading)
|
|
- mode |= MODE_I2C_READ;
|
|
- else
|
|
- mode |= MODE_I2C_WRITE;
|
|
- algo_data->address = address;
|
|
- algo_data->running = true;
|
|
- ret = i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL);
|
|
- return ret;
|
|
-}
|
|
-
|
|
-/*
|
|
- * Stop the I2C transaction. This closes out the link, sending
|
|
- * a bare address packet with the MOT bit turned off
|
|
- */
|
|
-static void
|
|
-i2c_algo_dp_aux_stop(struct i2c_adapter *adapter, bool reading)
|
|
-{
|
|
- struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
|
|
- int mode = MODE_I2C_STOP;
|
|
-
|
|
- if (reading)
|
|
- mode |= MODE_I2C_READ;
|
|
- else
|
|
- mode |= MODE_I2C_WRITE;
|
|
- if (algo_data->running) {
|
|
- (void) i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL);
|
|
- algo_data->running = false;
|
|
- }
|
|
-}
|
|
-
|
|
-/*
|
|
- * Write a single byte to the current I2C address, the
|
|
- * the I2C link must be running or this returns -EIO
|
|
- */
|
|
-static int
|
|
-i2c_algo_dp_aux_put_byte(struct i2c_adapter *adapter, u8 byte)
|
|
-{
|
|
- struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
|
|
- int ret;
|
|
-
|
|
- if (!algo_data->running)
|
|
- return -EIO;
|
|
-
|
|
- ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_WRITE, byte, NULL);
|
|
- return ret;
|
|
-}
|
|
-
|
|
-/*
|
|
- * Read a single byte from the current I2C address, the
|
|
- * I2C link must be running or this returns -EIO
|
|
- */
|
|
-static int
|
|
-i2c_algo_dp_aux_get_byte(struct i2c_adapter *adapter, u8 *byte_ret)
|
|
-{
|
|
- struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
|
|
- int ret;
|
|
-
|
|
- if (!algo_data->running)
|
|
- return -EIO;
|
|
-
|
|
- ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_READ, 0, byte_ret);
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static int
|
|
-i2c_algo_dp_aux_xfer(struct i2c_adapter *adapter,
|
|
- struct i2c_msg *msgs,
|
|
- int num)
|
|
-{
|
|
- int ret = 0;
|
|
- bool reading = false;
|
|
- int m;
|
|
- int b;
|
|
-
|
|
- for (m = 0; m < num; m++) {
|
|
- u16 len = msgs[m].len;
|
|
- u8 *buf = msgs[m].buf;
|
|
- reading = (msgs[m].flags & I2C_M_RD) != 0;
|
|
- ret = i2c_algo_dp_aux_address(adapter, msgs[m].addr, reading);
|
|
- if (ret < 0)
|
|
- break;
|
|
- if (reading) {
|
|
- for (b = 0; b < len; b++) {
|
|
- ret = i2c_algo_dp_aux_get_byte(adapter, &buf[b]);
|
|
- if (ret < 0)
|
|
- break;
|
|
- }
|
|
- } else {
|
|
- for (b = 0; b < len; b++) {
|
|
- ret = i2c_algo_dp_aux_put_byte(adapter, buf[b]);
|
|
- if (ret < 0)
|
|
- break;
|
|
- }
|
|
- }
|
|
- if (ret < 0)
|
|
- break;
|
|
- }
|
|
- if (ret >= 0)
|
|
- ret = num;
|
|
- i2c_algo_dp_aux_stop(adapter, reading);
|
|
- DRM_DEBUG("dp_aux_xfer return %d\n", ret);
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static u32
|
|
-i2c_algo_dp_aux_functionality(struct i2c_adapter *adapter)
|
|
-{
|
|
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
|
|
- I2C_FUNC_SMBUS_READ_BLOCK_DATA |
|
|
- I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
|
|
- I2C_FUNC_10BIT_ADDR;
|
|
-}
|
|
-
|
|
-static const struct i2c_algorithm i2c_dp_aux_algo = {
|
|
- .master_xfer = i2c_algo_dp_aux_xfer,
|
|
- .functionality = i2c_algo_dp_aux_functionality,
|
|
-};
|
|
-
|
|
-static void
|
|
-i2c_dp_aux_reset_bus(struct i2c_adapter *adapter)
|
|
-{
|
|
- (void) i2c_algo_dp_aux_address(adapter, 0, false);
|
|
- (void) i2c_algo_dp_aux_stop(adapter, false);
|
|
-
|
|
-}
|
|
-
|
|
-static int
|
|
-i2c_dp_aux_prepare_bus(struct i2c_adapter *adapter)
|
|
-{
|
|
- adapter->algo = &i2c_dp_aux_algo;
|
|
- adapter->retries = 3;
|
|
- i2c_dp_aux_reset_bus(adapter);
|
|
- return 0;
|
|
-}
|
|
-
|
|
-int
|
|
-i2c_dp_aux_add_bus(struct i2c_adapter *adapter)
|
|
-{
|
|
- int error;
|
|
-
|
|
- error = i2c_dp_aux_prepare_bus(adapter);
|
|
- if (error)
|
|
- return error;
|
|
- error = i2c_add_adapter(adapter);
|
|
- return error;
|
|
-}
|
|
-EXPORT_SYMBOL(i2c_dp_aux_add_bus);
|
|
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
|
|
index ef61fe9..a51573d 100644
|
|
--- a/drivers/gpu/drm/i915/intel_drv.h
|
|
+++ b/drivers/gpu/drm/i915/intel_drv.h
|
|
@@ -110,6 +110,32 @@ struct intel_output {
|
|
int clone_mask;
|
|
};
|
|
|
|
+struct intel_crtc;
|
|
+struct intel_overlay {
|
|
+ struct drm_device *dev;
|
|
+ struct intel_crtc *crtc;
|
|
+ struct drm_i915_gem_object *vid_bo;
|
|
+ struct drm_i915_gem_object *old_vid_bo;
|
|
+ int active;
|
|
+ int pfit_active;
|
|
+ u32 pfit_vscale_ratio; /* shifted-point number, (1<<12) == 1.0 */
|
|
+ u32 color_key;
|
|
+ u32 brightness, contrast, saturation;
|
|
+ u32 old_xscale, old_yscale;
|
|
+ /* register access */
|
|
+ u32 flip_addr;
|
|
+ struct drm_i915_gem_object *reg_bo;
|
|
+ void *virt_addr;
|
|
+ /* flip handling */
|
|
+ uint32_t last_flip_req;
|
|
+ int hw_wedged;
|
|
+#define HW_WEDGED 1
|
|
+#define NEEDS_WAIT_FOR_FLIP 2
|
|
+#define RELEASE_OLD_VID 3
|
|
+#define SWITCH_OFF_STAGE_1 4
|
|
+#define SWITCH_OFF_STAGE_2 5
|
|
+};
|
|
+
|
|
struct intel_crtc {
|
|
struct drm_crtc base;
|
|
enum pipe pipe;
|
|
@@ -121,6 +147,8 @@ struct intel_crtc {
|
|
bool busy; /* is scanout buffer being updated frequently? */
|
|
struct timer_list idle_timer;
|
|
bool lowfreq_avail;
|
|
+ struct intel_overlay *overlay;
|
|
+ struct intel_unpin_work *unpin_work;
|
|
};
|
|
|
|
#define to_intel_crtc(x) container_of(x, struct intel_crtc, base)
|
|
@@ -134,6 +162,8 @@ void intel_i2c_destroy(struct i2c_adapter *adapter);
|
|
int intel_ddc_get_modes(struct intel_output *intel_output);
|
|
extern bool intel_ddc_probe(struct intel_output *intel_output);
|
|
void intel_i2c_quirk_set(struct drm_device *dev, bool enable);
|
|
+void intel_i2c_reset_gmbus(struct drm_device *dev);
|
|
+
|
|
extern void intel_crt_init(struct drm_device *dev);
|
|
extern void intel_hdmi_init(struct drm_device *dev, int sdvox_reg);
|
|
extern bool intel_sdvo_init(struct drm_device *dev, int output_device);
|
|
@@ -148,6 +178,7 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
|
|
extern void intel_edp_link_config (struct intel_output *, int *, int *);
|
|
|
|
|
|
+extern int intel_panel_fitter_pipe (struct drm_device *dev);
|
|
extern void intel_crtc_load_lut(struct drm_crtc *crtc);
|
|
extern void intel_encoder_prepare (struct drm_encoder *encoder);
|
|
extern void intel_encoder_commit (struct drm_encoder *encoder);
|
|
@@ -177,10 +208,23 @@ extern void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
|
|
u16 blue, int regno);
|
|
extern void intel_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
|
|
u16 *blue, int regno);
|
|
+extern void intel_init_clock_gating(struct drm_device *dev);
|
|
|
|
extern int intel_framebuffer_create(struct drm_device *dev,
|
|
struct drm_mode_fb_cmd *mode_cmd,
|
|
struct drm_framebuffer **fb,
|
|
struct drm_gem_object *obj);
|
|
|
|
+extern void intel_prepare_page_flip(struct drm_device *dev, int plane);
|
|
+extern void intel_finish_page_flip(struct drm_device *dev, int pipe);
|
|
+
|
|
+extern void intel_setup_overlay(struct drm_device *dev);
|
|
+extern void intel_cleanup_overlay(struct drm_device *dev);
|
|
+extern int intel_overlay_switch_off(struct intel_overlay *overlay);
|
|
+extern int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay,
|
|
+ int interruptible);
|
|
+extern int intel_overlay_put_image(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv);
|
|
+extern int intel_overlay_attrs(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv);
|
|
#endif /* __INTEL_DRV_H__ */
|
|
diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c
|
|
index 2b0fe54..aaabbcb 100644
|
|
--- a/drivers/gpu/drm/i915/intel_fb.c
|
|
+++ b/drivers/gpu/drm/i915/intel_fb.c
|
|
@@ -70,7 +70,7 @@ static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
|
|
|
|
|
|
/**
|
|
- * Curretly it is assumed that the old framebuffer is reused.
|
|
+ * Currently it is assumed that the old framebuffer is reused.
|
|
*
|
|
* LOCKING
|
|
* caller should hold the mode config lock.
|
|
@@ -148,7 +148,7 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width,
|
|
|
|
mutex_lock(&dev->struct_mutex);
|
|
|
|
- ret = i915_gem_object_pin(fbo, PAGE_SIZE);
|
|
+ ret = i915_gem_object_pin(fbo, 64*1024);
|
|
if (ret) {
|
|
DRM_ERROR("failed to pin fb: %d\n", ret);
|
|
goto out_unref;
|
|
@@ -230,8 +230,9 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width,
|
|
par->intel_fb = intel_fb;
|
|
|
|
/* To allow resizeing without swapping buffers */
|
|
- DRM_DEBUG("allocated %dx%d fb: 0x%08x, bo %p\n", intel_fb->base.width,
|
|
- intel_fb->base.height, obj_priv->gtt_offset, fbo);
|
|
+ DRM_DEBUG_KMS("allocated %dx%d fb: 0x%08x, bo %p\n",
|
|
+ intel_fb->base.width, intel_fb->base.height,
|
|
+ obj_priv->gtt_offset, fbo);
|
|
|
|
mutex_unlock(&dev->struct_mutex);
|
|
return 0;
|
|
@@ -249,7 +250,7 @@ int intelfb_probe(struct drm_device *dev)
|
|
{
|
|
int ret;
|
|
|
|
- DRM_DEBUG("\n");
|
|
+ DRM_DEBUG_KMS("\n");
|
|
ret = drm_fb_helper_single_fb_probe(dev, 32, intelfb_create);
|
|
return ret;
|
|
}
|
|
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
|
|
index 85760bf..0e268de 100644
|
|
--- a/drivers/gpu/drm/i915/intel_hdmi.c
|
|
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
|
|
@@ -82,7 +82,7 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
|
|
/* HW workaround, need to toggle enable bit off and on for 12bpc, but
|
|
* we do this anyway which shows more stable in testing.
|
|
*/
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
I915_WRITE(hdmi_priv->sdvox_reg, temp & ~SDVO_ENABLE);
|
|
POSTING_READ(hdmi_priv->sdvox_reg);
|
|
}
|
|
@@ -99,7 +99,7 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
|
|
/* HW workaround, need to write this twice for issue that may result
|
|
* in first write getting masked.
|
|
*/
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
I915_WRITE(hdmi_priv->sdvox_reg, temp);
|
|
POSTING_READ(hdmi_priv->sdvox_reg);
|
|
}
|
|
@@ -225,7 +225,6 @@ static const struct drm_encoder_funcs intel_hdmi_enc_funcs = {
|
|
.destroy = intel_hdmi_enc_destroy,
|
|
};
|
|
|
|
-
|
|
void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
|
|
index c7eab72..8673c73 100644
|
|
--- a/drivers/gpu/drm/i915/intel_i2c.c
|
|
+++ b/drivers/gpu/drm/i915/intel_i2c.c
|
|
@@ -39,7 +39,7 @@ void intel_i2c_quirk_set(struct drm_device *dev, bool enable)
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
/* When using bit bashing for I2C, this bit needs to be set to 1 */
|
|
- if (!IS_IGD(dev))
|
|
+ if (!IS_PINEVIEW(dev))
|
|
return;
|
|
if (enable)
|
|
I915_WRITE(DSPCLK_GATE_D,
|
|
@@ -118,6 +118,23 @@ static void set_data(void *data, int state_high)
|
|
udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */
|
|
}
|
|
|
|
+/* Clears the GMBUS setup. Our driver doesn't make use of the GMBUS I2C
|
|
+ * engine, but if the BIOS leaves it enabled, then that can break our use
|
|
+ * of the bit-banging I2C interfaces. This is notably the case with the
|
|
+ * Mac Mini in EFI mode.
|
|
+ */
|
|
+void
|
|
+intel_i2c_reset_gmbus(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
+ I915_WRITE(PCH_GMBUS0, 0);
|
|
+ } else {
|
|
+ I915_WRITE(GMBUS0, 0);
|
|
+ }
|
|
+}
|
|
+
|
|
/**
|
|
* intel_i2c_create - instantiate an Intel i2c bus using the specified GPIO reg
|
|
* @dev: DRM device
|
|
@@ -168,6 +185,8 @@ struct i2c_adapter *intel_i2c_create(struct drm_device *dev, const u32 reg,
|
|
if(i2c_bit_add_bus(&chan->adapter))
|
|
goto out_free;
|
|
|
|
+ intel_i2c_reset_gmbus(dev);
|
|
+
|
|
/* JJJ: raise SCL and SDA? */
|
|
intel_i2c_quirk_set(dev, true);
|
|
set_data(chan, 1);
|
|
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
|
|
index 0e0e4b4..b1d0acb 100644
|
|
--- a/drivers/gpu/drm/i915/intel_lvds.c
|
|
+++ b/drivers/gpu/drm/i915/intel_lvds.c
|
|
@@ -56,7 +56,7 @@ static void intel_lvds_set_backlight(struct drm_device *dev, int level)
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
u32 blc_pwm_ctl, reg;
|
|
|
|
- if (IS_IGDNG(dev))
|
|
+ if (IS_IRONLAKE(dev))
|
|
reg = BLC_PWM_CPU_CTL;
|
|
else
|
|
reg = BLC_PWM_CTL;
|
|
@@ -74,7 +74,7 @@ static u32 intel_lvds_get_max_backlight(struct drm_device *dev)
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
u32 reg;
|
|
|
|
- if (IS_IGDNG(dev))
|
|
+ if (IS_IRONLAKE(dev))
|
|
reg = BLC_PWM_PCH_CTL2;
|
|
else
|
|
reg = BLC_PWM_CTL;
|
|
@@ -91,7 +91,7 @@ static void intel_lvds_set_power(struct drm_device *dev, bool on)
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
u32 pp_status, ctl_reg, status_reg;
|
|
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
ctl_reg = PCH_PP_CONTROL;
|
|
status_reg = PCH_PP_STATUS;
|
|
} else {
|
|
@@ -137,7 +137,7 @@ static void intel_lvds_save(struct drm_connector *connector)
|
|
u32 pp_on_reg, pp_off_reg, pp_ctl_reg, pp_div_reg;
|
|
u32 pwm_ctl_reg;
|
|
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
pp_on_reg = PCH_PP_ON_DELAYS;
|
|
pp_off_reg = PCH_PP_OFF_DELAYS;
|
|
pp_ctl_reg = PCH_PP_CONTROL;
|
|
@@ -174,7 +174,7 @@ static void intel_lvds_restore(struct drm_connector *connector)
|
|
u32 pp_on_reg, pp_off_reg, pp_ctl_reg, pp_div_reg;
|
|
u32 pwm_ctl_reg;
|
|
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
pp_on_reg = PCH_PP_ON_DELAYS;
|
|
pp_off_reg = PCH_PP_OFF_DELAYS;
|
|
pp_ctl_reg = PCH_PP_CONTROL;
|
|
@@ -297,7 +297,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
|
|
}
|
|
|
|
/* full screen scale for now */
|
|
- if (IS_IGDNG(dev))
|
|
+ if (IS_IRONLAKE(dev))
|
|
goto out;
|
|
|
|
/* 965+ wants fuzzy fitting */
|
|
@@ -327,7 +327,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
|
|
* to register description and PRM.
|
|
* Change the value here to see the borders for debugging
|
|
*/
|
|
- if (!IS_IGDNG(dev)) {
|
|
+ if (!IS_IRONLAKE(dev)) {
|
|
I915_WRITE(BCLRPAT_A, 0);
|
|
I915_WRITE(BCLRPAT_B, 0);
|
|
}
|
|
@@ -548,7 +548,7 @@ static void intel_lvds_prepare(struct drm_encoder *encoder)
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
u32 reg;
|
|
|
|
- if (IS_IGDNG(dev))
|
|
+ if (IS_IRONLAKE(dev))
|
|
reg = BLC_PWM_CPU_CTL;
|
|
else
|
|
reg = BLC_PWM_CTL;
|
|
@@ -587,7 +587,7 @@ static void intel_lvds_mode_set(struct drm_encoder *encoder,
|
|
* settings.
|
|
*/
|
|
|
|
- if (IS_IGDNG(dev))
|
|
+ if (IS_IRONLAKE(dev))
|
|
return;
|
|
|
|
/*
|
|
@@ -602,12 +602,40 @@ static void intel_lvds_mode_set(struct drm_encoder *encoder,
|
|
/* Some lid devices report incorrect lid status, assume they're connected */
|
|
static const struct dmi_system_id bad_lid_status[] = {
|
|
{
|
|
+ .ident = "Compaq nx9020",
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "3084"),
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .ident = "Samsung SX20S",
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_SYS_VENDOR, "Samsung Electronics"),
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "SX20S"),
|
|
+ },
|
|
+ },
|
|
+ {
|
|
.ident = "Aspire One",
|
|
.matches = {
|
|
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
|
|
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire one"),
|
|
},
|
|
},
|
|
+ {
|
|
+ .ident = "Aspire 1810T",
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
|
|
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1810T"),
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .ident = "PC-81005",
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_SYS_VENDOR, "MALATA"),
|
|
+ DMI_MATCH(DMI_PRODUCT_NAME, "PC-81005"),
|
|
+ },
|
|
+ },
|
|
{ }
|
|
};
|
|
|
|
@@ -622,7 +650,7 @@ static enum drm_connector_status intel_lvds_detect(struct drm_connector *connect
|
|
{
|
|
enum drm_connector_status status = connector_status_connected;
|
|
|
|
- if (!acpi_lid_open() && !dmi_check_system(bad_lid_status))
|
|
+ if (!dmi_check_system(bad_lid_status) && !acpi_lid_open())
|
|
status = connector_status_disconnected;
|
|
|
|
return status;
|
|
@@ -861,64 +889,101 @@ static const struct dmi_system_id intel_no_lvds[] = {
|
|
{ } /* terminating entry */
|
|
};
|
|
|
|
-#ifdef CONFIG_ACPI
|
|
-/*
|
|
- * check_lid_device -- check whether @handle is an ACPI LID device.
|
|
- * @handle: ACPI device handle
|
|
- * @level : depth in the ACPI namespace tree
|
|
- * @context: the number of LID device when we find the device
|
|
- * @rv: a return value to fill if desired (Not use)
|
|
+/**
|
|
+ * intel_find_lvds_downclock - find the reduced downclock for LVDS in EDID
|
|
+ * @dev: drm device
|
|
+ * @connector: LVDS connector
|
|
+ *
|
|
+ * Find the reduced downclock for LVDS in EDID.
|
|
*/
|
|
-static acpi_status
|
|
-check_lid_device(acpi_handle handle, u32 level, void *context,
|
|
- void **return_value)
|
|
+static void intel_find_lvds_downclock(struct drm_device *dev,
|
|
+ struct drm_connector *connector)
|
|
{
|
|
- struct acpi_device *acpi_dev;
|
|
- int *lid_present = context;
|
|
-
|
|
- acpi_dev = NULL;
|
|
- /* Get the acpi device for device handle */
|
|
- if (acpi_bus_get_device(handle, &acpi_dev) || !acpi_dev) {
|
|
- /* If there is no ACPI device for handle, return */
|
|
- return AE_OK;
|
|
- }
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
+ struct drm_display_mode *scan, *panel_fixed_mode;
|
|
+ int temp_downclock;
|
|
|
|
- if (!strncmp(acpi_device_hid(acpi_dev), "PNP0C0D", 7))
|
|
- *lid_present = 1;
|
|
+ panel_fixed_mode = dev_priv->panel_fixed_mode;
|
|
+ temp_downclock = panel_fixed_mode->clock;
|
|
|
|
- return AE_OK;
|
|
+ mutex_lock(&dev->mode_config.mutex);
|
|
+ list_for_each_entry(scan, &connector->probed_modes, head) {
|
|
+ /*
|
|
+ * If one mode has the same resolution with the fixed_panel
|
|
+ * mode while they have the different refresh rate, it means
|
|
+ * that the reduced downclock is found for the LVDS. In such
|
|
+ * case we can set the different FPx0/1 to dynamically select
|
|
+ * between low and high frequency.
|
|
+ */
|
|
+ if (scan->hdisplay == panel_fixed_mode->hdisplay &&
|
|
+ scan->hsync_start == panel_fixed_mode->hsync_start &&
|
|
+ scan->hsync_end == panel_fixed_mode->hsync_end &&
|
|
+ scan->htotal == panel_fixed_mode->htotal &&
|
|
+ scan->vdisplay == panel_fixed_mode->vdisplay &&
|
|
+ scan->vsync_start == panel_fixed_mode->vsync_start &&
|
|
+ scan->vsync_end == panel_fixed_mode->vsync_end &&
|
|
+ scan->vtotal == panel_fixed_mode->vtotal) {
|
|
+ if (scan->clock < temp_downclock) {
|
|
+ /*
|
|
+ * The downclock is already found. But we
|
|
+ * expect to find the lower downclock.
|
|
+ */
|
|
+ temp_downclock = scan->clock;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ mutex_unlock(&dev->mode_config.mutex);
|
|
+ if (temp_downclock < panel_fixed_mode->clock &&
|
|
+ i915_lvds_downclock) {
|
|
+ /* We found the downclock for LVDS. */
|
|
+ dev_priv->lvds_downclock_avail = 1;
|
|
+ dev_priv->lvds_downclock = temp_downclock;
|
|
+ DRM_DEBUG_KMS("LVDS downclock is found in EDID. "
|
|
+ "Normal clock %dKhz, downclock %dKhz\n",
|
|
+ panel_fixed_mode->clock, temp_downclock);
|
|
+ }
|
|
+ return;
|
|
}
|
|
|
|
-/**
|
|
- * check whether there exists the ACPI LID device by enumerating the ACPI
|
|
- * device tree.
|
|
+/*
|
|
+ * Enumerate the child dev array parsed from VBT to check whether
|
|
+ * the LVDS is present.
|
|
+ * If it is present, return 1.
|
|
+ * If it is not present, return false.
|
|
+ * If no child dev is parsed from VBT, it assumes that the LVDS is present.
|
|
+ * Note: The addin_offset should also be checked for LVDS panel.
|
|
+ * Only when it is non-zero, it is assumed that it is present.
|
|
*/
|
|
-static int intel_lid_present(void)
|
|
+static int lvds_is_present_in_vbt(struct drm_device *dev)
|
|
{
|
|
- int lid_present = 0;
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
+ struct child_device_config *p_child;
|
|
+ int i, ret;
|
|
|
|
- if (acpi_disabled) {
|
|
- /* If ACPI is disabled, there is no ACPI device tree to
|
|
- * check, so assume the LID device would have been present.
|
|
- */
|
|
+ if (!dev_priv->child_dev_num)
|
|
return 1;
|
|
- }
|
|
|
|
- acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
|
- ACPI_UINT32_MAX,
|
|
- check_lid_device, &lid_present, NULL);
|
|
+ ret = 0;
|
|
+ for (i = 0; i < dev_priv->child_dev_num; i++) {
|
|
+ p_child = dev_priv->child_dev + i;
|
|
+ /*
|
|
+ * If the device type is not LFP, continue.
|
|
+ * If the device type is 0x22, it is also regarded as LFP.
|
|
+ */
|
|
+ if (p_child->device_type != DEVICE_TYPE_INT_LFP &&
|
|
+ p_child->device_type != DEVICE_TYPE_LFP)
|
|
+ continue;
|
|
|
|
- return lid_present;
|
|
-}
|
|
-#else
|
|
-static int intel_lid_present(void)
|
|
-{
|
|
- /* In the absence of ACPI built in, assume that the LID device would
|
|
- * have been present.
|
|
- */
|
|
- return 1;
|
|
+ /* The addin_offset should be checked. Only when it is
|
|
+ * non-zero, it is regarded as present.
|
|
+ */
|
|
+ if (p_child->addin_offset) {
|
|
+ ret = 1;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return ret;
|
|
}
|
|
-#endif
|
|
|
|
/**
|
|
* intel_lvds_init - setup LVDS connectors on this device
|
|
@@ -943,21 +1008,16 @@ void intel_lvds_init(struct drm_device *dev)
|
|
if (dmi_check_system(intel_no_lvds))
|
|
return;
|
|
|
|
- /* Assume that any device without an ACPI LID device also doesn't
|
|
- * have an integrated LVDS. We would be better off parsing the BIOS
|
|
- * to get a reliable indicator, but that code isn't written yet.
|
|
- *
|
|
- * In the case of all-in-one desktops using LVDS that we've seen,
|
|
- * they're using SDVO LVDS.
|
|
- */
|
|
- if (!intel_lid_present())
|
|
+ if (!lvds_is_present_in_vbt(dev)) {
|
|
+ DRM_DEBUG_KMS("LVDS is not present in VBT\n");
|
|
return;
|
|
+ }
|
|
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
if ((I915_READ(PCH_LVDS) & LVDS_DETECTED) == 0)
|
|
return;
|
|
if (dev_priv->edp_support) {
|
|
- DRM_DEBUG("disable LVDS for eDP support\n");
|
|
+ DRM_DEBUG_KMS("disable LVDS for eDP support\n");
|
|
return;
|
|
}
|
|
gpio = PCH_GPIOC;
|
|
@@ -1030,6 +1090,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);
|
|
+ intel_find_lvds_downclock(dev, connector);
|
|
goto out;
|
|
}
|
|
mutex_unlock(&dev->mode_config.mutex);
|
|
@@ -1054,8 +1115,8 @@ void intel_lvds_init(struct drm_device *dev)
|
|
* correct mode.
|
|
*/
|
|
|
|
- /* IGDNG: FIXME if still fail, not try pipe mode now */
|
|
- if (IS_IGDNG(dev))
|
|
+ /* Ironlake: FIXME if still fail, not try pipe mode now */
|
|
+ if (IS_IRONLAKE(dev))
|
|
goto failed;
|
|
|
|
lvds = I915_READ(LVDS);
|
|
@@ -1076,7 +1137,7 @@ void intel_lvds_init(struct drm_device *dev)
|
|
goto failed;
|
|
|
|
out:
|
|
- if (IS_IGDNG(dev)) {
|
|
+ if (IS_IRONLAKE(dev)) {
|
|
u32 pwm;
|
|
/* make sure PWM is enabled */
|
|
pwm = I915_READ(BLC_PWM_CPU_CTL2);
|
|
@@ -1089,7 +1150,7 @@ out:
|
|
}
|
|
dev_priv->lid_notifier.notifier_call = intel_lid_notify;
|
|
if (acpi_lid_notifier_register(&dev_priv->lid_notifier)) {
|
|
- DRM_DEBUG("lid notifier registration failed\n");
|
|
+ DRM_DEBUG_KMS("lid notifier registration failed\n");
|
|
dev_priv->lid_notifier.notifier_call = NULL;
|
|
}
|
|
/* keep the LVDS connector */
|
|
@@ -1102,5 +1163,6 @@ failed:
|
|
if (intel_output->ddc_bus)
|
|
intel_i2c_destroy(intel_output->ddc_bus);
|
|
drm_connector_cleanup(connector);
|
|
+ drm_encoder_cleanup(encoder);
|
|
kfree(intel_output);
|
|
}
|
|
diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c
|
|
new file mode 100644
|
|
index 0000000..2639591
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/i915/intel_overlay.c
|
|
@@ -0,0 +1,1416 @@
|
|
+/*
|
|
+ * Copyright © 2009
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the next
|
|
+ * paragraph) shall be included in all copies or substantial portions of the
|
|
+ * Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
+ * SOFTWARE.
|
|
+ *
|
|
+ * Authors:
|
|
+ * Daniel Vetter <daniel@ffwll.ch>
|
|
+ *
|
|
+ * Derived from Xorg ddx, xf86-video-intel, src/i830_video.c
|
|
+ */
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "i915_drm.h"
|
|
+#include "i915_drv.h"
|
|
+#include "i915_reg.h"
|
|
+#include "intel_drv.h"
|
|
+
|
|
+/* Limits for overlay size. According to intel doc, the real limits are:
|
|
+ * Y width: 4095, UV width (planar): 2047, Y height: 2047,
|
|
+ * UV width (planar): * 1023. But the xorg thinks 2048 for height and width. Use
|
|
+ * the mininum of both. */
|
|
+#define IMAGE_MAX_WIDTH 2048
|
|
+#define IMAGE_MAX_HEIGHT 2046 /* 2 * 1023 */
|
|
+/* on 830 and 845 these large limits result in the card hanging */
|
|
+#define IMAGE_MAX_WIDTH_LEGACY 1024
|
|
+#define IMAGE_MAX_HEIGHT_LEGACY 1088
|
|
+
|
|
+/* overlay register definitions */
|
|
+/* OCMD register */
|
|
+#define OCMD_TILED_SURFACE (0x1<<19)
|
|
+#define OCMD_MIRROR_MASK (0x3<<17)
|
|
+#define OCMD_MIRROR_MODE (0x3<<17)
|
|
+#define OCMD_MIRROR_HORIZONTAL (0x1<<17)
|
|
+#define OCMD_MIRROR_VERTICAL (0x2<<17)
|
|
+#define OCMD_MIRROR_BOTH (0x3<<17)
|
|
+#define OCMD_BYTEORDER_MASK (0x3<<14) /* zero for YUYV or FOURCC YUY2 */
|
|
+#define OCMD_UV_SWAP (0x1<<14) /* YVYU */
|
|
+#define OCMD_Y_SWAP (0x2<<14) /* UYVY or FOURCC UYVY */
|
|
+#define OCMD_Y_AND_UV_SWAP (0x3<<14) /* VYUY */
|
|
+#define OCMD_SOURCE_FORMAT_MASK (0xf<<10)
|
|
+#define OCMD_RGB_888 (0x1<<10) /* not in i965 Intel docs */
|
|
+#define OCMD_RGB_555 (0x2<<10) /* not in i965 Intel docs */
|
|
+#define OCMD_RGB_565 (0x3<<10) /* not in i965 Intel docs */
|
|
+#define OCMD_YUV_422_PACKED (0x8<<10)
|
|
+#define OCMD_YUV_411_PACKED (0x9<<10) /* not in i965 Intel docs */
|
|
+#define OCMD_YUV_420_PLANAR (0xc<<10)
|
|
+#define OCMD_YUV_422_PLANAR (0xd<<10)
|
|
+#define OCMD_YUV_410_PLANAR (0xe<<10) /* also 411 */
|
|
+#define OCMD_TVSYNCFLIP_PARITY (0x1<<9)
|
|
+#define OCMD_TVSYNCFLIP_ENABLE (0x1<<7)
|
|
+#define OCMD_BUF_TYPE_MASK (Ox1<<5)
|
|
+#define OCMD_BUF_TYPE_FRAME (0x0<<5)
|
|
+#define OCMD_BUF_TYPE_FIELD (0x1<<5)
|
|
+#define OCMD_TEST_MODE (0x1<<4)
|
|
+#define OCMD_BUFFER_SELECT (0x3<<2)
|
|
+#define OCMD_BUFFER0 (0x0<<2)
|
|
+#define OCMD_BUFFER1 (0x1<<2)
|
|
+#define OCMD_FIELD_SELECT (0x1<<2)
|
|
+#define OCMD_FIELD0 (0x0<<1)
|
|
+#define OCMD_FIELD1 (0x1<<1)
|
|
+#define OCMD_ENABLE (0x1<<0)
|
|
+
|
|
+/* OCONFIG register */
|
|
+#define OCONF_PIPE_MASK (0x1<<18)
|
|
+#define OCONF_PIPE_A (0x0<<18)
|
|
+#define OCONF_PIPE_B (0x1<<18)
|
|
+#define OCONF_GAMMA2_ENABLE (0x1<<16)
|
|
+#define OCONF_CSC_MODE_BT601 (0x0<<5)
|
|
+#define OCONF_CSC_MODE_BT709 (0x1<<5)
|
|
+#define OCONF_CSC_BYPASS (0x1<<4)
|
|
+#define OCONF_CC_OUT_8BIT (0x1<<3)
|
|
+#define OCONF_TEST_MODE (0x1<<2)
|
|
+#define OCONF_THREE_LINE_BUFFER (0x1<<0)
|
|
+#define OCONF_TWO_LINE_BUFFER (0x0<<0)
|
|
+
|
|
+/* DCLRKM (dst-key) register */
|
|
+#define DST_KEY_ENABLE (0x1<<31)
|
|
+#define CLK_RGB24_MASK 0x0
|
|
+#define CLK_RGB16_MASK 0x070307
|
|
+#define CLK_RGB15_MASK 0x070707
|
|
+#define CLK_RGB8I_MASK 0xffffff
|
|
+
|
|
+#define RGB16_TO_COLORKEY(c) \
|
|
+ (((c & 0xF800) << 8) | ((c & 0x07E0) << 5) | ((c & 0x001F) << 3))
|
|
+#define RGB15_TO_COLORKEY(c) \
|
|
+ (((c & 0x7c00) << 9) | ((c & 0x03E0) << 6) | ((c & 0x001F) << 3))
|
|
+
|
|
+/* overlay flip addr flag */
|
|
+#define OFC_UPDATE 0x1
|
|
+
|
|
+/* polyphase filter coefficients */
|
|
+#define N_HORIZ_Y_TAPS 5
|
|
+#define N_VERT_Y_TAPS 3
|
|
+#define N_HORIZ_UV_TAPS 3
|
|
+#define N_VERT_UV_TAPS 3
|
|
+#define N_PHASES 17
|
|
+#define MAX_TAPS 5
|
|
+
|
|
+/* memory bufferd overlay registers */
|
|
+struct overlay_registers {
|
|
+ u32 OBUF_0Y;
|
|
+ u32 OBUF_1Y;
|
|
+ u32 OBUF_0U;
|
|
+ u32 OBUF_0V;
|
|
+ u32 OBUF_1U;
|
|
+ u32 OBUF_1V;
|
|
+ u32 OSTRIDE;
|
|
+ u32 YRGB_VPH;
|
|
+ u32 UV_VPH;
|
|
+ u32 HORZ_PH;
|
|
+ u32 INIT_PHS;
|
|
+ u32 DWINPOS;
|
|
+ u32 DWINSZ;
|
|
+ u32 SWIDTH;
|
|
+ u32 SWIDTHSW;
|
|
+ u32 SHEIGHT;
|
|
+ u32 YRGBSCALE;
|
|
+ u32 UVSCALE;
|
|
+ u32 OCLRC0;
|
|
+ u32 OCLRC1;
|
|
+ u32 DCLRKV;
|
|
+ u32 DCLRKM;
|
|
+ u32 SCLRKVH;
|
|
+ u32 SCLRKVL;
|
|
+ u32 SCLRKEN;
|
|
+ u32 OCONFIG;
|
|
+ u32 OCMD;
|
|
+ u32 RESERVED1; /* 0x6C */
|
|
+ u32 OSTART_0Y;
|
|
+ u32 OSTART_1Y;
|
|
+ u32 OSTART_0U;
|
|
+ u32 OSTART_0V;
|
|
+ u32 OSTART_1U;
|
|
+ u32 OSTART_1V;
|
|
+ u32 OTILEOFF_0Y;
|
|
+ u32 OTILEOFF_1Y;
|
|
+ u32 OTILEOFF_0U;
|
|
+ u32 OTILEOFF_0V;
|
|
+ u32 OTILEOFF_1U;
|
|
+ u32 OTILEOFF_1V;
|
|
+ u32 FASTHSCALE; /* 0xA0 */
|
|
+ u32 UVSCALEV; /* 0xA4 */
|
|
+ u32 RESERVEDC[(0x200 - 0xA8) / 4]; /* 0xA8 - 0x1FC */
|
|
+ u16 Y_VCOEFS[N_VERT_Y_TAPS * N_PHASES]; /* 0x200 */
|
|
+ u16 RESERVEDD[0x100 / 2 - N_VERT_Y_TAPS * N_PHASES];
|
|
+ u16 Y_HCOEFS[N_HORIZ_Y_TAPS * N_PHASES]; /* 0x300 */
|
|
+ u16 RESERVEDE[0x200 / 2 - N_HORIZ_Y_TAPS * N_PHASES];
|
|
+ u16 UV_VCOEFS[N_VERT_UV_TAPS * N_PHASES]; /* 0x500 */
|
|
+ u16 RESERVEDF[0x100 / 2 - N_VERT_UV_TAPS * N_PHASES];
|
|
+ u16 UV_HCOEFS[N_HORIZ_UV_TAPS * N_PHASES]; /* 0x600 */
|
|
+ u16 RESERVEDG[0x100 / 2 - N_HORIZ_UV_TAPS * N_PHASES];
|
|
+};
|
|
+
|
|
+/* overlay flip addr flag */
|
|
+#define OFC_UPDATE 0x1
|
|
+
|
|
+#define OVERLAY_NONPHYSICAL(dev) (IS_G33(dev) || IS_I965G(dev))
|
|
+#define OVERLAY_EXISTS(dev) (!IS_G4X(dev) && !IS_IRONLAKE(dev))
|
|
+
|
|
+
|
|
+static struct overlay_registers *intel_overlay_map_regs_atomic(struct intel_overlay *overlay)
|
|
+{
|
|
+ drm_i915_private_t *dev_priv = overlay->dev->dev_private;
|
|
+ struct overlay_registers *regs;
|
|
+
|
|
+ /* no recursive mappings */
|
|
+ BUG_ON(overlay->virt_addr);
|
|
+
|
|
+ if (OVERLAY_NONPHYSICAL(overlay->dev)) {
|
|
+ regs = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping,
|
|
+ overlay->reg_bo->gtt_offset);
|
|
+
|
|
+ if (!regs) {
|
|
+ DRM_ERROR("failed to map overlay regs in GTT\n");
|
|
+ return NULL;
|
|
+ }
|
|
+ } else
|
|
+ regs = overlay->reg_bo->phys_obj->handle->vaddr;
|
|
+
|
|
+ return overlay->virt_addr = regs;
|
|
+}
|
|
+
|
|
+static void intel_overlay_unmap_regs_atomic(struct intel_overlay *overlay)
|
|
+{
|
|
+ struct drm_device *dev = overlay->dev;
|
|
+ drm_i915_private_t *dev_priv = dev->dev_private;
|
|
+
|
|
+ if (OVERLAY_NONPHYSICAL(overlay->dev))
|
|
+ io_mapping_unmap_atomic(overlay->virt_addr);
|
|
+
|
|
+ overlay->virt_addr = NULL;
|
|
+
|
|
+ I915_READ(OVADD); /* flush wc cashes */
|
|
+
|
|
+ return;
|
|
+}
|
|
+
|
|
+/* overlay needs to be disable in OCMD reg */
|
|
+static int intel_overlay_on(struct intel_overlay *overlay)
|
|
+{
|
|
+ struct drm_device *dev = overlay->dev;
|
|
+ drm_i915_private_t *dev_priv = dev->dev_private;
|
|
+ int ret;
|
|
+ RING_LOCALS;
|
|
+
|
|
+ BUG_ON(overlay->active);
|
|
+
|
|
+ overlay->active = 1;
|
|
+ overlay->hw_wedged = NEEDS_WAIT_FOR_FLIP;
|
|
+
|
|
+ BEGIN_LP_RING(6);
|
|
+ OUT_RING(MI_FLUSH);
|
|
+ OUT_RING(MI_NOOP);
|
|
+ OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_ON);
|
|
+ OUT_RING(overlay->flip_addr | OFC_UPDATE);
|
|
+ OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
|
|
+ OUT_RING(MI_NOOP);
|
|
+ ADVANCE_LP_RING();
|
|
+
|
|
+ overlay->last_flip_req = i915_add_request(dev, NULL, 0);
|
|
+ if (overlay->last_flip_req == 0)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ ret = i915_do_wait_request(dev, overlay->last_flip_req, 1);
|
|
+ if (ret != 0)
|
|
+ return ret;
|
|
+
|
|
+ overlay->hw_wedged = 0;
|
|
+ overlay->last_flip_req = 0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* overlay needs to be enabled in OCMD reg */
|
|
+static void intel_overlay_continue(struct intel_overlay *overlay,
|
|
+ bool load_polyphase_filter)
|
|
+{
|
|
+ struct drm_device *dev = overlay->dev;
|
|
+ drm_i915_private_t *dev_priv = dev->dev_private;
|
|
+ u32 flip_addr = overlay->flip_addr;
|
|
+ u32 tmp;
|
|
+ RING_LOCALS;
|
|
+
|
|
+ BUG_ON(!overlay->active);
|
|
+
|
|
+ if (load_polyphase_filter)
|
|
+ flip_addr |= OFC_UPDATE;
|
|
+
|
|
+ /* check for underruns */
|
|
+ tmp = I915_READ(DOVSTA);
|
|
+ if (tmp & (1 << 17))
|
|
+ DRM_DEBUG("overlay underrun, DOVSTA: %x\n", tmp);
|
|
+
|
|
+ BEGIN_LP_RING(4);
|
|
+ OUT_RING(MI_FLUSH);
|
|
+ OUT_RING(MI_NOOP);
|
|
+ OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE);
|
|
+ OUT_RING(flip_addr);
|
|
+ ADVANCE_LP_RING();
|
|
+
|
|
+ overlay->last_flip_req = i915_add_request(dev, NULL, 0);
|
|
+}
|
|
+
|
|
+static int intel_overlay_wait_flip(struct intel_overlay *overlay)
|
|
+{
|
|
+ struct drm_device *dev = overlay->dev;
|
|
+ drm_i915_private_t *dev_priv = dev->dev_private;
|
|
+ int ret;
|
|
+ u32 tmp;
|
|
+ RING_LOCALS;
|
|
+
|
|
+ if (overlay->last_flip_req != 0) {
|
|
+ ret = i915_do_wait_request(dev, overlay->last_flip_req, 1);
|
|
+ if (ret == 0) {
|
|
+ overlay->last_flip_req = 0;
|
|
+
|
|
+ tmp = I915_READ(ISR);
|
|
+
|
|
+ if (!(tmp & I915_OVERLAY_PLANE_FLIP_PENDING_INTERRUPT))
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* synchronous slowpath */
|
|
+ overlay->hw_wedged = RELEASE_OLD_VID;
|
|
+
|
|
+ BEGIN_LP_RING(2);
|
|
+ OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
|
|
+ OUT_RING(MI_NOOP);
|
|
+ ADVANCE_LP_RING();
|
|
+
|
|
+ overlay->last_flip_req = i915_add_request(dev, NULL, 0);
|
|
+ if (overlay->last_flip_req == 0)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ ret = i915_do_wait_request(dev, overlay->last_flip_req, 1);
|
|
+ if (ret != 0)
|
|
+ return ret;
|
|
+
|
|
+ overlay->hw_wedged = 0;
|
|
+ overlay->last_flip_req = 0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* overlay needs to be disabled in OCMD reg */
|
|
+static int intel_overlay_off(struct intel_overlay *overlay)
|
|
+{
|
|
+ u32 flip_addr = overlay->flip_addr;
|
|
+ struct drm_device *dev = overlay->dev;
|
|
+ drm_i915_private_t *dev_priv = dev->dev_private;
|
|
+ int ret;
|
|
+ RING_LOCALS;
|
|
+
|
|
+ BUG_ON(!overlay->active);
|
|
+
|
|
+ /* According to intel docs the overlay hw may hang (when switching
|
|
+ * off) without loading the filter coeffs. It is however unclear whether
|
|
+ * this applies to the disabling of the overlay or to the switching off
|
|
+ * of the hw. Do it in both cases */
|
|
+ flip_addr |= OFC_UPDATE;
|
|
+
|
|
+ /* wait for overlay to go idle */
|
|
+ overlay->hw_wedged = SWITCH_OFF_STAGE_1;
|
|
+
|
|
+ BEGIN_LP_RING(6);
|
|
+ OUT_RING(MI_FLUSH);
|
|
+ OUT_RING(MI_NOOP);
|
|
+ OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE);
|
|
+ OUT_RING(flip_addr);
|
|
+ OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
|
|
+ OUT_RING(MI_NOOP);
|
|
+ ADVANCE_LP_RING();
|
|
+
|
|
+ overlay->last_flip_req = i915_add_request(dev, NULL, 0);
|
|
+ if (overlay->last_flip_req == 0)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ ret = i915_do_wait_request(dev, overlay->last_flip_req, 1);
|
|
+ if (ret != 0)
|
|
+ return ret;
|
|
+
|
|
+ /* turn overlay off */
|
|
+ overlay->hw_wedged = SWITCH_OFF_STAGE_2;
|
|
+
|
|
+ BEGIN_LP_RING(6);
|
|
+ OUT_RING(MI_FLUSH);
|
|
+ OUT_RING(MI_NOOP);
|
|
+ OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_OFF);
|
|
+ OUT_RING(flip_addr);
|
|
+ OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
|
|
+ OUT_RING(MI_NOOP);
|
|
+ ADVANCE_LP_RING();
|
|
+
|
|
+ overlay->last_flip_req = i915_add_request(dev, NULL, 0);
|
|
+ if (overlay->last_flip_req == 0)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ ret = i915_do_wait_request(dev, overlay->last_flip_req, 1);
|
|
+ if (ret != 0)
|
|
+ return ret;
|
|
+
|
|
+ overlay->hw_wedged = 0;
|
|
+ overlay->last_flip_req = 0;
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void intel_overlay_off_tail(struct intel_overlay *overlay)
|
|
+{
|
|
+ struct drm_gem_object *obj;
|
|
+
|
|
+ /* never have the overlay hw on without showing a frame */
|
|
+ BUG_ON(!overlay->vid_bo);
|
|
+ obj = overlay->vid_bo->obj;
|
|
+
|
|
+ i915_gem_object_unpin(obj);
|
|
+ drm_gem_object_unreference(obj);
|
|
+ overlay->vid_bo = NULL;
|
|
+
|
|
+ overlay->crtc->overlay = NULL;
|
|
+ overlay->crtc = NULL;
|
|
+ overlay->active = 0;
|
|
+}
|
|
+
|
|
+/* recover from an interruption due to a signal
|
|
+ * We have to be careful not to repeat work forever an make forward progess. */
|
|
+int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay,
|
|
+ int interruptible)
|
|
+{
|
|
+ struct drm_device *dev = overlay->dev;
|
|
+ drm_i915_private_t *dev_priv = dev->dev_private;
|
|
+ struct drm_gem_object *obj;
|
|
+ u32 flip_addr;
|
|
+ int ret;
|
|
+ RING_LOCALS;
|
|
+
|
|
+ if (overlay->hw_wedged == HW_WEDGED)
|
|
+ return -EIO;
|
|
+
|
|
+ if (overlay->last_flip_req == 0) {
|
|
+ overlay->last_flip_req = i915_add_request(dev, NULL, 0);
|
|
+ if (overlay->last_flip_req == 0)
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ ret = i915_do_wait_request(dev, overlay->last_flip_req, interruptible);
|
|
+ if (ret != 0)
|
|
+ return ret;
|
|
+
|
|
+ switch (overlay->hw_wedged) {
|
|
+ case RELEASE_OLD_VID:
|
|
+ obj = overlay->old_vid_bo->obj;
|
|
+ i915_gem_object_unpin(obj);
|
|
+ drm_gem_object_unreference(obj);
|
|
+ overlay->old_vid_bo = NULL;
|
|
+ break;
|
|
+ case SWITCH_OFF_STAGE_1:
|
|
+ flip_addr = overlay->flip_addr;
|
|
+ flip_addr |= OFC_UPDATE;
|
|
+
|
|
+ overlay->hw_wedged = SWITCH_OFF_STAGE_2;
|
|
+
|
|
+ BEGIN_LP_RING(6);
|
|
+ OUT_RING(MI_FLUSH);
|
|
+ OUT_RING(MI_NOOP);
|
|
+ OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_OFF);
|
|
+ OUT_RING(flip_addr);
|
|
+ OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
|
|
+ OUT_RING(MI_NOOP);
|
|
+ ADVANCE_LP_RING();
|
|
+
|
|
+ overlay->last_flip_req = i915_add_request(dev, NULL, 0);
|
|
+ if (overlay->last_flip_req == 0)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ ret = i915_do_wait_request(dev, overlay->last_flip_req,
|
|
+ interruptible);
|
|
+ if (ret != 0)
|
|
+ return ret;
|
|
+
|
|
+ case SWITCH_OFF_STAGE_2:
|
|
+ intel_overlay_off_tail(overlay);
|
|
+ break;
|
|
+ default:
|
|
+ BUG_ON(overlay->hw_wedged != NEEDS_WAIT_FOR_FLIP);
|
|
+ }
|
|
+
|
|
+ overlay->hw_wedged = 0;
|
|
+ overlay->last_flip_req = 0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Wait for pending overlay flip and release old frame.
|
|
+ * Needs to be called before the overlay register are changed
|
|
+ * via intel_overlay_(un)map_regs_atomic */
|
|
+static int intel_overlay_release_old_vid(struct intel_overlay *overlay)
|
|
+{
|
|
+ int ret;
|
|
+ struct drm_gem_object *obj;
|
|
+
|
|
+ /* only wait if there is actually an old frame to release to
|
|
+ * guarantee forward progress */
|
|
+ if (!overlay->old_vid_bo)
|
|
+ return 0;
|
|
+
|
|
+ ret = intel_overlay_wait_flip(overlay);
|
|
+ if (ret != 0)
|
|
+ return ret;
|
|
+
|
|
+ obj = overlay->old_vid_bo->obj;
|
|
+ i915_gem_object_unpin(obj);
|
|
+ drm_gem_object_unreference(obj);
|
|
+ overlay->old_vid_bo = NULL;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+struct put_image_params {
|
|
+ int format;
|
|
+ short dst_x;
|
|
+ short dst_y;
|
|
+ short dst_w;
|
|
+ short dst_h;
|
|
+ short src_w;
|
|
+ short src_scan_h;
|
|
+ short src_scan_w;
|
|
+ short src_h;
|
|
+ short stride_Y;
|
|
+ short stride_UV;
|
|
+ int offset_Y;
|
|
+ int offset_U;
|
|
+ int offset_V;
|
|
+};
|
|
+
|
|
+static int packed_depth_bytes(u32 format)
|
|
+{
|
|
+ switch (format & I915_OVERLAY_DEPTH_MASK) {
|
|
+ case I915_OVERLAY_YUV422:
|
|
+ return 4;
|
|
+ case I915_OVERLAY_YUV411:
|
|
+ /* return 6; not implemented */
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int packed_width_bytes(u32 format, short width)
|
|
+{
|
|
+ switch (format & I915_OVERLAY_DEPTH_MASK) {
|
|
+ case I915_OVERLAY_YUV422:
|
|
+ return width << 1;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int uv_hsubsampling(u32 format)
|
|
+{
|
|
+ switch (format & I915_OVERLAY_DEPTH_MASK) {
|
|
+ case I915_OVERLAY_YUV422:
|
|
+ case I915_OVERLAY_YUV420:
|
|
+ return 2;
|
|
+ case I915_OVERLAY_YUV411:
|
|
+ case I915_OVERLAY_YUV410:
|
|
+ return 4;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int uv_vsubsampling(u32 format)
|
|
+{
|
|
+ switch (format & I915_OVERLAY_DEPTH_MASK) {
|
|
+ case I915_OVERLAY_YUV420:
|
|
+ case I915_OVERLAY_YUV410:
|
|
+ return 2;
|
|
+ case I915_OVERLAY_YUV422:
|
|
+ case I915_OVERLAY_YUV411:
|
|
+ return 1;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static u32 calc_swidthsw(struct drm_device *dev, u32 offset, u32 width)
|
|
+{
|
|
+ u32 mask, shift, ret;
|
|
+ if (IS_I9XX(dev)) {
|
|
+ mask = 0x3f;
|
|
+ shift = 6;
|
|
+ } else {
|
|
+ mask = 0x1f;
|
|
+ shift = 5;
|
|
+ }
|
|
+ ret = ((offset + width + mask) >> shift) - (offset >> shift);
|
|
+ if (IS_I9XX(dev))
|
|
+ ret <<= 1;
|
|
+ ret -=1;
|
|
+ return ret << 2;
|
|
+}
|
|
+
|
|
+static const u16 y_static_hcoeffs[N_HORIZ_Y_TAPS * N_PHASES] = {
|
|
+ 0x3000, 0xb4a0, 0x1930, 0x1920, 0xb4a0,
|
|
+ 0x3000, 0xb500, 0x19d0, 0x1880, 0xb440,
|
|
+ 0x3000, 0xb540, 0x1a88, 0x2f80, 0xb3e0,
|
|
+ 0x3000, 0xb580, 0x1b30, 0x2e20, 0xb380,
|
|
+ 0x3000, 0xb5c0, 0x1bd8, 0x2cc0, 0xb320,
|
|
+ 0x3020, 0xb5e0, 0x1c60, 0x2b80, 0xb2c0,
|
|
+ 0x3020, 0xb5e0, 0x1cf8, 0x2a20, 0xb260,
|
|
+ 0x3020, 0xb5e0, 0x1d80, 0x28e0, 0xb200,
|
|
+ 0x3020, 0xb5c0, 0x1e08, 0x3f40, 0xb1c0,
|
|
+ 0x3020, 0xb580, 0x1e78, 0x3ce0, 0xb160,
|
|
+ 0x3040, 0xb520, 0x1ed8, 0x3aa0, 0xb120,
|
|
+ 0x3040, 0xb4a0, 0x1f30, 0x3880, 0xb0e0,
|
|
+ 0x3040, 0xb400, 0x1f78, 0x3680, 0xb0a0,
|
|
+ 0x3020, 0xb340, 0x1fb8, 0x34a0, 0xb060,
|
|
+ 0x3020, 0xb240, 0x1fe0, 0x32e0, 0xb040,
|
|
+ 0x3020, 0xb140, 0x1ff8, 0x3160, 0xb020,
|
|
+ 0xb000, 0x3000, 0x0800, 0x3000, 0xb000};
|
|
+static const u16 uv_static_hcoeffs[N_HORIZ_UV_TAPS * N_PHASES] = {
|
|
+ 0x3000, 0x1800, 0x1800, 0xb000, 0x18d0, 0x2e60,
|
|
+ 0xb000, 0x1990, 0x2ce0, 0xb020, 0x1a68, 0x2b40,
|
|
+ 0xb040, 0x1b20, 0x29e0, 0xb060, 0x1bd8, 0x2880,
|
|
+ 0xb080, 0x1c88, 0x3e60, 0xb0a0, 0x1d28, 0x3c00,
|
|
+ 0xb0c0, 0x1db8, 0x39e0, 0xb0e0, 0x1e40, 0x37e0,
|
|
+ 0xb100, 0x1eb8, 0x3620, 0xb100, 0x1f18, 0x34a0,
|
|
+ 0xb100, 0x1f68, 0x3360, 0xb0e0, 0x1fa8, 0x3240,
|
|
+ 0xb0c0, 0x1fe0, 0x3140, 0xb060, 0x1ff0, 0x30a0,
|
|
+ 0x3000, 0x0800, 0x3000};
|
|
+
|
|
+static void update_polyphase_filter(struct overlay_registers *regs)
|
|
+{
|
|
+ memcpy(regs->Y_HCOEFS, y_static_hcoeffs, sizeof(y_static_hcoeffs));
|
|
+ memcpy(regs->UV_HCOEFS, uv_static_hcoeffs, sizeof(uv_static_hcoeffs));
|
|
+}
|
|
+
|
|
+static bool update_scaling_factors(struct intel_overlay *overlay,
|
|
+ struct overlay_registers *regs,
|
|
+ struct put_image_params *params)
|
|
+{
|
|
+ /* fixed point with a 12 bit shift */
|
|
+ u32 xscale, yscale, xscale_UV, yscale_UV;
|
|
+#define FP_SHIFT 12
|
|
+#define FRACT_MASK 0xfff
|
|
+ bool scale_changed = false;
|
|
+ int uv_hscale = uv_hsubsampling(params->format);
|
|
+ int uv_vscale = uv_vsubsampling(params->format);
|
|
+
|
|
+ if (params->dst_w > 1)
|
|
+ xscale = ((params->src_scan_w - 1) << FP_SHIFT)
|
|
+ /(params->dst_w);
|
|
+ else
|
|
+ xscale = 1 << FP_SHIFT;
|
|
+
|
|
+ if (params->dst_h > 1)
|
|
+ yscale = ((params->src_scan_h - 1) << FP_SHIFT)
|
|
+ /(params->dst_h);
|
|
+ else
|
|
+ yscale = 1 << FP_SHIFT;
|
|
+
|
|
+ /*if (params->format & I915_OVERLAY_YUV_PLANAR) {*/
|
|
+ xscale_UV = xscale/uv_hscale;
|
|
+ yscale_UV = yscale/uv_vscale;
|
|
+ /* make the Y scale to UV scale ratio an exact multiply */
|
|
+ xscale = xscale_UV * uv_hscale;
|
|
+ yscale = yscale_UV * uv_vscale;
|
|
+ /*} else {
|
|
+ xscale_UV = 0;
|
|
+ yscale_UV = 0;
|
|
+ }*/
|
|
+
|
|
+ if (xscale != overlay->old_xscale || yscale != overlay->old_yscale)
|
|
+ scale_changed = true;
|
|
+ overlay->old_xscale = xscale;
|
|
+ overlay->old_yscale = yscale;
|
|
+
|
|
+ regs->YRGBSCALE = ((yscale & FRACT_MASK) << 20)
|
|
+ | ((xscale >> FP_SHIFT) << 16)
|
|
+ | ((xscale & FRACT_MASK) << 3);
|
|
+ regs->UVSCALE = ((yscale_UV & FRACT_MASK) << 20)
|
|
+ | ((xscale_UV >> FP_SHIFT) << 16)
|
|
+ | ((xscale_UV & FRACT_MASK) << 3);
|
|
+ regs->UVSCALEV = ((yscale >> FP_SHIFT) << 16)
|
|
+ | ((yscale_UV >> FP_SHIFT) << 0);
|
|
+
|
|
+ if (scale_changed)
|
|
+ update_polyphase_filter(regs);
|
|
+
|
|
+ return scale_changed;
|
|
+}
|
|
+
|
|
+static void update_colorkey(struct intel_overlay *overlay,
|
|
+ struct overlay_registers *regs)
|
|
+{
|
|
+ u32 key = overlay->color_key;
|
|
+ switch (overlay->crtc->base.fb->bits_per_pixel) {
|
|
+ case 8:
|
|
+ regs->DCLRKV = 0;
|
|
+ regs->DCLRKM = CLK_RGB8I_MASK | DST_KEY_ENABLE;
|
|
+ case 16:
|
|
+ if (overlay->crtc->base.fb->depth == 15) {
|
|
+ regs->DCLRKV = RGB15_TO_COLORKEY(key);
|
|
+ regs->DCLRKM = CLK_RGB15_MASK | DST_KEY_ENABLE;
|
|
+ } else {
|
|
+ regs->DCLRKV = RGB16_TO_COLORKEY(key);
|
|
+ regs->DCLRKM = CLK_RGB16_MASK | DST_KEY_ENABLE;
|
|
+ }
|
|
+ case 24:
|
|
+ case 32:
|
|
+ regs->DCLRKV = key;
|
|
+ regs->DCLRKM = CLK_RGB24_MASK | DST_KEY_ENABLE;
|
|
+ }
|
|
+}
|
|
+
|
|
+static u32 overlay_cmd_reg(struct put_image_params *params)
|
|
+{
|
|
+ u32 cmd = OCMD_ENABLE | OCMD_BUF_TYPE_FRAME | OCMD_BUFFER0;
|
|
+
|
|
+ if (params->format & I915_OVERLAY_YUV_PLANAR) {
|
|
+ switch (params->format & I915_OVERLAY_DEPTH_MASK) {
|
|
+ case I915_OVERLAY_YUV422:
|
|
+ cmd |= OCMD_YUV_422_PLANAR;
|
|
+ break;
|
|
+ case I915_OVERLAY_YUV420:
|
|
+ cmd |= OCMD_YUV_420_PLANAR;
|
|
+ break;
|
|
+ case I915_OVERLAY_YUV411:
|
|
+ case I915_OVERLAY_YUV410:
|
|
+ cmd |= OCMD_YUV_410_PLANAR;
|
|
+ break;
|
|
+ }
|
|
+ } else { /* YUV packed */
|
|
+ switch (params->format & I915_OVERLAY_DEPTH_MASK) {
|
|
+ case I915_OVERLAY_YUV422:
|
|
+ cmd |= OCMD_YUV_422_PACKED;
|
|
+ break;
|
|
+ case I915_OVERLAY_YUV411:
|
|
+ cmd |= OCMD_YUV_411_PACKED;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ switch (params->format & I915_OVERLAY_SWAP_MASK) {
|
|
+ case I915_OVERLAY_NO_SWAP:
|
|
+ break;
|
|
+ case I915_OVERLAY_UV_SWAP:
|
|
+ cmd |= OCMD_UV_SWAP;
|
|
+ break;
|
|
+ case I915_OVERLAY_Y_SWAP:
|
|
+ cmd |= OCMD_Y_SWAP;
|
|
+ break;
|
|
+ case I915_OVERLAY_Y_AND_UV_SWAP:
|
|
+ cmd |= OCMD_Y_AND_UV_SWAP;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return cmd;
|
|
+}
|
|
+
|
|
+int intel_overlay_do_put_image(struct intel_overlay *overlay,
|
|
+ struct drm_gem_object *new_bo,
|
|
+ struct put_image_params *params)
|
|
+{
|
|
+ int ret, tmp_width;
|
|
+ struct overlay_registers *regs;
|
|
+ bool scale_changed = false;
|
|
+ struct drm_i915_gem_object *bo_priv = new_bo->driver_private;
|
|
+ struct drm_device *dev = overlay->dev;
|
|
+
|
|
+ BUG_ON(!mutex_is_locked(&dev->struct_mutex));
|
|
+ BUG_ON(!mutex_is_locked(&dev->mode_config.mutex));
|
|
+ BUG_ON(!overlay);
|
|
+
|
|
+ ret = intel_overlay_release_old_vid(overlay);
|
|
+ if (ret != 0)
|
|
+ return ret;
|
|
+
|
|
+ ret = i915_gem_object_pin(new_bo, PAGE_SIZE);
|
|
+ if (ret != 0)
|
|
+ return ret;
|
|
+
|
|
+ ret = i915_gem_object_set_to_gtt_domain(new_bo, 0);
|
|
+ if (ret != 0)
|
|
+ goto out_unpin;
|
|
+
|
|
+ if (!overlay->active) {
|
|
+ regs = intel_overlay_map_regs_atomic(overlay);
|
|
+ if (!regs) {
|
|
+ ret = -ENOMEM;
|
|
+ goto out_unpin;
|
|
+ }
|
|
+ regs->OCONFIG = OCONF_CC_OUT_8BIT;
|
|
+ if (IS_I965GM(overlay->dev))
|
|
+ regs->OCONFIG |= OCONF_CSC_MODE_BT709;
|
|
+ regs->OCONFIG |= overlay->crtc->pipe == 0 ?
|
|
+ OCONF_PIPE_A : OCONF_PIPE_B;
|
|
+ intel_overlay_unmap_regs_atomic(overlay);
|
|
+
|
|
+ ret = intel_overlay_on(overlay);
|
|
+ if (ret != 0)
|
|
+ goto out_unpin;
|
|
+ }
|
|
+
|
|
+ regs = intel_overlay_map_regs_atomic(overlay);
|
|
+ if (!regs) {
|
|
+ ret = -ENOMEM;
|
|
+ goto out_unpin;
|
|
+ }
|
|
+
|
|
+ regs->DWINPOS = (params->dst_y << 16) | params->dst_x;
|
|
+ regs->DWINSZ = (params->dst_h << 16) | params->dst_w;
|
|
+
|
|
+ if (params->format & I915_OVERLAY_YUV_PACKED)
|
|
+ tmp_width = packed_width_bytes(params->format, params->src_w);
|
|
+ else
|
|
+ tmp_width = params->src_w;
|
|
+
|
|
+ regs->SWIDTH = params->src_w;
|
|
+ regs->SWIDTHSW = calc_swidthsw(overlay->dev,
|
|
+ params->offset_Y, tmp_width);
|
|
+ regs->SHEIGHT = params->src_h;
|
|
+ regs->OBUF_0Y = bo_priv->gtt_offset + params-> offset_Y;
|
|
+ regs->OSTRIDE = params->stride_Y;
|
|
+
|
|
+ if (params->format & I915_OVERLAY_YUV_PLANAR) {
|
|
+ int uv_hscale = uv_hsubsampling(params->format);
|
|
+ int uv_vscale = uv_vsubsampling(params->format);
|
|
+ u32 tmp_U, tmp_V;
|
|
+ regs->SWIDTH |= (params->src_w/uv_hscale) << 16;
|
|
+ tmp_U = calc_swidthsw(overlay->dev, params->offset_U,
|
|
+ params->src_w/uv_hscale);
|
|
+ tmp_V = calc_swidthsw(overlay->dev, params->offset_V,
|
|
+ params->src_w/uv_hscale);
|
|
+ regs->SWIDTHSW |= max_t(u32, tmp_U, tmp_V) << 16;
|
|
+ regs->SHEIGHT |= (params->src_h/uv_vscale) << 16;
|
|
+ regs->OBUF_0U = bo_priv->gtt_offset + params->offset_U;
|
|
+ regs->OBUF_0V = bo_priv->gtt_offset + params->offset_V;
|
|
+ regs->OSTRIDE |= params->stride_UV << 16;
|
|
+ }
|
|
+
|
|
+ scale_changed = update_scaling_factors(overlay, regs, params);
|
|
+
|
|
+ update_colorkey(overlay, regs);
|
|
+
|
|
+ regs->OCMD = overlay_cmd_reg(params);
|
|
+
|
|
+ intel_overlay_unmap_regs_atomic(overlay);
|
|
+
|
|
+ intel_overlay_continue(overlay, scale_changed);
|
|
+
|
|
+ overlay->old_vid_bo = overlay->vid_bo;
|
|
+ overlay->vid_bo = new_bo->driver_private;
|
|
+
|
|
+ return 0;
|
|
+
|
|
+out_unpin:
|
|
+ i915_gem_object_unpin(new_bo);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int intel_overlay_switch_off(struct intel_overlay *overlay)
|
|
+{
|
|
+ int ret;
|
|
+ struct overlay_registers *regs;
|
|
+ struct drm_device *dev = overlay->dev;
|
|
+
|
|
+ BUG_ON(!mutex_is_locked(&dev->struct_mutex));
|
|
+ BUG_ON(!mutex_is_locked(&dev->mode_config.mutex));
|
|
+
|
|
+ if (overlay->hw_wedged) {
|
|
+ ret = intel_overlay_recover_from_interrupt(overlay, 1);
|
|
+ if (ret != 0)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ if (!overlay->active)
|
|
+ return 0;
|
|
+
|
|
+ ret = intel_overlay_release_old_vid(overlay);
|
|
+ if (ret != 0)
|
|
+ return ret;
|
|
+
|
|
+ regs = intel_overlay_map_regs_atomic(overlay);
|
|
+ regs->OCMD = 0;
|
|
+ intel_overlay_unmap_regs_atomic(overlay);
|
|
+
|
|
+ ret = intel_overlay_off(overlay);
|
|
+ if (ret != 0)
|
|
+ return ret;
|
|
+
|
|
+ intel_overlay_off_tail(overlay);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int check_overlay_possible_on_crtc(struct intel_overlay *overlay,
|
|
+ struct intel_crtc *crtc)
|
|
+{
|
|
+ drm_i915_private_t *dev_priv = overlay->dev->dev_private;
|
|
+ u32 pipeconf;
|
|
+ int pipeconf_reg = (crtc->pipe == 0) ? PIPEACONF : PIPEBCONF;
|
|
+
|
|
+ if (!crtc->base.enabled || crtc->dpms_mode != DRM_MODE_DPMS_ON)
|
|
+ return -EINVAL;
|
|
+
|
|
+ pipeconf = I915_READ(pipeconf_reg);
|
|
+
|
|
+ /* can't use the overlay with double wide pipe */
|
|
+ if (!IS_I965G(overlay->dev) && pipeconf & PIPEACONF_DOUBLE_WIDE)
|
|
+ return -EINVAL;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void update_pfit_vscale_ratio(struct intel_overlay *overlay)
|
|
+{
|
|
+ struct drm_device *dev = overlay->dev;
|
|
+ drm_i915_private_t *dev_priv = dev->dev_private;
|
|
+ u32 ratio;
|
|
+ u32 pfit_control = I915_READ(PFIT_CONTROL);
|
|
+
|
|
+ /* XXX: This is not the same logic as in the xorg driver, but more in
|
|
+ * line with the intel documentation for the i965 */
|
|
+ if (!IS_I965G(dev) && (pfit_control & VERT_AUTO_SCALE)) {
|
|
+ ratio = I915_READ(PFIT_AUTO_RATIOS) >> PFIT_VERT_SCALE_SHIFT;
|
|
+ } else { /* on i965 use the PGM reg to read out the autoscaler values */
|
|
+ ratio = I915_READ(PFIT_PGM_RATIOS);
|
|
+ if (IS_I965G(dev))
|
|
+ ratio >>= PFIT_VERT_SCALE_SHIFT_965;
|
|
+ else
|
|
+ ratio >>= PFIT_VERT_SCALE_SHIFT;
|
|
+ }
|
|
+
|
|
+ overlay->pfit_vscale_ratio = ratio;
|
|
+}
|
|
+
|
|
+static int check_overlay_dst(struct intel_overlay *overlay,
|
|
+ struct drm_intel_overlay_put_image *rec)
|
|
+{
|
|
+ struct drm_display_mode *mode = &overlay->crtc->base.mode;
|
|
+
|
|
+ if ((rec->dst_x < mode->crtc_hdisplay)
|
|
+ && (rec->dst_x + rec->dst_width
|
|
+ <= mode->crtc_hdisplay)
|
|
+ && (rec->dst_y < mode->crtc_vdisplay)
|
|
+ && (rec->dst_y + rec->dst_height
|
|
+ <= mode->crtc_vdisplay))
|
|
+ return 0;
|
|
+ else
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static int check_overlay_scaling(struct put_image_params *rec)
|
|
+{
|
|
+ u32 tmp;
|
|
+
|
|
+ /* downscaling limit is 8.0 */
|
|
+ tmp = ((rec->src_scan_h << 16) / rec->dst_h) >> 16;
|
|
+ if (tmp > 7)
|
|
+ return -EINVAL;
|
|
+ tmp = ((rec->src_scan_w << 16) / rec->dst_w) >> 16;
|
|
+ if (tmp > 7)
|
|
+ return -EINVAL;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int check_overlay_src(struct drm_device *dev,
|
|
+ struct drm_intel_overlay_put_image *rec,
|
|
+ struct drm_gem_object *new_bo)
|
|
+{
|
|
+ u32 stride_mask;
|
|
+ int depth;
|
|
+ int uv_hscale = uv_hsubsampling(rec->flags);
|
|
+ int uv_vscale = uv_vsubsampling(rec->flags);
|
|
+ size_t tmp;
|
|
+
|
|
+ /* check src dimensions */
|
|
+ if (IS_845G(dev) || IS_I830(dev)) {
|
|
+ if (rec->src_height > IMAGE_MAX_HEIGHT_LEGACY
|
|
+ || rec->src_width > IMAGE_MAX_WIDTH_LEGACY)
|
|
+ return -EINVAL;
|
|
+ } else {
|
|
+ if (rec->src_height > IMAGE_MAX_HEIGHT
|
|
+ || rec->src_width > IMAGE_MAX_WIDTH)
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ /* better safe than sorry, use 4 as the maximal subsampling ratio */
|
|
+ if (rec->src_height < N_VERT_Y_TAPS*4
|
|
+ || rec->src_width < N_HORIZ_Y_TAPS*4)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* check alingment constrains */
|
|
+ switch (rec->flags & I915_OVERLAY_TYPE_MASK) {
|
|
+ case I915_OVERLAY_RGB:
|
|
+ /* not implemented */
|
|
+ return -EINVAL;
|
|
+ case I915_OVERLAY_YUV_PACKED:
|
|
+ depth = packed_depth_bytes(rec->flags);
|
|
+ if (uv_vscale != 1)
|
|
+ return -EINVAL;
|
|
+ if (depth < 0)
|
|
+ return depth;
|
|
+ /* ignore UV planes */
|
|
+ rec->stride_UV = 0;
|
|
+ rec->offset_U = 0;
|
|
+ rec->offset_V = 0;
|
|
+ /* check pixel alignment */
|
|
+ if (rec->offset_Y % depth)
|
|
+ return -EINVAL;
|
|
+ break;
|
|
+ case I915_OVERLAY_YUV_PLANAR:
|
|
+ if (uv_vscale < 0 || uv_hscale < 0)
|
|
+ return -EINVAL;
|
|
+ /* no offset restrictions for planar formats */
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (rec->src_width % uv_hscale)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* stride checking */
|
|
+ stride_mask = 63;
|
|
+
|
|
+ if (rec->stride_Y & stride_mask || rec->stride_UV & stride_mask)
|
|
+ return -EINVAL;
|
|
+ if (IS_I965G(dev) && rec->stride_Y < 512)
|
|
+ return -EINVAL;
|
|
+
|
|
+ tmp = (rec->flags & I915_OVERLAY_TYPE_MASK) == I915_OVERLAY_YUV_PLANAR ?
|
|
+ 4 : 8;
|
|
+ if (rec->stride_Y > tmp*1024 || rec->stride_UV > 2*1024)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* check buffer dimensions */
|
|
+ switch (rec->flags & I915_OVERLAY_TYPE_MASK) {
|
|
+ case I915_OVERLAY_RGB:
|
|
+ case I915_OVERLAY_YUV_PACKED:
|
|
+ /* always 4 Y values per depth pixels */
|
|
+ if (packed_width_bytes(rec->flags, rec->src_width)
|
|
+ > rec->stride_Y)
|
|
+ return -EINVAL;
|
|
+
|
|
+ tmp = rec->stride_Y*rec->src_height;
|
|
+ if (rec->offset_Y + tmp > new_bo->size)
|
|
+ return -EINVAL;
|
|
+ break;
|
|
+ case I915_OVERLAY_YUV_PLANAR:
|
|
+ if (rec->src_width > rec->stride_Y)
|
|
+ return -EINVAL;
|
|
+ if (rec->src_width/uv_hscale > rec->stride_UV)
|
|
+ return -EINVAL;
|
|
+
|
|
+ tmp = rec->stride_Y*rec->src_height;
|
|
+ if (rec->offset_Y + tmp > new_bo->size)
|
|
+ return -EINVAL;
|
|
+ tmp = rec->stride_UV*rec->src_height;
|
|
+ tmp /= uv_vscale;
|
|
+ if (rec->offset_U + tmp > new_bo->size
|
|
+ || rec->offset_V + tmp > new_bo->size)
|
|
+ return -EINVAL;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int intel_overlay_put_image(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_intel_overlay_put_image *put_image_rec = data;
|
|
+ drm_i915_private_t *dev_priv = dev->dev_private;
|
|
+ struct intel_overlay *overlay;
|
|
+ struct drm_mode_object *drmmode_obj;
|
|
+ struct intel_crtc *crtc;
|
|
+ struct drm_gem_object *new_bo;
|
|
+ struct put_image_params *params;
|
|
+ int ret;
|
|
+
|
|
+ if (!dev_priv) {
|
|
+ DRM_ERROR("called with no initialization\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ overlay = dev_priv->overlay;
|
|
+ if (!overlay) {
|
|
+ DRM_DEBUG("userspace bug: no overlay\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ if (!(put_image_rec->flags & I915_OVERLAY_ENABLE)) {
|
|
+ mutex_lock(&dev->mode_config.mutex);
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
+
|
|
+ ret = intel_overlay_switch_off(overlay);
|
|
+
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+ mutex_unlock(&dev->mode_config.mutex);
|
|
+
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ params = kmalloc(sizeof(struct put_image_params), GFP_KERNEL);
|
|
+ if (!params)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ drmmode_obj = drm_mode_object_find(dev, put_image_rec->crtc_id,
|
|
+ DRM_MODE_OBJECT_CRTC);
|
|
+ if (!drmmode_obj)
|
|
+ return -ENOENT;
|
|
+ crtc = to_intel_crtc(obj_to_crtc(drmmode_obj));
|
|
+
|
|
+ new_bo = drm_gem_object_lookup(dev, file_priv,
|
|
+ put_image_rec->bo_handle);
|
|
+ if (!new_bo)
|
|
+ return -ENOENT;
|
|
+
|
|
+ mutex_lock(&dev->mode_config.mutex);
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
+
|
|
+ if (overlay->hw_wedged) {
|
|
+ ret = intel_overlay_recover_from_interrupt(overlay, 1);
|
|
+ if (ret != 0)
|
|
+ goto out_unlock;
|
|
+ }
|
|
+
|
|
+ if (overlay->crtc != crtc) {
|
|
+ struct drm_display_mode *mode = &crtc->base.mode;
|
|
+ ret = intel_overlay_switch_off(overlay);
|
|
+ if (ret != 0)
|
|
+ goto out_unlock;
|
|
+
|
|
+ ret = check_overlay_possible_on_crtc(overlay, crtc);
|
|
+ if (ret != 0)
|
|
+ goto out_unlock;
|
|
+
|
|
+ overlay->crtc = crtc;
|
|
+ crtc->overlay = overlay;
|
|
+
|
|
+ if (intel_panel_fitter_pipe(dev) == crtc->pipe
|
|
+ /* and line to wide, i.e. one-line-mode */
|
|
+ && mode->hdisplay > 1024) {
|
|
+ overlay->pfit_active = 1;
|
|
+ update_pfit_vscale_ratio(overlay);
|
|
+ } else
|
|
+ overlay->pfit_active = 0;
|
|
+ }
|
|
+
|
|
+ ret = check_overlay_dst(overlay, put_image_rec);
|
|
+ if (ret != 0)
|
|
+ goto out_unlock;
|
|
+
|
|
+ if (overlay->pfit_active) {
|
|
+ params->dst_y = ((((u32)put_image_rec->dst_y) << 12) /
|
|
+ overlay->pfit_vscale_ratio);
|
|
+ /* shifting right rounds downwards, so add 1 */
|
|
+ params->dst_h = ((((u32)put_image_rec->dst_height) << 12) /
|
|
+ overlay->pfit_vscale_ratio) + 1;
|
|
+ } else {
|
|
+ params->dst_y = put_image_rec->dst_y;
|
|
+ params->dst_h = put_image_rec->dst_height;
|
|
+ }
|
|
+ params->dst_x = put_image_rec->dst_x;
|
|
+ params->dst_w = put_image_rec->dst_width;
|
|
+
|
|
+ params->src_w = put_image_rec->src_width;
|
|
+ params->src_h = put_image_rec->src_height;
|
|
+ params->src_scan_w = put_image_rec->src_scan_width;
|
|
+ params->src_scan_h = put_image_rec->src_scan_height;
|
|
+ if (params->src_scan_h > params->src_h
|
|
+ || params->src_scan_w > params->src_w) {
|
|
+ ret = -EINVAL;
|
|
+ goto out_unlock;
|
|
+ }
|
|
+
|
|
+ ret = check_overlay_src(dev, put_image_rec, new_bo);
|
|
+ if (ret != 0)
|
|
+ goto out_unlock;
|
|
+ params->format = put_image_rec->flags & ~I915_OVERLAY_FLAGS_MASK;
|
|
+ params->stride_Y = put_image_rec->stride_Y;
|
|
+ params->stride_UV = put_image_rec->stride_UV;
|
|
+ params->offset_Y = put_image_rec->offset_Y;
|
|
+ params->offset_U = put_image_rec->offset_U;
|
|
+ params->offset_V = put_image_rec->offset_V;
|
|
+
|
|
+ /* Check scaling after src size to prevent a divide-by-zero. */
|
|
+ ret = check_overlay_scaling(params);
|
|
+ if (ret != 0)
|
|
+ goto out_unlock;
|
|
+
|
|
+ ret = intel_overlay_do_put_image(overlay, new_bo, params);
|
|
+ if (ret != 0)
|
|
+ goto out_unlock;
|
|
+
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+ mutex_unlock(&dev->mode_config.mutex);
|
|
+
|
|
+ kfree(params);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+out_unlock:
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+ mutex_unlock(&dev->mode_config.mutex);
|
|
+ drm_gem_object_unreference(new_bo);
|
|
+ kfree(params);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void update_reg_attrs(struct intel_overlay *overlay,
|
|
+ struct overlay_registers *regs)
|
|
+{
|
|
+ regs->OCLRC0 = (overlay->contrast << 18) | (overlay->brightness & 0xff);
|
|
+ regs->OCLRC1 = overlay->saturation;
|
|
+}
|
|
+
|
|
+static bool check_gamma_bounds(u32 gamma1, u32 gamma2)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ if (gamma1 & 0xff000000 || gamma2 & 0xff000000)
|
|
+ return false;
|
|
+
|
|
+ for (i = 0; i < 3; i++) {
|
|
+ if (((gamma1 >> i * 8) & 0xff) >= ((gamma2 >> i*8) & 0xff))
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static bool check_gamma5_errata(u32 gamma5)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < 3; i++) {
|
|
+ if (((gamma5 >> i*8) & 0xff) == 0x80)
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static int check_gamma(struct drm_intel_overlay_attrs *attrs)
|
|
+{
|
|
+ if (!check_gamma_bounds(0, attrs->gamma0)
|
|
+ || !check_gamma_bounds(attrs->gamma0, attrs->gamma1)
|
|
+ || !check_gamma_bounds(attrs->gamma1, attrs->gamma2)
|
|
+ || !check_gamma_bounds(attrs->gamma2, attrs->gamma3)
|
|
+ || !check_gamma_bounds(attrs->gamma3, attrs->gamma4)
|
|
+ || !check_gamma_bounds(attrs->gamma4, attrs->gamma5)
|
|
+ || !check_gamma_bounds(attrs->gamma5, 0x00ffffff))
|
|
+ return -EINVAL;
|
|
+ if (!check_gamma5_errata(attrs->gamma5))
|
|
+ return -EINVAL;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int intel_overlay_attrs(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_intel_overlay_attrs *attrs = data;
|
|
+ drm_i915_private_t *dev_priv = dev->dev_private;
|
|
+ struct intel_overlay *overlay;
|
|
+ struct overlay_registers *regs;
|
|
+ int ret;
|
|
+
|
|
+ if (!dev_priv) {
|
|
+ DRM_ERROR("called with no initialization\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ overlay = dev_priv->overlay;
|
|
+ if (!overlay) {
|
|
+ DRM_DEBUG("userspace bug: no overlay\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ mutex_lock(&dev->mode_config.mutex);
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
+
|
|
+ if (!(attrs->flags & I915_OVERLAY_UPDATE_ATTRS)) {
|
|
+ attrs->color_key = overlay->color_key;
|
|
+ attrs->brightness = overlay->brightness;
|
|
+ attrs->contrast = overlay->contrast;
|
|
+ attrs->saturation = overlay->saturation;
|
|
+
|
|
+ if (IS_I9XX(dev)) {
|
|
+ attrs->gamma0 = I915_READ(OGAMC0);
|
|
+ attrs->gamma1 = I915_READ(OGAMC1);
|
|
+ attrs->gamma2 = I915_READ(OGAMC2);
|
|
+ attrs->gamma3 = I915_READ(OGAMC3);
|
|
+ attrs->gamma4 = I915_READ(OGAMC4);
|
|
+ attrs->gamma5 = I915_READ(OGAMC5);
|
|
+ }
|
|
+ ret = 0;
|
|
+ } else {
|
|
+ overlay->color_key = attrs->color_key;
|
|
+ if (attrs->brightness >= -128 && attrs->brightness <= 127) {
|
|
+ overlay->brightness = attrs->brightness;
|
|
+ } else {
|
|
+ ret = -EINVAL;
|
|
+ goto out_unlock;
|
|
+ }
|
|
+ if (attrs->contrast <= 255) {
|
|
+ overlay->contrast = attrs->contrast;
|
|
+ } else {
|
|
+ ret = -EINVAL;
|
|
+ goto out_unlock;
|
|
+ }
|
|
+ if (attrs->saturation <= 1023) {
|
|
+ overlay->saturation = attrs->saturation;
|
|
+ } else {
|
|
+ ret = -EINVAL;
|
|
+ goto out_unlock;
|
|
+ }
|
|
+
|
|
+ regs = intel_overlay_map_regs_atomic(overlay);
|
|
+ if (!regs) {
|
|
+ ret = -ENOMEM;
|
|
+ goto out_unlock;
|
|
+ }
|
|
+
|
|
+ update_reg_attrs(overlay, regs);
|
|
+
|
|
+ intel_overlay_unmap_regs_atomic(overlay);
|
|
+
|
|
+ if (attrs->flags & I915_OVERLAY_UPDATE_GAMMA) {
|
|
+ if (!IS_I9XX(dev)) {
|
|
+ ret = -EINVAL;
|
|
+ goto out_unlock;
|
|
+ }
|
|
+
|
|
+ if (overlay->active) {
|
|
+ ret = -EBUSY;
|
|
+ goto out_unlock;
|
|
+ }
|
|
+
|
|
+ ret = check_gamma(attrs);
|
|
+ if (ret != 0)
|
|
+ goto out_unlock;
|
|
+
|
|
+ I915_WRITE(OGAMC0, attrs->gamma0);
|
|
+ I915_WRITE(OGAMC1, attrs->gamma1);
|
|
+ I915_WRITE(OGAMC2, attrs->gamma2);
|
|
+ I915_WRITE(OGAMC3, attrs->gamma3);
|
|
+ I915_WRITE(OGAMC4, attrs->gamma4);
|
|
+ I915_WRITE(OGAMC5, attrs->gamma5);
|
|
+ }
|
|
+ ret = 0;
|
|
+ }
|
|
+
|
|
+out_unlock:
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+ mutex_unlock(&dev->mode_config.mutex);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void intel_setup_overlay(struct drm_device *dev)
|
|
+{
|
|
+ drm_i915_private_t *dev_priv = dev->dev_private;
|
|
+ struct intel_overlay *overlay;
|
|
+ struct drm_gem_object *reg_bo;
|
|
+ struct overlay_registers *regs;
|
|
+ int ret;
|
|
+
|
|
+ if (!OVERLAY_EXISTS(dev))
|
|
+ return;
|
|
+
|
|
+ overlay = kzalloc(sizeof(struct intel_overlay), GFP_KERNEL);
|
|
+ if (!overlay)
|
|
+ return;
|
|
+ overlay->dev = dev;
|
|
+
|
|
+ reg_bo = drm_gem_object_alloc(dev, PAGE_SIZE);
|
|
+ if (!reg_bo)
|
|
+ goto out_free;
|
|
+ overlay->reg_bo = reg_bo->driver_private;
|
|
+
|
|
+ if (OVERLAY_NONPHYSICAL(dev)) {
|
|
+ ret = i915_gem_object_pin(reg_bo, PAGE_SIZE);
|
|
+ if (ret) {
|
|
+ DRM_ERROR("failed to pin overlay register bo\n");
|
|
+ goto out_free_bo;
|
|
+ }
|
|
+ overlay->flip_addr = overlay->reg_bo->gtt_offset;
|
|
+ } else {
|
|
+ ret = i915_gem_attach_phys_object(dev, reg_bo,
|
|
+ I915_GEM_PHYS_OVERLAY_REGS);
|
|
+ if (ret) {
|
|
+ DRM_ERROR("failed to attach phys overlay regs\n");
|
|
+ goto out_free_bo;
|
|
+ }
|
|
+ overlay->flip_addr = overlay->reg_bo->phys_obj->handle->busaddr;
|
|
+ }
|
|
+
|
|
+ /* init all values */
|
|
+ overlay->color_key = 0x0101fe;
|
|
+ overlay->brightness = -19;
|
|
+ overlay->contrast = 75;
|
|
+ overlay->saturation = 146;
|
|
+
|
|
+ regs = intel_overlay_map_regs_atomic(overlay);
|
|
+ if (!regs)
|
|
+ goto out_free_bo;
|
|
+
|
|
+ memset(regs, 0, sizeof(struct overlay_registers));
|
|
+ update_polyphase_filter(regs);
|
|
+
|
|
+ update_reg_attrs(overlay, regs);
|
|
+
|
|
+ intel_overlay_unmap_regs_atomic(overlay);
|
|
+
|
|
+ dev_priv->overlay = overlay;
|
|
+ DRM_INFO("initialized overlay support\n");
|
|
+ return;
|
|
+
|
|
+out_free_bo:
|
|
+ drm_gem_object_unreference(reg_bo);
|
|
+out_free:
|
|
+ kfree(overlay);
|
|
+ return;
|
|
+}
|
|
+
|
|
+void intel_cleanup_overlay(struct drm_device *dev)
|
|
+{
|
|
+ drm_i915_private_t *dev_priv = dev->dev_private;
|
|
+
|
|
+ if (dev_priv->overlay) {
|
|
+ /* The bo's should be free'd by the generic code already.
|
|
+ * Furthermore modesetting teardown happens beforehand so the
|
|
+ * hardware should be off already */
|
|
+ BUG_ON(dev_priv->overlay->active);
|
|
+
|
|
+ kfree(dev_priv->overlay);
|
|
+ }
|
|
+}
|
|
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
|
|
index 3f5aaf1..82678d3 100644
|
|
--- a/drivers/gpu/drm/i915/intel_sdvo.c
|
|
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
|
|
@@ -36,8 +36,6 @@
|
|
#include "intel_sdvo_regs.h"
|
|
#include <linux/dmi.h>
|
|
|
|
-#undef SDVO_DEBUG
|
|
-
|
|
static char *tv_format_names[] = {
|
|
"NTSC_M" , "NTSC_J" , "NTSC_443",
|
|
"PAL_B" , "PAL_D" , "PAL_G" ,
|
|
@@ -356,7 +354,6 @@ static const struct _sdvo_cmd_name {
|
|
#define SDVO_NAME(dev_priv) ((dev_priv)->output_device == SDVOB ? "SDVOB" : "SDVOC")
|
|
#define SDVO_PRIV(output) ((struct intel_sdvo_priv *) (output)->dev_priv)
|
|
|
|
-#ifdef SDVO_DEBUG
|
|
static void intel_sdvo_debug_write(struct intel_output *intel_output, u8 cmd,
|
|
void *args, int args_len)
|
|
{
|
|
@@ -379,9 +376,6 @@ static void intel_sdvo_debug_write(struct intel_output *intel_output, u8 cmd,
|
|
DRM_LOG_KMS("(%02X)", cmd);
|
|
DRM_LOG_KMS("\n");
|
|
}
|
|
-#else
|
|
-#define intel_sdvo_debug_write(o, c, a, l)
|
|
-#endif
|
|
|
|
static void intel_sdvo_write_cmd(struct intel_output *intel_output, u8 cmd,
|
|
void *args, int args_len)
|
|
@@ -398,7 +392,6 @@ static void intel_sdvo_write_cmd(struct intel_output *intel_output, u8 cmd,
|
|
intel_sdvo_write_byte(intel_output, SDVO_I2C_OPCODE, cmd);
|
|
}
|
|
|
|
-#ifdef SDVO_DEBUG
|
|
static const char *cmd_status_names[] = {
|
|
"Power on",
|
|
"Success",
|
|
@@ -427,9 +420,6 @@ static void intel_sdvo_debug_response(struct intel_output *intel_output,
|
|
DRM_LOG_KMS("(??? %d)", status);
|
|
DRM_LOG_KMS("\n");
|
|
}
|
|
-#else
|
|
-#define intel_sdvo_debug_response(o, r, l, s)
|
|
-#endif
|
|
|
|
static u8 intel_sdvo_read_response(struct intel_output *intel_output,
|
|
void *response, int response_len)
|
|
@@ -1702,6 +1692,10 @@ static enum drm_connector_status intel_sdvo_detect(struct drm_connector *connect
|
|
|
|
intel_sdvo_write_cmd(intel_output,
|
|
SDVO_CMD_GET_ATTACHED_DISPLAYS, NULL, 0);
|
|
+ if (sdvo_priv->is_tv) {
|
|
+ /* add 30ms delay when the output type is SDVO-TV */
|
|
+ mdelay(30);
|
|
+ }
|
|
status = intel_sdvo_read_response(intel_output, &response, 2);
|
|
|
|
DRM_DEBUG_KMS("SDVO response %d %d\n", response & 0xff, response >> 8);
|
|
@@ -2351,6 +2345,14 @@ intel_sdvo_output_setup(struct intel_output *intel_output, uint16_t flags)
|
|
connector->connector_type = DRM_MODE_CONNECTOR_VGA;
|
|
intel_output->clone_mask = (1 << INTEL_SDVO_NON_TV_CLONE_BIT) |
|
|
(1 << INTEL_ANALOG_CLONE_BIT);
|
|
+ } else if (flags & SDVO_OUTPUT_CVBS0) {
|
|
+
|
|
+ sdvo_priv->controlled_output = SDVO_OUTPUT_CVBS0;
|
|
+ encoder->encoder_type = DRM_MODE_ENCODER_TVDAC;
|
|
+ connector->connector_type = DRM_MODE_CONNECTOR_SVIDEO;
|
|
+ sdvo_priv->is_tv = true;
|
|
+ intel_output->needs_tv_clock = true;
|
|
+ intel_output->clone_mask = 1 << INTEL_SDVO_TV_CLONE_BIT;
|
|
} else if (flags & SDVO_OUTPUT_LVDS0) {
|
|
|
|
sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS0;
|
|
@@ -2804,7 +2806,7 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)
|
|
/* Wrap with our custom algo which switches to DDC mode */
|
|
intel_output->ddc_bus->algo = &intel_sdvo_i2c_bit_algo;
|
|
|
|
- /* In defaut case sdvo lvds is false */
|
|
+ /* In default case sdvo lvds is false */
|
|
intel_sdvo_get_capabilities(intel_output, &sdvo_priv->caps);
|
|
|
|
if (intel_sdvo_output_setup(intel_output,
|
|
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
|
|
index ce026f0..552ec11 100644
|
|
--- a/drivers/gpu/drm/i915/intel_tv.c
|
|
+++ b/drivers/gpu/drm/i915/intel_tv.c
|
|
@@ -1413,16 +1413,16 @@ intel_tv_detect_type (struct drm_crtc *crtc, struct intel_output *intel_output)
|
|
* 0 0 0 Component
|
|
*/
|
|
if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) {
|
|
- DRM_DEBUG("Detected Composite TV connection\n");
|
|
+ DRM_DEBUG_KMS("Detected Composite TV connection\n");
|
|
type = DRM_MODE_CONNECTOR_Composite;
|
|
} else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) {
|
|
- DRM_DEBUG("Detected S-Video TV connection\n");
|
|
+ DRM_DEBUG_KMS("Detected S-Video TV connection\n");
|
|
type = DRM_MODE_CONNECTOR_SVIDEO;
|
|
} else if ((tv_dac & TVDAC_SENSE_MASK) == 0) {
|
|
- DRM_DEBUG("Detected Component TV connection\n");
|
|
+ DRM_DEBUG_KMS("Detected Component TV connection\n");
|
|
type = DRM_MODE_CONNECTOR_Component;
|
|
} else {
|
|
- DRM_DEBUG("No TV connection detected\n");
|
|
+ DRM_DEBUG_KMS("No TV connection detected\n");
|
|
type = -1;
|
|
}
|
|
|
|
@@ -1699,6 +1699,41 @@ static const struct drm_encoder_funcs intel_tv_enc_funcs = {
|
|
.destroy = intel_tv_enc_destroy,
|
|
};
|
|
|
|
+/*
|
|
+ * Enumerate the child dev array parsed from VBT to check whether
|
|
+ * the integrated TV is present.
|
|
+ * If it is present, return 1.
|
|
+ * If it is not present, return false.
|
|
+ * If no child dev is parsed from VBT, it assumes that the TV is present.
|
|
+ */
|
|
+static int tv_is_present_in_vbt(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
+ struct child_device_config *p_child;
|
|
+ int i, ret;
|
|
+
|
|
+ if (!dev_priv->child_dev_num)
|
|
+ return 1;
|
|
+
|
|
+ ret = 0;
|
|
+ for (i = 0; i < dev_priv->child_dev_num; i++) {
|
|
+ p_child = dev_priv->child_dev + i;
|
|
+ /*
|
|
+ * If the device type is not TV, continue.
|
|
+ */
|
|
+ if (p_child->device_type != DEVICE_TYPE_INT_TV &&
|
|
+ p_child->device_type != DEVICE_TYPE_TV)
|
|
+ continue;
|
|
+ /* Only when the addin_offset is non-zero, it is regarded
|
|
+ * as present.
|
|
+ */
|
|
+ if (p_child->addin_offset) {
|
|
+ ret = 1;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
|
|
void
|
|
intel_tv_init(struct drm_device *dev)
|
|
@@ -1714,6 +1749,10 @@ intel_tv_init(struct drm_device *dev)
|
|
if ((I915_READ(TV_CTL) & TV_FUSE_STATE_MASK) == TV_FUSE_STATE_DISABLED)
|
|
return;
|
|
|
|
+ if (!tv_is_present_in_vbt(dev)) {
|
|
+ DRM_DEBUG_KMS("Integrated TV is not present.\n");
|
|
+ return;
|
|
+ }
|
|
/* Even if we have an encoder we may not have a connector */
|
|
if (!dev_priv->int_tv_support)
|
|
return;
|
|
@@ -1801,8 +1840,6 @@ intel_tv_init(struct drm_device *dev)
|
|
drm_connector_attach_property(connector,
|
|
dev->mode_config.tv_bottom_margin_property,
|
|
tv_priv->margin[TV_MARGIN_BOTTOM]);
|
|
-
|
|
- dev_priv->hotplug_supported_mask |= TV_HOTPLUG_INT_STATUS;
|
|
out:
|
|
drm_sysfs_connector_add(connector);
|
|
}
|
|
diff --git a/drivers/gpu/drm/mga/mga_drv.c b/drivers/gpu/drm/mga/mga_drv.c
|
|
index 97ee566..ddfe161 100644
|
|
--- a/drivers/gpu/drm/mga/mga_drv.c
|
|
+++ b/drivers/gpu/drm/mga/mga_drv.c
|
|
@@ -68,7 +68,7 @@ static struct drm_driver driver = {
|
|
.owner = THIS_MODULE,
|
|
.open = drm_open,
|
|
.release = drm_release,
|
|
- .ioctl = drm_ioctl,
|
|
+ .unlocked_ioctl = drm_ioctl,
|
|
.mmap = drm_mmap,
|
|
.poll = drm_poll,
|
|
.fasync = drm_fasync,
|
|
diff --git a/drivers/gpu/drm/mga/mga_ioc32.c b/drivers/gpu/drm/mga/mga_ioc32.c
|
|
index 30d0047..c1f877b 100644
|
|
--- a/drivers/gpu/drm/mga/mga_ioc32.c
|
|
+++ b/drivers/gpu/drm/mga/mga_ioc32.c
|
|
@@ -100,8 +100,7 @@ static int compat_mga_init(struct file *file, unsigned int cmd,
|
|
if (err)
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_MGA_INIT, (unsigned long)init);
|
|
+ return drm_ioctl(file, DRM_IOCTL_MGA_INIT, (unsigned long)init);
|
|
}
|
|
|
|
typedef struct drm_mga_getparam32 {
|
|
@@ -125,8 +124,7 @@ static int compat_mga_getparam(struct file *file, unsigned int cmd,
|
|
&getparam->value))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_MGA_GETPARAM, (unsigned long)getparam);
|
|
+ return drm_ioctl(file, DRM_IOCTL_MGA_GETPARAM, (unsigned long)getparam);
|
|
}
|
|
|
|
typedef struct drm_mga_drm_bootstrap32 {
|
|
@@ -166,8 +164,7 @@ static int compat_mga_dma_bootstrap(struct file *file, unsigned int cmd,
|
|
|| __put_user(dma_bootstrap32.agp_size, &dma_bootstrap->agp_size))
|
|
return -EFAULT;
|
|
|
|
- err = drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_MGA_DMA_BOOTSTRAP,
|
|
+ err = drm_ioctl(file, DRM_IOCTL_MGA_DMA_BOOTSTRAP,
|
|
(unsigned long)dma_bootstrap);
|
|
if (err)
|
|
return err;
|
|
@@ -220,12 +217,10 @@ long mga_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|
if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(mga_compat_ioctls))
|
|
fn = mga_compat_ioctls[nr - DRM_COMMAND_BASE];
|
|
|
|
- lock_kernel(); /* XXX for now */
|
|
if (fn != NULL)
|
|
ret = (*fn) (filp, cmd, arg);
|
|
else
|
|
- ret = drm_ioctl(filp->f_path.dentry->d_inode, filp, cmd, arg);
|
|
- unlock_kernel();
|
|
+ ret = drm_ioctl(filp, cmd, arg);
|
|
|
|
return ret;
|
|
}
|
|
diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig
|
|
new file mode 100644
|
|
index 0000000..1175429
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/Kconfig
|
|
@@ -0,0 +1,44 @@
|
|
+config DRM_NOUVEAU
|
|
+ tristate "Nouveau (nVidia) cards"
|
|
+ depends on DRM
|
|
+ select FW_LOADER
|
|
+ select DRM_KMS_HELPER
|
|
+ select DRM_TTM
|
|
+ select FB_CFB_FILLRECT
|
|
+ select FB_CFB_COPYAREA
|
|
+ select FB_CFB_IMAGEBLIT
|
|
+ select FB
|
|
+ select FRAMEBUFFER_CONSOLE if !EMBEDDED
|
|
+ select FB_BACKLIGHT if DRM_NOUVEAU_BACKLIGHT
|
|
+ help
|
|
+ Choose this option for open-source nVidia support.
|
|
+
|
|
+config DRM_NOUVEAU_BACKLIGHT
|
|
+ bool "Support for backlight control"
|
|
+ depends on DRM_NOUVEAU
|
|
+ default y
|
|
+ help
|
|
+ Say Y here if you want to control the backlight of your display
|
|
+ (e.g. a laptop panel).
|
|
+
|
|
+config DRM_NOUVEAU_DEBUG
|
|
+ bool "Build in Nouveau's debugfs support"
|
|
+ depends on DRM_NOUVEAU && DEBUG_FS
|
|
+ default y
|
|
+ help
|
|
+ Say Y here if you want Nouveau to output debugging information
|
|
+ via debugfs.
|
|
+
|
|
+menu "I2C encoder or helper chips"
|
|
+ depends on DRM && DRM_KMS_HELPER && I2C
|
|
+
|
|
+config DRM_I2C_CH7006
|
|
+ tristate "Chrontel ch7006 TV encoder"
|
|
+ default m if DRM_NOUVEAU
|
|
+ help
|
|
+ Support for Chrontel ch7006 and similar TV encoders, found
|
|
+ on some nVidia video cards.
|
|
+
|
|
+ This driver is currently only useful if you're also using
|
|
+ the nouveau driver.
|
|
+endmenu
|
|
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile
|
|
new file mode 100644
|
|
index 0000000..48c290b
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/Makefile
|
|
@@ -0,0 +1,32 @@
|
|
+#
|
|
+# Makefile for the drm device driver. This driver provides support for the
|
|
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
|
|
+
|
|
+ccflags-y := -Iinclude/drm
|
|
+nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
|
|
+ nouveau_object.o nouveau_irq.o nouveau_notifier.o \
|
|
+ nouveau_sgdma.o nouveau_dma.o \
|
|
+ nouveau_bo.o nouveau_fence.o nouveau_gem.o nouveau_ttm.o \
|
|
+ nouveau_hw.o nouveau_calc.o nouveau_bios.o nouveau_i2c.o \
|
|
+ nouveau_display.o nouveau_connector.o nouveau_fbcon.o \
|
|
+ nouveau_dp.o nouveau_grctx.o \
|
|
+ nv04_timer.o \
|
|
+ nv04_mc.o nv40_mc.o nv50_mc.o \
|
|
+ nv04_fb.o nv10_fb.o nv40_fb.o \
|
|
+ nv04_fifo.o nv10_fifo.o nv40_fifo.o nv50_fifo.o \
|
|
+ nv04_graph.o nv10_graph.o nv20_graph.o \
|
|
+ nv40_graph.o nv50_graph.o \
|
|
+ nv40_grctx.o \
|
|
+ nv04_instmem.o nv50_instmem.o \
|
|
+ nv50_crtc.o nv50_dac.o nv50_sor.o \
|
|
+ nv50_cursor.o nv50_display.o nv50_fbcon.o \
|
|
+ nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o \
|
|
+ nv04_crtc.o nv04_display.o nv04_cursor.o nv04_fbcon.o \
|
|
+ nv17_gpio.o
|
|
+
|
|
+nouveau-$(CONFIG_DRM_NOUVEAU_DEBUG) += nouveau_debugfs.o
|
|
+nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o
|
|
+nouveau-$(CONFIG_DRM_NOUVEAU_BACKLIGHT) += nouveau_backlight.o
|
|
+nouveau-$(CONFIG_ACPI) += nouveau_acpi.o
|
|
+
|
|
+obj-$(CONFIG_DRM_NOUVEAU)+= nouveau.o
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c
|
|
new file mode 100644
|
|
index 0000000..48227e7
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c
|
|
@@ -0,0 +1,125 @@
|
|
+#include <linux/pci.h>
|
|
+#include <linux/acpi.h>
|
|
+#include <acpi/acpi_drivers.h>
|
|
+#include <acpi/acpi_bus.h>
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "drm_sarea.h"
|
|
+#include "drm_crtc_helper.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_drm.h"
|
|
+#include "nv50_display.h"
|
|
+
|
|
+#define NOUVEAU_DSM_SUPPORTED 0x00
|
|
+#define NOUVEAU_DSM_SUPPORTED_FUNCTIONS 0x00
|
|
+
|
|
+#define NOUVEAU_DSM_ACTIVE 0x01
|
|
+#define NOUVEAU_DSM_ACTIVE_QUERY 0x00
|
|
+
|
|
+#define NOUVEAU_DSM_LED 0x02
|
|
+#define NOUVEAU_DSM_LED_STATE 0x00
|
|
+#define NOUVEAU_DSM_LED_OFF 0x10
|
|
+#define NOUVEAU_DSM_LED_STAMINA 0x11
|
|
+#define NOUVEAU_DSM_LED_SPEED 0x12
|
|
+
|
|
+#define NOUVEAU_DSM_POWER 0x03
|
|
+#define NOUVEAU_DSM_POWER_STATE 0x00
|
|
+#define NOUVEAU_DSM_POWER_SPEED 0x01
|
|
+#define NOUVEAU_DSM_POWER_STAMINA 0x02
|
|
+
|
|
+static int nouveau_dsm(struct drm_device *dev, int func, int arg, int *result)
|
|
+{
|
|
+ static char muid[] = {
|
|
+ 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D,
|
|
+ 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4,
|
|
+ };
|
|
+
|
|
+ struct pci_dev *pdev = dev->pdev;
|
|
+ struct acpi_handle *handle;
|
|
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
|
+ struct acpi_object_list input;
|
|
+ union acpi_object params[4];
|
|
+ union acpi_object *obj;
|
|
+ int err;
|
|
+
|
|
+ handle = DEVICE_ACPI_HANDLE(&pdev->dev);
|
|
+
|
|
+ if (!handle)
|
|
+ return -ENODEV;
|
|
+
|
|
+ input.count = 4;
|
|
+ input.pointer = params;
|
|
+ params[0].type = ACPI_TYPE_BUFFER;
|
|
+ params[0].buffer.length = sizeof(muid);
|
|
+ params[0].buffer.pointer = (char *)muid;
|
|
+ params[1].type = ACPI_TYPE_INTEGER;
|
|
+ params[1].integer.value = 0x00000102;
|
|
+ params[2].type = ACPI_TYPE_INTEGER;
|
|
+ params[2].integer.value = func;
|
|
+ params[3].type = ACPI_TYPE_INTEGER;
|
|
+ params[3].integer.value = arg;
|
|
+
|
|
+ err = acpi_evaluate_object(handle, "_DSM", &input, &output);
|
|
+ if (err) {
|
|
+ NV_INFO(dev, "failed to evaluate _DSM: %d\n", err);
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ obj = (union acpi_object *)output.pointer;
|
|
+
|
|
+ if (obj->type == ACPI_TYPE_INTEGER)
|
|
+ if (obj->integer.value == 0x80000002)
|
|
+ return -ENODEV;
|
|
+
|
|
+ if (obj->type == ACPI_TYPE_BUFFER) {
|
|
+ if (obj->buffer.length == 4 && result) {
|
|
+ *result = 0;
|
|
+ *result |= obj->buffer.pointer[0];
|
|
+ *result |= (obj->buffer.pointer[1] << 8);
|
|
+ *result |= (obj->buffer.pointer[2] << 16);
|
|
+ *result |= (obj->buffer.pointer[3] << 24);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ kfree(output.pointer);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int nouveau_hybrid_setup(struct drm_device *dev)
|
|
+{
|
|
+ int result;
|
|
+
|
|
+ if (nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_STATE,
|
|
+ &result))
|
|
+ return -ENODEV;
|
|
+
|
|
+ NV_INFO(dev, "_DSM hardware status gave 0x%x\n", result);
|
|
+
|
|
+ if (result) { /* Ensure that the external GPU is enabled */
|
|
+ nouveau_dsm(dev, NOUVEAU_DSM_LED, NOUVEAU_DSM_LED_SPEED, NULL);
|
|
+ nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_SPEED,
|
|
+ NULL);
|
|
+ } else { /* Stamina mode - disable the external GPU */
|
|
+ nouveau_dsm(dev, NOUVEAU_DSM_LED, NOUVEAU_DSM_LED_STAMINA,
|
|
+ NULL);
|
|
+ nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_STAMINA,
|
|
+ NULL);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+bool nouveau_dsm_probe(struct drm_device *dev)
|
|
+{
|
|
+ int support = 0;
|
|
+
|
|
+ if (nouveau_dsm(dev, NOUVEAU_DSM_SUPPORTED,
|
|
+ NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &support))
|
|
+ return false;
|
|
+
|
|
+ if (!support)
|
|
+ return false;
|
|
+
|
|
+ return true;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c
|
|
new file mode 100644
|
|
index 0000000..20564f8
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c
|
|
@@ -0,0 +1,155 @@
|
|
+/*
|
|
+ * Copyright (C) 2009 Red Hat <mjg@redhat.com>
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * Authors:
|
|
+ * Matthew Garrett <mjg@redhat.com>
|
|
+ *
|
|
+ * Register locations derived from NVClock by Roderick Colenbrander
|
|
+ */
|
|
+
|
|
+#include <linux/backlight.h>
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_drm.h"
|
|
+#include "nouveau_reg.h"
|
|
+
|
|
+static int nv40_get_intensity(struct backlight_device *bd)
|
|
+{
|
|
+ struct drm_device *dev = bl_get_data(bd);
|
|
+ int val = (nv_rd32(dev, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK)
|
|
+ >> 16;
|
|
+
|
|
+ return val;
|
|
+}
|
|
+
|
|
+static int nv40_set_intensity(struct backlight_device *bd)
|
|
+{
|
|
+ struct drm_device *dev = bl_get_data(bd);
|
|
+ int val = bd->props.brightness;
|
|
+ int reg = nv_rd32(dev, NV40_PMC_BACKLIGHT);
|
|
+
|
|
+ nv_wr32(dev, NV40_PMC_BACKLIGHT,
|
|
+ (val << 16) | (reg & ~NV40_PMC_BACKLIGHT_MASK));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct backlight_ops nv40_bl_ops = {
|
|
+ .options = BL_CORE_SUSPENDRESUME,
|
|
+ .get_brightness = nv40_get_intensity,
|
|
+ .update_status = nv40_set_intensity,
|
|
+};
|
|
+
|
|
+static int nv50_get_intensity(struct backlight_device *bd)
|
|
+{
|
|
+ struct drm_device *dev = bl_get_data(bd);
|
|
+
|
|
+ return nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT);
|
|
+}
|
|
+
|
|
+static int nv50_set_intensity(struct backlight_device *bd)
|
|
+{
|
|
+ struct drm_device *dev = bl_get_data(bd);
|
|
+ int val = bd->props.brightness;
|
|
+
|
|
+ nv_wr32(dev, NV50_PDISPLAY_SOR_BACKLIGHT,
|
|
+ val | NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct backlight_ops nv50_bl_ops = {
|
|
+ .options = BL_CORE_SUSPENDRESUME,
|
|
+ .get_brightness = nv50_get_intensity,
|
|
+ .update_status = nv50_set_intensity,
|
|
+};
|
|
+
|
|
+static int nouveau_nv40_backlight_init(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct backlight_device *bd;
|
|
+
|
|
+ if (!(nv_rd32(dev, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK))
|
|
+ return 0;
|
|
+
|
|
+ bd = backlight_device_register("nv_backlight", &dev->pdev->dev, dev,
|
|
+ &nv40_bl_ops);
|
|
+ if (IS_ERR(bd))
|
|
+ return PTR_ERR(bd);
|
|
+
|
|
+ dev_priv->backlight = bd;
|
|
+ bd->props.max_brightness = 31;
|
|
+ bd->props.brightness = nv40_get_intensity(bd);
|
|
+ backlight_update_status(bd);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int nouveau_nv50_backlight_init(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct backlight_device *bd;
|
|
+
|
|
+ if (!nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT))
|
|
+ return 0;
|
|
+
|
|
+ bd = backlight_device_register("nv_backlight", &dev->pdev->dev, dev,
|
|
+ &nv50_bl_ops);
|
|
+ if (IS_ERR(bd))
|
|
+ return PTR_ERR(bd);
|
|
+
|
|
+ dev_priv->backlight = bd;
|
|
+ bd->props.max_brightness = 1025;
|
|
+ bd->props.brightness = nv50_get_intensity(bd);
|
|
+ backlight_update_status(bd);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int nouveau_backlight_init(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ switch (dev_priv->card_type) {
|
|
+ case NV_40:
|
|
+ return nouveau_nv40_backlight_init(dev);
|
|
+ case NV_50:
|
|
+ return nouveau_nv50_backlight_init(dev);
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void nouveau_backlight_exit(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ if (dev_priv->backlight) {
|
|
+ backlight_device_unregister(dev_priv->backlight);
|
|
+ dev_priv->backlight = NULL;
|
|
+ }
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
|
|
new file mode 100644
|
|
index 0000000..2cd0fad
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
|
|
@@ -0,0 +1,6052 @@
|
|
+/*
|
|
+ * Copyright 2005-2006 Erik Waling
|
|
+ * Copyright 2006 Stephane Marchesin
|
|
+ * Copyright 2007-2009 Stuart Bennett
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice shall be included in
|
|
+ * all copies or substantial portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
|
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
+ * SOFTWARE.
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#define NV_DEBUG_NOTRACE
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_hw.h"
|
|
+
|
|
+/* these defines are made up */
|
|
+#define NV_CIO_CRE_44_HEADA 0x0
|
|
+#define NV_CIO_CRE_44_HEADB 0x3
|
|
+#define FEATURE_MOBILE 0x10 /* also FEATURE_QUADRO for BMP */
|
|
+#define LEGACY_I2C_CRT 0x80
|
|
+#define LEGACY_I2C_PANEL 0x81
|
|
+#define LEGACY_I2C_TV 0x82
|
|
+
|
|
+#define EDID1_LEN 128
|
|
+
|
|
+#define BIOSLOG(sip, fmt, arg...) NV_DEBUG(sip->dev, fmt, ##arg)
|
|
+#define LOG_OLD_VALUE(x)
|
|
+
|
|
+#define ROM16(x) le16_to_cpu(*(uint16_t *)&(x))
|
|
+#define ROM32(x) le32_to_cpu(*(uint32_t *)&(x))
|
|
+
|
|
+struct init_exec {
|
|
+ bool execute;
|
|
+ bool repeat;
|
|
+};
|
|
+
|
|
+static bool nv_cksum(const uint8_t *data, unsigned int length)
|
|
+{
|
|
+ /*
|
|
+ * There's a few checksums in the BIOS, so here's a generic checking
|
|
+ * function.
|
|
+ */
|
|
+ int i;
|
|
+ uint8_t sum = 0;
|
|
+
|
|
+ for (i = 0; i < length; i++)
|
|
+ sum += data[i];
|
|
+
|
|
+ if (sum)
|
|
+ return true;
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+static int
|
|
+score_vbios(struct drm_device *dev, const uint8_t *data, const bool writeable)
|
|
+{
|
|
+ if (!(data[0] == 0x55 && data[1] == 0xAA)) {
|
|
+ NV_TRACEWARN(dev, "... BIOS signature not found\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (nv_cksum(data, data[2] * 512)) {
|
|
+ NV_TRACEWARN(dev, "... BIOS checksum invalid\n");
|
|
+ /* if a ro image is somewhat bad, it's probably all rubbish */
|
|
+ return writeable ? 2 : 1;
|
|
+ } else
|
|
+ NV_TRACE(dev, "... appears to be valid\n");
|
|
+
|
|
+ return 3;
|
|
+}
|
|
+
|
|
+static void load_vbios_prom(struct drm_device *dev, uint8_t *data)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t pci_nv_20, save_pci_nv_20;
|
|
+ int pcir_ptr;
|
|
+ int i;
|
|
+
|
|
+ if (dev_priv->card_type >= NV_50)
|
|
+ pci_nv_20 = 0x88050;
|
|
+ else
|
|
+ pci_nv_20 = NV_PBUS_PCI_NV_20;
|
|
+
|
|
+ /* enable ROM access */
|
|
+ save_pci_nv_20 = nvReadMC(dev, pci_nv_20);
|
|
+ nvWriteMC(dev, pci_nv_20,
|
|
+ save_pci_nv_20 & ~NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED);
|
|
+
|
|
+ /* bail if no rom signature */
|
|
+ if (nv_rd08(dev, NV_PROM_OFFSET) != 0x55 ||
|
|
+ nv_rd08(dev, NV_PROM_OFFSET + 1) != 0xaa)
|
|
+ goto out;
|
|
+
|
|
+ /* additional check (see note below) - read PCI record header */
|
|
+ pcir_ptr = nv_rd08(dev, NV_PROM_OFFSET + 0x18) |
|
|
+ nv_rd08(dev, NV_PROM_OFFSET + 0x19) << 8;
|
|
+ if (nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr) != 'P' ||
|
|
+ nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 1) != 'C' ||
|
|
+ nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 2) != 'I' ||
|
|
+ nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 3) != 'R')
|
|
+ goto out;
|
|
+
|
|
+ /* on some 6600GT/6800LE prom reads are messed up. nvclock alleges a
|
|
+ * a good read may be obtained by waiting or re-reading (cargocult: 5x)
|
|
+ * each byte. we'll hope pramin has something usable instead
|
|
+ */
|
|
+ for (i = 0; i < NV_PROM_SIZE; i++)
|
|
+ data[i] = nv_rd08(dev, NV_PROM_OFFSET + i);
|
|
+
|
|
+out:
|
|
+ /* disable ROM access */
|
|
+ nvWriteMC(dev, pci_nv_20,
|
|
+ save_pci_nv_20 | NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED);
|
|
+}
|
|
+
|
|
+static void load_vbios_pramin(struct drm_device *dev, uint8_t *data)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t old_bar0_pramin = 0;
|
|
+ int i;
|
|
+
|
|
+ if (dev_priv->card_type >= NV_50) {
|
|
+ uint32_t vbios_vram = (nv_rd32(dev, 0x619f04) & ~0xff) << 8;
|
|
+
|
|
+ if (!vbios_vram)
|
|
+ vbios_vram = (nv_rd32(dev, 0x1700) << 16) + 0xf0000;
|
|
+
|
|
+ old_bar0_pramin = nv_rd32(dev, 0x1700);
|
|
+ nv_wr32(dev, 0x1700, vbios_vram >> 16);
|
|
+ }
|
|
+
|
|
+ /* bail if no rom signature */
|
|
+ if (nv_rd08(dev, NV_PRAMIN_OFFSET) != 0x55 ||
|
|
+ nv_rd08(dev, NV_PRAMIN_OFFSET + 1) != 0xaa)
|
|
+ goto out;
|
|
+
|
|
+ for (i = 0; i < NV_PROM_SIZE; i++)
|
|
+ data[i] = nv_rd08(dev, NV_PRAMIN_OFFSET + i);
|
|
+
|
|
+out:
|
|
+ if (dev_priv->card_type >= NV_50)
|
|
+ nv_wr32(dev, 0x1700, old_bar0_pramin);
|
|
+}
|
|
+
|
|
+static void load_vbios_pci(struct drm_device *dev, uint8_t *data)
|
|
+{
|
|
+ void __iomem *rom = NULL;
|
|
+ size_t rom_len;
|
|
+ int ret;
|
|
+
|
|
+ ret = pci_enable_rom(dev->pdev);
|
|
+ if (ret)
|
|
+ return;
|
|
+
|
|
+ rom = pci_map_rom(dev->pdev, &rom_len);
|
|
+ if (!rom)
|
|
+ goto out;
|
|
+ memcpy_fromio(data, rom, rom_len);
|
|
+ pci_unmap_rom(dev->pdev, rom);
|
|
+
|
|
+out:
|
|
+ pci_disable_rom(dev->pdev);
|
|
+}
|
|
+
|
|
+struct methods {
|
|
+ const char desc[8];
|
|
+ void (*loadbios)(struct drm_device *, uint8_t *);
|
|
+ const bool rw;
|
|
+};
|
|
+
|
|
+static struct methods nv04_methods[] = {
|
|
+ { "PROM", load_vbios_prom, false },
|
|
+ { "PRAMIN", load_vbios_pramin, true },
|
|
+ { "PCIROM", load_vbios_pci, true },
|
|
+};
|
|
+
|
|
+static struct methods nv50_methods[] = {
|
|
+ { "PRAMIN", load_vbios_pramin, true },
|
|
+ { "PROM", load_vbios_prom, false },
|
|
+ { "PCIROM", load_vbios_pci, true },
|
|
+};
|
|
+
|
|
+#define METHODCNT 3
|
|
+
|
|
+static bool NVShadowVBIOS(struct drm_device *dev, uint8_t *data)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct methods *methods;
|
|
+ int i;
|
|
+ int testscore = 3;
|
|
+ int scores[METHODCNT];
|
|
+
|
|
+ if (nouveau_vbios) {
|
|
+ methods = nv04_methods;
|
|
+ for (i = 0; i < METHODCNT; i++)
|
|
+ if (!strcasecmp(nouveau_vbios, methods[i].desc))
|
|
+ break;
|
|
+
|
|
+ if (i < METHODCNT) {
|
|
+ NV_INFO(dev, "Attempting to use BIOS image from %s\n",
|
|
+ methods[i].desc);
|
|
+
|
|
+ methods[i].loadbios(dev, data);
|
|
+ if (score_vbios(dev, data, methods[i].rw))
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ NV_ERROR(dev, "VBIOS source \'%s\' invalid\n", nouveau_vbios);
|
|
+ }
|
|
+
|
|
+ if (dev_priv->card_type < NV_50)
|
|
+ methods = nv04_methods;
|
|
+ else
|
|
+ methods = nv50_methods;
|
|
+
|
|
+ for (i = 0; i < METHODCNT; i++) {
|
|
+ NV_TRACE(dev, "Attempting to load BIOS image from %s\n",
|
|
+ methods[i].desc);
|
|
+ data[0] = data[1] = 0; /* avoid reuse of previous image */
|
|
+ methods[i].loadbios(dev, data);
|
|
+ scores[i] = score_vbios(dev, data, methods[i].rw);
|
|
+ if (scores[i] == testscore)
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ while (--testscore > 0) {
|
|
+ for (i = 0; i < METHODCNT; i++) {
|
|
+ if (scores[i] == testscore) {
|
|
+ NV_TRACE(dev, "Using BIOS image from %s\n",
|
|
+ methods[i].desc);
|
|
+ methods[i].loadbios(dev, data);
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ NV_ERROR(dev, "No valid BIOS image found\n");
|
|
+ return false;
|
|
+}
|
|
+
|
|
+struct init_tbl_entry {
|
|
+ char *name;
|
|
+ uint8_t id;
|
|
+ int (*handler)(struct nvbios *, uint16_t, struct init_exec *);
|
|
+};
|
|
+
|
|
+struct bit_entry {
|
|
+ uint8_t id[2];
|
|
+ uint16_t length;
|
|
+ uint16_t offset;
|
|
+};
|
|
+
|
|
+static int parse_init_table(struct nvbios *, unsigned int, struct init_exec *);
|
|
+
|
|
+#define MACRO_INDEX_SIZE 2
|
|
+#define MACRO_SIZE 8
|
|
+#define CONDITION_SIZE 12
|
|
+#define IO_FLAG_CONDITION_SIZE 9
|
|
+#define IO_CONDITION_SIZE 5
|
|
+#define MEM_INIT_SIZE 66
|
|
+
|
|
+static void still_alive(void)
|
|
+{
|
|
+#if 0
|
|
+ sync();
|
|
+ msleep(2);
|
|
+#endif
|
|
+}
|
|
+
|
|
+static uint32_t
|
|
+munge_reg(struct nvbios *bios, uint32_t reg)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
|
|
+ struct dcb_entry *dcbent = bios->display.output;
|
|
+
|
|
+ if (dev_priv->card_type < NV_50)
|
|
+ return reg;
|
|
+
|
|
+ if (reg & 0x40000000) {
|
|
+ BUG_ON(!dcbent);
|
|
+
|
|
+ reg += (ffs(dcbent->or) - 1) * 0x800;
|
|
+ if ((reg & 0x20000000) && !(dcbent->sorconf.link & 1))
|
|
+ reg += 0x00000080;
|
|
+ }
|
|
+
|
|
+ reg &= ~0x60000000;
|
|
+ return reg;
|
|
+}
|
|
+
|
|
+static int
|
|
+valid_reg(struct nvbios *bios, uint32_t reg)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
|
|
+ struct drm_device *dev = bios->dev;
|
|
+
|
|
+ /* C51 has misaligned regs on purpose. Marvellous */
|
|
+ if (reg & 0x2 ||
|
|
+ (reg & 0x1 && dev_priv->VBIOS.pub.chip_version != 0x51))
|
|
+ NV_ERROR(dev, "======= misaligned reg 0x%08X =======\n", reg);
|
|
+
|
|
+ /* warn on C51 regs that haven't been verified accessible in tracing */
|
|
+ if (reg & 0x1 && dev_priv->VBIOS.pub.chip_version == 0x51 &&
|
|
+ reg != 0x130d && reg != 0x1311 && reg != 0x60081d)
|
|
+ NV_WARN(dev, "=== C51 misaligned reg 0x%08X not verified ===\n",
|
|
+ reg);
|
|
+
|
|
+ if (reg >= (8*1024*1024)) {
|
|
+ NV_ERROR(dev, "=== reg 0x%08x out of mapped bounds ===\n", reg);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static bool
|
|
+valid_idx_port(struct nvbios *bios, uint16_t port)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
|
|
+ struct drm_device *dev = bios->dev;
|
|
+
|
|
+ /*
|
|
+ * If adding more ports here, the read/write functions below will need
|
|
+ * updating so that the correct mmio range (PRMCIO, PRMDIO, PRMVIO) is
|
|
+ * used for the port in question
|
|
+ */
|
|
+ if (dev_priv->card_type < NV_50) {
|
|
+ if (port == NV_CIO_CRX__COLOR)
|
|
+ return true;
|
|
+ if (port == NV_VIO_SRX)
|
|
+ return true;
|
|
+ } else {
|
|
+ if (port == NV_CIO_CRX__COLOR)
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ NV_ERROR(dev, "========== unknown indexed io port 0x%04X ==========\n",
|
|
+ port);
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+static bool
|
|
+valid_port(struct nvbios *bios, uint16_t port)
|
|
+{
|
|
+ struct drm_device *dev = bios->dev;
|
|
+
|
|
+ /*
|
|
+ * If adding more ports here, the read/write functions below will need
|
|
+ * updating so that the correct mmio range (PRMCIO, PRMDIO, PRMVIO) is
|
|
+ * used for the port in question
|
|
+ */
|
|
+ if (port == NV_VIO_VSE2)
|
|
+ return true;
|
|
+
|
|
+ NV_ERROR(dev, "========== unknown io port 0x%04X ==========\n", port);
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+static uint32_t
|
|
+bios_rd32(struct nvbios *bios, uint32_t reg)
|
|
+{
|
|
+ uint32_t data;
|
|
+
|
|
+ reg = munge_reg(bios, reg);
|
|
+ if (!valid_reg(bios, reg))
|
|
+ return 0;
|
|
+
|
|
+ /*
|
|
+ * C51 sometimes uses regs with bit0 set in the address. For these
|
|
+ * cases there should exist a translation in a BIOS table to an IO
|
|
+ * port address which the BIOS uses for accessing the reg
|
|
+ *
|
|
+ * These only seem to appear for the power control regs to a flat panel,
|
|
+ * and the GPIO regs at 0x60081*. In C51 mmio traces the normal regs
|
|
+ * for 0x1308 and 0x1310 are used - hence the mask below. An S3
|
|
+ * suspend-resume mmio trace from a C51 will be required to see if this
|
|
+ * is true for the power microcode in 0x14.., or whether the direct IO
|
|
+ * port access method is needed
|
|
+ */
|
|
+ if (reg & 0x1)
|
|
+ reg &= ~0x1;
|
|
+
|
|
+ data = nv_rd32(bios->dev, reg);
|
|
+
|
|
+ BIOSLOG(bios, " Read: Reg: 0x%08X, Data: 0x%08X\n", reg, data);
|
|
+
|
|
+ return data;
|
|
+}
|
|
+
|
|
+static void
|
|
+bios_wr32(struct nvbios *bios, uint32_t reg, uint32_t data)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
|
|
+
|
|
+ reg = munge_reg(bios, reg);
|
|
+ if (!valid_reg(bios, reg))
|
|
+ return;
|
|
+
|
|
+ /* see note in bios_rd32 */
|
|
+ if (reg & 0x1)
|
|
+ reg &= 0xfffffffe;
|
|
+
|
|
+ LOG_OLD_VALUE(bios_rd32(bios, reg));
|
|
+ BIOSLOG(bios, " Write: Reg: 0x%08X, Data: 0x%08X\n", reg, data);
|
|
+
|
|
+ if (dev_priv->VBIOS.execute) {
|
|
+ still_alive();
|
|
+ nv_wr32(bios->dev, reg, data);
|
|
+ }
|
|
+}
|
|
+
|
|
+static uint8_t
|
|
+bios_idxprt_rd(struct nvbios *bios, uint16_t port, uint8_t index)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
|
|
+ struct drm_device *dev = bios->dev;
|
|
+ uint8_t data;
|
|
+
|
|
+ if (!valid_idx_port(bios, port))
|
|
+ return 0;
|
|
+
|
|
+ if (dev_priv->card_type < NV_50) {
|
|
+ if (port == NV_VIO_SRX)
|
|
+ data = NVReadVgaSeq(dev, bios->state.crtchead, index);
|
|
+ else /* assume NV_CIO_CRX__COLOR */
|
|
+ data = NVReadVgaCrtc(dev, bios->state.crtchead, index);
|
|
+ } else {
|
|
+ uint32_t data32;
|
|
+
|
|
+ data32 = bios_rd32(bios, NV50_PDISPLAY_VGACRTC(index & ~3));
|
|
+ data = (data32 >> ((index & 3) << 3)) & 0xff;
|
|
+ }
|
|
+
|
|
+ BIOSLOG(bios, " Indexed IO read: Port: 0x%04X, Index: 0x%02X, "
|
|
+ "Head: 0x%02X, Data: 0x%02X\n",
|
|
+ port, index, bios->state.crtchead, data);
|
|
+ return data;
|
|
+}
|
|
+
|
|
+static void
|
|
+bios_idxprt_wr(struct nvbios *bios, uint16_t port, uint8_t index, uint8_t data)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
|
|
+ struct drm_device *dev = bios->dev;
|
|
+
|
|
+ if (!valid_idx_port(bios, port))
|
|
+ return;
|
|
+
|
|
+ /*
|
|
+ * The current head is maintained in the nvbios member state.crtchead.
|
|
+ * We trap changes to CR44 and update the head variable and hence the
|
|
+ * register set written.
|
|
+ * As CR44 only exists on CRTC0, we update crtchead to head0 in advance
|
|
+ * of the write, and to head1 after the write
|
|
+ */
|
|
+ if (port == NV_CIO_CRX__COLOR && index == NV_CIO_CRE_44 &&
|
|
+ data != NV_CIO_CRE_44_HEADB)
|
|
+ bios->state.crtchead = 0;
|
|
+
|
|
+ LOG_OLD_VALUE(bios_idxprt_rd(bios, port, index));
|
|
+ BIOSLOG(bios, " Indexed IO write: Port: 0x%04X, Index: 0x%02X, "
|
|
+ "Head: 0x%02X, Data: 0x%02X\n",
|
|
+ port, index, bios->state.crtchead, data);
|
|
+
|
|
+ if (bios->execute && dev_priv->card_type < NV_50) {
|
|
+ still_alive();
|
|
+ if (port == NV_VIO_SRX)
|
|
+ NVWriteVgaSeq(dev, bios->state.crtchead, index, data);
|
|
+ else /* assume NV_CIO_CRX__COLOR */
|
|
+ NVWriteVgaCrtc(dev, bios->state.crtchead, index, data);
|
|
+ } else
|
|
+ if (bios->execute) {
|
|
+ uint32_t data32, shift = (index & 3) << 3;
|
|
+
|
|
+ still_alive();
|
|
+
|
|
+ data32 = bios_rd32(bios, NV50_PDISPLAY_VGACRTC(index & ~3));
|
|
+ data32 &= ~(0xff << shift);
|
|
+ data32 |= (data << shift);
|
|
+ bios_wr32(bios, NV50_PDISPLAY_VGACRTC(index & ~3), data32);
|
|
+ }
|
|
+
|
|
+ if (port == NV_CIO_CRX__COLOR &&
|
|
+ index == NV_CIO_CRE_44 && data == NV_CIO_CRE_44_HEADB)
|
|
+ bios->state.crtchead = 1;
|
|
+}
|
|
+
|
|
+static uint8_t
|
|
+bios_port_rd(struct nvbios *bios, uint16_t port)
|
|
+{
|
|
+ uint8_t data, head = bios->state.crtchead;
|
|
+
|
|
+ if (!valid_port(bios, port))
|
|
+ return 0;
|
|
+
|
|
+ data = NVReadPRMVIO(bios->dev, head, NV_PRMVIO0_OFFSET + port);
|
|
+
|
|
+ BIOSLOG(bios, " IO read: Port: 0x%04X, Head: 0x%02X, Data: 0x%02X\n",
|
|
+ port, head, data);
|
|
+
|
|
+ return data;
|
|
+}
|
|
+
|
|
+static void
|
|
+bios_port_wr(struct nvbios *bios, uint16_t port, uint8_t data)
|
|
+{
|
|
+ int head = bios->state.crtchead;
|
|
+
|
|
+ if (!valid_port(bios, port))
|
|
+ return;
|
|
+
|
|
+ LOG_OLD_VALUE(bios_port_rd(bios, port));
|
|
+ BIOSLOG(bios, " IO write: Port: 0x%04X, Head: 0x%02X, Data: 0x%02X\n",
|
|
+ port, head, data);
|
|
+
|
|
+ if (!bios->execute)
|
|
+ return;
|
|
+
|
|
+ still_alive();
|
|
+ NVWritePRMVIO(bios->dev, head, NV_PRMVIO0_OFFSET + port, data);
|
|
+}
|
|
+
|
|
+static bool
|
|
+io_flag_condition_met(struct nvbios *bios, uint16_t offset, uint8_t cond)
|
|
+{
|
|
+ /*
|
|
+ * The IO flag condition entry has 2 bytes for the CRTC port; 1 byte
|
|
+ * for the CRTC index; 1 byte for the mask to apply to the value
|
|
+ * retrieved from the CRTC; 1 byte for the shift right to apply to the
|
|
+ * masked CRTC value; 2 bytes for the offset to the flag array, to
|
|
+ * which the shifted value is added; 1 byte for the mask applied to the
|
|
+ * value read from the flag array; and 1 byte for the value to compare
|
|
+ * against the masked byte from the flag table.
|
|
+ */
|
|
+
|
|
+ uint16_t condptr = bios->io_flag_condition_tbl_ptr + cond * IO_FLAG_CONDITION_SIZE;
|
|
+ uint16_t crtcport = ROM16(bios->data[condptr]);
|
|
+ uint8_t crtcindex = bios->data[condptr + 2];
|
|
+ uint8_t mask = bios->data[condptr + 3];
|
|
+ uint8_t shift = bios->data[condptr + 4];
|
|
+ uint16_t flagarray = ROM16(bios->data[condptr + 5]);
|
|
+ uint8_t flagarraymask = bios->data[condptr + 7];
|
|
+ uint8_t cmpval = bios->data[condptr + 8];
|
|
+ uint8_t data;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X, "
|
|
+ "Shift: 0x%02X, FlagArray: 0x%04X, FAMask: 0x%02X, "
|
|
+ "Cmpval: 0x%02X\n",
|
|
+ offset, crtcport, crtcindex, mask, shift, flagarray, flagarraymask, cmpval);
|
|
+
|
|
+ data = bios_idxprt_rd(bios, crtcport, crtcindex);
|
|
+
|
|
+ data = bios->data[flagarray + ((data & mask) >> shift)];
|
|
+ data &= flagarraymask;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Checking if 0x%02X equals 0x%02X\n",
|
|
+ offset, data, cmpval);
|
|
+
|
|
+ return (data == cmpval);
|
|
+}
|
|
+
|
|
+static bool
|
|
+bios_condition_met(struct nvbios *bios, uint16_t offset, uint8_t cond)
|
|
+{
|
|
+ /*
|
|
+ * The condition table entry has 4 bytes for the address of the
|
|
+ * register to check, 4 bytes for a mask to apply to the register and
|
|
+ * 4 for a test comparison value
|
|
+ */
|
|
+
|
|
+ uint16_t condptr = bios->condition_tbl_ptr + cond * CONDITION_SIZE;
|
|
+ uint32_t reg = ROM32(bios->data[condptr]);
|
|
+ uint32_t mask = ROM32(bios->data[condptr + 4]);
|
|
+ uint32_t cmpval = ROM32(bios->data[condptr + 8]);
|
|
+ uint32_t data;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Cond: 0x%02X, Reg: 0x%08X, Mask: 0x%08X\n",
|
|
+ offset, cond, reg, mask);
|
|
+
|
|
+ data = bios_rd32(bios, reg) & mask;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Checking if 0x%08X equals 0x%08X\n",
|
|
+ offset, data, cmpval);
|
|
+
|
|
+ return (data == cmpval);
|
|
+}
|
|
+
|
|
+static bool
|
|
+io_condition_met(struct nvbios *bios, uint16_t offset, uint8_t cond)
|
|
+{
|
|
+ /*
|
|
+ * The IO condition entry has 2 bytes for the IO port address; 1 byte
|
|
+ * for the index to write to io_port; 1 byte for the mask to apply to
|
|
+ * the byte read from io_port+1; and 1 byte for the value to compare
|
|
+ * against the masked byte.
|
|
+ */
|
|
+
|
|
+ uint16_t condptr = bios->io_condition_tbl_ptr + cond * IO_CONDITION_SIZE;
|
|
+ uint16_t io_port = ROM16(bios->data[condptr]);
|
|
+ uint8_t port_index = bios->data[condptr + 2];
|
|
+ uint8_t mask = bios->data[condptr + 3];
|
|
+ uint8_t cmpval = bios->data[condptr + 4];
|
|
+
|
|
+ uint8_t data = bios_idxprt_rd(bios, io_port, port_index) & mask;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Checking if 0x%02X equals 0x%02X\n",
|
|
+ offset, data, cmpval);
|
|
+
|
|
+ return (data == cmpval);
|
|
+}
|
|
+
|
|
+static int
|
|
+nv50_pll_set(struct drm_device *dev, uint32_t reg, uint32_t clk)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t reg0 = nv_rd32(dev, reg + 0);
|
|
+ uint32_t reg1 = nv_rd32(dev, reg + 4);
|
|
+ struct nouveau_pll_vals pll;
|
|
+ struct pll_lims pll_limits;
|
|
+ int ret;
|
|
+
|
|
+ ret = get_pll_limits(dev, reg, &pll_limits);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ clk = nouveau_calc_pll_mnp(dev, &pll_limits, clk, &pll);
|
|
+ if (!clk)
|
|
+ return -ERANGE;
|
|
+
|
|
+ reg0 = (reg0 & 0xfff8ffff) | (pll.log2P << 16);
|
|
+ reg1 = (reg1 & 0xffff0000) | (pll.N1 << 8) | pll.M1;
|
|
+
|
|
+ if (dev_priv->VBIOS.execute) {
|
|
+ still_alive();
|
|
+ nv_wr32(dev, reg + 4, reg1);
|
|
+ nv_wr32(dev, reg + 0, reg0);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+setPLL(struct nvbios *bios, uint32_t reg, uint32_t clk)
|
|
+{
|
|
+ struct drm_device *dev = bios->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ /* clk in kHz */
|
|
+ struct pll_lims pll_lim;
|
|
+ struct nouveau_pll_vals pllvals;
|
|
+ int ret;
|
|
+
|
|
+ if (dev_priv->card_type >= NV_50)
|
|
+ return nv50_pll_set(dev, reg, clk);
|
|
+
|
|
+ /* high regs (such as in the mac g5 table) are not -= 4 */
|
|
+ ret = get_pll_limits(dev, reg > 0x405c ? reg : reg - 4, &pll_lim);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ clk = nouveau_calc_pll_mnp(dev, &pll_lim, clk, &pllvals);
|
|
+ if (!clk)
|
|
+ return -ERANGE;
|
|
+
|
|
+ if (bios->execute) {
|
|
+ still_alive();
|
|
+ nouveau_hw_setpll(dev, reg, &pllvals);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dcb_entry_idx_from_crtchead(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nvbios *bios = &dev_priv->VBIOS;
|
|
+
|
|
+ /*
|
|
+ * For the results of this function to be correct, CR44 must have been
|
|
+ * set (using bios_idxprt_wr to set crtchead), CR58 set for CR57 = 0,
|
|
+ * and the DCB table parsed, before the script calling the function is
|
|
+ * run. run_digital_op_script is example of how to do such setup
|
|
+ */
|
|
+
|
|
+ uint8_t dcb_entry = NVReadVgaCrtc5758(dev, bios->state.crtchead, 0);
|
|
+
|
|
+ if (dcb_entry > bios->bdcb.dcb.entries) {
|
|
+ NV_ERROR(dev, "CR58 doesn't have a valid DCB entry currently "
|
|
+ "(%02X)\n", dcb_entry);
|
|
+ dcb_entry = 0x7f; /* unused / invalid marker */
|
|
+ }
|
|
+
|
|
+ return dcb_entry;
|
|
+}
|
|
+
|
|
+static struct nouveau_i2c_chan *
|
|
+init_i2c_device_find(struct drm_device *dev, int i2c_index)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct bios_parsed_dcb *bdcb = &dev_priv->VBIOS.bdcb;
|
|
+
|
|
+ if (i2c_index == 0xff) {
|
|
+ /* note: dcb_entry_idx_from_crtchead needs pre-script set-up */
|
|
+ int idx = dcb_entry_idx_from_crtchead(dev), shift = 0;
|
|
+ int default_indices = bdcb->i2c_default_indices;
|
|
+
|
|
+ if (idx != 0x7f && bdcb->dcb.entry[idx].i2c_upper_default)
|
|
+ shift = 4;
|
|
+
|
|
+ i2c_index = (default_indices >> shift) & 0xf;
|
|
+ }
|
|
+ if (i2c_index == 0x80) /* g80+ */
|
|
+ i2c_index = bdcb->i2c_default_indices & 0xf;
|
|
+
|
|
+ return nouveau_i2c_find(dev, i2c_index);
|
|
+}
|
|
+
|
|
+static uint32_t get_tmds_index_reg(struct drm_device *dev, uint8_t mlv)
|
|
+{
|
|
+ /*
|
|
+ * For mlv < 0x80, it is an index into a table of TMDS base addresses.
|
|
+ * For mlv == 0x80 use the "or" value of the dcb_entry indexed by
|
|
+ * CR58 for CR57 = 0 to index a table of offsets to the basic
|
|
+ * 0x6808b0 address.
|
|
+ * For mlv == 0x81 use the "or" value of the dcb_entry indexed by
|
|
+ * CR58 for CR57 = 0 to index a table of offsets to the basic
|
|
+ * 0x6808b0 address, and then flip the offset by 8.
|
|
+ */
|
|
+
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ const int pramdac_offset[13] = {
|
|
+ 0, 0, 0x8, 0, 0x2000, 0, 0, 0, 0x2008, 0, 0, 0, 0x2000 };
|
|
+ const uint32_t pramdac_table[4] = {
|
|
+ 0x6808b0, 0x6808b8, 0x6828b0, 0x6828b8 };
|
|
+
|
|
+ if (mlv >= 0x80) {
|
|
+ int dcb_entry, dacoffset;
|
|
+
|
|
+ /* note: dcb_entry_idx_from_crtchead needs pre-script set-up */
|
|
+ dcb_entry = dcb_entry_idx_from_crtchead(dev);
|
|
+ if (dcb_entry == 0x7f)
|
|
+ return 0;
|
|
+ dacoffset = pramdac_offset[
|
|
+ dev_priv->VBIOS.bdcb.dcb.entry[dcb_entry].or];
|
|
+ if (mlv == 0x81)
|
|
+ dacoffset ^= 8;
|
|
+ return 0x6808b0 + dacoffset;
|
|
+ } else {
|
|
+ if (mlv > ARRAY_SIZE(pramdac_table)) {
|
|
+ NV_ERROR(dev, "Magic Lookup Value too big (%02X)\n",
|
|
+ mlv);
|
|
+ return 0;
|
|
+ }
|
|
+ return pramdac_table[mlv];
|
|
+ }
|
|
+}
|
|
+
|
|
+static int
|
|
+init_io_restrict_prog(struct nvbios *bios, uint16_t offset,
|
|
+ struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_IO_RESTRICT_PROG opcode: 0x32 ('2')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (16 bit): CRTC port
|
|
+ * offset + 3 (8 bit): CRTC index
|
|
+ * offset + 4 (8 bit): mask
|
|
+ * offset + 5 (8 bit): shift
|
|
+ * offset + 6 (8 bit): count
|
|
+ * offset + 7 (32 bit): register
|
|
+ * offset + 11 (32 bit): configuration 1
|
|
+ * ...
|
|
+ *
|
|
+ * Starting at offset + 11 there are "count" 32 bit values.
|
|
+ * To find out which value to use read index "CRTC index" on "CRTC
|
|
+ * port", AND this value with "mask" and then bit shift right "shift"
|
|
+ * bits. Read the appropriate value using this index and write to
|
|
+ * "register"
|
|
+ */
|
|
+
|
|
+ uint16_t crtcport = ROM16(bios->data[offset + 1]);
|
|
+ uint8_t crtcindex = bios->data[offset + 3];
|
|
+ uint8_t mask = bios->data[offset + 4];
|
|
+ uint8_t shift = bios->data[offset + 5];
|
|
+ uint8_t count = bios->data[offset + 6];
|
|
+ uint32_t reg = ROM32(bios->data[offset + 7]);
|
|
+ uint8_t config;
|
|
+ uint32_t configval;
|
|
+ int len = 11 + count * 4;
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return len;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X, "
|
|
+ "Shift: 0x%02X, Count: 0x%02X, Reg: 0x%08X\n",
|
|
+ offset, crtcport, crtcindex, mask, shift, count, reg);
|
|
+
|
|
+ config = (bios_idxprt_rd(bios, crtcport, crtcindex) & mask) >> shift;
|
|
+ if (config > count) {
|
|
+ NV_ERROR(bios->dev,
|
|
+ "0x%04X: Config 0x%02X exceeds maximal bound 0x%02X\n",
|
|
+ offset, config, count);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ configval = ROM32(bios->data[offset + 11 + config * 4]);
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Writing config %02X\n", offset, config);
|
|
+
|
|
+ bios_wr32(bios, reg, configval);
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_repeat(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_REPEAT opcode: 0x33 ('3')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (8 bit): count
|
|
+ *
|
|
+ * Execute script following this opcode up to INIT_REPEAT_END
|
|
+ * "count" times
|
|
+ */
|
|
+
|
|
+ uint8_t count = bios->data[offset + 1];
|
|
+ uint8_t i;
|
|
+
|
|
+ /* no iexec->execute check by design */
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Repeating following segment %d times\n",
|
|
+ offset, count);
|
|
+
|
|
+ iexec->repeat = true;
|
|
+
|
|
+ /*
|
|
+ * count - 1, as the script block will execute once when we leave this
|
|
+ * opcode -- this is compatible with bios behaviour as:
|
|
+ * a) the block is always executed at least once, even if count == 0
|
|
+ * b) the bios interpreter skips to the op following INIT_END_REPEAT,
|
|
+ * while we don't
|
|
+ */
|
|
+ for (i = 0; i < count - 1; i++)
|
|
+ parse_init_table(bios, offset + 2, iexec);
|
|
+
|
|
+ iexec->repeat = false;
|
|
+
|
|
+ return 2;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_io_restrict_pll(struct nvbios *bios, uint16_t offset,
|
|
+ struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_IO_RESTRICT_PLL opcode: 0x34 ('4')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (16 bit): CRTC port
|
|
+ * offset + 3 (8 bit): CRTC index
|
|
+ * offset + 4 (8 bit): mask
|
|
+ * offset + 5 (8 bit): shift
|
|
+ * offset + 6 (8 bit): IO flag condition index
|
|
+ * offset + 7 (8 bit): count
|
|
+ * offset + 8 (32 bit): register
|
|
+ * offset + 12 (16 bit): frequency 1
|
|
+ * ...
|
|
+ *
|
|
+ * Starting at offset + 12 there are "count" 16 bit frequencies (10kHz).
|
|
+ * Set PLL register "register" to coefficients for frequency n,
|
|
+ * selected by reading index "CRTC index" of "CRTC port" ANDed with
|
|
+ * "mask" and shifted right by "shift".
|
|
+ *
|
|
+ * If "IO flag condition index" > 0, and condition met, double
|
|
+ * frequency before setting it.
|
|
+ */
|
|
+
|
|
+ uint16_t crtcport = ROM16(bios->data[offset + 1]);
|
|
+ uint8_t crtcindex = bios->data[offset + 3];
|
|
+ uint8_t mask = bios->data[offset + 4];
|
|
+ uint8_t shift = bios->data[offset + 5];
|
|
+ int8_t io_flag_condition_idx = bios->data[offset + 6];
|
|
+ uint8_t count = bios->data[offset + 7];
|
|
+ uint32_t reg = ROM32(bios->data[offset + 8]);
|
|
+ uint8_t config;
|
|
+ uint16_t freq;
|
|
+ int len = 12 + count * 2;
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return len;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X, "
|
|
+ "Shift: 0x%02X, IO Flag Condition: 0x%02X, "
|
|
+ "Count: 0x%02X, Reg: 0x%08X\n",
|
|
+ offset, crtcport, crtcindex, mask, shift,
|
|
+ io_flag_condition_idx, count, reg);
|
|
+
|
|
+ config = (bios_idxprt_rd(bios, crtcport, crtcindex) & mask) >> shift;
|
|
+ if (config > count) {
|
|
+ NV_ERROR(bios->dev,
|
|
+ "0x%04X: Config 0x%02X exceeds maximal bound 0x%02X\n",
|
|
+ offset, config, count);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ freq = ROM16(bios->data[offset + 12 + config * 2]);
|
|
+
|
|
+ if (io_flag_condition_idx > 0) {
|
|
+ if (io_flag_condition_met(bios, offset, io_flag_condition_idx)) {
|
|
+ BIOSLOG(bios, "0x%04X: Condition fulfilled -- "
|
|
+ "frequency doubled\n", offset);
|
|
+ freq *= 2;
|
|
+ } else
|
|
+ BIOSLOG(bios, "0x%04X: Condition not fulfilled -- "
|
|
+ "frequency unchanged\n", offset);
|
|
+ }
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Config: 0x%02X, Freq: %d0kHz\n",
|
|
+ offset, reg, config, freq);
|
|
+
|
|
+ setPLL(bios, reg, freq * 10);
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_end_repeat(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_END_REPEAT opcode: 0x36 ('6')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ *
|
|
+ * Marks the end of the block for INIT_REPEAT to repeat
|
|
+ */
|
|
+
|
|
+ /* no iexec->execute check by design */
|
|
+
|
|
+ /*
|
|
+ * iexec->repeat flag necessary to go past INIT_END_REPEAT opcode when
|
|
+ * we're not in repeat mode
|
|
+ */
|
|
+ if (iexec->repeat)
|
|
+ return 0;
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_copy(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_COPY opcode: 0x37 ('7')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (32 bit): register
|
|
+ * offset + 5 (8 bit): shift
|
|
+ * offset + 6 (8 bit): srcmask
|
|
+ * offset + 7 (16 bit): CRTC port
|
|
+ * offset + 9 (8 bit): CRTC index
|
|
+ * offset + 10 (8 bit): mask
|
|
+ *
|
|
+ * Read index "CRTC index" on "CRTC port", AND with "mask", OR with
|
|
+ * (REGVAL("register") >> "shift" & "srcmask") and write-back to CRTC
|
|
+ * port
|
|
+ */
|
|
+
|
|
+ uint32_t reg = ROM32(bios->data[offset + 1]);
|
|
+ uint8_t shift = bios->data[offset + 5];
|
|
+ uint8_t srcmask = bios->data[offset + 6];
|
|
+ uint16_t crtcport = ROM16(bios->data[offset + 7]);
|
|
+ uint8_t crtcindex = bios->data[offset + 9];
|
|
+ uint8_t mask = bios->data[offset + 10];
|
|
+ uint32_t data;
|
|
+ uint8_t crtcdata;
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return 11;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Shift: 0x%02X, SrcMask: 0x%02X, "
|
|
+ "Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X\n",
|
|
+ offset, reg, shift, srcmask, crtcport, crtcindex, mask);
|
|
+
|
|
+ data = bios_rd32(bios, reg);
|
|
+
|
|
+ if (shift < 0x80)
|
|
+ data >>= shift;
|
|
+ else
|
|
+ data <<= (0x100 - shift);
|
|
+
|
|
+ data &= srcmask;
|
|
+
|
|
+ crtcdata = bios_idxprt_rd(bios, crtcport, crtcindex) & mask;
|
|
+ crtcdata |= (uint8_t)data;
|
|
+ bios_idxprt_wr(bios, crtcport, crtcindex, crtcdata);
|
|
+
|
|
+ return 11;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_not(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_NOT opcode: 0x38 ('8')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ *
|
|
+ * Invert the current execute / no-execute condition (i.e. "else")
|
|
+ */
|
|
+ if (iexec->execute)
|
|
+ BIOSLOG(bios, "0x%04X: ------ Skipping following commands ------\n", offset);
|
|
+ else
|
|
+ BIOSLOG(bios, "0x%04X: ------ Executing following commands ------\n", offset);
|
|
+
|
|
+ iexec->execute = !iexec->execute;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_io_flag_condition(struct nvbios *bios, uint16_t offset,
|
|
+ struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_IO_FLAG_CONDITION opcode: 0x39 ('9')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (8 bit): condition number
|
|
+ *
|
|
+ * Check condition "condition number" in the IO flag condition table.
|
|
+ * If condition not met skip subsequent opcodes until condition is
|
|
+ * inverted (INIT_NOT), or we hit INIT_RESUME
|
|
+ */
|
|
+
|
|
+ uint8_t cond = bios->data[offset + 1];
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return 2;
|
|
+
|
|
+ if (io_flag_condition_met(bios, offset, cond))
|
|
+ BIOSLOG(bios, "0x%04X: Condition fulfilled -- continuing to execute\n", offset);
|
|
+ else {
|
|
+ BIOSLOG(bios, "0x%04X: Condition not fulfilled -- skipping following commands\n", offset);
|
|
+ iexec->execute = false;
|
|
+ }
|
|
+
|
|
+ return 2;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_idx_addr_latched(struct nvbios *bios, uint16_t offset,
|
|
+ struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_INDEX_ADDRESS_LATCHED opcode: 0x49 ('I')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (32 bit): control register
|
|
+ * offset + 5 (32 bit): data register
|
|
+ * offset + 9 (32 bit): mask
|
|
+ * offset + 13 (32 bit): data
|
|
+ * offset + 17 (8 bit): count
|
|
+ * offset + 18 (8 bit): address 1
|
|
+ * offset + 19 (8 bit): data 1
|
|
+ * ...
|
|
+ *
|
|
+ * For each of "count" address and data pairs, write "data n" to
|
|
+ * "data register", read the current value of "control register",
|
|
+ * and write it back once ANDed with "mask", ORed with "data",
|
|
+ * and ORed with "address n"
|
|
+ */
|
|
+
|
|
+ uint32_t controlreg = ROM32(bios->data[offset + 1]);
|
|
+ uint32_t datareg = ROM32(bios->data[offset + 5]);
|
|
+ uint32_t mask = ROM32(bios->data[offset + 9]);
|
|
+ uint32_t data = ROM32(bios->data[offset + 13]);
|
|
+ uint8_t count = bios->data[offset + 17];
|
|
+ int len = 18 + count * 2;
|
|
+ uint32_t value;
|
|
+ int i;
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return len;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: ControlReg: 0x%08X, DataReg: 0x%08X, "
|
|
+ "Mask: 0x%08X, Data: 0x%08X, Count: 0x%02X\n",
|
|
+ offset, controlreg, datareg, mask, data, count);
|
|
+
|
|
+ for (i = 0; i < count; i++) {
|
|
+ uint8_t instaddress = bios->data[offset + 18 + i * 2];
|
|
+ uint8_t instdata = bios->data[offset + 19 + i * 2];
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Address: 0x%02X, Data: 0x%02X\n",
|
|
+ offset, instaddress, instdata);
|
|
+
|
|
+ bios_wr32(bios, datareg, instdata);
|
|
+ value = bios_rd32(bios, controlreg) & mask;
|
|
+ value |= data;
|
|
+ value |= instaddress;
|
|
+ bios_wr32(bios, controlreg, value);
|
|
+ }
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_io_restrict_pll2(struct nvbios *bios, uint16_t offset,
|
|
+ struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_IO_RESTRICT_PLL2 opcode: 0x4A ('J')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (16 bit): CRTC port
|
|
+ * offset + 3 (8 bit): CRTC index
|
|
+ * offset + 4 (8 bit): mask
|
|
+ * offset + 5 (8 bit): shift
|
|
+ * offset + 6 (8 bit): count
|
|
+ * offset + 7 (32 bit): register
|
|
+ * offset + 11 (32 bit): frequency 1
|
|
+ * ...
|
|
+ *
|
|
+ * Starting at offset + 11 there are "count" 32 bit frequencies (kHz).
|
|
+ * Set PLL register "register" to coefficients for frequency n,
|
|
+ * selected by reading index "CRTC index" of "CRTC port" ANDed with
|
|
+ * "mask" and shifted right by "shift".
|
|
+ */
|
|
+
|
|
+ uint16_t crtcport = ROM16(bios->data[offset + 1]);
|
|
+ uint8_t crtcindex = bios->data[offset + 3];
|
|
+ uint8_t mask = bios->data[offset + 4];
|
|
+ uint8_t shift = bios->data[offset + 5];
|
|
+ uint8_t count = bios->data[offset + 6];
|
|
+ uint32_t reg = ROM32(bios->data[offset + 7]);
|
|
+ int len = 11 + count * 4;
|
|
+ uint8_t config;
|
|
+ uint32_t freq;
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return len;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X, "
|
|
+ "Shift: 0x%02X, Count: 0x%02X, Reg: 0x%08X\n",
|
|
+ offset, crtcport, crtcindex, mask, shift, count, reg);
|
|
+
|
|
+ if (!reg)
|
|
+ return len;
|
|
+
|
|
+ config = (bios_idxprt_rd(bios, crtcport, crtcindex) & mask) >> shift;
|
|
+ if (config > count) {
|
|
+ NV_ERROR(bios->dev,
|
|
+ "0x%04X: Config 0x%02X exceeds maximal bound 0x%02X\n",
|
|
+ offset, config, count);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ freq = ROM32(bios->data[offset + 11 + config * 4]);
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Config: 0x%02X, Freq: %dkHz\n",
|
|
+ offset, reg, config, freq);
|
|
+
|
|
+ setPLL(bios, reg, freq);
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_pll2(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_PLL2 opcode: 0x4B ('K')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (32 bit): register
|
|
+ * offset + 5 (32 bit): freq
|
|
+ *
|
|
+ * Set PLL register "register" to coefficients for frequency "freq"
|
|
+ */
|
|
+
|
|
+ uint32_t reg = ROM32(bios->data[offset + 1]);
|
|
+ uint32_t freq = ROM32(bios->data[offset + 5]);
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return 9;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Reg: 0x%04X, Freq: %dkHz\n",
|
|
+ offset, reg, freq);
|
|
+
|
|
+ setPLL(bios, reg, freq);
|
|
+ return 9;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_I2C_BYTE opcode: 0x4C ('L')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (8 bit): DCB I2C table entry index
|
|
+ * offset + 2 (8 bit): I2C slave address
|
|
+ * offset + 3 (8 bit): count
|
|
+ * offset + 4 (8 bit): I2C register 1
|
|
+ * offset + 5 (8 bit): mask 1
|
|
+ * offset + 6 (8 bit): data 1
|
|
+ * ...
|
|
+ *
|
|
+ * For each of "count" registers given by "I2C register n" on the device
|
|
+ * addressed by "I2C slave address" on the I2C bus given by
|
|
+ * "DCB I2C table entry index", read the register, AND the result with
|
|
+ * "mask n" and OR it with "data n" before writing it back to the device
|
|
+ */
|
|
+
|
|
+ uint8_t i2c_index = bios->data[offset + 1];
|
|
+ uint8_t i2c_address = bios->data[offset + 2];
|
|
+ uint8_t count = bios->data[offset + 3];
|
|
+ int len = 4 + count * 3;
|
|
+ struct nouveau_i2c_chan *chan;
|
|
+ struct i2c_msg msg;
|
|
+ int i;
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return len;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: DCBI2CIndex: 0x%02X, I2CAddress: 0x%02X, "
|
|
+ "Count: 0x%02X\n",
|
|
+ offset, i2c_index, i2c_address, count);
|
|
+
|
|
+ chan = init_i2c_device_find(bios->dev, i2c_index);
|
|
+ if (!chan)
|
|
+ return 0;
|
|
+
|
|
+ for (i = 0; i < count; i++) {
|
|
+ uint8_t i2c_reg = bios->data[offset + 4 + i * 3];
|
|
+ uint8_t mask = bios->data[offset + 5 + i * 3];
|
|
+ uint8_t data = bios->data[offset + 6 + i * 3];
|
|
+ uint8_t value;
|
|
+
|
|
+ msg.addr = i2c_address;
|
|
+ msg.flags = I2C_M_RD;
|
|
+ msg.len = 1;
|
|
+ msg.buf = &value;
|
|
+ if (i2c_transfer(&chan->adapter, &msg, 1) != 1)
|
|
+ return 0;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X, Value: 0x%02X, "
|
|
+ "Mask: 0x%02X, Data: 0x%02X\n",
|
|
+ offset, i2c_reg, value, mask, data);
|
|
+
|
|
+ value = (value & mask) | data;
|
|
+
|
|
+ if (bios->execute) {
|
|
+ msg.addr = i2c_address;
|
|
+ msg.flags = 0;
|
|
+ msg.len = 1;
|
|
+ msg.buf = &value;
|
|
+ if (i2c_transfer(&chan->adapter, &msg, 1) != 1)
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_zm_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_ZM_I2C_BYTE opcode: 0x4D ('M')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (8 bit): DCB I2C table entry index
|
|
+ * offset + 2 (8 bit): I2C slave address
|
|
+ * offset + 3 (8 bit): count
|
|
+ * offset + 4 (8 bit): I2C register 1
|
|
+ * offset + 5 (8 bit): data 1
|
|
+ * ...
|
|
+ *
|
|
+ * For each of "count" registers given by "I2C register n" on the device
|
|
+ * addressed by "I2C slave address" on the I2C bus given by
|
|
+ * "DCB I2C table entry index", set the register to "data n"
|
|
+ */
|
|
+
|
|
+ uint8_t i2c_index = bios->data[offset + 1];
|
|
+ uint8_t i2c_address = bios->data[offset + 2];
|
|
+ uint8_t count = bios->data[offset + 3];
|
|
+ int len = 4 + count * 2;
|
|
+ struct nouveau_i2c_chan *chan;
|
|
+ struct i2c_msg msg;
|
|
+ int i;
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return len;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: DCBI2CIndex: 0x%02X, I2CAddress: 0x%02X, "
|
|
+ "Count: 0x%02X\n",
|
|
+ offset, i2c_index, i2c_address, count);
|
|
+
|
|
+ chan = init_i2c_device_find(bios->dev, i2c_index);
|
|
+ if (!chan)
|
|
+ return 0;
|
|
+
|
|
+ for (i = 0; i < count; i++) {
|
|
+ uint8_t i2c_reg = bios->data[offset + 4 + i * 2];
|
|
+ uint8_t data = bios->data[offset + 5 + i * 2];
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X, Data: 0x%02X\n",
|
|
+ offset, i2c_reg, data);
|
|
+
|
|
+ if (bios->execute) {
|
|
+ msg.addr = i2c_address;
|
|
+ msg.flags = 0;
|
|
+ msg.len = 1;
|
|
+ msg.buf = &data;
|
|
+ if (i2c_transfer(&chan->adapter, &msg, 1) != 1)
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_zm_i2c(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_ZM_I2C opcode: 0x4E ('N')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (8 bit): DCB I2C table entry index
|
|
+ * offset + 2 (8 bit): I2C slave address
|
|
+ * offset + 3 (8 bit): count
|
|
+ * offset + 4 (8 bit): data 1
|
|
+ * ...
|
|
+ *
|
|
+ * Send "count" bytes ("data n") to the device addressed by "I2C slave
|
|
+ * address" on the I2C bus given by "DCB I2C table entry index"
|
|
+ */
|
|
+
|
|
+ uint8_t i2c_index = bios->data[offset + 1];
|
|
+ uint8_t i2c_address = bios->data[offset + 2];
|
|
+ uint8_t count = bios->data[offset + 3];
|
|
+ int len = 4 + count;
|
|
+ struct nouveau_i2c_chan *chan;
|
|
+ struct i2c_msg msg;
|
|
+ uint8_t data[256];
|
|
+ int i;
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return len;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: DCBI2CIndex: 0x%02X, I2CAddress: 0x%02X, "
|
|
+ "Count: 0x%02X\n",
|
|
+ offset, i2c_index, i2c_address, count);
|
|
+
|
|
+ chan = init_i2c_device_find(bios->dev, i2c_index);
|
|
+ if (!chan)
|
|
+ return 0;
|
|
+
|
|
+ for (i = 0; i < count; i++) {
|
|
+ data[i] = bios->data[offset + 4 + i];
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Data: 0x%02X\n", offset, data[i]);
|
|
+ }
|
|
+
|
|
+ if (bios->execute) {
|
|
+ msg.addr = i2c_address;
|
|
+ msg.flags = 0;
|
|
+ msg.len = count;
|
|
+ msg.buf = data;
|
|
+ if (i2c_transfer(&chan->adapter, &msg, 1) != 1)
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_tmds(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_TMDS opcode: 0x4F ('O') (non-canon name)
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (8 bit): magic lookup value
|
|
+ * offset + 2 (8 bit): TMDS address
|
|
+ * offset + 3 (8 bit): mask
|
|
+ * offset + 4 (8 bit): data
|
|
+ *
|
|
+ * Read the data reg for TMDS address "TMDS address", AND it with mask
|
|
+ * and OR it with data, then write it back
|
|
+ * "magic lookup value" determines which TMDS base address register is
|
|
+ * used -- see get_tmds_index_reg()
|
|
+ */
|
|
+
|
|
+ uint8_t mlv = bios->data[offset + 1];
|
|
+ uint32_t tmdsaddr = bios->data[offset + 2];
|
|
+ uint8_t mask = bios->data[offset + 3];
|
|
+ uint8_t data = bios->data[offset + 4];
|
|
+ uint32_t reg, value;
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return 5;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: MagicLookupValue: 0x%02X, TMDSAddr: 0x%02X, "
|
|
+ "Mask: 0x%02X, Data: 0x%02X\n",
|
|
+ offset, mlv, tmdsaddr, mask, data);
|
|
+
|
|
+ reg = get_tmds_index_reg(bios->dev, mlv);
|
|
+ if (!reg)
|
|
+ return 0;
|
|
+
|
|
+ bios_wr32(bios, reg,
|
|
+ tmdsaddr | NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE);
|
|
+ value = (bios_rd32(bios, reg + 4) & mask) | data;
|
|
+ bios_wr32(bios, reg + 4, value);
|
|
+ bios_wr32(bios, reg, tmdsaddr);
|
|
+
|
|
+ return 5;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_zm_tmds_group(struct nvbios *bios, uint16_t offset,
|
|
+ struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_ZM_TMDS_GROUP opcode: 0x50 ('P') (non-canon name)
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (8 bit): magic lookup value
|
|
+ * offset + 2 (8 bit): count
|
|
+ * offset + 3 (8 bit): addr 1
|
|
+ * offset + 4 (8 bit): data 1
|
|
+ * ...
|
|
+ *
|
|
+ * For each of "count" TMDS address and data pairs write "data n" to
|
|
+ * "addr n". "magic lookup value" determines which TMDS base address
|
|
+ * register is used -- see get_tmds_index_reg()
|
|
+ */
|
|
+
|
|
+ uint8_t mlv = bios->data[offset + 1];
|
|
+ uint8_t count = bios->data[offset + 2];
|
|
+ int len = 3 + count * 2;
|
|
+ uint32_t reg;
|
|
+ int i;
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return len;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: MagicLookupValue: 0x%02X, Count: 0x%02X\n",
|
|
+ offset, mlv, count);
|
|
+
|
|
+ reg = get_tmds_index_reg(bios->dev, mlv);
|
|
+ if (!reg)
|
|
+ return 0;
|
|
+
|
|
+ for (i = 0; i < count; i++) {
|
|
+ uint8_t tmdsaddr = bios->data[offset + 3 + i * 2];
|
|
+ uint8_t tmdsdata = bios->data[offset + 4 + i * 2];
|
|
+
|
|
+ bios_wr32(bios, reg + 4, tmdsdata);
|
|
+ bios_wr32(bios, reg, tmdsaddr);
|
|
+ }
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_cr_idx_adr_latch(struct nvbios *bios, uint16_t offset,
|
|
+ struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_CR_INDEX_ADDRESS_LATCHED opcode: 0x51 ('Q')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (8 bit): CRTC index1
|
|
+ * offset + 2 (8 bit): CRTC index2
|
|
+ * offset + 3 (8 bit): baseaddr
|
|
+ * offset + 4 (8 bit): count
|
|
+ * offset + 5 (8 bit): data 1
|
|
+ * ...
|
|
+ *
|
|
+ * For each of "count" address and data pairs, write "baseaddr + n" to
|
|
+ * "CRTC index1" and "data n" to "CRTC index2"
|
|
+ * Once complete, restore initial value read from "CRTC index1"
|
|
+ */
|
|
+ uint8_t crtcindex1 = bios->data[offset + 1];
|
|
+ uint8_t crtcindex2 = bios->data[offset + 2];
|
|
+ uint8_t baseaddr = bios->data[offset + 3];
|
|
+ uint8_t count = bios->data[offset + 4];
|
|
+ int len = 5 + count;
|
|
+ uint8_t oldaddr, data;
|
|
+ int i;
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return len;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Index1: 0x%02X, Index2: 0x%02X, "
|
|
+ "BaseAddr: 0x%02X, Count: 0x%02X\n",
|
|
+ offset, crtcindex1, crtcindex2, baseaddr, count);
|
|
+
|
|
+ oldaddr = bios_idxprt_rd(bios, NV_CIO_CRX__COLOR, crtcindex1);
|
|
+
|
|
+ for (i = 0; i < count; i++) {
|
|
+ bios_idxprt_wr(bios, NV_CIO_CRX__COLOR, crtcindex1,
|
|
+ baseaddr + i);
|
|
+ data = bios->data[offset + 5 + i];
|
|
+ bios_idxprt_wr(bios, NV_CIO_CRX__COLOR, crtcindex2, data);
|
|
+ }
|
|
+
|
|
+ bios_idxprt_wr(bios, NV_CIO_CRX__COLOR, crtcindex1, oldaddr);
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_cr(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_CR opcode: 0x52 ('R')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (8 bit): CRTC index
|
|
+ * offset + 2 (8 bit): mask
|
|
+ * offset + 3 (8 bit): data
|
|
+ *
|
|
+ * Assign the value of at "CRTC index" ANDed with mask and ORed with
|
|
+ * data back to "CRTC index"
|
|
+ */
|
|
+
|
|
+ uint8_t crtcindex = bios->data[offset + 1];
|
|
+ uint8_t mask = bios->data[offset + 2];
|
|
+ uint8_t data = bios->data[offset + 3];
|
|
+ uint8_t value;
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return 4;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Index: 0x%02X, Mask: 0x%02X, Data: 0x%02X\n",
|
|
+ offset, crtcindex, mask, data);
|
|
+
|
|
+ value = bios_idxprt_rd(bios, NV_CIO_CRX__COLOR, crtcindex) & mask;
|
|
+ value |= data;
|
|
+ bios_idxprt_wr(bios, NV_CIO_CRX__COLOR, crtcindex, value);
|
|
+
|
|
+ return 4;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_zm_cr(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_ZM_CR opcode: 0x53 ('S')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (8 bit): CRTC index
|
|
+ * offset + 2 (8 bit): value
|
|
+ *
|
|
+ * Assign "value" to CRTC register with index "CRTC index".
|
|
+ */
|
|
+
|
|
+ uint8_t crtcindex = ROM32(bios->data[offset + 1]);
|
|
+ uint8_t data = bios->data[offset + 2];
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return 3;
|
|
+
|
|
+ bios_idxprt_wr(bios, NV_CIO_CRX__COLOR, crtcindex, data);
|
|
+
|
|
+ return 3;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_zm_cr_group(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_ZM_CR_GROUP opcode: 0x54 ('T')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (8 bit): count
|
|
+ * offset + 2 (8 bit): CRTC index 1
|
|
+ * offset + 3 (8 bit): value 1
|
|
+ * ...
|
|
+ *
|
|
+ * For "count", assign "value n" to CRTC register with index
|
|
+ * "CRTC index n".
|
|
+ */
|
|
+
|
|
+ uint8_t count = bios->data[offset + 1];
|
|
+ int len = 2 + count * 2;
|
|
+ int i;
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return len;
|
|
+
|
|
+ for (i = 0; i < count; i++)
|
|
+ init_zm_cr(bios, offset + 2 + 2 * i - 1, iexec);
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_condition_time(struct nvbios *bios, uint16_t offset,
|
|
+ struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_CONDITION_TIME opcode: 0x56 ('V')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (8 bit): condition number
|
|
+ * offset + 2 (8 bit): retries / 50
|
|
+ *
|
|
+ * Check condition "condition number" in the condition table.
|
|
+ * Bios code then sleeps for 2ms if the condition is not met, and
|
|
+ * repeats up to "retries" times, but on one C51 this has proved
|
|
+ * insufficient. In mmiotraces the driver sleeps for 20ms, so we do
|
|
+ * this, and bail after "retries" times, or 2s, whichever is less.
|
|
+ * If still not met after retries, clear execution flag for this table.
|
|
+ */
|
|
+
|
|
+ uint8_t cond = bios->data[offset + 1];
|
|
+ uint16_t retries = bios->data[offset + 2] * 50;
|
|
+ unsigned cnt;
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return 3;
|
|
+
|
|
+ if (retries > 100)
|
|
+ retries = 100;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Condition: 0x%02X, Retries: 0x%02X\n",
|
|
+ offset, cond, retries);
|
|
+
|
|
+ if (!bios->execute) /* avoid 2s delays when "faking" execution */
|
|
+ retries = 1;
|
|
+
|
|
+ for (cnt = 0; cnt < retries; cnt++) {
|
|
+ if (bios_condition_met(bios, offset, cond)) {
|
|
+ BIOSLOG(bios, "0x%04X: Condition met, continuing\n",
|
|
+ offset);
|
|
+ break;
|
|
+ } else {
|
|
+ BIOSLOG(bios, "0x%04X: "
|
|
+ "Condition not met, sleeping for 20ms\n",
|
|
+ offset);
|
|
+ msleep(20);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!bios_condition_met(bios, offset, cond)) {
|
|
+ NV_WARN(bios->dev,
|
|
+ "0x%04X: Condition still not met after %dms, "
|
|
+ "skipping following opcodes\n", offset, 20 * retries);
|
|
+ iexec->execute = false;
|
|
+ }
|
|
+
|
|
+ return 3;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_zm_reg_sequence(struct nvbios *bios, uint16_t offset,
|
|
+ struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_ZM_REG_SEQUENCE opcode: 0x58 ('X')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (32 bit): base register
|
|
+ * offset + 5 (8 bit): count
|
|
+ * offset + 6 (32 bit): value 1
|
|
+ * ...
|
|
+ *
|
|
+ * Starting at offset + 6 there are "count" 32 bit values.
|
|
+ * For "count" iterations set "base register" + 4 * current_iteration
|
|
+ * to "value current_iteration"
|
|
+ */
|
|
+
|
|
+ uint32_t basereg = ROM32(bios->data[offset + 1]);
|
|
+ uint32_t count = bios->data[offset + 5];
|
|
+ int len = 6 + count * 4;
|
|
+ int i;
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return len;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: BaseReg: 0x%08X, Count: 0x%02X\n",
|
|
+ offset, basereg, count);
|
|
+
|
|
+ for (i = 0; i < count; i++) {
|
|
+ uint32_t reg = basereg + i * 4;
|
|
+ uint32_t data = ROM32(bios->data[offset + 6 + i * 4]);
|
|
+
|
|
+ bios_wr32(bios, reg, data);
|
|
+ }
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_sub_direct(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_SUB_DIRECT opcode: 0x5B ('[')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (16 bit): subroutine offset (in bios)
|
|
+ *
|
|
+ * Calls a subroutine that will execute commands until INIT_DONE
|
|
+ * is found.
|
|
+ */
|
|
+
|
|
+ uint16_t sub_offset = ROM16(bios->data[offset + 1]);
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return 3;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Executing subroutine at 0x%04X\n",
|
|
+ offset, sub_offset);
|
|
+
|
|
+ parse_init_table(bios, sub_offset, iexec);
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: End of 0x%04X subroutine\n", offset, sub_offset);
|
|
+
|
|
+ return 3;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_copy_nv_reg(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_COPY_NV_REG opcode: 0x5F ('_')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (32 bit): src reg
|
|
+ * offset + 5 (8 bit): shift
|
|
+ * offset + 6 (32 bit): src mask
|
|
+ * offset + 10 (32 bit): xor
|
|
+ * offset + 14 (32 bit): dst reg
|
|
+ * offset + 18 (32 bit): dst mask
|
|
+ *
|
|
+ * Shift REGVAL("src reg") right by (signed) "shift", AND result with
|
|
+ * "src mask", then XOR with "xor". Write this OR'd with
|
|
+ * (REGVAL("dst reg") AND'd with "dst mask") to "dst reg"
|
|
+ */
|
|
+
|
|
+ uint32_t srcreg = *((uint32_t *)(&bios->data[offset + 1]));
|
|
+ uint8_t shift = bios->data[offset + 5];
|
|
+ uint32_t srcmask = *((uint32_t *)(&bios->data[offset + 6]));
|
|
+ uint32_t xor = *((uint32_t *)(&bios->data[offset + 10]));
|
|
+ uint32_t dstreg = *((uint32_t *)(&bios->data[offset + 14]));
|
|
+ uint32_t dstmask = *((uint32_t *)(&bios->data[offset + 18]));
|
|
+ uint32_t srcvalue, dstvalue;
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return 22;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: SrcReg: 0x%08X, Shift: 0x%02X, SrcMask: 0x%08X, "
|
|
+ "Xor: 0x%08X, DstReg: 0x%08X, DstMask: 0x%08X\n",
|
|
+ offset, srcreg, shift, srcmask, xor, dstreg, dstmask);
|
|
+
|
|
+ srcvalue = bios_rd32(bios, srcreg);
|
|
+
|
|
+ if (shift < 0x80)
|
|
+ srcvalue >>= shift;
|
|
+ else
|
|
+ srcvalue <<= (0x100 - shift);
|
|
+
|
|
+ srcvalue = (srcvalue & srcmask) ^ xor;
|
|
+
|
|
+ dstvalue = bios_rd32(bios, dstreg) & dstmask;
|
|
+
|
|
+ bios_wr32(bios, dstreg, dstvalue | srcvalue);
|
|
+
|
|
+ return 22;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_zm_index_io(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_ZM_INDEX_IO opcode: 0x62 ('b')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (16 bit): CRTC port
|
|
+ * offset + 3 (8 bit): CRTC index
|
|
+ * offset + 4 (8 bit): data
|
|
+ *
|
|
+ * Write "data" to index "CRTC index" of "CRTC port"
|
|
+ */
|
|
+ uint16_t crtcport = ROM16(bios->data[offset + 1]);
|
|
+ uint8_t crtcindex = bios->data[offset + 3];
|
|
+ uint8_t data = bios->data[offset + 4];
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return 5;
|
|
+
|
|
+ bios_idxprt_wr(bios, crtcport, crtcindex, data);
|
|
+
|
|
+ return 5;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_compute_mem(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_COMPUTE_MEM opcode: 0x63 ('c')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ *
|
|
+ * This opcode is meant to set NV_PFB_CFG0 (0x100200) appropriately so
|
|
+ * that the hardware can correctly calculate how much VRAM it has
|
|
+ * (and subsequently report that value in NV_PFB_CSTATUS (0x10020C))
|
|
+ *
|
|
+ * The implementation of this opcode in general consists of two parts:
|
|
+ * 1) determination of the memory bus width
|
|
+ * 2) determination of how many of the card's RAM pads have ICs attached
|
|
+ *
|
|
+ * 1) is done by a cunning combination of writes to offsets 0x1c and
|
|
+ * 0x3c in the framebuffer, and seeing whether the written values are
|
|
+ * read back correctly. This then affects bits 4-7 of NV_PFB_CFG0
|
|
+ *
|
|
+ * 2) is done by a cunning combination of writes to an offset slightly
|
|
+ * less than the maximum memory reported by NV_PFB_CSTATUS, then seeing
|
|
+ * if the test pattern can be read back. This then affects bits 12-15 of
|
|
+ * NV_PFB_CFG0
|
|
+ *
|
|
+ * In this context a "cunning combination" may include multiple reads
|
|
+ * and writes to varying locations, often alternating the test pattern
|
|
+ * and 0, doubtless to make sure buffers are filled, residual charges
|
|
+ * on tracks are removed etc.
|
|
+ *
|
|
+ * Unfortunately, the "cunning combination"s mentioned above, and the
|
|
+ * changes to the bits in NV_PFB_CFG0 differ with nearly every bios
|
|
+ * trace I have.
|
|
+ *
|
|
+ * Therefore, we cheat and assume the value of NV_PFB_CFG0 with which
|
|
+ * we started was correct, and use that instead
|
|
+ */
|
|
+
|
|
+ /* no iexec->execute check by design */
|
|
+
|
|
+ /*
|
|
+ * This appears to be a NOP on G8x chipsets, both io logs of the VBIOS
|
|
+ * and kmmio traces of the binary driver POSTing the card show nothing
|
|
+ * being done for this opcode. why is it still listed in the table?!
|
|
+ */
|
|
+
|
|
+ struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
|
|
+
|
|
+ if (dev_priv->card_type >= NV_40)
|
|
+ return 1;
|
|
+
|
|
+ /*
|
|
+ * On every card I've seen, this step gets done for us earlier in
|
|
+ * the init scripts
|
|
+ uint8_t crdata = bios_idxprt_rd(dev, NV_VIO_SRX, 0x01);
|
|
+ bios_idxprt_wr(dev, NV_VIO_SRX, 0x01, crdata | 0x20);
|
|
+ */
|
|
+
|
|
+ /*
|
|
+ * This also has probably been done in the scripts, but an mmio trace of
|
|
+ * s3 resume shows nvidia doing it anyway (unlike the NV_VIO_SRX write)
|
|
+ */
|
|
+ bios_wr32(bios, NV_PFB_REFCTRL, NV_PFB_REFCTRL_VALID_1);
|
|
+
|
|
+ /* write back the saved configuration value */
|
|
+ bios_wr32(bios, NV_PFB_CFG0, bios->state.saved_nv_pfb_cfg0);
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_reset(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_RESET opcode: 0x65 ('e')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (32 bit): register
|
|
+ * offset + 5 (32 bit): value1
|
|
+ * offset + 9 (32 bit): value2
|
|
+ *
|
|
+ * Assign "value1" to "register", then assign "value2" to "register"
|
|
+ */
|
|
+
|
|
+ uint32_t reg = ROM32(bios->data[offset + 1]);
|
|
+ uint32_t value1 = ROM32(bios->data[offset + 5]);
|
|
+ uint32_t value2 = ROM32(bios->data[offset + 9]);
|
|
+ uint32_t pci_nv_19, pci_nv_20;
|
|
+
|
|
+ /* no iexec->execute check by design */
|
|
+
|
|
+ pci_nv_19 = bios_rd32(bios, NV_PBUS_PCI_NV_19);
|
|
+ bios_wr32(bios, NV_PBUS_PCI_NV_19, 0);
|
|
+ bios_wr32(bios, reg, value1);
|
|
+
|
|
+ udelay(10);
|
|
+
|
|
+ bios_wr32(bios, reg, value2);
|
|
+ bios_wr32(bios, NV_PBUS_PCI_NV_19, pci_nv_19);
|
|
+
|
|
+ pci_nv_20 = bios_rd32(bios, NV_PBUS_PCI_NV_20);
|
|
+ pci_nv_20 &= ~NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED; /* 0xfffffffe */
|
|
+ bios_wr32(bios, NV_PBUS_PCI_NV_20, pci_nv_20);
|
|
+
|
|
+ return 13;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_configure_mem(struct nvbios *bios, uint16_t offset,
|
|
+ struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_CONFIGURE_MEM opcode: 0x66 ('f')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ *
|
|
+ * Equivalent to INIT_DONE on bios version 3 or greater.
|
|
+ * For early bios versions, sets up the memory registers, using values
|
|
+ * taken from the memory init table
|
|
+ */
|
|
+
|
|
+ /* no iexec->execute check by design */
|
|
+
|
|
+ uint16_t meminitoffs = bios->legacy.mem_init_tbl_ptr + MEM_INIT_SIZE * (bios_idxprt_rd(bios, NV_CIO_CRX__COLOR, NV_CIO_CRE_SCRATCH4__INDEX) >> 4);
|
|
+ uint16_t seqtbloffs = bios->legacy.sdr_seq_tbl_ptr, meminitdata = meminitoffs + 6;
|
|
+ uint32_t reg, data;
|
|
+
|
|
+ if (bios->major_version > 2)
|
|
+ return 0;
|
|
+
|
|
+ bios_idxprt_wr(bios, NV_VIO_SRX, NV_VIO_SR_CLOCK_INDEX, bios_idxprt_rd(
|
|
+ bios, NV_VIO_SRX, NV_VIO_SR_CLOCK_INDEX) | 0x20);
|
|
+
|
|
+ if (bios->data[meminitoffs] & 1)
|
|
+ seqtbloffs = bios->legacy.ddr_seq_tbl_ptr;
|
|
+
|
|
+ for (reg = ROM32(bios->data[seqtbloffs]);
|
|
+ reg != 0xffffffff;
|
|
+ reg = ROM32(bios->data[seqtbloffs += 4])) {
|
|
+
|
|
+ switch (reg) {
|
|
+ case NV_PFB_PRE:
|
|
+ data = NV_PFB_PRE_CMD_PRECHARGE;
|
|
+ break;
|
|
+ case NV_PFB_PAD:
|
|
+ data = NV_PFB_PAD_CKE_NORMAL;
|
|
+ break;
|
|
+ case NV_PFB_REF:
|
|
+ data = NV_PFB_REF_CMD_REFRESH;
|
|
+ break;
|
|
+ default:
|
|
+ data = ROM32(bios->data[meminitdata]);
|
|
+ meminitdata += 4;
|
|
+ if (data == 0xffffffff)
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ bios_wr32(bios, reg, data);
|
|
+ }
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_configure_clk(struct nvbios *bios, uint16_t offset,
|
|
+ struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_CONFIGURE_CLK opcode: 0x67 ('g')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ *
|
|
+ * Equivalent to INIT_DONE on bios version 3 or greater.
|
|
+ * For early bios versions, sets up the NVClk and MClk PLLs, using
|
|
+ * values taken from the memory init table
|
|
+ */
|
|
+
|
|
+ /* no iexec->execute check by design */
|
|
+
|
|
+ uint16_t meminitoffs = bios->legacy.mem_init_tbl_ptr + MEM_INIT_SIZE * (bios_idxprt_rd(bios, NV_CIO_CRX__COLOR, NV_CIO_CRE_SCRATCH4__INDEX) >> 4);
|
|
+ int clock;
|
|
+
|
|
+ if (bios->major_version > 2)
|
|
+ return 0;
|
|
+
|
|
+ clock = ROM16(bios->data[meminitoffs + 4]) * 10;
|
|
+ setPLL(bios, NV_PRAMDAC_NVPLL_COEFF, clock);
|
|
+
|
|
+ clock = ROM16(bios->data[meminitoffs + 2]) * 10;
|
|
+ if (bios->data[meminitoffs] & 1) /* DDR */
|
|
+ clock *= 2;
|
|
+ setPLL(bios, NV_PRAMDAC_MPLL_COEFF, clock);
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_configure_preinit(struct nvbios *bios, uint16_t offset,
|
|
+ struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_CONFIGURE_PREINIT opcode: 0x68 ('h')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ *
|
|
+ * Equivalent to INIT_DONE on bios version 3 or greater.
|
|
+ * For early bios versions, does early init, loading ram and crystal
|
|
+ * configuration from straps into CR3C
|
|
+ */
|
|
+
|
|
+ /* no iexec->execute check by design */
|
|
+
|
|
+ uint32_t straps = bios_rd32(bios, NV_PEXTDEV_BOOT_0);
|
|
+ uint8_t cr3c = ((straps << 2) & 0xf0) | (straps & (1 << 6));
|
|
+
|
|
+ if (bios->major_version > 2)
|
|
+ return 0;
|
|
+
|
|
+ bios_idxprt_wr(bios, NV_CIO_CRX__COLOR,
|
|
+ NV_CIO_CRE_SCRATCH4__INDEX, cr3c);
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_io(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_IO opcode: 0x69 ('i')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (16 bit): CRTC port
|
|
+ * offset + 3 (8 bit): mask
|
|
+ * offset + 4 (8 bit): data
|
|
+ *
|
|
+ * Assign ((IOVAL("crtc port") & "mask") | "data") to "crtc port"
|
|
+ */
|
|
+
|
|
+ struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
|
|
+ uint16_t crtcport = ROM16(bios->data[offset + 1]);
|
|
+ uint8_t mask = bios->data[offset + 3];
|
|
+ uint8_t data = bios->data[offset + 4];
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return 5;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Port: 0x%04X, Mask: 0x%02X, Data: 0x%02X\n",
|
|
+ offset, crtcport, mask, data);
|
|
+
|
|
+ /*
|
|
+ * I have no idea what this does, but NVIDIA do this magic sequence
|
|
+ * in the places where this INIT_IO happens..
|
|
+ */
|
|
+ if (dev_priv->card_type >= NV_50 && crtcport == 0x3c3 && data == 1) {
|
|
+ int i;
|
|
+
|
|
+ bios_wr32(bios, 0x614100, (bios_rd32(
|
|
+ bios, 0x614100) & 0x0fffffff) | 0x00800000);
|
|
+
|
|
+ bios_wr32(bios, 0x00e18c, bios_rd32(
|
|
+ bios, 0x00e18c) | 0x00020000);
|
|
+
|
|
+ bios_wr32(bios, 0x614900, (bios_rd32(
|
|
+ bios, 0x614900) & 0x0fffffff) | 0x00800000);
|
|
+
|
|
+ bios_wr32(bios, 0x000200, bios_rd32(
|
|
+ bios, 0x000200) & ~0x40000000);
|
|
+
|
|
+ mdelay(10);
|
|
+
|
|
+ bios_wr32(bios, 0x00e18c, bios_rd32(
|
|
+ bios, 0x00e18c) & ~0x00020000);
|
|
+
|
|
+ bios_wr32(bios, 0x000200, bios_rd32(
|
|
+ bios, 0x000200) | 0x40000000);
|
|
+
|
|
+ bios_wr32(bios, 0x614100, 0x00800018);
|
|
+ bios_wr32(bios, 0x614900, 0x00800018);
|
|
+
|
|
+ mdelay(10);
|
|
+
|
|
+ bios_wr32(bios, 0x614100, 0x10000018);
|
|
+ bios_wr32(bios, 0x614900, 0x10000018);
|
|
+
|
|
+ for (i = 0; i < 3; i++)
|
|
+ bios_wr32(bios, 0x614280 + (i*0x800), bios_rd32(
|
|
+ bios, 0x614280 + (i*0x800)) & 0xf0f0f0f0);
|
|
+
|
|
+ for (i = 0; i < 2; i++)
|
|
+ bios_wr32(bios, 0x614300 + (i*0x800), bios_rd32(
|
|
+ bios, 0x614300 + (i*0x800)) & 0xfffff0f0);
|
|
+
|
|
+ for (i = 0; i < 3; i++)
|
|
+ bios_wr32(bios, 0x614380 + (i*0x800), bios_rd32(
|
|
+ bios, 0x614380 + (i*0x800)) & 0xfffff0f0);
|
|
+
|
|
+ for (i = 0; i < 2; i++)
|
|
+ bios_wr32(bios, 0x614200 + (i*0x800), bios_rd32(
|
|
+ bios, 0x614200 + (i*0x800)) & 0xfffffff0);
|
|
+
|
|
+ for (i = 0; i < 2; i++)
|
|
+ bios_wr32(bios, 0x614108 + (i*0x800), bios_rd32(
|
|
+ bios, 0x614108 + (i*0x800)) & 0x0fffffff);
|
|
+ return 5;
|
|
+ }
|
|
+
|
|
+ bios_port_wr(bios, crtcport, (bios_port_rd(bios, crtcport) & mask) |
|
|
+ data);
|
|
+ return 5;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_sub(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_SUB opcode: 0x6B ('k')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (8 bit): script number
|
|
+ *
|
|
+ * Execute script number "script number", as a subroutine
|
|
+ */
|
|
+
|
|
+ uint8_t sub = bios->data[offset + 1];
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return 2;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Calling script %d\n", offset, sub);
|
|
+
|
|
+ parse_init_table(bios,
|
|
+ ROM16(bios->data[bios->init_script_tbls_ptr + sub * 2]),
|
|
+ iexec);
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: End of script %d\n", offset, sub);
|
|
+
|
|
+ return 2;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_ram_condition(struct nvbios *bios, uint16_t offset,
|
|
+ struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_RAM_CONDITION opcode: 0x6D ('m')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (8 bit): mask
|
|
+ * offset + 2 (8 bit): cmpval
|
|
+ *
|
|
+ * Test if (NV_PFB_BOOT_0 & "mask") equals "cmpval".
|
|
+ * If condition not met skip subsequent opcodes until condition is
|
|
+ * inverted (INIT_NOT), or we hit INIT_RESUME
|
|
+ */
|
|
+
|
|
+ uint8_t mask = bios->data[offset + 1];
|
|
+ uint8_t cmpval = bios->data[offset + 2];
|
|
+ uint8_t data;
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return 3;
|
|
+
|
|
+ data = bios_rd32(bios, NV_PFB_BOOT_0) & mask;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Checking if 0x%08X equals 0x%08X\n",
|
|
+ offset, data, cmpval);
|
|
+
|
|
+ if (data == cmpval)
|
|
+ BIOSLOG(bios, "0x%04X: Condition fulfilled -- continuing to execute\n", offset);
|
|
+ else {
|
|
+ BIOSLOG(bios, "0x%04X: Condition not fulfilled -- skipping following commands\n", offset);
|
|
+ iexec->execute = false;
|
|
+ }
|
|
+
|
|
+ return 3;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_nv_reg(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_NV_REG opcode: 0x6E ('n')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (32 bit): register
|
|
+ * offset + 5 (32 bit): mask
|
|
+ * offset + 9 (32 bit): data
|
|
+ *
|
|
+ * Assign ((REGVAL("register") & "mask") | "data") to "register"
|
|
+ */
|
|
+
|
|
+ uint32_t reg = ROM32(bios->data[offset + 1]);
|
|
+ uint32_t mask = ROM32(bios->data[offset + 5]);
|
|
+ uint32_t data = ROM32(bios->data[offset + 9]);
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return 13;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Mask: 0x%08X, Data: 0x%08X\n",
|
|
+ offset, reg, mask, data);
|
|
+
|
|
+ bios_wr32(bios, reg, (bios_rd32(bios, reg) & mask) | data);
|
|
+
|
|
+ return 13;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_macro(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_MACRO opcode: 0x6F ('o')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (8 bit): macro number
|
|
+ *
|
|
+ * Look up macro index "macro number" in the macro index table.
|
|
+ * The macro index table entry has 1 byte for the index in the macro
|
|
+ * table, and 1 byte for the number of times to repeat the macro.
|
|
+ * The macro table entry has 4 bytes for the register address and
|
|
+ * 4 bytes for the value to write to that register
|
|
+ */
|
|
+
|
|
+ uint8_t macro_index_tbl_idx = bios->data[offset + 1];
|
|
+ uint16_t tmp = bios->macro_index_tbl_ptr + (macro_index_tbl_idx * MACRO_INDEX_SIZE);
|
|
+ uint8_t macro_tbl_idx = bios->data[tmp];
|
|
+ uint8_t count = bios->data[tmp + 1];
|
|
+ uint32_t reg, data;
|
|
+ int i;
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return 2;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Macro: 0x%02X, MacroTableIndex: 0x%02X, "
|
|
+ "Count: 0x%02X\n",
|
|
+ offset, macro_index_tbl_idx, macro_tbl_idx, count);
|
|
+
|
|
+ for (i = 0; i < count; i++) {
|
|
+ uint16_t macroentryptr = bios->macro_tbl_ptr + (macro_tbl_idx + i) * MACRO_SIZE;
|
|
+
|
|
+ reg = ROM32(bios->data[macroentryptr]);
|
|
+ data = ROM32(bios->data[macroentryptr + 4]);
|
|
+
|
|
+ bios_wr32(bios, reg, data);
|
|
+ }
|
|
+
|
|
+ return 2;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_done(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_DONE opcode: 0x71 ('q')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ *
|
|
+ * End the current script
|
|
+ */
|
|
+
|
|
+ /* mild retval abuse to stop parsing this table */
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_resume(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_RESUME opcode: 0x72 ('r')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ *
|
|
+ * End the current execute / no-execute condition
|
|
+ */
|
|
+
|
|
+ if (iexec->execute)
|
|
+ return 1;
|
|
+
|
|
+ iexec->execute = true;
|
|
+ BIOSLOG(bios, "0x%04X: ---- Executing following commands ----\n", offset);
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_time(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_TIME opcode: 0x74 ('t')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (16 bit): time
|
|
+ *
|
|
+ * Sleep for "time" microseconds.
|
|
+ */
|
|
+
|
|
+ unsigned time = ROM16(bios->data[offset + 1]);
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return 3;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Sleeping for 0x%04X microseconds\n",
|
|
+ offset, time);
|
|
+
|
|
+ if (time < 1000)
|
|
+ udelay(time);
|
|
+ else
|
|
+ msleep((time + 900) / 1000);
|
|
+
|
|
+ return 3;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_CONDITION opcode: 0x75 ('u')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (8 bit): condition number
|
|
+ *
|
|
+ * Check condition "condition number" in the condition table.
|
|
+ * If condition not met skip subsequent opcodes until condition is
|
|
+ * inverted (INIT_NOT), or we hit INIT_RESUME
|
|
+ */
|
|
+
|
|
+ uint8_t cond = bios->data[offset + 1];
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return 2;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Condition: 0x%02X\n", offset, cond);
|
|
+
|
|
+ if (bios_condition_met(bios, offset, cond))
|
|
+ BIOSLOG(bios, "0x%04X: Condition fulfilled -- continuing to execute\n", offset);
|
|
+ else {
|
|
+ BIOSLOG(bios, "0x%04X: Condition not fulfilled -- skipping following commands\n", offset);
|
|
+ iexec->execute = false;
|
|
+ }
|
|
+
|
|
+ return 2;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_io_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_IO_CONDITION opcode: 0x76
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (8 bit): condition number
|
|
+ *
|
|
+ * Check condition "condition number" in the io condition table.
|
|
+ * If condition not met skip subsequent opcodes until condition is
|
|
+ * inverted (INIT_NOT), or we hit INIT_RESUME
|
|
+ */
|
|
+
|
|
+ uint8_t cond = bios->data[offset + 1];
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return 2;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: IO condition: 0x%02X\n", offset, cond);
|
|
+
|
|
+ if (io_condition_met(bios, offset, cond))
|
|
+ BIOSLOG(bios, "0x%04X: Condition fulfilled -- continuing to execute\n", offset);
|
|
+ else {
|
|
+ BIOSLOG(bios, "0x%04X: Condition not fulfilled -- skipping following commands\n", offset);
|
|
+ iexec->execute = false;
|
|
+ }
|
|
+
|
|
+ return 2;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_index_io(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_INDEX_IO opcode: 0x78 ('x')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (16 bit): CRTC port
|
|
+ * offset + 3 (8 bit): CRTC index
|
|
+ * offset + 4 (8 bit): mask
|
|
+ * offset + 5 (8 bit): data
|
|
+ *
|
|
+ * Read value at index "CRTC index" on "CRTC port", AND with "mask",
|
|
+ * OR with "data", write-back
|
|
+ */
|
|
+
|
|
+ uint16_t crtcport = ROM16(bios->data[offset + 1]);
|
|
+ uint8_t crtcindex = bios->data[offset + 3];
|
|
+ uint8_t mask = bios->data[offset + 4];
|
|
+ uint8_t data = bios->data[offset + 5];
|
|
+ uint8_t value;
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return 6;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X, "
|
|
+ "Data: 0x%02X\n",
|
|
+ offset, crtcport, crtcindex, mask, data);
|
|
+
|
|
+ value = (bios_idxprt_rd(bios, crtcport, crtcindex) & mask) | data;
|
|
+ bios_idxprt_wr(bios, crtcport, crtcindex, value);
|
|
+
|
|
+ return 6;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_pll(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_PLL opcode: 0x79 ('y')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (32 bit): register
|
|
+ * offset + 5 (16 bit): freq
|
|
+ *
|
|
+ * Set PLL register "register" to coefficients for frequency (10kHz)
|
|
+ * "freq"
|
|
+ */
|
|
+
|
|
+ uint32_t reg = ROM32(bios->data[offset + 1]);
|
|
+ uint16_t freq = ROM16(bios->data[offset + 5]);
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return 7;
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Freq: %d0kHz\n", offset, reg, freq);
|
|
+
|
|
+ setPLL(bios, reg, freq * 10);
|
|
+
|
|
+ return 7;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_zm_reg(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_ZM_REG opcode: 0x7A ('z')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (32 bit): register
|
|
+ * offset + 5 (32 bit): value
|
|
+ *
|
|
+ * Assign "value" to "register"
|
|
+ */
|
|
+
|
|
+ uint32_t reg = ROM32(bios->data[offset + 1]);
|
|
+ uint32_t value = ROM32(bios->data[offset + 5]);
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return 9;
|
|
+
|
|
+ if (reg == 0x000200)
|
|
+ value |= 1;
|
|
+
|
|
+ bios_wr32(bios, reg, value);
|
|
+
|
|
+ return 9;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_ram_restrict_pll(struct nvbios *bios, uint16_t offset,
|
|
+ struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_RAM_RESTRICT_PLL opcode: 0x87 ('')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (8 bit): PLL type
|
|
+ * offset + 2 (32 bit): frequency 0
|
|
+ *
|
|
+ * Uses the RAMCFG strap of PEXTDEV_BOOT as an index into the table at
|
|
+ * ram_restrict_table_ptr. The value read from there is used to select
|
|
+ * a frequency from the table starting at 'frequency 0' to be
|
|
+ * programmed into the PLL corresponding to 'type'.
|
|
+ *
|
|
+ * The PLL limits table on cards using this opcode has a mapping of
|
|
+ * 'type' to the relevant registers.
|
|
+ */
|
|
+
|
|
+ struct drm_device *dev = bios->dev;
|
|
+ uint32_t strap = (bios_rd32(bios, NV_PEXTDEV_BOOT_0) & 0x0000003c) >> 2;
|
|
+ uint8_t index = bios->data[bios->ram_restrict_tbl_ptr + strap];
|
|
+ uint8_t type = bios->data[offset + 1];
|
|
+ uint32_t freq = ROM32(bios->data[offset + 2 + (index * 4)]);
|
|
+ uint8_t *pll_limits = &bios->data[bios->pll_limit_tbl_ptr], *entry;
|
|
+ int len = 2 + bios->ram_restrict_group_count * 4;
|
|
+ int i;
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return len;
|
|
+
|
|
+ if (!bios->pll_limit_tbl_ptr || (pll_limits[0] & 0xf0) != 0x30) {
|
|
+ NV_ERROR(dev, "PLL limits table not version 3.x\n");
|
|
+ return len; /* deliberate, allow default clocks to remain */
|
|
+ }
|
|
+
|
|
+ entry = pll_limits + pll_limits[1];
|
|
+ for (i = 0; i < pll_limits[3]; i++, entry += pll_limits[2]) {
|
|
+ if (entry[0] == type) {
|
|
+ uint32_t reg = ROM32(entry[3]);
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: "
|
|
+ "Type %02x Reg 0x%08x Freq %dKHz\n",
|
|
+ offset, type, reg, freq);
|
|
+
|
|
+ setPLL(bios, reg, freq);
|
|
+ return len;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ NV_ERROR(dev, "PLL type 0x%02x not found in PLL limits table", type);
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_8c(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_8C opcode: 0x8C ('')
|
|
+ *
|
|
+ * NOP so far....
|
|
+ *
|
|
+ */
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_8d(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_8D opcode: 0x8D ('')
|
|
+ *
|
|
+ * NOP so far....
|
|
+ *
|
|
+ */
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_GPIO opcode: 0x8E ('')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ *
|
|
+ * Loop over all entries in the DCB GPIO table, and initialise
|
|
+ * each GPIO according to various values listed in each entry
|
|
+ */
|
|
+
|
|
+ const uint32_t nv50_gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 };
|
|
+ const uint32_t nv50_gpio_ctl[2] = { 0xe100, 0xe28c };
|
|
+ const uint8_t *gpio_table = &bios->data[bios->bdcb.gpio_table_ptr];
|
|
+ const uint8_t *gpio_entry;
|
|
+ int i;
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return 1;
|
|
+
|
|
+ if (bios->bdcb.version != 0x40) {
|
|
+ NV_ERROR(bios->dev, "DCB table not version 4.0\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (!bios->bdcb.gpio_table_ptr) {
|
|
+ NV_WARN(bios->dev, "Invalid pointer to INIT_8E table\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ gpio_entry = gpio_table + gpio_table[1];
|
|
+ for (i = 0; i < gpio_table[2]; i++, gpio_entry += gpio_table[3]) {
|
|
+ uint32_t entry = ROM32(gpio_entry[0]), r, s, v;
|
|
+ int line = (entry & 0x0000001f);
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Entry: 0x%08X\n", offset, entry);
|
|
+
|
|
+ if ((entry & 0x0000ff00) == 0x0000ff00)
|
|
+ continue;
|
|
+
|
|
+ r = nv50_gpio_reg[line >> 3];
|
|
+ s = (line & 0x07) << 2;
|
|
+ v = bios_rd32(bios, r) & ~(0x00000003 << s);
|
|
+ if (entry & 0x01000000)
|
|
+ v |= (((entry & 0x60000000) >> 29) ^ 2) << s;
|
|
+ else
|
|
+ v |= (((entry & 0x18000000) >> 27) ^ 2) << s;
|
|
+ bios_wr32(bios, r, v);
|
|
+
|
|
+ r = nv50_gpio_ctl[line >> 4];
|
|
+ s = (line & 0x0f);
|
|
+ v = bios_rd32(bios, r) & ~(0x00010001 << s);
|
|
+ switch ((entry & 0x06000000) >> 25) {
|
|
+ case 1:
|
|
+ v |= (0x00000001 << s);
|
|
+ break;
|
|
+ case 2:
|
|
+ v |= (0x00010000 << s);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ bios_wr32(bios, r, v);
|
|
+ }
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_ram_restrict_zm_reg_group(struct nvbios *bios, uint16_t offset,
|
|
+ struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_RAM_RESTRICT_ZM_REG_GROUP opcode: 0x8F ('')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (32 bit): reg
|
|
+ * offset + 5 (8 bit): regincrement
|
|
+ * offset + 6 (8 bit): count
|
|
+ * offset + 7 (32 bit): value 1,1
|
|
+ * ...
|
|
+ *
|
|
+ * Use the RAMCFG strap of PEXTDEV_BOOT as an index into the table at
|
|
+ * ram_restrict_table_ptr. The value read from here is 'n', and
|
|
+ * "value 1,n" gets written to "reg". This repeats "count" times and on
|
|
+ * each iteration 'm', "reg" increases by "regincrement" and
|
|
+ * "value m,n" is used. The extent of n is limited by a number read
|
|
+ * from the 'M' BIT table, herein called "blocklen"
|
|
+ */
|
|
+
|
|
+ uint32_t reg = ROM32(bios->data[offset + 1]);
|
|
+ uint8_t regincrement = bios->data[offset + 5];
|
|
+ uint8_t count = bios->data[offset + 6];
|
|
+ uint32_t strap_ramcfg, data;
|
|
+ /* previously set by 'M' BIT table */
|
|
+ uint16_t blocklen = bios->ram_restrict_group_count * 4;
|
|
+ int len = 7 + count * blocklen;
|
|
+ uint8_t index;
|
|
+ int i;
|
|
+
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return len;
|
|
+
|
|
+ if (!blocklen) {
|
|
+ NV_ERROR(bios->dev,
|
|
+ "0x%04X: Zero block length - has the M table "
|
|
+ "been parsed?\n", offset);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ strap_ramcfg = (bios_rd32(bios, NV_PEXTDEV_BOOT_0) >> 2) & 0xf;
|
|
+ index = bios->data[bios->ram_restrict_tbl_ptr + strap_ramcfg];
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: Reg: 0x%08X, RegIncrement: 0x%02X, "
|
|
+ "Count: 0x%02X, StrapRamCfg: 0x%02X, Index: 0x%02X\n",
|
|
+ offset, reg, regincrement, count, strap_ramcfg, index);
|
|
+
|
|
+ for (i = 0; i < count; i++) {
|
|
+ data = ROM32(bios->data[offset + 7 + index * 4 + blocklen * i]);
|
|
+
|
|
+ bios_wr32(bios, reg, data);
|
|
+
|
|
+ reg += regincrement;
|
|
+ }
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_copy_zm_reg(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_COPY_ZM_REG opcode: 0x90 ('')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (32 bit): src reg
|
|
+ * offset + 5 (32 bit): dst reg
|
|
+ *
|
|
+ * Put contents of "src reg" into "dst reg"
|
|
+ */
|
|
+
|
|
+ uint32_t srcreg = ROM32(bios->data[offset + 1]);
|
|
+ uint32_t dstreg = ROM32(bios->data[offset + 5]);
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return 9;
|
|
+
|
|
+ bios_wr32(bios, dstreg, bios_rd32(bios, srcreg));
|
|
+
|
|
+ return 9;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_zm_reg_group_addr_latched(struct nvbios *bios, uint16_t offset,
|
|
+ struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_ZM_REG_GROUP_ADDRESS_LATCHED opcode: 0x91 ('')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (32 bit): dst reg
|
|
+ * offset + 5 (8 bit): count
|
|
+ * offset + 6 (32 bit): data 1
|
|
+ * ...
|
|
+ *
|
|
+ * For each of "count" values write "data n" to "dst reg"
|
|
+ */
|
|
+
|
|
+ uint32_t reg = ROM32(bios->data[offset + 1]);
|
|
+ uint8_t count = bios->data[offset + 5];
|
|
+ int len = 6 + count * 4;
|
|
+ int i;
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return len;
|
|
+
|
|
+ for (i = 0; i < count; i++) {
|
|
+ uint32_t data = ROM32(bios->data[offset + 6 + 4 * i]);
|
|
+ bios_wr32(bios, reg, data);
|
|
+ }
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_reserved(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_RESERVED opcode: 0x92 ('')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ *
|
|
+ * Seemingly does nothing
|
|
+ */
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_96(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_96 opcode: 0x96 ('')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (32 bit): sreg
|
|
+ * offset + 5 (8 bit): sshift
|
|
+ * offset + 6 (8 bit): smask
|
|
+ * offset + 7 (8 bit): index
|
|
+ * offset + 8 (32 bit): reg
|
|
+ * offset + 12 (32 bit): mask
|
|
+ * offset + 16 (8 bit): shift
|
|
+ *
|
|
+ */
|
|
+
|
|
+ uint16_t xlatptr = bios->init96_tbl_ptr + (bios->data[offset + 7] * 2);
|
|
+ uint32_t reg = ROM32(bios->data[offset + 8]);
|
|
+ uint32_t mask = ROM32(bios->data[offset + 12]);
|
|
+ uint32_t val;
|
|
+
|
|
+ val = bios_rd32(bios, ROM32(bios->data[offset + 1]));
|
|
+ if (bios->data[offset + 5] < 0x80)
|
|
+ val >>= bios->data[offset + 5];
|
|
+ else
|
|
+ val <<= (0x100 - bios->data[offset + 5]);
|
|
+ val &= bios->data[offset + 6];
|
|
+
|
|
+ val = bios->data[ROM16(bios->data[xlatptr]) + val];
|
|
+ val <<= bios->data[offset + 16];
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return 17;
|
|
+
|
|
+ bios_wr32(bios, reg, (bios_rd32(bios, reg) & mask) | val);
|
|
+ return 17;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_97(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_97 opcode: 0x97 ('')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (32 bit): register
|
|
+ * offset + 5 (32 bit): mask
|
|
+ * offset + 9 (32 bit): value
|
|
+ *
|
|
+ * Adds "value" to "register" preserving the fields specified
|
|
+ * by "mask"
|
|
+ */
|
|
+
|
|
+ uint32_t reg = ROM32(bios->data[offset + 1]);
|
|
+ uint32_t mask = ROM32(bios->data[offset + 5]);
|
|
+ uint32_t add = ROM32(bios->data[offset + 9]);
|
|
+ uint32_t val;
|
|
+
|
|
+ val = bios_rd32(bios, reg);
|
|
+ val = (val & mask) | ((val + add) & ~mask);
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return 13;
|
|
+
|
|
+ bios_wr32(bios, reg, val);
|
|
+ return 13;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_AUXCH opcode: 0x98 ('')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (32 bit): address
|
|
+ * offset + 5 (8 bit): count
|
|
+ * offset + 6 (8 bit): mask 0
|
|
+ * offset + 7 (8 bit): data 0
|
|
+ * ...
|
|
+ *
|
|
+ */
|
|
+
|
|
+ struct drm_device *dev = bios->dev;
|
|
+ struct nouveau_i2c_chan *auxch;
|
|
+ uint32_t addr = ROM32(bios->data[offset + 1]);
|
|
+ uint8_t count = bios->data[offset + 5];
|
|
+ int len = 6 + count * 2;
|
|
+ int ret, i;
|
|
+
|
|
+ if (!bios->display.output) {
|
|
+ NV_ERROR(dev, "INIT_AUXCH: no active output\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ auxch = init_i2c_device_find(dev, bios->display.output->i2c_index);
|
|
+ if (!auxch) {
|
|
+ NV_ERROR(dev, "INIT_AUXCH: couldn't get auxch %d\n",
|
|
+ bios->display.output->i2c_index);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return len;
|
|
+
|
|
+ offset += 6;
|
|
+ for (i = 0; i < count; i++, offset += 2) {
|
|
+ uint8_t data;
|
|
+
|
|
+ ret = nouveau_dp_auxch(auxch, 9, addr, &data, 1);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "INIT_AUXCH: rd auxch fail %d\n", ret);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ data &= bios->data[offset + 0];
|
|
+ data |= bios->data[offset + 1];
|
|
+
|
|
+ ret = nouveau_dp_auxch(auxch, 8, addr, &data, 1);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "INIT_AUXCH: wr auxch fail %d\n", ret);
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static int
|
|
+init_zm_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * INIT_ZM_AUXCH opcode: 0x99 ('')
|
|
+ *
|
|
+ * offset (8 bit): opcode
|
|
+ * offset + 1 (32 bit): address
|
|
+ * offset + 5 (8 bit): count
|
|
+ * offset + 6 (8 bit): data 0
|
|
+ * ...
|
|
+ *
|
|
+ */
|
|
+
|
|
+ struct drm_device *dev = bios->dev;
|
|
+ struct nouveau_i2c_chan *auxch;
|
|
+ uint32_t addr = ROM32(bios->data[offset + 1]);
|
|
+ uint8_t count = bios->data[offset + 5];
|
|
+ int len = 6 + count;
|
|
+ int ret, i;
|
|
+
|
|
+ if (!bios->display.output) {
|
|
+ NV_ERROR(dev, "INIT_ZM_AUXCH: no active output\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ auxch = init_i2c_device_find(dev, bios->display.output->i2c_index);
|
|
+ if (!auxch) {
|
|
+ NV_ERROR(dev, "INIT_ZM_AUXCH: couldn't get auxch %d\n",
|
|
+ bios->display.output->i2c_index);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (!iexec->execute)
|
|
+ return len;
|
|
+
|
|
+ offset += 6;
|
|
+ for (i = 0; i < count; i++, offset++) {
|
|
+ ret = nouveau_dp_auxch(auxch, 8, addr, &bios->data[offset], 1);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "INIT_ZM_AUXCH: wr auxch fail %d\n", ret);
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static struct init_tbl_entry itbl_entry[] = {
|
|
+ /* command name , id , length , offset , mult , command handler */
|
|
+ /* INIT_PROG (0x31, 15, 10, 4) removed due to no example of use */
|
|
+ { "INIT_IO_RESTRICT_PROG" , 0x32, init_io_restrict_prog },
|
|
+ { "INIT_REPEAT" , 0x33, init_repeat },
|
|
+ { "INIT_IO_RESTRICT_PLL" , 0x34, init_io_restrict_pll },
|
|
+ { "INIT_END_REPEAT" , 0x36, init_end_repeat },
|
|
+ { "INIT_COPY" , 0x37, init_copy },
|
|
+ { "INIT_NOT" , 0x38, init_not },
|
|
+ { "INIT_IO_FLAG_CONDITION" , 0x39, init_io_flag_condition },
|
|
+ { "INIT_INDEX_ADDRESS_LATCHED" , 0x49, init_idx_addr_latched },
|
|
+ { "INIT_IO_RESTRICT_PLL2" , 0x4A, init_io_restrict_pll2 },
|
|
+ { "INIT_PLL2" , 0x4B, init_pll2 },
|
|
+ { "INIT_I2C_BYTE" , 0x4C, init_i2c_byte },
|
|
+ { "INIT_ZM_I2C_BYTE" , 0x4D, init_zm_i2c_byte },
|
|
+ { "INIT_ZM_I2C" , 0x4E, init_zm_i2c },
|
|
+ { "INIT_TMDS" , 0x4F, init_tmds },
|
|
+ { "INIT_ZM_TMDS_GROUP" , 0x50, init_zm_tmds_group },
|
|
+ { "INIT_CR_INDEX_ADDRESS_LATCHED" , 0x51, init_cr_idx_adr_latch },
|
|
+ { "INIT_CR" , 0x52, init_cr },
|
|
+ { "INIT_ZM_CR" , 0x53, init_zm_cr },
|
|
+ { "INIT_ZM_CR_GROUP" , 0x54, init_zm_cr_group },
|
|
+ { "INIT_CONDITION_TIME" , 0x56, init_condition_time },
|
|
+ { "INIT_ZM_REG_SEQUENCE" , 0x58, init_zm_reg_sequence },
|
|
+ /* INIT_INDIRECT_REG (0x5A, 7, 0, 0) removed due to no example of use */
|
|
+ { "INIT_SUB_DIRECT" , 0x5B, init_sub_direct },
|
|
+ { "INIT_COPY_NV_REG" , 0x5F, init_copy_nv_reg },
|
|
+ { "INIT_ZM_INDEX_IO" , 0x62, init_zm_index_io },
|
|
+ { "INIT_COMPUTE_MEM" , 0x63, init_compute_mem },
|
|
+ { "INIT_RESET" , 0x65, init_reset },
|
|
+ { "INIT_CONFIGURE_MEM" , 0x66, init_configure_mem },
|
|
+ { "INIT_CONFIGURE_CLK" , 0x67, init_configure_clk },
|
|
+ { "INIT_CONFIGURE_PREINIT" , 0x68, init_configure_preinit },
|
|
+ { "INIT_IO" , 0x69, init_io },
|
|
+ { "INIT_SUB" , 0x6B, init_sub },
|
|
+ { "INIT_RAM_CONDITION" , 0x6D, init_ram_condition },
|
|
+ { "INIT_NV_REG" , 0x6E, init_nv_reg },
|
|
+ { "INIT_MACRO" , 0x6F, init_macro },
|
|
+ { "INIT_DONE" , 0x71, init_done },
|
|
+ { "INIT_RESUME" , 0x72, init_resume },
|
|
+ /* INIT_RAM_CONDITION2 (0x73, 9, 0, 0) removed due to no example of use */
|
|
+ { "INIT_TIME" , 0x74, init_time },
|
|
+ { "INIT_CONDITION" , 0x75, init_condition },
|
|
+ { "INIT_IO_CONDITION" , 0x76, init_io_condition },
|
|
+ { "INIT_INDEX_IO" , 0x78, init_index_io },
|
|
+ { "INIT_PLL" , 0x79, init_pll },
|
|
+ { "INIT_ZM_REG" , 0x7A, init_zm_reg },
|
|
+ { "INIT_RAM_RESTRICT_PLL" , 0x87, init_ram_restrict_pll },
|
|
+ { "INIT_8C" , 0x8C, init_8c },
|
|
+ { "INIT_8D" , 0x8D, init_8d },
|
|
+ { "INIT_GPIO" , 0x8E, init_gpio },
|
|
+ { "INIT_RAM_RESTRICT_ZM_REG_GROUP" , 0x8F, init_ram_restrict_zm_reg_group },
|
|
+ { "INIT_COPY_ZM_REG" , 0x90, init_copy_zm_reg },
|
|
+ { "INIT_ZM_REG_GROUP_ADDRESS_LATCHED" , 0x91, init_zm_reg_group_addr_latched },
|
|
+ { "INIT_RESERVED" , 0x92, init_reserved },
|
|
+ { "INIT_96" , 0x96, init_96 },
|
|
+ { "INIT_97" , 0x97, init_97 },
|
|
+ { "INIT_AUXCH" , 0x98, init_auxch },
|
|
+ { "INIT_ZM_AUXCH" , 0x99, init_zm_auxch },
|
|
+ { NULL , 0 , NULL }
|
|
+};
|
|
+
|
|
+#define MAX_TABLE_OPS 1000
|
|
+
|
|
+static int
|
|
+parse_init_table(struct nvbios *bios, unsigned int offset,
|
|
+ struct init_exec *iexec)
|
|
+{
|
|
+ /*
|
|
+ * Parses all commands in an init table.
|
|
+ *
|
|
+ * We start out executing all commands found in the init table. Some
|
|
+ * opcodes may change the status of iexec->execute to SKIP, which will
|
|
+ * cause the following opcodes to perform no operation until the value
|
|
+ * is changed back to EXECUTE.
|
|
+ */
|
|
+
|
|
+ int count = 0, i, res;
|
|
+ uint8_t id;
|
|
+
|
|
+ /*
|
|
+ * Loop until INIT_DONE causes us to break out of the loop
|
|
+ * (or until offset > bios length just in case... )
|
|
+ * (and no more than MAX_TABLE_OPS iterations, just in case... )
|
|
+ */
|
|
+ while ((offset < bios->length) && (count++ < MAX_TABLE_OPS)) {
|
|
+ id = bios->data[offset];
|
|
+
|
|
+ /* Find matching id in itbl_entry */
|
|
+ for (i = 0; itbl_entry[i].name && (itbl_entry[i].id != id); i++)
|
|
+ ;
|
|
+
|
|
+ if (itbl_entry[i].name) {
|
|
+ BIOSLOG(bios, "0x%04X: [ (0x%02X) - %s ]\n",
|
|
+ offset, itbl_entry[i].id, itbl_entry[i].name);
|
|
+
|
|
+ /* execute eventual command handler */
|
|
+ res = (*itbl_entry[i].handler)(bios, offset, iexec);
|
|
+ if (!res)
|
|
+ break;
|
|
+ /*
|
|
+ * Add the offset of the current command including all data
|
|
+ * of that command. The offset will then be pointing on the
|
|
+ * next op code.
|
|
+ */
|
|
+ offset += res;
|
|
+ } else {
|
|
+ NV_ERROR(bios->dev,
|
|
+ "0x%04X: Init table command not found: "
|
|
+ "0x%02X\n", offset, id);
|
|
+ return -ENOENT;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (offset >= bios->length)
|
|
+ NV_WARN(bios->dev,
|
|
+ "Offset 0x%04X greater than known bios image length. "
|
|
+ "Corrupt image?\n", offset);
|
|
+ if (count >= MAX_TABLE_OPS)
|
|
+ NV_WARN(bios->dev,
|
|
+ "More than %d opcodes to a table is unlikely, "
|
|
+ "is the bios image corrupt?\n", MAX_TABLE_OPS);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void
|
|
+parse_init_tables(struct nvbios *bios)
|
|
+{
|
|
+ /* Loops and calls parse_init_table() for each present table. */
|
|
+
|
|
+ int i = 0;
|
|
+ uint16_t table;
|
|
+ struct init_exec iexec = {true, false};
|
|
+
|
|
+ if (bios->old_style_init) {
|
|
+ if (bios->init_script_tbls_ptr)
|
|
+ parse_init_table(bios, bios->init_script_tbls_ptr, &iexec);
|
|
+ if (bios->extra_init_script_tbl_ptr)
|
|
+ parse_init_table(bios, bios->extra_init_script_tbl_ptr, &iexec);
|
|
+
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ while ((table = ROM16(bios->data[bios->init_script_tbls_ptr + i]))) {
|
|
+ NV_INFO(bios->dev,
|
|
+ "Parsing VBIOS init table %d at offset 0x%04X\n",
|
|
+ i / 2, table);
|
|
+ BIOSLOG(bios, "0x%04X: ------ Executing following commands ------\n", table);
|
|
+
|
|
+ parse_init_table(bios, table, &iexec);
|
|
+ i += 2;
|
|
+ }
|
|
+}
|
|
+
|
|
+static uint16_t clkcmptable(struct nvbios *bios, uint16_t clktable, int pxclk)
|
|
+{
|
|
+ int compare_record_len, i = 0;
|
|
+ uint16_t compareclk, scriptptr = 0;
|
|
+
|
|
+ if (bios->major_version < 5) /* pre BIT */
|
|
+ compare_record_len = 3;
|
|
+ else
|
|
+ compare_record_len = 4;
|
|
+
|
|
+ do {
|
|
+ compareclk = ROM16(bios->data[clktable + compare_record_len * i]);
|
|
+ if (pxclk >= compareclk * 10) {
|
|
+ if (bios->major_version < 5) {
|
|
+ uint8_t tmdssub = bios->data[clktable + 2 + compare_record_len * i];
|
|
+ scriptptr = ROM16(bios->data[bios->init_script_tbls_ptr + tmdssub * 2]);
|
|
+ } else
|
|
+ scriptptr = ROM16(bios->data[clktable + 2 + compare_record_len * i]);
|
|
+ break;
|
|
+ }
|
|
+ i++;
|
|
+ } while (compareclk);
|
|
+
|
|
+ return scriptptr;
|
|
+}
|
|
+
|
|
+static void
|
|
+run_digital_op_script(struct drm_device *dev, uint16_t scriptptr,
|
|
+ struct dcb_entry *dcbent, int head, bool dl)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nvbios *bios = &dev_priv->VBIOS;
|
|
+ struct init_exec iexec = {true, false};
|
|
+
|
|
+ NV_TRACE(dev, "0x%04X: Parsing digital output script table\n",
|
|
+ scriptptr);
|
|
+ bios_idxprt_wr(bios, NV_CIO_CRX__COLOR, NV_CIO_CRE_44,
|
|
+ head ? NV_CIO_CRE_44_HEADB : NV_CIO_CRE_44_HEADA);
|
|
+ /* note: if dcb entries have been merged, index may be misleading */
|
|
+ NVWriteVgaCrtc5758(dev, head, 0, dcbent->index);
|
|
+ parse_init_table(bios, scriptptr, &iexec);
|
|
+
|
|
+ nv04_dfp_bind_head(dev, dcbent, head, dl);
|
|
+}
|
|
+
|
|
+static int call_lvds_manufacturer_script(struct drm_device *dev, struct dcb_entry *dcbent, int head, enum LVDS_script script)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nvbios *bios = &dev_priv->VBIOS;
|
|
+ uint8_t sub = bios->data[bios->fp.xlated_entry + script] + (bios->fp.link_c_increment && dcbent->or & OUTPUT_C ? 1 : 0);
|
|
+ uint16_t scriptofs = ROM16(bios->data[bios->init_script_tbls_ptr + sub * 2]);
|
|
+
|
|
+ if (!bios->fp.xlated_entry || !sub || !scriptofs)
|
|
+ return -EINVAL;
|
|
+
|
|
+ run_digital_op_script(dev, scriptofs, dcbent, head, bios->fp.dual_link);
|
|
+
|
|
+ if (script == LVDS_PANEL_OFF) {
|
|
+ /* off-on delay in ms */
|
|
+ msleep(ROM16(bios->data[bios->fp.xlated_entry + 7]));
|
|
+ }
|
|
+#ifdef __powerpc__
|
|
+ /* Powerbook specific quirks */
|
|
+ if ((dev->pci_device & 0xffff) == 0x0179 ||
|
|
+ (dev->pci_device & 0xffff) == 0x0189 ||
|
|
+ (dev->pci_device & 0xffff) == 0x0329) {
|
|
+ if (script == LVDS_RESET) {
|
|
+ nv_write_tmds(dev, dcbent->or, 0, 0x02, 0x72);
|
|
+
|
|
+ } else if (script == LVDS_PANEL_ON) {
|
|
+ bios_wr32(bios, NV_PBUS_DEBUG_DUALHEAD_CTL,
|
|
+ bios_rd32(bios, NV_PBUS_DEBUG_DUALHEAD_CTL)
|
|
+ | (1 << 31));
|
|
+ bios_wr32(bios, NV_PCRTC_GPIO_EXT,
|
|
+ bios_rd32(bios, NV_PCRTC_GPIO_EXT) | 1);
|
|
+
|
|
+ } else if (script == LVDS_PANEL_OFF) {
|
|
+ bios_wr32(bios, NV_PBUS_DEBUG_DUALHEAD_CTL,
|
|
+ bios_rd32(bios, NV_PBUS_DEBUG_DUALHEAD_CTL)
|
|
+ & ~(1 << 31));
|
|
+ bios_wr32(bios, NV_PCRTC_GPIO_EXT,
|
|
+ bios_rd32(bios, NV_PCRTC_GPIO_EXT) & ~3);
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int run_lvds_table(struct drm_device *dev, struct dcb_entry *dcbent, int head, enum LVDS_script script, int pxclk)
|
|
+{
|
|
+ /*
|
|
+ * The BIT LVDS table's header has the information to setup the
|
|
+ * necessary registers. Following the standard 4 byte header are:
|
|
+ * A bitmask byte and a dual-link transition pxclk value for use in
|
|
+ * selecting the init script when not using straps; 4 script pointers
|
|
+ * for panel power, selected by output and on/off; and 8 table pointers
|
|
+ * for panel init, the needed one determined by output, and bits in the
|
|
+ * conf byte. These tables are similar to the TMDS tables, consisting
|
|
+ * of a list of pxclks and script pointers.
|
|
+ */
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nvbios *bios = &dev_priv->VBIOS;
|
|
+ unsigned int outputset = (dcbent->or == 4) ? 1 : 0;
|
|
+ uint16_t scriptptr = 0, clktable;
|
|
+ uint8_t clktableptr = 0;
|
|
+
|
|
+ /*
|
|
+ * For now we assume version 3.0 table - g80 support will need some
|
|
+ * changes
|
|
+ */
|
|
+
|
|
+ switch (script) {
|
|
+ case LVDS_INIT:
|
|
+ return -ENOSYS;
|
|
+ case LVDS_BACKLIGHT_ON:
|
|
+ case LVDS_PANEL_ON:
|
|
+ scriptptr = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 7 + outputset * 2]);
|
|
+ break;
|
|
+ case LVDS_BACKLIGHT_OFF:
|
|
+ case LVDS_PANEL_OFF:
|
|
+ scriptptr = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 11 + outputset * 2]);
|
|
+ break;
|
|
+ case LVDS_RESET:
|
|
+ if (dcbent->lvdsconf.use_straps_for_mode) {
|
|
+ if (bios->fp.dual_link)
|
|
+ clktableptr += 2;
|
|
+ if (bios->fp.BITbit1)
|
|
+ clktableptr++;
|
|
+ } else {
|
|
+ /* using EDID */
|
|
+ uint8_t fallback = bios->data[bios->fp.lvdsmanufacturerpointer + 4];
|
|
+ int fallbackcmpval = (dcbent->or == 4) ? 4 : 1;
|
|
+
|
|
+ if (bios->fp.dual_link) {
|
|
+ clktableptr += 2;
|
|
+ fallbackcmpval *= 2;
|
|
+ }
|
|
+ if (fallbackcmpval & fallback)
|
|
+ clktableptr++;
|
|
+ }
|
|
+
|
|
+ /* adding outputset * 8 may not be correct */
|
|
+ clktable = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 15 + clktableptr * 2 + outputset * 8]);
|
|
+ if (!clktable) {
|
|
+ NV_ERROR(dev, "Pixel clock comparison table not found\n");
|
|
+ return -ENOENT;
|
|
+ }
|
|
+ scriptptr = clkcmptable(bios, clktable, pxclk);
|
|
+ }
|
|
+
|
|
+ if (!scriptptr) {
|
|
+ NV_ERROR(dev, "LVDS output init script not found\n");
|
|
+ return -ENOENT;
|
|
+ }
|
|
+ run_digital_op_script(dev, scriptptr, dcbent, head, bios->fp.dual_link);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int call_lvds_script(struct drm_device *dev, struct dcb_entry *dcbent, int head, enum LVDS_script script, int pxclk)
|
|
+{
|
|
+ /*
|
|
+ * LVDS operations are multiplexed in an effort to present a single API
|
|
+ * which works with two vastly differing underlying structures.
|
|
+ * This acts as the demux
|
|
+ */
|
|
+
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nvbios *bios = &dev_priv->VBIOS;
|
|
+ uint8_t lvds_ver = bios->data[bios->fp.lvdsmanufacturerpointer];
|
|
+ uint32_t sel_clk_binding, sel_clk;
|
|
+ int ret;
|
|
+
|
|
+ if (bios->fp.last_script_invoc == (script << 1 | head) || !lvds_ver ||
|
|
+ (lvds_ver >= 0x30 && script == LVDS_INIT))
|
|
+ return 0;
|
|
+
|
|
+ if (!bios->fp.lvds_init_run) {
|
|
+ bios->fp.lvds_init_run = true;
|
|
+ call_lvds_script(dev, dcbent, head, LVDS_INIT, pxclk);
|
|
+ }
|
|
+
|
|
+ if (script == LVDS_PANEL_ON && bios->fp.reset_after_pclk_change)
|
|
+ call_lvds_script(dev, dcbent, head, LVDS_RESET, pxclk);
|
|
+ if (script == LVDS_RESET && bios->fp.power_off_for_reset)
|
|
+ call_lvds_script(dev, dcbent, head, LVDS_PANEL_OFF, pxclk);
|
|
+
|
|
+ NV_TRACE(dev, "Calling LVDS script %d:\n", script);
|
|
+
|
|
+ /* don't let script change pll->head binding */
|
|
+ sel_clk_binding = bios_rd32(bios, NV_PRAMDAC_SEL_CLK) & 0x50000;
|
|
+
|
|
+ if (lvds_ver < 0x30)
|
|
+ ret = call_lvds_manufacturer_script(dev, dcbent, head, script);
|
|
+ else
|
|
+ ret = run_lvds_table(dev, dcbent, head, script, pxclk);
|
|
+
|
|
+ bios->fp.last_script_invoc = (script << 1 | head);
|
|
+
|
|
+ sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK) & ~0x50000;
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, sel_clk | sel_clk_binding);
|
|
+ /* some scripts set a value in NV_PBUS_POWERCTRL_2 and break video overlay */
|
|
+ nvWriteMC(dev, NV_PBUS_POWERCTRL_2, 0);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+struct lvdstableheader {
|
|
+ uint8_t lvds_ver, headerlen, recordlen;
|
|
+};
|
|
+
|
|
+static int parse_lvds_manufacturer_table_header(struct drm_device *dev, struct nvbios *bios, struct lvdstableheader *lth)
|
|
+{
|
|
+ /*
|
|
+ * BMP version (0xa) LVDS table has a simple header of version and
|
|
+ * record length. The BIT LVDS table has the typical BIT table header:
|
|
+ * version byte, header length byte, record length byte, and a byte for
|
|
+ * the maximum number of records that can be held in the table.
|
|
+ */
|
|
+
|
|
+ uint8_t lvds_ver, headerlen, recordlen;
|
|
+
|
|
+ memset(lth, 0, sizeof(struct lvdstableheader));
|
|
+
|
|
+ if (bios->fp.lvdsmanufacturerpointer == 0x0) {
|
|
+ NV_ERROR(dev, "Pointer to LVDS manufacturer table invalid\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ lvds_ver = bios->data[bios->fp.lvdsmanufacturerpointer];
|
|
+
|
|
+ switch (lvds_ver) {
|
|
+ case 0x0a: /* pre NV40 */
|
|
+ headerlen = 2;
|
|
+ recordlen = bios->data[bios->fp.lvdsmanufacturerpointer + 1];
|
|
+ break;
|
|
+ case 0x30: /* NV4x */
|
|
+ headerlen = bios->data[bios->fp.lvdsmanufacturerpointer + 1];
|
|
+ if (headerlen < 0x1f) {
|
|
+ NV_ERROR(dev, "LVDS table header not understood\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ recordlen = bios->data[bios->fp.lvdsmanufacturerpointer + 2];
|
|
+ break;
|
|
+ case 0x40: /* G80/G90 */
|
|
+ headerlen = bios->data[bios->fp.lvdsmanufacturerpointer + 1];
|
|
+ if (headerlen < 0x7) {
|
|
+ NV_ERROR(dev, "LVDS table header not understood\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ recordlen = bios->data[bios->fp.lvdsmanufacturerpointer + 2];
|
|
+ break;
|
|
+ default:
|
|
+ NV_ERROR(dev,
|
|
+ "LVDS table revision %d.%d not currently supported\n",
|
|
+ lvds_ver >> 4, lvds_ver & 0xf);
|
|
+ return -ENOSYS;
|
|
+ }
|
|
+
|
|
+ lth->lvds_ver = lvds_ver;
|
|
+ lth->headerlen = headerlen;
|
|
+ lth->recordlen = recordlen;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+get_fp_strap(struct drm_device *dev, struct nvbios *bios)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ /*
|
|
+ * The fp strap is normally dictated by the "User Strap" in
|
|
+ * PEXTDEV_BOOT_0[20:16], but on BMP cards when bit 2 of the
|
|
+ * Internal_Flags struct at 0x48 is set, the user strap gets overriden
|
|
+ * by the PCI subsystem ID during POST, but not before the previous user
|
|
+ * strap has been committed to CR58 for CR57=0xf on head A, which may be
|
|
+ * read and used instead
|
|
+ */
|
|
+
|
|
+ if (bios->major_version < 5 && bios->data[0x48] & 0x4)
|
|
+ return NVReadVgaCrtc5758(dev, 0, 0xf) & 0xf;
|
|
+
|
|
+ if (dev_priv->card_type >= NV_50)
|
|
+ return (bios_rd32(bios, NV_PEXTDEV_BOOT_0) >> 24) & 0xf;
|
|
+ else
|
|
+ return (bios_rd32(bios, NV_PEXTDEV_BOOT_0) >> 16) & 0xf;
|
|
+}
|
|
+
|
|
+static int parse_fp_mode_table(struct drm_device *dev, struct nvbios *bios)
|
|
+{
|
|
+ uint8_t *fptable;
|
|
+ uint8_t fptable_ver, headerlen = 0, recordlen, fpentries = 0xf, fpindex;
|
|
+ int ret, ofs, fpstrapping;
|
|
+ struct lvdstableheader lth;
|
|
+
|
|
+ if (bios->fp.fptablepointer == 0x0) {
|
|
+ /* Apple cards don't have the fp table; the laptops use DDC */
|
|
+ /* The table is also missing on some x86 IGPs */
|
|
+#ifndef __powerpc__
|
|
+ NV_ERROR(dev, "Pointer to flat panel table invalid\n");
|
|
+#endif
|
|
+ bios->pub.digital_min_front_porch = 0x4b;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ fptable = &bios->data[bios->fp.fptablepointer];
|
|
+ fptable_ver = fptable[0];
|
|
+
|
|
+ switch (fptable_ver) {
|
|
+ /*
|
|
+ * BMP version 0x5.0x11 BIOSen have version 1 like tables, but no
|
|
+ * version field, and miss one of the spread spectrum/PWM bytes.
|
|
+ * This could affect early GF2Go parts (not seen any appropriate ROMs
|
|
+ * though). Here we assume that a version of 0x05 matches this case
|
|
+ * (combining with a BMP version check would be better), as the
|
|
+ * common case for the panel type field is 0x0005, and that is in
|
|
+ * fact what we are reading the first byte of.
|
|
+ */
|
|
+ case 0x05: /* some NV10, 11, 15, 16 */
|
|
+ recordlen = 42;
|
|
+ ofs = -1;
|
|
+ break;
|
|
+ case 0x10: /* some NV15/16, and NV11+ */
|
|
+ recordlen = 44;
|
|
+ ofs = 0;
|
|
+ break;
|
|
+ case 0x20: /* NV40+ */
|
|
+ headerlen = fptable[1];
|
|
+ recordlen = fptable[2];
|
|
+ fpentries = fptable[3];
|
|
+ /*
|
|
+ * fptable[4] is the minimum
|
|
+ * RAMDAC_FP_HCRTC -> RAMDAC_FP_HSYNC_START gap
|
|
+ */
|
|
+ bios->pub.digital_min_front_porch = fptable[4];
|
|
+ ofs = -7;
|
|
+ break;
|
|
+ default:
|
|
+ NV_ERROR(dev,
|
|
+ "FP table revision %d.%d not currently supported\n",
|
|
+ fptable_ver >> 4, fptable_ver & 0xf);
|
|
+ return -ENOSYS;
|
|
+ }
|
|
+
|
|
+ if (!bios->is_mobile) /* !mobile only needs digital_min_front_porch */
|
|
+ return 0;
|
|
+
|
|
+ ret = parse_lvds_manufacturer_table_header(dev, bios, <h);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ if (lth.lvds_ver == 0x30 || lth.lvds_ver == 0x40) {
|
|
+ bios->fp.fpxlatetableptr = bios->fp.lvdsmanufacturerpointer +
|
|
+ lth.headerlen + 1;
|
|
+ bios->fp.xlatwidth = lth.recordlen;
|
|
+ }
|
|
+ if (bios->fp.fpxlatetableptr == 0x0) {
|
|
+ NV_ERROR(dev, "Pointer to flat panel xlat table invalid\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ fpstrapping = get_fp_strap(dev, bios);
|
|
+
|
|
+ fpindex = bios->data[bios->fp.fpxlatetableptr +
|
|
+ fpstrapping * bios->fp.xlatwidth];
|
|
+
|
|
+ if (fpindex > fpentries) {
|
|
+ NV_ERROR(dev, "Bad flat panel table index\n");
|
|
+ return -ENOENT;
|
|
+ }
|
|
+
|
|
+ /* nv4x cards need both a strap value and fpindex of 0xf to use DDC */
|
|
+ if (lth.lvds_ver > 0x10)
|
|
+ bios->pub.fp_no_ddc = fpstrapping != 0xf || fpindex != 0xf;
|
|
+
|
|
+ /*
|
|
+ * If either the strap or xlated fpindex value are 0xf there is no
|
|
+ * panel using a strap-derived bios mode present. this condition
|
|
+ * includes, but is different from, the DDC panel indicator above
|
|
+ */
|
|
+ if (fpstrapping == 0xf || fpindex == 0xf)
|
|
+ return 0;
|
|
+
|
|
+ bios->fp.mode_ptr = bios->fp.fptablepointer + headerlen +
|
|
+ recordlen * fpindex + ofs;
|
|
+
|
|
+ NV_TRACE(dev, "BIOS FP mode: %dx%d (%dkHz pixel clock)\n",
|
|
+ ROM16(bios->data[bios->fp.mode_ptr + 11]) + 1,
|
|
+ ROM16(bios->data[bios->fp.mode_ptr + 25]) + 1,
|
|
+ ROM16(bios->data[bios->fp.mode_ptr + 7]) * 10);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+bool nouveau_bios_fp_mode(struct drm_device *dev, struct drm_display_mode *mode)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nvbios *bios = &dev_priv->VBIOS;
|
|
+ uint8_t *mode_entry = &bios->data[bios->fp.mode_ptr];
|
|
+
|
|
+ if (!mode) /* just checking whether we can produce a mode */
|
|
+ return bios->fp.mode_ptr;
|
|
+
|
|
+ memset(mode, 0, sizeof(struct drm_display_mode));
|
|
+ /*
|
|
+ * For version 1.0 (version in byte 0):
|
|
+ * bytes 1-2 are "panel type", including bits on whether Colour/mono,
|
|
+ * single/dual link, and type (TFT etc.)
|
|
+ * bytes 3-6 are bits per colour in RGBX
|
|
+ */
|
|
+ mode->clock = ROM16(mode_entry[7]) * 10;
|
|
+ /* bytes 9-10 is HActive */
|
|
+ mode->hdisplay = ROM16(mode_entry[11]) + 1;
|
|
+ /*
|
|
+ * bytes 13-14 is HValid Start
|
|
+ * bytes 15-16 is HValid End
|
|
+ */
|
|
+ mode->hsync_start = ROM16(mode_entry[17]) + 1;
|
|
+ mode->hsync_end = ROM16(mode_entry[19]) + 1;
|
|
+ mode->htotal = ROM16(mode_entry[21]) + 1;
|
|
+ /* bytes 23-24, 27-30 similarly, but vertical */
|
|
+ mode->vdisplay = ROM16(mode_entry[25]) + 1;
|
|
+ mode->vsync_start = ROM16(mode_entry[31]) + 1;
|
|
+ mode->vsync_end = ROM16(mode_entry[33]) + 1;
|
|
+ mode->vtotal = ROM16(mode_entry[35]) + 1;
|
|
+ mode->flags |= (mode_entry[37] & 0x10) ?
|
|
+ DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
|
|
+ mode->flags |= (mode_entry[37] & 0x1) ?
|
|
+ DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
|
|
+ /*
|
|
+ * bytes 38-39 relate to spread spectrum settings
|
|
+ * bytes 40-43 are something to do with PWM
|
|
+ */
|
|
+
|
|
+ mode->status = MODE_OK;
|
|
+ mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
|
|
+ drm_mode_set_name(mode);
|
|
+ return bios->fp.mode_ptr;
|
|
+}
|
|
+
|
|
+int nouveau_bios_parse_lvds_table(struct drm_device *dev, int pxclk, bool *dl, bool *if_is_24bit)
|
|
+{
|
|
+ /*
|
|
+ * The LVDS table header is (mostly) described in
|
|
+ * parse_lvds_manufacturer_table_header(): the BIT header additionally
|
|
+ * contains the dual-link transition pxclk (in 10s kHz), at byte 5 - if
|
|
+ * straps are not being used for the panel, this specifies the frequency
|
|
+ * at which modes should be set up in the dual link style.
|
|
+ *
|
|
+ * Following the header, the BMP (ver 0xa) table has several records,
|
|
+ * indexed by a seperate xlat table, indexed in turn by the fp strap in
|
|
+ * EXTDEV_BOOT. Each record had a config byte, followed by 6 script
|
|
+ * numbers for use by INIT_SUB which controlled panel init and power,
|
|
+ * and finally a dword of ms to sleep between power off and on
|
|
+ * operations.
|
|
+ *
|
|
+ * In the BIT versions, the table following the header serves as an
|
|
+ * integrated config and xlat table: the records in the table are
|
|
+ * indexed by the FP strap nibble in EXTDEV_BOOT, and each record has
|
|
+ * two bytes - the first as a config byte, the second for indexing the
|
|
+ * fp mode table pointed to by the BIT 'D' table
|
|
+ *
|
|
+ * DDC is not used until after card init, so selecting the correct table
|
|
+ * entry and setting the dual link flag for EDID equipped panels,
|
|
+ * requiring tests against the native-mode pixel clock, cannot be done
|
|
+ * until later, when this function should be called with non-zero pxclk
|
|
+ */
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nvbios *bios = &dev_priv->VBIOS;
|
|
+ int fpstrapping = get_fp_strap(dev, bios), lvdsmanufacturerindex = 0;
|
|
+ struct lvdstableheader lth;
|
|
+ uint16_t lvdsofs;
|
|
+ int ret, chip_version = bios->pub.chip_version;
|
|
+
|
|
+ ret = parse_lvds_manufacturer_table_header(dev, bios, <h);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ switch (lth.lvds_ver) {
|
|
+ case 0x0a: /* pre NV40 */
|
|
+ lvdsmanufacturerindex = bios->data[
|
|
+ bios->fp.fpxlatemanufacturertableptr +
|
|
+ fpstrapping];
|
|
+
|
|
+ /* we're done if this isn't the EDID panel case */
|
|
+ if (!pxclk)
|
|
+ break;
|
|
+
|
|
+ if (chip_version < 0x25) {
|
|
+ /* nv17 behaviour
|
|
+ *
|
|
+ * It seems the old style lvds script pointer is reused
|
|
+ * to select 18/24 bit colour depth for EDID panels.
|
|
+ */
|
|
+ lvdsmanufacturerindex =
|
|
+ (bios->legacy.lvds_single_a_script_ptr & 1) ?
|
|
+ 2 : 0;
|
|
+ if (pxclk >= bios->fp.duallink_transition_clk)
|
|
+ lvdsmanufacturerindex++;
|
|
+ } else if (chip_version < 0x30) {
|
|
+ /* nv28 behaviour (off-chip encoder)
|
|
+ *
|
|
+ * nv28 does a complex dance of first using byte 121 of
|
|
+ * the EDID to choose the lvdsmanufacturerindex, then
|
|
+ * later attempting to match the EDID manufacturer and
|
|
+ * product IDs in a table (signature 'pidt' (panel id
|
|
+ * table?)), setting an lvdsmanufacturerindex of 0 and
|
|
+ * an fp strap of the match index (or 0xf if none)
|
|
+ */
|
|
+ lvdsmanufacturerindex = 0;
|
|
+ } else {
|
|
+ /* nv31, nv34 behaviour */
|
|
+ lvdsmanufacturerindex = 0;
|
|
+ if (pxclk >= bios->fp.duallink_transition_clk)
|
|
+ lvdsmanufacturerindex = 2;
|
|
+ if (pxclk >= 140000)
|
|
+ lvdsmanufacturerindex = 3;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * nvidia set the high nibble of (cr57=f, cr58) to
|
|
+ * lvdsmanufacturerindex in this case; we don't
|
|
+ */
|
|
+ break;
|
|
+ case 0x30: /* NV4x */
|
|
+ case 0x40: /* G80/G90 */
|
|
+ lvdsmanufacturerindex = fpstrapping;
|
|
+ break;
|
|
+ default:
|
|
+ NV_ERROR(dev, "LVDS table revision not currently supported\n");
|
|
+ return -ENOSYS;
|
|
+ }
|
|
+
|
|
+ lvdsofs = bios->fp.xlated_entry = bios->fp.lvdsmanufacturerpointer + lth.headerlen + lth.recordlen * lvdsmanufacturerindex;
|
|
+ switch (lth.lvds_ver) {
|
|
+ case 0x0a:
|
|
+ bios->fp.power_off_for_reset = bios->data[lvdsofs] & 1;
|
|
+ bios->fp.reset_after_pclk_change = bios->data[lvdsofs] & 2;
|
|
+ bios->fp.dual_link = bios->data[lvdsofs] & 4;
|
|
+ bios->fp.link_c_increment = bios->data[lvdsofs] & 8;
|
|
+ *if_is_24bit = bios->data[lvdsofs] & 16;
|
|
+ break;
|
|
+ case 0x30:
|
|
+ /*
|
|
+ * My money would be on there being a 24 bit interface bit in
|
|
+ * this table, but I have no example of a laptop bios with a
|
|
+ * 24 bit panel to confirm that. Hence we shout loudly if any
|
|
+ * bit other than bit 0 is set (I've not even seen bit 1)
|
|
+ */
|
|
+ if (bios->data[lvdsofs] > 1)
|
|
+ NV_ERROR(dev,
|
|
+ "You have a very unusual laptop display; please report it\n");
|
|
+ /*
|
|
+ * No sign of the "power off for reset" or "reset for panel
|
|
+ * on" bits, but it's safer to assume we should
|
|
+ */
|
|
+ bios->fp.power_off_for_reset = true;
|
|
+ bios->fp.reset_after_pclk_change = true;
|
|
+ /*
|
|
+ * It's ok lvdsofs is wrong for nv4x edid case; dual_link is
|
|
+ * over-written, and BITbit1 isn't used
|
|
+ */
|
|
+ bios->fp.dual_link = bios->data[lvdsofs] & 1;
|
|
+ bios->fp.BITbit1 = bios->data[lvdsofs] & 2;
|
|
+ bios->fp.duallink_transition_clk = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 5]) * 10;
|
|
+ break;
|
|
+ case 0x40:
|
|
+ bios->fp.dual_link = bios->data[lvdsofs] & 1;
|
|
+ bios->fp.if_is_24bit = bios->data[lvdsofs] & 2;
|
|
+ bios->fp.strapless_is_24bit = bios->data[bios->fp.lvdsmanufacturerpointer + 4];
|
|
+ bios->fp.duallink_transition_clk = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 5]) * 10;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* set dual_link flag for EDID case */
|
|
+ if (pxclk && (chip_version < 0x25 || chip_version > 0x28))
|
|
+ bios->fp.dual_link = (pxclk >= bios->fp.duallink_transition_clk);
|
|
+
|
|
+ *dl = bios->fp.dual_link;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static uint8_t *
|
|
+bios_output_config_match(struct drm_device *dev, struct dcb_entry *dcbent,
|
|
+ uint16_t record, int record_len, int record_nr)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nvbios *bios = &dev_priv->VBIOS;
|
|
+ uint32_t entry;
|
|
+ uint16_t table;
|
|
+ int i, v;
|
|
+
|
|
+ for (i = 0; i < record_nr; i++, record += record_len) {
|
|
+ table = ROM16(bios->data[record]);
|
|
+ if (!table)
|
|
+ continue;
|
|
+ entry = ROM32(bios->data[table]);
|
|
+
|
|
+ v = (entry & 0x000f0000) >> 16;
|
|
+ if (!(v & dcbent->or))
|
|
+ continue;
|
|
+
|
|
+ v = (entry & 0x000000f0) >> 4;
|
|
+ if (v != dcbent->location)
|
|
+ continue;
|
|
+
|
|
+ v = (entry & 0x0000000f);
|
|
+ if (v != dcbent->type)
|
|
+ continue;
|
|
+
|
|
+ return &bios->data[table];
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+void *
|
|
+nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent,
|
|
+ int *length)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nvbios *bios = &dev_priv->VBIOS;
|
|
+ uint8_t *table;
|
|
+
|
|
+ if (!bios->display.dp_table_ptr) {
|
|
+ NV_ERROR(dev, "No pointer to DisplayPort table\n");
|
|
+ return NULL;
|
|
+ }
|
|
+ table = &bios->data[bios->display.dp_table_ptr];
|
|
+
|
|
+ if (table[0] != 0x21) {
|
|
+ NV_ERROR(dev, "DisplayPort table version 0x%02x unknown\n",
|
|
+ table[0]);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ *length = table[4];
|
|
+ return bios_output_config_match(dev, dcbent,
|
|
+ bios->display.dp_table_ptr + table[1],
|
|
+ table[2], table[3]);
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
|
|
+ uint32_t sub, int pxclk)
|
|
+{
|
|
+ /*
|
|
+ * The display script table is located by the BIT 'U' table.
|
|
+ *
|
|
+ * It contains an array of pointers to various tables describing
|
|
+ * a particular output type. The first 32-bits of the output
|
|
+ * tables contains similar information to a DCB entry, and is
|
|
+ * used to decide whether that particular table is suitable for
|
|
+ * the output you want to access.
|
|
+ *
|
|
+ * The "record header length" field here seems to indicate the
|
|
+ * offset of the first configuration entry in the output tables.
|
|
+ * This is 10 on most cards I've seen, but 12 has been witnessed
|
|
+ * on DP cards, and there's another script pointer within the
|
|
+ * header.
|
|
+ *
|
|
+ * offset + 0 ( 8 bits): version
|
|
+ * offset + 1 ( 8 bits): header length
|
|
+ * offset + 2 ( 8 bits): record length
|
|
+ * offset + 3 ( 8 bits): number of records
|
|
+ * offset + 4 ( 8 bits): record header length
|
|
+ * offset + 5 (16 bits): pointer to first output script table
|
|
+ */
|
|
+
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nvbios *bios = &dev_priv->VBIOS;
|
|
+ uint8_t *table = &bios->data[bios->display.script_table_ptr];
|
|
+ uint8_t *otable = NULL;
|
|
+ uint16_t script;
|
|
+ int i = 0;
|
|
+
|
|
+ if (!bios->display.script_table_ptr) {
|
|
+ NV_ERROR(dev, "No pointer to output script table\n");
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Nothing useful has been in any of the pre-2.0 tables I've seen,
|
|
+ * so until they are, we really don't need to care.
|
|
+ */
|
|
+ if (table[0] < 0x20)
|
|
+ return 1;
|
|
+
|
|
+ if (table[0] != 0x20 && table[0] != 0x21) {
|
|
+ NV_ERROR(dev, "Output script table version 0x%02x unknown\n",
|
|
+ table[0]);
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * The output script tables describing a particular output type
|
|
+ * look as follows:
|
|
+ *
|
|
+ * offset + 0 (32 bits): output this table matches (hash of DCB)
|
|
+ * offset + 4 ( 8 bits): unknown
|
|
+ * offset + 5 ( 8 bits): number of configurations
|
|
+ * offset + 6 (16 bits): pointer to some script
|
|
+ * offset + 8 (16 bits): pointer to some script
|
|
+ *
|
|
+ * headerlen == 10
|
|
+ * offset + 10 : configuration 0
|
|
+ *
|
|
+ * headerlen == 12
|
|
+ * offset + 10 : pointer to some script
|
|
+ * offset + 12 : configuration 0
|
|
+ *
|
|
+ * Each config entry is as follows:
|
|
+ *
|
|
+ * offset + 0 (16 bits): unknown, assumed to be a match value
|
|
+ * offset + 2 (16 bits): pointer to script table (clock set?)
|
|
+ * offset + 4 (16 bits): pointer to script table (reset?)
|
|
+ *
|
|
+ * There doesn't appear to be a count value to say how many
|
|
+ * entries exist in each script table, instead, a 0 value in
|
|
+ * the first 16-bit word seems to indicate both the end of the
|
|
+ * list and the default entry. The second 16-bit word in the
|
|
+ * script tables is a pointer to the script to execute.
|
|
+ */
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "Searching for output entry for %d %d %d\n",
|
|
+ dcbent->type, dcbent->location, dcbent->or);
|
|
+ otable = bios_output_config_match(dev, dcbent, table[1] +
|
|
+ bios->display.script_table_ptr,
|
|
+ table[2], table[3]);
|
|
+ if (!otable) {
|
|
+ NV_ERROR(dev, "Couldn't find matching output script table\n");
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ if (pxclk < -2 || pxclk > 0) {
|
|
+ /* Try to find matching script table entry */
|
|
+ for (i = 0; i < otable[5]; i++) {
|
|
+ if (ROM16(otable[table[4] + i*6]) == sub)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (i == otable[5]) {
|
|
+ NV_ERROR(dev, "Table 0x%04x not found for %d/%d, "
|
|
+ "using first\n",
|
|
+ sub, dcbent->type, dcbent->or);
|
|
+ i = 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (pxclk == 0) {
|
|
+ script = ROM16(otable[6]);
|
|
+ if (!script) {
|
|
+ NV_DEBUG_KMS(dev, "output script 0 not found\n");
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ NV_TRACE(dev, "0x%04X: parsing output script 0\n", script);
|
|
+ nouveau_bios_run_init_table(dev, script, dcbent);
|
|
+ } else
|
|
+ if (pxclk == -1) {
|
|
+ script = ROM16(otable[8]);
|
|
+ if (!script) {
|
|
+ NV_DEBUG_KMS(dev, "output script 1 not found\n");
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ NV_TRACE(dev, "0x%04X: parsing output script 1\n", script);
|
|
+ nouveau_bios_run_init_table(dev, script, dcbent);
|
|
+ } else
|
|
+ if (pxclk == -2) {
|
|
+ if (table[4] >= 12)
|
|
+ script = ROM16(otable[10]);
|
|
+ else
|
|
+ script = 0;
|
|
+ if (!script) {
|
|
+ NV_DEBUG_KMS(dev, "output script 2 not found\n");
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ NV_TRACE(dev, "0x%04X: parsing output script 2\n", script);
|
|
+ nouveau_bios_run_init_table(dev, script, dcbent);
|
|
+ } else
|
|
+ if (pxclk > 0) {
|
|
+ script = ROM16(otable[table[4] + i*6 + 2]);
|
|
+ if (script)
|
|
+ script = clkcmptable(bios, script, pxclk);
|
|
+ if (!script) {
|
|
+ NV_ERROR(dev, "clock script 0 not found\n");
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ NV_TRACE(dev, "0x%04X: parsing clock script 0\n", script);
|
|
+ nouveau_bios_run_init_table(dev, script, dcbent);
|
|
+ } else
|
|
+ if (pxclk < 0) {
|
|
+ script = ROM16(otable[table[4] + i*6 + 4]);
|
|
+ if (script)
|
|
+ script = clkcmptable(bios, script, -pxclk);
|
|
+ if (!script) {
|
|
+ NV_DEBUG_KMS(dev, "clock script 1 not found\n");
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ NV_TRACE(dev, "0x%04X: parsing clock script 1\n", script);
|
|
+ nouveau_bios_run_init_table(dev, script, dcbent);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+int run_tmds_table(struct drm_device *dev, struct dcb_entry *dcbent, int head, int pxclk)
|
|
+{
|
|
+ /*
|
|
+ * the pxclk parameter is in kHz
|
|
+ *
|
|
+ * This runs the TMDS regs setting code found on BIT bios cards
|
|
+ *
|
|
+ * For ffs(or) == 1 use the first table, for ffs(or) == 2 and
|
|
+ * ffs(or) == 3, use the second.
|
|
+ */
|
|
+
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nvbios *bios = &dev_priv->VBIOS;
|
|
+ int cv = bios->pub.chip_version;
|
|
+ uint16_t clktable = 0, scriptptr;
|
|
+ uint32_t sel_clk_binding, sel_clk;
|
|
+
|
|
+ /* pre-nv17 off-chip tmds uses scripts, post nv17 doesn't */
|
|
+ if (cv >= 0x17 && cv != 0x1a && cv != 0x20 &&
|
|
+ dcbent->location != DCB_LOC_ON_CHIP)
|
|
+ return 0;
|
|
+
|
|
+ switch (ffs(dcbent->or)) {
|
|
+ case 1:
|
|
+ clktable = bios->tmds.output0_script_ptr;
|
|
+ break;
|
|
+ case 2:
|
|
+ case 3:
|
|
+ clktable = bios->tmds.output1_script_ptr;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (!clktable) {
|
|
+ NV_ERROR(dev, "Pixel clock comparison table not found\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ scriptptr = clkcmptable(bios, clktable, pxclk);
|
|
+
|
|
+ if (!scriptptr) {
|
|
+ NV_ERROR(dev, "TMDS output init script not found\n");
|
|
+ return -ENOENT;
|
|
+ }
|
|
+
|
|
+ /* don't let script change pll->head binding */
|
|
+ sel_clk_binding = bios_rd32(bios, NV_PRAMDAC_SEL_CLK) & 0x50000;
|
|
+ run_digital_op_script(dev, scriptptr, dcbent, head, pxclk >= 165000);
|
|
+ sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK) & ~0x50000;
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, sel_clk | sel_clk_binding);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims *pll_lim)
|
|
+{
|
|
+ /*
|
|
+ * PLL limits table
|
|
+ *
|
|
+ * Version 0x10: NV30, NV31
|
|
+ * One byte header (version), one record of 24 bytes
|
|
+ * Version 0x11: NV36 - Not implemented
|
|
+ * Seems to have same record style as 0x10, but 3 records rather than 1
|
|
+ * Version 0x20: Found on Geforce 6 cards
|
|
+ * Trivial 4 byte BIT header. 31 (0x1f) byte record length
|
|
+ * Version 0x21: Found on Geforce 7, 8 and some Geforce 6 cards
|
|
+ * 5 byte header, fifth byte of unknown purpose. 35 (0x23) byte record
|
|
+ * length in general, some (integrated) have an extra configuration byte
|
|
+ * Version 0x30: Found on Geforce 8, separates the register mapping
|
|
+ * from the limits tables.
|
|
+ */
|
|
+
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nvbios *bios = &dev_priv->VBIOS;
|
|
+ int cv = bios->pub.chip_version, pllindex = 0;
|
|
+ uint8_t pll_lim_ver = 0, headerlen = 0, recordlen = 0, entries = 0;
|
|
+ uint32_t crystal_strap_mask, crystal_straps;
|
|
+
|
|
+ if (!bios->pll_limit_tbl_ptr) {
|
|
+ if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 ||
|
|
+ cv >= 0x40) {
|
|
+ NV_ERROR(dev, "Pointer to PLL limits table invalid\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ } else
|
|
+ pll_lim_ver = bios->data[bios->pll_limit_tbl_ptr];
|
|
+
|
|
+ crystal_strap_mask = 1 << 6;
|
|
+ /* open coded dev->twoHeads test */
|
|
+ if (cv > 0x10 && cv != 0x15 && cv != 0x1a && cv != 0x20)
|
|
+ crystal_strap_mask |= 1 << 22;
|
|
+ crystal_straps = nvReadEXTDEV(dev, NV_PEXTDEV_BOOT_0) &
|
|
+ crystal_strap_mask;
|
|
+
|
|
+ switch (pll_lim_ver) {
|
|
+ /*
|
|
+ * We use version 0 to indicate a pre limit table bios (single stage
|
|
+ * pll) and load the hard coded limits instead.
|
|
+ */
|
|
+ case 0:
|
|
+ break;
|
|
+ case 0x10:
|
|
+ case 0x11:
|
|
+ /*
|
|
+ * Strictly v0x11 has 3 entries, but the last two don't seem
|
|
+ * to get used.
|
|
+ */
|
|
+ headerlen = 1;
|
|
+ recordlen = 0x18;
|
|
+ entries = 1;
|
|
+ pllindex = 0;
|
|
+ break;
|
|
+ case 0x20:
|
|
+ case 0x21:
|
|
+ case 0x30:
|
|
+ case 0x40:
|
|
+ headerlen = bios->data[bios->pll_limit_tbl_ptr + 1];
|
|
+ recordlen = bios->data[bios->pll_limit_tbl_ptr + 2];
|
|
+ entries = bios->data[bios->pll_limit_tbl_ptr + 3];
|
|
+ break;
|
|
+ default:
|
|
+ NV_ERROR(dev, "PLL limits table revision 0x%X not currently "
|
|
+ "supported\n", pll_lim_ver);
|
|
+ return -ENOSYS;
|
|
+ }
|
|
+
|
|
+ /* initialize all members to zero */
|
|
+ memset(pll_lim, 0, sizeof(struct pll_lims));
|
|
+
|
|
+ if (pll_lim_ver == 0x10 || pll_lim_ver == 0x11) {
|
|
+ uint8_t *pll_rec = &bios->data[bios->pll_limit_tbl_ptr + headerlen + recordlen * pllindex];
|
|
+
|
|
+ pll_lim->vco1.minfreq = ROM32(pll_rec[0]);
|
|
+ pll_lim->vco1.maxfreq = ROM32(pll_rec[4]);
|
|
+ pll_lim->vco2.minfreq = ROM32(pll_rec[8]);
|
|
+ pll_lim->vco2.maxfreq = ROM32(pll_rec[12]);
|
|
+ pll_lim->vco1.min_inputfreq = ROM32(pll_rec[16]);
|
|
+ pll_lim->vco2.min_inputfreq = ROM32(pll_rec[20]);
|
|
+ pll_lim->vco1.max_inputfreq = pll_lim->vco2.max_inputfreq = INT_MAX;
|
|
+
|
|
+ /* these values taken from nv30/31/36 */
|
|
+ pll_lim->vco1.min_n = 0x1;
|
|
+ if (cv == 0x36)
|
|
+ pll_lim->vco1.min_n = 0x5;
|
|
+ pll_lim->vco1.max_n = 0xff;
|
|
+ pll_lim->vco1.min_m = 0x1;
|
|
+ pll_lim->vco1.max_m = 0xd;
|
|
+ pll_lim->vco2.min_n = 0x4;
|
|
+ /*
|
|
+ * On nv30, 31, 36 (i.e. all cards with two stage PLLs with this
|
|
+ * table version (apart from nv35)), N2 is compared to
|
|
+ * maxN2 (0x46) and 10 * maxM2 (0x4), so set maxN2 to 0x28 and
|
|
+ * save a comparison
|
|
+ */
|
|
+ pll_lim->vco2.max_n = 0x28;
|
|
+ if (cv == 0x30 || cv == 0x35)
|
|
+ /* only 5 bits available for N2 on nv30/35 */
|
|
+ pll_lim->vco2.max_n = 0x1f;
|
|
+ pll_lim->vco2.min_m = 0x1;
|
|
+ pll_lim->vco2.max_m = 0x4;
|
|
+ pll_lim->max_log2p = 0x7;
|
|
+ pll_lim->max_usable_log2p = 0x6;
|
|
+ } else if (pll_lim_ver == 0x20 || pll_lim_ver == 0x21) {
|
|
+ uint16_t plloffs = bios->pll_limit_tbl_ptr + headerlen;
|
|
+ uint32_t reg = 0; /* default match */
|
|
+ uint8_t *pll_rec;
|
|
+ int i;
|
|
+
|
|
+ /*
|
|
+ * First entry is default match, if nothing better. warn if
|
|
+ * reg field nonzero
|
|
+ */
|
|
+ if (ROM32(bios->data[plloffs]))
|
|
+ NV_WARN(dev, "Default PLL limit entry has non-zero "
|
|
+ "register field\n");
|
|
+
|
|
+ if (limit_match > MAX_PLL_TYPES)
|
|
+ /* we've been passed a reg as the match */
|
|
+ reg = limit_match;
|
|
+ else /* limit match is a pll type */
|
|
+ for (i = 1; i < entries && !reg; i++) {
|
|
+ uint32_t cmpreg = ROM32(bios->data[plloffs + recordlen * i]);
|
|
+
|
|
+ if (limit_match == NVPLL &&
|
|
+ (cmpreg == NV_PRAMDAC_NVPLL_COEFF || cmpreg == 0x4000))
|
|
+ reg = cmpreg;
|
|
+ if (limit_match == MPLL &&
|
|
+ (cmpreg == NV_PRAMDAC_MPLL_COEFF || cmpreg == 0x4020))
|
|
+ reg = cmpreg;
|
|
+ if (limit_match == VPLL1 &&
|
|
+ (cmpreg == NV_PRAMDAC_VPLL_COEFF || cmpreg == 0x4010))
|
|
+ reg = cmpreg;
|
|
+ if (limit_match == VPLL2 &&
|
|
+ (cmpreg == NV_RAMDAC_VPLL2 || cmpreg == 0x4018))
|
|
+ reg = cmpreg;
|
|
+ }
|
|
+
|
|
+ for (i = 1; i < entries; i++)
|
|
+ if (ROM32(bios->data[plloffs + recordlen * i]) == reg) {
|
|
+ pllindex = i;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ pll_rec = &bios->data[plloffs + recordlen * pllindex];
|
|
+
|
|
+ BIOSLOG(bios, "Loading PLL limits for reg 0x%08x\n",
|
|
+ pllindex ? reg : 0);
|
|
+
|
|
+ /*
|
|
+ * Frequencies are stored in tables in MHz, kHz are more
|
|
+ * useful, so we convert.
|
|
+ */
|
|
+
|
|
+ /* What output frequencies can each VCO generate? */
|
|
+ pll_lim->vco1.minfreq = ROM16(pll_rec[4]) * 1000;
|
|
+ pll_lim->vco1.maxfreq = ROM16(pll_rec[6]) * 1000;
|
|
+ pll_lim->vco2.minfreq = ROM16(pll_rec[8]) * 1000;
|
|
+ pll_lim->vco2.maxfreq = ROM16(pll_rec[10]) * 1000;
|
|
+
|
|
+ /* What input frequencies they accept (past the m-divider)? */
|
|
+ pll_lim->vco1.min_inputfreq = ROM16(pll_rec[12]) * 1000;
|
|
+ pll_lim->vco2.min_inputfreq = ROM16(pll_rec[14]) * 1000;
|
|
+ pll_lim->vco1.max_inputfreq = ROM16(pll_rec[16]) * 1000;
|
|
+ pll_lim->vco2.max_inputfreq = ROM16(pll_rec[18]) * 1000;
|
|
+
|
|
+ /* What values are accepted as multiplier and divider? */
|
|
+ pll_lim->vco1.min_n = pll_rec[20];
|
|
+ pll_lim->vco1.max_n = pll_rec[21];
|
|
+ pll_lim->vco1.min_m = pll_rec[22];
|
|
+ pll_lim->vco1.max_m = pll_rec[23];
|
|
+ pll_lim->vco2.min_n = pll_rec[24];
|
|
+ pll_lim->vco2.max_n = pll_rec[25];
|
|
+ pll_lim->vco2.min_m = pll_rec[26];
|
|
+ pll_lim->vco2.max_m = pll_rec[27];
|
|
+
|
|
+ pll_lim->max_usable_log2p = pll_lim->max_log2p = pll_rec[29];
|
|
+ if (pll_lim->max_log2p > 0x7)
|
|
+ /* pll decoding in nv_hw.c assumes never > 7 */
|
|
+ NV_WARN(dev, "Max log2 P value greater than 7 (%d)\n",
|
|
+ pll_lim->max_log2p);
|
|
+ if (cv < 0x60)
|
|
+ pll_lim->max_usable_log2p = 0x6;
|
|
+ pll_lim->log2p_bias = pll_rec[30];
|
|
+
|
|
+ if (recordlen > 0x22)
|
|
+ pll_lim->refclk = ROM32(pll_rec[31]);
|
|
+
|
|
+ if (recordlen > 0x23 && pll_rec[35])
|
|
+ NV_WARN(dev,
|
|
+ "Bits set in PLL configuration byte (%x)\n",
|
|
+ pll_rec[35]);
|
|
+
|
|
+ /* C51 special not seen elsewhere */
|
|
+ if (cv == 0x51 && !pll_lim->refclk) {
|
|
+ uint32_t sel_clk = bios_rd32(bios, NV_PRAMDAC_SEL_CLK);
|
|
+
|
|
+ if (((limit_match == NV_PRAMDAC_VPLL_COEFF || limit_match == VPLL1) && sel_clk & 0x20) ||
|
|
+ ((limit_match == NV_RAMDAC_VPLL2 || limit_match == VPLL2) && sel_clk & 0x80)) {
|
|
+ if (bios_idxprt_rd(bios, NV_CIO_CRX__COLOR, NV_CIO_CRE_CHIP_ID_INDEX) < 0xa3)
|
|
+ pll_lim->refclk = 200000;
|
|
+ else
|
|
+ pll_lim->refclk = 25000;
|
|
+ }
|
|
+ }
|
|
+ } else if (pll_lim_ver == 0x30) { /* ver 0x30 */
|
|
+ uint8_t *entry = &bios->data[bios->pll_limit_tbl_ptr + headerlen];
|
|
+ uint8_t *record = NULL;
|
|
+ int i;
|
|
+
|
|
+ BIOSLOG(bios, "Loading PLL limits for register 0x%08x\n",
|
|
+ limit_match);
|
|
+
|
|
+ for (i = 0; i < entries; i++, entry += recordlen) {
|
|
+ if (ROM32(entry[3]) == limit_match) {
|
|
+ record = &bios->data[ROM16(entry[1])];
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!record) {
|
|
+ NV_ERROR(dev, "Register 0x%08x not found in PLL "
|
|
+ "limits table", limit_match);
|
|
+ return -ENOENT;
|
|
+ }
|
|
+
|
|
+ pll_lim->vco1.minfreq = ROM16(record[0]) * 1000;
|
|
+ pll_lim->vco1.maxfreq = ROM16(record[2]) * 1000;
|
|
+ pll_lim->vco2.minfreq = ROM16(record[4]) * 1000;
|
|
+ pll_lim->vco2.maxfreq = ROM16(record[6]) * 1000;
|
|
+ pll_lim->vco1.min_inputfreq = ROM16(record[8]) * 1000;
|
|
+ pll_lim->vco2.min_inputfreq = ROM16(record[10]) * 1000;
|
|
+ pll_lim->vco1.max_inputfreq = ROM16(record[12]) * 1000;
|
|
+ pll_lim->vco2.max_inputfreq = ROM16(record[14]) * 1000;
|
|
+ pll_lim->vco1.min_n = record[16];
|
|
+ pll_lim->vco1.max_n = record[17];
|
|
+ pll_lim->vco1.min_m = record[18];
|
|
+ pll_lim->vco1.max_m = record[19];
|
|
+ pll_lim->vco2.min_n = record[20];
|
|
+ pll_lim->vco2.max_n = record[21];
|
|
+ pll_lim->vco2.min_m = record[22];
|
|
+ pll_lim->vco2.max_m = record[23];
|
|
+ pll_lim->max_usable_log2p = pll_lim->max_log2p = record[25];
|
|
+ pll_lim->log2p_bias = record[27];
|
|
+ pll_lim->refclk = ROM32(record[28]);
|
|
+ } else if (pll_lim_ver) { /* ver 0x40 */
|
|
+ uint8_t *entry = &bios->data[bios->pll_limit_tbl_ptr + headerlen];
|
|
+ uint8_t *record = NULL;
|
|
+ int i;
|
|
+
|
|
+ BIOSLOG(bios, "Loading PLL limits for register 0x%08x\n",
|
|
+ limit_match);
|
|
+
|
|
+ for (i = 0; i < entries; i++, entry += recordlen) {
|
|
+ if (ROM32(entry[3]) == limit_match) {
|
|
+ record = &bios->data[ROM16(entry[1])];
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!record) {
|
|
+ NV_ERROR(dev, "Register 0x%08x not found in PLL "
|
|
+ "limits table", limit_match);
|
|
+ return -ENOENT;
|
|
+ }
|
|
+
|
|
+ pll_lim->vco1.minfreq = ROM16(record[0]) * 1000;
|
|
+ pll_lim->vco1.maxfreq = ROM16(record[2]) * 1000;
|
|
+ pll_lim->vco1.min_inputfreq = ROM16(record[4]) * 1000;
|
|
+ pll_lim->vco1.max_inputfreq = ROM16(record[6]) * 1000;
|
|
+ pll_lim->vco1.min_m = record[8];
|
|
+ pll_lim->vco1.max_m = record[9];
|
|
+ pll_lim->vco1.min_n = record[10];
|
|
+ pll_lim->vco1.max_n = record[11];
|
|
+ pll_lim->min_p = record[12];
|
|
+ pll_lim->max_p = record[13];
|
|
+ /* where did this go to?? */
|
|
+ if (limit_match == 0x00614100 || limit_match == 0x00614900)
|
|
+ pll_lim->refclk = 27000;
|
|
+ else
|
|
+ pll_lim->refclk = 100000;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * By now any valid limit table ought to have set a max frequency for
|
|
+ * vco1, so if it's zero it's either a pre limit table bios, or one
|
|
+ * with an empty limit table (seen on nv18)
|
|
+ */
|
|
+ if (!pll_lim->vco1.maxfreq) {
|
|
+ pll_lim->vco1.minfreq = bios->fminvco;
|
|
+ pll_lim->vco1.maxfreq = bios->fmaxvco;
|
|
+ pll_lim->vco1.min_inputfreq = 0;
|
|
+ pll_lim->vco1.max_inputfreq = INT_MAX;
|
|
+ pll_lim->vco1.min_n = 0x1;
|
|
+ pll_lim->vco1.max_n = 0xff;
|
|
+ pll_lim->vco1.min_m = 0x1;
|
|
+ if (crystal_straps == 0) {
|
|
+ /* nv05 does this, nv11 doesn't, nv10 unknown */
|
|
+ if (cv < 0x11)
|
|
+ pll_lim->vco1.min_m = 0x7;
|
|
+ pll_lim->vco1.max_m = 0xd;
|
|
+ } else {
|
|
+ if (cv < 0x11)
|
|
+ pll_lim->vco1.min_m = 0x8;
|
|
+ pll_lim->vco1.max_m = 0xe;
|
|
+ }
|
|
+ if (cv < 0x17 || cv == 0x1a || cv == 0x20)
|
|
+ pll_lim->max_log2p = 4;
|
|
+ else
|
|
+ pll_lim->max_log2p = 5;
|
|
+ pll_lim->max_usable_log2p = pll_lim->max_log2p;
|
|
+ }
|
|
+
|
|
+ if (!pll_lim->refclk)
|
|
+ switch (crystal_straps) {
|
|
+ case 0:
|
|
+ pll_lim->refclk = 13500;
|
|
+ break;
|
|
+ case (1 << 6):
|
|
+ pll_lim->refclk = 14318;
|
|
+ break;
|
|
+ case (1 << 22):
|
|
+ pll_lim->refclk = 27000;
|
|
+ break;
|
|
+ case (1 << 22 | 1 << 6):
|
|
+ pll_lim->refclk = 25000;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+#if 0 /* for easy debugging */
|
|
+ ErrorF("pll.vco1.minfreq: %d\n", pll_lim->vco1.minfreq);
|
|
+ ErrorF("pll.vco1.maxfreq: %d\n", pll_lim->vco1.maxfreq);
|
|
+ ErrorF("pll.vco2.minfreq: %d\n", pll_lim->vco2.minfreq);
|
|
+ ErrorF("pll.vco2.maxfreq: %d\n", pll_lim->vco2.maxfreq);
|
|
+
|
|
+ ErrorF("pll.vco1.min_inputfreq: %d\n", pll_lim->vco1.min_inputfreq);
|
|
+ ErrorF("pll.vco1.max_inputfreq: %d\n", pll_lim->vco1.max_inputfreq);
|
|
+ ErrorF("pll.vco2.min_inputfreq: %d\n", pll_lim->vco2.min_inputfreq);
|
|
+ ErrorF("pll.vco2.max_inputfreq: %d\n", pll_lim->vco2.max_inputfreq);
|
|
+
|
|
+ ErrorF("pll.vco1.min_n: %d\n", pll_lim->vco1.min_n);
|
|
+ ErrorF("pll.vco1.max_n: %d\n", pll_lim->vco1.max_n);
|
|
+ ErrorF("pll.vco1.min_m: %d\n", pll_lim->vco1.min_m);
|
|
+ ErrorF("pll.vco1.max_m: %d\n", pll_lim->vco1.max_m);
|
|
+ ErrorF("pll.vco2.min_n: %d\n", pll_lim->vco2.min_n);
|
|
+ ErrorF("pll.vco2.max_n: %d\n", pll_lim->vco2.max_n);
|
|
+ ErrorF("pll.vco2.min_m: %d\n", pll_lim->vco2.min_m);
|
|
+ ErrorF("pll.vco2.max_m: %d\n", pll_lim->vco2.max_m);
|
|
+
|
|
+ ErrorF("pll.max_log2p: %d\n", pll_lim->max_log2p);
|
|
+ ErrorF("pll.log2p_bias: %d\n", pll_lim->log2p_bias);
|
|
+
|
|
+ ErrorF("pll.refclk: %d\n", pll_lim->refclk);
|
|
+#endif
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void parse_bios_version(struct drm_device *dev, struct nvbios *bios, uint16_t offset)
|
|
+{
|
|
+ /*
|
|
+ * offset + 0 (8 bits): Micro version
|
|
+ * offset + 1 (8 bits): Minor version
|
|
+ * offset + 2 (8 bits): Chip version
|
|
+ * offset + 3 (8 bits): Major version
|
|
+ */
|
|
+
|
|
+ bios->major_version = bios->data[offset + 3];
|
|
+ bios->pub.chip_version = bios->data[offset + 2];
|
|
+ NV_TRACE(dev, "Bios version %02x.%02x.%02x.%02x\n",
|
|
+ bios->data[offset + 3], bios->data[offset + 2],
|
|
+ bios->data[offset + 1], bios->data[offset]);
|
|
+}
|
|
+
|
|
+static void parse_script_table_pointers(struct nvbios *bios, uint16_t offset)
|
|
+{
|
|
+ /*
|
|
+ * Parses the init table segment for pointers used in script execution.
|
|
+ *
|
|
+ * offset + 0 (16 bits): init script tables pointer
|
|
+ * offset + 2 (16 bits): macro index table pointer
|
|
+ * offset + 4 (16 bits): macro table pointer
|
|
+ * offset + 6 (16 bits): condition table pointer
|
|
+ * offset + 8 (16 bits): io condition table pointer
|
|
+ * offset + 10 (16 bits): io flag condition table pointer
|
|
+ * offset + 12 (16 bits): init function table pointer
|
|
+ */
|
|
+
|
|
+ bios->init_script_tbls_ptr = ROM16(bios->data[offset]);
|
|
+ bios->macro_index_tbl_ptr = ROM16(bios->data[offset + 2]);
|
|
+ bios->macro_tbl_ptr = ROM16(bios->data[offset + 4]);
|
|
+ bios->condition_tbl_ptr = ROM16(bios->data[offset + 6]);
|
|
+ bios->io_condition_tbl_ptr = ROM16(bios->data[offset + 8]);
|
|
+ bios->io_flag_condition_tbl_ptr = ROM16(bios->data[offset + 10]);
|
|
+ bios->init_function_tbl_ptr = ROM16(bios->data[offset + 12]);
|
|
+}
|
|
+
|
|
+static int parse_bit_A_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
|
|
+{
|
|
+ /*
|
|
+ * Parses the load detect values for g80 cards.
|
|
+ *
|
|
+ * offset + 0 (16 bits): loadval table pointer
|
|
+ */
|
|
+
|
|
+ uint16_t load_table_ptr;
|
|
+ uint8_t version, headerlen, entrylen, num_entries;
|
|
+
|
|
+ if (bitentry->length != 3) {
|
|
+ NV_ERROR(dev, "Do not understand BIT A table\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ load_table_ptr = ROM16(bios->data[bitentry->offset]);
|
|
+
|
|
+ if (load_table_ptr == 0x0) {
|
|
+ NV_ERROR(dev, "Pointer to BIT loadval table invalid\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ version = bios->data[load_table_ptr];
|
|
+
|
|
+ if (version != 0x10) {
|
|
+ NV_ERROR(dev, "BIT loadval table version %d.%d not supported\n",
|
|
+ version >> 4, version & 0xF);
|
|
+ return -ENOSYS;
|
|
+ }
|
|
+
|
|
+ headerlen = bios->data[load_table_ptr + 1];
|
|
+ entrylen = bios->data[load_table_ptr + 2];
|
|
+ num_entries = bios->data[load_table_ptr + 3];
|
|
+
|
|
+ if (headerlen != 4 || entrylen != 4 || num_entries != 2) {
|
|
+ NV_ERROR(dev, "Do not understand BIT loadval table\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* First entry is normal dac, 2nd tv-out perhaps? */
|
|
+ bios->pub.dactestval = ROM32(bios->data[load_table_ptr + headerlen]) & 0x3ff;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int parse_bit_C_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
|
|
+{
|
|
+ /*
|
|
+ * offset + 8 (16 bits): PLL limits table pointer
|
|
+ *
|
|
+ * There's more in here, but that's unknown.
|
|
+ */
|
|
+
|
|
+ if (bitentry->length < 10) {
|
|
+ NV_ERROR(dev, "Do not understand BIT C table\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ bios->pll_limit_tbl_ptr = ROM16(bios->data[bitentry->offset + 8]);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int parse_bit_display_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
|
|
+{
|
|
+ /*
|
|
+ * Parses the flat panel table segment that the bit entry points to.
|
|
+ * Starting at bitentry->offset:
|
|
+ *
|
|
+ * offset + 0 (16 bits): ??? table pointer - seems to have 18 byte
|
|
+ * records beginning with a freq.
|
|
+ * offset + 2 (16 bits): mode table pointer
|
|
+ */
|
|
+
|
|
+ if (bitentry->length != 4) {
|
|
+ NV_ERROR(dev, "Do not understand BIT display table\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ bios->fp.fptablepointer = ROM16(bios->data[bitentry->offset + 2]);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int parse_bit_init_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
|
|
+{
|
|
+ /*
|
|
+ * Parses the init table segment that the bit entry points to.
|
|
+ *
|
|
+ * See parse_script_table_pointers for layout
|
|
+ */
|
|
+
|
|
+ if (bitentry->length < 14) {
|
|
+ NV_ERROR(dev, "Do not understand init table\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ parse_script_table_pointers(bios, bitentry->offset);
|
|
+
|
|
+ if (bitentry->length >= 16)
|
|
+ bios->some_script_ptr = ROM16(bios->data[bitentry->offset + 14]);
|
|
+ if (bitentry->length >= 18)
|
|
+ bios->init96_tbl_ptr = ROM16(bios->data[bitentry->offset + 16]);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int parse_bit_i_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
|
|
+{
|
|
+ /*
|
|
+ * BIT 'i' (info?) table
|
|
+ *
|
|
+ * offset + 0 (32 bits): BIOS version dword (as in B table)
|
|
+ * offset + 5 (8 bits): BIOS feature byte (same as for BMP?)
|
|
+ * offset + 13 (16 bits): pointer to table containing DAC load
|
|
+ * detection comparison values
|
|
+ *
|
|
+ * There's other things in the table, purpose unknown
|
|
+ */
|
|
+
|
|
+ uint16_t daccmpoffset;
|
|
+ uint8_t dacver, dacheaderlen;
|
|
+
|
|
+ if (bitentry->length < 6) {
|
|
+ NV_ERROR(dev, "BIT i table too short for needed information\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ parse_bios_version(dev, bios, bitentry->offset);
|
|
+
|
|
+ /*
|
|
+ * bit 4 seems to indicate a mobile bios (doesn't suffer from BMP's
|
|
+ * Quadro identity crisis), other bits possibly as for BMP feature byte
|
|
+ */
|
|
+ bios->feature_byte = bios->data[bitentry->offset + 5];
|
|
+ bios->is_mobile = bios->feature_byte & FEATURE_MOBILE;
|
|
+
|
|
+ if (bitentry->length < 15) {
|
|
+ NV_WARN(dev, "BIT i table not long enough for DAC load "
|
|
+ "detection comparison table\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ daccmpoffset = ROM16(bios->data[bitentry->offset + 13]);
|
|
+
|
|
+ /* doesn't exist on g80 */
|
|
+ if (!daccmpoffset)
|
|
+ return 0;
|
|
+
|
|
+ /*
|
|
+ * The first value in the table, following the header, is the
|
|
+ * comparison value, the second entry is a comparison value for
|
|
+ * TV load detection.
|
|
+ */
|
|
+
|
|
+ dacver = bios->data[daccmpoffset];
|
|
+ dacheaderlen = bios->data[daccmpoffset + 1];
|
|
+
|
|
+ if (dacver != 0x00 && dacver != 0x10) {
|
|
+ NV_WARN(dev, "DAC load detection comparison table version "
|
|
+ "%d.%d not known\n", dacver >> 4, dacver & 0xf);
|
|
+ return -ENOSYS;
|
|
+ }
|
|
+
|
|
+ bios->pub.dactestval = ROM32(bios->data[daccmpoffset + dacheaderlen]);
|
|
+ bios->pub.tvdactestval = ROM32(bios->data[daccmpoffset + dacheaderlen + 4]);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int parse_bit_lvds_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
|
|
+{
|
|
+ /*
|
|
+ * Parses the LVDS table segment that the bit entry points to.
|
|
+ * Starting at bitentry->offset:
|
|
+ *
|
|
+ * offset + 0 (16 bits): LVDS strap xlate table pointer
|
|
+ */
|
|
+
|
|
+ if (bitentry->length != 2) {
|
|
+ NV_ERROR(dev, "Do not understand BIT LVDS table\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * No idea if it's still called the LVDS manufacturer table, but
|
|
+ * the concept's close enough.
|
|
+ */
|
|
+ bios->fp.lvdsmanufacturerpointer = ROM16(bios->data[bitentry->offset]);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+parse_bit_M_tbl_entry(struct drm_device *dev, struct nvbios *bios,
|
|
+ struct bit_entry *bitentry)
|
|
+{
|
|
+ /*
|
|
+ * offset + 2 (8 bits): number of options in an
|
|
+ * INIT_RAM_RESTRICT_ZM_REG_GROUP opcode option set
|
|
+ * offset + 3 (16 bits): pointer to strap xlate table for RAM
|
|
+ * restrict option selection
|
|
+ *
|
|
+ * There's a bunch of bits in this table other than the RAM restrict
|
|
+ * stuff that we don't use - their use currently unknown
|
|
+ */
|
|
+
|
|
+ /*
|
|
+ * Older bios versions don't have a sufficiently long table for
|
|
+ * what we want
|
|
+ */
|
|
+ if (bitentry->length < 0x5)
|
|
+ return 0;
|
|
+
|
|
+ if (bitentry->id[1] < 2) {
|
|
+ bios->ram_restrict_group_count = bios->data[bitentry->offset + 2];
|
|
+ bios->ram_restrict_tbl_ptr = ROM16(bios->data[bitentry->offset + 3]);
|
|
+ } else {
|
|
+ bios->ram_restrict_group_count = bios->data[bitentry->offset + 0];
|
|
+ bios->ram_restrict_tbl_ptr = ROM16(bios->data[bitentry->offset + 1]);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int parse_bit_tmds_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
|
|
+{
|
|
+ /*
|
|
+ * Parses the pointer to the TMDS table
|
|
+ *
|
|
+ * Starting at bitentry->offset:
|
|
+ *
|
|
+ * offset + 0 (16 bits): TMDS table pointer
|
|
+ *
|
|
+ * The TMDS table is typically found just before the DCB table, with a
|
|
+ * characteristic signature of 0x11,0x13 (1.1 being version, 0x13 being
|
|
+ * length?)
|
|
+ *
|
|
+ * At offset +7 is a pointer to a script, which I don't know how to
|
|
+ * run yet.
|
|
+ * At offset +9 is a pointer to another script, likewise
|
|
+ * Offset +11 has a pointer to a table where the first word is a pxclk
|
|
+ * frequency and the second word a pointer to a script, which should be
|
|
+ * run if the comparison pxclk frequency is less than the pxclk desired.
|
|
+ * This repeats for decreasing comparison frequencies
|
|
+ * Offset +13 has a pointer to a similar table
|
|
+ * The selection of table (and possibly +7/+9 script) is dictated by
|
|
+ * "or" from the DCB.
|
|
+ */
|
|
+
|
|
+ uint16_t tmdstableptr, script1, script2;
|
|
+
|
|
+ if (bitentry->length != 2) {
|
|
+ NV_ERROR(dev, "Do not understand BIT TMDS table\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ tmdstableptr = ROM16(bios->data[bitentry->offset]);
|
|
+
|
|
+ if (tmdstableptr == 0x0) {
|
|
+ NV_ERROR(dev, "Pointer to TMDS table invalid\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* nv50+ has v2.0, but we don't parse it atm */
|
|
+ if (bios->data[tmdstableptr] != 0x11) {
|
|
+ NV_WARN(dev,
|
|
+ "TMDS table revision %d.%d not currently supported\n",
|
|
+ bios->data[tmdstableptr] >> 4, bios->data[tmdstableptr] & 0xf);
|
|
+ return -ENOSYS;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * These two scripts are odd: they don't seem to get run even when
|
|
+ * they are not stubbed.
|
|
+ */
|
|
+ script1 = ROM16(bios->data[tmdstableptr + 7]);
|
|
+ script2 = ROM16(bios->data[tmdstableptr + 9]);
|
|
+ if (bios->data[script1] != 'q' || bios->data[script2] != 'q')
|
|
+ NV_WARN(dev, "TMDS table script pointers not stubbed\n");
|
|
+
|
|
+ bios->tmds.output0_script_ptr = ROM16(bios->data[tmdstableptr + 11]);
|
|
+ bios->tmds.output1_script_ptr = ROM16(bios->data[tmdstableptr + 13]);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+parse_bit_U_tbl_entry(struct drm_device *dev, struct nvbios *bios,
|
|
+ struct bit_entry *bitentry)
|
|
+{
|
|
+ /*
|
|
+ * Parses the pointer to the G80 output script tables
|
|
+ *
|
|
+ * Starting at bitentry->offset:
|
|
+ *
|
|
+ * offset + 0 (16 bits): output script table pointer
|
|
+ */
|
|
+
|
|
+ uint16_t outputscripttableptr;
|
|
+
|
|
+ if (bitentry->length != 3) {
|
|
+ NV_ERROR(dev, "Do not understand BIT U table\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ outputscripttableptr = ROM16(bios->data[bitentry->offset]);
|
|
+ bios->display.script_table_ptr = outputscripttableptr;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+parse_bit_displayport_tbl_entry(struct drm_device *dev, struct nvbios *bios,
|
|
+ struct bit_entry *bitentry)
|
|
+{
|
|
+ bios->display.dp_table_ptr = ROM16(bios->data[bitentry->offset]);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+struct bit_table {
|
|
+ const char id;
|
|
+ int (* const parse_fn)(struct drm_device *, struct nvbios *, struct bit_entry *);
|
|
+};
|
|
+
|
|
+#define BIT_TABLE(id, funcid) ((struct bit_table){ id, parse_bit_##funcid##_tbl_entry })
|
|
+
|
|
+static int
|
|
+parse_bit_table(struct nvbios *bios, const uint16_t bitoffset,
|
|
+ struct bit_table *table)
|
|
+{
|
|
+ struct drm_device *dev = bios->dev;
|
|
+ uint8_t maxentries = bios->data[bitoffset + 4];
|
|
+ int i, offset;
|
|
+ struct bit_entry bitentry;
|
|
+
|
|
+ for (i = 0, offset = bitoffset + 6; i < maxentries; i++, offset += 6) {
|
|
+ bitentry.id[0] = bios->data[offset];
|
|
+
|
|
+ if (bitentry.id[0] != table->id)
|
|
+ continue;
|
|
+
|
|
+ bitentry.id[1] = bios->data[offset + 1];
|
|
+ bitentry.length = ROM16(bios->data[offset + 2]);
|
|
+ bitentry.offset = ROM16(bios->data[offset + 4]);
|
|
+
|
|
+ return table->parse_fn(dev, bios, &bitentry);
|
|
+ }
|
|
+
|
|
+ NV_INFO(dev, "BIT table '%c' not found\n", table->id);
|
|
+ return -ENOSYS;
|
|
+}
|
|
+
|
|
+static int
|
|
+parse_bit_structure(struct nvbios *bios, const uint16_t bitoffset)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ /*
|
|
+ * The only restriction on parsing order currently is having 'i' first
|
|
+ * for use of bios->*_version or bios->feature_byte while parsing;
|
|
+ * functions shouldn't be actually *doing* anything apart from pulling
|
|
+ * data from the image into the bios struct, thus no interdependencies
|
|
+ */
|
|
+ ret = parse_bit_table(bios, bitoffset, &BIT_TABLE('i', i));
|
|
+ if (ret) /* info? */
|
|
+ return ret;
|
|
+ if (bios->major_version >= 0x60) /* g80+ */
|
|
+ parse_bit_table(bios, bitoffset, &BIT_TABLE('A', A));
|
|
+ ret = parse_bit_table(bios, bitoffset, &BIT_TABLE('C', C));
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ parse_bit_table(bios, bitoffset, &BIT_TABLE('D', display));
|
|
+ ret = parse_bit_table(bios, bitoffset, &BIT_TABLE('I', init));
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ parse_bit_table(bios, bitoffset, &BIT_TABLE('M', M)); /* memory? */
|
|
+ parse_bit_table(bios, bitoffset, &BIT_TABLE('L', lvds));
|
|
+ parse_bit_table(bios, bitoffset, &BIT_TABLE('T', tmds));
|
|
+ parse_bit_table(bios, bitoffset, &BIT_TABLE('U', U));
|
|
+ parse_bit_table(bios, bitoffset, &BIT_TABLE('d', displayport));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int parse_bmp_structure(struct drm_device *dev, struct nvbios *bios, unsigned int offset)
|
|
+{
|
|
+ /*
|
|
+ * Parses the BMP structure for useful things, but does not act on them
|
|
+ *
|
|
+ * offset + 5: BMP major version
|
|
+ * offset + 6: BMP minor version
|
|
+ * offset + 9: BMP feature byte
|
|
+ * offset + 10: BCD encoded BIOS version
|
|
+ *
|
|
+ * offset + 18: init script table pointer (for bios versions < 5.10h)
|
|
+ * offset + 20: extra init script table pointer (for bios
|
|
+ * versions < 5.10h)
|
|
+ *
|
|
+ * offset + 24: memory init table pointer (used on early bios versions)
|
|
+ * offset + 26: SDR memory sequencing setup data table
|
|
+ * offset + 28: DDR memory sequencing setup data table
|
|
+ *
|
|
+ * offset + 54: index of I2C CRTC pair to use for CRT output
|
|
+ * offset + 55: index of I2C CRTC pair to use for TV output
|
|
+ * offset + 56: index of I2C CRTC pair to use for flat panel output
|
|
+ * offset + 58: write CRTC index for I2C pair 0
|
|
+ * offset + 59: read CRTC index for I2C pair 0
|
|
+ * offset + 60: write CRTC index for I2C pair 1
|
|
+ * offset + 61: read CRTC index for I2C pair 1
|
|
+ *
|
|
+ * offset + 67: maximum internal PLL frequency (single stage PLL)
|
|
+ * offset + 71: minimum internal PLL frequency (single stage PLL)
|
|
+ *
|
|
+ * offset + 75: script table pointers, as described in
|
|
+ * parse_script_table_pointers
|
|
+ *
|
|
+ * offset + 89: TMDS single link output A table pointer
|
|
+ * offset + 91: TMDS single link output B table pointer
|
|
+ * offset + 95: LVDS single link output A table pointer
|
|
+ * offset + 105: flat panel timings table pointer
|
|
+ * offset + 107: flat panel strapping translation table pointer
|
|
+ * offset + 117: LVDS manufacturer panel config table pointer
|
|
+ * offset + 119: LVDS manufacturer strapping translation table pointer
|
|
+ *
|
|
+ * offset + 142: PLL limits table pointer
|
|
+ *
|
|
+ * offset + 156: minimum pixel clock for LVDS dual link
|
|
+ */
|
|
+
|
|
+ uint8_t *bmp = &bios->data[offset], bmp_version_major, bmp_version_minor;
|
|
+ uint16_t bmplength;
|
|
+ uint16_t legacy_scripts_offset, legacy_i2c_offset;
|
|
+
|
|
+ /* load needed defaults in case we can't parse this info */
|
|
+ bios->bdcb.dcb.i2c[0].write = NV_CIO_CRE_DDC_WR__INDEX;
|
|
+ bios->bdcb.dcb.i2c[0].read = NV_CIO_CRE_DDC_STATUS__INDEX;
|
|
+ bios->bdcb.dcb.i2c[1].write = NV_CIO_CRE_DDC0_WR__INDEX;
|
|
+ bios->bdcb.dcb.i2c[1].read = NV_CIO_CRE_DDC0_STATUS__INDEX;
|
|
+ bios->pub.digital_min_front_porch = 0x4b;
|
|
+ bios->fmaxvco = 256000;
|
|
+ bios->fminvco = 128000;
|
|
+ bios->fp.duallink_transition_clk = 90000;
|
|
+
|
|
+ bmp_version_major = bmp[5];
|
|
+ bmp_version_minor = bmp[6];
|
|
+
|
|
+ NV_TRACE(dev, "BMP version %d.%d\n",
|
|
+ bmp_version_major, bmp_version_minor);
|
|
+
|
|
+ /*
|
|
+ * Make sure that 0x36 is blank and can't be mistaken for a DCB
|
|
+ * pointer on early versions
|
|
+ */
|
|
+ if (bmp_version_major < 5)
|
|
+ *(uint16_t *)&bios->data[0x36] = 0;
|
|
+
|
|
+ /*
|
|
+ * Seems that the minor version was 1 for all major versions prior
|
|
+ * to 5. Version 6 could theoretically exist, but I suspect BIT
|
|
+ * happened instead.
|
|
+ */
|
|
+ if ((bmp_version_major < 5 && bmp_version_minor != 1) || bmp_version_major > 5) {
|
|
+ NV_ERROR(dev, "You have an unsupported BMP version. "
|
|
+ "Please send in your bios\n");
|
|
+ return -ENOSYS;
|
|
+ }
|
|
+
|
|
+ if (bmp_version_major == 0)
|
|
+ /* nothing that's currently useful in this version */
|
|
+ return 0;
|
|
+ else if (bmp_version_major == 1)
|
|
+ bmplength = 44; /* exact for 1.01 */
|
|
+ else if (bmp_version_major == 2)
|
|
+ bmplength = 48; /* exact for 2.01 */
|
|
+ else if (bmp_version_major == 3)
|
|
+ bmplength = 54;
|
|
+ /* guessed - mem init tables added in this version */
|
|
+ else if (bmp_version_major == 4 || bmp_version_minor < 0x1)
|
|
+ /* don't know if 5.0 exists... */
|
|
+ bmplength = 62;
|
|
+ /* guessed - BMP I2C indices added in version 4*/
|
|
+ else if (bmp_version_minor < 0x6)
|
|
+ bmplength = 67; /* exact for 5.01 */
|
|
+ else if (bmp_version_minor < 0x10)
|
|
+ bmplength = 75; /* exact for 5.06 */
|
|
+ else if (bmp_version_minor == 0x10)
|
|
+ bmplength = 89; /* exact for 5.10h */
|
|
+ else if (bmp_version_minor < 0x14)
|
|
+ bmplength = 118; /* exact for 5.11h */
|
|
+ else if (bmp_version_minor < 0x24)
|
|
+ /*
|
|
+ * Not sure of version where pll limits came in;
|
|
+ * certainly exist by 0x24 though.
|
|
+ */
|
|
+ /* length not exact: this is long enough to get lvds members */
|
|
+ bmplength = 123;
|
|
+ else if (bmp_version_minor < 0x27)
|
|
+ /*
|
|
+ * Length not exact: this is long enough to get pll limit
|
|
+ * member
|
|
+ */
|
|
+ bmplength = 144;
|
|
+ else
|
|
+ /*
|
|
+ * Length not exact: this is long enough to get dual link
|
|
+ * transition clock.
|
|
+ */
|
|
+ bmplength = 158;
|
|
+
|
|
+ /* checksum */
|
|
+ if (nv_cksum(bmp, 8)) {
|
|
+ NV_ERROR(dev, "Bad BMP checksum\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Bit 4 seems to indicate either a mobile bios or a quadro card --
|
|
+ * mobile behaviour consistent (nv11+), quadro only seen nv18gl-nv36gl
|
|
+ * (not nv10gl), bit 5 that the flat panel tables are present, and
|
|
+ * bit 6 a tv bios.
|
|
+ */
|
|
+ bios->feature_byte = bmp[9];
|
|
+
|
|
+ parse_bios_version(dev, bios, offset + 10);
|
|
+
|
|
+ if (bmp_version_major < 5 || bmp_version_minor < 0x10)
|
|
+ bios->old_style_init = true;
|
|
+ legacy_scripts_offset = 18;
|
|
+ if (bmp_version_major < 2)
|
|
+ legacy_scripts_offset -= 4;
|
|
+ bios->init_script_tbls_ptr = ROM16(bmp[legacy_scripts_offset]);
|
|
+ bios->extra_init_script_tbl_ptr = ROM16(bmp[legacy_scripts_offset + 2]);
|
|
+
|
|
+ if (bmp_version_major > 2) { /* appears in BMP 3 */
|
|
+ bios->legacy.mem_init_tbl_ptr = ROM16(bmp[24]);
|
|
+ bios->legacy.sdr_seq_tbl_ptr = ROM16(bmp[26]);
|
|
+ bios->legacy.ddr_seq_tbl_ptr = ROM16(bmp[28]);
|
|
+ }
|
|
+
|
|
+ legacy_i2c_offset = 0x48; /* BMP version 2 & 3 */
|
|
+ if (bmplength > 61)
|
|
+ legacy_i2c_offset = offset + 54;
|
|
+ bios->legacy.i2c_indices.crt = bios->data[legacy_i2c_offset];
|
|
+ bios->legacy.i2c_indices.tv = bios->data[legacy_i2c_offset + 1];
|
|
+ bios->legacy.i2c_indices.panel = bios->data[legacy_i2c_offset + 2];
|
|
+ bios->bdcb.dcb.i2c[0].write = bios->data[legacy_i2c_offset + 4];
|
|
+ bios->bdcb.dcb.i2c[0].read = bios->data[legacy_i2c_offset + 5];
|
|
+ bios->bdcb.dcb.i2c[1].write = bios->data[legacy_i2c_offset + 6];
|
|
+ bios->bdcb.dcb.i2c[1].read = bios->data[legacy_i2c_offset + 7];
|
|
+
|
|
+ if (bmplength > 74) {
|
|
+ bios->fmaxvco = ROM32(bmp[67]);
|
|
+ bios->fminvco = ROM32(bmp[71]);
|
|
+ }
|
|
+ if (bmplength > 88)
|
|
+ parse_script_table_pointers(bios, offset + 75);
|
|
+ if (bmplength > 94) {
|
|
+ bios->tmds.output0_script_ptr = ROM16(bmp[89]);
|
|
+ bios->tmds.output1_script_ptr = ROM16(bmp[91]);
|
|
+ /*
|
|
+ * Never observed in use with lvds scripts, but is reused for
|
|
+ * 18/24 bit panel interface default for EDID equipped panels
|
|
+ * (if_is_24bit not set directly to avoid any oscillation).
|
|
+ */
|
|
+ bios->legacy.lvds_single_a_script_ptr = ROM16(bmp[95]);
|
|
+ }
|
|
+ if (bmplength > 108) {
|
|
+ bios->fp.fptablepointer = ROM16(bmp[105]);
|
|
+ bios->fp.fpxlatetableptr = ROM16(bmp[107]);
|
|
+ bios->fp.xlatwidth = 1;
|
|
+ }
|
|
+ if (bmplength > 120) {
|
|
+ bios->fp.lvdsmanufacturerpointer = ROM16(bmp[117]);
|
|
+ bios->fp.fpxlatemanufacturertableptr = ROM16(bmp[119]);
|
|
+ }
|
|
+ if (bmplength > 143)
|
|
+ bios->pll_limit_tbl_ptr = ROM16(bmp[142]);
|
|
+
|
|
+ if (bmplength > 157)
|
|
+ bios->fp.duallink_transition_clk = ROM16(bmp[156]) * 10;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static uint16_t findstr(uint8_t *data, int n, const uint8_t *str, int len)
|
|
+{
|
|
+ int i, j;
|
|
+
|
|
+ for (i = 0; i <= (n - len); i++) {
|
|
+ for (j = 0; j < len; j++)
|
|
+ if (data[i + j] != str[j])
|
|
+ break;
|
|
+ if (j == len)
|
|
+ return i;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+read_dcb_i2c_entry(struct drm_device *dev, int dcb_version, uint8_t *i2ctable, int index, struct dcb_i2c_entry *i2c)
|
|
+{
|
|
+ uint8_t dcb_i2c_ver = dcb_version, headerlen = 0, entry_len = 4;
|
|
+ int i2c_entries = DCB_MAX_NUM_I2C_ENTRIES;
|
|
+ int recordoffset = 0, rdofs = 1, wrofs = 0;
|
|
+ uint8_t port_type = 0;
|
|
+
|
|
+ if (!i2ctable)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (dcb_version >= 0x30) {
|
|
+ if (i2ctable[0] != dcb_version) /* necessary? */
|
|
+ NV_WARN(dev,
|
|
+ "DCB I2C table version mismatch (%02X vs %02X)\n",
|
|
+ i2ctable[0], dcb_version);
|
|
+ dcb_i2c_ver = i2ctable[0];
|
|
+ headerlen = i2ctable[1];
|
|
+ if (i2ctable[2] <= DCB_MAX_NUM_I2C_ENTRIES)
|
|
+ i2c_entries = i2ctable[2];
|
|
+ else
|
|
+ NV_WARN(dev,
|
|
+ "DCB I2C table has more entries than indexable "
|
|
+ "(%d entries, max index 15)\n", i2ctable[2]);
|
|
+ entry_len = i2ctable[3];
|
|
+ /* [4] is i2c_default_indices, read in parse_dcb_table() */
|
|
+ }
|
|
+ /*
|
|
+ * It's your own fault if you call this function on a DCB 1.1 BIOS --
|
|
+ * the test below is for DCB 1.2
|
|
+ */
|
|
+ if (dcb_version < 0x14) {
|
|
+ recordoffset = 2;
|
|
+ rdofs = 0;
|
|
+ wrofs = 1;
|
|
+ }
|
|
+
|
|
+ if (index == 0xf)
|
|
+ return 0;
|
|
+ if (index > i2c_entries) {
|
|
+ NV_ERROR(dev, "DCB I2C index too big (%d > %d)\n",
|
|
+ index, i2ctable[2]);
|
|
+ return -ENOENT;
|
|
+ }
|
|
+ if (i2ctable[headerlen + entry_len * index + 3] == 0xff) {
|
|
+ NV_ERROR(dev, "DCB I2C entry invalid\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (dcb_i2c_ver >= 0x30) {
|
|
+ port_type = i2ctable[headerlen + recordoffset + 3 + entry_len * index];
|
|
+
|
|
+ /*
|
|
+ * Fixup for chips using same address offset for read and
|
|
+ * write.
|
|
+ */
|
|
+ if (port_type == 4) /* seen on C51 */
|
|
+ rdofs = wrofs = 1;
|
|
+ if (port_type >= 5) /* G80+ */
|
|
+ rdofs = wrofs = 0;
|
|
+ }
|
|
+
|
|
+ if (dcb_i2c_ver >= 0x40 && port_type != 5 && port_type != 6)
|
|
+ NV_WARN(dev, "DCB I2C table has port type %d\n", port_type);
|
|
+
|
|
+ i2c->port_type = port_type;
|
|
+ i2c->read = i2ctable[headerlen + recordoffset + rdofs + entry_len * index];
|
|
+ i2c->write = i2ctable[headerlen + recordoffset + wrofs + entry_len * index];
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct dcb_gpio_entry *
|
|
+new_gpio_entry(struct nvbios *bios)
|
|
+{
|
|
+ struct parsed_dcb_gpio *gpio = &bios->bdcb.gpio;
|
|
+
|
|
+ return &gpio->entry[gpio->entries++];
|
|
+}
|
|
+
|
|
+struct dcb_gpio_entry *
|
|
+nouveau_bios_gpio_entry(struct drm_device *dev, enum dcb_gpio_tag tag)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nvbios *bios = &dev_priv->VBIOS;
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < bios->bdcb.gpio.entries; i++) {
|
|
+ if (bios->bdcb.gpio.entry[i].tag != tag)
|
|
+ continue;
|
|
+
|
|
+ return &bios->bdcb.gpio.entry[i];
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static void
|
|
+parse_dcb30_gpio_entry(struct nvbios *bios, uint16_t offset)
|
|
+{
|
|
+ struct dcb_gpio_entry *gpio;
|
|
+ uint16_t ent = ROM16(bios->data[offset]);
|
|
+ uint8_t line = ent & 0x1f,
|
|
+ tag = ent >> 5 & 0x3f,
|
|
+ flags = ent >> 11 & 0x1f;
|
|
+
|
|
+ if (tag == 0x3f)
|
|
+ return;
|
|
+
|
|
+ gpio = new_gpio_entry(bios);
|
|
+
|
|
+ gpio->tag = tag;
|
|
+ gpio->line = line;
|
|
+ gpio->invert = flags != 4;
|
|
+}
|
|
+
|
|
+static void
|
|
+parse_dcb40_gpio_entry(struct nvbios *bios, uint16_t offset)
|
|
+{
|
|
+ struct dcb_gpio_entry *gpio;
|
|
+ uint32_t ent = ROM32(bios->data[offset]);
|
|
+ uint8_t line = ent & 0x1f,
|
|
+ tag = ent >> 8 & 0xff;
|
|
+
|
|
+ if (tag == 0xff)
|
|
+ return;
|
|
+
|
|
+ gpio = new_gpio_entry(bios);
|
|
+
|
|
+ /* Currently unused, we may need more fields parsed at some
|
|
+ * point. */
|
|
+ gpio->tag = tag;
|
|
+ gpio->line = line;
|
|
+}
|
|
+
|
|
+static void
|
|
+parse_dcb_gpio_table(struct nvbios *bios)
|
|
+{
|
|
+ struct drm_device *dev = bios->dev;
|
|
+ uint16_t gpio_table_ptr = bios->bdcb.gpio_table_ptr;
|
|
+ uint8_t *gpio_table = &bios->data[gpio_table_ptr];
|
|
+ int header_len = gpio_table[1],
|
|
+ entries = gpio_table[2],
|
|
+ entry_len = gpio_table[3];
|
|
+ void (*parse_entry)(struct nvbios *, uint16_t) = NULL;
|
|
+ int i;
|
|
+
|
|
+ if (bios->bdcb.version >= 0x40) {
|
|
+ if (gpio_table_ptr && entry_len != 4) {
|
|
+ NV_WARN(dev, "Invalid DCB GPIO table entry length.\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ parse_entry = parse_dcb40_gpio_entry;
|
|
+
|
|
+ } else if (bios->bdcb.version >= 0x30) {
|
|
+ if (gpio_table_ptr && entry_len != 2) {
|
|
+ NV_WARN(dev, "Invalid DCB GPIO table entry length.\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ parse_entry = parse_dcb30_gpio_entry;
|
|
+
|
|
+ } else if (bios->bdcb.version >= 0x22) {
|
|
+ /*
|
|
+ * DCBs older than v3.0 don't really have a GPIO
|
|
+ * table, instead they keep some GPIO info at fixed
|
|
+ * locations.
|
|
+ */
|
|
+ uint16_t dcbptr = ROM16(bios->data[0x36]);
|
|
+ uint8_t *tvdac_gpio = &bios->data[dcbptr - 5];
|
|
+
|
|
+ if (tvdac_gpio[0] & 1) {
|
|
+ struct dcb_gpio_entry *gpio = new_gpio_entry(bios);
|
|
+
|
|
+ gpio->tag = DCB_GPIO_TVDAC0;
|
|
+ gpio->line = tvdac_gpio[1] >> 4;
|
|
+ gpio->invert = tvdac_gpio[0] & 2;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!gpio_table_ptr)
|
|
+ return;
|
|
+
|
|
+ if (entries > DCB_MAX_NUM_GPIO_ENTRIES) {
|
|
+ NV_WARN(dev, "Too many entries in the DCB GPIO table.\n");
|
|
+ entries = DCB_MAX_NUM_GPIO_ENTRIES;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < entries; i++)
|
|
+ parse_entry(bios, gpio_table_ptr + header_len + entry_len * i);
|
|
+}
|
|
+
|
|
+struct dcb_connector_table_entry *
|
|
+nouveau_bios_connector_entry(struct drm_device *dev, int index)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nvbios *bios = &dev_priv->VBIOS;
|
|
+ struct dcb_connector_table_entry *cte;
|
|
+
|
|
+ if (index >= bios->bdcb.connector.entries)
|
|
+ return NULL;
|
|
+
|
|
+ cte = &bios->bdcb.connector.entry[index];
|
|
+ if (cte->type == 0xff)
|
|
+ return NULL;
|
|
+
|
|
+ return cte;
|
|
+}
|
|
+
|
|
+static void
|
|
+parse_dcb_connector_table(struct nvbios *bios)
|
|
+{
|
|
+ struct drm_device *dev = bios->dev;
|
|
+ struct dcb_connector_table *ct = &bios->bdcb.connector;
|
|
+ struct dcb_connector_table_entry *cte;
|
|
+ uint8_t *conntab = &bios->data[bios->bdcb.connector_table_ptr];
|
|
+ uint8_t *entry;
|
|
+ int i;
|
|
+
|
|
+ if (!bios->bdcb.connector_table_ptr) {
|
|
+ NV_DEBUG_KMS(dev, "No DCB connector table present\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ NV_INFO(dev, "DCB connector table: VHER 0x%02x %d %d %d\n",
|
|
+ conntab[0], conntab[1], conntab[2], conntab[3]);
|
|
+ if ((conntab[0] != 0x30 && conntab[0] != 0x40) ||
|
|
+ (conntab[3] != 2 && conntab[3] != 4)) {
|
|
+ NV_ERROR(dev, " Unknown! Please report.\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ct->entries = conntab[2];
|
|
+
|
|
+ entry = conntab + conntab[1];
|
|
+ cte = &ct->entry[0];
|
|
+ for (i = 0; i < conntab[2]; i++, entry += conntab[3], cte++) {
|
|
+ if (conntab[3] == 2)
|
|
+ cte->entry = ROM16(entry[0]);
|
|
+ else
|
|
+ cte->entry = ROM32(entry[0]);
|
|
+ cte->type = (cte->entry & 0x000000ff) >> 0;
|
|
+ cte->index = (cte->entry & 0x00000f00) >> 8;
|
|
+ switch (cte->entry & 0x00033000) {
|
|
+ case 0x00001000:
|
|
+ cte->gpio_tag = 0x07;
|
|
+ break;
|
|
+ case 0x00002000:
|
|
+ cte->gpio_tag = 0x08;
|
|
+ break;
|
|
+ case 0x00010000:
|
|
+ cte->gpio_tag = 0x51;
|
|
+ break;
|
|
+ case 0x00020000:
|
|
+ cte->gpio_tag = 0x52;
|
|
+ break;
|
|
+ default:
|
|
+ cte->gpio_tag = 0xff;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (cte->type == 0xff)
|
|
+ continue;
|
|
+
|
|
+ NV_INFO(dev, " %d: 0x%08x: type 0x%02x idx %d tag 0x%02x\n",
|
|
+ i, cte->entry, cte->type, cte->index, cte->gpio_tag);
|
|
+ }
|
|
+}
|
|
+
|
|
+static struct dcb_entry *new_dcb_entry(struct parsed_dcb *dcb)
|
|
+{
|
|
+ struct dcb_entry *entry = &dcb->entry[dcb->entries];
|
|
+
|
|
+ memset(entry, 0, sizeof(struct dcb_entry));
|
|
+ entry->index = dcb->entries++;
|
|
+
|
|
+ return entry;
|
|
+}
|
|
+
|
|
+static void fabricate_vga_output(struct parsed_dcb *dcb, int i2c, int heads)
|
|
+{
|
|
+ struct dcb_entry *entry = new_dcb_entry(dcb);
|
|
+
|
|
+ entry->type = 0;
|
|
+ entry->i2c_index = i2c;
|
|
+ entry->heads = heads;
|
|
+ entry->location = DCB_LOC_ON_CHIP;
|
|
+ /* "or" mostly unused in early gen crt modesetting, 0 is fine */
|
|
+}
|
|
+
|
|
+static void fabricate_dvi_i_output(struct parsed_dcb *dcb, bool twoHeads)
|
|
+{
|
|
+ struct dcb_entry *entry = new_dcb_entry(dcb);
|
|
+
|
|
+ entry->type = 2;
|
|
+ entry->i2c_index = LEGACY_I2C_PANEL;
|
|
+ entry->heads = twoHeads ? 3 : 1;
|
|
+ entry->location = !DCB_LOC_ON_CHIP; /* ie OFF CHIP */
|
|
+ entry->or = 1; /* means |0x10 gets set on CRE_LCD__INDEX */
|
|
+ entry->duallink_possible = false; /* SiI164 and co. are single link */
|
|
+
|
|
+#if 0
|
|
+ /*
|
|
+ * For dvi-a either crtc probably works, but my card appears to only
|
|
+ * support dvi-d. "nvidia" still attempts to program it for dvi-a,
|
|
+ * doing the full fp output setup (program 0x6808.. fp dimension regs,
|
|
+ * setting 0x680848 to 0x10000111 to enable, maybe setting 0x680880);
|
|
+ * the monitor picks up the mode res ok and lights up, but no pixel
|
|
+ * data appears, so the board manufacturer probably connected up the
|
|
+ * sync lines, but missed the video traces / components
|
|
+ *
|
|
+ * with this introduction, dvi-a left as an exercise for the reader.
|
|
+ */
|
|
+ fabricate_vga_output(dcb, LEGACY_I2C_PANEL, entry->heads);
|
|
+#endif
|
|
+}
|
|
+
|
|
+static void fabricate_tv_output(struct parsed_dcb *dcb, bool twoHeads)
|
|
+{
|
|
+ struct dcb_entry *entry = new_dcb_entry(dcb);
|
|
+
|
|
+ entry->type = 1;
|
|
+ entry->i2c_index = LEGACY_I2C_TV;
|
|
+ entry->heads = twoHeads ? 3 : 1;
|
|
+ entry->location = !DCB_LOC_ON_CHIP; /* ie OFF CHIP */
|
|
+}
|
|
+
|
|
+static bool
|
|
+parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb,
|
|
+ uint32_t conn, uint32_t conf, struct dcb_entry *entry)
|
|
+{
|
|
+ entry->type = conn & 0xf;
|
|
+ entry->i2c_index = (conn >> 4) & 0xf;
|
|
+ entry->heads = (conn >> 8) & 0xf;
|
|
+ if (bdcb->version >= 0x40)
|
|
+ entry->connector = (conn >> 12) & 0xf;
|
|
+ entry->bus = (conn >> 16) & 0xf;
|
|
+ entry->location = (conn >> 20) & 0x3;
|
|
+ entry->or = (conn >> 24) & 0xf;
|
|
+ /*
|
|
+ * Normal entries consist of a single bit, but dual link has the
|
|
+ * next most significant bit set too
|
|
+ */
|
|
+ entry->duallink_possible =
|
|
+ ((1 << (ffs(entry->or) - 1)) * 3 == entry->or);
|
|
+
|
|
+ switch (entry->type) {
|
|
+ case OUTPUT_ANALOG:
|
|
+ /*
|
|
+ * Although the rest of a CRT conf dword is usually
|
|
+ * zeros, mac biosen have stuff there so we must mask
|
|
+ */
|
|
+ entry->crtconf.maxfreq = (bdcb->version < 0x30) ?
|
|
+ (conf & 0xffff) * 10 :
|
|
+ (conf & 0xff) * 10000;
|
|
+ break;
|
|
+ case OUTPUT_LVDS:
|
|
+ {
|
|
+ uint32_t mask;
|
|
+ if (conf & 0x1)
|
|
+ entry->lvdsconf.use_straps_for_mode = true;
|
|
+ if (bdcb->version < 0x22) {
|
|
+ mask = ~0xd;
|
|
+ /*
|
|
+ * The laptop in bug 14567 lies and claims to not use
|
|
+ * straps when it does, so assume all DCB 2.0 laptops
|
|
+ * use straps, until a broken EDID using one is produced
|
|
+ */
|
|
+ entry->lvdsconf.use_straps_for_mode = true;
|
|
+ /*
|
|
+ * Both 0x4 and 0x8 show up in v2.0 tables; assume they
|
|
+ * mean the same thing (probably wrong, but might work)
|
|
+ */
|
|
+ if (conf & 0x4 || conf & 0x8)
|
|
+ entry->lvdsconf.use_power_scripts = true;
|
|
+ } else {
|
|
+ mask = ~0x5;
|
|
+ if (conf & 0x4)
|
|
+ entry->lvdsconf.use_power_scripts = true;
|
|
+ }
|
|
+ if (conf & mask) {
|
|
+ /*
|
|
+ * Until we even try to use these on G8x, it's
|
|
+ * useless reporting unknown bits. They all are.
|
|
+ */
|
|
+ if (bdcb->version >= 0x40)
|
|
+ break;
|
|
+
|
|
+ NV_ERROR(dev, "Unknown LVDS configuration bits, "
|
|
+ "please report\n");
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case OUTPUT_TV:
|
|
+ {
|
|
+ if (bdcb->version >= 0x30)
|
|
+ entry->tvconf.has_component_output = conf & (0x8 << 4);
|
|
+ else
|
|
+ entry->tvconf.has_component_output = false;
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ case OUTPUT_DP:
|
|
+ entry->dpconf.sor.link = (conf & 0x00000030) >> 4;
|
|
+ entry->dpconf.link_bw = (conf & 0x00e00000) >> 21;
|
|
+ switch ((conf & 0x0f000000) >> 24) {
|
|
+ case 0xf:
|
|
+ entry->dpconf.link_nr = 4;
|
|
+ break;
|
|
+ case 0x3:
|
|
+ entry->dpconf.link_nr = 2;
|
|
+ break;
|
|
+ default:
|
|
+ entry->dpconf.link_nr = 1;
|
|
+ break;
|
|
+ }
|
|
+ break;
|
|
+ case OUTPUT_TMDS:
|
|
+ entry->tmdsconf.sor.link = (conf & 0x00000030) >> 4;
|
|
+ break;
|
|
+ case 0xe:
|
|
+ /* weird g80 mobile type that "nv" treats as a terminator */
|
|
+ bdcb->dcb.entries--;
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ /* unsure what DCB version introduces this, 3.0? */
|
|
+ if (conf & 0x100000)
|
|
+ entry->i2c_upper_default = true;
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static bool
|
|
+parse_dcb15_entry(struct drm_device *dev, struct parsed_dcb *dcb,
|
|
+ uint32_t conn, uint32_t conf, struct dcb_entry *entry)
|
|
+{
|
|
+ switch (conn & 0x0000000f) {
|
|
+ case 0:
|
|
+ entry->type = OUTPUT_ANALOG;
|
|
+ break;
|
|
+ case 1:
|
|
+ entry->type = OUTPUT_TV;
|
|
+ break;
|
|
+ case 2:
|
|
+ case 3:
|
|
+ entry->type = OUTPUT_LVDS;
|
|
+ break;
|
|
+ case 4:
|
|
+ switch ((conn & 0x000000f0) >> 4) {
|
|
+ case 0:
|
|
+ entry->type = OUTPUT_TMDS;
|
|
+ break;
|
|
+ case 1:
|
|
+ entry->type = OUTPUT_LVDS;
|
|
+ break;
|
|
+ default:
|
|
+ NV_ERROR(dev, "Unknown DCB subtype 4/%d\n",
|
|
+ (conn & 0x000000f0) >> 4);
|
|
+ return false;
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ NV_ERROR(dev, "Unknown DCB type %d\n", conn & 0x0000000f);
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ entry->i2c_index = (conn & 0x0003c000) >> 14;
|
|
+ entry->heads = ((conn & 0x001c0000) >> 18) + 1;
|
|
+ entry->or = entry->heads; /* same as heads, hopefully safe enough */
|
|
+ entry->location = (conn & 0x01e00000) >> 21;
|
|
+ entry->bus = (conn & 0x0e000000) >> 25;
|
|
+ entry->duallink_possible = false;
|
|
+
|
|
+ switch (entry->type) {
|
|
+ case OUTPUT_ANALOG:
|
|
+ entry->crtconf.maxfreq = (conf & 0xffff) * 10;
|
|
+ break;
|
|
+ case OUTPUT_TV:
|
|
+ entry->tvconf.has_component_output = false;
|
|
+ break;
|
|
+ case OUTPUT_TMDS:
|
|
+ /*
|
|
+ * Invent a DVI-A output, by copying the fields of the DVI-D
|
|
+ * output; reported to work by math_b on an NV20(!).
|
|
+ */
|
|
+ fabricate_vga_output(dcb, entry->i2c_index, entry->heads);
|
|
+ break;
|
|
+ case OUTPUT_LVDS:
|
|
+ if ((conn & 0x00003f00) != 0x10)
|
|
+ entry->lvdsconf.use_straps_for_mode = true;
|
|
+ entry->lvdsconf.use_power_scripts = true;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static bool parse_dcb_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb,
|
|
+ uint32_t conn, uint32_t conf)
|
|
+{
|
|
+ struct dcb_entry *entry = new_dcb_entry(&bdcb->dcb);
|
|
+ bool ret;
|
|
+
|
|
+ if (bdcb->version >= 0x20)
|
|
+ ret = parse_dcb20_entry(dev, bdcb, conn, conf, entry);
|
|
+ else
|
|
+ ret = parse_dcb15_entry(dev, &bdcb->dcb, conn, conf, entry);
|
|
+ if (!ret)
|
|
+ return ret;
|
|
+
|
|
+ read_dcb_i2c_entry(dev, bdcb->version, bdcb->i2c_table,
|
|
+ entry->i2c_index, &bdcb->dcb.i2c[entry->i2c_index]);
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static
|
|
+void merge_like_dcb_entries(struct drm_device *dev, struct parsed_dcb *dcb)
|
|
+{
|
|
+ /*
|
|
+ * DCB v2.0 lists each output combination separately.
|
|
+ * Here we merge compatible entries to have fewer outputs, with
|
|
+ * more options
|
|
+ */
|
|
+
|
|
+ int i, newentries = 0;
|
|
+
|
|
+ for (i = 0; i < dcb->entries; i++) {
|
|
+ struct dcb_entry *ient = &dcb->entry[i];
|
|
+ int j;
|
|
+
|
|
+ for (j = i + 1; j < dcb->entries; j++) {
|
|
+ struct dcb_entry *jent = &dcb->entry[j];
|
|
+
|
|
+ if (jent->type == 100) /* already merged entry */
|
|
+ continue;
|
|
+
|
|
+ /* merge heads field when all other fields the same */
|
|
+ if (jent->i2c_index == ient->i2c_index &&
|
|
+ jent->type == ient->type &&
|
|
+ jent->location == ient->location &&
|
|
+ jent->or == ient->or) {
|
|
+ NV_TRACE(dev, "Merging DCB entries %d and %d\n",
|
|
+ i, j);
|
|
+ ient->heads |= jent->heads;
|
|
+ jent->type = 100; /* dummy value */
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Compact entries merged into others out of dcb */
|
|
+ for (i = 0; i < dcb->entries; i++) {
|
|
+ if (dcb->entry[i].type == 100)
|
|
+ continue;
|
|
+
|
|
+ if (newentries != i) {
|
|
+ dcb->entry[newentries] = dcb->entry[i];
|
|
+ dcb->entry[newentries].index = newentries;
|
|
+ }
|
|
+ newentries++;
|
|
+ }
|
|
+
|
|
+ dcb->entries = newentries;
|
|
+}
|
|
+
|
|
+static int
|
|
+parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct bios_parsed_dcb *bdcb = &bios->bdcb;
|
|
+ struct parsed_dcb *dcb;
|
|
+ uint16_t dcbptr = 0, i2ctabptr = 0;
|
|
+ uint8_t *dcbtable;
|
|
+ uint8_t headerlen = 0x4, entries = DCB_MAX_NUM_ENTRIES;
|
|
+ bool configblock = true;
|
|
+ int recordlength = 8, confofs = 4;
|
|
+ int i;
|
|
+
|
|
+ dcb = bios->pub.dcb = &bdcb->dcb;
|
|
+ dcb->entries = 0;
|
|
+
|
|
+ /* get the offset from 0x36 */
|
|
+ if (dev_priv->card_type > NV_04) {
|
|
+ dcbptr = ROM16(bios->data[0x36]);
|
|
+ if (dcbptr == 0x0000)
|
|
+ NV_WARN(dev, "No output data (DCB) found in BIOS\n");
|
|
+ }
|
|
+
|
|
+ /* this situation likely means a really old card, pre DCB */
|
|
+ if (dcbptr == 0x0) {
|
|
+ NV_INFO(dev, "Assuming a CRT output exists\n");
|
|
+ fabricate_vga_output(dcb, LEGACY_I2C_CRT, 1);
|
|
+
|
|
+ if (nv04_tv_identify(dev, bios->legacy.i2c_indices.tv) >= 0)
|
|
+ fabricate_tv_output(dcb, twoHeads);
|
|
+
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ dcbtable = &bios->data[dcbptr];
|
|
+
|
|
+ /* get DCB version */
|
|
+ bdcb->version = dcbtable[0];
|
|
+ NV_TRACE(dev, "Found Display Configuration Block version %d.%d\n",
|
|
+ bdcb->version >> 4, bdcb->version & 0xf);
|
|
+
|
|
+ if (bdcb->version >= 0x20) { /* NV17+ */
|
|
+ uint32_t sig;
|
|
+
|
|
+ if (bdcb->version >= 0x30) { /* NV40+ */
|
|
+ headerlen = dcbtable[1];
|
|
+ entries = dcbtable[2];
|
|
+ recordlength = dcbtable[3];
|
|
+ i2ctabptr = ROM16(dcbtable[4]);
|
|
+ sig = ROM32(dcbtable[6]);
|
|
+ bdcb->gpio_table_ptr = ROM16(dcbtable[10]);
|
|
+ bdcb->connector_table_ptr = ROM16(dcbtable[20]);
|
|
+ } else {
|
|
+ i2ctabptr = ROM16(dcbtable[2]);
|
|
+ sig = ROM32(dcbtable[4]);
|
|
+ headerlen = 8;
|
|
+ }
|
|
+
|
|
+ if (sig != 0x4edcbdcb) {
|
|
+ NV_ERROR(dev, "Bad Display Configuration Block "
|
|
+ "signature (%08X)\n", sig);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ } else if (bdcb->version >= 0x15) { /* some NV11 and NV20 */
|
|
+ char sig[8] = { 0 };
|
|
+
|
|
+ strncpy(sig, (char *)&dcbtable[-7], 7);
|
|
+ i2ctabptr = ROM16(dcbtable[2]);
|
|
+ recordlength = 10;
|
|
+ confofs = 6;
|
|
+
|
|
+ if (strcmp(sig, "DEV_REC")) {
|
|
+ NV_ERROR(dev, "Bad Display Configuration Block "
|
|
+ "signature (%s)\n", sig);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ } else {
|
|
+ /*
|
|
+ * v1.4 (some NV15/16, NV11+) seems the same as v1.5, but always
|
|
+ * has the same single (crt) entry, even when tv-out present, so
|
|
+ * the conclusion is this version cannot really be used.
|
|
+ * v1.2 tables (some NV6/10, and NV15+) normally have the same
|
|
+ * 5 entries, which are not specific to the card and so no use.
|
|
+ * v1.2 does have an I2C table that read_dcb_i2c_table can
|
|
+ * handle, but cards exist (nv11 in #14821) with a bad i2c table
|
|
+ * pointer, so use the indices parsed in parse_bmp_structure.
|
|
+ * v1.1 (NV5+, maybe some NV4) is entirely unhelpful
|
|
+ */
|
|
+ NV_TRACEWARN(dev, "No useful information in BIOS output table; "
|
|
+ "adding all possible outputs\n");
|
|
+ fabricate_vga_output(dcb, LEGACY_I2C_CRT, 1);
|
|
+
|
|
+ /*
|
|
+ * Attempt to detect TV before DVI because the test
|
|
+ * for the former is more accurate and it rules the
|
|
+ * latter out.
|
|
+ */
|
|
+ if (nv04_tv_identify(dev,
|
|
+ bios->legacy.i2c_indices.tv) >= 0)
|
|
+ fabricate_tv_output(dcb, twoHeads);
|
|
+
|
|
+ else if (bios->tmds.output0_script_ptr ||
|
|
+ bios->tmds.output1_script_ptr)
|
|
+ fabricate_dvi_i_output(dcb, twoHeads);
|
|
+
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (!i2ctabptr)
|
|
+ NV_WARN(dev, "No pointer to DCB I2C port table\n");
|
|
+ else {
|
|
+ bdcb->i2c_table = &bios->data[i2ctabptr];
|
|
+ if (bdcb->version >= 0x30)
|
|
+ bdcb->i2c_default_indices = bdcb->i2c_table[4];
|
|
+ }
|
|
+
|
|
+ parse_dcb_gpio_table(bios);
|
|
+ parse_dcb_connector_table(bios);
|
|
+
|
|
+ if (entries > DCB_MAX_NUM_ENTRIES)
|
|
+ entries = DCB_MAX_NUM_ENTRIES;
|
|
+
|
|
+ for (i = 0; i < entries; i++) {
|
|
+ uint32_t connection, config = 0;
|
|
+
|
|
+ connection = ROM32(dcbtable[headerlen + recordlength * i]);
|
|
+ if (configblock)
|
|
+ config = ROM32(dcbtable[headerlen + confofs + recordlength * i]);
|
|
+
|
|
+ /* seen on an NV11 with DCB v1.5 */
|
|
+ if (connection == 0x00000000)
|
|
+ break;
|
|
+
|
|
+ /* seen on an NV17 with DCB v2.0 */
|
|
+ if (connection == 0xffffffff)
|
|
+ break;
|
|
+
|
|
+ if ((connection & 0x0000000f) == 0x0000000f)
|
|
+ continue;
|
|
+
|
|
+ NV_TRACEWARN(dev, "Raw DCB entry %d: %08x %08x\n",
|
|
+ dcb->entries, connection, config);
|
|
+
|
|
+ if (!parse_dcb_entry(dev, bdcb, connection, config))
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * apart for v2.1+ not being known for requiring merging, this
|
|
+ * guarantees dcbent->index is the index of the entry in the rom image
|
|
+ */
|
|
+ if (bdcb->version < 0x21)
|
|
+ merge_like_dcb_entries(dev, dcb);
|
|
+
|
|
+ return dcb->entries ? 0 : -ENXIO;
|
|
+}
|
|
+
|
|
+static void
|
|
+fixup_legacy_connector(struct nvbios *bios)
|
|
+{
|
|
+ struct bios_parsed_dcb *bdcb = &bios->bdcb;
|
|
+ struct parsed_dcb *dcb = &bdcb->dcb;
|
|
+ int high = 0, i;
|
|
+
|
|
+ /*
|
|
+ * DCB 3.0 also has the table in most cases, but there are some cards
|
|
+ * where the table is filled with stub entries, and the DCB entriy
|
|
+ * indices are all 0. We don't need the connector indices on pre-G80
|
|
+ * chips (yet?) so limit the use to DCB 4.0 and above.
|
|
+ */
|
|
+ if (bdcb->version >= 0x40)
|
|
+ return;
|
|
+
|
|
+ /*
|
|
+ * No known connector info before v3.0, so make it up. the rule here
|
|
+ * is: anything on the same i2c bus is considered to be on the same
|
|
+ * connector. any output without an associated i2c bus is assigned
|
|
+ * its own unique connector index.
|
|
+ */
|
|
+ for (i = 0; i < dcb->entries; i++) {
|
|
+ if (dcb->entry[i].i2c_index == 0xf)
|
|
+ continue;
|
|
+
|
|
+ /*
|
|
+ * Ignore the I2C index for on-chip TV-out, as there
|
|
+ * are cards with bogus values (nv31m in bug 23212),
|
|
+ * and it's otherwise useless.
|
|
+ */
|
|
+ if (dcb->entry[i].type == OUTPUT_TV &&
|
|
+ dcb->entry[i].location == DCB_LOC_ON_CHIP) {
|
|
+ dcb->entry[i].i2c_index = 0xf;
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ dcb->entry[i].connector = dcb->entry[i].i2c_index;
|
|
+ if (dcb->entry[i].connector > high)
|
|
+ high = dcb->entry[i].connector;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < dcb->entries; i++) {
|
|
+ if (dcb->entry[i].i2c_index != 0xf)
|
|
+ continue;
|
|
+
|
|
+ dcb->entry[i].connector = ++high;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+fixup_legacy_i2c(struct nvbios *bios)
|
|
+{
|
|
+ struct parsed_dcb *dcb = &bios->bdcb.dcb;
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < dcb->entries; i++) {
|
|
+ if (dcb->entry[i].i2c_index == LEGACY_I2C_CRT)
|
|
+ dcb->entry[i].i2c_index = bios->legacy.i2c_indices.crt;
|
|
+ if (dcb->entry[i].i2c_index == LEGACY_I2C_PANEL)
|
|
+ dcb->entry[i].i2c_index = bios->legacy.i2c_indices.panel;
|
|
+ if (dcb->entry[i].i2c_index == LEGACY_I2C_TV)
|
|
+ dcb->entry[i].i2c_index = bios->legacy.i2c_indices.tv;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int load_nv17_hwsq_ucode_entry(struct drm_device *dev, struct nvbios *bios, uint16_t hwsq_offset, int entry)
|
|
+{
|
|
+ /*
|
|
+ * The header following the "HWSQ" signature has the number of entries,
|
|
+ * and the entry size
|
|
+ *
|
|
+ * An entry consists of a dword to write to the sequencer control reg
|
|
+ * (0x00001304), followed by the ucode bytes, written sequentially,
|
|
+ * starting at reg 0x00001400
|
|
+ */
|
|
+
|
|
+ uint8_t bytes_to_write;
|
|
+ uint16_t hwsq_entry_offset;
|
|
+ int i;
|
|
+
|
|
+ if (bios->data[hwsq_offset] <= entry) {
|
|
+ NV_ERROR(dev, "Too few entries in HW sequencer table for "
|
|
+ "requested entry\n");
|
|
+ return -ENOENT;
|
|
+ }
|
|
+
|
|
+ bytes_to_write = bios->data[hwsq_offset + 1];
|
|
+
|
|
+ if (bytes_to_write != 36) {
|
|
+ NV_ERROR(dev, "Unknown HW sequencer entry size\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ NV_TRACE(dev, "Loading NV17 power sequencing microcode\n");
|
|
+
|
|
+ hwsq_entry_offset = hwsq_offset + 2 + entry * bytes_to_write;
|
|
+
|
|
+ /* set sequencer control */
|
|
+ bios_wr32(bios, 0x00001304, ROM32(bios->data[hwsq_entry_offset]));
|
|
+ bytes_to_write -= 4;
|
|
+
|
|
+ /* write ucode */
|
|
+ for (i = 0; i < bytes_to_write; i += 4)
|
|
+ bios_wr32(bios, 0x00001400 + i, ROM32(bios->data[hwsq_entry_offset + i + 4]));
|
|
+
|
|
+ /* twiddle NV_PBUS_DEBUG_4 */
|
|
+ bios_wr32(bios, NV_PBUS_DEBUG_4, bios_rd32(bios, NV_PBUS_DEBUG_4) | 0x18);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int load_nv17_hw_sequencer_ucode(struct drm_device *dev,
|
|
+ struct nvbios *bios)
|
|
+{
|
|
+ /*
|
|
+ * BMP based cards, from NV17, need a microcode loading to correctly
|
|
+ * control the GPIO etc for LVDS panels
|
|
+ *
|
|
+ * BIT based cards seem to do this directly in the init scripts
|
|
+ *
|
|
+ * The microcode entries are found by the "HWSQ" signature.
|
|
+ */
|
|
+
|
|
+ const uint8_t hwsq_signature[] = { 'H', 'W', 'S', 'Q' };
|
|
+ const int sz = sizeof(hwsq_signature);
|
|
+ int hwsq_offset;
|
|
+
|
|
+ hwsq_offset = findstr(bios->data, bios->length, hwsq_signature, sz);
|
|
+ if (!hwsq_offset)
|
|
+ return 0;
|
|
+
|
|
+ /* always use entry 0? */
|
|
+ return load_nv17_hwsq_ucode_entry(dev, bios, hwsq_offset + sz, 0);
|
|
+}
|
|
+
|
|
+uint8_t *nouveau_bios_embedded_edid(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nvbios *bios = &dev_priv->VBIOS;
|
|
+ const uint8_t edid_sig[] = {
|
|
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 };
|
|
+ uint16_t offset = 0;
|
|
+ uint16_t newoffset;
|
|
+ int searchlen = NV_PROM_SIZE;
|
|
+
|
|
+ if (bios->fp.edid)
|
|
+ return bios->fp.edid;
|
|
+
|
|
+ while (searchlen) {
|
|
+ newoffset = findstr(&bios->data[offset], searchlen,
|
|
+ edid_sig, 8);
|
|
+ if (!newoffset)
|
|
+ return NULL;
|
|
+ offset += newoffset;
|
|
+ if (!nv_cksum(&bios->data[offset], EDID1_LEN))
|
|
+ break;
|
|
+
|
|
+ searchlen -= offset;
|
|
+ offset++;
|
|
+ }
|
|
+
|
|
+ NV_TRACE(dev, "Found EDID in BIOS\n");
|
|
+
|
|
+ return bios->fp.edid = &bios->data[offset];
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_bios_run_init_table(struct drm_device *dev, uint16_t table,
|
|
+ struct dcb_entry *dcbent)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nvbios *bios = &dev_priv->VBIOS;
|
|
+ struct init_exec iexec = { true, false };
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&bios->lock, flags);
|
|
+ bios->display.output = dcbent;
|
|
+ parse_init_table(bios, table, &iexec);
|
|
+ bios->display.output = NULL;
|
|
+ spin_unlock_irqrestore(&bios->lock, flags);
|
|
+}
|
|
+
|
|
+static bool NVInitVBIOS(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nvbios *bios = &dev_priv->VBIOS;
|
|
+
|
|
+ memset(bios, 0, sizeof(struct nvbios));
|
|
+ spin_lock_init(&bios->lock);
|
|
+ bios->dev = dev;
|
|
+
|
|
+ if (!NVShadowVBIOS(dev, bios->data))
|
|
+ return false;
|
|
+
|
|
+ bios->length = NV_PROM_SIZE;
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static int nouveau_parse_vbios_struct(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nvbios *bios = &dev_priv->VBIOS;
|
|
+ const uint8_t bit_signature[] = { 0xff, 0xb8, 'B', 'I', 'T' };
|
|
+ const uint8_t bmp_signature[] = { 0xff, 0x7f, 'N', 'V', 0x0 };
|
|
+ int offset;
|
|
+
|
|
+ offset = findstr(bios->data, bios->length,
|
|
+ bit_signature, sizeof(bit_signature));
|
|
+ if (offset) {
|
|
+ NV_TRACE(dev, "BIT BIOS found\n");
|
|
+ return parse_bit_structure(bios, offset + 6);
|
|
+ }
|
|
+
|
|
+ offset = findstr(bios->data, bios->length,
|
|
+ bmp_signature, sizeof(bmp_signature));
|
|
+ if (offset) {
|
|
+ NV_TRACE(dev, "BMP BIOS found\n");
|
|
+ return parse_bmp_structure(dev, bios, offset);
|
|
+ }
|
|
+
|
|
+ NV_ERROR(dev, "No known BIOS signature found\n");
|
|
+ return -ENODEV;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_run_vbios_init(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nvbios *bios = &dev_priv->VBIOS;
|
|
+ int i, ret = 0;
|
|
+
|
|
+ NVLockVgaCrtcs(dev, false);
|
|
+ if (nv_two_heads(dev))
|
|
+ NVSetOwner(dev, bios->state.crtchead);
|
|
+
|
|
+ if (bios->major_version < 5) /* BMP only */
|
|
+ load_nv17_hw_sequencer_ucode(dev, bios);
|
|
+
|
|
+ if (bios->execute) {
|
|
+ bios->fp.last_script_invoc = 0;
|
|
+ bios->fp.lvds_init_run = false;
|
|
+ }
|
|
+
|
|
+ parse_init_tables(bios);
|
|
+
|
|
+ /*
|
|
+ * Runs some additional script seen on G8x VBIOSen. The VBIOS'
|
|
+ * parser will run this right after the init tables, the binary
|
|
+ * driver appears to run it at some point later.
|
|
+ */
|
|
+ if (bios->some_script_ptr) {
|
|
+ struct init_exec iexec = {true, false};
|
|
+
|
|
+ NV_INFO(dev, "Parsing VBIOS init table at offset 0x%04X\n",
|
|
+ bios->some_script_ptr);
|
|
+ parse_init_table(bios, bios->some_script_ptr, &iexec);
|
|
+ }
|
|
+
|
|
+ if (dev_priv->card_type >= NV_50) {
|
|
+ for (i = 0; i < bios->bdcb.dcb.entries; i++) {
|
|
+ nouveau_bios_run_display_table(dev,
|
|
+ &bios->bdcb.dcb.entry[i],
|
|
+ 0, 0);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ NVLockVgaCrtcs(dev, true);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void
|
|
+nouveau_bios_i2c_devices_takedown(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nvbios *bios = &dev_priv->VBIOS;
|
|
+ struct dcb_i2c_entry *entry;
|
|
+ int i;
|
|
+
|
|
+ entry = &bios->bdcb.dcb.i2c[0];
|
|
+ for (i = 0; i < DCB_MAX_NUM_I2C_ENTRIES; i++, entry++)
|
|
+ nouveau_i2c_fini(dev, entry);
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_bios_init(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nvbios *bios = &dev_priv->VBIOS;
|
|
+ uint32_t saved_nv_pextdev_boot_0;
|
|
+ bool was_locked;
|
|
+ int ret;
|
|
+
|
|
+ dev_priv->vbios = &bios->pub;
|
|
+
|
|
+ if (!NVInitVBIOS(dev))
|
|
+ return -ENODEV;
|
|
+
|
|
+ ret = nouveau_parse_vbios_struct(dev);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = parse_dcb_table(dev, bios, nv_two_heads(dev));
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ fixup_legacy_i2c(bios);
|
|
+ fixup_legacy_connector(bios);
|
|
+
|
|
+ if (!bios->major_version) /* we don't run version 0 bios */
|
|
+ return 0;
|
|
+
|
|
+ /* these will need remembering across a suspend */
|
|
+ saved_nv_pextdev_boot_0 = bios_rd32(bios, NV_PEXTDEV_BOOT_0);
|
|
+ bios->state.saved_nv_pfb_cfg0 = bios_rd32(bios, NV_PFB_CFG0);
|
|
+
|
|
+ /* init script execution disabled */
|
|
+ bios->execute = false;
|
|
+
|
|
+ /* ... unless card isn't POSTed already */
|
|
+ if (dev_priv->card_type >= NV_10 &&
|
|
+ NVReadVgaCrtc(dev, 0, 0x00) == 0 &&
|
|
+ NVReadVgaCrtc(dev, 0, 0x1a) == 0) {
|
|
+ NV_INFO(dev, "Adaptor not initialised\n");
|
|
+ if (dev_priv->card_type < NV_50) {
|
|
+ NV_ERROR(dev, "Unable to POST this chipset\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ NV_INFO(dev, "Running VBIOS init tables\n");
|
|
+ bios->execute = true;
|
|
+ }
|
|
+
|
|
+ bios_wr32(bios, NV_PEXTDEV_BOOT_0, saved_nv_pextdev_boot_0);
|
|
+
|
|
+ ret = nouveau_run_vbios_init(dev);
|
|
+ if (ret) {
|
|
+ dev_priv->vbios = NULL;
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /* feature_byte on BMP is poor, but init always sets CR4B */
|
|
+ was_locked = NVLockVgaCrtcs(dev, false);
|
|
+ if (bios->major_version < 5)
|
|
+ bios->is_mobile = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_4B) & 0x40;
|
|
+
|
|
+ /* all BIT systems need p_f_m_t for digital_min_front_porch */
|
|
+ if (bios->is_mobile || bios->major_version >= 5)
|
|
+ ret = parse_fp_mode_table(dev, bios);
|
|
+ NVLockVgaCrtcs(dev, was_locked);
|
|
+
|
|
+ /* allow subsequent scripts to execute */
|
|
+ bios->execute = true;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_bios_takedown(struct drm_device *dev)
|
|
+{
|
|
+ nouveau_bios_i2c_devices_takedown(dev);
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h
|
|
new file mode 100644
|
|
index 0000000..68446fd
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.h
|
|
@@ -0,0 +1,292 @@
|
|
+/*
|
|
+ * Copyright 2007-2008 Nouveau Project
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the next
|
|
+ * paragraph) shall be included in all copies or substantial portions of the
|
|
+ * Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
+ * DEALINGS IN THE SOFTWARE.
|
|
+ */
|
|
+
|
|
+#ifndef __NOUVEAU_BIOS_H__
|
|
+#define __NOUVEAU_BIOS_H__
|
|
+
|
|
+#include "nvreg.h"
|
|
+#include "nouveau_i2c.h"
|
|
+
|
|
+#define DCB_MAX_NUM_ENTRIES 16
|
|
+#define DCB_MAX_NUM_I2C_ENTRIES 16
|
|
+#define DCB_MAX_NUM_GPIO_ENTRIES 32
|
|
+#define DCB_MAX_NUM_CONNECTOR_ENTRIES 16
|
|
+
|
|
+#define DCB_LOC_ON_CHIP 0
|
|
+
|
|
+struct dcb_entry {
|
|
+ int index; /* may not be raw dcb index if merging has happened */
|
|
+ uint8_t type;
|
|
+ uint8_t i2c_index;
|
|
+ uint8_t heads;
|
|
+ uint8_t connector;
|
|
+ uint8_t bus;
|
|
+ uint8_t location;
|
|
+ uint8_t or;
|
|
+ bool duallink_possible;
|
|
+ union {
|
|
+ struct sor_conf {
|
|
+ int link;
|
|
+ } sorconf;
|
|
+ struct {
|
|
+ int maxfreq;
|
|
+ } crtconf;
|
|
+ struct {
|
|
+ struct sor_conf sor;
|
|
+ bool use_straps_for_mode;
|
|
+ bool use_power_scripts;
|
|
+ } lvdsconf;
|
|
+ struct {
|
|
+ bool has_component_output;
|
|
+ } tvconf;
|
|
+ struct {
|
|
+ struct sor_conf sor;
|
|
+ int link_nr;
|
|
+ int link_bw;
|
|
+ } dpconf;
|
|
+ struct {
|
|
+ struct sor_conf sor;
|
|
+ } tmdsconf;
|
|
+ };
|
|
+ bool i2c_upper_default;
|
|
+};
|
|
+
|
|
+struct dcb_i2c_entry {
|
|
+ uint8_t port_type;
|
|
+ uint8_t read, write;
|
|
+ struct nouveau_i2c_chan *chan;
|
|
+};
|
|
+
|
|
+struct parsed_dcb {
|
|
+ int entries;
|
|
+ struct dcb_entry entry[DCB_MAX_NUM_ENTRIES];
|
|
+ struct dcb_i2c_entry i2c[DCB_MAX_NUM_I2C_ENTRIES];
|
|
+};
|
|
+
|
|
+enum dcb_gpio_tag {
|
|
+ DCB_GPIO_TVDAC0 = 0xc,
|
|
+ DCB_GPIO_TVDAC1 = 0x2d,
|
|
+};
|
|
+
|
|
+struct dcb_gpio_entry {
|
|
+ enum dcb_gpio_tag tag;
|
|
+ int line;
|
|
+ bool invert;
|
|
+};
|
|
+
|
|
+struct parsed_dcb_gpio {
|
|
+ int entries;
|
|
+ struct dcb_gpio_entry entry[DCB_MAX_NUM_GPIO_ENTRIES];
|
|
+};
|
|
+
|
|
+struct dcb_connector_table_entry {
|
|
+ uint32_t entry;
|
|
+ uint8_t type;
|
|
+ uint8_t index;
|
|
+ uint8_t gpio_tag;
|
|
+};
|
|
+
|
|
+struct dcb_connector_table {
|
|
+ int entries;
|
|
+ struct dcb_connector_table_entry entry[DCB_MAX_NUM_CONNECTOR_ENTRIES];
|
|
+};
|
|
+
|
|
+struct bios_parsed_dcb {
|
|
+ uint8_t version;
|
|
+
|
|
+ struct parsed_dcb dcb;
|
|
+
|
|
+ uint8_t *i2c_table;
|
|
+ uint8_t i2c_default_indices;
|
|
+
|
|
+ uint16_t gpio_table_ptr;
|
|
+ struct parsed_dcb_gpio gpio;
|
|
+ uint16_t connector_table_ptr;
|
|
+ struct dcb_connector_table connector;
|
|
+};
|
|
+
|
|
+enum nouveau_encoder_type {
|
|
+ OUTPUT_ANALOG = 0,
|
|
+ OUTPUT_TV = 1,
|
|
+ OUTPUT_TMDS = 2,
|
|
+ OUTPUT_LVDS = 3,
|
|
+ OUTPUT_DP = 6,
|
|
+ OUTPUT_ANY = -1
|
|
+};
|
|
+
|
|
+enum nouveau_or {
|
|
+ OUTPUT_A = (1 << 0),
|
|
+ OUTPUT_B = (1 << 1),
|
|
+ OUTPUT_C = (1 << 2)
|
|
+};
|
|
+
|
|
+enum LVDS_script {
|
|
+ /* Order *does* matter here */
|
|
+ LVDS_INIT = 1,
|
|
+ LVDS_RESET,
|
|
+ LVDS_BACKLIGHT_ON,
|
|
+ LVDS_BACKLIGHT_OFF,
|
|
+ LVDS_PANEL_ON,
|
|
+ LVDS_PANEL_OFF
|
|
+};
|
|
+
|
|
+/* changing these requires matching changes to reg tables in nv_get_clock */
|
|
+#define MAX_PLL_TYPES 4
|
|
+enum pll_types {
|
|
+ NVPLL,
|
|
+ MPLL,
|
|
+ VPLL1,
|
|
+ VPLL2
|
|
+};
|
|
+
|
|
+struct pll_lims {
|
|
+ struct {
|
|
+ int minfreq;
|
|
+ int maxfreq;
|
|
+ int min_inputfreq;
|
|
+ int max_inputfreq;
|
|
+
|
|
+ uint8_t min_m;
|
|
+ uint8_t max_m;
|
|
+ uint8_t min_n;
|
|
+ uint8_t max_n;
|
|
+ } vco1, vco2;
|
|
+
|
|
+ uint8_t max_log2p;
|
|
+ /*
|
|
+ * for most pre nv50 cards setting a log2P of 7 (the common max_log2p
|
|
+ * value) is no different to 6 (at least for vplls) so allowing the MNP
|
|
+ * calc to use 7 causes the generated clock to be out by a factor of 2.
|
|
+ * however, max_log2p cannot be fixed-up during parsing as the
|
|
+ * unmodified max_log2p value is still needed for setting mplls, hence
|
|
+ * an additional max_usable_log2p member
|
|
+ */
|
|
+ uint8_t max_usable_log2p;
|
|
+ uint8_t log2p_bias;
|
|
+
|
|
+ uint8_t min_p;
|
|
+ uint8_t max_p;
|
|
+
|
|
+ int refclk;
|
|
+};
|
|
+
|
|
+struct nouveau_bios_info {
|
|
+ struct parsed_dcb *dcb;
|
|
+
|
|
+ uint8_t chip_version;
|
|
+
|
|
+ uint32_t dactestval;
|
|
+ uint32_t tvdactestval;
|
|
+ uint8_t digital_min_front_porch;
|
|
+ bool fp_no_ddc;
|
|
+};
|
|
+
|
|
+struct nvbios {
|
|
+ struct drm_device *dev;
|
|
+ struct nouveau_bios_info pub;
|
|
+
|
|
+ spinlock_t lock;
|
|
+
|
|
+ uint8_t data[NV_PROM_SIZE];
|
|
+ unsigned int length;
|
|
+ bool execute;
|
|
+
|
|
+ uint8_t major_version;
|
|
+ uint8_t feature_byte;
|
|
+ bool is_mobile;
|
|
+
|
|
+ uint32_t fmaxvco, fminvco;
|
|
+
|
|
+ bool old_style_init;
|
|
+ uint16_t init_script_tbls_ptr;
|
|
+ uint16_t extra_init_script_tbl_ptr;
|
|
+ uint16_t macro_index_tbl_ptr;
|
|
+ uint16_t macro_tbl_ptr;
|
|
+ uint16_t condition_tbl_ptr;
|
|
+ uint16_t io_condition_tbl_ptr;
|
|
+ uint16_t io_flag_condition_tbl_ptr;
|
|
+ uint16_t init_function_tbl_ptr;
|
|
+
|
|
+ uint16_t pll_limit_tbl_ptr;
|
|
+ uint16_t ram_restrict_tbl_ptr;
|
|
+ uint8_t ram_restrict_group_count;
|
|
+
|
|
+ uint16_t some_script_ptr; /* BIT I + 14 */
|
|
+ uint16_t init96_tbl_ptr; /* BIT I + 16 */
|
|
+
|
|
+ struct bios_parsed_dcb bdcb;
|
|
+
|
|
+ struct {
|
|
+ int crtchead;
|
|
+ /* these need remembering across suspend */
|
|
+ uint32_t saved_nv_pfb_cfg0;
|
|
+ } state;
|
|
+
|
|
+ struct {
|
|
+ struct dcb_entry *output;
|
|
+ uint16_t script_table_ptr;
|
|
+ uint16_t dp_table_ptr;
|
|
+ } display;
|
|
+
|
|
+ struct {
|
|
+ uint16_t fptablepointer; /* also used by tmds */
|
|
+ uint16_t fpxlatetableptr;
|
|
+ int xlatwidth;
|
|
+ uint16_t lvdsmanufacturerpointer;
|
|
+ uint16_t fpxlatemanufacturertableptr;
|
|
+ uint16_t mode_ptr;
|
|
+ uint16_t xlated_entry;
|
|
+ bool power_off_for_reset;
|
|
+ bool reset_after_pclk_change;
|
|
+ bool dual_link;
|
|
+ bool link_c_increment;
|
|
+ bool BITbit1;
|
|
+ bool if_is_24bit;
|
|
+ int duallink_transition_clk;
|
|
+ uint8_t strapless_is_24bit;
|
|
+ uint8_t *edid;
|
|
+
|
|
+ /* will need resetting after suspend */
|
|
+ int last_script_invoc;
|
|
+ bool lvds_init_run;
|
|
+ } fp;
|
|
+
|
|
+ struct {
|
|
+ uint16_t output0_script_ptr;
|
|
+ uint16_t output1_script_ptr;
|
|
+ } tmds;
|
|
+
|
|
+ struct {
|
|
+ uint16_t mem_init_tbl_ptr;
|
|
+ uint16_t sdr_seq_tbl_ptr;
|
|
+ uint16_t ddr_seq_tbl_ptr;
|
|
+
|
|
+ struct {
|
|
+ uint8_t crt, tv, panel;
|
|
+ } i2c_indices;
|
|
+
|
|
+ uint16_t lvds_single_a_script_ptr;
|
|
+ } legacy;
|
|
+};
|
|
+
|
|
+#endif
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c
|
|
new file mode 100644
|
|
index 0000000..028719f
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_bo.c
|
|
@@ -0,0 +1,773 @@
|
|
+/*
|
|
+ * Copyright 2007 Dave Airlied
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the next
|
|
+ * paragraph) shall be included in all copies or substantial portions of the
|
|
+ * Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
+ * OTHER DEALINGS IN THE SOFTWARE.
|
|
+ */
|
|
+/*
|
|
+ * Authors: Dave Airlied <airlied@linux.ie>
|
|
+ * Ben Skeggs <darktama@iinet.net.au>
|
|
+ * Jeremy Kolb <jkolb@brandeis.edu>
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+
|
|
+#include "nouveau_drm.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_dma.h"
|
|
+
|
|
+#include <linux/log2.h>
|
|
+
|
|
+static void
|
|
+nouveau_bo_del_ttm(struct ttm_buffer_object *bo)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
|
|
+ struct drm_device *dev = dev_priv->dev;
|
|
+ struct nouveau_bo *nvbo = nouveau_bo(bo);
|
|
+
|
|
+ ttm_bo_kunmap(&nvbo->kmap);
|
|
+
|
|
+ if (unlikely(nvbo->gem))
|
|
+ DRM_ERROR("bo %p still attached to GEM object\n", bo);
|
|
+
|
|
+ if (nvbo->tile)
|
|
+ nv10_mem_expire_tiling(dev, nvbo->tile, NULL);
|
|
+
|
|
+ spin_lock(&dev_priv->ttm.bo_list_lock);
|
|
+ list_del(&nvbo->head);
|
|
+ spin_unlock(&dev_priv->ttm.bo_list_lock);
|
|
+ kfree(nvbo);
|
|
+}
|
|
+
|
|
+static void
|
|
+nouveau_bo_fixup_align(struct drm_device *dev,
|
|
+ uint32_t tile_mode, uint32_t tile_flags,
|
|
+ int *align, int *size)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ /*
|
|
+ * Some of the tile_flags have a periodic structure of N*4096 bytes,
|
|
+ * align to to that as well as the page size. Align the size to the
|
|
+ * appropriate boundaries. This does imply that sizes are rounded up
|
|
+ * 3-7 pages, so be aware of this and do not waste memory by allocating
|
|
+ * many small buffers.
|
|
+ */
|
|
+ if (dev_priv->card_type == NV_50) {
|
|
+ uint32_t block_size = nouveau_mem_fb_amount(dev) >> 15;
|
|
+ int i;
|
|
+
|
|
+ switch (tile_flags) {
|
|
+ case 0x1800:
|
|
+ case 0x2800:
|
|
+ case 0x4800:
|
|
+ case 0x7a00:
|
|
+ if (is_power_of_2(block_size)) {
|
|
+ for (i = 1; i < 10; i++) {
|
|
+ *align = 12 * i * block_size;
|
|
+ if (!(*align % 65536))
|
|
+ break;
|
|
+ }
|
|
+ } else {
|
|
+ for (i = 1; i < 10; i++) {
|
|
+ *align = 8 * i * block_size;
|
|
+ if (!(*align % 65536))
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ *size = roundup(*size, *align);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ } else {
|
|
+ if (tile_mode) {
|
|
+ if (dev_priv->chipset >= 0x40) {
|
|
+ *align = 65536;
|
|
+ *size = roundup(*size, 64 * tile_mode);
|
|
+
|
|
+ } else if (dev_priv->chipset >= 0x30) {
|
|
+ *align = 32768;
|
|
+ *size = roundup(*size, 64 * tile_mode);
|
|
+
|
|
+ } else if (dev_priv->chipset >= 0x20) {
|
|
+ *align = 16384;
|
|
+ *size = roundup(*size, 64 * tile_mode);
|
|
+
|
|
+ } else if (dev_priv->chipset >= 0x10) {
|
|
+ *align = 16384;
|
|
+ *size = roundup(*size, 32 * tile_mode);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* ALIGN works only on powers of two. */
|
|
+ *size = roundup(*size, PAGE_SIZE);
|
|
+
|
|
+ if (dev_priv->card_type == NV_50) {
|
|
+ *size = roundup(*size, 65536);
|
|
+ *align = max(65536, *align);
|
|
+ }
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_bo_new(struct drm_device *dev, struct nouveau_channel *chan,
|
|
+ int size, int align, uint32_t flags, uint32_t tile_mode,
|
|
+ uint32_t tile_flags, bool no_vm, bool mappable,
|
|
+ struct nouveau_bo **pnvbo)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_bo *nvbo;
|
|
+ int ret = 0;
|
|
+
|
|
+ nvbo = kzalloc(sizeof(struct nouveau_bo), GFP_KERNEL);
|
|
+ if (!nvbo)
|
|
+ return -ENOMEM;
|
|
+ INIT_LIST_HEAD(&nvbo->head);
|
|
+ INIT_LIST_HEAD(&nvbo->entry);
|
|
+ nvbo->mappable = mappable;
|
|
+ nvbo->no_vm = no_vm;
|
|
+ nvbo->tile_mode = tile_mode;
|
|
+ nvbo->tile_flags = tile_flags;
|
|
+
|
|
+ nouveau_bo_fixup_align(dev, tile_mode, tile_flags, &align, &size);
|
|
+ align >>= PAGE_SHIFT;
|
|
+
|
|
+ nvbo->placement.fpfn = 0;
|
|
+ nvbo->placement.lpfn = mappable ? dev_priv->fb_mappable_pages : 0;
|
|
+ nouveau_bo_placement_set(nvbo, flags);
|
|
+
|
|
+ nvbo->channel = chan;
|
|
+ ret = ttm_bo_init(&dev_priv->ttm.bdev, &nvbo->bo, size,
|
|
+ ttm_bo_type_device, &nvbo->placement, align, 0,
|
|
+ false, NULL, size, nouveau_bo_del_ttm);
|
|
+ nvbo->channel = NULL;
|
|
+ if (ret) {
|
|
+ /* ttm will call nouveau_bo_del_ttm if it fails.. */
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ spin_lock(&dev_priv->ttm.bo_list_lock);
|
|
+ list_add_tail(&nvbo->head, &dev_priv->ttm.bo_list);
|
|
+ spin_unlock(&dev_priv->ttm.bo_list_lock);
|
|
+ *pnvbo = nvbo;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_bo_placement_set(struct nouveau_bo *nvbo, uint32_t memtype)
|
|
+{
|
|
+ int n = 0;
|
|
+
|
|
+ if (memtype & TTM_PL_FLAG_VRAM)
|
|
+ nvbo->placements[n++] = TTM_PL_FLAG_VRAM | TTM_PL_MASK_CACHING;
|
|
+ if (memtype & TTM_PL_FLAG_TT)
|
|
+ nvbo->placements[n++] = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
|
|
+ if (memtype & TTM_PL_FLAG_SYSTEM)
|
|
+ nvbo->placements[n++] = TTM_PL_FLAG_SYSTEM | TTM_PL_MASK_CACHING;
|
|
+ nvbo->placement.placement = nvbo->placements;
|
|
+ nvbo->placement.busy_placement = nvbo->placements;
|
|
+ nvbo->placement.num_placement = n;
|
|
+ nvbo->placement.num_busy_placement = n;
|
|
+
|
|
+ if (nvbo->pin_refcnt) {
|
|
+ while (n--)
|
|
+ nvbo->placements[n] |= TTM_PL_FLAG_NO_EVICT;
|
|
+ }
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = nouveau_bdev(nvbo->bo.bdev);
|
|
+ struct ttm_buffer_object *bo = &nvbo->bo;
|
|
+ int ret, i;
|
|
+
|
|
+ if (nvbo->pin_refcnt && !(memtype & (1 << bo->mem.mem_type))) {
|
|
+ NV_ERROR(nouveau_bdev(bo->bdev)->dev,
|
|
+ "bo %p pinned elsewhere: 0x%08x vs 0x%08x\n", bo,
|
|
+ 1 << bo->mem.mem_type, memtype);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (nvbo->pin_refcnt++)
|
|
+ return 0;
|
|
+
|
|
+ ret = ttm_bo_reserve(bo, false, false, false, 0);
|
|
+ if (ret)
|
|
+ goto out;
|
|
+
|
|
+ nouveau_bo_placement_set(nvbo, memtype);
|
|
+ for (i = 0; i < nvbo->placement.num_placement; i++)
|
|
+ nvbo->placements[i] |= TTM_PL_FLAG_NO_EVICT;
|
|
+
|
|
+ ret = ttm_bo_validate(bo, &nvbo->placement, false, false);
|
|
+ if (ret == 0) {
|
|
+ switch (bo->mem.mem_type) {
|
|
+ case TTM_PL_VRAM:
|
|
+ dev_priv->fb_aper_free -= bo->mem.size;
|
|
+ break;
|
|
+ case TTM_PL_TT:
|
|
+ dev_priv->gart_info.aper_free -= bo->mem.size;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ ttm_bo_unreserve(bo);
|
|
+out:
|
|
+ if (unlikely(ret))
|
|
+ nvbo->pin_refcnt--;
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_bo_unpin(struct nouveau_bo *nvbo)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = nouveau_bdev(nvbo->bo.bdev);
|
|
+ struct ttm_buffer_object *bo = &nvbo->bo;
|
|
+ int ret, i;
|
|
+
|
|
+ if (--nvbo->pin_refcnt)
|
|
+ return 0;
|
|
+
|
|
+ ret = ttm_bo_reserve(bo, false, false, false, 0);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ for (i = 0; i < nvbo->placement.num_placement; i++)
|
|
+ nvbo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT;
|
|
+
|
|
+ ret = ttm_bo_validate(bo, &nvbo->placement, false, false);
|
|
+ if (ret == 0) {
|
|
+ switch (bo->mem.mem_type) {
|
|
+ case TTM_PL_VRAM:
|
|
+ dev_priv->fb_aper_free += bo->mem.size;
|
|
+ break;
|
|
+ case TTM_PL_TT:
|
|
+ dev_priv->gart_info.aper_free += bo->mem.size;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ttm_bo_unreserve(bo);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_bo_map(struct nouveau_bo *nvbo)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ret = ttm_bo_reserve(&nvbo->bo, false, false, false, 0);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = ttm_bo_kmap(&nvbo->bo, 0, nvbo->bo.mem.num_pages, &nvbo->kmap);
|
|
+ ttm_bo_unreserve(&nvbo->bo);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_bo_unmap(struct nouveau_bo *nvbo)
|
|
+{
|
|
+ ttm_bo_kunmap(&nvbo->kmap);
|
|
+}
|
|
+
|
|
+u16
|
|
+nouveau_bo_rd16(struct nouveau_bo *nvbo, unsigned index)
|
|
+{
|
|
+ bool is_iomem;
|
|
+ u16 *mem = ttm_kmap_obj_virtual(&nvbo->kmap, &is_iomem);
|
|
+ mem = &mem[index];
|
|
+ if (is_iomem)
|
|
+ return ioread16_native((void __force __iomem *)mem);
|
|
+ else
|
|
+ return *mem;
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_bo_wr16(struct nouveau_bo *nvbo, unsigned index, u16 val)
|
|
+{
|
|
+ bool is_iomem;
|
|
+ u16 *mem = ttm_kmap_obj_virtual(&nvbo->kmap, &is_iomem);
|
|
+ mem = &mem[index];
|
|
+ if (is_iomem)
|
|
+ iowrite16_native(val, (void __force __iomem *)mem);
|
|
+ else
|
|
+ *mem = val;
|
|
+}
|
|
+
|
|
+u32
|
|
+nouveau_bo_rd32(struct nouveau_bo *nvbo, unsigned index)
|
|
+{
|
|
+ bool is_iomem;
|
|
+ u32 *mem = ttm_kmap_obj_virtual(&nvbo->kmap, &is_iomem);
|
|
+ mem = &mem[index];
|
|
+ if (is_iomem)
|
|
+ return ioread32_native((void __force __iomem *)mem);
|
|
+ else
|
|
+ return *mem;
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_bo_wr32(struct nouveau_bo *nvbo, unsigned index, u32 val)
|
|
+{
|
|
+ bool is_iomem;
|
|
+ u32 *mem = ttm_kmap_obj_virtual(&nvbo->kmap, &is_iomem);
|
|
+ mem = &mem[index];
|
|
+ if (is_iomem)
|
|
+ iowrite32_native(val, (void __force __iomem *)mem);
|
|
+ else
|
|
+ *mem = val;
|
|
+}
|
|
+
|
|
+static struct ttm_backend *
|
|
+nouveau_bo_create_ttm_backend_entry(struct ttm_bo_device *bdev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = nouveau_bdev(bdev);
|
|
+ struct drm_device *dev = dev_priv->dev;
|
|
+
|
|
+ switch (dev_priv->gart_info.type) {
|
|
+#if __OS_HAS_AGP
|
|
+ case NOUVEAU_GART_AGP:
|
|
+ return ttm_agp_backend_init(bdev, dev->agp->bridge);
|
|
+#endif
|
|
+ case NOUVEAU_GART_SGDMA:
|
|
+ return nouveau_sgdma_init_ttm(dev);
|
|
+ default:
|
|
+ NV_ERROR(dev, "Unknown GART type %d\n",
|
|
+ dev_priv->gart_info.type);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_bo_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags)
|
|
+{
|
|
+ /* We'll do this from user space. */
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
|
|
+ struct ttm_mem_type_manager *man)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = nouveau_bdev(bdev);
|
|
+ struct drm_device *dev = dev_priv->dev;
|
|
+
|
|
+ switch (type) {
|
|
+ case TTM_PL_SYSTEM:
|
|
+ man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
|
|
+ man->available_caching = TTM_PL_MASK_CACHING;
|
|
+ man->default_caching = TTM_PL_FLAG_CACHED;
|
|
+ break;
|
|
+ case TTM_PL_VRAM:
|
|
+ man->flags = TTM_MEMTYPE_FLAG_FIXED |
|
|
+ TTM_MEMTYPE_FLAG_MAPPABLE |
|
|
+ TTM_MEMTYPE_FLAG_NEEDS_IOREMAP;
|
|
+ man->available_caching = TTM_PL_FLAG_UNCACHED |
|
|
+ TTM_PL_FLAG_WC;
|
|
+ man->default_caching = TTM_PL_FLAG_WC;
|
|
+
|
|
+ man->io_addr = NULL;
|
|
+ man->io_offset = drm_get_resource_start(dev, 1);
|
|
+ man->io_size = drm_get_resource_len(dev, 1);
|
|
+ if (man->io_size > nouveau_mem_fb_amount(dev))
|
|
+ man->io_size = nouveau_mem_fb_amount(dev);
|
|
+
|
|
+ man->gpu_offset = dev_priv->vm_vram_base;
|
|
+ break;
|
|
+ case TTM_PL_TT:
|
|
+ switch (dev_priv->gart_info.type) {
|
|
+ case NOUVEAU_GART_AGP:
|
|
+ man->flags = TTM_MEMTYPE_FLAG_MAPPABLE |
|
|
+ TTM_MEMTYPE_FLAG_NEEDS_IOREMAP;
|
|
+ man->available_caching = TTM_PL_FLAG_UNCACHED;
|
|
+ man->default_caching = TTM_PL_FLAG_UNCACHED;
|
|
+ break;
|
|
+ case NOUVEAU_GART_SGDMA:
|
|
+ man->flags = TTM_MEMTYPE_FLAG_MAPPABLE |
|
|
+ TTM_MEMTYPE_FLAG_CMA;
|
|
+ man->available_caching = TTM_PL_MASK_CACHING;
|
|
+ man->default_caching = TTM_PL_FLAG_CACHED;
|
|
+ break;
|
|
+ default:
|
|
+ NV_ERROR(dev, "Unknown GART type: %d\n",
|
|
+ dev_priv->gart_info.type);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ man->io_offset = dev_priv->gart_info.aper_base;
|
|
+ man->io_size = dev_priv->gart_info.aper_size;
|
|
+ man->io_addr = NULL;
|
|
+ man->gpu_offset = dev_priv->vm_gart_base;
|
|
+ break;
|
|
+ default:
|
|
+ NV_ERROR(dev, "Unsupported memory type %u\n", (unsigned)type);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void
|
|
+nouveau_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl)
|
|
+{
|
|
+ struct nouveau_bo *nvbo = nouveau_bo(bo);
|
|
+
|
|
+ switch (bo->mem.mem_type) {
|
|
+ case TTM_PL_VRAM:
|
|
+ nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_TT |
|
|
+ TTM_PL_FLAG_SYSTEM);
|
|
+ break;
|
|
+ default:
|
|
+ nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_SYSTEM);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ *pl = nvbo->placement;
|
|
+}
|
|
+
|
|
+
|
|
+/* GPU-assisted copy using NV_MEMORY_TO_MEMORY_FORMAT, can access
|
|
+ * TTM_PL_{VRAM,TT} directly.
|
|
+ */
|
|
+
|
|
+static int
|
|
+nouveau_bo_move_accel_cleanup(struct nouveau_channel *chan,
|
|
+ struct nouveau_bo *nvbo, bool evict, bool no_wait,
|
|
+ struct ttm_mem_reg *new_mem)
|
|
+{
|
|
+ struct nouveau_fence *fence = NULL;
|
|
+ int ret;
|
|
+
|
|
+ ret = nouveau_fence_new(chan, &fence, true);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = ttm_bo_move_accel_cleanup(&nvbo->bo, fence, NULL,
|
|
+ evict, no_wait, new_mem);
|
|
+ if (nvbo->channel && nvbo->channel != chan)
|
|
+ ret = nouveau_fence_wait(fence, NULL, false, false);
|
|
+ nouveau_fence_unref((void *)&fence);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static inline uint32_t
|
|
+nouveau_bo_mem_ctxdma(struct nouveau_bo *nvbo, struct nouveau_channel *chan,
|
|
+ struct ttm_mem_reg *mem)
|
|
+{
|
|
+ if (chan == nouveau_bdev(nvbo->bo.bdev)->channel) {
|
|
+ if (mem->mem_type == TTM_PL_TT)
|
|
+ return NvDmaGART;
|
|
+ return NvDmaVRAM;
|
|
+ }
|
|
+
|
|
+ if (mem->mem_type == TTM_PL_TT)
|
|
+ return chan->gart_handle;
|
|
+ return chan->vram_handle;
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr,
|
|
+ int no_wait, struct ttm_mem_reg *new_mem)
|
|
+{
|
|
+ struct nouveau_bo *nvbo = nouveau_bo(bo);
|
|
+ struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
|
|
+ struct ttm_mem_reg *old_mem = &bo->mem;
|
|
+ struct nouveau_channel *chan;
|
|
+ uint64_t src_offset, dst_offset;
|
|
+ uint32_t page_count;
|
|
+ int ret;
|
|
+
|
|
+ chan = nvbo->channel;
|
|
+ if (!chan || nvbo->tile_flags || nvbo->no_vm)
|
|
+ chan = dev_priv->channel;
|
|
+
|
|
+ src_offset = old_mem->mm_node->start << PAGE_SHIFT;
|
|
+ dst_offset = new_mem->mm_node->start << PAGE_SHIFT;
|
|
+ if (chan != dev_priv->channel) {
|
|
+ if (old_mem->mem_type == TTM_PL_TT)
|
|
+ src_offset += dev_priv->vm_gart_base;
|
|
+ else
|
|
+ src_offset += dev_priv->vm_vram_base;
|
|
+
|
|
+ if (new_mem->mem_type == TTM_PL_TT)
|
|
+ dst_offset += dev_priv->vm_gart_base;
|
|
+ else
|
|
+ dst_offset += dev_priv->vm_vram_base;
|
|
+ }
|
|
+
|
|
+ ret = RING_SPACE(chan, 3);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_SOURCE, 2);
|
|
+ OUT_RING(chan, nouveau_bo_mem_ctxdma(nvbo, chan, old_mem));
|
|
+ OUT_RING(chan, nouveau_bo_mem_ctxdma(nvbo, chan, new_mem));
|
|
+
|
|
+ if (dev_priv->card_type >= NV_50) {
|
|
+ ret = RING_SPACE(chan, 4);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ BEGIN_RING(chan, NvSubM2MF, 0x0200, 1);
|
|
+ OUT_RING(chan, 1);
|
|
+ BEGIN_RING(chan, NvSubM2MF, 0x021c, 1);
|
|
+ OUT_RING(chan, 1);
|
|
+ }
|
|
+
|
|
+ page_count = new_mem->num_pages;
|
|
+ while (page_count) {
|
|
+ int line_count = (page_count > 2047) ? 2047 : page_count;
|
|
+
|
|
+ if (dev_priv->card_type >= NV_50) {
|
|
+ ret = RING_SPACE(chan, 3);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ BEGIN_RING(chan, NvSubM2MF, 0x0238, 2);
|
|
+ OUT_RING(chan, upper_32_bits(src_offset));
|
|
+ OUT_RING(chan, upper_32_bits(dst_offset));
|
|
+ }
|
|
+ ret = RING_SPACE(chan, 11);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ BEGIN_RING(chan, NvSubM2MF,
|
|
+ NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8);
|
|
+ OUT_RING(chan, lower_32_bits(src_offset));
|
|
+ OUT_RING(chan, lower_32_bits(dst_offset));
|
|
+ OUT_RING(chan, PAGE_SIZE); /* src_pitch */
|
|
+ OUT_RING(chan, PAGE_SIZE); /* dst_pitch */
|
|
+ OUT_RING(chan, PAGE_SIZE); /* line_length */
|
|
+ OUT_RING(chan, line_count);
|
|
+ OUT_RING(chan, (1<<8)|(1<<0));
|
|
+ OUT_RING(chan, 0);
|
|
+ BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NOP, 1);
|
|
+ OUT_RING(chan, 0);
|
|
+
|
|
+ page_count -= line_count;
|
|
+ src_offset += (PAGE_SIZE * line_count);
|
|
+ dst_offset += (PAGE_SIZE * line_count);
|
|
+ }
|
|
+
|
|
+ return nouveau_bo_move_accel_cleanup(chan, nvbo, evict, no_wait, new_mem);
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_bo_move_flipd(struct ttm_buffer_object *bo, bool evict, bool intr,
|
|
+ bool no_wait, struct ttm_mem_reg *new_mem)
|
|
+{
|
|
+ u32 placement_memtype = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
|
|
+ struct ttm_placement placement;
|
|
+ struct ttm_mem_reg tmp_mem;
|
|
+ int ret;
|
|
+
|
|
+ placement.fpfn = placement.lpfn = 0;
|
|
+ placement.num_placement = placement.num_busy_placement = 1;
|
|
+ placement.placement = placement.busy_placement = &placement_memtype;
|
|
+
|
|
+ tmp_mem = *new_mem;
|
|
+ tmp_mem.mm_node = NULL;
|
|
+ ret = ttm_bo_mem_space(bo, &placement, &tmp_mem, intr, no_wait);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = ttm_tt_bind(bo->ttm, &tmp_mem);
|
|
+ if (ret)
|
|
+ goto out;
|
|
+
|
|
+ ret = nouveau_bo_move_m2mf(bo, true, intr, no_wait, &tmp_mem);
|
|
+ if (ret)
|
|
+ goto out;
|
|
+
|
|
+ ret = ttm_bo_move_ttm(bo, evict, no_wait, new_mem);
|
|
+out:
|
|
+ if (tmp_mem.mm_node) {
|
|
+ spin_lock(&bo->bdev->glob->lru_lock);
|
|
+ drm_mm_put_block(tmp_mem.mm_node);
|
|
+ spin_unlock(&bo->bdev->glob->lru_lock);
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_bo_move_flips(struct ttm_buffer_object *bo, bool evict, bool intr,
|
|
+ bool no_wait, struct ttm_mem_reg *new_mem)
|
|
+{
|
|
+ u32 placement_memtype = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
|
|
+ struct ttm_placement placement;
|
|
+ struct ttm_mem_reg tmp_mem;
|
|
+ int ret;
|
|
+
|
|
+ placement.fpfn = placement.lpfn = 0;
|
|
+ placement.num_placement = placement.num_busy_placement = 1;
|
|
+ placement.placement = placement.busy_placement = &placement_memtype;
|
|
+
|
|
+ tmp_mem = *new_mem;
|
|
+ tmp_mem.mm_node = NULL;
|
|
+ ret = ttm_bo_mem_space(bo, &placement, &tmp_mem, intr, no_wait);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = ttm_bo_move_ttm(bo, evict, no_wait, &tmp_mem);
|
|
+ if (ret)
|
|
+ goto out;
|
|
+
|
|
+ ret = nouveau_bo_move_m2mf(bo, evict, intr, no_wait, new_mem);
|
|
+ if (ret)
|
|
+ goto out;
|
|
+
|
|
+out:
|
|
+ if (tmp_mem.mm_node) {
|
|
+ spin_lock(&bo->bdev->glob->lru_lock);
|
|
+ drm_mm_put_block(tmp_mem.mm_node);
|
|
+ spin_unlock(&bo->bdev->glob->lru_lock);
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_bo_vm_bind(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem,
|
|
+ struct nouveau_tile_reg **new_tile)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
|
|
+ struct drm_device *dev = dev_priv->dev;
|
|
+ struct nouveau_bo *nvbo = nouveau_bo(bo);
|
|
+ uint64_t offset;
|
|
+ int ret;
|
|
+
|
|
+ if (nvbo->no_vm || new_mem->mem_type != TTM_PL_VRAM) {
|
|
+ /* Nothing to do. */
|
|
+ *new_tile = NULL;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ offset = new_mem->mm_node->start << PAGE_SHIFT;
|
|
+
|
|
+ if (dev_priv->card_type == NV_50) {
|
|
+ ret = nv50_mem_vm_bind_linear(dev,
|
|
+ offset + dev_priv->vm_vram_base,
|
|
+ new_mem->size, nvbo->tile_flags,
|
|
+ offset);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ } else if (dev_priv->card_type >= NV_10) {
|
|
+ *new_tile = nv10_mem_set_tiling(dev, offset, new_mem->size,
|
|
+ nvbo->tile_mode);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void
|
|
+nouveau_bo_vm_cleanup(struct ttm_buffer_object *bo,
|
|
+ struct nouveau_tile_reg *new_tile,
|
|
+ struct nouveau_tile_reg **old_tile)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
|
|
+ struct drm_device *dev = dev_priv->dev;
|
|
+
|
|
+ if (dev_priv->card_type >= NV_10 &&
|
|
+ dev_priv->card_type < NV_50) {
|
|
+ if (*old_tile)
|
|
+ nv10_mem_expire_tiling(dev, *old_tile, bo->sync_obj);
|
|
+
|
|
+ *old_tile = new_tile;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, bool intr,
|
|
+ bool no_wait, struct ttm_mem_reg *new_mem)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
|
|
+ struct nouveau_bo *nvbo = nouveau_bo(bo);
|
|
+ struct ttm_mem_reg *old_mem = &bo->mem;
|
|
+ struct nouveau_tile_reg *new_tile = NULL;
|
|
+ int ret = 0;
|
|
+
|
|
+ ret = nouveau_bo_vm_bind(bo, new_mem, &new_tile);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* Software copy if the card isn't up and running yet. */
|
|
+ if (dev_priv->init_state != NOUVEAU_CARD_INIT_DONE ||
|
|
+ !dev_priv->channel) {
|
|
+ ret = ttm_bo_move_memcpy(bo, evict, no_wait, new_mem);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /* Fake bo copy. */
|
|
+ if (old_mem->mem_type == TTM_PL_SYSTEM && !bo->ttm) {
|
|
+ BUG_ON(bo->mem.mm_node != NULL);
|
|
+ bo->mem = *new_mem;
|
|
+ new_mem->mm_node = NULL;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /* Hardware assisted copy. */
|
|
+ if (new_mem->mem_type == TTM_PL_SYSTEM)
|
|
+ ret = nouveau_bo_move_flipd(bo, evict, intr, no_wait, new_mem);
|
|
+ else if (old_mem->mem_type == TTM_PL_SYSTEM)
|
|
+ ret = nouveau_bo_move_flips(bo, evict, intr, no_wait, new_mem);
|
|
+ else
|
|
+ ret = nouveau_bo_move_m2mf(bo, evict, intr, no_wait, new_mem);
|
|
+
|
|
+ if (!ret)
|
|
+ goto out;
|
|
+
|
|
+ /* Fallback to software copy. */
|
|
+ ret = ttm_bo_move_memcpy(bo, evict, no_wait, new_mem);
|
|
+
|
|
+out:
|
|
+ if (ret)
|
|
+ nouveau_bo_vm_cleanup(bo, NULL, &new_tile);
|
|
+ else
|
|
+ nouveau_bo_vm_cleanup(bo, new_tile, &nvbo->tile);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_bo_verify_access(struct ttm_buffer_object *bo, struct file *filp)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+struct ttm_bo_driver nouveau_bo_driver = {
|
|
+ .create_ttm_backend_entry = nouveau_bo_create_ttm_backend_entry,
|
|
+ .invalidate_caches = nouveau_bo_invalidate_caches,
|
|
+ .init_mem_type = nouveau_bo_init_mem_type,
|
|
+ .evict_flags = nouveau_bo_evict_flags,
|
|
+ .move = nouveau_bo_move,
|
|
+ .verify_access = nouveau_bo_verify_access,
|
|
+ .sync_obj_signaled = nouveau_fence_signalled,
|
|
+ .sync_obj_wait = nouveau_fence_wait,
|
|
+ .sync_obj_flush = nouveau_fence_flush,
|
|
+ .sync_obj_unref = nouveau_fence_unref,
|
|
+ .sync_obj_ref = nouveau_fence_ref,
|
|
+};
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_calc.c b/drivers/gpu/drm/nouveau/nouveau_calc.c
|
|
new file mode 100644
|
|
index 0000000..ee2b845
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_calc.c
|
|
@@ -0,0 +1,478 @@
|
|
+/*
|
|
+ * Copyright 1993-2003 NVIDIA, Corporation
|
|
+ * Copyright 2007-2009 Stuart Bennett
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice shall be included in
|
|
+ * all copies or substantial portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
|
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
+ * SOFTWARE.
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_hw.h"
|
|
+
|
|
+/****************************************************************************\
|
|
+* *
|
|
+* The video arbitration routines calculate some "magic" numbers. Fixes *
|
|
+* the snow seen when accessing the framebuffer without it. *
|
|
+* It just works (I hope). *
|
|
+* *
|
|
+\****************************************************************************/
|
|
+
|
|
+struct nv_fifo_info {
|
|
+ int lwm;
|
|
+ int burst;
|
|
+};
|
|
+
|
|
+struct nv_sim_state {
|
|
+ int pclk_khz;
|
|
+ int mclk_khz;
|
|
+ int nvclk_khz;
|
|
+ int bpp;
|
|
+ int mem_page_miss;
|
|
+ int mem_latency;
|
|
+ int memory_type;
|
|
+ int memory_width;
|
|
+ int two_heads;
|
|
+};
|
|
+
|
|
+static void
|
|
+nv04_calc_arb(struct nv_fifo_info *fifo, struct nv_sim_state *arb)
|
|
+{
|
|
+ int pagemiss, cas, width, bpp;
|
|
+ int nvclks, mclks, pclks, crtpagemiss;
|
|
+ int found, mclk_extra, mclk_loop, cbs, m1, p1;
|
|
+ int mclk_freq, pclk_freq, nvclk_freq;
|
|
+ int us_m, us_n, us_p, crtc_drain_rate;
|
|
+ int cpm_us, us_crt, clwm;
|
|
+
|
|
+ pclk_freq = arb->pclk_khz;
|
|
+ mclk_freq = arb->mclk_khz;
|
|
+ nvclk_freq = arb->nvclk_khz;
|
|
+ pagemiss = arb->mem_page_miss;
|
|
+ cas = arb->mem_latency;
|
|
+ width = arb->memory_width >> 6;
|
|
+ bpp = arb->bpp;
|
|
+ cbs = 128;
|
|
+
|
|
+ pclks = 2;
|
|
+ nvclks = 10;
|
|
+ mclks = 13 + cas;
|
|
+ mclk_extra = 3;
|
|
+ found = 0;
|
|
+
|
|
+ while (!found) {
|
|
+ found = 1;
|
|
+
|
|
+ mclk_loop = mclks + mclk_extra;
|
|
+ us_m = mclk_loop * 1000 * 1000 / mclk_freq;
|
|
+ us_n = nvclks * 1000 * 1000 / nvclk_freq;
|
|
+ us_p = nvclks * 1000 * 1000 / pclk_freq;
|
|
+
|
|
+ crtc_drain_rate = pclk_freq * bpp / 8;
|
|
+ crtpagemiss = 2;
|
|
+ crtpagemiss += 1;
|
|
+ cpm_us = crtpagemiss * pagemiss * 1000 * 1000 / mclk_freq;
|
|
+ us_crt = cpm_us + us_m + us_n + us_p;
|
|
+ clwm = us_crt * crtc_drain_rate / (1000 * 1000);
|
|
+ clwm++;
|
|
+
|
|
+ m1 = clwm + cbs - 512;
|
|
+ p1 = m1 * pclk_freq / mclk_freq;
|
|
+ p1 = p1 * bpp / 8;
|
|
+ if ((p1 < m1 && m1 > 0) || clwm > 519) {
|
|
+ found = !mclk_extra;
|
|
+ mclk_extra--;
|
|
+ }
|
|
+ if (clwm < 384)
|
|
+ clwm = 384;
|
|
+
|
|
+ fifo->lwm = clwm;
|
|
+ fifo->burst = cbs;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+nv10_calc_arb(struct nv_fifo_info *fifo, struct nv_sim_state *arb)
|
|
+{
|
|
+ int fill_rate, drain_rate;
|
|
+ int pclks, nvclks, mclks, xclks;
|
|
+ int pclk_freq, nvclk_freq, mclk_freq;
|
|
+ int fill_lat, extra_lat;
|
|
+ int max_burst_o, max_burst_l;
|
|
+ int fifo_len, min_lwm, max_lwm;
|
|
+ const int burst_lat = 80; /* Maximum allowable latency due
|
|
+ * to the CRTC FIFO burst. (ns) */
|
|
+
|
|
+ pclk_freq = arb->pclk_khz;
|
|
+ nvclk_freq = arb->nvclk_khz;
|
|
+ mclk_freq = arb->mclk_khz;
|
|
+
|
|
+ fill_rate = mclk_freq * arb->memory_width / 8; /* kB/s */
|
|
+ drain_rate = pclk_freq * arb->bpp / 8; /* kB/s */
|
|
+
|
|
+ fifo_len = arb->two_heads ? 1536 : 1024; /* B */
|
|
+
|
|
+ /* Fixed FIFO refill latency. */
|
|
+
|
|
+ pclks = 4; /* lwm detect. */
|
|
+
|
|
+ nvclks = 3 /* lwm -> sync. */
|
|
+ + 2 /* fbi bus cycles (1 req + 1 busy) */
|
|
+ + 1 /* 2 edge sync. may be very close to edge so
|
|
+ * just put one. */
|
|
+ + 1 /* fbi_d_rdv_n */
|
|
+ + 1 /* Fbi_d_rdata */
|
|
+ + 1; /* crtfifo load */
|
|
+
|
|
+ mclks = 1 /* 2 edge sync. may be very close to edge so
|
|
+ * just put one. */
|
|
+ + 1 /* arb_hp_req */
|
|
+ + 5 /* tiling pipeline */
|
|
+ + 2 /* latency fifo */
|
|
+ + 2 /* memory request to fbio block */
|
|
+ + 7; /* data returned from fbio block */
|
|
+
|
|
+ /* Need to accumulate 256 bits for read */
|
|
+ mclks += (arb->memory_type == 0 ? 2 : 1)
|
|
+ * arb->memory_width / 32;
|
|
+
|
|
+ fill_lat = mclks * 1000 * 1000 / mclk_freq /* minimum mclk latency */
|
|
+ + nvclks * 1000 * 1000 / nvclk_freq /* nvclk latency */
|
|
+ + pclks * 1000 * 1000 / pclk_freq; /* pclk latency */
|
|
+
|
|
+ /* Conditional FIFO refill latency. */
|
|
+
|
|
+ xclks = 2 * arb->mem_page_miss + mclks /* Extra latency due to
|
|
+ * the overlay. */
|
|
+ + 2 * arb->mem_page_miss /* Extra pagemiss latency. */
|
|
+ + (arb->bpp == 32 ? 8 : 4); /* Margin of error. */
|
|
+
|
|
+ extra_lat = xclks * 1000 * 1000 / mclk_freq;
|
|
+
|
|
+ if (arb->two_heads)
|
|
+ /* Account for another CRTC. */
|
|
+ extra_lat += fill_lat + extra_lat + burst_lat;
|
|
+
|
|
+ /* FIFO burst */
|
|
+
|
|
+ /* Max burst not leading to overflows. */
|
|
+ max_burst_o = (1 + fifo_len - extra_lat * drain_rate / (1000 * 1000))
|
|
+ * (fill_rate / 1000) / ((fill_rate - drain_rate) / 1000);
|
|
+ fifo->burst = min(max_burst_o, 1024);
|
|
+
|
|
+ /* Max burst value with an acceptable latency. */
|
|
+ max_burst_l = burst_lat * fill_rate / (1000 * 1000);
|
|
+ fifo->burst = min(max_burst_l, fifo->burst);
|
|
+
|
|
+ fifo->burst = rounddown_pow_of_two(fifo->burst);
|
|
+
|
|
+ /* FIFO low watermark */
|
|
+
|
|
+ min_lwm = (fill_lat + extra_lat) * drain_rate / (1000 * 1000) + 1;
|
|
+ max_lwm = fifo_len - fifo->burst
|
|
+ + fill_lat * drain_rate / (1000 * 1000)
|
|
+ + fifo->burst * drain_rate / fill_rate;
|
|
+
|
|
+ fifo->lwm = min_lwm + 10 * (max_lwm - min_lwm) / 100; /* Empirical. */
|
|
+}
|
|
+
|
|
+static void
|
|
+nv04_update_arb(struct drm_device *dev, int VClk, int bpp,
|
|
+ int *burst, int *lwm)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nv_fifo_info fifo_data;
|
|
+ struct nv_sim_state sim_data;
|
|
+ int MClk = nouveau_hw_get_clock(dev, MPLL);
|
|
+ int NVClk = nouveau_hw_get_clock(dev, NVPLL);
|
|
+ uint32_t cfg1 = nvReadFB(dev, NV_PFB_CFG1);
|
|
+
|
|
+ sim_data.pclk_khz = VClk;
|
|
+ sim_data.mclk_khz = MClk;
|
|
+ sim_data.nvclk_khz = NVClk;
|
|
+ sim_data.bpp = bpp;
|
|
+ sim_data.two_heads = nv_two_heads(dev);
|
|
+ if ((dev->pci_device & 0xffff) == 0x01a0 /*CHIPSET_NFORCE*/ ||
|
|
+ (dev->pci_device & 0xffff) == 0x01f0 /*CHIPSET_NFORCE2*/) {
|
|
+ uint32_t type;
|
|
+
|
|
+ pci_read_config_dword(pci_get_bus_and_slot(0, 1), 0x7c, &type);
|
|
+
|
|
+ sim_data.memory_type = (type >> 12) & 1;
|
|
+ sim_data.memory_width = 64;
|
|
+ sim_data.mem_latency = 3;
|
|
+ sim_data.mem_page_miss = 10;
|
|
+ } else {
|
|
+ sim_data.memory_type = nvReadFB(dev, NV_PFB_CFG0) & 0x1;
|
|
+ sim_data.memory_width = (nvReadEXTDEV(dev, NV_PEXTDEV_BOOT_0) & 0x10) ? 128 : 64;
|
|
+ sim_data.mem_latency = cfg1 & 0xf;
|
|
+ sim_data.mem_page_miss = ((cfg1 >> 4) & 0xf) + ((cfg1 >> 31) & 0x1);
|
|
+ }
|
|
+
|
|
+ if (dev_priv->card_type == NV_04)
|
|
+ nv04_calc_arb(&fifo_data, &sim_data);
|
|
+ else
|
|
+ nv10_calc_arb(&fifo_data, &sim_data);
|
|
+
|
|
+ *burst = ilog2(fifo_data.burst >> 4);
|
|
+ *lwm = fifo_data.lwm >> 3;
|
|
+}
|
|
+
|
|
+static void
|
|
+nv30_update_arb(int *burst, int *lwm)
|
|
+{
|
|
+ unsigned int fifo_size, burst_size, graphics_lwm;
|
|
+
|
|
+ fifo_size = 2048;
|
|
+ burst_size = 512;
|
|
+ graphics_lwm = fifo_size - burst_size;
|
|
+
|
|
+ *burst = ilog2(burst_size >> 5);
|
|
+ *lwm = graphics_lwm >> 3;
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_calc_arb(struct drm_device *dev, int vclk, int bpp, int *burst, int *lwm)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ if (dev_priv->card_type < NV_30)
|
|
+ nv04_update_arb(dev, vclk, bpp, burst, lwm);
|
|
+ else if ((dev->pci_device & 0xfff0) == 0x0240 /*CHIPSET_C51*/ ||
|
|
+ (dev->pci_device & 0xfff0) == 0x03d0 /*CHIPSET_C512*/) {
|
|
+ *burst = 128;
|
|
+ *lwm = 0x0480;
|
|
+ } else
|
|
+ nv30_update_arb(burst, lwm);
|
|
+}
|
|
+
|
|
+static int
|
|
+getMNP_single(struct drm_device *dev, struct pll_lims *pll_lim, int clk,
|
|
+ struct nouveau_pll_vals *bestpv)
|
|
+{
|
|
+ /* Find M, N and P for a single stage PLL
|
|
+ *
|
|
+ * Note that some bioses (NV3x) have lookup tables of precomputed MNP
|
|
+ * values, but we're too lazy to use those atm
|
|
+ *
|
|
+ * "clk" parameter in kHz
|
|
+ * returns calculated clock
|
|
+ */
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ int cv = dev_priv->vbios->chip_version;
|
|
+ int minvco = pll_lim->vco1.minfreq, maxvco = pll_lim->vco1.maxfreq;
|
|
+ int minM = pll_lim->vco1.min_m, maxM = pll_lim->vco1.max_m;
|
|
+ int minN = pll_lim->vco1.min_n, maxN = pll_lim->vco1.max_n;
|
|
+ int minU = pll_lim->vco1.min_inputfreq;
|
|
+ int maxU = pll_lim->vco1.max_inputfreq;
|
|
+ int minP = pll_lim->max_p ? pll_lim->min_p : 0;
|
|
+ int maxP = pll_lim->max_p ? pll_lim->max_p : pll_lim->max_usable_log2p;
|
|
+ int crystal = pll_lim->refclk;
|
|
+ int M, N, thisP, P;
|
|
+ int clkP, calcclk;
|
|
+ int delta, bestdelta = INT_MAX;
|
|
+ int bestclk = 0;
|
|
+
|
|
+ /* this division verified for nv20, nv18, nv28 (Haiku), and nv34 */
|
|
+ /* possibly correlated with introduction of 27MHz crystal */
|
|
+ if (dev_priv->card_type < NV_50) {
|
|
+ if (cv < 0x17 || cv == 0x1a || cv == 0x20) {
|
|
+ if (clk > 250000)
|
|
+ maxM = 6;
|
|
+ if (clk > 340000)
|
|
+ maxM = 2;
|
|
+ } else if (cv < 0x40) {
|
|
+ if (clk > 150000)
|
|
+ maxM = 6;
|
|
+ if (clk > 200000)
|
|
+ maxM = 4;
|
|
+ if (clk > 340000)
|
|
+ maxM = 2;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ P = pll_lim->max_p ? maxP : (1 << maxP);
|
|
+ if ((clk * P) < minvco) {
|
|
+ minvco = clk * maxP;
|
|
+ maxvco = minvco * 2;
|
|
+ }
|
|
+
|
|
+ if (clk + clk/200 > maxvco) /* +0.5% */
|
|
+ maxvco = clk + clk/200;
|
|
+
|
|
+ /* NV34 goes maxlog2P->0, NV20 goes 0->maxlog2P */
|
|
+ for (thisP = minP; thisP <= maxP; thisP++) {
|
|
+ P = pll_lim->max_p ? thisP : (1 << thisP);
|
|
+ clkP = clk * P;
|
|
+
|
|
+ if (clkP < minvco)
|
|
+ continue;
|
|
+ if (clkP > maxvco)
|
|
+ return bestclk;
|
|
+
|
|
+ for (M = minM; M <= maxM; M++) {
|
|
+ if (crystal/M < minU)
|
|
+ return bestclk;
|
|
+ if (crystal/M > maxU)
|
|
+ continue;
|
|
+
|
|
+ /* add crystal/2 to round better */
|
|
+ N = (clkP * M + crystal/2) / crystal;
|
|
+
|
|
+ if (N < minN)
|
|
+ continue;
|
|
+ if (N > maxN)
|
|
+ break;
|
|
+
|
|
+ /* more rounding additions */
|
|
+ calcclk = ((N * crystal + P/2) / P + M/2) / M;
|
|
+ delta = abs(calcclk - clk);
|
|
+ /* we do an exhaustive search rather than terminating
|
|
+ * on an optimality condition...
|
|
+ */
|
|
+ if (delta < bestdelta) {
|
|
+ bestdelta = delta;
|
|
+ bestclk = calcclk;
|
|
+ bestpv->N1 = N;
|
|
+ bestpv->M1 = M;
|
|
+ bestpv->log2P = thisP;
|
|
+ if (delta == 0) /* except this one */
|
|
+ return bestclk;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return bestclk;
|
|
+}
|
|
+
|
|
+static int
|
|
+getMNP_double(struct drm_device *dev, struct pll_lims *pll_lim, int clk,
|
|
+ struct nouveau_pll_vals *bestpv)
|
|
+{
|
|
+ /* Find M, N and P for a two stage PLL
|
|
+ *
|
|
+ * Note that some bioses (NV30+) have lookup tables of precomputed MNP
|
|
+ * values, but we're too lazy to use those atm
|
|
+ *
|
|
+ * "clk" parameter in kHz
|
|
+ * returns calculated clock
|
|
+ */
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ int chip_version = dev_priv->vbios->chip_version;
|
|
+ int minvco1 = pll_lim->vco1.minfreq, maxvco1 = pll_lim->vco1.maxfreq;
|
|
+ int minvco2 = pll_lim->vco2.minfreq, maxvco2 = pll_lim->vco2.maxfreq;
|
|
+ int minU1 = pll_lim->vco1.min_inputfreq, minU2 = pll_lim->vco2.min_inputfreq;
|
|
+ int maxU1 = pll_lim->vco1.max_inputfreq, maxU2 = pll_lim->vco2.max_inputfreq;
|
|
+ int minM1 = pll_lim->vco1.min_m, maxM1 = pll_lim->vco1.max_m;
|
|
+ int minN1 = pll_lim->vco1.min_n, maxN1 = pll_lim->vco1.max_n;
|
|
+ int minM2 = pll_lim->vco2.min_m, maxM2 = pll_lim->vco2.max_m;
|
|
+ int minN2 = pll_lim->vco2.min_n, maxN2 = pll_lim->vco2.max_n;
|
|
+ int maxlog2P = pll_lim->max_usable_log2p;
|
|
+ int crystal = pll_lim->refclk;
|
|
+ bool fixedgain2 = (minM2 == maxM2 && minN2 == maxN2);
|
|
+ int M1, N1, M2, N2, log2P;
|
|
+ int clkP, calcclk1, calcclk2, calcclkout;
|
|
+ int delta, bestdelta = INT_MAX;
|
|
+ int bestclk = 0;
|
|
+
|
|
+ int vco2 = (maxvco2 - maxvco2/200) / 2;
|
|
+ for (log2P = 0; clk && log2P < maxlog2P && clk <= (vco2 >> log2P); log2P++)
|
|
+ ;
|
|
+ clkP = clk << log2P;
|
|
+
|
|
+ if (maxvco2 < clk + clk/200) /* +0.5% */
|
|
+ maxvco2 = clk + clk/200;
|
|
+
|
|
+ for (M1 = minM1; M1 <= maxM1; M1++) {
|
|
+ if (crystal/M1 < minU1)
|
|
+ return bestclk;
|
|
+ if (crystal/M1 > maxU1)
|
|
+ continue;
|
|
+
|
|
+ for (N1 = minN1; N1 <= maxN1; N1++) {
|
|
+ calcclk1 = crystal * N1 / M1;
|
|
+ if (calcclk1 < minvco1)
|
|
+ continue;
|
|
+ if (calcclk1 > maxvco1)
|
|
+ break;
|
|
+
|
|
+ for (M2 = minM2; M2 <= maxM2; M2++) {
|
|
+ if (calcclk1/M2 < minU2)
|
|
+ break;
|
|
+ if (calcclk1/M2 > maxU2)
|
|
+ continue;
|
|
+
|
|
+ /* add calcclk1/2 to round better */
|
|
+ N2 = (clkP * M2 + calcclk1/2) / calcclk1;
|
|
+ if (N2 < minN2)
|
|
+ continue;
|
|
+ if (N2 > maxN2)
|
|
+ break;
|
|
+
|
|
+ if (!fixedgain2) {
|
|
+ if (chip_version < 0x60)
|
|
+ if (N2/M2 < 4 || N2/M2 > 10)
|
|
+ continue;
|
|
+
|
|
+ calcclk2 = calcclk1 * N2 / M2;
|
|
+ if (calcclk2 < minvco2)
|
|
+ break;
|
|
+ if (calcclk2 > maxvco2)
|
|
+ continue;
|
|
+ } else
|
|
+ calcclk2 = calcclk1;
|
|
+
|
|
+ calcclkout = calcclk2 >> log2P;
|
|
+ delta = abs(calcclkout - clk);
|
|
+ /* we do an exhaustive search rather than terminating
|
|
+ * on an optimality condition...
|
|
+ */
|
|
+ if (delta < bestdelta) {
|
|
+ bestdelta = delta;
|
|
+ bestclk = calcclkout;
|
|
+ bestpv->N1 = N1;
|
|
+ bestpv->M1 = M1;
|
|
+ bestpv->N2 = N2;
|
|
+ bestpv->M2 = M2;
|
|
+ bestpv->log2P = log2P;
|
|
+ if (delta == 0) /* except this one */
|
|
+ return bestclk;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return bestclk;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_calc_pll_mnp(struct drm_device *dev, struct pll_lims *pll_lim, int clk,
|
|
+ struct nouveau_pll_vals *pv)
|
|
+{
|
|
+ int outclk;
|
|
+
|
|
+ if (!pll_lim->vco2.maxfreq)
|
|
+ outclk = getMNP_single(dev, pll_lim, clk, pv);
|
|
+ else
|
|
+ outclk = getMNP_double(dev, pll_lim, clk, pv);
|
|
+
|
|
+ if (!outclk)
|
|
+ NV_ERROR(dev, "Could not find a compatible set of PLL values\n");
|
|
+
|
|
+ return outclk;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_channel.c b/drivers/gpu/drm/nouveau/nouveau_channel.c
|
|
new file mode 100644
|
|
index 0000000..2281f99
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_channel.c
|
|
@@ -0,0 +1,430 @@
|
|
+/*
|
|
+ * Copyright 2005-2006 Stephane Marchesin
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the next
|
|
+ * paragraph) shall be included in all copies or substantial portions of the
|
|
+ * Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
+ * DEALINGS IN THE SOFTWARE.
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_drm.h"
|
|
+#include "nouveau_dma.h"
|
|
+
|
|
+static int
|
|
+nouveau_channel_pushbuf_ctxdma_init(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_bo *pb = chan->pushbuf_bo;
|
|
+ struct nouveau_gpuobj *pushbuf = NULL;
|
|
+ uint32_t start = pb->bo.mem.mm_node->start << PAGE_SHIFT;
|
|
+ int ret;
|
|
+
|
|
+ if (pb->bo.mem.mem_type == TTM_PL_TT) {
|
|
+ ret = nouveau_gpuobj_gart_dma_new(chan, 0,
|
|
+ dev_priv->gart_info.aper_size,
|
|
+ NV_DMA_ACCESS_RO, &pushbuf,
|
|
+ NULL);
|
|
+ chan->pushbuf_base = start;
|
|
+ } else
|
|
+ if (dev_priv->card_type != NV_04) {
|
|
+ ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, 0,
|
|
+ dev_priv->fb_available_size,
|
|
+ NV_DMA_ACCESS_RO,
|
|
+ NV_DMA_TARGET_VIDMEM, &pushbuf);
|
|
+ chan->pushbuf_base = start;
|
|
+ } else {
|
|
+ /* NV04 cmdbuf hack, from original ddx.. not sure of it's
|
|
+ * exact reason for existing :) PCI access to cmdbuf in
|
|
+ * VRAM.
|
|
+ */
|
|
+ ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY,
|
|
+ drm_get_resource_start(dev, 1),
|
|
+ dev_priv->fb_available_size,
|
|
+ NV_DMA_ACCESS_RO,
|
|
+ NV_DMA_TARGET_PCI, &pushbuf);
|
|
+ chan->pushbuf_base = start;
|
|
+ }
|
|
+
|
|
+ ret = nouveau_gpuobj_ref_add(dev, chan, 0, pushbuf, &chan->pushbuf);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Error referencing pushbuf ctxdma: %d\n", ret);
|
|
+ if (pushbuf != dev_priv->gart_info.sg_ctxdma)
|
|
+ nouveau_gpuobj_del(dev, &pushbuf);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct nouveau_bo *
|
|
+nouveau_channel_user_pushbuf_alloc(struct drm_device *dev)
|
|
+{
|
|
+ struct nouveau_bo *pushbuf = NULL;
|
|
+ int location, ret;
|
|
+
|
|
+ if (nouveau_vram_pushbuf)
|
|
+ location = TTM_PL_FLAG_VRAM;
|
|
+ else
|
|
+ location = TTM_PL_FLAG_TT;
|
|
+
|
|
+ ret = nouveau_bo_new(dev, NULL, 65536, 0, location, 0, 0x0000, false,
|
|
+ true, &pushbuf);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "error allocating DMA push buffer: %d\n", ret);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ ret = nouveau_bo_pin(pushbuf, location);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "error pinning DMA push buffer: %d\n", ret);
|
|
+ nouveau_bo_ref(NULL, &pushbuf);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ return pushbuf;
|
|
+}
|
|
+
|
|
+/* allocates and initializes a fifo for user space consumption */
|
|
+int
|
|
+nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
|
|
+ struct drm_file *file_priv,
|
|
+ uint32_t vram_handle, uint32_t tt_handle)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
|
|
+ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
|
|
+ struct nouveau_channel *chan;
|
|
+ int channel, user;
|
|
+ int ret;
|
|
+
|
|
+ /*
|
|
+ * Alright, here is the full story
|
|
+ * Nvidia cards have multiple hw fifo contexts (praise them for that,
|
|
+ * no complicated crash-prone context switches)
|
|
+ * We allocate a new context for each app and let it write to it
|
|
+ * directly (woo, full userspace command submission !)
|
|
+ * When there are no more contexts, you lost
|
|
+ */
|
|
+ for (channel = 0; channel < pfifo->channels; channel++) {
|
|
+ if (dev_priv->fifos[channel] == NULL)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* no more fifos. you lost. */
|
|
+ if (channel == pfifo->channels)
|
|
+ return -EINVAL;
|
|
+
|
|
+ dev_priv->fifos[channel] = kzalloc(sizeof(struct nouveau_channel),
|
|
+ GFP_KERNEL);
|
|
+ if (!dev_priv->fifos[channel])
|
|
+ return -ENOMEM;
|
|
+ dev_priv->fifo_alloc_count++;
|
|
+ chan = dev_priv->fifos[channel];
|
|
+ INIT_LIST_HEAD(&chan->nvsw.vbl_wait);
|
|
+ INIT_LIST_HEAD(&chan->fence.pending);
|
|
+ chan->dev = dev;
|
|
+ chan->id = channel;
|
|
+ chan->file_priv = file_priv;
|
|
+ chan->vram_handle = vram_handle;
|
|
+ chan->gart_handle = tt_handle;
|
|
+
|
|
+ NV_INFO(dev, "Allocating FIFO number %d\n", channel);
|
|
+
|
|
+ /* Allocate DMA push buffer */
|
|
+ chan->pushbuf_bo = nouveau_channel_user_pushbuf_alloc(dev);
|
|
+ if (!chan->pushbuf_bo) {
|
|
+ ret = -ENOMEM;
|
|
+ NV_ERROR(dev, "pushbuf %d\n", ret);
|
|
+ nouveau_channel_free(chan);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ nouveau_dma_pre_init(chan);
|
|
+
|
|
+ /* Locate channel's user control regs */
|
|
+ if (dev_priv->card_type < NV_40)
|
|
+ user = NV03_USER(channel);
|
|
+ else
|
|
+ if (dev_priv->card_type < NV_50)
|
|
+ user = NV40_USER(channel);
|
|
+ else
|
|
+ user = NV50_USER(channel);
|
|
+
|
|
+ chan->user = ioremap(pci_resource_start(dev->pdev, 0) + user,
|
|
+ PAGE_SIZE);
|
|
+ if (!chan->user) {
|
|
+ NV_ERROR(dev, "ioremap of regs failed.\n");
|
|
+ nouveau_channel_free(chan);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ chan->user_put = 0x40;
|
|
+ chan->user_get = 0x44;
|
|
+
|
|
+ /* Allocate space for per-channel fixed notifier memory */
|
|
+ ret = nouveau_notifier_init_channel(chan);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "ntfy %d\n", ret);
|
|
+ nouveau_channel_free(chan);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /* Setup channel's default objects */
|
|
+ ret = nouveau_gpuobj_channel_init(chan, vram_handle, tt_handle);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "gpuobj %d\n", ret);
|
|
+ nouveau_channel_free(chan);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /* Create a dma object for the push buffer */
|
|
+ ret = nouveau_channel_pushbuf_ctxdma_init(chan);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "pbctxdma %d\n", ret);
|
|
+ nouveau_channel_free(chan);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /* disable the fifo caches */
|
|
+ pfifo->reassign(dev, false);
|
|
+
|
|
+ /* Create a graphics context for new channel */
|
|
+ ret = pgraph->create_context(chan);
|
|
+ if (ret) {
|
|
+ nouveau_channel_free(chan);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /* Construct inital RAMFC for new channel */
|
|
+ ret = pfifo->create_context(chan);
|
|
+ if (ret) {
|
|
+ nouveau_channel_free(chan);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ pfifo->reassign(dev, true);
|
|
+
|
|
+ ret = nouveau_dma_init(chan);
|
|
+ if (!ret)
|
|
+ ret = nouveau_fence_init(chan);
|
|
+ if (ret) {
|
|
+ nouveau_channel_free(chan);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ nouveau_debugfs_channel_init(chan);
|
|
+
|
|
+ NV_INFO(dev, "%s: initialised FIFO %d\n", __func__, channel);
|
|
+ *chan_ret = chan;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* stops a fifo */
|
|
+void
|
|
+nouveau_channel_free(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
|
|
+ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
|
|
+ unsigned long flags;
|
|
+ int ret;
|
|
+
|
|
+ NV_INFO(dev, "%s: freeing fifo %d\n", __func__, chan->id);
|
|
+
|
|
+ nouveau_debugfs_channel_fini(chan);
|
|
+
|
|
+ /* Give outstanding push buffers a chance to complete */
|
|
+ spin_lock_irqsave(&chan->fence.lock, flags);
|
|
+ nouveau_fence_update(chan);
|
|
+ spin_unlock_irqrestore(&chan->fence.lock, flags);
|
|
+ if (chan->fence.sequence != chan->fence.sequence_ack) {
|
|
+ struct nouveau_fence *fence = NULL;
|
|
+
|
|
+ ret = nouveau_fence_new(chan, &fence, true);
|
|
+ if (ret == 0) {
|
|
+ ret = nouveau_fence_wait(fence, NULL, false, false);
|
|
+ nouveau_fence_unref((void *)&fence);
|
|
+ }
|
|
+
|
|
+ if (ret)
|
|
+ NV_ERROR(dev, "Failed to idle channel %d.\n", chan->id);
|
|
+ }
|
|
+
|
|
+ /* Ensure all outstanding fences are signaled. They should be if the
|
|
+ * above attempts at idling were OK, but if we failed this'll tell TTM
|
|
+ * we're done with the buffers.
|
|
+ */
|
|
+ nouveau_fence_fini(chan);
|
|
+
|
|
+ /* Ensure the channel is no longer active on the GPU */
|
|
+ pfifo->reassign(dev, false);
|
|
+
|
|
+ pgraph->fifo_access(dev, false);
|
|
+ if (pgraph->channel(dev) == chan)
|
|
+ pgraph->unload_context(dev);
|
|
+ pgraph->destroy_context(chan);
|
|
+ pgraph->fifo_access(dev, true);
|
|
+
|
|
+ if (pfifo->channel_id(dev) == chan->id) {
|
|
+ pfifo->disable(dev);
|
|
+ pfifo->unload_context(dev);
|
|
+ pfifo->enable(dev);
|
|
+ }
|
|
+ pfifo->destroy_context(chan);
|
|
+
|
|
+ pfifo->reassign(dev, true);
|
|
+
|
|
+ /* Release the channel's resources */
|
|
+ nouveau_gpuobj_ref_del(dev, &chan->pushbuf);
|
|
+ if (chan->pushbuf_bo) {
|
|
+ nouveau_bo_unpin(chan->pushbuf_bo);
|
|
+ nouveau_bo_ref(NULL, &chan->pushbuf_bo);
|
|
+ }
|
|
+ nouveau_gpuobj_channel_takedown(chan);
|
|
+ nouveau_notifier_takedown_channel(chan);
|
|
+ if (chan->user)
|
|
+ iounmap(chan->user);
|
|
+
|
|
+ dev_priv->fifos[chan->id] = NULL;
|
|
+ dev_priv->fifo_alloc_count--;
|
|
+ kfree(chan);
|
|
+}
|
|
+
|
|
+/* cleans up all the fifos from file_priv */
|
|
+void
|
|
+nouveau_channel_cleanup(struct drm_device *dev, struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_engine *engine = &dev_priv->engine;
|
|
+ int i;
|
|
+
|
|
+ NV_DEBUG(dev, "clearing FIFO enables from file_priv\n");
|
|
+ for (i = 0; i < engine->fifo.channels; i++) {
|
|
+ struct nouveau_channel *chan = dev_priv->fifos[i];
|
|
+
|
|
+ if (chan && chan->file_priv == file_priv)
|
|
+ nouveau_channel_free(chan);
|
|
+ }
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_channel_owner(struct drm_device *dev, struct drm_file *file_priv,
|
|
+ int channel)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_engine *engine = &dev_priv->engine;
|
|
+
|
|
+ if (channel >= engine->fifo.channels)
|
|
+ return 0;
|
|
+ if (dev_priv->fifos[channel] == NULL)
|
|
+ return 0;
|
|
+
|
|
+ return (dev_priv->fifos[channel]->file_priv == file_priv);
|
|
+}
|
|
+
|
|
+/***********************************
|
|
+ * ioctls wrapping the functions
|
|
+ ***********************************/
|
|
+
|
|
+static int
|
|
+nouveau_ioctl_fifo_alloc(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct drm_nouveau_channel_alloc *init = data;
|
|
+ struct nouveau_channel *chan;
|
|
+ int ret;
|
|
+
|
|
+ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
|
|
+
|
|
+ if (dev_priv->engine.graph.accel_blocked)
|
|
+ return -ENODEV;
|
|
+
|
|
+ if (init->fb_ctxdma_handle == ~0 || init->tt_ctxdma_handle == ~0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ ret = nouveau_channel_alloc(dev, &chan, file_priv,
|
|
+ init->fb_ctxdma_handle,
|
|
+ init->tt_ctxdma_handle);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ init->channel = chan->id;
|
|
+
|
|
+ init->subchan[0].handle = NvM2MF;
|
|
+ if (dev_priv->card_type < NV_50)
|
|
+ init->subchan[0].grclass = 0x0039;
|
|
+ else
|
|
+ init->subchan[0].grclass = 0x5039;
|
|
+ init->subchan[1].handle = NvSw;
|
|
+ init->subchan[1].grclass = NV_SW;
|
|
+ init->nr_subchan = 2;
|
|
+
|
|
+ /* Named memory object area */
|
|
+ ret = drm_gem_handle_create(file_priv, chan->notifier_bo->gem,
|
|
+ &init->notifier_handle);
|
|
+ if (ret) {
|
|
+ nouveau_channel_free(chan);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_ioctl_fifo_free(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_nouveau_channel_free *cfree = data;
|
|
+ struct nouveau_channel *chan;
|
|
+
|
|
+ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
|
|
+ NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(cfree->channel, file_priv, chan);
|
|
+
|
|
+ nouveau_channel_free(chan);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/***********************************
|
|
+ * finally, the ioctl table
|
|
+ ***********************************/
|
|
+
|
|
+struct drm_ioctl_desc nouveau_ioctls[] = {
|
|
+ DRM_IOCTL_DEF(DRM_NOUVEAU_CARD_INIT, nouveau_ioctl_card_init, DRM_AUTH),
|
|
+ DRM_IOCTL_DEF(DRM_NOUVEAU_GETPARAM, nouveau_ioctl_getparam, DRM_AUTH),
|
|
+ DRM_IOCTL_DEF(DRM_NOUVEAU_SETPARAM, nouveau_ioctl_setparam, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
|
|
+ DRM_IOCTL_DEF(DRM_NOUVEAU_CHANNEL_ALLOC, nouveau_ioctl_fifo_alloc, DRM_AUTH),
|
|
+ DRM_IOCTL_DEF(DRM_NOUVEAU_CHANNEL_FREE, nouveau_ioctl_fifo_free, DRM_AUTH),
|
|
+ DRM_IOCTL_DEF(DRM_NOUVEAU_GROBJ_ALLOC, nouveau_ioctl_grobj_alloc, DRM_AUTH),
|
|
+ DRM_IOCTL_DEF(DRM_NOUVEAU_NOTIFIEROBJ_ALLOC, nouveau_ioctl_notifier_alloc, DRM_AUTH),
|
|
+ DRM_IOCTL_DEF(DRM_NOUVEAU_GPUOBJ_FREE, nouveau_ioctl_gpuobj_free, DRM_AUTH),
|
|
+ DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_NEW, nouveau_gem_ioctl_new, DRM_AUTH),
|
|
+ DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PUSHBUF, nouveau_gem_ioctl_pushbuf, DRM_AUTH),
|
|
+ DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PUSHBUF_CALL, nouveau_gem_ioctl_pushbuf_call, DRM_AUTH),
|
|
+ DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PIN, nouveau_gem_ioctl_pin, DRM_AUTH),
|
|
+ DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_UNPIN, nouveau_gem_ioctl_unpin, DRM_AUTH),
|
|
+ DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_CPU_PREP, nouveau_gem_ioctl_cpu_prep, DRM_AUTH),
|
|
+ DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_CPU_FINI, nouveau_gem_ioctl_cpu_fini, DRM_AUTH),
|
|
+ DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_INFO, nouveau_gem_ioctl_info, DRM_AUTH),
|
|
+ DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PUSHBUF_CALL2, nouveau_gem_ioctl_pushbuf_call2, DRM_AUTH),
|
|
+};
|
|
+
|
|
+int nouveau_max_ioctl = DRM_ARRAY_SIZE(nouveau_ioctls);
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
|
|
new file mode 100644
|
|
index 0000000..d2f6335
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
|
|
@@ -0,0 +1,846 @@
|
|
+/*
|
|
+ * Copyright (C) 2008 Maarten Maathuis.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include <acpi/button.h>
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm_edid.h"
|
|
+#include "drm_crtc_helper.h"
|
|
+
|
|
+#include "nouveau_reg.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_encoder.h"
|
|
+#include "nouveau_crtc.h"
|
|
+#include "nouveau_connector.h"
|
|
+#include "nouveau_hw.h"
|
|
+
|
|
+static inline struct drm_encoder_slave_funcs *
|
|
+get_slave_funcs(struct nouveau_encoder *enc)
|
|
+{
|
|
+ return to_encoder_slave(to_drm_encoder(enc))->slave_funcs;
|
|
+}
|
|
+
|
|
+static struct nouveau_encoder *
|
|
+find_encoder_by_type(struct drm_connector *connector, int type)
|
|
+{
|
|
+ struct drm_device *dev = connector->dev;
|
|
+ struct nouveau_encoder *nv_encoder;
|
|
+ struct drm_mode_object *obj;
|
|
+ int i, id;
|
|
+
|
|
+ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
|
+ id = connector->encoder_ids[i];
|
|
+ if (!id)
|
|
+ break;
|
|
+
|
|
+ obj = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER);
|
|
+ if (!obj)
|
|
+ continue;
|
|
+ nv_encoder = nouveau_encoder(obj_to_encoder(obj));
|
|
+
|
|
+ if (type == OUTPUT_ANY || nv_encoder->dcb->type == type)
|
|
+ return nv_encoder;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+struct nouveau_connector *
|
|
+nouveau_encoder_connector_get(struct nouveau_encoder *encoder)
|
|
+{
|
|
+ struct drm_device *dev = to_drm_encoder(encoder)->dev;
|
|
+ struct drm_connector *drm_connector;
|
|
+
|
|
+ list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) {
|
|
+ if (drm_connector->encoder == to_drm_encoder(encoder))
|
|
+ return nouveau_connector(drm_connector);
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+nouveau_connector_destroy(struct drm_connector *drm_connector)
|
|
+{
|
|
+ struct nouveau_connector *nv_connector =
|
|
+ nouveau_connector(drm_connector);
|
|
+ struct drm_device *dev;
|
|
+
|
|
+ if (!nv_connector)
|
|
+ return;
|
|
+
|
|
+ dev = nv_connector->base.dev;
|
|
+ NV_DEBUG_KMS(dev, "\n");
|
|
+
|
|
+ kfree(nv_connector->edid);
|
|
+ drm_sysfs_connector_remove(drm_connector);
|
|
+ drm_connector_cleanup(drm_connector);
|
|
+ kfree(drm_connector);
|
|
+}
|
|
+
|
|
+static void
|
|
+nouveau_connector_ddc_prepare(struct drm_connector *connector, int *flags)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
|
|
+
|
|
+ if (dev_priv->card_type >= NV_50)
|
|
+ return;
|
|
+
|
|
+ *flags = 0;
|
|
+ if (NVLockVgaCrtcs(dev_priv->dev, false))
|
|
+ *flags |= 1;
|
|
+ if (nv_heads_tied(dev_priv->dev))
|
|
+ *flags |= 2;
|
|
+
|
|
+ if (*flags & 2)
|
|
+ NVSetOwner(dev_priv->dev, 0); /* necessary? */
|
|
+}
|
|
+
|
|
+static void
|
|
+nouveau_connector_ddc_finish(struct drm_connector *connector, int flags)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
|
|
+
|
|
+ if (dev_priv->card_type >= NV_50)
|
|
+ return;
|
|
+
|
|
+ if (flags & 2)
|
|
+ NVSetOwner(dev_priv->dev, 4);
|
|
+ if (flags & 1)
|
|
+ NVLockVgaCrtcs(dev_priv->dev, true);
|
|
+}
|
|
+
|
|
+static struct nouveau_i2c_chan *
|
|
+nouveau_connector_ddc_detect(struct drm_connector *connector,
|
|
+ struct nouveau_encoder **pnv_encoder)
|
|
+{
|
|
+ struct drm_device *dev = connector->dev;
|
|
+ uint8_t out_buf[] = { 0x0, 0x0}, buf[2];
|
|
+ int ret, flags, i;
|
|
+
|
|
+ struct i2c_msg msgs[] = {
|
|
+ {
|
|
+ .addr = 0x50,
|
|
+ .flags = 0,
|
|
+ .len = 1,
|
|
+ .buf = out_buf,
|
|
+ },
|
|
+ {
|
|
+ .addr = 0x50,
|
|
+ .flags = I2C_M_RD,
|
|
+ .len = 1,
|
|
+ .buf = buf,
|
|
+ }
|
|
+ };
|
|
+
|
|
+ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
|
+ struct nouveau_i2c_chan *i2c = NULL;
|
|
+ struct nouveau_encoder *nv_encoder;
|
|
+ struct drm_mode_object *obj;
|
|
+ int id;
|
|
+
|
|
+ id = connector->encoder_ids[i];
|
|
+ if (!id)
|
|
+ break;
|
|
+
|
|
+ obj = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER);
|
|
+ if (!obj)
|
|
+ continue;
|
|
+ nv_encoder = nouveau_encoder(obj_to_encoder(obj));
|
|
+
|
|
+ if (nv_encoder->dcb->i2c_index < 0xf)
|
|
+ i2c = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
|
|
+ if (!i2c)
|
|
+ continue;
|
|
+
|
|
+ nouveau_connector_ddc_prepare(connector, &flags);
|
|
+ ret = i2c_transfer(&i2c->adapter, msgs, 2);
|
|
+ nouveau_connector_ddc_finish(connector, flags);
|
|
+
|
|
+ if (ret == 2) {
|
|
+ *pnv_encoder = nv_encoder;
|
|
+ return i2c;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static void
|
|
+nouveau_connector_set_encoder(struct drm_connector *connector,
|
|
+ struct nouveau_encoder *nv_encoder)
|
|
+{
|
|
+ struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
|
+ struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
|
|
+ struct drm_device *dev = connector->dev;
|
|
+
|
|
+ if (nv_connector->detected_encoder == nv_encoder)
|
|
+ return;
|
|
+ nv_connector->detected_encoder = nv_encoder;
|
|
+
|
|
+ if (nv_encoder->dcb->type == OUTPUT_LVDS ||
|
|
+ nv_encoder->dcb->type == OUTPUT_TMDS) {
|
|
+ connector->doublescan_allowed = false;
|
|
+ connector->interlace_allowed = false;
|
|
+ } else {
|
|
+ connector->doublescan_allowed = true;
|
|
+ if (dev_priv->card_type == NV_20 ||
|
|
+ (dev_priv->card_type == NV_10 &&
|
|
+ (dev->pci_device & 0x0ff0) != 0x0100 &&
|
|
+ (dev->pci_device & 0x0ff0) != 0x0150))
|
|
+ /* HW is broken */
|
|
+ connector->interlace_allowed = false;
|
|
+ else
|
|
+ connector->interlace_allowed = true;
|
|
+ }
|
|
+
|
|
+ if (connector->connector_type == DRM_MODE_CONNECTOR_DVII) {
|
|
+ drm_connector_property_set_value(connector,
|
|
+ dev->mode_config.dvi_i_subconnector_property,
|
|
+ nv_encoder->dcb->type == OUTPUT_TMDS ?
|
|
+ DRM_MODE_SUBCONNECTOR_DVID :
|
|
+ DRM_MODE_SUBCONNECTOR_DVIA);
|
|
+ }
|
|
+}
|
|
+
|
|
+static enum drm_connector_status
|
|
+nouveau_connector_detect(struct drm_connector *connector)
|
|
+{
|
|
+ struct drm_device *dev = connector->dev;
|
|
+ struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
|
+ struct nouveau_encoder *nv_encoder = NULL;
|
|
+ struct nouveau_i2c_chan *i2c;
|
|
+ int type, flags;
|
|
+
|
|
+ if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
|
|
+ nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS);
|
|
+ if (nv_encoder && nv_connector->native_mode) {
|
|
+#ifdef CONFIG_ACPI
|
|
+ if (!nouveau_ignorelid && !acpi_lid_open())
|
|
+ return connector_status_disconnected;
|
|
+#endif
|
|
+ nouveau_connector_set_encoder(connector, nv_encoder);
|
|
+ return connector_status_connected;
|
|
+ }
|
|
+
|
|
+ /* Cleanup the previous EDID block. */
|
|
+ if (nv_connector->edid) {
|
|
+ drm_mode_connector_update_edid_property(connector, NULL);
|
|
+ kfree(nv_connector->edid);
|
|
+ nv_connector->edid = NULL;
|
|
+ }
|
|
+
|
|
+ i2c = nouveau_connector_ddc_detect(connector, &nv_encoder);
|
|
+ if (i2c) {
|
|
+ nouveau_connector_ddc_prepare(connector, &flags);
|
|
+ nv_connector->edid = drm_get_edid(connector, &i2c->adapter);
|
|
+ nouveau_connector_ddc_finish(connector, flags);
|
|
+ drm_mode_connector_update_edid_property(connector,
|
|
+ nv_connector->edid);
|
|
+ if (!nv_connector->edid) {
|
|
+ NV_ERROR(dev, "DDC responded, but no EDID for %s\n",
|
|
+ drm_get_connector_name(connector));
|
|
+ goto detect_analog;
|
|
+ }
|
|
+
|
|
+ if (nv_encoder->dcb->type == OUTPUT_DP &&
|
|
+ !nouveau_dp_detect(to_drm_encoder(nv_encoder))) {
|
|
+ NV_ERROR(dev, "Detected %s, but failed init\n",
|
|
+ drm_get_connector_name(connector));
|
|
+ return connector_status_disconnected;
|
|
+ }
|
|
+
|
|
+ /* Override encoder type for DVI-I based on whether EDID
|
|
+ * says the display is digital or analog, both use the
|
|
+ * same i2c channel so the value returned from ddc_detect
|
|
+ * isn't necessarily correct.
|
|
+ */
|
|
+ if (connector->connector_type == DRM_MODE_CONNECTOR_DVII) {
|
|
+ if (nv_connector->edid->input & DRM_EDID_INPUT_DIGITAL)
|
|
+ type = OUTPUT_TMDS;
|
|
+ else
|
|
+ type = OUTPUT_ANALOG;
|
|
+
|
|
+ nv_encoder = find_encoder_by_type(connector, type);
|
|
+ if (!nv_encoder) {
|
|
+ NV_ERROR(dev, "Detected %d encoder on %s, "
|
|
+ "but no object!\n", type,
|
|
+ drm_get_connector_name(connector));
|
|
+ return connector_status_disconnected;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ nouveau_connector_set_encoder(connector, nv_encoder);
|
|
+ return connector_status_connected;
|
|
+ }
|
|
+
|
|
+detect_analog:
|
|
+ nv_encoder = find_encoder_by_type(connector, OUTPUT_ANALOG);
|
|
+ if (!nv_encoder)
|
|
+ nv_encoder = find_encoder_by_type(connector, OUTPUT_TV);
|
|
+ if (nv_encoder) {
|
|
+ struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
|
|
+ struct drm_encoder_helper_funcs *helper =
|
|
+ encoder->helper_private;
|
|
+
|
|
+ if (helper->detect(encoder, connector) ==
|
|
+ connector_status_connected) {
|
|
+ nouveau_connector_set_encoder(connector, nv_encoder);
|
|
+ return connector_status_connected;
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ return connector_status_disconnected;
|
|
+}
|
|
+
|
|
+static void
|
|
+nouveau_connector_force(struct drm_connector *connector)
|
|
+{
|
|
+ struct drm_device *dev = connector->dev;
|
|
+ struct nouveau_encoder *nv_encoder;
|
|
+ int type;
|
|
+
|
|
+ if (connector->connector_type == DRM_MODE_CONNECTOR_DVII) {
|
|
+ if (connector->force == DRM_FORCE_ON_DIGITAL)
|
|
+ type = OUTPUT_TMDS;
|
|
+ else
|
|
+ type = OUTPUT_ANALOG;
|
|
+ } else
|
|
+ type = OUTPUT_ANY;
|
|
+
|
|
+ nv_encoder = find_encoder_by_type(connector, type);
|
|
+ if (!nv_encoder) {
|
|
+ NV_ERROR(dev, "can't find encoder to force %s on!\n",
|
|
+ drm_get_connector_name(connector));
|
|
+ connector->status = connector_status_disconnected;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ nouveau_connector_set_encoder(connector, nv_encoder);
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_connector_set_property(struct drm_connector *connector,
|
|
+ struct drm_property *property, uint64_t value)
|
|
+{
|
|
+ struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
|
+ struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
|
|
+ struct drm_device *dev = connector->dev;
|
|
+ int ret;
|
|
+
|
|
+ /* Scaling mode */
|
|
+ if (property == dev->mode_config.scaling_mode_property) {
|
|
+ struct nouveau_crtc *nv_crtc = NULL;
|
|
+ bool modeset = false;
|
|
+
|
|
+ switch (value) {
|
|
+ case DRM_MODE_SCALE_NONE:
|
|
+ case DRM_MODE_SCALE_FULLSCREEN:
|
|
+ case DRM_MODE_SCALE_CENTER:
|
|
+ case DRM_MODE_SCALE_ASPECT:
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* LVDS always needs gpu scaling */
|
|
+ if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS &&
|
|
+ value == DRM_MODE_SCALE_NONE)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* Changing between GPU and panel scaling requires a full
|
|
+ * modeset
|
|
+ */
|
|
+ if ((nv_connector->scaling_mode == DRM_MODE_SCALE_NONE) ||
|
|
+ (value == DRM_MODE_SCALE_NONE))
|
|
+ modeset = true;
|
|
+ nv_connector->scaling_mode = value;
|
|
+
|
|
+ if (connector->encoder && connector->encoder->crtc)
|
|
+ nv_crtc = nouveau_crtc(connector->encoder->crtc);
|
|
+ if (!nv_crtc)
|
|
+ return 0;
|
|
+
|
|
+ if (modeset || !nv_crtc->set_scale) {
|
|
+ ret = drm_crtc_helper_set_mode(&nv_crtc->base,
|
|
+ &nv_crtc->base.mode,
|
|
+ nv_crtc->base.x,
|
|
+ nv_crtc->base.y, NULL);
|
|
+ if (!ret)
|
|
+ return -EINVAL;
|
|
+ } else {
|
|
+ ret = nv_crtc->set_scale(nv_crtc, value, true);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* Dithering */
|
|
+ if (property == dev->mode_config.dithering_mode_property) {
|
|
+ struct nouveau_crtc *nv_crtc = NULL;
|
|
+
|
|
+ if (value == DRM_MODE_DITHERING_ON)
|
|
+ nv_connector->use_dithering = true;
|
|
+ else
|
|
+ nv_connector->use_dithering = false;
|
|
+
|
|
+ if (connector->encoder && connector->encoder->crtc)
|
|
+ nv_crtc = nouveau_crtc(connector->encoder->crtc);
|
|
+
|
|
+ if (!nv_crtc || !nv_crtc->set_dither)
|
|
+ return 0;
|
|
+
|
|
+ return nv_crtc->set_dither(nv_crtc, nv_connector->use_dithering,
|
|
+ true);
|
|
+ }
|
|
+
|
|
+ if (nv_encoder && nv_encoder->dcb->type == OUTPUT_TV)
|
|
+ return get_slave_funcs(nv_encoder)->
|
|
+ set_property(to_drm_encoder(nv_encoder), connector, property, value);
|
|
+
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static struct drm_display_mode *
|
|
+nouveau_connector_native_mode(struct nouveau_connector *connector)
|
|
+{
|
|
+ struct drm_device *dev = connector->base.dev;
|
|
+ struct drm_display_mode *mode, *largest = NULL;
|
|
+ int high_w = 0, high_h = 0, high_v = 0;
|
|
+
|
|
+ /* Use preferred mode if there is one.. */
|
|
+ list_for_each_entry(mode, &connector->base.probed_modes, head) {
|
|
+ if (mode->type & DRM_MODE_TYPE_PREFERRED) {
|
|
+ NV_DEBUG_KMS(dev, "native mode from preferred\n");
|
|
+ return drm_mode_duplicate(dev, mode);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Otherwise, take the resolution with the largest width, then height,
|
|
+ * then vertical refresh
|
|
+ */
|
|
+ list_for_each_entry(mode, &connector->base.probed_modes, head) {
|
|
+ if (mode->hdisplay < high_w)
|
|
+ continue;
|
|
+
|
|
+ if (mode->hdisplay == high_w && mode->vdisplay < high_h)
|
|
+ continue;
|
|
+
|
|
+ if (mode->hdisplay == high_w && mode->vdisplay == high_h &&
|
|
+ mode->vrefresh < high_v)
|
|
+ continue;
|
|
+
|
|
+ high_w = mode->hdisplay;
|
|
+ high_h = mode->vdisplay;
|
|
+ high_v = mode->vrefresh;
|
|
+ largest = mode;
|
|
+ }
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "native mode from largest: %dx%d@%d\n",
|
|
+ high_w, high_h, high_v);
|
|
+ return largest ? drm_mode_duplicate(dev, largest) : NULL;
|
|
+}
|
|
+
|
|
+struct moderec {
|
|
+ int hdisplay;
|
|
+ int vdisplay;
|
|
+};
|
|
+
|
|
+static struct moderec scaler_modes[] = {
|
|
+ { 1920, 1200 },
|
|
+ { 1920, 1080 },
|
|
+ { 1680, 1050 },
|
|
+ { 1600, 1200 },
|
|
+ { 1400, 1050 },
|
|
+ { 1280, 1024 },
|
|
+ { 1280, 960 },
|
|
+ { 1152, 864 },
|
|
+ { 1024, 768 },
|
|
+ { 800, 600 },
|
|
+ { 720, 400 },
|
|
+ { 640, 480 },
|
|
+ { 640, 400 },
|
|
+ { 640, 350 },
|
|
+ {}
|
|
+};
|
|
+
|
|
+static int
|
|
+nouveau_connector_scaler_modes_add(struct drm_connector *connector)
|
|
+{
|
|
+ struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
|
+ struct drm_display_mode *native = nv_connector->native_mode, *m;
|
|
+ struct drm_device *dev = connector->dev;
|
|
+ struct moderec *mode = &scaler_modes[0];
|
|
+ int modes = 0;
|
|
+
|
|
+ if (!native)
|
|
+ return 0;
|
|
+
|
|
+ while (mode->hdisplay) {
|
|
+ if (mode->hdisplay <= native->hdisplay &&
|
|
+ mode->vdisplay <= native->vdisplay) {
|
|
+ m = drm_cvt_mode(dev, mode->hdisplay, mode->vdisplay,
|
|
+ drm_mode_vrefresh(native), false,
|
|
+ false, false);
|
|
+ if (!m)
|
|
+ continue;
|
|
+
|
|
+ m->type |= DRM_MODE_TYPE_DRIVER;
|
|
+
|
|
+ drm_mode_probed_add(connector, m);
|
|
+ modes++;
|
|
+ }
|
|
+
|
|
+ mode++;
|
|
+ }
|
|
+
|
|
+ return modes;
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_connector_get_modes(struct drm_connector *connector)
|
|
+{
|
|
+ struct drm_device *dev = connector->dev;
|
|
+ struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
|
+ struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
|
|
+ int ret = 0;
|
|
+
|
|
+ /* If we're not LVDS, destroy the previous native mode, the attached
|
|
+ * monitor could have changed.
|
|
+ */
|
|
+ if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS &&
|
|
+ nv_connector->native_mode) {
|
|
+ drm_mode_destroy(dev, nv_connector->native_mode);
|
|
+ nv_connector->native_mode = NULL;
|
|
+ }
|
|
+
|
|
+ if (nv_connector->edid)
|
|
+ ret = drm_add_edid_modes(connector, nv_connector->edid);
|
|
+
|
|
+ /* Find the native mode if this is a digital panel, if we didn't
|
|
+ * find any modes through DDC previously add the native mode to
|
|
+ * the list of modes.
|
|
+ */
|
|
+ if (!nv_connector->native_mode)
|
|
+ nv_connector->native_mode =
|
|
+ nouveau_connector_native_mode(nv_connector);
|
|
+ if (ret == 0 && nv_connector->native_mode) {
|
|
+ struct drm_display_mode *mode;
|
|
+
|
|
+ mode = drm_mode_duplicate(dev, nv_connector->native_mode);
|
|
+ drm_mode_probed_add(connector, mode);
|
|
+ ret = 1;
|
|
+ }
|
|
+
|
|
+ if (nv_encoder->dcb->type == OUTPUT_TV)
|
|
+ ret = get_slave_funcs(nv_encoder)->
|
|
+ get_modes(to_drm_encoder(nv_encoder), connector);
|
|
+
|
|
+ if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
|
|
+ ret += nouveau_connector_scaler_modes_add(connector);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_connector_mode_valid(struct drm_connector *connector,
|
|
+ struct drm_display_mode *mode)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
|
|
+ struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
|
+ struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
|
|
+ unsigned min_clock = 25000, max_clock = min_clock;
|
|
+ unsigned clock = mode->clock;
|
|
+
|
|
+ switch (nv_encoder->dcb->type) {
|
|
+ case OUTPUT_LVDS:
|
|
+ BUG_ON(!nv_connector->native_mode);
|
|
+ if (mode->hdisplay > nv_connector->native_mode->hdisplay ||
|
|
+ mode->vdisplay > nv_connector->native_mode->vdisplay)
|
|
+ return MODE_PANEL;
|
|
+
|
|
+ min_clock = 0;
|
|
+ max_clock = 400000;
|
|
+ break;
|
|
+ case OUTPUT_TMDS:
|
|
+ if ((dev_priv->card_type >= NV_50 && !nouveau_duallink) ||
|
|
+ (dev_priv->card_type < NV_50 &&
|
|
+ !nv_encoder->dcb->duallink_possible))
|
|
+ max_clock = 165000;
|
|
+ else
|
|
+ max_clock = 330000;
|
|
+ break;
|
|
+ case OUTPUT_ANALOG:
|
|
+ max_clock = nv_encoder->dcb->crtconf.maxfreq;
|
|
+ if (!max_clock)
|
|
+ max_clock = 350000;
|
|
+ break;
|
|
+ case OUTPUT_TV:
|
|
+ return get_slave_funcs(nv_encoder)->
|
|
+ mode_valid(to_drm_encoder(nv_encoder), mode);
|
|
+ case OUTPUT_DP:
|
|
+ if (nv_encoder->dp.link_bw == DP_LINK_BW_2_7)
|
|
+ max_clock = nv_encoder->dp.link_nr * 270000;
|
|
+ else
|
|
+ max_clock = nv_encoder->dp.link_nr * 162000;
|
|
+
|
|
+ clock *= 3;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (clock < min_clock)
|
|
+ return MODE_CLOCK_LOW;
|
|
+
|
|
+ if (clock > max_clock)
|
|
+ return MODE_CLOCK_HIGH;
|
|
+
|
|
+ return MODE_OK;
|
|
+}
|
|
+
|
|
+static struct drm_encoder *
|
|
+nouveau_connector_best_encoder(struct drm_connector *connector)
|
|
+{
|
|
+ struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
|
+
|
|
+ if (nv_connector->detected_encoder)
|
|
+ return to_drm_encoder(nv_connector->detected_encoder);
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static const struct drm_connector_helper_funcs
|
|
+nouveau_connector_helper_funcs = {
|
|
+ .get_modes = nouveau_connector_get_modes,
|
|
+ .mode_valid = nouveau_connector_mode_valid,
|
|
+ .best_encoder = nouveau_connector_best_encoder,
|
|
+};
|
|
+
|
|
+static const struct drm_connector_funcs
|
|
+nouveau_connector_funcs = {
|
|
+ .dpms = drm_helper_connector_dpms,
|
|
+ .save = NULL,
|
|
+ .restore = NULL,
|
|
+ .detect = nouveau_connector_detect,
|
|
+ .destroy = nouveau_connector_destroy,
|
|
+ .fill_modes = drm_helper_probe_single_connector_modes,
|
|
+ .set_property = nouveau_connector_set_property,
|
|
+ .force = nouveau_connector_force
|
|
+};
|
|
+
|
|
+static int
|
|
+nouveau_connector_create_lvds(struct drm_device *dev,
|
|
+ struct drm_connector *connector)
|
|
+{
|
|
+ struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_i2c_chan *i2c = NULL;
|
|
+ struct nouveau_encoder *nv_encoder;
|
|
+ struct drm_display_mode native, *mode, *temp;
|
|
+ bool dummy, if_is_24bit = false;
|
|
+ int ret, flags;
|
|
+
|
|
+ nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS);
|
|
+ if (!nv_encoder)
|
|
+ return -ENODEV;
|
|
+
|
|
+ ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &if_is_24bit);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Error parsing LVDS table, disabling LVDS\n");
|
|
+ return ret;
|
|
+ }
|
|
+ nv_connector->use_dithering = !if_is_24bit;
|
|
+
|
|
+ /* Firstly try getting EDID over DDC, if allowed and I2C channel
|
|
+ * is available.
|
|
+ */
|
|
+ if (!dev_priv->VBIOS.pub.fp_no_ddc && nv_encoder->dcb->i2c_index < 0xf)
|
|
+ i2c = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
|
|
+
|
|
+ if (i2c) {
|
|
+ nouveau_connector_ddc_prepare(connector, &flags);
|
|
+ nv_connector->edid = drm_get_edid(connector, &i2c->adapter);
|
|
+ nouveau_connector_ddc_finish(connector, flags);
|
|
+ }
|
|
+
|
|
+ /* If no EDID found above, and the VBIOS indicates a hardcoded
|
|
+ * modeline is avalilable for the panel, set it as the panel's
|
|
+ * native mode and exit.
|
|
+ */
|
|
+ if (!nv_connector->edid && nouveau_bios_fp_mode(dev, &native) &&
|
|
+ (nv_encoder->dcb->lvdsconf.use_straps_for_mode ||
|
|
+ dev_priv->VBIOS.pub.fp_no_ddc)) {
|
|
+ nv_connector->native_mode = drm_mode_duplicate(dev, &native);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /* Still nothing, some VBIOS images have a hardcoded EDID block
|
|
+ * stored for the panel stored in them.
|
|
+ */
|
|
+ if (!nv_connector->edid && !nv_connector->native_mode &&
|
|
+ !dev_priv->VBIOS.pub.fp_no_ddc) {
|
|
+ struct edid *edid =
|
|
+ (struct edid *)nouveau_bios_embedded_edid(dev);
|
|
+ if (edid) {
|
|
+ nv_connector->edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
|
|
+ *(nv_connector->edid) = *edid;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!nv_connector->edid)
|
|
+ goto out;
|
|
+
|
|
+ /* We didn't find/use a panel mode from the VBIOS, so parse the EDID
|
|
+ * block and look for the preferred mode there.
|
|
+ */
|
|
+ ret = drm_add_edid_modes(connector, nv_connector->edid);
|
|
+ if (ret == 0)
|
|
+ goto out;
|
|
+ nv_connector->detected_encoder = nv_encoder;
|
|
+ nv_connector->native_mode = nouveau_connector_native_mode(nv_connector);
|
|
+ list_for_each_entry_safe(mode, temp, &connector->probed_modes, head)
|
|
+ drm_mode_remove(connector, mode);
|
|
+
|
|
+out:
|
|
+ if (!nv_connector->native_mode) {
|
|
+ NV_ERROR(dev, "LVDS present in DCB table, but couldn't "
|
|
+ "determine its native mode. Disabling.\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ drm_mode_connector_update_edid_property(connector, nv_connector->edid);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_connector_create(struct drm_device *dev, int index, int type)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_connector *nv_connector = NULL;
|
|
+ struct drm_connector *connector;
|
|
+ struct drm_encoder *encoder;
|
|
+ int ret;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "\n");
|
|
+
|
|
+ nv_connector = kzalloc(sizeof(*nv_connector), GFP_KERNEL);
|
|
+ if (!nv_connector)
|
|
+ return -ENOMEM;
|
|
+ nv_connector->dcb = nouveau_bios_connector_entry(dev, index);
|
|
+ connector = &nv_connector->base;
|
|
+
|
|
+ switch (type) {
|
|
+ case DRM_MODE_CONNECTOR_VGA:
|
|
+ NV_INFO(dev, "Detected a VGA connector\n");
|
|
+ break;
|
|
+ case DRM_MODE_CONNECTOR_DVID:
|
|
+ NV_INFO(dev, "Detected a DVI-D connector\n");
|
|
+ break;
|
|
+ case DRM_MODE_CONNECTOR_DVII:
|
|
+ NV_INFO(dev, "Detected a DVI-I connector\n");
|
|
+ break;
|
|
+ case DRM_MODE_CONNECTOR_LVDS:
|
|
+ NV_INFO(dev, "Detected a LVDS connector\n");
|
|
+ break;
|
|
+ case DRM_MODE_CONNECTOR_TV:
|
|
+ NV_INFO(dev, "Detected a TV connector\n");
|
|
+ break;
|
|
+ case DRM_MODE_CONNECTOR_DisplayPort:
|
|
+ NV_INFO(dev, "Detected a DisplayPort connector\n");
|
|
+ break;
|
|
+ default:
|
|
+ NV_ERROR(dev, "Unknown connector, this is not good.\n");
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* defaults, will get overridden in detect() */
|
|
+ connector->interlace_allowed = false;
|
|
+ connector->doublescan_allowed = false;
|
|
+
|
|
+ drm_connector_init(dev, connector, &nouveau_connector_funcs, type);
|
|
+ drm_connector_helper_add(connector, &nouveau_connector_helper_funcs);
|
|
+
|
|
+ /* Init DVI-I specific properties */
|
|
+ if (type == DRM_MODE_CONNECTOR_DVII) {
|
|
+ drm_mode_create_dvi_i_properties(dev);
|
|
+ drm_connector_attach_property(connector, dev->mode_config.dvi_i_subconnector_property, 0);
|
|
+ drm_connector_attach_property(connector, dev->mode_config.dvi_i_select_subconnector_property, 0);
|
|
+ }
|
|
+
|
|
+ if (type != DRM_MODE_CONNECTOR_LVDS)
|
|
+ nv_connector->use_dithering = false;
|
|
+
|
|
+ if (type == DRM_MODE_CONNECTOR_DVID ||
|
|
+ type == DRM_MODE_CONNECTOR_DVII ||
|
|
+ type == DRM_MODE_CONNECTOR_LVDS ||
|
|
+ type == DRM_MODE_CONNECTOR_DisplayPort) {
|
|
+ nv_connector->scaling_mode = DRM_MODE_SCALE_FULLSCREEN;
|
|
+
|
|
+ drm_connector_attach_property(connector, dev->mode_config.scaling_mode_property,
|
|
+ nv_connector->scaling_mode);
|
|
+ drm_connector_attach_property(connector, dev->mode_config.dithering_mode_property,
|
|
+ nv_connector->use_dithering ? DRM_MODE_DITHERING_ON
|
|
+ : DRM_MODE_DITHERING_OFF);
|
|
+
|
|
+ } else {
|
|
+ nv_connector->scaling_mode = DRM_MODE_SCALE_NONE;
|
|
+
|
|
+ if (type == DRM_MODE_CONNECTOR_VGA &&
|
|
+ dev_priv->card_type >= NV_50) {
|
|
+ drm_connector_attach_property(connector,
|
|
+ dev->mode_config.scaling_mode_property,
|
|
+ nv_connector->scaling_mode);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* attach encoders */
|
|
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+
|
|
+ if (nv_encoder->dcb->connector != index)
|
|
+ continue;
|
|
+
|
|
+ if (get_slave_funcs(nv_encoder))
|
|
+ get_slave_funcs(nv_encoder)->create_resources(encoder, connector);
|
|
+
|
|
+ drm_mode_connector_attach_encoder(connector, encoder);
|
|
+ }
|
|
+
|
|
+ drm_sysfs_connector_add(connector);
|
|
+
|
|
+ if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
|
|
+ ret = nouveau_connector_create_lvds(dev, connector);
|
|
+ if (ret) {
|
|
+ connector->funcs->destroy(connector);
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h
|
|
new file mode 100644
|
|
index 0000000..728b809
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.h
|
|
@@ -0,0 +1,54 @@
|
|
+/*
|
|
+ * Copyright (C) 2008 Maarten Maathuis.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef __NOUVEAU_CONNECTOR_H__
|
|
+#define __NOUVEAU_CONNECTOR_H__
|
|
+
|
|
+#include "drm_edid.h"
|
|
+#include "nouveau_i2c.h"
|
|
+
|
|
+struct nouveau_connector {
|
|
+ struct drm_connector base;
|
|
+
|
|
+ struct dcb_connector_table_entry *dcb;
|
|
+
|
|
+ int scaling_mode;
|
|
+ bool use_dithering;
|
|
+
|
|
+ struct nouveau_encoder *detected_encoder;
|
|
+ struct edid *edid;
|
|
+ struct drm_display_mode *native_mode;
|
|
+};
|
|
+
|
|
+static inline struct nouveau_connector *nouveau_connector(
|
|
+ struct drm_connector *con)
|
|
+{
|
|
+ return container_of(con, struct nouveau_connector, base);
|
|
+}
|
|
+
|
|
+int nouveau_connector_create(struct drm_device *dev, int i2c_index, int type);
|
|
+
|
|
+#endif /* __NOUVEAU_CONNECTOR_H__ */
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_crtc.h b/drivers/gpu/drm/nouveau/nouveau_crtc.h
|
|
new file mode 100644
|
|
index 0000000..49fa7b2
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_crtc.h
|
|
@@ -0,0 +1,95 @@
|
|
+/*
|
|
+ * Copyright (C) 2008 Maarten Maathuis.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef __NOUVEAU_CRTC_H__
|
|
+#define __NOUVEAU_CRTC_H__
|
|
+
|
|
+struct nouveau_crtc {
|
|
+ struct drm_crtc base;
|
|
+
|
|
+ int index;
|
|
+
|
|
+ struct drm_display_mode *mode;
|
|
+
|
|
+ uint32_t dpms_saved_fp_control;
|
|
+ uint32_t fp_users;
|
|
+ int saturation;
|
|
+ int sharpness;
|
|
+ int last_dpms;
|
|
+
|
|
+ struct {
|
|
+ int cpp;
|
|
+ bool blanked;
|
|
+ uint32_t offset;
|
|
+ uint32_t tile_flags;
|
|
+ } fb;
|
|
+
|
|
+ struct {
|
|
+ struct nouveau_bo *nvbo;
|
|
+ bool visible;
|
|
+ uint32_t offset;
|
|
+ void (*set_offset)(struct nouveau_crtc *, uint32_t offset);
|
|
+ void (*set_pos)(struct nouveau_crtc *, int x, int y);
|
|
+ void (*hide)(struct nouveau_crtc *, bool update);
|
|
+ void (*show)(struct nouveau_crtc *, bool update);
|
|
+ } cursor;
|
|
+
|
|
+ struct {
|
|
+ struct nouveau_bo *nvbo;
|
|
+ uint16_t r[256];
|
|
+ uint16_t g[256];
|
|
+ uint16_t b[256];
|
|
+ int depth;
|
|
+ } lut;
|
|
+
|
|
+ int (*set_dither)(struct nouveau_crtc *crtc, bool on, bool update);
|
|
+ int (*set_scale)(struct nouveau_crtc *crtc, int mode, bool update);
|
|
+};
|
|
+
|
|
+static inline struct nouveau_crtc *nouveau_crtc(struct drm_crtc *crtc)
|
|
+{
|
|
+ return container_of(crtc, struct nouveau_crtc, base);
|
|
+}
|
|
+
|
|
+static inline struct drm_crtc *to_drm_crtc(struct nouveau_crtc *crtc)
|
|
+{
|
|
+ return &crtc->base;
|
|
+}
|
|
+
|
|
+int nv50_crtc_create(struct drm_device *dev, int index);
|
|
+int nv50_cursor_init(struct nouveau_crtc *);
|
|
+void nv50_cursor_fini(struct nouveau_crtc *);
|
|
+int nv50_crtc_cursor_set(struct drm_crtc *drm_crtc, struct drm_file *file_priv,
|
|
+ uint32_t buffer_handle, uint32_t width,
|
|
+ uint32_t height);
|
|
+int nv50_crtc_cursor_move(struct drm_crtc *drm_crtc, int x, int y);
|
|
+
|
|
+int nv04_cursor_init(struct nouveau_crtc *);
|
|
+
|
|
+struct nouveau_connector *
|
|
+nouveau_crtc_connector_get(struct nouveau_crtc *crtc);
|
|
+
|
|
+#endif /* __NOUVEAU_CRTC_H__ */
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_debugfs.c b/drivers/gpu/drm/nouveau/nouveau_debugfs.c
|
|
new file mode 100644
|
|
index 0000000..d79db36
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_debugfs.c
|
|
@@ -0,0 +1,155 @@
|
|
+/*
|
|
+ * Copyright (C) 2009 Red Hat <bskeggs@redhat.com>
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * Authors:
|
|
+ * Ben Skeggs <bskeggs@redhat.com>
|
|
+ */
|
|
+
|
|
+#include <linux/debugfs.h>
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "nouveau_drv.h"
|
|
+
|
|
+static int
|
|
+nouveau_debugfs_channel_info(struct seq_file *m, void *data)
|
|
+{
|
|
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
|
|
+ struct nouveau_channel *chan = node->info_ent->data;
|
|
+
|
|
+ seq_printf(m, "channel id : %d\n", chan->id);
|
|
+
|
|
+ seq_printf(m, "cpu fifo state:\n");
|
|
+ seq_printf(m, " base: 0x%08x\n", chan->pushbuf_base);
|
|
+ seq_printf(m, " max: 0x%08x\n", chan->dma.max << 2);
|
|
+ seq_printf(m, " cur: 0x%08x\n", chan->dma.cur << 2);
|
|
+ seq_printf(m, " put: 0x%08x\n", chan->dma.put << 2);
|
|
+ seq_printf(m, " free: 0x%08x\n", chan->dma.free << 2);
|
|
+
|
|
+ seq_printf(m, "gpu fifo state:\n");
|
|
+ seq_printf(m, " get: 0x%08x\n",
|
|
+ nvchan_rd32(chan, chan->user_get));
|
|
+ seq_printf(m, " put: 0x%08x\n",
|
|
+ nvchan_rd32(chan, chan->user_put));
|
|
+
|
|
+ seq_printf(m, "last fence : %d\n", chan->fence.sequence);
|
|
+ seq_printf(m, "last signalled: %d\n", chan->fence.sequence_ack);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_debugfs_channel_init(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
|
|
+ struct drm_minor *minor = chan->dev->primary;
|
|
+ int ret;
|
|
+
|
|
+ if (!dev_priv->debugfs.channel_root) {
|
|
+ dev_priv->debugfs.channel_root =
|
|
+ debugfs_create_dir("channel", minor->debugfs_root);
|
|
+ if (!dev_priv->debugfs.channel_root)
|
|
+ return -ENOENT;
|
|
+ }
|
|
+
|
|
+ snprintf(chan->debugfs.name, 32, "%d", chan->id);
|
|
+ chan->debugfs.info.name = chan->debugfs.name;
|
|
+ chan->debugfs.info.show = nouveau_debugfs_channel_info;
|
|
+ chan->debugfs.info.driver_features = 0;
|
|
+ chan->debugfs.info.data = chan;
|
|
+
|
|
+ ret = drm_debugfs_create_files(&chan->debugfs.info, 1,
|
|
+ dev_priv->debugfs.channel_root,
|
|
+ chan->dev->primary);
|
|
+ if (ret == 0)
|
|
+ chan->debugfs.active = true;
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_debugfs_channel_fini(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
|
|
+
|
|
+ if (!chan->debugfs.active)
|
|
+ return;
|
|
+
|
|
+ drm_debugfs_remove_files(&chan->debugfs.info, 1, chan->dev->primary);
|
|
+ chan->debugfs.active = false;
|
|
+
|
|
+ if (chan == dev_priv->channel) {
|
|
+ debugfs_remove(dev_priv->debugfs.channel_root);
|
|
+ dev_priv->debugfs.channel_root = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_debugfs_chipset_info(struct seq_file *m, void *data)
|
|
+{
|
|
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
|
|
+ struct drm_minor *minor = node->minor;
|
|
+ struct drm_device *dev = minor->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t ppci_0;
|
|
+
|
|
+ ppci_0 = nv_rd32(dev, dev_priv->chipset >= 0x40 ? 0x88000 : 0x1800);
|
|
+
|
|
+ seq_printf(m, "PMC_BOOT_0: 0x%08x\n", nv_rd32(dev, NV03_PMC_BOOT_0));
|
|
+ seq_printf(m, "PCI ID : 0x%04x:0x%04x\n",
|
|
+ ppci_0 & 0xffff, ppci_0 >> 16);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_debugfs_memory_info(struct seq_file *m, void *data)
|
|
+{
|
|
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
|
|
+ struct drm_minor *minor = node->minor;
|
|
+ struct drm_device *dev = minor->dev;
|
|
+
|
|
+ seq_printf(m, "VRAM total: %dKiB\n",
|
|
+ (int)(nouveau_mem_fb_amount(dev) >> 10));
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct drm_info_list nouveau_debugfs_list[] = {
|
|
+ { "chipset", nouveau_debugfs_chipset_info, 0, NULL },
|
|
+ { "memory", nouveau_debugfs_memory_info, 0, NULL },
|
|
+};
|
|
+#define NOUVEAU_DEBUGFS_ENTRIES ARRAY_SIZE(nouveau_debugfs_list)
|
|
+
|
|
+int
|
|
+nouveau_debugfs_init(struct drm_minor *minor)
|
|
+{
|
|
+ drm_debugfs_create_files(nouveau_debugfs_list, NOUVEAU_DEBUGFS_ENTRIES,
|
|
+ minor->debugfs_root, minor);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_debugfs_takedown(struct drm_minor *minor)
|
|
+{
|
|
+ drm_debugfs_remove_files(nouveau_debugfs_list, NOUVEAU_DEBUGFS_ENTRIES,
|
|
+ minor);
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
|
|
new file mode 100644
|
|
index 0000000..dfc9439
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
|
|
@@ -0,0 +1,115 @@
|
|
+/*
|
|
+ * Copyright (C) 2008 Maarten Maathuis.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm_crtc_helper.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_fb.h"
|
|
+#include "nouveau_fbcon.h"
|
|
+
|
|
+static void
|
|
+nouveau_user_framebuffer_destroy(struct drm_framebuffer *drm_fb)
|
|
+{
|
|
+ struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);
|
|
+ struct drm_device *dev = drm_fb->dev;
|
|
+
|
|
+ if (drm_fb->fbdev)
|
|
+ nouveau_fbcon_remove(dev, drm_fb);
|
|
+
|
|
+ if (fb->nvbo) {
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
+ drm_gem_object_unreference(fb->nvbo->gem);
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+ }
|
|
+
|
|
+ drm_framebuffer_cleanup(drm_fb);
|
|
+ kfree(fb);
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_user_framebuffer_create_handle(struct drm_framebuffer *drm_fb,
|
|
+ struct drm_file *file_priv,
|
|
+ unsigned int *handle)
|
|
+{
|
|
+ struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);
|
|
+
|
|
+ return drm_gem_handle_create(file_priv, fb->nvbo->gem, handle);
|
|
+}
|
|
+
|
|
+static const struct drm_framebuffer_funcs nouveau_framebuffer_funcs = {
|
|
+ .destroy = nouveau_user_framebuffer_destroy,
|
|
+ .create_handle = nouveau_user_framebuffer_create_handle,
|
|
+};
|
|
+
|
|
+struct drm_framebuffer *
|
|
+nouveau_framebuffer_create(struct drm_device *dev, struct nouveau_bo *nvbo,
|
|
+ struct drm_mode_fb_cmd *mode_cmd)
|
|
+{
|
|
+ struct nouveau_framebuffer *fb;
|
|
+ int ret;
|
|
+
|
|
+ fb = kzalloc(sizeof(struct nouveau_framebuffer), GFP_KERNEL);
|
|
+ if (!fb)
|
|
+ return NULL;
|
|
+
|
|
+ ret = drm_framebuffer_init(dev, &fb->base, &nouveau_framebuffer_funcs);
|
|
+ if (ret) {
|
|
+ kfree(fb);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd);
|
|
+
|
|
+ fb->nvbo = nvbo;
|
|
+ return &fb->base;
|
|
+}
|
|
+
|
|
+static struct drm_framebuffer *
|
|
+nouveau_user_framebuffer_create(struct drm_device *dev,
|
|
+ struct drm_file *file_priv,
|
|
+ struct drm_mode_fb_cmd *mode_cmd)
|
|
+{
|
|
+ struct drm_framebuffer *fb;
|
|
+ struct drm_gem_object *gem;
|
|
+
|
|
+ gem = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle);
|
|
+ if (!gem)
|
|
+ return NULL;
|
|
+
|
|
+ fb = nouveau_framebuffer_create(dev, nouveau_gem_object(gem), mode_cmd);
|
|
+ if (!fb) {
|
|
+ drm_gem_object_unreference(gem);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ return fb;
|
|
+}
|
|
+
|
|
+const struct drm_mode_config_funcs nouveau_mode_config_funcs = {
|
|
+ .fb_create = nouveau_user_framebuffer_create,
|
|
+ .fb_changed = nouveau_fbcon_probe,
|
|
+};
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.c b/drivers/gpu/drm/nouveau/nouveau_dma.c
|
|
new file mode 100644
|
|
index 0000000..50d9e67
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_dma.c
|
|
@@ -0,0 +1,244 @@
|
|
+/*
|
|
+ * Copyright (C) 2007 Ben Skeggs.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_dma.h"
|
|
+
|
|
+void
|
|
+nouveau_dma_pre_init(struct nouveau_channel *chan)
|
|
+{
|
|
+ chan->dma.max = (chan->pushbuf_bo->bo.mem.size >> 2) - 2;
|
|
+ chan->dma.put = 0;
|
|
+ chan->dma.cur = chan->dma.put;
|
|
+ chan->dma.free = chan->dma.max - chan->dma.cur;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_dma_init(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_gpuobj *m2mf = NULL;
|
|
+ struct nouveau_gpuobj *nvsw = NULL;
|
|
+ int ret, i;
|
|
+
|
|
+ /* Create NV_MEMORY_TO_MEMORY_FORMAT for buffer moves */
|
|
+ ret = nouveau_gpuobj_gr_new(chan, dev_priv->card_type < NV_50 ?
|
|
+ 0x0039 : 0x5039, &m2mf);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = nouveau_gpuobj_ref_add(dev, chan, NvM2MF, m2mf, NULL);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* Create an NV_SW object for various sync purposes */
|
|
+ ret = nouveau_gpuobj_sw_new(chan, NV_SW, &nvsw);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = nouveau_gpuobj_ref_add(dev, chan, NvSw, nvsw, NULL);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* NV_MEMORY_TO_MEMORY_FORMAT requires a notifier object */
|
|
+ ret = nouveau_notifier_alloc(chan, NvNotify0, 32, &chan->m2mf_ntfy);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* Map push buffer */
|
|
+ ret = nouveau_bo_map(chan->pushbuf_bo);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* Map M2MF notifier object - fbcon. */
|
|
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
|
+ ret = nouveau_bo_map(chan->notifier_bo);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /* Insert NOPS for NOUVEAU_DMA_SKIPS */
|
|
+ ret = RING_SPACE(chan, NOUVEAU_DMA_SKIPS);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ for (i = 0; i < NOUVEAU_DMA_SKIPS; i++)
|
|
+ OUT_RING(chan, 0);
|
|
+
|
|
+ /* Initialise NV_MEMORY_TO_MEMORY_FORMAT */
|
|
+ ret = RING_SPACE(chan, 4);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NAME, 1);
|
|
+ OUT_RING(chan, NvM2MF);
|
|
+ BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY, 1);
|
|
+ OUT_RING(chan, NvNotify0);
|
|
+
|
|
+ /* Initialise NV_SW */
|
|
+ ret = RING_SPACE(chan, 2);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ BEGIN_RING(chan, NvSubSw, 0, 1);
|
|
+ OUT_RING(chan, NvSw);
|
|
+
|
|
+ /* Sit back and pray the channel works.. */
|
|
+ FIRE_RING(chan);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+OUT_RINGp(struct nouveau_channel *chan, const void *data, unsigned nr_dwords)
|
|
+{
|
|
+ bool is_iomem;
|
|
+ u32 *mem = ttm_kmap_obj_virtual(&chan->pushbuf_bo->kmap, &is_iomem);
|
|
+ mem = &mem[chan->dma.cur];
|
|
+ if (is_iomem)
|
|
+ memcpy_toio((void __force __iomem *)mem, data, nr_dwords * 4);
|
|
+ else
|
|
+ memcpy(mem, data, nr_dwords * 4);
|
|
+ chan->dma.cur += nr_dwords;
|
|
+}
|
|
+
|
|
+/* Fetch and adjust GPU GET pointer
|
|
+ *
|
|
+ * Returns:
|
|
+ * value >= 0, the adjusted GET pointer
|
|
+ * -EINVAL if GET pointer currently outside main push buffer
|
|
+ * -EBUSY if timeout exceeded
|
|
+ */
|
|
+static inline int
|
|
+READ_GET(struct nouveau_channel *chan, uint32_t *prev_get, uint32_t *timeout)
|
|
+{
|
|
+ uint32_t val;
|
|
+
|
|
+ val = nvchan_rd32(chan, chan->user_get);
|
|
+
|
|
+ /* reset counter as long as GET is still advancing, this is
|
|
+ * to avoid misdetecting a GPU lockup if the GPU happens to
|
|
+ * just be processing an operation that takes a long time
|
|
+ */
|
|
+ if (val != *prev_get) {
|
|
+ *prev_get = val;
|
|
+ *timeout = 0;
|
|
+ }
|
|
+
|
|
+ if ((++*timeout & 0xff) == 0) {
|
|
+ DRM_UDELAY(1);
|
|
+ if (*timeout > 100000)
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ if (val < chan->pushbuf_base ||
|
|
+ val > chan->pushbuf_base + (chan->dma.max << 2))
|
|
+ return -EINVAL;
|
|
+
|
|
+ return (val - chan->pushbuf_base) >> 2;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_dma_wait(struct nouveau_channel *chan, int size)
|
|
+{
|
|
+ uint32_t prev_get = 0, cnt = 0;
|
|
+ int get;
|
|
+
|
|
+ while (chan->dma.free < size) {
|
|
+ get = READ_GET(chan, &prev_get, &cnt);
|
|
+ if (unlikely(get == -EBUSY))
|
|
+ return -EBUSY;
|
|
+
|
|
+ /* loop until we have a usable GET pointer. the value
|
|
+ * we read from the GPU may be outside the main ring if
|
|
+ * PFIFO is processing a buffer called from the main ring,
|
|
+ * discard these values until something sensible is seen.
|
|
+ *
|
|
+ * the other case we discard GET is while the GPU is fetching
|
|
+ * from the SKIPS area, so the code below doesn't have to deal
|
|
+ * with some fun corner cases.
|
|
+ */
|
|
+ if (unlikely(get == -EINVAL) || get < NOUVEAU_DMA_SKIPS)
|
|
+ continue;
|
|
+
|
|
+ if (get <= chan->dma.cur) {
|
|
+ /* engine is fetching behind us, or is completely
|
|
+ * idle (GET == PUT) so we have free space up until
|
|
+ * the end of the push buffer
|
|
+ *
|
|
+ * we can only hit that path once per call due to
|
|
+ * looping back to the beginning of the push buffer,
|
|
+ * we'll hit the fetching-ahead-of-us path from that
|
|
+ * point on.
|
|
+ *
|
|
+ * the *one* exception to that rule is if we read
|
|
+ * GET==PUT, in which case the below conditional will
|
|
+ * always succeed and break us out of the wait loop.
|
|
+ */
|
|
+ chan->dma.free = chan->dma.max - chan->dma.cur;
|
|
+ if (chan->dma.free >= size)
|
|
+ break;
|
|
+
|
|
+ /* not enough space left at the end of the push buffer,
|
|
+ * instruct the GPU to jump back to the start right
|
|
+ * after processing the currently pending commands.
|
|
+ */
|
|
+ OUT_RING(chan, chan->pushbuf_base | 0x20000000);
|
|
+
|
|
+ /* wait for GET to depart from the skips area.
|
|
+ * prevents writing GET==PUT and causing a race
|
|
+ * condition that causes us to think the GPU is
|
|
+ * idle when it's not.
|
|
+ */
|
|
+ do {
|
|
+ get = READ_GET(chan, &prev_get, &cnt);
|
|
+ if (unlikely(get == -EBUSY))
|
|
+ return -EBUSY;
|
|
+ if (unlikely(get == -EINVAL))
|
|
+ continue;
|
|
+ } while (get <= NOUVEAU_DMA_SKIPS);
|
|
+ WRITE_PUT(NOUVEAU_DMA_SKIPS);
|
|
+
|
|
+ /* we're now submitting commands at the start of
|
|
+ * the push buffer.
|
|
+ */
|
|
+ chan->dma.cur =
|
|
+ chan->dma.put = NOUVEAU_DMA_SKIPS;
|
|
+ }
|
|
+
|
|
+ /* engine fetching ahead of us, we have space up until the
|
|
+ * current GET pointer. the "- 1" is to ensure there's
|
|
+ * space left to emit a jump back to the beginning of the
|
|
+ * push buffer if we require it. we can never get GET == PUT
|
|
+ * here, so this is safe.
|
|
+ */
|
|
+ chan->dma.free = get - chan->dma.cur - 1;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.h b/drivers/gpu/drm/nouveau/nouveau_dma.h
|
|
new file mode 100644
|
|
index 0000000..dabfd65
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_dma.h
|
|
@@ -0,0 +1,159 @@
|
|
+/*
|
|
+ * Copyright (C) 2007 Ben Skeggs.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef __NOUVEAU_DMA_H__
|
|
+#define __NOUVEAU_DMA_H__
|
|
+
|
|
+#ifndef NOUVEAU_DMA_DEBUG
|
|
+#define NOUVEAU_DMA_DEBUG 0
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * There's a hw race condition where you can't jump to your PUT offset,
|
|
+ * to avoid this we jump to offset + SKIPS and fill the difference with
|
|
+ * NOPs.
|
|
+ *
|
|
+ * xf86-video-nv configures the DMA fetch size to 32 bytes, and uses
|
|
+ * a SKIPS value of 8. Lets assume that the race condition is to do
|
|
+ * with writing into the fetch area, we configure a fetch size of 128
|
|
+ * bytes so we need a larger SKIPS value.
|
|
+ */
|
|
+#define NOUVEAU_DMA_SKIPS (128 / 4)
|
|
+
|
|
+/* Hardcoded object assignments to subchannels (subchannel id). */
|
|
+enum {
|
|
+ NvSubM2MF = 0,
|
|
+ NvSubSw = 1,
|
|
+ NvSub2D = 2,
|
|
+ NvSubCtxSurf2D = 2,
|
|
+ NvSubGdiRect = 3,
|
|
+ NvSubImageBlit = 4
|
|
+};
|
|
+
|
|
+/* Object handles. */
|
|
+enum {
|
|
+ NvM2MF = 0x80000001,
|
|
+ NvDmaFB = 0x80000002,
|
|
+ NvDmaTT = 0x80000003,
|
|
+ NvDmaVRAM = 0x80000004,
|
|
+ NvDmaGART = 0x80000005,
|
|
+ NvNotify0 = 0x80000006,
|
|
+ Nv2D = 0x80000007,
|
|
+ NvCtxSurf2D = 0x80000008,
|
|
+ NvRop = 0x80000009,
|
|
+ NvImagePatt = 0x8000000a,
|
|
+ NvClipRect = 0x8000000b,
|
|
+ NvGdiRect = 0x8000000c,
|
|
+ NvImageBlit = 0x8000000d,
|
|
+ NvSw = 0x8000000e,
|
|
+
|
|
+ /* G80+ display objects */
|
|
+ NvEvoVRAM = 0x01000000,
|
|
+ NvEvoFB16 = 0x01000001,
|
|
+ NvEvoFB32 = 0x01000002
|
|
+};
|
|
+
|
|
+#define NV_MEMORY_TO_MEMORY_FORMAT 0x00000039
|
|
+#define NV_MEMORY_TO_MEMORY_FORMAT_NAME 0x00000000
|
|
+#define NV_MEMORY_TO_MEMORY_FORMAT_SET_REF 0x00000050
|
|
+#define NV_MEMORY_TO_MEMORY_FORMAT_NOP 0x00000100
|
|
+#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY 0x00000104
|
|
+#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY_STYLE_WRITE 0x00000000
|
|
+#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY_STYLE_WRITE_LE_AWAKEN 0x00000001
|
|
+#define NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY 0x00000180
|
|
+#define NV_MEMORY_TO_MEMORY_FORMAT_DMA_SOURCE 0x00000184
|
|
+#define NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN 0x0000030c
|
|
+
|
|
+#define NV50_MEMORY_TO_MEMORY_FORMAT 0x00005039
|
|
+#define NV50_MEMORY_TO_MEMORY_FORMAT_UNK200 0x00000200
|
|
+#define NV50_MEMORY_TO_MEMORY_FORMAT_UNK21C 0x0000021c
|
|
+#define NV50_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN_HIGH 0x00000238
|
|
+#define NV50_MEMORY_TO_MEMORY_FORMAT_OFFSET_OUT_HIGH 0x0000023c
|
|
+
|
|
+static __must_check inline int
|
|
+RING_SPACE(struct nouveau_channel *chan, int size)
|
|
+{
|
|
+ if (chan->dma.free < size) {
|
|
+ int ret;
|
|
+
|
|
+ ret = nouveau_dma_wait(chan, size);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ chan->dma.free -= size;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline void
|
|
+OUT_RING(struct nouveau_channel *chan, int data)
|
|
+{
|
|
+ if (NOUVEAU_DMA_DEBUG) {
|
|
+ NV_INFO(chan->dev, "Ch%d/0x%08x: 0x%08x\n",
|
|
+ chan->id, chan->dma.cur << 2, data);
|
|
+ }
|
|
+
|
|
+ nouveau_bo_wr32(chan->pushbuf_bo, chan->dma.cur++, data);
|
|
+}
|
|
+
|
|
+extern void
|
|
+OUT_RINGp(struct nouveau_channel *chan, const void *data, unsigned nr_dwords);
|
|
+
|
|
+static inline void
|
|
+BEGIN_RING(struct nouveau_channel *chan, int subc, int mthd, int size)
|
|
+{
|
|
+ OUT_RING(chan, (subc << 13) | (size << 18) | mthd);
|
|
+}
|
|
+
|
|
+#define WRITE_PUT(val) do { \
|
|
+ DRM_MEMORYBARRIER(); \
|
|
+ nouveau_bo_rd32(chan->pushbuf_bo, 0); \
|
|
+ nvchan_wr32(chan, chan->user_put, ((val) << 2) + chan->pushbuf_base); \
|
|
+} while (0)
|
|
+
|
|
+static inline void
|
|
+FIRE_RING(struct nouveau_channel *chan)
|
|
+{
|
|
+ if (NOUVEAU_DMA_DEBUG) {
|
|
+ NV_INFO(chan->dev, "Ch%d/0x%08x: PUSH!\n",
|
|
+ chan->id, chan->dma.cur << 2);
|
|
+ }
|
|
+
|
|
+ if (chan->dma.cur == chan->dma.put)
|
|
+ return;
|
|
+ chan->accel_done = true;
|
|
+
|
|
+ WRITE_PUT(chan->dma.cur);
|
|
+ chan->dma.put = chan->dma.cur;
|
|
+}
|
|
+
|
|
+static inline void
|
|
+WIND_RING(struct nouveau_channel *chan)
|
|
+{
|
|
+ chan->dma.cur = chan->dma.put;
|
|
+}
|
|
+
|
|
+#endif
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c
|
|
new file mode 100644
|
|
index 0000000..f954ad9
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_dp.c
|
|
@@ -0,0 +1,575 @@
|
|
+/*
|
|
+ * Copyright 2009 Red Hat Inc.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice shall be included in
|
|
+ * all copies or substantial portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
+ * OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ * Authors: Ben Skeggs
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_i2c.h"
|
|
+#include "nouveau_encoder.h"
|
|
+
|
|
+static int
|
|
+auxch_rd(struct drm_encoder *encoder, int address, uint8_t *buf, int size)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ struct nouveau_i2c_chan *auxch;
|
|
+ int ret;
|
|
+
|
|
+ auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
|
|
+ if (!auxch)
|
|
+ return -ENODEV;
|
|
+
|
|
+ ret = nouveau_dp_auxch(auxch, 9, address, buf, size);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+auxch_wr(struct drm_encoder *encoder, int address, uint8_t *buf, int size)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ struct nouveau_i2c_chan *auxch;
|
|
+ int ret;
|
|
+
|
|
+ auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
|
|
+ if (!auxch)
|
|
+ return -ENODEV;
|
|
+
|
|
+ ret = nouveau_dp_auxch(auxch, 8, address, buf, size);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_dp_lane_count_set(struct drm_encoder *encoder, uint8_t cmd)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ uint32_t tmp;
|
|
+ int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1);
|
|
+
|
|
+ tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link));
|
|
+ tmp &= ~(NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED |
|
|
+ NV50_SOR_DP_CTRL_LANE_MASK);
|
|
+ tmp |= ((1 << (cmd & DP_LANE_COUNT_MASK)) - 1) << 16;
|
|
+ if (cmd & DP_LANE_COUNT_ENHANCED_FRAME_EN)
|
|
+ tmp |= NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED;
|
|
+ nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp);
|
|
+
|
|
+ return auxch_wr(encoder, DP_LANE_COUNT_SET, &cmd, 1);
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_dp_link_bw_set(struct drm_encoder *encoder, uint8_t cmd)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ uint32_t tmp;
|
|
+ int reg = 0x614300 + (nv_encoder->or * 0x800);
|
|
+
|
|
+ tmp = nv_rd32(dev, reg);
|
|
+ tmp &= 0xfff3ffff;
|
|
+ if (cmd == DP_LINK_BW_2_7)
|
|
+ tmp |= 0x00040000;
|
|
+ nv_wr32(dev, reg, tmp);
|
|
+
|
|
+ return auxch_wr(encoder, DP_LINK_BW_SET, &cmd, 1);
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_dp_link_train_set(struct drm_encoder *encoder, int pattern)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ uint32_t tmp;
|
|
+ uint8_t cmd;
|
|
+ int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1);
|
|
+ int ret;
|
|
+
|
|
+ tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link));
|
|
+ tmp &= ~NV50_SOR_DP_CTRL_TRAINING_PATTERN;
|
|
+ tmp |= (pattern << 24);
|
|
+ nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp);
|
|
+
|
|
+ ret = auxch_rd(encoder, DP_TRAINING_PATTERN_SET, &cmd, 1);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ cmd &= ~DP_TRAINING_PATTERN_MASK;
|
|
+ cmd |= (pattern & DP_TRAINING_PATTERN_MASK);
|
|
+ return auxch_wr(encoder, DP_TRAINING_PATTERN_SET, &cmd, 1);
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_dp_max_voltage_swing(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct bit_displayport_encoder_table_entry *dpse;
|
|
+ struct bit_displayport_encoder_table *dpe;
|
|
+ int i, dpe_headerlen, max_vs = 0;
|
|
+
|
|
+ dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
|
|
+ if (!dpe)
|
|
+ return false;
|
|
+ dpse = (void *)((char *)dpe + dpe_headerlen);
|
|
+
|
|
+ for (i = 0; i < dpe_headerlen; i++, dpse++) {
|
|
+ if (dpse->vs_level > max_vs)
|
|
+ max_vs = dpse->vs_level;
|
|
+ }
|
|
+
|
|
+ return max_vs;
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_dp_max_pre_emphasis(struct drm_encoder *encoder, int vs)
|
|
+{
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct bit_displayport_encoder_table_entry *dpse;
|
|
+ struct bit_displayport_encoder_table *dpe;
|
|
+ int i, dpe_headerlen, max_pre = 0;
|
|
+
|
|
+ dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
|
|
+ if (!dpe)
|
|
+ return false;
|
|
+ dpse = (void *)((char *)dpe + dpe_headerlen);
|
|
+
|
|
+ for (i = 0; i < dpe_headerlen; i++, dpse++) {
|
|
+ if (dpse->vs_level != vs)
|
|
+ continue;
|
|
+
|
|
+ if (dpse->pre_level > max_pre)
|
|
+ max_pre = dpse->pre_level;
|
|
+ }
|
|
+
|
|
+ return max_pre;
|
|
+}
|
|
+
|
|
+static bool
|
|
+nouveau_dp_link_train_adjust(struct drm_encoder *encoder, uint8_t *config)
|
|
+{
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct bit_displayport_encoder_table_entry *dpse;
|
|
+ struct bit_displayport_encoder_table *dpe;
|
|
+ int ret, i, dpe_headerlen, vs = 0, pre = 0;
|
|
+ uint8_t request[2];
|
|
+
|
|
+ dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
|
|
+ if (!dpe)
|
|
+ return false;
|
|
+ dpse = (void *)((char *)dpe + dpe_headerlen);
|
|
+
|
|
+ ret = auxch_rd(encoder, DP_ADJUST_REQUEST_LANE0_1, request, 2);
|
|
+ if (ret)
|
|
+ return false;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "\t\tadjust 0x%02x 0x%02x\n", request[0], request[1]);
|
|
+
|
|
+ /* Keep all lanes at the same level.. */
|
|
+ for (i = 0; i < nv_encoder->dp.link_nr; i++) {
|
|
+ int lane_req = (request[i >> 1] >> ((i & 1) << 2)) & 0xf;
|
|
+ int lane_vs = lane_req & 3;
|
|
+ int lane_pre = (lane_req >> 2) & 3;
|
|
+
|
|
+ if (lane_vs > vs)
|
|
+ vs = lane_vs;
|
|
+ if (lane_pre > pre)
|
|
+ pre = lane_pre;
|
|
+ }
|
|
+
|
|
+ if (vs >= nouveau_dp_max_voltage_swing(encoder)) {
|
|
+ vs = nouveau_dp_max_voltage_swing(encoder);
|
|
+ vs |= 4;
|
|
+ }
|
|
+
|
|
+ if (pre >= nouveau_dp_max_pre_emphasis(encoder, vs & 3)) {
|
|
+ pre = nouveau_dp_max_pre_emphasis(encoder, vs & 3);
|
|
+ pre |= 4;
|
|
+ }
|
|
+
|
|
+ /* Update the configuration for all lanes.. */
|
|
+ for (i = 0; i < nv_encoder->dp.link_nr; i++)
|
|
+ config[i] = (pre << 3) | vs;
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static bool
|
|
+nouveau_dp_link_train_commit(struct drm_encoder *encoder, uint8_t *config)
|
|
+{
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct bit_displayport_encoder_table_entry *dpse;
|
|
+ struct bit_displayport_encoder_table *dpe;
|
|
+ int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1);
|
|
+ int dpe_headerlen, ret, i;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "\t\tconfig 0x%02x 0x%02x 0x%02x 0x%02x\n",
|
|
+ config[0], config[1], config[2], config[3]);
|
|
+
|
|
+ dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
|
|
+ if (!dpe)
|
|
+ return false;
|
|
+ dpse = (void *)((char *)dpe + dpe_headerlen);
|
|
+
|
|
+ for (i = 0; i < dpe->record_nr; i++, dpse++) {
|
|
+ if (dpse->vs_level == (config[0] & 3) &&
|
|
+ dpse->pre_level == ((config[0] >> 3) & 3))
|
|
+ break;
|
|
+ }
|
|
+ BUG_ON(i == dpe->record_nr);
|
|
+
|
|
+ for (i = 0; i < nv_encoder->dp.link_nr; i++) {
|
|
+ const int shift[4] = { 16, 8, 0, 24 };
|
|
+ uint32_t mask = 0xff << shift[i];
|
|
+ uint32_t reg0, reg1, reg2;
|
|
+
|
|
+ reg0 = nv_rd32(dev, NV50_SOR_DP_UNK118(or, link)) & ~mask;
|
|
+ reg0 |= (dpse->reg0 << shift[i]);
|
|
+ reg1 = nv_rd32(dev, NV50_SOR_DP_UNK120(or, link)) & ~mask;
|
|
+ reg1 |= (dpse->reg1 << shift[i]);
|
|
+ reg2 = nv_rd32(dev, NV50_SOR_DP_UNK130(or, link)) & 0xffff00ff;
|
|
+ reg2 |= (dpse->reg2 << 8);
|
|
+ nv_wr32(dev, NV50_SOR_DP_UNK118(or, link), reg0);
|
|
+ nv_wr32(dev, NV50_SOR_DP_UNK120(or, link), reg1);
|
|
+ nv_wr32(dev, NV50_SOR_DP_UNK130(or, link), reg2);
|
|
+ }
|
|
+
|
|
+ ret = auxch_wr(encoder, DP_TRAINING_LANE0_SET, config, 4);
|
|
+ if (ret)
|
|
+ return false;
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+bool
|
|
+nouveau_dp_link_train(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ uint8_t config[4];
|
|
+ uint8_t status[3];
|
|
+ bool cr_done, cr_max_vs, eq_done;
|
|
+ int ret = 0, i, tries, voltage;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "link training!!\n");
|
|
+train:
|
|
+ cr_done = eq_done = false;
|
|
+
|
|
+ /* set link configuration */
|
|
+ NV_DEBUG_KMS(dev, "\tbegin train: bw %d, lanes %d\n",
|
|
+ nv_encoder->dp.link_bw, nv_encoder->dp.link_nr);
|
|
+
|
|
+ ret = nouveau_dp_link_bw_set(encoder, nv_encoder->dp.link_bw);
|
|
+ if (ret)
|
|
+ return false;
|
|
+
|
|
+ config[0] = nv_encoder->dp.link_nr;
|
|
+ if (nv_encoder->dp.dpcd_version >= 0x11)
|
|
+ config[0] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
|
|
+
|
|
+ ret = nouveau_dp_lane_count_set(encoder, config[0]);
|
|
+ if (ret)
|
|
+ return false;
|
|
+
|
|
+ /* clock recovery */
|
|
+ NV_DEBUG_KMS(dev, "\tbegin cr\n");
|
|
+ ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_1);
|
|
+ if (ret)
|
|
+ goto stop;
|
|
+
|
|
+ tries = 0;
|
|
+ voltage = -1;
|
|
+ memset(config, 0x00, sizeof(config));
|
|
+ for (;;) {
|
|
+ if (!nouveau_dp_link_train_commit(encoder, config))
|
|
+ break;
|
|
+
|
|
+ udelay(100);
|
|
+
|
|
+ ret = auxch_rd(encoder, DP_LANE0_1_STATUS, status, 2);
|
|
+ if (ret)
|
|
+ break;
|
|
+ NV_DEBUG_KMS(dev, "\t\tstatus: 0x%02x 0x%02x\n",
|
|
+ status[0], status[1]);
|
|
+
|
|
+ cr_done = true;
|
|
+ cr_max_vs = false;
|
|
+ for (i = 0; i < nv_encoder->dp.link_nr; i++) {
|
|
+ int lane = (status[i >> 1] >> ((i & 1) * 4)) & 0xf;
|
|
+
|
|
+ if (!(lane & DP_LANE_CR_DONE)) {
|
|
+ cr_done = false;
|
|
+ if (config[i] & DP_TRAIN_MAX_PRE_EMPHASIS_REACHED)
|
|
+ cr_max_vs = true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if ((config[0] & DP_TRAIN_VOLTAGE_SWING_MASK) != voltage) {
|
|
+ voltage = config[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
|
|
+ tries = 0;
|
|
+ }
|
|
+
|
|
+ if (cr_done || cr_max_vs || (++tries == 5))
|
|
+ break;
|
|
+
|
|
+ if (!nouveau_dp_link_train_adjust(encoder, config))
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (!cr_done)
|
|
+ goto stop;
|
|
+
|
|
+ /* channel equalisation */
|
|
+ NV_DEBUG_KMS(dev, "\tbegin eq\n");
|
|
+ ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_2);
|
|
+ if (ret)
|
|
+ goto stop;
|
|
+
|
|
+ for (tries = 0; tries <= 5; tries++) {
|
|
+ udelay(400);
|
|
+
|
|
+ ret = auxch_rd(encoder, DP_LANE0_1_STATUS, status, 3);
|
|
+ if (ret)
|
|
+ break;
|
|
+ NV_DEBUG_KMS(dev, "\t\tstatus: 0x%02x 0x%02x\n",
|
|
+ status[0], status[1]);
|
|
+
|
|
+ eq_done = true;
|
|
+ if (!(status[2] & DP_INTERLANE_ALIGN_DONE))
|
|
+ eq_done = false;
|
|
+
|
|
+ for (i = 0; eq_done && i < nv_encoder->dp.link_nr; i++) {
|
|
+ int lane = (status[i >> 1] >> ((i & 1) * 4)) & 0xf;
|
|
+
|
|
+ if (!(lane & DP_LANE_CR_DONE)) {
|
|
+ cr_done = false;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (!(lane & DP_LANE_CHANNEL_EQ_DONE) ||
|
|
+ !(lane & DP_LANE_SYMBOL_LOCKED)) {
|
|
+ eq_done = false;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (eq_done || !cr_done)
|
|
+ break;
|
|
+
|
|
+ if (!nouveau_dp_link_train_adjust(encoder, config) ||
|
|
+ !nouveau_dp_link_train_commit(encoder, config))
|
|
+ break;
|
|
+ }
|
|
+
|
|
+stop:
|
|
+ /* end link training */
|
|
+ ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_DISABLE);
|
|
+ if (ret)
|
|
+ return false;
|
|
+
|
|
+ /* retry at a lower setting, if possible */
|
|
+ if (!ret && !(eq_done && cr_done)) {
|
|
+ NV_DEBUG_KMS(dev, "\twe failed\n");
|
|
+ if (nv_encoder->dp.link_bw != DP_LINK_BW_1_62) {
|
|
+ NV_DEBUG_KMS(dev, "retry link training at low rate\n");
|
|
+ nv_encoder->dp.link_bw = DP_LINK_BW_1_62;
|
|
+ goto train;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return eq_done;
|
|
+}
|
|
+
|
|
+bool
|
|
+nouveau_dp_detect(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ uint8_t dpcd[4];
|
|
+ int ret;
|
|
+
|
|
+ ret = auxch_rd(encoder, 0x0000, dpcd, 4);
|
|
+ if (ret)
|
|
+ return false;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "encoder: link_bw %d, link_nr %d\n"
|
|
+ "display: link_bw %d, link_nr %d version 0x%02x\n",
|
|
+ nv_encoder->dcb->dpconf.link_bw,
|
|
+ nv_encoder->dcb->dpconf.link_nr,
|
|
+ dpcd[1], dpcd[2] & 0x0f, dpcd[0]);
|
|
+
|
|
+ nv_encoder->dp.dpcd_version = dpcd[0];
|
|
+
|
|
+ nv_encoder->dp.link_bw = dpcd[1];
|
|
+ if (nv_encoder->dp.link_bw != DP_LINK_BW_1_62 &&
|
|
+ !nv_encoder->dcb->dpconf.link_bw)
|
|
+ nv_encoder->dp.link_bw = DP_LINK_BW_1_62;
|
|
+
|
|
+ nv_encoder->dp.link_nr = dpcd[2] & 0xf;
|
|
+ if (nv_encoder->dp.link_nr > nv_encoder->dcb->dpconf.link_nr)
|
|
+ nv_encoder->dp.link_nr = nv_encoder->dcb->dpconf.link_nr;
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
|
|
+ uint8_t *data, int data_nr)
|
|
+{
|
|
+ struct drm_device *dev = auxch->dev;
|
|
+ uint32_t tmp, ctrl, stat = 0, data32[4] = {};
|
|
+ int ret = 0, i, index = auxch->rd;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "ch %d cmd %d addr 0x%x len %d\n", index, cmd, addr, data_nr);
|
|
+
|
|
+ tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd));
|
|
+ nv_wr32(dev, NV50_AUXCH_CTRL(auxch->rd), tmp | 0x00100000);
|
|
+ tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd));
|
|
+ if (!(tmp & 0x01000000)) {
|
|
+ NV_ERROR(dev, "expected bit 24 == 1, got 0x%08x\n", tmp);
|
|
+ ret = -EIO;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < 3; i++) {
|
|
+ tmp = nv_rd32(dev, NV50_AUXCH_STAT(auxch->rd));
|
|
+ if (tmp & NV50_AUXCH_STAT_STATE_READY)
|
|
+ break;
|
|
+ udelay(100);
|
|
+ }
|
|
+
|
|
+ if (i == 3) {
|
|
+ ret = -EBUSY;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (!(cmd & 1)) {
|
|
+ memcpy(data32, data, data_nr);
|
|
+ for (i = 0; i < 4; i++) {
|
|
+ NV_DEBUG_KMS(dev, "wr %d: 0x%08x\n", i, data32[i]);
|
|
+ nv_wr32(dev, NV50_AUXCH_DATA_OUT(index, i), data32[i]);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ nv_wr32(dev, NV50_AUXCH_ADDR(index), addr);
|
|
+ ctrl = nv_rd32(dev, NV50_AUXCH_CTRL(index));
|
|
+ ctrl &= ~(NV50_AUXCH_CTRL_CMD | NV50_AUXCH_CTRL_LEN);
|
|
+ ctrl |= (cmd << NV50_AUXCH_CTRL_CMD_SHIFT);
|
|
+ ctrl |= ((data_nr - 1) << NV50_AUXCH_CTRL_LEN_SHIFT);
|
|
+
|
|
+ for (;;) {
|
|
+ nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl | 0x80000000);
|
|
+ nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl);
|
|
+ nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl | 0x00010000);
|
|
+ if (!nv_wait(NV50_AUXCH_CTRL(index), 0x00010000, 0x00000000)) {
|
|
+ NV_ERROR(dev, "expected bit 16 == 0, got 0x%08x\n",
|
|
+ nv_rd32(dev, NV50_AUXCH_CTRL(index)));
|
|
+ ret = -EBUSY;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ udelay(400);
|
|
+
|
|
+ stat = nv_rd32(dev, NV50_AUXCH_STAT(index));
|
|
+ if ((stat & NV50_AUXCH_STAT_REPLY_AUX) !=
|
|
+ NV50_AUXCH_STAT_REPLY_AUX_DEFER)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (cmd & 1) {
|
|
+ if ((stat & NV50_AUXCH_STAT_COUNT) != data_nr) {
|
|
+ ret = -EREMOTEIO;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < 4; i++) {
|
|
+ data32[i] = nv_rd32(dev, NV50_AUXCH_DATA_IN(index, i));
|
|
+ NV_DEBUG_KMS(dev, "rd %d: 0x%08x\n", i, data32[i]);
|
|
+ }
|
|
+ memcpy(data, data32, data_nr);
|
|
+ }
|
|
+
|
|
+out:
|
|
+ tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd));
|
|
+ nv_wr32(dev, NV50_AUXCH_CTRL(auxch->rd), tmp & ~0x00100000);
|
|
+ tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd));
|
|
+ if (tmp & 0x01000000) {
|
|
+ NV_ERROR(dev, "expected bit 24 == 0, got 0x%08x\n", tmp);
|
|
+ ret = -EIO;
|
|
+ }
|
|
+
|
|
+ udelay(400);
|
|
+
|
|
+ return ret ? ret : (stat & NV50_AUXCH_STAT_REPLY);
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
|
|
+ uint8_t write_byte, uint8_t *read_byte)
|
|
+{
|
|
+ struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
|
|
+ struct nouveau_i2c_chan *auxch = (struct nouveau_i2c_chan *)adapter;
|
|
+ struct drm_device *dev = auxch->dev;
|
|
+ int ret = 0, cmd, addr = algo_data->address;
|
|
+ uint8_t *buf;
|
|
+
|
|
+ if (mode == MODE_I2C_READ) {
|
|
+ cmd = AUX_I2C_READ;
|
|
+ buf = read_byte;
|
|
+ } else {
|
|
+ cmd = (mode & MODE_I2C_READ) ? AUX_I2C_READ : AUX_I2C_WRITE;
|
|
+ buf = &write_byte;
|
|
+ }
|
|
+
|
|
+ if (!(mode & MODE_I2C_STOP))
|
|
+ cmd |= AUX_I2C_MOT;
|
|
+
|
|
+ if (mode & MODE_I2C_START)
|
|
+ return 1;
|
|
+
|
|
+ for (;;) {
|
|
+ ret = nouveau_dp_auxch(auxch, cmd, addr, buf, 1);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ switch (ret & NV50_AUXCH_STAT_REPLY_I2C) {
|
|
+ case NV50_AUXCH_STAT_REPLY_I2C_ACK:
|
|
+ return 1;
|
|
+ case NV50_AUXCH_STAT_REPLY_I2C_NACK:
|
|
+ return -EREMOTEIO;
|
|
+ case NV50_AUXCH_STAT_REPLY_I2C_DEFER:
|
|
+ udelay(100);
|
|
+ break;
|
|
+ default:
|
|
+ NV_ERROR(dev, "invalid auxch status: 0x%08x\n", ret);
|
|
+ return -EREMOTEIO;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c
|
|
new file mode 100644
|
|
index 0000000..da3b93b
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.c
|
|
@@ -0,0 +1,421 @@
|
|
+/*
|
|
+ * Copyright 2005 Stephane Marchesin.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the next
|
|
+ * paragraph) shall be included in all copies or substantial portions of the
|
|
+ * Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
+ * OTHER DEALINGS IN THE SOFTWARE.
|
|
+ */
|
|
+
|
|
+#include <linux/console.h>
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "drm_crtc_helper.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_hw.h"
|
|
+#include "nouveau_fb.h"
|
|
+#include "nouveau_fbcon.h"
|
|
+#include "nv50_display.h"
|
|
+
|
|
+#include "drm_pciids.h"
|
|
+
|
|
+MODULE_PARM_DESC(ctxfw, "Use external firmware blob for grctx init (NV40)");
|
|
+int nouveau_ctxfw = 0;
|
|
+module_param_named(ctxfw, nouveau_ctxfw, int, 0400);
|
|
+
|
|
+MODULE_PARM_DESC(noagp, "Disable AGP");
|
|
+int nouveau_noagp;
|
|
+module_param_named(noagp, nouveau_noagp, int, 0400);
|
|
+
|
|
+MODULE_PARM_DESC(modeset, "Enable kernel modesetting");
|
|
+static int nouveau_modeset = -1; /* kms */
|
|
+module_param_named(modeset, nouveau_modeset, int, 0400);
|
|
+
|
|
+MODULE_PARM_DESC(vbios, "Override default VBIOS location");
|
|
+char *nouveau_vbios;
|
|
+module_param_named(vbios, nouveau_vbios, charp, 0400);
|
|
+
|
|
+MODULE_PARM_DESC(vram_pushbuf, "Force DMA push buffers to be in VRAM");
|
|
+int nouveau_vram_pushbuf;
|
|
+module_param_named(vram_pushbuf, nouveau_vram_pushbuf, int, 0400);
|
|
+
|
|
+MODULE_PARM_DESC(vram_notify, "Force DMA notifiers to be in VRAM");
|
|
+int nouveau_vram_notify = 1;
|
|
+module_param_named(vram_notify, nouveau_vram_notify, int, 0400);
|
|
+
|
|
+MODULE_PARM_DESC(duallink, "Allow dual-link TMDS (>=GeForce 8)");
|
|
+int nouveau_duallink = 1;
|
|
+module_param_named(duallink, nouveau_duallink, int, 0400);
|
|
+
|
|
+MODULE_PARM_DESC(uscript_lvds, "LVDS output script table ID (>=GeForce 8)");
|
|
+int nouveau_uscript_lvds = -1;
|
|
+module_param_named(uscript_lvds, nouveau_uscript_lvds, int, 0400);
|
|
+
|
|
+MODULE_PARM_DESC(uscript_tmds, "TMDS output script table ID (>=GeForce 8)");
|
|
+int nouveau_uscript_tmds = -1;
|
|
+module_param_named(uscript_tmds, nouveau_uscript_tmds, int, 0400);
|
|
+
|
|
+MODULE_PARM_DESC(ignorelid, "Ignore ACPI lid status");
|
|
+int nouveau_ignorelid = 0;
|
|
+module_param_named(ignorelid, nouveau_ignorelid, int, 0400);
|
|
+
|
|
+MODULE_PARM_DESC(noagp, "Disable all acceleration");
|
|
+int nouveau_noaccel = 0;
|
|
+module_param_named(noaccel, nouveau_noaccel, int, 0400);
|
|
+
|
|
+MODULE_PARM_DESC(noagp, "Disable fbcon acceleration");
|
|
+int nouveau_nofbaccel = 0;
|
|
+module_param_named(nofbaccel, nouveau_nofbaccel, int, 0400);
|
|
+
|
|
+MODULE_PARM_DESC(tv_norm, "Default TV norm.\n"
|
|
+ "\t\tSupported: PAL, PAL-M, PAL-N, PAL-Nc, NTSC-M, NTSC-J,\n"
|
|
+ "\t\t\thd480i, hd480p, hd576i, hd576p, hd720p, hd1080i.\n"
|
|
+ "\t\tDefault: PAL\n"
|
|
+ "\t\t*NOTE* Ignored for cards with external TV encoders.");
|
|
+char *nouveau_tv_norm;
|
|
+module_param_named(tv_norm, nouveau_tv_norm, charp, 0400);
|
|
+
|
|
+MODULE_PARM_DESC(reg_debug, "Register access debug bitmask:\n"
|
|
+ "\t\t0x1 mc, 0x2 video, 0x4 fb, 0x8 extdev,\n"
|
|
+ "\t\t0x10 crtc, 0x20 ramdac, 0x40 vgacrtc, 0x80 rmvio,\n"
|
|
+ "\t\t0x100 vgaattr, 0x200 EVO (G80+). ");
|
|
+int nouveau_reg_debug;
|
|
+module_param_named(reg_debug, nouveau_reg_debug, int, 0600);
|
|
+
|
|
+int nouveau_fbpercrtc;
|
|
+#if 0
|
|
+module_param_named(fbpercrtc, nouveau_fbpercrtc, int, 0400);
|
|
+#endif
|
|
+
|
|
+static struct pci_device_id pciidlist[] = {
|
|
+ {
|
|
+ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID),
|
|
+ .class = PCI_BASE_CLASS_DISPLAY << 16,
|
|
+ .class_mask = 0xff << 16,
|
|
+ },
|
|
+ {
|
|
+ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA_SGS, PCI_ANY_ID),
|
|
+ .class = PCI_BASE_CLASS_DISPLAY << 16,
|
|
+ .class_mask = 0xff << 16,
|
|
+ },
|
|
+ {}
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(pci, pciidlist);
|
|
+
|
|
+static struct drm_driver driver;
|
|
+
|
|
+static int __devinit
|
|
+nouveau_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|
+{
|
|
+ return drm_get_dev(pdev, ent, &driver);
|
|
+}
|
|
+
|
|
+static void
|
|
+nouveau_pci_remove(struct pci_dev *pdev)
|
|
+{
|
|
+ struct drm_device *dev = pci_get_drvdata(pdev);
|
|
+
|
|
+ drm_put_dev(dev);
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state)
|
|
+{
|
|
+ struct drm_device *dev = pci_get_drvdata(pdev);
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem;
|
|
+ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
|
|
+ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
|
|
+ struct nouveau_channel *chan;
|
|
+ struct drm_crtc *crtc;
|
|
+ uint32_t fbdev_flags;
|
|
+ int ret, i;
|
|
+
|
|
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
|
+ return -ENODEV;
|
|
+
|
|
+ if (pm_state.event == PM_EVENT_PRETHAW)
|
|
+ return 0;
|
|
+
|
|
+ fbdev_flags = dev_priv->fbdev_info->flags;
|
|
+ dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED;
|
|
+
|
|
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
|
+ struct nouveau_framebuffer *nouveau_fb;
|
|
+
|
|
+ nouveau_fb = nouveau_framebuffer(crtc->fb);
|
|
+ if (!nouveau_fb || !nouveau_fb->nvbo)
|
|
+ continue;
|
|
+
|
|
+ nouveau_bo_unpin(nouveau_fb->nvbo);
|
|
+ }
|
|
+
|
|
+ NV_INFO(dev, "Evicting buffers...\n");
|
|
+ ttm_bo_evict_mm(&dev_priv->ttm.bdev, TTM_PL_VRAM);
|
|
+
|
|
+ NV_INFO(dev, "Idling channels...\n");
|
|
+ for (i = 0; i < pfifo->channels; i++) {
|
|
+ struct nouveau_fence *fence = NULL;
|
|
+
|
|
+ chan = dev_priv->fifos[i];
|
|
+ if (!chan || (dev_priv->card_type >= NV_50 &&
|
|
+ chan == dev_priv->fifos[0]))
|
|
+ continue;
|
|
+
|
|
+ ret = nouveau_fence_new(chan, &fence, true);
|
|
+ if (ret == 0) {
|
|
+ ret = nouveau_fence_wait(fence, NULL, false, false);
|
|
+ nouveau_fence_unref((void *)&fence);
|
|
+ }
|
|
+
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Failed to idle channel %d for suspend\n",
|
|
+ chan->id);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ pgraph->fifo_access(dev, false);
|
|
+ nouveau_wait_for_idle(dev);
|
|
+ pfifo->reassign(dev, false);
|
|
+ pfifo->disable(dev);
|
|
+ pfifo->unload_context(dev);
|
|
+ pgraph->unload_context(dev);
|
|
+
|
|
+ NV_INFO(dev, "Suspending GPU objects...\n");
|
|
+ ret = nouveau_gpuobj_suspend(dev);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "... failed: %d\n", ret);
|
|
+ goto out_abort;
|
|
+ }
|
|
+
|
|
+ ret = pinstmem->suspend(dev);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "... failed: %d\n", ret);
|
|
+ nouveau_gpuobj_suspend_cleanup(dev);
|
|
+ goto out_abort;
|
|
+ }
|
|
+
|
|
+ NV_INFO(dev, "And we're gone!\n");
|
|
+ pci_save_state(pdev);
|
|
+ if (pm_state.event == PM_EVENT_SUSPEND) {
|
|
+ pci_disable_device(pdev);
|
|
+ pci_set_power_state(pdev, PCI_D3hot);
|
|
+ }
|
|
+
|
|
+ acquire_console_sem();
|
|
+ fb_set_suspend(dev_priv->fbdev_info, 1);
|
|
+ release_console_sem();
|
|
+ dev_priv->fbdev_info->flags = fbdev_flags;
|
|
+ return 0;
|
|
+
|
|
+out_abort:
|
|
+ NV_INFO(dev, "Re-enabling acceleration..\n");
|
|
+ pfifo->enable(dev);
|
|
+ pfifo->reassign(dev, true);
|
|
+ pgraph->fifo_access(dev, true);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_pci_resume(struct pci_dev *pdev)
|
|
+{
|
|
+ struct drm_device *dev = pci_get_drvdata(pdev);
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_engine *engine = &dev_priv->engine;
|
|
+ struct drm_crtc *crtc;
|
|
+ uint32_t fbdev_flags;
|
|
+ int ret, i;
|
|
+
|
|
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
|
+ return -ENODEV;
|
|
+
|
|
+ fbdev_flags = dev_priv->fbdev_info->flags;
|
|
+ dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED;
|
|
+
|
|
+ NV_INFO(dev, "We're back, enabling device...\n");
|
|
+ pci_set_power_state(pdev, PCI_D0);
|
|
+ pci_restore_state(pdev);
|
|
+ if (pci_enable_device(pdev))
|
|
+ return -1;
|
|
+ pci_set_master(dev->pdev);
|
|
+
|
|
+ NV_INFO(dev, "POSTing device...\n");
|
|
+ ret = nouveau_run_vbios_init(dev);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ if (dev_priv->gart_info.type == NOUVEAU_GART_AGP) {
|
|
+ ret = nouveau_mem_init_agp(dev);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "error reinitialising AGP: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ NV_INFO(dev, "Reinitialising engines...\n");
|
|
+ engine->instmem.resume(dev);
|
|
+ engine->mc.init(dev);
|
|
+ engine->timer.init(dev);
|
|
+ engine->fb.init(dev);
|
|
+ engine->graph.init(dev);
|
|
+ engine->fifo.init(dev);
|
|
+
|
|
+ NV_INFO(dev, "Restoring GPU objects...\n");
|
|
+ nouveau_gpuobj_resume(dev);
|
|
+
|
|
+ nouveau_irq_postinstall(dev);
|
|
+
|
|
+ /* Re-write SKIPS, they'll have been lost over the suspend */
|
|
+ if (nouveau_vram_pushbuf) {
|
|
+ struct nouveau_channel *chan;
|
|
+ int j;
|
|
+
|
|
+ for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
|
|
+ chan = dev_priv->fifos[i];
|
|
+ if (!chan || !chan->pushbuf_bo)
|
|
+ continue;
|
|
+
|
|
+ for (j = 0; j < NOUVEAU_DMA_SKIPS; j++)
|
|
+ nouveau_bo_wr32(chan->pushbuf_bo, i, 0);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ NV_INFO(dev, "Restoring mode...\n");
|
|
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
|
+ struct nouveau_framebuffer *nouveau_fb;
|
|
+
|
|
+ nouveau_fb = nouveau_framebuffer(crtc->fb);
|
|
+ if (!nouveau_fb || !nouveau_fb->nvbo)
|
|
+ continue;
|
|
+
|
|
+ nouveau_bo_pin(nouveau_fb->nvbo, TTM_PL_FLAG_VRAM);
|
|
+ }
|
|
+
|
|
+ if (dev_priv->card_type < NV_50) {
|
|
+ nv04_display_restore(dev);
|
|
+ NVLockVgaCrtcs(dev, false);
|
|
+ } else
|
|
+ nv50_display_init(dev);
|
|
+
|
|
+ /* Force CLUT to get re-loaded during modeset */
|
|
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+
|
|
+ nv_crtc->lut.depth = 0;
|
|
+ }
|
|
+
|
|
+ acquire_console_sem();
|
|
+ fb_set_suspend(dev_priv->fbdev_info, 0);
|
|
+ release_console_sem();
|
|
+
|
|
+ nouveau_fbcon_zfill(dev);
|
|
+
|
|
+ drm_helper_resume_force_mode(dev);
|
|
+ dev_priv->fbdev_info->flags = fbdev_flags;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct drm_driver driver = {
|
|
+ .driver_features =
|
|
+ DRIVER_USE_AGP | DRIVER_PCI_DMA | DRIVER_SG |
|
|
+ DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM,
|
|
+ .load = nouveau_load,
|
|
+ .firstopen = nouveau_firstopen,
|
|
+ .lastclose = nouveau_lastclose,
|
|
+ .unload = nouveau_unload,
|
|
+ .preclose = nouveau_preclose,
|
|
+#if defined(CONFIG_DRM_NOUVEAU_DEBUG)
|
|
+ .debugfs_init = nouveau_debugfs_init,
|
|
+ .debugfs_cleanup = nouveau_debugfs_takedown,
|
|
+#endif
|
|
+ .irq_preinstall = nouveau_irq_preinstall,
|
|
+ .irq_postinstall = nouveau_irq_postinstall,
|
|
+ .irq_uninstall = nouveau_irq_uninstall,
|
|
+ .irq_handler = nouveau_irq_handler,
|
|
+ .reclaim_buffers = drm_core_reclaim_buffers,
|
|
+ .get_map_ofs = drm_core_get_map_ofs,
|
|
+ .get_reg_ofs = drm_core_get_reg_ofs,
|
|
+ .ioctls = nouveau_ioctls,
|
|
+ .fops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .open = drm_open,
|
|
+ .release = drm_release,
|
|
+ .unlocked_ioctl = drm_ioctl,
|
|
+ .mmap = nouveau_ttm_mmap,
|
|
+ .poll = drm_poll,
|
|
+ .fasync = drm_fasync,
|
|
+#if defined(CONFIG_COMPAT)
|
|
+ .compat_ioctl = nouveau_compat_ioctl,
|
|
+#endif
|
|
+ },
|
|
+ .pci_driver = {
|
|
+ .name = DRIVER_NAME,
|
|
+ .id_table = pciidlist,
|
|
+ .probe = nouveau_pci_probe,
|
|
+ .remove = nouveau_pci_remove,
|
|
+ .suspend = nouveau_pci_suspend,
|
|
+ .resume = nouveau_pci_resume
|
|
+ },
|
|
+
|
|
+ .gem_init_object = nouveau_gem_object_new,
|
|
+ .gem_free_object = nouveau_gem_object_del,
|
|
+
|
|
+ .name = DRIVER_NAME,
|
|
+ .desc = DRIVER_DESC,
|
|
+#ifdef GIT_REVISION
|
|
+ .date = GIT_REVISION,
|
|
+#else
|
|
+ .date = DRIVER_DATE,
|
|
+#endif
|
|
+ .major = DRIVER_MAJOR,
|
|
+ .minor = DRIVER_MINOR,
|
|
+ .patchlevel = DRIVER_PATCHLEVEL,
|
|
+};
|
|
+
|
|
+static int __init nouveau_init(void)
|
|
+{
|
|
+ driver.num_ioctls = nouveau_max_ioctl;
|
|
+
|
|
+ if (nouveau_modeset == -1) {
|
|
+#ifdef CONFIG_VGA_CONSOLE
|
|
+ if (vgacon_text_force())
|
|
+ nouveau_modeset = 0;
|
|
+ else
|
|
+#endif
|
|
+ nouveau_modeset = 1;
|
|
+ }
|
|
+
|
|
+ if (nouveau_modeset == 1)
|
|
+ driver.driver_features |= DRIVER_MODESET;
|
|
+
|
|
+ return drm_init(&driver);
|
|
+}
|
|
+
|
|
+static void __exit nouveau_exit(void)
|
|
+{
|
|
+ drm_exit(&driver);
|
|
+}
|
|
+
|
|
+module_init(nouveau_init);
|
|
+module_exit(nouveau_exit);
|
|
+
|
|
+MODULE_AUTHOR(DRIVER_AUTHOR);
|
|
+MODULE_DESCRIPTION(DRIVER_DESC);
|
|
+MODULE_LICENSE("GPL and additional rights");
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
|
|
new file mode 100644
|
|
index 0000000..5445cef
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
|
|
@@ -0,0 +1,1350 @@
|
|
+/*
|
|
+ * Copyright 2005 Stephane Marchesin.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the next
|
|
+ * paragraph) shall be included in all copies or substantial portions of the
|
|
+ * Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
+ * OTHER DEALINGS IN THE SOFTWARE.
|
|
+ */
|
|
+
|
|
+#ifndef __NOUVEAU_DRV_H__
|
|
+#define __NOUVEAU_DRV_H__
|
|
+
|
|
+#define DRIVER_AUTHOR "Stephane Marchesin"
|
|
+#define DRIVER_EMAIL "dri-devel@lists.sourceforge.net"
|
|
+
|
|
+#define DRIVER_NAME "nouveau"
|
|
+#define DRIVER_DESC "nVidia Riva/TNT/GeForce"
|
|
+#define DRIVER_DATE "20090420"
|
|
+
|
|
+#define DRIVER_MAJOR 0
|
|
+#define DRIVER_MINOR 0
|
|
+#define DRIVER_PATCHLEVEL 15
|
|
+
|
|
+#define NOUVEAU_FAMILY 0x0000FFFF
|
|
+#define NOUVEAU_FLAGS 0xFFFF0000
|
|
+
|
|
+#include "ttm/ttm_bo_api.h"
|
|
+#include "ttm/ttm_bo_driver.h"
|
|
+#include "ttm/ttm_placement.h"
|
|
+#include "ttm/ttm_memory.h"
|
|
+#include "ttm/ttm_module.h"
|
|
+
|
|
+struct nouveau_fpriv {
|
|
+ struct ttm_object_file *tfile;
|
|
+};
|
|
+
|
|
+#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT)
|
|
+
|
|
+#include "nouveau_drm.h"
|
|
+#include "nouveau_reg.h"
|
|
+#include "nouveau_bios.h"
|
|
+struct nouveau_grctx;
|
|
+
|
|
+#define MAX_NUM_DCB_ENTRIES 16
|
|
+
|
|
+#define NOUVEAU_MAX_CHANNEL_NR 128
|
|
+#define NOUVEAU_MAX_TILE_NR 15
|
|
+
|
|
+#define NV50_VM_MAX_VRAM (2*1024*1024*1024ULL)
|
|
+#define NV50_VM_BLOCK (512*1024*1024ULL)
|
|
+#define NV50_VM_VRAM_NR (NV50_VM_MAX_VRAM / NV50_VM_BLOCK)
|
|
+
|
|
+struct nouveau_tile_reg {
|
|
+ struct nouveau_fence *fence;
|
|
+ uint32_t addr;
|
|
+ uint32_t size;
|
|
+ bool used;
|
|
+};
|
|
+
|
|
+struct nouveau_bo {
|
|
+ struct ttm_buffer_object bo;
|
|
+ struct ttm_placement placement;
|
|
+ u32 placements[3];
|
|
+ struct ttm_bo_kmap_obj kmap;
|
|
+ struct list_head head;
|
|
+
|
|
+ /* protected by ttm_bo_reserve() */
|
|
+ struct drm_file *reserved_by;
|
|
+ struct list_head entry;
|
|
+ int pbbo_index;
|
|
+
|
|
+ struct nouveau_channel *channel;
|
|
+
|
|
+ bool mappable;
|
|
+ bool no_vm;
|
|
+
|
|
+ uint32_t tile_mode;
|
|
+ uint32_t tile_flags;
|
|
+ struct nouveau_tile_reg *tile;
|
|
+
|
|
+ struct drm_gem_object *gem;
|
|
+ struct drm_file *cpu_filp;
|
|
+ int pin_refcnt;
|
|
+};
|
|
+
|
|
+static inline struct nouveau_bo *
|
|
+nouveau_bo(struct ttm_buffer_object *bo)
|
|
+{
|
|
+ return container_of(bo, struct nouveau_bo, bo);
|
|
+}
|
|
+
|
|
+static inline struct nouveau_bo *
|
|
+nouveau_gem_object(struct drm_gem_object *gem)
|
|
+{
|
|
+ return gem ? gem->driver_private : NULL;
|
|
+}
|
|
+
|
|
+/* TODO: submit equivalent to TTM generic API upstream? */
|
|
+static inline void __iomem *
|
|
+nvbo_kmap_obj_iovirtual(struct nouveau_bo *nvbo)
|
|
+{
|
|
+ bool is_iomem;
|
|
+ void __iomem *ioptr = (void __force __iomem *)ttm_kmap_obj_virtual(
|
|
+ &nvbo->kmap, &is_iomem);
|
|
+ WARN_ON_ONCE(ioptr && !is_iomem);
|
|
+ return ioptr;
|
|
+}
|
|
+
|
|
+struct mem_block {
|
|
+ struct mem_block *next;
|
|
+ struct mem_block *prev;
|
|
+ uint64_t start;
|
|
+ uint64_t size;
|
|
+ struct drm_file *file_priv; /* NULL: free, -1: heap, other: real files */
|
|
+};
|
|
+
|
|
+enum nouveau_flags {
|
|
+ NV_NFORCE = 0x10000000,
|
|
+ NV_NFORCE2 = 0x20000000
|
|
+};
|
|
+
|
|
+#define NVOBJ_ENGINE_SW 0
|
|
+#define NVOBJ_ENGINE_GR 1
|
|
+#define NVOBJ_ENGINE_DISPLAY 2
|
|
+#define NVOBJ_ENGINE_INT 0xdeadbeef
|
|
+
|
|
+#define NVOBJ_FLAG_ALLOW_NO_REFS (1 << 0)
|
|
+#define NVOBJ_FLAG_ZERO_ALLOC (1 << 1)
|
|
+#define NVOBJ_FLAG_ZERO_FREE (1 << 2)
|
|
+#define NVOBJ_FLAG_FAKE (1 << 3)
|
|
+struct nouveau_gpuobj {
|
|
+ struct list_head list;
|
|
+
|
|
+ struct nouveau_channel *im_channel;
|
|
+ struct mem_block *im_pramin;
|
|
+ struct nouveau_bo *im_backing;
|
|
+ uint32_t im_backing_start;
|
|
+ uint32_t *im_backing_suspend;
|
|
+ int im_bound;
|
|
+
|
|
+ uint32_t flags;
|
|
+ int refcount;
|
|
+
|
|
+ uint32_t engine;
|
|
+ uint32_t class;
|
|
+
|
|
+ void (*dtor)(struct drm_device *, struct nouveau_gpuobj *);
|
|
+ void *priv;
|
|
+};
|
|
+
|
|
+struct nouveau_gpuobj_ref {
|
|
+ struct list_head list;
|
|
+
|
|
+ struct nouveau_gpuobj *gpuobj;
|
|
+ uint32_t instance;
|
|
+
|
|
+ struct nouveau_channel *channel;
|
|
+ int handle;
|
|
+};
|
|
+
|
|
+struct nouveau_channel {
|
|
+ struct drm_device *dev;
|
|
+ int id;
|
|
+
|
|
+ /* owner of this fifo */
|
|
+ struct drm_file *file_priv;
|
|
+ /* mapping of the fifo itself */
|
|
+ struct drm_local_map *map;
|
|
+
|
|
+ /* mapping of the regs controling the fifo */
|
|
+ void __iomem *user;
|
|
+ uint32_t user_get;
|
|
+ uint32_t user_put;
|
|
+
|
|
+ /* Fencing */
|
|
+ struct {
|
|
+ /* lock protects the pending list only */
|
|
+ spinlock_t lock;
|
|
+ struct list_head pending;
|
|
+ uint32_t sequence;
|
|
+ uint32_t sequence_ack;
|
|
+ uint32_t last_sequence_irq;
|
|
+ } fence;
|
|
+
|
|
+ /* DMA push buffer */
|
|
+ struct nouveau_gpuobj_ref *pushbuf;
|
|
+ struct nouveau_bo *pushbuf_bo;
|
|
+ uint32_t pushbuf_base;
|
|
+
|
|
+ /* Notifier memory */
|
|
+ struct nouveau_bo *notifier_bo;
|
|
+ struct mem_block *notifier_heap;
|
|
+
|
|
+ /* PFIFO context */
|
|
+ struct nouveau_gpuobj_ref *ramfc;
|
|
+ struct nouveau_gpuobj_ref *cache;
|
|
+
|
|
+ /* PGRAPH context */
|
|
+ /* XXX may be merge 2 pointers as private data ??? */
|
|
+ struct nouveau_gpuobj_ref *ramin_grctx;
|
|
+ void *pgraph_ctx;
|
|
+
|
|
+ /* NV50 VM */
|
|
+ struct nouveau_gpuobj *vm_pd;
|
|
+ struct nouveau_gpuobj_ref *vm_gart_pt;
|
|
+ struct nouveau_gpuobj_ref *vm_vram_pt[NV50_VM_VRAM_NR];
|
|
+
|
|
+ /* Objects */
|
|
+ struct nouveau_gpuobj_ref *ramin; /* Private instmem */
|
|
+ struct mem_block *ramin_heap; /* Private PRAMIN heap */
|
|
+ struct nouveau_gpuobj_ref *ramht; /* Hash table */
|
|
+ struct list_head ramht_refs; /* Objects referenced by RAMHT */
|
|
+
|
|
+ /* GPU object info for stuff used in-kernel (mm_enabled) */
|
|
+ uint32_t m2mf_ntfy;
|
|
+ uint32_t vram_handle;
|
|
+ uint32_t gart_handle;
|
|
+ bool accel_done;
|
|
+
|
|
+ /* Push buffer state (only for drm's channel on !mm_enabled) */
|
|
+ struct {
|
|
+ int max;
|
|
+ int free;
|
|
+ int cur;
|
|
+ int put;
|
|
+ /* access via pushbuf_bo */
|
|
+ } dma;
|
|
+
|
|
+ uint32_t sw_subchannel[8];
|
|
+
|
|
+ struct {
|
|
+ struct nouveau_gpuobj *vblsem;
|
|
+ uint32_t vblsem_offset;
|
|
+ uint32_t vblsem_rval;
|
|
+ struct list_head vbl_wait;
|
|
+ } nvsw;
|
|
+
|
|
+ struct {
|
|
+ bool active;
|
|
+ char name[32];
|
|
+ struct drm_info_list info;
|
|
+ } debugfs;
|
|
+};
|
|
+
|
|
+struct nouveau_instmem_engine {
|
|
+ void *priv;
|
|
+
|
|
+ int (*init)(struct drm_device *dev);
|
|
+ void (*takedown)(struct drm_device *dev);
|
|
+ int (*suspend)(struct drm_device *dev);
|
|
+ void (*resume)(struct drm_device *dev);
|
|
+
|
|
+ int (*populate)(struct drm_device *, struct nouveau_gpuobj *,
|
|
+ uint32_t *size);
|
|
+ void (*clear)(struct drm_device *, struct nouveau_gpuobj *);
|
|
+ int (*bind)(struct drm_device *, struct nouveau_gpuobj *);
|
|
+ int (*unbind)(struct drm_device *, struct nouveau_gpuobj *);
|
|
+ void (*prepare_access)(struct drm_device *, bool write);
|
|
+ void (*finish_access)(struct drm_device *);
|
|
+};
|
|
+
|
|
+struct nouveau_mc_engine {
|
|
+ int (*init)(struct drm_device *dev);
|
|
+ void (*takedown)(struct drm_device *dev);
|
|
+};
|
|
+
|
|
+struct nouveau_timer_engine {
|
|
+ int (*init)(struct drm_device *dev);
|
|
+ void (*takedown)(struct drm_device *dev);
|
|
+ uint64_t (*read)(struct drm_device *dev);
|
|
+};
|
|
+
|
|
+struct nouveau_fb_engine {
|
|
+ int num_tiles;
|
|
+
|
|
+ int (*init)(struct drm_device *dev);
|
|
+ void (*takedown)(struct drm_device *dev);
|
|
+
|
|
+ void (*set_region_tiling)(struct drm_device *dev, int i, uint32_t addr,
|
|
+ uint32_t size, uint32_t pitch);
|
|
+};
|
|
+
|
|
+struct nouveau_fifo_engine {
|
|
+ void *priv;
|
|
+
|
|
+ int channels;
|
|
+
|
|
+ int (*init)(struct drm_device *);
|
|
+ void (*takedown)(struct drm_device *);
|
|
+
|
|
+ void (*disable)(struct drm_device *);
|
|
+ void (*enable)(struct drm_device *);
|
|
+ bool (*reassign)(struct drm_device *, bool enable);
|
|
+ bool (*cache_flush)(struct drm_device *dev);
|
|
+ bool (*cache_pull)(struct drm_device *dev, bool enable);
|
|
+
|
|
+ int (*channel_id)(struct drm_device *);
|
|
+
|
|
+ int (*create_context)(struct nouveau_channel *);
|
|
+ void (*destroy_context)(struct nouveau_channel *);
|
|
+ int (*load_context)(struct nouveau_channel *);
|
|
+ int (*unload_context)(struct drm_device *);
|
|
+};
|
|
+
|
|
+struct nouveau_pgraph_object_method {
|
|
+ int id;
|
|
+ int (*exec)(struct nouveau_channel *chan, int grclass, int mthd,
|
|
+ uint32_t data);
|
|
+};
|
|
+
|
|
+struct nouveau_pgraph_object_class {
|
|
+ int id;
|
|
+ bool software;
|
|
+ struct nouveau_pgraph_object_method *methods;
|
|
+};
|
|
+
|
|
+struct nouveau_pgraph_engine {
|
|
+ struct nouveau_pgraph_object_class *grclass;
|
|
+ bool accel_blocked;
|
|
+ void *ctxprog;
|
|
+ void *ctxvals;
|
|
+ int grctx_size;
|
|
+
|
|
+ int (*init)(struct drm_device *);
|
|
+ void (*takedown)(struct drm_device *);
|
|
+
|
|
+ void (*fifo_access)(struct drm_device *, bool);
|
|
+
|
|
+ struct nouveau_channel *(*channel)(struct drm_device *);
|
|
+ int (*create_context)(struct nouveau_channel *);
|
|
+ void (*destroy_context)(struct nouveau_channel *);
|
|
+ int (*load_context)(struct nouveau_channel *);
|
|
+ int (*unload_context)(struct drm_device *);
|
|
+
|
|
+ void (*set_region_tiling)(struct drm_device *dev, int i, uint32_t addr,
|
|
+ uint32_t size, uint32_t pitch);
|
|
+};
|
|
+
|
|
+struct nouveau_engine {
|
|
+ struct nouveau_instmem_engine instmem;
|
|
+ struct nouveau_mc_engine mc;
|
|
+ struct nouveau_timer_engine timer;
|
|
+ struct nouveau_fb_engine fb;
|
|
+ struct nouveau_pgraph_engine graph;
|
|
+ struct nouveau_fifo_engine fifo;
|
|
+};
|
|
+
|
|
+struct nouveau_pll_vals {
|
|
+ union {
|
|
+ struct {
|
|
+#ifdef __BIG_ENDIAN
|
|
+ uint8_t N1, M1, N2, M2;
|
|
+#else
|
|
+ uint8_t M1, N1, M2, N2;
|
|
+#endif
|
|
+ };
|
|
+ struct {
|
|
+ uint16_t NM1, NM2;
|
|
+ } __attribute__((packed));
|
|
+ };
|
|
+ int log2P;
|
|
+
|
|
+ int refclk;
|
|
+};
|
|
+
|
|
+enum nv04_fp_display_regs {
|
|
+ FP_DISPLAY_END,
|
|
+ FP_TOTAL,
|
|
+ FP_CRTC,
|
|
+ FP_SYNC_START,
|
|
+ FP_SYNC_END,
|
|
+ FP_VALID_START,
|
|
+ FP_VALID_END
|
|
+};
|
|
+
|
|
+struct nv04_crtc_reg {
|
|
+ unsigned char MiscOutReg; /* */
|
|
+ uint8_t CRTC[0x9f];
|
|
+ uint8_t CR58[0x10];
|
|
+ uint8_t Sequencer[5];
|
|
+ uint8_t Graphics[9];
|
|
+ uint8_t Attribute[21];
|
|
+ unsigned char DAC[768]; /* Internal Colorlookuptable */
|
|
+
|
|
+ /* PCRTC regs */
|
|
+ uint32_t fb_start;
|
|
+ uint32_t crtc_cfg;
|
|
+ uint32_t cursor_cfg;
|
|
+ uint32_t gpio_ext;
|
|
+ uint32_t crtc_830;
|
|
+ uint32_t crtc_834;
|
|
+ uint32_t crtc_850;
|
|
+ uint32_t crtc_eng_ctrl;
|
|
+
|
|
+ /* PRAMDAC regs */
|
|
+ uint32_t nv10_cursync;
|
|
+ struct nouveau_pll_vals pllvals;
|
|
+ uint32_t ramdac_gen_ctrl;
|
|
+ uint32_t ramdac_630;
|
|
+ uint32_t ramdac_634;
|
|
+ uint32_t tv_setup;
|
|
+ uint32_t tv_vtotal;
|
|
+ uint32_t tv_vskew;
|
|
+ uint32_t tv_vsync_delay;
|
|
+ uint32_t tv_htotal;
|
|
+ uint32_t tv_hskew;
|
|
+ uint32_t tv_hsync_delay;
|
|
+ uint32_t tv_hsync_delay2;
|
|
+ uint32_t fp_horiz_regs[7];
|
|
+ uint32_t fp_vert_regs[7];
|
|
+ uint32_t dither;
|
|
+ uint32_t fp_control;
|
|
+ uint32_t dither_regs[6];
|
|
+ uint32_t fp_debug_0;
|
|
+ uint32_t fp_debug_1;
|
|
+ uint32_t fp_debug_2;
|
|
+ uint32_t fp_margin_color;
|
|
+ uint32_t ramdac_8c0;
|
|
+ uint32_t ramdac_a20;
|
|
+ uint32_t ramdac_a24;
|
|
+ uint32_t ramdac_a34;
|
|
+ uint32_t ctv_regs[38];
|
|
+};
|
|
+
|
|
+struct nv04_output_reg {
|
|
+ uint32_t output;
|
|
+ int head;
|
|
+};
|
|
+
|
|
+struct nv04_mode_state {
|
|
+ uint32_t bpp;
|
|
+ uint32_t width;
|
|
+ uint32_t height;
|
|
+ uint32_t interlace;
|
|
+ uint32_t repaint0;
|
|
+ uint32_t repaint1;
|
|
+ uint32_t screen;
|
|
+ uint32_t scale;
|
|
+ uint32_t dither;
|
|
+ uint32_t extra;
|
|
+ uint32_t fifo;
|
|
+ uint32_t pixel;
|
|
+ uint32_t horiz;
|
|
+ int arbitration0;
|
|
+ int arbitration1;
|
|
+ uint32_t pll;
|
|
+ uint32_t pllB;
|
|
+ uint32_t vpll;
|
|
+ uint32_t vpll2;
|
|
+ uint32_t vpllB;
|
|
+ uint32_t vpll2B;
|
|
+ uint32_t pllsel;
|
|
+ uint32_t sel_clk;
|
|
+ uint32_t general;
|
|
+ uint32_t crtcOwner;
|
|
+ uint32_t head;
|
|
+ uint32_t head2;
|
|
+ uint32_t cursorConfig;
|
|
+ uint32_t cursor0;
|
|
+ uint32_t cursor1;
|
|
+ uint32_t cursor2;
|
|
+ uint32_t timingH;
|
|
+ uint32_t timingV;
|
|
+ uint32_t displayV;
|
|
+ uint32_t crtcSync;
|
|
+
|
|
+ struct nv04_crtc_reg crtc_reg[2];
|
|
+};
|
|
+
|
|
+enum nouveau_card_type {
|
|
+ NV_04 = 0x00,
|
|
+ NV_10 = 0x10,
|
|
+ NV_20 = 0x20,
|
|
+ NV_30 = 0x30,
|
|
+ NV_40 = 0x40,
|
|
+ NV_50 = 0x50,
|
|
+};
|
|
+
|
|
+struct drm_nouveau_private {
|
|
+ struct drm_device *dev;
|
|
+ enum {
|
|
+ NOUVEAU_CARD_INIT_DOWN,
|
|
+ NOUVEAU_CARD_INIT_DONE,
|
|
+ NOUVEAU_CARD_INIT_FAILED
|
|
+ } init_state;
|
|
+
|
|
+ /* the card type, takes NV_* as values */
|
|
+ enum nouveau_card_type card_type;
|
|
+ /* exact chipset, derived from NV_PMC_BOOT_0 */
|
|
+ int chipset;
|
|
+ int flags;
|
|
+
|
|
+ void __iomem *mmio;
|
|
+ void __iomem *ramin;
|
|
+ uint32_t ramin_size;
|
|
+
|
|
+ struct nouveau_bo *vga_ram;
|
|
+
|
|
+ struct workqueue_struct *wq;
|
|
+ struct work_struct irq_work;
|
|
+
|
|
+ struct list_head vbl_waiting;
|
|
+
|
|
+ struct {
|
|
+ struct ttm_global_reference mem_global_ref;
|
|
+ struct ttm_bo_global_ref bo_global_ref;
|
|
+ struct ttm_bo_device bdev;
|
|
+ spinlock_t bo_list_lock;
|
|
+ struct list_head bo_list;
|
|
+ atomic_t validate_sequence;
|
|
+ } ttm;
|
|
+
|
|
+ struct fb_info *fbdev_info;
|
|
+
|
|
+ int fifo_alloc_count;
|
|
+ struct nouveau_channel *fifos[NOUVEAU_MAX_CHANNEL_NR];
|
|
+
|
|
+ struct nouveau_engine engine;
|
|
+ struct nouveau_channel *channel;
|
|
+
|
|
+ /* RAMIN configuration, RAMFC, RAMHT and RAMRO offsets */
|
|
+ struct nouveau_gpuobj *ramht;
|
|
+ uint32_t ramin_rsvd_vram;
|
|
+ uint32_t ramht_offset;
|
|
+ uint32_t ramht_size;
|
|
+ uint32_t ramht_bits;
|
|
+ uint32_t ramfc_offset;
|
|
+ uint32_t ramfc_size;
|
|
+ uint32_t ramro_offset;
|
|
+ uint32_t ramro_size;
|
|
+
|
|
+ /* base physical adresses */
|
|
+ uint64_t fb_phys;
|
|
+ uint64_t fb_available_size;
|
|
+ uint64_t fb_mappable_pages;
|
|
+ uint64_t fb_aper_free;
|
|
+
|
|
+ struct {
|
|
+ enum {
|
|
+ NOUVEAU_GART_NONE = 0,
|
|
+ NOUVEAU_GART_AGP,
|
|
+ NOUVEAU_GART_SGDMA
|
|
+ } type;
|
|
+ uint64_t aper_base;
|
|
+ uint64_t aper_size;
|
|
+ uint64_t aper_free;
|
|
+
|
|
+ struct nouveau_gpuobj *sg_ctxdma;
|
|
+ struct page *sg_dummy_page;
|
|
+ dma_addr_t sg_dummy_bus;
|
|
+
|
|
+ /* nottm hack */
|
|
+ struct drm_ttm_backend *sg_be;
|
|
+ unsigned long sg_handle;
|
|
+ } gart_info;
|
|
+
|
|
+ /* nv10-nv40 tiling regions */
|
|
+ struct {
|
|
+ struct nouveau_tile_reg reg[NOUVEAU_MAX_TILE_NR];
|
|
+ spinlock_t lock;
|
|
+ } tile;
|
|
+
|
|
+ /* G8x/G9x virtual address space */
|
|
+ uint64_t vm_gart_base;
|
|
+ uint64_t vm_gart_size;
|
|
+ uint64_t vm_vram_base;
|
|
+ uint64_t vm_vram_size;
|
|
+ uint64_t vm_end;
|
|
+ struct nouveau_gpuobj *vm_vram_pt[NV50_VM_VRAM_NR];
|
|
+ int vm_vram_pt_nr;
|
|
+
|
|
+ /* the mtrr covering the FB */
|
|
+ int fb_mtrr;
|
|
+
|
|
+ struct mem_block *ramin_heap;
|
|
+
|
|
+ /* context table pointed to be NV_PGRAPH_CHANNEL_CTX_TABLE (0x400780) */
|
|
+ uint32_t ctx_table_size;
|
|
+ struct nouveau_gpuobj_ref *ctx_table;
|
|
+
|
|
+ struct list_head gpuobj_list;
|
|
+
|
|
+ struct nvbios VBIOS;
|
|
+ struct nouveau_bios_info *vbios;
|
|
+
|
|
+ struct nv04_mode_state mode_reg;
|
|
+ struct nv04_mode_state saved_reg;
|
|
+ uint32_t saved_vga_font[4][16384];
|
|
+ uint32_t crtc_owner;
|
|
+ uint32_t dac_users[4];
|
|
+
|
|
+ struct nouveau_suspend_resume {
|
|
+ uint32_t fifo_mode;
|
|
+ uint32_t graph_ctx_control;
|
|
+ uint32_t graph_state;
|
|
+ uint32_t *ramin_copy;
|
|
+ uint64_t ramin_size;
|
|
+ } susres;
|
|
+
|
|
+ struct backlight_device *backlight;
|
|
+ bool acpi_dsm;
|
|
+
|
|
+ struct nouveau_channel *evo;
|
|
+
|
|
+ struct {
|
|
+ struct dentry *channel_root;
|
|
+ } debugfs;
|
|
+};
|
|
+
|
|
+static inline struct drm_nouveau_private *
|
|
+nouveau_bdev(struct ttm_bo_device *bd)
|
|
+{
|
|
+ return container_of(bd, struct drm_nouveau_private, ttm.bdev);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+nouveau_bo_ref(struct nouveau_bo *ref, struct nouveau_bo **pnvbo)
|
|
+{
|
|
+ struct nouveau_bo *prev;
|
|
+
|
|
+ if (!pnvbo)
|
|
+ return -EINVAL;
|
|
+ prev = *pnvbo;
|
|
+
|
|
+ *pnvbo = ref ? nouveau_bo(ttm_bo_reference(&ref->bo)) : NULL;
|
|
+ if (prev) {
|
|
+ struct ttm_buffer_object *bo = &prev->bo;
|
|
+
|
|
+ ttm_bo_unref(&bo);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#define NOUVEAU_CHECK_INITIALISED_WITH_RETURN do { \
|
|
+ struct drm_nouveau_private *nv = dev->dev_private; \
|
|
+ if (nv->init_state != NOUVEAU_CARD_INIT_DONE) { \
|
|
+ NV_ERROR(dev, "called without init\n"); \
|
|
+ return -EINVAL; \
|
|
+ } \
|
|
+} while (0)
|
|
+
|
|
+#define NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(id, cl, ch) do { \
|
|
+ struct drm_nouveau_private *nv = dev->dev_private; \
|
|
+ if (!nouveau_channel_owner(dev, (cl), (id))) { \
|
|
+ NV_ERROR(dev, "pid %d doesn't own channel %d\n", \
|
|
+ DRM_CURRENTPID, (id)); \
|
|
+ return -EPERM; \
|
|
+ } \
|
|
+ (ch) = nv->fifos[(id)]; \
|
|
+} while (0)
|
|
+
|
|
+/* nouveau_drv.c */
|
|
+extern int nouveau_noagp;
|
|
+extern int nouveau_duallink;
|
|
+extern int nouveau_uscript_lvds;
|
|
+extern int nouveau_uscript_tmds;
|
|
+extern int nouveau_vram_pushbuf;
|
|
+extern int nouveau_vram_notify;
|
|
+extern int nouveau_fbpercrtc;
|
|
+extern char *nouveau_tv_norm;
|
|
+extern int nouveau_reg_debug;
|
|
+extern char *nouveau_vbios;
|
|
+extern int nouveau_ctxfw;
|
|
+extern int nouveau_ignorelid;
|
|
+extern int nouveau_nofbaccel;
|
|
+extern int nouveau_noaccel;
|
|
+
|
|
+/* nouveau_state.c */
|
|
+extern void nouveau_preclose(struct drm_device *dev, struct drm_file *);
|
|
+extern int nouveau_load(struct drm_device *, unsigned long flags);
|
|
+extern int nouveau_firstopen(struct drm_device *);
|
|
+extern void nouveau_lastclose(struct drm_device *);
|
|
+extern int nouveau_unload(struct drm_device *);
|
|
+extern int nouveau_ioctl_getparam(struct drm_device *, void *data,
|
|
+ struct drm_file *);
|
|
+extern int nouveau_ioctl_setparam(struct drm_device *, void *data,
|
|
+ struct drm_file *);
|
|
+extern bool nouveau_wait_until(struct drm_device *, uint64_t timeout,
|
|
+ uint32_t reg, uint32_t mask, uint32_t val);
|
|
+extern bool nouveau_wait_for_idle(struct drm_device *);
|
|
+extern int nouveau_card_init(struct drm_device *);
|
|
+extern int nouveau_ioctl_card_init(struct drm_device *, void *data,
|
|
+ struct drm_file *);
|
|
+extern int nouveau_ioctl_suspend(struct drm_device *, void *data,
|
|
+ struct drm_file *);
|
|
+extern int nouveau_ioctl_resume(struct drm_device *, void *data,
|
|
+ struct drm_file *);
|
|
+
|
|
+/* nouveau_mem.c */
|
|
+extern int nouveau_mem_init_heap(struct mem_block **, uint64_t start,
|
|
+ uint64_t size);
|
|
+extern struct mem_block *nouveau_mem_alloc_block(struct mem_block *,
|
|
+ uint64_t size, int align2,
|
|
+ struct drm_file *, int tail);
|
|
+extern void nouveau_mem_takedown(struct mem_block **heap);
|
|
+extern void nouveau_mem_free_block(struct mem_block *);
|
|
+extern uint64_t nouveau_mem_fb_amount(struct drm_device *);
|
|
+extern void nouveau_mem_release(struct drm_file *, struct mem_block *heap);
|
|
+extern int nouveau_mem_init(struct drm_device *);
|
|
+extern int nouveau_mem_init_agp(struct drm_device *);
|
|
+extern void nouveau_mem_close(struct drm_device *);
|
|
+extern struct nouveau_tile_reg *nv10_mem_set_tiling(struct drm_device *dev,
|
|
+ uint32_t addr,
|
|
+ uint32_t size,
|
|
+ uint32_t pitch);
|
|
+extern void nv10_mem_expire_tiling(struct drm_device *dev,
|
|
+ struct nouveau_tile_reg *tile,
|
|
+ struct nouveau_fence *fence);
|
|
+extern int nv50_mem_vm_bind_linear(struct drm_device *, uint64_t virt,
|
|
+ uint32_t size, uint32_t flags,
|
|
+ uint64_t phys);
|
|
+extern void nv50_mem_vm_unbind(struct drm_device *, uint64_t virt,
|
|
+ uint32_t size);
|
|
+
|
|
+/* nouveau_notifier.c */
|
|
+extern int nouveau_notifier_init_channel(struct nouveau_channel *);
|
|
+extern void nouveau_notifier_takedown_channel(struct nouveau_channel *);
|
|
+extern int nouveau_notifier_alloc(struct nouveau_channel *, uint32_t handle,
|
|
+ int cout, uint32_t *offset);
|
|
+extern int nouveau_notifier_offset(struct nouveau_gpuobj *, uint32_t *);
|
|
+extern int nouveau_ioctl_notifier_alloc(struct drm_device *, void *data,
|
|
+ struct drm_file *);
|
|
+extern int nouveau_ioctl_notifier_free(struct drm_device *, void *data,
|
|
+ struct drm_file *);
|
|
+
|
|
+/* nouveau_channel.c */
|
|
+extern struct drm_ioctl_desc nouveau_ioctls[];
|
|
+extern int nouveau_max_ioctl;
|
|
+extern void nouveau_channel_cleanup(struct drm_device *, struct drm_file *);
|
|
+extern int nouveau_channel_owner(struct drm_device *, struct drm_file *,
|
|
+ int channel);
|
|
+extern int nouveau_channel_alloc(struct drm_device *dev,
|
|
+ struct nouveau_channel **chan,
|
|
+ struct drm_file *file_priv,
|
|
+ uint32_t fb_ctxdma, uint32_t tt_ctxdma);
|
|
+extern void nouveau_channel_free(struct nouveau_channel *);
|
|
+
|
|
+/* nouveau_object.c */
|
|
+extern int nouveau_gpuobj_early_init(struct drm_device *);
|
|
+extern int nouveau_gpuobj_init(struct drm_device *);
|
|
+extern void nouveau_gpuobj_takedown(struct drm_device *);
|
|
+extern void nouveau_gpuobj_late_takedown(struct drm_device *);
|
|
+extern int nouveau_gpuobj_suspend(struct drm_device *dev);
|
|
+extern void nouveau_gpuobj_suspend_cleanup(struct drm_device *dev);
|
|
+extern void nouveau_gpuobj_resume(struct drm_device *dev);
|
|
+extern int nouveau_gpuobj_channel_init(struct nouveau_channel *,
|
|
+ uint32_t vram_h, uint32_t tt_h);
|
|
+extern void nouveau_gpuobj_channel_takedown(struct nouveau_channel *);
|
|
+extern int nouveau_gpuobj_new(struct drm_device *, struct nouveau_channel *,
|
|
+ uint32_t size, int align, uint32_t flags,
|
|
+ struct nouveau_gpuobj **);
|
|
+extern int nouveau_gpuobj_del(struct drm_device *, struct nouveau_gpuobj **);
|
|
+extern int nouveau_gpuobj_ref_add(struct drm_device *, struct nouveau_channel *,
|
|
+ uint32_t handle, struct nouveau_gpuobj *,
|
|
+ struct nouveau_gpuobj_ref **);
|
|
+extern int nouveau_gpuobj_ref_del(struct drm_device *,
|
|
+ struct nouveau_gpuobj_ref **);
|
|
+extern int nouveau_gpuobj_ref_find(struct nouveau_channel *, uint32_t handle,
|
|
+ struct nouveau_gpuobj_ref **ref_ret);
|
|
+extern int nouveau_gpuobj_new_ref(struct drm_device *,
|
|
+ struct nouveau_channel *alloc_chan,
|
|
+ struct nouveau_channel *ref_chan,
|
|
+ uint32_t handle, uint32_t size, int align,
|
|
+ uint32_t flags, struct nouveau_gpuobj_ref **);
|
|
+extern int nouveau_gpuobj_new_fake(struct drm_device *,
|
|
+ uint32_t p_offset, uint32_t b_offset,
|
|
+ uint32_t size, uint32_t flags,
|
|
+ struct nouveau_gpuobj **,
|
|
+ struct nouveau_gpuobj_ref**);
|
|
+extern int nouveau_gpuobj_dma_new(struct nouveau_channel *, int class,
|
|
+ uint64_t offset, uint64_t size, int access,
|
|
+ int target, struct nouveau_gpuobj **);
|
|
+extern int nouveau_gpuobj_gart_dma_new(struct nouveau_channel *,
|
|
+ uint64_t offset, uint64_t size,
|
|
+ int access, struct nouveau_gpuobj **,
|
|
+ uint32_t *o_ret);
|
|
+extern int nouveau_gpuobj_gr_new(struct nouveau_channel *, int class,
|
|
+ struct nouveau_gpuobj **);
|
|
+extern int nouveau_gpuobj_sw_new(struct nouveau_channel *, int class,
|
|
+ struct nouveau_gpuobj **);
|
|
+extern int nouveau_ioctl_grobj_alloc(struct drm_device *, void *data,
|
|
+ struct drm_file *);
|
|
+extern int nouveau_ioctl_gpuobj_free(struct drm_device *, void *data,
|
|
+ struct drm_file *);
|
|
+
|
|
+/* nouveau_irq.c */
|
|
+extern irqreturn_t nouveau_irq_handler(DRM_IRQ_ARGS);
|
|
+extern void nouveau_irq_preinstall(struct drm_device *);
|
|
+extern int nouveau_irq_postinstall(struct drm_device *);
|
|
+extern void nouveau_irq_uninstall(struct drm_device *);
|
|
+
|
|
+/* nouveau_sgdma.c */
|
|
+extern int nouveau_sgdma_init(struct drm_device *);
|
|
+extern void nouveau_sgdma_takedown(struct drm_device *);
|
|
+extern int nouveau_sgdma_get_page(struct drm_device *, uint32_t offset,
|
|
+ uint32_t *page);
|
|
+extern struct ttm_backend *nouveau_sgdma_init_ttm(struct drm_device *);
|
|
+
|
|
+/* nouveau_debugfs.c */
|
|
+#if defined(CONFIG_DRM_NOUVEAU_DEBUG)
|
|
+extern int nouveau_debugfs_init(struct drm_minor *);
|
|
+extern void nouveau_debugfs_takedown(struct drm_minor *);
|
|
+extern int nouveau_debugfs_channel_init(struct nouveau_channel *);
|
|
+extern void nouveau_debugfs_channel_fini(struct nouveau_channel *);
|
|
+#else
|
|
+static inline int
|
|
+nouveau_debugfs_init(struct drm_minor *minor)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline void nouveau_debugfs_takedown(struct drm_minor *minor)
|
|
+{
|
|
+}
|
|
+
|
|
+static inline int
|
|
+nouveau_debugfs_channel_init(struct nouveau_channel *chan)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline void
|
|
+nouveau_debugfs_channel_fini(struct nouveau_channel *chan)
|
|
+{
|
|
+}
|
|
+#endif
|
|
+
|
|
+/* nouveau_dma.c */
|
|
+extern void nouveau_dma_pre_init(struct nouveau_channel *);
|
|
+extern int nouveau_dma_init(struct nouveau_channel *);
|
|
+extern int nouveau_dma_wait(struct nouveau_channel *, int size);
|
|
+
|
|
+/* nouveau_acpi.c */
|
|
+#ifdef CONFIG_ACPI
|
|
+extern int nouveau_hybrid_setup(struct drm_device *dev);
|
|
+extern bool nouveau_dsm_probe(struct drm_device *dev);
|
|
+#else
|
|
+static inline int nouveau_hybrid_setup(struct drm_device *dev)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+static inline bool nouveau_dsm_probe(struct drm_device *dev)
|
|
+{
|
|
+ return false;
|
|
+}
|
|
+#endif
|
|
+
|
|
+/* nouveau_backlight.c */
|
|
+#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
|
|
+extern int nouveau_backlight_init(struct drm_device *);
|
|
+extern void nouveau_backlight_exit(struct drm_device *);
|
|
+#else
|
|
+static inline int nouveau_backlight_init(struct drm_device *dev)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline void nouveau_backlight_exit(struct drm_device *dev) { }
|
|
+#endif
|
|
+
|
|
+/* nouveau_bios.c */
|
|
+extern int nouveau_bios_init(struct drm_device *);
|
|
+extern void nouveau_bios_takedown(struct drm_device *dev);
|
|
+extern int nouveau_run_vbios_init(struct drm_device *);
|
|
+extern void nouveau_bios_run_init_table(struct drm_device *, uint16_t table,
|
|
+ struct dcb_entry *);
|
|
+extern struct dcb_gpio_entry *nouveau_bios_gpio_entry(struct drm_device *,
|
|
+ enum dcb_gpio_tag);
|
|
+extern struct dcb_connector_table_entry *
|
|
+nouveau_bios_connector_entry(struct drm_device *, int index);
|
|
+extern int get_pll_limits(struct drm_device *, uint32_t limit_match,
|
|
+ struct pll_lims *);
|
|
+extern int nouveau_bios_run_display_table(struct drm_device *,
|
|
+ struct dcb_entry *,
|
|
+ uint32_t script, int pxclk);
|
|
+extern void *nouveau_bios_dp_table(struct drm_device *, struct dcb_entry *,
|
|
+ int *length);
|
|
+extern bool nouveau_bios_fp_mode(struct drm_device *, struct drm_display_mode *);
|
|
+extern uint8_t *nouveau_bios_embedded_edid(struct drm_device *);
|
|
+extern int nouveau_bios_parse_lvds_table(struct drm_device *, int pxclk,
|
|
+ bool *dl, bool *if_is_24bit);
|
|
+extern int run_tmds_table(struct drm_device *, struct dcb_entry *,
|
|
+ int head, int pxclk);
|
|
+extern int call_lvds_script(struct drm_device *, struct dcb_entry *, int head,
|
|
+ enum LVDS_script, int pxclk);
|
|
+
|
|
+/* nouveau_ttm.c */
|
|
+int nouveau_ttm_global_init(struct drm_nouveau_private *);
|
|
+void nouveau_ttm_global_release(struct drm_nouveau_private *);
|
|
+int nouveau_ttm_mmap(struct file *, struct vm_area_struct *);
|
|
+
|
|
+/* nouveau_dp.c */
|
|
+int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
|
|
+ uint8_t *data, int data_nr);
|
|
+bool nouveau_dp_detect(struct drm_encoder *);
|
|
+bool nouveau_dp_link_train(struct drm_encoder *);
|
|
+
|
|
+/* nv04_fb.c */
|
|
+extern int nv04_fb_init(struct drm_device *);
|
|
+extern void nv04_fb_takedown(struct drm_device *);
|
|
+
|
|
+/* nv10_fb.c */
|
|
+extern int nv10_fb_init(struct drm_device *);
|
|
+extern void nv10_fb_takedown(struct drm_device *);
|
|
+extern void nv10_fb_set_region_tiling(struct drm_device *, int, uint32_t,
|
|
+ uint32_t, uint32_t);
|
|
+
|
|
+/* nv40_fb.c */
|
|
+extern int nv40_fb_init(struct drm_device *);
|
|
+extern void nv40_fb_takedown(struct drm_device *);
|
|
+extern void nv40_fb_set_region_tiling(struct drm_device *, int, uint32_t,
|
|
+ uint32_t, uint32_t);
|
|
+
|
|
+/* nv04_fifo.c */
|
|
+extern int nv04_fifo_init(struct drm_device *);
|
|
+extern void nv04_fifo_disable(struct drm_device *);
|
|
+extern void nv04_fifo_enable(struct drm_device *);
|
|
+extern bool nv04_fifo_reassign(struct drm_device *, bool);
|
|
+extern bool nv04_fifo_cache_flush(struct drm_device *);
|
|
+extern bool nv04_fifo_cache_pull(struct drm_device *, bool);
|
|
+extern int nv04_fifo_channel_id(struct drm_device *);
|
|
+extern int nv04_fifo_create_context(struct nouveau_channel *);
|
|
+extern void nv04_fifo_destroy_context(struct nouveau_channel *);
|
|
+extern int nv04_fifo_load_context(struct nouveau_channel *);
|
|
+extern int nv04_fifo_unload_context(struct drm_device *);
|
|
+
|
|
+/* nv10_fifo.c */
|
|
+extern int nv10_fifo_init(struct drm_device *);
|
|
+extern int nv10_fifo_channel_id(struct drm_device *);
|
|
+extern int nv10_fifo_create_context(struct nouveau_channel *);
|
|
+extern void nv10_fifo_destroy_context(struct nouveau_channel *);
|
|
+extern int nv10_fifo_load_context(struct nouveau_channel *);
|
|
+extern int nv10_fifo_unload_context(struct drm_device *);
|
|
+
|
|
+/* nv40_fifo.c */
|
|
+extern int nv40_fifo_init(struct drm_device *);
|
|
+extern int nv40_fifo_create_context(struct nouveau_channel *);
|
|
+extern void nv40_fifo_destroy_context(struct nouveau_channel *);
|
|
+extern int nv40_fifo_load_context(struct nouveau_channel *);
|
|
+extern int nv40_fifo_unload_context(struct drm_device *);
|
|
+
|
|
+/* nv50_fifo.c */
|
|
+extern int nv50_fifo_init(struct drm_device *);
|
|
+extern void nv50_fifo_takedown(struct drm_device *);
|
|
+extern int nv50_fifo_channel_id(struct drm_device *);
|
|
+extern int nv50_fifo_create_context(struct nouveau_channel *);
|
|
+extern void nv50_fifo_destroy_context(struct nouveau_channel *);
|
|
+extern int nv50_fifo_load_context(struct nouveau_channel *);
|
|
+extern int nv50_fifo_unload_context(struct drm_device *);
|
|
+
|
|
+/* nv04_graph.c */
|
|
+extern struct nouveau_pgraph_object_class nv04_graph_grclass[];
|
|
+extern int nv04_graph_init(struct drm_device *);
|
|
+extern void nv04_graph_takedown(struct drm_device *);
|
|
+extern void nv04_graph_fifo_access(struct drm_device *, bool);
|
|
+extern struct nouveau_channel *nv04_graph_channel(struct drm_device *);
|
|
+extern int nv04_graph_create_context(struct nouveau_channel *);
|
|
+extern void nv04_graph_destroy_context(struct nouveau_channel *);
|
|
+extern int nv04_graph_load_context(struct nouveau_channel *);
|
|
+extern int nv04_graph_unload_context(struct drm_device *);
|
|
+extern void nv04_graph_context_switch(struct drm_device *);
|
|
+
|
|
+/* nv10_graph.c */
|
|
+extern struct nouveau_pgraph_object_class nv10_graph_grclass[];
|
|
+extern int nv10_graph_init(struct drm_device *);
|
|
+extern void nv10_graph_takedown(struct drm_device *);
|
|
+extern struct nouveau_channel *nv10_graph_channel(struct drm_device *);
|
|
+extern int nv10_graph_create_context(struct nouveau_channel *);
|
|
+extern void nv10_graph_destroy_context(struct nouveau_channel *);
|
|
+extern int nv10_graph_load_context(struct nouveau_channel *);
|
|
+extern int nv10_graph_unload_context(struct drm_device *);
|
|
+extern void nv10_graph_context_switch(struct drm_device *);
|
|
+extern void nv10_graph_set_region_tiling(struct drm_device *, int, uint32_t,
|
|
+ uint32_t, uint32_t);
|
|
+
|
|
+/* nv20_graph.c */
|
|
+extern struct nouveau_pgraph_object_class nv20_graph_grclass[];
|
|
+extern struct nouveau_pgraph_object_class nv30_graph_grclass[];
|
|
+extern int nv20_graph_create_context(struct nouveau_channel *);
|
|
+extern void nv20_graph_destroy_context(struct nouveau_channel *);
|
|
+extern int nv20_graph_load_context(struct nouveau_channel *);
|
|
+extern int nv20_graph_unload_context(struct drm_device *);
|
|
+extern int nv20_graph_init(struct drm_device *);
|
|
+extern void nv20_graph_takedown(struct drm_device *);
|
|
+extern int nv30_graph_init(struct drm_device *);
|
|
+extern void nv20_graph_set_region_tiling(struct drm_device *, int, uint32_t,
|
|
+ uint32_t, uint32_t);
|
|
+
|
|
+/* nv40_graph.c */
|
|
+extern struct nouveau_pgraph_object_class nv40_graph_grclass[];
|
|
+extern int nv40_graph_init(struct drm_device *);
|
|
+extern void nv40_graph_takedown(struct drm_device *);
|
|
+extern struct nouveau_channel *nv40_graph_channel(struct drm_device *);
|
|
+extern int nv40_graph_create_context(struct nouveau_channel *);
|
|
+extern void nv40_graph_destroy_context(struct nouveau_channel *);
|
|
+extern int nv40_graph_load_context(struct nouveau_channel *);
|
|
+extern int nv40_graph_unload_context(struct drm_device *);
|
|
+extern void nv40_grctx_init(struct nouveau_grctx *);
|
|
+extern void nv40_graph_set_region_tiling(struct drm_device *, int, uint32_t,
|
|
+ uint32_t, uint32_t);
|
|
+
|
|
+/* nv50_graph.c */
|
|
+extern struct nouveau_pgraph_object_class nv50_graph_grclass[];
|
|
+extern int nv50_graph_init(struct drm_device *);
|
|
+extern void nv50_graph_takedown(struct drm_device *);
|
|
+extern void nv50_graph_fifo_access(struct drm_device *, bool);
|
|
+extern struct nouveau_channel *nv50_graph_channel(struct drm_device *);
|
|
+extern int nv50_graph_create_context(struct nouveau_channel *);
|
|
+extern void nv50_graph_destroy_context(struct nouveau_channel *);
|
|
+extern int nv50_graph_load_context(struct nouveau_channel *);
|
|
+extern int nv50_graph_unload_context(struct drm_device *);
|
|
+extern void nv50_graph_context_switch(struct drm_device *);
|
|
+
|
|
+/* nouveau_grctx.c */
|
|
+extern int nouveau_grctx_prog_load(struct drm_device *);
|
|
+extern void nouveau_grctx_vals_load(struct drm_device *,
|
|
+ struct nouveau_gpuobj *);
|
|
+extern void nouveau_grctx_fini(struct drm_device *);
|
|
+
|
|
+/* nv04_instmem.c */
|
|
+extern int nv04_instmem_init(struct drm_device *);
|
|
+extern void nv04_instmem_takedown(struct drm_device *);
|
|
+extern int nv04_instmem_suspend(struct drm_device *);
|
|
+extern void nv04_instmem_resume(struct drm_device *);
|
|
+extern int nv04_instmem_populate(struct drm_device *, struct nouveau_gpuobj *,
|
|
+ uint32_t *size);
|
|
+extern void nv04_instmem_clear(struct drm_device *, struct nouveau_gpuobj *);
|
|
+extern int nv04_instmem_bind(struct drm_device *, struct nouveau_gpuobj *);
|
|
+extern int nv04_instmem_unbind(struct drm_device *, struct nouveau_gpuobj *);
|
|
+extern void nv04_instmem_prepare_access(struct drm_device *, bool write);
|
|
+extern void nv04_instmem_finish_access(struct drm_device *);
|
|
+
|
|
+/* nv50_instmem.c */
|
|
+extern int nv50_instmem_init(struct drm_device *);
|
|
+extern void nv50_instmem_takedown(struct drm_device *);
|
|
+extern int nv50_instmem_suspend(struct drm_device *);
|
|
+extern void nv50_instmem_resume(struct drm_device *);
|
|
+extern int nv50_instmem_populate(struct drm_device *, struct nouveau_gpuobj *,
|
|
+ uint32_t *size);
|
|
+extern void nv50_instmem_clear(struct drm_device *, struct nouveau_gpuobj *);
|
|
+extern int nv50_instmem_bind(struct drm_device *, struct nouveau_gpuobj *);
|
|
+extern int nv50_instmem_unbind(struct drm_device *, struct nouveau_gpuobj *);
|
|
+extern void nv50_instmem_prepare_access(struct drm_device *, bool write);
|
|
+extern void nv50_instmem_finish_access(struct drm_device *);
|
|
+
|
|
+/* nv04_mc.c */
|
|
+extern int nv04_mc_init(struct drm_device *);
|
|
+extern void nv04_mc_takedown(struct drm_device *);
|
|
+
|
|
+/* nv40_mc.c */
|
|
+extern int nv40_mc_init(struct drm_device *);
|
|
+extern void nv40_mc_takedown(struct drm_device *);
|
|
+
|
|
+/* nv50_mc.c */
|
|
+extern int nv50_mc_init(struct drm_device *);
|
|
+extern void nv50_mc_takedown(struct drm_device *);
|
|
+
|
|
+/* nv04_timer.c */
|
|
+extern int nv04_timer_init(struct drm_device *);
|
|
+extern uint64_t nv04_timer_read(struct drm_device *);
|
|
+extern void nv04_timer_takedown(struct drm_device *);
|
|
+
|
|
+extern long nouveau_compat_ioctl(struct file *file, unsigned int cmd,
|
|
+ unsigned long arg);
|
|
+
|
|
+/* nv04_dac.c */
|
|
+extern int nv04_dac_create(struct drm_device *dev, struct dcb_entry *entry);
|
|
+extern uint32_t nv17_dac_sample_load(struct drm_encoder *encoder);
|
|
+extern int nv04_dac_output_offset(struct drm_encoder *encoder);
|
|
+extern void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable);
|
|
+
|
|
+/* nv04_dfp.c */
|
|
+extern int nv04_dfp_create(struct drm_device *dev, struct dcb_entry *entry);
|
|
+extern int nv04_dfp_get_bound_head(struct drm_device *dev, struct dcb_entry *dcbent);
|
|
+extern void nv04_dfp_bind_head(struct drm_device *dev, struct dcb_entry *dcbent,
|
|
+ int head, bool dl);
|
|
+extern void nv04_dfp_disable(struct drm_device *dev, int head);
|
|
+extern void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode);
|
|
+
|
|
+/* nv04_tv.c */
|
|
+extern int nv04_tv_identify(struct drm_device *dev, int i2c_index);
|
|
+extern int nv04_tv_create(struct drm_device *dev, struct dcb_entry *entry);
|
|
+
|
|
+/* nv17_tv.c */
|
|
+extern int nv17_tv_create(struct drm_device *dev, struct dcb_entry *entry);
|
|
+
|
|
+/* nv04_display.c */
|
|
+extern int nv04_display_create(struct drm_device *);
|
|
+extern void nv04_display_destroy(struct drm_device *);
|
|
+extern void nv04_display_restore(struct drm_device *);
|
|
+
|
|
+/* nv04_crtc.c */
|
|
+extern int nv04_crtc_create(struct drm_device *, int index);
|
|
+
|
|
+/* nouveau_bo.c */
|
|
+extern struct ttm_bo_driver nouveau_bo_driver;
|
|
+extern int nouveau_bo_new(struct drm_device *, struct nouveau_channel *,
|
|
+ int size, int align, uint32_t flags,
|
|
+ uint32_t tile_mode, uint32_t tile_flags,
|
|
+ bool no_vm, bool mappable, struct nouveau_bo **);
|
|
+extern int nouveau_bo_pin(struct nouveau_bo *, uint32_t flags);
|
|
+extern int nouveau_bo_unpin(struct nouveau_bo *);
|
|
+extern int nouveau_bo_map(struct nouveau_bo *);
|
|
+extern void nouveau_bo_unmap(struct nouveau_bo *);
|
|
+extern void nouveau_bo_placement_set(struct nouveau_bo *, uint32_t memtype);
|
|
+extern u16 nouveau_bo_rd16(struct nouveau_bo *nvbo, unsigned index);
|
|
+extern void nouveau_bo_wr16(struct nouveau_bo *nvbo, unsigned index, u16 val);
|
|
+extern u32 nouveau_bo_rd32(struct nouveau_bo *nvbo, unsigned index);
|
|
+extern void nouveau_bo_wr32(struct nouveau_bo *nvbo, unsigned index, u32 val);
|
|
+
|
|
+/* nouveau_fence.c */
|
|
+struct nouveau_fence;
|
|
+extern int nouveau_fence_init(struct nouveau_channel *);
|
|
+extern void nouveau_fence_fini(struct nouveau_channel *);
|
|
+extern void nouveau_fence_update(struct nouveau_channel *);
|
|
+extern int nouveau_fence_new(struct nouveau_channel *, struct nouveau_fence **,
|
|
+ bool emit);
|
|
+extern int nouveau_fence_emit(struct nouveau_fence *);
|
|
+struct nouveau_channel *nouveau_fence_channel(struct nouveau_fence *);
|
|
+extern bool nouveau_fence_signalled(void *obj, void *arg);
|
|
+extern int nouveau_fence_wait(void *obj, void *arg, bool lazy, bool intr);
|
|
+extern int nouveau_fence_flush(void *obj, void *arg);
|
|
+extern void nouveau_fence_unref(void **obj);
|
|
+extern void *nouveau_fence_ref(void *obj);
|
|
+extern void nouveau_fence_handler(struct drm_device *dev, int channel);
|
|
+
|
|
+/* nouveau_gem.c */
|
|
+extern int nouveau_gem_new(struct drm_device *, struct nouveau_channel *,
|
|
+ int size, int align, uint32_t flags,
|
|
+ uint32_t tile_mode, uint32_t tile_flags,
|
|
+ bool no_vm, bool mappable, struct nouveau_bo **);
|
|
+extern int nouveau_gem_object_new(struct drm_gem_object *);
|
|
+extern void nouveau_gem_object_del(struct drm_gem_object *);
|
|
+extern int nouveau_gem_ioctl_new(struct drm_device *, void *,
|
|
+ struct drm_file *);
|
|
+extern int nouveau_gem_ioctl_pushbuf(struct drm_device *, void *,
|
|
+ struct drm_file *);
|
|
+extern int nouveau_gem_ioctl_pushbuf_call(struct drm_device *, void *,
|
|
+ struct drm_file *);
|
|
+extern int nouveau_gem_ioctl_pushbuf_call2(struct drm_device *, void *,
|
|
+ struct drm_file *);
|
|
+extern int nouveau_gem_ioctl_pin(struct drm_device *, void *,
|
|
+ struct drm_file *);
|
|
+extern int nouveau_gem_ioctl_unpin(struct drm_device *, void *,
|
|
+ struct drm_file *);
|
|
+extern int nouveau_gem_ioctl_tile(struct drm_device *, void *,
|
|
+ struct drm_file *);
|
|
+extern int nouveau_gem_ioctl_cpu_prep(struct drm_device *, void *,
|
|
+ struct drm_file *);
|
|
+extern int nouveau_gem_ioctl_cpu_fini(struct drm_device *, void *,
|
|
+ struct drm_file *);
|
|
+extern int nouveau_gem_ioctl_info(struct drm_device *, void *,
|
|
+ struct drm_file *);
|
|
+
|
|
+/* nv17_gpio.c */
|
|
+int nv17_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag);
|
|
+int nv17_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state);
|
|
+
|
|
+#ifndef ioread32_native
|
|
+#ifdef __BIG_ENDIAN
|
|
+#define ioread16_native ioread16be
|
|
+#define iowrite16_native iowrite16be
|
|
+#define ioread32_native ioread32be
|
|
+#define iowrite32_native iowrite32be
|
|
+#else /* def __BIG_ENDIAN */
|
|
+#define ioread16_native ioread16
|
|
+#define iowrite16_native iowrite16
|
|
+#define ioread32_native ioread32
|
|
+#define iowrite32_native iowrite32
|
|
+#endif /* def __BIG_ENDIAN else */
|
|
+#endif /* !ioread32_native */
|
|
+
|
|
+/* channel control reg access */
|
|
+static inline u32 nvchan_rd32(struct nouveau_channel *chan, unsigned reg)
|
|
+{
|
|
+ return ioread32_native(chan->user + reg);
|
|
+}
|
|
+
|
|
+static inline void nvchan_wr32(struct nouveau_channel *chan,
|
|
+ unsigned reg, u32 val)
|
|
+{
|
|
+ iowrite32_native(val, chan->user + reg);
|
|
+}
|
|
+
|
|
+/* register access */
|
|
+static inline u32 nv_rd32(struct drm_device *dev, unsigned reg)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ return ioread32_native(dev_priv->mmio + reg);
|
|
+}
|
|
+
|
|
+static inline void nv_wr32(struct drm_device *dev, unsigned reg, u32 val)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ iowrite32_native(val, dev_priv->mmio + reg);
|
|
+}
|
|
+
|
|
+static inline u8 nv_rd08(struct drm_device *dev, unsigned reg)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ return ioread8(dev_priv->mmio + reg);
|
|
+}
|
|
+
|
|
+static inline void nv_wr08(struct drm_device *dev, unsigned reg, u8 val)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ iowrite8(val, dev_priv->mmio + reg);
|
|
+}
|
|
+
|
|
+#define nv_wait(reg, mask, val) \
|
|
+ nouveau_wait_until(dev, 2000000000ULL, (reg), (mask), (val))
|
|
+
|
|
+/* PRAMIN access */
|
|
+static inline u32 nv_ri32(struct drm_device *dev, unsigned offset)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ return ioread32_native(dev_priv->ramin + offset);
|
|
+}
|
|
+
|
|
+static inline void nv_wi32(struct drm_device *dev, unsigned offset, u32 val)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ iowrite32_native(val, dev_priv->ramin + offset);
|
|
+}
|
|
+
|
|
+/* object access */
|
|
+static inline u32 nv_ro32(struct drm_device *dev, struct nouveau_gpuobj *obj,
|
|
+ unsigned index)
|
|
+{
|
|
+ return nv_ri32(dev, obj->im_pramin->start + index * 4);
|
|
+}
|
|
+
|
|
+static inline void nv_wo32(struct drm_device *dev, struct nouveau_gpuobj *obj,
|
|
+ unsigned index, u32 val)
|
|
+{
|
|
+ nv_wi32(dev, obj->im_pramin->start + index * 4, val);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Logging
|
|
+ * Argument d is (struct drm_device *).
|
|
+ */
|
|
+#define NV_PRINTK(level, d, fmt, arg...) \
|
|
+ printk(level "[" DRM_NAME "] " DRIVER_NAME " %s: " fmt, \
|
|
+ pci_name(d->pdev), ##arg)
|
|
+#ifndef NV_DEBUG_NOTRACE
|
|
+#define NV_DEBUG(d, fmt, arg...) do { \
|
|
+ if (drm_debug & DRM_UT_DRIVER) { \
|
|
+ NV_PRINTK(KERN_DEBUG, d, "%s:%d - " fmt, __func__, \
|
|
+ __LINE__, ##arg); \
|
|
+ } \
|
|
+} while (0)
|
|
+#define NV_DEBUG_KMS(d, fmt, arg...) do { \
|
|
+ if (drm_debug & DRM_UT_KMS) { \
|
|
+ NV_PRINTK(KERN_DEBUG, d, "%s:%d - " fmt, __func__, \
|
|
+ __LINE__, ##arg); \
|
|
+ } \
|
|
+} while (0)
|
|
+#else
|
|
+#define NV_DEBUG(d, fmt, arg...) do { \
|
|
+ if (drm_debug & DRM_UT_DRIVER) \
|
|
+ NV_PRINTK(KERN_DEBUG, d, fmt, ##arg); \
|
|
+} while (0)
|
|
+#define NV_DEBUG_KMS(d, fmt, arg...) do { \
|
|
+ if (drm_debug & DRM_UT_KMS) \
|
|
+ NV_PRINTK(KERN_DEBUG, d, fmt, ##arg); \
|
|
+} while (0)
|
|
+#endif
|
|
+#define NV_ERROR(d, fmt, arg...) NV_PRINTK(KERN_ERR, d, fmt, ##arg)
|
|
+#define NV_INFO(d, fmt, arg...) NV_PRINTK(KERN_INFO, d, fmt, ##arg)
|
|
+#define NV_TRACEWARN(d, fmt, arg...) NV_PRINTK(KERN_NOTICE, d, fmt, ##arg)
|
|
+#define NV_TRACE(d, fmt, arg...) NV_PRINTK(KERN_INFO, d, fmt, ##arg)
|
|
+#define NV_WARN(d, fmt, arg...) NV_PRINTK(KERN_WARNING, d, fmt, ##arg)
|
|
+
|
|
+/* nouveau_reg_debug bitmask */
|
|
+enum {
|
|
+ NOUVEAU_REG_DEBUG_MC = 0x1,
|
|
+ NOUVEAU_REG_DEBUG_VIDEO = 0x2,
|
|
+ NOUVEAU_REG_DEBUG_FB = 0x4,
|
|
+ NOUVEAU_REG_DEBUG_EXTDEV = 0x8,
|
|
+ NOUVEAU_REG_DEBUG_CRTC = 0x10,
|
|
+ NOUVEAU_REG_DEBUG_RAMDAC = 0x20,
|
|
+ NOUVEAU_REG_DEBUG_VGACRTC = 0x40,
|
|
+ NOUVEAU_REG_DEBUG_RMVIO = 0x80,
|
|
+ NOUVEAU_REG_DEBUG_VGAATTR = 0x100,
|
|
+ NOUVEAU_REG_DEBUG_EVO = 0x200,
|
|
+};
|
|
+
|
|
+#define NV_REG_DEBUG(type, dev, fmt, arg...) do { \
|
|
+ if (nouveau_reg_debug & NOUVEAU_REG_DEBUG_##type) \
|
|
+ NV_PRINTK(KERN_DEBUG, dev, "%s: " fmt, __func__, ##arg); \
|
|
+} while (0)
|
|
+
|
|
+static inline bool
|
|
+nv_two_heads(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ const int impl = dev->pci_device & 0x0ff0;
|
|
+
|
|
+ if (dev_priv->card_type >= NV_10 && impl != 0x0100 &&
|
|
+ impl != 0x0150 && impl != 0x01a0 && impl != 0x0200)
|
|
+ return true;
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+static inline bool
|
|
+nv_gf4_disp_arch(struct drm_device *dev)
|
|
+{
|
|
+ return nv_two_heads(dev) && (dev->pci_device & 0x0ff0) != 0x0110;
|
|
+}
|
|
+
|
|
+static inline bool
|
|
+nv_two_reg_pll(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ const int impl = dev->pci_device & 0x0ff0;
|
|
+
|
|
+ if (impl == 0x0310 || impl == 0x0340 || dev_priv->card_type >= NV_40)
|
|
+ return true;
|
|
+ return false;
|
|
+}
|
|
+
|
|
+#define NV_SW 0x0000506e
|
|
+#define NV_SW_DMA_SEMAPHORE 0x00000060
|
|
+#define NV_SW_SEMAPHORE_OFFSET 0x00000064
|
|
+#define NV_SW_SEMAPHORE_ACQUIRE 0x00000068
|
|
+#define NV_SW_SEMAPHORE_RELEASE 0x0000006c
|
|
+#define NV_SW_DMA_VBLSEM 0x0000018c
|
|
+#define NV_SW_VBLSEM_OFFSET 0x00000400
|
|
+#define NV_SW_VBLSEM_RELEASE_VALUE 0x00000404
|
|
+#define NV_SW_VBLSEM_RELEASE 0x00000408
|
|
+
|
|
+#endif /* __NOUVEAU_DRV_H__ */
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h
|
|
new file mode 100644
|
|
index 0000000..bc4a240
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h
|
|
@@ -0,0 +1,91 @@
|
|
+/*
|
|
+ * Copyright (C) 2008 Maarten Maathuis.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef __NOUVEAU_ENCODER_H__
|
|
+#define __NOUVEAU_ENCODER_H__
|
|
+
|
|
+#include "drm_encoder_slave.h"
|
|
+#include "nouveau_drv.h"
|
|
+
|
|
+#define NV_DPMS_CLEARED 0x80
|
|
+
|
|
+struct nouveau_encoder {
|
|
+ struct drm_encoder_slave base;
|
|
+
|
|
+ struct dcb_entry *dcb;
|
|
+ int or;
|
|
+
|
|
+ struct drm_display_mode mode;
|
|
+ int last_dpms;
|
|
+
|
|
+ struct nv04_output_reg restore;
|
|
+
|
|
+ void (*disconnect)(struct nouveau_encoder *encoder);
|
|
+
|
|
+ union {
|
|
+ struct {
|
|
+ int dpcd_version;
|
|
+ int link_nr;
|
|
+ int link_bw;
|
|
+ } dp;
|
|
+ };
|
|
+};
|
|
+
|
|
+static inline struct nouveau_encoder *nouveau_encoder(struct drm_encoder *enc)
|
|
+{
|
|
+ struct drm_encoder_slave *slave = to_encoder_slave(enc);
|
|
+
|
|
+ return container_of(slave, struct nouveau_encoder, base);
|
|
+}
|
|
+
|
|
+static inline struct drm_encoder *to_drm_encoder(struct nouveau_encoder *enc)
|
|
+{
|
|
+ return &enc->base.base;
|
|
+}
|
|
+
|
|
+struct nouveau_connector *
|
|
+nouveau_encoder_connector_get(struct nouveau_encoder *encoder);
|
|
+int nv50_sor_create(struct drm_device *dev, struct dcb_entry *entry);
|
|
+int nv50_dac_create(struct drm_device *dev, struct dcb_entry *entry);
|
|
+
|
|
+struct bit_displayport_encoder_table {
|
|
+ uint32_t match;
|
|
+ uint8_t record_nr;
|
|
+ uint8_t unknown;
|
|
+ uint16_t script0;
|
|
+ uint16_t script1;
|
|
+ uint16_t unknown_table;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct bit_displayport_encoder_table_entry {
|
|
+ uint8_t vs_level;
|
|
+ uint8_t pre_level;
|
|
+ uint8_t reg0;
|
|
+ uint8_t reg1;
|
|
+ uint8_t reg2;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+#endif /* __NOUVEAU_ENCODER_H__ */
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_fb.h b/drivers/gpu/drm/nouveau/nouveau_fb.h
|
|
new file mode 100644
|
|
index 0000000..4a3f31a
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_fb.h
|
|
@@ -0,0 +1,47 @@
|
|
+/*
|
|
+ * Copyright (C) 2008 Maarten Maathuis.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef __NOUVEAU_FB_H__
|
|
+#define __NOUVEAU_FB_H__
|
|
+
|
|
+struct nouveau_framebuffer {
|
|
+ struct drm_framebuffer base;
|
|
+ struct nouveau_bo *nvbo;
|
|
+};
|
|
+
|
|
+static inline struct nouveau_framebuffer *
|
|
+nouveau_framebuffer(struct drm_framebuffer *fb)
|
|
+{
|
|
+ return container_of(fb, struct nouveau_framebuffer, base);
|
|
+}
|
|
+
|
|
+extern const struct drm_mode_config_funcs nouveau_mode_config_funcs;
|
|
+
|
|
+struct drm_framebuffer *
|
|
+nouveau_framebuffer_create(struct drm_device *, struct nouveau_bo *,
|
|
+ struct drm_mode_fb_cmd *);
|
|
+
|
|
+#endif /* __NOUVEAU_FB_H__ */
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
|
|
new file mode 100644
|
|
index 0000000..ea879a2
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
|
|
@@ -0,0 +1,423 @@
|
|
+/*
|
|
+ * Copyright © 2007 David Airlie
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the next
|
|
+ * paragraph) shall be included in all copies or substantial portions of the
|
|
+ * Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
+ * DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ * Authors:
|
|
+ * David Airlie
|
|
+ */
|
|
+
|
|
+#include <linux/module.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/errno.h>
|
|
+#include <linux/string.h>
|
|
+#include <linux/mm.h>
|
|
+#include <linux/tty.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/sysrq.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/fb.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/screen_info.h>
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "drm_crtc.h"
|
|
+#include "drm_crtc_helper.h"
|
|
+#include "drm_fb_helper.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_drm.h"
|
|
+#include "nouveau_crtc.h"
|
|
+#include "nouveau_fb.h"
|
|
+#include "nouveau_fbcon.h"
|
|
+#include "nouveau_dma.h"
|
|
+
|
|
+static int
|
|
+nouveau_fbcon_sync(struct fb_info *info)
|
|
+{
|
|
+ struct nouveau_fbcon_par *par = info->par;
|
|
+ struct drm_device *dev = par->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_channel *chan = dev_priv->channel;
|
|
+ int ret, i;
|
|
+
|
|
+ if (!chan || !chan->accel_done ||
|
|
+ info->state != FBINFO_STATE_RUNNING ||
|
|
+ info->flags & FBINFO_HWACCEL_DISABLED)
|
|
+ return 0;
|
|
+
|
|
+ if (RING_SPACE(chan, 4)) {
|
|
+ nouveau_fbcon_gpu_lockup(info);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ BEGIN_RING(chan, 0, 0x0104, 1);
|
|
+ OUT_RING(chan, 0);
|
|
+ BEGIN_RING(chan, 0, 0x0100, 1);
|
|
+ OUT_RING(chan, 0);
|
|
+ nouveau_bo_wr32(chan->notifier_bo, chan->m2mf_ntfy + 3, 0xffffffff);
|
|
+ FIRE_RING(chan);
|
|
+
|
|
+ ret = -EBUSY;
|
|
+ for (i = 0; i < 100000; i++) {
|
|
+ if (!nouveau_bo_rd32(chan->notifier_bo, chan->m2mf_ntfy + 3)) {
|
|
+ ret = 0;
|
|
+ break;
|
|
+ }
|
|
+ DRM_UDELAY(1);
|
|
+ }
|
|
+
|
|
+ if (ret) {
|
|
+ nouveau_fbcon_gpu_lockup(info);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ chan->accel_done = false;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct fb_ops nouveau_fbcon_ops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .fb_check_var = drm_fb_helper_check_var,
|
|
+ .fb_set_par = drm_fb_helper_set_par,
|
|
+ .fb_setcolreg = drm_fb_helper_setcolreg,
|
|
+ .fb_fillrect = cfb_fillrect,
|
|
+ .fb_copyarea = cfb_copyarea,
|
|
+ .fb_imageblit = cfb_imageblit,
|
|
+ .fb_sync = nouveau_fbcon_sync,
|
|
+ .fb_pan_display = drm_fb_helper_pan_display,
|
|
+ .fb_blank = drm_fb_helper_blank,
|
|
+ .fb_setcmap = drm_fb_helper_setcmap,
|
|
+};
|
|
+
|
|
+static struct fb_ops nv04_fbcon_ops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .fb_check_var = drm_fb_helper_check_var,
|
|
+ .fb_set_par = drm_fb_helper_set_par,
|
|
+ .fb_setcolreg = drm_fb_helper_setcolreg,
|
|
+ .fb_fillrect = nv04_fbcon_fillrect,
|
|
+ .fb_copyarea = nv04_fbcon_copyarea,
|
|
+ .fb_imageblit = nv04_fbcon_imageblit,
|
|
+ .fb_sync = nouveau_fbcon_sync,
|
|
+ .fb_pan_display = drm_fb_helper_pan_display,
|
|
+ .fb_blank = drm_fb_helper_blank,
|
|
+ .fb_setcmap = drm_fb_helper_setcmap,
|
|
+};
|
|
+
|
|
+static struct fb_ops nv50_fbcon_ops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .fb_check_var = drm_fb_helper_check_var,
|
|
+ .fb_set_par = drm_fb_helper_set_par,
|
|
+ .fb_setcolreg = drm_fb_helper_setcolreg,
|
|
+ .fb_fillrect = nv50_fbcon_fillrect,
|
|
+ .fb_copyarea = nv50_fbcon_copyarea,
|
|
+ .fb_imageblit = nv50_fbcon_imageblit,
|
|
+ .fb_sync = nouveau_fbcon_sync,
|
|
+ .fb_pan_display = drm_fb_helper_pan_display,
|
|
+ .fb_blank = drm_fb_helper_blank,
|
|
+ .fb_setcmap = drm_fb_helper_setcmap,
|
|
+};
|
|
+
|
|
+static void nouveau_fbcon_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
|
|
+ u16 blue, int regno)
|
|
+{
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+
|
|
+ nv_crtc->lut.r[regno] = red;
|
|
+ nv_crtc->lut.g[regno] = green;
|
|
+ nv_crtc->lut.b[regno] = blue;
|
|
+}
|
|
+
|
|
+static void nouveau_fbcon_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
|
|
+ u16 *blue, int regno)
|
|
+{
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+
|
|
+ *red = nv_crtc->lut.r[regno];
|
|
+ *green = nv_crtc->lut.g[regno];
|
|
+ *blue = nv_crtc->lut.b[regno];
|
|
+}
|
|
+
|
|
+static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
|
|
+ .gamma_set = nouveau_fbcon_gamma_set,
|
|
+ .gamma_get = nouveau_fbcon_gamma_get
|
|
+};
|
|
+
|
|
+#if defined(__i386__) || defined(__x86_64__)
|
|
+static bool
|
|
+nouveau_fbcon_has_vesafb_or_efifb(struct drm_device *dev)
|
|
+{
|
|
+ struct pci_dev *pdev = dev->pdev;
|
|
+ int ramin;
|
|
+
|
|
+ if (screen_info.orig_video_isVGA != VIDEO_TYPE_VLFB &&
|
|
+ screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
|
|
+ return false;
|
|
+
|
|
+ if (screen_info.lfb_base < pci_resource_start(pdev, 1))
|
|
+ goto not_fb;
|
|
+
|
|
+ if (screen_info.lfb_base + screen_info.lfb_size >=
|
|
+ pci_resource_start(pdev, 1) + pci_resource_len(pdev, 1))
|
|
+ goto not_fb;
|
|
+
|
|
+ return true;
|
|
+not_fb:
|
|
+ ramin = 2;
|
|
+ if (pci_resource_len(pdev, ramin) == 0) {
|
|
+ ramin = 3;
|
|
+ if (pci_resource_len(pdev, ramin) == 0)
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (screen_info.lfb_base < pci_resource_start(pdev, ramin))
|
|
+ return false;
|
|
+
|
|
+ if (screen_info.lfb_base + screen_info.lfb_size >=
|
|
+ pci_resource_start(pdev, ramin) + pci_resource_len(pdev, ramin))
|
|
+ return false;
|
|
+
|
|
+ return true;
|
|
+}
|
|
+#endif
|
|
+
|
|
+void
|
|
+nouveau_fbcon_zfill(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct fb_info *info = dev_priv->fbdev_info;
|
|
+ struct fb_fillrect rect;
|
|
+
|
|
+ /* Clear the entire fbcon. The drm will program every connector
|
|
+ * with it's preferred mode. If the sizes differ, one display will
|
|
+ * quite likely have garbage around the console.
|
|
+ */
|
|
+ rect.dx = rect.dy = 0;
|
|
+ rect.width = info->var.xres_virtual;
|
|
+ rect.height = info->var.yres_virtual;
|
|
+ rect.color = 0;
|
|
+ rect.rop = ROP_COPY;
|
|
+ info->fbops->fb_fillrect(info, &rect);
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_fbcon_create(struct drm_device *dev, uint32_t fb_width,
|
|
+ uint32_t fb_height, uint32_t surface_width,
|
|
+ uint32_t surface_height, uint32_t surface_depth,
|
|
+ uint32_t surface_bpp, struct drm_framebuffer **pfb)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct fb_info *info;
|
|
+ struct nouveau_fbcon_par *par;
|
|
+ struct drm_framebuffer *fb;
|
|
+ struct nouveau_framebuffer *nouveau_fb;
|
|
+ struct nouveau_bo *nvbo;
|
|
+ struct drm_mode_fb_cmd mode_cmd;
|
|
+ struct device *device = &dev->pdev->dev;
|
|
+ int size, ret;
|
|
+
|
|
+ mode_cmd.width = surface_width;
|
|
+ mode_cmd.height = surface_height;
|
|
+
|
|
+ mode_cmd.bpp = surface_bpp;
|
|
+ mode_cmd.pitch = mode_cmd.width * (mode_cmd.bpp >> 3);
|
|
+ mode_cmd.pitch = roundup(mode_cmd.pitch, 256);
|
|
+ mode_cmd.depth = surface_depth;
|
|
+
|
|
+ size = mode_cmd.pitch * mode_cmd.height;
|
|
+ size = roundup(size, PAGE_SIZE);
|
|
+
|
|
+ ret = nouveau_gem_new(dev, dev_priv->channel, size, 0, TTM_PL_FLAG_VRAM,
|
|
+ 0, 0x0000, false, true, &nvbo);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "failed to allocate framebuffer\n");
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ ret = nouveau_bo_pin(nvbo, TTM_PL_FLAG_VRAM);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "failed to pin fb: %d\n", ret);
|
|
+ nouveau_bo_ref(NULL, &nvbo);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ ret = nouveau_bo_map(nvbo);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "failed to map fb: %d\n", ret);
|
|
+ nouveau_bo_unpin(nvbo);
|
|
+ nouveau_bo_ref(NULL, &nvbo);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
+
|
|
+ fb = nouveau_framebuffer_create(dev, nvbo, &mode_cmd);
|
|
+ if (!fb) {
|
|
+ ret = -ENOMEM;
|
|
+ NV_ERROR(dev, "failed to allocate fb.\n");
|
|
+ goto out_unref;
|
|
+ }
|
|
+
|
|
+ list_add(&fb->filp_head, &dev->mode_config.fb_kernel_list);
|
|
+
|
|
+ nouveau_fb = nouveau_framebuffer(fb);
|
|
+ *pfb = fb;
|
|
+
|
|
+ info = framebuffer_alloc(sizeof(struct nouveau_fbcon_par), device);
|
|
+ if (!info) {
|
|
+ ret = -ENOMEM;
|
|
+ goto out_unref;
|
|
+ }
|
|
+
|
|
+ par = info->par;
|
|
+ par->helper.funcs = &nouveau_fbcon_helper_funcs;
|
|
+ par->helper.dev = dev;
|
|
+ ret = drm_fb_helper_init_crtc_count(&par->helper, 2, 4);
|
|
+ if (ret)
|
|
+ goto out_unref;
|
|
+ dev_priv->fbdev_info = info;
|
|
+
|
|
+ strcpy(info->fix.id, "nouveaufb");
|
|
+ if (nouveau_nofbaccel)
|
|
+ info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_DISABLED;
|
|
+ else
|
|
+ info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA |
|
|
+ FBINFO_HWACCEL_FILLRECT |
|
|
+ FBINFO_HWACCEL_IMAGEBLIT;
|
|
+ info->fbops = &nouveau_fbcon_ops;
|
|
+ info->fix.smem_start = dev->mode_config.fb_base + nvbo->bo.offset -
|
|
+ dev_priv->vm_vram_base;
|
|
+ info->fix.smem_len = size;
|
|
+
|
|
+ info->screen_base = nvbo_kmap_obj_iovirtual(nouveau_fb->nvbo);
|
|
+ info->screen_size = size;
|
|
+
|
|
+ drm_fb_helper_fill_fix(info, fb->pitch, fb->depth);
|
|
+ drm_fb_helper_fill_var(info, fb, fb_width, fb_height);
|
|
+
|
|
+ /* FIXME: we really shouldn't expose mmio space at all */
|
|
+ info->fix.mmio_start = pci_resource_start(dev->pdev, 1);
|
|
+ info->fix.mmio_len = pci_resource_len(dev->pdev, 1);
|
|
+
|
|
+ /* Set aperture base/size for vesafb takeover */
|
|
+#if defined(__i386__) || defined(__x86_64__)
|
|
+ if (nouveau_fbcon_has_vesafb_or_efifb(dev)) {
|
|
+ /* Some NVIDIA VBIOS' are stupid and decide to put the
|
|
+ * framebuffer in the middle of the PRAMIN BAR for
|
|
+ * whatever reason. We need to know the exact lfb_base
|
|
+ * to get vesafb kicked off, and the only reliable way
|
|
+ * we have left is to find out lfb_base the same way
|
|
+ * vesafb did.
|
|
+ */
|
|
+ info->aperture_base = screen_info.lfb_base;
|
|
+ info->aperture_size = screen_info.lfb_size;
|
|
+ if (screen_info.orig_video_isVGA == VIDEO_TYPE_VLFB)
|
|
+ info->aperture_size *= 65536;
|
|
+ } else
|
|
+#endif
|
|
+ {
|
|
+ info->aperture_base = info->fix.mmio_start;
|
|
+ info->aperture_size = info->fix.mmio_len;
|
|
+ }
|
|
+
|
|
+ info->pixmap.size = 64*1024;
|
|
+ info->pixmap.buf_align = 8;
|
|
+ info->pixmap.access_align = 32;
|
|
+ info->pixmap.flags = FB_PIXMAP_SYSTEM;
|
|
+ info->pixmap.scan_align = 1;
|
|
+
|
|
+ fb->fbdev = info;
|
|
+
|
|
+ par->nouveau_fb = nouveau_fb;
|
|
+ par->dev = dev;
|
|
+
|
|
+ if (dev_priv->channel && !nouveau_nofbaccel) {
|
|
+ switch (dev_priv->card_type) {
|
|
+ case NV_50:
|
|
+ nv50_fbcon_accel_init(info);
|
|
+ info->fbops = &nv50_fbcon_ops;
|
|
+ break;
|
|
+ default:
|
|
+ nv04_fbcon_accel_init(info);
|
|
+ info->fbops = &nv04_fbcon_ops;
|
|
+ break;
|
|
+ };
|
|
+ }
|
|
+
|
|
+ nouveau_fbcon_zfill(dev);
|
|
+
|
|
+ /* To allow resizeing without swapping buffers */
|
|
+ NV_INFO(dev, "allocated %dx%d fb: 0x%lx, bo %p\n",
|
|
+ nouveau_fb->base.width,
|
|
+ nouveau_fb->base.height,
|
|
+ nvbo->bo.offset, nvbo);
|
|
+
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+ return 0;
|
|
+
|
|
+out_unref:
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+out:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_fbcon_probe(struct drm_device *dev)
|
|
+{
|
|
+ NV_DEBUG_KMS(dev, "\n");
|
|
+
|
|
+ return drm_fb_helper_single_fb_probe(dev, 32, nouveau_fbcon_create);
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_fbcon_remove(struct drm_device *dev, struct drm_framebuffer *fb)
|
|
+{
|
|
+ struct nouveau_framebuffer *nouveau_fb = nouveau_framebuffer(fb);
|
|
+ struct fb_info *info;
|
|
+
|
|
+ if (!fb)
|
|
+ return -EINVAL;
|
|
+
|
|
+ info = fb->fbdev;
|
|
+ if (info) {
|
|
+ struct nouveau_fbcon_par *par = info->par;
|
|
+
|
|
+ unregister_framebuffer(info);
|
|
+ nouveau_bo_unmap(nouveau_fb->nvbo);
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
+ drm_gem_object_unreference(nouveau_fb->nvbo->gem);
|
|
+ nouveau_fb->nvbo = NULL;
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+ if (par)
|
|
+ drm_fb_helper_free(&par->helper);
|
|
+ framebuffer_release(info);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void nouveau_fbcon_gpu_lockup(struct fb_info *info)
|
|
+{
|
|
+ struct nouveau_fbcon_par *par = info->par;
|
|
+ struct drm_device *dev = par->dev;
|
|
+
|
|
+ NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
|
|
+ info->flags |= FBINFO_HWACCEL_DISABLED;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.h b/drivers/gpu/drm/nouveau/nouveau_fbcon.h
|
|
new file mode 100644
|
|
index 0000000..f9c34e1
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.h
|
|
@@ -0,0 +1,54 @@
|
|
+/*
|
|
+ * Copyright (C) 2008 Maarten Maathuis.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef __NOUVEAU_FBCON_H__
|
|
+#define __NOUVEAU_FBCON_H__
|
|
+
|
|
+#include "drm_fb_helper.h"
|
|
+
|
|
+struct nouveau_fbcon_par {
|
|
+ struct drm_fb_helper helper;
|
|
+ struct drm_device *dev;
|
|
+ struct nouveau_framebuffer *nouveau_fb;
|
|
+};
|
|
+
|
|
+int nouveau_fbcon_probe(struct drm_device *dev);
|
|
+int nouveau_fbcon_remove(struct drm_device *dev, struct drm_framebuffer *fb);
|
|
+void nouveau_fbcon_restore(void);
|
|
+void nouveau_fbcon_zfill(struct drm_device *dev);
|
|
+
|
|
+void nv04_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region);
|
|
+void nv04_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect);
|
|
+void nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image);
|
|
+int nv04_fbcon_accel_init(struct fb_info *info);
|
|
+void nv50_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect);
|
|
+void nv50_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region);
|
|
+void nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image);
|
|
+int nv50_fbcon_accel_init(struct fb_info *info);
|
|
+
|
|
+void nouveau_fbcon_gpu_lockup(struct fb_info *info);
|
|
+#endif /* __NV50_FBCON_H__ */
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c
|
|
new file mode 100644
|
|
index 0000000..faddf53
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_fence.c
|
|
@@ -0,0 +1,262 @@
|
|
+/*
|
|
+ * Copyright (C) 2007 Ben Skeggs.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_dma.h"
|
|
+
|
|
+#define USE_REFCNT (dev_priv->card_type >= NV_10)
|
|
+
|
|
+struct nouveau_fence {
|
|
+ struct nouveau_channel *channel;
|
|
+ struct kref refcount;
|
|
+ struct list_head entry;
|
|
+
|
|
+ uint32_t sequence;
|
|
+ bool signalled;
|
|
+};
|
|
+
|
|
+static inline struct nouveau_fence *
|
|
+nouveau_fence(void *sync_obj)
|
|
+{
|
|
+ return (struct nouveau_fence *)sync_obj;
|
|
+}
|
|
+
|
|
+static void
|
|
+nouveau_fence_del(struct kref *ref)
|
|
+{
|
|
+ struct nouveau_fence *fence =
|
|
+ container_of(ref, struct nouveau_fence, refcount);
|
|
+
|
|
+ kfree(fence);
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_fence_update(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
|
|
+ struct list_head *entry, *tmp;
|
|
+ struct nouveau_fence *fence;
|
|
+ uint32_t sequence;
|
|
+
|
|
+ if (USE_REFCNT)
|
|
+ sequence = nvchan_rd32(chan, 0x48);
|
|
+ else
|
|
+ sequence = chan->fence.last_sequence_irq;
|
|
+
|
|
+ if (chan->fence.sequence_ack == sequence)
|
|
+ return;
|
|
+ chan->fence.sequence_ack = sequence;
|
|
+
|
|
+ list_for_each_safe(entry, tmp, &chan->fence.pending) {
|
|
+ fence = list_entry(entry, struct nouveau_fence, entry);
|
|
+
|
|
+ sequence = fence->sequence;
|
|
+ fence->signalled = true;
|
|
+ list_del(&fence->entry);
|
|
+ kref_put(&fence->refcount, nouveau_fence_del);
|
|
+
|
|
+ if (sequence == chan->fence.sequence_ack)
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_fence_new(struct nouveau_channel *chan, struct nouveau_fence **pfence,
|
|
+ bool emit)
|
|
+{
|
|
+ struct nouveau_fence *fence;
|
|
+ int ret = 0;
|
|
+
|
|
+ fence = kzalloc(sizeof(*fence), GFP_KERNEL);
|
|
+ if (!fence)
|
|
+ return -ENOMEM;
|
|
+ kref_init(&fence->refcount);
|
|
+ fence->channel = chan;
|
|
+
|
|
+ if (emit)
|
|
+ ret = nouveau_fence_emit(fence);
|
|
+
|
|
+ if (ret)
|
|
+ nouveau_fence_unref((void *)&fence);
|
|
+ *pfence = fence;
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+struct nouveau_channel *
|
|
+nouveau_fence_channel(struct nouveau_fence *fence)
|
|
+{
|
|
+ return fence ? fence->channel : NULL;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_fence_emit(struct nouveau_fence *fence)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = fence->channel->dev->dev_private;
|
|
+ struct nouveau_channel *chan = fence->channel;
|
|
+ unsigned long flags;
|
|
+ int ret;
|
|
+
|
|
+ ret = RING_SPACE(chan, 2);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ if (unlikely(chan->fence.sequence == chan->fence.sequence_ack - 1)) {
|
|
+ spin_lock_irqsave(&chan->fence.lock, flags);
|
|
+ nouveau_fence_update(chan);
|
|
+ spin_unlock_irqrestore(&chan->fence.lock, flags);
|
|
+
|
|
+ BUG_ON(chan->fence.sequence ==
|
|
+ chan->fence.sequence_ack - 1);
|
|
+ }
|
|
+
|
|
+ fence->sequence = ++chan->fence.sequence;
|
|
+
|
|
+ kref_get(&fence->refcount);
|
|
+ spin_lock_irqsave(&chan->fence.lock, flags);
|
|
+ list_add_tail(&fence->entry, &chan->fence.pending);
|
|
+ spin_unlock_irqrestore(&chan->fence.lock, flags);
|
|
+
|
|
+ BEGIN_RING(chan, NvSubSw, USE_REFCNT ? 0x0050 : 0x0150, 1);
|
|
+ OUT_RING(chan, fence->sequence);
|
|
+ FIRE_RING(chan);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_fence_unref(void **sync_obj)
|
|
+{
|
|
+ struct nouveau_fence *fence = nouveau_fence(*sync_obj);
|
|
+
|
|
+ if (fence)
|
|
+ kref_put(&fence->refcount, nouveau_fence_del);
|
|
+ *sync_obj = NULL;
|
|
+}
|
|
+
|
|
+void *
|
|
+nouveau_fence_ref(void *sync_obj)
|
|
+{
|
|
+ struct nouveau_fence *fence = nouveau_fence(sync_obj);
|
|
+
|
|
+ kref_get(&fence->refcount);
|
|
+ return sync_obj;
|
|
+}
|
|
+
|
|
+bool
|
|
+nouveau_fence_signalled(void *sync_obj, void *sync_arg)
|
|
+{
|
|
+ struct nouveau_fence *fence = nouveau_fence(sync_obj);
|
|
+ struct nouveau_channel *chan = fence->channel;
|
|
+ unsigned long flags;
|
|
+
|
|
+ if (fence->signalled)
|
|
+ return true;
|
|
+
|
|
+ spin_lock_irqsave(&chan->fence.lock, flags);
|
|
+ nouveau_fence_update(chan);
|
|
+ spin_unlock_irqrestore(&chan->fence.lock, flags);
|
|
+ return fence->signalled;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_fence_wait(void *sync_obj, void *sync_arg, bool lazy, bool intr)
|
|
+{
|
|
+ unsigned long timeout = jiffies + (3 * DRM_HZ);
|
|
+ int ret = 0;
|
|
+
|
|
+ __set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
|
|
+
|
|
+ while (1) {
|
|
+ if (nouveau_fence_signalled(sync_obj, sync_arg))
|
|
+ break;
|
|
+
|
|
+ if (time_after_eq(jiffies, timeout)) {
|
|
+ ret = -EBUSY;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (lazy)
|
|
+ schedule_timeout(1);
|
|
+
|
|
+ if (intr && signal_pending(current)) {
|
|
+ ret = -ERESTARTSYS;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ __set_current_state(TASK_RUNNING);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_fence_flush(void *sync_obj, void *sync_arg)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_fence_handler(struct drm_device *dev, int channel)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_channel *chan = NULL;
|
|
+
|
|
+ if (channel >= 0 && channel < dev_priv->engine.fifo.channels)
|
|
+ chan = dev_priv->fifos[channel];
|
|
+
|
|
+ if (chan) {
|
|
+ spin_lock_irq(&chan->fence.lock);
|
|
+ nouveau_fence_update(chan);
|
|
+ spin_unlock_irq(&chan->fence.lock);
|
|
+ }
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_fence_init(struct nouveau_channel *chan)
|
|
+{
|
|
+ INIT_LIST_HEAD(&chan->fence.pending);
|
|
+ spin_lock_init(&chan->fence.lock);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_fence_fini(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct list_head *entry, *tmp;
|
|
+ struct nouveau_fence *fence;
|
|
+
|
|
+ list_for_each_safe(entry, tmp, &chan->fence.pending) {
|
|
+ fence = list_entry(entry, struct nouveau_fence, entry);
|
|
+
|
|
+ fence->signalled = true;
|
|
+ list_del(&fence->entry);
|
|
+ kref_put(&fence->refcount, nouveau_fence_del);
|
|
+ }
|
|
+}
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
|
|
new file mode 100644
|
|
index 0000000..70cc308
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
|
|
@@ -0,0 +1,994 @@
|
|
+/*
|
|
+ * Copyright (C) 2008 Ben Skeggs.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_drm.h"
|
|
+#include "nouveau_dma.h"
|
|
+
|
|
+#define nouveau_gem_pushbuf_sync(chan) 0
|
|
+
|
|
+int
|
|
+nouveau_gem_object_new(struct drm_gem_object *gem)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_gem_object_del(struct drm_gem_object *gem)
|
|
+{
|
|
+ struct nouveau_bo *nvbo = gem->driver_private;
|
|
+ struct ttm_buffer_object *bo = &nvbo->bo;
|
|
+
|
|
+ if (!nvbo)
|
|
+ return;
|
|
+ nvbo->gem = NULL;
|
|
+
|
|
+ if (unlikely(nvbo->cpu_filp))
|
|
+ ttm_bo_synccpu_write_release(bo);
|
|
+
|
|
+ if (unlikely(nvbo->pin_refcnt)) {
|
|
+ nvbo->pin_refcnt = 1;
|
|
+ nouveau_bo_unpin(nvbo);
|
|
+ }
|
|
+
|
|
+ ttm_bo_unref(&bo);
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_gem_new(struct drm_device *dev, struct nouveau_channel *chan,
|
|
+ int size, int align, uint32_t flags, uint32_t tile_mode,
|
|
+ uint32_t tile_flags, bool no_vm, bool mappable,
|
|
+ struct nouveau_bo **pnvbo)
|
|
+{
|
|
+ struct nouveau_bo *nvbo;
|
|
+ int ret;
|
|
+
|
|
+ ret = nouveau_bo_new(dev, chan, size, align, flags, tile_mode,
|
|
+ tile_flags, no_vm, mappable, pnvbo);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ nvbo = *pnvbo;
|
|
+
|
|
+ nvbo->gem = drm_gem_object_alloc(dev, nvbo->bo.mem.size);
|
|
+ if (!nvbo->gem) {
|
|
+ nouveau_bo_ref(NULL, pnvbo);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ nvbo->bo.persistant_swap_storage = nvbo->gem->filp;
|
|
+ nvbo->gem->driver_private = nvbo;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_gem_info(struct drm_gem_object *gem, struct drm_nouveau_gem_info *rep)
|
|
+{
|
|
+ struct nouveau_bo *nvbo = nouveau_gem_object(gem);
|
|
+
|
|
+ if (nvbo->bo.mem.mem_type == TTM_PL_TT)
|
|
+ rep->domain = NOUVEAU_GEM_DOMAIN_GART;
|
|
+ else
|
|
+ rep->domain = NOUVEAU_GEM_DOMAIN_VRAM;
|
|
+
|
|
+ rep->size = nvbo->bo.mem.num_pages << PAGE_SHIFT;
|
|
+ rep->offset = nvbo->bo.offset;
|
|
+ rep->map_handle = nvbo->mappable ? nvbo->bo.addr_space_offset : 0;
|
|
+ rep->tile_mode = nvbo->tile_mode;
|
|
+ rep->tile_flags = nvbo->tile_flags;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static bool
|
|
+nouveau_gem_tile_flags_valid(struct drm_device *dev, uint32_t tile_flags) {
|
|
+ switch (tile_flags) {
|
|
+ case 0x0000:
|
|
+ case 0x1800:
|
|
+ case 0x2800:
|
|
+ case 0x4800:
|
|
+ case 0x7000:
|
|
+ case 0x7400:
|
|
+ case 0x7a00:
|
|
+ case 0xe000:
|
|
+ break;
|
|
+ default:
|
|
+ NV_ERROR(dev, "bad page flags: 0x%08x\n", tile_flags);
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_gem_ioctl_new(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct drm_nouveau_gem_new *req = data;
|
|
+ struct nouveau_bo *nvbo = NULL;
|
|
+ struct nouveau_channel *chan = NULL;
|
|
+ uint32_t flags = 0;
|
|
+ int ret = 0;
|
|
+
|
|
+ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
|
|
+
|
|
+ if (unlikely(dev_priv->ttm.bdev.dev_mapping == NULL))
|
|
+ dev_priv->ttm.bdev.dev_mapping = dev_priv->dev->dev_mapping;
|
|
+
|
|
+ if (req->channel_hint) {
|
|
+ NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(req->channel_hint,
|
|
+ file_priv, chan);
|
|
+ }
|
|
+
|
|
+ if (req->info.domain & NOUVEAU_GEM_DOMAIN_VRAM)
|
|
+ flags |= TTM_PL_FLAG_VRAM;
|
|
+ if (req->info.domain & NOUVEAU_GEM_DOMAIN_GART)
|
|
+ flags |= TTM_PL_FLAG_TT;
|
|
+ if (!flags || req->info.domain & NOUVEAU_GEM_DOMAIN_CPU)
|
|
+ flags |= TTM_PL_FLAG_SYSTEM;
|
|
+
|
|
+ if (!nouveau_gem_tile_flags_valid(dev, req->info.tile_flags))
|
|
+ return -EINVAL;
|
|
+
|
|
+ ret = nouveau_gem_new(dev, chan, req->info.size, req->align, flags,
|
|
+ req->info.tile_mode, req->info.tile_flags, false,
|
|
+ (req->info.domain & NOUVEAU_GEM_DOMAIN_MAPPABLE),
|
|
+ &nvbo);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = nouveau_gem_info(nvbo->gem, &req->info);
|
|
+ if (ret)
|
|
+ goto out;
|
|
+
|
|
+ ret = drm_gem_handle_create(file_priv, nvbo->gem, &req->info.handle);
|
|
+out:
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
+ drm_gem_object_handle_unreference(nvbo->gem);
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+
|
|
+ if (ret)
|
|
+ drm_gem_object_unreference(nvbo->gem);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_gem_set_domain(struct drm_gem_object *gem, uint32_t read_domains,
|
|
+ uint32_t write_domains, uint32_t valid_domains)
|
|
+{
|
|
+ struct nouveau_bo *nvbo = gem->driver_private;
|
|
+ struct ttm_buffer_object *bo = &nvbo->bo;
|
|
+ uint64_t flags;
|
|
+
|
|
+ if (!valid_domains || (!read_domains && !write_domains))
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (write_domains) {
|
|
+ if ((valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
|
|
+ (write_domains & NOUVEAU_GEM_DOMAIN_VRAM))
|
|
+ flags = TTM_PL_FLAG_VRAM;
|
|
+ else
|
|
+ if ((valid_domains & NOUVEAU_GEM_DOMAIN_GART) &&
|
|
+ (write_domains & NOUVEAU_GEM_DOMAIN_GART))
|
|
+ flags = TTM_PL_FLAG_TT;
|
|
+ else
|
|
+ return -EINVAL;
|
|
+ } else {
|
|
+ if ((valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
|
|
+ (read_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
|
|
+ bo->mem.mem_type == TTM_PL_VRAM)
|
|
+ flags = TTM_PL_FLAG_VRAM;
|
|
+ else
|
|
+ if ((valid_domains & NOUVEAU_GEM_DOMAIN_GART) &&
|
|
+ (read_domains & NOUVEAU_GEM_DOMAIN_GART) &&
|
|
+ bo->mem.mem_type == TTM_PL_TT)
|
|
+ flags = TTM_PL_FLAG_TT;
|
|
+ else
|
|
+ if ((valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
|
|
+ (read_domains & NOUVEAU_GEM_DOMAIN_VRAM))
|
|
+ flags = TTM_PL_FLAG_VRAM;
|
|
+ else
|
|
+ flags = TTM_PL_FLAG_TT;
|
|
+ }
|
|
+
|
|
+ nouveau_bo_placement_set(nvbo, flags);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+struct validate_op {
|
|
+ struct list_head vram_list;
|
|
+ struct list_head gart_list;
|
|
+ struct list_head both_list;
|
|
+};
|
|
+
|
|
+static void
|
|
+validate_fini_list(struct list_head *list, struct nouveau_fence *fence)
|
|
+{
|
|
+ struct list_head *entry, *tmp;
|
|
+ struct nouveau_bo *nvbo;
|
|
+
|
|
+ list_for_each_safe(entry, tmp, list) {
|
|
+ nvbo = list_entry(entry, struct nouveau_bo, entry);
|
|
+ if (likely(fence)) {
|
|
+ struct nouveau_fence *prev_fence;
|
|
+
|
|
+ spin_lock(&nvbo->bo.lock);
|
|
+ prev_fence = nvbo->bo.sync_obj;
|
|
+ nvbo->bo.sync_obj = nouveau_fence_ref(fence);
|
|
+ spin_unlock(&nvbo->bo.lock);
|
|
+ nouveau_fence_unref((void *)&prev_fence);
|
|
+ }
|
|
+
|
|
+ list_del(&nvbo->entry);
|
|
+ nvbo->reserved_by = NULL;
|
|
+ ttm_bo_unreserve(&nvbo->bo);
|
|
+ drm_gem_object_unreference(nvbo->gem);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+validate_fini(struct validate_op *op, struct nouveau_fence* fence)
|
|
+{
|
|
+ validate_fini_list(&op->vram_list, fence);
|
|
+ validate_fini_list(&op->gart_list, fence);
|
|
+ validate_fini_list(&op->both_list, fence);
|
|
+}
|
|
+
|
|
+static int
|
|
+validate_init(struct nouveau_channel *chan, struct drm_file *file_priv,
|
|
+ struct drm_nouveau_gem_pushbuf_bo *pbbo,
|
|
+ int nr_buffers, struct validate_op *op)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t sequence;
|
|
+ int trycnt = 0;
|
|
+ int ret, i;
|
|
+
|
|
+ sequence = atomic_add_return(1, &dev_priv->ttm.validate_sequence);
|
|
+retry:
|
|
+ if (++trycnt > 100000) {
|
|
+ NV_ERROR(dev, "%s failed and gave up.\n", __func__);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < nr_buffers; i++) {
|
|
+ struct drm_nouveau_gem_pushbuf_bo *b = &pbbo[i];
|
|
+ struct drm_gem_object *gem;
|
|
+ struct nouveau_bo *nvbo;
|
|
+
|
|
+ gem = drm_gem_object_lookup(dev, file_priv, b->handle);
|
|
+ if (!gem) {
|
|
+ NV_ERROR(dev, "Unknown handle 0x%08x\n", b->handle);
|
|
+ validate_fini(op, NULL);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ nvbo = gem->driver_private;
|
|
+
|
|
+ if (nvbo->reserved_by && nvbo->reserved_by == file_priv) {
|
|
+ NV_ERROR(dev, "multiple instances of buffer %d on "
|
|
+ "validation list\n", b->handle);
|
|
+ validate_fini(op, NULL);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ret = ttm_bo_reserve(&nvbo->bo, false, false, true, sequence);
|
|
+ if (ret) {
|
|
+ validate_fini(op, NULL);
|
|
+ if (ret == -EAGAIN)
|
|
+ ret = ttm_bo_wait_unreserved(&nvbo->bo, false);
|
|
+ drm_gem_object_unreference(gem);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ goto retry;
|
|
+ }
|
|
+
|
|
+ nvbo->reserved_by = file_priv;
|
|
+ nvbo->pbbo_index = i;
|
|
+ if ((b->valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
|
|
+ (b->valid_domains & NOUVEAU_GEM_DOMAIN_GART))
|
|
+ list_add_tail(&nvbo->entry, &op->both_list);
|
|
+ else
|
|
+ if (b->valid_domains & NOUVEAU_GEM_DOMAIN_VRAM)
|
|
+ list_add_tail(&nvbo->entry, &op->vram_list);
|
|
+ else
|
|
+ if (b->valid_domains & NOUVEAU_GEM_DOMAIN_GART)
|
|
+ list_add_tail(&nvbo->entry, &op->gart_list);
|
|
+ else {
|
|
+ NV_ERROR(dev, "invalid valid domains: 0x%08x\n",
|
|
+ b->valid_domains);
|
|
+ list_add_tail(&nvbo->entry, &op->both_list);
|
|
+ validate_fini(op, NULL);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (unlikely(atomic_read(&nvbo->bo.cpu_writers) > 0)) {
|
|
+ validate_fini(op, NULL);
|
|
+
|
|
+ if (nvbo->cpu_filp == file_priv) {
|
|
+ NV_ERROR(dev, "bo %p mapped by process trying "
|
|
+ "to validate it!\n", nvbo);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ret = ttm_bo_wait_cpu(&nvbo->bo, false);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ goto retry;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+validate_list(struct nouveau_channel *chan, struct list_head *list,
|
|
+ struct drm_nouveau_gem_pushbuf_bo *pbbo, uint64_t user_pbbo_ptr)
|
|
+{
|
|
+ struct drm_nouveau_gem_pushbuf_bo __user *upbbo =
|
|
+ (void __force __user *)(uintptr_t)user_pbbo_ptr;
|
|
+ struct nouveau_bo *nvbo;
|
|
+ int ret, relocs = 0;
|
|
+
|
|
+ list_for_each_entry(nvbo, list, entry) {
|
|
+ struct drm_nouveau_gem_pushbuf_bo *b = &pbbo[nvbo->pbbo_index];
|
|
+ struct nouveau_fence *prev_fence = nvbo->bo.sync_obj;
|
|
+
|
|
+ if (prev_fence && nouveau_fence_channel(prev_fence) != chan) {
|
|
+ spin_lock(&nvbo->bo.lock);
|
|
+ ret = ttm_bo_wait(&nvbo->bo, false, false, false);
|
|
+ spin_unlock(&nvbo->bo.lock);
|
|
+ if (unlikely(ret))
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = nouveau_gem_set_domain(nvbo->gem, b->read_domains,
|
|
+ b->write_domains,
|
|
+ b->valid_domains);
|
|
+ if (unlikely(ret))
|
|
+ return ret;
|
|
+
|
|
+ nvbo->channel = chan;
|
|
+ ret = ttm_bo_validate(&nvbo->bo, &nvbo->placement,
|
|
+ false, false);
|
|
+ nvbo->channel = NULL;
|
|
+ if (unlikely(ret))
|
|
+ return ret;
|
|
+
|
|
+ if (nvbo->bo.offset == b->presumed_offset &&
|
|
+ ((nvbo->bo.mem.mem_type == TTM_PL_VRAM &&
|
|
+ b->presumed_domain & NOUVEAU_GEM_DOMAIN_VRAM) ||
|
|
+ (nvbo->bo.mem.mem_type == TTM_PL_TT &&
|
|
+ b->presumed_domain & NOUVEAU_GEM_DOMAIN_GART)))
|
|
+ continue;
|
|
+
|
|
+ if (nvbo->bo.mem.mem_type == TTM_PL_TT)
|
|
+ b->presumed_domain = NOUVEAU_GEM_DOMAIN_GART;
|
|
+ else
|
|
+ b->presumed_domain = NOUVEAU_GEM_DOMAIN_VRAM;
|
|
+ b->presumed_offset = nvbo->bo.offset;
|
|
+ b->presumed_ok = 0;
|
|
+ relocs++;
|
|
+
|
|
+ if (DRM_COPY_TO_USER(&upbbo[nvbo->pbbo_index], b, sizeof(*b)))
|
|
+ return -EFAULT;
|
|
+ }
|
|
+
|
|
+ return relocs;
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_gem_pushbuf_validate(struct nouveau_channel *chan,
|
|
+ struct drm_file *file_priv,
|
|
+ struct drm_nouveau_gem_pushbuf_bo *pbbo,
|
|
+ uint64_t user_buffers, int nr_buffers,
|
|
+ struct validate_op *op, int *apply_relocs)
|
|
+{
|
|
+ int ret, relocs = 0;
|
|
+
|
|
+ INIT_LIST_HEAD(&op->vram_list);
|
|
+ INIT_LIST_HEAD(&op->gart_list);
|
|
+ INIT_LIST_HEAD(&op->both_list);
|
|
+
|
|
+ if (nr_buffers == 0)
|
|
+ return 0;
|
|
+
|
|
+ ret = validate_init(chan, file_priv, pbbo, nr_buffers, op);
|
|
+ if (unlikely(ret))
|
|
+ return ret;
|
|
+
|
|
+ ret = validate_list(chan, &op->vram_list, pbbo, user_buffers);
|
|
+ if (unlikely(ret < 0)) {
|
|
+ validate_fini(op, NULL);
|
|
+ return ret;
|
|
+ }
|
|
+ relocs += ret;
|
|
+
|
|
+ ret = validate_list(chan, &op->gart_list, pbbo, user_buffers);
|
|
+ if (unlikely(ret < 0)) {
|
|
+ validate_fini(op, NULL);
|
|
+ return ret;
|
|
+ }
|
|
+ relocs += ret;
|
|
+
|
|
+ ret = validate_list(chan, &op->both_list, pbbo, user_buffers);
|
|
+ if (unlikely(ret < 0)) {
|
|
+ validate_fini(op, NULL);
|
|
+ return ret;
|
|
+ }
|
|
+ relocs += ret;
|
|
+
|
|
+ *apply_relocs = relocs;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline void *
|
|
+u_memcpya(uint64_t user, unsigned nmemb, unsigned size)
|
|
+{
|
|
+ void *mem;
|
|
+ void __user *userptr = (void __force __user *)(uintptr_t)user;
|
|
+
|
|
+ mem = kmalloc(nmemb * size, GFP_KERNEL);
|
|
+ if (!mem)
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+
|
|
+ if (DRM_COPY_FROM_USER(mem, userptr, nmemb * size)) {
|
|
+ kfree(mem);
|
|
+ return ERR_PTR(-EFAULT);
|
|
+ }
|
|
+
|
|
+ return mem;
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_gem_pushbuf_reloc_apply(struct nouveau_channel *chan, int nr_bo,
|
|
+ struct drm_nouveau_gem_pushbuf_bo *bo,
|
|
+ unsigned nr_relocs, uint64_t ptr_relocs,
|
|
+ unsigned nr_dwords, unsigned first_dword,
|
|
+ uint32_t *pushbuf, bool is_iomem)
|
|
+{
|
|
+ struct drm_nouveau_gem_pushbuf_reloc *reloc = NULL;
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ int ret = 0;
|
|
+ unsigned i;
|
|
+
|
|
+ reloc = u_memcpya(ptr_relocs, nr_relocs, sizeof(*reloc));
|
|
+ if (IS_ERR(reloc))
|
|
+ return PTR_ERR(reloc);
|
|
+
|
|
+ for (i = 0; i < nr_relocs; i++) {
|
|
+ struct drm_nouveau_gem_pushbuf_reloc *r = &reloc[i];
|
|
+ struct drm_nouveau_gem_pushbuf_bo *b;
|
|
+ uint32_t data;
|
|
+
|
|
+ if (r->bo_index >= nr_bo || r->reloc_index < first_dword ||
|
|
+ r->reloc_index >= first_dword + nr_dwords) {
|
|
+ NV_ERROR(dev, "Bad relocation %d\n", i);
|
|
+ NV_ERROR(dev, " bo: %d max %d\n", r->bo_index, nr_bo);
|
|
+ NV_ERROR(dev, " id: %d max %d\n", r->reloc_index, nr_dwords);
|
|
+ ret = -EINVAL;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ b = &bo[r->bo_index];
|
|
+ if (b->presumed_ok)
|
|
+ continue;
|
|
+
|
|
+ if (r->flags & NOUVEAU_GEM_RELOC_LOW)
|
|
+ data = b->presumed_offset + r->data;
|
|
+ else
|
|
+ if (r->flags & NOUVEAU_GEM_RELOC_HIGH)
|
|
+ data = (b->presumed_offset + r->data) >> 32;
|
|
+ else
|
|
+ data = r->data;
|
|
+
|
|
+ if (r->flags & NOUVEAU_GEM_RELOC_OR) {
|
|
+ if (b->presumed_domain == NOUVEAU_GEM_DOMAIN_GART)
|
|
+ data |= r->tor;
|
|
+ else
|
|
+ data |= r->vor;
|
|
+ }
|
|
+
|
|
+ if (is_iomem)
|
|
+ iowrite32_native(data, (void __force __iomem *)
|
|
+ &pushbuf[r->reloc_index]);
|
|
+ else
|
|
+ pushbuf[r->reloc_index] = data;
|
|
+ }
|
|
+
|
|
+ kfree(reloc);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_nouveau_gem_pushbuf *req = data;
|
|
+ struct drm_nouveau_gem_pushbuf_bo *bo = NULL;
|
|
+ struct nouveau_channel *chan;
|
|
+ struct validate_op op;
|
|
+ struct nouveau_fence* fence = 0;
|
|
+ uint32_t *pushbuf = NULL;
|
|
+ int ret = 0, do_reloc = 0, i;
|
|
+
|
|
+ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
|
|
+ NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(req->channel, file_priv, chan);
|
|
+
|
|
+ if (req->nr_dwords >= chan->dma.max ||
|
|
+ req->nr_buffers > NOUVEAU_GEM_MAX_BUFFERS ||
|
|
+ req->nr_relocs > NOUVEAU_GEM_MAX_RELOCS) {
|
|
+ NV_ERROR(dev, "Pushbuf config exceeds limits:\n");
|
|
+ NV_ERROR(dev, " dwords : %d max %d\n", req->nr_dwords,
|
|
+ chan->dma.max - 1);
|
|
+ NV_ERROR(dev, " buffers: %d max %d\n", req->nr_buffers,
|
|
+ NOUVEAU_GEM_MAX_BUFFERS);
|
|
+ NV_ERROR(dev, " relocs : %d max %d\n", req->nr_relocs,
|
|
+ NOUVEAU_GEM_MAX_RELOCS);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ pushbuf = u_memcpya(req->dwords, req->nr_dwords, sizeof(uint32_t));
|
|
+ if (IS_ERR(pushbuf))
|
|
+ return PTR_ERR(pushbuf);
|
|
+
|
|
+ bo = u_memcpya(req->buffers, req->nr_buffers, sizeof(*bo));
|
|
+ if (IS_ERR(bo)) {
|
|
+ kfree(pushbuf);
|
|
+ return PTR_ERR(bo);
|
|
+ }
|
|
+
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
+
|
|
+ /* Validate buffer list */
|
|
+ ret = nouveau_gem_pushbuf_validate(chan, file_priv, bo, req->buffers,
|
|
+ req->nr_buffers, &op, &do_reloc);
|
|
+ if (ret)
|
|
+ goto out;
|
|
+
|
|
+ /* Apply any relocations that are required */
|
|
+ if (do_reloc) {
|
|
+ ret = nouveau_gem_pushbuf_reloc_apply(chan, req->nr_buffers,
|
|
+ bo, req->nr_relocs,
|
|
+ req->relocs,
|
|
+ req->nr_dwords, 0,
|
|
+ pushbuf, false);
|
|
+ if (ret)
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /* Emit push buffer to the hw
|
|
+ */
|
|
+ ret = RING_SPACE(chan, req->nr_dwords);
|
|
+ if (ret)
|
|
+ goto out;
|
|
+
|
|
+ OUT_RINGp(chan, pushbuf, req->nr_dwords);
|
|
+
|
|
+ ret = nouveau_fence_new(chan, &fence, true);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "error fencing pushbuf: %d\n", ret);
|
|
+ WIND_RING(chan);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (nouveau_gem_pushbuf_sync(chan)) {
|
|
+ ret = nouveau_fence_wait(fence, NULL, false, false);
|
|
+ if (ret) {
|
|
+ for (i = 0; i < req->nr_dwords; i++)
|
|
+ NV_ERROR(dev, "0x%08x\n", pushbuf[i]);
|
|
+ NV_ERROR(dev, "^^ above push buffer is fail :(\n");
|
|
+ }
|
|
+ }
|
|
+
|
|
+out:
|
|
+ validate_fini(&op, fence);
|
|
+ nouveau_fence_unref((void**)&fence);
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+ kfree(pushbuf);
|
|
+ kfree(bo);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+#define PUSHBUF_CAL (dev_priv->card_type >= NV_20)
|
|
+
|
|
+int
|
|
+nouveau_gem_ioctl_pushbuf_call(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct drm_nouveau_gem_pushbuf_call *req = data;
|
|
+ struct drm_nouveau_gem_pushbuf_bo *bo = NULL;
|
|
+ struct nouveau_channel *chan;
|
|
+ struct drm_gem_object *gem;
|
|
+ struct nouveau_bo *pbbo;
|
|
+ struct validate_op op;
|
|
+ struct nouveau_fence* fence = 0;
|
|
+ int i, ret = 0, do_reloc = 0;
|
|
+
|
|
+ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
|
|
+ NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(req->channel, file_priv, chan);
|
|
+
|
|
+ if (unlikely(req->handle == 0))
|
|
+ goto out_next;
|
|
+
|
|
+ if (req->nr_buffers > NOUVEAU_GEM_MAX_BUFFERS ||
|
|
+ req->nr_relocs > NOUVEAU_GEM_MAX_RELOCS) {
|
|
+ NV_ERROR(dev, "Pushbuf config exceeds limits:\n");
|
|
+ NV_ERROR(dev, " buffers: %d max %d\n", req->nr_buffers,
|
|
+ NOUVEAU_GEM_MAX_BUFFERS);
|
|
+ NV_ERROR(dev, " relocs : %d max %d\n", req->nr_relocs,
|
|
+ NOUVEAU_GEM_MAX_RELOCS);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ bo = u_memcpya(req->buffers, req->nr_buffers, sizeof(*bo));
|
|
+ if (IS_ERR(bo))
|
|
+ return PTR_ERR(bo);
|
|
+
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
+
|
|
+ /* Validate buffer list */
|
|
+ ret = nouveau_gem_pushbuf_validate(chan, file_priv, bo, req->buffers,
|
|
+ req->nr_buffers, &op, &do_reloc);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "validate: %d\n", ret);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /* Validate DMA push buffer */
|
|
+ gem = drm_gem_object_lookup(dev, file_priv, req->handle);
|
|
+ if (!gem) {
|
|
+ NV_ERROR(dev, "Unknown pb handle 0x%08x\n", req->handle);
|
|
+ ret = -EINVAL;
|
|
+ goto out;
|
|
+ }
|
|
+ pbbo = nouveau_gem_object(gem);
|
|
+
|
|
+ if ((req->offset & 3) || req->nr_dwords < 2 ||
|
|
+ (unsigned long)req->offset > (unsigned long)pbbo->bo.mem.size ||
|
|
+ (unsigned long)req->nr_dwords >
|
|
+ ((unsigned long)(pbbo->bo.mem.size - req->offset ) >> 2)) {
|
|
+ NV_ERROR(dev, "pb call misaligned or out of bounds: "
|
|
+ "%d + %d * 4 > %ld\n",
|
|
+ req->offset, req->nr_dwords, pbbo->bo.mem.size);
|
|
+ ret = -EINVAL;
|
|
+ drm_gem_object_unreference(gem);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ ret = ttm_bo_reserve(&pbbo->bo, false, false, true,
|
|
+ chan->fence.sequence);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "resv pb: %d\n", ret);
|
|
+ drm_gem_object_unreference(gem);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ nouveau_bo_placement_set(pbbo, 1 << chan->pushbuf_bo->bo.mem.mem_type);
|
|
+ ret = ttm_bo_validate(&pbbo->bo, &pbbo->placement, false, false);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "validate pb: %d\n", ret);
|
|
+ ttm_bo_unreserve(&pbbo->bo);
|
|
+ drm_gem_object_unreference(gem);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ list_add_tail(&pbbo->entry, &op.both_list);
|
|
+
|
|
+ /* If presumed return address doesn't match, we need to map the
|
|
+ * push buffer and fix it..
|
|
+ */
|
|
+ if (!PUSHBUF_CAL) {
|
|
+ uint32_t retaddy;
|
|
+
|
|
+ if (chan->dma.free < 4 + NOUVEAU_DMA_SKIPS) {
|
|
+ ret = nouveau_dma_wait(chan, 4 + NOUVEAU_DMA_SKIPS);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "jmp_space: %d\n", ret);
|
|
+ goto out;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ retaddy = chan->pushbuf_base + ((chan->dma.cur + 2) << 2);
|
|
+ retaddy |= 0x20000000;
|
|
+ if (retaddy != req->suffix0) {
|
|
+ req->suffix0 = retaddy;
|
|
+ do_reloc = 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Apply any relocations that are required */
|
|
+ if (do_reloc) {
|
|
+ void *pbvirt;
|
|
+ bool is_iomem;
|
|
+ ret = ttm_bo_kmap(&pbbo->bo, 0, pbbo->bo.mem.num_pages,
|
|
+ &pbbo->kmap);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "kmap pb: %d\n", ret);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ pbvirt = ttm_kmap_obj_virtual(&pbbo->kmap, &is_iomem);
|
|
+ ret = nouveau_gem_pushbuf_reloc_apply(chan, req->nr_buffers, bo,
|
|
+ req->nr_relocs,
|
|
+ req->relocs,
|
|
+ req->nr_dwords,
|
|
+ req->offset / 4,
|
|
+ pbvirt, is_iomem);
|
|
+
|
|
+ if (!PUSHBUF_CAL) {
|
|
+ nouveau_bo_wr32(pbbo,
|
|
+ req->offset / 4 + req->nr_dwords - 2,
|
|
+ req->suffix0);
|
|
+ }
|
|
+
|
|
+ ttm_bo_kunmap(&pbbo->kmap);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "reloc apply: %d\n", ret);
|
|
+ goto out;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (PUSHBUF_CAL) {
|
|
+ ret = RING_SPACE(chan, 2);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "cal_space: %d\n", ret);
|
|
+ goto out;
|
|
+ }
|
|
+ OUT_RING(chan, ((pbbo->bo.mem.mm_node->start << PAGE_SHIFT) +
|
|
+ req->offset) | 2);
|
|
+ OUT_RING(chan, 0);
|
|
+ } else {
|
|
+ ret = RING_SPACE(chan, 2 + NOUVEAU_DMA_SKIPS);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "jmp_space: %d\n", ret);
|
|
+ goto out;
|
|
+ }
|
|
+ OUT_RING(chan, ((pbbo->bo.mem.mm_node->start << PAGE_SHIFT) +
|
|
+ req->offset) | 0x20000000);
|
|
+ OUT_RING(chan, 0);
|
|
+
|
|
+ /* Space the jumps apart with NOPs. */
|
|
+ for (i = 0; i < NOUVEAU_DMA_SKIPS; i++)
|
|
+ OUT_RING(chan, 0);
|
|
+ }
|
|
+
|
|
+ ret = nouveau_fence_new(chan, &fence, true);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "error fencing pushbuf: %d\n", ret);
|
|
+ WIND_RING(chan);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+out:
|
|
+ validate_fini(&op, fence);
|
|
+ nouveau_fence_unref((void**)&fence);
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+ kfree(bo);
|
|
+
|
|
+out_next:
|
|
+ if (PUSHBUF_CAL) {
|
|
+ req->suffix0 = 0x00020000;
|
|
+ req->suffix1 = 0x00000000;
|
|
+ } else {
|
|
+ req->suffix0 = 0x20000000 |
|
|
+ (chan->pushbuf_base + ((chan->dma.cur + 2) << 2));
|
|
+ req->suffix1 = 0x00000000;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_gem_ioctl_pushbuf_call2(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct drm_nouveau_gem_pushbuf_call *req = data;
|
|
+
|
|
+ req->vram_available = dev_priv->fb_aper_free;
|
|
+ req->gart_available = dev_priv->gart_info.aper_free;
|
|
+
|
|
+ return nouveau_gem_ioctl_pushbuf_call(dev, data, file_priv);
|
|
+}
|
|
+
|
|
+static inline uint32_t
|
|
+domain_to_ttm(struct nouveau_bo *nvbo, uint32_t domain)
|
|
+{
|
|
+ uint32_t flags = 0;
|
|
+
|
|
+ if (domain & NOUVEAU_GEM_DOMAIN_VRAM)
|
|
+ flags |= TTM_PL_FLAG_VRAM;
|
|
+ if (domain & NOUVEAU_GEM_DOMAIN_GART)
|
|
+ flags |= TTM_PL_FLAG_TT;
|
|
+
|
|
+ return flags;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_gem_ioctl_pin(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_nouveau_gem_pin *req = data;
|
|
+ struct drm_gem_object *gem;
|
|
+ struct nouveau_bo *nvbo;
|
|
+ int ret = 0;
|
|
+
|
|
+ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
|
|
+
|
|
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
|
+ NV_ERROR(dev, "pin only allowed without kernel modesetting\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (!DRM_SUSER(DRM_CURPROC))
|
|
+ return -EPERM;
|
|
+
|
|
+ gem = drm_gem_object_lookup(dev, file_priv, req->handle);
|
|
+ if (!gem)
|
|
+ return -EINVAL;
|
|
+ nvbo = nouveau_gem_object(gem);
|
|
+
|
|
+ ret = nouveau_bo_pin(nvbo, domain_to_ttm(nvbo, req->domain));
|
|
+ if (ret)
|
|
+ goto out;
|
|
+
|
|
+ req->offset = nvbo->bo.offset;
|
|
+ if (nvbo->bo.mem.mem_type == TTM_PL_TT)
|
|
+ req->domain = NOUVEAU_GEM_DOMAIN_GART;
|
|
+ else
|
|
+ req->domain = NOUVEAU_GEM_DOMAIN_VRAM;
|
|
+
|
|
+out:
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
+ drm_gem_object_unreference(gem);
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_gem_ioctl_unpin(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_nouveau_gem_pin *req = data;
|
|
+ struct drm_gem_object *gem;
|
|
+ int ret;
|
|
+
|
|
+ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
|
|
+
|
|
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
|
|
+ return -EINVAL;
|
|
+
|
|
+ gem = drm_gem_object_lookup(dev, file_priv, req->handle);
|
|
+ if (!gem)
|
|
+ return -EINVAL;
|
|
+
|
|
+ ret = nouveau_bo_unpin(nouveau_gem_object(gem));
|
|
+
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
+ drm_gem_object_unreference(gem);
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_nouveau_gem_cpu_prep *req = data;
|
|
+ struct drm_gem_object *gem;
|
|
+ struct nouveau_bo *nvbo;
|
|
+ bool no_wait = !!(req->flags & NOUVEAU_GEM_CPU_PREP_NOWAIT);
|
|
+ int ret = -EINVAL;
|
|
+
|
|
+ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
|
|
+
|
|
+ gem = drm_gem_object_lookup(dev, file_priv, req->handle);
|
|
+ if (!gem)
|
|
+ return ret;
|
|
+ nvbo = nouveau_gem_object(gem);
|
|
+
|
|
+ if (nvbo->cpu_filp) {
|
|
+ if (nvbo->cpu_filp == file_priv)
|
|
+ goto out;
|
|
+
|
|
+ ret = ttm_bo_wait_cpu(&nvbo->bo, no_wait);
|
|
+ if (ret)
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (req->flags & NOUVEAU_GEM_CPU_PREP_NOBLOCK) {
|
|
+ spin_lock(&nvbo->bo.lock);
|
|
+ ret = ttm_bo_wait(&nvbo->bo, false, false, no_wait);
|
|
+ spin_unlock(&nvbo->bo.lock);
|
|
+ } else {
|
|
+ ret = ttm_bo_synccpu_write_grab(&nvbo->bo, no_wait);
|
|
+ if (ret == 0)
|
|
+ nvbo->cpu_filp = file_priv;
|
|
+ }
|
|
+
|
|
+out:
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
+ drm_gem_object_unreference(gem);
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_gem_ioctl_cpu_fini(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_nouveau_gem_cpu_prep *req = data;
|
|
+ struct drm_gem_object *gem;
|
|
+ struct nouveau_bo *nvbo;
|
|
+ int ret = -EINVAL;
|
|
+
|
|
+ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
|
|
+
|
|
+ gem = drm_gem_object_lookup(dev, file_priv, req->handle);
|
|
+ if (!gem)
|
|
+ return ret;
|
|
+ nvbo = nouveau_gem_object(gem);
|
|
+
|
|
+ if (nvbo->cpu_filp != file_priv)
|
|
+ goto out;
|
|
+ nvbo->cpu_filp = NULL;
|
|
+
|
|
+ ttm_bo_synccpu_write_release(&nvbo->bo);
|
|
+ ret = 0;
|
|
+
|
|
+out:
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
+ drm_gem_object_unreference(gem);
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_gem_ioctl_info(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_nouveau_gem_info *req = data;
|
|
+ struct drm_gem_object *gem;
|
|
+ int ret;
|
|
+
|
|
+ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
|
|
+
|
|
+ gem = drm_gem_object_lookup(dev, file_priv, req->handle);
|
|
+ if (!gem)
|
|
+ return -EINVAL;
|
|
+
|
|
+ ret = nouveau_gem_info(gem, req);
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
+ drm_gem_object_unreference(gem);
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_grctx.c b/drivers/gpu/drm/nouveau/nouveau_grctx.c
|
|
new file mode 100644
|
|
index 0000000..c7ebec6
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_grctx.c
|
|
@@ -0,0 +1,161 @@
|
|
+/*
|
|
+ * Copyright 2009 Red Hat Inc.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice shall be included in
|
|
+ * all copies or substantial portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
+ * OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ * Authors: Ben Skeggs
|
|
+ */
|
|
+
|
|
+#include <linux/firmware.h>
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "nouveau_drv.h"
|
|
+
|
|
+struct nouveau_ctxprog {
|
|
+ uint32_t signature;
|
|
+ uint8_t version;
|
|
+ uint16_t length;
|
|
+ uint32_t data[];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct nouveau_ctxvals {
|
|
+ uint32_t signature;
|
|
+ uint8_t version;
|
|
+ uint32_t length;
|
|
+ struct {
|
|
+ uint32_t offset;
|
|
+ uint32_t value;
|
|
+ } data[];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+int
|
|
+nouveau_grctx_prog_load(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
|
|
+ const int chipset = dev_priv->chipset;
|
|
+ const struct firmware *fw;
|
|
+ const struct nouveau_ctxprog *cp;
|
|
+ const struct nouveau_ctxvals *cv;
|
|
+ char name[32];
|
|
+ int ret, i;
|
|
+
|
|
+ if (pgraph->accel_blocked)
|
|
+ return -ENODEV;
|
|
+
|
|
+ if (!pgraph->ctxprog) {
|
|
+ sprintf(name, "nouveau/nv%02x.ctxprog", chipset);
|
|
+ ret = request_firmware(&fw, name, &dev->pdev->dev);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "No ctxprog for NV%02x\n", chipset);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ pgraph->ctxprog = kmalloc(fw->size, GFP_KERNEL);
|
|
+ if (!pgraph->ctxprog) {
|
|
+ NV_ERROR(dev, "OOM copying ctxprog\n");
|
|
+ release_firmware(fw);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ memcpy(pgraph->ctxprog, fw->data, fw->size);
|
|
+
|
|
+ cp = pgraph->ctxprog;
|
|
+ if (le32_to_cpu(cp->signature) != 0x5043564e ||
|
|
+ cp->version != 0 ||
|
|
+ le16_to_cpu(cp->length) != ((fw->size - 7) / 4)) {
|
|
+ NV_ERROR(dev, "ctxprog invalid\n");
|
|
+ release_firmware(fw);
|
|
+ nouveau_grctx_fini(dev);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ release_firmware(fw);
|
|
+ }
|
|
+
|
|
+ if (!pgraph->ctxvals) {
|
|
+ sprintf(name, "nouveau/nv%02x.ctxvals", chipset);
|
|
+ ret = request_firmware(&fw, name, &dev->pdev->dev);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "No ctxvals for NV%02x\n", chipset);
|
|
+ nouveau_grctx_fini(dev);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ pgraph->ctxvals = kmalloc(fw->size, GFP_KERNEL);
|
|
+ if (!pgraph->ctxvals) {
|
|
+ NV_ERROR(dev, "OOM copying ctxvals\n");
|
|
+ release_firmware(fw);
|
|
+ nouveau_grctx_fini(dev);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ memcpy(pgraph->ctxvals, fw->data, fw->size);
|
|
+
|
|
+ cv = (void *)pgraph->ctxvals;
|
|
+ if (le32_to_cpu(cv->signature) != 0x5643564e ||
|
|
+ cv->version != 0 ||
|
|
+ le32_to_cpu(cv->length) != ((fw->size - 9) / 8)) {
|
|
+ NV_ERROR(dev, "ctxvals invalid\n");
|
|
+ release_firmware(fw);
|
|
+ nouveau_grctx_fini(dev);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ release_firmware(fw);
|
|
+ }
|
|
+
|
|
+ cp = pgraph->ctxprog;
|
|
+
|
|
+ nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_INDEX, 0);
|
|
+ for (i = 0; i < le16_to_cpu(cp->length); i++)
|
|
+ nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_DATA,
|
|
+ le32_to_cpu(cp->data[i]));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_grctx_fini(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
|
|
+
|
|
+ if (pgraph->ctxprog) {
|
|
+ kfree(pgraph->ctxprog);
|
|
+ pgraph->ctxprog = NULL;
|
|
+ }
|
|
+
|
|
+ if (pgraph->ctxvals) {
|
|
+ kfree(pgraph->ctxprog);
|
|
+ pgraph->ctxvals = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_grctx_vals_load(struct drm_device *dev, struct nouveau_gpuobj *ctx)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
|
|
+ struct nouveau_ctxvals *cv = pgraph->ctxvals;
|
|
+ int i;
|
|
+
|
|
+ if (!cv)
|
|
+ return;
|
|
+
|
|
+ for (i = 0; i < le32_to_cpu(cv->length); i++)
|
|
+ nv_wo32(dev, ctx, le32_to_cpu(cv->data[i].offset),
|
|
+ le32_to_cpu(cv->data[i].value));
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_grctx.h b/drivers/gpu/drm/nouveau/nouveau_grctx.h
|
|
new file mode 100644
|
|
index 0000000..5d39c4c
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_grctx.h
|
|
@@ -0,0 +1,133 @@
|
|
+#ifndef __NOUVEAU_GRCTX_H__
|
|
+#define __NOUVEAU_GRCTX_H__
|
|
+
|
|
+struct nouveau_grctx {
|
|
+ struct drm_device *dev;
|
|
+
|
|
+ enum {
|
|
+ NOUVEAU_GRCTX_PROG,
|
|
+ NOUVEAU_GRCTX_VALS
|
|
+ } mode;
|
|
+ void *data;
|
|
+
|
|
+ uint32_t ctxprog_max;
|
|
+ uint32_t ctxprog_len;
|
|
+ uint32_t ctxprog_reg;
|
|
+ int ctxprog_label[32];
|
|
+ uint32_t ctxvals_pos;
|
|
+ uint32_t ctxvals_base;
|
|
+};
|
|
+
|
|
+#ifdef CP_CTX
|
|
+static inline void
|
|
+cp_out(struct nouveau_grctx *ctx, uint32_t inst)
|
|
+{
|
|
+ uint32_t *ctxprog = ctx->data;
|
|
+
|
|
+ if (ctx->mode != NOUVEAU_GRCTX_PROG)
|
|
+ return;
|
|
+
|
|
+ BUG_ON(ctx->ctxprog_len == ctx->ctxprog_max);
|
|
+ ctxprog[ctx->ctxprog_len++] = inst;
|
|
+}
|
|
+
|
|
+static inline void
|
|
+cp_lsr(struct nouveau_grctx *ctx, uint32_t val)
|
|
+{
|
|
+ cp_out(ctx, CP_LOAD_SR | val);
|
|
+}
|
|
+
|
|
+static inline void
|
|
+cp_ctx(struct nouveau_grctx *ctx, uint32_t reg, uint32_t length)
|
|
+{
|
|
+ ctx->ctxprog_reg = (reg - 0x00400000) >> 2;
|
|
+
|
|
+ ctx->ctxvals_base = ctx->ctxvals_pos;
|
|
+ ctx->ctxvals_pos = ctx->ctxvals_base + length;
|
|
+
|
|
+ if (length > (CP_CTX_COUNT >> CP_CTX_COUNT_SHIFT)) {
|
|
+ cp_lsr(ctx, length);
|
|
+ length = 0;
|
|
+ }
|
|
+
|
|
+ cp_out(ctx, CP_CTX | (length << CP_CTX_COUNT_SHIFT) | ctx->ctxprog_reg);
|
|
+}
|
|
+
|
|
+static inline void
|
|
+cp_name(struct nouveau_grctx *ctx, int name)
|
|
+{
|
|
+ uint32_t *ctxprog = ctx->data;
|
|
+ int i;
|
|
+
|
|
+ if (ctx->mode != NOUVEAU_GRCTX_PROG)
|
|
+ return;
|
|
+
|
|
+ ctx->ctxprog_label[name] = ctx->ctxprog_len;
|
|
+ for (i = 0; i < ctx->ctxprog_len; i++) {
|
|
+ if ((ctxprog[i] & 0xfff00000) != 0xff400000)
|
|
+ continue;
|
|
+ if ((ctxprog[i] & CP_BRA_IP) != ((name) << CP_BRA_IP_SHIFT))
|
|
+ continue;
|
|
+ ctxprog[i] = (ctxprog[i] & 0x00ff00ff) |
|
|
+ (ctx->ctxprog_len << CP_BRA_IP_SHIFT);
|
|
+ }
|
|
+}
|
|
+
|
|
+static inline void
|
|
+_cp_bra(struct nouveau_grctx *ctx, u32 mod, int flag, int state, int name)
|
|
+{
|
|
+ int ip = 0;
|
|
+
|
|
+ if (mod != 2) {
|
|
+ ip = ctx->ctxprog_label[name] << CP_BRA_IP_SHIFT;
|
|
+ if (ip == 0)
|
|
+ ip = 0xff000000 | (name << CP_BRA_IP_SHIFT);
|
|
+ }
|
|
+
|
|
+ cp_out(ctx, CP_BRA | (mod << 18) | ip | flag |
|
|
+ (state ? 0 : CP_BRA_IF_CLEAR));
|
|
+}
|
|
+#define cp_bra(c,f,s,n) _cp_bra((c), 0, CP_FLAG_##f, CP_FLAG_##f##_##s, n)
|
|
+#ifdef CP_BRA_MOD
|
|
+#define cp_cal(c,f,s,n) _cp_bra((c), 1, CP_FLAG_##f, CP_FLAG_##f##_##s, n)
|
|
+#define cp_ret(c,f,s) _cp_bra((c), 2, CP_FLAG_##f, CP_FLAG_##f##_##s, 0)
|
|
+#endif
|
|
+
|
|
+static inline void
|
|
+_cp_wait(struct nouveau_grctx *ctx, int flag, int state)
|
|
+{
|
|
+ cp_out(ctx, CP_WAIT | flag | (state ? CP_WAIT_SET : 0));
|
|
+}
|
|
+#define cp_wait(c,f,s) _cp_wait((c), CP_FLAG_##f, CP_FLAG_##f##_##s)
|
|
+
|
|
+static inline void
|
|
+_cp_set(struct nouveau_grctx *ctx, int flag, int state)
|
|
+{
|
|
+ cp_out(ctx, CP_SET | flag | (state ? CP_SET_1 : 0));
|
|
+}
|
|
+#define cp_set(c,f,s) _cp_set((c), CP_FLAG_##f, CP_FLAG_##f##_##s)
|
|
+
|
|
+static inline void
|
|
+cp_pos(struct nouveau_grctx *ctx, int offset)
|
|
+{
|
|
+ ctx->ctxvals_pos = offset;
|
|
+ ctx->ctxvals_base = ctx->ctxvals_pos;
|
|
+
|
|
+ cp_lsr(ctx, ctx->ctxvals_pos);
|
|
+ cp_out(ctx, CP_SET_CONTEXT_POINTER);
|
|
+}
|
|
+
|
|
+static inline void
|
|
+gr_def(struct nouveau_grctx *ctx, uint32_t reg, uint32_t val)
|
|
+{
|
|
+ if (ctx->mode != NOUVEAU_GRCTX_VALS)
|
|
+ return;
|
|
+
|
|
+ reg = (reg - 0x00400000) / 4;
|
|
+ reg = (reg - ctx->ctxprog_reg) + ctx->ctxvals_base;
|
|
+
|
|
+ nv_wo32(ctx->dev, ctx->data, reg, val);
|
|
+}
|
|
+#endif
|
|
+
|
|
+#endif
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.c b/drivers/gpu/drm/nouveau/nouveau_hw.c
|
|
new file mode 100644
|
|
index 0000000..dc46792
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_hw.c
|
|
@@ -0,0 +1,1080 @@
|
|
+/*
|
|
+ * Copyright 2006 Dave Airlie
|
|
+ * Copyright 2007 Maarten Maathuis
|
|
+ * Copyright 2007-2009 Stuart Bennett
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice shall be included in
|
|
+ * all copies or substantial portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
|
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
+ * SOFTWARE.
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_hw.h"
|
|
+
|
|
+#define CHIPSET_NFORCE 0x01a0
|
|
+#define CHIPSET_NFORCE2 0x01f0
|
|
+
|
|
+/*
|
|
+ * misc hw access wrappers/control functions
|
|
+ */
|
|
+
|
|
+void
|
|
+NVWriteVgaSeq(struct drm_device *dev, int head, uint8_t index, uint8_t value)
|
|
+{
|
|
+ NVWritePRMVIO(dev, head, NV_PRMVIO_SRX, index);
|
|
+ NVWritePRMVIO(dev, head, NV_PRMVIO_SR, value);
|
|
+}
|
|
+
|
|
+uint8_t
|
|
+NVReadVgaSeq(struct drm_device *dev, int head, uint8_t index)
|
|
+{
|
|
+ NVWritePRMVIO(dev, head, NV_PRMVIO_SRX, index);
|
|
+ return NVReadPRMVIO(dev, head, NV_PRMVIO_SR);
|
|
+}
|
|
+
|
|
+void
|
|
+NVWriteVgaGr(struct drm_device *dev, int head, uint8_t index, uint8_t value)
|
|
+{
|
|
+ NVWritePRMVIO(dev, head, NV_PRMVIO_GRX, index);
|
|
+ NVWritePRMVIO(dev, head, NV_PRMVIO_GX, value);
|
|
+}
|
|
+
|
|
+uint8_t
|
|
+NVReadVgaGr(struct drm_device *dev, int head, uint8_t index)
|
|
+{
|
|
+ NVWritePRMVIO(dev, head, NV_PRMVIO_GRX, index);
|
|
+ return NVReadPRMVIO(dev, head, NV_PRMVIO_GX);
|
|
+}
|
|
+
|
|
+/* CR44 takes values 0 (head A), 3 (head B) and 4 (heads tied)
|
|
+ * it affects only the 8 bit vga io regs, which we access using mmio at
|
|
+ * 0xc{0,2}3c*, 0x60{1,3}3*, and 0x68{1,3}3d*
|
|
+ * in general, the set value of cr44 does not matter: reg access works as
|
|
+ * expected and values can be set for the appropriate head by using a 0x2000
|
|
+ * offset as required
|
|
+ * however:
|
|
+ * a) pre nv40, the head B range of PRMVIO regs at 0xc23c* was not exposed and
|
|
+ * cr44 must be set to 0 or 3 for accessing values on the correct head
|
|
+ * through the common 0xc03c* addresses
|
|
+ * b) in tied mode (4) head B is programmed to the values set on head A, and
|
|
+ * access using the head B addresses can have strange results, ergo we leave
|
|
+ * tied mode in init once we know to what cr44 should be restored on exit
|
|
+ *
|
|
+ * the owner parameter is slightly abused:
|
|
+ * 0 and 1 are treated as head values and so the set value is (owner * 3)
|
|
+ * other values are treated as literal values to set
|
|
+ */
|
|
+void
|
|
+NVSetOwner(struct drm_device *dev, int owner)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ if (owner == 1)
|
|
+ owner *= 3;
|
|
+
|
|
+ if (dev_priv->chipset == 0x11) {
|
|
+ /* This might seem stupid, but the blob does it and
|
|
+ * omitting it often locks the system up.
|
|
+ */
|
|
+ NVReadVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX);
|
|
+ NVReadVgaCrtc(dev, 1, NV_CIO_SR_LOCK_INDEX);
|
|
+ }
|
|
+
|
|
+ /* CR44 is always changed on CRTC0 */
|
|
+ NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_44, owner);
|
|
+
|
|
+ if (dev_priv->chipset == 0x11) { /* set me harder */
|
|
+ NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_2E, owner);
|
|
+ NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_2E, owner);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+NVBlankScreen(struct drm_device *dev, int head, bool blank)
|
|
+{
|
|
+ unsigned char seq1;
|
|
+
|
|
+ if (nv_two_heads(dev))
|
|
+ NVSetOwner(dev, head);
|
|
+
|
|
+ seq1 = NVReadVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX);
|
|
+
|
|
+ NVVgaSeqReset(dev, head, true);
|
|
+ if (blank)
|
|
+ NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 | 0x20);
|
|
+ else
|
|
+ NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 & ~0x20);
|
|
+ NVVgaSeqReset(dev, head, false);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * PLL setting
|
|
+ */
|
|
+
|
|
+static int
|
|
+powerctrl_1_shift(int chip_version, int reg)
|
|
+{
|
|
+ int shift = -4;
|
|
+
|
|
+ if (chip_version < 0x17 || chip_version == 0x1a || chip_version == 0x20)
|
|
+ return shift;
|
|
+
|
|
+ switch (reg) {
|
|
+ case NV_RAMDAC_VPLL2:
|
|
+ shift += 4;
|
|
+ case NV_PRAMDAC_VPLL_COEFF:
|
|
+ shift += 4;
|
|
+ case NV_PRAMDAC_MPLL_COEFF:
|
|
+ shift += 4;
|
|
+ case NV_PRAMDAC_NVPLL_COEFF:
|
|
+ shift += 4;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * the shift for vpll regs is only used for nv3x chips with a single
|
|
+ * stage pll
|
|
+ */
|
|
+ if (shift > 4 && (chip_version < 0x32 || chip_version == 0x35 ||
|
|
+ chip_version == 0x36 || chip_version >= 0x40))
|
|
+ shift = -4;
|
|
+
|
|
+ return shift;
|
|
+}
|
|
+
|
|
+static void
|
|
+setPLL_single(struct drm_device *dev, uint32_t reg, struct nouveau_pll_vals *pv)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ int chip_version = dev_priv->vbios->chip_version;
|
|
+ uint32_t oldpll = NVReadRAMDAC(dev, 0, reg);
|
|
+ int oldN = (oldpll >> 8) & 0xff, oldM = oldpll & 0xff;
|
|
+ uint32_t pll = (oldpll & 0xfff80000) | pv->log2P << 16 | pv->NM1;
|
|
+ uint32_t saved_powerctrl_1 = 0;
|
|
+ int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg);
|
|
+
|
|
+ if (oldpll == pll)
|
|
+ return; /* already set */
|
|
+
|
|
+ if (shift_powerctrl_1 >= 0) {
|
|
+ saved_powerctrl_1 = nvReadMC(dev, NV_PBUS_POWERCTRL_1);
|
|
+ nvWriteMC(dev, NV_PBUS_POWERCTRL_1,
|
|
+ (saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) |
|
|
+ 1 << shift_powerctrl_1);
|
|
+ }
|
|
+
|
|
+ if (oldM && pv->M1 && (oldN / oldM < pv->N1 / pv->M1))
|
|
+ /* upclock -- write new post divider first */
|
|
+ NVWriteRAMDAC(dev, 0, reg, pv->log2P << 16 | (oldpll & 0xffff));
|
|
+ else
|
|
+ /* downclock -- write new NM first */
|
|
+ NVWriteRAMDAC(dev, 0, reg, (oldpll & 0xffff0000) | pv->NM1);
|
|
+
|
|
+ if (chip_version < 0x17 && chip_version != 0x11)
|
|
+ /* wait a bit on older chips */
|
|
+ msleep(64);
|
|
+ NVReadRAMDAC(dev, 0, reg);
|
|
+
|
|
+ /* then write the other half as well */
|
|
+ NVWriteRAMDAC(dev, 0, reg, pll);
|
|
+
|
|
+ if (shift_powerctrl_1 >= 0)
|
|
+ nvWriteMC(dev, NV_PBUS_POWERCTRL_1, saved_powerctrl_1);
|
|
+}
|
|
+
|
|
+static uint32_t
|
|
+new_ramdac580(uint32_t reg1, bool ss, uint32_t ramdac580)
|
|
+{
|
|
+ bool head_a = (reg1 == NV_PRAMDAC_VPLL_COEFF);
|
|
+
|
|
+ if (ss) /* single stage pll mode */
|
|
+ ramdac580 |= head_a ? NV_RAMDAC_580_VPLL1_ACTIVE :
|
|
+ NV_RAMDAC_580_VPLL2_ACTIVE;
|
|
+ else
|
|
+ ramdac580 &= head_a ? ~NV_RAMDAC_580_VPLL1_ACTIVE :
|
|
+ ~NV_RAMDAC_580_VPLL2_ACTIVE;
|
|
+
|
|
+ return ramdac580;
|
|
+}
|
|
+
|
|
+static void
|
|
+setPLL_double_highregs(struct drm_device *dev, uint32_t reg1,
|
|
+ struct nouveau_pll_vals *pv)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ int chip_version = dev_priv->vbios->chip_version;
|
|
+ bool nv3035 = chip_version == 0x30 || chip_version == 0x35;
|
|
+ uint32_t reg2 = reg1 + ((reg1 == NV_RAMDAC_VPLL2) ? 0x5c : 0x70);
|
|
+ uint32_t oldpll1 = NVReadRAMDAC(dev, 0, reg1);
|
|
+ uint32_t oldpll2 = !nv3035 ? NVReadRAMDAC(dev, 0, reg2) : 0;
|
|
+ uint32_t pll1 = (oldpll1 & 0xfff80000) | pv->log2P << 16 | pv->NM1;
|
|
+ uint32_t pll2 = (oldpll2 & 0x7fff0000) | 1 << 31 | pv->NM2;
|
|
+ uint32_t oldramdac580 = 0, ramdac580 = 0;
|
|
+ bool single_stage = !pv->NM2 || pv->N2 == pv->M2; /* nv41+ only */
|
|
+ uint32_t saved_powerctrl_1 = 0, savedc040 = 0;
|
|
+ int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg1);
|
|
+
|
|
+ /* model specific additions to generic pll1 and pll2 set up above */
|
|
+ if (nv3035) {
|
|
+ pll1 = (pll1 & 0xfcc7ffff) | (pv->N2 & 0x18) << 21 |
|
|
+ (pv->N2 & 0x7) << 19 | 8 << 4 | (pv->M2 & 7) << 4;
|
|
+ pll2 = 0;
|
|
+ }
|
|
+ if (chip_version > 0x40 && reg1 >= NV_PRAMDAC_VPLL_COEFF) { /* !nv40 */
|
|
+ oldramdac580 = NVReadRAMDAC(dev, 0, NV_PRAMDAC_580);
|
|
+ ramdac580 = new_ramdac580(reg1, single_stage, oldramdac580);
|
|
+ if (oldramdac580 != ramdac580)
|
|
+ oldpll1 = ~0; /* force mismatch */
|
|
+ if (single_stage)
|
|
+ /* magic value used by nvidia in single stage mode */
|
|
+ pll2 |= 0x011f;
|
|
+ }
|
|
+ if (chip_version > 0x70)
|
|
+ /* magic bits set by the blob (but not the bios) on g71-73 */
|
|
+ pll1 = (pll1 & 0x7fffffff) | (single_stage ? 0x4 : 0xc) << 28;
|
|
+
|
|
+ if (oldpll1 == pll1 && oldpll2 == pll2)
|
|
+ return; /* already set */
|
|
+
|
|
+ if (shift_powerctrl_1 >= 0) {
|
|
+ saved_powerctrl_1 = nvReadMC(dev, NV_PBUS_POWERCTRL_1);
|
|
+ nvWriteMC(dev, NV_PBUS_POWERCTRL_1,
|
|
+ (saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) |
|
|
+ 1 << shift_powerctrl_1);
|
|
+ }
|
|
+
|
|
+ if (chip_version >= 0x40) {
|
|
+ int shift_c040 = 14;
|
|
+
|
|
+ switch (reg1) {
|
|
+ case NV_PRAMDAC_MPLL_COEFF:
|
|
+ shift_c040 += 2;
|
|
+ case NV_PRAMDAC_NVPLL_COEFF:
|
|
+ shift_c040 += 2;
|
|
+ case NV_RAMDAC_VPLL2:
|
|
+ shift_c040 += 2;
|
|
+ case NV_PRAMDAC_VPLL_COEFF:
|
|
+ shift_c040 += 2;
|
|
+ }
|
|
+
|
|
+ savedc040 = nvReadMC(dev, 0xc040);
|
|
+ if (shift_c040 != 14)
|
|
+ nvWriteMC(dev, 0xc040, savedc040 & ~(3 << shift_c040));
|
|
+ }
|
|
+
|
|
+ if (oldramdac580 != ramdac580)
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_580, ramdac580);
|
|
+
|
|
+ if (!nv3035)
|
|
+ NVWriteRAMDAC(dev, 0, reg2, pll2);
|
|
+ NVWriteRAMDAC(dev, 0, reg1, pll1);
|
|
+
|
|
+ if (shift_powerctrl_1 >= 0)
|
|
+ nvWriteMC(dev, NV_PBUS_POWERCTRL_1, saved_powerctrl_1);
|
|
+ if (chip_version >= 0x40)
|
|
+ nvWriteMC(dev, 0xc040, savedc040);
|
|
+}
|
|
+
|
|
+static void
|
|
+setPLL_double_lowregs(struct drm_device *dev, uint32_t NMNMreg,
|
|
+ struct nouveau_pll_vals *pv)
|
|
+{
|
|
+ /* When setting PLLs, there is a merry game of disabling and enabling
|
|
+ * various bits of hardware during the process. This function is a
|
|
+ * synthesis of six nv4x traces, nearly each card doing a subtly
|
|
+ * different thing. With luck all the necessary bits for each card are
|
|
+ * combined herein. Without luck it deviates from each card's formula
|
|
+ * so as to not work on any :)
|
|
+ */
|
|
+
|
|
+ uint32_t Preg = NMNMreg - 4;
|
|
+ bool mpll = Preg == 0x4020;
|
|
+ uint32_t oldPval = nvReadMC(dev, Preg);
|
|
+ uint32_t NMNM = pv->NM2 << 16 | pv->NM1;
|
|
+ uint32_t Pval = (oldPval & (mpll ? ~(0x11 << 16) : ~(1 << 16))) |
|
|
+ 0xc << 28 | pv->log2P << 16;
|
|
+ uint32_t saved4600 = 0;
|
|
+ /* some cards have different maskc040s */
|
|
+ uint32_t maskc040 = ~(3 << 14), savedc040;
|
|
+ bool single_stage = !pv->NM2 || pv->N2 == pv->M2;
|
|
+
|
|
+ if (nvReadMC(dev, NMNMreg) == NMNM && (oldPval & 0xc0070000) == Pval)
|
|
+ return;
|
|
+
|
|
+ if (Preg == 0x4000)
|
|
+ maskc040 = ~0x333;
|
|
+ if (Preg == 0x4058)
|
|
+ maskc040 = ~(0xc << 24);
|
|
+
|
|
+ if (mpll) {
|
|
+ struct pll_lims pll_lim;
|
|
+ uint8_t Pval2;
|
|
+
|
|
+ if (get_pll_limits(dev, Preg, &pll_lim))
|
|
+ return;
|
|
+
|
|
+ Pval2 = pv->log2P + pll_lim.log2p_bias;
|
|
+ if (Pval2 > pll_lim.max_log2p)
|
|
+ Pval2 = pll_lim.max_log2p;
|
|
+ Pval |= 1 << 28 | Pval2 << 20;
|
|
+
|
|
+ saved4600 = nvReadMC(dev, 0x4600);
|
|
+ nvWriteMC(dev, 0x4600, saved4600 | 8 << 28);
|
|
+ }
|
|
+ if (single_stage)
|
|
+ Pval |= mpll ? 1 << 12 : 1 << 8;
|
|
+
|
|
+ nvWriteMC(dev, Preg, oldPval | 1 << 28);
|
|
+ nvWriteMC(dev, Preg, Pval & ~(4 << 28));
|
|
+ if (mpll) {
|
|
+ Pval |= 8 << 20;
|
|
+ nvWriteMC(dev, 0x4020, Pval & ~(0xc << 28));
|
|
+ nvWriteMC(dev, 0x4038, Pval & ~(0xc << 28));
|
|
+ }
|
|
+
|
|
+ savedc040 = nvReadMC(dev, 0xc040);
|
|
+ nvWriteMC(dev, 0xc040, savedc040 & maskc040);
|
|
+
|
|
+ nvWriteMC(dev, NMNMreg, NMNM);
|
|
+ if (NMNMreg == 0x4024)
|
|
+ nvWriteMC(dev, 0x403c, NMNM);
|
|
+
|
|
+ nvWriteMC(dev, Preg, Pval);
|
|
+ if (mpll) {
|
|
+ Pval &= ~(8 << 20);
|
|
+ nvWriteMC(dev, 0x4020, Pval);
|
|
+ nvWriteMC(dev, 0x4038, Pval);
|
|
+ nvWriteMC(dev, 0x4600, saved4600);
|
|
+ }
|
|
+
|
|
+ nvWriteMC(dev, 0xc040, savedc040);
|
|
+
|
|
+ if (mpll) {
|
|
+ nvWriteMC(dev, 0x4020, Pval & ~(1 << 28));
|
|
+ nvWriteMC(dev, 0x4038, Pval & ~(1 << 28));
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_hw_setpll(struct drm_device *dev, uint32_t reg1,
|
|
+ struct nouveau_pll_vals *pv)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ int cv = dev_priv->vbios->chip_version;
|
|
+
|
|
+ if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 ||
|
|
+ cv >= 0x40) {
|
|
+ if (reg1 > 0x405c)
|
|
+ setPLL_double_highregs(dev, reg1, pv);
|
|
+ else
|
|
+ setPLL_double_lowregs(dev, reg1, pv);
|
|
+ } else
|
|
+ setPLL_single(dev, reg1, pv);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * PLL getting
|
|
+ */
|
|
+
|
|
+static void
|
|
+nouveau_hw_decode_pll(struct drm_device *dev, uint32_t reg1, uint32_t pll1,
|
|
+ uint32_t pll2, struct nouveau_pll_vals *pllvals)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ /* to force parsing as single stage (i.e. nv40 vplls) pass pll2 as 0 */
|
|
+
|
|
+ /* log2P is & 0x7 as never more than 7, and nv30/35 only uses 3 bits */
|
|
+ pllvals->log2P = (pll1 >> 16) & 0x7;
|
|
+ pllvals->N2 = pllvals->M2 = 1;
|
|
+
|
|
+ if (reg1 <= 0x405c) {
|
|
+ pllvals->NM1 = pll2 & 0xffff;
|
|
+ /* single stage NVPLL and VPLLs use 1 << 8, MPLL uses 1 << 12 */
|
|
+ if (!(pll1 & 0x1100))
|
|
+ pllvals->NM2 = pll2 >> 16;
|
|
+ } else {
|
|
+ pllvals->NM1 = pll1 & 0xffff;
|
|
+ if (nv_two_reg_pll(dev) && pll2 & NV31_RAMDAC_ENABLE_VCO2)
|
|
+ pllvals->NM2 = pll2 & 0xffff;
|
|
+ else if (dev_priv->chipset == 0x30 || dev_priv->chipset == 0x35) {
|
|
+ pllvals->M1 &= 0xf; /* only 4 bits */
|
|
+ if (pll1 & NV30_RAMDAC_ENABLE_VCO2) {
|
|
+ pllvals->M2 = (pll1 >> 4) & 0x7;
|
|
+ pllvals->N2 = ((pll1 >> 21) & 0x18) |
|
|
+ ((pll1 >> 19) & 0x7);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_hw_get_pllvals(struct drm_device *dev, enum pll_types plltype,
|
|
+ struct nouveau_pll_vals *pllvals)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ const uint32_t nv04_regs[MAX_PLL_TYPES] = { NV_PRAMDAC_NVPLL_COEFF,
|
|
+ NV_PRAMDAC_MPLL_COEFF,
|
|
+ NV_PRAMDAC_VPLL_COEFF,
|
|
+ NV_RAMDAC_VPLL2 };
|
|
+ const uint32_t nv40_regs[MAX_PLL_TYPES] = { 0x4000,
|
|
+ 0x4020,
|
|
+ NV_PRAMDAC_VPLL_COEFF,
|
|
+ NV_RAMDAC_VPLL2 };
|
|
+ uint32_t reg1, pll1, pll2 = 0;
|
|
+ struct pll_lims pll_lim;
|
|
+ int ret;
|
|
+
|
|
+ if (dev_priv->card_type < NV_40)
|
|
+ reg1 = nv04_regs[plltype];
|
|
+ else
|
|
+ reg1 = nv40_regs[plltype];
|
|
+
|
|
+ pll1 = nvReadMC(dev, reg1);
|
|
+
|
|
+ if (reg1 <= 0x405c)
|
|
+ pll2 = nvReadMC(dev, reg1 + 4);
|
|
+ else if (nv_two_reg_pll(dev)) {
|
|
+ uint32_t reg2 = reg1 + (reg1 == NV_RAMDAC_VPLL2 ? 0x5c : 0x70);
|
|
+
|
|
+ pll2 = nvReadMC(dev, reg2);
|
|
+ }
|
|
+
|
|
+ if (dev_priv->card_type == 0x40 && reg1 >= NV_PRAMDAC_VPLL_COEFF) {
|
|
+ uint32_t ramdac580 = NVReadRAMDAC(dev, 0, NV_PRAMDAC_580);
|
|
+
|
|
+ /* check whether vpll has been forced into single stage mode */
|
|
+ if (reg1 == NV_PRAMDAC_VPLL_COEFF) {
|
|
+ if (ramdac580 & NV_RAMDAC_580_VPLL1_ACTIVE)
|
|
+ pll2 = 0;
|
|
+ } else
|
|
+ if (ramdac580 & NV_RAMDAC_580_VPLL2_ACTIVE)
|
|
+ pll2 = 0;
|
|
+ }
|
|
+
|
|
+ nouveau_hw_decode_pll(dev, reg1, pll1, pll2, pllvals);
|
|
+
|
|
+ ret = get_pll_limits(dev, plltype, &pll_lim);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ pllvals->refclk = pll_lim.refclk;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_hw_pllvals_to_clk(struct nouveau_pll_vals *pv)
|
|
+{
|
|
+ /* Avoid divide by zero if called at an inappropriate time */
|
|
+ if (!pv->M1 || !pv->M2)
|
|
+ return 0;
|
|
+
|
|
+ return pv->N1 * pv->N2 * pv->refclk / (pv->M1 * pv->M2) >> pv->log2P;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_hw_get_clock(struct drm_device *dev, enum pll_types plltype)
|
|
+{
|
|
+ struct nouveau_pll_vals pllvals;
|
|
+
|
|
+ if (plltype == MPLL && (dev->pci_device & 0x0ff0) == CHIPSET_NFORCE) {
|
|
+ uint32_t mpllP;
|
|
+
|
|
+ pci_read_config_dword(pci_get_bus_and_slot(0, 3), 0x6c, &mpllP);
|
|
+ if (!mpllP)
|
|
+ mpllP = 4;
|
|
+
|
|
+ return 400000 / mpllP;
|
|
+ } else
|
|
+ if (plltype == MPLL && (dev->pci_device & 0xff0) == CHIPSET_NFORCE2) {
|
|
+ uint32_t clock;
|
|
+
|
|
+ pci_read_config_dword(pci_get_bus_and_slot(0, 5), 0x4c, &clock);
|
|
+ return clock;
|
|
+ }
|
|
+
|
|
+ nouveau_hw_get_pllvals(dev, plltype, &pllvals);
|
|
+
|
|
+ return nouveau_hw_pllvals_to_clk(&pllvals);
|
|
+}
|
|
+
|
|
+static void
|
|
+nouveau_hw_fix_bad_vpll(struct drm_device *dev, int head)
|
|
+{
|
|
+ /* the vpll on an unused head can come up with a random value, way
|
|
+ * beyond the pll limits. for some reason this causes the chip to
|
|
+ * lock up when reading the dac palette regs, so set a valid pll here
|
|
+ * when such a condition detected. only seen on nv11 to date
|
|
+ */
|
|
+
|
|
+ struct pll_lims pll_lim;
|
|
+ struct nouveau_pll_vals pv;
|
|
+ uint32_t pllreg = head ? NV_RAMDAC_VPLL2 : NV_PRAMDAC_VPLL_COEFF;
|
|
+
|
|
+ if (get_pll_limits(dev, head ? VPLL2 : VPLL1, &pll_lim))
|
|
+ return;
|
|
+ nouveau_hw_get_pllvals(dev, head ? VPLL2 : VPLL1, &pv);
|
|
+
|
|
+ if (pv.M1 >= pll_lim.vco1.min_m && pv.M1 <= pll_lim.vco1.max_m &&
|
|
+ pv.N1 >= pll_lim.vco1.min_n && pv.N1 <= pll_lim.vco1.max_n &&
|
|
+ pv.log2P <= pll_lim.max_log2p)
|
|
+ return;
|
|
+
|
|
+ NV_WARN(dev, "VPLL %d outwith limits, attempting to fix\n", head + 1);
|
|
+
|
|
+ /* set lowest clock within static limits */
|
|
+ pv.M1 = pll_lim.vco1.max_m;
|
|
+ pv.N1 = pll_lim.vco1.min_n;
|
|
+ pv.log2P = pll_lim.max_usable_log2p;
|
|
+ nouveau_hw_setpll(dev, pllreg, &pv);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * vga font save/restore
|
|
+ */
|
|
+
|
|
+static void nouveau_vga_font_io(struct drm_device *dev,
|
|
+ void __iomem *iovram,
|
|
+ bool save, unsigned plane)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ unsigned i;
|
|
+
|
|
+ NVWriteVgaSeq(dev, 0, NV_VIO_SR_PLANE_MASK_INDEX, 1 << plane);
|
|
+ NVWriteVgaGr(dev, 0, NV_VIO_GX_READ_MAP_INDEX, plane);
|
|
+ for (i = 0; i < 16384; i++) {
|
|
+ if (save) {
|
|
+ dev_priv->saved_vga_font[plane][i] =
|
|
+ ioread32_native(iovram + i * 4);
|
|
+ } else {
|
|
+ iowrite32_native(dev_priv->saved_vga_font[plane][i],
|
|
+ iovram + i * 4);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_hw_save_vga_fonts(struct drm_device *dev, bool save)
|
|
+{
|
|
+ uint8_t misc, gr4, gr5, gr6, seq2, seq4;
|
|
+ bool graphicsmode;
|
|
+ unsigned plane;
|
|
+ void __iomem *iovram;
|
|
+
|
|
+ if (nv_two_heads(dev))
|
|
+ NVSetOwner(dev, 0);
|
|
+
|
|
+ NVSetEnablePalette(dev, 0, true);
|
|
+ graphicsmode = NVReadVgaAttr(dev, 0, NV_CIO_AR_MODE_INDEX) & 1;
|
|
+ NVSetEnablePalette(dev, 0, false);
|
|
+
|
|
+ if (graphicsmode) /* graphics mode => framebuffer => no need to save */
|
|
+ return;
|
|
+
|
|
+ NV_INFO(dev, "%sing VGA fonts\n", save ? "Sav" : "Restor");
|
|
+
|
|
+ /* map first 64KiB of VRAM, holds VGA fonts etc */
|
|
+ iovram = ioremap(pci_resource_start(dev->pdev, 1), 65536);
|
|
+ if (!iovram) {
|
|
+ NV_ERROR(dev, "Failed to map VRAM, "
|
|
+ "cannot save/restore VGA fonts.\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (nv_two_heads(dev))
|
|
+ NVBlankScreen(dev, 1, true);
|
|
+ NVBlankScreen(dev, 0, true);
|
|
+
|
|
+ /* save control regs */
|
|
+ misc = NVReadPRMVIO(dev, 0, NV_PRMVIO_MISC__READ);
|
|
+ seq2 = NVReadVgaSeq(dev, 0, NV_VIO_SR_PLANE_MASK_INDEX);
|
|
+ seq4 = NVReadVgaSeq(dev, 0, NV_VIO_SR_MEM_MODE_INDEX);
|
|
+ gr4 = NVReadVgaGr(dev, 0, NV_VIO_GX_READ_MAP_INDEX);
|
|
+ gr5 = NVReadVgaGr(dev, 0, NV_VIO_GX_MODE_INDEX);
|
|
+ gr6 = NVReadVgaGr(dev, 0, NV_VIO_GX_MISC_INDEX);
|
|
+
|
|
+ NVWritePRMVIO(dev, 0, NV_PRMVIO_MISC__WRITE, 0x67);
|
|
+ NVWriteVgaSeq(dev, 0, NV_VIO_SR_MEM_MODE_INDEX, 0x6);
|
|
+ NVWriteVgaGr(dev, 0, NV_VIO_GX_MODE_INDEX, 0x0);
|
|
+ NVWriteVgaGr(dev, 0, NV_VIO_GX_MISC_INDEX, 0x5);
|
|
+
|
|
+ /* store font in planes 0..3 */
|
|
+ for (plane = 0; plane < 4; plane++)
|
|
+ nouveau_vga_font_io(dev, iovram, save, plane);
|
|
+
|
|
+ /* restore control regs */
|
|
+ NVWritePRMVIO(dev, 0, NV_PRMVIO_MISC__WRITE, misc);
|
|
+ NVWriteVgaGr(dev, 0, NV_VIO_GX_READ_MAP_INDEX, gr4);
|
|
+ NVWriteVgaGr(dev, 0, NV_VIO_GX_MODE_INDEX, gr5);
|
|
+ NVWriteVgaGr(dev, 0, NV_VIO_GX_MISC_INDEX, gr6);
|
|
+ NVWriteVgaSeq(dev, 0, NV_VIO_SR_PLANE_MASK_INDEX, seq2);
|
|
+ NVWriteVgaSeq(dev, 0, NV_VIO_SR_MEM_MODE_INDEX, seq4);
|
|
+
|
|
+ if (nv_two_heads(dev))
|
|
+ NVBlankScreen(dev, 1, false);
|
|
+ NVBlankScreen(dev, 0, false);
|
|
+
|
|
+ iounmap(iovram);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * mode state save/load
|
|
+ */
|
|
+
|
|
+static void
|
|
+rd_cio_state(struct drm_device *dev, int head,
|
|
+ struct nv04_crtc_reg *crtcstate, int index)
|
|
+{
|
|
+ crtcstate->CRTC[index] = NVReadVgaCrtc(dev, head, index);
|
|
+}
|
|
+
|
|
+static void
|
|
+wr_cio_state(struct drm_device *dev, int head,
|
|
+ struct nv04_crtc_reg *crtcstate, int index)
|
|
+{
|
|
+ NVWriteVgaCrtc(dev, head, index, crtcstate->CRTC[index]);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv_save_state_ramdac(struct drm_device *dev, int head,
|
|
+ struct nv04_mode_state *state)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nv04_crtc_reg *regp = &state->crtc_reg[head];
|
|
+ int i;
|
|
+
|
|
+ if (dev_priv->card_type >= NV_10)
|
|
+ regp->nv10_cursync = NVReadRAMDAC(dev, head, NV_RAMDAC_NV10_CURSYNC);
|
|
+
|
|
+ nouveau_hw_get_pllvals(dev, head ? VPLL2 : VPLL1, ®p->pllvals);
|
|
+ state->pllsel = NVReadRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT);
|
|
+ if (nv_two_heads(dev))
|
|
+ state->sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK);
|
|
+ if (dev_priv->chipset == 0x11)
|
|
+ regp->dither = NVReadRAMDAC(dev, head, NV_RAMDAC_DITHER_NV11);
|
|
+
|
|
+ regp->ramdac_gen_ctrl = NVReadRAMDAC(dev, head, NV_PRAMDAC_GENERAL_CONTROL);
|
|
+
|
|
+ if (nv_gf4_disp_arch(dev))
|
|
+ regp->ramdac_630 = NVReadRAMDAC(dev, head, NV_PRAMDAC_630);
|
|
+ if (dev_priv->chipset >= 0x30)
|
|
+ regp->ramdac_634 = NVReadRAMDAC(dev, head, NV_PRAMDAC_634);
|
|
+
|
|
+ regp->tv_setup = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP);
|
|
+ regp->tv_vtotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_VTOTAL);
|
|
+ regp->tv_vskew = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_VSKEW);
|
|
+ regp->tv_vsync_delay = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_VSYNC_DELAY);
|
|
+ regp->tv_htotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HTOTAL);
|
|
+ regp->tv_hskew = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HSKEW);
|
|
+ regp->tv_hsync_delay = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY);
|
|
+ regp->tv_hsync_delay2 = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY2);
|
|
+
|
|
+ for (i = 0; i < 7; i++) {
|
|
+ uint32_t ramdac_reg = NV_PRAMDAC_FP_VDISPLAY_END + (i * 4);
|
|
+ regp->fp_vert_regs[i] = NVReadRAMDAC(dev, head, ramdac_reg);
|
|
+ regp->fp_horiz_regs[i] = NVReadRAMDAC(dev, head, ramdac_reg + 0x20);
|
|
+ }
|
|
+
|
|
+ if (nv_gf4_disp_arch(dev)) {
|
|
+ regp->dither = NVReadRAMDAC(dev, head, NV_RAMDAC_FP_DITHER);
|
|
+ for (i = 0; i < 3; i++) {
|
|
+ regp->dither_regs[i] = NVReadRAMDAC(dev, head, NV_PRAMDAC_850 + i * 4);
|
|
+ regp->dither_regs[i + 3] = NVReadRAMDAC(dev, head, NV_PRAMDAC_85C + i * 4);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ regp->fp_control = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL);
|
|
+ regp->fp_debug_0 = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_0);
|
|
+ if (!nv_gf4_disp_arch(dev) && head == 0) {
|
|
+ /* early chips don't allow access to PRAMDAC_TMDS_* without
|
|
+ * the head A FPCLK on (nv11 even locks up) */
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_FP_DEBUG_0, regp->fp_debug_0 &
|
|
+ ~NV_PRAMDAC_FP_DEBUG_0_PWRDOWN_FPCLK);
|
|
+ }
|
|
+ regp->fp_debug_1 = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1);
|
|
+ regp->fp_debug_2 = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_2);
|
|
+
|
|
+ regp->fp_margin_color = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_MARGIN_COLOR);
|
|
+
|
|
+ if (nv_gf4_disp_arch(dev))
|
|
+ regp->ramdac_8c0 = NVReadRAMDAC(dev, head, NV_PRAMDAC_8C0);
|
|
+
|
|
+ if (dev_priv->card_type == NV_40) {
|
|
+ regp->ramdac_a20 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A20);
|
|
+ regp->ramdac_a24 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A24);
|
|
+ regp->ramdac_a34 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A34);
|
|
+
|
|
+ for (i = 0; i < 38; i++)
|
|
+ regp->ctv_regs[i] = NVReadRAMDAC(dev, head,
|
|
+ NV_PRAMDAC_CTV + 4*i);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+nv_load_state_ramdac(struct drm_device *dev, int head,
|
|
+ struct nv04_mode_state *state)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nv04_crtc_reg *regp = &state->crtc_reg[head];
|
|
+ uint32_t pllreg = head ? NV_RAMDAC_VPLL2 : NV_PRAMDAC_VPLL_COEFF;
|
|
+ int i;
|
|
+
|
|
+ if (dev_priv->card_type >= NV_10)
|
|
+ NVWriteRAMDAC(dev, head, NV_RAMDAC_NV10_CURSYNC, regp->nv10_cursync);
|
|
+
|
|
+ nouveau_hw_setpll(dev, pllreg, ®p->pllvals);
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT, state->pllsel);
|
|
+ if (nv_two_heads(dev))
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, state->sel_clk);
|
|
+ if (dev_priv->chipset == 0x11)
|
|
+ NVWriteRAMDAC(dev, head, NV_RAMDAC_DITHER_NV11, regp->dither);
|
|
+
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_GENERAL_CONTROL, regp->ramdac_gen_ctrl);
|
|
+
|
|
+ if (nv_gf4_disp_arch(dev))
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_630, regp->ramdac_630);
|
|
+ if (dev_priv->chipset >= 0x30)
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_634, regp->ramdac_634);
|
|
+
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP, regp->tv_setup);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VTOTAL, regp->tv_vtotal);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VSKEW, regp->tv_vskew);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VSYNC_DELAY, regp->tv_vsync_delay);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HTOTAL, regp->tv_htotal);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSKEW, regp->tv_hskew);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY, regp->tv_hsync_delay);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY2, regp->tv_hsync_delay2);
|
|
+
|
|
+ for (i = 0; i < 7; i++) {
|
|
+ uint32_t ramdac_reg = NV_PRAMDAC_FP_VDISPLAY_END + (i * 4);
|
|
+
|
|
+ NVWriteRAMDAC(dev, head, ramdac_reg, regp->fp_vert_regs[i]);
|
|
+ NVWriteRAMDAC(dev, head, ramdac_reg + 0x20, regp->fp_horiz_regs[i]);
|
|
+ }
|
|
+
|
|
+ if (nv_gf4_disp_arch(dev)) {
|
|
+ NVWriteRAMDAC(dev, head, NV_RAMDAC_FP_DITHER, regp->dither);
|
|
+ for (i = 0; i < 3; i++) {
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_850 + i * 4, regp->dither_regs[i]);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_85C + i * 4, regp->dither_regs[i + 3]);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL, regp->fp_control);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_0, regp->fp_debug_0);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1, regp->fp_debug_1);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_2, regp->fp_debug_2);
|
|
+
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_MARGIN_COLOR, regp->fp_margin_color);
|
|
+
|
|
+ if (nv_gf4_disp_arch(dev))
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_8C0, regp->ramdac_8c0);
|
|
+
|
|
+ if (dev_priv->card_type == NV_40) {
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_A20, regp->ramdac_a20);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_A24, regp->ramdac_a24);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_A34, regp->ramdac_a34);
|
|
+
|
|
+ for (i = 0; i < 38; i++)
|
|
+ NVWriteRAMDAC(dev, head,
|
|
+ NV_PRAMDAC_CTV + 4*i, regp->ctv_regs[i]);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+nv_save_state_vga(struct drm_device *dev, int head,
|
|
+ struct nv04_mode_state *state)
|
|
+{
|
|
+ struct nv04_crtc_reg *regp = &state->crtc_reg[head];
|
|
+ int i;
|
|
+
|
|
+ regp->MiscOutReg = NVReadPRMVIO(dev, head, NV_PRMVIO_MISC__READ);
|
|
+
|
|
+ for (i = 0; i < 25; i++)
|
|
+ rd_cio_state(dev, head, regp, i);
|
|
+
|
|
+ NVSetEnablePalette(dev, head, true);
|
|
+ for (i = 0; i < 21; i++)
|
|
+ regp->Attribute[i] = NVReadVgaAttr(dev, head, i);
|
|
+ NVSetEnablePalette(dev, head, false);
|
|
+
|
|
+ for (i = 0; i < 9; i++)
|
|
+ regp->Graphics[i] = NVReadVgaGr(dev, head, i);
|
|
+
|
|
+ for (i = 0; i < 5; i++)
|
|
+ regp->Sequencer[i] = NVReadVgaSeq(dev, head, i);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv_load_state_vga(struct drm_device *dev, int head,
|
|
+ struct nv04_mode_state *state)
|
|
+{
|
|
+ struct nv04_crtc_reg *regp = &state->crtc_reg[head];
|
|
+ int i;
|
|
+
|
|
+ NVWritePRMVIO(dev, head, NV_PRMVIO_MISC__WRITE, regp->MiscOutReg);
|
|
+
|
|
+ for (i = 0; i < 5; i++)
|
|
+ NVWriteVgaSeq(dev, head, i, regp->Sequencer[i]);
|
|
+
|
|
+ nv_lock_vga_crtc_base(dev, head, false);
|
|
+ for (i = 0; i < 25; i++)
|
|
+ wr_cio_state(dev, head, regp, i);
|
|
+ nv_lock_vga_crtc_base(dev, head, true);
|
|
+
|
|
+ for (i = 0; i < 9; i++)
|
|
+ NVWriteVgaGr(dev, head, i, regp->Graphics[i]);
|
|
+
|
|
+ NVSetEnablePalette(dev, head, true);
|
|
+ for (i = 0; i < 21; i++)
|
|
+ NVWriteVgaAttr(dev, head, i, regp->Attribute[i]);
|
|
+ NVSetEnablePalette(dev, head, false);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv_save_state_ext(struct drm_device *dev, int head,
|
|
+ struct nv04_mode_state *state)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nv04_crtc_reg *regp = &state->crtc_reg[head];
|
|
+ int i;
|
|
+
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_LCD__INDEX);
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_RPC0_INDEX);
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_RPC1_INDEX);
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_LSR_INDEX);
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_PIXEL_INDEX);
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_HEB__INDEX);
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_ENH_INDEX);
|
|
+
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX);
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX);
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_21);
|
|
+ if (dev_priv->card_type >= NV_30)
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_47);
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_49);
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX);
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX);
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_ILACE__INDEX);
|
|
+
|
|
+ if (dev_priv->card_type >= NV_10) {
|
|
+ regp->crtc_830 = NVReadCRTC(dev, head, NV_PCRTC_830);
|
|
+ regp->crtc_834 = NVReadCRTC(dev, head, NV_PCRTC_834);
|
|
+
|
|
+ if (dev_priv->card_type >= NV_30)
|
|
+ regp->gpio_ext = NVReadCRTC(dev, head, NV_PCRTC_GPIO_EXT);
|
|
+
|
|
+ if (dev_priv->card_type == NV_40)
|
|
+ regp->crtc_850 = NVReadCRTC(dev, head, NV_PCRTC_850);
|
|
+
|
|
+ if (nv_two_heads(dev))
|
|
+ regp->crtc_eng_ctrl = NVReadCRTC(dev, head, NV_PCRTC_ENGINE_CTRL);
|
|
+ regp->cursor_cfg = NVReadCRTC(dev, head, NV_PCRTC_CURSOR_CONFIG);
|
|
+ }
|
|
+
|
|
+ regp->crtc_cfg = NVReadCRTC(dev, head, NV_PCRTC_CONFIG);
|
|
+
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH3__INDEX);
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH4__INDEX);
|
|
+ if (dev_priv->card_type >= NV_10) {
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_EBR_INDEX);
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_CSB);
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_4B);
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_TVOUT_LATENCY);
|
|
+ }
|
|
+ /* NV11 and NV20 don't have this, they stop at 0x52. */
|
|
+ if (nv_gf4_disp_arch(dev)) {
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_53);
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_54);
|
|
+
|
|
+ for (i = 0; i < 0x10; i++)
|
|
+ regp->CR58[i] = NVReadVgaCrtc5758(dev, head, i);
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_59);
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_5B);
|
|
+
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_85);
|
|
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_86);
|
|
+ }
|
|
+
|
|
+ regp->fb_start = NVReadCRTC(dev, head, NV_PCRTC_START);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv_load_state_ext(struct drm_device *dev, int head,
|
|
+ struct nv04_mode_state *state)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nv04_crtc_reg *regp = &state->crtc_reg[head];
|
|
+ uint32_t reg900;
|
|
+ int i;
|
|
+
|
|
+ if (dev_priv->card_type >= NV_10) {
|
|
+ if (nv_two_heads(dev))
|
|
+ /* setting ENGINE_CTRL (EC) *must* come before
|
|
+ * CIO_CRE_LCD, as writing CRE_LCD sets bits 16 & 17 in
|
|
+ * EC that should not be overwritten by writing stale EC
|
|
+ */
|
|
+ NVWriteCRTC(dev, head, NV_PCRTC_ENGINE_CTRL, regp->crtc_eng_ctrl);
|
|
+
|
|
+ nvWriteVIDEO(dev, NV_PVIDEO_STOP, 1);
|
|
+ nvWriteVIDEO(dev, NV_PVIDEO_INTR_EN, 0);
|
|
+ nvWriteVIDEO(dev, NV_PVIDEO_OFFSET_BUFF(0), 0);
|
|
+ nvWriteVIDEO(dev, NV_PVIDEO_OFFSET_BUFF(1), 0);
|
|
+ nvWriteVIDEO(dev, NV_PVIDEO_LIMIT(0), dev_priv->fb_available_size - 1);
|
|
+ nvWriteVIDEO(dev, NV_PVIDEO_LIMIT(1), dev_priv->fb_available_size - 1);
|
|
+ nvWriteVIDEO(dev, NV_PVIDEO_UVPLANE_LIMIT(0), dev_priv->fb_available_size - 1);
|
|
+ nvWriteVIDEO(dev, NV_PVIDEO_UVPLANE_LIMIT(1), dev_priv->fb_available_size - 1);
|
|
+ nvWriteMC(dev, NV_PBUS_POWERCTRL_2, 0);
|
|
+
|
|
+ NVWriteCRTC(dev, head, NV_PCRTC_CURSOR_CONFIG, regp->cursor_cfg);
|
|
+ NVWriteCRTC(dev, head, NV_PCRTC_830, regp->crtc_830);
|
|
+ NVWriteCRTC(dev, head, NV_PCRTC_834, regp->crtc_834);
|
|
+
|
|
+ if (dev_priv->card_type >= NV_30)
|
|
+ NVWriteCRTC(dev, head, NV_PCRTC_GPIO_EXT, regp->gpio_ext);
|
|
+
|
|
+ if (dev_priv->card_type == NV_40) {
|
|
+ NVWriteCRTC(dev, head, NV_PCRTC_850, regp->crtc_850);
|
|
+
|
|
+ reg900 = NVReadRAMDAC(dev, head, NV_PRAMDAC_900);
|
|
+ if (regp->crtc_cfg == NV_PCRTC_CONFIG_START_ADDRESS_HSYNC)
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_900, reg900 | 0x10000);
|
|
+ else
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_900, reg900 & ~0x10000);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ NVWriteCRTC(dev, head, NV_PCRTC_CONFIG, regp->crtc_cfg);
|
|
+
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_RPC0_INDEX);
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_RPC1_INDEX);
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_LSR_INDEX);
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_PIXEL_INDEX);
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_LCD__INDEX);
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_HEB__INDEX);
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_ENH_INDEX);
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX);
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX);
|
|
+ if (dev_priv->card_type >= NV_30)
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_47);
|
|
+
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_49);
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX);
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX);
|
|
+ if (dev_priv->card_type == NV_40)
|
|
+ nv_fix_nv40_hw_cursor(dev, head);
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_ILACE__INDEX);
|
|
+
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH3__INDEX);
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH4__INDEX);
|
|
+ if (dev_priv->card_type >= NV_10) {
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_EBR_INDEX);
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_CSB);
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_4B);
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_TVOUT_LATENCY);
|
|
+ }
|
|
+ /* NV11 and NV20 stop at 0x52. */
|
|
+ if (nv_gf4_disp_arch(dev)) {
|
|
+ if (dev_priv->card_type == NV_10) {
|
|
+ /* Not waiting for vertical retrace before modifying
|
|
+ CRE_53/CRE_54 causes lockups. */
|
|
+ nouveau_wait_until(dev, 650000000, NV_PRMCIO_INP0__COLOR, 0x8, 0x8);
|
|
+ nouveau_wait_until(dev, 650000000, NV_PRMCIO_INP0__COLOR, 0x8, 0x0);
|
|
+ }
|
|
+
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_53);
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_54);
|
|
+
|
|
+ for (i = 0; i < 0x10; i++)
|
|
+ NVWriteVgaCrtc5758(dev, head, i, regp->CR58[i]);
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_59);
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_5B);
|
|
+
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_85);
|
|
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_86);
|
|
+ }
|
|
+
|
|
+ NVWriteCRTC(dev, head, NV_PCRTC_START, regp->fb_start);
|
|
+
|
|
+ /* Setting 1 on this value gives you interrupts for every vblank period. */
|
|
+ NVWriteCRTC(dev, head, NV_PCRTC_INTR_EN_0, 0);
|
|
+ NVWriteCRTC(dev, head, NV_PCRTC_INTR_0, NV_PCRTC_INTR_0_VBLANK);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv_save_state_palette(struct drm_device *dev, int head,
|
|
+ struct nv04_mode_state *state)
|
|
+{
|
|
+ int head_offset = head * NV_PRMDIO_SIZE, i;
|
|
+
|
|
+ nv_wr08(dev, NV_PRMDIO_PIXEL_MASK + head_offset,
|
|
+ NV_PRMDIO_PIXEL_MASK_MASK);
|
|
+ nv_wr08(dev, NV_PRMDIO_READ_MODE_ADDRESS + head_offset, 0x0);
|
|
+
|
|
+ for (i = 0; i < 768; i++) {
|
|
+ state->crtc_reg[head].DAC[i] = nv_rd08(dev,
|
|
+ NV_PRMDIO_PALETTE_DATA + head_offset);
|
|
+ }
|
|
+
|
|
+ NVSetEnablePalette(dev, head, false);
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_hw_load_state_palette(struct drm_device *dev, int head,
|
|
+ struct nv04_mode_state *state)
|
|
+{
|
|
+ int head_offset = head * NV_PRMDIO_SIZE, i;
|
|
+
|
|
+ nv_wr08(dev, NV_PRMDIO_PIXEL_MASK + head_offset,
|
|
+ NV_PRMDIO_PIXEL_MASK_MASK);
|
|
+ nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS + head_offset, 0x0);
|
|
+
|
|
+ for (i = 0; i < 768; i++) {
|
|
+ nv_wr08(dev, NV_PRMDIO_PALETTE_DATA + head_offset,
|
|
+ state->crtc_reg[head].DAC[i]);
|
|
+ }
|
|
+
|
|
+ NVSetEnablePalette(dev, head, false);
|
|
+}
|
|
+
|
|
+void nouveau_hw_save_state(struct drm_device *dev, int head,
|
|
+ struct nv04_mode_state *state)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ if (dev_priv->chipset == 0x11)
|
|
+ /* NB: no attempt is made to restore the bad pll later on */
|
|
+ nouveau_hw_fix_bad_vpll(dev, head);
|
|
+ nv_save_state_ramdac(dev, head, state);
|
|
+ nv_save_state_vga(dev, head, state);
|
|
+ nv_save_state_palette(dev, head, state);
|
|
+ nv_save_state_ext(dev, head, state);
|
|
+}
|
|
+
|
|
+void nouveau_hw_load_state(struct drm_device *dev, int head,
|
|
+ struct nv04_mode_state *state)
|
|
+{
|
|
+ NVVgaProtect(dev, head, true);
|
|
+ nv_load_state_ramdac(dev, head, state);
|
|
+ nv_load_state_ext(dev, head, state);
|
|
+ nouveau_hw_load_state_palette(dev, head, state);
|
|
+ nv_load_state_vga(dev, head, state);
|
|
+ NVVgaProtect(dev, head, false);
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.h b/drivers/gpu/drm/nouveau/nouveau_hw.h
|
|
new file mode 100644
|
|
index 0000000..869130f
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_hw.h
|
|
@@ -0,0 +1,455 @@
|
|
+/*
|
|
+ * Copyright 2008 Stuart Bennett
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice shall be included in
|
|
+ * all copies or substantial portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
|
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
+ * SOFTWARE.
|
|
+ */
|
|
+
|
|
+#ifndef __NOUVEAU_HW_H__
|
|
+#define __NOUVEAU_HW_H__
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "nouveau_drv.h"
|
|
+
|
|
+#define MASK(field) ( \
|
|
+ (0xffffffff >> (31 - ((1 ? field) - (0 ? field)))) << (0 ? field))
|
|
+
|
|
+#define XLATE(src, srclowbit, outfield) ( \
|
|
+ (((src) >> (srclowbit)) << (0 ? outfield)) & MASK(outfield))
|
|
+
|
|
+void NVWriteVgaSeq(struct drm_device *, int head, uint8_t index, uint8_t value);
|
|
+uint8_t NVReadVgaSeq(struct drm_device *, int head, uint8_t index);
|
|
+void NVWriteVgaGr(struct drm_device *, int head, uint8_t index, uint8_t value);
|
|
+uint8_t NVReadVgaGr(struct drm_device *, int head, uint8_t index);
|
|
+void NVSetOwner(struct drm_device *, int owner);
|
|
+void NVBlankScreen(struct drm_device *, int head, bool blank);
|
|
+void nouveau_hw_setpll(struct drm_device *, uint32_t reg1,
|
|
+ struct nouveau_pll_vals *pv);
|
|
+int nouveau_hw_get_pllvals(struct drm_device *, enum pll_types plltype,
|
|
+ struct nouveau_pll_vals *pllvals);
|
|
+int nouveau_hw_pllvals_to_clk(struct nouveau_pll_vals *pllvals);
|
|
+int nouveau_hw_get_clock(struct drm_device *, enum pll_types plltype);
|
|
+void nouveau_hw_save_vga_fonts(struct drm_device *, bool save);
|
|
+void nouveau_hw_save_state(struct drm_device *, int head,
|
|
+ struct nv04_mode_state *state);
|
|
+void nouveau_hw_load_state(struct drm_device *, int head,
|
|
+ struct nv04_mode_state *state);
|
|
+void nouveau_hw_load_state_palette(struct drm_device *, int head,
|
|
+ struct nv04_mode_state *state);
|
|
+
|
|
+/* nouveau_calc.c */
|
|
+extern void nouveau_calc_arb(struct drm_device *, int vclk, int bpp,
|
|
+ int *burst, int *lwm);
|
|
+extern int nouveau_calc_pll_mnp(struct drm_device *, struct pll_lims *pll_lim,
|
|
+ int clk, struct nouveau_pll_vals *pv);
|
|
+
|
|
+static inline uint32_t
|
|
+nvReadMC(struct drm_device *dev, uint32_t reg)
|
|
+{
|
|
+ uint32_t val = nv_rd32(dev, reg);
|
|
+ NV_REG_DEBUG(MC, dev, "reg %08x val %08x\n", reg, val);
|
|
+ return val;
|
|
+}
|
|
+
|
|
+static inline void
|
|
+nvWriteMC(struct drm_device *dev, uint32_t reg, uint32_t val)
|
|
+{
|
|
+ NV_REG_DEBUG(MC, dev, "reg %08x val %08x\n", reg, val);
|
|
+ nv_wr32(dev, reg, val);
|
|
+}
|
|
+
|
|
+static inline uint32_t
|
|
+nvReadVIDEO(struct drm_device *dev, uint32_t reg)
|
|
+{
|
|
+ uint32_t val = nv_rd32(dev, reg);
|
|
+ NV_REG_DEBUG(VIDEO, dev, "reg %08x val %08x\n", reg, val);
|
|
+ return val;
|
|
+}
|
|
+
|
|
+static inline void
|
|
+nvWriteVIDEO(struct drm_device *dev, uint32_t reg, uint32_t val)
|
|
+{
|
|
+ NV_REG_DEBUG(VIDEO, dev, "reg %08x val %08x\n", reg, val);
|
|
+ nv_wr32(dev, reg, val);
|
|
+}
|
|
+
|
|
+static inline uint32_t
|
|
+nvReadFB(struct drm_device *dev, uint32_t reg)
|
|
+{
|
|
+ uint32_t val = nv_rd32(dev, reg);
|
|
+ NV_REG_DEBUG(FB, dev, "reg %08x val %08x\n", reg, val);
|
|
+ return val;
|
|
+}
|
|
+
|
|
+static inline void
|
|
+nvWriteFB(struct drm_device *dev, uint32_t reg, uint32_t val)
|
|
+{
|
|
+ NV_REG_DEBUG(FB, dev, "reg %08x val %08x\n", reg, val);
|
|
+ nv_wr32(dev, reg, val);
|
|
+}
|
|
+
|
|
+static inline uint32_t
|
|
+nvReadEXTDEV(struct drm_device *dev, uint32_t reg)
|
|
+{
|
|
+ uint32_t val = nv_rd32(dev, reg);
|
|
+ NV_REG_DEBUG(EXTDEV, dev, "reg %08x val %08x\n", reg, val);
|
|
+ return val;
|
|
+}
|
|
+
|
|
+static inline void
|
|
+nvWriteEXTDEV(struct drm_device *dev, uint32_t reg, uint32_t val)
|
|
+{
|
|
+ NV_REG_DEBUG(EXTDEV, dev, "reg %08x val %08x\n", reg, val);
|
|
+ nv_wr32(dev, reg, val);
|
|
+}
|
|
+
|
|
+static inline uint32_t NVReadCRTC(struct drm_device *dev,
|
|
+ int head, uint32_t reg)
|
|
+{
|
|
+ uint32_t val;
|
|
+ if (head)
|
|
+ reg += NV_PCRTC0_SIZE;
|
|
+ val = nv_rd32(dev, reg);
|
|
+ NV_REG_DEBUG(CRTC, dev, "head %d reg %08x val %08x\n", head, reg, val);
|
|
+ return val;
|
|
+}
|
|
+
|
|
+static inline void NVWriteCRTC(struct drm_device *dev,
|
|
+ int head, uint32_t reg, uint32_t val)
|
|
+{
|
|
+ if (head)
|
|
+ reg += NV_PCRTC0_SIZE;
|
|
+ NV_REG_DEBUG(CRTC, dev, "head %d reg %08x val %08x\n", head, reg, val);
|
|
+ nv_wr32(dev, reg, val);
|
|
+}
|
|
+
|
|
+static inline uint32_t NVReadRAMDAC(struct drm_device *dev,
|
|
+ int head, uint32_t reg)
|
|
+{
|
|
+ uint32_t val;
|
|
+ if (head)
|
|
+ reg += NV_PRAMDAC0_SIZE;
|
|
+ val = nv_rd32(dev, reg);
|
|
+ NV_REG_DEBUG(RAMDAC, dev, "head %d reg %08x val %08x\n",
|
|
+ head, reg, val);
|
|
+ return val;
|
|
+}
|
|
+
|
|
+static inline void NVWriteRAMDAC(struct drm_device *dev,
|
|
+ int head, uint32_t reg, uint32_t val)
|
|
+{
|
|
+ if (head)
|
|
+ reg += NV_PRAMDAC0_SIZE;
|
|
+ NV_REG_DEBUG(RAMDAC, dev, "head %d reg %08x val %08x\n",
|
|
+ head, reg, val);
|
|
+ nv_wr32(dev, reg, val);
|
|
+}
|
|
+
|
|
+static inline uint8_t nv_read_tmds(struct drm_device *dev,
|
|
+ int or, int dl, uint8_t address)
|
|
+{
|
|
+ int ramdac = (or & OUTPUT_C) >> 2;
|
|
+
|
|
+ NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL + dl * 8,
|
|
+ NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE | address);
|
|
+ return NVReadRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA + dl * 8);
|
|
+}
|
|
+
|
|
+static inline void nv_write_tmds(struct drm_device *dev,
|
|
+ int or, int dl, uint8_t address,
|
|
+ uint8_t data)
|
|
+{
|
|
+ int ramdac = (or & OUTPUT_C) >> 2;
|
|
+
|
|
+ NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA + dl * 8, data);
|
|
+ NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL + dl * 8, address);
|
|
+}
|
|
+
|
|
+static inline void NVWriteVgaCrtc(struct drm_device *dev,
|
|
+ int head, uint8_t index, uint8_t value)
|
|
+{
|
|
+ NV_REG_DEBUG(VGACRTC, dev, "head %d index 0x%02x data 0x%02x\n",
|
|
+ head, index, value);
|
|
+ nv_wr08(dev, NV_PRMCIO_CRX__COLOR + head * NV_PRMCIO_SIZE, index);
|
|
+ nv_wr08(dev, NV_PRMCIO_CR__COLOR + head * NV_PRMCIO_SIZE, value);
|
|
+}
|
|
+
|
|
+static inline uint8_t NVReadVgaCrtc(struct drm_device *dev,
|
|
+ int head, uint8_t index)
|
|
+{
|
|
+ uint8_t val;
|
|
+ nv_wr08(dev, NV_PRMCIO_CRX__COLOR + head * NV_PRMCIO_SIZE, index);
|
|
+ val = nv_rd08(dev, NV_PRMCIO_CR__COLOR + head * NV_PRMCIO_SIZE);
|
|
+ NV_REG_DEBUG(VGACRTC, dev, "head %d index 0x%02x data 0x%02x\n",
|
|
+ head, index, val);
|
|
+ return val;
|
|
+}
|
|
+
|
|
+/* CR57 and CR58 are a fun pair of regs. CR57 provides an index (0-0xf) for CR58
|
|
+ * I suspect they in fact do nothing, but are merely a way to carry useful
|
|
+ * per-head variables around
|
|
+ *
|
|
+ * Known uses:
|
|
+ * CR57 CR58
|
|
+ * 0x00 index to the appropriate dcb entry (or 7f for inactive)
|
|
+ * 0x02 dcb entry's "or" value (or 00 for inactive)
|
|
+ * 0x03 bit0 set for dual link (LVDS, possibly elsewhere too)
|
|
+ * 0x08 or 0x09 pxclk in MHz
|
|
+ * 0x0f laptop panel info - low nibble for PEXTDEV_BOOT_0 strap
|
|
+ * high nibble for xlat strap value
|
|
+ */
|
|
+
|
|
+static inline void
|
|
+NVWriteVgaCrtc5758(struct drm_device *dev, int head, uint8_t index, uint8_t value)
|
|
+{
|
|
+ NVWriteVgaCrtc(dev, head, NV_CIO_CRE_57, index);
|
|
+ NVWriteVgaCrtc(dev, head, NV_CIO_CRE_58, value);
|
|
+}
|
|
+
|
|
+static inline uint8_t NVReadVgaCrtc5758(struct drm_device *dev, int head, uint8_t index)
|
|
+{
|
|
+ NVWriteVgaCrtc(dev, head, NV_CIO_CRE_57, index);
|
|
+ return NVReadVgaCrtc(dev, head, NV_CIO_CRE_58);
|
|
+}
|
|
+
|
|
+static inline uint8_t NVReadPRMVIO(struct drm_device *dev,
|
|
+ int head, uint32_t reg)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint8_t val;
|
|
+
|
|
+ /* Only NV4x have two pvio ranges; other twoHeads cards MUST call
|
|
+ * NVSetOwner for the relevant head to be programmed */
|
|
+ if (head && dev_priv->card_type == NV_40)
|
|
+ reg += NV_PRMVIO_SIZE;
|
|
+
|
|
+ val = nv_rd08(dev, reg);
|
|
+ NV_REG_DEBUG(RMVIO, dev, "head %d reg %08x val %02x\n", head, reg, val);
|
|
+ return val;
|
|
+}
|
|
+
|
|
+static inline void NVWritePRMVIO(struct drm_device *dev,
|
|
+ int head, uint32_t reg, uint8_t value)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ /* Only NV4x have two pvio ranges; other twoHeads cards MUST call
|
|
+ * NVSetOwner for the relevant head to be programmed */
|
|
+ if (head && dev_priv->card_type == NV_40)
|
|
+ reg += NV_PRMVIO_SIZE;
|
|
+
|
|
+ NV_REG_DEBUG(RMVIO, dev, "head %d reg %08x val %02x\n",
|
|
+ head, reg, value);
|
|
+ nv_wr08(dev, reg, value);
|
|
+}
|
|
+
|
|
+static inline void NVSetEnablePalette(struct drm_device *dev, int head, bool enable)
|
|
+{
|
|
+ nv_rd08(dev, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE);
|
|
+ nv_wr08(dev, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, enable ? 0 : 0x20);
|
|
+}
|
|
+
|
|
+static inline bool NVGetEnablePalette(struct drm_device *dev, int head)
|
|
+{
|
|
+ nv_rd08(dev, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE);
|
|
+ return !(nv_rd08(dev, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE) & 0x20);
|
|
+}
|
|
+
|
|
+static inline void NVWriteVgaAttr(struct drm_device *dev,
|
|
+ int head, uint8_t index, uint8_t value)
|
|
+{
|
|
+ if (NVGetEnablePalette(dev, head))
|
|
+ index &= ~0x20;
|
|
+ else
|
|
+ index |= 0x20;
|
|
+
|
|
+ nv_rd08(dev, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE);
|
|
+ NV_REG_DEBUG(VGAATTR, dev, "head %d index 0x%02x data 0x%02x\n",
|
|
+ head, index, value);
|
|
+ nv_wr08(dev, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, index);
|
|
+ nv_wr08(dev, NV_PRMCIO_AR__WRITE + head * NV_PRMCIO_SIZE, value);
|
|
+}
|
|
+
|
|
+static inline uint8_t NVReadVgaAttr(struct drm_device *dev,
|
|
+ int head, uint8_t index)
|
|
+{
|
|
+ uint8_t val;
|
|
+ if (NVGetEnablePalette(dev, head))
|
|
+ index &= ~0x20;
|
|
+ else
|
|
+ index |= 0x20;
|
|
+
|
|
+ nv_rd08(dev, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE);
|
|
+ nv_wr08(dev, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, index);
|
|
+ val = nv_rd08(dev, NV_PRMCIO_AR__READ + head * NV_PRMCIO_SIZE);
|
|
+ NV_REG_DEBUG(VGAATTR, dev, "head %d index 0x%02x data 0x%02x\n",
|
|
+ head, index, val);
|
|
+ return val;
|
|
+}
|
|
+
|
|
+static inline void NVVgaSeqReset(struct drm_device *dev, int head, bool start)
|
|
+{
|
|
+ NVWriteVgaSeq(dev, head, NV_VIO_SR_RESET_INDEX, start ? 0x1 : 0x3);
|
|
+}
|
|
+
|
|
+static inline void NVVgaProtect(struct drm_device *dev, int head, bool protect)
|
|
+{
|
|
+ uint8_t seq1 = NVReadVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX);
|
|
+
|
|
+ if (protect) {
|
|
+ NVVgaSeqReset(dev, head, true);
|
|
+ NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 | 0x20);
|
|
+ } else {
|
|
+ /* Reenable sequencer, then turn on screen */
|
|
+ NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 & ~0x20); /* reenable display */
|
|
+ NVVgaSeqReset(dev, head, false);
|
|
+ }
|
|
+ NVSetEnablePalette(dev, head, protect);
|
|
+}
|
|
+
|
|
+static inline bool
|
|
+nv_heads_tied(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ if (dev_priv->chipset == 0x11)
|
|
+ return !!(nvReadMC(dev, NV_PBUS_DEBUG_1) & (1 << 28));
|
|
+
|
|
+ return NVReadVgaCrtc(dev, 0, NV_CIO_CRE_44) & 0x4;
|
|
+}
|
|
+
|
|
+/* makes cr0-7 on the specified head read-only */
|
|
+static inline bool
|
|
+nv_lock_vga_crtc_base(struct drm_device *dev, int head, bool lock)
|
|
+{
|
|
+ uint8_t cr11 = NVReadVgaCrtc(dev, head, NV_CIO_CR_VRE_INDEX);
|
|
+ bool waslocked = cr11 & 0x80;
|
|
+
|
|
+ if (lock)
|
|
+ cr11 |= 0x80;
|
|
+ else
|
|
+ cr11 &= ~0x80;
|
|
+ NVWriteVgaCrtc(dev, head, NV_CIO_CR_VRE_INDEX, cr11);
|
|
+
|
|
+ return waslocked;
|
|
+}
|
|
+
|
|
+static inline void
|
|
+nv_lock_vga_crtc_shadow(struct drm_device *dev, int head, int lock)
|
|
+{
|
|
+ /* shadow lock: connects 0x60?3d? regs to "real" 0x3d? regs
|
|
+ * bit7: unlocks HDT, HBS, HBE, HRS, HRE, HEB
|
|
+ * bit6: seems to have some effect on CR09 (double scan, VBS_9)
|
|
+ * bit5: unlocks HDE
|
|
+ * bit4: unlocks VDE
|
|
+ * bit3: unlocks VDT, OVL, VRS, ?VRE?, VBS, VBE, LSR, EBR
|
|
+ * bit2: same as bit 1 of 0x60?804
|
|
+ * bit0: same as bit 0 of 0x60?804
|
|
+ */
|
|
+
|
|
+ uint8_t cr21 = lock;
|
|
+
|
|
+ if (lock < 0)
|
|
+ /* 0xfa is generic "unlock all" mask */
|
|
+ cr21 = NVReadVgaCrtc(dev, head, NV_CIO_CRE_21) | 0xfa;
|
|
+
|
|
+ NVWriteVgaCrtc(dev, head, NV_CIO_CRE_21, cr21);
|
|
+}
|
|
+
|
|
+/* renders the extended crtc regs (cr19+) on all crtcs impervious:
|
|
+ * immutable and unreadable
|
|
+ */
|
|
+static inline bool
|
|
+NVLockVgaCrtcs(struct drm_device *dev, bool lock)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ bool waslocked = !NVReadVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX);
|
|
+
|
|
+ NVWriteVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX,
|
|
+ lock ? NV_CIO_SR_LOCK_VALUE : NV_CIO_SR_UNLOCK_RW_VALUE);
|
|
+ /* NV11 has independently lockable extended crtcs, except when tied */
|
|
+ if (dev_priv->chipset == 0x11 && !nv_heads_tied(dev))
|
|
+ NVWriteVgaCrtc(dev, 1, NV_CIO_SR_LOCK_INDEX,
|
|
+ lock ? NV_CIO_SR_LOCK_VALUE :
|
|
+ NV_CIO_SR_UNLOCK_RW_VALUE);
|
|
+
|
|
+ return waslocked;
|
|
+}
|
|
+
|
|
+/* nv04 cursor max dimensions of 32x32 (A1R5G5B5) */
|
|
+#define NV04_CURSOR_SIZE 32
|
|
+/* limit nv10 cursors to 64x64 (ARGB8) (we could go to 64x255) */
|
|
+#define NV10_CURSOR_SIZE 64
|
|
+
|
|
+static inline int nv_cursor_width(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ return dev_priv->card_type >= NV_10 ? NV10_CURSOR_SIZE : NV04_CURSOR_SIZE;
|
|
+}
|
|
+
|
|
+static inline void
|
|
+nv_fix_nv40_hw_cursor(struct drm_device *dev, int head)
|
|
+{
|
|
+ /* on some nv40 (such as the "true" (in the NV_PFB_BOOT_0 sense) nv40,
|
|
+ * the gf6800gt) a hardware bug requires a write to PRAMDAC_CURSOR_POS
|
|
+ * for changes to the CRTC CURCTL regs to take effect, whether changing
|
|
+ * the pixmap location, or just showing/hiding the cursor
|
|
+ */
|
|
+ uint32_t curpos = NVReadRAMDAC(dev, head, NV_PRAMDAC_CU_START_POS);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_CU_START_POS, curpos);
|
|
+}
|
|
+
|
|
+static inline void
|
|
+nv_show_cursor(struct drm_device *dev, int head, bool show)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint8_t *curctl1 =
|
|
+ &dev_priv->mode_reg.crtc_reg[head].CRTC[NV_CIO_CRE_HCUR_ADDR1_INDEX];
|
|
+
|
|
+ if (show)
|
|
+ *curctl1 |= MASK(NV_CIO_CRE_HCUR_ADDR1_ENABLE);
|
|
+ else
|
|
+ *curctl1 &= ~MASK(NV_CIO_CRE_HCUR_ADDR1_ENABLE);
|
|
+ NVWriteVgaCrtc(dev, head, NV_CIO_CRE_HCUR_ADDR1_INDEX, *curctl1);
|
|
+
|
|
+ if (dev_priv->card_type == NV_40)
|
|
+ nv_fix_nv40_hw_cursor(dev, head);
|
|
+}
|
|
+
|
|
+static inline uint32_t
|
|
+nv_pitch_align(struct drm_device *dev, uint32_t width, int bpp)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ int mask;
|
|
+
|
|
+ if (bpp == 15)
|
|
+ bpp = 16;
|
|
+ if (bpp == 24)
|
|
+ bpp = 8;
|
|
+
|
|
+ /* Alignment requirements taken from the Haiku driver */
|
|
+ if (dev_priv->card_type == NV_04)
|
|
+ mask = 128 / bpp - 1;
|
|
+ else
|
|
+ mask = 512 / bpp - 1;
|
|
+
|
|
+ return (width + mask) & ~mask;
|
|
+}
|
|
+
|
|
+#endif /* __NOUVEAU_HW_H__ */
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c
|
|
new file mode 100644
|
|
index 0000000..70e994d
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c
|
|
@@ -0,0 +1,269 @@
|
|
+/*
|
|
+ * Copyright 2009 Red Hat Inc.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice shall be included in
|
|
+ * all copies or substantial portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
+ * OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ * Authors: Ben Skeggs
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_i2c.h"
|
|
+#include "nouveau_hw.h"
|
|
+
|
|
+static void
|
|
+nv04_i2c_setscl(void *data, int state)
|
|
+{
|
|
+ struct nouveau_i2c_chan *i2c = data;
|
|
+ struct drm_device *dev = i2c->dev;
|
|
+ uint8_t val;
|
|
+
|
|
+ val = (NVReadVgaCrtc(dev, 0, i2c->wr) & 0xd0) | (state ? 0x20 : 0);
|
|
+ NVWriteVgaCrtc(dev, 0, i2c->wr, val | 0x01);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv04_i2c_setsda(void *data, int state)
|
|
+{
|
|
+ struct nouveau_i2c_chan *i2c = data;
|
|
+ struct drm_device *dev = i2c->dev;
|
|
+ uint8_t val;
|
|
+
|
|
+ val = (NVReadVgaCrtc(dev, 0, i2c->wr) & 0xe0) | (state ? 0x10 : 0);
|
|
+ NVWriteVgaCrtc(dev, 0, i2c->wr, val | 0x01);
|
|
+}
|
|
+
|
|
+static int
|
|
+nv04_i2c_getscl(void *data)
|
|
+{
|
|
+ struct nouveau_i2c_chan *i2c = data;
|
|
+ struct drm_device *dev = i2c->dev;
|
|
+
|
|
+ return !!(NVReadVgaCrtc(dev, 0, i2c->rd) & 4);
|
|
+}
|
|
+
|
|
+static int
|
|
+nv04_i2c_getsda(void *data)
|
|
+{
|
|
+ struct nouveau_i2c_chan *i2c = data;
|
|
+ struct drm_device *dev = i2c->dev;
|
|
+
|
|
+ return !!(NVReadVgaCrtc(dev, 0, i2c->rd) & 8);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv4e_i2c_setscl(void *data, int state)
|
|
+{
|
|
+ struct nouveau_i2c_chan *i2c = data;
|
|
+ struct drm_device *dev = i2c->dev;
|
|
+ uint8_t val;
|
|
+
|
|
+ val = (nv_rd32(dev, i2c->wr) & 0xd0) | (state ? 0x20 : 0);
|
|
+ nv_wr32(dev, i2c->wr, val | 0x01);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv4e_i2c_setsda(void *data, int state)
|
|
+{
|
|
+ struct nouveau_i2c_chan *i2c = data;
|
|
+ struct drm_device *dev = i2c->dev;
|
|
+ uint8_t val;
|
|
+
|
|
+ val = (nv_rd32(dev, i2c->wr) & 0xe0) | (state ? 0x10 : 0);
|
|
+ nv_wr32(dev, i2c->wr, val | 0x01);
|
|
+}
|
|
+
|
|
+static int
|
|
+nv4e_i2c_getscl(void *data)
|
|
+{
|
|
+ struct nouveau_i2c_chan *i2c = data;
|
|
+ struct drm_device *dev = i2c->dev;
|
|
+
|
|
+ return !!((nv_rd32(dev, i2c->rd) >> 16) & 4);
|
|
+}
|
|
+
|
|
+static int
|
|
+nv4e_i2c_getsda(void *data)
|
|
+{
|
|
+ struct nouveau_i2c_chan *i2c = data;
|
|
+ struct drm_device *dev = i2c->dev;
|
|
+
|
|
+ return !!((nv_rd32(dev, i2c->rd) >> 16) & 8);
|
|
+}
|
|
+
|
|
+static int
|
|
+nv50_i2c_getscl(void *data)
|
|
+{
|
|
+ struct nouveau_i2c_chan *i2c = data;
|
|
+ struct drm_device *dev = i2c->dev;
|
|
+
|
|
+ return !!(nv_rd32(dev, i2c->rd) & 1);
|
|
+}
|
|
+
|
|
+
|
|
+static int
|
|
+nv50_i2c_getsda(void *data)
|
|
+{
|
|
+ struct nouveau_i2c_chan *i2c = data;
|
|
+ struct drm_device *dev = i2c->dev;
|
|
+
|
|
+ return !!(nv_rd32(dev, i2c->rd) & 2);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_i2c_setscl(void *data, int state)
|
|
+{
|
|
+ struct nouveau_i2c_chan *i2c = data;
|
|
+ struct drm_device *dev = i2c->dev;
|
|
+
|
|
+ nv_wr32(dev, i2c->wr, 4 | (i2c->data ? 2 : 0) | (state ? 1 : 0));
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_i2c_setsda(void *data, int state)
|
|
+{
|
|
+ struct nouveau_i2c_chan *i2c = data;
|
|
+ struct drm_device *dev = i2c->dev;
|
|
+
|
|
+ nv_wr32(dev, i2c->wr,
|
|
+ (nv_rd32(dev, i2c->rd) & 1) | 4 | (state ? 2 : 0));
|
|
+ i2c->data = state;
|
|
+}
|
|
+
|
|
+static const uint32_t nv50_i2c_port[] = {
|
|
+ 0x00e138, 0x00e150, 0x00e168, 0x00e180,
|
|
+ 0x00e254, 0x00e274, 0x00e764, 0x00e780,
|
|
+ 0x00e79c, 0x00e7b8
|
|
+};
|
|
+#define NV50_I2C_PORTS ARRAY_SIZE(nv50_i2c_port)
|
|
+
|
|
+int
|
|
+nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_i2c_chan *i2c;
|
|
+ int ret;
|
|
+
|
|
+ if (entry->chan)
|
|
+ return -EEXIST;
|
|
+
|
|
+ if (dev_priv->card_type == NV_50 && entry->read >= NV50_I2C_PORTS) {
|
|
+ NV_ERROR(dev, "unknown i2c port %d\n", entry->read);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);
|
|
+ if (i2c == NULL)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ switch (entry->port_type) {
|
|
+ case 0:
|
|
+ i2c->algo.bit.setsda = nv04_i2c_setsda;
|
|
+ i2c->algo.bit.setscl = nv04_i2c_setscl;
|
|
+ i2c->algo.bit.getsda = nv04_i2c_getsda;
|
|
+ i2c->algo.bit.getscl = nv04_i2c_getscl;
|
|
+ i2c->rd = entry->read;
|
|
+ i2c->wr = entry->write;
|
|
+ break;
|
|
+ case 4:
|
|
+ i2c->algo.bit.setsda = nv4e_i2c_setsda;
|
|
+ i2c->algo.bit.setscl = nv4e_i2c_setscl;
|
|
+ i2c->algo.bit.getsda = nv4e_i2c_getsda;
|
|
+ i2c->algo.bit.getscl = nv4e_i2c_getscl;
|
|
+ i2c->rd = 0x600800 + entry->read;
|
|
+ i2c->wr = 0x600800 + entry->write;
|
|
+ break;
|
|
+ case 5:
|
|
+ i2c->algo.bit.setsda = nv50_i2c_setsda;
|
|
+ i2c->algo.bit.setscl = nv50_i2c_setscl;
|
|
+ i2c->algo.bit.getsda = nv50_i2c_getsda;
|
|
+ i2c->algo.bit.getscl = nv50_i2c_getscl;
|
|
+ i2c->rd = nv50_i2c_port[entry->read];
|
|
+ i2c->wr = i2c->rd;
|
|
+ break;
|
|
+ case 6:
|
|
+ i2c->rd = entry->read;
|
|
+ i2c->wr = entry->write;
|
|
+ break;
|
|
+ default:
|
|
+ NV_ERROR(dev, "DCB I2C port type %d unknown\n",
|
|
+ entry->port_type);
|
|
+ kfree(i2c);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ snprintf(i2c->adapter.name, sizeof(i2c->adapter.name),
|
|
+ "nouveau-%s-%d", pci_name(dev->pdev), index);
|
|
+ i2c->adapter.owner = THIS_MODULE;
|
|
+ i2c->adapter.dev.parent = &dev->pdev->dev;
|
|
+ i2c->dev = dev;
|
|
+ i2c_set_adapdata(&i2c->adapter, i2c);
|
|
+
|
|
+ if (entry->port_type < 6) {
|
|
+ i2c->adapter.algo_data = &i2c->algo.bit;
|
|
+ i2c->algo.bit.udelay = 40;
|
|
+ i2c->algo.bit.timeout = usecs_to_jiffies(5000);
|
|
+ i2c->algo.bit.data = i2c;
|
|
+ ret = i2c_bit_add_bus(&i2c->adapter);
|
|
+ } else {
|
|
+ i2c->adapter.algo_data = &i2c->algo.dp;
|
|
+ i2c->algo.dp.running = false;
|
|
+ i2c->algo.dp.address = 0;
|
|
+ i2c->algo.dp.aux_ch = nouveau_dp_i2c_aux_ch;
|
|
+ ret = i2c_dp_aux_add_bus(&i2c->adapter);
|
|
+ }
|
|
+
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Failed to register i2c %d\n", index);
|
|
+ kfree(i2c);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ entry->chan = i2c;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_i2c_fini(struct drm_device *dev, struct dcb_i2c_entry *entry)
|
|
+{
|
|
+ if (!entry->chan)
|
|
+ return;
|
|
+
|
|
+ i2c_del_adapter(&entry->chan->adapter);
|
|
+ kfree(entry->chan);
|
|
+ entry->chan = NULL;
|
|
+}
|
|
+
|
|
+struct nouveau_i2c_chan *
|
|
+nouveau_i2c_find(struct drm_device *dev, int index)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nvbios *bios = &dev_priv->VBIOS;
|
|
+
|
|
+ if (index > DCB_MAX_NUM_I2C_ENTRIES)
|
|
+ return NULL;
|
|
+
|
|
+ if (!bios->bdcb.dcb.i2c[index].chan) {
|
|
+ if (nouveau_i2c_init(dev, &bios->bdcb.dcb.i2c[index], index))
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ return bios->bdcb.dcb.i2c[index].chan;
|
|
+}
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.h b/drivers/gpu/drm/nouveau/nouveau_i2c.h
|
|
new file mode 100644
|
|
index 0000000..c8eaf7a
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_i2c.h
|
|
@@ -0,0 +1,52 @@
|
|
+/*
|
|
+ * Copyright 2009 Red Hat Inc.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice shall be included in
|
|
+ * all copies or substantial portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
+ * OTHER DEALINGS IN THE SOFTWARE.
|
|
+ */
|
|
+
|
|
+#ifndef __NOUVEAU_I2C_H__
|
|
+#define __NOUVEAU_I2C_H__
|
|
+
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/i2c-id.h>
|
|
+#include <linux/i2c-algo-bit.h>
|
|
+#include "drm_dp_helper.h"
|
|
+
|
|
+struct dcb_i2c_entry;
|
|
+
|
|
+struct nouveau_i2c_chan {
|
|
+ struct i2c_adapter adapter;
|
|
+ struct drm_device *dev;
|
|
+ union {
|
|
+ struct i2c_algo_bit_data bit;
|
|
+ struct i2c_algo_dp_aux_data dp;
|
|
+ } algo;
|
|
+ unsigned rd;
|
|
+ unsigned wr;
|
|
+ unsigned data;
|
|
+};
|
|
+
|
|
+int nouveau_i2c_init(struct drm_device *, struct dcb_i2c_entry *, int index);
|
|
+void nouveau_i2c_fini(struct drm_device *, struct dcb_i2c_entry *);
|
|
+struct nouveau_i2c_chan *nouveau_i2c_find(struct drm_device *, int index);
|
|
+
|
|
+int nouveau_dp_i2c_aux_ch(struct i2c_adapter *, int mode, uint8_t write_byte,
|
|
+ uint8_t *read_byte);
|
|
+
|
|
+#endif /* __NOUVEAU_I2C_H__ */
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_ioc32.c b/drivers/gpu/drm/nouveau/nouveau_ioc32.c
|
|
new file mode 100644
|
|
index 0000000..475ba81
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_ioc32.c
|
|
@@ -0,0 +1,70 @@
|
|
+/**
|
|
+ * \file mga_ioc32.c
|
|
+ *
|
|
+ * 32-bit ioctl compatibility routines for the MGA DRM.
|
|
+ *
|
|
+ * \author Dave Airlie <airlied@linux.ie> with code from patches by Egbert Eich
|
|
+ *
|
|
+ *
|
|
+ * Copyright (C) Paul Mackerras 2005
|
|
+ * Copyright (C) Egbert Eich 2003,2004
|
|
+ * Copyright (C) Dave Airlie 2005
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the next
|
|
+ * paragraph) shall be included in all copies or substantial portions of the
|
|
+ * Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
+ * IN THE SOFTWARE.
|
|
+ */
|
|
+
|
|
+#include <linux/compat.h>
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+
|
|
+#include "nouveau_drv.h"
|
|
+
|
|
+/**
|
|
+ * Called whenever a 32-bit process running under a 64-bit kernel
|
|
+ * performs an ioctl on /dev/dri/card<n>.
|
|
+ *
|
|
+ * \param filp file pointer.
|
|
+ * \param cmd command.
|
|
+ * \param arg user argument.
|
|
+ * \return zero on success or negative number on failure.
|
|
+ */
|
|
+long nouveau_compat_ioctl(struct file *filp, unsigned int cmd,
|
|
+ unsigned long arg)
|
|
+{
|
|
+ unsigned int nr = DRM_IOCTL_NR(cmd);
|
|
+ drm_ioctl_compat_t *fn = NULL;
|
|
+ int ret;
|
|
+
|
|
+ if (nr < DRM_COMMAND_BASE)
|
|
+ return drm_compat_ioctl(filp, cmd, arg);
|
|
+
|
|
+#if 0
|
|
+ if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(mga_compat_ioctls))
|
|
+ fn = nouveau_compat_ioctls[nr - DRM_COMMAND_BASE];
|
|
+#endif
|
|
+ if (fn != NULL)
|
|
+ ret = (*fn)(filp, cmd, arg);
|
|
+ else
|
|
+ ret = drm_ioctl(filp, cmd, arg);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_irq.c b/drivers/gpu/drm/nouveau/nouveau_irq.c
|
|
new file mode 100644
|
|
index 0000000..447f9f6
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_irq.c
|
|
@@ -0,0 +1,737 @@
|
|
+/*
|
|
+ * Copyright (C) 2006 Ben Skeggs.
|
|
+ *
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * Authors:
|
|
+ * Ben Skeggs <darktama@iinet.net.au>
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "nouveau_drm.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_reg.h"
|
|
+#include <linux/ratelimit.h>
|
|
+
|
|
+/* needed for hotplug irq */
|
|
+#include "nouveau_connector.h"
|
|
+#include "nv50_display.h"
|
|
+
|
|
+void
|
|
+nouveau_irq_preinstall(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ /* Master disable */
|
|
+ nv_wr32(dev, NV03_PMC_INTR_EN_0, 0);
|
|
+
|
|
+ if (dev_priv->card_type == NV_50) {
|
|
+ INIT_WORK(&dev_priv->irq_work, nv50_display_irq_handler_bh);
|
|
+ INIT_LIST_HEAD(&dev_priv->vbl_waiting);
|
|
+ }
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_irq_postinstall(struct drm_device *dev)
|
|
+{
|
|
+ /* Master enable */
|
|
+ nv_wr32(dev, NV03_PMC_INTR_EN_0, NV_PMC_INTR_EN_0_MASTER_ENABLE);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_irq_uninstall(struct drm_device *dev)
|
|
+{
|
|
+ /* Master disable */
|
|
+ nv_wr32(dev, NV03_PMC_INTR_EN_0, 0);
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_call_method(struct nouveau_channel *chan, int class, int mthd, int data)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
|
|
+ struct nouveau_pgraph_object_method *grm;
|
|
+ struct nouveau_pgraph_object_class *grc;
|
|
+
|
|
+ grc = dev_priv->engine.graph.grclass;
|
|
+ while (grc->id) {
|
|
+ if (grc->id == class)
|
|
+ break;
|
|
+ grc++;
|
|
+ }
|
|
+
|
|
+ if (grc->id != class || !grc->methods)
|
|
+ return -ENOENT;
|
|
+
|
|
+ grm = grc->methods;
|
|
+ while (grm->id) {
|
|
+ if (grm->id == mthd)
|
|
+ return grm->exec(chan, class, mthd, data);
|
|
+ grm++;
|
|
+ }
|
|
+
|
|
+ return -ENOENT;
|
|
+}
|
|
+
|
|
+static bool
|
|
+nouveau_fifo_swmthd(struct nouveau_channel *chan, uint32_t addr, uint32_t data)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ const int subc = (addr >> 13) & 0x7;
|
|
+ const int mthd = addr & 0x1ffc;
|
|
+
|
|
+ if (mthd == 0x0000) {
|
|
+ struct nouveau_gpuobj_ref *ref = NULL;
|
|
+
|
|
+ if (nouveau_gpuobj_ref_find(chan, data, &ref))
|
|
+ return false;
|
|
+
|
|
+ if (ref->gpuobj->engine != NVOBJ_ENGINE_SW)
|
|
+ return false;
|
|
+
|
|
+ chan->sw_subchannel[subc] = ref->gpuobj->class;
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_ENGINE, nv_rd32(dev,
|
|
+ NV04_PFIFO_CACHE1_ENGINE) & ~(0xf << subc * 4));
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ /* hw object */
|
|
+ if (nv_rd32(dev, NV04_PFIFO_CACHE1_ENGINE) & (1 << (subc*4)))
|
|
+ return false;
|
|
+
|
|
+ if (nouveau_call_method(chan, chan->sw_subchannel[subc], mthd, data))
|
|
+ return false;
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static void
|
|
+nouveau_fifo_irq_handler(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_engine *engine = &dev_priv->engine;
|
|
+ uint32_t status, reassign;
|
|
+ int cnt = 0;
|
|
+
|
|
+ reassign = nv_rd32(dev, NV03_PFIFO_CACHES) & 1;
|
|
+ while ((status = nv_rd32(dev, NV03_PFIFO_INTR_0)) && (cnt++ < 100)) {
|
|
+ struct nouveau_channel *chan = NULL;
|
|
+ uint32_t chid, get;
|
|
+
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHES, 0);
|
|
+
|
|
+ chid = engine->fifo.channel_id(dev);
|
|
+ if (chid >= 0 && chid < engine->fifo.channels)
|
|
+ chan = dev_priv->fifos[chid];
|
|
+ get = nv_rd32(dev, NV03_PFIFO_CACHE1_GET);
|
|
+
|
|
+ if (status & NV_PFIFO_INTR_CACHE_ERROR) {
|
|
+ uint32_t mthd, data;
|
|
+ int ptr;
|
|
+
|
|
+ /* NV_PFIFO_CACHE1_GET actually goes to 0xffc before
|
|
+ * wrapping on my G80 chips, but CACHE1 isn't big
|
|
+ * enough for this much data.. Tests show that it
|
|
+ * wraps around to the start at GET=0x800.. No clue
|
|
+ * as to why..
|
|
+ */
|
|
+ ptr = (get & 0x7ff) >> 2;
|
|
+
|
|
+ if (dev_priv->card_type < NV_40) {
|
|
+ mthd = nv_rd32(dev,
|
|
+ NV04_PFIFO_CACHE1_METHOD(ptr));
|
|
+ data = nv_rd32(dev,
|
|
+ NV04_PFIFO_CACHE1_DATA(ptr));
|
|
+ } else {
|
|
+ mthd = nv_rd32(dev,
|
|
+ NV40_PFIFO_CACHE1_METHOD(ptr));
|
|
+ data = nv_rd32(dev,
|
|
+ NV40_PFIFO_CACHE1_DATA(ptr));
|
|
+ }
|
|
+
|
|
+ if (!chan || !nouveau_fifo_swmthd(chan, mthd, data)) {
|
|
+ NV_INFO(dev, "PFIFO_CACHE_ERROR - Ch %d/%d "
|
|
+ "Mthd 0x%04x Data 0x%08x\n",
|
|
+ chid, (mthd >> 13) & 7, mthd & 0x1ffc,
|
|
+ data);
|
|
+ }
|
|
+
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 0);
|
|
+ nv_wr32(dev, NV03_PFIFO_INTR_0,
|
|
+ NV_PFIFO_INTR_CACHE_ERROR);
|
|
+
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0,
|
|
+ nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH0) & ~1);
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHE1_GET, get + 4);
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0,
|
|
+ nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH0) | 1);
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_HASH, 0);
|
|
+
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH,
|
|
+ nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUSH) | 1);
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1);
|
|
+
|
|
+ status &= ~NV_PFIFO_INTR_CACHE_ERROR;
|
|
+ }
|
|
+
|
|
+ if (status & NV_PFIFO_INTR_DMA_PUSHER) {
|
|
+ NV_INFO(dev, "PFIFO_DMA_PUSHER - Ch %d\n", chid);
|
|
+
|
|
+ status &= ~NV_PFIFO_INTR_DMA_PUSHER;
|
|
+ nv_wr32(dev, NV03_PFIFO_INTR_0,
|
|
+ NV_PFIFO_INTR_DMA_PUSHER);
|
|
+
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_STATE, 0x00000000);
|
|
+ if (nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT) != get)
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_GET,
|
|
+ get + 4);
|
|
+ }
|
|
+
|
|
+ if (status & NV_PFIFO_INTR_SEMAPHORE) {
|
|
+ uint32_t sem;
|
|
+
|
|
+ status &= ~NV_PFIFO_INTR_SEMAPHORE;
|
|
+ nv_wr32(dev, NV03_PFIFO_INTR_0,
|
|
+ NV_PFIFO_INTR_SEMAPHORE);
|
|
+
|
|
+ sem = nv_rd32(dev, NV10_PFIFO_CACHE1_SEMAPHORE);
|
|
+ nv_wr32(dev, NV10_PFIFO_CACHE1_SEMAPHORE, sem | 0x1);
|
|
+
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHE1_GET, get + 4);
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1);
|
|
+ }
|
|
+
|
|
+ if (status) {
|
|
+ NV_INFO(dev, "PFIFO_INTR 0x%08x - Ch %d\n",
|
|
+ status, chid);
|
|
+ nv_wr32(dev, NV03_PFIFO_INTR_0, status);
|
|
+ status = 0;
|
|
+ }
|
|
+
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHES, reassign);
|
|
+ }
|
|
+
|
|
+ if (status) {
|
|
+ NV_INFO(dev, "PFIFO still angry after %d spins, halt\n", cnt);
|
|
+ nv_wr32(dev, 0x2140, 0);
|
|
+ nv_wr32(dev, 0x140, 0);
|
|
+ }
|
|
+
|
|
+ nv_wr32(dev, NV03_PMC_INTR_0, NV_PMC_INTR_0_PFIFO_PENDING);
|
|
+}
|
|
+
|
|
+struct nouveau_bitfield_names {
|
|
+ uint32_t mask;
|
|
+ const char *name;
|
|
+};
|
|
+
|
|
+static struct nouveau_bitfield_names nstatus_names[] =
|
|
+{
|
|
+ { NV04_PGRAPH_NSTATUS_STATE_IN_USE, "STATE_IN_USE" },
|
|
+ { NV04_PGRAPH_NSTATUS_INVALID_STATE, "INVALID_STATE" },
|
|
+ { NV04_PGRAPH_NSTATUS_BAD_ARGUMENT, "BAD_ARGUMENT" },
|
|
+ { NV04_PGRAPH_NSTATUS_PROTECTION_FAULT, "PROTECTION_FAULT" }
|
|
+};
|
|
+
|
|
+static struct nouveau_bitfield_names nstatus_names_nv10[] =
|
|
+{
|
|
+ { NV10_PGRAPH_NSTATUS_STATE_IN_USE, "STATE_IN_USE" },
|
|
+ { NV10_PGRAPH_NSTATUS_INVALID_STATE, "INVALID_STATE" },
|
|
+ { NV10_PGRAPH_NSTATUS_BAD_ARGUMENT, "BAD_ARGUMENT" },
|
|
+ { NV10_PGRAPH_NSTATUS_PROTECTION_FAULT, "PROTECTION_FAULT" }
|
|
+};
|
|
+
|
|
+static struct nouveau_bitfield_names nsource_names[] =
|
|
+{
|
|
+ { NV03_PGRAPH_NSOURCE_NOTIFICATION, "NOTIFICATION" },
|
|
+ { NV03_PGRAPH_NSOURCE_DATA_ERROR, "DATA_ERROR" },
|
|
+ { NV03_PGRAPH_NSOURCE_PROTECTION_ERROR, "PROTECTION_ERROR" },
|
|
+ { NV03_PGRAPH_NSOURCE_RANGE_EXCEPTION, "RANGE_EXCEPTION" },
|
|
+ { NV03_PGRAPH_NSOURCE_LIMIT_COLOR, "LIMIT_COLOR" },
|
|
+ { NV03_PGRAPH_NSOURCE_LIMIT_ZETA, "LIMIT_ZETA" },
|
|
+ { NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD, "ILLEGAL_MTHD" },
|
|
+ { NV03_PGRAPH_NSOURCE_DMA_R_PROTECTION, "DMA_R_PROTECTION" },
|
|
+ { NV03_PGRAPH_NSOURCE_DMA_W_PROTECTION, "DMA_W_PROTECTION" },
|
|
+ { NV03_PGRAPH_NSOURCE_FORMAT_EXCEPTION, "FORMAT_EXCEPTION" },
|
|
+ { NV03_PGRAPH_NSOURCE_PATCH_EXCEPTION, "PATCH_EXCEPTION" },
|
|
+ { NV03_PGRAPH_NSOURCE_STATE_INVALID, "STATE_INVALID" },
|
|
+ { NV03_PGRAPH_NSOURCE_DOUBLE_NOTIFY, "DOUBLE_NOTIFY" },
|
|
+ { NV03_PGRAPH_NSOURCE_NOTIFY_IN_USE, "NOTIFY_IN_USE" },
|
|
+ { NV03_PGRAPH_NSOURCE_METHOD_CNT, "METHOD_CNT" },
|
|
+ { NV03_PGRAPH_NSOURCE_BFR_NOTIFICATION, "BFR_NOTIFICATION" },
|
|
+ { NV03_PGRAPH_NSOURCE_DMA_VTX_PROTECTION, "DMA_VTX_PROTECTION" },
|
|
+ { NV03_PGRAPH_NSOURCE_DMA_WIDTH_A, "DMA_WIDTH_A" },
|
|
+ { NV03_PGRAPH_NSOURCE_DMA_WIDTH_B, "DMA_WIDTH_B" },
|
|
+};
|
|
+
|
|
+static void
|
|
+nouveau_print_bitfield_names_(uint32_t value,
|
|
+ const struct nouveau_bitfield_names *namelist,
|
|
+ const int namelist_len)
|
|
+{
|
|
+ /*
|
|
+ * Caller must have already printed the KERN_* log level for us.
|
|
+ * Also the caller is responsible for adding the newline.
|
|
+ */
|
|
+ int i;
|
|
+ for (i = 0; i < namelist_len; ++i) {
|
|
+ uint32_t mask = namelist[i].mask;
|
|
+ if (value & mask) {
|
|
+ printk(" %s", namelist[i].name);
|
|
+ value &= ~mask;
|
|
+ }
|
|
+ }
|
|
+ if (value)
|
|
+ printk(" (unknown bits 0x%08x)", value);
|
|
+}
|
|
+#define nouveau_print_bitfield_names(val, namelist) \
|
|
+ nouveau_print_bitfield_names_((val), (namelist), ARRAY_SIZE(namelist))
|
|
+
|
|
+
|
|
+static int
|
|
+nouveau_graph_chid_from_grctx(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t inst;
|
|
+ int i;
|
|
+
|
|
+ if (dev_priv->card_type < NV_40)
|
|
+ return dev_priv->engine.fifo.channels;
|
|
+ else
|
|
+ if (dev_priv->card_type < NV_50) {
|
|
+ inst = (nv_rd32(dev, 0x40032c) & 0xfffff) << 4;
|
|
+
|
|
+ for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
|
|
+ struct nouveau_channel *chan = dev_priv->fifos[i];
|
|
+
|
|
+ if (!chan || !chan->ramin_grctx)
|
|
+ continue;
|
|
+
|
|
+ if (inst == chan->ramin_grctx->instance)
|
|
+ break;
|
|
+ }
|
|
+ } else {
|
|
+ inst = (nv_rd32(dev, 0x40032c) & 0xfffff) << 12;
|
|
+
|
|
+ for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
|
|
+ struct nouveau_channel *chan = dev_priv->fifos[i];
|
|
+
|
|
+ if (!chan || !chan->ramin)
|
|
+ continue;
|
|
+
|
|
+ if (inst == chan->ramin->instance)
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
+ return i;
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_graph_trapped_channel(struct drm_device *dev, int *channel_ret)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_engine *engine = &dev_priv->engine;
|
|
+ int channel;
|
|
+
|
|
+ if (dev_priv->card_type < NV_10)
|
|
+ channel = (nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR) >> 24) & 0xf;
|
|
+ else
|
|
+ if (dev_priv->card_type < NV_40)
|
|
+ channel = (nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR) >> 20) & 0x1f;
|
|
+ else
|
|
+ channel = nouveau_graph_chid_from_grctx(dev);
|
|
+
|
|
+ if (channel >= engine->fifo.channels || !dev_priv->fifos[channel]) {
|
|
+ NV_ERROR(dev, "AIII, invalid/inactive channel id %d\n", channel);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ *channel_ret = channel;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+struct nouveau_pgraph_trap {
|
|
+ int channel;
|
|
+ int class;
|
|
+ int subc, mthd, size;
|
|
+ uint32_t data, data2;
|
|
+ uint32_t nsource, nstatus;
|
|
+};
|
|
+
|
|
+static void
|
|
+nouveau_graph_trap_info(struct drm_device *dev,
|
|
+ struct nouveau_pgraph_trap *trap)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t address;
|
|
+
|
|
+ trap->nsource = trap->nstatus = 0;
|
|
+ if (dev_priv->card_type < NV_50) {
|
|
+ trap->nsource = nv_rd32(dev, NV03_PGRAPH_NSOURCE);
|
|
+ trap->nstatus = nv_rd32(dev, NV03_PGRAPH_NSTATUS);
|
|
+ }
|
|
+
|
|
+ if (nouveau_graph_trapped_channel(dev, &trap->channel))
|
|
+ trap->channel = -1;
|
|
+ address = nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR);
|
|
+
|
|
+ trap->mthd = address & 0x1FFC;
|
|
+ trap->data = nv_rd32(dev, NV04_PGRAPH_TRAPPED_DATA);
|
|
+ if (dev_priv->card_type < NV_10) {
|
|
+ trap->subc = (address >> 13) & 0x7;
|
|
+ } else {
|
|
+ trap->subc = (address >> 16) & 0x7;
|
|
+ trap->data2 = nv_rd32(dev, NV10_PGRAPH_TRAPPED_DATA_HIGH);
|
|
+ }
|
|
+
|
|
+ if (dev_priv->card_type < NV_10)
|
|
+ trap->class = nv_rd32(dev, 0x400180 + trap->subc*4) & 0xFF;
|
|
+ else if (dev_priv->card_type < NV_40)
|
|
+ trap->class = nv_rd32(dev, 0x400160 + trap->subc*4) & 0xFFF;
|
|
+ else if (dev_priv->card_type < NV_50)
|
|
+ trap->class = nv_rd32(dev, 0x400160 + trap->subc*4) & 0xFFFF;
|
|
+ else
|
|
+ trap->class = nv_rd32(dev, 0x400814);
|
|
+}
|
|
+
|
|
+static void
|
|
+nouveau_graph_dump_trap_info(struct drm_device *dev, const char *id,
|
|
+ struct nouveau_pgraph_trap *trap)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t nsource = trap->nsource, nstatus = trap->nstatus;
|
|
+
|
|
+ NV_INFO(dev, "%s - nSource:", id);
|
|
+ nouveau_print_bitfield_names(nsource, nsource_names);
|
|
+ printk(", nStatus:");
|
|
+ if (dev_priv->card_type < NV_10)
|
|
+ nouveau_print_bitfield_names(nstatus, nstatus_names);
|
|
+ else
|
|
+ nouveau_print_bitfield_names(nstatus, nstatus_names_nv10);
|
|
+ printk("\n");
|
|
+
|
|
+ NV_INFO(dev, "%s - Ch %d/%d Class 0x%04x Mthd 0x%04x "
|
|
+ "Data 0x%08x:0x%08x\n",
|
|
+ id, trap->channel, trap->subc,
|
|
+ trap->class, trap->mthd,
|
|
+ trap->data2, trap->data);
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_pgraph_intr_swmthd(struct drm_device *dev,
|
|
+ struct nouveau_pgraph_trap *trap)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ if (trap->channel < 0 ||
|
|
+ trap->channel >= dev_priv->engine.fifo.channels ||
|
|
+ !dev_priv->fifos[trap->channel])
|
|
+ return -ENODEV;
|
|
+
|
|
+ return nouveau_call_method(dev_priv->fifos[trap->channel],
|
|
+ trap->class, trap->mthd, trap->data);
|
|
+}
|
|
+
|
|
+static inline void
|
|
+nouveau_pgraph_intr_notify(struct drm_device *dev, uint32_t nsource)
|
|
+{
|
|
+ struct nouveau_pgraph_trap trap;
|
|
+ int unhandled = 0;
|
|
+
|
|
+ nouveau_graph_trap_info(dev, &trap);
|
|
+
|
|
+ if (nsource & NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD) {
|
|
+ if (nouveau_pgraph_intr_swmthd(dev, &trap))
|
|
+ unhandled = 1;
|
|
+ } else {
|
|
+ unhandled = 1;
|
|
+ }
|
|
+
|
|
+ if (unhandled)
|
|
+ nouveau_graph_dump_trap_info(dev, "PGRAPH_NOTIFY", &trap);
|
|
+}
|
|
+
|
|
+static DEFINE_RATELIMIT_STATE(nouveau_ratelimit_state, 3 * HZ, 20);
|
|
+
|
|
+static int nouveau_ratelimit(void)
|
|
+{
|
|
+ return __ratelimit(&nouveau_ratelimit_state);
|
|
+}
|
|
+
|
|
+
|
|
+static inline void
|
|
+nouveau_pgraph_intr_error(struct drm_device *dev, uint32_t nsource)
|
|
+{
|
|
+ struct nouveau_pgraph_trap trap;
|
|
+ int unhandled = 0;
|
|
+
|
|
+ nouveau_graph_trap_info(dev, &trap);
|
|
+ trap.nsource = nsource;
|
|
+
|
|
+ if (nsource & NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD) {
|
|
+ if (nouveau_pgraph_intr_swmthd(dev, &trap))
|
|
+ unhandled = 1;
|
|
+ } else if (nsource & NV03_PGRAPH_NSOURCE_DMA_VTX_PROTECTION) {
|
|
+ uint32_t v = nv_rd32(dev, 0x402000);
|
|
+ nv_wr32(dev, 0x402000, v);
|
|
+
|
|
+ /* dump the error anyway for now: it's useful for
|
|
+ Gallium development */
|
|
+ unhandled = 1;
|
|
+ } else {
|
|
+ unhandled = 1;
|
|
+ }
|
|
+
|
|
+ if (unhandled && nouveau_ratelimit())
|
|
+ nouveau_graph_dump_trap_info(dev, "PGRAPH_ERROR", &trap);
|
|
+}
|
|
+
|
|
+static inline void
|
|
+nouveau_pgraph_intr_context_switch(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_engine *engine = &dev_priv->engine;
|
|
+ uint32_t chid;
|
|
+
|
|
+ chid = engine->fifo.channel_id(dev);
|
|
+ NV_DEBUG(dev, "PGRAPH context switch interrupt channel %x\n", chid);
|
|
+
|
|
+ switch (dev_priv->card_type) {
|
|
+ case NV_04:
|
|
+ nv04_graph_context_switch(dev);
|
|
+ break;
|
|
+ case NV_10:
|
|
+ nv10_graph_context_switch(dev);
|
|
+ break;
|
|
+ default:
|
|
+ NV_ERROR(dev, "Context switch not implemented\n");
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+nouveau_pgraph_irq_handler(struct drm_device *dev)
|
|
+{
|
|
+ uint32_t status;
|
|
+
|
|
+ while ((status = nv_rd32(dev, NV03_PGRAPH_INTR))) {
|
|
+ uint32_t nsource = nv_rd32(dev, NV03_PGRAPH_NSOURCE);
|
|
+
|
|
+ if (status & NV_PGRAPH_INTR_NOTIFY) {
|
|
+ nouveau_pgraph_intr_notify(dev, nsource);
|
|
+
|
|
+ status &= ~NV_PGRAPH_INTR_NOTIFY;
|
|
+ nv_wr32(dev, NV03_PGRAPH_INTR, NV_PGRAPH_INTR_NOTIFY);
|
|
+ }
|
|
+
|
|
+ if (status & NV_PGRAPH_INTR_ERROR) {
|
|
+ nouveau_pgraph_intr_error(dev, nsource);
|
|
+
|
|
+ status &= ~NV_PGRAPH_INTR_ERROR;
|
|
+ nv_wr32(dev, NV03_PGRAPH_INTR, NV_PGRAPH_INTR_ERROR);
|
|
+ }
|
|
+
|
|
+ if (status & NV_PGRAPH_INTR_CONTEXT_SWITCH) {
|
|
+ nouveau_pgraph_intr_context_switch(dev);
|
|
+
|
|
+ status &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH;
|
|
+ nv_wr32(dev, NV03_PGRAPH_INTR,
|
|
+ NV_PGRAPH_INTR_CONTEXT_SWITCH);
|
|
+ }
|
|
+
|
|
+ if (status) {
|
|
+ NV_INFO(dev, "Unhandled PGRAPH_INTR - 0x%08x\n", status);
|
|
+ nv_wr32(dev, NV03_PGRAPH_INTR, status);
|
|
+ }
|
|
+
|
|
+ if ((nv_rd32(dev, NV04_PGRAPH_FIFO) & (1 << 0)) == 0)
|
|
+ nv_wr32(dev, NV04_PGRAPH_FIFO, 1);
|
|
+ }
|
|
+
|
|
+ nv_wr32(dev, NV03_PMC_INTR_0, NV_PMC_INTR_0_PGRAPH_PENDING);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_pgraph_irq_handler(struct drm_device *dev)
|
|
+{
|
|
+ uint32_t status;
|
|
+
|
|
+ while ((status = nv_rd32(dev, NV03_PGRAPH_INTR))) {
|
|
+ uint32_t nsource = nv_rd32(dev, NV03_PGRAPH_NSOURCE);
|
|
+
|
|
+ if (status & 0x00000001) {
|
|
+ nouveau_pgraph_intr_notify(dev, nsource);
|
|
+ status &= ~0x00000001;
|
|
+ nv_wr32(dev, NV03_PGRAPH_INTR, 0x00000001);
|
|
+ }
|
|
+
|
|
+ if (status & 0x00000010) {
|
|
+ nouveau_pgraph_intr_error(dev, nsource |
|
|
+ NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD);
|
|
+
|
|
+ status &= ~0x00000010;
|
|
+ nv_wr32(dev, NV03_PGRAPH_INTR, 0x00000010);
|
|
+ }
|
|
+
|
|
+ if (status & 0x00001000) {
|
|
+ nv_wr32(dev, 0x400500, 0x00000000);
|
|
+ nv_wr32(dev, NV03_PGRAPH_INTR,
|
|
+ NV_PGRAPH_INTR_CONTEXT_SWITCH);
|
|
+ nv_wr32(dev, NV40_PGRAPH_INTR_EN, nv_rd32(dev,
|
|
+ NV40_PGRAPH_INTR_EN) &
|
|
+ ~NV_PGRAPH_INTR_CONTEXT_SWITCH);
|
|
+ nv_wr32(dev, 0x400500, 0x00010001);
|
|
+
|
|
+ nv50_graph_context_switch(dev);
|
|
+
|
|
+ status &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH;
|
|
+ }
|
|
+
|
|
+ if (status & 0x00100000) {
|
|
+ nouveau_pgraph_intr_error(dev, nsource |
|
|
+ NV03_PGRAPH_NSOURCE_DATA_ERROR);
|
|
+
|
|
+ status &= ~0x00100000;
|
|
+ nv_wr32(dev, NV03_PGRAPH_INTR, 0x00100000);
|
|
+ }
|
|
+
|
|
+ if (status & 0x00200000) {
|
|
+ int r;
|
|
+
|
|
+ nouveau_pgraph_intr_error(dev, nsource |
|
|
+ NV03_PGRAPH_NSOURCE_PROTECTION_ERROR);
|
|
+
|
|
+ NV_ERROR(dev, "magic set 1:\n");
|
|
+ for (r = 0x408900; r <= 0x408910; r += 4)
|
|
+ NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r,
|
|
+ nv_rd32(dev, r));
|
|
+ nv_wr32(dev, 0x408900,
|
|
+ nv_rd32(dev, 0x408904) | 0xc0000000);
|
|
+ for (r = 0x408e08; r <= 0x408e24; r += 4)
|
|
+ NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r,
|
|
+ nv_rd32(dev, r));
|
|
+ nv_wr32(dev, 0x408e08,
|
|
+ nv_rd32(dev, 0x408e08) | 0xc0000000);
|
|
+
|
|
+ NV_ERROR(dev, "magic set 2:\n");
|
|
+ for (r = 0x409900; r <= 0x409910; r += 4)
|
|
+ NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r,
|
|
+ nv_rd32(dev, r));
|
|
+ nv_wr32(dev, 0x409900,
|
|
+ nv_rd32(dev, 0x409904) | 0xc0000000);
|
|
+ for (r = 0x409e08; r <= 0x409e24; r += 4)
|
|
+ NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r,
|
|
+ nv_rd32(dev, r));
|
|
+ nv_wr32(dev, 0x409e08,
|
|
+ nv_rd32(dev, 0x409e08) | 0xc0000000);
|
|
+
|
|
+ status &= ~0x00200000;
|
|
+ nv_wr32(dev, NV03_PGRAPH_NSOURCE, nsource);
|
|
+ nv_wr32(dev, NV03_PGRAPH_INTR, 0x00200000);
|
|
+ }
|
|
+
|
|
+ if (status) {
|
|
+ NV_INFO(dev, "Unhandled PGRAPH_INTR - 0x%08x\n",
|
|
+ status);
|
|
+ nv_wr32(dev, NV03_PGRAPH_INTR, status);
|
|
+ }
|
|
+
|
|
+ {
|
|
+ const int isb = (1 << 16) | (1 << 0);
|
|
+
|
|
+ if ((nv_rd32(dev, 0x400500) & isb) != isb)
|
|
+ nv_wr32(dev, 0x400500,
|
|
+ nv_rd32(dev, 0x400500) | isb);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ nv_wr32(dev, NV03_PMC_INTR_0, NV_PMC_INTR_0_PGRAPH_PENDING);
|
|
+ nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) & ~(1 << 31));
|
|
+}
|
|
+
|
|
+static void
|
|
+nouveau_crtc_irq_handler(struct drm_device *dev, int crtc)
|
|
+{
|
|
+ if (crtc & 1)
|
|
+ nv_wr32(dev, NV_CRTC0_INTSTAT, NV_CRTC_INTR_VBLANK);
|
|
+
|
|
+ if (crtc & 2)
|
|
+ nv_wr32(dev, NV_CRTC1_INTSTAT, NV_CRTC_INTR_VBLANK);
|
|
+}
|
|
+
|
|
+irqreturn_t
|
|
+nouveau_irq_handler(DRM_IRQ_ARGS)
|
|
+{
|
|
+ struct drm_device *dev = (struct drm_device *)arg;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t status, fbdev_flags = 0;
|
|
+
|
|
+ status = nv_rd32(dev, NV03_PMC_INTR_0);
|
|
+ if (!status)
|
|
+ return IRQ_NONE;
|
|
+
|
|
+ if (dev_priv->fbdev_info) {
|
|
+ fbdev_flags = dev_priv->fbdev_info->flags;
|
|
+ dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED;
|
|
+ }
|
|
+
|
|
+ if (status & NV_PMC_INTR_0_PFIFO_PENDING) {
|
|
+ nouveau_fifo_irq_handler(dev);
|
|
+ status &= ~NV_PMC_INTR_0_PFIFO_PENDING;
|
|
+ }
|
|
+
|
|
+ if (status & NV_PMC_INTR_0_PGRAPH_PENDING) {
|
|
+ if (dev_priv->card_type >= NV_50)
|
|
+ nv50_pgraph_irq_handler(dev);
|
|
+ else
|
|
+ nouveau_pgraph_irq_handler(dev);
|
|
+
|
|
+ status &= ~NV_PMC_INTR_0_PGRAPH_PENDING;
|
|
+ }
|
|
+
|
|
+ if (status & NV_PMC_INTR_0_CRTCn_PENDING) {
|
|
+ nouveau_crtc_irq_handler(dev, (status>>24)&3);
|
|
+ status &= ~NV_PMC_INTR_0_CRTCn_PENDING;
|
|
+ }
|
|
+
|
|
+ if (status & (NV_PMC_INTR_0_NV50_DISPLAY_PENDING |
|
|
+ NV_PMC_INTR_0_NV50_I2C_PENDING)) {
|
|
+ nv50_display_irq_handler(dev);
|
|
+ status &= ~(NV_PMC_INTR_0_NV50_DISPLAY_PENDING |
|
|
+ NV_PMC_INTR_0_NV50_I2C_PENDING);
|
|
+ }
|
|
+
|
|
+ if (status)
|
|
+ NV_ERROR(dev, "Unhandled PMC INTR status bits 0x%08x\n", status);
|
|
+
|
|
+ if (dev_priv->fbdev_info)
|
|
+ dev_priv->fbdev_info->flags = fbdev_flags;
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c
|
|
new file mode 100644
|
|
index 0000000..8f3a12f
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_mem.c
|
|
@@ -0,0 +1,668 @@
|
|
+/*
|
|
+ * Copyright (C) The Weather Channel, Inc. 2002. All Rights Reserved.
|
|
+ * Copyright 2005 Stephane Marchesin
|
|
+ *
|
|
+ * The Weather Channel (TM) funded Tungsten Graphics to develop the
|
|
+ * initial release of the Radeon 8500 driver under the XFree86 license.
|
|
+ * This notice must be preserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the next
|
|
+ * paragraph) shall be included in all copies or substantial portions of the
|
|
+ * Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE AUTHORS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
+ * DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ * Authors:
|
|
+ * Keith Whitwell <keith@tungstengraphics.com>
|
|
+ */
|
|
+
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "drm_sarea.h"
|
|
+#include "nouveau_drv.h"
|
|
+
|
|
+static struct mem_block *
|
|
+split_block(struct mem_block *p, uint64_t start, uint64_t size,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ /* Maybe cut off the start of an existing block */
|
|
+ if (start > p->start) {
|
|
+ struct mem_block *newblock =
|
|
+ kmalloc(sizeof(*newblock), GFP_KERNEL);
|
|
+ if (!newblock)
|
|
+ goto out;
|
|
+ newblock->start = start;
|
|
+ newblock->size = p->size - (start - p->start);
|
|
+ newblock->file_priv = NULL;
|
|
+ newblock->next = p->next;
|
|
+ newblock->prev = p;
|
|
+ p->next->prev = newblock;
|
|
+ p->next = newblock;
|
|
+ p->size -= newblock->size;
|
|
+ p = newblock;
|
|
+ }
|
|
+
|
|
+ /* Maybe cut off the end of an existing block */
|
|
+ if (size < p->size) {
|
|
+ struct mem_block *newblock =
|
|
+ kmalloc(sizeof(*newblock), GFP_KERNEL);
|
|
+ if (!newblock)
|
|
+ goto out;
|
|
+ newblock->start = start + size;
|
|
+ newblock->size = p->size - size;
|
|
+ newblock->file_priv = NULL;
|
|
+ newblock->next = p->next;
|
|
+ newblock->prev = p;
|
|
+ p->next->prev = newblock;
|
|
+ p->next = newblock;
|
|
+ p->size = size;
|
|
+ }
|
|
+
|
|
+out:
|
|
+ /* Our block is in the middle */
|
|
+ p->file_priv = file_priv;
|
|
+ return p;
|
|
+}
|
|
+
|
|
+struct mem_block *
|
|
+nouveau_mem_alloc_block(struct mem_block *heap, uint64_t size,
|
|
+ int align2, struct drm_file *file_priv, int tail)
|
|
+{
|
|
+ struct mem_block *p;
|
|
+ uint64_t mask = (1 << align2) - 1;
|
|
+
|
|
+ if (!heap)
|
|
+ return NULL;
|
|
+
|
|
+ if (tail) {
|
|
+ list_for_each_prev(p, heap) {
|
|
+ uint64_t start = ((p->start + p->size) - size) & ~mask;
|
|
+
|
|
+ if (p->file_priv == NULL && start >= p->start &&
|
|
+ start + size <= p->start + p->size)
|
|
+ return split_block(p, start, size, file_priv);
|
|
+ }
|
|
+ } else {
|
|
+ list_for_each(p, heap) {
|
|
+ uint64_t start = (p->start + mask) & ~mask;
|
|
+
|
|
+ if (p->file_priv == NULL &&
|
|
+ start + size <= p->start + p->size)
|
|
+ return split_block(p, start, size, file_priv);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+void nouveau_mem_free_block(struct mem_block *p)
|
|
+{
|
|
+ p->file_priv = NULL;
|
|
+
|
|
+ /* Assumes a single contiguous range. Needs a special file_priv in
|
|
+ * 'heap' to stop it being subsumed.
|
|
+ */
|
|
+ if (p->next->file_priv == NULL) {
|
|
+ struct mem_block *q = p->next;
|
|
+ p->size += q->size;
|
|
+ p->next = q->next;
|
|
+ p->next->prev = p;
|
|
+ kfree(q);
|
|
+ }
|
|
+
|
|
+ if (p->prev->file_priv == NULL) {
|
|
+ struct mem_block *q = p->prev;
|
|
+ q->size += p->size;
|
|
+ q->next = p->next;
|
|
+ q->next->prev = q;
|
|
+ kfree(p);
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Initialize. How to check for an uninitialized heap?
|
|
+ */
|
|
+int nouveau_mem_init_heap(struct mem_block **heap, uint64_t start,
|
|
+ uint64_t size)
|
|
+{
|
|
+ struct mem_block *blocks = kmalloc(sizeof(*blocks), GFP_KERNEL);
|
|
+
|
|
+ if (!blocks)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ *heap = kmalloc(sizeof(**heap), GFP_KERNEL);
|
|
+ if (!*heap) {
|
|
+ kfree(blocks);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ blocks->start = start;
|
|
+ blocks->size = size;
|
|
+ blocks->file_priv = NULL;
|
|
+ blocks->next = blocks->prev = *heap;
|
|
+
|
|
+ memset(*heap, 0, sizeof(**heap));
|
|
+ (*heap)->file_priv = (struct drm_file *) -1;
|
|
+ (*heap)->next = (*heap)->prev = blocks;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Free all blocks associated with the releasing file_priv
|
|
+ */
|
|
+void nouveau_mem_release(struct drm_file *file_priv, struct mem_block *heap)
|
|
+{
|
|
+ struct mem_block *p;
|
|
+
|
|
+ if (!heap || !heap->next)
|
|
+ return;
|
|
+
|
|
+ list_for_each(p, heap) {
|
|
+ if (p->file_priv == file_priv)
|
|
+ p->file_priv = NULL;
|
|
+ }
|
|
+
|
|
+ /* Assumes a single contiguous range. Needs a special file_priv in
|
|
+ * 'heap' to stop it being subsumed.
|
|
+ */
|
|
+ list_for_each(p, heap) {
|
|
+ while ((p->file_priv == NULL) &&
|
|
+ (p->next->file_priv == NULL) &&
|
|
+ (p->next != heap)) {
|
|
+ struct mem_block *q = p->next;
|
|
+ p->size += q->size;
|
|
+ p->next = q->next;
|
|
+ p->next->prev = p;
|
|
+ kfree(q);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * NV10-NV40 tiling helpers
|
|
+ */
|
|
+
|
|
+static void
|
|
+nv10_mem_set_region_tiling(struct drm_device *dev, int i, uint32_t addr,
|
|
+ uint32_t size, uint32_t pitch)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
|
|
+ struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
|
|
+ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
|
|
+ struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i];
|
|
+
|
|
+ tile->addr = addr;
|
|
+ tile->size = size;
|
|
+ tile->used = !!pitch;
|
|
+ nouveau_fence_unref((void **)&tile->fence);
|
|
+
|
|
+ if (!pfifo->cache_flush(dev))
|
|
+ return;
|
|
+
|
|
+ pfifo->reassign(dev, false);
|
|
+ pfifo->cache_flush(dev);
|
|
+ pfifo->cache_pull(dev, false);
|
|
+
|
|
+ nouveau_wait_for_idle(dev);
|
|
+
|
|
+ pgraph->set_region_tiling(dev, i, addr, size, pitch);
|
|
+ pfb->set_region_tiling(dev, i, addr, size, pitch);
|
|
+
|
|
+ pfifo->cache_pull(dev, true);
|
|
+ pfifo->reassign(dev, true);
|
|
+}
|
|
+
|
|
+struct nouveau_tile_reg *
|
|
+nv10_mem_set_tiling(struct drm_device *dev, uint32_t addr, uint32_t size,
|
|
+ uint32_t pitch)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
|
|
+ struct nouveau_tile_reg *tile = dev_priv->tile.reg, *found = NULL;
|
|
+ int i;
|
|
+
|
|
+ spin_lock(&dev_priv->tile.lock);
|
|
+
|
|
+ for (i = 0; i < pfb->num_tiles; i++) {
|
|
+ if (tile[i].used)
|
|
+ /* Tile region in use. */
|
|
+ continue;
|
|
+
|
|
+ if (tile[i].fence &&
|
|
+ !nouveau_fence_signalled(tile[i].fence, NULL))
|
|
+ /* Pending tile region. */
|
|
+ continue;
|
|
+
|
|
+ if (max(tile[i].addr, addr) <
|
|
+ min(tile[i].addr + tile[i].size, addr + size))
|
|
+ /* Kill an intersecting tile region. */
|
|
+ nv10_mem_set_region_tiling(dev, i, 0, 0, 0);
|
|
+
|
|
+ if (pitch && !found) {
|
|
+ /* Free tile region. */
|
|
+ nv10_mem_set_region_tiling(dev, i, addr, size, pitch);
|
|
+ found = &tile[i];
|
|
+ }
|
|
+ }
|
|
+
|
|
+ spin_unlock(&dev_priv->tile.lock);
|
|
+
|
|
+ return found;
|
|
+}
|
|
+
|
|
+void
|
|
+nv10_mem_expire_tiling(struct drm_device *dev, struct nouveau_tile_reg *tile,
|
|
+ struct nouveau_fence *fence)
|
|
+{
|
|
+ if (fence) {
|
|
+ /* Mark it as pending. */
|
|
+ tile->fence = fence;
|
|
+ nouveau_fence_ref(fence);
|
|
+ }
|
|
+
|
|
+ tile->used = false;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * NV50 VM helpers
|
|
+ */
|
|
+int
|
|
+nv50_mem_vm_bind_linear(struct drm_device *dev, uint64_t virt, uint32_t size,
|
|
+ uint32_t flags, uint64_t phys)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_gpuobj **pgt;
|
|
+ unsigned psz, pfl, pages;
|
|
+
|
|
+ if (virt >= dev_priv->vm_gart_base &&
|
|
+ (virt + size) < (dev_priv->vm_gart_base + dev_priv->vm_gart_size)) {
|
|
+ psz = 12;
|
|
+ pgt = &dev_priv->gart_info.sg_ctxdma;
|
|
+ pfl = 0x21;
|
|
+ virt -= dev_priv->vm_gart_base;
|
|
+ } else
|
|
+ if (virt >= dev_priv->vm_vram_base &&
|
|
+ (virt + size) < (dev_priv->vm_vram_base + dev_priv->vm_vram_size)) {
|
|
+ psz = 16;
|
|
+ pgt = dev_priv->vm_vram_pt;
|
|
+ pfl = 0x01;
|
|
+ virt -= dev_priv->vm_vram_base;
|
|
+ } else {
|
|
+ NV_ERROR(dev, "Invalid address: 0x%16llx-0x%16llx\n",
|
|
+ virt, virt + size - 1);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ pages = size >> psz;
|
|
+
|
|
+ dev_priv->engine.instmem.prepare_access(dev, true);
|
|
+ if (flags & 0x80000000) {
|
|
+ while (pages--) {
|
|
+ struct nouveau_gpuobj *pt = pgt[virt >> 29];
|
|
+ unsigned pte = ((virt & 0x1fffffffULL) >> psz) << 1;
|
|
+
|
|
+ nv_wo32(dev, pt, pte++, 0x00000000);
|
|
+ nv_wo32(dev, pt, pte++, 0x00000000);
|
|
+
|
|
+ virt += (1 << psz);
|
|
+ }
|
|
+ } else {
|
|
+ while (pages--) {
|
|
+ struct nouveau_gpuobj *pt = pgt[virt >> 29];
|
|
+ unsigned pte = ((virt & 0x1fffffffULL) >> psz) << 1;
|
|
+ unsigned offset_h = upper_32_bits(phys) & 0xff;
|
|
+ unsigned offset_l = lower_32_bits(phys);
|
|
+
|
|
+ nv_wo32(dev, pt, pte++, offset_l | pfl);
|
|
+ nv_wo32(dev, pt, pte++, offset_h | flags);
|
|
+
|
|
+ phys += (1 << psz);
|
|
+ virt += (1 << psz);
|
|
+ }
|
|
+ }
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+
|
|
+ nv_wr32(dev, 0x100c80, 0x00050001);
|
|
+ if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
|
|
+ NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
|
|
+ NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ nv_wr32(dev, 0x100c80, 0x00000001);
|
|
+ if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
|
|
+ NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
|
|
+ NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv50_mem_vm_unbind(struct drm_device *dev, uint64_t virt, uint32_t size)
|
|
+{
|
|
+ nv50_mem_vm_bind_linear(dev, virt, size, 0x80000000, 0);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Cleanup everything
|
|
+ */
|
|
+void nouveau_mem_takedown(struct mem_block **heap)
|
|
+{
|
|
+ struct mem_block *p;
|
|
+
|
|
+ if (!*heap)
|
|
+ return;
|
|
+
|
|
+ for (p = (*heap)->next; p != *heap;) {
|
|
+ struct mem_block *q = p;
|
|
+ p = p->next;
|
|
+ kfree(q);
|
|
+ }
|
|
+
|
|
+ kfree(*heap);
|
|
+ *heap = NULL;
|
|
+}
|
|
+
|
|
+void nouveau_mem_close(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ nouveau_bo_unpin(dev_priv->vga_ram);
|
|
+ nouveau_bo_ref(NULL, &dev_priv->vga_ram);
|
|
+
|
|
+ ttm_bo_device_release(&dev_priv->ttm.bdev);
|
|
+
|
|
+ nouveau_ttm_global_release(dev_priv);
|
|
+
|
|
+ if (drm_core_has_AGP(dev) && dev->agp &&
|
|
+ drm_core_check_feature(dev, DRIVER_MODESET)) {
|
|
+ struct drm_agp_mem *entry, *tempe;
|
|
+
|
|
+ /* Remove AGP resources, but leave dev->agp
|
|
+ intact until drv_cleanup is called. */
|
|
+ list_for_each_entry_safe(entry, tempe, &dev->agp->memory, head) {
|
|
+ if (entry->bound)
|
|
+ drm_unbind_agp(entry->memory);
|
|
+ drm_free_agp(entry->memory, entry->pages);
|
|
+ kfree(entry);
|
|
+ }
|
|
+ INIT_LIST_HEAD(&dev->agp->memory);
|
|
+
|
|
+ if (dev->agp->acquired)
|
|
+ drm_agp_release(dev);
|
|
+
|
|
+ dev->agp->acquired = 0;
|
|
+ dev->agp->enabled = 0;
|
|
+ }
|
|
+
|
|
+ if (dev_priv->fb_mtrr) {
|
|
+ drm_mtrr_del(dev_priv->fb_mtrr, drm_get_resource_start(dev, 1),
|
|
+ drm_get_resource_len(dev, 1), DRM_MTRR_WC);
|
|
+ dev_priv->fb_mtrr = 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*XXX won't work on BSD because of pci_read_config_dword */
|
|
+static uint32_t
|
|
+nouveau_mem_fb_amount_igp(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct pci_dev *bridge;
|
|
+ uint32_t mem;
|
|
+
|
|
+ bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 1));
|
|
+ if (!bridge) {
|
|
+ NV_ERROR(dev, "no bridge device\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (dev_priv->flags&NV_NFORCE) {
|
|
+ pci_read_config_dword(bridge, 0x7C, &mem);
|
|
+ return (uint64_t)(((mem >> 6) & 31) + 1)*1024*1024;
|
|
+ } else
|
|
+ if (dev_priv->flags&NV_NFORCE2) {
|
|
+ pci_read_config_dword(bridge, 0x84, &mem);
|
|
+ return (uint64_t)(((mem >> 4) & 127) + 1)*1024*1024;
|
|
+ }
|
|
+
|
|
+ NV_ERROR(dev, "impossible!\n");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* returns the amount of FB ram in bytes */
|
|
+uint64_t nouveau_mem_fb_amount(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t boot0;
|
|
+
|
|
+ switch (dev_priv->card_type) {
|
|
+ case NV_04:
|
|
+ boot0 = nv_rd32(dev, NV03_BOOT_0);
|
|
+ if (boot0 & 0x00000100)
|
|
+ return (((boot0 >> 12) & 0xf) * 2 + 2) * 1024 * 1024;
|
|
+
|
|
+ switch (boot0 & NV03_BOOT_0_RAM_AMOUNT) {
|
|
+ case NV04_BOOT_0_RAM_AMOUNT_32MB:
|
|
+ return 32 * 1024 * 1024;
|
|
+ case NV04_BOOT_0_RAM_AMOUNT_16MB:
|
|
+ return 16 * 1024 * 1024;
|
|
+ case NV04_BOOT_0_RAM_AMOUNT_8MB:
|
|
+ return 8 * 1024 * 1024;
|
|
+ case NV04_BOOT_0_RAM_AMOUNT_4MB:
|
|
+ return 4 * 1024 * 1024;
|
|
+ }
|
|
+ break;
|
|
+ case NV_10:
|
|
+ case NV_20:
|
|
+ case NV_30:
|
|
+ case NV_40:
|
|
+ case NV_50:
|
|
+ default:
|
|
+ if (dev_priv->flags & (NV_NFORCE | NV_NFORCE2)) {
|
|
+ return nouveau_mem_fb_amount_igp(dev);
|
|
+ } else {
|
|
+ uint64_t mem;
|
|
+ mem = (nv_rd32(dev, NV04_FIFO_DATA) &
|
|
+ NV10_FIFO_DATA_RAM_AMOUNT_MB_MASK) >>
|
|
+ NV10_FIFO_DATA_RAM_AMOUNT_MB_SHIFT;
|
|
+ return mem * 1024 * 1024;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ NV_ERROR(dev,
|
|
+ "Unable to detect video ram size. Please report your setup to "
|
|
+ DRIVER_EMAIL "\n");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#if __OS_HAS_AGP
|
|
+static void nouveau_mem_reset_agp(struct drm_device *dev)
|
|
+{
|
|
+ uint32_t saved_pci_nv_1, saved_pci_nv_19, pmc_enable;
|
|
+
|
|
+ saved_pci_nv_1 = nv_rd32(dev, NV04_PBUS_PCI_NV_1);
|
|
+ saved_pci_nv_19 = nv_rd32(dev, NV04_PBUS_PCI_NV_19);
|
|
+
|
|
+ /* clear busmaster bit */
|
|
+ nv_wr32(dev, NV04_PBUS_PCI_NV_1, saved_pci_nv_1 & ~0x4);
|
|
+ /* clear SBA and AGP bits */
|
|
+ nv_wr32(dev, NV04_PBUS_PCI_NV_19, saved_pci_nv_19 & 0xfffff0ff);
|
|
+
|
|
+ /* power cycle pgraph, if enabled */
|
|
+ pmc_enable = nv_rd32(dev, NV03_PMC_ENABLE);
|
|
+ if (pmc_enable & NV_PMC_ENABLE_PGRAPH) {
|
|
+ nv_wr32(dev, NV03_PMC_ENABLE,
|
|
+ pmc_enable & ~NV_PMC_ENABLE_PGRAPH);
|
|
+ nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) |
|
|
+ NV_PMC_ENABLE_PGRAPH);
|
|
+ }
|
|
+
|
|
+ /* and restore (gives effect of resetting AGP) */
|
|
+ nv_wr32(dev, NV04_PBUS_PCI_NV_19, saved_pci_nv_19);
|
|
+ nv_wr32(dev, NV04_PBUS_PCI_NV_1, saved_pci_nv_1);
|
|
+}
|
|
+#endif
|
|
+
|
|
+int
|
|
+nouveau_mem_init_agp(struct drm_device *dev)
|
|
+{
|
|
+#if __OS_HAS_AGP
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct drm_agp_info info;
|
|
+ struct drm_agp_mode mode;
|
|
+ int ret;
|
|
+
|
|
+ if (nouveau_noagp)
|
|
+ return 0;
|
|
+
|
|
+ nouveau_mem_reset_agp(dev);
|
|
+
|
|
+ if (!dev->agp->acquired) {
|
|
+ ret = drm_agp_acquire(dev);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Unable to acquire AGP: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ret = drm_agp_info(dev, &info);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Unable to get AGP info: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /* see agp.h for the AGPSTAT_* modes available */
|
|
+ mode.mode = info.mode;
|
|
+ ret = drm_agp_enable(dev, mode);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Unable to enable AGP: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ dev_priv->gart_info.type = NOUVEAU_GART_AGP;
|
|
+ dev_priv->gart_info.aper_base = info.aperture_base;
|
|
+ dev_priv->gart_info.aper_size = info.aperture_size;
|
|
+#endif
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_mem_init(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct ttm_bo_device *bdev = &dev_priv->ttm.bdev;
|
|
+ int ret, dma_bits = 32;
|
|
+
|
|
+ dev_priv->fb_phys = drm_get_resource_start(dev, 1);
|
|
+ dev_priv->gart_info.type = NOUVEAU_GART_NONE;
|
|
+
|
|
+ if (dev_priv->card_type >= NV_50 &&
|
|
+ pci_dma_supported(dev->pdev, DMA_BIT_MASK(40)))
|
|
+ dma_bits = 40;
|
|
+
|
|
+ ret = pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(dma_bits));
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Error setting DMA mask: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = nouveau_ttm_global_init(dev_priv);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = ttm_bo_device_init(&dev_priv->ttm.bdev,
|
|
+ dev_priv->ttm.bo_global_ref.ref.object,
|
|
+ &nouveau_bo_driver, DRM_FILE_PAGE_OFFSET,
|
|
+ dma_bits <= 32 ? true : false);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Error initialising bo driver: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ INIT_LIST_HEAD(&dev_priv->ttm.bo_list);
|
|
+ spin_lock_init(&dev_priv->ttm.bo_list_lock);
|
|
+ spin_lock_init(&dev_priv->tile.lock);
|
|
+
|
|
+ dev_priv->fb_available_size = nouveau_mem_fb_amount(dev);
|
|
+
|
|
+ dev_priv->fb_mappable_pages = dev_priv->fb_available_size;
|
|
+ if (dev_priv->fb_mappable_pages > drm_get_resource_len(dev, 1))
|
|
+ dev_priv->fb_mappable_pages = drm_get_resource_len(dev, 1);
|
|
+ dev_priv->fb_mappable_pages >>= PAGE_SHIFT;
|
|
+
|
|
+ NV_INFO(dev, "%d MiB VRAM\n", (int)(dev_priv->fb_available_size >> 20));
|
|
+
|
|
+ /* remove reserved space at end of vram from available amount */
|
|
+ dev_priv->fb_available_size -= dev_priv->ramin_rsvd_vram;
|
|
+ dev_priv->fb_aper_free = dev_priv->fb_available_size;
|
|
+
|
|
+ /* mappable vram */
|
|
+ ret = ttm_bo_init_mm(bdev, TTM_PL_VRAM,
|
|
+ dev_priv->fb_available_size >> PAGE_SHIFT);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Failed VRAM mm init: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = nouveau_bo_new(dev, NULL, 256*1024, 0, TTM_PL_FLAG_VRAM,
|
|
+ 0, 0, true, true, &dev_priv->vga_ram);
|
|
+ if (ret == 0)
|
|
+ ret = nouveau_bo_pin(dev_priv->vga_ram, TTM_PL_FLAG_VRAM);
|
|
+ if (ret) {
|
|
+ NV_WARN(dev, "failed to reserve VGA memory\n");
|
|
+ nouveau_bo_ref(NULL, &dev_priv->vga_ram);
|
|
+ }
|
|
+
|
|
+ /* GART */
|
|
+#if !defined(__powerpc__) && !defined(__ia64__)
|
|
+ if (drm_device_is_agp(dev) && dev->agp) {
|
|
+ ret = nouveau_mem_init_agp(dev);
|
|
+ if (ret)
|
|
+ NV_ERROR(dev, "Error initialising AGP: %d\n", ret);
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ if (dev_priv->gart_info.type == NOUVEAU_GART_NONE) {
|
|
+ ret = nouveau_sgdma_init(dev);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Error initialising PCI(E): %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ NV_INFO(dev, "%d MiB GART (aperture)\n",
|
|
+ (int)(dev_priv->gart_info.aper_size >> 20));
|
|
+ dev_priv->gart_info.aper_free = dev_priv->gart_info.aper_size;
|
|
+
|
|
+ ret = ttm_bo_init_mm(bdev, TTM_PL_TT,
|
|
+ dev_priv->gart_info.aper_size >> PAGE_SHIFT);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Failed TT mm init: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ dev_priv->fb_mtrr = drm_mtrr_add(drm_get_resource_start(dev, 1),
|
|
+ drm_get_resource_len(dev, 1),
|
|
+ DRM_MTRR_WC);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_notifier.c b/drivers/gpu/drm/nouveau/nouveau_notifier.c
|
|
new file mode 100644
|
|
index 0000000..d99dc08
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_notifier.c
|
|
@@ -0,0 +1,203 @@
|
|
+/*
|
|
+ * Copyright (C) 2007 Ben Skeggs.
|
|
+ *
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "nouveau_drv.h"
|
|
+
|
|
+int
|
|
+nouveau_notifier_init_channel(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct nouveau_bo *ntfy = NULL;
|
|
+ uint32_t flags;
|
|
+ int ret;
|
|
+
|
|
+ if (nouveau_vram_notify)
|
|
+ flags = TTM_PL_FLAG_VRAM;
|
|
+ else
|
|
+ flags = TTM_PL_FLAG_TT;
|
|
+
|
|
+ ret = nouveau_gem_new(dev, NULL, PAGE_SIZE, 0, flags,
|
|
+ 0, 0x0000, false, true, &ntfy);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = nouveau_bo_pin(ntfy, flags);
|
|
+ if (ret)
|
|
+ goto out_err;
|
|
+
|
|
+ ret = nouveau_bo_map(ntfy);
|
|
+ if (ret)
|
|
+ goto out_err;
|
|
+
|
|
+ ret = nouveau_mem_init_heap(&chan->notifier_heap, 0, ntfy->bo.mem.size);
|
|
+ if (ret)
|
|
+ goto out_err;
|
|
+
|
|
+ chan->notifier_bo = ntfy;
|
|
+out_err:
|
|
+ if (ret) {
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
+ drm_gem_object_unreference(ntfy->gem);
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_notifier_takedown_channel(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+
|
|
+ if (!chan->notifier_bo)
|
|
+ return;
|
|
+
|
|
+ nouveau_bo_unmap(chan->notifier_bo);
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
+ nouveau_bo_unpin(chan->notifier_bo);
|
|
+ drm_gem_object_unreference(chan->notifier_bo->gem);
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+ nouveau_mem_takedown(&chan->notifier_heap);
|
|
+}
|
|
+
|
|
+static void
|
|
+nouveau_notifier_gpuobj_dtor(struct drm_device *dev,
|
|
+ struct nouveau_gpuobj *gpuobj)
|
|
+{
|
|
+ NV_DEBUG(dev, "\n");
|
|
+
|
|
+ if (gpuobj->priv)
|
|
+ nouveau_mem_free_block(gpuobj->priv);
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_notifier_alloc(struct nouveau_channel *chan, uint32_t handle,
|
|
+ int size, uint32_t *b_offset)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_gpuobj *nobj = NULL;
|
|
+ struct mem_block *mem;
|
|
+ uint32_t offset;
|
|
+ int target, ret;
|
|
+
|
|
+ if (!chan->notifier_heap) {
|
|
+ NV_ERROR(dev, "Channel %d doesn't have a notifier heap!\n",
|
|
+ chan->id);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ mem = nouveau_mem_alloc_block(chan->notifier_heap, size, 0,
|
|
+ (struct drm_file *)-2, 0);
|
|
+ if (!mem) {
|
|
+ NV_ERROR(dev, "Channel %d notifier block full\n", chan->id);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ offset = chan->notifier_bo->bo.mem.mm_node->start << PAGE_SHIFT;
|
|
+ if (chan->notifier_bo->bo.mem.mem_type == TTM_PL_VRAM) {
|
|
+ target = NV_DMA_TARGET_VIDMEM;
|
|
+ } else
|
|
+ if (chan->notifier_bo->bo.mem.mem_type == TTM_PL_TT) {
|
|
+ if (dev_priv->gart_info.type == NOUVEAU_GART_SGDMA &&
|
|
+ dev_priv->card_type < NV_50) {
|
|
+ ret = nouveau_sgdma_get_page(dev, offset, &offset);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ target = NV_DMA_TARGET_PCI;
|
|
+ } else {
|
|
+ target = NV_DMA_TARGET_AGP;
|
|
+ if (dev_priv->card_type >= NV_50)
|
|
+ offset += dev_priv->vm_gart_base;
|
|
+ }
|
|
+ } else {
|
|
+ NV_ERROR(dev, "Bad DMA target, mem_type %d!\n",
|
|
+ chan->notifier_bo->bo.mem.mem_type);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ offset += mem->start;
|
|
+
|
|
+ ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, offset,
|
|
+ mem->size, NV_DMA_ACCESS_RW, target,
|
|
+ &nobj);
|
|
+ if (ret) {
|
|
+ nouveau_mem_free_block(mem);
|
|
+ NV_ERROR(dev, "Error creating notifier ctxdma: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+ nobj->dtor = nouveau_notifier_gpuobj_dtor;
|
|
+ nobj->priv = mem;
|
|
+
|
|
+ ret = nouveau_gpuobj_ref_add(dev, chan, handle, nobj, NULL);
|
|
+ if (ret) {
|
|
+ nouveau_gpuobj_del(dev, &nobj);
|
|
+ nouveau_mem_free_block(mem);
|
|
+ NV_ERROR(dev, "Error referencing notifier ctxdma: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ *b_offset = mem->start;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_notifier_offset(struct nouveau_gpuobj *nobj, uint32_t *poffset)
|
|
+{
|
|
+ if (!nobj || nobj->dtor != nouveau_notifier_gpuobj_dtor)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (poffset) {
|
|
+ struct mem_block *mem = nobj->priv;
|
|
+
|
|
+ if (*poffset >= mem->size)
|
|
+ return false;
|
|
+
|
|
+ *poffset += mem->start;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_ioctl_notifier_alloc(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_nouveau_notifierobj_alloc *na = data;
|
|
+ struct nouveau_channel *chan;
|
|
+ int ret;
|
|
+
|
|
+ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
|
|
+ NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(na->channel, file_priv, chan);
|
|
+
|
|
+ ret = nouveau_notifier_alloc(chan, na->handle, na->size, &na->offset);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_object.c b/drivers/gpu/drm/nouveau/nouveau_object.c
|
|
new file mode 100644
|
|
index 0000000..e7c100b
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_object.c
|
|
@@ -0,0 +1,1295 @@
|
|
+/*
|
|
+ * Copyright (C) 2006 Ben Skeggs.
|
|
+ *
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * Authors:
|
|
+ * Ben Skeggs <darktama@iinet.net.au>
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_drm.h"
|
|
+
|
|
+/* NVidia uses context objects to drive drawing operations.
|
|
+
|
|
+ Context objects can be selected into 8 subchannels in the FIFO,
|
|
+ and then used via DMA command buffers.
|
|
+
|
|
+ A context object is referenced by a user defined handle (CARD32). The HW
|
|
+ looks up graphics objects in a hash table in the instance RAM.
|
|
+
|
|
+ An entry in the hash table consists of 2 CARD32. The first CARD32 contains
|
|
+ the handle, the second one a bitfield, that contains the address of the
|
|
+ object in instance RAM.
|
|
+
|
|
+ The format of the second CARD32 seems to be:
|
|
+
|
|
+ NV4 to NV30:
|
|
+
|
|
+ 15: 0 instance_addr >> 4
|
|
+ 17:16 engine (here uses 1 = graphics)
|
|
+ 28:24 channel id (here uses 0)
|
|
+ 31 valid (use 1)
|
|
+
|
|
+ NV40:
|
|
+
|
|
+ 15: 0 instance_addr >> 4 (maybe 19-0)
|
|
+ 21:20 engine (here uses 1 = graphics)
|
|
+ I'm unsure about the other bits, but using 0 seems to work.
|
|
+
|
|
+ The key into the hash table depends on the object handle and channel id and
|
|
+ is given as:
|
|
+*/
|
|
+static uint32_t
|
|
+nouveau_ramht_hash_handle(struct drm_device *dev, int channel, uint32_t handle)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t hash = 0;
|
|
+ int i;
|
|
+
|
|
+ NV_DEBUG(dev, "ch%d handle=0x%08x\n", channel, handle);
|
|
+
|
|
+ for (i = 32; i > 0; i -= dev_priv->ramht_bits) {
|
|
+ hash ^= (handle & ((1 << dev_priv->ramht_bits) - 1));
|
|
+ handle >>= dev_priv->ramht_bits;
|
|
+ }
|
|
+
|
|
+ if (dev_priv->card_type < NV_50)
|
|
+ hash ^= channel << (dev_priv->ramht_bits - 4);
|
|
+ hash <<= 3;
|
|
+
|
|
+ NV_DEBUG(dev, "hash=0x%08x\n", hash);
|
|
+ return hash;
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_ramht_entry_valid(struct drm_device *dev, struct nouveau_gpuobj *ramht,
|
|
+ uint32_t offset)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t ctx = nv_ro32(dev, ramht, (offset + 4)/4);
|
|
+
|
|
+ if (dev_priv->card_type < NV_40)
|
|
+ return ((ctx & NV_RAMHT_CONTEXT_VALID) != 0);
|
|
+ return (ctx != 0);
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_ramht_insert(struct drm_device *dev, struct nouveau_gpuobj_ref *ref)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
|
|
+ struct nouveau_channel *chan = ref->channel;
|
|
+ struct nouveau_gpuobj *ramht = chan->ramht ? chan->ramht->gpuobj : NULL;
|
|
+ uint32_t ctx, co, ho;
|
|
+
|
|
+ if (!ramht) {
|
|
+ NV_ERROR(dev, "No hash table!\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (dev_priv->card_type < NV_40) {
|
|
+ ctx = NV_RAMHT_CONTEXT_VALID | (ref->instance >> 4) |
|
|
+ (chan->id << NV_RAMHT_CONTEXT_CHANNEL_SHIFT) |
|
|
+ (ref->gpuobj->engine << NV_RAMHT_CONTEXT_ENGINE_SHIFT);
|
|
+ } else
|
|
+ if (dev_priv->card_type < NV_50) {
|
|
+ ctx = (ref->instance >> 4) |
|
|
+ (chan->id << NV40_RAMHT_CONTEXT_CHANNEL_SHIFT) |
|
|
+ (ref->gpuobj->engine << NV40_RAMHT_CONTEXT_ENGINE_SHIFT);
|
|
+ } else {
|
|
+ if (ref->gpuobj->engine == NVOBJ_ENGINE_DISPLAY) {
|
|
+ ctx = (ref->instance << 10) | 2;
|
|
+ } else {
|
|
+ ctx = (ref->instance >> 4) |
|
|
+ ((ref->gpuobj->engine <<
|
|
+ NV40_RAMHT_CONTEXT_ENGINE_SHIFT));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ instmem->prepare_access(dev, true);
|
|
+ co = ho = nouveau_ramht_hash_handle(dev, chan->id, ref->handle);
|
|
+ do {
|
|
+ if (!nouveau_ramht_entry_valid(dev, ramht, co)) {
|
|
+ NV_DEBUG(dev,
|
|
+ "insert ch%d 0x%08x: h=0x%08x, c=0x%08x\n",
|
|
+ chan->id, co, ref->handle, ctx);
|
|
+ nv_wo32(dev, ramht, (co + 0)/4, ref->handle);
|
|
+ nv_wo32(dev, ramht, (co + 4)/4, ctx);
|
|
+
|
|
+ list_add_tail(&ref->list, &chan->ramht_refs);
|
|
+ instmem->finish_access(dev);
|
|
+ return 0;
|
|
+ }
|
|
+ NV_DEBUG(dev, "collision ch%d 0x%08x: h=0x%08x\n",
|
|
+ chan->id, co, nv_ro32(dev, ramht, co/4));
|
|
+
|
|
+ co += 8;
|
|
+ if (co >= dev_priv->ramht_size)
|
|
+ co = 0;
|
|
+ } while (co != ho);
|
|
+ instmem->finish_access(dev);
|
|
+
|
|
+ NV_ERROR(dev, "RAMHT space exhausted. ch=%d\n", chan->id);
|
|
+ return -ENOMEM;
|
|
+}
|
|
+
|
|
+static void
|
|
+nouveau_ramht_remove(struct drm_device *dev, struct nouveau_gpuobj_ref *ref)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
|
|
+ struct nouveau_channel *chan = ref->channel;
|
|
+ struct nouveau_gpuobj *ramht = chan->ramht ? chan->ramht->gpuobj : NULL;
|
|
+ uint32_t co, ho;
|
|
+
|
|
+ if (!ramht) {
|
|
+ NV_ERROR(dev, "No hash table!\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ instmem->prepare_access(dev, true);
|
|
+ co = ho = nouveau_ramht_hash_handle(dev, chan->id, ref->handle);
|
|
+ do {
|
|
+ if (nouveau_ramht_entry_valid(dev, ramht, co) &&
|
|
+ (ref->handle == nv_ro32(dev, ramht, (co/4)))) {
|
|
+ NV_DEBUG(dev,
|
|
+ "remove ch%d 0x%08x: h=0x%08x, c=0x%08x\n",
|
|
+ chan->id, co, ref->handle,
|
|
+ nv_ro32(dev, ramht, (co + 4)));
|
|
+ nv_wo32(dev, ramht, (co + 0)/4, 0x00000000);
|
|
+ nv_wo32(dev, ramht, (co + 4)/4, 0x00000000);
|
|
+
|
|
+ list_del(&ref->list);
|
|
+ instmem->finish_access(dev);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ co += 8;
|
|
+ if (co >= dev_priv->ramht_size)
|
|
+ co = 0;
|
|
+ } while (co != ho);
|
|
+ list_del(&ref->list);
|
|
+ instmem->finish_access(dev);
|
|
+
|
|
+ NV_ERROR(dev, "RAMHT entry not found. ch=%d, handle=0x%08x\n",
|
|
+ chan->id, ref->handle);
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan,
|
|
+ uint32_t size, int align, uint32_t flags,
|
|
+ struct nouveau_gpuobj **gpuobj_ret)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_engine *engine = &dev_priv->engine;
|
|
+ struct nouveau_gpuobj *gpuobj;
|
|
+ struct mem_block *pramin = NULL;
|
|
+ int ret;
|
|
+
|
|
+ NV_DEBUG(dev, "ch%d size=%u align=%d flags=0x%08x\n",
|
|
+ chan ? chan->id : -1, size, align, flags);
|
|
+
|
|
+ if (!dev_priv || !gpuobj_ret || *gpuobj_ret != NULL)
|
|
+ return -EINVAL;
|
|
+
|
|
+ gpuobj = kzalloc(sizeof(*gpuobj), GFP_KERNEL);
|
|
+ if (!gpuobj)
|
|
+ return -ENOMEM;
|
|
+ NV_DEBUG(dev, "gpuobj %p\n", gpuobj);
|
|
+ gpuobj->flags = flags;
|
|
+ gpuobj->im_channel = chan;
|
|
+
|
|
+ list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list);
|
|
+
|
|
+ /* Choose between global instmem heap, and per-channel private
|
|
+ * instmem heap. On <NV50 allow requests for private instmem
|
|
+ * to be satisfied from global heap if no per-channel area
|
|
+ * available.
|
|
+ */
|
|
+ if (chan) {
|
|
+ if (chan->ramin_heap) {
|
|
+ NV_DEBUG(dev, "private heap\n");
|
|
+ pramin = chan->ramin_heap;
|
|
+ } else
|
|
+ if (dev_priv->card_type < NV_50) {
|
|
+ NV_DEBUG(dev, "global heap fallback\n");
|
|
+ pramin = dev_priv->ramin_heap;
|
|
+ }
|
|
+ } else {
|
|
+ NV_DEBUG(dev, "global heap\n");
|
|
+ pramin = dev_priv->ramin_heap;
|
|
+ }
|
|
+
|
|
+ if (!pramin) {
|
|
+ NV_ERROR(dev, "No PRAMIN heap!\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (!chan) {
|
|
+ ret = engine->instmem.populate(dev, gpuobj, &size);
|
|
+ if (ret) {
|
|
+ nouveau_gpuobj_del(dev, &gpuobj);
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Allocate a chunk of the PRAMIN aperture */
|
|
+ gpuobj->im_pramin = nouveau_mem_alloc_block(pramin, size,
|
|
+ drm_order(align),
|
|
+ (struct drm_file *)-2, 0);
|
|
+ if (!gpuobj->im_pramin) {
|
|
+ nouveau_gpuobj_del(dev, &gpuobj);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ if (!chan) {
|
|
+ ret = engine->instmem.bind(dev, gpuobj);
|
|
+ if (ret) {
|
|
+ nouveau_gpuobj_del(dev, &gpuobj);
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (gpuobj->flags & NVOBJ_FLAG_ZERO_ALLOC) {
|
|
+ int i;
|
|
+
|
|
+ engine->instmem.prepare_access(dev, true);
|
|
+ for (i = 0; i < gpuobj->im_pramin->size; i += 4)
|
|
+ nv_wo32(dev, gpuobj, i/4, 0);
|
|
+ engine->instmem.finish_access(dev);
|
|
+ }
|
|
+
|
|
+ *gpuobj_ret = gpuobj;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_gpuobj_early_init(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ NV_DEBUG(dev, "\n");
|
|
+
|
|
+ INIT_LIST_HEAD(&dev_priv->gpuobj_list);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_gpuobj_init(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ int ret;
|
|
+
|
|
+ NV_DEBUG(dev, "\n");
|
|
+
|
|
+ if (dev_priv->card_type < NV_50) {
|
|
+ ret = nouveau_gpuobj_new_fake(dev,
|
|
+ dev_priv->ramht_offset, ~0, dev_priv->ramht_size,
|
|
+ NVOBJ_FLAG_ZERO_ALLOC | NVOBJ_FLAG_ALLOW_NO_REFS,
|
|
+ &dev_priv->ramht, NULL);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_gpuobj_takedown(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ NV_DEBUG(dev, "\n");
|
|
+
|
|
+ nouveau_gpuobj_del(dev, &dev_priv->ramht);
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_gpuobj_late_takedown(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_gpuobj *gpuobj = NULL;
|
|
+ struct list_head *entry, *tmp;
|
|
+
|
|
+ NV_DEBUG(dev, "\n");
|
|
+
|
|
+ list_for_each_safe(entry, tmp, &dev_priv->gpuobj_list) {
|
|
+ gpuobj = list_entry(entry, struct nouveau_gpuobj, list);
|
|
+
|
|
+ NV_ERROR(dev, "gpuobj %p still exists at takedown, refs=%d\n",
|
|
+ gpuobj, gpuobj->refcount);
|
|
+ gpuobj->refcount = 0;
|
|
+ nouveau_gpuobj_del(dev, &gpuobj);
|
|
+ }
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_gpuobj_del(struct drm_device *dev, struct nouveau_gpuobj **pgpuobj)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_engine *engine = &dev_priv->engine;
|
|
+ struct nouveau_gpuobj *gpuobj;
|
|
+ int i;
|
|
+
|
|
+ NV_DEBUG(dev, "gpuobj %p\n", pgpuobj ? *pgpuobj : NULL);
|
|
+
|
|
+ if (!dev_priv || !pgpuobj || !(*pgpuobj))
|
|
+ return -EINVAL;
|
|
+ gpuobj = *pgpuobj;
|
|
+
|
|
+ if (gpuobj->refcount != 0) {
|
|
+ NV_ERROR(dev, "gpuobj refcount is %d\n", gpuobj->refcount);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (gpuobj->im_pramin && (gpuobj->flags & NVOBJ_FLAG_ZERO_FREE)) {
|
|
+ engine->instmem.prepare_access(dev, true);
|
|
+ for (i = 0; i < gpuobj->im_pramin->size; i += 4)
|
|
+ nv_wo32(dev, gpuobj, i/4, 0);
|
|
+ engine->instmem.finish_access(dev);
|
|
+ }
|
|
+
|
|
+ if (gpuobj->dtor)
|
|
+ gpuobj->dtor(dev, gpuobj);
|
|
+
|
|
+ if (gpuobj->im_backing && !(gpuobj->flags & NVOBJ_FLAG_FAKE))
|
|
+ engine->instmem.clear(dev, gpuobj);
|
|
+
|
|
+ if (gpuobj->im_pramin) {
|
|
+ if (gpuobj->flags & NVOBJ_FLAG_FAKE)
|
|
+ kfree(gpuobj->im_pramin);
|
|
+ else
|
|
+ nouveau_mem_free_block(gpuobj->im_pramin);
|
|
+ }
|
|
+
|
|
+ list_del(&gpuobj->list);
|
|
+
|
|
+ *pgpuobj = NULL;
|
|
+ kfree(gpuobj);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_gpuobj_instance_get(struct drm_device *dev,
|
|
+ struct nouveau_channel *chan,
|
|
+ struct nouveau_gpuobj *gpuobj, uint32_t *inst)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_gpuobj *cpramin;
|
|
+
|
|
+ /* <NV50 use PRAMIN address everywhere */
|
|
+ if (dev_priv->card_type < NV_50) {
|
|
+ *inst = gpuobj->im_pramin->start;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (chan && gpuobj->im_channel != chan) {
|
|
+ NV_ERROR(dev, "Channel mismatch: obj %d, ref %d\n",
|
|
+ gpuobj->im_channel->id, chan->id);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* NV50 channel-local instance */
|
|
+ if (chan) {
|
|
+ cpramin = chan->ramin->gpuobj;
|
|
+ *inst = gpuobj->im_pramin->start - cpramin->im_pramin->start;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* NV50 global (VRAM) instance */
|
|
+ if (!gpuobj->im_channel) {
|
|
+ /* ...from global heap */
|
|
+ if (!gpuobj->im_backing) {
|
|
+ NV_ERROR(dev, "AII, no VRAM backing gpuobj\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ *inst = gpuobj->im_backing_start;
|
|
+ return 0;
|
|
+ } else {
|
|
+ /* ...from local heap */
|
|
+ cpramin = gpuobj->im_channel->ramin->gpuobj;
|
|
+ *inst = cpramin->im_backing_start +
|
|
+ (gpuobj->im_pramin->start - cpramin->im_pramin->start);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_gpuobj_ref_add(struct drm_device *dev, struct nouveau_channel *chan,
|
|
+ uint32_t handle, struct nouveau_gpuobj *gpuobj,
|
|
+ struct nouveau_gpuobj_ref **ref_ret)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_gpuobj_ref *ref;
|
|
+ uint32_t instance;
|
|
+ int ret;
|
|
+
|
|
+ NV_DEBUG(dev, "ch%d h=0x%08x gpuobj=%p\n",
|
|
+ chan ? chan->id : -1, handle, gpuobj);
|
|
+
|
|
+ if (!dev_priv || !gpuobj || (ref_ret && *ref_ret != NULL))
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (!chan && !ref_ret)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (gpuobj->engine == NVOBJ_ENGINE_SW && !gpuobj->im_pramin) {
|
|
+ /* sw object */
|
|
+ instance = 0x40;
|
|
+ } else {
|
|
+ ret = nouveau_gpuobj_instance_get(dev, chan, gpuobj, &instance);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ref = kzalloc(sizeof(*ref), GFP_KERNEL);
|
|
+ if (!ref)
|
|
+ return -ENOMEM;
|
|
+ INIT_LIST_HEAD(&ref->list);
|
|
+ ref->gpuobj = gpuobj;
|
|
+ ref->channel = chan;
|
|
+ ref->instance = instance;
|
|
+
|
|
+ if (!ref_ret) {
|
|
+ ref->handle = handle;
|
|
+
|
|
+ ret = nouveau_ramht_insert(dev, ref);
|
|
+ if (ret) {
|
|
+ kfree(ref);
|
|
+ return ret;
|
|
+ }
|
|
+ } else {
|
|
+ ref->handle = ~0;
|
|
+ *ref_ret = ref;
|
|
+ }
|
|
+
|
|
+ ref->gpuobj->refcount++;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int nouveau_gpuobj_ref_del(struct drm_device *dev, struct nouveau_gpuobj_ref **pref)
|
|
+{
|
|
+ struct nouveau_gpuobj_ref *ref;
|
|
+
|
|
+ NV_DEBUG(dev, "ref %p\n", pref ? *pref : NULL);
|
|
+
|
|
+ if (!dev || !pref || *pref == NULL)
|
|
+ return -EINVAL;
|
|
+ ref = *pref;
|
|
+
|
|
+ if (ref->handle != ~0)
|
|
+ nouveau_ramht_remove(dev, ref);
|
|
+
|
|
+ if (ref->gpuobj) {
|
|
+ ref->gpuobj->refcount--;
|
|
+
|
|
+ if (ref->gpuobj->refcount == 0) {
|
|
+ if (!(ref->gpuobj->flags & NVOBJ_FLAG_ALLOW_NO_REFS))
|
|
+ nouveau_gpuobj_del(dev, &ref->gpuobj);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ *pref = NULL;
|
|
+ kfree(ref);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_gpuobj_new_ref(struct drm_device *dev,
|
|
+ struct nouveau_channel *oc, struct nouveau_channel *rc,
|
|
+ uint32_t handle, uint32_t size, int align,
|
|
+ uint32_t flags, struct nouveau_gpuobj_ref **ref)
|
|
+{
|
|
+ struct nouveau_gpuobj *gpuobj = NULL;
|
|
+ int ret;
|
|
+
|
|
+ ret = nouveau_gpuobj_new(dev, oc, size, align, flags, &gpuobj);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = nouveau_gpuobj_ref_add(dev, rc, handle, gpuobj, ref);
|
|
+ if (ret) {
|
|
+ nouveau_gpuobj_del(dev, &gpuobj);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_gpuobj_ref_find(struct nouveau_channel *chan, uint32_t handle,
|
|
+ struct nouveau_gpuobj_ref **ref_ret)
|
|
+{
|
|
+ struct nouveau_gpuobj_ref *ref;
|
|
+ struct list_head *entry, *tmp;
|
|
+
|
|
+ list_for_each_safe(entry, tmp, &chan->ramht_refs) {
|
|
+ ref = list_entry(entry, struct nouveau_gpuobj_ref, list);
|
|
+
|
|
+ if (ref->handle == handle) {
|
|
+ if (ref_ret)
|
|
+ *ref_ret = ref;
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_gpuobj_new_fake(struct drm_device *dev, uint32_t p_offset,
|
|
+ uint32_t b_offset, uint32_t size,
|
|
+ uint32_t flags, struct nouveau_gpuobj **pgpuobj,
|
|
+ struct nouveau_gpuobj_ref **pref)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_gpuobj *gpuobj = NULL;
|
|
+ int i;
|
|
+
|
|
+ NV_DEBUG(dev,
|
|
+ "p_offset=0x%08x b_offset=0x%08x size=0x%08x flags=0x%08x\n",
|
|
+ p_offset, b_offset, size, flags);
|
|
+
|
|
+ gpuobj = kzalloc(sizeof(*gpuobj), GFP_KERNEL);
|
|
+ if (!gpuobj)
|
|
+ return -ENOMEM;
|
|
+ NV_DEBUG(dev, "gpuobj %p\n", gpuobj);
|
|
+ gpuobj->im_channel = NULL;
|
|
+ gpuobj->flags = flags | NVOBJ_FLAG_FAKE;
|
|
+
|
|
+ list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list);
|
|
+
|
|
+ if (p_offset != ~0) {
|
|
+ gpuobj->im_pramin = kzalloc(sizeof(struct mem_block),
|
|
+ GFP_KERNEL);
|
|
+ if (!gpuobj->im_pramin) {
|
|
+ nouveau_gpuobj_del(dev, &gpuobj);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ gpuobj->im_pramin->start = p_offset;
|
|
+ gpuobj->im_pramin->size = size;
|
|
+ }
|
|
+
|
|
+ if (b_offset != ~0) {
|
|
+ gpuobj->im_backing = (struct nouveau_bo *)-1;
|
|
+ gpuobj->im_backing_start = b_offset;
|
|
+ }
|
|
+
|
|
+ if (gpuobj->flags & NVOBJ_FLAG_ZERO_ALLOC) {
|
|
+ dev_priv->engine.instmem.prepare_access(dev, true);
|
|
+ for (i = 0; i < gpuobj->im_pramin->size; i += 4)
|
|
+ nv_wo32(dev, gpuobj, i/4, 0);
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+ }
|
|
+
|
|
+ if (pref) {
|
|
+ i = nouveau_gpuobj_ref_add(dev, NULL, 0, gpuobj, pref);
|
|
+ if (i) {
|
|
+ nouveau_gpuobj_del(dev, &gpuobj);
|
|
+ return i;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (pgpuobj)
|
|
+ *pgpuobj = gpuobj;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static uint32_t
|
|
+nouveau_gpuobj_class_instmem_size(struct drm_device *dev, int class)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ /*XXX: dodgy hack for now */
|
|
+ if (dev_priv->card_type >= NV_50)
|
|
+ return 24;
|
|
+ if (dev_priv->card_type >= NV_40)
|
|
+ return 32;
|
|
+ return 16;
|
|
+}
|
|
+
|
|
+/*
|
|
+ DMA objects are used to reference a piece of memory in the
|
|
+ framebuffer, PCI or AGP address space. Each object is 16 bytes big
|
|
+ and looks as follows:
|
|
+
|
|
+ entry[0]
|
|
+ 11:0 class (seems like I can always use 0 here)
|
|
+ 12 page table present?
|
|
+ 13 page entry linear?
|
|
+ 15:14 access: 0 rw, 1 ro, 2 wo
|
|
+ 17:16 target: 0 NV memory, 1 NV memory tiled, 2 PCI, 3 AGP
|
|
+ 31:20 dma adjust (bits 0-11 of the address)
|
|
+ entry[1]
|
|
+ dma limit (size of transfer)
|
|
+ entry[X]
|
|
+ 1 0 readonly, 1 readwrite
|
|
+ 31:12 dma frame address of the page (bits 12-31 of the address)
|
|
+ entry[N]
|
|
+ page table terminator, same value as the first pte, as does nvidia
|
|
+ rivatv uses 0xffffffff
|
|
+
|
|
+ Non linear page tables need a list of frame addresses afterwards,
|
|
+ the rivatv project has some info on this.
|
|
+
|
|
+ The method below creates a DMA object in instance RAM and returns a handle
|
|
+ to it that can be used to set up context objects.
|
|
+*/
|
|
+int
|
|
+nouveau_gpuobj_dma_new(struct nouveau_channel *chan, int class,
|
|
+ uint64_t offset, uint64_t size, int access,
|
|
+ int target, struct nouveau_gpuobj **gpuobj)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
|
|
+ int ret;
|
|
+
|
|
+ NV_DEBUG(dev, "ch%d class=0x%04x offset=0x%llx size=0x%llx\n",
|
|
+ chan->id, class, offset, size);
|
|
+ NV_DEBUG(dev, "access=%d target=%d\n", access, target);
|
|
+
|
|
+ switch (target) {
|
|
+ case NV_DMA_TARGET_AGP:
|
|
+ offset += dev_priv->gart_info.aper_base;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ ret = nouveau_gpuobj_new(dev, chan,
|
|
+ nouveau_gpuobj_class_instmem_size(dev, class),
|
|
+ 16, NVOBJ_FLAG_ZERO_ALLOC |
|
|
+ NVOBJ_FLAG_ZERO_FREE, gpuobj);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Error creating gpuobj: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ instmem->prepare_access(dev, true);
|
|
+
|
|
+ if (dev_priv->card_type < NV_50) {
|
|
+ uint32_t frame, adjust, pte_flags = 0;
|
|
+
|
|
+ if (access != NV_DMA_ACCESS_RO)
|
|
+ pte_flags |= (1<<1);
|
|
+ adjust = offset & 0x00000fff;
|
|
+ frame = offset & ~0x00000fff;
|
|
+
|
|
+ nv_wo32(dev, *gpuobj, 0, ((1<<12) | (1<<13) |
|
|
+ (adjust << 20) |
|
|
+ (access << 14) |
|
|
+ (target << 16) |
|
|
+ class));
|
|
+ nv_wo32(dev, *gpuobj, 1, size - 1);
|
|
+ nv_wo32(dev, *gpuobj, 2, frame | pte_flags);
|
|
+ nv_wo32(dev, *gpuobj, 3, frame | pte_flags);
|
|
+ } else {
|
|
+ uint64_t limit = offset + size - 1;
|
|
+ uint32_t flags0, flags5;
|
|
+
|
|
+ if (target == NV_DMA_TARGET_VIDMEM) {
|
|
+ flags0 = 0x00190000;
|
|
+ flags5 = 0x00010000;
|
|
+ } else {
|
|
+ flags0 = 0x7fc00000;
|
|
+ flags5 = 0x00080000;
|
|
+ }
|
|
+
|
|
+ nv_wo32(dev, *gpuobj, 0, flags0 | class);
|
|
+ nv_wo32(dev, *gpuobj, 1, lower_32_bits(limit));
|
|
+ nv_wo32(dev, *gpuobj, 2, lower_32_bits(offset));
|
|
+ nv_wo32(dev, *gpuobj, 3, ((upper_32_bits(limit) & 0xff) << 24) |
|
|
+ (upper_32_bits(offset) & 0xff));
|
|
+ nv_wo32(dev, *gpuobj, 5, flags5);
|
|
+ }
|
|
+
|
|
+ instmem->finish_access(dev);
|
|
+
|
|
+ (*gpuobj)->engine = NVOBJ_ENGINE_SW;
|
|
+ (*gpuobj)->class = class;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_gpuobj_gart_dma_new(struct nouveau_channel *chan,
|
|
+ uint64_t offset, uint64_t size, int access,
|
|
+ struct nouveau_gpuobj **gpuobj,
|
|
+ uint32_t *o_ret)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ int ret;
|
|
+
|
|
+ if (dev_priv->gart_info.type == NOUVEAU_GART_AGP ||
|
|
+ (dev_priv->card_type >= NV_50 &&
|
|
+ dev_priv->gart_info.type == NOUVEAU_GART_SGDMA)) {
|
|
+ ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY,
|
|
+ offset + dev_priv->vm_gart_base,
|
|
+ size, access, NV_DMA_TARGET_AGP,
|
|
+ gpuobj);
|
|
+ if (o_ret)
|
|
+ *o_ret = 0;
|
|
+ } else
|
|
+ if (dev_priv->gart_info.type == NOUVEAU_GART_SGDMA) {
|
|
+ *gpuobj = dev_priv->gart_info.sg_ctxdma;
|
|
+ if (offset & ~0xffffffffULL) {
|
|
+ NV_ERROR(dev, "obj offset exceeds 32-bits\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ if (o_ret)
|
|
+ *o_ret = (uint32_t)offset;
|
|
+ ret = (*gpuobj != NULL) ? 0 : -EINVAL;
|
|
+ } else {
|
|
+ NV_ERROR(dev, "Invalid GART type %d\n", dev_priv->gart_info.type);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/* Context objects in the instance RAM have the following structure.
|
|
+ * On NV40 they are 32 byte long, on NV30 and smaller 16 bytes.
|
|
+
|
|
+ NV4 - NV30:
|
|
+
|
|
+ entry[0]
|
|
+ 11:0 class
|
|
+ 12 chroma key enable
|
|
+ 13 user clip enable
|
|
+ 14 swizzle enable
|
|
+ 17:15 patch config:
|
|
+ scrcopy_and, rop_and, blend_and, scrcopy, srccopy_pre, blend_pre
|
|
+ 18 synchronize enable
|
|
+ 19 endian: 1 big, 0 little
|
|
+ 21:20 dither mode
|
|
+ 23 single step enable
|
|
+ 24 patch status: 0 invalid, 1 valid
|
|
+ 25 context_surface 0: 1 valid
|
|
+ 26 context surface 1: 1 valid
|
|
+ 27 context pattern: 1 valid
|
|
+ 28 context rop: 1 valid
|
|
+ 29,30 context beta, beta4
|
|
+ entry[1]
|
|
+ 7:0 mono format
|
|
+ 15:8 color format
|
|
+ 31:16 notify instance address
|
|
+ entry[2]
|
|
+ 15:0 dma 0 instance address
|
|
+ 31:16 dma 1 instance address
|
|
+ entry[3]
|
|
+ dma method traps
|
|
+
|
|
+ NV40:
|
|
+ No idea what the exact format is. Here's what can be deducted:
|
|
+
|
|
+ entry[0]:
|
|
+ 11:0 class (maybe uses more bits here?)
|
|
+ 17 user clip enable
|
|
+ 21:19 patch config
|
|
+ 25 patch status valid ?
|
|
+ entry[1]:
|
|
+ 15:0 DMA notifier (maybe 20:0)
|
|
+ entry[2]:
|
|
+ 15:0 DMA 0 instance (maybe 20:0)
|
|
+ 24 big endian
|
|
+ entry[3]:
|
|
+ 15:0 DMA 1 instance (maybe 20:0)
|
|
+ entry[4]:
|
|
+ entry[5]:
|
|
+ set to 0?
|
|
+*/
|
|
+int
|
|
+nouveau_gpuobj_gr_new(struct nouveau_channel *chan, int class,
|
|
+ struct nouveau_gpuobj **gpuobj)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ int ret;
|
|
+
|
|
+ NV_DEBUG(dev, "ch%d class=0x%04x\n", chan->id, class);
|
|
+
|
|
+ ret = nouveau_gpuobj_new(dev, chan,
|
|
+ nouveau_gpuobj_class_instmem_size(dev, class),
|
|
+ 16,
|
|
+ NVOBJ_FLAG_ZERO_ALLOC | NVOBJ_FLAG_ZERO_FREE,
|
|
+ gpuobj);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Error creating gpuobj: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ dev_priv->engine.instmem.prepare_access(dev, true);
|
|
+ if (dev_priv->card_type >= NV_50) {
|
|
+ nv_wo32(dev, *gpuobj, 0, class);
|
|
+ nv_wo32(dev, *gpuobj, 5, 0x00010000);
|
|
+ } else {
|
|
+ switch (class) {
|
|
+ case NV_CLASS_NULL:
|
|
+ nv_wo32(dev, *gpuobj, 0, 0x00001030);
|
|
+ nv_wo32(dev, *gpuobj, 1, 0xFFFFFFFF);
|
|
+ break;
|
|
+ default:
|
|
+ if (dev_priv->card_type >= NV_40) {
|
|
+ nv_wo32(dev, *gpuobj, 0, class);
|
|
+#ifdef __BIG_ENDIAN
|
|
+ nv_wo32(dev, *gpuobj, 2, 0x01000000);
|
|
+#endif
|
|
+ } else {
|
|
+#ifdef __BIG_ENDIAN
|
|
+ nv_wo32(dev, *gpuobj, 0, class | 0x00080000);
|
|
+#else
|
|
+ nv_wo32(dev, *gpuobj, 0, class);
|
|
+#endif
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+
|
|
+ (*gpuobj)->engine = NVOBJ_ENGINE_GR;
|
|
+ (*gpuobj)->class = class;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_gpuobj_sw_new(struct nouveau_channel *chan, int class,
|
|
+ struct nouveau_gpuobj **gpuobj_ret)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv;
|
|
+ struct nouveau_gpuobj *gpuobj;
|
|
+
|
|
+ if (!chan || !gpuobj_ret || *gpuobj_ret != NULL)
|
|
+ return -EINVAL;
|
|
+ dev_priv = chan->dev->dev_private;
|
|
+
|
|
+ gpuobj = kzalloc(sizeof(*gpuobj), GFP_KERNEL);
|
|
+ if (!gpuobj)
|
|
+ return -ENOMEM;
|
|
+ gpuobj->engine = NVOBJ_ENGINE_SW;
|
|
+ gpuobj->class = class;
|
|
+
|
|
+ list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list);
|
|
+ *gpuobj_ret = gpuobj;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_gpuobj_channel_init_pramin(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_gpuobj *pramin = NULL;
|
|
+ uint32_t size;
|
|
+ uint32_t base;
|
|
+ int ret;
|
|
+
|
|
+ NV_DEBUG(dev, "ch%d\n", chan->id);
|
|
+
|
|
+ /* Base amount for object storage (4KiB enough?) */
|
|
+ size = 0x1000;
|
|
+ base = 0;
|
|
+
|
|
+ /* PGRAPH context */
|
|
+
|
|
+ if (dev_priv->card_type == NV_50) {
|
|
+ /* Various fixed table thingos */
|
|
+ size += 0x1400; /* mostly unknown stuff */
|
|
+ size += 0x4000; /* vm pd */
|
|
+ base = 0x6000;
|
|
+ /* RAMHT, not sure about setting size yet, 32KiB to be safe */
|
|
+ size += 0x8000;
|
|
+ /* RAMFC */
|
|
+ size += 0x1000;
|
|
+ /* PGRAPH context */
|
|
+ size += 0x70000;
|
|
+ }
|
|
+
|
|
+ NV_DEBUG(dev, "ch%d PRAMIN size: 0x%08x bytes, base alloc=0x%08x\n",
|
|
+ chan->id, size, base);
|
|
+ ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, size, 0x1000, 0,
|
|
+ &chan->ramin);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Error allocating channel PRAMIN: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+ pramin = chan->ramin->gpuobj;
|
|
+
|
|
+ ret = nouveau_mem_init_heap(&chan->ramin_heap,
|
|
+ pramin->im_pramin->start + base, size);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Error creating PRAMIN heap: %d\n", ret);
|
|
+ nouveau_gpuobj_ref_del(dev, &chan->ramin);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_gpuobj_channel_init(struct nouveau_channel *chan,
|
|
+ uint32_t vram_h, uint32_t tt_h)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
|
|
+ struct nouveau_gpuobj *vram = NULL, *tt = NULL;
|
|
+ int ret, i;
|
|
+
|
|
+ INIT_LIST_HEAD(&chan->ramht_refs);
|
|
+
|
|
+ NV_DEBUG(dev, "ch%d vram=0x%08x tt=0x%08x\n", chan->id, vram_h, tt_h);
|
|
+
|
|
+ /* Reserve a block of PRAMIN for the channel
|
|
+ *XXX: maybe on <NV50 too at some point
|
|
+ */
|
|
+ if (0 || dev_priv->card_type == NV_50) {
|
|
+ ret = nouveau_gpuobj_channel_init_pramin(chan);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "init pramin\n");
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* NV50 VM
|
|
+ * - Allocate per-channel page-directory
|
|
+ * - Map GART and VRAM into the channel's address space at the
|
|
+ * locations determined during init.
|
|
+ */
|
|
+ if (dev_priv->card_type >= NV_50) {
|
|
+ uint32_t vm_offset, pde;
|
|
+
|
|
+ instmem->prepare_access(dev, true);
|
|
+
|
|
+ vm_offset = (dev_priv->chipset & 0xf0) == 0x50 ? 0x1400 : 0x200;
|
|
+ vm_offset += chan->ramin->gpuobj->im_pramin->start;
|
|
+
|
|
+ ret = nouveau_gpuobj_new_fake(dev, vm_offset, ~0, 0x4000,
|
|
+ 0, &chan->vm_pd, NULL);
|
|
+ if (ret) {
|
|
+ instmem->finish_access(dev);
|
|
+ return ret;
|
|
+ }
|
|
+ for (i = 0; i < 0x4000; i += 8) {
|
|
+ nv_wo32(dev, chan->vm_pd, (i+0)/4, 0x00000000);
|
|
+ nv_wo32(dev, chan->vm_pd, (i+4)/4, 0xdeadcafe);
|
|
+ }
|
|
+
|
|
+ pde = (dev_priv->vm_gart_base / (512*1024*1024)) * 2;
|
|
+ ret = nouveau_gpuobj_ref_add(dev, NULL, 0,
|
|
+ dev_priv->gart_info.sg_ctxdma,
|
|
+ &chan->vm_gart_pt);
|
|
+ if (ret) {
|
|
+ instmem->finish_access(dev);
|
|
+ return ret;
|
|
+ }
|
|
+ nv_wo32(dev, chan->vm_pd, pde++,
|
|
+ chan->vm_gart_pt->instance | 0x03);
|
|
+ nv_wo32(dev, chan->vm_pd, pde++, 0x00000000);
|
|
+
|
|
+ pde = (dev_priv->vm_vram_base / (512*1024*1024)) * 2;
|
|
+ for (i = 0; i < dev_priv->vm_vram_pt_nr; i++) {
|
|
+ ret = nouveau_gpuobj_ref_add(dev, NULL, 0,
|
|
+ dev_priv->vm_vram_pt[i],
|
|
+ &chan->vm_vram_pt[i]);
|
|
+ if (ret) {
|
|
+ instmem->finish_access(dev);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ nv_wo32(dev, chan->vm_pd, pde++,
|
|
+ chan->vm_vram_pt[i]->instance | 0x61);
|
|
+ nv_wo32(dev, chan->vm_pd, pde++, 0x00000000);
|
|
+ }
|
|
+
|
|
+ instmem->finish_access(dev);
|
|
+ }
|
|
+
|
|
+ /* RAMHT */
|
|
+ if (dev_priv->card_type < NV_50) {
|
|
+ ret = nouveau_gpuobj_ref_add(dev, NULL, 0, dev_priv->ramht,
|
|
+ &chan->ramht);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ } else {
|
|
+ ret = nouveau_gpuobj_new_ref(dev, chan, chan, 0,
|
|
+ 0x8000, 16,
|
|
+ NVOBJ_FLAG_ZERO_ALLOC,
|
|
+ &chan->ramht);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /* VRAM ctxdma */
|
|
+ if (dev_priv->card_type >= NV_50) {
|
|
+ ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY,
|
|
+ 0, dev_priv->vm_end,
|
|
+ NV_DMA_ACCESS_RW,
|
|
+ NV_DMA_TARGET_AGP, &vram);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Error creating VRAM ctxdma: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+ } else {
|
|
+ ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY,
|
|
+ 0, dev_priv->fb_available_size,
|
|
+ NV_DMA_ACCESS_RW,
|
|
+ NV_DMA_TARGET_VIDMEM, &vram);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Error creating VRAM ctxdma: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ret = nouveau_gpuobj_ref_add(dev, chan, vram_h, vram, NULL);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Error referencing VRAM ctxdma: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /* TT memory ctxdma */
|
|
+ if (dev_priv->card_type >= NV_50) {
|
|
+ tt = vram;
|
|
+ } else
|
|
+ if (dev_priv->gart_info.type != NOUVEAU_GART_NONE) {
|
|
+ ret = nouveau_gpuobj_gart_dma_new(chan, 0,
|
|
+ dev_priv->gart_info.aper_size,
|
|
+ NV_DMA_ACCESS_RW, &tt, NULL);
|
|
+ } else {
|
|
+ NV_ERROR(dev, "Invalid GART type %d\n", dev_priv->gart_info.type);
|
|
+ ret = -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Error creating TT ctxdma: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = nouveau_gpuobj_ref_add(dev, chan, tt_h, tt, NULL);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Error referencing TT ctxdma: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_gpuobj_channel_takedown(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct list_head *entry, *tmp;
|
|
+ struct nouveau_gpuobj_ref *ref;
|
|
+ int i;
|
|
+
|
|
+ NV_DEBUG(dev, "ch%d\n", chan->id);
|
|
+
|
|
+ if (!chan->ramht_refs.next)
|
|
+ return;
|
|
+
|
|
+ list_for_each_safe(entry, tmp, &chan->ramht_refs) {
|
|
+ ref = list_entry(entry, struct nouveau_gpuobj_ref, list);
|
|
+
|
|
+ nouveau_gpuobj_ref_del(dev, &ref);
|
|
+ }
|
|
+
|
|
+ nouveau_gpuobj_ref_del(dev, &chan->ramht);
|
|
+
|
|
+ nouveau_gpuobj_del(dev, &chan->vm_pd);
|
|
+ nouveau_gpuobj_ref_del(dev, &chan->vm_gart_pt);
|
|
+ for (i = 0; i < dev_priv->vm_vram_pt_nr; i++)
|
|
+ nouveau_gpuobj_ref_del(dev, &chan->vm_vram_pt[i]);
|
|
+
|
|
+ if (chan->ramin_heap)
|
|
+ nouveau_mem_takedown(&chan->ramin_heap);
|
|
+ if (chan->ramin)
|
|
+ nouveau_gpuobj_ref_del(dev, &chan->ramin);
|
|
+
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_gpuobj_suspend(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_gpuobj *gpuobj;
|
|
+ int i;
|
|
+
|
|
+ if (dev_priv->card_type < NV_50) {
|
|
+ dev_priv->susres.ramin_copy = vmalloc(dev_priv->ramin_rsvd_vram);
|
|
+ if (!dev_priv->susres.ramin_copy)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ for (i = 0; i < dev_priv->ramin_rsvd_vram; i += 4)
|
|
+ dev_priv->susres.ramin_copy[i/4] = nv_ri32(dev, i);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ list_for_each_entry(gpuobj, &dev_priv->gpuobj_list, list) {
|
|
+ if (!gpuobj->im_backing || (gpuobj->flags & NVOBJ_FLAG_FAKE))
|
|
+ continue;
|
|
+
|
|
+ gpuobj->im_backing_suspend = vmalloc(gpuobj->im_pramin->size);
|
|
+ if (!gpuobj->im_backing_suspend) {
|
|
+ nouveau_gpuobj_resume(dev);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ dev_priv->engine.instmem.prepare_access(dev, false);
|
|
+ for (i = 0; i < gpuobj->im_pramin->size / 4; i++)
|
|
+ gpuobj->im_backing_suspend[i] = nv_ro32(dev, gpuobj, i);
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_gpuobj_suspend_cleanup(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_gpuobj *gpuobj;
|
|
+
|
|
+ if (dev_priv->card_type < NV_50) {
|
|
+ vfree(dev_priv->susres.ramin_copy);
|
|
+ dev_priv->susres.ramin_copy = NULL;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ list_for_each_entry(gpuobj, &dev_priv->gpuobj_list, list) {
|
|
+ if (!gpuobj->im_backing_suspend)
|
|
+ continue;
|
|
+
|
|
+ vfree(gpuobj->im_backing_suspend);
|
|
+ gpuobj->im_backing_suspend = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_gpuobj_resume(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_gpuobj *gpuobj;
|
|
+ int i;
|
|
+
|
|
+ if (dev_priv->card_type < NV_50) {
|
|
+ for (i = 0; i < dev_priv->ramin_rsvd_vram; i += 4)
|
|
+ nv_wi32(dev, i, dev_priv->susres.ramin_copy[i/4]);
|
|
+ nouveau_gpuobj_suspend_cleanup(dev);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ list_for_each_entry(gpuobj, &dev_priv->gpuobj_list, list) {
|
|
+ if (!gpuobj->im_backing_suspend)
|
|
+ continue;
|
|
+
|
|
+ dev_priv->engine.instmem.prepare_access(dev, true);
|
|
+ for (i = 0; i < gpuobj->im_pramin->size / 4; i++)
|
|
+ nv_wo32(dev, gpuobj, i, gpuobj->im_backing_suspend[i]);
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+ }
|
|
+
|
|
+ nouveau_gpuobj_suspend_cleanup(dev);
|
|
+}
|
|
+
|
|
+int nouveau_ioctl_grobj_alloc(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct drm_nouveau_grobj_alloc *init = data;
|
|
+ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
|
|
+ struct nouveau_pgraph_object_class *grc;
|
|
+ struct nouveau_gpuobj *gr = NULL;
|
|
+ struct nouveau_channel *chan;
|
|
+ int ret;
|
|
+
|
|
+ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
|
|
+ NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(init->channel, file_priv, chan);
|
|
+
|
|
+ if (init->handle == ~0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ grc = pgraph->grclass;
|
|
+ while (grc->id) {
|
|
+ if (grc->id == init->class)
|
|
+ break;
|
|
+ grc++;
|
|
+ }
|
|
+
|
|
+ if (!grc->id) {
|
|
+ NV_ERROR(dev, "Illegal object class: 0x%x\n", init->class);
|
|
+ return -EPERM;
|
|
+ }
|
|
+
|
|
+ if (nouveau_gpuobj_ref_find(chan, init->handle, NULL) == 0)
|
|
+ return -EEXIST;
|
|
+
|
|
+ if (!grc->software)
|
|
+ ret = nouveau_gpuobj_gr_new(chan, grc->id, &gr);
|
|
+ else
|
|
+ ret = nouveau_gpuobj_sw_new(chan, grc->id, &gr);
|
|
+
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Error creating object: %d (%d/0x%08x)\n",
|
|
+ ret, init->channel, init->handle);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = nouveau_gpuobj_ref_add(dev, chan, init->handle, gr, NULL);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Error referencing object: %d (%d/0x%08x)\n",
|
|
+ ret, init->channel, init->handle);
|
|
+ nouveau_gpuobj_del(dev, &gr);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int nouveau_ioctl_gpuobj_free(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_nouveau_gpuobj_free *objfree = data;
|
|
+ struct nouveau_gpuobj_ref *ref;
|
|
+ struct nouveau_channel *chan;
|
|
+ int ret;
|
|
+
|
|
+ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
|
|
+ NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(objfree->channel, file_priv, chan);
|
|
+
|
|
+ ret = nouveau_gpuobj_ref_find(chan, objfree->handle, &ref);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ nouveau_gpuobj_ref_del(dev, &ref);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h
|
|
new file mode 100644
|
|
index 0000000..aa9b310
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_reg.h
|
|
@@ -0,0 +1,837 @@
|
|
+
|
|
+
|
|
+#define NV03_BOOT_0 0x00100000
|
|
+# define NV03_BOOT_0_RAM_AMOUNT 0x00000003
|
|
+# define NV03_BOOT_0_RAM_AMOUNT_8MB 0x00000000
|
|
+# define NV03_BOOT_0_RAM_AMOUNT_2MB 0x00000001
|
|
+# define NV03_BOOT_0_RAM_AMOUNT_4MB 0x00000002
|
|
+# define NV03_BOOT_0_RAM_AMOUNT_8MB_SDRAM 0x00000003
|
|
+# define NV04_BOOT_0_RAM_AMOUNT_32MB 0x00000000
|
|
+# define NV04_BOOT_0_RAM_AMOUNT_4MB 0x00000001
|
|
+# define NV04_BOOT_0_RAM_AMOUNT_8MB 0x00000002
|
|
+# define NV04_BOOT_0_RAM_AMOUNT_16MB 0x00000003
|
|
+
|
|
+#define NV04_FIFO_DATA 0x0010020c
|
|
+# define NV10_FIFO_DATA_RAM_AMOUNT_MB_MASK 0xfff00000
|
|
+# define NV10_FIFO_DATA_RAM_AMOUNT_MB_SHIFT 20
|
|
+
|
|
+#define NV_RAMIN 0x00700000
|
|
+
|
|
+#define NV_RAMHT_HANDLE_OFFSET 0
|
|
+#define NV_RAMHT_CONTEXT_OFFSET 4
|
|
+# define NV_RAMHT_CONTEXT_VALID (1<<31)
|
|
+# define NV_RAMHT_CONTEXT_CHANNEL_SHIFT 24
|
|
+# define NV_RAMHT_CONTEXT_ENGINE_SHIFT 16
|
|
+# define NV_RAMHT_CONTEXT_ENGINE_SOFTWARE 0
|
|
+# define NV_RAMHT_CONTEXT_ENGINE_GRAPHICS 1
|
|
+# define NV_RAMHT_CONTEXT_INSTANCE_SHIFT 0
|
|
+# define NV40_RAMHT_CONTEXT_CHANNEL_SHIFT 23
|
|
+# define NV40_RAMHT_CONTEXT_ENGINE_SHIFT 20
|
|
+# define NV40_RAMHT_CONTEXT_INSTANCE_SHIFT 0
|
|
+
|
|
+/* DMA object defines */
|
|
+#define NV_DMA_ACCESS_RW 0
|
|
+#define NV_DMA_ACCESS_RO 1
|
|
+#define NV_DMA_ACCESS_WO 2
|
|
+#define NV_DMA_TARGET_VIDMEM 0
|
|
+#define NV_DMA_TARGET_PCI 2
|
|
+#define NV_DMA_TARGET_AGP 3
|
|
+/* The following is not a real value used by the card, it's changed by
|
|
+ * nouveau_object_dma_create */
|
|
+#define NV_DMA_TARGET_PCI_NONLINEAR 8
|
|
+
|
|
+/* Some object classes we care about in the drm */
|
|
+#define NV_CLASS_DMA_FROM_MEMORY 0x00000002
|
|
+#define NV_CLASS_DMA_TO_MEMORY 0x00000003
|
|
+#define NV_CLASS_NULL 0x00000030
|
|
+#define NV_CLASS_DMA_IN_MEMORY 0x0000003D
|
|
+
|
|
+#define NV03_USER(i) (0x00800000+(i*NV03_USER_SIZE))
|
|
+#define NV03_USER__SIZE 16
|
|
+#define NV10_USER__SIZE 32
|
|
+#define NV03_USER_SIZE 0x00010000
|
|
+#define NV03_USER_DMA_PUT(i) (0x00800040+(i*NV03_USER_SIZE))
|
|
+#define NV03_USER_DMA_PUT__SIZE 16
|
|
+#define NV10_USER_DMA_PUT__SIZE 32
|
|
+#define NV03_USER_DMA_GET(i) (0x00800044+(i*NV03_USER_SIZE))
|
|
+#define NV03_USER_DMA_GET__SIZE 16
|
|
+#define NV10_USER_DMA_GET__SIZE 32
|
|
+#define NV03_USER_REF_CNT(i) (0x00800048+(i*NV03_USER_SIZE))
|
|
+#define NV03_USER_REF_CNT__SIZE 16
|
|
+#define NV10_USER_REF_CNT__SIZE 32
|
|
+
|
|
+#define NV40_USER(i) (0x00c00000+(i*NV40_USER_SIZE))
|
|
+#define NV40_USER_SIZE 0x00001000
|
|
+#define NV40_USER_DMA_PUT(i) (0x00c00040+(i*NV40_USER_SIZE))
|
|
+#define NV40_USER_DMA_PUT__SIZE 32
|
|
+#define NV40_USER_DMA_GET(i) (0x00c00044+(i*NV40_USER_SIZE))
|
|
+#define NV40_USER_DMA_GET__SIZE 32
|
|
+#define NV40_USER_REF_CNT(i) (0x00c00048+(i*NV40_USER_SIZE))
|
|
+#define NV40_USER_REF_CNT__SIZE 32
|
|
+
|
|
+#define NV50_USER(i) (0x00c00000+(i*NV50_USER_SIZE))
|
|
+#define NV50_USER_SIZE 0x00002000
|
|
+#define NV50_USER_DMA_PUT(i) (0x00c00040+(i*NV50_USER_SIZE))
|
|
+#define NV50_USER_DMA_PUT__SIZE 128
|
|
+#define NV50_USER_DMA_GET(i) (0x00c00044+(i*NV50_USER_SIZE))
|
|
+#define NV50_USER_DMA_GET__SIZE 128
|
|
+#define NV50_USER_REF_CNT(i) (0x00c00048+(i*NV50_USER_SIZE))
|
|
+#define NV50_USER_REF_CNT__SIZE 128
|
|
+
|
|
+#define NV03_FIFO_SIZE 0x8000UL
|
|
+
|
|
+#define NV03_PMC_BOOT_0 0x00000000
|
|
+#define NV03_PMC_BOOT_1 0x00000004
|
|
+#define NV03_PMC_INTR_0 0x00000100
|
|
+# define NV_PMC_INTR_0_PFIFO_PENDING (1<<8)
|
|
+# define NV_PMC_INTR_0_PGRAPH_PENDING (1<<12)
|
|
+# define NV_PMC_INTR_0_NV50_I2C_PENDING (1<<21)
|
|
+# define NV_PMC_INTR_0_CRTC0_PENDING (1<<24)
|
|
+# define NV_PMC_INTR_0_CRTC1_PENDING (1<<25)
|
|
+# define NV_PMC_INTR_0_NV50_DISPLAY_PENDING (1<<26)
|
|
+# define NV_PMC_INTR_0_CRTCn_PENDING (3<<24)
|
|
+#define NV03_PMC_INTR_EN_0 0x00000140
|
|
+# define NV_PMC_INTR_EN_0_MASTER_ENABLE (1<<0)
|
|
+#define NV03_PMC_ENABLE 0x00000200
|
|
+# define NV_PMC_ENABLE_PFIFO (1<<8)
|
|
+# define NV_PMC_ENABLE_PGRAPH (1<<12)
|
|
+/* Disabling the below bit breaks newer (G7X only?) mobile chipsets,
|
|
+ * the card will hang early on in the X init process.
|
|
+ */
|
|
+# define NV_PMC_ENABLE_UNK13 (1<<13)
|
|
+#define NV40_PMC_GRAPH_UNITS 0x00001540
|
|
+#define NV40_PMC_BACKLIGHT 0x000015f0
|
|
+# define NV40_PMC_BACKLIGHT_MASK 0x001f0000
|
|
+#define NV40_PMC_1700 0x00001700
|
|
+#define NV40_PMC_1704 0x00001704
|
|
+#define NV40_PMC_1708 0x00001708
|
|
+#define NV40_PMC_170C 0x0000170C
|
|
+
|
|
+/* probably PMC ? */
|
|
+#define NV50_PUNK_BAR0_PRAMIN 0x00001700
|
|
+#define NV50_PUNK_BAR_CFG_BASE 0x00001704
|
|
+#define NV50_PUNK_BAR_CFG_BASE_VALID (1<<30)
|
|
+#define NV50_PUNK_BAR1_CTXDMA 0x00001708
|
|
+#define NV50_PUNK_BAR1_CTXDMA_VALID (1<<31)
|
|
+#define NV50_PUNK_BAR3_CTXDMA 0x0000170C
|
|
+#define NV50_PUNK_BAR3_CTXDMA_VALID (1<<31)
|
|
+#define NV50_PUNK_UNK1710 0x00001710
|
|
+
|
|
+#define NV04_PBUS_PCI_NV_1 0x00001804
|
|
+#define NV04_PBUS_PCI_NV_19 0x0000184C
|
|
+#define NV04_PBUS_PCI_NV_20 0x00001850
|
|
+# define NV04_PBUS_PCI_NV_20_ROM_SHADOW_DISABLED (0 << 0)
|
|
+# define NV04_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED (1 << 0)
|
|
+
|
|
+#define NV04_PTIMER_INTR_0 0x00009100
|
|
+#define NV04_PTIMER_INTR_EN_0 0x00009140
|
|
+#define NV04_PTIMER_NUMERATOR 0x00009200
|
|
+#define NV04_PTIMER_DENOMINATOR 0x00009210
|
|
+#define NV04_PTIMER_TIME_0 0x00009400
|
|
+#define NV04_PTIMER_TIME_1 0x00009410
|
|
+#define NV04_PTIMER_ALARM_0 0x00009420
|
|
+
|
|
+#define NV04_PFB_CFG0 0x00100200
|
|
+#define NV04_PFB_CFG1 0x00100204
|
|
+#define NV40_PFB_020C 0x0010020C
|
|
+#define NV10_PFB_TILE(i) (0x00100240 + (i*16))
|
|
+#define NV10_PFB_TILE__SIZE 8
|
|
+#define NV10_PFB_TLIMIT(i) (0x00100244 + (i*16))
|
|
+#define NV10_PFB_TSIZE(i) (0x00100248 + (i*16))
|
|
+#define NV10_PFB_TSTATUS(i) (0x0010024C + (i*16))
|
|
+#define NV10_PFB_CLOSE_PAGE2 0x0010033C
|
|
+#define NV40_PFB_TILE(i) (0x00100600 + (i*16))
|
|
+#define NV40_PFB_TILE__SIZE_0 12
|
|
+#define NV40_PFB_TILE__SIZE_1 15
|
|
+#define NV40_PFB_TLIMIT(i) (0x00100604 + (i*16))
|
|
+#define NV40_PFB_TSIZE(i) (0x00100608 + (i*16))
|
|
+#define NV40_PFB_TSTATUS(i) (0x0010060C + (i*16))
|
|
+#define NV40_PFB_UNK_800 0x00100800
|
|
+
|
|
+#define NV04_PGRAPH_DEBUG_0 0x00400080
|
|
+#define NV04_PGRAPH_DEBUG_1 0x00400084
|
|
+#define NV04_PGRAPH_DEBUG_2 0x00400088
|
|
+#define NV04_PGRAPH_DEBUG_3 0x0040008c
|
|
+#define NV10_PGRAPH_DEBUG_4 0x00400090
|
|
+#define NV03_PGRAPH_INTR 0x00400100
|
|
+#define NV03_PGRAPH_NSTATUS 0x00400104
|
|
+# define NV04_PGRAPH_NSTATUS_STATE_IN_USE (1<<11)
|
|
+# define NV04_PGRAPH_NSTATUS_INVALID_STATE (1<<12)
|
|
+# define NV04_PGRAPH_NSTATUS_BAD_ARGUMENT (1<<13)
|
|
+# define NV04_PGRAPH_NSTATUS_PROTECTION_FAULT (1<<14)
|
|
+# define NV10_PGRAPH_NSTATUS_STATE_IN_USE (1<<23)
|
|
+# define NV10_PGRAPH_NSTATUS_INVALID_STATE (1<<24)
|
|
+# define NV10_PGRAPH_NSTATUS_BAD_ARGUMENT (1<<25)
|
|
+# define NV10_PGRAPH_NSTATUS_PROTECTION_FAULT (1<<26)
|
|
+#define NV03_PGRAPH_NSOURCE 0x00400108
|
|
+# define NV03_PGRAPH_NSOURCE_NOTIFICATION (1<<0)
|
|
+# define NV03_PGRAPH_NSOURCE_DATA_ERROR (1<<1)
|
|
+# define NV03_PGRAPH_NSOURCE_PROTECTION_ERROR (1<<2)
|
|
+# define NV03_PGRAPH_NSOURCE_RANGE_EXCEPTION (1<<3)
|
|
+# define NV03_PGRAPH_NSOURCE_LIMIT_COLOR (1<<4)
|
|
+# define NV03_PGRAPH_NSOURCE_LIMIT_ZETA (1<<5)
|
|
+# define NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD (1<<6)
|
|
+# define NV03_PGRAPH_NSOURCE_DMA_R_PROTECTION (1<<7)
|
|
+# define NV03_PGRAPH_NSOURCE_DMA_W_PROTECTION (1<<8)
|
|
+# define NV03_PGRAPH_NSOURCE_FORMAT_EXCEPTION (1<<9)
|
|
+# define NV03_PGRAPH_NSOURCE_PATCH_EXCEPTION (1<<10)
|
|
+# define NV03_PGRAPH_NSOURCE_STATE_INVALID (1<<11)
|
|
+# define NV03_PGRAPH_NSOURCE_DOUBLE_NOTIFY (1<<12)
|
|
+# define NV03_PGRAPH_NSOURCE_NOTIFY_IN_USE (1<<13)
|
|
+# define NV03_PGRAPH_NSOURCE_METHOD_CNT (1<<14)
|
|
+# define NV03_PGRAPH_NSOURCE_BFR_NOTIFICATION (1<<15)
|
|
+# define NV03_PGRAPH_NSOURCE_DMA_VTX_PROTECTION (1<<16)
|
|
+# define NV03_PGRAPH_NSOURCE_DMA_WIDTH_A (1<<17)
|
|
+# define NV03_PGRAPH_NSOURCE_DMA_WIDTH_B (1<<18)
|
|
+#define NV03_PGRAPH_INTR_EN 0x00400140
|
|
+#define NV40_PGRAPH_INTR_EN 0x0040013C
|
|
+# define NV_PGRAPH_INTR_NOTIFY (1<<0)
|
|
+# define NV_PGRAPH_INTR_MISSING_HW (1<<4)
|
|
+# define NV_PGRAPH_INTR_CONTEXT_SWITCH (1<<12)
|
|
+# define NV_PGRAPH_INTR_BUFFER_NOTIFY (1<<16)
|
|
+# define NV_PGRAPH_INTR_ERROR (1<<20)
|
|
+#define NV10_PGRAPH_CTX_CONTROL 0x00400144
|
|
+#define NV10_PGRAPH_CTX_USER 0x00400148
|
|
+#define NV10_PGRAPH_CTX_SWITCH1 0x0040014C
|
|
+#define NV10_PGRAPH_CTX_SWITCH2 0x00400150
|
|
+#define NV10_PGRAPH_CTX_SWITCH3 0x00400154
|
|
+#define NV10_PGRAPH_CTX_SWITCH4 0x00400158
|
|
+#define NV10_PGRAPH_CTX_SWITCH5 0x0040015C
|
|
+#define NV04_PGRAPH_CTX_SWITCH1 0x00400160
|
|
+#define NV10_PGRAPH_CTX_CACHE1 0x00400160
|
|
+#define NV04_PGRAPH_CTX_SWITCH2 0x00400164
|
|
+#define NV04_PGRAPH_CTX_SWITCH3 0x00400168
|
|
+#define NV04_PGRAPH_CTX_SWITCH4 0x0040016C
|
|
+#define NV04_PGRAPH_CTX_CONTROL 0x00400170
|
|
+#define NV04_PGRAPH_CTX_USER 0x00400174
|
|
+#define NV04_PGRAPH_CTX_CACHE1 0x00400180
|
|
+#define NV10_PGRAPH_CTX_CACHE2 0x00400180
|
|
+#define NV03_PGRAPH_CTX_CONTROL 0x00400190
|
|
+#define NV03_PGRAPH_CTX_USER 0x00400194
|
|
+#define NV04_PGRAPH_CTX_CACHE2 0x004001A0
|
|
+#define NV10_PGRAPH_CTX_CACHE3 0x004001A0
|
|
+#define NV04_PGRAPH_CTX_CACHE3 0x004001C0
|
|
+#define NV10_PGRAPH_CTX_CACHE4 0x004001C0
|
|
+#define NV04_PGRAPH_CTX_CACHE4 0x004001E0
|
|
+#define NV10_PGRAPH_CTX_CACHE5 0x004001E0
|
|
+#define NV40_PGRAPH_CTXCTL_0304 0x00400304
|
|
+#define NV40_PGRAPH_CTXCTL_0304_XFER_CTX 0x00000001
|
|
+#define NV40_PGRAPH_CTXCTL_UCODE_STAT 0x00400308
|
|
+#define NV40_PGRAPH_CTXCTL_UCODE_STAT_IP_MASK 0xff000000
|
|
+#define NV40_PGRAPH_CTXCTL_UCODE_STAT_IP_SHIFT 24
|
|
+#define NV40_PGRAPH_CTXCTL_UCODE_STAT_OP_MASK 0x00ffffff
|
|
+#define NV40_PGRAPH_CTXCTL_0310 0x00400310
|
|
+#define NV40_PGRAPH_CTXCTL_0310_XFER_SAVE 0x00000020
|
|
+#define NV40_PGRAPH_CTXCTL_0310_XFER_LOAD 0x00000040
|
|
+#define NV40_PGRAPH_CTXCTL_030C 0x0040030c
|
|
+#define NV40_PGRAPH_CTXCTL_UCODE_INDEX 0x00400324
|
|
+#define NV40_PGRAPH_CTXCTL_UCODE_DATA 0x00400328
|
|
+#define NV40_PGRAPH_CTXCTL_CUR 0x0040032c
|
|
+#define NV40_PGRAPH_CTXCTL_CUR_LOADED 0x01000000
|
|
+#define NV40_PGRAPH_CTXCTL_CUR_INSTANCE 0x000FFFFF
|
|
+#define NV40_PGRAPH_CTXCTL_NEXT 0x00400330
|
|
+#define NV40_PGRAPH_CTXCTL_NEXT_INSTANCE 0x000fffff
|
|
+#define NV50_PGRAPH_CTXCTL_CUR 0x0040032c
|
|
+#define NV50_PGRAPH_CTXCTL_CUR_LOADED 0x80000000
|
|
+#define NV50_PGRAPH_CTXCTL_CUR_INSTANCE 0x00ffffff
|
|
+#define NV50_PGRAPH_CTXCTL_NEXT 0x00400330
|
|
+#define NV50_PGRAPH_CTXCTL_NEXT_INSTANCE 0x00ffffff
|
|
+#define NV03_PGRAPH_ABS_X_RAM 0x00400400
|
|
+#define NV03_PGRAPH_ABS_Y_RAM 0x00400480
|
|
+#define NV03_PGRAPH_X_MISC 0x00400500
|
|
+#define NV03_PGRAPH_Y_MISC 0x00400504
|
|
+#define NV04_PGRAPH_VALID1 0x00400508
|
|
+#define NV04_PGRAPH_SOURCE_COLOR 0x0040050C
|
|
+#define NV04_PGRAPH_MISC24_0 0x00400510
|
|
+#define NV03_PGRAPH_XY_LOGIC_MISC0 0x00400514
|
|
+#define NV03_PGRAPH_XY_LOGIC_MISC1 0x00400518
|
|
+#define NV03_PGRAPH_XY_LOGIC_MISC2 0x0040051C
|
|
+#define NV03_PGRAPH_XY_LOGIC_MISC3 0x00400520
|
|
+#define NV03_PGRAPH_CLIPX_0 0x00400524
|
|
+#define NV03_PGRAPH_CLIPX_1 0x00400528
|
|
+#define NV03_PGRAPH_CLIPY_0 0x0040052C
|
|
+#define NV03_PGRAPH_CLIPY_1 0x00400530
|
|
+#define NV03_PGRAPH_ABS_ICLIP_XMAX 0x00400534
|
|
+#define NV03_PGRAPH_ABS_ICLIP_YMAX 0x00400538
|
|
+#define NV03_PGRAPH_ABS_UCLIP_XMIN 0x0040053C
|
|
+#define NV03_PGRAPH_ABS_UCLIP_YMIN 0x00400540
|
|
+#define NV03_PGRAPH_ABS_UCLIP_XMAX 0x00400544
|
|
+#define NV03_PGRAPH_ABS_UCLIP_YMAX 0x00400548
|
|
+#define NV03_PGRAPH_ABS_UCLIPA_XMIN 0x00400560
|
|
+#define NV03_PGRAPH_ABS_UCLIPA_YMIN 0x00400564
|
|
+#define NV03_PGRAPH_ABS_UCLIPA_XMAX 0x00400568
|
|
+#define NV03_PGRAPH_ABS_UCLIPA_YMAX 0x0040056C
|
|
+#define NV04_PGRAPH_MISC24_1 0x00400570
|
|
+#define NV04_PGRAPH_MISC24_2 0x00400574
|
|
+#define NV04_PGRAPH_VALID2 0x00400578
|
|
+#define NV04_PGRAPH_PASSTHRU_0 0x0040057C
|
|
+#define NV04_PGRAPH_PASSTHRU_1 0x00400580
|
|
+#define NV04_PGRAPH_PASSTHRU_2 0x00400584
|
|
+#define NV10_PGRAPH_DIMX_TEXTURE 0x00400588
|
|
+#define NV10_PGRAPH_WDIMX_TEXTURE 0x0040058C
|
|
+#define NV04_PGRAPH_COMBINE_0_ALPHA 0x00400590
|
|
+#define NV04_PGRAPH_COMBINE_0_COLOR 0x00400594
|
|
+#define NV04_PGRAPH_COMBINE_1_ALPHA 0x00400598
|
|
+#define NV04_PGRAPH_COMBINE_1_COLOR 0x0040059C
|
|
+#define NV04_PGRAPH_FORMAT_0 0x004005A8
|
|
+#define NV04_PGRAPH_FORMAT_1 0x004005AC
|
|
+#define NV04_PGRAPH_FILTER_0 0x004005B0
|
|
+#define NV04_PGRAPH_FILTER_1 0x004005B4
|
|
+#define NV03_PGRAPH_MONO_COLOR0 0x00400600
|
|
+#define NV04_PGRAPH_ROP3 0x00400604
|
|
+#define NV04_PGRAPH_BETA_AND 0x00400608
|
|
+#define NV04_PGRAPH_BETA_PREMULT 0x0040060C
|
|
+#define NV04_PGRAPH_LIMIT_VIOL_PIX 0x00400610
|
|
+#define NV04_PGRAPH_FORMATS 0x00400618
|
|
+#define NV10_PGRAPH_DEBUG_2 0x00400620
|
|
+#define NV04_PGRAPH_BOFFSET0 0x00400640
|
|
+#define NV04_PGRAPH_BOFFSET1 0x00400644
|
|
+#define NV04_PGRAPH_BOFFSET2 0x00400648
|
|
+#define NV04_PGRAPH_BOFFSET3 0x0040064C
|
|
+#define NV04_PGRAPH_BOFFSET4 0x00400650
|
|
+#define NV04_PGRAPH_BOFFSET5 0x00400654
|
|
+#define NV04_PGRAPH_BBASE0 0x00400658
|
|
+#define NV04_PGRAPH_BBASE1 0x0040065C
|
|
+#define NV04_PGRAPH_BBASE2 0x00400660
|
|
+#define NV04_PGRAPH_BBASE3 0x00400664
|
|
+#define NV04_PGRAPH_BBASE4 0x00400668
|
|
+#define NV04_PGRAPH_BBASE5 0x0040066C
|
|
+#define NV04_PGRAPH_BPITCH0 0x00400670
|
|
+#define NV04_PGRAPH_BPITCH1 0x00400674
|
|
+#define NV04_PGRAPH_BPITCH2 0x00400678
|
|
+#define NV04_PGRAPH_BPITCH3 0x0040067C
|
|
+#define NV04_PGRAPH_BPITCH4 0x00400680
|
|
+#define NV04_PGRAPH_BLIMIT0 0x00400684
|
|
+#define NV04_PGRAPH_BLIMIT1 0x00400688
|
|
+#define NV04_PGRAPH_BLIMIT2 0x0040068C
|
|
+#define NV04_PGRAPH_BLIMIT3 0x00400690
|
|
+#define NV04_PGRAPH_BLIMIT4 0x00400694
|
|
+#define NV04_PGRAPH_BLIMIT5 0x00400698
|
|
+#define NV04_PGRAPH_BSWIZZLE2 0x0040069C
|
|
+#define NV04_PGRAPH_BSWIZZLE5 0x004006A0
|
|
+#define NV03_PGRAPH_STATUS 0x004006B0
|
|
+#define NV04_PGRAPH_STATUS 0x00400700
|
|
+#define NV04_PGRAPH_TRAPPED_ADDR 0x00400704
|
|
+#define NV04_PGRAPH_TRAPPED_DATA 0x00400708
|
|
+#define NV04_PGRAPH_SURFACE 0x0040070C
|
|
+#define NV10_PGRAPH_TRAPPED_DATA_HIGH 0x0040070C
|
|
+#define NV04_PGRAPH_STATE 0x00400710
|
|
+#define NV10_PGRAPH_SURFACE 0x00400710
|
|
+#define NV04_PGRAPH_NOTIFY 0x00400714
|
|
+#define NV10_PGRAPH_STATE 0x00400714
|
|
+#define NV10_PGRAPH_NOTIFY 0x00400718
|
|
+
|
|
+#define NV04_PGRAPH_FIFO 0x00400720
|
|
+
|
|
+#define NV04_PGRAPH_BPIXEL 0x00400724
|
|
+#define NV10_PGRAPH_RDI_INDEX 0x00400750
|
|
+#define NV04_PGRAPH_FFINTFC_ST2 0x00400754
|
|
+#define NV10_PGRAPH_RDI_DATA 0x00400754
|
|
+#define NV04_PGRAPH_DMA_PITCH 0x00400760
|
|
+#define NV10_PGRAPH_FFINTFC_ST2 0x00400764
|
|
+#define NV04_PGRAPH_DVD_COLORFMT 0x00400764
|
|
+#define NV04_PGRAPH_SCALED_FORMAT 0x00400768
|
|
+#define NV10_PGRAPH_DMA_PITCH 0x00400770
|
|
+#define NV10_PGRAPH_DVD_COLORFMT 0x00400774
|
|
+#define NV10_PGRAPH_SCALED_FORMAT 0x00400778
|
|
+#define NV20_PGRAPH_CHANNEL_CTX_TABLE 0x00400780
|
|
+#define NV20_PGRAPH_CHANNEL_CTX_POINTER 0x00400784
|
|
+#define NV20_PGRAPH_CHANNEL_CTX_XFER 0x00400788
|
|
+#define NV20_PGRAPH_CHANNEL_CTX_XFER_LOAD 0x00000001
|
|
+#define NV20_PGRAPH_CHANNEL_CTX_XFER_SAVE 0x00000002
|
|
+#define NV04_PGRAPH_PATT_COLOR0 0x00400800
|
|
+#define NV04_PGRAPH_PATT_COLOR1 0x00400804
|
|
+#define NV04_PGRAPH_PATTERN 0x00400808
|
|
+#define NV04_PGRAPH_PATTERN_SHAPE 0x00400810
|
|
+#define NV04_PGRAPH_CHROMA 0x00400814
|
|
+#define NV04_PGRAPH_CONTROL0 0x00400818
|
|
+#define NV04_PGRAPH_CONTROL1 0x0040081C
|
|
+#define NV04_PGRAPH_CONTROL2 0x00400820
|
|
+#define NV04_PGRAPH_BLEND 0x00400824
|
|
+#define NV04_PGRAPH_STORED_FMT 0x00400830
|
|
+#define NV04_PGRAPH_PATT_COLORRAM 0x00400900
|
|
+#define NV20_PGRAPH_TILE(i) (0x00400900 + (i*16))
|
|
+#define NV20_PGRAPH_TLIMIT(i) (0x00400904 + (i*16))
|
|
+#define NV20_PGRAPH_TSIZE(i) (0x00400908 + (i*16))
|
|
+#define NV20_PGRAPH_TSTATUS(i) (0x0040090C + (i*16))
|
|
+#define NV10_PGRAPH_TILE(i) (0x00400B00 + (i*16))
|
|
+#define NV10_PGRAPH_TLIMIT(i) (0x00400B04 + (i*16))
|
|
+#define NV10_PGRAPH_TSIZE(i) (0x00400B08 + (i*16))
|
|
+#define NV10_PGRAPH_TSTATUS(i) (0x00400B0C + (i*16))
|
|
+#define NV04_PGRAPH_U_RAM 0x00400D00
|
|
+#define NV47_PGRAPH_TILE(i) (0x00400D00 + (i*16))
|
|
+#define NV47_PGRAPH_TLIMIT(i) (0x00400D04 + (i*16))
|
|
+#define NV47_PGRAPH_TSIZE(i) (0x00400D08 + (i*16))
|
|
+#define NV47_PGRAPH_TSTATUS(i) (0x00400D0C + (i*16))
|
|
+#define NV04_PGRAPH_V_RAM 0x00400D40
|
|
+#define NV04_PGRAPH_W_RAM 0x00400D80
|
|
+#define NV10_PGRAPH_COMBINER0_IN_ALPHA 0x00400E40
|
|
+#define NV10_PGRAPH_COMBINER1_IN_ALPHA 0x00400E44
|
|
+#define NV10_PGRAPH_COMBINER0_IN_RGB 0x00400E48
|
|
+#define NV10_PGRAPH_COMBINER1_IN_RGB 0x00400E4C
|
|
+#define NV10_PGRAPH_COMBINER_COLOR0 0x00400E50
|
|
+#define NV10_PGRAPH_COMBINER_COLOR1 0x00400E54
|
|
+#define NV10_PGRAPH_COMBINER0_OUT_ALPHA 0x00400E58
|
|
+#define NV10_PGRAPH_COMBINER1_OUT_ALPHA 0x00400E5C
|
|
+#define NV10_PGRAPH_COMBINER0_OUT_RGB 0x00400E60
|
|
+#define NV10_PGRAPH_COMBINER1_OUT_RGB 0x00400E64
|
|
+#define NV10_PGRAPH_COMBINER_FINAL0 0x00400E68
|
|
+#define NV10_PGRAPH_COMBINER_FINAL1 0x00400E6C
|
|
+#define NV10_PGRAPH_WINDOWCLIP_HORIZONTAL 0x00400F00
|
|
+#define NV10_PGRAPH_WINDOWCLIP_VERTICAL 0x00400F20
|
|
+#define NV10_PGRAPH_XFMODE0 0x00400F40
|
|
+#define NV10_PGRAPH_XFMODE1 0x00400F44
|
|
+#define NV10_PGRAPH_GLOBALSTATE0 0x00400F48
|
|
+#define NV10_PGRAPH_GLOBALSTATE1 0x00400F4C
|
|
+#define NV10_PGRAPH_PIPE_ADDRESS 0x00400F50
|
|
+#define NV10_PGRAPH_PIPE_DATA 0x00400F54
|
|
+#define NV04_PGRAPH_DMA_START_0 0x00401000
|
|
+#define NV04_PGRAPH_DMA_START_1 0x00401004
|
|
+#define NV04_PGRAPH_DMA_LENGTH 0x00401008
|
|
+#define NV04_PGRAPH_DMA_MISC 0x0040100C
|
|
+#define NV04_PGRAPH_DMA_DATA_0 0x00401020
|
|
+#define NV04_PGRAPH_DMA_DATA_1 0x00401024
|
|
+#define NV04_PGRAPH_DMA_RM 0x00401030
|
|
+#define NV04_PGRAPH_DMA_A_XLATE_INST 0x00401040
|
|
+#define NV04_PGRAPH_DMA_A_CONTROL 0x00401044
|
|
+#define NV04_PGRAPH_DMA_A_LIMIT 0x00401048
|
|
+#define NV04_PGRAPH_DMA_A_TLB_PTE 0x0040104C
|
|
+#define NV04_PGRAPH_DMA_A_TLB_TAG 0x00401050
|
|
+#define NV04_PGRAPH_DMA_A_ADJ_OFFSET 0x00401054
|
|
+#define NV04_PGRAPH_DMA_A_OFFSET 0x00401058
|
|
+#define NV04_PGRAPH_DMA_A_SIZE 0x0040105C
|
|
+#define NV04_PGRAPH_DMA_A_Y_SIZE 0x00401060
|
|
+#define NV04_PGRAPH_DMA_B_XLATE_INST 0x00401080
|
|
+#define NV04_PGRAPH_DMA_B_CONTROL 0x00401084
|
|
+#define NV04_PGRAPH_DMA_B_LIMIT 0x00401088
|
|
+#define NV04_PGRAPH_DMA_B_TLB_PTE 0x0040108C
|
|
+#define NV04_PGRAPH_DMA_B_TLB_TAG 0x00401090
|
|
+#define NV04_PGRAPH_DMA_B_ADJ_OFFSET 0x00401094
|
|
+#define NV04_PGRAPH_DMA_B_OFFSET 0x00401098
|
|
+#define NV04_PGRAPH_DMA_B_SIZE 0x0040109C
|
|
+#define NV04_PGRAPH_DMA_B_Y_SIZE 0x004010A0
|
|
+#define NV40_PGRAPH_TILE1(i) (0x00406900 + (i*16))
|
|
+#define NV40_PGRAPH_TLIMIT1(i) (0x00406904 + (i*16))
|
|
+#define NV40_PGRAPH_TSIZE1(i) (0x00406908 + (i*16))
|
|
+#define NV40_PGRAPH_TSTATUS1(i) (0x0040690C + (i*16))
|
|
+
|
|
+
|
|
+/* It's a guess that this works on NV03. Confirmed on NV04, though */
|
|
+#define NV04_PFIFO_DELAY_0 0x00002040
|
|
+#define NV04_PFIFO_DMA_TIMESLICE 0x00002044
|
|
+#define NV04_PFIFO_NEXT_CHANNEL 0x00002050
|
|
+#define NV03_PFIFO_INTR_0 0x00002100
|
|
+#define NV03_PFIFO_INTR_EN_0 0x00002140
|
|
+# define NV_PFIFO_INTR_CACHE_ERROR (1<<0)
|
|
+# define NV_PFIFO_INTR_RUNOUT (1<<4)
|
|
+# define NV_PFIFO_INTR_RUNOUT_OVERFLOW (1<<8)
|
|
+# define NV_PFIFO_INTR_DMA_PUSHER (1<<12)
|
|
+# define NV_PFIFO_INTR_DMA_PT (1<<16)
|
|
+# define NV_PFIFO_INTR_SEMAPHORE (1<<20)
|
|
+# define NV_PFIFO_INTR_ACQUIRE_TIMEOUT (1<<24)
|
|
+#define NV03_PFIFO_RAMHT 0x00002210
|
|
+#define NV03_PFIFO_RAMFC 0x00002214
|
|
+#define NV03_PFIFO_RAMRO 0x00002218
|
|
+#define NV40_PFIFO_RAMFC 0x00002220
|
|
+#define NV03_PFIFO_CACHES 0x00002500
|
|
+#define NV04_PFIFO_MODE 0x00002504
|
|
+#define NV04_PFIFO_DMA 0x00002508
|
|
+#define NV04_PFIFO_SIZE 0x0000250c
|
|
+#define NV50_PFIFO_CTX_TABLE(c) (0x2600+(c)*4)
|
|
+#define NV50_PFIFO_CTX_TABLE__SIZE 128
|
|
+#define NV50_PFIFO_CTX_TABLE_CHANNEL_ENABLED (1<<31)
|
|
+#define NV50_PFIFO_CTX_TABLE_UNK30_BAD (1<<30)
|
|
+#define NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G80 0x0FFFFFFF
|
|
+#define NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G84 0x00FFFFFF
|
|
+#define NV03_PFIFO_CACHE0_PUSH0 0x00003000
|
|
+#define NV03_PFIFO_CACHE0_PULL0 0x00003040
|
|
+#define NV04_PFIFO_CACHE0_PULL0 0x00003050
|
|
+#define NV04_PFIFO_CACHE0_PULL1 0x00003054
|
|
+#define NV03_PFIFO_CACHE1_PUSH0 0x00003200
|
|
+#define NV03_PFIFO_CACHE1_PUSH1 0x00003204
|
|
+#define NV03_PFIFO_CACHE1_PUSH1_DMA (1<<8)
|
|
+#define NV40_PFIFO_CACHE1_PUSH1_DMA (1<<16)
|
|
+#define NV03_PFIFO_CACHE1_PUSH1_CHID_MASK 0x0000000f
|
|
+#define NV10_PFIFO_CACHE1_PUSH1_CHID_MASK 0x0000001f
|
|
+#define NV50_PFIFO_CACHE1_PUSH1_CHID_MASK 0x0000007f
|
|
+#define NV03_PFIFO_CACHE1_PUT 0x00003210
|
|
+#define NV04_PFIFO_CACHE1_DMA_PUSH 0x00003220
|
|
+#define NV04_PFIFO_CACHE1_DMA_FETCH 0x00003224
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_8_BYTES 0x00000000
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_16_BYTES 0x00000008
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_24_BYTES 0x00000010
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_32_BYTES 0x00000018
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_40_BYTES 0x00000020
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_48_BYTES 0x00000028
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_56_BYTES 0x00000030
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_64_BYTES 0x00000038
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_72_BYTES 0x00000040
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_80_BYTES 0x00000048
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_88_BYTES 0x00000050
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_96_BYTES 0x00000058
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_104_BYTES 0x00000060
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_112_BYTES 0x00000068
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_120_BYTES 0x00000070
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES 0x00000078
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_136_BYTES 0x00000080
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_144_BYTES 0x00000088
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_152_BYTES 0x00000090
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_160_BYTES 0x00000098
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_168_BYTES 0x000000A0
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_176_BYTES 0x000000A8
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_184_BYTES 0x000000B0
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_192_BYTES 0x000000B8
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_200_BYTES 0x000000C0
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_208_BYTES 0x000000C8
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_216_BYTES 0x000000D0
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_224_BYTES 0x000000D8
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_232_BYTES 0x000000E0
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_240_BYTES 0x000000E8
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_248_BYTES 0x000000F0
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_256_BYTES 0x000000F8
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE 0x0000E000
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_32_BYTES 0x00000000
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_64_BYTES 0x00002000
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_96_BYTES 0x00004000
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES 0x00006000
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_160_BYTES 0x00008000
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_192_BYTES 0x0000A000
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_224_BYTES 0x0000C000
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_256_BYTES 0x0000E000
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS 0x001F0000
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_0 0x00000000
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_1 0x00010000
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_2 0x00020000
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_3 0x00030000
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_4 0x00040000
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_5 0x00050000
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_6 0x00060000
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_7 0x00070000
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8 0x00080000
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_9 0x00090000
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_10 0x000A0000
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_11 0x000B0000
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_12 0x000C0000
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_13 0x000D0000
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_14 0x000E0000
|
|
+# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_15 0x000F0000
|
|
+# define NV_PFIFO_CACHE1_ENDIAN 0x80000000
|
|
+# define NV_PFIFO_CACHE1_LITTLE_ENDIAN 0x7FFFFFFF
|
|
+# define NV_PFIFO_CACHE1_BIG_ENDIAN 0x80000000
|
|
+#define NV04_PFIFO_CACHE1_DMA_STATE 0x00003228
|
|
+#define NV04_PFIFO_CACHE1_DMA_INSTANCE 0x0000322c
|
|
+#define NV04_PFIFO_CACHE1_DMA_CTL 0x00003230
|
|
+#define NV04_PFIFO_CACHE1_DMA_PUT 0x00003240
|
|
+#define NV04_PFIFO_CACHE1_DMA_GET 0x00003244
|
|
+#define NV10_PFIFO_CACHE1_REF_CNT 0x00003248
|
|
+#define NV10_PFIFO_CACHE1_DMA_SUBROUTINE 0x0000324C
|
|
+#define NV03_PFIFO_CACHE1_PULL0 0x00003240
|
|
+#define NV04_PFIFO_CACHE1_PULL0 0x00003250
|
|
+#define NV03_PFIFO_CACHE1_PULL1 0x00003250
|
|
+#define NV04_PFIFO_CACHE1_PULL1 0x00003254
|
|
+#define NV04_PFIFO_CACHE1_HASH 0x00003258
|
|
+#define NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT 0x00003260
|
|
+#define NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP 0x00003264
|
|
+#define NV10_PFIFO_CACHE1_ACQUIRE_VALUE 0x00003268
|
|
+#define NV10_PFIFO_CACHE1_SEMAPHORE 0x0000326C
|
|
+#define NV03_PFIFO_CACHE1_GET 0x00003270
|
|
+#define NV04_PFIFO_CACHE1_ENGINE 0x00003280
|
|
+#define NV04_PFIFO_CACHE1_DMA_DCOUNT 0x000032A0
|
|
+#define NV40_PFIFO_GRCTX_INSTANCE 0x000032E0
|
|
+#define NV40_PFIFO_UNK32E4 0x000032E4
|
|
+#define NV04_PFIFO_CACHE1_METHOD(i) (0x00003800+(i*8))
|
|
+#define NV04_PFIFO_CACHE1_DATA(i) (0x00003804+(i*8))
|
|
+#define NV40_PFIFO_CACHE1_METHOD(i) (0x00090000+(i*8))
|
|
+#define NV40_PFIFO_CACHE1_DATA(i) (0x00090004+(i*8))
|
|
+
|
|
+#define NV_CRTC0_INTSTAT 0x00600100
|
|
+#define NV_CRTC0_INTEN 0x00600140
|
|
+#define NV_CRTC1_INTSTAT 0x00602100
|
|
+#define NV_CRTC1_INTEN 0x00602140
|
|
+# define NV_CRTC_INTR_VBLANK (1<<0)
|
|
+
|
|
+#define NV04_PRAMIN 0x00700000
|
|
+
|
|
+/* Fifo commands. These are not regs, neither masks */
|
|
+#define NV03_FIFO_CMD_JUMP 0x20000000
|
|
+#define NV03_FIFO_CMD_JUMP_OFFSET_MASK 0x1ffffffc
|
|
+#define NV03_FIFO_CMD_REWIND (NV03_FIFO_CMD_JUMP | (0 & NV03_FIFO_CMD_JUMP_OFFSET_MASK))
|
|
+
|
|
+/* This is a partial import from rules-ng, a few things may be duplicated.
|
|
+ * Eventually we should completely import everything from rules-ng.
|
|
+ * For the moment check rules-ng for docs.
|
|
+ */
|
|
+
|
|
+#define NV50_PMC 0x00000000
|
|
+#define NV50_PMC__LEN 0x1
|
|
+#define NV50_PMC__ESIZE 0x2000
|
|
+# define NV50_PMC_BOOT_0 0x00000000
|
|
+# define NV50_PMC_BOOT_0_REVISION 0x000000ff
|
|
+# define NV50_PMC_BOOT_0_REVISION__SHIFT 0
|
|
+# define NV50_PMC_BOOT_0_ARCH 0x0ff00000
|
|
+# define NV50_PMC_BOOT_0_ARCH__SHIFT 20
|
|
+# define NV50_PMC_INTR_0 0x00000100
|
|
+# define NV50_PMC_INTR_0_PFIFO (1<<8)
|
|
+# define NV50_PMC_INTR_0_PGRAPH (1<<12)
|
|
+# define NV50_PMC_INTR_0_PTIMER (1<<20)
|
|
+# define NV50_PMC_INTR_0_HOTPLUG (1<<21)
|
|
+# define NV50_PMC_INTR_0_DISPLAY (1<<26)
|
|
+# define NV50_PMC_INTR_EN_0 0x00000140
|
|
+# define NV50_PMC_INTR_EN_0_MASTER (1<<0)
|
|
+# define NV50_PMC_INTR_EN_0_MASTER_DISABLED (0<<0)
|
|
+# define NV50_PMC_INTR_EN_0_MASTER_ENABLED (1<<0)
|
|
+# define NV50_PMC_ENABLE 0x00000200
|
|
+# define NV50_PMC_ENABLE_PFIFO (1<<8)
|
|
+# define NV50_PMC_ENABLE_PGRAPH (1<<12)
|
|
+
|
|
+#define NV50_PCONNECTOR 0x0000e000
|
|
+#define NV50_PCONNECTOR__LEN 0x1
|
|
+#define NV50_PCONNECTOR__ESIZE 0x1000
|
|
+# define NV50_PCONNECTOR_HOTPLUG_INTR 0x0000e050
|
|
+# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C0 (1<<0)
|
|
+# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C1 (1<<1)
|
|
+# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C2 (1<<2)
|
|
+# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C3 (1<<3)
|
|
+# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C0 (1<<16)
|
|
+# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C1 (1<<17)
|
|
+# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C2 (1<<18)
|
|
+# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C3 (1<<19)
|
|
+# define NV50_PCONNECTOR_HOTPLUG_CTRL 0x0000e054
|
|
+# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C0 (1<<0)
|
|
+# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C1 (1<<1)
|
|
+# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C2 (1<<2)
|
|
+# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C3 (1<<3)
|
|
+# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C0 (1<<16)
|
|
+# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C1 (1<<17)
|
|
+# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C2 (1<<18)
|
|
+# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C3 (1<<19)
|
|
+# define NV50_PCONNECTOR_HOTPLUG_STATE 0x0000e104
|
|
+# define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C0 (1<<2)
|
|
+# define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C1 (1<<6)
|
|
+# define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C2 (1<<10)
|
|
+# define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C3 (1<<14)
|
|
+# define NV50_PCONNECTOR_I2C_PORT_0 0x0000e138
|
|
+# define NV50_PCONNECTOR_I2C_PORT_1 0x0000e150
|
|
+# define NV50_PCONNECTOR_I2C_PORT_2 0x0000e168
|
|
+# define NV50_PCONNECTOR_I2C_PORT_3 0x0000e180
|
|
+# define NV50_PCONNECTOR_I2C_PORT_4 0x0000e240
|
|
+# define NV50_PCONNECTOR_I2C_PORT_5 0x0000e258
|
|
+
|
|
+#define NV50_AUXCH_DATA_OUT(i,n) ((n) * 4 + (i) * 0x50 + 0x0000e4c0)
|
|
+#define NV50_AUXCH_DATA_OUT__SIZE 4
|
|
+#define NV50_AUXCH_DATA_IN(i,n) ((n) * 4 + (i) * 0x50 + 0x0000e4d0)
|
|
+#define NV50_AUXCH_DATA_IN__SIZE 4
|
|
+#define NV50_AUXCH_ADDR(i) ((i) * 0x50 + 0x0000e4e0)
|
|
+#define NV50_AUXCH_CTRL(i) ((i) * 0x50 + 0x0000e4e4)
|
|
+#define NV50_AUXCH_CTRL_LINKSTAT 0x01000000
|
|
+#define NV50_AUXCH_CTRL_LINKSTAT_NOT_READY 0x00000000
|
|
+#define NV50_AUXCH_CTRL_LINKSTAT_READY 0x01000000
|
|
+#define NV50_AUXCH_CTRL_LINKEN 0x00100000
|
|
+#define NV50_AUXCH_CTRL_LINKEN_DISABLED 0x00000000
|
|
+#define NV50_AUXCH_CTRL_LINKEN_ENABLED 0x00100000
|
|
+#define NV50_AUXCH_CTRL_EXEC 0x00010000
|
|
+#define NV50_AUXCH_CTRL_EXEC_COMPLETE 0x00000000
|
|
+#define NV50_AUXCH_CTRL_EXEC_IN_PROCESS 0x00010000
|
|
+#define NV50_AUXCH_CTRL_CMD 0x0000f000
|
|
+#define NV50_AUXCH_CTRL_CMD_SHIFT 12
|
|
+#define NV50_AUXCH_CTRL_LEN 0x0000000f
|
|
+#define NV50_AUXCH_CTRL_LEN_SHIFT 0
|
|
+#define NV50_AUXCH_STAT(i) ((i) * 0x50 + 0x0000e4e8)
|
|
+#define NV50_AUXCH_STAT_STATE 0x10000000
|
|
+#define NV50_AUXCH_STAT_STATE_NOT_READY 0x00000000
|
|
+#define NV50_AUXCH_STAT_STATE_READY 0x10000000
|
|
+#define NV50_AUXCH_STAT_REPLY 0x000f0000
|
|
+#define NV50_AUXCH_STAT_REPLY_AUX 0x00030000
|
|
+#define NV50_AUXCH_STAT_REPLY_AUX_ACK 0x00000000
|
|
+#define NV50_AUXCH_STAT_REPLY_AUX_NACK 0x00010000
|
|
+#define NV50_AUXCH_STAT_REPLY_AUX_DEFER 0x00020000
|
|
+#define NV50_AUXCH_STAT_REPLY_I2C 0x000c0000
|
|
+#define NV50_AUXCH_STAT_REPLY_I2C_ACK 0x00000000
|
|
+#define NV50_AUXCH_STAT_REPLY_I2C_NACK 0x00040000
|
|
+#define NV50_AUXCH_STAT_REPLY_I2C_DEFER 0x00080000
|
|
+#define NV50_AUXCH_STAT_COUNT 0x0000001f
|
|
+
|
|
+#define NV50_PBUS 0x00088000
|
|
+#define NV50_PBUS__LEN 0x1
|
|
+#define NV50_PBUS__ESIZE 0x1000
|
|
+# define NV50_PBUS_PCI_ID 0x00088000
|
|
+# define NV50_PBUS_PCI_ID_VENDOR_ID 0x0000ffff
|
|
+# define NV50_PBUS_PCI_ID_VENDOR_ID__SHIFT 0
|
|
+# define NV50_PBUS_PCI_ID_DEVICE_ID 0xffff0000
|
|
+# define NV50_PBUS_PCI_ID_DEVICE_ID__SHIFT 16
|
|
+
|
|
+#define NV50_PFB 0x00100000
|
|
+#define NV50_PFB__LEN 0x1
|
|
+#define NV50_PFB__ESIZE 0x1000
|
|
+
|
|
+#define NV50_PEXTDEV 0x00101000
|
|
+#define NV50_PEXTDEV__LEN 0x1
|
|
+#define NV50_PEXTDEV__ESIZE 0x1000
|
|
+
|
|
+#define NV50_PROM 0x00300000
|
|
+#define NV50_PROM__LEN 0x1
|
|
+#define NV50_PROM__ESIZE 0x10000
|
|
+
|
|
+#define NV50_PGRAPH 0x00400000
|
|
+#define NV50_PGRAPH__LEN 0x1
|
|
+#define NV50_PGRAPH__ESIZE 0x10000
|
|
+
|
|
+#define NV50_PDISPLAY 0x00610000
|
|
+#define NV50_PDISPLAY_OBJECTS 0x00610010
|
|
+#define NV50_PDISPLAY_INTR_0 0x00610020
|
|
+#define NV50_PDISPLAY_INTR_1 0x00610024
|
|
+#define NV50_PDISPLAY_INTR_1_VBLANK_CRTC 0x0000000c
|
|
+#define NV50_PDISPLAY_INTR_1_VBLANK_CRTC_SHIFT 2
|
|
+#define NV50_PDISPLAY_INTR_1_VBLANK_CRTC_(n) (1 << ((n) + 2))
|
|
+#define NV50_PDISPLAY_INTR_1_VBLANK_CRTC_0 0x00000004
|
|
+#define NV50_PDISPLAY_INTR_1_VBLANK_CRTC_1 0x00000008
|
|
+#define NV50_PDISPLAY_INTR_1_CLK_UNK10 0x00000010
|
|
+#define NV50_PDISPLAY_INTR_1_CLK_UNK20 0x00000020
|
|
+#define NV50_PDISPLAY_INTR_1_CLK_UNK40 0x00000040
|
|
+#define NV50_PDISPLAY_INTR_EN 0x0061002c
|
|
+#define NV50_PDISPLAY_INTR_EN_VBLANK_CRTC 0x0000000c
|
|
+#define NV50_PDISPLAY_INTR_EN_VBLANK_CRTC_(n) (1 << ((n) + 2))
|
|
+#define NV50_PDISPLAY_INTR_EN_VBLANK_CRTC_0 0x00000004
|
|
+#define NV50_PDISPLAY_INTR_EN_VBLANK_CRTC_1 0x00000008
|
|
+#define NV50_PDISPLAY_INTR_EN_CLK_UNK10 0x00000010
|
|
+#define NV50_PDISPLAY_INTR_EN_CLK_UNK20 0x00000020
|
|
+#define NV50_PDISPLAY_INTR_EN_CLK_UNK40 0x00000040
|
|
+#define NV50_PDISPLAY_UNK30_CTRL 0x00610030
|
|
+#define NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK0 0x00000200
|
|
+#define NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK1 0x00000400
|
|
+#define NV50_PDISPLAY_UNK30_CTRL_PENDING 0x80000000
|
|
+#define NV50_PDISPLAY_TRAPPED_ADDR 0x00610080
|
|
+#define NV50_PDISPLAY_TRAPPED_DATA 0x00610084
|
|
+#define NV50_PDISPLAY_CHANNEL_STAT(i) ((i) * 0x10 + 0x00610200)
|
|
+#define NV50_PDISPLAY_CHANNEL_STAT_DMA 0x00000010
|
|
+#define NV50_PDISPLAY_CHANNEL_STAT_DMA_DISABLED 0x00000000
|
|
+#define NV50_PDISPLAY_CHANNEL_STAT_DMA_ENABLED 0x00000010
|
|
+#define NV50_PDISPLAY_CHANNEL_DMA_CB(i) ((i) * 0x10 + 0x00610204)
|
|
+#define NV50_PDISPLAY_CHANNEL_DMA_CB_LOCATION 0x00000002
|
|
+#define NV50_PDISPLAY_CHANNEL_DMA_CB_LOCATION_VRAM 0x00000000
|
|
+#define NV50_PDISPLAY_CHANNEL_DMA_CB_LOCATION_SYSTEM 0x00000002
|
|
+#define NV50_PDISPLAY_CHANNEL_DMA_CB_VALID 0x00000001
|
|
+#define NV50_PDISPLAY_CHANNEL_UNK2(i) ((i) * 0x10 + 0x00610208)
|
|
+#define NV50_PDISPLAY_CHANNEL_UNK3(i) ((i) * 0x10 + 0x0061020c)
|
|
+
|
|
+#define NV50_PDISPLAY_CURSOR 0x00610270
|
|
+#define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i) ((i) * 0x10 + 0x00610270)
|
|
+#define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_ON 0x00000001
|
|
+#define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS 0x00030000
|
|
+#define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_ACTIVE 0x00010000
|
|
+
|
|
+#define NV50_PDISPLAY_CTRL_STATE 0x00610300
|
|
+#define NV50_PDISPLAY_CTRL_STATE_PENDING 0x80000000
|
|
+#define NV50_PDISPLAY_CTRL_STATE_METHOD 0x00001ffc
|
|
+#define NV50_PDISPLAY_CTRL_STATE_ENABLE 0x00000001
|
|
+#define NV50_PDISPLAY_CTRL_VAL 0x00610304
|
|
+#define NV50_PDISPLAY_UNK_380 0x00610380
|
|
+#define NV50_PDISPLAY_RAM_AMOUNT 0x00610384
|
|
+#define NV50_PDISPLAY_UNK_388 0x00610388
|
|
+#define NV50_PDISPLAY_UNK_38C 0x0061038c
|
|
+
|
|
+#define NV50_PDISPLAY_CRTC_P(i, r) ((i) * 0x540 + NV50_PDISPLAY_CRTC_##r)
|
|
+#define NV50_PDISPLAY_CRTC_C(i, r) (4 + (i) * 0x540 + NV50_PDISPLAY_CRTC_##r)
|
|
+#define NV50_PDISPLAY_CRTC_UNK_0A18 /* mthd 0x0900 */ 0x00610a18
|
|
+#define NV50_PDISPLAY_CRTC_CLUT_MODE 0x00610a24
|
|
+#define NV50_PDISPLAY_CRTC_INTERLACE 0x00610a48
|
|
+#define NV50_PDISPLAY_CRTC_SCALE_CTRL 0x00610a50
|
|
+#define NV50_PDISPLAY_CRTC_CURSOR_CTRL 0x00610a58
|
|
+#define NV50_PDISPLAY_CRTC_UNK0A78 /* mthd 0x0904 */ 0x00610a78
|
|
+#define NV50_PDISPLAY_CRTC_UNK0AB8 0x00610ab8
|
|
+#define NV50_PDISPLAY_CRTC_DEPTH 0x00610ac8
|
|
+#define NV50_PDISPLAY_CRTC_CLOCK 0x00610ad0
|
|
+#define NV50_PDISPLAY_CRTC_COLOR_CTRL 0x00610ae0
|
|
+#define NV50_PDISPLAY_CRTC_SYNC_START_TO_BLANK_END 0x00610ae8
|
|
+#define NV50_PDISPLAY_CRTC_MODE_UNK1 0x00610af0
|
|
+#define NV50_PDISPLAY_CRTC_DISPLAY_TOTAL 0x00610af8
|
|
+#define NV50_PDISPLAY_CRTC_SYNC_DURATION 0x00610b00
|
|
+#define NV50_PDISPLAY_CRTC_MODE_UNK2 0x00610b08
|
|
+#define NV50_PDISPLAY_CRTC_UNK_0B10 /* mthd 0x0828 */ 0x00610b10
|
|
+#define NV50_PDISPLAY_CRTC_FB_SIZE 0x00610b18
|
|
+#define NV50_PDISPLAY_CRTC_FB_PITCH 0x00610b20
|
|
+#define NV50_PDISPLAY_CRTC_FB_PITCH_LINEAR 0x00100000
|
|
+#define NV50_PDISPLAY_CRTC_FB_POS 0x00610b28
|
|
+#define NV50_PDISPLAY_CRTC_SCALE_CENTER_OFFSET 0x00610b38
|
|
+#define NV50_PDISPLAY_CRTC_REAL_RES 0x00610b40
|
|
+#define NV50_PDISPLAY_CRTC_SCALE_RES1 0x00610b48
|
|
+#define NV50_PDISPLAY_CRTC_SCALE_RES2 0x00610b50
|
|
+
|
|
+#define NV50_PDISPLAY_DAC_MODE_CTRL_P(i) (0x00610b58 + (i) * 0x8)
|
|
+#define NV50_PDISPLAY_DAC_MODE_CTRL_C(i) (0x00610b5c + (i) * 0x8)
|
|
+#define NV50_PDISPLAY_SOR_MODE_CTRL_P(i) (0x00610b70 + (i) * 0x8)
|
|
+#define NV50_PDISPLAY_SOR_MODE_CTRL_C(i) (0x00610b74 + (i) * 0x8)
|
|
+#define NV50_PDISPLAY_DAC_MODE_CTRL2_P(i) (0x00610bdc + (i) * 0x8)
|
|
+#define NV50_PDISPLAY_DAC_MODE_CTRL2_C(i) (0x00610be0 + (i) * 0x8)
|
|
+
|
|
+#define NV90_PDISPLAY_SOR_MODE_CTRL_P(i) (0x00610794 + (i) * 0x8)
|
|
+#define NV90_PDISPLAY_SOR_MODE_CTRL_C(i) (0x00610798 + (i) * 0x8)
|
|
+#define NV90_PDISPLAY_DAC_MODE_CTRL_P(i) (0x00610b58 + (i) * 0x8)
|
|
+#define NV90_PDISPLAY_DAC_MODE_CTRL_C(i) (0x00610b5c + (i) * 0x8)
|
|
+#define NV90_PDISPLAY_DAC_MODE_CTRL2_P(i) (0x00610b80 + (i) * 0x8)
|
|
+#define NV90_PDISPLAY_DAC_MODE_CTRL2_C(i) (0x00610b84 + (i) * 0x8)
|
|
+
|
|
+#define NV50_PDISPLAY_CRTC_CLK 0x00614000
|
|
+#define NV50_PDISPLAY_CRTC_CLK_CTRL1(i) ((i) * 0x800 + 0x614100)
|
|
+#define NV50_PDISPLAY_CRTC_CLK_CTRL1_CONNECTED 0x00000600
|
|
+#define NV50_PDISPLAY_CRTC_CLK_VPLL_A(i) ((i) * 0x800 + 0x614104)
|
|
+#define NV50_PDISPLAY_CRTC_CLK_VPLL_B(i) ((i) * 0x800 + 0x614108)
|
|
+#define NV50_PDISPLAY_CRTC_CLK_CTRL2(i) ((i) * 0x800 + 0x614200)
|
|
+
|
|
+#define NV50_PDISPLAY_DAC_CLK 0x00614000
|
|
+#define NV50_PDISPLAY_DAC_CLK_CTRL2(i) ((i) * 0x800 + 0x614280)
|
|
+
|
|
+#define NV50_PDISPLAY_SOR_CLK 0x00614000
|
|
+#define NV50_PDISPLAY_SOR_CLK_CTRL2(i) ((i) * 0x800 + 0x614300)
|
|
+
|
|
+#define NV50_PDISPLAY_VGACRTC(r) ((r) + 0x619400)
|
|
+
|
|
+#define NV50_PDISPLAY_DAC 0x0061a000
|
|
+#define NV50_PDISPLAY_DAC_DPMS_CTRL(i) (0x0061a004 + (i) * 0x800)
|
|
+#define NV50_PDISPLAY_DAC_DPMS_CTRL_HSYNC_OFF 0x00000001
|
|
+#define NV50_PDISPLAY_DAC_DPMS_CTRL_VSYNC_OFF 0x00000004
|
|
+#define NV50_PDISPLAY_DAC_DPMS_CTRL_BLANKED 0x00000010
|
|
+#define NV50_PDISPLAY_DAC_DPMS_CTRL_OFF 0x00000040
|
|
+#define NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING 0x80000000
|
|
+#define NV50_PDISPLAY_DAC_LOAD_CTRL(i) (0x0061a00c + (i) * 0x800)
|
|
+#define NV50_PDISPLAY_DAC_LOAD_CTRL_ACTIVE 0x00100000
|
|
+#define NV50_PDISPLAY_DAC_LOAD_CTRL_PRESENT 0x38000000
|
|
+#define NV50_PDISPLAY_DAC_LOAD_CTRL_DONE 0x80000000
|
|
+#define NV50_PDISPLAY_DAC_CLK_CTRL1(i) (0x0061a010 + (i) * 0x800)
|
|
+#define NV50_PDISPLAY_DAC_CLK_CTRL1_CONNECTED 0x00000600
|
|
+
|
|
+#define NV50_PDISPLAY_SOR 0x0061c000
|
|
+#define NV50_PDISPLAY_SOR_DPMS_CTRL(i) (0x0061c004 + (i) * 0x800)
|
|
+#define NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING 0x80000000
|
|
+#define NV50_PDISPLAY_SOR_DPMS_CTRL_ON 0x00000001
|
|
+#define NV50_PDISPLAY_SOR_CLK_CTRL1(i) (0x0061c008 + (i) * 0x800)
|
|
+#define NV50_PDISPLAY_SOR_CLK_CTRL1_CONNECTED 0x00000600
|
|
+#define NV50_PDISPLAY_SOR_DPMS_STATE(i) (0x0061c030 + (i) * 0x800)
|
|
+#define NV50_PDISPLAY_SOR_DPMS_STATE_ACTIVE 0x00030000
|
|
+#define NV50_PDISPLAY_SOR_DPMS_STATE_BLANKED 0x00080000
|
|
+#define NV50_PDISPLAY_SOR_DPMS_STATE_WAIT 0x10000000
|
|
+#define NV50_PDISPLAY_SOR_BACKLIGHT 0x0061c084
|
|
+#define NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE 0x80000000
|
|
+#define NV50_PDISPLAY_SOR_BACKLIGHT_LEVEL 0x00000fff
|
|
+#define NV50_SOR_DP_CTRL(i,l) (0x0061c10c + (i) * 0x800 + (l) * 0x80)
|
|
+#define NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED 0x00004000
|
|
+#define NV50_SOR_DP_CTRL_LANE_MASK 0x001f0000
|
|
+#define NV50_SOR_DP_CTRL_LANE_0_ENABLED 0x00010000
|
|
+#define NV50_SOR_DP_CTRL_LANE_1_ENABLED 0x00020000
|
|
+#define NV50_SOR_DP_CTRL_LANE_2_ENABLED 0x00040000
|
|
+#define NV50_SOR_DP_CTRL_LANE_3_ENABLED 0x00080000
|
|
+#define NV50_SOR_DP_CTRL_TRAINING_PATTERN 0x0f000000
|
|
+#define NV50_SOR_DP_CTRL_TRAINING_PATTERN_DISABLED 0x00000000
|
|
+#define NV50_SOR_DP_CTRL_TRAINING_PATTERN_1 0x01000000
|
|
+#define NV50_SOR_DP_CTRL_TRAINING_PATTERN_2 0x02000000
|
|
+#define NV50_SOR_DP_UNK118(i,l) (0x0061c118 + (i) * 0x800 + (l) * 0x80)
|
|
+#define NV50_SOR_DP_UNK120(i,l) (0x0061c120 + (i) * 0x800 + (l) * 0x80)
|
|
+#define NV50_SOR_DP_UNK130(i,l) (0x0061c130 + (i) * 0x800 + (l) * 0x80)
|
|
+
|
|
+#define NV50_PDISPLAY_USER(i) ((i) * 0x1000 + 0x00640000)
|
|
+#define NV50_PDISPLAY_USER_PUT(i) ((i) * 0x1000 + 0x00640000)
|
|
+#define NV50_PDISPLAY_USER_GET(i) ((i) * 0x1000 + 0x00640004)
|
|
+
|
|
+#define NV50_PDISPLAY_CURSOR_USER 0x00647000
|
|
+#define NV50_PDISPLAY_CURSOR_USER_POS_CTRL(i) ((i) * 0x1000 + 0x00647080)
|
|
+#define NV50_PDISPLAY_CURSOR_USER_POS(i) ((i) * 0x1000 + 0x00647084)
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
|
|
new file mode 100644
|
|
index 0000000..ed15905
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
|
|
@@ -0,0 +1,322 @@
|
|
+#include "drmP.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include <linux/pagemap.h>
|
|
+
|
|
+#define NV_CTXDMA_PAGE_SHIFT 12
|
|
+#define NV_CTXDMA_PAGE_SIZE (1 << NV_CTXDMA_PAGE_SHIFT)
|
|
+#define NV_CTXDMA_PAGE_MASK (NV_CTXDMA_PAGE_SIZE - 1)
|
|
+
|
|
+struct nouveau_sgdma_be {
|
|
+ struct ttm_backend backend;
|
|
+ struct drm_device *dev;
|
|
+
|
|
+ dma_addr_t *pages;
|
|
+ unsigned nr_pages;
|
|
+
|
|
+ unsigned pte_start;
|
|
+ bool bound;
|
|
+};
|
|
+
|
|
+static int
|
|
+nouveau_sgdma_populate(struct ttm_backend *be, unsigned long num_pages,
|
|
+ struct page **pages, struct page *dummy_read_page)
|
|
+{
|
|
+ struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
|
|
+ struct drm_device *dev = nvbe->dev;
|
|
+
|
|
+ NV_DEBUG(nvbe->dev, "num_pages = %ld\n", num_pages);
|
|
+
|
|
+ if (nvbe->pages)
|
|
+ return -EINVAL;
|
|
+
|
|
+ nvbe->pages = kmalloc(sizeof(dma_addr_t) * num_pages, GFP_KERNEL);
|
|
+ if (!nvbe->pages)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ nvbe->nr_pages = 0;
|
|
+ while (num_pages--) {
|
|
+ nvbe->pages[nvbe->nr_pages] =
|
|
+ pci_map_page(dev->pdev, pages[nvbe->nr_pages], 0,
|
|
+ PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
|
|
+ if (pci_dma_mapping_error(dev->pdev,
|
|
+ nvbe->pages[nvbe->nr_pages])) {
|
|
+ be->func->clear(be);
|
|
+ return -EFAULT;
|
|
+ }
|
|
+
|
|
+ nvbe->nr_pages++;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void
|
|
+nouveau_sgdma_clear(struct ttm_backend *be)
|
|
+{
|
|
+ struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
|
|
+ struct drm_device *dev;
|
|
+
|
|
+ if (nvbe && nvbe->pages) {
|
|
+ dev = nvbe->dev;
|
|
+ NV_DEBUG(dev, "\n");
|
|
+
|
|
+ if (nvbe->bound)
|
|
+ be->func->unbind(be);
|
|
+
|
|
+ while (nvbe->nr_pages--) {
|
|
+ pci_unmap_page(dev->pdev, nvbe->pages[nvbe->nr_pages],
|
|
+ PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
|
|
+ }
|
|
+ kfree(nvbe->pages);
|
|
+ nvbe->pages = NULL;
|
|
+ nvbe->nr_pages = 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+static inline unsigned
|
|
+nouveau_sgdma_pte(struct drm_device *dev, uint64_t offset)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ unsigned pte = (offset >> NV_CTXDMA_PAGE_SHIFT);
|
|
+
|
|
+ if (dev_priv->card_type < NV_50)
|
|
+ return pte + 2;
|
|
+
|
|
+ return pte << 1;
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem)
|
|
+{
|
|
+ struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
|
|
+ struct drm_device *dev = nvbe->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_gpuobj *gpuobj = dev_priv->gart_info.sg_ctxdma;
|
|
+ unsigned i, j, pte;
|
|
+
|
|
+ NV_DEBUG(dev, "pg=0x%lx\n", mem->mm_node->start);
|
|
+
|
|
+ dev_priv->engine.instmem.prepare_access(nvbe->dev, true);
|
|
+ pte = nouveau_sgdma_pte(nvbe->dev, mem->mm_node->start << PAGE_SHIFT);
|
|
+ nvbe->pte_start = pte;
|
|
+ for (i = 0; i < nvbe->nr_pages; i++) {
|
|
+ dma_addr_t dma_offset = nvbe->pages[i];
|
|
+ uint32_t offset_l = lower_32_bits(dma_offset);
|
|
+ uint32_t offset_h = upper_32_bits(dma_offset);
|
|
+
|
|
+ for (j = 0; j < PAGE_SIZE / NV_CTXDMA_PAGE_SIZE; j++) {
|
|
+ if (dev_priv->card_type < NV_50)
|
|
+ nv_wo32(dev, gpuobj, pte++, offset_l | 3);
|
|
+ else {
|
|
+ nv_wo32(dev, gpuobj, pte++, offset_l | 0x21);
|
|
+ nv_wo32(dev, gpuobj, pte++, offset_h & 0xff);
|
|
+ }
|
|
+
|
|
+ dma_offset += NV_CTXDMA_PAGE_SIZE;
|
|
+ }
|
|
+ }
|
|
+ dev_priv->engine.instmem.finish_access(nvbe->dev);
|
|
+
|
|
+ if (dev_priv->card_type == NV_50) {
|
|
+ nv_wr32(dev, 0x100c80, 0x00050001);
|
|
+ if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
|
|
+ NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
|
|
+ NV_ERROR(dev, "0x100c80 = 0x%08x\n",
|
|
+ nv_rd32(dev, 0x100c80));
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ nv_wr32(dev, 0x100c80, 0x00000001);
|
|
+ if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
|
|
+ NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
|
|
+ NV_ERROR(dev, "0x100c80 = 0x%08x\n",
|
|
+ nv_rd32(dev, 0x100c80));
|
|
+ return -EBUSY;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ nvbe->bound = true;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_sgdma_unbind(struct ttm_backend *be)
|
|
+{
|
|
+ struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
|
|
+ struct drm_device *dev = nvbe->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_gpuobj *gpuobj = dev_priv->gart_info.sg_ctxdma;
|
|
+ unsigned i, j, pte;
|
|
+
|
|
+ NV_DEBUG(dev, "\n");
|
|
+
|
|
+ if (!nvbe->bound)
|
|
+ return 0;
|
|
+
|
|
+ dev_priv->engine.instmem.prepare_access(nvbe->dev, true);
|
|
+ pte = nvbe->pte_start;
|
|
+ for (i = 0; i < nvbe->nr_pages; i++) {
|
|
+ dma_addr_t dma_offset = dev_priv->gart_info.sg_dummy_bus;
|
|
+
|
|
+ for (j = 0; j < PAGE_SIZE / NV_CTXDMA_PAGE_SIZE; j++) {
|
|
+ if (dev_priv->card_type < NV_50)
|
|
+ nv_wo32(dev, gpuobj, pte++, dma_offset | 3);
|
|
+ else {
|
|
+ nv_wo32(dev, gpuobj, pte++, dma_offset | 0x21);
|
|
+ nv_wo32(dev, gpuobj, pte++, 0x00000000);
|
|
+ }
|
|
+
|
|
+ dma_offset += NV_CTXDMA_PAGE_SIZE;
|
|
+ }
|
|
+ }
|
|
+ dev_priv->engine.instmem.finish_access(nvbe->dev);
|
|
+
|
|
+ nvbe->bound = false;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void
|
|
+nouveau_sgdma_destroy(struct ttm_backend *be)
|
|
+{
|
|
+ struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
|
|
+
|
|
+ if (be) {
|
|
+ NV_DEBUG(nvbe->dev, "\n");
|
|
+
|
|
+ if (nvbe) {
|
|
+ if (nvbe->pages)
|
|
+ be->func->clear(be);
|
|
+ kfree(nvbe);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static struct ttm_backend_func nouveau_sgdma_backend = {
|
|
+ .populate = nouveau_sgdma_populate,
|
|
+ .clear = nouveau_sgdma_clear,
|
|
+ .bind = nouveau_sgdma_bind,
|
|
+ .unbind = nouveau_sgdma_unbind,
|
|
+ .destroy = nouveau_sgdma_destroy
|
|
+};
|
|
+
|
|
+struct ttm_backend *
|
|
+nouveau_sgdma_init_ttm(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_sgdma_be *nvbe;
|
|
+
|
|
+ if (!dev_priv->gart_info.sg_ctxdma)
|
|
+ return NULL;
|
|
+
|
|
+ nvbe = kzalloc(sizeof(*nvbe), GFP_KERNEL);
|
|
+ if (!nvbe)
|
|
+ return NULL;
|
|
+
|
|
+ nvbe->dev = dev;
|
|
+
|
|
+ nvbe->backend.func = &nouveau_sgdma_backend;
|
|
+
|
|
+ return &nvbe->backend;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_sgdma_init(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_gpuobj *gpuobj = NULL;
|
|
+ uint32_t aper_size, obj_size;
|
|
+ int i, ret;
|
|
+
|
|
+ if (dev_priv->card_type < NV_50) {
|
|
+ aper_size = (64 * 1024 * 1024);
|
|
+ obj_size = (aper_size >> NV_CTXDMA_PAGE_SHIFT) * 4;
|
|
+ obj_size += 8; /* ctxdma header */
|
|
+ } else {
|
|
+ /* 1 entire VM page table */
|
|
+ aper_size = (512 * 1024 * 1024);
|
|
+ obj_size = (aper_size >> NV_CTXDMA_PAGE_SHIFT) * 8;
|
|
+ }
|
|
+
|
|
+ ret = nouveau_gpuobj_new(dev, NULL, obj_size, 16,
|
|
+ NVOBJ_FLAG_ALLOW_NO_REFS |
|
|
+ NVOBJ_FLAG_ZERO_ALLOC |
|
|
+ NVOBJ_FLAG_ZERO_FREE, &gpuobj);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Error creating sgdma object: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ dev_priv->gart_info.sg_dummy_page =
|
|
+ alloc_page(GFP_KERNEL|__GFP_DMA32);
|
|
+ set_bit(PG_locked, &dev_priv->gart_info.sg_dummy_page->flags);
|
|
+ dev_priv->gart_info.sg_dummy_bus =
|
|
+ pci_map_page(dev->pdev, dev_priv->gart_info.sg_dummy_page, 0,
|
|
+ PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
|
|
+
|
|
+ dev_priv->engine.instmem.prepare_access(dev, true);
|
|
+ if (dev_priv->card_type < NV_50) {
|
|
+ /* Maybe use NV_DMA_TARGET_AGP for PCIE? NVIDIA do this, and
|
|
+ * confirmed to work on c51. Perhaps means NV_DMA_TARGET_PCIE
|
|
+ * on those cards? */
|
|
+ nv_wo32(dev, gpuobj, 0, NV_CLASS_DMA_IN_MEMORY |
|
|
+ (1 << 12) /* PT present */ |
|
|
+ (0 << 13) /* PT *not* linear */ |
|
|
+ (NV_DMA_ACCESS_RW << 14) |
|
|
+ (NV_DMA_TARGET_PCI << 16));
|
|
+ nv_wo32(dev, gpuobj, 1, aper_size - 1);
|
|
+ for (i = 2; i < 2 + (aper_size >> 12); i++) {
|
|
+ nv_wo32(dev, gpuobj, i,
|
|
+ dev_priv->gart_info.sg_dummy_bus | 3);
|
|
+ }
|
|
+ } else {
|
|
+ for (i = 0; i < obj_size; i += 8) {
|
|
+ nv_wo32(dev, gpuobj, (i+0)/4,
|
|
+ dev_priv->gart_info.sg_dummy_bus | 0x21);
|
|
+ nv_wo32(dev, gpuobj, (i+4)/4, 0);
|
|
+ }
|
|
+ }
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+
|
|
+ dev_priv->gart_info.type = NOUVEAU_GART_SGDMA;
|
|
+ dev_priv->gart_info.aper_base = 0;
|
|
+ dev_priv->gart_info.aper_size = aper_size;
|
|
+ dev_priv->gart_info.sg_ctxdma = gpuobj;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_sgdma_takedown(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ if (dev_priv->gart_info.sg_dummy_page) {
|
|
+ pci_unmap_page(dev->pdev, dev_priv->gart_info.sg_dummy_bus,
|
|
+ NV_CTXDMA_PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
|
|
+ unlock_page(dev_priv->gart_info.sg_dummy_page);
|
|
+ __free_page(dev_priv->gart_info.sg_dummy_page);
|
|
+ dev_priv->gart_info.sg_dummy_page = NULL;
|
|
+ dev_priv->gart_info.sg_dummy_bus = 0;
|
|
+ }
|
|
+
|
|
+ nouveau_gpuobj_del(dev, &dev_priv->gart_info.sg_ctxdma);
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_sgdma_get_page(struct drm_device *dev, uint32_t offset, uint32_t *page)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_gpuobj *gpuobj = dev_priv->gart_info.sg_ctxdma;
|
|
+ struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
|
|
+ int pte;
|
|
+
|
|
+ pte = (offset >> NV_CTXDMA_PAGE_SHIFT);
|
|
+ if (dev_priv->card_type < NV_50) {
|
|
+ instmem->prepare_access(dev, false);
|
|
+ *page = nv_ro32(dev, gpuobj, (pte + 2)) & ~NV_CTXDMA_PAGE_MASK;
|
|
+ instmem->finish_access(dev);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ NV_ERROR(dev, "Unimplemented on NV50\n");
|
|
+ return -EINVAL;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
|
|
new file mode 100644
|
|
index 0000000..a4851af
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
|
|
@@ -0,0 +1,897 @@
|
|
+/*
|
|
+ * Copyright 2005 Stephane Marchesin
|
|
+ * Copyright 2008 Stuart Bennett
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the next
|
|
+ * paragraph) shall be included in all copies or substantial portions of the
|
|
+ * Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
+ * DEALINGS IN THE SOFTWARE.
|
|
+ */
|
|
+
|
|
+#include <linux/swab.h>
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "drm_sarea.h"
|
|
+#include "drm_crtc_helper.h"
|
|
+#include <linux/vgaarb.h>
|
|
+
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_drm.h"
|
|
+#include "nv50_display.h"
|
|
+
|
|
+static int nouveau_stub_init(struct drm_device *dev) { return 0; }
|
|
+static void nouveau_stub_takedown(struct drm_device *dev) {}
|
|
+
|
|
+static int nouveau_init_engine_ptrs(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_engine *engine = &dev_priv->engine;
|
|
+
|
|
+ switch (dev_priv->chipset & 0xf0) {
|
|
+ case 0x00:
|
|
+ engine->instmem.init = nv04_instmem_init;
|
|
+ engine->instmem.takedown = nv04_instmem_takedown;
|
|
+ engine->instmem.suspend = nv04_instmem_suspend;
|
|
+ engine->instmem.resume = nv04_instmem_resume;
|
|
+ engine->instmem.populate = nv04_instmem_populate;
|
|
+ engine->instmem.clear = nv04_instmem_clear;
|
|
+ engine->instmem.bind = nv04_instmem_bind;
|
|
+ engine->instmem.unbind = nv04_instmem_unbind;
|
|
+ engine->instmem.prepare_access = nv04_instmem_prepare_access;
|
|
+ engine->instmem.finish_access = nv04_instmem_finish_access;
|
|
+ engine->mc.init = nv04_mc_init;
|
|
+ engine->mc.takedown = nv04_mc_takedown;
|
|
+ engine->timer.init = nv04_timer_init;
|
|
+ engine->timer.read = nv04_timer_read;
|
|
+ engine->timer.takedown = nv04_timer_takedown;
|
|
+ engine->fb.init = nv04_fb_init;
|
|
+ engine->fb.takedown = nv04_fb_takedown;
|
|
+ engine->graph.grclass = nv04_graph_grclass;
|
|
+ engine->graph.init = nv04_graph_init;
|
|
+ engine->graph.takedown = nv04_graph_takedown;
|
|
+ engine->graph.fifo_access = nv04_graph_fifo_access;
|
|
+ engine->graph.channel = nv04_graph_channel;
|
|
+ engine->graph.create_context = nv04_graph_create_context;
|
|
+ engine->graph.destroy_context = nv04_graph_destroy_context;
|
|
+ engine->graph.load_context = nv04_graph_load_context;
|
|
+ engine->graph.unload_context = nv04_graph_unload_context;
|
|
+ engine->fifo.channels = 16;
|
|
+ engine->fifo.init = nv04_fifo_init;
|
|
+ engine->fifo.takedown = nouveau_stub_takedown;
|
|
+ engine->fifo.disable = nv04_fifo_disable;
|
|
+ engine->fifo.enable = nv04_fifo_enable;
|
|
+ engine->fifo.reassign = nv04_fifo_reassign;
|
|
+ engine->fifo.cache_flush = nv04_fifo_cache_flush;
|
|
+ engine->fifo.cache_pull = nv04_fifo_cache_pull;
|
|
+ engine->fifo.channel_id = nv04_fifo_channel_id;
|
|
+ engine->fifo.create_context = nv04_fifo_create_context;
|
|
+ engine->fifo.destroy_context = nv04_fifo_destroy_context;
|
|
+ engine->fifo.load_context = nv04_fifo_load_context;
|
|
+ engine->fifo.unload_context = nv04_fifo_unload_context;
|
|
+ break;
|
|
+ case 0x10:
|
|
+ engine->instmem.init = nv04_instmem_init;
|
|
+ engine->instmem.takedown = nv04_instmem_takedown;
|
|
+ engine->instmem.suspend = nv04_instmem_suspend;
|
|
+ engine->instmem.resume = nv04_instmem_resume;
|
|
+ engine->instmem.populate = nv04_instmem_populate;
|
|
+ engine->instmem.clear = nv04_instmem_clear;
|
|
+ engine->instmem.bind = nv04_instmem_bind;
|
|
+ engine->instmem.unbind = nv04_instmem_unbind;
|
|
+ engine->instmem.prepare_access = nv04_instmem_prepare_access;
|
|
+ engine->instmem.finish_access = nv04_instmem_finish_access;
|
|
+ engine->mc.init = nv04_mc_init;
|
|
+ engine->mc.takedown = nv04_mc_takedown;
|
|
+ engine->timer.init = nv04_timer_init;
|
|
+ engine->timer.read = nv04_timer_read;
|
|
+ engine->timer.takedown = nv04_timer_takedown;
|
|
+ engine->fb.init = nv10_fb_init;
|
|
+ engine->fb.takedown = nv10_fb_takedown;
|
|
+ engine->fb.set_region_tiling = nv10_fb_set_region_tiling;
|
|
+ engine->graph.grclass = nv10_graph_grclass;
|
|
+ engine->graph.init = nv10_graph_init;
|
|
+ engine->graph.takedown = nv10_graph_takedown;
|
|
+ engine->graph.channel = nv10_graph_channel;
|
|
+ engine->graph.create_context = nv10_graph_create_context;
|
|
+ engine->graph.destroy_context = nv10_graph_destroy_context;
|
|
+ engine->graph.fifo_access = nv04_graph_fifo_access;
|
|
+ engine->graph.load_context = nv10_graph_load_context;
|
|
+ engine->graph.unload_context = nv10_graph_unload_context;
|
|
+ engine->graph.set_region_tiling = nv10_graph_set_region_tiling;
|
|
+ engine->fifo.channels = 32;
|
|
+ engine->fifo.init = nv10_fifo_init;
|
|
+ engine->fifo.takedown = nouveau_stub_takedown;
|
|
+ engine->fifo.disable = nv04_fifo_disable;
|
|
+ engine->fifo.enable = nv04_fifo_enable;
|
|
+ engine->fifo.reassign = nv04_fifo_reassign;
|
|
+ engine->fifo.cache_flush = nv04_fifo_cache_flush;
|
|
+ engine->fifo.cache_pull = nv04_fifo_cache_pull;
|
|
+ engine->fifo.channel_id = nv10_fifo_channel_id;
|
|
+ engine->fifo.create_context = nv10_fifo_create_context;
|
|
+ engine->fifo.destroy_context = nv10_fifo_destroy_context;
|
|
+ engine->fifo.load_context = nv10_fifo_load_context;
|
|
+ engine->fifo.unload_context = nv10_fifo_unload_context;
|
|
+ break;
|
|
+ case 0x20:
|
|
+ engine->instmem.init = nv04_instmem_init;
|
|
+ engine->instmem.takedown = nv04_instmem_takedown;
|
|
+ engine->instmem.suspend = nv04_instmem_suspend;
|
|
+ engine->instmem.resume = nv04_instmem_resume;
|
|
+ engine->instmem.populate = nv04_instmem_populate;
|
|
+ engine->instmem.clear = nv04_instmem_clear;
|
|
+ engine->instmem.bind = nv04_instmem_bind;
|
|
+ engine->instmem.unbind = nv04_instmem_unbind;
|
|
+ engine->instmem.prepare_access = nv04_instmem_prepare_access;
|
|
+ engine->instmem.finish_access = nv04_instmem_finish_access;
|
|
+ engine->mc.init = nv04_mc_init;
|
|
+ engine->mc.takedown = nv04_mc_takedown;
|
|
+ engine->timer.init = nv04_timer_init;
|
|
+ engine->timer.read = nv04_timer_read;
|
|
+ engine->timer.takedown = nv04_timer_takedown;
|
|
+ engine->fb.init = nv10_fb_init;
|
|
+ engine->fb.takedown = nv10_fb_takedown;
|
|
+ engine->fb.set_region_tiling = nv10_fb_set_region_tiling;
|
|
+ engine->graph.grclass = nv20_graph_grclass;
|
|
+ engine->graph.init = nv20_graph_init;
|
|
+ engine->graph.takedown = nv20_graph_takedown;
|
|
+ engine->graph.channel = nv10_graph_channel;
|
|
+ engine->graph.create_context = nv20_graph_create_context;
|
|
+ engine->graph.destroy_context = nv20_graph_destroy_context;
|
|
+ engine->graph.fifo_access = nv04_graph_fifo_access;
|
|
+ engine->graph.load_context = nv20_graph_load_context;
|
|
+ engine->graph.unload_context = nv20_graph_unload_context;
|
|
+ engine->graph.set_region_tiling = nv20_graph_set_region_tiling;
|
|
+ engine->fifo.channels = 32;
|
|
+ engine->fifo.init = nv10_fifo_init;
|
|
+ engine->fifo.takedown = nouveau_stub_takedown;
|
|
+ engine->fifo.disable = nv04_fifo_disable;
|
|
+ engine->fifo.enable = nv04_fifo_enable;
|
|
+ engine->fifo.reassign = nv04_fifo_reassign;
|
|
+ engine->fifo.cache_flush = nv04_fifo_cache_flush;
|
|
+ engine->fifo.cache_pull = nv04_fifo_cache_pull;
|
|
+ engine->fifo.channel_id = nv10_fifo_channel_id;
|
|
+ engine->fifo.create_context = nv10_fifo_create_context;
|
|
+ engine->fifo.destroy_context = nv10_fifo_destroy_context;
|
|
+ engine->fifo.load_context = nv10_fifo_load_context;
|
|
+ engine->fifo.unload_context = nv10_fifo_unload_context;
|
|
+ break;
|
|
+ case 0x30:
|
|
+ engine->instmem.init = nv04_instmem_init;
|
|
+ engine->instmem.takedown = nv04_instmem_takedown;
|
|
+ engine->instmem.suspend = nv04_instmem_suspend;
|
|
+ engine->instmem.resume = nv04_instmem_resume;
|
|
+ engine->instmem.populate = nv04_instmem_populate;
|
|
+ engine->instmem.clear = nv04_instmem_clear;
|
|
+ engine->instmem.bind = nv04_instmem_bind;
|
|
+ engine->instmem.unbind = nv04_instmem_unbind;
|
|
+ engine->instmem.prepare_access = nv04_instmem_prepare_access;
|
|
+ engine->instmem.finish_access = nv04_instmem_finish_access;
|
|
+ engine->mc.init = nv04_mc_init;
|
|
+ engine->mc.takedown = nv04_mc_takedown;
|
|
+ engine->timer.init = nv04_timer_init;
|
|
+ engine->timer.read = nv04_timer_read;
|
|
+ engine->timer.takedown = nv04_timer_takedown;
|
|
+ engine->fb.init = nv10_fb_init;
|
|
+ engine->fb.takedown = nv10_fb_takedown;
|
|
+ engine->fb.set_region_tiling = nv10_fb_set_region_tiling;
|
|
+ engine->graph.grclass = nv30_graph_grclass;
|
|
+ engine->graph.init = nv30_graph_init;
|
|
+ engine->graph.takedown = nv20_graph_takedown;
|
|
+ engine->graph.fifo_access = nv04_graph_fifo_access;
|
|
+ engine->graph.channel = nv10_graph_channel;
|
|
+ engine->graph.create_context = nv20_graph_create_context;
|
|
+ engine->graph.destroy_context = nv20_graph_destroy_context;
|
|
+ engine->graph.load_context = nv20_graph_load_context;
|
|
+ engine->graph.unload_context = nv20_graph_unload_context;
|
|
+ engine->graph.set_region_tiling = nv20_graph_set_region_tiling;
|
|
+ engine->fifo.channels = 32;
|
|
+ engine->fifo.init = nv10_fifo_init;
|
|
+ engine->fifo.takedown = nouveau_stub_takedown;
|
|
+ engine->fifo.disable = nv04_fifo_disable;
|
|
+ engine->fifo.enable = nv04_fifo_enable;
|
|
+ engine->fifo.reassign = nv04_fifo_reassign;
|
|
+ engine->fifo.cache_flush = nv04_fifo_cache_flush;
|
|
+ engine->fifo.cache_pull = nv04_fifo_cache_pull;
|
|
+ engine->fifo.channel_id = nv10_fifo_channel_id;
|
|
+ engine->fifo.create_context = nv10_fifo_create_context;
|
|
+ engine->fifo.destroy_context = nv10_fifo_destroy_context;
|
|
+ engine->fifo.load_context = nv10_fifo_load_context;
|
|
+ engine->fifo.unload_context = nv10_fifo_unload_context;
|
|
+ break;
|
|
+ case 0x40:
|
|
+ case 0x60:
|
|
+ engine->instmem.init = nv04_instmem_init;
|
|
+ engine->instmem.takedown = nv04_instmem_takedown;
|
|
+ engine->instmem.suspend = nv04_instmem_suspend;
|
|
+ engine->instmem.resume = nv04_instmem_resume;
|
|
+ engine->instmem.populate = nv04_instmem_populate;
|
|
+ engine->instmem.clear = nv04_instmem_clear;
|
|
+ engine->instmem.bind = nv04_instmem_bind;
|
|
+ engine->instmem.unbind = nv04_instmem_unbind;
|
|
+ engine->instmem.prepare_access = nv04_instmem_prepare_access;
|
|
+ engine->instmem.finish_access = nv04_instmem_finish_access;
|
|
+ engine->mc.init = nv40_mc_init;
|
|
+ engine->mc.takedown = nv40_mc_takedown;
|
|
+ engine->timer.init = nv04_timer_init;
|
|
+ engine->timer.read = nv04_timer_read;
|
|
+ engine->timer.takedown = nv04_timer_takedown;
|
|
+ engine->fb.init = nv40_fb_init;
|
|
+ engine->fb.takedown = nv40_fb_takedown;
|
|
+ engine->fb.set_region_tiling = nv40_fb_set_region_tiling;
|
|
+ engine->graph.grclass = nv40_graph_grclass;
|
|
+ engine->graph.init = nv40_graph_init;
|
|
+ engine->graph.takedown = nv40_graph_takedown;
|
|
+ engine->graph.fifo_access = nv04_graph_fifo_access;
|
|
+ engine->graph.channel = nv40_graph_channel;
|
|
+ engine->graph.create_context = nv40_graph_create_context;
|
|
+ engine->graph.destroy_context = nv40_graph_destroy_context;
|
|
+ engine->graph.load_context = nv40_graph_load_context;
|
|
+ engine->graph.unload_context = nv40_graph_unload_context;
|
|
+ engine->graph.set_region_tiling = nv40_graph_set_region_tiling;
|
|
+ engine->fifo.channels = 32;
|
|
+ engine->fifo.init = nv40_fifo_init;
|
|
+ engine->fifo.takedown = nouveau_stub_takedown;
|
|
+ engine->fifo.disable = nv04_fifo_disable;
|
|
+ engine->fifo.enable = nv04_fifo_enable;
|
|
+ engine->fifo.reassign = nv04_fifo_reassign;
|
|
+ engine->fifo.cache_flush = nv04_fifo_cache_flush;
|
|
+ engine->fifo.cache_pull = nv04_fifo_cache_pull;
|
|
+ engine->fifo.channel_id = nv10_fifo_channel_id;
|
|
+ engine->fifo.create_context = nv40_fifo_create_context;
|
|
+ engine->fifo.destroy_context = nv40_fifo_destroy_context;
|
|
+ engine->fifo.load_context = nv40_fifo_load_context;
|
|
+ engine->fifo.unload_context = nv40_fifo_unload_context;
|
|
+ break;
|
|
+ case 0x50:
|
|
+ case 0x80: /* gotta love NVIDIA's consistency.. */
|
|
+ case 0x90:
|
|
+ case 0xA0:
|
|
+ engine->instmem.init = nv50_instmem_init;
|
|
+ engine->instmem.takedown = nv50_instmem_takedown;
|
|
+ engine->instmem.suspend = nv50_instmem_suspend;
|
|
+ engine->instmem.resume = nv50_instmem_resume;
|
|
+ engine->instmem.populate = nv50_instmem_populate;
|
|
+ engine->instmem.clear = nv50_instmem_clear;
|
|
+ engine->instmem.bind = nv50_instmem_bind;
|
|
+ engine->instmem.unbind = nv50_instmem_unbind;
|
|
+ engine->instmem.prepare_access = nv50_instmem_prepare_access;
|
|
+ engine->instmem.finish_access = nv50_instmem_finish_access;
|
|
+ engine->mc.init = nv50_mc_init;
|
|
+ engine->mc.takedown = nv50_mc_takedown;
|
|
+ engine->timer.init = nv04_timer_init;
|
|
+ engine->timer.read = nv04_timer_read;
|
|
+ engine->timer.takedown = nv04_timer_takedown;
|
|
+ engine->fb.init = nouveau_stub_init;
|
|
+ engine->fb.takedown = nouveau_stub_takedown;
|
|
+ engine->graph.grclass = nv50_graph_grclass;
|
|
+ engine->graph.init = nv50_graph_init;
|
|
+ engine->graph.takedown = nv50_graph_takedown;
|
|
+ engine->graph.fifo_access = nv50_graph_fifo_access;
|
|
+ engine->graph.channel = nv50_graph_channel;
|
|
+ engine->graph.create_context = nv50_graph_create_context;
|
|
+ engine->graph.destroy_context = nv50_graph_destroy_context;
|
|
+ engine->graph.load_context = nv50_graph_load_context;
|
|
+ engine->graph.unload_context = nv50_graph_unload_context;
|
|
+ engine->fifo.channels = 128;
|
|
+ engine->fifo.init = nv50_fifo_init;
|
|
+ engine->fifo.takedown = nv50_fifo_takedown;
|
|
+ engine->fifo.disable = nv04_fifo_disable;
|
|
+ engine->fifo.enable = nv04_fifo_enable;
|
|
+ engine->fifo.reassign = nv04_fifo_reassign;
|
|
+ engine->fifo.channel_id = nv50_fifo_channel_id;
|
|
+ engine->fifo.create_context = nv50_fifo_create_context;
|
|
+ engine->fifo.destroy_context = nv50_fifo_destroy_context;
|
|
+ engine->fifo.load_context = nv50_fifo_load_context;
|
|
+ engine->fifo.unload_context = nv50_fifo_unload_context;
|
|
+ break;
|
|
+ default:
|
|
+ NV_ERROR(dev, "NV%02x unsupported\n", dev_priv->chipset);
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static unsigned int
|
|
+nouveau_vga_set_decode(void *priv, bool state)
|
|
+{
|
|
+ struct drm_device *dev = priv;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ if (dev_priv->chipset >= 0x40)
|
|
+ nv_wr32(dev, 0x88054, state);
|
|
+ else
|
|
+ nv_wr32(dev, 0x1854, state);
|
|
+
|
|
+ if (state)
|
|
+ return VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM |
|
|
+ VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM;
|
|
+ else
|
|
+ return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM;
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_card_init_channel(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_gpuobj *gpuobj;
|
|
+ int ret;
|
|
+
|
|
+ ret = nouveau_channel_alloc(dev, &dev_priv->channel,
|
|
+ (struct drm_file *)-2,
|
|
+ NvDmaFB, NvDmaTT);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ gpuobj = NULL;
|
|
+ ret = nouveau_gpuobj_dma_new(dev_priv->channel, NV_CLASS_DMA_IN_MEMORY,
|
|
+ 0, nouveau_mem_fb_amount(dev),
|
|
+ NV_DMA_ACCESS_RW, NV_DMA_TARGET_VIDMEM,
|
|
+ &gpuobj);
|
|
+ if (ret)
|
|
+ goto out_err;
|
|
+
|
|
+ ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, NvDmaVRAM,
|
|
+ gpuobj, NULL);
|
|
+ if (ret)
|
|
+ goto out_err;
|
|
+
|
|
+ gpuobj = NULL;
|
|
+ ret = nouveau_gpuobj_gart_dma_new(dev_priv->channel, 0,
|
|
+ dev_priv->gart_info.aper_size,
|
|
+ NV_DMA_ACCESS_RW, &gpuobj, NULL);
|
|
+ if (ret)
|
|
+ goto out_err;
|
|
+
|
|
+ ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, NvDmaGART,
|
|
+ gpuobj, NULL);
|
|
+ if (ret)
|
|
+ goto out_err;
|
|
+
|
|
+ return 0;
|
|
+out_err:
|
|
+ nouveau_gpuobj_del(dev, &gpuobj);
|
|
+ nouveau_channel_free(dev_priv->channel);
|
|
+ dev_priv->channel = NULL;
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_card_init(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_engine *engine;
|
|
+ int ret;
|
|
+
|
|
+ NV_DEBUG(dev, "prev state = %d\n", dev_priv->init_state);
|
|
+
|
|
+ if (dev_priv->init_state == NOUVEAU_CARD_INIT_DONE)
|
|
+ return 0;
|
|
+
|
|
+ vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode);
|
|
+
|
|
+ /* Initialise internal driver API hooks */
|
|
+ ret = nouveau_init_engine_ptrs(dev);
|
|
+ if (ret)
|
|
+ goto out;
|
|
+ engine = &dev_priv->engine;
|
|
+ dev_priv->init_state = NOUVEAU_CARD_INIT_FAILED;
|
|
+
|
|
+ /* Parse BIOS tables / Run init tables if card not POSTed */
|
|
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
|
+ ret = nouveau_bios_init(dev);
|
|
+ if (ret)
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ ret = nouveau_gpuobj_early_init(dev);
|
|
+ if (ret)
|
|
+ goto out_bios;
|
|
+
|
|
+ /* Initialise instance memory, must happen before mem_init so we
|
|
+ * know exactly how much VRAM we're able to use for "normal"
|
|
+ * purposes.
|
|
+ */
|
|
+ ret = engine->instmem.init(dev);
|
|
+ if (ret)
|
|
+ goto out_gpuobj_early;
|
|
+
|
|
+ /* Setup the memory manager */
|
|
+ ret = nouveau_mem_init(dev);
|
|
+ if (ret)
|
|
+ goto out_instmem;
|
|
+
|
|
+ ret = nouveau_gpuobj_init(dev);
|
|
+ if (ret)
|
|
+ goto out_mem;
|
|
+
|
|
+ /* PMC */
|
|
+ ret = engine->mc.init(dev);
|
|
+ if (ret)
|
|
+ goto out_gpuobj;
|
|
+
|
|
+ /* PTIMER */
|
|
+ ret = engine->timer.init(dev);
|
|
+ if (ret)
|
|
+ goto out_mc;
|
|
+
|
|
+ /* PFB */
|
|
+ ret = engine->fb.init(dev);
|
|
+ if (ret)
|
|
+ goto out_timer;
|
|
+
|
|
+ if (nouveau_noaccel)
|
|
+ engine->graph.accel_blocked = true;
|
|
+ else {
|
|
+ /* PGRAPH */
|
|
+ ret = engine->graph.init(dev);
|
|
+ if (ret)
|
|
+ goto out_fb;
|
|
+
|
|
+ /* PFIFO */
|
|
+ ret = engine->fifo.init(dev);
|
|
+ if (ret)
|
|
+ goto out_graph;
|
|
+ }
|
|
+
|
|
+ /* this call irq_preinstall, register irq handler and
|
|
+ * call irq_postinstall
|
|
+ */
|
|
+ ret = drm_irq_install(dev);
|
|
+ if (ret)
|
|
+ goto out_fifo;
|
|
+
|
|
+ ret = drm_vblank_init(dev, 0);
|
|
+ if (ret)
|
|
+ goto out_irq;
|
|
+
|
|
+ /* what about PVIDEO/PCRTC/PRAMDAC etc? */
|
|
+
|
|
+ if (!engine->graph.accel_blocked) {
|
|
+ ret = nouveau_card_init_channel(dev);
|
|
+ if (ret)
|
|
+ goto out_irq;
|
|
+ }
|
|
+
|
|
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
|
+ if (dev_priv->card_type >= NV_50)
|
|
+ ret = nv50_display_create(dev);
|
|
+ else
|
|
+ ret = nv04_display_create(dev);
|
|
+ if (ret)
|
|
+ goto out_irq;
|
|
+ }
|
|
+
|
|
+ ret = nouveau_backlight_init(dev);
|
|
+ if (ret)
|
|
+ NV_ERROR(dev, "Error %d registering backlight\n", ret);
|
|
+
|
|
+ dev_priv->init_state = NOUVEAU_CARD_INIT_DONE;
|
|
+
|
|
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
|
|
+ drm_helper_initial_config(dev);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+out_irq:
|
|
+ drm_irq_uninstall(dev);
|
|
+out_fifo:
|
|
+ if (!nouveau_noaccel)
|
|
+ engine->fifo.takedown(dev);
|
|
+out_graph:
|
|
+ if (!nouveau_noaccel)
|
|
+ engine->graph.takedown(dev);
|
|
+out_fb:
|
|
+ engine->fb.takedown(dev);
|
|
+out_timer:
|
|
+ engine->timer.takedown(dev);
|
|
+out_mc:
|
|
+ engine->mc.takedown(dev);
|
|
+out_gpuobj:
|
|
+ nouveau_gpuobj_takedown(dev);
|
|
+out_mem:
|
|
+ nouveau_mem_close(dev);
|
|
+out_instmem:
|
|
+ engine->instmem.takedown(dev);
|
|
+out_gpuobj_early:
|
|
+ nouveau_gpuobj_late_takedown(dev);
|
|
+out_bios:
|
|
+ nouveau_bios_takedown(dev);
|
|
+out:
|
|
+ vga_client_register(dev->pdev, NULL, NULL, NULL);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void nouveau_card_takedown(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_engine *engine = &dev_priv->engine;
|
|
+
|
|
+ NV_DEBUG(dev, "prev state = %d\n", dev_priv->init_state);
|
|
+
|
|
+ if (dev_priv->init_state != NOUVEAU_CARD_INIT_DOWN) {
|
|
+ nouveau_backlight_exit(dev);
|
|
+
|
|
+ if (dev_priv->channel) {
|
|
+ nouveau_channel_free(dev_priv->channel);
|
|
+ dev_priv->channel = NULL;
|
|
+ }
|
|
+
|
|
+ if (!nouveau_noaccel) {
|
|
+ engine->fifo.takedown(dev);
|
|
+ engine->graph.takedown(dev);
|
|
+ }
|
|
+ engine->fb.takedown(dev);
|
|
+ engine->timer.takedown(dev);
|
|
+ engine->mc.takedown(dev);
|
|
+
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
+ ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_VRAM);
|
|
+ ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_TT);
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+ nouveau_sgdma_takedown(dev);
|
|
+
|
|
+ nouveau_gpuobj_takedown(dev);
|
|
+ nouveau_mem_close(dev);
|
|
+ engine->instmem.takedown(dev);
|
|
+
|
|
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
|
|
+ drm_irq_uninstall(dev);
|
|
+
|
|
+ nouveau_gpuobj_late_takedown(dev);
|
|
+ nouveau_bios_takedown(dev);
|
|
+
|
|
+ vga_client_register(dev->pdev, NULL, NULL, NULL);
|
|
+
|
|
+ dev_priv->init_state = NOUVEAU_CARD_INIT_DOWN;
|
|
+ }
|
|
+}
|
|
+
|
|
+/* here a client dies, release the stuff that was allocated for its
|
|
+ * file_priv */
|
|
+void nouveau_preclose(struct drm_device *dev, struct drm_file *file_priv)
|
|
+{
|
|
+ nouveau_channel_cleanup(dev, file_priv);
|
|
+}
|
|
+
|
|
+/* first module load, setup the mmio/fb mapping */
|
|
+/* KMS: we need mmio at load time, not when the first drm client opens. */
|
|
+int nouveau_firstopen(struct drm_device *dev)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* if we have an OF card, copy vbios to RAMIN */
|
|
+static void nouveau_OF_copy_vbios_to_ramin(struct drm_device *dev)
|
|
+{
|
|
+#if defined(__powerpc__)
|
|
+ int size, i;
|
|
+ const uint32_t *bios;
|
|
+ struct device_node *dn = pci_device_to_OF_node(dev->pdev);
|
|
+ if (!dn) {
|
|
+ NV_INFO(dev, "Unable to get the OF node\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ bios = of_get_property(dn, "NVDA,BMP", &size);
|
|
+ if (bios) {
|
|
+ for (i = 0; i < size; i += 4)
|
|
+ nv_wi32(dev, i, bios[i/4]);
|
|
+ NV_INFO(dev, "OF bios successfully copied (%d bytes)\n", size);
|
|
+ } else {
|
|
+ NV_INFO(dev, "Unable to get the OF bios\n");
|
|
+ }
|
|
+#endif
|
|
+}
|
|
+
|
|
+int nouveau_load(struct drm_device *dev, unsigned long flags)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv;
|
|
+ uint32_t reg0;
|
|
+ resource_size_t mmio_start_offs;
|
|
+
|
|
+ dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL);
|
|
+ if (!dev_priv)
|
|
+ return -ENOMEM;
|
|
+ dev->dev_private = dev_priv;
|
|
+ dev_priv->dev = dev;
|
|
+
|
|
+ dev_priv->flags = flags & NOUVEAU_FLAGS;
|
|
+ dev_priv->init_state = NOUVEAU_CARD_INIT_DOWN;
|
|
+
|
|
+ NV_DEBUG(dev, "vendor: 0x%X device: 0x%X class: 0x%X\n",
|
|
+ dev->pci_vendor, dev->pci_device, dev->pdev->class);
|
|
+
|
|
+ dev_priv->acpi_dsm = nouveau_dsm_probe(dev);
|
|
+
|
|
+ if (dev_priv->acpi_dsm)
|
|
+ nouveau_hybrid_setup(dev);
|
|
+
|
|
+ dev_priv->wq = create_workqueue("nouveau");
|
|
+ if (!dev_priv->wq)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* resource 0 is mmio regs */
|
|
+ /* resource 1 is linear FB */
|
|
+ /* resource 2 is RAMIN (mmio regs + 0x1000000) */
|
|
+ /* resource 6 is bios */
|
|
+
|
|
+ /* map the mmio regs */
|
|
+ mmio_start_offs = pci_resource_start(dev->pdev, 0);
|
|
+ dev_priv->mmio = ioremap(mmio_start_offs, 0x00800000);
|
|
+ if (!dev_priv->mmio) {
|
|
+ NV_ERROR(dev, "Unable to initialize the mmio mapping. "
|
|
+ "Please report your setup to " DRIVER_EMAIL "\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ NV_DEBUG(dev, "regs mapped ok at 0x%llx\n",
|
|
+ (unsigned long long)mmio_start_offs);
|
|
+
|
|
+#ifdef __BIG_ENDIAN
|
|
+ /* Put the card in BE mode if it's not */
|
|
+ if (nv_rd32(dev, NV03_PMC_BOOT_1))
|
|
+ nv_wr32(dev, NV03_PMC_BOOT_1, 0x00000001);
|
|
+
|
|
+ DRM_MEMORYBARRIER();
|
|
+#endif
|
|
+
|
|
+ /* Time to determine the card architecture */
|
|
+ reg0 = nv_rd32(dev, NV03_PMC_BOOT_0);
|
|
+
|
|
+ /* We're dealing with >=NV10 */
|
|
+ if ((reg0 & 0x0f000000) > 0) {
|
|
+ /* Bit 27-20 contain the architecture in hex */
|
|
+ dev_priv->chipset = (reg0 & 0xff00000) >> 20;
|
|
+ /* NV04 or NV05 */
|
|
+ } else if ((reg0 & 0xff00fff0) == 0x20004000) {
|
|
+ if (reg0 & 0x00f00000)
|
|
+ dev_priv->chipset = 0x05;
|
|
+ else
|
|
+ dev_priv->chipset = 0x04;
|
|
+ } else
|
|
+ dev_priv->chipset = 0xff;
|
|
+
|
|
+ switch (dev_priv->chipset & 0xf0) {
|
|
+ case 0x00:
|
|
+ case 0x10:
|
|
+ case 0x20:
|
|
+ case 0x30:
|
|
+ dev_priv->card_type = dev_priv->chipset & 0xf0;
|
|
+ break;
|
|
+ case 0x40:
|
|
+ case 0x60:
|
|
+ dev_priv->card_type = NV_40;
|
|
+ break;
|
|
+ case 0x50:
|
|
+ case 0x80:
|
|
+ case 0x90:
|
|
+ case 0xa0:
|
|
+ dev_priv->card_type = NV_50;
|
|
+ break;
|
|
+ default:
|
|
+ NV_INFO(dev, "Unsupported chipset 0x%08x\n", reg0);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ NV_INFO(dev, "Detected an NV%2x generation card (0x%08x)\n",
|
|
+ dev_priv->card_type, reg0);
|
|
+
|
|
+ /* map larger RAMIN aperture on NV40 cards */
|
|
+ dev_priv->ramin = NULL;
|
|
+ if (dev_priv->card_type >= NV_40) {
|
|
+ int ramin_bar = 2;
|
|
+ if (pci_resource_len(dev->pdev, ramin_bar) == 0)
|
|
+ ramin_bar = 3;
|
|
+
|
|
+ dev_priv->ramin_size = pci_resource_len(dev->pdev, ramin_bar);
|
|
+ dev_priv->ramin = ioremap(
|
|
+ pci_resource_start(dev->pdev, ramin_bar),
|
|
+ dev_priv->ramin_size);
|
|
+ if (!dev_priv->ramin) {
|
|
+ NV_ERROR(dev, "Failed to init RAMIN mapping, "
|
|
+ "limited instance memory available\n");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* On older cards (or if the above failed), create a map covering
|
|
+ * the BAR0 PRAMIN aperture */
|
|
+ if (!dev_priv->ramin) {
|
|
+ dev_priv->ramin_size = 1 * 1024 * 1024;
|
|
+ dev_priv->ramin = ioremap(mmio_start_offs + NV_RAMIN,
|
|
+ dev_priv->ramin_size);
|
|
+ if (!dev_priv->ramin) {
|
|
+ NV_ERROR(dev, "Failed to map BAR0 PRAMIN.\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ nouveau_OF_copy_vbios_to_ramin(dev);
|
|
+
|
|
+ /* Special flags */
|
|
+ if (dev->pci_device == 0x01a0)
|
|
+ dev_priv->flags |= NV_NFORCE;
|
|
+ else if (dev->pci_device == 0x01f0)
|
|
+ dev_priv->flags |= NV_NFORCE2;
|
|
+
|
|
+ /* For kernel modesetting, init card now and bring up fbcon */
|
|
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
|
+ int ret = nouveau_card_init(dev);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void nouveau_close(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ /* In the case of an error dev_priv may not be allocated yet */
|
|
+ if (dev_priv)
|
|
+ nouveau_card_takedown(dev);
|
|
+}
|
|
+
|
|
+/* KMS: we need mmio at load time, not when the first drm client opens. */
|
|
+void nouveau_lastclose(struct drm_device *dev)
|
|
+{
|
|
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
|
|
+ return;
|
|
+
|
|
+ nouveau_close(dev);
|
|
+}
|
|
+
|
|
+int nouveau_unload(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
|
+ if (dev_priv->card_type >= NV_50)
|
|
+ nv50_display_destroy(dev);
|
|
+ else
|
|
+ nv04_display_destroy(dev);
|
|
+ nouveau_close(dev);
|
|
+ }
|
|
+
|
|
+ iounmap(dev_priv->mmio);
|
|
+ iounmap(dev_priv->ramin);
|
|
+
|
|
+ kfree(dev_priv);
|
|
+ dev->dev_private = NULL;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_ioctl_card_init(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ return nouveau_card_init(dev);
|
|
+}
|
|
+
|
|
+int nouveau_ioctl_getparam(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct drm_nouveau_getparam *getparam = data;
|
|
+
|
|
+ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
|
|
+
|
|
+ switch (getparam->param) {
|
|
+ case NOUVEAU_GETPARAM_CHIPSET_ID:
|
|
+ getparam->value = dev_priv->chipset;
|
|
+ break;
|
|
+ case NOUVEAU_GETPARAM_PCI_VENDOR:
|
|
+ getparam->value = dev->pci_vendor;
|
|
+ break;
|
|
+ case NOUVEAU_GETPARAM_PCI_DEVICE:
|
|
+ getparam->value = dev->pci_device;
|
|
+ break;
|
|
+ case NOUVEAU_GETPARAM_BUS_TYPE:
|
|
+ if (drm_device_is_agp(dev))
|
|
+ getparam->value = NV_AGP;
|
|
+ else if (drm_device_is_pcie(dev))
|
|
+ getparam->value = NV_PCIE;
|
|
+ else
|
|
+ getparam->value = NV_PCI;
|
|
+ break;
|
|
+ case NOUVEAU_GETPARAM_FB_PHYSICAL:
|
|
+ getparam->value = dev_priv->fb_phys;
|
|
+ break;
|
|
+ case NOUVEAU_GETPARAM_AGP_PHYSICAL:
|
|
+ getparam->value = dev_priv->gart_info.aper_base;
|
|
+ break;
|
|
+ case NOUVEAU_GETPARAM_PCI_PHYSICAL:
|
|
+ if (dev->sg) {
|
|
+ getparam->value = (unsigned long)dev->sg->virtual;
|
|
+ } else {
|
|
+ NV_ERROR(dev, "Requested PCIGART address, "
|
|
+ "while no PCIGART was created\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ break;
|
|
+ case NOUVEAU_GETPARAM_FB_SIZE:
|
|
+ getparam->value = dev_priv->fb_available_size;
|
|
+ break;
|
|
+ case NOUVEAU_GETPARAM_AGP_SIZE:
|
|
+ getparam->value = dev_priv->gart_info.aper_size;
|
|
+ break;
|
|
+ case NOUVEAU_GETPARAM_VM_VRAM_BASE:
|
|
+ getparam->value = dev_priv->vm_vram_base;
|
|
+ break;
|
|
+ case NOUVEAU_GETPARAM_GRAPH_UNITS:
|
|
+ /* NV40 and NV50 versions are quite different, but register
|
|
+ * address is the same. User is supposed to know the card
|
|
+ * family anyway... */
|
|
+ if (dev_priv->chipset >= 0x40) {
|
|
+ getparam->value = nv_rd32(dev, NV40_PMC_GRAPH_UNITS);
|
|
+ break;
|
|
+ }
|
|
+ /* FALLTHRU */
|
|
+ default:
|
|
+ NV_ERROR(dev, "unknown parameter %lld\n", getparam->param);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_ioctl_setparam(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_nouveau_setparam *setparam = data;
|
|
+
|
|
+ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
|
|
+
|
|
+ switch (setparam->param) {
|
|
+ default:
|
|
+ NV_ERROR(dev, "unknown parameter %lld\n", setparam->param);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Wait until (value(reg) & mask) == val, up until timeout has hit */
|
|
+bool nouveau_wait_until(struct drm_device *dev, uint64_t timeout,
|
|
+ uint32_t reg, uint32_t mask, uint32_t val)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
|
|
+ uint64_t start = ptimer->read(dev);
|
|
+
|
|
+ do {
|
|
+ if ((nv_rd32(dev, reg) & mask) == val)
|
|
+ return true;
|
|
+ } while (ptimer->read(dev) - start < timeout);
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+/* Waits for PGRAPH to go completely idle */
|
|
+bool nouveau_wait_for_idle(struct drm_device *dev)
|
|
+{
|
|
+ if (!nv_wait(NV04_PGRAPH_STATUS, 0xffffffff, 0x00000000)) {
|
|
+ NV_ERROR(dev, "PGRAPH idle timed out with status 0x%08x\n",
|
|
+ nv_rd32(dev, NV04_PGRAPH_STATUS));
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_ttm.c b/drivers/gpu/drm/nouveau/nouveau_ttm.c
|
|
new file mode 100644
|
|
index 0000000..c385d50
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_ttm.c
|
|
@@ -0,0 +1,103 @@
|
|
+/*
|
|
+ * Copyright (c) 2007-2008 Tungsten Graphics, Inc., Cedar Park, TX., USA,
|
|
+ * All Rights Reserved.
|
|
+ * Copyright (c) 2009 VMware, Inc., Palo Alto, CA., USA,
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sub license,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial portions
|
|
+ * of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+
|
|
+#include "nouveau_drv.h"
|
|
+
|
|
+int
|
|
+nouveau_ttm_mmap(struct file *filp, struct vm_area_struct *vma)
|
|
+{
|
|
+ struct drm_file *file_priv = filp->private_data;
|
|
+ struct drm_nouveau_private *dev_priv =
|
|
+ file_priv->minor->dev->dev_private;
|
|
+
|
|
+ if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET))
|
|
+ return drm_mmap(filp, vma);
|
|
+
|
|
+ return ttm_bo_mmap(filp, vma, &dev_priv->ttm.bdev);
|
|
+}
|
|
+
|
|
+static int
|
|
+nouveau_ttm_mem_global_init(struct ttm_global_reference *ref)
|
|
+{
|
|
+ return ttm_mem_global_init(ref->object);
|
|
+}
|
|
+
|
|
+static void
|
|
+nouveau_ttm_mem_global_release(struct ttm_global_reference *ref)
|
|
+{
|
|
+ ttm_mem_global_release(ref->object);
|
|
+}
|
|
+
|
|
+int
|
|
+nouveau_ttm_global_init(struct drm_nouveau_private *dev_priv)
|
|
+{
|
|
+ struct ttm_global_reference *global_ref;
|
|
+ int ret;
|
|
+
|
|
+ global_ref = &dev_priv->ttm.mem_global_ref;
|
|
+ global_ref->global_type = TTM_GLOBAL_TTM_MEM;
|
|
+ global_ref->size = sizeof(struct ttm_mem_global);
|
|
+ global_ref->init = &nouveau_ttm_mem_global_init;
|
|
+ global_ref->release = &nouveau_ttm_mem_global_release;
|
|
+
|
|
+ ret = ttm_global_item_ref(global_ref);
|
|
+ if (unlikely(ret != 0)) {
|
|
+ DRM_ERROR("Failed setting up TTM memory accounting\n");
|
|
+ dev_priv->ttm.mem_global_ref.release = NULL;
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ dev_priv->ttm.bo_global_ref.mem_glob = global_ref->object;
|
|
+ global_ref = &dev_priv->ttm.bo_global_ref.ref;
|
|
+ global_ref->global_type = TTM_GLOBAL_TTM_BO;
|
|
+ global_ref->size = sizeof(struct ttm_bo_global);
|
|
+ global_ref->init = &ttm_bo_global_init;
|
|
+ global_ref->release = &ttm_bo_global_release;
|
|
+
|
|
+ ret = ttm_global_item_ref(global_ref);
|
|
+ if (unlikely(ret != 0)) {
|
|
+ DRM_ERROR("Failed setting up TTM BO subsystem\n");
|
|
+ ttm_global_item_unref(&dev_priv->ttm.mem_global_ref);
|
|
+ dev_priv->ttm.mem_global_ref.release = NULL;
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nouveau_ttm_global_release(struct drm_nouveau_private *dev_priv)
|
|
+{
|
|
+ if (dev_priv->ttm.mem_global_ref.release == NULL)
|
|
+ return;
|
|
+
|
|
+ ttm_global_item_unref(&dev_priv->ttm.bo_global_ref.ref);
|
|
+ ttm_global_item_unref(&dev_priv->ttm.mem_global_ref);
|
|
+ dev_priv->ttm.mem_global_ref.release = NULL;
|
|
+}
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c b/drivers/gpu/drm/nouveau/nv04_crtc.c
|
|
new file mode 100644
|
|
index 0000000..d2f143e
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv04_crtc.c
|
|
@@ -0,0 +1,1002 @@
|
|
+/*
|
|
+ * Copyright 1993-2003 NVIDIA, Corporation
|
|
+ * Copyright 2006 Dave Airlie
|
|
+ * Copyright 2007 Maarten Maathuis
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the next
|
|
+ * paragraph) shall be included in all copies or substantial portions of the
|
|
+ * Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
+ * DEALINGS IN THE SOFTWARE.
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm_crtc_helper.h"
|
|
+
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_encoder.h"
|
|
+#include "nouveau_connector.h"
|
|
+#include "nouveau_crtc.h"
|
|
+#include "nouveau_fb.h"
|
|
+#include "nouveau_hw.h"
|
|
+#include "nvreg.h"
|
|
+
|
|
+static int
|
|
+nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
|
|
+ struct drm_framebuffer *old_fb);
|
|
+
|
|
+static void
|
|
+crtc_wr_cio_state(struct drm_crtc *crtc, struct nv04_crtc_reg *crtcstate, int index)
|
|
+{
|
|
+ NVWriteVgaCrtc(crtc->dev, nouveau_crtc(crtc)->index, index,
|
|
+ crtcstate->CRTC[index]);
|
|
+}
|
|
+
|
|
+static void nv_crtc_set_digital_vibrance(struct drm_crtc *crtc, int level)
|
|
+{
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
|
|
+ struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
|
|
+
|
|
+ regp->CRTC[NV_CIO_CRE_CSB] = nv_crtc->saturation = level;
|
|
+ if (nv_crtc->saturation && nv_gf4_disp_arch(crtc->dev)) {
|
|
+ regp->CRTC[NV_CIO_CRE_CSB] = 0x80;
|
|
+ regp->CRTC[NV_CIO_CRE_5B] = nv_crtc->saturation << 2;
|
|
+ crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_5B);
|
|
+ }
|
|
+ crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_CSB);
|
|
+}
|
|
+
|
|
+static void nv_crtc_set_image_sharpening(struct drm_crtc *crtc, int level)
|
|
+{
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
|
|
+ struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
|
|
+
|
|
+ nv_crtc->sharpness = level;
|
|
+ if (level < 0) /* blur is in hw range 0x3f -> 0x20 */
|
|
+ level += 0x40;
|
|
+ regp->ramdac_634 = level;
|
|
+ NVWriteRAMDAC(crtc->dev, nv_crtc->index, NV_PRAMDAC_634, regp->ramdac_634);
|
|
+}
|
|
+
|
|
+#define PLLSEL_VPLL1_MASK \
|
|
+ (NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL \
|
|
+ | NV_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2)
|
|
+#define PLLSEL_VPLL2_MASK \
|
|
+ (NV_PRAMDAC_PLL_COEFF_SELECT_PLL_SOURCE_VPLL2 \
|
|
+ | NV_PRAMDAC_PLL_COEFF_SELECT_VCLK2_RATIO_DB2)
|
|
+#define PLLSEL_TV_MASK \
|
|
+ (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 \
|
|
+ | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1 \
|
|
+ | NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 \
|
|
+ | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2)
|
|
+
|
|
+/* NV4x 0x40.. pll notes:
|
|
+ * gpu pll: 0x4000 + 0x4004
|
|
+ * ?gpu? pll: 0x4008 + 0x400c
|
|
+ * vpll1: 0x4010 + 0x4014
|
|
+ * vpll2: 0x4018 + 0x401c
|
|
+ * mpll: 0x4020 + 0x4024
|
|
+ * mpll: 0x4038 + 0x403c
|
|
+ *
|
|
+ * the first register of each pair has some unknown details:
|
|
+ * bits 0-7: redirected values from elsewhere? (similar to PLL_SETUP_CONTROL?)
|
|
+ * bits 20-23: (mpll) something to do with post divider?
|
|
+ * bits 28-31: related to single stage mode? (bit 8/12)
|
|
+ */
|
|
+
|
|
+static void nv_crtc_calc_state_ext(struct drm_crtc *crtc, struct drm_display_mode * mode, int dot_clock)
|
|
+{
|
|
+ struct drm_device *dev = crtc->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+ struct nv04_mode_state *state = &dev_priv->mode_reg;
|
|
+ struct nv04_crtc_reg *regp = &state->crtc_reg[nv_crtc->index];
|
|
+ struct nouveau_pll_vals *pv = ®p->pllvals;
|
|
+ struct pll_lims pll_lim;
|
|
+
|
|
+ if (get_pll_limits(dev, nv_crtc->index ? VPLL2 : VPLL1, &pll_lim))
|
|
+ return;
|
|
+
|
|
+ /* NM2 == 0 is used to determine single stage mode on two stage plls */
|
|
+ pv->NM2 = 0;
|
|
+
|
|
+ /* for newer nv4x the blob uses only the first stage of the vpll below a
|
|
+ * certain clock. for a certain nv4b this is 150MHz. since the max
|
|
+ * output frequency of the first stage for this card is 300MHz, it is
|
|
+ * assumed the threshold is given by vco1 maxfreq/2
|
|
+ */
|
|
+ /* for early nv4x, specifically nv40 and *some* nv43 (devids 0 and 6,
|
|
+ * not 8, others unknown), the blob always uses both plls. no problem
|
|
+ * has yet been observed in allowing the use a single stage pll on all
|
|
+ * nv43 however. the behaviour of single stage use is untested on nv40
|
|
+ */
|
|
+ if (dev_priv->chipset > 0x40 && dot_clock <= (pll_lim.vco1.maxfreq / 2))
|
|
+ memset(&pll_lim.vco2, 0, sizeof(pll_lim.vco2));
|
|
+
|
|
+ if (!nouveau_calc_pll_mnp(dev, &pll_lim, dot_clock, pv))
|
|
+ return;
|
|
+
|
|
+ state->pllsel &= PLLSEL_VPLL1_MASK | PLLSEL_VPLL2_MASK | PLLSEL_TV_MASK;
|
|
+
|
|
+ /* The blob uses this always, so let's do the same */
|
|
+ if (dev_priv->card_type == NV_40)
|
|
+ state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_USE_VPLL2_TRUE;
|
|
+ /* again nv40 and some nv43 act more like nv3x as described above */
|
|
+ if (dev_priv->chipset < 0x41)
|
|
+ state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL |
|
|
+ NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL;
|
|
+ state->pllsel |= nv_crtc->index ? PLLSEL_VPLL2_MASK : PLLSEL_VPLL1_MASK;
|
|
+
|
|
+ if (pv->NM2)
|
|
+ NV_DEBUG_KMS(dev, "vpll: n1 %d n2 %d m1 %d m2 %d log2p %d\n",
|
|
+ pv->N1, pv->N2, pv->M1, pv->M2, pv->log2P);
|
|
+ else
|
|
+ NV_DEBUG_KMS(dev, "vpll: n %d m %d log2p %d\n",
|
|
+ pv->N1, pv->M1, pv->log2P);
|
|
+
|
|
+ nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.offset);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|
+{
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+ struct drm_device *dev = crtc->dev;
|
|
+ unsigned char seq1 = 0, crtc17 = 0;
|
|
+ unsigned char crtc1A;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "Setting dpms mode %d on CRTC %d\n", mode,
|
|
+ nv_crtc->index);
|
|
+
|
|
+ if (nv_crtc->last_dpms == mode) /* Don't do unnecesary mode changes. */
|
|
+ return;
|
|
+
|
|
+ nv_crtc->last_dpms = mode;
|
|
+
|
|
+ if (nv_two_heads(dev))
|
|
+ NVSetOwner(dev, nv_crtc->index);
|
|
+
|
|
+ /* nv4ref indicates these two RPC1 bits inhibit h/v sync */
|
|
+ crtc1A = NVReadVgaCrtc(dev, nv_crtc->index,
|
|
+ NV_CIO_CRE_RPC1_INDEX) & ~0xC0;
|
|
+ switch (mode) {
|
|
+ case DRM_MODE_DPMS_STANDBY:
|
|
+ /* Screen: Off; HSync: Off, VSync: On -- Not Supported */
|
|
+ seq1 = 0x20;
|
|
+ crtc17 = 0x80;
|
|
+ crtc1A |= 0x80;
|
|
+ break;
|
|
+ case DRM_MODE_DPMS_SUSPEND:
|
|
+ /* Screen: Off; HSync: On, VSync: Off -- Not Supported */
|
|
+ seq1 = 0x20;
|
|
+ crtc17 = 0x80;
|
|
+ crtc1A |= 0x40;
|
|
+ break;
|
|
+ case DRM_MODE_DPMS_OFF:
|
|
+ /* Screen: Off; HSync: Off, VSync: Off */
|
|
+ seq1 = 0x20;
|
|
+ crtc17 = 0x00;
|
|
+ crtc1A |= 0xC0;
|
|
+ break;
|
|
+ case DRM_MODE_DPMS_ON:
|
|
+ default:
|
|
+ /* Screen: On; HSync: On, VSync: On */
|
|
+ seq1 = 0x00;
|
|
+ crtc17 = 0x80;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ NVVgaSeqReset(dev, nv_crtc->index, true);
|
|
+ /* Each head has it's own sequencer, so we can turn it off when we want */
|
|
+ seq1 |= (NVReadVgaSeq(dev, nv_crtc->index, NV_VIO_SR_CLOCK_INDEX) & ~0x20);
|
|
+ NVWriteVgaSeq(dev, nv_crtc->index, NV_VIO_SR_CLOCK_INDEX, seq1);
|
|
+ crtc17 |= (NVReadVgaCrtc(dev, nv_crtc->index, NV_CIO_CR_MODE_INDEX) & ~0x80);
|
|
+ mdelay(10);
|
|
+ NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CR_MODE_INDEX, crtc17);
|
|
+ NVVgaSeqReset(dev, nv_crtc->index, false);
|
|
+
|
|
+ NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RPC1_INDEX, crtc1A);
|
|
+}
|
|
+
|
|
+static bool
|
|
+nv_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *mode,
|
|
+ struct drm_display_mode *adjusted_mode)
|
|
+{
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static void
|
|
+nv_crtc_mode_set_vga(struct drm_crtc *crtc, struct drm_display_mode *mode)
|
|
+{
|
|
+ struct drm_device *dev = crtc->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+ struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
|
|
+ struct drm_framebuffer *fb = crtc->fb;
|
|
+
|
|
+ /* Calculate our timings */
|
|
+ int horizDisplay = (mode->crtc_hdisplay >> 3) - 1;
|
|
+ int horizStart = (mode->crtc_hsync_start >> 3) - 1;
|
|
+ int horizEnd = (mode->crtc_hsync_end >> 3) - 1;
|
|
+ int horizTotal = (mode->crtc_htotal >> 3) - 5;
|
|
+ int horizBlankStart = (mode->crtc_hdisplay >> 3) - 1;
|
|
+ int horizBlankEnd = (mode->crtc_htotal >> 3) - 1;
|
|
+ int vertDisplay = mode->crtc_vdisplay - 1;
|
|
+ int vertStart = mode->crtc_vsync_start - 1;
|
|
+ int vertEnd = mode->crtc_vsync_end - 1;
|
|
+ int vertTotal = mode->crtc_vtotal - 2;
|
|
+ int vertBlankStart = mode->crtc_vdisplay - 1;
|
|
+ int vertBlankEnd = mode->crtc_vtotal - 1;
|
|
+
|
|
+ struct drm_encoder *encoder;
|
|
+ bool fp_output = false;
|
|
+
|
|
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+
|
|
+ if (encoder->crtc == crtc &&
|
|
+ (nv_encoder->dcb->type == OUTPUT_LVDS ||
|
|
+ nv_encoder->dcb->type == OUTPUT_TMDS))
|
|
+ fp_output = true;
|
|
+ }
|
|
+
|
|
+ if (fp_output) {
|
|
+ vertStart = vertTotal - 3;
|
|
+ vertEnd = vertTotal - 2;
|
|
+ vertBlankStart = vertStart;
|
|
+ horizStart = horizTotal - 5;
|
|
+ horizEnd = horizTotal - 2;
|
|
+ horizBlankEnd = horizTotal + 4;
|
|
+#if 0
|
|
+ if (dev->overlayAdaptor && dev_priv->card_type >= NV_10)
|
|
+ /* This reportedly works around some video overlay bandwidth problems */
|
|
+ horizTotal += 2;
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
|
|
+ vertTotal |= 1;
|
|
+
|
|
+#if 0
|
|
+ ErrorF("horizDisplay: 0x%X \n", horizDisplay);
|
|
+ ErrorF("horizStart: 0x%X \n", horizStart);
|
|
+ ErrorF("horizEnd: 0x%X \n", horizEnd);
|
|
+ ErrorF("horizTotal: 0x%X \n", horizTotal);
|
|
+ ErrorF("horizBlankStart: 0x%X \n", horizBlankStart);
|
|
+ ErrorF("horizBlankEnd: 0x%X \n", horizBlankEnd);
|
|
+ ErrorF("vertDisplay: 0x%X \n", vertDisplay);
|
|
+ ErrorF("vertStart: 0x%X \n", vertStart);
|
|
+ ErrorF("vertEnd: 0x%X \n", vertEnd);
|
|
+ ErrorF("vertTotal: 0x%X \n", vertTotal);
|
|
+ ErrorF("vertBlankStart: 0x%X \n", vertBlankStart);
|
|
+ ErrorF("vertBlankEnd: 0x%X \n", vertBlankEnd);
|
|
+#endif
|
|
+
|
|
+ /*
|
|
+ * compute correct Hsync & Vsync polarity
|
|
+ */
|
|
+ if ((mode->flags & (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC))
|
|
+ && (mode->flags & (DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC))) {
|
|
+
|
|
+ regp->MiscOutReg = 0x23;
|
|
+ if (mode->flags & DRM_MODE_FLAG_NHSYNC)
|
|
+ regp->MiscOutReg |= 0x40;
|
|
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
|
|
+ regp->MiscOutReg |= 0x80;
|
|
+ } else {
|
|
+ int vdisplay = mode->vdisplay;
|
|
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
|
|
+ vdisplay *= 2;
|
|
+ if (mode->vscan > 1)
|
|
+ vdisplay *= mode->vscan;
|
|
+ if (vdisplay < 400)
|
|
+ regp->MiscOutReg = 0xA3; /* +hsync -vsync */
|
|
+ else if (vdisplay < 480)
|
|
+ regp->MiscOutReg = 0x63; /* -hsync +vsync */
|
|
+ else if (vdisplay < 768)
|
|
+ regp->MiscOutReg = 0xE3; /* -hsync -vsync */
|
|
+ else
|
|
+ regp->MiscOutReg = 0x23; /* +hsync +vsync */
|
|
+ }
|
|
+
|
|
+ regp->MiscOutReg |= (mode->clock_index & 0x03) << 2;
|
|
+
|
|
+ /*
|
|
+ * Time Sequencer
|
|
+ */
|
|
+ regp->Sequencer[NV_VIO_SR_RESET_INDEX] = 0x00;
|
|
+ /* 0x20 disables the sequencer */
|
|
+ if (mode->flags & DRM_MODE_FLAG_CLKDIV2)
|
|
+ regp->Sequencer[NV_VIO_SR_CLOCK_INDEX] = 0x29;
|
|
+ else
|
|
+ regp->Sequencer[NV_VIO_SR_CLOCK_INDEX] = 0x21;
|
|
+ regp->Sequencer[NV_VIO_SR_PLANE_MASK_INDEX] = 0x0F;
|
|
+ regp->Sequencer[NV_VIO_SR_CHAR_MAP_INDEX] = 0x00;
|
|
+ regp->Sequencer[NV_VIO_SR_MEM_MODE_INDEX] = 0x0E;
|
|
+
|
|
+ /*
|
|
+ * CRTC
|
|
+ */
|
|
+ regp->CRTC[NV_CIO_CR_HDT_INDEX] = horizTotal;
|
|
+ regp->CRTC[NV_CIO_CR_HDE_INDEX] = horizDisplay;
|
|
+ regp->CRTC[NV_CIO_CR_HBS_INDEX] = horizBlankStart;
|
|
+ regp->CRTC[NV_CIO_CR_HBE_INDEX] = (1 << 7) |
|
|
+ XLATE(horizBlankEnd, 0, NV_CIO_CR_HBE_4_0);
|
|
+ regp->CRTC[NV_CIO_CR_HRS_INDEX] = horizStart;
|
|
+ regp->CRTC[NV_CIO_CR_HRE_INDEX] = XLATE(horizBlankEnd, 5, NV_CIO_CR_HRE_HBE_5) |
|
|
+ XLATE(horizEnd, 0, NV_CIO_CR_HRE_4_0);
|
|
+ regp->CRTC[NV_CIO_CR_VDT_INDEX] = vertTotal;
|
|
+ regp->CRTC[NV_CIO_CR_OVL_INDEX] = XLATE(vertStart, 9, NV_CIO_CR_OVL_VRS_9) |
|
|
+ XLATE(vertDisplay, 9, NV_CIO_CR_OVL_VDE_9) |
|
|
+ XLATE(vertTotal, 9, NV_CIO_CR_OVL_VDT_9) |
|
|
+ (1 << 4) |
|
|
+ XLATE(vertBlankStart, 8, NV_CIO_CR_OVL_VBS_8) |
|
|
+ XLATE(vertStart, 8, NV_CIO_CR_OVL_VRS_8) |
|
|
+ XLATE(vertDisplay, 8, NV_CIO_CR_OVL_VDE_8) |
|
|
+ XLATE(vertTotal, 8, NV_CIO_CR_OVL_VDT_8);
|
|
+ regp->CRTC[NV_CIO_CR_RSAL_INDEX] = 0x00;
|
|
+ regp->CRTC[NV_CIO_CR_CELL_HT_INDEX] = ((mode->flags & DRM_MODE_FLAG_DBLSCAN) ? MASK(NV_CIO_CR_CELL_HT_SCANDBL) : 0) |
|
|
+ 1 << 6 |
|
|
+ XLATE(vertBlankStart, 9, NV_CIO_CR_CELL_HT_VBS_9);
|
|
+ regp->CRTC[NV_CIO_CR_CURS_ST_INDEX] = 0x00;
|
|
+ regp->CRTC[NV_CIO_CR_CURS_END_INDEX] = 0x00;
|
|
+ regp->CRTC[NV_CIO_CR_SA_HI_INDEX] = 0x00;
|
|
+ regp->CRTC[NV_CIO_CR_SA_LO_INDEX] = 0x00;
|
|
+ regp->CRTC[NV_CIO_CR_TCOFF_HI_INDEX] = 0x00;
|
|
+ regp->CRTC[NV_CIO_CR_TCOFF_LO_INDEX] = 0x00;
|
|
+ regp->CRTC[NV_CIO_CR_VRS_INDEX] = vertStart;
|
|
+ regp->CRTC[NV_CIO_CR_VRE_INDEX] = 1 << 5 | XLATE(vertEnd, 0, NV_CIO_CR_VRE_3_0);
|
|
+ regp->CRTC[NV_CIO_CR_VDE_INDEX] = vertDisplay;
|
|
+ /* framebuffer can be larger than crtc scanout area. */
|
|
+ regp->CRTC[NV_CIO_CR_OFFSET_INDEX] = fb->pitch / 8;
|
|
+ regp->CRTC[NV_CIO_CR_ULINE_INDEX] = 0x00;
|
|
+ regp->CRTC[NV_CIO_CR_VBS_INDEX] = vertBlankStart;
|
|
+ regp->CRTC[NV_CIO_CR_VBE_INDEX] = vertBlankEnd;
|
|
+ regp->CRTC[NV_CIO_CR_MODE_INDEX] = 0x43;
|
|
+ regp->CRTC[NV_CIO_CR_LCOMP_INDEX] = 0xff;
|
|
+
|
|
+ /*
|
|
+ * Some extended CRTC registers (they are not saved with the rest of the vga regs).
|
|
+ */
|
|
+
|
|
+ /* framebuffer can be larger than crtc scanout area. */
|
|
+ regp->CRTC[NV_CIO_CRE_RPC0_INDEX] = XLATE(fb->pitch / 8, 8, NV_CIO_CRE_RPC0_OFFSET_10_8);
|
|
+ regp->CRTC[NV_CIO_CRE_RPC1_INDEX] = mode->crtc_hdisplay < 1280 ?
|
|
+ MASK(NV_CIO_CRE_RPC1_LARGE) : 0x00;
|
|
+ regp->CRTC[NV_CIO_CRE_LSR_INDEX] = XLATE(horizBlankEnd, 6, NV_CIO_CRE_LSR_HBE_6) |
|
|
+ XLATE(vertBlankStart, 10, NV_CIO_CRE_LSR_VBS_10) |
|
|
+ XLATE(vertStart, 10, NV_CIO_CRE_LSR_VRS_10) |
|
|
+ XLATE(vertDisplay, 10, NV_CIO_CRE_LSR_VDE_10) |
|
|
+ XLATE(vertTotal, 10, NV_CIO_CRE_LSR_VDT_10);
|
|
+ regp->CRTC[NV_CIO_CRE_HEB__INDEX] = XLATE(horizStart, 8, NV_CIO_CRE_HEB_HRS_8) |
|
|
+ XLATE(horizBlankStart, 8, NV_CIO_CRE_HEB_HBS_8) |
|
|
+ XLATE(horizDisplay, 8, NV_CIO_CRE_HEB_HDE_8) |
|
|
+ XLATE(horizTotal, 8, NV_CIO_CRE_HEB_HDT_8);
|
|
+ regp->CRTC[NV_CIO_CRE_EBR_INDEX] = XLATE(vertBlankStart, 11, NV_CIO_CRE_EBR_VBS_11) |
|
|
+ XLATE(vertStart, 11, NV_CIO_CRE_EBR_VRS_11) |
|
|
+ XLATE(vertDisplay, 11, NV_CIO_CRE_EBR_VDE_11) |
|
|
+ XLATE(vertTotal, 11, NV_CIO_CRE_EBR_VDT_11);
|
|
+
|
|
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
|
|
+ horizTotal = (horizTotal >> 1) & ~1;
|
|
+ regp->CRTC[NV_CIO_CRE_ILACE__INDEX] = horizTotal;
|
|
+ regp->CRTC[NV_CIO_CRE_HEB__INDEX] |= XLATE(horizTotal, 8, NV_CIO_CRE_HEB_ILC_8);
|
|
+ } else
|
|
+ regp->CRTC[NV_CIO_CRE_ILACE__INDEX] = 0xff; /* interlace off */
|
|
+
|
|
+ /*
|
|
+ * Graphics Display Controller
|
|
+ */
|
|
+ regp->Graphics[NV_VIO_GX_SR_INDEX] = 0x00;
|
|
+ regp->Graphics[NV_VIO_GX_SREN_INDEX] = 0x00;
|
|
+ regp->Graphics[NV_VIO_GX_CCOMP_INDEX] = 0x00;
|
|
+ regp->Graphics[NV_VIO_GX_ROP_INDEX] = 0x00;
|
|
+ regp->Graphics[NV_VIO_GX_READ_MAP_INDEX] = 0x00;
|
|
+ regp->Graphics[NV_VIO_GX_MODE_INDEX] = 0x40; /* 256 color mode */
|
|
+ regp->Graphics[NV_VIO_GX_MISC_INDEX] = 0x05; /* map 64k mem + graphic mode */
|
|
+ regp->Graphics[NV_VIO_GX_DONT_CARE_INDEX] = 0x0F;
|
|
+ regp->Graphics[NV_VIO_GX_BIT_MASK_INDEX] = 0xFF;
|
|
+
|
|
+ regp->Attribute[0] = 0x00; /* standard colormap translation */
|
|
+ regp->Attribute[1] = 0x01;
|
|
+ regp->Attribute[2] = 0x02;
|
|
+ regp->Attribute[3] = 0x03;
|
|
+ regp->Attribute[4] = 0x04;
|
|
+ regp->Attribute[5] = 0x05;
|
|
+ regp->Attribute[6] = 0x06;
|
|
+ regp->Attribute[7] = 0x07;
|
|
+ regp->Attribute[8] = 0x08;
|
|
+ regp->Attribute[9] = 0x09;
|
|
+ regp->Attribute[10] = 0x0A;
|
|
+ regp->Attribute[11] = 0x0B;
|
|
+ regp->Attribute[12] = 0x0C;
|
|
+ regp->Attribute[13] = 0x0D;
|
|
+ regp->Attribute[14] = 0x0E;
|
|
+ regp->Attribute[15] = 0x0F;
|
|
+ regp->Attribute[NV_CIO_AR_MODE_INDEX] = 0x01; /* Enable graphic mode */
|
|
+ /* Non-vga */
|
|
+ regp->Attribute[NV_CIO_AR_OSCAN_INDEX] = 0x00;
|
|
+ regp->Attribute[NV_CIO_AR_PLANE_INDEX] = 0x0F; /* enable all color planes */
|
|
+ regp->Attribute[NV_CIO_AR_HPP_INDEX] = 0x00;
|
|
+ regp->Attribute[NV_CIO_AR_CSEL_INDEX] = 0x00;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Sets up registers for the given mode/adjusted_mode pair.
|
|
+ *
|
|
+ * The clocks, CRTCs and outputs attached to this CRTC must be off.
|
|
+ *
|
|
+ * This shouldn't enable any clocks, CRTCs, or outputs, but they should
|
|
+ * be easily turned on/off after this.
|
|
+ */
|
|
+static void
|
|
+nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode)
|
|
+{
|
|
+ struct drm_device *dev = crtc->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+ struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
|
|
+ struct nv04_crtc_reg *savep = &dev_priv->saved_reg.crtc_reg[nv_crtc->index];
|
|
+ struct drm_encoder *encoder;
|
|
+ bool lvds_output = false, tmds_output = false, tv_output = false,
|
|
+ off_chip_digital = false;
|
|
+
|
|
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ bool digital = false;
|
|
+
|
|
+ if (encoder->crtc != crtc)
|
|
+ continue;
|
|
+
|
|
+ if (nv_encoder->dcb->type == OUTPUT_LVDS)
|
|
+ digital = lvds_output = true;
|
|
+ if (nv_encoder->dcb->type == OUTPUT_TV)
|
|
+ tv_output = true;
|
|
+ if (nv_encoder->dcb->type == OUTPUT_TMDS)
|
|
+ digital = tmds_output = true;
|
|
+ if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP && digital)
|
|
+ off_chip_digital = true;
|
|
+ }
|
|
+
|
|
+ /* Registers not directly related to the (s)vga mode */
|
|
+
|
|
+ /* What is the meaning of this register? */
|
|
+ /* A few popular values are 0x18, 0x1c, 0x38, 0x3c */
|
|
+ regp->CRTC[NV_CIO_CRE_ENH_INDEX] = savep->CRTC[NV_CIO_CRE_ENH_INDEX] & ~(1<<5);
|
|
+
|
|
+ regp->crtc_eng_ctrl = 0;
|
|
+ /* Except for rare conditions I2C is enabled on the primary crtc */
|
|
+ if (nv_crtc->index == 0)
|
|
+ regp->crtc_eng_ctrl |= NV_CRTC_FSEL_I2C;
|
|
+#if 0
|
|
+ /* Set overlay to desired crtc. */
|
|
+ if (dev->overlayAdaptor) {
|
|
+ NVPortPrivPtr pPriv = GET_OVERLAY_PRIVATE(dev);
|
|
+ if (pPriv->overlayCRTC == nv_crtc->index)
|
|
+ regp->crtc_eng_ctrl |= NV_CRTC_FSEL_OVERLAY;
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ /* ADDRESS_SPACE_PNVM is the same as setting HCUR_ASI */
|
|
+ regp->cursor_cfg = NV_PCRTC_CURSOR_CONFIG_CUR_LINES_64 |
|
|
+ NV_PCRTC_CURSOR_CONFIG_CUR_PIXELS_64 |
|
|
+ NV_PCRTC_CURSOR_CONFIG_ADDRESS_SPACE_PNVM;
|
|
+ if (dev_priv->chipset >= 0x11)
|
|
+ regp->cursor_cfg |= NV_PCRTC_CURSOR_CONFIG_CUR_BPP_32;
|
|
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
|
|
+ regp->cursor_cfg |= NV_PCRTC_CURSOR_CONFIG_DOUBLE_SCAN_ENABLE;
|
|
+
|
|
+ /* Unblock some timings */
|
|
+ regp->CRTC[NV_CIO_CRE_53] = 0;
|
|
+ regp->CRTC[NV_CIO_CRE_54] = 0;
|
|
+
|
|
+ /* 0x00 is disabled, 0x11 is lvds, 0x22 crt and 0x88 tmds */
|
|
+ if (lvds_output)
|
|
+ regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x11;
|
|
+ else if (tmds_output)
|
|
+ regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x88;
|
|
+ else
|
|
+ regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x22;
|
|
+
|
|
+ /* These values seem to vary */
|
|
+ /* This register seems to be used by the bios to make certain decisions on some G70 cards? */
|
|
+ regp->CRTC[NV_CIO_CRE_SCRATCH4__INDEX] = savep->CRTC[NV_CIO_CRE_SCRATCH4__INDEX];
|
|
+
|
|
+ nv_crtc_set_digital_vibrance(crtc, nv_crtc->saturation);
|
|
+
|
|
+ /* probably a scratch reg, but kept for cargo-cult purposes:
|
|
+ * bit0: crtc0?, head A
|
|
+ * bit6: lvds, head A
|
|
+ * bit7: (only in X), head A
|
|
+ */
|
|
+ if (nv_crtc->index == 0)
|
|
+ regp->CRTC[NV_CIO_CRE_4B] = savep->CRTC[NV_CIO_CRE_4B] | 0x80;
|
|
+
|
|
+ /* The blob seems to take the current value from crtc 0, add 4 to that
|
|
+ * and reuse the old value for crtc 1 */
|
|
+ regp->CRTC[NV_CIO_CRE_TVOUT_LATENCY] = dev_priv->saved_reg.crtc_reg[0].CRTC[NV_CIO_CRE_TVOUT_LATENCY];
|
|
+ if (!nv_crtc->index)
|
|
+ regp->CRTC[NV_CIO_CRE_TVOUT_LATENCY] += 4;
|
|
+
|
|
+ /* the blob sometimes sets |= 0x10 (which is the same as setting |=
|
|
+ * 1 << 30 on 0x60.830), for no apparent reason */
|
|
+ regp->CRTC[NV_CIO_CRE_59] = off_chip_digital;
|
|
+
|
|
+ regp->crtc_830 = mode->crtc_vdisplay - 3;
|
|
+ regp->crtc_834 = mode->crtc_vdisplay - 1;
|
|
+
|
|
+ if (dev_priv->card_type == NV_40)
|
|
+ /* This is what the blob does */
|
|
+ regp->crtc_850 = NVReadCRTC(dev, 0, NV_PCRTC_850);
|
|
+
|
|
+ if (dev_priv->card_type >= NV_30)
|
|
+ regp->gpio_ext = NVReadCRTC(dev, 0, NV_PCRTC_GPIO_EXT);
|
|
+
|
|
+ regp->crtc_cfg = NV_PCRTC_CONFIG_START_ADDRESS_HSYNC;
|
|
+
|
|
+ /* Some misc regs */
|
|
+ if (dev_priv->card_type == NV_40) {
|
|
+ regp->CRTC[NV_CIO_CRE_85] = 0xFF;
|
|
+ regp->CRTC[NV_CIO_CRE_86] = 0x1;
|
|
+ }
|
|
+
|
|
+ regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] = (crtc->fb->depth + 1) / 8;
|
|
+ /* Enable slaved mode (called MODE_TV in nv4ref.h) */
|
|
+ if (lvds_output || tmds_output || tv_output)
|
|
+ regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (1 << 7);
|
|
+
|
|
+ /* Generic PRAMDAC regs */
|
|
+
|
|
+ if (dev_priv->card_type >= NV_10)
|
|
+ /* Only bit that bios and blob set. */
|
|
+ regp->nv10_cursync = (1 << 25);
|
|
+
|
|
+ regp->ramdac_gen_ctrl = NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS |
|
|
+ NV_PRAMDAC_GENERAL_CONTROL_VGA_STATE_SEL |
|
|
+ NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON;
|
|
+ if (crtc->fb->depth == 16)
|
|
+ regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL;
|
|
+ if (dev_priv->chipset >= 0x11)
|
|
+ regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_PIPE_LONG;
|
|
+
|
|
+ regp->ramdac_630 = 0; /* turn off green mode (tv test pattern?) */
|
|
+ regp->tv_setup = 0;
|
|
+
|
|
+ nv_crtc_set_image_sharpening(crtc, nv_crtc->sharpness);
|
|
+
|
|
+ /* Some values the blob sets */
|
|
+ regp->ramdac_8c0 = 0x100;
|
|
+ regp->ramdac_a20 = 0x0;
|
|
+ regp->ramdac_a24 = 0xfffff;
|
|
+ regp->ramdac_a34 = 0x1;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Sets up registers for the given mode/adjusted_mode pair.
|
|
+ *
|
|
+ * The clocks, CRTCs and outputs attached to this CRTC must be off.
|
|
+ *
|
|
+ * This shouldn't enable any clocks, CRTCs, or outputs, but they should
|
|
+ * be easily turned on/off after this.
|
|
+ */
|
|
+static int
|
|
+nv_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 nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "CTRC mode on CRTC %d:\n", nv_crtc->index);
|
|
+ drm_mode_debug_printmodeline(adjusted_mode);
|
|
+
|
|
+ /* unlock must come after turning off FP_TG_CONTROL in output_prepare */
|
|
+ nv_lock_vga_crtc_shadow(dev, nv_crtc->index, -1);
|
|
+
|
|
+ nv_crtc_mode_set_vga(crtc, adjusted_mode);
|
|
+ /* calculated in nv04_dfp_prepare, nv40 needs it written before calculating PLLs */
|
|
+ if (dev_priv->card_type == NV_40)
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, dev_priv->mode_reg.sel_clk);
|
|
+ nv_crtc_mode_set_regs(crtc, adjusted_mode);
|
|
+ nv_crtc_calc_state_ext(crtc, mode, adjusted_mode->clock);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void nv_crtc_save(struct drm_crtc *crtc)
|
|
+{
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
|
|
+ struct nv04_mode_state *state = &dev_priv->mode_reg;
|
|
+ struct nv04_crtc_reg *crtc_state = &state->crtc_reg[nv_crtc->index];
|
|
+ struct nv04_mode_state *saved = &dev_priv->saved_reg;
|
|
+ struct nv04_crtc_reg *crtc_saved = &saved->crtc_reg[nv_crtc->index];
|
|
+
|
|
+ if (nv_two_heads(crtc->dev))
|
|
+ NVSetOwner(crtc->dev, nv_crtc->index);
|
|
+
|
|
+ nouveau_hw_save_state(crtc->dev, nv_crtc->index, saved);
|
|
+
|
|
+ /* init some state to saved value */
|
|
+ state->sel_clk = saved->sel_clk & ~(0x5 << 16);
|
|
+ crtc_state->CRTC[NV_CIO_CRE_LCD__INDEX] = crtc_saved->CRTC[NV_CIO_CRE_LCD__INDEX];
|
|
+ state->pllsel = saved->pllsel & ~(PLLSEL_VPLL1_MASK | PLLSEL_VPLL2_MASK | PLLSEL_TV_MASK);
|
|
+ crtc_state->gpio_ext = crtc_saved->gpio_ext;
|
|
+}
|
|
+
|
|
+static void nv_crtc_restore(struct drm_crtc *crtc)
|
|
+{
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
|
|
+ int head = nv_crtc->index;
|
|
+ uint8_t saved_cr21 = dev_priv->saved_reg.crtc_reg[head].CRTC[NV_CIO_CRE_21];
|
|
+
|
|
+ if (nv_two_heads(crtc->dev))
|
|
+ NVSetOwner(crtc->dev, head);
|
|
+
|
|
+ nouveau_hw_load_state(crtc->dev, head, &dev_priv->saved_reg);
|
|
+ nv_lock_vga_crtc_shadow(crtc->dev, head, saved_cr21);
|
|
+
|
|
+ nv_crtc->last_dpms = NV_DPMS_CLEARED;
|
|
+}
|
|
+
|
|
+static void nv_crtc_prepare(struct drm_crtc *crtc)
|
|
+{
|
|
+ struct drm_device *dev = crtc->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+ struct drm_crtc_helper_funcs *funcs = crtc->helper_private;
|
|
+
|
|
+ if (nv_two_heads(dev))
|
|
+ NVSetOwner(dev, nv_crtc->index);
|
|
+
|
|
+ funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
|
|
+
|
|
+ NVBlankScreen(dev, nv_crtc->index, true);
|
|
+
|
|
+ /* Some more preperation. */
|
|
+ NVWriteCRTC(dev, nv_crtc->index, NV_PCRTC_CONFIG, NV_PCRTC_CONFIG_START_ADDRESS_NON_VGA);
|
|
+ if (dev_priv->card_type == NV_40) {
|
|
+ uint32_t reg900 = NVReadRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_900);
|
|
+ NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_900, reg900 & ~0x10000);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void nv_crtc_commit(struct drm_crtc *crtc)
|
|
+{
|
|
+ struct drm_device *dev = crtc->dev;
|
|
+ struct drm_crtc_helper_funcs *funcs = crtc->helper_private;
|
|
+ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+
|
|
+ nouveau_hw_load_state(dev, nv_crtc->index, &dev_priv->mode_reg);
|
|
+ nv04_crtc_mode_set_base(crtc, crtc->x, crtc->y, NULL);
|
|
+
|
|
+#ifdef __BIG_ENDIAN
|
|
+ /* turn on LFB swapping */
|
|
+ {
|
|
+ uint8_t tmp = NVReadVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RCR);
|
|
+ tmp |= MASK(NV_CIO_CRE_RCR_ENDIAN_BIG);
|
|
+ NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RCR, tmp);
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ funcs->dpms(crtc, DRM_MODE_DPMS_ON);
|
|
+}
|
|
+
|
|
+static void nv_crtc_destroy(struct drm_crtc *crtc)
|
|
+{
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+
|
|
+ NV_DEBUG_KMS(crtc->dev, "\n");
|
|
+
|
|
+ if (!nv_crtc)
|
|
+ return;
|
|
+
|
|
+ drm_crtc_cleanup(crtc);
|
|
+
|
|
+ nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo);
|
|
+ kfree(nv_crtc);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv_crtc_gamma_load(struct drm_crtc *crtc)
|
|
+{
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+ struct drm_device *dev = nv_crtc->base.dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct rgb { uint8_t r, g, b; } __attribute__((packed)) *rgbs;
|
|
+ int i;
|
|
+
|
|
+ rgbs = (struct rgb *)dev_priv->mode_reg.crtc_reg[nv_crtc->index].DAC;
|
|
+ for (i = 0; i < 256; i++) {
|
|
+ rgbs[i].r = nv_crtc->lut.r[i] >> 8;
|
|
+ rgbs[i].g = nv_crtc->lut.g[i] >> 8;
|
|
+ rgbs[i].b = nv_crtc->lut.b[i] >> 8;
|
|
+ }
|
|
+
|
|
+ nouveau_hw_load_state_palette(dev, nv_crtc->index, &dev_priv->mode_reg);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, uint32_t size)
|
|
+{
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+ int i;
|
|
+
|
|
+ if (size != 256)
|
|
+ return;
|
|
+
|
|
+ for (i = 0; i < 256; i++) {
|
|
+ nv_crtc->lut.r[i] = r[i];
|
|
+ nv_crtc->lut.g[i] = g[i];
|
|
+ nv_crtc->lut.b[i] = b[i];
|
|
+ }
|
|
+
|
|
+ /* We need to know the depth before we upload, but it's possible to
|
|
+ * get called before a framebuffer is bound. If this is the case,
|
|
+ * mark the lut values as dirty by setting depth==0, and it'll be
|
|
+ * uploaded on the first mode_set_base()
|
|
+ */
|
|
+ if (!nv_crtc->base.fb) {
|
|
+ nv_crtc->lut.depth = 0;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ nv_crtc_gamma_load(crtc);
|
|
+}
|
|
+
|
|
+static int
|
|
+nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
|
|
+ struct drm_framebuffer *old_fb)
|
|
+{
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+ struct drm_device *dev = crtc->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
|
|
+ struct drm_framebuffer *drm_fb = nv_crtc->base.fb;
|
|
+ struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);
|
|
+ int arb_burst, arb_lwm;
|
|
+ int ret;
|
|
+
|
|
+ ret = nouveau_bo_pin(fb->nvbo, TTM_PL_FLAG_VRAM);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ if (old_fb) {
|
|
+ struct nouveau_framebuffer *ofb = nouveau_framebuffer(old_fb);
|
|
+ nouveau_bo_unpin(ofb->nvbo);
|
|
+ }
|
|
+
|
|
+ nv_crtc->fb.offset = fb->nvbo->bo.offset;
|
|
+
|
|
+ if (nv_crtc->lut.depth != drm_fb->depth) {
|
|
+ nv_crtc->lut.depth = drm_fb->depth;
|
|
+ nv_crtc_gamma_load(crtc);
|
|
+ }
|
|
+
|
|
+ /* Update the framebuffer format. */
|
|
+ regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] &= ~3;
|
|
+ regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (crtc->fb->depth + 1) / 8;
|
|
+ regp->ramdac_gen_ctrl &= ~NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL;
|
|
+ if (crtc->fb->depth == 16)
|
|
+ regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL;
|
|
+ crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_PIXEL_INDEX);
|
|
+ NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_GENERAL_CONTROL,
|
|
+ regp->ramdac_gen_ctrl);
|
|
+
|
|
+ regp->CRTC[NV_CIO_CR_OFFSET_INDEX] = drm_fb->pitch >> 3;
|
|
+ regp->CRTC[NV_CIO_CRE_RPC0_INDEX] =
|
|
+ XLATE(drm_fb->pitch >> 3, 8, NV_CIO_CRE_RPC0_OFFSET_10_8);
|
|
+ crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_RPC0_INDEX);
|
|
+ crtc_wr_cio_state(crtc, regp, NV_CIO_CR_OFFSET_INDEX);
|
|
+
|
|
+ /* Update the framebuffer location. */
|
|
+ regp->fb_start = nv_crtc->fb.offset & ~3;
|
|
+ regp->fb_start += (y * drm_fb->pitch) + (x * drm_fb->bits_per_pixel / 8);
|
|
+ NVWriteCRTC(dev, nv_crtc->index, NV_PCRTC_START, regp->fb_start);
|
|
+
|
|
+ /* Update the arbitration parameters. */
|
|
+ nouveau_calc_arb(dev, crtc->mode.clock, drm_fb->bits_per_pixel,
|
|
+ &arb_burst, &arb_lwm);
|
|
+
|
|
+ regp->CRTC[NV_CIO_CRE_FF_INDEX] = arb_burst;
|
|
+ regp->CRTC[NV_CIO_CRE_FFLWM__INDEX] = arb_lwm & 0xff;
|
|
+ crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_FF_INDEX);
|
|
+ crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_FFLWM__INDEX);
|
|
+
|
|
+ if (dev_priv->card_type >= NV_30) {
|
|
+ regp->CRTC[NV_CIO_CRE_47] = arb_lwm >> 8;
|
|
+ crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_47);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void nv04_cursor_upload(struct drm_device *dev, struct nouveau_bo *src,
|
|
+ struct nouveau_bo *dst)
|
|
+{
|
|
+ int width = nv_cursor_width(dev);
|
|
+ uint32_t pixel;
|
|
+ int i, j;
|
|
+
|
|
+ for (i = 0; i < width; i++) {
|
|
+ for (j = 0; j < width; j++) {
|
|
+ pixel = nouveau_bo_rd32(src, i*64 + j);
|
|
+
|
|
+ nouveau_bo_wr16(dst, i*width + j, (pixel & 0x80000000) >> 16
|
|
+ | (pixel & 0xf80000) >> 9
|
|
+ | (pixel & 0xf800) >> 6
|
|
+ | (pixel & 0xf8) >> 3);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static void nv11_cursor_upload(struct drm_device *dev, struct nouveau_bo *src,
|
|
+ struct nouveau_bo *dst)
|
|
+{
|
|
+ uint32_t pixel;
|
|
+ int alpha, i;
|
|
+
|
|
+ /* nv11+ supports premultiplied (PM), or non-premultiplied (NPM) alpha
|
|
+ * cursors (though NPM in combination with fp dithering may not work on
|
|
+ * nv11, from "nv" driver history)
|
|
+ * NPM mode needs NV_PCRTC_CURSOR_CONFIG_ALPHA_BLEND set and is what the
|
|
+ * blob uses, however we get given PM cursors so we use PM mode
|
|
+ */
|
|
+ for (i = 0; i < 64 * 64; i++) {
|
|
+ pixel = nouveau_bo_rd32(src, i);
|
|
+
|
|
+ /* hw gets unhappy if alpha <= rgb values. for a PM image "less
|
|
+ * than" shouldn't happen; fix "equal to" case by adding one to
|
|
+ * alpha channel (slightly inaccurate, but so is attempting to
|
|
+ * get back to NPM images, due to limits of integer precision)
|
|
+ */
|
|
+ alpha = pixel >> 24;
|
|
+ if (alpha > 0 && alpha < 255)
|
|
+ pixel = (pixel & 0x00ffffff) | ((alpha + 1) << 24);
|
|
+
|
|
+#ifdef __BIG_ENDIAN
|
|
+ {
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ if (dev_priv->chipset == 0x11) {
|
|
+ pixel = ((pixel & 0x000000ff) << 24) |
|
|
+ ((pixel & 0x0000ff00) << 8) |
|
|
+ ((pixel & 0x00ff0000) >> 8) |
|
|
+ ((pixel & 0xff000000) >> 24);
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ nouveau_bo_wr32(dst, i, pixel);
|
|
+ }
|
|
+}
|
|
+
|
|
+static int
|
|
+nv04_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
|
|
+ uint32_t buffer_handle, uint32_t width, uint32_t height)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
|
|
+ struct drm_device *dev = dev_priv->dev;
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+ struct nouveau_bo *cursor = NULL;
|
|
+ struct drm_gem_object *gem;
|
|
+ int ret = 0;
|
|
+
|
|
+ if (width != 64 || height != 64)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (!buffer_handle) {
|
|
+ nv_crtc->cursor.hide(nv_crtc, true);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ gem = drm_gem_object_lookup(dev, file_priv, buffer_handle);
|
|
+ if (!gem)
|
|
+ return -EINVAL;
|
|
+ cursor = nouveau_gem_object(gem);
|
|
+
|
|
+ ret = nouveau_bo_map(cursor);
|
|
+ if (ret)
|
|
+ goto out;
|
|
+
|
|
+ if (dev_priv->chipset >= 0x11)
|
|
+ nv11_cursor_upload(dev, cursor, nv_crtc->cursor.nvbo);
|
|
+ else
|
|
+ nv04_cursor_upload(dev, cursor, nv_crtc->cursor.nvbo);
|
|
+
|
|
+ nouveau_bo_unmap(cursor);
|
|
+ nv_crtc->cursor.offset = nv_crtc->cursor.nvbo->bo.offset;
|
|
+ nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.offset);
|
|
+ nv_crtc->cursor.show(nv_crtc, true);
|
|
+out:
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
+ drm_gem_object_unreference(gem);
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int
|
|
+nv04_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
|
|
+{
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+
|
|
+ nv_crtc->cursor.set_pos(nv_crtc, x, y);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct drm_crtc_funcs nv04_crtc_funcs = {
|
|
+ .save = nv_crtc_save,
|
|
+ .restore = nv_crtc_restore,
|
|
+ .cursor_set = nv04_crtc_cursor_set,
|
|
+ .cursor_move = nv04_crtc_cursor_move,
|
|
+ .gamma_set = nv_crtc_gamma_set,
|
|
+ .set_config = drm_crtc_helper_set_config,
|
|
+ .destroy = nv_crtc_destroy,
|
|
+};
|
|
+
|
|
+static const struct drm_crtc_helper_funcs nv04_crtc_helper_funcs = {
|
|
+ .dpms = nv_crtc_dpms,
|
|
+ .prepare = nv_crtc_prepare,
|
|
+ .commit = nv_crtc_commit,
|
|
+ .mode_fixup = nv_crtc_mode_fixup,
|
|
+ .mode_set = nv_crtc_mode_set,
|
|
+ .mode_set_base = nv04_crtc_mode_set_base,
|
|
+ .load_lut = nv_crtc_gamma_load,
|
|
+};
|
|
+
|
|
+int
|
|
+nv04_crtc_create(struct drm_device *dev, int crtc_num)
|
|
+{
|
|
+ struct nouveau_crtc *nv_crtc;
|
|
+ int ret, i;
|
|
+
|
|
+ nv_crtc = kzalloc(sizeof(*nv_crtc), GFP_KERNEL);
|
|
+ if (!nv_crtc)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ for (i = 0; i < 256; i++) {
|
|
+ nv_crtc->lut.r[i] = i << 8;
|
|
+ nv_crtc->lut.g[i] = i << 8;
|
|
+ nv_crtc->lut.b[i] = i << 8;
|
|
+ }
|
|
+ nv_crtc->lut.depth = 0;
|
|
+
|
|
+ nv_crtc->index = crtc_num;
|
|
+ nv_crtc->last_dpms = NV_DPMS_CLEARED;
|
|
+
|
|
+ drm_crtc_init(dev, &nv_crtc->base, &nv04_crtc_funcs);
|
|
+ drm_crtc_helper_add(&nv_crtc->base, &nv04_crtc_helper_funcs);
|
|
+ drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256);
|
|
+
|
|
+ ret = nouveau_bo_new(dev, NULL, 64*64*4, 0x100, TTM_PL_FLAG_VRAM,
|
|
+ 0, 0x0000, false, true, &nv_crtc->cursor.nvbo);
|
|
+ if (!ret) {
|
|
+ ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM);
|
|
+ if (!ret)
|
|
+ ret = nouveau_bo_map(nv_crtc->cursor.nvbo);
|
|
+ if (ret)
|
|
+ nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo);
|
|
+ }
|
|
+
|
|
+ nv04_cursor_init(nv_crtc);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nv04_cursor.c b/drivers/gpu/drm/nouveau/nv04_cursor.c
|
|
new file mode 100644
|
|
index 0000000..89a91b9
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv04_cursor.c
|
|
@@ -0,0 +1,70 @@
|
|
+#include "drmP.h"
|
|
+#include "drm_mode.h"
|
|
+#include "nouveau_reg.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_crtc.h"
|
|
+#include "nouveau_hw.h"
|
|
+
|
|
+static void
|
|
+nv04_cursor_show(struct nouveau_crtc *nv_crtc, bool update)
|
|
+{
|
|
+ nv_show_cursor(nv_crtc->base.dev, nv_crtc->index, true);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv04_cursor_hide(struct nouveau_crtc *nv_crtc, bool update)
|
|
+{
|
|
+ nv_show_cursor(nv_crtc->base.dev, nv_crtc->index, false);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv04_cursor_set_pos(struct nouveau_crtc *nv_crtc, int x, int y)
|
|
+{
|
|
+ NVWriteRAMDAC(nv_crtc->base.dev, nv_crtc->index,
|
|
+ NV_PRAMDAC_CU_START_POS,
|
|
+ XLATE(y, 0, NV_PRAMDAC_CU_START_POS_Y) |
|
|
+ XLATE(x, 0, NV_PRAMDAC_CU_START_POS_X));
|
|
+}
|
|
+
|
|
+static void
|
|
+crtc_wr_cio_state(struct drm_crtc *crtc, struct nv04_crtc_reg *crtcstate, int index)
|
|
+{
|
|
+ NVWriteVgaCrtc(crtc->dev, nouveau_crtc(crtc)->index, index,
|
|
+ crtcstate->CRTC[index]);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv04_cursor_set_offset(struct nouveau_crtc *nv_crtc, uint32_t offset)
|
|
+{
|
|
+ struct drm_device *dev = nv_crtc->base.dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
|
|
+ struct drm_crtc *crtc = &nv_crtc->base;
|
|
+
|
|
+ regp->CRTC[NV_CIO_CRE_HCUR_ADDR0_INDEX] =
|
|
+ MASK(NV_CIO_CRE_HCUR_ASI) |
|
|
+ XLATE(offset, 17, NV_CIO_CRE_HCUR_ADDR0_ADR);
|
|
+ regp->CRTC[NV_CIO_CRE_HCUR_ADDR1_INDEX] =
|
|
+ XLATE(offset, 11, NV_CIO_CRE_HCUR_ADDR1_ADR);
|
|
+ if (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN)
|
|
+ regp->CRTC[NV_CIO_CRE_HCUR_ADDR1_INDEX] |=
|
|
+ MASK(NV_CIO_CRE_HCUR_ADDR1_CUR_DBL);
|
|
+ regp->CRTC[NV_CIO_CRE_HCUR_ADDR2_INDEX] = offset >> 24;
|
|
+
|
|
+ crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
|
|
+ crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX);
|
|
+ crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX);
|
|
+ if (dev_priv->card_type == NV_40)
|
|
+ nv_fix_nv40_hw_cursor(dev, nv_crtc->index);
|
|
+}
|
|
+
|
|
+int
|
|
+nv04_cursor_init(struct nouveau_crtc *crtc)
|
|
+{
|
|
+ crtc->cursor.set_offset = nv04_cursor_set_offset;
|
|
+ crtc->cursor.set_pos = nv04_cursor_set_pos;
|
|
+ crtc->cursor.hide = nv04_cursor_hide;
|
|
+ crtc->cursor.show = nv04_cursor_show;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nv04_dac.c b/drivers/gpu/drm/nouveau/nv04_dac.c
|
|
new file mode 100644
|
|
index 0000000..d0e038d
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv04_dac.c
|
|
@@ -0,0 +1,527 @@
|
|
+/*
|
|
+ * Copyright 2003 NVIDIA, Corporation
|
|
+ * Copyright 2006 Dave Airlie
|
|
+ * Copyright 2007 Maarten Maathuis
|
|
+ * Copyright 2007-2009 Stuart Bennett
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the next
|
|
+ * paragraph) shall be included in all copies or substantial portions of the
|
|
+ * Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
+ * DEALINGS IN THE SOFTWARE.
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm_crtc_helper.h"
|
|
+
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_encoder.h"
|
|
+#include "nouveau_connector.h"
|
|
+#include "nouveau_crtc.h"
|
|
+#include "nouveau_hw.h"
|
|
+#include "nvreg.h"
|
|
+
|
|
+int nv04_dac_output_offset(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
|
|
+ int offset = 0;
|
|
+
|
|
+ if (dcb->or & (8 | OUTPUT_C))
|
|
+ offset += 0x68;
|
|
+ if (dcb->or & (8 | OUTPUT_B))
|
|
+ offset += 0x2000;
|
|
+
|
|
+ return offset;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * arbitrary limit to number of sense oscillations tolerated in one sample
|
|
+ * period (observed to be at least 13 in "nvidia")
|
|
+ */
|
|
+#define MAX_HBLANK_OSC 20
|
|
+
|
|
+/*
|
|
+ * arbitrary limit to number of conflicting sample pairs to tolerate at a
|
|
+ * voltage step (observed to be at least 5 in "nvidia")
|
|
+ */
|
|
+#define MAX_SAMPLE_PAIRS 10
|
|
+
|
|
+static int sample_load_twice(struct drm_device *dev, bool sense[2])
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < 2; i++) {
|
|
+ bool sense_a, sense_b, sense_b_prime;
|
|
+ int j = 0;
|
|
+
|
|
+ /*
|
|
+ * wait for bit 0 clear -- out of hblank -- (say reg value 0x4),
|
|
+ * then wait for transition 0x4->0x5->0x4: enter hblank, leave
|
|
+ * hblank again
|
|
+ * use a 10ms timeout (guards against crtc being inactive, in
|
|
+ * which case blank state would never change)
|
|
+ */
|
|
+ if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR,
|
|
+ 0x00000001, 0x00000000))
|
|
+ return -EBUSY;
|
|
+ if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR,
|
|
+ 0x00000001, 0x00000001))
|
|
+ return -EBUSY;
|
|
+ if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR,
|
|
+ 0x00000001, 0x00000000))
|
|
+ return -EBUSY;
|
|
+
|
|
+ udelay(100);
|
|
+ /* when level triggers, sense is _LO_ */
|
|
+ sense_a = nv_rd08(dev, NV_PRMCIO_INP0) & 0x10;
|
|
+
|
|
+ /* take another reading until it agrees with sense_a... */
|
|
+ do {
|
|
+ udelay(100);
|
|
+ sense_b = nv_rd08(dev, NV_PRMCIO_INP0) & 0x10;
|
|
+ if (sense_a != sense_b) {
|
|
+ sense_b_prime =
|
|
+ nv_rd08(dev, NV_PRMCIO_INP0) & 0x10;
|
|
+ if (sense_b == sense_b_prime) {
|
|
+ /* ... unless two consecutive subsequent
|
|
+ * samples agree; sense_a is replaced */
|
|
+ sense_a = sense_b;
|
|
+ /* force mis-match so we loop */
|
|
+ sense_b = !sense_a;
|
|
+ }
|
|
+ }
|
|
+ } while ((sense_a != sense_b) && ++j < MAX_HBLANK_OSC);
|
|
+
|
|
+ if (j == MAX_HBLANK_OSC)
|
|
+ /* with so much oscillation, default to sense:LO */
|
|
+ sense[i] = false;
|
|
+ else
|
|
+ sense[i] = sense_a;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static enum drm_connector_status nv04_dac_detect(struct drm_encoder *encoder,
|
|
+ struct drm_connector *connector)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ uint8_t saved_seq1, saved_pi, saved_rpc1;
|
|
+ uint8_t saved_palette0[3], saved_palette_mask;
|
|
+ uint32_t saved_rtest_ctrl, saved_rgen_ctrl;
|
|
+ int i;
|
|
+ uint8_t blue;
|
|
+ bool sense = true;
|
|
+
|
|
+ /*
|
|
+ * for this detection to work, there needs to be a mode set up on the
|
|
+ * CRTC. this is presumed to be the case
|
|
+ */
|
|
+
|
|
+ if (nv_two_heads(dev))
|
|
+ /* only implemented for head A for now */
|
|
+ NVSetOwner(dev, 0);
|
|
+
|
|
+ saved_seq1 = NVReadVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX);
|
|
+ NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1 & ~0x20);
|
|
+
|
|
+ saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL);
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL,
|
|
+ saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF);
|
|
+
|
|
+ msleep(10);
|
|
+
|
|
+ saved_pi = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX);
|
|
+ NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX,
|
|
+ saved_pi & ~(0x80 | MASK(NV_CIO_CRE_PIXEL_FORMAT)));
|
|
+ saved_rpc1 = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX);
|
|
+ NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1 & ~0xc0);
|
|
+
|
|
+ nv_wr08(dev, NV_PRMDIO_READ_MODE_ADDRESS, 0x0);
|
|
+ for (i = 0; i < 3; i++)
|
|
+ saved_palette0[i] = nv_rd08(dev, NV_PRMDIO_PALETTE_DATA);
|
|
+ saved_palette_mask = nv_rd08(dev, NV_PRMDIO_PIXEL_MASK);
|
|
+ nv_wr08(dev, NV_PRMDIO_PIXEL_MASK, 0);
|
|
+
|
|
+ saved_rgen_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL);
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL,
|
|
+ (saved_rgen_ctrl & ~(NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS |
|
|
+ NV_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM)) |
|
|
+ NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON);
|
|
+
|
|
+ blue = 8; /* start of test range */
|
|
+
|
|
+ do {
|
|
+ bool sense_pair[2];
|
|
+
|
|
+ nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS, 0);
|
|
+ nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, 0);
|
|
+ nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, 0);
|
|
+ /* testing blue won't find monochrome monitors. I don't care */
|
|
+ nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, blue);
|
|
+
|
|
+ i = 0;
|
|
+ /* take sample pairs until both samples in the pair agree */
|
|
+ do {
|
|
+ if (sample_load_twice(dev, sense_pair))
|
|
+ goto out;
|
|
+ } while ((sense_pair[0] != sense_pair[1]) &&
|
|
+ ++i < MAX_SAMPLE_PAIRS);
|
|
+
|
|
+ if (i == MAX_SAMPLE_PAIRS)
|
|
+ /* too much oscillation defaults to LO */
|
|
+ sense = false;
|
|
+ else
|
|
+ sense = sense_pair[0];
|
|
+
|
|
+ /*
|
|
+ * if sense goes LO before blue ramps to 0x18, monitor is not connected.
|
|
+ * ergo, if blue gets to 0x18, monitor must be connected
|
|
+ */
|
|
+ } while (++blue < 0x18 && sense);
|
|
+
|
|
+out:
|
|
+ nv_wr08(dev, NV_PRMDIO_PIXEL_MASK, saved_palette_mask);
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, saved_rgen_ctrl);
|
|
+ nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS, 0);
|
|
+ for (i = 0; i < 3; i++)
|
|
+ nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, saved_palette0[i]);
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, saved_rtest_ctrl);
|
|
+ NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, saved_pi);
|
|
+ NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1);
|
|
+ NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1);
|
|
+
|
|
+ if (blue == 0x18) {
|
|
+ NV_INFO(dev, "Load detected on head A\n");
|
|
+ return connector_status_connected;
|
|
+ }
|
|
+
|
|
+ return connector_status_disconnected;
|
|
+}
|
|
+
|
|
+uint32_t nv17_dac_sample_load(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
|
|
+ uint32_t sample, testval, regoffset = nv04_dac_output_offset(encoder);
|
|
+ uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput,
|
|
+ saved_rtest_ctrl, saved_gpio0, saved_gpio1, temp, routput;
|
|
+ int head;
|
|
+
|
|
+#define RGB_TEST_DATA(r, g, b) (r << 0 | g << 10 | b << 20)
|
|
+ if (dcb->type == OUTPUT_TV) {
|
|
+ testval = RGB_TEST_DATA(0xa0, 0xa0, 0xa0);
|
|
+
|
|
+ if (dev_priv->vbios->tvdactestval)
|
|
+ testval = dev_priv->vbios->tvdactestval;
|
|
+ } else {
|
|
+ testval = RGB_TEST_DATA(0x140, 0x140, 0x140); /* 0x94050140 */
|
|
+
|
|
+ if (dev_priv->vbios->dactestval)
|
|
+ testval = dev_priv->vbios->dactestval;
|
|
+ }
|
|
+
|
|
+ saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset);
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset,
|
|
+ saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF);
|
|
+
|
|
+ saved_powerctrl_2 = nvReadMC(dev, NV_PBUS_POWERCTRL_2);
|
|
+
|
|
+ nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2 & 0xd7ffffff);
|
|
+ if (regoffset == 0x68) {
|
|
+ saved_powerctrl_4 = nvReadMC(dev, NV_PBUS_POWERCTRL_4);
|
|
+ nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf);
|
|
+ }
|
|
+
|
|
+ saved_gpio1 = nv17_gpio_get(dev, DCB_GPIO_TVDAC1);
|
|
+ saved_gpio0 = nv17_gpio_get(dev, DCB_GPIO_TVDAC0);
|
|
+
|
|
+ nv17_gpio_set(dev, DCB_GPIO_TVDAC1, dcb->type == OUTPUT_TV);
|
|
+ nv17_gpio_set(dev, DCB_GPIO_TVDAC0, dcb->type == OUTPUT_TV);
|
|
+
|
|
+ msleep(4);
|
|
+
|
|
+ saved_routput = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset);
|
|
+ head = (saved_routput & 0x100) >> 8;
|
|
+#if 0
|
|
+ /* if there's a spare crtc, using it will minimise flicker for the case
|
|
+ * where the in-use crtc is in use by an off-chip tmds encoder */
|
|
+ if (xf86_config->crtc[head]->enabled && !xf86_config->crtc[head ^ 1]->enabled)
|
|
+ head ^= 1;
|
|
+#endif
|
|
+ /* nv driver and nv31 use 0xfffffeee, nv34 and 6600 use 0xfffffece */
|
|
+ routput = (saved_routput & 0xfffffece) | head << 8;
|
|
+
|
|
+ if (dev_priv->card_type >= NV_40) {
|
|
+ if (dcb->type == OUTPUT_TV)
|
|
+ routput |= 0x1a << 16;
|
|
+ else
|
|
+ routput &= ~(0x1a << 16);
|
|
+ }
|
|
+
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, routput);
|
|
+ msleep(1);
|
|
+
|
|
+ temp = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset);
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, temp | 1);
|
|
+
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA,
|
|
+ NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK | testval);
|
|
+ temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL,
|
|
+ temp | NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED);
|
|
+ msleep(5);
|
|
+
|
|
+ sample = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset);
|
|
+
|
|
+ temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL,
|
|
+ temp & ~NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, 0);
|
|
+
|
|
+ /* bios does something more complex for restoring, but I think this is good enough */
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, saved_routput);
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, saved_rtest_ctrl);
|
|
+ if (regoffset == 0x68)
|
|
+ nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4);
|
|
+ nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2);
|
|
+
|
|
+ nv17_gpio_set(dev, DCB_GPIO_TVDAC1, saved_gpio1);
|
|
+ nv17_gpio_set(dev, DCB_GPIO_TVDAC0, saved_gpio0);
|
|
+
|
|
+ return sample;
|
|
+}
|
|
+
|
|
+static enum drm_connector_status
|
|
+nv17_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
|
|
+ uint32_t sample = nv17_dac_sample_load(encoder);
|
|
+
|
|
+ if (sample & NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI) {
|
|
+ NV_INFO(dev, "Load detected on output %c\n",
|
|
+ '@' + ffs(dcb->or));
|
|
+ return connector_status_connected;
|
|
+ } else {
|
|
+ return connector_status_disconnected;
|
|
+ }
|
|
+}
|
|
+
|
|
+static bool nv04_dac_mode_fixup(struct drm_encoder *encoder,
|
|
+ struct drm_display_mode *mode,
|
|
+ struct drm_display_mode *adjusted_mode)
|
|
+{
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static void nv04_dac_prepare(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct drm_encoder_helper_funcs *helper = encoder->helper_private;
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ int head = nouveau_crtc(encoder->crtc)->index;
|
|
+ struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg;
|
|
+
|
|
+ helper->dpms(encoder, DRM_MODE_DPMS_OFF);
|
|
+
|
|
+ nv04_dfp_disable(dev, head);
|
|
+
|
|
+ /* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f)
|
|
+ * at LCD__INDEX which we don't alter
|
|
+ */
|
|
+ if (!(crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] & 0x44))
|
|
+ crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] = 0;
|
|
+}
|
|
+
|
|
+
|
|
+static void nv04_dac_mode_set(struct drm_encoder *encoder,
|
|
+ struct drm_display_mode *mode,
|
|
+ struct drm_display_mode *adjusted_mode)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ int head = nouveau_crtc(encoder->crtc)->index;
|
|
+
|
|
+ if (nv_gf4_disp_arch(dev)) {
|
|
+ struct drm_encoder *rebind;
|
|
+ uint32_t dac_offset = nv04_dac_output_offset(encoder);
|
|
+ uint32_t otherdac;
|
|
+
|
|
+ /* bit 16-19 are bits that are set on some G70 cards,
|
|
+ * but don't seem to have much effect */
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset,
|
|
+ head << 8 | NV_PRAMDAC_DACCLK_SEL_DACCLK);
|
|
+ /* force any other vga encoders to bind to the other crtc */
|
|
+ list_for_each_entry(rebind, &dev->mode_config.encoder_list, head) {
|
|
+ if (rebind == encoder
|
|
+ || nouveau_encoder(rebind)->dcb->type != OUTPUT_ANALOG)
|
|
+ continue;
|
|
+
|
|
+ dac_offset = nv04_dac_output_offset(rebind);
|
|
+ otherdac = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset);
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset,
|
|
+ (otherdac & ~0x0100) | (head ^ 1) << 8);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* This could use refinement for flatpanels, but it should work this way */
|
|
+ if (dev_priv->chipset < 0x44)
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000);
|
|
+ else
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000);
|
|
+}
|
|
+
|
|
+static void nv04_dac_commit(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
|
|
+ struct drm_encoder_helper_funcs *helper = encoder->helper_private;
|
|
+
|
|
+ helper->dpms(encoder, DRM_MODE_DPMS_ON);
|
|
+
|
|
+ NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
|
|
+ drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base),
|
|
+ nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
|
|
+}
|
|
+
|
|
+void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
|
|
+
|
|
+ if (nv_gf4_disp_arch(dev)) {
|
|
+ uint32_t *dac_users = &dev_priv->dac_users[ffs(dcb->or) - 1];
|
|
+ int dacclk_off = NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder);
|
|
+ uint32_t dacclk = NVReadRAMDAC(dev, 0, dacclk_off);
|
|
+
|
|
+ if (enable) {
|
|
+ *dac_users |= 1 << dcb->index;
|
|
+ NVWriteRAMDAC(dev, 0, dacclk_off, dacclk | NV_PRAMDAC_DACCLK_SEL_DACCLK);
|
|
+
|
|
+ } else {
|
|
+ *dac_users &= ~(1 << dcb->index);
|
|
+ if (!*dac_users)
|
|
+ NVWriteRAMDAC(dev, 0, dacclk_off,
|
|
+ dacclk & ~NV_PRAMDAC_DACCLK_SEL_DACCLK);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static void nv04_dac_dpms(struct drm_encoder *encoder, int mode)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+
|
|
+ if (nv_encoder->last_dpms == mode)
|
|
+ return;
|
|
+ nv_encoder->last_dpms = mode;
|
|
+
|
|
+ NV_INFO(dev, "Setting dpms mode %d on vga encoder (output %d)\n",
|
|
+ mode, nv_encoder->dcb->index);
|
|
+
|
|
+ nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON);
|
|
+}
|
|
+
|
|
+static void nv04_dac_save(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+
|
|
+ if (nv_gf4_disp_arch(dev))
|
|
+ nv_encoder->restore.output = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK +
|
|
+ nv04_dac_output_offset(encoder));
|
|
+}
|
|
+
|
|
+static void nv04_dac_restore(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+
|
|
+ if (nv_gf4_disp_arch(dev))
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder),
|
|
+ nv_encoder->restore.output);
|
|
+
|
|
+ nv_encoder->last_dpms = NV_DPMS_CLEARED;
|
|
+}
|
|
+
|
|
+static void nv04_dac_destroy(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+
|
|
+ NV_DEBUG_KMS(encoder->dev, "\n");
|
|
+
|
|
+ drm_encoder_cleanup(encoder);
|
|
+ kfree(nv_encoder);
|
|
+}
|
|
+
|
|
+static const struct drm_encoder_helper_funcs nv04_dac_helper_funcs = {
|
|
+ .dpms = nv04_dac_dpms,
|
|
+ .save = nv04_dac_save,
|
|
+ .restore = nv04_dac_restore,
|
|
+ .mode_fixup = nv04_dac_mode_fixup,
|
|
+ .prepare = nv04_dac_prepare,
|
|
+ .commit = nv04_dac_commit,
|
|
+ .mode_set = nv04_dac_mode_set,
|
|
+ .detect = nv04_dac_detect
|
|
+};
|
|
+
|
|
+static const struct drm_encoder_helper_funcs nv17_dac_helper_funcs = {
|
|
+ .dpms = nv04_dac_dpms,
|
|
+ .save = nv04_dac_save,
|
|
+ .restore = nv04_dac_restore,
|
|
+ .mode_fixup = nv04_dac_mode_fixup,
|
|
+ .prepare = nv04_dac_prepare,
|
|
+ .commit = nv04_dac_commit,
|
|
+ .mode_set = nv04_dac_mode_set,
|
|
+ .detect = nv17_dac_detect
|
|
+};
|
|
+
|
|
+static const struct drm_encoder_funcs nv04_dac_funcs = {
|
|
+ .destroy = nv04_dac_destroy,
|
|
+};
|
|
+
|
|
+int nv04_dac_create(struct drm_device *dev, struct dcb_entry *entry)
|
|
+{
|
|
+ const struct drm_encoder_helper_funcs *helper;
|
|
+ struct drm_encoder *encoder;
|
|
+ struct nouveau_encoder *nv_encoder = NULL;
|
|
+
|
|
+ nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
|
|
+ if (!nv_encoder)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ encoder = to_drm_encoder(nv_encoder);
|
|
+
|
|
+ nv_encoder->dcb = entry;
|
|
+ nv_encoder->or = ffs(entry->or) - 1;
|
|
+
|
|
+ if (nv_gf4_disp_arch(dev))
|
|
+ helper = &nv17_dac_helper_funcs;
|
|
+ else
|
|
+ helper = &nv04_dac_helper_funcs;
|
|
+
|
|
+ drm_encoder_init(dev, encoder, &nv04_dac_funcs, DRM_MODE_ENCODER_DAC);
|
|
+ drm_encoder_helper_add(encoder, helper);
|
|
+
|
|
+ encoder->possible_crtcs = entry->heads;
|
|
+ encoder->possible_clones = 0;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nv04_dfp.c b/drivers/gpu/drm/nouveau/nv04_dfp.c
|
|
new file mode 100644
|
|
index 0000000..483f875
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv04_dfp.c
|
|
@@ -0,0 +1,623 @@
|
|
+/*
|
|
+ * Copyright 2003 NVIDIA, Corporation
|
|
+ * Copyright 2006 Dave Airlie
|
|
+ * Copyright 2007 Maarten Maathuis
|
|
+ * Copyright 2007-2009 Stuart Bennett
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the next
|
|
+ * paragraph) shall be included in all copies or substantial portions of the
|
|
+ * Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
+ * DEALINGS IN THE SOFTWARE.
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm_crtc_helper.h"
|
|
+
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_encoder.h"
|
|
+#include "nouveau_connector.h"
|
|
+#include "nouveau_crtc.h"
|
|
+#include "nouveau_hw.h"
|
|
+#include "nvreg.h"
|
|
+
|
|
+#define FP_TG_CONTROL_ON (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | \
|
|
+ NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS | \
|
|
+ NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS)
|
|
+#define FP_TG_CONTROL_OFF (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_DISABLE | \
|
|
+ NV_PRAMDAC_FP_TG_CONTROL_HSYNC_DISABLE | \
|
|
+ NV_PRAMDAC_FP_TG_CONTROL_VSYNC_DISABLE)
|
|
+
|
|
+static inline bool is_fpc_off(uint32_t fpc)
|
|
+{
|
|
+ return ((fpc & (FP_TG_CONTROL_ON | FP_TG_CONTROL_OFF)) ==
|
|
+ FP_TG_CONTROL_OFF);
|
|
+}
|
|
+
|
|
+int nv04_dfp_get_bound_head(struct drm_device *dev, struct dcb_entry *dcbent)
|
|
+{
|
|
+ /* special case of nv_read_tmds to find crtc associated with an output.
|
|
+ * this does not give a correct answer for off-chip dvi, but there's no
|
|
+ * use for such an answer anyway
|
|
+ */
|
|
+ int ramdac = (dcbent->or & OUTPUT_C) >> 2;
|
|
+
|
|
+ NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL,
|
|
+ NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE | 0x4);
|
|
+ return ((NVReadRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA) & 0x8) >> 3) ^ ramdac;
|
|
+}
|
|
+
|
|
+void nv04_dfp_bind_head(struct drm_device *dev, struct dcb_entry *dcbent,
|
|
+ int head, bool dl)
|
|
+{
|
|
+ /* The BIOS scripts don't do this for us, sadly
|
|
+ * Luckily we do know the values ;-)
|
|
+ *
|
|
+ * head < 0 indicates we wish to force a setting with the overrideval
|
|
+ * (for VT restore etc.)
|
|
+ */
|
|
+
|
|
+ int ramdac = (dcbent->or & OUTPUT_C) >> 2;
|
|
+ uint8_t tmds04 = 0x80;
|
|
+
|
|
+ if (head != ramdac)
|
|
+ tmds04 = 0x88;
|
|
+
|
|
+ if (dcbent->type == OUTPUT_LVDS)
|
|
+ tmds04 |= 0x01;
|
|
+
|
|
+ nv_write_tmds(dev, dcbent->or, 0, 0x04, tmds04);
|
|
+
|
|
+ if (dl) /* dual link */
|
|
+ nv_write_tmds(dev, dcbent->or, 1, 0x04, tmds04 ^ 0x08);
|
|
+}
|
|
+
|
|
+void nv04_dfp_disable(struct drm_device *dev, int head)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg;
|
|
+
|
|
+ if (NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL) &
|
|
+ FP_TG_CONTROL_ON) {
|
|
+ /* digital remnants must be cleaned before new crtc
|
|
+ * values programmed. delay is time for the vga stuff
|
|
+ * to realise it's in control again
|
|
+ */
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL,
|
|
+ FP_TG_CONTROL_OFF);
|
|
+ msleep(50);
|
|
+ }
|
|
+ /* don't inadvertently turn it on when state written later */
|
|
+ crtcstate[head].fp_control = FP_TG_CONTROL_OFF;
|
|
+}
|
|
+
|
|
+void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct drm_crtc *crtc;
|
|
+ struct nouveau_crtc *nv_crtc;
|
|
+ uint32_t *fpc;
|
|
+
|
|
+ if (mode == DRM_MODE_DPMS_ON) {
|
|
+ nv_crtc = nouveau_crtc(encoder->crtc);
|
|
+ fpc = &dev_priv->mode_reg.crtc_reg[nv_crtc->index].fp_control;
|
|
+
|
|
+ if (is_fpc_off(*fpc)) {
|
|
+ /* using saved value is ok, as (is_digital && dpms_on &&
|
|
+ * fp_control==OFF) is (at present) *only* true when
|
|
+ * fpc's most recent change was by below "off" code
|
|
+ */
|
|
+ *fpc = nv_crtc->dpms_saved_fp_control;
|
|
+ }
|
|
+
|
|
+ nv_crtc->fp_users |= 1 << nouveau_encoder(encoder)->dcb->index;
|
|
+ NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_FP_TG_CONTROL, *fpc);
|
|
+ } else {
|
|
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
|
+ nv_crtc = nouveau_crtc(crtc);
|
|
+ fpc = &dev_priv->mode_reg.crtc_reg[nv_crtc->index].fp_control;
|
|
+
|
|
+ nv_crtc->fp_users &= ~(1 << nouveau_encoder(encoder)->dcb->index);
|
|
+ if (!is_fpc_off(*fpc) && !nv_crtc->fp_users) {
|
|
+ nv_crtc->dpms_saved_fp_control = *fpc;
|
|
+ /* cut the FP output */
|
|
+ *fpc &= ~FP_TG_CONTROL_ON;
|
|
+ *fpc |= FP_TG_CONTROL_OFF;
|
|
+ NVWriteRAMDAC(dev, nv_crtc->index,
|
|
+ NV_PRAMDAC_FP_TG_CONTROL, *fpc);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static bool nv04_dfp_mode_fixup(struct drm_encoder *encoder,
|
|
+ struct drm_display_mode *mode,
|
|
+ struct drm_display_mode *adjusted_mode)
|
|
+{
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ struct nouveau_connector *nv_connector = nouveau_encoder_connector_get(nv_encoder);
|
|
+
|
|
+ /* For internal panels and gpu scaling on DVI we need the native mode */
|
|
+ if (nv_connector->scaling_mode != DRM_MODE_SCALE_NONE) {
|
|
+ if (!nv_connector->native_mode)
|
|
+ return false;
|
|
+ nv_encoder->mode = *nv_connector->native_mode;
|
|
+ adjusted_mode->clock = nv_connector->native_mode->clock;
|
|
+ } else {
|
|
+ nv_encoder->mode = *adjusted_mode;
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static void nv04_dfp_prepare_sel_clk(struct drm_device *dev,
|
|
+ struct nouveau_encoder *nv_encoder, int head)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nv04_mode_state *state = &dev_priv->mode_reg;
|
|
+ uint32_t bits1618 = nv_encoder->dcb->or & OUTPUT_A ? 0x10000 : 0x40000;
|
|
+
|
|
+ if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP)
|
|
+ return;
|
|
+
|
|
+ /* SEL_CLK is only used on the primary ramdac
|
|
+ * It toggles spread spectrum PLL output and sets the bindings of PLLs
|
|
+ * to heads on digital outputs
|
|
+ */
|
|
+ if (head)
|
|
+ state->sel_clk |= bits1618;
|
|
+ else
|
|
+ state->sel_clk &= ~bits1618;
|
|
+
|
|
+ /* nv30:
|
|
+ * bit 0 NVClk spread spectrum on/off
|
|
+ * bit 2 MemClk spread spectrum on/off
|
|
+ * bit 4 PixClk1 spread spectrum on/off toggle
|
|
+ * bit 6 PixClk2 spread spectrum on/off toggle
|
|
+ *
|
|
+ * nv40 (observations from bios behaviour and mmio traces):
|
|
+ * bits 4&6 as for nv30
|
|
+ * bits 5&7 head dependent as for bits 4&6, but do not appear with 4&6;
|
|
+ * maybe a different spread mode
|
|
+ * bits 8&10 seen on dual-link dvi outputs, purpose unknown (set by POST scripts)
|
|
+ * The logic behind turning spread spectrum on/off in the first place,
|
|
+ * and which bit-pair to use, is unclear on nv40 (for earlier cards, the fp table
|
|
+ * entry has the necessary info)
|
|
+ */
|
|
+ if (nv_encoder->dcb->type == OUTPUT_LVDS && dev_priv->saved_reg.sel_clk & 0xf0) {
|
|
+ int shift = (dev_priv->saved_reg.sel_clk & 0x50) ? 0 : 1;
|
|
+
|
|
+ state->sel_clk &= ~0xf0;
|
|
+ state->sel_clk |= (head ? 0x40 : 0x10) << shift;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void nv04_dfp_prepare(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ struct drm_encoder_helper_funcs *helper = encoder->helper_private;
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ int head = nouveau_crtc(encoder->crtc)->index;
|
|
+ struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg;
|
|
+ uint8_t *cr_lcd = &crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX];
|
|
+ uint8_t *cr_lcd_oth = &crtcstate[head ^ 1].CRTC[NV_CIO_CRE_LCD__INDEX];
|
|
+
|
|
+ helper->dpms(encoder, DRM_MODE_DPMS_OFF);
|
|
+
|
|
+ nv04_dfp_prepare_sel_clk(dev, nv_encoder, head);
|
|
+
|
|
+ /* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f)
|
|
+ * at LCD__INDEX which we don't alter
|
|
+ */
|
|
+ if (!(*cr_lcd & 0x44)) {
|
|
+ *cr_lcd = 0x3;
|
|
+
|
|
+ if (nv_two_heads(dev)) {
|
|
+ if (nv_encoder->dcb->location == DCB_LOC_ON_CHIP)
|
|
+ *cr_lcd |= head ? 0x0 : 0x8;
|
|
+ else {
|
|
+ *cr_lcd |= (nv_encoder->dcb->or << 4) & 0x30;
|
|
+ if (nv_encoder->dcb->type == OUTPUT_LVDS)
|
|
+ *cr_lcd |= 0x30;
|
|
+ if ((*cr_lcd & 0x30) == (*cr_lcd_oth & 0x30)) {
|
|
+ /* avoid being connected to both crtcs */
|
|
+ *cr_lcd_oth &= ~0x30;
|
|
+ NVWriteVgaCrtc(dev, head ^ 1,
|
|
+ NV_CIO_CRE_LCD__INDEX,
|
|
+ *cr_lcd_oth);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+static void nv04_dfp_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_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
|
|
+ struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
|
|
+ struct nv04_crtc_reg *savep = &dev_priv->saved_reg.crtc_reg[nv_crtc->index];
|
|
+ struct nouveau_connector *nv_connector = nouveau_crtc_connector_get(nv_crtc);
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ struct drm_display_mode *output_mode = &nv_encoder->mode;
|
|
+ uint32_t mode_ratio, panel_ratio;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "Output mode on CRTC %d:\n", nv_crtc->index);
|
|
+ drm_mode_debug_printmodeline(output_mode);
|
|
+
|
|
+ /* Initialize the FP registers in this CRTC. */
|
|
+ regp->fp_horiz_regs[FP_DISPLAY_END] = output_mode->hdisplay - 1;
|
|
+ regp->fp_horiz_regs[FP_TOTAL] = output_mode->htotal - 1;
|
|
+ if (!nv_gf4_disp_arch(dev) ||
|
|
+ (output_mode->hsync_start - output_mode->hdisplay) >=
|
|
+ dev_priv->vbios->digital_min_front_porch)
|
|
+ regp->fp_horiz_regs[FP_CRTC] = output_mode->hdisplay;
|
|
+ else
|
|
+ regp->fp_horiz_regs[FP_CRTC] = output_mode->hsync_start - dev_priv->vbios->digital_min_front_porch - 1;
|
|
+ regp->fp_horiz_regs[FP_SYNC_START] = output_mode->hsync_start - 1;
|
|
+ regp->fp_horiz_regs[FP_SYNC_END] = output_mode->hsync_end - 1;
|
|
+ regp->fp_horiz_regs[FP_VALID_START] = output_mode->hskew;
|
|
+ regp->fp_horiz_regs[FP_VALID_END] = output_mode->hdisplay - 1;
|
|
+
|
|
+ regp->fp_vert_regs[FP_DISPLAY_END] = output_mode->vdisplay - 1;
|
|
+ regp->fp_vert_regs[FP_TOTAL] = output_mode->vtotal - 1;
|
|
+ regp->fp_vert_regs[FP_CRTC] = output_mode->vtotal - 5 - 1;
|
|
+ regp->fp_vert_regs[FP_SYNC_START] = output_mode->vsync_start - 1;
|
|
+ regp->fp_vert_regs[FP_SYNC_END] = output_mode->vsync_end - 1;
|
|
+ regp->fp_vert_regs[FP_VALID_START] = 0;
|
|
+ regp->fp_vert_regs[FP_VALID_END] = output_mode->vdisplay - 1;
|
|
+
|
|
+ /* bit26: a bit seen on some g7x, no as yet discernable purpose */
|
|
+ regp->fp_control = NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS |
|
|
+ (savep->fp_control & (1 << 26 | NV_PRAMDAC_FP_TG_CONTROL_READ_PROG));
|
|
+ /* Deal with vsync/hsync polarity */
|
|
+ /* LVDS screens do set this, but modes with +ve syncs are very rare */
|
|
+ if (output_mode->flags & DRM_MODE_FLAG_PVSYNC)
|
|
+ regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS;
|
|
+ if (output_mode->flags & DRM_MODE_FLAG_PHSYNC)
|
|
+ regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS;
|
|
+ /* panel scaling first, as native would get set otherwise */
|
|
+ if (nv_connector->scaling_mode == DRM_MODE_SCALE_NONE ||
|
|
+ nv_connector->scaling_mode == DRM_MODE_SCALE_CENTER) /* panel handles it */
|
|
+ regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_CENTER;
|
|
+ else if (adjusted_mode->hdisplay == output_mode->hdisplay &&
|
|
+ adjusted_mode->vdisplay == output_mode->vdisplay) /* native mode */
|
|
+ regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_NATIVE;
|
|
+ else /* gpu needs to scale */
|
|
+ regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_SCALE;
|
|
+ if (nvReadEXTDEV(dev, NV_PEXTDEV_BOOT_0) & NV_PEXTDEV_BOOT_0_STRAP_FP_IFACE_12BIT)
|
|
+ regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12;
|
|
+ if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP &&
|
|
+ output_mode->clock > 165000)
|
|
+ regp->fp_control |= (2 << 24);
|
|
+ if (nv_encoder->dcb->type == OUTPUT_LVDS) {
|
|
+ bool duallink, dummy;
|
|
+
|
|
+ nouveau_bios_parse_lvds_table(dev, nv_connector->native_mode->
|
|
+ clock, &duallink, &dummy);
|
|
+ if (duallink)
|
|
+ regp->fp_control |= (8 << 28);
|
|
+ } else
|
|
+ if (output_mode->clock > 165000)
|
|
+ regp->fp_control |= (8 << 28);
|
|
+
|
|
+ regp->fp_debug_0 = NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND |
|
|
+ NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND |
|
|
+ NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR |
|
|
+ NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR |
|
|
+ NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED |
|
|
+ NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE |
|
|
+ NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE;
|
|
+
|
|
+ /* We want automatic scaling */
|
|
+ regp->fp_debug_1 = 0;
|
|
+ /* This can override HTOTAL and VTOTAL */
|
|
+ regp->fp_debug_2 = 0;
|
|
+
|
|
+ /* Use 20.12 fixed point format to avoid floats */
|
|
+ mode_ratio = (1 << 12) * adjusted_mode->hdisplay / adjusted_mode->vdisplay;
|
|
+ panel_ratio = (1 << 12) * output_mode->hdisplay / output_mode->vdisplay;
|
|
+ /* if ratios are equal, SCALE_ASPECT will automatically (and correctly)
|
|
+ * get treated the same as SCALE_FULLSCREEN */
|
|
+ if (nv_connector->scaling_mode == DRM_MODE_SCALE_ASPECT &&
|
|
+ mode_ratio != panel_ratio) {
|
|
+ uint32_t diff, scale;
|
|
+ bool divide_by_2 = nv_gf4_disp_arch(dev);
|
|
+
|
|
+ if (mode_ratio < panel_ratio) {
|
|
+ /* vertical needs to expand to glass size (automatic)
|
|
+ * horizontal needs to be scaled at vertical scale factor
|
|
+ * to maintain aspect */
|
|
+
|
|
+ scale = (1 << 12) * adjusted_mode->vdisplay / output_mode->vdisplay;
|
|
+ regp->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE |
|
|
+ XLATE(scale, divide_by_2, NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE);
|
|
+
|
|
+ /* restrict area of screen used, horizontally */
|
|
+ diff = output_mode->hdisplay -
|
|
+ output_mode->vdisplay * mode_ratio / (1 << 12);
|
|
+ regp->fp_horiz_regs[FP_VALID_START] += diff / 2;
|
|
+ regp->fp_horiz_regs[FP_VALID_END] -= diff / 2;
|
|
+ }
|
|
+
|
|
+ if (mode_ratio > panel_ratio) {
|
|
+ /* horizontal needs to expand to glass size (automatic)
|
|
+ * vertical needs to be scaled at horizontal scale factor
|
|
+ * to maintain aspect */
|
|
+
|
|
+ scale = (1 << 12) * adjusted_mode->hdisplay / output_mode->hdisplay;
|
|
+ regp->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE |
|
|
+ XLATE(scale, divide_by_2, NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE);
|
|
+
|
|
+ /* restrict area of screen used, vertically */
|
|
+ diff = output_mode->vdisplay -
|
|
+ (1 << 12) * output_mode->hdisplay / mode_ratio;
|
|
+ regp->fp_vert_regs[FP_VALID_START] += diff / 2;
|
|
+ regp->fp_vert_regs[FP_VALID_END] -= diff / 2;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Output property. */
|
|
+ if (nv_connector->use_dithering) {
|
|
+ if (dev_priv->chipset == 0x11)
|
|
+ regp->dither = savep->dither | 0x00010000;
|
|
+ else {
|
|
+ int i;
|
|
+ regp->dither = savep->dither | 0x00000001;
|
|
+ for (i = 0; i < 3; i++) {
|
|
+ regp->dither_regs[i] = 0xe4e4e4e4;
|
|
+ regp->dither_regs[i + 3] = 0x44444444;
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ if (dev_priv->chipset != 0x11) {
|
|
+ /* reset them */
|
|
+ int i;
|
|
+ for (i = 0; i < 3; i++) {
|
|
+ regp->dither_regs[i] = savep->dither_regs[i];
|
|
+ regp->dither_regs[i + 3] = savep->dither_regs[i + 3];
|
|
+ }
|
|
+ }
|
|
+ regp->dither = savep->dither;
|
|
+ }
|
|
+
|
|
+ regp->fp_margin_color = 0;
|
|
+}
|
|
+
|
|
+static void nv04_dfp_commit(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct drm_encoder_helper_funcs *helper = encoder->helper_private;
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ struct dcb_entry *dcbe = nv_encoder->dcb;
|
|
+ int head = nouveau_crtc(encoder->crtc)->index;
|
|
+
|
|
+ NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
|
|
+ drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base),
|
|
+ nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
|
|
+
|
|
+ if (dcbe->type == OUTPUT_TMDS)
|
|
+ run_tmds_table(dev, dcbe, head, nv_encoder->mode.clock);
|
|
+ else if (dcbe->type == OUTPUT_LVDS)
|
|
+ call_lvds_script(dev, dcbe, head, LVDS_RESET, nv_encoder->mode.clock);
|
|
+
|
|
+ /* update fp_control state for any changes made by scripts,
|
|
+ * so correct value is written at DPMS on */
|
|
+ dev_priv->mode_reg.crtc_reg[head].fp_control =
|
|
+ NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL);
|
|
+
|
|
+ /* This could use refinement for flatpanels, but it should work this way */
|
|
+ if (dev_priv->chipset < 0x44)
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000);
|
|
+ else
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000);
|
|
+
|
|
+ helper->dpms(encoder, DRM_MODE_DPMS_ON);
|
|
+
|
|
+ NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
|
|
+ drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base),
|
|
+ nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
|
|
+}
|
|
+
|
|
+static inline bool is_powersaving_dpms(int mode)
|
|
+{
|
|
+ return (mode != DRM_MODE_DPMS_ON);
|
|
+}
|
|
+
|
|
+static void nv04_lvds_dpms(struct drm_encoder *encoder, int mode)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct drm_crtc *crtc = encoder->crtc;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ bool was_powersaving = is_powersaving_dpms(nv_encoder->last_dpms);
|
|
+
|
|
+ if (nv_encoder->last_dpms == mode)
|
|
+ return;
|
|
+ nv_encoder->last_dpms = mode;
|
|
+
|
|
+ NV_INFO(dev, "Setting dpms mode %d on lvds encoder (output %d)\n",
|
|
+ mode, nv_encoder->dcb->index);
|
|
+
|
|
+ if (was_powersaving && is_powersaving_dpms(mode))
|
|
+ return;
|
|
+
|
|
+ if (nv_encoder->dcb->lvdsconf.use_power_scripts) {
|
|
+ struct nouveau_connector *nv_connector = nouveau_encoder_connector_get(nv_encoder);
|
|
+
|
|
+ /* when removing an output, crtc may not be set, but PANEL_OFF
|
|
+ * must still be run
|
|
+ */
|
|
+ int head = crtc ? nouveau_crtc(crtc)->index :
|
|
+ nv04_dfp_get_bound_head(dev, nv_encoder->dcb);
|
|
+
|
|
+ if (mode == DRM_MODE_DPMS_ON) {
|
|
+ if (!nv_connector->native_mode) {
|
|
+ NV_ERROR(dev, "Not turning on LVDS without native mode\n");
|
|
+ return;
|
|
+ }
|
|
+ call_lvds_script(dev, nv_encoder->dcb, head,
|
|
+ LVDS_PANEL_ON, nv_connector->native_mode->clock);
|
|
+ } else
|
|
+ /* pxclk of 0 is fine for PANEL_OFF, and for a
|
|
+ * disconnected LVDS encoder there is no native_mode
|
|
+ */
|
|
+ call_lvds_script(dev, nv_encoder->dcb, head,
|
|
+ LVDS_PANEL_OFF, 0);
|
|
+ }
|
|
+
|
|
+ nv04_dfp_update_fp_control(encoder, mode);
|
|
+
|
|
+ if (mode == DRM_MODE_DPMS_ON)
|
|
+ nv04_dfp_prepare_sel_clk(dev, nv_encoder, nouveau_crtc(crtc)->index);
|
|
+ else {
|
|
+ dev_priv->mode_reg.sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK);
|
|
+ dev_priv->mode_reg.sel_clk &= ~0xf0;
|
|
+ }
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, dev_priv->mode_reg.sel_clk);
|
|
+}
|
|
+
|
|
+static void nv04_tmds_dpms(struct drm_encoder *encoder, int mode)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+
|
|
+ if (nv_encoder->last_dpms == mode)
|
|
+ return;
|
|
+ nv_encoder->last_dpms = mode;
|
|
+
|
|
+ NV_INFO(dev, "Setting dpms mode %d on tmds encoder (output %d)\n",
|
|
+ mode, nv_encoder->dcb->index);
|
|
+
|
|
+ nv04_dfp_update_fp_control(encoder, mode);
|
|
+}
|
|
+
|
|
+static void nv04_dfp_save(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+
|
|
+ if (nv_two_heads(dev))
|
|
+ nv_encoder->restore.head =
|
|
+ nv04_dfp_get_bound_head(dev, nv_encoder->dcb);
|
|
+}
|
|
+
|
|
+static void nv04_dfp_restore(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ int head = nv_encoder->restore.head;
|
|
+
|
|
+ if (nv_encoder->dcb->type == OUTPUT_LVDS) {
|
|
+ struct drm_display_mode *native_mode = nouveau_encoder_connector_get(nv_encoder)->native_mode;
|
|
+ if (native_mode)
|
|
+ call_lvds_script(dev, nv_encoder->dcb, head, LVDS_PANEL_ON,
|
|
+ native_mode->clock);
|
|
+ else
|
|
+ NV_ERROR(dev, "Not restoring LVDS without native mode\n");
|
|
+
|
|
+ } else if (nv_encoder->dcb->type == OUTPUT_TMDS) {
|
|
+ int clock = nouveau_hw_pllvals_to_clk
|
|
+ (&dev_priv->saved_reg.crtc_reg[head].pllvals);
|
|
+
|
|
+ run_tmds_table(dev, nv_encoder->dcb, head, clock);
|
|
+ }
|
|
+
|
|
+ nv_encoder->last_dpms = NV_DPMS_CLEARED;
|
|
+}
|
|
+
|
|
+static void nv04_dfp_destroy(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+
|
|
+ NV_DEBUG_KMS(encoder->dev, "\n");
|
|
+
|
|
+ drm_encoder_cleanup(encoder);
|
|
+ kfree(nv_encoder);
|
|
+}
|
|
+
|
|
+static const struct drm_encoder_helper_funcs nv04_lvds_helper_funcs = {
|
|
+ .dpms = nv04_lvds_dpms,
|
|
+ .save = nv04_dfp_save,
|
|
+ .restore = nv04_dfp_restore,
|
|
+ .mode_fixup = nv04_dfp_mode_fixup,
|
|
+ .prepare = nv04_dfp_prepare,
|
|
+ .commit = nv04_dfp_commit,
|
|
+ .mode_set = nv04_dfp_mode_set,
|
|
+ .detect = NULL,
|
|
+};
|
|
+
|
|
+static const struct drm_encoder_helper_funcs nv04_tmds_helper_funcs = {
|
|
+ .dpms = nv04_tmds_dpms,
|
|
+ .save = nv04_dfp_save,
|
|
+ .restore = nv04_dfp_restore,
|
|
+ .mode_fixup = nv04_dfp_mode_fixup,
|
|
+ .prepare = nv04_dfp_prepare,
|
|
+ .commit = nv04_dfp_commit,
|
|
+ .mode_set = nv04_dfp_mode_set,
|
|
+ .detect = NULL,
|
|
+};
|
|
+
|
|
+static const struct drm_encoder_funcs nv04_dfp_funcs = {
|
|
+ .destroy = nv04_dfp_destroy,
|
|
+};
|
|
+
|
|
+int nv04_dfp_create(struct drm_device *dev, struct dcb_entry *entry)
|
|
+{
|
|
+ const struct drm_encoder_helper_funcs *helper;
|
|
+ struct drm_encoder *encoder;
|
|
+ struct nouveau_encoder *nv_encoder = NULL;
|
|
+ int type;
|
|
+
|
|
+ switch (entry->type) {
|
|
+ case OUTPUT_TMDS:
|
|
+ type = DRM_MODE_ENCODER_TMDS;
|
|
+ helper = &nv04_tmds_helper_funcs;
|
|
+ break;
|
|
+ case OUTPUT_LVDS:
|
|
+ type = DRM_MODE_ENCODER_LVDS;
|
|
+ helper = &nv04_lvds_helper_funcs;
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
|
|
+ if (!nv_encoder)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ encoder = to_drm_encoder(nv_encoder);
|
|
+
|
|
+ nv_encoder->dcb = entry;
|
|
+ nv_encoder->or = ffs(entry->or) - 1;
|
|
+
|
|
+ drm_encoder_init(dev, encoder, &nv04_dfp_funcs, type);
|
|
+ drm_encoder_helper_add(encoder, helper);
|
|
+
|
|
+ encoder->possible_crtcs = entry->heads;
|
|
+ encoder->possible_clones = 0;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c
|
|
new file mode 100644
|
|
index 0000000..ef77215
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv04_display.c
|
|
@@ -0,0 +1,287 @@
|
|
+/*
|
|
+ * Copyright 2009 Red Hat Inc.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice shall be included in
|
|
+ * all copies or substantial portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
+ * OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ * Author: Ben Skeggs
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "drm_crtc_helper.h"
|
|
+
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_fb.h"
|
|
+#include "nouveau_hw.h"
|
|
+#include "nouveau_encoder.h"
|
|
+#include "nouveau_connector.h"
|
|
+
|
|
+#define MULTIPLE_ENCODERS(e) (e & (e - 1))
|
|
+
|
|
+static void
|
|
+nv04_display_store_initial_head_owner(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ if (dev_priv->chipset != 0x11) {
|
|
+ dev_priv->crtc_owner = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_44);
|
|
+ goto ownerknown;
|
|
+ }
|
|
+
|
|
+ /* reading CR44 is broken on nv11, so we attempt to infer it */
|
|
+ if (nvReadMC(dev, NV_PBUS_DEBUG_1) & (1 << 28)) /* heads tied, restore both */
|
|
+ dev_priv->crtc_owner = 0x4;
|
|
+ else {
|
|
+ uint8_t slaved_on_A, slaved_on_B;
|
|
+ bool tvA = false;
|
|
+ bool tvB = false;
|
|
+
|
|
+ NVLockVgaCrtcs(dev, false);
|
|
+
|
|
+ slaved_on_B = NVReadVgaCrtc(dev, 1, NV_CIO_CRE_PIXEL_INDEX) &
|
|
+ 0x80;
|
|
+ if (slaved_on_B)
|
|
+ tvB = !(NVReadVgaCrtc(dev, 1, NV_CIO_CRE_LCD__INDEX) &
|
|
+ MASK(NV_CIO_CRE_LCD_LCD_SELECT));
|
|
+
|
|
+ slaved_on_A = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX) &
|
|
+ 0x80;
|
|
+ if (slaved_on_A)
|
|
+ tvA = !(NVReadVgaCrtc(dev, 0, NV_CIO_CRE_LCD__INDEX) &
|
|
+ MASK(NV_CIO_CRE_LCD_LCD_SELECT));
|
|
+
|
|
+ NVLockVgaCrtcs(dev, true);
|
|
+
|
|
+ if (slaved_on_A && !tvA)
|
|
+ dev_priv->crtc_owner = 0x0;
|
|
+ else if (slaved_on_B && !tvB)
|
|
+ dev_priv->crtc_owner = 0x3;
|
|
+ else if (slaved_on_A)
|
|
+ dev_priv->crtc_owner = 0x0;
|
|
+ else if (slaved_on_B)
|
|
+ dev_priv->crtc_owner = 0x3;
|
|
+ else
|
|
+ dev_priv->crtc_owner = 0x0;
|
|
+ }
|
|
+
|
|
+ownerknown:
|
|
+ NV_INFO(dev, "Initial CRTC_OWNER is %d\n", dev_priv->crtc_owner);
|
|
+
|
|
+ /* we need to ensure the heads are not tied henceforth, or reading any
|
|
+ * 8 bit reg on head B will fail
|
|
+ * setting a single arbitrary head solves that */
|
|
+ NVSetOwner(dev, 0);
|
|
+}
|
|
+
|
|
+int
|
|
+nv04_display_create(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct parsed_dcb *dcb = dev_priv->vbios->dcb;
|
|
+ struct drm_encoder *encoder;
|
|
+ struct drm_crtc *crtc;
|
|
+ uint16_t connector[16] = { 0 };
|
|
+ int i, ret;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "\n");
|
|
+
|
|
+ if (nv_two_heads(dev))
|
|
+ nv04_display_store_initial_head_owner(dev);
|
|
+ nouveau_hw_save_vga_fonts(dev, 1);
|
|
+
|
|
+ drm_mode_config_init(dev);
|
|
+ drm_mode_create_scaling_mode_property(dev);
|
|
+ drm_mode_create_dithering_property(dev);
|
|
+
|
|
+ dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs;
|
|
+
|
|
+ dev->mode_config.min_width = 0;
|
|
+ dev->mode_config.min_height = 0;
|
|
+ switch (dev_priv->card_type) {
|
|
+ case NV_04:
|
|
+ dev->mode_config.max_width = 2048;
|
|
+ dev->mode_config.max_height = 2048;
|
|
+ break;
|
|
+ default:
|
|
+ dev->mode_config.max_width = 4096;
|
|
+ dev->mode_config.max_height = 4096;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ dev->mode_config.fb_base = dev_priv->fb_phys;
|
|
+
|
|
+ nv04_crtc_create(dev, 0);
|
|
+ if (nv_two_heads(dev))
|
|
+ nv04_crtc_create(dev, 1);
|
|
+
|
|
+ for (i = 0; i < dcb->entries; i++) {
|
|
+ struct dcb_entry *dcbent = &dcb->entry[i];
|
|
+
|
|
+ switch (dcbent->type) {
|
|
+ case OUTPUT_ANALOG:
|
|
+ ret = nv04_dac_create(dev, dcbent);
|
|
+ break;
|
|
+ case OUTPUT_LVDS:
|
|
+ case OUTPUT_TMDS:
|
|
+ ret = nv04_dfp_create(dev, dcbent);
|
|
+ break;
|
|
+ case OUTPUT_TV:
|
|
+ if (dcbent->location == DCB_LOC_ON_CHIP)
|
|
+ ret = nv17_tv_create(dev, dcbent);
|
|
+ else
|
|
+ ret = nv04_tv_create(dev, dcbent);
|
|
+ break;
|
|
+ default:
|
|
+ NV_WARN(dev, "DCB type %d not known\n", dcbent->type);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (ret)
|
|
+ continue;
|
|
+
|
|
+ connector[dcbent->connector] |= (1 << dcbent->type);
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < dcb->entries; i++) {
|
|
+ struct dcb_entry *dcbent = &dcb->entry[i];
|
|
+ uint16_t encoders;
|
|
+ int type;
|
|
+
|
|
+ encoders = connector[dcbent->connector];
|
|
+ if (!(encoders & (1 << dcbent->type)))
|
|
+ continue;
|
|
+ connector[dcbent->connector] = 0;
|
|
+
|
|
+ switch (dcbent->type) {
|
|
+ case OUTPUT_ANALOG:
|
|
+ if (!MULTIPLE_ENCODERS(encoders))
|
|
+ type = DRM_MODE_CONNECTOR_VGA;
|
|
+ else
|
|
+ type = DRM_MODE_CONNECTOR_DVII;
|
|
+ break;
|
|
+ case OUTPUT_TMDS:
|
|
+ if (!MULTIPLE_ENCODERS(encoders))
|
|
+ type = DRM_MODE_CONNECTOR_DVID;
|
|
+ else
|
|
+ type = DRM_MODE_CONNECTOR_DVII;
|
|
+ break;
|
|
+ case OUTPUT_LVDS:
|
|
+ type = DRM_MODE_CONNECTOR_LVDS;
|
|
+#if 0
|
|
+ /* don't create i2c adapter when lvds ddc not allowed */
|
|
+ if (dcbent->lvdsconf.use_straps_for_mode ||
|
|
+ dev_priv->vbios->fp_no_ddc)
|
|
+ i2c_index = 0xf;
|
|
+#endif
|
|
+ break;
|
|
+ case OUTPUT_TV:
|
|
+ type = DRM_MODE_CONNECTOR_TV;
|
|
+ break;
|
|
+ default:
|
|
+ type = DRM_MODE_CONNECTOR_Unknown;
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ nouveau_connector_create(dev, dcbent->connector, type);
|
|
+ }
|
|
+
|
|
+ /* Save previous state */
|
|
+ NVLockVgaCrtcs(dev, false);
|
|
+
|
|
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
|
|
+ crtc->funcs->save(crtc);
|
|
+
|
|
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
|
+ struct drm_encoder_helper_funcs *func = encoder->helper_private;
|
|
+
|
|
+ func->save(encoder);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv04_display_destroy(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_encoder *encoder;
|
|
+ struct drm_crtc *crtc;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "\n");
|
|
+
|
|
+ /* Turn every CRTC off. */
|
|
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
|
+ struct drm_mode_set modeset = {
|
|
+ .crtc = crtc,
|
|
+ };
|
|
+
|
|
+ crtc->funcs->set_config(&modeset);
|
|
+ }
|
|
+
|
|
+ /* Restore state */
|
|
+ NVLockVgaCrtcs(dev, false);
|
|
+
|
|
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
|
+ struct drm_encoder_helper_funcs *func = encoder->helper_private;
|
|
+
|
|
+ func->restore(encoder);
|
|
+ }
|
|
+
|
|
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
|
|
+ crtc->funcs->restore(crtc);
|
|
+
|
|
+ drm_mode_config_cleanup(dev);
|
|
+
|
|
+ nouveau_hw_save_vga_fonts(dev, 0);
|
|
+}
|
|
+
|
|
+void
|
|
+nv04_display_restore(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct drm_encoder *encoder;
|
|
+ struct drm_crtc *crtc;
|
|
+
|
|
+ NVLockVgaCrtcs(dev, false);
|
|
+
|
|
+ /* meh.. modeset apparently doesn't setup all the regs and depends
|
|
+ * on pre-existing state, for now load the state of the card *before*
|
|
+ * nouveau was loaded, and then do a modeset.
|
|
+ *
|
|
+ * best thing to do probably is to make save/restore routines not
|
|
+ * save/restore "pre-load" state, but more general so we can save
|
|
+ * on suspend too.
|
|
+ */
|
|
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
|
+ struct drm_encoder_helper_funcs *func = encoder->helper_private;
|
|
+
|
|
+ func->restore(encoder);
|
|
+ }
|
|
+
|
|
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
|
|
+ crtc->funcs->restore(crtc);
|
|
+
|
|
+ if (nv_two_heads(dev)) {
|
|
+ NV_INFO(dev, "Restoring CRTC_OWNER to %d.\n",
|
|
+ dev_priv->crtc_owner);
|
|
+ NVSetOwner(dev, dev_priv->crtc_owner);
|
|
+ }
|
|
+
|
|
+ NVLockVgaCrtcs(dev, true);
|
|
+}
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nv04_fb.c b/drivers/gpu/drm/nouveau/nv04_fb.c
|
|
new file mode 100644
|
|
index 0000000..638cf60
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv04_fb.c
|
|
@@ -0,0 +1,21 @@
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_drm.h"
|
|
+
|
|
+int
|
|
+nv04_fb_init(struct drm_device *dev)
|
|
+{
|
|
+ /* This is what the DDX did for NV_ARCH_04, but a mmio-trace shows
|
|
+ * nvidia reading PFB_CFG_0, then writing back its original value.
|
|
+ * (which was 0x701114 in this case)
|
|
+ */
|
|
+
|
|
+ nv_wr32(dev, NV04_PFB_CFG0, 0x1114);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv04_fb_takedown(struct drm_device *dev)
|
|
+{
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nv04_fbcon.c b/drivers/gpu/drm/nouveau/nv04_fbcon.c
|
|
new file mode 100644
|
|
index 0000000..fd01caa
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv04_fbcon.c
|
|
@@ -0,0 +1,312 @@
|
|
+/*
|
|
+ * Copyright 2009 Ben Skeggs
|
|
+ * Copyright 2008 Stuart Bennett
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the next
|
|
+ * paragraph) shall be included in all copies or substantial portions of the
|
|
+ * Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
+ * DEALINGS IN THE SOFTWARE.
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_dma.h"
|
|
+#include "nouveau_fbcon.h"
|
|
+
|
|
+void
|
|
+nv04_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region)
|
|
+{
|
|
+ struct nouveau_fbcon_par *par = info->par;
|
|
+ struct drm_device *dev = par->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_channel *chan = dev_priv->channel;
|
|
+
|
|
+ if (info->state != FBINFO_STATE_RUNNING)
|
|
+ return;
|
|
+
|
|
+ if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 4)) {
|
|
+ nouveau_fbcon_gpu_lockup(info);
|
|
+ }
|
|
+
|
|
+ if (info->flags & FBINFO_HWACCEL_DISABLED) {
|
|
+ cfb_copyarea(info, region);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ BEGIN_RING(chan, NvSubImageBlit, 0x0300, 3);
|
|
+ OUT_RING(chan, (region->sy << 16) | region->sx);
|
|
+ OUT_RING(chan, (region->dy << 16) | region->dx);
|
|
+ OUT_RING(chan, (region->height << 16) | region->width);
|
|
+ FIRE_RING(chan);
|
|
+}
|
|
+
|
|
+void
|
|
+nv04_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
|
|
+{
|
|
+ struct nouveau_fbcon_par *par = info->par;
|
|
+ struct drm_device *dev = par->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_channel *chan = dev_priv->channel;
|
|
+
|
|
+ if (info->state != FBINFO_STATE_RUNNING)
|
|
+ return;
|
|
+
|
|
+ if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 7)) {
|
|
+ nouveau_fbcon_gpu_lockup(info);
|
|
+ }
|
|
+
|
|
+ if (info->flags & FBINFO_HWACCEL_DISABLED) {
|
|
+ cfb_fillrect(info, rect);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ BEGIN_RING(chan, NvSubGdiRect, 0x02fc, 1);
|
|
+ OUT_RING(chan, (rect->rop != ROP_COPY) ? 1 : 3);
|
|
+ BEGIN_RING(chan, NvSubGdiRect, 0x03fc, 1);
|
|
+ if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
|
|
+ info->fix.visual == FB_VISUAL_DIRECTCOLOR)
|
|
+ OUT_RING(chan, ((uint32_t *)info->pseudo_palette)[rect->color]);
|
|
+ else
|
|
+ OUT_RING(chan, rect->color);
|
|
+ BEGIN_RING(chan, NvSubGdiRect, 0x0400, 2);
|
|
+ OUT_RING(chan, (rect->dx << 16) | rect->dy);
|
|
+ OUT_RING(chan, (rect->width << 16) | rect->height);
|
|
+ FIRE_RING(chan);
|
|
+}
|
|
+
|
|
+void
|
|
+nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
|
|
+{
|
|
+ struct nouveau_fbcon_par *par = info->par;
|
|
+ struct drm_device *dev = par->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_channel *chan = dev_priv->channel;
|
|
+ uint32_t fg;
|
|
+ uint32_t bg;
|
|
+ uint32_t dsize;
|
|
+ uint32_t width;
|
|
+ uint32_t *data = (uint32_t *)image->data;
|
|
+
|
|
+ if (info->state != FBINFO_STATE_RUNNING)
|
|
+ return;
|
|
+
|
|
+ if (image->depth != 1) {
|
|
+ cfb_imageblit(info, image);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 8)) {
|
|
+ nouveau_fbcon_gpu_lockup(info);
|
|
+ }
|
|
+
|
|
+ if (info->flags & FBINFO_HWACCEL_DISABLED) {
|
|
+ cfb_imageblit(info, image);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ width = (image->width + 31) & ~31;
|
|
+ dsize = (width * image->height) >> 5;
|
|
+
|
|
+ if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
|
|
+ info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
|
|
+ fg = ((uint32_t *) info->pseudo_palette)[image->fg_color];
|
|
+ bg = ((uint32_t *) info->pseudo_palette)[image->bg_color];
|
|
+ } else {
|
|
+ fg = image->fg_color;
|
|
+ bg = image->bg_color;
|
|
+ }
|
|
+
|
|
+ BEGIN_RING(chan, NvSubGdiRect, 0x0be4, 7);
|
|
+ OUT_RING(chan, (image->dy << 16) | (image->dx & 0xffff));
|
|
+ OUT_RING(chan, ((image->dy + image->height) << 16) |
|
|
+ ((image->dx + image->width) & 0xffff));
|
|
+ OUT_RING(chan, bg);
|
|
+ OUT_RING(chan, fg);
|
|
+ OUT_RING(chan, (image->height << 16) | image->width);
|
|
+ OUT_RING(chan, (image->height << 16) | width);
|
|
+ OUT_RING(chan, (image->dy << 16) | (image->dx & 0xffff));
|
|
+
|
|
+ while (dsize) {
|
|
+ int iter_len = dsize > 128 ? 128 : dsize;
|
|
+
|
|
+ if (RING_SPACE(chan, iter_len + 1)) {
|
|
+ nouveau_fbcon_gpu_lockup(info);
|
|
+ cfb_imageblit(info, image);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ BEGIN_RING(chan, NvSubGdiRect, 0x0c00, iter_len);
|
|
+ OUT_RINGp(chan, data, iter_len);
|
|
+ data += iter_len;
|
|
+ dsize -= iter_len;
|
|
+ }
|
|
+
|
|
+ FIRE_RING(chan);
|
|
+}
|
|
+
|
|
+static int
|
|
+nv04_fbcon_grobj_new(struct drm_device *dev, int class, uint32_t handle)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_gpuobj *obj = NULL;
|
|
+ int ret;
|
|
+
|
|
+ ret = nouveau_gpuobj_gr_new(dev_priv->channel, class, &obj);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, handle, obj, NULL);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nv04_fbcon_accel_init(struct fb_info *info)
|
|
+{
|
|
+ struct nouveau_fbcon_par *par = info->par;
|
|
+ struct drm_device *dev = par->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_channel *chan = dev_priv->channel;
|
|
+ const int sub = NvSubCtxSurf2D;
|
|
+ int surface_fmt, pattern_fmt, rect_fmt;
|
|
+ int ret;
|
|
+
|
|
+ switch (info->var.bits_per_pixel) {
|
|
+ case 8:
|
|
+ surface_fmt = 1;
|
|
+ pattern_fmt = 3;
|
|
+ rect_fmt = 3;
|
|
+ break;
|
|
+ case 16:
|
|
+ surface_fmt = 4;
|
|
+ pattern_fmt = 1;
|
|
+ rect_fmt = 1;
|
|
+ break;
|
|
+ case 32:
|
|
+ switch (info->var.transp.length) {
|
|
+ case 0: /* depth 24 */
|
|
+ case 8: /* depth 32 */
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ surface_fmt = 6;
|
|
+ pattern_fmt = 3;
|
|
+ rect_fmt = 3;
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ret = nv04_fbcon_grobj_new(dev, dev_priv->card_type >= NV_10 ?
|
|
+ 0x0062 : 0x0042, NvCtxSurf2D);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = nv04_fbcon_grobj_new(dev, 0x0019, NvClipRect);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = nv04_fbcon_grobj_new(dev, 0x0043, NvRop);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = nv04_fbcon_grobj_new(dev, 0x0044, NvImagePatt);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = nv04_fbcon_grobj_new(dev, 0x004a, NvGdiRect);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = nv04_fbcon_grobj_new(dev, dev_priv->card_type >= NV_10 ?
|
|
+ 0x009f : 0x005f, NvImageBlit);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ if (RING_SPACE(chan, 49)) {
|
|
+ nouveau_fbcon_gpu_lockup(info);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ BEGIN_RING(chan, sub, 0x0000, 1);
|
|
+ OUT_RING(chan, NvCtxSurf2D);
|
|
+ BEGIN_RING(chan, sub, 0x0184, 2);
|
|
+ OUT_RING(chan, NvDmaFB);
|
|
+ OUT_RING(chan, NvDmaFB);
|
|
+ BEGIN_RING(chan, sub, 0x0300, 4);
|
|
+ OUT_RING(chan, surface_fmt);
|
|
+ OUT_RING(chan, info->fix.line_length | (info->fix.line_length << 16));
|
|
+ OUT_RING(chan, info->fix.smem_start - dev->mode_config.fb_base);
|
|
+ OUT_RING(chan, info->fix.smem_start - dev->mode_config.fb_base);
|
|
+
|
|
+ BEGIN_RING(chan, sub, 0x0000, 1);
|
|
+ OUT_RING(chan, NvRop);
|
|
+ BEGIN_RING(chan, sub, 0x0300, 1);
|
|
+ OUT_RING(chan, 0x55);
|
|
+
|
|
+ BEGIN_RING(chan, sub, 0x0000, 1);
|
|
+ OUT_RING(chan, NvImagePatt);
|
|
+ BEGIN_RING(chan, sub, 0x0300, 8);
|
|
+ OUT_RING(chan, pattern_fmt);
|
|
+#ifdef __BIG_ENDIAN
|
|
+ OUT_RING(chan, 2);
|
|
+#else
|
|
+ OUT_RING(chan, 1);
|
|
+#endif
|
|
+ OUT_RING(chan, 0);
|
|
+ OUT_RING(chan, 1);
|
|
+ OUT_RING(chan, ~0);
|
|
+ OUT_RING(chan, ~0);
|
|
+ OUT_RING(chan, ~0);
|
|
+ OUT_RING(chan, ~0);
|
|
+
|
|
+ BEGIN_RING(chan, sub, 0x0000, 1);
|
|
+ OUT_RING(chan, NvClipRect);
|
|
+ BEGIN_RING(chan, sub, 0x0300, 2);
|
|
+ OUT_RING(chan, 0);
|
|
+ OUT_RING(chan, (info->var.yres_virtual << 16) | info->var.xres_virtual);
|
|
+
|
|
+ BEGIN_RING(chan, NvSubImageBlit, 0x0000, 1);
|
|
+ OUT_RING(chan, NvImageBlit);
|
|
+ BEGIN_RING(chan, NvSubImageBlit, 0x019c, 1);
|
|
+ OUT_RING(chan, NvCtxSurf2D);
|
|
+ BEGIN_RING(chan, NvSubImageBlit, 0x02fc, 1);
|
|
+ OUT_RING(chan, 3);
|
|
+
|
|
+ BEGIN_RING(chan, NvSubGdiRect, 0x0000, 1);
|
|
+ OUT_RING(chan, NvGdiRect);
|
|
+ BEGIN_RING(chan, NvSubGdiRect, 0x0198, 1);
|
|
+ OUT_RING(chan, NvCtxSurf2D);
|
|
+ BEGIN_RING(chan, NvSubGdiRect, 0x0188, 2);
|
|
+ OUT_RING(chan, NvImagePatt);
|
|
+ OUT_RING(chan, NvRop);
|
|
+ BEGIN_RING(chan, NvSubGdiRect, 0x0304, 1);
|
|
+ OUT_RING(chan, 1);
|
|
+ BEGIN_RING(chan, NvSubGdiRect, 0x0300, 1);
|
|
+ OUT_RING(chan, rect_fmt);
|
|
+ BEGIN_RING(chan, NvSubGdiRect, 0x02fc, 1);
|
|
+ OUT_RING(chan, 3);
|
|
+
|
|
+ FIRE_RING(chan);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nv04_fifo.c b/drivers/gpu/drm/nouveau/nv04_fifo.c
|
|
new file mode 100644
|
|
index 0000000..f31347b
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv04_fifo.c
|
|
@@ -0,0 +1,305 @@
|
|
+/*
|
|
+ * Copyright (C) 2007 Ben Skeggs.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "nouveau_drv.h"
|
|
+
|
|
+#define NV04_RAMFC(c) (dev_priv->ramfc_offset + ((c) * NV04_RAMFC__SIZE))
|
|
+#define NV04_RAMFC__SIZE 32
|
|
+#define NV04_RAMFC_DMA_PUT 0x00
|
|
+#define NV04_RAMFC_DMA_GET 0x04
|
|
+#define NV04_RAMFC_DMA_INSTANCE 0x08
|
|
+#define NV04_RAMFC_DMA_STATE 0x0C
|
|
+#define NV04_RAMFC_DMA_FETCH 0x10
|
|
+#define NV04_RAMFC_ENGINE 0x14
|
|
+#define NV04_RAMFC_PULL1_ENGINE 0x18
|
|
+
|
|
+#define RAMFC_WR(offset, val) nv_wo32(dev, chan->ramfc->gpuobj, \
|
|
+ NV04_RAMFC_##offset/4, (val))
|
|
+#define RAMFC_RD(offset) nv_ro32(dev, chan->ramfc->gpuobj, \
|
|
+ NV04_RAMFC_##offset/4)
|
|
+
|
|
+void
|
|
+nv04_fifo_disable(struct drm_device *dev)
|
|
+{
|
|
+ uint32_t tmp;
|
|
+
|
|
+ tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUSH);
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, tmp & ~1);
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, 0);
|
|
+ tmp = nv_rd32(dev, NV03_PFIFO_CACHE1_PULL1);
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, tmp & ~1);
|
|
+}
|
|
+
|
|
+void
|
|
+nv04_fifo_enable(struct drm_device *dev)
|
|
+{
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, 1);
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1);
|
|
+}
|
|
+
|
|
+bool
|
|
+nv04_fifo_reassign(struct drm_device *dev, bool enable)
|
|
+{
|
|
+ uint32_t reassign = nv_rd32(dev, NV03_PFIFO_CACHES);
|
|
+
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHES, enable ? 1 : 0);
|
|
+ return (reassign == 1);
|
|
+}
|
|
+
|
|
+bool
|
|
+nv04_fifo_cache_flush(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
|
|
+ uint64_t start = ptimer->read(dev);
|
|
+
|
|
+ do {
|
|
+ if (nv_rd32(dev, NV03_PFIFO_CACHE1_GET) ==
|
|
+ nv_rd32(dev, NV03_PFIFO_CACHE1_PUT))
|
|
+ return true;
|
|
+
|
|
+ } while (ptimer->read(dev) - start < 100000000);
|
|
+
|
|
+ NV_ERROR(dev, "Timeout flushing the PFIFO cache.\n");
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+bool
|
|
+nv04_fifo_cache_pull(struct drm_device *dev, bool enable)
|
|
+{
|
|
+ uint32_t pull = nv_rd32(dev, NV04_PFIFO_CACHE1_PULL0);
|
|
+
|
|
+ if (enable) {
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, pull | 1);
|
|
+ } else {
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, pull & ~1);
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_HASH, 0);
|
|
+ }
|
|
+
|
|
+ return !!(pull & 1);
|
|
+}
|
|
+
|
|
+int
|
|
+nv04_fifo_channel_id(struct drm_device *dev)
|
|
+{
|
|
+ return nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH1) &
|
|
+ NV03_PFIFO_CACHE1_PUSH1_CHID_MASK;
|
|
+}
|
|
+
|
|
+int
|
|
+nv04_fifo_create_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ int ret;
|
|
+
|
|
+ ret = nouveau_gpuobj_new_fake(dev, NV04_RAMFC(chan->id), ~0,
|
|
+ NV04_RAMFC__SIZE,
|
|
+ NVOBJ_FLAG_ZERO_ALLOC |
|
|
+ NVOBJ_FLAG_ZERO_FREE,
|
|
+ NULL, &chan->ramfc);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* Setup initial state */
|
|
+ dev_priv->engine.instmem.prepare_access(dev, true);
|
|
+ RAMFC_WR(DMA_PUT, chan->pushbuf_base);
|
|
+ RAMFC_WR(DMA_GET, chan->pushbuf_base);
|
|
+ RAMFC_WR(DMA_INSTANCE, chan->pushbuf->instance >> 4);
|
|
+ RAMFC_WR(DMA_FETCH, (NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES |
|
|
+ NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES |
|
|
+ NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8 |
|
|
+#ifdef __BIG_ENDIAN
|
|
+ NV_PFIFO_CACHE1_BIG_ENDIAN |
|
|
+#endif
|
|
+ 0));
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+
|
|
+ /* enable the fifo dma operation */
|
|
+ nv_wr32(dev, NV04_PFIFO_MODE,
|
|
+ nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id));
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv04_fifo_destroy_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+
|
|
+ nv_wr32(dev, NV04_PFIFO_MODE,
|
|
+ nv_rd32(dev, NV04_PFIFO_MODE) & ~(1 << chan->id));
|
|
+
|
|
+ nouveau_gpuobj_ref_del(dev, &chan->ramfc);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv04_fifo_do_load_context(struct drm_device *dev, int chid)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t fc = NV04_RAMFC(chid), tmp;
|
|
+
|
|
+ dev_priv->engine.instmem.prepare_access(dev, false);
|
|
+
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUT, nv_ri32(dev, fc + 0));
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_GET, nv_ri32(dev, fc + 4));
|
|
+ tmp = nv_ri32(dev, fc + 8);
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE, tmp & 0xFFFF);
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT, tmp >> 16);
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_STATE, nv_ri32(dev, fc + 12));
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_FETCH, nv_ri32(dev, fc + 16));
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_ENGINE, nv_ri32(dev, fc + 20));
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_PULL1, nv_ri32(dev, fc + 24));
|
|
+
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0);
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0);
|
|
+}
|
|
+
|
|
+int
|
|
+nv04_fifo_load_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ uint32_t tmp;
|
|
+
|
|
+ nv_wr32(chan->dev, NV03_PFIFO_CACHE1_PUSH1,
|
|
+ NV03_PFIFO_CACHE1_PUSH1_DMA | chan->id);
|
|
+ nv04_fifo_do_load_context(chan->dev, chan->id);
|
|
+ nv_wr32(chan->dev, NV04_PFIFO_CACHE1_DMA_PUSH, 1);
|
|
+
|
|
+ /* Reset NV04_PFIFO_CACHE1_DMA_CTL_AT_INFO to INVALID */
|
|
+ tmp = nv_rd32(chan->dev, NV04_PFIFO_CACHE1_DMA_CTL) & ~(1 << 31);
|
|
+ nv_wr32(chan->dev, NV04_PFIFO_CACHE1_DMA_CTL, tmp);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nv04_fifo_unload_context(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
|
|
+ struct nouveau_channel *chan = NULL;
|
|
+ uint32_t tmp;
|
|
+ int chid;
|
|
+
|
|
+ chid = pfifo->channel_id(dev);
|
|
+ if (chid < 0 || chid >= dev_priv->engine.fifo.channels)
|
|
+ return 0;
|
|
+
|
|
+ chan = dev_priv->fifos[chid];
|
|
+ if (!chan) {
|
|
+ NV_ERROR(dev, "Inactive channel on PFIFO: %d\n", chid);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ dev_priv->engine.instmem.prepare_access(dev, true);
|
|
+ RAMFC_WR(DMA_PUT, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT));
|
|
+ RAMFC_WR(DMA_GET, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET));
|
|
+ tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT) << 16;
|
|
+ tmp |= nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE);
|
|
+ RAMFC_WR(DMA_INSTANCE, tmp);
|
|
+ RAMFC_WR(DMA_STATE, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_STATE));
|
|
+ RAMFC_WR(DMA_FETCH, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_FETCH));
|
|
+ RAMFC_WR(ENGINE, nv_rd32(dev, NV04_PFIFO_CACHE1_ENGINE));
|
|
+ RAMFC_WR(PULL1_ENGINE, nv_rd32(dev, NV04_PFIFO_CACHE1_PULL1));
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+
|
|
+ nv04_fifo_do_load_context(dev, pfifo->channels - 1);
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void
|
|
+nv04_fifo_init_reset(struct drm_device *dev)
|
|
+{
|
|
+ nv_wr32(dev, NV03_PMC_ENABLE,
|
|
+ nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PFIFO);
|
|
+ nv_wr32(dev, NV03_PMC_ENABLE,
|
|
+ nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PFIFO);
|
|
+
|
|
+ nv_wr32(dev, 0x003224, 0x000f0078);
|
|
+ nv_wr32(dev, 0x002044, 0x0101ffff);
|
|
+ nv_wr32(dev, 0x002040, 0x000000ff);
|
|
+ nv_wr32(dev, 0x002500, 0x00000000);
|
|
+ nv_wr32(dev, 0x003000, 0x00000000);
|
|
+ nv_wr32(dev, 0x003050, 0x00000000);
|
|
+ nv_wr32(dev, 0x003200, 0x00000000);
|
|
+ nv_wr32(dev, 0x003250, 0x00000000);
|
|
+ nv_wr32(dev, 0x003220, 0x00000000);
|
|
+
|
|
+ nv_wr32(dev, 0x003250, 0x00000000);
|
|
+ nv_wr32(dev, 0x003270, 0x00000000);
|
|
+ nv_wr32(dev, 0x003210, 0x00000000);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv04_fifo_init_ramxx(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ nv_wr32(dev, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ |
|
|
+ ((dev_priv->ramht_bits - 9) << 16) |
|
|
+ (dev_priv->ramht_offset >> 8));
|
|
+ nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro_offset>>8);
|
|
+ nv_wr32(dev, NV03_PFIFO_RAMFC, dev_priv->ramfc_offset >> 8);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv04_fifo_init_intr(struct drm_device *dev)
|
|
+{
|
|
+ nv_wr32(dev, 0x002100, 0xffffffff);
|
|
+ nv_wr32(dev, 0x002140, 0xffffffff);
|
|
+}
|
|
+
|
|
+int
|
|
+nv04_fifo_init(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
|
|
+ int i;
|
|
+
|
|
+ nv04_fifo_init_reset(dev);
|
|
+ nv04_fifo_init_ramxx(dev);
|
|
+
|
|
+ nv04_fifo_do_load_context(dev, pfifo->channels - 1);
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1);
|
|
+
|
|
+ nv04_fifo_init_intr(dev);
|
|
+ pfifo->enable(dev);
|
|
+
|
|
+ for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
|
|
+ if (dev_priv->fifos[i]) {
|
|
+ uint32_t mode = nv_rd32(dev, NV04_PFIFO_MODE);
|
|
+ nv_wr32(dev, NV04_PFIFO_MODE, mode | (1 << i));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nv04_graph.c b/drivers/gpu/drm/nouveau/nv04_graph.c
|
|
new file mode 100644
|
|
index 0000000..e260986
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv04_graph.c
|
|
@@ -0,0 +1,584 @@
|
|
+/*
|
|
+ * Copyright 2007 Stephane Marchesin
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the next
|
|
+ * paragraph) shall be included in all copies or substantial portions of the
|
|
+ * Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
+ * DEALINGS IN THE SOFTWARE.
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "nouveau_drm.h"
|
|
+#include "nouveau_drv.h"
|
|
+
|
|
+static uint32_t nv04_graph_ctx_regs[] = {
|
|
+ 0x0040053c,
|
|
+ 0x00400544,
|
|
+ 0x00400540,
|
|
+ 0x00400548,
|
|
+ NV04_PGRAPH_CTX_SWITCH1,
|
|
+ NV04_PGRAPH_CTX_SWITCH2,
|
|
+ NV04_PGRAPH_CTX_SWITCH3,
|
|
+ NV04_PGRAPH_CTX_SWITCH4,
|
|
+ NV04_PGRAPH_CTX_CACHE1,
|
|
+ NV04_PGRAPH_CTX_CACHE2,
|
|
+ NV04_PGRAPH_CTX_CACHE3,
|
|
+ NV04_PGRAPH_CTX_CACHE4,
|
|
+ 0x00400184,
|
|
+ 0x004001a4,
|
|
+ 0x004001c4,
|
|
+ 0x004001e4,
|
|
+ 0x00400188,
|
|
+ 0x004001a8,
|
|
+ 0x004001c8,
|
|
+ 0x004001e8,
|
|
+ 0x0040018c,
|
|
+ 0x004001ac,
|
|
+ 0x004001cc,
|
|
+ 0x004001ec,
|
|
+ 0x00400190,
|
|
+ 0x004001b0,
|
|
+ 0x004001d0,
|
|
+ 0x004001f0,
|
|
+ 0x00400194,
|
|
+ 0x004001b4,
|
|
+ 0x004001d4,
|
|
+ 0x004001f4,
|
|
+ 0x00400198,
|
|
+ 0x004001b8,
|
|
+ 0x004001d8,
|
|
+ 0x004001f8,
|
|
+ 0x0040019c,
|
|
+ 0x004001bc,
|
|
+ 0x004001dc,
|
|
+ 0x004001fc,
|
|
+ 0x00400174,
|
|
+ NV04_PGRAPH_DMA_START_0,
|
|
+ NV04_PGRAPH_DMA_START_1,
|
|
+ NV04_PGRAPH_DMA_LENGTH,
|
|
+ NV04_PGRAPH_DMA_MISC,
|
|
+ NV04_PGRAPH_DMA_PITCH,
|
|
+ NV04_PGRAPH_BOFFSET0,
|
|
+ NV04_PGRAPH_BBASE0,
|
|
+ NV04_PGRAPH_BLIMIT0,
|
|
+ NV04_PGRAPH_BOFFSET1,
|
|
+ NV04_PGRAPH_BBASE1,
|
|
+ NV04_PGRAPH_BLIMIT1,
|
|
+ NV04_PGRAPH_BOFFSET2,
|
|
+ NV04_PGRAPH_BBASE2,
|
|
+ NV04_PGRAPH_BLIMIT2,
|
|
+ NV04_PGRAPH_BOFFSET3,
|
|
+ NV04_PGRAPH_BBASE3,
|
|
+ NV04_PGRAPH_BLIMIT3,
|
|
+ NV04_PGRAPH_BOFFSET4,
|
|
+ NV04_PGRAPH_BBASE4,
|
|
+ NV04_PGRAPH_BLIMIT4,
|
|
+ NV04_PGRAPH_BOFFSET5,
|
|
+ NV04_PGRAPH_BBASE5,
|
|
+ NV04_PGRAPH_BLIMIT5,
|
|
+ NV04_PGRAPH_BPITCH0,
|
|
+ NV04_PGRAPH_BPITCH1,
|
|
+ NV04_PGRAPH_BPITCH2,
|
|
+ NV04_PGRAPH_BPITCH3,
|
|
+ NV04_PGRAPH_BPITCH4,
|
|
+ NV04_PGRAPH_SURFACE,
|
|
+ NV04_PGRAPH_STATE,
|
|
+ NV04_PGRAPH_BSWIZZLE2,
|
|
+ NV04_PGRAPH_BSWIZZLE5,
|
|
+ NV04_PGRAPH_BPIXEL,
|
|
+ NV04_PGRAPH_NOTIFY,
|
|
+ NV04_PGRAPH_PATT_COLOR0,
|
|
+ NV04_PGRAPH_PATT_COLOR1,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x00,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x04,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x08,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x0c,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x10,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x14,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x18,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x1c,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x20,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x24,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x28,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x2c,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x30,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x34,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x38,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x3c,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x40,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x44,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x48,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x4c,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x50,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x54,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x58,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x5c,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x60,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x64,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x68,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x6c,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x70,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x74,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x78,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x7c,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x80,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x84,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x88,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x8c,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x90,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x94,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x98,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0x9c,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0xa0,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0xa4,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0xa8,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0xac,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0xb0,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0xb4,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0xb8,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0xbc,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0xc0,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0xc4,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0xc8,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0xcc,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0xd0,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0xd4,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0xd8,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0xdc,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0xe0,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0xe4,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0xe8,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0xec,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0xf0,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0xf4,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0xf8,
|
|
+ NV04_PGRAPH_PATT_COLORRAM+0xfc,
|
|
+ NV04_PGRAPH_PATTERN,
|
|
+ 0x0040080c,
|
|
+ NV04_PGRAPH_PATTERN_SHAPE,
|
|
+ 0x00400600,
|
|
+ NV04_PGRAPH_ROP3,
|
|
+ NV04_PGRAPH_CHROMA,
|
|
+ NV04_PGRAPH_BETA_AND,
|
|
+ NV04_PGRAPH_BETA_PREMULT,
|
|
+ NV04_PGRAPH_CONTROL0,
|
|
+ NV04_PGRAPH_CONTROL1,
|
|
+ NV04_PGRAPH_CONTROL2,
|
|
+ NV04_PGRAPH_BLEND,
|
|
+ NV04_PGRAPH_STORED_FMT,
|
|
+ NV04_PGRAPH_SOURCE_COLOR,
|
|
+ 0x00400560,
|
|
+ 0x00400568,
|
|
+ 0x00400564,
|
|
+ 0x0040056c,
|
|
+ 0x00400400,
|
|
+ 0x00400480,
|
|
+ 0x00400404,
|
|
+ 0x00400484,
|
|
+ 0x00400408,
|
|
+ 0x00400488,
|
|
+ 0x0040040c,
|
|
+ 0x0040048c,
|
|
+ 0x00400410,
|
|
+ 0x00400490,
|
|
+ 0x00400414,
|
|
+ 0x00400494,
|
|
+ 0x00400418,
|
|
+ 0x00400498,
|
|
+ 0x0040041c,
|
|
+ 0x0040049c,
|
|
+ 0x00400420,
|
|
+ 0x004004a0,
|
|
+ 0x00400424,
|
|
+ 0x004004a4,
|
|
+ 0x00400428,
|
|
+ 0x004004a8,
|
|
+ 0x0040042c,
|
|
+ 0x004004ac,
|
|
+ 0x00400430,
|
|
+ 0x004004b0,
|
|
+ 0x00400434,
|
|
+ 0x004004b4,
|
|
+ 0x00400438,
|
|
+ 0x004004b8,
|
|
+ 0x0040043c,
|
|
+ 0x004004bc,
|
|
+ 0x00400440,
|
|
+ 0x004004c0,
|
|
+ 0x00400444,
|
|
+ 0x004004c4,
|
|
+ 0x00400448,
|
|
+ 0x004004c8,
|
|
+ 0x0040044c,
|
|
+ 0x004004cc,
|
|
+ 0x00400450,
|
|
+ 0x004004d0,
|
|
+ 0x00400454,
|
|
+ 0x004004d4,
|
|
+ 0x00400458,
|
|
+ 0x004004d8,
|
|
+ 0x0040045c,
|
|
+ 0x004004dc,
|
|
+ 0x00400460,
|
|
+ 0x004004e0,
|
|
+ 0x00400464,
|
|
+ 0x004004e4,
|
|
+ 0x00400468,
|
|
+ 0x004004e8,
|
|
+ 0x0040046c,
|
|
+ 0x004004ec,
|
|
+ 0x00400470,
|
|
+ 0x004004f0,
|
|
+ 0x00400474,
|
|
+ 0x004004f4,
|
|
+ 0x00400478,
|
|
+ 0x004004f8,
|
|
+ 0x0040047c,
|
|
+ 0x004004fc,
|
|
+ 0x00400534,
|
|
+ 0x00400538,
|
|
+ 0x00400514,
|
|
+ 0x00400518,
|
|
+ 0x0040051c,
|
|
+ 0x00400520,
|
|
+ 0x00400524,
|
|
+ 0x00400528,
|
|
+ 0x0040052c,
|
|
+ 0x00400530,
|
|
+ 0x00400d00,
|
|
+ 0x00400d40,
|
|
+ 0x00400d80,
|
|
+ 0x00400d04,
|
|
+ 0x00400d44,
|
|
+ 0x00400d84,
|
|
+ 0x00400d08,
|
|
+ 0x00400d48,
|
|
+ 0x00400d88,
|
|
+ 0x00400d0c,
|
|
+ 0x00400d4c,
|
|
+ 0x00400d8c,
|
|
+ 0x00400d10,
|
|
+ 0x00400d50,
|
|
+ 0x00400d90,
|
|
+ 0x00400d14,
|
|
+ 0x00400d54,
|
|
+ 0x00400d94,
|
|
+ 0x00400d18,
|
|
+ 0x00400d58,
|
|
+ 0x00400d98,
|
|
+ 0x00400d1c,
|
|
+ 0x00400d5c,
|
|
+ 0x00400d9c,
|
|
+ 0x00400d20,
|
|
+ 0x00400d60,
|
|
+ 0x00400da0,
|
|
+ 0x00400d24,
|
|
+ 0x00400d64,
|
|
+ 0x00400da4,
|
|
+ 0x00400d28,
|
|
+ 0x00400d68,
|
|
+ 0x00400da8,
|
|
+ 0x00400d2c,
|
|
+ 0x00400d6c,
|
|
+ 0x00400dac,
|
|
+ 0x00400d30,
|
|
+ 0x00400d70,
|
|
+ 0x00400db0,
|
|
+ 0x00400d34,
|
|
+ 0x00400d74,
|
|
+ 0x00400db4,
|
|
+ 0x00400d38,
|
|
+ 0x00400d78,
|
|
+ 0x00400db8,
|
|
+ 0x00400d3c,
|
|
+ 0x00400d7c,
|
|
+ 0x00400dbc,
|
|
+ 0x00400590,
|
|
+ 0x00400594,
|
|
+ 0x00400598,
|
|
+ 0x0040059c,
|
|
+ 0x004005a8,
|
|
+ 0x004005ac,
|
|
+ 0x004005b0,
|
|
+ 0x004005b4,
|
|
+ 0x004005c0,
|
|
+ 0x004005c4,
|
|
+ 0x004005c8,
|
|
+ 0x004005cc,
|
|
+ 0x004005d0,
|
|
+ 0x004005d4,
|
|
+ 0x004005d8,
|
|
+ 0x004005dc,
|
|
+ 0x004005e0,
|
|
+ NV04_PGRAPH_PASSTHRU_0,
|
|
+ NV04_PGRAPH_PASSTHRU_1,
|
|
+ NV04_PGRAPH_PASSTHRU_2,
|
|
+ NV04_PGRAPH_DVD_COLORFMT,
|
|
+ NV04_PGRAPH_SCALED_FORMAT,
|
|
+ NV04_PGRAPH_MISC24_0,
|
|
+ NV04_PGRAPH_MISC24_1,
|
|
+ NV04_PGRAPH_MISC24_2,
|
|
+ 0x00400500,
|
|
+ 0x00400504,
|
|
+ NV04_PGRAPH_VALID1,
|
|
+ NV04_PGRAPH_VALID2,
|
|
+ NV04_PGRAPH_DEBUG_3
|
|
+};
|
|
+
|
|
+struct graph_state {
|
|
+ int nv04[ARRAY_SIZE(nv04_graph_ctx_regs)];
|
|
+};
|
|
+
|
|
+struct nouveau_channel *
|
|
+nv04_graph_channel(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ int chid = dev_priv->engine.fifo.channels;
|
|
+
|
|
+ if (nv_rd32(dev, NV04_PGRAPH_CTX_CONTROL) & 0x00010000)
|
|
+ chid = nv_rd32(dev, NV04_PGRAPH_CTX_USER) >> 24;
|
|
+
|
|
+ if (chid >= dev_priv->engine.fifo.channels)
|
|
+ return NULL;
|
|
+
|
|
+ return dev_priv->fifos[chid];
|
|
+}
|
|
+
|
|
+void
|
|
+nv04_graph_context_switch(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
|
|
+ struct nouveau_channel *chan = NULL;
|
|
+ int chid;
|
|
+
|
|
+ pgraph->fifo_access(dev, false);
|
|
+ nouveau_wait_for_idle(dev);
|
|
+
|
|
+ /* If previous context is valid, we need to save it */
|
|
+ pgraph->unload_context(dev);
|
|
+
|
|
+ /* Load context for next channel */
|
|
+ chid = dev_priv->engine.fifo.channel_id(dev);
|
|
+ chan = dev_priv->fifos[chid];
|
|
+ if (chan)
|
|
+ nv04_graph_load_context(chan);
|
|
+
|
|
+ pgraph->fifo_access(dev, true);
|
|
+}
|
|
+
|
|
+static uint32_t *ctx_reg(struct graph_state *ctx, uint32_t reg)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(nv04_graph_ctx_regs); i++) {
|
|
+ if (nv04_graph_ctx_regs[i] == reg)
|
|
+ return &ctx->nv04[i];
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+int nv04_graph_create_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct graph_state *pgraph_ctx;
|
|
+ NV_DEBUG(chan->dev, "nv04_graph_context_create %d\n", chan->id);
|
|
+
|
|
+ chan->pgraph_ctx = pgraph_ctx = kzalloc(sizeof(*pgraph_ctx),
|
|
+ GFP_KERNEL);
|
|
+ if (pgraph_ctx == NULL)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ *ctx_reg(pgraph_ctx, NV04_PGRAPH_DEBUG_3) = 0xfad4ff31;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void nv04_graph_destroy_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct graph_state *pgraph_ctx = chan->pgraph_ctx;
|
|
+
|
|
+ kfree(pgraph_ctx);
|
|
+ chan->pgraph_ctx = NULL;
|
|
+}
|
|
+
|
|
+int nv04_graph_load_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct graph_state *pgraph_ctx = chan->pgraph_ctx;
|
|
+ uint32_t tmp;
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(nv04_graph_ctx_regs); i++)
|
|
+ nv_wr32(dev, nv04_graph_ctx_regs[i], pgraph_ctx->nv04[i]);
|
|
+
|
|
+ nv_wr32(dev, NV04_PGRAPH_CTX_CONTROL, 0x10010100);
|
|
+
|
|
+ tmp = nv_rd32(dev, NV04_PGRAPH_CTX_USER) & 0x00ffffff;
|
|
+ nv_wr32(dev, NV04_PGRAPH_CTX_USER, tmp | chan->id << 24);
|
|
+
|
|
+ tmp = nv_rd32(dev, NV04_PGRAPH_FFINTFC_ST2);
|
|
+ nv_wr32(dev, NV04_PGRAPH_FFINTFC_ST2, tmp & 0x000fffff);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nv04_graph_unload_context(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
|
|
+ struct nouveau_channel *chan = NULL;
|
|
+ struct graph_state *ctx;
|
|
+ uint32_t tmp;
|
|
+ int i;
|
|
+
|
|
+ chan = pgraph->channel(dev);
|
|
+ if (!chan)
|
|
+ return 0;
|
|
+ ctx = chan->pgraph_ctx;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(nv04_graph_ctx_regs); i++)
|
|
+ ctx->nv04[i] = nv_rd32(dev, nv04_graph_ctx_regs[i]);
|
|
+
|
|
+ nv_wr32(dev, NV04_PGRAPH_CTX_CONTROL, 0x10000000);
|
|
+ tmp = nv_rd32(dev, NV04_PGRAPH_CTX_USER) & 0x00ffffff;
|
|
+ tmp |= (dev_priv->engine.fifo.channels - 1) << 24;
|
|
+ nv_wr32(dev, NV04_PGRAPH_CTX_USER, tmp);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int nv04_graph_init(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t tmp;
|
|
+
|
|
+ nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) &
|
|
+ ~NV_PMC_ENABLE_PGRAPH);
|
|
+ nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) |
|
|
+ NV_PMC_ENABLE_PGRAPH);
|
|
+
|
|
+ /* Enable PGRAPH interrupts */
|
|
+ nv_wr32(dev, NV03_PGRAPH_INTR, 0xFFFFFFFF);
|
|
+ nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF);
|
|
+
|
|
+ nv_wr32(dev, NV04_PGRAPH_VALID1, 0);
|
|
+ nv_wr32(dev, NV04_PGRAPH_VALID2, 0);
|
|
+ /*nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x000001FF);
|
|
+ nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x001FFFFF);*/
|
|
+ nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x1231c000);
|
|
+ /*1231C000 blob, 001 haiku*/
|
|
+ //*V_WRITE(NV04_PGRAPH_DEBUG_1, 0xf2d91100);*/
|
|
+ nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x72111100);
|
|
+ /*0x72111100 blob , 01 haiku*/
|
|
+ /*nv_wr32(dev, NV04_PGRAPH_DEBUG_2, 0x11d5f870);*/
|
|
+ nv_wr32(dev, NV04_PGRAPH_DEBUG_2, 0x11d5f071);
|
|
+ /*haiku same*/
|
|
+
|
|
+ /*nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0xfad4ff31);*/
|
|
+ nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0xf0d4ff31);
|
|
+ /*haiku and blob 10d4*/
|
|
+
|
|
+ nv_wr32(dev, NV04_PGRAPH_STATE , 0xFFFFFFFF);
|
|
+ nv_wr32(dev, NV04_PGRAPH_CTX_CONTROL , 0x10000100);
|
|
+ tmp = nv_rd32(dev, NV04_PGRAPH_CTX_USER) & 0x00ffffff;
|
|
+ tmp |= (dev_priv->engine.fifo.channels - 1) << 24;
|
|
+ nv_wr32(dev, NV04_PGRAPH_CTX_USER, tmp);
|
|
+
|
|
+ /* These don't belong here, they're part of a per-channel context */
|
|
+ nv_wr32(dev, NV04_PGRAPH_PATTERN_SHAPE, 0x00000000);
|
|
+ nv_wr32(dev, NV04_PGRAPH_BETA_AND , 0xFFFFFFFF);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void nv04_graph_takedown(struct drm_device *dev)
|
|
+{
|
|
+}
|
|
+
|
|
+void
|
|
+nv04_graph_fifo_access(struct drm_device *dev, bool enabled)
|
|
+{
|
|
+ if (enabled)
|
|
+ nv_wr32(dev, NV04_PGRAPH_FIFO,
|
|
+ nv_rd32(dev, NV04_PGRAPH_FIFO) | 1);
|
|
+ else
|
|
+ nv_wr32(dev, NV04_PGRAPH_FIFO,
|
|
+ nv_rd32(dev, NV04_PGRAPH_FIFO) & ~1);
|
|
+}
|
|
+
|
|
+static int
|
|
+nv04_graph_mthd_set_ref(struct nouveau_channel *chan, int grclass,
|
|
+ int mthd, uint32_t data)
|
|
+{
|
|
+ chan->fence.last_sequence_irq = data;
|
|
+ nouveau_fence_handler(chan->dev, chan->id);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+nv04_graph_mthd_set_operation(struct nouveau_channel *chan, int grclass,
|
|
+ int mthd, uint32_t data)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ uint32_t instance = (nv_rd32(dev, NV04_PGRAPH_CTX_SWITCH4) & 0xffff) << 4;
|
|
+ int subc = (nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR) >> 13) & 0x7;
|
|
+ uint32_t tmp;
|
|
+
|
|
+ tmp = nv_ri32(dev, instance);
|
|
+ tmp &= ~0x00038000;
|
|
+ tmp |= ((data & 7) << 15);
|
|
+
|
|
+ nv_wi32(dev, instance, tmp);
|
|
+ nv_wr32(dev, NV04_PGRAPH_CTX_SWITCH1, tmp);
|
|
+ nv_wr32(dev, NV04_PGRAPH_CTX_CACHE1 + (subc<<2), tmp);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct nouveau_pgraph_object_method nv04_graph_mthds_sw[] = {
|
|
+ { 0x0150, nv04_graph_mthd_set_ref },
|
|
+ {}
|
|
+};
|
|
+
|
|
+static struct nouveau_pgraph_object_method nv04_graph_mthds_set_operation[] = {
|
|
+ { 0x02fc, nv04_graph_mthd_set_operation },
|
|
+ {},
|
|
+};
|
|
+
|
|
+struct nouveau_pgraph_object_class nv04_graph_grclass[] = {
|
|
+ { 0x0039, false, NULL },
|
|
+ { 0x004a, false, nv04_graph_mthds_set_operation }, /* gdirect */
|
|
+ { 0x005f, false, nv04_graph_mthds_set_operation }, /* imageblit */
|
|
+ { 0x0061, false, nv04_graph_mthds_set_operation }, /* ifc */
|
|
+ { 0x0077, false, nv04_graph_mthds_set_operation }, /* sifm */
|
|
+ { 0x0030, false, NULL }, /* null */
|
|
+ { 0x0042, false, NULL }, /* surf2d */
|
|
+ { 0x0043, false, NULL }, /* rop */
|
|
+ { 0x0012, false, NULL }, /* beta1 */
|
|
+ { 0x0072, false, NULL }, /* beta4 */
|
|
+ { 0x0019, false, NULL }, /* cliprect */
|
|
+ { 0x0044, false, NULL }, /* pattern */
|
|
+ { 0x0052, false, NULL }, /* swzsurf */
|
|
+ { 0x0053, false, NULL }, /* surf3d */
|
|
+ { 0x0054, false, NULL }, /* tex_tri */
|
|
+ { 0x0055, false, NULL }, /* multitex_tri */
|
|
+ { 0x506e, true, nv04_graph_mthds_sw },
|
|
+ {}
|
|
+};
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nv04_instmem.c b/drivers/gpu/drm/nouveau/nv04_instmem.c
|
|
new file mode 100644
|
|
index 0000000..a3b9563
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv04_instmem.c
|
|
@@ -0,0 +1,208 @@
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "nouveau_drv.h"
|
|
+
|
|
+/* returns the size of fifo context */
|
|
+static int
|
|
+nouveau_fifo_ctx_size(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ if (dev_priv->chipset >= 0x40)
|
|
+ return 128;
|
|
+ else
|
|
+ if (dev_priv->chipset >= 0x17)
|
|
+ return 64;
|
|
+
|
|
+ return 32;
|
|
+}
|
|
+
|
|
+static void
|
|
+nv04_instmem_determine_amount(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ int i;
|
|
+
|
|
+ /* Figure out how much instance memory we need */
|
|
+ if (dev_priv->card_type >= NV_40) {
|
|
+ /* We'll want more instance memory than this on some NV4x cards.
|
|
+ * There's a 16MB aperture to play with that maps onto the end
|
|
+ * of vram. For now, only reserve a small piece until we know
|
|
+ * more about what each chipset requires.
|
|
+ */
|
|
+ switch (dev_priv->chipset) {
|
|
+ case 0x40:
|
|
+ case 0x47:
|
|
+ case 0x49:
|
|
+ case 0x4b:
|
|
+ dev_priv->ramin_rsvd_vram = (2 * 1024 * 1024);
|
|
+ break;
|
|
+ default:
|
|
+ dev_priv->ramin_rsvd_vram = (1 * 1024 * 1024);
|
|
+ break;
|
|
+ }
|
|
+ } else {
|
|
+ /*XXX: what *are* the limits on <NV40 cards?
|
|
+ */
|
|
+ dev_priv->ramin_rsvd_vram = (512 * 1024);
|
|
+ }
|
|
+ NV_DEBUG(dev, "RAMIN size: %dKiB\n", dev_priv->ramin_rsvd_vram >> 10);
|
|
+
|
|
+ /* Clear all of it, except the BIOS image that's in the first 64KiB */
|
|
+ dev_priv->engine.instmem.prepare_access(dev, true);
|
|
+ for (i = 64 * 1024; i < dev_priv->ramin_rsvd_vram; i += 4)
|
|
+ nv_wi32(dev, i, 0x00000000);
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv04_instmem_configure_fixed_tables(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_engine *engine = &dev_priv->engine;
|
|
+
|
|
+ /* FIFO hash table (RAMHT)
|
|
+ * use 4k hash table at RAMIN+0x10000
|
|
+ * TODO: extend the hash table
|
|
+ */
|
|
+ dev_priv->ramht_offset = 0x10000;
|
|
+ dev_priv->ramht_bits = 9;
|
|
+ dev_priv->ramht_size = (1 << dev_priv->ramht_bits); /* nr entries */
|
|
+ dev_priv->ramht_size *= 8; /* 2 32-bit values per entry in RAMHT */
|
|
+ NV_DEBUG(dev, "RAMHT offset=0x%x, size=%d\n", dev_priv->ramht_offset,
|
|
+ dev_priv->ramht_size);
|
|
+
|
|
+ /* FIFO runout table (RAMRO) - 512k at 0x11200 */
|
|
+ dev_priv->ramro_offset = 0x11200;
|
|
+ dev_priv->ramro_size = 512;
|
|
+ NV_DEBUG(dev, "RAMRO offset=0x%x, size=%d\n", dev_priv->ramro_offset,
|
|
+ dev_priv->ramro_size);
|
|
+
|
|
+ /* FIFO context table (RAMFC)
|
|
+ * NV40 : Not sure exactly how to position RAMFC on some cards,
|
|
+ * 0x30002 seems to position it at RAMIN+0x20000 on these
|
|
+ * cards. RAMFC is 4kb (32 fifos, 128byte entries).
|
|
+ * Others: Position RAMFC at RAMIN+0x11400
|
|
+ */
|
|
+ dev_priv->ramfc_size = engine->fifo.channels *
|
|
+ nouveau_fifo_ctx_size(dev);
|
|
+ switch (dev_priv->card_type) {
|
|
+ case NV_40:
|
|
+ dev_priv->ramfc_offset = 0x20000;
|
|
+ break;
|
|
+ case NV_30:
|
|
+ case NV_20:
|
|
+ case NV_10:
|
|
+ case NV_04:
|
|
+ default:
|
|
+ dev_priv->ramfc_offset = 0x11400;
|
|
+ break;
|
|
+ }
|
|
+ NV_DEBUG(dev, "RAMFC offset=0x%x, size=%d\n", dev_priv->ramfc_offset,
|
|
+ dev_priv->ramfc_size);
|
|
+}
|
|
+
|
|
+int nv04_instmem_init(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t offset;
|
|
+ int ret = 0;
|
|
+
|
|
+ nv04_instmem_determine_amount(dev);
|
|
+ nv04_instmem_configure_fixed_tables(dev);
|
|
+
|
|
+ /* Create a heap to manage RAMIN allocations, we don't allocate
|
|
+ * the space that was reserved for RAMHT/FC/RO.
|
|
+ */
|
|
+ offset = dev_priv->ramfc_offset + dev_priv->ramfc_size;
|
|
+
|
|
+ /* It appears RAMRO (or something?) is controlled by 0x2220/0x2230
|
|
+ * on certain NV4x chipsets as well as RAMFC. When 0x2230 == 0
|
|
+ * ("new style" control) the upper 16-bits of 0x2220 points at this
|
|
+ * other mysterious table that's clobbering important things.
|
|
+ *
|
|
+ * We're now pointing this at RAMIN+0x30000 to avoid RAMFC getting
|
|
+ * smashed to pieces on us, so reserve 0x30000-0x40000 too..
|
|
+ */
|
|
+ if (dev_priv->card_type >= NV_40) {
|
|
+ if (offset < 0x40000)
|
|
+ offset = 0x40000;
|
|
+ }
|
|
+
|
|
+ ret = nouveau_mem_init_heap(&dev_priv->ramin_heap,
|
|
+ offset, dev_priv->ramin_rsvd_vram - offset);
|
|
+ if (ret) {
|
|
+ dev_priv->ramin_heap = NULL;
|
|
+ NV_ERROR(dev, "Failed to init RAMIN heap\n");
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void
|
|
+nv04_instmem_takedown(struct drm_device *dev)
|
|
+{
|
|
+}
|
|
+
|
|
+int
|
|
+nv04_instmem_populate(struct drm_device *dev, struct nouveau_gpuobj *gpuobj, uint32_t *sz)
|
|
+{
|
|
+ if (gpuobj->im_backing)
|
|
+ return -EINVAL;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv04_instmem_clear(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ if (gpuobj && gpuobj->im_backing) {
|
|
+ if (gpuobj->im_bound)
|
|
+ dev_priv->engine.instmem.unbind(dev, gpuobj);
|
|
+ gpuobj->im_backing = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+int
|
|
+nv04_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
|
|
+{
|
|
+ if (!gpuobj->im_pramin || gpuobj->im_bound)
|
|
+ return -EINVAL;
|
|
+
|
|
+ gpuobj->im_bound = 1;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nv04_instmem_unbind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
|
|
+{
|
|
+ if (gpuobj->im_bound == 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ gpuobj->im_bound = 0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv04_instmem_prepare_access(struct drm_device *dev, bool write)
|
|
+{
|
|
+}
|
|
+
|
|
+void
|
|
+nv04_instmem_finish_access(struct drm_device *dev)
|
|
+{
|
|
+}
|
|
+
|
|
+int
|
|
+nv04_instmem_suspend(struct drm_device *dev)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv04_instmem_resume(struct drm_device *dev)
|
|
+{
|
|
+}
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nv04_mc.c b/drivers/gpu/drm/nouveau/nv04_mc.c
|
|
new file mode 100644
|
|
index 0000000..617ed1e
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv04_mc.c
|
|
@@ -0,0 +1,20 @@
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_drm.h"
|
|
+
|
|
+int
|
|
+nv04_mc_init(struct drm_device *dev)
|
|
+{
|
|
+ /* Power up everything, resetting each individual unit will
|
|
+ * be done later if needed.
|
|
+ */
|
|
+
|
|
+ nv_wr32(dev, NV03_PMC_ENABLE, 0xFFFFFFFF);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv04_mc_takedown(struct drm_device *dev)
|
|
+{
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nv04_timer.c b/drivers/gpu/drm/nouveau/nv04_timer.c
|
|
new file mode 100644
|
|
index 0000000..1d09ddd
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv04_timer.c
|
|
@@ -0,0 +1,51 @@
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_drm.h"
|
|
+
|
|
+int
|
|
+nv04_timer_init(struct drm_device *dev)
|
|
+{
|
|
+ nv_wr32(dev, NV04_PTIMER_INTR_EN_0, 0x00000000);
|
|
+ nv_wr32(dev, NV04_PTIMER_INTR_0, 0xFFFFFFFF);
|
|
+
|
|
+ /* Just use the pre-existing values when possible for now; these regs
|
|
+ * are not written in nv (driver writer missed a /4 on the address), and
|
|
+ * writing 8 and 3 to the correct regs breaks the timings on the LVDS
|
|
+ * hardware sequencing microcode.
|
|
+ * A correct solution (involving calculations with the GPU PLL) can
|
|
+ * be done when kernel modesetting lands
|
|
+ */
|
|
+ if (!nv_rd32(dev, NV04_PTIMER_NUMERATOR) ||
|
|
+ !nv_rd32(dev, NV04_PTIMER_DENOMINATOR)) {
|
|
+ nv_wr32(dev, NV04_PTIMER_NUMERATOR, 0x00000008);
|
|
+ nv_wr32(dev, NV04_PTIMER_DENOMINATOR, 0x00000003);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+uint64_t
|
|
+nv04_timer_read(struct drm_device *dev)
|
|
+{
|
|
+ uint32_t low;
|
|
+ /* From kmmio dumps on nv28 this looks like how the blob does this.
|
|
+ * It reads the high dword twice, before and after.
|
|
+ * The only explanation seems to be that the 64-bit timer counter
|
|
+ * advances between high and low dword reads and may corrupt the
|
|
+ * result. Not confirmed.
|
|
+ */
|
|
+ uint32_t high2 = nv_rd32(dev, NV04_PTIMER_TIME_1);
|
|
+ uint32_t high1;
|
|
+ do {
|
|
+ high1 = high2;
|
|
+ low = nv_rd32(dev, NV04_PTIMER_TIME_0);
|
|
+ high2 = nv_rd32(dev, NV04_PTIMER_TIME_1);
|
|
+ } while (high1 != high2);
|
|
+ return (((uint64_t)high2) << 32) | (uint64_t)low;
|
|
+}
|
|
+
|
|
+void
|
|
+nv04_timer_takedown(struct drm_device *dev)
|
|
+{
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nv04_tv.c b/drivers/gpu/drm/nouveau/nv04_tv.c
|
|
new file mode 100644
|
|
index 0000000..9c63099
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv04_tv.c
|
|
@@ -0,0 +1,305 @@
|
|
+/*
|
|
+ * Copyright (C) 2009 Francisco Jerez.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_encoder.h"
|
|
+#include "nouveau_connector.h"
|
|
+#include "nouveau_crtc.h"
|
|
+#include "nouveau_hw.h"
|
|
+#include "drm_crtc_helper.h"
|
|
+
|
|
+#include "i2c/ch7006.h"
|
|
+
|
|
+static struct {
|
|
+ struct i2c_board_info board_info;
|
|
+ struct drm_encoder_funcs funcs;
|
|
+ struct drm_encoder_helper_funcs hfuncs;
|
|
+ void *params;
|
|
+
|
|
+} nv04_tv_encoder_info[] = {
|
|
+ {
|
|
+ .board_info = { I2C_BOARD_INFO("ch7006", 0x75) },
|
|
+ .params = &(struct ch7006_encoder_params) {
|
|
+ CH7006_FORMAT_RGB24m12I, CH7006_CLOCK_MASTER,
|
|
+ 0, 0, 0,
|
|
+ CH7006_SYNC_SLAVE, CH7006_SYNC_SEPARATED,
|
|
+ CH7006_POUT_3_3V, CH7006_ACTIVE_HSYNC
|
|
+ },
|
|
+ },
|
|
+};
|
|
+
|
|
+static bool probe_i2c_addr(struct i2c_adapter *adapter, int addr)
|
|
+{
|
|
+ struct i2c_msg msg = {
|
|
+ .addr = addr,
|
|
+ .len = 0,
|
|
+ };
|
|
+
|
|
+ return i2c_transfer(adapter, &msg, 1) == 1;
|
|
+}
|
|
+
|
|
+int nv04_tv_identify(struct drm_device *dev, int i2c_index)
|
|
+{
|
|
+ struct nouveau_i2c_chan *i2c;
|
|
+ bool was_locked;
|
|
+ int i, ret;
|
|
+
|
|
+ NV_TRACE(dev, "Probing TV encoders on I2C bus: %d\n", i2c_index);
|
|
+
|
|
+ i2c = nouveau_i2c_find(dev, i2c_index);
|
|
+ if (!i2c)
|
|
+ return -ENODEV;
|
|
+
|
|
+ was_locked = NVLockVgaCrtcs(dev, false);
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(nv04_tv_encoder_info); i++) {
|
|
+ if (probe_i2c_addr(&i2c->adapter,
|
|
+ nv04_tv_encoder_info[i].board_info.addr)) {
|
|
+ ret = i;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (i < ARRAY_SIZE(nv04_tv_encoder_info)) {
|
|
+ NV_TRACE(dev, "Detected TV encoder: %s\n",
|
|
+ nv04_tv_encoder_info[i].board_info.type);
|
|
+
|
|
+ } else {
|
|
+ NV_TRACE(dev, "No TV encoders found.\n");
|
|
+ i = -ENODEV;
|
|
+ }
|
|
+
|
|
+ NVLockVgaCrtcs(dev, was_locked);
|
|
+ return i;
|
|
+}
|
|
+
|
|
+#define PLLSEL_TV_CRTC1_MASK \
|
|
+ (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 \
|
|
+ | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1)
|
|
+#define PLLSEL_TV_CRTC2_MASK \
|
|
+ (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 \
|
|
+ | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2)
|
|
+
|
|
+static void nv04_tv_dpms(struct drm_encoder *encoder, int mode)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nv04_mode_state *state = &dev_priv->mode_reg;
|
|
+ uint8_t crtc1A;
|
|
+
|
|
+ NV_INFO(dev, "Setting dpms mode %d on TV encoder (output %d)\n",
|
|
+ mode, nv_encoder->dcb->index);
|
|
+
|
|
+ state->pllsel &= ~(PLLSEL_TV_CRTC1_MASK | PLLSEL_TV_CRTC2_MASK);
|
|
+
|
|
+ if (mode == DRM_MODE_DPMS_ON) {
|
|
+ int head = nouveau_crtc(encoder->crtc)->index;
|
|
+ crtc1A = NVReadVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX);
|
|
+
|
|
+ state->pllsel |= head ? PLLSEL_TV_CRTC2_MASK :
|
|
+ PLLSEL_TV_CRTC1_MASK;
|
|
+
|
|
+ /* Inhibit hsync */
|
|
+ crtc1A |= 0x80;
|
|
+
|
|
+ NVWriteVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX, crtc1A);
|
|
+ }
|
|
+
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT, state->pllsel);
|
|
+
|
|
+ to_encoder_slave(encoder)->slave_funcs->dpms(encoder, mode);
|
|
+}
|
|
+
|
|
+static void nv04_tv_bind(struct drm_device *dev, int head, bool bind)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nv04_crtc_reg *state = &dev_priv->mode_reg.crtc_reg[head];
|
|
+
|
|
+ state->tv_setup = 0;
|
|
+
|
|
+ if (bind) {
|
|
+ state->CRTC[NV_CIO_CRE_LCD__INDEX] = 0;
|
|
+ state->CRTC[NV_CIO_CRE_49] |= 0x10;
|
|
+ } else {
|
|
+ state->CRTC[NV_CIO_CRE_49] &= ~0x10;
|
|
+ }
|
|
+
|
|
+ NVWriteVgaCrtc(dev, head, NV_CIO_CRE_LCD__INDEX,
|
|
+ state->CRTC[NV_CIO_CRE_LCD__INDEX]);
|
|
+ NVWriteVgaCrtc(dev, head, NV_CIO_CRE_49,
|
|
+ state->CRTC[NV_CIO_CRE_49]);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP,
|
|
+ state->tv_setup);
|
|
+}
|
|
+
|
|
+static void nv04_tv_prepare(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ int head = nouveau_crtc(encoder->crtc)->index;
|
|
+ struct drm_encoder_helper_funcs *helper = encoder->helper_private;
|
|
+
|
|
+ helper->dpms(encoder, DRM_MODE_DPMS_OFF);
|
|
+
|
|
+ nv04_dfp_disable(dev, head);
|
|
+
|
|
+ if (nv_two_heads(dev))
|
|
+ nv04_tv_bind(dev, head ^ 1, false);
|
|
+
|
|
+ nv04_tv_bind(dev, head, true);
|
|
+}
|
|
+
|
|
+static void nv04_tv_mode_set(struct drm_encoder *encoder,
|
|
+ struct drm_display_mode *mode,
|
|
+ struct drm_display_mode *adjusted_mode)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
|
|
+ struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
|
|
+
|
|
+ regp->tv_htotal = adjusted_mode->htotal;
|
|
+ regp->tv_vtotal = adjusted_mode->vtotal;
|
|
+
|
|
+ /* These delay the TV signals with respect to the VGA port,
|
|
+ * they might be useful if we ever allow a CRTC to drive
|
|
+ * multiple outputs.
|
|
+ */
|
|
+ regp->tv_hskew = 1;
|
|
+ regp->tv_hsync_delay = 1;
|
|
+ regp->tv_hsync_delay2 = 64;
|
|
+ regp->tv_vskew = 1;
|
|
+ regp->tv_vsync_delay = 1;
|
|
+
|
|
+ to_encoder_slave(encoder)->slave_funcs->mode_set(encoder, mode, adjusted_mode);
|
|
+}
|
|
+
|
|
+static void nv04_tv_commit(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
|
|
+ struct drm_encoder_helper_funcs *helper = encoder->helper_private;
|
|
+
|
|
+ helper->dpms(encoder, DRM_MODE_DPMS_ON);
|
|
+
|
|
+ NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
|
|
+ drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base), nv_crtc->index,
|
|
+ '@' + ffs(nv_encoder->dcb->or));
|
|
+}
|
|
+
|
|
+static void nv04_tv_destroy(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+
|
|
+ to_encoder_slave(encoder)->slave_funcs->destroy(encoder);
|
|
+
|
|
+ drm_encoder_cleanup(encoder);
|
|
+
|
|
+ kfree(nv_encoder);
|
|
+}
|
|
+
|
|
+int nv04_tv_create(struct drm_device *dev, struct dcb_entry *entry)
|
|
+{
|
|
+ struct nouveau_encoder *nv_encoder;
|
|
+ struct drm_encoder *encoder;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct i2c_adapter *adap;
|
|
+ struct drm_encoder_funcs *funcs = NULL;
|
|
+ struct drm_encoder_helper_funcs *hfuncs = NULL;
|
|
+ struct drm_encoder_slave_funcs *sfuncs = NULL;
|
|
+ int i2c_index = entry->i2c_index;
|
|
+ int type, ret;
|
|
+ bool was_locked;
|
|
+
|
|
+ /* Ensure that we can talk to this encoder */
|
|
+ type = nv04_tv_identify(dev, i2c_index);
|
|
+ if (type < 0)
|
|
+ return type;
|
|
+
|
|
+ /* Allocate the necessary memory */
|
|
+ nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
|
|
+ if (!nv_encoder)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Initialize the common members */
|
|
+ encoder = to_drm_encoder(nv_encoder);
|
|
+
|
|
+ funcs = &nv04_tv_encoder_info[type].funcs;
|
|
+ hfuncs = &nv04_tv_encoder_info[type].hfuncs;
|
|
+
|
|
+ drm_encoder_init(dev, encoder, funcs, DRM_MODE_ENCODER_TVDAC);
|
|
+ drm_encoder_helper_add(encoder, hfuncs);
|
|
+
|
|
+ encoder->possible_crtcs = entry->heads;
|
|
+ encoder->possible_clones = 0;
|
|
+
|
|
+ nv_encoder->dcb = entry;
|
|
+ nv_encoder->or = ffs(entry->or) - 1;
|
|
+
|
|
+ /* Run the slave-specific initialization */
|
|
+ adap = &dev_priv->vbios->dcb->i2c[i2c_index].chan->adapter;
|
|
+
|
|
+ was_locked = NVLockVgaCrtcs(dev, false);
|
|
+
|
|
+ ret = drm_i2c_encoder_init(encoder->dev, to_encoder_slave(encoder), adap,
|
|
+ &nv04_tv_encoder_info[type].board_info);
|
|
+
|
|
+ NVLockVgaCrtcs(dev, was_locked);
|
|
+
|
|
+ if (ret < 0)
|
|
+ goto fail;
|
|
+
|
|
+ /* Fill the function pointers */
|
|
+ sfuncs = to_encoder_slave(encoder)->slave_funcs;
|
|
+
|
|
+ *funcs = (struct drm_encoder_funcs) {
|
|
+ .destroy = nv04_tv_destroy,
|
|
+ };
|
|
+
|
|
+ *hfuncs = (struct drm_encoder_helper_funcs) {
|
|
+ .dpms = nv04_tv_dpms,
|
|
+ .save = sfuncs->save,
|
|
+ .restore = sfuncs->restore,
|
|
+ .mode_fixup = sfuncs->mode_fixup,
|
|
+ .prepare = nv04_tv_prepare,
|
|
+ .commit = nv04_tv_commit,
|
|
+ .mode_set = nv04_tv_mode_set,
|
|
+ .detect = sfuncs->detect,
|
|
+ };
|
|
+
|
|
+ /* Set the slave encoder configuration */
|
|
+ sfuncs->set_config(encoder, nv04_tv_encoder_info[type].params);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+fail:
|
|
+ drm_encoder_cleanup(encoder);
|
|
+
|
|
+ kfree(nv_encoder);
|
|
+ return ret;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nv10_fb.c b/drivers/gpu/drm/nouveau/nv10_fb.c
|
|
new file mode 100644
|
|
index 0000000..cc5cda4
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv10_fb.c
|
|
@@ -0,0 +1,44 @@
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_drm.h"
|
|
+
|
|
+void
|
|
+nv10_fb_set_region_tiling(struct drm_device *dev, int i, uint32_t addr,
|
|
+ uint32_t size, uint32_t pitch)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t limit = max(1u, addr + size) - 1;
|
|
+
|
|
+ if (pitch) {
|
|
+ if (dev_priv->card_type >= NV_20)
|
|
+ addr |= 1;
|
|
+ else
|
|
+ addr |= 1 << 31;
|
|
+ }
|
|
+
|
|
+ nv_wr32(dev, NV10_PFB_TLIMIT(i), limit);
|
|
+ nv_wr32(dev, NV10_PFB_TSIZE(i), pitch);
|
|
+ nv_wr32(dev, NV10_PFB_TILE(i), addr);
|
|
+}
|
|
+
|
|
+int
|
|
+nv10_fb_init(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
|
|
+ int i;
|
|
+
|
|
+ pfb->num_tiles = NV10_PFB_TILE__SIZE;
|
|
+
|
|
+ /* Turn all the tiling regions off. */
|
|
+ for (i = 0; i < pfb->num_tiles; i++)
|
|
+ pfb->set_region_tiling(dev, i, 0, 0, 0);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv10_fb_takedown(struct drm_device *dev)
|
|
+{
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nv10_fifo.c b/drivers/gpu/drm/nouveau/nv10_fifo.c
|
|
new file mode 100644
|
|
index 0000000..7aeabf2
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv10_fifo.c
|
|
@@ -0,0 +1,260 @@
|
|
+/*
|
|
+ * Copyright (C) 2007 Ben Skeggs.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "nouveau_drv.h"
|
|
+
|
|
+#define NV10_RAMFC(c) (dev_priv->ramfc_offset + ((c) * NV10_RAMFC__SIZE))
|
|
+#define NV10_RAMFC__SIZE ((dev_priv->chipset) >= 0x17 ? 64 : 32)
|
|
+
|
|
+int
|
|
+nv10_fifo_channel_id(struct drm_device *dev)
|
|
+{
|
|
+ return nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH1) &
|
|
+ NV10_PFIFO_CACHE1_PUSH1_CHID_MASK;
|
|
+}
|
|
+
|
|
+int
|
|
+nv10_fifo_create_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ uint32_t fc = NV10_RAMFC(chan->id);
|
|
+ int ret;
|
|
+
|
|
+ ret = nouveau_gpuobj_new_fake(dev, NV10_RAMFC(chan->id), ~0,
|
|
+ NV10_RAMFC__SIZE, NVOBJ_FLAG_ZERO_ALLOC |
|
|
+ NVOBJ_FLAG_ZERO_FREE, NULL, &chan->ramfc);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* Fill entries that are seen filled in dumps of nvidia driver just
|
|
+ * after channel's is put into DMA mode
|
|
+ */
|
|
+ dev_priv->engine.instmem.prepare_access(dev, true);
|
|
+ nv_wi32(dev, fc + 0, chan->pushbuf_base);
|
|
+ nv_wi32(dev, fc + 4, chan->pushbuf_base);
|
|
+ nv_wi32(dev, fc + 12, chan->pushbuf->instance >> 4);
|
|
+ nv_wi32(dev, fc + 20, NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES |
|
|
+ NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES |
|
|
+ NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8 |
|
|
+#ifdef __BIG_ENDIAN
|
|
+ NV_PFIFO_CACHE1_BIG_ENDIAN |
|
|
+#endif
|
|
+ 0);
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+
|
|
+ /* enable the fifo dma operation */
|
|
+ nv_wr32(dev, NV04_PFIFO_MODE,
|
|
+ nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id));
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv10_fifo_destroy_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+
|
|
+ nv_wr32(dev, NV04_PFIFO_MODE,
|
|
+ nv_rd32(dev, NV04_PFIFO_MODE) & ~(1 << chan->id));
|
|
+
|
|
+ nouveau_gpuobj_ref_del(dev, &chan->ramfc);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv10_fifo_do_load_context(struct drm_device *dev, int chid)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t fc = NV10_RAMFC(chid), tmp;
|
|
+
|
|
+ dev_priv->engine.instmem.prepare_access(dev, false);
|
|
+
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUT, nv_ri32(dev, fc + 0));
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_GET, nv_ri32(dev, fc + 4));
|
|
+ nv_wr32(dev, NV10_PFIFO_CACHE1_REF_CNT, nv_ri32(dev, fc + 8));
|
|
+
|
|
+ tmp = nv_ri32(dev, fc + 12);
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE, tmp & 0xFFFF);
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT, tmp >> 16);
|
|
+
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_STATE, nv_ri32(dev, fc + 16));
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_FETCH, nv_ri32(dev, fc + 20));
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_ENGINE, nv_ri32(dev, fc + 24));
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_PULL1, nv_ri32(dev, fc + 28));
|
|
+
|
|
+ if (dev_priv->chipset < 0x17)
|
|
+ goto out;
|
|
+
|
|
+ nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_VALUE, nv_ri32(dev, fc + 32));
|
|
+ tmp = nv_ri32(dev, fc + 36);
|
|
+ nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP, tmp);
|
|
+ nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT, nv_ri32(dev, fc + 40));
|
|
+ nv_wr32(dev, NV10_PFIFO_CACHE1_SEMAPHORE, nv_ri32(dev, fc + 44));
|
|
+ nv_wr32(dev, NV10_PFIFO_CACHE1_DMA_SUBROUTINE, nv_ri32(dev, fc + 48));
|
|
+
|
|
+out:
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0);
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0);
|
|
+}
|
|
+
|
|
+int
|
|
+nv10_fifo_load_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ uint32_t tmp;
|
|
+
|
|
+ nv10_fifo_do_load_context(dev, chan->id);
|
|
+
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1,
|
|
+ NV03_PFIFO_CACHE1_PUSH1_DMA | chan->id);
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 1);
|
|
+
|
|
+ /* Reset NV04_PFIFO_CACHE1_DMA_CTL_AT_INFO to INVALID */
|
|
+ tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_CTL) & ~(1 << 31);
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_CTL, tmp);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nv10_fifo_unload_context(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
|
|
+ uint32_t fc, tmp;
|
|
+ int chid;
|
|
+
|
|
+ chid = pfifo->channel_id(dev);
|
|
+ if (chid < 0 || chid >= dev_priv->engine.fifo.channels)
|
|
+ return 0;
|
|
+ fc = NV10_RAMFC(chid);
|
|
+
|
|
+ dev_priv->engine.instmem.prepare_access(dev, true);
|
|
+
|
|
+ nv_wi32(dev, fc + 0, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT));
|
|
+ nv_wi32(dev, fc + 4, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET));
|
|
+ nv_wi32(dev, fc + 8, nv_rd32(dev, NV10_PFIFO_CACHE1_REF_CNT));
|
|
+ tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE) & 0xFFFF;
|
|
+ tmp |= (nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT) << 16);
|
|
+ nv_wi32(dev, fc + 12, tmp);
|
|
+ nv_wi32(dev, fc + 16, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_STATE));
|
|
+ nv_wi32(dev, fc + 20, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_FETCH));
|
|
+ nv_wi32(dev, fc + 24, nv_rd32(dev, NV04_PFIFO_CACHE1_ENGINE));
|
|
+ nv_wi32(dev, fc + 28, nv_rd32(dev, NV04_PFIFO_CACHE1_PULL1));
|
|
+
|
|
+ if (dev_priv->chipset < 0x17)
|
|
+ goto out;
|
|
+
|
|
+ nv_wi32(dev, fc + 32, nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_VALUE));
|
|
+ tmp = nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP);
|
|
+ nv_wi32(dev, fc + 36, tmp);
|
|
+ nv_wi32(dev, fc + 40, nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT));
|
|
+ nv_wi32(dev, fc + 44, nv_rd32(dev, NV10_PFIFO_CACHE1_SEMAPHORE));
|
|
+ nv_wi32(dev, fc + 48, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET));
|
|
+
|
|
+out:
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+
|
|
+ nv10_fifo_do_load_context(dev, pfifo->channels - 1);
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void
|
|
+nv10_fifo_init_reset(struct drm_device *dev)
|
|
+{
|
|
+ nv_wr32(dev, NV03_PMC_ENABLE,
|
|
+ nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PFIFO);
|
|
+ nv_wr32(dev, NV03_PMC_ENABLE,
|
|
+ nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PFIFO);
|
|
+
|
|
+ nv_wr32(dev, 0x003224, 0x000f0078);
|
|
+ nv_wr32(dev, 0x002044, 0x0101ffff);
|
|
+ nv_wr32(dev, 0x002040, 0x000000ff);
|
|
+ nv_wr32(dev, 0x002500, 0x00000000);
|
|
+ nv_wr32(dev, 0x003000, 0x00000000);
|
|
+ nv_wr32(dev, 0x003050, 0x00000000);
|
|
+
|
|
+ nv_wr32(dev, 0x003258, 0x00000000);
|
|
+ nv_wr32(dev, 0x003210, 0x00000000);
|
|
+ nv_wr32(dev, 0x003270, 0x00000000);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv10_fifo_init_ramxx(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ nv_wr32(dev, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ |
|
|
+ ((dev_priv->ramht_bits - 9) << 16) |
|
|
+ (dev_priv->ramht_offset >> 8));
|
|
+ nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro_offset>>8);
|
|
+
|
|
+ if (dev_priv->chipset < 0x17) {
|
|
+ nv_wr32(dev, NV03_PFIFO_RAMFC, dev_priv->ramfc_offset >> 8);
|
|
+ } else {
|
|
+ nv_wr32(dev, NV03_PFIFO_RAMFC, (dev_priv->ramfc_offset >> 8) |
|
|
+ (1 << 16) /* 64 Bytes entry*/);
|
|
+ /* XXX nvidia blob set bit 18, 21,23 for nv20 & nv30 */
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+nv10_fifo_init_intr(struct drm_device *dev)
|
|
+{
|
|
+ nv_wr32(dev, 0x002100, 0xffffffff);
|
|
+ nv_wr32(dev, 0x002140, 0xffffffff);
|
|
+}
|
|
+
|
|
+int
|
|
+nv10_fifo_init(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
|
|
+ int i;
|
|
+
|
|
+ nv10_fifo_init_reset(dev);
|
|
+ nv10_fifo_init_ramxx(dev);
|
|
+
|
|
+ nv10_fifo_do_load_context(dev, pfifo->channels - 1);
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1);
|
|
+
|
|
+ nv10_fifo_init_intr(dev);
|
|
+ pfifo->enable(dev);
|
|
+ pfifo->reassign(dev, true);
|
|
+
|
|
+ for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
|
|
+ if (dev_priv->fifos[i]) {
|
|
+ uint32_t mode = nv_rd32(dev, NV04_PFIFO_MODE);
|
|
+ nv_wr32(dev, NV04_PFIFO_MODE, mode | (1 << i));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nv10_graph.c b/drivers/gpu/drm/nouveau/nv10_graph.c
|
|
new file mode 100644
|
|
index 0000000..fcf2cdd
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv10_graph.c
|
|
@@ -0,0 +1,1009 @@
|
|
+/*
|
|
+ * Copyright 2007 Matthieu CASTET <castet.matthieu@free.fr>
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the next
|
|
+ * paragraph) shall be included in all copies or substantial portions of the
|
|
+ * Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
+ * DEALINGS IN THE SOFTWARE.
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "nouveau_drm.h"
|
|
+#include "nouveau_drv.h"
|
|
+
|
|
+#define NV10_FIFO_NUMBER 32
|
|
+
|
|
+struct pipe_state {
|
|
+ uint32_t pipe_0x0000[0x040/4];
|
|
+ uint32_t pipe_0x0040[0x010/4];
|
|
+ uint32_t pipe_0x0200[0x0c0/4];
|
|
+ uint32_t pipe_0x4400[0x080/4];
|
|
+ uint32_t pipe_0x6400[0x3b0/4];
|
|
+ uint32_t pipe_0x6800[0x2f0/4];
|
|
+ uint32_t pipe_0x6c00[0x030/4];
|
|
+ uint32_t pipe_0x7000[0x130/4];
|
|
+ uint32_t pipe_0x7400[0x0c0/4];
|
|
+ uint32_t pipe_0x7800[0x0c0/4];
|
|
+};
|
|
+
|
|
+static int nv10_graph_ctx_regs[] = {
|
|
+ NV10_PGRAPH_CTX_SWITCH1,
|
|
+ NV10_PGRAPH_CTX_SWITCH2,
|
|
+ NV10_PGRAPH_CTX_SWITCH3,
|
|
+ NV10_PGRAPH_CTX_SWITCH4,
|
|
+ NV10_PGRAPH_CTX_SWITCH5,
|
|
+ NV10_PGRAPH_CTX_CACHE1, /* 8 values from 0x400160 to 0x40017c */
|
|
+ NV10_PGRAPH_CTX_CACHE2, /* 8 values from 0x400180 to 0x40019c */
|
|
+ NV10_PGRAPH_CTX_CACHE3, /* 8 values from 0x4001a0 to 0x4001bc */
|
|
+ NV10_PGRAPH_CTX_CACHE4, /* 8 values from 0x4001c0 to 0x4001dc */
|
|
+ NV10_PGRAPH_CTX_CACHE5, /* 8 values from 0x4001e0 to 0x4001fc */
|
|
+ 0x00400164,
|
|
+ 0x00400184,
|
|
+ 0x004001a4,
|
|
+ 0x004001c4,
|
|
+ 0x004001e4,
|
|
+ 0x00400168,
|
|
+ 0x00400188,
|
|
+ 0x004001a8,
|
|
+ 0x004001c8,
|
|
+ 0x004001e8,
|
|
+ 0x0040016c,
|
|
+ 0x0040018c,
|
|
+ 0x004001ac,
|
|
+ 0x004001cc,
|
|
+ 0x004001ec,
|
|
+ 0x00400170,
|
|
+ 0x00400190,
|
|
+ 0x004001b0,
|
|
+ 0x004001d0,
|
|
+ 0x004001f0,
|
|
+ 0x00400174,
|
|
+ 0x00400194,
|
|
+ 0x004001b4,
|
|
+ 0x004001d4,
|
|
+ 0x004001f4,
|
|
+ 0x00400178,
|
|
+ 0x00400198,
|
|
+ 0x004001b8,
|
|
+ 0x004001d8,
|
|
+ 0x004001f8,
|
|
+ 0x0040017c,
|
|
+ 0x0040019c,
|
|
+ 0x004001bc,
|
|
+ 0x004001dc,
|
|
+ 0x004001fc,
|
|
+ NV10_PGRAPH_CTX_USER,
|
|
+ NV04_PGRAPH_DMA_START_0,
|
|
+ NV04_PGRAPH_DMA_START_1,
|
|
+ NV04_PGRAPH_DMA_LENGTH,
|
|
+ NV04_PGRAPH_DMA_MISC,
|
|
+ NV10_PGRAPH_DMA_PITCH,
|
|
+ NV04_PGRAPH_BOFFSET0,
|
|
+ NV04_PGRAPH_BBASE0,
|
|
+ NV04_PGRAPH_BLIMIT0,
|
|
+ NV04_PGRAPH_BOFFSET1,
|
|
+ NV04_PGRAPH_BBASE1,
|
|
+ NV04_PGRAPH_BLIMIT1,
|
|
+ NV04_PGRAPH_BOFFSET2,
|
|
+ NV04_PGRAPH_BBASE2,
|
|
+ NV04_PGRAPH_BLIMIT2,
|
|
+ NV04_PGRAPH_BOFFSET3,
|
|
+ NV04_PGRAPH_BBASE3,
|
|
+ NV04_PGRAPH_BLIMIT3,
|
|
+ NV04_PGRAPH_BOFFSET4,
|
|
+ NV04_PGRAPH_BBASE4,
|
|
+ NV04_PGRAPH_BLIMIT4,
|
|
+ NV04_PGRAPH_BOFFSET5,
|
|
+ NV04_PGRAPH_BBASE5,
|
|
+ NV04_PGRAPH_BLIMIT5,
|
|
+ NV04_PGRAPH_BPITCH0,
|
|
+ NV04_PGRAPH_BPITCH1,
|
|
+ NV04_PGRAPH_BPITCH2,
|
|
+ NV04_PGRAPH_BPITCH3,
|
|
+ NV04_PGRAPH_BPITCH4,
|
|
+ NV10_PGRAPH_SURFACE,
|
|
+ NV10_PGRAPH_STATE,
|
|
+ NV04_PGRAPH_BSWIZZLE2,
|
|
+ NV04_PGRAPH_BSWIZZLE5,
|
|
+ NV04_PGRAPH_BPIXEL,
|
|
+ NV10_PGRAPH_NOTIFY,
|
|
+ NV04_PGRAPH_PATT_COLOR0,
|
|
+ NV04_PGRAPH_PATT_COLOR1,
|
|
+ NV04_PGRAPH_PATT_COLORRAM, /* 64 values from 0x400900 to 0x4009fc */
|
|
+ 0x00400904,
|
|
+ 0x00400908,
|
|
+ 0x0040090c,
|
|
+ 0x00400910,
|
|
+ 0x00400914,
|
|
+ 0x00400918,
|
|
+ 0x0040091c,
|
|
+ 0x00400920,
|
|
+ 0x00400924,
|
|
+ 0x00400928,
|
|
+ 0x0040092c,
|
|
+ 0x00400930,
|
|
+ 0x00400934,
|
|
+ 0x00400938,
|
|
+ 0x0040093c,
|
|
+ 0x00400940,
|
|
+ 0x00400944,
|
|
+ 0x00400948,
|
|
+ 0x0040094c,
|
|
+ 0x00400950,
|
|
+ 0x00400954,
|
|
+ 0x00400958,
|
|
+ 0x0040095c,
|
|
+ 0x00400960,
|
|
+ 0x00400964,
|
|
+ 0x00400968,
|
|
+ 0x0040096c,
|
|
+ 0x00400970,
|
|
+ 0x00400974,
|
|
+ 0x00400978,
|
|
+ 0x0040097c,
|
|
+ 0x00400980,
|
|
+ 0x00400984,
|
|
+ 0x00400988,
|
|
+ 0x0040098c,
|
|
+ 0x00400990,
|
|
+ 0x00400994,
|
|
+ 0x00400998,
|
|
+ 0x0040099c,
|
|
+ 0x004009a0,
|
|
+ 0x004009a4,
|
|
+ 0x004009a8,
|
|
+ 0x004009ac,
|
|
+ 0x004009b0,
|
|
+ 0x004009b4,
|
|
+ 0x004009b8,
|
|
+ 0x004009bc,
|
|
+ 0x004009c0,
|
|
+ 0x004009c4,
|
|
+ 0x004009c8,
|
|
+ 0x004009cc,
|
|
+ 0x004009d0,
|
|
+ 0x004009d4,
|
|
+ 0x004009d8,
|
|
+ 0x004009dc,
|
|
+ 0x004009e0,
|
|
+ 0x004009e4,
|
|
+ 0x004009e8,
|
|
+ 0x004009ec,
|
|
+ 0x004009f0,
|
|
+ 0x004009f4,
|
|
+ 0x004009f8,
|
|
+ 0x004009fc,
|
|
+ NV04_PGRAPH_PATTERN, /* 2 values from 0x400808 to 0x40080c */
|
|
+ 0x0040080c,
|
|
+ NV04_PGRAPH_PATTERN_SHAPE,
|
|
+ NV03_PGRAPH_MONO_COLOR0,
|
|
+ NV04_PGRAPH_ROP3,
|
|
+ NV04_PGRAPH_CHROMA,
|
|
+ NV04_PGRAPH_BETA_AND,
|
|
+ NV04_PGRAPH_BETA_PREMULT,
|
|
+ 0x00400e70,
|
|
+ 0x00400e74,
|
|
+ 0x00400e78,
|
|
+ 0x00400e7c,
|
|
+ 0x00400e80,
|
|
+ 0x00400e84,
|
|
+ 0x00400e88,
|
|
+ 0x00400e8c,
|
|
+ 0x00400ea0,
|
|
+ 0x00400ea4,
|
|
+ 0x00400ea8,
|
|
+ 0x00400e90,
|
|
+ 0x00400e94,
|
|
+ 0x00400e98,
|
|
+ 0x00400e9c,
|
|
+ NV10_PGRAPH_WINDOWCLIP_HORIZONTAL, /* 8 values from 0x400f00-0x400f1c */
|
|
+ NV10_PGRAPH_WINDOWCLIP_VERTICAL, /* 8 values from 0x400f20-0x400f3c */
|
|
+ 0x00400f04,
|
|
+ 0x00400f24,
|
|
+ 0x00400f08,
|
|
+ 0x00400f28,
|
|
+ 0x00400f0c,
|
|
+ 0x00400f2c,
|
|
+ 0x00400f10,
|
|
+ 0x00400f30,
|
|
+ 0x00400f14,
|
|
+ 0x00400f34,
|
|
+ 0x00400f18,
|
|
+ 0x00400f38,
|
|
+ 0x00400f1c,
|
|
+ 0x00400f3c,
|
|
+ NV10_PGRAPH_XFMODE0,
|
|
+ NV10_PGRAPH_XFMODE1,
|
|
+ NV10_PGRAPH_GLOBALSTATE0,
|
|
+ NV10_PGRAPH_GLOBALSTATE1,
|
|
+ NV04_PGRAPH_STORED_FMT,
|
|
+ NV04_PGRAPH_SOURCE_COLOR,
|
|
+ NV03_PGRAPH_ABS_X_RAM, /* 32 values from 0x400400 to 0x40047c */
|
|
+ NV03_PGRAPH_ABS_Y_RAM, /* 32 values from 0x400480 to 0x4004fc */
|
|
+ 0x00400404,
|
|
+ 0x00400484,
|
|
+ 0x00400408,
|
|
+ 0x00400488,
|
|
+ 0x0040040c,
|
|
+ 0x0040048c,
|
|
+ 0x00400410,
|
|
+ 0x00400490,
|
|
+ 0x00400414,
|
|
+ 0x00400494,
|
|
+ 0x00400418,
|
|
+ 0x00400498,
|
|
+ 0x0040041c,
|
|
+ 0x0040049c,
|
|
+ 0x00400420,
|
|
+ 0x004004a0,
|
|
+ 0x00400424,
|
|
+ 0x004004a4,
|
|
+ 0x00400428,
|
|
+ 0x004004a8,
|
|
+ 0x0040042c,
|
|
+ 0x004004ac,
|
|
+ 0x00400430,
|
|
+ 0x004004b0,
|
|
+ 0x00400434,
|
|
+ 0x004004b4,
|
|
+ 0x00400438,
|
|
+ 0x004004b8,
|
|
+ 0x0040043c,
|
|
+ 0x004004bc,
|
|
+ 0x00400440,
|
|
+ 0x004004c0,
|
|
+ 0x00400444,
|
|
+ 0x004004c4,
|
|
+ 0x00400448,
|
|
+ 0x004004c8,
|
|
+ 0x0040044c,
|
|
+ 0x004004cc,
|
|
+ 0x00400450,
|
|
+ 0x004004d0,
|
|
+ 0x00400454,
|
|
+ 0x004004d4,
|
|
+ 0x00400458,
|
|
+ 0x004004d8,
|
|
+ 0x0040045c,
|
|
+ 0x004004dc,
|
|
+ 0x00400460,
|
|
+ 0x004004e0,
|
|
+ 0x00400464,
|
|
+ 0x004004e4,
|
|
+ 0x00400468,
|
|
+ 0x004004e8,
|
|
+ 0x0040046c,
|
|
+ 0x004004ec,
|
|
+ 0x00400470,
|
|
+ 0x004004f0,
|
|
+ 0x00400474,
|
|
+ 0x004004f4,
|
|
+ 0x00400478,
|
|
+ 0x004004f8,
|
|
+ 0x0040047c,
|
|
+ 0x004004fc,
|
|
+ NV03_PGRAPH_ABS_UCLIP_XMIN,
|
|
+ NV03_PGRAPH_ABS_UCLIP_XMAX,
|
|
+ NV03_PGRAPH_ABS_UCLIP_YMIN,
|
|
+ NV03_PGRAPH_ABS_UCLIP_YMAX,
|
|
+ 0x00400550,
|
|
+ 0x00400558,
|
|
+ 0x00400554,
|
|
+ 0x0040055c,
|
|
+ NV03_PGRAPH_ABS_UCLIPA_XMIN,
|
|
+ NV03_PGRAPH_ABS_UCLIPA_XMAX,
|
|
+ NV03_PGRAPH_ABS_UCLIPA_YMIN,
|
|
+ NV03_PGRAPH_ABS_UCLIPA_YMAX,
|
|
+ NV03_PGRAPH_ABS_ICLIP_XMAX,
|
|
+ NV03_PGRAPH_ABS_ICLIP_YMAX,
|
|
+ NV03_PGRAPH_XY_LOGIC_MISC0,
|
|
+ NV03_PGRAPH_XY_LOGIC_MISC1,
|
|
+ NV03_PGRAPH_XY_LOGIC_MISC2,
|
|
+ NV03_PGRAPH_XY_LOGIC_MISC3,
|
|
+ NV03_PGRAPH_CLIPX_0,
|
|
+ NV03_PGRAPH_CLIPX_1,
|
|
+ NV03_PGRAPH_CLIPY_0,
|
|
+ NV03_PGRAPH_CLIPY_1,
|
|
+ NV10_PGRAPH_COMBINER0_IN_ALPHA,
|
|
+ NV10_PGRAPH_COMBINER1_IN_ALPHA,
|
|
+ NV10_PGRAPH_COMBINER0_IN_RGB,
|
|
+ NV10_PGRAPH_COMBINER1_IN_RGB,
|
|
+ NV10_PGRAPH_COMBINER_COLOR0,
|
|
+ NV10_PGRAPH_COMBINER_COLOR1,
|
|
+ NV10_PGRAPH_COMBINER0_OUT_ALPHA,
|
|
+ NV10_PGRAPH_COMBINER1_OUT_ALPHA,
|
|
+ NV10_PGRAPH_COMBINER0_OUT_RGB,
|
|
+ NV10_PGRAPH_COMBINER1_OUT_RGB,
|
|
+ NV10_PGRAPH_COMBINER_FINAL0,
|
|
+ NV10_PGRAPH_COMBINER_FINAL1,
|
|
+ 0x00400e00,
|
|
+ 0x00400e04,
|
|
+ 0x00400e08,
|
|
+ 0x00400e0c,
|
|
+ 0x00400e10,
|
|
+ 0x00400e14,
|
|
+ 0x00400e18,
|
|
+ 0x00400e1c,
|
|
+ 0x00400e20,
|
|
+ 0x00400e24,
|
|
+ 0x00400e28,
|
|
+ 0x00400e2c,
|
|
+ 0x00400e30,
|
|
+ 0x00400e34,
|
|
+ 0x00400e38,
|
|
+ 0x00400e3c,
|
|
+ NV04_PGRAPH_PASSTHRU_0,
|
|
+ NV04_PGRAPH_PASSTHRU_1,
|
|
+ NV04_PGRAPH_PASSTHRU_2,
|
|
+ NV10_PGRAPH_DIMX_TEXTURE,
|
|
+ NV10_PGRAPH_WDIMX_TEXTURE,
|
|
+ NV10_PGRAPH_DVD_COLORFMT,
|
|
+ NV10_PGRAPH_SCALED_FORMAT,
|
|
+ NV04_PGRAPH_MISC24_0,
|
|
+ NV04_PGRAPH_MISC24_1,
|
|
+ NV04_PGRAPH_MISC24_2,
|
|
+ NV03_PGRAPH_X_MISC,
|
|
+ NV03_PGRAPH_Y_MISC,
|
|
+ NV04_PGRAPH_VALID1,
|
|
+ NV04_PGRAPH_VALID2,
|
|
+};
|
|
+
|
|
+static int nv17_graph_ctx_regs[] = {
|
|
+ NV10_PGRAPH_DEBUG_4,
|
|
+ 0x004006b0,
|
|
+ 0x00400eac,
|
|
+ 0x00400eb0,
|
|
+ 0x00400eb4,
|
|
+ 0x00400eb8,
|
|
+ 0x00400ebc,
|
|
+ 0x00400ec0,
|
|
+ 0x00400ec4,
|
|
+ 0x00400ec8,
|
|
+ 0x00400ecc,
|
|
+ 0x00400ed0,
|
|
+ 0x00400ed4,
|
|
+ 0x00400ed8,
|
|
+ 0x00400edc,
|
|
+ 0x00400ee0,
|
|
+ 0x00400a00,
|
|
+ 0x00400a04,
|
|
+};
|
|
+
|
|
+struct graph_state {
|
|
+ int nv10[ARRAY_SIZE(nv10_graph_ctx_regs)];
|
|
+ int nv17[ARRAY_SIZE(nv17_graph_ctx_regs)];
|
|
+ struct pipe_state pipe_state;
|
|
+ uint32_t lma_window[4];
|
|
+};
|
|
+
|
|
+#define PIPE_SAVE(dev, state, addr) \
|
|
+ do { \
|
|
+ int __i; \
|
|
+ nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, addr); \
|
|
+ for (__i = 0; __i < ARRAY_SIZE(state); __i++) \
|
|
+ state[__i] = nv_rd32(dev, NV10_PGRAPH_PIPE_DATA); \
|
|
+ } while (0)
|
|
+
|
|
+#define PIPE_RESTORE(dev, state, addr) \
|
|
+ do { \
|
|
+ int __i; \
|
|
+ nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, addr); \
|
|
+ for (__i = 0; __i < ARRAY_SIZE(state); __i++) \
|
|
+ nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, state[__i]); \
|
|
+ } while (0)
|
|
+
|
|
+static void nv10_graph_save_pipe(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct graph_state *pgraph_ctx = chan->pgraph_ctx;
|
|
+ struct pipe_state *pipe = &pgraph_ctx->pipe_state;
|
|
+
|
|
+ PIPE_SAVE(dev, pipe->pipe_0x4400, 0x4400);
|
|
+ PIPE_SAVE(dev, pipe->pipe_0x0200, 0x0200);
|
|
+ PIPE_SAVE(dev, pipe->pipe_0x6400, 0x6400);
|
|
+ PIPE_SAVE(dev, pipe->pipe_0x6800, 0x6800);
|
|
+ PIPE_SAVE(dev, pipe->pipe_0x6c00, 0x6c00);
|
|
+ PIPE_SAVE(dev, pipe->pipe_0x7000, 0x7000);
|
|
+ PIPE_SAVE(dev, pipe->pipe_0x7400, 0x7400);
|
|
+ PIPE_SAVE(dev, pipe->pipe_0x7800, 0x7800);
|
|
+ PIPE_SAVE(dev, pipe->pipe_0x0040, 0x0040);
|
|
+ PIPE_SAVE(dev, pipe->pipe_0x0000, 0x0000);
|
|
+}
|
|
+
|
|
+static void nv10_graph_load_pipe(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct graph_state *pgraph_ctx = chan->pgraph_ctx;
|
|
+ struct pipe_state *pipe = &pgraph_ctx->pipe_state;
|
|
+ uint32_t xfmode0, xfmode1;
|
|
+ int i;
|
|
+
|
|
+ nouveau_wait_for_idle(dev);
|
|
+ /* XXX check haiku comments */
|
|
+ xfmode0 = nv_rd32(dev, NV10_PGRAPH_XFMODE0);
|
|
+ xfmode1 = nv_rd32(dev, NV10_PGRAPH_XFMODE1);
|
|
+ nv_wr32(dev, NV10_PGRAPH_XFMODE0, 0x10000000);
|
|
+ nv_wr32(dev, NV10_PGRAPH_XFMODE1, 0x00000000);
|
|
+ nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x000064c0);
|
|
+ for (i = 0; i < 4; i++)
|
|
+ nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x3f800000);
|
|
+ for (i = 0; i < 4; i++)
|
|
+ nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x00000000);
|
|
+
|
|
+ nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x00006ab0);
|
|
+ for (i = 0; i < 3; i++)
|
|
+ nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x3f800000);
|
|
+
|
|
+ nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x00006a80);
|
|
+ for (i = 0; i < 3; i++)
|
|
+ nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x00000000);
|
|
+
|
|
+ nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x00000040);
|
|
+ nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x00000008);
|
|
+
|
|
+
|
|
+ PIPE_RESTORE(dev, pipe->pipe_0x0200, 0x0200);
|
|
+ nouveau_wait_for_idle(dev);
|
|
+
|
|
+ /* restore XFMODE */
|
|
+ nv_wr32(dev, NV10_PGRAPH_XFMODE0, xfmode0);
|
|
+ nv_wr32(dev, NV10_PGRAPH_XFMODE1, xfmode1);
|
|
+ PIPE_RESTORE(dev, pipe->pipe_0x6400, 0x6400);
|
|
+ PIPE_RESTORE(dev, pipe->pipe_0x6800, 0x6800);
|
|
+ PIPE_RESTORE(dev, pipe->pipe_0x6c00, 0x6c00);
|
|
+ PIPE_RESTORE(dev, pipe->pipe_0x7000, 0x7000);
|
|
+ PIPE_RESTORE(dev, pipe->pipe_0x7400, 0x7400);
|
|
+ PIPE_RESTORE(dev, pipe->pipe_0x7800, 0x7800);
|
|
+ PIPE_RESTORE(dev, pipe->pipe_0x4400, 0x4400);
|
|
+ PIPE_RESTORE(dev, pipe->pipe_0x0000, 0x0000);
|
|
+ PIPE_RESTORE(dev, pipe->pipe_0x0040, 0x0040);
|
|
+ nouveau_wait_for_idle(dev);
|
|
+}
|
|
+
|
|
+static void nv10_graph_create_pipe(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct graph_state *pgraph_ctx = chan->pgraph_ctx;
|
|
+ struct pipe_state *fifo_pipe_state = &pgraph_ctx->pipe_state;
|
|
+ uint32_t *fifo_pipe_state_addr;
|
|
+ int i;
|
|
+#define PIPE_INIT(addr) \
|
|
+ do { \
|
|
+ fifo_pipe_state_addr = fifo_pipe_state->pipe_##addr; \
|
|
+ } while (0)
|
|
+#define PIPE_INIT_END(addr) \
|
|
+ do { \
|
|
+ uint32_t *__end_addr = fifo_pipe_state->pipe_##addr + \
|
|
+ ARRAY_SIZE(fifo_pipe_state->pipe_##addr); \
|
|
+ if (fifo_pipe_state_addr != __end_addr) \
|
|
+ NV_ERROR(dev, "incomplete pipe init for 0x%x : %p/%p\n", \
|
|
+ addr, fifo_pipe_state_addr, __end_addr); \
|
|
+ } while (0)
|
|
+#define NV_WRITE_PIPE_INIT(value) *(fifo_pipe_state_addr++) = value
|
|
+
|
|
+ PIPE_INIT(0x0200);
|
|
+ for (i = 0; i < 48; i++)
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ PIPE_INIT_END(0x0200);
|
|
+
|
|
+ PIPE_INIT(0x6400);
|
|
+ for (i = 0; i < 211; i++)
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x3f800000);
|
|
+ NV_WRITE_PIPE_INIT(0x40000000);
|
|
+ NV_WRITE_PIPE_INIT(0x40000000);
|
|
+ NV_WRITE_PIPE_INIT(0x40000000);
|
|
+ NV_WRITE_PIPE_INIT(0x40000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x3f800000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x3f000000);
|
|
+ NV_WRITE_PIPE_INIT(0x3f000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x3f800000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x3f800000);
|
|
+ NV_WRITE_PIPE_INIT(0x3f800000);
|
|
+ NV_WRITE_PIPE_INIT(0x3f800000);
|
|
+ NV_WRITE_PIPE_INIT(0x3f800000);
|
|
+ PIPE_INIT_END(0x6400);
|
|
+
|
|
+ PIPE_INIT(0x6800);
|
|
+ for (i = 0; i < 162; i++)
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x3f800000);
|
|
+ for (i = 0; i < 25; i++)
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ PIPE_INIT_END(0x6800);
|
|
+
|
|
+ PIPE_INIT(0x6c00);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0xbf800000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ PIPE_INIT_END(0x6c00);
|
|
+
|
|
+ PIPE_INIT(0x7000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x7149f2ca);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x7149f2ca);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x7149f2ca);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x7149f2ca);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x7149f2ca);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x7149f2ca);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x7149f2ca);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ NV_WRITE_PIPE_INIT(0x7149f2ca);
|
|
+ for (i = 0; i < 35; i++)
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ PIPE_INIT_END(0x7000);
|
|
+
|
|
+ PIPE_INIT(0x7400);
|
|
+ for (i = 0; i < 48; i++)
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ PIPE_INIT_END(0x7400);
|
|
+
|
|
+ PIPE_INIT(0x7800);
|
|
+ for (i = 0; i < 48; i++)
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ PIPE_INIT_END(0x7800);
|
|
+
|
|
+ PIPE_INIT(0x4400);
|
|
+ for (i = 0; i < 32; i++)
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ PIPE_INIT_END(0x4400);
|
|
+
|
|
+ PIPE_INIT(0x0000);
|
|
+ for (i = 0; i < 16; i++)
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ PIPE_INIT_END(0x0000);
|
|
+
|
|
+ PIPE_INIT(0x0040);
|
|
+ for (i = 0; i < 4; i++)
|
|
+ NV_WRITE_PIPE_INIT(0x00000000);
|
|
+ PIPE_INIT_END(0x0040);
|
|
+
|
|
+#undef PIPE_INIT
|
|
+#undef PIPE_INIT_END
|
|
+#undef NV_WRITE_PIPE_INIT
|
|
+}
|
|
+
|
|
+static int nv10_graph_ctx_regs_find_offset(struct drm_device *dev, int reg)
|
|
+{
|
|
+ int i;
|
|
+ for (i = 0; i < ARRAY_SIZE(nv10_graph_ctx_regs); i++) {
|
|
+ if (nv10_graph_ctx_regs[i] == reg)
|
|
+ return i;
|
|
+ }
|
|
+ NV_ERROR(dev, "unknow offset nv10_ctx_regs %d\n", reg);
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static int nv17_graph_ctx_regs_find_offset(struct drm_device *dev, int reg)
|
|
+{
|
|
+ int i;
|
|
+ for (i = 0; i < ARRAY_SIZE(nv17_graph_ctx_regs); i++) {
|
|
+ if (nv17_graph_ctx_regs[i] == reg)
|
|
+ return i;
|
|
+ }
|
|
+ NV_ERROR(dev, "unknow offset nv17_ctx_regs %d\n", reg);
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+int nv10_graph_load_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct graph_state *pgraph_ctx = chan->pgraph_ctx;
|
|
+ uint32_t tmp;
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(nv10_graph_ctx_regs); i++)
|
|
+ nv_wr32(dev, nv10_graph_ctx_regs[i], pgraph_ctx->nv10[i]);
|
|
+ if (dev_priv->chipset >= 0x17) {
|
|
+ for (i = 0; i < ARRAY_SIZE(nv17_graph_ctx_regs); i++)
|
|
+ nv_wr32(dev, nv17_graph_ctx_regs[i],
|
|
+ pgraph_ctx->nv17[i]);
|
|
+ }
|
|
+
|
|
+ nv10_graph_load_pipe(chan);
|
|
+
|
|
+ nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10010100);
|
|
+ tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER);
|
|
+ nv_wr32(dev, NV10_PGRAPH_CTX_USER, (tmp & 0xffffff) | chan->id << 24);
|
|
+ tmp = nv_rd32(dev, NV10_PGRAPH_FFINTFC_ST2);
|
|
+ nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2, tmp & 0xcfffffff);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nv10_graph_unload_context(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
|
|
+ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
|
|
+ struct nouveau_channel *chan;
|
|
+ struct graph_state *ctx;
|
|
+ uint32_t tmp;
|
|
+ int i;
|
|
+
|
|
+ chan = pgraph->channel(dev);
|
|
+ if (!chan)
|
|
+ return 0;
|
|
+ ctx = chan->pgraph_ctx;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(nv10_graph_ctx_regs); i++)
|
|
+ ctx->nv10[i] = nv_rd32(dev, nv10_graph_ctx_regs[i]);
|
|
+
|
|
+ if (dev_priv->chipset >= 0x17) {
|
|
+ for (i = 0; i < ARRAY_SIZE(nv17_graph_ctx_regs); i++)
|
|
+ ctx->nv17[i] = nv_rd32(dev, nv17_graph_ctx_regs[i]);
|
|
+ }
|
|
+
|
|
+ nv10_graph_save_pipe(chan);
|
|
+
|
|
+ nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000000);
|
|
+ tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff;
|
|
+ tmp |= (pfifo->channels - 1) << 24;
|
|
+ nv_wr32(dev, NV10_PGRAPH_CTX_USER, tmp);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv10_graph_context_switch(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
|
|
+ struct nouveau_channel *chan = NULL;
|
|
+ int chid;
|
|
+
|
|
+ pgraph->fifo_access(dev, false);
|
|
+ nouveau_wait_for_idle(dev);
|
|
+
|
|
+ /* If previous context is valid, we need to save it */
|
|
+ nv10_graph_unload_context(dev);
|
|
+
|
|
+ /* Load context for next channel */
|
|
+ chid = (nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR) >> 20) & 0x1f;
|
|
+ chan = dev_priv->fifos[chid];
|
|
+ if (chan)
|
|
+ nv10_graph_load_context(chan);
|
|
+
|
|
+ pgraph->fifo_access(dev, true);
|
|
+}
|
|
+
|
|
+#define NV_WRITE_CTX(reg, val) do { \
|
|
+ int offset = nv10_graph_ctx_regs_find_offset(dev, reg); \
|
|
+ if (offset > 0) \
|
|
+ pgraph_ctx->nv10[offset] = val; \
|
|
+ } while (0)
|
|
+
|
|
+#define NV17_WRITE_CTX(reg, val) do { \
|
|
+ int offset = nv17_graph_ctx_regs_find_offset(dev, reg); \
|
|
+ if (offset > 0) \
|
|
+ pgraph_ctx->nv17[offset] = val; \
|
|
+ } while (0)
|
|
+
|
|
+struct nouveau_channel *
|
|
+nv10_graph_channel(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ int chid = dev_priv->engine.fifo.channels;
|
|
+
|
|
+ if (nv_rd32(dev, NV10_PGRAPH_CTX_CONTROL) & 0x00010000)
|
|
+ chid = nv_rd32(dev, NV10_PGRAPH_CTX_USER) >> 24;
|
|
+
|
|
+ if (chid >= dev_priv->engine.fifo.channels)
|
|
+ return NULL;
|
|
+
|
|
+ return dev_priv->fifos[chid];
|
|
+}
|
|
+
|
|
+int nv10_graph_create_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct graph_state *pgraph_ctx;
|
|
+
|
|
+ NV_DEBUG(dev, "nv10_graph_context_create %d\n", chan->id);
|
|
+
|
|
+ chan->pgraph_ctx = pgraph_ctx = kzalloc(sizeof(*pgraph_ctx),
|
|
+ GFP_KERNEL);
|
|
+ if (pgraph_ctx == NULL)
|
|
+ return -ENOMEM;
|
|
+
|
|
+
|
|
+ NV_WRITE_CTX(0x00400e88, 0x08000000);
|
|
+ NV_WRITE_CTX(0x00400e9c, 0x4b7fffff);
|
|
+ NV_WRITE_CTX(NV03_PGRAPH_XY_LOGIC_MISC0, 0x0001ffff);
|
|
+ NV_WRITE_CTX(0x00400e10, 0x00001000);
|
|
+ NV_WRITE_CTX(0x00400e14, 0x00001000);
|
|
+ NV_WRITE_CTX(0x00400e30, 0x00080008);
|
|
+ NV_WRITE_CTX(0x00400e34, 0x00080008);
|
|
+ if (dev_priv->chipset >= 0x17) {
|
|
+ /* is it really needed ??? */
|
|
+ NV17_WRITE_CTX(NV10_PGRAPH_DEBUG_4,
|
|
+ nv_rd32(dev, NV10_PGRAPH_DEBUG_4));
|
|
+ NV17_WRITE_CTX(0x004006b0, nv_rd32(dev, 0x004006b0));
|
|
+ NV17_WRITE_CTX(0x00400eac, 0x0fff0000);
|
|
+ NV17_WRITE_CTX(0x00400eb0, 0x0fff0000);
|
|
+ NV17_WRITE_CTX(0x00400ec0, 0x00000080);
|
|
+ NV17_WRITE_CTX(0x00400ed0, 0x00000080);
|
|
+ }
|
|
+ NV_WRITE_CTX(NV10_PGRAPH_CTX_USER, chan->id << 24);
|
|
+
|
|
+ nv10_graph_create_pipe(chan);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void nv10_graph_destroy_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct graph_state *pgraph_ctx = chan->pgraph_ctx;
|
|
+
|
|
+ kfree(pgraph_ctx);
|
|
+ chan->pgraph_ctx = NULL;
|
|
+}
|
|
+
|
|
+void
|
|
+nv10_graph_set_region_tiling(struct drm_device *dev, int i, uint32_t addr,
|
|
+ uint32_t size, uint32_t pitch)
|
|
+{
|
|
+ uint32_t limit = max(1u, addr + size) - 1;
|
|
+
|
|
+ if (pitch)
|
|
+ addr |= 1 << 31;
|
|
+
|
|
+ nv_wr32(dev, NV10_PGRAPH_TLIMIT(i), limit);
|
|
+ nv_wr32(dev, NV10_PGRAPH_TSIZE(i), pitch);
|
|
+ nv_wr32(dev, NV10_PGRAPH_TILE(i), addr);
|
|
+}
|
|
+
|
|
+int nv10_graph_init(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t tmp;
|
|
+ int i;
|
|
+
|
|
+ nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) &
|
|
+ ~NV_PMC_ENABLE_PGRAPH);
|
|
+ nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) |
|
|
+ NV_PMC_ENABLE_PGRAPH);
|
|
+
|
|
+ nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF);
|
|
+ nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF);
|
|
+
|
|
+ nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF);
|
|
+ nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x00000000);
|
|
+ nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x00118700);
|
|
+ /* nv_wr32(dev, NV04_PGRAPH_DEBUG_2, 0x24E00810); */ /* 0x25f92ad9 */
|
|
+ nv_wr32(dev, NV04_PGRAPH_DEBUG_2, 0x25f92ad9);
|
|
+ nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0x55DE0830 |
|
|
+ (1<<29) |
|
|
+ (1<<31));
|
|
+ if (dev_priv->chipset >= 0x17) {
|
|
+ nv_wr32(dev, NV10_PGRAPH_DEBUG_4, 0x1f000000);
|
|
+ nv_wr32(dev, 0x400a10, 0x3ff3fb6);
|
|
+ nv_wr32(dev, 0x400838, 0x2f8684);
|
|
+ nv_wr32(dev, 0x40083c, 0x115f3f);
|
|
+ nv_wr32(dev, 0x004006b0, 0x40000020);
|
|
+ } else
|
|
+ nv_wr32(dev, NV10_PGRAPH_DEBUG_4, 0x00000000);
|
|
+
|
|
+ /* Turn all the tiling regions off. */
|
|
+ for (i = 0; i < NV10_PFB_TILE__SIZE; i++)
|
|
+ nv10_graph_set_region_tiling(dev, i, 0, 0, 0);
|
|
+
|
|
+ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH1, 0x00000000);
|
|
+ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH2, 0x00000000);
|
|
+ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH3, 0x00000000);
|
|
+ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH4, 0x00000000);
|
|
+ nv_wr32(dev, NV10_PGRAPH_STATE , 0xFFFFFFFF);
|
|
+
|
|
+ tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff;
|
|
+ tmp |= (dev_priv->engine.fifo.channels - 1) << 24;
|
|
+ nv_wr32(dev, NV10_PGRAPH_CTX_USER, tmp);
|
|
+ nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000100);
|
|
+ nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2, 0x08000000);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void nv10_graph_takedown(struct drm_device *dev)
|
|
+{
|
|
+}
|
|
+
|
|
+static int
|
|
+nv17_graph_mthd_lma_window(struct nouveau_channel *chan, int grclass,
|
|
+ int mthd, uint32_t data)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct graph_state *ctx = chan->pgraph_ctx;
|
|
+ struct pipe_state *pipe = &ctx->pipe_state;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
|
|
+ uint32_t pipe_0x0040[1], pipe_0x64c0[8], pipe_0x6a80[3], pipe_0x6ab0[3];
|
|
+ uint32_t xfmode0, xfmode1;
|
|
+ int i;
|
|
+
|
|
+ ctx->lma_window[(mthd - 0x1638) / 4] = data;
|
|
+
|
|
+ if (mthd != 0x1644)
|
|
+ return 0;
|
|
+
|
|
+ nouveau_wait_for_idle(dev);
|
|
+
|
|
+ PIPE_SAVE(dev, pipe_0x0040, 0x0040);
|
|
+ PIPE_SAVE(dev, pipe->pipe_0x0200, 0x0200);
|
|
+
|
|
+ PIPE_RESTORE(dev, ctx->lma_window, 0x6790);
|
|
+
|
|
+ nouveau_wait_for_idle(dev);
|
|
+
|
|
+ xfmode0 = nv_rd32(dev, NV10_PGRAPH_XFMODE0);
|
|
+ xfmode1 = nv_rd32(dev, NV10_PGRAPH_XFMODE1);
|
|
+
|
|
+ PIPE_SAVE(dev, pipe->pipe_0x4400, 0x4400);
|
|
+ PIPE_SAVE(dev, pipe_0x64c0, 0x64c0);
|
|
+ PIPE_SAVE(dev, pipe_0x6ab0, 0x6ab0);
|
|
+ PIPE_SAVE(dev, pipe_0x6a80, 0x6a80);
|
|
+
|
|
+ nouveau_wait_for_idle(dev);
|
|
+
|
|
+ nv_wr32(dev, NV10_PGRAPH_XFMODE0, 0x10000000);
|
|
+ nv_wr32(dev, NV10_PGRAPH_XFMODE1, 0x00000000);
|
|
+ nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x000064c0);
|
|
+ for (i = 0; i < 4; i++)
|
|
+ nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x3f800000);
|
|
+ for (i = 0; i < 4; i++)
|
|
+ nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x00000000);
|
|
+
|
|
+ nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x00006ab0);
|
|
+ for (i = 0; i < 3; i++)
|
|
+ nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x3f800000);
|
|
+
|
|
+ nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x00006a80);
|
|
+ for (i = 0; i < 3; i++)
|
|
+ nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x00000000);
|
|
+
|
|
+ nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x00000040);
|
|
+ nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x00000008);
|
|
+
|
|
+ PIPE_RESTORE(dev, pipe->pipe_0x0200, 0x0200);
|
|
+
|
|
+ nouveau_wait_for_idle(dev);
|
|
+
|
|
+ PIPE_RESTORE(dev, pipe_0x0040, 0x0040);
|
|
+
|
|
+ nv_wr32(dev, NV10_PGRAPH_XFMODE0, xfmode0);
|
|
+ nv_wr32(dev, NV10_PGRAPH_XFMODE1, xfmode1);
|
|
+
|
|
+ PIPE_RESTORE(dev, pipe_0x64c0, 0x64c0);
|
|
+ PIPE_RESTORE(dev, pipe_0x6ab0, 0x6ab0);
|
|
+ PIPE_RESTORE(dev, pipe_0x6a80, 0x6a80);
|
|
+ PIPE_RESTORE(dev, pipe->pipe_0x4400, 0x4400);
|
|
+
|
|
+ nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x000000c0);
|
|
+ nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x00000000);
|
|
+
|
|
+ nouveau_wait_for_idle(dev);
|
|
+
|
|
+ pgraph->fifo_access(dev, true);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+nv17_graph_mthd_lma_enable(struct nouveau_channel *chan, int grclass,
|
|
+ int mthd, uint32_t data)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
|
|
+
|
|
+ nouveau_wait_for_idle(dev);
|
|
+
|
|
+ nv_wr32(dev, NV10_PGRAPH_DEBUG_4,
|
|
+ nv_rd32(dev, NV10_PGRAPH_DEBUG_4) | 0x1 << 8);
|
|
+ nv_wr32(dev, 0x004006b0,
|
|
+ nv_rd32(dev, 0x004006b0) | 0x8 << 24);
|
|
+
|
|
+ pgraph->fifo_access(dev, true);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct nouveau_pgraph_object_method nv17_graph_celsius_mthds[] = {
|
|
+ { 0x1638, nv17_graph_mthd_lma_window },
|
|
+ { 0x163c, nv17_graph_mthd_lma_window },
|
|
+ { 0x1640, nv17_graph_mthd_lma_window },
|
|
+ { 0x1644, nv17_graph_mthd_lma_window },
|
|
+ { 0x1658, nv17_graph_mthd_lma_enable },
|
|
+ {}
|
|
+};
|
|
+
|
|
+struct nouveau_pgraph_object_class nv10_graph_grclass[] = {
|
|
+ { 0x0030, false, NULL }, /* null */
|
|
+ { 0x0039, false, NULL }, /* m2mf */
|
|
+ { 0x004a, false, NULL }, /* gdirect */
|
|
+ { 0x005f, false, NULL }, /* imageblit */
|
|
+ { 0x009f, false, NULL }, /* imageblit (nv12) */
|
|
+ { 0x008a, false, NULL }, /* ifc */
|
|
+ { 0x0089, false, NULL }, /* sifm */
|
|
+ { 0x0062, false, NULL }, /* surf2d */
|
|
+ { 0x0043, false, NULL }, /* rop */
|
|
+ { 0x0012, false, NULL }, /* beta1 */
|
|
+ { 0x0072, false, NULL }, /* beta4 */
|
|
+ { 0x0019, false, NULL }, /* cliprect */
|
|
+ { 0x0044, false, NULL }, /* pattern */
|
|
+ { 0x0052, false, NULL }, /* swzsurf */
|
|
+ { 0x0093, false, NULL }, /* surf3d */
|
|
+ { 0x0094, false, NULL }, /* tex_tri */
|
|
+ { 0x0095, false, NULL }, /* multitex_tri */
|
|
+ { 0x0056, false, NULL }, /* celcius (nv10) */
|
|
+ { 0x0096, false, NULL }, /* celcius (nv11) */
|
|
+ { 0x0099, false, nv17_graph_celsius_mthds }, /* celcius (nv17) */
|
|
+ {}
|
|
+};
|
|
diff --git a/drivers/gpu/drm/nouveau/nv17_gpio.c b/drivers/gpu/drm/nouveau/nv17_gpio.c
|
|
new file mode 100644
|
|
index 0000000..2e58c33
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv17_gpio.c
|
|
@@ -0,0 +1,92 @@
|
|
+/*
|
|
+ * Copyright (C) 2009 Francisco Jerez.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_hw.h"
|
|
+
|
|
+static bool
|
|
+get_gpio_location(struct dcb_gpio_entry *ent, uint32_t *reg, uint32_t *shift,
|
|
+ uint32_t *mask)
|
|
+{
|
|
+ if (ent->line < 2) {
|
|
+ *reg = NV_PCRTC_GPIO;
|
|
+ *shift = ent->line * 16;
|
|
+ *mask = 0x11;
|
|
+
|
|
+ } else if (ent->line < 10) {
|
|
+ *reg = NV_PCRTC_GPIO_EXT;
|
|
+ *shift = (ent->line - 2) * 4;
|
|
+ *mask = 0x3;
|
|
+
|
|
+ } else if (ent->line < 14) {
|
|
+ *reg = NV_PCRTC_850;
|
|
+ *shift = (ent->line - 10) * 4;
|
|
+ *mask = 0x3;
|
|
+
|
|
+ } else {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+int
|
|
+nv17_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag)
|
|
+{
|
|
+ struct dcb_gpio_entry *ent = nouveau_bios_gpio_entry(dev, tag);
|
|
+ uint32_t reg, shift, mask, value;
|
|
+
|
|
+ if (!ent)
|
|
+ return -ENODEV;
|
|
+
|
|
+ if (!get_gpio_location(ent, ®, &shift, &mask))
|
|
+ return -ENODEV;
|
|
+
|
|
+ value = NVReadCRTC(dev, 0, reg) >> shift;
|
|
+
|
|
+ return (ent->invert ? 1 : 0) ^ (value & 1);
|
|
+}
|
|
+
|
|
+int
|
|
+nv17_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state)
|
|
+{
|
|
+ struct dcb_gpio_entry *ent = nouveau_bios_gpio_entry(dev, tag);
|
|
+ uint32_t reg, shift, mask, value;
|
|
+
|
|
+ if (!ent)
|
|
+ return -ENODEV;
|
|
+
|
|
+ if (!get_gpio_location(ent, ®, &shift, &mask))
|
|
+ return -ENODEV;
|
|
+
|
|
+ value = ((ent->invert ? 1 : 0) ^ (state ? 1 : 0)) << shift;
|
|
+ mask = ~(mask << shift);
|
|
+
|
|
+ NVWriteCRTC(dev, 0, reg, value | (NVReadCRTC(dev, 0, reg) & mask));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nv17_tv.c b/drivers/gpu/drm/nouveau/nv17_tv.c
|
|
new file mode 100644
|
|
index 0000000..58b917c
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv17_tv.c
|
|
@@ -0,0 +1,776 @@
|
|
+/*
|
|
+ * Copyright (C) 2009 Francisco Jerez.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm_crtc_helper.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_encoder.h"
|
|
+#include "nouveau_connector.h"
|
|
+#include "nouveau_crtc.h"
|
|
+#include "nouveau_hw.h"
|
|
+#include "nv17_tv.h"
|
|
+
|
|
+static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t testval, regoffset = nv04_dac_output_offset(encoder);
|
|
+ uint32_t gpio0, gpio1, fp_htotal, fp_hsync_start, fp_hsync_end,
|
|
+ fp_control, test_ctrl, dacclk, ctv_14, ctv_1c, ctv_6c;
|
|
+ uint32_t sample = 0;
|
|
+ int head;
|
|
+
|
|
+#define RGB_TEST_DATA(r, g, b) (r << 0 | g << 10 | b << 20)
|
|
+ testval = RGB_TEST_DATA(0x82, 0xeb, 0x82);
|
|
+ if (dev_priv->vbios->tvdactestval)
|
|
+ testval = dev_priv->vbios->tvdactestval;
|
|
+
|
|
+ dacclk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset);
|
|
+ head = (dacclk & 0x100) >> 8;
|
|
+
|
|
+ /* Save the previous state. */
|
|
+ gpio1 = nv17_gpio_get(dev, DCB_GPIO_TVDAC1);
|
|
+ gpio0 = nv17_gpio_get(dev, DCB_GPIO_TVDAC0);
|
|
+ fp_htotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL);
|
|
+ fp_hsync_start = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START);
|
|
+ fp_hsync_end = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_END);
|
|
+ fp_control = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL);
|
|
+ test_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset);
|
|
+ ctv_1c = NVReadRAMDAC(dev, head, 0x680c1c);
|
|
+ ctv_14 = NVReadRAMDAC(dev, head, 0x680c14);
|
|
+ ctv_6c = NVReadRAMDAC(dev, head, 0x680c6c);
|
|
+
|
|
+ /* Prepare the DAC for load detection. */
|
|
+ nv17_gpio_set(dev, DCB_GPIO_TVDAC1, true);
|
|
+ nv17_gpio_set(dev, DCB_GPIO_TVDAC0, true);
|
|
+
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL, 1343);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START, 1047);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_END, 1183);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL,
|
|
+ NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS |
|
|
+ NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12 |
|
|
+ NV_PRAMDAC_FP_TG_CONTROL_READ_PROG |
|
|
+ NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS |
|
|
+ NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS);
|
|
+
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, 0);
|
|
+
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset,
|
|
+ (dacclk & ~0xff) | 0x22);
|
|
+ msleep(1);
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset,
|
|
+ (dacclk & ~0xff) | 0x21);
|
|
+
|
|
+ NVWriteRAMDAC(dev, head, 0x680c1c, 1 << 20);
|
|
+ NVWriteRAMDAC(dev, head, 0x680c14, 4 << 16);
|
|
+
|
|
+ /* Sample pin 0x4 (usually S-video luma). */
|
|
+ NVWriteRAMDAC(dev, head, 0x680c6c, testval >> 10 & 0x3ff);
|
|
+ msleep(20);
|
|
+ sample |= NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset)
|
|
+ & 0x4 << 28;
|
|
+
|
|
+ /* Sample the remaining pins. */
|
|
+ NVWriteRAMDAC(dev, head, 0x680c6c, testval & 0x3ff);
|
|
+ msleep(20);
|
|
+ sample |= NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset)
|
|
+ & 0xa << 28;
|
|
+
|
|
+ /* Restore the previous state. */
|
|
+ NVWriteRAMDAC(dev, head, 0x680c1c, ctv_1c);
|
|
+ NVWriteRAMDAC(dev, head, 0x680c14, ctv_14);
|
|
+ NVWriteRAMDAC(dev, head, 0x680c6c, ctv_6c);
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, dacclk);
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, test_ctrl);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL, fp_control);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_END, fp_hsync_end);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START, fp_hsync_start);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL, fp_htotal);
|
|
+ nv17_gpio_set(dev, DCB_GPIO_TVDAC1, gpio1);
|
|
+ nv17_gpio_set(dev, DCB_GPIO_TVDAC0, gpio0);
|
|
+
|
|
+ return sample;
|
|
+}
|
|
+
|
|
+static enum drm_connector_status
|
|
+nv17_tv_detect(struct drm_encoder *encoder, struct drm_connector *connector)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct drm_mode_config *conf = &dev->mode_config;
|
|
+ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
|
|
+ struct dcb_entry *dcb = tv_enc->base.dcb;
|
|
+
|
|
+ if (dev_priv->chipset == 0x42 ||
|
|
+ dev_priv->chipset == 0x43)
|
|
+ tv_enc->pin_mask = nv42_tv_sample_load(encoder) >> 28 & 0xe;
|
|
+ else
|
|
+ tv_enc->pin_mask = nv17_dac_sample_load(encoder) >> 28 & 0xe;
|
|
+
|
|
+ switch (tv_enc->pin_mask) {
|
|
+ case 0x2:
|
|
+ case 0x4:
|
|
+ tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Composite;
|
|
+ break;
|
|
+ case 0xc:
|
|
+ tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_SVIDEO;
|
|
+ break;
|
|
+ case 0xe:
|
|
+ if (dcb->tvconf.has_component_output)
|
|
+ tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Component;
|
|
+ else
|
|
+ tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_SCART;
|
|
+ break;
|
|
+ default:
|
|
+ tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ drm_connector_property_set_value(connector,
|
|
+ conf->tv_subconnector_property,
|
|
+ tv_enc->subconnector);
|
|
+
|
|
+ if (tv_enc->subconnector) {
|
|
+ NV_INFO(dev, "Load detected on output %c\n",
|
|
+ '@' + ffs(dcb->or));
|
|
+ return connector_status_connected;
|
|
+ } else {
|
|
+ return connector_status_disconnected;
|
|
+ }
|
|
+}
|
|
+
|
|
+static const struct {
|
|
+ int hdisplay;
|
|
+ int vdisplay;
|
|
+} modes[] = {
|
|
+ { 640, 400 },
|
|
+ { 640, 480 },
|
|
+ { 720, 480 },
|
|
+ { 720, 576 },
|
|
+ { 800, 600 },
|
|
+ { 1024, 768 },
|
|
+ { 1280, 720 },
|
|
+ { 1280, 1024 },
|
|
+ { 1920, 1080 }
|
|
+};
|
|
+
|
|
+static int nv17_tv_get_modes(struct drm_encoder *encoder,
|
|
+ struct drm_connector *connector)
|
|
+{
|
|
+ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
|
|
+ struct drm_display_mode *mode;
|
|
+ struct drm_display_mode *output_mode;
|
|
+ int n = 0;
|
|
+ int i;
|
|
+
|
|
+ if (tv_norm->kind != CTV_ENC_MODE) {
|
|
+ struct drm_display_mode *tv_mode;
|
|
+
|
|
+ for (tv_mode = nv17_tv_modes; tv_mode->hdisplay; tv_mode++) {
|
|
+ mode = drm_mode_duplicate(encoder->dev, tv_mode);
|
|
+
|
|
+ mode->clock = tv_norm->tv_enc_mode.vrefresh *
|
|
+ mode->htotal / 1000 *
|
|
+ mode->vtotal / 1000;
|
|
+
|
|
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
|
|
+ mode->clock *= 2;
|
|
+
|
|
+ if (mode->hdisplay == tv_norm->tv_enc_mode.hdisplay &&
|
|
+ mode->vdisplay == tv_norm->tv_enc_mode.vdisplay)
|
|
+ mode->type |= DRM_MODE_TYPE_PREFERRED;
|
|
+
|
|
+ drm_mode_probed_add(connector, mode);
|
|
+ n++;
|
|
+ }
|
|
+ return n;
|
|
+ }
|
|
+
|
|
+ /* tv_norm->kind == CTV_ENC_MODE */
|
|
+ output_mode = &tv_norm->ctv_enc_mode.mode;
|
|
+ for (i = 0; i < ARRAY_SIZE(modes); i++) {
|
|
+ if (modes[i].hdisplay > output_mode->hdisplay ||
|
|
+ modes[i].vdisplay > output_mode->vdisplay)
|
|
+ continue;
|
|
+
|
|
+ if (modes[i].hdisplay == output_mode->hdisplay &&
|
|
+ modes[i].vdisplay == output_mode->vdisplay) {
|
|
+ mode = drm_mode_duplicate(encoder->dev, output_mode);
|
|
+ mode->type |= DRM_MODE_TYPE_PREFERRED;
|
|
+ } else {
|
|
+ mode = drm_cvt_mode(encoder->dev, modes[i].hdisplay,
|
|
+ modes[i].vdisplay, 60, false,
|
|
+ output_mode->flags & DRM_MODE_FLAG_INTERLACE,
|
|
+ false);
|
|
+ }
|
|
+
|
|
+ /* CVT modes are sometimes unsuitable... */
|
|
+ if (output_mode->hdisplay <= 720
|
|
+ || output_mode->hdisplay >= 1920) {
|
|
+ mode->htotal = output_mode->htotal;
|
|
+ mode->hsync_start = (mode->hdisplay + (mode->htotal
|
|
+ - mode->hdisplay) * 9 / 10) & ~7;
|
|
+ mode->hsync_end = mode->hsync_start + 8;
|
|
+ }
|
|
+ if (output_mode->vdisplay >= 1024) {
|
|
+ mode->vtotal = output_mode->vtotal;
|
|
+ mode->vsync_start = output_mode->vsync_start;
|
|
+ mode->vsync_end = output_mode->vsync_end;
|
|
+ }
|
|
+
|
|
+ mode->type |= DRM_MODE_TYPE_DRIVER;
|
|
+ drm_mode_probed_add(connector, mode);
|
|
+ n++;
|
|
+ }
|
|
+ return n;
|
|
+}
|
|
+
|
|
+static int nv17_tv_mode_valid(struct drm_encoder *encoder,
|
|
+ struct drm_display_mode *mode)
|
|
+{
|
|
+ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
|
|
+
|
|
+ if (tv_norm->kind == CTV_ENC_MODE) {
|
|
+ struct drm_display_mode *output_mode =
|
|
+ &tv_norm->ctv_enc_mode.mode;
|
|
+
|
|
+ if (mode->clock > 400000)
|
|
+ return MODE_CLOCK_HIGH;
|
|
+
|
|
+ if (mode->hdisplay > output_mode->hdisplay ||
|
|
+ mode->vdisplay > output_mode->vdisplay)
|
|
+ return MODE_BAD;
|
|
+
|
|
+ if ((mode->flags & DRM_MODE_FLAG_INTERLACE) !=
|
|
+ (output_mode->flags & DRM_MODE_FLAG_INTERLACE))
|
|
+ return MODE_NO_INTERLACE;
|
|
+
|
|
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
|
|
+ return MODE_NO_DBLESCAN;
|
|
+
|
|
+ } else {
|
|
+ const int vsync_tolerance = 600;
|
|
+
|
|
+ if (mode->clock > 70000)
|
|
+ return MODE_CLOCK_HIGH;
|
|
+
|
|
+ if (abs(drm_mode_vrefresh(mode) * 1000 -
|
|
+ tv_norm->tv_enc_mode.vrefresh) > vsync_tolerance)
|
|
+ return MODE_VSYNC;
|
|
+
|
|
+ /* The encoder takes care of the actual interlacing */
|
|
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
|
|
+ return MODE_NO_INTERLACE;
|
|
+ }
|
|
+
|
|
+ return MODE_OK;
|
|
+}
|
|
+
|
|
+static bool nv17_tv_mode_fixup(struct drm_encoder *encoder,
|
|
+ struct drm_display_mode *mode,
|
|
+ struct drm_display_mode *adjusted_mode)
|
|
+{
|
|
+ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
|
|
+
|
|
+ if (tv_norm->kind == CTV_ENC_MODE)
|
|
+ adjusted_mode->clock = tv_norm->ctv_enc_mode.mode.clock;
|
|
+ else
|
|
+ adjusted_mode->clock = 90000;
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static void nv17_tv_dpms(struct drm_encoder *encoder, int mode)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct nv17_tv_state *regs = &to_tv_enc(encoder)->state;
|
|
+ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
|
|
+
|
|
+ if (nouveau_encoder(encoder)->last_dpms == mode)
|
|
+ return;
|
|
+ nouveau_encoder(encoder)->last_dpms = mode;
|
|
+
|
|
+ NV_INFO(dev, "Setting dpms mode %d on TV encoder (output %d)\n",
|
|
+ mode, nouveau_encoder(encoder)->dcb->index);
|
|
+
|
|
+ regs->ptv_200 &= ~1;
|
|
+
|
|
+ if (tv_norm->kind == CTV_ENC_MODE) {
|
|
+ nv04_dfp_update_fp_control(encoder, mode);
|
|
+
|
|
+ } else {
|
|
+ nv04_dfp_update_fp_control(encoder, DRM_MODE_DPMS_OFF);
|
|
+
|
|
+ if (mode == DRM_MODE_DPMS_ON)
|
|
+ regs->ptv_200 |= 1;
|
|
+ }
|
|
+
|
|
+ nv_load_ptv(dev, regs, 200);
|
|
+
|
|
+ nv17_gpio_set(dev, DCB_GPIO_TVDAC1, mode == DRM_MODE_DPMS_ON);
|
|
+ nv17_gpio_set(dev, DCB_GPIO_TVDAC0, mode == DRM_MODE_DPMS_ON);
|
|
+
|
|
+ nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON);
|
|
+}
|
|
+
|
|
+static void nv17_tv_prepare(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct drm_encoder_helper_funcs *helper = encoder->helper_private;
|
|
+ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
|
|
+ int head = nouveau_crtc(encoder->crtc)->index;
|
|
+ uint8_t *cr_lcd = &dev_priv->mode_reg.crtc_reg[head].CRTC[
|
|
+ NV_CIO_CRE_LCD__INDEX];
|
|
+ uint32_t dacclk_off = NV_PRAMDAC_DACCLK +
|
|
+ nv04_dac_output_offset(encoder);
|
|
+ uint32_t dacclk;
|
|
+
|
|
+ helper->dpms(encoder, DRM_MODE_DPMS_OFF);
|
|
+
|
|
+ nv04_dfp_disable(dev, head);
|
|
+
|
|
+ /* Unbind any FP encoders from this head if we need the FP
|
|
+ * stuff enabled. */
|
|
+ if (tv_norm->kind == CTV_ENC_MODE) {
|
|
+ struct drm_encoder *enc;
|
|
+
|
|
+ list_for_each_entry(enc, &dev->mode_config.encoder_list, head) {
|
|
+ struct dcb_entry *dcb = nouveau_encoder(enc)->dcb;
|
|
+
|
|
+ if ((dcb->type == OUTPUT_TMDS ||
|
|
+ dcb->type == OUTPUT_LVDS) &&
|
|
+ !enc->crtc &&
|
|
+ nv04_dfp_get_bound_head(dev, dcb) == head) {
|
|
+ nv04_dfp_bind_head(dev, dcb, head ^ 1,
|
|
+ dev_priv->VBIOS.fp.dual_link);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ /* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f)
|
|
+ * at LCD__INDEX which we don't alter
|
|
+ */
|
|
+ if (!(*cr_lcd & 0x44)) {
|
|
+ if (tv_norm->kind == CTV_ENC_MODE)
|
|
+ *cr_lcd = 0x1 | (head ? 0x0 : 0x8);
|
|
+ else
|
|
+ *cr_lcd = 0;
|
|
+ }
|
|
+
|
|
+ /* Set the DACCLK register */
|
|
+ dacclk = (NVReadRAMDAC(dev, 0, dacclk_off) & ~0x30) | 0x1;
|
|
+
|
|
+ if (dev_priv->card_type == NV_40)
|
|
+ dacclk |= 0x1a << 16;
|
|
+
|
|
+ if (tv_norm->kind == CTV_ENC_MODE) {
|
|
+ dacclk |= 0x20;
|
|
+
|
|
+ if (head)
|
|
+ dacclk |= 0x100;
|
|
+ else
|
|
+ dacclk &= ~0x100;
|
|
+
|
|
+ } else {
|
|
+ dacclk |= 0x10;
|
|
+
|
|
+ }
|
|
+
|
|
+ NVWriteRAMDAC(dev, 0, dacclk_off, dacclk);
|
|
+}
|
|
+
|
|
+static void nv17_tv_mode_set(struct drm_encoder *encoder,
|
|
+ struct drm_display_mode *drm_mode,
|
|
+ struct drm_display_mode *adjusted_mode)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ int head = nouveau_crtc(encoder->crtc)->index;
|
|
+ struct nv04_crtc_reg *regs = &dev_priv->mode_reg.crtc_reg[head];
|
|
+ struct nv17_tv_state *tv_regs = &to_tv_enc(encoder)->state;
|
|
+ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
|
|
+ int i;
|
|
+
|
|
+ regs->CRTC[NV_CIO_CRE_53] = 0x40; /* FP_HTIMING */
|
|
+ regs->CRTC[NV_CIO_CRE_54] = 0; /* FP_VTIMING */
|
|
+ regs->ramdac_630 = 0x2; /* turn off green mode (tv test pattern?) */
|
|
+ regs->tv_setup = 1;
|
|
+ regs->ramdac_8c0 = 0x0;
|
|
+
|
|
+ if (tv_norm->kind == TV_ENC_MODE) {
|
|
+ tv_regs->ptv_200 = 0x13111100;
|
|
+ if (head)
|
|
+ tv_regs->ptv_200 |= 0x10;
|
|
+
|
|
+ tv_regs->ptv_20c = 0x808010;
|
|
+ tv_regs->ptv_304 = 0x2d00000;
|
|
+ tv_regs->ptv_600 = 0x0;
|
|
+ tv_regs->ptv_60c = 0x0;
|
|
+ tv_regs->ptv_610 = 0x1e00000;
|
|
+
|
|
+ if (tv_norm->tv_enc_mode.vdisplay == 576) {
|
|
+ tv_regs->ptv_508 = 0x1200000;
|
|
+ tv_regs->ptv_614 = 0x33;
|
|
+
|
|
+ } else if (tv_norm->tv_enc_mode.vdisplay == 480) {
|
|
+ tv_regs->ptv_508 = 0xf00000;
|
|
+ tv_regs->ptv_614 = 0x13;
|
|
+ }
|
|
+
|
|
+ if (dev_priv->card_type >= NV_30) {
|
|
+ tv_regs->ptv_500 = 0xe8e0;
|
|
+ tv_regs->ptv_504 = 0x1710;
|
|
+ tv_regs->ptv_604 = 0x0;
|
|
+ tv_regs->ptv_608 = 0x0;
|
|
+ } else {
|
|
+ if (tv_norm->tv_enc_mode.vdisplay == 576) {
|
|
+ tv_regs->ptv_604 = 0x20;
|
|
+ tv_regs->ptv_608 = 0x10;
|
|
+ tv_regs->ptv_500 = 0x19710;
|
|
+ tv_regs->ptv_504 = 0x68f0;
|
|
+
|
|
+ } else if (tv_norm->tv_enc_mode.vdisplay == 480) {
|
|
+ tv_regs->ptv_604 = 0x10;
|
|
+ tv_regs->ptv_608 = 0x20;
|
|
+ tv_regs->ptv_500 = 0x4b90;
|
|
+ tv_regs->ptv_504 = 0x1b480;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < 0x40; i++)
|
|
+ tv_regs->tv_enc[i] = tv_norm->tv_enc_mode.tv_enc[i];
|
|
+
|
|
+ } else {
|
|
+ struct drm_display_mode *output_mode =
|
|
+ &tv_norm->ctv_enc_mode.mode;
|
|
+
|
|
+ /* The registers in PRAMDAC+0xc00 control some timings and CSC
|
|
+ * parameters for the CTV encoder (It's only used for "HD" TV
|
|
+ * modes, I don't think I have enough working to guess what
|
|
+ * they exactly mean...), it's probably connected at the
|
|
+ * output of the FP encoder, but it also needs the analog
|
|
+ * encoder in its OR enabled and routed to the head it's
|
|
+ * using. It's enabled with the DACCLK register, bits [5:4].
|
|
+ */
|
|
+ for (i = 0; i < 38; i++)
|
|
+ regs->ctv_regs[i] = tv_norm->ctv_enc_mode.ctv_regs[i];
|
|
+
|
|
+ regs->fp_horiz_regs[FP_DISPLAY_END] = output_mode->hdisplay - 1;
|
|
+ regs->fp_horiz_regs[FP_TOTAL] = output_mode->htotal - 1;
|
|
+ regs->fp_horiz_regs[FP_SYNC_START] =
|
|
+ output_mode->hsync_start - 1;
|
|
+ regs->fp_horiz_regs[FP_SYNC_END] = output_mode->hsync_end - 1;
|
|
+ regs->fp_horiz_regs[FP_CRTC] = output_mode->hdisplay +
|
|
+ max((output_mode->hdisplay-600)/40 - 1, 1);
|
|
+
|
|
+ regs->fp_vert_regs[FP_DISPLAY_END] = output_mode->vdisplay - 1;
|
|
+ regs->fp_vert_regs[FP_TOTAL] = output_mode->vtotal - 1;
|
|
+ regs->fp_vert_regs[FP_SYNC_START] =
|
|
+ output_mode->vsync_start - 1;
|
|
+ regs->fp_vert_regs[FP_SYNC_END] = output_mode->vsync_end - 1;
|
|
+ regs->fp_vert_regs[FP_CRTC] = output_mode->vdisplay - 1;
|
|
+
|
|
+ regs->fp_control = NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS |
|
|
+ NV_PRAMDAC_FP_TG_CONTROL_READ_PROG |
|
|
+ NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12;
|
|
+
|
|
+ if (output_mode->flags & DRM_MODE_FLAG_PVSYNC)
|
|
+ regs->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS;
|
|
+ if (output_mode->flags & DRM_MODE_FLAG_PHSYNC)
|
|
+ regs->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS;
|
|
+
|
|
+ regs->fp_debug_0 = NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND |
|
|
+ NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND |
|
|
+ NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR |
|
|
+ NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR |
|
|
+ NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED |
|
|
+ NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE |
|
|
+ NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE;
|
|
+
|
|
+ regs->fp_debug_2 = 0;
|
|
+
|
|
+ regs->fp_margin_color = 0x801080;
|
|
+
|
|
+ }
|
|
+}
|
|
+
|
|
+static void nv17_tv_commit(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ struct drm_encoder_helper_funcs *helper = encoder->helper_private;
|
|
+
|
|
+ if (get_tv_norm(encoder)->kind == TV_ENC_MODE) {
|
|
+ nv17_tv_update_rescaler(encoder);
|
|
+ nv17_tv_update_properties(encoder);
|
|
+ } else {
|
|
+ nv17_ctv_update_rescaler(encoder);
|
|
+ }
|
|
+
|
|
+ nv17_tv_state_load(dev, &to_tv_enc(encoder)->state);
|
|
+
|
|
+ /* This could use refinement for flatpanels, but it should work */
|
|
+ if (dev_priv->chipset < 0x44)
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL +
|
|
+ nv04_dac_output_offset(encoder),
|
|
+ 0xf0000000);
|
|
+ else
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL +
|
|
+ nv04_dac_output_offset(encoder),
|
|
+ 0x00100000);
|
|
+
|
|
+ helper->dpms(encoder, DRM_MODE_DPMS_ON);
|
|
+
|
|
+ NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
|
|
+ drm_get_connector_name(
|
|
+ &nouveau_encoder_connector_get(nv_encoder)->base),
|
|
+ nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
|
|
+}
|
|
+
|
|
+static void nv17_tv_save(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
|
|
+
|
|
+ nouveau_encoder(encoder)->restore.output =
|
|
+ NVReadRAMDAC(dev, 0,
|
|
+ NV_PRAMDAC_DACCLK +
|
|
+ nv04_dac_output_offset(encoder));
|
|
+
|
|
+ nv17_tv_state_save(dev, &tv_enc->saved_state);
|
|
+
|
|
+ tv_enc->state.ptv_200 = tv_enc->saved_state.ptv_200;
|
|
+}
|
|
+
|
|
+static void nv17_tv_restore(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+
|
|
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK +
|
|
+ nv04_dac_output_offset(encoder),
|
|
+ nouveau_encoder(encoder)->restore.output);
|
|
+
|
|
+ nv17_tv_state_load(dev, &to_tv_enc(encoder)->saved_state);
|
|
+}
|
|
+
|
|
+static int nv17_tv_create_resources(struct drm_encoder *encoder,
|
|
+ struct drm_connector *connector)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct drm_mode_config *conf = &dev->mode_config;
|
|
+ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
|
|
+ struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
|
|
+ int num_tv_norms = dcb->tvconf.has_component_output ? NUM_TV_NORMS :
|
|
+ NUM_LD_TV_NORMS;
|
|
+ int i;
|
|
+
|
|
+ if (nouveau_tv_norm) {
|
|
+ for (i = 0; i < num_tv_norms; i++) {
|
|
+ if (!strcmp(nv17_tv_norm_names[i], nouveau_tv_norm)) {
|
|
+ tv_enc->tv_norm = i;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (i == num_tv_norms)
|
|
+ NV_WARN(dev, "Invalid TV norm setting \"%s\"\n",
|
|
+ nouveau_tv_norm);
|
|
+ }
|
|
+
|
|
+ drm_mode_create_tv_properties(dev, num_tv_norms, nv17_tv_norm_names);
|
|
+
|
|
+ drm_connector_attach_property(connector,
|
|
+ conf->tv_select_subconnector_property,
|
|
+ tv_enc->select_subconnector);
|
|
+ drm_connector_attach_property(connector,
|
|
+ conf->tv_subconnector_property,
|
|
+ tv_enc->subconnector);
|
|
+ drm_connector_attach_property(connector,
|
|
+ conf->tv_mode_property,
|
|
+ tv_enc->tv_norm);
|
|
+ drm_connector_attach_property(connector,
|
|
+ conf->tv_flicker_reduction_property,
|
|
+ tv_enc->flicker);
|
|
+ drm_connector_attach_property(connector,
|
|
+ conf->tv_saturation_property,
|
|
+ tv_enc->saturation);
|
|
+ drm_connector_attach_property(connector,
|
|
+ conf->tv_hue_property,
|
|
+ tv_enc->hue);
|
|
+ drm_connector_attach_property(connector,
|
|
+ conf->tv_overscan_property,
|
|
+ tv_enc->overscan);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int nv17_tv_set_property(struct drm_encoder *encoder,
|
|
+ struct drm_connector *connector,
|
|
+ struct drm_property *property,
|
|
+ uint64_t val)
|
|
+{
|
|
+ struct drm_mode_config *conf = &encoder->dev->mode_config;
|
|
+ struct drm_crtc *crtc = encoder->crtc;
|
|
+ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
|
|
+ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
|
|
+ bool modes_changed = false;
|
|
+
|
|
+ if (property == conf->tv_overscan_property) {
|
|
+ tv_enc->overscan = val;
|
|
+ if (encoder->crtc) {
|
|
+ if (tv_norm->kind == CTV_ENC_MODE)
|
|
+ nv17_ctv_update_rescaler(encoder);
|
|
+ else
|
|
+ nv17_tv_update_rescaler(encoder);
|
|
+ }
|
|
+
|
|
+ } else if (property == conf->tv_saturation_property) {
|
|
+ if (tv_norm->kind != TV_ENC_MODE)
|
|
+ return -EINVAL;
|
|
+
|
|
+ tv_enc->saturation = val;
|
|
+ nv17_tv_update_properties(encoder);
|
|
+
|
|
+ } else if (property == conf->tv_hue_property) {
|
|
+ if (tv_norm->kind != TV_ENC_MODE)
|
|
+ return -EINVAL;
|
|
+
|
|
+ tv_enc->hue = val;
|
|
+ nv17_tv_update_properties(encoder);
|
|
+
|
|
+ } else if (property == conf->tv_flicker_reduction_property) {
|
|
+ if (tv_norm->kind != TV_ENC_MODE)
|
|
+ return -EINVAL;
|
|
+
|
|
+ tv_enc->flicker = val;
|
|
+ if (encoder->crtc)
|
|
+ nv17_tv_update_rescaler(encoder);
|
|
+
|
|
+ } else if (property == conf->tv_mode_property) {
|
|
+ if (connector->dpms != DRM_MODE_DPMS_OFF)
|
|
+ return -EINVAL;
|
|
+
|
|
+ tv_enc->tv_norm = val;
|
|
+
|
|
+ modes_changed = true;
|
|
+
|
|
+ } else if (property == conf->tv_select_subconnector_property) {
|
|
+ if (tv_norm->kind != TV_ENC_MODE)
|
|
+ return -EINVAL;
|
|
+
|
|
+ tv_enc->select_subconnector = val;
|
|
+ nv17_tv_update_properties(encoder);
|
|
+
|
|
+ } else {
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (modes_changed) {
|
|
+ drm_helper_probe_single_connector_modes(connector, 0, 0);
|
|
+
|
|
+ /* Disable the crtc to ensure a full modeset is
|
|
+ * performed whenever it's turned on again. */
|
|
+ if (crtc) {
|
|
+ struct drm_mode_set modeset = {
|
|
+ .crtc = crtc,
|
|
+ };
|
|
+
|
|
+ crtc->funcs->set_config(&modeset);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void nv17_tv_destroy(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
|
|
+
|
|
+ NV_DEBUG_KMS(encoder->dev, "\n");
|
|
+
|
|
+ drm_encoder_cleanup(encoder);
|
|
+ kfree(tv_enc);
|
|
+}
|
|
+
|
|
+static struct drm_encoder_helper_funcs nv17_tv_helper_funcs = {
|
|
+ .dpms = nv17_tv_dpms,
|
|
+ .save = nv17_tv_save,
|
|
+ .restore = nv17_tv_restore,
|
|
+ .mode_fixup = nv17_tv_mode_fixup,
|
|
+ .prepare = nv17_tv_prepare,
|
|
+ .commit = nv17_tv_commit,
|
|
+ .mode_set = nv17_tv_mode_set,
|
|
+ .detect = nv17_tv_detect,
|
|
+};
|
|
+
|
|
+static struct drm_encoder_slave_funcs nv17_tv_slave_funcs = {
|
|
+ .get_modes = nv17_tv_get_modes,
|
|
+ .mode_valid = nv17_tv_mode_valid,
|
|
+ .create_resources = nv17_tv_create_resources,
|
|
+ .set_property = nv17_tv_set_property,
|
|
+};
|
|
+
|
|
+static struct drm_encoder_funcs nv17_tv_funcs = {
|
|
+ .destroy = nv17_tv_destroy,
|
|
+};
|
|
+
|
|
+int nv17_tv_create(struct drm_device *dev, struct dcb_entry *entry)
|
|
+{
|
|
+ struct drm_encoder *encoder;
|
|
+ struct nv17_tv_encoder *tv_enc = NULL;
|
|
+
|
|
+ tv_enc = kzalloc(sizeof(*tv_enc), GFP_KERNEL);
|
|
+ if (!tv_enc)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ tv_enc->overscan = 50;
|
|
+ tv_enc->flicker = 50;
|
|
+ tv_enc->saturation = 50;
|
|
+ tv_enc->hue = 0;
|
|
+ tv_enc->tv_norm = TV_NORM_PAL;
|
|
+ tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
|
|
+ tv_enc->select_subconnector = DRM_MODE_SUBCONNECTOR_Automatic;
|
|
+ tv_enc->pin_mask = 0;
|
|
+
|
|
+ encoder = to_drm_encoder(&tv_enc->base);
|
|
+
|
|
+ tv_enc->base.dcb = entry;
|
|
+ tv_enc->base.or = ffs(entry->or) - 1;
|
|
+
|
|
+ drm_encoder_init(dev, encoder, &nv17_tv_funcs, DRM_MODE_ENCODER_TVDAC);
|
|
+ drm_encoder_helper_add(encoder, &nv17_tv_helper_funcs);
|
|
+ to_encoder_slave(encoder)->slave_funcs = &nv17_tv_slave_funcs;
|
|
+
|
|
+ encoder->possible_crtcs = entry->heads;
|
|
+ encoder->possible_clones = 0;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nv17_tv.h b/drivers/gpu/drm/nouveau/nv17_tv.h
|
|
new file mode 100644
|
|
index 0000000..c00977c
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv17_tv.h
|
|
@@ -0,0 +1,156 @@
|
|
+/*
|
|
+ * Copyright (C) 2009 Francisco Jerez.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef __NV17_TV_H__
|
|
+#define __NV17_TV_H__
|
|
+
|
|
+struct nv17_tv_state {
|
|
+ uint8_t tv_enc[0x40];
|
|
+
|
|
+ uint32_t hfilter[4][7];
|
|
+ uint32_t hfilter2[4][7];
|
|
+ uint32_t vfilter[4][7];
|
|
+
|
|
+ uint32_t ptv_200;
|
|
+ uint32_t ptv_204;
|
|
+ uint32_t ptv_208;
|
|
+ uint32_t ptv_20c;
|
|
+ uint32_t ptv_304;
|
|
+ uint32_t ptv_500;
|
|
+ uint32_t ptv_504;
|
|
+ uint32_t ptv_508;
|
|
+ uint32_t ptv_600;
|
|
+ uint32_t ptv_604;
|
|
+ uint32_t ptv_608;
|
|
+ uint32_t ptv_60c;
|
|
+ uint32_t ptv_610;
|
|
+ uint32_t ptv_614;
|
|
+};
|
|
+
|
|
+enum nv17_tv_norm{
|
|
+ TV_NORM_PAL,
|
|
+ TV_NORM_PAL_M,
|
|
+ TV_NORM_PAL_N,
|
|
+ TV_NORM_PAL_NC,
|
|
+ TV_NORM_NTSC_M,
|
|
+ TV_NORM_NTSC_J,
|
|
+ NUM_LD_TV_NORMS,
|
|
+ TV_NORM_HD480I = NUM_LD_TV_NORMS,
|
|
+ TV_NORM_HD480P,
|
|
+ TV_NORM_HD576I,
|
|
+ TV_NORM_HD576P,
|
|
+ TV_NORM_HD720P,
|
|
+ TV_NORM_HD1080I,
|
|
+ NUM_TV_NORMS
|
|
+};
|
|
+
|
|
+struct nv17_tv_encoder {
|
|
+ struct nouveau_encoder base;
|
|
+
|
|
+ struct nv17_tv_state state;
|
|
+ struct nv17_tv_state saved_state;
|
|
+
|
|
+ int overscan;
|
|
+ int flicker;
|
|
+ int saturation;
|
|
+ int hue;
|
|
+ enum nv17_tv_norm tv_norm;
|
|
+ int subconnector;
|
|
+ int select_subconnector;
|
|
+ uint32_t pin_mask;
|
|
+};
|
|
+#define to_tv_enc(x) container_of(nouveau_encoder(x), \
|
|
+ struct nv17_tv_encoder, base)
|
|
+
|
|
+extern char *nv17_tv_norm_names[NUM_TV_NORMS];
|
|
+
|
|
+extern struct nv17_tv_norm_params {
|
|
+ enum {
|
|
+ TV_ENC_MODE,
|
|
+ CTV_ENC_MODE,
|
|
+ } kind;
|
|
+
|
|
+ union {
|
|
+ struct {
|
|
+ int hdisplay;
|
|
+ int vdisplay;
|
|
+ int vrefresh; /* mHz */
|
|
+
|
|
+ uint8_t tv_enc[0x40];
|
|
+ } tv_enc_mode;
|
|
+
|
|
+ struct {
|
|
+ struct drm_display_mode mode;
|
|
+
|
|
+ uint32_t ctv_regs[38];
|
|
+ } ctv_enc_mode;
|
|
+ };
|
|
+
|
|
+} nv17_tv_norms[NUM_TV_NORMS];
|
|
+#define get_tv_norm(enc) (&nv17_tv_norms[to_tv_enc(enc)->tv_norm])
|
|
+
|
|
+extern struct drm_display_mode nv17_tv_modes[];
|
|
+
|
|
+static inline int interpolate(int y0, int y1, int y2, int x)
|
|
+{
|
|
+ return y1 + (x < 50 ? y1 - y0 : y2 - y1) * (x - 50) / 50;
|
|
+}
|
|
+
|
|
+void nv17_tv_state_save(struct drm_device *dev, struct nv17_tv_state *state);
|
|
+void nv17_tv_state_load(struct drm_device *dev, struct nv17_tv_state *state);
|
|
+void nv17_tv_update_properties(struct drm_encoder *encoder);
|
|
+void nv17_tv_update_rescaler(struct drm_encoder *encoder);
|
|
+void nv17_ctv_update_rescaler(struct drm_encoder *encoder);
|
|
+
|
|
+/* TV hardware access functions */
|
|
+
|
|
+static inline void nv_write_ptv(struct drm_device *dev, uint32_t reg, uint32_t val)
|
|
+{
|
|
+ nv_wr32(dev, reg, val);
|
|
+}
|
|
+
|
|
+static inline uint32_t nv_read_ptv(struct drm_device *dev, uint32_t reg)
|
|
+{
|
|
+ return nv_rd32(dev, reg);
|
|
+}
|
|
+
|
|
+static inline void nv_write_tv_enc(struct drm_device *dev, uint8_t reg, uint8_t val)
|
|
+{
|
|
+ nv_write_ptv(dev, NV_PTV_TV_INDEX, reg);
|
|
+ nv_write_ptv(dev, NV_PTV_TV_DATA, val);
|
|
+}
|
|
+
|
|
+static inline uint8_t nv_read_tv_enc(struct drm_device *dev, uint8_t reg)
|
|
+{
|
|
+ nv_write_ptv(dev, NV_PTV_TV_INDEX, reg);
|
|
+ return nv_read_ptv(dev, NV_PTV_TV_DATA);
|
|
+}
|
|
+
|
|
+#define nv_load_ptv(dev, state, reg) nv_write_ptv(dev, NV_PTV_OFFSET + 0x##reg, state->ptv_##reg)
|
|
+#define nv_save_ptv(dev, state, reg) state->ptv_##reg = nv_read_ptv(dev, NV_PTV_OFFSET + 0x##reg)
|
|
+#define nv_load_tv_enc(dev, state, reg) nv_write_tv_enc(dev, 0x##reg, state->tv_enc[0x##reg])
|
|
+
|
|
+#endif
|
|
diff --git a/drivers/gpu/drm/nouveau/nv17_tv_modes.c b/drivers/gpu/drm/nouveau/nv17_tv_modes.c
|
|
new file mode 100644
|
|
index 0000000..d64683d
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv17_tv_modes.c
|
|
@@ -0,0 +1,583 @@
|
|
+/*
|
|
+ * Copyright (C) 2009 Francisco Jerez.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm_crtc_helper.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_encoder.h"
|
|
+#include "nouveau_crtc.h"
|
|
+#include "nouveau_hw.h"
|
|
+#include "nv17_tv.h"
|
|
+
|
|
+char *nv17_tv_norm_names[NUM_TV_NORMS] = {
|
|
+ [TV_NORM_PAL] = "PAL",
|
|
+ [TV_NORM_PAL_M] = "PAL-M",
|
|
+ [TV_NORM_PAL_N] = "PAL-N",
|
|
+ [TV_NORM_PAL_NC] = "PAL-Nc",
|
|
+ [TV_NORM_NTSC_M] = "NTSC-M",
|
|
+ [TV_NORM_NTSC_J] = "NTSC-J",
|
|
+ [TV_NORM_HD480I] = "hd480i",
|
|
+ [TV_NORM_HD480P] = "hd480p",
|
|
+ [TV_NORM_HD576I] = "hd576i",
|
|
+ [TV_NORM_HD576P] = "hd576p",
|
|
+ [TV_NORM_HD720P] = "hd720p",
|
|
+ [TV_NORM_HD1080I] = "hd1080i"
|
|
+};
|
|
+
|
|
+/* TV standard specific parameters */
|
|
+
|
|
+struct nv17_tv_norm_params nv17_tv_norms[NUM_TV_NORMS] = {
|
|
+ [TV_NORM_PAL] = { TV_ENC_MODE, {
|
|
+ .tv_enc_mode = { 720, 576, 50000, {
|
|
+ 0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18,
|
|
+ 0x7e, 0x40, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3,
|
|
+ 0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c,
|
|
+ 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3,
|
|
+ 0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5,
|
|
+ 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0,
|
|
+ 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b,
|
|
+ 0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0
|
|
+ } } } },
|
|
+
|
|
+ [TV_NORM_PAL_M] = { TV_ENC_MODE, {
|
|
+ .tv_enc_mode = { 720, 480, 59940, {
|
|
+ 0x21, 0xe6, 0xef, 0xe3, 0x0, 0x0, 0xb, 0x18,
|
|
+ 0x7e, 0x44, 0x76, 0x32, 0x25, 0x0, 0x3c, 0x0,
|
|
+ 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83,
|
|
+ 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
|
|
+ 0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5,
|
|
+ 0x0, 0x18, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0,
|
|
+ 0x0, 0xb4, 0x0, 0x15, 0x40, 0x10, 0x0, 0x9c,
|
|
+ 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
|
|
+ } } } },
|
|
+
|
|
+ [TV_NORM_PAL_N] = { TV_ENC_MODE, {
|
|
+ .tv_enc_mode = { 720, 576, 50000, {
|
|
+ 0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18,
|
|
+ 0x7e, 0x40, 0x8a, 0x32, 0x25, 0x0, 0x3c, 0x0,
|
|
+ 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c,
|
|
+ 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
|
|
+ 0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5,
|
|
+ 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0,
|
|
+ 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b,
|
|
+ 0xbd, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
|
|
+ } } } },
|
|
+
|
|
+ [TV_NORM_PAL_NC] = { TV_ENC_MODE, {
|
|
+ .tv_enc_mode = { 720, 576, 50000, {
|
|
+ 0x21, 0xf6, 0x94, 0x46, 0x0, 0x0, 0xb, 0x18,
|
|
+ 0x7e, 0x44, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3,
|
|
+ 0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c,
|
|
+ 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3,
|
|
+ 0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5,
|
|
+ 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0,
|
|
+ 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b,
|
|
+ 0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0
|
|
+ } } } },
|
|
+
|
|
+ [TV_NORM_NTSC_M] = { TV_ENC_MODE, {
|
|
+ .tv_enc_mode = { 720, 480, 59940, {
|
|
+ 0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18,
|
|
+ 0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x3c, 0x0,
|
|
+ 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83,
|
|
+ 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
|
|
+ 0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5,
|
|
+ 0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0,
|
|
+ 0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0x9c,
|
|
+ 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
|
|
+ } } } },
|
|
+
|
|
+ [TV_NORM_NTSC_J] = { TV_ENC_MODE, {
|
|
+ .tv_enc_mode = { 720, 480, 59940, {
|
|
+ 0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18,
|
|
+ 0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x32, 0x0,
|
|
+ 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83,
|
|
+ 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
|
|
+ 0xcf, 0x4, 0xcf, 0x1, 0x2, 0x0, 0xa, 0x5,
|
|
+ 0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0,
|
|
+ 0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0xa4,
|
|
+ 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
|
|
+ } } } },
|
|
+
|
|
+ [TV_NORM_HD480I] = { TV_ENC_MODE, {
|
|
+ .tv_enc_mode = { 720, 480, 59940, {
|
|
+ 0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18,
|
|
+ 0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x32, 0x0,
|
|
+ 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83,
|
|
+ 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
|
|
+ 0xcf, 0x4, 0xcf, 0x1, 0x2, 0x0, 0xa, 0x5,
|
|
+ 0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0,
|
|
+ 0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0xa4,
|
|
+ 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
|
|
+ } } } },
|
|
+
|
|
+ [TV_NORM_HD576I] = { TV_ENC_MODE, {
|
|
+ .tv_enc_mode = { 720, 576, 50000, {
|
|
+ 0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18,
|
|
+ 0x7e, 0x40, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3,
|
|
+ 0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c,
|
|
+ 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3,
|
|
+ 0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5,
|
|
+ 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0,
|
|
+ 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b,
|
|
+ 0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0
|
|
+ } } } },
|
|
+
|
|
+
|
|
+ [TV_NORM_HD480P] = { CTV_ENC_MODE, {
|
|
+ .ctv_enc_mode = {
|
|
+ .mode = { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000,
|
|
+ 720, 735, 743, 858, 0, 480, 490, 494, 525, 0,
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
+ .ctv_regs = { 0x3540000, 0x0, 0x0, 0x314,
|
|
+ 0x354003a, 0x40000, 0x6f0344, 0x18100000,
|
|
+ 0x10160004, 0x10060005, 0x1006000c, 0x10060020,
|
|
+ 0x10060021, 0x140e0022, 0x10060202, 0x1802020a,
|
|
+ 0x1810020b, 0x10000fff, 0x10000fff, 0x10000fff,
|
|
+ 0x10000fff, 0x10000fff, 0x10000fff, 0x70,
|
|
+ 0x3ff0000, 0x57, 0x2e001e, 0x258012c,
|
|
+ 0xa0aa04ec, 0x30, 0x80960019, 0x12c0300,
|
|
+ 0x2019, 0x600, 0x32060019, 0x0, 0x0, 0x400
|
|
+ } } } },
|
|
+
|
|
+ [TV_NORM_HD576P] = { CTV_ENC_MODE, {
|
|
+ .ctv_enc_mode = {
|
|
+ .mode = { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000,
|
|
+ 720, 730, 738, 864, 0, 576, 581, 585, 625, 0,
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
+ .ctv_regs = { 0x3540000, 0x0, 0x0, 0x314,
|
|
+ 0x354003a, 0x40000, 0x6f0344, 0x18100000,
|
|
+ 0x10060001, 0x10060009, 0x10060026, 0x10060027,
|
|
+ 0x140e0028, 0x10060268, 0x1810026d, 0x10000fff,
|
|
+ 0x10000fff, 0x10000fff, 0x10000fff, 0x10000fff,
|
|
+ 0x10000fff, 0x10000fff, 0x10000fff, 0x69,
|
|
+ 0x3ff0000, 0x57, 0x2e001e, 0x258012c,
|
|
+ 0xa0aa04ec, 0x30, 0x80960019, 0x12c0300,
|
|
+ 0x2019, 0x600, 0x32060019, 0x0, 0x0, 0x400
|
|
+ } } } },
|
|
+
|
|
+ [TV_NORM_HD720P] = { CTV_ENC_MODE, {
|
|
+ .ctv_enc_mode = {
|
|
+ .mode = { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250,
|
|
+ 1280, 1349, 1357, 1650, 0, 720, 725, 730, 750, 0,
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
+ .ctv_regs = { 0x1260394, 0x0, 0x0, 0x622,
|
|
+ 0x66b0021, 0x6004a, 0x1210626, 0x8170000,
|
|
+ 0x70004, 0x70016, 0x70017, 0x40f0018,
|
|
+ 0x702e8, 0x81702ed, 0xfff, 0xfff,
|
|
+ 0xfff, 0xfff, 0xfff, 0xfff,
|
|
+ 0xfff, 0xfff, 0xfff, 0x0,
|
|
+ 0x2e40001, 0x58, 0x2e001e, 0x258012c,
|
|
+ 0xa0aa04ec, 0x30, 0x810c0039, 0x12c0300,
|
|
+ 0xc0002039, 0x600, 0x32060039, 0x0, 0x0, 0x0
|
|
+ } } } },
|
|
+
|
|
+ [TV_NORM_HD1080I] = { CTV_ENC_MODE, {
|
|
+ .ctv_enc_mode = {
|
|
+ .mode = { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250,
|
|
+ 1920, 1961, 2049, 2200, 0, 1080, 1084, 1088, 1125, 0,
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC
|
|
+ | DRM_MODE_FLAG_INTERLACE) },
|
|
+ .ctv_regs = { 0xac0420, 0x44c0478, 0x4a4, 0x4fc0868,
|
|
+ 0x8940028, 0x60054, 0xe80870, 0xbf70000,
|
|
+ 0xbc70004, 0x70005, 0x70012, 0x70013,
|
|
+ 0x40f0014, 0x70230, 0xbf70232, 0xbf70233,
|
|
+ 0x1c70237, 0x70238, 0x70244, 0x70245,
|
|
+ 0x40f0246, 0x70462, 0x1f70464, 0x0,
|
|
+ 0x2e40001, 0x58, 0x2e001e, 0x258012c,
|
|
+ 0xa0aa04ec, 0x30, 0x815f004c, 0x12c0300,
|
|
+ 0xc000204c, 0x600, 0x3206004c, 0x0, 0x0, 0x0
|
|
+ } } } }
|
|
+};
|
|
+
|
|
+/*
|
|
+ * The following is some guesswork on how the TV encoder flicker
|
|
+ * filter/rescaler works:
|
|
+ *
|
|
+ * It seems to use some sort of resampling filter, it is controlled
|
|
+ * through the registers at NV_PTV_HFILTER and NV_PTV_VFILTER, they
|
|
+ * control the horizontal and vertical stage respectively, there is
|
|
+ * also NV_PTV_HFILTER2 the blob fills identically to NV_PTV_HFILTER,
|
|
+ * but they seem to do nothing. A rough guess might be that they could
|
|
+ * be used to independently control the filtering of each interlaced
|
|
+ * field, but I don't know how they are enabled. The whole filtering
|
|
+ * process seems to be disabled with bits 26:27 of PTV_200, but we
|
|
+ * aren't doing that.
|
|
+ *
|
|
+ * The layout of both register sets is the same:
|
|
+ *
|
|
+ * A: [BASE+0x18]...[BASE+0x0] [BASE+0x58]..[BASE+0x40]
|
|
+ * B: [BASE+0x34]...[BASE+0x1c] [BASE+0x74]..[BASE+0x5c]
|
|
+ *
|
|
+ * Each coefficient is stored in bits [31],[15:9] in two's complement
|
|
+ * format. They seem to be some kind of weights used in a low-pass
|
|
+ * filter. Both A and B coefficients are applied to the 14 nearest
|
|
+ * samples on each side (Listed from nearest to furthermost. They
|
|
+ * roughly cover 2 framebuffer pixels on each side). They are
|
|
+ * probably multiplied with some more hardwired weights before being
|
|
+ * used: B-coefficients are applied the same on both sides,
|
|
+ * A-coefficients are inverted before being applied to the opposite
|
|
+ * side.
|
|
+ *
|
|
+ * After all the hassle, I got the following formula by empirical
|
|
+ * means...
|
|
+ */
|
|
+
|
|
+#define calc_overscan(o) interpolate(0x100, 0xe1, 0xc1, o)
|
|
+
|
|
+#define id1 (1LL << 8)
|
|
+#define id2 (1LL << 16)
|
|
+#define id3 (1LL << 24)
|
|
+#define id4 (1LL << 32)
|
|
+#define id5 (1LL << 48)
|
|
+
|
|
+static struct filter_params{
|
|
+ int64_t k1;
|
|
+ int64_t ki;
|
|
+ int64_t ki2;
|
|
+ int64_t ki3;
|
|
+ int64_t kr;
|
|
+ int64_t kir;
|
|
+ int64_t ki2r;
|
|
+ int64_t ki3r;
|
|
+ int64_t kf;
|
|
+ int64_t kif;
|
|
+ int64_t ki2f;
|
|
+ int64_t ki3f;
|
|
+ int64_t krf;
|
|
+ int64_t kirf;
|
|
+ int64_t ki2rf;
|
|
+ int64_t ki3rf;
|
|
+} fparams[2][4] = {
|
|
+ /* Horizontal filter parameters */
|
|
+ {
|
|
+ {64.311690 * id5, -39.516924 * id5, 6.586143 * id5, 0.000002 * id5,
|
|
+ 0.051285 * id4, 26.168746 * id4, -4.361449 * id4, -0.000001 * id4,
|
|
+ 9.308169 * id3, 78.180965 * id3, -13.030158 * id3, -0.000001 * id3,
|
|
+ -8.801540 * id1, -46.572890 * id1, 7.762145 * id1, -0.000000 * id1},
|
|
+ {-44.565569 * id5, -68.081246 * id5, 39.812074 * id5, -4.009316 * id5,
|
|
+ 29.832207 * id4, 50.047322 * id4, -25.380017 * id4, 2.546422 * id4,
|
|
+ 104.605622 * id3, 141.908641 * id3, -74.322319 * id3, 7.484316 * id3,
|
|
+ -37.081621 * id1, -90.397510 * id1, 42.784229 * id1, -4.289952 * id1},
|
|
+ {-56.793244 * id5, 31.153584 * id5, -5.192247 * id5, -0.000003 * id5,
|
|
+ 33.541131 * id4, -34.149302 * id4, 5.691537 * id4, 0.000002 * id4,
|
|
+ 87.196610 * id3, -88.995169 * id3, 14.832456 * id3, 0.000012 * id3,
|
|
+ 17.288138 * id1, 71.864786 * id1, -11.977408 * id1, -0.000009 * id1},
|
|
+ {51.787796 * id5, 21.211771 * id5, -18.993730 * id5, 1.853310 * id5,
|
|
+ -41.470726 * id4, -17.775823 * id4, 13.057821 * id4, -1.15823 * id4,
|
|
+ -154.235673 * id3, -44.878641 * id3, 40.656077 * id3, -3.695595 * id3,
|
|
+ 112.201065 * id1, 39.992155 * id1, -25.155714 * id1, 2.113984 * id1},
|
|
+ },
|
|
+
|
|
+ /* Vertical filter parameters */
|
|
+ {
|
|
+ {67.601979 * id5, 0.428319 * id5, -0.071318 * id5, -0.000012 * id5,
|
|
+ -3.402339 * id4, 0.000209 * id4, -0.000092 * id4, 0.000010 * id4,
|
|
+ -9.180996 * id3, 6.111270 * id3, -1.024457 * id3, 0.001043 * id3,
|
|
+ 6.060315 * id1, -0.017425 * id1, 0.007830 * id1, -0.000869 * id1},
|
|
+ {6.755647 * id5, 5.841348 * id5, 1.469734 * id5, -0.149656 * id5,
|
|
+ 8.293120 * id4, -1.192888 * id4, -0.947652 * id4, 0.094507 * id4,
|
|
+ 37.526655 * id3, 10.257875 * id3, -10.823275 * id3, 1.081497 * id3,
|
|
+ -2.361928 * id1, -2.059432 * id1, 1.840671 * id1, -0.168100 * id1},
|
|
+ {-14.780391 * id5, -16.042148 * id5, 2.673692 * id5, -0.000000 * id5,
|
|
+ 39.541978 * id4, 5.680053 * id4, -0.946676 * id4, 0.000000 * id4,
|
|
+ 152.994486 * id3, 12.625439 * id3, -2.119579 * id3, 0.002708 * id3,
|
|
+ -38.125089 * id1, -0.855880 * id1, 0.155359 * id1, -0.002245 * id1},
|
|
+ {-27.476193 * id5, -1.454976 * id5, 1.286557 * id5, 0.025346 * id5,
|
|
+ 20.687300 * id4, 3.014003 * id4, -0.557786 * id4, -0.01311 * id4,
|
|
+ 60.008737 * id3, -0.738273 * id3, 5.408217 * id3, -0.796798 * id3,
|
|
+ -17.296835 * id1, 4.438577 * id1, -2.809420 * id1, 0.385491 * id1},
|
|
+ }
|
|
+};
|
|
+
|
|
+static void tv_setup_filter(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
|
|
+ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
|
|
+ struct drm_display_mode *mode = &encoder->crtc->mode;
|
|
+ uint32_t (*filters[])[4][7] = {&tv_enc->state.hfilter,
|
|
+ &tv_enc->state.vfilter};
|
|
+ int i, j, k;
|
|
+ int32_t overscan = calc_overscan(tv_enc->overscan);
|
|
+ int64_t flicker = (tv_enc->flicker - 50) * (id3 / 100);
|
|
+ uint64_t rs[] = {mode->hdisplay * id3,
|
|
+ mode->vdisplay * id3};
|
|
+
|
|
+ do_div(rs[0], overscan * tv_norm->tv_enc_mode.hdisplay);
|
|
+ do_div(rs[1], overscan * tv_norm->tv_enc_mode.vdisplay);
|
|
+
|
|
+ for (k = 0; k < 2; k++) {
|
|
+ rs[k] = max((int64_t)rs[k], id2);
|
|
+
|
|
+ for (j = 0; j < 4; j++) {
|
|
+ struct filter_params *p = &fparams[k][j];
|
|
+
|
|
+ for (i = 0; i < 7; i++) {
|
|
+ int64_t c = (p->k1 + p->ki*i + p->ki2*i*i + p->ki3*i*i*i)
|
|
+ + (p->kr + p->kir*i + p->ki2r*i*i + p->ki3r*i*i*i)*rs[k]
|
|
+ + (p->kf + p->kif*i + p->ki2f*i*i + p->ki3f*i*i*i)*flicker
|
|
+ + (p->krf + p->kirf*i + p->ki2rf*i*i + p->ki3rf*i*i*i)*flicker*rs[k];
|
|
+
|
|
+ (*filters[k])[j][i] = (c + id5/2) >> 39 & (0x1 << 31 | 0x7f << 9);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Hardware state saving/restoring */
|
|
+
|
|
+static void tv_save_filter(struct drm_device *dev, uint32_t base, uint32_t regs[4][7])
|
|
+{
|
|
+ int i, j;
|
|
+ uint32_t offsets[] = { base, base + 0x1c, base + 0x40, base + 0x5c };
|
|
+
|
|
+ for (i = 0; i < 4; i++) {
|
|
+ for (j = 0; j < 7; j++)
|
|
+ regs[i][j] = nv_read_ptv(dev, offsets[i]+4*j);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tv_load_filter(struct drm_device *dev, uint32_t base, uint32_t regs[4][7])
|
|
+{
|
|
+ int i, j;
|
|
+ uint32_t offsets[] = { base, base + 0x1c, base + 0x40, base + 0x5c };
|
|
+
|
|
+ for (i = 0; i < 4; i++) {
|
|
+ for (j = 0; j < 7; j++)
|
|
+ nv_write_ptv(dev, offsets[i]+4*j, regs[i][j]);
|
|
+ }
|
|
+}
|
|
+
|
|
+void nv17_tv_state_save(struct drm_device *dev, struct nv17_tv_state *state)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < 0x40; i++)
|
|
+ state->tv_enc[i] = nv_read_tv_enc(dev, i);
|
|
+
|
|
+ tv_save_filter(dev, NV_PTV_HFILTER, state->hfilter);
|
|
+ tv_save_filter(dev, NV_PTV_HFILTER2, state->hfilter2);
|
|
+ tv_save_filter(dev, NV_PTV_VFILTER, state->vfilter);
|
|
+
|
|
+ nv_save_ptv(dev, state, 200);
|
|
+ nv_save_ptv(dev, state, 204);
|
|
+ nv_save_ptv(dev, state, 208);
|
|
+ nv_save_ptv(dev, state, 20c);
|
|
+ nv_save_ptv(dev, state, 304);
|
|
+ nv_save_ptv(dev, state, 500);
|
|
+ nv_save_ptv(dev, state, 504);
|
|
+ nv_save_ptv(dev, state, 508);
|
|
+ nv_save_ptv(dev, state, 600);
|
|
+ nv_save_ptv(dev, state, 604);
|
|
+ nv_save_ptv(dev, state, 608);
|
|
+ nv_save_ptv(dev, state, 60c);
|
|
+ nv_save_ptv(dev, state, 610);
|
|
+ nv_save_ptv(dev, state, 614);
|
|
+}
|
|
+
|
|
+void nv17_tv_state_load(struct drm_device *dev, struct nv17_tv_state *state)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < 0x40; i++)
|
|
+ nv_write_tv_enc(dev, i, state->tv_enc[i]);
|
|
+
|
|
+ tv_load_filter(dev, NV_PTV_HFILTER, state->hfilter);
|
|
+ tv_load_filter(dev, NV_PTV_HFILTER2, state->hfilter2);
|
|
+ tv_load_filter(dev, NV_PTV_VFILTER, state->vfilter);
|
|
+
|
|
+ nv_load_ptv(dev, state, 200);
|
|
+ nv_load_ptv(dev, state, 204);
|
|
+ nv_load_ptv(dev, state, 208);
|
|
+ nv_load_ptv(dev, state, 20c);
|
|
+ nv_load_ptv(dev, state, 304);
|
|
+ nv_load_ptv(dev, state, 500);
|
|
+ nv_load_ptv(dev, state, 504);
|
|
+ nv_load_ptv(dev, state, 508);
|
|
+ nv_load_ptv(dev, state, 600);
|
|
+ nv_load_ptv(dev, state, 604);
|
|
+ nv_load_ptv(dev, state, 608);
|
|
+ nv_load_ptv(dev, state, 60c);
|
|
+ nv_load_ptv(dev, state, 610);
|
|
+ nv_load_ptv(dev, state, 614);
|
|
+
|
|
+ /* This is required for some settings to kick in. */
|
|
+ nv_write_tv_enc(dev, 0x3e, 1);
|
|
+ nv_write_tv_enc(dev, 0x3e, 0);
|
|
+}
|
|
+
|
|
+/* Timings similar to the ones the blob sets */
|
|
+
|
|
+struct drm_display_mode nv17_tv_modes[] = {
|
|
+ { DRM_MODE("320x200", DRM_MODE_TYPE_DRIVER, 0,
|
|
+ 320, 344, 392, 560, 0, 200, 200, 202, 220, 0,
|
|
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC
|
|
+ | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) },
|
|
+ { DRM_MODE("320x240", DRM_MODE_TYPE_DRIVER, 0,
|
|
+ 320, 344, 392, 560, 0, 240, 240, 246, 263, 0,
|
|
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC
|
|
+ | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) },
|
|
+ { DRM_MODE("400x300", DRM_MODE_TYPE_DRIVER, 0,
|
|
+ 400, 432, 496, 640, 0, 300, 300, 303, 314, 0,
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC
|
|
+ | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) },
|
|
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 0,
|
|
+ 640, 672, 768, 880, 0, 480, 480, 492, 525, 0,
|
|
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
|
+ { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 0,
|
|
+ 720, 752, 872, 960, 0, 480, 480, 493, 525, 0,
|
|
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
|
+ { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 0,
|
|
+ 720, 776, 856, 960, 0, 576, 576, 588, 597, 0,
|
|
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
|
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 0,
|
|
+ 800, 840, 920, 1040, 0, 600, 600, 604, 618, 0,
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 0,
|
|
+ 1024, 1064, 1200, 1344, 0, 768, 768, 777, 806, 0,
|
|
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
|
+ {}
|
|
+};
|
|
+
|
|
+void nv17_tv_update_properties(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
|
|
+ struct nv17_tv_state *regs = &tv_enc->state;
|
|
+ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
|
|
+ int subconnector = tv_enc->select_subconnector ?
|
|
+ tv_enc->select_subconnector :
|
|
+ tv_enc->subconnector;
|
|
+
|
|
+ switch (subconnector) {
|
|
+ case DRM_MODE_SUBCONNECTOR_Composite:
|
|
+ {
|
|
+ regs->ptv_204 = 0x2;
|
|
+
|
|
+ /* The composite connector may be found on either pin. */
|
|
+ if (tv_enc->pin_mask & 0x4)
|
|
+ regs->ptv_204 |= 0x010000;
|
|
+ else if (tv_enc->pin_mask & 0x2)
|
|
+ regs->ptv_204 |= 0x100000;
|
|
+ else
|
|
+ regs->ptv_204 |= 0x110000;
|
|
+
|
|
+ regs->tv_enc[0x7] = 0x10;
|
|
+ break;
|
|
+ }
|
|
+ case DRM_MODE_SUBCONNECTOR_SVIDEO:
|
|
+ regs->ptv_204 = 0x11012;
|
|
+ regs->tv_enc[0x7] = 0x18;
|
|
+ break;
|
|
+
|
|
+ case DRM_MODE_SUBCONNECTOR_Component:
|
|
+ regs->ptv_204 = 0x111333;
|
|
+ regs->tv_enc[0x7] = 0x14;
|
|
+ break;
|
|
+
|
|
+ case DRM_MODE_SUBCONNECTOR_SCART:
|
|
+ regs->ptv_204 = 0x111012;
|
|
+ regs->tv_enc[0x7] = 0x18;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ regs->tv_enc[0x20] = interpolate(0, tv_norm->tv_enc_mode.tv_enc[0x20], 255,
|
|
+ tv_enc->saturation);
|
|
+ regs->tv_enc[0x22] = interpolate(0, tv_norm->tv_enc_mode.tv_enc[0x22], 255,
|
|
+ tv_enc->saturation);
|
|
+ regs->tv_enc[0x25] = tv_enc->hue * 255 / 100;
|
|
+
|
|
+ nv_load_ptv(dev, regs, 204);
|
|
+ nv_load_tv_enc(dev, regs, 7);
|
|
+ nv_load_tv_enc(dev, regs, 20);
|
|
+ nv_load_tv_enc(dev, regs, 22);
|
|
+ nv_load_tv_enc(dev, regs, 25);
|
|
+}
|
|
+
|
|
+void nv17_tv_update_rescaler(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
|
|
+ struct nv17_tv_state *regs = &tv_enc->state;
|
|
+
|
|
+ regs->ptv_208 = 0x40 | (calc_overscan(tv_enc->overscan) << 8);
|
|
+
|
|
+ tv_setup_filter(encoder);
|
|
+
|
|
+ nv_load_ptv(dev, regs, 208);
|
|
+ tv_load_filter(dev, NV_PTV_HFILTER, regs->hfilter);
|
|
+ tv_load_filter(dev, NV_PTV_HFILTER2, regs->hfilter2);
|
|
+ tv_load_filter(dev, NV_PTV_VFILTER, regs->vfilter);
|
|
+}
|
|
+
|
|
+void nv17_ctv_update_rescaler(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
|
|
+ int head = nouveau_crtc(encoder->crtc)->index;
|
|
+ struct nv04_crtc_reg *regs = &dev_priv->mode_reg.crtc_reg[head];
|
|
+ struct drm_display_mode *crtc_mode = &encoder->crtc->mode;
|
|
+ struct drm_display_mode *output_mode = &get_tv_norm(encoder)->ctv_enc_mode.mode;
|
|
+ int overscan, hmargin, vmargin, hratio, vratio;
|
|
+
|
|
+ /* The rescaler doesn't do the right thing for interlaced modes. */
|
|
+ if (output_mode->flags & DRM_MODE_FLAG_INTERLACE)
|
|
+ overscan = 100;
|
|
+ else
|
|
+ overscan = tv_enc->overscan;
|
|
+
|
|
+ hmargin = (output_mode->hdisplay - crtc_mode->hdisplay) / 2;
|
|
+ vmargin = (output_mode->vdisplay - crtc_mode->vdisplay) / 2;
|
|
+
|
|
+ hmargin = interpolate(0, min(hmargin, output_mode->hdisplay/20), hmargin,
|
|
+ overscan);
|
|
+ vmargin = interpolate(0, min(vmargin, output_mode->vdisplay/20), vmargin,
|
|
+ overscan);
|
|
+
|
|
+ hratio = crtc_mode->hdisplay * 0x800 / (output_mode->hdisplay - 2*hmargin);
|
|
+ vratio = crtc_mode->vdisplay * 0x800 / (output_mode->vdisplay - 2*vmargin) & ~3;
|
|
+
|
|
+ regs->fp_horiz_regs[FP_VALID_START] = hmargin;
|
|
+ regs->fp_horiz_regs[FP_VALID_END] = output_mode->hdisplay - hmargin - 1;
|
|
+ regs->fp_vert_regs[FP_VALID_START] = vmargin;
|
|
+ regs->fp_vert_regs[FP_VALID_END] = output_mode->vdisplay - vmargin - 1;
|
|
+
|
|
+ regs->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE |
|
|
+ XLATE(vratio, 0, NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE) |
|
|
+ NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE |
|
|
+ XLATE(hratio, 0, NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE);
|
|
+
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HVALID_START,
|
|
+ regs->fp_horiz_regs[FP_VALID_START]);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HVALID_END,
|
|
+ regs->fp_horiz_regs[FP_VALID_END]);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_VVALID_START,
|
|
+ regs->fp_vert_regs[FP_VALID_START]);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_VVALID_END,
|
|
+ regs->fp_vert_regs[FP_VALID_END]);
|
|
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1, regs->fp_debug_1);
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nv20_graph.c b/drivers/gpu/drm/nouveau/nv20_graph.c
|
|
new file mode 100644
|
|
index 0000000..d6fc0a8
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv20_graph.c
|
|
@@ -0,0 +1,775 @@
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_drm.h"
|
|
+
|
|
+/*
|
|
+ * NV20
|
|
+ * -----
|
|
+ * There are 3 families :
|
|
+ * NV20 is 0x10de:0x020*
|
|
+ * NV25/28 is 0x10de:0x025* / 0x10de:0x028*
|
|
+ * NV2A is 0x10de:0x02A0
|
|
+ *
|
|
+ * NV30
|
|
+ * -----
|
|
+ * There are 3 families :
|
|
+ * NV30/31 is 0x10de:0x030* / 0x10de:0x031*
|
|
+ * NV34 is 0x10de:0x032*
|
|
+ * NV35/36 is 0x10de:0x033* / 0x10de:0x034*
|
|
+ *
|
|
+ * Not seen in the wild, no dumps (probably NV35) :
|
|
+ * NV37 is 0x10de:0x00fc, 0x10de:0x00fd
|
|
+ * NV38 is 0x10de:0x0333, 0x10de:0x00fe
|
|
+ *
|
|
+ */
|
|
+
|
|
+#define NV20_GRCTX_SIZE (3580*4)
|
|
+#define NV25_GRCTX_SIZE (3529*4)
|
|
+#define NV2A_GRCTX_SIZE (3500*4)
|
|
+
|
|
+#define NV30_31_GRCTX_SIZE (24392)
|
|
+#define NV34_GRCTX_SIZE (18140)
|
|
+#define NV35_36_GRCTX_SIZE (22396)
|
|
+
|
|
+static void
|
|
+nv20_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ nv_wo32(dev, ctx, 0x033c/4, 0xffff0000);
|
|
+ nv_wo32(dev, ctx, 0x03a0/4, 0x0fff0000);
|
|
+ nv_wo32(dev, ctx, 0x03a4/4, 0x0fff0000);
|
|
+ nv_wo32(dev, ctx, 0x047c/4, 0x00000101);
|
|
+ nv_wo32(dev, ctx, 0x0490/4, 0x00000111);
|
|
+ nv_wo32(dev, ctx, 0x04a8/4, 0x44400000);
|
|
+ for (i = 0x04d4; i <= 0x04e0; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x00030303);
|
|
+ for (i = 0x04f4; i <= 0x0500; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x00080000);
|
|
+ for (i = 0x050c; i <= 0x0518; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x01012000);
|
|
+ for (i = 0x051c; i <= 0x0528; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x000105b8);
|
|
+ for (i = 0x052c; i <= 0x0538; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x00080008);
|
|
+ for (i = 0x055c; i <= 0x0598; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x07ff0000);
|
|
+ nv_wo32(dev, ctx, 0x05a4/4, 0x4b7fffff);
|
|
+ nv_wo32(dev, ctx, 0x05fc/4, 0x00000001);
|
|
+ nv_wo32(dev, ctx, 0x0604/4, 0x00004000);
|
|
+ nv_wo32(dev, ctx, 0x0610/4, 0x00000001);
|
|
+ nv_wo32(dev, ctx, 0x0618/4, 0x00040000);
|
|
+ nv_wo32(dev, ctx, 0x061c/4, 0x00010000);
|
|
+ for (i = 0x1c1c; i <= 0x248c; i += 16) {
|
|
+ nv_wo32(dev, ctx, (i + 0)/4, 0x10700ff9);
|
|
+ nv_wo32(dev, ctx, (i + 4)/4, 0x0436086c);
|
|
+ nv_wo32(dev, ctx, (i + 8)/4, 0x000c001b);
|
|
+ }
|
|
+ nv_wo32(dev, ctx, 0x281c/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x2830/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x285c/4, 0x40000000);
|
|
+ nv_wo32(dev, ctx, 0x2860/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x2864/4, 0x3f000000);
|
|
+ nv_wo32(dev, ctx, 0x286c/4, 0x40000000);
|
|
+ nv_wo32(dev, ctx, 0x2870/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x2878/4, 0xbf800000);
|
|
+ nv_wo32(dev, ctx, 0x2880/4, 0xbf800000);
|
|
+ nv_wo32(dev, ctx, 0x34a4/4, 0x000fe000);
|
|
+ nv_wo32(dev, ctx, 0x3530/4, 0x000003f8);
|
|
+ nv_wo32(dev, ctx, 0x3540/4, 0x002fe000);
|
|
+ for (i = 0x355c; i <= 0x3578; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x001c527c);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv25_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ nv_wo32(dev, ctx, 0x035c/4, 0xffff0000);
|
|
+ nv_wo32(dev, ctx, 0x03c0/4, 0x0fff0000);
|
|
+ nv_wo32(dev, ctx, 0x03c4/4, 0x0fff0000);
|
|
+ nv_wo32(dev, ctx, 0x049c/4, 0x00000101);
|
|
+ nv_wo32(dev, ctx, 0x04b0/4, 0x00000111);
|
|
+ nv_wo32(dev, ctx, 0x04c8/4, 0x00000080);
|
|
+ nv_wo32(dev, ctx, 0x04cc/4, 0xffff0000);
|
|
+ nv_wo32(dev, ctx, 0x04d0/4, 0x00000001);
|
|
+ nv_wo32(dev, ctx, 0x04e4/4, 0x44400000);
|
|
+ nv_wo32(dev, ctx, 0x04fc/4, 0x4b800000);
|
|
+ for (i = 0x0510; i <= 0x051c; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x00030303);
|
|
+ for (i = 0x0530; i <= 0x053c; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x00080000);
|
|
+ for (i = 0x0548; i <= 0x0554; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x01012000);
|
|
+ for (i = 0x0558; i <= 0x0564; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x000105b8);
|
|
+ for (i = 0x0568; i <= 0x0574; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x00080008);
|
|
+ for (i = 0x0598; i <= 0x05d4; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x07ff0000);
|
|
+ nv_wo32(dev, ctx, 0x05e0/4, 0x4b7fffff);
|
|
+ nv_wo32(dev, ctx, 0x0620/4, 0x00000080);
|
|
+ nv_wo32(dev, ctx, 0x0624/4, 0x30201000);
|
|
+ nv_wo32(dev, ctx, 0x0628/4, 0x70605040);
|
|
+ nv_wo32(dev, ctx, 0x062c/4, 0xb0a09080);
|
|
+ nv_wo32(dev, ctx, 0x0630/4, 0xf0e0d0c0);
|
|
+ nv_wo32(dev, ctx, 0x0664/4, 0x00000001);
|
|
+ nv_wo32(dev, ctx, 0x066c/4, 0x00004000);
|
|
+ nv_wo32(dev, ctx, 0x0678/4, 0x00000001);
|
|
+ nv_wo32(dev, ctx, 0x0680/4, 0x00040000);
|
|
+ nv_wo32(dev, ctx, 0x0684/4, 0x00010000);
|
|
+ for (i = 0x1b04; i <= 0x2374; i += 16) {
|
|
+ nv_wo32(dev, ctx, (i + 0)/4, 0x10700ff9);
|
|
+ nv_wo32(dev, ctx, (i + 4)/4, 0x0436086c);
|
|
+ nv_wo32(dev, ctx, (i + 8)/4, 0x000c001b);
|
|
+ }
|
|
+ nv_wo32(dev, ctx, 0x2704/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x2718/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x2744/4, 0x40000000);
|
|
+ nv_wo32(dev, ctx, 0x2748/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x274c/4, 0x3f000000);
|
|
+ nv_wo32(dev, ctx, 0x2754/4, 0x40000000);
|
|
+ nv_wo32(dev, ctx, 0x2758/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x2760/4, 0xbf800000);
|
|
+ nv_wo32(dev, ctx, 0x2768/4, 0xbf800000);
|
|
+ nv_wo32(dev, ctx, 0x308c/4, 0x000fe000);
|
|
+ nv_wo32(dev, ctx, 0x3108/4, 0x000003f8);
|
|
+ nv_wo32(dev, ctx, 0x3468/4, 0x002fe000);
|
|
+ for (i = 0x3484; i <= 0x34a0; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x001c527c);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv2a_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ nv_wo32(dev, ctx, 0x033c/4, 0xffff0000);
|
|
+ nv_wo32(dev, ctx, 0x03a0/4, 0x0fff0000);
|
|
+ nv_wo32(dev, ctx, 0x03a4/4, 0x0fff0000);
|
|
+ nv_wo32(dev, ctx, 0x047c/4, 0x00000101);
|
|
+ nv_wo32(dev, ctx, 0x0490/4, 0x00000111);
|
|
+ nv_wo32(dev, ctx, 0x04a8/4, 0x44400000);
|
|
+ for (i = 0x04d4; i <= 0x04e0; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x00030303);
|
|
+ for (i = 0x04f4; i <= 0x0500; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x00080000);
|
|
+ for (i = 0x050c; i <= 0x0518; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x01012000);
|
|
+ for (i = 0x051c; i <= 0x0528; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x000105b8);
|
|
+ for (i = 0x052c; i <= 0x0538; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x00080008);
|
|
+ for (i = 0x055c; i <= 0x0598; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x07ff0000);
|
|
+ nv_wo32(dev, ctx, 0x05a4/4, 0x4b7fffff);
|
|
+ nv_wo32(dev, ctx, 0x05fc/4, 0x00000001);
|
|
+ nv_wo32(dev, ctx, 0x0604/4, 0x00004000);
|
|
+ nv_wo32(dev, ctx, 0x0610/4, 0x00000001);
|
|
+ nv_wo32(dev, ctx, 0x0618/4, 0x00040000);
|
|
+ nv_wo32(dev, ctx, 0x061c/4, 0x00010000);
|
|
+ for (i = 0x1a9c; i <= 0x22fc; i += 16) { /*XXX: check!! */
|
|
+ nv_wo32(dev, ctx, (i + 0)/4, 0x10700ff9);
|
|
+ nv_wo32(dev, ctx, (i + 4)/4, 0x0436086c);
|
|
+ nv_wo32(dev, ctx, (i + 8)/4, 0x000c001b);
|
|
+ }
|
|
+ nv_wo32(dev, ctx, 0x269c/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x26b0/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x26dc/4, 0x40000000);
|
|
+ nv_wo32(dev, ctx, 0x26e0/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x26e4/4, 0x3f000000);
|
|
+ nv_wo32(dev, ctx, 0x26ec/4, 0x40000000);
|
|
+ nv_wo32(dev, ctx, 0x26f0/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x26f8/4, 0xbf800000);
|
|
+ nv_wo32(dev, ctx, 0x2700/4, 0xbf800000);
|
|
+ nv_wo32(dev, ctx, 0x3024/4, 0x000fe000);
|
|
+ nv_wo32(dev, ctx, 0x30a0/4, 0x000003f8);
|
|
+ nv_wo32(dev, ctx, 0x33fc/4, 0x002fe000);
|
|
+ for (i = 0x341c; i <= 0x3438; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x001c527c);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv30_31_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ nv_wo32(dev, ctx, 0x0410/4, 0x00000101);
|
|
+ nv_wo32(dev, ctx, 0x0424/4, 0x00000111);
|
|
+ nv_wo32(dev, ctx, 0x0428/4, 0x00000060);
|
|
+ nv_wo32(dev, ctx, 0x0444/4, 0x00000080);
|
|
+ nv_wo32(dev, ctx, 0x0448/4, 0xffff0000);
|
|
+ nv_wo32(dev, ctx, 0x044c/4, 0x00000001);
|
|
+ nv_wo32(dev, ctx, 0x0460/4, 0x44400000);
|
|
+ nv_wo32(dev, ctx, 0x048c/4, 0xffff0000);
|
|
+ for (i = 0x04e0; i < 0x04e8; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x0fff0000);
|
|
+ nv_wo32(dev, ctx, 0x04ec/4, 0x00011100);
|
|
+ for (i = 0x0508; i < 0x0548; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x07ff0000);
|
|
+ nv_wo32(dev, ctx, 0x0550/4, 0x4b7fffff);
|
|
+ nv_wo32(dev, ctx, 0x058c/4, 0x00000080);
|
|
+ nv_wo32(dev, ctx, 0x0590/4, 0x30201000);
|
|
+ nv_wo32(dev, ctx, 0x0594/4, 0x70605040);
|
|
+ nv_wo32(dev, ctx, 0x0598/4, 0xb8a89888);
|
|
+ nv_wo32(dev, ctx, 0x059c/4, 0xf8e8d8c8);
|
|
+ nv_wo32(dev, ctx, 0x05b0/4, 0xb0000000);
|
|
+ for (i = 0x0600; i < 0x0640; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x00010588);
|
|
+ for (i = 0x0640; i < 0x0680; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x00030303);
|
|
+ for (i = 0x06c0; i < 0x0700; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x0008aae4);
|
|
+ for (i = 0x0700; i < 0x0740; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x01012000);
|
|
+ for (i = 0x0740; i < 0x0780; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x00080008);
|
|
+ nv_wo32(dev, ctx, 0x085c/4, 0x00040000);
|
|
+ nv_wo32(dev, ctx, 0x0860/4, 0x00010000);
|
|
+ for (i = 0x0864; i < 0x0874; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x00040004);
|
|
+ for (i = 0x1f18; i <= 0x3088 ; i += 16) {
|
|
+ nv_wo32(dev, ctx, i/4 + 0, 0x10700ff9);
|
|
+ nv_wo32(dev, ctx, i/4 + 1, 0x0436086c);
|
|
+ nv_wo32(dev, ctx, i/4 + 2, 0x000c001b);
|
|
+ }
|
|
+ for (i = 0x30b8; i < 0x30c8; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x0000ffff);
|
|
+ nv_wo32(dev, ctx, 0x344c/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x3808/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x381c/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x3848/4, 0x40000000);
|
|
+ nv_wo32(dev, ctx, 0x384c/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x3850/4, 0x3f000000);
|
|
+ nv_wo32(dev, ctx, 0x3858/4, 0x40000000);
|
|
+ nv_wo32(dev, ctx, 0x385c/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x3864/4, 0xbf800000);
|
|
+ nv_wo32(dev, ctx, 0x386c/4, 0xbf800000);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv34_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ nv_wo32(dev, ctx, 0x040c/4, 0x01000101);
|
|
+ nv_wo32(dev, ctx, 0x0420/4, 0x00000111);
|
|
+ nv_wo32(dev, ctx, 0x0424/4, 0x00000060);
|
|
+ nv_wo32(dev, ctx, 0x0440/4, 0x00000080);
|
|
+ nv_wo32(dev, ctx, 0x0444/4, 0xffff0000);
|
|
+ nv_wo32(dev, ctx, 0x0448/4, 0x00000001);
|
|
+ nv_wo32(dev, ctx, 0x045c/4, 0x44400000);
|
|
+ nv_wo32(dev, ctx, 0x0480/4, 0xffff0000);
|
|
+ for (i = 0x04d4; i < 0x04dc; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x0fff0000);
|
|
+ nv_wo32(dev, ctx, 0x04e0/4, 0x00011100);
|
|
+ for (i = 0x04fc; i < 0x053c; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x07ff0000);
|
|
+ nv_wo32(dev, ctx, 0x0544/4, 0x4b7fffff);
|
|
+ nv_wo32(dev, ctx, 0x057c/4, 0x00000080);
|
|
+ nv_wo32(dev, ctx, 0x0580/4, 0x30201000);
|
|
+ nv_wo32(dev, ctx, 0x0584/4, 0x70605040);
|
|
+ nv_wo32(dev, ctx, 0x0588/4, 0xb8a89888);
|
|
+ nv_wo32(dev, ctx, 0x058c/4, 0xf8e8d8c8);
|
|
+ nv_wo32(dev, ctx, 0x05a0/4, 0xb0000000);
|
|
+ for (i = 0x05f0; i < 0x0630; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x00010588);
|
|
+ for (i = 0x0630; i < 0x0670; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x00030303);
|
|
+ for (i = 0x06b0; i < 0x06f0; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x0008aae4);
|
|
+ for (i = 0x06f0; i < 0x0730; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x01012000);
|
|
+ for (i = 0x0730; i < 0x0770; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x00080008);
|
|
+ nv_wo32(dev, ctx, 0x0850/4, 0x00040000);
|
|
+ nv_wo32(dev, ctx, 0x0854/4, 0x00010000);
|
|
+ for (i = 0x0858; i < 0x0868; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x00040004);
|
|
+ for (i = 0x15ac; i <= 0x271c ; i += 16) {
|
|
+ nv_wo32(dev, ctx, i/4 + 0, 0x10700ff9);
|
|
+ nv_wo32(dev, ctx, i/4 + 1, 0x0436086c);
|
|
+ nv_wo32(dev, ctx, i/4 + 2, 0x000c001b);
|
|
+ }
|
|
+ for (i = 0x274c; i < 0x275c; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x0000ffff);
|
|
+ nv_wo32(dev, ctx, 0x2ae0/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x2e9c/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x2eb0/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x2edc/4, 0x40000000);
|
|
+ nv_wo32(dev, ctx, 0x2ee0/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x2ee4/4, 0x3f000000);
|
|
+ nv_wo32(dev, ctx, 0x2eec/4, 0x40000000);
|
|
+ nv_wo32(dev, ctx, 0x2ef0/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x2ef8/4, 0xbf800000);
|
|
+ nv_wo32(dev, ctx, 0x2f00/4, 0xbf800000);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv35_36_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ nv_wo32(dev, ctx, 0x040c/4, 0x00000101);
|
|
+ nv_wo32(dev, ctx, 0x0420/4, 0x00000111);
|
|
+ nv_wo32(dev, ctx, 0x0424/4, 0x00000060);
|
|
+ nv_wo32(dev, ctx, 0x0440/4, 0x00000080);
|
|
+ nv_wo32(dev, ctx, 0x0444/4, 0xffff0000);
|
|
+ nv_wo32(dev, ctx, 0x0448/4, 0x00000001);
|
|
+ nv_wo32(dev, ctx, 0x045c/4, 0x44400000);
|
|
+ nv_wo32(dev, ctx, 0x0488/4, 0xffff0000);
|
|
+ for (i = 0x04dc; i < 0x04e4; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x0fff0000);
|
|
+ nv_wo32(dev, ctx, 0x04e8/4, 0x00011100);
|
|
+ for (i = 0x0504; i < 0x0544; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x07ff0000);
|
|
+ nv_wo32(dev, ctx, 0x054c/4, 0x4b7fffff);
|
|
+ nv_wo32(dev, ctx, 0x0588/4, 0x00000080);
|
|
+ nv_wo32(dev, ctx, 0x058c/4, 0x30201000);
|
|
+ nv_wo32(dev, ctx, 0x0590/4, 0x70605040);
|
|
+ nv_wo32(dev, ctx, 0x0594/4, 0xb8a89888);
|
|
+ nv_wo32(dev, ctx, 0x0598/4, 0xf8e8d8c8);
|
|
+ nv_wo32(dev, ctx, 0x05ac/4, 0xb0000000);
|
|
+ for (i = 0x0604; i < 0x0644; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x00010588);
|
|
+ for (i = 0x0644; i < 0x0684; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x00030303);
|
|
+ for (i = 0x06c4; i < 0x0704; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x0008aae4);
|
|
+ for (i = 0x0704; i < 0x0744; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x01012000);
|
|
+ for (i = 0x0744; i < 0x0784; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x00080008);
|
|
+ nv_wo32(dev, ctx, 0x0860/4, 0x00040000);
|
|
+ nv_wo32(dev, ctx, 0x0864/4, 0x00010000);
|
|
+ for (i = 0x0868; i < 0x0878; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x00040004);
|
|
+ for (i = 0x1f1c; i <= 0x308c ; i += 16) {
|
|
+ nv_wo32(dev, ctx, i/4 + 0, 0x10700ff9);
|
|
+ nv_wo32(dev, ctx, i/4 + 1, 0x0436086c);
|
|
+ nv_wo32(dev, ctx, i/4 + 2, 0x000c001b);
|
|
+ }
|
|
+ for (i = 0x30bc; i < 0x30cc; i += 4)
|
|
+ nv_wo32(dev, ctx, i/4, 0x0000ffff);
|
|
+ nv_wo32(dev, ctx, 0x3450/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x380c/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x3820/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x384c/4, 0x40000000);
|
|
+ nv_wo32(dev, ctx, 0x3850/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x3854/4, 0x3f000000);
|
|
+ nv_wo32(dev, ctx, 0x385c/4, 0x40000000);
|
|
+ nv_wo32(dev, ctx, 0x3860/4, 0x3f800000);
|
|
+ nv_wo32(dev, ctx, 0x3868/4, 0xbf800000);
|
|
+ nv_wo32(dev, ctx, 0x3870/4, 0xbf800000);
|
|
+}
|
|
+
|
|
+int
|
|
+nv20_graph_create_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ void (*ctx_init)(struct drm_device *, struct nouveau_gpuobj *);
|
|
+ unsigned int ctx_size;
|
|
+ unsigned int idoffs = 0x28/4;
|
|
+ int ret;
|
|
+
|
|
+ switch (dev_priv->chipset) {
|
|
+ case 0x20:
|
|
+ ctx_size = NV20_GRCTX_SIZE;
|
|
+ ctx_init = nv20_graph_context_init;
|
|
+ idoffs = 0;
|
|
+ break;
|
|
+ case 0x25:
|
|
+ case 0x28:
|
|
+ ctx_size = NV25_GRCTX_SIZE;
|
|
+ ctx_init = nv25_graph_context_init;
|
|
+ break;
|
|
+ case 0x2a:
|
|
+ ctx_size = NV2A_GRCTX_SIZE;
|
|
+ ctx_init = nv2a_graph_context_init;
|
|
+ idoffs = 0;
|
|
+ break;
|
|
+ case 0x30:
|
|
+ case 0x31:
|
|
+ ctx_size = NV30_31_GRCTX_SIZE;
|
|
+ ctx_init = nv30_31_graph_context_init;
|
|
+ break;
|
|
+ case 0x34:
|
|
+ ctx_size = NV34_GRCTX_SIZE;
|
|
+ ctx_init = nv34_graph_context_init;
|
|
+ break;
|
|
+ case 0x35:
|
|
+ case 0x36:
|
|
+ ctx_size = NV35_36_GRCTX_SIZE;
|
|
+ ctx_init = nv35_36_graph_context_init;
|
|
+ break;
|
|
+ default:
|
|
+ ctx_size = 0;
|
|
+ ctx_init = nv35_36_graph_context_init;
|
|
+ NV_ERROR(dev, "Please contact the devs if you want your NV%x"
|
|
+ " card to work\n", dev_priv->chipset);
|
|
+ return -ENOSYS;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, ctx_size, 16,
|
|
+ NVOBJ_FLAG_ZERO_ALLOC,
|
|
+ &chan->ramin_grctx);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* Initialise default context values */
|
|
+ dev_priv->engine.instmem.prepare_access(dev, true);
|
|
+ ctx_init(dev, chan->ramin_grctx->gpuobj);
|
|
+
|
|
+ /* nv20: nv_wo32(dev, chan->ramin_grctx->gpuobj, 10, chan->id<<24); */
|
|
+ nv_wo32(dev, chan->ramin_grctx->gpuobj, idoffs,
|
|
+ (chan->id << 24) | 0x1); /* CTX_USER */
|
|
+
|
|
+ nv_wo32(dev, dev_priv->ctx_table->gpuobj, chan->id,
|
|
+ chan->ramin_grctx->instance >> 4);
|
|
+
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv20_graph_destroy_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ if (chan->ramin_grctx)
|
|
+ nouveau_gpuobj_ref_del(dev, &chan->ramin_grctx);
|
|
+
|
|
+ dev_priv->engine.instmem.prepare_access(dev, true);
|
|
+ nv_wo32(dev, dev_priv->ctx_table->gpuobj, chan->id, 0);
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+}
|
|
+
|
|
+int
|
|
+nv20_graph_load_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ uint32_t inst;
|
|
+
|
|
+ if (!chan->ramin_grctx)
|
|
+ return -EINVAL;
|
|
+ inst = chan->ramin_grctx->instance >> 4;
|
|
+
|
|
+ nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst);
|
|
+ nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_XFER,
|
|
+ NV20_PGRAPH_CHANNEL_CTX_XFER_LOAD);
|
|
+ nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10010100);
|
|
+
|
|
+ nouveau_wait_for_idle(dev);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nv20_graph_unload_context(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
|
|
+ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
|
|
+ struct nouveau_channel *chan;
|
|
+ uint32_t inst, tmp;
|
|
+
|
|
+ chan = pgraph->channel(dev);
|
|
+ if (!chan)
|
|
+ return 0;
|
|
+ inst = chan->ramin_grctx->instance >> 4;
|
|
+
|
|
+ nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst);
|
|
+ nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_XFER,
|
|
+ NV20_PGRAPH_CHANNEL_CTX_XFER_SAVE);
|
|
+
|
|
+ nouveau_wait_for_idle(dev);
|
|
+
|
|
+ nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000000);
|
|
+ tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff;
|
|
+ tmp |= (pfifo->channels - 1) << 24;
|
|
+ nv_wr32(dev, NV10_PGRAPH_CTX_USER, tmp);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void
|
|
+nv20_graph_rdi(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ int i, writecount = 32;
|
|
+ uint32_t rdi_index = 0x2c80000;
|
|
+
|
|
+ if (dev_priv->chipset == 0x20) {
|
|
+ rdi_index = 0x3d0000;
|
|
+ writecount = 15;
|
|
+ }
|
|
+
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, rdi_index);
|
|
+ for (i = 0; i < writecount; i++)
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_DATA, 0);
|
|
+
|
|
+ nouveau_wait_for_idle(dev);
|
|
+}
|
|
+
|
|
+void
|
|
+nv20_graph_set_region_tiling(struct drm_device *dev, int i, uint32_t addr,
|
|
+ uint32_t size, uint32_t pitch)
|
|
+{
|
|
+ uint32_t limit = max(1u, addr + size) - 1;
|
|
+
|
|
+ if (pitch)
|
|
+ addr |= 1;
|
|
+
|
|
+ nv_wr32(dev, NV20_PGRAPH_TLIMIT(i), limit);
|
|
+ nv_wr32(dev, NV20_PGRAPH_TSIZE(i), pitch);
|
|
+ nv_wr32(dev, NV20_PGRAPH_TILE(i), addr);
|
|
+
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0030 + 4 * i);
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_DATA, limit);
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0050 + 4 * i);
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_DATA, pitch);
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0010 + 4 * i);
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_DATA, addr);
|
|
+}
|
|
+
|
|
+int
|
|
+nv20_graph_init(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv =
|
|
+ (struct drm_nouveau_private *)dev->dev_private;
|
|
+ uint32_t tmp, vramsz;
|
|
+ int ret, i;
|
|
+
|
|
+ nv_wr32(dev, NV03_PMC_ENABLE,
|
|
+ nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PGRAPH);
|
|
+ nv_wr32(dev, NV03_PMC_ENABLE,
|
|
+ nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PGRAPH);
|
|
+
|
|
+ if (!dev_priv->ctx_table) {
|
|
+ /* Create Context Pointer Table */
|
|
+ dev_priv->ctx_table_size = 32 * 4;
|
|
+ ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0,
|
|
+ dev_priv->ctx_table_size, 16,
|
|
+ NVOBJ_FLAG_ZERO_ALLOC,
|
|
+ &dev_priv->ctx_table);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE,
|
|
+ dev_priv->ctx_table->instance >> 4);
|
|
+
|
|
+ nv20_graph_rdi(dev);
|
|
+
|
|
+ nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF);
|
|
+ nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF);
|
|
+
|
|
+ nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF);
|
|
+ nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x00000000);
|
|
+ nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x00118700);
|
|
+ nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0xF3CE0475); /* 0x4 = auto ctx switch */
|
|
+ nv_wr32(dev, NV10_PGRAPH_DEBUG_4, 0x00000000);
|
|
+ nv_wr32(dev, 0x40009C , 0x00000040);
|
|
+
|
|
+ if (dev_priv->chipset >= 0x25) {
|
|
+ nv_wr32(dev, 0x400890, 0x00080000);
|
|
+ nv_wr32(dev, 0x400610, 0x304B1FB6);
|
|
+ nv_wr32(dev, 0x400B80, 0x18B82880);
|
|
+ nv_wr32(dev, 0x400B84, 0x44000000);
|
|
+ nv_wr32(dev, 0x400098, 0x40000080);
|
|
+ nv_wr32(dev, 0x400B88, 0x000000ff);
|
|
+ } else {
|
|
+ nv_wr32(dev, 0x400880, 0x00080000); /* 0x0008c7df */
|
|
+ nv_wr32(dev, 0x400094, 0x00000005);
|
|
+ nv_wr32(dev, 0x400B80, 0x45CAA208); /* 0x45eae20e */
|
|
+ nv_wr32(dev, 0x400B84, 0x24000000);
|
|
+ nv_wr32(dev, 0x400098, 0x00000040);
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00E00038);
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00000030);
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00E10038);
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00000030);
|
|
+ }
|
|
+
|
|
+ /* Turn all the tiling regions off. */
|
|
+ for (i = 0; i < NV10_PFB_TILE__SIZE; i++)
|
|
+ nv20_graph_set_region_tiling(dev, i, 0, 0, 0);
|
|
+
|
|
+ for (i = 0; i < 8; i++) {
|
|
+ nv_wr32(dev, 0x400980 + i * 4, nv_rd32(dev, 0x100300 + i * 4));
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0090 + i * 4);
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_DATA,
|
|
+ nv_rd32(dev, 0x100300 + i * 4));
|
|
+ }
|
|
+ nv_wr32(dev, 0x4009a0, nv_rd32(dev, 0x100324));
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA000C);
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_DATA, nv_rd32(dev, 0x100324));
|
|
+
|
|
+ nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000100);
|
|
+ nv_wr32(dev, NV10_PGRAPH_STATE , 0xFFFFFFFF);
|
|
+
|
|
+ tmp = nv_rd32(dev, NV10_PGRAPH_SURFACE) & 0x0007ff00;
|
|
+ nv_wr32(dev, NV10_PGRAPH_SURFACE, tmp);
|
|
+ tmp = nv_rd32(dev, NV10_PGRAPH_SURFACE) | 0x00020100;
|
|
+ nv_wr32(dev, NV10_PGRAPH_SURFACE, tmp);
|
|
+
|
|
+ /* begin RAM config */
|
|
+ vramsz = drm_get_resource_len(dev, 0) - 1;
|
|
+ nv_wr32(dev, 0x4009A4, nv_rd32(dev, NV04_PFB_CFG0));
|
|
+ nv_wr32(dev, 0x4009A8, nv_rd32(dev, NV04_PFB_CFG1));
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0000);
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_DATA , nv_rd32(dev, NV04_PFB_CFG0));
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0004);
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_DATA , nv_rd32(dev, NV04_PFB_CFG1));
|
|
+ nv_wr32(dev, 0x400820, 0);
|
|
+ nv_wr32(dev, 0x400824, 0);
|
|
+ nv_wr32(dev, 0x400864, vramsz - 1);
|
|
+ nv_wr32(dev, 0x400868, vramsz - 1);
|
|
+
|
|
+ /* interesting.. the below overwrites some of the tile setup above.. */
|
|
+ nv_wr32(dev, 0x400B20, 0x00000000);
|
|
+ nv_wr32(dev, 0x400B04, 0xFFFFFFFF);
|
|
+
|
|
+ nv_wr32(dev, NV03_PGRAPH_ABS_UCLIP_XMIN, 0);
|
|
+ nv_wr32(dev, NV03_PGRAPH_ABS_UCLIP_YMIN, 0);
|
|
+ nv_wr32(dev, NV03_PGRAPH_ABS_UCLIP_XMAX, 0x7fff);
|
|
+ nv_wr32(dev, NV03_PGRAPH_ABS_UCLIP_YMAX, 0x7fff);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv20_graph_takedown(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ nouveau_gpuobj_ref_del(dev, &dev_priv->ctx_table);
|
|
+}
|
|
+
|
|
+int
|
|
+nv30_graph_init(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ int ret, i;
|
|
+
|
|
+ nv_wr32(dev, NV03_PMC_ENABLE,
|
|
+ nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PGRAPH);
|
|
+ nv_wr32(dev, NV03_PMC_ENABLE,
|
|
+ nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PGRAPH);
|
|
+
|
|
+ if (!dev_priv->ctx_table) {
|
|
+ /* Create Context Pointer Table */
|
|
+ dev_priv->ctx_table_size = 32 * 4;
|
|
+ ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0,
|
|
+ dev_priv->ctx_table_size, 16,
|
|
+ NVOBJ_FLAG_ZERO_ALLOC,
|
|
+ &dev_priv->ctx_table);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE,
|
|
+ dev_priv->ctx_table->instance >> 4);
|
|
+
|
|
+ nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF);
|
|
+ nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF);
|
|
+
|
|
+ nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF);
|
|
+ nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x00000000);
|
|
+ nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x401287c0);
|
|
+ nv_wr32(dev, 0x400890, 0x01b463ff);
|
|
+ nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0xf2de0475);
|
|
+ nv_wr32(dev, NV10_PGRAPH_DEBUG_4, 0x00008000);
|
|
+ nv_wr32(dev, NV04_PGRAPH_LIMIT_VIOL_PIX, 0xf04bdff6);
|
|
+ nv_wr32(dev, 0x400B80, 0x1003d888);
|
|
+ nv_wr32(dev, 0x400B84, 0x0c000000);
|
|
+ nv_wr32(dev, 0x400098, 0x00000000);
|
|
+ nv_wr32(dev, 0x40009C, 0x0005ad00);
|
|
+ nv_wr32(dev, 0x400B88, 0x62ff00ff); /* suspiciously like PGRAPH_DEBUG_2 */
|
|
+ nv_wr32(dev, 0x4000a0, 0x00000000);
|
|
+ nv_wr32(dev, 0x4000a4, 0x00000008);
|
|
+ nv_wr32(dev, 0x4008a8, 0xb784a400);
|
|
+ nv_wr32(dev, 0x400ba0, 0x002f8685);
|
|
+ nv_wr32(dev, 0x400ba4, 0x00231f3f);
|
|
+ nv_wr32(dev, 0x4008a4, 0x40000020);
|
|
+
|
|
+ if (dev_priv->chipset == 0x34) {
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0004);
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00200201);
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0008);
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00000008);
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0000);
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00000032);
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00E00004);
|
|
+ nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00000002);
|
|
+ }
|
|
+
|
|
+ nv_wr32(dev, 0x4000c0, 0x00000016);
|
|
+
|
|
+ /* Turn all the tiling regions off. */
|
|
+ for (i = 0; i < NV10_PFB_TILE__SIZE; i++)
|
|
+ nv20_graph_set_region_tiling(dev, i, 0, 0, 0);
|
|
+
|
|
+ nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000100);
|
|
+ nv_wr32(dev, NV10_PGRAPH_STATE , 0xFFFFFFFF);
|
|
+ nv_wr32(dev, 0x0040075c , 0x00000001);
|
|
+
|
|
+ /* begin RAM config */
|
|
+ /* vramsz = drm_get_resource_len(dev, 0) - 1; */
|
|
+ nv_wr32(dev, 0x4009A4, nv_rd32(dev, NV04_PFB_CFG0));
|
|
+ nv_wr32(dev, 0x4009A8, nv_rd32(dev, NV04_PFB_CFG1));
|
|
+ if (dev_priv->chipset != 0x34) {
|
|
+ nv_wr32(dev, 0x400750, 0x00EA0000);
|
|
+ nv_wr32(dev, 0x400754, nv_rd32(dev, NV04_PFB_CFG0));
|
|
+ nv_wr32(dev, 0x400750, 0x00EA0004);
|
|
+ nv_wr32(dev, 0x400754, nv_rd32(dev, NV04_PFB_CFG1));
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+struct nouveau_pgraph_object_class nv20_graph_grclass[] = {
|
|
+ { 0x0030, false, NULL }, /* null */
|
|
+ { 0x0039, false, NULL }, /* m2mf */
|
|
+ { 0x004a, false, NULL }, /* gdirect */
|
|
+ { 0x009f, false, NULL }, /* imageblit (nv12) */
|
|
+ { 0x008a, false, NULL }, /* ifc */
|
|
+ { 0x0089, false, NULL }, /* sifm */
|
|
+ { 0x0062, false, NULL }, /* surf2d */
|
|
+ { 0x0043, false, NULL }, /* rop */
|
|
+ { 0x0012, false, NULL }, /* beta1 */
|
|
+ { 0x0072, false, NULL }, /* beta4 */
|
|
+ { 0x0019, false, NULL }, /* cliprect */
|
|
+ { 0x0044, false, NULL }, /* pattern */
|
|
+ { 0x009e, false, NULL }, /* swzsurf */
|
|
+ { 0x0096, false, NULL }, /* celcius */
|
|
+ { 0x0097, false, NULL }, /* kelvin (nv20) */
|
|
+ { 0x0597, false, NULL }, /* kelvin (nv25) */
|
|
+ {}
|
|
+};
|
|
+
|
|
+struct nouveau_pgraph_object_class nv30_graph_grclass[] = {
|
|
+ { 0x0030, false, NULL }, /* null */
|
|
+ { 0x0039, false, NULL }, /* m2mf */
|
|
+ { 0x004a, false, NULL }, /* gdirect */
|
|
+ { 0x009f, false, NULL }, /* imageblit (nv12) */
|
|
+ { 0x008a, false, NULL }, /* ifc */
|
|
+ { 0x038a, false, NULL }, /* ifc (nv30) */
|
|
+ { 0x0089, false, NULL }, /* sifm */
|
|
+ { 0x0389, false, NULL }, /* sifm (nv30) */
|
|
+ { 0x0062, false, NULL }, /* surf2d */
|
|
+ { 0x0362, false, NULL }, /* surf2d (nv30) */
|
|
+ { 0x0043, false, NULL }, /* rop */
|
|
+ { 0x0012, false, NULL }, /* beta1 */
|
|
+ { 0x0072, false, NULL }, /* beta4 */
|
|
+ { 0x0019, false, NULL }, /* cliprect */
|
|
+ { 0x0044, false, NULL }, /* pattern */
|
|
+ { 0x039e, false, NULL }, /* swzsurf */
|
|
+ { 0x0397, false, NULL }, /* rankine (nv30) */
|
|
+ { 0x0497, false, NULL }, /* rankine (nv35) */
|
|
+ { 0x0697, false, NULL }, /* rankine (nv34) */
|
|
+ {}
|
|
+};
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nv40_fb.c b/drivers/gpu/drm/nouveau/nv40_fb.c
|
|
new file mode 100644
|
|
index 0000000..3cd07d8
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv40_fb.c
|
|
@@ -0,0 +1,75 @@
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_drm.h"
|
|
+
|
|
+void
|
|
+nv40_fb_set_region_tiling(struct drm_device *dev, int i, uint32_t addr,
|
|
+ uint32_t size, uint32_t pitch)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t limit = max(1u, addr + size) - 1;
|
|
+
|
|
+ if (pitch)
|
|
+ addr |= 1;
|
|
+
|
|
+ switch (dev_priv->chipset) {
|
|
+ case 0x40:
|
|
+ nv_wr32(dev, NV10_PFB_TLIMIT(i), limit);
|
|
+ nv_wr32(dev, NV10_PFB_TSIZE(i), pitch);
|
|
+ nv_wr32(dev, NV10_PFB_TILE(i), addr);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ nv_wr32(dev, NV40_PFB_TLIMIT(i), limit);
|
|
+ nv_wr32(dev, NV40_PFB_TSIZE(i), pitch);
|
|
+ nv_wr32(dev, NV40_PFB_TILE(i), addr);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+int
|
|
+nv40_fb_init(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
|
|
+ uint32_t tmp;
|
|
+ int i;
|
|
+
|
|
+ /* This is strictly a NV4x register (don't know about NV5x). */
|
|
+ /* The blob sets these to all kinds of values, and they mess up our setup. */
|
|
+ /* I got value 0x52802 instead. For some cards the blob even sets it back to 0x1. */
|
|
+ /* Note: the blob doesn't read this value, so i'm pretty sure this is safe for all cards. */
|
|
+ /* Any idea what this is? */
|
|
+ nv_wr32(dev, NV40_PFB_UNK_800, 0x1);
|
|
+
|
|
+ switch (dev_priv->chipset) {
|
|
+ case 0x40:
|
|
+ case 0x45:
|
|
+ tmp = nv_rd32(dev, NV10_PFB_CLOSE_PAGE2);
|
|
+ nv_wr32(dev, NV10_PFB_CLOSE_PAGE2, tmp & ~(1 << 15));
|
|
+ pfb->num_tiles = NV10_PFB_TILE__SIZE;
|
|
+ break;
|
|
+ case 0x46: /* G72 */
|
|
+ case 0x47: /* G70 */
|
|
+ case 0x49: /* G71 */
|
|
+ case 0x4b: /* G73 */
|
|
+ case 0x4c: /* C51 (G7X version) */
|
|
+ pfb->num_tiles = NV40_PFB_TILE__SIZE_1;
|
|
+ break;
|
|
+ default:
|
|
+ pfb->num_tiles = NV40_PFB_TILE__SIZE_0;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* Turn all the tiling regions off. */
|
|
+ for (i = 0; i < pfb->num_tiles; i++)
|
|
+ pfb->set_region_tiling(dev, i, 0, 0, 0);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv40_fb_takedown(struct drm_device *dev)
|
|
+{
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nv40_fifo.c b/drivers/gpu/drm/nouveau/nv40_fifo.c
|
|
new file mode 100644
|
|
index 0000000..b4f19cc
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv40_fifo.c
|
|
@@ -0,0 +1,314 @@
|
|
+/*
|
|
+ * Copyright (C) 2007 Ben Skeggs.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_drm.h"
|
|
+
|
|
+#define NV40_RAMFC(c) (dev_priv->ramfc_offset + ((c) * NV40_RAMFC__SIZE))
|
|
+#define NV40_RAMFC__SIZE 128
|
|
+
|
|
+int
|
|
+nv40_fifo_create_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t fc = NV40_RAMFC(chan->id);
|
|
+ int ret;
|
|
+
|
|
+ ret = nouveau_gpuobj_new_fake(dev, NV40_RAMFC(chan->id), ~0,
|
|
+ NV40_RAMFC__SIZE, NVOBJ_FLAG_ZERO_ALLOC |
|
|
+ NVOBJ_FLAG_ZERO_FREE, NULL, &chan->ramfc);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ dev_priv->engine.instmem.prepare_access(dev, true);
|
|
+ nv_wi32(dev, fc + 0, chan->pushbuf_base);
|
|
+ nv_wi32(dev, fc + 4, chan->pushbuf_base);
|
|
+ nv_wi32(dev, fc + 12, chan->pushbuf->instance >> 4);
|
|
+ nv_wi32(dev, fc + 24, NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES |
|
|
+ NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES |
|
|
+ NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8 |
|
|
+#ifdef __BIG_ENDIAN
|
|
+ NV_PFIFO_CACHE1_BIG_ENDIAN |
|
|
+#endif
|
|
+ 0x30000000 /* no idea.. */);
|
|
+ nv_wi32(dev, fc + 56, chan->ramin_grctx->instance >> 4);
|
|
+ nv_wi32(dev, fc + 60, 0x0001FFFF);
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+
|
|
+ /* enable the fifo dma operation */
|
|
+ nv_wr32(dev, NV04_PFIFO_MODE,
|
|
+ nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id));
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv40_fifo_destroy_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+
|
|
+ nv_wr32(dev, NV04_PFIFO_MODE,
|
|
+ nv_rd32(dev, NV04_PFIFO_MODE) & ~(1 << chan->id));
|
|
+
|
|
+ if (chan->ramfc)
|
|
+ nouveau_gpuobj_ref_del(dev, &chan->ramfc);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv40_fifo_do_load_context(struct drm_device *dev, int chid)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t fc = NV40_RAMFC(chid), tmp, tmp2;
|
|
+
|
|
+ dev_priv->engine.instmem.prepare_access(dev, false);
|
|
+
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUT, nv_ri32(dev, fc + 0));
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_GET, nv_ri32(dev, fc + 4));
|
|
+ nv_wr32(dev, NV10_PFIFO_CACHE1_REF_CNT, nv_ri32(dev, fc + 8));
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE, nv_ri32(dev, fc + 12));
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT, nv_ri32(dev, fc + 16));
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_STATE, nv_ri32(dev, fc + 20));
|
|
+
|
|
+ /* No idea what 0x2058 is.. */
|
|
+ tmp = nv_ri32(dev, fc + 24);
|
|
+ tmp2 = nv_rd32(dev, 0x2058) & 0xFFF;
|
|
+ tmp2 |= (tmp & 0x30000000);
|
|
+ nv_wr32(dev, 0x2058, tmp2);
|
|
+ tmp &= ~0x30000000;
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_FETCH, tmp);
|
|
+
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_ENGINE, nv_ri32(dev, fc + 28));
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_PULL1, nv_ri32(dev, fc + 32));
|
|
+ nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_VALUE, nv_ri32(dev, fc + 36));
|
|
+ tmp = nv_ri32(dev, fc + 40);
|
|
+ nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP, tmp);
|
|
+ nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT, nv_ri32(dev, fc + 44));
|
|
+ nv_wr32(dev, NV10_PFIFO_CACHE1_SEMAPHORE, nv_ri32(dev, fc + 48));
|
|
+ nv_wr32(dev, NV10_PFIFO_CACHE1_DMA_SUBROUTINE, nv_ri32(dev, fc + 52));
|
|
+ nv_wr32(dev, NV40_PFIFO_GRCTX_INSTANCE, nv_ri32(dev, fc + 56));
|
|
+
|
|
+ /* Don't clobber the TIMEOUT_ENABLED flag when restoring from RAMFC */
|
|
+ tmp = nv_rd32(dev, NV04_PFIFO_DMA_TIMESLICE) & ~0x1FFFF;
|
|
+ tmp |= nv_ri32(dev, fc + 60) & 0x1FFFF;
|
|
+ nv_wr32(dev, NV04_PFIFO_DMA_TIMESLICE, tmp);
|
|
+
|
|
+ nv_wr32(dev, 0x32e4, nv_ri32(dev, fc + 64));
|
|
+ /* NVIDIA does this next line twice... */
|
|
+ nv_wr32(dev, 0x32e8, nv_ri32(dev, fc + 68));
|
|
+ nv_wr32(dev, 0x2088, nv_ri32(dev, fc + 76));
|
|
+ nv_wr32(dev, 0x3300, nv_ri32(dev, fc + 80));
|
|
+
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0);
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0);
|
|
+}
|
|
+
|
|
+int
|
|
+nv40_fifo_load_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ uint32_t tmp;
|
|
+
|
|
+ nv40_fifo_do_load_context(dev, chan->id);
|
|
+
|
|
+ /* Set channel active, and in DMA mode */
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1,
|
|
+ NV40_PFIFO_CACHE1_PUSH1_DMA | chan->id);
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 1);
|
|
+
|
|
+ /* Reset DMA_CTL_AT_INFO to INVALID */
|
|
+ tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_CTL) & ~(1 << 31);
|
|
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_CTL, tmp);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nv40_fifo_unload_context(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
|
|
+ uint32_t fc, tmp;
|
|
+ int chid;
|
|
+
|
|
+ chid = pfifo->channel_id(dev);
|
|
+ if (chid < 0 || chid >= dev_priv->engine.fifo.channels)
|
|
+ return 0;
|
|
+ fc = NV40_RAMFC(chid);
|
|
+
|
|
+ dev_priv->engine.instmem.prepare_access(dev, true);
|
|
+ nv_wi32(dev, fc + 0, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT));
|
|
+ nv_wi32(dev, fc + 4, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET));
|
|
+ nv_wi32(dev, fc + 8, nv_rd32(dev, NV10_PFIFO_CACHE1_REF_CNT));
|
|
+ nv_wi32(dev, fc + 12, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE));
|
|
+ nv_wi32(dev, fc + 16, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT));
|
|
+ nv_wi32(dev, fc + 20, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_STATE));
|
|
+ tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_FETCH);
|
|
+ tmp |= nv_rd32(dev, 0x2058) & 0x30000000;
|
|
+ nv_wi32(dev, fc + 24, tmp);
|
|
+ nv_wi32(dev, fc + 28, nv_rd32(dev, NV04_PFIFO_CACHE1_ENGINE));
|
|
+ nv_wi32(dev, fc + 32, nv_rd32(dev, NV04_PFIFO_CACHE1_PULL1));
|
|
+ nv_wi32(dev, fc + 36, nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_VALUE));
|
|
+ tmp = nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP);
|
|
+ nv_wi32(dev, fc + 40, tmp);
|
|
+ nv_wi32(dev, fc + 44, nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT));
|
|
+ nv_wi32(dev, fc + 48, nv_rd32(dev, NV10_PFIFO_CACHE1_SEMAPHORE));
|
|
+ /* NVIDIA read 0x3228 first, then write DMA_GET here.. maybe something
|
|
+ * more involved depending on the value of 0x3228?
|
|
+ */
|
|
+ nv_wi32(dev, fc + 52, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET));
|
|
+ nv_wi32(dev, fc + 56, nv_rd32(dev, NV40_PFIFO_GRCTX_INSTANCE));
|
|
+ nv_wi32(dev, fc + 60, nv_rd32(dev, NV04_PFIFO_DMA_TIMESLICE) & 0x1ffff);
|
|
+ /* No idea what the below is for exactly, ripped from a mmio-trace */
|
|
+ nv_wi32(dev, fc + 64, nv_rd32(dev, NV40_PFIFO_UNK32E4));
|
|
+ /* NVIDIA do this next line twice.. bug? */
|
|
+ nv_wi32(dev, fc + 68, nv_rd32(dev, 0x32e8));
|
|
+ nv_wi32(dev, fc + 76, nv_rd32(dev, 0x2088));
|
|
+ nv_wi32(dev, fc + 80, nv_rd32(dev, 0x3300));
|
|
+#if 0 /* no real idea which is PUT/GET in UNK_48.. */
|
|
+ tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_GET);
|
|
+ tmp |= (nv_rd32(dev, NV04_PFIFO_CACHE1_PUT) << 16);
|
|
+ nv_wi32(dev, fc + 72, tmp);
|
|
+#endif
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+
|
|
+ nv40_fifo_do_load_context(dev, pfifo->channels - 1);
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1,
|
|
+ NV40_PFIFO_CACHE1_PUSH1_DMA | (pfifo->channels - 1));
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void
|
|
+nv40_fifo_init_reset(struct drm_device *dev)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ nv_wr32(dev, NV03_PMC_ENABLE,
|
|
+ nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PFIFO);
|
|
+ nv_wr32(dev, NV03_PMC_ENABLE,
|
|
+ nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PFIFO);
|
|
+
|
|
+ nv_wr32(dev, 0x003224, 0x000f0078);
|
|
+ nv_wr32(dev, 0x003210, 0x00000000);
|
|
+ nv_wr32(dev, 0x003270, 0x00000000);
|
|
+ nv_wr32(dev, 0x003240, 0x00000000);
|
|
+ nv_wr32(dev, 0x003244, 0x00000000);
|
|
+ nv_wr32(dev, 0x003258, 0x00000000);
|
|
+ nv_wr32(dev, 0x002504, 0x00000000);
|
|
+ for (i = 0; i < 16; i++)
|
|
+ nv_wr32(dev, 0x002510 + (i * 4), 0x00000000);
|
|
+ nv_wr32(dev, 0x00250c, 0x0000ffff);
|
|
+ nv_wr32(dev, 0x002048, 0x00000000);
|
|
+ nv_wr32(dev, 0x003228, 0x00000000);
|
|
+ nv_wr32(dev, 0x0032e8, 0x00000000);
|
|
+ nv_wr32(dev, 0x002410, 0x00000000);
|
|
+ nv_wr32(dev, 0x002420, 0x00000000);
|
|
+ nv_wr32(dev, 0x002058, 0x00000001);
|
|
+ nv_wr32(dev, 0x00221c, 0x00000000);
|
|
+ /* something with 0x2084, read/modify/write, no change */
|
|
+ nv_wr32(dev, 0x002040, 0x000000ff);
|
|
+ nv_wr32(dev, 0x002500, 0x00000000);
|
|
+ nv_wr32(dev, 0x003200, 0x00000000);
|
|
+
|
|
+ nv_wr32(dev, NV04_PFIFO_DMA_TIMESLICE, 0x2101ffff);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv40_fifo_init_ramxx(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ nv_wr32(dev, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ |
|
|
+ ((dev_priv->ramht_bits - 9) << 16) |
|
|
+ (dev_priv->ramht_offset >> 8));
|
|
+ nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro_offset>>8);
|
|
+
|
|
+ switch (dev_priv->chipset) {
|
|
+ case 0x47:
|
|
+ case 0x49:
|
|
+ case 0x4b:
|
|
+ nv_wr32(dev, 0x2230, 1);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ switch (dev_priv->chipset) {
|
|
+ case 0x40:
|
|
+ case 0x41:
|
|
+ case 0x42:
|
|
+ case 0x43:
|
|
+ case 0x45:
|
|
+ case 0x47:
|
|
+ case 0x48:
|
|
+ case 0x49:
|
|
+ case 0x4b:
|
|
+ nv_wr32(dev, NV40_PFIFO_RAMFC, 0x30002);
|
|
+ break;
|
|
+ default:
|
|
+ nv_wr32(dev, 0x2230, 0);
|
|
+ nv_wr32(dev, NV40_PFIFO_RAMFC,
|
|
+ ((nouveau_mem_fb_amount(dev) - 512 * 1024 +
|
|
+ dev_priv->ramfc_offset) >> 16) | (3 << 16));
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+nv40_fifo_init_intr(struct drm_device *dev)
|
|
+{
|
|
+ nv_wr32(dev, 0x002100, 0xffffffff);
|
|
+ nv_wr32(dev, 0x002140, 0xffffffff);
|
|
+}
|
|
+
|
|
+int
|
|
+nv40_fifo_init(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
|
|
+ int i;
|
|
+
|
|
+ nv40_fifo_init_reset(dev);
|
|
+ nv40_fifo_init_ramxx(dev);
|
|
+
|
|
+ nv40_fifo_do_load_context(dev, pfifo->channels - 1);
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1);
|
|
+
|
|
+ nv40_fifo_init_intr(dev);
|
|
+ pfifo->enable(dev);
|
|
+ pfifo->reassign(dev, true);
|
|
+
|
|
+ for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
|
|
+ if (dev_priv->fifos[i]) {
|
|
+ uint32_t mode = nv_rd32(dev, NV04_PFIFO_MODE);
|
|
+ nv_wr32(dev, NV04_PFIFO_MODE, mode | (1 << i));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nv40_graph.c b/drivers/gpu/drm/nouveau/nv40_graph.c
|
|
new file mode 100644
|
|
index 0000000..53e8afe
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv40_graph.c
|
|
@@ -0,0 +1,406 @@
|
|
+/*
|
|
+ * Copyright (C) 2007 Ben Skeggs.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_grctx.h"
|
|
+
|
|
+struct nouveau_channel *
|
|
+nv40_graph_channel(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t inst;
|
|
+ int i;
|
|
+
|
|
+ inst = nv_rd32(dev, NV40_PGRAPH_CTXCTL_CUR);
|
|
+ if (!(inst & NV40_PGRAPH_CTXCTL_CUR_LOADED))
|
|
+ return NULL;
|
|
+ inst = (inst & NV40_PGRAPH_CTXCTL_CUR_INSTANCE) << 4;
|
|
+
|
|
+ for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
|
|
+ struct nouveau_channel *chan = dev_priv->fifos[i];
|
|
+
|
|
+ if (chan && chan->ramin_grctx &&
|
|
+ chan->ramin_grctx->instance == inst)
|
|
+ return chan;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+int
|
|
+nv40_graph_create_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
|
|
+ int ret;
|
|
+
|
|
+ ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, pgraph->grctx_size,
|
|
+ 16, NVOBJ_FLAG_ZERO_ALLOC,
|
|
+ &chan->ramin_grctx);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* Initialise default context values */
|
|
+ dev_priv->engine.instmem.prepare_access(dev, true);
|
|
+ if (!pgraph->ctxprog) {
|
|
+ struct nouveau_grctx ctx = {};
|
|
+
|
|
+ ctx.dev = chan->dev;
|
|
+ ctx.mode = NOUVEAU_GRCTX_VALS;
|
|
+ ctx.data = chan->ramin_grctx->gpuobj;
|
|
+ nv40_grctx_init(&ctx);
|
|
+ } else {
|
|
+ nouveau_grctx_vals_load(dev, chan->ramin_grctx->gpuobj);
|
|
+ }
|
|
+ nv_wo32(dev, chan->ramin_grctx->gpuobj, 0,
|
|
+ chan->ramin_grctx->gpuobj->im_pramin->start);
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv40_graph_destroy_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ nouveau_gpuobj_ref_del(chan->dev, &chan->ramin_grctx);
|
|
+}
|
|
+
|
|
+static int
|
|
+nv40_graph_transfer_context(struct drm_device *dev, uint32_t inst, int save)
|
|
+{
|
|
+ uint32_t old_cp, tv = 1000, tmp;
|
|
+ int i;
|
|
+
|
|
+ old_cp = nv_rd32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER);
|
|
+ nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst);
|
|
+
|
|
+ tmp = nv_rd32(dev, NV40_PGRAPH_CTXCTL_0310);
|
|
+ tmp |= save ? NV40_PGRAPH_CTXCTL_0310_XFER_SAVE :
|
|
+ NV40_PGRAPH_CTXCTL_0310_XFER_LOAD;
|
|
+ nv_wr32(dev, NV40_PGRAPH_CTXCTL_0310, tmp);
|
|
+
|
|
+ tmp = nv_rd32(dev, NV40_PGRAPH_CTXCTL_0304);
|
|
+ tmp |= NV40_PGRAPH_CTXCTL_0304_XFER_CTX;
|
|
+ nv_wr32(dev, NV40_PGRAPH_CTXCTL_0304, tmp);
|
|
+
|
|
+ nouveau_wait_for_idle(dev);
|
|
+
|
|
+ for (i = 0; i < tv; i++) {
|
|
+ if (nv_rd32(dev, NV40_PGRAPH_CTXCTL_030C) == 0)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, old_cp);
|
|
+
|
|
+ if (i == tv) {
|
|
+ uint32_t ucstat = nv_rd32(dev, NV40_PGRAPH_CTXCTL_UCODE_STAT);
|
|
+ NV_ERROR(dev, "Failed: Instance=0x%08x Save=%d\n", inst, save);
|
|
+ NV_ERROR(dev, "IP: 0x%02x, Opcode: 0x%08x\n",
|
|
+ ucstat >> NV40_PGRAPH_CTXCTL_UCODE_STAT_IP_SHIFT,
|
|
+ ucstat & NV40_PGRAPH_CTXCTL_UCODE_STAT_OP_MASK);
|
|
+ NV_ERROR(dev, "0x40030C = 0x%08x\n",
|
|
+ nv_rd32(dev, NV40_PGRAPH_CTXCTL_030C));
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Restore the context for a specific channel into PGRAPH */
|
|
+int
|
|
+nv40_graph_load_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ uint32_t inst;
|
|
+ int ret;
|
|
+
|
|
+ if (!chan->ramin_grctx)
|
|
+ return -EINVAL;
|
|
+ inst = chan->ramin_grctx->instance >> 4;
|
|
+
|
|
+ ret = nv40_graph_transfer_context(dev, inst, 0);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* 0x40032C, no idea of it's exact function. Could simply be a
|
|
+ * record of the currently active PGRAPH context. It's currently
|
|
+ * unknown as to what bit 24 does. The nv ddx has it set, so we will
|
|
+ * set it here too.
|
|
+ */
|
|
+ nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst);
|
|
+ nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR,
|
|
+ (inst & NV40_PGRAPH_CTXCTL_CUR_INSTANCE) |
|
|
+ NV40_PGRAPH_CTXCTL_CUR_LOADED);
|
|
+ /* 0x32E0 records the instance address of the active FIFO's PGRAPH
|
|
+ * context. If at any time this doesn't match 0x40032C, you will
|
|
+ * recieve PGRAPH_INTR_CONTEXT_SWITCH
|
|
+ */
|
|
+ nv_wr32(dev, NV40_PFIFO_GRCTX_INSTANCE, inst);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nv40_graph_unload_context(struct drm_device *dev)
|
|
+{
|
|
+ uint32_t inst;
|
|
+ int ret;
|
|
+
|
|
+ inst = nv_rd32(dev, NV40_PGRAPH_CTXCTL_CUR);
|
|
+ if (!(inst & NV40_PGRAPH_CTXCTL_CUR_LOADED))
|
|
+ return 0;
|
|
+ inst &= NV40_PGRAPH_CTXCTL_CUR_INSTANCE;
|
|
+
|
|
+ ret = nv40_graph_transfer_context(dev, inst, 1);
|
|
+
|
|
+ nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, inst);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void
|
|
+nv40_graph_set_region_tiling(struct drm_device *dev, int i, uint32_t addr,
|
|
+ uint32_t size, uint32_t pitch)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t limit = max(1u, addr + size) - 1;
|
|
+
|
|
+ if (pitch)
|
|
+ addr |= 1;
|
|
+
|
|
+ switch (dev_priv->chipset) {
|
|
+ case 0x44:
|
|
+ case 0x4a:
|
|
+ case 0x4e:
|
|
+ nv_wr32(dev, NV20_PGRAPH_TSIZE(i), pitch);
|
|
+ nv_wr32(dev, NV20_PGRAPH_TLIMIT(i), limit);
|
|
+ nv_wr32(dev, NV20_PGRAPH_TILE(i), addr);
|
|
+ break;
|
|
+
|
|
+ case 0x46:
|
|
+ case 0x47:
|
|
+ case 0x49:
|
|
+ case 0x4b:
|
|
+ nv_wr32(dev, NV47_PGRAPH_TSIZE(i), pitch);
|
|
+ nv_wr32(dev, NV47_PGRAPH_TLIMIT(i), limit);
|
|
+ nv_wr32(dev, NV47_PGRAPH_TILE(i), addr);
|
|
+ nv_wr32(dev, NV40_PGRAPH_TSIZE1(i), pitch);
|
|
+ nv_wr32(dev, NV40_PGRAPH_TLIMIT1(i), limit);
|
|
+ nv_wr32(dev, NV40_PGRAPH_TILE1(i), addr);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ nv_wr32(dev, NV20_PGRAPH_TSIZE(i), pitch);
|
|
+ nv_wr32(dev, NV20_PGRAPH_TLIMIT(i), limit);
|
|
+ nv_wr32(dev, NV20_PGRAPH_TILE(i), addr);
|
|
+ nv_wr32(dev, NV40_PGRAPH_TSIZE1(i), pitch);
|
|
+ nv_wr32(dev, NV40_PGRAPH_TLIMIT1(i), limit);
|
|
+ nv_wr32(dev, NV40_PGRAPH_TILE1(i), addr);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * G70 0x47
|
|
+ * G71 0x49
|
|
+ * NV45 0x48
|
|
+ * G72[M] 0x46
|
|
+ * G73 0x4b
|
|
+ * C51_G7X 0x4c
|
|
+ * C51 0x4e
|
|
+ */
|
|
+int
|
|
+nv40_graph_init(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv =
|
|
+ (struct drm_nouveau_private *)dev->dev_private;
|
|
+ struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
|
|
+ uint32_t vramsz;
|
|
+ int i, j;
|
|
+
|
|
+ nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) &
|
|
+ ~NV_PMC_ENABLE_PGRAPH);
|
|
+ nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) |
|
|
+ NV_PMC_ENABLE_PGRAPH);
|
|
+
|
|
+ if (nouveau_ctxfw) {
|
|
+ nouveau_grctx_prog_load(dev);
|
|
+ dev_priv->engine.graph.grctx_size = 175 * 1024;
|
|
+ }
|
|
+
|
|
+ if (!dev_priv->engine.graph.ctxprog) {
|
|
+ struct nouveau_grctx ctx = {};
|
|
+ uint32_t cp[256];
|
|
+
|
|
+ ctx.dev = dev;
|
|
+ ctx.mode = NOUVEAU_GRCTX_PROG;
|
|
+ ctx.data = cp;
|
|
+ ctx.ctxprog_max = 256;
|
|
+ nv40_grctx_init(&ctx);
|
|
+ dev_priv->engine.graph.grctx_size = ctx.ctxvals_pos * 4;
|
|
+
|
|
+ nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_INDEX, 0);
|
|
+ for (i = 0; i < ctx.ctxprog_len; i++)
|
|
+ nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_DATA, cp[i]);
|
|
+ }
|
|
+
|
|
+ /* No context present currently */
|
|
+ nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, 0x00000000);
|
|
+
|
|
+ nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF);
|
|
+ nv_wr32(dev, NV40_PGRAPH_INTR_EN, 0xFFFFFFFF);
|
|
+
|
|
+ nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF);
|
|
+ nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x00000000);
|
|
+ nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x401287c0);
|
|
+ nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0xe0de8055);
|
|
+ nv_wr32(dev, NV10_PGRAPH_DEBUG_4, 0x00008000);
|
|
+ nv_wr32(dev, NV04_PGRAPH_LIMIT_VIOL_PIX, 0x00be3c5f);
|
|
+
|
|
+ nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10010100);
|
|
+ nv_wr32(dev, NV10_PGRAPH_STATE , 0xFFFFFFFF);
|
|
+
|
|
+ j = nv_rd32(dev, 0x1540) & 0xff;
|
|
+ if (j) {
|
|
+ for (i = 0; !(j & 1); j >>= 1, i++)
|
|
+ ;
|
|
+ nv_wr32(dev, 0x405000, i);
|
|
+ }
|
|
+
|
|
+ if (dev_priv->chipset == 0x40) {
|
|
+ nv_wr32(dev, 0x4009b0, 0x83280fff);
|
|
+ nv_wr32(dev, 0x4009b4, 0x000000a0);
|
|
+ } else {
|
|
+ nv_wr32(dev, 0x400820, 0x83280eff);
|
|
+ nv_wr32(dev, 0x400824, 0x000000a0);
|
|
+ }
|
|
+
|
|
+ switch (dev_priv->chipset) {
|
|
+ case 0x40:
|
|
+ case 0x45:
|
|
+ nv_wr32(dev, 0x4009b8, 0x0078e366);
|
|
+ nv_wr32(dev, 0x4009bc, 0x0000014c);
|
|
+ break;
|
|
+ case 0x41:
|
|
+ case 0x42: /* pciid also 0x00Cx */
|
|
+ /* case 0x0120: XXX (pciid) */
|
|
+ nv_wr32(dev, 0x400828, 0x007596ff);
|
|
+ nv_wr32(dev, 0x40082c, 0x00000108);
|
|
+ break;
|
|
+ case 0x43:
|
|
+ nv_wr32(dev, 0x400828, 0x0072cb77);
|
|
+ nv_wr32(dev, 0x40082c, 0x00000108);
|
|
+ break;
|
|
+ case 0x44:
|
|
+ case 0x46: /* G72 */
|
|
+ case 0x4a:
|
|
+ case 0x4c: /* G7x-based C51 */
|
|
+ case 0x4e:
|
|
+ nv_wr32(dev, 0x400860, 0);
|
|
+ nv_wr32(dev, 0x400864, 0);
|
|
+ break;
|
|
+ case 0x47: /* G70 */
|
|
+ case 0x49: /* G71 */
|
|
+ case 0x4b: /* G73 */
|
|
+ nv_wr32(dev, 0x400828, 0x07830610);
|
|
+ nv_wr32(dev, 0x40082c, 0x0000016A);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ nv_wr32(dev, 0x400b38, 0x2ffff800);
|
|
+ nv_wr32(dev, 0x400b3c, 0x00006000);
|
|
+
|
|
+ /* Turn all the tiling regions off. */
|
|
+ for (i = 0; i < pfb->num_tiles; i++)
|
|
+ nv40_graph_set_region_tiling(dev, i, 0, 0, 0);
|
|
+
|
|
+ /* begin RAM config */
|
|
+ vramsz = drm_get_resource_len(dev, 0) - 1;
|
|
+ switch (dev_priv->chipset) {
|
|
+ case 0x40:
|
|
+ nv_wr32(dev, 0x4009A4, nv_rd32(dev, NV04_PFB_CFG0));
|
|
+ nv_wr32(dev, 0x4009A8, nv_rd32(dev, NV04_PFB_CFG1));
|
|
+ nv_wr32(dev, 0x4069A4, nv_rd32(dev, NV04_PFB_CFG0));
|
|
+ nv_wr32(dev, 0x4069A8, nv_rd32(dev, NV04_PFB_CFG1));
|
|
+ nv_wr32(dev, 0x400820, 0);
|
|
+ nv_wr32(dev, 0x400824, 0);
|
|
+ nv_wr32(dev, 0x400864, vramsz);
|
|
+ nv_wr32(dev, 0x400868, vramsz);
|
|
+ break;
|
|
+ default:
|
|
+ switch (dev_priv->chipset) {
|
|
+ case 0x46:
|
|
+ case 0x47:
|
|
+ case 0x49:
|
|
+ case 0x4b:
|
|
+ nv_wr32(dev, 0x400DF0, nv_rd32(dev, NV04_PFB_CFG0));
|
|
+ nv_wr32(dev, 0x400DF4, nv_rd32(dev, NV04_PFB_CFG1));
|
|
+ break;
|
|
+ default:
|
|
+ nv_wr32(dev, 0x4009F0, nv_rd32(dev, NV04_PFB_CFG0));
|
|
+ nv_wr32(dev, 0x4009F4, nv_rd32(dev, NV04_PFB_CFG1));
|
|
+ break;
|
|
+ }
|
|
+ nv_wr32(dev, 0x4069F0, nv_rd32(dev, NV04_PFB_CFG0));
|
|
+ nv_wr32(dev, 0x4069F4, nv_rd32(dev, NV04_PFB_CFG1));
|
|
+ nv_wr32(dev, 0x400840, 0);
|
|
+ nv_wr32(dev, 0x400844, 0);
|
|
+ nv_wr32(dev, 0x4008A0, vramsz);
|
|
+ nv_wr32(dev, 0x4008A4, vramsz);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void nv40_graph_takedown(struct drm_device *dev)
|
|
+{
|
|
+ nouveau_grctx_fini(dev);
|
|
+}
|
|
+
|
|
+struct nouveau_pgraph_object_class nv40_graph_grclass[] = {
|
|
+ { 0x0030, false, NULL }, /* null */
|
|
+ { 0x0039, false, NULL }, /* m2mf */
|
|
+ { 0x004a, false, NULL }, /* gdirect */
|
|
+ { 0x009f, false, NULL }, /* imageblit (nv12) */
|
|
+ { 0x008a, false, NULL }, /* ifc */
|
|
+ { 0x0089, false, NULL }, /* sifm */
|
|
+ { 0x3089, false, NULL }, /* sifm (nv40) */
|
|
+ { 0x0062, false, NULL }, /* surf2d */
|
|
+ { 0x3062, false, NULL }, /* surf2d (nv40) */
|
|
+ { 0x0043, false, NULL }, /* rop */
|
|
+ { 0x0012, false, NULL }, /* beta1 */
|
|
+ { 0x0072, false, NULL }, /* beta4 */
|
|
+ { 0x0019, false, NULL }, /* cliprect */
|
|
+ { 0x0044, false, NULL }, /* pattern */
|
|
+ { 0x309e, false, NULL }, /* swzsurf */
|
|
+ { 0x4097, false, NULL }, /* curie (nv40) */
|
|
+ { 0x4497, false, NULL }, /* curie (nv44) */
|
|
+ {}
|
|
+};
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nv40_grctx.c b/drivers/gpu/drm/nouveau/nv40_grctx.c
|
|
new file mode 100644
|
|
index 0000000..11b11c3
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv40_grctx.c
|
|
@@ -0,0 +1,678 @@
|
|
+/*
|
|
+ * Copyright 2009 Red Hat Inc.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice shall be included in
|
|
+ * all copies or substantial portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
+ * OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ * Authors: Ben Skeggs
|
|
+ */
|
|
+
|
|
+/* NVIDIA context programs handle a number of other conditions which are
|
|
+ * not implemented in our versions. It's not clear why NVIDIA context
|
|
+ * programs have this code, nor whether it's strictly necessary for
|
|
+ * correct operation. We'll implement additional handling if/when we
|
|
+ * discover it's necessary.
|
|
+ *
|
|
+ * - On context save, NVIDIA set 0x400314 bit 0 to 1 if the "3D state"
|
|
+ * flag is set, this gets saved into the context.
|
|
+ * - On context save, the context program for all cards load nsource
|
|
+ * into a flag register and check for ILLEGAL_MTHD. If it's set,
|
|
+ * opcode 0x60000d is called before resuming normal operation.
|
|
+ * - Some context programs check more conditions than the above. NV44
|
|
+ * checks: ((nsource & 0x0857) || (0x400718 & 0x0100) || (intr & 0x0001))
|
|
+ * and calls 0x60000d before resuming normal operation.
|
|
+ * - At the very beginning of NVIDIA's context programs, flag 9 is checked
|
|
+ * and if true 0x800001 is called with count=0, pos=0, the flag is cleared
|
|
+ * and then the ctxprog is aborted. It looks like a complicated NOP,
|
|
+ * its purpose is unknown.
|
|
+ * - In the section of code that loads the per-vs state, NVIDIA check
|
|
+ * flag 10. If it's set, they only transfer the small 0x300 byte block
|
|
+ * of state + the state for a single vs as opposed to the state for
|
|
+ * all vs units. It doesn't seem likely that it'll occur in normal
|
|
+ * operation, especially seeing as it appears NVIDIA may have screwed
|
|
+ * up the ctxprogs for some cards and have an invalid instruction
|
|
+ * rather than a cp_lsr(ctx, dwords_for_1_vs_unit) instruction.
|
|
+ * - There's a number of places where context offset 0 (where we place
|
|
+ * the PRAMIN offset of the context) is loaded into either 0x408000,
|
|
+ * 0x408004 or 0x408008. Not sure what's up there either.
|
|
+ * - The ctxprogs for some cards save 0x400a00 again during the cleanup
|
|
+ * path for auto-loadctx.
|
|
+ */
|
|
+
|
|
+#define CP_FLAG_CLEAR 0
|
|
+#define CP_FLAG_SET 1
|
|
+#define CP_FLAG_SWAP_DIRECTION ((0 * 32) + 0)
|
|
+#define CP_FLAG_SWAP_DIRECTION_LOAD 0
|
|
+#define CP_FLAG_SWAP_DIRECTION_SAVE 1
|
|
+#define CP_FLAG_USER_SAVE ((0 * 32) + 5)
|
|
+#define CP_FLAG_USER_SAVE_NOT_PENDING 0
|
|
+#define CP_FLAG_USER_SAVE_PENDING 1
|
|
+#define CP_FLAG_USER_LOAD ((0 * 32) + 6)
|
|
+#define CP_FLAG_USER_LOAD_NOT_PENDING 0
|
|
+#define CP_FLAG_USER_LOAD_PENDING 1
|
|
+#define CP_FLAG_STATUS ((3 * 32) + 0)
|
|
+#define CP_FLAG_STATUS_IDLE 0
|
|
+#define CP_FLAG_STATUS_BUSY 1
|
|
+#define CP_FLAG_AUTO_SAVE ((3 * 32) + 4)
|
|
+#define CP_FLAG_AUTO_SAVE_NOT_PENDING 0
|
|
+#define CP_FLAG_AUTO_SAVE_PENDING 1
|
|
+#define CP_FLAG_AUTO_LOAD ((3 * 32) + 5)
|
|
+#define CP_FLAG_AUTO_LOAD_NOT_PENDING 0
|
|
+#define CP_FLAG_AUTO_LOAD_PENDING 1
|
|
+#define CP_FLAG_UNK54 ((3 * 32) + 6)
|
|
+#define CP_FLAG_UNK54_CLEAR 0
|
|
+#define CP_FLAG_UNK54_SET 1
|
|
+#define CP_FLAG_ALWAYS ((3 * 32) + 8)
|
|
+#define CP_FLAG_ALWAYS_FALSE 0
|
|
+#define CP_FLAG_ALWAYS_TRUE 1
|
|
+#define CP_FLAG_UNK57 ((3 * 32) + 9)
|
|
+#define CP_FLAG_UNK57_CLEAR 0
|
|
+#define CP_FLAG_UNK57_SET 1
|
|
+
|
|
+#define CP_CTX 0x00100000
|
|
+#define CP_CTX_COUNT 0x000fc000
|
|
+#define CP_CTX_COUNT_SHIFT 14
|
|
+#define CP_CTX_REG 0x00003fff
|
|
+#define CP_LOAD_SR 0x00200000
|
|
+#define CP_LOAD_SR_VALUE 0x000fffff
|
|
+#define CP_BRA 0x00400000
|
|
+#define CP_BRA_IP 0x0000ff00
|
|
+#define CP_BRA_IP_SHIFT 8
|
|
+#define CP_BRA_IF_CLEAR 0x00000080
|
|
+#define CP_BRA_FLAG 0x0000007f
|
|
+#define CP_WAIT 0x00500000
|
|
+#define CP_WAIT_SET 0x00000080
|
|
+#define CP_WAIT_FLAG 0x0000007f
|
|
+#define CP_SET 0x00700000
|
|
+#define CP_SET_1 0x00000080
|
|
+#define CP_SET_FLAG 0x0000007f
|
|
+#define CP_NEXT_TO_SWAP 0x00600007
|
|
+#define CP_NEXT_TO_CURRENT 0x00600009
|
|
+#define CP_SET_CONTEXT_POINTER 0x0060000a
|
|
+#define CP_END 0x0060000e
|
|
+#define CP_LOAD_MAGIC_UNK01 0x00800001 /* unknown */
|
|
+#define CP_LOAD_MAGIC_NV44TCL 0x00800029 /* per-vs state (0x4497) */
|
|
+#define CP_LOAD_MAGIC_NV40TCL 0x00800041 /* per-vs state (0x4097) */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_grctx.h"
|
|
+
|
|
+/* TODO:
|
|
+ * - get vs count from 0x1540
|
|
+ * - document unimplemented bits compared to nvidia
|
|
+ * - nsource handling
|
|
+ * - R0 & 0x0200 handling
|
|
+ * - single-vs handling
|
|
+ * - 400314 bit 0
|
|
+ */
|
|
+
|
|
+static int
|
|
+nv40_graph_4097(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ if ((dev_priv->chipset & 0xf0) == 0x60)
|
|
+ return 0;
|
|
+
|
|
+ return !!(0x0baf & (1 << dev_priv->chipset));
|
|
+}
|
|
+
|
|
+static int
|
|
+nv40_graph_vs_count(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ switch (dev_priv->chipset) {
|
|
+ case 0x47:
|
|
+ case 0x49:
|
|
+ case 0x4b:
|
|
+ return 8;
|
|
+ case 0x40:
|
|
+ return 6;
|
|
+ case 0x41:
|
|
+ case 0x42:
|
|
+ return 5;
|
|
+ case 0x43:
|
|
+ case 0x44:
|
|
+ case 0x46:
|
|
+ case 0x4a:
|
|
+ return 3;
|
|
+ case 0x4c:
|
|
+ case 0x4e:
|
|
+ case 0x67:
|
|
+ default:
|
|
+ return 1;
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+enum cp_label {
|
|
+ cp_check_load = 1,
|
|
+ cp_setup_auto_load,
|
|
+ cp_setup_load,
|
|
+ cp_setup_save,
|
|
+ cp_swap_state,
|
|
+ cp_swap_state3d_3_is_save,
|
|
+ cp_prepare_exit,
|
|
+ cp_exit,
|
|
+};
|
|
+
|
|
+static void
|
|
+nv40_graph_construct_general(struct nouveau_grctx *ctx)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
|
|
+ int i;
|
|
+
|
|
+ cp_ctx(ctx, 0x4000a4, 1);
|
|
+ gr_def(ctx, 0x4000a4, 0x00000008);
|
|
+ cp_ctx(ctx, 0x400144, 58);
|
|
+ gr_def(ctx, 0x400144, 0x00000001);
|
|
+ cp_ctx(ctx, 0x400314, 1);
|
|
+ gr_def(ctx, 0x400314, 0x00000000);
|
|
+ cp_ctx(ctx, 0x400400, 10);
|
|
+ cp_ctx(ctx, 0x400480, 10);
|
|
+ cp_ctx(ctx, 0x400500, 19);
|
|
+ gr_def(ctx, 0x400514, 0x00040000);
|
|
+ gr_def(ctx, 0x400524, 0x55555555);
|
|
+ gr_def(ctx, 0x400528, 0x55555555);
|
|
+ gr_def(ctx, 0x40052c, 0x55555555);
|
|
+ gr_def(ctx, 0x400530, 0x55555555);
|
|
+ cp_ctx(ctx, 0x400560, 6);
|
|
+ gr_def(ctx, 0x400568, 0x0000ffff);
|
|
+ gr_def(ctx, 0x40056c, 0x0000ffff);
|
|
+ cp_ctx(ctx, 0x40057c, 5);
|
|
+ cp_ctx(ctx, 0x400710, 3);
|
|
+ gr_def(ctx, 0x400710, 0x20010001);
|
|
+ gr_def(ctx, 0x400714, 0x0f73ef00);
|
|
+ cp_ctx(ctx, 0x400724, 1);
|
|
+ gr_def(ctx, 0x400724, 0x02008821);
|
|
+ cp_ctx(ctx, 0x400770, 3);
|
|
+ if (dev_priv->chipset == 0x40) {
|
|
+ cp_ctx(ctx, 0x400814, 4);
|
|
+ cp_ctx(ctx, 0x400828, 5);
|
|
+ cp_ctx(ctx, 0x400840, 5);
|
|
+ gr_def(ctx, 0x400850, 0x00000040);
|
|
+ cp_ctx(ctx, 0x400858, 4);
|
|
+ gr_def(ctx, 0x400858, 0x00000040);
|
|
+ gr_def(ctx, 0x40085c, 0x00000040);
|
|
+ gr_def(ctx, 0x400864, 0x80000000);
|
|
+ cp_ctx(ctx, 0x40086c, 9);
|
|
+ gr_def(ctx, 0x40086c, 0x80000000);
|
|
+ gr_def(ctx, 0x400870, 0x80000000);
|
|
+ gr_def(ctx, 0x400874, 0x80000000);
|
|
+ gr_def(ctx, 0x400878, 0x80000000);
|
|
+ gr_def(ctx, 0x400888, 0x00000040);
|
|
+ gr_def(ctx, 0x40088c, 0x80000000);
|
|
+ cp_ctx(ctx, 0x4009c0, 8);
|
|
+ gr_def(ctx, 0x4009cc, 0x80000000);
|
|
+ gr_def(ctx, 0x4009dc, 0x80000000);
|
|
+ } else {
|
|
+ cp_ctx(ctx, 0x400840, 20);
|
|
+ if (!nv40_graph_4097(ctx->dev)) {
|
|
+ for (i = 0; i < 8; i++)
|
|
+ gr_def(ctx, 0x400860 + (i * 4), 0x00000001);
|
|
+ }
|
|
+ gr_def(ctx, 0x400880, 0x00000040);
|
|
+ gr_def(ctx, 0x400884, 0x00000040);
|
|
+ gr_def(ctx, 0x400888, 0x00000040);
|
|
+ cp_ctx(ctx, 0x400894, 11);
|
|
+ gr_def(ctx, 0x400894, 0x00000040);
|
|
+ if (nv40_graph_4097(ctx->dev)) {
|
|
+ for (i = 0; i < 8; i++)
|
|
+ gr_def(ctx, 0x4008a0 + (i * 4), 0x80000000);
|
|
+ }
|
|
+ cp_ctx(ctx, 0x4008e0, 2);
|
|
+ cp_ctx(ctx, 0x4008f8, 2);
|
|
+ if (dev_priv->chipset == 0x4c ||
|
|
+ (dev_priv->chipset & 0xf0) == 0x60)
|
|
+ cp_ctx(ctx, 0x4009f8, 1);
|
|
+ }
|
|
+ cp_ctx(ctx, 0x400a00, 73);
|
|
+ gr_def(ctx, 0x400b0c, 0x0b0b0b0c);
|
|
+ cp_ctx(ctx, 0x401000, 4);
|
|
+ cp_ctx(ctx, 0x405004, 1);
|
|
+ switch (dev_priv->chipset) {
|
|
+ case 0x47:
|
|
+ case 0x49:
|
|
+ case 0x4b:
|
|
+ cp_ctx(ctx, 0x403448, 1);
|
|
+ gr_def(ctx, 0x403448, 0x00001010);
|
|
+ break;
|
|
+ default:
|
|
+ cp_ctx(ctx, 0x403440, 1);
|
|
+ switch (dev_priv->chipset) {
|
|
+ case 0x40:
|
|
+ gr_def(ctx, 0x403440, 0x00000010);
|
|
+ break;
|
|
+ case 0x44:
|
|
+ case 0x46:
|
|
+ case 0x4a:
|
|
+ gr_def(ctx, 0x403440, 0x00003010);
|
|
+ break;
|
|
+ case 0x41:
|
|
+ case 0x42:
|
|
+ case 0x43:
|
|
+ case 0x4c:
|
|
+ case 0x4e:
|
|
+ case 0x67:
|
|
+ default:
|
|
+ gr_def(ctx, 0x403440, 0x00001010);
|
|
+ break;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+nv40_graph_construct_state3d(struct nouveau_grctx *ctx)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
|
|
+ int i;
|
|
+
|
|
+ if (dev_priv->chipset == 0x40) {
|
|
+ cp_ctx(ctx, 0x401880, 51);
|
|
+ gr_def(ctx, 0x401940, 0x00000100);
|
|
+ } else
|
|
+ if (dev_priv->chipset == 0x46 || dev_priv->chipset == 0x47 ||
|
|
+ dev_priv->chipset == 0x49 || dev_priv->chipset == 0x4b) {
|
|
+ cp_ctx(ctx, 0x401880, 32);
|
|
+ for (i = 0; i < 16; i++)
|
|
+ gr_def(ctx, 0x401880 + (i * 4), 0x00000111);
|
|
+ if (dev_priv->chipset == 0x46)
|
|
+ cp_ctx(ctx, 0x401900, 16);
|
|
+ cp_ctx(ctx, 0x401940, 3);
|
|
+ }
|
|
+ cp_ctx(ctx, 0x40194c, 18);
|
|
+ gr_def(ctx, 0x401954, 0x00000111);
|
|
+ gr_def(ctx, 0x401958, 0x00080060);
|
|
+ gr_def(ctx, 0x401974, 0x00000080);
|
|
+ gr_def(ctx, 0x401978, 0xffff0000);
|
|
+ gr_def(ctx, 0x40197c, 0x00000001);
|
|
+ gr_def(ctx, 0x401990, 0x46400000);
|
|
+ if (dev_priv->chipset == 0x40) {
|
|
+ cp_ctx(ctx, 0x4019a0, 2);
|
|
+ cp_ctx(ctx, 0x4019ac, 5);
|
|
+ } else {
|
|
+ cp_ctx(ctx, 0x4019a0, 1);
|
|
+ cp_ctx(ctx, 0x4019b4, 3);
|
|
+ }
|
|
+ gr_def(ctx, 0x4019bc, 0xffff0000);
|
|
+ switch (dev_priv->chipset) {
|
|
+ case 0x46:
|
|
+ case 0x47:
|
|
+ case 0x49:
|
|
+ case 0x4b:
|
|
+ cp_ctx(ctx, 0x4019c0, 18);
|
|
+ for (i = 0; i < 16; i++)
|
|
+ gr_def(ctx, 0x4019c0 + (i * 4), 0x88888888);
|
|
+ break;
|
|
+ }
|
|
+ cp_ctx(ctx, 0x401a08, 8);
|
|
+ gr_def(ctx, 0x401a10, 0x0fff0000);
|
|
+ gr_def(ctx, 0x401a14, 0x0fff0000);
|
|
+ gr_def(ctx, 0x401a1c, 0x00011100);
|
|
+ cp_ctx(ctx, 0x401a2c, 4);
|
|
+ cp_ctx(ctx, 0x401a44, 26);
|
|
+ for (i = 0; i < 16; i++)
|
|
+ gr_def(ctx, 0x401a44 + (i * 4), 0x07ff0000);
|
|
+ gr_def(ctx, 0x401a8c, 0x4b7fffff);
|
|
+ if (dev_priv->chipset == 0x40) {
|
|
+ cp_ctx(ctx, 0x401ab8, 3);
|
|
+ } else {
|
|
+ cp_ctx(ctx, 0x401ab8, 1);
|
|
+ cp_ctx(ctx, 0x401ac0, 1);
|
|
+ }
|
|
+ cp_ctx(ctx, 0x401ad0, 8);
|
|
+ gr_def(ctx, 0x401ad0, 0x30201000);
|
|
+ gr_def(ctx, 0x401ad4, 0x70605040);
|
|
+ gr_def(ctx, 0x401ad8, 0xb8a89888);
|
|
+ gr_def(ctx, 0x401adc, 0xf8e8d8c8);
|
|
+ cp_ctx(ctx, 0x401b10, dev_priv->chipset == 0x40 ? 2 : 1);
|
|
+ gr_def(ctx, 0x401b10, 0x40100000);
|
|
+ cp_ctx(ctx, 0x401b18, dev_priv->chipset == 0x40 ? 6 : 5);
|
|
+ gr_def(ctx, 0x401b28, dev_priv->chipset == 0x40 ?
|
|
+ 0x00000004 : 0x00000000);
|
|
+ cp_ctx(ctx, 0x401b30, 25);
|
|
+ gr_def(ctx, 0x401b34, 0x0000ffff);
|
|
+ gr_def(ctx, 0x401b68, 0x435185d6);
|
|
+ gr_def(ctx, 0x401b6c, 0x2155b699);
|
|
+ gr_def(ctx, 0x401b70, 0xfedcba98);
|
|
+ gr_def(ctx, 0x401b74, 0x00000098);
|
|
+ gr_def(ctx, 0x401b84, 0xffffffff);
|
|
+ gr_def(ctx, 0x401b88, 0x00ff7000);
|
|
+ gr_def(ctx, 0x401b8c, 0x0000ffff);
|
|
+ if (dev_priv->chipset != 0x44 && dev_priv->chipset != 0x4a &&
|
|
+ dev_priv->chipset != 0x4e)
|
|
+ cp_ctx(ctx, 0x401b94, 1);
|
|
+ cp_ctx(ctx, 0x401b98, 8);
|
|
+ gr_def(ctx, 0x401b9c, 0x00ff0000);
|
|
+ cp_ctx(ctx, 0x401bc0, 9);
|
|
+ gr_def(ctx, 0x401be0, 0x00ffff00);
|
|
+ cp_ctx(ctx, 0x401c00, 192);
|
|
+ for (i = 0; i < 16; i++) { /* fragment texture units */
|
|
+ gr_def(ctx, 0x401c40 + (i * 4), 0x00018488);
|
|
+ gr_def(ctx, 0x401c80 + (i * 4), 0x00028202);
|
|
+ gr_def(ctx, 0x401d00 + (i * 4), 0x0000aae4);
|
|
+ gr_def(ctx, 0x401d40 + (i * 4), 0x01012000);
|
|
+ gr_def(ctx, 0x401d80 + (i * 4), 0x00080008);
|
|
+ gr_def(ctx, 0x401e00 + (i * 4), 0x00100008);
|
|
+ }
|
|
+ for (i = 0; i < 4; i++) { /* vertex texture units */
|
|
+ gr_def(ctx, 0x401e90 + (i * 4), 0x0001bc80);
|
|
+ gr_def(ctx, 0x401ea0 + (i * 4), 0x00000202);
|
|
+ gr_def(ctx, 0x401ec0 + (i * 4), 0x00000008);
|
|
+ gr_def(ctx, 0x401ee0 + (i * 4), 0x00080008);
|
|
+ }
|
|
+ cp_ctx(ctx, 0x400f5c, 3);
|
|
+ gr_def(ctx, 0x400f5c, 0x00000002);
|
|
+ cp_ctx(ctx, 0x400f84, 1);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv40_graph_construct_state3d_2(struct nouveau_grctx *ctx)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
|
|
+ int i;
|
|
+
|
|
+ cp_ctx(ctx, 0x402000, 1);
|
|
+ cp_ctx(ctx, 0x402404, dev_priv->chipset == 0x40 ? 1 : 2);
|
|
+ switch (dev_priv->chipset) {
|
|
+ case 0x40:
|
|
+ gr_def(ctx, 0x402404, 0x00000001);
|
|
+ break;
|
|
+ case 0x4c:
|
|
+ case 0x4e:
|
|
+ case 0x67:
|
|
+ gr_def(ctx, 0x402404, 0x00000020);
|
|
+ break;
|
|
+ case 0x46:
|
|
+ case 0x49:
|
|
+ case 0x4b:
|
|
+ gr_def(ctx, 0x402404, 0x00000421);
|
|
+ break;
|
|
+ default:
|
|
+ gr_def(ctx, 0x402404, 0x00000021);
|
|
+ }
|
|
+ if (dev_priv->chipset != 0x40)
|
|
+ gr_def(ctx, 0x402408, 0x030c30c3);
|
|
+ switch (dev_priv->chipset) {
|
|
+ case 0x44:
|
|
+ case 0x46:
|
|
+ case 0x4a:
|
|
+ case 0x4c:
|
|
+ case 0x4e:
|
|
+ case 0x67:
|
|
+ cp_ctx(ctx, 0x402440, 1);
|
|
+ gr_def(ctx, 0x402440, 0x00011001);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ cp_ctx(ctx, 0x402480, dev_priv->chipset == 0x40 ? 8 : 9);
|
|
+ gr_def(ctx, 0x402488, 0x3e020200);
|
|
+ gr_def(ctx, 0x40248c, 0x00ffffff);
|
|
+ switch (dev_priv->chipset) {
|
|
+ case 0x40:
|
|
+ gr_def(ctx, 0x402490, 0x60103f00);
|
|
+ break;
|
|
+ case 0x47:
|
|
+ gr_def(ctx, 0x402490, 0x40103f00);
|
|
+ break;
|
|
+ case 0x41:
|
|
+ case 0x42:
|
|
+ case 0x49:
|
|
+ case 0x4b:
|
|
+ gr_def(ctx, 0x402490, 0x20103f00);
|
|
+ break;
|
|
+ default:
|
|
+ gr_def(ctx, 0x402490, 0x0c103f00);
|
|
+ break;
|
|
+ }
|
|
+ gr_def(ctx, 0x40249c, dev_priv->chipset <= 0x43 ?
|
|
+ 0x00020000 : 0x00040000);
|
|
+ cp_ctx(ctx, 0x402500, 31);
|
|
+ gr_def(ctx, 0x402530, 0x00008100);
|
|
+ if (dev_priv->chipset == 0x40)
|
|
+ cp_ctx(ctx, 0x40257c, 6);
|
|
+ cp_ctx(ctx, 0x402594, 16);
|
|
+ cp_ctx(ctx, 0x402800, 17);
|
|
+ gr_def(ctx, 0x402800, 0x00000001);
|
|
+ switch (dev_priv->chipset) {
|
|
+ case 0x47:
|
|
+ case 0x49:
|
|
+ case 0x4b:
|
|
+ cp_ctx(ctx, 0x402864, 1);
|
|
+ gr_def(ctx, 0x402864, 0x00001001);
|
|
+ cp_ctx(ctx, 0x402870, 3);
|
|
+ gr_def(ctx, 0x402878, 0x00000003);
|
|
+ if (dev_priv->chipset != 0x47) { /* belong at end!! */
|
|
+ cp_ctx(ctx, 0x402900, 1);
|
|
+ cp_ctx(ctx, 0x402940, 1);
|
|
+ cp_ctx(ctx, 0x402980, 1);
|
|
+ cp_ctx(ctx, 0x4029c0, 1);
|
|
+ cp_ctx(ctx, 0x402a00, 1);
|
|
+ cp_ctx(ctx, 0x402a40, 1);
|
|
+ cp_ctx(ctx, 0x402a80, 1);
|
|
+ cp_ctx(ctx, 0x402ac0, 1);
|
|
+ }
|
|
+ break;
|
|
+ case 0x40:
|
|
+ cp_ctx(ctx, 0x402844, 1);
|
|
+ gr_def(ctx, 0x402844, 0x00000001);
|
|
+ cp_ctx(ctx, 0x402850, 1);
|
|
+ break;
|
|
+ default:
|
|
+ cp_ctx(ctx, 0x402844, 1);
|
|
+ gr_def(ctx, 0x402844, 0x00001001);
|
|
+ cp_ctx(ctx, 0x402850, 2);
|
|
+ gr_def(ctx, 0x402854, 0x00000003);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ cp_ctx(ctx, 0x402c00, 4);
|
|
+ gr_def(ctx, 0x402c00, dev_priv->chipset == 0x40 ?
|
|
+ 0x80800001 : 0x00888001);
|
|
+ switch (dev_priv->chipset) {
|
|
+ case 0x47:
|
|
+ case 0x49:
|
|
+ case 0x4b:
|
|
+ cp_ctx(ctx, 0x402c20, 40);
|
|
+ for (i = 0; i < 32; i++)
|
|
+ gr_def(ctx, 0x402c40 + (i * 4), 0xffffffff);
|
|
+ cp_ctx(ctx, 0x4030b8, 13);
|
|
+ gr_def(ctx, 0x4030dc, 0x00000005);
|
|
+ gr_def(ctx, 0x4030e8, 0x0000ffff);
|
|
+ break;
|
|
+ default:
|
|
+ cp_ctx(ctx, 0x402c10, 4);
|
|
+ if (dev_priv->chipset == 0x40)
|
|
+ cp_ctx(ctx, 0x402c20, 36);
|
|
+ else
|
|
+ if (dev_priv->chipset <= 0x42)
|
|
+ cp_ctx(ctx, 0x402c20, 24);
|
|
+ else
|
|
+ if (dev_priv->chipset <= 0x4a)
|
|
+ cp_ctx(ctx, 0x402c20, 16);
|
|
+ else
|
|
+ cp_ctx(ctx, 0x402c20, 8);
|
|
+ cp_ctx(ctx, 0x402cb0, dev_priv->chipset == 0x40 ? 12 : 13);
|
|
+ gr_def(ctx, 0x402cd4, 0x00000005);
|
|
+ if (dev_priv->chipset != 0x40)
|
|
+ gr_def(ctx, 0x402ce0, 0x0000ffff);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ cp_ctx(ctx, 0x403400, dev_priv->chipset == 0x40 ? 4 : 3);
|
|
+ cp_ctx(ctx, 0x403410, dev_priv->chipset == 0x40 ? 4 : 3);
|
|
+ cp_ctx(ctx, 0x403420, nv40_graph_vs_count(ctx->dev));
|
|
+ for (i = 0; i < nv40_graph_vs_count(ctx->dev); i++)
|
|
+ gr_def(ctx, 0x403420 + (i * 4), 0x00005555);
|
|
+
|
|
+ if (dev_priv->chipset != 0x40) {
|
|
+ cp_ctx(ctx, 0x403600, 1);
|
|
+ gr_def(ctx, 0x403600, 0x00000001);
|
|
+ }
|
|
+ cp_ctx(ctx, 0x403800, 1);
|
|
+
|
|
+ cp_ctx(ctx, 0x403c18, 1);
|
|
+ gr_def(ctx, 0x403c18, 0x00000001);
|
|
+ switch (dev_priv->chipset) {
|
|
+ case 0x46:
|
|
+ case 0x47:
|
|
+ case 0x49:
|
|
+ case 0x4b:
|
|
+ cp_ctx(ctx, 0x405018, 1);
|
|
+ gr_def(ctx, 0x405018, 0x08e00001);
|
|
+ cp_ctx(ctx, 0x405c24, 1);
|
|
+ gr_def(ctx, 0x405c24, 0x000e3000);
|
|
+ break;
|
|
+ }
|
|
+ if (dev_priv->chipset != 0x4e)
|
|
+ cp_ctx(ctx, 0x405800, 11);
|
|
+ cp_ctx(ctx, 0x407000, 1);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv40_graph_construct_state3d_3(struct nouveau_grctx *ctx)
|
|
+{
|
|
+ int len = nv40_graph_4097(ctx->dev) ? 0x0684 : 0x0084;
|
|
+
|
|
+ cp_out (ctx, 0x300000);
|
|
+ cp_lsr (ctx, len - 4);
|
|
+ cp_bra (ctx, SWAP_DIRECTION, SAVE, cp_swap_state3d_3_is_save);
|
|
+ cp_lsr (ctx, len);
|
|
+ cp_name(ctx, cp_swap_state3d_3_is_save);
|
|
+ cp_out (ctx, 0x800001);
|
|
+
|
|
+ ctx->ctxvals_pos += len;
|
|
+}
|
|
+
|
|
+static void
|
|
+nv40_graph_construct_shader(struct nouveau_grctx *ctx)
|
|
+{
|
|
+ struct drm_device *dev = ctx->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_gpuobj *obj = ctx->data;
|
|
+ int vs, vs_nr, vs_len, vs_nr_b0, vs_nr_b1, b0_offset, b1_offset;
|
|
+ int offset, i;
|
|
+
|
|
+ vs_nr = nv40_graph_vs_count(ctx->dev);
|
|
+ vs_nr_b0 = 363;
|
|
+ vs_nr_b1 = dev_priv->chipset == 0x40 ? 128 : 64;
|
|
+ if (dev_priv->chipset == 0x40) {
|
|
+ b0_offset = 0x2200/4; /* 33a0 */
|
|
+ b1_offset = 0x55a0/4; /* 1500 */
|
|
+ vs_len = 0x6aa0/4;
|
|
+ } else
|
|
+ if (dev_priv->chipset == 0x41 || dev_priv->chipset == 0x42) {
|
|
+ b0_offset = 0x2200/4; /* 2200 */
|
|
+ b1_offset = 0x4400/4; /* 0b00 */
|
|
+ vs_len = 0x4f00/4;
|
|
+ } else {
|
|
+ b0_offset = 0x1d40/4; /* 2200 */
|
|
+ b1_offset = 0x3f40/4; /* 0b00 : 0a40 */
|
|
+ vs_len = nv40_graph_4097(dev) ? 0x4a40/4 : 0x4980/4;
|
|
+ }
|
|
+
|
|
+ cp_lsr(ctx, vs_len * vs_nr + 0x300/4);
|
|
+ cp_out(ctx, nv40_graph_4097(dev) ? 0x800041 : 0x800029);
|
|
+
|
|
+ offset = ctx->ctxvals_pos;
|
|
+ ctx->ctxvals_pos += (0x0300/4 + (vs_nr * vs_len));
|
|
+
|
|
+ if (ctx->mode != NOUVEAU_GRCTX_VALS)
|
|
+ return;
|
|
+
|
|
+ offset += 0x0280/4;
|
|
+ for (i = 0; i < 16; i++, offset += 2)
|
|
+ nv_wo32(dev, obj, offset, 0x3f800000);
|
|
+
|
|
+ for (vs = 0; vs < vs_nr; vs++, offset += vs_len) {
|
|
+ for (i = 0; i < vs_nr_b0 * 6; i += 6)
|
|
+ nv_wo32(dev, obj, offset + b0_offset + i, 0x00000001);
|
|
+ for (i = 0; i < vs_nr_b1 * 4; i += 4)
|
|
+ nv_wo32(dev, obj, offset + b1_offset + i, 0x3f800000);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+nv40_grctx_init(struct nouveau_grctx *ctx)
|
|
+{
|
|
+ /* decide whether we're loading/unloading the context */
|
|
+ cp_bra (ctx, AUTO_SAVE, PENDING, cp_setup_save);
|
|
+ cp_bra (ctx, USER_SAVE, PENDING, cp_setup_save);
|
|
+
|
|
+ cp_name(ctx, cp_check_load);
|
|
+ cp_bra (ctx, AUTO_LOAD, PENDING, cp_setup_auto_load);
|
|
+ cp_bra (ctx, USER_LOAD, PENDING, cp_setup_load);
|
|
+ cp_bra (ctx, ALWAYS, TRUE, cp_exit);
|
|
+
|
|
+ /* setup for context load */
|
|
+ cp_name(ctx, cp_setup_auto_load);
|
|
+ cp_wait(ctx, STATUS, IDLE);
|
|
+ cp_out (ctx, CP_NEXT_TO_SWAP);
|
|
+ cp_name(ctx, cp_setup_load);
|
|
+ cp_wait(ctx, STATUS, IDLE);
|
|
+ cp_set (ctx, SWAP_DIRECTION, LOAD);
|
|
+ cp_out (ctx, 0x00910880); /* ?? */
|
|
+ cp_out (ctx, 0x00901ffe); /* ?? */
|
|
+ cp_out (ctx, 0x01940000); /* ?? */
|
|
+ cp_lsr (ctx, 0x20);
|
|
+ cp_out (ctx, 0x0060000b); /* ?? */
|
|
+ cp_wait(ctx, UNK57, CLEAR);
|
|
+ cp_out (ctx, 0x0060000c); /* ?? */
|
|
+ cp_bra (ctx, ALWAYS, TRUE, cp_swap_state);
|
|
+
|
|
+ /* setup for context save */
|
|
+ cp_name(ctx, cp_setup_save);
|
|
+ cp_set (ctx, SWAP_DIRECTION, SAVE);
|
|
+
|
|
+ /* general PGRAPH state */
|
|
+ cp_name(ctx, cp_swap_state);
|
|
+ cp_pos (ctx, 0x00020/4);
|
|
+ nv40_graph_construct_general(ctx);
|
|
+ cp_wait(ctx, STATUS, IDLE);
|
|
+
|
|
+ /* 3D state, block 1 */
|
|
+ cp_bra (ctx, UNK54, CLEAR, cp_prepare_exit);
|
|
+ nv40_graph_construct_state3d(ctx);
|
|
+ cp_wait(ctx, STATUS, IDLE);
|
|
+
|
|
+ /* 3D state, block 2 */
|
|
+ nv40_graph_construct_state3d_2(ctx);
|
|
+
|
|
+ /* Some other block of "random" state */
|
|
+ nv40_graph_construct_state3d_3(ctx);
|
|
+
|
|
+ /* Per-vertex shader state */
|
|
+ cp_pos (ctx, ctx->ctxvals_pos);
|
|
+ nv40_graph_construct_shader(ctx);
|
|
+
|
|
+ /* pre-exit state updates */
|
|
+ cp_name(ctx, cp_prepare_exit);
|
|
+ cp_bra (ctx, SWAP_DIRECTION, SAVE, cp_check_load);
|
|
+ cp_bra (ctx, USER_SAVE, PENDING, cp_exit);
|
|
+ cp_out (ctx, CP_NEXT_TO_CURRENT);
|
|
+
|
|
+ cp_name(ctx, cp_exit);
|
|
+ cp_set (ctx, USER_SAVE, NOT_PENDING);
|
|
+ cp_set (ctx, USER_LOAD, NOT_PENDING);
|
|
+ cp_out (ctx, CP_END);
|
|
+}
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nv40_mc.c b/drivers/gpu/drm/nouveau/nv40_mc.c
|
|
new file mode 100644
|
|
index 0000000..2a3495e
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv40_mc.c
|
|
@@ -0,0 +1,38 @@
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_drm.h"
|
|
+
|
|
+int
|
|
+nv40_mc_init(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t tmp;
|
|
+
|
|
+ /* Power up everything, resetting each individual unit will
|
|
+ * be done later if needed.
|
|
+ */
|
|
+ nv_wr32(dev, NV03_PMC_ENABLE, 0xFFFFFFFF);
|
|
+
|
|
+ switch (dev_priv->chipset) {
|
|
+ case 0x44:
|
|
+ case 0x46: /* G72 */
|
|
+ case 0x4e:
|
|
+ case 0x4c: /* C51_G7X */
|
|
+ tmp = nv_rd32(dev, NV40_PFB_020C);
|
|
+ nv_wr32(dev, NV40_PMC_1700, tmp);
|
|
+ nv_wr32(dev, NV40_PMC_1704, 0);
|
|
+ nv_wr32(dev, NV40_PMC_1708, 0);
|
|
+ nv_wr32(dev, NV40_PMC_170C, tmp);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv40_mc_takedown(struct drm_device *dev)
|
|
+{
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c
|
|
new file mode 100644
|
|
index 0000000..d1a651e
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv50_crtc.c
|
|
@@ -0,0 +1,792 @@
|
|
+/*
|
|
+ * Copyright (C) 2008 Maarten Maathuis.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm_mode.h"
|
|
+#include "drm_crtc_helper.h"
|
|
+
|
|
+#define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO)
|
|
+#include "nouveau_reg.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_hw.h"
|
|
+#include "nouveau_encoder.h"
|
|
+#include "nouveau_crtc.h"
|
|
+#include "nouveau_fb.h"
|
|
+#include "nouveau_connector.h"
|
|
+#include "nv50_display.h"
|
|
+
|
|
+static void
|
|
+nv50_crtc_lut_load(struct drm_crtc *crtc)
|
|
+{
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+ void __iomem *lut = nvbo_kmap_obj_iovirtual(nv_crtc->lut.nvbo);
|
|
+ int i;
|
|
+
|
|
+ NV_DEBUG_KMS(crtc->dev, "\n");
|
|
+
|
|
+ for (i = 0; i < 256; i++) {
|
|
+ writew(nv_crtc->lut.r[i] >> 2, lut + 8*i + 0);
|
|
+ writew(nv_crtc->lut.g[i] >> 2, lut + 8*i + 2);
|
|
+ writew(nv_crtc->lut.b[i] >> 2, lut + 8*i + 4);
|
|
+ }
|
|
+
|
|
+ if (nv_crtc->lut.depth == 30) {
|
|
+ writew(nv_crtc->lut.r[i - 1] >> 2, lut + 8*i + 0);
|
|
+ writew(nv_crtc->lut.g[i - 1] >> 2, lut + 8*i + 2);
|
|
+ writew(nv_crtc->lut.b[i - 1] >> 2, lut + 8*i + 4);
|
|
+ }
|
|
+}
|
|
+
|
|
+int
|
|
+nv50_crtc_blank(struct nouveau_crtc *nv_crtc, bool blanked)
|
|
+{
|
|
+ struct drm_device *dev = nv_crtc->base.dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_channel *evo = dev_priv->evo;
|
|
+ int index = nv_crtc->index, ret;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index);
|
|
+ NV_DEBUG_KMS(dev, "%s\n", blanked ? "blanked" : "unblanked");
|
|
+
|
|
+ if (blanked) {
|
|
+ nv_crtc->cursor.hide(nv_crtc, false);
|
|
+
|
|
+ ret = RING_SPACE(evo, dev_priv->chipset != 0x50 ? 7 : 5);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "no space while blanking crtc\n");
|
|
+ return ret;
|
|
+ }
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_CRTC(index, CLUT_MODE), 2);
|
|
+ OUT_RING(evo, NV50_EVO_CRTC_CLUT_MODE_BLANK);
|
|
+ OUT_RING(evo, 0);
|
|
+ if (dev_priv->chipset != 0x50) {
|
|
+ BEGIN_RING(evo, 0, NV84_EVO_CRTC(index, CLUT_DMA), 1);
|
|
+ OUT_RING(evo, NV84_EVO_CRTC_CLUT_DMA_HANDLE_NONE);
|
|
+ }
|
|
+
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_CRTC(index, FB_DMA), 1);
|
|
+ OUT_RING(evo, NV50_EVO_CRTC_FB_DMA_HANDLE_NONE);
|
|
+ } else {
|
|
+ if (nv_crtc->cursor.visible)
|
|
+ nv_crtc->cursor.show(nv_crtc, false);
|
|
+ else
|
|
+ nv_crtc->cursor.hide(nv_crtc, false);
|
|
+
|
|
+ ret = RING_SPACE(evo, dev_priv->chipset != 0x50 ? 10 : 8);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "no space while unblanking crtc\n");
|
|
+ return ret;
|
|
+ }
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_CRTC(index, CLUT_MODE), 2);
|
|
+ OUT_RING(evo, nv_crtc->lut.depth == 8 ?
|
|
+ NV50_EVO_CRTC_CLUT_MODE_OFF :
|
|
+ NV50_EVO_CRTC_CLUT_MODE_ON);
|
|
+ OUT_RING(evo, (nv_crtc->lut.nvbo->bo.mem.mm_node->start <<
|
|
+ PAGE_SHIFT) >> 8);
|
|
+ if (dev_priv->chipset != 0x50) {
|
|
+ BEGIN_RING(evo, 0, NV84_EVO_CRTC(index, CLUT_DMA), 1);
|
|
+ OUT_RING(evo, NvEvoVRAM);
|
|
+ }
|
|
+
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_CRTC(index, FB_OFFSET), 2);
|
|
+ OUT_RING(evo, nv_crtc->fb.offset >> 8);
|
|
+ OUT_RING(evo, 0);
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_CRTC(index, FB_DMA), 1);
|
|
+ if (dev_priv->chipset != 0x50)
|
|
+ if (nv_crtc->fb.tile_flags == 0x7a00)
|
|
+ OUT_RING(evo, NvEvoFB32);
|
|
+ else
|
|
+ if (nv_crtc->fb.tile_flags == 0x7000)
|
|
+ OUT_RING(evo, NvEvoFB16);
|
|
+ else
|
|
+ OUT_RING(evo, NvEvoVRAM);
|
|
+ else
|
|
+ OUT_RING(evo, NvEvoVRAM);
|
|
+ }
|
|
+
|
|
+ nv_crtc->fb.blanked = blanked;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool on, bool update)
|
|
+{
|
|
+ struct drm_device *dev = nv_crtc->base.dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_channel *evo = dev_priv->evo;
|
|
+ int ret;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "\n");
|
|
+
|
|
+ ret = RING_SPACE(evo, 2 + (update ? 2 : 0));
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "no space while setting dither\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, DITHER_CTRL), 1);
|
|
+ if (on)
|
|
+ OUT_RING(evo, NV50_EVO_CRTC_DITHER_CTRL_ON);
|
|
+ else
|
|
+ OUT_RING(evo, NV50_EVO_CRTC_DITHER_CTRL_OFF);
|
|
+
|
|
+ if (update) {
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
|
|
+ OUT_RING(evo, 0);
|
|
+ FIRE_RING(evo);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+struct nouveau_connector *
|
|
+nouveau_crtc_connector_get(struct nouveau_crtc *nv_crtc)
|
|
+{
|
|
+ struct drm_device *dev = nv_crtc->base.dev;
|
|
+ struct drm_connector *connector;
|
|
+ struct drm_crtc *crtc = to_drm_crtc(nv_crtc);
|
|
+
|
|
+ /* The safest approach is to find an encoder with the right crtc, that
|
|
+ * is also linked to a connector. */
|
|
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
|
+ if (connector->encoder)
|
|
+ if (connector->encoder->crtc == crtc)
|
|
+ return nouveau_connector(connector);
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static int
|
|
+nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, int scaling_mode, bool update)
|
|
+{
|
|
+ struct nouveau_connector *nv_connector =
|
|
+ nouveau_crtc_connector_get(nv_crtc);
|
|
+ struct drm_device *dev = nv_crtc->base.dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_channel *evo = dev_priv->evo;
|
|
+ struct drm_display_mode *native_mode = NULL;
|
|
+ struct drm_display_mode *mode = &nv_crtc->base.mode;
|
|
+ uint32_t outX, outY, horiz, vert;
|
|
+ int ret;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "\n");
|
|
+
|
|
+ switch (scaling_mode) {
|
|
+ case DRM_MODE_SCALE_NONE:
|
|
+ break;
|
|
+ default:
|
|
+ if (!nv_connector || !nv_connector->native_mode) {
|
|
+ NV_ERROR(dev, "No native mode, forcing panel scaling\n");
|
|
+ scaling_mode = DRM_MODE_SCALE_NONE;
|
|
+ } else {
|
|
+ native_mode = nv_connector->native_mode;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ switch (scaling_mode) {
|
|
+ case DRM_MODE_SCALE_ASPECT:
|
|
+ horiz = (native_mode->hdisplay << 19) / mode->hdisplay;
|
|
+ vert = (native_mode->vdisplay << 19) / mode->vdisplay;
|
|
+
|
|
+ if (vert > horiz) {
|
|
+ outX = (mode->hdisplay * horiz) >> 19;
|
|
+ outY = (mode->vdisplay * horiz) >> 19;
|
|
+ } else {
|
|
+ outX = (mode->hdisplay * vert) >> 19;
|
|
+ outY = (mode->vdisplay * vert) >> 19;
|
|
+ }
|
|
+ break;
|
|
+ case DRM_MODE_SCALE_FULLSCREEN:
|
|
+ outX = native_mode->hdisplay;
|
|
+ outY = native_mode->vdisplay;
|
|
+ break;
|
|
+ case DRM_MODE_SCALE_CENTER:
|
|
+ case DRM_MODE_SCALE_NONE:
|
|
+ default:
|
|
+ outX = mode->hdisplay;
|
|
+ outY = mode->vdisplay;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ ret = RING_SPACE(evo, update ? 7 : 5);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* Got a better name for SCALER_ACTIVE? */
|
|
+ /* One day i've got to really figure out why this is needed. */
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_CTRL), 1);
|
|
+ if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) ||
|
|
+ (mode->flags & DRM_MODE_FLAG_INTERLACE) ||
|
|
+ mode->hdisplay != outX || mode->vdisplay != outY) {
|
|
+ OUT_RING(evo, NV50_EVO_CRTC_SCALE_CTRL_ACTIVE);
|
|
+ } else {
|
|
+ OUT_RING(evo, NV50_EVO_CRTC_SCALE_CTRL_INACTIVE);
|
|
+ }
|
|
+
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_RES1), 2);
|
|
+ OUT_RING(evo, outY << 16 | outX);
|
|
+ OUT_RING(evo, outY << 16 | outX);
|
|
+
|
|
+ if (update) {
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
|
|
+ OUT_RING(evo, 0);
|
|
+ FIRE_RING(evo);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk)
|
|
+{
|
|
+ uint32_t pll_reg = NV50_PDISPLAY_CRTC_CLK_CTRL1(head);
|
|
+ struct nouveau_pll_vals pll;
|
|
+ struct pll_lims limits;
|
|
+ uint32_t reg1, reg2;
|
|
+ int ret;
|
|
+
|
|
+ ret = get_pll_limits(dev, pll_reg, &limits);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = nouveau_calc_pll_mnp(dev, &limits, pclk, &pll);
|
|
+ if (ret <= 0)
|
|
+ return ret;
|
|
+
|
|
+ if (limits.vco2.maxfreq) {
|
|
+ reg1 = nv_rd32(dev, pll_reg + 4) & 0xff00ff00;
|
|
+ reg2 = nv_rd32(dev, pll_reg + 8) & 0x8000ff00;
|
|
+ nv_wr32(dev, pll_reg, 0x10000611);
|
|
+ nv_wr32(dev, pll_reg + 4, reg1 | (pll.M1 << 16) | pll.N1);
|
|
+ nv_wr32(dev, pll_reg + 8,
|
|
+ reg2 | (pll.log2P << 28) | (pll.M2 << 16) | pll.N2);
|
|
+ } else {
|
|
+ reg1 = nv_rd32(dev, pll_reg + 4) & 0xffc00000;
|
|
+ nv_wr32(dev, pll_reg, 0x50000610);
|
|
+ nv_wr32(dev, pll_reg + 4, reg1 |
|
|
+ (pll.log2P << 16) | (pll.M1 << 8) | pll.N1);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_crtc_destroy(struct drm_crtc *crtc)
|
|
+{
|
|
+ struct drm_device *dev;
|
|
+ struct nouveau_crtc *nv_crtc;
|
|
+
|
|
+ if (!crtc)
|
|
+ return;
|
|
+
|
|
+ dev = crtc->dev;
|
|
+ nv_crtc = nouveau_crtc(crtc);
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "\n");
|
|
+
|
|
+ drm_crtc_cleanup(&nv_crtc->base);
|
|
+
|
|
+ nv50_cursor_fini(nv_crtc);
|
|
+
|
|
+ nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo);
|
|
+ nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo);
|
|
+ kfree(nv_crtc->mode);
|
|
+ kfree(nv_crtc);
|
|
+}
|
|
+
|
|
+int
|
|
+nv50_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
|
|
+ uint32_t buffer_handle, uint32_t width, uint32_t height)
|
|
+{
|
|
+ struct drm_device *dev = crtc->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+ struct nouveau_bo *cursor = NULL;
|
|
+ struct drm_gem_object *gem;
|
|
+ int ret = 0, i;
|
|
+
|
|
+ if (width != 64 || height != 64)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (!buffer_handle) {
|
|
+ nv_crtc->cursor.hide(nv_crtc, true);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ gem = drm_gem_object_lookup(dev, file_priv, buffer_handle);
|
|
+ if (!gem)
|
|
+ return -EINVAL;
|
|
+ cursor = nouveau_gem_object(gem);
|
|
+
|
|
+ ret = nouveau_bo_map(cursor);
|
|
+ if (ret)
|
|
+ goto out;
|
|
+
|
|
+ /* The simple will do for now. */
|
|
+ for (i = 0; i < 64 * 64; i++)
|
|
+ nouveau_bo_wr32(nv_crtc->cursor.nvbo, i, nouveau_bo_rd32(cursor, i));
|
|
+
|
|
+ nouveau_bo_unmap(cursor);
|
|
+
|
|
+ nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.nvbo->bo.offset -
|
|
+ dev_priv->vm_vram_base);
|
|
+ nv_crtc->cursor.show(nv_crtc, true);
|
|
+
|
|
+out:
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
+ drm_gem_object_unreference(gem);
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int
|
|
+nv50_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
|
|
+{
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+
|
|
+ nv_crtc->cursor.set_pos(nv_crtc, x, y);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,
|
|
+ uint32_t size)
|
|
+{
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+ int i;
|
|
+
|
|
+ if (size != 256)
|
|
+ return;
|
|
+
|
|
+ for (i = 0; i < 256; i++) {
|
|
+ nv_crtc->lut.r[i] = r[i];
|
|
+ nv_crtc->lut.g[i] = g[i];
|
|
+ nv_crtc->lut.b[i] = b[i];
|
|
+ }
|
|
+
|
|
+ /* We need to know the depth before we upload, but it's possible to
|
|
+ * get called before a framebuffer is bound. If this is the case,
|
|
+ * mark the lut values as dirty by setting depth==0, and it'll be
|
|
+ * uploaded on the first mode_set_base()
|
|
+ */
|
|
+ if (!nv_crtc->base.fb) {
|
|
+ nv_crtc->lut.depth = 0;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ nv50_crtc_lut_load(crtc);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_crtc_save(struct drm_crtc *crtc)
|
|
+{
|
|
+ NV_ERROR(crtc->dev, "!!\n");
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_crtc_restore(struct drm_crtc *crtc)
|
|
+{
|
|
+ NV_ERROR(crtc->dev, "!!\n");
|
|
+}
|
|
+
|
|
+static const struct drm_crtc_funcs nv50_crtc_funcs = {
|
|
+ .save = nv50_crtc_save,
|
|
+ .restore = nv50_crtc_restore,
|
|
+ .cursor_set = nv50_crtc_cursor_set,
|
|
+ .cursor_move = nv50_crtc_cursor_move,
|
|
+ .gamma_set = nv50_crtc_gamma_set,
|
|
+ .set_config = drm_crtc_helper_set_config,
|
|
+ .destroy = nv50_crtc_destroy,
|
|
+};
|
|
+
|
|
+static void
|
|
+nv50_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|
+{
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_crtc_prepare(struct drm_crtc *crtc)
|
|
+{
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+ struct drm_device *dev = crtc->dev;
|
|
+ struct drm_encoder *encoder;
|
|
+ uint32_t dac = 0, sor = 0;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index);
|
|
+
|
|
+ /* Disconnect all unused encoders. */
|
|
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+
|
|
+ if (!drm_helper_encoder_in_use(encoder))
|
|
+ continue;
|
|
+
|
|
+ if (nv_encoder->dcb->type == OUTPUT_ANALOG ||
|
|
+ nv_encoder->dcb->type == OUTPUT_TV)
|
|
+ dac |= (1 << nv_encoder->or);
|
|
+ else
|
|
+ sor |= (1 << nv_encoder->or);
|
|
+ }
|
|
+
|
|
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+
|
|
+ if (nv_encoder->dcb->type == OUTPUT_ANALOG ||
|
|
+ nv_encoder->dcb->type == OUTPUT_TV) {
|
|
+ if (dac & (1 << nv_encoder->or))
|
|
+ continue;
|
|
+ } else {
|
|
+ if (sor & (1 << nv_encoder->or))
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ nv_encoder->disconnect(nv_encoder);
|
|
+ }
|
|
+
|
|
+ nv50_crtc_blank(nv_crtc, true);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_crtc_commit(struct drm_crtc *crtc)
|
|
+{
|
|
+ struct drm_crtc *crtc2;
|
|
+ struct drm_device *dev = crtc->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_channel *evo = dev_priv->evo;
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+ int ret;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index);
|
|
+
|
|
+ nv50_crtc_blank(nv_crtc, false);
|
|
+
|
|
+ /* Explicitly blank all unused crtc's. */
|
|
+ list_for_each_entry(crtc2, &dev->mode_config.crtc_list, head) {
|
|
+ if (!drm_helper_crtc_in_use(crtc2))
|
|
+ nv50_crtc_blank(nouveau_crtc(crtc2), true);
|
|
+ }
|
|
+
|
|
+ ret = RING_SPACE(evo, 2);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "no space while committing crtc\n");
|
|
+ return;
|
|
+ }
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
|
|
+ OUT_RING(evo, 0);
|
|
+ FIRE_RING(evo);
|
|
+}
|
|
+
|
|
+static bool
|
|
+nv50_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *mode,
|
|
+ struct drm_display_mode *adjusted_mode)
|
|
+{
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static int
|
|
+nv50_crtc_do_mode_set_base(struct drm_crtc *crtc, int x, int y,
|
|
+ struct drm_framebuffer *old_fb, bool update)
|
|
+{
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+ struct drm_device *dev = nv_crtc->base.dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_channel *evo = dev_priv->evo;
|
|
+ struct drm_framebuffer *drm_fb = nv_crtc->base.fb;
|
|
+ struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);
|
|
+ int ret, format;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index);
|
|
+
|
|
+ switch (drm_fb->depth) {
|
|
+ case 8:
|
|
+ format = NV50_EVO_CRTC_FB_DEPTH_8;
|
|
+ break;
|
|
+ case 15:
|
|
+ format = NV50_EVO_CRTC_FB_DEPTH_15;
|
|
+ break;
|
|
+ case 16:
|
|
+ format = NV50_EVO_CRTC_FB_DEPTH_16;
|
|
+ break;
|
|
+ case 24:
|
|
+ case 32:
|
|
+ format = NV50_EVO_CRTC_FB_DEPTH_24;
|
|
+ break;
|
|
+ case 30:
|
|
+ format = NV50_EVO_CRTC_FB_DEPTH_30;
|
|
+ break;
|
|
+ default:
|
|
+ NV_ERROR(dev, "unknown depth %d\n", drm_fb->depth);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ret = nouveau_bo_pin(fb->nvbo, TTM_PL_FLAG_VRAM);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ if (old_fb) {
|
|
+ struct nouveau_framebuffer *ofb = nouveau_framebuffer(old_fb);
|
|
+ nouveau_bo_unpin(ofb->nvbo);
|
|
+ }
|
|
+
|
|
+ nv_crtc->fb.offset = fb->nvbo->bo.offset - dev_priv->vm_vram_base;
|
|
+ nv_crtc->fb.tile_flags = fb->nvbo->tile_flags;
|
|
+ nv_crtc->fb.cpp = drm_fb->bits_per_pixel / 8;
|
|
+ if (!nv_crtc->fb.blanked && dev_priv->chipset != 0x50) {
|
|
+ ret = RING_SPACE(evo, 2);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_DMA), 1);
|
|
+ if (nv_crtc->fb.tile_flags == 0x7a00)
|
|
+ OUT_RING(evo, NvEvoFB32);
|
|
+ else
|
|
+ if (nv_crtc->fb.tile_flags == 0x7000)
|
|
+ OUT_RING(evo, NvEvoFB16);
|
|
+ else
|
|
+ OUT_RING(evo, NvEvoVRAM);
|
|
+ }
|
|
+
|
|
+ ret = RING_SPACE(evo, 12);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_OFFSET), 5);
|
|
+ OUT_RING(evo, nv_crtc->fb.offset >> 8);
|
|
+ OUT_RING(evo, 0);
|
|
+ OUT_RING(evo, (drm_fb->height << 16) | drm_fb->width);
|
|
+ if (!nv_crtc->fb.tile_flags) {
|
|
+ OUT_RING(evo, drm_fb->pitch | (1 << 20));
|
|
+ } else {
|
|
+ OUT_RING(evo, ((drm_fb->pitch / 4) << 4) |
|
|
+ fb->nvbo->tile_mode);
|
|
+ }
|
|
+ if (dev_priv->chipset == 0x50)
|
|
+ OUT_RING(evo, (fb->nvbo->tile_flags << 8) | format);
|
|
+ else
|
|
+ OUT_RING(evo, format);
|
|
+
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CLUT_MODE), 1);
|
|
+ OUT_RING(evo, fb->base.depth == 8 ?
|
|
+ NV50_EVO_CRTC_CLUT_MODE_OFF : NV50_EVO_CRTC_CLUT_MODE_ON);
|
|
+
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, COLOR_CTRL), 1);
|
|
+ OUT_RING(evo, NV50_EVO_CRTC_COLOR_CTRL_COLOR);
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_POS), 1);
|
|
+ OUT_RING(evo, (y << 16) | x);
|
|
+
|
|
+ if (nv_crtc->lut.depth != fb->base.depth) {
|
|
+ nv_crtc->lut.depth = fb->base.depth;
|
|
+ nv50_crtc_lut_load(crtc);
|
|
+ }
|
|
+
|
|
+ if (update) {
|
|
+ ret = RING_SPACE(evo, 2);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
|
|
+ OUT_RING(evo, 0);
|
|
+ FIRE_RING(evo);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+nv50_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_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_channel *evo = dev_priv->evo;
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
+ struct nouveau_connector *nv_connector = NULL;
|
|
+ uint32_t hsync_dur, vsync_dur, hsync_start_to_end, vsync_start_to_end;
|
|
+ uint32_t hunk1, vunk1, vunk2a, vunk2b;
|
|
+ int ret;
|
|
+
|
|
+ /* Find the connector attached to this CRTC */
|
|
+ nv_connector = nouveau_crtc_connector_get(nv_crtc);
|
|
+
|
|
+ *nv_crtc->mode = *adjusted_mode;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index);
|
|
+
|
|
+ hsync_dur = adjusted_mode->hsync_end - adjusted_mode->hsync_start;
|
|
+ vsync_dur = adjusted_mode->vsync_end - adjusted_mode->vsync_start;
|
|
+ hsync_start_to_end = adjusted_mode->htotal - adjusted_mode->hsync_start;
|
|
+ vsync_start_to_end = adjusted_mode->vtotal - adjusted_mode->vsync_start;
|
|
+ /* I can't give this a proper name, anyone else can? */
|
|
+ hunk1 = adjusted_mode->htotal -
|
|
+ adjusted_mode->hsync_start + adjusted_mode->hdisplay;
|
|
+ vunk1 = adjusted_mode->vtotal -
|
|
+ adjusted_mode->vsync_start + adjusted_mode->vdisplay;
|
|
+ /* Another strange value, this time only for interlaced adjusted_modes. */
|
|
+ vunk2a = 2 * adjusted_mode->vtotal -
|
|
+ adjusted_mode->vsync_start + adjusted_mode->vdisplay;
|
|
+ vunk2b = adjusted_mode->vtotal -
|
|
+ adjusted_mode->vsync_start + adjusted_mode->vtotal;
|
|
+
|
|
+ if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) {
|
|
+ vsync_dur /= 2;
|
|
+ vsync_start_to_end /= 2;
|
|
+ vunk1 /= 2;
|
|
+ vunk2a /= 2;
|
|
+ vunk2b /= 2;
|
|
+ /* magic */
|
|
+ if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) {
|
|
+ vsync_start_to_end -= 1;
|
|
+ vunk1 -= 1;
|
|
+ vunk2a -= 1;
|
|
+ vunk2b -= 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ret = RING_SPACE(evo, 17);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CLOCK), 2);
|
|
+ OUT_RING(evo, adjusted_mode->clock | 0x800000);
|
|
+ OUT_RING(evo, (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 0);
|
|
+
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, DISPLAY_START), 5);
|
|
+ OUT_RING(evo, 0);
|
|
+ OUT_RING(evo, (adjusted_mode->vtotal << 16) | adjusted_mode->htotal);
|
|
+ OUT_RING(evo, (vsync_dur - 1) << 16 | (hsync_dur - 1));
|
|
+ OUT_RING(evo, (vsync_start_to_end - 1) << 16 |
|
|
+ (hsync_start_to_end - 1));
|
|
+ OUT_RING(evo, (vunk1 - 1) << 16 | (hunk1 - 1));
|
|
+
|
|
+ if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) {
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, UNK0824), 1);
|
|
+ OUT_RING(evo, (vunk2b - 1) << 16 | (vunk2a - 1));
|
|
+ } else {
|
|
+ OUT_RING(evo, 0);
|
|
+ OUT_RING(evo, 0);
|
|
+ }
|
|
+
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, UNK082C), 1);
|
|
+ OUT_RING(evo, 0);
|
|
+
|
|
+ /* This is the actual resolution of the mode. */
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, REAL_RES), 1);
|
|
+ OUT_RING(evo, (mode->vdisplay << 16) | mode->hdisplay);
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_CENTER_OFFSET), 1);
|
|
+ OUT_RING(evo, NV50_EVO_CRTC_SCALE_CENTER_OFFSET_VAL(0, 0));
|
|
+
|
|
+ nv_crtc->set_dither(nv_crtc, nv_connector->use_dithering, false);
|
|
+ nv_crtc->set_scale(nv_crtc, nv_connector->scaling_mode, false);
|
|
+
|
|
+ return nv50_crtc_do_mode_set_base(crtc, x, y, old_fb, false);
|
|
+}
|
|
+
|
|
+static int
|
|
+nv50_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
|
|
+ struct drm_framebuffer *old_fb)
|
|
+{
|
|
+ return nv50_crtc_do_mode_set_base(crtc, x, y, old_fb, true);
|
|
+}
|
|
+
|
|
+static const struct drm_crtc_helper_funcs nv50_crtc_helper_funcs = {
|
|
+ .dpms = nv50_crtc_dpms,
|
|
+ .prepare = nv50_crtc_prepare,
|
|
+ .commit = nv50_crtc_commit,
|
|
+ .mode_fixup = nv50_crtc_mode_fixup,
|
|
+ .mode_set = nv50_crtc_mode_set,
|
|
+ .mode_set_base = nv50_crtc_mode_set_base,
|
|
+ .load_lut = nv50_crtc_lut_load,
|
|
+};
|
|
+
|
|
+int
|
|
+nv50_crtc_create(struct drm_device *dev, int index)
|
|
+{
|
|
+ struct nouveau_crtc *nv_crtc = NULL;
|
|
+ int ret, i;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "\n");
|
|
+
|
|
+ nv_crtc = kzalloc(sizeof(*nv_crtc), GFP_KERNEL);
|
|
+ if (!nv_crtc)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ nv_crtc->mode = kzalloc(sizeof(*nv_crtc->mode), GFP_KERNEL);
|
|
+ if (!nv_crtc->mode) {
|
|
+ kfree(nv_crtc);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ /* Default CLUT parameters, will be activated on the hw upon
|
|
+ * first mode set.
|
|
+ */
|
|
+ for (i = 0; i < 256; i++) {
|
|
+ nv_crtc->lut.r[i] = i << 8;
|
|
+ nv_crtc->lut.g[i] = i << 8;
|
|
+ nv_crtc->lut.b[i] = i << 8;
|
|
+ }
|
|
+ nv_crtc->lut.depth = 0;
|
|
+
|
|
+ ret = nouveau_bo_new(dev, NULL, 4096, 0x100, TTM_PL_FLAG_VRAM,
|
|
+ 0, 0x0000, false, true, &nv_crtc->lut.nvbo);
|
|
+ if (!ret) {
|
|
+ ret = nouveau_bo_pin(nv_crtc->lut.nvbo, TTM_PL_FLAG_VRAM);
|
|
+ if (!ret)
|
|
+ ret = nouveau_bo_map(nv_crtc->lut.nvbo);
|
|
+ if (ret)
|
|
+ nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo);
|
|
+ }
|
|
+
|
|
+ if (ret) {
|
|
+ kfree(nv_crtc->mode);
|
|
+ kfree(nv_crtc);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ nv_crtc->index = index;
|
|
+
|
|
+ /* set function pointers */
|
|
+ nv_crtc->set_dither = nv50_crtc_set_dither;
|
|
+ nv_crtc->set_scale = nv50_crtc_set_scale;
|
|
+
|
|
+ drm_crtc_init(dev, &nv_crtc->base, &nv50_crtc_funcs);
|
|
+ drm_crtc_helper_add(&nv_crtc->base, &nv50_crtc_helper_funcs);
|
|
+ drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256);
|
|
+
|
|
+ ret = nouveau_bo_new(dev, NULL, 64*64*4, 0x100, TTM_PL_FLAG_VRAM,
|
|
+ 0, 0x0000, false, true, &nv_crtc->cursor.nvbo);
|
|
+ if (!ret) {
|
|
+ ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM);
|
|
+ if (!ret)
|
|
+ ret = nouveau_bo_map(nv_crtc->cursor.nvbo);
|
|
+ if (ret)
|
|
+ nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo);
|
|
+ }
|
|
+
|
|
+ nv50_cursor_init(nv_crtc);
|
|
+ return 0;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nv50_cursor.c b/drivers/gpu/drm/nouveau/nv50_cursor.c
|
|
new file mode 100644
|
|
index 0000000..753e723
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv50_cursor.c
|
|
@@ -0,0 +1,156 @@
|
|
+/*
|
|
+ * Copyright (C) 2008 Maarten Maathuis.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm_mode.h"
|
|
+
|
|
+#define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO)
|
|
+#include "nouveau_reg.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_crtc.h"
|
|
+#include "nv50_display.h"
|
|
+
|
|
+static void
|
|
+nv50_cursor_show(struct nouveau_crtc *nv_crtc, bool update)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = nv_crtc->base.dev->dev_private;
|
|
+ struct nouveau_channel *evo = dev_priv->evo;
|
|
+ struct drm_device *dev = nv_crtc->base.dev;
|
|
+ int ret;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "\n");
|
|
+
|
|
+ if (update && nv_crtc->cursor.visible)
|
|
+ return;
|
|
+
|
|
+ ret = RING_SPACE(evo, (dev_priv->chipset != 0x50 ? 5 : 3) + update * 2);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "no space while unhiding cursor\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (dev_priv->chipset != 0x50) {
|
|
+ BEGIN_RING(evo, 0, NV84_EVO_CRTC(nv_crtc->index, CURSOR_DMA), 1);
|
|
+ OUT_RING(evo, NvEvoVRAM);
|
|
+ }
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CURSOR_CTRL), 2);
|
|
+ OUT_RING(evo, NV50_EVO_CRTC_CURSOR_CTRL_SHOW);
|
|
+ OUT_RING(evo, nv_crtc->cursor.offset >> 8);
|
|
+
|
|
+ if (update) {
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
|
|
+ OUT_RING(evo, 0);
|
|
+ FIRE_RING(evo);
|
|
+ nv_crtc->cursor.visible = true;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_cursor_hide(struct nouveau_crtc *nv_crtc, bool update)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = nv_crtc->base.dev->dev_private;
|
|
+ struct nouveau_channel *evo = dev_priv->evo;
|
|
+ struct drm_device *dev = nv_crtc->base.dev;
|
|
+ int ret;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "\n");
|
|
+
|
|
+ if (update && !nv_crtc->cursor.visible)
|
|
+ return;
|
|
+
|
|
+ ret = RING_SPACE(evo, (dev_priv->chipset != 0x50 ? 5 : 3) + update * 2);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "no space while hiding cursor\n");
|
|
+ return;
|
|
+ }
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CURSOR_CTRL), 2);
|
|
+ OUT_RING(evo, NV50_EVO_CRTC_CURSOR_CTRL_HIDE);
|
|
+ OUT_RING(evo, 0);
|
|
+ if (dev_priv->chipset != 0x50) {
|
|
+ BEGIN_RING(evo, 0, NV84_EVO_CRTC(nv_crtc->index, CURSOR_DMA), 1);
|
|
+ OUT_RING(evo, NV84_EVO_CRTC_CURSOR_DMA_HANDLE_NONE);
|
|
+ }
|
|
+
|
|
+ if (update) {
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
|
|
+ OUT_RING(evo, 0);
|
|
+ FIRE_RING(evo);
|
|
+ nv_crtc->cursor.visible = false;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_cursor_set_pos(struct nouveau_crtc *nv_crtc, int x, int y)
|
|
+{
|
|
+ struct drm_device *dev = nv_crtc->base.dev;
|
|
+
|
|
+ nv_wr32(dev, NV50_PDISPLAY_CURSOR_USER_POS(nv_crtc->index),
|
|
+ ((y & 0xFFFF) << 16) | (x & 0xFFFF));
|
|
+ /* Needed to make the cursor move. */
|
|
+ nv_wr32(dev, NV50_PDISPLAY_CURSOR_USER_POS_CTRL(nv_crtc->index), 0);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_cursor_set_offset(struct nouveau_crtc *nv_crtc, uint32_t offset)
|
|
+{
|
|
+ NV_DEBUG_KMS(nv_crtc->base.dev, "\n");
|
|
+ if (offset == nv_crtc->cursor.offset)
|
|
+ return;
|
|
+
|
|
+ nv_crtc->cursor.offset = offset;
|
|
+ if (nv_crtc->cursor.visible) {
|
|
+ nv_crtc->cursor.visible = false;
|
|
+ nv_crtc->cursor.show(nv_crtc, true);
|
|
+ }
|
|
+}
|
|
+
|
|
+int
|
|
+nv50_cursor_init(struct nouveau_crtc *nv_crtc)
|
|
+{
|
|
+ nv_crtc->cursor.set_offset = nv50_cursor_set_offset;
|
|
+ nv_crtc->cursor.set_pos = nv50_cursor_set_pos;
|
|
+ nv_crtc->cursor.hide = nv50_cursor_hide;
|
|
+ nv_crtc->cursor.show = nv50_cursor_show;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv50_cursor_fini(struct nouveau_crtc *nv_crtc)
|
|
+{
|
|
+ struct drm_device *dev = nv_crtc->base.dev;
|
|
+ int idx = nv_crtc->index;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "\n");
|
|
+
|
|
+ nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx), 0);
|
|
+ if (!nv_wait(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx),
|
|
+ NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, 0)) {
|
|
+ NV_ERROR(dev, "timeout: CURSOR_CTRL2_STATUS == 0\n");
|
|
+ NV_ERROR(dev, "CURSOR_CTRL2 = 0x%08x\n",
|
|
+ nv_rd32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx)));
|
|
+ }
|
|
+}
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nv50_dac.c b/drivers/gpu/drm/nouveau/nv50_dac.c
|
|
new file mode 100644
|
|
index 0000000..f08f042
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv50_dac.c
|
|
@@ -0,0 +1,304 @@
|
|
+/*
|
|
+ * Copyright (C) 2008 Maarten Maathuis.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm_crtc_helper.h"
|
|
+
|
|
+#define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO)
|
|
+#include "nouveau_reg.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_dma.h"
|
|
+#include "nouveau_encoder.h"
|
|
+#include "nouveau_connector.h"
|
|
+#include "nouveau_crtc.h"
|
|
+#include "nv50_display.h"
|
|
+
|
|
+static void
|
|
+nv50_dac_disconnect(struct nouveau_encoder *nv_encoder)
|
|
+{
|
|
+ struct drm_device *dev = to_drm_encoder(nv_encoder)->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_channel *evo = dev_priv->evo;
|
|
+ int ret;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "Disconnecting DAC %d\n", nv_encoder->or);
|
|
+
|
|
+ ret = RING_SPACE(evo, 2);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "no space while disconnecting DAC\n");
|
|
+ return;
|
|
+ }
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_DAC(nv_encoder->or, MODE_CTRL), 1);
|
|
+ OUT_RING(evo, 0);
|
|
+}
|
|
+
|
|
+static enum drm_connector_status
|
|
+nv50_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector)
|
|
+{
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ enum drm_connector_status status = connector_status_disconnected;
|
|
+ uint32_t dpms_state, load_pattern, load_state;
|
|
+ int or = nv_encoder->or;
|
|
+
|
|
+ nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL1(or), 0x00000001);
|
|
+ dpms_state = nv_rd32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or));
|
|
+
|
|
+ nv_wr32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or),
|
|
+ 0x00150000 | NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING);
|
|
+ if (!nv_wait(NV50_PDISPLAY_DAC_DPMS_CTRL(or),
|
|
+ NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING, 0)) {
|
|
+ NV_ERROR(dev, "timeout: DAC_DPMS_CTRL_PENDING(%d) == 0\n", or);
|
|
+ NV_ERROR(dev, "DAC_DPMS_CTRL(%d) = 0x%08x\n", or,
|
|
+ nv_rd32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or)));
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ /* Use bios provided value if possible. */
|
|
+ if (dev_priv->vbios->dactestval) {
|
|
+ load_pattern = dev_priv->vbios->dactestval;
|
|
+ NV_DEBUG_KMS(dev, "Using bios provided load_pattern of %d\n",
|
|
+ load_pattern);
|
|
+ } else {
|
|
+ load_pattern = 340;
|
|
+ NV_DEBUG_KMS(dev, "Using default load_pattern of %d\n",
|
|
+ load_pattern);
|
|
+ }
|
|
+
|
|
+ nv_wr32(dev, NV50_PDISPLAY_DAC_LOAD_CTRL(or),
|
|
+ NV50_PDISPLAY_DAC_LOAD_CTRL_ACTIVE | load_pattern);
|
|
+ mdelay(45); /* give it some time to process */
|
|
+ load_state = nv_rd32(dev, NV50_PDISPLAY_DAC_LOAD_CTRL(or));
|
|
+
|
|
+ nv_wr32(dev, NV50_PDISPLAY_DAC_LOAD_CTRL(or), 0);
|
|
+ nv_wr32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or), dpms_state |
|
|
+ NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING);
|
|
+
|
|
+ if ((load_state & NV50_PDISPLAY_DAC_LOAD_CTRL_PRESENT) ==
|
|
+ NV50_PDISPLAY_DAC_LOAD_CTRL_PRESENT)
|
|
+ status = connector_status_connected;
|
|
+
|
|
+ if (status == connector_status_connected)
|
|
+ NV_DEBUG_KMS(dev, "Load was detected on output with or %d\n", or);
|
|
+ else
|
|
+ NV_DEBUG_KMS(dev, "Load was not detected on output with or %d\n", or);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_dac_dpms(struct drm_encoder *encoder, int mode)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ uint32_t val;
|
|
+ int or = nv_encoder->or;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "or %d mode %d\n", or, mode);
|
|
+
|
|
+ /* wait for it to be done */
|
|
+ if (!nv_wait(NV50_PDISPLAY_DAC_DPMS_CTRL(or),
|
|
+ NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING, 0)) {
|
|
+ NV_ERROR(dev, "timeout: DAC_DPMS_CTRL_PENDING(%d) == 0\n", or);
|
|
+ NV_ERROR(dev, "DAC_DPMS_CTRL(%d) = 0x%08x\n", or,
|
|
+ nv_rd32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or)));
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ val = nv_rd32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or)) & ~0x7F;
|
|
+
|
|
+ if (mode != DRM_MODE_DPMS_ON)
|
|
+ val |= NV50_PDISPLAY_DAC_DPMS_CTRL_BLANKED;
|
|
+
|
|
+ switch (mode) {
|
|
+ case DRM_MODE_DPMS_STANDBY:
|
|
+ val |= NV50_PDISPLAY_DAC_DPMS_CTRL_HSYNC_OFF;
|
|
+ break;
|
|
+ case DRM_MODE_DPMS_SUSPEND:
|
|
+ val |= NV50_PDISPLAY_DAC_DPMS_CTRL_VSYNC_OFF;
|
|
+ break;
|
|
+ case DRM_MODE_DPMS_OFF:
|
|
+ val |= NV50_PDISPLAY_DAC_DPMS_CTRL_OFF;
|
|
+ val |= NV50_PDISPLAY_DAC_DPMS_CTRL_HSYNC_OFF;
|
|
+ val |= NV50_PDISPLAY_DAC_DPMS_CTRL_VSYNC_OFF;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ nv_wr32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or), val |
|
|
+ NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_dac_save(struct drm_encoder *encoder)
|
|
+{
|
|
+ NV_ERROR(encoder->dev, "!!\n");
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_dac_restore(struct drm_encoder *encoder)
|
|
+{
|
|
+ NV_ERROR(encoder->dev, "!!\n");
|
|
+}
|
|
+
|
|
+static bool
|
|
+nv50_dac_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
|
|
+ struct drm_display_mode *adjusted_mode)
|
|
+{
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ struct nouveau_connector *connector;
|
|
+
|
|
+ NV_DEBUG_KMS(encoder->dev, "or %d\n", nv_encoder->or);
|
|
+
|
|
+ connector = nouveau_encoder_connector_get(nv_encoder);
|
|
+ if (!connector) {
|
|
+ NV_ERROR(encoder->dev, "Encoder has no connector\n");
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (connector->scaling_mode != DRM_MODE_SCALE_NONE &&
|
|
+ connector->native_mode) {
|
|
+ int id = adjusted_mode->base.id;
|
|
+ *adjusted_mode = *connector->native_mode;
|
|
+ adjusted_mode->base.id = id;
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_dac_prepare(struct drm_encoder *encoder)
|
|
+{
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_dac_commit(struct drm_encoder *encoder)
|
|
+{
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_dac_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
|
|
+ struct drm_display_mode *adjusted_mode)
|
|
+{
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_channel *evo = dev_priv->evo;
|
|
+ struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc);
|
|
+ uint32_t mode_ctl = 0, mode_ctl2 = 0;
|
|
+ int ret;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "or %d\n", nv_encoder->or);
|
|
+
|
|
+ nv50_dac_dpms(encoder, DRM_MODE_DPMS_ON);
|
|
+
|
|
+ if (crtc->index == 1)
|
|
+ mode_ctl |= NV50_EVO_DAC_MODE_CTRL_CRTC1;
|
|
+ else
|
|
+ mode_ctl |= NV50_EVO_DAC_MODE_CTRL_CRTC0;
|
|
+
|
|
+ /* Lacking a working tv-out, this is not a 100% sure. */
|
|
+ if (nv_encoder->dcb->type == OUTPUT_ANALOG)
|
|
+ mode_ctl |= 0x40;
|
|
+ else
|
|
+ if (nv_encoder->dcb->type == OUTPUT_TV)
|
|
+ mode_ctl |= 0x100;
|
|
+
|
|
+ if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
|
|
+ mode_ctl2 |= NV50_EVO_DAC_MODE_CTRL2_NHSYNC;
|
|
+
|
|
+ if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
|
|
+ mode_ctl2 |= NV50_EVO_DAC_MODE_CTRL2_NVSYNC;
|
|
+
|
|
+ ret = RING_SPACE(evo, 3);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "no space while connecting DAC\n");
|
|
+ return;
|
|
+ }
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_DAC(nv_encoder->or, MODE_CTRL), 2);
|
|
+ OUT_RING(evo, mode_ctl);
|
|
+ OUT_RING(evo, mode_ctl2);
|
|
+}
|
|
+
|
|
+static const struct drm_encoder_helper_funcs nv50_dac_helper_funcs = {
|
|
+ .dpms = nv50_dac_dpms,
|
|
+ .save = nv50_dac_save,
|
|
+ .restore = nv50_dac_restore,
|
|
+ .mode_fixup = nv50_dac_mode_fixup,
|
|
+ .prepare = nv50_dac_prepare,
|
|
+ .commit = nv50_dac_commit,
|
|
+ .mode_set = nv50_dac_mode_set,
|
|
+ .detect = nv50_dac_detect
|
|
+};
|
|
+
|
|
+static void
|
|
+nv50_dac_destroy(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+
|
|
+ if (!encoder)
|
|
+ return;
|
|
+
|
|
+ NV_DEBUG_KMS(encoder->dev, "\n");
|
|
+
|
|
+ drm_encoder_cleanup(encoder);
|
|
+ kfree(nv_encoder);
|
|
+}
|
|
+
|
|
+static const struct drm_encoder_funcs nv50_dac_encoder_funcs = {
|
|
+ .destroy = nv50_dac_destroy,
|
|
+};
|
|
+
|
|
+int
|
|
+nv50_dac_create(struct drm_device *dev, struct dcb_entry *entry)
|
|
+{
|
|
+ struct nouveau_encoder *nv_encoder;
|
|
+ struct drm_encoder *encoder;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "\n");
|
|
+ NV_INFO(dev, "Detected a DAC output\n");
|
|
+
|
|
+ nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
|
|
+ if (!nv_encoder)
|
|
+ return -ENOMEM;
|
|
+ encoder = to_drm_encoder(nv_encoder);
|
|
+
|
|
+ nv_encoder->dcb = entry;
|
|
+ nv_encoder->or = ffs(entry->or) - 1;
|
|
+
|
|
+ nv_encoder->disconnect = nv50_dac_disconnect;
|
|
+
|
|
+ drm_encoder_init(dev, encoder, &nv50_dac_encoder_funcs,
|
|
+ DRM_MODE_ENCODER_DAC);
|
|
+ drm_encoder_helper_add(encoder, &nv50_dac_helper_funcs);
|
|
+
|
|
+ encoder->possible_crtcs = entry->heads;
|
|
+ encoder->possible_clones = 0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
|
|
new file mode 100644
|
|
index 0000000..90f0bf5
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv50_display.c
|
|
@@ -0,0 +1,1032 @@
|
|
+/*
|
|
+ * Copyright (C) 2008 Maarten Maathuis.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "nv50_display.h"
|
|
+#include "nouveau_crtc.h"
|
|
+#include "nouveau_encoder.h"
|
|
+#include "nouveau_connector.h"
|
|
+#include "nouveau_fb.h"
|
|
+#include "drm_crtc_helper.h"
|
|
+
|
|
+static void
|
|
+nv50_evo_channel_del(struct nouveau_channel **pchan)
|
|
+{
|
|
+ struct nouveau_channel *chan = *pchan;
|
|
+
|
|
+ if (!chan)
|
|
+ return;
|
|
+ *pchan = NULL;
|
|
+
|
|
+ nouveau_gpuobj_channel_takedown(chan);
|
|
+ nouveau_bo_ref(NULL, &chan->pushbuf_bo);
|
|
+
|
|
+ if (chan->user)
|
|
+ iounmap(chan->user);
|
|
+
|
|
+ kfree(chan);
|
|
+}
|
|
+
|
|
+static int
|
|
+nv50_evo_dmaobj_new(struct nouveau_channel *evo, uint32_t class, uint32_t name,
|
|
+ uint32_t tile_flags, uint32_t magic_flags,
|
|
+ uint32_t offset, uint32_t limit)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = evo->dev->dev_private;
|
|
+ struct drm_device *dev = evo->dev;
|
|
+ struct nouveau_gpuobj *obj = NULL;
|
|
+ int ret;
|
|
+
|
|
+ ret = nouveau_gpuobj_new(dev, evo, 6*4, 32, 0, &obj);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ obj->engine = NVOBJ_ENGINE_DISPLAY;
|
|
+
|
|
+ ret = nouveau_gpuobj_ref_add(dev, evo, name, obj, NULL);
|
|
+ if (ret) {
|
|
+ nouveau_gpuobj_del(dev, &obj);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ dev_priv->engine.instmem.prepare_access(dev, true);
|
|
+ nv_wo32(dev, obj, 0, (tile_flags << 22) | (magic_flags << 16) | class);
|
|
+ nv_wo32(dev, obj, 1, limit);
|
|
+ nv_wo32(dev, obj, 2, offset);
|
|
+ nv_wo32(dev, obj, 3, 0x00000000);
|
|
+ nv_wo32(dev, obj, 4, 0x00000000);
|
|
+ nv_wo32(dev, obj, 5, 0x00010000);
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+nv50_evo_channel_new(struct drm_device *dev, struct nouveau_channel **pchan)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_channel *chan;
|
|
+ int ret;
|
|
+
|
|
+ chan = kzalloc(sizeof(struct nouveau_channel), GFP_KERNEL);
|
|
+ if (!chan)
|
|
+ return -ENOMEM;
|
|
+ *pchan = chan;
|
|
+
|
|
+ chan->id = -1;
|
|
+ chan->dev = dev;
|
|
+ chan->user_get = 4;
|
|
+ chan->user_put = 0;
|
|
+
|
|
+ INIT_LIST_HEAD(&chan->ramht_refs);
|
|
+
|
|
+ ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 32768, 0x1000,
|
|
+ NVOBJ_FLAG_ZERO_ALLOC, &chan->ramin);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Error allocating EVO channel memory: %d\n", ret);
|
|
+ nv50_evo_channel_del(pchan);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = nouveau_mem_init_heap(&chan->ramin_heap, chan->ramin->gpuobj->
|
|
+ im_pramin->start, 32768);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Error initialising EVO PRAMIN heap: %d\n", ret);
|
|
+ nv50_evo_channel_del(pchan);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = nouveau_gpuobj_new_ref(dev, chan, chan, 0, 4096, 16,
|
|
+ 0, &chan->ramht);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Unable to allocate EVO RAMHT: %d\n", ret);
|
|
+ nv50_evo_channel_del(pchan);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ if (dev_priv->chipset != 0x50) {
|
|
+ ret = nv50_evo_dmaobj_new(chan, 0x3d, NvEvoFB16, 0x70, 0x19,
|
|
+ 0, 0xffffffff);
|
|
+ if (ret) {
|
|
+ nv50_evo_channel_del(pchan);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+
|
|
+ ret = nv50_evo_dmaobj_new(chan, 0x3d, NvEvoFB32, 0x7a, 0x19,
|
|
+ 0, 0xffffffff);
|
|
+ if (ret) {
|
|
+ nv50_evo_channel_del(pchan);
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ret = nv50_evo_dmaobj_new(chan, 0x3d, NvEvoVRAM, 0, 0x19,
|
|
+ 0, nouveau_mem_fb_amount(dev));
|
|
+ if (ret) {
|
|
+ nv50_evo_channel_del(pchan);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = nouveau_bo_new(dev, NULL, 4096, 0, TTM_PL_FLAG_VRAM, 0, 0,
|
|
+ false, true, &chan->pushbuf_bo);
|
|
+ if (ret == 0)
|
|
+ ret = nouveau_bo_pin(chan->pushbuf_bo, TTM_PL_FLAG_VRAM);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Error creating EVO DMA push buffer: %d\n", ret);
|
|
+ nv50_evo_channel_del(pchan);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = nouveau_bo_map(chan->pushbuf_bo);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Error mapping EVO DMA push buffer: %d\n", ret);
|
|
+ nv50_evo_channel_del(pchan);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ chan->user = ioremap(pci_resource_start(dev->pdev, 0) +
|
|
+ NV50_PDISPLAY_USER(0), PAGE_SIZE);
|
|
+ if (!chan->user) {
|
|
+ NV_ERROR(dev, "Error mapping EVO control regs.\n");
|
|
+ nv50_evo_channel_del(pchan);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nv50_display_init(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
|
|
+ struct nouveau_channel *evo = dev_priv->evo;
|
|
+ struct drm_connector *connector;
|
|
+ uint32_t val, ram_amount, hpd_en[2];
|
|
+ uint64_t start;
|
|
+ int ret, i;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "\n");
|
|
+
|
|
+ nv_wr32(dev, 0x00610184, nv_rd32(dev, 0x00614004));
|
|
+ /*
|
|
+ * I think the 0x006101XX range is some kind of main control area
|
|
+ * that enables things.
|
|
+ */
|
|
+ /* CRTC? */
|
|
+ for (i = 0; i < 2; i++) {
|
|
+ val = nv_rd32(dev, 0x00616100 + (i * 0x800));
|
|
+ nv_wr32(dev, 0x00610190 + (i * 0x10), val);
|
|
+ val = nv_rd32(dev, 0x00616104 + (i * 0x800));
|
|
+ nv_wr32(dev, 0x00610194 + (i * 0x10), val);
|
|
+ val = nv_rd32(dev, 0x00616108 + (i * 0x800));
|
|
+ nv_wr32(dev, 0x00610198 + (i * 0x10), val);
|
|
+ val = nv_rd32(dev, 0x0061610c + (i * 0x800));
|
|
+ nv_wr32(dev, 0x0061019c + (i * 0x10), val);
|
|
+ }
|
|
+ /* DAC */
|
|
+ for (i = 0; i < 3; i++) {
|
|
+ val = nv_rd32(dev, 0x0061a000 + (i * 0x800));
|
|
+ nv_wr32(dev, 0x006101d0 + (i * 0x04), val);
|
|
+ }
|
|
+ /* SOR */
|
|
+ for (i = 0; i < 4; i++) {
|
|
+ val = nv_rd32(dev, 0x0061c000 + (i * 0x800));
|
|
+ nv_wr32(dev, 0x006101e0 + (i * 0x04), val);
|
|
+ }
|
|
+ /* Something not yet in use, tv-out maybe. */
|
|
+ for (i = 0; i < 3; i++) {
|
|
+ val = nv_rd32(dev, 0x0061e000 + (i * 0x800));
|
|
+ nv_wr32(dev, 0x006101f0 + (i * 0x04), val);
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < 3; i++) {
|
|
+ nv_wr32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(i), 0x00550000 |
|
|
+ NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING);
|
|
+ nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL1(i), 0x00000001);
|
|
+ }
|
|
+
|
|
+ /* This used to be in crtc unblank, but seems out of place there. */
|
|
+ nv_wr32(dev, NV50_PDISPLAY_UNK_380, 0);
|
|
+ /* RAM is clamped to 256 MiB. */
|
|
+ ram_amount = nouveau_mem_fb_amount(dev);
|
|
+ NV_DEBUG_KMS(dev, "ram_amount %d\n", ram_amount);
|
|
+ if (ram_amount > 256*1024*1024)
|
|
+ ram_amount = 256*1024*1024;
|
|
+ nv_wr32(dev, NV50_PDISPLAY_RAM_AMOUNT, ram_amount - 1);
|
|
+ nv_wr32(dev, NV50_PDISPLAY_UNK_388, 0x150000);
|
|
+ nv_wr32(dev, NV50_PDISPLAY_UNK_38C, 0);
|
|
+
|
|
+ /* The precise purpose is unknown, i suspect it has something to do
|
|
+ * with text mode.
|
|
+ */
|
|
+ if (nv_rd32(dev, NV50_PDISPLAY_INTR_1) & 0x100) {
|
|
+ nv_wr32(dev, NV50_PDISPLAY_INTR_1, 0x100);
|
|
+ nv_wr32(dev, 0x006194e8, nv_rd32(dev, 0x006194e8) & ~1);
|
|
+ if (!nv_wait(0x006194e8, 2, 0)) {
|
|
+ NV_ERROR(dev, "timeout: (0x6194e8 & 2) != 0\n");
|
|
+ NV_ERROR(dev, "0x6194e8 = 0x%08x\n",
|
|
+ nv_rd32(dev, 0x6194e8));
|
|
+ return -EBUSY;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* taken from nv bug #12637, attempts to un-wedge the hw if it's
|
|
+ * stuck in some unspecified state
|
|
+ */
|
|
+ start = ptimer->read(dev);
|
|
+ nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0), 0x2b00);
|
|
+ while ((val = nv_rd32(dev, NV50_PDISPLAY_CHANNEL_STAT(0))) & 0x1e0000) {
|
|
+ if ((val & 0x9f0000) == 0x20000)
|
|
+ nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0),
|
|
+ val | 0x800000);
|
|
+
|
|
+ if ((val & 0x3f0000) == 0x30000)
|
|
+ nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0),
|
|
+ val | 0x200000);
|
|
+
|
|
+ if (ptimer->read(dev) - start > 1000000000ULL) {
|
|
+ NV_ERROR(dev, "timeout: (0x610200 & 0x1e0000) != 0\n");
|
|
+ NV_ERROR(dev, "0x610200 = 0x%08x\n", val);
|
|
+ return -EBUSY;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ nv_wr32(dev, NV50_PDISPLAY_CTRL_STATE, NV50_PDISPLAY_CTRL_STATE_ENABLE);
|
|
+ nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0), 0x1000b03);
|
|
+ if (!nv_wait(NV50_PDISPLAY_CHANNEL_STAT(0), 0x40000000, 0x40000000)) {
|
|
+ NV_ERROR(dev, "timeout: (0x610200 & 0x40000000) == 0x40000000\n");
|
|
+ NV_ERROR(dev, "0x610200 = 0x%08x\n",
|
|
+ nv_rd32(dev, NV50_PDISPLAY_CHANNEL_STAT(0)));
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < 2; i++) {
|
|
+ nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), 0x2000);
|
|
+ if (!nv_wait(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i),
|
|
+ NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, 0)) {
|
|
+ NV_ERROR(dev, "timeout: CURSOR_CTRL2_STATUS == 0\n");
|
|
+ NV_ERROR(dev, "CURSOR_CTRL2 = 0x%08x\n",
|
|
+ nv_rd32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i)));
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i),
|
|
+ NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_ON);
|
|
+ if (!nv_wait(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i),
|
|
+ NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS,
|
|
+ NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_ACTIVE)) {
|
|
+ NV_ERROR(dev, "timeout: "
|
|
+ "CURSOR_CTRL2_STATUS_ACTIVE(%d)\n", i);
|
|
+ NV_ERROR(dev, "CURSOR_CTRL2(%d) = 0x%08x\n", i,
|
|
+ nv_rd32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i)));
|
|
+ return -EBUSY;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ nv_wr32(dev, NV50_PDISPLAY_OBJECTS, (evo->ramin->instance >> 8) | 9);
|
|
+
|
|
+ /* initialise fifo */
|
|
+ nv_wr32(dev, NV50_PDISPLAY_CHANNEL_DMA_CB(0),
|
|
+ ((evo->pushbuf_bo->bo.mem.mm_node->start << PAGE_SHIFT) >> 8) |
|
|
+ NV50_PDISPLAY_CHANNEL_DMA_CB_LOCATION_VRAM |
|
|
+ NV50_PDISPLAY_CHANNEL_DMA_CB_VALID);
|
|
+ nv_wr32(dev, NV50_PDISPLAY_CHANNEL_UNK2(0), 0x00010000);
|
|
+ nv_wr32(dev, NV50_PDISPLAY_CHANNEL_UNK3(0), 0x00000002);
|
|
+ if (!nv_wait(0x610200, 0x80000000, 0x00000000)) {
|
|
+ NV_ERROR(dev, "timeout: (0x610200 & 0x80000000) == 0\n");
|
|
+ NV_ERROR(dev, "0x610200 = 0x%08x\n", nv_rd32(dev, 0x610200));
|
|
+ return -EBUSY;
|
|
+ }
|
|
+ nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0),
|
|
+ (nv_rd32(dev, NV50_PDISPLAY_CHANNEL_STAT(0)) & ~0x00000003) |
|
|
+ NV50_PDISPLAY_CHANNEL_STAT_DMA_ENABLED);
|
|
+ nv_wr32(dev, NV50_PDISPLAY_USER_PUT(0), 0);
|
|
+ nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0), 0x01000003 |
|
|
+ NV50_PDISPLAY_CHANNEL_STAT_DMA_ENABLED);
|
|
+ nv_wr32(dev, 0x610300, nv_rd32(dev, 0x610300) & ~1);
|
|
+
|
|
+ evo->dma.max = (4096/4) - 2;
|
|
+ evo->dma.put = 0;
|
|
+ evo->dma.cur = evo->dma.put;
|
|
+ evo->dma.free = evo->dma.max - evo->dma.cur;
|
|
+
|
|
+ ret = RING_SPACE(evo, NOUVEAU_DMA_SKIPS);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ for (i = 0; i < NOUVEAU_DMA_SKIPS; i++)
|
|
+ OUT_RING(evo, 0);
|
|
+
|
|
+ ret = RING_SPACE(evo, 11);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_UNK84, 2);
|
|
+ OUT_RING(evo, NV50_EVO_UNK84_NOTIFY_DISABLED);
|
|
+ OUT_RING(evo, NV50_EVO_DMA_NOTIFY_HANDLE_NONE);
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, FB_DMA), 1);
|
|
+ OUT_RING(evo, NV50_EVO_CRTC_FB_DMA_HANDLE_NONE);
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK0800), 1);
|
|
+ OUT_RING(evo, 0);
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, DISPLAY_START), 1);
|
|
+ OUT_RING(evo, 0);
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK082C), 1);
|
|
+ OUT_RING(evo, 0);
|
|
+ FIRE_RING(evo);
|
|
+ if (!nv_wait(0x640004, 0xffffffff, evo->dma.put << 2))
|
|
+ NV_ERROR(dev, "evo pushbuf stalled\n");
|
|
+
|
|
+ /* enable clock change interrupts. */
|
|
+ nv_wr32(dev, 0x610028, 0x00010001);
|
|
+ nv_wr32(dev, NV50_PDISPLAY_INTR_EN, (NV50_PDISPLAY_INTR_EN_CLK_UNK10 |
|
|
+ NV50_PDISPLAY_INTR_EN_CLK_UNK20 |
|
|
+ NV50_PDISPLAY_INTR_EN_CLK_UNK40));
|
|
+
|
|
+ /* enable hotplug interrupts */
|
|
+ hpd_en[0] = hpd_en[1] = 0;
|
|
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
|
+ struct nouveau_connector *conn = nouveau_connector(connector);
|
|
+ struct dcb_gpio_entry *gpio;
|
|
+
|
|
+ if (connector->connector_type != DRM_MODE_CONNECTOR_DVII &&
|
|
+ connector->connector_type != DRM_MODE_CONNECTOR_DVID &&
|
|
+ connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort)
|
|
+ continue;
|
|
+
|
|
+ gpio = nouveau_bios_gpio_entry(dev, conn->dcb->gpio_tag);
|
|
+ if (!gpio)
|
|
+ continue;
|
|
+
|
|
+ hpd_en[gpio->line >> 4] |= (0x00010001 << (gpio->line & 0xf));
|
|
+ }
|
|
+
|
|
+ nv_wr32(dev, 0xe054, 0xffffffff);
|
|
+ nv_wr32(dev, 0xe050, hpd_en[0]);
|
|
+ if (dev_priv->chipset >= 0x90) {
|
|
+ nv_wr32(dev, 0xe074, 0xffffffff);
|
|
+ nv_wr32(dev, 0xe070, hpd_en[1]);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int nv50_display_disable(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct drm_crtc *drm_crtc;
|
|
+ int ret, i;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "\n");
|
|
+
|
|
+ list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) {
|
|
+ struct nouveau_crtc *crtc = nouveau_crtc(drm_crtc);
|
|
+
|
|
+ nv50_crtc_blank(crtc, true);
|
|
+ }
|
|
+
|
|
+ ret = RING_SPACE(dev_priv->evo, 2);
|
|
+ if (ret == 0) {
|
|
+ BEGIN_RING(dev_priv->evo, 0, NV50_EVO_UPDATE, 1);
|
|
+ OUT_RING(dev_priv->evo, 0);
|
|
+ }
|
|
+ FIRE_RING(dev_priv->evo);
|
|
+
|
|
+ /* Almost like ack'ing a vblank interrupt, maybe in the spirit of
|
|
+ * cleaning up?
|
|
+ */
|
|
+ list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) {
|
|
+ struct nouveau_crtc *crtc = nouveau_crtc(drm_crtc);
|
|
+ uint32_t mask = NV50_PDISPLAY_INTR_1_VBLANK_CRTC_(crtc->index);
|
|
+
|
|
+ if (!crtc->base.enabled)
|
|
+ continue;
|
|
+
|
|
+ nv_wr32(dev, NV50_PDISPLAY_INTR_1, mask);
|
|
+ if (!nv_wait(NV50_PDISPLAY_INTR_1, mask, mask)) {
|
|
+ NV_ERROR(dev, "timeout: (0x610024 & 0x%08x) == "
|
|
+ "0x%08x\n", mask, mask);
|
|
+ NV_ERROR(dev, "0x610024 = 0x%08x\n",
|
|
+ nv_rd32(dev, NV50_PDISPLAY_INTR_1));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0), 0);
|
|
+ nv_wr32(dev, NV50_PDISPLAY_CTRL_STATE, 0);
|
|
+ if (!nv_wait(NV50_PDISPLAY_CHANNEL_STAT(0), 0x1e0000, 0)) {
|
|
+ NV_ERROR(dev, "timeout: (0x610200 & 0x1e0000) == 0\n");
|
|
+ NV_ERROR(dev, "0x610200 = 0x%08x\n",
|
|
+ nv_rd32(dev, NV50_PDISPLAY_CHANNEL_STAT(0)));
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < 3; i++) {
|
|
+ if (!nv_wait(NV50_PDISPLAY_SOR_DPMS_STATE(i),
|
|
+ NV50_PDISPLAY_SOR_DPMS_STATE_WAIT, 0)) {
|
|
+ NV_ERROR(dev, "timeout: SOR_DPMS_STATE_WAIT(%d) == 0\n", i);
|
|
+ NV_ERROR(dev, "SOR_DPMS_STATE(%d) = 0x%08x\n", i,
|
|
+ nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_STATE(i)));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* disable interrupts. */
|
|
+ nv_wr32(dev, NV50_PDISPLAY_INTR_EN, 0x00000000);
|
|
+
|
|
+ /* disable hotplug interrupts */
|
|
+ nv_wr32(dev, 0xe054, 0xffffffff);
|
|
+ nv_wr32(dev, 0xe050, 0x00000000);
|
|
+ if (dev_priv->chipset >= 0x90) {
|
|
+ nv_wr32(dev, 0xe074, 0xffffffff);
|
|
+ nv_wr32(dev, 0xe070, 0x00000000);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int nv50_display_create(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct parsed_dcb *dcb = dev_priv->vbios->dcb;
|
|
+ uint32_t connector[16] = {};
|
|
+ int ret, i;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "\n");
|
|
+
|
|
+ /* init basic kernel modesetting */
|
|
+ drm_mode_config_init(dev);
|
|
+
|
|
+ /* Initialise some optional connector properties. */
|
|
+ drm_mode_create_scaling_mode_property(dev);
|
|
+ drm_mode_create_dithering_property(dev);
|
|
+
|
|
+ dev->mode_config.min_width = 0;
|
|
+ dev->mode_config.min_height = 0;
|
|
+
|
|
+ dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs;
|
|
+
|
|
+ dev->mode_config.max_width = 8192;
|
|
+ dev->mode_config.max_height = 8192;
|
|
+
|
|
+ dev->mode_config.fb_base = dev_priv->fb_phys;
|
|
+
|
|
+ /* Create EVO channel */
|
|
+ ret = nv50_evo_channel_new(dev, &dev_priv->evo);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Error creating EVO channel: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /* Create CRTC objects */
|
|
+ for (i = 0; i < 2; i++)
|
|
+ nv50_crtc_create(dev, i);
|
|
+
|
|
+ /* We setup the encoders from the BIOS table */
|
|
+ for (i = 0 ; i < dcb->entries; i++) {
|
|
+ struct dcb_entry *entry = &dcb->entry[i];
|
|
+
|
|
+ if (entry->location != DCB_LOC_ON_CHIP) {
|
|
+ NV_WARN(dev, "Off-chip encoder %d/%d unsupported\n",
|
|
+ entry->type, ffs(entry->or) - 1);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ switch (entry->type) {
|
|
+ case OUTPUT_TMDS:
|
|
+ case OUTPUT_LVDS:
|
|
+ case OUTPUT_DP:
|
|
+ nv50_sor_create(dev, entry);
|
|
+ break;
|
|
+ case OUTPUT_ANALOG:
|
|
+ nv50_dac_create(dev, entry);
|
|
+ break;
|
|
+ default:
|
|
+ NV_WARN(dev, "DCB encoder %d unknown\n", entry->type);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ connector[entry->connector] |= (1 << entry->type);
|
|
+ }
|
|
+
|
|
+ /* It appears that DCB 3.0+ VBIOS has a connector table, however,
|
|
+ * I'm not 100% certain how to decode it correctly yet so just
|
|
+ * look at what encoders are present on each connector index and
|
|
+ * attempt to derive the connector type from that.
|
|
+ */
|
|
+ for (i = 0 ; i < dcb->entries; i++) {
|
|
+ struct dcb_entry *entry = &dcb->entry[i];
|
|
+ uint16_t encoders;
|
|
+ int type;
|
|
+
|
|
+ encoders = connector[entry->connector];
|
|
+ if (!(encoders & (1 << entry->type)))
|
|
+ continue;
|
|
+ connector[entry->connector] = 0;
|
|
+
|
|
+ if (encoders & (1 << OUTPUT_DP)) {
|
|
+ type = DRM_MODE_CONNECTOR_DisplayPort;
|
|
+ } else if (encoders & (1 << OUTPUT_TMDS)) {
|
|
+ if (encoders & (1 << OUTPUT_ANALOG))
|
|
+ type = DRM_MODE_CONNECTOR_DVII;
|
|
+ else
|
|
+ type = DRM_MODE_CONNECTOR_DVID;
|
|
+ } else if (encoders & (1 << OUTPUT_ANALOG)) {
|
|
+ type = DRM_MODE_CONNECTOR_VGA;
|
|
+ } else if (encoders & (1 << OUTPUT_LVDS)) {
|
|
+ type = DRM_MODE_CONNECTOR_LVDS;
|
|
+ } else {
|
|
+ type = DRM_MODE_CONNECTOR_Unknown;
|
|
+ }
|
|
+
|
|
+ if (type == DRM_MODE_CONNECTOR_Unknown)
|
|
+ continue;
|
|
+
|
|
+ nouveau_connector_create(dev, entry->connector, type);
|
|
+ }
|
|
+
|
|
+ ret = nv50_display_init(dev);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int nv50_display_destroy(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "\n");
|
|
+
|
|
+ drm_mode_config_cleanup(dev);
|
|
+
|
|
+ nv50_display_disable(dev);
|
|
+ nv50_evo_channel_del(&dev_priv->evo);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline uint32_t
|
|
+nv50_display_mode_ctrl(struct drm_device *dev, bool sor, int or)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t mc;
|
|
+
|
|
+ if (sor) {
|
|
+ if (dev_priv->chipset < 0x90 ||
|
|
+ dev_priv->chipset == 0x92 || dev_priv->chipset == 0xa0)
|
|
+ mc = nv_rd32(dev, NV50_PDISPLAY_SOR_MODE_CTRL_P(or));
|
|
+ else
|
|
+ mc = nv_rd32(dev, NV90_PDISPLAY_SOR_MODE_CTRL_P(or));
|
|
+ } else {
|
|
+ mc = nv_rd32(dev, NV50_PDISPLAY_DAC_MODE_CTRL_P(or));
|
|
+ }
|
|
+
|
|
+ return mc;
|
|
+}
|
|
+
|
|
+static int
|
|
+nv50_display_irq_head(struct drm_device *dev, int *phead,
|
|
+ struct dcb_entry **pdcbent)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t unk30 = nv_rd32(dev, NV50_PDISPLAY_UNK30_CTRL);
|
|
+ uint32_t dac = 0, sor = 0;
|
|
+ int head, i, or = 0, type = OUTPUT_ANY;
|
|
+
|
|
+ /* We're assuming that head 0 *or* head 1 will be active here,
|
|
+ * and not both. I'm not sure if the hw will even signal both
|
|
+ * ever, but it definitely shouldn't for us as we commit each
|
|
+ * CRTC separately, and submission will be blocked by the GPU
|
|
+ * until we handle each in turn.
|
|
+ */
|
|
+ NV_DEBUG_KMS(dev, "0x610030: 0x%08x\n", unk30);
|
|
+ head = ffs((unk30 >> 9) & 3) - 1;
|
|
+ if (head < 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* This assumes CRTCs are never bound to multiple encoders, which
|
|
+ * should be the case.
|
|
+ */
|
|
+ for (i = 0; i < 3 && type == OUTPUT_ANY; i++) {
|
|
+ uint32_t mc = nv50_display_mode_ctrl(dev, false, i);
|
|
+ if (!(mc & (1 << head)))
|
|
+ continue;
|
|
+
|
|
+ switch ((mc >> 8) & 0xf) {
|
|
+ case 0: type = OUTPUT_ANALOG; break;
|
|
+ case 1: type = OUTPUT_TV; break;
|
|
+ default:
|
|
+ NV_ERROR(dev, "unknown dac mode_ctrl: 0x%08x\n", dac);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ or = i;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < 4 && type == OUTPUT_ANY; i++) {
|
|
+ uint32_t mc = nv50_display_mode_ctrl(dev, true, i);
|
|
+ if (!(mc & (1 << head)))
|
|
+ continue;
|
|
+
|
|
+ switch ((mc >> 8) & 0xf) {
|
|
+ case 0: type = OUTPUT_LVDS; break;
|
|
+ case 1: type = OUTPUT_TMDS; break;
|
|
+ case 2: type = OUTPUT_TMDS; break;
|
|
+ case 5: type = OUTPUT_TMDS; break;
|
|
+ case 8: type = OUTPUT_DP; break;
|
|
+ case 9: type = OUTPUT_DP; break;
|
|
+ default:
|
|
+ NV_ERROR(dev, "unknown sor mode_ctrl: 0x%08x\n", sor);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ or = i;
|
|
+ }
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "type %d, or %d\n", type, or);
|
|
+ if (type == OUTPUT_ANY) {
|
|
+ NV_ERROR(dev, "unknown encoder!!\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < dev_priv->vbios->dcb->entries; i++) {
|
|
+ struct dcb_entry *dcbent = &dev_priv->vbios->dcb->entry[i];
|
|
+
|
|
+ if (dcbent->type != type)
|
|
+ continue;
|
|
+
|
|
+ if (!(dcbent->or & (1 << or)))
|
|
+ continue;
|
|
+
|
|
+ *phead = head;
|
|
+ *pdcbent = dcbent;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ NV_ERROR(dev, "no DCB entry for %d %d\n", dac != 0, or);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static uint32_t
|
|
+nv50_display_script_select(struct drm_device *dev, struct dcb_entry *dcbent,
|
|
+ int pxclk)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_connector *nv_connector = NULL;
|
|
+ struct drm_encoder *encoder;
|
|
+ struct nvbios *bios = &dev_priv->VBIOS;
|
|
+ uint32_t mc, script = 0, or;
|
|
+
|
|
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+
|
|
+ if (nv_encoder->dcb != dcbent)
|
|
+ continue;
|
|
+
|
|
+ nv_connector = nouveau_encoder_connector_get(nv_encoder);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ or = ffs(dcbent->or) - 1;
|
|
+ mc = nv50_display_mode_ctrl(dev, dcbent->type != OUTPUT_ANALOG, or);
|
|
+ switch (dcbent->type) {
|
|
+ case OUTPUT_LVDS:
|
|
+ script = (mc >> 8) & 0xf;
|
|
+ if (bios->pub.fp_no_ddc) {
|
|
+ if (bios->fp.dual_link)
|
|
+ script |= 0x0100;
|
|
+ if (bios->fp.if_is_24bit)
|
|
+ script |= 0x0200;
|
|
+ } else {
|
|
+ if (pxclk >= bios->fp.duallink_transition_clk) {
|
|
+ script |= 0x0100;
|
|
+ if (bios->fp.strapless_is_24bit & 2)
|
|
+ script |= 0x0200;
|
|
+ } else
|
|
+ if (bios->fp.strapless_is_24bit & 1)
|
|
+ script |= 0x0200;
|
|
+
|
|
+ if (nv_connector && nv_connector->edid &&
|
|
+ (nv_connector->edid->revision >= 4) &&
|
|
+ (nv_connector->edid->input & 0x70) >= 0x20)
|
|
+ script |= 0x0200;
|
|
+ }
|
|
+
|
|
+ if (nouveau_uscript_lvds >= 0) {
|
|
+ NV_INFO(dev, "override script 0x%04x with 0x%04x "
|
|
+ "for output LVDS-%d\n", script,
|
|
+ nouveau_uscript_lvds, or);
|
|
+ script = nouveau_uscript_lvds;
|
|
+ }
|
|
+ break;
|
|
+ case OUTPUT_TMDS:
|
|
+ script = (mc >> 8) & 0xf;
|
|
+ if (pxclk >= 165000)
|
|
+ script |= 0x0100;
|
|
+
|
|
+ if (nouveau_uscript_tmds >= 0) {
|
|
+ NV_INFO(dev, "override script 0x%04x with 0x%04x "
|
|
+ "for output TMDS-%d\n", script,
|
|
+ nouveau_uscript_tmds, or);
|
|
+ script = nouveau_uscript_tmds;
|
|
+ }
|
|
+ break;
|
|
+ case OUTPUT_DP:
|
|
+ script = (mc >> 8) & 0xf;
|
|
+ break;
|
|
+ case OUTPUT_ANALOG:
|
|
+ script = 0xff;
|
|
+ break;
|
|
+ default:
|
|
+ NV_ERROR(dev, "modeset on unsupported output type!\n");
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return script;
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_display_vblank_crtc_handler(struct drm_device *dev, int crtc)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_channel *chan;
|
|
+ struct list_head *entry, *tmp;
|
|
+
|
|
+ list_for_each_safe(entry, tmp, &dev_priv->vbl_waiting) {
|
|
+ chan = list_entry(entry, struct nouveau_channel, nvsw.vbl_wait);
|
|
+
|
|
+ nouveau_bo_wr32(chan->notifier_bo, chan->nvsw.vblsem_offset,
|
|
+ chan->nvsw.vblsem_rval);
|
|
+ list_del(&chan->nvsw.vbl_wait);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_display_vblank_handler(struct drm_device *dev, uint32_t intr)
|
|
+{
|
|
+ intr &= NV50_PDISPLAY_INTR_1_VBLANK_CRTC;
|
|
+
|
|
+ if (intr & NV50_PDISPLAY_INTR_1_VBLANK_CRTC_0)
|
|
+ nv50_display_vblank_crtc_handler(dev, 0);
|
|
+
|
|
+ if (intr & NV50_PDISPLAY_INTR_1_VBLANK_CRTC_1)
|
|
+ nv50_display_vblank_crtc_handler(dev, 1);
|
|
+
|
|
+ nv_wr32(dev, NV50_PDISPLAY_INTR_EN, nv_rd32(dev,
|
|
+ NV50_PDISPLAY_INTR_EN) & ~intr);
|
|
+ nv_wr32(dev, NV50_PDISPLAY_INTR_1, intr);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_display_unk10_handler(struct drm_device *dev)
|
|
+{
|
|
+ struct dcb_entry *dcbent;
|
|
+ int head, ret;
|
|
+
|
|
+ ret = nv50_display_irq_head(dev, &head, &dcbent);
|
|
+ if (ret)
|
|
+ goto ack;
|
|
+
|
|
+ nv_wr32(dev, 0x619494, nv_rd32(dev, 0x619494) & ~8);
|
|
+
|
|
+ nouveau_bios_run_display_table(dev, dcbent, 0, -1);
|
|
+
|
|
+ack:
|
|
+ nv_wr32(dev, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK10);
|
|
+ nv_wr32(dev, 0x610030, 0x80000000);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_display_unk20_handler(struct drm_device *dev)
|
|
+{
|
|
+ struct dcb_entry *dcbent;
|
|
+ uint32_t tmp, pclk, script;
|
|
+ int head, or, ret;
|
|
+
|
|
+ ret = nv50_display_irq_head(dev, &head, &dcbent);
|
|
+ if (ret)
|
|
+ goto ack;
|
|
+ or = ffs(dcbent->or) - 1;
|
|
+ pclk = nv_rd32(dev, NV50_PDISPLAY_CRTC_P(head, CLOCK)) & 0x3fffff;
|
|
+ script = nv50_display_script_select(dev, dcbent, pclk);
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "head %d pxclk: %dKHz\n", head, pclk);
|
|
+
|
|
+ if (dcbent->type != OUTPUT_DP)
|
|
+ nouveau_bios_run_display_table(dev, dcbent, 0, -2);
|
|
+
|
|
+ nv50_crtc_set_clock(dev, head, pclk);
|
|
+
|
|
+ nouveau_bios_run_display_table(dev, dcbent, script, pclk);
|
|
+
|
|
+ tmp = nv_rd32(dev, NV50_PDISPLAY_CRTC_CLK_CTRL2(head));
|
|
+ tmp &= ~0x000000f;
|
|
+ nv_wr32(dev, NV50_PDISPLAY_CRTC_CLK_CTRL2(head), tmp);
|
|
+
|
|
+ if (dcbent->type != OUTPUT_ANALOG) {
|
|
+ tmp = nv_rd32(dev, NV50_PDISPLAY_SOR_CLK_CTRL2(or));
|
|
+ tmp &= ~0x00000f0f;
|
|
+ if (script & 0x0100)
|
|
+ tmp |= 0x00000101;
|
|
+ nv_wr32(dev, NV50_PDISPLAY_SOR_CLK_CTRL2(or), tmp);
|
|
+ } else {
|
|
+ nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL2(or), 0);
|
|
+ }
|
|
+
|
|
+ack:
|
|
+ nv_wr32(dev, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK20);
|
|
+ nv_wr32(dev, 0x610030, 0x80000000);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_display_unk40_handler(struct drm_device *dev)
|
|
+{
|
|
+ struct dcb_entry *dcbent;
|
|
+ int head, pclk, script, ret;
|
|
+
|
|
+ ret = nv50_display_irq_head(dev, &head, &dcbent);
|
|
+ if (ret)
|
|
+ goto ack;
|
|
+ pclk = nv_rd32(dev, NV50_PDISPLAY_CRTC_P(head, CLOCK)) & 0x3fffff;
|
|
+ script = nv50_display_script_select(dev, dcbent, pclk);
|
|
+
|
|
+ nouveau_bios_run_display_table(dev, dcbent, script, -pclk);
|
|
+
|
|
+ack:
|
|
+ nv_wr32(dev, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK40);
|
|
+ nv_wr32(dev, 0x610030, 0x80000000);
|
|
+ nv_wr32(dev, 0x619494, nv_rd32(dev, 0x619494) | 8);
|
|
+}
|
|
+
|
|
+void
|
|
+nv50_display_irq_handler_bh(struct work_struct *work)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv =
|
|
+ container_of(work, struct drm_nouveau_private, irq_work);
|
|
+ struct drm_device *dev = dev_priv->dev;
|
|
+
|
|
+ for (;;) {
|
|
+ uint32_t intr0 = nv_rd32(dev, NV50_PDISPLAY_INTR_0);
|
|
+ uint32_t intr1 = nv_rd32(dev, NV50_PDISPLAY_INTR_1);
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "PDISPLAY_INTR_BH 0x%08x 0x%08x\n", intr0, intr1);
|
|
+
|
|
+ if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK10)
|
|
+ nv50_display_unk10_handler(dev);
|
|
+ else
|
|
+ if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK20)
|
|
+ nv50_display_unk20_handler(dev);
|
|
+ else
|
|
+ if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK40)
|
|
+ nv50_display_unk40_handler(dev);
|
|
+ else
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ nv_wr32(dev, NV03_PMC_INTR_EN_0, 1);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_display_error_handler(struct drm_device *dev)
|
|
+{
|
|
+ uint32_t addr, data;
|
|
+
|
|
+ nv_wr32(dev, NV50_PDISPLAY_INTR_0, 0x00010000);
|
|
+ addr = nv_rd32(dev, NV50_PDISPLAY_TRAPPED_ADDR);
|
|
+ data = nv_rd32(dev, NV50_PDISPLAY_TRAPPED_DATA);
|
|
+
|
|
+ NV_ERROR(dev, "EvoCh %d Mthd 0x%04x Data 0x%08x (0x%04x 0x%02x)\n",
|
|
+ 0, addr & 0xffc, data, addr >> 16, (addr >> 12) & 0xf);
|
|
+
|
|
+ nv_wr32(dev, NV50_PDISPLAY_TRAPPED_ADDR, 0x90000000);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_display_irq_hotplug(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct drm_connector *connector;
|
|
+ const uint32_t gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 };
|
|
+ uint32_t unplug_mask, plug_mask, change_mask;
|
|
+ uint32_t hpd0, hpd1 = 0;
|
|
+
|
|
+ hpd0 = nv_rd32(dev, 0xe054) & nv_rd32(dev, 0xe050);
|
|
+ if (dev_priv->chipset >= 0x90)
|
|
+ hpd1 = nv_rd32(dev, 0xe074) & nv_rd32(dev, 0xe070);
|
|
+
|
|
+ plug_mask = (hpd0 & 0x0000ffff) | (hpd1 << 16);
|
|
+ unplug_mask = (hpd0 >> 16) | (hpd1 & 0xffff0000);
|
|
+ change_mask = plug_mask | unplug_mask;
|
|
+
|
|
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
|
+ struct drm_encoder_helper_funcs *helper;
|
|
+ struct nouveau_connector *nv_connector =
|
|
+ nouveau_connector(connector);
|
|
+ struct nouveau_encoder *nv_encoder;
|
|
+ struct dcb_gpio_entry *gpio;
|
|
+ uint32_t reg;
|
|
+ bool plugged;
|
|
+
|
|
+ if (!nv_connector->dcb)
|
|
+ continue;
|
|
+
|
|
+ gpio = nouveau_bios_gpio_entry(dev, nv_connector->dcb->gpio_tag);
|
|
+ if (!gpio || !(change_mask & (1 << gpio->line)))
|
|
+ continue;
|
|
+
|
|
+ reg = nv_rd32(dev, gpio_reg[gpio->line >> 3]);
|
|
+ plugged = !!(reg & (4 << ((gpio->line & 7) << 2)));
|
|
+ NV_INFO(dev, "%splugged %s\n", plugged ? "" : "un",
|
|
+ drm_get_connector_name(connector)) ;
|
|
+
|
|
+ if (!connector->encoder || !connector->encoder->crtc ||
|
|
+ !connector->encoder->crtc->enabled)
|
|
+ continue;
|
|
+ nv_encoder = nouveau_encoder(connector->encoder);
|
|
+ helper = connector->encoder->helper_private;
|
|
+
|
|
+ if (nv_encoder->dcb->type != OUTPUT_DP)
|
|
+ continue;
|
|
+
|
|
+ if (plugged)
|
|
+ helper->dpms(connector->encoder, DRM_MODE_DPMS_ON);
|
|
+ else
|
|
+ helper->dpms(connector->encoder, DRM_MODE_DPMS_OFF);
|
|
+ }
|
|
+
|
|
+ nv_wr32(dev, 0xe054, nv_rd32(dev, 0xe054));
|
|
+ if (dev_priv->chipset >= 0x90)
|
|
+ nv_wr32(dev, 0xe074, nv_rd32(dev, 0xe074));
|
|
+}
|
|
+
|
|
+void
|
|
+nv50_display_irq_handler(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t delayed = 0;
|
|
+
|
|
+ while (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_HOTPLUG)
|
|
+ nv50_display_irq_hotplug(dev);
|
|
+
|
|
+ while (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_DISPLAY) {
|
|
+ uint32_t intr0 = nv_rd32(dev, NV50_PDISPLAY_INTR_0);
|
|
+ uint32_t intr1 = nv_rd32(dev, NV50_PDISPLAY_INTR_1);
|
|
+ uint32_t clock;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "PDISPLAY_INTR 0x%08x 0x%08x\n", intr0, intr1);
|
|
+
|
|
+ if (!intr0 && !(intr1 & ~delayed))
|
|
+ break;
|
|
+
|
|
+ if (intr0 & 0x00010000) {
|
|
+ nv50_display_error_handler(dev);
|
|
+ intr0 &= ~0x00010000;
|
|
+ }
|
|
+
|
|
+ if (intr1 & NV50_PDISPLAY_INTR_1_VBLANK_CRTC) {
|
|
+ nv50_display_vblank_handler(dev, intr1);
|
|
+ intr1 &= ~NV50_PDISPLAY_INTR_1_VBLANK_CRTC;
|
|
+ }
|
|
+
|
|
+ clock = (intr1 & (NV50_PDISPLAY_INTR_1_CLK_UNK10 |
|
|
+ NV50_PDISPLAY_INTR_1_CLK_UNK20 |
|
|
+ NV50_PDISPLAY_INTR_1_CLK_UNK40));
|
|
+ if (clock) {
|
|
+ nv_wr32(dev, NV03_PMC_INTR_EN_0, 0);
|
|
+ if (!work_pending(&dev_priv->irq_work))
|
|
+ queue_work(dev_priv->wq, &dev_priv->irq_work);
|
|
+ delayed |= clock;
|
|
+ intr1 &= ~clock;
|
|
+ }
|
|
+
|
|
+ if (intr0) {
|
|
+ NV_ERROR(dev, "unknown PDISPLAY_INTR_0: 0x%08x\n", intr0);
|
|
+ nv_wr32(dev, NV50_PDISPLAY_INTR_0, intr0);
|
|
+ }
|
|
+
|
|
+ if (intr1) {
|
|
+ NV_ERROR(dev,
|
|
+ "unknown PDISPLAY_INTR_1: 0x%08x\n", intr1);
|
|
+ nv_wr32(dev, NV50_PDISPLAY_INTR_1, intr1);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nv50_display.h b/drivers/gpu/drm/nouveau/nv50_display.h
|
|
new file mode 100644
|
|
index 0000000..3ae8d07
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv50_display.h
|
|
@@ -0,0 +1,46 @@
|
|
+/*
|
|
+ * Copyright (C) 2008 Maarten Maathuis.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef __NV50_DISPLAY_H__
|
|
+#define __NV50_DISPLAY_H__
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_dma.h"
|
|
+#include "nouveau_reg.h"
|
|
+#include "nouveau_crtc.h"
|
|
+#include "nv50_evo.h"
|
|
+
|
|
+void nv50_display_irq_handler(struct drm_device *dev);
|
|
+void nv50_display_irq_handler_bh(struct work_struct *work);
|
|
+int nv50_display_init(struct drm_device *dev);
|
|
+int nv50_display_create(struct drm_device *dev);
|
|
+int nv50_display_destroy(struct drm_device *dev);
|
|
+int nv50_crtc_blank(struct nouveau_crtc *, bool blank);
|
|
+int nv50_crtc_set_clock(struct drm_device *, int head, int pclk);
|
|
+
|
|
+#endif /* __NV50_DISPLAY_H__ */
|
|
diff --git a/drivers/gpu/drm/nouveau/nv50_evo.h b/drivers/gpu/drm/nouveau/nv50_evo.h
|
|
new file mode 100644
|
|
index 0000000..aae1334
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv50_evo.h
|
|
@@ -0,0 +1,113 @@
|
|
+/*
|
|
+ * Copyright (C) 2008 Maarten Maathuis.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#define NV50_EVO_UPDATE 0x00000080
|
|
+#define NV50_EVO_UNK84 0x00000084
|
|
+#define NV50_EVO_UNK84_NOTIFY 0x40000000
|
|
+#define NV50_EVO_UNK84_NOTIFY_DISABLED 0x00000000
|
|
+#define NV50_EVO_UNK84_NOTIFY_ENABLED 0x40000000
|
|
+#define NV50_EVO_DMA_NOTIFY 0x00000088
|
|
+#define NV50_EVO_DMA_NOTIFY_HANDLE 0xffffffff
|
|
+#define NV50_EVO_DMA_NOTIFY_HANDLE_NONE 0x00000000
|
|
+#define NV50_EVO_UNK8C 0x0000008C
|
|
+
|
|
+#define NV50_EVO_DAC(n, r) ((n) * 0x80 + NV50_EVO_DAC_##r)
|
|
+#define NV50_EVO_DAC_MODE_CTRL 0x00000400
|
|
+#define NV50_EVO_DAC_MODE_CTRL_CRTC0 0x00000001
|
|
+#define NV50_EVO_DAC_MODE_CTRL_CRTC1 0x00000002
|
|
+#define NV50_EVO_DAC_MODE_CTRL2 0x00000404
|
|
+#define NV50_EVO_DAC_MODE_CTRL2_NHSYNC 0x00000001
|
|
+#define NV50_EVO_DAC_MODE_CTRL2_NVSYNC 0x00000002
|
|
+
|
|
+#define NV50_EVO_SOR(n, r) ((n) * 0x40 + NV50_EVO_SOR_##r)
|
|
+#define NV50_EVO_SOR_MODE_CTRL 0x00000600
|
|
+#define NV50_EVO_SOR_MODE_CTRL_CRTC0 0x00000001
|
|
+#define NV50_EVO_SOR_MODE_CTRL_CRTC1 0x00000002
|
|
+#define NV50_EVO_SOR_MODE_CTRL_TMDS 0x00000100
|
|
+#define NV50_EVO_SOR_MODE_CTRL_TMDS_DUAL_LINK 0x00000400
|
|
+#define NV50_EVO_SOR_MODE_CTRL_NHSYNC 0x00001000
|
|
+#define NV50_EVO_SOR_MODE_CTRL_NVSYNC 0x00002000
|
|
+
|
|
+#define NV50_EVO_CRTC(n, r) ((n) * 0x400 + NV50_EVO_CRTC_##r)
|
|
+#define NV84_EVO_CRTC(n, r) ((n) * 0x400 + NV84_EVO_CRTC_##r)
|
|
+#define NV50_EVO_CRTC_UNK0800 0x00000800
|
|
+#define NV50_EVO_CRTC_CLOCK 0x00000804
|
|
+#define NV50_EVO_CRTC_INTERLACE 0x00000808
|
|
+#define NV50_EVO_CRTC_DISPLAY_START 0x00000810
|
|
+#define NV50_EVO_CRTC_DISPLAY_TOTAL 0x00000814
|
|
+#define NV50_EVO_CRTC_SYNC_DURATION 0x00000818
|
|
+#define NV50_EVO_CRTC_SYNC_START_TO_BLANK_END 0x0000081c
|
|
+#define NV50_EVO_CRTC_UNK0820 0x00000820
|
|
+#define NV50_EVO_CRTC_UNK0824 0x00000824
|
|
+#define NV50_EVO_CRTC_UNK082C 0x0000082c
|
|
+#define NV50_EVO_CRTC_CLUT_MODE 0x00000840
|
|
+/* You can't have a palette in 8 bit mode (=OFF) */
|
|
+#define NV50_EVO_CRTC_CLUT_MODE_BLANK 0x00000000
|
|
+#define NV50_EVO_CRTC_CLUT_MODE_OFF 0x80000000
|
|
+#define NV50_EVO_CRTC_CLUT_MODE_ON 0xC0000000
|
|
+#define NV50_EVO_CRTC_CLUT_OFFSET 0x00000844
|
|
+#define NV84_EVO_CRTC_CLUT_DMA 0x0000085C
|
|
+#define NV84_EVO_CRTC_CLUT_DMA_HANDLE 0xffffffff
|
|
+#define NV84_EVO_CRTC_CLUT_DMA_HANDLE_NONE 0x00000000
|
|
+#define NV50_EVO_CRTC_FB_OFFSET 0x00000860
|
|
+#define NV50_EVO_CRTC_FB_SIZE 0x00000868
|
|
+#define NV50_EVO_CRTC_FB_CONFIG 0x0000086c
|
|
+#define NV50_EVO_CRTC_FB_CONFIG_MODE 0x00100000
|
|
+#define NV50_EVO_CRTC_FB_CONFIG_MODE_TILE 0x00000000
|
|
+#define NV50_EVO_CRTC_FB_CONFIG_MODE_PITCH 0x00100000
|
|
+#define NV50_EVO_CRTC_FB_DEPTH 0x00000870
|
|
+#define NV50_EVO_CRTC_FB_DEPTH_8 0x00001e00
|
|
+#define NV50_EVO_CRTC_FB_DEPTH_15 0x0000e900
|
|
+#define NV50_EVO_CRTC_FB_DEPTH_16 0x0000e800
|
|
+#define NV50_EVO_CRTC_FB_DEPTH_24 0x0000cf00
|
|
+#define NV50_EVO_CRTC_FB_DEPTH_30 0x0000d100
|
|
+#define NV50_EVO_CRTC_FB_DMA 0x00000874
|
|
+#define NV50_EVO_CRTC_FB_DMA_HANDLE 0xffffffff
|
|
+#define NV50_EVO_CRTC_FB_DMA_HANDLE_NONE 0x00000000
|
|
+#define NV50_EVO_CRTC_CURSOR_CTRL 0x00000880
|
|
+#define NV50_EVO_CRTC_CURSOR_CTRL_HIDE 0x05000000
|
|
+#define NV50_EVO_CRTC_CURSOR_CTRL_SHOW 0x85000000
|
|
+#define NV50_EVO_CRTC_CURSOR_OFFSET 0x00000884
|
|
+#define NV84_EVO_CRTC_CURSOR_DMA 0x0000089c
|
|
+#define NV84_EVO_CRTC_CURSOR_DMA_HANDLE 0xffffffff
|
|
+#define NV84_EVO_CRTC_CURSOR_DMA_HANDLE_NONE 0x00000000
|
|
+#define NV50_EVO_CRTC_DITHER_CTRL 0x000008a0
|
|
+#define NV50_EVO_CRTC_DITHER_CTRL_OFF 0x00000000
|
|
+#define NV50_EVO_CRTC_DITHER_CTRL_ON 0x00000011
|
|
+#define NV50_EVO_CRTC_SCALE_CTRL 0x000008a4
|
|
+#define NV50_EVO_CRTC_SCALE_CTRL_INACTIVE 0x00000000
|
|
+#define NV50_EVO_CRTC_SCALE_CTRL_ACTIVE 0x00000009
|
|
+#define NV50_EVO_CRTC_COLOR_CTRL 0x000008a8
|
|
+#define NV50_EVO_CRTC_COLOR_CTRL_COLOR 0x00040000
|
|
+#define NV50_EVO_CRTC_FB_POS 0x000008c0
|
|
+#define NV50_EVO_CRTC_REAL_RES 0x000008c8
|
|
+#define NV50_EVO_CRTC_SCALE_CENTER_OFFSET 0x000008d4
|
|
+#define NV50_EVO_CRTC_SCALE_CENTER_OFFSET_VAL(x, y) \
|
|
+ ((((unsigned)y << 16) & 0xFFFF0000) | (((unsigned)x) & 0x0000FFFF))
|
|
+/* Both of these are needed, otherwise nothing happens. */
|
|
+#define NV50_EVO_CRTC_SCALE_RES1 0x000008d8
|
|
+#define NV50_EVO_CRTC_SCALE_RES2 0x000008dc
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nv50_fbcon.c b/drivers/gpu/drm/nouveau/nv50_fbcon.c
|
|
new file mode 100644
|
|
index 0000000..0f57cdf
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv50_fbcon.c
|
|
@@ -0,0 +1,267 @@
|
|
+#include "drmP.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_dma.h"
|
|
+#include "nouveau_fbcon.h"
|
|
+
|
|
+void
|
|
+nv50_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
|
|
+{
|
|
+ struct nouveau_fbcon_par *par = info->par;
|
|
+ struct drm_device *dev = par->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_channel *chan = dev_priv->channel;
|
|
+
|
|
+ if (info->state != FBINFO_STATE_RUNNING)
|
|
+ return;
|
|
+
|
|
+ if (!(info->flags & FBINFO_HWACCEL_DISABLED) &&
|
|
+ RING_SPACE(chan, rect->rop == ROP_COPY ? 7 : 11)) {
|
|
+ nouveau_fbcon_gpu_lockup(info);
|
|
+ }
|
|
+
|
|
+ if (info->flags & FBINFO_HWACCEL_DISABLED) {
|
|
+ cfb_fillrect(info, rect);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (rect->rop != ROP_COPY) {
|
|
+ BEGIN_RING(chan, NvSub2D, 0x02ac, 1);
|
|
+ OUT_RING(chan, 1);
|
|
+ }
|
|
+ BEGIN_RING(chan, NvSub2D, 0x0588, 1);
|
|
+ if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
|
|
+ info->fix.visual == FB_VISUAL_DIRECTCOLOR)
|
|
+ OUT_RING(chan, ((uint32_t *)info->pseudo_palette)[rect->color]);
|
|
+ else
|
|
+ OUT_RING(chan, rect->color);
|
|
+ BEGIN_RING(chan, NvSub2D, 0x0600, 4);
|
|
+ OUT_RING(chan, rect->dx);
|
|
+ OUT_RING(chan, rect->dy);
|
|
+ OUT_RING(chan, rect->dx + rect->width);
|
|
+ OUT_RING(chan, rect->dy + rect->height);
|
|
+ if (rect->rop != ROP_COPY) {
|
|
+ BEGIN_RING(chan, NvSub2D, 0x02ac, 1);
|
|
+ OUT_RING(chan, 3);
|
|
+ }
|
|
+ FIRE_RING(chan);
|
|
+}
|
|
+
|
|
+void
|
|
+nv50_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region)
|
|
+{
|
|
+ struct nouveau_fbcon_par *par = info->par;
|
|
+ struct drm_device *dev = par->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_channel *chan = dev_priv->channel;
|
|
+
|
|
+ if (info->state != FBINFO_STATE_RUNNING)
|
|
+ return;
|
|
+
|
|
+ if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 12)) {
|
|
+ nouveau_fbcon_gpu_lockup(info);
|
|
+ }
|
|
+
|
|
+ if (info->flags & FBINFO_HWACCEL_DISABLED) {
|
|
+ cfb_copyarea(info, region);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ BEGIN_RING(chan, NvSub2D, 0x0110, 1);
|
|
+ OUT_RING(chan, 0);
|
|
+ BEGIN_RING(chan, NvSub2D, 0x08b0, 4);
|
|
+ OUT_RING(chan, region->dx);
|
|
+ OUT_RING(chan, region->dy);
|
|
+ OUT_RING(chan, region->width);
|
|
+ OUT_RING(chan, region->height);
|
|
+ BEGIN_RING(chan, NvSub2D, 0x08d0, 4);
|
|
+ OUT_RING(chan, 0);
|
|
+ OUT_RING(chan, region->sx);
|
|
+ OUT_RING(chan, 0);
|
|
+ OUT_RING(chan, region->sy);
|
|
+ FIRE_RING(chan);
|
|
+}
|
|
+
|
|
+void
|
|
+nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
|
|
+{
|
|
+ struct nouveau_fbcon_par *par = info->par;
|
|
+ struct drm_device *dev = par->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_channel *chan = dev_priv->channel;
|
|
+ uint32_t width, dwords, *data = (uint32_t *)image->data;
|
|
+ uint32_t mask = ~(~0 >> (32 - info->var.bits_per_pixel));
|
|
+ uint32_t *palette = info->pseudo_palette;
|
|
+
|
|
+ if (info->state != FBINFO_STATE_RUNNING)
|
|
+ return;
|
|
+
|
|
+ if (image->depth != 1) {
|
|
+ cfb_imageblit(info, image);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 11)) {
|
|
+ nouveau_fbcon_gpu_lockup(info);
|
|
+ }
|
|
+
|
|
+ if (info->flags & FBINFO_HWACCEL_DISABLED) {
|
|
+ cfb_imageblit(info, image);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ width = (image->width + 31) & ~31;
|
|
+ dwords = (width * image->height) >> 5;
|
|
+
|
|
+ BEGIN_RING(chan, NvSub2D, 0x0814, 2);
|
|
+ if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
|
|
+ info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
|
|
+ OUT_RING(chan, palette[image->bg_color] | mask);
|
|
+ OUT_RING(chan, palette[image->fg_color] | mask);
|
|
+ } else {
|
|
+ OUT_RING(chan, image->bg_color);
|
|
+ OUT_RING(chan, image->fg_color);
|
|
+ }
|
|
+ BEGIN_RING(chan, NvSub2D, 0x0838, 2);
|
|
+ OUT_RING(chan, image->width);
|
|
+ OUT_RING(chan, image->height);
|
|
+ BEGIN_RING(chan, NvSub2D, 0x0850, 4);
|
|
+ OUT_RING(chan, 0);
|
|
+ OUT_RING(chan, image->dx);
|
|
+ OUT_RING(chan, 0);
|
|
+ OUT_RING(chan, image->dy);
|
|
+
|
|
+ while (dwords) {
|
|
+ int push = dwords > 2047 ? 2047 : dwords;
|
|
+
|
|
+ if (RING_SPACE(chan, push + 1)) {
|
|
+ nouveau_fbcon_gpu_lockup(info);
|
|
+ cfb_imageblit(info, image);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ dwords -= push;
|
|
+
|
|
+ BEGIN_RING(chan, NvSub2D, 0x40000860, push);
|
|
+ OUT_RINGp(chan, data, push);
|
|
+ data += push;
|
|
+ }
|
|
+
|
|
+ FIRE_RING(chan);
|
|
+}
|
|
+
|
|
+int
|
|
+nv50_fbcon_accel_init(struct fb_info *info)
|
|
+{
|
|
+ struct nouveau_fbcon_par *par = info->par;
|
|
+ struct drm_device *dev = par->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_channel *chan = dev_priv->channel;
|
|
+ struct nouveau_gpuobj *eng2d = NULL;
|
|
+ int ret, format;
|
|
+
|
|
+ switch (info->var.bits_per_pixel) {
|
|
+ case 8:
|
|
+ format = 0xf3;
|
|
+ break;
|
|
+ case 15:
|
|
+ format = 0xf8;
|
|
+ break;
|
|
+ case 16:
|
|
+ format = 0xe8;
|
|
+ break;
|
|
+ case 32:
|
|
+ switch (info->var.transp.length) {
|
|
+ case 0: /* depth 24 */
|
|
+ case 8: /* depth 32, just use 24.. */
|
|
+ format = 0xe6;
|
|
+ break;
|
|
+ case 2: /* depth 30 */
|
|
+ format = 0xd1;
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ret = nouveau_gpuobj_gr_new(dev_priv->channel, 0x502d, &eng2d);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, Nv2D, eng2d, NULL);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = RING_SPACE(chan, 59);
|
|
+ if (ret) {
|
|
+ nouveau_fbcon_gpu_lockup(info);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ BEGIN_RING(chan, NvSub2D, 0x0000, 1);
|
|
+ OUT_RING(chan, Nv2D);
|
|
+ BEGIN_RING(chan, NvSub2D, 0x0180, 4);
|
|
+ OUT_RING(chan, NvNotify0);
|
|
+ OUT_RING(chan, chan->vram_handle);
|
|
+ OUT_RING(chan, chan->vram_handle);
|
|
+ OUT_RING(chan, chan->vram_handle);
|
|
+ BEGIN_RING(chan, NvSub2D, 0x0290, 1);
|
|
+ OUT_RING(chan, 0);
|
|
+ BEGIN_RING(chan, NvSub2D, 0x0888, 1);
|
|
+ OUT_RING(chan, 1);
|
|
+ BEGIN_RING(chan, NvSub2D, 0x02ac, 1);
|
|
+ OUT_RING(chan, 3);
|
|
+ BEGIN_RING(chan, NvSub2D, 0x02a0, 1);
|
|
+ OUT_RING(chan, 0x55);
|
|
+ BEGIN_RING(chan, NvSub2D, 0x08c0, 4);
|
|
+ OUT_RING(chan, 0);
|
|
+ OUT_RING(chan, 1);
|
|
+ OUT_RING(chan, 0);
|
|
+ OUT_RING(chan, 1);
|
|
+ BEGIN_RING(chan, NvSub2D, 0x0580, 2);
|
|
+ OUT_RING(chan, 4);
|
|
+ OUT_RING(chan, format);
|
|
+ BEGIN_RING(chan, NvSub2D, 0x02e8, 2);
|
|
+ OUT_RING(chan, 2);
|
|
+ OUT_RING(chan, 1);
|
|
+ BEGIN_RING(chan, NvSub2D, 0x0804, 1);
|
|
+ OUT_RING(chan, format);
|
|
+ BEGIN_RING(chan, NvSub2D, 0x0800, 1);
|
|
+ OUT_RING(chan, 1);
|
|
+ BEGIN_RING(chan, NvSub2D, 0x0808, 3);
|
|
+ OUT_RING(chan, 0);
|
|
+ OUT_RING(chan, 0);
|
|
+ OUT_RING(chan, 0);
|
|
+ BEGIN_RING(chan, NvSub2D, 0x081c, 1);
|
|
+ OUT_RING(chan, 1);
|
|
+ BEGIN_RING(chan, NvSub2D, 0x0840, 4);
|
|
+ OUT_RING(chan, 0);
|
|
+ OUT_RING(chan, 1);
|
|
+ OUT_RING(chan, 0);
|
|
+ OUT_RING(chan, 1);
|
|
+ BEGIN_RING(chan, NvSub2D, 0x0200, 2);
|
|
+ OUT_RING(chan, format);
|
|
+ OUT_RING(chan, 1);
|
|
+ BEGIN_RING(chan, NvSub2D, 0x0214, 5);
|
|
+ OUT_RING(chan, info->fix.line_length);
|
|
+ OUT_RING(chan, info->var.xres_virtual);
|
|
+ OUT_RING(chan, info->var.yres_virtual);
|
|
+ OUT_RING(chan, 0);
|
|
+ OUT_RING(chan, info->fix.smem_start - dev_priv->fb_phys +
|
|
+ dev_priv->vm_vram_base);
|
|
+ BEGIN_RING(chan, NvSub2D, 0x0230, 2);
|
|
+ OUT_RING(chan, format);
|
|
+ OUT_RING(chan, 1);
|
|
+ BEGIN_RING(chan, NvSub2D, 0x0244, 5);
|
|
+ OUT_RING(chan, info->fix.line_length);
|
|
+ OUT_RING(chan, info->var.xres_virtual);
|
|
+ OUT_RING(chan, info->var.yres_virtual);
|
|
+ OUT_RING(chan, 0);
|
|
+ OUT_RING(chan, info->fix.smem_start - dev_priv->fb_phys +
|
|
+ dev_priv->vm_vram_base);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nv50_fifo.c b/drivers/gpu/drm/nouveau/nv50_fifo.c
|
|
new file mode 100644
|
|
index 0000000..204a79f
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv50_fifo.c
|
|
@@ -0,0 +1,495 @@
|
|
+/*
|
|
+ * Copyright (C) 2007 Ben Skeggs.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "nouveau_drv.h"
|
|
+
|
|
+struct nv50_fifo_priv {
|
|
+ struct nouveau_gpuobj_ref *thingo[2];
|
|
+ int cur_thingo;
|
|
+};
|
|
+
|
|
+#define IS_G80 ((dev_priv->chipset & 0xf0) == 0x50)
|
|
+
|
|
+static void
|
|
+nv50_fifo_init_thingo(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nv50_fifo_priv *priv = dev_priv->engine.fifo.priv;
|
|
+ struct nouveau_gpuobj_ref *cur;
|
|
+ int i, nr;
|
|
+
|
|
+ NV_DEBUG(dev, "\n");
|
|
+
|
|
+ cur = priv->thingo[priv->cur_thingo];
|
|
+ priv->cur_thingo = !priv->cur_thingo;
|
|
+
|
|
+ /* We never schedule channel 0 or 127 */
|
|
+ dev_priv->engine.instmem.prepare_access(dev, true);
|
|
+ for (i = 1, nr = 0; i < 127; i++) {
|
|
+ if (dev_priv->fifos[i] && dev_priv->fifos[i]->ramfc)
|
|
+ nv_wo32(dev, cur->gpuobj, nr++, i);
|
|
+ }
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+
|
|
+ nv_wr32(dev, 0x32f4, cur->instance >> 12);
|
|
+ nv_wr32(dev, 0x32ec, nr);
|
|
+ nv_wr32(dev, 0x2500, 0x101);
|
|
+}
|
|
+
|
|
+static int
|
|
+nv50_fifo_channel_enable(struct drm_device *dev, int channel, bool nt)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_channel *chan = dev_priv->fifos[channel];
|
|
+ uint32_t inst;
|
|
+
|
|
+ NV_DEBUG(dev, "ch%d\n", channel);
|
|
+
|
|
+ if (!chan->ramfc)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (IS_G80)
|
|
+ inst = chan->ramfc->instance >> 12;
|
|
+ else
|
|
+ inst = chan->ramfc->instance >> 8;
|
|
+ nv_wr32(dev, NV50_PFIFO_CTX_TABLE(channel),
|
|
+ inst | NV50_PFIFO_CTX_TABLE_CHANNEL_ENABLED);
|
|
+
|
|
+ if (!nt)
|
|
+ nv50_fifo_init_thingo(dev);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_fifo_channel_disable(struct drm_device *dev, int channel, bool nt)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t inst;
|
|
+
|
|
+ NV_DEBUG(dev, "ch%d, nt=%d\n", channel, nt);
|
|
+
|
|
+ if (IS_G80)
|
|
+ inst = NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G80;
|
|
+ else
|
|
+ inst = NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G84;
|
|
+ nv_wr32(dev, NV50_PFIFO_CTX_TABLE(channel), inst);
|
|
+
|
|
+ if (!nt)
|
|
+ nv50_fifo_init_thingo(dev);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_fifo_init_reset(struct drm_device *dev)
|
|
+{
|
|
+ uint32_t pmc_e = NV_PMC_ENABLE_PFIFO;
|
|
+
|
|
+ NV_DEBUG(dev, "\n");
|
|
+
|
|
+ nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) & ~pmc_e);
|
|
+ nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) | pmc_e);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_fifo_init_intr(struct drm_device *dev)
|
|
+{
|
|
+ NV_DEBUG(dev, "\n");
|
|
+
|
|
+ nv_wr32(dev, NV03_PFIFO_INTR_0, 0xFFFFFFFF);
|
|
+ nv_wr32(dev, NV03_PFIFO_INTR_EN_0, 0xFFFFFFFF);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_fifo_init_context_table(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ int i;
|
|
+
|
|
+ NV_DEBUG(dev, "\n");
|
|
+
|
|
+ for (i = 0; i < NV50_PFIFO_CTX_TABLE__SIZE; i++) {
|
|
+ if (dev_priv->fifos[i])
|
|
+ nv50_fifo_channel_enable(dev, i, true);
|
|
+ else
|
|
+ nv50_fifo_channel_disable(dev, i, true);
|
|
+ }
|
|
+
|
|
+ nv50_fifo_init_thingo(dev);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_fifo_init_regs__nv(struct drm_device *dev)
|
|
+{
|
|
+ NV_DEBUG(dev, "\n");
|
|
+
|
|
+ nv_wr32(dev, 0x250c, 0x6f3cfc34);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_fifo_init_regs(struct drm_device *dev)
|
|
+{
|
|
+ NV_DEBUG(dev, "\n");
|
|
+
|
|
+ nv_wr32(dev, 0x2500, 0);
|
|
+ nv_wr32(dev, 0x3250, 0);
|
|
+ nv_wr32(dev, 0x3220, 0);
|
|
+ nv_wr32(dev, 0x3204, 0);
|
|
+ nv_wr32(dev, 0x3210, 0);
|
|
+ nv_wr32(dev, 0x3270, 0);
|
|
+
|
|
+ /* Enable dummy channels setup by nv50_instmem.c */
|
|
+ nv50_fifo_channel_enable(dev, 0, true);
|
|
+ nv50_fifo_channel_enable(dev, 127, true);
|
|
+}
|
|
+
|
|
+int
|
|
+nv50_fifo_init(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nv50_fifo_priv *priv;
|
|
+ int ret;
|
|
+
|
|
+ NV_DEBUG(dev, "\n");
|
|
+
|
|
+ priv = dev_priv->engine.fifo.priv;
|
|
+ if (priv) {
|
|
+ priv->cur_thingo = !priv->cur_thingo;
|
|
+ goto just_reset;
|
|
+ }
|
|
+
|
|
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
|
+ if (!priv)
|
|
+ return -ENOMEM;
|
|
+ dev_priv->engine.fifo.priv = priv;
|
|
+
|
|
+ ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 128*4, 0x1000,
|
|
+ NVOBJ_FLAG_ZERO_ALLOC, &priv->thingo[0]);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "error creating thingo0: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 128*4, 0x1000,
|
|
+ NVOBJ_FLAG_ZERO_ALLOC, &priv->thingo[1]);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "error creating thingo1: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+just_reset:
|
|
+ nv50_fifo_init_reset(dev);
|
|
+ nv50_fifo_init_intr(dev);
|
|
+ nv50_fifo_init_context_table(dev);
|
|
+ nv50_fifo_init_regs__nv(dev);
|
|
+ nv50_fifo_init_regs(dev);
|
|
+ dev_priv->engine.fifo.enable(dev);
|
|
+ dev_priv->engine.fifo.reassign(dev, true);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv50_fifo_takedown(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nv50_fifo_priv *priv = dev_priv->engine.fifo.priv;
|
|
+
|
|
+ NV_DEBUG(dev, "\n");
|
|
+
|
|
+ if (!priv)
|
|
+ return;
|
|
+
|
|
+ nouveau_gpuobj_ref_del(dev, &priv->thingo[0]);
|
|
+ nouveau_gpuobj_ref_del(dev, &priv->thingo[1]);
|
|
+
|
|
+ dev_priv->engine.fifo.priv = NULL;
|
|
+ kfree(priv);
|
|
+}
|
|
+
|
|
+int
|
|
+nv50_fifo_channel_id(struct drm_device *dev)
|
|
+{
|
|
+ return nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH1) &
|
|
+ NV50_PFIFO_CACHE1_PUSH1_CHID_MASK;
|
|
+}
|
|
+
|
|
+int
|
|
+nv50_fifo_create_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_gpuobj *ramfc = NULL;
|
|
+ int ret;
|
|
+
|
|
+ NV_DEBUG(dev, "ch%d\n", chan->id);
|
|
+
|
|
+ if (IS_G80) {
|
|
+ uint32_t ramin_poffset = chan->ramin->gpuobj->im_pramin->start;
|
|
+ uint32_t ramin_voffset = chan->ramin->gpuobj->im_backing_start;
|
|
+
|
|
+ ret = nouveau_gpuobj_new_fake(dev, ramin_poffset, ramin_voffset,
|
|
+ 0x100, NVOBJ_FLAG_ZERO_ALLOC |
|
|
+ NVOBJ_FLAG_ZERO_FREE, &ramfc,
|
|
+ &chan->ramfc);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = nouveau_gpuobj_new_fake(dev, ramin_poffset + 0x0400,
|
|
+ ramin_voffset + 0x0400, 4096,
|
|
+ 0, NULL, &chan->cache);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ } else {
|
|
+ ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, 0x100, 256,
|
|
+ NVOBJ_FLAG_ZERO_ALLOC |
|
|
+ NVOBJ_FLAG_ZERO_FREE,
|
|
+ &chan->ramfc);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ ramfc = chan->ramfc->gpuobj;
|
|
+
|
|
+ ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, 4096, 1024,
|
|
+ 0, &chan->cache);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ dev_priv->engine.instmem.prepare_access(dev, true);
|
|
+
|
|
+ nv_wo32(dev, ramfc, 0x08/4, chan->pushbuf_base);
|
|
+ nv_wo32(dev, ramfc, 0x10/4, chan->pushbuf_base);
|
|
+ nv_wo32(dev, ramfc, 0x48/4, chan->pushbuf->instance >> 4);
|
|
+ nv_wo32(dev, ramfc, 0x80/4, (0xc << 24) | (chan->ramht->instance >> 4));
|
|
+ nv_wo32(dev, ramfc, 0x3c/4, 0x00086078);
|
|
+ nv_wo32(dev, ramfc, 0x44/4, 0x2101ffff);
|
|
+ nv_wo32(dev, ramfc, 0x60/4, 0x7fffffff);
|
|
+ nv_wo32(dev, ramfc, 0x40/4, 0x00000000);
|
|
+ nv_wo32(dev, ramfc, 0x7c/4, 0x30000001);
|
|
+ nv_wo32(dev, ramfc, 0x78/4, 0x00000000);
|
|
+ nv_wo32(dev, ramfc, 0x4c/4, 0xffffffff);
|
|
+
|
|
+ if (!IS_G80) {
|
|
+ nv_wo32(dev, chan->ramin->gpuobj, 0, chan->id);
|
|
+ nv_wo32(dev, chan->ramin->gpuobj, 1,
|
|
+ chan->ramfc->instance >> 8);
|
|
+
|
|
+ nv_wo32(dev, ramfc, 0x88/4, chan->cache->instance >> 10);
|
|
+ nv_wo32(dev, ramfc, 0x98/4, chan->ramin->instance >> 12);
|
|
+ }
|
|
+
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+
|
|
+ ret = nv50_fifo_channel_enable(dev, chan->id, false);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "error enabling ch%d: %d\n", chan->id, ret);
|
|
+ nouveau_gpuobj_ref_del(dev, &chan->ramfc);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv50_fifo_destroy_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct nouveau_gpuobj_ref *ramfc = chan->ramfc;
|
|
+
|
|
+ NV_DEBUG(dev, "ch%d\n", chan->id);
|
|
+
|
|
+ /* This will ensure the channel is seen as disabled. */
|
|
+ chan->ramfc = NULL;
|
|
+ nv50_fifo_channel_disable(dev, chan->id, false);
|
|
+
|
|
+ /* Dummy channel, also used on ch 127 */
|
|
+ if (chan->id == 0)
|
|
+ nv50_fifo_channel_disable(dev, 127, false);
|
|
+
|
|
+ nouveau_gpuobj_ref_del(dev, &ramfc);
|
|
+ nouveau_gpuobj_ref_del(dev, &chan->cache);
|
|
+}
|
|
+
|
|
+int
|
|
+nv50_fifo_load_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_gpuobj *ramfc = chan->ramfc->gpuobj;
|
|
+ struct nouveau_gpuobj *cache = chan->cache->gpuobj;
|
|
+ int ptr, cnt;
|
|
+
|
|
+ NV_DEBUG(dev, "ch%d\n", chan->id);
|
|
+
|
|
+ dev_priv->engine.instmem.prepare_access(dev, false);
|
|
+
|
|
+ nv_wr32(dev, 0x3330, nv_ro32(dev, ramfc, 0x00/4));
|
|
+ nv_wr32(dev, 0x3334, nv_ro32(dev, ramfc, 0x04/4));
|
|
+ nv_wr32(dev, 0x3240, nv_ro32(dev, ramfc, 0x08/4));
|
|
+ nv_wr32(dev, 0x3320, nv_ro32(dev, ramfc, 0x0c/4));
|
|
+ nv_wr32(dev, 0x3244, nv_ro32(dev, ramfc, 0x10/4));
|
|
+ nv_wr32(dev, 0x3328, nv_ro32(dev, ramfc, 0x14/4));
|
|
+ nv_wr32(dev, 0x3368, nv_ro32(dev, ramfc, 0x18/4));
|
|
+ nv_wr32(dev, 0x336c, nv_ro32(dev, ramfc, 0x1c/4));
|
|
+ nv_wr32(dev, 0x3370, nv_ro32(dev, ramfc, 0x20/4));
|
|
+ nv_wr32(dev, 0x3374, nv_ro32(dev, ramfc, 0x24/4));
|
|
+ nv_wr32(dev, 0x3378, nv_ro32(dev, ramfc, 0x28/4));
|
|
+ nv_wr32(dev, 0x337c, nv_ro32(dev, ramfc, 0x2c/4));
|
|
+ nv_wr32(dev, 0x3228, nv_ro32(dev, ramfc, 0x30/4));
|
|
+ nv_wr32(dev, 0x3364, nv_ro32(dev, ramfc, 0x34/4));
|
|
+ nv_wr32(dev, 0x32a0, nv_ro32(dev, ramfc, 0x38/4));
|
|
+ nv_wr32(dev, 0x3224, nv_ro32(dev, ramfc, 0x3c/4));
|
|
+ nv_wr32(dev, 0x324c, nv_ro32(dev, ramfc, 0x40/4));
|
|
+ nv_wr32(dev, 0x2044, nv_ro32(dev, ramfc, 0x44/4));
|
|
+ nv_wr32(dev, 0x322c, nv_ro32(dev, ramfc, 0x48/4));
|
|
+ nv_wr32(dev, 0x3234, nv_ro32(dev, ramfc, 0x4c/4));
|
|
+ nv_wr32(dev, 0x3340, nv_ro32(dev, ramfc, 0x50/4));
|
|
+ nv_wr32(dev, 0x3344, nv_ro32(dev, ramfc, 0x54/4));
|
|
+ nv_wr32(dev, 0x3280, nv_ro32(dev, ramfc, 0x58/4));
|
|
+ nv_wr32(dev, 0x3254, nv_ro32(dev, ramfc, 0x5c/4));
|
|
+ nv_wr32(dev, 0x3260, nv_ro32(dev, ramfc, 0x60/4));
|
|
+ nv_wr32(dev, 0x3264, nv_ro32(dev, ramfc, 0x64/4));
|
|
+ nv_wr32(dev, 0x3268, nv_ro32(dev, ramfc, 0x68/4));
|
|
+ nv_wr32(dev, 0x326c, nv_ro32(dev, ramfc, 0x6c/4));
|
|
+ nv_wr32(dev, 0x32e4, nv_ro32(dev, ramfc, 0x70/4));
|
|
+ nv_wr32(dev, 0x3248, nv_ro32(dev, ramfc, 0x74/4));
|
|
+ nv_wr32(dev, 0x2088, nv_ro32(dev, ramfc, 0x78/4));
|
|
+ nv_wr32(dev, 0x2058, nv_ro32(dev, ramfc, 0x7c/4));
|
|
+ nv_wr32(dev, 0x2210, nv_ro32(dev, ramfc, 0x80/4));
|
|
+
|
|
+ cnt = nv_ro32(dev, ramfc, 0x84/4);
|
|
+ for (ptr = 0; ptr < cnt; ptr++) {
|
|
+ nv_wr32(dev, NV40_PFIFO_CACHE1_METHOD(ptr),
|
|
+ nv_ro32(dev, cache, (ptr * 2) + 0));
|
|
+ nv_wr32(dev, NV40_PFIFO_CACHE1_DATA(ptr),
|
|
+ nv_ro32(dev, cache, (ptr * 2) + 1));
|
|
+ }
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, cnt << 2);
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0);
|
|
+
|
|
+ /* guessing that all the 0x34xx regs aren't on NV50 */
|
|
+ if (!IS_G80) {
|
|
+ nv_wr32(dev, 0x340c, nv_ro32(dev, ramfc, 0x88/4));
|
|
+ nv_wr32(dev, 0x3400, nv_ro32(dev, ramfc, 0x8c/4));
|
|
+ nv_wr32(dev, 0x3404, nv_ro32(dev, ramfc, 0x90/4));
|
|
+ nv_wr32(dev, 0x3408, nv_ro32(dev, ramfc, 0x94/4));
|
|
+ nv_wr32(dev, 0x3410, nv_ro32(dev, ramfc, 0x98/4));
|
|
+ }
|
|
+
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, chan->id | (1<<16));
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nv50_fifo_unload_context(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
|
|
+ struct nouveau_gpuobj *ramfc, *cache;
|
|
+ struct nouveau_channel *chan = NULL;
|
|
+ int chid, get, put, ptr;
|
|
+
|
|
+ NV_DEBUG(dev, "\n");
|
|
+
|
|
+ chid = pfifo->channel_id(dev);
|
|
+ if (chid < 1 || chid >= dev_priv->engine.fifo.channels - 1)
|
|
+ return 0;
|
|
+
|
|
+ chan = dev_priv->fifos[chid];
|
|
+ if (!chan) {
|
|
+ NV_ERROR(dev, "Inactive channel on PFIFO: %d\n", chid);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ NV_DEBUG(dev, "ch%d\n", chan->id);
|
|
+ ramfc = chan->ramfc->gpuobj;
|
|
+ cache = chan->cache->gpuobj;
|
|
+
|
|
+ dev_priv->engine.instmem.prepare_access(dev, true);
|
|
+
|
|
+ nv_wo32(dev, ramfc, 0x00/4, nv_rd32(dev, 0x3330));
|
|
+ nv_wo32(dev, ramfc, 0x04/4, nv_rd32(dev, 0x3334));
|
|
+ nv_wo32(dev, ramfc, 0x08/4, nv_rd32(dev, 0x3240));
|
|
+ nv_wo32(dev, ramfc, 0x0c/4, nv_rd32(dev, 0x3320));
|
|
+ nv_wo32(dev, ramfc, 0x10/4, nv_rd32(dev, 0x3244));
|
|
+ nv_wo32(dev, ramfc, 0x14/4, nv_rd32(dev, 0x3328));
|
|
+ nv_wo32(dev, ramfc, 0x18/4, nv_rd32(dev, 0x3368));
|
|
+ nv_wo32(dev, ramfc, 0x1c/4, nv_rd32(dev, 0x336c));
|
|
+ nv_wo32(dev, ramfc, 0x20/4, nv_rd32(dev, 0x3370));
|
|
+ nv_wo32(dev, ramfc, 0x24/4, nv_rd32(dev, 0x3374));
|
|
+ nv_wo32(dev, ramfc, 0x28/4, nv_rd32(dev, 0x3378));
|
|
+ nv_wo32(dev, ramfc, 0x2c/4, nv_rd32(dev, 0x337c));
|
|
+ nv_wo32(dev, ramfc, 0x30/4, nv_rd32(dev, 0x3228));
|
|
+ nv_wo32(dev, ramfc, 0x34/4, nv_rd32(dev, 0x3364));
|
|
+ nv_wo32(dev, ramfc, 0x38/4, nv_rd32(dev, 0x32a0));
|
|
+ nv_wo32(dev, ramfc, 0x3c/4, nv_rd32(dev, 0x3224));
|
|
+ nv_wo32(dev, ramfc, 0x40/4, nv_rd32(dev, 0x324c));
|
|
+ nv_wo32(dev, ramfc, 0x44/4, nv_rd32(dev, 0x2044));
|
|
+ nv_wo32(dev, ramfc, 0x48/4, nv_rd32(dev, 0x322c));
|
|
+ nv_wo32(dev, ramfc, 0x4c/4, nv_rd32(dev, 0x3234));
|
|
+ nv_wo32(dev, ramfc, 0x50/4, nv_rd32(dev, 0x3340));
|
|
+ nv_wo32(dev, ramfc, 0x54/4, nv_rd32(dev, 0x3344));
|
|
+ nv_wo32(dev, ramfc, 0x58/4, nv_rd32(dev, 0x3280));
|
|
+ nv_wo32(dev, ramfc, 0x5c/4, nv_rd32(dev, 0x3254));
|
|
+ nv_wo32(dev, ramfc, 0x60/4, nv_rd32(dev, 0x3260));
|
|
+ nv_wo32(dev, ramfc, 0x64/4, nv_rd32(dev, 0x3264));
|
|
+ nv_wo32(dev, ramfc, 0x68/4, nv_rd32(dev, 0x3268));
|
|
+ nv_wo32(dev, ramfc, 0x6c/4, nv_rd32(dev, 0x326c));
|
|
+ nv_wo32(dev, ramfc, 0x70/4, nv_rd32(dev, 0x32e4));
|
|
+ nv_wo32(dev, ramfc, 0x74/4, nv_rd32(dev, 0x3248));
|
|
+ nv_wo32(dev, ramfc, 0x78/4, nv_rd32(dev, 0x2088));
|
|
+ nv_wo32(dev, ramfc, 0x7c/4, nv_rd32(dev, 0x2058));
|
|
+ nv_wo32(dev, ramfc, 0x80/4, nv_rd32(dev, 0x2210));
|
|
+
|
|
+ put = (nv_rd32(dev, NV03_PFIFO_CACHE1_PUT) & 0x7ff) >> 2;
|
|
+ get = (nv_rd32(dev, NV03_PFIFO_CACHE1_GET) & 0x7ff) >> 2;
|
|
+ ptr = 0;
|
|
+ while (put != get) {
|
|
+ nv_wo32(dev, cache, ptr++,
|
|
+ nv_rd32(dev, NV40_PFIFO_CACHE1_METHOD(get)));
|
|
+ nv_wo32(dev, cache, ptr++,
|
|
+ nv_rd32(dev, NV40_PFIFO_CACHE1_DATA(get)));
|
|
+ get = (get + 1) & 0x1ff;
|
|
+ }
|
|
+
|
|
+ /* guessing that all the 0x34xx regs aren't on NV50 */
|
|
+ if (!IS_G80) {
|
|
+ nv_wo32(dev, ramfc, 0x84/4, ptr >> 1);
|
|
+ nv_wo32(dev, ramfc, 0x88/4, nv_rd32(dev, 0x340c));
|
|
+ nv_wo32(dev, ramfc, 0x8c/4, nv_rd32(dev, 0x3400));
|
|
+ nv_wo32(dev, ramfc, 0x90/4, nv_rd32(dev, 0x3404));
|
|
+ nv_wo32(dev, ramfc, 0x94/4, nv_rd32(dev, 0x3408));
|
|
+ nv_wo32(dev, ramfc, 0x98/4, nv_rd32(dev, 0x3410));
|
|
+ }
|
|
+
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+
|
|
+ /*XXX: probably reload ch127 (NULL) state back too */
|
|
+ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, 127);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c
|
|
new file mode 100644
|
|
index 0000000..6d50480
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv50_graph.c
|
|
@@ -0,0 +1,394 @@
|
|
+/*
|
|
+ * Copyright (C) 2007 Ben Skeggs.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "nouveau_drv.h"
|
|
+
|
|
+MODULE_FIRMWARE("nouveau/nv50.ctxprog");
|
|
+MODULE_FIRMWARE("nouveau/nv50.ctxvals");
|
|
+MODULE_FIRMWARE("nouveau/nv84.ctxprog");
|
|
+MODULE_FIRMWARE("nouveau/nv84.ctxvals");
|
|
+MODULE_FIRMWARE("nouveau/nv86.ctxprog");
|
|
+MODULE_FIRMWARE("nouveau/nv86.ctxvals");
|
|
+MODULE_FIRMWARE("nouveau/nv92.ctxprog");
|
|
+MODULE_FIRMWARE("nouveau/nv92.ctxvals");
|
|
+MODULE_FIRMWARE("nouveau/nv94.ctxprog");
|
|
+MODULE_FIRMWARE("nouveau/nv94.ctxvals");
|
|
+MODULE_FIRMWARE("nouveau/nv96.ctxprog");
|
|
+MODULE_FIRMWARE("nouveau/nv96.ctxvals");
|
|
+MODULE_FIRMWARE("nouveau/nv98.ctxprog");
|
|
+MODULE_FIRMWARE("nouveau/nv98.ctxvals");
|
|
+MODULE_FIRMWARE("nouveau/nva0.ctxprog");
|
|
+MODULE_FIRMWARE("nouveau/nva0.ctxvals");
|
|
+MODULE_FIRMWARE("nouveau/nva5.ctxprog");
|
|
+MODULE_FIRMWARE("nouveau/nva5.ctxvals");
|
|
+MODULE_FIRMWARE("nouveau/nva8.ctxprog");
|
|
+MODULE_FIRMWARE("nouveau/nva8.ctxvals");
|
|
+MODULE_FIRMWARE("nouveau/nvaa.ctxprog");
|
|
+MODULE_FIRMWARE("nouveau/nvaa.ctxvals");
|
|
+MODULE_FIRMWARE("nouveau/nvac.ctxprog");
|
|
+MODULE_FIRMWARE("nouveau/nvac.ctxvals");
|
|
+
|
|
+#define IS_G80 ((dev_priv->chipset & 0xf0) == 0x50)
|
|
+
|
|
+static void
|
|
+nv50_graph_init_reset(struct drm_device *dev)
|
|
+{
|
|
+ uint32_t pmc_e = NV_PMC_ENABLE_PGRAPH | (1 << 21);
|
|
+
|
|
+ NV_DEBUG(dev, "\n");
|
|
+
|
|
+ nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) & ~pmc_e);
|
|
+ nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) | pmc_e);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_graph_init_intr(struct drm_device *dev)
|
|
+{
|
|
+ NV_DEBUG(dev, "\n");
|
|
+
|
|
+ nv_wr32(dev, NV03_PGRAPH_INTR, 0xffffffff);
|
|
+ nv_wr32(dev, 0x400138, 0xffffffff);
|
|
+ nv_wr32(dev, NV40_PGRAPH_INTR_EN, 0xffffffff);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_graph_init_regs__nv(struct drm_device *dev)
|
|
+{
|
|
+ NV_DEBUG(dev, "\n");
|
|
+
|
|
+ nv_wr32(dev, 0x400804, 0xc0000000);
|
|
+ nv_wr32(dev, 0x406800, 0xc0000000);
|
|
+ nv_wr32(dev, 0x400c04, 0xc0000000);
|
|
+ nv_wr32(dev, 0x401800, 0xc0000000);
|
|
+ nv_wr32(dev, 0x405018, 0xc0000000);
|
|
+ nv_wr32(dev, 0x402000, 0xc0000000);
|
|
+
|
|
+ nv_wr32(dev, 0x400108, 0xffffffff);
|
|
+
|
|
+ nv_wr32(dev, 0x400824, 0x00004000);
|
|
+ nv_wr32(dev, 0x400500, 0x00010001);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_graph_init_regs(struct drm_device *dev)
|
|
+{
|
|
+ NV_DEBUG(dev, "\n");
|
|
+
|
|
+ nv_wr32(dev, NV04_PGRAPH_DEBUG_3,
|
|
+ (1 << 2) /* HW_CONTEXT_SWITCH_ENABLED */);
|
|
+ nv_wr32(dev, 0x402ca8, 0x800);
|
|
+}
|
|
+
|
|
+static int
|
|
+nv50_graph_init_ctxctl(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ NV_DEBUG(dev, "\n");
|
|
+
|
|
+ nouveau_grctx_prog_load(dev);
|
|
+ if (!dev_priv->engine.graph.ctxprog)
|
|
+ dev_priv->engine.graph.accel_blocked = true;
|
|
+
|
|
+ nv_wr32(dev, 0x400320, 4);
|
|
+ nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, 0);
|
|
+ nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, 0);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nv50_graph_init(struct drm_device *dev)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ NV_DEBUG(dev, "\n");
|
|
+
|
|
+ nv50_graph_init_reset(dev);
|
|
+ nv50_graph_init_regs__nv(dev);
|
|
+ nv50_graph_init_regs(dev);
|
|
+ nv50_graph_init_intr(dev);
|
|
+
|
|
+ ret = nv50_graph_init_ctxctl(dev);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv50_graph_takedown(struct drm_device *dev)
|
|
+{
|
|
+ NV_DEBUG(dev, "\n");
|
|
+ nouveau_grctx_fini(dev);
|
|
+}
|
|
+
|
|
+void
|
|
+nv50_graph_fifo_access(struct drm_device *dev, bool enabled)
|
|
+{
|
|
+ const uint32_t mask = 0x00010001;
|
|
+
|
|
+ if (enabled)
|
|
+ nv_wr32(dev, 0x400500, nv_rd32(dev, 0x400500) | mask);
|
|
+ else
|
|
+ nv_wr32(dev, 0x400500, nv_rd32(dev, 0x400500) & ~mask);
|
|
+}
|
|
+
|
|
+struct nouveau_channel *
|
|
+nv50_graph_channel(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ uint32_t inst;
|
|
+ int i;
|
|
+
|
|
+ /* Be sure we're not in the middle of a context switch or bad things
|
|
+ * will happen, such as unloading the wrong pgraph context.
|
|
+ */
|
|
+ if (!nv_wait(0x400300, 0x00000001, 0x00000000))
|
|
+ NV_ERROR(dev, "Ctxprog is still running\n");
|
|
+
|
|
+ inst = nv_rd32(dev, NV50_PGRAPH_CTXCTL_CUR);
|
|
+ if (!(inst & NV50_PGRAPH_CTXCTL_CUR_LOADED))
|
|
+ return NULL;
|
|
+ inst = (inst & NV50_PGRAPH_CTXCTL_CUR_INSTANCE) << 12;
|
|
+
|
|
+ for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
|
|
+ struct nouveau_channel *chan = dev_priv->fifos[i];
|
|
+
|
|
+ if (chan && chan->ramin && chan->ramin->instance == inst)
|
|
+ return chan;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+int
|
|
+nv50_graph_create_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_gpuobj *ramin = chan->ramin->gpuobj;
|
|
+ struct nouveau_gpuobj *ctx;
|
|
+ uint32_t grctx_size = 0x70000;
|
|
+ int hdr, ret;
|
|
+
|
|
+ NV_DEBUG(dev, "ch%d\n", chan->id);
|
|
+
|
|
+ ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, grctx_size, 0x1000,
|
|
+ NVOBJ_FLAG_ZERO_ALLOC |
|
|
+ NVOBJ_FLAG_ZERO_FREE, &chan->ramin_grctx);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ ctx = chan->ramin_grctx->gpuobj;
|
|
+
|
|
+ hdr = IS_G80 ? 0x200 : 0x20;
|
|
+ dev_priv->engine.instmem.prepare_access(dev, true);
|
|
+ nv_wo32(dev, ramin, (hdr + 0x00)/4, 0x00190002);
|
|
+ nv_wo32(dev, ramin, (hdr + 0x04)/4, chan->ramin_grctx->instance +
|
|
+ grctx_size - 1);
|
|
+ nv_wo32(dev, ramin, (hdr + 0x08)/4, chan->ramin_grctx->instance);
|
|
+ nv_wo32(dev, ramin, (hdr + 0x0c)/4, 0);
|
|
+ nv_wo32(dev, ramin, (hdr + 0x10)/4, 0);
|
|
+ nv_wo32(dev, ramin, (hdr + 0x14)/4, 0x00010000);
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+
|
|
+ dev_priv->engine.instmem.prepare_access(dev, true);
|
|
+ nouveau_grctx_vals_load(dev, ctx);
|
|
+ nv_wo32(dev, ctx, 0x00000/4, chan->ramin->instance >> 12);
|
|
+ if ((dev_priv->chipset & 0xf0) == 0xa0)
|
|
+ nv_wo32(dev, ctx, 0x00004/4, 0x00000000);
|
|
+ else
|
|
+ nv_wo32(dev, ctx, 0x0011c/4, 0x00000000);
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv50_graph_destroy_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ int i, hdr = IS_G80 ? 0x200 : 0x20;
|
|
+
|
|
+ NV_DEBUG(dev, "ch%d\n", chan->id);
|
|
+
|
|
+ if (!chan->ramin || !chan->ramin->gpuobj)
|
|
+ return;
|
|
+
|
|
+ dev_priv->engine.instmem.prepare_access(dev, true);
|
|
+ for (i = hdr; i < hdr + 24; i += 4)
|
|
+ nv_wo32(dev, chan->ramin->gpuobj, i/4, 0);
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+
|
|
+ nouveau_gpuobj_ref_del(dev, &chan->ramin_grctx);
|
|
+}
|
|
+
|
|
+static int
|
|
+nv50_graph_do_load_context(struct drm_device *dev, uint32_t inst)
|
|
+{
|
|
+ uint32_t fifo = nv_rd32(dev, 0x400500);
|
|
+
|
|
+ nv_wr32(dev, 0x400500, fifo & ~1);
|
|
+ nv_wr32(dev, 0x400784, inst);
|
|
+ nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) | 0x40);
|
|
+ nv_wr32(dev, 0x400320, nv_rd32(dev, 0x400320) | 0x11);
|
|
+ nv_wr32(dev, 0x400040, 0xffffffff);
|
|
+ (void)nv_rd32(dev, 0x400040);
|
|
+ nv_wr32(dev, 0x400040, 0x00000000);
|
|
+ nv_wr32(dev, 0x400304, nv_rd32(dev, 0x400304) | 1);
|
|
+
|
|
+ if (nouveau_wait_for_idle(dev))
|
|
+ nv_wr32(dev, 0x40032c, inst | (1<<31));
|
|
+ nv_wr32(dev, 0x400500, fifo);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nv50_graph_load_context(struct nouveau_channel *chan)
|
|
+{
|
|
+ uint32_t inst = chan->ramin->instance >> 12;
|
|
+
|
|
+ NV_DEBUG(chan->dev, "ch%d\n", chan->id);
|
|
+ return nv50_graph_do_load_context(chan->dev, inst);
|
|
+}
|
|
+
|
|
+int
|
|
+nv50_graph_unload_context(struct drm_device *dev)
|
|
+{
|
|
+ uint32_t inst;
|
|
+
|
|
+ inst = nv_rd32(dev, NV50_PGRAPH_CTXCTL_CUR);
|
|
+ if (!(inst & NV50_PGRAPH_CTXCTL_CUR_LOADED))
|
|
+ return 0;
|
|
+ inst &= NV50_PGRAPH_CTXCTL_CUR_INSTANCE;
|
|
+
|
|
+ nouveau_wait_for_idle(dev);
|
|
+ nv_wr32(dev, 0x400784, inst);
|
|
+ nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) | 0x20);
|
|
+ nv_wr32(dev, 0x400304, nv_rd32(dev, 0x400304) | 0x01);
|
|
+ nouveau_wait_for_idle(dev);
|
|
+
|
|
+ nv_wr32(dev, NV50_PGRAPH_CTXCTL_CUR, inst);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv50_graph_context_switch(struct drm_device *dev)
|
|
+{
|
|
+ uint32_t inst;
|
|
+
|
|
+ nv50_graph_unload_context(dev);
|
|
+
|
|
+ inst = nv_rd32(dev, NV50_PGRAPH_CTXCTL_NEXT);
|
|
+ inst &= NV50_PGRAPH_CTXCTL_NEXT_INSTANCE;
|
|
+ nv50_graph_do_load_context(dev, inst);
|
|
+
|
|
+ nv_wr32(dev, NV40_PGRAPH_INTR_EN, nv_rd32(dev,
|
|
+ NV40_PGRAPH_INTR_EN) | NV_PGRAPH_INTR_CONTEXT_SWITCH);
|
|
+}
|
|
+
|
|
+static int
|
|
+nv50_graph_nvsw_dma_vblsem(struct nouveau_channel *chan, int grclass,
|
|
+ int mthd, uint32_t data)
|
|
+{
|
|
+ struct nouveau_gpuobj_ref *ref = NULL;
|
|
+
|
|
+ if (nouveau_gpuobj_ref_find(chan, data, &ref))
|
|
+ return -ENOENT;
|
|
+
|
|
+ if (nouveau_notifier_offset(ref->gpuobj, NULL))
|
|
+ return -EINVAL;
|
|
+
|
|
+ chan->nvsw.vblsem = ref->gpuobj;
|
|
+ chan->nvsw.vblsem_offset = ~0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+nv50_graph_nvsw_vblsem_offset(struct nouveau_channel *chan, int grclass,
|
|
+ int mthd, uint32_t data)
|
|
+{
|
|
+ if (nouveau_notifier_offset(chan->nvsw.vblsem, &data))
|
|
+ return -ERANGE;
|
|
+
|
|
+ chan->nvsw.vblsem_offset = data >> 2;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+nv50_graph_nvsw_vblsem_release_val(struct nouveau_channel *chan, int grclass,
|
|
+ int mthd, uint32_t data)
|
|
+{
|
|
+ chan->nvsw.vblsem_rval = data;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+nv50_graph_nvsw_vblsem_release(struct nouveau_channel *chan, int grclass,
|
|
+ int mthd, uint32_t data)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ if (!chan->nvsw.vblsem || chan->nvsw.vblsem_offset == ~0 || data > 1)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (!(nv_rd32(dev, NV50_PDISPLAY_INTR_EN) &
|
|
+ NV50_PDISPLAY_INTR_EN_VBLANK_CRTC_(data))) {
|
|
+ nv_wr32(dev, NV50_PDISPLAY_INTR_1,
|
|
+ NV50_PDISPLAY_INTR_1_VBLANK_CRTC_(data));
|
|
+ nv_wr32(dev, NV50_PDISPLAY_INTR_EN, nv_rd32(dev,
|
|
+ NV50_PDISPLAY_INTR_EN) |
|
|
+ NV50_PDISPLAY_INTR_EN_VBLANK_CRTC_(data));
|
|
+ }
|
|
+
|
|
+ list_add(&chan->nvsw.vbl_wait, &dev_priv->vbl_waiting);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct nouveau_pgraph_object_method nv50_graph_nvsw_methods[] = {
|
|
+ { 0x018c, nv50_graph_nvsw_dma_vblsem },
|
|
+ { 0x0400, nv50_graph_nvsw_vblsem_offset },
|
|
+ { 0x0404, nv50_graph_nvsw_vblsem_release_val },
|
|
+ { 0x0408, nv50_graph_nvsw_vblsem_release },
|
|
+ {}
|
|
+};
|
|
+
|
|
+struct nouveau_pgraph_object_class nv50_graph_grclass[] = {
|
|
+ { 0x506e, true, nv50_graph_nvsw_methods }, /* nvsw */
|
|
+ { 0x0030, false, NULL }, /* null */
|
|
+ { 0x5039, false, NULL }, /* m2mf */
|
|
+ { 0x502d, false, NULL }, /* 2d */
|
|
+ { 0x50c0, false, NULL }, /* compute */
|
|
+ { 0x5097, false, NULL }, /* tesla (nv50) */
|
|
+ { 0x8297, false, NULL }, /* tesla (nv80/nv90) */
|
|
+ { 0x8397, false, NULL }, /* tesla (nva0) */
|
|
+ { 0x8597, false, NULL }, /* tesla (nva8) */
|
|
+ {}
|
|
+};
|
|
diff --git a/drivers/gpu/drm/nouveau/nv50_instmem.c b/drivers/gpu/drm/nouveau/nv50_instmem.c
|
|
new file mode 100644
|
|
index 0000000..94400f7
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv50_instmem.c
|
|
@@ -0,0 +1,509 @@
|
|
+/*
|
|
+ * Copyright (C) 2007 Ben Skeggs.
|
|
+ *
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "nouveau_drv.h"
|
|
+
|
|
+struct nv50_instmem_priv {
|
|
+ uint32_t save1700[5]; /* 0x1700->0x1710 */
|
|
+
|
|
+ struct nouveau_gpuobj_ref *pramin_pt;
|
|
+ struct nouveau_gpuobj_ref *pramin_bar;
|
|
+ struct nouveau_gpuobj_ref *fb_bar;
|
|
+
|
|
+ bool last_access_wr;
|
|
+};
|
|
+
|
|
+#define NV50_INSTMEM_PAGE_SHIFT 12
|
|
+#define NV50_INSTMEM_PAGE_SIZE (1 << NV50_INSTMEM_PAGE_SHIFT)
|
|
+#define NV50_INSTMEM_PT_SIZE(a) (((a) >> 12) << 3)
|
|
+
|
|
+/*NOTE: - Assumes 0x1700 already covers the correct MiB of PRAMIN
|
|
+ */
|
|
+#define BAR0_WI32(g, o, v) do { \
|
|
+ uint32_t offset; \
|
|
+ if ((g)->im_backing) { \
|
|
+ offset = (g)->im_backing_start; \
|
|
+ } else { \
|
|
+ offset = chan->ramin->gpuobj->im_backing_start; \
|
|
+ offset += (g)->im_pramin->start; \
|
|
+ } \
|
|
+ offset += (o); \
|
|
+ nv_wr32(dev, NV_RAMIN + (offset & 0xfffff), (v)); \
|
|
+} while (0)
|
|
+
|
|
+int
|
|
+nv50_instmem_init(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_channel *chan;
|
|
+ uint32_t c_offset, c_size, c_ramfc, c_vmpd, c_base, pt_size;
|
|
+ struct nv50_instmem_priv *priv;
|
|
+ int ret, i;
|
|
+ uint32_t v, save_nv001700;
|
|
+
|
|
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
|
+ if (!priv)
|
|
+ return -ENOMEM;
|
|
+ dev_priv->engine.instmem.priv = priv;
|
|
+
|
|
+ /* Save state, will restore at takedown. */
|
|
+ for (i = 0x1700; i <= 0x1710; i += 4)
|
|
+ priv->save1700[(i-0x1700)/4] = nv_rd32(dev, i);
|
|
+
|
|
+ /* Reserve the last MiB of VRAM, we should probably try to avoid
|
|
+ * setting up the below tables over the top of the VBIOS image at
|
|
+ * some point.
|
|
+ */
|
|
+ dev_priv->ramin_rsvd_vram = 1 << 20;
|
|
+ c_offset = nouveau_mem_fb_amount(dev) - dev_priv->ramin_rsvd_vram;
|
|
+ c_size = 128 << 10;
|
|
+ c_vmpd = ((dev_priv->chipset & 0xf0) == 0x50) ? 0x1400 : 0x200;
|
|
+ c_ramfc = ((dev_priv->chipset & 0xf0) == 0x50) ? 0x0 : 0x20;
|
|
+ c_base = c_vmpd + 0x4000;
|
|
+ pt_size = NV50_INSTMEM_PT_SIZE(dev_priv->ramin_size);
|
|
+
|
|
+ NV_DEBUG(dev, " Rsvd VRAM base: 0x%08x\n", c_offset);
|
|
+ NV_DEBUG(dev, " VBIOS image: 0x%08x\n",
|
|
+ (nv_rd32(dev, 0x619f04) & ~0xff) << 8);
|
|
+ NV_DEBUG(dev, " Aperture size: %d MiB\n", dev_priv->ramin_size >> 20);
|
|
+ NV_DEBUG(dev, " PT size: %d KiB\n", pt_size >> 10);
|
|
+
|
|
+ /* Determine VM layout, we need to do this first to make sure
|
|
+ * we allocate enough memory for all the page tables.
|
|
+ */
|
|
+ dev_priv->vm_gart_base = roundup(NV50_VM_BLOCK, NV50_VM_BLOCK);
|
|
+ dev_priv->vm_gart_size = NV50_VM_BLOCK;
|
|
+
|
|
+ dev_priv->vm_vram_base = dev_priv->vm_gart_base + dev_priv->vm_gart_size;
|
|
+ dev_priv->vm_vram_size = nouveau_mem_fb_amount(dev);
|
|
+ if (dev_priv->vm_vram_size > NV50_VM_MAX_VRAM)
|
|
+ dev_priv->vm_vram_size = NV50_VM_MAX_VRAM;
|
|
+ dev_priv->vm_vram_size = roundup(dev_priv->vm_vram_size, NV50_VM_BLOCK);
|
|
+ dev_priv->vm_vram_pt_nr = dev_priv->vm_vram_size / NV50_VM_BLOCK;
|
|
+
|
|
+ dev_priv->vm_end = dev_priv->vm_vram_base + dev_priv->vm_vram_size;
|
|
+
|
|
+ NV_DEBUG(dev, "NV50VM: GART 0x%016llx-0x%016llx\n",
|
|
+ dev_priv->vm_gart_base,
|
|
+ dev_priv->vm_gart_base + dev_priv->vm_gart_size - 1);
|
|
+ NV_DEBUG(dev, "NV50VM: VRAM 0x%016llx-0x%016llx\n",
|
|
+ dev_priv->vm_vram_base,
|
|
+ dev_priv->vm_vram_base + dev_priv->vm_vram_size - 1);
|
|
+
|
|
+ c_size += dev_priv->vm_vram_pt_nr * (NV50_VM_BLOCK / 65536 * 8);
|
|
+
|
|
+ /* Map BAR0 PRAMIN aperture over the memory we want to use */
|
|
+ save_nv001700 = nv_rd32(dev, NV50_PUNK_BAR0_PRAMIN);
|
|
+ nv_wr32(dev, NV50_PUNK_BAR0_PRAMIN, (c_offset >> 16));
|
|
+
|
|
+ /* Create a fake channel, and use it as our "dummy" channels 0/127.
|
|
+ * The main reason for creating a channel is so we can use the gpuobj
|
|
+ * code. However, it's probably worth noting that NVIDIA also setup
|
|
+ * their channels 0/127 with the same values they configure here.
|
|
+ * So, there may be some other reason for doing this.
|
|
+ *
|
|
+ * Have to create the entire channel manually, as the real channel
|
|
+ * creation code assumes we have PRAMIN access, and we don't until
|
|
+ * we're done here.
|
|
+ */
|
|
+ chan = kzalloc(sizeof(*chan), GFP_KERNEL);
|
|
+ if (!chan)
|
|
+ return -ENOMEM;
|
|
+ chan->id = 0;
|
|
+ chan->dev = dev;
|
|
+ chan->file_priv = (struct drm_file *)-2;
|
|
+ dev_priv->fifos[0] = dev_priv->fifos[127] = chan;
|
|
+
|
|
+ /* Channel's PRAMIN object + heap */
|
|
+ ret = nouveau_gpuobj_new_fake(dev, 0, c_offset, c_size, 0,
|
|
+ NULL, &chan->ramin);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ if (nouveau_mem_init_heap(&chan->ramin_heap, c_base, c_size - c_base))
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* RAMFC + zero channel's PRAMIN up to start of VM pagedir */
|
|
+ ret = nouveau_gpuobj_new_fake(dev, c_ramfc, c_offset + c_ramfc,
|
|
+ 0x4000, 0, NULL, &chan->ramfc);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ for (i = 0; i < c_vmpd; i += 4)
|
|
+ BAR0_WI32(chan->ramin->gpuobj, i, 0);
|
|
+
|
|
+ /* VM page directory */
|
|
+ ret = nouveau_gpuobj_new_fake(dev, c_vmpd, c_offset + c_vmpd,
|
|
+ 0x4000, 0, &chan->vm_pd, NULL);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ for (i = 0; i < 0x4000; i += 8) {
|
|
+ BAR0_WI32(chan->vm_pd, i + 0x00, 0x00000000);
|
|
+ BAR0_WI32(chan->vm_pd, i + 0x04, 0x00000000);
|
|
+ }
|
|
+
|
|
+ /* PRAMIN page table, cheat and map into VM at 0x0000000000.
|
|
+ * We map the entire fake channel into the start of the PRAMIN BAR
|
|
+ */
|
|
+ ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, pt_size, 0x1000,
|
|
+ 0, &priv->pramin_pt);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ for (i = 0, v = c_offset; i < pt_size; i += 8, v += 0x1000) {
|
|
+ if (v < (c_offset + c_size))
|
|
+ BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, v | 1);
|
|
+ else
|
|
+ BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, 0x00000009);
|
|
+ BAR0_WI32(priv->pramin_pt->gpuobj, i + 4, 0x00000000);
|
|
+ }
|
|
+
|
|
+ BAR0_WI32(chan->vm_pd, 0x00, priv->pramin_pt->instance | 0x63);
|
|
+ BAR0_WI32(chan->vm_pd, 0x04, 0x00000000);
|
|
+
|
|
+ /* VRAM page table(s), mapped into VM at +1GiB */
|
|
+ for (i = 0; i < dev_priv->vm_vram_pt_nr; i++) {
|
|
+ ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0,
|
|
+ NV50_VM_BLOCK/65536*8, 0, 0,
|
|
+ &chan->vm_vram_pt[i]);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Error creating VRAM page tables: %d\n",
|
|
+ ret);
|
|
+ dev_priv->vm_vram_pt_nr = i;
|
|
+ return ret;
|
|
+ }
|
|
+ dev_priv->vm_vram_pt[i] = chan->vm_vram_pt[i]->gpuobj;
|
|
+
|
|
+ for (v = 0; v < dev_priv->vm_vram_pt[i]->im_pramin->size;
|
|
+ v += 4)
|
|
+ BAR0_WI32(dev_priv->vm_vram_pt[i], v, 0);
|
|
+
|
|
+ BAR0_WI32(chan->vm_pd, 0x10 + (i*8),
|
|
+ chan->vm_vram_pt[i]->instance | 0x61);
|
|
+ BAR0_WI32(chan->vm_pd, 0x14 + (i*8), 0);
|
|
+ }
|
|
+
|
|
+ /* DMA object for PRAMIN BAR */
|
|
+ ret = nouveau_gpuobj_new_ref(dev, chan, chan, 0, 6*4, 16, 0,
|
|
+ &priv->pramin_bar);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ BAR0_WI32(priv->pramin_bar->gpuobj, 0x00, 0x7fc00000);
|
|
+ BAR0_WI32(priv->pramin_bar->gpuobj, 0x04, dev_priv->ramin_size - 1);
|
|
+ BAR0_WI32(priv->pramin_bar->gpuobj, 0x08, 0x00000000);
|
|
+ BAR0_WI32(priv->pramin_bar->gpuobj, 0x0c, 0x00000000);
|
|
+ BAR0_WI32(priv->pramin_bar->gpuobj, 0x10, 0x00000000);
|
|
+ BAR0_WI32(priv->pramin_bar->gpuobj, 0x14, 0x00000000);
|
|
+
|
|
+ /* DMA object for FB BAR */
|
|
+ ret = nouveau_gpuobj_new_ref(dev, chan, chan, 0, 6*4, 16, 0,
|
|
+ &priv->fb_bar);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ BAR0_WI32(priv->fb_bar->gpuobj, 0x00, 0x7fc00000);
|
|
+ BAR0_WI32(priv->fb_bar->gpuobj, 0x04, 0x40000000 +
|
|
+ drm_get_resource_len(dev, 1) - 1);
|
|
+ BAR0_WI32(priv->fb_bar->gpuobj, 0x08, 0x40000000);
|
|
+ BAR0_WI32(priv->fb_bar->gpuobj, 0x0c, 0x00000000);
|
|
+ BAR0_WI32(priv->fb_bar->gpuobj, 0x10, 0x00000000);
|
|
+ BAR0_WI32(priv->fb_bar->gpuobj, 0x14, 0x00000000);
|
|
+
|
|
+ /* Poke the relevant regs, and pray it works :) */
|
|
+ nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->instance >> 12));
|
|
+ nv_wr32(dev, NV50_PUNK_UNK1710, 0);
|
|
+ nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->instance >> 12) |
|
|
+ NV50_PUNK_BAR_CFG_BASE_VALID);
|
|
+ nv_wr32(dev, NV50_PUNK_BAR1_CTXDMA, (priv->fb_bar->instance >> 4) |
|
|
+ NV50_PUNK_BAR1_CTXDMA_VALID);
|
|
+ nv_wr32(dev, NV50_PUNK_BAR3_CTXDMA, (priv->pramin_bar->instance >> 4) |
|
|
+ NV50_PUNK_BAR3_CTXDMA_VALID);
|
|
+
|
|
+ for (i = 0; i < 8; i++)
|
|
+ nv_wr32(dev, 0x1900 + (i*4), 0);
|
|
+
|
|
+ /* Assume that praying isn't enough, check that we can re-read the
|
|
+ * entire fake channel back from the PRAMIN BAR */
|
|
+ dev_priv->engine.instmem.prepare_access(dev, false);
|
|
+ for (i = 0; i < c_size; i += 4) {
|
|
+ if (nv_rd32(dev, NV_RAMIN + i) != nv_ri32(dev, i)) {
|
|
+ NV_ERROR(dev, "Error reading back PRAMIN at 0x%08x\n",
|
|
+ i);
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ }
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+
|
|
+ nv_wr32(dev, NV50_PUNK_BAR0_PRAMIN, save_nv001700);
|
|
+
|
|
+ /* Global PRAMIN heap */
|
|
+ if (nouveau_mem_init_heap(&dev_priv->ramin_heap,
|
|
+ c_size, dev_priv->ramin_size - c_size)) {
|
|
+ dev_priv->ramin_heap = NULL;
|
|
+ NV_ERROR(dev, "Failed to init RAMIN heap\n");
|
|
+ }
|
|
+
|
|
+ /*XXX: incorrect, but needed to make hash func "work" */
|
|
+ dev_priv->ramht_offset = 0x10000;
|
|
+ dev_priv->ramht_bits = 9;
|
|
+ dev_priv->ramht_size = (1 << dev_priv->ramht_bits);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv50_instmem_takedown(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
|
|
+ struct nouveau_channel *chan = dev_priv->fifos[0];
|
|
+ int i;
|
|
+
|
|
+ NV_DEBUG(dev, "\n");
|
|
+
|
|
+ if (!priv)
|
|
+ return;
|
|
+
|
|
+ /* Restore state from before init */
|
|
+ for (i = 0x1700; i <= 0x1710; i += 4)
|
|
+ nv_wr32(dev, i, priv->save1700[(i - 0x1700) / 4]);
|
|
+
|
|
+ nouveau_gpuobj_ref_del(dev, &priv->fb_bar);
|
|
+ nouveau_gpuobj_ref_del(dev, &priv->pramin_bar);
|
|
+ nouveau_gpuobj_ref_del(dev, &priv->pramin_pt);
|
|
+
|
|
+ /* Destroy dummy channel */
|
|
+ if (chan) {
|
|
+ for (i = 0; i < dev_priv->vm_vram_pt_nr; i++) {
|
|
+ nouveau_gpuobj_ref_del(dev, &chan->vm_vram_pt[i]);
|
|
+ dev_priv->vm_vram_pt[i] = NULL;
|
|
+ }
|
|
+ dev_priv->vm_vram_pt_nr = 0;
|
|
+
|
|
+ nouveau_gpuobj_del(dev, &chan->vm_pd);
|
|
+ nouveau_gpuobj_ref_del(dev, &chan->ramfc);
|
|
+ nouveau_gpuobj_ref_del(dev, &chan->ramin);
|
|
+ nouveau_mem_takedown(&chan->ramin_heap);
|
|
+
|
|
+ dev_priv->fifos[0] = dev_priv->fifos[127] = NULL;
|
|
+ kfree(chan);
|
|
+ }
|
|
+
|
|
+ dev_priv->engine.instmem.priv = NULL;
|
|
+ kfree(priv);
|
|
+}
|
|
+
|
|
+int
|
|
+nv50_instmem_suspend(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_channel *chan = dev_priv->fifos[0];
|
|
+ struct nouveau_gpuobj *ramin = chan->ramin->gpuobj;
|
|
+ int i;
|
|
+
|
|
+ ramin->im_backing_suspend = vmalloc(ramin->im_pramin->size);
|
|
+ if (!ramin->im_backing_suspend)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ for (i = 0; i < ramin->im_pramin->size; i += 4)
|
|
+ ramin->im_backing_suspend[i/4] = nv_ri32(dev, i);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv50_instmem_resume(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
|
|
+ struct nouveau_channel *chan = dev_priv->fifos[0];
|
|
+ struct nouveau_gpuobj *ramin = chan->ramin->gpuobj;
|
|
+ int i;
|
|
+
|
|
+ nv_wr32(dev, NV50_PUNK_BAR0_PRAMIN, (ramin->im_backing_start >> 16));
|
|
+ for (i = 0; i < ramin->im_pramin->size; i += 4)
|
|
+ BAR0_WI32(ramin, i, ramin->im_backing_suspend[i/4]);
|
|
+ vfree(ramin->im_backing_suspend);
|
|
+ ramin->im_backing_suspend = NULL;
|
|
+
|
|
+ /* Poke the relevant regs, and pray it works :) */
|
|
+ nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->instance >> 12));
|
|
+ nv_wr32(dev, NV50_PUNK_UNK1710, 0);
|
|
+ nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->instance >> 12) |
|
|
+ NV50_PUNK_BAR_CFG_BASE_VALID);
|
|
+ nv_wr32(dev, NV50_PUNK_BAR1_CTXDMA, (priv->fb_bar->instance >> 4) |
|
|
+ NV50_PUNK_BAR1_CTXDMA_VALID);
|
|
+ nv_wr32(dev, NV50_PUNK_BAR3_CTXDMA, (priv->pramin_bar->instance >> 4) |
|
|
+ NV50_PUNK_BAR3_CTXDMA_VALID);
|
|
+
|
|
+ for (i = 0; i < 8; i++)
|
|
+ nv_wr32(dev, 0x1900 + (i*4), 0);
|
|
+}
|
|
+
|
|
+int
|
|
+nv50_instmem_populate(struct drm_device *dev, struct nouveau_gpuobj *gpuobj,
|
|
+ uint32_t *sz)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ if (gpuobj->im_backing)
|
|
+ return -EINVAL;
|
|
+
|
|
+ *sz = (*sz + (NV50_INSTMEM_PAGE_SIZE-1)) & ~(NV50_INSTMEM_PAGE_SIZE-1);
|
|
+ if (*sz == 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ ret = nouveau_bo_new(dev, NULL, *sz, 0, TTM_PL_FLAG_VRAM, 0, 0x0000,
|
|
+ true, false, &gpuobj->im_backing);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "error getting PRAMIN backing pages: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = nouveau_bo_pin(gpuobj->im_backing, TTM_PL_FLAG_VRAM);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "error pinning PRAMIN backing VRAM: %d\n", ret);
|
|
+ nouveau_bo_ref(NULL, &gpuobj->im_backing);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ gpuobj->im_backing_start = gpuobj->im_backing->bo.mem.mm_node->start;
|
|
+ gpuobj->im_backing_start <<= PAGE_SHIFT;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv50_instmem_clear(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+
|
|
+ if (gpuobj && gpuobj->im_backing) {
|
|
+ if (gpuobj->im_bound)
|
|
+ dev_priv->engine.instmem.unbind(dev, gpuobj);
|
|
+ nouveau_bo_unpin(gpuobj->im_backing);
|
|
+ nouveau_bo_ref(NULL, &gpuobj->im_backing);
|
|
+ gpuobj->im_backing = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+int
|
|
+nv50_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
|
|
+ uint32_t pte, pte_end, vram;
|
|
+
|
|
+ if (!gpuobj->im_backing || !gpuobj->im_pramin || gpuobj->im_bound)
|
|
+ return -EINVAL;
|
|
+
|
|
+ NV_DEBUG(dev, "st=0x%0llx sz=0x%0llx\n",
|
|
+ gpuobj->im_pramin->start, gpuobj->im_pramin->size);
|
|
+
|
|
+ pte = (gpuobj->im_pramin->start >> 12) << 3;
|
|
+ pte_end = ((gpuobj->im_pramin->size >> 12) << 3) + pte;
|
|
+ vram = gpuobj->im_backing_start;
|
|
+
|
|
+ NV_DEBUG(dev, "pramin=0x%llx, pte=%d, pte_end=%d\n",
|
|
+ gpuobj->im_pramin->start, pte, pte_end);
|
|
+ NV_DEBUG(dev, "first vram page: 0x%08x\n", gpuobj->im_backing_start);
|
|
+
|
|
+ dev_priv->engine.instmem.prepare_access(dev, true);
|
|
+ while (pte < pte_end) {
|
|
+ nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 0)/4, vram | 1);
|
|
+ nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 4)/4, 0x00000000);
|
|
+
|
|
+ pte += 8;
|
|
+ vram += NV50_INSTMEM_PAGE_SIZE;
|
|
+ }
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+
|
|
+ nv_wr32(dev, 0x100c80, 0x00040001);
|
|
+ if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
|
|
+ NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (1)\n");
|
|
+ NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ nv_wr32(dev, 0x100c80, 0x00060001);
|
|
+ if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
|
|
+ NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
|
|
+ NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ gpuobj->im_bound = 1;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+nv50_instmem_unbind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
|
|
+ uint32_t pte, pte_end;
|
|
+
|
|
+ if (gpuobj->im_bound == 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ pte = (gpuobj->im_pramin->start >> 12) << 3;
|
|
+ pte_end = ((gpuobj->im_pramin->size >> 12) << 3) + pte;
|
|
+
|
|
+ dev_priv->engine.instmem.prepare_access(dev, true);
|
|
+ while (pte < pte_end) {
|
|
+ nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 0)/4, 0x00000009);
|
|
+ nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 4)/4, 0x00000000);
|
|
+ pte += 8;
|
|
+ }
|
|
+ dev_priv->engine.instmem.finish_access(dev);
|
|
+
|
|
+ gpuobj->im_bound = 0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+nv50_instmem_prepare_access(struct drm_device *dev, bool write)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
|
|
+
|
|
+ priv->last_access_wr = write;
|
|
+}
|
|
+
|
|
+void
|
|
+nv50_instmem_finish_access(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
|
|
+
|
|
+ if (priv->last_access_wr) {
|
|
+ nv_wr32(dev, 0x070000, 0x00000001);
|
|
+ if (!nv_wait(0x070000, 0x00000001, 0x00000000))
|
|
+ NV_ERROR(dev, "PRAMIN flush timeout\n");
|
|
+ }
|
|
+}
|
|
+
|
|
diff --git a/drivers/gpu/drm/nouveau/nv50_mc.c b/drivers/gpu/drm/nouveau/nv50_mc.c
|
|
new file mode 100644
|
|
index 0000000..e0a9c3f
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv50_mc.c
|
|
@@ -0,0 +1,40 @@
|
|
+/*
|
|
+ * Copyright (C) 2007 Ben Skeggs.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm.h"
|
|
+#include "nouveau_drv.h"
|
|
+
|
|
+int
|
|
+nv50_mc_init(struct drm_device *dev)
|
|
+{
|
|
+ nv_wr32(dev, NV03_PMC_ENABLE, 0xFFFFFFFF);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void nv50_mc_takedown(struct drm_device *dev)
|
|
+{
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c
|
|
new file mode 100644
|
|
index 0000000..c2fff54
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv50_sor.c
|
|
@@ -0,0 +1,323 @@
|
|
+/*
|
|
+ * Copyright (C) 2008 Maarten Maathuis.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "drm_crtc_helper.h"
|
|
+
|
|
+#define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO)
|
|
+#include "nouveau_reg.h"
|
|
+#include "nouveau_drv.h"
|
|
+#include "nouveau_dma.h"
|
|
+#include "nouveau_encoder.h"
|
|
+#include "nouveau_connector.h"
|
|
+#include "nouveau_crtc.h"
|
|
+#include "nv50_display.h"
|
|
+
|
|
+static void
|
|
+nv50_sor_disconnect(struct nouveau_encoder *nv_encoder)
|
|
+{
|
|
+ struct drm_device *dev = to_drm_encoder(nv_encoder)->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_channel *evo = dev_priv->evo;
|
|
+ int ret;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "Disconnecting SOR %d\n", nv_encoder->or);
|
|
+
|
|
+ ret = RING_SPACE(evo, 2);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "no space while disconnecting SOR\n");
|
|
+ return;
|
|
+ }
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1);
|
|
+ OUT_RING(evo, 0);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_sor_dp_link_train(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ struct bit_displayport_encoder_table *dpe;
|
|
+ int dpe_headerlen;
|
|
+
|
|
+ dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
|
|
+ if (!dpe) {
|
|
+ NV_ERROR(dev, "SOR-%d: no DP encoder table!\n", nv_encoder->or);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (dpe->script0) {
|
|
+ NV_DEBUG_KMS(dev, "SOR-%d: running DP script 0\n", nv_encoder->or);
|
|
+ nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script0),
|
|
+ nv_encoder->dcb);
|
|
+ }
|
|
+
|
|
+ if (!nouveau_dp_link_train(encoder))
|
|
+ NV_ERROR(dev, "SOR-%d: link training failed\n", nv_encoder->or);
|
|
+
|
|
+ if (dpe->script1) {
|
|
+ NV_DEBUG_KMS(dev, "SOR-%d: running DP script 1\n", nv_encoder->or);
|
|
+ nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script1),
|
|
+ nv_encoder->dcb);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_sor_dpms(struct drm_encoder *encoder, int mode)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ struct drm_encoder *enc;
|
|
+ uint32_t val;
|
|
+ int or = nv_encoder->or;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "or %d mode %d\n", or, mode);
|
|
+
|
|
+ nv_encoder->last_dpms = mode;
|
|
+ list_for_each_entry(enc, &dev->mode_config.encoder_list, head) {
|
|
+ struct nouveau_encoder *nvenc = nouveau_encoder(enc);
|
|
+
|
|
+ if (nvenc == nv_encoder ||
|
|
+ nvenc->disconnect != nv50_sor_disconnect ||
|
|
+ nvenc->dcb->or != nv_encoder->dcb->or)
|
|
+ continue;
|
|
+
|
|
+ if (nvenc->last_dpms == DRM_MODE_DPMS_ON)
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* wait for it to be done */
|
|
+ if (!nv_wait(NV50_PDISPLAY_SOR_DPMS_CTRL(or),
|
|
+ NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING, 0)) {
|
|
+ NV_ERROR(dev, "timeout: SOR_DPMS_CTRL_PENDING(%d) == 0\n", or);
|
|
+ NV_ERROR(dev, "SOR_DPMS_CTRL(%d) = 0x%08x\n", or,
|
|
+ nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or)));
|
|
+ }
|
|
+
|
|
+ val = nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or));
|
|
+
|
|
+ if (mode == DRM_MODE_DPMS_ON)
|
|
+ val |= NV50_PDISPLAY_SOR_DPMS_CTRL_ON;
|
|
+ else
|
|
+ val &= ~NV50_PDISPLAY_SOR_DPMS_CTRL_ON;
|
|
+
|
|
+ nv_wr32(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or), val |
|
|
+ NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING);
|
|
+ if (!nv_wait(NV50_PDISPLAY_SOR_DPMS_STATE(or),
|
|
+ NV50_PDISPLAY_SOR_DPMS_STATE_WAIT, 0)) {
|
|
+ NV_ERROR(dev, "timeout: SOR_DPMS_STATE_WAIT(%d) == 0\n", or);
|
|
+ NV_ERROR(dev, "SOR_DPMS_STATE(%d) = 0x%08x\n", or,
|
|
+ nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_STATE(or)));
|
|
+ }
|
|
+
|
|
+ if (nv_encoder->dcb->type == OUTPUT_DP && mode == DRM_MODE_DPMS_ON)
|
|
+ nv50_sor_dp_link_train(encoder);
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_sor_save(struct drm_encoder *encoder)
|
|
+{
|
|
+ NV_ERROR(encoder->dev, "!!\n");
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_sor_restore(struct drm_encoder *encoder)
|
|
+{
|
|
+ NV_ERROR(encoder->dev, "!!\n");
|
|
+}
|
|
+
|
|
+static bool
|
|
+nv50_sor_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
|
|
+ struct drm_display_mode *adjusted_mode)
|
|
+{
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ struct nouveau_connector *connector;
|
|
+
|
|
+ NV_DEBUG_KMS(encoder->dev, "or %d\n", nv_encoder->or);
|
|
+
|
|
+ connector = nouveau_encoder_connector_get(nv_encoder);
|
|
+ if (!connector) {
|
|
+ NV_ERROR(encoder->dev, "Encoder has no connector\n");
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (connector->scaling_mode != DRM_MODE_SCALE_NONE &&
|
|
+ connector->native_mode) {
|
|
+ int id = adjusted_mode->base.id;
|
|
+ *adjusted_mode = *connector->native_mode;
|
|
+ adjusted_mode->base.id = id;
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_sor_prepare(struct drm_encoder *encoder)
|
|
+{
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_sor_commit(struct drm_encoder *encoder)
|
|
+{
|
|
+}
|
|
+
|
|
+static void
|
|
+nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
|
|
+ struct drm_display_mode *adjusted_mode)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = encoder->dev->dev_private;
|
|
+ struct nouveau_channel *evo = dev_priv->evo;
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc);
|
|
+ uint32_t mode_ctl = 0;
|
|
+ int ret;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "or %d\n", nv_encoder->or);
|
|
+
|
|
+ nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON);
|
|
+
|
|
+ switch (nv_encoder->dcb->type) {
|
|
+ case OUTPUT_TMDS:
|
|
+ if (nv_encoder->dcb->sorconf.link & 1) {
|
|
+ if (adjusted_mode->clock < 165000)
|
|
+ mode_ctl = 0x0100;
|
|
+ else
|
|
+ mode_ctl = 0x0500;
|
|
+ } else
|
|
+ mode_ctl = 0x0200;
|
|
+ break;
|
|
+ case OUTPUT_DP:
|
|
+ mode_ctl |= 0x00050000;
|
|
+ if (nv_encoder->dcb->sorconf.link & 1)
|
|
+ mode_ctl |= 0x00000800;
|
|
+ else
|
|
+ mode_ctl |= 0x00000900;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (crtc->index == 1)
|
|
+ mode_ctl |= NV50_EVO_SOR_MODE_CTRL_CRTC1;
|
|
+ else
|
|
+ mode_ctl |= NV50_EVO_SOR_MODE_CTRL_CRTC0;
|
|
+
|
|
+ if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
|
|
+ mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NHSYNC;
|
|
+
|
|
+ if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
|
|
+ mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NVSYNC;
|
|
+
|
|
+ ret = RING_SPACE(evo, 2);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "no space while connecting SOR\n");
|
|
+ return;
|
|
+ }
|
|
+ BEGIN_RING(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1);
|
|
+ OUT_RING(evo, mode_ctl);
|
|
+}
|
|
+
|
|
+static const struct drm_encoder_helper_funcs nv50_sor_helper_funcs = {
|
|
+ .dpms = nv50_sor_dpms,
|
|
+ .save = nv50_sor_save,
|
|
+ .restore = nv50_sor_restore,
|
|
+ .mode_fixup = nv50_sor_mode_fixup,
|
|
+ .prepare = nv50_sor_prepare,
|
|
+ .commit = nv50_sor_commit,
|
|
+ .mode_set = nv50_sor_mode_set,
|
|
+ .detect = NULL
|
|
+};
|
|
+
|
|
+static void
|
|
+nv50_sor_destroy(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
+
|
|
+ if (!encoder)
|
|
+ return;
|
|
+
|
|
+ NV_DEBUG_KMS(encoder->dev, "\n");
|
|
+
|
|
+ drm_encoder_cleanup(encoder);
|
|
+
|
|
+ kfree(nv_encoder);
|
|
+}
|
|
+
|
|
+static const struct drm_encoder_funcs nv50_sor_encoder_funcs = {
|
|
+ .destroy = nv50_sor_destroy,
|
|
+};
|
|
+
|
|
+int
|
|
+nv50_sor_create(struct drm_device *dev, struct dcb_entry *entry)
|
|
+{
|
|
+ struct nouveau_encoder *nv_encoder = NULL;
|
|
+ struct drm_encoder *encoder;
|
|
+ bool dum;
|
|
+ int type;
|
|
+
|
|
+ NV_DEBUG_KMS(dev, "\n");
|
|
+
|
|
+ switch (entry->type) {
|
|
+ case OUTPUT_TMDS:
|
|
+ NV_INFO(dev, "Detected a TMDS output\n");
|
|
+ type = DRM_MODE_ENCODER_TMDS;
|
|
+ break;
|
|
+ case OUTPUT_LVDS:
|
|
+ NV_INFO(dev, "Detected a LVDS output\n");
|
|
+ type = DRM_MODE_ENCODER_LVDS;
|
|
+
|
|
+ if (nouveau_bios_parse_lvds_table(dev, 0, &dum, &dum)) {
|
|
+ NV_ERROR(dev, "Failed parsing LVDS table\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ break;
|
|
+ case OUTPUT_DP:
|
|
+ NV_INFO(dev, "Detected a DP output\n");
|
|
+ type = DRM_MODE_ENCODER_TMDS;
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
|
|
+ if (!nv_encoder)
|
|
+ return -ENOMEM;
|
|
+ encoder = to_drm_encoder(nv_encoder);
|
|
+
|
|
+ nv_encoder->dcb = entry;
|
|
+ nv_encoder->or = ffs(entry->or) - 1;
|
|
+
|
|
+ nv_encoder->disconnect = nv50_sor_disconnect;
|
|
+
|
|
+ drm_encoder_init(dev, encoder, &nv50_sor_encoder_funcs, type);
|
|
+ drm_encoder_helper_add(encoder, &nv50_sor_helper_funcs);
|
|
+
|
|
+ encoder->possible_crtcs = entry->heads;
|
|
+ encoder->possible_clones = 0;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/nouveau/nvreg.h b/drivers/gpu/drm/nouveau/nvreg.h
|
|
new file mode 100644
|
|
index 0000000..5998c35
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nvreg.h
|
|
@@ -0,0 +1,535 @@
|
|
+/* $XConsortium: nvreg.h /main/2 1996/10/28 05:13:41 kaleb $ */
|
|
+/*
|
|
+ * Copyright 1996-1997 David J. McKay
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice shall be included in
|
|
+ * all copies or substantial portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * DAVID J. MCKAY BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
|
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
+ * SOFTWARE.
|
|
+ */
|
|
+
|
|
+/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/nv/nvreg.h,v 1.6 2002/01/25 21:56:06 tsi Exp $ */
|
|
+
|
|
+#ifndef __NVREG_H_
|
|
+#define __NVREG_H_
|
|
+
|
|
+#define NV_PMC_OFFSET 0x00000000
|
|
+#define NV_PMC_SIZE 0x00001000
|
|
+
|
|
+#define NV_PBUS_OFFSET 0x00001000
|
|
+#define NV_PBUS_SIZE 0x00001000
|
|
+
|
|
+#define NV_PFIFO_OFFSET 0x00002000
|
|
+#define NV_PFIFO_SIZE 0x00002000
|
|
+
|
|
+#define NV_HDIAG_OFFSET 0x00005000
|
|
+#define NV_HDIAG_SIZE 0x00001000
|
|
+
|
|
+#define NV_PRAM_OFFSET 0x00006000
|
|
+#define NV_PRAM_SIZE 0x00001000
|
|
+
|
|
+#define NV_PVIDEO_OFFSET 0x00008000
|
|
+#define NV_PVIDEO_SIZE 0x00001000
|
|
+
|
|
+#define NV_PTIMER_OFFSET 0x00009000
|
|
+#define NV_PTIMER_SIZE 0x00001000
|
|
+
|
|
+#define NV_PPM_OFFSET 0x0000A000
|
|
+#define NV_PPM_SIZE 0x00001000
|
|
+
|
|
+#define NV_PTV_OFFSET 0x0000D000
|
|
+#define NV_PTV_SIZE 0x00001000
|
|
+
|
|
+#define NV_PRMVGA_OFFSET 0x000A0000
|
|
+#define NV_PRMVGA_SIZE 0x00020000
|
|
+
|
|
+#define NV_PRMVIO0_OFFSET 0x000C0000
|
|
+#define NV_PRMVIO_SIZE 0x00002000
|
|
+#define NV_PRMVIO1_OFFSET 0x000C2000
|
|
+
|
|
+#define NV_PFB_OFFSET 0x00100000
|
|
+#define NV_PFB_SIZE 0x00001000
|
|
+
|
|
+#define NV_PEXTDEV_OFFSET 0x00101000
|
|
+#define NV_PEXTDEV_SIZE 0x00001000
|
|
+
|
|
+#define NV_PME_OFFSET 0x00200000
|
|
+#define NV_PME_SIZE 0x00001000
|
|
+
|
|
+#define NV_PROM_OFFSET 0x00300000
|
|
+#define NV_PROM_SIZE 0x00010000
|
|
+
|
|
+#define NV_PGRAPH_OFFSET 0x00400000
|
|
+#define NV_PGRAPH_SIZE 0x00010000
|
|
+
|
|
+#define NV_PCRTC0_OFFSET 0x00600000
|
|
+#define NV_PCRTC0_SIZE 0x00002000 /* empirical */
|
|
+
|
|
+#define NV_PRMCIO0_OFFSET 0x00601000
|
|
+#define NV_PRMCIO_SIZE 0x00002000
|
|
+#define NV_PRMCIO1_OFFSET 0x00603000
|
|
+
|
|
+#define NV50_DISPLAY_OFFSET 0x00610000
|
|
+#define NV50_DISPLAY_SIZE 0x0000FFFF
|
|
+
|
|
+#define NV_PRAMDAC0_OFFSET 0x00680000
|
|
+#define NV_PRAMDAC0_SIZE 0x00002000
|
|
+
|
|
+#define NV_PRMDIO0_OFFSET 0x00681000
|
|
+#define NV_PRMDIO_SIZE 0x00002000
|
|
+#define NV_PRMDIO1_OFFSET 0x00683000
|
|
+
|
|
+#define NV_PRAMIN_OFFSET 0x00700000
|
|
+#define NV_PRAMIN_SIZE 0x00100000
|
|
+
|
|
+#define NV_FIFO_OFFSET 0x00800000
|
|
+#define NV_FIFO_SIZE 0x00800000
|
|
+
|
|
+#define NV_PMC_BOOT_0 0x00000000
|
|
+#define NV_PMC_ENABLE 0x00000200
|
|
+
|
|
+#define NV_VIO_VSE2 0x000003c3
|
|
+#define NV_VIO_SRX 0x000003c4
|
|
+
|
|
+#define NV_CIO_CRX__COLOR 0x000003d4
|
|
+#define NV_CIO_CR__COLOR 0x000003d5
|
|
+
|
|
+#define NV_PBUS_DEBUG_1 0x00001084
|
|
+#define NV_PBUS_DEBUG_4 0x00001098
|
|
+#define NV_PBUS_DEBUG_DUALHEAD_CTL 0x000010f0
|
|
+#define NV_PBUS_POWERCTRL_1 0x00001584
|
|
+#define NV_PBUS_POWERCTRL_2 0x00001588
|
|
+#define NV_PBUS_POWERCTRL_4 0x00001590
|
|
+#define NV_PBUS_PCI_NV_19 0x0000184C
|
|
+#define NV_PBUS_PCI_NV_20 0x00001850
|
|
+# define NV_PBUS_PCI_NV_20_ROM_SHADOW_DISABLED (0 << 0)
|
|
+# define NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED (1 << 0)
|
|
+
|
|
+#define NV_PFIFO_RAMHT 0x00002210
|
|
+
|
|
+#define NV_PTV_TV_INDEX 0x0000d220
|
|
+#define NV_PTV_TV_DATA 0x0000d224
|
|
+#define NV_PTV_HFILTER 0x0000d310
|
|
+#define NV_PTV_HFILTER2 0x0000d390
|
|
+#define NV_PTV_VFILTER 0x0000d510
|
|
+
|
|
+#define NV_PRMVIO_MISC__WRITE 0x000c03c2
|
|
+#define NV_PRMVIO_SRX 0x000c03c4
|
|
+#define NV_PRMVIO_SR 0x000c03c5
|
|
+# define NV_VIO_SR_RESET_INDEX 0x00
|
|
+# define NV_VIO_SR_CLOCK_INDEX 0x01
|
|
+# define NV_VIO_SR_PLANE_MASK_INDEX 0x02
|
|
+# define NV_VIO_SR_CHAR_MAP_INDEX 0x03
|
|
+# define NV_VIO_SR_MEM_MODE_INDEX 0x04
|
|
+#define NV_PRMVIO_MISC__READ 0x000c03cc
|
|
+#define NV_PRMVIO_GRX 0x000c03ce
|
|
+#define NV_PRMVIO_GX 0x000c03cf
|
|
+# define NV_VIO_GX_SR_INDEX 0x00
|
|
+# define NV_VIO_GX_SREN_INDEX 0x01
|
|
+# define NV_VIO_GX_CCOMP_INDEX 0x02
|
|
+# define NV_VIO_GX_ROP_INDEX 0x03
|
|
+# define NV_VIO_GX_READ_MAP_INDEX 0x04
|
|
+# define NV_VIO_GX_MODE_INDEX 0x05
|
|
+# define NV_VIO_GX_MISC_INDEX 0x06
|
|
+# define NV_VIO_GX_DONT_CARE_INDEX 0x07
|
|
+# define NV_VIO_GX_BIT_MASK_INDEX 0x08
|
|
+
|
|
+#define NV_PFB_BOOT_0 0x00100000
|
|
+#define NV_PFB_CFG0 0x00100200
|
|
+#define NV_PFB_CFG1 0x00100204
|
|
+#define NV_PFB_CSTATUS 0x0010020C
|
|
+#define NV_PFB_REFCTRL 0x00100210
|
|
+# define NV_PFB_REFCTRL_VALID_1 (1 << 31)
|
|
+#define NV_PFB_PAD 0x0010021C
|
|
+# define NV_PFB_PAD_CKE_NORMAL (1 << 0)
|
|
+#define NV_PFB_TILE_NV10 0x00100240
|
|
+#define NV_PFB_TILE_SIZE_NV10 0x00100244
|
|
+#define NV_PFB_REF 0x001002D0
|
|
+# define NV_PFB_REF_CMD_REFRESH (1 << 0)
|
|
+#define NV_PFB_PRE 0x001002D4
|
|
+# define NV_PFB_PRE_CMD_PRECHARGE (1 << 0)
|
|
+#define NV_PFB_CLOSE_PAGE2 0x0010033C
|
|
+#define NV_PFB_TILE_NV40 0x00100600
|
|
+#define NV_PFB_TILE_SIZE_NV40 0x00100604
|
|
+
|
|
+#define NV_PEXTDEV_BOOT_0 0x00101000
|
|
+# define NV_PEXTDEV_BOOT_0_STRAP_FP_IFACE_12BIT (8 << 12)
|
|
+#define NV_PEXTDEV_BOOT_3 0x0010100c
|
|
+
|
|
+#define NV_PCRTC_INTR_0 0x00600100
|
|
+# define NV_PCRTC_INTR_0_VBLANK (1 << 0)
|
|
+#define NV_PCRTC_INTR_EN_0 0x00600140
|
|
+#define NV_PCRTC_START 0x00600800
|
|
+#define NV_PCRTC_CONFIG 0x00600804
|
|
+# define NV_PCRTC_CONFIG_START_ADDRESS_NON_VGA (1 << 0)
|
|
+# define NV_PCRTC_CONFIG_START_ADDRESS_HSYNC (2 << 0)
|
|
+#define NV_PCRTC_CURSOR_CONFIG 0x00600810
|
|
+# define NV_PCRTC_CURSOR_CONFIG_ENABLE_ENABLE (1 << 0)
|
|
+# define NV_PCRTC_CURSOR_CONFIG_DOUBLE_SCAN_ENABLE (1 << 4)
|
|
+# define NV_PCRTC_CURSOR_CONFIG_ADDRESS_SPACE_PNVM (1 << 8)
|
|
+# define NV_PCRTC_CURSOR_CONFIG_CUR_BPP_32 (1 << 12)
|
|
+# define NV_PCRTC_CURSOR_CONFIG_CUR_PIXELS_64 (1 << 16)
|
|
+# define NV_PCRTC_CURSOR_CONFIG_CUR_LINES_32 (2 << 24)
|
|
+# define NV_PCRTC_CURSOR_CONFIG_CUR_LINES_64 (4 << 24)
|
|
+# define NV_PCRTC_CURSOR_CONFIG_CUR_BLEND_ALPHA (1 << 28)
|
|
+
|
|
+/* note: PCRTC_GPIO is not available on nv10, and in fact aliases 0x600810 */
|
|
+#define NV_PCRTC_GPIO 0x00600818
|
|
+#define NV_PCRTC_GPIO_EXT 0x0060081c
|
|
+#define NV_PCRTC_830 0x00600830
|
|
+#define NV_PCRTC_834 0x00600834
|
|
+#define NV_PCRTC_850 0x00600850
|
|
+#define NV_PCRTC_ENGINE_CTRL 0x00600860
|
|
+# define NV_CRTC_FSEL_I2C (1 << 4)
|
|
+# define NV_CRTC_FSEL_OVERLAY (1 << 12)
|
|
+
|
|
+#define NV_PRMCIO_ARX 0x006013c0
|
|
+#define NV_PRMCIO_AR__WRITE 0x006013c0
|
|
+#define NV_PRMCIO_AR__READ 0x006013c1
|
|
+# define NV_CIO_AR_MODE_INDEX 0x10
|
|
+# define NV_CIO_AR_OSCAN_INDEX 0x11
|
|
+# define NV_CIO_AR_PLANE_INDEX 0x12
|
|
+# define NV_CIO_AR_HPP_INDEX 0x13
|
|
+# define NV_CIO_AR_CSEL_INDEX 0x14
|
|
+#define NV_PRMCIO_INP0 0x006013c2
|
|
+#define NV_PRMCIO_CRX__COLOR 0x006013d4
|
|
+#define NV_PRMCIO_CR__COLOR 0x006013d5
|
|
+ /* Standard VGA CRTC registers */
|
|
+# define NV_CIO_CR_HDT_INDEX 0x00 /* horizontal display total */
|
|
+# define NV_CIO_CR_HDE_INDEX 0x01 /* horizontal display end */
|
|
+# define NV_CIO_CR_HBS_INDEX 0x02 /* horizontal blanking start */
|
|
+# define NV_CIO_CR_HBE_INDEX 0x03 /* horizontal blanking end */
|
|
+# define NV_CIO_CR_HBE_4_0 4:0
|
|
+# define NV_CIO_CR_HRS_INDEX 0x04 /* horizontal retrace start */
|
|
+# define NV_CIO_CR_HRE_INDEX 0x05 /* horizontal retrace end */
|
|
+# define NV_CIO_CR_HRE_4_0 4:0
|
|
+# define NV_CIO_CR_HRE_HBE_5 7:7
|
|
+# define NV_CIO_CR_VDT_INDEX 0x06 /* vertical display total */
|
|
+# define NV_CIO_CR_OVL_INDEX 0x07 /* overflow bits */
|
|
+# define NV_CIO_CR_OVL_VDT_8 0:0
|
|
+# define NV_CIO_CR_OVL_VDE_8 1:1
|
|
+# define NV_CIO_CR_OVL_VRS_8 2:2
|
|
+# define NV_CIO_CR_OVL_VBS_8 3:3
|
|
+# define NV_CIO_CR_OVL_VDT_9 5:5
|
|
+# define NV_CIO_CR_OVL_VDE_9 6:6
|
|
+# define NV_CIO_CR_OVL_VRS_9 7:7
|
|
+# define NV_CIO_CR_RSAL_INDEX 0x08 /* normally "preset row scan" */
|
|
+# define NV_CIO_CR_CELL_HT_INDEX 0x09 /* cell height?! normally "max scan line" */
|
|
+# define NV_CIO_CR_CELL_HT_VBS_9 5:5
|
|
+# define NV_CIO_CR_CELL_HT_SCANDBL 7:7
|
|
+# define NV_CIO_CR_CURS_ST_INDEX 0x0a /* cursor start */
|
|
+# define NV_CIO_CR_CURS_END_INDEX 0x0b /* cursor end */
|
|
+# define NV_CIO_CR_SA_HI_INDEX 0x0c /* screen start address high */
|
|
+# define NV_CIO_CR_SA_LO_INDEX 0x0d /* screen start address low */
|
|
+# define NV_CIO_CR_TCOFF_HI_INDEX 0x0e /* cursor offset high */
|
|
+# define NV_CIO_CR_TCOFF_LO_INDEX 0x0f /* cursor offset low */
|
|
+# define NV_CIO_CR_VRS_INDEX 0x10 /* vertical retrace start */
|
|
+# define NV_CIO_CR_VRE_INDEX 0x11 /* vertical retrace end */
|
|
+# define NV_CIO_CR_VRE_3_0 3:0
|
|
+# define NV_CIO_CR_VDE_INDEX 0x12 /* vertical display end */
|
|
+# define NV_CIO_CR_OFFSET_INDEX 0x13 /* sets screen pitch */
|
|
+# define NV_CIO_CR_ULINE_INDEX 0x14 /* underline location */
|
|
+# define NV_CIO_CR_VBS_INDEX 0x15 /* vertical blank start */
|
|
+# define NV_CIO_CR_VBE_INDEX 0x16 /* vertical blank end */
|
|
+# define NV_CIO_CR_MODE_INDEX 0x17 /* crtc mode control */
|
|
+# define NV_CIO_CR_LCOMP_INDEX 0x18 /* line compare */
|
|
+ /* Extended VGA CRTC registers */
|
|
+# define NV_CIO_CRE_RPC0_INDEX 0x19 /* repaint control 0 */
|
|
+# define NV_CIO_CRE_RPC0_OFFSET_10_8 7:5
|
|
+# define NV_CIO_CRE_RPC1_INDEX 0x1a /* repaint control 1 */
|
|
+# define NV_CIO_CRE_RPC1_LARGE 2:2
|
|
+# define NV_CIO_CRE_FF_INDEX 0x1b /* fifo control */
|
|
+# define NV_CIO_CRE_ENH_INDEX 0x1c /* enhanced? */
|
|
+# define NV_CIO_SR_LOCK_INDEX 0x1f /* crtc lock */
|
|
+# define NV_CIO_SR_UNLOCK_RW_VALUE 0x57
|
|
+# define NV_CIO_SR_LOCK_VALUE 0x99
|
|
+# define NV_CIO_CRE_FFLWM__INDEX 0x20 /* fifo low water mark */
|
|
+# define NV_CIO_CRE_21 0x21 /* vga shadow crtc lock */
|
|
+# define NV_CIO_CRE_LSR_INDEX 0x25 /* ? */
|
|
+# define NV_CIO_CRE_LSR_VDT_10 0:0
|
|
+# define NV_CIO_CRE_LSR_VDE_10 1:1
|
|
+# define NV_CIO_CRE_LSR_VRS_10 2:2
|
|
+# define NV_CIO_CRE_LSR_VBS_10 3:3
|
|
+# define NV_CIO_CRE_LSR_HBE_6 4:4
|
|
+# define NV_CIO_CR_ARX_INDEX 0x26 /* attribute index -- ro copy of 0x60.3c0 */
|
|
+# define NV_CIO_CRE_CHIP_ID_INDEX 0x27 /* chip revision */
|
|
+# define NV_CIO_CRE_PIXEL_INDEX 0x28
|
|
+# define NV_CIO_CRE_PIXEL_FORMAT 1:0
|
|
+# define NV_CIO_CRE_HEB__INDEX 0x2d /* horizontal extra bits? */
|
|
+# define NV_CIO_CRE_HEB_HDT_8 0:0
|
|
+# define NV_CIO_CRE_HEB_HDE_8 1:1
|
|
+# define NV_CIO_CRE_HEB_HBS_8 2:2
|
|
+# define NV_CIO_CRE_HEB_HRS_8 3:3
|
|
+# define NV_CIO_CRE_HEB_ILC_8 4:4
|
|
+# define NV_CIO_CRE_2E 0x2e /* some scratch or dummy reg to force writes to sink in */
|
|
+# define NV_CIO_CRE_HCUR_ADDR2_INDEX 0x2f /* cursor */
|
|
+# define NV_CIO_CRE_HCUR_ADDR0_INDEX 0x30 /* pixmap */
|
|
+# define NV_CIO_CRE_HCUR_ADDR0_ADR 6:0
|
|
+# define NV_CIO_CRE_HCUR_ASI 7:7
|
|
+# define NV_CIO_CRE_HCUR_ADDR1_INDEX 0x31 /* address */
|
|
+# define NV_CIO_CRE_HCUR_ADDR1_ENABLE 0:0
|
|
+# define NV_CIO_CRE_HCUR_ADDR1_CUR_DBL 1:1
|
|
+# define NV_CIO_CRE_HCUR_ADDR1_ADR 7:2
|
|
+# define NV_CIO_CRE_LCD__INDEX 0x33
|
|
+# define NV_CIO_CRE_LCD_LCD_SELECT 0:0
|
|
+# define NV_CIO_CRE_DDC0_STATUS__INDEX 0x36
|
|
+# define NV_CIO_CRE_DDC0_WR__INDEX 0x37
|
|
+# define NV_CIO_CRE_ILACE__INDEX 0x39 /* interlace */
|
|
+# define NV_CIO_CRE_SCRATCH3__INDEX 0x3b
|
|
+# define NV_CIO_CRE_SCRATCH4__INDEX 0x3c
|
|
+# define NV_CIO_CRE_DDC_STATUS__INDEX 0x3e
|
|
+# define NV_CIO_CRE_DDC_WR__INDEX 0x3f
|
|
+# define NV_CIO_CRE_EBR_INDEX 0x41 /* extra bits ? (vertical) */
|
|
+# define NV_CIO_CRE_EBR_VDT_11 0:0
|
|
+# define NV_CIO_CRE_EBR_VDE_11 2:2
|
|
+# define NV_CIO_CRE_EBR_VRS_11 4:4
|
|
+# define NV_CIO_CRE_EBR_VBS_11 6:6
|
|
+# define NV_CIO_CRE_43 0x43
|
|
+# define NV_CIO_CRE_44 0x44 /* head control */
|
|
+# define NV_CIO_CRE_CSB 0x45 /* colour saturation boost */
|
|
+# define NV_CIO_CRE_RCR 0x46
|
|
+# define NV_CIO_CRE_RCR_ENDIAN_BIG 7:7
|
|
+# define NV_CIO_CRE_47 0x47 /* extended fifo lwm, used on nv30+ */
|
|
+# define NV_CIO_CRE_49 0x49
|
|
+# define NV_CIO_CRE_4B 0x4b /* given patterns in 0x[2-3][a-c] regs, probably scratch 6 */
|
|
+# define NV_CIO_CRE_TVOUT_LATENCY 0x52
|
|
+# define NV_CIO_CRE_53 0x53 /* `fp_htiming' according to Haiku */
|
|
+# define NV_CIO_CRE_54 0x54 /* `fp_vtiming' according to Haiku */
|
|
+# define NV_CIO_CRE_57 0x57 /* index reg for cr58 */
|
|
+# define NV_CIO_CRE_58 0x58 /* data reg for cr57 */
|
|
+# define NV_CIO_CRE_59 0x59 /* related to on/off-chip-ness of digital outputs */
|
|
+# define NV_CIO_CRE_5B 0x5B /* newer colour saturation reg */
|
|
+# define NV_CIO_CRE_85 0x85
|
|
+# define NV_CIO_CRE_86 0x86
|
|
+#define NV_PRMCIO_INP0__COLOR 0x006013da
|
|
+
|
|
+#define NV_PRAMDAC_CU_START_POS 0x00680300
|
|
+# define NV_PRAMDAC_CU_START_POS_X 15:0
|
|
+# define NV_PRAMDAC_CU_START_POS_Y 31:16
|
|
+#define NV_RAMDAC_NV10_CURSYNC 0x00680404
|
|
+
|
|
+#define NV_PRAMDAC_NVPLL_COEFF 0x00680500
|
|
+#define NV_PRAMDAC_MPLL_COEFF 0x00680504
|
|
+#define NV_PRAMDAC_VPLL_COEFF 0x00680508
|
|
+# define NV30_RAMDAC_ENABLE_VCO2 (8 << 4)
|
|
+
|
|
+#define NV_PRAMDAC_PLL_COEFF_SELECT 0x0068050c
|
|
+# define NV_PRAMDAC_PLL_COEFF_SELECT_USE_VPLL2_TRUE (4 << 0)
|
|
+# define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL (1 << 8)
|
|
+# define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL (2 << 8)
|
|
+# define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL (4 << 8)
|
|
+# define NV_PRAMDAC_PLL_COEFF_SELECT_PLL_SOURCE_VPLL2 (8 << 8)
|
|
+# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 (1 << 16)
|
|
+# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1 (2 << 16)
|
|
+# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 (4 << 16)
|
|
+# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2 (8 << 16)
|
|
+# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_CLK_SOURCE_VIP (1 << 20)
|
|
+# define NV_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2 (1 << 28)
|
|
+# define NV_PRAMDAC_PLL_COEFF_SELECT_VCLK2_RATIO_DB2 (2 << 28)
|
|
+
|
|
+#define NV_PRAMDAC_PLL_SETUP_CONTROL 0x00680510
|
|
+#define NV_RAMDAC_VPLL2 0x00680520
|
|
+#define NV_PRAMDAC_SEL_CLK 0x00680524
|
|
+#define NV_RAMDAC_DITHER_NV11 0x00680528
|
|
+#define NV_PRAMDAC_DACCLK 0x0068052c
|
|
+# define NV_PRAMDAC_DACCLK_SEL_DACCLK (1 << 0)
|
|
+
|
|
+#define NV_RAMDAC_NVPLL_B 0x00680570
|
|
+#define NV_RAMDAC_MPLL_B 0x00680574
|
|
+#define NV_RAMDAC_VPLL_B 0x00680578
|
|
+#define NV_RAMDAC_VPLL2_B 0x0068057c
|
|
+# define NV31_RAMDAC_ENABLE_VCO2 (8 << 28)
|
|
+#define NV_PRAMDAC_580 0x00680580
|
|
+# define NV_RAMDAC_580_VPLL1_ACTIVE (1 << 8)
|
|
+# define NV_RAMDAC_580_VPLL2_ACTIVE (1 << 28)
|
|
+
|
|
+#define NV_PRAMDAC_GENERAL_CONTROL 0x00680600
|
|
+# define NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON (3 << 4)
|
|
+# define NV_PRAMDAC_GENERAL_CONTROL_VGA_STATE_SEL (1 << 8)
|
|
+# define NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL (1 << 12)
|
|
+# define NV_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM (2 << 16)
|
|
+# define NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS (1 << 20)
|
|
+# define NV_PRAMDAC_GENERAL_CONTROL_PIPE_LONG (2 << 28)
|
|
+#define NV_PRAMDAC_TEST_CONTROL 0x00680608
|
|
+# define NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED (1 << 12)
|
|
+# define NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF (1 << 16)
|
|
+# define NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI (1 << 28)
|
|
+#define NV_PRAMDAC_TESTPOINT_DATA 0x00680610
|
|
+# define NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK (8 << 28)
|
|
+#define NV_PRAMDAC_630 0x00680630
|
|
+#define NV_PRAMDAC_634 0x00680634
|
|
+
|
|
+#define NV_PRAMDAC_TV_SETUP 0x00680700
|
|
+#define NV_PRAMDAC_TV_VTOTAL 0x00680720
|
|
+#define NV_PRAMDAC_TV_VSKEW 0x00680724
|
|
+#define NV_PRAMDAC_TV_VSYNC_DELAY 0x00680728
|
|
+#define NV_PRAMDAC_TV_HTOTAL 0x0068072c
|
|
+#define NV_PRAMDAC_TV_HSKEW 0x00680730
|
|
+#define NV_PRAMDAC_TV_HSYNC_DELAY 0x00680734
|
|
+#define NV_PRAMDAC_TV_HSYNC_DELAY2 0x00680738
|
|
+
|
|
+#define NV_PRAMDAC_TV_SETUP 0x00680700
|
|
+
|
|
+#define NV_PRAMDAC_FP_VDISPLAY_END 0x00680800
|
|
+#define NV_PRAMDAC_FP_VTOTAL 0x00680804
|
|
+#define NV_PRAMDAC_FP_VCRTC 0x00680808
|
|
+#define NV_PRAMDAC_FP_VSYNC_START 0x0068080c
|
|
+#define NV_PRAMDAC_FP_VSYNC_END 0x00680810
|
|
+#define NV_PRAMDAC_FP_VVALID_START 0x00680814
|
|
+#define NV_PRAMDAC_FP_VVALID_END 0x00680818
|
|
+#define NV_PRAMDAC_FP_HDISPLAY_END 0x00680820
|
|
+#define NV_PRAMDAC_FP_HTOTAL 0x00680824
|
|
+#define NV_PRAMDAC_FP_HCRTC 0x00680828
|
|
+#define NV_PRAMDAC_FP_HSYNC_START 0x0068082c
|
|
+#define NV_PRAMDAC_FP_HSYNC_END 0x00680830
|
|
+#define NV_PRAMDAC_FP_HVALID_START 0x00680834
|
|
+#define NV_PRAMDAC_FP_HVALID_END 0x00680838
|
|
+
|
|
+#define NV_RAMDAC_FP_DITHER 0x0068083c
|
|
+#define NV_PRAMDAC_FP_TG_CONTROL 0x00680848
|
|
+# define NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS (1 << 0)
|
|
+# define NV_PRAMDAC_FP_TG_CONTROL_VSYNC_DISABLE (2 << 0)
|
|
+# define NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS (1 << 4)
|
|
+# define NV_PRAMDAC_FP_TG_CONTROL_HSYNC_DISABLE (2 << 4)
|
|
+# define NV_PRAMDAC_FP_TG_CONTROL_MODE_SCALE (0 << 8)
|
|
+# define NV_PRAMDAC_FP_TG_CONTROL_MODE_CENTER (1 << 8)
|
|
+# define NV_PRAMDAC_FP_TG_CONTROL_MODE_NATIVE (2 << 8)
|
|
+# define NV_PRAMDAC_FP_TG_CONTROL_READ_PROG (1 << 20)
|
|
+# define NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12 (1 << 24)
|
|
+# define NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS (1 << 28)
|
|
+# define NV_PRAMDAC_FP_TG_CONTROL_DISPEN_DISABLE (2 << 28)
|
|
+#define NV_PRAMDAC_FP_MARGIN_COLOR 0x0068084c
|
|
+#define NV_PRAMDAC_850 0x00680850
|
|
+#define NV_PRAMDAC_85C 0x0068085c
|
|
+#define NV_PRAMDAC_FP_DEBUG_0 0x00680880
|
|
+# define NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE (1 << 0)
|
|
+# define NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE (1 << 4)
|
|
+/* This doesn't seem to be essential for tmds, but still often set */
|
|
+# define NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED (8 << 4)
|
|
+# define NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR (1 << 8)
|
|
+# define NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR (1 << 12)
|
|
+# define NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND (1 << 20)
|
|
+# define NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND (1 << 24)
|
|
+# define NV_PRAMDAC_FP_DEBUG_0_PWRDOWN_FPCLK (1 << 28)
|
|
+#define NV_PRAMDAC_FP_DEBUG_1 0x00680884
|
|
+# define NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE 11:0
|
|
+# define NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE (1 << 12)
|
|
+# define NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE 27:16
|
|
+# define NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE (1 << 28)
|
|
+#define NV_PRAMDAC_FP_DEBUG_2 0x00680888
|
|
+#define NV_PRAMDAC_FP_DEBUG_3 0x0068088C
|
|
+
|
|
+/* see NV_PRAMDAC_INDIR_TMDS in rules.xml */
|
|
+#define NV_PRAMDAC_FP_TMDS_CONTROL 0x006808b0
|
|
+# define NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE (1 << 16)
|
|
+#define NV_PRAMDAC_FP_TMDS_DATA 0x006808b4
|
|
+
|
|
+#define NV_PRAMDAC_8C0 0x006808c0
|
|
+
|
|
+/* Some kind of switch */
|
|
+#define NV_PRAMDAC_900 0x00680900
|
|
+#define NV_PRAMDAC_A20 0x00680A20
|
|
+#define NV_PRAMDAC_A24 0x00680A24
|
|
+#define NV_PRAMDAC_A34 0x00680A34
|
|
+
|
|
+#define NV_PRAMDAC_CTV 0x00680c00
|
|
+
|
|
+/* names fabricated from NV_USER_DAC info */
|
|
+#define NV_PRMDIO_PIXEL_MASK 0x006813c6
|
|
+# define NV_PRMDIO_PIXEL_MASK_MASK 0xff
|
|
+#define NV_PRMDIO_READ_MODE_ADDRESS 0x006813c7
|
|
+#define NV_PRMDIO_WRITE_MODE_ADDRESS 0x006813c8
|
|
+#define NV_PRMDIO_PALETTE_DATA 0x006813c9
|
|
+
|
|
+#define NV_PGRAPH_DEBUG_0 0x00400080
|
|
+#define NV_PGRAPH_DEBUG_1 0x00400084
|
|
+#define NV_PGRAPH_DEBUG_2_NV04 0x00400088
|
|
+#define NV_PGRAPH_DEBUG_2 0x00400620
|
|
+#define NV_PGRAPH_DEBUG_3 0x0040008c
|
|
+#define NV_PGRAPH_DEBUG_4 0x00400090
|
|
+#define NV_PGRAPH_INTR 0x00400100
|
|
+#define NV_PGRAPH_INTR_EN 0x00400140
|
|
+#define NV_PGRAPH_CTX_CONTROL 0x00400144
|
|
+#define NV_PGRAPH_CTX_CONTROL_NV04 0x00400170
|
|
+#define NV_PGRAPH_ABS_UCLIP_XMIN 0x0040053C
|
|
+#define NV_PGRAPH_ABS_UCLIP_YMIN 0x00400540
|
|
+#define NV_PGRAPH_ABS_UCLIP_XMAX 0x00400544
|
|
+#define NV_PGRAPH_ABS_UCLIP_YMAX 0x00400548
|
|
+#define NV_PGRAPH_BETA_AND 0x00400608
|
|
+#define NV_PGRAPH_LIMIT_VIOL_PIX 0x00400610
|
|
+#define NV_PGRAPH_BOFFSET0 0x00400640
|
|
+#define NV_PGRAPH_BOFFSET1 0x00400644
|
|
+#define NV_PGRAPH_BOFFSET2 0x00400648
|
|
+#define NV_PGRAPH_BLIMIT0 0x00400684
|
|
+#define NV_PGRAPH_BLIMIT1 0x00400688
|
|
+#define NV_PGRAPH_BLIMIT2 0x0040068c
|
|
+#define NV_PGRAPH_STATUS 0x00400700
|
|
+#define NV_PGRAPH_SURFACE 0x00400710
|
|
+#define NV_PGRAPH_STATE 0x00400714
|
|
+#define NV_PGRAPH_FIFO 0x00400720
|
|
+#define NV_PGRAPH_PATTERN_SHAPE 0x00400810
|
|
+#define NV_PGRAPH_TILE 0x00400b00
|
|
+
|
|
+#define NV_PVIDEO_INTR_EN 0x00008140
|
|
+#define NV_PVIDEO_BUFFER 0x00008700
|
|
+#define NV_PVIDEO_STOP 0x00008704
|
|
+#define NV_PVIDEO_UVPLANE_BASE(buff) (0x00008800+(buff)*4)
|
|
+#define NV_PVIDEO_UVPLANE_LIMIT(buff) (0x00008808+(buff)*4)
|
|
+#define NV_PVIDEO_UVPLANE_OFFSET_BUFF(buff) (0x00008820+(buff)*4)
|
|
+#define NV_PVIDEO_BASE(buff) (0x00008900+(buff)*4)
|
|
+#define NV_PVIDEO_LIMIT(buff) (0x00008908+(buff)*4)
|
|
+#define NV_PVIDEO_LUMINANCE(buff) (0x00008910+(buff)*4)
|
|
+#define NV_PVIDEO_CHROMINANCE(buff) (0x00008918+(buff)*4)
|
|
+#define NV_PVIDEO_OFFSET_BUFF(buff) (0x00008920+(buff)*4)
|
|
+#define NV_PVIDEO_SIZE_IN(buff) (0x00008928+(buff)*4)
|
|
+#define NV_PVIDEO_POINT_IN(buff) (0x00008930+(buff)*4)
|
|
+#define NV_PVIDEO_DS_DX(buff) (0x00008938+(buff)*4)
|
|
+#define NV_PVIDEO_DT_DY(buff) (0x00008940+(buff)*4)
|
|
+#define NV_PVIDEO_POINT_OUT(buff) (0x00008948+(buff)*4)
|
|
+#define NV_PVIDEO_SIZE_OUT(buff) (0x00008950+(buff)*4)
|
|
+#define NV_PVIDEO_FORMAT(buff) (0x00008958+(buff)*4)
|
|
+# define NV_PVIDEO_FORMAT_PLANAR (1 << 0)
|
|
+# define NV_PVIDEO_FORMAT_COLOR_LE_CR8YB8CB8YA8 (1 << 16)
|
|
+# define NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY (1 << 20)
|
|
+# define NV_PVIDEO_FORMAT_MATRIX_ITURBT709 (1 << 24)
|
|
+#define NV_PVIDEO_COLOR_KEY 0x00008B00
|
|
+
|
|
+/* NV04 overlay defines from VIDIX & Haiku */
|
|
+#define NV_PVIDEO_INTR_EN_0 0x00680140
|
|
+#define NV_PVIDEO_STEP_SIZE 0x00680200
|
|
+#define NV_PVIDEO_CONTROL_Y 0x00680204
|
|
+#define NV_PVIDEO_CONTROL_X 0x00680208
|
|
+#define NV_PVIDEO_BUFF0_START_ADDRESS 0x0068020c
|
|
+#define NV_PVIDEO_BUFF0_PITCH_LENGTH 0x00680214
|
|
+#define NV_PVIDEO_BUFF0_OFFSET 0x0068021c
|
|
+#define NV_PVIDEO_BUFF1_START_ADDRESS 0x00680210
|
|
+#define NV_PVIDEO_BUFF1_PITCH_LENGTH 0x00680218
|
|
+#define NV_PVIDEO_BUFF1_OFFSET 0x00680220
|
|
+#define NV_PVIDEO_OE_STATE 0x00680224
|
|
+#define NV_PVIDEO_SU_STATE 0x00680228
|
|
+#define NV_PVIDEO_RM_STATE 0x0068022c
|
|
+#define NV_PVIDEO_WINDOW_START 0x00680230
|
|
+#define NV_PVIDEO_WINDOW_SIZE 0x00680234
|
|
+#define NV_PVIDEO_FIFO_THRES_SIZE 0x00680238
|
|
+#define NV_PVIDEO_FIFO_BURST_LENGTH 0x0068023c
|
|
+#define NV_PVIDEO_KEY 0x00680240
|
|
+#define NV_PVIDEO_OVERLAY 0x00680244
|
|
+#define NV_PVIDEO_RED_CSC_OFFSET 0x00680280
|
|
+#define NV_PVIDEO_GREEN_CSC_OFFSET 0x00680284
|
|
+#define NV_PVIDEO_BLUE_CSC_OFFSET 0x00680288
|
|
+#define NV_PVIDEO_CSC_ADJUST 0x0068028c
|
|
+
|
|
+#endif
|
|
diff --git a/drivers/gpu/drm/r128/r128_drv.c b/drivers/gpu/drm/r128/r128_drv.c
|
|
index 601f4c0..b806fdc 100644
|
|
--- a/drivers/gpu/drm/r128/r128_drv.c
|
|
+++ b/drivers/gpu/drm/r128/r128_drv.c
|
|
@@ -64,7 +64,7 @@ static struct drm_driver driver = {
|
|
.owner = THIS_MODULE,
|
|
.open = drm_open,
|
|
.release = drm_release,
|
|
- .ioctl = drm_ioctl,
|
|
+ .unlocked_ioctl = drm_ioctl,
|
|
.mmap = drm_mmap,
|
|
.poll = drm_poll,
|
|
.fasync = drm_fasync,
|
|
diff --git a/drivers/gpu/drm/r128/r128_ioc32.c b/drivers/gpu/drm/r128/r128_ioc32.c
|
|
index d3cb676..51c99fc 100644
|
|
--- a/drivers/gpu/drm/r128/r128_ioc32.c
|
|
+++ b/drivers/gpu/drm/r128/r128_ioc32.c
|
|
@@ -95,8 +95,7 @@ static int compat_r128_init(struct file *file, unsigned int cmd,
|
|
&init->agp_textures_offset))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_R128_INIT, (unsigned long)init);
|
|
+ return drm_ioctl(file, DRM_IOCTL_R128_INIT, (unsigned long)init);
|
|
}
|
|
|
|
typedef struct drm_r128_depth32 {
|
|
@@ -129,8 +128,7 @@ static int compat_r128_depth(struct file *file, unsigned int cmd,
|
|
&depth->mask))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_R128_DEPTH, (unsigned long)depth);
|
|
+ return drm_ioctl(file, DRM_IOCTL_R128_DEPTH, (unsigned long)depth);
|
|
|
|
}
|
|
|
|
@@ -153,8 +151,7 @@ static int compat_r128_stipple(struct file *file, unsigned int cmd,
|
|
&stipple->mask))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_R128_STIPPLE, (unsigned long)stipple);
|
|
+ return drm_ioctl(file, DRM_IOCTL_R128_STIPPLE, (unsigned long)stipple);
|
|
}
|
|
|
|
typedef struct drm_r128_getparam32 {
|
|
@@ -178,8 +175,7 @@ static int compat_r128_getparam(struct file *file, unsigned int cmd,
|
|
&getparam->value))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_R128_GETPARAM, (unsigned long)getparam);
|
|
+ return drm_ioctl(file, DRM_IOCTL_R128_GETPARAM, (unsigned long)getparam);
|
|
}
|
|
|
|
drm_ioctl_compat_t *r128_compat_ioctls[] = {
|
|
@@ -210,12 +206,10 @@ long r128_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|
if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(r128_compat_ioctls))
|
|
fn = r128_compat_ioctls[nr - DRM_COMMAND_BASE];
|
|
|
|
- lock_kernel(); /* XXX for now */
|
|
if (fn != NULL)
|
|
ret = (*fn) (filp, cmd, arg);
|
|
else
|
|
- ret = drm_ioctl(filp->f_path.dentry->d_inode, filp, cmd, arg);
|
|
- unlock_kernel();
|
|
+ ret = drm_ioctl(filp, cmd, arg);
|
|
|
|
return ret;
|
|
}
|
|
diff --git a/drivers/gpu/drm/radeon/Kconfig b/drivers/gpu/drm/radeon/Kconfig
|
|
index 5982321..1c02d23 100644
|
|
--- a/drivers/gpu/drm/radeon/Kconfig
|
|
+++ b/drivers/gpu/drm/radeon/Kconfig
|
|
@@ -1,10 +1,14 @@
|
|
config DRM_RADEON_KMS
|
|
- bool "Enable modesetting on radeon by default"
|
|
+ bool "Enable modesetting on radeon by default - NEW DRIVER"
|
|
depends on DRM_RADEON
|
|
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.
|
|
+ Choose this option if you want kernel modesetting enabled by default.
|
|
+
|
|
+ This is a completely new driver. It's only part of the existing drm
|
|
+ for compatibility reasons. It requires an entirely different graphics
|
|
+ stack above it and works very differently from the old drm stack.
|
|
+ i.e. don't enable this unless you know what you are doing it may
|
|
+ cause issues or bugs compared to the previous userspace driver stack.
|
|
|
|
When kernel modesetting is enabled the IOCTL of radeon/drm
|
|
driver are considered as invalid and an error message is printed
|
|
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile
|
|
index b5713ee..1cc7b93 100644
|
|
--- a/drivers/gpu/drm/radeon/Makefile
|
|
+++ b/drivers/gpu/drm/radeon/Makefile
|
|
@@ -24,6 +24,9 @@ $(obj)/rv515_reg_safe.h: $(src)/reg_srcs/rv515 $(obj)/mkregtable
|
|
$(obj)/r300_reg_safe.h: $(src)/reg_srcs/r300 $(obj)/mkregtable
|
|
$(call if_changed,mkregtable)
|
|
|
|
+$(obj)/r420_reg_safe.h: $(src)/reg_srcs/r420 $(obj)/mkregtable
|
|
+ $(call if_changed,mkregtable)
|
|
+
|
|
$(obj)/rs600_reg_safe.h: $(src)/reg_srcs/rs600 $(obj)/mkregtable
|
|
$(call if_changed,mkregtable)
|
|
|
|
@@ -35,6 +38,8 @@ $(obj)/rv515.o: $(obj)/rv515_reg_safe.h
|
|
|
|
$(obj)/r300.o: $(obj)/r300_reg_safe.h
|
|
|
|
+$(obj)/r420.o: $(obj)/r420_reg_safe.h
|
|
+
|
|
$(obj)/rs600.o: $(obj)/rs600_reg_safe.h
|
|
|
|
radeon-y := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o \
|
|
@@ -49,7 +54,7 @@ radeon-y += radeon_device.o radeon_kms.o \
|
|
radeon_cs.o radeon_bios.o radeon_benchmark.o r100.o r300.o r420.o \
|
|
rs400.o rs600.o rs690.o rv515.o r520.o r600.o rv770.o radeon_test.o \
|
|
r200.o radeon_legacy_tv.o r600_cs.o r600_blit.o r600_blit_shaders.o \
|
|
- r600_blit_kms.o radeon_pm.o
|
|
+ r600_blit_kms.o radeon_pm.o atombios_dp.o r600_audio.o r600_hdmi.o
|
|
|
|
radeon-$(CONFIG_COMPAT) += radeon_ioc32.o
|
|
|
|
diff --git a/drivers/gpu/drm/radeon/ObjectID.h b/drivers/gpu/drm/radeon/ObjectID.h
|
|
index 6d0183c..c714179 100644
|
|
--- a/drivers/gpu/drm/radeon/ObjectID.h
|
|
+++ b/drivers/gpu/drm/radeon/ObjectID.h
|
|
@@ -1,5 +1,5 @@
|
|
/*
|
|
-* Copyright 2006-2007 Advanced Micro Devices, Inc.
|
|
+* Copyright 2006-2007 Advanced Micro Devices, Inc.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
@@ -41,14 +41,14 @@
|
|
/****************************************************/
|
|
/* Encoder Object ID Definition */
|
|
/****************************************************/
|
|
-#define ENCODER_OBJECT_ID_NONE 0x00
|
|
+#define ENCODER_OBJECT_ID_NONE 0x00
|
|
|
|
/* Radeon Class Display Hardware */
|
|
#define ENCODER_OBJECT_ID_INTERNAL_LVDS 0x01
|
|
#define ENCODER_OBJECT_ID_INTERNAL_TMDS1 0x02
|
|
#define ENCODER_OBJECT_ID_INTERNAL_TMDS2 0x03
|
|
#define ENCODER_OBJECT_ID_INTERNAL_DAC1 0x04
|
|
-#define ENCODER_OBJECT_ID_INTERNAL_DAC2 0x05 /* TV/CV DAC */
|
|
+#define ENCODER_OBJECT_ID_INTERNAL_DAC2 0x05 /* TV/CV DAC */
|
|
#define ENCODER_OBJECT_ID_INTERNAL_SDVOA 0x06
|
|
#define ENCODER_OBJECT_ID_INTERNAL_SDVOB 0x07
|
|
|
|
@@ -56,11 +56,11 @@
|
|
#define ENCODER_OBJECT_ID_SI170B 0x08
|
|
#define ENCODER_OBJECT_ID_CH7303 0x09
|
|
#define ENCODER_OBJECT_ID_CH7301 0x0A
|
|
-#define ENCODER_OBJECT_ID_INTERNAL_DVO1 0x0B /* This belongs to Radeon Class Display Hardware */
|
|
+#define ENCODER_OBJECT_ID_INTERNAL_DVO1 0x0B /* This belongs to Radeon Class Display Hardware */
|
|
#define ENCODER_OBJECT_ID_EXTERNAL_SDVOA 0x0C
|
|
#define ENCODER_OBJECT_ID_EXTERNAL_SDVOB 0x0D
|
|
#define ENCODER_OBJECT_ID_TITFP513 0x0E
|
|
-#define ENCODER_OBJECT_ID_INTERNAL_LVTM1 0x0F /* not used for Radeon */
|
|
+#define ENCODER_OBJECT_ID_INTERNAL_LVTM1 0x0F /* not used for Radeon */
|
|
#define ENCODER_OBJECT_ID_VT1623 0x10
|
|
#define ENCODER_OBJECT_ID_HDMI_SI1930 0x11
|
|
#define ENCODER_OBJECT_ID_HDMI_INTERNAL 0x12
|
|
@@ -68,9 +68,9 @@
|
|
#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1 0x13
|
|
#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1 0x14
|
|
#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1 0x15
|
|
-#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2 0x16 /* Shared with CV/TV and CRT */
|
|
-#define ENCODER_OBJECT_ID_SI178 0X17 /* External TMDS (dual link, no HDCP.) */
|
|
-#define ENCODER_OBJECT_ID_MVPU_FPGA 0x18 /* MVPU FPGA chip */
|
|
+#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2 0x16 /* Shared with CV/TV and CRT */
|
|
+#define ENCODER_OBJECT_ID_SI178 0X17 /* External TMDS (dual link, no HDCP.) */
|
|
+#define ENCODER_OBJECT_ID_MVPU_FPGA 0x18 /* MVPU FPGA chip */
|
|
#define ENCODER_OBJECT_ID_INTERNAL_DDI 0x19
|
|
#define ENCODER_OBJECT_ID_VT1625 0x1A
|
|
#define ENCODER_OBJECT_ID_HDMI_SI1932 0x1B
|
|
@@ -86,7 +86,7 @@
|
|
/****************************************************/
|
|
/* Connector Object ID Definition */
|
|
/****************************************************/
|
|
-#define CONNECTOR_OBJECT_ID_NONE 0x00
|
|
+#define CONNECTOR_OBJECT_ID_NONE 0x00
|
|
#define CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I 0x01
|
|
#define CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I 0x02
|
|
#define CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D 0x03
|
|
@@ -96,7 +96,7 @@
|
|
#define CONNECTOR_OBJECT_ID_SVIDEO 0x07
|
|
#define CONNECTOR_OBJECT_ID_YPbPr 0x08
|
|
#define CONNECTOR_OBJECT_ID_D_CONNECTOR 0x09
|
|
-#define CONNECTOR_OBJECT_ID_9PIN_DIN 0x0A /* Supports both CV & TV */
|
|
+#define CONNECTOR_OBJECT_ID_9PIN_DIN 0x0A /* Supports both CV & TV */
|
|
#define CONNECTOR_OBJECT_ID_SCART 0x0B
|
|
#define CONNECTOR_OBJECT_ID_HDMI_TYPE_A 0x0C
|
|
#define CONNECTOR_OBJECT_ID_HDMI_TYPE_B 0x0D
|
|
@@ -106,6 +106,8 @@
|
|
#define CONNECTOR_OBJECT_ID_CROSSFIRE 0x11
|
|
#define CONNECTOR_OBJECT_ID_HARDCODE_DVI 0x12
|
|
#define CONNECTOR_OBJECT_ID_DISPLAYPORT 0x13
|
|
+#define CONNECTOR_OBJECT_ID_eDP 0x14
|
|
+#define CONNECTOR_OBJECT_ID_MXM 0x15
|
|
|
|
/* deleted */
|
|
|
|
@@ -116,6 +118,14 @@
|
|
#define ROUTER_OBJECT_ID_I2C_EXTENDER_CNTL 0x01
|
|
|
|
/****************************************************/
|
|
+/* Generic Object ID Definition */
|
|
+/****************************************************/
|
|
+#define GENERIC_OBJECT_ID_NONE 0x00
|
|
+#define GENERIC_OBJECT_ID_GLSYNC 0x01
|
|
+#define GENERIC_OBJECT_ID_PX2_NON_DRIVABLE 0x02
|
|
+#define GENERIC_OBJECT_ID_MXM_OPM 0x03
|
|
+
|
|
+/****************************************************/
|
|
/* Graphics Object ENUM ID Definition */
|
|
/****************************************************/
|
|
#define GRAPH_OBJECT_ENUM_ID1 0x01
|
|
@@ -124,6 +134,7 @@
|
|
#define GRAPH_OBJECT_ENUM_ID4 0x04
|
|
#define GRAPH_OBJECT_ENUM_ID5 0x05
|
|
#define GRAPH_OBJECT_ENUM_ID6 0x06
|
|
+#define GRAPH_OBJECT_ENUM_ID7 0x07
|
|
|
|
/****************************************************/
|
|
/* Graphics Object ID Bit definition */
|
|
@@ -133,35 +144,35 @@
|
|
#define RESERVED1_ID_MASK 0x0800
|
|
#define OBJECT_TYPE_MASK 0x7000
|
|
#define RESERVED2_ID_MASK 0x8000
|
|
-
|
|
+
|
|
#define OBJECT_ID_SHIFT 0x00
|
|
#define ENUM_ID_SHIFT 0x08
|
|
#define OBJECT_TYPE_SHIFT 0x0C
|
|
|
|
+
|
|
/****************************************************/
|
|
/* Graphics Object family definition */
|
|
/****************************************************/
|
|
-#define CONSTRUCTOBJECTFAMILYID(GRAPHICS_OBJECT_TYPE, GRAPHICS_OBJECT_ID) \
|
|
- (GRAPHICS_OBJECT_TYPE << OBJECT_TYPE_SHIFT | \
|
|
- GRAPHICS_OBJECT_ID << OBJECT_ID_SHIFT)
|
|
+#define CONSTRUCTOBJECTFAMILYID(GRAPHICS_OBJECT_TYPE, GRAPHICS_OBJECT_ID) (GRAPHICS_OBJECT_TYPE << OBJECT_TYPE_SHIFT | \
|
|
+ GRAPHICS_OBJECT_ID << OBJECT_ID_SHIFT)
|
|
/****************************************************/
|
|
/* GPU Object ID definition - Shared with BIOS */
|
|
/****************************************************/
|
|
-#define GPU_ENUM_ID1 (GRAPH_OBJECT_TYPE_GPU << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT)
|
|
+#define GPU_ENUM_ID1 ( GRAPH_OBJECT_TYPE_GPU << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT)
|
|
|
|
/****************************************************/
|
|
/* Encoder Object ID definition - Shared with BIOS */
|
|
/****************************************************/
|
|
/*
|
|
-#define ENCODER_INTERNAL_LVDS_ENUM_ID1 0x2101
|
|
+#define ENCODER_INTERNAL_LVDS_ENUM_ID1 0x2101
|
|
#define ENCODER_INTERNAL_TMDS1_ENUM_ID1 0x2102
|
|
#define ENCODER_INTERNAL_TMDS2_ENUM_ID1 0x2103
|
|
#define ENCODER_INTERNAL_DAC1_ENUM_ID1 0x2104
|
|
#define ENCODER_INTERNAL_DAC2_ENUM_ID1 0x2105
|
|
#define ENCODER_INTERNAL_SDVOA_ENUM_ID1 0x2106
|
|
#define ENCODER_INTERNAL_SDVOB_ENUM_ID1 0x2107
|
|
-#define ENCODER_SIL170B_ENUM_ID1 0x2108
|
|
+#define ENCODER_SIL170B_ENUM_ID1 0x2108
|
|
#define ENCODER_CH7303_ENUM_ID1 0x2109
|
|
#define ENCODER_CH7301_ENUM_ID1 0x210A
|
|
#define ENCODER_INTERNAL_DVO1_ENUM_ID1 0x210B
|
|
@@ -175,8 +186,8 @@
|
|
#define ENCODER_INTERNAL_KLDSCP_TMDS1_ENUM_ID1 0x2113
|
|
#define ENCODER_INTERNAL_KLDSCP_DVO1_ENUM_ID1 0x2114
|
|
#define ENCODER_INTERNAL_KLDSCP_DAC1_ENUM_ID1 0x2115
|
|
-#define ENCODER_INTERNAL_KLDSCP_DAC2_ENUM_ID1 0x2116
|
|
-#define ENCODER_SI178_ENUM_ID1 0x2117
|
|
+#define ENCODER_INTERNAL_KLDSCP_DAC2_ENUM_ID1 0x2116
|
|
+#define ENCODER_SI178_ENUM_ID1 0x2117
|
|
#define ENCODER_MVPU_FPGA_ENUM_ID1 0x2118
|
|
#define ENCODER_INTERNAL_DDI_ENUM_ID1 0x2119
|
|
#define ENCODER_VT1625_ENUM_ID1 0x211A
|
|
@@ -185,205 +196,169 @@
|
|
#define ENCODER_DP_DP501_ENUM_ID1 0x211D
|
|
#define ENCODER_INTERNAL_UNIPHY_ENUM_ID1 0x211E
|
|
*/
|
|
-#define ENCODER_INTERNAL_LVDS_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_INTERNAL_LVDS << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_INTERNAL_TMDS1_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_INTERNAL_TMDS1 << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_INTERNAL_TMDS2_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_INTERNAL_TMDS2 << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_INTERNAL_DAC1_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_INTERNAL_DAC1 << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_INTERNAL_DAC2_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_INTERNAL_DAC2 << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_INTERNAL_SDVOA_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_INTERNAL_SDVOA << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_INTERNAL_SDVOA_ENUM_ID2 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_INTERNAL_SDVOA << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_INTERNAL_SDVOB_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_INTERNAL_SDVOB << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_SIL170B_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_SI170B << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_CH7303_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_CH7303 << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_CH7301_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_CH7301 << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_INTERNAL_DVO1_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_INTERNAL_DVO1 << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_EXTERNAL_SDVOA_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_EXTERNAL_SDVOA << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_EXTERNAL_SDVOA_ENUM_ID2 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_EXTERNAL_SDVOA << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_EXTERNAL_SDVOB_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_EXTERNAL_SDVOB << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_TITFP513_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_TITFP513 << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_INTERNAL_LVTM1_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_INTERNAL_LVTM1 << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_VT1623_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_VT1623 << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_HDMI_SI1930_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_HDMI_SI1930 << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_HDMI_INTERNAL_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_HDMI_INTERNAL << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_INTERNAL_KLDSCP_TMDS1_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1 << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_INTERNAL_KLDSCP_TMDS1_ENUM_ID2 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1 << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_INTERNAL_KLDSCP_DVO1_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1 << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_INTERNAL_KLDSCP_DAC1_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1 << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_INTERNAL_KLDSCP_DAC2_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2 << OBJECT_ID_SHIFT) /* Shared with CV/TV and CRT */
|
|
-
|
|
-#define ENCODER_SI178_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_SI178 << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_MVPU_FPGA_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_MVPU_FPGA << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_INTERNAL_DDI_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_INTERNAL_DDI << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_VT1625_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_VT1625 << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_HDMI_SI1932_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_HDMI_SI1932 << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_DP_DP501_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_DP_DP501 << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_DP_AN9801_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_DP_AN9801 << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_INTERNAL_UNIPHY_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_INTERNAL_UNIPHY << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_INTERNAL_UNIPHY_ENUM_ID2 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_INTERNAL_UNIPHY << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_INTERNAL_KLDSCP_LVTMA_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_INTERNAL_UNIPHY1_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_INTERNAL_UNIPHY1 << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_INTERNAL_UNIPHY1_ENUM_ID2 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_INTERNAL_UNIPHY1 << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_INTERNAL_UNIPHY2_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_INTERNAL_UNIPHY2 << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_INTERNAL_UNIPHY2_ENUM_ID2 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_INTERNAL_UNIPHY2 << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define ENCODER_GENERAL_EXTERNAL_DVO_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ENCODER_OBJECT_ID_GENERAL_EXTERNAL_DVO << OBJECT_ID_SHIFT)
|
|
+#define ENCODER_INTERNAL_LVDS_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_INTERNAL_LVDS << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_INTERNAL_TMDS1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_INTERNAL_TMDS1 << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_INTERNAL_TMDS2_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_INTERNAL_TMDS2 << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_INTERNAL_DAC1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_INTERNAL_DAC1 << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_INTERNAL_DAC2_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_INTERNAL_DAC2 << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_INTERNAL_SDVOA_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_INTERNAL_SDVOA << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_INTERNAL_SDVOA_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_INTERNAL_SDVOA << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_INTERNAL_SDVOB_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_INTERNAL_SDVOB << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_SIL170B_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_SI170B << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_CH7303_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_CH7303 << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_CH7301_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_CH7301 << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_INTERNAL_DVO1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_INTERNAL_DVO1 << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_EXTERNAL_SDVOA_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_EXTERNAL_SDVOA << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_EXTERNAL_SDVOA_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_EXTERNAL_SDVOA << OBJECT_ID_SHIFT)
|
|
+
|
|
+
|
|
+#define ENCODER_EXTERNAL_SDVOB_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_EXTERNAL_SDVOB << OBJECT_ID_SHIFT)
|
|
+
|
|
+
|
|
+#define ENCODER_TITFP513_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_TITFP513 << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_INTERNAL_LVTM1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_INTERNAL_LVTM1 << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_VT1623_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_VT1623 << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_HDMI_SI1930_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_HDMI_SI1930 << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_HDMI_INTERNAL_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_HDMI_INTERNAL << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_INTERNAL_KLDSCP_TMDS1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1 << OBJECT_ID_SHIFT)
|
|
+
|
|
+
|
|
+#define ENCODER_INTERNAL_KLDSCP_TMDS1_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1 << OBJECT_ID_SHIFT)
|
|
+
|
|
+
|
|
+#define ENCODER_INTERNAL_KLDSCP_DVO1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1 << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_INTERNAL_KLDSCP_DAC1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1 << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_INTERNAL_KLDSCP_DAC2_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2 << OBJECT_ID_SHIFT) // Shared with CV/TV and CRT
|
|
+
|
|
+#define ENCODER_SI178_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_SI178 << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_MVPU_FPGA_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_MVPU_FPGA << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_INTERNAL_DDI_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_INTERNAL_DDI << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_VT1625_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_VT1625 << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_HDMI_SI1932_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_HDMI_SI1932 << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_DP_DP501_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_DP_DP501 << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_DP_AN9801_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_DP_AN9801 << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_INTERNAL_UNIPHY_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_INTERNAL_UNIPHY << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_INTERNAL_UNIPHY_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_INTERNAL_UNIPHY << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_INTERNAL_KLDSCP_LVTMA_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_INTERNAL_UNIPHY1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_INTERNAL_UNIPHY1 << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_INTERNAL_UNIPHY1_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_INTERNAL_UNIPHY1 << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_INTERNAL_UNIPHY2_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_INTERNAL_UNIPHY2 << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_INTERNAL_UNIPHY2_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_INTERNAL_UNIPHY2 << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define ENCODER_GENERAL_EXTERNAL_DVO_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ENCODER_OBJECT_ID_GENERAL_EXTERNAL_DVO << OBJECT_ID_SHIFT)
|
|
|
|
/****************************************************/
|
|
/* Connector Object ID definition - Shared with BIOS */
|
|
@@ -406,167 +381,253 @@
|
|
#define CONNECTOR_7PIN_DIN_ENUM_ID1 0x310F
|
|
#define CONNECTOR_PCIE_CONNECTOR_ENUM_ID1 0x3110
|
|
*/
|
|
-#define CONNECTOR_LVDS_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_LVDS << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_SINGLE_LINK_DVI_I_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_SINGLE_LINK_DVI_I_ENUM_ID2 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_DUAL_LINK_DVI_I_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_DUAL_LINK_DVI_I_ENUM_ID2 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID2 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_DUAL_LINK_DVI_D_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_VGA_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_VGA << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_VGA_ENUM_ID2 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_VGA << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_COMPOSITE_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_COMPOSITE << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_SVIDEO_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_SVIDEO << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_YPbPr_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_YPbPr << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_D_CONNECTOR_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_D_CONNECTOR << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_9PIN_DIN_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_9PIN_DIN << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_SCART_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_SCART << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_HDMI_TYPE_A_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_HDMI_TYPE_B_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_HDMI_TYPE_B << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_7PIN_DIN_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_7PIN_DIN << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_PCIE_CONNECTOR_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_PCIE_CONNECTOR << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_PCIE_CONNECTOR_ENUM_ID2 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_PCIE_CONNECTOR << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_CROSSFIRE_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_CROSSFIRE << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_CROSSFIRE_ENUM_ID2 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_CROSSFIRE << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_HARDCODE_DVI_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_HARDCODE_DVI << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_HARDCODE_DVI_ENUM_ID2 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_HARDCODE_DVI << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_DISPLAYPORT_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_DISPLAYPORT_ENUM_ID2 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_DISPLAYPORT_ENUM_ID3 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID3 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT)
|
|
-
|
|
-#define CONNECTOR_DISPLAYPORT_ENUM_ID4 \
|
|
- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID4 << ENUM_ID_SHIFT |\
|
|
- CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT)
|
|
+#define CONNECTOR_LVDS_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_LVDS << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_LVDS_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_LVDS << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_eDP_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_eDP << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_eDP_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_eDP << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_SINGLE_LINK_DVI_I_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_SINGLE_LINK_DVI_I_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_DUAL_LINK_DVI_I_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_DUAL_LINK_DVI_I_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_DUAL_LINK_DVI_D_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_DUAL_LINK_DVI_D_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_DUAL_LINK_DVI_D_ENUM_ID3 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID3 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_VGA_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_VGA << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_VGA_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_VGA << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_COMPOSITE_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_COMPOSITE << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_COMPOSITE_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_COMPOSITE << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_SVIDEO_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_SVIDEO << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_SVIDEO_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_SVIDEO << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_YPbPr_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_YPbPr << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_YPbPr_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_YPbPr << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_D_CONNECTOR_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_D_CONNECTOR << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_D_CONNECTOR_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_D_CONNECTOR << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_9PIN_DIN_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_9PIN_DIN << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_9PIN_DIN_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_9PIN_DIN << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_SCART_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_SCART << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_SCART_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_SCART << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_HDMI_TYPE_A_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_HDMI_TYPE_A_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_HDMI_TYPE_A_ENUM_ID3 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID3 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_HDMI_TYPE_B_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_HDMI_TYPE_B << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_HDMI_TYPE_B_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_HDMI_TYPE_B << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_7PIN_DIN_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_7PIN_DIN << OBJECT_ID_SHIFT)
|
|
+#define CONNECTOR_7PIN_DIN_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_7PIN_DIN << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_PCIE_CONNECTOR_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_PCIE_CONNECTOR << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_PCIE_CONNECTOR_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_PCIE_CONNECTOR << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_CROSSFIRE_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_CROSSFIRE << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_CROSSFIRE_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_CROSSFIRE << OBJECT_ID_SHIFT)
|
|
+
|
|
+
|
|
+#define CONNECTOR_HARDCODE_DVI_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_HARDCODE_DVI << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_HARDCODE_DVI_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_HARDCODE_DVI << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_DISPLAYPORT_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_DISPLAYPORT_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_DISPLAYPORT_ENUM_ID3 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID3 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_DISPLAYPORT_ENUM_ID4 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID4 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_DISPLAYPORT_ENUM_ID5 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID5 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_DISPLAYPORT_ENUM_ID6 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID6 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define CONNECTOR_MXM_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_DP_A
|
|
+
|
|
+#define CONNECTOR_MXM_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_DP_B
|
|
+
|
|
+#define CONNECTOR_MXM_ENUM_ID3 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID3 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_DP_C
|
|
+
|
|
+#define CONNECTOR_MXM_ENUM_ID4 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID4 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_DP_D
|
|
+
|
|
+#define CONNECTOR_MXM_ENUM_ID5 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID5 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_LVDS_TXxx
|
|
+
|
|
+#define CONNECTOR_MXM_ENUM_ID6 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID6 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_LVDS_UXxx
|
|
+
|
|
+#define CONNECTOR_MXM_ENUM_ID7 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID7 << ENUM_ID_SHIFT |\
|
|
+ CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_DAC
|
|
|
|
/****************************************************/
|
|
/* Router Object ID definition - Shared with BIOS */
|
|
/****************************************************/
|
|
-#define ROUTER_I2C_EXTENDER_CNTL_ENUM_ID1 \
|
|
- (GRAPH_OBJECT_TYPE_ROUTER << OBJECT_TYPE_SHIFT |\
|
|
- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
- ROUTER_OBJECT_ID_I2C_EXTENDER_CNTL << OBJECT_ID_SHIFT)
|
|
+#define ROUTER_I2C_EXTENDER_CNTL_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ROUTER << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ ROUTER_OBJECT_ID_I2C_EXTENDER_CNTL << OBJECT_ID_SHIFT)
|
|
|
|
/* deleted */
|
|
|
|
/****************************************************/
|
|
+/* Generic Object ID definition - Shared with BIOS */
|
|
+/****************************************************/
|
|
+#define GENERICOBJECT_GLSYNC_ENUM_ID1 (GRAPH_OBJECT_TYPE_GENERIC << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ GENERIC_OBJECT_ID_GLSYNC << OBJECT_ID_SHIFT)
|
|
+
|
|
+#define GENERICOBJECT_PX2_NON_DRIVABLE_ID1 (GRAPH_OBJECT_TYPE_GENERIC << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ GENERIC_OBJECT_ID_PX2_NON_DRIVABLE<< OBJECT_ID_SHIFT)
|
|
+
|
|
+#define GENERICOBJECT_PX2_NON_DRIVABLE_ID2 (GRAPH_OBJECT_TYPE_GENERIC << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
|
|
+ GENERIC_OBJECT_ID_PX2_NON_DRIVABLE<< OBJECT_ID_SHIFT)
|
|
+
|
|
+#define GENERICOBJECT_MXM_OPM_ENUM_ID1 (GRAPH_OBJECT_TYPE_GENERIC << OBJECT_TYPE_SHIFT |\
|
|
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
|
|
+ GENERIC_OBJECT_ID_MXM_OPM << OBJECT_ID_SHIFT)
|
|
+
|
|
+/****************************************************/
|
|
/* Object Cap definition - Shared with BIOS */
|
|
/****************************************************/
|
|
#define GRAPHICS_OBJECT_CAP_I2C 0x00000001L
|
|
#define GRAPHICS_OBJECT_CAP_TABLE_ID 0x00000002L
|
|
|
|
+
|
|
#define GRAPHICS_OBJECT_I2CCOMMAND_TABLE_ID 0x01
|
|
#define GRAPHICS_OBJECT_HOTPLUGDETECTIONINTERUPT_TABLE_ID 0x02
|
|
#define GRAPHICS_OBJECT_ENCODER_OUTPUT_PROTECTION_TABLE_ID 0x03
|
|
@@ -575,4 +636,8 @@
|
|
#pragma pack()
|
|
#endif
|
|
|
|
-#endif /*GRAPHICTYPE */
|
|
+#endif /*GRAPHICTYPE */
|
|
+
|
|
+
|
|
+
|
|
+
|
|
diff --git a/drivers/gpu/drm/radeon/atom.c b/drivers/gpu/drm/radeon/atom.c
|
|
index d67c425..592ce91 100644
|
|
--- a/drivers/gpu/drm/radeon/atom.c
|
|
+++ b/drivers/gpu/drm/radeon/atom.c
|
|
@@ -24,6 +24,7 @@
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/sched.h>
|
|
+#include <asm/unaligned.h>
|
|
|
|
#define ATOM_DEBUG
|
|
|
|
@@ -58,6 +59,7 @@ typedef struct {
|
|
} atom_exec_context;
|
|
|
|
int atom_debug = 0;
|
|
+static void atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params);
|
|
void atom_execute_table(struct atom_context *ctx, int index, uint32_t * params);
|
|
|
|
static uint32_t atom_arg_mask[8] =
|
|
@@ -211,7 +213,9 @@ static uint32_t atom_get_src_int(atom_exec_context *ctx, uint8_t attr,
|
|
case ATOM_ARG_PS:
|
|
idx = U8(*ptr);
|
|
(*ptr)++;
|
|
- val = le32_to_cpu(ctx->ps[idx]);
|
|
+ /* get_unaligned_le32 avoids unaligned accesses from atombios
|
|
+ * tables, noticed on a DEC Alpha. */
|
|
+ val = get_unaligned_le32((u32 *)&ctx->ps[idx]);
|
|
if (print)
|
|
DEBUG("PS[0x%02X,0x%04X]", idx, val);
|
|
break;
|
|
@@ -245,6 +249,9 @@ static uint32_t atom_get_src_int(atom_exec_context *ctx, uint8_t attr,
|
|
case ATOM_WS_ATTRIBUTES:
|
|
val = gctx->io_attr;
|
|
break;
|
|
+ case ATOM_WS_REGPTR:
|
|
+ val = gctx->reg_block;
|
|
+ break;
|
|
default:
|
|
val = ctx->ws[idx];
|
|
}
|
|
@@ -263,10 +270,10 @@ static uint32_t atom_get_src_int(atom_exec_context *ctx, uint8_t attr,
|
|
case ATOM_ARG_FB:
|
|
idx = U8(*ptr);
|
|
(*ptr)++;
|
|
+ val = gctx->scratch[((gctx->fb_base + idx) / 4)];
|
|
if (print)
|
|
DEBUG("FB[0x%02X]", idx);
|
|
- printk(KERN_INFO "FB access is not implemented.\n");
|
|
- return 0;
|
|
+ break;
|
|
case ATOM_ARG_IMM:
|
|
switch (align) {
|
|
case ATOM_SRC_DWORD:
|
|
@@ -384,6 +391,32 @@ static uint32_t atom_get_src(atom_exec_context *ctx, uint8_t attr, int *ptr)
|
|
return atom_get_src_int(ctx, attr, ptr, NULL, 1);
|
|
}
|
|
|
|
+static uint32_t atom_get_src_direct(atom_exec_context *ctx, uint8_t align, int *ptr)
|
|
+{
|
|
+ uint32_t val = 0xCDCDCDCD;
|
|
+
|
|
+ switch (align) {
|
|
+ case ATOM_SRC_DWORD:
|
|
+ val = U32(*ptr);
|
|
+ (*ptr) += 4;
|
|
+ break;
|
|
+ case ATOM_SRC_WORD0:
|
|
+ case ATOM_SRC_WORD8:
|
|
+ case ATOM_SRC_WORD16:
|
|
+ val = U16(*ptr);
|
|
+ (*ptr) += 2;
|
|
+ break;
|
|
+ case ATOM_SRC_BYTE0:
|
|
+ case ATOM_SRC_BYTE8:
|
|
+ case ATOM_SRC_BYTE16:
|
|
+ case ATOM_SRC_BYTE24:
|
|
+ val = U8(*ptr);
|
|
+ (*ptr)++;
|
|
+ break;
|
|
+ }
|
|
+ return val;
|
|
+}
|
|
+
|
|
static uint32_t atom_get_dst(atom_exec_context *ctx, int arg, uint8_t attr,
|
|
int *ptr, uint32_t *saved, int print)
|
|
{
|
|
@@ -481,6 +514,9 @@ static void atom_put_dst(atom_exec_context *ctx, int arg, uint8_t attr,
|
|
case ATOM_WS_ATTRIBUTES:
|
|
gctx->io_attr = val;
|
|
break;
|
|
+ case ATOM_WS_REGPTR:
|
|
+ gctx->reg_block = val;
|
|
+ break;
|
|
default:
|
|
ctx->ws[idx] = val;
|
|
}
|
|
@@ -488,9 +524,9 @@ static void atom_put_dst(atom_exec_context *ctx, int arg, uint8_t attr,
|
|
case ATOM_ARG_FB:
|
|
idx = U8(*ptr);
|
|
(*ptr)++;
|
|
+ gctx->scratch[((gctx->fb_base + idx) / 4)] = val;
|
|
DEBUG("FB[0x%02X]", idx);
|
|
- printk(KERN_INFO "FB access is not implemented.\n");
|
|
- return;
|
|
+ break;
|
|
case ATOM_ARG_PLL:
|
|
idx = U8(*ptr);
|
|
(*ptr)++;
|
|
@@ -573,7 +609,7 @@ static void atom_op_calltable(atom_exec_context *ctx, int *ptr, int arg)
|
|
else
|
|
SDEBUG(" table: %d\n", idx);
|
|
if (U16(ctx->ctx->cmd_table + 4 + 2 * idx))
|
|
- atom_execute_table(ctx->ctx, idx, ctx->ps + ctx->ps_shift);
|
|
+ atom_execute_table_locked(ctx->ctx, idx, ctx->ps + ctx->ps_shift);
|
|
}
|
|
|
|
static void atom_op_clear(atom_exec_context *ctx, int *ptr, int arg)
|
|
@@ -607,7 +643,8 @@ static void atom_op_delay(atom_exec_context *ctx, int *ptr, int arg)
|
|
uint8_t count = U8((*ptr)++);
|
|
SDEBUG(" count: %d\n", count);
|
|
if (arg == ATOM_UNIT_MICROSEC)
|
|
- schedule_timeout_uninterruptible(usecs_to_jiffies(count));
|
|
+ udelay(count);
|
|
+ // schedule_timeout_uninterruptible(usecs_to_jiffies(count));
|
|
else
|
|
schedule_timeout_uninterruptible(msecs_to_jiffies(count));
|
|
}
|
|
@@ -676,7 +713,7 @@ static void atom_op_mask(atom_exec_context *ctx, int *ptr, int arg)
|
|
SDEBUG(" dst: ");
|
|
dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
|
|
SDEBUG(" src1: ");
|
|
- src1 = atom_get_src(ctx, attr, ptr);
|
|
+ src1 = atom_get_src_direct(ctx, ((attr >> 3) & 7), ptr);
|
|
SDEBUG(" src2: ");
|
|
src2 = atom_get_src(ctx, attr, ptr);
|
|
dst &= src1;
|
|
@@ -808,6 +845,38 @@ static void atom_op_setregblock(atom_exec_context *ctx, int *ptr, int arg)
|
|
SDEBUG(" base: 0x%04X\n", ctx->ctx->reg_block);
|
|
}
|
|
|
|
+static void atom_op_shift_left(atom_exec_context *ctx, int *ptr, int arg)
|
|
+{
|
|
+ uint8_t attr = U8((*ptr)++), shift;
|
|
+ uint32_t saved, dst;
|
|
+ int dptr = *ptr;
|
|
+ attr &= 0x38;
|
|
+ attr |= atom_def_dst[attr >> 3] << 6;
|
|
+ SDEBUG(" dst: ");
|
|
+ dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
|
|
+ shift = atom_get_src_direct(ctx, ATOM_SRC_BYTE0, ptr);
|
|
+ SDEBUG(" shift: %d\n", shift);
|
|
+ dst <<= shift;
|
|
+ SDEBUG(" dst: ");
|
|
+ atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
|
|
+}
|
|
+
|
|
+static void atom_op_shift_right(atom_exec_context *ctx, int *ptr, int arg)
|
|
+{
|
|
+ uint8_t attr = U8((*ptr)++), shift;
|
|
+ uint32_t saved, dst;
|
|
+ int dptr = *ptr;
|
|
+ attr &= 0x38;
|
|
+ attr |= atom_def_dst[attr >> 3] << 6;
|
|
+ SDEBUG(" dst: ");
|
|
+ dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
|
|
+ shift = atom_get_src_direct(ctx, ATOM_SRC_BYTE0, ptr);
|
|
+ SDEBUG(" shift: %d\n", shift);
|
|
+ dst >>= shift;
|
|
+ SDEBUG(" dst: ");
|
|
+ atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
|
|
+}
|
|
+
|
|
static void atom_op_shl(atom_exec_context *ctx, int *ptr, int arg)
|
|
{
|
|
uint8_t attr = U8((*ptr)++), shift;
|
|
@@ -817,7 +886,7 @@ static void atom_op_shl(atom_exec_context *ctx, int *ptr, int arg)
|
|
attr |= atom_def_dst[attr >> 3] << 6;
|
|
SDEBUG(" dst: ");
|
|
dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
|
|
- shift = U8((*ptr)++);
|
|
+ shift = atom_get_src(ctx, attr, ptr);
|
|
SDEBUG(" shift: %d\n", shift);
|
|
dst <<= shift;
|
|
SDEBUG(" dst: ");
|
|
@@ -833,7 +902,7 @@ static void atom_op_shr(atom_exec_context *ctx, int *ptr, int arg)
|
|
attr |= atom_def_dst[attr >> 3] << 6;
|
|
SDEBUG(" dst: ");
|
|
dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
|
|
- shift = U8((*ptr)++);
|
|
+ shift = atom_get_src(ctx, attr, ptr);
|
|
SDEBUG(" shift: %d\n", shift);
|
|
dst >>= shift;
|
|
SDEBUG(" dst: ");
|
|
@@ -936,18 +1005,18 @@ static struct {
|
|
atom_op_or, ATOM_ARG_FB}, {
|
|
atom_op_or, ATOM_ARG_PLL}, {
|
|
atom_op_or, ATOM_ARG_MC}, {
|
|
- atom_op_shl, ATOM_ARG_REG}, {
|
|
- atom_op_shl, ATOM_ARG_PS}, {
|
|
- atom_op_shl, ATOM_ARG_WS}, {
|
|
- atom_op_shl, ATOM_ARG_FB}, {
|
|
- atom_op_shl, ATOM_ARG_PLL}, {
|
|
- atom_op_shl, ATOM_ARG_MC}, {
|
|
- atom_op_shr, ATOM_ARG_REG}, {
|
|
- atom_op_shr, ATOM_ARG_PS}, {
|
|
- atom_op_shr, ATOM_ARG_WS}, {
|
|
- atom_op_shr, ATOM_ARG_FB}, {
|
|
- atom_op_shr, ATOM_ARG_PLL}, {
|
|
- atom_op_shr, ATOM_ARG_MC}, {
|
|
+ atom_op_shift_left, ATOM_ARG_REG}, {
|
|
+ atom_op_shift_left, ATOM_ARG_PS}, {
|
|
+ atom_op_shift_left, ATOM_ARG_WS}, {
|
|
+ atom_op_shift_left, ATOM_ARG_FB}, {
|
|
+ atom_op_shift_left, ATOM_ARG_PLL}, {
|
|
+ atom_op_shift_left, ATOM_ARG_MC}, {
|
|
+ atom_op_shift_right, ATOM_ARG_REG}, {
|
|
+ atom_op_shift_right, ATOM_ARG_PS}, {
|
|
+ atom_op_shift_right, ATOM_ARG_WS}, {
|
|
+ atom_op_shift_right, ATOM_ARG_FB}, {
|
|
+ atom_op_shift_right, ATOM_ARG_PLL}, {
|
|
+ atom_op_shift_right, ATOM_ARG_MC}, {
|
|
atom_op_mul, ATOM_ARG_REG}, {
|
|
atom_op_mul, ATOM_ARG_PS}, {
|
|
atom_op_mul, ATOM_ARG_WS}, {
|
|
@@ -1040,7 +1109,7 @@ static struct {
|
|
atom_op_shr, ATOM_ARG_MC}, {
|
|
atom_op_debug, 0},};
|
|
|
|
-void atom_execute_table(struct atom_context *ctx, int index, uint32_t * params)
|
|
+static void atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params)
|
|
{
|
|
int base = CU16(ctx->cmd_table + 4 + 2 * index);
|
|
int len, ws, ps, ptr;
|
|
@@ -1057,8 +1126,6 @@ void atom_execute_table(struct atom_context *ctx, int index, uint32_t * params)
|
|
|
|
SDEBUG(">> execute %04X (len %d, WS %d, PS %d)\n", base, len, ws, ps);
|
|
|
|
- /* reset reg block */
|
|
- ctx->reg_block = 0;
|
|
ectx.ctx = ctx;
|
|
ectx.ps_shift = ps / 4;
|
|
ectx.start = base;
|
|
@@ -1092,6 +1159,19 @@ void atom_execute_table(struct atom_context *ctx, int index, uint32_t * params)
|
|
kfree(ectx.ws);
|
|
}
|
|
|
|
+void atom_execute_table(struct atom_context *ctx, int index, uint32_t * params)
|
|
+{
|
|
+ mutex_lock(&ctx->mutex);
|
|
+ /* reset reg block */
|
|
+ ctx->reg_block = 0;
|
|
+ /* reset fb window */
|
|
+ ctx->fb_base = 0;
|
|
+ /* reset io mode */
|
|
+ ctx->io_mode = ATOM_IO_MM;
|
|
+ atom_execute_table_locked(ctx, index, params);
|
|
+ mutex_unlock(&ctx->mutex);
|
|
+}
|
|
+
|
|
static int atom_iio_len[] = { 1, 2, 3, 3, 3, 3, 4, 4, 4, 3 };
|
|
|
|
static void atom_index_iio(struct atom_context *ctx, int base)
|
|
@@ -1214,3 +1294,28 @@ void atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t * frev,
|
|
*crev = CU8(idx + 3);
|
|
return;
|
|
}
|
|
+
|
|
+int atom_allocate_fb_scratch(struct atom_context *ctx)
|
|
+{
|
|
+ int index = GetIndexIntoMasterTable(DATA, VRAM_UsageByFirmware);
|
|
+ uint16_t data_offset;
|
|
+ int usage_bytes;
|
|
+ struct _ATOM_VRAM_USAGE_BY_FIRMWARE *firmware_usage;
|
|
+
|
|
+ atom_parse_data_header(ctx, index, NULL, NULL, NULL, &data_offset);
|
|
+
|
|
+ firmware_usage = (struct _ATOM_VRAM_USAGE_BY_FIRMWARE *)(ctx->bios + data_offset);
|
|
+
|
|
+ DRM_DEBUG("atom firmware requested %08x %dkb\n",
|
|
+ firmware_usage->asFirmwareVramReserveInfo[0].ulStartAddrUsedByFirmware,
|
|
+ firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb);
|
|
+
|
|
+ usage_bytes = firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb * 1024;
|
|
+ if (usage_bytes == 0)
|
|
+ usage_bytes = 20 * 1024;
|
|
+ /* allocate some scratch memory */
|
|
+ ctx->scratch = kzalloc(usage_bytes, GFP_KERNEL);
|
|
+ if (!ctx->scratch)
|
|
+ return -ENOMEM;
|
|
+ return 0;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/radeon/atom.h b/drivers/gpu/drm/radeon/atom.h
|
|
index e6eb38f..bc73781 100644
|
|
--- a/drivers/gpu/drm/radeon/atom.h
|
|
+++ b/drivers/gpu/drm/radeon/atom.h
|
|
@@ -91,6 +91,7 @@
|
|
#define ATOM_WS_AND_MASK 0x45
|
|
#define ATOM_WS_FB_WINDOW 0x46
|
|
#define ATOM_WS_ATTRIBUTES 0x47
|
|
+#define ATOM_WS_REGPTR 0x48
|
|
|
|
#define ATOM_IIO_NOP 0
|
|
#define ATOM_IIO_START 1
|
|
@@ -120,6 +121,7 @@ struct card_info {
|
|
|
|
struct atom_context {
|
|
struct card_info *card;
|
|
+ struct mutex mutex;
|
|
void *bios;
|
|
uint32_t cmd_table, data_table;
|
|
uint16_t *iio;
|
|
@@ -132,6 +134,7 @@ struct atom_context {
|
|
uint8_t shift;
|
|
int cs_equal, cs_above;
|
|
int io_mode;
|
|
+ uint32_t *scratch;
|
|
};
|
|
|
|
extern int atom_debug;
|
|
@@ -142,6 +145,7 @@ int atom_asic_init(struct atom_context *);
|
|
void atom_destroy(struct atom_context *);
|
|
void atom_parse_data_header(struct atom_context *ctx, int index, uint16_t *size, uint8_t *frev, uint8_t *crev, uint16_t *data_start);
|
|
void atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t *frev, uint8_t *crev);
|
|
+int atom_allocate_fb_scratch(struct atom_context *ctx);
|
|
#include "atom-types.h"
|
|
#include "atombios.h"
|
|
#include "ObjectID.h"
|
|
diff --git a/drivers/gpu/drm/radeon/atombios.h b/drivers/gpu/drm/radeon/atombios.h
|
|
index c11dddd..91ad0d1 100644
|
|
--- a/drivers/gpu/drm/radeon/atombios.h
|
|
+++ b/drivers/gpu/drm/radeon/atombios.h
|
|
@@ -1141,7 +1141,7 @@ typedef struct _LVDS_ENCODER_CONTROL_PARAMETERS {
|
|
/* ucTableFormatRevision=1,ucTableContentRevision=2 */
|
|
typedef struct _LVDS_ENCODER_CONTROL_PARAMETERS_V2 {
|
|
USHORT usPixelClock; /* in 10KHz; for bios convenient */
|
|
- UCHAR ucMisc; /* see PANEL_ENCODER_MISC_xx defintions below */
|
|
+ UCHAR ucMisc; /* see PANEL_ENCODER_MISC_xx definitions below */
|
|
UCHAR ucAction; /* 0: turn off encoder */
|
|
/* 1: setup and turn on encoder */
|
|
UCHAR ucTruncate; /* bit0=0: Disable truncate */
|
|
@@ -1424,7 +1424,7 @@ typedef struct _ATOM_MULTIMEDIA_CONFIG_INFO {
|
|
/* Structures used in FirmwareInfoTable */
|
|
/****************************************************************************/
|
|
|
|
-/* usBIOSCapability Defintion: */
|
|
+/* usBIOSCapability Definition: */
|
|
/* Bit 0 = 0: Bios image is not Posted, =1:Bios image is Posted; */
|
|
/* Bit 1 = 0: Dual CRTC is not supported, =1: Dual CRTC is supported; */
|
|
/* Bit 2 = 0: Extended Desktop is not supported, =1: Extended Desktop is supported; */
|
|
@@ -2386,7 +2386,7 @@ typedef struct _ATOM_ANALOG_TV_INFO_V1_2 {
|
|
} ATOM_ANALOG_TV_INFO_V1_2;
|
|
|
|
/**************************************************************************/
|
|
-/* VRAM usage and their defintions */
|
|
+/* VRAM usage and their definitions */
|
|
|
|
/* One chunk of VRAM used by Bios are for HWICON surfaces,EDID data. */
|
|
/* Current Mode timing and Dail Timing and/or STD timing data EACH device. They can be broken down as below. */
|
|
@@ -2680,7 +2680,7 @@ typedef struct _ATOM_I2C_RECORD {
|
|
typedef struct _ATOM_HPD_INT_RECORD {
|
|
ATOM_COMMON_RECORD_HEADER sheader;
|
|
UCHAR ucHPDIntGPIOID; /* Corresponding block in GPIO_PIN_INFO table gives the pin info */
|
|
- UCHAR ucPluggged_PinState;
|
|
+ UCHAR ucPlugged_PinState;
|
|
} ATOM_HPD_INT_RECORD;
|
|
|
|
typedef struct _ATOM_OUTPUT_PROTECTION_RECORD {
|
|
@@ -3046,7 +3046,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
|
|
#define ATOM_S0_SYSTEM_POWER_STATE_VALUE_DC 2
|
|
#define ATOM_S0_SYSTEM_POWER_STATE_VALUE_LITEAC 3
|
|
|
|
-/* Byte aligned defintion for BIOS usage */
|
|
+/* Byte aligned definition for BIOS usage */
|
|
#define ATOM_S0_CRT1_MONOb0 0x01
|
|
#define ATOM_S0_CRT1_COLORb0 0x02
|
|
#define ATOM_S0_CRT1_MASKb0 (ATOM_S0_CRT1_MONOb0+ATOM_S0_CRT1_COLORb0)
|
|
@@ -3131,7 +3131,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
|
|
#define ATOM_S2_DISPLAY_ROTATION_DEGREE_SHIFT 30
|
|
#define ATOM_S2_DISPLAY_ROTATION_ANGLE_MASK 0xC0000000L
|
|
|
|
-/* Byte aligned defintion for BIOS usage */
|
|
+/* Byte aligned definition for BIOS usage */
|
|
#define ATOM_S2_TV1_STANDARD_MASKb0 0x0F
|
|
#define ATOM_S2_CURRENT_BL_LEVEL_MASKb1 0xFF
|
|
#define ATOM_S2_CRT1_DPMS_STATEb2 0x01
|
|
@@ -3190,7 +3190,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
|
|
#define ATOM_S3_ALLOW_FAST_PWR_SWITCH 0x40000000L
|
|
#define ATOM_S3_RQST_GPU_USE_MIN_PWR 0x80000000L
|
|
|
|
-/* Byte aligned defintion for BIOS usage */
|
|
+/* Byte aligned definition for BIOS usage */
|
|
#define ATOM_S3_CRT1_ACTIVEb0 0x01
|
|
#define ATOM_S3_LCD1_ACTIVEb0 0x02
|
|
#define ATOM_S3_TV1_ACTIVEb0 0x04
|
|
@@ -3230,7 +3230,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
|
|
#define ATOM_S4_LCD1_REFRESH_MASK 0x0000FF00L
|
|
#define ATOM_S4_LCD1_REFRESH_SHIFT 8
|
|
|
|
-/* Byte aligned defintion for BIOS usage */
|
|
+/* Byte aligned definition for BIOS usage */
|
|
#define ATOM_S4_LCD1_PANEL_ID_MASKb0 0x0FF
|
|
#define ATOM_S4_LCD1_REFRESH_MASKb1 ATOM_S4_LCD1_PANEL_ID_MASKb0
|
|
#define ATOM_S4_VRAM_INFO_MASKb2 ATOM_S4_LCD1_PANEL_ID_MASKb0
|
|
@@ -3310,7 +3310,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
|
|
#define ATOM_S6_VRI_BRIGHTNESS_CHANGE 0x40000000L
|
|
#define ATOM_S6_CONFIG_DISPLAY_CHANGE_MASK 0x80000000L
|
|
|
|
-/* Byte aligned defintion for BIOS usage */
|
|
+/* Byte aligned definition for BIOS usage */
|
|
#define ATOM_S6_DEVICE_CHANGEb0 0x01
|
|
#define ATOM_S6_SCALER_CHANGEb0 0x02
|
|
#define ATOM_S6_LID_CHANGEb0 0x04
|
|
@@ -4690,6 +4690,205 @@ typedef struct _ATOM_POWERPLAY_INFO_V3 {
|
|
ATOM_POWERMODE_INFO_V3 asPowerPlayInfo[ATOM_MAX_NUMBEROF_POWER_BLOCK];
|
|
} ATOM_POWERPLAY_INFO_V3;
|
|
|
|
+/* New PPlib */
|
|
+/**************************************************************************/
|
|
+typedef struct _ATOM_PPLIB_THERMALCONTROLLER
|
|
+
|
|
+{
|
|
+ UCHAR ucType; // one of ATOM_PP_THERMALCONTROLLER_*
|
|
+ UCHAR ucI2cLine; // as interpreted by DAL I2C
|
|
+ UCHAR ucI2cAddress;
|
|
+ UCHAR ucFanParameters; // Fan Control Parameters.
|
|
+ UCHAR ucFanMinRPM; // Fan Minimum RPM (hundreds) -- for display purposes only.
|
|
+ UCHAR ucFanMaxRPM; // Fan Maximum RPM (hundreds) -- for display purposes only.
|
|
+ UCHAR ucReserved; // ----
|
|
+ UCHAR ucFlags; // to be defined
|
|
+} ATOM_PPLIB_THERMALCONTROLLER;
|
|
+
|
|
+#define ATOM_PP_FANPARAMETERS_TACHOMETER_PULSES_PER_REVOLUTION_MASK 0x0f
|
|
+#define ATOM_PP_FANPARAMETERS_NOFAN 0x80 // No fan is connected to this controller.
|
|
+
|
|
+#define ATOM_PP_THERMALCONTROLLER_NONE 0
|
|
+#define ATOM_PP_THERMALCONTROLLER_LM63 1 // Not used by PPLib
|
|
+#define ATOM_PP_THERMALCONTROLLER_ADM1032 2 // Not used by PPLib
|
|
+#define ATOM_PP_THERMALCONTROLLER_ADM1030 3 // Not used by PPLib
|
|
+#define ATOM_PP_THERMALCONTROLLER_MUA6649 4 // Not used by PPLib
|
|
+#define ATOM_PP_THERMALCONTROLLER_LM64 5
|
|
+#define ATOM_PP_THERMALCONTROLLER_F75375 6 // Not used by PPLib
|
|
+#define ATOM_PP_THERMALCONTROLLER_RV6xx 7
|
|
+#define ATOM_PP_THERMALCONTROLLER_RV770 8
|
|
+#define ATOM_PP_THERMALCONTROLLER_ADT7473 9
|
|
+
|
|
+typedef struct _ATOM_PPLIB_STATE
|
|
+{
|
|
+ UCHAR ucNonClockStateIndex;
|
|
+ UCHAR ucClockStateIndices[1]; // variable-sized
|
|
+} ATOM_PPLIB_STATE;
|
|
+
|
|
+//// ATOM_PPLIB_POWERPLAYTABLE::ulPlatformCaps
|
|
+#define ATOM_PP_PLATFORM_CAP_BACKBIAS 1
|
|
+#define ATOM_PP_PLATFORM_CAP_POWERPLAY 2
|
|
+#define ATOM_PP_PLATFORM_CAP_SBIOSPOWERSOURCE 4
|
|
+#define ATOM_PP_PLATFORM_CAP_ASPM_L0s 8
|
|
+#define ATOM_PP_PLATFORM_CAP_ASPM_L1 16
|
|
+#define ATOM_PP_PLATFORM_CAP_HARDWAREDC 32
|
|
+#define ATOM_PP_PLATFORM_CAP_GEMINIPRIMARY 64
|
|
+#define ATOM_PP_PLATFORM_CAP_STEPVDDC 128
|
|
+#define ATOM_PP_PLATFORM_CAP_VOLTAGECONTROL 256
|
|
+#define ATOM_PP_PLATFORM_CAP_SIDEPORTCONTROL 512
|
|
+#define ATOM_PP_PLATFORM_CAP_TURNOFFPLL_ASPML1 1024
|
|
+#define ATOM_PP_PLATFORM_CAP_HTLINKCONTROL 2048
|
|
+
|
|
+typedef struct _ATOM_PPLIB_POWERPLAYTABLE
|
|
+{
|
|
+ ATOM_COMMON_TABLE_HEADER sHeader;
|
|
+
|
|
+ UCHAR ucDataRevision;
|
|
+
|
|
+ UCHAR ucNumStates;
|
|
+ UCHAR ucStateEntrySize;
|
|
+ UCHAR ucClockInfoSize;
|
|
+ UCHAR ucNonClockSize;
|
|
+
|
|
+ // offset from start of this table to array of ucNumStates ATOM_PPLIB_STATE structures
|
|
+ USHORT usStateArrayOffset;
|
|
+
|
|
+ // offset from start of this table to array of ASIC-specific structures,
|
|
+ // currently ATOM_PPLIB_CLOCK_INFO.
|
|
+ USHORT usClockInfoArrayOffset;
|
|
+
|
|
+ // offset from start of this table to array of ATOM_PPLIB_NONCLOCK_INFO
|
|
+ USHORT usNonClockInfoArrayOffset;
|
|
+
|
|
+ USHORT usBackbiasTime; // in microseconds
|
|
+ USHORT usVoltageTime; // in microseconds
|
|
+ USHORT usTableSize; //the size of this structure, or the extended structure
|
|
+
|
|
+ ULONG ulPlatformCaps; // See ATOM_PPLIB_CAPS_*
|
|
+
|
|
+ ATOM_PPLIB_THERMALCONTROLLER sThermalController;
|
|
+
|
|
+ USHORT usBootClockInfoOffset;
|
|
+ USHORT usBootNonClockInfoOffset;
|
|
+
|
|
+} ATOM_PPLIB_POWERPLAYTABLE;
|
|
+
|
|
+//// ATOM_PPLIB_NONCLOCK_INFO::usClassification
|
|
+#define ATOM_PPLIB_CLASSIFICATION_UI_MASK 0x0007
|
|
+#define ATOM_PPLIB_CLASSIFICATION_UI_SHIFT 0
|
|
+#define ATOM_PPLIB_CLASSIFICATION_UI_NONE 0
|
|
+#define ATOM_PPLIB_CLASSIFICATION_UI_BATTERY 1
|
|
+#define ATOM_PPLIB_CLASSIFICATION_UI_BALANCED 3
|
|
+#define ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE 5
|
|
+// 2, 4, 6, 7 are reserved
|
|
+
|
|
+#define ATOM_PPLIB_CLASSIFICATION_BOOT 0x0008
|
|
+#define ATOM_PPLIB_CLASSIFICATION_THERMAL 0x0010
|
|
+#define ATOM_PPLIB_CLASSIFICATION_LIMITEDPOWERSOURCE 0x0020
|
|
+#define ATOM_PPLIB_CLASSIFICATION_REST 0x0040
|
|
+#define ATOM_PPLIB_CLASSIFICATION_FORCED 0x0080
|
|
+#define ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE 0x0100
|
|
+#define ATOM_PPLIB_CLASSIFICATION_OVERDRIVETEMPLATE 0x0200
|
|
+#define ATOM_PPLIB_CLASSIFICATION_UVDSTATE 0x0400
|
|
+#define ATOM_PPLIB_CLASSIFICATION_3DLOW 0x0800
|
|
+#define ATOM_PPLIB_CLASSIFICATION_ACPI 0x1000
|
|
+// remaining 3 bits are reserved
|
|
+
|
|
+//// ATOM_PPLIB_NONCLOCK_INFO::ulCapsAndSettings
|
|
+#define ATOM_PPLIB_SINGLE_DISPLAY_ONLY 0x00000001
|
|
+#define ATOM_PPLIB_SUPPORTS_VIDEO_PLAYBACK 0x00000002
|
|
+
|
|
+// 0 is 2.5Gb/s, 1 is 5Gb/s
|
|
+#define ATOM_PPLIB_PCIE_LINK_SPEED_MASK 0x00000004
|
|
+#define ATOM_PPLIB_PCIE_LINK_SPEED_SHIFT 2
|
|
+
|
|
+// lanes - 1: 1, 2, 4, 8, 12, 16 permitted by PCIE spec
|
|
+#define ATOM_PPLIB_PCIE_LINK_WIDTH_MASK 0x000000F8
|
|
+#define ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT 3
|
|
+
|
|
+// lookup into reduced refresh-rate table
|
|
+#define ATOM_PPLIB_LIMITED_REFRESHRATE_VALUE_MASK 0x00000F00
|
|
+#define ATOM_PPLIB_LIMITED_REFRESHRATE_VALUE_SHIFT 8
|
|
+
|
|
+#define ATOM_PPLIB_LIMITED_REFRESHRATE_UNLIMITED 0
|
|
+#define ATOM_PPLIB_LIMITED_REFRESHRATE_50HZ 1
|
|
+// 2-15 TBD as needed.
|
|
+
|
|
+#define ATOM_PPLIB_SOFTWARE_DISABLE_LOADBALANCING 0x00001000
|
|
+#define ATOM_PPLIB_SOFTWARE_ENABLE_SLEEP_FOR_TIMESTAMPS 0x00002000
|
|
+#define ATOM_PPLIB_ENABLE_VARIBRIGHT 0x00008000
|
|
+
|
|
+#define ATOM_PPLIB_DISALLOW_ON_DC 0x00004000
|
|
+
|
|
+// Contained in an array starting at the offset
|
|
+// in ATOM_PPLIB_POWERPLAYTABLE::usNonClockInfoArrayOffset.
|
|
+// referenced from ATOM_PPLIB_STATE_INFO::ucNonClockStateIndex
|
|
+typedef struct _ATOM_PPLIB_NONCLOCK_INFO
|
|
+{
|
|
+ USHORT usClassification;
|
|
+ UCHAR ucMinTemperature;
|
|
+ UCHAR ucMaxTemperature;
|
|
+ ULONG ulCapsAndSettings;
|
|
+ UCHAR ucRequiredPower;
|
|
+ UCHAR ucUnused1[3];
|
|
+} ATOM_PPLIB_NONCLOCK_INFO;
|
|
+
|
|
+// Contained in an array starting at the offset
|
|
+// in ATOM_PPLIB_POWERPLAYTABLE::usClockInfoArrayOffset.
|
|
+// referenced from ATOM_PPLIB_STATE::ucClockStateIndices
|
|
+typedef struct _ATOM_PPLIB_R600_CLOCK_INFO
|
|
+{
|
|
+ USHORT usEngineClockLow;
|
|
+ UCHAR ucEngineClockHigh;
|
|
+
|
|
+ USHORT usMemoryClockLow;
|
|
+ UCHAR ucMemoryClockHigh;
|
|
+
|
|
+ USHORT usVDDC;
|
|
+ USHORT usUnused1;
|
|
+ USHORT usUnused2;
|
|
+
|
|
+ ULONG ulFlags; // ATOM_PPLIB_R600_FLAGS_*
|
|
+
|
|
+} ATOM_PPLIB_R600_CLOCK_INFO;
|
|
+
|
|
+// ulFlags in ATOM_PPLIB_R600_CLOCK_INFO
|
|
+#define ATOM_PPLIB_R600_FLAGS_PCIEGEN2 1
|
|
+#define ATOM_PPLIB_R600_FLAGS_UVDSAFE 2
|
|
+#define ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE 4
|
|
+#define ATOM_PPLIB_R600_FLAGS_MEMORY_ODT_OFF 8
|
|
+#define ATOM_PPLIB_R600_FLAGS_MEMORY_DLL_OFF 16
|
|
+
|
|
+typedef struct _ATOM_PPLIB_RS780_CLOCK_INFO
|
|
+
|
|
+{
|
|
+ USHORT usLowEngineClockLow; // Low Engine clock in MHz (the same way as on the R600).
|
|
+ UCHAR ucLowEngineClockHigh;
|
|
+ USHORT usHighEngineClockLow; // High Engine clock in MHz.
|
|
+ UCHAR ucHighEngineClockHigh;
|
|
+ USHORT usMemoryClockLow; // For now one of the ATOM_PPLIB_RS780_SPMCLK_XXXX constants.
|
|
+ UCHAR ucMemoryClockHigh; // Currentyl unused.
|
|
+ UCHAR ucPadding; // For proper alignment and size.
|
|
+ USHORT usVDDC; // For the 780, use: None, Low, High, Variable
|
|
+ UCHAR ucMaxHTLinkWidth; // From SBIOS - {2, 4, 8, 16}
|
|
+ UCHAR ucMinHTLinkWidth; // From SBIOS - {2, 4, 8, 16}. Effective only if CDLW enabled. Minimum down stream width could be bigger as display BW requriement.
|
|
+ USHORT usHTLinkFreq; // See definition ATOM_PPLIB_RS780_HTLINKFREQ_xxx or in MHz(>=200).
|
|
+ ULONG ulFlags;
|
|
+} ATOM_PPLIB_RS780_CLOCK_INFO;
|
|
+
|
|
+#define ATOM_PPLIB_RS780_VOLTAGE_NONE 0
|
|
+#define ATOM_PPLIB_RS780_VOLTAGE_LOW 1
|
|
+#define ATOM_PPLIB_RS780_VOLTAGE_HIGH 2
|
|
+#define ATOM_PPLIB_RS780_VOLTAGE_VARIABLE 3
|
|
+
|
|
+#define ATOM_PPLIB_RS780_SPMCLK_NONE 0 // We cannot change the side port memory clock, leave it as it is.
|
|
+#define ATOM_PPLIB_RS780_SPMCLK_LOW 1
|
|
+#define ATOM_PPLIB_RS780_SPMCLK_HIGH 2
|
|
+
|
|
+#define ATOM_PPLIB_RS780_HTLINKFREQ_NONE 0
|
|
+#define ATOM_PPLIB_RS780_HTLINKFREQ_LOW 1
|
|
+#define ATOM_PPLIB_RS780_HTLINKFREQ_HIGH 2
|
|
+
|
|
/**************************************************************************/
|
|
|
|
/* Following definitions are for compatiblity issue in different SW components. */
|
|
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c
|
|
index 19f93f2..af464e3 100644
|
|
--- a/drivers/gpu/drm/radeon/atombios_crtc.c
|
|
+++ b/drivers/gpu/drm/radeon/atombios_crtc.c
|
|
@@ -249,15 +249,13 @@ void atombios_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|
if (ASIC_IS_DCE3(rdev))
|
|
atombios_enable_crtc_memreq(crtc, 1);
|
|
atombios_blank_crtc(crtc, 0);
|
|
- if (rdev->family < CHIP_R600)
|
|
- drm_vblank_post_modeset(dev, radeon_crtc->crtc_id);
|
|
+ drm_vblank_post_modeset(dev, radeon_crtc->crtc_id);
|
|
radeon_crtc_load_lut(crtc);
|
|
break;
|
|
case DRM_MODE_DPMS_STANDBY:
|
|
case DRM_MODE_DPMS_SUSPEND:
|
|
case DRM_MODE_DPMS_OFF:
|
|
- if (rdev->family < CHIP_R600)
|
|
- drm_vblank_pre_modeset(dev, radeon_crtc->crtc_id);
|
|
+ drm_vblank_pre_modeset(dev, radeon_crtc->crtc_id);
|
|
atombios_blank_crtc(crtc, 1);
|
|
if (ASIC_IS_DCE3(rdev))
|
|
atombios_enable_crtc_memreq(crtc, 0);
|
|
@@ -309,7 +307,6 @@ atombios_set_crtc_dtd_timing(struct drm_crtc *crtc,
|
|
args.susModeMiscInfo.usAccess = cpu_to_le16(misc);
|
|
args.ucCRTC = radeon_crtc->crtc_id;
|
|
|
|
- printk("executing set crtc dtd timing\n");
|
|
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
|
|
}
|
|
|
|
@@ -349,7 +346,6 @@ static void atombios_crtc_set_timing(struct drm_crtc *crtc,
|
|
args.susModeMiscInfo.usAccess = cpu_to_le16(misc);
|
|
args.ucCRTC = radeon_crtc->crtc_id;
|
|
|
|
- printk("executing set crtc timing\n");
|
|
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
|
|
}
|
|
|
|
@@ -411,60 +407,57 @@ static void atombios_set_ss(struct drm_crtc *crtc, int enable)
|
|
}
|
|
}
|
|
|
|
-void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
|
|
+union adjust_pixel_clock {
|
|
+ ADJUST_DISPLAY_PLL_PS_ALLOCATION v1;
|
|
+};
|
|
+
|
|
+static u32 atombios_adjust_pll(struct drm_crtc *crtc,
|
|
+ struct drm_display_mode *mode,
|
|
+ struct radeon_pll *pll)
|
|
{
|
|
- struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
|
|
struct drm_device *dev = crtc->dev;
|
|
struct radeon_device *rdev = dev->dev_private;
|
|
struct drm_encoder *encoder = NULL;
|
|
struct radeon_encoder *radeon_encoder = NULL;
|
|
- uint8_t frev, crev;
|
|
- int index;
|
|
- SET_PIXEL_CLOCK_PS_ALLOCATION args;
|
|
- PIXEL_CLOCK_PARAMETERS *spc1_ptr;
|
|
- PIXEL_CLOCK_PARAMETERS_V2 *spc2_ptr;
|
|
- PIXEL_CLOCK_PARAMETERS_V3 *spc3_ptr;
|
|
- uint32_t pll_clock = mode->clock;
|
|
- uint32_t adjusted_clock;
|
|
- uint32_t ref_div = 0, fb_div = 0, frac_fb_div = 0, post_div = 0;
|
|
- struct radeon_pll *pll;
|
|
- int pll_flags = 0;
|
|
+ u32 adjusted_clock = mode->clock;
|
|
|
|
- memset(&args, 0, sizeof(args));
|
|
+ /* reset the pll flags */
|
|
+ pll->flags = 0;
|
|
|
|
if (ASIC_IS_AVIVO(rdev)) {
|
|
if ((rdev->family == CHIP_RS600) ||
|
|
(rdev->family == CHIP_RS690) ||
|
|
(rdev->family == CHIP_RS740))
|
|
- pll_flags |= (RADEON_PLL_USE_FRAC_FB_DIV |
|
|
- RADEON_PLL_PREFER_CLOSEST_LOWER);
|
|
+ pll->flags |= (RADEON_PLL_USE_FRAC_FB_DIV |
|
|
+ RADEON_PLL_PREFER_CLOSEST_LOWER);
|
|
|
|
if (ASIC_IS_DCE32(rdev) && mode->clock > 200000) /* range limits??? */
|
|
- pll_flags |= RADEON_PLL_PREFER_HIGH_FB_DIV;
|
|
+ pll->flags |= RADEON_PLL_PREFER_HIGH_FB_DIV;
|
|
else
|
|
- pll_flags |= RADEON_PLL_PREFER_LOW_REF_DIV;
|
|
+ pll->flags |= RADEON_PLL_PREFER_LOW_REF_DIV;
|
|
} else {
|
|
- pll_flags |= RADEON_PLL_LEGACY;
|
|
+ pll->flags |= RADEON_PLL_LEGACY;
|
|
|
|
if (mode->clock > 200000) /* range limits??? */
|
|
- pll_flags |= RADEON_PLL_PREFER_HIGH_FB_DIV;
|
|
+ pll->flags |= RADEON_PLL_PREFER_HIGH_FB_DIV;
|
|
else
|
|
- pll_flags |= RADEON_PLL_PREFER_LOW_REF_DIV;
|
|
+ pll->flags |= RADEON_PLL_PREFER_LOW_REF_DIV;
|
|
|
|
}
|
|
|
|
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
|
if (encoder->crtc == crtc) {
|
|
- if (!ASIC_IS_AVIVO(rdev)) {
|
|
- if (encoder->encoder_type !=
|
|
- DRM_MODE_ENCODER_DAC)
|
|
- pll_flags |= RADEON_PLL_NO_ODD_POST_DIV;
|
|
- if (!ASIC_IS_AVIVO(rdev)
|
|
- && (encoder->encoder_type ==
|
|
- DRM_MODE_ENCODER_LVDS))
|
|
- pll_flags |= RADEON_PLL_USE_REF_DIV;
|
|
- }
|
|
radeon_encoder = to_radeon_encoder(encoder);
|
|
+ if (ASIC_IS_AVIVO(rdev)) {
|
|
+ /* DVO wants 2x pixel clock if the DVO chip is in 12 bit mode */
|
|
+ if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1)
|
|
+ adjusted_clock = mode->clock * 2;
|
|
+ } else {
|
|
+ if (encoder->encoder_type != DRM_MODE_ENCODER_DAC)
|
|
+ pll->flags |= RADEON_PLL_NO_ODD_POST_DIV;
|
|
+ if (encoder->encoder_type == DRM_MODE_ENCODER_LVDS)
|
|
+ pll->flags |= RADEON_PLL_USE_REF_DIV;
|
|
+ }
|
|
break;
|
|
}
|
|
}
|
|
@@ -474,36 +467,101 @@ void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
|
|
* special hw requirements.
|
|
*/
|
|
if (ASIC_IS_DCE3(rdev)) {
|
|
- ADJUST_DISPLAY_PLL_PS_ALLOCATION adjust_pll_args;
|
|
-
|
|
- if (!encoder)
|
|
- return;
|
|
+ union adjust_pixel_clock args;
|
|
+ struct radeon_encoder_atom_dig *dig;
|
|
+ u8 frev, crev;
|
|
+ int index;
|
|
|
|
- memset(&adjust_pll_args, 0, sizeof(adjust_pll_args));
|
|
- adjust_pll_args.usPixelClock = cpu_to_le16(mode->clock / 10);
|
|
- adjust_pll_args.ucTransmitterID = radeon_encoder->encoder_id;
|
|
- adjust_pll_args.ucEncodeMode = atombios_get_encoder_mode(encoder);
|
|
+ if (!radeon_encoder->enc_priv)
|
|
+ return adjusted_clock;
|
|
+ dig = radeon_encoder->enc_priv;
|
|
|
|
index = GetIndexIntoMasterTable(COMMAND, AdjustDisplayPll);
|
|
- atom_execute_table(rdev->mode_info.atom_context,
|
|
- index, (uint32_t *)&adjust_pll_args);
|
|
- adjusted_clock = le16_to_cpu(adjust_pll_args.usPixelClock) * 10;
|
|
- } else {
|
|
- /* DVO wants 2x pixel clock if the DVO chip is in 12 bit mode */
|
|
- if (ASIC_IS_AVIVO(rdev) &&
|
|
- (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1))
|
|
- adjusted_clock = mode->clock * 2;
|
|
- else
|
|
- adjusted_clock = mode->clock;
|
|
+ atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev,
|
|
+ &crev);
|
|
+
|
|
+ memset(&args, 0, sizeof(args));
|
|
+
|
|
+ switch (frev) {
|
|
+ case 1:
|
|
+ switch (crev) {
|
|
+ case 1:
|
|
+ case 2:
|
|
+ args.v1.usPixelClock = cpu_to_le16(mode->clock / 10);
|
|
+ args.v1.ucTransmitterID = radeon_encoder->encoder_id;
|
|
+ args.v1.ucEncodeMode = atombios_get_encoder_mode(encoder);
|
|
+
|
|
+ atom_execute_table(rdev->mode_info.atom_context,
|
|
+ index, (uint32_t *)&args);
|
|
+ adjusted_clock = le16_to_cpu(args.v1.usPixelClock) * 10;
|
|
+ break;
|
|
+ default:
|
|
+ DRM_ERROR("Unknown table version %d %d\n", frev, crev);
|
|
+ return adjusted_clock;
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ DRM_ERROR("Unknown table version %d %d\n", frev, crev);
|
|
+ return adjusted_clock;
|
|
+ }
|
|
+ }
|
|
+ return adjusted_clock;
|
|
+}
|
|
+
|
|
+union set_pixel_clock {
|
|
+ SET_PIXEL_CLOCK_PS_ALLOCATION base;
|
|
+ PIXEL_CLOCK_PARAMETERS v1;
|
|
+ PIXEL_CLOCK_PARAMETERS_V2 v2;
|
|
+ PIXEL_CLOCK_PARAMETERS_V3 v3;
|
|
+};
|
|
+
|
|
+void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
|
|
+{
|
|
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
|
|
+ struct drm_device *dev = crtc->dev;
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
+ struct drm_encoder *encoder = NULL;
|
|
+ struct radeon_encoder *radeon_encoder = NULL;
|
|
+ u8 frev, crev;
|
|
+ int index;
|
|
+ union set_pixel_clock args;
|
|
+ u32 pll_clock = mode->clock;
|
|
+ u32 ref_div = 0, fb_div = 0, frac_fb_div = 0, post_div = 0;
|
|
+ struct radeon_pll *pll;
|
|
+ u32 adjusted_clock;
|
|
+
|
|
+ memset(&args, 0, sizeof(args));
|
|
+
|
|
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
|
+ if (encoder->crtc == crtc) {
|
|
+ radeon_encoder = to_radeon_encoder(encoder);
|
|
+ break;
|
|
+ }
|
|
}
|
|
|
|
+ if (!radeon_encoder)
|
|
+ return;
|
|
+
|
|
if (radeon_crtc->crtc_id == 0)
|
|
pll = &rdev->clock.p1pll;
|
|
else
|
|
pll = &rdev->clock.p2pll;
|
|
|
|
- radeon_compute_pll(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div,
|
|
- &ref_div, &post_div, pll_flags);
|
|
+ /* adjust pixel clock as needed */
|
|
+ adjusted_clock = atombios_adjust_pll(crtc, mode, pll);
|
|
+
|
|
+ if (ASIC_IS_AVIVO(rdev)) {
|
|
+ if (radeon_new_pll)
|
|
+ radeon_compute_pll_avivo(pll, adjusted_clock, &pll_clock,
|
|
+ &fb_div, &frac_fb_div,
|
|
+ &ref_div, &post_div);
|
|
+ else
|
|
+ radeon_compute_pll(pll, adjusted_clock, &pll_clock,
|
|
+ &fb_div, &frac_fb_div,
|
|
+ &ref_div, &post_div);
|
|
+ } else
|
|
+ radeon_compute_pll(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div,
|
|
+ &ref_div, &post_div);
|
|
|
|
index = GetIndexIntoMasterTable(COMMAND, SetPixelClock);
|
|
atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev,
|
|
@@ -513,45 +571,38 @@ void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
|
|
case 1:
|
|
switch (crev) {
|
|
case 1:
|
|
- spc1_ptr = (PIXEL_CLOCK_PARAMETERS *) & args.sPCLKInput;
|
|
- spc1_ptr->usPixelClock = cpu_to_le16(mode->clock / 10);
|
|
- spc1_ptr->usRefDiv = cpu_to_le16(ref_div);
|
|
- spc1_ptr->usFbDiv = cpu_to_le16(fb_div);
|
|
- spc1_ptr->ucFracFbDiv = frac_fb_div;
|
|
- spc1_ptr->ucPostDiv = post_div;
|
|
- spc1_ptr->ucPpll =
|
|
+ args.v1.usPixelClock = cpu_to_le16(mode->clock / 10);
|
|
+ args.v1.usRefDiv = cpu_to_le16(ref_div);
|
|
+ args.v1.usFbDiv = cpu_to_le16(fb_div);
|
|
+ args.v1.ucFracFbDiv = frac_fb_div;
|
|
+ args.v1.ucPostDiv = post_div;
|
|
+ args.v1.ucPpll =
|
|
radeon_crtc->crtc_id ? ATOM_PPLL2 : ATOM_PPLL1;
|
|
- spc1_ptr->ucCRTC = radeon_crtc->crtc_id;
|
|
- spc1_ptr->ucRefDivSrc = 1;
|
|
+ args.v1.ucCRTC = radeon_crtc->crtc_id;
|
|
+ args.v1.ucRefDivSrc = 1;
|
|
break;
|
|
case 2:
|
|
- spc2_ptr =
|
|
- (PIXEL_CLOCK_PARAMETERS_V2 *) & args.sPCLKInput;
|
|
- spc2_ptr->usPixelClock = cpu_to_le16(mode->clock / 10);
|
|
- spc2_ptr->usRefDiv = cpu_to_le16(ref_div);
|
|
- spc2_ptr->usFbDiv = cpu_to_le16(fb_div);
|
|
- spc2_ptr->ucFracFbDiv = frac_fb_div;
|
|
- spc2_ptr->ucPostDiv = post_div;
|
|
- spc2_ptr->ucPpll =
|
|
+ args.v2.usPixelClock = cpu_to_le16(mode->clock / 10);
|
|
+ args.v2.usRefDiv = cpu_to_le16(ref_div);
|
|
+ args.v2.usFbDiv = cpu_to_le16(fb_div);
|
|
+ args.v2.ucFracFbDiv = frac_fb_div;
|
|
+ args.v2.ucPostDiv = post_div;
|
|
+ args.v2.ucPpll =
|
|
radeon_crtc->crtc_id ? ATOM_PPLL2 : ATOM_PPLL1;
|
|
- spc2_ptr->ucCRTC = radeon_crtc->crtc_id;
|
|
- spc2_ptr->ucRefDivSrc = 1;
|
|
+ args.v2.ucCRTC = radeon_crtc->crtc_id;
|
|
+ args.v2.ucRefDivSrc = 1;
|
|
break;
|
|
case 3:
|
|
- if (!encoder)
|
|
- return;
|
|
- spc3_ptr =
|
|
- (PIXEL_CLOCK_PARAMETERS_V3 *) & args.sPCLKInput;
|
|
- spc3_ptr->usPixelClock = cpu_to_le16(mode->clock / 10);
|
|
- spc3_ptr->usRefDiv = cpu_to_le16(ref_div);
|
|
- spc3_ptr->usFbDiv = cpu_to_le16(fb_div);
|
|
- spc3_ptr->ucFracFbDiv = frac_fb_div;
|
|
- spc3_ptr->ucPostDiv = post_div;
|
|
- spc3_ptr->ucPpll =
|
|
+ args.v3.usPixelClock = cpu_to_le16(mode->clock / 10);
|
|
+ args.v3.usRefDiv = cpu_to_le16(ref_div);
|
|
+ args.v3.usFbDiv = cpu_to_le16(fb_div);
|
|
+ args.v3.ucFracFbDiv = frac_fb_div;
|
|
+ args.v3.ucPostDiv = post_div;
|
|
+ args.v3.ucPpll =
|
|
radeon_crtc->crtc_id ? ATOM_PPLL2 : ATOM_PPLL1;
|
|
- spc3_ptr->ucMiscInfo = (radeon_crtc->crtc_id << 2);
|
|
- spc3_ptr->ucTransmitterId = radeon_encoder->encoder_id;
|
|
- spc3_ptr->ucEncoderMode =
|
|
+ args.v3.ucMiscInfo = (radeon_crtc->crtc_id << 2);
|
|
+ args.v3.ucTransmitterId = radeon_encoder->encoder_id;
|
|
+ args.v3.ucEncoderMode =
|
|
atombios_get_encoder_mode(encoder);
|
|
break;
|
|
default:
|
|
@@ -564,33 +615,43 @@ void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
|
|
return;
|
|
}
|
|
|
|
- printk("executing set pll\n");
|
|
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
|
|
}
|
|
|
|
-int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y,
|
|
- struct drm_framebuffer *old_fb)
|
|
+static int avivo_crtc_set_base(struct drm_crtc *crtc, int x, int y,
|
|
+ struct drm_framebuffer *old_fb)
|
|
{
|
|
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
|
|
struct drm_device *dev = crtc->dev;
|
|
struct radeon_device *rdev = dev->dev_private;
|
|
struct radeon_framebuffer *radeon_fb;
|
|
struct drm_gem_object *obj;
|
|
- struct drm_radeon_gem_object *obj_priv;
|
|
+ struct radeon_bo *rbo;
|
|
uint64_t fb_location;
|
|
uint32_t fb_format, fb_pitch_pixels, tiling_flags;
|
|
+ int r;
|
|
|
|
- if (!crtc->fb)
|
|
- return -EINVAL;
|
|
+ /* no fb bound */
|
|
+ if (!crtc->fb) {
|
|
+ DRM_DEBUG("No FB bound\n");
|
|
+ return 0;
|
|
+ }
|
|
|
|
radeon_fb = to_radeon_framebuffer(crtc->fb);
|
|
|
|
+ /* Pin framebuffer & get tilling informations */
|
|
obj = radeon_fb->obj;
|
|
- obj_priv = obj->driver_private;
|
|
-
|
|
- if (radeon_gem_object_pin(obj, RADEON_GEM_DOMAIN_VRAM, &fb_location)) {
|
|
+ rbo = obj->driver_private;
|
|
+ r = radeon_bo_reserve(rbo, false);
|
|
+ if (unlikely(r != 0))
|
|
+ return r;
|
|
+ r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &fb_location);
|
|
+ if (unlikely(r != 0)) {
|
|
+ radeon_bo_unreserve(rbo);
|
|
return -EINVAL;
|
|
}
|
|
+ radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL);
|
|
+ radeon_bo_unreserve(rbo);
|
|
|
|
switch (crtc->fb->bits_per_pixel) {
|
|
case 8:
|
|
@@ -620,8 +681,6 @@ int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y,
|
|
return -EINVAL;
|
|
}
|
|
|
|
- radeon_object_get_tiling_flags(obj->driver_private,
|
|
- &tiling_flags, NULL);
|
|
if (tiling_flags & RADEON_TILING_MACRO)
|
|
fb_format |= AVIVO_D1GRPH_MACRO_ADDRESS_MODE;
|
|
|
|
@@ -676,7 +735,12 @@ int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y,
|
|
|
|
if (old_fb && old_fb != crtc->fb) {
|
|
radeon_fb = to_radeon_framebuffer(old_fb);
|
|
- radeon_gem_object_unpin(radeon_fb->obj);
|
|
+ rbo = radeon_fb->obj->driver_private;
|
|
+ r = radeon_bo_reserve(rbo, false);
|
|
+ if (unlikely(r != 0))
|
|
+ return r;
|
|
+ radeon_bo_unpin(rbo);
|
|
+ radeon_bo_unreserve(rbo);
|
|
}
|
|
|
|
/* Bytes per pixel may have changed */
|
|
@@ -685,6 +749,42 @@ int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y,
|
|
return 0;
|
|
}
|
|
|
|
+int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y,
|
|
+ struct drm_framebuffer *old_fb)
|
|
+{
|
|
+ struct drm_device *dev = crtc->dev;
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
+
|
|
+ if (ASIC_IS_AVIVO(rdev))
|
|
+ return avivo_crtc_set_base(crtc, x, y, old_fb);
|
|
+ else
|
|
+ return radeon_crtc_set_base(crtc, x, y, old_fb);
|
|
+}
|
|
+
|
|
+/* properly set additional regs when using atombios */
|
|
+static void radeon_legacy_atom_fixup(struct drm_crtc *crtc)
|
|
+{
|
|
+ struct drm_device *dev = crtc->dev;
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
|
|
+ u32 disp_merge_cntl;
|
|
+
|
|
+ switch (radeon_crtc->crtc_id) {
|
|
+ case 0:
|
|
+ disp_merge_cntl = RREG32(RADEON_DISP_MERGE_CNTL);
|
|
+ disp_merge_cntl &= ~RADEON_DISP_RGB_OFFSET_EN;
|
|
+ WREG32(RADEON_DISP_MERGE_CNTL, disp_merge_cntl);
|
|
+ break;
|
|
+ case 1:
|
|
+ disp_merge_cntl = RREG32(RADEON_DISP2_MERGE_CNTL);
|
|
+ disp_merge_cntl &= ~RADEON_DISP2_RGB_OFFSET_EN;
|
|
+ WREG32(RADEON_DISP2_MERGE_CNTL, disp_merge_cntl);
|
|
+ WREG32(RADEON_FP_H2_SYNC_STRT_WID, RREG32(RADEON_CRTC2_H_SYNC_STRT_WID));
|
|
+ WREG32(RADEON_FP_V2_SYNC_STRT_WID, RREG32(RADEON_CRTC2_V_SYNC_STRT_WID));
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
int atombios_crtc_mode_set(struct drm_crtc *crtc,
|
|
struct drm_display_mode *mode,
|
|
struct drm_display_mode *adjusted_mode,
|
|
@@ -706,8 +806,8 @@ int atombios_crtc_mode_set(struct drm_crtc *crtc,
|
|
else {
|
|
if (radeon_crtc->crtc_id == 0)
|
|
atombios_set_crtc_dtd_timing(crtc, adjusted_mode);
|
|
- radeon_crtc_set_base(crtc, x, y, old_fb);
|
|
- radeon_legacy_atom_set_surface(crtc);
|
|
+ atombios_crtc_set_base(crtc, x, y, old_fb);
|
|
+ radeon_legacy_atom_fixup(crtc);
|
|
}
|
|
atombios_overscan_setup(crtc, mode, adjusted_mode);
|
|
atombios_scaler_setup(crtc);
|
|
@@ -725,8 +825,8 @@ static bool atombios_crtc_mode_fixup(struct drm_crtc *crtc,
|
|
|
|
static void atombios_crtc_prepare(struct drm_crtc *crtc)
|
|
{
|
|
- atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
|
|
atombios_lock_crtc(crtc, 1);
|
|
+ atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
|
|
}
|
|
|
|
static void atombios_crtc_commit(struct drm_crtc *crtc)
|
|
diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c
|
|
new file mode 100644
|
|
index 0000000..99915a6
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/radeon/atombios_dp.c
|
|
@@ -0,0 +1,789 @@
|
|
+/*
|
|
+ * Copyright 2007-8 Advanced Micro Devices, Inc.
|
|
+ * Copyright 2008 Red Hat Inc.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice shall be included in
|
|
+ * all copies or substantial portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
+ * OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ * Authors: Dave Airlie
|
|
+ * Alex Deucher
|
|
+ */
|
|
+#include "drmP.h"
|
|
+#include "radeon_drm.h"
|
|
+#include "radeon.h"
|
|
+
|
|
+#include "atom.h"
|
|
+#include "atom-bits.h"
|
|
+#include "drm_dp_helper.h"
|
|
+
|
|
+/* move these to drm_dp_helper.c/h */
|
|
+#define DP_LINK_CONFIGURATION_SIZE 9
|
|
+#define DP_LINK_STATUS_SIZE 6
|
|
+#define DP_DPCD_SIZE 8
|
|
+
|
|
+static char *voltage_names[] = {
|
|
+ "0.4V", "0.6V", "0.8V", "1.2V"
|
|
+};
|
|
+static char *pre_emph_names[] = {
|
|
+ "0dB", "3.5dB", "6dB", "9.5dB"
|
|
+};
|
|
+
|
|
+static const int dp_clocks[] = {
|
|
+ 54000, /* 1 lane, 1.62 Ghz */
|
|
+ 90000, /* 1 lane, 2.70 Ghz */
|
|
+ 108000, /* 2 lane, 1.62 Ghz */
|
|
+ 180000, /* 2 lane, 2.70 Ghz */
|
|
+ 216000, /* 4 lane, 1.62 Ghz */
|
|
+ 360000, /* 4 lane, 2.70 Ghz */
|
|
+};
|
|
+
|
|
+static const int num_dp_clocks = sizeof(dp_clocks) / sizeof(int);
|
|
+
|
|
+/* common helper functions */
|
|
+static int dp_lanes_for_mode_clock(u8 dpcd[DP_DPCD_SIZE], int mode_clock)
|
|
+{
|
|
+ int i;
|
|
+ u8 max_link_bw;
|
|
+ u8 max_lane_count;
|
|
+
|
|
+ if (!dpcd)
|
|
+ return 0;
|
|
+
|
|
+ max_link_bw = dpcd[DP_MAX_LINK_RATE];
|
|
+ max_lane_count = dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK;
|
|
+
|
|
+ switch (max_link_bw) {
|
|
+ case DP_LINK_BW_1_62:
|
|
+ default:
|
|
+ for (i = 0; i < num_dp_clocks; i++) {
|
|
+ if (i % 2)
|
|
+ continue;
|
|
+ switch (max_lane_count) {
|
|
+ case 1:
|
|
+ if (i > 1)
|
|
+ return 0;
|
|
+ break;
|
|
+ case 2:
|
|
+ if (i > 3)
|
|
+ return 0;
|
|
+ break;
|
|
+ case 4:
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ if (dp_clocks[i] > mode_clock) {
|
|
+ if (i < 2)
|
|
+ return 1;
|
|
+ else if (i < 4)
|
|
+ return 2;
|
|
+ else
|
|
+ return 4;
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ case DP_LINK_BW_2_7:
|
|
+ for (i = 0; i < num_dp_clocks; i++) {
|
|
+ switch (max_lane_count) {
|
|
+ case 1:
|
|
+ if (i > 1)
|
|
+ return 0;
|
|
+ break;
|
|
+ case 2:
|
|
+ if (i > 3)
|
|
+ return 0;
|
|
+ break;
|
|
+ case 4:
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ if (dp_clocks[i] > mode_clock) {
|
|
+ if (i < 2)
|
|
+ return 1;
|
|
+ else if (i < 4)
|
|
+ return 2;
|
|
+ else
|
|
+ return 4;
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dp_link_clock_for_mode_clock(u8 dpcd[DP_DPCD_SIZE], int mode_clock)
|
|
+{
|
|
+ int i;
|
|
+ u8 max_link_bw;
|
|
+ u8 max_lane_count;
|
|
+
|
|
+ if (!dpcd)
|
|
+ return 0;
|
|
+
|
|
+ max_link_bw = dpcd[DP_MAX_LINK_RATE];
|
|
+ max_lane_count = dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK;
|
|
+
|
|
+ switch (max_link_bw) {
|
|
+ case DP_LINK_BW_1_62:
|
|
+ default:
|
|
+ for (i = 0; i < num_dp_clocks; i++) {
|
|
+ if (i % 2)
|
|
+ continue;
|
|
+ switch (max_lane_count) {
|
|
+ case 1:
|
|
+ if (i > 1)
|
|
+ return 0;
|
|
+ break;
|
|
+ case 2:
|
|
+ if (i > 3)
|
|
+ return 0;
|
|
+ break;
|
|
+ case 4:
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ if (dp_clocks[i] > mode_clock)
|
|
+ return 162000;
|
|
+ }
|
|
+ break;
|
|
+ case DP_LINK_BW_2_7:
|
|
+ for (i = 0; i < num_dp_clocks; i++) {
|
|
+ switch (max_lane_count) {
|
|
+ case 1:
|
|
+ if (i > 1)
|
|
+ return 0;
|
|
+ break;
|
|
+ case 2:
|
|
+ if (i > 3)
|
|
+ return 0;
|
|
+ break;
|
|
+ case 4:
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ if (dp_clocks[i] > mode_clock)
|
|
+ return (i % 2) ? 270000 : 162000;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int dp_mode_valid(u8 dpcd[DP_DPCD_SIZE], int mode_clock)
|
|
+{
|
|
+ int lanes = dp_lanes_for_mode_clock(dpcd, mode_clock);
|
|
+ int bw = dp_lanes_for_mode_clock(dpcd, mode_clock);
|
|
+
|
|
+ if ((lanes == 0) || (bw == 0))
|
|
+ return MODE_CLOCK_HIGH;
|
|
+
|
|
+ return MODE_OK;
|
|
+}
|
|
+
|
|
+static u8 dp_link_status(u8 link_status[DP_LINK_STATUS_SIZE], int r)
|
|
+{
|
|
+ return link_status[r - DP_LANE0_1_STATUS];
|
|
+}
|
|
+
|
|
+static u8 dp_get_lane_status(u8 link_status[DP_LINK_STATUS_SIZE],
|
|
+ int lane)
|
|
+{
|
|
+ int i = DP_LANE0_1_STATUS + (lane >> 1);
|
|
+ int s = (lane & 1) * 4;
|
|
+ u8 l = dp_link_status(link_status, i);
|
|
+ return (l >> s) & 0xf;
|
|
+}
|
|
+
|
|
+static bool dp_clock_recovery_ok(u8 link_status[DP_LINK_STATUS_SIZE],
|
|
+ int lane_count)
|
|
+{
|
|
+ int lane;
|
|
+ u8 lane_status;
|
|
+
|
|
+ for (lane = 0; lane < lane_count; lane++) {
|
|
+ lane_status = dp_get_lane_status(link_status, lane);
|
|
+ if ((lane_status & DP_LANE_CR_DONE) == 0)
|
|
+ return false;
|
|
+ }
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static bool dp_channel_eq_ok(u8 link_status[DP_LINK_STATUS_SIZE],
|
|
+ int lane_count)
|
|
+{
|
|
+ u8 lane_align;
|
|
+ u8 lane_status;
|
|
+ int lane;
|
|
+
|
|
+ lane_align = dp_link_status(link_status,
|
|
+ DP_LANE_ALIGN_STATUS_UPDATED);
|
|
+ if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0)
|
|
+ return false;
|
|
+ for (lane = 0; lane < lane_count; lane++) {
|
|
+ lane_status = dp_get_lane_status(link_status, lane);
|
|
+ if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS)
|
|
+ return false;
|
|
+ }
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static u8 dp_get_adjust_request_voltage(uint8_t link_status[DP_LINK_STATUS_SIZE],
|
|
+ int lane)
|
|
+
|
|
+{
|
|
+ int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
|
|
+ int s = ((lane & 1) ?
|
|
+ DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT :
|
|
+ DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT);
|
|
+ u8 l = dp_link_status(link_status, i);
|
|
+
|
|
+ return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT;
|
|
+}
|
|
+
|
|
+static u8 dp_get_adjust_request_pre_emphasis(uint8_t link_status[DP_LINK_STATUS_SIZE],
|
|
+ int lane)
|
|
+{
|
|
+ int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
|
|
+ int s = ((lane & 1) ?
|
|
+ DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT :
|
|
+ DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT);
|
|
+ u8 l = dp_link_status(link_status, i);
|
|
+
|
|
+ return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT;
|
|
+}
|
|
+
|
|
+/* XXX fix me -- chip specific */
|
|
+#define DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_1200
|
|
+static u8 dp_pre_emphasis_max(u8 voltage_swing)
|
|
+{
|
|
+ switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
|
|
+ case DP_TRAIN_VOLTAGE_SWING_400:
|
|
+ return DP_TRAIN_PRE_EMPHASIS_6;
|
|
+ case DP_TRAIN_VOLTAGE_SWING_600:
|
|
+ return DP_TRAIN_PRE_EMPHASIS_6;
|
|
+ case DP_TRAIN_VOLTAGE_SWING_800:
|
|
+ return DP_TRAIN_PRE_EMPHASIS_3_5;
|
|
+ case DP_TRAIN_VOLTAGE_SWING_1200:
|
|
+ default:
|
|
+ return DP_TRAIN_PRE_EMPHASIS_0;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void dp_get_adjust_train(u8 link_status[DP_LINK_STATUS_SIZE],
|
|
+ int lane_count,
|
|
+ u8 train_set[4])
|
|
+{
|
|
+ u8 v = 0;
|
|
+ u8 p = 0;
|
|
+ int lane;
|
|
+
|
|
+ for (lane = 0; lane < lane_count; lane++) {
|
|
+ u8 this_v = dp_get_adjust_request_voltage(link_status, lane);
|
|
+ u8 this_p = dp_get_adjust_request_pre_emphasis(link_status, lane);
|
|
+
|
|
+ DRM_DEBUG("requested signal parameters: lane %d voltage %s pre_emph %s\n",
|
|
+ lane,
|
|
+ voltage_names[this_v >> DP_TRAIN_VOLTAGE_SWING_SHIFT],
|
|
+ pre_emph_names[this_p >> DP_TRAIN_PRE_EMPHASIS_SHIFT]);
|
|
+
|
|
+ if (this_v > v)
|
|
+ v = this_v;
|
|
+ if (this_p > p)
|
|
+ p = this_p;
|
|
+ }
|
|
+
|
|
+ if (v >= DP_VOLTAGE_MAX)
|
|
+ v = DP_VOLTAGE_MAX | DP_TRAIN_MAX_SWING_REACHED;
|
|
+
|
|
+ if (p >= dp_pre_emphasis_max(v))
|
|
+ p = dp_pre_emphasis_max(v) | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
|
|
+
|
|
+ DRM_DEBUG("using signal parameters: voltage %s pre_emph %s\n",
|
|
+ voltage_names[(v & DP_TRAIN_VOLTAGE_SWING_MASK) >> DP_TRAIN_VOLTAGE_SWING_SHIFT],
|
|
+ pre_emph_names[(p & DP_TRAIN_PRE_EMPHASIS_MASK) >> DP_TRAIN_PRE_EMPHASIS_SHIFT]);
|
|
+
|
|
+ for (lane = 0; lane < 4; lane++)
|
|
+ train_set[lane] = v | p;
|
|
+}
|
|
+
|
|
+
|
|
+/* radeon aux chan functions */
|
|
+bool radeon_process_aux_ch(struct radeon_i2c_chan *chan, u8 *req_bytes,
|
|
+ int num_bytes, u8 *read_byte,
|
|
+ u8 read_buf_len, u8 delay)
|
|
+{
|
|
+ struct drm_device *dev = chan->dev;
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
+ PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION args;
|
|
+ int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction);
|
|
+ unsigned char *base;
|
|
+ int retry_count = 0;
|
|
+
|
|
+ memset(&args, 0, sizeof(args));
|
|
+
|
|
+ base = (unsigned char *)rdev->mode_info.atom_context->scratch;
|
|
+
|
|
+retry:
|
|
+ memcpy(base, req_bytes, num_bytes);
|
|
+
|
|
+ args.lpAuxRequest = 0;
|
|
+ args.lpDataOut = 16;
|
|
+ args.ucDataOutLen = 0;
|
|
+ args.ucChannelID = chan->rec.i2c_id;
|
|
+ args.ucDelay = delay / 10;
|
|
+
|
|
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
|
|
+
|
|
+ if (args.ucReplyStatus && !args.ucDataOutLen) {
|
|
+ if (args.ucReplyStatus == 0x20 && retry_count++ < 10)
|
|
+ goto retry;
|
|
+ DRM_DEBUG("failed to get auxch %02x%02x %02x %02x 0x%02x %02x after %d retries\n",
|
|
+ req_bytes[1], req_bytes[0], req_bytes[2], req_bytes[3],
|
|
+ chan->rec.i2c_id, args.ucReplyStatus, retry_count);
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (args.ucDataOutLen && read_byte && read_buf_len) {
|
|
+ if (read_buf_len < args.ucDataOutLen) {
|
|
+ DRM_ERROR("Buffer to small for return answer %d %d\n",
|
|
+ read_buf_len, args.ucDataOutLen);
|
|
+ return false;
|
|
+ }
|
|
+ {
|
|
+ int len = min(read_buf_len, args.ucDataOutLen);
|
|
+ memcpy(read_byte, base + 16, len);
|
|
+ }
|
|
+ }
|
|
+ return true;
|
|
+}
|
|
+
|
|
+bool radeon_dp_aux_native_write(struct radeon_connector *radeon_connector, uint16_t address,
|
|
+ uint8_t send_bytes, uint8_t *send)
|
|
+{
|
|
+ struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
|
|
+ u8 msg[20];
|
|
+ u8 msg_len, dp_msg_len;
|
|
+ bool ret;
|
|
+
|
|
+ dp_msg_len = 4;
|
|
+ msg[0] = address;
|
|
+ msg[1] = address >> 8;
|
|
+ msg[2] = AUX_NATIVE_WRITE << 4;
|
|
+ dp_msg_len += send_bytes;
|
|
+ msg[3] = (dp_msg_len << 4) | (send_bytes - 1);
|
|
+
|
|
+ if (send_bytes > 16)
|
|
+ return false;
|
|
+
|
|
+ memcpy(&msg[4], send, send_bytes);
|
|
+ msg_len = 4 + send_bytes;
|
|
+ ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus, msg, msg_len, NULL, 0, 0);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+bool radeon_dp_aux_native_read(struct radeon_connector *radeon_connector, uint16_t address,
|
|
+ uint8_t delay, uint8_t expected_bytes,
|
|
+ uint8_t *read_p)
|
|
+{
|
|
+ struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
|
|
+ u8 msg[20];
|
|
+ u8 msg_len, dp_msg_len;
|
|
+ bool ret = false;
|
|
+ msg_len = 4;
|
|
+ dp_msg_len = 4;
|
|
+ msg[0] = address;
|
|
+ msg[1] = address >> 8;
|
|
+ msg[2] = AUX_NATIVE_READ << 4;
|
|
+ msg[3] = (dp_msg_len) << 4;
|
|
+ msg[3] |= expected_bytes - 1;
|
|
+
|
|
+ ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus, msg, msg_len, read_p, expected_bytes, delay);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/* radeon dp functions */
|
|
+static u8 radeon_dp_encoder_service(struct radeon_device *rdev, int action, int dp_clock,
|
|
+ uint8_t ucconfig, uint8_t lane_num)
|
|
+{
|
|
+ DP_ENCODER_SERVICE_PARAMETERS args;
|
|
+ int index = GetIndexIntoMasterTable(COMMAND, DPEncoderService);
|
|
+
|
|
+ memset(&args, 0, sizeof(args));
|
|
+ args.ucLinkClock = dp_clock / 10;
|
|
+ args.ucConfig = ucconfig;
|
|
+ args.ucAction = action;
|
|
+ args.ucLaneNum = lane_num;
|
|
+ args.ucStatus = 0;
|
|
+
|
|
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
|
|
+ return args.ucStatus;
|
|
+}
|
|
+
|
|
+u8 radeon_dp_getsinktype(struct radeon_connector *radeon_connector)
|
|
+{
|
|
+ struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
|
|
+ struct drm_device *dev = radeon_connector->base.dev;
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
+
|
|
+ return radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_GET_SINK_TYPE, 0,
|
|
+ dig_connector->dp_i2c_bus->rec.i2c_id, 0);
|
|
+}
|
|
+
|
|
+bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector)
|
|
+{
|
|
+ struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
|
|
+ u8 msg[25];
|
|
+ int ret;
|
|
+
|
|
+ ret = radeon_dp_aux_native_read(radeon_connector, DP_DPCD_REV, 0, 8, msg);
|
|
+ if (ret) {
|
|
+ memcpy(dig_connector->dpcd, msg, 8);
|
|
+ {
|
|
+ int i;
|
|
+ DRM_DEBUG("DPCD: ");
|
|
+ for (i = 0; i < 8; i++)
|
|
+ DRM_DEBUG("%02x ", msg[i]);
|
|
+ DRM_DEBUG("\n");
|
|
+ }
|
|
+ return true;
|
|
+ }
|
|
+ dig_connector->dpcd[0] = 0;
|
|
+ return false;
|
|
+}
|
|
+
|
|
+void radeon_dp_set_link_config(struct drm_connector *connector,
|
|
+ struct drm_display_mode *mode)
|
|
+{
|
|
+ struct radeon_connector *radeon_connector;
|
|
+ struct radeon_connector_atom_dig *dig_connector;
|
|
+
|
|
+ if ((connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort) &&
|
|
+ (connector->connector_type != DRM_MODE_CONNECTOR_eDP))
|
|
+ return;
|
|
+
|
|
+ radeon_connector = to_radeon_connector(connector);
|
|
+ if (!radeon_connector->con_priv)
|
|
+ return;
|
|
+ dig_connector = radeon_connector->con_priv;
|
|
+
|
|
+ dig_connector->dp_clock =
|
|
+ dp_link_clock_for_mode_clock(dig_connector->dpcd, mode->clock);
|
|
+ dig_connector->dp_lane_count =
|
|
+ dp_lanes_for_mode_clock(dig_connector->dpcd, mode->clock);
|
|
+}
|
|
+
|
|
+int radeon_dp_mode_valid_helper(struct radeon_connector *radeon_connector,
|
|
+ struct drm_display_mode *mode)
|
|
+{
|
|
+ struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
|
|
+
|
|
+ return dp_mode_valid(dig_connector->dpcd, mode->clock);
|
|
+}
|
|
+
|
|
+static bool atom_dp_get_link_status(struct radeon_connector *radeon_connector,
|
|
+ u8 link_status[DP_LINK_STATUS_SIZE])
|
|
+{
|
|
+ int ret;
|
|
+ ret = radeon_dp_aux_native_read(radeon_connector, DP_LANE0_1_STATUS, 100,
|
|
+ DP_LINK_STATUS_SIZE, link_status);
|
|
+ if (!ret) {
|
|
+ DRM_ERROR("displayport link status failed\n");
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ DRM_DEBUG("link status %02x %02x %02x %02x %02x %02x\n",
|
|
+ link_status[0], link_status[1], link_status[2],
|
|
+ link_status[3], link_status[4], link_status[5]);
|
|
+ return true;
|
|
+}
|
|
+
|
|
+bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector)
|
|
+{
|
|
+ struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
|
|
+ u8 link_status[DP_LINK_STATUS_SIZE];
|
|
+
|
|
+ if (!atom_dp_get_link_status(radeon_connector, link_status))
|
|
+ return false;
|
|
+ if (dp_channel_eq_ok(link_status, dig_connector->dp_lane_count))
|
|
+ return false;
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static void dp_set_power(struct radeon_connector *radeon_connector, u8 power_state)
|
|
+{
|
|
+ struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
|
|
+
|
|
+ if (dig_connector->dpcd[0] >= 0x11) {
|
|
+ radeon_dp_aux_native_write(radeon_connector, DP_SET_POWER, 1,
|
|
+ &power_state);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void dp_set_downspread(struct radeon_connector *radeon_connector, u8 downspread)
|
|
+{
|
|
+ radeon_dp_aux_native_write(radeon_connector, DP_DOWNSPREAD_CTRL, 1,
|
|
+ &downspread);
|
|
+}
|
|
+
|
|
+static void dp_set_link_bw_lanes(struct radeon_connector *radeon_connector,
|
|
+ u8 link_configuration[DP_LINK_CONFIGURATION_SIZE])
|
|
+{
|
|
+ radeon_dp_aux_native_write(radeon_connector, DP_LINK_BW_SET, 2,
|
|
+ link_configuration);
|
|
+}
|
|
+
|
|
+static void dp_update_dpvs_emph(struct radeon_connector *radeon_connector,
|
|
+ struct drm_encoder *encoder,
|
|
+ u8 train_set[4])
|
|
+{
|
|
+ struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < dig_connector->dp_lane_count; i++)
|
|
+ atombios_dig_transmitter_setup(encoder,
|
|
+ ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH,
|
|
+ i, train_set[i]);
|
|
+
|
|
+ radeon_dp_aux_native_write(radeon_connector, DP_TRAINING_LANE0_SET,
|
|
+ dig_connector->dp_lane_count, train_set);
|
|
+}
|
|
+
|
|
+static void dp_set_training(struct radeon_connector *radeon_connector,
|
|
+ u8 training)
|
|
+{
|
|
+ radeon_dp_aux_native_write(radeon_connector, DP_TRAINING_PATTERN_SET,
|
|
+ 1, &training);
|
|
+}
|
|
+
|
|
+void dp_link_train(struct drm_encoder *encoder,
|
|
+ struct drm_connector *connector)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
|
|
+ struct radeon_encoder_atom_dig *dig;
|
|
+ struct radeon_connector *radeon_connector;
|
|
+ struct radeon_connector_atom_dig *dig_connector;
|
|
+ int enc_id = 0;
|
|
+ bool clock_recovery, channel_eq;
|
|
+ u8 link_status[DP_LINK_STATUS_SIZE];
|
|
+ u8 link_configuration[DP_LINK_CONFIGURATION_SIZE];
|
|
+ u8 tries, voltage;
|
|
+ u8 train_set[4];
|
|
+ int i;
|
|
+
|
|
+ if ((connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort) &&
|
|
+ (connector->connector_type != DRM_MODE_CONNECTOR_eDP))
|
|
+ return;
|
|
+
|
|
+ if (!radeon_encoder->enc_priv)
|
|
+ return;
|
|
+ dig = radeon_encoder->enc_priv;
|
|
+
|
|
+ radeon_connector = to_radeon_connector(connector);
|
|
+ if (!radeon_connector->con_priv)
|
|
+ return;
|
|
+ dig_connector = radeon_connector->con_priv;
|
|
+
|
|
+ if (dig->dig_encoder)
|
|
+ enc_id |= ATOM_DP_CONFIG_DIG2_ENCODER;
|
|
+ else
|
|
+ enc_id |= ATOM_DP_CONFIG_DIG1_ENCODER;
|
|
+ if (dig_connector->linkb)
|
|
+ enc_id |= ATOM_DP_CONFIG_LINK_B;
|
|
+ else
|
|
+ enc_id |= ATOM_DP_CONFIG_LINK_A;
|
|
+
|
|
+ memset(link_configuration, 0, DP_LINK_CONFIGURATION_SIZE);
|
|
+ if (dig_connector->dp_clock == 270000)
|
|
+ link_configuration[0] = DP_LINK_BW_2_7;
|
|
+ else
|
|
+ link_configuration[0] = DP_LINK_BW_1_62;
|
|
+ link_configuration[1] = dig_connector->dp_lane_count;
|
|
+ if (dig_connector->dpcd[0] >= 0x11)
|
|
+ link_configuration[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
|
|
+
|
|
+ /* power up the sink */
|
|
+ dp_set_power(radeon_connector, DP_SET_POWER_D0);
|
|
+ /* disable the training pattern on the sink */
|
|
+ dp_set_training(radeon_connector, DP_TRAINING_PATTERN_DISABLE);
|
|
+ /* set link bw and lanes on the sink */
|
|
+ dp_set_link_bw_lanes(radeon_connector, link_configuration);
|
|
+ /* disable downspread on the sink */
|
|
+ dp_set_downspread(radeon_connector, 0);
|
|
+ /* start training on the source */
|
|
+ radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_START,
|
|
+ dig_connector->dp_clock, enc_id, 0);
|
|
+ /* set training pattern 1 on the source */
|
|
+ radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_PATTERN_SEL,
|
|
+ dig_connector->dp_clock, enc_id, 0);
|
|
+
|
|
+ /* set initial vs/emph */
|
|
+ memset(train_set, 0, 4);
|
|
+ udelay(400);
|
|
+ /* set training pattern 1 on the sink */
|
|
+ dp_set_training(radeon_connector, DP_TRAINING_PATTERN_1);
|
|
+
|
|
+ dp_update_dpvs_emph(radeon_connector, encoder, train_set);
|
|
+
|
|
+ /* clock recovery loop */
|
|
+ clock_recovery = false;
|
|
+ tries = 0;
|
|
+ voltage = 0xff;
|
|
+ for (;;) {
|
|
+ udelay(100);
|
|
+ if (!atom_dp_get_link_status(radeon_connector, link_status))
|
|
+ break;
|
|
+
|
|
+ if (dp_clock_recovery_ok(link_status, dig_connector->dp_lane_count)) {
|
|
+ clock_recovery = true;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < dig_connector->dp_lane_count; i++) {
|
|
+ if ((train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)
|
|
+ break;
|
|
+ }
|
|
+ if (i == dig_connector->dp_lane_count) {
|
|
+ DRM_ERROR("clock recovery reached max voltage\n");
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if ((train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
|
|
+ ++tries;
|
|
+ if (tries == 5) {
|
|
+ DRM_ERROR("clock recovery tried 5 times\n");
|
|
+ break;
|
|
+ }
|
|
+ } else
|
|
+ tries = 0;
|
|
+
|
|
+ voltage = train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
|
|
+
|
|
+ /* Compute new train_set as requested by sink */
|
|
+ dp_get_adjust_train(link_status, dig_connector->dp_lane_count, train_set);
|
|
+ dp_update_dpvs_emph(radeon_connector, encoder, train_set);
|
|
+ }
|
|
+ if (!clock_recovery)
|
|
+ DRM_ERROR("clock recovery failed\n");
|
|
+ else
|
|
+ DRM_DEBUG("clock recovery at voltage %d pre-emphasis %d\n",
|
|
+ train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK,
|
|
+ (train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK) >>
|
|
+ DP_TRAIN_PRE_EMPHASIS_SHIFT);
|
|
+
|
|
+
|
|
+ /* set training pattern 2 on the sink */
|
|
+ dp_set_training(radeon_connector, DP_TRAINING_PATTERN_2);
|
|
+ /* set training pattern 2 on the source */
|
|
+ radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_PATTERN_SEL,
|
|
+ dig_connector->dp_clock, enc_id, 1);
|
|
+
|
|
+ /* channel equalization loop */
|
|
+ tries = 0;
|
|
+ channel_eq = false;
|
|
+ for (;;) {
|
|
+ udelay(400);
|
|
+ if (!atom_dp_get_link_status(radeon_connector, link_status))
|
|
+ break;
|
|
+
|
|
+ if (dp_channel_eq_ok(link_status, dig_connector->dp_lane_count)) {
|
|
+ channel_eq = true;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* Try 5 times */
|
|
+ if (tries > 5) {
|
|
+ DRM_ERROR("channel eq failed: 5 tries\n");
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* Compute new train_set as requested by sink */
|
|
+ dp_get_adjust_train(link_status, dig_connector->dp_lane_count, train_set);
|
|
+ dp_update_dpvs_emph(radeon_connector, encoder, train_set);
|
|
+
|
|
+ tries++;
|
|
+ }
|
|
+
|
|
+ if (!channel_eq)
|
|
+ DRM_ERROR("channel eq failed\n");
|
|
+ else
|
|
+ DRM_DEBUG("channel eq at voltage %d pre-emphasis %d\n",
|
|
+ train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK,
|
|
+ (train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK)
|
|
+ >> DP_TRAIN_PRE_EMPHASIS_SHIFT);
|
|
+
|
|
+ /* disable the training pattern on the sink */
|
|
+ dp_set_training(radeon_connector, DP_TRAINING_PATTERN_DISABLE);
|
|
+
|
|
+ radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_COMPLETE,
|
|
+ dig_connector->dp_clock, enc_id, 0);
|
|
+}
|
|
+
|
|
+int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
|
|
+ uint8_t write_byte, uint8_t *read_byte)
|
|
+{
|
|
+ struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
|
|
+ struct radeon_i2c_chan *auxch = (struct radeon_i2c_chan *)adapter;
|
|
+ int ret = 0;
|
|
+ uint16_t address = algo_data->address;
|
|
+ uint8_t msg[5];
|
|
+ uint8_t reply[2];
|
|
+ int msg_len, dp_msg_len;
|
|
+ int reply_bytes;
|
|
+
|
|
+ /* Set up the command byte */
|
|
+ if (mode & MODE_I2C_READ)
|
|
+ msg[2] = AUX_I2C_READ << 4;
|
|
+ else
|
|
+ msg[2] = AUX_I2C_WRITE << 4;
|
|
+
|
|
+ if (!(mode & MODE_I2C_STOP))
|
|
+ msg[2] |= AUX_I2C_MOT << 4;
|
|
+
|
|
+ msg[0] = address;
|
|
+ msg[1] = address >> 8;
|
|
+
|
|
+ reply_bytes = 1;
|
|
+
|
|
+ msg_len = 4;
|
|
+ dp_msg_len = 3;
|
|
+ switch (mode) {
|
|
+ case MODE_I2C_WRITE:
|
|
+ msg[4] = write_byte;
|
|
+ msg_len++;
|
|
+ dp_msg_len += 2;
|
|
+ break;
|
|
+ case MODE_I2C_READ:
|
|
+ dp_msg_len += 1;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ msg[3] = (dp_msg_len) << 4;
|
|
+ ret = radeon_process_aux_ch(auxch, msg, msg_len, reply, reply_bytes, 0);
|
|
+
|
|
+ if (ret) {
|
|
+ if (read_byte)
|
|
+ *read_byte = reply[0];
|
|
+ return reply_bytes;
|
|
+ }
|
|
+ return -EREMOTEIO;
|
|
+}
|
|
+
|
|
diff --git a/drivers/gpu/drm/radeon/mkregtable.c b/drivers/gpu/drm/radeon/mkregtable.c
|
|
index 0d79577..607241c 100644
|
|
--- a/drivers/gpu/drm/radeon/mkregtable.c
|
|
+++ b/drivers/gpu/drm/radeon/mkregtable.c
|
|
@@ -661,8 +661,10 @@ static int parser_auth(struct table *t, const char *filename)
|
|
fseek(file, 0, SEEK_SET);
|
|
|
|
/* get header */
|
|
- if (fgets(buf, 1024, file) == NULL)
|
|
+ if (fgets(buf, 1024, file) == NULL) {
|
|
+ fclose(file);
|
|
return -1;
|
|
+ }
|
|
|
|
/* first line will contain the last register
|
|
* and gpu name */
|
|
diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
|
|
index c9e93ea..38209a6 100644
|
|
--- a/drivers/gpu/drm/radeon/r100.c
|
|
+++ b/drivers/gpu/drm/radeon/r100.c
|
|
@@ -65,6 +65,96 @@ MODULE_FIRMWARE(FIRMWARE_R520);
|
|
* r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280
|
|
*/
|
|
|
|
+/* hpd for digital panel detect/disconnect */
|
|
+bool r100_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd)
|
|
+{
|
|
+ bool connected = false;
|
|
+
|
|
+ switch (hpd) {
|
|
+ case RADEON_HPD_1:
|
|
+ if (RREG32(RADEON_FP_GEN_CNTL) & RADEON_FP_DETECT_SENSE)
|
|
+ connected = true;
|
|
+ break;
|
|
+ case RADEON_HPD_2:
|
|
+ if (RREG32(RADEON_FP2_GEN_CNTL) & RADEON_FP2_DETECT_SENSE)
|
|
+ connected = true;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ return connected;
|
|
+}
|
|
+
|
|
+void r100_hpd_set_polarity(struct radeon_device *rdev,
|
|
+ enum radeon_hpd_id hpd)
|
|
+{
|
|
+ u32 tmp;
|
|
+ bool connected = r100_hpd_sense(rdev, hpd);
|
|
+
|
|
+ switch (hpd) {
|
|
+ case RADEON_HPD_1:
|
|
+ tmp = RREG32(RADEON_FP_GEN_CNTL);
|
|
+ if (connected)
|
|
+ tmp &= ~RADEON_FP_DETECT_INT_POL;
|
|
+ else
|
|
+ tmp |= RADEON_FP_DETECT_INT_POL;
|
|
+ WREG32(RADEON_FP_GEN_CNTL, tmp);
|
|
+ break;
|
|
+ case RADEON_HPD_2:
|
|
+ tmp = RREG32(RADEON_FP2_GEN_CNTL);
|
|
+ if (connected)
|
|
+ tmp &= ~RADEON_FP2_DETECT_INT_POL;
|
|
+ else
|
|
+ tmp |= RADEON_FP2_DETECT_INT_POL;
|
|
+ WREG32(RADEON_FP2_GEN_CNTL, tmp);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+void r100_hpd_init(struct radeon_device *rdev)
|
|
+{
|
|
+ struct drm_device *dev = rdev->ddev;
|
|
+ struct drm_connector *connector;
|
|
+
|
|
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
|
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
|
|
+ switch (radeon_connector->hpd.hpd) {
|
|
+ case RADEON_HPD_1:
|
|
+ rdev->irq.hpd[0] = true;
|
|
+ break;
|
|
+ case RADEON_HPD_2:
|
|
+ rdev->irq.hpd[1] = true;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (rdev->irq.installed)
|
|
+ r100_irq_set(rdev);
|
|
+}
|
|
+
|
|
+void r100_hpd_fini(struct radeon_device *rdev)
|
|
+{
|
|
+ struct drm_device *dev = rdev->ddev;
|
|
+ struct drm_connector *connector;
|
|
+
|
|
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
|
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
|
|
+ switch (radeon_connector->hpd.hpd) {
|
|
+ case RADEON_HPD_1:
|
|
+ rdev->irq.hpd[0] = false;
|
|
+ break;
|
|
+ case RADEON_HPD_2:
|
|
+ rdev->irq.hpd[1] = false;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
/*
|
|
* PCI GART
|
|
*/
|
|
@@ -94,6 +184,15 @@ int r100_pci_gart_init(struct radeon_device *rdev)
|
|
return radeon_gart_table_ram_alloc(rdev);
|
|
}
|
|
|
|
+/* required on r1xx, r2xx, r300, r(v)350, r420/r481, rs400/rs480 */
|
|
+void r100_enable_bm(struct radeon_device *rdev)
|
|
+{
|
|
+ uint32_t tmp;
|
|
+ /* Enable bus mastering */
|
|
+ tmp = RREG32(RADEON_BUS_CNTL) & ~RADEON_BUS_MASTER_DIS;
|
|
+ WREG32(RADEON_BUS_CNTL, tmp);
|
|
+}
|
|
+
|
|
int r100_pci_gart_enable(struct radeon_device *rdev)
|
|
{
|
|
uint32_t tmp;
|
|
@@ -105,9 +204,6 @@ int r100_pci_gart_enable(struct radeon_device *rdev)
|
|
WREG32(RADEON_AIC_LO_ADDR, rdev->mc.gtt_location);
|
|
tmp = rdev->mc.gtt_location + rdev->mc.gtt_size - 1;
|
|
WREG32(RADEON_AIC_HI_ADDR, tmp);
|
|
- /* Enable bus mastering */
|
|
- tmp = RREG32(RADEON_BUS_CNTL) & ~RADEON_BUS_MASTER_DIS;
|
|
- WREG32(RADEON_BUS_CNTL, tmp);
|
|
/* set PCI GART page-table base address */
|
|
WREG32(RADEON_AIC_PT_BASE, rdev->gart.table_addr);
|
|
tmp = RREG32(RADEON_AIC_CNTL) | RADEON_PCIGART_TRANSLATE_EN;
|
|
@@ -148,6 +244,11 @@ int r100_irq_set(struct radeon_device *rdev)
|
|
{
|
|
uint32_t tmp = 0;
|
|
|
|
+ if (!rdev->irq.installed) {
|
|
+ WARN(1, "Can't enable IRQ/MSI because no handler is installed.\n");
|
|
+ WREG32(R_000040_GEN_INT_CNTL, 0);
|
|
+ return -EINVAL;
|
|
+ }
|
|
if (rdev->irq.sw_int) {
|
|
tmp |= RADEON_SW_INT_ENABLE;
|
|
}
|
|
@@ -157,6 +258,12 @@ int r100_irq_set(struct radeon_device *rdev)
|
|
if (rdev->irq.crtc_vblank_int[1]) {
|
|
tmp |= RADEON_CRTC2_VBLANK_MASK;
|
|
}
|
|
+ if (rdev->irq.hpd[0]) {
|
|
+ tmp |= RADEON_FP_DETECT_MASK;
|
|
+ }
|
|
+ if (rdev->irq.hpd[1]) {
|
|
+ tmp |= RADEON_FP2_DETECT_MASK;
|
|
+ }
|
|
WREG32(RADEON_GEN_INT_CNTL, tmp);
|
|
return 0;
|
|
}
|
|
@@ -175,8 +282,9 @@ void r100_irq_disable(struct radeon_device *rdev)
|
|
static inline uint32_t r100_irq_ack(struct radeon_device *rdev)
|
|
{
|
|
uint32_t irqs = RREG32(RADEON_GEN_INT_STATUS);
|
|
- uint32_t irq_mask = RADEON_SW_INT_TEST | RADEON_CRTC_VBLANK_STAT |
|
|
- RADEON_CRTC2_VBLANK_STAT;
|
|
+ uint32_t irq_mask = RADEON_SW_INT_TEST |
|
|
+ RADEON_CRTC_VBLANK_STAT | RADEON_CRTC2_VBLANK_STAT |
|
|
+ RADEON_FP_DETECT_STAT | RADEON_FP2_DETECT_STAT;
|
|
|
|
if (irqs) {
|
|
WREG32(RADEON_GEN_INT_STATUS, irqs);
|
|
@@ -187,6 +295,7 @@ static inline uint32_t r100_irq_ack(struct radeon_device *rdev)
|
|
int r100_irq_process(struct radeon_device *rdev)
|
|
{
|
|
uint32_t status, msi_rearm;
|
|
+ bool queue_hotplug = false;
|
|
|
|
status = r100_irq_ack(rdev);
|
|
if (!status) {
|
|
@@ -207,8 +316,18 @@ int r100_irq_process(struct radeon_device *rdev)
|
|
if (status & RADEON_CRTC2_VBLANK_STAT) {
|
|
drm_handle_vblank(rdev->ddev, 1);
|
|
}
|
|
+ if (status & RADEON_FP_DETECT_STAT) {
|
|
+ queue_hotplug = true;
|
|
+ DRM_DEBUG("HPD1\n");
|
|
+ }
|
|
+ if (status & RADEON_FP2_DETECT_STAT) {
|
|
+ queue_hotplug = true;
|
|
+ DRM_DEBUG("HPD2\n");
|
|
+ }
|
|
status = r100_irq_ack(rdev);
|
|
}
|
|
+ if (queue_hotplug)
|
|
+ queue_work(rdev->wq, &rdev->hotplug_work);
|
|
if (rdev->msi_enabled) {
|
|
switch (rdev->family) {
|
|
case CHIP_RS400:
|
|
@@ -235,14 +354,25 @@ u32 r100_get_vblank_counter(struct radeon_device *rdev, int crtc)
|
|
return RREG32(RADEON_CRTC2_CRNT_FRAME);
|
|
}
|
|
|
|
+/* Who ever call radeon_fence_emit should call ring_lock and ask
|
|
+ * for enough space (today caller are ib schedule and buffer move) */
|
|
void r100_fence_ring_emit(struct radeon_device *rdev,
|
|
struct radeon_fence *fence)
|
|
{
|
|
- /* Who ever call radeon_fence_emit should call ring_lock and ask
|
|
- * for enough space (today caller are ib schedule and buffer move) */
|
|
+ /* We have to make sure that caches are flushed before
|
|
+ * CPU might read something from VRAM. */
|
|
+ radeon_ring_write(rdev, PACKET0(RADEON_RB3D_DSTCACHE_CTLSTAT, 0));
|
|
+ radeon_ring_write(rdev, RADEON_RB3D_DC_FLUSH_ALL);
|
|
+ radeon_ring_write(rdev, PACKET0(RADEON_RB3D_ZCACHE_CTLSTAT, 0));
|
|
+ radeon_ring_write(rdev, RADEON_RB3D_ZC_FLUSH_ALL);
|
|
/* Wait until IDLE & CLEAN */
|
|
radeon_ring_write(rdev, PACKET0(0x1720, 0));
|
|
radeon_ring_write(rdev, (1 << 16) | (1 << 17));
|
|
+ radeon_ring_write(rdev, PACKET0(RADEON_HOST_PATH_CNTL, 0));
|
|
+ radeon_ring_write(rdev, rdev->config.r100.hdp_cntl |
|
|
+ RADEON_HDP_READ_BUFFER_INVALIDATE);
|
|
+ radeon_ring_write(rdev, PACKET0(RADEON_HOST_PATH_CNTL, 0));
|
|
+ radeon_ring_write(rdev, rdev->config.r100.hdp_cntl);
|
|
/* Emit fence sequence & fire IRQ */
|
|
radeon_ring_write(rdev, PACKET0(rdev->fence_drv.scratch_reg, 0));
|
|
radeon_ring_write(rdev, fence->seq);
|
|
@@ -255,24 +385,27 @@ int r100_wb_init(struct radeon_device *rdev)
|
|
int r;
|
|
|
|
if (rdev->wb.wb_obj == NULL) {
|
|
- r = radeon_object_create(rdev, NULL, RADEON_GPU_PAGE_SIZE,
|
|
- true,
|
|
- RADEON_GEM_DOMAIN_GTT,
|
|
- false, &rdev->wb.wb_obj);
|
|
+ r = radeon_bo_create(rdev, NULL, RADEON_GPU_PAGE_SIZE, true,
|
|
+ RADEON_GEM_DOMAIN_GTT,
|
|
+ &rdev->wb.wb_obj);
|
|
if (r) {
|
|
- DRM_ERROR("radeon: failed to create WB buffer (%d).\n", r);
|
|
+ dev_err(rdev->dev, "(%d) create WB buffer failed\n", r);
|
|
return r;
|
|
}
|
|
- r = radeon_object_pin(rdev->wb.wb_obj,
|
|
- RADEON_GEM_DOMAIN_GTT,
|
|
- &rdev->wb.gpu_addr);
|
|
+ r = radeon_bo_reserve(rdev->wb.wb_obj, false);
|
|
+ if (unlikely(r != 0))
|
|
+ return r;
|
|
+ r = radeon_bo_pin(rdev->wb.wb_obj, RADEON_GEM_DOMAIN_GTT,
|
|
+ &rdev->wb.gpu_addr);
|
|
if (r) {
|
|
- DRM_ERROR("radeon: failed to pin WB buffer (%d).\n", r);
|
|
+ dev_err(rdev->dev, "(%d) pin WB buffer failed\n", r);
|
|
+ radeon_bo_unreserve(rdev->wb.wb_obj);
|
|
return r;
|
|
}
|
|
- r = radeon_object_kmap(rdev->wb.wb_obj, (void **)&rdev->wb.wb);
|
|
+ r = radeon_bo_kmap(rdev->wb.wb_obj, (void **)&rdev->wb.wb);
|
|
+ radeon_bo_unreserve(rdev->wb.wb_obj);
|
|
if (r) {
|
|
- DRM_ERROR("radeon: failed to map WB buffer (%d).\n", r);
|
|
+ dev_err(rdev->dev, "(%d) map WB buffer failed\n", r);
|
|
return r;
|
|
}
|
|
}
|
|
@@ -290,11 +423,19 @@ void r100_wb_disable(struct radeon_device *rdev)
|
|
|
|
void r100_wb_fini(struct radeon_device *rdev)
|
|
{
|
|
+ int r;
|
|
+
|
|
r100_wb_disable(rdev);
|
|
if (rdev->wb.wb_obj) {
|
|
- radeon_object_kunmap(rdev->wb.wb_obj);
|
|
- radeon_object_unpin(rdev->wb.wb_obj);
|
|
- radeon_object_unref(&rdev->wb.wb_obj);
|
|
+ r = radeon_bo_reserve(rdev->wb.wb_obj, false);
|
|
+ if (unlikely(r != 0)) {
|
|
+ dev_err(rdev->dev, "(%d) can't finish WB\n", r);
|
|
+ return;
|
|
+ }
|
|
+ radeon_bo_kunmap(rdev->wb.wb_obj);
|
|
+ radeon_bo_unpin(rdev->wb.wb_obj);
|
|
+ radeon_bo_unreserve(rdev->wb.wb_obj);
|
|
+ radeon_bo_unref(&rdev->wb.wb_obj);
|
|
rdev->wb.wb = NULL;
|
|
rdev->wb.wb_obj = NULL;
|
|
}
|
|
@@ -1250,7 +1391,6 @@ static int r100_packet0_check(struct radeon_cs_parser *p,
|
|
case RADEON_TXFORMAT_ARGB4444:
|
|
case RADEON_TXFORMAT_VYUY422:
|
|
case RADEON_TXFORMAT_YVYU422:
|
|
- case RADEON_TXFORMAT_DXT1:
|
|
case RADEON_TXFORMAT_SHADOW16:
|
|
case RADEON_TXFORMAT_LDUDV655:
|
|
case RADEON_TXFORMAT_DUDV88:
|
|
@@ -1258,12 +1398,19 @@ static int r100_packet0_check(struct radeon_cs_parser *p,
|
|
break;
|
|
case RADEON_TXFORMAT_ARGB8888:
|
|
case RADEON_TXFORMAT_RGBA8888:
|
|
- case RADEON_TXFORMAT_DXT23:
|
|
- case RADEON_TXFORMAT_DXT45:
|
|
case RADEON_TXFORMAT_SHADOW32:
|
|
case RADEON_TXFORMAT_LDUDUV8888:
|
|
track->textures[i].cpp = 4;
|
|
break;
|
|
+ case RADEON_TXFORMAT_DXT1:
|
|
+ track->textures[i].cpp = 1;
|
|
+ track->textures[i].compress_format = R100_TRACK_COMP_DXT1;
|
|
+ break;
|
|
+ case RADEON_TXFORMAT_DXT23:
|
|
+ case RADEON_TXFORMAT_DXT45:
|
|
+ track->textures[i].cpp = 1;
|
|
+ track->textures[i].compress_format = R100_TRACK_COMP_DXT35;
|
|
+ break;
|
|
}
|
|
track->textures[i].cube_info[4].width = 1 << ((idx_value >> 16) & 0xf);
|
|
track->textures[i].cube_info[4].height = 1 << ((idx_value >> 20) & 0xf);
|
|
@@ -1288,17 +1435,17 @@ static int r100_packet0_check(struct radeon_cs_parser *p,
|
|
|
|
int r100_cs_track_check_pkt3_indx_buffer(struct radeon_cs_parser *p,
|
|
struct radeon_cs_packet *pkt,
|
|
- struct radeon_object *robj)
|
|
+ struct radeon_bo *robj)
|
|
{
|
|
unsigned idx;
|
|
u32 value;
|
|
idx = pkt->idx + 1;
|
|
value = radeon_get_ib_value(p, idx + 2);
|
|
- if ((value + 1) > radeon_object_size(robj)) {
|
|
+ if ((value + 1) > radeon_bo_size(robj)) {
|
|
DRM_ERROR("[drm] Buffer too small for PACKET3 INDX_BUFFER "
|
|
"(need %u have %lu) !\n",
|
|
value + 1,
|
|
- radeon_object_size(robj));
|
|
+ radeon_bo_size(robj));
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
@@ -1363,6 +1510,7 @@ static int r100_packet3_check(struct radeon_cs_parser *p,
|
|
DRM_ERROR("PRIM_WALK must be 3 for IMMD draw\n");
|
|
return -EINVAL;
|
|
}
|
|
+ track->vtx_size = r100_get_vtx_size(radeon_get_ib_value(p, idx + 0));
|
|
track->vap_vf_cntl = radeon_get_ib_value(p, idx + 1);
|
|
track->immd_dwords = pkt->count - 1;
|
|
r = r100_cs_track_check(p->rdev, track);
|
|
@@ -1650,6 +1798,82 @@ int r100_gpu_reset(struct radeon_device *rdev)
|
|
return 0;
|
|
}
|
|
|
|
+void r100_set_common_regs(struct radeon_device *rdev)
|
|
+{
|
|
+ struct drm_device *dev = rdev->ddev;
|
|
+ bool force_dac2 = false;
|
|
+
|
|
+ /* set these so they don't interfere with anything */
|
|
+ WREG32(RADEON_OV0_SCALE_CNTL, 0);
|
|
+ WREG32(RADEON_SUBPIC_CNTL, 0);
|
|
+ WREG32(RADEON_VIPH_CONTROL, 0);
|
|
+ WREG32(RADEON_I2C_CNTL_1, 0);
|
|
+ WREG32(RADEON_DVI_I2C_CNTL_1, 0);
|
|
+ WREG32(RADEON_CAP0_TRIG_CNTL, 0);
|
|
+ WREG32(RADEON_CAP1_TRIG_CNTL, 0);
|
|
+
|
|
+ /* always set up dac2 on rn50 and some rv100 as lots
|
|
+ * of servers seem to wire it up to a VGA port but
|
|
+ * don't report it in the bios connector
|
|
+ * table.
|
|
+ */
|
|
+ switch (dev->pdev->device) {
|
|
+ /* RN50 */
|
|
+ case 0x515e:
|
|
+ case 0x5969:
|
|
+ force_dac2 = true;
|
|
+ break;
|
|
+ /* RV100*/
|
|
+ case 0x5159:
|
|
+ case 0x515a:
|
|
+ /* DELL triple head servers */
|
|
+ if ((dev->pdev->subsystem_vendor == 0x1028 /* DELL */) &&
|
|
+ ((dev->pdev->subsystem_device == 0x016c) ||
|
|
+ (dev->pdev->subsystem_device == 0x016d) ||
|
|
+ (dev->pdev->subsystem_device == 0x016e) ||
|
|
+ (dev->pdev->subsystem_device == 0x016f) ||
|
|
+ (dev->pdev->subsystem_device == 0x0170) ||
|
|
+ (dev->pdev->subsystem_device == 0x017d) ||
|
|
+ (dev->pdev->subsystem_device == 0x017e) ||
|
|
+ (dev->pdev->subsystem_device == 0x0183) ||
|
|
+ (dev->pdev->subsystem_device == 0x018a) ||
|
|
+ (dev->pdev->subsystem_device == 0x019a)))
|
|
+ force_dac2 = true;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (force_dac2) {
|
|
+ u32 disp_hw_debug = RREG32(RADEON_DISP_HW_DEBUG);
|
|
+ u32 tv_dac_cntl = RREG32(RADEON_TV_DAC_CNTL);
|
|
+ u32 dac2_cntl = RREG32(RADEON_DAC_CNTL2);
|
|
+
|
|
+ /* For CRT on DAC2, don't turn it on if BIOS didn't
|
|
+ enable it, even it's detected.
|
|
+ */
|
|
+
|
|
+ /* force it to crtc0 */
|
|
+ dac2_cntl &= ~RADEON_DAC2_DAC_CLK_SEL;
|
|
+ dac2_cntl |= RADEON_DAC2_DAC2_CLK_SEL;
|
|
+ disp_hw_debug |= RADEON_CRT2_DISP1_SEL;
|
|
+
|
|
+ /* set up the TV DAC */
|
|
+ tv_dac_cntl &= ~(RADEON_TV_DAC_PEDESTAL |
|
|
+ RADEON_TV_DAC_STD_MASK |
|
|
+ RADEON_TV_DAC_RDACPD |
|
|
+ RADEON_TV_DAC_GDACPD |
|
|
+ RADEON_TV_DAC_BDACPD |
|
|
+ RADEON_TV_DAC_BGADJ_MASK |
|
|
+ RADEON_TV_DAC_DACADJ_MASK);
|
|
+ tv_dac_cntl |= (RADEON_TV_DAC_NBLANK |
|
|
+ RADEON_TV_DAC_NHOLD |
|
|
+ RADEON_TV_DAC_STD_PS2 |
|
|
+ (0x58 << 16));
|
|
+
|
|
+ WREG32(RADEON_TV_DAC_CNTL, tv_dac_cntl);
|
|
+ WREG32(RADEON_DISP_HW_DEBUG, disp_hw_debug);
|
|
+ WREG32(RADEON_DAC_CNTL2, dac2_cntl);
|
|
+ }
|
|
+}
|
|
|
|
/*
|
|
* VRAM info
|
|
@@ -2588,13 +2812,14 @@ static inline void r100_cs_track_texture_print(struct r100_cs_track_texture *t)
|
|
DRM_ERROR("coordinate type %d\n", t->tex_coord_type);
|
|
DRM_ERROR("width round to power of 2 %d\n", t->roundup_w);
|
|
DRM_ERROR("height round to power of 2 %d\n", t->roundup_h);
|
|
+ DRM_ERROR("compress format %d\n", t->compress_format);
|
|
}
|
|
|
|
static int r100_cs_track_cube(struct radeon_device *rdev,
|
|
struct r100_cs_track *track, unsigned idx)
|
|
{
|
|
unsigned face, w, h;
|
|
- struct radeon_object *cube_robj;
|
|
+ struct radeon_bo *cube_robj;
|
|
unsigned long size;
|
|
|
|
for (face = 0; face < 5; face++) {
|
|
@@ -2607,9 +2832,9 @@ static int r100_cs_track_cube(struct radeon_device *rdev,
|
|
|
|
size += track->textures[idx].cube_info[face].offset;
|
|
|
|
- if (size > radeon_object_size(cube_robj)) {
|
|
+ if (size > radeon_bo_size(cube_robj)) {
|
|
DRM_ERROR("Cube texture offset greater than object size %lu %lu\n",
|
|
- size, radeon_object_size(cube_robj));
|
|
+ size, radeon_bo_size(cube_robj));
|
|
r100_cs_track_texture_print(&track->textures[idx]);
|
|
return -1;
|
|
}
|
|
@@ -2617,10 +2842,40 @@ static int r100_cs_track_cube(struct radeon_device *rdev,
|
|
return 0;
|
|
}
|
|
|
|
+static int r100_track_compress_size(int compress_format, int w, int h)
|
|
+{
|
|
+ int block_width, block_height, block_bytes;
|
|
+ int wblocks, hblocks;
|
|
+ int min_wblocks;
|
|
+ int sz;
|
|
+
|
|
+ block_width = 4;
|
|
+ block_height = 4;
|
|
+
|
|
+ switch (compress_format) {
|
|
+ case R100_TRACK_COMP_DXT1:
|
|
+ block_bytes = 8;
|
|
+ min_wblocks = 4;
|
|
+ break;
|
|
+ default:
|
|
+ case R100_TRACK_COMP_DXT35:
|
|
+ block_bytes = 16;
|
|
+ min_wblocks = 2;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ hblocks = (h + block_height - 1) / block_height;
|
|
+ wblocks = (w + block_width - 1) / block_width;
|
|
+ if (wblocks < min_wblocks)
|
|
+ wblocks = min_wblocks;
|
|
+ sz = wblocks * hblocks * block_bytes;
|
|
+ return sz;
|
|
+}
|
|
+
|
|
static int r100_cs_track_texture_check(struct radeon_device *rdev,
|
|
struct r100_cs_track *track)
|
|
{
|
|
- struct radeon_object *robj;
|
|
+ struct radeon_bo *robj;
|
|
unsigned long size;
|
|
unsigned u, i, w, h;
|
|
int ret;
|
|
@@ -2654,9 +2909,15 @@ static int r100_cs_track_texture_check(struct radeon_device *rdev,
|
|
h = h / (1 << i);
|
|
if (track->textures[u].roundup_h)
|
|
h = roundup_pow_of_two(h);
|
|
- size += w * h;
|
|
+ if (track->textures[u].compress_format) {
|
|
+
|
|
+ size += r100_track_compress_size(track->textures[u].compress_format, w, h);
|
|
+ /* compressed textures are block based */
|
|
+ } else
|
|
+ size += w * h;
|
|
}
|
|
size *= track->textures[u].cpp;
|
|
+
|
|
switch (track->textures[u].tex_coord_type) {
|
|
case 0:
|
|
break;
|
|
@@ -2676,9 +2937,9 @@ static int r100_cs_track_texture_check(struct radeon_device *rdev,
|
|
"%u\n", track->textures[u].tex_coord_type, u);
|
|
return -EINVAL;
|
|
}
|
|
- if (size > radeon_object_size(robj)) {
|
|
+ if (size > radeon_bo_size(robj)) {
|
|
DRM_ERROR("Texture of unit %u needs %lu bytes but is "
|
|
- "%lu\n", u, size, radeon_object_size(robj));
|
|
+ "%lu\n", u, size, radeon_bo_size(robj));
|
|
r100_cs_track_texture_print(&track->textures[u]);
|
|
return -EINVAL;
|
|
}
|
|
@@ -2695,15 +2956,19 @@ int r100_cs_track_check(struct radeon_device *rdev, struct r100_cs_track *track)
|
|
|
|
for (i = 0; i < track->num_cb; i++) {
|
|
if (track->cb[i].robj == NULL) {
|
|
+ if (!(track->fastfill || track->color_channel_mask ||
|
|
+ track->blend_read_enable)) {
|
|
+ continue;
|
|
+ }
|
|
DRM_ERROR("[drm] No buffer for color buffer %d !\n", i);
|
|
return -EINVAL;
|
|
}
|
|
size = track->cb[i].pitch * track->cb[i].cpp * track->maxy;
|
|
size += track->cb[i].offset;
|
|
- if (size > radeon_object_size(track->cb[i].robj)) {
|
|
+ if (size > radeon_bo_size(track->cb[i].robj)) {
|
|
DRM_ERROR("[drm] Buffer too small for color buffer %d "
|
|
"(need %lu have %lu) !\n", i, size,
|
|
- radeon_object_size(track->cb[i].robj));
|
|
+ radeon_bo_size(track->cb[i].robj));
|
|
DRM_ERROR("[drm] color buffer %d (%u %u %u %u)\n",
|
|
i, track->cb[i].pitch, track->cb[i].cpp,
|
|
track->cb[i].offset, track->maxy);
|
|
@@ -2717,10 +2982,10 @@ int r100_cs_track_check(struct radeon_device *rdev, struct r100_cs_track *track)
|
|
}
|
|
size = track->zb.pitch * track->zb.cpp * track->maxy;
|
|
size += track->zb.offset;
|
|
- if (size > radeon_object_size(track->zb.robj)) {
|
|
+ if (size > radeon_bo_size(track->zb.robj)) {
|
|
DRM_ERROR("[drm] Buffer too small for z buffer "
|
|
"(need %lu have %lu) !\n", size,
|
|
- radeon_object_size(track->zb.robj));
|
|
+ radeon_bo_size(track->zb.robj));
|
|
DRM_ERROR("[drm] zbuffer (%u %u %u %u)\n",
|
|
track->zb.pitch, track->zb.cpp,
|
|
track->zb.offset, track->maxy);
|
|
@@ -2738,11 +3003,12 @@ int r100_cs_track_check(struct radeon_device *rdev, struct r100_cs_track *track)
|
|
"bound\n", prim_walk, i);
|
|
return -EINVAL;
|
|
}
|
|
- if (size > radeon_object_size(track->arrays[i].robj)) {
|
|
- DRM_ERROR("(PW %u) Vertex array %u need %lu dwords "
|
|
- "have %lu dwords\n", prim_walk, i,
|
|
- size >> 2,
|
|
- radeon_object_size(track->arrays[i].robj) >> 2);
|
|
+ if (size > radeon_bo_size(track->arrays[i].robj)) {
|
|
+ dev_err(rdev->dev, "(PW %u) Vertex array %u "
|
|
+ "need %lu dwords have %lu dwords\n",
|
|
+ prim_walk, i, size >> 2,
|
|
+ radeon_bo_size(track->arrays[i].robj)
|
|
+ >> 2);
|
|
DRM_ERROR("Max indices %u\n", track->max_indx);
|
|
return -EINVAL;
|
|
}
|
|
@@ -2756,10 +3022,12 @@ int r100_cs_track_check(struct radeon_device *rdev, struct r100_cs_track *track)
|
|
"bound\n", prim_walk, i);
|
|
return -EINVAL;
|
|
}
|
|
- if (size > radeon_object_size(track->arrays[i].robj)) {
|
|
- DRM_ERROR("(PW %u) Vertex array %u need %lu dwords "
|
|
- "have %lu dwords\n", prim_walk, i, size >> 2,
|
|
- radeon_object_size(track->arrays[i].robj) >> 2);
|
|
+ if (size > radeon_bo_size(track->arrays[i].robj)) {
|
|
+ dev_err(rdev->dev, "(PW %u) Vertex array %u "
|
|
+ "need %lu dwords have %lu dwords\n",
|
|
+ prim_walk, i, size >> 2,
|
|
+ radeon_bo_size(track->arrays[i].robj)
|
|
+ >> 2);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
@@ -2821,6 +3089,7 @@ void r100_cs_track_clear(struct radeon_device *rdev, struct r100_cs_track *track
|
|
track->arrays[i].esize = 0x7F;
|
|
}
|
|
for (i = 0; i < track->num_texture; i++) {
|
|
+ track->textures[i].compress_format = R100_TRACK_COMP_NONE;
|
|
track->textures[i].pitch = 16536;
|
|
track->textures[i].width = 16536;
|
|
track->textures[i].height = 16536;
|
|
@@ -3101,6 +3370,9 @@ static int r100_startup(struct radeon_device *rdev)
|
|
{
|
|
int r;
|
|
|
|
+ /* set common regs */
|
|
+ r100_set_common_regs(rdev);
|
|
+ /* program mc */
|
|
r100_mc_program(rdev);
|
|
/* Resume clock */
|
|
r100_clock_startup(rdev);
|
|
@@ -3108,14 +3380,15 @@ static int r100_startup(struct radeon_device *rdev)
|
|
r100_gpu_init(rdev);
|
|
/* Initialize GART (initialize after TTM so we can allocate
|
|
* memory through TTM but finalize after TTM) */
|
|
+ r100_enable_bm(rdev);
|
|
if (rdev->flags & RADEON_IS_PCI) {
|
|
r = r100_pci_gart_enable(rdev);
|
|
if (r)
|
|
return r;
|
|
}
|
|
/* Enable IRQ */
|
|
- rdev->irq.sw_int = true;
|
|
r100_irq_set(rdev);
|
|
+ rdev->config.r100.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL);
|
|
/* 1M ring buffer */
|
|
r = r100_cp_init(rdev, 1024 * 1024);
|
|
if (r) {
|
|
@@ -3150,6 +3423,8 @@ int r100_resume(struct radeon_device *rdev)
|
|
radeon_combios_asic_init(rdev->ddev);
|
|
/* Resume clock after posting */
|
|
r100_clock_startup(rdev);
|
|
+ /* Initialize surface registers */
|
|
+ radeon_surface_init(rdev);
|
|
return r100_startup(rdev);
|
|
}
|
|
|
|
@@ -3165,16 +3440,16 @@ int r100_suspend(struct radeon_device *rdev)
|
|
|
|
void r100_fini(struct radeon_device *rdev)
|
|
{
|
|
- r100_suspend(rdev);
|
|
r100_cp_fini(rdev);
|
|
r100_wb_fini(rdev);
|
|
r100_ib_fini(rdev);
|
|
radeon_gem_fini(rdev);
|
|
if (rdev->flags & RADEON_IS_PCI)
|
|
r100_pci_gart_fini(rdev);
|
|
+ radeon_agp_fini(rdev);
|
|
radeon_irq_kms_fini(rdev);
|
|
radeon_fence_driver_fini(rdev);
|
|
- radeon_object_fini(rdev);
|
|
+ radeon_bo_fini(rdev);
|
|
radeon_atombios_fini(rdev);
|
|
kfree(rdev->bios);
|
|
rdev->bios = NULL;
|
|
@@ -3195,9 +3470,7 @@ int r100_mc_init(struct radeon_device *rdev)
|
|
if (rdev->flags & RADEON_IS_AGP) {
|
|
r = radeon_agp_init(rdev);
|
|
if (r) {
|
|
- printk(KERN_WARNING "[drm] Disabling AGP\n");
|
|
- rdev->flags &= ~RADEON_IS_AGP;
|
|
- rdev->mc.gtt_size = radeon_gart_size * 1024 * 1024;
|
|
+ radeon_agp_disable(rdev);
|
|
} else {
|
|
rdev->mc.gtt_location = rdev->mc.agp_base;
|
|
}
|
|
@@ -3242,14 +3515,14 @@ int r100_init(struct radeon_device *rdev)
|
|
RREG32(R_0007C0_CP_STAT));
|
|
}
|
|
/* check if cards are posted or not */
|
|
- if (!radeon_card_posted(rdev) && rdev->bios) {
|
|
- DRM_INFO("GPU not posted. posting now...\n");
|
|
- radeon_combios_asic_init(rdev->ddev);
|
|
- }
|
|
+ if (radeon_boot_test_post_card(rdev) == false)
|
|
+ return -EINVAL;
|
|
/* Set asic errata */
|
|
r100_errata(rdev);
|
|
/* Initialize clocks */
|
|
radeon_get_clock_info(rdev->ddev);
|
|
+ /* Initialize power management */
|
|
+ radeon_pm_init(rdev);
|
|
/* Get vram informations */
|
|
r100_vram_info(rdev);
|
|
/* Initialize memory controller (also test AGP) */
|
|
@@ -3264,7 +3537,7 @@ int r100_init(struct radeon_device *rdev)
|
|
if (r)
|
|
return r;
|
|
/* Memory manager */
|
|
- r = radeon_object_init(rdev);
|
|
+ r = radeon_bo_init(rdev);
|
|
if (r)
|
|
return r;
|
|
if (rdev->flags & RADEON_IS_PCI) {
|
|
@@ -3278,13 +3551,12 @@ int r100_init(struct radeon_device *rdev)
|
|
if (r) {
|
|
/* Somethings want wront with the accel init stop accel */
|
|
dev_err(rdev->dev, "Disabling GPU acceleration\n");
|
|
- r100_suspend(rdev);
|
|
r100_cp_fini(rdev);
|
|
r100_wb_fini(rdev);
|
|
r100_ib_fini(rdev);
|
|
+ radeon_irq_kms_fini(rdev);
|
|
if (rdev->flags & RADEON_IS_PCI)
|
|
r100_pci_gart_fini(rdev);
|
|
- radeon_irq_kms_fini(rdev);
|
|
rdev->accel_working = false;
|
|
}
|
|
return 0;
|
|
diff --git a/drivers/gpu/drm/radeon/r100_track.h b/drivers/gpu/drm/radeon/r100_track.h
|
|
index 0daf0d7..b27a699 100644
|
|
--- a/drivers/gpu/drm/radeon/r100_track.h
|
|
+++ b/drivers/gpu/drm/radeon/r100_track.h
|
|
@@ -10,26 +10,30 @@
|
|
* CS functions
|
|
*/
|
|
struct r100_cs_track_cb {
|
|
- struct radeon_object *robj;
|
|
+ struct radeon_bo *robj;
|
|
unsigned pitch;
|
|
unsigned cpp;
|
|
unsigned offset;
|
|
};
|
|
|
|
struct r100_cs_track_array {
|
|
- struct radeon_object *robj;
|
|
+ struct radeon_bo *robj;
|
|
unsigned esize;
|
|
};
|
|
|
|
struct r100_cs_cube_info {
|
|
- struct radeon_object *robj;
|
|
- unsigned offset;
|
|
+ struct radeon_bo *robj;
|
|
+ unsigned offset;
|
|
unsigned width;
|
|
unsigned height;
|
|
};
|
|
|
|
+#define R100_TRACK_COMP_NONE 0
|
|
+#define R100_TRACK_COMP_DXT1 1
|
|
+#define R100_TRACK_COMP_DXT35 2
|
|
+
|
|
struct r100_cs_track_texture {
|
|
- struct radeon_object *robj;
|
|
+ struct radeon_bo *robj;
|
|
struct r100_cs_cube_info cube_info[5]; /* info for 5 non-primary faces */
|
|
unsigned pitch;
|
|
unsigned width;
|
|
@@ -44,6 +48,7 @@ struct r100_cs_track_texture {
|
|
bool enabled;
|
|
bool roundup_w;
|
|
bool roundup_h;
|
|
+ unsigned compress_format;
|
|
};
|
|
|
|
struct r100_cs_track_limits {
|
|
@@ -62,13 +67,15 @@ struct r100_cs_track {
|
|
unsigned immd_dwords;
|
|
unsigned num_arrays;
|
|
unsigned max_indx;
|
|
+ unsigned color_channel_mask;
|
|
struct r100_cs_track_array arrays[11];
|
|
struct r100_cs_track_cb cb[R300_MAX_CB];
|
|
struct r100_cs_track_cb zb;
|
|
struct r100_cs_track_texture textures[R300_TRACK_MAX_TEXTURE];
|
|
bool z_enabled;
|
|
bool separate_cube;
|
|
-
|
|
+ bool fastfill;
|
|
+ bool blend_read_enable;
|
|
};
|
|
|
|
int r100_cs_track_check(struct radeon_device *rdev, struct r100_cs_track *track);
|
|
diff --git a/drivers/gpu/drm/radeon/r200.c b/drivers/gpu/drm/radeon/r200.c
|
|
index eb740fc..ff1e0cd 100644
|
|
--- a/drivers/gpu/drm/radeon/r200.c
|
|
+++ b/drivers/gpu/drm/radeon/r200.c
|
|
@@ -371,13 +371,16 @@ int r200_packet0_check(struct radeon_cs_parser *p,
|
|
case 5:
|
|
case 6:
|
|
case 7:
|
|
+ /* 1D/2D */
|
|
track->textures[i].tex_coord_type = 0;
|
|
break;
|
|
case 1:
|
|
- track->textures[i].tex_coord_type = 1;
|
|
+ /* CUBE */
|
|
+ track->textures[i].tex_coord_type = 2;
|
|
break;
|
|
case 2:
|
|
- track->textures[i].tex_coord_type = 2;
|
|
+ /* 3D */
|
|
+ track->textures[i].tex_coord_type = 1;
|
|
break;
|
|
}
|
|
break;
|
|
@@ -401,7 +404,6 @@ int r200_packet0_check(struct radeon_cs_parser *p,
|
|
case R200_TXFORMAT_Y8:
|
|
track->textures[i].cpp = 1;
|
|
break;
|
|
- case R200_TXFORMAT_DXT1:
|
|
case R200_TXFORMAT_AI88:
|
|
case R200_TXFORMAT_ARGB1555:
|
|
case R200_TXFORMAT_RGB565:
|
|
@@ -418,9 +420,16 @@ int r200_packet0_check(struct radeon_cs_parser *p,
|
|
case R200_TXFORMAT_ABGR8888:
|
|
case R200_TXFORMAT_BGR111110:
|
|
case R200_TXFORMAT_LDVDU8888:
|
|
+ track->textures[i].cpp = 4;
|
|
+ break;
|
|
+ case R200_TXFORMAT_DXT1:
|
|
+ track->textures[i].cpp = 1;
|
|
+ track->textures[i].compress_format = R100_TRACK_COMP_DXT1;
|
|
+ break;
|
|
case R200_TXFORMAT_DXT23:
|
|
case R200_TXFORMAT_DXT45:
|
|
- track->textures[i].cpp = 4;
|
|
+ track->textures[i].cpp = 1;
|
|
+ track->textures[i].compress_format = R100_TRACK_COMP_DXT1;
|
|
break;
|
|
}
|
|
track->textures[i].cube_info[4].width = 1 << ((idx_value >> 16) & 0xf);
|
|
diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c
|
|
index 2f43ee8..43b55a0 100644
|
|
--- a/drivers/gpu/drm/radeon/r300.c
|
|
+++ b/drivers/gpu/drm/radeon/r300.c
|
|
@@ -36,7 +36,15 @@
|
|
#include "rv350d.h"
|
|
#include "r300_reg_safe.h"
|
|
|
|
-/* This files gather functions specifics to: r300,r350,rv350,rv370,rv380 */
|
|
+/* This files gather functions specifics to: r300,r350,rv350,rv370,rv380
|
|
+ *
|
|
+ * GPU Errata:
|
|
+ * - HOST_PATH_CNTL: r300 family seems to dislike write to HOST_PATH_CNTL
|
|
+ * using MMIO to flush host path read cache, this lead to HARDLOCKUP.
|
|
+ * However, scheduling such write to the ring seems harmless, i suspect
|
|
+ * the CP read collide with the flush somehow, or maybe the MC, hard to
|
|
+ * tell. (Jerome Glisse)
|
|
+ */
|
|
|
|
/*
|
|
* rv370,rv380 PCIE GART
|
|
@@ -137,14 +145,19 @@ int rv370_pcie_gart_enable(struct radeon_device *rdev)
|
|
|
|
void rv370_pcie_gart_disable(struct radeon_device *rdev)
|
|
{
|
|
- uint32_t tmp;
|
|
+ u32 tmp;
|
|
+ int r;
|
|
|
|
tmp = RREG32_PCIE(RADEON_PCIE_TX_GART_CNTL);
|
|
tmp |= RADEON_PCIE_TX_GART_UNMAPPED_ACCESS_DISCARD;
|
|
WREG32_PCIE(RADEON_PCIE_TX_GART_CNTL, tmp & ~RADEON_PCIE_TX_GART_EN);
|
|
if (rdev->gart.table.vram.robj) {
|
|
- radeon_object_kunmap(rdev->gart.table.vram.robj);
|
|
- radeon_object_unpin(rdev->gart.table.vram.robj);
|
|
+ r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
|
|
+ if (likely(r == 0)) {
|
|
+ radeon_bo_kunmap(rdev->gart.table.vram.robj);
|
|
+ radeon_bo_unpin(rdev->gart.table.vram.robj);
|
|
+ radeon_bo_unreserve(rdev->gart.table.vram.robj);
|
|
+ }
|
|
}
|
|
}
|
|
|
|
@@ -173,6 +186,11 @@ void r300_fence_ring_emit(struct radeon_device *rdev,
|
|
/* Wait until IDLE & CLEAN */
|
|
radeon_ring_write(rdev, PACKET0(0x1720, 0));
|
|
radeon_ring_write(rdev, (1 << 17) | (1 << 16) | (1 << 9));
|
|
+ radeon_ring_write(rdev, PACKET0(RADEON_HOST_PATH_CNTL, 0));
|
|
+ radeon_ring_write(rdev, rdev->config.r300.hdp_cntl |
|
|
+ RADEON_HDP_READ_BUFFER_INVALIDATE);
|
|
+ radeon_ring_write(rdev, PACKET0(RADEON_HOST_PATH_CNTL, 0));
|
|
+ radeon_ring_write(rdev, rdev->config.r300.hdp_cntl);
|
|
/* Emit fence sequence & fire IRQ */
|
|
radeon_ring_write(rdev, PACKET0(rdev->fence_drv.scratch_reg, 0));
|
|
radeon_ring_write(rdev, fence->seq);
|
|
@@ -488,11 +506,14 @@ void r300_vram_info(struct radeon_device *rdev)
|
|
|
|
/* DDR for all card after R300 & IGP */
|
|
rdev->mc.vram_is_ddr = true;
|
|
+
|
|
tmp = RREG32(RADEON_MEM_CNTL);
|
|
- if (tmp & R300_MEM_NUM_CHANNELS_MASK) {
|
|
- rdev->mc.vram_width = 128;
|
|
- } else {
|
|
- rdev->mc.vram_width = 64;
|
|
+ tmp &= R300_MEM_NUM_CHANNELS_MASK;
|
|
+ switch (tmp) {
|
|
+ case 0: rdev->mc.vram_width = 64; break;
|
|
+ case 1: rdev->mc.vram_width = 128; break;
|
|
+ case 2: rdev->mc.vram_width = 256; break;
|
|
+ default: rdev->mc.vram_width = 128; break;
|
|
}
|
|
|
|
r100_vram_init_sizes(rdev);
|
|
@@ -681,7 +702,15 @@ static int r300_packet0_check(struct radeon_cs_parser *p,
|
|
r100_cs_dump_packet(p, pkt);
|
|
return r;
|
|
}
|
|
- ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset);
|
|
+
|
|
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
|
|
+ tile_flags |= R300_TXO_MACRO_TILE;
|
|
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
|
|
+ tile_flags |= R300_TXO_MICRO_TILE;
|
|
+
|
|
+ tmp = idx_value + ((u32)reloc->lobj.gpu_offset);
|
|
+ tmp |= tile_flags;
|
|
+ ib[idx] = tmp;
|
|
track->textures[i].robj = reloc->robj;
|
|
break;
|
|
/* Tracked registers */
|
|
@@ -847,7 +876,6 @@ static int r300_packet0_check(struct radeon_cs_parser *p,
|
|
case R300_TX_FORMAT_Z6Y5X5:
|
|
case R300_TX_FORMAT_W4Z4Y4X4:
|
|
case R300_TX_FORMAT_W1Z5Y5X5:
|
|
- case R300_TX_FORMAT_DXT1:
|
|
case R300_TX_FORMAT_D3DMFT_CxV8U8:
|
|
case R300_TX_FORMAT_B8G8_B8G8:
|
|
case R300_TX_FORMAT_G8R8_G8B8:
|
|
@@ -861,8 +889,6 @@ static int r300_packet0_check(struct radeon_cs_parser *p,
|
|
case 0x17:
|
|
case R300_TX_FORMAT_FL_I32:
|
|
case 0x1e:
|
|
- case R300_TX_FORMAT_DXT3:
|
|
- case R300_TX_FORMAT_DXT5:
|
|
track->textures[i].cpp = 4;
|
|
break;
|
|
case R300_TX_FORMAT_W16Z16Y16X16:
|
|
@@ -873,6 +899,23 @@ static int r300_packet0_check(struct radeon_cs_parser *p,
|
|
case R300_TX_FORMAT_FL_R32G32B32A32:
|
|
track->textures[i].cpp = 16;
|
|
break;
|
|
+ case R300_TX_FORMAT_DXT1:
|
|
+ track->textures[i].cpp = 1;
|
|
+ track->textures[i].compress_format = R100_TRACK_COMP_DXT1;
|
|
+ break;
|
|
+ case R300_TX_FORMAT_ATI2N:
|
|
+ if (p->rdev->family < CHIP_R420) {
|
|
+ DRM_ERROR("Invalid texture format %u\n",
|
|
+ (idx_value & 0x1F));
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ /* The same rules apply as for DXT3/5. */
|
|
+ /* Pass through. */
|
|
+ case R300_TX_FORMAT_DXT3:
|
|
+ case R300_TX_FORMAT_DXT5:
|
|
+ track->textures[i].cpp = 1;
|
|
+ track->textures[i].compress_format = R100_TRACK_COMP_DXT35;
|
|
+ break;
|
|
default:
|
|
DRM_ERROR("Invalid texture format %u\n",
|
|
(idx_value & 0x1F));
|
|
@@ -932,6 +975,16 @@ static int r300_packet0_check(struct radeon_cs_parser *p,
|
|
track->textures[i].width_11 = tmp;
|
|
tmp = ((idx_value >> 16) & 1) << 11;
|
|
track->textures[i].height_11 = tmp;
|
|
+
|
|
+ /* ATI1N */
|
|
+ if (idx_value & (1 << 14)) {
|
|
+ /* The same rules apply as for DXT1. */
|
|
+ track->textures[i].compress_format =
|
|
+ R100_TRACK_COMP_DXT1;
|
|
+ }
|
|
+ } else if (idx_value & (1 << 14)) {
|
|
+ DRM_ERROR("Forbidden bit TXFORMAT_MSB\n");
|
|
+ return -EINVAL;
|
|
}
|
|
break;
|
|
case 0x4480:
|
|
@@ -973,6 +1026,18 @@ static int r300_packet0_check(struct radeon_cs_parser *p,
|
|
}
|
|
ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset);
|
|
break;
|
|
+ case 0x4e0c:
|
|
+ /* RB3D_COLOR_CHANNEL_MASK */
|
|
+ track->color_channel_mask = idx_value;
|
|
+ break;
|
|
+ case 0x4d1c:
|
|
+ /* ZB_BW_CNTL */
|
|
+ track->fastfill = !!(idx_value & (1 << 2));
|
|
+ break;
|
|
+ case 0x4e04:
|
|
+ /* RB3D_BLENDCNTL */
|
|
+ track->blend_read_enable = !!(idx_value & (1 << 2));
|
|
+ break;
|
|
case 0x4be8:
|
|
/* valid register only on RV530 */
|
|
if (p->rdev->family == CHIP_RV530)
|
|
@@ -1181,6 +1246,9 @@ static int r300_startup(struct radeon_device *rdev)
|
|
{
|
|
int r;
|
|
|
|
+ /* set common regs */
|
|
+ r100_set_common_regs(rdev);
|
|
+ /* program mc */
|
|
r300_mc_program(rdev);
|
|
/* Resume clock */
|
|
r300_clock_startup(rdev);
|
|
@@ -1193,14 +1261,20 @@ static int r300_startup(struct radeon_device *rdev)
|
|
if (r)
|
|
return r;
|
|
}
|
|
+
|
|
+ if (rdev->family == CHIP_R300 ||
|
|
+ rdev->family == CHIP_R350 ||
|
|
+ rdev->family == CHIP_RV350)
|
|
+ r100_enable_bm(rdev);
|
|
+
|
|
if (rdev->flags & RADEON_IS_PCI) {
|
|
r = r100_pci_gart_enable(rdev);
|
|
if (r)
|
|
return r;
|
|
}
|
|
/* Enable IRQ */
|
|
- rdev->irq.sw_int = true;
|
|
r100_irq_set(rdev);
|
|
+ rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL);
|
|
/* 1M ring buffer */
|
|
r = r100_cp_init(rdev, 1024 * 1024);
|
|
if (r) {
|
|
@@ -1237,6 +1311,8 @@ int r300_resume(struct radeon_device *rdev)
|
|
radeon_combios_asic_init(rdev->ddev);
|
|
/* Resume clock after posting */
|
|
r300_clock_startup(rdev);
|
|
+ /* Initialize surface registers */
|
|
+ radeon_surface_init(rdev);
|
|
return r300_startup(rdev);
|
|
}
|
|
|
|
@@ -1254,7 +1330,6 @@ int r300_suspend(struct radeon_device *rdev)
|
|
|
|
void r300_fini(struct radeon_device *rdev)
|
|
{
|
|
- r300_suspend(rdev);
|
|
r100_cp_fini(rdev);
|
|
r100_wb_fini(rdev);
|
|
r100_ib_fini(rdev);
|
|
@@ -1263,9 +1338,10 @@ void r300_fini(struct radeon_device *rdev)
|
|
rv370_pcie_gart_fini(rdev);
|
|
if (rdev->flags & RADEON_IS_PCI)
|
|
r100_pci_gart_fini(rdev);
|
|
+ radeon_agp_fini(rdev);
|
|
radeon_irq_kms_fini(rdev);
|
|
radeon_fence_driver_fini(rdev);
|
|
- radeon_object_fini(rdev);
|
|
+ radeon_bo_fini(rdev);
|
|
radeon_atombios_fini(rdev);
|
|
kfree(rdev->bios);
|
|
rdev->bios = NULL;
|
|
@@ -1303,14 +1379,14 @@ int r300_init(struct radeon_device *rdev)
|
|
RREG32(R_0007C0_CP_STAT));
|
|
}
|
|
/* check if cards are posted or not */
|
|
- if (!radeon_card_posted(rdev) && rdev->bios) {
|
|
- DRM_INFO("GPU not posted. posting now...\n");
|
|
- radeon_combios_asic_init(rdev->ddev);
|
|
- }
|
|
+ if (radeon_boot_test_post_card(rdev) == false)
|
|
+ return -EINVAL;
|
|
/* Set asic errata */
|
|
r300_errata(rdev);
|
|
/* Initialize clocks */
|
|
radeon_get_clock_info(rdev->ddev);
|
|
+ /* Initialize power management */
|
|
+ radeon_pm_init(rdev);
|
|
/* Get vram informations */
|
|
r300_vram_info(rdev);
|
|
/* Initialize memory controller (also test AGP) */
|
|
@@ -1325,7 +1401,7 @@ int r300_init(struct radeon_device *rdev)
|
|
if (r)
|
|
return r;
|
|
/* Memory manager */
|
|
- r = radeon_object_init(rdev);
|
|
+ r = radeon_bo_init(rdev);
|
|
if (r)
|
|
return r;
|
|
if (rdev->flags & RADEON_IS_PCIE) {
|
|
@@ -1344,15 +1420,15 @@ int r300_init(struct radeon_device *rdev)
|
|
if (r) {
|
|
/* Somethings want wront with the accel init stop accel */
|
|
dev_err(rdev->dev, "Disabling GPU acceleration\n");
|
|
- r300_suspend(rdev);
|
|
r100_cp_fini(rdev);
|
|
r100_wb_fini(rdev);
|
|
r100_ib_fini(rdev);
|
|
+ radeon_irq_kms_fini(rdev);
|
|
if (rdev->flags & RADEON_IS_PCIE)
|
|
rv370_pcie_gart_fini(rdev);
|
|
if (rdev->flags & RADEON_IS_PCI)
|
|
r100_pci_gart_fini(rdev);
|
|
- radeon_irq_kms_fini(rdev);
|
|
+ radeon_agp_fini(rdev);
|
|
rdev->accel_working = false;
|
|
}
|
|
return 0;
|
|
diff --git a/drivers/gpu/drm/radeon/r300_cmdbuf.c b/drivers/gpu/drm/radeon/r300_cmdbuf.c
|
|
index cb2e470..34bffa0 100644
|
|
--- a/drivers/gpu/drm/radeon/r300_cmdbuf.c
|
|
+++ b/drivers/gpu/drm/radeon/r300_cmdbuf.c
|
|
@@ -990,7 +990,7 @@ static inline int r300_emit_r500fp(drm_radeon_private_t *dev_priv,
|
|
int sz;
|
|
int addr;
|
|
int type;
|
|
- int clamp;
|
|
+ int isclamp;
|
|
int stride;
|
|
RING_LOCALS;
|
|
|
|
@@ -999,10 +999,10 @@ static inline int r300_emit_r500fp(drm_radeon_private_t *dev_priv,
|
|
addr = ((header.r500fp.adrhi_flags & 1) << 8) | header.r500fp.adrlo;
|
|
|
|
type = !!(header.r500fp.adrhi_flags & R500FP_CONSTANT_TYPE);
|
|
- clamp = !!(header.r500fp.adrhi_flags & R500FP_CONSTANT_CLAMP);
|
|
+ isclamp = !!(header.r500fp.adrhi_flags & R500FP_CONSTANT_CLAMP);
|
|
|
|
addr |= (type << 16);
|
|
- addr |= (clamp << 17);
|
|
+ addr |= (isclamp << 17);
|
|
|
|
stride = type ? 4 : 6;
|
|
|
|
diff --git a/drivers/gpu/drm/radeon/r300_reg.h b/drivers/gpu/drm/radeon/r300_reg.h
|
|
index 4b7afef..1735a2b 100644
|
|
--- a/drivers/gpu/drm/radeon/r300_reg.h
|
|
+++ b/drivers/gpu/drm/radeon/r300_reg.h
|
|
@@ -900,6 +900,7 @@
|
|
# define R300_TX_FORMAT_FL_I32 0x1B
|
|
# define R300_TX_FORMAT_FL_I32A32 0x1C
|
|
# define R300_TX_FORMAT_FL_R32G32B32A32 0x1D
|
|
+# define R300_TX_FORMAT_ATI2N 0x1F
|
|
/* alpha modes, convenience mostly */
|
|
/* if you have alpha, pick constant appropriate to the
|
|
number of channels (1 for I8, 2 for I8A8, 4 for R8G8B8A8, etc */
|
|
diff --git a/drivers/gpu/drm/radeon/r420.c b/drivers/gpu/drm/radeon/r420.c
|
|
index 1cefdbc..d937324 100644
|
|
--- a/drivers/gpu/drm/radeon/r420.c
|
|
+++ b/drivers/gpu/drm/radeon/r420.c
|
|
@@ -30,7 +30,15 @@
|
|
#include "radeon_reg.h"
|
|
#include "radeon.h"
|
|
#include "atom.h"
|
|
+#include "r100d.h"
|
|
#include "r420d.h"
|
|
+#include "r420_reg_safe.h"
|
|
+
|
|
+static void r420_set_reg_safe(struct radeon_device *rdev)
|
|
+{
|
|
+ rdev->config.r300.reg_safe_bm = r420_reg_safe_bm;
|
|
+ rdev->config.r300.reg_safe_bm_size = ARRAY_SIZE(r420_reg_safe_bm);
|
|
+}
|
|
|
|
int r420_mc_init(struct radeon_device *rdev)
|
|
{
|
|
@@ -42,9 +50,7 @@ int r420_mc_init(struct radeon_device *rdev)
|
|
if (rdev->flags & RADEON_IS_AGP) {
|
|
r = radeon_agp_init(rdev);
|
|
if (r) {
|
|
- printk(KERN_WARNING "[drm] Disabling AGP\n");
|
|
- rdev->flags &= ~RADEON_IS_AGP;
|
|
- rdev->mc.gtt_size = radeon_gart_size * 1024 * 1024;
|
|
+ radeon_agp_disable(rdev);
|
|
} else {
|
|
rdev->mc.gtt_location = rdev->mc.agp_base;
|
|
}
|
|
@@ -165,10 +171,41 @@ static void r420_clock_resume(struct radeon_device *rdev)
|
|
WREG32_PLL(R_00000D_SCLK_CNTL, sclk_cntl);
|
|
}
|
|
|
|
+static void r420_cp_errata_init(struct radeon_device *rdev)
|
|
+{
|
|
+ /* RV410 and R420 can lock up if CP DMA to host memory happens
|
|
+ * while the 2D engine is busy.
|
|
+ *
|
|
+ * The proper workaround is to queue a RESYNC at the beginning
|
|
+ * of the CP init, apparently.
|
|
+ */
|
|
+ radeon_scratch_get(rdev, &rdev->config.r300.resync_scratch);
|
|
+ radeon_ring_lock(rdev, 8);
|
|
+ radeon_ring_write(rdev, PACKET0(R300_CP_RESYNC_ADDR, 1));
|
|
+ radeon_ring_write(rdev, rdev->config.r300.resync_scratch);
|
|
+ radeon_ring_write(rdev, 0xDEADBEEF);
|
|
+ radeon_ring_unlock_commit(rdev);
|
|
+}
|
|
+
|
|
+static void r420_cp_errata_fini(struct radeon_device *rdev)
|
|
+{
|
|
+ /* Catch the RESYNC we dispatched all the way back,
|
|
+ * at the very beginning of the CP init.
|
|
+ */
|
|
+ radeon_ring_lock(rdev, 8);
|
|
+ radeon_ring_write(rdev, PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0));
|
|
+ radeon_ring_write(rdev, R300_RB3D_DC_FINISH);
|
|
+ radeon_ring_unlock_commit(rdev);
|
|
+ radeon_scratch_free(rdev, rdev->config.r300.resync_scratch);
|
|
+}
|
|
+
|
|
static int r420_startup(struct radeon_device *rdev)
|
|
{
|
|
int r;
|
|
|
|
+ /* set common regs */
|
|
+ r100_set_common_regs(rdev);
|
|
+ /* program mc */
|
|
r300_mc_program(rdev);
|
|
/* Resume clock */
|
|
r420_clock_resume(rdev);
|
|
@@ -186,14 +223,15 @@ static int r420_startup(struct radeon_device *rdev)
|
|
}
|
|
r420_pipes_init(rdev);
|
|
/* Enable IRQ */
|
|
- rdev->irq.sw_int = true;
|
|
r100_irq_set(rdev);
|
|
+ rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL);
|
|
/* 1M ring buffer */
|
|
r = r100_cp_init(rdev, 1024 * 1024);
|
|
if (r) {
|
|
dev_err(rdev->dev, "failled initializing CP (%d).\n", r);
|
|
return r;
|
|
}
|
|
+ r420_cp_errata_init(rdev);
|
|
r = r100_wb_init(rdev);
|
|
if (r) {
|
|
dev_err(rdev->dev, "failled initializing WB (%d).\n", r);
|
|
@@ -229,12 +267,14 @@ int r420_resume(struct radeon_device *rdev)
|
|
}
|
|
/* Resume clock after posting */
|
|
r420_clock_resume(rdev);
|
|
-
|
|
+ /* Initialize surface registers */
|
|
+ radeon_surface_init(rdev);
|
|
return r420_startup(rdev);
|
|
}
|
|
|
|
int r420_suspend(struct radeon_device *rdev)
|
|
{
|
|
+ r420_cp_errata_fini(rdev);
|
|
r100_cp_disable(rdev);
|
|
r100_wb_disable(rdev);
|
|
r100_irq_disable(rdev);
|
|
@@ -258,7 +298,7 @@ void r420_fini(struct radeon_device *rdev)
|
|
radeon_agp_fini(rdev);
|
|
radeon_irq_kms_fini(rdev);
|
|
radeon_fence_driver_fini(rdev);
|
|
- radeon_object_fini(rdev);
|
|
+ radeon_bo_fini(rdev);
|
|
if (rdev->is_atom_bios) {
|
|
radeon_atombios_fini(rdev);
|
|
} else {
|
|
@@ -301,14 +341,9 @@ int r420_init(struct radeon_device *rdev)
|
|
RREG32(R_0007C0_CP_STAT));
|
|
}
|
|
/* check if cards are posted or not */
|
|
- if (!radeon_card_posted(rdev) && rdev->bios) {
|
|
- DRM_INFO("GPU not posted. posting now...\n");
|
|
- if (rdev->is_atom_bios) {
|
|
- atom_asic_init(rdev->mode_info.atom_context);
|
|
- } else {
|
|
- radeon_combios_asic_init(rdev->ddev);
|
|
- }
|
|
- }
|
|
+ if (radeon_boot_test_post_card(rdev) == false)
|
|
+ return -EINVAL;
|
|
+
|
|
/* Initialize clocks */
|
|
radeon_get_clock_info(rdev->ddev);
|
|
/* Initialize power management */
|
|
@@ -331,10 +366,13 @@ int r420_init(struct radeon_device *rdev)
|
|
return r;
|
|
}
|
|
/* Memory manager */
|
|
- r = radeon_object_init(rdev);
|
|
+ r = radeon_bo_init(rdev);
|
|
if (r) {
|
|
return r;
|
|
}
|
|
+ if (rdev->family == CHIP_R420)
|
|
+ r100_enable_bm(rdev);
|
|
+
|
|
if (rdev->flags & RADEON_IS_PCIE) {
|
|
r = rv370_pcie_gart_init(rdev);
|
|
if (r)
|
|
@@ -345,22 +383,21 @@ int r420_init(struct radeon_device *rdev)
|
|
if (r)
|
|
return r;
|
|
}
|
|
- r300_set_reg_safe(rdev);
|
|
+ r420_set_reg_safe(rdev);
|
|
rdev->accel_working = true;
|
|
r = r420_startup(rdev);
|
|
if (r) {
|
|
/* Somethings want wront with the accel init stop accel */
|
|
dev_err(rdev->dev, "Disabling GPU acceleration\n");
|
|
- r420_suspend(rdev);
|
|
r100_cp_fini(rdev);
|
|
r100_wb_fini(rdev);
|
|
r100_ib_fini(rdev);
|
|
+ radeon_irq_kms_fini(rdev);
|
|
if (rdev->flags & RADEON_IS_PCIE)
|
|
rv370_pcie_gart_fini(rdev);
|
|
if (rdev->flags & RADEON_IS_PCI)
|
|
r100_pci_gart_fini(rdev);
|
|
radeon_agp_fini(rdev);
|
|
- radeon_irq_kms_fini(rdev);
|
|
rdev->accel_working = false;
|
|
}
|
|
return 0;
|
|
diff --git a/drivers/gpu/drm/radeon/r500_reg.h b/drivers/gpu/drm/radeon/r500_reg.h
|
|
index 7baa739..74ad89b 100644
|
|
--- a/drivers/gpu/drm/radeon/r500_reg.h
|
|
+++ b/drivers/gpu/drm/radeon/r500_reg.h
|
|
@@ -716,6 +716,8 @@
|
|
|
|
#define AVIVO_DVOA_BIT_DEPTH_CONTROL 0x7988
|
|
|
|
+#define AVIVO_DC_GPIO_HPD_A 0x7e94
|
|
+
|
|
#define AVIVO_GPIO_0 0x7e30
|
|
#define AVIVO_GPIO_1 0x7e40
|
|
#define AVIVO_GPIO_2 0x7e50
|
|
diff --git a/drivers/gpu/drm/radeon/r520.c b/drivers/gpu/drm/radeon/r520.c
|
|
index f743518..ddf5731 100644
|
|
--- a/drivers/gpu/drm/radeon/r520.c
|
|
+++ b/drivers/gpu/drm/radeon/r520.c
|
|
@@ -185,8 +185,8 @@ static int r520_startup(struct radeon_device *rdev)
|
|
return r;
|
|
}
|
|
/* Enable IRQ */
|
|
- rdev->irq.sw_int = true;
|
|
rs600_irq_set(rdev);
|
|
+ rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL);
|
|
/* 1M ring buffer */
|
|
r = r100_cp_init(rdev, 1024 * 1024);
|
|
if (r) {
|
|
@@ -221,6 +221,8 @@ int r520_resume(struct radeon_device *rdev)
|
|
atom_asic_init(rdev->mode_info.atom_context);
|
|
/* Resume clock after posting */
|
|
rv515_clock_startup(rdev);
|
|
+ /* Initialize surface registers */
|
|
+ radeon_surface_init(rdev);
|
|
return r520_startup(rdev);
|
|
}
|
|
|
|
@@ -254,6 +256,9 @@ int r520_init(struct radeon_device *rdev)
|
|
RREG32(R_0007C0_CP_STAT));
|
|
}
|
|
/* check if cards are posted or not */
|
|
+ if (radeon_boot_test_post_card(rdev) == false)
|
|
+ return -EINVAL;
|
|
+
|
|
if (!radeon_card_posted(rdev) && rdev->bios) {
|
|
DRM_INFO("GPU not posted. posting now...\n");
|
|
atom_asic_init(rdev->mode_info.atom_context);
|
|
@@ -277,7 +282,7 @@ int r520_init(struct radeon_device *rdev)
|
|
if (r)
|
|
return r;
|
|
/* Memory manager */
|
|
- r = radeon_object_init(rdev);
|
|
+ r = radeon_bo_init(rdev);
|
|
if (r)
|
|
return r;
|
|
r = rv370_pcie_gart_init(rdev);
|
|
@@ -289,13 +294,12 @@ int r520_init(struct radeon_device *rdev)
|
|
if (r) {
|
|
/* Somethings want wront with the accel init stop accel */
|
|
dev_err(rdev->dev, "Disabling GPU acceleration\n");
|
|
- rv515_suspend(rdev);
|
|
r100_cp_fini(rdev);
|
|
r100_wb_fini(rdev);
|
|
r100_ib_fini(rdev);
|
|
+ radeon_irq_kms_fini(rdev);
|
|
rv370_pcie_gart_fini(rdev);
|
|
radeon_agp_fini(rdev);
|
|
- radeon_irq_kms_fini(rdev);
|
|
rdev->accel_working = false;
|
|
}
|
|
return 0;
|
|
diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c
|
|
index 278f646..91f5af9 100644
|
|
--- a/drivers/gpu/drm/radeon/r600.c
|
|
+++ b/drivers/gpu/drm/radeon/r600.c
|
|
@@ -38,8 +38,10 @@
|
|
|
|
#define PFP_UCODE_SIZE 576
|
|
#define PM4_UCODE_SIZE 1792
|
|
+#define RLC_UCODE_SIZE 768
|
|
#define R700_PFP_UCODE_SIZE 848
|
|
#define R700_PM4_UCODE_SIZE 1360
|
|
+#define R700_RLC_UCODE_SIZE 1024
|
|
|
|
/* Firmware Names */
|
|
MODULE_FIRMWARE("radeon/R600_pfp.bin");
|
|
@@ -62,6 +64,8 @@ MODULE_FIRMWARE("radeon/RV730_pfp.bin");
|
|
MODULE_FIRMWARE("radeon/RV730_me.bin");
|
|
MODULE_FIRMWARE("radeon/RV710_pfp.bin");
|
|
MODULE_FIRMWARE("radeon/RV710_me.bin");
|
|
+MODULE_FIRMWARE("radeon/R600_rlc.bin");
|
|
+MODULE_FIRMWARE("radeon/R700_rlc.bin");
|
|
|
|
int r600_debugfs_mc_info_init(struct radeon_device *rdev);
|
|
|
|
@@ -70,6 +74,282 @@ int r600_mc_wait_for_idle(struct radeon_device *rdev);
|
|
void r600_gpu_init(struct radeon_device *rdev);
|
|
void r600_fini(struct radeon_device *rdev);
|
|
|
|
+/* hpd for digital panel detect/disconnect */
|
|
+bool r600_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd)
|
|
+{
|
|
+ bool connected = false;
|
|
+
|
|
+ if (ASIC_IS_DCE3(rdev)) {
|
|
+ switch (hpd) {
|
|
+ case RADEON_HPD_1:
|
|
+ if (RREG32(DC_HPD1_INT_STATUS) & DC_HPDx_SENSE)
|
|
+ connected = true;
|
|
+ break;
|
|
+ case RADEON_HPD_2:
|
|
+ if (RREG32(DC_HPD2_INT_STATUS) & DC_HPDx_SENSE)
|
|
+ connected = true;
|
|
+ break;
|
|
+ case RADEON_HPD_3:
|
|
+ if (RREG32(DC_HPD3_INT_STATUS) & DC_HPDx_SENSE)
|
|
+ connected = true;
|
|
+ break;
|
|
+ case RADEON_HPD_4:
|
|
+ if (RREG32(DC_HPD4_INT_STATUS) & DC_HPDx_SENSE)
|
|
+ connected = true;
|
|
+ break;
|
|
+ /* DCE 3.2 */
|
|
+ case RADEON_HPD_5:
|
|
+ if (RREG32(DC_HPD5_INT_STATUS) & DC_HPDx_SENSE)
|
|
+ connected = true;
|
|
+ break;
|
|
+ case RADEON_HPD_6:
|
|
+ if (RREG32(DC_HPD6_INT_STATUS) & DC_HPDx_SENSE)
|
|
+ connected = true;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ } else {
|
|
+ switch (hpd) {
|
|
+ case RADEON_HPD_1:
|
|
+ if (RREG32(DC_HOT_PLUG_DETECT1_INT_STATUS) & DC_HOT_PLUG_DETECTx_SENSE)
|
|
+ connected = true;
|
|
+ break;
|
|
+ case RADEON_HPD_2:
|
|
+ if (RREG32(DC_HOT_PLUG_DETECT2_INT_STATUS) & DC_HOT_PLUG_DETECTx_SENSE)
|
|
+ connected = true;
|
|
+ break;
|
|
+ case RADEON_HPD_3:
|
|
+ if (RREG32(DC_HOT_PLUG_DETECT3_INT_STATUS) & DC_HOT_PLUG_DETECTx_SENSE)
|
|
+ connected = true;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return connected;
|
|
+}
|
|
+
|
|
+void r600_hpd_set_polarity(struct radeon_device *rdev,
|
|
+ enum radeon_hpd_id hpd)
|
|
+{
|
|
+ u32 tmp;
|
|
+ bool connected = r600_hpd_sense(rdev, hpd);
|
|
+
|
|
+ if (ASIC_IS_DCE3(rdev)) {
|
|
+ switch (hpd) {
|
|
+ case RADEON_HPD_1:
|
|
+ tmp = RREG32(DC_HPD1_INT_CONTROL);
|
|
+ if (connected)
|
|
+ tmp &= ~DC_HPDx_INT_POLARITY;
|
|
+ else
|
|
+ tmp |= DC_HPDx_INT_POLARITY;
|
|
+ WREG32(DC_HPD1_INT_CONTROL, tmp);
|
|
+ break;
|
|
+ case RADEON_HPD_2:
|
|
+ tmp = RREG32(DC_HPD2_INT_CONTROL);
|
|
+ if (connected)
|
|
+ tmp &= ~DC_HPDx_INT_POLARITY;
|
|
+ else
|
|
+ tmp |= DC_HPDx_INT_POLARITY;
|
|
+ WREG32(DC_HPD2_INT_CONTROL, tmp);
|
|
+ break;
|
|
+ case RADEON_HPD_3:
|
|
+ tmp = RREG32(DC_HPD3_INT_CONTROL);
|
|
+ if (connected)
|
|
+ tmp &= ~DC_HPDx_INT_POLARITY;
|
|
+ else
|
|
+ tmp |= DC_HPDx_INT_POLARITY;
|
|
+ WREG32(DC_HPD3_INT_CONTROL, tmp);
|
|
+ break;
|
|
+ case RADEON_HPD_4:
|
|
+ tmp = RREG32(DC_HPD4_INT_CONTROL);
|
|
+ if (connected)
|
|
+ tmp &= ~DC_HPDx_INT_POLARITY;
|
|
+ else
|
|
+ tmp |= DC_HPDx_INT_POLARITY;
|
|
+ WREG32(DC_HPD4_INT_CONTROL, tmp);
|
|
+ break;
|
|
+ case RADEON_HPD_5:
|
|
+ tmp = RREG32(DC_HPD5_INT_CONTROL);
|
|
+ if (connected)
|
|
+ tmp &= ~DC_HPDx_INT_POLARITY;
|
|
+ else
|
|
+ tmp |= DC_HPDx_INT_POLARITY;
|
|
+ WREG32(DC_HPD5_INT_CONTROL, tmp);
|
|
+ break;
|
|
+ /* DCE 3.2 */
|
|
+ case RADEON_HPD_6:
|
|
+ tmp = RREG32(DC_HPD6_INT_CONTROL);
|
|
+ if (connected)
|
|
+ tmp &= ~DC_HPDx_INT_POLARITY;
|
|
+ else
|
|
+ tmp |= DC_HPDx_INT_POLARITY;
|
|
+ WREG32(DC_HPD6_INT_CONTROL, tmp);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ } else {
|
|
+ switch (hpd) {
|
|
+ case RADEON_HPD_1:
|
|
+ tmp = RREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL);
|
|
+ if (connected)
|
|
+ tmp &= ~DC_HOT_PLUG_DETECTx_INT_POLARITY;
|
|
+ else
|
|
+ tmp |= DC_HOT_PLUG_DETECTx_INT_POLARITY;
|
|
+ WREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL, tmp);
|
|
+ break;
|
|
+ case RADEON_HPD_2:
|
|
+ tmp = RREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL);
|
|
+ if (connected)
|
|
+ tmp &= ~DC_HOT_PLUG_DETECTx_INT_POLARITY;
|
|
+ else
|
|
+ tmp |= DC_HOT_PLUG_DETECTx_INT_POLARITY;
|
|
+ WREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL, tmp);
|
|
+ break;
|
|
+ case RADEON_HPD_3:
|
|
+ tmp = RREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL);
|
|
+ if (connected)
|
|
+ tmp &= ~DC_HOT_PLUG_DETECTx_INT_POLARITY;
|
|
+ else
|
|
+ tmp |= DC_HOT_PLUG_DETECTx_INT_POLARITY;
|
|
+ WREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL, tmp);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void r600_hpd_init(struct radeon_device *rdev)
|
|
+{
|
|
+ struct drm_device *dev = rdev->ddev;
|
|
+ struct drm_connector *connector;
|
|
+
|
|
+ if (ASIC_IS_DCE3(rdev)) {
|
|
+ u32 tmp = DC_HPDx_CONNECTION_TIMER(0x9c4) | DC_HPDx_RX_INT_TIMER(0xfa);
|
|
+ if (ASIC_IS_DCE32(rdev))
|
|
+ tmp |= DC_HPDx_EN;
|
|
+
|
|
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
|
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
|
|
+ switch (radeon_connector->hpd.hpd) {
|
|
+ case RADEON_HPD_1:
|
|
+ WREG32(DC_HPD1_CONTROL, tmp);
|
|
+ rdev->irq.hpd[0] = true;
|
|
+ break;
|
|
+ case RADEON_HPD_2:
|
|
+ WREG32(DC_HPD2_CONTROL, tmp);
|
|
+ rdev->irq.hpd[1] = true;
|
|
+ break;
|
|
+ case RADEON_HPD_3:
|
|
+ WREG32(DC_HPD3_CONTROL, tmp);
|
|
+ rdev->irq.hpd[2] = true;
|
|
+ break;
|
|
+ case RADEON_HPD_4:
|
|
+ WREG32(DC_HPD4_CONTROL, tmp);
|
|
+ rdev->irq.hpd[3] = true;
|
|
+ break;
|
|
+ /* DCE 3.2 */
|
|
+ case RADEON_HPD_5:
|
|
+ WREG32(DC_HPD5_CONTROL, tmp);
|
|
+ rdev->irq.hpd[4] = true;
|
|
+ break;
|
|
+ case RADEON_HPD_6:
|
|
+ WREG32(DC_HPD6_CONTROL, tmp);
|
|
+ rdev->irq.hpd[5] = true;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
|
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
|
|
+ switch (radeon_connector->hpd.hpd) {
|
|
+ case RADEON_HPD_1:
|
|
+ WREG32(DC_HOT_PLUG_DETECT1_CONTROL, DC_HOT_PLUG_DETECTx_EN);
|
|
+ rdev->irq.hpd[0] = true;
|
|
+ break;
|
|
+ case RADEON_HPD_2:
|
|
+ WREG32(DC_HOT_PLUG_DETECT2_CONTROL, DC_HOT_PLUG_DETECTx_EN);
|
|
+ rdev->irq.hpd[1] = true;
|
|
+ break;
|
|
+ case RADEON_HPD_3:
|
|
+ WREG32(DC_HOT_PLUG_DETECT3_CONTROL, DC_HOT_PLUG_DETECTx_EN);
|
|
+ rdev->irq.hpd[2] = true;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ if (rdev->irq.installed)
|
|
+ r600_irq_set(rdev);
|
|
+}
|
|
+
|
|
+void r600_hpd_fini(struct radeon_device *rdev)
|
|
+{
|
|
+ struct drm_device *dev = rdev->ddev;
|
|
+ struct drm_connector *connector;
|
|
+
|
|
+ if (ASIC_IS_DCE3(rdev)) {
|
|
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
|
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
|
|
+ switch (radeon_connector->hpd.hpd) {
|
|
+ case RADEON_HPD_1:
|
|
+ WREG32(DC_HPD1_CONTROL, 0);
|
|
+ rdev->irq.hpd[0] = false;
|
|
+ break;
|
|
+ case RADEON_HPD_2:
|
|
+ WREG32(DC_HPD2_CONTROL, 0);
|
|
+ rdev->irq.hpd[1] = false;
|
|
+ break;
|
|
+ case RADEON_HPD_3:
|
|
+ WREG32(DC_HPD3_CONTROL, 0);
|
|
+ rdev->irq.hpd[2] = false;
|
|
+ break;
|
|
+ case RADEON_HPD_4:
|
|
+ WREG32(DC_HPD4_CONTROL, 0);
|
|
+ rdev->irq.hpd[3] = false;
|
|
+ break;
|
|
+ /* DCE 3.2 */
|
|
+ case RADEON_HPD_5:
|
|
+ WREG32(DC_HPD5_CONTROL, 0);
|
|
+ rdev->irq.hpd[4] = false;
|
|
+ break;
|
|
+ case RADEON_HPD_6:
|
|
+ WREG32(DC_HPD6_CONTROL, 0);
|
|
+ rdev->irq.hpd[5] = false;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
|
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
|
|
+ switch (radeon_connector->hpd.hpd) {
|
|
+ case RADEON_HPD_1:
|
|
+ WREG32(DC_HOT_PLUG_DETECT1_CONTROL, 0);
|
|
+ rdev->irq.hpd[0] = false;
|
|
+ break;
|
|
+ case RADEON_HPD_2:
|
|
+ WREG32(DC_HOT_PLUG_DETECT2_CONTROL, 0);
|
|
+ rdev->irq.hpd[1] = false;
|
|
+ break;
|
|
+ case RADEON_HPD_3:
|
|
+ WREG32(DC_HOT_PLUG_DETECT3_CONTROL, 0);
|
|
+ rdev->irq.hpd[2] = false;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
/*
|
|
* R600 PCIE GART
|
|
*/
|
|
@@ -90,6 +370,9 @@ void r600_pcie_gart_tlb_flush(struct radeon_device *rdev)
|
|
unsigned i;
|
|
u32 tmp;
|
|
|
|
+ /* flush hdp cache so updates hit vram */
|
|
+ WREG32(R_005480_HDP_MEM_COHERENCY_FLUSH_CNTL, 0x1);
|
|
+
|
|
WREG32(VM_CONTEXT0_INVALIDATION_LOW_ADDR, rdev->mc.gtt_start >> 12);
|
|
WREG32(VM_CONTEXT0_INVALIDATION_HIGH_ADDR, (rdev->mc.gtt_end - 1) >> 12);
|
|
WREG32(VM_CONTEXT0_REQUEST_RESPONSE, REQUEST_TYPE(1));
|
|
@@ -180,7 +463,7 @@ int r600_pcie_gart_enable(struct radeon_device *rdev)
|
|
void r600_pcie_gart_disable(struct radeon_device *rdev)
|
|
{
|
|
u32 tmp;
|
|
- int i;
|
|
+ int i, r;
|
|
|
|
/* Disable all tables */
|
|
for (i = 0; i < 7; i++)
|
|
@@ -208,8 +491,12 @@ void r600_pcie_gart_disable(struct radeon_device *rdev)
|
|
WREG32(MC_VM_L1_TLB_MCB_RD_HDP_CNTL, tmp);
|
|
WREG32(MC_VM_L1_TLB_MCB_WR_HDP_CNTL, tmp);
|
|
if (rdev->gart.table.vram.robj) {
|
|
- radeon_object_kunmap(rdev->gart.table.vram.robj);
|
|
- radeon_object_unpin(rdev->gart.table.vram.robj);
|
|
+ r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
|
|
+ if (likely(r == 0)) {
|
|
+ radeon_bo_kunmap(rdev->gart.table.vram.robj);
|
|
+ radeon_bo_unpin(rdev->gart.table.vram.robj);
|
|
+ radeon_bo_unreserve(rdev->gart.table.vram.robj);
|
|
+ }
|
|
}
|
|
}
|
|
|
|
@@ -340,7 +627,6 @@ int r600_mc_init(struct radeon_device *rdev)
|
|
fixed20_12 a;
|
|
u32 tmp;
|
|
int chansize, numchan;
|
|
- int r;
|
|
|
|
/* Get VRAM informations */
|
|
rdev->mc.vram_is_ddr = true;
|
|
@@ -383,9 +669,6 @@ int r600_mc_init(struct radeon_device *rdev)
|
|
rdev->mc.real_vram_size = rdev->mc.aper_size;
|
|
|
|
if (rdev->flags & RADEON_IS_AGP) {
|
|
- r = radeon_agp_init(rdev);
|
|
- if (r)
|
|
- return r;
|
|
/* gtt_size is setup by radeon_agp_init */
|
|
rdev->mc.gtt_location = rdev->mc.agp_base;
|
|
tmp = 0xFFFFFFFFUL - rdev->mc.agp_base - rdev->mc.gtt_size;
|
|
@@ -394,11 +677,11 @@ int r600_mc_init(struct radeon_device *rdev)
|
|
* AGP so that GPU can catch out of VRAM/AGP access
|
|
*/
|
|
if (rdev->mc.gtt_location > rdev->mc.mc_vram_size) {
|
|
- /* Enought place before */
|
|
+ /* Enough place before */
|
|
rdev->mc.vram_location = rdev->mc.gtt_location -
|
|
rdev->mc.mc_vram_size;
|
|
} else if (tmp > rdev->mc.mc_vram_size) {
|
|
- /* Enought place after */
|
|
+ /* Enough place after */
|
|
rdev->mc.vram_location = rdev->mc.gtt_location +
|
|
rdev->mc.gtt_size;
|
|
} else {
|
|
@@ -443,6 +726,10 @@ int r600_mc_init(struct radeon_device *rdev)
|
|
a.full = rfixed_const(100);
|
|
rdev->pm.sclk.full = rfixed_const(rdev->clock.default_sclk);
|
|
rdev->pm.sclk.full = rfixed_div(rdev->pm.sclk, a);
|
|
+
|
|
+ if (rdev->flags & RADEON_IS_IGP)
|
|
+ rdev->mc.igp_sideport_enabled = radeon_atombios_sideport_present(rdev);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
@@ -1101,7 +1388,6 @@ void r600_pciep_wreg(struct radeon_device *rdev, u32 reg, u32 v)
|
|
(void)RREG32(PCIE_PORT_DATA);
|
|
}
|
|
|
|
-
|
|
/*
|
|
* CP & Ring
|
|
*/
|
|
@@ -1110,11 +1396,12 @@ void r600_cp_stop(struct radeon_device *rdev)
|
|
WREG32(R_0086D8_CP_ME_CNTL, S_0086D8_CP_ME_HALT(1));
|
|
}
|
|
|
|
-int r600_cp_init_microcode(struct radeon_device *rdev)
|
|
+int r600_init_microcode(struct radeon_device *rdev)
|
|
{
|
|
struct platform_device *pdev;
|
|
const char *chip_name;
|
|
- size_t pfp_req_size, me_req_size;
|
|
+ const char *rlc_chip_name;
|
|
+ size_t pfp_req_size, me_req_size, rlc_req_size;
|
|
char fw_name[30];
|
|
int err;
|
|
|
|
@@ -1128,30 +1415,62 @@ int r600_cp_init_microcode(struct radeon_device *rdev)
|
|
}
|
|
|
|
switch (rdev->family) {
|
|
- case CHIP_R600: chip_name = "R600"; break;
|
|
- case CHIP_RV610: chip_name = "RV610"; break;
|
|
- case CHIP_RV630: chip_name = "RV630"; break;
|
|
- case CHIP_RV620: chip_name = "RV620"; break;
|
|
- case CHIP_RV635: chip_name = "RV635"; break;
|
|
- case CHIP_RV670: chip_name = "RV670"; break;
|
|
+ case CHIP_R600:
|
|
+ chip_name = "R600";
|
|
+ rlc_chip_name = "R600";
|
|
+ break;
|
|
+ case CHIP_RV610:
|
|
+ chip_name = "RV610";
|
|
+ rlc_chip_name = "R600";
|
|
+ break;
|
|
+ case CHIP_RV630:
|
|
+ chip_name = "RV630";
|
|
+ rlc_chip_name = "R600";
|
|
+ break;
|
|
+ case CHIP_RV620:
|
|
+ chip_name = "RV620";
|
|
+ rlc_chip_name = "R600";
|
|
+ break;
|
|
+ case CHIP_RV635:
|
|
+ chip_name = "RV635";
|
|
+ rlc_chip_name = "R600";
|
|
+ break;
|
|
+ case CHIP_RV670:
|
|
+ chip_name = "RV670";
|
|
+ rlc_chip_name = "R600";
|
|
+ break;
|
|
case CHIP_RS780:
|
|
- case CHIP_RS880: chip_name = "RS780"; break;
|
|
- case CHIP_RV770: chip_name = "RV770"; break;
|
|
+ case CHIP_RS880:
|
|
+ chip_name = "RS780";
|
|
+ rlc_chip_name = "R600";
|
|
+ break;
|
|
+ case CHIP_RV770:
|
|
+ chip_name = "RV770";
|
|
+ rlc_chip_name = "R700";
|
|
+ break;
|
|
case CHIP_RV730:
|
|
- case CHIP_RV740: chip_name = "RV730"; break;
|
|
- case CHIP_RV710: chip_name = "RV710"; break;
|
|
+ case CHIP_RV740:
|
|
+ chip_name = "RV730";
|
|
+ rlc_chip_name = "R700";
|
|
+ break;
|
|
+ case CHIP_RV710:
|
|
+ chip_name = "RV710";
|
|
+ rlc_chip_name = "R700";
|
|
+ break;
|
|
default: BUG();
|
|
}
|
|
|
|
if (rdev->family >= CHIP_RV770) {
|
|
pfp_req_size = R700_PFP_UCODE_SIZE * 4;
|
|
me_req_size = R700_PM4_UCODE_SIZE * 4;
|
|
+ rlc_req_size = R700_RLC_UCODE_SIZE * 4;
|
|
} else {
|
|
pfp_req_size = PFP_UCODE_SIZE * 4;
|
|
me_req_size = PM4_UCODE_SIZE * 12;
|
|
+ rlc_req_size = RLC_UCODE_SIZE * 4;
|
|
}
|
|
|
|
- DRM_INFO("Loading %s CP Microcode\n", chip_name);
|
|
+ DRM_INFO("Loading %s Microcode\n", chip_name);
|
|
|
|
snprintf(fw_name, sizeof(fw_name), "radeon/%s_pfp.bin", chip_name);
|
|
err = request_firmware(&rdev->pfp_fw, fw_name, &pdev->dev);
|
|
@@ -1175,6 +1494,18 @@ int r600_cp_init_microcode(struct radeon_device *rdev)
|
|
rdev->me_fw->size, fw_name);
|
|
err = -EINVAL;
|
|
}
|
|
+
|
|
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_rlc.bin", rlc_chip_name);
|
|
+ err = request_firmware(&rdev->rlc_fw, fw_name, &pdev->dev);
|
|
+ if (err)
|
|
+ goto out;
|
|
+ if (rdev->rlc_fw->size != rlc_req_size) {
|
|
+ printk(KERN_ERR
|
|
+ "r600_rlc: Bogus length %zu in firmware \"%s\"\n",
|
|
+ rdev->rlc_fw->size, fw_name);
|
|
+ err = -EINVAL;
|
|
+ }
|
|
+
|
|
out:
|
|
platform_device_unregister(pdev);
|
|
|
|
@@ -1187,6 +1518,8 @@ out:
|
|
rdev->pfp_fw = NULL;
|
|
release_firmware(rdev->me_fw);
|
|
rdev->me_fw = NULL;
|
|
+ release_firmware(rdev->rlc_fw);
|
|
+ rdev->rlc_fw = NULL;
|
|
}
|
|
return err;
|
|
}
|
|
@@ -1324,6 +1657,12 @@ void r600_ring_init(struct radeon_device *rdev, unsigned ring_size)
|
|
rdev->cp.align_mask = 16 - 1;
|
|
}
|
|
|
|
+void r600_cp_fini(struct radeon_device *rdev)
|
|
+{
|
|
+ r600_cp_stop(rdev);
|
|
+ radeon_ring_fini(rdev);
|
|
+}
|
|
+
|
|
|
|
/*
|
|
* GPU scratch registers helpers function.
|
|
@@ -1381,10 +1720,16 @@ int r600_ring_test(struct radeon_device *rdev)
|
|
|
|
void r600_wb_disable(struct radeon_device *rdev)
|
|
{
|
|
+ int r;
|
|
+
|
|
WREG32(SCRATCH_UMSK, 0);
|
|
if (rdev->wb.wb_obj) {
|
|
- radeon_object_kunmap(rdev->wb.wb_obj);
|
|
- radeon_object_unpin(rdev->wb.wb_obj);
|
|
+ r = radeon_bo_reserve(rdev->wb.wb_obj, false);
|
|
+ if (unlikely(r != 0))
|
|
+ return;
|
|
+ radeon_bo_kunmap(rdev->wb.wb_obj);
|
|
+ radeon_bo_unpin(rdev->wb.wb_obj);
|
|
+ radeon_bo_unreserve(rdev->wb.wb_obj);
|
|
}
|
|
}
|
|
|
|
@@ -1392,7 +1737,7 @@ void r600_wb_fini(struct radeon_device *rdev)
|
|
{
|
|
r600_wb_disable(rdev);
|
|
if (rdev->wb.wb_obj) {
|
|
- radeon_object_unref(&rdev->wb.wb_obj);
|
|
+ radeon_bo_unref(&rdev->wb.wb_obj);
|
|
rdev->wb.wb = NULL;
|
|
rdev->wb.wb_obj = NULL;
|
|
}
|
|
@@ -1403,22 +1748,29 @@ int r600_wb_enable(struct radeon_device *rdev)
|
|
int r;
|
|
|
|
if (rdev->wb.wb_obj == NULL) {
|
|
- r = radeon_object_create(rdev, NULL, RADEON_GPU_PAGE_SIZE, true,
|
|
- RADEON_GEM_DOMAIN_GTT, false, &rdev->wb.wb_obj);
|
|
+ r = radeon_bo_create(rdev, NULL, RADEON_GPU_PAGE_SIZE, true,
|
|
+ RADEON_GEM_DOMAIN_GTT, &rdev->wb.wb_obj);
|
|
if (r) {
|
|
- dev_warn(rdev->dev, "failed to create WB buffer (%d).\n", r);
|
|
+ dev_warn(rdev->dev, "(%d) create WB bo failed\n", r);
|
|
+ return r;
|
|
+ }
|
|
+ r = radeon_bo_reserve(rdev->wb.wb_obj, false);
|
|
+ if (unlikely(r != 0)) {
|
|
+ r600_wb_fini(rdev);
|
|
return r;
|
|
}
|
|
- r = radeon_object_pin(rdev->wb.wb_obj, RADEON_GEM_DOMAIN_GTT,
|
|
+ r = radeon_bo_pin(rdev->wb.wb_obj, RADEON_GEM_DOMAIN_GTT,
|
|
&rdev->wb.gpu_addr);
|
|
if (r) {
|
|
- dev_warn(rdev->dev, "failed to pin WB buffer (%d).\n", r);
|
|
+ radeon_bo_unreserve(rdev->wb.wb_obj);
|
|
+ dev_warn(rdev->dev, "(%d) pin WB bo failed\n", r);
|
|
r600_wb_fini(rdev);
|
|
return r;
|
|
}
|
|
- r = radeon_object_kmap(rdev->wb.wb_obj, (void **)&rdev->wb.wb);
|
|
+ r = radeon_bo_kmap(rdev->wb.wb_obj, (void **)&rdev->wb.wb);
|
|
+ radeon_bo_unreserve(rdev->wb.wb_obj);
|
|
if (r) {
|
|
- dev_warn(rdev->dev, "failed to map WB buffer (%d).\n", r);
|
|
+ dev_warn(rdev->dev, "(%d) map WB bo failed\n", r);
|
|
r600_wb_fini(rdev);
|
|
return r;
|
|
}
|
|
@@ -1433,41 +1785,43 @@ int r600_wb_enable(struct radeon_device *rdev)
|
|
void r600_fence_ring_emit(struct radeon_device *rdev,
|
|
struct radeon_fence *fence)
|
|
{
|
|
+ /* Also consider EVENT_WRITE_EOP. it handles the interrupts + timestamps + events */
|
|
+
|
|
+ radeon_ring_write(rdev, PACKET3(PACKET3_EVENT_WRITE, 0));
|
|
+ radeon_ring_write(rdev, CACHE_FLUSH_AND_INV_EVENT);
|
|
+ /* wait for 3D idle clean */
|
|
+ radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONFIG_REG, 1));
|
|
+ radeon_ring_write(rdev, (WAIT_UNTIL - PACKET3_SET_CONFIG_REG_OFFSET) >> 2);
|
|
+ radeon_ring_write(rdev, WAIT_3D_IDLE_bit | WAIT_3D_IDLECLEAN_bit);
|
|
/* Emit fence sequence & fire IRQ */
|
|
radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONFIG_REG, 1));
|
|
radeon_ring_write(rdev, ((rdev->fence_drv.scratch_reg - PACKET3_SET_CONFIG_REG_OFFSET) >> 2));
|
|
radeon_ring_write(rdev, fence->seq);
|
|
-}
|
|
-
|
|
-int r600_copy_dma(struct radeon_device *rdev,
|
|
- uint64_t src_offset,
|
|
- uint64_t dst_offset,
|
|
- unsigned num_pages,
|
|
- struct radeon_fence *fence)
|
|
-{
|
|
- /* FIXME: implement */
|
|
- return 0;
|
|
+ radeon_ring_write(rdev, PACKET0(R_005480_HDP_MEM_COHERENCY_FLUSH_CNTL, 0));
|
|
+ radeon_ring_write(rdev, 1);
|
|
+ /* CP_INTERRUPT packet 3 no longer exists, use packet 0 */
|
|
+ radeon_ring_write(rdev, PACKET0(CP_INT_STATUS, 0));
|
|
+ radeon_ring_write(rdev, RB_INT_STAT);
|
|
}
|
|
|
|
int r600_copy_blit(struct radeon_device *rdev,
|
|
uint64_t src_offset, uint64_t dst_offset,
|
|
unsigned num_pages, struct radeon_fence *fence)
|
|
{
|
|
- r600_blit_prepare_copy(rdev, num_pages * RADEON_GPU_PAGE_SIZE);
|
|
+ int r;
|
|
+
|
|
+ mutex_lock(&rdev->r600_blit.mutex);
|
|
+ rdev->r600_blit.vb_ib = NULL;
|
|
+ r = r600_blit_prepare_copy(rdev, num_pages * RADEON_GPU_PAGE_SIZE);
|
|
+ if (r) {
|
|
+ if (rdev->r600_blit.vb_ib)
|
|
+ radeon_ib_free(rdev, &rdev->r600_blit.vb_ib);
|
|
+ mutex_unlock(&rdev->r600_blit.mutex);
|
|
+ return r;
|
|
+ }
|
|
r600_kms_blit_copy(rdev, src_offset, dst_offset, num_pages * RADEON_GPU_PAGE_SIZE);
|
|
r600_blit_done_copy(rdev, fence);
|
|
- return 0;
|
|
-}
|
|
-
|
|
-int r600_irq_process(struct radeon_device *rdev)
|
|
-{
|
|
- /* FIXME: implement */
|
|
- return 0;
|
|
-}
|
|
-
|
|
-int r600_irq_set(struct radeon_device *rdev)
|
|
-{
|
|
- /* FIXME: implement */
|
|
+ mutex_unlock(&rdev->r600_blit.mutex);
|
|
return 0;
|
|
}
|
|
|
|
@@ -1506,6 +1860,14 @@ int r600_startup(struct radeon_device *rdev)
|
|
{
|
|
int r;
|
|
|
|
+ if (!rdev->me_fw || !rdev->pfp_fw || !rdev->rlc_fw) {
|
|
+ r = r600_init_microcode(rdev);
|
|
+ if (r) {
|
|
+ DRM_ERROR("Failed to load firmware!\n");
|
|
+ return r;
|
|
+ }
|
|
+ }
|
|
+
|
|
r600_mc_program(rdev);
|
|
if (rdev->flags & RADEON_IS_AGP) {
|
|
r600_agp_enable(rdev);
|
|
@@ -1515,13 +1877,33 @@ int r600_startup(struct radeon_device *rdev)
|
|
return r;
|
|
}
|
|
r600_gpu_init(rdev);
|
|
-
|
|
- r = radeon_object_pin(rdev->r600_blit.shader_obj, RADEON_GEM_DOMAIN_VRAM,
|
|
- &rdev->r600_blit.shader_gpu_addr);
|
|
+ r = r600_blit_init(rdev);
|
|
if (r) {
|
|
- DRM_ERROR("failed to pin blit object %d\n", r);
|
|
+ r600_blit_fini(rdev);
|
|
+ rdev->asic->copy = NULL;
|
|
+ dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r);
|
|
+ }
|
|
+ /* pin copy shader into vram */
|
|
+ if (rdev->r600_blit.shader_obj) {
|
|
+ r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
|
|
+ if (unlikely(r != 0))
|
|
+ return r;
|
|
+ r = radeon_bo_pin(rdev->r600_blit.shader_obj, RADEON_GEM_DOMAIN_VRAM,
|
|
+ &rdev->r600_blit.shader_gpu_addr);
|
|
+ radeon_bo_unreserve(rdev->r600_blit.shader_obj);
|
|
+ if (r) {
|
|
+ dev_err(rdev->dev, "(%d) pin blit object failed\n", r);
|
|
+ return r;
|
|
+ }
|
|
+ }
|
|
+ /* Enable IRQ */
|
|
+ r = r600_irq_init(rdev);
|
|
+ if (r) {
|
|
+ DRM_ERROR("radeon: IH init failed (%d).\n", r);
|
|
+ radeon_irq_kms_fini(rdev);
|
|
return r;
|
|
}
|
|
+ r600_irq_set(rdev);
|
|
|
|
r = radeon_ring_init(rdev, rdev->cp.ring_size);
|
|
if (r)
|
|
@@ -1578,18 +1960,35 @@ int r600_resume(struct radeon_device *rdev)
|
|
DRM_ERROR("radeon: failled testing IB (%d).\n", r);
|
|
return r;
|
|
}
|
|
+
|
|
+ r = r600_audio_init(rdev);
|
|
+ if (r) {
|
|
+ DRM_ERROR("radeon: audio resume failed\n");
|
|
+ return r;
|
|
+ }
|
|
+
|
|
return r;
|
|
}
|
|
|
|
int r600_suspend(struct radeon_device *rdev)
|
|
{
|
|
+ int r;
|
|
+
|
|
+ r600_audio_fini(rdev);
|
|
/* FIXME: we should wait for ring to be empty */
|
|
r600_cp_stop(rdev);
|
|
rdev->cp.ready = false;
|
|
+ r600_irq_suspend(rdev);
|
|
r600_wb_disable(rdev);
|
|
r600_pcie_gart_disable(rdev);
|
|
/* unpin shaders bo */
|
|
- radeon_object_unpin(rdev->r600_blit.shader_obj);
|
|
+ if (rdev->r600_blit.shader_obj) {
|
|
+ r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
|
|
+ if (!r) {
|
|
+ radeon_bo_unpin(rdev->r600_blit.shader_obj);
|
|
+ radeon_bo_unreserve(rdev->r600_blit.shader_obj);
|
|
+ }
|
|
+ }
|
|
return 0;
|
|
}
|
|
|
|
@@ -1627,7 +2026,11 @@ int r600_init(struct radeon_device *rdev)
|
|
if (r)
|
|
return r;
|
|
/* Post card if necessary */
|
|
- if (!r600_card_posted(rdev) && rdev->bios) {
|
|
+ if (!r600_card_posted(rdev)) {
|
|
+ if (!rdev->bios) {
|
|
+ dev_err(rdev->dev, "Card not posted and no BIOS - ignoring\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
DRM_INFO("GPU not posted. posting now...\n");
|
|
atom_asic_init(rdev->mode_info.atom_context);
|
|
}
|
|
@@ -1646,73 +2049,78 @@ int r600_init(struct radeon_device *rdev)
|
|
r = radeon_fence_driver_init(rdev);
|
|
if (r)
|
|
return r;
|
|
+ if (rdev->flags & RADEON_IS_AGP) {
|
|
+ r = radeon_agp_init(rdev);
|
|
+ if (r)
|
|
+ radeon_agp_disable(rdev);
|
|
+ }
|
|
r = r600_mc_init(rdev);
|
|
if (r)
|
|
return r;
|
|
/* Memory manager */
|
|
- r = radeon_object_init(rdev);
|
|
+ r = radeon_bo_init(rdev);
|
|
if (r)
|
|
return r;
|
|
+
|
|
+ r = radeon_irq_kms_init(rdev);
|
|
+ if (r)
|
|
+ return r;
|
|
+
|
|
rdev->cp.ring_obj = NULL;
|
|
r600_ring_init(rdev, 1024 * 1024);
|
|
|
|
- if (!rdev->me_fw || !rdev->pfp_fw) {
|
|
- r = r600_cp_init_microcode(rdev);
|
|
- if (r) {
|
|
- DRM_ERROR("Failed to load firmware!\n");
|
|
- return r;
|
|
- }
|
|
- }
|
|
+ rdev->ih.ring_obj = NULL;
|
|
+ r600_ih_ring_init(rdev, 64 * 1024);
|
|
|
|
r = r600_pcie_gart_init(rdev);
|
|
if (r)
|
|
return r;
|
|
|
|
rdev->accel_working = true;
|
|
- r = r600_blit_init(rdev);
|
|
- if (r) {
|
|
- DRM_ERROR("radeon: failled blitter (%d).\n", r);
|
|
- return r;
|
|
- }
|
|
-
|
|
r = r600_startup(rdev);
|
|
if (r) {
|
|
- r600_suspend(rdev);
|
|
+ dev_err(rdev->dev, "disabling GPU acceleration\n");
|
|
+ r600_cp_fini(rdev);
|
|
r600_wb_fini(rdev);
|
|
- radeon_ring_fini(rdev);
|
|
+ r600_irq_fini(rdev);
|
|
+ radeon_irq_kms_fini(rdev);
|
|
r600_pcie_gart_fini(rdev);
|
|
rdev->accel_working = false;
|
|
}
|
|
if (rdev->accel_working) {
|
|
r = radeon_ib_pool_init(rdev);
|
|
if (r) {
|
|
- DRM_ERROR("radeon: failled initializing IB pool (%d).\n", r);
|
|
- rdev->accel_working = false;
|
|
- }
|
|
- r = r600_ib_test(rdev);
|
|
- if (r) {
|
|
- DRM_ERROR("radeon: failled testing IB (%d).\n", r);
|
|
+ dev_err(rdev->dev, "IB initialization failed (%d).\n", r);
|
|
rdev->accel_working = false;
|
|
+ } else {
|
|
+ r = r600_ib_test(rdev);
|
|
+ if (r) {
|
|
+ dev_err(rdev->dev, "IB test failed (%d).\n", r);
|
|
+ rdev->accel_working = false;
|
|
+ }
|
|
}
|
|
}
|
|
+
|
|
+ r = r600_audio_init(rdev);
|
|
+ if (r)
|
|
+ return r; /* TODO error handling */
|
|
return 0;
|
|
}
|
|
|
|
void r600_fini(struct radeon_device *rdev)
|
|
{
|
|
- /* Suspend operations */
|
|
- r600_suspend(rdev);
|
|
-
|
|
+ r600_audio_fini(rdev);
|
|
r600_blit_fini(rdev);
|
|
- radeon_ring_fini(rdev);
|
|
+ r600_cp_fini(rdev);
|
|
r600_wb_fini(rdev);
|
|
+ r600_irq_fini(rdev);
|
|
+ radeon_irq_kms_fini(rdev);
|
|
r600_pcie_gart_fini(rdev);
|
|
+ radeon_agp_fini(rdev);
|
|
radeon_gem_fini(rdev);
|
|
radeon_fence_driver_fini(rdev);
|
|
radeon_clocks_fini(rdev);
|
|
- if (rdev->flags & RADEON_IS_AGP)
|
|
- radeon_agp_fini(rdev);
|
|
- radeon_object_fini(rdev);
|
|
+ radeon_bo_fini(rdev);
|
|
radeon_atombios_fini(rdev);
|
|
kfree(rdev->bios);
|
|
rdev->bios = NULL;
|
|
@@ -1798,8 +2206,668 @@ int r600_ib_test(struct radeon_device *rdev)
|
|
return r;
|
|
}
|
|
|
|
+/*
|
|
+ * Interrupts
|
|
+ *
|
|
+ * Interrupts use a ring buffer on r6xx/r7xx hardware. It works pretty
|
|
+ * the same as the CP ring buffer, but in reverse. Rather than the CPU
|
|
+ * writing to the ring and the GPU consuming, the GPU writes to the ring
|
|
+ * and host consumes. As the host irq handler processes interrupts, it
|
|
+ * increments the rptr. When the rptr catches up with the wptr, all the
|
|
+ * current interrupts have been processed.
|
|
+ */
|
|
+
|
|
+void r600_ih_ring_init(struct radeon_device *rdev, unsigned ring_size)
|
|
+{
|
|
+ u32 rb_bufsz;
|
|
+
|
|
+ /* Align ring size */
|
|
+ rb_bufsz = drm_order(ring_size / 4);
|
|
+ ring_size = (1 << rb_bufsz) * 4;
|
|
+ rdev->ih.ring_size = ring_size;
|
|
+ rdev->ih.ptr_mask = rdev->ih.ring_size - 1;
|
|
+ rdev->ih.rptr = 0;
|
|
+}
|
|
+
|
|
+static int r600_ih_ring_alloc(struct radeon_device *rdev)
|
|
+{
|
|
+ int r;
|
|
+
|
|
+ /* Allocate ring buffer */
|
|
+ if (rdev->ih.ring_obj == NULL) {
|
|
+ r = radeon_bo_create(rdev, NULL, rdev->ih.ring_size,
|
|
+ true,
|
|
+ RADEON_GEM_DOMAIN_GTT,
|
|
+ &rdev->ih.ring_obj);
|
|
+ if (r) {
|
|
+ DRM_ERROR("radeon: failed to create ih ring buffer (%d).\n", r);
|
|
+ return r;
|
|
+ }
|
|
+ r = radeon_bo_reserve(rdev->ih.ring_obj, false);
|
|
+ if (unlikely(r != 0))
|
|
+ return r;
|
|
+ r = radeon_bo_pin(rdev->ih.ring_obj,
|
|
+ RADEON_GEM_DOMAIN_GTT,
|
|
+ &rdev->ih.gpu_addr);
|
|
+ if (r) {
|
|
+ radeon_bo_unreserve(rdev->ih.ring_obj);
|
|
+ DRM_ERROR("radeon: failed to pin ih ring buffer (%d).\n", r);
|
|
+ return r;
|
|
+ }
|
|
+ r = radeon_bo_kmap(rdev->ih.ring_obj,
|
|
+ (void **)&rdev->ih.ring);
|
|
+ radeon_bo_unreserve(rdev->ih.ring_obj);
|
|
+ if (r) {
|
|
+ DRM_ERROR("radeon: failed to map ih ring buffer (%d).\n", r);
|
|
+ return r;
|
|
+ }
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void r600_ih_ring_fini(struct radeon_device *rdev)
|
|
+{
|
|
+ int r;
|
|
+ if (rdev->ih.ring_obj) {
|
|
+ r = radeon_bo_reserve(rdev->ih.ring_obj, false);
|
|
+ if (likely(r == 0)) {
|
|
+ radeon_bo_kunmap(rdev->ih.ring_obj);
|
|
+ radeon_bo_unpin(rdev->ih.ring_obj);
|
|
+ radeon_bo_unreserve(rdev->ih.ring_obj);
|
|
+ }
|
|
+ radeon_bo_unref(&rdev->ih.ring_obj);
|
|
+ rdev->ih.ring = NULL;
|
|
+ rdev->ih.ring_obj = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void r600_rlc_stop(struct radeon_device *rdev)
|
|
+{
|
|
+
|
|
+ if (rdev->family >= CHIP_RV770) {
|
|
+ /* r7xx asics need to soft reset RLC before halting */
|
|
+ WREG32(SRBM_SOFT_RESET, SOFT_RESET_RLC);
|
|
+ RREG32(SRBM_SOFT_RESET);
|
|
+ udelay(15000);
|
|
+ WREG32(SRBM_SOFT_RESET, 0);
|
|
+ RREG32(SRBM_SOFT_RESET);
|
|
+ }
|
|
+
|
|
+ WREG32(RLC_CNTL, 0);
|
|
+}
|
|
+
|
|
+static void r600_rlc_start(struct radeon_device *rdev)
|
|
+{
|
|
+ WREG32(RLC_CNTL, RLC_ENABLE);
|
|
+}
|
|
+
|
|
+static int r600_rlc_init(struct radeon_device *rdev)
|
|
+{
|
|
+ u32 i;
|
|
+ const __be32 *fw_data;
|
|
+
|
|
+ if (!rdev->rlc_fw)
|
|
+ return -EINVAL;
|
|
+
|
|
+ r600_rlc_stop(rdev);
|
|
+
|
|
+ WREG32(RLC_HB_BASE, 0);
|
|
+ WREG32(RLC_HB_CNTL, 0);
|
|
+ WREG32(RLC_HB_RPTR, 0);
|
|
+ WREG32(RLC_HB_WPTR, 0);
|
|
+ WREG32(RLC_HB_WPTR_LSB_ADDR, 0);
|
|
+ WREG32(RLC_HB_WPTR_MSB_ADDR, 0);
|
|
+ WREG32(RLC_MC_CNTL, 0);
|
|
+ WREG32(RLC_UCODE_CNTL, 0);
|
|
+
|
|
+ fw_data = (const __be32 *)rdev->rlc_fw->data;
|
|
+ if (rdev->family >= CHIP_RV770) {
|
|
+ for (i = 0; i < R700_RLC_UCODE_SIZE; i++) {
|
|
+ WREG32(RLC_UCODE_ADDR, i);
|
|
+ WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++));
|
|
+ }
|
|
+ } else {
|
|
+ for (i = 0; i < RLC_UCODE_SIZE; i++) {
|
|
+ WREG32(RLC_UCODE_ADDR, i);
|
|
+ WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++));
|
|
+ }
|
|
+ }
|
|
+ WREG32(RLC_UCODE_ADDR, 0);
|
|
+
|
|
+ r600_rlc_start(rdev);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void r600_enable_interrupts(struct radeon_device *rdev)
|
|
+{
|
|
+ u32 ih_cntl = RREG32(IH_CNTL);
|
|
+ u32 ih_rb_cntl = RREG32(IH_RB_CNTL);
|
|
+
|
|
+ ih_cntl |= ENABLE_INTR;
|
|
+ ih_rb_cntl |= IH_RB_ENABLE;
|
|
+ WREG32(IH_CNTL, ih_cntl);
|
|
+ WREG32(IH_RB_CNTL, ih_rb_cntl);
|
|
+ rdev->ih.enabled = true;
|
|
+}
|
|
+
|
|
+static void r600_disable_interrupts(struct radeon_device *rdev)
|
|
+{
|
|
+ u32 ih_rb_cntl = RREG32(IH_RB_CNTL);
|
|
+ u32 ih_cntl = RREG32(IH_CNTL);
|
|
+
|
|
+ ih_rb_cntl &= ~IH_RB_ENABLE;
|
|
+ ih_cntl &= ~ENABLE_INTR;
|
|
+ WREG32(IH_RB_CNTL, ih_rb_cntl);
|
|
+ WREG32(IH_CNTL, ih_cntl);
|
|
+ /* set rptr, wptr to 0 */
|
|
+ WREG32(IH_RB_RPTR, 0);
|
|
+ WREG32(IH_RB_WPTR, 0);
|
|
+ rdev->ih.enabled = false;
|
|
+ rdev->ih.wptr = 0;
|
|
+ rdev->ih.rptr = 0;
|
|
+}
|
|
+
|
|
+static void r600_disable_interrupt_state(struct radeon_device *rdev)
|
|
+{
|
|
+ u32 tmp;
|
|
+
|
|
+ WREG32(CP_INT_CNTL, 0);
|
|
+ WREG32(GRBM_INT_CNTL, 0);
|
|
+ WREG32(DxMODE_INT_MASK, 0);
|
|
+ if (ASIC_IS_DCE3(rdev)) {
|
|
+ WREG32(DCE3_DACA_AUTODETECT_INT_CONTROL, 0);
|
|
+ WREG32(DCE3_DACB_AUTODETECT_INT_CONTROL, 0);
|
|
+ tmp = RREG32(DC_HPD1_INT_CONTROL) & DC_HPDx_INT_POLARITY;
|
|
+ WREG32(DC_HPD1_INT_CONTROL, tmp);
|
|
+ tmp = RREG32(DC_HPD2_INT_CONTROL) & DC_HPDx_INT_POLARITY;
|
|
+ WREG32(DC_HPD2_INT_CONTROL, tmp);
|
|
+ tmp = RREG32(DC_HPD3_INT_CONTROL) & DC_HPDx_INT_POLARITY;
|
|
+ WREG32(DC_HPD3_INT_CONTROL, tmp);
|
|
+ tmp = RREG32(DC_HPD4_INT_CONTROL) & DC_HPDx_INT_POLARITY;
|
|
+ WREG32(DC_HPD4_INT_CONTROL, tmp);
|
|
+ if (ASIC_IS_DCE32(rdev)) {
|
|
+ tmp = RREG32(DC_HPD5_INT_CONTROL) & DC_HPDx_INT_POLARITY;
|
|
+ WREG32(DC_HPD5_INT_CONTROL, 0);
|
|
+ tmp = RREG32(DC_HPD6_INT_CONTROL) & DC_HPDx_INT_POLARITY;
|
|
+ WREG32(DC_HPD6_INT_CONTROL, 0);
|
|
+ }
|
|
+ } else {
|
|
+ WREG32(DACA_AUTODETECT_INT_CONTROL, 0);
|
|
+ WREG32(DACB_AUTODETECT_INT_CONTROL, 0);
|
|
+ tmp = RREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL) & DC_HOT_PLUG_DETECTx_INT_POLARITY;
|
|
+ WREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL, 0);
|
|
+ tmp = RREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL) & DC_HOT_PLUG_DETECTx_INT_POLARITY;
|
|
+ WREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL, 0);
|
|
+ tmp = RREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL) & DC_HOT_PLUG_DETECTx_INT_POLARITY;
|
|
+ WREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL, 0);
|
|
+ }
|
|
+}
|
|
+
|
|
+int r600_irq_init(struct radeon_device *rdev)
|
|
+{
|
|
+ int ret = 0;
|
|
+ int rb_bufsz;
|
|
+ u32 interrupt_cntl, ih_cntl, ih_rb_cntl;
|
|
+
|
|
+ /* allocate ring */
|
|
+ ret = r600_ih_ring_alloc(rdev);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* disable irqs */
|
|
+ r600_disable_interrupts(rdev);
|
|
+
|
|
+ /* init rlc */
|
|
+ ret = r600_rlc_init(rdev);
|
|
+ if (ret) {
|
|
+ r600_ih_ring_fini(rdev);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /* setup interrupt control */
|
|
+ /* set dummy read address to ring address */
|
|
+ WREG32(INTERRUPT_CNTL2, rdev->ih.gpu_addr >> 8);
|
|
+ interrupt_cntl = RREG32(INTERRUPT_CNTL);
|
|
+ /* IH_DUMMY_RD_OVERRIDE=0 - dummy read disabled with msi, enabled without msi
|
|
+ * IH_DUMMY_RD_OVERRIDE=1 - dummy read controlled by IH_DUMMY_RD_EN
|
|
+ */
|
|
+ interrupt_cntl &= ~IH_DUMMY_RD_OVERRIDE;
|
|
+ /* IH_REQ_NONSNOOP_EN=1 if ring is in non-cacheable memory, e.g., vram */
|
|
+ interrupt_cntl &= ~IH_REQ_NONSNOOP_EN;
|
|
+ WREG32(INTERRUPT_CNTL, interrupt_cntl);
|
|
+
|
|
+ WREG32(IH_RB_BASE, rdev->ih.gpu_addr >> 8);
|
|
+ rb_bufsz = drm_order(rdev->ih.ring_size / 4);
|
|
+
|
|
+ ih_rb_cntl = (IH_WPTR_OVERFLOW_ENABLE |
|
|
+ IH_WPTR_OVERFLOW_CLEAR |
|
|
+ (rb_bufsz << 1));
|
|
+ /* WPTR writeback, not yet */
|
|
+ /*ih_rb_cntl |= IH_WPTR_WRITEBACK_ENABLE;*/
|
|
+ WREG32(IH_RB_WPTR_ADDR_LO, 0);
|
|
+ WREG32(IH_RB_WPTR_ADDR_HI, 0);
|
|
+
|
|
+ WREG32(IH_RB_CNTL, ih_rb_cntl);
|
|
+
|
|
+ /* set rptr, wptr to 0 */
|
|
+ WREG32(IH_RB_RPTR, 0);
|
|
+ WREG32(IH_RB_WPTR, 0);
|
|
+
|
|
+ /* Default settings for IH_CNTL (disabled at first) */
|
|
+ ih_cntl = MC_WRREQ_CREDIT(0x10) | MC_WR_CLEAN_CNT(0x10);
|
|
+ /* RPTR_REARM only works if msi's are enabled */
|
|
+ if (rdev->msi_enabled)
|
|
+ ih_cntl |= RPTR_REARM;
|
|
+
|
|
+#ifdef __BIG_ENDIAN
|
|
+ ih_cntl |= IH_MC_SWAP(IH_MC_SWAP_32BIT);
|
|
+#endif
|
|
+ WREG32(IH_CNTL, ih_cntl);
|
|
+
|
|
+ /* force the active interrupt state to all disabled */
|
|
+ r600_disable_interrupt_state(rdev);
|
|
+
|
|
+ /* enable irqs */
|
|
+ r600_enable_interrupts(rdev);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void r600_irq_suspend(struct radeon_device *rdev)
|
|
+{
|
|
+ r600_disable_interrupts(rdev);
|
|
+ r600_rlc_stop(rdev);
|
|
+}
|
|
+
|
|
+void r600_irq_fini(struct radeon_device *rdev)
|
|
+{
|
|
+ r600_irq_suspend(rdev);
|
|
+ r600_ih_ring_fini(rdev);
|
|
+}
|
|
+
|
|
+int r600_irq_set(struct radeon_device *rdev)
|
|
+{
|
|
+ u32 cp_int_cntl = CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE;
|
|
+ u32 mode_int = 0;
|
|
+ u32 hpd1, hpd2, hpd3, hpd4 = 0, hpd5 = 0, hpd6 = 0;
|
|
+
|
|
+ if (!rdev->irq.installed) {
|
|
+ WARN(1, "Can't enable IRQ/MSI because no handler is installed.\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ /* don't enable anything if the ih is disabled */
|
|
+ if (!rdev->ih.enabled) {
|
|
+ r600_disable_interrupts(rdev);
|
|
+ /* force the active interrupt state to all disabled */
|
|
+ r600_disable_interrupt_state(rdev);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (ASIC_IS_DCE3(rdev)) {
|
|
+ hpd1 = RREG32(DC_HPD1_INT_CONTROL) & ~DC_HPDx_INT_EN;
|
|
+ hpd2 = RREG32(DC_HPD2_INT_CONTROL) & ~DC_HPDx_INT_EN;
|
|
+ hpd3 = RREG32(DC_HPD3_INT_CONTROL) & ~DC_HPDx_INT_EN;
|
|
+ hpd4 = RREG32(DC_HPD4_INT_CONTROL) & ~DC_HPDx_INT_EN;
|
|
+ if (ASIC_IS_DCE32(rdev)) {
|
|
+ hpd5 = RREG32(DC_HPD5_INT_CONTROL) & ~DC_HPDx_INT_EN;
|
|
+ hpd6 = RREG32(DC_HPD6_INT_CONTROL) & ~DC_HPDx_INT_EN;
|
|
+ }
|
|
+ } else {
|
|
+ hpd1 = RREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL) & ~DC_HPDx_INT_EN;
|
|
+ hpd2 = RREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL) & ~DC_HPDx_INT_EN;
|
|
+ hpd3 = RREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL) & ~DC_HPDx_INT_EN;
|
|
+ }
|
|
+
|
|
+ if (rdev->irq.sw_int) {
|
|
+ DRM_DEBUG("r600_irq_set: sw int\n");
|
|
+ cp_int_cntl |= RB_INT_ENABLE;
|
|
+ }
|
|
+ if (rdev->irq.crtc_vblank_int[0]) {
|
|
+ DRM_DEBUG("r600_irq_set: vblank 0\n");
|
|
+ mode_int |= D1MODE_VBLANK_INT_MASK;
|
|
+ }
|
|
+ if (rdev->irq.crtc_vblank_int[1]) {
|
|
+ DRM_DEBUG("r600_irq_set: vblank 1\n");
|
|
+ mode_int |= D2MODE_VBLANK_INT_MASK;
|
|
+ }
|
|
+ if (rdev->irq.hpd[0]) {
|
|
+ DRM_DEBUG("r600_irq_set: hpd 1\n");
|
|
+ hpd1 |= DC_HPDx_INT_EN;
|
|
+ }
|
|
+ if (rdev->irq.hpd[1]) {
|
|
+ DRM_DEBUG("r600_irq_set: hpd 2\n");
|
|
+ hpd2 |= DC_HPDx_INT_EN;
|
|
+ }
|
|
+ if (rdev->irq.hpd[2]) {
|
|
+ DRM_DEBUG("r600_irq_set: hpd 3\n");
|
|
+ hpd3 |= DC_HPDx_INT_EN;
|
|
+ }
|
|
+ if (rdev->irq.hpd[3]) {
|
|
+ DRM_DEBUG("r600_irq_set: hpd 4\n");
|
|
+ hpd4 |= DC_HPDx_INT_EN;
|
|
+ }
|
|
+ if (rdev->irq.hpd[4]) {
|
|
+ DRM_DEBUG("r600_irq_set: hpd 5\n");
|
|
+ hpd5 |= DC_HPDx_INT_EN;
|
|
+ }
|
|
+ if (rdev->irq.hpd[5]) {
|
|
+ DRM_DEBUG("r600_irq_set: hpd 6\n");
|
|
+ hpd6 |= DC_HPDx_INT_EN;
|
|
+ }
|
|
+
|
|
+ WREG32(CP_INT_CNTL, cp_int_cntl);
|
|
+ WREG32(DxMODE_INT_MASK, mode_int);
|
|
+ if (ASIC_IS_DCE3(rdev)) {
|
|
+ WREG32(DC_HPD1_INT_CONTROL, hpd1);
|
|
+ WREG32(DC_HPD2_INT_CONTROL, hpd2);
|
|
+ WREG32(DC_HPD3_INT_CONTROL, hpd3);
|
|
+ WREG32(DC_HPD4_INT_CONTROL, hpd4);
|
|
+ if (ASIC_IS_DCE32(rdev)) {
|
|
+ WREG32(DC_HPD5_INT_CONTROL, hpd5);
|
|
+ WREG32(DC_HPD6_INT_CONTROL, hpd6);
|
|
+ }
|
|
+ } else {
|
|
+ WREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL, hpd1);
|
|
+ WREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL, hpd2);
|
|
+ WREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL, hpd3);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline void r600_irq_ack(struct radeon_device *rdev,
|
|
+ u32 *disp_int,
|
|
+ u32 *disp_int_cont,
|
|
+ u32 *disp_int_cont2)
|
|
+{
|
|
+ u32 tmp;
|
|
+
|
|
+ if (ASIC_IS_DCE3(rdev)) {
|
|
+ *disp_int = RREG32(DCE3_DISP_INTERRUPT_STATUS);
|
|
+ *disp_int_cont = RREG32(DCE3_DISP_INTERRUPT_STATUS_CONTINUE);
|
|
+ *disp_int_cont2 = RREG32(DCE3_DISP_INTERRUPT_STATUS_CONTINUE2);
|
|
+ } else {
|
|
+ *disp_int = RREG32(DISP_INTERRUPT_STATUS);
|
|
+ *disp_int_cont = RREG32(DISP_INTERRUPT_STATUS_CONTINUE);
|
|
+ *disp_int_cont2 = 0;
|
|
+ }
|
|
+
|
|
+ if (*disp_int & LB_D1_VBLANK_INTERRUPT)
|
|
+ WREG32(D1MODE_VBLANK_STATUS, DxMODE_VBLANK_ACK);
|
|
+ if (*disp_int & LB_D1_VLINE_INTERRUPT)
|
|
+ WREG32(D1MODE_VLINE_STATUS, DxMODE_VLINE_ACK);
|
|
+ if (*disp_int & LB_D2_VBLANK_INTERRUPT)
|
|
+ WREG32(D2MODE_VBLANK_STATUS, DxMODE_VBLANK_ACK);
|
|
+ if (*disp_int & LB_D2_VLINE_INTERRUPT)
|
|
+ WREG32(D2MODE_VLINE_STATUS, DxMODE_VLINE_ACK);
|
|
+ if (*disp_int & DC_HPD1_INTERRUPT) {
|
|
+ if (ASIC_IS_DCE3(rdev)) {
|
|
+ tmp = RREG32(DC_HPD1_INT_CONTROL);
|
|
+ tmp |= DC_HPDx_INT_ACK;
|
|
+ WREG32(DC_HPD1_INT_CONTROL, tmp);
|
|
+ } else {
|
|
+ tmp = RREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL);
|
|
+ tmp |= DC_HPDx_INT_ACK;
|
|
+ WREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL, tmp);
|
|
+ }
|
|
+ }
|
|
+ if (*disp_int & DC_HPD2_INTERRUPT) {
|
|
+ if (ASIC_IS_DCE3(rdev)) {
|
|
+ tmp = RREG32(DC_HPD2_INT_CONTROL);
|
|
+ tmp |= DC_HPDx_INT_ACK;
|
|
+ WREG32(DC_HPD2_INT_CONTROL, tmp);
|
|
+ } else {
|
|
+ tmp = RREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL);
|
|
+ tmp |= DC_HPDx_INT_ACK;
|
|
+ WREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL, tmp);
|
|
+ }
|
|
+ }
|
|
+ if (*disp_int_cont & DC_HPD3_INTERRUPT) {
|
|
+ if (ASIC_IS_DCE3(rdev)) {
|
|
+ tmp = RREG32(DC_HPD3_INT_CONTROL);
|
|
+ tmp |= DC_HPDx_INT_ACK;
|
|
+ WREG32(DC_HPD3_INT_CONTROL, tmp);
|
|
+ } else {
|
|
+ tmp = RREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL);
|
|
+ tmp |= DC_HPDx_INT_ACK;
|
|
+ WREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL, tmp);
|
|
+ }
|
|
+ }
|
|
+ if (*disp_int_cont & DC_HPD4_INTERRUPT) {
|
|
+ tmp = RREG32(DC_HPD4_INT_CONTROL);
|
|
+ tmp |= DC_HPDx_INT_ACK;
|
|
+ WREG32(DC_HPD4_INT_CONTROL, tmp);
|
|
+ }
|
|
+ if (ASIC_IS_DCE32(rdev)) {
|
|
+ if (*disp_int_cont2 & DC_HPD5_INTERRUPT) {
|
|
+ tmp = RREG32(DC_HPD5_INT_CONTROL);
|
|
+ tmp |= DC_HPDx_INT_ACK;
|
|
+ WREG32(DC_HPD5_INT_CONTROL, tmp);
|
|
+ }
|
|
+ if (*disp_int_cont2 & DC_HPD6_INTERRUPT) {
|
|
+ tmp = RREG32(DC_HPD5_INT_CONTROL);
|
|
+ tmp |= DC_HPDx_INT_ACK;
|
|
+ WREG32(DC_HPD6_INT_CONTROL, tmp);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void r600_irq_disable(struct radeon_device *rdev)
|
|
+{
|
|
+ u32 disp_int, disp_int_cont, disp_int_cont2;
|
|
+
|
|
+ r600_disable_interrupts(rdev);
|
|
+ /* Wait and acknowledge irq */
|
|
+ mdelay(1);
|
|
+ r600_irq_ack(rdev, &disp_int, &disp_int_cont, &disp_int_cont2);
|
|
+ r600_disable_interrupt_state(rdev);
|
|
+}
|
|
+
|
|
+static inline u32 r600_get_ih_wptr(struct radeon_device *rdev)
|
|
+{
|
|
+ u32 wptr, tmp;
|
|
+
|
|
+ /* XXX use writeback */
|
|
+ wptr = RREG32(IH_RB_WPTR);
|
|
+
|
|
+ if (wptr & RB_OVERFLOW) {
|
|
+ /* When a ring buffer overflow happen start parsing interrupt
|
|
+ * from the last not overwritten vector (wptr + 16). Hopefully
|
|
+ * this should allow us to catchup.
|
|
+ */
|
|
+ dev_warn(rdev->dev, "IH ring buffer overflow (0x%08X, %d, %d)\n",
|
|
+ wptr, rdev->ih.rptr, (wptr + 16) + rdev->ih.ptr_mask);
|
|
+ rdev->ih.rptr = (wptr + 16) & rdev->ih.ptr_mask;
|
|
+ tmp = RREG32(IH_RB_CNTL);
|
|
+ tmp |= IH_WPTR_OVERFLOW_CLEAR;
|
|
+ WREG32(IH_RB_CNTL, tmp);
|
|
+ }
|
|
+ return (wptr & rdev->ih.ptr_mask);
|
|
+}
|
|
+
|
|
+/* r600 IV Ring
|
|
+ * Each IV ring entry is 128 bits:
|
|
+ * [7:0] - interrupt source id
|
|
+ * [31:8] - reserved
|
|
+ * [59:32] - interrupt source data
|
|
+ * [127:60] - reserved
|
|
+ *
|
|
+ * The basic interrupt vector entries
|
|
+ * are decoded as follows:
|
|
+ * src_id src_data description
|
|
+ * 1 0 D1 Vblank
|
|
+ * 1 1 D1 Vline
|
|
+ * 5 0 D2 Vblank
|
|
+ * 5 1 D2 Vline
|
|
+ * 19 0 FP Hot plug detection A
|
|
+ * 19 1 FP Hot plug detection B
|
|
+ * 19 2 DAC A auto-detection
|
|
+ * 19 3 DAC B auto-detection
|
|
+ * 176 - CP_INT RB
|
|
+ * 177 - CP_INT IB1
|
|
+ * 178 - CP_INT IB2
|
|
+ * 181 - EOP Interrupt
|
|
+ * 233 - GUI Idle
|
|
+ *
|
|
+ * Note, these are based on r600 and may need to be
|
|
+ * adjusted or added to on newer asics
|
|
+ */
|
|
|
|
+int r600_irq_process(struct radeon_device *rdev)
|
|
+{
|
|
+ u32 wptr = r600_get_ih_wptr(rdev);
|
|
+ u32 rptr = rdev->ih.rptr;
|
|
+ u32 src_id, src_data;
|
|
+ u32 ring_index, disp_int, disp_int_cont, disp_int_cont2;
|
|
+ unsigned long flags;
|
|
+ bool queue_hotplug = false;
|
|
+
|
|
+ DRM_DEBUG("r600_irq_process start: rptr %d, wptr %d\n", rptr, wptr);
|
|
+ if (!rdev->ih.enabled)
|
|
+ return IRQ_NONE;
|
|
+
|
|
+ spin_lock_irqsave(&rdev->ih.lock, flags);
|
|
+
|
|
+ if (rptr == wptr) {
|
|
+ spin_unlock_irqrestore(&rdev->ih.lock, flags);
|
|
+ return IRQ_NONE;
|
|
+ }
|
|
+ if (rdev->shutdown) {
|
|
+ spin_unlock_irqrestore(&rdev->ih.lock, flags);
|
|
+ return IRQ_NONE;
|
|
+ }
|
|
+
|
|
+restart_ih:
|
|
+ /* display interrupts */
|
|
+ r600_irq_ack(rdev, &disp_int, &disp_int_cont, &disp_int_cont2);
|
|
+
|
|
+ rdev->ih.wptr = wptr;
|
|
+ while (rptr != wptr) {
|
|
+ /* wptr/rptr are in bytes! */
|
|
+ ring_index = rptr / 4;
|
|
+ src_id = rdev->ih.ring[ring_index] & 0xff;
|
|
+ src_data = rdev->ih.ring[ring_index + 1] & 0xfffffff;
|
|
+
|
|
+ switch (src_id) {
|
|
+ case 1: /* D1 vblank/vline */
|
|
+ switch (src_data) {
|
|
+ case 0: /* D1 vblank */
|
|
+ if (disp_int & LB_D1_VBLANK_INTERRUPT) {
|
|
+ drm_handle_vblank(rdev->ddev, 0);
|
|
+ disp_int &= ~LB_D1_VBLANK_INTERRUPT;
|
|
+ DRM_DEBUG("IH: D1 vblank\n");
|
|
+ }
|
|
+ break;
|
|
+ case 1: /* D1 vline */
|
|
+ if (disp_int & LB_D1_VLINE_INTERRUPT) {
|
|
+ disp_int &= ~LB_D1_VLINE_INTERRUPT;
|
|
+ DRM_DEBUG("IH: D1 vline\n");
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
|
|
+ break;
|
|
+ }
|
|
+ break;
|
|
+ case 5: /* D2 vblank/vline */
|
|
+ switch (src_data) {
|
|
+ case 0: /* D2 vblank */
|
|
+ if (disp_int & LB_D2_VBLANK_INTERRUPT) {
|
|
+ drm_handle_vblank(rdev->ddev, 1);
|
|
+ disp_int &= ~LB_D2_VBLANK_INTERRUPT;
|
|
+ DRM_DEBUG("IH: D2 vblank\n");
|
|
+ }
|
|
+ break;
|
|
+ case 1: /* D1 vline */
|
|
+ if (disp_int & LB_D2_VLINE_INTERRUPT) {
|
|
+ disp_int &= ~LB_D2_VLINE_INTERRUPT;
|
|
+ DRM_DEBUG("IH: D2 vline\n");
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
|
|
+ break;
|
|
+ }
|
|
+ break;
|
|
+ case 19: /* HPD/DAC hotplug */
|
|
+ switch (src_data) {
|
|
+ case 0:
|
|
+ if (disp_int & DC_HPD1_INTERRUPT) {
|
|
+ disp_int &= ~DC_HPD1_INTERRUPT;
|
|
+ queue_hotplug = true;
|
|
+ DRM_DEBUG("IH: HPD1\n");
|
|
+ }
|
|
+ break;
|
|
+ case 1:
|
|
+ if (disp_int & DC_HPD2_INTERRUPT) {
|
|
+ disp_int &= ~DC_HPD2_INTERRUPT;
|
|
+ queue_hotplug = true;
|
|
+ DRM_DEBUG("IH: HPD2\n");
|
|
+ }
|
|
+ break;
|
|
+ case 4:
|
|
+ if (disp_int_cont & DC_HPD3_INTERRUPT) {
|
|
+ disp_int_cont &= ~DC_HPD3_INTERRUPT;
|
|
+ queue_hotplug = true;
|
|
+ DRM_DEBUG("IH: HPD3\n");
|
|
+ }
|
|
+ break;
|
|
+ case 5:
|
|
+ if (disp_int_cont & DC_HPD4_INTERRUPT) {
|
|
+ disp_int_cont &= ~DC_HPD4_INTERRUPT;
|
|
+ queue_hotplug = true;
|
|
+ DRM_DEBUG("IH: HPD4\n");
|
|
+ }
|
|
+ break;
|
|
+ case 10:
|
|
+ if (disp_int_cont2 & DC_HPD5_INTERRUPT) {
|
|
+ disp_int_cont &= ~DC_HPD5_INTERRUPT;
|
|
+ queue_hotplug = true;
|
|
+ DRM_DEBUG("IH: HPD5\n");
|
|
+ }
|
|
+ break;
|
|
+ case 12:
|
|
+ if (disp_int_cont2 & DC_HPD6_INTERRUPT) {
|
|
+ disp_int_cont &= ~DC_HPD6_INTERRUPT;
|
|
+ queue_hotplug = true;
|
|
+ DRM_DEBUG("IH: HPD6\n");
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
|
|
+ break;
|
|
+ }
|
|
+ break;
|
|
+ case 176: /* CP_INT in ring buffer */
|
|
+ case 177: /* CP_INT in IB1 */
|
|
+ case 178: /* CP_INT in IB2 */
|
|
+ DRM_DEBUG("IH: CP int: 0x%08x\n", src_data);
|
|
+ radeon_fence_process(rdev);
|
|
+ break;
|
|
+ case 181: /* CP EOP event */
|
|
+ DRM_DEBUG("IH: CP EOP\n");
|
|
+ break;
|
|
+ default:
|
|
+ DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
|
|
+ break;
|
|
+ }
|
|
|
|
+ /* wptr/rptr are in bytes! */
|
|
+ rptr += 16;
|
|
+ rptr &= rdev->ih.ptr_mask;
|
|
+ }
|
|
+ /* make sure wptr hasn't changed while processing */
|
|
+ wptr = r600_get_ih_wptr(rdev);
|
|
+ if (wptr != rdev->ih.wptr)
|
|
+ goto restart_ih;
|
|
+ if (queue_hotplug)
|
|
+ queue_work(rdev->wq, &rdev->hotplug_work);
|
|
+ rdev->ih.rptr = rptr;
|
|
+ WREG32(IH_RB_RPTR, rdev->ih.rptr);
|
|
+ spin_unlock_irqrestore(&rdev->ih.lock, flags);
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
|
|
/*
|
|
* Debugfs info
|
|
@@ -1811,21 +2879,21 @@ static int r600_debugfs_cp_ring_info(struct seq_file *m, void *data)
|
|
struct drm_info_node *node = (struct drm_info_node *) m->private;
|
|
struct drm_device *dev = node->minor->dev;
|
|
struct radeon_device *rdev = dev->dev_private;
|
|
- uint32_t rdp, wdp;
|
|
unsigned count, i, j;
|
|
|
|
radeon_ring_free_size(rdev);
|
|
- rdp = RREG32(CP_RB_RPTR);
|
|
- wdp = RREG32(CP_RB_WPTR);
|
|
- count = (rdp + rdev->cp.ring_size - wdp) & rdev->cp.ptr_mask;
|
|
+ count = (rdev->cp.ring_size / 4) - rdev->cp.ring_free_dw;
|
|
seq_printf(m, "CP_STAT 0x%08x\n", RREG32(CP_STAT));
|
|
- seq_printf(m, "CP_RB_WPTR 0x%08x\n", wdp);
|
|
- seq_printf(m, "CP_RB_RPTR 0x%08x\n", rdp);
|
|
+ seq_printf(m, "CP_RB_WPTR 0x%08x\n", RREG32(CP_RB_WPTR));
|
|
+ seq_printf(m, "CP_RB_RPTR 0x%08x\n", RREG32(CP_RB_RPTR));
|
|
+ seq_printf(m, "driver's copy of the CP_RB_WPTR 0x%08x\n", rdev->cp.wptr);
|
|
+ seq_printf(m, "driver's copy of the CP_RB_RPTR 0x%08x\n", rdev->cp.rptr);
|
|
seq_printf(m, "%u free dwords in ring\n", rdev->cp.ring_free_dw);
|
|
seq_printf(m, "%u dwords in ring\n", count);
|
|
+ i = rdev->cp.rptr;
|
|
for (j = 0; j <= count; j++) {
|
|
- i = (rdp + j) & rdev->cp.ptr_mask;
|
|
seq_printf(m, "r[%04d]=0x%08x\n", i, rdev->cp.ring[i]);
|
|
+ i = (i + 1) & rdev->cp.ptr_mask;
|
|
}
|
|
return 0;
|
|
}
|
|
@@ -1855,3 +2923,18 @@ int r600_debugfs_mc_info_init(struct radeon_device *rdev)
|
|
return 0;
|
|
#endif
|
|
}
|
|
+
|
|
+/**
|
|
+ * r600_ioctl_wait_idle - flush host path cache on wait idle ioctl
|
|
+ * rdev: radeon device structure
|
|
+ * bo: buffer object struct which userspace is waiting for idle
|
|
+ *
|
|
+ * Some R6XX/R7XX doesn't seems to take into account HDP flush performed
|
|
+ * through ring buffer, this leads to corruption in rendering, see
|
|
+ * http://bugzilla.kernel.org/show_bug.cgi?id=15186 to avoid this we
|
|
+ * directly perform HDP flush by writing register through MMIO.
|
|
+ */
|
|
+void r600_ioctl_wait_idle(struct radeon_device *rdev, struct radeon_bo *bo)
|
|
+{
|
|
+ WREG32(R_005480_HDP_MEM_COHERENCY_FLUSH_CNTL, 0x1);
|
|
+}
|
|
diff --git a/drivers/gpu/drm/radeon/r600_audio.c b/drivers/gpu/drm/radeon/r600_audio.c
|
|
new file mode 100644
|
|
index 0000000..0dcb690
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/radeon/r600_audio.c
|
|
@@ -0,0 +1,266 @@
|
|
+/*
|
|
+ * Copyright 2008 Advanced Micro Devices, Inc.
|
|
+ * Copyright 2008 Red Hat Inc.
|
|
+ * Copyright 2009 Christian König.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice shall be included in
|
|
+ * all copies or substantial portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
+ * OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ * Authors: Christian König
|
|
+ */
|
|
+#include "drmP.h"
|
|
+#include "radeon.h"
|
|
+#include "radeon_reg.h"
|
|
+#include "atom.h"
|
|
+
|
|
+#define AUDIO_TIMER_INTERVALL 100 /* 1/10 sekund should be enough */
|
|
+
|
|
+/*
|
|
+ * check if the chipset is supported
|
|
+ */
|
|
+static int r600_audio_chipset_supported(struct radeon_device *rdev)
|
|
+{
|
|
+ return (rdev->family >= CHIP_R600 && rdev->family < CHIP_RV710)
|
|
+ || rdev->family == CHIP_RS600
|
|
+ || rdev->family == CHIP_RS690
|
|
+ || rdev->family == CHIP_RS740;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * current number of channels
|
|
+ */
|
|
+static int r600_audio_channels(struct radeon_device *rdev)
|
|
+{
|
|
+ return (RREG32(R600_AUDIO_RATE_BPS_CHANNEL) & 0x7) + 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * current bits per sample
|
|
+ */
|
|
+static int r600_audio_bits_per_sample(struct radeon_device *rdev)
|
|
+{
|
|
+ uint32_t value = (RREG32(R600_AUDIO_RATE_BPS_CHANNEL) & 0xF0) >> 4;
|
|
+ switch (value) {
|
|
+ case 0x0: return 8;
|
|
+ case 0x1: return 16;
|
|
+ case 0x2: return 20;
|
|
+ case 0x3: return 24;
|
|
+ case 0x4: return 32;
|
|
+ }
|
|
+
|
|
+ DRM_ERROR("Unknown bits per sample 0x%x using 16 instead.\n", (int)value);
|
|
+
|
|
+ return 16;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * current sampling rate in HZ
|
|
+ */
|
|
+static int r600_audio_rate(struct radeon_device *rdev)
|
|
+{
|
|
+ uint32_t value = RREG32(R600_AUDIO_RATE_BPS_CHANNEL);
|
|
+ uint32_t result;
|
|
+
|
|
+ if (value & 0x4000)
|
|
+ result = 44100;
|
|
+ else
|
|
+ result = 48000;
|
|
+
|
|
+ result *= ((value >> 11) & 0x7) + 1;
|
|
+ result /= ((value >> 8) & 0x7) + 1;
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * iec 60958 status bits
|
|
+ */
|
|
+static uint8_t r600_audio_status_bits(struct radeon_device *rdev)
|
|
+{
|
|
+ return RREG32(R600_AUDIO_STATUS_BITS) & 0xff;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * iec 60958 category code
|
|
+ */
|
|
+static uint8_t r600_audio_category_code(struct radeon_device *rdev)
|
|
+{
|
|
+ return (RREG32(R600_AUDIO_STATUS_BITS) >> 8) & 0xff;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * update all hdmi interfaces with current audio parameters
|
|
+ */
|
|
+static void r600_audio_update_hdmi(unsigned long param)
|
|
+{
|
|
+ struct radeon_device *rdev = (struct radeon_device *)param;
|
|
+ struct drm_device *dev = rdev->ddev;
|
|
+
|
|
+ int channels = r600_audio_channels(rdev);
|
|
+ int rate = r600_audio_rate(rdev);
|
|
+ int bps = r600_audio_bits_per_sample(rdev);
|
|
+ uint8_t status_bits = r600_audio_status_bits(rdev);
|
|
+ uint8_t category_code = r600_audio_category_code(rdev);
|
|
+
|
|
+ struct drm_encoder *encoder;
|
|
+ int changes = 0;
|
|
+
|
|
+ changes |= channels != rdev->audio_channels;
|
|
+ changes |= rate != rdev->audio_rate;
|
|
+ changes |= bps != rdev->audio_bits_per_sample;
|
|
+ changes |= status_bits != rdev->audio_status_bits;
|
|
+ changes |= category_code != rdev->audio_category_code;
|
|
+
|
|
+ if (changes) {
|
|
+ rdev->audio_channels = channels;
|
|
+ rdev->audio_rate = rate;
|
|
+ rdev->audio_bits_per_sample = bps;
|
|
+ rdev->audio_status_bits = status_bits;
|
|
+ rdev->audio_category_code = category_code;
|
|
+ }
|
|
+
|
|
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
|
+ if (changes || r600_hdmi_buffer_status_changed(encoder))
|
|
+ r600_hdmi_update_audio_settings(
|
|
+ encoder, channels,
|
|
+ rate, bps, status_bits,
|
|
+ category_code);
|
|
+ }
|
|
+
|
|
+ mod_timer(&rdev->audio_timer,
|
|
+ jiffies + msecs_to_jiffies(AUDIO_TIMER_INTERVALL));
|
|
+}
|
|
+
|
|
+/*
|
|
+ * initialize the audio vars and register the update timer
|
|
+ */
|
|
+int r600_audio_init(struct radeon_device *rdev)
|
|
+{
|
|
+ if (!r600_audio_chipset_supported(rdev))
|
|
+ return 0;
|
|
+
|
|
+ DRM_INFO("%s audio support", radeon_audio ? "Enabling" : "Disabling");
|
|
+ WREG32_P(R600_AUDIO_ENABLE, radeon_audio ? 0x81000000 : 0x0, ~0x81000000);
|
|
+
|
|
+ rdev->audio_channels = -1;
|
|
+ rdev->audio_rate = -1;
|
|
+ rdev->audio_bits_per_sample = -1;
|
|
+ rdev->audio_status_bits = 0;
|
|
+ rdev->audio_category_code = 0;
|
|
+
|
|
+ setup_timer(
|
|
+ &rdev->audio_timer,
|
|
+ r600_audio_update_hdmi,
|
|
+ (unsigned long)rdev);
|
|
+
|
|
+ mod_timer(&rdev->audio_timer, jiffies + 1);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * determin how the encoders and audio interface is wired together
|
|
+ */
|
|
+int r600_audio_tmds_index(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
|
|
+ struct drm_encoder *other;
|
|
+
|
|
+ switch (radeon_encoder->encoder_id) {
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
|
|
+ return 0;
|
|
+
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
|
|
+ /* special case check if an TMDS1 is present */
|
|
+ list_for_each_entry(other, &dev->mode_config.encoder_list, head) {
|
|
+ if (to_radeon_encoder(other)->encoder_id ==
|
|
+ ENCODER_OBJECT_ID_INTERNAL_TMDS1)
|
|
+ return 1;
|
|
+ }
|
|
+ return 0;
|
|
+
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
|
|
+ return 1;
|
|
+
|
|
+ default:
|
|
+ DRM_ERROR("Unsupported encoder type 0x%02X\n",
|
|
+ radeon_encoder->encoder_id);
|
|
+ return -1;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * atach the audio codec to the clock source of the encoder
|
|
+ */
|
|
+void r600_audio_set_clock(struct drm_encoder *encoder, int clock)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
|
|
+ int base_rate = 48000;
|
|
+
|
|
+ switch (radeon_encoder->encoder_id) {
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
|
|
+ WREG32_P(R600_AUDIO_TIMING, 0, ~0x301);
|
|
+ break;
|
|
+
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
|
|
+ WREG32_P(R600_AUDIO_TIMING, 0x100, ~0x301);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ DRM_ERROR("Unsupported encoder type 0x%02X\n",
|
|
+ radeon_encoder->encoder_id);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ switch (r600_audio_tmds_index(encoder)) {
|
|
+ case 0:
|
|
+ WREG32(R600_AUDIO_PLL1_MUL, base_rate*50);
|
|
+ WREG32(R600_AUDIO_PLL1_DIV, clock*100);
|
|
+ WREG32(R600_AUDIO_CLK_SRCSEL, 0);
|
|
+ break;
|
|
+
|
|
+ case 1:
|
|
+ WREG32(R600_AUDIO_PLL2_MUL, base_rate*50);
|
|
+ WREG32(R600_AUDIO_PLL2_DIV, clock*100);
|
|
+ WREG32(R600_AUDIO_CLK_SRCSEL, 1);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * release the audio timer
|
|
+ * TODO: How to do this correctly on SMP systems?
|
|
+ */
|
|
+void r600_audio_fini(struct radeon_device *rdev)
|
|
+{
|
|
+ if (!r600_audio_chipset_supported(rdev))
|
|
+ return;
|
|
+
|
|
+ del_timer(&rdev->audio_timer);
|
|
+ WREG32_P(R600_AUDIO_ENABLE, 0x0, ~0x81000000);
|
|
+}
|
|
diff --git a/drivers/gpu/drm/radeon/r600_blit_kms.c b/drivers/gpu/drm/radeon/r600_blit_kms.c
|
|
index dbf716e..ec49dad 100644
|
|
--- a/drivers/gpu/drm/radeon/r600_blit_kms.c
|
|
+++ b/drivers/gpu/drm/radeon/r600_blit_kms.c
|
|
@@ -403,8 +403,6 @@ set_default_state(struct radeon_device *rdev)
|
|
radeon_ring_write(rdev, upper_32_bits(gpu_addr) & 0xFF);
|
|
radeon_ring_write(rdev, dwords);
|
|
|
|
- radeon_ring_write(rdev, PACKET3(PACKET3_EVENT_WRITE, 0));
|
|
- radeon_ring_write(rdev, CACHE_FLUSH_AND_INV_EVENT);
|
|
/* SQ config */
|
|
radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONFIG_REG, 6));
|
|
radeon_ring_write(rdev, (SQ_CONFIG - PACKET3_SET_CONFIG_REG_OFFSET) >> 2);
|
|
@@ -449,6 +447,7 @@ int r600_blit_init(struct radeon_device *rdev)
|
|
u32 packet2s[16];
|
|
int num_packet2s = 0;
|
|
|
|
+ mutex_init(&rdev->r600_blit.mutex);
|
|
rdev->r600_blit.state_offset = 0;
|
|
|
|
if (rdev->family >= CHIP_RV770)
|
|
@@ -473,9 +472,8 @@ int r600_blit_init(struct radeon_device *rdev)
|
|
obj_size += r6xx_ps_size * 4;
|
|
obj_size = ALIGN(obj_size, 256);
|
|
|
|
- r = radeon_object_create(rdev, NULL, obj_size,
|
|
- true, RADEON_GEM_DOMAIN_VRAM,
|
|
- false, &rdev->r600_blit.shader_obj);
|
|
+ r = radeon_bo_create(rdev, NULL, obj_size, true, RADEON_GEM_DOMAIN_VRAM,
|
|
+ &rdev->r600_blit.shader_obj);
|
|
if (r) {
|
|
DRM_ERROR("r600 failed to allocate shader\n");
|
|
return r;
|
|
@@ -485,12 +483,14 @@ int r600_blit_init(struct radeon_device *rdev)
|
|
obj_size,
|
|
rdev->r600_blit.vs_offset, rdev->r600_blit.ps_offset);
|
|
|
|
- r = radeon_object_kmap(rdev->r600_blit.shader_obj, &ptr);
|
|
+ r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
|
|
+ if (unlikely(r != 0))
|
|
+ return r;
|
|
+ r = radeon_bo_kmap(rdev->r600_blit.shader_obj, &ptr);
|
|
if (r) {
|
|
DRM_ERROR("failed to map blit object %d\n", r);
|
|
return r;
|
|
}
|
|
-
|
|
if (rdev->family >= CHIP_RV770)
|
|
memcpy_toio(ptr + rdev->r600_blit.state_offset,
|
|
r7xx_default_state, rdev->r600_blit.state_len * 4);
|
|
@@ -500,19 +500,28 @@ int r600_blit_init(struct radeon_device *rdev)
|
|
if (num_packet2s)
|
|
memcpy_toio(ptr + rdev->r600_blit.state_offset + (rdev->r600_blit.state_len * 4),
|
|
packet2s, num_packet2s * 4);
|
|
-
|
|
-
|
|
memcpy(ptr + rdev->r600_blit.vs_offset, r6xx_vs, r6xx_vs_size * 4);
|
|
memcpy(ptr + rdev->r600_blit.ps_offset, r6xx_ps, r6xx_ps_size * 4);
|
|
-
|
|
- radeon_object_kunmap(rdev->r600_blit.shader_obj);
|
|
+ radeon_bo_kunmap(rdev->r600_blit.shader_obj);
|
|
+ radeon_bo_unreserve(rdev->r600_blit.shader_obj);
|
|
return 0;
|
|
}
|
|
|
|
void r600_blit_fini(struct radeon_device *rdev)
|
|
{
|
|
- radeon_object_unpin(rdev->r600_blit.shader_obj);
|
|
- radeon_object_unref(&rdev->r600_blit.shader_obj);
|
|
+ int r;
|
|
+
|
|
+ if (rdev->r600_blit.shader_obj == NULL)
|
|
+ return;
|
|
+ /* If we can't reserve the bo, unref should be enough to destroy
|
|
+ * it when it becomes idle.
|
|
+ */
|
|
+ r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
|
|
+ if (!r) {
|
|
+ radeon_bo_unpin(rdev->r600_blit.shader_obj);
|
|
+ radeon_bo_unreserve(rdev->r600_blit.shader_obj);
|
|
+ }
|
|
+ radeon_bo_unref(&rdev->r600_blit.shader_obj);
|
|
}
|
|
|
|
int r600_vb_ib_get(struct radeon_device *rdev)
|
|
@@ -532,9 +541,6 @@ int r600_vb_ib_get(struct radeon_device *rdev)
|
|
void r600_vb_ib_put(struct radeon_device *rdev)
|
|
{
|
|
radeon_fence_emit(rdev, rdev->r600_blit.vb_ib->fence);
|
|
- mutex_lock(&rdev->ib_pool.mutex);
|
|
- list_add_tail(&rdev->r600_blit.vb_ib->list, &rdev->ib_pool.scheduled_ibs);
|
|
- mutex_unlock(&rdev->ib_pool.mutex);
|
|
radeon_ib_free(rdev, &rdev->r600_blit.vb_ib);
|
|
}
|
|
|
|
@@ -547,7 +553,8 @@ int r600_blit_prepare_copy(struct radeon_device *rdev, int size_bytes)
|
|
int dwords_per_loop = 76, num_loops;
|
|
|
|
r = r600_vb_ib_get(rdev);
|
|
- WARN_ON(r);
|
|
+ if (r)
|
|
+ return r;
|
|
|
|
/* set_render_target emits 2 extra dwords on rv6xx */
|
|
if (rdev->family > CHIP_R600 && rdev->family < CHIP_RV770)
|
|
@@ -569,11 +576,12 @@ int r600_blit_prepare_copy(struct radeon_device *rdev, int size_bytes)
|
|
ring_size = num_loops * dwords_per_loop;
|
|
/* set default + shaders */
|
|
ring_size += 40; /* shaders + def state */
|
|
- ring_size += 3; /* fence emit for VB IB */
|
|
+ ring_size += 12; /* fence emit for VB IB */
|
|
ring_size += 5; /* done copy */
|
|
- ring_size += 3; /* fence emit for done copy */
|
|
+ ring_size += 12; /* fence emit for done copy */
|
|
r = radeon_ring_lock(rdev, ring_size);
|
|
- WARN_ON(r);
|
|
+ if (r)
|
|
+ return r;
|
|
|
|
set_default_state(rdev); /* 14 */
|
|
set_shaders(rdev); /* 26 */
|
|
@@ -584,13 +592,6 @@ void r600_blit_done_copy(struct radeon_device *rdev, struct radeon_fence *fence)
|
|
{
|
|
int r;
|
|
|
|
- radeon_ring_write(rdev, PACKET3(PACKET3_EVENT_WRITE, 0));
|
|
- radeon_ring_write(rdev, CACHE_FLUSH_AND_INV_EVENT);
|
|
- /* wait for 3D idle clean */
|
|
- radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONFIG_REG, 1));
|
|
- radeon_ring_write(rdev, (WAIT_UNTIL - PACKET3_SET_CONFIG_REG_OFFSET) >> 2);
|
|
- radeon_ring_write(rdev, WAIT_3D_IDLE_bit | WAIT_3D_IDLECLEAN_bit);
|
|
-
|
|
if (rdev->r600_blit.vb_ib)
|
|
r600_vb_ib_put(rdev);
|
|
|
|
diff --git a/drivers/gpu/drm/radeon/r600_blit_shaders.c b/drivers/gpu/drm/radeon/r600_blit_shaders.c
|
|
index d745e81..a112c59 100644
|
|
--- a/drivers/gpu/drm/radeon/r600_blit_shaders.c
|
|
+++ b/drivers/gpu/drm/radeon/r600_blit_shaders.c
|
|
@@ -9,11 +9,6 @@ const u32 r6xx_default_state[] =
|
|
0xc0012800,
|
|
0x80000000,
|
|
0x80000000,
|
|
- 0xc0004600,
|
|
- 0x00000016,
|
|
- 0xc0016800,
|
|
- 0x00000010,
|
|
- 0x00028000,
|
|
0xc0016800,
|
|
0x00000010,
|
|
0x00008000,
|
|
@@ -531,11 +526,6 @@ const u32 r7xx_default_state[] =
|
|
0xc0012800,
|
|
0x80000000,
|
|
0x80000000,
|
|
- 0xc0004600,
|
|
- 0x00000016,
|
|
- 0xc0016800,
|
|
- 0x00000010,
|
|
- 0x00028000,
|
|
0xc0016800,
|
|
0x00000010,
|
|
0x00008000,
|
|
diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c
|
|
index 0d82076..e4c45ec 100644
|
|
--- a/drivers/gpu/drm/radeon/r600_cs.c
|
|
+++ b/drivers/gpu/drm/radeon/r600_cs.c
|
|
@@ -36,6 +36,10 @@ static int r600_cs_packet_next_reloc_nomm(struct radeon_cs_parser *p,
|
|
typedef int (*next_reloc_t)(struct radeon_cs_parser*, struct radeon_cs_reloc**);
|
|
static next_reloc_t r600_cs_packet_next_reloc = &r600_cs_packet_next_reloc_mm;
|
|
|
|
+struct r600_cs_track {
|
|
+ u32 cb_color0_base_last;
|
|
+};
|
|
+
|
|
/**
|
|
* r600_cs_packet_parse() - parse cp packet and point ib index to next packet
|
|
* @parser: parser structure holding parsing context.
|
|
@@ -170,13 +174,35 @@ static int r600_cs_packet_next_reloc_nomm(struct radeon_cs_parser *p,
|
|
idx, relocs_chunk->length_dw);
|
|
return -EINVAL;
|
|
}
|
|
- *cs_reloc = &p->relocs[0];
|
|
+ *cs_reloc = p->relocs;
|
|
(*cs_reloc)->lobj.gpu_offset = (u64)relocs_chunk->kdata[idx + 3] << 32;
|
|
(*cs_reloc)->lobj.gpu_offset |= relocs_chunk->kdata[idx + 0];
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
+ * r600_cs_packet_next_is_pkt3_nop() - test if next packet is packet3 nop for reloc
|
|
+ * @parser: parser structure holding parsing context.
|
|
+ *
|
|
+ * Check next packet is relocation packet3, do bo validation and compute
|
|
+ * GPU offset using the provided start.
|
|
+ **/
|
|
+static inline int r600_cs_packet_next_is_pkt3_nop(struct radeon_cs_parser *p)
|
|
+{
|
|
+ struct radeon_cs_packet p3reloc;
|
|
+ int r;
|
|
+
|
|
+ r = r600_cs_packet_parse(p, &p3reloc, p->idx);
|
|
+ if (r) {
|
|
+ return 0;
|
|
+ }
|
|
+ if (p3reloc.type != PACKET_TYPE3 || p3reloc.opcode != PACKET3_NOP) {
|
|
+ return 0;
|
|
+ }
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/**
|
|
* r600_cs_packet_next_vline() - parse userspace VLINE packet
|
|
* @parser: parser structure holding parsing context.
|
|
*
|
|
@@ -337,6 +363,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
|
|
struct radeon_cs_packet *pkt)
|
|
{
|
|
struct radeon_cs_reloc *reloc;
|
|
+ struct r600_cs_track *track;
|
|
volatile u32 *ib;
|
|
unsigned idx;
|
|
unsigned i;
|
|
@@ -344,6 +371,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
|
|
int r;
|
|
u32 idx_value;
|
|
|
|
+ track = (struct r600_cs_track *)p->track;
|
|
ib = p->ib->ptr;
|
|
idx = pkt->idx + 1;
|
|
idx_value = radeon_get_ib_value(p, idx);
|
|
@@ -503,9 +531,60 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
|
|
for (i = 0; i < pkt->count; i++) {
|
|
reg = start_reg + (4 * i);
|
|
switch (reg) {
|
|
+ /* This register were added late, there is userspace
|
|
+ * which does provide relocation for those but set
|
|
+ * 0 offset. In order to avoid breaking old userspace
|
|
+ * we detect this and set address to point to last
|
|
+ * CB_COLOR0_BASE, note that if userspace doesn't set
|
|
+ * CB_COLOR0_BASE before this register we will report
|
|
+ * error. Old userspace always set CB_COLOR0_BASE
|
|
+ * before any of this.
|
|
+ */
|
|
+ case R_0280E0_CB_COLOR0_FRAG:
|
|
+ case R_0280E4_CB_COLOR1_FRAG:
|
|
+ case R_0280E8_CB_COLOR2_FRAG:
|
|
+ case R_0280EC_CB_COLOR3_FRAG:
|
|
+ case R_0280F0_CB_COLOR4_FRAG:
|
|
+ case R_0280F4_CB_COLOR5_FRAG:
|
|
+ case R_0280F8_CB_COLOR6_FRAG:
|
|
+ case R_0280FC_CB_COLOR7_FRAG:
|
|
+ case R_0280C0_CB_COLOR0_TILE:
|
|
+ case R_0280C4_CB_COLOR1_TILE:
|
|
+ case R_0280C8_CB_COLOR2_TILE:
|
|
+ case R_0280CC_CB_COLOR3_TILE:
|
|
+ case R_0280D0_CB_COLOR4_TILE:
|
|
+ case R_0280D4_CB_COLOR5_TILE:
|
|
+ case R_0280D8_CB_COLOR6_TILE:
|
|
+ case R_0280DC_CB_COLOR7_TILE:
|
|
+ if (!r600_cs_packet_next_is_pkt3_nop(p)) {
|
|
+ if (!track->cb_color0_base_last) {
|
|
+ dev_err(p->dev, "Broken old userspace ? no cb_color0_base supplied before trying to write 0x%08X\n", reg);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ ib[idx+1+i] = track->cb_color0_base_last;
|
|
+ printk_once(KERN_WARNING "radeon: You have old & broken userspace "
|
|
+ "please consider updating mesa & xf86-video-ati\n");
|
|
+ } else {
|
|
+ r = r600_cs_packet_next_reloc(p, &reloc);
|
|
+ if (r) {
|
|
+ dev_err(p->dev, "bad SET_CONTEXT_REG 0x%04X\n", reg);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ ib[idx+1+i] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
|
|
+ }
|
|
+ break;
|
|
case DB_DEPTH_BASE:
|
|
case DB_HTILE_DATA_BASE:
|
|
case CB_COLOR0_BASE:
|
|
+ r = r600_cs_packet_next_reloc(p, &reloc);
|
|
+ if (r) {
|
|
+ DRM_ERROR("bad SET_CONTEXT_REG "
|
|
+ "0x%04X\n", reg);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ ib[idx+1+i] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
|
|
+ track->cb_color0_base_last = ib[idx+1+i];
|
|
+ break;
|
|
case CB_COLOR1_BASE:
|
|
case CB_COLOR2_BASE:
|
|
case CB_COLOR3_BASE:
|
|
@@ -678,8 +757,11 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
|
|
int r600_cs_parse(struct radeon_cs_parser *p)
|
|
{
|
|
struct radeon_cs_packet pkt;
|
|
+ struct r600_cs_track *track;
|
|
int r;
|
|
|
|
+ track = kzalloc(sizeof(*track), GFP_KERNEL);
|
|
+ p->track = track;
|
|
do {
|
|
r = r600_cs_packet_parse(p, &pkt, p->idx);
|
|
if (r) {
|
|
@@ -717,7 +799,7 @@ static int r600_cs_parser_relocs_legacy(struct radeon_cs_parser *p)
|
|
if (p->chunk_relocs_idx == -1) {
|
|
return 0;
|
|
}
|
|
- p->relocs = kcalloc(1, sizeof(struct radeon_cs_reloc), GFP_KERNEL);
|
|
+ p->relocs = kzalloc(sizeof(struct radeon_cs_reloc), GFP_KERNEL);
|
|
if (p->relocs == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
@@ -757,6 +839,7 @@ int r600_cs_legacy(struct drm_device *dev, void *data, struct drm_file *filp,
|
|
/* initialize parser */
|
|
memset(&parser, 0, sizeof(struct radeon_cs_parser));
|
|
parser.filp = filp;
|
|
+ parser.dev = &dev->pdev->dev;
|
|
parser.rdev = NULL;
|
|
parser.family = family;
|
|
parser.ib = &fake_ib;
|
|
diff --git a/drivers/gpu/drm/radeon/r600_hdmi.c b/drivers/gpu/drm/radeon/r600_hdmi.c
|
|
new file mode 100644
|
|
index 0000000..fcc949d
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/radeon/r600_hdmi.c
|
|
@@ -0,0 +1,506 @@
|
|
+/*
|
|
+ * Copyright 2008 Advanced Micro Devices, Inc.
|
|
+ * Copyright 2008 Red Hat Inc.
|
|
+ * Copyright 2009 Christian König.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice shall be included in
|
|
+ * all copies or substantial portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
+ * OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ * Authors: Christian König
|
|
+ */
|
|
+#include "drmP.h"
|
|
+#include "radeon_drm.h"
|
|
+#include "radeon.h"
|
|
+#include "atom.h"
|
|
+
|
|
+/*
|
|
+ * HDMI color format
|
|
+ */
|
|
+enum r600_hdmi_color_format {
|
|
+ RGB = 0,
|
|
+ YCC_422 = 1,
|
|
+ YCC_444 = 2
|
|
+};
|
|
+
|
|
+/*
|
|
+ * IEC60958 status bits
|
|
+ */
|
|
+enum r600_hdmi_iec_status_bits {
|
|
+ AUDIO_STATUS_DIG_ENABLE = 0x01,
|
|
+ AUDIO_STATUS_V = 0x02,
|
|
+ AUDIO_STATUS_VCFG = 0x04,
|
|
+ AUDIO_STATUS_EMPHASIS = 0x08,
|
|
+ AUDIO_STATUS_COPYRIGHT = 0x10,
|
|
+ AUDIO_STATUS_NONAUDIO = 0x20,
|
|
+ AUDIO_STATUS_PROFESSIONAL = 0x40,
|
|
+ AUDIO_STATUS_LEVEL = 0x80
|
|
+};
|
|
+
|
|
+struct {
|
|
+ uint32_t Clock;
|
|
+
|
|
+ int N_32kHz;
|
|
+ int CTS_32kHz;
|
|
+
|
|
+ int N_44_1kHz;
|
|
+ int CTS_44_1kHz;
|
|
+
|
|
+ int N_48kHz;
|
|
+ int CTS_48kHz;
|
|
+
|
|
+} r600_hdmi_ACR[] = {
|
|
+ /* 32kHz 44.1kHz 48kHz */
|
|
+ /* Clock N CTS N CTS N CTS */
|
|
+ { 25174, 4576, 28125, 7007, 31250, 6864, 28125 }, /* 25,20/1.001 MHz */
|
|
+ { 25200, 4096, 25200, 6272, 28000, 6144, 25200 }, /* 25.20 MHz */
|
|
+ { 27000, 4096, 27000, 6272, 30000, 6144, 27000 }, /* 27.00 MHz */
|
|
+ { 27027, 4096, 27027, 6272, 30030, 6144, 27027 }, /* 27.00*1.001 MHz */
|
|
+ { 54000, 4096, 54000, 6272, 60000, 6144, 54000 }, /* 54.00 MHz */
|
|
+ { 54054, 4096, 54054, 6272, 60060, 6144, 54054 }, /* 54.00*1.001 MHz */
|
|
+ { 74175, 11648, 210937, 17836, 234375, 11648, 140625 }, /* 74.25/1.001 MHz */
|
|
+ { 74250, 4096, 74250, 6272, 82500, 6144, 74250 }, /* 74.25 MHz */
|
|
+ { 148351, 11648, 421875, 8918, 234375, 5824, 140625 }, /* 148.50/1.001 MHz */
|
|
+ { 148500, 4096, 148500, 6272, 165000, 6144, 148500 }, /* 148.50 MHz */
|
|
+ { 0, 4096, 0, 6272, 0, 6144, 0 } /* Other */
|
|
+};
|
|
+
|
|
+/*
|
|
+ * calculate CTS value if it's not found in the table
|
|
+ */
|
|
+static void r600_hdmi_calc_CTS(uint32_t clock, int *CTS, int N, int freq)
|
|
+{
|
|
+ if (*CTS == 0)
|
|
+ *CTS = clock*N/(128*freq)*1000;
|
|
+ DRM_DEBUG("Using ACR timing N=%d CTS=%d for frequency %d\n",
|
|
+ N, *CTS, freq);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * update the N and CTS parameters for a given pixel clock rate
|
|
+ */
|
|
+static void r600_hdmi_update_ACR(struct drm_encoder *encoder, uint32_t clock)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
+ uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset;
|
|
+ int CTS;
|
|
+ int N;
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; r600_hdmi_ACR[i].Clock != clock && r600_hdmi_ACR[i].Clock != 0; i++);
|
|
+
|
|
+ CTS = r600_hdmi_ACR[i].CTS_32kHz;
|
|
+ N = r600_hdmi_ACR[i].N_32kHz;
|
|
+ r600_hdmi_calc_CTS(clock, &CTS, N, 32000);
|
|
+ WREG32(offset+R600_HDMI_32kHz_CTS, CTS << 12);
|
|
+ WREG32(offset+R600_HDMI_32kHz_N, N);
|
|
+
|
|
+ CTS = r600_hdmi_ACR[i].CTS_44_1kHz;
|
|
+ N = r600_hdmi_ACR[i].N_44_1kHz;
|
|
+ r600_hdmi_calc_CTS(clock, &CTS, N, 44100);
|
|
+ WREG32(offset+R600_HDMI_44_1kHz_CTS, CTS << 12);
|
|
+ WREG32(offset+R600_HDMI_44_1kHz_N, N);
|
|
+
|
|
+ CTS = r600_hdmi_ACR[i].CTS_48kHz;
|
|
+ N = r600_hdmi_ACR[i].N_48kHz;
|
|
+ r600_hdmi_calc_CTS(clock, &CTS, N, 48000);
|
|
+ WREG32(offset+R600_HDMI_48kHz_CTS, CTS << 12);
|
|
+ WREG32(offset+R600_HDMI_48kHz_N, N);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * calculate the crc for a given info frame
|
|
+ */
|
|
+static void r600_hdmi_infoframe_checksum(uint8_t packetType,
|
|
+ uint8_t versionNumber,
|
|
+ uint8_t length,
|
|
+ uint8_t *frame)
|
|
+{
|
|
+ int i;
|
|
+ frame[0] = packetType + versionNumber + length;
|
|
+ for (i = 1; i <= length; i++)
|
|
+ frame[0] += frame[i];
|
|
+ frame[0] = 0x100 - frame[0];
|
|
+}
|
|
+
|
|
+/*
|
|
+ * build a HDMI Video Info Frame
|
|
+ */
|
|
+static void r600_hdmi_videoinfoframe(
|
|
+ struct drm_encoder *encoder,
|
|
+ enum r600_hdmi_color_format color_format,
|
|
+ int active_information_present,
|
|
+ uint8_t active_format_aspect_ratio,
|
|
+ uint8_t scan_information,
|
|
+ uint8_t colorimetry,
|
|
+ uint8_t ex_colorimetry,
|
|
+ uint8_t quantization,
|
|
+ int ITC,
|
|
+ uint8_t picture_aspect_ratio,
|
|
+ uint8_t video_format_identification,
|
|
+ uint8_t pixel_repetition,
|
|
+ uint8_t non_uniform_picture_scaling,
|
|
+ uint8_t bar_info_data_valid,
|
|
+ uint16_t top_bar,
|
|
+ uint16_t bottom_bar,
|
|
+ uint16_t left_bar,
|
|
+ uint16_t right_bar
|
|
+)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
+ uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset;
|
|
+
|
|
+ uint8_t frame[14];
|
|
+
|
|
+ frame[0x0] = 0;
|
|
+ frame[0x1] =
|
|
+ (scan_information & 0x3) |
|
|
+ ((bar_info_data_valid & 0x3) << 2) |
|
|
+ ((active_information_present & 0x1) << 4) |
|
|
+ ((color_format & 0x3) << 5);
|
|
+ frame[0x2] =
|
|
+ (active_format_aspect_ratio & 0xF) |
|
|
+ ((picture_aspect_ratio & 0x3) << 4) |
|
|
+ ((colorimetry & 0x3) << 6);
|
|
+ frame[0x3] =
|
|
+ (non_uniform_picture_scaling & 0x3) |
|
|
+ ((quantization & 0x3) << 2) |
|
|
+ ((ex_colorimetry & 0x7) << 4) |
|
|
+ ((ITC & 0x1) << 7);
|
|
+ frame[0x4] = (video_format_identification & 0x7F);
|
|
+ frame[0x5] = (pixel_repetition & 0xF);
|
|
+ frame[0x6] = (top_bar & 0xFF);
|
|
+ frame[0x7] = (top_bar >> 8);
|
|
+ frame[0x8] = (bottom_bar & 0xFF);
|
|
+ frame[0x9] = (bottom_bar >> 8);
|
|
+ frame[0xA] = (left_bar & 0xFF);
|
|
+ frame[0xB] = (left_bar >> 8);
|
|
+ frame[0xC] = (right_bar & 0xFF);
|
|
+ frame[0xD] = (right_bar >> 8);
|
|
+
|
|
+ r600_hdmi_infoframe_checksum(0x82, 0x02, 0x0D, frame);
|
|
+
|
|
+ WREG32(offset+R600_HDMI_VIDEOINFOFRAME_0,
|
|
+ frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24));
|
|
+ WREG32(offset+R600_HDMI_VIDEOINFOFRAME_1,
|
|
+ frame[0x4] | (frame[0x5] << 8) | (frame[0x6] << 16) | (frame[0x7] << 24));
|
|
+ WREG32(offset+R600_HDMI_VIDEOINFOFRAME_2,
|
|
+ frame[0x8] | (frame[0x9] << 8) | (frame[0xA] << 16) | (frame[0xB] << 24));
|
|
+ WREG32(offset+R600_HDMI_VIDEOINFOFRAME_3,
|
|
+ frame[0xC] | (frame[0xD] << 8));
|
|
+}
|
|
+
|
|
+/*
|
|
+ * build a Audio Info Frame
|
|
+ */
|
|
+static void r600_hdmi_audioinfoframe(
|
|
+ struct drm_encoder *encoder,
|
|
+ uint8_t channel_count,
|
|
+ uint8_t coding_type,
|
|
+ uint8_t sample_size,
|
|
+ uint8_t sample_frequency,
|
|
+ uint8_t format,
|
|
+ uint8_t channel_allocation,
|
|
+ uint8_t level_shift,
|
|
+ int downmix_inhibit
|
|
+)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
+ uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset;
|
|
+
|
|
+ uint8_t frame[11];
|
|
+
|
|
+ frame[0x0] = 0;
|
|
+ frame[0x1] = (channel_count & 0x7) | ((coding_type & 0xF) << 4);
|
|
+ frame[0x2] = (sample_size & 0x3) | ((sample_frequency & 0x7) << 2);
|
|
+ frame[0x3] = format;
|
|
+ frame[0x4] = channel_allocation;
|
|
+ frame[0x5] = ((level_shift & 0xF) << 3) | ((downmix_inhibit & 0x1) << 7);
|
|
+ frame[0x6] = 0;
|
|
+ frame[0x7] = 0;
|
|
+ frame[0x8] = 0;
|
|
+ frame[0x9] = 0;
|
|
+ frame[0xA] = 0;
|
|
+
|
|
+ r600_hdmi_infoframe_checksum(0x84, 0x01, 0x0A, frame);
|
|
+
|
|
+ WREG32(offset+R600_HDMI_AUDIOINFOFRAME_0,
|
|
+ frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24));
|
|
+ WREG32(offset+R600_HDMI_AUDIOINFOFRAME_1,
|
|
+ frame[0x4] | (frame[0x5] << 8) | (frame[0x6] << 16) | (frame[0x8] << 24));
|
|
+}
|
|
+
|
|
+/*
|
|
+ * test if audio buffer is filled enough to start playing
|
|
+ */
|
|
+static int r600_hdmi_is_audio_buffer_filled(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
+ uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset;
|
|
+
|
|
+ return (RREG32(offset+R600_HDMI_STATUS) & 0x10) != 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * have buffer status changed since last call?
|
|
+ */
|
|
+int r600_hdmi_buffer_status_changed(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
|
|
+ int status, result;
|
|
+
|
|
+ if (!radeon_encoder->hdmi_offset)
|
|
+ return 0;
|
|
+
|
|
+ status = r600_hdmi_is_audio_buffer_filled(encoder);
|
|
+ result = radeon_encoder->hdmi_buffer_status != status;
|
|
+ radeon_encoder->hdmi_buffer_status = status;
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * write the audio workaround status to the hardware
|
|
+ */
|
|
+void r600_hdmi_audio_workaround(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
|
|
+ uint32_t offset = radeon_encoder->hdmi_offset;
|
|
+
|
|
+ if (!offset)
|
|
+ return;
|
|
+
|
|
+ if (r600_hdmi_is_audio_buffer_filled(encoder)) {
|
|
+ /* disable audio workaround and start delivering of audio frames */
|
|
+ WREG32_P(offset+R600_HDMI_CNTL, 0x00000001, ~0x00001001);
|
|
+
|
|
+ } else if (radeon_encoder->hdmi_audio_workaround) {
|
|
+ /* enable audio workaround and start delivering of audio frames */
|
|
+ WREG32_P(offset+R600_HDMI_CNTL, 0x00001001, ~0x00001001);
|
|
+
|
|
+ } else {
|
|
+ /* disable audio workaround and stop delivering of audio frames */
|
|
+ WREG32_P(offset+R600_HDMI_CNTL, 0x00000000, ~0x00001001);
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * update the info frames with the data from the current display mode
|
|
+ */
|
|
+void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mode)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
+ uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset;
|
|
+
|
|
+ if (!offset)
|
|
+ return;
|
|
+
|
|
+ r600_audio_set_clock(encoder, mode->clock);
|
|
+
|
|
+ WREG32(offset+R600_HDMI_UNKNOWN_0, 0x1000);
|
|
+ WREG32(offset+R600_HDMI_UNKNOWN_1, 0x0);
|
|
+ WREG32(offset+R600_HDMI_UNKNOWN_2, 0x1000);
|
|
+
|
|
+ r600_hdmi_update_ACR(encoder, mode->clock);
|
|
+
|
|
+ WREG32(offset+R600_HDMI_VIDEOCNTL, 0x13);
|
|
+
|
|
+ WREG32(offset+R600_HDMI_VERSION, 0x202);
|
|
+
|
|
+ r600_hdmi_videoinfoframe(encoder, RGB, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
+
|
|
+ /* it's unknown what these bits do excatly, but it's indeed quite usefull for debugging */
|
|
+ WREG32(offset+R600_HDMI_AUDIO_DEBUG_0, 0x00FFFFFF);
|
|
+ WREG32(offset+R600_HDMI_AUDIO_DEBUG_1, 0x007FFFFF);
|
|
+ WREG32(offset+R600_HDMI_AUDIO_DEBUG_2, 0x00000001);
|
|
+ WREG32(offset+R600_HDMI_AUDIO_DEBUG_3, 0x00000001);
|
|
+
|
|
+ r600_hdmi_audio_workaround(encoder);
|
|
+
|
|
+ /* audio packets per line, does anyone know how to calc this ? */
|
|
+ WREG32_P(offset+R600_HDMI_CNTL, 0x00040000, ~0x001F0000);
|
|
+
|
|
+ /* update? reset? don't realy know */
|
|
+ WREG32_P(offset+R600_HDMI_CNTL, 0x14000000, ~0x14000000);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * update settings with current parameters from audio engine
|
|
+ */
|
|
+void r600_hdmi_update_audio_settings(struct drm_encoder *encoder,
|
|
+ int channels,
|
|
+ int rate,
|
|
+ int bps,
|
|
+ uint8_t status_bits,
|
|
+ uint8_t category_code)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
+ uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset;
|
|
+
|
|
+ uint32_t iec;
|
|
+
|
|
+ if (!offset)
|
|
+ return;
|
|
+
|
|
+ DRM_DEBUG("%s with %d channels, %d Hz sampling rate, %d bits per sample,\n",
|
|
+ r600_hdmi_is_audio_buffer_filled(encoder) ? "playing" : "stopped",
|
|
+ channels, rate, bps);
|
|
+ DRM_DEBUG("0x%02X IEC60958 status bits and 0x%02X category code\n",
|
|
+ (int)status_bits, (int)category_code);
|
|
+
|
|
+ iec = 0;
|
|
+ if (status_bits & AUDIO_STATUS_PROFESSIONAL)
|
|
+ iec |= 1 << 0;
|
|
+ if (status_bits & AUDIO_STATUS_NONAUDIO)
|
|
+ iec |= 1 << 1;
|
|
+ if (status_bits & AUDIO_STATUS_COPYRIGHT)
|
|
+ iec |= 1 << 2;
|
|
+ if (status_bits & AUDIO_STATUS_EMPHASIS)
|
|
+ iec |= 1 << 3;
|
|
+
|
|
+ iec |= category_code << 8;
|
|
+
|
|
+ switch (rate) {
|
|
+ case 32000: iec |= 0x3 << 24; break;
|
|
+ case 44100: iec |= 0x0 << 24; break;
|
|
+ case 88200: iec |= 0x8 << 24; break;
|
|
+ case 176400: iec |= 0xc << 24; break;
|
|
+ case 48000: iec |= 0x2 << 24; break;
|
|
+ case 96000: iec |= 0xa << 24; break;
|
|
+ case 192000: iec |= 0xe << 24; break;
|
|
+ }
|
|
+
|
|
+ WREG32(offset+R600_HDMI_IEC60958_1, iec);
|
|
+
|
|
+ iec = 0;
|
|
+ switch (bps) {
|
|
+ case 16: iec |= 0x2; break;
|
|
+ case 20: iec |= 0x3; break;
|
|
+ case 24: iec |= 0xb; break;
|
|
+ }
|
|
+ if (status_bits & AUDIO_STATUS_V)
|
|
+ iec |= 0x5 << 16;
|
|
+
|
|
+ WREG32_P(offset+R600_HDMI_IEC60958_2, iec, ~0x5000f);
|
|
+
|
|
+ /* 0x021 or 0x031 sets the audio frame length */
|
|
+ WREG32(offset+R600_HDMI_AUDIOCNTL, 0x31);
|
|
+ r600_hdmi_audioinfoframe(encoder, channels-1, 0, 0, 0, 0, 0, 0, 0);
|
|
+
|
|
+ r600_hdmi_audio_workaround(encoder);
|
|
+
|
|
+ /* update? reset? don't realy know */
|
|
+ WREG32_P(offset+R600_HDMI_CNTL, 0x04000000, ~0x04000000);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * enable/disable the HDMI engine
|
|
+ */
|
|
+void r600_hdmi_enable(struct drm_encoder *encoder, int enable)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
|
|
+ uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset;
|
|
+
|
|
+ if (!offset)
|
|
+ return;
|
|
+
|
|
+ DRM_DEBUG("%s HDMI interface @ 0x%04X\n", enable ? "Enabling" : "Disabling", offset);
|
|
+
|
|
+ /* some version of atombios ignore the enable HDMI flag
|
|
+ * so enabling/disabling HDMI was moved here for TMDS1+2 */
|
|
+ switch (radeon_encoder->encoder_id) {
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
|
|
+ WREG32_P(AVIVO_TMDSA_CNTL, enable ? 0x4 : 0x0, ~0x4);
|
|
+ WREG32(offset+R600_HDMI_ENABLE, enable ? 0x101 : 0x0);
|
|
+ break;
|
|
+
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
|
|
+ WREG32_P(AVIVO_LVTMA_CNTL, enable ? 0x4 : 0x0, ~0x4);
|
|
+ WREG32(offset+R600_HDMI_ENABLE, enable ? 0x105 : 0x0);
|
|
+ break;
|
|
+
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
|
|
+ /* This part is doubtfull in my opinion */
|
|
+ WREG32(offset+R600_HDMI_ENABLE, enable ? 0x110 : 0x0);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ DRM_ERROR("unknown HDMI output type\n");
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * determin at which register offset the HDMI encoder is
|
|
+ */
|
|
+void r600_hdmi_init(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
|
|
+
|
|
+ switch (radeon_encoder->encoder_id) {
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
|
|
+ radeon_encoder->hdmi_offset = R600_HDMI_TMDS1;
|
|
+ break;
|
|
+
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
|
|
+ switch (r600_audio_tmds_index(encoder)) {
|
|
+ case 0:
|
|
+ radeon_encoder->hdmi_offset = R600_HDMI_TMDS1;
|
|
+ break;
|
|
+ case 1:
|
|
+ radeon_encoder->hdmi_offset = R600_HDMI_TMDS2;
|
|
+ break;
|
|
+ default:
|
|
+ radeon_encoder->hdmi_offset = 0;
|
|
+ break;
|
|
+ }
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
|
|
+ radeon_encoder->hdmi_offset = R600_HDMI_TMDS2;
|
|
+ break;
|
|
+
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
|
|
+ radeon_encoder->hdmi_offset = R600_HDMI_DIG;
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ radeon_encoder->hdmi_offset = 0;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ DRM_DEBUG("using HDMI engine at offset 0x%04X for encoder 0x%x\n",
|
|
+ radeon_encoder->hdmi_offset, radeon_encoder->encoder_id);
|
|
+
|
|
+ /* TODO: make this configureable */
|
|
+ radeon_encoder->hdmi_audio_workaround = 0;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/radeon/r600_reg.h b/drivers/gpu/drm/radeon/r600_reg.h
|
|
index e2d1f5f..d0e28ff 100644
|
|
--- a/drivers/gpu/drm/radeon/r600_reg.h
|
|
+++ b/drivers/gpu/drm/radeon/r600_reg.h
|
|
@@ -110,5 +110,79 @@
|
|
#define R600_BIOS_6_SCRATCH 0x173c
|
|
#define R600_BIOS_7_SCRATCH 0x1740
|
|
|
|
+/* Audio, these regs were reverse enginered,
|
|
+ * so the chance is high that the naming is wrong
|
|
+ * R6xx+ ??? */
|
|
+
|
|
+/* Audio clocks */
|
|
+#define R600_AUDIO_PLL1_MUL 0x0514
|
|
+#define R600_AUDIO_PLL1_DIV 0x0518
|
|
+#define R600_AUDIO_PLL2_MUL 0x0524
|
|
+#define R600_AUDIO_PLL2_DIV 0x0528
|
|
+#define R600_AUDIO_CLK_SRCSEL 0x0534
|
|
+
|
|
+/* Audio general */
|
|
+#define R600_AUDIO_ENABLE 0x7300
|
|
+#define R600_AUDIO_TIMING 0x7344
|
|
+
|
|
+/* Audio params */
|
|
+#define R600_AUDIO_VENDOR_ID 0x7380
|
|
+#define R600_AUDIO_REVISION_ID 0x7384
|
|
+#define R600_AUDIO_ROOT_NODE_COUNT 0x7388
|
|
+#define R600_AUDIO_NID1_NODE_COUNT 0x738c
|
|
+#define R600_AUDIO_NID1_TYPE 0x7390
|
|
+#define R600_AUDIO_SUPPORTED_SIZE_RATE 0x7394
|
|
+#define R600_AUDIO_SUPPORTED_CODEC 0x7398
|
|
+#define R600_AUDIO_SUPPORTED_POWER_STATES 0x739c
|
|
+#define R600_AUDIO_NID2_CAPS 0x73a0
|
|
+#define R600_AUDIO_NID3_CAPS 0x73a4
|
|
+#define R600_AUDIO_NID3_PIN_CAPS 0x73a8
|
|
+
|
|
+/* Audio conn list */
|
|
+#define R600_AUDIO_CONN_LIST_LEN 0x73ac
|
|
+#define R600_AUDIO_CONN_LIST 0x73b0
|
|
+
|
|
+/* Audio verbs */
|
|
+#define R600_AUDIO_RATE_BPS_CHANNEL 0x73c0
|
|
+#define R600_AUDIO_PLAYING 0x73c4
|
|
+#define R600_AUDIO_IMPLEMENTATION_ID 0x73c8
|
|
+#define R600_AUDIO_CONFIG_DEFAULT 0x73cc
|
|
+#define R600_AUDIO_PIN_SENSE 0x73d0
|
|
+#define R600_AUDIO_PIN_WIDGET_CNTL 0x73d4
|
|
+#define R600_AUDIO_STATUS_BITS 0x73d8
|
|
+
|
|
+/* HDMI base register addresses */
|
|
+#define R600_HDMI_TMDS1 0x7400
|
|
+#define R600_HDMI_TMDS2 0x7700
|
|
+#define R600_HDMI_DIG 0x7800
|
|
+
|
|
+/* HDMI registers */
|
|
+#define R600_HDMI_ENABLE 0x00
|
|
+#define R600_HDMI_STATUS 0x04
|
|
+#define R600_HDMI_CNTL 0x08
|
|
+#define R600_HDMI_UNKNOWN_0 0x0C
|
|
+#define R600_HDMI_AUDIOCNTL 0x10
|
|
+#define R600_HDMI_VIDEOCNTL 0x14
|
|
+#define R600_HDMI_VERSION 0x18
|
|
+#define R600_HDMI_UNKNOWN_1 0x28
|
|
+#define R600_HDMI_VIDEOINFOFRAME_0 0x54
|
|
+#define R600_HDMI_VIDEOINFOFRAME_1 0x58
|
|
+#define R600_HDMI_VIDEOINFOFRAME_2 0x5c
|
|
+#define R600_HDMI_VIDEOINFOFRAME_3 0x60
|
|
+#define R600_HDMI_32kHz_CTS 0xac
|
|
+#define R600_HDMI_32kHz_N 0xb0
|
|
+#define R600_HDMI_44_1kHz_CTS 0xb4
|
|
+#define R600_HDMI_44_1kHz_N 0xb8
|
|
+#define R600_HDMI_48kHz_CTS 0xbc
|
|
+#define R600_HDMI_48kHz_N 0xc0
|
|
+#define R600_HDMI_AUDIOINFOFRAME_0 0xcc
|
|
+#define R600_HDMI_AUDIOINFOFRAME_1 0xd0
|
|
+#define R600_HDMI_IEC60958_1 0xd4
|
|
+#define R600_HDMI_IEC60958_2 0xd8
|
|
+#define R600_HDMI_UNKNOWN_2 0xdc
|
|
+#define R600_HDMI_AUDIO_DEBUG_0 0xe0
|
|
+#define R600_HDMI_AUDIO_DEBUG_1 0xe4
|
|
+#define R600_HDMI_AUDIO_DEBUG_2 0xe8
|
|
+#define R600_HDMI_AUDIO_DEBUG_3 0xec
|
|
|
|
#endif
|
|
diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h
|
|
index 27ab428..3048088 100644
|
|
--- a/drivers/gpu/drm/radeon/r600d.h
|
|
+++ b/drivers/gpu/drm/radeon/r600d.h
|
|
@@ -456,7 +456,215 @@
|
|
#define WAIT_2D_IDLECLEAN_bit (1 << 16)
|
|
#define WAIT_3D_IDLECLEAN_bit (1 << 17)
|
|
|
|
-
|
|
+#define IH_RB_CNTL 0x3e00
|
|
+# define IH_RB_ENABLE (1 << 0)
|
|
+# define IH_IB_SIZE(x) ((x) << 1) /* log2 */
|
|
+# define IH_RB_FULL_DRAIN_ENABLE (1 << 6)
|
|
+# define IH_WPTR_WRITEBACK_ENABLE (1 << 8)
|
|
+# define IH_WPTR_WRITEBACK_TIMER(x) ((x) << 9) /* log2 */
|
|
+# define IH_WPTR_OVERFLOW_ENABLE (1 << 16)
|
|
+# define IH_WPTR_OVERFLOW_CLEAR (1 << 31)
|
|
+#define IH_RB_BASE 0x3e04
|
|
+#define IH_RB_RPTR 0x3e08
|
|
+#define IH_RB_WPTR 0x3e0c
|
|
+# define RB_OVERFLOW (1 << 0)
|
|
+# define WPTR_OFFSET_MASK 0x3fffc
|
|
+#define IH_RB_WPTR_ADDR_HI 0x3e10
|
|
+#define IH_RB_WPTR_ADDR_LO 0x3e14
|
|
+#define IH_CNTL 0x3e18
|
|
+# define ENABLE_INTR (1 << 0)
|
|
+# define IH_MC_SWAP(x) ((x) << 2)
|
|
+# define IH_MC_SWAP_NONE 0
|
|
+# define IH_MC_SWAP_16BIT 1
|
|
+# define IH_MC_SWAP_32BIT 2
|
|
+# define IH_MC_SWAP_64BIT 3
|
|
+# define RPTR_REARM (1 << 4)
|
|
+# define MC_WRREQ_CREDIT(x) ((x) << 15)
|
|
+# define MC_WR_CLEAN_CNT(x) ((x) << 20)
|
|
+
|
|
+#define RLC_CNTL 0x3f00
|
|
+# define RLC_ENABLE (1 << 0)
|
|
+#define RLC_HB_BASE 0x3f10
|
|
+#define RLC_HB_CNTL 0x3f0c
|
|
+#define RLC_HB_RPTR 0x3f20
|
|
+#define RLC_HB_WPTR 0x3f1c
|
|
+#define RLC_HB_WPTR_LSB_ADDR 0x3f14
|
|
+#define RLC_HB_WPTR_MSB_ADDR 0x3f18
|
|
+#define RLC_MC_CNTL 0x3f44
|
|
+#define RLC_UCODE_CNTL 0x3f48
|
|
+#define RLC_UCODE_ADDR 0x3f2c
|
|
+#define RLC_UCODE_DATA 0x3f30
|
|
+
|
|
+#define SRBM_SOFT_RESET 0xe60
|
|
+# define SOFT_RESET_RLC (1 << 13)
|
|
+
|
|
+#define CP_INT_CNTL 0xc124
|
|
+# define CNTX_BUSY_INT_ENABLE (1 << 19)
|
|
+# define CNTX_EMPTY_INT_ENABLE (1 << 20)
|
|
+# define SCRATCH_INT_ENABLE (1 << 25)
|
|
+# define TIME_STAMP_INT_ENABLE (1 << 26)
|
|
+# define IB2_INT_ENABLE (1 << 29)
|
|
+# define IB1_INT_ENABLE (1 << 30)
|
|
+# define RB_INT_ENABLE (1 << 31)
|
|
+#define CP_INT_STATUS 0xc128
|
|
+# define SCRATCH_INT_STAT (1 << 25)
|
|
+# define TIME_STAMP_INT_STAT (1 << 26)
|
|
+# define IB2_INT_STAT (1 << 29)
|
|
+# define IB1_INT_STAT (1 << 30)
|
|
+# define RB_INT_STAT (1 << 31)
|
|
+
|
|
+#define GRBM_INT_CNTL 0x8060
|
|
+# define RDERR_INT_ENABLE (1 << 0)
|
|
+# define WAIT_COUNT_TIMEOUT_INT_ENABLE (1 << 1)
|
|
+# define GUI_IDLE_INT_ENABLE (1 << 19)
|
|
+
|
|
+#define INTERRUPT_CNTL 0x5468
|
|
+# define IH_DUMMY_RD_OVERRIDE (1 << 0)
|
|
+# define IH_DUMMY_RD_EN (1 << 1)
|
|
+# define IH_REQ_NONSNOOP_EN (1 << 3)
|
|
+# define GEN_IH_INT_EN (1 << 8)
|
|
+#define INTERRUPT_CNTL2 0x546c
|
|
+
|
|
+#define D1MODE_VBLANK_STATUS 0x6534
|
|
+#define D2MODE_VBLANK_STATUS 0x6d34
|
|
+# define DxMODE_VBLANK_OCCURRED (1 << 0)
|
|
+# define DxMODE_VBLANK_ACK (1 << 4)
|
|
+# define DxMODE_VBLANK_STAT (1 << 12)
|
|
+# define DxMODE_VBLANK_INTERRUPT (1 << 16)
|
|
+# define DxMODE_VBLANK_INTERRUPT_TYPE (1 << 17)
|
|
+#define D1MODE_VLINE_STATUS 0x653c
|
|
+#define D2MODE_VLINE_STATUS 0x6d3c
|
|
+# define DxMODE_VLINE_OCCURRED (1 << 0)
|
|
+# define DxMODE_VLINE_ACK (1 << 4)
|
|
+# define DxMODE_VLINE_STAT (1 << 12)
|
|
+# define DxMODE_VLINE_INTERRUPT (1 << 16)
|
|
+# define DxMODE_VLINE_INTERRUPT_TYPE (1 << 17)
|
|
+#define DxMODE_INT_MASK 0x6540
|
|
+# define D1MODE_VBLANK_INT_MASK (1 << 0)
|
|
+# define D1MODE_VLINE_INT_MASK (1 << 4)
|
|
+# define D2MODE_VBLANK_INT_MASK (1 << 8)
|
|
+# define D2MODE_VLINE_INT_MASK (1 << 12)
|
|
+#define DCE3_DISP_INTERRUPT_STATUS 0x7ddc
|
|
+# define DC_HPD1_INTERRUPT (1 << 18)
|
|
+# define DC_HPD2_INTERRUPT (1 << 19)
|
|
+#define DISP_INTERRUPT_STATUS 0x7edc
|
|
+# define LB_D1_VLINE_INTERRUPT (1 << 2)
|
|
+# define LB_D2_VLINE_INTERRUPT (1 << 3)
|
|
+# define LB_D1_VBLANK_INTERRUPT (1 << 4)
|
|
+# define LB_D2_VBLANK_INTERRUPT (1 << 5)
|
|
+# define DACA_AUTODETECT_INTERRUPT (1 << 16)
|
|
+# define DACB_AUTODETECT_INTERRUPT (1 << 17)
|
|
+# define DC_HOT_PLUG_DETECT1_INTERRUPT (1 << 18)
|
|
+# define DC_HOT_PLUG_DETECT2_INTERRUPT (1 << 19)
|
|
+# define DC_I2C_SW_DONE_INTERRUPT (1 << 20)
|
|
+# define DC_I2C_HW_DONE_INTERRUPT (1 << 21)
|
|
+#define DISP_INTERRUPT_STATUS_CONTINUE 0x7ee8
|
|
+#define DCE3_DISP_INTERRUPT_STATUS_CONTINUE 0x7de8
|
|
+# define DC_HPD4_INTERRUPT (1 << 14)
|
|
+# define DC_HPD4_RX_INTERRUPT (1 << 15)
|
|
+# define DC_HPD3_INTERRUPT (1 << 28)
|
|
+# define DC_HPD1_RX_INTERRUPT (1 << 29)
|
|
+# define DC_HPD2_RX_INTERRUPT (1 << 30)
|
|
+#define DCE3_DISP_INTERRUPT_STATUS_CONTINUE2 0x7dec
|
|
+# define DC_HPD3_RX_INTERRUPT (1 << 0)
|
|
+# define DIGA_DP_VID_STREAM_DISABLE_INTERRUPT (1 << 1)
|
|
+# define DIGA_DP_STEER_FIFO_OVERFLOW_INTERRUPT (1 << 2)
|
|
+# define DIGB_DP_VID_STREAM_DISABLE_INTERRUPT (1 << 3)
|
|
+# define DIGB_DP_STEER_FIFO_OVERFLOW_INTERRUPT (1 << 4)
|
|
+# define AUX1_SW_DONE_INTERRUPT (1 << 5)
|
|
+# define AUX1_LS_DONE_INTERRUPT (1 << 6)
|
|
+# define AUX2_SW_DONE_INTERRUPT (1 << 7)
|
|
+# define AUX2_LS_DONE_INTERRUPT (1 << 8)
|
|
+# define AUX3_SW_DONE_INTERRUPT (1 << 9)
|
|
+# define AUX3_LS_DONE_INTERRUPT (1 << 10)
|
|
+# define AUX4_SW_DONE_INTERRUPT (1 << 11)
|
|
+# define AUX4_LS_DONE_INTERRUPT (1 << 12)
|
|
+# define DIGA_DP_FAST_TRAINING_COMPLETE_INTERRUPT (1 << 13)
|
|
+# define DIGB_DP_FAST_TRAINING_COMPLETE_INTERRUPT (1 << 14)
|
|
+/* DCE 3.2 */
|
|
+# define AUX5_SW_DONE_INTERRUPT (1 << 15)
|
|
+# define AUX5_LS_DONE_INTERRUPT (1 << 16)
|
|
+# define AUX6_SW_DONE_INTERRUPT (1 << 17)
|
|
+# define AUX6_LS_DONE_INTERRUPT (1 << 18)
|
|
+# define DC_HPD5_INTERRUPT (1 << 19)
|
|
+# define DC_HPD5_RX_INTERRUPT (1 << 20)
|
|
+# define DC_HPD6_INTERRUPT (1 << 21)
|
|
+# define DC_HPD6_RX_INTERRUPT (1 << 22)
|
|
+
|
|
+#define DACA_AUTO_DETECT_CONTROL 0x7828
|
|
+#define DACB_AUTO_DETECT_CONTROL 0x7a28
|
|
+#define DCE3_DACA_AUTO_DETECT_CONTROL 0x7028
|
|
+#define DCE3_DACB_AUTO_DETECT_CONTROL 0x7128
|
|
+# define DACx_AUTODETECT_MODE(x) ((x) << 0)
|
|
+# define DACx_AUTODETECT_MODE_NONE 0
|
|
+# define DACx_AUTODETECT_MODE_CONNECT 1
|
|
+# define DACx_AUTODETECT_MODE_DISCONNECT 2
|
|
+# define DACx_AUTODETECT_FRAME_TIME_COUNTER(x) ((x) << 8)
|
|
+/* bit 18 = R/C, 17 = G/Y, 16 = B/Comp */
|
|
+# define DACx_AUTODETECT_CHECK_MASK(x) ((x) << 16)
|
|
+
|
|
+#define DCE3_DACA_AUTODETECT_INT_CONTROL 0x7038
|
|
+#define DCE3_DACB_AUTODETECT_INT_CONTROL 0x7138
|
|
+#define DACA_AUTODETECT_INT_CONTROL 0x7838
|
|
+#define DACB_AUTODETECT_INT_CONTROL 0x7a38
|
|
+# define DACx_AUTODETECT_ACK (1 << 0)
|
|
+# define DACx_AUTODETECT_INT_ENABLE (1 << 16)
|
|
+
|
|
+#define DC_HOT_PLUG_DETECT1_CONTROL 0x7d00
|
|
+#define DC_HOT_PLUG_DETECT2_CONTROL 0x7d10
|
|
+#define DC_HOT_PLUG_DETECT3_CONTROL 0x7d24
|
|
+# define DC_HOT_PLUG_DETECTx_EN (1 << 0)
|
|
+
|
|
+#define DC_HOT_PLUG_DETECT1_INT_STATUS 0x7d04
|
|
+#define DC_HOT_PLUG_DETECT2_INT_STATUS 0x7d14
|
|
+#define DC_HOT_PLUG_DETECT3_INT_STATUS 0x7d28
|
|
+# define DC_HOT_PLUG_DETECTx_INT_STATUS (1 << 0)
|
|
+# define DC_HOT_PLUG_DETECTx_SENSE (1 << 1)
|
|
+
|
|
+/* DCE 3.0 */
|
|
+#define DC_HPD1_INT_STATUS 0x7d00
|
|
+#define DC_HPD2_INT_STATUS 0x7d0c
|
|
+#define DC_HPD3_INT_STATUS 0x7d18
|
|
+#define DC_HPD4_INT_STATUS 0x7d24
|
|
+/* DCE 3.2 */
|
|
+#define DC_HPD5_INT_STATUS 0x7dc0
|
|
+#define DC_HPD6_INT_STATUS 0x7df4
|
|
+# define DC_HPDx_INT_STATUS (1 << 0)
|
|
+# define DC_HPDx_SENSE (1 << 1)
|
|
+# define DC_HPDx_RX_INT_STATUS (1 << 8)
|
|
+
|
|
+#define DC_HOT_PLUG_DETECT1_INT_CONTROL 0x7d08
|
|
+#define DC_HOT_PLUG_DETECT2_INT_CONTROL 0x7d18
|
|
+#define DC_HOT_PLUG_DETECT3_INT_CONTROL 0x7d2c
|
|
+# define DC_HOT_PLUG_DETECTx_INT_ACK (1 << 0)
|
|
+# define DC_HOT_PLUG_DETECTx_INT_POLARITY (1 << 8)
|
|
+# define DC_HOT_PLUG_DETECTx_INT_EN (1 << 16)
|
|
+/* DCE 3.0 */
|
|
+#define DC_HPD1_INT_CONTROL 0x7d04
|
|
+#define DC_HPD2_INT_CONTROL 0x7d10
|
|
+#define DC_HPD3_INT_CONTROL 0x7d1c
|
|
+#define DC_HPD4_INT_CONTROL 0x7d28
|
|
+/* DCE 3.2 */
|
|
+#define DC_HPD5_INT_CONTROL 0x7dc4
|
|
+#define DC_HPD6_INT_CONTROL 0x7df8
|
|
+# define DC_HPDx_INT_ACK (1 << 0)
|
|
+# define DC_HPDx_INT_POLARITY (1 << 8)
|
|
+# define DC_HPDx_INT_EN (1 << 16)
|
|
+# define DC_HPDx_RX_INT_ACK (1 << 20)
|
|
+# define DC_HPDx_RX_INT_EN (1 << 24)
|
|
+
|
|
+/* DCE 3.0 */
|
|
+#define DC_HPD1_CONTROL 0x7d08
|
|
+#define DC_HPD2_CONTROL 0x7d14
|
|
+#define DC_HPD3_CONTROL 0x7d20
|
|
+#define DC_HPD4_CONTROL 0x7d2c
|
|
+/* DCE 3.2 */
|
|
+#define DC_HPD5_CONTROL 0x7dc8
|
|
+#define DC_HPD6_CONTROL 0x7dfc
|
|
+# define DC_HPDx_CONNECTION_TIMER(x) ((x) << 0)
|
|
+# define DC_HPDx_RX_INT_TIMER(x) ((x) << 16)
|
|
+/* DCE 3.2 */
|
|
+# define DC_HPDx_EN (1 << 28)
|
|
|
|
/*
|
|
* PM4
|
|
@@ -500,7 +708,6 @@
|
|
#define PACKET3_WAIT_REG_MEM 0x3C
|
|
#define PACKET3_MEM_WRITE 0x3D
|
|
#define PACKET3_INDIRECT_BUFFER 0x32
|
|
-#define PACKET3_CP_INTERRUPT 0x40
|
|
#define PACKET3_SURFACE_SYNC 0x43
|
|
# define PACKET3_CB0_DEST_BASE_ENA (1 << 6)
|
|
# define PACKET3_TC_ACTION_ENA (1 << 23)
|
|
@@ -674,4 +881,30 @@
|
|
#define S_000E60_SOFT_RESET_TSC(x) (((x) & 1) << 16)
|
|
#define S_000E60_SOFT_RESET_VMC(x) (((x) & 1) << 17)
|
|
|
|
+#define R_005480_HDP_MEM_COHERENCY_FLUSH_CNTL 0x5480
|
|
+
|
|
+#define R_0280E0_CB_COLOR0_FRAG 0x0280E0
|
|
+#define S_0280E0_BASE_256B(x) (((x) & 0xFFFFFFFF) << 0)
|
|
+#define G_0280E0_BASE_256B(x) (((x) >> 0) & 0xFFFFFFFF)
|
|
+#define C_0280E0_BASE_256B 0x00000000
|
|
+#define R_0280E4_CB_COLOR1_FRAG 0x0280E4
|
|
+#define R_0280E8_CB_COLOR2_FRAG 0x0280E8
|
|
+#define R_0280EC_CB_COLOR3_FRAG 0x0280EC
|
|
+#define R_0280F0_CB_COLOR4_FRAG 0x0280F0
|
|
+#define R_0280F4_CB_COLOR5_FRAG 0x0280F4
|
|
+#define R_0280F8_CB_COLOR6_FRAG 0x0280F8
|
|
+#define R_0280FC_CB_COLOR7_FRAG 0x0280FC
|
|
+#define R_0280C0_CB_COLOR0_TILE 0x0280C0
|
|
+#define S_0280C0_BASE_256B(x) (((x) & 0xFFFFFFFF) << 0)
|
|
+#define G_0280C0_BASE_256B(x) (((x) >> 0) & 0xFFFFFFFF)
|
|
+#define C_0280C0_BASE_256B 0x00000000
|
|
+#define R_0280C4_CB_COLOR1_TILE 0x0280C4
|
|
+#define R_0280C8_CB_COLOR2_TILE 0x0280C8
|
|
+#define R_0280CC_CB_COLOR3_TILE 0x0280CC
|
|
+#define R_0280D0_CB_COLOR4_TILE 0x0280D0
|
|
+#define R_0280D4_CB_COLOR5_TILE 0x0280D4
|
|
+#define R_0280D8_CB_COLOR6_TILE 0x0280D8
|
|
+#define R_0280DC_CB_COLOR7_TILE 0x0280DC
|
|
+
|
|
+
|
|
#endif
|
|
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
|
|
index 224506a..c0356bb 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon.h
|
|
+++ b/drivers/gpu/drm/radeon/radeon.h
|
|
@@ -28,8 +28,6 @@
|
|
#ifndef __RADEON_H__
|
|
#define __RADEON_H__
|
|
|
|
-#include "radeon_object.h"
|
|
-
|
|
/* TODO: Here are things that needs to be done :
|
|
* - surface allocator & initializer : (bit like scratch reg) should
|
|
* initialize HDP_ stuff on RS600, R600, R700 hw, well anythings
|
|
@@ -67,6 +65,11 @@
|
|
#include <linux/list.h>
|
|
#include <linux/kref.h>
|
|
|
|
+#include <ttm/ttm_bo_api.h>
|
|
+#include <ttm/ttm_bo_driver.h>
|
|
+#include <ttm/ttm_placement.h>
|
|
+#include <ttm/ttm_module.h>
|
|
+
|
|
#include "radeon_family.h"
|
|
#include "radeon_mode.h"
|
|
#include "radeon_reg.h"
|
|
@@ -85,12 +88,15 @@ extern int radeon_benchmarking;
|
|
extern int radeon_testing;
|
|
extern int radeon_connector_table;
|
|
extern int radeon_tv;
|
|
+extern int radeon_new_pll;
|
|
+extern int radeon_audio;
|
|
|
|
/*
|
|
* Copy from radeon_drv.h so we don't have to include both and have conflicting
|
|
* symbol;
|
|
*/
|
|
#define RADEON_MAX_USEC_TIMEOUT 100000 /* 100 ms */
|
|
+/* RADEON_IB_POOL_SIZE must be a power of 2 */
|
|
#define RADEON_IB_POOL_SIZE 16
|
|
#define RADEON_DEBUGFS_MAX_NUM_FILES 32
|
|
#define RADEONFB_CONN_LIMIT 4
|
|
@@ -157,6 +163,7 @@ struct radeon_fence_driver {
|
|
struct list_head created;
|
|
struct list_head emited;
|
|
struct list_head signaled;
|
|
+ bool initialized;
|
|
};
|
|
|
|
struct radeon_fence {
|
|
@@ -186,76 +193,63 @@ void radeon_fence_unref(struct radeon_fence **fence);
|
|
* Tiling registers
|
|
*/
|
|
struct radeon_surface_reg {
|
|
- struct radeon_object *robj;
|
|
+ struct radeon_bo *bo;
|
|
};
|
|
|
|
#define RADEON_GEM_MAX_SURFACES 8
|
|
|
|
/*
|
|
- * Radeon buffer.
|
|
+ * TTM.
|
|
*/
|
|
-struct radeon_object;
|
|
+struct radeon_mman {
|
|
+ struct ttm_bo_global_ref bo_global_ref;
|
|
+ struct ttm_global_reference mem_global_ref;
|
|
+ struct ttm_bo_device bdev;
|
|
+ bool mem_global_referenced;
|
|
+ bool initialized;
|
|
+};
|
|
|
|
-struct radeon_object_list {
|
|
+struct radeon_bo {
|
|
+ /* Protected by gem.mutex */
|
|
+ struct list_head list;
|
|
+ /* Protected by tbo.reserved */
|
|
+ u32 placements[3];
|
|
+ struct ttm_placement placement;
|
|
+ struct ttm_buffer_object tbo;
|
|
+ struct ttm_bo_kmap_obj kmap;
|
|
+ unsigned pin_count;
|
|
+ void *kptr;
|
|
+ u32 tiling_flags;
|
|
+ u32 pitch;
|
|
+ int surface_reg;
|
|
+ /* Constant after initialization */
|
|
+ struct radeon_device *rdev;
|
|
+ struct drm_gem_object *gobj;
|
|
+};
|
|
+
|
|
+struct radeon_bo_list {
|
|
struct list_head list;
|
|
- struct radeon_object *robj;
|
|
+ struct radeon_bo *bo;
|
|
uint64_t gpu_offset;
|
|
unsigned rdomain;
|
|
unsigned wdomain;
|
|
- uint32_t tiling_flags;
|
|
+ u32 tiling_flags;
|
|
};
|
|
|
|
-int radeon_object_init(struct radeon_device *rdev);
|
|
-void radeon_object_fini(struct radeon_device *rdev);
|
|
-int radeon_object_create(struct radeon_device *rdev,
|
|
- struct drm_gem_object *gobj,
|
|
- unsigned long size,
|
|
- bool kernel,
|
|
- uint32_t domain,
|
|
- bool interruptible,
|
|
- struct radeon_object **robj_ptr);
|
|
-int radeon_object_kmap(struct radeon_object *robj, void **ptr);
|
|
-void radeon_object_kunmap(struct radeon_object *robj);
|
|
-void radeon_object_unref(struct radeon_object **robj);
|
|
-int radeon_object_pin(struct radeon_object *robj, uint32_t domain,
|
|
- uint64_t *gpu_addr);
|
|
-void radeon_object_unpin(struct radeon_object *robj);
|
|
-int radeon_object_wait(struct radeon_object *robj);
|
|
-int radeon_object_busy_domain(struct radeon_object *robj, uint32_t *cur_placement);
|
|
-int radeon_object_evict_vram(struct radeon_device *rdev);
|
|
-int radeon_object_mmap(struct radeon_object *robj, uint64_t *offset);
|
|
-void radeon_object_force_delete(struct radeon_device *rdev);
|
|
-void radeon_object_list_add_object(struct radeon_object_list *lobj,
|
|
- struct list_head *head);
|
|
-int radeon_object_list_validate(struct list_head *head, void *fence);
|
|
-void radeon_object_list_unvalidate(struct list_head *head);
|
|
-void radeon_object_list_clean(struct list_head *head);
|
|
-int radeon_object_fbdev_mmap(struct radeon_object *robj,
|
|
- struct vm_area_struct *vma);
|
|
-unsigned long radeon_object_size(struct radeon_object *robj);
|
|
-void radeon_object_clear_surface_reg(struct radeon_object *robj);
|
|
-int radeon_object_check_tiling(struct radeon_object *robj, bool has_moved,
|
|
- bool force_drop);
|
|
-void radeon_object_set_tiling_flags(struct radeon_object *robj,
|
|
- uint32_t tiling_flags, uint32_t pitch);
|
|
-void radeon_object_get_tiling_flags(struct radeon_object *robj, uint32_t *tiling_flags, uint32_t *pitch);
|
|
-void radeon_bo_move_notify(struct ttm_buffer_object *bo,
|
|
- struct ttm_mem_reg *mem);
|
|
-void radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo);
|
|
/*
|
|
* GEM objects.
|
|
*/
|
|
struct radeon_gem {
|
|
+ struct mutex mutex;
|
|
struct list_head objects;
|
|
};
|
|
|
|
int radeon_gem_init(struct radeon_device *rdev);
|
|
void radeon_gem_fini(struct radeon_device *rdev);
|
|
int radeon_gem_object_create(struct radeon_device *rdev, int size,
|
|
- int alignment, int initial_domain,
|
|
- bool discardable, bool kernel,
|
|
- bool interruptible,
|
|
- struct drm_gem_object **obj);
|
|
+ int alignment, int initial_domain,
|
|
+ bool discardable, bool kernel,
|
|
+ struct drm_gem_object **obj);
|
|
int radeon_gem_object_pin(struct drm_gem_object *obj, uint32_t pin_domain,
|
|
uint64_t *gpu_addr);
|
|
void radeon_gem_object_unpin(struct drm_gem_object *obj);
|
|
@@ -271,7 +265,7 @@ struct radeon_gart_table_ram {
|
|
};
|
|
|
|
struct radeon_gart_table_vram {
|
|
- struct radeon_object *robj;
|
|
+ struct radeon_bo *robj;
|
|
volatile uint32_t *ptr;
|
|
};
|
|
|
|
@@ -326,10 +320,12 @@ struct radeon_mc {
|
|
u64 real_vram_size;
|
|
int vram_mtrr;
|
|
bool vram_is_ddr;
|
|
+ bool igp_sideport_enabled;
|
|
};
|
|
|
|
int radeon_mc_setup(struct radeon_device *rdev);
|
|
-
|
|
+bool radeon_combios_sideport_present(struct radeon_device *rdev);
|
|
+bool radeon_atombios_sideport_present(struct radeon_device *rdev);
|
|
|
|
/*
|
|
* GPU scratch registers structures, functions & helpers
|
|
@@ -352,22 +348,28 @@ struct radeon_irq {
|
|
bool sw_int;
|
|
/* FIXME: use a define max crtc rather than hardcode it */
|
|
bool crtc_vblank_int[2];
|
|
+ /* FIXME: use defines for max hpd/dacs */
|
|
+ bool hpd[6];
|
|
+ spinlock_t sw_lock;
|
|
+ int sw_refcount;
|
|
};
|
|
|
|
int radeon_irq_kms_init(struct radeon_device *rdev);
|
|
void radeon_irq_kms_fini(struct radeon_device *rdev);
|
|
-
|
|
+void radeon_irq_kms_sw_irq_get(struct radeon_device *rdev);
|
|
+void radeon_irq_kms_sw_irq_put(struct radeon_device *rdev);
|
|
|
|
/*
|
|
* CP & ring.
|
|
*/
|
|
struct radeon_ib {
|
|
struct list_head list;
|
|
- unsigned long idx;
|
|
+ unsigned idx;
|
|
uint64_t gpu_addr;
|
|
struct radeon_fence *fence;
|
|
- uint32_t *ptr;
|
|
+ uint32_t *ptr;
|
|
uint32_t length_dw;
|
|
+ bool free;
|
|
};
|
|
|
|
/*
|
|
@@ -376,15 +378,14 @@ struct radeon_ib {
|
|
*/
|
|
struct radeon_ib_pool {
|
|
struct mutex mutex;
|
|
- struct radeon_object *robj;
|
|
- struct list_head scheduled_ibs;
|
|
+ struct radeon_bo *robj;
|
|
struct radeon_ib ibs[RADEON_IB_POOL_SIZE];
|
|
bool ready;
|
|
- DECLARE_BITMAP(alloc_bm, RADEON_IB_POOL_SIZE);
|
|
+ unsigned head_id;
|
|
};
|
|
|
|
struct radeon_cp {
|
|
- struct radeon_object *ring_obj;
|
|
+ struct radeon_bo *ring_obj;
|
|
volatile uint32_t *ring;
|
|
unsigned rptr;
|
|
unsigned wptr;
|
|
@@ -399,8 +400,25 @@ struct radeon_cp {
|
|
bool ready;
|
|
};
|
|
|
|
+/*
|
|
+ * R6xx+ IH ring
|
|
+ */
|
|
+struct r600_ih {
|
|
+ struct radeon_bo *ring_obj;
|
|
+ volatile uint32_t *ring;
|
|
+ unsigned rptr;
|
|
+ unsigned wptr;
|
|
+ unsigned wptr_old;
|
|
+ unsigned ring_size;
|
|
+ uint64_t gpu_addr;
|
|
+ uint32_t ptr_mask;
|
|
+ spinlock_t lock;
|
|
+ bool enabled;
|
|
+};
|
|
+
|
|
struct r600_blit {
|
|
- struct radeon_object *shader_obj;
|
|
+ struct mutex mutex;
|
|
+ struct radeon_bo *shader_obj;
|
|
u64 shader_gpu_addr;
|
|
u32 vs_offset, ps_offset;
|
|
u32 state_offset;
|
|
@@ -430,8 +448,8 @@ void radeon_ring_fini(struct radeon_device *rdev);
|
|
*/
|
|
struct radeon_cs_reloc {
|
|
struct drm_gem_object *gobj;
|
|
- struct radeon_object *robj;
|
|
- struct radeon_object_list lobj;
|
|
+ struct radeon_bo *robj;
|
|
+ struct radeon_bo_list lobj;
|
|
uint32_t handle;
|
|
uint32_t flags;
|
|
};
|
|
@@ -448,6 +466,7 @@ struct radeon_cs_chunk {
|
|
};
|
|
|
|
struct radeon_cs_parser {
|
|
+ struct device *dev;
|
|
struct radeon_device *rdev;
|
|
struct drm_file *filp;
|
|
/* chunks */
|
|
@@ -527,7 +546,7 @@ void radeon_agp_fini(struct radeon_device *rdev);
|
|
* Writeback
|
|
*/
|
|
struct radeon_wb {
|
|
- struct radeon_object *wb_obj;
|
|
+ struct radeon_bo *wb_obj;
|
|
volatile uint32_t *wb;
|
|
uint64_t gpu_addr;
|
|
};
|
|
@@ -639,6 +658,17 @@ struct radeon_asic {
|
|
uint32_t offset, uint32_t obj_size);
|
|
int (*clear_surface_reg)(struct radeon_device *rdev, int reg);
|
|
void (*bandwidth_update)(struct radeon_device *rdev);
|
|
+ void (*hpd_init)(struct radeon_device *rdev);
|
|
+ void (*hpd_fini)(struct radeon_device *rdev);
|
|
+ bool (*hpd_sense)(struct radeon_device *rdev, enum radeon_hpd_id hpd);
|
|
+ void (*hpd_set_polarity)(struct radeon_device *rdev, enum radeon_hpd_id hpd);
|
|
+ /* ioctl hw specific callback. Some hw might want to perform special
|
|
+ * operation on specific ioctl. For instance on wait idle some hw
|
|
+ * might want to perform and HDP flush through MMIO as it seems that
|
|
+ * some R6XX/R7XX hw doesn't take HDP flush into account if programmed
|
|
+ * through ring.
|
|
+ */
|
|
+ void (*ioctl_wait_idle)(struct radeon_device *rdev, struct radeon_bo *bo);
|
|
};
|
|
|
|
/*
|
|
@@ -647,11 +677,14 @@ struct radeon_asic {
|
|
struct r100_asic {
|
|
const unsigned *reg_safe_bm;
|
|
unsigned reg_safe_bm_size;
|
|
+ u32 hdp_cntl;
|
|
};
|
|
|
|
struct r300_asic {
|
|
const unsigned *reg_safe_bm;
|
|
unsigned reg_safe_bm_size;
|
|
+ u32 resync_scratch;
|
|
+ u32 hdp_cntl;
|
|
};
|
|
|
|
struct r600_asic {
|
|
@@ -751,9 +784,9 @@ struct radeon_device {
|
|
uint8_t *bios;
|
|
bool is_atom_bios;
|
|
uint16_t bios_header_start;
|
|
- struct radeon_object *stollen_vga_memory;
|
|
+ struct radeon_bo *stollen_vga_memory;
|
|
struct fb_info *fbdev_info;
|
|
- struct radeon_object *fbdev_robj;
|
|
+ struct radeon_bo *fbdev_rbo;
|
|
struct radeon_framebuffer *fbdev_rfb;
|
|
/* Register mmio */
|
|
resource_size_t rmmio_base;
|
|
@@ -791,8 +824,20 @@ struct radeon_device {
|
|
struct radeon_surface_reg surface_regs[RADEON_GEM_MAX_SURFACES];
|
|
const struct firmware *me_fw; /* all family ME firmware */
|
|
const struct firmware *pfp_fw; /* r6/700 PFP firmware */
|
|
+ const struct firmware *rlc_fw; /* r6/700 RLC firmware */
|
|
struct r600_blit r600_blit;
|
|
int msi_enabled; /* msi enabled */
|
|
+ struct r600_ih ih; /* r6/700 interrupt ring */
|
|
+ struct workqueue_struct *wq;
|
|
+ struct work_struct hotplug_work;
|
|
+
|
|
+ /* audio stuff */
|
|
+ struct timer_list audio_timer;
|
|
+ int audio_channels;
|
|
+ int audio_rate;
|
|
+ int audio_bits_per_sample;
|
|
+ uint8_t audio_status_bits;
|
|
+ uint8_t audio_category_code;
|
|
};
|
|
|
|
int radeon_device_init(struct radeon_device *rdev,
|
|
@@ -811,7 +856,7 @@ void r600_kms_blit_copy(struct radeon_device *rdev,
|
|
|
|
static inline uint32_t r100_mm_rreg(struct radeon_device *rdev, uint32_t reg)
|
|
{
|
|
- if (reg < 0x10000)
|
|
+ if (reg < rdev->rmmio_size)
|
|
return readl(((void __iomem *)rdev->rmmio) + reg);
|
|
else {
|
|
writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX);
|
|
@@ -821,7 +866,7 @@ static inline uint32_t r100_mm_rreg(struct radeon_device *rdev, uint32_t reg)
|
|
|
|
static inline void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v)
|
|
{
|
|
- if (reg < 0x10000)
|
|
+ if (reg < rdev->rmmio_size)
|
|
writel(v, ((void __iomem *)rdev->rmmio) + reg);
|
|
else {
|
|
writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX);
|
|
@@ -829,6 +874,10 @@ static inline void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32
|
|
}
|
|
}
|
|
|
|
+/*
|
|
+ * Cast helper
|
|
+ */
|
|
+#define to_radeon_fence(p) ((struct radeon_fence *)(p))
|
|
|
|
/*
|
|
* Registers read & write functions.
|
|
@@ -965,18 +1014,25 @@ static inline void radeon_ring_write(struct radeon_device *rdev, uint32_t v)
|
|
#define radeon_get_engine_clock(rdev) (rdev)->asic->get_engine_clock((rdev))
|
|
#define radeon_set_engine_clock(rdev, e) (rdev)->asic->set_engine_clock((rdev), (e))
|
|
#define radeon_get_memory_clock(rdev) (rdev)->asic->get_memory_clock((rdev))
|
|
-#define radeon_set_memory_clock(rdev, e) (rdev)->asic->set_engine_clock((rdev), (e))
|
|
+#define radeon_set_memory_clock(rdev, e) (rdev)->asic->set_memory_clock((rdev), (e))
|
|
#define radeon_set_pcie_lanes(rdev, l) (rdev)->asic->set_pcie_lanes((rdev), (l))
|
|
#define radeon_set_clock_gating(rdev, e) (rdev)->asic->set_clock_gating((rdev), (e))
|
|
#define radeon_set_surface_reg(rdev, r, f, p, o, s) ((rdev)->asic->set_surface_reg((rdev), (r), (f), (p), (o), (s)))
|
|
#define radeon_clear_surface_reg(rdev, r) ((rdev)->asic->clear_surface_reg((rdev), (r)))
|
|
#define radeon_bandwidth_update(rdev) (rdev)->asic->bandwidth_update((rdev))
|
|
+#define radeon_hpd_init(rdev) (rdev)->asic->hpd_init((rdev))
|
|
+#define radeon_hpd_fini(rdev) (rdev)->asic->hpd_fini((rdev))
|
|
+#define radeon_hpd_sense(rdev, hpd) (rdev)->asic->hpd_sense((rdev), (hpd))
|
|
+#define radeon_hpd_set_polarity(rdev, hpd) (rdev)->asic->hpd_set_polarity((rdev), (hpd))
|
|
|
|
/* Common functions */
|
|
+/* AGP */
|
|
+extern void radeon_agp_disable(struct radeon_device *rdev);
|
|
extern int radeon_gart_table_vram_pin(struct radeon_device *rdev);
|
|
extern int radeon_modeset_init(struct radeon_device *rdev);
|
|
extern void radeon_modeset_fini(struct radeon_device *rdev);
|
|
extern bool radeon_card_posted(struct radeon_device *rdev);
|
|
+extern bool radeon_boot_test_post_card(struct radeon_device *rdev);
|
|
extern int radeon_clocks_init(struct radeon_device *rdev);
|
|
extern void radeon_clocks_fini(struct radeon_device *rdev);
|
|
extern void radeon_scratch_init(struct radeon_device *rdev);
|
|
@@ -984,6 +1040,8 @@ extern void radeon_surface_init(struct radeon_device *rdev);
|
|
extern int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data);
|
|
extern void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enable);
|
|
extern void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable);
|
|
+extern void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain);
|
|
+extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo);
|
|
|
|
/* r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280 */
|
|
struct r100_mc_save {
|
|
@@ -1021,7 +1079,7 @@ extern int r100_cp_reset(struct radeon_device *rdev);
|
|
extern void r100_vga_render_disable(struct radeon_device *rdev);
|
|
extern int r100_cs_track_check_pkt3_indx_buffer(struct radeon_cs_parser *p,
|
|
struct radeon_cs_packet *pkt,
|
|
- struct radeon_object *robj);
|
|
+ struct radeon_bo *robj);
|
|
extern int r100_cs_parse_packet0(struct radeon_cs_parser *p,
|
|
struct radeon_cs_packet *pkt,
|
|
const unsigned *auth, unsigned n,
|
|
@@ -1029,6 +1087,8 @@ extern int r100_cs_parse_packet0(struct radeon_cs_parser *p,
|
|
extern int r100_cs_packet_parse(struct radeon_cs_parser *p,
|
|
struct radeon_cs_packet *pkt,
|
|
unsigned idx);
|
|
+extern void r100_enable_bm(struct radeon_device *rdev);
|
|
+extern void r100_set_common_regs(struct radeon_device *rdev);
|
|
|
|
/* rv200,rv250,rv280 */
|
|
extern void r200_set_safe_registers(struct radeon_device *rdev);
|
|
@@ -1091,6 +1151,7 @@ extern bool r600_card_posted(struct radeon_device *rdev);
|
|
extern void r600_cp_stop(struct radeon_device *rdev);
|
|
extern void r600_ring_init(struct radeon_device *rdev, unsigned ring_size);
|
|
extern int r600_cp_resume(struct radeon_device *rdev);
|
|
+extern void r600_cp_fini(struct radeon_device *rdev);
|
|
extern int r600_count_pipe_bits(uint32_t val);
|
|
extern int r600_gart_clear_page(struct radeon_device *rdev, int i);
|
|
extern int r600_mc_wait_for_idle(struct radeon_device *rdev);
|
|
@@ -1104,7 +1165,30 @@ extern void r600_wb_disable(struct radeon_device *rdev);
|
|
extern void r600_scratch_init(struct radeon_device *rdev);
|
|
extern int r600_blit_init(struct radeon_device *rdev);
|
|
extern void r600_blit_fini(struct radeon_device *rdev);
|
|
-extern int r600_cp_init_microcode(struct radeon_device *rdev);
|
|
+extern int r600_init_microcode(struct radeon_device *rdev);
|
|
extern int r600_gpu_reset(struct radeon_device *rdev);
|
|
+/* r600 irq */
|
|
+extern int r600_irq_init(struct radeon_device *rdev);
|
|
+extern void r600_irq_fini(struct radeon_device *rdev);
|
|
+extern void r600_ih_ring_init(struct radeon_device *rdev, unsigned ring_size);
|
|
+extern int r600_irq_set(struct radeon_device *rdev);
|
|
+extern void r600_irq_suspend(struct radeon_device *rdev);
|
|
+/* r600 audio */
|
|
+extern int r600_audio_init(struct radeon_device *rdev);
|
|
+extern int r600_audio_tmds_index(struct drm_encoder *encoder);
|
|
+extern void r600_audio_set_clock(struct drm_encoder *encoder, int clock);
|
|
+extern void r600_audio_fini(struct radeon_device *rdev);
|
|
+extern void r600_hdmi_init(struct drm_encoder *encoder);
|
|
+extern void r600_hdmi_enable(struct drm_encoder *encoder, int enable);
|
|
+extern void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mode);
|
|
+extern int r600_hdmi_buffer_status_changed(struct drm_encoder *encoder);
|
|
+extern void r600_hdmi_update_audio_settings(struct drm_encoder *encoder,
|
|
+ int channels,
|
|
+ int rate,
|
|
+ int bps,
|
|
+ uint8_t status_bits,
|
|
+ uint8_t category_code);
|
|
+
|
|
+#include "radeon_object.h"
|
|
|
|
#endif
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_agp.c b/drivers/gpu/drm/radeon/radeon_agp.c
|
|
index 54bf49a..c0681a5 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_agp.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_agp.c
|
|
@@ -144,9 +144,19 @@ int radeon_agp_init(struct radeon_device *rdev)
|
|
|
|
ret = drm_agp_info(rdev->ddev, &info);
|
|
if (ret) {
|
|
+ drm_agp_release(rdev->ddev);
|
|
DRM_ERROR("Unable to get AGP info: %d\n", ret);
|
|
return ret;
|
|
}
|
|
+
|
|
+ if (rdev->ddev->agp->agp_info.aper_size < 32) {
|
|
+ drm_agp_release(rdev->ddev);
|
|
+ dev_warn(rdev->dev, "AGP aperture too small (%zuM) "
|
|
+ "need at least 32M, disabling AGP\n",
|
|
+ rdev->ddev->agp->agp_info.aper_size);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
mode.mode = info.mode;
|
|
agp_status = (RREG32(RADEON_AGP_STATUS) | RADEON_AGPv3_MODE) & mode.mode;
|
|
is_v3 = !!(agp_status & RADEON_AGPv3_MODE);
|
|
@@ -221,6 +231,7 @@ int radeon_agp_init(struct radeon_device *rdev)
|
|
ret = drm_agp_enable(rdev->ddev, mode);
|
|
if (ret) {
|
|
DRM_ERROR("Unable to enable AGP (mode = 0x%lx)\n", mode.mode);
|
|
+ drm_agp_release(rdev->ddev);
|
|
return ret;
|
|
}
|
|
|
|
@@ -252,10 +263,8 @@ void radeon_agp_resume(struct radeon_device *rdev)
|
|
void radeon_agp_fini(struct radeon_device *rdev)
|
|
{
|
|
#if __OS_HAS_AGP
|
|
- if (rdev->flags & RADEON_IS_AGP) {
|
|
- if (rdev->ddev->agp && rdev->ddev->agp->acquired) {
|
|
- drm_agp_release(rdev->ddev);
|
|
- }
|
|
+ if (rdev->ddev->agp && rdev->ddev->agp->acquired) {
|
|
+ drm_agp_release(rdev->ddev);
|
|
}
|
|
#endif
|
|
}
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h
|
|
index c18fbee..05ee1ae 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_asic.h
|
|
+++ b/drivers/gpu/drm/radeon/radeon_asic.h
|
|
@@ -33,6 +33,7 @@
|
|
*/
|
|
uint32_t radeon_legacy_get_engine_clock(struct radeon_device *rdev);
|
|
void radeon_legacy_set_engine_clock(struct radeon_device *rdev, uint32_t eng_clock);
|
|
+uint32_t radeon_legacy_get_memory_clock(struct radeon_device *rdev);
|
|
void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enable);
|
|
|
|
uint32_t radeon_atom_get_engine_clock(struct radeon_device *rdev);
|
|
@@ -76,6 +77,11 @@ int r100_clear_surface_reg(struct radeon_device *rdev, int reg);
|
|
void r100_bandwidth_update(struct radeon_device *rdev);
|
|
void r100_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib);
|
|
int r100_ring_test(struct radeon_device *rdev);
|
|
+void r100_hpd_init(struct radeon_device *rdev);
|
|
+void r100_hpd_fini(struct radeon_device *rdev);
|
|
+bool r100_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd);
|
|
+void r100_hpd_set_polarity(struct radeon_device *rdev,
|
|
+ enum radeon_hpd_id hpd);
|
|
|
|
static struct radeon_asic r100_asic = {
|
|
.init = &r100_init,
|
|
@@ -100,13 +106,18 @@ static struct radeon_asic r100_asic = {
|
|
.copy = &r100_copy_blit,
|
|
.get_engine_clock = &radeon_legacy_get_engine_clock,
|
|
.set_engine_clock = &radeon_legacy_set_engine_clock,
|
|
- .get_memory_clock = NULL,
|
|
+ .get_memory_clock = &radeon_legacy_get_memory_clock,
|
|
.set_memory_clock = NULL,
|
|
.set_pcie_lanes = NULL,
|
|
.set_clock_gating = &radeon_legacy_set_clock_gating,
|
|
.set_surface_reg = r100_set_surface_reg,
|
|
.clear_surface_reg = r100_clear_surface_reg,
|
|
.bandwidth_update = &r100_bandwidth_update,
|
|
+ .hpd_init = &r100_hpd_init,
|
|
+ .hpd_fini = &r100_hpd_fini,
|
|
+ .hpd_sense = &r100_hpd_sense,
|
|
+ .hpd_set_polarity = &r100_hpd_set_polarity,
|
|
+ .ioctl_wait_idle = NULL,
|
|
};
|
|
|
|
|
|
@@ -155,13 +166,18 @@ static struct radeon_asic r300_asic = {
|
|
.copy = &r100_copy_blit,
|
|
.get_engine_clock = &radeon_legacy_get_engine_clock,
|
|
.set_engine_clock = &radeon_legacy_set_engine_clock,
|
|
- .get_memory_clock = NULL,
|
|
+ .get_memory_clock = &radeon_legacy_get_memory_clock,
|
|
.set_memory_clock = NULL,
|
|
.set_pcie_lanes = &rv370_set_pcie_lanes,
|
|
.set_clock_gating = &radeon_legacy_set_clock_gating,
|
|
.set_surface_reg = r100_set_surface_reg,
|
|
.clear_surface_reg = r100_clear_surface_reg,
|
|
.bandwidth_update = &r100_bandwidth_update,
|
|
+ .hpd_init = &r100_hpd_init,
|
|
+ .hpd_fini = &r100_hpd_fini,
|
|
+ .hpd_sense = &r100_hpd_sense,
|
|
+ .hpd_set_polarity = &r100_hpd_set_polarity,
|
|
+ .ioctl_wait_idle = NULL,
|
|
};
|
|
|
|
/*
|
|
@@ -201,6 +217,11 @@ static struct radeon_asic r420_asic = {
|
|
.set_surface_reg = r100_set_surface_reg,
|
|
.clear_surface_reg = r100_clear_surface_reg,
|
|
.bandwidth_update = &r100_bandwidth_update,
|
|
+ .hpd_init = &r100_hpd_init,
|
|
+ .hpd_fini = &r100_hpd_fini,
|
|
+ .hpd_sense = &r100_hpd_sense,
|
|
+ .hpd_set_polarity = &r100_hpd_set_polarity,
|
|
+ .ioctl_wait_idle = NULL,
|
|
};
|
|
|
|
|
|
@@ -238,13 +259,18 @@ static struct radeon_asic rs400_asic = {
|
|
.copy = &r100_copy_blit,
|
|
.get_engine_clock = &radeon_legacy_get_engine_clock,
|
|
.set_engine_clock = &radeon_legacy_set_engine_clock,
|
|
- .get_memory_clock = NULL,
|
|
+ .get_memory_clock = &radeon_legacy_get_memory_clock,
|
|
.set_memory_clock = NULL,
|
|
.set_pcie_lanes = NULL,
|
|
.set_clock_gating = &radeon_legacy_set_clock_gating,
|
|
.set_surface_reg = r100_set_surface_reg,
|
|
.clear_surface_reg = r100_clear_surface_reg,
|
|
.bandwidth_update = &r100_bandwidth_update,
|
|
+ .hpd_init = &r100_hpd_init,
|
|
+ .hpd_fini = &r100_hpd_fini,
|
|
+ .hpd_sense = &r100_hpd_sense,
|
|
+ .hpd_set_polarity = &r100_hpd_set_polarity,
|
|
+ .ioctl_wait_idle = NULL,
|
|
};
|
|
|
|
|
|
@@ -263,6 +289,12 @@ int rs600_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr);
|
|
uint32_t rs600_mc_rreg(struct radeon_device *rdev, uint32_t reg);
|
|
void rs600_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v);
|
|
void rs600_bandwidth_update(struct radeon_device *rdev);
|
|
+void rs600_hpd_init(struct radeon_device *rdev);
|
|
+void rs600_hpd_fini(struct radeon_device *rdev);
|
|
+bool rs600_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd);
|
|
+void rs600_hpd_set_polarity(struct radeon_device *rdev,
|
|
+ enum radeon_hpd_id hpd);
|
|
+
|
|
static struct radeon_asic rs600_asic = {
|
|
.init = &rs600_init,
|
|
.fini = &rs600_fini,
|
|
@@ -291,6 +323,11 @@ static struct radeon_asic rs600_asic = {
|
|
.set_pcie_lanes = NULL,
|
|
.set_clock_gating = &radeon_atom_set_clock_gating,
|
|
.bandwidth_update = &rs600_bandwidth_update,
|
|
+ .hpd_init = &rs600_hpd_init,
|
|
+ .hpd_fini = &rs600_hpd_fini,
|
|
+ .hpd_sense = &rs600_hpd_sense,
|
|
+ .hpd_set_polarity = &rs600_hpd_set_polarity,
|
|
+ .ioctl_wait_idle = NULL,
|
|
};
|
|
|
|
|
|
@@ -334,6 +371,11 @@ static struct radeon_asic rs690_asic = {
|
|
.set_surface_reg = r100_set_surface_reg,
|
|
.clear_surface_reg = r100_clear_surface_reg,
|
|
.bandwidth_update = &rs690_bandwidth_update,
|
|
+ .hpd_init = &rs600_hpd_init,
|
|
+ .hpd_fini = &rs600_hpd_fini,
|
|
+ .hpd_sense = &rs600_hpd_sense,
|
|
+ .hpd_set_polarity = &rs600_hpd_set_polarity,
|
|
+ .ioctl_wait_idle = NULL,
|
|
};
|
|
|
|
|
|
@@ -381,6 +423,11 @@ static struct radeon_asic rv515_asic = {
|
|
.set_surface_reg = r100_set_surface_reg,
|
|
.clear_surface_reg = r100_clear_surface_reg,
|
|
.bandwidth_update = &rv515_bandwidth_update,
|
|
+ .hpd_init = &rs600_hpd_init,
|
|
+ .hpd_fini = &rs600_hpd_fini,
|
|
+ .hpd_sense = &rs600_hpd_sense,
|
|
+ .hpd_set_polarity = &rs600_hpd_set_polarity,
|
|
+ .ioctl_wait_idle = NULL,
|
|
};
|
|
|
|
|
|
@@ -419,6 +466,11 @@ static struct radeon_asic r520_asic = {
|
|
.set_surface_reg = r100_set_surface_reg,
|
|
.clear_surface_reg = r100_clear_surface_reg,
|
|
.bandwidth_update = &rv515_bandwidth_update,
|
|
+ .hpd_init = &rs600_hpd_init,
|
|
+ .hpd_fini = &rs600_hpd_fini,
|
|
+ .hpd_sense = &rs600_hpd_sense,
|
|
+ .hpd_set_polarity = &rs600_hpd_set_polarity,
|
|
+ .ioctl_wait_idle = NULL,
|
|
};
|
|
|
|
/*
|
|
@@ -455,6 +507,12 @@ int r600_ring_test(struct radeon_device *rdev);
|
|
int r600_copy_blit(struct radeon_device *rdev,
|
|
uint64_t src_offset, uint64_t dst_offset,
|
|
unsigned num_pages, struct radeon_fence *fence);
|
|
+void r600_hpd_init(struct radeon_device *rdev);
|
|
+void r600_hpd_fini(struct radeon_device *rdev);
|
|
+bool r600_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd);
|
|
+void r600_hpd_set_polarity(struct radeon_device *rdev,
|
|
+ enum radeon_hpd_id hpd);
|
|
+extern void r600_ioctl_wait_idle(struct radeon_device *rdev, struct radeon_bo *bo);
|
|
|
|
static struct radeon_asic r600_asic = {
|
|
.init = &r600_init,
|
|
@@ -470,6 +528,7 @@ static struct radeon_asic r600_asic = {
|
|
.ring_ib_execute = &r600_ring_ib_execute,
|
|
.irq_set = &r600_irq_set,
|
|
.irq_process = &r600_irq_process,
|
|
+ .get_vblank_counter = &rs600_get_vblank_counter,
|
|
.fence_ring_emit = &r600_fence_ring_emit,
|
|
.cs_parse = &r600_cs_parse,
|
|
.copy_blit = &r600_copy_blit,
|
|
@@ -484,6 +543,11 @@ static struct radeon_asic r600_asic = {
|
|
.set_surface_reg = r600_set_surface_reg,
|
|
.clear_surface_reg = r600_clear_surface_reg,
|
|
.bandwidth_update = &rv515_bandwidth_update,
|
|
+ .hpd_init = &r600_hpd_init,
|
|
+ .hpd_fini = &r600_hpd_fini,
|
|
+ .hpd_sense = &r600_hpd_sense,
|
|
+ .hpd_set_polarity = &r600_hpd_set_polarity,
|
|
+ .ioctl_wait_idle = r600_ioctl_wait_idle,
|
|
};
|
|
|
|
/*
|
|
@@ -509,6 +573,7 @@ static struct radeon_asic rv770_asic = {
|
|
.ring_ib_execute = &r600_ring_ib_execute,
|
|
.irq_set = &r600_irq_set,
|
|
.irq_process = &r600_irq_process,
|
|
+ .get_vblank_counter = &rs600_get_vblank_counter,
|
|
.fence_ring_emit = &r600_fence_ring_emit,
|
|
.cs_parse = &r600_cs_parse,
|
|
.copy_blit = &r600_copy_blit,
|
|
@@ -523,6 +588,11 @@ static struct radeon_asic rv770_asic = {
|
|
.set_surface_reg = r600_set_surface_reg,
|
|
.clear_surface_reg = r600_clear_surface_reg,
|
|
.bandwidth_update = &rv515_bandwidth_update,
|
|
+ .hpd_init = &r600_hpd_init,
|
|
+ .hpd_fini = &r600_hpd_fini,
|
|
+ .hpd_sense = &r600_hpd_sense,
|
|
+ .hpd_set_polarity = &r600_hpd_set_polarity,
|
|
+ .ioctl_wait_idle = r600_ioctl_wait_idle,
|
|
};
|
|
|
|
#endif
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c
|
|
index 969502a..2dcda61 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_atombios.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_atombios.c
|
|
@@ -47,7 +47,8 @@ radeon_add_atom_connector(struct drm_device *dev,
|
|
int connector_type,
|
|
struct radeon_i2c_bus_rec *i2c_bus,
|
|
bool linkb, uint32_t igp_lane_info,
|
|
- uint16_t connector_object_id);
|
|
+ uint16_t connector_object_id,
|
|
+ struct radeon_hpd *hpd);
|
|
|
|
/* from radeon_legacy_encoder.c */
|
|
extern void
|
|
@@ -60,16 +61,16 @@ union atom_supported_devices {
|
|
struct _ATOM_SUPPORTED_DEVICES_INFO_2d1 info_2d1;
|
|
};
|
|
|
|
-static inline struct radeon_i2c_bus_rec radeon_lookup_gpio(struct drm_device
|
|
- *dev, uint8_t id)
|
|
+static inline struct radeon_i2c_bus_rec radeon_lookup_i2c_gpio(struct radeon_device *rdev,
|
|
+ uint8_t id)
|
|
{
|
|
- struct radeon_device *rdev = dev->dev_private;
|
|
struct atom_context *ctx = rdev->mode_info.atom_context;
|
|
- ATOM_GPIO_I2C_ASSIGMENT gpio;
|
|
+ ATOM_GPIO_I2C_ASSIGMENT *gpio;
|
|
struct radeon_i2c_bus_rec i2c;
|
|
int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info);
|
|
struct _ATOM_GPIO_I2C_INFO *i2c_info;
|
|
uint16_t data_offset;
|
|
+ int i;
|
|
|
|
memset(&i2c, 0, sizeof(struct radeon_i2c_bus_rec));
|
|
i2c.valid = false;
|
|
@@ -78,34 +79,122 @@ static inline struct radeon_i2c_bus_rec radeon_lookup_gpio(struct drm_device
|
|
|
|
i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset);
|
|
|
|
- gpio = i2c_info->asGPIO_Info[id];
|
|
-
|
|
- i2c.mask_clk_reg = le16_to_cpu(gpio.usClkMaskRegisterIndex) * 4;
|
|
- i2c.mask_data_reg = le16_to_cpu(gpio.usDataMaskRegisterIndex) * 4;
|
|
- i2c.put_clk_reg = le16_to_cpu(gpio.usClkEnRegisterIndex) * 4;
|
|
- i2c.put_data_reg = le16_to_cpu(gpio.usDataEnRegisterIndex) * 4;
|
|
- i2c.get_clk_reg = le16_to_cpu(gpio.usClkY_RegisterIndex) * 4;
|
|
- i2c.get_data_reg = le16_to_cpu(gpio.usDataY_RegisterIndex) * 4;
|
|
- i2c.a_clk_reg = le16_to_cpu(gpio.usClkA_RegisterIndex) * 4;
|
|
- i2c.a_data_reg = le16_to_cpu(gpio.usDataA_RegisterIndex) * 4;
|
|
- i2c.mask_clk_mask = (1 << gpio.ucClkMaskShift);
|
|
- i2c.mask_data_mask = (1 << gpio.ucDataMaskShift);
|
|
- i2c.put_clk_mask = (1 << gpio.ucClkEnShift);
|
|
- i2c.put_data_mask = (1 << gpio.ucDataEnShift);
|
|
- i2c.get_clk_mask = (1 << gpio.ucClkY_Shift);
|
|
- i2c.get_data_mask = (1 << gpio.ucDataY_Shift);
|
|
- i2c.a_clk_mask = (1 << gpio.ucClkA_Shift);
|
|
- i2c.a_data_mask = (1 << gpio.ucDataA_Shift);
|
|
- i2c.valid = true;
|
|
+
|
|
+ for (i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) {
|
|
+ gpio = &i2c_info->asGPIO_Info[i];
|
|
+
|
|
+ if (gpio->sucI2cId.ucAccess == id) {
|
|
+ i2c.mask_clk_reg = le16_to_cpu(gpio->usClkMaskRegisterIndex) * 4;
|
|
+ i2c.mask_data_reg = le16_to_cpu(gpio->usDataMaskRegisterIndex) * 4;
|
|
+ i2c.en_clk_reg = le16_to_cpu(gpio->usClkEnRegisterIndex) * 4;
|
|
+ i2c.en_data_reg = le16_to_cpu(gpio->usDataEnRegisterIndex) * 4;
|
|
+ i2c.y_clk_reg = le16_to_cpu(gpio->usClkY_RegisterIndex) * 4;
|
|
+ i2c.y_data_reg = le16_to_cpu(gpio->usDataY_RegisterIndex) * 4;
|
|
+ i2c.a_clk_reg = le16_to_cpu(gpio->usClkA_RegisterIndex) * 4;
|
|
+ i2c.a_data_reg = le16_to_cpu(gpio->usDataA_RegisterIndex) * 4;
|
|
+ i2c.mask_clk_mask = (1 << gpio->ucClkMaskShift);
|
|
+ i2c.mask_data_mask = (1 << gpio->ucDataMaskShift);
|
|
+ i2c.en_clk_mask = (1 << gpio->ucClkEnShift);
|
|
+ i2c.en_data_mask = (1 << gpio->ucDataEnShift);
|
|
+ i2c.y_clk_mask = (1 << gpio->ucClkY_Shift);
|
|
+ i2c.y_data_mask = (1 << gpio->ucDataY_Shift);
|
|
+ i2c.a_clk_mask = (1 << gpio->ucClkA_Shift);
|
|
+ i2c.a_data_mask = (1 << gpio->ucDataA_Shift);
|
|
+
|
|
+ if (gpio->sucI2cId.sbfAccess.bfHW_Capable)
|
|
+ i2c.hw_capable = true;
|
|
+ else
|
|
+ i2c.hw_capable = false;
|
|
+
|
|
+ if (gpio->sucI2cId.ucAccess == 0xa0)
|
|
+ i2c.mm_i2c = true;
|
|
+ else
|
|
+ i2c.mm_i2c = false;
|
|
+
|
|
+ i2c.i2c_id = gpio->sucI2cId.ucAccess;
|
|
+
|
|
+ i2c.valid = true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
|
|
return i2c;
|
|
}
|
|
|
|
+static inline struct radeon_gpio_rec radeon_lookup_gpio(struct radeon_device *rdev,
|
|
+ u8 id)
|
|
+{
|
|
+ struct atom_context *ctx = rdev->mode_info.atom_context;
|
|
+ struct radeon_gpio_rec gpio;
|
|
+ int index = GetIndexIntoMasterTable(DATA, GPIO_Pin_LUT);
|
|
+ struct _ATOM_GPIO_PIN_LUT *gpio_info;
|
|
+ ATOM_GPIO_PIN_ASSIGNMENT *pin;
|
|
+ u16 data_offset, size;
|
|
+ int i, num_indices;
|
|
+
|
|
+ memset(&gpio, 0, sizeof(struct radeon_gpio_rec));
|
|
+ gpio.valid = false;
|
|
+
|
|
+ atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset);
|
|
+
|
|
+ gpio_info = (struct _ATOM_GPIO_PIN_LUT *)(ctx->bios + data_offset);
|
|
+
|
|
+ num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / sizeof(ATOM_GPIO_PIN_ASSIGNMENT);
|
|
+
|
|
+ for (i = 0; i < num_indices; i++) {
|
|
+ pin = &gpio_info->asGPIO_Pin[i];
|
|
+ if (id == pin->ucGPIO_ID) {
|
|
+ gpio.id = pin->ucGPIO_ID;
|
|
+ gpio.reg = pin->usGpioPin_AIndex * 4;
|
|
+ gpio.mask = (1 << pin->ucGpioPinBitShift);
|
|
+ gpio.valid = true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return gpio;
|
|
+}
|
|
+
|
|
+static struct radeon_hpd radeon_atom_get_hpd_info_from_gpio(struct radeon_device *rdev,
|
|
+ struct radeon_gpio_rec *gpio)
|
|
+{
|
|
+ struct radeon_hpd hpd;
|
|
+ hpd.gpio = *gpio;
|
|
+ if (gpio->reg == AVIVO_DC_GPIO_HPD_A) {
|
|
+ switch(gpio->mask) {
|
|
+ case (1 << 0):
|
|
+ hpd.hpd = RADEON_HPD_1;
|
|
+ break;
|
|
+ case (1 << 8):
|
|
+ hpd.hpd = RADEON_HPD_2;
|
|
+ break;
|
|
+ case (1 << 16):
|
|
+ hpd.hpd = RADEON_HPD_3;
|
|
+ break;
|
|
+ case (1 << 24):
|
|
+ hpd.hpd = RADEON_HPD_4;
|
|
+ break;
|
|
+ case (1 << 26):
|
|
+ hpd.hpd = RADEON_HPD_5;
|
|
+ break;
|
|
+ case (1 << 28):
|
|
+ hpd.hpd = RADEON_HPD_6;
|
|
+ break;
|
|
+ default:
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
+ break;
|
|
+ }
|
|
+ } else
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
+ return hpd;
|
|
+}
|
|
+
|
|
static bool radeon_atom_apply_quirks(struct drm_device *dev,
|
|
uint32_t supported_device,
|
|
int *connector_type,
|
|
struct radeon_i2c_bus_rec *i2c_bus,
|
|
- uint16_t *line_mux)
|
|
+ uint16_t *line_mux,
|
|
+ struct radeon_hpd *hpd)
|
|
{
|
|
|
|
/* Asus M2A-VM HDMI board lists the DVI port as HDMI */
|
|
@@ -143,6 +232,15 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev,
|
|
return false;
|
|
}
|
|
|
|
+ /* Gigabyte X1300 is DVI+VGA, not DVI+DVI */
|
|
+ if ((dev->pdev->device == 0x7142) &&
|
|
+ (dev->pdev->subsystem_vendor == 0x1458) &&
|
|
+ (dev->pdev->subsystem_device == 0x2134)) {
|
|
+ if (supported_device == ATOM_DEVICE_DFP1_SUPPORT)
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+
|
|
/* Funky macbooks */
|
|
if ((dev->pdev->device == 0x71C5) &&
|
|
(dev->pdev->subsystem_vendor == 0x106b) &&
|
|
@@ -180,6 +278,24 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev,
|
|
}
|
|
}
|
|
|
|
+ /* Acer laptop reports DVI-D as DVI-I */
|
|
+ if ((dev->pdev->device == 0x95c4) &&
|
|
+ (dev->pdev->subsystem_vendor == 0x1025) &&
|
|
+ (dev->pdev->subsystem_device == 0x013c)) {
|
|
+ if ((*connector_type == DRM_MODE_CONNECTOR_DVII) &&
|
|
+ (supported_device == ATOM_DEVICE_DFP1_SUPPORT))
|
|
+ *connector_type = DRM_MODE_CONNECTOR_DVID;
|
|
+ }
|
|
+
|
|
+ /* XFX Pine Group device rv730 reports no VGA DDC lines
|
|
+ * even though they are wired up to record 0x93
|
|
+ */
|
|
+ if ((dev->pdev->device == 0x9498) &&
|
|
+ (dev->pdev->subsystem_vendor == 0x1682) &&
|
|
+ (dev->pdev->subsystem_device == 0x2452)) {
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
+ *i2c_bus = radeon_lookup_i2c_gpio(rdev, 0x93);
|
|
+ }
|
|
return true;
|
|
}
|
|
|
|
@@ -239,7 +355,9 @@ const int object_connector_convert[] = {
|
|
DRM_MODE_CONNECTOR_Unknown,
|
|
DRM_MODE_CONNECTOR_Unknown,
|
|
DRM_MODE_CONNECTOR_Unknown,
|
|
- DRM_MODE_CONNECTOR_DisplayPort
|
|
+ DRM_MODE_CONNECTOR_DisplayPort,
|
|
+ DRM_MODE_CONNECTOR_eDP,
|
|
+ DRM_MODE_CONNECTOR_Unknown
|
|
};
|
|
|
|
bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
|
|
@@ -248,16 +366,18 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
|
|
struct radeon_mode_info *mode_info = &rdev->mode_info;
|
|
struct atom_context *ctx = mode_info->atom_context;
|
|
int index = GetIndexIntoMasterTable(DATA, Object_Header);
|
|
- uint16_t size, data_offset;
|
|
- uint8_t frev, crev, line_mux = 0;
|
|
+ u16 size, data_offset;
|
|
+ u8 frev, crev;
|
|
ATOM_CONNECTOR_OBJECT_TABLE *con_obj;
|
|
ATOM_DISPLAY_OBJECT_PATH_TABLE *path_obj;
|
|
ATOM_OBJECT_HEADER *obj_header;
|
|
int i, j, path_size, device_support;
|
|
int connector_type;
|
|
- uint16_t igp_lane_info, conn_id, connector_object_id;
|
|
+ u16 igp_lane_info, conn_id, connector_object_id;
|
|
bool linkb;
|
|
struct radeon_i2c_bus_rec ddc_bus;
|
|
+ struct radeon_gpio_rec gpio;
|
|
+ struct radeon_hpd hpd;
|
|
|
|
atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset);
|
|
|
|
@@ -284,7 +404,6 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
|
|
path = (ATOM_DISPLAY_OBJECT_PATH *) addr;
|
|
path_size += le16_to_cpu(path->usSize);
|
|
linkb = false;
|
|
-
|
|
if (device_support & le16_to_cpu(path->usDeviceTag)) {
|
|
uint8_t con_obj_id, con_obj_num, con_obj_type;
|
|
|
|
@@ -385,10 +504,9 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
|
|
}
|
|
}
|
|
|
|
- /* look up gpio for ddc */
|
|
+ /* look up gpio for ddc, hpd */
|
|
if ((le16_to_cpu(path->usDeviceTag) &
|
|
- (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT))
|
|
- == 0) {
|
|
+ (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT)) == 0) {
|
|
for (j = 0; j < con_obj->ucNumberOfObjects; j++) {
|
|
if (le16_to_cpu(path->usConnObjectId) ==
|
|
le16_to_cpu(con_obj->asObjects[j].
|
|
@@ -402,21 +520,34 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
|
|
asObjects[j].
|
|
usRecordOffset));
|
|
ATOM_I2C_RECORD *i2c_record;
|
|
+ ATOM_HPD_INT_RECORD *hpd_record;
|
|
+ ATOM_I2C_ID_CONFIG_ACCESS *i2c_config;
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
|
|
while (record->ucRecordType > 0
|
|
&& record->
|
|
ucRecordType <=
|
|
ATOM_MAX_OBJECT_RECORD_NUMBER) {
|
|
- switch (record->
|
|
- ucRecordType) {
|
|
+ switch (record->ucRecordType) {
|
|
case ATOM_I2C_RECORD_TYPE:
|
|
i2c_record =
|
|
- (ATOM_I2C_RECORD
|
|
- *) record;
|
|
- line_mux =
|
|
- i2c_record->
|
|
- sucI2cId.
|
|
- bfI2C_LineMux;
|
|
+ (ATOM_I2C_RECORD *)
|
|
+ record;
|
|
+ i2c_config =
|
|
+ (ATOM_I2C_ID_CONFIG_ACCESS *)
|
|
+ &i2c_record->sucI2cId;
|
|
+ ddc_bus = radeon_lookup_i2c_gpio(rdev,
|
|
+ i2c_config->
|
|
+ ucAccess);
|
|
+ break;
|
|
+ case ATOM_HPD_INT_RECORD_TYPE:
|
|
+ hpd_record =
|
|
+ (ATOM_HPD_INT_RECORD *)
|
|
+ record;
|
|
+ gpio = radeon_lookup_gpio(rdev,
|
|
+ hpd_record->ucHPDIntGPIOID);
|
|
+ hpd = radeon_atom_get_hpd_info_from_gpio(rdev, &gpio);
|
|
+ hpd.plugged_state = hpd_record->ucPlugged_PinState;
|
|
break;
|
|
}
|
|
record =
|
|
@@ -429,24 +560,16 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
|
|
break;
|
|
}
|
|
}
|
|
- } else
|
|
- line_mux = 0;
|
|
-
|
|
- if ((le16_to_cpu(path->usDeviceTag) ==
|
|
- ATOM_DEVICE_TV1_SUPPORT)
|
|
- || (le16_to_cpu(path->usDeviceTag) ==
|
|
- ATOM_DEVICE_TV2_SUPPORT)
|
|
- || (le16_to_cpu(path->usDeviceTag) ==
|
|
- ATOM_DEVICE_CV_SUPPORT))
|
|
+ } else {
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
ddc_bus.valid = false;
|
|
- else
|
|
- ddc_bus = radeon_lookup_gpio(dev, line_mux);
|
|
+ }
|
|
|
|
conn_id = le16_to_cpu(path->usConnObjectId);
|
|
|
|
if (!radeon_atom_apply_quirks
|
|
(dev, le16_to_cpu(path->usDeviceTag), &connector_type,
|
|
- &ddc_bus, &conn_id))
|
|
+ &ddc_bus, &conn_id, &hpd))
|
|
continue;
|
|
|
|
radeon_add_atom_connector(dev,
|
|
@@ -455,7 +578,8 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
|
|
usDeviceTag),
|
|
connector_type, &ddc_bus,
|
|
linkb, igp_lane_info,
|
|
- connector_object_id);
|
|
+ connector_object_id,
|
|
+ &hpd);
|
|
|
|
}
|
|
}
|
|
@@ -510,6 +634,7 @@ struct bios_connector {
|
|
uint16_t devices;
|
|
int connector_type;
|
|
struct radeon_i2c_bus_rec ddc_bus;
|
|
+ struct radeon_hpd hpd;
|
|
};
|
|
|
|
bool radeon_get_atom_connector_info_from_supported_devices_table(struct
|
|
@@ -525,7 +650,7 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
|
|
uint16_t device_support;
|
|
uint8_t dac;
|
|
union atom_supported_devices *supported_devices;
|
|
- int i, j;
|
|
+ int i, j, max_device;
|
|
struct bios_connector bios_connectors[ATOM_MAX_SUPPORTED_DEVICE];
|
|
|
|
atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset);
|
|
@@ -535,7 +660,12 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
|
|
|
|
device_support = le16_to_cpu(supported_devices->info.usDeviceSupport);
|
|
|
|
- for (i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) {
|
|
+ if (frev > 1)
|
|
+ max_device = ATOM_MAX_SUPPORTED_DEVICE;
|
|
+ else
|
|
+ max_device = ATOM_MAX_SUPPORTED_DEVICE_INFO;
|
|
+
|
|
+ for (i = 0; i < max_device; i++) {
|
|
ATOM_CONNECTOR_INFO_I2C ci =
|
|
supported_devices->info.asConnInfo[i];
|
|
|
|
@@ -561,22 +691,8 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
|
|
|
|
dac = ci.sucConnectorInfo.sbfAccess.bfAssociatedDAC;
|
|
|
|
- if ((rdev->family == CHIP_RS690) ||
|
|
- (rdev->family == CHIP_RS740)) {
|
|
- if ((i == ATOM_DEVICE_DFP2_INDEX)
|
|
- && (ci.sucI2cId.sbfAccess.bfI2C_LineMux == 2))
|
|
- bios_connectors[i].line_mux =
|
|
- ci.sucI2cId.sbfAccess.bfI2C_LineMux + 1;
|
|
- else if ((i == ATOM_DEVICE_DFP3_INDEX)
|
|
- && (ci.sucI2cId.sbfAccess.bfI2C_LineMux == 1))
|
|
- bios_connectors[i].line_mux =
|
|
- ci.sucI2cId.sbfAccess.bfI2C_LineMux + 1;
|
|
- else
|
|
- bios_connectors[i].line_mux =
|
|
- ci.sucI2cId.sbfAccess.bfI2C_LineMux;
|
|
- } else
|
|
- bios_connectors[i].line_mux =
|
|
- ci.sucI2cId.sbfAccess.bfI2C_LineMux;
|
|
+ bios_connectors[i].line_mux =
|
|
+ ci.sucI2cId.ucAccess;
|
|
|
|
/* give tv unique connector ids */
|
|
if (i == ATOM_DEVICE_TV1_INDEX) {
|
|
@@ -590,8 +706,30 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
|
|
bios_connectors[i].line_mux = 52;
|
|
} else
|
|
bios_connectors[i].ddc_bus =
|
|
- radeon_lookup_gpio(dev,
|
|
- bios_connectors[i].line_mux);
|
|
+ radeon_lookup_i2c_gpio(rdev,
|
|
+ bios_connectors[i].line_mux);
|
|
+
|
|
+ if ((crev > 1) && (frev > 1)) {
|
|
+ u8 isb = supported_devices->info_2d1.asIntSrcInfo[i].ucIntSrcBitmap;
|
|
+ switch (isb) {
|
|
+ case 0x4:
|
|
+ bios_connectors[i].hpd.hpd = RADEON_HPD_1;
|
|
+ break;
|
|
+ case 0xa:
|
|
+ bios_connectors[i].hpd.hpd = RADEON_HPD_2;
|
|
+ break;
|
|
+ default:
|
|
+ bios_connectors[i].hpd.hpd = RADEON_HPD_NONE;
|
|
+ break;
|
|
+ }
|
|
+ } else {
|
|
+ if (i == ATOM_DEVICE_DFP1_INDEX)
|
|
+ bios_connectors[i].hpd.hpd = RADEON_HPD_1;
|
|
+ else if (i == ATOM_DEVICE_DFP2_INDEX)
|
|
+ bios_connectors[i].hpd.hpd = RADEON_HPD_2;
|
|
+ else
|
|
+ bios_connectors[i].hpd.hpd = RADEON_HPD_NONE;
|
|
+ }
|
|
|
|
/* Always set the connector type to VGA for CRT1/CRT2. if they are
|
|
* shared with a DVI port, we'll pick up the DVI connector when we
|
|
@@ -603,7 +741,8 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
|
|
|
|
if (!radeon_atom_apply_quirks
|
|
(dev, (1 << i), &bios_connectors[i].connector_type,
|
|
- &bios_connectors[i].ddc_bus, &bios_connectors[i].line_mux))
|
|
+ &bios_connectors[i].ddc_bus, &bios_connectors[i].line_mux,
|
|
+ &bios_connectors[i].hpd))
|
|
continue;
|
|
|
|
bios_connectors[i].valid = true;
|
|
@@ -618,41 +757,42 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
|
|
else
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
- (1 <<
|
|
- i),
|
|
+ (1 << i),
|
|
dac),
|
|
(1 << i));
|
|
}
|
|
|
|
/* combine shared connectors */
|
|
- for (i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) {
|
|
+ for (i = 0; i < max_device; i++) {
|
|
if (bios_connectors[i].valid) {
|
|
- for (j = 0; j < ATOM_MAX_SUPPORTED_DEVICE; j++) {
|
|
+ for (j = 0; j < max_device; j++) {
|
|
if (bios_connectors[j].valid && (i != j)) {
|
|
if (bios_connectors[i].line_mux ==
|
|
bios_connectors[j].line_mux) {
|
|
- if (((bios_connectors[i].
|
|
- devices &
|
|
- (ATOM_DEVICE_DFP_SUPPORT))
|
|
- && (bios_connectors[j].
|
|
- devices &
|
|
- (ATOM_DEVICE_CRT_SUPPORT)))
|
|
- ||
|
|
- ((bios_connectors[j].
|
|
- devices &
|
|
- (ATOM_DEVICE_DFP_SUPPORT))
|
|
- && (bios_connectors[i].
|
|
- devices &
|
|
- (ATOM_DEVICE_CRT_SUPPORT)))) {
|
|
- bios_connectors[i].
|
|
- devices |=
|
|
- bios_connectors[j].
|
|
- devices;
|
|
- bios_connectors[i].
|
|
- connector_type =
|
|
- DRM_MODE_CONNECTOR_DVII;
|
|
- bios_connectors[j].
|
|
- valid = false;
|
|
+ /* make sure not to combine LVDS */
|
|
+ if (bios_connectors[i].devices & (ATOM_DEVICE_LCD_SUPPORT)) {
|
|
+ bios_connectors[i].line_mux = 53;
|
|
+ bios_connectors[i].ddc_bus.valid = false;
|
|
+ continue;
|
|
+ }
|
|
+ if (bios_connectors[j].devices & (ATOM_DEVICE_LCD_SUPPORT)) {
|
|
+ bios_connectors[j].line_mux = 53;
|
|
+ bios_connectors[j].ddc_bus.valid = false;
|
|
+ continue;
|
|
+ }
|
|
+ /* combine analog and digital for DVI-I */
|
|
+ if (((bios_connectors[i].devices & (ATOM_DEVICE_DFP_SUPPORT)) &&
|
|
+ (bios_connectors[j].devices & (ATOM_DEVICE_CRT_SUPPORT))) ||
|
|
+ ((bios_connectors[j].devices & (ATOM_DEVICE_DFP_SUPPORT)) &&
|
|
+ (bios_connectors[i].devices & (ATOM_DEVICE_CRT_SUPPORT)))) {
|
|
+ bios_connectors[i].devices |=
|
|
+ bios_connectors[j].devices;
|
|
+ bios_connectors[i].connector_type =
|
|
+ DRM_MODE_CONNECTOR_DVII;
|
|
+ if (bios_connectors[j].devices & (ATOM_DEVICE_DFP_SUPPORT))
|
|
+ bios_connectors[i].hpd =
|
|
+ bios_connectors[j].hpd;
|
|
+ bios_connectors[j].valid = false;
|
|
}
|
|
}
|
|
}
|
|
@@ -661,7 +801,7 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
|
|
}
|
|
|
|
/* add the connectors */
|
|
- for (i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) {
|
|
+ for (i = 0; i < max_device; i++) {
|
|
if (bios_connectors[i].valid) {
|
|
uint16_t connector_object_id =
|
|
atombios_get_connector_object_id(dev,
|
|
@@ -674,7 +814,8 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
|
|
connector_type,
|
|
&bios_connectors[i].ddc_bus,
|
|
false, 0,
|
|
- connector_object_id);
|
|
+ connector_object_id,
|
|
+ &bios_connectors[i].hpd);
|
|
}
|
|
}
|
|
|
|
@@ -739,7 +880,8 @@ bool radeon_atom_get_clock_info(struct drm_device *dev)
|
|
* pre-DCE 3.0 r6xx hardware. This might need to be adjusted per
|
|
* family.
|
|
*/
|
|
- p1pll->pll_out_min = 64800;
|
|
+ if (!radeon_new_pll)
|
|
+ p1pll->pll_out_min = 64800;
|
|
}
|
|
|
|
p1pll->pll_in_min =
|
|
@@ -805,6 +947,43 @@ bool radeon_atom_get_clock_info(struct drm_device *dev)
|
|
return false;
|
|
}
|
|
|
|
+union igp_info {
|
|
+ struct _ATOM_INTEGRATED_SYSTEM_INFO info;
|
|
+ struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 info_2;
|
|
+};
|
|
+
|
|
+bool radeon_atombios_sideport_present(struct radeon_device *rdev)
|
|
+{
|
|
+ struct radeon_mode_info *mode_info = &rdev->mode_info;
|
|
+ int index = GetIndexIntoMasterTable(DATA, IntegratedSystemInfo);
|
|
+ union igp_info *igp_info;
|
|
+ u8 frev, crev;
|
|
+ u16 data_offset;
|
|
+
|
|
+ atom_parse_data_header(mode_info->atom_context, index, NULL, &frev,
|
|
+ &crev, &data_offset);
|
|
+
|
|
+ igp_info = (union igp_info *)(mode_info->atom_context->bios +
|
|
+ data_offset);
|
|
+
|
|
+ if (igp_info) {
|
|
+ switch (crev) {
|
|
+ case 1:
|
|
+ if (igp_info->info.ucMemoryType & 0xf0)
|
|
+ return true;
|
|
+ break;
|
|
+ case 2:
|
|
+ if (igp_info->info_2.ucMemoryType & 0x0f)
|
|
+ return true;
|
|
+ break;
|
|
+ default:
|
|
+ DRM_ERROR("Unsupported IGP table: %d %d\n", frev, crev);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return false;
|
|
+}
|
|
+
|
|
bool radeon_atombios_get_tmds_info(struct radeon_encoder *encoder,
|
|
struct radeon_encoder_int_tmds *tmds)
|
|
{
|
|
@@ -869,6 +1048,7 @@ static struct radeon_atom_ss *radeon_atombios_get_ss_info(struct
|
|
struct _ATOM_SPREAD_SPECTRUM_INFO *ss_info;
|
|
uint8_t frev, crev;
|
|
struct radeon_atom_ss *ss = NULL;
|
|
+ int i;
|
|
|
|
if (id > ATOM_MAX_SS_ENTRY)
|
|
return NULL;
|
|
@@ -886,12 +1066,18 @@ static struct radeon_atom_ss *radeon_atombios_get_ss_info(struct
|
|
if (!ss)
|
|
return NULL;
|
|
|
|
- ss->percentage = le16_to_cpu(ss_info->asSS_Info[id].usSpreadSpectrumPercentage);
|
|
- ss->type = ss_info->asSS_Info[id].ucSpreadSpectrumType;
|
|
- ss->step = ss_info->asSS_Info[id].ucSS_Step;
|
|
- ss->delay = ss_info->asSS_Info[id].ucSS_Delay;
|
|
- ss->range = ss_info->asSS_Info[id].ucSS_Range;
|
|
- ss->refdiv = ss_info->asSS_Info[id].ucRecommendedRef_Div;
|
|
+ for (i = 0; i < ATOM_MAX_SS_ENTRY; i++) {
|
|
+ if (ss_info->asSS_Info[i].ucSS_Id == id) {
|
|
+ ss->percentage =
|
|
+ le16_to_cpu(ss_info->asSS_Info[i].usSpreadSpectrumPercentage);
|
|
+ ss->type = ss_info->asSS_Info[i].ucSpreadSpectrumType;
|
|
+ ss->step = ss_info->asSS_Info[i].ucSS_Step;
|
|
+ ss->delay = ss_info->asSS_Info[i].ucSS_Delay;
|
|
+ ss->range = ss_info->asSS_Info[i].ucSS_Range;
|
|
+ ss->refdiv = ss_info->asSS_Info[i].ucRecommendedRef_Div;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
}
|
|
return ss;
|
|
}
|
|
@@ -909,7 +1095,7 @@ struct radeon_encoder_atom_dig *radeon_atombios_get_lvds_info(struct
|
|
struct radeon_device *rdev = dev->dev_private;
|
|
struct radeon_mode_info *mode_info = &rdev->mode_info;
|
|
int index = GetIndexIntoMasterTable(DATA, LVDS_Info);
|
|
- uint16_t data_offset;
|
|
+ uint16_t data_offset, misc;
|
|
union lvds_info *lvds_info;
|
|
uint8_t frev, crev;
|
|
struct radeon_encoder_atom_dig *lvds = NULL;
|
|
@@ -948,6 +1134,19 @@ struct radeon_encoder_atom_dig *radeon_atombios_get_lvds_info(struct
|
|
lvds->panel_pwr_delay =
|
|
le16_to_cpu(lvds_info->info.usOffDelayInMs);
|
|
lvds->lvds_misc = lvds_info->info.ucLVDS_Misc;
|
|
+
|
|
+ misc = le16_to_cpu(lvds_info->info.sLCDTiming.susModeMiscInfo.usAccess);
|
|
+ if (misc & ATOM_VSYNC_POLARITY)
|
|
+ lvds->native_mode.flags |= DRM_MODE_FLAG_NVSYNC;
|
|
+ if (misc & ATOM_HSYNC_POLARITY)
|
|
+ lvds->native_mode.flags |= DRM_MODE_FLAG_NHSYNC;
|
|
+ if (misc & ATOM_COMPOSITESYNC)
|
|
+ lvds->native_mode.flags |= DRM_MODE_FLAG_CSYNC;
|
|
+ if (misc & ATOM_INTERLACE)
|
|
+ lvds->native_mode.flags |= DRM_MODE_FLAG_INTERLACE;
|
|
+ if (misc & ATOM_DOUBLE_CLOCK_MODE)
|
|
+ lvds->native_mode.flags |= DRM_MODE_FLAG_DBLSCAN;
|
|
+
|
|
/* set crtc values */
|
|
drm_mode_set_crtcinfo(&lvds->native_mode, CRTC_INTERLACE_HALVE_V);
|
|
|
|
@@ -1082,6 +1281,61 @@ bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index,
|
|
return true;
|
|
}
|
|
|
|
+enum radeon_tv_std
|
|
+radeon_atombios_get_tv_info(struct radeon_device *rdev)
|
|
+{
|
|
+ struct radeon_mode_info *mode_info = &rdev->mode_info;
|
|
+ int index = GetIndexIntoMasterTable(DATA, AnalogTV_Info);
|
|
+ uint16_t data_offset;
|
|
+ uint8_t frev, crev;
|
|
+ struct _ATOM_ANALOG_TV_INFO *tv_info;
|
|
+ enum radeon_tv_std tv_std = TV_STD_NTSC;
|
|
+
|
|
+ atom_parse_data_header(mode_info->atom_context, index, NULL, &frev, &crev, &data_offset);
|
|
+
|
|
+ tv_info = (struct _ATOM_ANALOG_TV_INFO *)(mode_info->atom_context->bios + data_offset);
|
|
+
|
|
+ switch (tv_info->ucTV_BootUpDefaultStandard) {
|
|
+ case ATOM_TV_NTSC:
|
|
+ tv_std = TV_STD_NTSC;
|
|
+ DRM_INFO("Default TV standard: NTSC\n");
|
|
+ break;
|
|
+ case ATOM_TV_NTSCJ:
|
|
+ tv_std = TV_STD_NTSC_J;
|
|
+ DRM_INFO("Default TV standard: NTSC-J\n");
|
|
+ break;
|
|
+ case ATOM_TV_PAL:
|
|
+ tv_std = TV_STD_PAL;
|
|
+ DRM_INFO("Default TV standard: PAL\n");
|
|
+ break;
|
|
+ case ATOM_TV_PALM:
|
|
+ tv_std = TV_STD_PAL_M;
|
|
+ DRM_INFO("Default TV standard: PAL-M\n");
|
|
+ break;
|
|
+ case ATOM_TV_PALN:
|
|
+ tv_std = TV_STD_PAL_N;
|
|
+ DRM_INFO("Default TV standard: PAL-N\n");
|
|
+ break;
|
|
+ case ATOM_TV_PALCN:
|
|
+ tv_std = TV_STD_PAL_CN;
|
|
+ DRM_INFO("Default TV standard: PAL-CN\n");
|
|
+ break;
|
|
+ case ATOM_TV_PAL60:
|
|
+ tv_std = TV_STD_PAL_60;
|
|
+ DRM_INFO("Default TV standard: PAL-60\n");
|
|
+ break;
|
|
+ case ATOM_TV_SECAM:
|
|
+ tv_std = TV_STD_SECAM;
|
|
+ DRM_INFO("Default TV standard: SECAM\n");
|
|
+ break;
|
|
+ default:
|
|
+ tv_std = TV_STD_NTSC;
|
|
+ DRM_INFO("Unknown TV standard; defaulting to NTSC\n");
|
|
+ break;
|
|
+ }
|
|
+ return tv_std;
|
|
+}
|
|
+
|
|
struct radeon_encoder_tv_dac *
|
|
radeon_atombios_get_tv_dac_info(struct radeon_encoder *encoder)
|
|
{
|
|
@@ -1117,6 +1371,7 @@ radeon_atombios_get_tv_dac_info(struct radeon_encoder *encoder)
|
|
dac = dac_info->ucDAC2_NTSC_DAC_Adjustment;
|
|
tv_dac->ntsc_tvdac_adj = (bg << 16) | (dac << 20);
|
|
|
|
+ tv_dac->tv_std = radeon_atombios_get_tv_info(rdev);
|
|
}
|
|
return tv_dac;
|
|
}
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_benchmark.c b/drivers/gpu/drm/radeon/radeon_benchmark.c
|
|
index 10bd50a..7932dc4 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_benchmark.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_benchmark.c
|
|
@@ -29,8 +29,8 @@
|
|
void radeon_benchmark_move(struct radeon_device *rdev, unsigned bsize,
|
|
unsigned sdomain, unsigned ddomain)
|
|
{
|
|
- struct radeon_object *dobj = NULL;
|
|
- struct radeon_object *sobj = NULL;
|
|
+ struct radeon_bo *dobj = NULL;
|
|
+ struct radeon_bo *sobj = NULL;
|
|
struct radeon_fence *fence = NULL;
|
|
uint64_t saddr, daddr;
|
|
unsigned long start_jiffies;
|
|
@@ -41,47 +41,66 @@ void radeon_benchmark_move(struct radeon_device *rdev, unsigned bsize,
|
|
|
|
size = bsize;
|
|
n = 1024;
|
|
- r = radeon_object_create(rdev, NULL, size, true, sdomain, false, &sobj);
|
|
+ r = radeon_bo_create(rdev, NULL, size, true, sdomain, &sobj);
|
|
if (r) {
|
|
goto out_cleanup;
|
|
}
|
|
- r = radeon_object_pin(sobj, sdomain, &saddr);
|
|
+ r = radeon_bo_reserve(sobj, false);
|
|
+ if (unlikely(r != 0))
|
|
+ goto out_cleanup;
|
|
+ r = radeon_bo_pin(sobj, sdomain, &saddr);
|
|
+ radeon_bo_unreserve(sobj);
|
|
if (r) {
|
|
goto out_cleanup;
|
|
}
|
|
- r = radeon_object_create(rdev, NULL, size, true, ddomain, false, &dobj);
|
|
+ r = radeon_bo_create(rdev, NULL, size, true, ddomain, &dobj);
|
|
if (r) {
|
|
goto out_cleanup;
|
|
}
|
|
- r = radeon_object_pin(dobj, ddomain, &daddr);
|
|
+ r = radeon_bo_reserve(dobj, false);
|
|
+ if (unlikely(r != 0))
|
|
+ goto out_cleanup;
|
|
+ r = radeon_bo_pin(dobj, ddomain, &daddr);
|
|
+ radeon_bo_unreserve(dobj);
|
|
if (r) {
|
|
goto out_cleanup;
|
|
}
|
|
- start_jiffies = jiffies;
|
|
- for (i = 0; i < n; i++) {
|
|
- r = radeon_fence_create(rdev, &fence);
|
|
- if (r) {
|
|
- goto out_cleanup;
|
|
+
|
|
+ /* r100 doesn't have dma engine so skip the test */
|
|
+ if (rdev->asic->copy_dma) {
|
|
+
|
|
+ start_jiffies = jiffies;
|
|
+ for (i = 0; i < n; i++) {
|
|
+ r = radeon_fence_create(rdev, &fence);
|
|
+ if (r) {
|
|
+ goto out_cleanup;
|
|
+ }
|
|
+
|
|
+ r = radeon_copy_dma(rdev, saddr, daddr,
|
|
+ size / RADEON_GPU_PAGE_SIZE, fence);
|
|
+
|
|
+ if (r) {
|
|
+ goto out_cleanup;
|
|
+ }
|
|
+ r = radeon_fence_wait(fence, false);
|
|
+ if (r) {
|
|
+ goto out_cleanup;
|
|
+ }
|
|
+ radeon_fence_unref(&fence);
|
|
}
|
|
- r = radeon_copy_dma(rdev, saddr, daddr, size / RADEON_GPU_PAGE_SIZE, fence);
|
|
- if (r) {
|
|
- goto out_cleanup;
|
|
+ end_jiffies = jiffies;
|
|
+ time = end_jiffies - start_jiffies;
|
|
+ time = jiffies_to_msecs(time);
|
|
+ if (time > 0) {
|
|
+ i = ((n * size) >> 10) / time;
|
|
+ printk(KERN_INFO "radeon: dma %u bo moves of %ukb from"
|
|
+ " %d to %d in %lums (%ukb/ms %ukb/s %uM/s)\n",
|
|
+ n, size >> 10,
|
|
+ sdomain, ddomain, time,
|
|
+ i, i * 1000, (i * 1000) / 1024);
|
|
}
|
|
- r = radeon_fence_wait(fence, false);
|
|
- if (r) {
|
|
- goto out_cleanup;
|
|
- }
|
|
- radeon_fence_unref(&fence);
|
|
- }
|
|
- end_jiffies = jiffies;
|
|
- time = end_jiffies - start_jiffies;
|
|
- time = jiffies_to_msecs(time);
|
|
- if (time > 0) {
|
|
- i = ((n * size) >> 10) / time;
|
|
- printk(KERN_INFO "radeon: dma %u bo moves of %ukb from %d to %d"
|
|
- " in %lums (%ukb/ms %ukb/s %uM/s)\n", n, size >> 10,
|
|
- sdomain, ddomain, time, i, i * 1000, (i * 1000) / 1024);
|
|
}
|
|
+
|
|
start_jiffies = jiffies;
|
|
for (i = 0; i < n; i++) {
|
|
r = radeon_fence_create(rdev, &fence);
|
|
@@ -109,12 +128,20 @@ void radeon_benchmark_move(struct radeon_device *rdev, unsigned bsize,
|
|
}
|
|
out_cleanup:
|
|
if (sobj) {
|
|
- radeon_object_unpin(sobj);
|
|
- radeon_object_unref(&sobj);
|
|
+ r = radeon_bo_reserve(sobj, false);
|
|
+ if (likely(r == 0)) {
|
|
+ radeon_bo_unpin(sobj);
|
|
+ radeon_bo_unreserve(sobj);
|
|
+ }
|
|
+ radeon_bo_unref(&sobj);
|
|
}
|
|
if (dobj) {
|
|
- radeon_object_unpin(dobj);
|
|
- radeon_object_unref(&dobj);
|
|
+ r = radeon_bo_reserve(dobj, false);
|
|
+ if (likely(r == 0)) {
|
|
+ radeon_bo_unpin(dobj);
|
|
+ radeon_bo_unreserve(dobj);
|
|
+ }
|
|
+ radeon_bo_unref(&dobj);
|
|
}
|
|
if (fence) {
|
|
radeon_fence_unref(&fence);
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_bios.c b/drivers/gpu/drm/radeon/radeon_bios.c
|
|
index 9069217..26856ed 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_bios.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_bios.c
|
|
@@ -411,6 +411,12 @@ bool radeon_get_bios(struct radeon_device *rdev)
|
|
goto free_bios;
|
|
}
|
|
|
|
+ tmp = RBIOS16(0x18);
|
|
+ if (RBIOS8(tmp + 0x14) != 0x0) {
|
|
+ DRM_INFO("Not an x86 BIOS ROM, not using.\n");
|
|
+ goto free_bios;
|
|
+ }
|
|
+
|
|
rdev->bios_header_start = RBIOS16(0x48);
|
|
if (!rdev->bios_header_start) {
|
|
goto free_bios;
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_clocks.c b/drivers/gpu/drm/radeon/radeon_clocks.c
|
|
index a813541..73c4405 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_clocks.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_clocks.c
|
|
@@ -44,6 +44,10 @@ uint32_t radeon_legacy_get_engine_clock(struct radeon_device *rdev)
|
|
|
|
ref_div =
|
|
RREG32_PLL(RADEON_M_SPLL_REF_FB_DIV) & RADEON_M_SPLL_REF_DIV_MASK;
|
|
+
|
|
+ if (ref_div == 0)
|
|
+ return 0;
|
|
+
|
|
sclk = fb_div / ref_div;
|
|
|
|
post_div = RREG32_PLL(RADEON_SCLK_CNTL) & RADEON_SCLK_SRC_SEL_MASK;
|
|
@@ -52,13 +56,13 @@ uint32_t radeon_legacy_get_engine_clock(struct radeon_device *rdev)
|
|
else if (post_div == 3)
|
|
sclk >>= 2;
|
|
else if (post_div == 4)
|
|
- sclk >>= 4;
|
|
+ sclk >>= 3;
|
|
|
|
return sclk;
|
|
}
|
|
|
|
/* 10 khz */
|
|
-static uint32_t radeon_legacy_get_memory_clock(struct radeon_device *rdev)
|
|
+uint32_t radeon_legacy_get_memory_clock(struct radeon_device *rdev)
|
|
{
|
|
struct radeon_pll *mpll = &rdev->clock.mpll;
|
|
uint32_t fb_div, ref_div, post_div, mclk;
|
|
@@ -70,6 +74,10 @@ static uint32_t radeon_legacy_get_memory_clock(struct radeon_device *rdev)
|
|
|
|
ref_div =
|
|
RREG32_PLL(RADEON_M_SPLL_REF_FB_DIV) & RADEON_M_SPLL_REF_DIV_MASK;
|
|
+
|
|
+ if (ref_div == 0)
|
|
+ return 0;
|
|
+
|
|
mclk = fb_div / ref_div;
|
|
|
|
post_div = RREG32_PLL(RADEON_MCLK_CNTL) & 0x7;
|
|
@@ -78,7 +86,7 @@ static uint32_t radeon_legacy_get_memory_clock(struct radeon_device *rdev)
|
|
else if (post_div == 3)
|
|
mclk >>= 2;
|
|
else if (post_div == 4)
|
|
- mclk >>= 4;
|
|
+ mclk >>= 3;
|
|
|
|
return mclk;
|
|
}
|
|
@@ -98,8 +106,19 @@ void radeon_get_clock_info(struct drm_device *dev)
|
|
ret = radeon_combios_get_clock_info(dev);
|
|
|
|
if (ret) {
|
|
- if (p1pll->reference_div < 2)
|
|
- p1pll->reference_div = 12;
|
|
+ if (p1pll->reference_div < 2) {
|
|
+ if (!ASIC_IS_AVIVO(rdev)) {
|
|
+ u32 tmp = RREG32_PLL(RADEON_PPLL_REF_DIV);
|
|
+ if (ASIC_IS_R300(rdev))
|
|
+ p1pll->reference_div =
|
|
+ (tmp & R300_PPLL_REF_DIV_ACC_MASK) >> R300_PPLL_REF_DIV_ACC_SHIFT;
|
|
+ else
|
|
+ p1pll->reference_div = tmp & RADEON_PPLL_REF_DIV_MASK;
|
|
+ if (p1pll->reference_div < 2)
|
|
+ p1pll->reference_div = 12;
|
|
+ } else
|
|
+ p1pll->reference_div = 12;
|
|
+ }
|
|
if (p2pll->reference_div < 2)
|
|
p2pll->reference_div = 12;
|
|
if (rdev->family < CHIP_RS600) {
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c
|
|
index 5253cbf..69f487d 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_combios.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_combios.c
|
|
@@ -50,7 +50,8 @@ radeon_add_legacy_connector(struct drm_device *dev,
|
|
uint32_t supported_device,
|
|
int connector_type,
|
|
struct radeon_i2c_bus_rec *i2c_bus,
|
|
- uint16_t connector_object_id);
|
|
+ uint16_t connector_object_id,
|
|
+ struct radeon_hpd *hpd);
|
|
|
|
/* from radeon_legacy_encoder.c */
|
|
extern void
|
|
@@ -442,38 +443,103 @@ static uint16_t combios_get_table_offset(struct drm_device *dev,
|
|
|
|
}
|
|
|
|
-struct radeon_i2c_bus_rec combios_setup_i2c_bus(int ddc_line)
|
|
+bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev)
|
|
+{
|
|
+ int edid_info;
|
|
+ struct edid *edid;
|
|
+ edid_info = combios_get_table_offset(rdev->ddev, COMBIOS_HARDCODED_EDID_TABLE);
|
|
+ if (!edid_info)
|
|
+ return false;
|
|
+
|
|
+ edid = kmalloc(EDID_LENGTH * (DRM_MAX_EDID_EXT_NUM + 1),
|
|
+ GFP_KERNEL);
|
|
+ if (edid == NULL)
|
|
+ return false;
|
|
+
|
|
+ memcpy((unsigned char *)edid,
|
|
+ (unsigned char *)(rdev->bios + edid_info), EDID_LENGTH);
|
|
+
|
|
+ if (!drm_edid_is_valid(edid)) {
|
|
+ kfree(edid);
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ rdev->mode_info.bios_hardcoded_edid = edid;
|
|
+ return true;
|
|
+}
|
|
+
|
|
+struct edid *
|
|
+radeon_combios_get_hardcoded_edid(struct radeon_device *rdev)
|
|
+{
|
|
+ if (rdev->mode_info.bios_hardcoded_edid)
|
|
+ return rdev->mode_info.bios_hardcoded_edid;
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rdev,
|
|
+ int ddc_line)
|
|
{
|
|
struct radeon_i2c_bus_rec i2c;
|
|
|
|
- i2c.mask_clk_mask = RADEON_GPIO_EN_1;
|
|
- i2c.mask_data_mask = RADEON_GPIO_EN_0;
|
|
- i2c.a_clk_mask = RADEON_GPIO_A_1;
|
|
- i2c.a_data_mask = RADEON_GPIO_A_0;
|
|
- i2c.put_clk_mask = RADEON_GPIO_EN_1;
|
|
- i2c.put_data_mask = RADEON_GPIO_EN_0;
|
|
- i2c.get_clk_mask = RADEON_GPIO_Y_1;
|
|
- i2c.get_data_mask = RADEON_GPIO_Y_0;
|
|
- if ((ddc_line == RADEON_LCD_GPIO_MASK) ||
|
|
- (ddc_line == RADEON_MDGPIO_EN_REG)) {
|
|
- i2c.mask_clk_reg = ddc_line;
|
|
- i2c.mask_data_reg = ddc_line;
|
|
- i2c.a_clk_reg = ddc_line;
|
|
- i2c.a_data_reg = ddc_line;
|
|
- i2c.put_clk_reg = ddc_line;
|
|
- i2c.put_data_reg = ddc_line;
|
|
- i2c.get_clk_reg = ddc_line + 4;
|
|
- i2c.get_data_reg = ddc_line + 4;
|
|
+ if (ddc_line == RADEON_GPIOPAD_MASK) {
|
|
+ i2c.mask_clk_reg = RADEON_GPIOPAD_MASK;
|
|
+ i2c.mask_data_reg = RADEON_GPIOPAD_MASK;
|
|
+ i2c.a_clk_reg = RADEON_GPIOPAD_A;
|
|
+ i2c.a_data_reg = RADEON_GPIOPAD_A;
|
|
+ i2c.en_clk_reg = RADEON_GPIOPAD_EN;
|
|
+ i2c.en_data_reg = RADEON_GPIOPAD_EN;
|
|
+ i2c.y_clk_reg = RADEON_GPIOPAD_Y;
|
|
+ i2c.y_data_reg = RADEON_GPIOPAD_Y;
|
|
+ } else if (ddc_line == RADEON_MDGPIO_MASK) {
|
|
+ i2c.mask_clk_reg = RADEON_MDGPIO_MASK;
|
|
+ i2c.mask_data_reg = RADEON_MDGPIO_MASK;
|
|
+ i2c.a_clk_reg = RADEON_MDGPIO_A;
|
|
+ i2c.a_data_reg = RADEON_MDGPIO_A;
|
|
+ i2c.en_clk_reg = RADEON_MDGPIO_EN;
|
|
+ i2c.en_data_reg = RADEON_MDGPIO_EN;
|
|
+ i2c.y_clk_reg = RADEON_MDGPIO_Y;
|
|
+ i2c.y_data_reg = RADEON_MDGPIO_Y;
|
|
} else {
|
|
+ i2c.mask_clk_mask = RADEON_GPIO_EN_1;
|
|
+ i2c.mask_data_mask = RADEON_GPIO_EN_0;
|
|
+ i2c.a_clk_mask = RADEON_GPIO_A_1;
|
|
+ i2c.a_data_mask = RADEON_GPIO_A_0;
|
|
+ i2c.en_clk_mask = RADEON_GPIO_EN_1;
|
|
+ i2c.en_data_mask = RADEON_GPIO_EN_0;
|
|
+ i2c.y_clk_mask = RADEON_GPIO_Y_1;
|
|
+ i2c.y_data_mask = RADEON_GPIO_Y_0;
|
|
+
|
|
i2c.mask_clk_reg = ddc_line;
|
|
i2c.mask_data_reg = ddc_line;
|
|
i2c.a_clk_reg = ddc_line;
|
|
i2c.a_data_reg = ddc_line;
|
|
- i2c.put_clk_reg = ddc_line;
|
|
- i2c.put_data_reg = ddc_line;
|
|
- i2c.get_clk_reg = ddc_line;
|
|
- i2c.get_data_reg = ddc_line;
|
|
+ i2c.en_clk_reg = ddc_line;
|
|
+ i2c.en_data_reg = ddc_line;
|
|
+ i2c.y_clk_reg = ddc_line;
|
|
+ i2c.y_data_reg = ddc_line;
|
|
+ }
|
|
+
|
|
+ if (rdev->family < CHIP_R200)
|
|
+ i2c.hw_capable = false;
|
|
+ else {
|
|
+ switch (ddc_line) {
|
|
+ case RADEON_GPIO_VGA_DDC:
|
|
+ case RADEON_GPIO_DVI_DDC:
|
|
+ i2c.hw_capable = true;
|
|
+ break;
|
|
+ case RADEON_GPIO_MONID:
|
|
+ /* hw i2c on RADEON_GPIO_MONID doesn't seem to work
|
|
+ * reliably on some pre-r4xx hardware; not sure why.
|
|
+ */
|
|
+ i2c.hw_capable = false;
|
|
+ break;
|
|
+ default:
|
|
+ i2c.hw_capable = false;
|
|
+ break;
|
|
+ }
|
|
}
|
|
+ i2c.mm_i2c = false;
|
|
+ i2c.i2c_id = 0;
|
|
|
|
if (ddc_line)
|
|
i2c.valid = true;
|
|
@@ -495,7 +561,7 @@ bool radeon_combios_get_clock_info(struct drm_device *dev)
|
|
uint16_t sclk, mclk;
|
|
|
|
if (rdev->bios == NULL)
|
|
- return NULL;
|
|
+ return false;
|
|
|
|
pll_info = combios_get_table_offset(dev, COMBIOS_PLL_INFO_TABLE);
|
|
if (pll_info) {
|
|
@@ -562,6 +628,48 @@ bool radeon_combios_get_clock_info(struct drm_device *dev)
|
|
return false;
|
|
}
|
|
|
|
+bool radeon_combios_sideport_present(struct radeon_device *rdev)
|
|
+{
|
|
+ struct drm_device *dev = rdev->ddev;
|
|
+ u16 igp_info;
|
|
+
|
|
+ igp_info = combios_get_table_offset(dev, COMBIOS_INTEGRATED_SYSTEM_INFO_TABLE);
|
|
+
|
|
+ if (igp_info) {
|
|
+ if (RBIOS16(igp_info + 0x4))
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+}
|
|
+
|
|
+static const uint32_t default_primarydac_adj[CHIP_LAST] = {
|
|
+ 0x00000808, /* r100 */
|
|
+ 0x00000808, /* rv100 */
|
|
+ 0x00000808, /* rs100 */
|
|
+ 0x00000808, /* rv200 */
|
|
+ 0x00000808, /* rs200 */
|
|
+ 0x00000808, /* r200 */
|
|
+ 0x00000808, /* rv250 */
|
|
+ 0x00000000, /* rs300 */
|
|
+ 0x00000808, /* rv280 */
|
|
+ 0x00000808, /* r300 */
|
|
+ 0x00000808, /* r350 */
|
|
+ 0x00000808, /* rv350 */
|
|
+ 0x00000808, /* rv380 */
|
|
+ 0x00000808, /* r420 */
|
|
+ 0x00000808, /* r423 */
|
|
+ 0x00000808, /* rv410 */
|
|
+ 0x00000000, /* rs400 */
|
|
+ 0x00000000, /* rs480 */
|
|
+};
|
|
+
|
|
+static void radeon_legacy_get_primary_dac_info_from_table(struct radeon_device *rdev,
|
|
+ struct radeon_encoder_primary_dac *p_dac)
|
|
+{
|
|
+ p_dac->ps2_pdac_adj = default_primarydac_adj[rdev->family];
|
|
+ return;
|
|
+}
|
|
+
|
|
struct radeon_encoder_primary_dac *radeon_combios_get_primary_dac_info(struct
|
|
radeon_encoder
|
|
*encoder)
|
|
@@ -571,20 +679,20 @@ struct radeon_encoder_primary_dac *radeon_combios_get_primary_dac_info(struct
|
|
uint16_t dac_info;
|
|
uint8_t rev, bg, dac;
|
|
struct radeon_encoder_primary_dac *p_dac = NULL;
|
|
+ int found = 0;
|
|
|
|
- if (rdev->bios == NULL)
|
|
+ p_dac = kzalloc(sizeof(struct radeon_encoder_primary_dac),
|
|
+ GFP_KERNEL);
|
|
+
|
|
+ if (!p_dac)
|
|
return NULL;
|
|
|
|
+ if (rdev->bios == NULL)
|
|
+ goto out;
|
|
+
|
|
/* check CRT table */
|
|
dac_info = combios_get_table_offset(dev, COMBIOS_CRT_INFO_TABLE);
|
|
if (dac_info) {
|
|
- p_dac =
|
|
- kzalloc(sizeof(struct radeon_encoder_primary_dac),
|
|
- GFP_KERNEL);
|
|
-
|
|
- if (!p_dac)
|
|
- return NULL;
|
|
-
|
|
rev = RBIOS8(dac_info) & 0x3;
|
|
if (rev < 2) {
|
|
bg = RBIOS8(dac_info + 0x2) & 0xf;
|
|
@@ -595,20 +703,26 @@ struct radeon_encoder_primary_dac *radeon_combios_get_primary_dac_info(struct
|
|
dac = RBIOS8(dac_info + 0x3) & 0xf;
|
|
p_dac->ps2_pdac_adj = (bg << 8) | (dac);
|
|
}
|
|
-
|
|
+ found = 1;
|
|
}
|
|
|
|
+out:
|
|
+ if (!found) /* fallback to defaults */
|
|
+ radeon_legacy_get_primary_dac_info_from_table(rdev, p_dac);
|
|
+
|
|
return p_dac;
|
|
}
|
|
|
|
-static enum radeon_tv_std
|
|
-radeon_combios_get_tv_info(struct radeon_encoder *encoder)
|
|
+enum radeon_tv_std
|
|
+radeon_combios_get_tv_info(struct radeon_device *rdev)
|
|
{
|
|
- struct drm_device *dev = encoder->base.dev;
|
|
- struct radeon_device *rdev = dev->dev_private;
|
|
+ struct drm_device *dev = rdev->ddev;
|
|
uint16_t tv_info;
|
|
enum radeon_tv_std tv_std = TV_STD_NTSC;
|
|
|
|
+ if (rdev->bios == NULL)
|
|
+ return tv_std;
|
|
+
|
|
tv_info = combios_get_table_offset(dev, COMBIOS_TV_INFO_TABLE);
|
|
if (tv_info) {
|
|
if (RBIOS8(tv_info + 6) == 'T') {
|
|
@@ -746,7 +860,7 @@ struct radeon_encoder_tv_dac *radeon_combios_get_tv_dac_info(struct
|
|
tv_dac->ntsc_tvdac_adj = (bg << 16) | (dac << 20);
|
|
found = 1;
|
|
}
|
|
- tv_dac->tv_std = radeon_combios_get_tv_info(encoder);
|
|
+ tv_dac->tv_std = radeon_combios_get_tv_info(rdev);
|
|
}
|
|
if (!found) {
|
|
/* then check CRT table */
|
|
@@ -890,8 +1004,7 @@ struct radeon_encoder_lvds *radeon_combios_get_lvds_info(struct radeon_encoder
|
|
lvds->native_mode.vdisplay);
|
|
|
|
lvds->panel_vcc_delay = RBIOS16(lcd_info + 0x2c);
|
|
- if (lvds->panel_vcc_delay > 2000 || lvds->panel_vcc_delay < 0)
|
|
- lvds->panel_vcc_delay = 2000;
|
|
+ lvds->panel_vcc_delay = min_t(u16, lvds->panel_vcc_delay, 2000);
|
|
|
|
lvds->panel_pwr_delay = RBIOS8(lcd_info + 0x24);
|
|
lvds->panel_digon_delay = RBIOS16(lcd_info + 0x38) & 0xf;
|
|
@@ -993,8 +1106,8 @@ static const struct radeon_tmds_pll default_tmds_pll[CHIP_LAST][4] = {
|
|
{{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}}, /* CHIP_R420 */
|
|
{{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}}, /* CHIP_R423 */
|
|
{{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}}, /* CHIP_RV410 */
|
|
- {{15000, 0xb0155}, {0xffffffff, 0xb01cb}, {0, 0}, {0, 0}}, /* CHIP_RS400 */
|
|
- {{15000, 0xb0155}, {0xffffffff, 0xb01cb}, {0, 0}, {0, 0}}, /* CHIP_RS480 */
|
|
+ { {0, 0}, {0, 0}, {0, 0}, {0, 0} }, /* CHIP_RS400 */
|
|
+ { {0, 0}, {0, 0}, {0, 0}, {0, 0} }, /* CHIP_RS480 */
|
|
};
|
|
|
|
bool radeon_legacy_get_tmds_info_from_table(struct radeon_encoder *encoder,
|
|
@@ -1028,7 +1141,6 @@ bool radeon_legacy_get_tmds_info_from_combios(struct radeon_encoder *encoder,
|
|
tmds_info = combios_get_table_offset(dev, COMBIOS_DFP_INFO_TABLE);
|
|
|
|
if (tmds_info) {
|
|
-
|
|
ver = RBIOS8(tmds_info);
|
|
DRM_INFO("DFP table revision: %d\n", ver);
|
|
if (ver == 3) {
|
|
@@ -1063,51 +1175,139 @@ bool radeon_legacy_get_tmds_info_from_combios(struct radeon_encoder *encoder,
|
|
tmds->tmds_pll[i].value);
|
|
}
|
|
}
|
|
- } else
|
|
+ } else {
|
|
DRM_INFO("No TMDS info found in BIOS\n");
|
|
+ return false;
|
|
+ }
|
|
return true;
|
|
}
|
|
|
|
-struct radeon_encoder_int_tmds *radeon_combios_get_tmds_info(struct radeon_encoder *encoder)
|
|
+bool radeon_legacy_get_ext_tmds_info_from_table(struct radeon_encoder *encoder,
|
|
+ struct radeon_encoder_ext_tmds *tmds)
|
|
{
|
|
- struct radeon_encoder_int_tmds *tmds = NULL;
|
|
- bool ret;
|
|
-
|
|
- tmds = kzalloc(sizeof(struct radeon_encoder_int_tmds), GFP_KERNEL);
|
|
+ struct drm_device *dev = encoder->base.dev;
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
+ struct radeon_i2c_bus_rec i2c_bus;
|
|
|
|
- if (!tmds)
|
|
- return NULL;
|
|
+ /* default for macs */
|
|
+ i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
|
|
+ tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
|
|
|
|
- ret = radeon_legacy_get_tmds_info_from_combios(encoder, tmds);
|
|
- if (ret == false)
|
|
- radeon_legacy_get_tmds_info_from_table(encoder, tmds);
|
|
+ /* XXX some macs have duallink chips */
|
|
+ switch (rdev->mode_info.connector_table) {
|
|
+ case CT_POWERBOOK_EXTERNAL:
|
|
+ case CT_MINI_EXTERNAL:
|
|
+ default:
|
|
+ tmds->dvo_chip = DVO_SIL164;
|
|
+ tmds->slave_addr = 0x70 >> 1; /* 7 bit addressing */
|
|
+ break;
|
|
+ }
|
|
|
|
- return tmds;
|
|
+ return true;
|
|
}
|
|
|
|
-void radeon_combios_get_ext_tmds_info(struct radeon_encoder *encoder)
|
|
+bool radeon_legacy_get_ext_tmds_info_from_combios(struct radeon_encoder *encoder,
|
|
+ struct radeon_encoder_ext_tmds *tmds)
|
|
{
|
|
struct drm_device *dev = encoder->base.dev;
|
|
struct radeon_device *rdev = dev->dev_private;
|
|
- uint16_t ext_tmds_info;
|
|
- uint8_t ver;
|
|
+ uint16_t offset;
|
|
+ uint8_t ver, id, blocks, clk, data;
|
|
+ int i;
|
|
+ enum radeon_combios_ddc gpio;
|
|
+ struct radeon_i2c_bus_rec i2c_bus;
|
|
|
|
if (rdev->bios == NULL)
|
|
- return;
|
|
+ return false;
|
|
|
|
- ext_tmds_info =
|
|
- combios_get_table_offset(dev, COMBIOS_EXT_TMDS_INFO_TABLE);
|
|
- if (ext_tmds_info) {
|
|
- ver = RBIOS8(ext_tmds_info);
|
|
- DRM_INFO("External TMDS Table revision: %d\n", ver);
|
|
- // TODO
|
|
+ tmds->i2c_bus = NULL;
|
|
+ if (rdev->flags & RADEON_IS_IGP) {
|
|
+ offset = combios_get_table_offset(dev, COMBIOS_I2C_INFO_TABLE);
|
|
+ if (offset) {
|
|
+ ver = RBIOS8(offset);
|
|
+ DRM_INFO("GPIO Table revision: %d\n", ver);
|
|
+ blocks = RBIOS8(offset + 2);
|
|
+ for (i = 0; i < blocks; i++) {
|
|
+ id = RBIOS8(offset + 3 + (i * 5) + 0);
|
|
+ if (id == 136) {
|
|
+ clk = RBIOS8(offset + 3 + (i * 5) + 3);
|
|
+ data = RBIOS8(offset + 3 + (i * 5) + 4);
|
|
+ i2c_bus.valid = true;
|
|
+ i2c_bus.mask_clk_mask = (1 << clk);
|
|
+ i2c_bus.mask_data_mask = (1 << data);
|
|
+ i2c_bus.a_clk_mask = (1 << clk);
|
|
+ i2c_bus.a_data_mask = (1 << data);
|
|
+ i2c_bus.en_clk_mask = (1 << clk);
|
|
+ i2c_bus.en_data_mask = (1 << data);
|
|
+ i2c_bus.y_clk_mask = (1 << clk);
|
|
+ i2c_bus.y_data_mask = (1 << data);
|
|
+ i2c_bus.mask_clk_reg = RADEON_GPIOPAD_MASK;
|
|
+ i2c_bus.mask_data_reg = RADEON_GPIOPAD_MASK;
|
|
+ i2c_bus.a_clk_reg = RADEON_GPIOPAD_A;
|
|
+ i2c_bus.a_data_reg = RADEON_GPIOPAD_A;
|
|
+ i2c_bus.en_clk_reg = RADEON_GPIOPAD_EN;
|
|
+ i2c_bus.en_data_reg = RADEON_GPIOPAD_EN;
|
|
+ i2c_bus.y_clk_reg = RADEON_GPIOPAD_Y;
|
|
+ i2c_bus.y_data_reg = RADEON_GPIOPAD_Y;
|
|
+ tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
|
|
+ tmds->dvo_chip = DVO_SIL164;
|
|
+ tmds->slave_addr = 0x70 >> 1; /* 7 bit addressing */
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ offset = combios_get_table_offset(dev, COMBIOS_EXT_TMDS_INFO_TABLE);
|
|
+ if (offset) {
|
|
+ ver = RBIOS8(offset);
|
|
+ DRM_INFO("External TMDS Table revision: %d\n", ver);
|
|
+ tmds->slave_addr = RBIOS8(offset + 4 + 2);
|
|
+ tmds->slave_addr >>= 1; /* 7 bit addressing */
|
|
+ gpio = RBIOS8(offset + 4 + 3);
|
|
+ switch (gpio) {
|
|
+ case DDC_MONID:
|
|
+ i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
|
|
+ tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
|
|
+ break;
|
|
+ case DDC_DVI:
|
|
+ i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
|
|
+ tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
|
|
+ break;
|
|
+ case DDC_VGA:
|
|
+ i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
|
|
+ tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
|
|
+ break;
|
|
+ case DDC_CRT2:
|
|
+ /* R3xx+ chips don't have GPIO_CRT2_DDC gpio pad */
|
|
+ if (rdev->family >= CHIP_R300)
|
|
+ i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
|
|
+ else
|
|
+ i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
|
|
+ tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
|
|
+ break;
|
|
+ case DDC_LCD: /* MM i2c */
|
|
+ DRM_ERROR("MM i2c requires hw i2c engine\n");
|
|
+ break;
|
|
+ default:
|
|
+ DRM_ERROR("Unsupported gpio %d\n", gpio);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!tmds->i2c_bus) {
|
|
+ DRM_INFO("No valid Ext TMDS info found in BIOS\n");
|
|
+ return false;
|
|
}
|
|
+
|
|
+ return true;
|
|
}
|
|
|
|
bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
{
|
|
struct radeon_device *rdev = dev->dev_private;
|
|
struct radeon_i2c_bus_rec ddc_i2c;
|
|
+ struct radeon_hpd hpd;
|
|
|
|
rdev->mode_info.connector_table = radeon_connector_table;
|
|
if (rdev->mode_info.connector_table == CT_NONE) {
|
|
@@ -1168,7 +1368,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
/* these are the most common settings */
|
|
if (rdev->flags & RADEON_SINGLE_CRTC) {
|
|
/* VGA - primary dac */
|
|
- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
|
|
+ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_CRT1_SUPPORT,
|
|
@@ -1178,10 +1379,12 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
ATOM_DEVICE_CRT1_SUPPORT,
|
|
DRM_MODE_CONNECTOR_VGA,
|
|
&ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_VGA);
|
|
+ CONNECTOR_OBJECT_ID_VGA,
|
|
+ &hpd);
|
|
} else if (rdev->flags & RADEON_IS_MOBILITY) {
|
|
/* LVDS */
|
|
- ddc_i2c = combios_setup_i2c_bus(RADEON_LCD_GPIO_MASK);
|
|
+ ddc_i2c = combios_setup_i2c_bus(rdev, 0);
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_LCD1_SUPPORT,
|
|
@@ -1191,10 +1394,12 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
ATOM_DEVICE_LCD1_SUPPORT,
|
|
DRM_MODE_CONNECTOR_LVDS,
|
|
&ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_LVDS);
|
|
+ CONNECTOR_OBJECT_ID_LVDS,
|
|
+ &hpd);
|
|
|
|
/* VGA - primary dac */
|
|
- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
|
|
+ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_CRT1_SUPPORT,
|
|
@@ -1204,10 +1409,12 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
ATOM_DEVICE_CRT1_SUPPORT,
|
|
DRM_MODE_CONNECTOR_VGA,
|
|
&ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_VGA);
|
|
+ CONNECTOR_OBJECT_ID_VGA,
|
|
+ &hpd);
|
|
} else {
|
|
/* DVI-I - tv dac, int tmds */
|
|
- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC);
|
|
+ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
|
|
+ hpd.hpd = RADEON_HPD_1;
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_DFP1_SUPPORT,
|
|
@@ -1223,10 +1430,12 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
ATOM_DEVICE_CRT2_SUPPORT,
|
|
DRM_MODE_CONNECTOR_DVII,
|
|
&ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I);
|
|
+ CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I,
|
|
+ &hpd);
|
|
|
|
/* VGA - primary dac */
|
|
- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
|
|
+ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_CRT1_SUPPORT,
|
|
@@ -1236,11 +1445,14 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
ATOM_DEVICE_CRT1_SUPPORT,
|
|
DRM_MODE_CONNECTOR_VGA,
|
|
&ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_VGA);
|
|
+ CONNECTOR_OBJECT_ID_VGA,
|
|
+ &hpd);
|
|
}
|
|
|
|
if (rdev->family != CHIP_R100 && rdev->family != CHIP_R200) {
|
|
/* TV - tv dac */
|
|
+ ddc_i2c.valid = false;
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_TV1_SUPPORT,
|
|
@@ -1250,14 +1462,16 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
ATOM_DEVICE_TV1_SUPPORT,
|
|
DRM_MODE_CONNECTOR_SVIDEO,
|
|
&ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_SVIDEO);
|
|
+ CONNECTOR_OBJECT_ID_SVIDEO,
|
|
+ &hpd);
|
|
}
|
|
break;
|
|
case CT_IBOOK:
|
|
DRM_INFO("Connector Table: %d (ibook)\n",
|
|
rdev->mode_info.connector_table);
|
|
/* LVDS */
|
|
- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC);
|
|
+ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_LCD1_SUPPORT,
|
|
@@ -1265,9 +1479,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
ATOM_DEVICE_LCD1_SUPPORT);
|
|
radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT,
|
|
DRM_MODE_CONNECTOR_LVDS, &ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_LVDS);
|
|
+ CONNECTOR_OBJECT_ID_LVDS,
|
|
+ &hpd);
|
|
/* VGA - TV DAC */
|
|
- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
|
|
+ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_CRT2_SUPPORT,
|
|
@@ -1275,8 +1491,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
ATOM_DEVICE_CRT2_SUPPORT);
|
|
radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT2_SUPPORT,
|
|
DRM_MODE_CONNECTOR_VGA, &ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_VGA);
|
|
+ CONNECTOR_OBJECT_ID_VGA,
|
|
+ &hpd);
|
|
/* TV - TV DAC */
|
|
+ ddc_i2c.valid = false;
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_TV1_SUPPORT,
|
|
@@ -1285,13 +1504,15 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT,
|
|
DRM_MODE_CONNECTOR_SVIDEO,
|
|
&ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_SVIDEO);
|
|
+ CONNECTOR_OBJECT_ID_SVIDEO,
|
|
+ &hpd);
|
|
break;
|
|
case CT_POWERBOOK_EXTERNAL:
|
|
DRM_INFO("Connector Table: %d (powerbook external tmds)\n",
|
|
rdev->mode_info.connector_table);
|
|
/* LVDS */
|
|
- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC);
|
|
+ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_LCD1_SUPPORT,
|
|
@@ -1299,9 +1520,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
ATOM_DEVICE_LCD1_SUPPORT);
|
|
radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT,
|
|
DRM_MODE_CONNECTOR_LVDS, &ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_LVDS);
|
|
+ CONNECTOR_OBJECT_ID_LVDS,
|
|
+ &hpd);
|
|
/* DVI-I - primary dac, ext tmds */
|
|
- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
|
|
+ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
|
|
+ hpd.hpd = RADEON_HPD_2; /* ??? */
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_DFP2_SUPPORT,
|
|
@@ -1317,8 +1540,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
ATOM_DEVICE_DFP2_SUPPORT |
|
|
ATOM_DEVICE_CRT1_SUPPORT,
|
|
DRM_MODE_CONNECTOR_DVII, &ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I);
|
|
+ CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I,
|
|
+ &hpd);
|
|
/* TV - TV DAC */
|
|
+ ddc_i2c.valid = false;
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_TV1_SUPPORT,
|
|
@@ -1327,13 +1553,15 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT,
|
|
DRM_MODE_CONNECTOR_SVIDEO,
|
|
&ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_SVIDEO);
|
|
+ CONNECTOR_OBJECT_ID_SVIDEO,
|
|
+ &hpd);
|
|
break;
|
|
case CT_POWERBOOK_INTERNAL:
|
|
DRM_INFO("Connector Table: %d (powerbook internal tmds)\n",
|
|
rdev->mode_info.connector_table);
|
|
/* LVDS */
|
|
- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC);
|
|
+ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_LCD1_SUPPORT,
|
|
@@ -1341,9 +1569,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
ATOM_DEVICE_LCD1_SUPPORT);
|
|
radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT,
|
|
DRM_MODE_CONNECTOR_LVDS, &ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_LVDS);
|
|
+ CONNECTOR_OBJECT_ID_LVDS,
|
|
+ &hpd);
|
|
/* DVI-I - primary dac, int tmds */
|
|
- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
|
|
+ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
|
|
+ hpd.hpd = RADEON_HPD_1; /* ??? */
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_DFP1_SUPPORT,
|
|
@@ -1358,8 +1588,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
ATOM_DEVICE_DFP1_SUPPORT |
|
|
ATOM_DEVICE_CRT1_SUPPORT,
|
|
DRM_MODE_CONNECTOR_DVII, &ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I);
|
|
+ CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I,
|
|
+ &hpd);
|
|
/* TV - TV DAC */
|
|
+ ddc_i2c.valid = false;
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_TV1_SUPPORT,
|
|
@@ -1368,13 +1601,15 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT,
|
|
DRM_MODE_CONNECTOR_SVIDEO,
|
|
&ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_SVIDEO);
|
|
+ CONNECTOR_OBJECT_ID_SVIDEO,
|
|
+ &hpd);
|
|
break;
|
|
case CT_POWERBOOK_VGA:
|
|
DRM_INFO("Connector Table: %d (powerbook vga)\n",
|
|
rdev->mode_info.connector_table);
|
|
/* LVDS */
|
|
- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC);
|
|
+ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_LCD1_SUPPORT,
|
|
@@ -1382,9 +1617,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
ATOM_DEVICE_LCD1_SUPPORT);
|
|
radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT,
|
|
DRM_MODE_CONNECTOR_LVDS, &ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_LVDS);
|
|
+ CONNECTOR_OBJECT_ID_LVDS,
|
|
+ &hpd);
|
|
/* VGA - primary dac */
|
|
- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
|
|
+ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_CRT1_SUPPORT,
|
|
@@ -1392,8 +1629,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
ATOM_DEVICE_CRT1_SUPPORT);
|
|
radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT1_SUPPORT,
|
|
DRM_MODE_CONNECTOR_VGA, &ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_VGA);
|
|
+ CONNECTOR_OBJECT_ID_VGA,
|
|
+ &hpd);
|
|
/* TV - TV DAC */
|
|
+ ddc_i2c.valid = false;
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_TV1_SUPPORT,
|
|
@@ -1402,13 +1642,15 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT,
|
|
DRM_MODE_CONNECTOR_SVIDEO,
|
|
&ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_SVIDEO);
|
|
+ CONNECTOR_OBJECT_ID_SVIDEO,
|
|
+ &hpd);
|
|
break;
|
|
case CT_MINI_EXTERNAL:
|
|
DRM_INFO("Connector Table: %d (mini external tmds)\n",
|
|
rdev->mode_info.connector_table);
|
|
/* DVI-I - tv dac, ext tmds */
|
|
- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_CRT2_DDC);
|
|
+ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
|
|
+ hpd.hpd = RADEON_HPD_2; /* ??? */
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_DFP2_SUPPORT,
|
|
@@ -1424,8 +1666,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
ATOM_DEVICE_DFP2_SUPPORT |
|
|
ATOM_DEVICE_CRT2_SUPPORT,
|
|
DRM_MODE_CONNECTOR_DVII, &ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I);
|
|
+ CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I,
|
|
+ &hpd);
|
|
/* TV - TV DAC */
|
|
+ ddc_i2c.valid = false;
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_TV1_SUPPORT,
|
|
@@ -1434,13 +1679,15 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_TV1_SUPPORT,
|
|
DRM_MODE_CONNECTOR_SVIDEO,
|
|
&ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_SVIDEO);
|
|
+ CONNECTOR_OBJECT_ID_SVIDEO,
|
|
+ &hpd);
|
|
break;
|
|
case CT_MINI_INTERNAL:
|
|
DRM_INFO("Connector Table: %d (mini internal tmds)\n",
|
|
rdev->mode_info.connector_table);
|
|
/* DVI-I - tv dac, int tmds */
|
|
- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_CRT2_DDC);
|
|
+ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
|
|
+ hpd.hpd = RADEON_HPD_1; /* ??? */
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_DFP1_SUPPORT,
|
|
@@ -1455,8 +1702,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
ATOM_DEVICE_DFP1_SUPPORT |
|
|
ATOM_DEVICE_CRT2_SUPPORT,
|
|
DRM_MODE_CONNECTOR_DVII, &ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I);
|
|
+ CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I,
|
|
+ &hpd);
|
|
/* TV - TV DAC */
|
|
+ ddc_i2c.valid = false;
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_TV1_SUPPORT,
|
|
@@ -1465,13 +1715,15 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_TV1_SUPPORT,
|
|
DRM_MODE_CONNECTOR_SVIDEO,
|
|
&ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_SVIDEO);
|
|
+ CONNECTOR_OBJECT_ID_SVIDEO,
|
|
+ &hpd);
|
|
break;
|
|
case CT_IMAC_G5_ISIGHT:
|
|
DRM_INFO("Connector Table: %d (imac g5 isight)\n",
|
|
rdev->mode_info.connector_table);
|
|
/* DVI-D - int tmds */
|
|
- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_MONID);
|
|
+ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
|
|
+ hpd.hpd = RADEON_HPD_1; /* ??? */
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_DFP1_SUPPORT,
|
|
@@ -1479,9 +1731,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
ATOM_DEVICE_DFP1_SUPPORT);
|
|
radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_DFP1_SUPPORT,
|
|
DRM_MODE_CONNECTOR_DVID, &ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D);
|
|
+ CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D,
|
|
+ &hpd);
|
|
/* VGA - tv dac */
|
|
- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC);
|
|
+ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_CRT2_SUPPORT,
|
|
@@ -1489,8 +1743,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
ATOM_DEVICE_CRT2_SUPPORT);
|
|
radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT2_SUPPORT,
|
|
DRM_MODE_CONNECTOR_VGA, &ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_VGA);
|
|
+ CONNECTOR_OBJECT_ID_VGA,
|
|
+ &hpd);
|
|
/* TV - TV DAC */
|
|
+ ddc_i2c.valid = false;
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_TV1_SUPPORT,
|
|
@@ -1499,13 +1756,15 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT,
|
|
DRM_MODE_CONNECTOR_SVIDEO,
|
|
&ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_SVIDEO);
|
|
+ CONNECTOR_OBJECT_ID_SVIDEO,
|
|
+ &hpd);
|
|
break;
|
|
case CT_EMAC:
|
|
DRM_INFO("Connector Table: %d (emac)\n",
|
|
rdev->mode_info.connector_table);
|
|
/* VGA - primary dac */
|
|
- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
|
|
+ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_CRT1_SUPPORT,
|
|
@@ -1513,9 +1772,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
ATOM_DEVICE_CRT1_SUPPORT);
|
|
radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_CRT1_SUPPORT,
|
|
DRM_MODE_CONNECTOR_VGA, &ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_VGA);
|
|
+ CONNECTOR_OBJECT_ID_VGA,
|
|
+ &hpd);
|
|
/* VGA - tv dac */
|
|
- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_CRT2_DDC);
|
|
+ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_CRT2_SUPPORT,
|
|
@@ -1523,8 +1784,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
ATOM_DEVICE_CRT2_SUPPORT);
|
|
radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT2_SUPPORT,
|
|
DRM_MODE_CONNECTOR_VGA, &ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_VGA);
|
|
+ CONNECTOR_OBJECT_ID_VGA,
|
|
+ &hpd);
|
|
/* TV - TV DAC */
|
|
+ ddc_i2c.valid = false;
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id(dev,
|
|
ATOM_DEVICE_TV1_SUPPORT,
|
|
@@ -1533,7 +1797,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
|
|
radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT,
|
|
DRM_MODE_CONNECTOR_SVIDEO,
|
|
&ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_SVIDEO);
|
|
+ CONNECTOR_OBJECT_ID_SVIDEO,
|
|
+ &hpd);
|
|
break;
|
|
default:
|
|
DRM_INFO("Connector table: %d (invalid)\n",
|
|
@@ -1550,7 +1815,8 @@ static bool radeon_apply_legacy_quirks(struct drm_device *dev,
|
|
int bios_index,
|
|
enum radeon_combios_connector
|
|
*legacy_connector,
|
|
- struct radeon_i2c_bus_rec *ddc_i2c)
|
|
+ struct radeon_i2c_bus_rec *ddc_i2c,
|
|
+ struct radeon_hpd *hpd)
|
|
{
|
|
struct radeon_device *rdev = dev->dev_private;
|
|
|
|
@@ -1558,29 +1824,26 @@ static bool radeon_apply_legacy_quirks(struct drm_device *dev,
|
|
if ((rdev->family == CHIP_RS400 ||
|
|
rdev->family == CHIP_RS480) &&
|
|
ddc_i2c->mask_clk_reg == RADEON_GPIO_CRT2_DDC)
|
|
- *ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_MONID);
|
|
+ *ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
|
|
else if ((rdev->family == CHIP_RS400 ||
|
|
rdev->family == CHIP_RS480) &&
|
|
ddc_i2c->mask_clk_reg == RADEON_GPIO_MONID) {
|
|
- ddc_i2c->valid = true;
|
|
+ *ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIOPAD_MASK);
|
|
ddc_i2c->mask_clk_mask = (0x20 << 8);
|
|
ddc_i2c->mask_data_mask = 0x80;
|
|
ddc_i2c->a_clk_mask = (0x20 << 8);
|
|
ddc_i2c->a_data_mask = 0x80;
|
|
- ddc_i2c->put_clk_mask = (0x20 << 8);
|
|
- ddc_i2c->put_data_mask = 0x80;
|
|
- ddc_i2c->get_clk_mask = (0x20 << 8);
|
|
- ddc_i2c->get_data_mask = 0x80;
|
|
- ddc_i2c->mask_clk_reg = RADEON_GPIOPAD_MASK;
|
|
- ddc_i2c->mask_data_reg = RADEON_GPIOPAD_MASK;
|
|
- ddc_i2c->a_clk_reg = RADEON_GPIOPAD_A;
|
|
- ddc_i2c->a_data_reg = RADEON_GPIOPAD_A;
|
|
- ddc_i2c->put_clk_reg = RADEON_GPIOPAD_EN;
|
|
- ddc_i2c->put_data_reg = RADEON_GPIOPAD_EN;
|
|
- ddc_i2c->get_clk_reg = RADEON_LCD_GPIO_Y_REG;
|
|
- ddc_i2c->get_data_reg = RADEON_LCD_GPIO_Y_REG;
|
|
+ ddc_i2c->en_clk_mask = (0x20 << 8);
|
|
+ ddc_i2c->en_data_mask = 0x80;
|
|
+ ddc_i2c->y_clk_mask = (0x20 << 8);
|
|
+ ddc_i2c->y_data_mask = 0x80;
|
|
}
|
|
|
|
+ /* R3xx+ chips don't have GPIO_CRT2_DDC gpio pad */
|
|
+ if ((rdev->family >= CHIP_R300) &&
|
|
+ ddc_i2c->mask_clk_reg == RADEON_GPIO_CRT2_DDC)
|
|
+ *ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
|
|
+
|
|
/* Certain IBM chipset RN50s have a BIOS reporting two VGAs,
|
|
one with VGA DDC and one with CRT2 DDC. - kill the CRT2 DDC one */
|
|
if (dev->pdev->device == 0x515e &&
|
|
@@ -1624,6 +1887,12 @@ static bool radeon_apply_legacy_tv_quirks(struct drm_device *dev)
|
|
dev->pdev->subsystem_device == 0x280a)
|
|
return false;
|
|
|
|
+ /* MSI S270 has non-existent TV port */
|
|
+ if (dev->pdev->device == 0x5955 &&
|
|
+ dev->pdev->subsystem_vendor == 0x1462 &&
|
|
+ dev->pdev->subsystem_device == 0x0131)
|
|
+ return false;
|
|
+
|
|
return true;
|
|
}
|
|
|
|
@@ -1671,6 +1940,7 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
|
|
enum radeon_combios_connector connector;
|
|
int i = 0;
|
|
struct radeon_i2c_bus_rec ddc_i2c;
|
|
+ struct radeon_hpd hpd;
|
|
|
|
if (rdev->bios == NULL)
|
|
return false;
|
|
@@ -1691,26 +1961,40 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
|
|
switch (ddc_type) {
|
|
case DDC_MONID:
|
|
ddc_i2c =
|
|
- combios_setup_i2c_bus(RADEON_GPIO_MONID);
|
|
+ combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
|
|
break;
|
|
case DDC_DVI:
|
|
ddc_i2c =
|
|
- combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC);
|
|
+ combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
|
|
break;
|
|
case DDC_VGA:
|
|
ddc_i2c =
|
|
- combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
|
|
+ combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
|
|
break;
|
|
case DDC_CRT2:
|
|
ddc_i2c =
|
|
- combios_setup_i2c_bus(RADEON_GPIO_CRT2_DDC);
|
|
+ combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ switch (connector) {
|
|
+ case CONNECTOR_PROPRIETARY_LEGACY:
|
|
+ case CONNECTOR_DVI_I_LEGACY:
|
|
+ case CONNECTOR_DVI_D_LEGACY:
|
|
+ if ((tmp >> 4) & 0x1)
|
|
+ hpd.hpd = RADEON_HPD_2;
|
|
+ else
|
|
+ hpd.hpd = RADEON_HPD_1;
|
|
break;
|
|
default:
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
break;
|
|
}
|
|
|
|
if (!radeon_apply_legacy_quirks(dev, i, &connector,
|
|
- &ddc_i2c))
|
|
+ &ddc_i2c, &hpd))
|
|
continue;
|
|
|
|
switch (connector) {
|
|
@@ -1727,7 +2011,8 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
|
|
legacy_connector_convert
|
|
[connector],
|
|
&ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D);
|
|
+ CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D,
|
|
+ &hpd);
|
|
break;
|
|
case CONNECTOR_CRT_LEGACY:
|
|
if (tmp & 0x1) {
|
|
@@ -1753,7 +2038,8 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
|
|
legacy_connector_convert
|
|
[connector],
|
|
&ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_VGA);
|
|
+ CONNECTOR_OBJECT_ID_VGA,
|
|
+ &hpd);
|
|
break;
|
|
case CONNECTOR_DVI_I_LEGACY:
|
|
devices = 0;
|
|
@@ -1799,7 +2085,8 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
|
|
legacy_connector_convert
|
|
[connector],
|
|
&ddc_i2c,
|
|
- connector_object_id);
|
|
+ connector_object_id,
|
|
+ &hpd);
|
|
break;
|
|
case CONNECTOR_DVI_D_LEGACY:
|
|
if ((tmp >> 4) & 0x1) {
|
|
@@ -1817,7 +2104,8 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
|
|
legacy_connector_convert
|
|
[connector],
|
|
&ddc_i2c,
|
|
- connector_object_id);
|
|
+ connector_object_id,
|
|
+ &hpd);
|
|
break;
|
|
case CONNECTOR_CTV_LEGACY:
|
|
case CONNECTOR_STV_LEGACY:
|
|
@@ -1832,7 +2120,8 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
|
|
legacy_connector_convert
|
|
[connector],
|
|
&ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_SVIDEO);
|
|
+ CONNECTOR_OBJECT_ID_SVIDEO,
|
|
+ &hpd);
|
|
break;
|
|
default:
|
|
DRM_ERROR("Unknown connector type: %d\n",
|
|
@@ -1858,14 +2147,16 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
|
|
0),
|
|
ATOM_DEVICE_DFP1_SUPPORT);
|
|
|
|
- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC);
|
|
+ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
radeon_add_legacy_connector(dev,
|
|
0,
|
|
ATOM_DEVICE_CRT1_SUPPORT |
|
|
ATOM_DEVICE_DFP1_SUPPORT,
|
|
DRM_MODE_CONNECTOR_DVII,
|
|
&ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I);
|
|
+ CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I,
|
|
+ &hpd);
|
|
} else {
|
|
uint16_t crt_info =
|
|
combios_get_table_offset(dev, COMBIOS_CRT_INFO_TABLE);
|
|
@@ -1876,13 +2167,15 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
|
|
ATOM_DEVICE_CRT1_SUPPORT,
|
|
1),
|
|
ATOM_DEVICE_CRT1_SUPPORT);
|
|
- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
|
|
+ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
radeon_add_legacy_connector(dev,
|
|
0,
|
|
ATOM_DEVICE_CRT1_SUPPORT,
|
|
DRM_MODE_CONNECTOR_VGA,
|
|
&ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_VGA);
|
|
+ CONNECTOR_OBJECT_ID_VGA,
|
|
+ &hpd);
|
|
} else {
|
|
DRM_DEBUG("No connector info found\n");
|
|
return false;
|
|
@@ -1910,27 +2203,27 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
|
|
case DDC_MONID:
|
|
ddc_i2c =
|
|
combios_setup_i2c_bus
|
|
- (RADEON_GPIO_MONID);
|
|
+ (rdev, RADEON_GPIO_MONID);
|
|
break;
|
|
case DDC_DVI:
|
|
ddc_i2c =
|
|
combios_setup_i2c_bus
|
|
- (RADEON_GPIO_DVI_DDC);
|
|
+ (rdev, RADEON_GPIO_DVI_DDC);
|
|
break;
|
|
case DDC_VGA:
|
|
ddc_i2c =
|
|
combios_setup_i2c_bus
|
|
- (RADEON_GPIO_VGA_DDC);
|
|
+ (rdev, RADEON_GPIO_VGA_DDC);
|
|
break;
|
|
case DDC_CRT2:
|
|
ddc_i2c =
|
|
combios_setup_i2c_bus
|
|
- (RADEON_GPIO_CRT2_DDC);
|
|
+ (rdev, RADEON_GPIO_CRT2_DDC);
|
|
break;
|
|
case DDC_LCD:
|
|
ddc_i2c =
|
|
combios_setup_i2c_bus
|
|
- (RADEON_LCD_GPIO_MASK);
|
|
+ (rdev, RADEON_GPIOPAD_MASK);
|
|
ddc_i2c.mask_clk_mask =
|
|
RBIOS32(lcd_ddc_info + 3);
|
|
ddc_i2c.mask_data_mask =
|
|
@@ -1939,19 +2232,19 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
|
|
RBIOS32(lcd_ddc_info + 3);
|
|
ddc_i2c.a_data_mask =
|
|
RBIOS32(lcd_ddc_info + 7);
|
|
- ddc_i2c.put_clk_mask =
|
|
+ ddc_i2c.en_clk_mask =
|
|
RBIOS32(lcd_ddc_info + 3);
|
|
- ddc_i2c.put_data_mask =
|
|
+ ddc_i2c.en_data_mask =
|
|
RBIOS32(lcd_ddc_info + 7);
|
|
- ddc_i2c.get_clk_mask =
|
|
+ ddc_i2c.y_clk_mask =
|
|
RBIOS32(lcd_ddc_info + 3);
|
|
- ddc_i2c.get_data_mask =
|
|
+ ddc_i2c.y_data_mask =
|
|
RBIOS32(lcd_ddc_info + 7);
|
|
break;
|
|
case DDC_GPIO:
|
|
ddc_i2c =
|
|
combios_setup_i2c_bus
|
|
- (RADEON_MDGPIO_EN_REG);
|
|
+ (rdev, RADEON_MDGPIO_MASK);
|
|
ddc_i2c.mask_clk_mask =
|
|
RBIOS32(lcd_ddc_info + 3);
|
|
ddc_i2c.mask_data_mask =
|
|
@@ -1960,13 +2253,13 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
|
|
RBIOS32(lcd_ddc_info + 3);
|
|
ddc_i2c.a_data_mask =
|
|
RBIOS32(lcd_ddc_info + 7);
|
|
- ddc_i2c.put_clk_mask =
|
|
+ ddc_i2c.en_clk_mask =
|
|
RBIOS32(lcd_ddc_info + 3);
|
|
- ddc_i2c.put_data_mask =
|
|
+ ddc_i2c.en_data_mask =
|
|
RBIOS32(lcd_ddc_info + 7);
|
|
- ddc_i2c.get_clk_mask =
|
|
+ ddc_i2c.y_clk_mask =
|
|
RBIOS32(lcd_ddc_info + 3);
|
|
- ddc_i2c.get_data_mask =
|
|
+ ddc_i2c.y_data_mask =
|
|
RBIOS32(lcd_ddc_info + 7);
|
|
break;
|
|
default:
|
|
@@ -1977,12 +2270,14 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
|
|
} else
|
|
ddc_i2c.valid = false;
|
|
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
radeon_add_legacy_connector(dev,
|
|
5,
|
|
ATOM_DEVICE_LCD1_SUPPORT,
|
|
DRM_MODE_CONNECTOR_LVDS,
|
|
&ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_LVDS);
|
|
+ CONNECTOR_OBJECT_ID_LVDS,
|
|
+ &hpd);
|
|
}
|
|
}
|
|
|
|
@@ -1993,6 +2288,7 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
|
|
if (tv_info) {
|
|
if (RBIOS8(tv_info + 6) == 'T') {
|
|
if (radeon_apply_legacy_tv_quirks(dev)) {
|
|
+ hpd.hpd = RADEON_HPD_NONE;
|
|
radeon_add_legacy_encoder(dev,
|
|
radeon_get_encoder_id
|
|
(dev,
|
|
@@ -2003,7 +2299,8 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
|
|
ATOM_DEVICE_TV1_SUPPORT,
|
|
DRM_MODE_CONNECTOR_SVIDEO,
|
|
&ddc_i2c,
|
|
- CONNECTOR_OBJECT_ID_SVIDEO);
|
|
+ CONNECTOR_OBJECT_ID_SVIDEO,
|
|
+ &hpd);
|
|
}
|
|
}
|
|
}
|
|
@@ -2014,6 +2311,193 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
|
|
return true;
|
|
}
|
|
|
|
+void radeon_external_tmds_setup(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
|
|
+ struct radeon_encoder_ext_tmds *tmds = radeon_encoder->enc_priv;
|
|
+
|
|
+ if (!tmds)
|
|
+ return;
|
|
+
|
|
+ switch (tmds->dvo_chip) {
|
|
+ case DVO_SIL164:
|
|
+ /* sil 164 */
|
|
+ radeon_i2c_do_lock(tmds->i2c_bus, 1);
|
|
+ radeon_i2c_sw_put_byte(tmds->i2c_bus,
|
|
+ tmds->slave_addr,
|
|
+ 0x08, 0x30);
|
|
+ radeon_i2c_sw_put_byte(tmds->i2c_bus,
|
|
+ tmds->slave_addr,
|
|
+ 0x09, 0x00);
|
|
+ radeon_i2c_sw_put_byte(tmds->i2c_bus,
|
|
+ tmds->slave_addr,
|
|
+ 0x0a, 0x90);
|
|
+ radeon_i2c_sw_put_byte(tmds->i2c_bus,
|
|
+ tmds->slave_addr,
|
|
+ 0x0c, 0x89);
|
|
+ radeon_i2c_sw_put_byte(tmds->i2c_bus,
|
|
+ tmds->slave_addr,
|
|
+ 0x08, 0x3b);
|
|
+ radeon_i2c_do_lock(tmds->i2c_bus, 0);
|
|
+ break;
|
|
+ case DVO_SIL1178:
|
|
+ /* sil 1178 - untested */
|
|
+ /*
|
|
+ * 0x0f, 0x44
|
|
+ * 0x0f, 0x4c
|
|
+ * 0x0e, 0x01
|
|
+ * 0x0a, 0x80
|
|
+ * 0x09, 0x30
|
|
+ * 0x0c, 0xc9
|
|
+ * 0x0d, 0x70
|
|
+ * 0x08, 0x32
|
|
+ * 0x08, 0x33
|
|
+ */
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+}
|
|
+
|
|
+bool radeon_combios_external_tmds_setup(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
|
|
+ uint16_t offset;
|
|
+ uint8_t blocks, slave_addr, rev;
|
|
+ uint32_t index, id;
|
|
+ uint32_t reg, val, and_mask, or_mask;
|
|
+ struct radeon_encoder_ext_tmds *tmds = radeon_encoder->enc_priv;
|
|
+
|
|
+ if (rdev->bios == NULL)
|
|
+ return false;
|
|
+
|
|
+ if (!tmds)
|
|
+ return false;
|
|
+
|
|
+ if (rdev->flags & RADEON_IS_IGP) {
|
|
+ offset = combios_get_table_offset(dev, COMBIOS_TMDS_POWER_ON_TABLE);
|
|
+ rev = RBIOS8(offset);
|
|
+ if (offset) {
|
|
+ rev = RBIOS8(offset);
|
|
+ if (rev > 1) {
|
|
+ blocks = RBIOS8(offset + 3);
|
|
+ index = offset + 4;
|
|
+ while (blocks > 0) {
|
|
+ id = RBIOS16(index);
|
|
+ index += 2;
|
|
+ switch (id >> 13) {
|
|
+ case 0:
|
|
+ reg = (id & 0x1fff) * 4;
|
|
+ val = RBIOS32(index);
|
|
+ index += 4;
|
|
+ WREG32(reg, val);
|
|
+ break;
|
|
+ case 2:
|
|
+ reg = (id & 0x1fff) * 4;
|
|
+ and_mask = RBIOS32(index);
|
|
+ index += 4;
|
|
+ or_mask = RBIOS32(index);
|
|
+ index += 4;
|
|
+ val = RREG32(reg);
|
|
+ val = (val & and_mask) | or_mask;
|
|
+ WREG32(reg, val);
|
|
+ break;
|
|
+ case 3:
|
|
+ val = RBIOS16(index);
|
|
+ index += 2;
|
|
+ udelay(val);
|
|
+ break;
|
|
+ case 4:
|
|
+ val = RBIOS16(index);
|
|
+ index += 2;
|
|
+ udelay(val * 1000);
|
|
+ break;
|
|
+ case 6:
|
|
+ slave_addr = id & 0xff;
|
|
+ slave_addr >>= 1; /* 7 bit addressing */
|
|
+ index++;
|
|
+ reg = RBIOS8(index);
|
|
+ index++;
|
|
+ val = RBIOS8(index);
|
|
+ index++;
|
|
+ radeon_i2c_do_lock(tmds->i2c_bus, 1);
|
|
+ radeon_i2c_sw_put_byte(tmds->i2c_bus,
|
|
+ slave_addr,
|
|
+ reg, val);
|
|
+ radeon_i2c_do_lock(tmds->i2c_bus, 0);
|
|
+ break;
|
|
+ default:
|
|
+ DRM_ERROR("Unknown id %d\n", id >> 13);
|
|
+ break;
|
|
+ }
|
|
+ blocks--;
|
|
+ }
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ offset = combios_get_table_offset(dev, COMBIOS_EXT_TMDS_INFO_TABLE);
|
|
+ if (offset) {
|
|
+ index = offset + 10;
|
|
+ id = RBIOS16(index);
|
|
+ while (id != 0xffff) {
|
|
+ index += 2;
|
|
+ switch (id >> 13) {
|
|
+ case 0:
|
|
+ reg = (id & 0x1fff) * 4;
|
|
+ val = RBIOS32(index);
|
|
+ WREG32(reg, val);
|
|
+ break;
|
|
+ case 2:
|
|
+ reg = (id & 0x1fff) * 4;
|
|
+ and_mask = RBIOS32(index);
|
|
+ index += 4;
|
|
+ or_mask = RBIOS32(index);
|
|
+ index += 4;
|
|
+ val = RREG32(reg);
|
|
+ val = (val & and_mask) | or_mask;
|
|
+ WREG32(reg, val);
|
|
+ break;
|
|
+ case 4:
|
|
+ val = RBIOS16(index);
|
|
+ index += 2;
|
|
+ udelay(val);
|
|
+ break;
|
|
+ case 5:
|
|
+ reg = id & 0x1fff;
|
|
+ and_mask = RBIOS32(index);
|
|
+ index += 4;
|
|
+ or_mask = RBIOS32(index);
|
|
+ index += 4;
|
|
+ val = RREG32_PLL(reg);
|
|
+ val = (val & and_mask) | or_mask;
|
|
+ WREG32_PLL(reg, val);
|
|
+ break;
|
|
+ case 6:
|
|
+ reg = id & 0x1fff;
|
|
+ val = RBIOS8(index);
|
|
+ index += 1;
|
|
+ radeon_i2c_do_lock(tmds->i2c_bus, 1);
|
|
+ radeon_i2c_sw_put_byte(tmds->i2c_bus,
|
|
+ tmds->slave_addr,
|
|
+ reg, val);
|
|
+ radeon_i2c_do_lock(tmds->i2c_bus, 0);
|
|
+ break;
|
|
+ default:
|
|
+ DRM_ERROR("Unknown id %d\n", id >> 13);
|
|
+ break;
|
|
+ }
|
|
+ id = RBIOS16(index);
|
|
+ }
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ return false;
|
|
+}
|
|
+
|
|
static void combios_parse_mmio_table(struct drm_device *dev, uint16_t offset)
|
|
{
|
|
struct radeon_device *rdev = dev->dev_private;
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
|
|
index 29763ce..2381885 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
|
|
@@ -40,6 +40,28 @@ radeon_atombios_connected_scratch_regs(struct drm_connector *connector,
|
|
struct drm_encoder *encoder,
|
|
bool connected);
|
|
|
|
+void radeon_connector_hotplug(struct drm_connector *connector)
|
|
+{
|
|
+ struct drm_device *dev = connector->dev;
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
|
|
+
|
|
+ if (radeon_connector->hpd.hpd != RADEON_HPD_NONE)
|
|
+ radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd);
|
|
+
|
|
+ if ((connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) ||
|
|
+ (connector->connector_type == DRM_MODE_CONNECTOR_eDP)) {
|
|
+ if ((radeon_dp_getsinktype(radeon_connector) == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
|
|
+ (radeon_dp_getsinktype(radeon_connector) == CONNECTOR_OBJECT_ID_eDP)) {
|
|
+ if (radeon_dp_needs_link_train(radeon_connector)) {
|
|
+ if (connector->encoder)
|
|
+ dp_link_train(connector->encoder, connector);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+}
|
|
+
|
|
static void radeon_property_change_mode(struct drm_encoder *encoder)
|
|
{
|
|
struct drm_crtc *crtc = encoder->crtc;
|
|
@@ -188,6 +210,18 @@ static struct drm_display_mode *radeon_fp_native_mode(struct drm_encoder *encode
|
|
drm_mode_set_name(mode);
|
|
|
|
DRM_DEBUG("Adding native panel mode %s\n", mode->name);
|
|
+ } else if (native_mode->hdisplay != 0 &&
|
|
+ native_mode->vdisplay != 0) {
|
|
+ /* mac laptops without an edid */
|
|
+ /* Note that this is not necessarily the exact panel mode,
|
|
+ * but an approximation based on the cvt formula. For these
|
|
+ * systems we should ideally read the mode info out of the
|
|
+ * registers or add a mode table, but this works and is much
|
|
+ * simpler.
|
|
+ */
|
|
+ mode = drm_cvt_mode(dev, native_mode->hdisplay, native_mode->vdisplay, 60, true, false, false);
|
|
+ mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER;
|
|
+ DRM_DEBUG("Adding cvt approximation of native panel mode %s\n", mode->name);
|
|
}
|
|
return mode;
|
|
}
|
|
@@ -445,10 +479,10 @@ static enum drm_connector_status radeon_lvds_detect(struct drm_connector *connec
|
|
ret = connector_status_connected;
|
|
else {
|
|
if (radeon_connector->ddc_bus) {
|
|
- radeon_i2c_do_lock(radeon_connector, 1);
|
|
+ radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
|
|
radeon_connector->edid = drm_get_edid(&radeon_connector->base,
|
|
&radeon_connector->ddc_bus->adapter);
|
|
- radeon_i2c_do_lock(radeon_connector, 0);
|
|
+ radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
|
|
if (radeon_connector->edid)
|
|
ret = connector_status_connected;
|
|
}
|
|
@@ -546,24 +580,26 @@ static enum drm_connector_status radeon_vga_detect(struct drm_connector *connect
|
|
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
|
|
struct drm_encoder *encoder;
|
|
struct drm_encoder_helper_funcs *encoder_funcs;
|
|
- bool dret;
|
|
+ bool dret = false;
|
|
enum drm_connector_status ret = connector_status_disconnected;
|
|
|
|
encoder = radeon_best_single_encoder(connector);
|
|
if (!encoder)
|
|
ret = connector_status_disconnected;
|
|
|
|
- radeon_i2c_do_lock(radeon_connector, 1);
|
|
- dret = radeon_ddc_probe(radeon_connector);
|
|
- radeon_i2c_do_lock(radeon_connector, 0);
|
|
+ if (radeon_connector->ddc_bus) {
|
|
+ radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
|
|
+ dret = radeon_ddc_probe(radeon_connector);
|
|
+ radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
|
|
+ }
|
|
if (dret) {
|
|
if (radeon_connector->edid) {
|
|
kfree(radeon_connector->edid);
|
|
radeon_connector->edid = NULL;
|
|
}
|
|
- radeon_i2c_do_lock(radeon_connector, 1);
|
|
+ radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
|
|
radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter);
|
|
- radeon_i2c_do_lock(radeon_connector, 0);
|
|
+ radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
|
|
|
|
if (!radeon_connector->edid) {
|
|
DRM_ERROR("%s: probed a monitor but no|invalid EDID\n",
|
|
@@ -583,7 +619,7 @@ static enum drm_connector_status radeon_vga_detect(struct drm_connector *connect
|
|
ret = connector_status_connected;
|
|
}
|
|
} else {
|
|
- if (radeon_connector->dac_load_detect) {
|
|
+ if (radeon_connector->dac_load_detect && encoder) {
|
|
encoder_funcs = encoder->helper_private;
|
|
ret = encoder_funcs->detect(encoder, connector);
|
|
}
|
|
@@ -706,19 +742,21 @@ static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connect
|
|
struct drm_mode_object *obj;
|
|
int i;
|
|
enum drm_connector_status ret = connector_status_disconnected;
|
|
- bool dret;
|
|
+ bool dret = false;
|
|
|
|
- radeon_i2c_do_lock(radeon_connector, 1);
|
|
- dret = radeon_ddc_probe(radeon_connector);
|
|
- radeon_i2c_do_lock(radeon_connector, 0);
|
|
+ if (radeon_connector->ddc_bus) {
|
|
+ radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
|
|
+ dret = radeon_ddc_probe(radeon_connector);
|
|
+ radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
|
|
+ }
|
|
if (dret) {
|
|
if (radeon_connector->edid) {
|
|
kfree(radeon_connector->edid);
|
|
radeon_connector->edid = NULL;
|
|
}
|
|
- radeon_i2c_do_lock(radeon_connector, 1);
|
|
+ radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
|
|
radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter);
|
|
- radeon_i2c_do_lock(radeon_connector, 0);
|
|
+ radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
|
|
|
|
if (!radeon_connector->edid) {
|
|
DRM_ERROR("%s: probed a monitor but no|invalid EDID\n",
|
|
@@ -735,6 +773,39 @@ static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connect
|
|
ret = connector_status_disconnected;
|
|
} else
|
|
ret = connector_status_connected;
|
|
+
|
|
+ /* multiple connectors on the same encoder with the same ddc line
|
|
+ * This tends to be HDMI and DVI on the same encoder with the
|
|
+ * same ddc line. If the edid says HDMI, consider the HDMI port
|
|
+ * connected and the DVI port disconnected. If the edid doesn't
|
|
+ * say HDMI, vice versa.
|
|
+ */
|
|
+ if (radeon_connector->shared_ddc && connector_status_connected) {
|
|
+ struct drm_device *dev = connector->dev;
|
|
+ struct drm_connector *list_connector;
|
|
+ struct radeon_connector *list_radeon_connector;
|
|
+ list_for_each_entry(list_connector, &dev->mode_config.connector_list, head) {
|
|
+ if (connector == list_connector)
|
|
+ continue;
|
|
+ list_radeon_connector = to_radeon_connector(list_connector);
|
|
+ if (radeon_connector->devices == list_radeon_connector->devices) {
|
|
+ if (drm_detect_hdmi_monitor(radeon_connector->edid)) {
|
|
+ if (connector->connector_type == DRM_MODE_CONNECTOR_DVID) {
|
|
+ kfree(radeon_connector->edid);
|
|
+ radeon_connector->edid = NULL;
|
|
+ ret = connector_status_disconnected;
|
|
+ }
|
|
+ } else {
|
|
+ if ((connector->connector_type == DRM_MODE_CONNECTOR_HDMIA) ||
|
|
+ (connector->connector_type == DRM_MODE_CONNECTOR_HDMIB)) {
|
|
+ kfree(radeon_connector->edid);
|
|
+ radeon_connector->edid = NULL;
|
|
+ ret = connector_status_disconnected;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
}
|
|
}
|
|
|
|
@@ -833,10 +904,18 @@ static void radeon_dvi_force(struct drm_connector *connector)
|
|
static int radeon_dvi_mode_valid(struct drm_connector *connector,
|
|
struct drm_display_mode *mode)
|
|
{
|
|
+ struct drm_device *dev = connector->dev;
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
|
|
|
|
/* XXX check mode bandwidth */
|
|
|
|
+ /* clocks over 135 MHz have heat issues with DVI on RV100 */
|
|
+ if (radeon_connector->use_digital &&
|
|
+ (rdev->family == CHIP_RV100) &&
|
|
+ (mode->clock > 135000))
|
|
+ return MODE_CLOCK_HIGH;
|
|
+
|
|
if (radeon_connector->use_digital && (mode->clock > 165000)) {
|
|
if ((radeon_connector->connector_object_id == CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I) ||
|
|
(radeon_connector->connector_object_id == CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D) ||
|
|
@@ -863,6 +942,93 @@ struct drm_connector_funcs radeon_dvi_connector_funcs = {
|
|
.force = radeon_dvi_force,
|
|
};
|
|
|
|
+static void radeon_dp_connector_destroy(struct drm_connector *connector)
|
|
+{
|
|
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
|
|
+ struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv;
|
|
+
|
|
+ if (radeon_connector->ddc_bus)
|
|
+ radeon_i2c_destroy(radeon_connector->ddc_bus);
|
|
+ if (radeon_connector->edid)
|
|
+ kfree(radeon_connector->edid);
|
|
+ if (radeon_dig_connector->dp_i2c_bus)
|
|
+ radeon_i2c_destroy(radeon_dig_connector->dp_i2c_bus);
|
|
+ kfree(radeon_connector->con_priv);
|
|
+ drm_sysfs_connector_remove(connector);
|
|
+ drm_connector_cleanup(connector);
|
|
+ kfree(connector);
|
|
+}
|
|
+
|
|
+static int radeon_dp_get_modes(struct drm_connector *connector)
|
|
+{
|
|
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
|
|
+ int ret;
|
|
+
|
|
+ ret = radeon_ddc_get_modes(radeon_connector);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static enum drm_connector_status radeon_dp_detect(struct drm_connector *connector)
|
|
+{
|
|
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
|
|
+ enum drm_connector_status ret = connector_status_disconnected;
|
|
+ struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv;
|
|
+ u8 sink_type;
|
|
+
|
|
+ if (radeon_connector->edid) {
|
|
+ kfree(radeon_connector->edid);
|
|
+ radeon_connector->edid = NULL;
|
|
+ }
|
|
+
|
|
+ sink_type = radeon_dp_getsinktype(radeon_connector);
|
|
+ if ((sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
|
|
+ (sink_type == CONNECTOR_OBJECT_ID_eDP)) {
|
|
+ if (radeon_dp_getdpcd(radeon_connector)) {
|
|
+ radeon_dig_connector->dp_sink_type = sink_type;
|
|
+ ret = connector_status_connected;
|
|
+ }
|
|
+ } else {
|
|
+ radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
|
|
+ if (radeon_ddc_probe(radeon_connector)) {
|
|
+ radeon_dig_connector->dp_sink_type = sink_type;
|
|
+ ret = connector_status_connected;
|
|
+ }
|
|
+ radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int radeon_dp_mode_valid(struct drm_connector *connector,
|
|
+ struct drm_display_mode *mode)
|
|
+{
|
|
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
|
|
+ struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv;
|
|
+
|
|
+ /* XXX check mode bandwidth */
|
|
+
|
|
+ if ((radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
|
|
+ (radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP))
|
|
+ return radeon_dp_mode_valid_helper(radeon_connector, mode);
|
|
+ else
|
|
+ return MODE_OK;
|
|
+}
|
|
+
|
|
+struct drm_connector_helper_funcs radeon_dp_connector_helper_funcs = {
|
|
+ .get_modes = radeon_dp_get_modes,
|
|
+ .mode_valid = radeon_dp_mode_valid,
|
|
+ .best_encoder = radeon_dvi_encoder,
|
|
+};
|
|
+
|
|
+struct drm_connector_funcs radeon_dp_connector_funcs = {
|
|
+ .dpms = drm_helper_connector_dpms,
|
|
+ .detect = radeon_dp_detect,
|
|
+ .fill_modes = drm_helper_probe_single_connector_modes,
|
|
+ .set_property = radeon_connector_set_property,
|
|
+ .destroy = radeon_dp_connector_destroy,
|
|
+ .force = radeon_dvi_force,
|
|
+};
|
|
+
|
|
void
|
|
radeon_add_atom_connector(struct drm_device *dev,
|
|
uint32_t connector_id,
|
|
@@ -871,7 +1037,8 @@ radeon_add_atom_connector(struct drm_device *dev,
|
|
struct radeon_i2c_bus_rec *i2c_bus,
|
|
bool linkb,
|
|
uint32_t igp_lane_info,
|
|
- uint16_t connector_object_id)
|
|
+ uint16_t connector_object_id,
|
|
+ struct radeon_hpd *hpd)
|
|
{
|
|
struct radeon_device *rdev = dev->dev_private;
|
|
struct drm_connector *connector;
|
|
@@ -911,6 +1078,7 @@ radeon_add_atom_connector(struct drm_device *dev,
|
|
radeon_connector->devices = supported_device;
|
|
radeon_connector->shared_ddc = shared_ddc;
|
|
radeon_connector->connector_object_id = connector_object_id;
|
|
+ radeon_connector->hpd = *hpd;
|
|
switch (connector_type) {
|
|
case DRM_MODE_CONNECTOR_VGA:
|
|
drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
|
|
@@ -963,10 +1131,12 @@ radeon_add_atom_connector(struct drm_device *dev,
|
|
drm_connector_attach_property(&radeon_connector->base,
|
|
rdev->mode_info.coherent_mode_property,
|
|
1);
|
|
- radeon_connector->dac_load_detect = true;
|
|
- drm_connector_attach_property(&radeon_connector->base,
|
|
- rdev->mode_info.load_detect_property,
|
|
- 1);
|
|
+ if (connector_type == DRM_MODE_CONNECTOR_DVII) {
|
|
+ radeon_connector->dac_load_detect = true;
|
|
+ drm_connector_attach_property(&radeon_connector->base,
|
|
+ rdev->mode_info.load_detect_property,
|
|
+ 1);
|
|
+ }
|
|
break;
|
|
case DRM_MODE_CONNECTOR_HDMIA:
|
|
case DRM_MODE_CONNECTOR_HDMIB:
|
|
@@ -991,22 +1161,36 @@ radeon_add_atom_connector(struct drm_device *dev,
|
|
subpixel_order = SubPixelHorizontalRGB;
|
|
break;
|
|
case DRM_MODE_CONNECTOR_DisplayPort:
|
|
+ case DRM_MODE_CONNECTOR_eDP:
|
|
radeon_dig_connector = kzalloc(sizeof(struct radeon_connector_atom_dig), GFP_KERNEL);
|
|
if (!radeon_dig_connector)
|
|
goto failed;
|
|
radeon_dig_connector->linkb = linkb;
|
|
radeon_dig_connector->igp_lane_info = igp_lane_info;
|
|
radeon_connector->con_priv = radeon_dig_connector;
|
|
- drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type);
|
|
- ret = drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs);
|
|
+ drm_connector_init(dev, &radeon_connector->base, &radeon_dp_connector_funcs, connector_type);
|
|
+ ret = drm_connector_helper_add(&radeon_connector->base, &radeon_dp_connector_helper_funcs);
|
|
if (ret)
|
|
goto failed;
|
|
if (i2c_bus->valid) {
|
|
- radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DP");
|
|
+ /* add DP i2c bus */
|
|
+ if (connector_type == DRM_MODE_CONNECTOR_eDP)
|
|
+ radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "eDP-auxch");
|
|
+ else
|
|
+ radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "DP-auxch");
|
|
+ if (!radeon_dig_connector->dp_i2c_bus)
|
|
+ goto failed;
|
|
+ if (connector_type == DRM_MODE_CONNECTOR_eDP)
|
|
+ radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "eDP");
|
|
+ else
|
|
+ radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DP");
|
|
if (!radeon_connector->ddc_bus)
|
|
goto failed;
|
|
}
|
|
subpixel_order = SubPixelHorizontalRGB;
|
|
+ drm_connector_attach_property(&radeon_connector->base,
|
|
+ rdev->mode_info.coherent_mode_property,
|
|
+ 1);
|
|
break;
|
|
case DRM_MODE_CONNECTOR_SVIDEO:
|
|
case DRM_MODE_CONNECTOR_Composite:
|
|
@@ -1020,6 +1204,9 @@ radeon_add_atom_connector(struct drm_device *dev,
|
|
drm_connector_attach_property(&radeon_connector->base,
|
|
rdev->mode_info.load_detect_property,
|
|
1);
|
|
+ drm_connector_attach_property(&radeon_connector->base,
|
|
+ rdev->mode_info.tv_std_property,
|
|
+ radeon_atombios_get_tv_info(rdev));
|
|
}
|
|
break;
|
|
case DRM_MODE_CONNECTOR_LVDS:
|
|
@@ -1038,7 +1225,6 @@ radeon_add_atom_connector(struct drm_device *dev,
|
|
if (!radeon_connector->ddc_bus)
|
|
goto failed;
|
|
}
|
|
- drm_mode_create_scaling_mode_property(dev);
|
|
drm_connector_attach_property(&radeon_connector->base,
|
|
dev->mode_config.scaling_mode_property,
|
|
DRM_MODE_SCALE_FULLSCREEN);
|
|
@@ -1063,7 +1249,8 @@ radeon_add_legacy_connector(struct drm_device *dev,
|
|
uint32_t supported_device,
|
|
int connector_type,
|
|
struct radeon_i2c_bus_rec *i2c_bus,
|
|
- uint16_t connector_object_id)
|
|
+ uint16_t connector_object_id,
|
|
+ struct radeon_hpd *hpd)
|
|
{
|
|
struct radeon_device *rdev = dev->dev_private;
|
|
struct drm_connector *connector;
|
|
@@ -1093,6 +1280,7 @@ radeon_add_legacy_connector(struct drm_device *dev,
|
|
radeon_connector->connector_id = connector_id;
|
|
radeon_connector->devices = supported_device;
|
|
radeon_connector->connector_object_id = connector_object_id;
|
|
+ radeon_connector->hpd = *hpd;
|
|
switch (connector_type) {
|
|
case DRM_MODE_CONNECTOR_VGA:
|
|
drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
|
|
@@ -1159,7 +1347,10 @@ radeon_add_legacy_connector(struct drm_device *dev,
|
|
radeon_connector->dac_load_detect = false;
|
|
drm_connector_attach_property(&radeon_connector->base,
|
|
rdev->mode_info.load_detect_property,
|
|
- 1);
|
|
+ radeon_connector->dac_load_detect);
|
|
+ drm_connector_attach_property(&radeon_connector->base,
|
|
+ rdev->mode_info.tv_std_property,
|
|
+ radeon_combios_get_tv_info(rdev));
|
|
}
|
|
break;
|
|
case DRM_MODE_CONNECTOR_LVDS:
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_cp.c b/drivers/gpu/drm/radeon/radeon_cp.c
|
|
index 4f7afc7..06123ba 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_cp.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_cp.c
|
|
@@ -1941,8 +1941,8 @@ struct drm_buf *radeon_freelist_get(struct drm_device * dev)
|
|
for (t = 0; t < dev_priv->usec_timeout; t++) {
|
|
u32 done_age = GET_SCRATCH(dev_priv, 1);
|
|
DRM_DEBUG("done_age = %d\n", done_age);
|
|
- for (i = start; i < dma->buf_count; i++) {
|
|
- buf = dma->buflist[i];
|
|
+ for (i = 0; i < dma->buf_count; i++) {
|
|
+ buf = dma->buflist[start];
|
|
buf_priv = buf->dev_private;
|
|
if (buf->file_priv == NULL || (buf->pending &&
|
|
buf_priv->age <=
|
|
@@ -1951,7 +1951,8 @@ struct drm_buf *radeon_freelist_get(struct drm_device * dev)
|
|
buf->pending = 0;
|
|
return buf;
|
|
}
|
|
- start = 0;
|
|
+ if (++start >= dma->buf_count)
|
|
+ start = 0;
|
|
}
|
|
|
|
if (t) {
|
|
@@ -1960,47 +1961,9 @@ struct drm_buf *radeon_freelist_get(struct drm_device * dev)
|
|
}
|
|
}
|
|
|
|
- DRM_DEBUG("returning NULL!\n");
|
|
return NULL;
|
|
}
|
|
|
|
-#if 0
|
|
-struct drm_buf *radeon_freelist_get(struct drm_device * dev)
|
|
-{
|
|
- struct drm_device_dma *dma = dev->dma;
|
|
- drm_radeon_private_t *dev_priv = dev->dev_private;
|
|
- drm_radeon_buf_priv_t *buf_priv;
|
|
- struct drm_buf *buf;
|
|
- int i, t;
|
|
- int start;
|
|
- u32 done_age;
|
|
-
|
|
- done_age = radeon_read_ring_rptr(dev_priv, RADEON_SCRATCHOFF(1));
|
|
- if (++dev_priv->last_buf >= dma->buf_count)
|
|
- dev_priv->last_buf = 0;
|
|
-
|
|
- start = dev_priv->last_buf;
|
|
- dev_priv->stats.freelist_loops++;
|
|
-
|
|
- for (t = 0; t < 2; t++) {
|
|
- for (i = start; i < dma->buf_count; i++) {
|
|
- buf = dma->buflist[i];
|
|
- buf_priv = buf->dev_private;
|
|
- if (buf->file_priv == 0 || (buf->pending &&
|
|
- buf_priv->age <=
|
|
- done_age)) {
|
|
- dev_priv->stats.requested_bufs++;
|
|
- buf->pending = 0;
|
|
- return buf;
|
|
- }
|
|
- }
|
|
- start = 0;
|
|
- }
|
|
-
|
|
- return NULL;
|
|
-}
|
|
-#endif
|
|
-
|
|
void radeon_freelist_reset(struct drm_device * dev)
|
|
{
|
|
struct drm_device_dma *dma = dev->dma;
|
|
@@ -2182,6 +2145,7 @@ int radeon_master_create(struct drm_device *dev, struct drm_master *master)
|
|
&master_priv->sarea);
|
|
if (ret) {
|
|
DRM_ERROR("SAREA setup failed\n");
|
|
+ kfree(master_priv);
|
|
return ret;
|
|
}
|
|
master_priv->sarea_priv = master_priv->sarea->handle + sizeof(struct drm_sarea);
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c
|
|
index 5ab2cf9..e9d0850 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_cs.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_cs.c
|
|
@@ -76,17 +76,17 @@ int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
|
|
}
|
|
p->relocs_ptr[i] = &p->relocs[i];
|
|
p->relocs[i].robj = p->relocs[i].gobj->driver_private;
|
|
- p->relocs[i].lobj.robj = p->relocs[i].robj;
|
|
+ p->relocs[i].lobj.bo = p->relocs[i].robj;
|
|
p->relocs[i].lobj.rdomain = r->read_domains;
|
|
p->relocs[i].lobj.wdomain = r->write_domain;
|
|
p->relocs[i].handle = r->handle;
|
|
p->relocs[i].flags = r->flags;
|
|
INIT_LIST_HEAD(&p->relocs[i].lobj.list);
|
|
- radeon_object_list_add_object(&p->relocs[i].lobj,
|
|
- &p->validated);
|
|
+ radeon_bo_list_add_object(&p->relocs[i].lobj,
|
|
+ &p->validated);
|
|
}
|
|
}
|
|
- return radeon_object_list_validate(&p->validated, p->ib->fence);
|
|
+ return radeon_bo_list_validate(&p->validated);
|
|
}
|
|
|
|
int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
|
|
@@ -189,11 +189,10 @@ static void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error)
|
|
{
|
|
unsigned i;
|
|
|
|
- if (error) {
|
|
- radeon_object_list_unvalidate(&parser->validated);
|
|
- } else {
|
|
- radeon_object_list_clean(&parser->validated);
|
|
+ if (!error && parser->ib) {
|
|
+ radeon_bo_list_fence(&parser->validated, parser->ib->fence);
|
|
}
|
|
+ radeon_bo_list_unreserve(&parser->validated);
|
|
for (i = 0; i < parser->nrelocs; i++) {
|
|
if (parser->relocs[i].gobj) {
|
|
mutex_lock(&parser->rdev->ddev->struct_mutex);
|
|
@@ -230,6 +229,7 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
|
|
memset(&parser, 0, sizeof(struct radeon_cs_parser));
|
|
parser.filp = filp;
|
|
parser.rdev = rdev;
|
|
+ parser.dev = rdev->dev;
|
|
r = radeon_cs_parser_init(&parser, data);
|
|
if (r) {
|
|
DRM_ERROR("Failed to initialize parser !\n");
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
|
|
index 41bb76f..768b150 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_device.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_device.c
|
|
@@ -44,10 +44,11 @@ void radeon_surface_init(struct radeon_device *rdev)
|
|
if (rdev->family < CHIP_R600) {
|
|
int i;
|
|
|
|
- for (i = 0; i < 8; i++) {
|
|
- WREG32(RADEON_SURFACE0_INFO +
|
|
- i * (RADEON_SURFACE1_INFO - RADEON_SURFACE0_INFO),
|
|
- 0);
|
|
+ for (i = 0; i < RADEON_GEM_MAX_SURFACES; i++) {
|
|
+ if (rdev->surface_regs[i].bo)
|
|
+ radeon_bo_get_surface_reg(rdev->surface_regs[i].bo);
|
|
+ else
|
|
+ radeon_clear_surface_reg(rdev, i);
|
|
}
|
|
/* enable surfaces */
|
|
WREG32(RADEON_SURFACE_CNTL, 0);
|
|
@@ -208,6 +209,24 @@ bool radeon_card_posted(struct radeon_device *rdev)
|
|
|
|
}
|
|
|
|
+bool radeon_boot_test_post_card(struct radeon_device *rdev)
|
|
+{
|
|
+ if (radeon_card_posted(rdev))
|
|
+ return true;
|
|
+
|
|
+ if (rdev->bios) {
|
|
+ DRM_INFO("GPU not posted. posting now...\n");
|
|
+ if (rdev->is_atom_bios)
|
|
+ atom_asic_init(rdev->mode_info.atom_context);
|
|
+ else
|
|
+ radeon_combios_asic_init(rdev->ddev);
|
|
+ return true;
|
|
+ } else {
|
|
+ dev_err(rdev->dev, "Card not posted and no BIOS - ignoring\n");
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
+
|
|
int radeon_dummy_page_init(struct radeon_device *rdev)
|
|
{
|
|
rdev->dummy_page.page = alloc_page(GFP_DMA32 | GFP_KERNEL | __GFP_ZERO);
|
|
@@ -372,6 +391,12 @@ int radeon_asic_init(struct radeon_device *rdev)
|
|
/* FIXME: not supported yet */
|
|
return -EINVAL;
|
|
}
|
|
+
|
|
+ if (rdev->flags & RADEON_IS_IGP) {
|
|
+ rdev->asic->get_memory_clock = NULL;
|
|
+ rdev->asic->set_memory_clock = NULL;
|
|
+ }
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
@@ -462,13 +487,18 @@ int radeon_atombios_init(struct radeon_device *rdev)
|
|
atom_card_info->pll_write = cail_pll_write;
|
|
|
|
rdev->mode_info.atom_context = atom_parse(atom_card_info, rdev->bios);
|
|
+ mutex_init(&rdev->mode_info.atom_context->mutex);
|
|
radeon_atom_initialize_bios_scratch_regs(rdev->ddev);
|
|
+ atom_allocate_fb_scratch(rdev->mode_info.atom_context);
|
|
return 0;
|
|
}
|
|
|
|
void radeon_atombios_fini(struct radeon_device *rdev)
|
|
{
|
|
- kfree(rdev->mode_info.atom_context);
|
|
+ if (rdev->mode_info.atom_context) {
|
|
+ kfree(rdev->mode_info.atom_context->scratch);
|
|
+ kfree(rdev->mode_info.atom_context);
|
|
+ }
|
|
kfree(rdev->mode_info.atom_card_info);
|
|
}
|
|
|
|
@@ -514,11 +544,75 @@ void radeon_agp_disable(struct radeon_device *rdev)
|
|
rdev->asic->gart_tlb_flush = &r100_pci_gart_tlb_flush;
|
|
rdev->asic->gart_set_page = &r100_pci_gart_set_page;
|
|
}
|
|
+ rdev->mc.gtt_size = radeon_gart_size * 1024 * 1024;
|
|
+}
|
|
+
|
|
+void radeon_check_arguments(struct radeon_device *rdev)
|
|
+{
|
|
+ /* vramlimit must be a power of two */
|
|
+ switch (radeon_vram_limit) {
|
|
+ case 0:
|
|
+ case 4:
|
|
+ case 8:
|
|
+ case 16:
|
|
+ case 32:
|
|
+ case 64:
|
|
+ case 128:
|
|
+ case 256:
|
|
+ case 512:
|
|
+ case 1024:
|
|
+ case 2048:
|
|
+ case 4096:
|
|
+ break;
|
|
+ default:
|
|
+ dev_warn(rdev->dev, "vram limit (%d) must be a power of 2\n",
|
|
+ radeon_vram_limit);
|
|
+ radeon_vram_limit = 0;
|
|
+ break;
|
|
+ }
|
|
+ radeon_vram_limit = radeon_vram_limit << 20;
|
|
+ /* gtt size must be power of two and greater or equal to 32M */
|
|
+ switch (radeon_gart_size) {
|
|
+ case 4:
|
|
+ case 8:
|
|
+ case 16:
|
|
+ dev_warn(rdev->dev, "gart size (%d) too small forcing to 512M\n",
|
|
+ radeon_gart_size);
|
|
+ radeon_gart_size = 512;
|
|
+ break;
|
|
+ case 32:
|
|
+ case 64:
|
|
+ case 128:
|
|
+ case 256:
|
|
+ case 512:
|
|
+ case 1024:
|
|
+ case 2048:
|
|
+ case 4096:
|
|
+ break;
|
|
+ default:
|
|
+ dev_warn(rdev->dev, "gart size (%d) must be a power of 2\n",
|
|
+ radeon_gart_size);
|
|
+ radeon_gart_size = 512;
|
|
+ break;
|
|
+ }
|
|
+ rdev->mc.gtt_size = radeon_gart_size * 1024 * 1024;
|
|
+ /* AGP mode can only be -1, 1, 2, 4, 8 */
|
|
+ switch (radeon_agpmode) {
|
|
+ case -1:
|
|
+ case 0:
|
|
+ case 1:
|
|
+ case 2:
|
|
+ case 4:
|
|
+ case 8:
|
|
+ break;
|
|
+ default:
|
|
+ dev_warn(rdev->dev, "invalid AGP mode %d (valid mode: "
|
|
+ "-1, 0, 1, 2, 4, 8)\n", radeon_agpmode);
|
|
+ radeon_agpmode = 0;
|
|
+ break;
|
|
+ }
|
|
}
|
|
|
|
-/*
|
|
- * Radeon device.
|
|
- */
|
|
int radeon_device_init(struct radeon_device *rdev,
|
|
struct drm_device *ddev,
|
|
struct pci_dev *pdev,
|
|
@@ -544,16 +638,24 @@ int radeon_device_init(struct radeon_device *rdev,
|
|
mutex_init(&rdev->cs_mutex);
|
|
mutex_init(&rdev->ib_pool.mutex);
|
|
mutex_init(&rdev->cp.mutex);
|
|
+ if (rdev->family >= CHIP_R600)
|
|
+ spin_lock_init(&rdev->ih.lock);
|
|
+ mutex_init(&rdev->gem.mutex);
|
|
rwlock_init(&rdev->fence_drv.lock);
|
|
INIT_LIST_HEAD(&rdev->gem.objects);
|
|
|
|
+ /* setup workqueue */
|
|
+ rdev->wq = create_workqueue("radeon");
|
|
+ if (rdev->wq == NULL)
|
|
+ return -ENOMEM;
|
|
+
|
|
/* Set asic functions */
|
|
r = radeon_asic_init(rdev);
|
|
- if (r) {
|
|
+ if (r)
|
|
return r;
|
|
- }
|
|
+ radeon_check_arguments(rdev);
|
|
|
|
- if (radeon_agpmode == -1) {
|
|
+ if (rdev->flags & RADEON_IS_AGP && radeon_agpmode == -1) {
|
|
radeon_agp_disable(rdev);
|
|
}
|
|
|
|
@@ -620,6 +722,7 @@ void radeon_device_fini(struct radeon_device *rdev)
|
|
DRM_INFO("radeon: finishing device.\n");
|
|
rdev->shutdown = true;
|
|
radeon_fini(rdev);
|
|
+ destroy_workqueue(rdev->wq);
|
|
vga_client_register(rdev->pdev, NULL, NULL, NULL);
|
|
iounmap(rdev->rmmio);
|
|
rdev->rmmio = NULL;
|
|
@@ -631,38 +734,46 @@ void radeon_device_fini(struct radeon_device *rdev)
|
|
*/
|
|
int radeon_suspend_kms(struct drm_device *dev, pm_message_t state)
|
|
{
|
|
- struct radeon_device *rdev = dev->dev_private;
|
|
+ struct radeon_device *rdev;
|
|
struct drm_crtc *crtc;
|
|
+ int r;
|
|
|
|
- if (dev == NULL || rdev == NULL) {
|
|
+ if (dev == NULL || dev->dev_private == NULL) {
|
|
return -ENODEV;
|
|
}
|
|
if (state.event == PM_EVENT_PRETHAW) {
|
|
return 0;
|
|
}
|
|
+ rdev = dev->dev_private;
|
|
+
|
|
/* unpin the front buffers */
|
|
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
|
struct radeon_framebuffer *rfb = to_radeon_framebuffer(crtc->fb);
|
|
- struct radeon_object *robj;
|
|
+ struct radeon_bo *robj;
|
|
|
|
if (rfb == NULL || rfb->obj == NULL) {
|
|
continue;
|
|
}
|
|
robj = rfb->obj->driver_private;
|
|
- if (robj != rdev->fbdev_robj) {
|
|
- radeon_object_unpin(robj);
|
|
+ if (robj != rdev->fbdev_rbo) {
|
|
+ r = radeon_bo_reserve(robj, false);
|
|
+ if (unlikely(r == 0)) {
|
|
+ radeon_bo_unpin(robj);
|
|
+ radeon_bo_unreserve(robj);
|
|
+ }
|
|
}
|
|
}
|
|
/* evict vram memory */
|
|
- radeon_object_evict_vram(rdev);
|
|
+ radeon_bo_evict_vram(rdev);
|
|
/* wait for gpu to finish processing current batch */
|
|
radeon_fence_wait_last(rdev);
|
|
|
|
radeon_save_bios_scratch_regs(rdev);
|
|
|
|
radeon_suspend(rdev);
|
|
+ radeon_hpd_fini(rdev);
|
|
/* evict remaining vram memory */
|
|
- radeon_object_evict_vram(rdev);
|
|
+ radeon_bo_evict_vram(rdev);
|
|
|
|
pci_save_state(dev->pdev);
|
|
if (state.event == PM_EVENT_SUSPEND) {
|
|
@@ -695,6 +806,8 @@ int radeon_resume_kms(struct drm_device *dev)
|
|
fb_set_suspend(rdev->fbdev_info, 0);
|
|
release_console_sem();
|
|
|
|
+ /* reset hpd state */
|
|
+ radeon_hpd_init(rdev);
|
|
/* blat the mode back in */
|
|
drm_helper_resume_force_mode(dev);
|
|
return 0;
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
|
|
index c85df4a..ac31a66 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_display.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_display.c
|
|
@@ -234,7 +234,7 @@ static const char *encoder_names[34] = {
|
|
"INTERNAL_UNIPHY2",
|
|
};
|
|
|
|
-static const char *connector_names[13] = {
|
|
+static const char *connector_names[15] = {
|
|
"Unknown",
|
|
"VGA",
|
|
"DVI-I",
|
|
@@ -248,6 +248,18 @@ static const char *connector_names[13] = {
|
|
"DisplayPort",
|
|
"HDMI-A",
|
|
"HDMI-B",
|
|
+ "TV",
|
|
+ "eDP",
|
|
+};
|
|
+
|
|
+static const char *hpd_names[7] = {
|
|
+ "NONE",
|
|
+ "HPD1",
|
|
+ "HPD2",
|
|
+ "HPD3",
|
|
+ "HPD4",
|
|
+ "HPD5",
|
|
+ "HPD6",
|
|
};
|
|
|
|
static void radeon_print_display_setup(struct drm_device *dev)
|
|
@@ -264,16 +276,27 @@ static void radeon_print_display_setup(struct drm_device *dev)
|
|
radeon_connector = to_radeon_connector(connector);
|
|
DRM_INFO("Connector %d:\n", i);
|
|
DRM_INFO(" %s\n", connector_names[connector->connector_type]);
|
|
- if (radeon_connector->ddc_bus)
|
|
+ if (radeon_connector->hpd.hpd != RADEON_HPD_NONE)
|
|
+ DRM_INFO(" %s\n", hpd_names[radeon_connector->hpd.hpd]);
|
|
+ if (radeon_connector->ddc_bus) {
|
|
DRM_INFO(" DDC: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
|
|
radeon_connector->ddc_bus->rec.mask_clk_reg,
|
|
radeon_connector->ddc_bus->rec.mask_data_reg,
|
|
radeon_connector->ddc_bus->rec.a_clk_reg,
|
|
radeon_connector->ddc_bus->rec.a_data_reg,
|
|
- radeon_connector->ddc_bus->rec.put_clk_reg,
|
|
- radeon_connector->ddc_bus->rec.put_data_reg,
|
|
- radeon_connector->ddc_bus->rec.get_clk_reg,
|
|
- radeon_connector->ddc_bus->rec.get_data_reg);
|
|
+ radeon_connector->ddc_bus->rec.en_clk_reg,
|
|
+ radeon_connector->ddc_bus->rec.en_data_reg,
|
|
+ radeon_connector->ddc_bus->rec.y_clk_reg,
|
|
+ radeon_connector->ddc_bus->rec.y_data_reg);
|
|
+ } else {
|
|
+ if (connector->connector_type == DRM_MODE_CONNECTOR_VGA ||
|
|
+ connector->connector_type == DRM_MODE_CONNECTOR_DVII ||
|
|
+ connector->connector_type == DRM_MODE_CONNECTOR_DVID ||
|
|
+ connector->connector_type == DRM_MODE_CONNECTOR_DVIA ||
|
|
+ connector->connector_type == DRM_MODE_CONNECTOR_HDMIA ||
|
|
+ connector->connector_type == DRM_MODE_CONNECTOR_HDMIB)
|
|
+ DRM_INFO(" DDC: no ddc bus - possible BIOS bug - please report to xorg-driver-ati@lists.x.org\n");
|
|
+ }
|
|
DRM_INFO(" Encoders:\n");
|
|
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
|
radeon_encoder = to_radeon_encoder(encoder);
|
|
@@ -317,13 +340,17 @@ static bool radeon_setup_enc_conn(struct drm_device *dev)
|
|
ret = radeon_get_atom_connector_info_from_object_table(dev);
|
|
else
|
|
ret = radeon_get_atom_connector_info_from_supported_devices_table(dev);
|
|
- } else
|
|
+ } else {
|
|
ret = radeon_get_legacy_connector_info_from_bios(dev);
|
|
+ if (ret == false)
|
|
+ ret = radeon_get_legacy_connector_info_from_table(dev);
|
|
+ }
|
|
} else {
|
|
if (!ASIC_IS_AVIVO(rdev))
|
|
ret = radeon_get_legacy_connector_info_from_table(dev);
|
|
}
|
|
if (ret) {
|
|
+ radeon_setup_encoder_clones(dev);
|
|
radeon_print_display_setup(dev);
|
|
list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head)
|
|
radeon_ddc_dump(drm_connector);
|
|
@@ -334,16 +361,27 @@ static bool radeon_setup_enc_conn(struct drm_device *dev)
|
|
|
|
int radeon_ddc_get_modes(struct radeon_connector *radeon_connector)
|
|
{
|
|
+ struct drm_device *dev = radeon_connector->base.dev;
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
int ret = 0;
|
|
|
|
+ if ((radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_DisplayPort) ||
|
|
+ (radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_eDP)) {
|
|
+ struct radeon_connector_atom_dig *dig = radeon_connector->con_priv;
|
|
+ if ((dig->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT ||
|
|
+ dig->dp_sink_type == CONNECTOR_OBJECT_ID_eDP) && dig->dp_i2c_bus)
|
|
+ radeon_connector->edid = drm_get_edid(&radeon_connector->base, &dig->dp_i2c_bus->adapter);
|
|
+ }
|
|
if (!radeon_connector->ddc_bus)
|
|
return -1;
|
|
if (!radeon_connector->edid) {
|
|
- radeon_i2c_do_lock(radeon_connector, 1);
|
|
+ radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
|
|
radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter);
|
|
- radeon_i2c_do_lock(radeon_connector, 0);
|
|
+ radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
|
|
}
|
|
-
|
|
+ /* some servers provide a hardcoded edid in rom for KVMs */
|
|
+ if (!radeon_connector->edid)
|
|
+ radeon_connector->edid = radeon_combios_get_hardcoded_edid(rdev);
|
|
if (radeon_connector->edid) {
|
|
drm_mode_connector_update_edid_property(&radeon_connector->base, radeon_connector->edid);
|
|
ret = drm_add_edid_modes(&radeon_connector->base, radeon_connector->edid);
|
|
@@ -361,9 +399,9 @@ static int radeon_ddc_dump(struct drm_connector *connector)
|
|
|
|
if (!radeon_connector->ddc_bus)
|
|
return -1;
|
|
- radeon_i2c_do_lock(radeon_connector, 1);
|
|
+ radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
|
|
edid = drm_get_edid(connector, &radeon_connector->ddc_bus->adapter);
|
|
- radeon_i2c_do_lock(radeon_connector, 0);
|
|
+ radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
|
|
if (edid) {
|
|
kfree(edid);
|
|
}
|
|
@@ -386,11 +424,12 @@ void radeon_compute_pll(struct radeon_pll *pll,
|
|
uint32_t *fb_div_p,
|
|
uint32_t *frac_fb_div_p,
|
|
uint32_t *ref_div_p,
|
|
- uint32_t *post_div_p,
|
|
- int flags)
|
|
+ uint32_t *post_div_p)
|
|
{
|
|
uint32_t min_ref_div = pll->min_ref_div;
|
|
uint32_t max_ref_div = pll->max_ref_div;
|
|
+ uint32_t min_post_div = pll->min_post_div;
|
|
+ uint32_t max_post_div = pll->max_post_div;
|
|
uint32_t min_fractional_feed_div = 0;
|
|
uint32_t max_fractional_feed_div = 0;
|
|
uint32_t best_vco = pll->best_vco;
|
|
@@ -406,7 +445,7 @@ void radeon_compute_pll(struct radeon_pll *pll,
|
|
DRM_DEBUG("PLL freq %llu %u %u\n", freq, pll->min_ref_div, pll->max_ref_div);
|
|
freq = freq * 1000;
|
|
|
|
- if (flags & RADEON_PLL_USE_REF_DIV)
|
|
+ if (pll->flags & RADEON_PLL_USE_REF_DIV)
|
|
min_ref_div = max_ref_div = pll->reference_div;
|
|
else {
|
|
while (min_ref_div < max_ref_div-1) {
|
|
@@ -421,19 +460,22 @@ void radeon_compute_pll(struct radeon_pll *pll,
|
|
}
|
|
}
|
|
|
|
- if (flags & RADEON_PLL_USE_FRAC_FB_DIV) {
|
|
+ if (pll->flags & RADEON_PLL_USE_POST_DIV)
|
|
+ min_post_div = max_post_div = pll->post_div;
|
|
+
|
|
+ if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) {
|
|
min_fractional_feed_div = pll->min_frac_feedback_div;
|
|
max_fractional_feed_div = pll->max_frac_feedback_div;
|
|
}
|
|
|
|
- for (post_div = pll->min_post_div; post_div <= pll->max_post_div; ++post_div) {
|
|
+ for (post_div = min_post_div; post_div <= max_post_div; ++post_div) {
|
|
uint32_t ref_div;
|
|
|
|
- if ((flags & RADEON_PLL_NO_ODD_POST_DIV) && (post_div & 1))
|
|
+ if ((pll->flags & RADEON_PLL_NO_ODD_POST_DIV) && (post_div & 1))
|
|
continue;
|
|
|
|
/* legacy radeons only have a few post_divs */
|
|
- if (flags & RADEON_PLL_LEGACY) {
|
|
+ if (pll->flags & RADEON_PLL_LEGACY) {
|
|
if ((post_div == 5) ||
|
|
(post_div == 7) ||
|
|
(post_div == 9) ||
|
|
@@ -480,7 +522,7 @@ void radeon_compute_pll(struct radeon_pll *pll,
|
|
tmp += (uint64_t)pll->reference_freq * 1000 * frac_feedback_div;
|
|
current_freq = radeon_div(tmp, ref_div * post_div);
|
|
|
|
- if (flags & RADEON_PLL_PREFER_CLOSEST_LOWER) {
|
|
+ if (pll->flags & RADEON_PLL_PREFER_CLOSEST_LOWER) {
|
|
error = freq - current_freq;
|
|
error = error < 0 ? 0xffffffff : error;
|
|
} else
|
|
@@ -507,12 +549,12 @@ void radeon_compute_pll(struct radeon_pll *pll,
|
|
best_freq = current_freq;
|
|
best_error = error;
|
|
best_vco_diff = vco_diff;
|
|
- } else if (((flags & RADEON_PLL_PREFER_LOW_REF_DIV) && (ref_div < best_ref_div)) ||
|
|
- ((flags & RADEON_PLL_PREFER_HIGH_REF_DIV) && (ref_div > best_ref_div)) ||
|
|
- ((flags & RADEON_PLL_PREFER_LOW_FB_DIV) && (feedback_div < best_feedback_div)) ||
|
|
- ((flags & RADEON_PLL_PREFER_HIGH_FB_DIV) && (feedback_div > best_feedback_div)) ||
|
|
- ((flags & RADEON_PLL_PREFER_LOW_POST_DIV) && (post_div < best_post_div)) ||
|
|
- ((flags & RADEON_PLL_PREFER_HIGH_POST_DIV) && (post_div > best_post_div))) {
|
|
+ } else if (((pll->flags & RADEON_PLL_PREFER_LOW_REF_DIV) && (ref_div < best_ref_div)) ||
|
|
+ ((pll->flags & RADEON_PLL_PREFER_HIGH_REF_DIV) && (ref_div > best_ref_div)) ||
|
|
+ ((pll->flags & RADEON_PLL_PREFER_LOW_FB_DIV) && (feedback_div < best_feedback_div)) ||
|
|
+ ((pll->flags & RADEON_PLL_PREFER_HIGH_FB_DIV) && (feedback_div > best_feedback_div)) ||
|
|
+ ((pll->flags & RADEON_PLL_PREFER_LOW_POST_DIV) && (post_div < best_post_div)) ||
|
|
+ ((pll->flags & RADEON_PLL_PREFER_HIGH_POST_DIV) && (post_div > best_post_div))) {
|
|
best_post_div = post_div;
|
|
best_ref_div = ref_div;
|
|
best_feedback_div = feedback_div;
|
|
@@ -542,6 +584,97 @@ void radeon_compute_pll(struct radeon_pll *pll,
|
|
*post_div_p = best_post_div;
|
|
}
|
|
|
|
+void radeon_compute_pll_avivo(struct radeon_pll *pll,
|
|
+ uint64_t freq,
|
|
+ uint32_t *dot_clock_p,
|
|
+ uint32_t *fb_div_p,
|
|
+ uint32_t *frac_fb_div_p,
|
|
+ uint32_t *ref_div_p,
|
|
+ uint32_t *post_div_p)
|
|
+{
|
|
+ fixed20_12 m, n, frac_n, p, f_vco, f_pclk, best_freq;
|
|
+ fixed20_12 pll_out_max, pll_out_min;
|
|
+ fixed20_12 pll_in_max, pll_in_min;
|
|
+ fixed20_12 reference_freq;
|
|
+ fixed20_12 error, ffreq, a, b;
|
|
+
|
|
+ pll_out_max.full = rfixed_const(pll->pll_out_max);
|
|
+ pll_out_min.full = rfixed_const(pll->pll_out_min);
|
|
+ pll_in_max.full = rfixed_const(pll->pll_in_max);
|
|
+ pll_in_min.full = rfixed_const(pll->pll_in_min);
|
|
+ reference_freq.full = rfixed_const(pll->reference_freq);
|
|
+ do_div(freq, 10);
|
|
+ ffreq.full = rfixed_const(freq);
|
|
+ error.full = rfixed_const(100 * 100);
|
|
+
|
|
+ /* max p */
|
|
+ p.full = rfixed_div(pll_out_max, ffreq);
|
|
+ p.full = rfixed_floor(p);
|
|
+
|
|
+ /* min m */
|
|
+ m.full = rfixed_div(reference_freq, pll_in_max);
|
|
+ m.full = rfixed_ceil(m);
|
|
+
|
|
+ while (1) {
|
|
+ n.full = rfixed_div(ffreq, reference_freq);
|
|
+ n.full = rfixed_mul(n, m);
|
|
+ n.full = rfixed_mul(n, p);
|
|
+
|
|
+ f_vco.full = rfixed_div(n, m);
|
|
+ f_vco.full = rfixed_mul(f_vco, reference_freq);
|
|
+
|
|
+ f_pclk.full = rfixed_div(f_vco, p);
|
|
+
|
|
+ if (f_pclk.full > ffreq.full)
|
|
+ error.full = f_pclk.full - ffreq.full;
|
|
+ else
|
|
+ error.full = ffreq.full - f_pclk.full;
|
|
+ error.full = rfixed_div(error, f_pclk);
|
|
+ a.full = rfixed_const(100 * 100);
|
|
+ error.full = rfixed_mul(error, a);
|
|
+
|
|
+ a.full = rfixed_mul(m, p);
|
|
+ a.full = rfixed_div(n, a);
|
|
+ best_freq.full = rfixed_mul(reference_freq, a);
|
|
+
|
|
+ if (rfixed_trunc(error) < 25)
|
|
+ break;
|
|
+
|
|
+ a.full = rfixed_const(1);
|
|
+ m.full = m.full + a.full;
|
|
+ a.full = rfixed_div(reference_freq, m);
|
|
+ if (a.full >= pll_in_min.full)
|
|
+ continue;
|
|
+
|
|
+ m.full = rfixed_div(reference_freq, pll_in_max);
|
|
+ m.full = rfixed_ceil(m);
|
|
+ a.full= rfixed_const(1);
|
|
+ p.full = p.full - a.full;
|
|
+ a.full = rfixed_mul(p, ffreq);
|
|
+ if (a.full >= pll_out_min.full)
|
|
+ continue;
|
|
+ else {
|
|
+ DRM_ERROR("Unable to find pll dividers\n");
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ a.full = rfixed_const(10);
|
|
+ b.full = rfixed_mul(n, a);
|
|
+
|
|
+ frac_n.full = rfixed_floor(n);
|
|
+ frac_n.full = rfixed_mul(frac_n, a);
|
|
+ frac_n.full = b.full - frac_n.full;
|
|
+
|
|
+ *dot_clock_p = rfixed_trunc(best_freq);
|
|
+ *fb_div_p = rfixed_trunc(n);
|
|
+ *frac_fb_div_p = rfixed_trunc(frac_n);
|
|
+ *ref_div_p = rfixed_trunc(m);
|
|
+ *post_div_p = rfixed_trunc(p);
|
|
+
|
|
+ DRM_DEBUG("%u %d.%d, %d, %d\n", *dot_clock_p * 10, *fb_div_p, *frac_fb_div_p, *ref_div_p, *post_div_p);
|
|
+}
|
|
+
|
|
static void radeon_user_framebuffer_destroy(struct drm_framebuffer *fb)
|
|
{
|
|
struct radeon_framebuffer *radeon_fb = to_radeon_framebuffer(fb);
|
|
@@ -551,7 +684,6 @@ static void radeon_user_framebuffer_destroy(struct drm_framebuffer *fb)
|
|
radeonfb_remove(dev, fb);
|
|
|
|
if (radeon_fb->obj) {
|
|
- radeon_gem_object_unpin(radeon_fb->obj);
|
|
mutex_lock(&dev->struct_mutex);
|
|
drm_gem_object_unreference(radeon_fb->obj);
|
|
mutex_unlock(&dev->struct_mutex);
|
|
@@ -599,7 +731,11 @@ radeon_user_framebuffer_create(struct drm_device *dev,
|
|
struct drm_gem_object *obj;
|
|
|
|
obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle);
|
|
-
|
|
+ if (obj == NULL) {
|
|
+ dev_err(&dev->pdev->dev, "No GEM object associated to handle 0x%08X, "
|
|
+ "can't create framebuffer\n", mode_cmd->handle);
|
|
+ return NULL;
|
|
+ }
|
|
return radeon_framebuffer_create(dev, mode_cmd, obj);
|
|
}
|
|
|
|
@@ -629,7 +765,7 @@ static struct drm_prop_enum_list radeon_tv_std_enum_list[] =
|
|
{ TV_STD_SECAM, "secam" },
|
|
};
|
|
|
|
-int radeon_modeset_create_props(struct radeon_device *rdev)
|
|
+static int radeon_modeset_create_props(struct radeon_device *rdev)
|
|
{
|
|
int i, sz;
|
|
|
|
@@ -642,7 +778,7 @@ int radeon_modeset_create_props(struct radeon_device *rdev)
|
|
return -ENOMEM;
|
|
|
|
rdev->mode_info.coherent_mode_property->values[0] = 0;
|
|
- rdev->mode_info.coherent_mode_property->values[0] = 1;
|
|
+ rdev->mode_info.coherent_mode_property->values[1] = 1;
|
|
}
|
|
|
|
if (!ASIC_IS_AVIVO(rdev)) {
|
|
@@ -666,7 +802,7 @@ int radeon_modeset_create_props(struct radeon_device *rdev)
|
|
if (!rdev->mode_info.load_detect_property)
|
|
return -ENOMEM;
|
|
rdev->mode_info.load_detect_property->values[0] = 0;
|
|
- rdev->mode_info.load_detect_property->values[0] = 1;
|
|
+ rdev->mode_info.load_detect_property->values[1] = 1;
|
|
|
|
drm_mode_create_scaling_mode_property(rdev->ddev);
|
|
|
|
@@ -710,6 +846,12 @@ int radeon_modeset_init(struct radeon_device *rdev)
|
|
return ret;
|
|
}
|
|
|
|
+ /* check combios for a valid hardcoded EDID - Sun servers */
|
|
+ if (!rdev->is_atom_bios) {
|
|
+ /* check for hardcoded EDID in BIOS */
|
|
+ radeon_combios_check_hardcoded_edid(rdev);
|
|
+ }
|
|
+
|
|
if (rdev->flags & RADEON_SINGLE_CRTC)
|
|
num_crtc = 1;
|
|
|
|
@@ -723,13 +865,18 @@ int radeon_modeset_init(struct radeon_device *rdev)
|
|
if (!ret) {
|
|
return ret;
|
|
}
|
|
+ /* initialize hpd */
|
|
+ radeon_hpd_init(rdev);
|
|
drm_helper_initial_config(rdev->ddev);
|
|
return 0;
|
|
}
|
|
|
|
void radeon_modeset_fini(struct radeon_device *rdev)
|
|
{
|
|
+ kfree(rdev->mode_info.bios_hardcoded_edid);
|
|
+
|
|
if (rdev->mode_info.mode_config_initialized) {
|
|
+ radeon_hpd_fini(rdev);
|
|
drm_mode_config_cleanup(rdev->ddev);
|
|
rdev->mode_info.mode_config_initialized = false;
|
|
}
|
|
@@ -750,9 +897,17 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
|
|
if (encoder->crtc != crtc)
|
|
continue;
|
|
if (first) {
|
|
- radeon_crtc->rmx_type = radeon_encoder->rmx_type;
|
|
+ /* set scaling */
|
|
+ if (radeon_encoder->rmx_type == RMX_OFF)
|
|
+ radeon_crtc->rmx_type = RMX_OFF;
|
|
+ else if (mode->hdisplay < radeon_encoder->native_mode.hdisplay ||
|
|
+ mode->vdisplay < radeon_encoder->native_mode.vdisplay)
|
|
+ radeon_crtc->rmx_type = radeon_encoder->rmx_type;
|
|
+ else
|
|
+ radeon_crtc->rmx_type = RMX_OFF;
|
|
+ /* copy native mode */
|
|
memcpy(&radeon_crtc->native_mode,
|
|
- &radeon_encoder->native_mode,
|
|
+ &radeon_encoder->native_mode,
|
|
sizeof(struct drm_display_mode));
|
|
first = false;
|
|
} else {
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
|
|
index 7f50fb8..8ba3de7 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_drv.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
|
|
@@ -86,6 +86,8 @@ int radeon_benchmarking = 0;
|
|
int radeon_testing = 0;
|
|
int radeon_connector_table = 0;
|
|
int radeon_tv = 1;
|
|
+int radeon_new_pll = 1;
|
|
+int radeon_audio = 1;
|
|
|
|
MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers");
|
|
module_param_named(no_wb, radeon_no_wb, int, 0444);
|
|
@@ -120,6 +122,12 @@ module_param_named(connector_table, radeon_connector_table, int, 0444);
|
|
MODULE_PARM_DESC(tv, "TV enable (0 = disable)");
|
|
module_param_named(tv, radeon_tv, int, 0444);
|
|
|
|
+MODULE_PARM_DESC(new_pll, "Select new PLL code for AVIVO chips");
|
|
+module_param_named(new_pll, radeon_new_pll, int, 0444);
|
|
+
|
|
+MODULE_PARM_DESC(audio, "Audio enable (0 = disable)");
|
|
+module_param_named(audio, radeon_audio, int, 0444);
|
|
+
|
|
static int radeon_suspend(struct drm_device *dev, pm_message_t state)
|
|
{
|
|
drm_radeon_private_t *dev_priv = dev->dev_private;
|
|
@@ -188,7 +196,7 @@ static struct drm_driver driver_old = {
|
|
.owner = THIS_MODULE,
|
|
.open = drm_open,
|
|
.release = drm_release,
|
|
- .ioctl = drm_ioctl,
|
|
+ .unlocked_ioctl = drm_ioctl,
|
|
.mmap = drm_mmap,
|
|
.poll = drm_poll,
|
|
.fasync = drm_fasync,
|
|
@@ -276,7 +284,7 @@ static struct drm_driver kms_driver = {
|
|
.owner = THIS_MODULE,
|
|
.open = drm_open,
|
|
.release = drm_release,
|
|
- .ioctl = drm_ioctl,
|
|
+ .unlocked_ioctl = drm_ioctl,
|
|
.mmap = radeon_mmap,
|
|
.poll = drm_poll,
|
|
.fasync = drm_fasync,
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_drv.h b/drivers/gpu/drm/radeon/radeon_drv.h
|
|
index 350962e..e137852 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_drv.h
|
|
+++ b/drivers/gpu/drm/radeon/radeon_drv.h
|
|
@@ -1104,7 +1104,6 @@ extern u32 radeon_get_scratch(drm_radeon_private_t *dev_priv, int index);
|
|
# define R600_IT_WAIT_REG_MEM 0x00003C00
|
|
# define R600_IT_MEM_WRITE 0x00003D00
|
|
# define R600_IT_INDIRECT_BUFFER 0x00003200
|
|
-# define R600_IT_CP_INTERRUPT 0x00004000
|
|
# define R600_IT_SURFACE_SYNC 0x00004300
|
|
# define R600_CB0_DEST_BASE_ENA (1 << 6)
|
|
# define R600_TC_ACTION_ENA (1 << 23)
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c
|
|
index d42bc51..3c91724 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_encoders.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_encoders.c
|
|
@@ -35,6 +35,51 @@ extern int atom_debug;
|
|
bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index,
|
|
struct drm_display_mode *mode);
|
|
|
|
+static uint32_t radeon_encoder_clones(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
|
|
+ struct drm_encoder *clone_encoder;
|
|
+ uint32_t index_mask = 0;
|
|
+ int count;
|
|
+
|
|
+ /* DIG routing gets problematic */
|
|
+ if (rdev->family >= CHIP_R600)
|
|
+ return index_mask;
|
|
+ /* LVDS/TV are too wacky */
|
|
+ if (radeon_encoder->devices & ATOM_DEVICE_LCD_SUPPORT)
|
|
+ return index_mask;
|
|
+ /* DVO requires 2x ppll clocks depending on tmds chip */
|
|
+ if (radeon_encoder->devices & ATOM_DEVICE_DFP2_SUPPORT)
|
|
+ return index_mask;
|
|
+
|
|
+ count = -1;
|
|
+ list_for_each_entry(clone_encoder, &dev->mode_config.encoder_list, head) {
|
|
+ struct radeon_encoder *radeon_clone = to_radeon_encoder(clone_encoder);
|
|
+ count++;
|
|
+
|
|
+ if (clone_encoder == encoder)
|
|
+ continue;
|
|
+ if (radeon_clone->devices & (ATOM_DEVICE_LCD_SUPPORT))
|
|
+ continue;
|
|
+ if (radeon_clone->devices & ATOM_DEVICE_DFP2_SUPPORT)
|
|
+ continue;
|
|
+ else
|
|
+ index_mask |= (1 << count);
|
|
+ }
|
|
+ return index_mask;
|
|
+}
|
|
+
|
|
+void radeon_setup_encoder_clones(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_encoder *encoder;
|
|
+
|
|
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
|
+ encoder->possible_clones = radeon_encoder_clones(encoder);
|
|
+ }
|
|
+}
|
|
+
|
|
uint32_t
|
|
radeon_get_encoder_id(struct drm_device *dev, uint32_t supported_device, uint8_t dac)
|
|
{
|
|
@@ -111,6 +156,26 @@ radeon_get_encoder_id(struct drm_device *dev, uint32_t supported_device, uint8_t
|
|
return ret;
|
|
}
|
|
|
|
+static inline bool radeon_encoder_is_digital(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
|
|
+ switch (radeon_encoder->encoder_id) {
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_LVDS:
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_DVO1:
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_DDI:
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
|
|
+ return true;
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
void
|
|
radeon_link_encoder_connector(struct drm_device *dev)
|
|
{
|
|
@@ -157,35 +222,12 @@ radeon_get_connector_for_encoder(struct drm_encoder *encoder)
|
|
|
|
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
|
radeon_connector = to_radeon_connector(connector);
|
|
- if (radeon_encoder->devices & radeon_connector->devices)
|
|
+ if (radeon_encoder->active_device & radeon_connector->devices)
|
|
return connector;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
-/* used for both atom and legacy */
|
|
-void radeon_rmx_mode_fixup(struct drm_encoder *encoder,
|
|
- struct drm_display_mode *mode,
|
|
- struct drm_display_mode *adjusted_mode)
|
|
-{
|
|
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
|
|
- struct drm_device *dev = encoder->dev;
|
|
- struct radeon_device *rdev = dev->dev_private;
|
|
- struct drm_display_mode *native_mode = &radeon_encoder->native_mode;
|
|
-
|
|
- if (mode->hdisplay < native_mode->hdisplay ||
|
|
- mode->vdisplay < native_mode->vdisplay) {
|
|
- int mode_id = adjusted_mode->base.id;
|
|
- *adjusted_mode = *native_mode;
|
|
- if (!ASIC_IS_AVIVO(rdev)) {
|
|
- adjusted_mode->hdisplay = mode->hdisplay;
|
|
- adjusted_mode->vdisplay = mode->vdisplay;
|
|
- }
|
|
- adjusted_mode->base.id = mode_id;
|
|
- }
|
|
-}
|
|
-
|
|
-
|
|
static bool radeon_atom_mode_fixup(struct drm_encoder *encoder,
|
|
struct drm_display_mode *mode,
|
|
struct drm_display_mode *adjusted_mode)
|
|
@@ -198,14 +240,26 @@ static bool radeon_atom_mode_fixup(struct drm_encoder *encoder,
|
|
radeon_encoder_set_active_device(encoder);
|
|
drm_mode_set_crtcinfo(adjusted_mode, 0);
|
|
|
|
- if (radeon_encoder->rmx_type != RMX_OFF)
|
|
- radeon_rmx_mode_fixup(encoder, mode, adjusted_mode);
|
|
-
|
|
/* hw bug */
|
|
if ((mode->flags & DRM_MODE_FLAG_INTERLACE)
|
|
&& (mode->crtc_vsync_start < (mode->crtc_vdisplay + 2)))
|
|
adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + 2;
|
|
|
|
+ /* get the native mode for LVDS */
|
|
+ if (radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
|
|
+ struct drm_display_mode *native_mode = &radeon_encoder->native_mode;
|
|
+ int mode_id = adjusted_mode->base.id;
|
|
+ *adjusted_mode = *native_mode;
|
|
+ if (!ASIC_IS_AVIVO(rdev)) {
|
|
+ adjusted_mode->hdisplay = mode->hdisplay;
|
|
+ adjusted_mode->vdisplay = mode->vdisplay;
|
|
+ adjusted_mode->crtc_hdisplay = mode->hdisplay;
|
|
+ adjusted_mode->crtc_vdisplay = mode->vdisplay;
|
|
+ }
|
|
+ adjusted_mode->base.id = mode_id;
|
|
+ }
|
|
+
|
|
+ /* get the native mode for TV */
|
|
if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) {
|
|
struct radeon_encoder_atom_dac *tv_dac = radeon_encoder->enc_priv;
|
|
if (tv_dac) {
|
|
@@ -218,6 +272,12 @@ static bool radeon_atom_mode_fixup(struct drm_encoder *encoder,
|
|
}
|
|
}
|
|
|
|
+ if (ASIC_IS_DCE3(rdev) &&
|
|
+ (radeon_encoder->active_device & (ATOM_DEVICE_DFP_SUPPORT))) {
|
|
+ struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
|
|
+ radeon_dp_set_link_config(connector, mode);
|
|
+ }
|
|
+
|
|
return true;
|
|
}
|
|
|
|
@@ -392,7 +452,7 @@ union lvds_encoder_control {
|
|
LVDS_ENCODER_CONTROL_PS_ALLOCATION_V2 v2;
|
|
};
|
|
|
|
-static void
|
|
+void
|
|
atombios_digital_setup(struct drm_encoder *encoder, int action)
|
|
{
|
|
struct drm_device *dev = encoder->dev;
|
|
@@ -400,6 +460,7 @@ atombios_digital_setup(struct drm_encoder *encoder, int action)
|
|
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
|
|
union lvds_encoder_control args;
|
|
int index = 0;
|
|
+ int hdmi_detected = 0;
|
|
uint8_t frev, crev;
|
|
struct radeon_encoder_atom_dig *dig;
|
|
struct drm_connector *connector;
|
|
@@ -420,6 +481,9 @@ atombios_digital_setup(struct drm_encoder *encoder, int action)
|
|
if (!radeon_connector->con_priv)
|
|
return;
|
|
|
|
+ if (drm_detect_hdmi_monitor(radeon_connector->edid))
|
|
+ hdmi_detected = 1;
|
|
+
|
|
dig_connector = radeon_connector->con_priv;
|
|
|
|
memset(&args, 0, sizeof(args));
|
|
@@ -449,13 +513,13 @@ atombios_digital_setup(struct drm_encoder *encoder, int action)
|
|
case 1:
|
|
args.v1.ucMisc = 0;
|
|
args.v1.ucAction = action;
|
|
- if (drm_detect_hdmi_monitor(radeon_connector->edid))
|
|
+ if (hdmi_detected)
|
|
args.v1.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE;
|
|
args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
|
|
if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
|
|
- if (dig->lvds_misc & (1 << 0))
|
|
+ if (dig->lvds_misc & ATOM_PANEL_MISC_DUAL)
|
|
args.v1.ucMisc |= PANEL_ENCODER_MISC_DUAL;
|
|
- if (dig->lvds_misc & (1 << 1))
|
|
+ if (dig->lvds_misc & ATOM_PANEL_MISC_888RGB)
|
|
args.v1.ucMisc |= (1 << 1);
|
|
} else {
|
|
if (dig_connector->linkb)
|
|
@@ -474,7 +538,7 @@ atombios_digital_setup(struct drm_encoder *encoder, int action)
|
|
if (dig->coherent_mode)
|
|
args.v2.ucMisc |= PANEL_ENCODER_MISC_COHERENT;
|
|
}
|
|
- if (drm_detect_hdmi_monitor(radeon_connector->edid))
|
|
+ if (hdmi_detected)
|
|
args.v2.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE;
|
|
args.v2.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
|
|
args.v2.ucTruncate = 0;
|
|
@@ -482,18 +546,18 @@ atombios_digital_setup(struct drm_encoder *encoder, int action)
|
|
args.v2.ucTemporal = 0;
|
|
args.v2.ucFRC = 0;
|
|
if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
|
|
- if (dig->lvds_misc & (1 << 0))
|
|
+ if (dig->lvds_misc & ATOM_PANEL_MISC_DUAL)
|
|
args.v2.ucMisc |= PANEL_ENCODER_MISC_DUAL;
|
|
- if (dig->lvds_misc & (1 << 5)) {
|
|
+ if (dig->lvds_misc & ATOM_PANEL_MISC_SPATIAL) {
|
|
args.v2.ucSpatial = PANEL_ENCODER_SPATIAL_DITHER_EN;
|
|
- if (dig->lvds_misc & (1 << 1))
|
|
+ if (dig->lvds_misc & ATOM_PANEL_MISC_888RGB)
|
|
args.v2.ucSpatial |= PANEL_ENCODER_SPATIAL_DITHER_DEPTH;
|
|
}
|
|
- if (dig->lvds_misc & (1 << 6)) {
|
|
+ if (dig->lvds_misc & ATOM_PANEL_MISC_TEMPORAL) {
|
|
args.v2.ucTemporal = PANEL_ENCODER_TEMPORAL_DITHER_EN;
|
|
- if (dig->lvds_misc & (1 << 1))
|
|
+ if (dig->lvds_misc & ATOM_PANEL_MISC_888RGB)
|
|
args.v2.ucTemporal |= PANEL_ENCODER_TEMPORAL_DITHER_DEPTH;
|
|
- if (((dig->lvds_misc >> 2) & 0x3) == 2)
|
|
+ if (((dig->lvds_misc >> ATOM_PANEL_MISC_GREY_LEVEL_SHIFT) & 0x3) == 2)
|
|
args.v2.ucTemporal |= PANEL_ENCODER_TEMPORAL_LEVEL_4;
|
|
}
|
|
} else {
|
|
@@ -514,7 +578,7 @@ atombios_digital_setup(struct drm_encoder *encoder, int action)
|
|
}
|
|
|
|
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
|
|
-
|
|
+ r600_hdmi_enable(encoder, hdmi_detected);
|
|
}
|
|
|
|
int
|
|
@@ -522,6 +586,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
|
|
{
|
|
struct drm_connector *connector;
|
|
struct radeon_connector *radeon_connector;
|
|
+ struct radeon_connector_atom_dig *radeon_dig_connector;
|
|
|
|
connector = radeon_get_connector_for_encoder(encoder);
|
|
if (!connector)
|
|
@@ -551,21 +616,23 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
|
|
return ATOM_ENCODER_MODE_LVDS;
|
|
break;
|
|
case DRM_MODE_CONNECTOR_DisplayPort:
|
|
- /*if (radeon_output->MonType == MT_DP)
|
|
- return ATOM_ENCODER_MODE_DP;
|
|
- else*/
|
|
- if (drm_detect_hdmi_monitor(radeon_connector->edid))
|
|
+ case DRM_MODE_CONNECTOR_eDP:
|
|
+ radeon_dig_connector = radeon_connector->con_priv;
|
|
+ if ((radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
|
|
+ (radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP))
|
|
+ return ATOM_ENCODER_MODE_DP;
|
|
+ else if (drm_detect_hdmi_monitor(radeon_connector->edid))
|
|
return ATOM_ENCODER_MODE_HDMI;
|
|
else
|
|
return ATOM_ENCODER_MODE_DVI;
|
|
break;
|
|
- case CONNECTOR_DVI_A:
|
|
- case CONNECTOR_VGA:
|
|
+ case DRM_MODE_CONNECTOR_DVIA:
|
|
+ case DRM_MODE_CONNECTOR_VGA:
|
|
return ATOM_ENCODER_MODE_CRT;
|
|
break;
|
|
- case CONNECTOR_STV:
|
|
- case CONNECTOR_CTV:
|
|
- case CONNECTOR_DIN:
|
|
+ case DRM_MODE_CONNECTOR_Composite:
|
|
+ case DRM_MODE_CONNECTOR_SVIDEO:
|
|
+ case DRM_MODE_CONNECTOR_9PinDIN:
|
|
/* fix me */
|
|
return ATOM_ENCODER_MODE_TV;
|
|
/*return ATOM_ENCODER_MODE_CV;*/
|
|
@@ -573,6 +640,30 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
|
|
}
|
|
}
|
|
|
|
+/*
|
|
+ * DIG Encoder/Transmitter Setup
|
|
+ *
|
|
+ * DCE 3.0/3.1
|
|
+ * - 2 DIG transmitter blocks. UNIPHY (links A and B) and LVTMA.
|
|
+ * Supports up to 3 digital outputs
|
|
+ * - 2 DIG encoder blocks.
|
|
+ * DIG1 can drive UNIPHY link A or link B
|
|
+ * DIG2 can drive UNIPHY link B or LVTMA
|
|
+ *
|
|
+ * DCE 3.2
|
|
+ * - 3 DIG transmitter blocks. UNIPHY0/1/2 (links A and B).
|
|
+ * Supports up to 5 digital outputs
|
|
+ * - 2 DIG encoder blocks.
|
|
+ * DIG1/2 can drive UNIPHY0/1/2 link A or link B
|
|
+ *
|
|
+ * Routing
|
|
+ * crtc -> dig encoder -> UNIPHY/LVTMA (1 or 2 links)
|
|
+ * Examples:
|
|
+ * crtc0 -> dig2 -> LVTMA links A+B -> TMDS/HDMI
|
|
+ * crtc1 -> dig1 -> UNIPHY0 link B -> DP
|
|
+ * crtc0 -> dig1 -> UNIPHY2 link A -> LVDS
|
|
+ * crtc1 -> dig2 -> UNIPHY1 link B+A -> TMDS/HDMI
|
|
+ */
|
|
static void
|
|
atombios_dig_encoder_setup(struct drm_encoder *encoder, int action)
|
|
{
|
|
@@ -605,24 +696,11 @@ atombios_dig_encoder_setup(struct drm_encoder *encoder, int action)
|
|
|
|
memset(&args, 0, sizeof(args));
|
|
|
|
- if (ASIC_IS_DCE32(rdev)) {
|
|
- if (dig->dig_block)
|
|
- index = GetIndexIntoMasterTable(COMMAND, DIG2EncoderControl);
|
|
- else
|
|
- index = GetIndexIntoMasterTable(COMMAND, DIG1EncoderControl);
|
|
- num = dig->dig_block + 1;
|
|
- } else {
|
|
- switch (radeon_encoder->encoder_id) {
|
|
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
|
|
- index = GetIndexIntoMasterTable(COMMAND, DIG1EncoderControl);
|
|
- num = 1;
|
|
- break;
|
|
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
|
|
- index = GetIndexIntoMasterTable(COMMAND, DIG2EncoderControl);
|
|
- num = 2;
|
|
- break;
|
|
- }
|
|
- }
|
|
+ if (dig->dig_encoder)
|
|
+ index = GetIndexIntoMasterTable(COMMAND, DIG2EncoderControl);
|
|
+ else
|
|
+ index = GetIndexIntoMasterTable(COMMAND, DIG1EncoderControl);
|
|
+ num = dig->dig_encoder + 1;
|
|
|
|
atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev);
|
|
|
|
@@ -652,18 +730,21 @@ atombios_dig_encoder_setup(struct drm_encoder *encoder, int action)
|
|
}
|
|
}
|
|
|
|
- if (radeon_encoder->pixel_clock > 165000) {
|
|
- args.ucConfig |= ATOM_ENCODER_CONFIG_LINKA_B;
|
|
+ args.ucEncoderMode = atombios_get_encoder_mode(encoder);
|
|
+
|
|
+ if (args.ucEncoderMode == ATOM_ENCODER_MODE_DP) {
|
|
+ if (dig_connector->dp_clock == 270000)
|
|
+ args.ucConfig |= ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ;
|
|
+ args.ucLaneNum = dig_connector->dp_lane_count;
|
|
+ } else if (radeon_encoder->pixel_clock > 165000)
|
|
args.ucLaneNum = 8;
|
|
- } else {
|
|
- if (dig_connector->linkb)
|
|
- args.ucConfig |= ATOM_ENCODER_CONFIG_LINKB;
|
|
- else
|
|
- args.ucConfig |= ATOM_ENCODER_CONFIG_LINKA;
|
|
+ else
|
|
args.ucLaneNum = 4;
|
|
- }
|
|
|
|
- args.ucEncoderMode = atombios_get_encoder_mode(encoder);
|
|
+ if (dig_connector->linkb)
|
|
+ args.ucConfig |= ATOM_ENCODER_CONFIG_LINKB;
|
|
+ else
|
|
+ args.ucConfig |= ATOM_ENCODER_CONFIG_LINKA;
|
|
|
|
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
|
|
|
|
@@ -674,8 +755,8 @@ union dig_transmitter_control {
|
|
DIG_TRANSMITTER_CONTROL_PARAMETERS_V2 v2;
|
|
};
|
|
|
|
-static void
|
|
-atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action)
|
|
+void
|
|
+atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t lane_num, uint8_t lane_set)
|
|
{
|
|
struct drm_device *dev = encoder->dev;
|
|
struct radeon_device *rdev = dev->dev_private;
|
|
@@ -687,6 +768,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action)
|
|
struct drm_connector *connector;
|
|
struct radeon_connector *radeon_connector;
|
|
struct radeon_connector_atom_dig *dig_connector;
|
|
+ bool is_dp = false;
|
|
|
|
connector = radeon_get_connector_for_encoder(encoder);
|
|
if (!connector)
|
|
@@ -704,6 +786,9 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action)
|
|
|
|
dig_connector = radeon_connector->con_priv;
|
|
|
|
+ if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_DP)
|
|
+ is_dp = true;
|
|
+
|
|
memset(&args, 0, sizeof(args));
|
|
|
|
if (ASIC_IS_DCE32(rdev))
|
|
@@ -724,17 +809,23 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action)
|
|
args.v1.ucAction = action;
|
|
if (action == ATOM_TRANSMITTER_ACTION_INIT) {
|
|
args.v1.usInitInfo = radeon_connector->connector_object_id;
|
|
+ } else if (action == ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH) {
|
|
+ args.v1.asMode.ucLaneSel = lane_num;
|
|
+ args.v1.asMode.ucLaneSet = lane_set;
|
|
} else {
|
|
- if (radeon_encoder->pixel_clock > 165000)
|
|
+ if (is_dp)
|
|
+ args.v1.usPixelClock =
|
|
+ cpu_to_le16(dig_connector->dp_clock / 10);
|
|
+ else if (radeon_encoder->pixel_clock > 165000)
|
|
args.v1.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10);
|
|
else
|
|
args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
|
|
}
|
|
if (ASIC_IS_DCE32(rdev)) {
|
|
- if (radeon_encoder->pixel_clock > 165000)
|
|
- args.v2.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10);
|
|
- if (dig->dig_block)
|
|
+ if (dig->dig_encoder == 1)
|
|
args.v2.acConfig.ucEncoderSel = 1;
|
|
+ if (dig_connector->linkb)
|
|
+ args.v2.acConfig.ucLinkSel = 1;
|
|
|
|
switch (radeon_encoder->encoder_id) {
|
|
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
|
|
@@ -751,26 +842,30 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action)
|
|
break;
|
|
}
|
|
|
|
- if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
|
|
+ if (is_dp)
|
|
+ args.v2.acConfig.fCoherentMode = 1;
|
|
+ else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
|
|
if (dig->coherent_mode)
|
|
args.v2.acConfig.fCoherentMode = 1;
|
|
}
|
|
} else {
|
|
+
|
|
args.v1.ucConfig = ATOM_TRANSMITTER_CONFIG_CLKSRC_PPLL;
|
|
|
|
+ if (dig->dig_encoder)
|
|
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG2_ENCODER;
|
|
+ else
|
|
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG1_ENCODER;
|
|
+
|
|
switch (radeon_encoder->encoder_id) {
|
|
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
|
|
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG1_ENCODER;
|
|
if (rdev->flags & RADEON_IS_IGP) {
|
|
if (radeon_encoder->pixel_clock > 165000) {
|
|
- args.v1.ucConfig |= (ATOM_TRANSMITTER_CONFIG_8LANE_LINK |
|
|
- ATOM_TRANSMITTER_CONFIG_LINKA_B);
|
|
if (dig_connector->igp_lane_info & 0x3)
|
|
args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_0_7;
|
|
else if (dig_connector->igp_lane_info & 0xc)
|
|
args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_8_15;
|
|
} else {
|
|
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA;
|
|
if (dig_connector->igp_lane_info & 0x1)
|
|
args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_0_3;
|
|
else if (dig_connector->igp_lane_info & 0x2)
|
|
@@ -780,42 +875,27 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action)
|
|
else if (dig_connector->igp_lane_info & 0x8)
|
|
args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_12_15;
|
|
}
|
|
- } else {
|
|
- if (radeon_encoder->pixel_clock > 165000)
|
|
- args.v1.ucConfig |= (ATOM_TRANSMITTER_CONFIG_8LANE_LINK |
|
|
- ATOM_TRANSMITTER_CONFIG_LINKA_B |
|
|
- ATOM_TRANSMITTER_CONFIG_LANE_0_7);
|
|
- else {
|
|
- if (dig_connector->linkb)
|
|
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKB | ATOM_TRANSMITTER_CONFIG_LANE_0_3;
|
|
- else
|
|
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA | ATOM_TRANSMITTER_CONFIG_LANE_0_3;
|
|
- }
|
|
- }
|
|
- break;
|
|
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
|
|
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG2_ENCODER;
|
|
- if (radeon_encoder->pixel_clock > 165000)
|
|
- args.v1.ucConfig |= (ATOM_TRANSMITTER_CONFIG_8LANE_LINK |
|
|
- ATOM_TRANSMITTER_CONFIG_LINKA_B |
|
|
- ATOM_TRANSMITTER_CONFIG_LANE_0_7);
|
|
- else {
|
|
- if (dig_connector->linkb)
|
|
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKB | ATOM_TRANSMITTER_CONFIG_LANE_0_3;
|
|
- else
|
|
- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA | ATOM_TRANSMITTER_CONFIG_LANE_0_3;
|
|
}
|
|
break;
|
|
}
|
|
|
|
- if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
|
|
+ if (radeon_encoder->pixel_clock > 165000)
|
|
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_8LANE_LINK;
|
|
+
|
|
+ if (dig_connector->linkb)
|
|
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKB;
|
|
+ else
|
|
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA;
|
|
+
|
|
+ if (is_dp)
|
|
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_COHERENT;
|
|
+ else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
|
|
if (dig->coherent_mode)
|
|
args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_COHERENT;
|
|
}
|
|
}
|
|
|
|
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
|
|
-
|
|
}
|
|
|
|
static void
|
|
@@ -918,12 +998,16 @@ radeon_atom_encoder_dpms(struct drm_encoder *encoder, int mode)
|
|
if (is_dig) {
|
|
switch (mode) {
|
|
case DRM_MODE_DPMS_ON:
|
|
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE);
|
|
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT, 0, 0);
|
|
+ {
|
|
+ struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
|
|
+ dp_link_train(encoder, connector);
|
|
+ }
|
|
break;
|
|
case DRM_MODE_DPMS_STANDBY:
|
|
case DRM_MODE_DPMS_SUSPEND:
|
|
case DRM_MODE_DPMS_OFF:
|
|
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE);
|
|
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE_OUTPUT, 0, 0);
|
|
break;
|
|
}
|
|
} else {
|
|
@@ -957,6 +1041,7 @@ atombios_set_encoder_crtc_source(struct drm_encoder *encoder)
|
|
union crtc_sourc_param args;
|
|
int index = GetIndexIntoMasterTable(COMMAND, SelectCRTC_Source);
|
|
uint8_t frev, crev;
|
|
+ struct radeon_encoder_atom_dig *dig;
|
|
|
|
memset(&args, 0, sizeof(args));
|
|
|
|
@@ -1020,20 +1105,16 @@ atombios_set_encoder_crtc_source(struct drm_encoder *encoder)
|
|
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
|
|
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
|
|
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
|
|
- if (ASIC_IS_DCE32(rdev)) {
|
|
- if (radeon_crtc->crtc_id)
|
|
- args.v2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID;
|
|
- else
|
|
- args.v2.ucEncoderID = ASIC_INT_DIG1_ENCODER_ID;
|
|
- } else
|
|
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
|
|
+ dig = radeon_encoder->enc_priv;
|
|
+ if (dig->dig_encoder)
|
|
+ args.v2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID;
|
|
+ else
|
|
args.v2.ucEncoderID = ASIC_INT_DIG1_ENCODER_ID;
|
|
break;
|
|
case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
|
|
args.v2.ucEncoderID = ASIC_INT_DVO_ENCODER_ID;
|
|
break;
|
|
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
|
|
- args.v2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID;
|
|
- break;
|
|
case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
|
|
if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))
|
|
args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID;
|
|
@@ -1060,7 +1141,6 @@ atombios_set_encoder_crtc_source(struct drm_encoder *encoder)
|
|
}
|
|
|
|
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
|
|
-
|
|
}
|
|
|
|
static void
|
|
@@ -1094,6 +1174,47 @@ atombios_apply_encoder_quirks(struct drm_encoder *encoder,
|
|
}
|
|
}
|
|
|
|
+static int radeon_atom_pick_dig_encoder(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct drm_device *dev = encoder->dev;
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
|
|
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
|
|
+ struct drm_encoder *test_encoder;
|
|
+ struct radeon_encoder_atom_dig *dig;
|
|
+ uint32_t dig_enc_in_use = 0;
|
|
+ /* on DCE32 and encoder can driver any block so just crtc id */
|
|
+ if (ASIC_IS_DCE32(rdev)) {
|
|
+ return radeon_crtc->crtc_id;
|
|
+ }
|
|
+
|
|
+ /* on DCE3 - LVTMA can only be driven by DIGB */
|
|
+ list_for_each_entry(test_encoder, &dev->mode_config.encoder_list, head) {
|
|
+ struct radeon_encoder *radeon_test_encoder;
|
|
+
|
|
+ if (encoder == test_encoder)
|
|
+ continue;
|
|
+
|
|
+ if (!radeon_encoder_is_digital(test_encoder))
|
|
+ continue;
|
|
+
|
|
+ radeon_test_encoder = to_radeon_encoder(test_encoder);
|
|
+ dig = radeon_test_encoder->enc_priv;
|
|
+
|
|
+ if (dig->dig_encoder >= 0)
|
|
+ dig_enc_in_use |= (1 << dig->dig_encoder);
|
|
+ }
|
|
+
|
|
+ if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA) {
|
|
+ if (dig_enc_in_use & 0x2)
|
|
+ DRM_ERROR("LVDS required digital encoder 2 but it was in use - stealing\n");
|
|
+ return 1;
|
|
+ }
|
|
+ if (!(dig_enc_in_use & 1))
|
|
+ return 0;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
static void
|
|
radeon_atom_encoder_mode_set(struct drm_encoder *encoder,
|
|
struct drm_display_mode *mode,
|
|
@@ -1104,11 +1225,11 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder,
|
|
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
|
|
struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
|
|
|
|
- if (radeon_encoder->enc_priv) {
|
|
- struct radeon_encoder_atom_dig *dig;
|
|
-
|
|
- dig = radeon_encoder->enc_priv;
|
|
- dig->dig_block = radeon_crtc->crtc_id;
|
|
+ if (radeon_encoder->active_device &
|
|
+ (ATOM_DEVICE_DFP_SUPPORT | ATOM_DEVICE_LCD_SUPPORT)) {
|
|
+ struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
|
|
+ if (dig)
|
|
+ dig->dig_encoder = radeon_atom_pick_dig_encoder(encoder);
|
|
}
|
|
radeon_encoder->pixel_clock = adjusted_mode->clock;
|
|
|
|
@@ -1134,14 +1255,14 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder,
|
|
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
|
|
case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
|
|
/* disable the encoder and transmitter */
|
|
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE);
|
|
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
|
|
atombios_dig_encoder_setup(encoder, ATOM_DISABLE);
|
|
|
|
/* setup and enable the encoder and transmitter */
|
|
atombios_dig_encoder_setup(encoder, ATOM_ENABLE);
|
|
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_INIT);
|
|
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_SETUP);
|
|
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE);
|
|
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_INIT, 0, 0);
|
|
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_SETUP, 0, 0);
|
|
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
|
|
break;
|
|
case ENCODER_OBJECT_ID_INTERNAL_DDI:
|
|
atombios_ddia_setup(encoder, ATOM_ENABLE);
|
|
@@ -1160,6 +1281,8 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder,
|
|
break;
|
|
}
|
|
atombios_apply_encoder_quirks(encoder, adjusted_mode);
|
|
+
|
|
+ r600_hdmi_setmode(encoder, adjusted_mode);
|
|
}
|
|
|
|
static bool
|
|
@@ -1266,7 +1389,13 @@ static void radeon_atom_encoder_commit(struct drm_encoder *encoder)
|
|
static void radeon_atom_encoder_disable(struct drm_encoder *encoder)
|
|
{
|
|
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
|
|
+ struct radeon_encoder_atom_dig *dig;
|
|
radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
|
|
+
|
|
+ if (radeon_encoder_is_digital(encoder)) {
|
|
+ dig = radeon_encoder->enc_priv;
|
|
+ dig->dig_encoder = -1;
|
|
+ }
|
|
radeon_encoder->active_device = 0;
|
|
}
|
|
|
|
@@ -1323,6 +1452,7 @@ radeon_atombios_set_dig_info(struct radeon_encoder *radeon_encoder)
|
|
|
|
/* coherent mode by default */
|
|
dig->coherent_mode = true;
|
|
+ dig->dig_encoder = -1;
|
|
|
|
return dig;
|
|
}
|
|
@@ -1354,7 +1484,6 @@ radeon_add_atom_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t su
|
|
encoder->possible_crtcs = 0x1;
|
|
else
|
|
encoder->possible_crtcs = 0x3;
|
|
- encoder->possible_clones = 0;
|
|
|
|
radeon_encoder->enc_priv = NULL;
|
|
|
|
@@ -1406,4 +1535,6 @@ radeon_add_atom_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t su
|
|
drm_encoder_helper_add(encoder, &radeon_atom_dig_helper_funcs);
|
|
break;
|
|
}
|
|
+
|
|
+ r600_hdmi_init(encoder);
|
|
}
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
|
|
index b38c4c8..d71e346 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_fb.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_fb.c
|
|
@@ -59,7 +59,7 @@ static struct fb_ops radeonfb_ops = {
|
|
};
|
|
|
|
/**
|
|
- * Curretly it is assumed that the old framebuffer is reused.
|
|
+ * Currently it is assumed that the old framebuffer is reused.
|
|
*
|
|
* LOCKING
|
|
* caller should hold the mode config lock.
|
|
@@ -140,7 +140,7 @@ int radeonfb_create(struct drm_device *dev,
|
|
struct radeon_framebuffer *rfb;
|
|
struct drm_mode_fb_cmd mode_cmd;
|
|
struct drm_gem_object *gobj = NULL;
|
|
- struct radeon_object *robj = NULL;
|
|
+ struct radeon_bo *rbo = NULL;
|
|
struct device *device = &rdev->pdev->dev;
|
|
int size, aligned_size, ret;
|
|
u64 fb_gpuaddr;
|
|
@@ -168,14 +168,14 @@ int radeonfb_create(struct drm_device *dev,
|
|
ret = radeon_gem_object_create(rdev, aligned_size, 0,
|
|
RADEON_GEM_DOMAIN_VRAM,
|
|
false, ttm_bo_type_kernel,
|
|
- false, &gobj);
|
|
+ &gobj);
|
|
if (ret) {
|
|
printk(KERN_ERR "failed to allocate framebuffer (%d %d)\n",
|
|
surface_width, surface_height);
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
}
|
|
- robj = gobj->driver_private;
|
|
+ rbo = gobj->driver_private;
|
|
|
|
if (fb_tiled)
|
|
tiling_flags = RADEON_TILING_MACRO;
|
|
@@ -192,8 +192,13 @@ int radeonfb_create(struct drm_device *dev,
|
|
}
|
|
#endif
|
|
|
|
- if (tiling_flags)
|
|
- radeon_object_set_tiling_flags(robj, tiling_flags | RADEON_TILING_SURFACE, mode_cmd.pitch);
|
|
+ if (tiling_flags) {
|
|
+ ret = radeon_bo_set_tiling_flags(rbo,
|
|
+ tiling_flags | RADEON_TILING_SURFACE,
|
|
+ mode_cmd.pitch);
|
|
+ if (ret)
|
|
+ dev_err(rdev->dev, "FB failed to set tiling flags\n");
|
|
+ }
|
|
mutex_lock(&rdev->ddev->struct_mutex);
|
|
fb = radeon_framebuffer_create(rdev->ddev, &mode_cmd, gobj);
|
|
if (fb == NULL) {
|
|
@@ -201,10 +206,19 @@ int radeonfb_create(struct drm_device *dev,
|
|
ret = -ENOMEM;
|
|
goto out_unref;
|
|
}
|
|
- ret = radeon_object_pin(robj, RADEON_GEM_DOMAIN_VRAM, &fb_gpuaddr);
|
|
+ ret = radeon_bo_reserve(rbo, false);
|
|
+ if (unlikely(ret != 0))
|
|
+ goto out_unref;
|
|
+ ret = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &fb_gpuaddr);
|
|
+ if (ret) {
|
|
+ radeon_bo_unreserve(rbo);
|
|
+ goto out_unref;
|
|
+ }
|
|
+ if (fb_tiled)
|
|
+ radeon_bo_check_tiling(rbo, 0, 0);
|
|
+ ret = radeon_bo_kmap(rbo, &fbptr);
|
|
+ radeon_bo_unreserve(rbo);
|
|
if (ret) {
|
|
- printk(KERN_ERR "failed to pin framebuffer\n");
|
|
- ret = -ENOMEM;
|
|
goto out_unref;
|
|
}
|
|
|
|
@@ -213,7 +227,7 @@ int radeonfb_create(struct drm_device *dev,
|
|
*fb_p = fb;
|
|
rfb = to_radeon_framebuffer(fb);
|
|
rdev->fbdev_rfb = rfb;
|
|
- rdev->fbdev_robj = robj;
|
|
+ rdev->fbdev_rbo = rbo;
|
|
|
|
info = framebuffer_alloc(sizeof(struct radeon_fb_device), device);
|
|
if (info == NULL) {
|
|
@@ -234,15 +248,7 @@ int radeonfb_create(struct drm_device *dev,
|
|
if (ret)
|
|
goto out_unref;
|
|
|
|
- if (fb_tiled)
|
|
- radeon_object_check_tiling(robj, 0, 0);
|
|
-
|
|
- ret = radeon_object_kmap(robj, &fbptr);
|
|
- if (ret) {
|
|
- goto out_unref;
|
|
- }
|
|
-
|
|
- memset_io(fbptr, 0, aligned_size);
|
|
+ memset_io(fbptr, 0x0, aligned_size);
|
|
|
|
strcpy(info->fix.id, "radeondrmfb");
|
|
|
|
@@ -288,8 +294,12 @@ int radeonfb_create(struct drm_device *dev,
|
|
return 0;
|
|
|
|
out_unref:
|
|
- if (robj) {
|
|
- radeon_object_kunmap(robj);
|
|
+ if (rbo) {
|
|
+ ret = radeon_bo_reserve(rbo, false);
|
|
+ if (likely(ret == 0)) {
|
|
+ radeon_bo_kunmap(rbo);
|
|
+ radeon_bo_unreserve(rbo);
|
|
+ }
|
|
}
|
|
if (fb && ret) {
|
|
list_del(&fb->filp_head);
|
|
@@ -321,14 +331,22 @@ int radeon_parse_options(char *options)
|
|
|
|
int radeonfb_probe(struct drm_device *dev)
|
|
{
|
|
- return drm_fb_helper_single_fb_probe(dev, 32, &radeonfb_create);
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
+ int bpp_sel = 32;
|
|
+
|
|
+ /* select 8 bpp console on RN50 or 16MB cards */
|
|
+ if (ASIC_IS_RN50(rdev) || rdev->mc.real_vram_size <= (32*1024*1024))
|
|
+ bpp_sel = 8;
|
|
+
|
|
+ return drm_fb_helper_single_fb_probe(dev, bpp_sel, &radeonfb_create);
|
|
}
|
|
|
|
int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *fb)
|
|
{
|
|
struct fb_info *info;
|
|
struct radeon_framebuffer *rfb = to_radeon_framebuffer(fb);
|
|
- struct radeon_object *robj;
|
|
+ struct radeon_bo *rbo;
|
|
+ int r;
|
|
|
|
if (!fb) {
|
|
return -EINVAL;
|
|
@@ -336,10 +354,14 @@ int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *fb)
|
|
info = fb->fbdev;
|
|
if (info) {
|
|
struct radeon_fb_device *rfbdev = info->par;
|
|
- robj = rfb->obj->driver_private;
|
|
+ rbo = rfb->obj->driver_private;
|
|
unregister_framebuffer(info);
|
|
- radeon_object_kunmap(robj);
|
|
- radeon_object_unpin(robj);
|
|
+ r = radeon_bo_reserve(rbo, false);
|
|
+ if (likely(r == 0)) {
|
|
+ radeon_bo_kunmap(rbo);
|
|
+ radeon_bo_unpin(rbo);
|
|
+ radeon_bo_unreserve(rbo);
|
|
+ }
|
|
drm_fb_helper_free(&rfbdev->helper);
|
|
framebuffer_release(info);
|
|
}
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c
|
|
index 3beb26d..8495d4e 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_fence.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_fence.c
|
|
@@ -140,16 +140,15 @@ int radeon_fence_create(struct radeon_device *rdev, struct radeon_fence **fence)
|
|
|
|
bool radeon_fence_signaled(struct radeon_fence *fence)
|
|
{
|
|
- struct radeon_device *rdev = fence->rdev;
|
|
unsigned long irq_flags;
|
|
bool signaled = false;
|
|
|
|
- if (rdev->gpu_lockup) {
|
|
+ if (!fence)
|
|
return true;
|
|
- }
|
|
- if (fence == NULL) {
|
|
+
|
|
+ if (fence->rdev->gpu_lockup)
|
|
return true;
|
|
- }
|
|
+
|
|
write_lock_irqsave(&fence->rdev->fence_drv.lock, irq_flags);
|
|
signaled = fence->signaled;
|
|
/* if we are shuting down report all fence as signaled */
|
|
@@ -168,37 +167,6 @@ bool radeon_fence_signaled(struct radeon_fence *fence)
|
|
return signaled;
|
|
}
|
|
|
|
-int r600_fence_wait(struct radeon_fence *fence, bool intr, bool lazy)
|
|
-{
|
|
- struct radeon_device *rdev;
|
|
- int ret = 0;
|
|
-
|
|
- rdev = fence->rdev;
|
|
-
|
|
- __set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
|
|
-
|
|
- while (1) {
|
|
- if (radeon_fence_signaled(fence))
|
|
- break;
|
|
-
|
|
- if (time_after_eq(jiffies, fence->timeout)) {
|
|
- ret = -EBUSY;
|
|
- break;
|
|
- }
|
|
-
|
|
- if (lazy)
|
|
- schedule_timeout(1);
|
|
-
|
|
- if (intr && signal_pending(current)) {
|
|
- ret = -ERESTARTSYS;
|
|
- break;
|
|
- }
|
|
- }
|
|
- __set_current_state(TASK_RUNNING);
|
|
- return ret;
|
|
-}
|
|
-
|
|
-
|
|
int radeon_fence_wait(struct radeon_fence *fence, bool intr)
|
|
{
|
|
struct radeon_device *rdev;
|
|
@@ -216,13 +184,6 @@ int radeon_fence_wait(struct radeon_fence *fence, bool intr)
|
|
return 0;
|
|
}
|
|
|
|
- if (rdev->family >= CHIP_R600) {
|
|
- r = r600_fence_wait(fence, intr, 0);
|
|
- if (r == -ERESTARTSYS)
|
|
- return -EBUSY;
|
|
- return r;
|
|
- }
|
|
-
|
|
retry:
|
|
cur_jiffies = jiffies;
|
|
timeout = HZ / 100;
|
|
@@ -231,14 +192,17 @@ retry:
|
|
}
|
|
|
|
if (intr) {
|
|
+ radeon_irq_kms_sw_irq_get(rdev);
|
|
r = wait_event_interruptible_timeout(rdev->fence_drv.queue,
|
|
radeon_fence_signaled(fence), timeout);
|
|
- if (unlikely(r == -ERESTARTSYS)) {
|
|
- return -EBUSY;
|
|
- }
|
|
+ radeon_irq_kms_sw_irq_put(rdev);
|
|
+ if (unlikely(r < 0))
|
|
+ return r;
|
|
} else {
|
|
+ radeon_irq_kms_sw_irq_get(rdev);
|
|
r = wait_event_timeout(rdev->fence_drv.queue,
|
|
radeon_fence_signaled(fence), timeout);
|
|
+ radeon_irq_kms_sw_irq_put(rdev);
|
|
}
|
|
if (unlikely(!radeon_fence_signaled(fence))) {
|
|
if (unlikely(r == 0)) {
|
|
@@ -359,7 +323,7 @@ int radeon_fence_driver_init(struct radeon_device *rdev)
|
|
write_lock_irqsave(&rdev->fence_drv.lock, irq_flags);
|
|
r = radeon_scratch_get(rdev, &rdev->fence_drv.scratch_reg);
|
|
if (r) {
|
|
- DRM_ERROR("Fence failed to get a scratch register.");
|
|
+ dev_err(rdev->dev, "fence failed to get scratch register\n");
|
|
write_unlock_irqrestore(&rdev->fence_drv.lock, irq_flags);
|
|
return r;
|
|
}
|
|
@@ -370,9 +334,10 @@ int radeon_fence_driver_init(struct radeon_device *rdev)
|
|
INIT_LIST_HEAD(&rdev->fence_drv.signaled);
|
|
rdev->fence_drv.count_timeout = 0;
|
|
init_waitqueue_head(&rdev->fence_drv.queue);
|
|
+ rdev->fence_drv.initialized = true;
|
|
write_unlock_irqrestore(&rdev->fence_drv.lock, irq_flags);
|
|
if (radeon_debugfs_fence_init(rdev)) {
|
|
- DRM_ERROR("Failed to register debugfs file for fence !\n");
|
|
+ dev_err(rdev->dev, "fence debugfs file creation failed\n");
|
|
}
|
|
return 0;
|
|
}
|
|
@@ -381,11 +346,13 @@ void radeon_fence_driver_fini(struct radeon_device *rdev)
|
|
{
|
|
unsigned long irq_flags;
|
|
|
|
+ if (!rdev->fence_drv.initialized)
|
|
+ return;
|
|
wake_up_all(&rdev->fence_drv.queue);
|
|
write_lock_irqsave(&rdev->fence_drv.lock, irq_flags);
|
|
radeon_scratch_free(rdev, rdev->fence_drv.scratch_reg);
|
|
write_unlock_irqrestore(&rdev->fence_drv.lock, irq_flags);
|
|
- DRM_INFO("radeon: fence finalized\n");
|
|
+ rdev->fence_drv.initialized = false;
|
|
}
|
|
|
|
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_fixed.h b/drivers/gpu/drm/radeon/radeon_fixed.h
|
|
index 90187d1..3d4d84e 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_fixed.h
|
|
+++ b/drivers/gpu/drm/radeon/radeon_fixed.h
|
|
@@ -38,6 +38,23 @@ typedef union rfixed {
|
|
#define fixed_init_half(A) { .full = rfixed_const_half((A)) }
|
|
#define rfixed_trunc(A) ((A).full >> 12)
|
|
|
|
+static inline u32 rfixed_floor(fixed20_12 A)
|
|
+{
|
|
+ u32 non_frac = rfixed_trunc(A);
|
|
+
|
|
+ return rfixed_const(non_frac);
|
|
+}
|
|
+
|
|
+static inline u32 rfixed_ceil(fixed20_12 A)
|
|
+{
|
|
+ u32 non_frac = rfixed_trunc(A);
|
|
+
|
|
+ if (A.full > rfixed_const(non_frac))
|
|
+ return rfixed_const(non_frac + 1);
|
|
+ else
|
|
+ return rfixed_const(non_frac);
|
|
+}
|
|
+
|
|
static inline u32 rfixed_div(fixed20_12 A, fixed20_12 B)
|
|
{
|
|
u64 tmp = ((u64)A.full << 13);
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_gart.c b/drivers/gpu/drm/radeon/radeon_gart.c
|
|
index a68d756..e73d56e 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_gart.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_gart.c
|
|
@@ -78,11 +78,9 @@ int radeon_gart_table_vram_alloc(struct radeon_device *rdev)
|
|
int r;
|
|
|
|
if (rdev->gart.table.vram.robj == NULL) {
|
|
- r = radeon_object_create(rdev, NULL,
|
|
- rdev->gart.table_size,
|
|
- true,
|
|
- RADEON_GEM_DOMAIN_VRAM,
|
|
- false, &rdev->gart.table.vram.robj);
|
|
+ r = radeon_bo_create(rdev, NULL, rdev->gart.table_size,
|
|
+ true, RADEON_GEM_DOMAIN_VRAM,
|
|
+ &rdev->gart.table.vram.robj);
|
|
if (r) {
|
|
return r;
|
|
}
|
|
@@ -95,32 +93,38 @@ int radeon_gart_table_vram_pin(struct radeon_device *rdev)
|
|
uint64_t gpu_addr;
|
|
int r;
|
|
|
|
- r = radeon_object_pin(rdev->gart.table.vram.robj,
|
|
- RADEON_GEM_DOMAIN_VRAM, &gpu_addr);
|
|
- if (r) {
|
|
- radeon_object_unref(&rdev->gart.table.vram.robj);
|
|
+ r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
|
|
+ if (unlikely(r != 0))
|
|
return r;
|
|
- }
|
|
- r = radeon_object_kmap(rdev->gart.table.vram.robj,
|
|
- (void **)&rdev->gart.table.vram.ptr);
|
|
+ r = radeon_bo_pin(rdev->gart.table.vram.robj,
|
|
+ RADEON_GEM_DOMAIN_VRAM, &gpu_addr);
|
|
if (r) {
|
|
- radeon_object_unpin(rdev->gart.table.vram.robj);
|
|
- radeon_object_unref(&rdev->gart.table.vram.robj);
|
|
- DRM_ERROR("radeon: failed to map gart vram table.\n");
|
|
+ radeon_bo_unreserve(rdev->gart.table.vram.robj);
|
|
return r;
|
|
}
|
|
+ r = radeon_bo_kmap(rdev->gart.table.vram.robj,
|
|
+ (void **)&rdev->gart.table.vram.ptr);
|
|
+ if (r)
|
|
+ radeon_bo_unpin(rdev->gart.table.vram.robj);
|
|
+ radeon_bo_unreserve(rdev->gart.table.vram.robj);
|
|
rdev->gart.table_addr = gpu_addr;
|
|
- return 0;
|
|
+ return r;
|
|
}
|
|
|
|
void radeon_gart_table_vram_free(struct radeon_device *rdev)
|
|
{
|
|
+ int r;
|
|
+
|
|
if (rdev->gart.table.vram.robj == NULL) {
|
|
return;
|
|
}
|
|
- radeon_object_kunmap(rdev->gart.table.vram.robj);
|
|
- radeon_object_unpin(rdev->gart.table.vram.robj);
|
|
- radeon_object_unref(&rdev->gart.table.vram.robj);
|
|
+ r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
|
|
+ if (likely(r == 0)) {
|
|
+ radeon_bo_kunmap(rdev->gart.table.vram.robj);
|
|
+ radeon_bo_unpin(rdev->gart.table.vram.robj);
|
|
+ radeon_bo_unreserve(rdev->gart.table.vram.robj);
|
|
+ }
|
|
+ radeon_bo_unref(&rdev->gart.table.vram.robj);
|
|
}
|
|
|
|
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c
|
|
index d880edf..db8e9a3 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_gem.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_gem.c
|
|
@@ -38,22 +38,21 @@ int radeon_gem_object_init(struct drm_gem_object *obj)
|
|
|
|
void radeon_gem_object_free(struct drm_gem_object *gobj)
|
|
{
|
|
- struct radeon_object *robj = gobj->driver_private;
|
|
+ struct radeon_bo *robj = gobj->driver_private;
|
|
|
|
gobj->driver_private = NULL;
|
|
if (robj) {
|
|
- radeon_object_unref(&robj);
|
|
+ radeon_bo_unref(&robj);
|
|
}
|
|
}
|
|
|
|
int radeon_gem_object_create(struct radeon_device *rdev, int size,
|
|
- int alignment, int initial_domain,
|
|
- bool discardable, bool kernel,
|
|
- bool interruptible,
|
|
- struct drm_gem_object **obj)
|
|
+ int alignment, int initial_domain,
|
|
+ bool discardable, bool kernel,
|
|
+ struct drm_gem_object **obj)
|
|
{
|
|
struct drm_gem_object *gobj;
|
|
- struct radeon_object *robj;
|
|
+ struct radeon_bo *robj;
|
|
int r;
|
|
|
|
*obj = NULL;
|
|
@@ -65,11 +64,11 @@ int radeon_gem_object_create(struct radeon_device *rdev, int size,
|
|
if (alignment < PAGE_SIZE) {
|
|
alignment = PAGE_SIZE;
|
|
}
|
|
- r = radeon_object_create(rdev, gobj, size, kernel, initial_domain,
|
|
- interruptible, &robj);
|
|
+ r = radeon_bo_create(rdev, gobj, size, kernel, initial_domain, &robj);
|
|
if (r) {
|
|
- DRM_ERROR("Failed to allocate GEM object (%d, %d, %u)\n",
|
|
- size, initial_domain, alignment);
|
|
+ if (r != -ERESTARTSYS)
|
|
+ DRM_ERROR("Failed to allocate GEM object (%d, %d, %u, %d)\n",
|
|
+ size, initial_domain, alignment, r);
|
|
mutex_lock(&rdev->ddev->struct_mutex);
|
|
drm_gem_object_unreference(gobj);
|
|
mutex_unlock(&rdev->ddev->struct_mutex);
|
|
@@ -83,33 +82,33 @@ int radeon_gem_object_create(struct radeon_device *rdev, int size,
|
|
int radeon_gem_object_pin(struct drm_gem_object *obj, uint32_t pin_domain,
|
|
uint64_t *gpu_addr)
|
|
{
|
|
- struct radeon_object *robj = obj->driver_private;
|
|
- uint32_t flags;
|
|
+ struct radeon_bo *robj = obj->driver_private;
|
|
+ int r;
|
|
|
|
- switch (pin_domain) {
|
|
- case RADEON_GEM_DOMAIN_VRAM:
|
|
- flags = TTM_PL_FLAG_VRAM;
|
|
- break;
|
|
- case RADEON_GEM_DOMAIN_GTT:
|
|
- flags = TTM_PL_FLAG_TT;
|
|
- break;
|
|
- default:
|
|
- flags = TTM_PL_FLAG_SYSTEM;
|
|
- break;
|
|
- }
|
|
- return radeon_object_pin(robj, flags, gpu_addr);
|
|
+ r = radeon_bo_reserve(robj, false);
|
|
+ if (unlikely(r != 0))
|
|
+ return r;
|
|
+ r = radeon_bo_pin(robj, pin_domain, gpu_addr);
|
|
+ radeon_bo_unreserve(robj);
|
|
+ return r;
|
|
}
|
|
|
|
void radeon_gem_object_unpin(struct drm_gem_object *obj)
|
|
{
|
|
- struct radeon_object *robj = obj->driver_private;
|
|
- radeon_object_unpin(robj);
|
|
+ struct radeon_bo *robj = obj->driver_private;
|
|
+ int r;
|
|
+
|
|
+ r = radeon_bo_reserve(robj, false);
|
|
+ if (likely(r == 0)) {
|
|
+ radeon_bo_unpin(robj);
|
|
+ radeon_bo_unreserve(robj);
|
|
+ }
|
|
}
|
|
|
|
int radeon_gem_set_domain(struct drm_gem_object *gobj,
|
|
uint32_t rdomain, uint32_t wdomain)
|
|
{
|
|
- struct radeon_object *robj;
|
|
+ struct radeon_bo *robj;
|
|
uint32_t domain;
|
|
int r;
|
|
|
|
@@ -127,7 +126,7 @@ int radeon_gem_set_domain(struct drm_gem_object *gobj,
|
|
}
|
|
if (domain == RADEON_GEM_DOMAIN_CPU) {
|
|
/* Asking for cpu access wait for object idle */
|
|
- r = radeon_object_wait(robj);
|
|
+ r = radeon_bo_wait(robj, NULL, false);
|
|
if (r) {
|
|
printk(KERN_ERR "Failed to wait for object !\n");
|
|
return r;
|
|
@@ -144,7 +143,7 @@ int radeon_gem_init(struct radeon_device *rdev)
|
|
|
|
void radeon_gem_fini(struct radeon_device *rdev)
|
|
{
|
|
- radeon_object_force_delete(rdev);
|
|
+ radeon_bo_force_delete(rdev);
|
|
}
|
|
|
|
|
|
@@ -158,9 +157,13 @@ int radeon_gem_info_ioctl(struct drm_device *dev, void *data,
|
|
struct drm_radeon_gem_info *args = data;
|
|
|
|
args->vram_size = rdev->mc.real_vram_size;
|
|
- /* FIXME: report somethings that makes sense */
|
|
- args->vram_visible = rdev->mc.real_vram_size - (4 * 1024 * 1024);
|
|
- args->gart_size = rdev->mc.gtt_size;
|
|
+ args->vram_visible = rdev->mc.real_vram_size;
|
|
+ if (rdev->stollen_vga_memory)
|
|
+ args->vram_visible -= radeon_bo_size(rdev->stollen_vga_memory);
|
|
+ if (rdev->fbdev_rbo)
|
|
+ args->vram_visible -= radeon_bo_size(rdev->fbdev_rbo);
|
|
+ args->gart_size = rdev->mc.gtt_size - rdev->cp.ring_size - 4096 -
|
|
+ RADEON_IB_POOL_SIZE*64*1024;
|
|
return 0;
|
|
}
|
|
|
|
@@ -192,8 +195,8 @@ int radeon_gem_create_ioctl(struct drm_device *dev, void *data,
|
|
/* create a gem object to contain this object in */
|
|
args->size = roundup(args->size, PAGE_SIZE);
|
|
r = radeon_gem_object_create(rdev, args->size, args->alignment,
|
|
- args->initial_domain, false,
|
|
- false, true, &gobj);
|
|
+ args->initial_domain, false,
|
|
+ false, &gobj);
|
|
if (r) {
|
|
return r;
|
|
}
|
|
@@ -218,7 +221,7 @@ int radeon_gem_set_domain_ioctl(struct drm_device *dev, void *data,
|
|
* just validate the BO into a certain domain */
|
|
struct drm_radeon_gem_set_domain *args = data;
|
|
struct drm_gem_object *gobj;
|
|
- struct radeon_object *robj;
|
|
+ struct radeon_bo *robj;
|
|
int r;
|
|
|
|
/* for now if someone requests domain CPU -
|
|
@@ -244,19 +247,18 @@ int radeon_gem_mmap_ioctl(struct drm_device *dev, void *data,
|
|
{
|
|
struct drm_radeon_gem_mmap *args = data;
|
|
struct drm_gem_object *gobj;
|
|
- struct radeon_object *robj;
|
|
- int r;
|
|
+ struct radeon_bo *robj;
|
|
|
|
gobj = drm_gem_object_lookup(dev, filp, args->handle);
|
|
if (gobj == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
robj = gobj->driver_private;
|
|
- r = radeon_object_mmap(robj, &args->addr_ptr);
|
|
+ args->addr_ptr = radeon_bo_mmap_offset(robj);
|
|
mutex_lock(&dev->struct_mutex);
|
|
drm_gem_object_unreference(gobj);
|
|
mutex_unlock(&dev->struct_mutex);
|
|
- return r;
|
|
+ return 0;
|
|
}
|
|
|
|
int radeon_gem_busy_ioctl(struct drm_device *dev, void *data,
|
|
@@ -264,16 +266,16 @@ int radeon_gem_busy_ioctl(struct drm_device *dev, void *data,
|
|
{
|
|
struct drm_radeon_gem_busy *args = data;
|
|
struct drm_gem_object *gobj;
|
|
- struct radeon_object *robj;
|
|
+ struct radeon_bo *robj;
|
|
int r;
|
|
- uint32_t cur_placement;
|
|
+ uint32_t cur_placement = 0;
|
|
|
|
gobj = drm_gem_object_lookup(dev, filp, args->handle);
|
|
if (gobj == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
robj = gobj->driver_private;
|
|
- r = radeon_object_busy_domain(robj, &cur_placement);
|
|
+ r = radeon_bo_wait(robj, &cur_placement, true);
|
|
switch (cur_placement) {
|
|
case TTM_PL_VRAM:
|
|
args->domain = RADEON_GEM_DOMAIN_VRAM;
|
|
@@ -297,7 +299,7 @@ int radeon_gem_wait_idle_ioctl(struct drm_device *dev, void *data,
|
|
{
|
|
struct drm_radeon_gem_wait_idle *args = data;
|
|
struct drm_gem_object *gobj;
|
|
- struct radeon_object *robj;
|
|
+ struct radeon_bo *robj;
|
|
int r;
|
|
|
|
gobj = drm_gem_object_lookup(dev, filp, args->handle);
|
|
@@ -305,7 +307,10 @@ int radeon_gem_wait_idle_ioctl(struct drm_device *dev, void *data,
|
|
return -EINVAL;
|
|
}
|
|
robj = gobj->driver_private;
|
|
- r = radeon_object_wait(robj);
|
|
+ r = radeon_bo_wait(robj, NULL, false);
|
|
+ /* callback hw specific functions if any */
|
|
+ if (robj->rdev->asic->ioctl_wait_idle)
|
|
+ robj->rdev->asic->ioctl_wait_idle(robj->rdev, robj);
|
|
mutex_lock(&dev->struct_mutex);
|
|
drm_gem_object_unreference(gobj);
|
|
mutex_unlock(&dev->struct_mutex);
|
|
@@ -317,7 +322,7 @@ int radeon_gem_set_tiling_ioctl(struct drm_device *dev, void *data,
|
|
{
|
|
struct drm_radeon_gem_set_tiling *args = data;
|
|
struct drm_gem_object *gobj;
|
|
- struct radeon_object *robj;
|
|
+ struct radeon_bo *robj;
|
|
int r = 0;
|
|
|
|
DRM_DEBUG("%d \n", args->handle);
|
|
@@ -325,7 +330,7 @@ int radeon_gem_set_tiling_ioctl(struct drm_device *dev, void *data,
|
|
if (gobj == NULL)
|
|
return -EINVAL;
|
|
robj = gobj->driver_private;
|
|
- radeon_object_set_tiling_flags(robj, args->tiling_flags, args->pitch);
|
|
+ r = radeon_bo_set_tiling_flags(robj, args->tiling_flags, args->pitch);
|
|
mutex_lock(&dev->struct_mutex);
|
|
drm_gem_object_unreference(gobj);
|
|
mutex_unlock(&dev->struct_mutex);
|
|
@@ -337,16 +342,20 @@ int radeon_gem_get_tiling_ioctl(struct drm_device *dev, void *data,
|
|
{
|
|
struct drm_radeon_gem_get_tiling *args = data;
|
|
struct drm_gem_object *gobj;
|
|
- struct radeon_object *robj;
|
|
+ struct radeon_bo *rbo;
|
|
int r = 0;
|
|
|
|
DRM_DEBUG("\n");
|
|
gobj = drm_gem_object_lookup(dev, filp, args->handle);
|
|
if (gobj == NULL)
|
|
return -EINVAL;
|
|
- robj = gobj->driver_private;
|
|
- radeon_object_get_tiling_flags(robj, &args->tiling_flags,
|
|
- &args->pitch);
|
|
+ rbo = gobj->driver_private;
|
|
+ r = radeon_bo_reserve(rbo, false);
|
|
+ if (unlikely(r != 0))
|
|
+ goto out;
|
|
+ radeon_bo_get_tiling_flags(rbo, &args->tiling_flags, &args->pitch);
|
|
+ radeon_bo_unreserve(rbo);
|
|
+out:
|
|
mutex_lock(&dev->struct_mutex);
|
|
drm_gem_object_unreference(gobj);
|
|
mutex_unlock(&dev->struct_mutex);
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_i2c.c b/drivers/gpu/drm/radeon/radeon_i2c.c
|
|
index dd438d3..da3da1e 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_i2c.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_i2c.c
|
|
@@ -59,35 +59,43 @@ bool radeon_ddc_probe(struct radeon_connector *radeon_connector)
|
|
}
|
|
|
|
|
|
-void radeon_i2c_do_lock(struct radeon_connector *radeon_connector, int lock_state)
|
|
+void radeon_i2c_do_lock(struct radeon_i2c_chan *i2c, int lock_state)
|
|
{
|
|
- struct radeon_device *rdev = radeon_connector->base.dev->dev_private;
|
|
+ struct radeon_device *rdev = i2c->dev->dev_private;
|
|
+ struct radeon_i2c_bus_rec *rec = &i2c->rec;
|
|
uint32_t temp;
|
|
- struct radeon_i2c_bus_rec *rec = &radeon_connector->ddc_bus->rec;
|
|
|
|
/* RV410 appears to have a bug where the hw i2c in reset
|
|
* holds the i2c port in a bad state - switch hw i2c away before
|
|
* doing DDC - do this for all r200s/r300s/r400s for safety sake
|
|
*/
|
|
- if ((rdev->family >= CHIP_R200) && !ASIC_IS_AVIVO(rdev)) {
|
|
- if (rec->a_clk_reg == RADEON_GPIO_MONID) {
|
|
- WREG32(RADEON_DVI_I2C_CNTL_0, (RADEON_I2C_SOFT_RST |
|
|
- R200_DVI_I2C_PIN_SEL(R200_SEL_DDC1)));
|
|
- } else {
|
|
- WREG32(RADEON_DVI_I2C_CNTL_0, (RADEON_I2C_SOFT_RST |
|
|
- R200_DVI_I2C_PIN_SEL(R200_SEL_DDC3)));
|
|
+ if (rec->hw_capable) {
|
|
+ if ((rdev->family >= CHIP_R200) && !ASIC_IS_AVIVO(rdev)) {
|
|
+ if (rec->a_clk_reg == RADEON_GPIO_MONID) {
|
|
+ WREG32(RADEON_DVI_I2C_CNTL_0, (RADEON_I2C_SOFT_RST |
|
|
+ R200_DVI_I2C_PIN_SEL(R200_SEL_DDC1)));
|
|
+ } else {
|
|
+ WREG32(RADEON_DVI_I2C_CNTL_0, (RADEON_I2C_SOFT_RST |
|
|
+ R200_DVI_I2C_PIN_SEL(R200_SEL_DDC3)));
|
|
+ }
|
|
}
|
|
}
|
|
- if (lock_state) {
|
|
- temp = RREG32(rec->a_clk_reg);
|
|
- temp &= ~(rec->a_clk_mask);
|
|
- WREG32(rec->a_clk_reg, temp);
|
|
-
|
|
- temp = RREG32(rec->a_data_reg);
|
|
- temp &= ~(rec->a_data_mask);
|
|
- WREG32(rec->a_data_reg, temp);
|
|
- }
|
|
|
|
+ /* clear the output pin values */
|
|
+ temp = RREG32(rec->a_clk_reg) & ~rec->a_clk_mask;
|
|
+ WREG32(rec->a_clk_reg, temp);
|
|
+
|
|
+ temp = RREG32(rec->a_data_reg) & ~rec->a_data_mask;
|
|
+ WREG32(rec->a_data_reg, temp);
|
|
+
|
|
+ /* set the pins to input */
|
|
+ temp = RREG32(rec->en_clk_reg) & ~rec->en_clk_mask;
|
|
+ WREG32(rec->en_clk_reg, temp);
|
|
+
|
|
+ temp = RREG32(rec->en_data_reg) & ~rec->en_data_mask;
|
|
+ WREG32(rec->en_data_reg, temp);
|
|
+
|
|
+ /* mask the gpio pins for software use */
|
|
temp = RREG32(rec->mask_clk_reg);
|
|
if (lock_state)
|
|
temp |= rec->mask_clk_mask;
|
|
@@ -112,8 +120,9 @@ static int get_clock(void *i2c_priv)
|
|
struct radeon_i2c_bus_rec *rec = &i2c->rec;
|
|
uint32_t val;
|
|
|
|
- val = RREG32(rec->get_clk_reg);
|
|
- val &= rec->get_clk_mask;
|
|
+ /* read the value off the pin */
|
|
+ val = RREG32(rec->y_clk_reg);
|
|
+ val &= rec->y_clk_mask;
|
|
|
|
return (val != 0);
|
|
}
|
|
@@ -126,8 +135,10 @@ static int get_data(void *i2c_priv)
|
|
struct radeon_i2c_bus_rec *rec = &i2c->rec;
|
|
uint32_t val;
|
|
|
|
- val = RREG32(rec->get_data_reg);
|
|
- val &= rec->get_data_mask;
|
|
+ /* read the value off the pin */
|
|
+ val = RREG32(rec->y_data_reg);
|
|
+ val &= rec->y_data_mask;
|
|
+
|
|
return (val != 0);
|
|
}
|
|
|
|
@@ -138,9 +149,10 @@ static void set_clock(void *i2c_priv, int clock)
|
|
struct radeon_i2c_bus_rec *rec = &i2c->rec;
|
|
uint32_t val;
|
|
|
|
- val = RREG32(rec->put_clk_reg) & (uint32_t)~(rec->put_clk_mask);
|
|
- val |= clock ? 0 : rec->put_clk_mask;
|
|
- WREG32(rec->put_clk_reg, val);
|
|
+ /* set pin direction */
|
|
+ val = RREG32(rec->en_clk_reg) & ~rec->en_clk_mask;
|
|
+ val |= clock ? 0 : rec->en_clk_mask;
|
|
+ WREG32(rec->en_clk_reg, val);
|
|
}
|
|
|
|
static void set_data(void *i2c_priv, int data)
|
|
@@ -150,14 +162,15 @@ static void set_data(void *i2c_priv, int data)
|
|
struct radeon_i2c_bus_rec *rec = &i2c->rec;
|
|
uint32_t val;
|
|
|
|
- val = RREG32(rec->put_data_reg) & (uint32_t)~(rec->put_data_mask);
|
|
- val |= data ? 0 : rec->put_data_mask;
|
|
- WREG32(rec->put_data_reg, val);
|
|
+ /* set pin direction */
|
|
+ val = RREG32(rec->en_data_reg) & ~rec->en_data_mask;
|
|
+ val |= data ? 0 : rec->en_data_mask;
|
|
+ WREG32(rec->en_data_reg, val);
|
|
}
|
|
|
|
struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev,
|
|
- struct radeon_i2c_bus_rec *rec,
|
|
- const char *name)
|
|
+ struct radeon_i2c_bus_rec *rec,
|
|
+ const char *name)
|
|
{
|
|
struct radeon_i2c_chan *i2c;
|
|
int ret;
|
|
@@ -167,20 +180,19 @@ struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev,
|
|
return NULL;
|
|
|
|
i2c->adapter.owner = THIS_MODULE;
|
|
- i2c->adapter.algo_data = &i2c->algo;
|
|
i2c->dev = dev;
|
|
- i2c->algo.setsda = set_data;
|
|
- i2c->algo.setscl = set_clock;
|
|
- i2c->algo.getsda = get_data;
|
|
- i2c->algo.getscl = get_clock;
|
|
- i2c->algo.udelay = 20;
|
|
+ i2c_set_adapdata(&i2c->adapter, i2c);
|
|
+ i2c->adapter.algo_data = &i2c->algo.bit;
|
|
+ i2c->algo.bit.setsda = set_data;
|
|
+ i2c->algo.bit.setscl = set_clock;
|
|
+ i2c->algo.bit.getsda = get_data;
|
|
+ i2c->algo.bit.getscl = get_clock;
|
|
+ i2c->algo.bit.udelay = 20;
|
|
/* vesa says 2.2 ms is enough, 1 jiffy doesn't seem to always
|
|
* make this, 2 jiffies is a lot more reliable */
|
|
- i2c->algo.timeout = 2;
|
|
- i2c->algo.data = i2c;
|
|
+ i2c->algo.bit.timeout = 2;
|
|
+ i2c->algo.bit.data = i2c;
|
|
i2c->rec = *rec;
|
|
- i2c_set_adapdata(&i2c->adapter, i2c);
|
|
-
|
|
ret = i2c_bit_add_bus(&i2c->adapter);
|
|
if (ret) {
|
|
DRM_INFO("Failed to register i2c %s\n", name);
|
|
@@ -194,6 +206,38 @@ out_free:
|
|
|
|
}
|
|
|
|
+struct radeon_i2c_chan *radeon_i2c_create_dp(struct drm_device *dev,
|
|
+ struct radeon_i2c_bus_rec *rec,
|
|
+ const char *name)
|
|
+{
|
|
+ struct radeon_i2c_chan *i2c;
|
|
+ int ret;
|
|
+
|
|
+ i2c = kzalloc(sizeof(struct radeon_i2c_chan), GFP_KERNEL);
|
|
+ if (i2c == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ i2c->rec = *rec;
|
|
+ i2c->adapter.owner = THIS_MODULE;
|
|
+ i2c->dev = dev;
|
|
+ i2c_set_adapdata(&i2c->adapter, i2c);
|
|
+ i2c->adapter.algo_data = &i2c->algo.dp;
|
|
+ i2c->algo.dp.aux_ch = radeon_dp_i2c_aux_ch;
|
|
+ i2c->algo.dp.address = 0;
|
|
+ ret = i2c_dp_aux_add_bus(&i2c->adapter);
|
|
+ if (ret) {
|
|
+ DRM_INFO("Failed to register i2c %s\n", name);
|
|
+ goto out_free;
|
|
+ }
|
|
+
|
|
+ return i2c;
|
|
+out_free:
|
|
+ kfree(i2c);
|
|
+ return NULL;
|
|
+
|
|
+}
|
|
+
|
|
+
|
|
void radeon_i2c_destroy(struct radeon_i2c_chan *i2c)
|
|
{
|
|
if (!i2c)
|
|
@@ -207,3 +251,59 @@ struct drm_encoder *radeon_best_encoder(struct drm_connector *connector)
|
|
{
|
|
return NULL;
|
|
}
|
|
+
|
|
+void radeon_i2c_sw_get_byte(struct radeon_i2c_chan *i2c_bus,
|
|
+ u8 slave_addr,
|
|
+ u8 addr,
|
|
+ u8 *val)
|
|
+{
|
|
+ u8 out_buf[2];
|
|
+ u8 in_buf[2];
|
|
+ struct i2c_msg msgs[] = {
|
|
+ {
|
|
+ .addr = slave_addr,
|
|
+ .flags = 0,
|
|
+ .len = 1,
|
|
+ .buf = out_buf,
|
|
+ },
|
|
+ {
|
|
+ .addr = slave_addr,
|
|
+ .flags = I2C_M_RD,
|
|
+ .len = 1,
|
|
+ .buf = in_buf,
|
|
+ }
|
|
+ };
|
|
+
|
|
+ out_buf[0] = addr;
|
|
+ out_buf[1] = 0;
|
|
+
|
|
+ if (i2c_transfer(&i2c_bus->adapter, msgs, 2) == 2) {
|
|
+ *val = in_buf[0];
|
|
+ DRM_DEBUG("val = 0x%02x\n", *val);
|
|
+ } else {
|
|
+ DRM_ERROR("i2c 0x%02x 0x%02x read failed\n",
|
|
+ addr, *val);
|
|
+ }
|
|
+}
|
|
+
|
|
+void radeon_i2c_sw_put_byte(struct radeon_i2c_chan *i2c_bus,
|
|
+ u8 slave_addr,
|
|
+ u8 addr,
|
|
+ u8 val)
|
|
+{
|
|
+ uint8_t out_buf[2];
|
|
+ struct i2c_msg msg = {
|
|
+ .addr = slave_addr,
|
|
+ .flags = 0,
|
|
+ .len = 2,
|
|
+ .buf = out_buf,
|
|
+ };
|
|
+
|
|
+ out_buf[0] = addr;
|
|
+ out_buf[1] = val;
|
|
+
|
|
+ if (i2c_transfer(&i2c_bus->adapter, &msg, 1) != 1)
|
|
+ DRM_ERROR("i2c 0x%02x 0x%02x write failed\n",
|
|
+ addr, val);
|
|
+}
|
|
+
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_ioc32.c b/drivers/gpu/drm/radeon/radeon_ioc32.c
|
|
index a1bf11d..48b7cea 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_ioc32.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_ioc32.c
|
|
@@ -92,8 +92,7 @@ static int compat_radeon_cp_init(struct file *file, unsigned int cmd,
|
|
&init->gart_textures_offset))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_RADEON_CP_INIT, (unsigned long)init);
|
|
+ return drm_ioctl(file, DRM_IOCTL_RADEON_CP_INIT, (unsigned long)init);
|
|
}
|
|
|
|
typedef struct drm_radeon_clear32 {
|
|
@@ -125,8 +124,7 @@ static int compat_radeon_cp_clear(struct file *file, unsigned int cmd,
|
|
&clr->depth_boxes))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_RADEON_CLEAR, (unsigned long)clr);
|
|
+ return drm_ioctl(file, DRM_IOCTL_RADEON_CLEAR, (unsigned long)clr);
|
|
}
|
|
|
|
typedef struct drm_radeon_stipple32 {
|
|
@@ -149,8 +147,7 @@ static int compat_radeon_cp_stipple(struct file *file, unsigned int cmd,
|
|
&request->mask))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_RADEON_STIPPLE, (unsigned long)request);
|
|
+ return drm_ioctl(file, DRM_IOCTL_RADEON_STIPPLE, (unsigned long)request);
|
|
}
|
|
|
|
typedef struct drm_radeon_tex_image32 {
|
|
@@ -204,8 +201,7 @@ static int compat_radeon_cp_texture(struct file *file, unsigned int cmd,
|
|
&image->data))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_RADEON_TEXTURE, (unsigned long)request);
|
|
+ return drm_ioctl(file, DRM_IOCTL_RADEON_TEXTURE, (unsigned long)request);
|
|
}
|
|
|
|
typedef struct drm_radeon_vertex2_32 {
|
|
@@ -238,8 +234,7 @@ static int compat_radeon_cp_vertex2(struct file *file, unsigned int cmd,
|
|
&request->prim))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_RADEON_VERTEX2, (unsigned long)request);
|
|
+ return drm_ioctl(file, DRM_IOCTL_RADEON_VERTEX2, (unsigned long)request);
|
|
}
|
|
|
|
typedef struct drm_radeon_cmd_buffer32 {
|
|
@@ -268,8 +263,7 @@ static int compat_radeon_cp_cmdbuf(struct file *file, unsigned int cmd,
|
|
&request->boxes))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_RADEON_CMDBUF, (unsigned long)request);
|
|
+ return drm_ioctl(file, DRM_IOCTL_RADEON_CMDBUF, (unsigned long)request);
|
|
}
|
|
|
|
typedef struct drm_radeon_getparam32 {
|
|
@@ -293,8 +287,7 @@ static int compat_radeon_cp_getparam(struct file *file, unsigned int cmd,
|
|
&request->value))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_RADEON_GETPARAM, (unsigned long)request);
|
|
+ return drm_ioctl(file, DRM_IOCTL_RADEON_GETPARAM, (unsigned long)request);
|
|
}
|
|
|
|
typedef struct drm_radeon_mem_alloc32 {
|
|
@@ -322,8 +315,7 @@ static int compat_radeon_mem_alloc(struct file *file, unsigned int cmd,
|
|
&request->region_offset))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_RADEON_ALLOC, (unsigned long)request);
|
|
+ return drm_ioctl(file, DRM_IOCTL_RADEON_ALLOC, (unsigned long)request);
|
|
}
|
|
|
|
typedef struct drm_radeon_irq_emit32 {
|
|
@@ -345,8 +337,7 @@ static int compat_radeon_irq_emit(struct file *file, unsigned int cmd,
|
|
&request->irq_seq))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_path.dentry->d_inode, file,
|
|
- DRM_IOCTL_RADEON_IRQ_EMIT, (unsigned long)request);
|
|
+ return drm_ioctl(file, DRM_IOCTL_RADEON_IRQ_EMIT, (unsigned long)request);
|
|
}
|
|
|
|
/* The two 64-bit arches where alignof(u64)==4 in 32-bit code */
|
|
@@ -372,8 +363,7 @@ static int compat_radeon_cp_setparam(struct file *file, unsigned int cmd,
|
|
&request->value))
|
|
return -EFAULT;
|
|
|
|
- return drm_ioctl(file->f_dentry->d_inode, file,
|
|
- DRM_IOCTL_RADEON_SETPARAM, (unsigned long) request);
|
|
+ return drm_ioctl(file, DRM_IOCTL_RADEON_SETPARAM, (unsigned long) request);
|
|
}
|
|
#else
|
|
#define compat_radeon_cp_setparam NULL
|
|
@@ -413,12 +403,10 @@ long radeon_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|
if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(radeon_compat_ioctls))
|
|
fn = radeon_compat_ioctls[nr - DRM_COMMAND_BASE];
|
|
|
|
- lock_kernel(); /* XXX for now */
|
|
if (fn != NULL)
|
|
ret = (*fn) (filp, cmd, arg);
|
|
else
|
|
- ret = drm_ioctl(filp->f_path.dentry->d_inode, filp, cmd, arg);
|
|
- unlock_kernel();
|
|
+ ret = drm_ioctl(filp, cmd, arg);
|
|
|
|
return ret;
|
|
}
|
|
@@ -431,9 +419,7 @@ long radeon_kms_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long
|
|
if (nr < DRM_COMMAND_BASE)
|
|
return drm_compat_ioctl(filp, cmd, arg);
|
|
|
|
- lock_kernel(); /* XXX for now */
|
|
- ret = drm_ioctl(filp->f_path.dentry->d_inode, filp, cmd, arg);
|
|
- unlock_kernel();
|
|
+ ret = drm_ioctl(filp, cmd, arg);
|
|
|
|
return ret;
|
|
}
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_irq.c b/drivers/gpu/drm/radeon/radeon_irq.c
|
|
index b79ecc4..2f349a3 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_irq.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_irq.c
|
|
@@ -289,16 +289,16 @@ int radeon_irq_emit(struct drm_device *dev, void *data, struct drm_file *file_pr
|
|
drm_radeon_irq_emit_t *emit = data;
|
|
int result;
|
|
|
|
- if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600)
|
|
- return -EINVAL;
|
|
-
|
|
- LOCK_TEST_WITH_RETURN(dev, file_priv);
|
|
-
|
|
if (!dev_priv) {
|
|
DRM_ERROR("called with no initialization\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
+ if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600)
|
|
+ return -EINVAL;
|
|
+
|
|
+ LOCK_TEST_WITH_RETURN(dev, file_priv);
|
|
+
|
|
result = radeon_emit_irq(dev);
|
|
|
|
if (DRM_COPY_TO_USER(emit->irq_seq, &result, sizeof(int))) {
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c
|
|
index a0fe623..3cfd60f 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_irq_kms.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c
|
|
@@ -39,11 +39,32 @@ irqreturn_t radeon_driver_irq_handler_kms(DRM_IRQ_ARGS)
|
|
return radeon_irq_process(rdev);
|
|
}
|
|
|
|
+/*
|
|
+ * Handle hotplug events outside the interrupt handler proper.
|
|
+ */
|
|
+static void radeon_hotplug_work_func(struct work_struct *work)
|
|
+{
|
|
+ struct radeon_device *rdev = container_of(work, struct radeon_device,
|
|
+ hotplug_work);
|
|
+ struct drm_device *dev = rdev->ddev;
|
|
+ struct drm_mode_config *mode_config = &dev->mode_config;
|
|
+ struct drm_connector *connector;
|
|
+
|
|
+ if (mode_config->num_connector) {
|
|
+ list_for_each_entry(connector, &mode_config->connector_list, head)
|
|
+ radeon_connector_hotplug(connector);
|
|
+ }
|
|
+ /* Just fire off a uevent and let userspace tell us what to do */
|
|
+ drm_sysfs_hotplug_event(dev);
|
|
+}
|
|
+
|
|
void radeon_driver_irq_preinstall_kms(struct drm_device *dev)
|
|
{
|
|
struct radeon_device *rdev = dev->dev_private;
|
|
unsigned i;
|
|
|
|
+ INIT_WORK(&rdev->hotplug_work, radeon_hotplug_work_func);
|
|
+
|
|
/* Disable *all* interrupts */
|
|
rdev->irq.sw_int = false;
|
|
for (i = 0; i < 2; i++) {
|
|
@@ -76,6 +97,7 @@ void radeon_driver_irq_uninstall_kms(struct drm_device *dev)
|
|
rdev->irq.sw_int = false;
|
|
for (i = 0; i < 2; i++) {
|
|
rdev->irq.crtc_vblank_int[i] = false;
|
|
+ rdev->irq.hpd[i] = false;
|
|
}
|
|
radeon_irq_set(rdev);
|
|
}
|
|
@@ -87,30 +109,69 @@ int radeon_irq_kms_init(struct radeon_device *rdev)
|
|
|
|
if (rdev->flags & RADEON_SINGLE_CRTC)
|
|
num_crtc = 1;
|
|
-
|
|
+ spin_lock_init(&rdev->irq.sw_lock);
|
|
r = drm_vblank_init(rdev->ddev, num_crtc);
|
|
if (r) {
|
|
return r;
|
|
}
|
|
/* enable msi */
|
|
rdev->msi_enabled = 0;
|
|
- if (rdev->family >= CHIP_RV380) {
|
|
+ /* MSIs don't seem to work on my rs780;
|
|
+ * not sure about rs880 or other rs780s.
|
|
+ * Needs more investigation.
|
|
+ */
|
|
+ if ((rdev->family >= CHIP_RV380) &&
|
|
+ (rdev->family != CHIP_RS780) &&
|
|
+ (rdev->family != CHIP_RS880)) {
|
|
int ret = pci_enable_msi(rdev->pdev);
|
|
- if (!ret)
|
|
+ if (!ret) {
|
|
rdev->msi_enabled = 1;
|
|
+ DRM_INFO("radeon: using MSI.\n");
|
|
+ }
|
|
}
|
|
- drm_irq_install(rdev->ddev);
|
|
rdev->irq.installed = true;
|
|
+ r = drm_irq_install(rdev->ddev);
|
|
+ if (r) {
|
|
+ rdev->irq.installed = false;
|
|
+ return r;
|
|
+ }
|
|
DRM_INFO("radeon: irq initialized.\n");
|
|
return 0;
|
|
}
|
|
|
|
void radeon_irq_kms_fini(struct radeon_device *rdev)
|
|
{
|
|
+ drm_vblank_cleanup(rdev->ddev);
|
|
if (rdev->irq.installed) {
|
|
- rdev->irq.installed = false;
|
|
drm_irq_uninstall(rdev->ddev);
|
|
+ rdev->irq.installed = false;
|
|
if (rdev->msi_enabled)
|
|
pci_disable_msi(rdev->pdev);
|
|
}
|
|
}
|
|
+
|
|
+void radeon_irq_kms_sw_irq_get(struct radeon_device *rdev)
|
|
+{
|
|
+ unsigned long irqflags;
|
|
+
|
|
+ spin_lock_irqsave(&rdev->irq.sw_lock, irqflags);
|
|
+ if (rdev->ddev->irq_enabled && (++rdev->irq.sw_refcount == 1)) {
|
|
+ rdev->irq.sw_int = true;
|
|
+ radeon_irq_set(rdev);
|
|
+ }
|
|
+ spin_unlock_irqrestore(&rdev->irq.sw_lock, irqflags);
|
|
+}
|
|
+
|
|
+void radeon_irq_kms_sw_irq_put(struct radeon_device *rdev)
|
|
+{
|
|
+ unsigned long irqflags;
|
|
+
|
|
+ spin_lock_irqsave(&rdev->irq.sw_lock, irqflags);
|
|
+ BUG_ON(rdev->ddev->irq_enabled && rdev->irq.sw_refcount <= 0);
|
|
+ if (rdev->ddev->irq_enabled && (--rdev->irq.sw_refcount == 0)) {
|
|
+ rdev->irq.sw_int = false;
|
|
+ radeon_irq_set(rdev);
|
|
+ }
|
|
+ spin_unlock_irqrestore(&rdev->irq.sw_lock, irqflags);
|
|
+}
|
|
+
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c
|
|
index ba12862..f23b056 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_kms.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_kms.c
|
|
@@ -30,10 +30,19 @@
|
|
#include "radeon.h"
|
|
#include "radeon_drm.h"
|
|
|
|
+int radeon_driver_unload_kms(struct drm_device *dev)
|
|
+{
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
+
|
|
+ if (rdev == NULL)
|
|
+ return 0;
|
|
+ radeon_modeset_fini(rdev);
|
|
+ radeon_device_fini(rdev);
|
|
+ kfree(rdev);
|
|
+ dev->dev_private = NULL;
|
|
+ return 0;
|
|
+}
|
|
|
|
-/*
|
|
- * Driver load/unload
|
|
- */
|
|
int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags)
|
|
{
|
|
struct radeon_device *rdev;
|
|
@@ -62,31 +71,20 @@ int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags)
|
|
*/
|
|
r = radeon_device_init(rdev, dev, dev->pdev, flags);
|
|
if (r) {
|
|
- DRM_ERROR("Fatal error while trying to initialize radeon.\n");
|
|
- return r;
|
|
+ dev_err(&dev->pdev->dev, "Fatal error during GPU init\n");
|
|
+ goto out;
|
|
}
|
|
/* Again modeset_init should fail only on fatal error
|
|
* otherwise it should provide enough functionalities
|
|
* for shadowfb to run
|
|
*/
|
|
r = radeon_modeset_init(rdev);
|
|
- if (r) {
|
|
- return r;
|
|
- }
|
|
- return 0;
|
|
-}
|
|
-
|
|
-int radeon_driver_unload_kms(struct drm_device *dev)
|
|
-{
|
|
- struct radeon_device *rdev = dev->dev_private;
|
|
-
|
|
- if (rdev == NULL)
|
|
- return 0;
|
|
- radeon_modeset_fini(rdev);
|
|
- radeon_device_fini(rdev);
|
|
- kfree(rdev);
|
|
- dev->dev_private = NULL;
|
|
- return 0;
|
|
+ if (r)
|
|
+ dev_err(&dev->pdev->dev, "Fatal error during modeset init\n");
|
|
+out:
|
|
+ if (r)
|
|
+ radeon_driver_unload_kms(dev);
|
|
+ return r;
|
|
}
|
|
|
|
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
|
|
index 22ce4d6..d6d69bb 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
|
|
@@ -30,9 +30,20 @@
|
|
#include "radeon.h"
|
|
#include "atom.h"
|
|
|
|
+static void radeon_overscan_setup(struct drm_crtc *crtc,
|
|
+ struct drm_display_mode *mode)
|
|
+{
|
|
+ struct drm_device *dev = crtc->dev;
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
|
|
+
|
|
+ WREG32(RADEON_OVR_CLR + radeon_crtc->crtc_offset, 0);
|
|
+ WREG32(RADEON_OVR_WID_LEFT_RIGHT + radeon_crtc->crtc_offset, 0);
|
|
+ WREG32(RADEON_OVR_WID_TOP_BOTTOM + radeon_crtc->crtc_offset, 0);
|
|
+}
|
|
+
|
|
static void radeon_legacy_rmx_mode_set(struct drm_crtc *crtc,
|
|
- struct drm_display_mode *mode,
|
|
- struct drm_display_mode *adjusted_mode)
|
|
+ struct drm_display_mode *mode)
|
|
{
|
|
struct drm_device *dev = crtc->dev;
|
|
struct radeon_device *rdev = dev->dev_private;
|
|
@@ -328,69 +339,6 @@ void radeon_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|
}
|
|
}
|
|
|
|
-/* properly set crtc bpp when using atombios */
|
|
-void radeon_legacy_atom_set_surface(struct drm_crtc *crtc)
|
|
-{
|
|
- struct drm_device *dev = crtc->dev;
|
|
- struct radeon_device *rdev = dev->dev_private;
|
|
- struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
|
|
- int format;
|
|
- uint32_t crtc_gen_cntl;
|
|
- uint32_t disp_merge_cntl;
|
|
- uint32_t crtc_pitch;
|
|
-
|
|
- switch (crtc->fb->bits_per_pixel) {
|
|
- case 8:
|
|
- format = 2;
|
|
- break;
|
|
- case 15: /* 555 */
|
|
- format = 3;
|
|
- break;
|
|
- case 16: /* 565 */
|
|
- format = 4;
|
|
- break;
|
|
- case 24: /* RGB */
|
|
- format = 5;
|
|
- break;
|
|
- case 32: /* xRGB */
|
|
- format = 6;
|
|
- break;
|
|
- default:
|
|
- return;
|
|
- }
|
|
-
|
|
- crtc_pitch = ((((crtc->fb->pitch / (crtc->fb->bits_per_pixel / 8)) * crtc->fb->bits_per_pixel) +
|
|
- ((crtc->fb->bits_per_pixel * 8) - 1)) /
|
|
- (crtc->fb->bits_per_pixel * 8));
|
|
- crtc_pitch |= crtc_pitch << 16;
|
|
-
|
|
- WREG32(RADEON_CRTC_PITCH + radeon_crtc->crtc_offset, crtc_pitch);
|
|
-
|
|
- switch (radeon_crtc->crtc_id) {
|
|
- case 0:
|
|
- disp_merge_cntl = RREG32(RADEON_DISP_MERGE_CNTL);
|
|
- disp_merge_cntl &= ~RADEON_DISP_RGB_OFFSET_EN;
|
|
- WREG32(RADEON_DISP_MERGE_CNTL, disp_merge_cntl);
|
|
-
|
|
- crtc_gen_cntl = RREG32(RADEON_CRTC_GEN_CNTL) & 0xfffff0ff;
|
|
- crtc_gen_cntl |= (format << 8);
|
|
- crtc_gen_cntl |= RADEON_CRTC_EXT_DISP_EN;
|
|
- WREG32(RADEON_CRTC_GEN_CNTL, crtc_gen_cntl);
|
|
- break;
|
|
- case 1:
|
|
- disp_merge_cntl = RREG32(RADEON_DISP2_MERGE_CNTL);
|
|
- disp_merge_cntl &= ~RADEON_DISP2_RGB_OFFSET_EN;
|
|
- WREG32(RADEON_DISP2_MERGE_CNTL, disp_merge_cntl);
|
|
-
|
|
- crtc_gen_cntl = RREG32(RADEON_CRTC2_GEN_CNTL) & 0xfffff0ff;
|
|
- crtc_gen_cntl |= (format << 8);
|
|
- WREG32(RADEON_CRTC2_GEN_CNTL, crtc_gen_cntl);
|
|
- WREG32(RADEON_FP_H2_SYNC_STRT_WID, RREG32(RADEON_CRTC2_H_SYNC_STRT_WID));
|
|
- WREG32(RADEON_FP_V2_SYNC_STRT_WID, RREG32(RADEON_CRTC2_V_SYNC_STRT_WID));
|
|
- break;
|
|
- }
|
|
-}
|
|
-
|
|
int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y,
|
|
struct drm_framebuffer *old_fb)
|
|
{
|
|
@@ -399,14 +347,21 @@ int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y,
|
|
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
|
|
struct radeon_framebuffer *radeon_fb;
|
|
struct drm_gem_object *obj;
|
|
+ struct radeon_bo *rbo;
|
|
uint64_t base;
|
|
uint32_t crtc_offset, crtc_offset_cntl, crtc_tile_x0_y0 = 0;
|
|
uint32_t crtc_pitch, pitch_pixels;
|
|
uint32_t tiling_flags;
|
|
int format;
|
|
uint32_t gen_cntl_reg, gen_cntl_val;
|
|
+ int r;
|
|
|
|
DRM_DEBUG("\n");
|
|
+ /* no fb bound */
|
|
+ if (!crtc->fb) {
|
|
+ DRM_DEBUG("No FB bound\n");
|
|
+ return 0;
|
|
+ }
|
|
|
|
radeon_fb = to_radeon_framebuffer(crtc->fb);
|
|
|
|
@@ -430,10 +385,22 @@ int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y,
|
|
return false;
|
|
}
|
|
|
|
+ /* Pin framebuffer & get tilling informations */
|
|
obj = radeon_fb->obj;
|
|
- if (radeon_gem_object_pin(obj, RADEON_GEM_DOMAIN_VRAM, &base)) {
|
|
+ rbo = obj->driver_private;
|
|
+ r = radeon_bo_reserve(rbo, false);
|
|
+ if (unlikely(r != 0))
|
|
+ return r;
|
|
+ r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &base);
|
|
+ if (unlikely(r != 0)) {
|
|
+ radeon_bo_unreserve(rbo);
|
|
return -EINVAL;
|
|
}
|
|
+ radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL);
|
|
+ radeon_bo_unreserve(rbo);
|
|
+ if (tiling_flags & RADEON_TILING_MICRO)
|
|
+ DRM_ERROR("trying to scanout microtiled buffer\n");
|
|
+
|
|
/* if scanout was in GTT this really wouldn't work */
|
|
/* crtc offset is from display base addr not FB location */
|
|
radeon_crtc->legacy_display_base_addr = rdev->mc.vram_location;
|
|
@@ -448,10 +415,6 @@ int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y,
|
|
(crtc->fb->bits_per_pixel * 8));
|
|
crtc_pitch |= crtc_pitch << 16;
|
|
|
|
- radeon_object_get_tiling_flags(obj->driver_private,
|
|
- &tiling_flags, NULL);
|
|
- if (tiling_flags & RADEON_TILING_MICRO)
|
|
- DRM_ERROR("trying to scanout microtiled buffer\n");
|
|
|
|
if (tiling_flags & RADEON_TILING_MACRO) {
|
|
if (ASIC_IS_R300(rdev))
|
|
@@ -529,7 +492,12 @@ int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y,
|
|
|
|
if (old_fb && old_fb != crtc->fb) {
|
|
radeon_fb = to_radeon_framebuffer(old_fb);
|
|
- radeon_gem_object_unpin(radeon_fb->obj);
|
|
+ rbo = radeon_fb->obj->driver_private;
|
|
+ r = radeon_bo_reserve(rbo, false);
|
|
+ if (unlikely(r != 0))
|
|
+ return r;
|
|
+ radeon_bo_unpin(rbo);
|
|
+ radeon_bo_unreserve(rbo);
|
|
}
|
|
|
|
/* Bytes per pixel may have changed */
|
|
@@ -614,39 +582,12 @@ static bool radeon_set_crtc_timing(struct drm_crtc *crtc, struct drm_display_mod
|
|
? RADEON_CRTC_V_SYNC_POL
|
|
: 0));
|
|
|
|
- /* TODO -> Dell Server */
|
|
- if (0) {
|
|
- uint32_t disp_hw_debug = RREG32(RADEON_DISP_HW_DEBUG);
|
|
- uint32_t tv_dac_cntl = RREG32(RADEON_TV_DAC_CNTL);
|
|
- uint32_t dac2_cntl = RREG32(RADEON_DAC_CNTL2);
|
|
- uint32_t crtc2_gen_cntl = RREG32(RADEON_CRTC2_GEN_CNTL);
|
|
-
|
|
- dac2_cntl &= ~RADEON_DAC2_DAC_CLK_SEL;
|
|
- dac2_cntl |= RADEON_DAC2_DAC2_CLK_SEL;
|
|
-
|
|
- /* For CRT on DAC2, don't turn it on if BIOS didn't
|
|
- enable it, even it's detected.
|
|
- */
|
|
- disp_hw_debug |= RADEON_CRT2_DISP1_SEL;
|
|
- tv_dac_cntl &= ~((1<<2) | (3<<8) | (7<<24) | (0xff<<16));
|
|
- tv_dac_cntl |= (0x03 | (2<<8) | (0x58<<16));
|
|
-
|
|
- WREG32(RADEON_TV_DAC_CNTL, tv_dac_cntl);
|
|
- WREG32(RADEON_DISP_HW_DEBUG, disp_hw_debug);
|
|
- WREG32(RADEON_DAC_CNTL2, dac2_cntl);
|
|
- WREG32(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl);
|
|
- }
|
|
-
|
|
if (radeon_crtc->crtc_id) {
|
|
uint32_t crtc2_gen_cntl;
|
|
uint32_t disp2_merge_cntl;
|
|
|
|
- /* check to see if TV DAC is enabled for another crtc and keep it enabled */
|
|
- if (RREG32(RADEON_CRTC2_GEN_CNTL) & RADEON_CRTC2_CRT2_ON)
|
|
- crtc2_gen_cntl = RADEON_CRTC2_CRT2_ON;
|
|
- else
|
|
- crtc2_gen_cntl = 0;
|
|
-
|
|
+ /* if TV DAC is enabled for another crtc and keep it enabled */
|
|
+ crtc2_gen_cntl = RREG32(RADEON_CRTC2_GEN_CNTL) & 0x00718080;
|
|
crtc2_gen_cntl |= ((format << 8)
|
|
| RADEON_CRTC2_VSYNC_DIS
|
|
| RADEON_CRTC2_HSYNC_DIS
|
|
@@ -675,7 +616,8 @@ static bool radeon_set_crtc_timing(struct drm_crtc *crtc, struct drm_display_mod
|
|
uint32_t crtc_ext_cntl;
|
|
uint32_t disp_merge_cntl;
|
|
|
|
- crtc_gen_cntl = (RADEON_CRTC_EXT_DISP_EN
|
|
+ crtc_gen_cntl = RREG32(RADEON_CRTC_GEN_CNTL) & 0x00718000;
|
|
+ crtc_gen_cntl |= (RADEON_CRTC_EXT_DISP_EN
|
|
| (format << 8)
|
|
| RADEON_CRTC_DISP_REQ_EN_B
|
|
| ((mode->flags & DRM_MODE_FLAG_DBLSCAN)
|
|
@@ -727,7 +669,6 @@ static void radeon_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
|
|
uint32_t post_divider = 0;
|
|
uint32_t freq = 0;
|
|
uint8_t pll_gain;
|
|
- int pll_flags = RADEON_PLL_LEGACY;
|
|
bool use_bios_divs = false;
|
|
/* PLL registers */
|
|
uint32_t pll_ref_div = 0;
|
|
@@ -761,10 +702,12 @@ static void radeon_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
|
|
else
|
|
pll = &rdev->clock.p1pll;
|
|
|
|
+ pll->flags = RADEON_PLL_LEGACY;
|
|
+
|
|
if (mode->clock > 200000) /* range limits??? */
|
|
- pll_flags |= RADEON_PLL_PREFER_HIGH_FB_DIV;
|
|
+ pll->flags |= RADEON_PLL_PREFER_HIGH_FB_DIV;
|
|
else
|
|
- pll_flags |= RADEON_PLL_PREFER_LOW_REF_DIV;
|
|
+ pll->flags |= RADEON_PLL_PREFER_LOW_REF_DIV;
|
|
|
|
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
|
if (encoder->crtc == crtc) {
|
|
@@ -776,20 +719,22 @@ static void radeon_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
|
|
}
|
|
|
|
if (encoder->encoder_type != DRM_MODE_ENCODER_DAC)
|
|
- pll_flags |= RADEON_PLL_NO_ODD_POST_DIV;
|
|
+ pll->flags |= RADEON_PLL_NO_ODD_POST_DIV;
|
|
if (encoder->encoder_type == DRM_MODE_ENCODER_LVDS) {
|
|
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
|
|
- struct radeon_encoder_lvds *lvds = (struct radeon_encoder_lvds *)radeon_encoder->enc_priv;
|
|
- if (lvds) {
|
|
- if (lvds->use_bios_dividers) {
|
|
- pll_ref_div = lvds->panel_ref_divider;
|
|
- pll_fb_post_div = (lvds->panel_fb_divider |
|
|
- (lvds->panel_post_divider << 16));
|
|
- htotal_cntl = 0;
|
|
- use_bios_divs = true;
|
|
+ if (!rdev->is_atom_bios) {
|
|
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
|
|
+ struct radeon_encoder_lvds *lvds = (struct radeon_encoder_lvds *)radeon_encoder->enc_priv;
|
|
+ if (lvds) {
|
|
+ if (lvds->use_bios_dividers) {
|
|
+ pll_ref_div = lvds->panel_ref_divider;
|
|
+ pll_fb_post_div = (lvds->panel_fb_divider |
|
|
+ (lvds->panel_post_divider << 16));
|
|
+ htotal_cntl = 0;
|
|
+ use_bios_divs = true;
|
|
+ }
|
|
}
|
|
}
|
|
- pll_flags |= RADEON_PLL_USE_REF_DIV;
|
|
+ pll->flags |= RADEON_PLL_USE_REF_DIV;
|
|
}
|
|
}
|
|
}
|
|
@@ -799,8 +744,7 @@ static void radeon_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
|
|
if (!use_bios_divs) {
|
|
radeon_compute_pll(pll, mode->clock,
|
|
&freq, &feedback_div, &frac_fb_div,
|
|
- &reference_div, &post_divider,
|
|
- pll_flags);
|
|
+ &reference_div, &post_divider);
|
|
|
|
for (post_div = &post_divs[0]; post_div->divider; ++post_div) {
|
|
if (post_div->divider == post_divider)
|
|
@@ -1026,8 +970,9 @@ static int radeon_crtc_mode_set(struct drm_crtc *crtc,
|
|
radeon_crtc_set_base(crtc, x, y, old_fb);
|
|
radeon_set_crtc_timing(crtc, adjusted_mode);
|
|
radeon_set_pll(crtc, adjusted_mode);
|
|
+ radeon_overscan_setup(crtc, adjusted_mode);
|
|
if (radeon_crtc->crtc_id == 0) {
|
|
- radeon_legacy_rmx_mode_set(crtc, mode, adjusted_mode);
|
|
+ radeon_legacy_rmx_mode_set(crtc, adjusted_mode);
|
|
} else {
|
|
if (radeon_crtc->rmx_type != RMX_OFF) {
|
|
/* FIXME: only first crtc has rmx what should we
|
|
@@ -1041,12 +986,29 @@ static int radeon_crtc_mode_set(struct drm_crtc *crtc,
|
|
|
|
static void radeon_crtc_prepare(struct drm_crtc *crtc)
|
|
{
|
|
- radeon_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
|
|
+ struct drm_device *dev = crtc->dev;
|
|
+ struct drm_crtc *crtci;
|
|
+
|
|
+ /*
|
|
+ * The hardware wedges sometimes if you reconfigure one CRTC
|
|
+ * whilst another is running (see fdo bug #24611).
|
|
+ */
|
|
+ list_for_each_entry(crtci, &dev->mode_config.crtc_list, head)
|
|
+ radeon_crtc_dpms(crtci, DRM_MODE_DPMS_OFF);
|
|
}
|
|
|
|
static void radeon_crtc_commit(struct drm_crtc *crtc)
|
|
{
|
|
- radeon_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
|
|
+ struct drm_device *dev = crtc->dev;
|
|
+ struct drm_crtc *crtci;
|
|
+
|
|
+ /*
|
|
+ * Reenable the CRTCs that should be running.
|
|
+ */
|
|
+ list_for_each_entry(crtci, &dev->mode_config.crtc_list, head) {
|
|
+ if (crtci->enabled)
|
|
+ radeon_crtc_dpms(crtci, DRM_MODE_DPMS_ON);
|
|
+ }
|
|
}
|
|
|
|
static const struct drm_crtc_helper_funcs legacy_helper_funcs = {
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c
|
|
index 0038212..38e45e2 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c
|
|
@@ -46,6 +46,7 @@ static void radeon_legacy_lvds_dpms(struct drm_encoder *encoder, int mode)
|
|
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
|
|
uint32_t lvds_gen_cntl, lvds_pll_cntl, pixclks_cntl, disp_pwr_man;
|
|
int panel_pwr_delay = 2000;
|
|
+ bool is_mac = false;
|
|
DRM_DEBUG("\n");
|
|
|
|
if (radeon_encoder->enc_priv) {
|
|
@@ -58,6 +59,15 @@ static void radeon_legacy_lvds_dpms(struct drm_encoder *encoder, int mode)
|
|
}
|
|
}
|
|
|
|
+ /* macs (and possibly some x86 oem systems?) wire up LVDS strangely
|
|
+ * Taken from radeonfb.
|
|
+ */
|
|
+ if ((rdev->mode_info.connector_table == CT_IBOOK) ||
|
|
+ (rdev->mode_info.connector_table == CT_POWERBOOK_EXTERNAL) ||
|
|
+ (rdev->mode_info.connector_table == CT_POWERBOOK_INTERNAL) ||
|
|
+ (rdev->mode_info.connector_table == CT_POWERBOOK_VGA))
|
|
+ is_mac = true;
|
|
+
|
|
switch (mode) {
|
|
case DRM_MODE_DPMS_ON:
|
|
disp_pwr_man = RREG32(RADEON_DISP_PWR_MAN);
|
|
@@ -74,6 +84,8 @@ static void radeon_legacy_lvds_dpms(struct drm_encoder *encoder, int mode)
|
|
|
|
lvds_gen_cntl = RREG32(RADEON_LVDS_GEN_CNTL);
|
|
lvds_gen_cntl |= (RADEON_LVDS_ON | RADEON_LVDS_EN | RADEON_LVDS_DIGON | RADEON_LVDS_BLON);
|
|
+ if (is_mac)
|
|
+ lvds_gen_cntl |= RADEON_LVDS_BL_MOD_EN;
|
|
lvds_gen_cntl &= ~(RADEON_LVDS_DISPLAY_DIS);
|
|
udelay(panel_pwr_delay * 1000);
|
|
WREG32(RADEON_LVDS_GEN_CNTL, lvds_gen_cntl);
|
|
@@ -85,7 +97,14 @@ static void radeon_legacy_lvds_dpms(struct drm_encoder *encoder, int mode)
|
|
WREG32_PLL_P(RADEON_PIXCLKS_CNTL, 0, ~RADEON_PIXCLK_LVDS_ALWAYS_ONb);
|
|
lvds_gen_cntl = RREG32(RADEON_LVDS_GEN_CNTL);
|
|
lvds_gen_cntl |= RADEON_LVDS_DISPLAY_DIS;
|
|
- lvds_gen_cntl &= ~(RADEON_LVDS_ON | RADEON_LVDS_BLON | RADEON_LVDS_EN | RADEON_LVDS_DIGON);
|
|
+ if (is_mac) {
|
|
+ lvds_gen_cntl &= ~RADEON_LVDS_BL_MOD_EN;
|
|
+ WREG32(RADEON_LVDS_GEN_CNTL, lvds_gen_cntl);
|
|
+ lvds_gen_cntl &= ~(RADEON_LVDS_ON | RADEON_LVDS_EN);
|
|
+ } else {
|
|
+ WREG32(RADEON_LVDS_GEN_CNTL, lvds_gen_cntl);
|
|
+ lvds_gen_cntl &= ~(RADEON_LVDS_ON | RADEON_LVDS_BLON | RADEON_LVDS_EN | RADEON_LVDS_DIGON);
|
|
+ }
|
|
udelay(panel_pwr_delay * 1000);
|
|
WREG32(RADEON_LVDS_GEN_CNTL, lvds_gen_cntl);
|
|
WREG32_PLL(RADEON_PIXCLKS_CNTL, pixclks_cntl);
|
|
@@ -136,7 +155,14 @@ static void radeon_legacy_lvds_mode_set(struct drm_encoder *encoder,
|
|
lvds_pll_cntl &= ~RADEON_LVDS_PLL_EN;
|
|
|
|
lvds_ss_gen_cntl = RREG32(RADEON_LVDS_SS_GEN_CNTL);
|
|
- if ((!rdev->is_atom_bios)) {
|
|
+ if (rdev->is_atom_bios) {
|
|
+ /* LVDS_GEN_CNTL parameters are computed in LVDSEncoderControl
|
|
+ * need to call that on resume to set up the reg properly.
|
|
+ */
|
|
+ radeon_encoder->pixel_clock = adjusted_mode->clock;
|
|
+ atombios_digital_setup(encoder, PANEL_ENCODER_ACTION_ENABLE);
|
|
+ lvds_gen_cntl = RREG32(RADEON_LVDS_GEN_CNTL);
|
|
+ } else {
|
|
struct radeon_encoder_lvds *lvds = (struct radeon_encoder_lvds *)radeon_encoder->enc_priv;
|
|
if (lvds) {
|
|
DRM_DEBUG("bios LVDS_GEN_CNTL: 0x%x\n", lvds->lvds_gen_cntl);
|
|
@@ -147,8 +173,7 @@ static void radeon_legacy_lvds_mode_set(struct drm_encoder *encoder,
|
|
(lvds->panel_blon_delay << RADEON_LVDS_PWRSEQ_DELAY2_SHIFT));
|
|
} else
|
|
lvds_gen_cntl = RREG32(RADEON_LVDS_GEN_CNTL);
|
|
- } else
|
|
- lvds_gen_cntl = RREG32(RADEON_LVDS_GEN_CNTL);
|
|
+ }
|
|
lvds_gen_cntl |= RADEON_LVDS_DISPLAY_DIS;
|
|
lvds_gen_cntl &= ~(RADEON_LVDS_ON |
|
|
RADEON_LVDS_BLON |
|
|
@@ -184,9 +209,9 @@ static void radeon_legacy_lvds_mode_set(struct drm_encoder *encoder,
|
|
radeon_combios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id);
|
|
}
|
|
|
|
-static bool radeon_legacy_lvds_mode_fixup(struct drm_encoder *encoder,
|
|
- struct drm_display_mode *mode,
|
|
- struct drm_display_mode *adjusted_mode)
|
|
+static bool radeon_legacy_mode_fixup(struct drm_encoder *encoder,
|
|
+ struct drm_display_mode *mode,
|
|
+ struct drm_display_mode *adjusted_mode)
|
|
{
|
|
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
|
|
|
|
@@ -194,15 +219,24 @@ static bool radeon_legacy_lvds_mode_fixup(struct drm_encoder *encoder,
|
|
radeon_encoder_set_active_device(encoder);
|
|
drm_mode_set_crtcinfo(adjusted_mode, 0);
|
|
|
|
- if (radeon_encoder->rmx_type != RMX_OFF)
|
|
- radeon_rmx_mode_fixup(encoder, mode, adjusted_mode);
|
|
+ /* get the native mode for LVDS */
|
|
+ if (radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
|
|
+ struct drm_display_mode *native_mode = &radeon_encoder->native_mode;
|
|
+ int mode_id = adjusted_mode->base.id;
|
|
+ *adjusted_mode = *native_mode;
|
|
+ adjusted_mode->hdisplay = mode->hdisplay;
|
|
+ adjusted_mode->vdisplay = mode->vdisplay;
|
|
+ adjusted_mode->crtc_hdisplay = mode->hdisplay;
|
|
+ adjusted_mode->crtc_vdisplay = mode->vdisplay;
|
|
+ adjusted_mode->base.id = mode_id;
|
|
+ }
|
|
|
|
return true;
|
|
}
|
|
|
|
static const struct drm_encoder_helper_funcs radeon_legacy_lvds_helper_funcs = {
|
|
.dpms = radeon_legacy_lvds_dpms,
|
|
- .mode_fixup = radeon_legacy_lvds_mode_fixup,
|
|
+ .mode_fixup = radeon_legacy_mode_fixup,
|
|
.prepare = radeon_legacy_lvds_prepare,
|
|
.mode_set = radeon_legacy_lvds_mode_set,
|
|
.commit = radeon_legacy_lvds_commit,
|
|
@@ -214,17 +248,6 @@ static const struct drm_encoder_funcs radeon_legacy_lvds_enc_funcs = {
|
|
.destroy = radeon_enc_destroy,
|
|
};
|
|
|
|
-static bool radeon_legacy_primary_dac_mode_fixup(struct drm_encoder *encoder,
|
|
- struct drm_display_mode *mode,
|
|
- struct drm_display_mode *adjusted_mode)
|
|
-{
|
|
- /* set the active encoder to connector routing */
|
|
- radeon_encoder_set_active_device(encoder);
|
|
- drm_mode_set_crtcinfo(adjusted_mode, 0);
|
|
-
|
|
- return true;
|
|
-}
|
|
-
|
|
static void radeon_legacy_primary_dac_dpms(struct drm_encoder *encoder, int mode)
|
|
{
|
|
struct drm_device *dev = encoder->dev;
|
|
@@ -410,7 +433,7 @@ static enum drm_connector_status radeon_legacy_primary_dac_detect(struct drm_enc
|
|
|
|
static const struct drm_encoder_helper_funcs radeon_legacy_primary_dac_helper_funcs = {
|
|
.dpms = radeon_legacy_primary_dac_dpms,
|
|
- .mode_fixup = radeon_legacy_primary_dac_mode_fixup,
|
|
+ .mode_fixup = radeon_legacy_mode_fixup,
|
|
.prepare = radeon_legacy_primary_dac_prepare,
|
|
.mode_set = radeon_legacy_primary_dac_mode_set,
|
|
.commit = radeon_legacy_primary_dac_commit,
|
|
@@ -423,16 +446,6 @@ static const struct drm_encoder_funcs radeon_legacy_primary_dac_enc_funcs = {
|
|
.destroy = radeon_enc_destroy,
|
|
};
|
|
|
|
-static bool radeon_legacy_tmds_int_mode_fixup(struct drm_encoder *encoder,
|
|
- struct drm_display_mode *mode,
|
|
- struct drm_display_mode *adjusted_mode)
|
|
-{
|
|
-
|
|
- drm_mode_set_crtcinfo(adjusted_mode, 0);
|
|
-
|
|
- return true;
|
|
-}
|
|
-
|
|
static void radeon_legacy_tmds_int_dpms(struct drm_encoder *encoder, int mode)
|
|
{
|
|
struct drm_device *dev = encoder->dev;
|
|
@@ -584,7 +597,7 @@ static void radeon_legacy_tmds_int_mode_set(struct drm_encoder *encoder,
|
|
|
|
static const struct drm_encoder_helper_funcs radeon_legacy_tmds_int_helper_funcs = {
|
|
.dpms = radeon_legacy_tmds_int_dpms,
|
|
- .mode_fixup = radeon_legacy_tmds_int_mode_fixup,
|
|
+ .mode_fixup = radeon_legacy_mode_fixup,
|
|
.prepare = radeon_legacy_tmds_int_prepare,
|
|
.mode_set = radeon_legacy_tmds_int_mode_set,
|
|
.commit = radeon_legacy_tmds_int_commit,
|
|
@@ -596,17 +609,6 @@ static const struct drm_encoder_funcs radeon_legacy_tmds_int_enc_funcs = {
|
|
.destroy = radeon_enc_destroy,
|
|
};
|
|
|
|
-static bool radeon_legacy_tmds_ext_mode_fixup(struct drm_encoder *encoder,
|
|
- struct drm_display_mode *mode,
|
|
- struct drm_display_mode *adjusted_mode)
|
|
-{
|
|
- /* set the active encoder to connector routing */
|
|
- radeon_encoder_set_active_device(encoder);
|
|
- drm_mode_set_crtcinfo(adjusted_mode, 0);
|
|
-
|
|
- return true;
|
|
-}
|
|
-
|
|
static void radeon_legacy_tmds_ext_dpms(struct drm_encoder *encoder, int mode)
|
|
{
|
|
struct drm_device *dev = encoder->dev;
|
|
@@ -697,6 +699,8 @@ static void radeon_legacy_tmds_ext_mode_set(struct drm_encoder *encoder,
|
|
/*if (mode->clock > 165000)
|
|
fp2_gen_cntl |= R300_FP2_DVO_DUAL_CHANNEL_EN;*/
|
|
}
|
|
+ if (!radeon_combios_external_tmds_setup(encoder))
|
|
+ radeon_external_tmds_setup(encoder);
|
|
}
|
|
|
|
if (radeon_crtc->crtc_id == 0) {
|
|
@@ -724,9 +728,22 @@ static void radeon_legacy_tmds_ext_mode_set(struct drm_encoder *encoder,
|
|
radeon_combios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id);
|
|
}
|
|
|
|
+static void radeon_ext_tmds_enc_destroy(struct drm_encoder *encoder)
|
|
+{
|
|
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
|
|
+ struct radeon_encoder_ext_tmds *tmds = radeon_encoder->enc_priv;
|
|
+ if (tmds) {
|
|
+ if (tmds->i2c_bus)
|
|
+ radeon_i2c_destroy(tmds->i2c_bus);
|
|
+ }
|
|
+ kfree(radeon_encoder->enc_priv);
|
|
+ drm_encoder_cleanup(encoder);
|
|
+ kfree(radeon_encoder);
|
|
+}
|
|
+
|
|
static const struct drm_encoder_helper_funcs radeon_legacy_tmds_ext_helper_funcs = {
|
|
.dpms = radeon_legacy_tmds_ext_dpms,
|
|
- .mode_fixup = radeon_legacy_tmds_ext_mode_fixup,
|
|
+ .mode_fixup = radeon_legacy_mode_fixup,
|
|
.prepare = radeon_legacy_tmds_ext_prepare,
|
|
.mode_set = radeon_legacy_tmds_ext_mode_set,
|
|
.commit = radeon_legacy_tmds_ext_commit,
|
|
@@ -735,20 +752,9 @@ static const struct drm_encoder_helper_funcs radeon_legacy_tmds_ext_helper_funcs
|
|
|
|
|
|
static const struct drm_encoder_funcs radeon_legacy_tmds_ext_enc_funcs = {
|
|
- .destroy = radeon_enc_destroy,
|
|
+ .destroy = radeon_ext_tmds_enc_destroy,
|
|
};
|
|
|
|
-static bool radeon_legacy_tv_dac_mode_fixup(struct drm_encoder *encoder,
|
|
- struct drm_display_mode *mode,
|
|
- struct drm_display_mode *adjusted_mode)
|
|
-{
|
|
- /* set the active encoder to connector routing */
|
|
- radeon_encoder_set_active_device(encoder);
|
|
- drm_mode_set_crtcinfo(adjusted_mode, 0);
|
|
-
|
|
- return true;
|
|
-}
|
|
-
|
|
static void radeon_legacy_tv_dac_dpms(struct drm_encoder *encoder, int mode)
|
|
{
|
|
struct drm_device *dev = encoder->dev;
|
|
@@ -1265,7 +1271,7 @@ static enum drm_connector_status radeon_legacy_tv_dac_detect(struct drm_encoder
|
|
|
|
static const struct drm_encoder_helper_funcs radeon_legacy_tv_dac_helper_funcs = {
|
|
.dpms = radeon_legacy_tv_dac_dpms,
|
|
- .mode_fixup = radeon_legacy_tv_dac_mode_fixup,
|
|
+ .mode_fixup = radeon_legacy_mode_fixup,
|
|
.prepare = radeon_legacy_tv_dac_prepare,
|
|
.mode_set = radeon_legacy_tv_dac_mode_set,
|
|
.commit = radeon_legacy_tv_dac_commit,
|
|
@@ -1302,6 +1308,29 @@ static struct radeon_encoder_int_tmds *radeon_legacy_get_tmds_info(struct radeon
|
|
return tmds;
|
|
}
|
|
|
|
+static struct radeon_encoder_ext_tmds *radeon_legacy_get_ext_tmds_info(struct radeon_encoder *encoder)
|
|
+{
|
|
+ struct drm_device *dev = encoder->base.dev;
|
|
+ struct radeon_device *rdev = dev->dev_private;
|
|
+ struct radeon_encoder_ext_tmds *tmds = NULL;
|
|
+ bool ret;
|
|
+
|
|
+ if (rdev->is_atom_bios)
|
|
+ return NULL;
|
|
+
|
|
+ tmds = kzalloc(sizeof(struct radeon_encoder_ext_tmds), GFP_KERNEL);
|
|
+
|
|
+ if (!tmds)
|
|
+ return NULL;
|
|
+
|
|
+ ret = radeon_legacy_get_ext_tmds_info_from_combios(encoder, tmds);
|
|
+
|
|
+ if (ret == false)
|
|
+ radeon_legacy_get_ext_tmds_info_from_table(encoder, tmds);
|
|
+
|
|
+ return tmds;
|
|
+}
|
|
+
|
|
void
|
|
radeon_add_legacy_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t supported_device)
|
|
{
|
|
@@ -1329,7 +1358,6 @@ radeon_add_legacy_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t
|
|
encoder->possible_crtcs = 0x1;
|
|
else
|
|
encoder->possible_crtcs = 0x3;
|
|
- encoder->possible_clones = 0;
|
|
|
|
radeon_encoder->enc_priv = NULL;
|
|
|
|
@@ -1373,7 +1401,7 @@ radeon_add_legacy_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t
|
|
drm_encoder_init(dev, encoder, &radeon_legacy_tmds_ext_enc_funcs, DRM_MODE_ENCODER_TMDS);
|
|
drm_encoder_helper_add(encoder, &radeon_legacy_tmds_ext_helper_funcs);
|
|
if (!rdev->is_atom_bios)
|
|
- radeon_combios_get_ext_tmds_info(radeon_encoder);
|
|
+ radeon_encoder->enc_priv = radeon_legacy_get_ext_tmds_info(radeon_encoder);
|
|
break;
|
|
}
|
|
}
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_legacy_tv.c b/drivers/gpu/drm/radeon/radeon_legacy_tv.c
|
|
index 3a12bb0..417684d 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_legacy_tv.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_legacy_tv.c
|
|
@@ -77,7 +77,7 @@ struct radeon_tv_mode_constants {
|
|
unsigned pix_to_tv;
|
|
};
|
|
|
|
-static const uint16_t hor_timing_NTSC[] = {
|
|
+static const uint16_t hor_timing_NTSC[MAX_H_CODE_TIMING_LEN] = {
|
|
0x0007,
|
|
0x003f,
|
|
0x0263,
|
|
@@ -98,7 +98,7 @@ static const uint16_t hor_timing_NTSC[] = {
|
|
0
|
|
};
|
|
|
|
-static const uint16_t vert_timing_NTSC[] = {
|
|
+static const uint16_t vert_timing_NTSC[MAX_V_CODE_TIMING_LEN] = {
|
|
0x2001,
|
|
0x200d,
|
|
0x1006,
|
|
@@ -115,7 +115,7 @@ static const uint16_t vert_timing_NTSC[] = {
|
|
0
|
|
};
|
|
|
|
-static const uint16_t hor_timing_PAL[] = {
|
|
+static const uint16_t hor_timing_PAL[MAX_H_CODE_TIMING_LEN] = {
|
|
0x0007,
|
|
0x0058,
|
|
0x027c,
|
|
@@ -136,7 +136,7 @@ static const uint16_t hor_timing_PAL[] = {
|
|
0
|
|
};
|
|
|
|
-static const uint16_t vert_timing_PAL[] = {
|
|
+static const uint16_t vert_timing_PAL[MAX_V_CODE_TIMING_LEN] = {
|
|
0x2001,
|
|
0x200c,
|
|
0x1005,
|
|
@@ -623,9 +623,9 @@ void radeon_legacy_tv_mode_set(struct drm_encoder *encoder,
|
|
}
|
|
flicker_removal = (tmp + 500) / 1000;
|
|
|
|
- if (flicker_removal < 3)
|
|
- flicker_removal = 3;
|
|
- for (i = 0; i < 6; ++i) {
|
|
+ if (flicker_removal < 2)
|
|
+ flicker_removal = 2;
|
|
+ for (i = 0; i < ARRAY_SIZE(SLOPE_limit); ++i) {
|
|
if (flicker_removal == SLOPE_limit[i])
|
|
break;
|
|
}
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
|
|
index ace726a..96f0243 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_mode.h
|
|
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
|
|
@@ -33,6 +33,7 @@
|
|
#include <drm_crtc.h>
|
|
#include <drm_mode.h>
|
|
#include <drm_edid.h>
|
|
+#include <drm_dp_helper.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/i2c-id.h>
|
|
#include <linux/i2c-algo-bit.h>
|
|
@@ -45,32 +46,6 @@ struct radeon_device;
|
|
#define to_radeon_encoder(x) container_of(x, struct radeon_encoder, base)
|
|
#define to_radeon_framebuffer(x) container_of(x, struct radeon_framebuffer, base)
|
|
|
|
-enum radeon_connector_type {
|
|
- CONNECTOR_NONE,
|
|
- CONNECTOR_VGA,
|
|
- CONNECTOR_DVI_I,
|
|
- CONNECTOR_DVI_D,
|
|
- CONNECTOR_DVI_A,
|
|
- CONNECTOR_STV,
|
|
- CONNECTOR_CTV,
|
|
- CONNECTOR_LVDS,
|
|
- CONNECTOR_DIGITAL,
|
|
- CONNECTOR_SCART,
|
|
- CONNECTOR_HDMI_TYPE_A,
|
|
- CONNECTOR_HDMI_TYPE_B,
|
|
- CONNECTOR_0XC,
|
|
- CONNECTOR_0XD,
|
|
- CONNECTOR_DIN,
|
|
- CONNECTOR_DISPLAY_PORT,
|
|
- CONNECTOR_UNSUPPORTED
|
|
-};
|
|
-
|
|
-enum radeon_dvi_type {
|
|
- DVI_AUTO,
|
|
- DVI_DIGITAL,
|
|
- DVI_ANALOG
|
|
-};
|
|
-
|
|
enum radeon_rmx_type {
|
|
RMX_OFF,
|
|
RMX_FULL,
|
|
@@ -87,26 +62,48 @@ enum radeon_tv_std {
|
|
TV_STD_SCART_PAL,
|
|
TV_STD_SECAM,
|
|
TV_STD_PAL_CN,
|
|
+ TV_STD_PAL_N,
|
|
};
|
|
|
|
+/* radeon gpio-based i2c
|
|
+ * 1. "mask" reg and bits
|
|
+ * grabs the gpio pins for software use
|
|
+ * 0=not held 1=held
|
|
+ * 2. "a" reg and bits
|
|
+ * output pin value
|
|
+ * 0=low 1=high
|
|
+ * 3. "en" reg and bits
|
|
+ * sets the pin direction
|
|
+ * 0=input 1=output
|
|
+ * 4. "y" reg and bits
|
|
+ * input pin value
|
|
+ * 0=low 1=high
|
|
+ */
|
|
struct radeon_i2c_bus_rec {
|
|
bool valid;
|
|
+ /* id used by atom */
|
|
+ uint8_t i2c_id;
|
|
+ /* can be used with hw i2c engine */
|
|
+ bool hw_capable;
|
|
+ /* uses multi-media i2c engine */
|
|
+ bool mm_i2c;
|
|
+ /* regs and bits */
|
|
uint32_t mask_clk_reg;
|
|
uint32_t mask_data_reg;
|
|
uint32_t a_clk_reg;
|
|
uint32_t a_data_reg;
|
|
- uint32_t put_clk_reg;
|
|
- uint32_t put_data_reg;
|
|
- uint32_t get_clk_reg;
|
|
- uint32_t get_data_reg;
|
|
+ uint32_t en_clk_reg;
|
|
+ uint32_t en_data_reg;
|
|
+ uint32_t y_clk_reg;
|
|
+ uint32_t y_data_reg;
|
|
uint32_t mask_clk_mask;
|
|
uint32_t mask_data_mask;
|
|
- uint32_t put_clk_mask;
|
|
- uint32_t put_data_mask;
|
|
- uint32_t get_clk_mask;
|
|
- uint32_t get_data_mask;
|
|
uint32_t a_clk_mask;
|
|
uint32_t a_data_mask;
|
|
+ uint32_t en_clk_mask;
|
|
+ uint32_t en_data_mask;
|
|
+ uint32_t y_clk_mask;
|
|
+ uint32_t y_data_mask;
|
|
};
|
|
|
|
struct radeon_tmds_pll {
|
|
@@ -128,16 +125,24 @@ struct radeon_tmds_pll {
|
|
#define RADEON_PLL_PREFER_HIGH_POST_DIV (1 << 9)
|
|
#define RADEON_PLL_USE_FRAC_FB_DIV (1 << 10)
|
|
#define RADEON_PLL_PREFER_CLOSEST_LOWER (1 << 11)
|
|
+#define RADEON_PLL_USE_POST_DIV (1 << 12)
|
|
|
|
struct radeon_pll {
|
|
- uint16_t reference_freq;
|
|
- uint16_t reference_div;
|
|
+ /* reference frequency */
|
|
+ uint32_t reference_freq;
|
|
+
|
|
+ /* fixed dividers */
|
|
+ uint32_t reference_div;
|
|
+ uint32_t post_div;
|
|
+
|
|
+ /* pll in/out limits */
|
|
uint32_t pll_in_min;
|
|
uint32_t pll_in_max;
|
|
uint32_t pll_out_min;
|
|
uint32_t pll_out_max;
|
|
- uint16_t xclk;
|
|
+ uint32_t best_vco;
|
|
|
|
+ /* divider limits */
|
|
uint32_t min_ref_div;
|
|
uint32_t max_ref_div;
|
|
uint32_t min_post_div;
|
|
@@ -146,13 +151,21 @@ struct radeon_pll {
|
|
uint32_t max_feedback_div;
|
|
uint32_t min_frac_feedback_div;
|
|
uint32_t max_frac_feedback_div;
|
|
- uint32_t best_vco;
|
|
+
|
|
+ /* flags for the current clock */
|
|
+ uint32_t flags;
|
|
+
|
|
+ /* pll id */
|
|
+ uint32_t id;
|
|
};
|
|
|
|
struct radeon_i2c_chan {
|
|
- struct drm_device *dev;
|
|
struct i2c_adapter adapter;
|
|
- struct i2c_algo_bit_data algo;
|
|
+ struct drm_device *dev;
|
|
+ union {
|
|
+ struct i2c_algo_dp_aux_data dp;
|
|
+ struct i2c_algo_bit_data bit;
|
|
+ } algo;
|
|
struct radeon_i2c_bus_rec rec;
|
|
};
|
|
|
|
@@ -170,6 +183,11 @@ enum radeon_connector_table {
|
|
CT_EMAC,
|
|
};
|
|
|
|
+enum radeon_dvo_chip {
|
|
+ DVO_SIL164,
|
|
+ DVO_SIL1178,
|
|
+};
|
|
+
|
|
struct radeon_mode_info {
|
|
struct atom_context *atom_context;
|
|
struct card_info *atom_card_info;
|
|
@@ -184,7 +202,8 @@ struct radeon_mode_info {
|
|
struct drm_property *tv_std_property;
|
|
/* legacy TMDS PLL detect */
|
|
struct drm_property *tmds_pll_property;
|
|
-
|
|
+ /* hardcoded DFP edid from BIOS */
|
|
+ struct edid *bios_hardcoded_edid;
|
|
};
|
|
|
|
#define MAX_H_CODE_TIMING_LEN 32
|
|
@@ -261,6 +280,13 @@ struct radeon_encoder_int_tmds {
|
|
struct radeon_tmds_pll tmds_pll[4];
|
|
};
|
|
|
|
+struct radeon_encoder_ext_tmds {
|
|
+ /* tmds over dvo */
|
|
+ struct radeon_i2c_chan *i2c_bus;
|
|
+ uint8_t slave_addr;
|
|
+ enum radeon_dvo_chip dvo_chip;
|
|
+};
|
|
+
|
|
/* spread spectrum */
|
|
struct radeon_atom_ss {
|
|
uint16_t percentage;
|
|
@@ -274,7 +300,7 @@ struct radeon_atom_ss {
|
|
struct radeon_encoder_atom_dig {
|
|
/* atom dig */
|
|
bool coherent_mode;
|
|
- int dig_block;
|
|
+ int dig_encoder; /* -1 disabled, 0 DIGA, 1 DIGB */
|
|
/* atom lvds */
|
|
uint32_t lvds_misc;
|
|
uint16_t panel_pwr_delay;
|
|
@@ -297,11 +323,43 @@ struct radeon_encoder {
|
|
enum radeon_rmx_type rmx_type;
|
|
struct drm_display_mode native_mode;
|
|
void *enc_priv;
|
|
+ int hdmi_offset;
|
|
+ int hdmi_audio_workaround;
|
|
+ int hdmi_buffer_status;
|
|
};
|
|
|
|
struct radeon_connector_atom_dig {
|
|
uint32_t igp_lane_info;
|
|
bool linkb;
|
|
+ /* displayport */
|
|
+ struct radeon_i2c_chan *dp_i2c_bus;
|
|
+ u8 dpcd[8];
|
|
+ u8 dp_sink_type;
|
|
+ int dp_clock;
|
|
+ int dp_lane_count;
|
|
+};
|
|
+
|
|
+struct radeon_gpio_rec {
|
|
+ bool valid;
|
|
+ u8 id;
|
|
+ u32 reg;
|
|
+ u32 mask;
|
|
+};
|
|
+
|
|
+enum radeon_hpd_id {
|
|
+ RADEON_HPD_NONE = 0,
|
|
+ RADEON_HPD_1,
|
|
+ RADEON_HPD_2,
|
|
+ RADEON_HPD_3,
|
|
+ RADEON_HPD_4,
|
|
+ RADEON_HPD_5,
|
|
+ RADEON_HPD_6,
|
|
+};
|
|
+
|
|
+struct radeon_hpd {
|
|
+ enum radeon_hpd_id hpd;
|
|
+ u8 plugged_state;
|
|
+ struct radeon_gpio_rec gpio;
|
|
};
|
|
|
|
struct radeon_connector {
|
|
@@ -318,6 +376,7 @@ struct radeon_connector {
|
|
void *con_priv;
|
|
bool dac_load_detect;
|
|
uint16_t connector_object_id;
|
|
+ struct radeon_hpd hpd;
|
|
};
|
|
|
|
struct radeon_framebuffer {
|
|
@@ -325,10 +384,42 @@ struct radeon_framebuffer {
|
|
struct drm_gem_object *obj;
|
|
};
|
|
|
|
+extern enum radeon_tv_std
|
|
+radeon_combios_get_tv_info(struct radeon_device *rdev);
|
|
+extern enum radeon_tv_std
|
|
+radeon_atombios_get_tv_info(struct radeon_device *rdev);
|
|
+
|
|
+extern void radeon_connector_hotplug(struct drm_connector *connector);
|
|
+extern bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector);
|
|
+extern int radeon_dp_mode_valid_helper(struct radeon_connector *radeon_connector,
|
|
+ struct drm_display_mode *mode);
|
|
+extern void radeon_dp_set_link_config(struct drm_connector *connector,
|
|
+ struct drm_display_mode *mode);
|
|
+extern void dp_link_train(struct drm_encoder *encoder,
|
|
+ struct drm_connector *connector);
|
|
+extern u8 radeon_dp_getsinktype(struct radeon_connector *radeon_connector);
|
|
+extern bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector);
|
|
+extern void atombios_dig_transmitter_setup(struct drm_encoder *encoder,
|
|
+ int action, uint8_t lane_num,
|
|
+ uint8_t lane_set);
|
|
+extern int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
|
|
+ uint8_t write_byte, uint8_t *read_byte);
|
|
+
|
|
+extern struct radeon_i2c_chan *radeon_i2c_create_dp(struct drm_device *dev,
|
|
+ struct radeon_i2c_bus_rec *rec,
|
|
+ const char *name);
|
|
extern struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev,
|
|
struct radeon_i2c_bus_rec *rec,
|
|
const char *name);
|
|
extern void radeon_i2c_destroy(struct radeon_i2c_chan *i2c);
|
|
+extern void radeon_i2c_sw_get_byte(struct radeon_i2c_chan *i2c_bus,
|
|
+ u8 slave_addr,
|
|
+ u8 addr,
|
|
+ u8 *val);
|
|
+extern void radeon_i2c_sw_put_byte(struct radeon_i2c_chan *i2c,
|
|
+ u8 slave_addr,
|
|
+ u8 addr,
|
|
+ u8 val);
|
|
extern bool radeon_ddc_probe(struct radeon_connector *radeon_connector);
|
|
extern int radeon_ddc_get_modes(struct radeon_connector *radeon_connector);
|
|
|
|
@@ -340,8 +431,17 @@ extern void radeon_compute_pll(struct radeon_pll *pll,
|
|
uint32_t *fb_div_p,
|
|
uint32_t *frac_fb_div_p,
|
|
uint32_t *ref_div_p,
|
|
- uint32_t *post_div_p,
|
|
- int flags);
|
|
+ uint32_t *post_div_p);
|
|
+
|
|
+extern void radeon_compute_pll_avivo(struct radeon_pll *pll,
|
|
+ uint64_t freq,
|
|
+ uint32_t *dot_clock_p,
|
|
+ uint32_t *fb_div_p,
|
|
+ uint32_t *frac_fb_div_p,
|
|
+ uint32_t *ref_div_p,
|
|
+ uint32_t *post_div_p);
|
|
+
|
|
+extern void radeon_setup_encoder_clones(struct drm_device *dev);
|
|
|
|
struct drm_encoder *radeon_encoder_legacy_lvds_add(struct drm_device *dev, int bios_index);
|
|
struct drm_encoder *radeon_encoder_legacy_primary_dac_add(struct drm_device *dev, int bios_index, int with_tv);
|
|
@@ -349,6 +449,7 @@ struct drm_encoder *radeon_encoder_legacy_tv_dac_add(struct drm_device *dev, int
|
|
struct drm_encoder *radeon_encoder_legacy_tmds_int_add(struct drm_device *dev, int bios_index);
|
|
struct drm_encoder *radeon_encoder_legacy_tmds_ext_add(struct drm_device *dev, int bios_index);
|
|
extern void atombios_external_tmds_setup(struct drm_encoder *encoder, int action);
|
|
+extern void atombios_digital_setup(struct drm_encoder *encoder, int action);
|
|
extern int atombios_get_encoder_mode(struct drm_encoder *encoder);
|
|
extern void radeon_encoder_set_active_device(struct drm_encoder *encoder);
|
|
|
|
@@ -364,7 +465,6 @@ extern void atombios_crtc_dpms(struct drm_crtc *crtc, int mode);
|
|
|
|
extern int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y,
|
|
struct drm_framebuffer *old_fb);
|
|
-extern void radeon_legacy_atom_set_surface(struct drm_crtc *crtc);
|
|
|
|
extern int radeon_crtc_cursor_set(struct drm_crtc *crtc,
|
|
struct drm_file *file_priv,
|
|
@@ -374,16 +474,23 @@ extern int radeon_crtc_cursor_set(struct drm_crtc *crtc,
|
|
extern int radeon_crtc_cursor_move(struct drm_crtc *crtc,
|
|
int x, int y);
|
|
|
|
+extern bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev);
|
|
+extern struct edid *
|
|
+radeon_combios_get_hardcoded_edid(struct radeon_device *rdev);
|
|
extern bool radeon_atom_get_clock_info(struct drm_device *dev);
|
|
extern bool radeon_combios_get_clock_info(struct drm_device *dev);
|
|
extern struct radeon_encoder_atom_dig *
|
|
radeon_atombios_get_lvds_info(struct radeon_encoder *encoder);
|
|
-bool radeon_atombios_get_tmds_info(struct radeon_encoder *encoder,
|
|
- struct radeon_encoder_int_tmds *tmds);
|
|
-bool radeon_legacy_get_tmds_info_from_combios(struct radeon_encoder *encoder,
|
|
- struct radeon_encoder_int_tmds *tmds);
|
|
-bool radeon_legacy_get_tmds_info_from_table(struct radeon_encoder *encoder,
|
|
- struct radeon_encoder_int_tmds *tmds);
|
|
+extern bool radeon_atombios_get_tmds_info(struct radeon_encoder *encoder,
|
|
+ struct radeon_encoder_int_tmds *tmds);
|
|
+extern bool radeon_legacy_get_tmds_info_from_combios(struct radeon_encoder *encoder,
|
|
+ struct radeon_encoder_int_tmds *tmds);
|
|
+extern bool radeon_legacy_get_tmds_info_from_table(struct radeon_encoder *encoder,
|
|
+ struct radeon_encoder_int_tmds *tmds);
|
|
+extern bool radeon_legacy_get_ext_tmds_info_from_combios(struct radeon_encoder *encoder,
|
|
+ struct radeon_encoder_ext_tmds *tmds);
|
|
+extern bool radeon_legacy_get_ext_tmds_info_from_table(struct radeon_encoder *encoder,
|
|
+ struct radeon_encoder_ext_tmds *tmds);
|
|
extern struct radeon_encoder_primary_dac *
|
|
radeon_atombios_get_primary_dac_info(struct radeon_encoder *encoder);
|
|
extern struct radeon_encoder_tv_dac *
|
|
@@ -395,6 +502,8 @@ extern struct radeon_encoder_tv_dac *
|
|
radeon_combios_get_tv_dac_info(struct radeon_encoder *encoder);
|
|
extern struct radeon_encoder_primary_dac *
|
|
radeon_combios_get_primary_dac_info(struct radeon_encoder *encoder);
|
|
+extern bool radeon_combios_external_tmds_setup(struct drm_encoder *encoder);
|
|
+extern void radeon_external_tmds_setup(struct drm_encoder *encoder);
|
|
extern void radeon_combios_output_lock(struct drm_encoder *encoder, bool lock);
|
|
extern void radeon_combios_initialize_bios_scratch_regs(struct drm_device *dev);
|
|
extern void radeon_atom_output_lock(struct drm_encoder *encoder, bool lock);
|
|
@@ -426,16 +535,13 @@ void radeon_atombios_init_crtc(struct drm_device *dev,
|
|
struct radeon_crtc *radeon_crtc);
|
|
void radeon_legacy_init_crtc(struct drm_device *dev,
|
|
struct radeon_crtc *radeon_crtc);
|
|
-void radeon_i2c_do_lock(struct radeon_connector *radeon_connector, int lock_state);
|
|
+extern void radeon_i2c_do_lock(struct radeon_i2c_chan *i2c, int lock_state);
|
|
|
|
void radeon_get_clock_info(struct drm_device *dev);
|
|
|
|
extern bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev);
|
|
extern bool radeon_get_atom_connector_info_from_supported_devices_table(struct drm_device *dev);
|
|
|
|
-void radeon_rmx_mode_fixup(struct drm_encoder *encoder,
|
|
- struct drm_display_mode *mode,
|
|
- struct drm_display_mode *adjusted_mode);
|
|
void radeon_enc_destroy(struct drm_encoder *encoder);
|
|
void radeon_copy_fb(struct drm_device *dev, struct drm_gem_object *dst_obj);
|
|
void radeon_combios_asic_init(struct drm_device *dev);
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c
|
|
index 1f056da..f1da370 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_object.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_object.c
|
|
@@ -34,100 +34,62 @@
|
|
#include "radeon_drm.h"
|
|
#include "radeon.h"
|
|
|
|
-struct radeon_object {
|
|
- struct ttm_buffer_object tobj;
|
|
- struct list_head list;
|
|
- struct radeon_device *rdev;
|
|
- struct drm_gem_object *gobj;
|
|
- struct ttm_bo_kmap_obj kmap;
|
|
- unsigned pin_count;
|
|
- uint64_t gpu_addr;
|
|
- void *kptr;
|
|
- bool is_iomem;
|
|
- uint32_t tiling_flags;
|
|
- uint32_t pitch;
|
|
- int surface_reg;
|
|
-};
|
|
|
|
int radeon_ttm_init(struct radeon_device *rdev);
|
|
void radeon_ttm_fini(struct radeon_device *rdev);
|
|
+static void radeon_bo_clear_surface_reg(struct radeon_bo *bo);
|
|
|
|
/*
|
|
* To exclude mutual BO access we rely on bo_reserve exclusion, as all
|
|
* function are calling it.
|
|
*/
|
|
|
|
-static int radeon_object_reserve(struct radeon_object *robj, bool interruptible)
|
|
+static void radeon_ttm_bo_destroy(struct ttm_buffer_object *tbo)
|
|
{
|
|
- return ttm_bo_reserve(&robj->tobj, interruptible, false, false, 0);
|
|
-}
|
|
+ struct radeon_bo *bo;
|
|
|
|
-static void radeon_object_unreserve(struct radeon_object *robj)
|
|
-{
|
|
- ttm_bo_unreserve(&robj->tobj);
|
|
+ bo = container_of(tbo, struct radeon_bo, tbo);
|
|
+ mutex_lock(&bo->rdev->gem.mutex);
|
|
+ list_del_init(&bo->list);
|
|
+ mutex_unlock(&bo->rdev->gem.mutex);
|
|
+ radeon_bo_clear_surface_reg(bo);
|
|
+ kfree(bo);
|
|
}
|
|
|
|
-static void radeon_ttm_object_object_destroy(struct ttm_buffer_object *tobj)
|
|
+bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo)
|
|
{
|
|
- struct radeon_object *robj;
|
|
-
|
|
- robj = container_of(tobj, struct radeon_object, tobj);
|
|
- list_del_init(&robj->list);
|
|
- radeon_object_clear_surface_reg(robj);
|
|
- kfree(robj);
|
|
+ if (bo->destroy == &radeon_ttm_bo_destroy)
|
|
+ return true;
|
|
+ return false;
|
|
}
|
|
|
|
-static inline void radeon_object_gpu_addr(struct radeon_object *robj)
|
|
+void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain)
|
|
{
|
|
- /* Default gpu address */
|
|
- robj->gpu_addr = 0xFFFFFFFFFFFFFFFFULL;
|
|
- if (robj->tobj.mem.mm_node == NULL) {
|
|
- return;
|
|
- }
|
|
- robj->gpu_addr = ((u64)robj->tobj.mem.mm_node->start) << PAGE_SHIFT;
|
|
- switch (robj->tobj.mem.mem_type) {
|
|
- case TTM_PL_VRAM:
|
|
- robj->gpu_addr += (u64)robj->rdev->mc.vram_location;
|
|
- break;
|
|
- case TTM_PL_TT:
|
|
- robj->gpu_addr += (u64)robj->rdev->mc.gtt_location;
|
|
- break;
|
|
- default:
|
|
- DRM_ERROR("Unknown placement %d\n", robj->tobj.mem.mem_type);
|
|
- robj->gpu_addr = 0xFFFFFFFFFFFFFFFFULL;
|
|
- return;
|
|
- }
|
|
-}
|
|
+ u32 c = 0;
|
|
|
|
-static inline uint32_t radeon_object_flags_from_domain(uint32_t domain)
|
|
-{
|
|
- uint32_t flags = 0;
|
|
- if (domain & RADEON_GEM_DOMAIN_VRAM) {
|
|
- flags |= TTM_PL_FLAG_VRAM | TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED;
|
|
- }
|
|
- if (domain & RADEON_GEM_DOMAIN_GTT) {
|
|
- flags |= TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
|
|
- }
|
|
- if (domain & RADEON_GEM_DOMAIN_CPU) {
|
|
- flags |= TTM_PL_FLAG_SYSTEM | TTM_PL_MASK_CACHING;
|
|
- }
|
|
- if (!flags) {
|
|
- flags |= TTM_PL_FLAG_SYSTEM | TTM_PL_MASK_CACHING;
|
|
- }
|
|
- return flags;
|
|
+ rbo->placement.fpfn = 0;
|
|
+ rbo->placement.lpfn = 0;
|
|
+ rbo->placement.placement = rbo->placements;
|
|
+ rbo->placement.busy_placement = rbo->placements;
|
|
+ if (domain & RADEON_GEM_DOMAIN_VRAM)
|
|
+ rbo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED |
|
|
+ TTM_PL_FLAG_VRAM;
|
|
+ if (domain & RADEON_GEM_DOMAIN_GTT)
|
|
+ rbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT;
|
|
+ if (domain & RADEON_GEM_DOMAIN_CPU)
|
|
+ rbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
|
|
+ if (!c)
|
|
+ rbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
|
|
+ rbo->placement.num_placement = c;
|
|
+ rbo->placement.num_busy_placement = c;
|
|
}
|
|
|
|
-int radeon_object_create(struct radeon_device *rdev,
|
|
- struct drm_gem_object *gobj,
|
|
- unsigned long size,
|
|
- bool kernel,
|
|
- uint32_t domain,
|
|
- bool interruptible,
|
|
- struct radeon_object **robj_ptr)
|
|
+int radeon_bo_create(struct radeon_device *rdev, struct drm_gem_object *gobj,
|
|
+ unsigned long size, bool kernel, u32 domain,
|
|
+ struct radeon_bo **bo_ptr)
|
|
{
|
|
- struct radeon_object *robj;
|
|
+ struct radeon_bo *bo;
|
|
enum ttm_bo_type type;
|
|
- uint32_t flags;
|
|
int r;
|
|
|
|
if (unlikely(rdev->mman.bdev.dev_mapping == NULL)) {
|
|
@@ -138,238 +100,161 @@ int radeon_object_create(struct radeon_device *rdev,
|
|
} else {
|
|
type = ttm_bo_type_device;
|
|
}
|
|
- *robj_ptr = NULL;
|
|
- robj = kzalloc(sizeof(struct radeon_object), GFP_KERNEL);
|
|
- if (robj == NULL) {
|
|
+ *bo_ptr = NULL;
|
|
+ bo = kzalloc(sizeof(struct radeon_bo), GFP_KERNEL);
|
|
+ if (bo == NULL)
|
|
return -ENOMEM;
|
|
- }
|
|
- robj->rdev = rdev;
|
|
- robj->gobj = gobj;
|
|
- robj->surface_reg = -1;
|
|
- INIT_LIST_HEAD(&robj->list);
|
|
-
|
|
- flags = radeon_object_flags_from_domain(domain);
|
|
- r = ttm_buffer_object_init(&rdev->mman.bdev, &robj->tobj, size, type, flags,
|
|
- 0, 0, false, NULL, size,
|
|
- &radeon_ttm_object_object_destroy);
|
|
+ bo->rdev = rdev;
|
|
+ bo->gobj = gobj;
|
|
+ bo->surface_reg = -1;
|
|
+ INIT_LIST_HEAD(&bo->list);
|
|
+
|
|
+ radeon_ttm_placement_from_domain(bo, domain);
|
|
+ /* Kernel allocation are uninterruptible */
|
|
+ r = ttm_bo_init(&rdev->mman.bdev, &bo->tbo, size, type,
|
|
+ &bo->placement, 0, 0, !kernel, NULL, size,
|
|
+ &radeon_ttm_bo_destroy);
|
|
if (unlikely(r != 0)) {
|
|
- /* ttm call radeon_ttm_object_object_destroy if error happen */
|
|
- DRM_ERROR("Failed to allocate TTM object (%ld, 0x%08X, %u)\n",
|
|
- size, flags, 0);
|
|
+ if (r != -ERESTARTSYS)
|
|
+ dev_err(rdev->dev,
|
|
+ "object_init failed for (%lu, 0x%08X)\n",
|
|
+ size, domain);
|
|
return r;
|
|
}
|
|
- *robj_ptr = robj;
|
|
+ *bo_ptr = bo;
|
|
if (gobj) {
|
|
- list_add_tail(&robj->list, &rdev->gem.objects);
|
|
+ mutex_lock(&bo->rdev->gem.mutex);
|
|
+ list_add_tail(&bo->list, &rdev->gem.objects);
|
|
+ mutex_unlock(&bo->rdev->gem.mutex);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
-int radeon_object_kmap(struct radeon_object *robj, void **ptr)
|
|
+int radeon_bo_kmap(struct radeon_bo *bo, void **ptr)
|
|
{
|
|
+ bool is_iomem;
|
|
int r;
|
|
|
|
- spin_lock(&robj->tobj.lock);
|
|
- if (robj->kptr) {
|
|
+ if (bo->kptr) {
|
|
if (ptr) {
|
|
- *ptr = robj->kptr;
|
|
+ *ptr = bo->kptr;
|
|
}
|
|
- spin_unlock(&robj->tobj.lock);
|
|
return 0;
|
|
}
|
|
- spin_unlock(&robj->tobj.lock);
|
|
- r = ttm_bo_kmap(&robj->tobj, 0, robj->tobj.num_pages, &robj->kmap);
|
|
+ r = ttm_bo_kmap(&bo->tbo, 0, bo->tbo.num_pages, &bo->kmap);
|
|
if (r) {
|
|
return r;
|
|
}
|
|
- spin_lock(&robj->tobj.lock);
|
|
- robj->kptr = ttm_kmap_obj_virtual(&robj->kmap, &robj->is_iomem);
|
|
- spin_unlock(&robj->tobj.lock);
|
|
+ bo->kptr = ttm_kmap_obj_virtual(&bo->kmap, &is_iomem);
|
|
if (ptr) {
|
|
- *ptr = robj->kptr;
|
|
+ *ptr = bo->kptr;
|
|
}
|
|
- radeon_object_check_tiling(robj, 0, 0);
|
|
+ radeon_bo_check_tiling(bo, 0, 0);
|
|
return 0;
|
|
}
|
|
|
|
-void radeon_object_kunmap(struct radeon_object *robj)
|
|
+void radeon_bo_kunmap(struct radeon_bo *bo)
|
|
{
|
|
- spin_lock(&robj->tobj.lock);
|
|
- if (robj->kptr == NULL) {
|
|
- spin_unlock(&robj->tobj.lock);
|
|
+ if (bo->kptr == NULL)
|
|
return;
|
|
- }
|
|
- robj->kptr = NULL;
|
|
- spin_unlock(&robj->tobj.lock);
|
|
- radeon_object_check_tiling(robj, 0, 0);
|
|
- ttm_bo_kunmap(&robj->kmap);
|
|
+ bo->kptr = NULL;
|
|
+ radeon_bo_check_tiling(bo, 0, 0);
|
|
+ ttm_bo_kunmap(&bo->kmap);
|
|
}
|
|
|
|
-void radeon_object_unref(struct radeon_object **robj)
|
|
+void radeon_bo_unref(struct radeon_bo **bo)
|
|
{
|
|
- struct ttm_buffer_object *tobj;
|
|
+ struct ttm_buffer_object *tbo;
|
|
|
|
- if ((*robj) == NULL) {
|
|
+ if ((*bo) == NULL)
|
|
return;
|
|
- }
|
|
- tobj = &((*robj)->tobj);
|
|
- ttm_bo_unref(&tobj);
|
|
- if (tobj == NULL) {
|
|
- *robj = NULL;
|
|
- }
|
|
-}
|
|
-
|
|
-int radeon_object_mmap(struct radeon_object *robj, uint64_t *offset)
|
|
-{
|
|
- *offset = robj->tobj.addr_space_offset;
|
|
- return 0;
|
|
+ tbo = &((*bo)->tbo);
|
|
+ ttm_bo_unref(&tbo);
|
|
+ if (tbo == NULL)
|
|
+ *bo = NULL;
|
|
}
|
|
|
|
-int radeon_object_pin(struct radeon_object *robj, uint32_t domain,
|
|
- uint64_t *gpu_addr)
|
|
+int radeon_bo_pin(struct radeon_bo *bo, u32 domain, u64 *gpu_addr)
|
|
{
|
|
- uint32_t flags;
|
|
- uint32_t tmp;
|
|
- int r;
|
|
+ int r, i;
|
|
|
|
- flags = radeon_object_flags_from_domain(domain);
|
|
- spin_lock(&robj->tobj.lock);
|
|
- if (robj->pin_count) {
|
|
- robj->pin_count++;
|
|
- if (gpu_addr != NULL) {
|
|
- *gpu_addr = robj->gpu_addr;
|
|
- }
|
|
- spin_unlock(&robj->tobj.lock);
|
|
+ radeon_ttm_placement_from_domain(bo, domain);
|
|
+ if (bo->pin_count) {
|
|
+ bo->pin_count++;
|
|
+ if (gpu_addr)
|
|
+ *gpu_addr = radeon_bo_gpu_offset(bo);
|
|
return 0;
|
|
}
|
|
- spin_unlock(&robj->tobj.lock);
|
|
- r = radeon_object_reserve(robj, false);
|
|
- if (unlikely(r != 0)) {
|
|
- DRM_ERROR("radeon: failed to reserve object for pinning it.\n");
|
|
- return r;
|
|
- }
|
|
- tmp = robj->tobj.mem.placement;
|
|
- ttm_flag_masked(&tmp, flags, TTM_PL_MASK_MEM);
|
|
- robj->tobj.proposed_placement = tmp | TTM_PL_FLAG_NO_EVICT | TTM_PL_MASK_CACHING;
|
|
- r = ttm_buffer_object_validate(&robj->tobj,
|
|
- robj->tobj.proposed_placement,
|
|
- false, false);
|
|
- radeon_object_gpu_addr(robj);
|
|
- if (gpu_addr != NULL) {
|
|
- *gpu_addr = robj->gpu_addr;
|
|
- }
|
|
- robj->pin_count = 1;
|
|
- if (unlikely(r != 0)) {
|
|
- DRM_ERROR("radeon: failed to pin object.\n");
|
|
- }
|
|
- radeon_object_unreserve(robj);
|
|
+ radeon_ttm_placement_from_domain(bo, domain);
|
|
+ for (i = 0; i < bo->placement.num_placement; i++)
|
|
+ bo->placements[i] |= TTM_PL_FLAG_NO_EVICT;
|
|
+ r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false);
|
|
+ if (likely(r == 0)) {
|
|
+ bo->pin_count = 1;
|
|
+ if (gpu_addr != NULL)
|
|
+ *gpu_addr = radeon_bo_gpu_offset(bo);
|
|
+ }
|
|
+ if (unlikely(r != 0))
|
|
+ dev_err(bo->rdev->dev, "%p pin failed\n", bo);
|
|
return r;
|
|
}
|
|
|
|
-void radeon_object_unpin(struct radeon_object *robj)
|
|
+int radeon_bo_unpin(struct radeon_bo *bo)
|
|
{
|
|
- uint32_t flags;
|
|
- int r;
|
|
+ int r, i;
|
|
|
|
- spin_lock(&robj->tobj.lock);
|
|
- if (!robj->pin_count) {
|
|
- spin_unlock(&robj->tobj.lock);
|
|
- printk(KERN_WARNING "Unpin not necessary for %p !\n", robj);
|
|
- return;
|
|
- }
|
|
- robj->pin_count--;
|
|
- if (robj->pin_count) {
|
|
- spin_unlock(&robj->tobj.lock);
|
|
- return;
|
|
- }
|
|
- spin_unlock(&robj->tobj.lock);
|
|
- r = radeon_object_reserve(robj, false);
|
|
- if (unlikely(r != 0)) {
|
|
- DRM_ERROR("radeon: failed to reserve object for unpinning it.\n");
|
|
- return;
|
|
- }
|
|
- flags = robj->tobj.mem.placement;
|
|
- robj->tobj.proposed_placement = flags & ~TTM_PL_FLAG_NO_EVICT;
|
|
- r = ttm_buffer_object_validate(&robj->tobj,
|
|
- robj->tobj.proposed_placement,
|
|
- false, false);
|
|
- if (unlikely(r != 0)) {
|
|
- DRM_ERROR("radeon: failed to unpin buffer.\n");
|
|
- }
|
|
- radeon_object_unreserve(robj);
|
|
-}
|
|
-
|
|
-int radeon_object_wait(struct radeon_object *robj)
|
|
-{
|
|
- int r = 0;
|
|
-
|
|
- /* FIXME: should use block reservation instead */
|
|
- r = radeon_object_reserve(robj, true);
|
|
- if (unlikely(r != 0)) {
|
|
- DRM_ERROR("radeon: failed to reserve object for waiting.\n");
|
|
- return r;
|
|
- }
|
|
- spin_lock(&robj->tobj.lock);
|
|
- if (robj->tobj.sync_obj) {
|
|
- r = ttm_bo_wait(&robj->tobj, true, true, false);
|
|
- }
|
|
- spin_unlock(&robj->tobj.lock);
|
|
- radeon_object_unreserve(robj);
|
|
- return r;
|
|
-}
|
|
-
|
|
-int radeon_object_busy_domain(struct radeon_object *robj, uint32_t *cur_placement)
|
|
-{
|
|
- int r = 0;
|
|
-
|
|
- r = radeon_object_reserve(robj, true);
|
|
- if (unlikely(r != 0)) {
|
|
- DRM_ERROR("radeon: failed to reserve object for waiting.\n");
|
|
- return r;
|
|
- }
|
|
- spin_lock(&robj->tobj.lock);
|
|
- *cur_placement = robj->tobj.mem.mem_type;
|
|
- if (robj->tobj.sync_obj) {
|
|
- r = ttm_bo_wait(&robj->tobj, true, true, true);
|
|
+ if (!bo->pin_count) {
|
|
+ dev_warn(bo->rdev->dev, "%p unpin not necessary\n", bo);
|
|
+ return 0;
|
|
}
|
|
- spin_unlock(&robj->tobj.lock);
|
|
- radeon_object_unreserve(robj);
|
|
+ bo->pin_count--;
|
|
+ if (bo->pin_count)
|
|
+ return 0;
|
|
+ for (i = 0; i < bo->placement.num_placement; i++)
|
|
+ bo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT;
|
|
+ r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false);
|
|
+ if (unlikely(r != 0))
|
|
+ dev_err(bo->rdev->dev, "%p validate failed for unpin\n", bo);
|
|
return r;
|
|
}
|
|
|
|
-int radeon_object_evict_vram(struct radeon_device *rdev)
|
|
+int radeon_bo_evict_vram(struct radeon_device *rdev)
|
|
{
|
|
- if (rdev->flags & RADEON_IS_IGP) {
|
|
- /* Useless to evict on IGP chips */
|
|
- return 0;
|
|
+ /* late 2.6.33 fix IGP hibernate - we need pm ops to do this correct */
|
|
+ if (0 && (rdev->flags & RADEON_IS_IGP)) {
|
|
+ if (rdev->mc.igp_sideport_enabled == false)
|
|
+ /* Useless to evict on IGP chips */
|
|
+ return 0;
|
|
}
|
|
return ttm_bo_evict_mm(&rdev->mman.bdev, TTM_PL_VRAM);
|
|
}
|
|
|
|
-void radeon_object_force_delete(struct radeon_device *rdev)
|
|
+void radeon_bo_force_delete(struct radeon_device *rdev)
|
|
{
|
|
- struct radeon_object *robj, *n;
|
|
+ struct radeon_bo *bo, *n;
|
|
struct drm_gem_object *gobj;
|
|
|
|
if (list_empty(&rdev->gem.objects)) {
|
|
return;
|
|
}
|
|
- DRM_ERROR("Userspace still has active objects !\n");
|
|
- list_for_each_entry_safe(robj, n, &rdev->gem.objects, list) {
|
|
+ dev_err(rdev->dev, "Userspace still has active objects !\n");
|
|
+ list_for_each_entry_safe(bo, n, &rdev->gem.objects, list) {
|
|
mutex_lock(&rdev->ddev->struct_mutex);
|
|
- gobj = robj->gobj;
|
|
- DRM_ERROR("Force free for (%p,%p,%lu,%lu)\n",
|
|
- gobj, robj, (unsigned long)gobj->size,
|
|
- *((unsigned long *)&gobj->refcount));
|
|
- list_del_init(&robj->list);
|
|
- radeon_object_unref(&robj);
|
|
+ gobj = bo->gobj;
|
|
+ dev_err(rdev->dev, "%p %p %lu %lu force free\n",
|
|
+ gobj, bo, (unsigned long)gobj->size,
|
|
+ *((unsigned long *)&gobj->refcount));
|
|
+ mutex_lock(&bo->rdev->gem.mutex);
|
|
+ list_del_init(&bo->list);
|
|
+ mutex_unlock(&bo->rdev->gem.mutex);
|
|
+ radeon_bo_unref(&bo);
|
|
gobj->driver_private = NULL;
|
|
drm_gem_object_unreference(gobj);
|
|
mutex_unlock(&rdev->ddev->struct_mutex);
|
|
}
|
|
}
|
|
|
|
-int radeon_object_init(struct radeon_device *rdev)
|
|
+int radeon_bo_init(struct radeon_device *rdev)
|
|
{
|
|
/* Add an MTRR for the VRAM */
|
|
rdev->mc.vram_mtrr = mtrr_add(rdev->mc.aper_base, rdev->mc.aper_size,
|
|
@@ -382,13 +267,13 @@ int radeon_object_init(struct radeon_device *rdev)
|
|
return radeon_ttm_init(rdev);
|
|
}
|
|
|
|
-void radeon_object_fini(struct radeon_device *rdev)
|
|
+void radeon_bo_fini(struct radeon_device *rdev)
|
|
{
|
|
radeon_ttm_fini(rdev);
|
|
}
|
|
|
|
-void radeon_object_list_add_object(struct radeon_object_list *lobj,
|
|
- struct list_head *head)
|
|
+void radeon_bo_list_add_object(struct radeon_bo_list *lobj,
|
|
+ struct list_head *head)
|
|
{
|
|
if (lobj->wdomain) {
|
|
list_add(&lobj->list, head);
|
|
@@ -397,125 +282,102 @@ void radeon_object_list_add_object(struct radeon_object_list *lobj,
|
|
}
|
|
}
|
|
|
|
-int radeon_object_list_reserve(struct list_head *head)
|
|
+int radeon_bo_list_reserve(struct list_head *head)
|
|
{
|
|
- struct radeon_object_list *lobj;
|
|
+ struct radeon_bo_list *lobj;
|
|
int r;
|
|
|
|
list_for_each_entry(lobj, head, list){
|
|
- if (!lobj->robj->pin_count) {
|
|
- r = radeon_object_reserve(lobj->robj, true);
|
|
- if (unlikely(r != 0)) {
|
|
- DRM_ERROR("radeon: failed to reserve object.\n");
|
|
- return r;
|
|
- }
|
|
- } else {
|
|
- }
|
|
+ r = radeon_bo_reserve(lobj->bo, false);
|
|
+ if (unlikely(r != 0))
|
|
+ return r;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
-void radeon_object_list_unreserve(struct list_head *head)
|
|
+void radeon_bo_list_unreserve(struct list_head *head)
|
|
{
|
|
- struct radeon_object_list *lobj;
|
|
+ struct radeon_bo_list *lobj;
|
|
|
|
list_for_each_entry(lobj, head, list) {
|
|
- if (!lobj->robj->pin_count) {
|
|
- radeon_object_unreserve(lobj->robj);
|
|
- }
|
|
+ /* only unreserve object we successfully reserved */
|
|
+ if (radeon_bo_is_reserved(lobj->bo))
|
|
+ radeon_bo_unreserve(lobj->bo);
|
|
}
|
|
}
|
|
|
|
-int radeon_object_list_validate(struct list_head *head, void *fence)
|
|
+int radeon_bo_list_validate(struct list_head *head)
|
|
{
|
|
- struct radeon_object_list *lobj;
|
|
- struct radeon_object *robj;
|
|
- struct radeon_fence *old_fence = NULL;
|
|
+ struct radeon_bo_list *lobj;
|
|
+ struct radeon_bo *bo;
|
|
int r;
|
|
|
|
- r = radeon_object_list_reserve(head);
|
|
+ r = radeon_bo_list_reserve(head);
|
|
if (unlikely(r != 0)) {
|
|
- radeon_object_list_unreserve(head);
|
|
return r;
|
|
}
|
|
list_for_each_entry(lobj, head, list) {
|
|
- robj = lobj->robj;
|
|
- if (!robj->pin_count) {
|
|
+ bo = lobj->bo;
|
|
+ if (!bo->pin_count) {
|
|
if (lobj->wdomain) {
|
|
- robj->tobj.proposed_placement =
|
|
- radeon_object_flags_from_domain(lobj->wdomain);
|
|
+ radeon_ttm_placement_from_domain(bo,
|
|
+ lobj->wdomain);
|
|
} else {
|
|
- robj->tobj.proposed_placement =
|
|
- radeon_object_flags_from_domain(lobj->rdomain);
|
|
+ radeon_ttm_placement_from_domain(bo,
|
|
+ lobj->rdomain);
|
|
}
|
|
- r = ttm_buffer_object_validate(&robj->tobj,
|
|
- robj->tobj.proposed_placement,
|
|
- true, false);
|
|
- if (unlikely(r)) {
|
|
- DRM_ERROR("radeon: failed to validate.\n");
|
|
+ r = ttm_bo_validate(&bo->tbo, &bo->placement,
|
|
+ true, false);
|
|
+ if (unlikely(r))
|
|
return r;
|
|
- }
|
|
- radeon_object_gpu_addr(robj);
|
|
- }
|
|
- lobj->gpu_offset = robj->gpu_addr;
|
|
- lobj->tiling_flags = robj->tiling_flags;
|
|
- if (fence) {
|
|
- old_fence = (struct radeon_fence *)robj->tobj.sync_obj;
|
|
- robj->tobj.sync_obj = radeon_fence_ref(fence);
|
|
- robj->tobj.sync_obj_arg = NULL;
|
|
- }
|
|
- if (old_fence) {
|
|
- radeon_fence_unref(&old_fence);
|
|
}
|
|
+ lobj->gpu_offset = radeon_bo_gpu_offset(bo);
|
|
+ lobj->tiling_flags = bo->tiling_flags;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
-void radeon_object_list_unvalidate(struct list_head *head)
|
|
+void radeon_bo_list_fence(struct list_head *head, void *fence)
|
|
{
|
|
- struct radeon_object_list *lobj;
|
|
+ struct radeon_bo_list *lobj;
|
|
+ struct radeon_bo *bo;
|
|
struct radeon_fence *old_fence = NULL;
|
|
|
|
list_for_each_entry(lobj, head, list) {
|
|
- old_fence = (struct radeon_fence *)lobj->robj->tobj.sync_obj;
|
|
- lobj->robj->tobj.sync_obj = NULL;
|
|
+ bo = lobj->bo;
|
|
+ spin_lock(&bo->tbo.lock);
|
|
+ old_fence = (struct radeon_fence *)bo->tbo.sync_obj;
|
|
+ bo->tbo.sync_obj = radeon_fence_ref(fence);
|
|
+ bo->tbo.sync_obj_arg = NULL;
|
|
+ spin_unlock(&bo->tbo.lock);
|
|
if (old_fence) {
|
|
radeon_fence_unref(&old_fence);
|
|
}
|
|
}
|
|
- radeon_object_list_unreserve(head);
|
|
}
|
|
|
|
-void radeon_object_list_clean(struct list_head *head)
|
|
-{
|
|
- radeon_object_list_unreserve(head);
|
|
-}
|
|
-
|
|
-int radeon_object_fbdev_mmap(struct radeon_object *robj,
|
|
+int radeon_bo_fbdev_mmap(struct radeon_bo *bo,
|
|
struct vm_area_struct *vma)
|
|
{
|
|
- return ttm_fbdev_mmap(vma, &robj->tobj);
|
|
-}
|
|
-
|
|
-unsigned long radeon_object_size(struct radeon_object *robj)
|
|
-{
|
|
- return robj->tobj.num_pages << PAGE_SHIFT;
|
|
+ return ttm_fbdev_mmap(vma, &bo->tbo);
|
|
}
|
|
|
|
-int radeon_object_get_surface_reg(struct radeon_object *robj)
|
|
+int radeon_bo_get_surface_reg(struct radeon_bo *bo)
|
|
{
|
|
- struct radeon_device *rdev = robj->rdev;
|
|
+ struct radeon_device *rdev = bo->rdev;
|
|
struct radeon_surface_reg *reg;
|
|
- struct radeon_object *old_object;
|
|
+ struct radeon_bo *old_object;
|
|
int steal;
|
|
int i;
|
|
|
|
- if (!robj->tiling_flags)
|
|
+ BUG_ON(!atomic_read(&bo->tbo.reserved));
|
|
+
|
|
+ if (!bo->tiling_flags)
|
|
return 0;
|
|
|
|
- if (robj->surface_reg >= 0) {
|
|
- reg = &rdev->surface_regs[robj->surface_reg];
|
|
- i = robj->surface_reg;
|
|
+ if (bo->surface_reg >= 0) {
|
|
+ reg = &rdev->surface_regs[bo->surface_reg];
|
|
+ i = bo->surface_reg;
|
|
goto out;
|
|
}
|
|
|
|
@@ -523,10 +385,10 @@ int radeon_object_get_surface_reg(struct radeon_object *robj)
|
|
for (i = 0; i < RADEON_GEM_MAX_SURFACES; i++) {
|
|
|
|
reg = &rdev->surface_regs[i];
|
|
- if (!reg->robj)
|
|
+ if (!reg->bo)
|
|
break;
|
|
|
|
- old_object = reg->robj;
|
|
+ old_object = reg->bo;
|
|
if (old_object->pin_count == 0)
|
|
steal = i;
|
|
}
|
|
@@ -537,91 +399,107 @@ int radeon_object_get_surface_reg(struct radeon_object *robj)
|
|
return -ENOMEM;
|
|
/* find someone with a surface reg and nuke their BO */
|
|
reg = &rdev->surface_regs[steal];
|
|
- old_object = reg->robj;
|
|
+ old_object = reg->bo;
|
|
/* blow away the mapping */
|
|
DRM_DEBUG("stealing surface reg %d from %p\n", steal, old_object);
|
|
- ttm_bo_unmap_virtual(&old_object->tobj);
|
|
+ ttm_bo_unmap_virtual(&old_object->tbo);
|
|
old_object->surface_reg = -1;
|
|
i = steal;
|
|
}
|
|
|
|
- robj->surface_reg = i;
|
|
- reg->robj = robj;
|
|
+ bo->surface_reg = i;
|
|
+ reg->bo = bo;
|
|
|
|
out:
|
|
- radeon_set_surface_reg(rdev, i, robj->tiling_flags, robj->pitch,
|
|
- robj->tobj.mem.mm_node->start << PAGE_SHIFT,
|
|
- robj->tobj.num_pages << PAGE_SHIFT);
|
|
+ radeon_set_surface_reg(rdev, i, bo->tiling_flags, bo->pitch,
|
|
+ bo->tbo.mem.mm_node->start << PAGE_SHIFT,
|
|
+ bo->tbo.num_pages << PAGE_SHIFT);
|
|
return 0;
|
|
}
|
|
|
|
-void radeon_object_clear_surface_reg(struct radeon_object *robj)
|
|
+static void radeon_bo_clear_surface_reg(struct radeon_bo *bo)
|
|
{
|
|
- struct radeon_device *rdev = robj->rdev;
|
|
+ struct radeon_device *rdev = bo->rdev;
|
|
struct radeon_surface_reg *reg;
|
|
|
|
- if (robj->surface_reg == -1)
|
|
+ if (bo->surface_reg == -1)
|
|
return;
|
|
|
|
- reg = &rdev->surface_regs[robj->surface_reg];
|
|
- radeon_clear_surface_reg(rdev, robj->surface_reg);
|
|
+ reg = &rdev->surface_regs[bo->surface_reg];
|
|
+ radeon_clear_surface_reg(rdev, bo->surface_reg);
|
|
|
|
- reg->robj = NULL;
|
|
- robj->surface_reg = -1;
|
|
+ reg->bo = NULL;
|
|
+ bo->surface_reg = -1;
|
|
}
|
|
|
|
-void radeon_object_set_tiling_flags(struct radeon_object *robj,
|
|
- uint32_t tiling_flags, uint32_t pitch)
|
|
+int radeon_bo_set_tiling_flags(struct radeon_bo *bo,
|
|
+ uint32_t tiling_flags, uint32_t pitch)
|
|
{
|
|
- robj->tiling_flags = tiling_flags;
|
|
- robj->pitch = pitch;
|
|
+ int r;
|
|
+
|
|
+ r = radeon_bo_reserve(bo, false);
|
|
+ if (unlikely(r != 0))
|
|
+ return r;
|
|
+ bo->tiling_flags = tiling_flags;
|
|
+ bo->pitch = pitch;
|
|
+ radeon_bo_unreserve(bo);
|
|
+ return 0;
|
|
}
|
|
|
|
-void radeon_object_get_tiling_flags(struct radeon_object *robj,
|
|
- uint32_t *tiling_flags,
|
|
- uint32_t *pitch)
|
|
+void radeon_bo_get_tiling_flags(struct radeon_bo *bo,
|
|
+ uint32_t *tiling_flags,
|
|
+ uint32_t *pitch)
|
|
{
|
|
+ BUG_ON(!atomic_read(&bo->tbo.reserved));
|
|
if (tiling_flags)
|
|
- *tiling_flags = robj->tiling_flags;
|
|
+ *tiling_flags = bo->tiling_flags;
|
|
if (pitch)
|
|
- *pitch = robj->pitch;
|
|
+ *pitch = bo->pitch;
|
|
}
|
|
|
|
-int radeon_object_check_tiling(struct radeon_object *robj, bool has_moved,
|
|
- bool force_drop)
|
|
+int radeon_bo_check_tiling(struct radeon_bo *bo, bool has_moved,
|
|
+ bool force_drop)
|
|
{
|
|
- if (!(robj->tiling_flags & RADEON_TILING_SURFACE))
|
|
+ BUG_ON(!atomic_read(&bo->tbo.reserved));
|
|
+
|
|
+ if (!(bo->tiling_flags & RADEON_TILING_SURFACE))
|
|
return 0;
|
|
|
|
if (force_drop) {
|
|
- radeon_object_clear_surface_reg(robj);
|
|
+ radeon_bo_clear_surface_reg(bo);
|
|
return 0;
|
|
}
|
|
|
|
- if (robj->tobj.mem.mem_type != TTM_PL_VRAM) {
|
|
+ if (bo->tbo.mem.mem_type != TTM_PL_VRAM) {
|
|
if (!has_moved)
|
|
return 0;
|
|
|
|
- if (robj->surface_reg >= 0)
|
|
- radeon_object_clear_surface_reg(robj);
|
|
+ if (bo->surface_reg >= 0)
|
|
+ radeon_bo_clear_surface_reg(bo);
|
|
return 0;
|
|
}
|
|
|
|
- if ((robj->surface_reg >= 0) && !has_moved)
|
|
+ if ((bo->surface_reg >= 0) && !has_moved)
|
|
return 0;
|
|
|
|
- return radeon_object_get_surface_reg(robj);
|
|
+ return radeon_bo_get_surface_reg(bo);
|
|
}
|
|
|
|
void radeon_bo_move_notify(struct ttm_buffer_object *bo,
|
|
- struct ttm_mem_reg *mem)
|
|
+ struct ttm_mem_reg *mem)
|
|
{
|
|
- struct radeon_object *robj = container_of(bo, struct radeon_object, tobj);
|
|
- radeon_object_check_tiling(robj, 0, 1);
|
|
+ struct radeon_bo *rbo;
|
|
+ if (!radeon_ttm_bo_is_radeon_bo(bo))
|
|
+ return;
|
|
+ rbo = container_of(bo, struct radeon_bo, tbo);
|
|
+ radeon_bo_check_tiling(rbo, 0, 1);
|
|
}
|
|
|
|
void radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo)
|
|
{
|
|
- struct radeon_object *robj = container_of(bo, struct radeon_object, tobj);
|
|
- radeon_object_check_tiling(robj, 0, 0);
|
|
+ struct radeon_bo *rbo;
|
|
+ if (!radeon_ttm_bo_is_radeon_bo(bo))
|
|
+ return;
|
|
+ rbo = container_of(bo, struct radeon_bo, tbo);
|
|
+ radeon_bo_check_tiling(rbo, 0, 0);
|
|
}
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_object.h b/drivers/gpu/drm/radeon/radeon_object.h
|
|
index 10e8af6..7ab43de 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_object.h
|
|
+++ b/drivers/gpu/drm/radeon/radeon_object.h
|
|
@@ -28,19 +28,146 @@
|
|
#ifndef __RADEON_OBJECT_H__
|
|
#define __RADEON_OBJECT_H__
|
|
|
|
-#include <ttm/ttm_bo_api.h>
|
|
-#include <ttm/ttm_bo_driver.h>
|
|
-#include <ttm/ttm_placement.h>
|
|
-#include <ttm/ttm_module.h>
|
|
+#include <drm/radeon_drm.h>
|
|
+#include "radeon.h"
|
|
|
|
-/*
|
|
- * TTM.
|
|
+/**
|
|
+ * radeon_mem_type_to_domain - return domain corresponding to mem_type
|
|
+ * @mem_type: ttm memory type
|
|
+ *
|
|
+ * Returns corresponding domain of the ttm mem_type
|
|
+ */
|
|
+static inline unsigned radeon_mem_type_to_domain(u32 mem_type)
|
|
+{
|
|
+ switch (mem_type) {
|
|
+ case TTM_PL_VRAM:
|
|
+ return RADEON_GEM_DOMAIN_VRAM;
|
|
+ case TTM_PL_TT:
|
|
+ return RADEON_GEM_DOMAIN_GTT;
|
|
+ case TTM_PL_SYSTEM:
|
|
+ return RADEON_GEM_DOMAIN_CPU;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * radeon_bo_reserve - reserve bo
|
|
+ * @bo: bo structure
|
|
+ * @no_wait: don't sleep while trying to reserve (return -EBUSY)
|
|
+ *
|
|
+ * Returns:
|
|
+ * -EBUSY: buffer is busy and @no_wait is true
|
|
+ * -ERESTARTSYS: A wait for the buffer to become unreserved was interrupted by
|
|
+ * a signal. Release all buffer reservations and return to user-space.
|
|
*/
|
|
-struct radeon_mman {
|
|
- struct ttm_bo_global_ref bo_global_ref;
|
|
- struct ttm_global_reference mem_global_ref;
|
|
- bool mem_global_referenced;
|
|
- struct ttm_bo_device bdev;
|
|
-};
|
|
+static inline int radeon_bo_reserve(struct radeon_bo *bo, bool no_wait)
|
|
+{
|
|
+ int r;
|
|
+
|
|
+ r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, 0);
|
|
+ if (unlikely(r != 0)) {
|
|
+ if (r != -ERESTARTSYS)
|
|
+ dev_err(bo->rdev->dev, "%p reserve failed\n", bo);
|
|
+ return r;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline void radeon_bo_unreserve(struct radeon_bo *bo)
|
|
+{
|
|
+ ttm_bo_unreserve(&bo->tbo);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * radeon_bo_gpu_offset - return GPU offset of bo
|
|
+ * @bo: radeon object for which we query the offset
|
|
+ *
|
|
+ * Returns current GPU offset of the object.
|
|
+ *
|
|
+ * Note: object should either be pinned or reserved when calling this
|
|
+ * function, it might be usefull to add check for this for debugging.
|
|
+ */
|
|
+static inline u64 radeon_bo_gpu_offset(struct radeon_bo *bo)
|
|
+{
|
|
+ return bo->tbo.offset;
|
|
+}
|
|
+
|
|
+static inline unsigned long radeon_bo_size(struct radeon_bo *bo)
|
|
+{
|
|
+ return bo->tbo.num_pages << PAGE_SHIFT;
|
|
+}
|
|
+
|
|
+static inline bool radeon_bo_is_reserved(struct radeon_bo *bo)
|
|
+{
|
|
+ return !!atomic_read(&bo->tbo.reserved);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * radeon_bo_mmap_offset - return mmap offset of bo
|
|
+ * @bo: radeon object for which we query the offset
|
|
+ *
|
|
+ * Returns mmap offset of the object.
|
|
+ *
|
|
+ * Note: addr_space_offset is constant after ttm bo init thus isn't protected
|
|
+ * by any lock.
|
|
+ */
|
|
+static inline u64 radeon_bo_mmap_offset(struct radeon_bo *bo)
|
|
+{
|
|
+ return bo->tbo.addr_space_offset;
|
|
+}
|
|
+
|
|
+static inline int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type,
|
|
+ bool no_wait)
|
|
+{
|
|
+ int r;
|
|
+
|
|
+ r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, 0);
|
|
+ if (unlikely(r != 0)) {
|
|
+ if (r != -ERESTARTSYS)
|
|
+ dev_err(bo->rdev->dev, "%p reserve failed for wait\n", bo);
|
|
+ return r;
|
|
+ }
|
|
+ spin_lock(&bo->tbo.lock);
|
|
+ if (mem_type)
|
|
+ *mem_type = bo->tbo.mem.mem_type;
|
|
+ if (bo->tbo.sync_obj)
|
|
+ r = ttm_bo_wait(&bo->tbo, true, true, no_wait);
|
|
+ spin_unlock(&bo->tbo.lock);
|
|
+ ttm_bo_unreserve(&bo->tbo);
|
|
+ return r;
|
|
+}
|
|
|
|
+extern int radeon_bo_create(struct radeon_device *rdev,
|
|
+ struct drm_gem_object *gobj, unsigned long size,
|
|
+ bool kernel, u32 domain,
|
|
+ struct radeon_bo **bo_ptr);
|
|
+extern int radeon_bo_kmap(struct radeon_bo *bo, void **ptr);
|
|
+extern void radeon_bo_kunmap(struct radeon_bo *bo);
|
|
+extern void radeon_bo_unref(struct radeon_bo **bo);
|
|
+extern int radeon_bo_pin(struct radeon_bo *bo, u32 domain, u64 *gpu_addr);
|
|
+extern int radeon_bo_unpin(struct radeon_bo *bo);
|
|
+extern int radeon_bo_evict_vram(struct radeon_device *rdev);
|
|
+extern void radeon_bo_force_delete(struct radeon_device *rdev);
|
|
+extern int radeon_bo_init(struct radeon_device *rdev);
|
|
+extern void radeon_bo_fini(struct radeon_device *rdev);
|
|
+extern void radeon_bo_list_add_object(struct radeon_bo_list *lobj,
|
|
+ struct list_head *head);
|
|
+extern int radeon_bo_list_reserve(struct list_head *head);
|
|
+extern void radeon_bo_list_unreserve(struct list_head *head);
|
|
+extern int radeon_bo_list_validate(struct list_head *head);
|
|
+extern void radeon_bo_list_fence(struct list_head *head, void *fence);
|
|
+extern int radeon_bo_fbdev_mmap(struct radeon_bo *bo,
|
|
+ struct vm_area_struct *vma);
|
|
+extern int radeon_bo_set_tiling_flags(struct radeon_bo *bo,
|
|
+ u32 tiling_flags, u32 pitch);
|
|
+extern void radeon_bo_get_tiling_flags(struct radeon_bo *bo,
|
|
+ u32 *tiling_flags, u32 *pitch);
|
|
+extern int radeon_bo_check_tiling(struct radeon_bo *bo, bool has_moved,
|
|
+ bool force_drop);
|
|
+extern void radeon_bo_move_notify(struct ttm_buffer_object *bo,
|
|
+ struct ttm_mem_reg *mem);
|
|
+extern void radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo);
|
|
+extern int radeon_bo_get_surface_reg(struct radeon_bo *bo);
|
|
#endif
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c
|
|
index 46146c6..8bce64c 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_pm.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_pm.c
|
|
@@ -27,7 +27,7 @@ int radeon_debugfs_pm_init(struct radeon_device *rdev);
|
|
int radeon_pm_init(struct radeon_device *rdev)
|
|
{
|
|
if (radeon_debugfs_pm_init(rdev)) {
|
|
- DRM_ERROR("Failed to register debugfs file for CP !\n");
|
|
+ DRM_ERROR("Failed to register debugfs file for PM!\n");
|
|
}
|
|
|
|
return 0;
|
|
@@ -44,8 +44,11 @@ static int radeon_debugfs_pm_info(struct seq_file *m, void *data)
|
|
struct drm_device *dev = node->minor->dev;
|
|
struct radeon_device *rdev = dev->dev_private;
|
|
|
|
- seq_printf(m, "engine clock: %u0 Hz\n", radeon_get_engine_clock(rdev));
|
|
- seq_printf(m, "memory clock: %u0 Hz\n", radeon_get_memory_clock(rdev));
|
|
+ seq_printf(m, "default engine clock: %u0 kHz\n", rdev->clock.default_sclk);
|
|
+ seq_printf(m, "current engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev));
|
|
+ seq_printf(m, "default memory clock: %u0 kHz\n", rdev->clock.default_mclk);
|
|
+ if (rdev->asic->get_memory_clock)
|
|
+ seq_printf(m, "current memory clock: %u0 kHz\n", radeon_get_memory_clock(rdev));
|
|
|
|
return 0;
|
|
}
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_reg.h b/drivers/gpu/drm/radeon/radeon_reg.h
|
|
index 29ab759..6d0a009 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_reg.h
|
|
+++ b/drivers/gpu/drm/radeon/radeon_reg.h
|
|
@@ -887,6 +887,7 @@
|
|
# define RADEON_FP_PANEL_FORMAT (1 << 3)
|
|
# define RADEON_FP_EN_TMDS (1 << 7)
|
|
# define RADEON_FP_DETECT_SENSE (1 << 8)
|
|
+# define RADEON_FP_DETECT_INT_POL (1 << 9)
|
|
# define R200_FP_SOURCE_SEL_MASK (3 << 10)
|
|
# define R200_FP_SOURCE_SEL_CRTC1 (0 << 10)
|
|
# define R200_FP_SOURCE_SEL_CRTC2 (1 << 10)
|
|
@@ -894,6 +895,7 @@
|
|
# define R200_FP_SOURCE_SEL_TRANS (3 << 10)
|
|
# define RADEON_FP_SEL_CRTC1 (0 << 13)
|
|
# define RADEON_FP_SEL_CRTC2 (1 << 13)
|
|
+# define R300_HPD_SEL(x) ((x) << 13)
|
|
# define RADEON_FP_CRTC_DONT_SHADOW_HPAR (1 << 15)
|
|
# define RADEON_FP_CRTC_DONT_SHADOW_VPAR (1 << 16)
|
|
# define RADEON_FP_CRTC_DONT_SHADOW_HEND (1 << 17)
|
|
@@ -909,6 +911,7 @@
|
|
# define RADEON_FP2_ON (1 << 2)
|
|
# define RADEON_FP2_PANEL_FORMAT (1 << 3)
|
|
# define RADEON_FP2_DETECT_SENSE (1 << 8)
|
|
+# define RADEON_FP2_DETECT_INT_POL (1 << 9)
|
|
# define R200_FP2_SOURCE_SEL_MASK (3 << 10)
|
|
# define R200_FP2_SOURCE_SEL_CRTC1 (0 << 10)
|
|
# define R200_FP2_SOURCE_SEL_CRTC2 (1 << 10)
|
|
@@ -988,14 +991,20 @@
|
|
|
|
#define RADEON_GEN_INT_CNTL 0x0040
|
|
# define RADEON_CRTC_VBLANK_MASK (1 << 0)
|
|
+# define RADEON_FP_DETECT_MASK (1 << 4)
|
|
# define RADEON_CRTC2_VBLANK_MASK (1 << 9)
|
|
+# define RADEON_FP2_DETECT_MASK (1 << 10)
|
|
# define RADEON_SW_INT_ENABLE (1 << 25)
|
|
#define RADEON_GEN_INT_STATUS 0x0044
|
|
# define AVIVO_DISPLAY_INT_STATUS (1 << 0)
|
|
# define RADEON_CRTC_VBLANK_STAT (1 << 0)
|
|
# define RADEON_CRTC_VBLANK_STAT_ACK (1 << 0)
|
|
+# define RADEON_FP_DETECT_STAT (1 << 4)
|
|
+# define RADEON_FP_DETECT_STAT_ACK (1 << 4)
|
|
# define RADEON_CRTC2_VBLANK_STAT (1 << 9)
|
|
# define RADEON_CRTC2_VBLANK_STAT_ACK (1 << 9)
|
|
+# define RADEON_FP2_DETECT_STAT (1 << 10)
|
|
+# define RADEON_FP2_DETECT_STAT_ACK (1 << 10)
|
|
# define RADEON_SW_INT_FIRE (1 << 26)
|
|
# define RADEON_SW_INT_TEST (1 << 25)
|
|
# define RADEON_SW_INT_TEST_ACK (1 << 25)
|
|
@@ -1051,20 +1060,25 @@
|
|
|
|
/* Multimedia I2C bus */
|
|
#define RADEON_I2C_CNTL_0 0x0090
|
|
-#define RADEON_I2C_DONE (1<<0)
|
|
-#define RADEON_I2C_NACK (1<<1)
|
|
-#define RADEON_I2C_HALT (1<<2)
|
|
-#define RADEON_I2C_SOFT_RST (1<<5)
|
|
-#define RADEON_I2C_DRIVE_EN (1<<6)
|
|
-#define RADEON_I2C_DRIVE_SEL (1<<7)
|
|
-#define RADEON_I2C_START (1<<8)
|
|
-#define RADEON_I2C_STOP (1<<9)
|
|
-#define RADEON_I2C_RECEIVE (1<<10)
|
|
-#define RADEON_I2C_ABORT (1<<11)
|
|
-#define RADEON_I2C_GO (1<<12)
|
|
+#define RADEON_I2C_DONE (1 << 0)
|
|
+#define RADEON_I2C_NACK (1 << 1)
|
|
+#define RADEON_I2C_HALT (1 << 2)
|
|
+#define RADEON_I2C_SOFT_RST (1 << 5)
|
|
+#define RADEON_I2C_DRIVE_EN (1 << 6)
|
|
+#define RADEON_I2C_DRIVE_SEL (1 << 7)
|
|
+#define RADEON_I2C_START (1 << 8)
|
|
+#define RADEON_I2C_STOP (1 << 9)
|
|
+#define RADEON_I2C_RECEIVE (1 << 10)
|
|
+#define RADEON_I2C_ABORT (1 << 11)
|
|
+#define RADEON_I2C_GO (1 << 12)
|
|
+#define RADEON_I2C_PRESCALE_SHIFT 16
|
|
#define RADEON_I2C_CNTL_1 0x0094
|
|
-#define RADEON_I2C_SEL (1<<16)
|
|
-#define RADEON_I2C_EN (1<<17)
|
|
+#define RADEON_I2C_DATA_COUNT_SHIFT 0
|
|
+#define RADEON_I2C_ADDR_COUNT_SHIFT 4
|
|
+#define RADEON_I2C_INTRA_BYTE_DELAY_SHIFT 8
|
|
+#define RADEON_I2C_SEL (1 << 16)
|
|
+#define RADEON_I2C_EN (1 << 17)
|
|
+#define RADEON_I2C_TIME_LIMIT_SHIFT 24
|
|
#define RADEON_I2C_DATA 0x0098
|
|
|
|
#define RADEON_DVI_I2C_CNTL_0 0x02e0
|
|
@@ -1072,7 +1086,7 @@
|
|
# define R200_SEL_DDC1 0 /* 0x60 - VGA_DDC */
|
|
# define R200_SEL_DDC2 1 /* 0x64 - DVI_DDC */
|
|
# define R200_SEL_DDC3 2 /* 0x68 - MONID_DDC */
|
|
-#define RADEON_DVI_I2C_CNTL_1 0x02e4 /* ? */
|
|
+#define RADEON_DVI_I2C_CNTL_1 0x02e4
|
|
#define RADEON_DVI_I2C_DATA 0x02e8
|
|
|
|
#define RADEON_INTERRUPT_LINE 0x0f3c /* PCI */
|
|
@@ -1143,15 +1157,16 @@
|
|
# define RADEON_IO_MCLK_MAX_DYN_STOP_LAT (1 << 13)
|
|
# define RADEON_MC_MCLK_DYN_ENABLE (1 << 14)
|
|
# define RADEON_IO_MCLK_DYN_ENABLE (1 << 15)
|
|
-#define RADEON_LCD_GPIO_MASK 0x01a0
|
|
-#define RADEON_GPIOPAD_EN 0x01a0
|
|
-#define RADEON_LCD_GPIO_Y_REG 0x01a4
|
|
-#define RADEON_MDGPIO_A_REG 0x01ac
|
|
-#define RADEON_MDGPIO_EN_REG 0x01b0
|
|
-#define RADEON_MDGPIO_MASK 0x0198
|
|
+
|
|
#define RADEON_GPIOPAD_MASK 0x0198
|
|
#define RADEON_GPIOPAD_A 0x019c
|
|
-#define RADEON_MDGPIO_Y_REG 0x01b4
|
|
+#define RADEON_GPIOPAD_EN 0x01a0
|
|
+#define RADEON_GPIOPAD_Y 0x01a4
|
|
+#define RADEON_MDGPIO_MASK 0x01a8
|
|
+#define RADEON_MDGPIO_A 0x01ac
|
|
+#define RADEON_MDGPIO_EN 0x01b0
|
|
+#define RADEON_MDGPIO_Y 0x01b4
|
|
+
|
|
#define RADEON_MEM_ADDR_CONFIG 0x0148
|
|
#define RADEON_MEM_BASE 0x0f10 /* PCI */
|
|
#define RADEON_MEM_CNTL 0x0140
|
|
@@ -1360,6 +1375,9 @@
|
|
#define RADEON_OVR_CLR 0x0230
|
|
#define RADEON_OVR_WID_LEFT_RIGHT 0x0234
|
|
#define RADEON_OVR_WID_TOP_BOTTOM 0x0238
|
|
+#define RADEON_OVR2_CLR 0x0330
|
|
+#define RADEON_OVR2_WID_LEFT_RIGHT 0x0334
|
|
+#define RADEON_OVR2_WID_TOP_BOTTOM 0x0338
|
|
|
|
/* first capture unit */
|
|
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c
|
|
index 747b4bf..694799f 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_ring.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_ring.c
|
|
@@ -41,68 +41,55 @@ int radeon_ib_get(struct radeon_device *rdev, struct radeon_ib **ib)
|
|
{
|
|
struct radeon_fence *fence;
|
|
struct radeon_ib *nib;
|
|
- unsigned long i;
|
|
- int r = 0;
|
|
+ int r = 0, i, c;
|
|
|
|
*ib = NULL;
|
|
r = radeon_fence_create(rdev, &fence);
|
|
if (r) {
|
|
- DRM_ERROR("failed to create fence for new IB\n");
|
|
+ dev_err(rdev->dev, "failed to create fence for new IB\n");
|
|
return r;
|
|
}
|
|
mutex_lock(&rdev->ib_pool.mutex);
|
|
- i = find_first_zero_bit(rdev->ib_pool.alloc_bm, RADEON_IB_POOL_SIZE);
|
|
- if (i < RADEON_IB_POOL_SIZE) {
|
|
- set_bit(i, rdev->ib_pool.alloc_bm);
|
|
- rdev->ib_pool.ibs[i].length_dw = 0;
|
|
- *ib = &rdev->ib_pool.ibs[i];
|
|
- mutex_unlock(&rdev->ib_pool.mutex);
|
|
- goto out;
|
|
+ for (i = rdev->ib_pool.head_id, c = 0, nib = NULL; c < RADEON_IB_POOL_SIZE; c++, i++) {
|
|
+ i &= (RADEON_IB_POOL_SIZE - 1);
|
|
+ if (rdev->ib_pool.ibs[i].free) {
|
|
+ nib = &rdev->ib_pool.ibs[i];
|
|
+ break;
|
|
+ }
|
|
}
|
|
- if (list_empty(&rdev->ib_pool.scheduled_ibs)) {
|
|
- /* we go do nothings here */
|
|
+ if (nib == NULL) {
|
|
+ /* This should never happen, it means we allocated all
|
|
+ * IB and haven't scheduled one yet, return EBUSY to
|
|
+ * userspace hoping that on ioctl recall we get better
|
|
+ * luck
|
|
+ */
|
|
+ dev_err(rdev->dev, "no free indirect buffer !\n");
|
|
mutex_unlock(&rdev->ib_pool.mutex);
|
|
- DRM_ERROR("all IB allocated none scheduled.\n");
|
|
- r = -EINVAL;
|
|
- goto out;
|
|
+ radeon_fence_unref(&fence);
|
|
+ return -EBUSY;
|
|
}
|
|
- /* get the first ib on the scheduled list */
|
|
- nib = list_entry(rdev->ib_pool.scheduled_ibs.next,
|
|
- struct radeon_ib, list);
|
|
- if (nib->fence == NULL) {
|
|
- /* we go do nothings here */
|
|
+ rdev->ib_pool.head_id = (nib->idx + 1) & (RADEON_IB_POOL_SIZE - 1);
|
|
+ nib->free = false;
|
|
+ if (nib->fence) {
|
|
mutex_unlock(&rdev->ib_pool.mutex);
|
|
- DRM_ERROR("IB %lu scheduled without a fence.\n", nib->idx);
|
|
- r = -EINVAL;
|
|
- goto out;
|
|
- }
|
|
- mutex_unlock(&rdev->ib_pool.mutex);
|
|
-
|
|
- r = radeon_fence_wait(nib->fence, false);
|
|
- if (r) {
|
|
- DRM_ERROR("radeon: IB(%lu:0x%016lX:%u)\n", nib->idx,
|
|
- (unsigned long)nib->gpu_addr, nib->length_dw);
|
|
- DRM_ERROR("radeon: GPU lockup detected, fail to get a IB\n");
|
|
- goto out;
|
|
+ r = radeon_fence_wait(nib->fence, false);
|
|
+ if (r) {
|
|
+ dev_err(rdev->dev, "error waiting fence of IB(%u:0x%016lX:%u)\n",
|
|
+ nib->idx, (unsigned long)nib->gpu_addr, nib->length_dw);
|
|
+ mutex_lock(&rdev->ib_pool.mutex);
|
|
+ nib->free = true;
|
|
+ mutex_unlock(&rdev->ib_pool.mutex);
|
|
+ radeon_fence_unref(&fence);
|
|
+ return r;
|
|
+ }
|
|
+ mutex_lock(&rdev->ib_pool.mutex);
|
|
}
|
|
radeon_fence_unref(&nib->fence);
|
|
-
|
|
+ nib->fence = fence;
|
|
nib->length_dw = 0;
|
|
-
|
|
- /* scheduled list is accessed here */
|
|
- mutex_lock(&rdev->ib_pool.mutex);
|
|
- list_del(&nib->list);
|
|
- INIT_LIST_HEAD(&nib->list);
|
|
mutex_unlock(&rdev->ib_pool.mutex);
|
|
-
|
|
*ib = nib;
|
|
-out:
|
|
- if (r) {
|
|
- radeon_fence_unref(&fence);
|
|
- } else {
|
|
- (*ib)->fence = fence;
|
|
- }
|
|
- return r;
|
|
+ return 0;
|
|
}
|
|
|
|
void radeon_ib_free(struct radeon_device *rdev, struct radeon_ib **ib)
|
|
@@ -114,18 +101,7 @@ void radeon_ib_free(struct radeon_device *rdev, struct radeon_ib **ib)
|
|
return;
|
|
}
|
|
mutex_lock(&rdev->ib_pool.mutex);
|
|
- if (!list_empty(&tmp->list) && !radeon_fence_signaled(tmp->fence)) {
|
|
- /* IB is scheduled & not signaled don't do anythings */
|
|
- mutex_unlock(&rdev->ib_pool.mutex);
|
|
- return;
|
|
- }
|
|
- list_del(&tmp->list);
|
|
- INIT_LIST_HEAD(&tmp->list);
|
|
- if (tmp->fence)
|
|
- radeon_fence_unref(&tmp->fence);
|
|
-
|
|
- tmp->length_dw = 0;
|
|
- clear_bit(tmp->idx, rdev->ib_pool.alloc_bm);
|
|
+ tmp->free = true;
|
|
mutex_unlock(&rdev->ib_pool.mutex);
|
|
}
|
|
|
|
@@ -135,7 +111,7 @@ int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib)
|
|
|
|
if (!ib->length_dw || !rdev->cp.ready) {
|
|
/* TODO: Nothings in the ib we should report. */
|
|
- DRM_ERROR("radeon: couldn't schedule IB(%lu).\n", ib->idx);
|
|
+ DRM_ERROR("radeon: couldn't schedule IB(%u).\n", ib->idx);
|
|
return -EINVAL;
|
|
}
|
|
|
|
@@ -148,7 +124,8 @@ int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib)
|
|
radeon_ring_ib_execute(rdev, ib);
|
|
radeon_fence_emit(rdev, ib->fence);
|
|
mutex_lock(&rdev->ib_pool.mutex);
|
|
- list_add_tail(&ib->list, &rdev->ib_pool.scheduled_ibs);
|
|
+ /* once scheduled IB is considered free and protected by the fence */
|
|
+ ib->free = true;
|
|
mutex_unlock(&rdev->ib_pool.mutex);
|
|
radeon_ring_unlock_commit(rdev);
|
|
return 0;
|
|
@@ -164,20 +141,24 @@ int radeon_ib_pool_init(struct radeon_device *rdev)
|
|
if (rdev->ib_pool.robj)
|
|
return 0;
|
|
/* Allocate 1M object buffer */
|
|
- INIT_LIST_HEAD(&rdev->ib_pool.scheduled_ibs);
|
|
- r = radeon_object_create(rdev, NULL, RADEON_IB_POOL_SIZE*64*1024,
|
|
- true, RADEON_GEM_DOMAIN_GTT,
|
|
- false, &rdev->ib_pool.robj);
|
|
+ r = radeon_bo_create(rdev, NULL, RADEON_IB_POOL_SIZE*64*1024,
|
|
+ true, RADEON_GEM_DOMAIN_GTT,
|
|
+ &rdev->ib_pool.robj);
|
|
if (r) {
|
|
DRM_ERROR("radeon: failed to ib pool (%d).\n", r);
|
|
return r;
|
|
}
|
|
- r = radeon_object_pin(rdev->ib_pool.robj, RADEON_GEM_DOMAIN_GTT, &gpu_addr);
|
|
+ r = radeon_bo_reserve(rdev->ib_pool.robj, false);
|
|
+ if (unlikely(r != 0))
|
|
+ return r;
|
|
+ r = radeon_bo_pin(rdev->ib_pool.robj, RADEON_GEM_DOMAIN_GTT, &gpu_addr);
|
|
if (r) {
|
|
+ radeon_bo_unreserve(rdev->ib_pool.robj);
|
|
DRM_ERROR("radeon: failed to pin ib pool (%d).\n", r);
|
|
return r;
|
|
}
|
|
- r = radeon_object_kmap(rdev->ib_pool.robj, &ptr);
|
|
+ r = radeon_bo_kmap(rdev->ib_pool.robj, &ptr);
|
|
+ radeon_bo_unreserve(rdev->ib_pool.robj);
|
|
if (r) {
|
|
DRM_ERROR("radeon: failed to map ib poll (%d).\n", r);
|
|
return r;
|
|
@@ -190,9 +171,9 @@ int radeon_ib_pool_init(struct radeon_device *rdev)
|
|
rdev->ib_pool.ibs[i].ptr = ptr + offset;
|
|
rdev->ib_pool.ibs[i].idx = i;
|
|
rdev->ib_pool.ibs[i].length_dw = 0;
|
|
- INIT_LIST_HEAD(&rdev->ib_pool.ibs[i].list);
|
|
+ rdev->ib_pool.ibs[i].free = true;
|
|
}
|
|
- bitmap_zero(rdev->ib_pool.alloc_bm, RADEON_IB_POOL_SIZE);
|
|
+ rdev->ib_pool.head_id = 0;
|
|
rdev->ib_pool.ready = true;
|
|
DRM_INFO("radeon: ib pool ready.\n");
|
|
if (radeon_debugfs_ib_init(rdev)) {
|
|
@@ -203,14 +184,20 @@ int radeon_ib_pool_init(struct radeon_device *rdev)
|
|
|
|
void radeon_ib_pool_fini(struct radeon_device *rdev)
|
|
{
|
|
+ int r;
|
|
+
|
|
if (!rdev->ib_pool.ready) {
|
|
return;
|
|
}
|
|
mutex_lock(&rdev->ib_pool.mutex);
|
|
- bitmap_zero(rdev->ib_pool.alloc_bm, RADEON_IB_POOL_SIZE);
|
|
if (rdev->ib_pool.robj) {
|
|
- radeon_object_kunmap(rdev->ib_pool.robj);
|
|
- radeon_object_unref(&rdev->ib_pool.robj);
|
|
+ r = radeon_bo_reserve(rdev->ib_pool.robj, false);
|
|
+ if (likely(r == 0)) {
|
|
+ radeon_bo_kunmap(rdev->ib_pool.robj);
|
|
+ radeon_bo_unpin(rdev->ib_pool.robj);
|
|
+ radeon_bo_unreserve(rdev->ib_pool.robj);
|
|
+ }
|
|
+ radeon_bo_unref(&rdev->ib_pool.robj);
|
|
rdev->ib_pool.robj = NULL;
|
|
}
|
|
mutex_unlock(&rdev->ib_pool.mutex);
|
|
@@ -288,29 +275,28 @@ int radeon_ring_init(struct radeon_device *rdev, unsigned ring_size)
|
|
rdev->cp.ring_size = ring_size;
|
|
/* Allocate ring buffer */
|
|
if (rdev->cp.ring_obj == NULL) {
|
|
- r = radeon_object_create(rdev, NULL, rdev->cp.ring_size,
|
|
- true,
|
|
- RADEON_GEM_DOMAIN_GTT,
|
|
- false,
|
|
- &rdev->cp.ring_obj);
|
|
+ r = radeon_bo_create(rdev, NULL, rdev->cp.ring_size, true,
|
|
+ RADEON_GEM_DOMAIN_GTT,
|
|
+ &rdev->cp.ring_obj);
|
|
if (r) {
|
|
- DRM_ERROR("radeon: failed to create ring buffer (%d).\n", r);
|
|
- mutex_unlock(&rdev->cp.mutex);
|
|
+ dev_err(rdev->dev, "(%d) ring create failed\n", r);
|
|
return r;
|
|
}
|
|
- r = radeon_object_pin(rdev->cp.ring_obj,
|
|
- RADEON_GEM_DOMAIN_GTT,
|
|
- &rdev->cp.gpu_addr);
|
|
+ r = radeon_bo_reserve(rdev->cp.ring_obj, false);
|
|
+ if (unlikely(r != 0))
|
|
+ return r;
|
|
+ r = radeon_bo_pin(rdev->cp.ring_obj, RADEON_GEM_DOMAIN_GTT,
|
|
+ &rdev->cp.gpu_addr);
|
|
if (r) {
|
|
- DRM_ERROR("radeon: failed to pin ring buffer (%d).\n", r);
|
|
- mutex_unlock(&rdev->cp.mutex);
|
|
+ radeon_bo_unreserve(rdev->cp.ring_obj);
|
|
+ dev_err(rdev->dev, "(%d) ring pin failed\n", r);
|
|
return r;
|
|
}
|
|
- r = radeon_object_kmap(rdev->cp.ring_obj,
|
|
+ r = radeon_bo_kmap(rdev->cp.ring_obj,
|
|
(void **)&rdev->cp.ring);
|
|
+ radeon_bo_unreserve(rdev->cp.ring_obj);
|
|
if (r) {
|
|
- DRM_ERROR("radeon: failed to map ring buffer (%d).\n", r);
|
|
- mutex_unlock(&rdev->cp.mutex);
|
|
+ dev_err(rdev->dev, "(%d) ring map failed\n", r);
|
|
return r;
|
|
}
|
|
}
|
|
@@ -321,11 +307,17 @@ int radeon_ring_init(struct radeon_device *rdev, unsigned ring_size)
|
|
|
|
void radeon_ring_fini(struct radeon_device *rdev)
|
|
{
|
|
+ int r;
|
|
+
|
|
mutex_lock(&rdev->cp.mutex);
|
|
if (rdev->cp.ring_obj) {
|
|
- radeon_object_kunmap(rdev->cp.ring_obj);
|
|
- radeon_object_unpin(rdev->cp.ring_obj);
|
|
- radeon_object_unref(&rdev->cp.ring_obj);
|
|
+ r = radeon_bo_reserve(rdev->cp.ring_obj, false);
|
|
+ if (likely(r == 0)) {
|
|
+ radeon_bo_kunmap(rdev->cp.ring_obj);
|
|
+ radeon_bo_unpin(rdev->cp.ring_obj);
|
|
+ radeon_bo_unreserve(rdev->cp.ring_obj);
|
|
+ }
|
|
+ radeon_bo_unref(&rdev->cp.ring_obj);
|
|
rdev->cp.ring = NULL;
|
|
rdev->cp.ring_obj = NULL;
|
|
}
|
|
@@ -346,7 +338,7 @@ static int radeon_debugfs_ib_info(struct seq_file *m, void *data)
|
|
if (ib == NULL) {
|
|
return 0;
|
|
}
|
|
- seq_printf(m, "IB %04lu\n", ib->idx);
|
|
+ seq_printf(m, "IB %04u\n", ib->idx);
|
|
seq_printf(m, "IB fence %p\n", ib->fence);
|
|
seq_printf(m, "IB size %05u dwords\n", ib->length_dw);
|
|
for (i = 0; i < ib->length_dw; i++) {
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_state.c b/drivers/gpu/drm/radeon/radeon_state.c
|
|
index 38537d9..067167c 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_state.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_state.c
|
|
@@ -1950,7 +1950,7 @@ static void radeon_apply_surface_regs(int surf_index,
|
|
* Note that refcount can be at most 2, since during a free refcount=3
|
|
* might mean we have to allocate a new surface which might not always
|
|
* be available.
|
|
- * For example : we allocate three contigous surfaces ABC. If B is
|
|
+ * For example : we allocate three contiguous surfaces ABC. If B is
|
|
* freed, we suddenly need two surfaces to store A and C, which might
|
|
* not always be available.
|
|
*/
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_test.c b/drivers/gpu/drm/radeon/radeon_test.c
|
|
index c8942ca..9f5e2f9 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_test.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_test.c
|
|
@@ -30,8 +30,8 @@
|
|
/* Test BO GTT->VRAM and VRAM->GTT GPU copies across the whole GTT aperture */
|
|
void radeon_test_moves(struct radeon_device *rdev)
|
|
{
|
|
- struct radeon_object *vram_obj = NULL;
|
|
- struct radeon_object **gtt_obj = NULL;
|
|
+ struct radeon_bo *vram_obj = NULL;
|
|
+ struct radeon_bo **gtt_obj = NULL;
|
|
struct radeon_fence *fence = NULL;
|
|
uint64_t gtt_addr, vram_addr;
|
|
unsigned i, n, size;
|
|
@@ -52,38 +52,42 @@ void radeon_test_moves(struct radeon_device *rdev)
|
|
goto out_cleanup;
|
|
}
|
|
|
|
- r = radeon_object_create(rdev, NULL, size, true, RADEON_GEM_DOMAIN_VRAM,
|
|
- false, &vram_obj);
|
|
+ r = radeon_bo_create(rdev, NULL, size, true, RADEON_GEM_DOMAIN_VRAM,
|
|
+ &vram_obj);
|
|
if (r) {
|
|
DRM_ERROR("Failed to create VRAM object\n");
|
|
goto out_cleanup;
|
|
}
|
|
-
|
|
- r = radeon_object_pin(vram_obj, RADEON_GEM_DOMAIN_VRAM, &vram_addr);
|
|
+ r = radeon_bo_reserve(vram_obj, false);
|
|
+ if (unlikely(r != 0))
|
|
+ goto out_cleanup;
|
|
+ r = radeon_bo_pin(vram_obj, RADEON_GEM_DOMAIN_VRAM, &vram_addr);
|
|
if (r) {
|
|
DRM_ERROR("Failed to pin VRAM object\n");
|
|
goto out_cleanup;
|
|
}
|
|
-
|
|
for (i = 0; i < n; i++) {
|
|
void *gtt_map, *vram_map;
|
|
void **gtt_start, **gtt_end;
|
|
void **vram_start, **vram_end;
|
|
|
|
- r = radeon_object_create(rdev, NULL, size, true,
|
|
- RADEON_GEM_DOMAIN_GTT, false, gtt_obj + i);
|
|
+ r = radeon_bo_create(rdev, NULL, size, true,
|
|
+ RADEON_GEM_DOMAIN_GTT, gtt_obj + i);
|
|
if (r) {
|
|
DRM_ERROR("Failed to create GTT object %d\n", i);
|
|
goto out_cleanup;
|
|
}
|
|
|
|
- r = radeon_object_pin(gtt_obj[i], RADEON_GEM_DOMAIN_GTT, >t_addr);
|
|
+ r = radeon_bo_reserve(gtt_obj[i], false);
|
|
+ if (unlikely(r != 0))
|
|
+ goto out_cleanup;
|
|
+ r = radeon_bo_pin(gtt_obj[i], RADEON_GEM_DOMAIN_GTT, >t_addr);
|
|
if (r) {
|
|
DRM_ERROR("Failed to pin GTT object %d\n", i);
|
|
goto out_cleanup;
|
|
}
|
|
|
|
- r = radeon_object_kmap(gtt_obj[i], >t_map);
|
|
+ r = radeon_bo_kmap(gtt_obj[i], >t_map);
|
|
if (r) {
|
|
DRM_ERROR("Failed to map GTT object %d\n", i);
|
|
goto out_cleanup;
|
|
@@ -94,7 +98,7 @@ void radeon_test_moves(struct radeon_device *rdev)
|
|
gtt_start++)
|
|
*gtt_start = gtt_start;
|
|
|
|
- radeon_object_kunmap(gtt_obj[i]);
|
|
+ radeon_bo_kunmap(gtt_obj[i]);
|
|
|
|
r = radeon_fence_create(rdev, &fence);
|
|
if (r) {
|
|
@@ -116,7 +120,7 @@ void radeon_test_moves(struct radeon_device *rdev)
|
|
|
|
radeon_fence_unref(&fence);
|
|
|
|
- r = radeon_object_kmap(vram_obj, &vram_map);
|
|
+ r = radeon_bo_kmap(vram_obj, &vram_map);
|
|
if (r) {
|
|
DRM_ERROR("Failed to map VRAM object after copy %d\n", i);
|
|
goto out_cleanup;
|
|
@@ -131,13 +135,13 @@ void radeon_test_moves(struct radeon_device *rdev)
|
|
"expected 0x%p (GTT map 0x%p-0x%p)\n",
|
|
i, *vram_start, gtt_start, gtt_map,
|
|
gtt_end);
|
|
- radeon_object_kunmap(vram_obj);
|
|
+ radeon_bo_kunmap(vram_obj);
|
|
goto out_cleanup;
|
|
}
|
|
*vram_start = vram_start;
|
|
}
|
|
|
|
- radeon_object_kunmap(vram_obj);
|
|
+ radeon_bo_kunmap(vram_obj);
|
|
|
|
r = radeon_fence_create(rdev, &fence);
|
|
if (r) {
|
|
@@ -159,7 +163,7 @@ void radeon_test_moves(struct radeon_device *rdev)
|
|
|
|
radeon_fence_unref(&fence);
|
|
|
|
- r = radeon_object_kmap(gtt_obj[i], >t_map);
|
|
+ r = radeon_bo_kmap(gtt_obj[i], >t_map);
|
|
if (r) {
|
|
DRM_ERROR("Failed to map GTT object after copy %d\n", i);
|
|
goto out_cleanup;
|
|
@@ -174,12 +178,12 @@ void radeon_test_moves(struct radeon_device *rdev)
|
|
"expected 0x%p (VRAM map 0x%p-0x%p)\n",
|
|
i, *gtt_start, vram_start, vram_map,
|
|
vram_end);
|
|
- radeon_object_kunmap(gtt_obj[i]);
|
|
+ radeon_bo_kunmap(gtt_obj[i]);
|
|
goto out_cleanup;
|
|
}
|
|
}
|
|
|
|
- radeon_object_kunmap(gtt_obj[i]);
|
|
+ radeon_bo_kunmap(gtt_obj[i]);
|
|
|
|
DRM_INFO("Tested GTT->VRAM and VRAM->GTT copy for GTT offset 0x%llx\n",
|
|
gtt_addr - rdev->mc.gtt_location);
|
|
@@ -187,14 +191,20 @@ void radeon_test_moves(struct radeon_device *rdev)
|
|
|
|
out_cleanup:
|
|
if (vram_obj) {
|
|
- radeon_object_unpin(vram_obj);
|
|
- radeon_object_unref(&vram_obj);
|
|
+ if (radeon_bo_is_reserved(vram_obj)) {
|
|
+ radeon_bo_unpin(vram_obj);
|
|
+ radeon_bo_unreserve(vram_obj);
|
|
+ }
|
|
+ radeon_bo_unref(&vram_obj);
|
|
}
|
|
if (gtt_obj) {
|
|
for (i = 0; i < n; i++) {
|
|
if (gtt_obj[i]) {
|
|
- radeon_object_unpin(gtt_obj[i]);
|
|
- radeon_object_unref(>t_obj[i]);
|
|
+ if (radeon_bo_is_reserved(gtt_obj[i])) {
|
|
+ radeon_bo_unpin(gtt_obj[i]);
|
|
+ radeon_bo_unreserve(gtt_obj[i]);
|
|
+ }
|
|
+ radeon_bo_unref(>t_obj[i]);
|
|
}
|
|
}
|
|
kfree(gtt_obj);
|
|
@@ -206,4 +216,3 @@ out_cleanup:
|
|
printk(KERN_WARNING "Error while testing BO move.\n");
|
|
}
|
|
}
|
|
-
|
|
diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c
|
|
index 1381e06..58b5adf 100644
|
|
--- a/drivers/gpu/drm/radeon/radeon_ttm.c
|
|
+++ b/drivers/gpu/drm/radeon/radeon_ttm.c
|
|
@@ -150,7 +150,7 @@ static int radeon_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
|
|
man->default_caching = TTM_PL_FLAG_CACHED;
|
|
break;
|
|
case TTM_PL_TT:
|
|
- man->gpu_offset = 0;
|
|
+ man->gpu_offset = rdev->mc.gtt_location;
|
|
man->available_caching = TTM_PL_MASK_CACHING;
|
|
man->default_caching = TTM_PL_FLAG_CACHED;
|
|
man->flags = TTM_MEMTYPE_FLAG_MAPPABLE | TTM_MEMTYPE_FLAG_CMA;
|
|
@@ -180,7 +180,7 @@ static int radeon_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
|
|
break;
|
|
case TTM_PL_VRAM:
|
|
/* "On-card" video ram */
|
|
- man->gpu_offset = 0;
|
|
+ man->gpu_offset = rdev->mc.vram_location;
|
|
man->flags = TTM_MEMTYPE_FLAG_FIXED |
|
|
TTM_MEMTYPE_FLAG_NEEDS_IOREMAP |
|
|
TTM_MEMTYPE_FLAG_MAPPABLE;
|
|
@@ -197,16 +197,34 @@ static int radeon_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
|
|
return 0;
|
|
}
|
|
|
|
-static uint32_t radeon_evict_flags(struct ttm_buffer_object *bo)
|
|
+static void radeon_evict_flags(struct ttm_buffer_object *bo,
|
|
+ struct ttm_placement *placement)
|
|
{
|
|
- uint32_t cur_placement = bo->mem.placement & ~TTM_PL_MASK_MEMTYPE;
|
|
+ struct radeon_bo *rbo;
|
|
+ static u32 placements = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
|
|
|
|
+ if (!radeon_ttm_bo_is_radeon_bo(bo)) {
|
|
+ placement->fpfn = 0;
|
|
+ placement->lpfn = 0;
|
|
+ placement->placement = &placements;
|
|
+ placement->busy_placement = &placements;
|
|
+ placement->num_placement = 1;
|
|
+ placement->num_busy_placement = 1;
|
|
+ return;
|
|
+ }
|
|
+ rbo = container_of(bo, struct radeon_bo, tbo);
|
|
switch (bo->mem.mem_type) {
|
|
+ case TTM_PL_VRAM:
|
|
+ if (rbo->rdev->cp.ready == false)
|
|
+ radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_CPU);
|
|
+ else
|
|
+ radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_GTT);
|
|
+ break;
|
|
+ case TTM_PL_TT:
|
|
default:
|
|
- return (cur_placement & ~TTM_PL_MASK_CACHING) |
|
|
- TTM_PL_FLAG_SYSTEM |
|
|
- TTM_PL_FLAG_CACHED;
|
|
+ radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_CPU);
|
|
}
|
|
+ *placement = rbo->placement;
|
|
}
|
|
|
|
static int radeon_verify_access(struct ttm_buffer_object *bo, struct file *filp)
|
|
@@ -283,14 +301,21 @@ static int radeon_move_vram_ram(struct ttm_buffer_object *bo,
|
|
struct radeon_device *rdev;
|
|
struct ttm_mem_reg *old_mem = &bo->mem;
|
|
struct ttm_mem_reg tmp_mem;
|
|
- uint32_t proposed_placement;
|
|
+ u32 placements;
|
|
+ struct ttm_placement placement;
|
|
int r;
|
|
|
|
rdev = radeon_get_rdev(bo->bdev);
|
|
tmp_mem = *new_mem;
|
|
tmp_mem.mm_node = NULL;
|
|
- proposed_placement = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
|
|
- r = ttm_bo_mem_space(bo, proposed_placement, &tmp_mem,
|
|
+ placement.fpfn = 0;
|
|
+ placement.lpfn = 0;
|
|
+ placement.num_placement = 1;
|
|
+ placement.placement = &placements;
|
|
+ placement.num_busy_placement = 1;
|
|
+ placement.busy_placement = &placements;
|
|
+ placements = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT;
|
|
+ r = ttm_bo_mem_space(bo, &placement, &tmp_mem,
|
|
interruptible, no_wait);
|
|
if (unlikely(r)) {
|
|
return r;
|
|
@@ -329,15 +354,21 @@ static int radeon_move_ram_vram(struct ttm_buffer_object *bo,
|
|
struct radeon_device *rdev;
|
|
struct ttm_mem_reg *old_mem = &bo->mem;
|
|
struct ttm_mem_reg tmp_mem;
|
|
- uint32_t proposed_flags;
|
|
+ struct ttm_placement placement;
|
|
+ u32 placements;
|
|
int r;
|
|
|
|
rdev = radeon_get_rdev(bo->bdev);
|
|
tmp_mem = *new_mem;
|
|
tmp_mem.mm_node = NULL;
|
|
- proposed_flags = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
|
|
- r = ttm_bo_mem_space(bo, proposed_flags, &tmp_mem,
|
|
- interruptible, no_wait);
|
|
+ placement.fpfn = 0;
|
|
+ placement.lpfn = 0;
|
|
+ placement.num_placement = 1;
|
|
+ placement.placement = &placements;
|
|
+ placement.num_busy_placement = 1;
|
|
+ placement.busy_placement = &placements;
|
|
+ placements = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT;
|
|
+ r = ttm_bo_mem_space(bo, &placement, &tmp_mem, interruptible, no_wait);
|
|
if (unlikely(r)) {
|
|
return r;
|
|
}
|
|
@@ -378,7 +409,7 @@ static int radeon_bo_move(struct ttm_buffer_object *bo,
|
|
new_mem->mem_type == TTM_PL_SYSTEM) ||
|
|
(old_mem->mem_type == TTM_PL_SYSTEM &&
|
|
new_mem->mem_type == TTM_PL_TT)) {
|
|
- /* bind is enought */
|
|
+ /* bind is enough */
|
|
radeon_move_null(bo, new_mem);
|
|
return 0;
|
|
}
|
|
@@ -407,18 +438,6 @@ memcpy:
|
|
return r;
|
|
}
|
|
|
|
-const uint32_t radeon_mem_prios[] = {
|
|
- TTM_PL_VRAM,
|
|
- TTM_PL_TT,
|
|
- TTM_PL_SYSTEM,
|
|
-};
|
|
-
|
|
-const uint32_t radeon_busy_prios[] = {
|
|
- TTM_PL_TT,
|
|
- TTM_PL_VRAM,
|
|
- TTM_PL_SYSTEM,
|
|
-};
|
|
-
|
|
static int radeon_sync_obj_wait(void *sync_obj, void *sync_arg,
|
|
bool lazy, bool interruptible)
|
|
{
|
|
@@ -446,10 +465,6 @@ static bool radeon_sync_obj_signaled(void *sync_obj, void *sync_arg)
|
|
}
|
|
|
|
static struct ttm_bo_driver radeon_bo_driver = {
|
|
- .mem_type_prio = radeon_mem_prios,
|
|
- .mem_busy_prio = radeon_busy_prios,
|
|
- .num_mem_type_prio = ARRAY_SIZE(radeon_mem_prios),
|
|
- .num_mem_busy_prio = ARRAY_SIZE(radeon_busy_prios),
|
|
.create_ttm_backend_entry = &radeon_create_ttm_backend_entry,
|
|
.invalidate_caches = &radeon_invalidate_caches,
|
|
.init_mem_type = &radeon_init_mem_type,
|
|
@@ -482,27 +497,32 @@ int radeon_ttm_init(struct radeon_device *rdev)
|
|
DRM_ERROR("failed initializing buffer object driver(%d).\n", r);
|
|
return r;
|
|
}
|
|
- r = ttm_bo_init_mm(&rdev->mman.bdev, TTM_PL_VRAM, 0,
|
|
- ((rdev->mc.real_vram_size) >> PAGE_SHIFT));
|
|
+ rdev->mman.initialized = true;
|
|
+ r = ttm_bo_init_mm(&rdev->mman.bdev, TTM_PL_VRAM,
|
|
+ rdev->mc.real_vram_size >> PAGE_SHIFT);
|
|
if (r) {
|
|
DRM_ERROR("Failed initializing VRAM heap.\n");
|
|
return r;
|
|
}
|
|
- r = radeon_object_create(rdev, NULL, 256 * 1024, true,
|
|
- RADEON_GEM_DOMAIN_VRAM, false,
|
|
- &rdev->stollen_vga_memory);
|
|
+ r = radeon_bo_create(rdev, NULL, 256 * 1024, true,
|
|
+ RADEON_GEM_DOMAIN_VRAM,
|
|
+ &rdev->stollen_vga_memory);
|
|
if (r) {
|
|
return r;
|
|
}
|
|
- r = radeon_object_pin(rdev->stollen_vga_memory, RADEON_GEM_DOMAIN_VRAM, NULL);
|
|
+ r = radeon_bo_reserve(rdev->stollen_vga_memory, false);
|
|
+ if (r)
|
|
+ return r;
|
|
+ r = radeon_bo_pin(rdev->stollen_vga_memory, RADEON_GEM_DOMAIN_VRAM, NULL);
|
|
+ radeon_bo_unreserve(rdev->stollen_vga_memory);
|
|
if (r) {
|
|
- radeon_object_unref(&rdev->stollen_vga_memory);
|
|
+ radeon_bo_unref(&rdev->stollen_vga_memory);
|
|
return r;
|
|
}
|
|
DRM_INFO("radeon: %uM of VRAM memory ready\n",
|
|
(unsigned)rdev->mc.real_vram_size / (1024 * 1024));
|
|
- r = ttm_bo_init_mm(&rdev->mman.bdev, TTM_PL_TT, 0,
|
|
- ((rdev->mc.gtt_size) >> PAGE_SHIFT));
|
|
+ r = ttm_bo_init_mm(&rdev->mman.bdev, TTM_PL_TT,
|
|
+ rdev->mc.gtt_size >> PAGE_SHIFT);
|
|
if (r) {
|
|
DRM_ERROR("Failed initializing GTT heap.\n");
|
|
return r;
|
|
@@ -523,15 +543,24 @@ int radeon_ttm_init(struct radeon_device *rdev)
|
|
|
|
void radeon_ttm_fini(struct radeon_device *rdev)
|
|
{
|
|
+ int r;
|
|
+
|
|
+ if (!rdev->mman.initialized)
|
|
+ return;
|
|
if (rdev->stollen_vga_memory) {
|
|
- radeon_object_unpin(rdev->stollen_vga_memory);
|
|
- radeon_object_unref(&rdev->stollen_vga_memory);
|
|
+ r = radeon_bo_reserve(rdev->stollen_vga_memory, false);
|
|
+ if (r == 0) {
|
|
+ radeon_bo_unpin(rdev->stollen_vga_memory);
|
|
+ radeon_bo_unreserve(rdev->stollen_vga_memory);
|
|
+ }
|
|
+ radeon_bo_unref(&rdev->stollen_vga_memory);
|
|
}
|
|
ttm_bo_clean_mm(&rdev->mman.bdev, TTM_PL_VRAM);
|
|
ttm_bo_clean_mm(&rdev->mman.bdev, TTM_PL_TT);
|
|
ttm_bo_device_release(&rdev->mman.bdev);
|
|
radeon_gart_fini(rdev);
|
|
radeon_ttm_global_fini(rdev);
|
|
+ rdev->mman.initialized = false;
|
|
DRM_INFO("radeon: ttm finalized\n");
|
|
}
|
|
|
|
diff --git a/drivers/gpu/drm/radeon/reg_srcs/r200 b/drivers/gpu/drm/radeon/reg_srcs/r200
|
|
index 6021c88..c29ac43 100644
|
|
--- a/drivers/gpu/drm/radeon/reg_srcs/r200
|
|
+++ b/drivers/gpu/drm/radeon/reg_srcs/r200
|
|
@@ -91,6 +91,8 @@ r200 0x3294
|
|
0x22b8 SE_TCL_TEX_CYL_WRAP_CTL
|
|
0x22c0 SE_TCL_UCP_VERT_BLEND_CNTL
|
|
0x22c4 SE_TCL_POINT_SPRITE_CNTL
|
|
+0x22d0 SE_PVS_CNTL
|
|
+0x22d4 SE_PVS_CONST_CNTL
|
|
0x2648 RE_POINTSIZE
|
|
0x26c0 RE_TOP_LEFT
|
|
0x26c4 RE_MISC
|
|
diff --git a/drivers/gpu/drm/radeon/reg_srcs/r420 b/drivers/gpu/drm/radeon/reg_srcs/r420
|
|
new file mode 100644
|
|
index 0000000..989f7a0
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/radeon/reg_srcs/r420
|
|
@@ -0,0 +1,795 @@
|
|
+r420 0x4f60
|
|
+0x1434 SRC_Y_X
|
|
+0x1438 DST_Y_X
|
|
+0x143C DST_HEIGHT_WIDTH
|
|
+0x146C DP_GUI_MASTER_CNTL
|
|
+0x1474 BRUSH_Y_X
|
|
+0x1478 DP_BRUSH_BKGD_CLR
|
|
+0x147C DP_BRUSH_FRGD_CLR
|
|
+0x1480 BRUSH_DATA0
|
|
+0x1484 BRUSH_DATA1
|
|
+0x1598 DST_WIDTH_HEIGHT
|
|
+0x15C0 CLR_CMP_CNTL
|
|
+0x15C4 CLR_CMP_CLR_SRC
|
|
+0x15C8 CLR_CMP_CLR_DST
|
|
+0x15CC CLR_CMP_MSK
|
|
+0x15D8 DP_SRC_FRGD_CLR
|
|
+0x15DC DP_SRC_BKGD_CLR
|
|
+0x1600 DST_LINE_START
|
|
+0x1604 DST_LINE_END
|
|
+0x1608 DST_LINE_PATCOUNT
|
|
+0x16C0 DP_CNTL
|
|
+0x16CC DP_WRITE_MSK
|
|
+0x16D0 DP_CNTL_XDIR_YDIR_YMAJOR
|
|
+0x16E8 DEFAULT_SC_BOTTOM_RIGHT
|
|
+0x16EC SC_TOP_LEFT
|
|
+0x16F0 SC_BOTTOM_RIGHT
|
|
+0x16F4 SRC_SC_BOTTOM_RIGHT
|
|
+0x1714 DSTCACHE_CTLSTAT
|
|
+0x1720 WAIT_UNTIL
|
|
+0x172C RBBM_GUICNTL
|
|
+0x1D98 VAP_VPORT_XSCALE
|
|
+0x1D9C VAP_VPORT_XOFFSET
|
|
+0x1DA0 VAP_VPORT_YSCALE
|
|
+0x1DA4 VAP_VPORT_YOFFSET
|
|
+0x1DA8 VAP_VPORT_ZSCALE
|
|
+0x1DAC VAP_VPORT_ZOFFSET
|
|
+0x2080 VAP_CNTL
|
|
+0x2090 VAP_OUT_VTX_FMT_0
|
|
+0x2094 VAP_OUT_VTX_FMT_1
|
|
+0x20B0 VAP_VTE_CNTL
|
|
+0x2138 VAP_VF_MIN_VTX_INDX
|
|
+0x2140 VAP_CNTL_STATUS
|
|
+0x2150 VAP_PROG_STREAM_CNTL_0
|
|
+0x2154 VAP_PROG_STREAM_CNTL_1
|
|
+0x2158 VAP_PROG_STREAM_CNTL_2
|
|
+0x215C VAP_PROG_STREAM_CNTL_3
|
|
+0x2160 VAP_PROG_STREAM_CNTL_4
|
|
+0x2164 VAP_PROG_STREAM_CNTL_5
|
|
+0x2168 VAP_PROG_STREAM_CNTL_6
|
|
+0x216C VAP_PROG_STREAM_CNTL_7
|
|
+0x2180 VAP_VTX_STATE_CNTL
|
|
+0x2184 VAP_VSM_VTX_ASSM
|
|
+0x2188 VAP_VTX_STATE_IND_REG_0
|
|
+0x218C VAP_VTX_STATE_IND_REG_1
|
|
+0x2190 VAP_VTX_STATE_IND_REG_2
|
|
+0x2194 VAP_VTX_STATE_IND_REG_3
|
|
+0x2198 VAP_VTX_STATE_IND_REG_4
|
|
+0x219C VAP_VTX_STATE_IND_REG_5
|
|
+0x21A0 VAP_VTX_STATE_IND_REG_6
|
|
+0x21A4 VAP_VTX_STATE_IND_REG_7
|
|
+0x21A8 VAP_VTX_STATE_IND_REG_8
|
|
+0x21AC VAP_VTX_STATE_IND_REG_9
|
|
+0x21B0 VAP_VTX_STATE_IND_REG_10
|
|
+0x21B4 VAP_VTX_STATE_IND_REG_11
|
|
+0x21B8 VAP_VTX_STATE_IND_REG_12
|
|
+0x21BC VAP_VTX_STATE_IND_REG_13
|
|
+0x21C0 VAP_VTX_STATE_IND_REG_14
|
|
+0x21C4 VAP_VTX_STATE_IND_REG_15
|
|
+0x21DC VAP_PSC_SGN_NORM_CNTL
|
|
+0x21E0 VAP_PROG_STREAM_CNTL_EXT_0
|
|
+0x21E4 VAP_PROG_STREAM_CNTL_EXT_1
|
|
+0x21E8 VAP_PROG_STREAM_CNTL_EXT_2
|
|
+0x21EC VAP_PROG_STREAM_CNTL_EXT_3
|
|
+0x21F0 VAP_PROG_STREAM_CNTL_EXT_4
|
|
+0x21F4 VAP_PROG_STREAM_CNTL_EXT_5
|
|
+0x21F8 VAP_PROG_STREAM_CNTL_EXT_6
|
|
+0x21FC VAP_PROG_STREAM_CNTL_EXT_7
|
|
+0x2200 VAP_PVS_VECTOR_INDX_REG
|
|
+0x2204 VAP_PVS_VECTOR_DATA_REG
|
|
+0x2208 VAP_PVS_VECTOR_DATA_REG_128
|
|
+0x221C VAP_CLIP_CNTL
|
|
+0x2220 VAP_GB_VERT_CLIP_ADJ
|
|
+0x2224 VAP_GB_VERT_DISC_ADJ
|
|
+0x2228 VAP_GB_HORZ_CLIP_ADJ
|
|
+0x222C VAP_GB_HORZ_DISC_ADJ
|
|
+0x2230 VAP_PVS_FLOW_CNTL_ADDRS_0
|
|
+0x2234 VAP_PVS_FLOW_CNTL_ADDRS_1
|
|
+0x2238 VAP_PVS_FLOW_CNTL_ADDRS_2
|
|
+0x223C VAP_PVS_FLOW_CNTL_ADDRS_3
|
|
+0x2240 VAP_PVS_FLOW_CNTL_ADDRS_4
|
|
+0x2244 VAP_PVS_FLOW_CNTL_ADDRS_5
|
|
+0x2248 VAP_PVS_FLOW_CNTL_ADDRS_6
|
|
+0x224C VAP_PVS_FLOW_CNTL_ADDRS_7
|
|
+0x2250 VAP_PVS_FLOW_CNTL_ADDRS_8
|
|
+0x2254 VAP_PVS_FLOW_CNTL_ADDRS_9
|
|
+0x2258 VAP_PVS_FLOW_CNTL_ADDRS_10
|
|
+0x225C VAP_PVS_FLOW_CNTL_ADDRS_11
|
|
+0x2260 VAP_PVS_FLOW_CNTL_ADDRS_12
|
|
+0x2264 VAP_PVS_FLOW_CNTL_ADDRS_13
|
|
+0x2268 VAP_PVS_FLOW_CNTL_ADDRS_14
|
|
+0x226C VAP_PVS_FLOW_CNTL_ADDRS_15
|
|
+0x2284 VAP_PVS_STATE_FLUSH_REG
|
|
+0x2288 VAP_PVS_VTX_TIMEOUT_REG
|
|
+0x2290 VAP_PVS_FLOW_CNTL_LOOP_INDEX_0
|
|
+0x2294 VAP_PVS_FLOW_CNTL_LOOP_INDEX_1
|
|
+0x2298 VAP_PVS_FLOW_CNTL_LOOP_INDEX_2
|
|
+0x229C VAP_PVS_FLOW_CNTL_LOOP_INDEX_3
|
|
+0x22A0 VAP_PVS_FLOW_CNTL_LOOP_INDEX_4
|
|
+0x22A4 VAP_PVS_FLOW_CNTL_LOOP_INDEX_5
|
|
+0x22A8 VAP_PVS_FLOW_CNTL_LOOP_INDEX_6
|
|
+0x22AC VAP_PVS_FLOW_CNTL_LOOP_INDEX_7
|
|
+0x22B0 VAP_PVS_FLOW_CNTL_LOOP_INDEX_8
|
|
+0x22B4 VAP_PVS_FLOW_CNTL_LOOP_INDEX_9
|
|
+0x22B8 VAP_PVS_FLOW_CNTL_LOOP_INDEX_10
|
|
+0x22BC VAP_PVS_FLOW_CNTL_LOOP_INDEX_11
|
|
+0x22C0 VAP_PVS_FLOW_CNTL_LOOP_INDEX_12
|
|
+0x22C4 VAP_PVS_FLOW_CNTL_LOOP_INDEX_13
|
|
+0x22C8 VAP_PVS_FLOW_CNTL_LOOP_INDEX_14
|
|
+0x22CC VAP_PVS_FLOW_CNTL_LOOP_INDEX_15
|
|
+0x22D0 VAP_PVS_CODE_CNTL_0
|
|
+0x22D4 VAP_PVS_CONST_CNTL
|
|
+0x22D8 VAP_PVS_CODE_CNTL_1
|
|
+0x22DC VAP_PVS_FLOW_CNTL_OPC
|
|
+0x342C RB2D_DSTCACHE_CTLSTAT
|
|
+0x4000 GB_VAP_RASTER_VTX_FMT_0
|
|
+0x4004 GB_VAP_RASTER_VTX_FMT_1
|
|
+0x4008 GB_ENABLE
|
|
+0x401C GB_SELECT
|
|
+0x4020 GB_AA_CONFIG
|
|
+0x4024 GB_FIFO_SIZE
|
|
+0x4100 TX_INVALTAGS
|
|
+0x4200 GA_POINT_S0
|
|
+0x4204 GA_POINT_T0
|
|
+0x4208 GA_POINT_S1
|
|
+0x420C GA_POINT_T1
|
|
+0x4214 GA_TRIANGLE_STIPPLE
|
|
+0x421C GA_POINT_SIZE
|
|
+0x4230 GA_POINT_MINMAX
|
|
+0x4234 GA_LINE_CNTL
|
|
+0x4238 GA_LINE_STIPPLE_CONFIG
|
|
+0x4260 GA_LINE_STIPPLE_VALUE
|
|
+0x4264 GA_LINE_S0
|
|
+0x4268 GA_LINE_S1
|
|
+0x4278 GA_COLOR_CONTROL
|
|
+0x427C GA_SOLID_RG
|
|
+0x4280 GA_SOLID_BA
|
|
+0x4288 GA_POLY_MODE
|
|
+0x428C GA_ROUND_MODE
|
|
+0x4290 GA_OFFSET
|
|
+0x4294 GA_FOG_SCALE
|
|
+0x4298 GA_FOG_OFFSET
|
|
+0x42A0 SU_TEX_WRAP
|
|
+0x42A4 SU_POLY_OFFSET_FRONT_SCALE
|
|
+0x42A8 SU_POLY_OFFSET_FRONT_OFFSET
|
|
+0x42AC SU_POLY_OFFSET_BACK_SCALE
|
|
+0x42B0 SU_POLY_OFFSET_BACK_OFFSET
|
|
+0x42B4 SU_POLY_OFFSET_ENABLE
|
|
+0x42B8 SU_CULL_MODE
|
|
+0x42C0 SU_DEPTH_SCALE
|
|
+0x42C4 SU_DEPTH_OFFSET
|
|
+0x42C8 SU_REG_DEST
|
|
+0x4300 RS_COUNT
|
|
+0x4304 RS_INST_COUNT
|
|
+0x4310 RS_IP_0
|
|
+0x4314 RS_IP_1
|
|
+0x4318 RS_IP_2
|
|
+0x431C RS_IP_3
|
|
+0x4320 RS_IP_4
|
|
+0x4324 RS_IP_5
|
|
+0x4328 RS_IP_6
|
|
+0x432C RS_IP_7
|
|
+0x4330 RS_INST_0
|
|
+0x4334 RS_INST_1
|
|
+0x4338 RS_INST_2
|
|
+0x433C RS_INST_3
|
|
+0x4340 RS_INST_4
|
|
+0x4344 RS_INST_5
|
|
+0x4348 RS_INST_6
|
|
+0x434C RS_INST_7
|
|
+0x4350 RS_INST_8
|
|
+0x4354 RS_INST_9
|
|
+0x4358 RS_INST_10
|
|
+0x435C RS_INST_11
|
|
+0x4360 RS_INST_12
|
|
+0x4364 RS_INST_13
|
|
+0x4368 RS_INST_14
|
|
+0x436C RS_INST_15
|
|
+0x43A4 SC_HYPERZ_EN
|
|
+0x43A8 SC_EDGERULE
|
|
+0x43B0 SC_CLIP_0_A
|
|
+0x43B4 SC_CLIP_0_B
|
|
+0x43B8 SC_CLIP_1_A
|
|
+0x43BC SC_CLIP_1_B
|
|
+0x43C0 SC_CLIP_2_A
|
|
+0x43C4 SC_CLIP_2_B
|
|
+0x43C8 SC_CLIP_3_A
|
|
+0x43CC SC_CLIP_3_B
|
|
+0x43D0 SC_CLIP_RULE
|
|
+0x43E0 SC_SCISSOR0
|
|
+0x43E8 SC_SCREENDOOR
|
|
+0x4440 TX_FILTER1_0
|
|
+0x4444 TX_FILTER1_1
|
|
+0x4448 TX_FILTER1_2
|
|
+0x444C TX_FILTER1_3
|
|
+0x4450 TX_FILTER1_4
|
|
+0x4454 TX_FILTER1_5
|
|
+0x4458 TX_FILTER1_6
|
|
+0x445C TX_FILTER1_7
|
|
+0x4460 TX_FILTER1_8
|
|
+0x4464 TX_FILTER1_9
|
|
+0x4468 TX_FILTER1_10
|
|
+0x446C TX_FILTER1_11
|
|
+0x4470 TX_FILTER1_12
|
|
+0x4474 TX_FILTER1_13
|
|
+0x4478 TX_FILTER1_14
|
|
+0x447C TX_FILTER1_15
|
|
+0x4580 TX_CHROMA_KEY_0
|
|
+0x4584 TX_CHROMA_KEY_1
|
|
+0x4588 TX_CHROMA_KEY_2
|
|
+0x458C TX_CHROMA_KEY_3
|
|
+0x4590 TX_CHROMA_KEY_4
|
|
+0x4594 TX_CHROMA_KEY_5
|
|
+0x4598 TX_CHROMA_KEY_6
|
|
+0x459C TX_CHROMA_KEY_7
|
|
+0x45A0 TX_CHROMA_KEY_8
|
|
+0x45A4 TX_CHROMA_KEY_9
|
|
+0x45A8 TX_CHROMA_KEY_10
|
|
+0x45AC TX_CHROMA_KEY_11
|
|
+0x45B0 TX_CHROMA_KEY_12
|
|
+0x45B4 TX_CHROMA_KEY_13
|
|
+0x45B8 TX_CHROMA_KEY_14
|
|
+0x45BC TX_CHROMA_KEY_15
|
|
+0x45C0 TX_BORDER_COLOR_0
|
|
+0x45C4 TX_BORDER_COLOR_1
|
|
+0x45C8 TX_BORDER_COLOR_2
|
|
+0x45CC TX_BORDER_COLOR_3
|
|
+0x45D0 TX_BORDER_COLOR_4
|
|
+0x45D4 TX_BORDER_COLOR_5
|
|
+0x45D8 TX_BORDER_COLOR_6
|
|
+0x45DC TX_BORDER_COLOR_7
|
|
+0x45E0 TX_BORDER_COLOR_8
|
|
+0x45E4 TX_BORDER_COLOR_9
|
|
+0x45E8 TX_BORDER_COLOR_10
|
|
+0x45EC TX_BORDER_COLOR_11
|
|
+0x45F0 TX_BORDER_COLOR_12
|
|
+0x45F4 TX_BORDER_COLOR_13
|
|
+0x45F8 TX_BORDER_COLOR_14
|
|
+0x45FC TX_BORDER_COLOR_15
|
|
+0x4600 US_CONFIG
|
|
+0x4604 US_PIXSIZE
|
|
+0x4608 US_CODE_OFFSET
|
|
+0x460C US_RESET
|
|
+0x4610 US_CODE_ADDR_0
|
|
+0x4614 US_CODE_ADDR_1
|
|
+0x4618 US_CODE_ADDR_2
|
|
+0x461C US_CODE_ADDR_3
|
|
+0x4620 US_TEX_INST_0
|
|
+0x4624 US_TEX_INST_1
|
|
+0x4628 US_TEX_INST_2
|
|
+0x462C US_TEX_INST_3
|
|
+0x4630 US_TEX_INST_4
|
|
+0x4634 US_TEX_INST_5
|
|
+0x4638 US_TEX_INST_6
|
|
+0x463C US_TEX_INST_7
|
|
+0x4640 US_TEX_INST_8
|
|
+0x4644 US_TEX_INST_9
|
|
+0x4648 US_TEX_INST_10
|
|
+0x464C US_TEX_INST_11
|
|
+0x4650 US_TEX_INST_12
|
|
+0x4654 US_TEX_INST_13
|
|
+0x4658 US_TEX_INST_14
|
|
+0x465C US_TEX_INST_15
|
|
+0x4660 US_TEX_INST_16
|
|
+0x4664 US_TEX_INST_17
|
|
+0x4668 US_TEX_INST_18
|
|
+0x466C US_TEX_INST_19
|
|
+0x4670 US_TEX_INST_20
|
|
+0x4674 US_TEX_INST_21
|
|
+0x4678 US_TEX_INST_22
|
|
+0x467C US_TEX_INST_23
|
|
+0x4680 US_TEX_INST_24
|
|
+0x4684 US_TEX_INST_25
|
|
+0x4688 US_TEX_INST_26
|
|
+0x468C US_TEX_INST_27
|
|
+0x4690 US_TEX_INST_28
|
|
+0x4694 US_TEX_INST_29
|
|
+0x4698 US_TEX_INST_30
|
|
+0x469C US_TEX_INST_31
|
|
+0x46A4 US_OUT_FMT_0
|
|
+0x46A8 US_OUT_FMT_1
|
|
+0x46AC US_OUT_FMT_2
|
|
+0x46B0 US_OUT_FMT_3
|
|
+0x46B4 US_W_FMT
|
|
+0x46B8 US_CODE_BANK
|
|
+0x46BC US_CODE_EXT
|
|
+0x46C0 US_ALU_RGB_ADDR_0
|
|
+0x46C4 US_ALU_RGB_ADDR_1
|
|
+0x46C8 US_ALU_RGB_ADDR_2
|
|
+0x46CC US_ALU_RGB_ADDR_3
|
|
+0x46D0 US_ALU_RGB_ADDR_4
|
|
+0x46D4 US_ALU_RGB_ADDR_5
|
|
+0x46D8 US_ALU_RGB_ADDR_6
|
|
+0x46DC US_ALU_RGB_ADDR_7
|
|
+0x46E0 US_ALU_RGB_ADDR_8
|
|
+0x46E4 US_ALU_RGB_ADDR_9
|
|
+0x46E8 US_ALU_RGB_ADDR_10
|
|
+0x46EC US_ALU_RGB_ADDR_11
|
|
+0x46F0 US_ALU_RGB_ADDR_12
|
|
+0x46F4 US_ALU_RGB_ADDR_13
|
|
+0x46F8 US_ALU_RGB_ADDR_14
|
|
+0x46FC US_ALU_RGB_ADDR_15
|
|
+0x4700 US_ALU_RGB_ADDR_16
|
|
+0x4704 US_ALU_RGB_ADDR_17
|
|
+0x4708 US_ALU_RGB_ADDR_18
|
|
+0x470C US_ALU_RGB_ADDR_19
|
|
+0x4710 US_ALU_RGB_ADDR_20
|
|
+0x4714 US_ALU_RGB_ADDR_21
|
|
+0x4718 US_ALU_RGB_ADDR_22
|
|
+0x471C US_ALU_RGB_ADDR_23
|
|
+0x4720 US_ALU_RGB_ADDR_24
|
|
+0x4724 US_ALU_RGB_ADDR_25
|
|
+0x4728 US_ALU_RGB_ADDR_26
|
|
+0x472C US_ALU_RGB_ADDR_27
|
|
+0x4730 US_ALU_RGB_ADDR_28
|
|
+0x4734 US_ALU_RGB_ADDR_29
|
|
+0x4738 US_ALU_RGB_ADDR_30
|
|
+0x473C US_ALU_RGB_ADDR_31
|
|
+0x4740 US_ALU_RGB_ADDR_32
|
|
+0x4744 US_ALU_RGB_ADDR_33
|
|
+0x4748 US_ALU_RGB_ADDR_34
|
|
+0x474C US_ALU_RGB_ADDR_35
|
|
+0x4750 US_ALU_RGB_ADDR_36
|
|
+0x4754 US_ALU_RGB_ADDR_37
|
|
+0x4758 US_ALU_RGB_ADDR_38
|
|
+0x475C US_ALU_RGB_ADDR_39
|
|
+0x4760 US_ALU_RGB_ADDR_40
|
|
+0x4764 US_ALU_RGB_ADDR_41
|
|
+0x4768 US_ALU_RGB_ADDR_42
|
|
+0x476C US_ALU_RGB_ADDR_43
|
|
+0x4770 US_ALU_RGB_ADDR_44
|
|
+0x4774 US_ALU_RGB_ADDR_45
|
|
+0x4778 US_ALU_RGB_ADDR_46
|
|
+0x477C US_ALU_RGB_ADDR_47
|
|
+0x4780 US_ALU_RGB_ADDR_48
|
|
+0x4784 US_ALU_RGB_ADDR_49
|
|
+0x4788 US_ALU_RGB_ADDR_50
|
|
+0x478C US_ALU_RGB_ADDR_51
|
|
+0x4790 US_ALU_RGB_ADDR_52
|
|
+0x4794 US_ALU_RGB_ADDR_53
|
|
+0x4798 US_ALU_RGB_ADDR_54
|
|
+0x479C US_ALU_RGB_ADDR_55
|
|
+0x47A0 US_ALU_RGB_ADDR_56
|
|
+0x47A4 US_ALU_RGB_ADDR_57
|
|
+0x47A8 US_ALU_RGB_ADDR_58
|
|
+0x47AC US_ALU_RGB_ADDR_59
|
|
+0x47B0 US_ALU_RGB_ADDR_60
|
|
+0x47B4 US_ALU_RGB_ADDR_61
|
|
+0x47B8 US_ALU_RGB_ADDR_62
|
|
+0x47BC US_ALU_RGB_ADDR_63
|
|
+0x47C0 US_ALU_ALPHA_ADDR_0
|
|
+0x47C4 US_ALU_ALPHA_ADDR_1
|
|
+0x47C8 US_ALU_ALPHA_ADDR_2
|
|
+0x47CC US_ALU_ALPHA_ADDR_3
|
|
+0x47D0 US_ALU_ALPHA_ADDR_4
|
|
+0x47D4 US_ALU_ALPHA_ADDR_5
|
|
+0x47D8 US_ALU_ALPHA_ADDR_6
|
|
+0x47DC US_ALU_ALPHA_ADDR_7
|
|
+0x47E0 US_ALU_ALPHA_ADDR_8
|
|
+0x47E4 US_ALU_ALPHA_ADDR_9
|
|
+0x47E8 US_ALU_ALPHA_ADDR_10
|
|
+0x47EC US_ALU_ALPHA_ADDR_11
|
|
+0x47F0 US_ALU_ALPHA_ADDR_12
|
|
+0x47F4 US_ALU_ALPHA_ADDR_13
|
|
+0x47F8 US_ALU_ALPHA_ADDR_14
|
|
+0x47FC US_ALU_ALPHA_ADDR_15
|
|
+0x4800 US_ALU_ALPHA_ADDR_16
|
|
+0x4804 US_ALU_ALPHA_ADDR_17
|
|
+0x4808 US_ALU_ALPHA_ADDR_18
|
|
+0x480C US_ALU_ALPHA_ADDR_19
|
|
+0x4810 US_ALU_ALPHA_ADDR_20
|
|
+0x4814 US_ALU_ALPHA_ADDR_21
|
|
+0x4818 US_ALU_ALPHA_ADDR_22
|
|
+0x481C US_ALU_ALPHA_ADDR_23
|
|
+0x4820 US_ALU_ALPHA_ADDR_24
|
|
+0x4824 US_ALU_ALPHA_ADDR_25
|
|
+0x4828 US_ALU_ALPHA_ADDR_26
|
|
+0x482C US_ALU_ALPHA_ADDR_27
|
|
+0x4830 US_ALU_ALPHA_ADDR_28
|
|
+0x4834 US_ALU_ALPHA_ADDR_29
|
|
+0x4838 US_ALU_ALPHA_ADDR_30
|
|
+0x483C US_ALU_ALPHA_ADDR_31
|
|
+0x4840 US_ALU_ALPHA_ADDR_32
|
|
+0x4844 US_ALU_ALPHA_ADDR_33
|
|
+0x4848 US_ALU_ALPHA_ADDR_34
|
|
+0x484C US_ALU_ALPHA_ADDR_35
|
|
+0x4850 US_ALU_ALPHA_ADDR_36
|
|
+0x4854 US_ALU_ALPHA_ADDR_37
|
|
+0x4858 US_ALU_ALPHA_ADDR_38
|
|
+0x485C US_ALU_ALPHA_ADDR_39
|
|
+0x4860 US_ALU_ALPHA_ADDR_40
|
|
+0x4864 US_ALU_ALPHA_ADDR_41
|
|
+0x4868 US_ALU_ALPHA_ADDR_42
|
|
+0x486C US_ALU_ALPHA_ADDR_43
|
|
+0x4870 US_ALU_ALPHA_ADDR_44
|
|
+0x4874 US_ALU_ALPHA_ADDR_45
|
|
+0x4878 US_ALU_ALPHA_ADDR_46
|
|
+0x487C US_ALU_ALPHA_ADDR_47
|
|
+0x4880 US_ALU_ALPHA_ADDR_48
|
|
+0x4884 US_ALU_ALPHA_ADDR_49
|
|
+0x4888 US_ALU_ALPHA_ADDR_50
|
|
+0x488C US_ALU_ALPHA_ADDR_51
|
|
+0x4890 US_ALU_ALPHA_ADDR_52
|
|
+0x4894 US_ALU_ALPHA_ADDR_53
|
|
+0x4898 US_ALU_ALPHA_ADDR_54
|
|
+0x489C US_ALU_ALPHA_ADDR_55
|
|
+0x48A0 US_ALU_ALPHA_ADDR_56
|
|
+0x48A4 US_ALU_ALPHA_ADDR_57
|
|
+0x48A8 US_ALU_ALPHA_ADDR_58
|
|
+0x48AC US_ALU_ALPHA_ADDR_59
|
|
+0x48B0 US_ALU_ALPHA_ADDR_60
|
|
+0x48B4 US_ALU_ALPHA_ADDR_61
|
|
+0x48B8 US_ALU_ALPHA_ADDR_62
|
|
+0x48BC US_ALU_ALPHA_ADDR_63
|
|
+0x48C0 US_ALU_RGB_INST_0
|
|
+0x48C4 US_ALU_RGB_INST_1
|
|
+0x48C8 US_ALU_RGB_INST_2
|
|
+0x48CC US_ALU_RGB_INST_3
|
|
+0x48D0 US_ALU_RGB_INST_4
|
|
+0x48D4 US_ALU_RGB_INST_5
|
|
+0x48D8 US_ALU_RGB_INST_6
|
|
+0x48DC US_ALU_RGB_INST_7
|
|
+0x48E0 US_ALU_RGB_INST_8
|
|
+0x48E4 US_ALU_RGB_INST_9
|
|
+0x48E8 US_ALU_RGB_INST_10
|
|
+0x48EC US_ALU_RGB_INST_11
|
|
+0x48F0 US_ALU_RGB_INST_12
|
|
+0x48F4 US_ALU_RGB_INST_13
|
|
+0x48F8 US_ALU_RGB_INST_14
|
|
+0x48FC US_ALU_RGB_INST_15
|
|
+0x4900 US_ALU_RGB_INST_16
|
|
+0x4904 US_ALU_RGB_INST_17
|
|
+0x4908 US_ALU_RGB_INST_18
|
|
+0x490C US_ALU_RGB_INST_19
|
|
+0x4910 US_ALU_RGB_INST_20
|
|
+0x4914 US_ALU_RGB_INST_21
|
|
+0x4918 US_ALU_RGB_INST_22
|
|
+0x491C US_ALU_RGB_INST_23
|
|
+0x4920 US_ALU_RGB_INST_24
|
|
+0x4924 US_ALU_RGB_INST_25
|
|
+0x4928 US_ALU_RGB_INST_26
|
|
+0x492C US_ALU_RGB_INST_27
|
|
+0x4930 US_ALU_RGB_INST_28
|
|
+0x4934 US_ALU_RGB_INST_29
|
|
+0x4938 US_ALU_RGB_INST_30
|
|
+0x493C US_ALU_RGB_INST_31
|
|
+0x4940 US_ALU_RGB_INST_32
|
|
+0x4944 US_ALU_RGB_INST_33
|
|
+0x4948 US_ALU_RGB_INST_34
|
|
+0x494C US_ALU_RGB_INST_35
|
|
+0x4950 US_ALU_RGB_INST_36
|
|
+0x4954 US_ALU_RGB_INST_37
|
|
+0x4958 US_ALU_RGB_INST_38
|
|
+0x495C US_ALU_RGB_INST_39
|
|
+0x4960 US_ALU_RGB_INST_40
|
|
+0x4964 US_ALU_RGB_INST_41
|
|
+0x4968 US_ALU_RGB_INST_42
|
|
+0x496C US_ALU_RGB_INST_43
|
|
+0x4970 US_ALU_RGB_INST_44
|
|
+0x4974 US_ALU_RGB_INST_45
|
|
+0x4978 US_ALU_RGB_INST_46
|
|
+0x497C US_ALU_RGB_INST_47
|
|
+0x4980 US_ALU_RGB_INST_48
|
|
+0x4984 US_ALU_RGB_INST_49
|
|
+0x4988 US_ALU_RGB_INST_50
|
|
+0x498C US_ALU_RGB_INST_51
|
|
+0x4990 US_ALU_RGB_INST_52
|
|
+0x4994 US_ALU_RGB_INST_53
|
|
+0x4998 US_ALU_RGB_INST_54
|
|
+0x499C US_ALU_RGB_INST_55
|
|
+0x49A0 US_ALU_RGB_INST_56
|
|
+0x49A4 US_ALU_RGB_INST_57
|
|
+0x49A8 US_ALU_RGB_INST_58
|
|
+0x49AC US_ALU_RGB_INST_59
|
|
+0x49B0 US_ALU_RGB_INST_60
|
|
+0x49B4 US_ALU_RGB_INST_61
|
|
+0x49B8 US_ALU_RGB_INST_62
|
|
+0x49BC US_ALU_RGB_INST_63
|
|
+0x49C0 US_ALU_ALPHA_INST_0
|
|
+0x49C4 US_ALU_ALPHA_INST_1
|
|
+0x49C8 US_ALU_ALPHA_INST_2
|
|
+0x49CC US_ALU_ALPHA_INST_3
|
|
+0x49D0 US_ALU_ALPHA_INST_4
|
|
+0x49D4 US_ALU_ALPHA_INST_5
|
|
+0x49D8 US_ALU_ALPHA_INST_6
|
|
+0x49DC US_ALU_ALPHA_INST_7
|
|
+0x49E0 US_ALU_ALPHA_INST_8
|
|
+0x49E4 US_ALU_ALPHA_INST_9
|
|
+0x49E8 US_ALU_ALPHA_INST_10
|
|
+0x49EC US_ALU_ALPHA_INST_11
|
|
+0x49F0 US_ALU_ALPHA_INST_12
|
|
+0x49F4 US_ALU_ALPHA_INST_13
|
|
+0x49F8 US_ALU_ALPHA_INST_14
|
|
+0x49FC US_ALU_ALPHA_INST_15
|
|
+0x4A00 US_ALU_ALPHA_INST_16
|
|
+0x4A04 US_ALU_ALPHA_INST_17
|
|
+0x4A08 US_ALU_ALPHA_INST_18
|
|
+0x4A0C US_ALU_ALPHA_INST_19
|
|
+0x4A10 US_ALU_ALPHA_INST_20
|
|
+0x4A14 US_ALU_ALPHA_INST_21
|
|
+0x4A18 US_ALU_ALPHA_INST_22
|
|
+0x4A1C US_ALU_ALPHA_INST_23
|
|
+0x4A20 US_ALU_ALPHA_INST_24
|
|
+0x4A24 US_ALU_ALPHA_INST_25
|
|
+0x4A28 US_ALU_ALPHA_INST_26
|
|
+0x4A2C US_ALU_ALPHA_INST_27
|
|
+0x4A30 US_ALU_ALPHA_INST_28
|
|
+0x4A34 US_ALU_ALPHA_INST_29
|
|
+0x4A38 US_ALU_ALPHA_INST_30
|
|
+0x4A3C US_ALU_ALPHA_INST_31
|
|
+0x4A40 US_ALU_ALPHA_INST_32
|
|
+0x4A44 US_ALU_ALPHA_INST_33
|
|
+0x4A48 US_ALU_ALPHA_INST_34
|
|
+0x4A4C US_ALU_ALPHA_INST_35
|
|
+0x4A50 US_ALU_ALPHA_INST_36
|
|
+0x4A54 US_ALU_ALPHA_INST_37
|
|
+0x4A58 US_ALU_ALPHA_INST_38
|
|
+0x4A5C US_ALU_ALPHA_INST_39
|
|
+0x4A60 US_ALU_ALPHA_INST_40
|
|
+0x4A64 US_ALU_ALPHA_INST_41
|
|
+0x4A68 US_ALU_ALPHA_INST_42
|
|
+0x4A6C US_ALU_ALPHA_INST_43
|
|
+0x4A70 US_ALU_ALPHA_INST_44
|
|
+0x4A74 US_ALU_ALPHA_INST_45
|
|
+0x4A78 US_ALU_ALPHA_INST_46
|
|
+0x4A7C US_ALU_ALPHA_INST_47
|
|
+0x4A80 US_ALU_ALPHA_INST_48
|
|
+0x4A84 US_ALU_ALPHA_INST_49
|
|
+0x4A88 US_ALU_ALPHA_INST_50
|
|
+0x4A8C US_ALU_ALPHA_INST_51
|
|
+0x4A90 US_ALU_ALPHA_INST_52
|
|
+0x4A94 US_ALU_ALPHA_INST_53
|
|
+0x4A98 US_ALU_ALPHA_INST_54
|
|
+0x4A9C US_ALU_ALPHA_INST_55
|
|
+0x4AA0 US_ALU_ALPHA_INST_56
|
|
+0x4AA4 US_ALU_ALPHA_INST_57
|
|
+0x4AA8 US_ALU_ALPHA_INST_58
|
|
+0x4AAC US_ALU_ALPHA_INST_59
|
|
+0x4AB0 US_ALU_ALPHA_INST_60
|
|
+0x4AB4 US_ALU_ALPHA_INST_61
|
|
+0x4AB8 US_ALU_ALPHA_INST_62
|
|
+0x4ABC US_ALU_ALPHA_INST_63
|
|
+0x4AC0 US_ALU_EXT_ADDR_0
|
|
+0x4AC4 US_ALU_EXT_ADDR_1
|
|
+0x4AC8 US_ALU_EXT_ADDR_2
|
|
+0x4ACC US_ALU_EXT_ADDR_3
|
|
+0x4AD0 US_ALU_EXT_ADDR_4
|
|
+0x4AD4 US_ALU_EXT_ADDR_5
|
|
+0x4AD8 US_ALU_EXT_ADDR_6
|
|
+0x4ADC US_ALU_EXT_ADDR_7
|
|
+0x4AE0 US_ALU_EXT_ADDR_8
|
|
+0x4AE4 US_ALU_EXT_ADDR_9
|
|
+0x4AE8 US_ALU_EXT_ADDR_10
|
|
+0x4AEC US_ALU_EXT_ADDR_11
|
|
+0x4AF0 US_ALU_EXT_ADDR_12
|
|
+0x4AF4 US_ALU_EXT_ADDR_13
|
|
+0x4AF8 US_ALU_EXT_ADDR_14
|
|
+0x4AFC US_ALU_EXT_ADDR_15
|
|
+0x4B00 US_ALU_EXT_ADDR_16
|
|
+0x4B04 US_ALU_EXT_ADDR_17
|
|
+0x4B08 US_ALU_EXT_ADDR_18
|
|
+0x4B0C US_ALU_EXT_ADDR_19
|
|
+0x4B10 US_ALU_EXT_ADDR_20
|
|
+0x4B14 US_ALU_EXT_ADDR_21
|
|
+0x4B18 US_ALU_EXT_ADDR_22
|
|
+0x4B1C US_ALU_EXT_ADDR_23
|
|
+0x4B20 US_ALU_EXT_ADDR_24
|
|
+0x4B24 US_ALU_EXT_ADDR_25
|
|
+0x4B28 US_ALU_EXT_ADDR_26
|
|
+0x4B2C US_ALU_EXT_ADDR_27
|
|
+0x4B30 US_ALU_EXT_ADDR_28
|
|
+0x4B34 US_ALU_EXT_ADDR_29
|
|
+0x4B38 US_ALU_EXT_ADDR_30
|
|
+0x4B3C US_ALU_EXT_ADDR_31
|
|
+0x4B40 US_ALU_EXT_ADDR_32
|
|
+0x4B44 US_ALU_EXT_ADDR_33
|
|
+0x4B48 US_ALU_EXT_ADDR_34
|
|
+0x4B4C US_ALU_EXT_ADDR_35
|
|
+0x4B50 US_ALU_EXT_ADDR_36
|
|
+0x4B54 US_ALU_EXT_ADDR_37
|
|
+0x4B58 US_ALU_EXT_ADDR_38
|
|
+0x4B5C US_ALU_EXT_ADDR_39
|
|
+0x4B60 US_ALU_EXT_ADDR_40
|
|
+0x4B64 US_ALU_EXT_ADDR_41
|
|
+0x4B68 US_ALU_EXT_ADDR_42
|
|
+0x4B6C US_ALU_EXT_ADDR_43
|
|
+0x4B70 US_ALU_EXT_ADDR_44
|
|
+0x4B74 US_ALU_EXT_ADDR_45
|
|
+0x4B78 US_ALU_EXT_ADDR_46
|
|
+0x4B7C US_ALU_EXT_ADDR_47
|
|
+0x4B80 US_ALU_EXT_ADDR_48
|
|
+0x4B84 US_ALU_EXT_ADDR_49
|
|
+0x4B88 US_ALU_EXT_ADDR_50
|
|
+0x4B8C US_ALU_EXT_ADDR_51
|
|
+0x4B90 US_ALU_EXT_ADDR_52
|
|
+0x4B94 US_ALU_EXT_ADDR_53
|
|
+0x4B98 US_ALU_EXT_ADDR_54
|
|
+0x4B9C US_ALU_EXT_ADDR_55
|
|
+0x4BA0 US_ALU_EXT_ADDR_56
|
|
+0x4BA4 US_ALU_EXT_ADDR_57
|
|
+0x4BA8 US_ALU_EXT_ADDR_58
|
|
+0x4BAC US_ALU_EXT_ADDR_59
|
|
+0x4BB0 US_ALU_EXT_ADDR_60
|
|
+0x4BB4 US_ALU_EXT_ADDR_61
|
|
+0x4BB8 US_ALU_EXT_ADDR_62
|
|
+0x4BBC US_ALU_EXT_ADDR_63
|
|
+0x4BC0 FG_FOG_BLEND
|
|
+0x4BC4 FG_FOG_FACTOR
|
|
+0x4BC8 FG_FOG_COLOR_R
|
|
+0x4BCC FG_FOG_COLOR_G
|
|
+0x4BD0 FG_FOG_COLOR_B
|
|
+0x4BD4 FG_ALPHA_FUNC
|
|
+0x4BD8 FG_DEPTH_SRC
|
|
+0x4C00 US_ALU_CONST_R_0
|
|
+0x4C04 US_ALU_CONST_G_0
|
|
+0x4C08 US_ALU_CONST_B_0
|
|
+0x4C0C US_ALU_CONST_A_0
|
|
+0x4C10 US_ALU_CONST_R_1
|
|
+0x4C14 US_ALU_CONST_G_1
|
|
+0x4C18 US_ALU_CONST_B_1
|
|
+0x4C1C US_ALU_CONST_A_1
|
|
+0x4C20 US_ALU_CONST_R_2
|
|
+0x4C24 US_ALU_CONST_G_2
|
|
+0x4C28 US_ALU_CONST_B_2
|
|
+0x4C2C US_ALU_CONST_A_2
|
|
+0x4C30 US_ALU_CONST_R_3
|
|
+0x4C34 US_ALU_CONST_G_3
|
|
+0x4C38 US_ALU_CONST_B_3
|
|
+0x4C3C US_ALU_CONST_A_3
|
|
+0x4C40 US_ALU_CONST_R_4
|
|
+0x4C44 US_ALU_CONST_G_4
|
|
+0x4C48 US_ALU_CONST_B_4
|
|
+0x4C4C US_ALU_CONST_A_4
|
|
+0x4C50 US_ALU_CONST_R_5
|
|
+0x4C54 US_ALU_CONST_G_5
|
|
+0x4C58 US_ALU_CONST_B_5
|
|
+0x4C5C US_ALU_CONST_A_5
|
|
+0x4C60 US_ALU_CONST_R_6
|
|
+0x4C64 US_ALU_CONST_G_6
|
|
+0x4C68 US_ALU_CONST_B_6
|
|
+0x4C6C US_ALU_CONST_A_6
|
|
+0x4C70 US_ALU_CONST_R_7
|
|
+0x4C74 US_ALU_CONST_G_7
|
|
+0x4C78 US_ALU_CONST_B_7
|
|
+0x4C7C US_ALU_CONST_A_7
|
|
+0x4C80 US_ALU_CONST_R_8
|
|
+0x4C84 US_ALU_CONST_G_8
|
|
+0x4C88 US_ALU_CONST_B_8
|
|
+0x4C8C US_ALU_CONST_A_8
|
|
+0x4C90 US_ALU_CONST_R_9
|
|
+0x4C94 US_ALU_CONST_G_9
|
|
+0x4C98 US_ALU_CONST_B_9
|
|
+0x4C9C US_ALU_CONST_A_9
|
|
+0x4CA0 US_ALU_CONST_R_10
|
|
+0x4CA4 US_ALU_CONST_G_10
|
|
+0x4CA8 US_ALU_CONST_B_10
|
|
+0x4CAC US_ALU_CONST_A_10
|
|
+0x4CB0 US_ALU_CONST_R_11
|
|
+0x4CB4 US_ALU_CONST_G_11
|
|
+0x4CB8 US_ALU_CONST_B_11
|
|
+0x4CBC US_ALU_CONST_A_11
|
|
+0x4CC0 US_ALU_CONST_R_12
|
|
+0x4CC4 US_ALU_CONST_G_12
|
|
+0x4CC8 US_ALU_CONST_B_12
|
|
+0x4CCC US_ALU_CONST_A_12
|
|
+0x4CD0 US_ALU_CONST_R_13
|
|
+0x4CD4 US_ALU_CONST_G_13
|
|
+0x4CD8 US_ALU_CONST_B_13
|
|
+0x4CDC US_ALU_CONST_A_13
|
|
+0x4CE0 US_ALU_CONST_R_14
|
|
+0x4CE4 US_ALU_CONST_G_14
|
|
+0x4CE8 US_ALU_CONST_B_14
|
|
+0x4CEC US_ALU_CONST_A_14
|
|
+0x4CF0 US_ALU_CONST_R_15
|
|
+0x4CF4 US_ALU_CONST_G_15
|
|
+0x4CF8 US_ALU_CONST_B_15
|
|
+0x4CFC US_ALU_CONST_A_15
|
|
+0x4D00 US_ALU_CONST_R_16
|
|
+0x4D04 US_ALU_CONST_G_16
|
|
+0x4D08 US_ALU_CONST_B_16
|
|
+0x4D0C US_ALU_CONST_A_16
|
|
+0x4D10 US_ALU_CONST_R_17
|
|
+0x4D14 US_ALU_CONST_G_17
|
|
+0x4D18 US_ALU_CONST_B_17
|
|
+0x4D1C US_ALU_CONST_A_17
|
|
+0x4D20 US_ALU_CONST_R_18
|
|
+0x4D24 US_ALU_CONST_G_18
|
|
+0x4D28 US_ALU_CONST_B_18
|
|
+0x4D2C US_ALU_CONST_A_18
|
|
+0x4D30 US_ALU_CONST_R_19
|
|
+0x4D34 US_ALU_CONST_G_19
|
|
+0x4D38 US_ALU_CONST_B_19
|
|
+0x4D3C US_ALU_CONST_A_19
|
|
+0x4D40 US_ALU_CONST_R_20
|
|
+0x4D44 US_ALU_CONST_G_20
|
|
+0x4D48 US_ALU_CONST_B_20
|
|
+0x4D4C US_ALU_CONST_A_20
|
|
+0x4D50 US_ALU_CONST_R_21
|
|
+0x4D54 US_ALU_CONST_G_21
|
|
+0x4D58 US_ALU_CONST_B_21
|
|
+0x4D5C US_ALU_CONST_A_21
|
|
+0x4D60 US_ALU_CONST_R_22
|
|
+0x4D64 US_ALU_CONST_G_22
|
|
+0x4D68 US_ALU_CONST_B_22
|
|
+0x4D6C US_ALU_CONST_A_22
|
|
+0x4D70 US_ALU_CONST_R_23
|
|
+0x4D74 US_ALU_CONST_G_23
|
|
+0x4D78 US_ALU_CONST_B_23
|
|
+0x4D7C US_ALU_CONST_A_23
|
|
+0x4D80 US_ALU_CONST_R_24
|
|
+0x4D84 US_ALU_CONST_G_24
|
|
+0x4D88 US_ALU_CONST_B_24
|
|
+0x4D8C US_ALU_CONST_A_24
|
|
+0x4D90 US_ALU_CONST_R_25
|
|
+0x4D94 US_ALU_CONST_G_25
|
|
+0x4D98 US_ALU_CONST_B_25
|
|
+0x4D9C US_ALU_CONST_A_25
|
|
+0x4DA0 US_ALU_CONST_R_26
|
|
+0x4DA4 US_ALU_CONST_G_26
|
|
+0x4DA8 US_ALU_CONST_B_26
|
|
+0x4DAC US_ALU_CONST_A_26
|
|
+0x4DB0 US_ALU_CONST_R_27
|
|
+0x4DB4 US_ALU_CONST_G_27
|
|
+0x4DB8 US_ALU_CONST_B_27
|
|
+0x4DBC US_ALU_CONST_A_27
|
|
+0x4DC0 US_ALU_CONST_R_28
|
|
+0x4DC4 US_ALU_CONST_G_28
|
|
+0x4DC8 US_ALU_CONST_B_28
|
|
+0x4DCC US_ALU_CONST_A_28
|
|
+0x4DD0 US_ALU_CONST_R_29
|
|
+0x4DD4 US_ALU_CONST_G_29
|
|
+0x4DD8 US_ALU_CONST_B_29
|
|
+0x4DDC US_ALU_CONST_A_29
|
|
+0x4DE0 US_ALU_CONST_R_30
|
|
+0x4DE4 US_ALU_CONST_G_30
|
|
+0x4DE8 US_ALU_CONST_B_30
|
|
+0x4DEC US_ALU_CONST_A_30
|
|
+0x4DF0 US_ALU_CONST_R_31
|
|
+0x4DF4 US_ALU_CONST_G_31
|
|
+0x4DF8 US_ALU_CONST_B_31
|
|
+0x4DFC US_ALU_CONST_A_31
|
|
+0x4E04 RB3D_BLENDCNTL_R3
|
|
+0x4E08 RB3D_ABLENDCNTL_R3
|
|
+0x4E0C RB3D_COLOR_CHANNEL_MASK
|
|
+0x4E10 RB3D_CONSTANT_COLOR
|
|
+0x4E14 RB3D_COLOR_CLEAR_VALUE
|
|
+0x4E18 RB3D_ROPCNTL_R3
|
|
+0x4E1C RB3D_CLRCMP_FLIPE_R3
|
|
+0x4E20 RB3D_CLRCMP_CLR_R3
|
|
+0x4E24 RB3D_CLRCMP_MSK_R3
|
|
+0x4E48 RB3D_DEBUG_CTL
|
|
+0x4E4C RB3D_DSTCACHE_CTLSTAT_R3
|
|
+0x4E50 RB3D_DITHER_CTL
|
|
+0x4E54 RB3D_CMASK_OFFSET0
|
|
+0x4E58 RB3D_CMASK_OFFSET1
|
|
+0x4E5C RB3D_CMASK_OFFSET2
|
|
+0x4E60 RB3D_CMASK_OFFSET3
|
|
+0x4E64 RB3D_CMASK_PITCH0
|
|
+0x4E68 RB3D_CMASK_PITCH1
|
|
+0x4E6C RB3D_CMASK_PITCH2
|
|
+0x4E70 RB3D_CMASK_PITCH3
|
|
+0x4E74 RB3D_CMASK_WRINDEX
|
|
+0x4E78 RB3D_CMASK_DWORD
|
|
+0x4E7C RB3D_CMASK_RDINDEX
|
|
+0x4E80 RB3D_AARESOLVE_OFFSET
|
|
+0x4E84 RB3D_AARESOLVE_PITCH
|
|
+0x4E88 RB3D_AARESOLVE_CTL
|
|
+0x4EA0 RB3D_DISCARD_SRC_PIXEL_LTE_THRESHOLD
|
|
+0x4EA4 RB3D_DISCARD_SRC_PIXEL_GTE_THRESHOLD
|
|
+0x4F04 ZB_ZSTENCILCNTL
|
|
+0x4F08 ZB_STENCILREFMASK
|
|
+0x4F14 ZB_ZTOP
|
|
+0x4F18 ZB_ZCACHE_CTLSTAT
|
|
+0x4F1C ZB_BW_CNTL
|
|
+0x4F28 ZB_DEPTHCLEARVALUE
|
|
+0x4F30 ZB_ZMASK_OFFSET
|
|
+0x4F34 ZB_ZMASK_PITCH
|
|
+0x4F38 ZB_ZMASK_WRINDEX
|
|
+0x4F3C ZB_ZMASK_DWORD
|
|
+0x4F40 ZB_ZMASK_RDINDEX
|
|
+0x4F44 ZB_HIZ_OFFSET
|
|
+0x4F48 ZB_HIZ_WRINDEX
|
|
+0x4F4C ZB_HIZ_DWORD
|
|
+0x4F50 ZB_HIZ_RDINDEX
|
|
+0x4F54 ZB_HIZ_PITCH
|
|
+0x4F58 ZB_ZPASS_DATA
|
|
diff --git a/drivers/gpu/drm/radeon/reg_srcs/rs600 b/drivers/gpu/drm/radeon/reg_srcs/rs600
|
|
index 8e3c0b8..6801b86 100644
|
|
--- a/drivers/gpu/drm/radeon/reg_srcs/rs600
|
|
+++ b/drivers/gpu/drm/radeon/reg_srcs/rs600
|
|
@@ -153,7 +153,7 @@ rs600 0x6d40
|
|
0x42A4 SU_POLY_OFFSET_FRONT_SCALE
|
|
0x42A8 SU_POLY_OFFSET_FRONT_OFFSET
|
|
0x42AC SU_POLY_OFFSET_BACK_SCALE
|
|
-0x42B0 SU_POLY_OFFSET_BACK_OFFSET
|
|
+0x42B0 SU_POLY_OFFSET_BACK_OFFSET
|
|
0x42B4 SU_POLY_OFFSET_ENABLE
|
|
0x42B8 SU_CULL_MODE
|
|
0x42C0 SU_DEPTH_SCALE
|
|
@@ -291,6 +291,8 @@ rs600 0x6d40
|
|
0x46AC US_OUT_FMT_2
|
|
0x46B0 US_OUT_FMT_3
|
|
0x46B4 US_W_FMT
|
|
+0x46B8 US_CODE_BANK
|
|
+0x46BC US_CODE_EXT
|
|
0x46C0 US_ALU_RGB_ADDR_0
|
|
0x46C4 US_ALU_RGB_ADDR_1
|
|
0x46C8 US_ALU_RGB_ADDR_2
|
|
@@ -547,6 +549,70 @@ rs600 0x6d40
|
|
0x4AB4 US_ALU_ALPHA_INST_61
|
|
0x4AB8 US_ALU_ALPHA_INST_62
|
|
0x4ABC US_ALU_ALPHA_INST_63
|
|
+0x4AC0 US_ALU_EXT_ADDR_0
|
|
+0x4AC4 US_ALU_EXT_ADDR_1
|
|
+0x4AC8 US_ALU_EXT_ADDR_2
|
|
+0x4ACC US_ALU_EXT_ADDR_3
|
|
+0x4AD0 US_ALU_EXT_ADDR_4
|
|
+0x4AD4 US_ALU_EXT_ADDR_5
|
|
+0x4AD8 US_ALU_EXT_ADDR_6
|
|
+0x4ADC US_ALU_EXT_ADDR_7
|
|
+0x4AE0 US_ALU_EXT_ADDR_8
|
|
+0x4AE4 US_ALU_EXT_ADDR_9
|
|
+0x4AE8 US_ALU_EXT_ADDR_10
|
|
+0x4AEC US_ALU_EXT_ADDR_11
|
|
+0x4AF0 US_ALU_EXT_ADDR_12
|
|
+0x4AF4 US_ALU_EXT_ADDR_13
|
|
+0x4AF8 US_ALU_EXT_ADDR_14
|
|
+0x4AFC US_ALU_EXT_ADDR_15
|
|
+0x4B00 US_ALU_EXT_ADDR_16
|
|
+0x4B04 US_ALU_EXT_ADDR_17
|
|
+0x4B08 US_ALU_EXT_ADDR_18
|
|
+0x4B0C US_ALU_EXT_ADDR_19
|
|
+0x4B10 US_ALU_EXT_ADDR_20
|
|
+0x4B14 US_ALU_EXT_ADDR_21
|
|
+0x4B18 US_ALU_EXT_ADDR_22
|
|
+0x4B1C US_ALU_EXT_ADDR_23
|
|
+0x4B20 US_ALU_EXT_ADDR_24
|
|
+0x4B24 US_ALU_EXT_ADDR_25
|
|
+0x4B28 US_ALU_EXT_ADDR_26
|
|
+0x4B2C US_ALU_EXT_ADDR_27
|
|
+0x4B30 US_ALU_EXT_ADDR_28
|
|
+0x4B34 US_ALU_EXT_ADDR_29
|
|
+0x4B38 US_ALU_EXT_ADDR_30
|
|
+0x4B3C US_ALU_EXT_ADDR_31
|
|
+0x4B40 US_ALU_EXT_ADDR_32
|
|
+0x4B44 US_ALU_EXT_ADDR_33
|
|
+0x4B48 US_ALU_EXT_ADDR_34
|
|
+0x4B4C US_ALU_EXT_ADDR_35
|
|
+0x4B50 US_ALU_EXT_ADDR_36
|
|
+0x4B54 US_ALU_EXT_ADDR_37
|
|
+0x4B58 US_ALU_EXT_ADDR_38
|
|
+0x4B5C US_ALU_EXT_ADDR_39
|
|
+0x4B60 US_ALU_EXT_ADDR_40
|
|
+0x4B64 US_ALU_EXT_ADDR_41
|
|
+0x4B68 US_ALU_EXT_ADDR_42
|
|
+0x4B6C US_ALU_EXT_ADDR_43
|
|
+0x4B70 US_ALU_EXT_ADDR_44
|
|
+0x4B74 US_ALU_EXT_ADDR_45
|
|
+0x4B78 US_ALU_EXT_ADDR_46
|
|
+0x4B7C US_ALU_EXT_ADDR_47
|
|
+0x4B80 US_ALU_EXT_ADDR_48
|
|
+0x4B84 US_ALU_EXT_ADDR_49
|
|
+0x4B88 US_ALU_EXT_ADDR_50
|
|
+0x4B8C US_ALU_EXT_ADDR_51
|
|
+0x4B90 US_ALU_EXT_ADDR_52
|
|
+0x4B94 US_ALU_EXT_ADDR_53
|
|
+0x4B98 US_ALU_EXT_ADDR_54
|
|
+0x4B9C US_ALU_EXT_ADDR_55
|
|
+0x4BA0 US_ALU_EXT_ADDR_56
|
|
+0x4BA4 US_ALU_EXT_ADDR_57
|
|
+0x4BA8 US_ALU_EXT_ADDR_58
|
|
+0x4BAC US_ALU_EXT_ADDR_59
|
|
+0x4BB0 US_ALU_EXT_ADDR_60
|
|
+0x4BB4 US_ALU_EXT_ADDR_61
|
|
+0x4BB8 US_ALU_EXT_ADDR_62
|
|
+0x4BBC US_ALU_EXT_ADDR_63
|
|
0x4BC0 FG_FOG_BLEND
|
|
0x4BC4 FG_FOG_FACTOR
|
|
0x4BC8 FG_FOG_COLOR_R
|
|
diff --git a/drivers/gpu/drm/radeon/reg_srcs/rv515 b/drivers/gpu/drm/radeon/reg_srcs/rv515
|
|
index 0102a0d..38abf63 100644
|
|
--- a/drivers/gpu/drm/radeon/reg_srcs/rv515
|
|
+++ b/drivers/gpu/drm/radeon/reg_srcs/rv515
|
|
@@ -161,7 +161,12 @@ rv515 0x6d40
|
|
0x401C GB_SELECT
|
|
0x4020 GB_AA_CONFIG
|
|
0x4024 GB_FIFO_SIZE
|
|
+0x4028 GB_Z_PEQ_CONFIG
|
|
0x4100 TX_INVALTAGS
|
|
+0x4114 SU_TEX_WRAP_PS3
|
|
+0x4118 PS3_ENABLE
|
|
+0x411c PS3_VTX_FMT
|
|
+0x4120 PS3_TEX_SOURCE
|
|
0x4200 GA_POINT_S0
|
|
0x4204 GA_POINT_T0
|
|
0x4208 GA_POINT_S1
|
|
@@ -171,6 +176,7 @@ rv515 0x6d40
|
|
0x4230 GA_POINT_MINMAX
|
|
0x4234 GA_LINE_CNTL
|
|
0x4238 GA_LINE_STIPPLE_CONFIG
|
|
+0x4258 GA_COLOR_CONTROL_PS3
|
|
0x4260 GA_LINE_STIPPLE_VALUE
|
|
0x4264 GA_LINE_S0
|
|
0x4268 GA_LINE_S1
|
|
diff --git a/drivers/gpu/drm/radeon/rs400.c b/drivers/gpu/drm/radeon/rs400.c
|
|
index ca03716..287fceb 100644
|
|
--- a/drivers/gpu/drm/radeon/rs400.c
|
|
+++ b/drivers/gpu/drm/radeon/rs400.c
|
|
@@ -223,15 +223,31 @@ int rs400_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr)
|
|
return 0;
|
|
}
|
|
|
|
+int rs400_mc_wait_for_idle(struct radeon_device *rdev)
|
|
+{
|
|
+ unsigned i;
|
|
+ uint32_t tmp;
|
|
+
|
|
+ for (i = 0; i < rdev->usec_timeout; i++) {
|
|
+ /* read MC_STATUS */
|
|
+ tmp = RREG32(0x0150);
|
|
+ if (tmp & (1 << 2)) {
|
|
+ return 0;
|
|
+ }
|
|
+ DRM_UDELAY(1);
|
|
+ }
|
|
+ return -1;
|
|
+}
|
|
+
|
|
void rs400_gpu_init(struct radeon_device *rdev)
|
|
{
|
|
/* FIXME: HDP same place on rs400 ? */
|
|
r100_hdp_reset(rdev);
|
|
/* FIXME: is this correct ? */
|
|
r420_pipes_init(rdev);
|
|
- if (r300_mc_wait_for_idle(rdev)) {
|
|
- printk(KERN_WARNING "Failed to wait MC idle while "
|
|
- "programming pipes. Bad things might happen.\n");
|
|
+ if (rs400_mc_wait_for_idle(rdev)) {
|
|
+ printk(KERN_WARNING "rs400: Failed to wait MC idle while "
|
|
+ "programming pipes. Bad things might happen. %08x\n", RREG32(0x150));
|
|
}
|
|
}
|
|
|
|
@@ -352,10 +368,11 @@ static int rs400_mc_init(struct radeon_device *rdev)
|
|
u32 tmp;
|
|
|
|
/* Setup GPU memory space */
|
|
- tmp = G_00015C_MC_FB_START(RREG32(R_00015C_NB_TOM));
|
|
+ tmp = RREG32(R_00015C_NB_TOM);
|
|
rdev->mc.vram_location = G_00015C_MC_FB_START(tmp) << 16;
|
|
rdev->mc.gtt_location = 0xFFFFFFFFUL;
|
|
r = radeon_mc_setup(rdev);
|
|
+ rdev->mc.igp_sideport_enabled = radeon_combios_sideport_present(rdev);
|
|
if (r)
|
|
return r;
|
|
return 0;
|
|
@@ -369,8 +386,8 @@ void rs400_mc_program(struct radeon_device *rdev)
|
|
r100_mc_stop(rdev, &save);
|
|
|
|
/* Wait for mc idle */
|
|
- if (r300_mc_wait_for_idle(rdev))
|
|
- dev_warn(rdev->dev, "Wait MC idle timeout before updating MC.\n");
|
|
+ if (rs400_mc_wait_for_idle(rdev))
|
|
+ dev_warn(rdev->dev, "rs400: Wait MC idle timeout before updating MC.\n");
|
|
WREG32(R_000148_MC_FB_LOCATION,
|
|
S_000148_MC_FB_START(rdev->mc.vram_start >> 16) |
|
|
S_000148_MC_FB_TOP(rdev->mc.vram_end >> 16));
|
|
@@ -387,14 +404,15 @@ static int rs400_startup(struct radeon_device *rdev)
|
|
r300_clock_startup(rdev);
|
|
/* Initialize GPU configuration (# pipes, ...) */
|
|
rs400_gpu_init(rdev);
|
|
+ r100_enable_bm(rdev);
|
|
/* Initialize GART (initialize after TTM so we can allocate
|
|
* memory through TTM but finalize after TTM) */
|
|
r = rs400_gart_enable(rdev);
|
|
if (r)
|
|
return r;
|
|
/* Enable IRQ */
|
|
- rdev->irq.sw_int = true;
|
|
r100_irq_set(rdev);
|
|
+ rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL);
|
|
/* 1M ring buffer */
|
|
r = r100_cp_init(rdev, 1024 * 1024);
|
|
if (r) {
|
|
@@ -430,6 +448,8 @@ int rs400_resume(struct radeon_device *rdev)
|
|
radeon_combios_asic_init(rdev->ddev);
|
|
/* Resume clock after posting */
|
|
r300_clock_startup(rdev);
|
|
+ /* Initialize surface registers */
|
|
+ radeon_surface_init(rdev);
|
|
return rs400_startup(rdev);
|
|
}
|
|
|
|
@@ -444,7 +464,6 @@ int rs400_suspend(struct radeon_device *rdev)
|
|
|
|
void rs400_fini(struct radeon_device *rdev)
|
|
{
|
|
- rs400_suspend(rdev);
|
|
r100_cp_fini(rdev);
|
|
r100_wb_fini(rdev);
|
|
r100_ib_fini(rdev);
|
|
@@ -452,7 +471,7 @@ void rs400_fini(struct radeon_device *rdev)
|
|
rs400_gart_fini(rdev);
|
|
radeon_irq_kms_fini(rdev);
|
|
radeon_fence_driver_fini(rdev);
|
|
- radeon_object_fini(rdev);
|
|
+ radeon_bo_fini(rdev);
|
|
radeon_atombios_fini(rdev);
|
|
kfree(rdev->bios);
|
|
rdev->bios = NULL;
|
|
@@ -490,12 +509,13 @@ int rs400_init(struct radeon_device *rdev)
|
|
RREG32(R_0007C0_CP_STAT));
|
|
}
|
|
/* check if cards are posted or not */
|
|
- if (!radeon_card_posted(rdev) && rdev->bios) {
|
|
- DRM_INFO("GPU not posted. posting now...\n");
|
|
- radeon_combios_asic_init(rdev->ddev);
|
|
- }
|
|
+ if (radeon_boot_test_post_card(rdev) == false)
|
|
+ return -EINVAL;
|
|
+
|
|
/* Initialize clocks */
|
|
radeon_get_clock_info(rdev->ddev);
|
|
+ /* Initialize power management */
|
|
+ radeon_pm_init(rdev);
|
|
/* Get vram informations */
|
|
rs400_vram_info(rdev);
|
|
/* Initialize memory controller (also test AGP) */
|
|
@@ -510,7 +530,7 @@ int rs400_init(struct radeon_device *rdev)
|
|
if (r)
|
|
return r;
|
|
/* Memory manager */
|
|
- r = radeon_object_init(rdev);
|
|
+ r = radeon_bo_init(rdev);
|
|
if (r)
|
|
return r;
|
|
r = rs400_gart_init(rdev);
|
|
@@ -522,7 +542,6 @@ int rs400_init(struct radeon_device *rdev)
|
|
if (r) {
|
|
/* Somethings want wront with the accel init stop accel */
|
|
dev_err(rdev->dev, "Disabling GPU acceleration\n");
|
|
- rs400_suspend(rdev);
|
|
r100_cp_fini(rdev);
|
|
r100_wb_fini(rdev);
|
|
r100_ib_fini(rdev);
|
|
diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c
|
|
index 4444f48..c381856 100644
|
|
--- a/drivers/gpu/drm/radeon/rs600.c
|
|
+++ b/drivers/gpu/drm/radeon/rs600.c
|
|
@@ -45,6 +45,124 @@
|
|
void rs600_gpu_init(struct radeon_device *rdev);
|
|
int rs600_mc_wait_for_idle(struct radeon_device *rdev);
|
|
|
|
+int rs600_mc_init(struct radeon_device *rdev)
|
|
+{
|
|
+ /* read back the MC value from the hw */
|
|
+ int r;
|
|
+ u32 tmp;
|
|
+
|
|
+ /* Setup GPU memory space */
|
|
+ tmp = RREG32_MC(R_000004_MC_FB_LOCATION);
|
|
+ rdev->mc.vram_location = G_000004_MC_FB_START(tmp) << 16;
|
|
+ rdev->mc.gtt_location = 0xffffffffUL;
|
|
+ r = radeon_mc_setup(rdev);
|
|
+ rdev->mc.igp_sideport_enabled = radeon_atombios_sideport_present(rdev);
|
|
+ if (r)
|
|
+ return r;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* hpd for digital panel detect/disconnect */
|
|
+bool rs600_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd)
|
|
+{
|
|
+ u32 tmp;
|
|
+ bool connected = false;
|
|
+
|
|
+ switch (hpd) {
|
|
+ case RADEON_HPD_1:
|
|
+ tmp = RREG32(R_007D04_DC_HOT_PLUG_DETECT1_INT_STATUS);
|
|
+ if (G_007D04_DC_HOT_PLUG_DETECT1_SENSE(tmp))
|
|
+ connected = true;
|
|
+ break;
|
|
+ case RADEON_HPD_2:
|
|
+ tmp = RREG32(R_007D14_DC_HOT_PLUG_DETECT2_INT_STATUS);
|
|
+ if (G_007D14_DC_HOT_PLUG_DETECT2_SENSE(tmp))
|
|
+ connected = true;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ return connected;
|
|
+}
|
|
+
|
|
+void rs600_hpd_set_polarity(struct radeon_device *rdev,
|
|
+ enum radeon_hpd_id hpd)
|
|
+{
|
|
+ u32 tmp;
|
|
+ bool connected = rs600_hpd_sense(rdev, hpd);
|
|
+
|
|
+ switch (hpd) {
|
|
+ case RADEON_HPD_1:
|
|
+ tmp = RREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL);
|
|
+ if (connected)
|
|
+ tmp &= ~S_007D08_DC_HOT_PLUG_DETECT1_INT_POLARITY(1);
|
|
+ else
|
|
+ tmp |= S_007D08_DC_HOT_PLUG_DETECT1_INT_POLARITY(1);
|
|
+ WREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL, tmp);
|
|
+ break;
|
|
+ case RADEON_HPD_2:
|
|
+ tmp = RREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL);
|
|
+ if (connected)
|
|
+ tmp &= ~S_007D18_DC_HOT_PLUG_DETECT2_INT_POLARITY(1);
|
|
+ else
|
|
+ tmp |= S_007D18_DC_HOT_PLUG_DETECT2_INT_POLARITY(1);
|
|
+ WREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL, tmp);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+void rs600_hpd_init(struct radeon_device *rdev)
|
|
+{
|
|
+ struct drm_device *dev = rdev->ddev;
|
|
+ struct drm_connector *connector;
|
|
+
|
|
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
|
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
|
|
+ switch (radeon_connector->hpd.hpd) {
|
|
+ case RADEON_HPD_1:
|
|
+ WREG32(R_007D00_DC_HOT_PLUG_DETECT1_CONTROL,
|
|
+ S_007D00_DC_HOT_PLUG_DETECT1_EN(1));
|
|
+ rdev->irq.hpd[0] = true;
|
|
+ break;
|
|
+ case RADEON_HPD_2:
|
|
+ WREG32(R_007D10_DC_HOT_PLUG_DETECT2_CONTROL,
|
|
+ S_007D10_DC_HOT_PLUG_DETECT2_EN(1));
|
|
+ rdev->irq.hpd[1] = true;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (rdev->irq.installed)
|
|
+ rs600_irq_set(rdev);
|
|
+}
|
|
+
|
|
+void rs600_hpd_fini(struct radeon_device *rdev)
|
|
+{
|
|
+ struct drm_device *dev = rdev->ddev;
|
|
+ struct drm_connector *connector;
|
|
+
|
|
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
|
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
|
|
+ switch (radeon_connector->hpd.hpd) {
|
|
+ case RADEON_HPD_1:
|
|
+ WREG32(R_007D00_DC_HOT_PLUG_DETECT1_CONTROL,
|
|
+ S_007D00_DC_HOT_PLUG_DETECT1_EN(0));
|
|
+ rdev->irq.hpd[0] = false;
|
|
+ break;
|
|
+ case RADEON_HPD_2:
|
|
+ WREG32(R_007D10_DC_HOT_PLUG_DETECT2_CONTROL,
|
|
+ S_007D10_DC_HOT_PLUG_DETECT2_EN(0));
|
|
+ rdev->irq.hpd[1] = false;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
/*
|
|
* GART.
|
|
*/
|
|
@@ -100,40 +218,40 @@ int rs600_gart_enable(struct radeon_device *rdev)
|
|
WREG32(R_00004C_BUS_CNTL, tmp);
|
|
/* FIXME: setup default page */
|
|
WREG32_MC(R_000100_MC_PT0_CNTL,
|
|
- (S_000100_EFFECTIVE_L2_CACHE_SIZE(6) |
|
|
- S_000100_EFFECTIVE_L2_QUEUE_SIZE(6)));
|
|
+ (S_000100_EFFECTIVE_L2_CACHE_SIZE(6) |
|
|
+ S_000100_EFFECTIVE_L2_QUEUE_SIZE(6)));
|
|
+
|
|
for (i = 0; i < 19; i++) {
|
|
WREG32_MC(R_00016C_MC_PT0_CLIENT0_CNTL + i,
|
|
- S_00016C_ENABLE_TRANSLATION_MODE_OVERRIDE(1) |
|
|
- S_00016C_SYSTEM_ACCESS_MODE_MASK(
|
|
- V_00016C_SYSTEM_ACCESS_MODE_IN_SYS) |
|
|
- S_00016C_SYSTEM_APERTURE_UNMAPPED_ACCESS(
|
|
- V_00016C_SYSTEM_APERTURE_UNMAPPED_DEFAULT_PAGE) |
|
|
- S_00016C_EFFECTIVE_L1_CACHE_SIZE(1) |
|
|
- S_00016C_ENABLE_FRAGMENT_PROCESSING(1) |
|
|
- S_00016C_EFFECTIVE_L1_QUEUE_SIZE(1));
|
|
+ S_00016C_ENABLE_TRANSLATION_MODE_OVERRIDE(1) |
|
|
+ S_00016C_SYSTEM_ACCESS_MODE_MASK(
|
|
+ V_00016C_SYSTEM_ACCESS_MODE_NOT_IN_SYS) |
|
|
+ S_00016C_SYSTEM_APERTURE_UNMAPPED_ACCESS(
|
|
+ V_00016C_SYSTEM_APERTURE_UNMAPPED_PASSTHROUGH) |
|
|
+ S_00016C_EFFECTIVE_L1_CACHE_SIZE(3) |
|
|
+ S_00016C_ENABLE_FRAGMENT_PROCESSING(1) |
|
|
+ S_00016C_EFFECTIVE_L1_QUEUE_SIZE(3));
|
|
}
|
|
-
|
|
- /* System context map to GART space */
|
|
- WREG32_MC(R_000112_MC_PT0_SYSTEM_APERTURE_LOW_ADDR, rdev->mc.gtt_start);
|
|
- WREG32_MC(R_000114_MC_PT0_SYSTEM_APERTURE_HIGH_ADDR, rdev->mc.gtt_end);
|
|
-
|
|
/* enable first context */
|
|
- WREG32_MC(R_00013C_MC_PT0_CONTEXT0_FLAT_START_ADDR, rdev->mc.gtt_start);
|
|
- WREG32_MC(R_00014C_MC_PT0_CONTEXT0_FLAT_END_ADDR, rdev->mc.gtt_end);
|
|
WREG32_MC(R_000102_MC_PT0_CONTEXT0_CNTL,
|
|
- S_000102_ENABLE_PAGE_TABLE(1) |
|
|
- S_000102_PAGE_TABLE_DEPTH(V_000102_PAGE_TABLE_FLAT));
|
|
+ S_000102_ENABLE_PAGE_TABLE(1) |
|
|
+ S_000102_PAGE_TABLE_DEPTH(V_000102_PAGE_TABLE_FLAT));
|
|
+
|
|
/* disable all other contexts */
|
|
- for (i = 1; i < 8; i++) {
|
|
+ for (i = 1; i < 8; i++)
|
|
WREG32_MC(R_000102_MC_PT0_CONTEXT0_CNTL + i, 0);
|
|
- }
|
|
|
|
/* setup the page table */
|
|
WREG32_MC(R_00012C_MC_PT0_CONTEXT0_FLAT_BASE_ADDR,
|
|
- rdev->gart.table_addr);
|
|
+ rdev->gart.table_addr);
|
|
+ WREG32_MC(R_00013C_MC_PT0_CONTEXT0_FLAT_START_ADDR, rdev->mc.gtt_start);
|
|
+ WREG32_MC(R_00014C_MC_PT0_CONTEXT0_FLAT_END_ADDR, rdev->mc.gtt_end);
|
|
WREG32_MC(R_00011C_MC_PT0_CONTEXT0_DEFAULT_READ_ADDR, 0);
|
|
|
|
+ /* System context maps to VRAM space */
|
|
+ WREG32_MC(R_000112_MC_PT0_SYSTEM_APERTURE_LOW_ADDR, rdev->mc.vram_start);
|
|
+ WREG32_MC(R_000114_MC_PT0_SYSTEM_APERTURE_HIGH_ADDR, rdev->mc.vram_end);
|
|
+
|
|
/* enable page tables */
|
|
tmp = RREG32_MC(R_000100_MC_PT0_CNTL);
|
|
WREG32_MC(R_000100_MC_PT0_CNTL, (tmp | S_000100_ENABLE_PT(1)));
|
|
@@ -146,15 +264,20 @@ int rs600_gart_enable(struct radeon_device *rdev)
|
|
|
|
void rs600_gart_disable(struct radeon_device *rdev)
|
|
{
|
|
- uint32_t tmp;
|
|
+ u32 tmp;
|
|
+ int r;
|
|
|
|
/* FIXME: disable out of gart access */
|
|
WREG32_MC(R_000100_MC_PT0_CNTL, 0);
|
|
tmp = RREG32_MC(R_000009_MC_CNTL1);
|
|
WREG32_MC(R_000009_MC_CNTL1, tmp & C_000009_ENABLE_PAGE_TABLES);
|
|
if (rdev->gart.table.vram.robj) {
|
|
- radeon_object_kunmap(rdev->gart.table.vram.robj);
|
|
- radeon_object_unpin(rdev->gart.table.vram.robj);
|
|
+ r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
|
|
+ if (r == 0) {
|
|
+ radeon_bo_kunmap(rdev->gart.table.vram.robj);
|
|
+ radeon_bo_unpin(rdev->gart.table.vram.robj);
|
|
+ radeon_bo_unreserve(rdev->gart.table.vram.robj);
|
|
+ }
|
|
}
|
|
}
|
|
|
|
@@ -189,7 +312,16 @@ int rs600_irq_set(struct radeon_device *rdev)
|
|
{
|
|
uint32_t tmp = 0;
|
|
uint32_t mode_int = 0;
|
|
-
|
|
+ u32 hpd1 = RREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL) &
|
|
+ ~S_007D08_DC_HOT_PLUG_DETECT1_INT_EN(1);
|
|
+ u32 hpd2 = RREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL) &
|
|
+ ~S_007D18_DC_HOT_PLUG_DETECT2_INT_EN(1);
|
|
+
|
|
+ if (!rdev->irq.installed) {
|
|
+ WARN(1, "Can't enable IRQ/MSI because no handler is installed.\n");
|
|
+ WREG32(R_000040_GEN_INT_CNTL, 0);
|
|
+ return -EINVAL;
|
|
+ }
|
|
if (rdev->irq.sw_int) {
|
|
tmp |= S_000040_SW_INT_EN(1);
|
|
}
|
|
@@ -199,8 +331,16 @@ int rs600_irq_set(struct radeon_device *rdev)
|
|
if (rdev->irq.crtc_vblank_int[1]) {
|
|
mode_int |= S_006540_D2MODE_VBLANK_INT_MASK(1);
|
|
}
|
|
+ if (rdev->irq.hpd[0]) {
|
|
+ hpd1 |= S_007D08_DC_HOT_PLUG_DETECT1_INT_EN(1);
|
|
+ }
|
|
+ if (rdev->irq.hpd[1]) {
|
|
+ hpd2 |= S_007D18_DC_HOT_PLUG_DETECT2_INT_EN(1);
|
|
+ }
|
|
WREG32(R_000040_GEN_INT_CNTL, tmp);
|
|
WREG32(R_006540_DxMODE_INT_MASK, mode_int);
|
|
+ WREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL, hpd1);
|
|
+ WREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL, hpd2);
|
|
return 0;
|
|
}
|
|
|
|
@@ -208,6 +348,7 @@ static inline uint32_t rs600_irq_ack(struct radeon_device *rdev, u32 *r500_disp_
|
|
{
|
|
uint32_t irqs = RREG32(R_000044_GEN_INT_STATUS);
|
|
uint32_t irq_mask = ~C_000044_SW_INT;
|
|
+ u32 tmp;
|
|
|
|
if (G_000044_DISPLAY_INT_STAT(irqs)) {
|
|
*r500_disp_int = RREG32(R_007EDC_DISP_INTERRUPT_STATUS);
|
|
@@ -219,6 +360,16 @@ static inline uint32_t rs600_irq_ack(struct radeon_device *rdev, u32 *r500_disp_
|
|
WREG32(R_006D34_D2MODE_VBLANK_STATUS,
|
|
S_006D34_D2MODE_VBLANK_ACK(1));
|
|
}
|
|
+ if (G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(*r500_disp_int)) {
|
|
+ tmp = RREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL);
|
|
+ tmp |= S_007D08_DC_HOT_PLUG_DETECT1_INT_ACK(1);
|
|
+ WREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL, tmp);
|
|
+ }
|
|
+ if (G_007EDC_DC_HOT_PLUG_DETECT2_INTERRUPT(*r500_disp_int)) {
|
|
+ tmp = RREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL);
|
|
+ tmp |= S_007D18_DC_HOT_PLUG_DETECT2_INT_ACK(1);
|
|
+ WREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL, tmp);
|
|
+ }
|
|
} else {
|
|
*r500_disp_int = 0;
|
|
}
|
|
@@ -244,6 +395,7 @@ int rs600_irq_process(struct radeon_device *rdev)
|
|
{
|
|
uint32_t status, msi_rearm;
|
|
uint32_t r500_disp_int;
|
|
+ bool queue_hotplug = false;
|
|
|
|
status = rs600_irq_ack(rdev, &r500_disp_int);
|
|
if (!status && !r500_disp_int) {
|
|
@@ -251,15 +403,25 @@ int rs600_irq_process(struct radeon_device *rdev)
|
|
}
|
|
while (status || r500_disp_int) {
|
|
/* SW interrupt */
|
|
- if (G_000040_SW_INT_EN(status))
|
|
+ if (G_000044_SW_INT(status))
|
|
radeon_fence_process(rdev);
|
|
/* Vertical blank interrupts */
|
|
if (G_007EDC_LB_D1_VBLANK_INTERRUPT(r500_disp_int))
|
|
drm_handle_vblank(rdev->ddev, 0);
|
|
if (G_007EDC_LB_D2_VBLANK_INTERRUPT(r500_disp_int))
|
|
drm_handle_vblank(rdev->ddev, 1);
|
|
+ if (G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(r500_disp_int)) {
|
|
+ queue_hotplug = true;
|
|
+ DRM_DEBUG("HPD1\n");
|
|
+ }
|
|
+ if (G_007EDC_DC_HOT_PLUG_DETECT2_INTERRUPT(r500_disp_int)) {
|
|
+ queue_hotplug = true;
|
|
+ DRM_DEBUG("HPD2\n");
|
|
+ }
|
|
status = rs600_irq_ack(rdev, &r500_disp_int);
|
|
}
|
|
+ if (queue_hotplug)
|
|
+ queue_work(rdev->wq, &rdev->hotplug_work);
|
|
if (rdev->msi_enabled) {
|
|
switch (rdev->family) {
|
|
case CHIP_RS600:
|
|
@@ -397,8 +559,8 @@ static int rs600_startup(struct radeon_device *rdev)
|
|
if (r)
|
|
return r;
|
|
/* Enable IRQ */
|
|
- rdev->irq.sw_int = true;
|
|
rs600_irq_set(rdev);
|
|
+ rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL);
|
|
/* 1M ring buffer */
|
|
r = r100_cp_init(rdev, 1024 * 1024);
|
|
if (r) {
|
|
@@ -432,6 +594,8 @@ int rs600_resume(struct radeon_device *rdev)
|
|
atom_asic_init(rdev->mode_info.atom_context);
|
|
/* Resume clock after posting */
|
|
rv515_clock_startup(rdev);
|
|
+ /* Initialize surface registers */
|
|
+ radeon_surface_init(rdev);
|
|
return rs600_startup(rdev);
|
|
}
|
|
|
|
@@ -446,7 +610,6 @@ int rs600_suspend(struct radeon_device *rdev)
|
|
|
|
void rs600_fini(struct radeon_device *rdev)
|
|
{
|
|
- rs600_suspend(rdev);
|
|
r100_cp_fini(rdev);
|
|
r100_wb_fini(rdev);
|
|
r100_ib_fini(rdev);
|
|
@@ -454,7 +617,7 @@ void rs600_fini(struct radeon_device *rdev)
|
|
rs600_gart_fini(rdev);
|
|
radeon_irq_kms_fini(rdev);
|
|
radeon_fence_driver_fini(rdev);
|
|
- radeon_object_fini(rdev);
|
|
+ radeon_bo_fini(rdev);
|
|
radeon_atombios_fini(rdev);
|
|
kfree(rdev->bios);
|
|
rdev->bios = NULL;
|
|
@@ -491,10 +654,9 @@ int rs600_init(struct radeon_device *rdev)
|
|
RREG32(R_0007C0_CP_STAT));
|
|
}
|
|
/* check if cards are posted or not */
|
|
- if (!radeon_card_posted(rdev) && rdev->bios) {
|
|
- DRM_INFO("GPU not posted. posting now...\n");
|
|
- atom_asic_init(rdev->mode_info.atom_context);
|
|
- }
|
|
+ if (radeon_boot_test_post_card(rdev) == false)
|
|
+ return -EINVAL;
|
|
+
|
|
/* Initialize clocks */
|
|
radeon_get_clock_info(rdev->ddev);
|
|
/* Initialize power management */
|
|
@@ -502,7 +664,7 @@ int rs600_init(struct radeon_device *rdev)
|
|
/* Get vram informations */
|
|
rs600_vram_info(rdev);
|
|
/* Initialize memory controller (also test AGP) */
|
|
- r = r420_mc_init(rdev);
|
|
+ r = rs600_mc_init(rdev);
|
|
if (r)
|
|
return r;
|
|
rs600_debugfs(rdev);
|
|
@@ -514,7 +676,7 @@ int rs600_init(struct radeon_device *rdev)
|
|
if (r)
|
|
return r;
|
|
/* Memory manager */
|
|
- r = radeon_object_init(rdev);
|
|
+ r = radeon_bo_init(rdev);
|
|
if (r)
|
|
return r;
|
|
r = rs600_gart_init(rdev);
|
|
@@ -526,7 +688,6 @@ int rs600_init(struct radeon_device *rdev)
|
|
if (r) {
|
|
/* Somethings want wront with the accel init stop accel */
|
|
dev_err(rdev->dev, "Disabling GPU acceleration\n");
|
|
- rs600_suspend(rdev);
|
|
r100_cp_fini(rdev);
|
|
r100_wb_fini(rdev);
|
|
r100_ib_fini(rdev);
|
|
diff --git a/drivers/gpu/drm/radeon/rs600d.h b/drivers/gpu/drm/radeon/rs600d.h
|
|
index 8130892..c1c8f58 100644
|
|
--- a/drivers/gpu/drm/radeon/rs600d.h
|
|
+++ b/drivers/gpu/drm/radeon/rs600d.h
|
|
@@ -30,27 +30,12 @@
|
|
|
|
/* Registers */
|
|
#define R_000040_GEN_INT_CNTL 0x000040
|
|
-#define S_000040_DISPLAY_INT_STATUS(x) (((x) & 0x1) << 0)
|
|
-#define G_000040_DISPLAY_INT_STATUS(x) (((x) >> 0) & 0x1)
|
|
-#define C_000040_DISPLAY_INT_STATUS 0xFFFFFFFE
|
|
-#define S_000040_DMA_VIPH0_INT_EN(x) (((x) & 0x1) << 12)
|
|
-#define G_000040_DMA_VIPH0_INT_EN(x) (((x) >> 12) & 0x1)
|
|
-#define C_000040_DMA_VIPH0_INT_EN 0xFFFFEFFF
|
|
-#define S_000040_CRTC2_VSYNC(x) (((x) & 0x1) << 6)
|
|
-#define G_000040_CRTC2_VSYNC(x) (((x) >> 6) & 0x1)
|
|
-#define C_000040_CRTC2_VSYNC 0xFFFFFFBF
|
|
-#define S_000040_SNAPSHOT2(x) (((x) & 0x1) << 7)
|
|
-#define G_000040_SNAPSHOT2(x) (((x) >> 7) & 0x1)
|
|
-#define C_000040_SNAPSHOT2 0xFFFFFF7F
|
|
-#define S_000040_CRTC2_VBLANK(x) (((x) & 0x1) << 9)
|
|
-#define G_000040_CRTC2_VBLANK(x) (((x) >> 9) & 0x1)
|
|
-#define C_000040_CRTC2_VBLANK 0xFFFFFDFF
|
|
-#define S_000040_FP2_DETECT(x) (((x) & 0x1) << 10)
|
|
-#define G_000040_FP2_DETECT(x) (((x) >> 10) & 0x1)
|
|
-#define C_000040_FP2_DETECT 0xFFFFFBFF
|
|
-#define S_000040_VSYNC_DIFF_OVER_LIMIT(x) (((x) & 0x1) << 11)
|
|
-#define G_000040_VSYNC_DIFF_OVER_LIMIT(x) (((x) >> 11) & 0x1)
|
|
-#define C_000040_VSYNC_DIFF_OVER_LIMIT 0xFFFFF7FF
|
|
+#define S_000040_SCRATCH_INT_MASK(x) (((x) & 0x1) << 18)
|
|
+#define G_000040_SCRATCH_INT_MASK(x) (((x) >> 18) & 0x1)
|
|
+#define C_000040_SCRATCH_INT_MASK 0xFFFBFFFF
|
|
+#define S_000040_GUI_IDLE_MASK(x) (((x) & 0x1) << 19)
|
|
+#define G_000040_GUI_IDLE_MASK(x) (((x) >> 19) & 0x1)
|
|
+#define C_000040_GUI_IDLE_MASK 0xFFF7FFFF
|
|
#define S_000040_DMA_VIPH1_INT_EN(x) (((x) & 0x1) << 13)
|
|
#define G_000040_DMA_VIPH1_INT_EN(x) (((x) >> 13) & 0x1)
|
|
#define C_000040_DMA_VIPH1_INT_EN 0xFFFFDFFF
|
|
@@ -370,7 +355,90 @@
|
|
#define S_007EDC_LB_D2_VBLANK_INTERRUPT(x) (((x) & 0x1) << 5)
|
|
#define G_007EDC_LB_D2_VBLANK_INTERRUPT(x) (((x) >> 5) & 0x1)
|
|
#define C_007EDC_LB_D2_VBLANK_INTERRUPT 0xFFFFFFDF
|
|
-
|
|
+#define S_007EDC_DACA_AUTODETECT_INTERRUPT(x) (((x) & 0x1) << 16)
|
|
+#define G_007EDC_DACA_AUTODETECT_INTERRUPT(x) (((x) >> 16) & 0x1)
|
|
+#define C_007EDC_DACA_AUTODETECT_INTERRUPT 0xFFFEFFFF
|
|
+#define S_007EDC_DACB_AUTODETECT_INTERRUPT(x) (((x) & 0x1) << 17)
|
|
+#define G_007EDC_DACB_AUTODETECT_INTERRUPT(x) (((x) >> 17) & 0x1)
|
|
+#define C_007EDC_DACB_AUTODETECT_INTERRUPT 0xFFFDFFFF
|
|
+#define S_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(x) (((x) & 0x1) << 18)
|
|
+#define G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(x) (((x) >> 18) & 0x1)
|
|
+#define C_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT 0xFFFBFFFF
|
|
+#define S_007EDC_DC_HOT_PLUG_DETECT2_INTERRUPT(x) (((x) & 0x1) << 19)
|
|
+#define G_007EDC_DC_HOT_PLUG_DETECT2_INTERRUPT(x) (((x) >> 19) & 0x1)
|
|
+#define C_007EDC_DC_HOT_PLUG_DETECT2_INTERRUPT 0xFFF7FFFF
|
|
+#define R_007828_DACA_AUTODETECT_CONTROL 0x007828
|
|
+#define S_007828_DACA_AUTODETECT_MODE(x) (((x) & 0x3) << 0)
|
|
+#define G_007828_DACA_AUTODETECT_MODE(x) (((x) >> 0) & 0x3)
|
|
+#define C_007828_DACA_AUTODETECT_MODE 0xFFFFFFFC
|
|
+#define S_007828_DACA_AUTODETECT_FRAME_TIME_COUNTER(x) (((x) & 0xff) << 8)
|
|
+#define G_007828_DACA_AUTODETECT_FRAME_TIME_COUNTER(x) (((x) >> 8) & 0xff)
|
|
+#define C_007828_DACA_AUTODETECT_FRAME_TIME_COUNTER 0xFFFF00FF
|
|
+#define S_007828_DACA_AUTODETECT_CHECK_MASK(x) (((x) & 0x3) << 16)
|
|
+#define G_007828_DACA_AUTODETECT_CHECK_MASK(x) (((x) >> 16) & 0x3)
|
|
+#define C_007828_DACA_AUTODETECT_CHECK_MASK 0xFFFCFFFF
|
|
+#define R_007838_DACA_AUTODETECT_INT_CONTROL 0x007838
|
|
+#define S_007838_DACA_AUTODETECT_ACK(x) (((x) & 0x1) << 0)
|
|
+#define C_007838_DACA_DACA_AUTODETECT_ACK 0xFFFFFFFE
|
|
+#define S_007838_DACA_AUTODETECT_INT_ENABLE(x) (((x) & 0x1) << 16)
|
|
+#define G_007838_DACA_AUTODETECT_INT_ENABLE(x) (((x) >> 16) & 0x1)
|
|
+#define C_007838_DACA_AUTODETECT_INT_ENABLE 0xFFFCFFFF
|
|
+#define R_007A28_DACB_AUTODETECT_CONTROL 0x007A28
|
|
+#define S_007A28_DACB_AUTODETECT_MODE(x) (((x) & 0x3) << 0)
|
|
+#define G_007A28_DACB_AUTODETECT_MODE(x) (((x) >> 0) & 0x3)
|
|
+#define C_007A28_DACB_AUTODETECT_MODE 0xFFFFFFFC
|
|
+#define S_007A28_DACB_AUTODETECT_FRAME_TIME_COUNTER(x) (((x) & 0xff) << 8)
|
|
+#define G_007A28_DACB_AUTODETECT_FRAME_TIME_COUNTER(x) (((x) >> 8) & 0xff)
|
|
+#define C_007A28_DACB_AUTODETECT_FRAME_TIME_COUNTER 0xFFFF00FF
|
|
+#define S_007A28_DACB_AUTODETECT_CHECK_MASK(x) (((x) & 0x3) << 16)
|
|
+#define G_007A28_DACB_AUTODETECT_CHECK_MASK(x) (((x) >> 16) & 0x3)
|
|
+#define C_007A28_DACB_AUTODETECT_CHECK_MASK 0xFFFCFFFF
|
|
+#define R_007A38_DACB_AUTODETECT_INT_CONTROL 0x007A38
|
|
+#define S_007A38_DACB_AUTODETECT_ACK(x) (((x) & 0x1) << 0)
|
|
+#define C_007A38_DACB_DACA_AUTODETECT_ACK 0xFFFFFFFE
|
|
+#define S_007A38_DACB_AUTODETECT_INT_ENABLE(x) (((x) & 0x1) << 16)
|
|
+#define G_007A38_DACB_AUTODETECT_INT_ENABLE(x) (((x) >> 16) & 0x1)
|
|
+#define C_007A38_DACB_AUTODETECT_INT_ENABLE 0xFFFCFFFF
|
|
+#define R_007D00_DC_HOT_PLUG_DETECT1_CONTROL 0x007D00
|
|
+#define S_007D00_DC_HOT_PLUG_DETECT1_EN(x) (((x) & 0x1) << 0)
|
|
+#define G_007D00_DC_HOT_PLUG_DETECT1_EN(x) (((x) >> 0) & 0x1)
|
|
+#define C_007D00_DC_HOT_PLUG_DETECT1_EN 0xFFFFFFFE
|
|
+#define R_007D04_DC_HOT_PLUG_DETECT1_INT_STATUS 0x007D04
|
|
+#define S_007D04_DC_HOT_PLUG_DETECT1_INT_STATUS(x) (((x) & 0x1) << 0)
|
|
+#define G_007D04_DC_HOT_PLUG_DETECT1_INT_STATUS(x) (((x) >> 0) & 0x1)
|
|
+#define C_007D04_DC_HOT_PLUG_DETECT1_INT_STATUS 0xFFFFFFFE
|
|
+#define S_007D04_DC_HOT_PLUG_DETECT1_SENSE(x) (((x) & 0x1) << 1)
|
|
+#define G_007D04_DC_HOT_PLUG_DETECT1_SENSE(x) (((x) >> 1) & 0x1)
|
|
+#define C_007D04_DC_HOT_PLUG_DETECT1_SENSE 0xFFFFFFFD
|
|
+#define R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL 0x007D08
|
|
+#define S_007D08_DC_HOT_PLUG_DETECT1_INT_ACK(x) (((x) & 0x1) << 0)
|
|
+#define C_007D08_DC_HOT_PLUG_DETECT1_INT_ACK 0xFFFFFFFE
|
|
+#define S_007D08_DC_HOT_PLUG_DETECT1_INT_POLARITY(x) (((x) & 0x1) << 8)
|
|
+#define G_007D08_DC_HOT_PLUG_DETECT1_INT_POLARITY(x) (((x) >> 8) & 0x1)
|
|
+#define C_007D08_DC_HOT_PLUG_DETECT1_INT_POLARITY 0xFFFFFEFF
|
|
+#define S_007D08_DC_HOT_PLUG_DETECT1_INT_EN(x) (((x) & 0x1) << 16)
|
|
+#define G_007D08_DC_HOT_PLUG_DETECT1_INT_EN(x) (((x) >> 16) & 0x1)
|
|
+#define C_007D08_DC_HOT_PLUG_DETECT1_INT_EN 0xFFFEFFFF
|
|
+#define R_007D10_DC_HOT_PLUG_DETECT2_CONTROL 0x007D10
|
|
+#define S_007D10_DC_HOT_PLUG_DETECT2_EN(x) (((x) & 0x1) << 0)
|
|
+#define G_007D10_DC_HOT_PLUG_DETECT2_EN(x) (((x) >> 0) & 0x1)
|
|
+#define C_007D10_DC_HOT_PLUG_DETECT2_EN 0xFFFFFFFE
|
|
+#define R_007D14_DC_HOT_PLUG_DETECT2_INT_STATUS 0x007D14
|
|
+#define S_007D14_DC_HOT_PLUG_DETECT2_INT_STATUS(x) (((x) & 0x1) << 0)
|
|
+#define G_007D14_DC_HOT_PLUG_DETECT2_INT_STATUS(x) (((x) >> 0) & 0x1)
|
|
+#define C_007D14_DC_HOT_PLUG_DETECT2_INT_STATUS 0xFFFFFFFE
|
|
+#define S_007D14_DC_HOT_PLUG_DETECT2_SENSE(x) (((x) & 0x1) << 1)
|
|
+#define G_007D14_DC_HOT_PLUG_DETECT2_SENSE(x) (((x) >> 1) & 0x1)
|
|
+#define C_007D14_DC_HOT_PLUG_DETECT2_SENSE 0xFFFFFFFD
|
|
+#define R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL 0x007D18
|
|
+#define S_007D18_DC_HOT_PLUG_DETECT2_INT_ACK(x) (((x) & 0x1) << 0)
|
|
+#define C_007D18_DC_HOT_PLUG_DETECT2_INT_ACK 0xFFFFFFFE
|
|
+#define S_007D18_DC_HOT_PLUG_DETECT2_INT_POLARITY(x) (((x) & 0x1) << 8)
|
|
+#define G_007D18_DC_HOT_PLUG_DETECT2_INT_POLARITY(x) (((x) >> 8) & 0x1)
|
|
+#define C_007D18_DC_HOT_PLUG_DETECT2_INT_POLARITY 0xFFFFFEFF
|
|
+#define S_007D18_DC_HOT_PLUG_DETECT2_INT_EN(x) (((x) & 0x1) << 16)
|
|
+#define G_007D18_DC_HOT_PLUG_DETECT2_INT_EN(x) (((x) >> 16) & 0x1)
|
|
+#define C_007D18_DC_HOT_PLUG_DETECT2_INT_EN 0xFFFEFFFF
|
|
|
|
/* MC registers */
|
|
#define R_000000_MC_STATUS 0x000000
|
|
diff --git a/drivers/gpu/drm/radeon/rs690.c b/drivers/gpu/drm/radeon/rs690.c
|
|
index b12ff76..06e2771 100644
|
|
--- a/drivers/gpu/drm/radeon/rs690.c
|
|
+++ b/drivers/gpu/drm/radeon/rs690.c
|
|
@@ -162,6 +162,22 @@ void rs690_vram_info(struct radeon_device *rdev)
|
|
rdev->pm.core_bandwidth.full = rfixed_div(rdev->pm.sclk, a);
|
|
}
|
|
|
|
+static int rs690_mc_init(struct radeon_device *rdev)
|
|
+{
|
|
+ int r;
|
|
+ u32 tmp;
|
|
+
|
|
+ /* Setup GPU memory space */
|
|
+ tmp = RREG32_MC(R_000100_MCCFG_FB_LOCATION);
|
|
+ rdev->mc.vram_location = G_000100_MC_FB_START(tmp) << 16;
|
|
+ rdev->mc.gtt_location = 0xFFFFFFFFUL;
|
|
+ r = radeon_mc_setup(rdev);
|
|
+ rdev->mc.igp_sideport_enabled = radeon_atombios_sideport_present(rdev);
|
|
+ if (r)
|
|
+ return r;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
void rs690_line_buffer_adjust(struct radeon_device *rdev,
|
|
struct drm_display_mode *mode1,
|
|
struct drm_display_mode *mode2)
|
|
@@ -245,8 +261,9 @@ void rs690_crtc_bandwidth_compute(struct radeon_device *rdev,
|
|
|
|
b.full = rfixed_const(mode->crtc_hdisplay);
|
|
c.full = rfixed_const(256);
|
|
- a.full = rfixed_mul(wm->num_line_pair, b);
|
|
- request_fifo_depth.full = rfixed_div(a, c);
|
|
+ a.full = rfixed_div(b, c);
|
|
+ request_fifo_depth.full = rfixed_mul(a, wm->num_line_pair);
|
|
+ request_fifo_depth.full = rfixed_ceil(request_fifo_depth);
|
|
if (a.full < rfixed_const(4)) {
|
|
wm->lb_request_fifo_depth = 4;
|
|
} else {
|
|
@@ -375,6 +392,7 @@ void rs690_crtc_bandwidth_compute(struct radeon_device *rdev,
|
|
a.full = rfixed_const(16);
|
|
wm->priority_mark_max.full = rfixed_const(crtc->base.mode.crtc_hdisplay);
|
|
wm->priority_mark_max.full = rfixed_div(wm->priority_mark_max, a);
|
|
+ wm->priority_mark_max.full = rfixed_ceil(wm->priority_mark_max);
|
|
|
|
/* Determine estimated width */
|
|
estimated_width.full = tolerable_latency.full - wm->worst_case_latency.full;
|
|
@@ -384,6 +402,7 @@ void rs690_crtc_bandwidth_compute(struct radeon_device *rdev,
|
|
} else {
|
|
a.full = rfixed_const(16);
|
|
wm->priority_mark.full = rfixed_div(estimated_width, a);
|
|
+ wm->priority_mark.full = rfixed_ceil(wm->priority_mark);
|
|
wm->priority_mark.full = wm->priority_mark_max.full - wm->priority_mark.full;
|
|
}
|
|
}
|
|
@@ -606,8 +625,8 @@ static int rs690_startup(struct radeon_device *rdev)
|
|
if (r)
|
|
return r;
|
|
/* Enable IRQ */
|
|
- rdev->irq.sw_int = true;
|
|
rs600_irq_set(rdev);
|
|
+ rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL);
|
|
/* 1M ring buffer */
|
|
r = r100_cp_init(rdev, 1024 * 1024);
|
|
if (r) {
|
|
@@ -641,6 +660,8 @@ int rs690_resume(struct radeon_device *rdev)
|
|
atom_asic_init(rdev->mode_info.atom_context);
|
|
/* Resume clock after posting */
|
|
rv515_clock_startup(rdev);
|
|
+ /* Initialize surface registers */
|
|
+ radeon_surface_init(rdev);
|
|
return rs690_startup(rdev);
|
|
}
|
|
|
|
@@ -655,7 +676,6 @@ int rs690_suspend(struct radeon_device *rdev)
|
|
|
|
void rs690_fini(struct radeon_device *rdev)
|
|
{
|
|
- rs690_suspend(rdev);
|
|
r100_cp_fini(rdev);
|
|
r100_wb_fini(rdev);
|
|
r100_ib_fini(rdev);
|
|
@@ -663,7 +683,7 @@ void rs690_fini(struct radeon_device *rdev)
|
|
rs400_gart_fini(rdev);
|
|
radeon_irq_kms_fini(rdev);
|
|
radeon_fence_driver_fini(rdev);
|
|
- radeon_object_fini(rdev);
|
|
+ radeon_bo_fini(rdev);
|
|
radeon_atombios_fini(rdev);
|
|
kfree(rdev->bios);
|
|
rdev->bios = NULL;
|
|
@@ -701,10 +721,9 @@ int rs690_init(struct radeon_device *rdev)
|
|
RREG32(R_0007C0_CP_STAT));
|
|
}
|
|
/* check if cards are posted or not */
|
|
- if (!radeon_card_posted(rdev) && rdev->bios) {
|
|
- DRM_INFO("GPU not posted. posting now...\n");
|
|
- atom_asic_init(rdev->mode_info.atom_context);
|
|
- }
|
|
+ if (radeon_boot_test_post_card(rdev) == false)
|
|
+ return -EINVAL;
|
|
+
|
|
/* Initialize clocks */
|
|
radeon_get_clock_info(rdev->ddev);
|
|
/* Initialize power management */
|
|
@@ -712,7 +731,7 @@ int rs690_init(struct radeon_device *rdev)
|
|
/* Get vram informations */
|
|
rs690_vram_info(rdev);
|
|
/* Initialize memory controller (also test AGP) */
|
|
- r = r420_mc_init(rdev);
|
|
+ r = rs690_mc_init(rdev);
|
|
if (r)
|
|
return r;
|
|
rv515_debugfs(rdev);
|
|
@@ -724,7 +743,7 @@ int rs690_init(struct radeon_device *rdev)
|
|
if (r)
|
|
return r;
|
|
/* Memory manager */
|
|
- r = radeon_object_init(rdev);
|
|
+ r = radeon_bo_init(rdev);
|
|
if (r)
|
|
return r;
|
|
r = rs400_gart_init(rdev);
|
|
@@ -736,7 +755,6 @@ int rs690_init(struct radeon_device *rdev)
|
|
if (r) {
|
|
/* Somethings want wront with the accel init stop accel */
|
|
dev_err(rdev->dev, "Disabling GPU acceleration\n");
|
|
- rs690_suspend(rdev);
|
|
r100_cp_fini(rdev);
|
|
r100_wb_fini(rdev);
|
|
r100_ib_fini(rdev);
|
|
diff --git a/drivers/gpu/drm/radeon/rv515.c b/drivers/gpu/drm/radeon/rv515.c
|
|
index ba68c9f..0e1e6b8 100644
|
|
--- a/drivers/gpu/drm/radeon/rv515.c
|
|
+++ b/drivers/gpu/drm/radeon/rv515.c
|
|
@@ -478,8 +478,8 @@ static int rv515_startup(struct radeon_device *rdev)
|
|
return r;
|
|
}
|
|
/* Enable IRQ */
|
|
- rdev->irq.sw_int = true;
|
|
rs600_irq_set(rdev);
|
|
+ rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL);
|
|
/* 1M ring buffer */
|
|
r = r100_cp_init(rdev, 1024 * 1024);
|
|
if (r) {
|
|
@@ -514,6 +514,8 @@ int rv515_resume(struct radeon_device *rdev)
|
|
atom_asic_init(rdev->mode_info.atom_context);
|
|
/* Resume clock after posting */
|
|
rv515_clock_startup(rdev);
|
|
+ /* Initialize surface registers */
|
|
+ radeon_surface_init(rdev);
|
|
return rv515_startup(rdev);
|
|
}
|
|
|
|
@@ -535,16 +537,15 @@ void rv515_set_safe_registers(struct radeon_device *rdev)
|
|
|
|
void rv515_fini(struct radeon_device *rdev)
|
|
{
|
|
- rv515_suspend(rdev);
|
|
r100_cp_fini(rdev);
|
|
r100_wb_fini(rdev);
|
|
r100_ib_fini(rdev);
|
|
radeon_gem_fini(rdev);
|
|
- rv370_pcie_gart_fini(rdev);
|
|
+ rv370_pcie_gart_fini(rdev);
|
|
radeon_agp_fini(rdev);
|
|
radeon_irq_kms_fini(rdev);
|
|
radeon_fence_driver_fini(rdev);
|
|
- radeon_object_fini(rdev);
|
|
+ radeon_bo_fini(rdev);
|
|
radeon_atombios_fini(rdev);
|
|
kfree(rdev->bios);
|
|
rdev->bios = NULL;
|
|
@@ -580,10 +581,8 @@ int rv515_init(struct radeon_device *rdev)
|
|
RREG32(R_0007C0_CP_STAT));
|
|
}
|
|
/* check if cards are posted or not */
|
|
- if (!radeon_card_posted(rdev) && rdev->bios) {
|
|
- DRM_INFO("GPU not posted. posting now...\n");
|
|
- atom_asic_init(rdev->mode_info.atom_context);
|
|
- }
|
|
+ if (radeon_boot_test_post_card(rdev) == false)
|
|
+ return -EINVAL;
|
|
/* Initialize clocks */
|
|
radeon_get_clock_info(rdev->ddev);
|
|
/* Initialize power management */
|
|
@@ -603,7 +602,7 @@ int rv515_init(struct radeon_device *rdev)
|
|
if (r)
|
|
return r;
|
|
/* Memory manager */
|
|
- r = radeon_object_init(rdev);
|
|
+ r = radeon_bo_init(rdev);
|
|
if (r)
|
|
return r;
|
|
r = rv370_pcie_gart_init(rdev);
|
|
@@ -615,13 +614,12 @@ int rv515_init(struct radeon_device *rdev)
|
|
if (r) {
|
|
/* Somethings want wront with the accel init stop accel */
|
|
dev_err(rdev->dev, "Disabling GPU acceleration\n");
|
|
- rv515_suspend(rdev);
|
|
r100_cp_fini(rdev);
|
|
r100_wb_fini(rdev);
|
|
r100_ib_fini(rdev);
|
|
+ radeon_irq_kms_fini(rdev);
|
|
rv370_pcie_gart_fini(rdev);
|
|
radeon_agp_fini(rdev);
|
|
- radeon_irq_kms_fini(rdev);
|
|
rdev->accel_working = false;
|
|
}
|
|
return 0;
|
|
@@ -892,8 +890,9 @@ void rv515_crtc_bandwidth_compute(struct radeon_device *rdev,
|
|
|
|
b.full = rfixed_const(mode->crtc_hdisplay);
|
|
c.full = rfixed_const(256);
|
|
- a.full = rfixed_mul(wm->num_line_pair, b);
|
|
- request_fifo_depth.full = rfixed_div(a, c);
|
|
+ a.full = rfixed_div(b, c);
|
|
+ request_fifo_depth.full = rfixed_mul(a, wm->num_line_pair);
|
|
+ request_fifo_depth.full = rfixed_ceil(request_fifo_depth);
|
|
if (a.full < rfixed_const(4)) {
|
|
wm->lb_request_fifo_depth = 4;
|
|
} else {
|
|
@@ -995,15 +994,17 @@ void rv515_crtc_bandwidth_compute(struct radeon_device *rdev,
|
|
a.full = rfixed_const(16);
|
|
wm->priority_mark_max.full = rfixed_const(crtc->base.mode.crtc_hdisplay);
|
|
wm->priority_mark_max.full = rfixed_div(wm->priority_mark_max, a);
|
|
+ wm->priority_mark_max.full = rfixed_ceil(wm->priority_mark_max);
|
|
|
|
/* Determine estimated width */
|
|
estimated_width.full = tolerable_latency.full - wm->worst_case_latency.full;
|
|
estimated_width.full = rfixed_div(estimated_width, consumption_time);
|
|
if (rfixed_trunc(estimated_width) > crtc->base.mode.crtc_hdisplay) {
|
|
- wm->priority_mark.full = rfixed_const(10);
|
|
+ wm->priority_mark.full = wm->priority_mark_max.full;
|
|
} else {
|
|
a.full = rfixed_const(16);
|
|
wm->priority_mark.full = rfixed_div(estimated_width, a);
|
|
+ wm->priority_mark.full = rfixed_ceil(wm->priority_mark);
|
|
wm->priority_mark.full = wm->priority_mark_max.full - wm->priority_mark.full;
|
|
}
|
|
}
|
|
diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c
|
|
index b0efd0d..5943d56 100644
|
|
--- a/drivers/gpu/drm/radeon/rv770.c
|
|
+++ b/drivers/gpu/drm/radeon/rv770.c
|
|
@@ -92,7 +92,7 @@ int rv770_pcie_gart_enable(struct radeon_device *rdev)
|
|
void rv770_pcie_gart_disable(struct radeon_device *rdev)
|
|
{
|
|
u32 tmp;
|
|
- int i;
|
|
+ int i, r;
|
|
|
|
/* Disable all tables */
|
|
for (i = 0; i < 7; i++)
|
|
@@ -113,8 +113,12 @@ void rv770_pcie_gart_disable(struct radeon_device *rdev)
|
|
WREG32(MC_VM_MB_L1_TLB2_CNTL, tmp);
|
|
WREG32(MC_VM_MB_L1_TLB3_CNTL, tmp);
|
|
if (rdev->gart.table.vram.robj) {
|
|
- radeon_object_kunmap(rdev->gart.table.vram.robj);
|
|
- radeon_object_unpin(rdev->gart.table.vram.robj);
|
|
+ r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
|
|
+ if (likely(r == 0)) {
|
|
+ radeon_bo_kunmap(rdev->gart.table.vram.robj);
|
|
+ radeon_bo_unpin(rdev->gart.table.vram.robj);
|
|
+ radeon_bo_unreserve(rdev->gart.table.vram.robj);
|
|
+ }
|
|
}
|
|
}
|
|
|
|
@@ -775,7 +779,6 @@ int rv770_mc_init(struct radeon_device *rdev)
|
|
fixed20_12 a;
|
|
u32 tmp;
|
|
int chansize, numchan;
|
|
- int r;
|
|
|
|
/* Get VRAM informations */
|
|
rdev->mc.vram_is_ddr = true;
|
|
@@ -818,9 +821,6 @@ int rv770_mc_init(struct radeon_device *rdev)
|
|
rdev->mc.real_vram_size = rdev->mc.aper_size;
|
|
|
|
if (rdev->flags & RADEON_IS_AGP) {
|
|
- r = radeon_agp_init(rdev);
|
|
- if (r)
|
|
- return r;
|
|
/* gtt_size is setup by radeon_agp_init */
|
|
rdev->mc.gtt_location = rdev->mc.agp_base;
|
|
tmp = 0xFFFFFFFFUL - rdev->mc.agp_base - rdev->mc.gtt_size;
|
|
@@ -829,11 +829,11 @@ int rv770_mc_init(struct radeon_device *rdev)
|
|
* AGP so that GPU can catch out of VRAM/AGP access
|
|
*/
|
|
if (rdev->mc.gtt_location > rdev->mc.mc_vram_size) {
|
|
- /* Enought place before */
|
|
+ /* Enough place before */
|
|
rdev->mc.vram_location = rdev->mc.gtt_location -
|
|
rdev->mc.mc_vram_size;
|
|
} else if (tmp > rdev->mc.mc_vram_size) {
|
|
- /* Enought place after */
|
|
+ /* Enough place after */
|
|
rdev->mc.vram_location = rdev->mc.gtt_location +
|
|
rdev->mc.gtt_size;
|
|
} else {
|
|
@@ -870,6 +870,14 @@ static int rv770_startup(struct radeon_device *rdev)
|
|
{
|
|
int r;
|
|
|
|
+ if (!rdev->me_fw || !rdev->pfp_fw || !rdev->rlc_fw) {
|
|
+ r = r600_init_microcode(rdev);
|
|
+ if (r) {
|
|
+ DRM_ERROR("Failed to load firmware!\n");
|
|
+ return r;
|
|
+ }
|
|
+ }
|
|
+
|
|
rv770_mc_program(rdev);
|
|
if (rdev->flags & RADEON_IS_AGP) {
|
|
rv770_agp_enable(rdev);
|
|
@@ -879,13 +887,33 @@ static int rv770_startup(struct radeon_device *rdev)
|
|
return r;
|
|
}
|
|
rv770_gpu_init(rdev);
|
|
-
|
|
- r = radeon_object_pin(rdev->r600_blit.shader_obj, RADEON_GEM_DOMAIN_VRAM,
|
|
- &rdev->r600_blit.shader_gpu_addr);
|
|
+ r = r600_blit_init(rdev);
|
|
+ if (r) {
|
|
+ r600_blit_fini(rdev);
|
|
+ rdev->asic->copy = NULL;
|
|
+ dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r);
|
|
+ }
|
|
+ /* pin copy shader into vram */
|
|
+ if (rdev->r600_blit.shader_obj) {
|
|
+ r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
|
|
+ if (unlikely(r != 0))
|
|
+ return r;
|
|
+ r = radeon_bo_pin(rdev->r600_blit.shader_obj, RADEON_GEM_DOMAIN_VRAM,
|
|
+ &rdev->r600_blit.shader_gpu_addr);
|
|
+ radeon_bo_unreserve(rdev->r600_blit.shader_obj);
|
|
+ if (r) {
|
|
+ DRM_ERROR("failed to pin blit object %d\n", r);
|
|
+ return r;
|
|
+ }
|
|
+ }
|
|
+ /* Enable IRQ */
|
|
+ r = r600_irq_init(rdev);
|
|
if (r) {
|
|
- DRM_ERROR("failed to pin blit object %d\n", r);
|
|
+ DRM_ERROR("radeon: IH init failed (%d).\n", r);
|
|
+ radeon_irq_kms_fini(rdev);
|
|
return r;
|
|
}
|
|
+ r600_irq_set(rdev);
|
|
|
|
r = radeon_ring_init(rdev, rdev->cp.ring_size);
|
|
if (r)
|
|
@@ -934,13 +962,22 @@ int rv770_resume(struct radeon_device *rdev)
|
|
|
|
int rv770_suspend(struct radeon_device *rdev)
|
|
{
|
|
+ int r;
|
|
+
|
|
/* FIXME: we should wait for ring to be empty */
|
|
r700_cp_stop(rdev);
|
|
rdev->cp.ready = false;
|
|
+ r600_irq_suspend(rdev);
|
|
r600_wb_disable(rdev);
|
|
rv770_pcie_gart_disable(rdev);
|
|
/* unpin shaders bo */
|
|
- radeon_object_unpin(rdev->r600_blit.shader_obj);
|
|
+ if (rdev->r600_blit.shader_obj) {
|
|
+ r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
|
|
+ if (likely(r == 0)) {
|
|
+ radeon_bo_unpin(rdev->r600_blit.shader_obj);
|
|
+ radeon_bo_unreserve(rdev->r600_blit.shader_obj);
|
|
+ }
|
|
+ }
|
|
return 0;
|
|
}
|
|
|
|
@@ -975,7 +1012,11 @@ int rv770_init(struct radeon_device *rdev)
|
|
if (r)
|
|
return r;
|
|
/* Post card if necessary */
|
|
- if (!r600_card_posted(rdev) && rdev->bios) {
|
|
+ if (!r600_card_posted(rdev)) {
|
|
+ if (!rdev->bios) {
|
|
+ dev_err(rdev->dev, "Card not posted and no BIOS - ignoring\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
DRM_INFO("GPU not posted. posting now...\n");
|
|
atom_asic_init(rdev->mode_info.atom_context);
|
|
}
|
|
@@ -994,53 +1035,55 @@ int rv770_init(struct radeon_device *rdev)
|
|
r = radeon_fence_driver_init(rdev);
|
|
if (r)
|
|
return r;
|
|
+ if (rdev->flags & RADEON_IS_AGP) {
|
|
+ r = radeon_agp_init(rdev);
|
|
+ if (r)
|
|
+ radeon_agp_disable(rdev);
|
|
+ }
|
|
r = rv770_mc_init(rdev);
|
|
if (r)
|
|
return r;
|
|
/* Memory manager */
|
|
- r = radeon_object_init(rdev);
|
|
+ r = radeon_bo_init(rdev);
|
|
if (r)
|
|
return r;
|
|
+
|
|
+ r = radeon_irq_kms_init(rdev);
|
|
+ if (r)
|
|
+ return r;
|
|
+
|
|
rdev->cp.ring_obj = NULL;
|
|
r600_ring_init(rdev, 1024 * 1024);
|
|
|
|
- if (!rdev->me_fw || !rdev->pfp_fw) {
|
|
- r = r600_cp_init_microcode(rdev);
|
|
- if (r) {
|
|
- DRM_ERROR("Failed to load firmware!\n");
|
|
- return r;
|
|
- }
|
|
- }
|
|
+ rdev->ih.ring_obj = NULL;
|
|
+ r600_ih_ring_init(rdev, 64 * 1024);
|
|
|
|
r = r600_pcie_gart_init(rdev);
|
|
if (r)
|
|
return r;
|
|
|
|
rdev->accel_working = true;
|
|
- r = r600_blit_init(rdev);
|
|
- if (r) {
|
|
- DRM_ERROR("radeon: failled blitter (%d).\n", r);
|
|
- rdev->accel_working = false;
|
|
- }
|
|
-
|
|
r = rv770_startup(rdev);
|
|
if (r) {
|
|
- rv770_suspend(rdev);
|
|
+ dev_err(rdev->dev, "disabling GPU acceleration\n");
|
|
+ r600_cp_fini(rdev);
|
|
r600_wb_fini(rdev);
|
|
- radeon_ring_fini(rdev);
|
|
+ r600_irq_fini(rdev);
|
|
+ radeon_irq_kms_fini(rdev);
|
|
rv770_pcie_gart_fini(rdev);
|
|
rdev->accel_working = false;
|
|
}
|
|
if (rdev->accel_working) {
|
|
r = radeon_ib_pool_init(rdev);
|
|
if (r) {
|
|
- DRM_ERROR("radeon: failled initializing IB pool (%d).\n", r);
|
|
- rdev->accel_working = false;
|
|
- }
|
|
- r = r600_ib_test(rdev);
|
|
- if (r) {
|
|
- DRM_ERROR("radeon: failled testing IB (%d).\n", r);
|
|
+ dev_err(rdev->dev, "IB initialization failed (%d).\n", r);
|
|
rdev->accel_working = false;
|
|
+ } else {
|
|
+ r = r600_ib_test(rdev);
|
|
+ if (r) {
|
|
+ dev_err(rdev->dev, "IB test failed (%d).\n", r);
|
|
+ rdev->accel_working = false;
|
|
+ }
|
|
}
|
|
}
|
|
return 0;
|
|
@@ -1048,18 +1091,17 @@ int rv770_init(struct radeon_device *rdev)
|
|
|
|
void rv770_fini(struct radeon_device *rdev)
|
|
{
|
|
- rv770_suspend(rdev);
|
|
-
|
|
r600_blit_fini(rdev);
|
|
- radeon_ring_fini(rdev);
|
|
+ r600_cp_fini(rdev);
|
|
r600_wb_fini(rdev);
|
|
+ r600_irq_fini(rdev);
|
|
+ radeon_irq_kms_fini(rdev);
|
|
rv770_pcie_gart_fini(rdev);
|
|
radeon_gem_fini(rdev);
|
|
radeon_fence_driver_fini(rdev);
|
|
radeon_clocks_fini(rdev);
|
|
- if (rdev->flags & RADEON_IS_AGP)
|
|
- radeon_agp_fini(rdev);
|
|
- radeon_object_fini(rdev);
|
|
+ radeon_agp_fini(rdev);
|
|
+ radeon_bo_fini(rdev);
|
|
radeon_atombios_fini(rdev);
|
|
kfree(rdev->bios);
|
|
rdev->bios = NULL;
|
|
diff --git a/drivers/gpu/drm/savage/savage_drv.c b/drivers/gpu/drm/savage/savage_drv.c
|
|
index eee52aa..021de44 100644
|
|
--- a/drivers/gpu/drm/savage/savage_drv.c
|
|
+++ b/drivers/gpu/drm/savage/savage_drv.c
|
|
@@ -50,7 +50,7 @@ static struct drm_driver driver = {
|
|
.owner = THIS_MODULE,
|
|
.open = drm_open,
|
|
.release = drm_release,
|
|
- .ioctl = drm_ioctl,
|
|
+ .unlocked_ioctl = drm_ioctl,
|
|
.mmap = drm_mmap,
|
|
.poll = drm_poll,
|
|
.fasync = drm_fasync,
|
|
diff --git a/drivers/gpu/drm/sis/sis_drv.c b/drivers/gpu/drm/sis/sis_drv.c
|
|
index e725cc0..4fd1f06 100644
|
|
--- a/drivers/gpu/drm/sis/sis_drv.c
|
|
+++ b/drivers/gpu/drm/sis/sis_drv.c
|
|
@@ -80,7 +80,7 @@ static struct drm_driver driver = {
|
|
.owner = THIS_MODULE,
|
|
.open = drm_open,
|
|
.release = drm_release,
|
|
- .ioctl = drm_ioctl,
|
|
+ .unlocked_ioctl = drm_ioctl,
|
|
.mmap = drm_mmap,
|
|
.poll = drm_poll,
|
|
.fasync = drm_fasync,
|
|
diff --git a/drivers/gpu/drm/tdfx/tdfx_drv.c b/drivers/gpu/drm/tdfx/tdfx_drv.c
|
|
index 012ff2e..ec5a43e 100644
|
|
--- a/drivers/gpu/drm/tdfx/tdfx_drv.c
|
|
+++ b/drivers/gpu/drm/tdfx/tdfx_drv.c
|
|
@@ -48,7 +48,7 @@ static struct drm_driver driver = {
|
|
.owner = THIS_MODULE,
|
|
.open = drm_open,
|
|
.release = drm_release,
|
|
- .ioctl = drm_ioctl,
|
|
+ .unlocked_ioctl = drm_ioctl,
|
|
.mmap = drm_mmap,
|
|
.poll = drm_poll,
|
|
.fasync = drm_fasync,
|
|
diff --git a/drivers/gpu/drm/ttm/Makefile b/drivers/gpu/drm/ttm/Makefile
|
|
index b0a9de7..1e138f5 100644
|
|
--- a/drivers/gpu/drm/ttm/Makefile
|
|
+++ b/drivers/gpu/drm/ttm/Makefile
|
|
@@ -3,6 +3,7 @@
|
|
|
|
ccflags-y := -Iinclude/drm
|
|
ttm-y := ttm_agp_backend.o ttm_memory.o ttm_tt.o ttm_bo.o \
|
|
- ttm_bo_util.o ttm_bo_vm.o ttm_module.o ttm_global.o
|
|
+ ttm_bo_util.o ttm_bo_vm.o ttm_module.o ttm_global.o \
|
|
+ ttm_object.o ttm_lock.o ttm_execbuf_util.o
|
|
|
|
obj-$(CONFIG_DRM_TTM) += ttm.o
|
|
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
|
|
index 87c0625..c7320ce 100644
|
|
--- a/drivers/gpu/drm/ttm/ttm_bo.c
|
|
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
|
|
@@ -27,6 +27,14 @@
|
|
/*
|
|
* Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
|
|
*/
|
|
+/* Notes:
|
|
+ *
|
|
+ * We store bo pointer in drm_mm_node struct so we know which bo own a
|
|
+ * specific node. There is no protection on the pointer, thus to make
|
|
+ * sure things don't go berserk you have to access this pointer while
|
|
+ * holding the global lru lock and make sure anytime you free a node you
|
|
+ * reset the pointer to NULL.
|
|
+ */
|
|
|
|
#include "ttm/ttm_module.h"
|
|
#include "ttm/ttm_bo_driver.h"
|
|
@@ -51,6 +59,59 @@ static struct attribute ttm_bo_count = {
|
|
.mode = S_IRUGO
|
|
};
|
|
|
|
+static inline int ttm_mem_type_from_flags(uint32_t flags, uint32_t *mem_type)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i <= TTM_PL_PRIV5; i++)
|
|
+ if (flags & (1 << i)) {
|
|
+ *mem_type = i;
|
|
+ return 0;
|
|
+ }
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static void ttm_mem_type_debug(struct ttm_bo_device *bdev, int mem_type)
|
|
+{
|
|
+ struct ttm_mem_type_manager *man = &bdev->man[mem_type];
|
|
+
|
|
+ printk(KERN_ERR TTM_PFX " has_type: %d\n", man->has_type);
|
|
+ printk(KERN_ERR TTM_PFX " use_type: %d\n", man->use_type);
|
|
+ printk(KERN_ERR TTM_PFX " flags: 0x%08X\n", man->flags);
|
|
+ printk(KERN_ERR TTM_PFX " gpu_offset: 0x%08lX\n", man->gpu_offset);
|
|
+ printk(KERN_ERR TTM_PFX " io_offset: 0x%08lX\n", man->io_offset);
|
|
+ printk(KERN_ERR TTM_PFX " io_size: %ld\n", man->io_size);
|
|
+ printk(KERN_ERR TTM_PFX " size: %llu\n", man->size);
|
|
+ printk(KERN_ERR TTM_PFX " available_caching: 0x%08X\n",
|
|
+ man->available_caching);
|
|
+ printk(KERN_ERR TTM_PFX " default_caching: 0x%08X\n",
|
|
+ man->default_caching);
|
|
+ if (mem_type != TTM_PL_SYSTEM) {
|
|
+ spin_lock(&bdev->glob->lru_lock);
|
|
+ drm_mm_debug_table(&man->manager, TTM_PFX);
|
|
+ spin_unlock(&bdev->glob->lru_lock);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void ttm_bo_mem_space_debug(struct ttm_buffer_object *bo,
|
|
+ struct ttm_placement *placement)
|
|
+{
|
|
+ int i, ret, mem_type;
|
|
+
|
|
+ printk(KERN_ERR TTM_PFX "No space for %p (%lu pages, %luK, %luM)\n",
|
|
+ bo, bo->mem.num_pages, bo->mem.size >> 10,
|
|
+ bo->mem.size >> 20);
|
|
+ for (i = 0; i < placement->num_placement; i++) {
|
|
+ ret = ttm_mem_type_from_flags(placement->placement[i],
|
|
+ &mem_type);
|
|
+ if (ret)
|
|
+ return;
|
|
+ printk(KERN_ERR TTM_PFX " placement[%d]=0x%08X (%d)\n",
|
|
+ i, placement->placement[i], mem_type);
|
|
+ ttm_mem_type_debug(bo->bdev, mem_type);
|
|
+ }
|
|
+}
|
|
+
|
|
static ssize_t ttm_bo_global_show(struct kobject *kobj,
|
|
struct attribute *attr,
|
|
char *buffer)
|
|
@@ -117,12 +178,13 @@ int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo, bool interruptible)
|
|
ret = wait_event_interruptible(bo->event_queue,
|
|
atomic_read(&bo->reserved) == 0);
|
|
if (unlikely(ret != 0))
|
|
- return -ERESTART;
|
|
+ return ret;
|
|
} else {
|
|
wait_event(bo->event_queue, atomic_read(&bo->reserved) == 0);
|
|
}
|
|
return 0;
|
|
}
|
|
+EXPORT_SYMBOL(ttm_bo_wait_unreserved);
|
|
|
|
static void ttm_bo_add_to_lru(struct ttm_buffer_object *bo)
|
|
{
|
|
@@ -247,7 +309,6 @@ EXPORT_SYMBOL(ttm_bo_unreserve);
|
|
/*
|
|
* Call bo->mutex locked.
|
|
*/
|
|
-
|
|
static int ttm_bo_add_ttm(struct ttm_buffer_object *bo, bool zero_alloc)
|
|
{
|
|
struct ttm_bo_device *bdev = bo->bdev;
|
|
@@ -275,9 +336,10 @@ static int ttm_bo_add_ttm(struct ttm_buffer_object *bo, bool zero_alloc)
|
|
bo->ttm = ttm_tt_create(bdev, bo->num_pages << PAGE_SHIFT,
|
|
page_flags | TTM_PAGE_FLAG_USER,
|
|
glob->dummy_read_page);
|
|
- if (unlikely(bo->ttm == NULL))
|
|
+ if (unlikely(bo->ttm == NULL)) {
|
|
ret = -ENOMEM;
|
|
- break;
|
|
+ break;
|
|
+ }
|
|
|
|
ret = ttm_tt_set_user(bo->ttm, current,
|
|
bo->buffer_start, bo->num_pages);
|
|
@@ -328,14 +390,8 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo,
|
|
}
|
|
|
|
if (bo->mem.mem_type == TTM_PL_SYSTEM) {
|
|
-
|
|
- struct ttm_mem_reg *old_mem = &bo->mem;
|
|
- uint32_t save_flags = old_mem->placement;
|
|
-
|
|
- *old_mem = *mem;
|
|
+ bo->mem = *mem;
|
|
mem->mm_node = NULL;
|
|
- ttm_flag_masked(&save_flags, mem->placement,
|
|
- TTM_PL_MASK_MEMTYPE);
|
|
goto moved;
|
|
}
|
|
|
|
@@ -370,7 +426,8 @@ moved:
|
|
bdev->man[bo->mem.mem_type].gpu_offset;
|
|
bo->cur_placement = bo->mem.placement;
|
|
spin_unlock(&bo->lock);
|
|
- }
|
|
+ } else
|
|
+ bo->offset = 0;
|
|
|
|
return 0;
|
|
|
|
@@ -408,6 +465,8 @@ static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo, bool remove_all)
|
|
spin_unlock(&bo->lock);
|
|
|
|
spin_lock(&glob->lru_lock);
|
|
+ put_count = ttm_bo_del_from_lru(bo);
|
|
+
|
|
ret = ttm_bo_reserve_locked(bo, false, false, false, 0);
|
|
BUG_ON(ret);
|
|
if (bo->ttm)
|
|
@@ -415,19 +474,19 @@ static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo, bool remove_all)
|
|
|
|
if (!list_empty(&bo->ddestroy)) {
|
|
list_del_init(&bo->ddestroy);
|
|
- kref_put(&bo->list_kref, ttm_bo_ref_bug);
|
|
+ ++put_count;
|
|
}
|
|
if (bo->mem.mm_node) {
|
|
+ bo->mem.mm_node->private = NULL;
|
|
drm_mm_put_block(bo->mem.mm_node);
|
|
bo->mem.mm_node = NULL;
|
|
}
|
|
- put_count = ttm_bo_del_from_lru(bo);
|
|
spin_unlock(&glob->lru_lock);
|
|
|
|
atomic_set(&bo->reserved, 0);
|
|
|
|
while (put_count--)
|
|
- kref_put(&bo->list_kref, ttm_bo_release_list);
|
|
+ kref_put(&bo->list_kref, ttm_bo_ref_bug);
|
|
|
|
return 0;
|
|
}
|
|
@@ -465,52 +524,44 @@ static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo, bool remove_all)
|
|
static int ttm_bo_delayed_delete(struct ttm_bo_device *bdev, bool remove_all)
|
|
{
|
|
struct ttm_bo_global *glob = bdev->glob;
|
|
- struct ttm_buffer_object *entry, *nentry;
|
|
- struct list_head *list, *next;
|
|
- int ret;
|
|
+ struct ttm_buffer_object *entry = NULL;
|
|
+ int ret = 0;
|
|
|
|
spin_lock(&glob->lru_lock);
|
|
- list_for_each_safe(list, next, &bdev->ddestroy) {
|
|
- entry = list_entry(list, struct ttm_buffer_object, ddestroy);
|
|
- nentry = NULL;
|
|
+ if (list_empty(&bdev->ddestroy))
|
|
+ goto out_unlock;
|
|
|
|
- /*
|
|
- * Protect the next list entry from destruction while we
|
|
- * unlock the lru_lock.
|
|
- */
|
|
+ entry = list_first_entry(&bdev->ddestroy,
|
|
+ struct ttm_buffer_object, ddestroy);
|
|
+ kref_get(&entry->list_kref);
|
|
|
|
- if (next != &bdev->ddestroy) {
|
|
- nentry = list_entry(next, struct ttm_buffer_object,
|
|
- ddestroy);
|
|
+ for (;;) {
|
|
+ struct ttm_buffer_object *nentry = NULL;
|
|
+
|
|
+ if (entry->ddestroy.next != &bdev->ddestroy) {
|
|
+ nentry = list_first_entry(&entry->ddestroy,
|
|
+ struct ttm_buffer_object, ddestroy);
|
|
kref_get(&nentry->list_kref);
|
|
}
|
|
- kref_get(&entry->list_kref);
|
|
|
|
spin_unlock(&glob->lru_lock);
|
|
ret = ttm_bo_cleanup_refs(entry, remove_all);
|
|
kref_put(&entry->list_kref, ttm_bo_release_list);
|
|
+ entry = nentry;
|
|
+
|
|
+ if (ret || !entry)
|
|
+ goto out;
|
|
|
|
spin_lock(&glob->lru_lock);
|
|
- if (nentry) {
|
|
- bool next_onlist = !list_empty(next);
|
|
- spin_unlock(&glob->lru_lock);
|
|
- kref_put(&nentry->list_kref, ttm_bo_release_list);
|
|
- spin_lock(&glob->lru_lock);
|
|
- /*
|
|
- * Someone might have raced us and removed the
|
|
- * next entry from the list. We don't bother restarting
|
|
- * list traversal.
|
|
- */
|
|
-
|
|
- if (!next_onlist)
|
|
- break;
|
|
- }
|
|
- if (ret)
|
|
+ if (list_empty(&entry->ddestroy))
|
|
break;
|
|
}
|
|
- ret = !list_empty(&bdev->ddestroy);
|
|
- spin_unlock(&glob->lru_lock);
|
|
|
|
+out_unlock:
|
|
+ spin_unlock(&glob->lru_lock);
|
|
+out:
|
|
+ if (entry)
|
|
+ kref_put(&entry->list_kref, ttm_bo_release_list);
|
|
return ret;
|
|
}
|
|
|
|
@@ -554,24 +605,21 @@ void ttm_bo_unref(struct ttm_buffer_object **p_bo)
|
|
}
|
|
EXPORT_SYMBOL(ttm_bo_unref);
|
|
|
|
-static int ttm_bo_evict(struct ttm_buffer_object *bo, unsigned mem_type,
|
|
- bool interruptible, bool no_wait)
|
|
+static int ttm_bo_evict(struct ttm_buffer_object *bo, bool interruptible,
|
|
+ bool no_wait)
|
|
{
|
|
- int ret = 0;
|
|
struct ttm_bo_device *bdev = bo->bdev;
|
|
struct ttm_bo_global *glob = bo->glob;
|
|
struct ttm_mem_reg evict_mem;
|
|
- uint32_t proposed_placement;
|
|
-
|
|
- if (bo->mem.mem_type != mem_type)
|
|
- goto out;
|
|
+ struct ttm_placement placement;
|
|
+ int ret = 0;
|
|
|
|
spin_lock(&bo->lock);
|
|
ret = ttm_bo_wait(bo, false, interruptible, no_wait);
|
|
spin_unlock(&bo->lock);
|
|
|
|
if (unlikely(ret != 0)) {
|
|
- if (ret != -ERESTART) {
|
|
+ if (ret != -ERESTARTSYS) {
|
|
printk(KERN_ERR TTM_PFX
|
|
"Failed to expire sync object before "
|
|
"buffer eviction.\n");
|
|
@@ -584,116 +632,165 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, unsigned mem_type,
|
|
evict_mem = bo->mem;
|
|
evict_mem.mm_node = NULL;
|
|
|
|
- proposed_placement = bdev->driver->evict_flags(bo);
|
|
-
|
|
- ret = ttm_bo_mem_space(bo, proposed_placement,
|
|
- &evict_mem, interruptible, no_wait);
|
|
- if (unlikely(ret != 0 && ret != -ERESTART))
|
|
- ret = ttm_bo_mem_space(bo, TTM_PL_FLAG_SYSTEM,
|
|
- &evict_mem, interruptible, no_wait);
|
|
-
|
|
+ placement.fpfn = 0;
|
|
+ placement.lpfn = 0;
|
|
+ placement.num_placement = 0;
|
|
+ placement.num_busy_placement = 0;
|
|
+ bdev->driver->evict_flags(bo, &placement);
|
|
+ ret = ttm_bo_mem_space(bo, &placement, &evict_mem, interruptible,
|
|
+ no_wait);
|
|
if (ret) {
|
|
- if (ret != -ERESTART)
|
|
+ if (ret != -ERESTARTSYS) {
|
|
printk(KERN_ERR TTM_PFX
|
|
"Failed to find memory space for "
|
|
"buffer 0x%p eviction.\n", bo);
|
|
+ ttm_bo_mem_space_debug(bo, &placement);
|
|
+ }
|
|
goto out;
|
|
}
|
|
|
|
ret = ttm_bo_handle_move_mem(bo, &evict_mem, true, interruptible,
|
|
no_wait);
|
|
if (ret) {
|
|
- if (ret != -ERESTART)
|
|
+ if (ret != -ERESTARTSYS)
|
|
printk(KERN_ERR TTM_PFX "Buffer eviction failed\n");
|
|
+ spin_lock(&glob->lru_lock);
|
|
+ if (evict_mem.mm_node) {
|
|
+ evict_mem.mm_node->private = NULL;
|
|
+ drm_mm_put_block(evict_mem.mm_node);
|
|
+ evict_mem.mm_node = NULL;
|
|
+ }
|
|
+ spin_unlock(&glob->lru_lock);
|
|
goto out;
|
|
}
|
|
-
|
|
- spin_lock(&glob->lru_lock);
|
|
- if (evict_mem.mm_node) {
|
|
- drm_mm_put_block(evict_mem.mm_node);
|
|
- evict_mem.mm_node = NULL;
|
|
- }
|
|
- spin_unlock(&glob->lru_lock);
|
|
bo->evicted = true;
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
-/**
|
|
- * Repeatedly evict memory from the LRU for @mem_type until we create enough
|
|
- * space, or we've evicted everything and there isn't enough space.
|
|
- */
|
|
-static int ttm_bo_mem_force_space(struct ttm_bo_device *bdev,
|
|
- struct ttm_mem_reg *mem,
|
|
- uint32_t mem_type,
|
|
- bool interruptible, bool no_wait)
|
|
+static int ttm_mem_evict_first(struct ttm_bo_device *bdev,
|
|
+ uint32_t mem_type,
|
|
+ bool interruptible, bool no_wait)
|
|
{
|
|
struct ttm_bo_global *glob = bdev->glob;
|
|
- struct drm_mm_node *node;
|
|
- struct ttm_buffer_object *entry;
|
|
struct ttm_mem_type_manager *man = &bdev->man[mem_type];
|
|
- struct list_head *lru;
|
|
- unsigned long num_pages = mem->num_pages;
|
|
- int put_count = 0;
|
|
- int ret;
|
|
-
|
|
-retry_pre_get:
|
|
- ret = drm_mm_pre_get(&man->manager);
|
|
- if (unlikely(ret != 0))
|
|
- return ret;
|
|
+ struct ttm_buffer_object *bo;
|
|
+ int ret, put_count = 0;
|
|
|
|
+retry:
|
|
spin_lock(&glob->lru_lock);
|
|
- do {
|
|
- node = drm_mm_search_free(&man->manager, num_pages,
|
|
- mem->page_alignment, 1);
|
|
- if (node)
|
|
- break;
|
|
+ if (list_empty(&man->lru)) {
|
|
+ spin_unlock(&glob->lru_lock);
|
|
+ return -EBUSY;
|
|
+ }
|
|
|
|
- lru = &man->lru;
|
|
- if (list_empty(lru))
|
|
- break;
|
|
+ bo = list_first_entry(&man->lru, struct ttm_buffer_object, lru);
|
|
+ kref_get(&bo->list_kref);
|
|
|
|
- entry = list_first_entry(lru, struct ttm_buffer_object, lru);
|
|
- kref_get(&entry->list_kref);
|
|
+ ret = ttm_bo_reserve_locked(bo, false, true, false, 0);
|
|
|
|
- ret =
|
|
- ttm_bo_reserve_locked(entry, interruptible, no_wait,
|
|
- false, 0);
|
|
+ if (unlikely(ret == -EBUSY)) {
|
|
+ spin_unlock(&glob->lru_lock);
|
|
+ if (likely(!no_wait))
|
|
+ ret = ttm_bo_wait_unreserved(bo, interruptible);
|
|
|
|
- if (likely(ret == 0))
|
|
- put_count = ttm_bo_del_from_lru(entry);
|
|
+ kref_put(&bo->list_kref, ttm_bo_release_list);
|
|
|
|
- spin_unlock(&glob->lru_lock);
|
|
+ /**
|
|
+ * We *need* to retry after releasing the lru lock.
|
|
+ */
|
|
|
|
if (unlikely(ret != 0))
|
|
return ret;
|
|
+ goto retry;
|
|
+ }
|
|
|
|
- while (put_count--)
|
|
- kref_put(&entry->list_kref, ttm_bo_ref_bug);
|
|
+ put_count = ttm_bo_del_from_lru(bo);
|
|
+ spin_unlock(&glob->lru_lock);
|
|
+
|
|
+ BUG_ON(ret != 0);
|
|
+
|
|
+ while (put_count--)
|
|
+ kref_put(&bo->list_kref, ttm_bo_ref_bug);
|
|
|
|
- ret = ttm_bo_evict(entry, mem_type, interruptible, no_wait);
|
|
+ ret = ttm_bo_evict(bo, interruptible, no_wait);
|
|
+ ttm_bo_unreserve(bo);
|
|
|
|
- ttm_bo_unreserve(entry);
|
|
+ kref_put(&bo->list_kref, ttm_bo_release_list);
|
|
+ return ret;
|
|
+}
|
|
|
|
- kref_put(&entry->list_kref, ttm_bo_release_list);
|
|
- if (ret)
|
|
+static int ttm_bo_man_get_node(struct ttm_buffer_object *bo,
|
|
+ struct ttm_mem_type_manager *man,
|
|
+ struct ttm_placement *placement,
|
|
+ struct ttm_mem_reg *mem,
|
|
+ struct drm_mm_node **node)
|
|
+{
|
|
+ struct ttm_bo_global *glob = bo->glob;
|
|
+ unsigned long lpfn;
|
|
+ int ret;
|
|
+
|
|
+ lpfn = placement->lpfn;
|
|
+ if (!lpfn)
|
|
+ lpfn = man->size;
|
|
+ *node = NULL;
|
|
+ do {
|
|
+ ret = drm_mm_pre_get(&man->manager);
|
|
+ if (unlikely(ret))
|
|
return ret;
|
|
|
|
spin_lock(&glob->lru_lock);
|
|
- } while (1);
|
|
-
|
|
- if (!node) {
|
|
+ *node = drm_mm_search_free_in_range(&man->manager,
|
|
+ mem->num_pages, mem->page_alignment,
|
|
+ placement->fpfn, lpfn, 1);
|
|
+ if (unlikely(*node == NULL)) {
|
|
+ spin_unlock(&glob->lru_lock);
|
|
+ return 0;
|
|
+ }
|
|
+ *node = drm_mm_get_block_atomic_range(*node, mem->num_pages,
|
|
+ mem->page_alignment,
|
|
+ placement->fpfn,
|
|
+ lpfn);
|
|
spin_unlock(&glob->lru_lock);
|
|
- return -ENOMEM;
|
|
- }
|
|
+ } while (*node == NULL);
|
|
+ return 0;
|
|
+}
|
|
|
|
- node = drm_mm_get_block_atomic(node, num_pages, mem->page_alignment);
|
|
- if (unlikely(!node)) {
|
|
- spin_unlock(&glob->lru_lock);
|
|
- goto retry_pre_get;
|
|
- }
|
|
+/**
|
|
+ * Repeatedly evict memory from the LRU for @mem_type until we create enough
|
|
+ * space, or we've evicted everything and there isn't enough space.
|
|
+ */
|
|
+static int ttm_bo_mem_force_space(struct ttm_buffer_object *bo,
|
|
+ uint32_t mem_type,
|
|
+ struct ttm_placement *placement,
|
|
+ struct ttm_mem_reg *mem,
|
|
+ bool interruptible, bool no_wait)
|
|
+{
|
|
+ struct ttm_bo_device *bdev = bo->bdev;
|
|
+ struct ttm_bo_global *glob = bdev->glob;
|
|
+ struct ttm_mem_type_manager *man = &bdev->man[mem_type];
|
|
+ struct drm_mm_node *node;
|
|
+ int ret;
|
|
|
|
- spin_unlock(&glob->lru_lock);
|
|
+ do {
|
|
+ ret = ttm_bo_man_get_node(bo, man, placement, mem, &node);
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+ if (node)
|
|
+ break;
|
|
+ spin_lock(&glob->lru_lock);
|
|
+ if (list_empty(&man->lru)) {
|
|
+ spin_unlock(&glob->lru_lock);
|
|
+ break;
|
|
+ }
|
|
+ spin_unlock(&glob->lru_lock);
|
|
+ ret = ttm_mem_evict_first(bdev, mem_type, interruptible,
|
|
+ no_wait);
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+ } while (1);
|
|
+ if (node == NULL)
|
|
+ return -ENOMEM;
|
|
mem->mm_node = node;
|
|
mem->mem_type = mem_type;
|
|
return 0;
|
|
@@ -724,7 +821,6 @@ static uint32_t ttm_bo_select_caching(struct ttm_mem_type_manager *man,
|
|
return result;
|
|
}
|
|
|
|
-
|
|
static bool ttm_bo_mt_compatible(struct ttm_mem_type_manager *man,
|
|
bool disallow_fixed,
|
|
uint32_t mem_type,
|
|
@@ -757,66 +853,55 @@ static bool ttm_bo_mt_compatible(struct ttm_mem_type_manager *man,
|
|
* space.
|
|
*/
|
|
int ttm_bo_mem_space(struct ttm_buffer_object *bo,
|
|
- uint32_t proposed_placement,
|
|
- struct ttm_mem_reg *mem,
|
|
- bool interruptible, bool no_wait)
|
|
+ struct ttm_placement *placement,
|
|
+ struct ttm_mem_reg *mem,
|
|
+ bool interruptible, bool no_wait)
|
|
{
|
|
struct ttm_bo_device *bdev = bo->bdev;
|
|
- struct ttm_bo_global *glob = bo->glob;
|
|
struct ttm_mem_type_manager *man;
|
|
-
|
|
- uint32_t num_prios = bdev->driver->num_mem_type_prio;
|
|
- const uint32_t *prios = bdev->driver->mem_type_prio;
|
|
- uint32_t i;
|
|
uint32_t mem_type = TTM_PL_SYSTEM;
|
|
uint32_t cur_flags = 0;
|
|
bool type_found = false;
|
|
bool type_ok = false;
|
|
- bool has_eagain = false;
|
|
+ bool has_erestartsys = false;
|
|
struct drm_mm_node *node = NULL;
|
|
- int ret;
|
|
+ int i, ret;
|
|
|
|
mem->mm_node = NULL;
|
|
- for (i = 0; i < num_prios; ++i) {
|
|
- mem_type = prios[i];
|
|
+ for (i = 0; i < placement->num_placement; ++i) {
|
|
+ ret = ttm_mem_type_from_flags(placement->placement[i],
|
|
+ &mem_type);
|
|
+ if (ret)
|
|
+ return ret;
|
|
man = &bdev->man[mem_type];
|
|
|
|
type_ok = ttm_bo_mt_compatible(man,
|
|
- bo->type == ttm_bo_type_user,
|
|
- mem_type, proposed_placement,
|
|
- &cur_flags);
|
|
+ bo->type == ttm_bo_type_user,
|
|
+ mem_type,
|
|
+ placement->placement[i],
|
|
+ &cur_flags);
|
|
|
|
if (!type_ok)
|
|
continue;
|
|
|
|
cur_flags = ttm_bo_select_caching(man, bo->mem.placement,
|
|
cur_flags);
|
|
+ /*
|
|
+ * Use the access and other non-mapping-related flag bits from
|
|
+ * the memory placement flags to the current flags
|
|
+ */
|
|
+ ttm_flag_masked(&cur_flags, placement->placement[i],
|
|
+ ~TTM_PL_MASK_MEMTYPE);
|
|
|
|
if (mem_type == TTM_PL_SYSTEM)
|
|
break;
|
|
|
|
if (man->has_type && man->use_type) {
|
|
type_found = true;
|
|
- do {
|
|
- ret = drm_mm_pre_get(&man->manager);
|
|
- if (unlikely(ret))
|
|
- return ret;
|
|
-
|
|
- spin_lock(&glob->lru_lock);
|
|
- node = drm_mm_search_free(&man->manager,
|
|
- mem->num_pages,
|
|
- mem->page_alignment,
|
|
- 1);
|
|
- if (unlikely(!node)) {
|
|
- spin_unlock(&glob->lru_lock);
|
|
- break;
|
|
- }
|
|
- node = drm_mm_get_block_atomic(node,
|
|
- mem->num_pages,
|
|
- mem->
|
|
- page_alignment);
|
|
- spin_unlock(&glob->lru_lock);
|
|
- } while (!node);
|
|
+ ret = ttm_bo_man_get_node(bo, man, placement, mem,
|
|
+ &node);
|
|
+ if (unlikely(ret))
|
|
+ return ret;
|
|
}
|
|
if (node)
|
|
break;
|
|
@@ -826,67 +911,74 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo,
|
|
mem->mm_node = node;
|
|
mem->mem_type = mem_type;
|
|
mem->placement = cur_flags;
|
|
+ if (node)
|
|
+ node->private = bo;
|
|
return 0;
|
|
}
|
|
|
|
if (!type_found)
|
|
return -EINVAL;
|
|
|
|
- num_prios = bdev->driver->num_mem_busy_prio;
|
|
- prios = bdev->driver->mem_busy_prio;
|
|
-
|
|
- for (i = 0; i < num_prios; ++i) {
|
|
- mem_type = prios[i];
|
|
+ for (i = 0; i < placement->num_busy_placement; ++i) {
|
|
+ ret = ttm_mem_type_from_flags(placement->busy_placement[i],
|
|
+ &mem_type);
|
|
+ if (ret)
|
|
+ return ret;
|
|
man = &bdev->man[mem_type];
|
|
-
|
|
if (!man->has_type)
|
|
continue;
|
|
-
|
|
if (!ttm_bo_mt_compatible(man,
|
|
- bo->type == ttm_bo_type_user,
|
|
- mem_type,
|
|
- proposed_placement, &cur_flags))
|
|
+ bo->type == ttm_bo_type_user,
|
|
+ mem_type,
|
|
+ placement->busy_placement[i],
|
|
+ &cur_flags))
|
|
continue;
|
|
|
|
cur_flags = ttm_bo_select_caching(man, bo->mem.placement,
|
|
cur_flags);
|
|
+ /*
|
|
+ * Use the access and other non-mapping-related flag bits from
|
|
+ * the memory placement flags to the current flags
|
|
+ */
|
|
+ ttm_flag_masked(&cur_flags, placement->busy_placement[i],
|
|
+ ~TTM_PL_MASK_MEMTYPE);
|
|
|
|
- ret = ttm_bo_mem_force_space(bdev, mem, mem_type,
|
|
- interruptible, no_wait);
|
|
|
|
- if (ret == 0 && mem->mm_node) {
|
|
+ if (mem_type == TTM_PL_SYSTEM) {
|
|
+ mem->mem_type = mem_type;
|
|
mem->placement = cur_flags;
|
|
+ mem->mm_node = NULL;
|
|
return 0;
|
|
}
|
|
|
|
- if (ret == -ERESTART)
|
|
- has_eagain = true;
|
|
+ ret = ttm_bo_mem_force_space(bo, mem_type, placement, mem,
|
|
+ interruptible, no_wait);
|
|
+ if (ret == 0 && mem->mm_node) {
|
|
+ mem->placement = cur_flags;
|
|
+ mem->mm_node->private = bo;
|
|
+ return 0;
|
|
+ }
|
|
+ if (ret == -ERESTARTSYS)
|
|
+ has_erestartsys = true;
|
|
}
|
|
-
|
|
- ret = (has_eagain) ? -ERESTART : -ENOMEM;
|
|
+ ret = (has_erestartsys) ? -ERESTARTSYS : -ENOMEM;
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(ttm_bo_mem_space);
|
|
|
|
int ttm_bo_wait_cpu(struct ttm_buffer_object *bo, bool no_wait)
|
|
{
|
|
- int ret = 0;
|
|
-
|
|
if ((atomic_read(&bo->cpu_writers) > 0) && no_wait)
|
|
return -EBUSY;
|
|
|
|
- ret = wait_event_interruptible(bo->event_queue,
|
|
- atomic_read(&bo->cpu_writers) == 0);
|
|
-
|
|
- if (ret == -ERESTARTSYS)
|
|
- ret = -ERESTART;
|
|
-
|
|
- return ret;
|
|
+ return wait_event_interruptible(bo->event_queue,
|
|
+ atomic_read(&bo->cpu_writers) == 0);
|
|
}
|
|
+EXPORT_SYMBOL(ttm_bo_wait_cpu);
|
|
|
|
int ttm_bo_move_buffer(struct ttm_buffer_object *bo,
|
|
- uint32_t proposed_placement,
|
|
- bool interruptible, bool no_wait)
|
|
+ struct ttm_placement *placement,
|
|
+ bool interruptible, bool no_wait)
|
|
{
|
|
struct ttm_bo_global *glob = bo->glob;
|
|
int ret = 0;
|
|
@@ -899,147 +991,138 @@ int ttm_bo_move_buffer(struct ttm_buffer_object *bo,
|
|
* Have the driver move function wait for idle when necessary,
|
|
* instead of doing it here.
|
|
*/
|
|
-
|
|
spin_lock(&bo->lock);
|
|
ret = ttm_bo_wait(bo, false, interruptible, no_wait);
|
|
spin_unlock(&bo->lock);
|
|
-
|
|
if (ret)
|
|
return ret;
|
|
-
|
|
mem.num_pages = bo->num_pages;
|
|
mem.size = mem.num_pages << PAGE_SHIFT;
|
|
mem.page_alignment = bo->mem.page_alignment;
|
|
-
|
|
/*
|
|
* Determine where to move the buffer.
|
|
*/
|
|
-
|
|
- ret = ttm_bo_mem_space(bo, proposed_placement, &mem,
|
|
- interruptible, no_wait);
|
|
+ ret = ttm_bo_mem_space(bo, placement, &mem, interruptible, no_wait);
|
|
if (ret)
|
|
goto out_unlock;
|
|
-
|
|
ret = ttm_bo_handle_move_mem(bo, &mem, false, interruptible, no_wait);
|
|
-
|
|
out_unlock:
|
|
if (ret && mem.mm_node) {
|
|
spin_lock(&glob->lru_lock);
|
|
+ mem.mm_node->private = NULL;
|
|
drm_mm_put_block(mem.mm_node);
|
|
spin_unlock(&glob->lru_lock);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
-static int ttm_bo_mem_compat(uint32_t proposed_placement,
|
|
+static int ttm_bo_mem_compat(struct ttm_placement *placement,
|
|
struct ttm_mem_reg *mem)
|
|
{
|
|
- if ((proposed_placement & mem->placement & TTM_PL_MASK_MEM) == 0)
|
|
- return 0;
|
|
- if ((proposed_placement & mem->placement & TTM_PL_MASK_CACHING) == 0)
|
|
- return 0;
|
|
-
|
|
- return 1;
|
|
+ int i;
|
|
+ struct drm_mm_node *node = mem->mm_node;
|
|
+
|
|
+ if (node && placement->lpfn != 0 &&
|
|
+ (node->start < placement->fpfn ||
|
|
+ node->start + node->size > placement->lpfn))
|
|
+ return -1;
|
|
+
|
|
+ for (i = 0; i < placement->num_placement; i++) {
|
|
+ if ((placement->placement[i] & mem->placement &
|
|
+ TTM_PL_MASK_CACHING) &&
|
|
+ (placement->placement[i] & mem->placement &
|
|
+ TTM_PL_MASK_MEM))
|
|
+ return i;
|
|
+ }
|
|
+ return -1;
|
|
}
|
|
|
|
-int ttm_buffer_object_validate(struct ttm_buffer_object *bo,
|
|
- uint32_t proposed_placement,
|
|
- bool interruptible, bool no_wait)
|
|
+int ttm_bo_validate(struct ttm_buffer_object *bo,
|
|
+ struct ttm_placement *placement,
|
|
+ bool interruptible, bool no_wait)
|
|
{
|
|
int ret;
|
|
|
|
BUG_ON(!atomic_read(&bo->reserved));
|
|
- bo->proposed_placement = proposed_placement;
|
|
-
|
|
- TTM_DEBUG("Proposed placement 0x%08lx, Old flags 0x%08lx\n",
|
|
- (unsigned long)proposed_placement,
|
|
- (unsigned long)bo->mem.placement);
|
|
-
|
|
+ /* Check that range is valid */
|
|
+ if (placement->lpfn || placement->fpfn)
|
|
+ if (placement->fpfn > placement->lpfn ||
|
|
+ (placement->lpfn - placement->fpfn) < bo->num_pages)
|
|
+ return -EINVAL;
|
|
/*
|
|
* Check whether we need to move buffer.
|
|
*/
|
|
-
|
|
- if (!ttm_bo_mem_compat(bo->proposed_placement, &bo->mem)) {
|
|
- ret = ttm_bo_move_buffer(bo, bo->proposed_placement,
|
|
- interruptible, no_wait);
|
|
- if (ret) {
|
|
- if (ret != -ERESTART)
|
|
- printk(KERN_ERR TTM_PFX
|
|
- "Failed moving buffer. "
|
|
- "Proposed placement 0x%08x\n",
|
|
- bo->proposed_placement);
|
|
- if (ret == -ENOMEM)
|
|
- printk(KERN_ERR TTM_PFX
|
|
- "Out of aperture space or "
|
|
- "DRM memory quota.\n");
|
|
+ ret = ttm_bo_mem_compat(placement, &bo->mem);
|
|
+ if (ret < 0) {
|
|
+ ret = ttm_bo_move_buffer(bo, placement, interruptible, no_wait);
|
|
+ if (ret)
|
|
return ret;
|
|
- }
|
|
+ } else {
|
|
+ /*
|
|
+ * Use the access and other non-mapping-related flag bits from
|
|
+ * the compatible memory placement flags to the active flags
|
|
+ */
|
|
+ ttm_flag_masked(&bo->mem.placement, placement->placement[ret],
|
|
+ ~TTM_PL_MASK_MEMTYPE);
|
|
}
|
|
-
|
|
/*
|
|
* We might need to add a TTM.
|
|
*/
|
|
-
|
|
if (bo->mem.mem_type == TTM_PL_SYSTEM && bo->ttm == NULL) {
|
|
ret = ttm_bo_add_ttm(bo, true);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
- /*
|
|
- * Validation has succeeded, move the access and other
|
|
- * non-mapping-related flag bits from the proposed flags to
|
|
- * the active flags
|
|
- */
|
|
-
|
|
- ttm_flag_masked(&bo->mem.placement, bo->proposed_placement,
|
|
- ~TTM_PL_MASK_MEMTYPE);
|
|
-
|
|
return 0;
|
|
}
|
|
-EXPORT_SYMBOL(ttm_buffer_object_validate);
|
|
+EXPORT_SYMBOL(ttm_bo_validate);
|
|
|
|
-int
|
|
-ttm_bo_check_placement(struct ttm_buffer_object *bo,
|
|
- uint32_t set_flags, uint32_t clr_flags)
|
|
+int ttm_bo_check_placement(struct ttm_buffer_object *bo,
|
|
+ struct ttm_placement *placement)
|
|
{
|
|
- uint32_t new_mask = set_flags | clr_flags;
|
|
-
|
|
- if ((bo->type == ttm_bo_type_user) &&
|
|
- (clr_flags & TTM_PL_FLAG_CACHED)) {
|
|
- printk(KERN_ERR TTM_PFX
|
|
- "User buffers require cache-coherent memory.\n");
|
|
- return -EINVAL;
|
|
- }
|
|
-
|
|
- if (!capable(CAP_SYS_ADMIN)) {
|
|
- if (new_mask & TTM_PL_FLAG_NO_EVICT) {
|
|
- printk(KERN_ERR TTM_PFX "Need to be root to modify"
|
|
- " NO_EVICT status.\n");
|
|
+ int i;
|
|
+
|
|
+ if (placement->fpfn || placement->lpfn) {
|
|
+ if (bo->mem.num_pages > (placement->lpfn - placement->fpfn)) {
|
|
+ printk(KERN_ERR TTM_PFX "Page number range to small "
|
|
+ "Need %lu pages, range is [%u, %u]\n",
|
|
+ bo->mem.num_pages, placement->fpfn,
|
|
+ placement->lpfn);
|
|
return -EINVAL;
|
|
}
|
|
-
|
|
- if ((clr_flags & bo->mem.placement & TTM_PL_MASK_MEMTYPE) &&
|
|
- (bo->mem.placement & TTM_PL_FLAG_NO_EVICT)) {
|
|
- printk(KERN_ERR TTM_PFX
|
|
- "Incompatible memory specification"
|
|
- " for NO_EVICT buffer.\n");
|
|
- return -EINVAL;
|
|
+ }
|
|
+ for (i = 0; i < placement->num_placement; i++) {
|
|
+ if (!capable(CAP_SYS_ADMIN)) {
|
|
+ if (placement->placement[i] & TTM_PL_FLAG_NO_EVICT) {
|
|
+ printk(KERN_ERR TTM_PFX "Need to be root to "
|
|
+ "modify NO_EVICT status.\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ for (i = 0; i < placement->num_busy_placement; i++) {
|
|
+ if (!capable(CAP_SYS_ADMIN)) {
|
|
+ if (placement->busy_placement[i] & TTM_PL_FLAG_NO_EVICT) {
|
|
+ printk(KERN_ERR TTM_PFX "Need to be root to "
|
|
+ "modify NO_EVICT status.\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
-int ttm_buffer_object_init(struct ttm_bo_device *bdev,
|
|
- struct ttm_buffer_object *bo,
|
|
- unsigned long size,
|
|
- enum ttm_bo_type type,
|
|
- uint32_t flags,
|
|
- uint32_t page_alignment,
|
|
- unsigned long buffer_start,
|
|
- bool interruptible,
|
|
- struct file *persistant_swap_storage,
|
|
- size_t acc_size,
|
|
- void (*destroy) (struct ttm_buffer_object *))
|
|
+int ttm_bo_init(struct ttm_bo_device *bdev,
|
|
+ struct ttm_buffer_object *bo,
|
|
+ unsigned long size,
|
|
+ enum ttm_bo_type type,
|
|
+ struct ttm_placement *placement,
|
|
+ uint32_t page_alignment,
|
|
+ unsigned long buffer_start,
|
|
+ bool interruptible,
|
|
+ struct file *persistant_swap_storage,
|
|
+ size_t acc_size,
|
|
+ void (*destroy) (struct ttm_buffer_object *))
|
|
{
|
|
int ret = 0;
|
|
unsigned long num_pages;
|
|
@@ -1065,6 +1148,7 @@ int ttm_buffer_object_init(struct ttm_bo_device *bdev,
|
|
bo->glob = bdev->glob;
|
|
bo->type = type;
|
|
bo->num_pages = num_pages;
|
|
+ bo->mem.size = num_pages << PAGE_SHIFT;
|
|
bo->mem.mem_type = TTM_PL_SYSTEM;
|
|
bo->mem.num_pages = bo->num_pages;
|
|
bo->mem.mm_node = NULL;
|
|
@@ -1077,29 +1161,21 @@ int ttm_buffer_object_init(struct ttm_bo_device *bdev,
|
|
bo->acc_size = acc_size;
|
|
atomic_inc(&bo->glob->bo_count);
|
|
|
|
- ret = ttm_bo_check_placement(bo, flags, 0ULL);
|
|
+ ret = ttm_bo_check_placement(bo, placement);
|
|
if (unlikely(ret != 0))
|
|
goto out_err;
|
|
|
|
/*
|
|
- * If no caching attributes are set, accept any form of caching.
|
|
- */
|
|
-
|
|
- if ((flags & TTM_PL_MASK_CACHING) == 0)
|
|
- flags |= TTM_PL_MASK_CACHING;
|
|
-
|
|
- /*
|
|
* For ttm_bo_type_device buffers, allocate
|
|
* address space from the device.
|
|
*/
|
|
-
|
|
if (bo->type == ttm_bo_type_device) {
|
|
ret = ttm_bo_setup_vm(bo);
|
|
if (ret)
|
|
goto out_err;
|
|
}
|
|
|
|
- ret = ttm_buffer_object_validate(bo, flags, interruptible, false);
|
|
+ ret = ttm_bo_validate(bo, placement, interruptible, false);
|
|
if (ret)
|
|
goto out_err;
|
|
|
|
@@ -1112,7 +1188,7 @@ out_err:
|
|
|
|
return ret;
|
|
}
|
|
-EXPORT_SYMBOL(ttm_buffer_object_init);
|
|
+EXPORT_SYMBOL(ttm_bo_init);
|
|
|
|
static inline size_t ttm_bo_size(struct ttm_bo_global *glob,
|
|
unsigned long num_pages)
|
|
@@ -1123,19 +1199,19 @@ static inline size_t ttm_bo_size(struct ttm_bo_global *glob,
|
|
return glob->ttm_bo_size + 2 * page_array_size;
|
|
}
|
|
|
|
-int ttm_buffer_object_create(struct ttm_bo_device *bdev,
|
|
- unsigned long size,
|
|
- enum ttm_bo_type type,
|
|
- uint32_t flags,
|
|
- uint32_t page_alignment,
|
|
- unsigned long buffer_start,
|
|
- bool interruptible,
|
|
- struct file *persistant_swap_storage,
|
|
- struct ttm_buffer_object **p_bo)
|
|
+int ttm_bo_create(struct ttm_bo_device *bdev,
|
|
+ unsigned long size,
|
|
+ enum ttm_bo_type type,
|
|
+ struct ttm_placement *placement,
|
|
+ uint32_t page_alignment,
|
|
+ unsigned long buffer_start,
|
|
+ bool interruptible,
|
|
+ struct file *persistant_swap_storage,
|
|
+ struct ttm_buffer_object **p_bo)
|
|
{
|
|
struct ttm_buffer_object *bo;
|
|
- int ret;
|
|
struct ttm_mem_global *mem_glob = bdev->glob->mem_glob;
|
|
+ int ret;
|
|
|
|
size_t acc_size =
|
|
ttm_bo_size(bdev->glob, (size + PAGE_SIZE - 1) >> PAGE_SHIFT);
|
|
@@ -1150,76 +1226,41 @@ int ttm_buffer_object_create(struct ttm_bo_device *bdev,
|
|
return -ENOMEM;
|
|
}
|
|
|
|
- ret = ttm_buffer_object_init(bdev, bo, size, type, flags,
|
|
- page_alignment, buffer_start,
|
|
- interruptible,
|
|
- persistant_swap_storage, acc_size, NULL);
|
|
+ ret = ttm_bo_init(bdev, bo, size, type, placement, page_alignment,
|
|
+ buffer_start, interruptible,
|
|
+ persistant_swap_storage, acc_size, NULL);
|
|
if (likely(ret == 0))
|
|
*p_bo = bo;
|
|
|
|
return ret;
|
|
}
|
|
|
|
-static int ttm_bo_leave_list(struct ttm_buffer_object *bo,
|
|
- uint32_t mem_type, bool allow_errors)
|
|
-{
|
|
- int ret;
|
|
-
|
|
- spin_lock(&bo->lock);
|
|
- ret = ttm_bo_wait(bo, false, false, false);
|
|
- spin_unlock(&bo->lock);
|
|
-
|
|
- if (ret && allow_errors)
|
|
- goto out;
|
|
-
|
|
- if (bo->mem.mem_type == mem_type)
|
|
- ret = ttm_bo_evict(bo, mem_type, false, false);
|
|
-
|
|
- if (ret) {
|
|
- if (allow_errors) {
|
|
- goto out;
|
|
- } else {
|
|
- ret = 0;
|
|
- printk(KERN_ERR TTM_PFX "Cleanup eviction failed\n");
|
|
- }
|
|
- }
|
|
-
|
|
-out:
|
|
- return ret;
|
|
-}
|
|
-
|
|
static int ttm_bo_force_list_clean(struct ttm_bo_device *bdev,
|
|
- struct list_head *head,
|
|
- unsigned mem_type, bool allow_errors)
|
|
+ unsigned mem_type, bool allow_errors)
|
|
{
|
|
+ struct ttm_mem_type_manager *man = &bdev->man[mem_type];
|
|
struct ttm_bo_global *glob = bdev->glob;
|
|
- struct ttm_buffer_object *entry;
|
|
int ret;
|
|
- int put_count;
|
|
|
|
/*
|
|
* Can't use standard list traversal since we're unlocking.
|
|
*/
|
|
|
|
spin_lock(&glob->lru_lock);
|
|
-
|
|
- while (!list_empty(head)) {
|
|
- entry = list_first_entry(head, struct ttm_buffer_object, lru);
|
|
- kref_get(&entry->list_kref);
|
|
- ret = ttm_bo_reserve_locked(entry, false, false, false, 0);
|
|
- put_count = ttm_bo_del_from_lru(entry);
|
|
+ while (!list_empty(&man->lru)) {
|
|
spin_unlock(&glob->lru_lock);
|
|
- while (put_count--)
|
|
- kref_put(&entry->list_kref, ttm_bo_ref_bug);
|
|
- BUG_ON(ret);
|
|
- ret = ttm_bo_leave_list(entry, mem_type, allow_errors);
|
|
- ttm_bo_unreserve(entry);
|
|
- kref_put(&entry->list_kref, ttm_bo_release_list);
|
|
+ ret = ttm_mem_evict_first(bdev, mem_type, false, false);
|
|
+ if (ret) {
|
|
+ if (allow_errors) {
|
|
+ return ret;
|
|
+ } else {
|
|
+ printk(KERN_ERR TTM_PFX
|
|
+ "Cleanup eviction failed\n");
|
|
+ }
|
|
+ }
|
|
spin_lock(&glob->lru_lock);
|
|
}
|
|
-
|
|
spin_unlock(&glob->lru_lock);
|
|
-
|
|
return 0;
|
|
}
|
|
|
|
@@ -1246,7 +1287,7 @@ int ttm_bo_clean_mm(struct ttm_bo_device *bdev, unsigned mem_type)
|
|
|
|
ret = 0;
|
|
if (mem_type > 0) {
|
|
- ttm_bo_force_list_clean(bdev, &man->lru, mem_type, false);
|
|
+ ttm_bo_force_list_clean(bdev, mem_type, false);
|
|
|
|
spin_lock(&glob->lru_lock);
|
|
if (drm_mm_clean(&man->manager))
|
|
@@ -1279,12 +1320,12 @@ int ttm_bo_evict_mm(struct ttm_bo_device *bdev, unsigned mem_type)
|
|
return 0;
|
|
}
|
|
|
|
- return ttm_bo_force_list_clean(bdev, &man->lru, mem_type, true);
|
|
+ return ttm_bo_force_list_clean(bdev, mem_type, true);
|
|
}
|
|
EXPORT_SYMBOL(ttm_bo_evict_mm);
|
|
|
|
int ttm_bo_init_mm(struct ttm_bo_device *bdev, unsigned type,
|
|
- unsigned long p_offset, unsigned long p_size)
|
|
+ unsigned long p_size)
|
|
{
|
|
int ret = -EINVAL;
|
|
struct ttm_mem_type_manager *man;
|
|
@@ -1314,7 +1355,7 @@ int ttm_bo_init_mm(struct ttm_bo_device *bdev, unsigned type,
|
|
type);
|
|
return ret;
|
|
}
|
|
- ret = drm_mm_init(&man->manager, p_offset, p_size);
|
|
+ ret = drm_mm_init(&man->manager, 0, p_size);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
@@ -1463,7 +1504,7 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev,
|
|
* Initialize the system memory buffer type.
|
|
* Other types need to be driver / IOCTL initialized.
|
|
*/
|
|
- ret = ttm_bo_init_mm(bdev, TTM_PL_SYSTEM, 0, 0);
|
|
+ ret = ttm_bo_init_mm(bdev, TTM_PL_SYSTEM, 0);
|
|
if (unlikely(ret != 0))
|
|
goto out_no_sys;
|
|
|
|
@@ -1693,7 +1734,7 @@ int ttm_bo_block_reservation(struct ttm_buffer_object *bo, bool interruptible,
|
|
ret = wait_event_interruptible
|
|
(bo->event_queue, atomic_read(&bo->reserved) == 0);
|
|
if (unlikely(ret != 0))
|
|
- return -ERESTART;
|
|
+ return ret;
|
|
} else {
|
|
wait_event(bo->event_queue,
|
|
atomic_read(&bo->reserved) == 0);
|
|
@@ -1722,12 +1763,14 @@ int ttm_bo_synccpu_write_grab(struct ttm_buffer_object *bo, bool no_wait)
|
|
ttm_bo_unreserve(bo);
|
|
return ret;
|
|
}
|
|
+EXPORT_SYMBOL(ttm_bo_synccpu_write_grab);
|
|
|
|
void ttm_bo_synccpu_write_release(struct ttm_buffer_object *bo)
|
|
{
|
|
if (atomic_dec_and_test(&bo->cpu_writers))
|
|
wake_up_all(&bo->event_queue);
|
|
}
|
|
+EXPORT_SYMBOL(ttm_bo_synccpu_write_release);
|
|
|
|
/**
|
|
* A buffer object shrink method that tries to swap out the first
|
|
@@ -1808,6 +1851,9 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink)
|
|
* anyone tries to access a ttm page.
|
|
*/
|
|
|
|
+ if (bo->bdev->driver->swap_notify)
|
|
+ bo->bdev->driver->swap_notify(bo);
|
|
+
|
|
ret = ttm_tt_swapout(bo->ttm, bo->persistant_swap_storage);
|
|
out:
|
|
|
|
@@ -1828,3 +1874,4 @@ void ttm_bo_swapout_all(struct ttm_bo_device *bdev)
|
|
while (ttm_bo_swapout(&bdev->glob->shrink) == 0)
|
|
;
|
|
}
|
|
+EXPORT_SYMBOL(ttm_bo_swapout_all);
|
|
diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
|
|
index c70927e..5ca37a5 100644
|
|
--- a/drivers/gpu/drm/ttm/ttm_bo_util.c
|
|
+++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
|
|
@@ -53,7 +53,6 @@ int ttm_bo_move_ttm(struct ttm_buffer_object *bo,
|
|
{
|
|
struct ttm_tt *ttm = bo->ttm;
|
|
struct ttm_mem_reg *old_mem = &bo->mem;
|
|
- uint32_t save_flags = old_mem->placement;
|
|
int ret;
|
|
|
|
if (old_mem->mem_type != TTM_PL_SYSTEM) {
|
|
@@ -62,7 +61,6 @@ int ttm_bo_move_ttm(struct ttm_buffer_object *bo,
|
|
ttm_flag_masked(&old_mem->placement, TTM_PL_FLAG_SYSTEM,
|
|
TTM_PL_MASK_MEM);
|
|
old_mem->mem_type = TTM_PL_SYSTEM;
|
|
- save_flags = old_mem->placement;
|
|
}
|
|
|
|
ret = ttm_tt_set_placement_caching(ttm, new_mem->placement);
|
|
@@ -77,7 +75,7 @@ int ttm_bo_move_ttm(struct ttm_buffer_object *bo,
|
|
|
|
*old_mem = *new_mem;
|
|
new_mem->mm_node = NULL;
|
|
- ttm_flag_masked(&save_flags, new_mem->placement, TTM_PL_MASK_MEMTYPE);
|
|
+
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(ttm_bo_move_ttm);
|
|
@@ -219,7 +217,6 @@ int ttm_bo_move_memcpy(struct ttm_buffer_object *bo,
|
|
void *old_iomap;
|
|
void *new_iomap;
|
|
int ret;
|
|
- uint32_t save_flags = old_mem->placement;
|
|
unsigned long i;
|
|
unsigned long page;
|
|
unsigned long add = 0;
|
|
@@ -270,7 +267,6 @@ out2:
|
|
|
|
*old_mem = *new_mem;
|
|
new_mem->mm_node = NULL;
|
|
- ttm_flag_masked(&save_flags, new_mem->placement, TTM_PL_MASK_MEMTYPE);
|
|
|
|
if ((man->flags & TTM_MEMTYPE_FLAG_FIXED) && (ttm != NULL)) {
|
|
ttm_tt_unbind(ttm);
|
|
@@ -369,6 +365,7 @@ pgprot_t ttm_io_prot(uint32_t caching_flags, pgprot_t tmp)
|
|
#endif
|
|
return tmp;
|
|
}
|
|
+EXPORT_SYMBOL(ttm_io_prot);
|
|
|
|
static int ttm_bo_ioremap(struct ttm_buffer_object *bo,
|
|
unsigned long bus_base,
|
|
@@ -427,7 +424,7 @@ static int ttm_bo_kmap_ttm(struct ttm_buffer_object *bo,
|
|
|
|
/*
|
|
* We need to use vmap to get the desired page protection
|
|
- * or to make the buffer object look contigous.
|
|
+ * or to make the buffer object look contiguous.
|
|
*/
|
|
prot = (mem->placement & TTM_PL_FLAG_CACHED) ?
|
|
PAGE_KERNEL :
|
|
@@ -536,7 +533,6 @@ int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo,
|
|
struct ttm_mem_type_manager *man = &bdev->man[new_mem->mem_type];
|
|
struct ttm_mem_reg *old_mem = &bo->mem;
|
|
int ret;
|
|
- uint32_t save_flags = old_mem->placement;
|
|
struct ttm_buffer_object *ghost_obj;
|
|
void *tmp_obj = NULL;
|
|
|
|
@@ -597,7 +593,7 @@ int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo,
|
|
|
|
*old_mem = *new_mem;
|
|
new_mem->mm_node = NULL;
|
|
- ttm_flag_masked(&save_flags, new_mem->placement, TTM_PL_MASK_MEMTYPE);
|
|
+
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(ttm_bo_move_accel_cleanup);
|
|
diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c
|
|
index 1c040d0..668dbe8 100644
|
|
--- a/drivers/gpu/drm/ttm/ttm_bo_vm.c
|
|
+++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c
|
|
@@ -114,7 +114,7 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
|
|
ret = ttm_bo_wait(bo, false, true, false);
|
|
spin_unlock(&bo->lock);
|
|
if (unlikely(ret != 0)) {
|
|
- retval = (ret != -ERESTART) ?
|
|
+ retval = (ret != -ERESTARTSYS) ?
|
|
VM_FAULT_SIGBUS : VM_FAULT_NOPAGE;
|
|
goto out_unlock;
|
|
}
|
|
@@ -320,7 +320,7 @@ ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp,
|
|
return -EFAULT;
|
|
|
|
driver = bo->bdev->driver;
|
|
- if (unlikely(driver->verify_access)) {
|
|
+ if (unlikely(!driver->verify_access)) {
|
|
ret = -EPERM;
|
|
goto out_unref;
|
|
}
|
|
@@ -349,9 +349,6 @@ ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp,
|
|
switch (ret) {
|
|
case 0:
|
|
break;
|
|
- case -ERESTART:
|
|
- ret = -EINTR;
|
|
- goto out_unref;
|
|
case -EBUSY:
|
|
ret = -EAGAIN;
|
|
goto out_unref;
|
|
@@ -421,8 +418,6 @@ ssize_t ttm_bo_fbdev_io(struct ttm_buffer_object *bo, const char __user *wbuf,
|
|
switch (ret) {
|
|
case 0:
|
|
break;
|
|
- case -ERESTART:
|
|
- return -EINTR;
|
|
case -EBUSY:
|
|
return -EAGAIN;
|
|
default:
|
|
diff --git a/drivers/gpu/drm/ttm/ttm_execbuf_util.c b/drivers/gpu/drm/ttm/ttm_execbuf_util.c
|
|
new file mode 100644
|
|
index 0000000..c285c29
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/ttm/ttm_execbuf_util.c
|
|
@@ -0,0 +1,117 @@
|
|
+/**************************************************************************
|
|
+ *
|
|
+ * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sub license, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial portions
|
|
+ * of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ **************************************************************************/
|
|
+
|
|
+#include "ttm/ttm_execbuf_util.h"
|
|
+#include "ttm/ttm_bo_driver.h"
|
|
+#include "ttm/ttm_placement.h"
|
|
+#include <linux/wait.h>
|
|
+#include <linux/sched.h>
|
|
+#include <linux/module.h>
|
|
+
|
|
+void ttm_eu_backoff_reservation(struct list_head *list)
|
|
+{
|
|
+ struct ttm_validate_buffer *entry;
|
|
+
|
|
+ list_for_each_entry(entry, list, head) {
|
|
+ struct ttm_buffer_object *bo = entry->bo;
|
|
+ if (!entry->reserved)
|
|
+ continue;
|
|
+
|
|
+ entry->reserved = false;
|
|
+ ttm_bo_unreserve(bo);
|
|
+ }
|
|
+}
|
|
+EXPORT_SYMBOL(ttm_eu_backoff_reservation);
|
|
+
|
|
+/*
|
|
+ * Reserve buffers for validation.
|
|
+ *
|
|
+ * If a buffer in the list is marked for CPU access, we back off and
|
|
+ * wait for that buffer to become free for GPU access.
|
|
+ *
|
|
+ * If a buffer is reserved for another validation, the validator with
|
|
+ * the highest validation sequence backs off and waits for that buffer
|
|
+ * to become unreserved. This prevents deadlocks when validating multiple
|
|
+ * buffers in different orders.
|
|
+ */
|
|
+
|
|
+int ttm_eu_reserve_buffers(struct list_head *list, uint32_t val_seq)
|
|
+{
|
|
+ struct ttm_validate_buffer *entry;
|
|
+ int ret;
|
|
+
|
|
+retry:
|
|
+ list_for_each_entry(entry, list, head) {
|
|
+ struct ttm_buffer_object *bo = entry->bo;
|
|
+
|
|
+ entry->reserved = false;
|
|
+ ret = ttm_bo_reserve(bo, true, false, true, val_seq);
|
|
+ if (ret != 0) {
|
|
+ ttm_eu_backoff_reservation(list);
|
|
+ if (ret == -EAGAIN) {
|
|
+ ret = ttm_bo_wait_unreserved(bo, true);
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+ goto retry;
|
|
+ } else
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ entry->reserved = true;
|
|
+ if (unlikely(atomic_read(&bo->cpu_writers) > 0)) {
|
|
+ ttm_eu_backoff_reservation(list);
|
|
+ ret = ttm_bo_wait_cpu(bo, false);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ goto retry;
|
|
+ }
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL(ttm_eu_reserve_buffers);
|
|
+
|
|
+void ttm_eu_fence_buffer_objects(struct list_head *list, void *sync_obj)
|
|
+{
|
|
+ struct ttm_validate_buffer *entry;
|
|
+
|
|
+ list_for_each_entry(entry, list, head) {
|
|
+ struct ttm_buffer_object *bo = entry->bo;
|
|
+ struct ttm_bo_driver *driver = bo->bdev->driver;
|
|
+ void *old_sync_obj;
|
|
+
|
|
+ spin_lock(&bo->lock);
|
|
+ old_sync_obj = bo->sync_obj;
|
|
+ bo->sync_obj = driver->sync_obj_ref(sync_obj);
|
|
+ bo->sync_obj_arg = entry->new_sync_obj_arg;
|
|
+ spin_unlock(&bo->lock);
|
|
+ ttm_bo_unreserve(bo);
|
|
+ entry->reserved = false;
|
|
+ if (old_sync_obj)
|
|
+ driver->sync_obj_unref(&old_sync_obj);
|
|
+ }
|
|
+}
|
|
+EXPORT_SYMBOL(ttm_eu_fence_buffer_objects);
|
|
diff --git a/drivers/gpu/drm/ttm/ttm_lock.c b/drivers/gpu/drm/ttm/ttm_lock.c
|
|
new file mode 100644
|
|
index 0000000..3d172ef
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/ttm/ttm_lock.c
|
|
@@ -0,0 +1,313 @@
|
|
+/**************************************************************************
|
|
+ *
|
|
+ * Copyright (c) 2007-2009 VMware, Inc., Palo Alto, CA., USA
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sub license, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial portions
|
|
+ * of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ **************************************************************************/
|
|
+/*
|
|
+ * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
|
|
+ */
|
|
+
|
|
+#include "ttm/ttm_lock.h"
|
|
+#include "ttm/ttm_module.h"
|
|
+#include <asm/atomic.h>
|
|
+#include <linux/errno.h>
|
|
+#include <linux/wait.h>
|
|
+#include <linux/sched.h>
|
|
+#include <linux/module.h>
|
|
+
|
|
+#define TTM_WRITE_LOCK_PENDING (1 << 0)
|
|
+#define TTM_VT_LOCK_PENDING (1 << 1)
|
|
+#define TTM_SUSPEND_LOCK_PENDING (1 << 2)
|
|
+#define TTM_VT_LOCK (1 << 3)
|
|
+#define TTM_SUSPEND_LOCK (1 << 4)
|
|
+
|
|
+void ttm_lock_init(struct ttm_lock *lock)
|
|
+{
|
|
+ spin_lock_init(&lock->lock);
|
|
+ init_waitqueue_head(&lock->queue);
|
|
+ lock->rw = 0;
|
|
+ lock->flags = 0;
|
|
+ lock->kill_takers = false;
|
|
+ lock->signal = SIGKILL;
|
|
+}
|
|
+EXPORT_SYMBOL(ttm_lock_init);
|
|
+
|
|
+void ttm_read_unlock(struct ttm_lock *lock)
|
|
+{
|
|
+ spin_lock(&lock->lock);
|
|
+ if (--lock->rw == 0)
|
|
+ wake_up_all(&lock->queue);
|
|
+ spin_unlock(&lock->lock);
|
|
+}
|
|
+EXPORT_SYMBOL(ttm_read_unlock);
|
|
+
|
|
+static bool __ttm_read_lock(struct ttm_lock *lock)
|
|
+{
|
|
+ bool locked = false;
|
|
+
|
|
+ spin_lock(&lock->lock);
|
|
+ if (unlikely(lock->kill_takers)) {
|
|
+ send_sig(lock->signal, current, 0);
|
|
+ spin_unlock(&lock->lock);
|
|
+ return false;
|
|
+ }
|
|
+ if (lock->rw >= 0 && lock->flags == 0) {
|
|
+ ++lock->rw;
|
|
+ locked = true;
|
|
+ }
|
|
+ spin_unlock(&lock->lock);
|
|
+ return locked;
|
|
+}
|
|
+
|
|
+int ttm_read_lock(struct ttm_lock *lock, bool interruptible)
|
|
+{
|
|
+ int ret = 0;
|
|
+
|
|
+ if (interruptible)
|
|
+ ret = wait_event_interruptible(lock->queue,
|
|
+ __ttm_read_lock(lock));
|
|
+ else
|
|
+ wait_event(lock->queue, __ttm_read_lock(lock));
|
|
+ return ret;
|
|
+}
|
|
+EXPORT_SYMBOL(ttm_read_lock);
|
|
+
|
|
+static bool __ttm_read_trylock(struct ttm_lock *lock, bool *locked)
|
|
+{
|
|
+ bool block = true;
|
|
+
|
|
+ *locked = false;
|
|
+
|
|
+ spin_lock(&lock->lock);
|
|
+ if (unlikely(lock->kill_takers)) {
|
|
+ send_sig(lock->signal, current, 0);
|
|
+ spin_unlock(&lock->lock);
|
|
+ return false;
|
|
+ }
|
|
+ if (lock->rw >= 0 && lock->flags == 0) {
|
|
+ ++lock->rw;
|
|
+ block = false;
|
|
+ *locked = true;
|
|
+ } else if (lock->flags == 0) {
|
|
+ block = false;
|
|
+ }
|
|
+ spin_unlock(&lock->lock);
|
|
+
|
|
+ return !block;
|
|
+}
|
|
+
|
|
+int ttm_read_trylock(struct ttm_lock *lock, bool interruptible)
|
|
+{
|
|
+ int ret = 0;
|
|
+ bool locked;
|
|
+
|
|
+ if (interruptible)
|
|
+ ret = wait_event_interruptible
|
|
+ (lock->queue, __ttm_read_trylock(lock, &locked));
|
|
+ else
|
|
+ wait_event(lock->queue, __ttm_read_trylock(lock, &locked));
|
|
+
|
|
+ if (unlikely(ret != 0)) {
|
|
+ BUG_ON(locked);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return (locked) ? 0 : -EBUSY;
|
|
+}
|
|
+
|
|
+void ttm_write_unlock(struct ttm_lock *lock)
|
|
+{
|
|
+ spin_lock(&lock->lock);
|
|
+ lock->rw = 0;
|
|
+ wake_up_all(&lock->queue);
|
|
+ spin_unlock(&lock->lock);
|
|
+}
|
|
+EXPORT_SYMBOL(ttm_write_unlock);
|
|
+
|
|
+static bool __ttm_write_lock(struct ttm_lock *lock)
|
|
+{
|
|
+ bool locked = false;
|
|
+
|
|
+ spin_lock(&lock->lock);
|
|
+ if (unlikely(lock->kill_takers)) {
|
|
+ send_sig(lock->signal, current, 0);
|
|
+ spin_unlock(&lock->lock);
|
|
+ return false;
|
|
+ }
|
|
+ if (lock->rw == 0 && ((lock->flags & ~TTM_WRITE_LOCK_PENDING) == 0)) {
|
|
+ lock->rw = -1;
|
|
+ lock->flags &= ~TTM_WRITE_LOCK_PENDING;
|
|
+ locked = true;
|
|
+ } else {
|
|
+ lock->flags |= TTM_WRITE_LOCK_PENDING;
|
|
+ }
|
|
+ spin_unlock(&lock->lock);
|
|
+ return locked;
|
|
+}
|
|
+
|
|
+int ttm_write_lock(struct ttm_lock *lock, bool interruptible)
|
|
+{
|
|
+ int ret = 0;
|
|
+
|
|
+ if (interruptible) {
|
|
+ ret = wait_event_interruptible(lock->queue,
|
|
+ __ttm_write_lock(lock));
|
|
+ if (unlikely(ret != 0)) {
|
|
+ spin_lock(&lock->lock);
|
|
+ lock->flags &= ~TTM_WRITE_LOCK_PENDING;
|
|
+ wake_up_all(&lock->queue);
|
|
+ spin_unlock(&lock->lock);
|
|
+ }
|
|
+ } else
|
|
+ wait_event(lock->queue, __ttm_read_lock(lock));
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+EXPORT_SYMBOL(ttm_write_lock);
|
|
+
|
|
+void ttm_write_lock_downgrade(struct ttm_lock *lock)
|
|
+{
|
|
+ spin_lock(&lock->lock);
|
|
+ lock->rw = 1;
|
|
+ wake_up_all(&lock->queue);
|
|
+ spin_unlock(&lock->lock);
|
|
+}
|
|
+
|
|
+static int __ttm_vt_unlock(struct ttm_lock *lock)
|
|
+{
|
|
+ int ret = 0;
|
|
+
|
|
+ spin_lock(&lock->lock);
|
|
+ if (unlikely(!(lock->flags & TTM_VT_LOCK)))
|
|
+ ret = -EINVAL;
|
|
+ lock->flags &= ~TTM_VT_LOCK;
|
|
+ wake_up_all(&lock->queue);
|
|
+ spin_unlock(&lock->lock);
|
|
+ printk(KERN_INFO TTM_PFX "vt unlock.\n");
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void ttm_vt_lock_remove(struct ttm_base_object **p_base)
|
|
+{
|
|
+ struct ttm_base_object *base = *p_base;
|
|
+ struct ttm_lock *lock = container_of(base, struct ttm_lock, base);
|
|
+ int ret;
|
|
+
|
|
+ *p_base = NULL;
|
|
+ ret = __ttm_vt_unlock(lock);
|
|
+ BUG_ON(ret != 0);
|
|
+}
|
|
+
|
|
+static bool __ttm_vt_lock(struct ttm_lock *lock)
|
|
+{
|
|
+ bool locked = false;
|
|
+
|
|
+ spin_lock(&lock->lock);
|
|
+ if (lock->rw == 0) {
|
|
+ lock->flags &= ~TTM_VT_LOCK_PENDING;
|
|
+ lock->flags |= TTM_VT_LOCK;
|
|
+ locked = true;
|
|
+ } else {
|
|
+ lock->flags |= TTM_VT_LOCK_PENDING;
|
|
+ }
|
|
+ spin_unlock(&lock->lock);
|
|
+ return locked;
|
|
+}
|
|
+
|
|
+int ttm_vt_lock(struct ttm_lock *lock,
|
|
+ bool interruptible,
|
|
+ struct ttm_object_file *tfile)
|
|
+{
|
|
+ int ret = 0;
|
|
+
|
|
+ if (interruptible) {
|
|
+ ret = wait_event_interruptible(lock->queue,
|
|
+ __ttm_vt_lock(lock));
|
|
+ if (unlikely(ret != 0)) {
|
|
+ spin_lock(&lock->lock);
|
|
+ lock->flags &= ~TTM_VT_LOCK_PENDING;
|
|
+ wake_up_all(&lock->queue);
|
|
+ spin_unlock(&lock->lock);
|
|
+ return ret;
|
|
+ }
|
|
+ } else
|
|
+ wait_event(lock->queue, __ttm_vt_lock(lock));
|
|
+
|
|
+ /*
|
|
+ * Add a base-object, the destructor of which will
|
|
+ * make sure the lock is released if the client dies
|
|
+ * while holding it.
|
|
+ */
|
|
+
|
|
+ ret = ttm_base_object_init(tfile, &lock->base, false,
|
|
+ ttm_lock_type, &ttm_vt_lock_remove, NULL);
|
|
+ if (ret)
|
|
+ (void)__ttm_vt_unlock(lock);
|
|
+ else {
|
|
+ lock->vt_holder = tfile;
|
|
+ printk(KERN_INFO TTM_PFX "vt lock.\n");
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+EXPORT_SYMBOL(ttm_vt_lock);
|
|
+
|
|
+int ttm_vt_unlock(struct ttm_lock *lock)
|
|
+{
|
|
+ return ttm_ref_object_base_unref(lock->vt_holder,
|
|
+ lock->base.hash.key, TTM_REF_USAGE);
|
|
+}
|
|
+EXPORT_SYMBOL(ttm_vt_unlock);
|
|
+
|
|
+void ttm_suspend_unlock(struct ttm_lock *lock)
|
|
+{
|
|
+ spin_lock(&lock->lock);
|
|
+ lock->flags &= ~TTM_SUSPEND_LOCK;
|
|
+ wake_up_all(&lock->queue);
|
|
+ spin_unlock(&lock->lock);
|
|
+}
|
|
+EXPORT_SYMBOL(ttm_suspend_unlock);
|
|
+
|
|
+static bool __ttm_suspend_lock(struct ttm_lock *lock)
|
|
+{
|
|
+ bool locked = false;
|
|
+
|
|
+ spin_lock(&lock->lock);
|
|
+ if (lock->rw == 0) {
|
|
+ lock->flags &= ~TTM_SUSPEND_LOCK_PENDING;
|
|
+ lock->flags |= TTM_SUSPEND_LOCK;
|
|
+ locked = true;
|
|
+ } else {
|
|
+ lock->flags |= TTM_SUSPEND_LOCK_PENDING;
|
|
+ }
|
|
+ spin_unlock(&lock->lock);
|
|
+ return locked;
|
|
+}
|
|
+
|
|
+void ttm_suspend_lock(struct ttm_lock *lock)
|
|
+{
|
|
+ wait_event(lock->queue, __ttm_suspend_lock(lock));
|
|
+}
|
|
+EXPORT_SYMBOL(ttm_suspend_lock);
|
|
diff --git a/drivers/gpu/drm/ttm/ttm_memory.c b/drivers/gpu/drm/ttm/ttm_memory.c
|
|
index 072c281..f5245c0 100644
|
|
--- a/drivers/gpu/drm/ttm/ttm_memory.c
|
|
+++ b/drivers/gpu/drm/ttm/ttm_memory.c
|
|
@@ -274,16 +274,17 @@ static int ttm_mem_init_kernel_zone(struct ttm_mem_global *glob,
|
|
static int ttm_mem_init_highmem_zone(struct ttm_mem_global *glob,
|
|
const struct sysinfo *si)
|
|
{
|
|
- struct ttm_mem_zone *zone = kzalloc(sizeof(*zone), GFP_KERNEL);
|
|
+ struct ttm_mem_zone *zone;
|
|
uint64_t mem;
|
|
int ret;
|
|
|
|
- if (unlikely(!zone))
|
|
- return -ENOMEM;
|
|
-
|
|
if (si->totalhigh == 0)
|
|
return 0;
|
|
|
|
+ zone = kzalloc(sizeof(*zone), GFP_KERNEL);
|
|
+ if (unlikely(!zone))
|
|
+ return -ENOMEM;
|
|
+
|
|
mem = si->totalram;
|
|
mem *= si->mem_unit;
|
|
|
|
@@ -322,8 +323,10 @@ static int ttm_mem_init_dma32_zone(struct ttm_mem_global *glob,
|
|
* No special dma32 zone needed.
|
|
*/
|
|
|
|
- if (mem <= ((uint64_t) 1ULL << 32))
|
|
+ if (mem <= ((uint64_t) 1ULL << 32)) {
|
|
+ kfree(zone);
|
|
return 0;
|
|
+ }
|
|
|
|
/*
|
|
* Limit max dma32 memory to 4GB for now
|
|
@@ -460,6 +463,7 @@ void ttm_mem_global_free(struct ttm_mem_global *glob,
|
|
{
|
|
return ttm_mem_global_free_zone(glob, NULL, amount);
|
|
}
|
|
+EXPORT_SYMBOL(ttm_mem_global_free);
|
|
|
|
static int ttm_mem_global_reserve(struct ttm_mem_global *glob,
|
|
struct ttm_mem_zone *single_zone,
|
|
@@ -533,6 +537,7 @@ int ttm_mem_global_alloc(struct ttm_mem_global *glob, uint64_t memory,
|
|
return ttm_mem_global_alloc_zone(glob, NULL, memory, no_wait,
|
|
interruptible);
|
|
}
|
|
+EXPORT_SYMBOL(ttm_mem_global_alloc);
|
|
|
|
int ttm_mem_global_alloc_page(struct ttm_mem_global *glob,
|
|
struct page *page,
|
|
@@ -588,3 +593,4 @@ size_t ttm_round_pot(size_t size)
|
|
}
|
|
return 0;
|
|
}
|
|
+EXPORT_SYMBOL(ttm_round_pot);
|
|
diff --git a/drivers/gpu/drm/ttm/ttm_object.c b/drivers/gpu/drm/ttm/ttm_object.c
|
|
new file mode 100644
|
|
index 0000000..75e9d6f
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/ttm/ttm_object.c
|
|
@@ -0,0 +1,452 @@
|
|
+/**************************************************************************
|
|
+ *
|
|
+ * Copyright (c) 2009 VMware, Inc., Palo Alto, CA., USA
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sub license, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial portions
|
|
+ * of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ **************************************************************************/
|
|
+/*
|
|
+ * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
|
|
+ */
|
|
+/** @file ttm_ref_object.c
|
|
+ *
|
|
+ * Base- and reference object implementation for the various
|
|
+ * ttm objects. Implements reference counting, minimal security checks
|
|
+ * and release on file close.
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * struct ttm_object_file
|
|
+ *
|
|
+ * @tdev: Pointer to the ttm_object_device.
|
|
+ *
|
|
+ * @lock: Lock that protects the ref_list list and the
|
|
+ * ref_hash hash tables.
|
|
+ *
|
|
+ * @ref_list: List of ttm_ref_objects to be destroyed at
|
|
+ * file release.
|
|
+ *
|
|
+ * @ref_hash: Hash tables of ref objects, one per ttm_ref_type,
|
|
+ * for fast lookup of ref objects given a base object.
|
|
+ */
|
|
+
|
|
+#include "ttm/ttm_object.h"
|
|
+#include "ttm/ttm_module.h"
|
|
+#include <linux/list.h>
|
|
+#include <linux/spinlock.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/module.h>
|
|
+#include <asm/atomic.h>
|
|
+
|
|
+struct ttm_object_file {
|
|
+ struct ttm_object_device *tdev;
|
|
+ rwlock_t lock;
|
|
+ struct list_head ref_list;
|
|
+ struct drm_open_hash ref_hash[TTM_REF_NUM];
|
|
+ struct kref refcount;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct ttm_object_device
|
|
+ *
|
|
+ * @object_lock: lock that protects the object_hash hash table.
|
|
+ *
|
|
+ * @object_hash: hash table for fast lookup of object global names.
|
|
+ *
|
|
+ * @object_count: Per device object count.
|
|
+ *
|
|
+ * This is the per-device data structure needed for ttm object management.
|
|
+ */
|
|
+
|
|
+struct ttm_object_device {
|
|
+ rwlock_t object_lock;
|
|
+ struct drm_open_hash object_hash;
|
|
+ atomic_t object_count;
|
|
+ struct ttm_mem_global *mem_glob;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct ttm_ref_object
|
|
+ *
|
|
+ * @hash: Hash entry for the per-file object reference hash.
|
|
+ *
|
|
+ * @head: List entry for the per-file list of ref-objects.
|
|
+ *
|
|
+ * @kref: Ref count.
|
|
+ *
|
|
+ * @obj: Base object this ref object is referencing.
|
|
+ *
|
|
+ * @ref_type: Type of ref object.
|
|
+ *
|
|
+ * This is similar to an idr object, but it also has a hash table entry
|
|
+ * that allows lookup with a pointer to the referenced object as a key. In
|
|
+ * that way, one can easily detect whether a base object is referenced by
|
|
+ * a particular ttm_object_file. It also carries a ref count to avoid creating
|
|
+ * multiple ref objects if a ttm_object_file references the same base
|
|
+ * object more than once.
|
|
+ */
|
|
+
|
|
+struct ttm_ref_object {
|
|
+ struct drm_hash_item hash;
|
|
+ struct list_head head;
|
|
+ struct kref kref;
|
|
+ enum ttm_ref_type ref_type;
|
|
+ struct ttm_base_object *obj;
|
|
+ struct ttm_object_file *tfile;
|
|
+};
|
|
+
|
|
+static inline struct ttm_object_file *
|
|
+ttm_object_file_ref(struct ttm_object_file *tfile)
|
|
+{
|
|
+ kref_get(&tfile->refcount);
|
|
+ return tfile;
|
|
+}
|
|
+
|
|
+static void ttm_object_file_destroy(struct kref *kref)
|
|
+{
|
|
+ struct ttm_object_file *tfile =
|
|
+ container_of(kref, struct ttm_object_file, refcount);
|
|
+
|
|
+ kfree(tfile);
|
|
+}
|
|
+
|
|
+
|
|
+static inline void ttm_object_file_unref(struct ttm_object_file **p_tfile)
|
|
+{
|
|
+ struct ttm_object_file *tfile = *p_tfile;
|
|
+
|
|
+ *p_tfile = NULL;
|
|
+ kref_put(&tfile->refcount, ttm_object_file_destroy);
|
|
+}
|
|
+
|
|
+
|
|
+int ttm_base_object_init(struct ttm_object_file *tfile,
|
|
+ struct ttm_base_object *base,
|
|
+ bool shareable,
|
|
+ enum ttm_object_type object_type,
|
|
+ void (*refcount_release) (struct ttm_base_object **),
|
|
+ void (*ref_obj_release) (struct ttm_base_object *,
|
|
+ enum ttm_ref_type ref_type))
|
|
+{
|
|
+ struct ttm_object_device *tdev = tfile->tdev;
|
|
+ int ret;
|
|
+
|
|
+ base->shareable = shareable;
|
|
+ base->tfile = ttm_object_file_ref(tfile);
|
|
+ base->refcount_release = refcount_release;
|
|
+ base->ref_obj_release = ref_obj_release;
|
|
+ base->object_type = object_type;
|
|
+ write_lock(&tdev->object_lock);
|
|
+ kref_init(&base->refcount);
|
|
+ ret = drm_ht_just_insert_please(&tdev->object_hash,
|
|
+ &base->hash,
|
|
+ (unsigned long)base, 31, 0, 0);
|
|
+ write_unlock(&tdev->object_lock);
|
|
+ if (unlikely(ret != 0))
|
|
+ goto out_err0;
|
|
+
|
|
+ ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL);
|
|
+ if (unlikely(ret != 0))
|
|
+ goto out_err1;
|
|
+
|
|
+ ttm_base_object_unref(&base);
|
|
+
|
|
+ return 0;
|
|
+out_err1:
|
|
+ (void)drm_ht_remove_item(&tdev->object_hash, &base->hash);
|
|
+out_err0:
|
|
+ return ret;
|
|
+}
|
|
+EXPORT_SYMBOL(ttm_base_object_init);
|
|
+
|
|
+static void ttm_release_base(struct kref *kref)
|
|
+{
|
|
+ struct ttm_base_object *base =
|
|
+ container_of(kref, struct ttm_base_object, refcount);
|
|
+ struct ttm_object_device *tdev = base->tfile->tdev;
|
|
+
|
|
+ (void)drm_ht_remove_item(&tdev->object_hash, &base->hash);
|
|
+ write_unlock(&tdev->object_lock);
|
|
+ if (base->refcount_release) {
|
|
+ ttm_object_file_unref(&base->tfile);
|
|
+ base->refcount_release(&base);
|
|
+ }
|
|
+ write_lock(&tdev->object_lock);
|
|
+}
|
|
+
|
|
+void ttm_base_object_unref(struct ttm_base_object **p_base)
|
|
+{
|
|
+ struct ttm_base_object *base = *p_base;
|
|
+ struct ttm_object_device *tdev = base->tfile->tdev;
|
|
+
|
|
+ *p_base = NULL;
|
|
+
|
|
+ /*
|
|
+ * Need to take the lock here to avoid racing with
|
|
+ * users trying to look up the object.
|
|
+ */
|
|
+
|
|
+ write_lock(&tdev->object_lock);
|
|
+ (void)kref_put(&base->refcount, &ttm_release_base);
|
|
+ write_unlock(&tdev->object_lock);
|
|
+}
|
|
+EXPORT_SYMBOL(ttm_base_object_unref);
|
|
+
|
|
+struct ttm_base_object *ttm_base_object_lookup(struct ttm_object_file *tfile,
|
|
+ uint32_t key)
|
|
+{
|
|
+ struct ttm_object_device *tdev = tfile->tdev;
|
|
+ struct ttm_base_object *base;
|
|
+ struct drm_hash_item *hash;
|
|
+ int ret;
|
|
+
|
|
+ read_lock(&tdev->object_lock);
|
|
+ ret = drm_ht_find_item(&tdev->object_hash, key, &hash);
|
|
+
|
|
+ if (likely(ret == 0)) {
|
|
+ base = drm_hash_entry(hash, struct ttm_base_object, hash);
|
|
+ kref_get(&base->refcount);
|
|
+ }
|
|
+ read_unlock(&tdev->object_lock);
|
|
+
|
|
+ if (unlikely(ret != 0))
|
|
+ return NULL;
|
|
+
|
|
+ if (tfile != base->tfile && !base->shareable) {
|
|
+ printk(KERN_ERR TTM_PFX
|
|
+ "Attempted access of non-shareable object.\n");
|
|
+ ttm_base_object_unref(&base);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ return base;
|
|
+}
|
|
+EXPORT_SYMBOL(ttm_base_object_lookup);
|
|
+
|
|
+int ttm_ref_object_add(struct ttm_object_file *tfile,
|
|
+ struct ttm_base_object *base,
|
|
+ enum ttm_ref_type ref_type, bool *existed)
|
|
+{
|
|
+ struct drm_open_hash *ht = &tfile->ref_hash[ref_type];
|
|
+ struct ttm_ref_object *ref;
|
|
+ struct drm_hash_item *hash;
|
|
+ struct ttm_mem_global *mem_glob = tfile->tdev->mem_glob;
|
|
+ int ret = -EINVAL;
|
|
+
|
|
+ if (existed != NULL)
|
|
+ *existed = true;
|
|
+
|
|
+ while (ret == -EINVAL) {
|
|
+ read_lock(&tfile->lock);
|
|
+ ret = drm_ht_find_item(ht, base->hash.key, &hash);
|
|
+
|
|
+ if (ret == 0) {
|
|
+ ref = drm_hash_entry(hash, struct ttm_ref_object, hash);
|
|
+ kref_get(&ref->kref);
|
|
+ read_unlock(&tfile->lock);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ read_unlock(&tfile->lock);
|
|
+ ret = ttm_mem_global_alloc(mem_glob, sizeof(*ref),
|
|
+ false, false);
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+ ref = kmalloc(sizeof(*ref), GFP_KERNEL);
|
|
+ if (unlikely(ref == NULL)) {
|
|
+ ttm_mem_global_free(mem_glob, sizeof(*ref));
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ ref->hash.key = base->hash.key;
|
|
+ ref->obj = base;
|
|
+ ref->tfile = tfile;
|
|
+ ref->ref_type = ref_type;
|
|
+ kref_init(&ref->kref);
|
|
+
|
|
+ write_lock(&tfile->lock);
|
|
+ ret = drm_ht_insert_item(ht, &ref->hash);
|
|
+
|
|
+ if (likely(ret == 0)) {
|
|
+ list_add_tail(&ref->head, &tfile->ref_list);
|
|
+ kref_get(&base->refcount);
|
|
+ write_unlock(&tfile->lock);
|
|
+ if (existed != NULL)
|
|
+ *existed = false;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ write_unlock(&tfile->lock);
|
|
+ BUG_ON(ret != -EINVAL);
|
|
+
|
|
+ ttm_mem_global_free(mem_glob, sizeof(*ref));
|
|
+ kfree(ref);
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+EXPORT_SYMBOL(ttm_ref_object_add);
|
|
+
|
|
+static void ttm_ref_object_release(struct kref *kref)
|
|
+{
|
|
+ struct ttm_ref_object *ref =
|
|
+ container_of(kref, struct ttm_ref_object, kref);
|
|
+ struct ttm_base_object *base = ref->obj;
|
|
+ struct ttm_object_file *tfile = ref->tfile;
|
|
+ struct drm_open_hash *ht;
|
|
+ struct ttm_mem_global *mem_glob = tfile->tdev->mem_glob;
|
|
+
|
|
+ ht = &tfile->ref_hash[ref->ref_type];
|
|
+ (void)drm_ht_remove_item(ht, &ref->hash);
|
|
+ list_del(&ref->head);
|
|
+ write_unlock(&tfile->lock);
|
|
+
|
|
+ if (ref->ref_type != TTM_REF_USAGE && base->ref_obj_release)
|
|
+ base->ref_obj_release(base, ref->ref_type);
|
|
+
|
|
+ ttm_base_object_unref(&ref->obj);
|
|
+ ttm_mem_global_free(mem_glob, sizeof(*ref));
|
|
+ kfree(ref);
|
|
+ write_lock(&tfile->lock);
|
|
+}
|
|
+
|
|
+int ttm_ref_object_base_unref(struct ttm_object_file *tfile,
|
|
+ unsigned long key, enum ttm_ref_type ref_type)
|
|
+{
|
|
+ struct drm_open_hash *ht = &tfile->ref_hash[ref_type];
|
|
+ struct ttm_ref_object *ref;
|
|
+ struct drm_hash_item *hash;
|
|
+ int ret;
|
|
+
|
|
+ write_lock(&tfile->lock);
|
|
+ ret = drm_ht_find_item(ht, key, &hash);
|
|
+ if (unlikely(ret != 0)) {
|
|
+ write_unlock(&tfile->lock);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ ref = drm_hash_entry(hash, struct ttm_ref_object, hash);
|
|
+ kref_put(&ref->kref, ttm_ref_object_release);
|
|
+ write_unlock(&tfile->lock);
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL(ttm_ref_object_base_unref);
|
|
+
|
|
+void ttm_object_file_release(struct ttm_object_file **p_tfile)
|
|
+{
|
|
+ struct ttm_ref_object *ref;
|
|
+ struct list_head *list;
|
|
+ unsigned int i;
|
|
+ struct ttm_object_file *tfile = *p_tfile;
|
|
+
|
|
+ *p_tfile = NULL;
|
|
+ write_lock(&tfile->lock);
|
|
+
|
|
+ /*
|
|
+ * Since we release the lock within the loop, we have to
|
|
+ * restart it from the beginning each time.
|
|
+ */
|
|
+
|
|
+ while (!list_empty(&tfile->ref_list)) {
|
|
+ list = tfile->ref_list.next;
|
|
+ ref = list_entry(list, struct ttm_ref_object, head);
|
|
+ ttm_ref_object_release(&ref->kref);
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < TTM_REF_NUM; ++i)
|
|
+ drm_ht_remove(&tfile->ref_hash[i]);
|
|
+
|
|
+ write_unlock(&tfile->lock);
|
|
+ ttm_object_file_unref(&tfile);
|
|
+}
|
|
+EXPORT_SYMBOL(ttm_object_file_release);
|
|
+
|
|
+struct ttm_object_file *ttm_object_file_init(struct ttm_object_device *tdev,
|
|
+ unsigned int hash_order)
|
|
+{
|
|
+ struct ttm_object_file *tfile = kmalloc(sizeof(*tfile), GFP_KERNEL);
|
|
+ unsigned int i;
|
|
+ unsigned int j = 0;
|
|
+ int ret;
|
|
+
|
|
+ if (unlikely(tfile == NULL))
|
|
+ return NULL;
|
|
+
|
|
+ rwlock_init(&tfile->lock);
|
|
+ tfile->tdev = tdev;
|
|
+ kref_init(&tfile->refcount);
|
|
+ INIT_LIST_HEAD(&tfile->ref_list);
|
|
+
|
|
+ for (i = 0; i < TTM_REF_NUM; ++i) {
|
|
+ ret = drm_ht_create(&tfile->ref_hash[i], hash_order);
|
|
+ if (ret) {
|
|
+ j = i;
|
|
+ goto out_err;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return tfile;
|
|
+out_err:
|
|
+ for (i = 0; i < j; ++i)
|
|
+ drm_ht_remove(&tfile->ref_hash[i]);
|
|
+
|
|
+ kfree(tfile);
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+EXPORT_SYMBOL(ttm_object_file_init);
|
|
+
|
|
+struct ttm_object_device *ttm_object_device_init(struct ttm_mem_global
|
|
+ *mem_glob,
|
|
+ unsigned int hash_order)
|
|
+{
|
|
+ struct ttm_object_device *tdev = kmalloc(sizeof(*tdev), GFP_KERNEL);
|
|
+ int ret;
|
|
+
|
|
+ if (unlikely(tdev == NULL))
|
|
+ return NULL;
|
|
+
|
|
+ tdev->mem_glob = mem_glob;
|
|
+ rwlock_init(&tdev->object_lock);
|
|
+ atomic_set(&tdev->object_count, 0);
|
|
+ ret = drm_ht_create(&tdev->object_hash, hash_order);
|
|
+
|
|
+ if (likely(ret == 0))
|
|
+ return tdev;
|
|
+
|
|
+ kfree(tdev);
|
|
+ return NULL;
|
|
+}
|
|
+EXPORT_SYMBOL(ttm_object_device_init);
|
|
+
|
|
+void ttm_object_device_release(struct ttm_object_device **p_tdev)
|
|
+{
|
|
+ struct ttm_object_device *tdev = *p_tdev;
|
|
+
|
|
+ *p_tdev = NULL;
|
|
+
|
|
+ write_lock(&tdev->object_lock);
|
|
+ drm_ht_remove(&tdev->object_hash);
|
|
+ write_unlock(&tdev->object_lock);
|
|
+
|
|
+ kfree(tdev);
|
|
+}
|
|
+EXPORT_SYMBOL(ttm_object_device_release);
|
|
diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c
|
|
index 7bcb89f..e2123af 100644
|
|
--- a/drivers/gpu/drm/ttm/ttm_tt.c
|
|
+++ b/drivers/gpu/drm/ttm/ttm_tt.c
|
|
@@ -192,22 +192,32 @@ int ttm_tt_populate(struct ttm_tt *ttm)
|
|
ttm->state = tt_unbound;
|
|
return 0;
|
|
}
|
|
+EXPORT_SYMBOL(ttm_tt_populate);
|
|
|
|
#ifdef CONFIG_X86
|
|
static inline int ttm_tt_set_page_caching(struct page *p,
|
|
enum ttm_caching_state c_state)
|
|
{
|
|
+ int ret = 0;
|
|
+
|
|
if (PageHighMem(p))
|
|
return 0;
|
|
|
|
- switch (c_state) {
|
|
- case tt_cached:
|
|
- return set_pages_wb(p, 1);
|
|
- case tt_wc:
|
|
- return set_memory_wc((unsigned long) page_address(p), 1);
|
|
- default:
|
|
- return set_pages_uc(p, 1);
|
|
+ if (get_page_memtype(p) != -1) {
|
|
+ /* p isn't in the default caching state, set it to
|
|
+ * writeback first to free its current memtype. */
|
|
+
|
|
+ ret = set_pages_wb(p, 1);
|
|
+ if (ret)
|
|
+ return ret;
|
|
}
|
|
+
|
|
+ if (c_state == tt_wc)
|
|
+ ret = set_memory_wc((unsigned long) page_address(p), 1);
|
|
+ else if (c_state == tt_uncached)
|
|
+ ret = set_pages_uc(p, 1);
|
|
+
|
|
+ return ret;
|
|
}
|
|
#else /* CONFIG_X86 */
|
|
static inline int ttm_tt_set_page_caching(struct page *p,
|
|
diff --git a/drivers/gpu/drm/via/via_drv.c b/drivers/gpu/drm/via/via_drv.c
|
|
index bc2f518..7a1b210 100644
|
|
--- a/drivers/gpu/drm/via/via_drv.c
|
|
+++ b/drivers/gpu/drm/via/via_drv.c
|
|
@@ -58,7 +58,7 @@ static struct drm_driver driver = {
|
|
.owner = THIS_MODULE,
|
|
.open = drm_open,
|
|
.release = drm_release,
|
|
- .ioctl = drm_ioctl,
|
|
+ .unlocked_ioctl = drm_ioctl,
|
|
.mmap = drm_mmap,
|
|
.poll = drm_poll,
|
|
.fasync = drm_fasync,
|
|
diff --git a/drivers/gpu/drm/vmwgfx/Kconfig b/drivers/gpu/drm/vmwgfx/Kconfig
|
|
new file mode 100644
|
|
index 0000000..f20b8bc
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/vmwgfx/Kconfig
|
|
@@ -0,0 +1,13 @@
|
|
+config DRM_VMWGFX
|
|
+ tristate "DRM driver for VMware Virtual GPU"
|
|
+ depends on DRM && PCI
|
|
+ select FB_DEFERRED_IO
|
|
+ select FB_CFB_FILLRECT
|
|
+ select FB_CFB_COPYAREA
|
|
+ select FB_CFB_IMAGEBLIT
|
|
+ select DRM_TTM
|
|
+ help
|
|
+ KMS enabled DRM driver for SVGA2 virtual hardware.
|
|
+
|
|
+ If unsure say n. The compiled module will be
|
|
+ called vmwgfx.ko
|
|
diff --git a/drivers/gpu/drm/vmwgfx/Makefile b/drivers/gpu/drm/vmwgfx/Makefile
|
|
new file mode 100644
|
|
index 0000000..1a3cb68
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/vmwgfx/Makefile
|
|
@@ -0,0 +1,9 @@
|
|
+
|
|
+ccflags-y := -Iinclude/drm
|
|
+
|
|
+vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \
|
|
+ vmwgfx_fb.o vmwgfx_ioctl.o vmwgfx_resource.o vmwgfx_buffer.o \
|
|
+ vmwgfx_fifo.o vmwgfx_irq.o vmwgfx_ldu.o vmwgfx_ttm_glue.o \
|
|
+ vmwgfx_overlay.o
|
|
+
|
|
+obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o
|
|
diff --git a/drivers/gpu/drm/vmwgfx/svga3d_reg.h b/drivers/gpu/drm/vmwgfx/svga3d_reg.h
|
|
new file mode 100644
|
|
index 0000000..77cb453
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/vmwgfx/svga3d_reg.h
|
|
@@ -0,0 +1,1793 @@
|
|
+/**********************************************************
|
|
+ * Copyright 1998-2009 VMware, Inc. All rights reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person
|
|
+ * obtaining a copy of this software and associated documentation
|
|
+ * files (the "Software"), to deal in the Software without
|
|
+ * restriction, including without limitation the rights to use, copy,
|
|
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
+ * of the Software, and to permit persons to whom the Software is
|
|
+ * furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice shall be
|
|
+ * included in all copies or substantial portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
+ * SOFTWARE.
|
|
+ *
|
|
+ **********************************************************/
|
|
+
|
|
+/*
|
|
+ * svga3d_reg.h --
|
|
+ *
|
|
+ * SVGA 3D hardware definitions
|
|
+ */
|
|
+
|
|
+#ifndef _SVGA3D_REG_H_
|
|
+#define _SVGA3D_REG_H_
|
|
+
|
|
+#include "svga_reg.h"
|
|
+
|
|
+
|
|
+/*
|
|
+ * 3D Hardware Version
|
|
+ *
|
|
+ * The hardware version is stored in the SVGA_FIFO_3D_HWVERSION fifo
|
|
+ * register. Is set by the host and read by the guest. This lets
|
|
+ * us make new guest drivers which are backwards-compatible with old
|
|
+ * SVGA hardware revisions. It does not let us support old guest
|
|
+ * drivers. Good enough for now.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#define SVGA3D_MAKE_HWVERSION(major, minor) (((major) << 16) | ((minor) & 0xFF))
|
|
+#define SVGA3D_MAJOR_HWVERSION(version) ((version) >> 16)
|
|
+#define SVGA3D_MINOR_HWVERSION(version) ((version) & 0xFF)
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_HWVERSION_WS5_RC1 = SVGA3D_MAKE_HWVERSION(0, 1),
|
|
+ SVGA3D_HWVERSION_WS5_RC2 = SVGA3D_MAKE_HWVERSION(0, 2),
|
|
+ SVGA3D_HWVERSION_WS51_RC1 = SVGA3D_MAKE_HWVERSION(0, 3),
|
|
+ SVGA3D_HWVERSION_WS6_B1 = SVGA3D_MAKE_HWVERSION(1, 1),
|
|
+ SVGA3D_HWVERSION_FUSION_11 = SVGA3D_MAKE_HWVERSION(1, 4),
|
|
+ SVGA3D_HWVERSION_WS65_B1 = SVGA3D_MAKE_HWVERSION(2, 0),
|
|
+ SVGA3D_HWVERSION_CURRENT = SVGA3D_HWVERSION_WS65_B1,
|
|
+} SVGA3dHardwareVersion;
|
|
+
|
|
+/*
|
|
+ * Generic Types
|
|
+ */
|
|
+
|
|
+typedef uint32 SVGA3dBool; /* 32-bit Bool definition */
|
|
+#define SVGA3D_NUM_CLIPPLANES 6
|
|
+#define SVGA3D_MAX_SIMULTANEOUS_RENDER_TARGETS 8
|
|
+
|
|
+
|
|
+/*
|
|
+ * Surface formats.
|
|
+ *
|
|
+ * If you modify this list, be sure to keep GLUtil.c in sync. It
|
|
+ * includes the internal format definition of each surface in
|
|
+ * GLUtil_ConvertSurfaceFormat, and it contains a table of
|
|
+ * human-readable names in GLUtil_GetFormatName.
|
|
+ */
|
|
+
|
|
+typedef enum SVGA3dSurfaceFormat {
|
|
+ SVGA3D_FORMAT_INVALID = 0,
|
|
+
|
|
+ SVGA3D_X8R8G8B8 = 1,
|
|
+ SVGA3D_A8R8G8B8 = 2,
|
|
+
|
|
+ SVGA3D_R5G6B5 = 3,
|
|
+ SVGA3D_X1R5G5B5 = 4,
|
|
+ SVGA3D_A1R5G5B5 = 5,
|
|
+ SVGA3D_A4R4G4B4 = 6,
|
|
+
|
|
+ SVGA3D_Z_D32 = 7,
|
|
+ SVGA3D_Z_D16 = 8,
|
|
+ SVGA3D_Z_D24S8 = 9,
|
|
+ SVGA3D_Z_D15S1 = 10,
|
|
+
|
|
+ SVGA3D_LUMINANCE8 = 11,
|
|
+ SVGA3D_LUMINANCE4_ALPHA4 = 12,
|
|
+ SVGA3D_LUMINANCE16 = 13,
|
|
+ SVGA3D_LUMINANCE8_ALPHA8 = 14,
|
|
+
|
|
+ SVGA3D_DXT1 = 15,
|
|
+ SVGA3D_DXT2 = 16,
|
|
+ SVGA3D_DXT3 = 17,
|
|
+ SVGA3D_DXT4 = 18,
|
|
+ SVGA3D_DXT5 = 19,
|
|
+
|
|
+ SVGA3D_BUMPU8V8 = 20,
|
|
+ SVGA3D_BUMPL6V5U5 = 21,
|
|
+ SVGA3D_BUMPX8L8V8U8 = 22,
|
|
+ SVGA3D_BUMPL8V8U8 = 23,
|
|
+
|
|
+ SVGA3D_ARGB_S10E5 = 24, /* 16-bit floating-point ARGB */
|
|
+ SVGA3D_ARGB_S23E8 = 25, /* 32-bit floating-point ARGB */
|
|
+
|
|
+ SVGA3D_A2R10G10B10 = 26,
|
|
+
|
|
+ /* signed formats */
|
|
+ SVGA3D_V8U8 = 27,
|
|
+ SVGA3D_Q8W8V8U8 = 28,
|
|
+ SVGA3D_CxV8U8 = 29,
|
|
+
|
|
+ /* mixed formats */
|
|
+ SVGA3D_X8L8V8U8 = 30,
|
|
+ SVGA3D_A2W10V10U10 = 31,
|
|
+
|
|
+ SVGA3D_ALPHA8 = 32,
|
|
+
|
|
+ /* Single- and dual-component floating point formats */
|
|
+ SVGA3D_R_S10E5 = 33,
|
|
+ SVGA3D_R_S23E8 = 34,
|
|
+ SVGA3D_RG_S10E5 = 35,
|
|
+ SVGA3D_RG_S23E8 = 36,
|
|
+
|
|
+ /*
|
|
+ * Any surface can be used as a buffer object, but SVGA3D_BUFFER is
|
|
+ * the most efficient format to use when creating new surfaces
|
|
+ * expressly for index or vertex data.
|
|
+ */
|
|
+ SVGA3D_BUFFER = 37,
|
|
+
|
|
+ SVGA3D_Z_D24X8 = 38,
|
|
+
|
|
+ SVGA3D_V16U16 = 39,
|
|
+
|
|
+ SVGA3D_G16R16 = 40,
|
|
+ SVGA3D_A16B16G16R16 = 41,
|
|
+
|
|
+ /* Packed Video formats */
|
|
+ SVGA3D_UYVY = 42,
|
|
+ SVGA3D_YUY2 = 43,
|
|
+
|
|
+ SVGA3D_FORMAT_MAX
|
|
+} SVGA3dSurfaceFormat;
|
|
+
|
|
+typedef uint32 SVGA3dColor; /* a, r, g, b */
|
|
+
|
|
+/*
|
|
+ * These match the D3DFORMAT_OP definitions used by Direct3D. We need
|
|
+ * them so that we can query the host for what the supported surface
|
|
+ * operations are (when we're using the D3D backend, in particular),
|
|
+ * and so we can send those operations to the guest.
|
|
+ */
|
|
+typedef enum {
|
|
+ SVGA3DFORMAT_OP_TEXTURE = 0x00000001,
|
|
+ SVGA3DFORMAT_OP_VOLUMETEXTURE = 0x00000002,
|
|
+ SVGA3DFORMAT_OP_CUBETEXTURE = 0x00000004,
|
|
+ SVGA3DFORMAT_OP_OFFSCREEN_RENDERTARGET = 0x00000008,
|
|
+ SVGA3DFORMAT_OP_SAME_FORMAT_RENDERTARGET = 0x00000010,
|
|
+ SVGA3DFORMAT_OP_ZSTENCIL = 0x00000040,
|
|
+ SVGA3DFORMAT_OP_ZSTENCIL_WITH_ARBITRARY_COLOR_DEPTH = 0x00000080,
|
|
+
|
|
+/*
|
|
+ * This format can be used as a render target if the current display mode
|
|
+ * is the same depth if the alpha channel is ignored. e.g. if the device
|
|
+ * can render to A8R8G8B8 when the display mode is X8R8G8B8, then the
|
|
+ * format op list entry for A8R8G8B8 should have this cap.
|
|
+ */
|
|
+ SVGA3DFORMAT_OP_SAME_FORMAT_UP_TO_ALPHA_RENDERTARGET = 0x00000100,
|
|
+
|
|
+/*
|
|
+ * This format contains DirectDraw support (including Flip). This flag
|
|
+ * should not to be set on alpha formats.
|
|
+ */
|
|
+ SVGA3DFORMAT_OP_DISPLAYMODE = 0x00000400,
|
|
+
|
|
+/*
|
|
+ * The rasterizer can support some level of Direct3D support in this format
|
|
+ * and implies that the driver can create a Context in this mode (for some
|
|
+ * render target format). When this flag is set, the SVGA3DFORMAT_OP_DISPLAYMODE
|
|
+ * flag must also be set.
|
|
+ */
|
|
+ SVGA3DFORMAT_OP_3DACCELERATION = 0x00000800,
|
|
+
|
|
+/*
|
|
+ * This is set for a private format when the driver has put the bpp in
|
|
+ * the structure.
|
|
+ */
|
|
+ SVGA3DFORMAT_OP_PIXELSIZE = 0x00001000,
|
|
+
|
|
+/*
|
|
+ * Indicates that this format can be converted to any RGB format for which
|
|
+ * SVGA3DFORMAT_OP_MEMBEROFGROUP_ARGB is specified
|
|
+ */
|
|
+ SVGA3DFORMAT_OP_CONVERT_TO_ARGB = 0x00002000,
|
|
+
|
|
+/*
|
|
+ * Indicates that this format can be used to create offscreen plain surfaces.
|
|
+ */
|
|
+ SVGA3DFORMAT_OP_OFFSCREENPLAIN = 0x00004000,
|
|
+
|
|
+/*
|
|
+ * Indicated that this format can be read as an SRGB texture (meaning that the
|
|
+ * sampler will linearize the looked up data)
|
|
+ */
|
|
+ SVGA3DFORMAT_OP_SRGBREAD = 0x00008000,
|
|
+
|
|
+/*
|
|
+ * Indicates that this format can be used in the bumpmap instructions
|
|
+ */
|
|
+ SVGA3DFORMAT_OP_BUMPMAP = 0x00010000,
|
|
+
|
|
+/*
|
|
+ * Indicates that this format can be sampled by the displacement map sampler
|
|
+ */
|
|
+ SVGA3DFORMAT_OP_DMAP = 0x00020000,
|
|
+
|
|
+/*
|
|
+ * Indicates that this format cannot be used with texture filtering
|
|
+ */
|
|
+ SVGA3DFORMAT_OP_NOFILTER = 0x00040000,
|
|
+
|
|
+/*
|
|
+ * Indicates that format conversions are supported to this RGB format if
|
|
+ * SVGA3DFORMAT_OP_CONVERT_TO_ARGB is specified in the source format.
|
|
+ */
|
|
+ SVGA3DFORMAT_OP_MEMBEROFGROUP_ARGB = 0x00080000,
|
|
+
|
|
+/*
|
|
+ * Indicated that this format can be written as an SRGB target (meaning that the
|
|
+ * pixel pipe will DE-linearize data on output to format)
|
|
+ */
|
|
+ SVGA3DFORMAT_OP_SRGBWRITE = 0x00100000,
|
|
+
|
|
+/*
|
|
+ * Indicates that this format cannot be used with alpha blending
|
|
+ */
|
|
+ SVGA3DFORMAT_OP_NOALPHABLEND = 0x00200000,
|
|
+
|
|
+/*
|
|
+ * Indicates that the device can auto-generated sublevels for resources
|
|
+ * of this format
|
|
+ */
|
|
+ SVGA3DFORMAT_OP_AUTOGENMIPMAP = 0x00400000,
|
|
+
|
|
+/*
|
|
+ * Indicates that this format can be used by vertex texture sampler
|
|
+ */
|
|
+ SVGA3DFORMAT_OP_VERTEXTEXTURE = 0x00800000,
|
|
+
|
|
+/*
|
|
+ * Indicates that this format supports neither texture coordinate wrap
|
|
+ * modes, nor mipmapping
|
|
+ */
|
|
+ SVGA3DFORMAT_OP_NOTEXCOORDWRAPNORMIP = 0x01000000
|
|
+} SVGA3dFormatOp;
|
|
+
|
|
+/*
|
|
+ * This structure is a conversion of SVGA3DFORMAT_OP_*.
|
|
+ * Entries must be located at the same position.
|
|
+ */
|
|
+typedef union {
|
|
+ uint32 value;
|
|
+ struct {
|
|
+ uint32 texture : 1;
|
|
+ uint32 volumeTexture : 1;
|
|
+ uint32 cubeTexture : 1;
|
|
+ uint32 offscreenRenderTarget : 1;
|
|
+ uint32 sameFormatRenderTarget : 1;
|
|
+ uint32 unknown1 : 1;
|
|
+ uint32 zStencil : 1;
|
|
+ uint32 zStencilArbitraryDepth : 1;
|
|
+ uint32 sameFormatUpToAlpha : 1;
|
|
+ uint32 unknown2 : 1;
|
|
+ uint32 displayMode : 1;
|
|
+ uint32 acceleration3d : 1;
|
|
+ uint32 pixelSize : 1;
|
|
+ uint32 convertToARGB : 1;
|
|
+ uint32 offscreenPlain : 1;
|
|
+ uint32 sRGBRead : 1;
|
|
+ uint32 bumpMap : 1;
|
|
+ uint32 dmap : 1;
|
|
+ uint32 noFilter : 1;
|
|
+ uint32 memberOfGroupARGB : 1;
|
|
+ uint32 sRGBWrite : 1;
|
|
+ uint32 noAlphaBlend : 1;
|
|
+ uint32 autoGenMipMap : 1;
|
|
+ uint32 vertexTexture : 1;
|
|
+ uint32 noTexCoordWrapNorMip : 1;
|
|
+ };
|
|
+} SVGA3dSurfaceFormatCaps;
|
|
+
|
|
+/*
|
|
+ * SVGA_3D_CMD_SETRENDERSTATE Types. All value types
|
|
+ * must fit in a uint32.
|
|
+ */
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_RS_INVALID = 0,
|
|
+ SVGA3D_RS_ZENABLE = 1, /* SVGA3dBool */
|
|
+ SVGA3D_RS_ZWRITEENABLE = 2, /* SVGA3dBool */
|
|
+ SVGA3D_RS_ALPHATESTENABLE = 3, /* SVGA3dBool */
|
|
+ SVGA3D_RS_DITHERENABLE = 4, /* SVGA3dBool */
|
|
+ SVGA3D_RS_BLENDENABLE = 5, /* SVGA3dBool */
|
|
+ SVGA3D_RS_FOGENABLE = 6, /* SVGA3dBool */
|
|
+ SVGA3D_RS_SPECULARENABLE = 7, /* SVGA3dBool */
|
|
+ SVGA3D_RS_STENCILENABLE = 8, /* SVGA3dBool */
|
|
+ SVGA3D_RS_LIGHTINGENABLE = 9, /* SVGA3dBool */
|
|
+ SVGA3D_RS_NORMALIZENORMALS = 10, /* SVGA3dBool */
|
|
+ SVGA3D_RS_POINTSPRITEENABLE = 11, /* SVGA3dBool */
|
|
+ SVGA3D_RS_POINTSCALEENABLE = 12, /* SVGA3dBool */
|
|
+ SVGA3D_RS_STENCILREF = 13, /* uint32 */
|
|
+ SVGA3D_RS_STENCILMASK = 14, /* uint32 */
|
|
+ SVGA3D_RS_STENCILWRITEMASK = 15, /* uint32 */
|
|
+ SVGA3D_RS_FOGSTART = 16, /* float */
|
|
+ SVGA3D_RS_FOGEND = 17, /* float */
|
|
+ SVGA3D_RS_FOGDENSITY = 18, /* float */
|
|
+ SVGA3D_RS_POINTSIZE = 19, /* float */
|
|
+ SVGA3D_RS_POINTSIZEMIN = 20, /* float */
|
|
+ SVGA3D_RS_POINTSIZEMAX = 21, /* float */
|
|
+ SVGA3D_RS_POINTSCALE_A = 22, /* float */
|
|
+ SVGA3D_RS_POINTSCALE_B = 23, /* float */
|
|
+ SVGA3D_RS_POINTSCALE_C = 24, /* float */
|
|
+ SVGA3D_RS_FOGCOLOR = 25, /* SVGA3dColor */
|
|
+ SVGA3D_RS_AMBIENT = 26, /* SVGA3dColor */
|
|
+ SVGA3D_RS_CLIPPLANEENABLE = 27, /* SVGA3dClipPlanes */
|
|
+ SVGA3D_RS_FOGMODE = 28, /* SVGA3dFogMode */
|
|
+ SVGA3D_RS_FILLMODE = 29, /* SVGA3dFillMode */
|
|
+ SVGA3D_RS_SHADEMODE = 30, /* SVGA3dShadeMode */
|
|
+ SVGA3D_RS_LINEPATTERN = 31, /* SVGA3dLinePattern */
|
|
+ SVGA3D_RS_SRCBLEND = 32, /* SVGA3dBlendOp */
|
|
+ SVGA3D_RS_DSTBLEND = 33, /* SVGA3dBlendOp */
|
|
+ SVGA3D_RS_BLENDEQUATION = 34, /* SVGA3dBlendEquation */
|
|
+ SVGA3D_RS_CULLMODE = 35, /* SVGA3dFace */
|
|
+ SVGA3D_RS_ZFUNC = 36, /* SVGA3dCmpFunc */
|
|
+ SVGA3D_RS_ALPHAFUNC = 37, /* SVGA3dCmpFunc */
|
|
+ SVGA3D_RS_STENCILFUNC = 38, /* SVGA3dCmpFunc */
|
|
+ SVGA3D_RS_STENCILFAIL = 39, /* SVGA3dStencilOp */
|
|
+ SVGA3D_RS_STENCILZFAIL = 40, /* SVGA3dStencilOp */
|
|
+ SVGA3D_RS_STENCILPASS = 41, /* SVGA3dStencilOp */
|
|
+ SVGA3D_RS_ALPHAREF = 42, /* float (0.0 .. 1.0) */
|
|
+ SVGA3D_RS_FRONTWINDING = 43, /* SVGA3dFrontWinding */
|
|
+ SVGA3D_RS_COORDINATETYPE = 44, /* SVGA3dCoordinateType */
|
|
+ SVGA3D_RS_ZBIAS = 45, /* float */
|
|
+ SVGA3D_RS_RANGEFOGENABLE = 46, /* SVGA3dBool */
|
|
+ SVGA3D_RS_COLORWRITEENABLE = 47, /* SVGA3dColorMask */
|
|
+ SVGA3D_RS_VERTEXMATERIALENABLE = 48, /* SVGA3dBool */
|
|
+ SVGA3D_RS_DIFFUSEMATERIALSOURCE = 49, /* SVGA3dVertexMaterial */
|
|
+ SVGA3D_RS_SPECULARMATERIALSOURCE = 50, /* SVGA3dVertexMaterial */
|
|
+ SVGA3D_RS_AMBIENTMATERIALSOURCE = 51, /* SVGA3dVertexMaterial */
|
|
+ SVGA3D_RS_EMISSIVEMATERIALSOURCE = 52, /* SVGA3dVertexMaterial */
|
|
+ SVGA3D_RS_TEXTUREFACTOR = 53, /* SVGA3dColor */
|
|
+ SVGA3D_RS_LOCALVIEWER = 54, /* SVGA3dBool */
|
|
+ SVGA3D_RS_SCISSORTESTENABLE = 55, /* SVGA3dBool */
|
|
+ SVGA3D_RS_BLENDCOLOR = 56, /* SVGA3dColor */
|
|
+ SVGA3D_RS_STENCILENABLE2SIDED = 57, /* SVGA3dBool */
|
|
+ SVGA3D_RS_CCWSTENCILFUNC = 58, /* SVGA3dCmpFunc */
|
|
+ SVGA3D_RS_CCWSTENCILFAIL = 59, /* SVGA3dStencilOp */
|
|
+ SVGA3D_RS_CCWSTENCILZFAIL = 60, /* SVGA3dStencilOp */
|
|
+ SVGA3D_RS_CCWSTENCILPASS = 61, /* SVGA3dStencilOp */
|
|
+ SVGA3D_RS_VERTEXBLEND = 62, /* SVGA3dVertexBlendFlags */
|
|
+ SVGA3D_RS_SLOPESCALEDEPTHBIAS = 63, /* float */
|
|
+ SVGA3D_RS_DEPTHBIAS = 64, /* float */
|
|
+
|
|
+
|
|
+ /*
|
|
+ * Output Gamma Level
|
|
+ *
|
|
+ * Output gamma effects the gamma curve of colors that are output from the
|
|
+ * rendering pipeline. A value of 1.0 specifies a linear color space. If the
|
|
+ * value is <= 0.0, gamma correction is ignored and linear color space is
|
|
+ * used.
|
|
+ */
|
|
+
|
|
+ SVGA3D_RS_OUTPUTGAMMA = 65, /* float */
|
|
+ SVGA3D_RS_ZVISIBLE = 66, /* SVGA3dBool */
|
|
+ SVGA3D_RS_LASTPIXEL = 67, /* SVGA3dBool */
|
|
+ SVGA3D_RS_CLIPPING = 68, /* SVGA3dBool */
|
|
+ SVGA3D_RS_WRAP0 = 69, /* SVGA3dWrapFlags */
|
|
+ SVGA3D_RS_WRAP1 = 70, /* SVGA3dWrapFlags */
|
|
+ SVGA3D_RS_WRAP2 = 71, /* SVGA3dWrapFlags */
|
|
+ SVGA3D_RS_WRAP3 = 72, /* SVGA3dWrapFlags */
|
|
+ SVGA3D_RS_WRAP4 = 73, /* SVGA3dWrapFlags */
|
|
+ SVGA3D_RS_WRAP5 = 74, /* SVGA3dWrapFlags */
|
|
+ SVGA3D_RS_WRAP6 = 75, /* SVGA3dWrapFlags */
|
|
+ SVGA3D_RS_WRAP7 = 76, /* SVGA3dWrapFlags */
|
|
+ SVGA3D_RS_WRAP8 = 77, /* SVGA3dWrapFlags */
|
|
+ SVGA3D_RS_WRAP9 = 78, /* SVGA3dWrapFlags */
|
|
+ SVGA3D_RS_WRAP10 = 79, /* SVGA3dWrapFlags */
|
|
+ SVGA3D_RS_WRAP11 = 80, /* SVGA3dWrapFlags */
|
|
+ SVGA3D_RS_WRAP12 = 81, /* SVGA3dWrapFlags */
|
|
+ SVGA3D_RS_WRAP13 = 82, /* SVGA3dWrapFlags */
|
|
+ SVGA3D_RS_WRAP14 = 83, /* SVGA3dWrapFlags */
|
|
+ SVGA3D_RS_WRAP15 = 84, /* SVGA3dWrapFlags */
|
|
+ SVGA3D_RS_MULTISAMPLEANTIALIAS = 85, /* SVGA3dBool */
|
|
+ SVGA3D_RS_MULTISAMPLEMASK = 86, /* uint32 */
|
|
+ SVGA3D_RS_INDEXEDVERTEXBLENDENABLE = 87, /* SVGA3dBool */
|
|
+ SVGA3D_RS_TWEENFACTOR = 88, /* float */
|
|
+ SVGA3D_RS_ANTIALIASEDLINEENABLE = 89, /* SVGA3dBool */
|
|
+ SVGA3D_RS_COLORWRITEENABLE1 = 90, /* SVGA3dColorMask */
|
|
+ SVGA3D_RS_COLORWRITEENABLE2 = 91, /* SVGA3dColorMask */
|
|
+ SVGA3D_RS_COLORWRITEENABLE3 = 92, /* SVGA3dColorMask */
|
|
+ SVGA3D_RS_SEPARATEALPHABLENDENABLE = 93, /* SVGA3dBool */
|
|
+ SVGA3D_RS_SRCBLENDALPHA = 94, /* SVGA3dBlendOp */
|
|
+ SVGA3D_RS_DSTBLENDALPHA = 95, /* SVGA3dBlendOp */
|
|
+ SVGA3D_RS_BLENDEQUATIONALPHA = 96, /* SVGA3dBlendEquation */
|
|
+ SVGA3D_RS_MAX
|
|
+} SVGA3dRenderStateName;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_VERTEXMATERIAL_NONE = 0, /* Use the value in the current material */
|
|
+ SVGA3D_VERTEXMATERIAL_DIFFUSE = 1, /* Use the value in the diffuse component */
|
|
+ SVGA3D_VERTEXMATERIAL_SPECULAR = 2, /* Use the value in the specular component */
|
|
+} SVGA3dVertexMaterial;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_FILLMODE_INVALID = 0,
|
|
+ SVGA3D_FILLMODE_POINT = 1,
|
|
+ SVGA3D_FILLMODE_LINE = 2,
|
|
+ SVGA3D_FILLMODE_FILL = 3,
|
|
+ SVGA3D_FILLMODE_MAX
|
|
+} SVGA3dFillModeType;
|
|
+
|
|
+
|
|
+typedef
|
|
+union {
|
|
+ struct {
|
|
+ uint16 mode; /* SVGA3dFillModeType */
|
|
+ uint16 face; /* SVGA3dFace */
|
|
+ };
|
|
+ uint32 uintValue;
|
|
+} SVGA3dFillMode;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_SHADEMODE_INVALID = 0,
|
|
+ SVGA3D_SHADEMODE_FLAT = 1,
|
|
+ SVGA3D_SHADEMODE_SMOOTH = 2,
|
|
+ SVGA3D_SHADEMODE_PHONG = 3, /* Not supported */
|
|
+ SVGA3D_SHADEMODE_MAX
|
|
+} SVGA3dShadeMode;
|
|
+
|
|
+typedef
|
|
+union {
|
|
+ struct {
|
|
+ uint16 repeat;
|
|
+ uint16 pattern;
|
|
+ };
|
|
+ uint32 uintValue;
|
|
+} SVGA3dLinePattern;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_BLENDOP_INVALID = 0,
|
|
+ SVGA3D_BLENDOP_ZERO = 1,
|
|
+ SVGA3D_BLENDOP_ONE = 2,
|
|
+ SVGA3D_BLENDOP_SRCCOLOR = 3,
|
|
+ SVGA3D_BLENDOP_INVSRCCOLOR = 4,
|
|
+ SVGA3D_BLENDOP_SRCALPHA = 5,
|
|
+ SVGA3D_BLENDOP_INVSRCALPHA = 6,
|
|
+ SVGA3D_BLENDOP_DESTALPHA = 7,
|
|
+ SVGA3D_BLENDOP_INVDESTALPHA = 8,
|
|
+ SVGA3D_BLENDOP_DESTCOLOR = 9,
|
|
+ SVGA3D_BLENDOP_INVDESTCOLOR = 10,
|
|
+ SVGA3D_BLENDOP_SRCALPHASAT = 11,
|
|
+ SVGA3D_BLENDOP_BLENDFACTOR = 12,
|
|
+ SVGA3D_BLENDOP_INVBLENDFACTOR = 13,
|
|
+ SVGA3D_BLENDOP_MAX
|
|
+} SVGA3dBlendOp;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_BLENDEQ_INVALID = 0,
|
|
+ SVGA3D_BLENDEQ_ADD = 1,
|
|
+ SVGA3D_BLENDEQ_SUBTRACT = 2,
|
|
+ SVGA3D_BLENDEQ_REVSUBTRACT = 3,
|
|
+ SVGA3D_BLENDEQ_MINIMUM = 4,
|
|
+ SVGA3D_BLENDEQ_MAXIMUM = 5,
|
|
+ SVGA3D_BLENDEQ_MAX
|
|
+} SVGA3dBlendEquation;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_FRONTWINDING_INVALID = 0,
|
|
+ SVGA3D_FRONTWINDING_CW = 1,
|
|
+ SVGA3D_FRONTWINDING_CCW = 2,
|
|
+ SVGA3D_FRONTWINDING_MAX
|
|
+} SVGA3dFrontWinding;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_FACE_INVALID = 0,
|
|
+ SVGA3D_FACE_NONE = 1,
|
|
+ SVGA3D_FACE_FRONT = 2,
|
|
+ SVGA3D_FACE_BACK = 3,
|
|
+ SVGA3D_FACE_FRONT_BACK = 4,
|
|
+ SVGA3D_FACE_MAX
|
|
+} SVGA3dFace;
|
|
+
|
|
+/*
|
|
+ * The order and the values should not be changed
|
|
+ */
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_CMP_INVALID = 0,
|
|
+ SVGA3D_CMP_NEVER = 1,
|
|
+ SVGA3D_CMP_LESS = 2,
|
|
+ SVGA3D_CMP_EQUAL = 3,
|
|
+ SVGA3D_CMP_LESSEQUAL = 4,
|
|
+ SVGA3D_CMP_GREATER = 5,
|
|
+ SVGA3D_CMP_NOTEQUAL = 6,
|
|
+ SVGA3D_CMP_GREATEREQUAL = 7,
|
|
+ SVGA3D_CMP_ALWAYS = 8,
|
|
+ SVGA3D_CMP_MAX
|
|
+} SVGA3dCmpFunc;
|
|
+
|
|
+/*
|
|
+ * SVGA3D_FOGFUNC_* specifies the fog equation, or PER_VERTEX which allows
|
|
+ * the fog factor to be specified in the alpha component of the specular
|
|
+ * (a.k.a. secondary) vertex color.
|
|
+ */
|
|
+typedef enum {
|
|
+ SVGA3D_FOGFUNC_INVALID = 0,
|
|
+ SVGA3D_FOGFUNC_EXP = 1,
|
|
+ SVGA3D_FOGFUNC_EXP2 = 2,
|
|
+ SVGA3D_FOGFUNC_LINEAR = 3,
|
|
+ SVGA3D_FOGFUNC_PER_VERTEX = 4
|
|
+} SVGA3dFogFunction;
|
|
+
|
|
+/*
|
|
+ * SVGA3D_FOGTYPE_* specifies if fog factors are computed on a per-vertex
|
|
+ * or per-pixel basis.
|
|
+ */
|
|
+typedef enum {
|
|
+ SVGA3D_FOGTYPE_INVALID = 0,
|
|
+ SVGA3D_FOGTYPE_VERTEX = 1,
|
|
+ SVGA3D_FOGTYPE_PIXEL = 2,
|
|
+ SVGA3D_FOGTYPE_MAX = 3
|
|
+} SVGA3dFogType;
|
|
+
|
|
+/*
|
|
+ * SVGA3D_FOGBASE_* selects depth or range-based fog. Depth-based fog is
|
|
+ * computed using the eye Z value of each pixel (or vertex), whereas range-
|
|
+ * based fog is computed using the actual distance (range) to the eye.
|
|
+ */
|
|
+typedef enum {
|
|
+ SVGA3D_FOGBASE_INVALID = 0,
|
|
+ SVGA3D_FOGBASE_DEPTHBASED = 1,
|
|
+ SVGA3D_FOGBASE_RANGEBASED = 2,
|
|
+ SVGA3D_FOGBASE_MAX = 3
|
|
+} SVGA3dFogBase;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_STENCILOP_INVALID = 0,
|
|
+ SVGA3D_STENCILOP_KEEP = 1,
|
|
+ SVGA3D_STENCILOP_ZERO = 2,
|
|
+ SVGA3D_STENCILOP_REPLACE = 3,
|
|
+ SVGA3D_STENCILOP_INCRSAT = 4,
|
|
+ SVGA3D_STENCILOP_DECRSAT = 5,
|
|
+ SVGA3D_STENCILOP_INVERT = 6,
|
|
+ SVGA3D_STENCILOP_INCR = 7,
|
|
+ SVGA3D_STENCILOP_DECR = 8,
|
|
+ SVGA3D_STENCILOP_MAX
|
|
+} SVGA3dStencilOp;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_CLIPPLANE_0 = (1 << 0),
|
|
+ SVGA3D_CLIPPLANE_1 = (1 << 1),
|
|
+ SVGA3D_CLIPPLANE_2 = (1 << 2),
|
|
+ SVGA3D_CLIPPLANE_3 = (1 << 3),
|
|
+ SVGA3D_CLIPPLANE_4 = (1 << 4),
|
|
+ SVGA3D_CLIPPLANE_5 = (1 << 5),
|
|
+} SVGA3dClipPlanes;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_CLEAR_COLOR = 0x1,
|
|
+ SVGA3D_CLEAR_DEPTH = 0x2,
|
|
+ SVGA3D_CLEAR_STENCIL = 0x4
|
|
+} SVGA3dClearFlag;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_RT_DEPTH = 0,
|
|
+ SVGA3D_RT_STENCIL = 1,
|
|
+ SVGA3D_RT_COLOR0 = 2,
|
|
+ SVGA3D_RT_COLOR1 = 3,
|
|
+ SVGA3D_RT_COLOR2 = 4,
|
|
+ SVGA3D_RT_COLOR3 = 5,
|
|
+ SVGA3D_RT_COLOR4 = 6,
|
|
+ SVGA3D_RT_COLOR5 = 7,
|
|
+ SVGA3D_RT_COLOR6 = 8,
|
|
+ SVGA3D_RT_COLOR7 = 9,
|
|
+ SVGA3D_RT_MAX,
|
|
+ SVGA3D_RT_INVALID = ((uint32)-1),
|
|
+} SVGA3dRenderTargetType;
|
|
+
|
|
+#define SVGA3D_MAX_RT_COLOR (SVGA3D_RT_COLOR7 - SVGA3D_RT_COLOR0 + 1)
|
|
+
|
|
+typedef
|
|
+union {
|
|
+ struct {
|
|
+ uint32 red : 1;
|
|
+ uint32 green : 1;
|
|
+ uint32 blue : 1;
|
|
+ uint32 alpha : 1;
|
|
+ };
|
|
+ uint32 uintValue;
|
|
+} SVGA3dColorMask;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_VBLEND_DISABLE = 0,
|
|
+ SVGA3D_VBLEND_1WEIGHT = 1,
|
|
+ SVGA3D_VBLEND_2WEIGHT = 2,
|
|
+ SVGA3D_VBLEND_3WEIGHT = 3,
|
|
+} SVGA3dVertexBlendFlags;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_WRAPCOORD_0 = 1 << 0,
|
|
+ SVGA3D_WRAPCOORD_1 = 1 << 1,
|
|
+ SVGA3D_WRAPCOORD_2 = 1 << 2,
|
|
+ SVGA3D_WRAPCOORD_3 = 1 << 3,
|
|
+ SVGA3D_WRAPCOORD_ALL = 0xF,
|
|
+} SVGA3dWrapFlags;
|
|
+
|
|
+/*
|
|
+ * SVGA_3D_CMD_TEXTURESTATE Types. All value types
|
|
+ * must fit in a uint32.
|
|
+ */
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_TS_INVALID = 0,
|
|
+ SVGA3D_TS_BIND_TEXTURE = 1, /* SVGA3dSurfaceId */
|
|
+ SVGA3D_TS_COLOROP = 2, /* SVGA3dTextureCombiner */
|
|
+ SVGA3D_TS_COLORARG1 = 3, /* SVGA3dTextureArgData */
|
|
+ SVGA3D_TS_COLORARG2 = 4, /* SVGA3dTextureArgData */
|
|
+ SVGA3D_TS_ALPHAOP = 5, /* SVGA3dTextureCombiner */
|
|
+ SVGA3D_TS_ALPHAARG1 = 6, /* SVGA3dTextureArgData */
|
|
+ SVGA3D_TS_ALPHAARG2 = 7, /* SVGA3dTextureArgData */
|
|
+ SVGA3D_TS_ADDRESSU = 8, /* SVGA3dTextureAddress */
|
|
+ SVGA3D_TS_ADDRESSV = 9, /* SVGA3dTextureAddress */
|
|
+ SVGA3D_TS_MIPFILTER = 10, /* SVGA3dTextureFilter */
|
|
+ SVGA3D_TS_MAGFILTER = 11, /* SVGA3dTextureFilter */
|
|
+ SVGA3D_TS_MINFILTER = 12, /* SVGA3dTextureFilter */
|
|
+ SVGA3D_TS_BORDERCOLOR = 13, /* SVGA3dColor */
|
|
+ SVGA3D_TS_TEXCOORDINDEX = 14, /* uint32 */
|
|
+ SVGA3D_TS_TEXTURETRANSFORMFLAGS = 15, /* SVGA3dTexTransformFlags */
|
|
+ SVGA3D_TS_TEXCOORDGEN = 16, /* SVGA3dTextureCoordGen */
|
|
+ SVGA3D_TS_BUMPENVMAT00 = 17, /* float */
|
|
+ SVGA3D_TS_BUMPENVMAT01 = 18, /* float */
|
|
+ SVGA3D_TS_BUMPENVMAT10 = 19, /* float */
|
|
+ SVGA3D_TS_BUMPENVMAT11 = 20, /* float */
|
|
+ SVGA3D_TS_TEXTURE_MIPMAP_LEVEL = 21, /* uint32 */
|
|
+ SVGA3D_TS_TEXTURE_LOD_BIAS = 22, /* float */
|
|
+ SVGA3D_TS_TEXTURE_ANISOTROPIC_LEVEL = 23, /* uint32 */
|
|
+ SVGA3D_TS_ADDRESSW = 24, /* SVGA3dTextureAddress */
|
|
+
|
|
+
|
|
+ /*
|
|
+ * Sampler Gamma Level
|
|
+ *
|
|
+ * Sampler gamma effects the color of samples taken from the sampler. A
|
|
+ * value of 1.0 will produce linear samples. If the value is <= 0.0 the
|
|
+ * gamma value is ignored and a linear space is used.
|
|
+ */
|
|
+
|
|
+ SVGA3D_TS_GAMMA = 25, /* float */
|
|
+ SVGA3D_TS_BUMPENVLSCALE = 26, /* float */
|
|
+ SVGA3D_TS_BUMPENVLOFFSET = 27, /* float */
|
|
+ SVGA3D_TS_COLORARG0 = 28, /* SVGA3dTextureArgData */
|
|
+ SVGA3D_TS_ALPHAARG0 = 29, /* SVGA3dTextureArgData */
|
|
+ SVGA3D_TS_MAX
|
|
+} SVGA3dTextureStateName;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_TC_INVALID = 0,
|
|
+ SVGA3D_TC_DISABLE = 1,
|
|
+ SVGA3D_TC_SELECTARG1 = 2,
|
|
+ SVGA3D_TC_SELECTARG2 = 3,
|
|
+ SVGA3D_TC_MODULATE = 4,
|
|
+ SVGA3D_TC_ADD = 5,
|
|
+ SVGA3D_TC_ADDSIGNED = 6,
|
|
+ SVGA3D_TC_SUBTRACT = 7,
|
|
+ SVGA3D_TC_BLENDTEXTUREALPHA = 8,
|
|
+ SVGA3D_TC_BLENDDIFFUSEALPHA = 9,
|
|
+ SVGA3D_TC_BLENDCURRENTALPHA = 10,
|
|
+ SVGA3D_TC_BLENDFACTORALPHA = 11,
|
|
+ SVGA3D_TC_MODULATE2X = 12,
|
|
+ SVGA3D_TC_MODULATE4X = 13,
|
|
+ SVGA3D_TC_DSDT = 14,
|
|
+ SVGA3D_TC_DOTPRODUCT3 = 15,
|
|
+ SVGA3D_TC_BLENDTEXTUREALPHAPM = 16,
|
|
+ SVGA3D_TC_ADDSIGNED2X = 17,
|
|
+ SVGA3D_TC_ADDSMOOTH = 18,
|
|
+ SVGA3D_TC_PREMODULATE = 19,
|
|
+ SVGA3D_TC_MODULATEALPHA_ADDCOLOR = 20,
|
|
+ SVGA3D_TC_MODULATECOLOR_ADDALPHA = 21,
|
|
+ SVGA3D_TC_MODULATEINVALPHA_ADDCOLOR = 22,
|
|
+ SVGA3D_TC_MODULATEINVCOLOR_ADDALPHA = 23,
|
|
+ SVGA3D_TC_BUMPENVMAPLUMINANCE = 24,
|
|
+ SVGA3D_TC_MULTIPLYADD = 25,
|
|
+ SVGA3D_TC_LERP = 26,
|
|
+ SVGA3D_TC_MAX
|
|
+} SVGA3dTextureCombiner;
|
|
+
|
|
+#define SVGA3D_TC_CAP_BIT(svga3d_tc_op) (svga3d_tc_op ? (1 << (svga3d_tc_op - 1)) : 0)
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_TEX_ADDRESS_INVALID = 0,
|
|
+ SVGA3D_TEX_ADDRESS_WRAP = 1,
|
|
+ SVGA3D_TEX_ADDRESS_MIRROR = 2,
|
|
+ SVGA3D_TEX_ADDRESS_CLAMP = 3,
|
|
+ SVGA3D_TEX_ADDRESS_BORDER = 4,
|
|
+ SVGA3D_TEX_ADDRESS_MIRRORONCE = 5,
|
|
+ SVGA3D_TEX_ADDRESS_EDGE = 6,
|
|
+ SVGA3D_TEX_ADDRESS_MAX
|
|
+} SVGA3dTextureAddress;
|
|
+
|
|
+/*
|
|
+ * SVGA3D_TEX_FILTER_NONE as the minification filter means mipmapping is
|
|
+ * disabled, and the rasterizer should use the magnification filter instead.
|
|
+ */
|
|
+typedef enum {
|
|
+ SVGA3D_TEX_FILTER_NONE = 0,
|
|
+ SVGA3D_TEX_FILTER_NEAREST = 1,
|
|
+ SVGA3D_TEX_FILTER_LINEAR = 2,
|
|
+ SVGA3D_TEX_FILTER_ANISOTROPIC = 3,
|
|
+ SVGA3D_TEX_FILTER_FLATCUBIC = 4, // Deprecated, not implemented
|
|
+ SVGA3D_TEX_FILTER_GAUSSIANCUBIC = 5, // Deprecated, not implemented
|
|
+ SVGA3D_TEX_FILTER_PYRAMIDALQUAD = 6, // Not currently implemented
|
|
+ SVGA3D_TEX_FILTER_GAUSSIANQUAD = 7, // Not currently implemented
|
|
+ SVGA3D_TEX_FILTER_MAX
|
|
+} SVGA3dTextureFilter;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_TEX_TRANSFORM_OFF = 0,
|
|
+ SVGA3D_TEX_TRANSFORM_S = (1 << 0),
|
|
+ SVGA3D_TEX_TRANSFORM_T = (1 << 1),
|
|
+ SVGA3D_TEX_TRANSFORM_R = (1 << 2),
|
|
+ SVGA3D_TEX_TRANSFORM_Q = (1 << 3),
|
|
+ SVGA3D_TEX_PROJECTED = (1 << 15),
|
|
+} SVGA3dTexTransformFlags;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_TEXCOORD_GEN_OFF = 0,
|
|
+ SVGA3D_TEXCOORD_GEN_EYE_POSITION = 1,
|
|
+ SVGA3D_TEXCOORD_GEN_EYE_NORMAL = 2,
|
|
+ SVGA3D_TEXCOORD_GEN_REFLECTIONVECTOR = 3,
|
|
+ SVGA3D_TEXCOORD_GEN_SPHERE = 4,
|
|
+ SVGA3D_TEXCOORD_GEN_MAX
|
|
+} SVGA3dTextureCoordGen;
|
|
+
|
|
+/*
|
|
+ * Texture argument constants for texture combiner
|
|
+ */
|
|
+typedef enum {
|
|
+ SVGA3D_TA_INVALID = 0,
|
|
+ SVGA3D_TA_CONSTANT = 1,
|
|
+ SVGA3D_TA_PREVIOUS = 2,
|
|
+ SVGA3D_TA_DIFFUSE = 3,
|
|
+ SVGA3D_TA_TEXTURE = 4,
|
|
+ SVGA3D_TA_SPECULAR = 5,
|
|
+ SVGA3D_TA_MAX
|
|
+} SVGA3dTextureArgData;
|
|
+
|
|
+#define SVGA3D_TM_MASK_LEN 4
|
|
+
|
|
+/* Modifiers for texture argument constants defined above. */
|
|
+typedef enum {
|
|
+ SVGA3D_TM_NONE = 0,
|
|
+ SVGA3D_TM_ALPHA = (1 << SVGA3D_TM_MASK_LEN),
|
|
+ SVGA3D_TM_ONE_MINUS = (2 << SVGA3D_TM_MASK_LEN),
|
|
+} SVGA3dTextureArgModifier;
|
|
+
|
|
+#define SVGA3D_INVALID_ID ((uint32)-1)
|
|
+#define SVGA3D_MAX_CLIP_PLANES 6
|
|
+
|
|
+/*
|
|
+ * This is the limit to the number of fixed-function texture
|
|
+ * transforms and texture coordinates we can support. It does *not*
|
|
+ * correspond to the number of texture image units (samplers) we
|
|
+ * support!
|
|
+ */
|
|
+#define SVGA3D_MAX_TEXTURE_COORDS 8
|
|
+
|
|
+/*
|
|
+ * Vertex declarations
|
|
+ *
|
|
+ * Notes:
|
|
+ *
|
|
+ * SVGA3D_DECLUSAGE_POSITIONT is for pre-transformed vertices. If you
|
|
+ * draw with any POSITIONT vertex arrays, the programmable vertex
|
|
+ * pipeline will be implicitly disabled. Drawing will take place as if
|
|
+ * no vertex shader was bound.
|
|
+ */
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_DECLUSAGE_POSITION = 0,
|
|
+ SVGA3D_DECLUSAGE_BLENDWEIGHT, // 1
|
|
+ SVGA3D_DECLUSAGE_BLENDINDICES, // 2
|
|
+ SVGA3D_DECLUSAGE_NORMAL, // 3
|
|
+ SVGA3D_DECLUSAGE_PSIZE, // 4
|
|
+ SVGA3D_DECLUSAGE_TEXCOORD, // 5
|
|
+ SVGA3D_DECLUSAGE_TANGENT, // 6
|
|
+ SVGA3D_DECLUSAGE_BINORMAL, // 7
|
|
+ SVGA3D_DECLUSAGE_TESSFACTOR, // 8
|
|
+ SVGA3D_DECLUSAGE_POSITIONT, // 9
|
|
+ SVGA3D_DECLUSAGE_COLOR, // 10
|
|
+ SVGA3D_DECLUSAGE_FOG, // 11
|
|
+ SVGA3D_DECLUSAGE_DEPTH, // 12
|
|
+ SVGA3D_DECLUSAGE_SAMPLE, // 13
|
|
+ SVGA3D_DECLUSAGE_MAX
|
|
+} SVGA3dDeclUsage;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_DECLMETHOD_DEFAULT = 0,
|
|
+ SVGA3D_DECLMETHOD_PARTIALU,
|
|
+ SVGA3D_DECLMETHOD_PARTIALV,
|
|
+ SVGA3D_DECLMETHOD_CROSSUV, // Normal
|
|
+ SVGA3D_DECLMETHOD_UV,
|
|
+ SVGA3D_DECLMETHOD_LOOKUP, // Lookup a displacement map
|
|
+ SVGA3D_DECLMETHOD_LOOKUPPRESAMPLED, // Lookup a pre-sampled displacement map
|
|
+} SVGA3dDeclMethod;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_DECLTYPE_FLOAT1 = 0,
|
|
+ SVGA3D_DECLTYPE_FLOAT2 = 1,
|
|
+ SVGA3D_DECLTYPE_FLOAT3 = 2,
|
|
+ SVGA3D_DECLTYPE_FLOAT4 = 3,
|
|
+ SVGA3D_DECLTYPE_D3DCOLOR = 4,
|
|
+ SVGA3D_DECLTYPE_UBYTE4 = 5,
|
|
+ SVGA3D_DECLTYPE_SHORT2 = 6,
|
|
+ SVGA3D_DECLTYPE_SHORT4 = 7,
|
|
+ SVGA3D_DECLTYPE_UBYTE4N = 8,
|
|
+ SVGA3D_DECLTYPE_SHORT2N = 9,
|
|
+ SVGA3D_DECLTYPE_SHORT4N = 10,
|
|
+ SVGA3D_DECLTYPE_USHORT2N = 11,
|
|
+ SVGA3D_DECLTYPE_USHORT4N = 12,
|
|
+ SVGA3D_DECLTYPE_UDEC3 = 13,
|
|
+ SVGA3D_DECLTYPE_DEC3N = 14,
|
|
+ SVGA3D_DECLTYPE_FLOAT16_2 = 15,
|
|
+ SVGA3D_DECLTYPE_FLOAT16_4 = 16,
|
|
+ SVGA3D_DECLTYPE_MAX,
|
|
+} SVGA3dDeclType;
|
|
+
|
|
+/*
|
|
+ * This structure is used for the divisor for geometry instancing;
|
|
+ * it's a direct translation of the Direct3D equivalent.
|
|
+ */
|
|
+typedef union {
|
|
+ struct {
|
|
+ /*
|
|
+ * For index data, this number represents the number of instances to draw.
|
|
+ * For instance data, this number represents the number of
|
|
+ * instances/vertex in this stream
|
|
+ */
|
|
+ uint32 count : 30;
|
|
+
|
|
+ /*
|
|
+ * This is 1 if this is supposed to be the data that is repeated for
|
|
+ * every instance.
|
|
+ */
|
|
+ uint32 indexedData : 1;
|
|
+
|
|
+ /*
|
|
+ * This is 1 if this is supposed to be the per-instance data.
|
|
+ */
|
|
+ uint32 instanceData : 1;
|
|
+ };
|
|
+
|
|
+ uint32 value;
|
|
+} SVGA3dVertexDivisor;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_PRIMITIVE_INVALID = 0,
|
|
+ SVGA3D_PRIMITIVE_TRIANGLELIST = 1,
|
|
+ SVGA3D_PRIMITIVE_POINTLIST = 2,
|
|
+ SVGA3D_PRIMITIVE_LINELIST = 3,
|
|
+ SVGA3D_PRIMITIVE_LINESTRIP = 4,
|
|
+ SVGA3D_PRIMITIVE_TRIANGLESTRIP = 5,
|
|
+ SVGA3D_PRIMITIVE_TRIANGLEFAN = 6,
|
|
+ SVGA3D_PRIMITIVE_MAX
|
|
+} SVGA3dPrimitiveType;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_COORDINATE_INVALID = 0,
|
|
+ SVGA3D_COORDINATE_LEFTHANDED = 1,
|
|
+ SVGA3D_COORDINATE_RIGHTHANDED = 2,
|
|
+ SVGA3D_COORDINATE_MAX
|
|
+} SVGA3dCoordinateType;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_TRANSFORM_INVALID = 0,
|
|
+ SVGA3D_TRANSFORM_WORLD = 1,
|
|
+ SVGA3D_TRANSFORM_VIEW = 2,
|
|
+ SVGA3D_TRANSFORM_PROJECTION = 3,
|
|
+ SVGA3D_TRANSFORM_TEXTURE0 = 4,
|
|
+ SVGA3D_TRANSFORM_TEXTURE1 = 5,
|
|
+ SVGA3D_TRANSFORM_TEXTURE2 = 6,
|
|
+ SVGA3D_TRANSFORM_TEXTURE3 = 7,
|
|
+ SVGA3D_TRANSFORM_TEXTURE4 = 8,
|
|
+ SVGA3D_TRANSFORM_TEXTURE5 = 9,
|
|
+ SVGA3D_TRANSFORM_TEXTURE6 = 10,
|
|
+ SVGA3D_TRANSFORM_TEXTURE7 = 11,
|
|
+ SVGA3D_TRANSFORM_WORLD1 = 12,
|
|
+ SVGA3D_TRANSFORM_WORLD2 = 13,
|
|
+ SVGA3D_TRANSFORM_WORLD3 = 14,
|
|
+ SVGA3D_TRANSFORM_MAX
|
|
+} SVGA3dTransformType;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_LIGHTTYPE_INVALID = 0,
|
|
+ SVGA3D_LIGHTTYPE_POINT = 1,
|
|
+ SVGA3D_LIGHTTYPE_SPOT1 = 2, /* 1-cone, in degrees */
|
|
+ SVGA3D_LIGHTTYPE_SPOT2 = 3, /* 2-cone, in radians */
|
|
+ SVGA3D_LIGHTTYPE_DIRECTIONAL = 4,
|
|
+ SVGA3D_LIGHTTYPE_MAX
|
|
+} SVGA3dLightType;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_CUBEFACE_POSX = 0,
|
|
+ SVGA3D_CUBEFACE_NEGX = 1,
|
|
+ SVGA3D_CUBEFACE_POSY = 2,
|
|
+ SVGA3D_CUBEFACE_NEGY = 3,
|
|
+ SVGA3D_CUBEFACE_POSZ = 4,
|
|
+ SVGA3D_CUBEFACE_NEGZ = 5,
|
|
+} SVGA3dCubeFace;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_SHADERTYPE_COMPILED_DX8 = 0,
|
|
+ SVGA3D_SHADERTYPE_VS = 1,
|
|
+ SVGA3D_SHADERTYPE_PS = 2,
|
|
+ SVGA3D_SHADERTYPE_MAX
|
|
+} SVGA3dShaderType;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_CONST_TYPE_FLOAT = 0,
|
|
+ SVGA3D_CONST_TYPE_INT = 1,
|
|
+ SVGA3D_CONST_TYPE_BOOL = 2,
|
|
+} SVGA3dShaderConstType;
|
|
+
|
|
+#define SVGA3D_MAX_SURFACE_FACES 6
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_STRETCH_BLT_POINT = 0,
|
|
+ SVGA3D_STRETCH_BLT_LINEAR = 1,
|
|
+ SVGA3D_STRETCH_BLT_MAX
|
|
+} SVGA3dStretchBltMode;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_QUERYTYPE_OCCLUSION = 0,
|
|
+ SVGA3D_QUERYTYPE_MAX
|
|
+} SVGA3dQueryType;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_QUERYSTATE_PENDING = 0, /* Waiting on the host (set by guest) */
|
|
+ SVGA3D_QUERYSTATE_SUCCEEDED = 1, /* Completed successfully (set by host) */
|
|
+ SVGA3D_QUERYSTATE_FAILED = 2, /* Completed unsuccessfully (set by host) */
|
|
+ SVGA3D_QUERYSTATE_NEW = 3, /* Never submitted (For guest use only) */
|
|
+} SVGA3dQueryState;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_WRITE_HOST_VRAM = 1,
|
|
+ SVGA3D_READ_HOST_VRAM = 2,
|
|
+} SVGA3dTransferType;
|
|
+
|
|
+/*
|
|
+ * The maximum number vertex arrays we're guaranteed to support in
|
|
+ * SVGA_3D_CMD_DRAWPRIMITIVES.
|
|
+ */
|
|
+#define SVGA3D_MAX_VERTEX_ARRAYS 32
|
|
+
|
|
+/*
|
|
+ * Identifiers for commands in the command FIFO.
|
|
+ *
|
|
+ * IDs between 1000 and 1039 (inclusive) were used by obsolete versions of
|
|
+ * the SVGA3D protocol and remain reserved; they should not be used in the
|
|
+ * future.
|
|
+ *
|
|
+ * IDs between 1040 and 1999 (inclusive) are available for use by the
|
|
+ * current SVGA3D protocol.
|
|
+ *
|
|
+ * FIFO clients other than SVGA3D should stay below 1000, or at 2000
|
|
+ * and up.
|
|
+ */
|
|
+
|
|
+#define SVGA_3D_CMD_LEGACY_BASE 1000
|
|
+#define SVGA_3D_CMD_BASE 1040
|
|
+
|
|
+#define SVGA_3D_CMD_SURFACE_DEFINE SVGA_3D_CMD_BASE + 0
|
|
+#define SVGA_3D_CMD_SURFACE_DESTROY SVGA_3D_CMD_BASE + 1
|
|
+#define SVGA_3D_CMD_SURFACE_COPY SVGA_3D_CMD_BASE + 2
|
|
+#define SVGA_3D_CMD_SURFACE_STRETCHBLT SVGA_3D_CMD_BASE + 3
|
|
+#define SVGA_3D_CMD_SURFACE_DMA SVGA_3D_CMD_BASE + 4
|
|
+#define SVGA_3D_CMD_CONTEXT_DEFINE SVGA_3D_CMD_BASE + 5
|
|
+#define SVGA_3D_CMD_CONTEXT_DESTROY SVGA_3D_CMD_BASE + 6
|
|
+#define SVGA_3D_CMD_SETTRANSFORM SVGA_3D_CMD_BASE + 7
|
|
+#define SVGA_3D_CMD_SETZRANGE SVGA_3D_CMD_BASE + 8
|
|
+#define SVGA_3D_CMD_SETRENDERSTATE SVGA_3D_CMD_BASE + 9
|
|
+#define SVGA_3D_CMD_SETRENDERTARGET SVGA_3D_CMD_BASE + 10
|
|
+#define SVGA_3D_CMD_SETTEXTURESTATE SVGA_3D_CMD_BASE + 11
|
|
+#define SVGA_3D_CMD_SETMATERIAL SVGA_3D_CMD_BASE + 12
|
|
+#define SVGA_3D_CMD_SETLIGHTDATA SVGA_3D_CMD_BASE + 13
|
|
+#define SVGA_3D_CMD_SETLIGHTENABLED SVGA_3D_CMD_BASE + 14
|
|
+#define SVGA_3D_CMD_SETVIEWPORT SVGA_3D_CMD_BASE + 15
|
|
+#define SVGA_3D_CMD_SETCLIPPLANE SVGA_3D_CMD_BASE + 16
|
|
+#define SVGA_3D_CMD_CLEAR SVGA_3D_CMD_BASE + 17
|
|
+#define SVGA_3D_CMD_PRESENT SVGA_3D_CMD_BASE + 18 // Deprecated
|
|
+#define SVGA_3D_CMD_SHADER_DEFINE SVGA_3D_CMD_BASE + 19
|
|
+#define SVGA_3D_CMD_SHADER_DESTROY SVGA_3D_CMD_BASE + 20
|
|
+#define SVGA_3D_CMD_SET_SHADER SVGA_3D_CMD_BASE + 21
|
|
+#define SVGA_3D_CMD_SET_SHADER_CONST SVGA_3D_CMD_BASE + 22
|
|
+#define SVGA_3D_CMD_DRAW_PRIMITIVES SVGA_3D_CMD_BASE + 23
|
|
+#define SVGA_3D_CMD_SETSCISSORRECT SVGA_3D_CMD_BASE + 24
|
|
+#define SVGA_3D_CMD_BEGIN_QUERY SVGA_3D_CMD_BASE + 25
|
|
+#define SVGA_3D_CMD_END_QUERY SVGA_3D_CMD_BASE + 26
|
|
+#define SVGA_3D_CMD_WAIT_FOR_QUERY SVGA_3D_CMD_BASE + 27
|
|
+#define SVGA_3D_CMD_PRESENT_READBACK SVGA_3D_CMD_BASE + 28 // Deprecated
|
|
+#define SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN SVGA_3D_CMD_BASE + 29
|
|
+#define SVGA_3D_CMD_MAX SVGA_3D_CMD_BASE + 30
|
|
+
|
|
+#define SVGA_3D_CMD_FUTURE_MAX 2000
|
|
+
|
|
+/*
|
|
+ * Common substructures used in multiple FIFO commands:
|
|
+ */
|
|
+
|
|
+typedef struct {
|
|
+ union {
|
|
+ struct {
|
|
+ uint16 function; // SVGA3dFogFunction
|
|
+ uint8 type; // SVGA3dFogType
|
|
+ uint8 base; // SVGA3dFogBase
|
|
+ };
|
|
+ uint32 uintValue;
|
|
+ };
|
|
+} SVGA3dFogMode;
|
|
+
|
|
+/*
|
|
+ * Uniquely identify one image (a 1D/2D/3D array) from a surface. This
|
|
+ * is a surface ID as well as face/mipmap indices.
|
|
+ */
|
|
+
|
|
+typedef
|
|
+struct SVGA3dSurfaceImageId {
|
|
+ uint32 sid;
|
|
+ uint32 face;
|
|
+ uint32 mipmap;
|
|
+} SVGA3dSurfaceImageId;
|
|
+
|
|
+typedef
|
|
+struct SVGA3dGuestImage {
|
|
+ SVGAGuestPtr ptr;
|
|
+
|
|
+ /*
|
|
+ * A note on interpretation of pitch: This value of pitch is the
|
|
+ * number of bytes between vertically adjacent image
|
|
+ * blocks. Normally this is the number of bytes between the first
|
|
+ * pixel of two adjacent scanlines. With compressed textures,
|
|
+ * however, this may represent the number of bytes between
|
|
+ * compression blocks rather than between rows of pixels.
|
|
+ *
|
|
+ * XXX: Compressed textures currently must be tightly packed in guest memory.
|
|
+ *
|
|
+ * If the image is 1-dimensional, pitch is ignored.
|
|
+ *
|
|
+ * If 'pitch' is zero, the SVGA3D device calculates a pitch value
|
|
+ * assuming each row of blocks is tightly packed.
|
|
+ */
|
|
+ uint32 pitch;
|
|
+} SVGA3dGuestImage;
|
|
+
|
|
+
|
|
+/*
|
|
+ * FIFO command format definitions:
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * The data size header following cmdNum for every 3d command
|
|
+ */
|
|
+typedef
|
|
+struct {
|
|
+ uint32 id;
|
|
+ uint32 size;
|
|
+} SVGA3dCmdHeader;
|
|
+
|
|
+/*
|
|
+ * A surface is a hierarchy of host VRAM surfaces: 1D, 2D, or 3D, with
|
|
+ * optional mipmaps and cube faces.
|
|
+ */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 width;
|
|
+ uint32 height;
|
|
+ uint32 depth;
|
|
+} SVGA3dSize;
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_SURFACE_CUBEMAP = (1 << 0),
|
|
+ SVGA3D_SURFACE_HINT_STATIC = (1 << 1),
|
|
+ SVGA3D_SURFACE_HINT_DYNAMIC = (1 << 2),
|
|
+ SVGA3D_SURFACE_HINT_INDEXBUFFER = (1 << 3),
|
|
+ SVGA3D_SURFACE_HINT_VERTEXBUFFER = (1 << 4),
|
|
+ SVGA3D_SURFACE_HINT_TEXTURE = (1 << 5),
|
|
+ SVGA3D_SURFACE_HINT_RENDERTARGET = (1 << 6),
|
|
+ SVGA3D_SURFACE_HINT_DEPTHSTENCIL = (1 << 7),
|
|
+ SVGA3D_SURFACE_HINT_WRITEONLY = (1 << 8),
|
|
+} SVGA3dSurfaceFlags;
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 numMipLevels;
|
|
+} SVGA3dSurfaceFace;
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 sid;
|
|
+ SVGA3dSurfaceFlags surfaceFlags;
|
|
+ SVGA3dSurfaceFormat format;
|
|
+ SVGA3dSurfaceFace face[SVGA3D_MAX_SURFACE_FACES];
|
|
+ /*
|
|
+ * Followed by an SVGA3dSize structure for each mip level in each face.
|
|
+ *
|
|
+ * A note on surface sizes: Sizes are always specified in pixels,
|
|
+ * even if the true surface size is not a multiple of the minimum
|
|
+ * block size of the surface's format. For example, a 3x3x1 DXT1
|
|
+ * compressed texture would actually be stored as a 4x4x1 image in
|
|
+ * memory.
|
|
+ */
|
|
+} SVGA3dCmdDefineSurface; /* SVGA_3D_CMD_SURFACE_DEFINE */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 sid;
|
|
+} SVGA3dCmdDestroySurface; /* SVGA_3D_CMD_SURFACE_DESTROY */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 cid;
|
|
+} SVGA3dCmdDefineContext; /* SVGA_3D_CMD_CONTEXT_DEFINE */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 cid;
|
|
+} SVGA3dCmdDestroyContext; /* SVGA_3D_CMD_CONTEXT_DESTROY */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 cid;
|
|
+ SVGA3dClearFlag clearFlag;
|
|
+ uint32 color;
|
|
+ float depth;
|
|
+ uint32 stencil;
|
|
+ /* Followed by variable number of SVGA3dRect structures */
|
|
+} SVGA3dCmdClear; /* SVGA_3D_CMD_CLEAR */
|
|
+
|
|
+typedef
|
|
+struct SVGA3dCopyRect {
|
|
+ uint32 x;
|
|
+ uint32 y;
|
|
+ uint32 w;
|
|
+ uint32 h;
|
|
+ uint32 srcx;
|
|
+ uint32 srcy;
|
|
+} SVGA3dCopyRect;
|
|
+
|
|
+typedef
|
|
+struct SVGA3dCopyBox {
|
|
+ uint32 x;
|
|
+ uint32 y;
|
|
+ uint32 z;
|
|
+ uint32 w;
|
|
+ uint32 h;
|
|
+ uint32 d;
|
|
+ uint32 srcx;
|
|
+ uint32 srcy;
|
|
+ uint32 srcz;
|
|
+} SVGA3dCopyBox;
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 x;
|
|
+ uint32 y;
|
|
+ uint32 w;
|
|
+ uint32 h;
|
|
+} SVGA3dRect;
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 x;
|
|
+ uint32 y;
|
|
+ uint32 z;
|
|
+ uint32 w;
|
|
+ uint32 h;
|
|
+ uint32 d;
|
|
+} SVGA3dBox;
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 x;
|
|
+ uint32 y;
|
|
+ uint32 z;
|
|
+} SVGA3dPoint;
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ SVGA3dLightType type;
|
|
+ SVGA3dBool inWorldSpace;
|
|
+ float diffuse[4];
|
|
+ float specular[4];
|
|
+ float ambient[4];
|
|
+ float position[4];
|
|
+ float direction[4];
|
|
+ float range;
|
|
+ float falloff;
|
|
+ float attenuation0;
|
|
+ float attenuation1;
|
|
+ float attenuation2;
|
|
+ float theta;
|
|
+ float phi;
|
|
+} SVGA3dLightData;
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 sid;
|
|
+ /* Followed by variable number of SVGA3dCopyRect structures */
|
|
+} SVGA3dCmdPresent; /* SVGA_3D_CMD_PRESENT */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ SVGA3dRenderStateName state;
|
|
+ union {
|
|
+ uint32 uintValue;
|
|
+ float floatValue;
|
|
+ };
|
|
+} SVGA3dRenderState;
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 cid;
|
|
+ /* Followed by variable number of SVGA3dRenderState structures */
|
|
+} SVGA3dCmdSetRenderState; /* SVGA_3D_CMD_SETRENDERSTATE */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 cid;
|
|
+ SVGA3dRenderTargetType type;
|
|
+ SVGA3dSurfaceImageId target;
|
|
+} SVGA3dCmdSetRenderTarget; /* SVGA_3D_CMD_SETRENDERTARGET */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ SVGA3dSurfaceImageId src;
|
|
+ SVGA3dSurfaceImageId dest;
|
|
+ /* Followed by variable number of SVGA3dCopyBox structures */
|
|
+} SVGA3dCmdSurfaceCopy; /* SVGA_3D_CMD_SURFACE_COPY */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ SVGA3dSurfaceImageId src;
|
|
+ SVGA3dSurfaceImageId dest;
|
|
+ SVGA3dBox boxSrc;
|
|
+ SVGA3dBox boxDest;
|
|
+ SVGA3dStretchBltMode mode;
|
|
+} SVGA3dCmdSurfaceStretchBlt; /* SVGA_3D_CMD_SURFACE_STRETCHBLT */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ /*
|
|
+ * If the discard flag is present in a surface DMA operation, the host may
|
|
+ * discard the contents of the current mipmap level and face of the target
|
|
+ * surface before applying the surface DMA contents.
|
|
+ */
|
|
+ uint32 discard : 1;
|
|
+
|
|
+ /*
|
|
+ * If the unsynchronized flag is present, the host may perform this upload
|
|
+ * without syncing to pending reads on this surface.
|
|
+ */
|
|
+ uint32 unsynchronized : 1;
|
|
+
|
|
+ /*
|
|
+ * Guests *MUST* set the reserved bits to 0 before submitting the command
|
|
+ * suffix as future flags may occupy these bits.
|
|
+ */
|
|
+ uint32 reserved : 30;
|
|
+} SVGA3dSurfaceDMAFlags;
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ SVGA3dGuestImage guest;
|
|
+ SVGA3dSurfaceImageId host;
|
|
+ SVGA3dTransferType transfer;
|
|
+ /*
|
|
+ * Followed by variable number of SVGA3dCopyBox structures. For consistency
|
|
+ * in all clipping logic and coordinate translation, we define the
|
|
+ * "source" in each copyBox as the guest image and the
|
|
+ * "destination" as the host image, regardless of transfer
|
|
+ * direction.
|
|
+ *
|
|
+ * For efficiency, the SVGA3D device is free to copy more data than
|
|
+ * specified. For example, it may round copy boxes outwards such
|
|
+ * that they lie on particular alignment boundaries.
|
|
+ */
|
|
+} SVGA3dCmdSurfaceDMA; /* SVGA_3D_CMD_SURFACE_DMA */
|
|
+
|
|
+/*
|
|
+ * SVGA3dCmdSurfaceDMASuffix --
|
|
+ *
|
|
+ * This is a command suffix that will appear after a SurfaceDMA command in
|
|
+ * the FIFO. It contains some extra information that hosts may use to
|
|
+ * optimize performance or protect the guest. This suffix exists to preserve
|
|
+ * backwards compatibility while also allowing for new functionality to be
|
|
+ * implemented.
|
|
+ */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 suffixSize;
|
|
+
|
|
+ /*
|
|
+ * The maximum offset is used to determine the maximum offset from the
|
|
+ * guestPtr base address that will be accessed or written to during this
|
|
+ * surfaceDMA. If the suffix is supported, the host will respect this
|
|
+ * boundary while performing surface DMAs.
|
|
+ *
|
|
+ * Defaults to MAX_UINT32
|
|
+ */
|
|
+ uint32 maximumOffset;
|
|
+
|
|
+ /*
|
|
+ * A set of flags that describes optimizations that the host may perform
|
|
+ * while performing this surface DMA operation. The guest should never rely
|
|
+ * on behaviour that is different when these flags are set for correctness.
|
|
+ *
|
|
+ * Defaults to 0
|
|
+ */
|
|
+ SVGA3dSurfaceDMAFlags flags;
|
|
+} SVGA3dCmdSurfaceDMASuffix;
|
|
+
|
|
+/*
|
|
+ * SVGA_3D_CMD_DRAW_PRIMITIVES --
|
|
+ *
|
|
+ * This command is the SVGA3D device's generic drawing entry point.
|
|
+ * It can draw multiple ranges of primitives, optionally using an
|
|
+ * index buffer, using an arbitrary collection of vertex buffers.
|
|
+ *
|
|
+ * Each SVGA3dVertexDecl defines a distinct vertex array to bind
|
|
+ * during this draw call. The declarations specify which surface
|
|
+ * the vertex data lives in, what that vertex data is used for,
|
|
+ * and how to interpret it.
|
|
+ *
|
|
+ * Each SVGA3dPrimitiveRange defines a collection of primitives
|
|
+ * to render using the same vertex arrays. An index buffer is
|
|
+ * optional.
|
|
+ */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ /*
|
|
+ * A range hint is an optional specification for the range of indices
|
|
+ * in an SVGA3dArray that will be used. If 'last' is zero, it is assumed
|
|
+ * that the entire array will be used.
|
|
+ *
|
|
+ * These are only hints. The SVGA3D device may use them for
|
|
+ * performance optimization if possible, but it's also allowed to
|
|
+ * ignore these values.
|
|
+ */
|
|
+ uint32 first;
|
|
+ uint32 last;
|
|
+} SVGA3dArrayRangeHint;
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ /*
|
|
+ * Define the origin and shape of a vertex or index array. Both
|
|
+ * 'offset' and 'stride' are in bytes. The provided surface will be
|
|
+ * reinterpreted as a flat array of bytes in the same format used
|
|
+ * by surface DMA operations. To avoid unnecessary conversions, the
|
|
+ * surface should be created with the SVGA3D_BUFFER format.
|
|
+ *
|
|
+ * Index 0 in the array starts 'offset' bytes into the surface.
|
|
+ * Index 1 begins at byte 'offset + stride', etc. Array indices may
|
|
+ * not be negative.
|
|
+ */
|
|
+ uint32 surfaceId;
|
|
+ uint32 offset;
|
|
+ uint32 stride;
|
|
+} SVGA3dArray;
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ /*
|
|
+ * Describe a vertex array's data type, and define how it is to be
|
|
+ * used by the fixed function pipeline or the vertex shader. It
|
|
+ * isn't useful to have two VertexDecls with the same
|
|
+ * VertexArrayIdentity in one draw call.
|
|
+ */
|
|
+ SVGA3dDeclType type;
|
|
+ SVGA3dDeclMethod method;
|
|
+ SVGA3dDeclUsage usage;
|
|
+ uint32 usageIndex;
|
|
+} SVGA3dVertexArrayIdentity;
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ SVGA3dVertexArrayIdentity identity;
|
|
+ SVGA3dArray array;
|
|
+ SVGA3dArrayRangeHint rangeHint;
|
|
+} SVGA3dVertexDecl;
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ /*
|
|
+ * Define a group of primitives to render, from sequential indices.
|
|
+ *
|
|
+ * The value of 'primitiveType' and 'primitiveCount' imply the
|
|
+ * total number of vertices that will be rendered.
|
|
+ */
|
|
+ SVGA3dPrimitiveType primType;
|
|
+ uint32 primitiveCount;
|
|
+
|
|
+ /*
|
|
+ * Optional index buffer. If indexArray.surfaceId is
|
|
+ * SVGA3D_INVALID_ID, we render without an index buffer. Rendering
|
|
+ * without an index buffer is identical to rendering with an index
|
|
+ * buffer containing the sequence [0, 1, 2, 3, ...].
|
|
+ *
|
|
+ * If an index buffer is in use, indexWidth specifies the width in
|
|
+ * bytes of each index value. It must be less than or equal to
|
|
+ * indexArray.stride.
|
|
+ *
|
|
+ * (Currently, the SVGA3D device requires index buffers to be tightly
|
|
+ * packed. In other words, indexWidth == indexArray.stride)
|
|
+ */
|
|
+ SVGA3dArray indexArray;
|
|
+ uint32 indexWidth;
|
|
+
|
|
+ /*
|
|
+ * Optional index bias. This number is added to all indices from
|
|
+ * indexArray before they are used as vertex array indices. This
|
|
+ * can be used in multiple ways:
|
|
+ *
|
|
+ * - When not using an indexArray, this bias can be used to
|
|
+ * specify where in the vertex arrays to begin rendering.
|
|
+ *
|
|
+ * - A positive number here is equivalent to increasing the
|
|
+ * offset in each vertex array.
|
|
+ *
|
|
+ * - A negative number can be used to render using a small
|
|
+ * vertex array and an index buffer that contains large
|
|
+ * values. This may be used by some applications that
|
|
+ * crop a vertex buffer without modifying their index
|
|
+ * buffer.
|
|
+ *
|
|
+ * Note that rendering with a negative bias value may be slower and
|
|
+ * use more memory than rendering with a positive or zero bias.
|
|
+ */
|
|
+ int32 indexBias;
|
|
+} SVGA3dPrimitiveRange;
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 cid;
|
|
+ uint32 numVertexDecls;
|
|
+ uint32 numRanges;
|
|
+
|
|
+ /*
|
|
+ * There are two variable size arrays after the
|
|
+ * SVGA3dCmdDrawPrimitives structure. In order,
|
|
+ * they are:
|
|
+ *
|
|
+ * 1. SVGA3dVertexDecl, quantity 'numVertexDecls'
|
|
+ * 2. SVGA3dPrimitiveRange, quantity 'numRanges'
|
|
+ * 3. Optionally, SVGA3dVertexDivisor, quantity 'numVertexDecls' (contains
|
|
+ * the frequency divisor for this the corresponding vertex decl)
|
|
+ */
|
|
+} SVGA3dCmdDrawPrimitives; /* SVGA_3D_CMD_DRAWPRIMITIVES */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 stage;
|
|
+ SVGA3dTextureStateName name;
|
|
+ union {
|
|
+ uint32 value;
|
|
+ float floatValue;
|
|
+ };
|
|
+} SVGA3dTextureState;
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 cid;
|
|
+ /* Followed by variable number of SVGA3dTextureState structures */
|
|
+} SVGA3dCmdSetTextureState; /* SVGA_3D_CMD_SETTEXTURESTATE */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 cid;
|
|
+ SVGA3dTransformType type;
|
|
+ float matrix[16];
|
|
+} SVGA3dCmdSetTransform; /* SVGA_3D_CMD_SETTRANSFORM */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ float min;
|
|
+ float max;
|
|
+} SVGA3dZRange;
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 cid;
|
|
+ SVGA3dZRange zRange;
|
|
+} SVGA3dCmdSetZRange; /* SVGA_3D_CMD_SETZRANGE */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ float diffuse[4];
|
|
+ float ambient[4];
|
|
+ float specular[4];
|
|
+ float emissive[4];
|
|
+ float shininess;
|
|
+} SVGA3dMaterial;
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 cid;
|
|
+ SVGA3dFace face;
|
|
+ SVGA3dMaterial material;
|
|
+} SVGA3dCmdSetMaterial; /* SVGA_3D_CMD_SETMATERIAL */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 cid;
|
|
+ uint32 index;
|
|
+ SVGA3dLightData data;
|
|
+} SVGA3dCmdSetLightData; /* SVGA_3D_CMD_SETLIGHTDATA */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 cid;
|
|
+ uint32 index;
|
|
+ uint32 enabled;
|
|
+} SVGA3dCmdSetLightEnabled; /* SVGA_3D_CMD_SETLIGHTENABLED */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 cid;
|
|
+ SVGA3dRect rect;
|
|
+} SVGA3dCmdSetViewport; /* SVGA_3D_CMD_SETVIEWPORT */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 cid;
|
|
+ SVGA3dRect rect;
|
|
+} SVGA3dCmdSetScissorRect; /* SVGA_3D_CMD_SETSCISSORRECT */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 cid;
|
|
+ uint32 index;
|
|
+ float plane[4];
|
|
+} SVGA3dCmdSetClipPlane; /* SVGA_3D_CMD_SETCLIPPLANE */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 cid;
|
|
+ uint32 shid;
|
|
+ SVGA3dShaderType type;
|
|
+ /* Followed by variable number of DWORDs for shader bycode */
|
|
+} SVGA3dCmdDefineShader; /* SVGA_3D_CMD_SHADER_DEFINE */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 cid;
|
|
+ uint32 shid;
|
|
+ SVGA3dShaderType type;
|
|
+} SVGA3dCmdDestroyShader; /* SVGA_3D_CMD_SHADER_DESTROY */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 cid;
|
|
+ uint32 reg; /* register number */
|
|
+ SVGA3dShaderType type;
|
|
+ SVGA3dShaderConstType ctype;
|
|
+ uint32 values[4];
|
|
+} SVGA3dCmdSetShaderConst; /* SVGA_3D_CMD_SET_SHADER_CONST */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 cid;
|
|
+ SVGA3dShaderType type;
|
|
+ uint32 shid;
|
|
+} SVGA3dCmdSetShader; /* SVGA_3D_CMD_SET_SHADER */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 cid;
|
|
+ SVGA3dQueryType type;
|
|
+} SVGA3dCmdBeginQuery; /* SVGA_3D_CMD_BEGIN_QUERY */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 cid;
|
|
+ SVGA3dQueryType type;
|
|
+ SVGAGuestPtr guestResult; /* Points to an SVGA3dQueryResult structure */
|
|
+} SVGA3dCmdEndQuery; /* SVGA_3D_CMD_END_QUERY */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 cid; /* Same parameters passed to END_QUERY */
|
|
+ SVGA3dQueryType type;
|
|
+ SVGAGuestPtr guestResult;
|
|
+} SVGA3dCmdWaitForQuery; /* SVGA_3D_CMD_WAIT_FOR_QUERY */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 totalSize; /* Set by guest before query is ended. */
|
|
+ SVGA3dQueryState state; /* Set by host or guest. See SVGA3dQueryState. */
|
|
+ union { /* Set by host on exit from PENDING state */
|
|
+ uint32 result32;
|
|
+ };
|
|
+} SVGA3dQueryResult;
|
|
+
|
|
+/*
|
|
+ * SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN --
|
|
+ *
|
|
+ * This is a blit from an SVGA3D surface to a Screen Object. Just
|
|
+ * like GMR-to-screen blits, this blit may be directed at a
|
|
+ * specific screen or to the virtual coordinate space.
|
|
+ *
|
|
+ * The blit copies from a rectangular region of an SVGA3D surface
|
|
+ * image to a rectangular region of a screen or screens.
|
|
+ *
|
|
+ * This command takes an optional variable-length list of clipping
|
|
+ * rectangles after the body of the command. If no rectangles are
|
|
+ * specified, there is no clipping region. The entire destRect is
|
|
+ * drawn to. If one or more rectangles are included, they describe
|
|
+ * a clipping region. The clip rectangle coordinates are measured
|
|
+ * relative to the top-left corner of destRect.
|
|
+ *
|
|
+ * This clipping region serves multiple purposes:
|
|
+ *
|
|
+ * - It can be used to perform an irregularly shaped blit more
|
|
+ * efficiently than by issuing many separate blit commands.
|
|
+ *
|
|
+ * - It is equivalent to allowing blits with non-integer
|
|
+ * source coordinates. You could blit just one half-pixel
|
|
+ * of a source, for example, by specifying a larger
|
|
+ * destination rectangle than you need, then removing
|
|
+ * part of it using a clip rectangle.
|
|
+ *
|
|
+ * Availability:
|
|
+ * SVGA_FIFO_CAP_SCREEN_OBJECT
|
|
+ *
|
|
+ * Limitations:
|
|
+ *
|
|
+ * - Currently, no backend supports blits from a mipmap or face
|
|
+ * other than the first one.
|
|
+ */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ SVGA3dSurfaceImageId srcImage;
|
|
+ SVGASignedRect srcRect;
|
|
+ uint32 destScreenId; /* Screen ID or SVGA_ID_INVALID for virt. coords */
|
|
+ SVGASignedRect destRect; /* Supports scaling if src/rest different size */
|
|
+ /* Clipping: zero or more SVGASignedRects follow */
|
|
+} SVGA3dCmdBlitSurfaceToScreen; /* SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN */
|
|
+
|
|
+
|
|
+/*
|
|
+ * Capability query index.
|
|
+ *
|
|
+ * Notes:
|
|
+ *
|
|
+ * 1. SVGA3D_DEVCAP_MAX_TEXTURES reflects the maximum number of
|
|
+ * fixed-function texture units available. Each of these units
|
|
+ * work in both FFP and Shader modes, and they support texture
|
|
+ * transforms and texture coordinates. The host may have additional
|
|
+ * texture image units that are only usable with shaders.
|
|
+ *
|
|
+ * 2. The BUFFER_FORMAT capabilities are deprecated, and they always
|
|
+ * return TRUE. Even on physical hardware that does not support
|
|
+ * these formats natively, the SVGA3D device will provide an emulation
|
|
+ * which should be invisible to the guest OS.
|
|
+ *
|
|
+ * In general, the SVGA3D device should support any operation on
|
|
+ * any surface format, it just may perform some of these
|
|
+ * operations in software depending on the capabilities of the
|
|
+ * available physical hardware.
|
|
+ *
|
|
+ * XXX: In the future, we will add capabilities that describe in
|
|
+ * detail what formats are supported in hardware for what kinds
|
|
+ * of operations.
|
|
+ */
|
|
+
|
|
+typedef enum {
|
|
+ SVGA3D_DEVCAP_3D = 0,
|
|
+ SVGA3D_DEVCAP_MAX_LIGHTS = 1,
|
|
+ SVGA3D_DEVCAP_MAX_TEXTURES = 2, /* See note (1) */
|
|
+ SVGA3D_DEVCAP_MAX_CLIP_PLANES = 3,
|
|
+ SVGA3D_DEVCAP_VERTEX_SHADER_VERSION = 4,
|
|
+ SVGA3D_DEVCAP_VERTEX_SHADER = 5,
|
|
+ SVGA3D_DEVCAP_FRAGMENT_SHADER_VERSION = 6,
|
|
+ SVGA3D_DEVCAP_FRAGMENT_SHADER = 7,
|
|
+ SVGA3D_DEVCAP_MAX_RENDER_TARGETS = 8,
|
|
+ SVGA3D_DEVCAP_S23E8_TEXTURES = 9,
|
|
+ SVGA3D_DEVCAP_S10E5_TEXTURES = 10,
|
|
+ SVGA3D_DEVCAP_MAX_FIXED_VERTEXBLEND = 11,
|
|
+ SVGA3D_DEVCAP_D16_BUFFER_FORMAT = 12, /* See note (2) */
|
|
+ SVGA3D_DEVCAP_D24S8_BUFFER_FORMAT = 13, /* See note (2) */
|
|
+ SVGA3D_DEVCAP_D24X8_BUFFER_FORMAT = 14, /* See note (2) */
|
|
+ SVGA3D_DEVCAP_QUERY_TYPES = 15,
|
|
+ SVGA3D_DEVCAP_TEXTURE_GRADIENT_SAMPLING = 16,
|
|
+ SVGA3D_DEVCAP_MAX_POINT_SIZE = 17,
|
|
+ SVGA3D_DEVCAP_MAX_SHADER_TEXTURES = 18,
|
|
+ SVGA3D_DEVCAP_MAX_TEXTURE_WIDTH = 19,
|
|
+ SVGA3D_DEVCAP_MAX_TEXTURE_HEIGHT = 20,
|
|
+ SVGA3D_DEVCAP_MAX_VOLUME_EXTENT = 21,
|
|
+ SVGA3D_DEVCAP_MAX_TEXTURE_REPEAT = 22,
|
|
+ SVGA3D_DEVCAP_MAX_TEXTURE_ASPECT_RATIO = 23,
|
|
+ SVGA3D_DEVCAP_MAX_TEXTURE_ANISOTROPY = 24,
|
|
+ SVGA3D_DEVCAP_MAX_PRIMITIVE_COUNT = 25,
|
|
+ SVGA3D_DEVCAP_MAX_VERTEX_INDEX = 26,
|
|
+ SVGA3D_DEVCAP_MAX_VERTEX_SHADER_INSTRUCTIONS = 27,
|
|
+ SVGA3D_DEVCAP_MAX_FRAGMENT_SHADER_INSTRUCTIONS = 28,
|
|
+ SVGA3D_DEVCAP_MAX_VERTEX_SHADER_TEMPS = 29,
|
|
+ SVGA3D_DEVCAP_MAX_FRAGMENT_SHADER_TEMPS = 30,
|
|
+ SVGA3D_DEVCAP_TEXTURE_OPS = 31,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_X8R8G8B8 = 32,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_A8R8G8B8 = 33,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_A2R10G10B10 = 34,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_X1R5G5B5 = 35,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_A1R5G5B5 = 36,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_A4R4G4B4 = 37,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_R5G6B5 = 38,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_LUMINANCE16 = 39,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_LUMINANCE8_ALPHA8 = 40,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_ALPHA8 = 41,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_LUMINANCE8 = 42,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_Z_D16 = 43,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_Z_D24S8 = 44,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_Z_D24X8 = 45,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_DXT1 = 46,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_DXT2 = 47,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_DXT3 = 48,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_DXT4 = 49,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_DXT5 = 50,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_BUMPX8L8V8U8 = 51,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_A2W10V10U10 = 52,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_BUMPU8V8 = 53,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_Q8W8V8U8 = 54,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_CxV8U8 = 55,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_R_S10E5 = 56,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_R_S23E8 = 57,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_RG_S10E5 = 58,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_RG_S23E8 = 59,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_ARGB_S10E5 = 60,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_ARGB_S23E8 = 61,
|
|
+ SVGA3D_DEVCAP_MAX_VERTEX_SHADER_TEXTURES = 63,
|
|
+
|
|
+ /*
|
|
+ * Note that MAX_SIMULTANEOUS_RENDER_TARGETS is a maximum count of color
|
|
+ * render targets. This does no include the depth or stencil targets.
|
|
+ */
|
|
+ SVGA3D_DEVCAP_MAX_SIMULTANEOUS_RENDER_TARGETS = 64,
|
|
+
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_V16U16 = 65,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_G16R16 = 66,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_A16B16G16R16 = 67,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_UYVY = 68,
|
|
+ SVGA3D_DEVCAP_SURFACEFMT_YUY2 = 69,
|
|
+
|
|
+ /*
|
|
+ * Don't add new caps into the previous section; the values in this
|
|
+ * enumeration must not change. You can put new values right before
|
|
+ * SVGA3D_DEVCAP_MAX.
|
|
+ */
|
|
+ SVGA3D_DEVCAP_MAX /* This must be the last index. */
|
|
+} SVGA3dDevCapIndex;
|
|
+
|
|
+typedef union {
|
|
+ Bool b;
|
|
+ uint32 u;
|
|
+ int32 i;
|
|
+ float f;
|
|
+} SVGA3dDevCapResult;
|
|
+
|
|
+#endif /* _SVGA3D_REG_H_ */
|
|
diff --git a/drivers/gpu/drm/vmwgfx/svga_escape.h b/drivers/gpu/drm/vmwgfx/svga_escape.h
|
|
new file mode 100644
|
|
index 0000000..7b85e9b
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/vmwgfx/svga_escape.h
|
|
@@ -0,0 +1,89 @@
|
|
+/**********************************************************
|
|
+ * Copyright 2007-2009 VMware, Inc. All rights reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person
|
|
+ * obtaining a copy of this software and associated documentation
|
|
+ * files (the "Software"), to deal in the Software without
|
|
+ * restriction, including without limitation the rights to use, copy,
|
|
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
+ * of the Software, and to permit persons to whom the Software is
|
|
+ * furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice shall be
|
|
+ * included in all copies or substantial portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
+ * SOFTWARE.
|
|
+ *
|
|
+ **********************************************************/
|
|
+
|
|
+/*
|
|
+ * svga_escape.h --
|
|
+ *
|
|
+ * Definitions for our own (vendor-specific) SVGA Escape commands.
|
|
+ */
|
|
+
|
|
+#ifndef _SVGA_ESCAPE_H_
|
|
+#define _SVGA_ESCAPE_H_
|
|
+
|
|
+
|
|
+/*
|
|
+ * Namespace IDs for the escape command
|
|
+ */
|
|
+
|
|
+#define SVGA_ESCAPE_NSID_VMWARE 0x00000000
|
|
+#define SVGA_ESCAPE_NSID_DEVEL 0xFFFFFFFF
|
|
+
|
|
+
|
|
+/*
|
|
+ * Within SVGA_ESCAPE_NSID_VMWARE, we multiplex commands according to
|
|
+ * the first DWORD of escape data (after the nsID and size). As a
|
|
+ * guideline we're using the high word and low word as a major and
|
|
+ * minor command number, respectively.
|
|
+ *
|
|
+ * Major command number allocation:
|
|
+ *
|
|
+ * 0000: Reserved
|
|
+ * 0001: SVGA_ESCAPE_VMWARE_LOG (svga_binary_logger.h)
|
|
+ * 0002: SVGA_ESCAPE_VMWARE_VIDEO (svga_overlay.h)
|
|
+ * 0003: SVGA_ESCAPE_VMWARE_HINT (svga_escape.h)
|
|
+ */
|
|
+
|
|
+#define SVGA_ESCAPE_VMWARE_MAJOR_MASK 0xFFFF0000
|
|
+
|
|
+
|
|
+/*
|
|
+ * SVGA Hint commands.
|
|
+ *
|
|
+ * These escapes let the SVGA driver provide optional information to
|
|
+ * he host about the state of the guest or guest applications. The
|
|
+ * host can use these hints to make user interface or performance
|
|
+ * decisions.
|
|
+ *
|
|
+ * Notes:
|
|
+ *
|
|
+ * - SVGA_ESCAPE_VMWARE_HINT_FULLSCREEN is deprecated for guests
|
|
+ * that use the SVGA Screen Object extension. Instead of sending
|
|
+ * this escape, use the SVGA_SCREEN_FULLSCREEN_HINT flag on your
|
|
+ * Screen Object.
|
|
+ */
|
|
+
|
|
+#define SVGA_ESCAPE_VMWARE_HINT 0x00030000
|
|
+#define SVGA_ESCAPE_VMWARE_HINT_FULLSCREEN 0x00030001 // Deprecated
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 command;
|
|
+ uint32 fullscreen;
|
|
+ struct {
|
|
+ int32 x, y;
|
|
+ } monitorPosition;
|
|
+} SVGAEscapeHintFullscreen;
|
|
+
|
|
+#endif /* _SVGA_ESCAPE_H_ */
|
|
diff --git a/drivers/gpu/drm/vmwgfx/svga_overlay.h b/drivers/gpu/drm/vmwgfx/svga_overlay.h
|
|
new file mode 100644
|
|
index 0000000..f753d73
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/vmwgfx/svga_overlay.h
|
|
@@ -0,0 +1,201 @@
|
|
+/**********************************************************
|
|
+ * Copyright 2007-2009 VMware, Inc. All rights reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person
|
|
+ * obtaining a copy of this software and associated documentation
|
|
+ * files (the "Software"), to deal in the Software without
|
|
+ * restriction, including without limitation the rights to use, copy,
|
|
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
+ * of the Software, and to permit persons to whom the Software is
|
|
+ * furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice shall be
|
|
+ * included in all copies or substantial portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
+ * SOFTWARE.
|
|
+ *
|
|
+ **********************************************************/
|
|
+
|
|
+/*
|
|
+ * svga_overlay.h --
|
|
+ *
|
|
+ * Definitions for video-overlay support.
|
|
+ */
|
|
+
|
|
+#ifndef _SVGA_OVERLAY_H_
|
|
+#define _SVGA_OVERLAY_H_
|
|
+
|
|
+#include "svga_reg.h"
|
|
+
|
|
+/*
|
|
+ * Video formats we support
|
|
+ */
|
|
+
|
|
+#define VMWARE_FOURCC_YV12 0x32315659 // 'Y' 'V' '1' '2'
|
|
+#define VMWARE_FOURCC_YUY2 0x32595559 // 'Y' 'U' 'Y' '2'
|
|
+#define VMWARE_FOURCC_UYVY 0x59565955 // 'U' 'Y' 'V' 'Y'
|
|
+
|
|
+typedef enum {
|
|
+ SVGA_OVERLAY_FORMAT_INVALID = 0,
|
|
+ SVGA_OVERLAY_FORMAT_YV12 = VMWARE_FOURCC_YV12,
|
|
+ SVGA_OVERLAY_FORMAT_YUY2 = VMWARE_FOURCC_YUY2,
|
|
+ SVGA_OVERLAY_FORMAT_UYVY = VMWARE_FOURCC_UYVY,
|
|
+} SVGAOverlayFormat;
|
|
+
|
|
+#define SVGA_VIDEO_COLORKEY_MASK 0x00ffffff
|
|
+
|
|
+#define SVGA_ESCAPE_VMWARE_VIDEO 0x00020000
|
|
+
|
|
+#define SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS 0x00020001
|
|
+ /* FIFO escape layout:
|
|
+ * Type, Stream Id, (Register Id, Value) pairs */
|
|
+
|
|
+#define SVGA_ESCAPE_VMWARE_VIDEO_FLUSH 0x00020002
|
|
+ /* FIFO escape layout:
|
|
+ * Type, Stream Id */
|
|
+
|
|
+typedef
|
|
+struct SVGAEscapeVideoSetRegs {
|
|
+ struct {
|
|
+ uint32 cmdType;
|
|
+ uint32 streamId;
|
|
+ } header;
|
|
+
|
|
+ // May include zero or more items.
|
|
+ struct {
|
|
+ uint32 registerId;
|
|
+ uint32 value;
|
|
+ } items[1];
|
|
+} SVGAEscapeVideoSetRegs;
|
|
+
|
|
+typedef
|
|
+struct SVGAEscapeVideoFlush {
|
|
+ uint32 cmdType;
|
|
+ uint32 streamId;
|
|
+} SVGAEscapeVideoFlush;
|
|
+
|
|
+
|
|
+/*
|
|
+ * Struct definitions for the video overlay commands built on
|
|
+ * SVGAFifoCmdEscape.
|
|
+ */
|
|
+typedef
|
|
+struct {
|
|
+ uint32 command;
|
|
+ uint32 overlay;
|
|
+} SVGAFifoEscapeCmdVideoBase;
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ SVGAFifoEscapeCmdVideoBase videoCmd;
|
|
+} SVGAFifoEscapeCmdVideoFlush;
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ SVGAFifoEscapeCmdVideoBase videoCmd;
|
|
+ struct {
|
|
+ uint32 regId;
|
|
+ uint32 value;
|
|
+ } items[1];
|
|
+} SVGAFifoEscapeCmdVideoSetRegs;
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ SVGAFifoEscapeCmdVideoBase videoCmd;
|
|
+ struct {
|
|
+ uint32 regId;
|
|
+ uint32 value;
|
|
+ } items[SVGA_VIDEO_NUM_REGS];
|
|
+} SVGAFifoEscapeCmdVideoSetAllRegs;
|
|
+
|
|
+
|
|
+/*
|
|
+ *----------------------------------------------------------------------
|
|
+ *
|
|
+ * VMwareVideoGetAttributes --
|
|
+ *
|
|
+ * Computes the size, pitches and offsets for YUV frames.
|
|
+ *
|
|
+ * Results:
|
|
+ * TRUE on success; otherwise FALSE on failure.
|
|
+ *
|
|
+ * Side effects:
|
|
+ * Pitches and offsets for the given YUV frame are put in 'pitches'
|
|
+ * and 'offsets' respectively. They are both optional though.
|
|
+ *
|
|
+ *----------------------------------------------------------------------
|
|
+ */
|
|
+
|
|
+static inline bool
|
|
+VMwareVideoGetAttributes(const SVGAOverlayFormat format, // IN
|
|
+ uint32 *width, // IN / OUT
|
|
+ uint32 *height, // IN / OUT
|
|
+ uint32 *size, // OUT
|
|
+ uint32 *pitches, // OUT (optional)
|
|
+ uint32 *offsets) // OUT (optional)
|
|
+{
|
|
+ int tmp;
|
|
+
|
|
+ *width = (*width + 1) & ~1;
|
|
+
|
|
+ if (offsets) {
|
|
+ offsets[0] = 0;
|
|
+ }
|
|
+
|
|
+ switch (format) {
|
|
+ case VMWARE_FOURCC_YV12:
|
|
+ *height = (*height + 1) & ~1;
|
|
+ *size = (*width + 3) & ~3;
|
|
+
|
|
+ if (pitches) {
|
|
+ pitches[0] = *size;
|
|
+ }
|
|
+
|
|
+ *size *= *height;
|
|
+
|
|
+ if (offsets) {
|
|
+ offsets[1] = *size;
|
|
+ }
|
|
+
|
|
+ tmp = ((*width >> 1) + 3) & ~3;
|
|
+
|
|
+ if (pitches) {
|
|
+ pitches[1] = pitches[2] = tmp;
|
|
+ }
|
|
+
|
|
+ tmp *= (*height >> 1);
|
|
+ *size += tmp;
|
|
+
|
|
+ if (offsets) {
|
|
+ offsets[2] = *size;
|
|
+ }
|
|
+
|
|
+ *size += tmp;
|
|
+ break;
|
|
+
|
|
+ case VMWARE_FOURCC_YUY2:
|
|
+ case VMWARE_FOURCC_UYVY:
|
|
+ *size = *width * 2;
|
|
+
|
|
+ if (pitches) {
|
|
+ pitches[0] = *size;
|
|
+ }
|
|
+
|
|
+ *size *= *height;
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+#endif // _SVGA_OVERLAY_H_
|
|
diff --git a/drivers/gpu/drm/vmwgfx/svga_reg.h b/drivers/gpu/drm/vmwgfx/svga_reg.h
|
|
new file mode 100644
|
|
index 0000000..1b96c2e
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/vmwgfx/svga_reg.h
|
|
@@ -0,0 +1,1346 @@
|
|
+/**********************************************************
|
|
+ * Copyright 1998-2009 VMware, Inc. All rights reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person
|
|
+ * obtaining a copy of this software and associated documentation
|
|
+ * files (the "Software"), to deal in the Software without
|
|
+ * restriction, including without limitation the rights to use, copy,
|
|
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
+ * of the Software, and to permit persons to whom the Software is
|
|
+ * furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice shall be
|
|
+ * included in all copies or substantial portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
+ * SOFTWARE.
|
|
+ *
|
|
+ **********************************************************/
|
|
+
|
|
+/*
|
|
+ * svga_reg.h --
|
|
+ *
|
|
+ * Virtual hardware definitions for the VMware SVGA II device.
|
|
+ */
|
|
+
|
|
+#ifndef _SVGA_REG_H_
|
|
+#define _SVGA_REG_H_
|
|
+
|
|
+/*
|
|
+ * PCI device IDs.
|
|
+ */
|
|
+#define PCI_VENDOR_ID_VMWARE 0x15AD
|
|
+#define PCI_DEVICE_ID_VMWARE_SVGA2 0x0405
|
|
+
|
|
+/*
|
|
+ * Legal values for the SVGA_REG_CURSOR_ON register in old-fashioned
|
|
+ * cursor bypass mode. This is still supported, but no new guest
|
|
+ * drivers should use it.
|
|
+ */
|
|
+#define SVGA_CURSOR_ON_HIDE 0x0 /* Must be 0 to maintain backward compatibility */
|
|
+#define SVGA_CURSOR_ON_SHOW 0x1 /* Must be 1 to maintain backward compatibility */
|
|
+#define SVGA_CURSOR_ON_REMOVE_FROM_FB 0x2 /* Remove the cursor from the framebuffer because we need to see what's under it */
|
|
+#define SVGA_CURSOR_ON_RESTORE_TO_FB 0x3 /* Put the cursor back in the framebuffer so the user can see it */
|
|
+
|
|
+/*
|
|
+ * The maximum framebuffer size that can traced for e.g. guests in VESA mode.
|
|
+ * The changeMap in the monitor is proportional to this number. Therefore, we'd
|
|
+ * like to keep it as small as possible to reduce monitor overhead (using
|
|
+ * SVGA_VRAM_MAX_SIZE for this increases the size of the shared area by over
|
|
+ * 4k!).
|
|
+ *
|
|
+ * NB: For compatibility reasons, this value must be greater than 0xff0000.
|
|
+ * See bug 335072.
|
|
+ */
|
|
+#define SVGA_FB_MAX_TRACEABLE_SIZE 0x1000000
|
|
+
|
|
+#define SVGA_MAX_PSEUDOCOLOR_DEPTH 8
|
|
+#define SVGA_MAX_PSEUDOCOLORS (1 << SVGA_MAX_PSEUDOCOLOR_DEPTH)
|
|
+#define SVGA_NUM_PALETTE_REGS (3 * SVGA_MAX_PSEUDOCOLORS)
|
|
+
|
|
+#define SVGA_MAGIC 0x900000UL
|
|
+#define SVGA_MAKE_ID(ver) (SVGA_MAGIC << 8 | (ver))
|
|
+
|
|
+/* Version 2 let the address of the frame buffer be unsigned on Win32 */
|
|
+#define SVGA_VERSION_2 2
|
|
+#define SVGA_ID_2 SVGA_MAKE_ID(SVGA_VERSION_2)
|
|
+
|
|
+/* Version 1 has new registers starting with SVGA_REG_CAPABILITIES so
|
|
+ PALETTE_BASE has moved */
|
|
+#define SVGA_VERSION_1 1
|
|
+#define SVGA_ID_1 SVGA_MAKE_ID(SVGA_VERSION_1)
|
|
+
|
|
+/* Version 0 is the initial version */
|
|
+#define SVGA_VERSION_0 0
|
|
+#define SVGA_ID_0 SVGA_MAKE_ID(SVGA_VERSION_0)
|
|
+
|
|
+/* "Invalid" value for all SVGA IDs. (Version ID, screen object ID, surface ID...) */
|
|
+#define SVGA_ID_INVALID 0xFFFFFFFF
|
|
+
|
|
+/* Port offsets, relative to BAR0 */
|
|
+#define SVGA_INDEX_PORT 0x0
|
|
+#define SVGA_VALUE_PORT 0x1
|
|
+#define SVGA_BIOS_PORT 0x2
|
|
+#define SVGA_IRQSTATUS_PORT 0x8
|
|
+
|
|
+/*
|
|
+ * Interrupt source flags for IRQSTATUS_PORT and IRQMASK.
|
|
+ *
|
|
+ * Interrupts are only supported when the
|
|
+ * SVGA_CAP_IRQMASK capability is present.
|
|
+ */
|
|
+#define SVGA_IRQFLAG_ANY_FENCE 0x1 /* Any fence was passed */
|
|
+#define SVGA_IRQFLAG_FIFO_PROGRESS 0x2 /* Made forward progress in the FIFO */
|
|
+#define SVGA_IRQFLAG_FENCE_GOAL 0x4 /* SVGA_FIFO_FENCE_GOAL reached */
|
|
+
|
|
+/*
|
|
+ * Registers
|
|
+ */
|
|
+
|
|
+enum {
|
|
+ SVGA_REG_ID = 0,
|
|
+ SVGA_REG_ENABLE = 1,
|
|
+ SVGA_REG_WIDTH = 2,
|
|
+ SVGA_REG_HEIGHT = 3,
|
|
+ SVGA_REG_MAX_WIDTH = 4,
|
|
+ SVGA_REG_MAX_HEIGHT = 5,
|
|
+ SVGA_REG_DEPTH = 6,
|
|
+ SVGA_REG_BITS_PER_PIXEL = 7, /* Current bpp in the guest */
|
|
+ SVGA_REG_PSEUDOCOLOR = 8,
|
|
+ SVGA_REG_RED_MASK = 9,
|
|
+ SVGA_REG_GREEN_MASK = 10,
|
|
+ SVGA_REG_BLUE_MASK = 11,
|
|
+ SVGA_REG_BYTES_PER_LINE = 12,
|
|
+ SVGA_REG_FB_START = 13, /* (Deprecated) */
|
|
+ SVGA_REG_FB_OFFSET = 14,
|
|
+ SVGA_REG_VRAM_SIZE = 15,
|
|
+ SVGA_REG_FB_SIZE = 16,
|
|
+
|
|
+ /* ID 0 implementation only had the above registers, then the palette */
|
|
+
|
|
+ SVGA_REG_CAPABILITIES = 17,
|
|
+ SVGA_REG_MEM_START = 18, /* (Deprecated) */
|
|
+ SVGA_REG_MEM_SIZE = 19,
|
|
+ SVGA_REG_CONFIG_DONE = 20, /* Set when memory area configured */
|
|
+ SVGA_REG_SYNC = 21, /* See "FIFO Synchronization Registers" */
|
|
+ SVGA_REG_BUSY = 22, /* See "FIFO Synchronization Registers" */
|
|
+ SVGA_REG_GUEST_ID = 23, /* Set guest OS identifier */
|
|
+ SVGA_REG_CURSOR_ID = 24, /* (Deprecated) */
|
|
+ SVGA_REG_CURSOR_X = 25, /* (Deprecated) */
|
|
+ SVGA_REG_CURSOR_Y = 26, /* (Deprecated) */
|
|
+ SVGA_REG_CURSOR_ON = 27, /* (Deprecated) */
|
|
+ SVGA_REG_HOST_BITS_PER_PIXEL = 28, /* (Deprecated) */
|
|
+ SVGA_REG_SCRATCH_SIZE = 29, /* Number of scratch registers */
|
|
+ SVGA_REG_MEM_REGS = 30, /* Number of FIFO registers */
|
|
+ SVGA_REG_NUM_DISPLAYS = 31, /* (Deprecated) */
|
|
+ SVGA_REG_PITCHLOCK = 32, /* Fixed pitch for all modes */
|
|
+ SVGA_REG_IRQMASK = 33, /* Interrupt mask */
|
|
+
|
|
+ /* Legacy multi-monitor support */
|
|
+ SVGA_REG_NUM_GUEST_DISPLAYS = 34,/* Number of guest displays in X/Y direction */
|
|
+ SVGA_REG_DISPLAY_ID = 35, /* Display ID for the following display attributes */
|
|
+ SVGA_REG_DISPLAY_IS_PRIMARY = 36,/* Whether this is a primary display */
|
|
+ SVGA_REG_DISPLAY_POSITION_X = 37,/* The display position x */
|
|
+ SVGA_REG_DISPLAY_POSITION_Y = 38,/* The display position y */
|
|
+ SVGA_REG_DISPLAY_WIDTH = 39, /* The display's width */
|
|
+ SVGA_REG_DISPLAY_HEIGHT = 40, /* The display's height */
|
|
+
|
|
+ /* See "Guest memory regions" below. */
|
|
+ SVGA_REG_GMR_ID = 41,
|
|
+ SVGA_REG_GMR_DESCRIPTOR = 42,
|
|
+ SVGA_REG_GMR_MAX_IDS = 43,
|
|
+ SVGA_REG_GMR_MAX_DESCRIPTOR_LENGTH = 44,
|
|
+
|
|
+ SVGA_REG_TRACES = 45, /* Enable trace-based updates even when FIFO is on */
|
|
+ SVGA_REG_TOP = 46, /* Must be 1 more than the last register */
|
|
+
|
|
+ SVGA_PALETTE_BASE = 1024, /* Base of SVGA color map */
|
|
+ /* Next 768 (== 256*3) registers exist for colormap */
|
|
+
|
|
+ SVGA_SCRATCH_BASE = SVGA_PALETTE_BASE + SVGA_NUM_PALETTE_REGS
|
|
+ /* Base of scratch registers */
|
|
+ /* Next reg[SVGA_REG_SCRATCH_SIZE] registers exist for scratch usage:
|
|
+ First 4 are reserved for VESA BIOS Extension; any remaining are for
|
|
+ the use of the current SVGA driver. */
|
|
+};
|
|
+
|
|
+
|
|
+/*
|
|
+ * Guest memory regions (GMRs):
|
|
+ *
|
|
+ * This is a new memory mapping feature available in SVGA devices
|
|
+ * which have the SVGA_CAP_GMR bit set. Previously, there were two
|
|
+ * fixed memory regions available with which to share data between the
|
|
+ * device and the driver: the FIFO ('MEM') and the framebuffer. GMRs
|
|
+ * are our name for an extensible way of providing arbitrary DMA
|
|
+ * buffers for use between the driver and the SVGA device. They are a
|
|
+ * new alternative to framebuffer memory, usable for both 2D and 3D
|
|
+ * graphics operations.
|
|
+ *
|
|
+ * Since GMR mapping must be done synchronously with guest CPU
|
|
+ * execution, we use a new pair of SVGA registers:
|
|
+ *
|
|
+ * SVGA_REG_GMR_ID --
|
|
+ *
|
|
+ * Read/write.
|
|
+ * This register holds the 32-bit ID (a small positive integer)
|
|
+ * of a GMR to create, delete, or redefine. Writing this register
|
|
+ * has no side-effects.
|
|
+ *
|
|
+ * SVGA_REG_GMR_DESCRIPTOR --
|
|
+ *
|
|
+ * Write-only.
|
|
+ * Writing this register will create, delete, or redefine the GMR
|
|
+ * specified by the above ID register. If this register is zero,
|
|
+ * the GMR is deleted. Any pointers into this GMR (including those
|
|
+ * currently being processed by FIFO commands) will be
|
|
+ * synchronously invalidated.
|
|
+ *
|
|
+ * If this register is nonzero, it must be the physical page
|
|
+ * number (PPN) of a data structure which describes the physical
|
|
+ * layout of the memory region this GMR should describe. The
|
|
+ * descriptor structure will be read synchronously by the SVGA
|
|
+ * device when this register is written. The descriptor need not
|
|
+ * remain allocated for the lifetime of the GMR.
|
|
+ *
|
|
+ * The guest driver should write SVGA_REG_GMR_ID first, then
|
|
+ * SVGA_REG_GMR_DESCRIPTOR.
|
|
+ *
|
|
+ * SVGA_REG_GMR_MAX_IDS --
|
|
+ *
|
|
+ * Read-only.
|
|
+ * The SVGA device may choose to support a maximum number of
|
|
+ * user-defined GMR IDs. This register holds the number of supported
|
|
+ * IDs. (The maximum supported ID plus 1)
|
|
+ *
|
|
+ * SVGA_REG_GMR_MAX_DESCRIPTOR_LENGTH --
|
|
+ *
|
|
+ * Read-only.
|
|
+ * The SVGA device may choose to put a limit on the total number
|
|
+ * of SVGAGuestMemDescriptor structures it will read when defining
|
|
+ * a single GMR.
|
|
+ *
|
|
+ * The descriptor structure is an array of SVGAGuestMemDescriptor
|
|
+ * structures. Each structure may do one of three things:
|
|
+ *
|
|
+ * - Terminate the GMR descriptor list.
|
|
+ * (ppn==0, numPages==0)
|
|
+ *
|
|
+ * - Add a PPN or range of PPNs to the GMR's virtual address space.
|
|
+ * (ppn != 0, numPages != 0)
|
|
+ *
|
|
+ * - Provide the PPN of the next SVGAGuestMemDescriptor, in order to
|
|
+ * support multi-page GMR descriptor tables without forcing the
|
|
+ * driver to allocate physically contiguous memory.
|
|
+ * (ppn != 0, numPages == 0)
|
|
+ *
|
|
+ * Note that each physical page of SVGAGuestMemDescriptor structures
|
|
+ * can describe at least 2MB of guest memory. If the driver needs to
|
|
+ * use more than one page of descriptor structures, it must use one of
|
|
+ * its SVGAGuestMemDescriptors to point to an additional page. The
|
|
+ * device will never automatically cross a page boundary.
|
|
+ *
|
|
+ * Once the driver has described a GMR, it is immediately available
|
|
+ * for use via any FIFO command that uses an SVGAGuestPtr structure.
|
|
+ * These pointers include a GMR identifier plus an offset into that
|
|
+ * GMR.
|
|
+ *
|
|
+ * The driver must check the SVGA_CAP_GMR bit before using the GMR
|
|
+ * registers.
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * Special GMR IDs, allowing SVGAGuestPtrs to point to framebuffer
|
|
+ * memory as well. In the future, these IDs could even be used to
|
|
+ * allow legacy memory regions to be redefined by the guest as GMRs.
|
|
+ *
|
|
+ * Using the guest framebuffer (GFB) at BAR1 for general purpose DMA
|
|
+ * is being phased out. Please try to use user-defined GMRs whenever
|
|
+ * possible.
|
|
+ */
|
|
+#define SVGA_GMR_NULL ((uint32) -1)
|
|
+#define SVGA_GMR_FRAMEBUFFER ((uint32) -2) // Guest Framebuffer (GFB)
|
|
+
|
|
+typedef
|
|
+struct SVGAGuestMemDescriptor {
|
|
+ uint32 ppn;
|
|
+ uint32 numPages;
|
|
+} SVGAGuestMemDescriptor;
|
|
+
|
|
+typedef
|
|
+struct SVGAGuestPtr {
|
|
+ uint32 gmrId;
|
|
+ uint32 offset;
|
|
+} SVGAGuestPtr;
|
|
+
|
|
+
|
|
+/*
|
|
+ * SVGAGMRImageFormat --
|
|
+ *
|
|
+ * This is a packed representation of the source 2D image format
|
|
+ * for a GMR-to-screen blit. Currently it is defined as an encoding
|
|
+ * of the screen's color depth and bits-per-pixel, however, 16 bits
|
|
+ * are reserved for future use to identify other encodings (such as
|
|
+ * RGBA or higher-precision images).
|
|
+ *
|
|
+ * Currently supported formats:
|
|
+ *
|
|
+ * bpp depth Format Name
|
|
+ * --- ----- -----------
|
|
+ * 32 24 32-bit BGRX
|
|
+ * 24 24 24-bit BGR
|
|
+ * 16 16 RGB 5-6-5
|
|
+ * 16 15 RGB 5-5-5
|
|
+ *
|
|
+ */
|
|
+
|
|
+typedef
|
|
+struct SVGAGMRImageFormat {
|
|
+ union {
|
|
+ struct {
|
|
+ uint32 bitsPerPixel : 8;
|
|
+ uint32 colorDepth : 8;
|
|
+ uint32 reserved : 16; // Must be zero
|
|
+ };
|
|
+
|
|
+ uint32 value;
|
|
+ };
|
|
+} SVGAGMRImageFormat;
|
|
+
|
|
+/*
|
|
+ * SVGAColorBGRX --
|
|
+ *
|
|
+ * A 24-bit color format (BGRX), which does not depend on the
|
|
+ * format of the legacy guest framebuffer (GFB) or the current
|
|
+ * GMRFB state.
|
|
+ */
|
|
+
|
|
+typedef
|
|
+struct SVGAColorBGRX {
|
|
+ union {
|
|
+ struct {
|
|
+ uint32 b : 8;
|
|
+ uint32 g : 8;
|
|
+ uint32 r : 8;
|
|
+ uint32 x : 8; // Unused
|
|
+ };
|
|
+
|
|
+ uint32 value;
|
|
+ };
|
|
+} SVGAColorBGRX;
|
|
+
|
|
+
|
|
+/*
|
|
+ * SVGASignedRect --
|
|
+ * SVGASignedPoint --
|
|
+ *
|
|
+ * Signed rectangle and point primitives. These are used by the new
|
|
+ * 2D primitives for drawing to Screen Objects, which can occupy a
|
|
+ * signed virtual coordinate space.
|
|
+ *
|
|
+ * SVGASignedRect specifies a half-open interval: the (left, top)
|
|
+ * pixel is part of the rectangle, but the (right, bottom) pixel is
|
|
+ * not.
|
|
+ */
|
|
+
|
|
+typedef
|
|
+struct SVGASignedRect {
|
|
+ int32 left;
|
|
+ int32 top;
|
|
+ int32 right;
|
|
+ int32 bottom;
|
|
+} SVGASignedRect;
|
|
+
|
|
+typedef
|
|
+struct SVGASignedPoint {
|
|
+ int32 x;
|
|
+ int32 y;
|
|
+} SVGASignedPoint;
|
|
+
|
|
+
|
|
+/*
|
|
+ * Capabilities
|
|
+ *
|
|
+ * Note the holes in the bitfield. Missing bits have been deprecated,
|
|
+ * and must not be reused. Those capabilities will never be reported
|
|
+ * by new versions of the SVGA device.
|
|
+ */
|
|
+
|
|
+#define SVGA_CAP_NONE 0x00000000
|
|
+#define SVGA_CAP_RECT_COPY 0x00000002
|
|
+#define SVGA_CAP_CURSOR 0x00000020
|
|
+#define SVGA_CAP_CURSOR_BYPASS 0x00000040 // Legacy (Use Cursor Bypass 3 instead)
|
|
+#define SVGA_CAP_CURSOR_BYPASS_2 0x00000080 // Legacy (Use Cursor Bypass 3 instead)
|
|
+#define SVGA_CAP_8BIT_EMULATION 0x00000100
|
|
+#define SVGA_CAP_ALPHA_CURSOR 0x00000200
|
|
+#define SVGA_CAP_3D 0x00004000
|
|
+#define SVGA_CAP_EXTENDED_FIFO 0x00008000
|
|
+#define SVGA_CAP_MULTIMON 0x00010000 // Legacy multi-monitor support
|
|
+#define SVGA_CAP_PITCHLOCK 0x00020000
|
|
+#define SVGA_CAP_IRQMASK 0x00040000
|
|
+#define SVGA_CAP_DISPLAY_TOPOLOGY 0x00080000 // Legacy multi-monitor support
|
|
+#define SVGA_CAP_GMR 0x00100000
|
|
+#define SVGA_CAP_TRACES 0x00200000
|
|
+
|
|
+
|
|
+/*
|
|
+ * FIFO register indices.
|
|
+ *
|
|
+ * The FIFO is a chunk of device memory mapped into guest physmem. It
|
|
+ * is always treated as 32-bit words.
|
|
+ *
|
|
+ * The guest driver gets to decide how to partition it between
|
|
+ * - FIFO registers (there are always at least 4, specifying where the
|
|
+ * following data area is and how much data it contains; there may be
|
|
+ * more registers following these, depending on the FIFO protocol
|
|
+ * version in use)
|
|
+ * - FIFO data, written by the guest and slurped out by the VMX.
|
|
+ * These indices are 32-bit word offsets into the FIFO.
|
|
+ */
|
|
+
|
|
+enum {
|
|
+ /*
|
|
+ * Block 1 (basic registers): The originally defined FIFO registers.
|
|
+ * These exist and are valid for all versions of the FIFO protocol.
|
|
+ */
|
|
+
|
|
+ SVGA_FIFO_MIN = 0,
|
|
+ SVGA_FIFO_MAX, /* The distance from MIN to MAX must be at least 10K */
|
|
+ SVGA_FIFO_NEXT_CMD,
|
|
+ SVGA_FIFO_STOP,
|
|
+
|
|
+ /*
|
|
+ * Block 2 (extended registers): Mandatory registers for the extended
|
|
+ * FIFO. These exist if the SVGA caps register includes
|
|
+ * SVGA_CAP_EXTENDED_FIFO; some of them are valid only if their
|
|
+ * associated capability bit is enabled.
|
|
+ *
|
|
+ * Note that when originally defined, SVGA_CAP_EXTENDED_FIFO implied
|
|
+ * support only for (FIFO registers) CAPABILITIES, FLAGS, and FENCE.
|
|
+ * This means that the guest has to test individually (in most cases
|
|
+ * using FIFO caps) for the presence of registers after this; the VMX
|
|
+ * can define "extended FIFO" to mean whatever it wants, and currently
|
|
+ * won't enable it unless there's room for that set and much more.
|
|
+ */
|
|
+
|
|
+ SVGA_FIFO_CAPABILITIES = 4,
|
|
+ SVGA_FIFO_FLAGS,
|
|
+ // Valid with SVGA_FIFO_CAP_FENCE:
|
|
+ SVGA_FIFO_FENCE,
|
|
+
|
|
+ /*
|
|
+ * Block 3a (optional extended registers): Additional registers for the
|
|
+ * extended FIFO, whose presence isn't actually implied by
|
|
+ * SVGA_CAP_EXTENDED_FIFO; these exist if SVGA_FIFO_MIN is high enough to
|
|
+ * leave room for them.
|
|
+ *
|
|
+ * These in block 3a, the VMX currently considers mandatory for the
|
|
+ * extended FIFO.
|
|
+ */
|
|
+
|
|
+ // Valid if exists (i.e. if extended FIFO enabled):
|
|
+ SVGA_FIFO_3D_HWVERSION, /* See SVGA3dHardwareVersion in svga3d_reg.h */
|
|
+ // Valid with SVGA_FIFO_CAP_PITCHLOCK:
|
|
+ SVGA_FIFO_PITCHLOCK,
|
|
+
|
|
+ // Valid with SVGA_FIFO_CAP_CURSOR_BYPASS_3:
|
|
+ SVGA_FIFO_CURSOR_ON, /* Cursor bypass 3 show/hide register */
|
|
+ SVGA_FIFO_CURSOR_X, /* Cursor bypass 3 x register */
|
|
+ SVGA_FIFO_CURSOR_Y, /* Cursor bypass 3 y register */
|
|
+ SVGA_FIFO_CURSOR_COUNT, /* Incremented when any of the other 3 change */
|
|
+ SVGA_FIFO_CURSOR_LAST_UPDATED,/* Last time the host updated the cursor */
|
|
+
|
|
+ // Valid with SVGA_FIFO_CAP_RESERVE:
|
|
+ SVGA_FIFO_RESERVED, /* Bytes past NEXT_CMD with real contents */
|
|
+
|
|
+ /*
|
|
+ * Valid with SVGA_FIFO_CAP_SCREEN_OBJECT:
|
|
+ *
|
|
+ * By default this is SVGA_ID_INVALID, to indicate that the cursor
|
|
+ * coordinates are specified relative to the virtual root. If this
|
|
+ * is set to a specific screen ID, cursor position is reinterpreted
|
|
+ * as a signed offset relative to that screen's origin. This is the
|
|
+ * only way to place the cursor on a non-rooted screen.
|
|
+ */
|
|
+ SVGA_FIFO_CURSOR_SCREEN_ID,
|
|
+
|
|
+ /*
|
|
+ * XXX: The gap here, up until SVGA_FIFO_3D_CAPS, can be used for new
|
|
+ * registers, but this must be done carefully and with judicious use of
|
|
+ * capability bits, since comparisons based on SVGA_FIFO_MIN aren't
|
|
+ * enough to tell you whether the register exists: we've shipped drivers
|
|
+ * and products that used SVGA_FIFO_3D_CAPS but didn't know about some of
|
|
+ * the earlier ones. The actual order of introduction was:
|
|
+ * - PITCHLOCK
|
|
+ * - 3D_CAPS
|
|
+ * - CURSOR_* (cursor bypass 3)
|
|
+ * - RESERVED
|
|
+ * So, code that wants to know whether it can use any of the
|
|
+ * aforementioned registers, or anything else added after PITCHLOCK and
|
|
+ * before 3D_CAPS, needs to reason about something other than
|
|
+ * SVGA_FIFO_MIN.
|
|
+ */
|
|
+
|
|
+ /*
|
|
+ * 3D caps block space; valid with 3D hardware version >=
|
|
+ * SVGA3D_HWVERSION_WS6_B1.
|
|
+ */
|
|
+ SVGA_FIFO_3D_CAPS = 32,
|
|
+ SVGA_FIFO_3D_CAPS_LAST = 32 + 255,
|
|
+
|
|
+ /*
|
|
+ * End of VMX's current definition of "extended-FIFO registers".
|
|
+ * Registers before here are always enabled/disabled as a block; either
|
|
+ * the extended FIFO is enabled and includes all preceding registers, or
|
|
+ * it's disabled entirely.
|
|
+ *
|
|
+ * Block 3b (truly optional extended registers): Additional registers for
|
|
+ * the extended FIFO, which the VMX already knows how to enable and
|
|
+ * disable with correct granularity.
|
|
+ *
|
|
+ * Registers after here exist if and only if the guest SVGA driver
|
|
+ * sets SVGA_FIFO_MIN high enough to leave room for them.
|
|
+ */
|
|
+
|
|
+ // Valid if register exists:
|
|
+ SVGA_FIFO_GUEST_3D_HWVERSION, /* Guest driver's 3D version */
|
|
+ SVGA_FIFO_FENCE_GOAL, /* Matching target for SVGA_IRQFLAG_FENCE_GOAL */
|
|
+ SVGA_FIFO_BUSY, /* See "FIFO Synchronization Registers" */
|
|
+
|
|
+ /*
|
|
+ * Always keep this last. This defines the maximum number of
|
|
+ * registers we know about. At power-on, this value is placed in
|
|
+ * the SVGA_REG_MEM_REGS register, and we expect the guest driver
|
|
+ * to allocate this much space in FIFO memory for registers.
|
|
+ */
|
|
+ SVGA_FIFO_NUM_REGS
|
|
+};
|
|
+
|
|
+
|
|
+/*
|
|
+ * Definition of registers included in extended FIFO support.
|
|
+ *
|
|
+ * The guest SVGA driver gets to allocate the FIFO between registers
|
|
+ * and data. It must always allocate at least 4 registers, but old
|
|
+ * drivers stopped there.
|
|
+ *
|
|
+ * The VMX will enable extended FIFO support if and only if the guest
|
|
+ * left enough room for all registers defined as part of the mandatory
|
|
+ * set for the extended FIFO.
|
|
+ *
|
|
+ * Note that the guest drivers typically allocate the FIFO only at
|
|
+ * initialization time, not at mode switches, so it's likely that the
|
|
+ * number of FIFO registers won't change without a reboot.
|
|
+ *
|
|
+ * All registers less than this value are guaranteed to be present if
|
|
+ * svgaUser->fifo.extended is set. Any later registers must be tested
|
|
+ * individually for compatibility at each use (in the VMX).
|
|
+ *
|
|
+ * This value is used only by the VMX, so it can change without
|
|
+ * affecting driver compatibility; keep it that way?
|
|
+ */
|
|
+#define SVGA_FIFO_EXTENDED_MANDATORY_REGS (SVGA_FIFO_3D_CAPS_LAST + 1)
|
|
+
|
|
+
|
|
+/*
|
|
+ * FIFO Synchronization Registers
|
|
+ *
|
|
+ * This explains the relationship between the various FIFO
|
|
+ * sync-related registers in IOSpace and in FIFO space.
|
|
+ *
|
|
+ * SVGA_REG_SYNC --
|
|
+ *
|
|
+ * The SYNC register can be used in two different ways by the guest:
|
|
+ *
|
|
+ * 1. If the guest wishes to fully sync (drain) the FIFO,
|
|
+ * it will write once to SYNC then poll on the BUSY
|
|
+ * register. The FIFO is sync'ed once BUSY is zero.
|
|
+ *
|
|
+ * 2. If the guest wants to asynchronously wake up the host,
|
|
+ * it will write once to SYNC without polling on BUSY.
|
|
+ * Ideally it will do this after some new commands have
|
|
+ * been placed in the FIFO, and after reading a zero
|
|
+ * from SVGA_FIFO_BUSY.
|
|
+ *
|
|
+ * (1) is the original behaviour that SYNC was designed to
|
|
+ * support. Originally, a write to SYNC would implicitly
|
|
+ * trigger a read from BUSY. This causes us to synchronously
|
|
+ * process the FIFO.
|
|
+ *
|
|
+ * This behaviour has since been changed so that writing SYNC
|
|
+ * will *not* implicitly cause a read from BUSY. Instead, it
|
|
+ * makes a channel call which asynchronously wakes up the MKS
|
|
+ * thread.
|
|
+ *
|
|
+ * New guests can use this new behaviour to implement (2)
|
|
+ * efficiently. This lets guests get the host's attention
|
|
+ * without waiting for the MKS to poll, which gives us much
|
|
+ * better CPU utilization on SMP hosts and on UP hosts while
|
|
+ * we're blocked on the host GPU.
|
|
+ *
|
|
+ * Old guests shouldn't notice the behaviour change. SYNC was
|
|
+ * never guaranteed to process the entire FIFO, since it was
|
|
+ * bounded to a particular number of CPU cycles. Old guests will
|
|
+ * still loop on the BUSY register until the FIFO is empty.
|
|
+ *
|
|
+ * Writing to SYNC currently has the following side-effects:
|
|
+ *
|
|
+ * - Sets SVGA_REG_BUSY to TRUE (in the monitor)
|
|
+ * - Asynchronously wakes up the MKS thread for FIFO processing
|
|
+ * - The value written to SYNC is recorded as a "reason", for
|
|
+ * stats purposes.
|
|
+ *
|
|
+ * If SVGA_FIFO_BUSY is available, drivers are advised to only
|
|
+ * write to SYNC if SVGA_FIFO_BUSY is FALSE. Drivers should set
|
|
+ * SVGA_FIFO_BUSY to TRUE after writing to SYNC. The MKS will
|
|
+ * eventually set SVGA_FIFO_BUSY on its own, but this approach
|
|
+ * lets the driver avoid sending multiple asynchronous wakeup
|
|
+ * messages to the MKS thread.
|
|
+ *
|
|
+ * SVGA_REG_BUSY --
|
|
+ *
|
|
+ * This register is set to TRUE when SVGA_REG_SYNC is written,
|
|
+ * and it reads as FALSE when the FIFO has been completely
|
|
+ * drained.
|
|
+ *
|
|
+ * Every read from this register causes us to synchronously
|
|
+ * process FIFO commands. There is no guarantee as to how many
|
|
+ * commands each read will process.
|
|
+ *
|
|
+ * CPU time spent processing FIFO commands will be billed to
|
|
+ * the guest.
|
|
+ *
|
|
+ * New drivers should avoid using this register unless they
|
|
+ * need to guarantee that the FIFO is completely drained. It
|
|
+ * is overkill for performing a sync-to-fence. Older drivers
|
|
+ * will use this register for any type of synchronization.
|
|
+ *
|
|
+ * SVGA_FIFO_BUSY --
|
|
+ *
|
|
+ * This register is a fast way for the guest driver to check
|
|
+ * whether the FIFO is already being processed. It reads and
|
|
+ * writes at normal RAM speeds, with no monitor intervention.
|
|
+ *
|
|
+ * If this register reads as TRUE, the host is guaranteeing that
|
|
+ * any new commands written into the FIFO will be noticed before
|
|
+ * the MKS goes back to sleep.
|
|
+ *
|
|
+ * If this register reads as FALSE, no such guarantee can be
|
|
+ * made.
|
|
+ *
|
|
+ * The guest should use this register to quickly determine
|
|
+ * whether or not it needs to wake up the host. If the guest
|
|
+ * just wrote a command or group of commands that it would like
|
|
+ * the host to begin processing, it should:
|
|
+ *
|
|
+ * 1. Read SVGA_FIFO_BUSY. If it reads as TRUE, no further
|
|
+ * action is necessary.
|
|
+ *
|
|
+ * 2. Write TRUE to SVGA_FIFO_BUSY. This informs future guest
|
|
+ * code that we've already sent a SYNC to the host and we
|
|
+ * don't need to send a duplicate.
|
|
+ *
|
|
+ * 3. Write a reason to SVGA_REG_SYNC. This will send an
|
|
+ * asynchronous wakeup to the MKS thread.
|
|
+ */
|
|
+
|
|
+
|
|
+/*
|
|
+ * FIFO Capabilities
|
|
+ *
|
|
+ * Fence -- Fence register and command are supported
|
|
+ * Accel Front -- Front buffer only commands are supported
|
|
+ * Pitch Lock -- Pitch lock register is supported
|
|
+ * Video -- SVGA Video overlay units are supported
|
|
+ * Escape -- Escape command is supported
|
|
+ *
|
|
+ * XXX: Add longer descriptions for each capability, including a list
|
|
+ * of the new features that each capability provides.
|
|
+ *
|
|
+ * SVGA_FIFO_CAP_SCREEN_OBJECT --
|
|
+ *
|
|
+ * Provides dynamic multi-screen rendering, for improved Unity and
|
|
+ * multi-monitor modes. With Screen Object, the guest can
|
|
+ * dynamically create and destroy 'screens', which can represent
|
|
+ * Unity windows or virtual monitors. Screen Object also provides
|
|
+ * strong guarantees that DMA operations happen only when
|
|
+ * guest-initiated. Screen Object deprecates the BAR1 guest
|
|
+ * framebuffer (GFB) and all commands that work only with the GFB.
|
|
+ *
|
|
+ * New registers:
|
|
+ * FIFO_CURSOR_SCREEN_ID, VIDEO_DATA_GMRID, VIDEO_DST_SCREEN_ID
|
|
+ *
|
|
+ * New 2D commands:
|
|
+ * DEFINE_SCREEN, DESTROY_SCREEN, DEFINE_GMRFB, BLIT_GMRFB_TO_SCREEN,
|
|
+ * BLIT_SCREEN_TO_GMRFB, ANNOTATION_FILL, ANNOTATION_COPY
|
|
+ *
|
|
+ * New 3D commands:
|
|
+ * BLIT_SURFACE_TO_SCREEN
|
|
+ *
|
|
+ * New guarantees:
|
|
+ *
|
|
+ * - The host will not read or write guest memory, including the GFB,
|
|
+ * except when explicitly initiated by a DMA command.
|
|
+ *
|
|
+ * - All DMA, including legacy DMA like UPDATE and PRESENT_READBACK,
|
|
+ * is guaranteed to complete before any subsequent FENCEs.
|
|
+ *
|
|
+ * - All legacy commands which affect a Screen (UPDATE, PRESENT,
|
|
+ * PRESENT_READBACK) as well as new Screen blit commands will
|
|
+ * all behave consistently as blits, and memory will be read
|
|
+ * or written in FIFO order.
|
|
+ *
|
|
+ * For example, if you PRESENT from one SVGA3D surface to multiple
|
|
+ * places on the screen, the data copied will always be from the
|
|
+ * SVGA3D surface at the time the PRESENT was issued in the FIFO.
|
|
+ * This was not necessarily true on devices without Screen Object.
|
|
+ *
|
|
+ * This means that on devices that support Screen Object, the
|
|
+ * PRESENT_READBACK command should not be necessary unless you
|
|
+ * actually want to read back the results of 3D rendering into
|
|
+ * system memory. (And for that, the BLIT_SCREEN_TO_GMRFB
|
|
+ * command provides a strict superset of functionality.)
|
|
+ *
|
|
+ * - When a screen is resized, either using Screen Object commands or
|
|
+ * legacy multimon registers, its contents are preserved.
|
|
+ */
|
|
+
|
|
+#define SVGA_FIFO_CAP_NONE 0
|
|
+#define SVGA_FIFO_CAP_FENCE (1<<0)
|
|
+#define SVGA_FIFO_CAP_ACCELFRONT (1<<1)
|
|
+#define SVGA_FIFO_CAP_PITCHLOCK (1<<2)
|
|
+#define SVGA_FIFO_CAP_VIDEO (1<<3)
|
|
+#define SVGA_FIFO_CAP_CURSOR_BYPASS_3 (1<<4)
|
|
+#define SVGA_FIFO_CAP_ESCAPE (1<<5)
|
|
+#define SVGA_FIFO_CAP_RESERVE (1<<6)
|
|
+#define SVGA_FIFO_CAP_SCREEN_OBJECT (1<<7)
|
|
+
|
|
+
|
|
+/*
|
|
+ * FIFO Flags
|
|
+ *
|
|
+ * Accel Front -- Driver should use front buffer only commands
|
|
+ */
|
|
+
|
|
+#define SVGA_FIFO_FLAG_NONE 0
|
|
+#define SVGA_FIFO_FLAG_ACCELFRONT (1<<0)
|
|
+#define SVGA_FIFO_FLAG_RESERVED (1<<31) // Internal use only
|
|
+
|
|
+/*
|
|
+ * FIFO reservation sentinel value
|
|
+ */
|
|
+
|
|
+#define SVGA_FIFO_RESERVED_UNKNOWN 0xffffffff
|
|
+
|
|
+
|
|
+/*
|
|
+ * Video overlay support
|
|
+ */
|
|
+
|
|
+#define SVGA_NUM_OVERLAY_UNITS 32
|
|
+
|
|
+
|
|
+/*
|
|
+ * Video capabilities that the guest is currently using
|
|
+ */
|
|
+
|
|
+#define SVGA_VIDEO_FLAG_COLORKEY 0x0001
|
|
+
|
|
+
|
|
+/*
|
|
+ * Offsets for the video overlay registers
|
|
+ */
|
|
+
|
|
+enum {
|
|
+ SVGA_VIDEO_ENABLED = 0,
|
|
+ SVGA_VIDEO_FLAGS,
|
|
+ SVGA_VIDEO_DATA_OFFSET,
|
|
+ SVGA_VIDEO_FORMAT,
|
|
+ SVGA_VIDEO_COLORKEY,
|
|
+ SVGA_VIDEO_SIZE, // Deprecated
|
|
+ SVGA_VIDEO_WIDTH,
|
|
+ SVGA_VIDEO_HEIGHT,
|
|
+ SVGA_VIDEO_SRC_X,
|
|
+ SVGA_VIDEO_SRC_Y,
|
|
+ SVGA_VIDEO_SRC_WIDTH,
|
|
+ SVGA_VIDEO_SRC_HEIGHT,
|
|
+ SVGA_VIDEO_DST_X, // Signed int32
|
|
+ SVGA_VIDEO_DST_Y, // Signed int32
|
|
+ SVGA_VIDEO_DST_WIDTH,
|
|
+ SVGA_VIDEO_DST_HEIGHT,
|
|
+ SVGA_VIDEO_PITCH_1,
|
|
+ SVGA_VIDEO_PITCH_2,
|
|
+ SVGA_VIDEO_PITCH_3,
|
|
+ SVGA_VIDEO_DATA_GMRID, // Optional, defaults to SVGA_GMR_FRAMEBUFFER
|
|
+ SVGA_VIDEO_DST_SCREEN_ID, // Optional, defaults to virtual coords (SVGA_ID_INVALID)
|
|
+ SVGA_VIDEO_NUM_REGS
|
|
+};
|
|
+
|
|
+
|
|
+/*
|
|
+ * SVGA Overlay Units
|
|
+ *
|
|
+ * width and height relate to the entire source video frame.
|
|
+ * srcX, srcY, srcWidth and srcHeight represent subset of the source
|
|
+ * video frame to be displayed.
|
|
+ */
|
|
+
|
|
+typedef struct SVGAOverlayUnit {
|
|
+ uint32 enabled;
|
|
+ uint32 flags;
|
|
+ uint32 dataOffset;
|
|
+ uint32 format;
|
|
+ uint32 colorKey;
|
|
+ uint32 size;
|
|
+ uint32 width;
|
|
+ uint32 height;
|
|
+ uint32 srcX;
|
|
+ uint32 srcY;
|
|
+ uint32 srcWidth;
|
|
+ uint32 srcHeight;
|
|
+ int32 dstX;
|
|
+ int32 dstY;
|
|
+ uint32 dstWidth;
|
|
+ uint32 dstHeight;
|
|
+ uint32 pitches[3];
|
|
+ uint32 dataGMRId;
|
|
+ uint32 dstScreenId;
|
|
+} SVGAOverlayUnit;
|
|
+
|
|
+
|
|
+/*
|
|
+ * SVGAScreenObject --
|
|
+ *
|
|
+ * This is a new way to represent a guest's multi-monitor screen or
|
|
+ * Unity window. Screen objects are only supported if the
|
|
+ * SVGA_FIFO_CAP_SCREEN_OBJECT capability bit is set.
|
|
+ *
|
|
+ * If Screen Objects are supported, they can be used to fully
|
|
+ * replace the functionality provided by the framebuffer registers
|
|
+ * (SVGA_REG_WIDTH, HEIGHT, etc.) and by SVGA_CAP_DISPLAY_TOPOLOGY.
|
|
+ *
|
|
+ * The screen object is a struct with guaranteed binary
|
|
+ * compatibility. New flags can be added, and the struct may grow,
|
|
+ * but existing fields must retain their meaning.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#define SVGA_SCREEN_HAS_ROOT (1 << 0) // Screen is present in the virtual coord space
|
|
+#define SVGA_SCREEN_IS_PRIMARY (1 << 1) // Guest considers this screen to be 'primary'
|
|
+#define SVGA_SCREEN_FULLSCREEN_HINT (1 << 2) // Guest is running a fullscreen app here
|
|
+
|
|
+typedef
|
|
+struct SVGAScreenObject {
|
|
+ uint32 structSize; // sizeof(SVGAScreenObject)
|
|
+ uint32 id;
|
|
+ uint32 flags;
|
|
+ struct {
|
|
+ uint32 width;
|
|
+ uint32 height;
|
|
+ } size;
|
|
+ struct {
|
|
+ int32 x;
|
|
+ int32 y;
|
|
+ } root; // Only used if SVGA_SCREEN_HAS_ROOT is set.
|
|
+} SVGAScreenObject;
|
|
+
|
|
+
|
|
+/*
|
|
+ * Commands in the command FIFO:
|
|
+ *
|
|
+ * Command IDs defined below are used for the traditional 2D FIFO
|
|
+ * communication (not all commands are available for all versions of the
|
|
+ * SVGA FIFO protocol).
|
|
+ *
|
|
+ * Note the holes in the command ID numbers: These commands have been
|
|
+ * deprecated, and the old IDs must not be reused.
|
|
+ *
|
|
+ * Command IDs from 1000 to 1999 are reserved for use by the SVGA3D
|
|
+ * protocol.
|
|
+ *
|
|
+ * Each command's parameters are described by the comments and
|
|
+ * structs below.
|
|
+ */
|
|
+
|
|
+typedef enum {
|
|
+ SVGA_CMD_INVALID_CMD = 0,
|
|
+ SVGA_CMD_UPDATE = 1,
|
|
+ SVGA_CMD_RECT_COPY = 3,
|
|
+ SVGA_CMD_DEFINE_CURSOR = 19,
|
|
+ SVGA_CMD_DEFINE_ALPHA_CURSOR = 22,
|
|
+ SVGA_CMD_UPDATE_VERBOSE = 25,
|
|
+ SVGA_CMD_FRONT_ROP_FILL = 29,
|
|
+ SVGA_CMD_FENCE = 30,
|
|
+ SVGA_CMD_ESCAPE = 33,
|
|
+ SVGA_CMD_DEFINE_SCREEN = 34,
|
|
+ SVGA_CMD_DESTROY_SCREEN = 35,
|
|
+ SVGA_CMD_DEFINE_GMRFB = 36,
|
|
+ SVGA_CMD_BLIT_GMRFB_TO_SCREEN = 37,
|
|
+ SVGA_CMD_BLIT_SCREEN_TO_GMRFB = 38,
|
|
+ SVGA_CMD_ANNOTATION_FILL = 39,
|
|
+ SVGA_CMD_ANNOTATION_COPY = 40,
|
|
+ SVGA_CMD_MAX
|
|
+} SVGAFifoCmdId;
|
|
+
|
|
+#define SVGA_CMD_MAX_ARGS 64
|
|
+
|
|
+
|
|
+/*
|
|
+ * SVGA_CMD_UPDATE --
|
|
+ *
|
|
+ * This is a DMA transfer which copies from the Guest Framebuffer
|
|
+ * (GFB) at BAR1 + SVGA_REG_FB_OFFSET to any screens which
|
|
+ * intersect with the provided virtual rectangle.
|
|
+ *
|
|
+ * This command does not support using arbitrary guest memory as a
|
|
+ * data source- it only works with the pre-defined GFB memory.
|
|
+ * This command also does not support signed virtual coordinates.
|
|
+ * If you have defined screens (using SVGA_CMD_DEFINE_SCREEN) with
|
|
+ * negative root x/y coordinates, the negative portion of those
|
|
+ * screens will not be reachable by this command.
|
|
+ *
|
|
+ * This command is not necessary when using framebuffer
|
|
+ * traces. Traces are automatically enabled if the SVGA FIFO is
|
|
+ * disabled, and you may explicitly enable/disable traces using
|
|
+ * SVGA_REG_TRACES. With traces enabled, any write to the GFB will
|
|
+ * automatically act as if a subsequent SVGA_CMD_UPDATE was issued.
|
|
+ *
|
|
+ * Traces and SVGA_CMD_UPDATE are the only supported ways to render
|
|
+ * pseudocolor screen updates. The newer Screen Object commands
|
|
+ * only support true color formats.
|
|
+ *
|
|
+ * Availability:
|
|
+ * Always available.
|
|
+ */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 x;
|
|
+ uint32 y;
|
|
+ uint32 width;
|
|
+ uint32 height;
|
|
+} SVGAFifoCmdUpdate;
|
|
+
|
|
+
|
|
+/*
|
|
+ * SVGA_CMD_RECT_COPY --
|
|
+ *
|
|
+ * Perform a rectangular DMA transfer from one area of the GFB to
|
|
+ * another, and copy the result to any screens which intersect it.
|
|
+ *
|
|
+ * Availability:
|
|
+ * SVGA_CAP_RECT_COPY
|
|
+ */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 srcX;
|
|
+ uint32 srcY;
|
|
+ uint32 destX;
|
|
+ uint32 destY;
|
|
+ uint32 width;
|
|
+ uint32 height;
|
|
+} SVGAFifoCmdRectCopy;
|
|
+
|
|
+
|
|
+/*
|
|
+ * SVGA_CMD_DEFINE_CURSOR --
|
|
+ *
|
|
+ * Provide a new cursor image, as an AND/XOR mask.
|
|
+ *
|
|
+ * The recommended way to position the cursor overlay is by using
|
|
+ * the SVGA_FIFO_CURSOR_* registers, supported by the
|
|
+ * SVGA_FIFO_CAP_CURSOR_BYPASS_3 capability.
|
|
+ *
|
|
+ * Availability:
|
|
+ * SVGA_CAP_CURSOR
|
|
+ */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 id; // Reserved, must be zero.
|
|
+ uint32 hotspotX;
|
|
+ uint32 hotspotY;
|
|
+ uint32 width;
|
|
+ uint32 height;
|
|
+ uint32 andMaskDepth; // Value must be 1 or equal to BITS_PER_PIXEL
|
|
+ uint32 xorMaskDepth; // Value must be 1 or equal to BITS_PER_PIXEL
|
|
+ /*
|
|
+ * Followed by scanline data for AND mask, then XOR mask.
|
|
+ * Each scanline is padded to a 32-bit boundary.
|
|
+ */
|
|
+} SVGAFifoCmdDefineCursor;
|
|
+
|
|
+
|
|
+/*
|
|
+ * SVGA_CMD_DEFINE_ALPHA_CURSOR --
|
|
+ *
|
|
+ * Provide a new cursor image, in 32-bit BGRA format.
|
|
+ *
|
|
+ * The recommended way to position the cursor overlay is by using
|
|
+ * the SVGA_FIFO_CURSOR_* registers, supported by the
|
|
+ * SVGA_FIFO_CAP_CURSOR_BYPASS_3 capability.
|
|
+ *
|
|
+ * Availability:
|
|
+ * SVGA_CAP_ALPHA_CURSOR
|
|
+ */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 id; // Reserved, must be zero.
|
|
+ uint32 hotspotX;
|
|
+ uint32 hotspotY;
|
|
+ uint32 width;
|
|
+ uint32 height;
|
|
+ /* Followed by scanline data */
|
|
+} SVGAFifoCmdDefineAlphaCursor;
|
|
+
|
|
+
|
|
+/*
|
|
+ * SVGA_CMD_UPDATE_VERBOSE --
|
|
+ *
|
|
+ * Just like SVGA_CMD_UPDATE, but also provide a per-rectangle
|
|
+ * 'reason' value, an opaque cookie which is used by internal
|
|
+ * debugging tools. Third party drivers should not use this
|
|
+ * command.
|
|
+ *
|
|
+ * Availability:
|
|
+ * SVGA_CAP_EXTENDED_FIFO
|
|
+ */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 x;
|
|
+ uint32 y;
|
|
+ uint32 width;
|
|
+ uint32 height;
|
|
+ uint32 reason;
|
|
+} SVGAFifoCmdUpdateVerbose;
|
|
+
|
|
+
|
|
+/*
|
|
+ * SVGA_CMD_FRONT_ROP_FILL --
|
|
+ *
|
|
+ * This is a hint which tells the SVGA device that the driver has
|
|
+ * just filled a rectangular region of the GFB with a solid
|
|
+ * color. Instead of reading these pixels from the GFB, the device
|
|
+ * can assume that they all equal 'color'. This is primarily used
|
|
+ * for remote desktop protocols.
|
|
+ *
|
|
+ * Availability:
|
|
+ * SVGA_FIFO_CAP_ACCELFRONT
|
|
+ */
|
|
+
|
|
+#define SVGA_ROP_COPY 0x03
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 color; // In the same format as the GFB
|
|
+ uint32 x;
|
|
+ uint32 y;
|
|
+ uint32 width;
|
|
+ uint32 height;
|
|
+ uint32 rop; // Must be SVGA_ROP_COPY
|
|
+} SVGAFifoCmdFrontRopFill;
|
|
+
|
|
+
|
|
+/*
|
|
+ * SVGA_CMD_FENCE --
|
|
+ *
|
|
+ * Insert a synchronization fence. When the SVGA device reaches
|
|
+ * this command, it will copy the 'fence' value into the
|
|
+ * SVGA_FIFO_FENCE register. It will also compare the fence against
|
|
+ * SVGA_FIFO_FENCE_GOAL. If the fence matches the goal and the
|
|
+ * SVGA_IRQFLAG_FENCE_GOAL interrupt is enabled, the device will
|
|
+ * raise this interrupt.
|
|
+ *
|
|
+ * Availability:
|
|
+ * SVGA_FIFO_FENCE for this command,
|
|
+ * SVGA_CAP_IRQMASK for SVGA_FIFO_FENCE_GOAL.
|
|
+ */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 fence;
|
|
+} SVGAFifoCmdFence;
|
|
+
|
|
+
|
|
+/*
|
|
+ * SVGA_CMD_ESCAPE --
|
|
+ *
|
|
+ * Send an extended or vendor-specific variable length command.
|
|
+ * This is used for video overlay, third party plugins, and
|
|
+ * internal debugging tools. See svga_escape.h
|
|
+ *
|
|
+ * Availability:
|
|
+ * SVGA_FIFO_CAP_ESCAPE
|
|
+ */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 nsid;
|
|
+ uint32 size;
|
|
+ /* followed by 'size' bytes of data */
|
|
+} SVGAFifoCmdEscape;
|
|
+
|
|
+
|
|
+/*
|
|
+ * SVGA_CMD_DEFINE_SCREEN --
|
|
+ *
|
|
+ * Define or redefine an SVGAScreenObject. See the description of
|
|
+ * SVGAScreenObject above. The video driver is responsible for
|
|
+ * generating new screen IDs. They should be small positive
|
|
+ * integers. The virtual device will have an implementation
|
|
+ * specific upper limit on the number of screen IDs
|
|
+ * supported. Drivers are responsible for recycling IDs. The first
|
|
+ * valid ID is zero.
|
|
+ *
|
|
+ * - Interaction with other registers:
|
|
+ *
|
|
+ * For backwards compatibility, when the GFB mode registers (WIDTH,
|
|
+ * HEIGHT, PITCHLOCK, BITS_PER_PIXEL) are modified, the SVGA device
|
|
+ * deletes all screens other than screen #0, and redefines screen
|
|
+ * #0 according to the specified mode. Drivers that use
|
|
+ * SVGA_CMD_DEFINE_SCREEN should destroy or redefine screen #0.
|
|
+ *
|
|
+ * If you use screen objects, do not use the legacy multi-mon
|
|
+ * registers (SVGA_REG_NUM_GUEST_DISPLAYS, SVGA_REG_DISPLAY_*).
|
|
+ *
|
|
+ * Availability:
|
|
+ * SVGA_FIFO_CAP_SCREEN_OBJECT
|
|
+ */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ SVGAScreenObject screen; // Variable-length according to version
|
|
+} SVGAFifoCmdDefineScreen;
|
|
+
|
|
+
|
|
+/*
|
|
+ * SVGA_CMD_DESTROY_SCREEN --
|
|
+ *
|
|
+ * Destroy an SVGAScreenObject. Its ID is immediately available for
|
|
+ * re-use.
|
|
+ *
|
|
+ * Availability:
|
|
+ * SVGA_FIFO_CAP_SCREEN_OBJECT
|
|
+ */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ uint32 screenId;
|
|
+} SVGAFifoCmdDestroyScreen;
|
|
+
|
|
+
|
|
+/*
|
|
+ * SVGA_CMD_DEFINE_GMRFB --
|
|
+ *
|
|
+ * This command sets a piece of SVGA device state called the
|
|
+ * Guest Memory Region Framebuffer, or GMRFB. The GMRFB is a
|
|
+ * piece of light-weight state which identifies the location and
|
|
+ * format of an image in guest memory or in BAR1. The GMRFB has
|
|
+ * an arbitrary size, and it doesn't need to match the geometry
|
|
+ * of the GFB or any screen object.
|
|
+ *
|
|
+ * The GMRFB can be redefined as often as you like. You could
|
|
+ * always use the same GMRFB, you could redefine it before
|
|
+ * rendering from a different guest screen, or you could even
|
|
+ * redefine it before every blit.
|
|
+ *
|
|
+ * There are multiple ways to use this command. The simplest way is
|
|
+ * to use it to move the framebuffer either to elsewhere in the GFB
|
|
+ * (BAR1) memory region, or to a user-defined GMR. This lets a
|
|
+ * driver use a framebuffer allocated entirely out of normal system
|
|
+ * memory, which we encourage.
|
|
+ *
|
|
+ * Another way to use this command is to set up a ring buffer of
|
|
+ * updates in GFB memory. If a driver wants to ensure that no
|
|
+ * frames are skipped by the SVGA device, it is important that the
|
|
+ * driver not modify the source data for a blit until the device is
|
|
+ * done processing the command. One efficient way to accomplish
|
|
+ * this is to use a ring of small DMA buffers. Each buffer is used
|
|
+ * for one blit, then we move on to the next buffer in the
|
|
+ * ring. The FENCE mechanism is used to protect each buffer from
|
|
+ * re-use until the device is finished with that buffer's
|
|
+ * corresponding blit.
|
|
+ *
|
|
+ * This command does not affect the meaning of SVGA_CMD_UPDATE.
|
|
+ * UPDATEs always occur from the legacy GFB memory area. This
|
|
+ * command has no support for pseudocolor GMRFBs. Currently only
|
|
+ * true-color 15, 16, and 24-bit depths are supported. Future
|
|
+ * devices may expose capabilities for additional framebuffer
|
|
+ * formats.
|
|
+ *
|
|
+ * The default GMRFB value is undefined. Drivers must always send
|
|
+ * this command at least once before performing any blit from the
|
|
+ * GMRFB.
|
|
+ *
|
|
+ * Availability:
|
|
+ * SVGA_FIFO_CAP_SCREEN_OBJECT
|
|
+ */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ SVGAGuestPtr ptr;
|
|
+ uint32 bytesPerLine;
|
|
+ SVGAGMRImageFormat format;
|
|
+} SVGAFifoCmdDefineGMRFB;
|
|
+
|
|
+
|
|
+/*
|
|
+ * SVGA_CMD_BLIT_GMRFB_TO_SCREEN --
|
|
+ *
|
|
+ * This is a guest-to-host blit. It performs a DMA operation to
|
|
+ * copy a rectangular region of pixels from the current GMRFB to
|
|
+ * one or more Screen Objects.
|
|
+ *
|
|
+ * The destination coordinate may be specified relative to a
|
|
+ * screen's origin (if a screen ID is specified) or relative to the
|
|
+ * virtual coordinate system's origin (if the screen ID is
|
|
+ * SVGA_ID_INVALID). The actual destination may span zero or more
|
|
+ * screens, in the case of a virtual destination rect or a rect
|
|
+ * which extends off the edge of the specified screen.
|
|
+ *
|
|
+ * This command writes to the screen's "base layer": the underlying
|
|
+ * framebuffer which exists below any cursor or video overlays. No
|
|
+ * action is necessary to explicitly hide or update any overlays
|
|
+ * which exist on top of the updated region.
|
|
+ *
|
|
+ * The SVGA device is guaranteed to finish reading from the GMRFB
|
|
+ * by the time any subsequent FENCE commands are reached.
|
|
+ *
|
|
+ * This command consumes an annotation. See the
|
|
+ * SVGA_CMD_ANNOTATION_* commands for details.
|
|
+ *
|
|
+ * Availability:
|
|
+ * SVGA_FIFO_CAP_SCREEN_OBJECT
|
|
+ */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ SVGASignedPoint srcOrigin;
|
|
+ SVGASignedRect destRect;
|
|
+ uint32 destScreenId;
|
|
+} SVGAFifoCmdBlitGMRFBToScreen;
|
|
+
|
|
+
|
|
+/*
|
|
+ * SVGA_CMD_BLIT_SCREEN_TO_GMRFB --
|
|
+ *
|
|
+ * This is a host-to-guest blit. It performs a DMA operation to
|
|
+ * copy a rectangular region of pixels from a single Screen Object
|
|
+ * back to the current GMRFB.
|
|
+ *
|
|
+ * Usage note: This command should be used rarely. It will
|
|
+ * typically be inefficient, but it is necessary for some types of
|
|
+ * synchronization between 3D (GPU) and 2D (CPU) rendering into
|
|
+ * overlapping areas of a screen.
|
|
+ *
|
|
+ * The source coordinate is specified relative to a screen's
|
|
+ * origin. The provided screen ID must be valid. If any parameters
|
|
+ * are invalid, the resulting pixel values are undefined.
|
|
+ *
|
|
+ * This command reads the screen's "base layer". Overlays like
|
|
+ * video and cursor are not included, but any data which was sent
|
|
+ * using a blit-to-screen primitive will be available, no matter
|
|
+ * whether the data's original source was the GMRFB or the 3D
|
|
+ * acceleration hardware.
|
|
+ *
|
|
+ * Note that our guest-to-host blits and host-to-guest blits aren't
|
|
+ * symmetric in their current implementation. While the parameters
|
|
+ * are identical, host-to-guest blits are a lot less featureful.
|
|
+ * They do not support clipping: If the source parameters don't
|
|
+ * fully fit within a screen, the blit fails. They must originate
|
|
+ * from exactly one screen. Virtual coordinates are not directly
|
|
+ * supported.
|
|
+ *
|
|
+ * Host-to-guest blits do support the same set of GMRFB formats
|
|
+ * offered by guest-to-host blits.
|
|
+ *
|
|
+ * The SVGA device is guaranteed to finish writing to the GMRFB by
|
|
+ * the time any subsequent FENCE commands are reached.
|
|
+ *
|
|
+ * Availability:
|
|
+ * SVGA_FIFO_CAP_SCREEN_OBJECT
|
|
+ */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ SVGASignedPoint destOrigin;
|
|
+ SVGASignedRect srcRect;
|
|
+ uint32 srcScreenId;
|
|
+} SVGAFifoCmdBlitScreenToGMRFB;
|
|
+
|
|
+
|
|
+/*
|
|
+ * SVGA_CMD_ANNOTATION_FILL --
|
|
+ *
|
|
+ * This is a blit annotation. This command stores a small piece of
|
|
+ * device state which is consumed by the next blit-to-screen
|
|
+ * command. The state is only cleared by commands which are
|
|
+ * specifically documented as consuming an annotation. Other
|
|
+ * commands (such as ESCAPEs for debugging) may intervene between
|
|
+ * the annotation and its associated blit.
|
|
+ *
|
|
+ * This annotation is a promise about the contents of the next
|
|
+ * blit: The video driver is guaranteeing that all pixels in that
|
|
+ * blit will have the same value, specified here as a color in
|
|
+ * SVGAColorBGRX format.
|
|
+ *
|
|
+ * The SVGA device can still render the blit correctly even if it
|
|
+ * ignores this annotation, but the annotation may allow it to
|
|
+ * perform the blit more efficiently, for example by ignoring the
|
|
+ * source data and performing a fill in hardware.
|
|
+ *
|
|
+ * This annotation is most important for performance when the
|
|
+ * user's display is being remoted over a network connection.
|
|
+ *
|
|
+ * Availability:
|
|
+ * SVGA_FIFO_CAP_SCREEN_OBJECT
|
|
+ */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ SVGAColorBGRX color;
|
|
+} SVGAFifoCmdAnnotationFill;
|
|
+
|
|
+
|
|
+/*
|
|
+ * SVGA_CMD_ANNOTATION_COPY --
|
|
+ *
|
|
+ * This is a blit annotation. See SVGA_CMD_ANNOTATION_FILL for more
|
|
+ * information about annotations.
|
|
+ *
|
|
+ * This annotation is a promise about the contents of the next
|
|
+ * blit: The video driver is guaranteeing that all pixels in that
|
|
+ * blit will have the same value as those which already exist at an
|
|
+ * identically-sized region on the same or a different screen.
|
|
+ *
|
|
+ * Note that the source pixels for the COPY in this annotation are
|
|
+ * sampled before applying the anqnotation's associated blit. They
|
|
+ * are allowed to overlap with the blit's destination pixels.
|
|
+ *
|
|
+ * The copy source rectangle is specified the same way as the blit
|
|
+ * destination: it can be a rectangle which spans zero or more
|
|
+ * screens, specified relative to either a screen or to the virtual
|
|
+ * coordinate system's origin. If the source rectangle includes
|
|
+ * pixels which are not from exactly one screen, the results are
|
|
+ * undefined.
|
|
+ *
|
|
+ * Availability:
|
|
+ * SVGA_FIFO_CAP_SCREEN_OBJECT
|
|
+ */
|
|
+
|
|
+typedef
|
|
+struct {
|
|
+ SVGASignedPoint srcOrigin;
|
|
+ uint32 srcScreenId;
|
|
+} SVGAFifoCmdAnnotationCopy;
|
|
+
|
|
+#endif
|
|
diff --git a/drivers/gpu/drm/vmwgfx/svga_types.h b/drivers/gpu/drm/vmwgfx/svga_types.h
|
|
new file mode 100644
|
|
index 0000000..55836de
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/vmwgfx/svga_types.h
|
|
@@ -0,0 +1,45 @@
|
|
+/**************************************************************************
|
|
+ *
|
|
+ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sub license, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial portions
|
|
+ * of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ **************************************************************************/
|
|
+
|
|
+/**
|
|
+ * Silly typedefs for the svga headers. Currently the headers are shared
|
|
+ * between all components that talk to svga. And as such the headers are
|
|
+ * are in a completely different style and use weird defines.
|
|
+ *
|
|
+ * This file lets all the ugly be prefixed with svga*.
|
|
+ */
|
|
+
|
|
+#ifndef _SVGA_TYPES_H_
|
|
+#define _SVGA_TYPES_H_
|
|
+
|
|
+typedef uint16_t uint16;
|
|
+typedef uint32_t uint32;
|
|
+typedef uint8_t uint8;
|
|
+typedef int32_t int32;
|
|
+typedef bool Bool;
|
|
+
|
|
+#endif
|
|
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c
|
|
new file mode 100644
|
|
index 0000000..825ebe3
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c
|
|
@@ -0,0 +1,252 @@
|
|
+/**************************************************************************
|
|
+ *
|
|
+ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sub license, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial portions
|
|
+ * of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ **************************************************************************/
|
|
+
|
|
+#include "vmwgfx_drv.h"
|
|
+#include "ttm/ttm_bo_driver.h"
|
|
+#include "ttm/ttm_placement.h"
|
|
+
|
|
+static uint32_t vram_placement_flags = TTM_PL_FLAG_VRAM |
|
|
+ TTM_PL_FLAG_CACHED;
|
|
+
|
|
+static uint32_t vram_ne_placement_flags = TTM_PL_FLAG_VRAM |
|
|
+ TTM_PL_FLAG_CACHED |
|
|
+ TTM_PL_FLAG_NO_EVICT;
|
|
+
|
|
+static uint32_t sys_placement_flags = TTM_PL_FLAG_SYSTEM |
|
|
+ TTM_PL_FLAG_CACHED;
|
|
+
|
|
+struct ttm_placement vmw_vram_placement = {
|
|
+ .fpfn = 0,
|
|
+ .lpfn = 0,
|
|
+ .num_placement = 1,
|
|
+ .placement = &vram_placement_flags,
|
|
+ .num_busy_placement = 1,
|
|
+ .busy_placement = &vram_placement_flags
|
|
+};
|
|
+
|
|
+struct ttm_placement vmw_vram_sys_placement = {
|
|
+ .fpfn = 0,
|
|
+ .lpfn = 0,
|
|
+ .num_placement = 1,
|
|
+ .placement = &vram_placement_flags,
|
|
+ .num_busy_placement = 1,
|
|
+ .busy_placement = &sys_placement_flags
|
|
+};
|
|
+
|
|
+struct ttm_placement vmw_vram_ne_placement = {
|
|
+ .fpfn = 0,
|
|
+ .lpfn = 0,
|
|
+ .num_placement = 1,
|
|
+ .placement = &vram_ne_placement_flags,
|
|
+ .num_busy_placement = 1,
|
|
+ .busy_placement = &vram_ne_placement_flags
|
|
+};
|
|
+
|
|
+struct ttm_placement vmw_sys_placement = {
|
|
+ .fpfn = 0,
|
|
+ .lpfn = 0,
|
|
+ .num_placement = 1,
|
|
+ .placement = &sys_placement_flags,
|
|
+ .num_busy_placement = 1,
|
|
+ .busy_placement = &sys_placement_flags
|
|
+};
|
|
+
|
|
+struct vmw_ttm_backend {
|
|
+ struct ttm_backend backend;
|
|
+};
|
|
+
|
|
+static int vmw_ttm_populate(struct ttm_backend *backend,
|
|
+ unsigned long num_pages, struct page **pages,
|
|
+ struct page *dummy_read_page)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vmw_ttm_bind(struct ttm_backend *backend, struct ttm_mem_reg *bo_mem)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vmw_ttm_unbind(struct ttm_backend *backend)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void vmw_ttm_clear(struct ttm_backend *backend)
|
|
+{
|
|
+}
|
|
+
|
|
+static void vmw_ttm_destroy(struct ttm_backend *backend)
|
|
+{
|
|
+ struct vmw_ttm_backend *vmw_be =
|
|
+ container_of(backend, struct vmw_ttm_backend, backend);
|
|
+
|
|
+ kfree(vmw_be);
|
|
+}
|
|
+
|
|
+static struct ttm_backend_func vmw_ttm_func = {
|
|
+ .populate = vmw_ttm_populate,
|
|
+ .clear = vmw_ttm_clear,
|
|
+ .bind = vmw_ttm_bind,
|
|
+ .unbind = vmw_ttm_unbind,
|
|
+ .destroy = vmw_ttm_destroy,
|
|
+};
|
|
+
|
|
+struct ttm_backend *vmw_ttm_backend_init(struct ttm_bo_device *bdev)
|
|
+{
|
|
+ struct vmw_ttm_backend *vmw_be;
|
|
+
|
|
+ vmw_be = kmalloc(sizeof(*vmw_be), GFP_KERNEL);
|
|
+ if (!vmw_be)
|
|
+ return NULL;
|
|
+
|
|
+ vmw_be->backend.func = &vmw_ttm_func;
|
|
+
|
|
+ return &vmw_be->backend;
|
|
+}
|
|
+
|
|
+int vmw_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int vmw_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
|
|
+ struct ttm_mem_type_manager *man)
|
|
+{
|
|
+ struct vmw_private *dev_priv =
|
|
+ container_of(bdev, struct vmw_private, bdev);
|
|
+
|
|
+ switch (type) {
|
|
+ case TTM_PL_SYSTEM:
|
|
+ /* System memory */
|
|
+
|
|
+ man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
|
|
+ man->available_caching = TTM_PL_MASK_CACHING;
|
|
+ man->default_caching = TTM_PL_FLAG_CACHED;
|
|
+ break;
|
|
+ case TTM_PL_VRAM:
|
|
+ /* "On-card" video ram */
|
|
+ man->gpu_offset = 0;
|
|
+ man->io_offset = dev_priv->vram_start;
|
|
+ man->io_size = dev_priv->vram_size;
|
|
+ man->flags = TTM_MEMTYPE_FLAG_FIXED |
|
|
+ TTM_MEMTYPE_FLAG_NEEDS_IOREMAP | TTM_MEMTYPE_FLAG_MAPPABLE;
|
|
+ man->io_addr = NULL;
|
|
+ man->available_caching = TTM_PL_MASK_CACHING;
|
|
+ man->default_caching = TTM_PL_FLAG_WC;
|
|
+ break;
|
|
+ default:
|
|
+ DRM_ERROR("Unsupported memory type %u\n", (unsigned)type);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void vmw_evict_flags(struct ttm_buffer_object *bo,
|
|
+ struct ttm_placement *placement)
|
|
+{
|
|
+ *placement = vmw_sys_placement;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * FIXME: Proper access checks on buffers.
|
|
+ */
|
|
+
|
|
+static int vmw_verify_access(struct ttm_buffer_object *bo, struct file *filp)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void vmw_move_notify(struct ttm_buffer_object *bo,
|
|
+ struct ttm_mem_reg *new_mem)
|
|
+{
|
|
+ if (new_mem->mem_type != TTM_PL_SYSTEM)
|
|
+ vmw_dmabuf_gmr_unbind(bo);
|
|
+}
|
|
+
|
|
+static void vmw_swap_notify(struct ttm_buffer_object *bo)
|
|
+{
|
|
+ vmw_dmabuf_gmr_unbind(bo);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * FIXME: We're using the old vmware polling method to sync.
|
|
+ * Do this with fences instead.
|
|
+ */
|
|
+
|
|
+static void *vmw_sync_obj_ref(void *sync_obj)
|
|
+{
|
|
+ return sync_obj;
|
|
+}
|
|
+
|
|
+static void vmw_sync_obj_unref(void **sync_obj)
|
|
+{
|
|
+ *sync_obj = NULL;
|
|
+}
|
|
+
|
|
+static int vmw_sync_obj_flush(void *sync_obj, void *sync_arg)
|
|
+{
|
|
+ struct vmw_private *dev_priv = (struct vmw_private *)sync_arg;
|
|
+
|
|
+ mutex_lock(&dev_priv->hw_mutex);
|
|
+ vmw_write(dev_priv, SVGA_REG_SYNC, SVGA_SYNC_GENERIC);
|
|
+ mutex_unlock(&dev_priv->hw_mutex);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static bool vmw_sync_obj_signaled(void *sync_obj, void *sync_arg)
|
|
+{
|
|
+ struct vmw_private *dev_priv = (struct vmw_private *)sync_arg;
|
|
+ uint32_t sequence = (unsigned long) sync_obj;
|
|
+
|
|
+ return vmw_fence_signaled(dev_priv, sequence);
|
|
+}
|
|
+
|
|
+static int vmw_sync_obj_wait(void *sync_obj, void *sync_arg,
|
|
+ bool lazy, bool interruptible)
|
|
+{
|
|
+ struct vmw_private *dev_priv = (struct vmw_private *)sync_arg;
|
|
+ uint32_t sequence = (unsigned long) sync_obj;
|
|
+
|
|
+ return vmw_wait_fence(dev_priv, false, sequence, false, 3*HZ);
|
|
+}
|
|
+
|
|
+struct ttm_bo_driver vmw_bo_driver = {
|
|
+ .create_ttm_backend_entry = vmw_ttm_backend_init,
|
|
+ .invalidate_caches = vmw_invalidate_caches,
|
|
+ .init_mem_type = vmw_init_mem_type,
|
|
+ .evict_flags = vmw_evict_flags,
|
|
+ .move = NULL,
|
|
+ .verify_access = vmw_verify_access,
|
|
+ .sync_obj_signaled = vmw_sync_obj_signaled,
|
|
+ .sync_obj_wait = vmw_sync_obj_wait,
|
|
+ .sync_obj_flush = vmw_sync_obj_flush,
|
|
+ .sync_obj_unref = vmw_sync_obj_unref,
|
|
+ .sync_obj_ref = vmw_sync_obj_ref,
|
|
+ .move_notify = vmw_move_notify,
|
|
+ .swap_notify = vmw_swap_notify
|
|
+};
|
|
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
|
|
new file mode 100644
|
|
index 0000000..a6e8f68
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
|
|
@@ -0,0 +1,800 @@
|
|
+/**************************************************************************
|
|
+ *
|
|
+ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sub license, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial portions
|
|
+ * of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ **************************************************************************/
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "vmwgfx_drv.h"
|
|
+#include "ttm/ttm_placement.h"
|
|
+#include "ttm/ttm_bo_driver.h"
|
|
+#include "ttm/ttm_object.h"
|
|
+#include "ttm/ttm_module.h"
|
|
+
|
|
+#define VMWGFX_DRIVER_NAME "vmwgfx"
|
|
+#define VMWGFX_DRIVER_DESC "Linux drm driver for VMware graphics devices"
|
|
+#define VMWGFX_CHIP_SVGAII 0
|
|
+#define VMW_FB_RESERVATION 0
|
|
+
|
|
+/**
|
|
+ * Fully encoded drm commands. Might move to vmw_drm.h
|
|
+ */
|
|
+
|
|
+#define DRM_IOCTL_VMW_GET_PARAM \
|
|
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_GET_PARAM, \
|
|
+ struct drm_vmw_getparam_arg)
|
|
+#define DRM_IOCTL_VMW_ALLOC_DMABUF \
|
|
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_ALLOC_DMABUF, \
|
|
+ union drm_vmw_alloc_dmabuf_arg)
|
|
+#define DRM_IOCTL_VMW_UNREF_DMABUF \
|
|
+ DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_UNREF_DMABUF, \
|
|
+ struct drm_vmw_unref_dmabuf_arg)
|
|
+#define DRM_IOCTL_VMW_CURSOR_BYPASS \
|
|
+ DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_CURSOR_BYPASS, \
|
|
+ struct drm_vmw_cursor_bypass_arg)
|
|
+
|
|
+#define DRM_IOCTL_VMW_CONTROL_STREAM \
|
|
+ DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_CONTROL_STREAM, \
|
|
+ struct drm_vmw_control_stream_arg)
|
|
+#define DRM_IOCTL_VMW_CLAIM_STREAM \
|
|
+ DRM_IOR(DRM_COMMAND_BASE + DRM_VMW_CLAIM_STREAM, \
|
|
+ struct drm_vmw_stream_arg)
|
|
+#define DRM_IOCTL_VMW_UNREF_STREAM \
|
|
+ DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_UNREF_STREAM, \
|
|
+ struct drm_vmw_stream_arg)
|
|
+
|
|
+#define DRM_IOCTL_VMW_CREATE_CONTEXT \
|
|
+ DRM_IOR(DRM_COMMAND_BASE + DRM_VMW_CREATE_CONTEXT, \
|
|
+ struct drm_vmw_context_arg)
|
|
+#define DRM_IOCTL_VMW_UNREF_CONTEXT \
|
|
+ DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_UNREF_CONTEXT, \
|
|
+ struct drm_vmw_context_arg)
|
|
+#define DRM_IOCTL_VMW_CREATE_SURFACE \
|
|
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_CREATE_SURFACE, \
|
|
+ union drm_vmw_surface_create_arg)
|
|
+#define DRM_IOCTL_VMW_UNREF_SURFACE \
|
|
+ DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_UNREF_SURFACE, \
|
|
+ struct drm_vmw_surface_arg)
|
|
+#define DRM_IOCTL_VMW_REF_SURFACE \
|
|
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_REF_SURFACE, \
|
|
+ union drm_vmw_surface_reference_arg)
|
|
+#define DRM_IOCTL_VMW_EXECBUF \
|
|
+ DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_EXECBUF, \
|
|
+ struct drm_vmw_execbuf_arg)
|
|
+#define DRM_IOCTL_VMW_FIFO_DEBUG \
|
|
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_FIFO_DEBUG, \
|
|
+ struct drm_vmw_fifo_debug_arg)
|
|
+#define DRM_IOCTL_VMW_FENCE_WAIT \
|
|
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_FENCE_WAIT, \
|
|
+ struct drm_vmw_fence_wait_arg)
|
|
+
|
|
+
|
|
+/**
|
|
+ * The core DRM version of this macro doesn't account for
|
|
+ * DRM_COMMAND_BASE.
|
|
+ */
|
|
+
|
|
+#define VMW_IOCTL_DEF(ioctl, func, flags) \
|
|
+ [DRM_IOCTL_NR(ioctl) - DRM_COMMAND_BASE] = {ioctl, flags, func}
|
|
+
|
|
+/**
|
|
+ * Ioctl definitions.
|
|
+ */
|
|
+
|
|
+static struct drm_ioctl_desc vmw_ioctls[] = {
|
|
+ VMW_IOCTL_DEF(DRM_IOCTL_VMW_GET_PARAM, vmw_getparam_ioctl,
|
|
+ DRM_AUTH | DRM_UNLOCKED),
|
|
+ VMW_IOCTL_DEF(DRM_IOCTL_VMW_ALLOC_DMABUF, vmw_dmabuf_alloc_ioctl,
|
|
+ DRM_AUTH | DRM_UNLOCKED),
|
|
+ VMW_IOCTL_DEF(DRM_IOCTL_VMW_UNREF_DMABUF, vmw_dmabuf_unref_ioctl,
|
|
+ DRM_AUTH | DRM_UNLOCKED),
|
|
+ VMW_IOCTL_DEF(DRM_IOCTL_VMW_CURSOR_BYPASS,
|
|
+ vmw_kms_cursor_bypass_ioctl,
|
|
+ DRM_MASTER | DRM_CONTROL_ALLOW | DRM_UNLOCKED),
|
|
+
|
|
+ VMW_IOCTL_DEF(DRM_IOCTL_VMW_CONTROL_STREAM, vmw_overlay_ioctl,
|
|
+ DRM_MASTER | DRM_CONTROL_ALLOW | DRM_UNLOCKED),
|
|
+ VMW_IOCTL_DEF(DRM_IOCTL_VMW_CLAIM_STREAM, vmw_stream_claim_ioctl,
|
|
+ DRM_MASTER | DRM_CONTROL_ALLOW | DRM_UNLOCKED),
|
|
+ VMW_IOCTL_DEF(DRM_IOCTL_VMW_UNREF_STREAM, vmw_stream_unref_ioctl,
|
|
+ DRM_MASTER | DRM_CONTROL_ALLOW | DRM_UNLOCKED),
|
|
+
|
|
+ VMW_IOCTL_DEF(DRM_IOCTL_VMW_CREATE_CONTEXT, vmw_context_define_ioctl,
|
|
+ DRM_AUTH | DRM_UNLOCKED),
|
|
+ VMW_IOCTL_DEF(DRM_IOCTL_VMW_UNREF_CONTEXT, vmw_context_destroy_ioctl,
|
|
+ DRM_AUTH | DRM_UNLOCKED),
|
|
+ VMW_IOCTL_DEF(DRM_IOCTL_VMW_CREATE_SURFACE, vmw_surface_define_ioctl,
|
|
+ DRM_AUTH | DRM_UNLOCKED),
|
|
+ VMW_IOCTL_DEF(DRM_IOCTL_VMW_UNREF_SURFACE, vmw_surface_destroy_ioctl,
|
|
+ DRM_AUTH | DRM_UNLOCKED),
|
|
+ VMW_IOCTL_DEF(DRM_IOCTL_VMW_REF_SURFACE, vmw_surface_reference_ioctl,
|
|
+ DRM_AUTH | DRM_UNLOCKED),
|
|
+ VMW_IOCTL_DEF(DRM_IOCTL_VMW_EXECBUF, vmw_execbuf_ioctl,
|
|
+ DRM_AUTH | DRM_UNLOCKED),
|
|
+ VMW_IOCTL_DEF(DRM_IOCTL_VMW_FIFO_DEBUG, vmw_fifo_debug_ioctl,
|
|
+ DRM_AUTH | DRM_ROOT_ONLY | DRM_MASTER | DRM_UNLOCKED),
|
|
+ VMW_IOCTL_DEF(DRM_IOCTL_VMW_FENCE_WAIT, vmw_fence_wait_ioctl,
|
|
+ DRM_AUTH | DRM_UNLOCKED)
|
|
+};
|
|
+
|
|
+static struct pci_device_id vmw_pci_id_list[] = {
|
|
+ {0x15ad, 0x0405, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VMWGFX_CHIP_SVGAII},
|
|
+ {0, 0, 0}
|
|
+};
|
|
+
|
|
+static char *vmw_devname = "vmwgfx";
|
|
+
|
|
+static int vmw_probe(struct pci_dev *, const struct pci_device_id *);
|
|
+static void vmw_master_init(struct vmw_master *);
|
|
+static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val,
|
|
+ void *ptr);
|
|
+
|
|
+static void vmw_print_capabilities(uint32_t capabilities)
|
|
+{
|
|
+ DRM_INFO("Capabilities:\n");
|
|
+ if (capabilities & SVGA_CAP_RECT_COPY)
|
|
+ DRM_INFO(" Rect copy.\n");
|
|
+ if (capabilities & SVGA_CAP_CURSOR)
|
|
+ DRM_INFO(" Cursor.\n");
|
|
+ if (capabilities & SVGA_CAP_CURSOR_BYPASS)
|
|
+ DRM_INFO(" Cursor bypass.\n");
|
|
+ if (capabilities & SVGA_CAP_CURSOR_BYPASS_2)
|
|
+ DRM_INFO(" Cursor bypass 2.\n");
|
|
+ if (capabilities & SVGA_CAP_8BIT_EMULATION)
|
|
+ DRM_INFO(" 8bit emulation.\n");
|
|
+ if (capabilities & SVGA_CAP_ALPHA_CURSOR)
|
|
+ DRM_INFO(" Alpha cursor.\n");
|
|
+ if (capabilities & SVGA_CAP_3D)
|
|
+ DRM_INFO(" 3D.\n");
|
|
+ if (capabilities & SVGA_CAP_EXTENDED_FIFO)
|
|
+ DRM_INFO(" Extended Fifo.\n");
|
|
+ if (capabilities & SVGA_CAP_MULTIMON)
|
|
+ DRM_INFO(" Multimon.\n");
|
|
+ if (capabilities & SVGA_CAP_PITCHLOCK)
|
|
+ DRM_INFO(" Pitchlock.\n");
|
|
+ if (capabilities & SVGA_CAP_IRQMASK)
|
|
+ DRM_INFO(" Irq mask.\n");
|
|
+ if (capabilities & SVGA_CAP_DISPLAY_TOPOLOGY)
|
|
+ DRM_INFO(" Display Topology.\n");
|
|
+ if (capabilities & SVGA_CAP_GMR)
|
|
+ DRM_INFO(" GMR.\n");
|
|
+ if (capabilities & SVGA_CAP_TRACES)
|
|
+ DRM_INFO(" Traces.\n");
|
|
+}
|
|
+
|
|
+static int vmw_request_device(struct vmw_private *dev_priv)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ vmw_kms_save_vga(dev_priv);
|
|
+
|
|
+ ret = vmw_fifo_init(dev_priv, &dev_priv->fifo);
|
|
+ if (unlikely(ret != 0)) {
|
|
+ DRM_ERROR("Unable to initialize FIFO.\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void vmw_release_device(struct vmw_private *dev_priv)
|
|
+{
|
|
+ vmw_fifo_release(dev_priv, &dev_priv->fifo);
|
|
+ vmw_kms_restore_vga(dev_priv);
|
|
+}
|
|
+
|
|
+
|
|
+static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
|
|
+{
|
|
+ struct vmw_private *dev_priv;
|
|
+ int ret;
|
|
+ uint32_t svga_id;
|
|
+
|
|
+ dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL);
|
|
+ if (unlikely(dev_priv == NULL)) {
|
|
+ DRM_ERROR("Failed allocating a device private struct.\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ memset(dev_priv, 0, sizeof(*dev_priv));
|
|
+
|
|
+ dev_priv->dev = dev;
|
|
+ dev_priv->vmw_chipset = chipset;
|
|
+ dev_priv->last_read_sequence = (uint32_t) -100;
|
|
+ mutex_init(&dev_priv->hw_mutex);
|
|
+ mutex_init(&dev_priv->cmdbuf_mutex);
|
|
+ rwlock_init(&dev_priv->resource_lock);
|
|
+ idr_init(&dev_priv->context_idr);
|
|
+ idr_init(&dev_priv->surface_idr);
|
|
+ idr_init(&dev_priv->stream_idr);
|
|
+ ida_init(&dev_priv->gmr_ida);
|
|
+ mutex_init(&dev_priv->init_mutex);
|
|
+ init_waitqueue_head(&dev_priv->fence_queue);
|
|
+ init_waitqueue_head(&dev_priv->fifo_queue);
|
|
+ atomic_set(&dev_priv->fence_queue_waiters, 0);
|
|
+ atomic_set(&dev_priv->fifo_queue_waiters, 0);
|
|
+ INIT_LIST_HEAD(&dev_priv->gmr_lru);
|
|
+
|
|
+ dev_priv->io_start = pci_resource_start(dev->pdev, 0);
|
|
+ dev_priv->vram_start = pci_resource_start(dev->pdev, 1);
|
|
+ dev_priv->mmio_start = pci_resource_start(dev->pdev, 2);
|
|
+
|
|
+ mutex_lock(&dev_priv->hw_mutex);
|
|
+
|
|
+ vmw_write(dev_priv, SVGA_REG_ID, SVGA_ID_2);
|
|
+ svga_id = vmw_read(dev_priv, SVGA_REG_ID);
|
|
+ if (svga_id != SVGA_ID_2) {
|
|
+ ret = -ENOSYS;
|
|
+ DRM_ERROR("Unsuported SVGA ID 0x%x\n", svga_id);
|
|
+ mutex_unlock(&dev_priv->hw_mutex);
|
|
+ goto out_err0;
|
|
+ }
|
|
+
|
|
+ dev_priv->capabilities = vmw_read(dev_priv, SVGA_REG_CAPABILITIES);
|
|
+
|
|
+ if (dev_priv->capabilities & SVGA_CAP_GMR) {
|
|
+ dev_priv->max_gmr_descriptors =
|
|
+ vmw_read(dev_priv,
|
|
+ SVGA_REG_GMR_MAX_DESCRIPTOR_LENGTH);
|
|
+ dev_priv->max_gmr_ids =
|
|
+ vmw_read(dev_priv, SVGA_REG_GMR_MAX_IDS);
|
|
+ }
|
|
+
|
|
+ dev_priv->vram_size = vmw_read(dev_priv, SVGA_REG_VRAM_SIZE);
|
|
+ dev_priv->mmio_size = vmw_read(dev_priv, SVGA_REG_MEM_SIZE);
|
|
+ dev_priv->fb_max_width = vmw_read(dev_priv, SVGA_REG_MAX_WIDTH);
|
|
+ dev_priv->fb_max_height = vmw_read(dev_priv, SVGA_REG_MAX_HEIGHT);
|
|
+
|
|
+ mutex_unlock(&dev_priv->hw_mutex);
|
|
+
|
|
+ vmw_print_capabilities(dev_priv->capabilities);
|
|
+
|
|
+ if (dev_priv->capabilities & SVGA_CAP_GMR) {
|
|
+ DRM_INFO("Max GMR ids is %u\n",
|
|
+ (unsigned)dev_priv->max_gmr_ids);
|
|
+ DRM_INFO("Max GMR descriptors is %u\n",
|
|
+ (unsigned)dev_priv->max_gmr_descriptors);
|
|
+ }
|
|
+ DRM_INFO("VRAM at 0x%08x size is %u kiB\n",
|
|
+ dev_priv->vram_start, dev_priv->vram_size / 1024);
|
|
+ DRM_INFO("MMIO at 0x%08x size is %u kiB\n",
|
|
+ dev_priv->mmio_start, dev_priv->mmio_size / 1024);
|
|
+
|
|
+ ret = vmw_ttm_global_init(dev_priv);
|
|
+ if (unlikely(ret != 0))
|
|
+ goto out_err0;
|
|
+
|
|
+
|
|
+ vmw_master_init(&dev_priv->fbdev_master);
|
|
+ ttm_lock_set_kill(&dev_priv->fbdev_master.lock, false, SIGTERM);
|
|
+ dev_priv->active_master = &dev_priv->fbdev_master;
|
|
+
|
|
+
|
|
+ ret = ttm_bo_device_init(&dev_priv->bdev,
|
|
+ dev_priv->bo_global_ref.ref.object,
|
|
+ &vmw_bo_driver, VMWGFX_FILE_PAGE_OFFSET,
|
|
+ false);
|
|
+ if (unlikely(ret != 0)) {
|
|
+ DRM_ERROR("Failed initializing TTM buffer object driver.\n");
|
|
+ goto out_err1;
|
|
+ }
|
|
+
|
|
+ ret = ttm_bo_init_mm(&dev_priv->bdev, TTM_PL_VRAM,
|
|
+ (dev_priv->vram_size >> PAGE_SHIFT));
|
|
+ if (unlikely(ret != 0)) {
|
|
+ DRM_ERROR("Failed initializing memory manager for VRAM.\n");
|
|
+ goto out_err2;
|
|
+ }
|
|
+
|
|
+ dev_priv->mmio_mtrr = drm_mtrr_add(dev_priv->mmio_start,
|
|
+ dev_priv->mmio_size, DRM_MTRR_WC);
|
|
+
|
|
+ dev_priv->mmio_virt = ioremap_wc(dev_priv->mmio_start,
|
|
+ dev_priv->mmio_size);
|
|
+
|
|
+ if (unlikely(dev_priv->mmio_virt == NULL)) {
|
|
+ ret = -ENOMEM;
|
|
+ DRM_ERROR("Failed mapping MMIO.\n");
|
|
+ goto out_err3;
|
|
+ }
|
|
+
|
|
+ dev_priv->tdev = ttm_object_device_init
|
|
+ (dev_priv->mem_global_ref.object, 12);
|
|
+
|
|
+ if (unlikely(dev_priv->tdev == NULL)) {
|
|
+ DRM_ERROR("Unable to initialize TTM object management.\n");
|
|
+ ret = -ENOMEM;
|
|
+ goto out_err4;
|
|
+ }
|
|
+
|
|
+ dev->dev_private = dev_priv;
|
|
+
|
|
+ if (!dev->devname)
|
|
+ dev->devname = vmw_devname;
|
|
+
|
|
+ if (dev_priv->capabilities & SVGA_CAP_IRQMASK) {
|
|
+ ret = drm_irq_install(dev);
|
|
+ if (unlikely(ret != 0)) {
|
|
+ DRM_ERROR("Failed installing irq: %d\n", ret);
|
|
+ goto out_no_irq;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ret = pci_request_regions(dev->pdev, "vmwgfx probe");
|
|
+ dev_priv->stealth = (ret != 0);
|
|
+ if (dev_priv->stealth) {
|
|
+ /**
|
|
+ * Request at least the mmio PCI resource.
|
|
+ */
|
|
+
|
|
+ DRM_INFO("It appears like vesafb is loaded. "
|
|
+ "Ignore above error if any. Entering stealth mode.\n");
|
|
+ ret = pci_request_region(dev->pdev, 2, "vmwgfx stealth probe");
|
|
+ if (unlikely(ret != 0)) {
|
|
+ DRM_ERROR("Failed reserving the SVGA MMIO resource.\n");
|
|
+ goto out_no_device;
|
|
+ }
|
|
+ vmw_kms_init(dev_priv);
|
|
+ vmw_overlay_init(dev_priv);
|
|
+ } else {
|
|
+ ret = vmw_request_device(dev_priv);
|
|
+ if (unlikely(ret != 0))
|
|
+ goto out_no_device;
|
|
+ vmw_kms_init(dev_priv);
|
|
+ vmw_overlay_init(dev_priv);
|
|
+ vmw_fb_init(dev_priv);
|
|
+ }
|
|
+
|
|
+ dev_priv->pm_nb.notifier_call = vmwgfx_pm_notifier;
|
|
+ register_pm_notifier(&dev_priv->pm_nb);
|
|
+
|
|
+ DRM_INFO("%s", vmw_fifo_have_3d(dev_priv) ? "Have 3D\n" : "No 3D\n");
|
|
+
|
|
+ return 0;
|
|
+
|
|
+out_no_device:
|
|
+ if (dev_priv->capabilities & SVGA_CAP_IRQMASK)
|
|
+ drm_irq_uninstall(dev_priv->dev);
|
|
+ if (dev->devname == vmw_devname)
|
|
+ dev->devname = NULL;
|
|
+out_no_irq:
|
|
+ ttm_object_device_release(&dev_priv->tdev);
|
|
+out_err4:
|
|
+ iounmap(dev_priv->mmio_virt);
|
|
+out_err3:
|
|
+ drm_mtrr_del(dev_priv->mmio_mtrr, dev_priv->mmio_start,
|
|
+ dev_priv->mmio_size, DRM_MTRR_WC);
|
|
+ (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM);
|
|
+out_err2:
|
|
+ (void)ttm_bo_device_release(&dev_priv->bdev);
|
|
+out_err1:
|
|
+ vmw_ttm_global_release(dev_priv);
|
|
+out_err0:
|
|
+ ida_destroy(&dev_priv->gmr_ida);
|
|
+ idr_destroy(&dev_priv->surface_idr);
|
|
+ idr_destroy(&dev_priv->context_idr);
|
|
+ idr_destroy(&dev_priv->stream_idr);
|
|
+ kfree(dev_priv);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int vmw_driver_unload(struct drm_device *dev)
|
|
+{
|
|
+ struct vmw_private *dev_priv = vmw_priv(dev);
|
|
+
|
|
+ DRM_INFO(VMWGFX_DRIVER_NAME " unload.\n");
|
|
+
|
|
+ unregister_pm_notifier(&dev_priv->pm_nb);
|
|
+
|
|
+ if (!dev_priv->stealth) {
|
|
+ vmw_fb_close(dev_priv);
|
|
+ vmw_kms_close(dev_priv);
|
|
+ vmw_overlay_close(dev_priv);
|
|
+ vmw_release_device(dev_priv);
|
|
+ pci_release_regions(dev->pdev);
|
|
+ } else {
|
|
+ vmw_kms_close(dev_priv);
|
|
+ vmw_overlay_close(dev_priv);
|
|
+ pci_release_region(dev->pdev, 2);
|
|
+ }
|
|
+ if (dev_priv->capabilities & SVGA_CAP_IRQMASK)
|
|
+ drm_irq_uninstall(dev_priv->dev);
|
|
+ if (dev->devname == vmw_devname)
|
|
+ dev->devname = NULL;
|
|
+ ttm_object_device_release(&dev_priv->tdev);
|
|
+ iounmap(dev_priv->mmio_virt);
|
|
+ drm_mtrr_del(dev_priv->mmio_mtrr, dev_priv->mmio_start,
|
|
+ dev_priv->mmio_size, DRM_MTRR_WC);
|
|
+ (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM);
|
|
+ (void)ttm_bo_device_release(&dev_priv->bdev);
|
|
+ vmw_ttm_global_release(dev_priv);
|
|
+ ida_destroy(&dev_priv->gmr_ida);
|
|
+ idr_destroy(&dev_priv->surface_idr);
|
|
+ idr_destroy(&dev_priv->context_idr);
|
|
+ idr_destroy(&dev_priv->stream_idr);
|
|
+
|
|
+ kfree(dev_priv);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void vmw_postclose(struct drm_device *dev,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct vmw_fpriv *vmw_fp;
|
|
+
|
|
+ vmw_fp = vmw_fpriv(file_priv);
|
|
+ ttm_object_file_release(&vmw_fp->tfile);
|
|
+ if (vmw_fp->locked_master)
|
|
+ drm_master_put(&vmw_fp->locked_master);
|
|
+ kfree(vmw_fp);
|
|
+}
|
|
+
|
|
+static int vmw_driver_open(struct drm_device *dev, struct drm_file *file_priv)
|
|
+{
|
|
+ struct vmw_private *dev_priv = vmw_priv(dev);
|
|
+ struct vmw_fpriv *vmw_fp;
|
|
+ int ret = -ENOMEM;
|
|
+
|
|
+ vmw_fp = kzalloc(sizeof(*vmw_fp), GFP_KERNEL);
|
|
+ if (unlikely(vmw_fp == NULL))
|
|
+ return ret;
|
|
+
|
|
+ vmw_fp->tfile = ttm_object_file_init(dev_priv->tdev, 10);
|
|
+ if (unlikely(vmw_fp->tfile == NULL))
|
|
+ goto out_no_tfile;
|
|
+
|
|
+ file_priv->driver_priv = vmw_fp;
|
|
+
|
|
+ if (unlikely(dev_priv->bdev.dev_mapping == NULL))
|
|
+ dev_priv->bdev.dev_mapping =
|
|
+ file_priv->filp->f_path.dentry->d_inode->i_mapping;
|
|
+
|
|
+ return 0;
|
|
+
|
|
+out_no_tfile:
|
|
+ kfree(vmw_fp);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static long vmw_unlocked_ioctl(struct file *filp, unsigned int cmd,
|
|
+ unsigned long arg)
|
|
+{
|
|
+ struct drm_file *file_priv = filp->private_data;
|
|
+ struct drm_device *dev = file_priv->minor->dev;
|
|
+ unsigned int nr = DRM_IOCTL_NR(cmd);
|
|
+
|
|
+ /*
|
|
+ * Do extra checking on driver private ioctls.
|
|
+ */
|
|
+
|
|
+ if ((nr >= DRM_COMMAND_BASE) && (nr < DRM_COMMAND_END)
|
|
+ && (nr < DRM_COMMAND_BASE + dev->driver->num_ioctls)) {
|
|
+ struct drm_ioctl_desc *ioctl =
|
|
+ &vmw_ioctls[nr - DRM_COMMAND_BASE];
|
|
+
|
|
+ if (unlikely(ioctl->cmd != cmd)) {
|
|
+ DRM_ERROR("Invalid command format, ioctl %d\n",
|
|
+ nr - DRM_COMMAND_BASE);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return drm_ioctl(filp, cmd, arg);
|
|
+}
|
|
+
|
|
+static int vmw_firstopen(struct drm_device *dev)
|
|
+{
|
|
+ struct vmw_private *dev_priv = vmw_priv(dev);
|
|
+ dev_priv->is_opened = true;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void vmw_lastclose(struct drm_device *dev)
|
|
+{
|
|
+ struct vmw_private *dev_priv = vmw_priv(dev);
|
|
+ struct drm_crtc *crtc;
|
|
+ struct drm_mode_set set;
|
|
+ int ret;
|
|
+
|
|
+ /**
|
|
+ * Do nothing on the lastclose call from drm_unload.
|
|
+ */
|
|
+
|
|
+ if (!dev_priv->is_opened)
|
|
+ return;
|
|
+
|
|
+ dev_priv->is_opened = false;
|
|
+ set.x = 0;
|
|
+ set.y = 0;
|
|
+ set.fb = NULL;
|
|
+ set.mode = NULL;
|
|
+ set.connectors = NULL;
|
|
+ set.num_connectors = 0;
|
|
+
|
|
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
|
+ set.crtc = crtc;
|
|
+ ret = crtc->funcs->set_config(&set);
|
|
+ WARN_ON(ret != 0);
|
|
+ }
|
|
+
|
|
+}
|
|
+
|
|
+static void vmw_master_init(struct vmw_master *vmaster)
|
|
+{
|
|
+ ttm_lock_init(&vmaster->lock);
|
|
+}
|
|
+
|
|
+static int vmw_master_create(struct drm_device *dev,
|
|
+ struct drm_master *master)
|
|
+{
|
|
+ struct vmw_master *vmaster;
|
|
+
|
|
+ DRM_INFO("Master create.\n");
|
|
+ vmaster = kzalloc(sizeof(*vmaster), GFP_KERNEL);
|
|
+ if (unlikely(vmaster == NULL))
|
|
+ return -ENOMEM;
|
|
+
|
|
+ ttm_lock_init(&vmaster->lock);
|
|
+ ttm_lock_set_kill(&vmaster->lock, true, SIGTERM);
|
|
+ master->driver_priv = vmaster;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void vmw_master_destroy(struct drm_device *dev,
|
|
+ struct drm_master *master)
|
|
+{
|
|
+ struct vmw_master *vmaster = vmw_master(master);
|
|
+
|
|
+ DRM_INFO("Master destroy.\n");
|
|
+ master->driver_priv = NULL;
|
|
+ kfree(vmaster);
|
|
+}
|
|
+
|
|
+
|
|
+static int vmw_master_set(struct drm_device *dev,
|
|
+ struct drm_file *file_priv,
|
|
+ bool from_open)
|
|
+{
|
|
+ struct vmw_private *dev_priv = vmw_priv(dev);
|
|
+ struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv);
|
|
+ struct vmw_master *active = dev_priv->active_master;
|
|
+ struct vmw_master *vmaster = vmw_master(file_priv->master);
|
|
+ int ret = 0;
|
|
+
|
|
+ DRM_INFO("Master set.\n");
|
|
+ if (dev_priv->stealth) {
|
|
+ ret = vmw_request_device(dev_priv);
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ if (active) {
|
|
+ BUG_ON(active != &dev_priv->fbdev_master);
|
|
+ ret = ttm_vt_lock(&active->lock, false, vmw_fp->tfile);
|
|
+ if (unlikely(ret != 0))
|
|
+ goto out_no_active_lock;
|
|
+
|
|
+ ttm_lock_set_kill(&active->lock, true, SIGTERM);
|
|
+ ret = ttm_bo_evict_mm(&dev_priv->bdev, TTM_PL_VRAM);
|
|
+ if (unlikely(ret != 0)) {
|
|
+ DRM_ERROR("Unable to clean VRAM on "
|
|
+ "master drop.\n");
|
|
+ }
|
|
+
|
|
+ dev_priv->active_master = NULL;
|
|
+ }
|
|
+
|
|
+ ttm_lock_set_kill(&vmaster->lock, false, SIGTERM);
|
|
+ if (!from_open) {
|
|
+ ttm_vt_unlock(&vmaster->lock);
|
|
+ BUG_ON(vmw_fp->locked_master != file_priv->master);
|
|
+ drm_master_put(&vmw_fp->locked_master);
|
|
+ }
|
|
+
|
|
+ dev_priv->active_master = vmaster;
|
|
+
|
|
+ return 0;
|
|
+
|
|
+out_no_active_lock:
|
|
+ vmw_release_device(dev_priv);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void vmw_master_drop(struct drm_device *dev,
|
|
+ struct drm_file *file_priv,
|
|
+ bool from_release)
|
|
+{
|
|
+ struct vmw_private *dev_priv = vmw_priv(dev);
|
|
+ struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv);
|
|
+ struct vmw_master *vmaster = vmw_master(file_priv->master);
|
|
+ int ret;
|
|
+
|
|
+ DRM_INFO("Master drop.\n");
|
|
+
|
|
+ /**
|
|
+ * Make sure the master doesn't disappear while we have
|
|
+ * it locked.
|
|
+ */
|
|
+
|
|
+ vmw_fp->locked_master = drm_master_get(file_priv->master);
|
|
+ ret = ttm_vt_lock(&vmaster->lock, false, vmw_fp->tfile);
|
|
+
|
|
+ if (unlikely((ret != 0))) {
|
|
+ DRM_ERROR("Unable to lock TTM at VT switch.\n");
|
|
+ drm_master_put(&vmw_fp->locked_master);
|
|
+ }
|
|
+
|
|
+ ttm_lock_set_kill(&vmaster->lock, true, SIGTERM);
|
|
+
|
|
+ if (dev_priv->stealth) {
|
|
+ ret = ttm_bo_evict_mm(&dev_priv->bdev, TTM_PL_VRAM);
|
|
+ if (unlikely(ret != 0))
|
|
+ DRM_ERROR("Unable to clean VRAM on master drop.\n");
|
|
+ vmw_release_device(dev_priv);
|
|
+ }
|
|
+ dev_priv->active_master = &dev_priv->fbdev_master;
|
|
+ ttm_lock_set_kill(&dev_priv->fbdev_master.lock, false, SIGTERM);
|
|
+ ttm_vt_unlock(&dev_priv->fbdev_master.lock);
|
|
+
|
|
+ if (!dev_priv->stealth)
|
|
+ vmw_fb_on(dev_priv);
|
|
+}
|
|
+
|
|
+
|
|
+static void vmw_remove(struct pci_dev *pdev)
|
|
+{
|
|
+ struct drm_device *dev = pci_get_drvdata(pdev);
|
|
+
|
|
+ drm_put_dev(dev);
|
|
+}
|
|
+
|
|
+static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val,
|
|
+ void *ptr)
|
|
+{
|
|
+ struct vmw_private *dev_priv =
|
|
+ container_of(nb, struct vmw_private, pm_nb);
|
|
+ struct vmw_master *vmaster = dev_priv->active_master;
|
|
+
|
|
+ switch (val) {
|
|
+ case PM_HIBERNATION_PREPARE:
|
|
+ case PM_SUSPEND_PREPARE:
|
|
+ ttm_suspend_lock(&vmaster->lock);
|
|
+
|
|
+ /**
|
|
+ * This empties VRAM and unbinds all GMR bindings.
|
|
+ * Buffer contents is moved to swappable memory.
|
|
+ */
|
|
+ ttm_bo_swapout_all(&dev_priv->bdev);
|
|
+ break;
|
|
+ case PM_POST_HIBERNATION:
|
|
+ case PM_POST_SUSPEND:
|
|
+ ttm_suspend_unlock(&vmaster->lock);
|
|
+ break;
|
|
+ case PM_RESTORE_PREPARE:
|
|
+ break;
|
|
+ case PM_POST_RESTORE:
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * These might not be needed with the virtual SVGA device.
|
|
+ */
|
|
+
|
|
+int vmw_pci_suspend(struct pci_dev *pdev, pm_message_t state)
|
|
+{
|
|
+ pci_save_state(pdev);
|
|
+ pci_disable_device(pdev);
|
|
+ pci_set_power_state(pdev, PCI_D3hot);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int vmw_pci_resume(struct pci_dev *pdev)
|
|
+{
|
|
+ pci_set_power_state(pdev, PCI_D0);
|
|
+ pci_restore_state(pdev);
|
|
+ return pci_enable_device(pdev);
|
|
+}
|
|
+
|
|
+static struct drm_driver driver = {
|
|
+ .driver_features = DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED |
|
|
+ DRIVER_MODESET,
|
|
+ .load = vmw_driver_load,
|
|
+ .unload = vmw_driver_unload,
|
|
+ .firstopen = vmw_firstopen,
|
|
+ .lastclose = vmw_lastclose,
|
|
+ .irq_preinstall = vmw_irq_preinstall,
|
|
+ .irq_postinstall = vmw_irq_postinstall,
|
|
+ .irq_uninstall = vmw_irq_uninstall,
|
|
+ .irq_handler = vmw_irq_handler,
|
|
+ .reclaim_buffers_locked = NULL,
|
|
+ .get_map_ofs = drm_core_get_map_ofs,
|
|
+ .get_reg_ofs = drm_core_get_reg_ofs,
|
|
+ .ioctls = vmw_ioctls,
|
|
+ .num_ioctls = DRM_ARRAY_SIZE(vmw_ioctls),
|
|
+ .dma_quiescent = NULL, /*vmw_dma_quiescent, */
|
|
+ .master_create = vmw_master_create,
|
|
+ .master_destroy = vmw_master_destroy,
|
|
+ .master_set = vmw_master_set,
|
|
+ .master_drop = vmw_master_drop,
|
|
+ .open = vmw_driver_open,
|
|
+ .postclose = vmw_postclose,
|
|
+ .fops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .open = drm_open,
|
|
+ .release = drm_release,
|
|
+ .unlocked_ioctl = vmw_unlocked_ioctl,
|
|
+ .mmap = vmw_mmap,
|
|
+ .poll = drm_poll,
|
|
+ .fasync = drm_fasync,
|
|
+#if defined(CONFIG_COMPAT)
|
|
+ .compat_ioctl = drm_compat_ioctl,
|
|
+#endif
|
|
+ },
|
|
+ .pci_driver = {
|
|
+ .name = VMWGFX_DRIVER_NAME,
|
|
+ .id_table = vmw_pci_id_list,
|
|
+ .probe = vmw_probe,
|
|
+ .remove = vmw_remove,
|
|
+ .suspend = vmw_pci_suspend,
|
|
+ .resume = vmw_pci_resume
|
|
+ },
|
|
+ .name = VMWGFX_DRIVER_NAME,
|
|
+ .desc = VMWGFX_DRIVER_DESC,
|
|
+ .date = VMWGFX_DRIVER_DATE,
|
|
+ .major = VMWGFX_DRIVER_MAJOR,
|
|
+ .minor = VMWGFX_DRIVER_MINOR,
|
|
+ .patchlevel = VMWGFX_DRIVER_PATCHLEVEL
|
|
+};
|
|
+
|
|
+static int vmw_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|
+{
|
|
+ return drm_get_dev(pdev, ent, &driver);
|
|
+}
|
|
+
|
|
+static int __init vmwgfx_init(void)
|
|
+{
|
|
+ int ret;
|
|
+ ret = drm_init(&driver);
|
|
+ if (ret)
|
|
+ DRM_ERROR("Failed initializing DRM.\n");
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void __exit vmwgfx_exit(void)
|
|
+{
|
|
+ drm_exit(&driver);
|
|
+}
|
|
+
|
|
+module_init(vmwgfx_init);
|
|
+module_exit(vmwgfx_exit);
|
|
+
|
|
+MODULE_AUTHOR("VMware Inc. and others");
|
|
+MODULE_DESCRIPTION("Standalone drm driver for the VMware SVGA device");
|
|
+MODULE_LICENSE("GPL and additional rights");
|
|
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
|
|
new file mode 100644
|
|
index 0000000..356dc93
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
|
|
@@ -0,0 +1,521 @@
|
|
+/**************************************************************************
|
|
+ *
|
|
+ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sub license, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial portions
|
|
+ * of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ **************************************************************************/
|
|
+
|
|
+#ifndef _VMWGFX_DRV_H_
|
|
+#define _VMWGFX_DRV_H_
|
|
+
|
|
+#include "vmwgfx_reg.h"
|
|
+#include "drmP.h"
|
|
+#include "vmwgfx_drm.h"
|
|
+#include "drm_hashtab.h"
|
|
+#include "linux/suspend.h"
|
|
+#include "ttm/ttm_bo_driver.h"
|
|
+#include "ttm/ttm_object.h"
|
|
+#include "ttm/ttm_lock.h"
|
|
+#include "ttm/ttm_execbuf_util.h"
|
|
+#include "ttm/ttm_module.h"
|
|
+
|
|
+#define VMWGFX_DRIVER_DATE "20100209"
|
|
+#define VMWGFX_DRIVER_MAJOR 1
|
|
+#define VMWGFX_DRIVER_MINOR 0
|
|
+#define VMWGFX_DRIVER_PATCHLEVEL 0
|
|
+#define VMWGFX_FILE_PAGE_OFFSET 0x00100000
|
|
+#define VMWGFX_FIFO_STATIC_SIZE (1024*1024)
|
|
+#define VMWGFX_MAX_RELOCATIONS 2048
|
|
+#define VMWGFX_MAX_GMRS 2048
|
|
+
|
|
+struct vmw_fpriv {
|
|
+ struct drm_master *locked_master;
|
|
+ struct ttm_object_file *tfile;
|
|
+};
|
|
+
|
|
+struct vmw_dma_buffer {
|
|
+ struct ttm_buffer_object base;
|
|
+ struct list_head validate_list;
|
|
+ struct list_head gmr_lru;
|
|
+ uint32_t gmr_id;
|
|
+ bool gmr_bound;
|
|
+ uint32_t cur_validate_node;
|
|
+ bool on_validate_list;
|
|
+};
|
|
+
|
|
+struct vmw_resource {
|
|
+ struct kref kref;
|
|
+ struct vmw_private *dev_priv;
|
|
+ struct idr *idr;
|
|
+ int id;
|
|
+ enum ttm_object_type res_type;
|
|
+ bool avail;
|
|
+ void (*hw_destroy) (struct vmw_resource *res);
|
|
+ void (*res_free) (struct vmw_resource *res);
|
|
+
|
|
+ /* TODO is a generic snooper needed? */
|
|
+#if 0
|
|
+ void (*snoop)(struct vmw_resource *res,
|
|
+ struct ttm_object_file *tfile,
|
|
+ SVGA3dCmdHeader *header);
|
|
+ void *snoop_priv;
|
|
+#endif
|
|
+};
|
|
+
|
|
+struct vmw_cursor_snooper {
|
|
+ struct drm_crtc *crtc;
|
|
+ size_t age;
|
|
+ uint32_t *image;
|
|
+};
|
|
+
|
|
+struct vmw_surface {
|
|
+ struct vmw_resource res;
|
|
+ uint32_t flags;
|
|
+ uint32_t format;
|
|
+ uint32_t mip_levels[DRM_VMW_MAX_SURFACE_FACES];
|
|
+ struct drm_vmw_size *sizes;
|
|
+ uint32_t num_sizes;
|
|
+
|
|
+ bool scanout;
|
|
+
|
|
+ /* TODO so far just a extra pointer */
|
|
+ struct vmw_cursor_snooper snooper;
|
|
+};
|
|
+
|
|
+struct vmw_fifo_state {
|
|
+ unsigned long reserved_size;
|
|
+ __le32 *dynamic_buffer;
|
|
+ __le32 *static_buffer;
|
|
+ __le32 *last_buffer;
|
|
+ uint32_t last_data_size;
|
|
+ uint32_t last_buffer_size;
|
|
+ bool last_buffer_add;
|
|
+ unsigned long static_buffer_size;
|
|
+ bool using_bounce_buffer;
|
|
+ uint32_t capabilities;
|
|
+ struct mutex fifo_mutex;
|
|
+ struct rw_semaphore rwsem;
|
|
+};
|
|
+
|
|
+struct vmw_relocation {
|
|
+ SVGAGuestPtr *location;
|
|
+ uint32_t index;
|
|
+};
|
|
+
|
|
+struct vmw_sw_context{
|
|
+ struct ida bo_list;
|
|
+ uint32_t last_cid;
|
|
+ bool cid_valid;
|
|
+ uint32_t last_sid;
|
|
+ uint32_t sid_translation;
|
|
+ bool sid_valid;
|
|
+ struct ttm_object_file *tfile;
|
|
+ struct list_head validate_nodes;
|
|
+ struct vmw_relocation relocs[VMWGFX_MAX_RELOCATIONS];
|
|
+ uint32_t cur_reloc;
|
|
+ struct ttm_validate_buffer val_bufs[VMWGFX_MAX_GMRS];
|
|
+ uint32_t cur_val_buf;
|
|
+};
|
|
+
|
|
+struct vmw_legacy_display;
|
|
+struct vmw_overlay;
|
|
+
|
|
+struct vmw_master {
|
|
+ struct ttm_lock lock;
|
|
+};
|
|
+
|
|
+struct vmw_private {
|
|
+ struct ttm_bo_device bdev;
|
|
+ struct ttm_bo_global_ref bo_global_ref;
|
|
+ struct ttm_global_reference mem_global_ref;
|
|
+
|
|
+ struct vmw_fifo_state fifo;
|
|
+
|
|
+ struct drm_device *dev;
|
|
+ unsigned long vmw_chipset;
|
|
+ unsigned int io_start;
|
|
+ uint32_t vram_start;
|
|
+ uint32_t vram_size;
|
|
+ uint32_t mmio_start;
|
|
+ uint32_t mmio_size;
|
|
+ uint32_t fb_max_width;
|
|
+ uint32_t fb_max_height;
|
|
+ __le32 __iomem *mmio_virt;
|
|
+ int mmio_mtrr;
|
|
+ uint32_t capabilities;
|
|
+ uint32_t max_gmr_descriptors;
|
|
+ uint32_t max_gmr_ids;
|
|
+ struct mutex hw_mutex;
|
|
+
|
|
+ /*
|
|
+ * VGA registers.
|
|
+ */
|
|
+
|
|
+ uint32_t vga_width;
|
|
+ uint32_t vga_height;
|
|
+ uint32_t vga_depth;
|
|
+ uint32_t vga_bpp;
|
|
+ uint32_t vga_pseudo;
|
|
+ uint32_t vga_red_mask;
|
|
+ uint32_t vga_blue_mask;
|
|
+ uint32_t vga_green_mask;
|
|
+
|
|
+ /*
|
|
+ * Framebuffer info.
|
|
+ */
|
|
+
|
|
+ void *fb_info;
|
|
+ struct vmw_legacy_display *ldu_priv;
|
|
+ struct vmw_overlay *overlay_priv;
|
|
+
|
|
+ /*
|
|
+ * Context and surface management.
|
|
+ */
|
|
+
|
|
+ rwlock_t resource_lock;
|
|
+ struct idr context_idr;
|
|
+ struct idr surface_idr;
|
|
+ struct idr stream_idr;
|
|
+
|
|
+ /*
|
|
+ * Block lastclose from racing with firstopen.
|
|
+ */
|
|
+
|
|
+ struct mutex init_mutex;
|
|
+
|
|
+ /*
|
|
+ * A resource manager for kernel-only surfaces and
|
|
+ * contexts.
|
|
+ */
|
|
+
|
|
+ struct ttm_object_device *tdev;
|
|
+
|
|
+ /*
|
|
+ * Fencing and IRQs.
|
|
+ */
|
|
+
|
|
+ atomic_t fence_seq;
|
|
+ wait_queue_head_t fence_queue;
|
|
+ wait_queue_head_t fifo_queue;
|
|
+ atomic_t fence_queue_waiters;
|
|
+ atomic_t fifo_queue_waiters;
|
|
+ uint32_t last_read_sequence;
|
|
+ spinlock_t irq_lock;
|
|
+
|
|
+ /*
|
|
+ * Device state
|
|
+ */
|
|
+
|
|
+ uint32_t traces_state;
|
|
+ uint32_t enable_state;
|
|
+ uint32_t config_done_state;
|
|
+
|
|
+ /**
|
|
+ * Execbuf
|
|
+ */
|
|
+ /**
|
|
+ * Protected by the cmdbuf mutex.
|
|
+ */
|
|
+
|
|
+ struct vmw_sw_context ctx;
|
|
+ uint32_t val_seq;
|
|
+ struct mutex cmdbuf_mutex;
|
|
+
|
|
+ /**
|
|
+ * GMR management. Protected by the lru spinlock.
|
|
+ */
|
|
+
|
|
+ struct ida gmr_ida;
|
|
+ struct list_head gmr_lru;
|
|
+
|
|
+
|
|
+ /**
|
|
+ * Operating mode.
|
|
+ */
|
|
+
|
|
+ bool stealth;
|
|
+ bool is_opened;
|
|
+
|
|
+ /**
|
|
+ * Master management.
|
|
+ */
|
|
+
|
|
+ struct vmw_master *active_master;
|
|
+ struct vmw_master fbdev_master;
|
|
+ struct notifier_block pm_nb;
|
|
+};
|
|
+
|
|
+static inline struct vmw_private *vmw_priv(struct drm_device *dev)
|
|
+{
|
|
+ return (struct vmw_private *)dev->dev_private;
|
|
+}
|
|
+
|
|
+static inline struct vmw_fpriv *vmw_fpriv(struct drm_file *file_priv)
|
|
+{
|
|
+ return (struct vmw_fpriv *)file_priv->driver_priv;
|
|
+}
|
|
+
|
|
+static inline struct vmw_master *vmw_master(struct drm_master *master)
|
|
+{
|
|
+ return (struct vmw_master *) master->driver_priv;
|
|
+}
|
|
+
|
|
+static inline void vmw_write(struct vmw_private *dev_priv,
|
|
+ unsigned int offset, uint32_t value)
|
|
+{
|
|
+ outl(offset, dev_priv->io_start + VMWGFX_INDEX_PORT);
|
|
+ outl(value, dev_priv->io_start + VMWGFX_VALUE_PORT);
|
|
+}
|
|
+
|
|
+static inline uint32_t vmw_read(struct vmw_private *dev_priv,
|
|
+ unsigned int offset)
|
|
+{
|
|
+ uint32_t val;
|
|
+
|
|
+ outl(offset, dev_priv->io_start + VMWGFX_INDEX_PORT);
|
|
+ val = inl(dev_priv->io_start + VMWGFX_VALUE_PORT);
|
|
+ return val;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * GMR utilities - vmwgfx_gmr.c
|
|
+ */
|
|
+
|
|
+extern int vmw_gmr_bind(struct vmw_private *dev_priv,
|
|
+ struct ttm_buffer_object *bo);
|
|
+extern void vmw_gmr_unbind(struct vmw_private *dev_priv, int gmr_id);
|
|
+
|
|
+/**
|
|
+ * Resource utilities - vmwgfx_resource.c
|
|
+ */
|
|
+
|
|
+extern struct vmw_resource *vmw_context_alloc(struct vmw_private *dev_priv);
|
|
+extern void vmw_resource_unreference(struct vmw_resource **p_res);
|
|
+extern struct vmw_resource *vmw_resource_reference(struct vmw_resource *res);
|
|
+extern int vmw_context_destroy_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv);
|
|
+extern int vmw_context_define_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv);
|
|
+extern int vmw_context_check(struct vmw_private *dev_priv,
|
|
+ struct ttm_object_file *tfile,
|
|
+ int id);
|
|
+extern void vmw_surface_res_free(struct vmw_resource *res);
|
|
+extern int vmw_surface_init(struct vmw_private *dev_priv,
|
|
+ struct vmw_surface *srf,
|
|
+ void (*res_free) (struct vmw_resource *res));
|
|
+extern int vmw_user_surface_lookup_handle(struct vmw_private *dev_priv,
|
|
+ struct ttm_object_file *tfile,
|
|
+ uint32_t handle,
|
|
+ struct vmw_surface **out);
|
|
+extern int vmw_surface_destroy_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv);
|
|
+extern int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv);
|
|
+extern int vmw_surface_reference_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv);
|
|
+extern int vmw_surface_check(struct vmw_private *dev_priv,
|
|
+ struct ttm_object_file *tfile,
|
|
+ uint32_t handle, int *id);
|
|
+extern void vmw_dmabuf_bo_free(struct ttm_buffer_object *bo);
|
|
+extern int vmw_dmabuf_init(struct vmw_private *dev_priv,
|
|
+ struct vmw_dma_buffer *vmw_bo,
|
|
+ size_t size, struct ttm_placement *placement,
|
|
+ bool interuptable,
|
|
+ void (*bo_free) (struct ttm_buffer_object *bo));
|
|
+extern int vmw_dmabuf_alloc_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv);
|
|
+extern int vmw_dmabuf_unref_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv);
|
|
+extern uint32_t vmw_dmabuf_validate_node(struct ttm_buffer_object *bo,
|
|
+ uint32_t cur_validate_node);
|
|
+extern void vmw_dmabuf_validate_clear(struct ttm_buffer_object *bo);
|
|
+extern int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile,
|
|
+ uint32_t id, struct vmw_dma_buffer **out);
|
|
+extern uint32_t vmw_dmabuf_gmr(struct ttm_buffer_object *bo);
|
|
+extern void vmw_dmabuf_set_gmr(struct ttm_buffer_object *bo, uint32_t id);
|
|
+extern int vmw_gmr_id_alloc(struct vmw_private *dev_priv, uint32_t *p_id);
|
|
+extern int vmw_dmabuf_to_start_of_vram(struct vmw_private *vmw_priv,
|
|
+ struct vmw_dma_buffer *bo);
|
|
+extern int vmw_dmabuf_from_vram(struct vmw_private *vmw_priv,
|
|
+ struct vmw_dma_buffer *bo);
|
|
+extern void vmw_dmabuf_gmr_unbind(struct ttm_buffer_object *bo);
|
|
+extern int vmw_stream_claim_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv);
|
|
+extern int vmw_stream_unref_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv);
|
|
+extern int vmw_user_stream_lookup(struct vmw_private *dev_priv,
|
|
+ struct ttm_object_file *tfile,
|
|
+ uint32_t *inout_id,
|
|
+ struct vmw_resource **out);
|
|
+
|
|
+
|
|
+/**
|
|
+ * Misc Ioctl functionality - vmwgfx_ioctl.c
|
|
+ */
|
|
+
|
|
+extern int vmw_getparam_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv);
|
|
+extern int vmw_fifo_debug_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv);
|
|
+
|
|
+/**
|
|
+ * Fifo utilities - vmwgfx_fifo.c
|
|
+ */
|
|
+
|
|
+extern int vmw_fifo_init(struct vmw_private *dev_priv,
|
|
+ struct vmw_fifo_state *fifo);
|
|
+extern void vmw_fifo_release(struct vmw_private *dev_priv,
|
|
+ struct vmw_fifo_state *fifo);
|
|
+extern void *vmw_fifo_reserve(struct vmw_private *dev_priv, uint32_t bytes);
|
|
+extern void vmw_fifo_commit(struct vmw_private *dev_priv, uint32_t bytes);
|
|
+extern int vmw_fifo_send_fence(struct vmw_private *dev_priv,
|
|
+ uint32_t *sequence);
|
|
+extern void vmw_fifo_ping_host(struct vmw_private *dev_priv, uint32_t reason);
|
|
+extern int vmw_fifo_mmap(struct file *filp, struct vm_area_struct *vma);
|
|
+extern bool vmw_fifo_have_3d(struct vmw_private *dev_priv);
|
|
+
|
|
+/**
|
|
+ * TTM glue - vmwgfx_ttm_glue.c
|
|
+ */
|
|
+
|
|
+extern int vmw_ttm_global_init(struct vmw_private *dev_priv);
|
|
+extern void vmw_ttm_global_release(struct vmw_private *dev_priv);
|
|
+extern int vmw_mmap(struct file *filp, struct vm_area_struct *vma);
|
|
+
|
|
+/**
|
|
+ * TTM buffer object driver - vmwgfx_buffer.c
|
|
+ */
|
|
+
|
|
+extern struct ttm_placement vmw_vram_placement;
|
|
+extern struct ttm_placement vmw_vram_ne_placement;
|
|
+extern struct ttm_placement vmw_vram_sys_placement;
|
|
+extern struct ttm_placement vmw_sys_placement;
|
|
+extern struct ttm_bo_driver vmw_bo_driver;
|
|
+extern int vmw_dma_quiescent(struct drm_device *dev);
|
|
+
|
|
+/**
|
|
+ * Command submission - vmwgfx_execbuf.c
|
|
+ */
|
|
+
|
|
+extern int vmw_execbuf_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv);
|
|
+
|
|
+/**
|
|
+ * IRQs and wating - vmwgfx_irq.c
|
|
+ */
|
|
+
|
|
+extern irqreturn_t vmw_irq_handler(DRM_IRQ_ARGS);
|
|
+extern int vmw_wait_fence(struct vmw_private *dev_priv, bool lazy,
|
|
+ uint32_t sequence, bool interruptible,
|
|
+ unsigned long timeout);
|
|
+extern void vmw_irq_preinstall(struct drm_device *dev);
|
|
+extern int vmw_irq_postinstall(struct drm_device *dev);
|
|
+extern void vmw_irq_uninstall(struct drm_device *dev);
|
|
+extern bool vmw_fence_signaled(struct vmw_private *dev_priv,
|
|
+ uint32_t sequence);
|
|
+extern int vmw_fence_wait_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv);
|
|
+extern int vmw_fallback_wait(struct vmw_private *dev_priv,
|
|
+ bool lazy,
|
|
+ bool fifo_idle,
|
|
+ uint32_t sequence,
|
|
+ bool interruptible,
|
|
+ unsigned long timeout);
|
|
+
|
|
+/**
|
|
+ * Kernel framebuffer - vmwgfx_fb.c
|
|
+ */
|
|
+
|
|
+int vmw_fb_init(struct vmw_private *vmw_priv);
|
|
+int vmw_fb_close(struct vmw_private *dev_priv);
|
|
+int vmw_fb_off(struct vmw_private *vmw_priv);
|
|
+int vmw_fb_on(struct vmw_private *vmw_priv);
|
|
+
|
|
+/**
|
|
+ * Kernel modesetting - vmwgfx_kms.c
|
|
+ */
|
|
+
|
|
+int vmw_kms_init(struct vmw_private *dev_priv);
|
|
+int vmw_kms_close(struct vmw_private *dev_priv);
|
|
+int vmw_kms_save_vga(struct vmw_private *vmw_priv);
|
|
+int vmw_kms_restore_vga(struct vmw_private *vmw_priv);
|
|
+int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv);
|
|
+void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv);
|
|
+void vmw_kms_cursor_snoop(struct vmw_surface *srf,
|
|
+ struct ttm_object_file *tfile,
|
|
+ struct ttm_buffer_object *bo,
|
|
+ SVGA3dCmdHeader *header);
|
|
+
|
|
+/**
|
|
+ * Overlay control - vmwgfx_overlay.c
|
|
+ */
|
|
+
|
|
+int vmw_overlay_init(struct vmw_private *dev_priv);
|
|
+int vmw_overlay_close(struct vmw_private *dev_priv);
|
|
+int vmw_overlay_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv);
|
|
+int vmw_overlay_stop_all(struct vmw_private *dev_priv);
|
|
+int vmw_overlay_resume_all(struct vmw_private *dev_priv);
|
|
+int vmw_overlay_pause_all(struct vmw_private *dev_priv);
|
|
+int vmw_overlay_claim(struct vmw_private *dev_priv, uint32_t *out);
|
|
+int vmw_overlay_unref(struct vmw_private *dev_priv, uint32_t stream_id);
|
|
+int vmw_overlay_num_overlays(struct vmw_private *dev_priv);
|
|
+int vmw_overlay_num_free_overlays(struct vmw_private *dev_priv);
|
|
+
|
|
+/**
|
|
+ * Inline helper functions
|
|
+ */
|
|
+
|
|
+static inline void vmw_surface_unreference(struct vmw_surface **srf)
|
|
+{
|
|
+ struct vmw_surface *tmp_srf = *srf;
|
|
+ struct vmw_resource *res = &tmp_srf->res;
|
|
+ *srf = NULL;
|
|
+
|
|
+ vmw_resource_unreference(&res);
|
|
+}
|
|
+
|
|
+static inline struct vmw_surface *vmw_surface_reference(struct vmw_surface *srf)
|
|
+{
|
|
+ (void) vmw_resource_reference(&srf->res);
|
|
+ return srf;
|
|
+}
|
|
+
|
|
+static inline void vmw_dmabuf_unreference(struct vmw_dma_buffer **buf)
|
|
+{
|
|
+ struct vmw_dma_buffer *tmp_buf = *buf;
|
|
+ struct ttm_buffer_object *bo = &tmp_buf->base;
|
|
+ *buf = NULL;
|
|
+
|
|
+ ttm_bo_unref(&bo);
|
|
+}
|
|
+
|
|
+static inline struct vmw_dma_buffer *vmw_dmabuf_reference(struct vmw_dma_buffer *buf)
|
|
+{
|
|
+ if (ttm_bo_reference(&buf->base))
|
|
+ return buf;
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+#endif
|
|
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
|
|
new file mode 100644
|
|
index 0000000..d69caf9
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
|
|
@@ -0,0 +1,640 @@
|
|
+/**************************************************************************
|
|
+ *
|
|
+ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sub license, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial portions
|
|
+ * of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ **************************************************************************/
|
|
+
|
|
+#include "vmwgfx_drv.h"
|
|
+#include "vmwgfx_reg.h"
|
|
+#include "ttm/ttm_bo_api.h"
|
|
+#include "ttm/ttm_placement.h"
|
|
+
|
|
+static int vmw_cmd_invalid(struct vmw_private *dev_priv,
|
|
+ struct vmw_sw_context *sw_context,
|
|
+ SVGA3dCmdHeader *header)
|
|
+{
|
|
+ return capable(CAP_SYS_ADMIN) ? : -EINVAL;
|
|
+}
|
|
+
|
|
+static int vmw_cmd_ok(struct vmw_private *dev_priv,
|
|
+ struct vmw_sw_context *sw_context,
|
|
+ SVGA3dCmdHeader *header)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vmw_cmd_cid_check(struct vmw_private *dev_priv,
|
|
+ struct vmw_sw_context *sw_context,
|
|
+ SVGA3dCmdHeader *header)
|
|
+{
|
|
+ struct vmw_cid_cmd {
|
|
+ SVGA3dCmdHeader header;
|
|
+ __le32 cid;
|
|
+ } *cmd;
|
|
+ int ret;
|
|
+
|
|
+ cmd = container_of(header, struct vmw_cid_cmd, header);
|
|
+ if (likely(sw_context->cid_valid && cmd->cid == sw_context->last_cid))
|
|
+ return 0;
|
|
+
|
|
+ ret = vmw_context_check(dev_priv, sw_context->tfile, cmd->cid);
|
|
+ if (unlikely(ret != 0)) {
|
|
+ DRM_ERROR("Could not find or use context %u\n",
|
|
+ (unsigned) cmd->cid);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ sw_context->last_cid = cmd->cid;
|
|
+ sw_context->cid_valid = true;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vmw_cmd_sid_check(struct vmw_private *dev_priv,
|
|
+ struct vmw_sw_context *sw_context,
|
|
+ uint32_t *sid)
|
|
+{
|
|
+ if (*sid == SVGA3D_INVALID_ID)
|
|
+ return 0;
|
|
+
|
|
+ if (unlikely((!sw_context->sid_valid ||
|
|
+ *sid != sw_context->last_sid))) {
|
|
+ int real_id;
|
|
+ int ret = vmw_surface_check(dev_priv, sw_context->tfile,
|
|
+ *sid, &real_id);
|
|
+
|
|
+ if (unlikely(ret != 0)) {
|
|
+ DRM_ERROR("Could ot find or use surface 0x%08x "
|
|
+ "address 0x%08lx\n",
|
|
+ (unsigned int) *sid,
|
|
+ (unsigned long) sid);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ sw_context->last_sid = *sid;
|
|
+ sw_context->sid_valid = true;
|
|
+ *sid = real_id;
|
|
+ sw_context->sid_translation = real_id;
|
|
+ } else
|
|
+ *sid = sw_context->sid_translation;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static int vmw_cmd_set_render_target_check(struct vmw_private *dev_priv,
|
|
+ struct vmw_sw_context *sw_context,
|
|
+ SVGA3dCmdHeader *header)
|
|
+{
|
|
+ struct vmw_sid_cmd {
|
|
+ SVGA3dCmdHeader header;
|
|
+ SVGA3dCmdSetRenderTarget body;
|
|
+ } *cmd;
|
|
+ int ret;
|
|
+
|
|
+ ret = vmw_cmd_cid_check(dev_priv, sw_context, header);
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+
|
|
+ cmd = container_of(header, struct vmw_sid_cmd, header);
|
|
+ ret = vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.target.sid);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int vmw_cmd_surface_copy_check(struct vmw_private *dev_priv,
|
|
+ struct vmw_sw_context *sw_context,
|
|
+ SVGA3dCmdHeader *header)
|
|
+{
|
|
+ struct vmw_sid_cmd {
|
|
+ SVGA3dCmdHeader header;
|
|
+ SVGA3dCmdSurfaceCopy body;
|
|
+ } *cmd;
|
|
+ int ret;
|
|
+
|
|
+ cmd = container_of(header, struct vmw_sid_cmd, header);
|
|
+ ret = vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.src.sid);
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+ return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.dest.sid);
|
|
+}
|
|
+
|
|
+static int vmw_cmd_stretch_blt_check(struct vmw_private *dev_priv,
|
|
+ struct vmw_sw_context *sw_context,
|
|
+ SVGA3dCmdHeader *header)
|
|
+{
|
|
+ struct vmw_sid_cmd {
|
|
+ SVGA3dCmdHeader header;
|
|
+ SVGA3dCmdSurfaceStretchBlt body;
|
|
+ } *cmd;
|
|
+ int ret;
|
|
+
|
|
+ cmd = container_of(header, struct vmw_sid_cmd, header);
|
|
+ ret = vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.src.sid);
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+ return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.dest.sid);
|
|
+}
|
|
+
|
|
+static int vmw_cmd_blt_surf_screen_check(struct vmw_private *dev_priv,
|
|
+ struct vmw_sw_context *sw_context,
|
|
+ SVGA3dCmdHeader *header)
|
|
+{
|
|
+ struct vmw_sid_cmd {
|
|
+ SVGA3dCmdHeader header;
|
|
+ SVGA3dCmdBlitSurfaceToScreen body;
|
|
+ } *cmd;
|
|
+
|
|
+ cmd = container_of(header, struct vmw_sid_cmd, header);
|
|
+ return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.srcImage.sid);
|
|
+}
|
|
+
|
|
+static int vmw_cmd_present_check(struct vmw_private *dev_priv,
|
|
+ struct vmw_sw_context *sw_context,
|
|
+ SVGA3dCmdHeader *header)
|
|
+{
|
|
+ struct vmw_sid_cmd {
|
|
+ SVGA3dCmdHeader header;
|
|
+ SVGA3dCmdPresent body;
|
|
+ } *cmd;
|
|
+
|
|
+ cmd = container_of(header, struct vmw_sid_cmd, header);
|
|
+ return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.sid);
|
|
+}
|
|
+
|
|
+static int vmw_cmd_dma(struct vmw_private *dev_priv,
|
|
+ struct vmw_sw_context *sw_context,
|
|
+ SVGA3dCmdHeader *header)
|
|
+{
|
|
+ uint32_t handle;
|
|
+ struct vmw_dma_buffer *vmw_bo = NULL;
|
|
+ struct ttm_buffer_object *bo;
|
|
+ struct vmw_surface *srf = NULL;
|
|
+ struct vmw_dma_cmd {
|
|
+ SVGA3dCmdHeader header;
|
|
+ SVGA3dCmdSurfaceDMA dma;
|
|
+ } *cmd;
|
|
+ struct vmw_relocation *reloc;
|
|
+ int ret;
|
|
+ uint32_t cur_validate_node;
|
|
+ struct ttm_validate_buffer *val_buf;
|
|
+
|
|
+ cmd = container_of(header, struct vmw_dma_cmd, header);
|
|
+ handle = cmd->dma.guest.ptr.gmrId;
|
|
+ ret = vmw_user_dmabuf_lookup(sw_context->tfile, handle, &vmw_bo);
|
|
+ if (unlikely(ret != 0)) {
|
|
+ DRM_ERROR("Could not find or use GMR region.\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ bo = &vmw_bo->base;
|
|
+
|
|
+ if (unlikely(sw_context->cur_reloc >= VMWGFX_MAX_RELOCATIONS)) {
|
|
+ DRM_ERROR("Max number of DMA commands per submission"
|
|
+ " exceeded\n");
|
|
+ ret = -EINVAL;
|
|
+ goto out_no_reloc;
|
|
+ }
|
|
+
|
|
+ reloc = &sw_context->relocs[sw_context->cur_reloc++];
|
|
+ reloc->location = &cmd->dma.guest.ptr;
|
|
+
|
|
+ cur_validate_node = vmw_dmabuf_validate_node(bo, sw_context->cur_val_buf);
|
|
+ if (unlikely(cur_validate_node >= VMWGFX_MAX_GMRS)) {
|
|
+ DRM_ERROR("Max number of DMA buffers per submission"
|
|
+ " exceeded.\n");
|
|
+ ret = -EINVAL;
|
|
+ goto out_no_reloc;
|
|
+ }
|
|
+
|
|
+ reloc->index = cur_validate_node;
|
|
+ if (unlikely(cur_validate_node == sw_context->cur_val_buf)) {
|
|
+ val_buf = &sw_context->val_bufs[cur_validate_node];
|
|
+ val_buf->bo = ttm_bo_reference(bo);
|
|
+ val_buf->new_sync_obj_arg = (void *) dev_priv;
|
|
+ list_add_tail(&val_buf->head, &sw_context->validate_nodes);
|
|
+ ++sw_context->cur_val_buf;
|
|
+ }
|
|
+
|
|
+ ret = vmw_user_surface_lookup_handle(dev_priv, sw_context->tfile,
|
|
+ cmd->dma.host.sid, &srf);
|
|
+ if (ret) {
|
|
+ DRM_ERROR("could not find surface\n");
|
|
+ goto out_no_reloc;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Patch command stream with device SID.
|
|
+ */
|
|
+
|
|
+ cmd->dma.host.sid = srf->res.id;
|
|
+ vmw_kms_cursor_snoop(srf, sw_context->tfile, bo, header);
|
|
+ /**
|
|
+ * FIXME: May deadlock here when called from the
|
|
+ * command parsing code.
|
|
+ */
|
|
+ vmw_surface_unreference(&srf);
|
|
+
|
|
+out_no_reloc:
|
|
+ vmw_dmabuf_unreference(&vmw_bo);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int vmw_cmd_draw(struct vmw_private *dev_priv,
|
|
+ struct vmw_sw_context *sw_context,
|
|
+ SVGA3dCmdHeader *header)
|
|
+{
|
|
+ struct vmw_draw_cmd {
|
|
+ SVGA3dCmdHeader header;
|
|
+ SVGA3dCmdDrawPrimitives body;
|
|
+ } *cmd;
|
|
+ SVGA3dVertexDecl *decl = (SVGA3dVertexDecl *)(
|
|
+ (unsigned long)header + sizeof(*cmd));
|
|
+ SVGA3dPrimitiveRange *range;
|
|
+ uint32_t i;
|
|
+ uint32_t maxnum;
|
|
+ int ret;
|
|
+
|
|
+ ret = vmw_cmd_cid_check(dev_priv, sw_context, header);
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+
|
|
+ cmd = container_of(header, struct vmw_draw_cmd, header);
|
|
+ maxnum = (header->size - sizeof(cmd->body)) / sizeof(*decl);
|
|
+
|
|
+ if (unlikely(cmd->body.numVertexDecls > maxnum)) {
|
|
+ DRM_ERROR("Illegal number of vertex declarations.\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < cmd->body.numVertexDecls; ++i, ++decl) {
|
|
+ ret = vmw_cmd_sid_check(dev_priv, sw_context,
|
|
+ &decl->array.surfaceId);
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ maxnum = (header->size - sizeof(cmd->body) -
|
|
+ cmd->body.numVertexDecls * sizeof(*decl)) / sizeof(*range);
|
|
+ if (unlikely(cmd->body.numRanges > maxnum)) {
|
|
+ DRM_ERROR("Illegal number of index ranges.\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ range = (SVGA3dPrimitiveRange *) decl;
|
|
+ for (i = 0; i < cmd->body.numRanges; ++i, ++range) {
|
|
+ ret = vmw_cmd_sid_check(dev_priv, sw_context,
|
|
+ &range->indexArray.surfaceId);
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static int vmw_cmd_tex_state(struct vmw_private *dev_priv,
|
|
+ struct vmw_sw_context *sw_context,
|
|
+ SVGA3dCmdHeader *header)
|
|
+{
|
|
+ struct vmw_tex_state_cmd {
|
|
+ SVGA3dCmdHeader header;
|
|
+ SVGA3dCmdSetTextureState state;
|
|
+ };
|
|
+
|
|
+ SVGA3dTextureState *last_state = (SVGA3dTextureState *)
|
|
+ ((unsigned long) header + header->size + sizeof(header));
|
|
+ SVGA3dTextureState *cur_state = (SVGA3dTextureState *)
|
|
+ ((unsigned long) header + sizeof(struct vmw_tex_state_cmd));
|
|
+ int ret;
|
|
+
|
|
+ ret = vmw_cmd_cid_check(dev_priv, sw_context, header);
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+
|
|
+ for (; cur_state < last_state; ++cur_state) {
|
|
+ if (likely(cur_state->name != SVGA3D_TS_BIND_TEXTURE))
|
|
+ continue;
|
|
+
|
|
+ ret = vmw_cmd_sid_check(dev_priv, sw_context,
|
|
+ &cur_state->value);
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+typedef int (*vmw_cmd_func) (struct vmw_private *,
|
|
+ struct vmw_sw_context *,
|
|
+ SVGA3dCmdHeader *);
|
|
+
|
|
+#define VMW_CMD_DEF(cmd, func) \
|
|
+ [cmd - SVGA_3D_CMD_BASE] = func
|
|
+
|
|
+static vmw_cmd_func vmw_cmd_funcs[SVGA_3D_CMD_MAX] = {
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_SURFACE_DEFINE, &vmw_cmd_invalid),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_SURFACE_DESTROY, &vmw_cmd_invalid),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_SURFACE_COPY, &vmw_cmd_surface_copy_check),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_SURFACE_STRETCHBLT, &vmw_cmd_stretch_blt_check),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_SURFACE_DMA, &vmw_cmd_dma),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_CONTEXT_DEFINE, &vmw_cmd_invalid),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_CONTEXT_DESTROY, &vmw_cmd_invalid),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_SETTRANSFORM, &vmw_cmd_cid_check),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_SETZRANGE, &vmw_cmd_cid_check),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_SETRENDERSTATE, &vmw_cmd_cid_check),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_SETRENDERTARGET,
|
|
+ &vmw_cmd_set_render_target_check),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_SETTEXTURESTATE, &vmw_cmd_tex_state),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_SETMATERIAL, &vmw_cmd_cid_check),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_SETLIGHTDATA, &vmw_cmd_cid_check),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_SETLIGHTENABLED, &vmw_cmd_cid_check),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_SETVIEWPORT, &vmw_cmd_cid_check),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_SETCLIPPLANE, &vmw_cmd_cid_check),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_CLEAR, &vmw_cmd_cid_check),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_PRESENT, &vmw_cmd_present_check),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_SHADER_DEFINE, &vmw_cmd_cid_check),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_SHADER_DESTROY, &vmw_cmd_cid_check),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_SET_SHADER, &vmw_cmd_cid_check),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_SET_SHADER_CONST, &vmw_cmd_cid_check),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_DRAW_PRIMITIVES, &vmw_cmd_draw),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_SETSCISSORRECT, &vmw_cmd_cid_check),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_BEGIN_QUERY, &vmw_cmd_cid_check),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_END_QUERY, &vmw_cmd_cid_check),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_WAIT_FOR_QUERY, &vmw_cmd_cid_check),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_PRESENT_READBACK, &vmw_cmd_ok),
|
|
+ VMW_CMD_DEF(SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN,
|
|
+ &vmw_cmd_blt_surf_screen_check)
|
|
+};
|
|
+
|
|
+static int vmw_cmd_check(struct vmw_private *dev_priv,
|
|
+ struct vmw_sw_context *sw_context,
|
|
+ void *buf, uint32_t *size)
|
|
+{
|
|
+ uint32_t cmd_id;
|
|
+ uint32_t size_remaining = *size;
|
|
+ SVGA3dCmdHeader *header = (SVGA3dCmdHeader *) buf;
|
|
+ int ret;
|
|
+
|
|
+ cmd_id = ((uint32_t *)buf)[0];
|
|
+ if (cmd_id == SVGA_CMD_UPDATE) {
|
|
+ *size = 5 << 2;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ cmd_id = le32_to_cpu(header->id);
|
|
+ *size = le32_to_cpu(header->size) + sizeof(SVGA3dCmdHeader);
|
|
+
|
|
+ cmd_id -= SVGA_3D_CMD_BASE;
|
|
+ if (unlikely(*size > size_remaining))
|
|
+ goto out_err;
|
|
+
|
|
+ if (unlikely(cmd_id >= SVGA_3D_CMD_MAX - SVGA_3D_CMD_BASE))
|
|
+ goto out_err;
|
|
+
|
|
+ ret = vmw_cmd_funcs[cmd_id](dev_priv, sw_context, header);
|
|
+ if (unlikely(ret != 0))
|
|
+ goto out_err;
|
|
+
|
|
+ return 0;
|
|
+out_err:
|
|
+ DRM_ERROR("Illegal / Invalid SVGA3D command: %d\n",
|
|
+ cmd_id + SVGA_3D_CMD_BASE);
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static int vmw_cmd_check_all(struct vmw_private *dev_priv,
|
|
+ struct vmw_sw_context *sw_context,
|
|
+ void *buf, uint32_t size)
|
|
+{
|
|
+ int32_t cur_size = size;
|
|
+ int ret;
|
|
+
|
|
+ while (cur_size > 0) {
|
|
+ size = cur_size;
|
|
+ ret = vmw_cmd_check(dev_priv, sw_context, buf, &size);
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+ buf = (void *)((unsigned long) buf + size);
|
|
+ cur_size -= size;
|
|
+ }
|
|
+
|
|
+ if (unlikely(cur_size != 0)) {
|
|
+ DRM_ERROR("Command verifier out of sync.\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void vmw_free_relocations(struct vmw_sw_context *sw_context)
|
|
+{
|
|
+ sw_context->cur_reloc = 0;
|
|
+}
|
|
+
|
|
+static void vmw_apply_relocations(struct vmw_sw_context *sw_context)
|
|
+{
|
|
+ uint32_t i;
|
|
+ struct vmw_relocation *reloc;
|
|
+ struct ttm_validate_buffer *validate;
|
|
+ struct ttm_buffer_object *bo;
|
|
+
|
|
+ for (i = 0; i < sw_context->cur_reloc; ++i) {
|
|
+ reloc = &sw_context->relocs[i];
|
|
+ validate = &sw_context->val_bufs[reloc->index];
|
|
+ bo = validate->bo;
|
|
+ reloc->location->offset += bo->offset;
|
|
+ reloc->location->gmrId = vmw_dmabuf_gmr(bo);
|
|
+ }
|
|
+ vmw_free_relocations(sw_context);
|
|
+}
|
|
+
|
|
+static void vmw_clear_validations(struct vmw_sw_context *sw_context)
|
|
+{
|
|
+ struct ttm_validate_buffer *entry, *next;
|
|
+
|
|
+ list_for_each_entry_safe(entry, next, &sw_context->validate_nodes,
|
|
+ head) {
|
|
+ list_del(&entry->head);
|
|
+ vmw_dmabuf_validate_clear(entry->bo);
|
|
+ ttm_bo_unref(&entry->bo);
|
|
+ sw_context->cur_val_buf--;
|
|
+ }
|
|
+ BUG_ON(sw_context->cur_val_buf != 0);
|
|
+}
|
|
+
|
|
+static int vmw_validate_single_buffer(struct vmw_private *dev_priv,
|
|
+ struct ttm_buffer_object *bo)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ if (vmw_dmabuf_gmr(bo) != SVGA_GMR_NULL)
|
|
+ return 0;
|
|
+
|
|
+ /**
|
|
+ * Put BO in VRAM, only if there is space.
|
|
+ */
|
|
+
|
|
+ ret = ttm_bo_validate(bo, &vmw_vram_sys_placement, true, false);
|
|
+ if (unlikely(ret == -ERESTARTSYS))
|
|
+ return ret;
|
|
+
|
|
+ /**
|
|
+ * Otherwise, set it up as GMR.
|
|
+ */
|
|
+
|
|
+ if (vmw_dmabuf_gmr(bo) != SVGA_GMR_NULL)
|
|
+ return 0;
|
|
+
|
|
+ ret = vmw_gmr_bind(dev_priv, bo);
|
|
+ if (likely(ret == 0 || ret == -ERESTARTSYS))
|
|
+ return ret;
|
|
+
|
|
+ /**
|
|
+ * If that failed, try VRAM again, this time evicting
|
|
+ * previous contents.
|
|
+ */
|
|
+
|
|
+ ret = ttm_bo_validate(bo, &vmw_vram_placement, true, false);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+static int vmw_validate_buffers(struct vmw_private *dev_priv,
|
|
+ struct vmw_sw_context *sw_context)
|
|
+{
|
|
+ struct ttm_validate_buffer *entry;
|
|
+ int ret;
|
|
+
|
|
+ list_for_each_entry(entry, &sw_context->validate_nodes, head) {
|
|
+ ret = vmw_validate_single_buffer(dev_priv, entry->bo);
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int vmw_execbuf_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct vmw_private *dev_priv = vmw_priv(dev);
|
|
+ struct drm_vmw_execbuf_arg *arg = (struct drm_vmw_execbuf_arg *)data;
|
|
+ struct drm_vmw_fence_rep fence_rep;
|
|
+ struct drm_vmw_fence_rep __user *user_fence_rep;
|
|
+ int ret;
|
|
+ void *user_cmd;
|
|
+ void *cmd;
|
|
+ uint32_t sequence;
|
|
+ struct vmw_sw_context *sw_context = &dev_priv->ctx;
|
|
+ struct vmw_master *vmaster = vmw_master(file_priv->master);
|
|
+
|
|
+ ret = ttm_read_lock(&vmaster->lock, true);
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+
|
|
+ ret = mutex_lock_interruptible(&dev_priv->cmdbuf_mutex);
|
|
+ if (unlikely(ret != 0)) {
|
|
+ ret = -ERESTARTSYS;
|
|
+ goto out_no_cmd_mutex;
|
|
+ }
|
|
+
|
|
+ cmd = vmw_fifo_reserve(dev_priv, arg->command_size);
|
|
+ if (unlikely(cmd == NULL)) {
|
|
+ DRM_ERROR("Failed reserving fifo space for commands.\n");
|
|
+ ret = -ENOMEM;
|
|
+ goto out_unlock;
|
|
+ }
|
|
+
|
|
+ user_cmd = (void __user *)(unsigned long)arg->commands;
|
|
+ ret = copy_from_user(cmd, user_cmd, arg->command_size);
|
|
+
|
|
+ if (unlikely(ret != 0)) {
|
|
+ DRM_ERROR("Failed copying commands.\n");
|
|
+ goto out_commit;
|
|
+ }
|
|
+
|
|
+ sw_context->tfile = vmw_fpriv(file_priv)->tfile;
|
|
+ sw_context->cid_valid = false;
|
|
+ sw_context->sid_valid = false;
|
|
+ sw_context->cur_reloc = 0;
|
|
+ sw_context->cur_val_buf = 0;
|
|
+
|
|
+ INIT_LIST_HEAD(&sw_context->validate_nodes);
|
|
+
|
|
+ ret = vmw_cmd_check_all(dev_priv, sw_context, cmd, arg->command_size);
|
|
+ if (unlikely(ret != 0))
|
|
+ goto out_err;
|
|
+ ret = ttm_eu_reserve_buffers(&sw_context->validate_nodes,
|
|
+ dev_priv->val_seq++);
|
|
+ if (unlikely(ret != 0))
|
|
+ goto out_err;
|
|
+
|
|
+ ret = vmw_validate_buffers(dev_priv, sw_context);
|
|
+ if (unlikely(ret != 0))
|
|
+ goto out_err;
|
|
+
|
|
+ vmw_apply_relocations(sw_context);
|
|
+ vmw_fifo_commit(dev_priv, arg->command_size);
|
|
+
|
|
+ ret = vmw_fifo_send_fence(dev_priv, &sequence);
|
|
+
|
|
+ ttm_eu_fence_buffer_objects(&sw_context->validate_nodes,
|
|
+ (void *)(unsigned long) sequence);
|
|
+ vmw_clear_validations(sw_context);
|
|
+ mutex_unlock(&dev_priv->cmdbuf_mutex);
|
|
+
|
|
+ /*
|
|
+ * This error is harmless, because if fence submission fails,
|
|
+ * vmw_fifo_send_fence will sync.
|
|
+ */
|
|
+
|
|
+ if (ret != 0)
|
|
+ DRM_ERROR("Fence submission error. Syncing.\n");
|
|
+
|
|
+ fence_rep.error = ret;
|
|
+ fence_rep.fence_seq = (uint64_t) sequence;
|
|
+
|
|
+ user_fence_rep = (struct drm_vmw_fence_rep __user *)
|
|
+ (unsigned long)arg->fence_rep;
|
|
+
|
|
+ /*
|
|
+ * copy_to_user errors will be detected by user space not
|
|
+ * seeing fence_rep::error filled in.
|
|
+ */
|
|
+
|
|
+ ret = copy_to_user(user_fence_rep, &fence_rep, sizeof(fence_rep));
|
|
+
|
|
+ vmw_kms_cursor_post_execbuf(dev_priv);
|
|
+ ttm_read_unlock(&vmaster->lock);
|
|
+ return 0;
|
|
+out_err:
|
|
+ vmw_free_relocations(sw_context);
|
|
+ ttm_eu_backoff_reservation(&sw_context->validate_nodes);
|
|
+ vmw_clear_validations(sw_context);
|
|
+out_commit:
|
|
+ vmw_fifo_commit(dev_priv, 0);
|
|
+out_unlock:
|
|
+ mutex_unlock(&dev_priv->cmdbuf_mutex);
|
|
+out_no_cmd_mutex:
|
|
+ ttm_read_unlock(&vmaster->lock);
|
|
+ return ret;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
|
|
new file mode 100644
|
|
index 0000000..4f4f643
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
|
|
@@ -0,0 +1,734 @@
|
|
+/**************************************************************************
|
|
+ *
|
|
+ * Copyright © 2007 David Airlie
|
|
+ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sub license, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial portions
|
|
+ * of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ **************************************************************************/
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "vmwgfx_drv.h"
|
|
+
|
|
+#include "ttm/ttm_placement.h"
|
|
+
|
|
+#define VMW_DIRTY_DELAY (HZ / 30)
|
|
+
|
|
+struct vmw_fb_par {
|
|
+ struct vmw_private *vmw_priv;
|
|
+
|
|
+ void *vmalloc;
|
|
+
|
|
+ struct vmw_dma_buffer *vmw_bo;
|
|
+ struct ttm_bo_kmap_obj map;
|
|
+
|
|
+ u32 pseudo_palette[17];
|
|
+
|
|
+ unsigned depth;
|
|
+ unsigned bpp;
|
|
+
|
|
+ unsigned max_width;
|
|
+ unsigned max_height;
|
|
+
|
|
+ void *bo_ptr;
|
|
+ unsigned bo_size;
|
|
+ bool bo_iowrite;
|
|
+
|
|
+ struct {
|
|
+ spinlock_t lock;
|
|
+ bool active;
|
|
+ unsigned x1;
|
|
+ unsigned y1;
|
|
+ unsigned x2;
|
|
+ unsigned y2;
|
|
+ } dirty;
|
|
+};
|
|
+
|
|
+static int vmw_fb_setcolreg(unsigned regno, unsigned red, unsigned green,
|
|
+ unsigned blue, unsigned transp,
|
|
+ struct fb_info *info)
|
|
+{
|
|
+ struct vmw_fb_par *par = info->par;
|
|
+ u32 *pal = par->pseudo_palette;
|
|
+
|
|
+ if (regno > 15) {
|
|
+ DRM_ERROR("Bad regno %u.\n", regno);
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ switch (par->depth) {
|
|
+ case 24:
|
|
+ case 32:
|
|
+ pal[regno] = ((red & 0xff00) << 8) |
|
|
+ (green & 0xff00) |
|
|
+ ((blue & 0xff00) >> 8);
|
|
+ break;
|
|
+ default:
|
|
+ DRM_ERROR("Bad depth %u, bpp %u.\n", par->depth, par->bpp);
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vmw_fb_check_var(struct fb_var_screeninfo *var,
|
|
+ struct fb_info *info)
|
|
+{
|
|
+ int depth = var->bits_per_pixel;
|
|
+ struct vmw_fb_par *par = info->par;
|
|
+ struct vmw_private *vmw_priv = par->vmw_priv;
|
|
+
|
|
+ switch (var->bits_per_pixel) {
|
|
+ case 32:
|
|
+ depth = (var->transp.length > 0) ? 32 : 24;
|
|
+ break;
|
|
+ default:
|
|
+ DRM_ERROR("Bad bpp %u.\n", var->bits_per_pixel);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ switch (depth) {
|
|
+ case 24:
|
|
+ var->red.offset = 16;
|
|
+ var->green.offset = 8;
|
|
+ var->blue.offset = 0;
|
|
+ var->red.length = 8;
|
|
+ var->green.length = 8;
|
|
+ var->blue.length = 8;
|
|
+ var->transp.length = 0;
|
|
+ var->transp.offset = 0;
|
|
+ break;
|
|
+ case 32:
|
|
+ var->red.offset = 16;
|
|
+ var->green.offset = 8;
|
|
+ var->blue.offset = 0;
|
|
+ var->red.length = 8;
|
|
+ var->green.length = 8;
|
|
+ var->blue.length = 8;
|
|
+ var->transp.length = 8;
|
|
+ var->transp.offset = 24;
|
|
+ break;
|
|
+ default:
|
|
+ DRM_ERROR("Bad depth %u.\n", depth);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* without multimon its hard to resize */
|
|
+ if (!(vmw_priv->capabilities & SVGA_CAP_MULTIMON) &&
|
|
+ (var->xres != par->max_width ||
|
|
+ var->yres != par->max_height)) {
|
|
+ DRM_ERROR("Tried to resize, but we don't have multimon\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (var->xres > par->max_width ||
|
|
+ var->yres > par->max_height) {
|
|
+ DRM_ERROR("Requested geom can not fit in framebuffer\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vmw_fb_set_par(struct fb_info *info)
|
|
+{
|
|
+ struct vmw_fb_par *par = info->par;
|
|
+ struct vmw_private *vmw_priv = par->vmw_priv;
|
|
+
|
|
+ if (vmw_priv->capabilities & SVGA_CAP_MULTIMON) {
|
|
+ vmw_write(vmw_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 1);
|
|
+ vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, 0);
|
|
+ vmw_write(vmw_priv, SVGA_REG_DISPLAY_IS_PRIMARY, true);
|
|
+ vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_X, 0);
|
|
+ vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_Y, 0);
|
|
+ vmw_write(vmw_priv, SVGA_REG_DISPLAY_WIDTH, 0);
|
|
+ vmw_write(vmw_priv, SVGA_REG_DISPLAY_HEIGHT, 0);
|
|
+ vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID);
|
|
+
|
|
+ vmw_write(vmw_priv, SVGA_REG_ENABLE, 1);
|
|
+ vmw_write(vmw_priv, SVGA_REG_WIDTH, par->max_width);
|
|
+ vmw_write(vmw_priv, SVGA_REG_HEIGHT, par->max_height);
|
|
+ vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, par->bpp);
|
|
+ vmw_write(vmw_priv, SVGA_REG_DEPTH, par->depth);
|
|
+ vmw_write(vmw_priv, SVGA_REG_RED_MASK, 0x00ff0000);
|
|
+ vmw_write(vmw_priv, SVGA_REG_GREEN_MASK, 0x0000ff00);
|
|
+ vmw_write(vmw_priv, SVGA_REG_BLUE_MASK, 0x000000ff);
|
|
+
|
|
+ /* TODO check if pitch and offset changes */
|
|
+
|
|
+ vmw_write(vmw_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 1);
|
|
+ vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, 0);
|
|
+ vmw_write(vmw_priv, SVGA_REG_DISPLAY_IS_PRIMARY, true);
|
|
+ vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_X, info->var.xoffset);
|
|
+ vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_Y, info->var.yoffset);
|
|
+ vmw_write(vmw_priv, SVGA_REG_DISPLAY_WIDTH, info->var.xres);
|
|
+ vmw_write(vmw_priv, SVGA_REG_DISPLAY_HEIGHT, info->var.yres);
|
|
+ vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID);
|
|
+ } else {
|
|
+ vmw_write(vmw_priv, SVGA_REG_WIDTH, info->var.xres);
|
|
+ vmw_write(vmw_priv, SVGA_REG_HEIGHT, info->var.yres);
|
|
+
|
|
+ /* TODO check if pitch and offset changes */
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vmw_fb_pan_display(struct fb_var_screeninfo *var,
|
|
+ struct fb_info *info)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vmw_fb_blank(int blank, struct fb_info *info)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Dirty code
|
|
+ */
|
|
+
|
|
+static void vmw_fb_dirty_flush(struct vmw_fb_par *par)
|
|
+{
|
|
+ struct vmw_private *vmw_priv = par->vmw_priv;
|
|
+ struct fb_info *info = vmw_priv->fb_info;
|
|
+ int stride = (info->fix.line_length / 4);
|
|
+ int *src = (int *)info->screen_base;
|
|
+ __le32 __iomem *vram_mem = par->bo_ptr;
|
|
+ unsigned long flags;
|
|
+ unsigned x, y, w, h;
|
|
+ int i, k;
|
|
+ struct {
|
|
+ uint32_t header;
|
|
+ SVGAFifoCmdUpdate body;
|
|
+ } *cmd;
|
|
+
|
|
+ spin_lock_irqsave(&par->dirty.lock, flags);
|
|
+ if (!par->dirty.active) {
|
|
+ spin_unlock_irqrestore(&par->dirty.lock, flags);
|
|
+ return;
|
|
+ }
|
|
+ x = par->dirty.x1;
|
|
+ y = par->dirty.y1;
|
|
+ w = min(par->dirty.x2, info->var.xres) - x;
|
|
+ h = min(par->dirty.y2, info->var.yres) - y;
|
|
+ par->dirty.x1 = par->dirty.x2 = 0;
|
|
+ par->dirty.y1 = par->dirty.y2 = 0;
|
|
+ spin_unlock_irqrestore(&par->dirty.lock, flags);
|
|
+
|
|
+ for (i = y * stride; i < info->fix.smem_len / 4; i += stride) {
|
|
+ for (k = i+x; k < i+x+w && k < info->fix.smem_len / 4; k++)
|
|
+ iowrite32(src[k], vram_mem + k);
|
|
+ }
|
|
+
|
|
+#if 0
|
|
+ DRM_INFO("%s, (%u, %u) (%ux%u)\n", __func__, x, y, w, h);
|
|
+#endif
|
|
+
|
|
+ cmd = vmw_fifo_reserve(vmw_priv, sizeof(*cmd));
|
|
+ if (unlikely(cmd == NULL)) {
|
|
+ DRM_ERROR("Fifo reserve failed.\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ cmd->header = cpu_to_le32(SVGA_CMD_UPDATE);
|
|
+ cmd->body.x = cpu_to_le32(x);
|
|
+ cmd->body.y = cpu_to_le32(y);
|
|
+ cmd->body.width = cpu_to_le32(w);
|
|
+ cmd->body.height = cpu_to_le32(h);
|
|
+ vmw_fifo_commit(vmw_priv, sizeof(*cmd));
|
|
+}
|
|
+
|
|
+static void vmw_fb_dirty_mark(struct vmw_fb_par *par,
|
|
+ unsigned x1, unsigned y1,
|
|
+ unsigned width, unsigned height)
|
|
+{
|
|
+ struct fb_info *info = par->vmw_priv->fb_info;
|
|
+ unsigned long flags;
|
|
+ unsigned x2 = x1 + width;
|
|
+ unsigned y2 = y1 + height;
|
|
+
|
|
+ spin_lock_irqsave(&par->dirty.lock, flags);
|
|
+ if (par->dirty.x1 == par->dirty.x2) {
|
|
+ par->dirty.x1 = x1;
|
|
+ par->dirty.y1 = y1;
|
|
+ par->dirty.x2 = x2;
|
|
+ par->dirty.y2 = y2;
|
|
+ /* if we are active start the dirty work
|
|
+ * we share the work with the defio system */
|
|
+ if (par->dirty.active)
|
|
+ schedule_delayed_work(&info->deferred_work, VMW_DIRTY_DELAY);
|
|
+ } else {
|
|
+ if (x1 < par->dirty.x1)
|
|
+ par->dirty.x1 = x1;
|
|
+ if (y1 < par->dirty.y1)
|
|
+ par->dirty.y1 = y1;
|
|
+ if (x2 > par->dirty.x2)
|
|
+ par->dirty.x2 = x2;
|
|
+ if (y2 > par->dirty.y2)
|
|
+ par->dirty.y2 = y2;
|
|
+ }
|
|
+ spin_unlock_irqrestore(&par->dirty.lock, flags);
|
|
+}
|
|
+
|
|
+static void vmw_deferred_io(struct fb_info *info,
|
|
+ struct list_head *pagelist)
|
|
+{
|
|
+ struct vmw_fb_par *par = info->par;
|
|
+ unsigned long start, end, min, max;
|
|
+ unsigned long flags;
|
|
+ struct page *page;
|
|
+ int y1, y2;
|
|
+
|
|
+ min = ULONG_MAX;
|
|
+ max = 0;
|
|
+ list_for_each_entry(page, pagelist, lru) {
|
|
+ start = page->index << PAGE_SHIFT;
|
|
+ end = start + PAGE_SIZE - 1;
|
|
+ min = min(min, start);
|
|
+ max = max(max, end);
|
|
+ }
|
|
+
|
|
+ if (min < max) {
|
|
+ y1 = min / info->fix.line_length;
|
|
+ y2 = (max / info->fix.line_length) + 1;
|
|
+
|
|
+ spin_lock_irqsave(&par->dirty.lock, flags);
|
|
+ par->dirty.x1 = 0;
|
|
+ par->dirty.y1 = y1;
|
|
+ par->dirty.x2 = info->var.xres;
|
|
+ par->dirty.y2 = y2;
|
|
+ spin_unlock_irqrestore(&par->dirty.lock, flags);
|
|
+ }
|
|
+
|
|
+ vmw_fb_dirty_flush(par);
|
|
+};
|
|
+
|
|
+struct fb_deferred_io vmw_defio = {
|
|
+ .delay = VMW_DIRTY_DELAY,
|
|
+ .deferred_io = vmw_deferred_io,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Draw code
|
|
+ */
|
|
+
|
|
+static void vmw_fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
|
|
+{
|
|
+ cfb_fillrect(info, rect);
|
|
+ vmw_fb_dirty_mark(info->par, rect->dx, rect->dy,
|
|
+ rect->width, rect->height);
|
|
+}
|
|
+
|
|
+static void vmw_fb_copyarea(struct fb_info *info, const struct fb_copyarea *region)
|
|
+{
|
|
+ cfb_copyarea(info, region);
|
|
+ vmw_fb_dirty_mark(info->par, region->dx, region->dy,
|
|
+ region->width, region->height);
|
|
+}
|
|
+
|
|
+static void vmw_fb_imageblit(struct fb_info *info, const struct fb_image *image)
|
|
+{
|
|
+ cfb_imageblit(info, image);
|
|
+ vmw_fb_dirty_mark(info->par, image->dx, image->dy,
|
|
+ image->width, image->height);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Bring up code
|
|
+ */
|
|
+
|
|
+static struct fb_ops vmw_fb_ops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .fb_check_var = vmw_fb_check_var,
|
|
+ .fb_set_par = vmw_fb_set_par,
|
|
+ .fb_setcolreg = vmw_fb_setcolreg,
|
|
+ .fb_fillrect = vmw_fb_fillrect,
|
|
+ .fb_copyarea = vmw_fb_copyarea,
|
|
+ .fb_imageblit = vmw_fb_imageblit,
|
|
+ .fb_pan_display = vmw_fb_pan_display,
|
|
+ .fb_blank = vmw_fb_blank,
|
|
+};
|
|
+
|
|
+static int vmw_fb_create_bo(struct vmw_private *vmw_priv,
|
|
+ size_t size, struct vmw_dma_buffer **out)
|
|
+{
|
|
+ struct vmw_dma_buffer *vmw_bo;
|
|
+ struct ttm_placement ne_placement = vmw_vram_ne_placement;
|
|
+ int ret;
|
|
+
|
|
+ ne_placement.lpfn = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
|
+
|
|
+ /* interuptable? */
|
|
+ ret = ttm_write_lock(&vmw_priv->fbdev_master.lock, false);
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+
|
|
+ vmw_bo = kmalloc(sizeof(*vmw_bo), GFP_KERNEL);
|
|
+ if (!vmw_bo)
|
|
+ goto err_unlock;
|
|
+
|
|
+ ret = vmw_dmabuf_init(vmw_priv, vmw_bo, size,
|
|
+ &ne_placement,
|
|
+ false,
|
|
+ &vmw_dmabuf_bo_free);
|
|
+ if (unlikely(ret != 0))
|
|
+ goto err_unlock; /* init frees the buffer on failure */
|
|
+
|
|
+ *out = vmw_bo;
|
|
+
|
|
+ ttm_write_unlock(&vmw_priv->fbdev_master.lock);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_unlock:
|
|
+ ttm_write_unlock(&vmw_priv->fbdev_master.lock);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int vmw_fb_init(struct vmw_private *vmw_priv)
|
|
+{
|
|
+ struct device *device = &vmw_priv->dev->pdev->dev;
|
|
+ struct vmw_fb_par *par;
|
|
+ struct fb_info *info;
|
|
+ unsigned initial_width, initial_height;
|
|
+ unsigned fb_width, fb_height;
|
|
+ unsigned fb_bbp, fb_depth, fb_offset, fb_pitch, fb_size;
|
|
+ int ret;
|
|
+
|
|
+ initial_width = 800;
|
|
+ initial_height = 600;
|
|
+
|
|
+ fb_bbp = 32;
|
|
+ fb_depth = 24;
|
|
+
|
|
+ if (vmw_priv->capabilities & SVGA_CAP_MULTIMON) {
|
|
+ fb_width = min(vmw_priv->fb_max_width, (unsigned)2048);
|
|
+ fb_height = min(vmw_priv->fb_max_height, (unsigned)2048);
|
|
+ } else {
|
|
+ fb_width = min(vmw_priv->fb_max_width, initial_width);
|
|
+ fb_height = min(vmw_priv->fb_max_height, initial_height);
|
|
+ }
|
|
+
|
|
+ initial_width = min(fb_width, initial_width);
|
|
+ initial_height = min(fb_height, initial_height);
|
|
+
|
|
+ vmw_write(vmw_priv, SVGA_REG_WIDTH, fb_width);
|
|
+ vmw_write(vmw_priv, SVGA_REG_HEIGHT, fb_height);
|
|
+ vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, fb_bbp);
|
|
+ vmw_write(vmw_priv, SVGA_REG_DEPTH, fb_depth);
|
|
+ vmw_write(vmw_priv, SVGA_REG_RED_MASK, 0x00ff0000);
|
|
+ vmw_write(vmw_priv, SVGA_REG_GREEN_MASK, 0x0000ff00);
|
|
+ vmw_write(vmw_priv, SVGA_REG_BLUE_MASK, 0x000000ff);
|
|
+
|
|
+ fb_size = vmw_read(vmw_priv, SVGA_REG_FB_SIZE);
|
|
+ fb_offset = vmw_read(vmw_priv, SVGA_REG_FB_OFFSET);
|
|
+ fb_pitch = vmw_read(vmw_priv, SVGA_REG_BYTES_PER_LINE);
|
|
+
|
|
+ DRM_DEBUG("width %u\n", vmw_read(vmw_priv, SVGA_REG_MAX_WIDTH));
|
|
+ DRM_DEBUG("height %u\n", vmw_read(vmw_priv, SVGA_REG_MAX_HEIGHT));
|
|
+ DRM_DEBUG("width %u\n", vmw_read(vmw_priv, SVGA_REG_WIDTH));
|
|
+ DRM_DEBUG("height %u\n", vmw_read(vmw_priv, SVGA_REG_HEIGHT));
|
|
+ DRM_DEBUG("bpp %u\n", vmw_read(vmw_priv, SVGA_REG_BITS_PER_PIXEL));
|
|
+ DRM_DEBUG("depth %u\n", vmw_read(vmw_priv, SVGA_REG_DEPTH));
|
|
+ DRM_DEBUG("bpl %u\n", vmw_read(vmw_priv, SVGA_REG_BYTES_PER_LINE));
|
|
+ DRM_DEBUG("r mask %08x\n", vmw_read(vmw_priv, SVGA_REG_RED_MASK));
|
|
+ DRM_DEBUG("g mask %08x\n", vmw_read(vmw_priv, SVGA_REG_GREEN_MASK));
|
|
+ DRM_DEBUG("b mask %08x\n", vmw_read(vmw_priv, SVGA_REG_BLUE_MASK));
|
|
+ DRM_DEBUG("fb_offset 0x%08x\n", fb_offset);
|
|
+ DRM_DEBUG("fb_pitch %u\n", fb_pitch);
|
|
+ DRM_DEBUG("fb_size %u kiB\n", fb_size / 1024);
|
|
+
|
|
+ info = framebuffer_alloc(sizeof(*par), device);
|
|
+ if (!info)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /*
|
|
+ * Par
|
|
+ */
|
|
+ vmw_priv->fb_info = info;
|
|
+ par = info->par;
|
|
+ par->vmw_priv = vmw_priv;
|
|
+ par->depth = fb_depth;
|
|
+ par->bpp = fb_bbp;
|
|
+ par->vmalloc = NULL;
|
|
+ par->max_width = fb_width;
|
|
+ par->max_height = fb_height;
|
|
+
|
|
+ /*
|
|
+ * Create buffers and alloc memory
|
|
+ */
|
|
+ par->vmalloc = vmalloc(fb_size);
|
|
+ if (unlikely(par->vmalloc == NULL)) {
|
|
+ ret = -ENOMEM;
|
|
+ goto err_free;
|
|
+ }
|
|
+
|
|
+ ret = vmw_fb_create_bo(vmw_priv, fb_size, &par->vmw_bo);
|
|
+ if (unlikely(ret != 0))
|
|
+ goto err_free;
|
|
+
|
|
+ ret = ttm_bo_kmap(&par->vmw_bo->base,
|
|
+ 0,
|
|
+ par->vmw_bo->base.num_pages,
|
|
+ &par->map);
|
|
+ if (unlikely(ret != 0))
|
|
+ goto err_unref;
|
|
+ par->bo_ptr = ttm_kmap_obj_virtual(&par->map, &par->bo_iowrite);
|
|
+ par->bo_size = fb_size;
|
|
+
|
|
+ /*
|
|
+ * Fixed and var
|
|
+ */
|
|
+ strcpy(info->fix.id, "svgadrmfb");
|
|
+ info->fix.type = FB_TYPE_PACKED_PIXELS;
|
|
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
|
|
+ info->fix.type_aux = 0;
|
|
+ info->fix.xpanstep = 1; /* doing it in hw */
|
|
+ info->fix.ypanstep = 1; /* doing it in hw */
|
|
+ info->fix.ywrapstep = 0;
|
|
+ info->fix.accel = FB_ACCEL_NONE;
|
|
+ info->fix.line_length = fb_pitch;
|
|
+
|
|
+ info->fix.smem_start = 0;
|
|
+ info->fix.smem_len = fb_size;
|
|
+
|
|
+ info->fix.mmio_start = 0;
|
|
+ info->fix.mmio_len = 0;
|
|
+
|
|
+ info->pseudo_palette = par->pseudo_palette;
|
|
+ info->screen_base = par->vmalloc;
|
|
+ info->screen_size = fb_size;
|
|
+
|
|
+ info->flags = FBINFO_DEFAULT;
|
|
+ info->fbops = &vmw_fb_ops;
|
|
+
|
|
+ /* 24 depth per default */
|
|
+ info->var.red.offset = 16;
|
|
+ info->var.green.offset = 8;
|
|
+ info->var.blue.offset = 0;
|
|
+ info->var.red.length = 8;
|
|
+ info->var.green.length = 8;
|
|
+ info->var.blue.length = 8;
|
|
+ info->var.transp.offset = 0;
|
|
+ info->var.transp.length = 0;
|
|
+
|
|
+ info->var.xres_virtual = fb_width;
|
|
+ info->var.yres_virtual = fb_height;
|
|
+ info->var.bits_per_pixel = par->bpp;
|
|
+ info->var.xoffset = 0;
|
|
+ info->var.yoffset = 0;
|
|
+ info->var.activate = FB_ACTIVATE_NOW;
|
|
+ info->var.height = -1;
|
|
+ info->var.width = -1;
|
|
+
|
|
+ info->var.xres = initial_width;
|
|
+ info->var.yres = initial_height;
|
|
+
|
|
+#if 0
|
|
+ info->pixmap.size = 64*1024;
|
|
+ info->pixmap.buf_align = 8;
|
|
+ info->pixmap.access_align = 32;
|
|
+ info->pixmap.flags = FB_PIXMAP_SYSTEM;
|
|
+ info->pixmap.scan_align = 1;
|
|
+#else
|
|
+ info->pixmap.size = 0;
|
|
+ info->pixmap.buf_align = 8;
|
|
+ info->pixmap.access_align = 32;
|
|
+ info->pixmap.flags = FB_PIXMAP_SYSTEM;
|
|
+ info->pixmap.scan_align = 1;
|
|
+#endif
|
|
+
|
|
+ /*
|
|
+ * Dirty & Deferred IO
|
|
+ */
|
|
+ par->dirty.x1 = par->dirty.x2 = 0;
|
|
+ par->dirty.y1 = par->dirty.y1 = 0;
|
|
+ par->dirty.active = true;
|
|
+ spin_lock_init(&par->dirty.lock);
|
|
+ info->fbdefio = &vmw_defio;
|
|
+ fb_deferred_io_init(info);
|
|
+
|
|
+ ret = register_framebuffer(info);
|
|
+ if (unlikely(ret != 0))
|
|
+ goto err_defio;
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_defio:
|
|
+ fb_deferred_io_cleanup(info);
|
|
+ ttm_bo_kunmap(&par->map);
|
|
+err_unref:
|
|
+ ttm_bo_unref((struct ttm_buffer_object **)&par->vmw_bo);
|
|
+err_free:
|
|
+ vfree(par->vmalloc);
|
|
+ framebuffer_release(info);
|
|
+ vmw_priv->fb_info = NULL;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int vmw_fb_close(struct vmw_private *vmw_priv)
|
|
+{
|
|
+ struct fb_info *info;
|
|
+ struct vmw_fb_par *par;
|
|
+ struct ttm_buffer_object *bo;
|
|
+
|
|
+ if (!vmw_priv->fb_info)
|
|
+ return 0;
|
|
+
|
|
+ info = vmw_priv->fb_info;
|
|
+ par = info->par;
|
|
+ bo = &par->vmw_bo->base;
|
|
+ par->vmw_bo = NULL;
|
|
+
|
|
+ /* ??? order */
|
|
+ fb_deferred_io_cleanup(info);
|
|
+ unregister_framebuffer(info);
|
|
+
|
|
+ ttm_bo_kunmap(&par->map);
|
|
+ ttm_bo_unref(&bo);
|
|
+
|
|
+ vfree(par->vmalloc);
|
|
+ framebuffer_release(info);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int vmw_dmabuf_from_vram(struct vmw_private *vmw_priv,
|
|
+ struct vmw_dma_buffer *vmw_bo)
|
|
+{
|
|
+ struct ttm_buffer_object *bo = &vmw_bo->base;
|
|
+ int ret = 0;
|
|
+
|
|
+ ret = ttm_bo_reserve(bo, false, false, false, 0);
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+
|
|
+ ret = ttm_bo_validate(bo, &vmw_sys_placement, false, false);
|
|
+ ttm_bo_unreserve(bo);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int vmw_dmabuf_to_start_of_vram(struct vmw_private *vmw_priv,
|
|
+ struct vmw_dma_buffer *vmw_bo)
|
|
+{
|
|
+ struct ttm_buffer_object *bo = &vmw_bo->base;
|
|
+ struct ttm_placement ne_placement = vmw_vram_ne_placement;
|
|
+ int ret = 0;
|
|
+
|
|
+ ne_placement.lpfn = bo->num_pages;
|
|
+
|
|
+ /* interuptable? */
|
|
+ ret = ttm_write_lock(&vmw_priv->active_master->lock, false);
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+
|
|
+ ret = ttm_bo_reserve(bo, false, false, false, 0);
|
|
+ if (unlikely(ret != 0))
|
|
+ goto err_unlock;
|
|
+
|
|
+ ret = ttm_bo_validate(bo, &ne_placement, false, false);
|
|
+ ttm_bo_unreserve(bo);
|
|
+err_unlock:
|
|
+ ttm_write_unlock(&vmw_priv->active_master->lock);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int vmw_fb_off(struct vmw_private *vmw_priv)
|
|
+{
|
|
+ struct fb_info *info;
|
|
+ struct vmw_fb_par *par;
|
|
+ unsigned long flags;
|
|
+
|
|
+ if (!vmw_priv->fb_info)
|
|
+ return -EINVAL;
|
|
+
|
|
+ info = vmw_priv->fb_info;
|
|
+ par = info->par;
|
|
+
|
|
+ spin_lock_irqsave(&par->dirty.lock, flags);
|
|
+ par->dirty.active = false;
|
|
+ spin_unlock_irqrestore(&par->dirty.lock, flags);
|
|
+
|
|
+ flush_scheduled_work();
|
|
+
|
|
+ par->bo_ptr = NULL;
|
|
+ ttm_bo_kunmap(&par->map);
|
|
+
|
|
+ vmw_dmabuf_from_vram(vmw_priv, par->vmw_bo);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int vmw_fb_on(struct vmw_private *vmw_priv)
|
|
+{
|
|
+ struct fb_info *info;
|
|
+ struct vmw_fb_par *par;
|
|
+ unsigned long flags;
|
|
+ bool dummy;
|
|
+ int ret;
|
|
+
|
|
+ if (!vmw_priv->fb_info)
|
|
+ return -EINVAL;
|
|
+
|
|
+ info = vmw_priv->fb_info;
|
|
+ par = info->par;
|
|
+
|
|
+ /* we are already active */
|
|
+ if (par->bo_ptr != NULL)
|
|
+ return 0;
|
|
+
|
|
+ /* Make sure that all overlays are stoped when we take over */
|
|
+ vmw_overlay_stop_all(vmw_priv);
|
|
+
|
|
+ ret = vmw_dmabuf_to_start_of_vram(vmw_priv, par->vmw_bo);
|
|
+ if (unlikely(ret != 0)) {
|
|
+ DRM_ERROR("could not move buffer to start of VRAM\n");
|
|
+ goto err_no_buffer;
|
|
+ }
|
|
+
|
|
+ ret = ttm_bo_kmap(&par->vmw_bo->base,
|
|
+ 0,
|
|
+ par->vmw_bo->base.num_pages,
|
|
+ &par->map);
|
|
+ BUG_ON(ret != 0);
|
|
+ par->bo_ptr = ttm_kmap_obj_virtual(&par->map, &dummy);
|
|
+
|
|
+ spin_lock_irqsave(&par->dirty.lock, flags);
|
|
+ par->dirty.active = true;
|
|
+ spin_unlock_irqrestore(&par->dirty.lock, flags);
|
|
+
|
|
+err_no_buffer:
|
|
+ vmw_fb_set_par(info);
|
|
+
|
|
+ vmw_fb_dirty_mark(par, 0, 0, info->var.xres, info->var.yres);
|
|
+
|
|
+ /* If there already was stuff dirty we wont
|
|
+ * schedule a new work, so lets do it now */
|
|
+ schedule_delayed_work(&info->deferred_work, 0);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c
|
|
new file mode 100644
|
|
index 0000000..39d43a0
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c
|
|
@@ -0,0 +1,538 @@
|
|
+/**************************************************************************
|
|
+ *
|
|
+ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sub license, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial portions
|
|
+ * of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ **************************************************************************/
|
|
+
|
|
+#include "vmwgfx_drv.h"
|
|
+#include "drmP.h"
|
|
+#include "ttm/ttm_placement.h"
|
|
+
|
|
+bool vmw_fifo_have_3d(struct vmw_private *dev_priv)
|
|
+{
|
|
+ __le32 __iomem *fifo_mem = dev_priv->mmio_virt;
|
|
+ uint32_t fifo_min, hwversion;
|
|
+
|
|
+ fifo_min = ioread32(fifo_mem + SVGA_FIFO_MIN);
|
|
+ if (fifo_min <= SVGA_FIFO_3D_HWVERSION * sizeof(unsigned int))
|
|
+ return false;
|
|
+
|
|
+ hwversion = ioread32(fifo_mem + SVGA_FIFO_3D_HWVERSION);
|
|
+ if (hwversion == 0)
|
|
+ return false;
|
|
+
|
|
+ if (hwversion < SVGA3D_HWVERSION_WS65_B1)
|
|
+ return false;
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo)
|
|
+{
|
|
+ __le32 __iomem *fifo_mem = dev_priv->mmio_virt;
|
|
+ uint32_t max;
|
|
+ uint32_t min;
|
|
+ uint32_t dummy;
|
|
+ int ret;
|
|
+
|
|
+ fifo->static_buffer_size = VMWGFX_FIFO_STATIC_SIZE;
|
|
+ fifo->static_buffer = vmalloc(fifo->static_buffer_size);
|
|
+ if (unlikely(fifo->static_buffer == NULL))
|
|
+ return -ENOMEM;
|
|
+
|
|
+ fifo->last_buffer_size = VMWGFX_FIFO_STATIC_SIZE;
|
|
+ fifo->last_data_size = 0;
|
|
+ fifo->last_buffer_add = false;
|
|
+ fifo->last_buffer = vmalloc(fifo->last_buffer_size);
|
|
+ if (unlikely(fifo->last_buffer == NULL)) {
|
|
+ ret = -ENOMEM;
|
|
+ goto out_err;
|
|
+ }
|
|
+
|
|
+ fifo->dynamic_buffer = NULL;
|
|
+ fifo->reserved_size = 0;
|
|
+ fifo->using_bounce_buffer = false;
|
|
+
|
|
+ mutex_init(&fifo->fifo_mutex);
|
|
+ init_rwsem(&fifo->rwsem);
|
|
+
|
|
+ /*
|
|
+ * Allow mapping the first page read-only to user-space.
|
|
+ */
|
|
+
|
|
+ DRM_INFO("width %d\n", vmw_read(dev_priv, SVGA_REG_WIDTH));
|
|
+ DRM_INFO("height %d\n", vmw_read(dev_priv, SVGA_REG_HEIGHT));
|
|
+ DRM_INFO("bpp %d\n", vmw_read(dev_priv, SVGA_REG_BITS_PER_PIXEL));
|
|
+
|
|
+ mutex_lock(&dev_priv->hw_mutex);
|
|
+ dev_priv->enable_state = vmw_read(dev_priv, SVGA_REG_ENABLE);
|
|
+ dev_priv->config_done_state = vmw_read(dev_priv, SVGA_REG_CONFIG_DONE);
|
|
+ vmw_write(dev_priv, SVGA_REG_ENABLE, 1);
|
|
+
|
|
+ min = 4;
|
|
+ if (dev_priv->capabilities & SVGA_CAP_EXTENDED_FIFO)
|
|
+ min = vmw_read(dev_priv, SVGA_REG_MEM_REGS);
|
|
+ min <<= 2;
|
|
+
|
|
+ if (min < PAGE_SIZE)
|
|
+ min = PAGE_SIZE;
|
|
+
|
|
+ iowrite32(min, fifo_mem + SVGA_FIFO_MIN);
|
|
+ iowrite32(dev_priv->mmio_size, fifo_mem + SVGA_FIFO_MAX);
|
|
+ wmb();
|
|
+ iowrite32(min, fifo_mem + SVGA_FIFO_NEXT_CMD);
|
|
+ iowrite32(min, fifo_mem + SVGA_FIFO_STOP);
|
|
+ iowrite32(0, fifo_mem + SVGA_FIFO_BUSY);
|
|
+ mb();
|
|
+
|
|
+ vmw_write(dev_priv, SVGA_REG_CONFIG_DONE, 1);
|
|
+ mutex_unlock(&dev_priv->hw_mutex);
|
|
+
|
|
+ max = ioread32(fifo_mem + SVGA_FIFO_MAX);
|
|
+ min = ioread32(fifo_mem + SVGA_FIFO_MIN);
|
|
+ fifo->capabilities = ioread32(fifo_mem + SVGA_FIFO_CAPABILITIES);
|
|
+
|
|
+ DRM_INFO("Fifo max 0x%08x min 0x%08x cap 0x%08x\n",
|
|
+ (unsigned int) max,
|
|
+ (unsigned int) min,
|
|
+ (unsigned int) fifo->capabilities);
|
|
+
|
|
+ atomic_set(&dev_priv->fence_seq, dev_priv->last_read_sequence);
|
|
+ iowrite32(dev_priv->last_read_sequence, fifo_mem + SVGA_FIFO_FENCE);
|
|
+
|
|
+ return vmw_fifo_send_fence(dev_priv, &dummy);
|
|
+out_err:
|
|
+ vfree(fifo->static_buffer);
|
|
+ fifo->static_buffer = NULL;
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void vmw_fifo_ping_host(struct vmw_private *dev_priv, uint32_t reason)
|
|
+{
|
|
+ __le32 __iomem *fifo_mem = dev_priv->mmio_virt;
|
|
+
|
|
+ mutex_lock(&dev_priv->hw_mutex);
|
|
+
|
|
+ if (unlikely(ioread32(fifo_mem + SVGA_FIFO_BUSY) == 0)) {
|
|
+ iowrite32(1, fifo_mem + SVGA_FIFO_BUSY);
|
|
+ vmw_write(dev_priv, SVGA_REG_SYNC, reason);
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&dev_priv->hw_mutex);
|
|
+}
|
|
+
|
|
+void vmw_fifo_release(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo)
|
|
+{
|
|
+ __le32 __iomem *fifo_mem = dev_priv->mmio_virt;
|
|
+
|
|
+ mutex_lock(&dev_priv->hw_mutex);
|
|
+
|
|
+ while (vmw_read(dev_priv, SVGA_REG_BUSY) != 0)
|
|
+ vmw_write(dev_priv, SVGA_REG_SYNC, SVGA_SYNC_GENERIC);
|
|
+
|
|
+ dev_priv->last_read_sequence = ioread32(fifo_mem + SVGA_FIFO_FENCE);
|
|
+
|
|
+ vmw_write(dev_priv, SVGA_REG_CONFIG_DONE,
|
|
+ dev_priv->config_done_state);
|
|
+ vmw_write(dev_priv, SVGA_REG_ENABLE,
|
|
+ dev_priv->enable_state);
|
|
+
|
|
+ mutex_unlock(&dev_priv->hw_mutex);
|
|
+
|
|
+ if (likely(fifo->last_buffer != NULL)) {
|
|
+ vfree(fifo->last_buffer);
|
|
+ fifo->last_buffer = NULL;
|
|
+ }
|
|
+
|
|
+ if (likely(fifo->static_buffer != NULL)) {
|
|
+ vfree(fifo->static_buffer);
|
|
+ fifo->static_buffer = NULL;
|
|
+ }
|
|
+
|
|
+ if (likely(fifo->dynamic_buffer != NULL)) {
|
|
+ vfree(fifo->dynamic_buffer);
|
|
+ fifo->dynamic_buffer = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static bool vmw_fifo_is_full(struct vmw_private *dev_priv, uint32_t bytes)
|
|
+{
|
|
+ __le32 __iomem *fifo_mem = dev_priv->mmio_virt;
|
|
+ uint32_t max = ioread32(fifo_mem + SVGA_FIFO_MAX);
|
|
+ uint32_t next_cmd = ioread32(fifo_mem + SVGA_FIFO_NEXT_CMD);
|
|
+ uint32_t min = ioread32(fifo_mem + SVGA_FIFO_MIN);
|
|
+ uint32_t stop = ioread32(fifo_mem + SVGA_FIFO_STOP);
|
|
+
|
|
+ return ((max - next_cmd) + (stop - min) <= bytes);
|
|
+}
|
|
+
|
|
+static int vmw_fifo_wait_noirq(struct vmw_private *dev_priv,
|
|
+ uint32_t bytes, bool interruptible,
|
|
+ unsigned long timeout)
|
|
+{
|
|
+ int ret = 0;
|
|
+ unsigned long end_jiffies = jiffies + timeout;
|
|
+ DEFINE_WAIT(__wait);
|
|
+
|
|
+ DRM_INFO("Fifo wait noirq.\n");
|
|
+
|
|
+ for (;;) {
|
|
+ prepare_to_wait(&dev_priv->fifo_queue, &__wait,
|
|
+ (interruptible) ?
|
|
+ TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
|
|
+ if (!vmw_fifo_is_full(dev_priv, bytes))
|
|
+ break;
|
|
+ if (time_after_eq(jiffies, end_jiffies)) {
|
|
+ ret = -EBUSY;
|
|
+ DRM_ERROR("SVGA device lockup.\n");
|
|
+ break;
|
|
+ }
|
|
+ schedule_timeout(1);
|
|
+ if (interruptible && signal_pending(current)) {
|
|
+ ret = -ERESTARTSYS;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ finish_wait(&dev_priv->fifo_queue, &__wait);
|
|
+ wake_up_all(&dev_priv->fifo_queue);
|
|
+ DRM_INFO("Fifo noirq exit.\n");
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int vmw_fifo_wait(struct vmw_private *dev_priv,
|
|
+ uint32_t bytes, bool interruptible,
|
|
+ unsigned long timeout)
|
|
+{
|
|
+ long ret = 1L;
|
|
+ unsigned long irq_flags;
|
|
+
|
|
+ if (likely(!vmw_fifo_is_full(dev_priv, bytes)))
|
|
+ return 0;
|
|
+
|
|
+ vmw_fifo_ping_host(dev_priv, SVGA_SYNC_FIFOFULL);
|
|
+ if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK))
|
|
+ return vmw_fifo_wait_noirq(dev_priv, bytes,
|
|
+ interruptible, timeout);
|
|
+
|
|
+ mutex_lock(&dev_priv->hw_mutex);
|
|
+ if (atomic_add_return(1, &dev_priv->fifo_queue_waiters) > 0) {
|
|
+ spin_lock_irqsave(&dev_priv->irq_lock, irq_flags);
|
|
+ outl(SVGA_IRQFLAG_FIFO_PROGRESS,
|
|
+ dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
|
|
+ vmw_write(dev_priv, SVGA_REG_IRQMASK,
|
|
+ vmw_read(dev_priv, SVGA_REG_IRQMASK) |
|
|
+ SVGA_IRQFLAG_FIFO_PROGRESS);
|
|
+ spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags);
|
|
+ }
|
|
+ mutex_unlock(&dev_priv->hw_mutex);
|
|
+
|
|
+ if (interruptible)
|
|
+ ret = wait_event_interruptible_timeout
|
|
+ (dev_priv->fifo_queue,
|
|
+ !vmw_fifo_is_full(dev_priv, bytes), timeout);
|
|
+ else
|
|
+ ret = wait_event_timeout
|
|
+ (dev_priv->fifo_queue,
|
|
+ !vmw_fifo_is_full(dev_priv, bytes), timeout);
|
|
+
|
|
+ if (unlikely(ret == 0))
|
|
+ ret = -EBUSY;
|
|
+ else if (likely(ret > 0))
|
|
+ ret = 0;
|
|
+
|
|
+ mutex_lock(&dev_priv->hw_mutex);
|
|
+ if (atomic_dec_and_test(&dev_priv->fifo_queue_waiters)) {
|
|
+ spin_lock_irqsave(&dev_priv->irq_lock, irq_flags);
|
|
+ vmw_write(dev_priv, SVGA_REG_IRQMASK,
|
|
+ vmw_read(dev_priv, SVGA_REG_IRQMASK) &
|
|
+ ~SVGA_IRQFLAG_FIFO_PROGRESS);
|
|
+ spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags);
|
|
+ }
|
|
+ mutex_unlock(&dev_priv->hw_mutex);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void *vmw_fifo_reserve(struct vmw_private *dev_priv, uint32_t bytes)
|
|
+{
|
|
+ struct vmw_fifo_state *fifo_state = &dev_priv->fifo;
|
|
+ __le32 __iomem *fifo_mem = dev_priv->mmio_virt;
|
|
+ uint32_t max;
|
|
+ uint32_t min;
|
|
+ uint32_t next_cmd;
|
|
+ uint32_t reserveable = fifo_state->capabilities & SVGA_FIFO_CAP_RESERVE;
|
|
+ int ret;
|
|
+
|
|
+ mutex_lock(&fifo_state->fifo_mutex);
|
|
+ max = ioread32(fifo_mem + SVGA_FIFO_MAX);
|
|
+ min = ioread32(fifo_mem + SVGA_FIFO_MIN);
|
|
+ next_cmd = ioread32(fifo_mem + SVGA_FIFO_NEXT_CMD);
|
|
+
|
|
+ if (unlikely(bytes >= (max - min)))
|
|
+ goto out_err;
|
|
+
|
|
+ BUG_ON(fifo_state->reserved_size != 0);
|
|
+ BUG_ON(fifo_state->dynamic_buffer != NULL);
|
|
+
|
|
+ fifo_state->reserved_size = bytes;
|
|
+
|
|
+ while (1) {
|
|
+ uint32_t stop = ioread32(fifo_mem + SVGA_FIFO_STOP);
|
|
+ bool need_bounce = false;
|
|
+ bool reserve_in_place = false;
|
|
+
|
|
+ if (next_cmd >= stop) {
|
|
+ if (likely((next_cmd + bytes < max ||
|
|
+ (next_cmd + bytes == max && stop > min))))
|
|
+ reserve_in_place = true;
|
|
+
|
|
+ else if (vmw_fifo_is_full(dev_priv, bytes)) {
|
|
+ ret = vmw_fifo_wait(dev_priv, bytes,
|
|
+ false, 3 * HZ);
|
|
+ if (unlikely(ret != 0))
|
|
+ goto out_err;
|
|
+ } else
|
|
+ need_bounce = true;
|
|
+
|
|
+ } else {
|
|
+
|
|
+ if (likely((next_cmd + bytes < stop)))
|
|
+ reserve_in_place = true;
|
|
+ else {
|
|
+ ret = vmw_fifo_wait(dev_priv, bytes,
|
|
+ false, 3 * HZ);
|
|
+ if (unlikely(ret != 0))
|
|
+ goto out_err;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (reserve_in_place) {
|
|
+ if (reserveable || bytes <= sizeof(uint32_t)) {
|
|
+ fifo_state->using_bounce_buffer = false;
|
|
+
|
|
+ if (reserveable)
|
|
+ iowrite32(bytes, fifo_mem +
|
|
+ SVGA_FIFO_RESERVED);
|
|
+ return fifo_mem + (next_cmd >> 2);
|
|
+ } else {
|
|
+ need_bounce = true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (need_bounce) {
|
|
+ fifo_state->using_bounce_buffer = true;
|
|
+ if (bytes < fifo_state->static_buffer_size)
|
|
+ return fifo_state->static_buffer;
|
|
+ else {
|
|
+ fifo_state->dynamic_buffer = vmalloc(bytes);
|
|
+ return fifo_state->dynamic_buffer;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+out_err:
|
|
+ fifo_state->reserved_size = 0;
|
|
+ mutex_unlock(&fifo_state->fifo_mutex);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static void vmw_fifo_res_copy(struct vmw_fifo_state *fifo_state,
|
|
+ __le32 __iomem *fifo_mem,
|
|
+ uint32_t next_cmd,
|
|
+ uint32_t max, uint32_t min, uint32_t bytes)
|
|
+{
|
|
+ uint32_t chunk_size = max - next_cmd;
|
|
+ uint32_t rest;
|
|
+ uint32_t *buffer = (fifo_state->dynamic_buffer != NULL) ?
|
|
+ fifo_state->dynamic_buffer : fifo_state->static_buffer;
|
|
+
|
|
+ if (bytes < chunk_size)
|
|
+ chunk_size = bytes;
|
|
+
|
|
+ iowrite32(bytes, fifo_mem + SVGA_FIFO_RESERVED);
|
|
+ mb();
|
|
+ memcpy_toio(fifo_mem + (next_cmd >> 2), buffer, chunk_size);
|
|
+ rest = bytes - chunk_size;
|
|
+ if (rest)
|
|
+ memcpy_toio(fifo_mem + (min >> 2), buffer + (chunk_size >> 2),
|
|
+ rest);
|
|
+}
|
|
+
|
|
+static void vmw_fifo_slow_copy(struct vmw_fifo_state *fifo_state,
|
|
+ __le32 __iomem *fifo_mem,
|
|
+ uint32_t next_cmd,
|
|
+ uint32_t max, uint32_t min, uint32_t bytes)
|
|
+{
|
|
+ uint32_t *buffer = (fifo_state->dynamic_buffer != NULL) ?
|
|
+ fifo_state->dynamic_buffer : fifo_state->static_buffer;
|
|
+
|
|
+ while (bytes > 0) {
|
|
+ iowrite32(*buffer++, fifo_mem + (next_cmd >> 2));
|
|
+ next_cmd += sizeof(uint32_t);
|
|
+ if (unlikely(next_cmd == max))
|
|
+ next_cmd = min;
|
|
+ mb();
|
|
+ iowrite32(next_cmd, fifo_mem + SVGA_FIFO_NEXT_CMD);
|
|
+ mb();
|
|
+ bytes -= sizeof(uint32_t);
|
|
+ }
|
|
+}
|
|
+
|
|
+void vmw_fifo_commit(struct vmw_private *dev_priv, uint32_t bytes)
|
|
+{
|
|
+ struct vmw_fifo_state *fifo_state = &dev_priv->fifo;
|
|
+ __le32 __iomem *fifo_mem = dev_priv->mmio_virt;
|
|
+ uint32_t next_cmd = ioread32(fifo_mem + SVGA_FIFO_NEXT_CMD);
|
|
+ uint32_t max = ioread32(fifo_mem + SVGA_FIFO_MAX);
|
|
+ uint32_t min = ioread32(fifo_mem + SVGA_FIFO_MIN);
|
|
+ bool reserveable = fifo_state->capabilities & SVGA_FIFO_CAP_RESERVE;
|
|
+
|
|
+ BUG_ON((bytes & 3) != 0);
|
|
+ BUG_ON(bytes > fifo_state->reserved_size);
|
|
+
|
|
+ fifo_state->reserved_size = 0;
|
|
+
|
|
+ if (fifo_state->using_bounce_buffer) {
|
|
+ if (reserveable)
|
|
+ vmw_fifo_res_copy(fifo_state, fifo_mem,
|
|
+ next_cmd, max, min, bytes);
|
|
+ else
|
|
+ vmw_fifo_slow_copy(fifo_state, fifo_mem,
|
|
+ next_cmd, max, min, bytes);
|
|
+
|
|
+ if (fifo_state->dynamic_buffer) {
|
|
+ vfree(fifo_state->dynamic_buffer);
|
|
+ fifo_state->dynamic_buffer = NULL;
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ down_write(&fifo_state->rwsem);
|
|
+ if (fifo_state->using_bounce_buffer || reserveable) {
|
|
+ next_cmd += bytes;
|
|
+ if (next_cmd >= max)
|
|
+ next_cmd -= max - min;
|
|
+ mb();
|
|
+ iowrite32(next_cmd, fifo_mem + SVGA_FIFO_NEXT_CMD);
|
|
+ }
|
|
+
|
|
+ if (reserveable)
|
|
+ iowrite32(0, fifo_mem + SVGA_FIFO_RESERVED);
|
|
+ mb();
|
|
+ up_write(&fifo_state->rwsem);
|
|
+ vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC);
|
|
+ mutex_unlock(&fifo_state->fifo_mutex);
|
|
+}
|
|
+
|
|
+int vmw_fifo_send_fence(struct vmw_private *dev_priv, uint32_t *sequence)
|
|
+{
|
|
+ struct vmw_fifo_state *fifo_state = &dev_priv->fifo;
|
|
+ struct svga_fifo_cmd_fence *cmd_fence;
|
|
+ void *fm;
|
|
+ int ret = 0;
|
|
+ uint32_t bytes = sizeof(__le32) + sizeof(*cmd_fence);
|
|
+
|
|
+ fm = vmw_fifo_reserve(dev_priv, bytes);
|
|
+ if (unlikely(fm == NULL)) {
|
|
+ *sequence = atomic_read(&dev_priv->fence_seq);
|
|
+ ret = -ENOMEM;
|
|
+ (void)vmw_fallback_wait(dev_priv, false, true, *sequence,
|
|
+ false, 3*HZ);
|
|
+ goto out_err;
|
|
+ }
|
|
+
|
|
+ do {
|
|
+ *sequence = atomic_add_return(1, &dev_priv->fence_seq);
|
|
+ } while (*sequence == 0);
|
|
+
|
|
+ if (!(fifo_state->capabilities & SVGA_FIFO_CAP_FENCE)) {
|
|
+
|
|
+ /*
|
|
+ * Don't request hardware to send a fence. The
|
|
+ * waiting code in vmwgfx_irq.c will emulate this.
|
|
+ */
|
|
+
|
|
+ vmw_fifo_commit(dev_priv, 0);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ *(__le32 *) fm = cpu_to_le32(SVGA_CMD_FENCE);
|
|
+ cmd_fence = (struct svga_fifo_cmd_fence *)
|
|
+ ((unsigned long)fm + sizeof(__le32));
|
|
+
|
|
+ iowrite32(*sequence, &cmd_fence->fence);
|
|
+ fifo_state->last_buffer_add = true;
|
|
+ vmw_fifo_commit(dev_priv, bytes);
|
|
+ fifo_state->last_buffer_add = false;
|
|
+
|
|
+out_err:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Map the first page of the FIFO read-only to user-space.
|
|
+ */
|
|
+
|
|
+static int vmw_fifo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
|
|
+{
|
|
+ int ret;
|
|
+ unsigned long address = (unsigned long)vmf->virtual_address;
|
|
+
|
|
+ if (address != vma->vm_start)
|
|
+ return VM_FAULT_SIGBUS;
|
|
+
|
|
+ ret = vm_insert_pfn(vma, address, vma->vm_pgoff);
|
|
+ if (likely(ret == -EBUSY || ret == 0))
|
|
+ return VM_FAULT_NOPAGE;
|
|
+ else if (ret == -ENOMEM)
|
|
+ return VM_FAULT_OOM;
|
|
+
|
|
+ return VM_FAULT_SIGBUS;
|
|
+}
|
|
+
|
|
+static struct vm_operations_struct vmw_fifo_vm_ops = {
|
|
+ .fault = vmw_fifo_vm_fault,
|
|
+ .open = NULL,
|
|
+ .close = NULL
|
|
+};
|
|
+
|
|
+int vmw_fifo_mmap(struct file *filp, struct vm_area_struct *vma)
|
|
+{
|
|
+ struct drm_file *file_priv;
|
|
+ struct vmw_private *dev_priv;
|
|
+
|
|
+ file_priv = (struct drm_file *)filp->private_data;
|
|
+ dev_priv = vmw_priv(file_priv->minor->dev);
|
|
+
|
|
+ if (vma->vm_pgoff != (dev_priv->mmio_start >> PAGE_SHIFT) ||
|
|
+ (vma->vm_end - vma->vm_start) != PAGE_SIZE)
|
|
+ return -EINVAL;
|
|
+
|
|
+ vma->vm_flags &= ~(VM_WRITE | VM_MAYWRITE);
|
|
+ vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_SHARED;
|
|
+ vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
|
|
+ vma->vm_page_prot = ttm_io_prot(TTM_PL_FLAG_UNCACHED,
|
|
+ vma->vm_page_prot);
|
|
+ vma->vm_ops = &vmw_fifo_vm_ops;
|
|
+ return 0;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c
|
|
new file mode 100644
|
|
index 0000000..5f8908a
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c
|
|
@@ -0,0 +1,213 @@
|
|
+/**************************************************************************
|
|
+ *
|
|
+ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sub license, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial portions
|
|
+ * of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ **************************************************************************/
|
|
+
|
|
+#include "vmwgfx_drv.h"
|
|
+#include "drmP.h"
|
|
+#include "ttm/ttm_bo_driver.h"
|
|
+
|
|
+/**
|
|
+ * FIXME: Adjust to the ttm lowmem / highmem storage to minimize
|
|
+ * the number of used descriptors.
|
|
+ */
|
|
+
|
|
+static int vmw_gmr_build_descriptors(struct list_head *desc_pages,
|
|
+ struct page *pages[],
|
|
+ unsigned long num_pages)
|
|
+{
|
|
+ struct page *page, *next;
|
|
+ struct svga_guest_mem_descriptor *page_virtual = NULL;
|
|
+ struct svga_guest_mem_descriptor *desc_virtual = NULL;
|
|
+ unsigned int desc_per_page;
|
|
+ unsigned long prev_pfn;
|
|
+ unsigned long pfn;
|
|
+ int ret;
|
|
+
|
|
+ desc_per_page = PAGE_SIZE /
|
|
+ sizeof(struct svga_guest_mem_descriptor) - 1;
|
|
+
|
|
+ while (likely(num_pages != 0)) {
|
|
+ page = alloc_page(__GFP_HIGHMEM);
|
|
+ if (unlikely(page == NULL)) {
|
|
+ ret = -ENOMEM;
|
|
+ goto out_err;
|
|
+ }
|
|
+
|
|
+ list_add_tail(&page->lru, desc_pages);
|
|
+
|
|
+ /*
|
|
+ * Point previous page terminating descriptor to this
|
|
+ * page before unmapping it.
|
|
+ */
|
|
+
|
|
+ if (likely(page_virtual != NULL)) {
|
|
+ desc_virtual->ppn = page_to_pfn(page);
|
|
+ kunmap_atomic(page_virtual, KM_USER0);
|
|
+ }
|
|
+
|
|
+ page_virtual = kmap_atomic(page, KM_USER0);
|
|
+ desc_virtual = page_virtual - 1;
|
|
+ prev_pfn = ~(0UL);
|
|
+
|
|
+ while (likely(num_pages != 0)) {
|
|
+ pfn = page_to_pfn(*pages);
|
|
+
|
|
+ if (pfn != prev_pfn + 1) {
|
|
+
|
|
+ if (desc_virtual - page_virtual ==
|
|
+ desc_per_page - 1)
|
|
+ break;
|
|
+
|
|
+ (++desc_virtual)->ppn = cpu_to_le32(pfn);
|
|
+ desc_virtual->num_pages = cpu_to_le32(1);
|
|
+ } else {
|
|
+ uint32_t tmp =
|
|
+ le32_to_cpu(desc_virtual->num_pages);
|
|
+ desc_virtual->num_pages = cpu_to_le32(tmp + 1);
|
|
+ }
|
|
+ prev_pfn = pfn;
|
|
+ --num_pages;
|
|
+ ++pages;
|
|
+ }
|
|
+
|
|
+ (++desc_virtual)->ppn = cpu_to_le32(0);
|
|
+ desc_virtual->num_pages = cpu_to_le32(0);
|
|
+ }
|
|
+
|
|
+ if (likely(page_virtual != NULL))
|
|
+ kunmap_atomic(page_virtual, KM_USER0);
|
|
+
|
|
+ return 0;
|
|
+out_err:
|
|
+ list_for_each_entry_safe(page, next, desc_pages, lru) {
|
|
+ list_del_init(&page->lru);
|
|
+ __free_page(page);
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static inline void vmw_gmr_free_descriptors(struct list_head *desc_pages)
|
|
+{
|
|
+ struct page *page, *next;
|
|
+
|
|
+ list_for_each_entry_safe(page, next, desc_pages, lru) {
|
|
+ list_del_init(&page->lru);
|
|
+ __free_page(page);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void vmw_gmr_fire_descriptors(struct vmw_private *dev_priv,
|
|
+ int gmr_id, struct list_head *desc_pages)
|
|
+{
|
|
+ struct page *page;
|
|
+
|
|
+ if (unlikely(list_empty(desc_pages)))
|
|
+ return;
|
|
+
|
|
+ page = list_entry(desc_pages->next, struct page, lru);
|
|
+
|
|
+ mutex_lock(&dev_priv->hw_mutex);
|
|
+
|
|
+ vmw_write(dev_priv, SVGA_REG_GMR_ID, gmr_id);
|
|
+ wmb();
|
|
+ vmw_write(dev_priv, SVGA_REG_GMR_DESCRIPTOR, page_to_pfn(page));
|
|
+ mb();
|
|
+
|
|
+ mutex_unlock(&dev_priv->hw_mutex);
|
|
+
|
|
+}
|
|
+
|
|
+/**
|
|
+ * FIXME: Adjust to the ttm lowmem / highmem storage to minimize
|
|
+ * the number of used descriptors.
|
|
+ */
|
|
+
|
|
+static unsigned long vmw_gmr_count_descriptors(struct page *pages[],
|
|
+ unsigned long num_pages)
|
|
+{
|
|
+ unsigned long prev_pfn = ~(0UL);
|
|
+ unsigned long pfn;
|
|
+ unsigned long descriptors = 0;
|
|
+
|
|
+ while (num_pages--) {
|
|
+ pfn = page_to_pfn(*pages++);
|
|
+ if (prev_pfn + 1 != pfn)
|
|
+ ++descriptors;
|
|
+ prev_pfn = pfn;
|
|
+ }
|
|
+
|
|
+ return descriptors;
|
|
+}
|
|
+
|
|
+int vmw_gmr_bind(struct vmw_private *dev_priv,
|
|
+ struct ttm_buffer_object *bo)
|
|
+{
|
|
+ struct ttm_tt *ttm = bo->ttm;
|
|
+ unsigned long descriptors;
|
|
+ int ret;
|
|
+ uint32_t id;
|
|
+ struct list_head desc_pages;
|
|
+
|
|
+ if (!(dev_priv->capabilities & SVGA_CAP_GMR))
|
|
+ return -EINVAL;
|
|
+
|
|
+ ret = ttm_tt_populate(ttm);
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+
|
|
+ descriptors = vmw_gmr_count_descriptors(ttm->pages, ttm->num_pages);
|
|
+ if (unlikely(descriptors > dev_priv->max_gmr_descriptors))
|
|
+ return -EINVAL;
|
|
+
|
|
+ INIT_LIST_HEAD(&desc_pages);
|
|
+ ret = vmw_gmr_build_descriptors(&desc_pages, ttm->pages,
|
|
+ ttm->num_pages);
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+
|
|
+ ret = vmw_gmr_id_alloc(dev_priv, &id);
|
|
+ if (unlikely(ret != 0))
|
|
+ goto out_no_id;
|
|
+
|
|
+ vmw_gmr_fire_descriptors(dev_priv, id, &desc_pages);
|
|
+ vmw_gmr_free_descriptors(&desc_pages);
|
|
+ vmw_dmabuf_set_gmr(bo, id);
|
|
+ return 0;
|
|
+
|
|
+out_no_id:
|
|
+ vmw_gmr_free_descriptors(&desc_pages);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void vmw_gmr_unbind(struct vmw_private *dev_priv, int gmr_id)
|
|
+{
|
|
+ mutex_lock(&dev_priv->hw_mutex);
|
|
+ vmw_write(dev_priv, SVGA_REG_GMR_ID, gmr_id);
|
|
+ wmb();
|
|
+ vmw_write(dev_priv, SVGA_REG_GMR_DESCRIPTOR, 0);
|
|
+ mb();
|
|
+ mutex_unlock(&dev_priv->hw_mutex);
|
|
+}
|
|
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
|
|
new file mode 100644
|
|
index 0000000..1c7a316
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
|
|
@@ -0,0 +1,87 @@
|
|
+/**************************************************************************
|
|
+ *
|
|
+ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sub license, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial portions
|
|
+ * of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ **************************************************************************/
|
|
+
|
|
+#include "vmwgfx_drv.h"
|
|
+#include "vmwgfx_drm.h"
|
|
+
|
|
+int vmw_getparam_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct vmw_private *dev_priv = vmw_priv(dev);
|
|
+ struct drm_vmw_getparam_arg *param =
|
|
+ (struct drm_vmw_getparam_arg *)data;
|
|
+
|
|
+ switch (param->param) {
|
|
+ case DRM_VMW_PARAM_NUM_STREAMS:
|
|
+ param->value = vmw_overlay_num_overlays(dev_priv);
|
|
+ break;
|
|
+ case DRM_VMW_PARAM_NUM_FREE_STREAMS:
|
|
+ param->value = vmw_overlay_num_free_overlays(dev_priv);
|
|
+ break;
|
|
+ case DRM_VMW_PARAM_3D:
|
|
+ param->value = vmw_fifo_have_3d(dev_priv) ? 1 : 0;
|
|
+ break;
|
|
+ case DRM_VMW_PARAM_FIFO_OFFSET:
|
|
+ param->value = dev_priv->mmio_start;
|
|
+ break;
|
|
+ case DRM_VMW_PARAM_HW_CAPS:
|
|
+ param->value = dev_priv->capabilities;
|
|
+ break;
|
|
+ case DRM_VMW_PARAM_FIFO_CAPS:
|
|
+ param->value = dev_priv->fifo.capabilities;
|
|
+ break;
|
|
+ default:
|
|
+ DRM_ERROR("Illegal vmwgfx get param request: %d\n",
|
|
+ param->param);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int vmw_fifo_debug_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct vmw_private *dev_priv = vmw_priv(dev);
|
|
+ struct vmw_fifo_state *fifo_state = &dev_priv->fifo;
|
|
+ struct drm_vmw_fifo_debug_arg *arg =
|
|
+ (struct drm_vmw_fifo_debug_arg *)data;
|
|
+ __le32 __user *buffer = (__le32 __user *)
|
|
+ (unsigned long)arg->debug_buffer;
|
|
+
|
|
+ if (unlikely(fifo_state->last_buffer == NULL))
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (arg->debug_buffer_size < fifo_state->last_data_size) {
|
|
+ arg->used_size = arg->debug_buffer_size;
|
|
+ arg->did_not_fit = 1;
|
|
+ } else {
|
|
+ arg->used_size = fifo_state->last_data_size;
|
|
+ arg->did_not_fit = 0;
|
|
+ }
|
|
+ return copy_to_user(buffer, fifo_state->last_buffer, arg->used_size);
|
|
+}
|
|
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c b/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c
|
|
new file mode 100644
|
|
index 0000000..4d7cb53
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c
|
|
@@ -0,0 +1,286 @@
|
|
+/**************************************************************************
|
|
+ *
|
|
+ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sub license, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial portions
|
|
+ * of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ **************************************************************************/
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "vmwgfx_drv.h"
|
|
+
|
|
+#define VMW_FENCE_WRAP (1 << 24)
|
|
+
|
|
+irqreturn_t vmw_irq_handler(DRM_IRQ_ARGS)
|
|
+{
|
|
+ struct drm_device *dev = (struct drm_device *)arg;
|
|
+ struct vmw_private *dev_priv = vmw_priv(dev);
|
|
+ uint32_t status;
|
|
+
|
|
+ spin_lock(&dev_priv->irq_lock);
|
|
+ status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
|
|
+ spin_unlock(&dev_priv->irq_lock);
|
|
+
|
|
+ if (status & SVGA_IRQFLAG_ANY_FENCE)
|
|
+ wake_up_all(&dev_priv->fence_queue);
|
|
+ if (status & SVGA_IRQFLAG_FIFO_PROGRESS)
|
|
+ wake_up_all(&dev_priv->fifo_queue);
|
|
+
|
|
+ if (likely(status)) {
|
|
+ outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
|
|
+ return IRQ_HANDLED;
|
|
+ }
|
|
+
|
|
+ return IRQ_NONE;
|
|
+}
|
|
+
|
|
+static bool vmw_fifo_idle(struct vmw_private *dev_priv, uint32_t sequence)
|
|
+{
|
|
+ uint32_t busy;
|
|
+
|
|
+ mutex_lock(&dev_priv->hw_mutex);
|
|
+ busy = vmw_read(dev_priv, SVGA_REG_BUSY);
|
|
+ mutex_unlock(&dev_priv->hw_mutex);
|
|
+
|
|
+ return (busy == 0);
|
|
+}
|
|
+
|
|
+
|
|
+bool vmw_fence_signaled(struct vmw_private *dev_priv,
|
|
+ uint32_t sequence)
|
|
+{
|
|
+ __le32 __iomem *fifo_mem = dev_priv->mmio_virt;
|
|
+ struct vmw_fifo_state *fifo_state;
|
|
+ bool ret;
|
|
+
|
|
+ if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP))
|
|
+ return true;
|
|
+
|
|
+ dev_priv->last_read_sequence = ioread32(fifo_mem + SVGA_FIFO_FENCE);
|
|
+ if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP))
|
|
+ return true;
|
|
+
|
|
+ fifo_state = &dev_priv->fifo;
|
|
+ if (!(fifo_state->capabilities & SVGA_FIFO_CAP_FENCE) &&
|
|
+ vmw_fifo_idle(dev_priv, sequence))
|
|
+ return true;
|
|
+
|
|
+ /**
|
|
+ * Then check if the sequence is higher than what we've actually
|
|
+ * emitted. Then the fence is stale and signaled.
|
|
+ */
|
|
+
|
|
+ ret = ((atomic_read(&dev_priv->fence_seq) - sequence)
|
|
+ > VMW_FENCE_WRAP);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int vmw_fallback_wait(struct vmw_private *dev_priv,
|
|
+ bool lazy,
|
|
+ bool fifo_idle,
|
|
+ uint32_t sequence,
|
|
+ bool interruptible,
|
|
+ unsigned long timeout)
|
|
+{
|
|
+ struct vmw_fifo_state *fifo_state = &dev_priv->fifo;
|
|
+
|
|
+ uint32_t count = 0;
|
|
+ uint32_t signal_seq;
|
|
+ int ret;
|
|
+ unsigned long end_jiffies = jiffies + timeout;
|
|
+ bool (*wait_condition)(struct vmw_private *, uint32_t);
|
|
+ DEFINE_WAIT(__wait);
|
|
+
|
|
+ wait_condition = (fifo_idle) ? &vmw_fifo_idle :
|
|
+ &vmw_fence_signaled;
|
|
+
|
|
+ /**
|
|
+ * Block command submission while waiting for idle.
|
|
+ */
|
|
+
|
|
+ if (fifo_idle)
|
|
+ down_read(&fifo_state->rwsem);
|
|
+ signal_seq = atomic_read(&dev_priv->fence_seq);
|
|
+ ret = 0;
|
|
+
|
|
+ for (;;) {
|
|
+ prepare_to_wait(&dev_priv->fence_queue, &__wait,
|
|
+ (interruptible) ?
|
|
+ TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
|
|
+ if (wait_condition(dev_priv, sequence))
|
|
+ break;
|
|
+ if (time_after_eq(jiffies, end_jiffies)) {
|
|
+ DRM_ERROR("SVGA device lockup.\n");
|
|
+ break;
|
|
+ }
|
|
+ if (lazy)
|
|
+ schedule_timeout(1);
|
|
+ else if ((++count & 0x0F) == 0) {
|
|
+ /**
|
|
+ * FIXME: Use schedule_hr_timeout here for
|
|
+ * newer kernels and lower CPU utilization.
|
|
+ */
|
|
+
|
|
+ __set_current_state(TASK_RUNNING);
|
|
+ schedule();
|
|
+ __set_current_state((interruptible) ?
|
|
+ TASK_INTERRUPTIBLE :
|
|
+ TASK_UNINTERRUPTIBLE);
|
|
+ }
|
|
+ if (interruptible && signal_pending(current)) {
|
|
+ ret = -ERESTARTSYS;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ finish_wait(&dev_priv->fence_queue, &__wait);
|
|
+ if (ret == 0 && fifo_idle) {
|
|
+ __le32 __iomem *fifo_mem = dev_priv->mmio_virt;
|
|
+ iowrite32(signal_seq, fifo_mem + SVGA_FIFO_FENCE);
|
|
+ }
|
|
+ wake_up_all(&dev_priv->fence_queue);
|
|
+ if (fifo_idle)
|
|
+ up_read(&fifo_state->rwsem);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int vmw_wait_fence(struct vmw_private *dev_priv,
|
|
+ bool lazy, uint32_t sequence,
|
|
+ bool interruptible, unsigned long timeout)
|
|
+{
|
|
+ long ret;
|
|
+ unsigned long irq_flags;
|
|
+ struct vmw_fifo_state *fifo = &dev_priv->fifo;
|
|
+
|
|
+ if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP))
|
|
+ return 0;
|
|
+
|
|
+ if (likely(vmw_fence_signaled(dev_priv, sequence)))
|
|
+ return 0;
|
|
+
|
|
+ vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC);
|
|
+
|
|
+ if (!(fifo->capabilities & SVGA_FIFO_CAP_FENCE))
|
|
+ return vmw_fallback_wait(dev_priv, lazy, true, sequence,
|
|
+ interruptible, timeout);
|
|
+
|
|
+ if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK))
|
|
+ return vmw_fallback_wait(dev_priv, lazy, false, sequence,
|
|
+ interruptible, timeout);
|
|
+
|
|
+ mutex_lock(&dev_priv->hw_mutex);
|
|
+ if (atomic_add_return(1, &dev_priv->fence_queue_waiters) > 0) {
|
|
+ spin_lock_irqsave(&dev_priv->irq_lock, irq_flags);
|
|
+ outl(SVGA_IRQFLAG_ANY_FENCE,
|
|
+ dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
|
|
+ vmw_write(dev_priv, SVGA_REG_IRQMASK,
|
|
+ vmw_read(dev_priv, SVGA_REG_IRQMASK) |
|
|
+ SVGA_IRQFLAG_ANY_FENCE);
|
|
+ spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags);
|
|
+ }
|
|
+ mutex_unlock(&dev_priv->hw_mutex);
|
|
+
|
|
+ if (interruptible)
|
|
+ ret = wait_event_interruptible_timeout
|
|
+ (dev_priv->fence_queue,
|
|
+ vmw_fence_signaled(dev_priv, sequence),
|
|
+ timeout);
|
|
+ else
|
|
+ ret = wait_event_timeout
|
|
+ (dev_priv->fence_queue,
|
|
+ vmw_fence_signaled(dev_priv, sequence),
|
|
+ timeout);
|
|
+
|
|
+ if (unlikely(ret == 0))
|
|
+ ret = -EBUSY;
|
|
+ else if (likely(ret > 0))
|
|
+ ret = 0;
|
|
+
|
|
+ mutex_lock(&dev_priv->hw_mutex);
|
|
+ if (atomic_dec_and_test(&dev_priv->fence_queue_waiters)) {
|
|
+ spin_lock_irqsave(&dev_priv->irq_lock, irq_flags);
|
|
+ vmw_write(dev_priv, SVGA_REG_IRQMASK,
|
|
+ vmw_read(dev_priv, SVGA_REG_IRQMASK) &
|
|
+ ~SVGA_IRQFLAG_ANY_FENCE);
|
|
+ spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags);
|
|
+ }
|
|
+ mutex_unlock(&dev_priv->hw_mutex);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void vmw_irq_preinstall(struct drm_device *dev)
|
|
+{
|
|
+ struct vmw_private *dev_priv = vmw_priv(dev);
|
|
+ uint32_t status;
|
|
+
|
|
+ if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK))
|
|
+ return;
|
|
+
|
|
+ spin_lock_init(&dev_priv->irq_lock);
|
|
+ status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
|
|
+ outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
|
|
+}
|
|
+
|
|
+int vmw_irq_postinstall(struct drm_device *dev)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void vmw_irq_uninstall(struct drm_device *dev)
|
|
+{
|
|
+ struct vmw_private *dev_priv = vmw_priv(dev);
|
|
+ uint32_t status;
|
|
+
|
|
+ if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK))
|
|
+ return;
|
|
+
|
|
+ mutex_lock(&dev_priv->hw_mutex);
|
|
+ vmw_write(dev_priv, SVGA_REG_IRQMASK, 0);
|
|
+ mutex_unlock(&dev_priv->hw_mutex);
|
|
+
|
|
+ status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
|
|
+ outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
|
|
+}
|
|
+
|
|
+#define VMW_FENCE_WAIT_TIMEOUT 3*HZ;
|
|
+
|
|
+int vmw_fence_wait_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_vmw_fence_wait_arg *arg =
|
|
+ (struct drm_vmw_fence_wait_arg *)data;
|
|
+ unsigned long timeout;
|
|
+
|
|
+ if (!arg->cookie_valid) {
|
|
+ arg->cookie_valid = 1;
|
|
+ arg->kernel_cookie = jiffies + VMW_FENCE_WAIT_TIMEOUT;
|
|
+ }
|
|
+
|
|
+ timeout = jiffies;
|
|
+ if (time_after_eq(timeout, (unsigned long)arg->kernel_cookie))
|
|
+ return -EBUSY;
|
|
+
|
|
+ timeout = (unsigned long)arg->kernel_cookie - timeout;
|
|
+ return vmw_wait_fence(vmw_priv(dev), true, arg->sequence, true, timeout);
|
|
+}
|
|
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
|
|
new file mode 100644
|
|
index 0000000..31f9afe
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
|
|
@@ -0,0 +1,880 @@
|
|
+/**************************************************************************
|
|
+ *
|
|
+ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sub license, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial portions
|
|
+ * of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ **************************************************************************/
|
|
+
|
|
+#include "vmwgfx_kms.h"
|
|
+
|
|
+/* Might need a hrtimer here? */
|
|
+#define VMWGFX_PRESENT_RATE ((HZ / 60 > 0) ? HZ / 60 : 1)
|
|
+
|
|
+
|
|
+void vmw_display_unit_cleanup(struct vmw_display_unit *du)
|
|
+{
|
|
+ if (du->cursor_surface)
|
|
+ vmw_surface_unreference(&du->cursor_surface);
|
|
+ if (du->cursor_dmabuf)
|
|
+ vmw_dmabuf_unreference(&du->cursor_dmabuf);
|
|
+ drm_crtc_cleanup(&du->crtc);
|
|
+ drm_encoder_cleanup(&du->encoder);
|
|
+ drm_connector_cleanup(&du->connector);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Display Unit Cursor functions
|
|
+ */
|
|
+
|
|
+int vmw_cursor_update_image(struct vmw_private *dev_priv,
|
|
+ u32 *image, u32 width, u32 height,
|
|
+ u32 hotspotX, u32 hotspotY)
|
|
+{
|
|
+ struct {
|
|
+ u32 cmd;
|
|
+ SVGAFifoCmdDefineAlphaCursor cursor;
|
|
+ } *cmd;
|
|
+ u32 image_size = width * height * 4;
|
|
+ u32 cmd_size = sizeof(*cmd) + image_size;
|
|
+
|
|
+ if (!image)
|
|
+ return -EINVAL;
|
|
+
|
|
+ cmd = vmw_fifo_reserve(dev_priv, cmd_size);
|
|
+ if (unlikely(cmd == NULL)) {
|
|
+ DRM_ERROR("Fifo reserve failed.\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ memset(cmd, 0, sizeof(*cmd));
|
|
+
|
|
+ memcpy(&cmd[1], image, image_size);
|
|
+
|
|
+ cmd->cmd = cpu_to_le32(SVGA_CMD_DEFINE_ALPHA_CURSOR);
|
|
+ cmd->cursor.id = cpu_to_le32(0);
|
|
+ cmd->cursor.width = cpu_to_le32(width);
|
|
+ cmd->cursor.height = cpu_to_le32(height);
|
|
+ cmd->cursor.hotspotX = cpu_to_le32(hotspotX);
|
|
+ cmd->cursor.hotspotY = cpu_to_le32(hotspotY);
|
|
+
|
|
+ vmw_fifo_commit(dev_priv, cmd_size);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void vmw_cursor_update_position(struct vmw_private *dev_priv,
|
|
+ bool show, int x, int y)
|
|
+{
|
|
+ __le32 __iomem *fifo_mem = dev_priv->mmio_virt;
|
|
+ uint32_t count;
|
|
+
|
|
+ iowrite32(show ? 1 : 0, fifo_mem + SVGA_FIFO_CURSOR_ON);
|
|
+ iowrite32(x, fifo_mem + SVGA_FIFO_CURSOR_X);
|
|
+ iowrite32(y, fifo_mem + SVGA_FIFO_CURSOR_Y);
|
|
+ count = ioread32(fifo_mem + SVGA_FIFO_CURSOR_COUNT);
|
|
+ iowrite32(++count, fifo_mem + SVGA_FIFO_CURSOR_COUNT);
|
|
+}
|
|
+
|
|
+int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
|
|
+ uint32_t handle, uint32_t width, uint32_t height)
|
|
+{
|
|
+ struct vmw_private *dev_priv = vmw_priv(crtc->dev);
|
|
+ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
|
|
+ struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
|
|
+ struct vmw_surface *surface = NULL;
|
|
+ struct vmw_dma_buffer *dmabuf = NULL;
|
|
+ int ret;
|
|
+
|
|
+ if (handle) {
|
|
+ ret = vmw_user_surface_lookup_handle(dev_priv, tfile,
|
|
+ handle, &surface);
|
|
+ if (!ret) {
|
|
+ if (!surface->snooper.image) {
|
|
+ DRM_ERROR("surface not suitable for cursor\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ } else {
|
|
+ ret = vmw_user_dmabuf_lookup(tfile,
|
|
+ handle, &dmabuf);
|
|
+ if (ret) {
|
|
+ DRM_ERROR("failed to find surface or dmabuf: %i\n", ret);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* takedown old cursor */
|
|
+ if (du->cursor_surface) {
|
|
+ du->cursor_surface->snooper.crtc = NULL;
|
|
+ vmw_surface_unreference(&du->cursor_surface);
|
|
+ }
|
|
+ if (du->cursor_dmabuf)
|
|
+ vmw_dmabuf_unreference(&du->cursor_dmabuf);
|
|
+
|
|
+ /* setup new image */
|
|
+ if (surface) {
|
|
+ /* vmw_user_surface_lookup takes one reference */
|
|
+ du->cursor_surface = surface;
|
|
+
|
|
+ du->cursor_surface->snooper.crtc = crtc;
|
|
+ du->cursor_age = du->cursor_surface->snooper.age;
|
|
+ vmw_cursor_update_image(dev_priv, surface->snooper.image,
|
|
+ 64, 64, du->hotspot_x, du->hotspot_y);
|
|
+ } else if (dmabuf) {
|
|
+ struct ttm_bo_kmap_obj map;
|
|
+ unsigned long kmap_offset;
|
|
+ unsigned long kmap_num;
|
|
+ void *virtual;
|
|
+ bool dummy;
|
|
+
|
|
+ /* vmw_user_surface_lookup takes one reference */
|
|
+ du->cursor_dmabuf = dmabuf;
|
|
+
|
|
+ kmap_offset = 0;
|
|
+ kmap_num = (64*64*4) >> PAGE_SHIFT;
|
|
+
|
|
+ ret = ttm_bo_reserve(&dmabuf->base, true, false, false, 0);
|
|
+ if (unlikely(ret != 0)) {
|
|
+ DRM_ERROR("reserve failed\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ret = ttm_bo_kmap(&dmabuf->base, kmap_offset, kmap_num, &map);
|
|
+ if (unlikely(ret != 0))
|
|
+ goto err_unreserve;
|
|
+
|
|
+ virtual = ttm_kmap_obj_virtual(&map, &dummy);
|
|
+ vmw_cursor_update_image(dev_priv, virtual, 64, 64,
|
|
+ du->hotspot_x, du->hotspot_y);
|
|
+
|
|
+ ttm_bo_kunmap(&map);
|
|
+err_unreserve:
|
|
+ ttm_bo_unreserve(&dmabuf->base);
|
|
+
|
|
+ } else {
|
|
+ vmw_cursor_update_position(dev_priv, false, 0, 0);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ vmw_cursor_update_position(dev_priv, true, du->cursor_x, du->cursor_y);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
|
|
+{
|
|
+ struct vmw_private *dev_priv = vmw_priv(crtc->dev);
|
|
+ struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
|
|
+ bool shown = du->cursor_surface || du->cursor_dmabuf ? true : false;
|
|
+
|
|
+ du->cursor_x = x + crtc->x;
|
|
+ du->cursor_y = y + crtc->y;
|
|
+
|
|
+ vmw_cursor_update_position(dev_priv, shown,
|
|
+ du->cursor_x, du->cursor_y);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void vmw_kms_cursor_snoop(struct vmw_surface *srf,
|
|
+ struct ttm_object_file *tfile,
|
|
+ struct ttm_buffer_object *bo,
|
|
+ SVGA3dCmdHeader *header)
|
|
+{
|
|
+ struct ttm_bo_kmap_obj map;
|
|
+ unsigned long kmap_offset;
|
|
+ unsigned long kmap_num;
|
|
+ SVGA3dCopyBox *box;
|
|
+ unsigned box_count;
|
|
+ void *virtual;
|
|
+ bool dummy;
|
|
+ struct vmw_dma_cmd {
|
|
+ SVGA3dCmdHeader header;
|
|
+ SVGA3dCmdSurfaceDMA dma;
|
|
+ } *cmd;
|
|
+ int ret;
|
|
+
|
|
+ cmd = container_of(header, struct vmw_dma_cmd, header);
|
|
+
|
|
+ /* No snooper installed */
|
|
+ if (!srf->snooper.image)
|
|
+ return;
|
|
+
|
|
+ if (cmd->dma.host.face != 0 || cmd->dma.host.mipmap != 0) {
|
|
+ DRM_ERROR("face and mipmap for cursors should never != 0\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (cmd->header.size < 64) {
|
|
+ DRM_ERROR("at least one full copy box must be given\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ box = (SVGA3dCopyBox *)&cmd[1];
|
|
+ box_count = (cmd->header.size - sizeof(SVGA3dCmdSurfaceDMA)) /
|
|
+ sizeof(SVGA3dCopyBox);
|
|
+
|
|
+ if (cmd->dma.guest.pitch != (64 * 4) ||
|
|
+ cmd->dma.guest.ptr.offset % PAGE_SIZE ||
|
|
+ box->x != 0 || box->y != 0 || box->z != 0 ||
|
|
+ box->srcx != 0 || box->srcy != 0 || box->srcz != 0 ||
|
|
+ box->w != 64 || box->h != 64 || box->d != 1 ||
|
|
+ box_count != 1) {
|
|
+ /* TODO handle none page aligned offsets */
|
|
+ /* TODO handle partial uploads and pitch != 256 */
|
|
+ /* TODO handle more then one copy (size != 64) */
|
|
+ DRM_ERROR("lazy programer, cant handle wierd stuff\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ kmap_offset = cmd->dma.guest.ptr.offset >> PAGE_SHIFT;
|
|
+ kmap_num = (64*64*4) >> PAGE_SHIFT;
|
|
+
|
|
+ ret = ttm_bo_reserve(bo, true, false, false, 0);
|
|
+ if (unlikely(ret != 0)) {
|
|
+ DRM_ERROR("reserve failed\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ret = ttm_bo_kmap(bo, kmap_offset, kmap_num, &map);
|
|
+ if (unlikely(ret != 0))
|
|
+ goto err_unreserve;
|
|
+
|
|
+ virtual = ttm_kmap_obj_virtual(&map, &dummy);
|
|
+
|
|
+ memcpy(srf->snooper.image, virtual, 64*64*4);
|
|
+ srf->snooper.age++;
|
|
+
|
|
+ /* we can't call this function from this function since execbuf has
|
|
+ * reserved fifo space.
|
|
+ *
|
|
+ * if (srf->snooper.crtc)
|
|
+ * vmw_ldu_crtc_cursor_update_image(dev_priv,
|
|
+ * srf->snooper.image, 64, 64,
|
|
+ * du->hotspot_x, du->hotspot_y);
|
|
+ */
|
|
+
|
|
+ ttm_bo_kunmap(&map);
|
|
+err_unreserve:
|
|
+ ttm_bo_unreserve(bo);
|
|
+}
|
|
+
|
|
+void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv)
|
|
+{
|
|
+ struct drm_device *dev = dev_priv->dev;
|
|
+ struct vmw_display_unit *du;
|
|
+ struct drm_crtc *crtc;
|
|
+
|
|
+ mutex_lock(&dev->mode_config.mutex);
|
|
+
|
|
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
|
+ du = vmw_crtc_to_du(crtc);
|
|
+ if (!du->cursor_surface ||
|
|
+ du->cursor_age == du->cursor_surface->snooper.age)
|
|
+ continue;
|
|
+
|
|
+ du->cursor_age = du->cursor_surface->snooper.age;
|
|
+ vmw_cursor_update_image(dev_priv,
|
|
+ du->cursor_surface->snooper.image,
|
|
+ 64, 64, du->hotspot_x, du->hotspot_y);
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&dev->mode_config.mutex);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Generic framebuffer code
|
|
+ */
|
|
+
|
|
+int vmw_framebuffer_create_handle(struct drm_framebuffer *fb,
|
|
+ struct drm_file *file_priv,
|
|
+ unsigned int *handle)
|
|
+{
|
|
+ if (handle)
|
|
+ handle = 0;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Surface framebuffer code
|
|
+ */
|
|
+
|
|
+#define vmw_framebuffer_to_vfbs(x) \
|
|
+ container_of(x, struct vmw_framebuffer_surface, base.base)
|
|
+
|
|
+struct vmw_framebuffer_surface {
|
|
+ struct vmw_framebuffer base;
|
|
+ struct vmw_surface *surface;
|
|
+ struct delayed_work d_work;
|
|
+ struct mutex work_lock;
|
|
+ bool present_fs;
|
|
+};
|
|
+
|
|
+void vmw_framebuffer_surface_destroy(struct drm_framebuffer *framebuffer)
|
|
+{
|
|
+ struct vmw_framebuffer_surface *vfb =
|
|
+ vmw_framebuffer_to_vfbs(framebuffer);
|
|
+
|
|
+ cancel_delayed_work_sync(&vfb->d_work);
|
|
+ drm_framebuffer_cleanup(framebuffer);
|
|
+ vmw_surface_unreference(&vfb->surface);
|
|
+
|
|
+ kfree(framebuffer);
|
|
+}
|
|
+
|
|
+static void vmw_framebuffer_present_fs_callback(struct work_struct *work)
|
|
+{
|
|
+ struct delayed_work *d_work =
|
|
+ container_of(work, struct delayed_work, work);
|
|
+ struct vmw_framebuffer_surface *vfbs =
|
|
+ container_of(d_work, struct vmw_framebuffer_surface, d_work);
|
|
+ struct vmw_surface *surf = vfbs->surface;
|
|
+ struct drm_framebuffer *framebuffer = &vfbs->base.base;
|
|
+ struct vmw_private *dev_priv = vmw_priv(framebuffer->dev);
|
|
+
|
|
+ struct {
|
|
+ SVGA3dCmdHeader header;
|
|
+ SVGA3dCmdPresent body;
|
|
+ SVGA3dCopyRect cr;
|
|
+ } *cmd;
|
|
+
|
|
+ mutex_lock(&vfbs->work_lock);
|
|
+ if (!vfbs->present_fs)
|
|
+ goto out_unlock;
|
|
+
|
|
+ cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd));
|
|
+ if (unlikely(cmd == NULL))
|
|
+ goto out_resched;
|
|
+
|
|
+ cmd->header.id = cpu_to_le32(SVGA_3D_CMD_PRESENT);
|
|
+ cmd->header.size = cpu_to_le32(sizeof(cmd->body) + sizeof(cmd->cr));
|
|
+ cmd->body.sid = cpu_to_le32(surf->res.id);
|
|
+ cmd->cr.x = cpu_to_le32(0);
|
|
+ cmd->cr.y = cpu_to_le32(0);
|
|
+ cmd->cr.srcx = cmd->cr.x;
|
|
+ cmd->cr.srcy = cmd->cr.y;
|
|
+ cmd->cr.w = cpu_to_le32(framebuffer->width);
|
|
+ cmd->cr.h = cpu_to_le32(framebuffer->height);
|
|
+ vfbs->present_fs = false;
|
|
+ vmw_fifo_commit(dev_priv, sizeof(*cmd));
|
|
+out_resched:
|
|
+ /**
|
|
+ * Will not re-add if already pending.
|
|
+ */
|
|
+ schedule_delayed_work(&vfbs->d_work, VMWGFX_PRESENT_RATE);
|
|
+out_unlock:
|
|
+ mutex_unlock(&vfbs->work_lock);
|
|
+}
|
|
+
|
|
+
|
|
+int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,
|
|
+ unsigned flags, unsigned color,
|
|
+ struct drm_clip_rect *clips,
|
|
+ unsigned num_clips)
|
|
+{
|
|
+ struct vmw_private *dev_priv = vmw_priv(framebuffer->dev);
|
|
+ struct vmw_framebuffer_surface *vfbs =
|
|
+ vmw_framebuffer_to_vfbs(framebuffer);
|
|
+ struct vmw_surface *surf = vfbs->surface;
|
|
+ struct drm_clip_rect norect;
|
|
+ SVGA3dCopyRect *cr;
|
|
+ int i, inc = 1;
|
|
+
|
|
+ struct {
|
|
+ SVGA3dCmdHeader header;
|
|
+ SVGA3dCmdPresent body;
|
|
+ SVGA3dCopyRect cr;
|
|
+ } *cmd;
|
|
+
|
|
+ if (!num_clips ||
|
|
+ !(dev_priv->fifo.capabilities &
|
|
+ SVGA_FIFO_CAP_SCREEN_OBJECT)) {
|
|
+ int ret;
|
|
+
|
|
+ mutex_lock(&vfbs->work_lock);
|
|
+ vfbs->present_fs = true;
|
|
+ ret = schedule_delayed_work(&vfbs->d_work, VMWGFX_PRESENT_RATE);
|
|
+ mutex_unlock(&vfbs->work_lock);
|
|
+ if (ret) {
|
|
+ /**
|
|
+ * No work pending, Force immediate present.
|
|
+ */
|
|
+ vmw_framebuffer_present_fs_callback(&vfbs->d_work.work);
|
|
+ }
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (!num_clips) {
|
|
+ num_clips = 1;
|
|
+ clips = &norect;
|
|
+ norect.x1 = norect.y1 = 0;
|
|
+ norect.x2 = framebuffer->width;
|
|
+ norect.y2 = framebuffer->height;
|
|
+ } else if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) {
|
|
+ num_clips /= 2;
|
|
+ inc = 2; /* skip source rects */
|
|
+ }
|
|
+
|
|
+ cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd) + (num_clips - 1) * sizeof(cmd->cr));
|
|
+ if (unlikely(cmd == NULL)) {
|
|
+ DRM_ERROR("Fifo reserve failed.\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ memset(cmd, 0, sizeof(*cmd));
|
|
+
|
|
+ cmd->header.id = cpu_to_le32(SVGA_3D_CMD_PRESENT);
|
|
+ cmd->header.size = cpu_to_le32(sizeof(cmd->body) + num_clips * sizeof(cmd->cr));
|
|
+ cmd->body.sid = cpu_to_le32(surf->res.id);
|
|
+
|
|
+ for (i = 0, cr = &cmd->cr; i < num_clips; i++, cr++, clips += inc) {
|
|
+ cr->x = cpu_to_le16(clips->x1);
|
|
+ cr->y = cpu_to_le16(clips->y1);
|
|
+ cr->srcx = cr->x;
|
|
+ cr->srcy = cr->y;
|
|
+ cr->w = cpu_to_le16(clips->x2 - clips->x1);
|
|
+ cr->h = cpu_to_le16(clips->y2 - clips->y1);
|
|
+ }
|
|
+
|
|
+ vmw_fifo_commit(dev_priv, sizeof(*cmd) + (num_clips - 1) * sizeof(cmd->cr));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct drm_framebuffer_funcs vmw_framebuffer_surface_funcs = {
|
|
+ .destroy = vmw_framebuffer_surface_destroy,
|
|
+ .dirty = vmw_framebuffer_surface_dirty,
|
|
+ .create_handle = vmw_framebuffer_create_handle,
|
|
+};
|
|
+
|
|
+int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,
|
|
+ struct vmw_surface *surface,
|
|
+ struct vmw_framebuffer **out,
|
|
+ unsigned width, unsigned height)
|
|
+
|
|
+{
|
|
+ struct drm_device *dev = dev_priv->dev;
|
|
+ struct vmw_framebuffer_surface *vfbs;
|
|
+ int ret;
|
|
+
|
|
+ vfbs = kzalloc(sizeof(*vfbs), GFP_KERNEL);
|
|
+ if (!vfbs) {
|
|
+ ret = -ENOMEM;
|
|
+ goto out_err1;
|
|
+ }
|
|
+
|
|
+ ret = drm_framebuffer_init(dev, &vfbs->base.base,
|
|
+ &vmw_framebuffer_surface_funcs);
|
|
+ if (ret)
|
|
+ goto out_err2;
|
|
+
|
|
+ if (!vmw_surface_reference(surface)) {
|
|
+ DRM_ERROR("failed to reference surface %p\n", surface);
|
|
+ goto out_err3;
|
|
+ }
|
|
+
|
|
+ /* XXX get the first 3 from the surface info */
|
|
+ vfbs->base.base.bits_per_pixel = 32;
|
|
+ vfbs->base.base.pitch = width * 32 / 4;
|
|
+ vfbs->base.base.depth = 24;
|
|
+ vfbs->base.base.width = width;
|
|
+ vfbs->base.base.height = height;
|
|
+ vfbs->base.pin = NULL;
|
|
+ vfbs->base.unpin = NULL;
|
|
+ vfbs->surface = surface;
|
|
+ mutex_init(&vfbs->work_lock);
|
|
+ INIT_DELAYED_WORK(&vfbs->d_work, &vmw_framebuffer_present_fs_callback);
|
|
+ *out = &vfbs->base;
|
|
+
|
|
+ return 0;
|
|
+
|
|
+out_err3:
|
|
+ drm_framebuffer_cleanup(&vfbs->base.base);
|
|
+out_err2:
|
|
+ kfree(vfbs);
|
|
+out_err1:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Dmabuf framebuffer code
|
|
+ */
|
|
+
|
|
+#define vmw_framebuffer_to_vfbd(x) \
|
|
+ container_of(x, struct vmw_framebuffer_dmabuf, base.base)
|
|
+
|
|
+struct vmw_framebuffer_dmabuf {
|
|
+ struct vmw_framebuffer base;
|
|
+ struct vmw_dma_buffer *buffer;
|
|
+};
|
|
+
|
|
+void vmw_framebuffer_dmabuf_destroy(struct drm_framebuffer *framebuffer)
|
|
+{
|
|
+ struct vmw_framebuffer_dmabuf *vfbd =
|
|
+ vmw_framebuffer_to_vfbd(framebuffer);
|
|
+
|
|
+ drm_framebuffer_cleanup(framebuffer);
|
|
+ vmw_dmabuf_unreference(&vfbd->buffer);
|
|
+
|
|
+ kfree(vfbd);
|
|
+}
|
|
+
|
|
+int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer,
|
|
+ unsigned flags, unsigned color,
|
|
+ struct drm_clip_rect *clips,
|
|
+ unsigned num_clips)
|
|
+{
|
|
+ struct vmw_private *dev_priv = vmw_priv(framebuffer->dev);
|
|
+ struct drm_clip_rect norect;
|
|
+ struct {
|
|
+ uint32_t header;
|
|
+ SVGAFifoCmdUpdate body;
|
|
+ } *cmd;
|
|
+ int i, increment = 1;
|
|
+
|
|
+ if (!num_clips) {
|
|
+ num_clips = 1;
|
|
+ clips = &norect;
|
|
+ norect.x1 = norect.y1 = 0;
|
|
+ norect.x2 = framebuffer->width;
|
|
+ norect.y2 = framebuffer->height;
|
|
+ } else if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) {
|
|
+ num_clips /= 2;
|
|
+ increment = 2;
|
|
+ }
|
|
+
|
|
+ cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd) * num_clips);
|
|
+ if (unlikely(cmd == NULL)) {
|
|
+ DRM_ERROR("Fifo reserve failed.\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < num_clips; i++, clips += increment) {
|
|
+ cmd[i].header = cpu_to_le32(SVGA_CMD_UPDATE);
|
|
+ cmd[i].body.x = cpu_to_le32(clips->x1);
|
|
+ cmd[i].body.y = cpu_to_le32(clips->y1);
|
|
+ cmd[i].body.width = cpu_to_le32(clips->x2 - clips->x1);
|
|
+ cmd[i].body.height = cpu_to_le32(clips->y2 - clips->y1);
|
|
+ }
|
|
+
|
|
+ vmw_fifo_commit(dev_priv, sizeof(*cmd) * num_clips);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct drm_framebuffer_funcs vmw_framebuffer_dmabuf_funcs = {
|
|
+ .destroy = vmw_framebuffer_dmabuf_destroy,
|
|
+ .dirty = vmw_framebuffer_dmabuf_dirty,
|
|
+ .create_handle = vmw_framebuffer_create_handle,
|
|
+};
|
|
+
|
|
+static int vmw_framebuffer_dmabuf_pin(struct vmw_framebuffer *vfb)
|
|
+{
|
|
+ struct vmw_private *dev_priv = vmw_priv(vfb->base.dev);
|
|
+ struct vmw_framebuffer_dmabuf *vfbd =
|
|
+ vmw_framebuffer_to_vfbd(&vfb->base);
|
|
+ int ret;
|
|
+
|
|
+ vmw_overlay_pause_all(dev_priv);
|
|
+
|
|
+ ret = vmw_dmabuf_to_start_of_vram(dev_priv, vfbd->buffer);
|
|
+
|
|
+ if (dev_priv->capabilities & SVGA_CAP_MULTIMON) {
|
|
+ vmw_write(dev_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 1);
|
|
+ vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, 0);
|
|
+ vmw_write(dev_priv, SVGA_REG_DISPLAY_IS_PRIMARY, true);
|
|
+ vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_X, 0);
|
|
+ vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_Y, 0);
|
|
+ vmw_write(dev_priv, SVGA_REG_DISPLAY_WIDTH, 0);
|
|
+ vmw_write(dev_priv, SVGA_REG_DISPLAY_HEIGHT, 0);
|
|
+ vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID);
|
|
+
|
|
+ vmw_write(dev_priv, SVGA_REG_ENABLE, 1);
|
|
+ vmw_write(dev_priv, SVGA_REG_WIDTH, vfb->base.width);
|
|
+ vmw_write(dev_priv, SVGA_REG_HEIGHT, vfb->base.height);
|
|
+ vmw_write(dev_priv, SVGA_REG_BITS_PER_PIXEL, vfb->base.bits_per_pixel);
|
|
+ vmw_write(dev_priv, SVGA_REG_DEPTH, vfb->base.depth);
|
|
+ vmw_write(dev_priv, SVGA_REG_RED_MASK, 0x00ff0000);
|
|
+ vmw_write(dev_priv, SVGA_REG_GREEN_MASK, 0x0000ff00);
|
|
+ vmw_write(dev_priv, SVGA_REG_BLUE_MASK, 0x000000ff);
|
|
+ } else
|
|
+ WARN_ON(true);
|
|
+
|
|
+ vmw_overlay_resume_all(dev_priv);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vmw_framebuffer_dmabuf_unpin(struct vmw_framebuffer *vfb)
|
|
+{
|
|
+ struct vmw_private *dev_priv = vmw_priv(vfb->base.dev);
|
|
+ struct vmw_framebuffer_dmabuf *vfbd =
|
|
+ vmw_framebuffer_to_vfbd(&vfb->base);
|
|
+
|
|
+ if (!vfbd->buffer) {
|
|
+ WARN_ON(!vfbd->buffer);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return vmw_dmabuf_from_vram(dev_priv, vfbd->buffer);
|
|
+}
|
|
+
|
|
+int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv,
|
|
+ struct vmw_dma_buffer *dmabuf,
|
|
+ struct vmw_framebuffer **out,
|
|
+ unsigned width, unsigned height)
|
|
+
|
|
+{
|
|
+ struct drm_device *dev = dev_priv->dev;
|
|
+ struct vmw_framebuffer_dmabuf *vfbd;
|
|
+ int ret;
|
|
+
|
|
+ vfbd = kzalloc(sizeof(*vfbd), GFP_KERNEL);
|
|
+ if (!vfbd) {
|
|
+ ret = -ENOMEM;
|
|
+ goto out_err1;
|
|
+ }
|
|
+
|
|
+ ret = drm_framebuffer_init(dev, &vfbd->base.base,
|
|
+ &vmw_framebuffer_dmabuf_funcs);
|
|
+ if (ret)
|
|
+ goto out_err2;
|
|
+
|
|
+ if (!vmw_dmabuf_reference(dmabuf)) {
|
|
+ DRM_ERROR("failed to reference dmabuf %p\n", dmabuf);
|
|
+ goto out_err3;
|
|
+ }
|
|
+
|
|
+ /* XXX get the first 3 from the surface info */
|
|
+ vfbd->base.base.bits_per_pixel = 32;
|
|
+ vfbd->base.base.pitch = width * 32 / 4;
|
|
+ vfbd->base.base.depth = 24;
|
|
+ vfbd->base.base.width = width;
|
|
+ vfbd->base.base.height = height;
|
|
+ vfbd->base.pin = vmw_framebuffer_dmabuf_pin;
|
|
+ vfbd->base.unpin = vmw_framebuffer_dmabuf_unpin;
|
|
+ vfbd->buffer = dmabuf;
|
|
+ *out = &vfbd->base;
|
|
+
|
|
+ return 0;
|
|
+
|
|
+out_err3:
|
|
+ drm_framebuffer_cleanup(&vfbd->base.base);
|
|
+out_err2:
|
|
+ kfree(vfbd);
|
|
+out_err1:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Generic Kernel modesetting functions
|
|
+ */
|
|
+
|
|
+static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev,
|
|
+ struct drm_file *file_priv,
|
|
+ struct drm_mode_fb_cmd *mode_cmd)
|
|
+{
|
|
+ struct vmw_private *dev_priv = vmw_priv(dev);
|
|
+ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
|
|
+ struct vmw_framebuffer *vfb = NULL;
|
|
+ struct vmw_surface *surface = NULL;
|
|
+ struct vmw_dma_buffer *bo = NULL;
|
|
+ int ret;
|
|
+
|
|
+ ret = vmw_user_surface_lookup_handle(dev_priv, tfile,
|
|
+ mode_cmd->handle, &surface);
|
|
+ if (ret)
|
|
+ goto try_dmabuf;
|
|
+
|
|
+ if (!surface->scanout)
|
|
+ goto err_not_scanout;
|
|
+
|
|
+ ret = vmw_kms_new_framebuffer_surface(dev_priv, surface, &vfb,
|
|
+ mode_cmd->width, mode_cmd->height);
|
|
+
|
|
+ /* vmw_user_surface_lookup takes one ref so does new_fb */
|
|
+ vmw_surface_unreference(&surface);
|
|
+
|
|
+ if (ret) {
|
|
+ DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret);
|
|
+ return NULL;
|
|
+ }
|
|
+ return &vfb->base;
|
|
+
|
|
+try_dmabuf:
|
|
+ DRM_INFO("%s: trying buffer\n", __func__);
|
|
+
|
|
+ ret = vmw_user_dmabuf_lookup(tfile, mode_cmd->handle, &bo);
|
|
+ if (ret) {
|
|
+ DRM_ERROR("failed to find buffer: %i\n", ret);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ ret = vmw_kms_new_framebuffer_dmabuf(dev_priv, bo, &vfb,
|
|
+ mode_cmd->width, mode_cmd->height);
|
|
+
|
|
+ /* vmw_user_dmabuf_lookup takes one ref so does new_fb */
|
|
+ vmw_dmabuf_unreference(&bo);
|
|
+
|
|
+ if (ret) {
|
|
+ DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ return &vfb->base;
|
|
+
|
|
+err_not_scanout:
|
|
+ DRM_ERROR("surface not marked as scanout\n");
|
|
+ /* vmw_user_surface_lookup takes one ref */
|
|
+ vmw_surface_unreference(&surface);
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static int vmw_kms_fb_changed(struct drm_device *dev)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct drm_mode_config_funcs vmw_kms_funcs = {
|
|
+ .fb_create = vmw_kms_fb_create,
|
|
+ .fb_changed = vmw_kms_fb_changed,
|
|
+};
|
|
+
|
|
+int vmw_kms_init(struct vmw_private *dev_priv)
|
|
+{
|
|
+ struct drm_device *dev = dev_priv->dev;
|
|
+ int ret;
|
|
+
|
|
+ drm_mode_config_init(dev);
|
|
+ dev->mode_config.funcs = &vmw_kms_funcs;
|
|
+ dev->mode_config.min_width = 1;
|
|
+ dev->mode_config.min_height = 1;
|
|
+ dev->mode_config.max_width = dev_priv->fb_max_width;
|
|
+ dev->mode_config.max_height = dev_priv->fb_max_height;
|
|
+
|
|
+ ret = vmw_kms_init_legacy_display_system(dev_priv);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int vmw_kms_close(struct vmw_private *dev_priv)
|
|
+{
|
|
+ /*
|
|
+ * Docs says we should take the lock before calling this function
|
|
+ * but since it destroys encoders and our destructor calls
|
|
+ * drm_encoder_cleanup which takes the lock we deadlock.
|
|
+ */
|
|
+ drm_mode_config_cleanup(dev_priv->dev);
|
|
+ vmw_kms_close_legacy_display_system(dev_priv);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_vmw_cursor_bypass_arg *arg = data;
|
|
+ struct vmw_display_unit *du;
|
|
+ struct drm_mode_object *obj;
|
|
+ struct drm_crtc *crtc;
|
|
+ int ret = 0;
|
|
+
|
|
+
|
|
+ mutex_lock(&dev->mode_config.mutex);
|
|
+ if (arg->flags & DRM_VMW_CURSOR_BYPASS_ALL) {
|
|
+
|
|
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
|
+ du = vmw_crtc_to_du(crtc);
|
|
+ du->hotspot_x = arg->xhot;
|
|
+ du->hotspot_y = arg->yhot;
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&dev->mode_config.mutex);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ obj = drm_mode_object_find(dev, arg->crtc_id, DRM_MODE_OBJECT_CRTC);
|
|
+ if (!obj) {
|
|
+ ret = -EINVAL;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ crtc = obj_to_crtc(obj);
|
|
+ du = vmw_crtc_to_du(crtc);
|
|
+
|
|
+ du->hotspot_x = arg->xhot;
|
|
+ du->hotspot_y = arg->yhot;
|
|
+
|
|
+out:
|
|
+ mutex_unlock(&dev->mode_config.mutex);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int vmw_kms_save_vga(struct vmw_private *vmw_priv)
|
|
+{
|
|
+ /*
|
|
+ * setup a single multimon monitor with the size
|
|
+ * of 0x0, this stops the UI from resizing when we
|
|
+ * change the framebuffer size
|
|
+ */
|
|
+ if (vmw_priv->capabilities & SVGA_CAP_MULTIMON) {
|
|
+ vmw_write(vmw_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 1);
|
|
+ vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, 0);
|
|
+ vmw_write(vmw_priv, SVGA_REG_DISPLAY_IS_PRIMARY, true);
|
|
+ vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_X, 0);
|
|
+ vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_Y, 0);
|
|
+ vmw_write(vmw_priv, SVGA_REG_DISPLAY_WIDTH, 0);
|
|
+ vmw_write(vmw_priv, SVGA_REG_DISPLAY_HEIGHT, 0);
|
|
+ vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID);
|
|
+ }
|
|
+
|
|
+ vmw_priv->vga_width = vmw_read(vmw_priv, SVGA_REG_WIDTH);
|
|
+ vmw_priv->vga_height = vmw_read(vmw_priv, SVGA_REG_HEIGHT);
|
|
+ vmw_priv->vga_bpp = vmw_read(vmw_priv, SVGA_REG_BITS_PER_PIXEL);
|
|
+ vmw_priv->vga_depth = vmw_read(vmw_priv, SVGA_REG_DEPTH);
|
|
+ vmw_priv->vga_pseudo = vmw_read(vmw_priv, SVGA_REG_PSEUDOCOLOR);
|
|
+ vmw_priv->vga_red_mask = vmw_read(vmw_priv, SVGA_REG_RED_MASK);
|
|
+ vmw_priv->vga_green_mask = vmw_read(vmw_priv, SVGA_REG_GREEN_MASK);
|
|
+ vmw_priv->vga_blue_mask = vmw_read(vmw_priv, SVGA_REG_BLUE_MASK);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int vmw_kms_restore_vga(struct vmw_private *vmw_priv)
|
|
+{
|
|
+ vmw_write(vmw_priv, SVGA_REG_WIDTH, vmw_priv->vga_width);
|
|
+ vmw_write(vmw_priv, SVGA_REG_HEIGHT, vmw_priv->vga_height);
|
|
+ vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, vmw_priv->vga_bpp);
|
|
+ vmw_write(vmw_priv, SVGA_REG_DEPTH, vmw_priv->vga_depth);
|
|
+ vmw_write(vmw_priv, SVGA_REG_PSEUDOCOLOR, vmw_priv->vga_pseudo);
|
|
+ vmw_write(vmw_priv, SVGA_REG_RED_MASK, vmw_priv->vga_red_mask);
|
|
+ vmw_write(vmw_priv, SVGA_REG_GREEN_MASK, vmw_priv->vga_green_mask);
|
|
+ vmw_write(vmw_priv, SVGA_REG_BLUE_MASK, vmw_priv->vga_blue_mask);
|
|
+
|
|
+ /* TODO check for multimon */
|
|
+ vmw_write(vmw_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 0);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
|
|
new file mode 100644
|
|
index 0000000..8b95249
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
|
|
@@ -0,0 +1,102 @@
|
|
+/**************************************************************************
|
|
+ *
|
|
+ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sub license, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial portions
|
|
+ * of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ **************************************************************************/
|
|
+
|
|
+#ifndef VMWGFX_KMS_H_
|
|
+#define VMWGFX_KMS_H_
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "vmwgfx_drv.h"
|
|
+
|
|
+
|
|
+#define vmw_framebuffer_to_vfb(x) \
|
|
+ container_of(x, struct vmw_framebuffer, base)
|
|
+
|
|
+/**
|
|
+ * Base class for framebuffers
|
|
+ *
|
|
+ * @pin is called the when ever a crtc uses this framebuffer
|
|
+ * @unpin is called
|
|
+ */
|
|
+struct vmw_framebuffer {
|
|
+ struct drm_framebuffer base;
|
|
+ int (*pin)(struct vmw_framebuffer *fb);
|
|
+ int (*unpin)(struct vmw_framebuffer *fb);
|
|
+};
|
|
+
|
|
+
|
|
+#define vmw_crtc_to_du(x) \
|
|
+ container_of(x, struct vmw_display_unit, crtc)
|
|
+
|
|
+/*
|
|
+ * Basic cursor manipulation
|
|
+ */
|
|
+int vmw_cursor_update_image(struct vmw_private *dev_priv,
|
|
+ u32 *image, u32 width, u32 height,
|
|
+ u32 hotspotX, u32 hotspotY);
|
|
+void vmw_cursor_update_position(struct vmw_private *dev_priv,
|
|
+ bool show, int x, int y);
|
|
+
|
|
+/**
|
|
+ * Base class display unit.
|
|
+ *
|
|
+ * Since the SVGA hw doesn't have a concept of a crtc, encoder or connector
|
|
+ * so the display unit is all of them at the same time. This is true for both
|
|
+ * legacy multimon and screen objects.
|
|
+ */
|
|
+struct vmw_display_unit {
|
|
+ struct drm_crtc crtc;
|
|
+ struct drm_encoder encoder;
|
|
+ struct drm_connector connector;
|
|
+
|
|
+ struct vmw_surface *cursor_surface;
|
|
+ struct vmw_dma_buffer *cursor_dmabuf;
|
|
+ size_t cursor_age;
|
|
+
|
|
+ int cursor_x;
|
|
+ int cursor_y;
|
|
+
|
|
+ int hotspot_x;
|
|
+ int hotspot_y;
|
|
+
|
|
+ unsigned unit;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Shared display unit functions - vmwgfx_kms.c
|
|
+ */
|
|
+void vmw_display_unit_cleanup(struct vmw_display_unit *du);
|
|
+int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
|
|
+ uint32_t handle, uint32_t width, uint32_t height);
|
|
+int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y);
|
|
+
|
|
+/*
|
|
+ * Legacy display unit functions - vmwgfx_ldu.h
|
|
+ */
|
|
+int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv);
|
|
+int vmw_kms_close_legacy_display_system(struct vmw_private *dev_priv);
|
|
+
|
|
+#endif
|
|
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
|
|
new file mode 100644
|
|
index 0000000..9089159
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
|
|
@@ -0,0 +1,516 @@
|
|
+/**************************************************************************
|
|
+ *
|
|
+ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sub license, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial portions
|
|
+ * of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ **************************************************************************/
|
|
+
|
|
+#include "vmwgfx_kms.h"
|
|
+
|
|
+#define vmw_crtc_to_ldu(x) \
|
|
+ container_of(x, struct vmw_legacy_display_unit, base.crtc)
|
|
+#define vmw_encoder_to_ldu(x) \
|
|
+ container_of(x, struct vmw_legacy_display_unit, base.encoder)
|
|
+#define vmw_connector_to_ldu(x) \
|
|
+ container_of(x, struct vmw_legacy_display_unit, base.connector)
|
|
+
|
|
+struct vmw_legacy_display {
|
|
+ struct list_head active;
|
|
+
|
|
+ unsigned num_active;
|
|
+
|
|
+ struct vmw_framebuffer *fb;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * Display unit using the legacy register interface.
|
|
+ */
|
|
+struct vmw_legacy_display_unit {
|
|
+ struct vmw_display_unit base;
|
|
+
|
|
+ struct list_head active;
|
|
+
|
|
+ unsigned unit;
|
|
+};
|
|
+
|
|
+static void vmw_ldu_destroy(struct vmw_legacy_display_unit *ldu)
|
|
+{
|
|
+ list_del_init(&ldu->active);
|
|
+ vmw_display_unit_cleanup(&ldu->base);
|
|
+ kfree(ldu);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * Legacy Display Unit CRTC functions
|
|
+ */
|
|
+
|
|
+static void vmw_ldu_crtc_save(struct drm_crtc *crtc)
|
|
+{
|
|
+}
|
|
+
|
|
+static void vmw_ldu_crtc_restore(struct drm_crtc *crtc)
|
|
+{
|
|
+}
|
|
+
|
|
+static void vmw_ldu_crtc_gamma_set(struct drm_crtc *crtc,
|
|
+ u16 *r, u16 *g, u16 *b,
|
|
+ uint32_t size)
|
|
+{
|
|
+}
|
|
+
|
|
+static void vmw_ldu_crtc_destroy(struct drm_crtc *crtc)
|
|
+{
|
|
+ vmw_ldu_destroy(vmw_crtc_to_ldu(crtc));
|
|
+}
|
|
+
|
|
+static int vmw_ldu_commit_list(struct vmw_private *dev_priv)
|
|
+{
|
|
+ struct vmw_legacy_display *lds = dev_priv->ldu_priv;
|
|
+ struct vmw_legacy_display_unit *entry;
|
|
+ struct drm_crtc *crtc;
|
|
+ int i = 0;
|
|
+
|
|
+ /* to stop the screen from changing size on resize */
|
|
+ vmw_write(dev_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 0);
|
|
+ for (i = 0; i < lds->num_active; i++) {
|
|
+ vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, i);
|
|
+ vmw_write(dev_priv, SVGA_REG_DISPLAY_IS_PRIMARY, !i);
|
|
+ vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_X, 0);
|
|
+ vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_Y, 0);
|
|
+ vmw_write(dev_priv, SVGA_REG_DISPLAY_WIDTH, 0);
|
|
+ vmw_write(dev_priv, SVGA_REG_DISPLAY_HEIGHT, 0);
|
|
+ vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID);
|
|
+ }
|
|
+
|
|
+ /* Now set the mode */
|
|
+ vmw_write(dev_priv, SVGA_REG_NUM_GUEST_DISPLAYS, lds->num_active);
|
|
+ i = 0;
|
|
+ list_for_each_entry(entry, &lds->active, active) {
|
|
+ crtc = &entry->base.crtc;
|
|
+
|
|
+ vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, i);
|
|
+ vmw_write(dev_priv, SVGA_REG_DISPLAY_IS_PRIMARY, !i);
|
|
+ vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_X, crtc->x);
|
|
+ vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_Y, crtc->y);
|
|
+ vmw_write(dev_priv, SVGA_REG_DISPLAY_WIDTH, crtc->mode.hdisplay);
|
|
+ vmw_write(dev_priv, SVGA_REG_DISPLAY_HEIGHT, crtc->mode.vdisplay);
|
|
+ vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID);
|
|
+
|
|
+ i++;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vmw_ldu_del_active(struct vmw_private *vmw_priv,
|
|
+ struct vmw_legacy_display_unit *ldu)
|
|
+{
|
|
+ struct vmw_legacy_display *ld = vmw_priv->ldu_priv;
|
|
+ if (list_empty(&ldu->active))
|
|
+ return 0;
|
|
+
|
|
+ list_del_init(&ldu->active);
|
|
+ if (--(ld->num_active) == 0) {
|
|
+ BUG_ON(!ld->fb);
|
|
+ if (ld->fb->unpin)
|
|
+ ld->fb->unpin(ld->fb);
|
|
+ ld->fb = NULL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vmw_ldu_add_active(struct vmw_private *vmw_priv,
|
|
+ struct vmw_legacy_display_unit *ldu,
|
|
+ struct vmw_framebuffer *vfb)
|
|
+{
|
|
+ struct vmw_legacy_display *ld = vmw_priv->ldu_priv;
|
|
+ struct vmw_legacy_display_unit *entry;
|
|
+ struct list_head *at;
|
|
+
|
|
+ if (!list_empty(&ldu->active))
|
|
+ return 0;
|
|
+
|
|
+ at = &ld->active;
|
|
+ list_for_each_entry(entry, &ld->active, active) {
|
|
+ if (entry->unit > ldu->unit)
|
|
+ break;
|
|
+
|
|
+ at = &entry->active;
|
|
+ }
|
|
+
|
|
+ list_add(&ldu->active, at);
|
|
+ if (ld->num_active++ == 0) {
|
|
+ BUG_ON(ld->fb);
|
|
+ if (vfb->pin)
|
|
+ vfb->pin(vfb);
|
|
+ ld->fb = vfb;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vmw_ldu_crtc_set_config(struct drm_mode_set *set)
|
|
+{
|
|
+ struct vmw_private *dev_priv;
|
|
+ struct vmw_legacy_display_unit *ldu;
|
|
+ struct drm_connector *connector;
|
|
+ struct drm_display_mode *mode;
|
|
+ struct drm_encoder *encoder;
|
|
+ struct vmw_framebuffer *vfb;
|
|
+ struct drm_framebuffer *fb;
|
|
+ struct drm_crtc *crtc;
|
|
+
|
|
+ if (!set)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (!set->crtc)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* get the ldu */
|
|
+ crtc = set->crtc;
|
|
+ ldu = vmw_crtc_to_ldu(crtc);
|
|
+ vfb = set->fb ? vmw_framebuffer_to_vfb(set->fb) : NULL;
|
|
+ dev_priv = vmw_priv(crtc->dev);
|
|
+
|
|
+ if (set->num_connectors > 1) {
|
|
+ DRM_ERROR("to many connectors\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (set->num_connectors == 1 &&
|
|
+ set->connectors[0] != &ldu->base.connector) {
|
|
+ DRM_ERROR("connector doesn't match %p %p\n",
|
|
+ set->connectors[0], &ldu->base.connector);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* ldu only supports one fb active at the time */
|
|
+ if (dev_priv->ldu_priv->fb && vfb &&
|
|
+ dev_priv->ldu_priv->fb != vfb) {
|
|
+ DRM_ERROR("Multiple framebuffers not supported\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* since they always map one to one these are safe */
|
|
+ connector = &ldu->base.connector;
|
|
+ encoder = &ldu->base.encoder;
|
|
+
|
|
+ /* should we turn the crtc off? */
|
|
+ if (set->num_connectors == 0 || !set->mode || !set->fb) {
|
|
+
|
|
+ connector->encoder = NULL;
|
|
+ encoder->crtc = NULL;
|
|
+ crtc->fb = NULL;
|
|
+
|
|
+ vmw_ldu_del_active(dev_priv, ldu);
|
|
+
|
|
+ vmw_ldu_commit_list(dev_priv);
|
|
+
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+
|
|
+ /* we now know we want to set a mode */
|
|
+ mode = set->mode;
|
|
+ fb = set->fb;
|
|
+
|
|
+ if (set->x + mode->hdisplay > fb->width ||
|
|
+ set->y + mode->vdisplay > fb->height) {
|
|
+ DRM_ERROR("set outside of framebuffer\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ vmw_fb_off(dev_priv);
|
|
+
|
|
+ crtc->fb = fb;
|
|
+ encoder->crtc = crtc;
|
|
+ connector->encoder = encoder;
|
|
+ crtc->x = set->x;
|
|
+ crtc->y = set->y;
|
|
+ crtc->mode = *mode;
|
|
+
|
|
+ vmw_ldu_add_active(dev_priv, ldu, vfb);
|
|
+
|
|
+ vmw_ldu_commit_list(dev_priv);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct drm_crtc_funcs vmw_legacy_crtc_funcs = {
|
|
+ .save = vmw_ldu_crtc_save,
|
|
+ .restore = vmw_ldu_crtc_restore,
|
|
+ .cursor_set = vmw_du_crtc_cursor_set,
|
|
+ .cursor_move = vmw_du_crtc_cursor_move,
|
|
+ .gamma_set = vmw_ldu_crtc_gamma_set,
|
|
+ .destroy = vmw_ldu_crtc_destroy,
|
|
+ .set_config = vmw_ldu_crtc_set_config,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Legacy Display Unit encoder functions
|
|
+ */
|
|
+
|
|
+static void vmw_ldu_encoder_destroy(struct drm_encoder *encoder)
|
|
+{
|
|
+ vmw_ldu_destroy(vmw_encoder_to_ldu(encoder));
|
|
+}
|
|
+
|
|
+static struct drm_encoder_funcs vmw_legacy_encoder_funcs = {
|
|
+ .destroy = vmw_ldu_encoder_destroy,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Legacy Display Unit connector functions
|
|
+ */
|
|
+
|
|
+static void vmw_ldu_connector_dpms(struct drm_connector *connector, int mode)
|
|
+{
|
|
+}
|
|
+
|
|
+static void vmw_ldu_connector_save(struct drm_connector *connector)
|
|
+{
|
|
+}
|
|
+
|
|
+static void vmw_ldu_connector_restore(struct drm_connector *connector)
|
|
+{
|
|
+}
|
|
+
|
|
+static enum drm_connector_status
|
|
+ vmw_ldu_connector_detect(struct drm_connector *connector)
|
|
+{
|
|
+ /* XXX vmwctrl should control connection status */
|
|
+ if (vmw_connector_to_ldu(connector)->base.unit == 0)
|
|
+ return connector_status_connected;
|
|
+ return connector_status_disconnected;
|
|
+}
|
|
+
|
|
+static struct drm_display_mode vmw_ldu_connector_builtin[] = {
|
|
+ /* 640x480@60Hz */
|
|
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
|
|
+ 752, 800, 0, 480, 489, 492, 525, 0,
|
|
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
|
+ /* 800x600@60Hz */
|
|
+ { DRM_MODE("800x600",
|
|
+ DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
|
|
+ 40000, 800, 840, 968, 1056, 0, 600, 601, 605, 628,
|
|
+ 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
+ /* 1024x768@60Hz */
|
|
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
|
|
+ 1184, 1344, 0, 768, 771, 777, 806, 0,
|
|
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
|
+ /* 1152x864@75Hz */
|
|
+ { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
|
|
+ 1344, 1600, 0, 864, 865, 868, 900, 0,
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
+ /* 1280x768@60Hz */
|
|
+ { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344,
|
|
+ 1472, 1664, 0, 768, 771, 778, 798, 0,
|
|
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
+ /* 1280x800@60Hz */
|
|
+ { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352,
|
|
+ 1480, 1680, 0, 800, 803, 809, 831, 0,
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
|
+ /* 1280x960@60Hz */
|
|
+ { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376,
|
|
+ 1488, 1800, 0, 960, 961, 964, 1000, 0,
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
+ /* 1280x1024@60Hz */
|
|
+ { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328,
|
|
+ 1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
+ /* 1360x768@60Hz */
|
|
+ { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424,
|
|
+ 1536, 1792, 0, 768, 771, 777, 795, 0,
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
+ /* 1440x1050@60Hz */
|
|
+ { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488,
|
|
+ 1632, 1864, 0, 1050, 1053, 1057, 1089, 0,
|
|
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
+ /* 1440x900@60Hz */
|
|
+ { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520,
|
|
+ 1672, 1904, 0, 900, 903, 909, 934, 0,
|
|
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
+ /* 1600x1200@60Hz */
|
|
+ { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664,
|
|
+ 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
+ /* 1680x1050@60Hz */
|
|
+ { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784,
|
|
+ 1960, 2240, 0, 1050, 1053, 1059, 1089, 0,
|
|
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
+ /* 1792x1344@60Hz */
|
|
+ { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920,
|
|
+ 2120, 2448, 0, 1344, 1345, 1348, 1394, 0,
|
|
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
+ /* 1853x1392@60Hz */
|
|
+ { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952,
|
|
+ 2176, 2528, 0, 1392, 1393, 1396, 1439, 0,
|
|
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
+ /* 1920x1200@60Hz */
|
|
+ { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056,
|
|
+ 2256, 2592, 0, 1200, 1203, 1209, 1245, 0,
|
|
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
+ /* 1920x1440@60Hz */
|
|
+ { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048,
|
|
+ 2256, 2600, 0, 1440, 1441, 1444, 1500, 0,
|
|
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
+ /* 2560x1600@60Hz */
|
|
+ { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752,
|
|
+ 3032, 3504, 0, 1600, 1603, 1609, 1658, 0,
|
|
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
+ /* Terminate */
|
|
+ { DRM_MODE("", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) },
|
|
+};
|
|
+
|
|
+static int vmw_ldu_connector_fill_modes(struct drm_connector *connector,
|
|
+ uint32_t max_width, uint32_t max_height)
|
|
+{
|
|
+ struct drm_device *dev = connector->dev;
|
|
+ struct drm_display_mode *mode = NULL;
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; vmw_ldu_connector_builtin[i].type != 0; i++) {
|
|
+ if (vmw_ldu_connector_builtin[i].hdisplay > max_width ||
|
|
+ vmw_ldu_connector_builtin[i].vdisplay > max_height)
|
|
+ continue;
|
|
+
|
|
+ mode = drm_mode_duplicate(dev, &vmw_ldu_connector_builtin[i]);
|
|
+ if (!mode)
|
|
+ return 0;
|
|
+ mode->vrefresh = drm_mode_vrefresh(mode);
|
|
+
|
|
+ drm_mode_probed_add(connector, mode);
|
|
+ }
|
|
+
|
|
+ drm_mode_connector_list_update(connector);
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int vmw_ldu_connector_set_property(struct drm_connector *connector,
|
|
+ struct drm_property *property,
|
|
+ uint64_t val)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void vmw_ldu_connector_destroy(struct drm_connector *connector)
|
|
+{
|
|
+ vmw_ldu_destroy(vmw_connector_to_ldu(connector));
|
|
+}
|
|
+
|
|
+static struct drm_connector_funcs vmw_legacy_connector_funcs = {
|
|
+ .dpms = vmw_ldu_connector_dpms,
|
|
+ .save = vmw_ldu_connector_save,
|
|
+ .restore = vmw_ldu_connector_restore,
|
|
+ .detect = vmw_ldu_connector_detect,
|
|
+ .fill_modes = vmw_ldu_connector_fill_modes,
|
|
+ .set_property = vmw_ldu_connector_set_property,
|
|
+ .destroy = vmw_ldu_connector_destroy,
|
|
+};
|
|
+
|
|
+static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)
|
|
+{
|
|
+ struct vmw_legacy_display_unit *ldu;
|
|
+ struct drm_device *dev = dev_priv->dev;
|
|
+ struct drm_connector *connector;
|
|
+ struct drm_encoder *encoder;
|
|
+ struct drm_crtc *crtc;
|
|
+
|
|
+ ldu = kzalloc(sizeof(*ldu), GFP_KERNEL);
|
|
+ if (!ldu)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ ldu->unit = unit;
|
|
+ crtc = &ldu->base.crtc;
|
|
+ encoder = &ldu->base.encoder;
|
|
+ connector = &ldu->base.connector;
|
|
+
|
|
+ drm_connector_init(dev, connector, &vmw_legacy_connector_funcs,
|
|
+ DRM_MODE_CONNECTOR_LVDS);
|
|
+ /* Initial status */
|
|
+ if (unit == 0)
|
|
+ connector->status = connector_status_connected;
|
|
+ else
|
|
+ connector->status = connector_status_disconnected;
|
|
+
|
|
+ drm_encoder_init(dev, encoder, &vmw_legacy_encoder_funcs,
|
|
+ DRM_MODE_ENCODER_LVDS);
|
|
+ drm_mode_connector_attach_encoder(connector, encoder);
|
|
+ encoder->possible_crtcs = (1 << unit);
|
|
+ encoder->possible_clones = 0;
|
|
+
|
|
+ INIT_LIST_HEAD(&ldu->active);
|
|
+
|
|
+ drm_crtc_init(dev, crtc, &vmw_legacy_crtc_funcs);
|
|
+
|
|
+ drm_connector_attach_property(connector,
|
|
+ dev->mode_config.dirty_info_property,
|
|
+ 1);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv)
|
|
+{
|
|
+ if (dev_priv->ldu_priv) {
|
|
+ DRM_INFO("ldu system already on\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ dev_priv->ldu_priv = kmalloc(GFP_KERNEL, sizeof(*dev_priv->ldu_priv));
|
|
+
|
|
+ if (!dev_priv->ldu_priv)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ INIT_LIST_HEAD(&dev_priv->ldu_priv->active);
|
|
+ dev_priv->ldu_priv->num_active = 0;
|
|
+ dev_priv->ldu_priv->fb = NULL;
|
|
+
|
|
+ drm_mode_create_dirty_info_property(dev_priv->dev);
|
|
+
|
|
+ vmw_ldu_init(dev_priv, 0);
|
|
+ vmw_ldu_init(dev_priv, 1);
|
|
+ vmw_ldu_init(dev_priv, 2);
|
|
+ vmw_ldu_init(dev_priv, 3);
|
|
+ vmw_ldu_init(dev_priv, 4);
|
|
+ vmw_ldu_init(dev_priv, 5);
|
|
+ vmw_ldu_init(dev_priv, 6);
|
|
+ vmw_ldu_init(dev_priv, 7);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int vmw_kms_close_legacy_display_system(struct vmw_private *dev_priv)
|
|
+{
|
|
+ if (!dev_priv->ldu_priv)
|
|
+ return -ENOSYS;
|
|
+
|
|
+ BUG_ON(!list_empty(&dev_priv->ldu_priv->active));
|
|
+
|
|
+ kfree(dev_priv->ldu_priv);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c b/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c
|
|
new file mode 100644
|
|
index 0000000..5b6eabe
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c
|
|
@@ -0,0 +1,625 @@
|
|
+/**************************************************************************
|
|
+ *
|
|
+ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sub license, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial portions
|
|
+ * of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ **************************************************************************/
|
|
+
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "vmwgfx_drv.h"
|
|
+
|
|
+#include "ttm/ttm_placement.h"
|
|
+
|
|
+#include "svga_overlay.h"
|
|
+#include "svga_escape.h"
|
|
+
|
|
+#define VMW_MAX_NUM_STREAMS 1
|
|
+
|
|
+struct vmw_stream {
|
|
+ struct vmw_dma_buffer *buf;
|
|
+ bool claimed;
|
|
+ bool paused;
|
|
+ struct drm_vmw_control_stream_arg saved;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * Overlay control
|
|
+ */
|
|
+struct vmw_overlay {
|
|
+ /*
|
|
+ * Each stream is a single overlay. In Xv these are called ports.
|
|
+ */
|
|
+ struct mutex mutex;
|
|
+ struct vmw_stream stream[VMW_MAX_NUM_STREAMS];
|
|
+};
|
|
+
|
|
+static inline struct vmw_overlay *vmw_overlay(struct drm_device *dev)
|
|
+{
|
|
+ struct vmw_private *dev_priv = vmw_priv(dev);
|
|
+ return dev_priv ? dev_priv->overlay_priv : NULL;
|
|
+}
|
|
+
|
|
+struct vmw_escape_header {
|
|
+ uint32_t cmd;
|
|
+ SVGAFifoCmdEscape body;
|
|
+};
|
|
+
|
|
+struct vmw_escape_video_flush {
|
|
+ struct vmw_escape_header escape;
|
|
+ SVGAEscapeVideoFlush flush;
|
|
+};
|
|
+
|
|
+static inline void fill_escape(struct vmw_escape_header *header,
|
|
+ uint32_t size)
|
|
+{
|
|
+ header->cmd = SVGA_CMD_ESCAPE;
|
|
+ header->body.nsid = SVGA_ESCAPE_NSID_VMWARE;
|
|
+ header->body.size = size;
|
|
+}
|
|
+
|
|
+static inline void fill_flush(struct vmw_escape_video_flush *cmd,
|
|
+ uint32_t stream_id)
|
|
+{
|
|
+ fill_escape(&cmd->escape, sizeof(cmd->flush));
|
|
+ cmd->flush.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_FLUSH;
|
|
+ cmd->flush.streamId = stream_id;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Pin or unpin a buffer in vram.
|
|
+ *
|
|
+ * @dev_priv: Driver private.
|
|
+ * @buf: DMA buffer to pin or unpin.
|
|
+ * @pin: Pin buffer in vram if true.
|
|
+ * @interruptible: Use interruptible wait.
|
|
+ *
|
|
+ * Takes the current masters ttm lock in read.
|
|
+ *
|
|
+ * Returns
|
|
+ * -ERESTARTSYS if interrupted by a signal.
|
|
+ */
|
|
+static int vmw_dmabuf_pin_in_vram(struct vmw_private *dev_priv,
|
|
+ struct vmw_dma_buffer *buf,
|
|
+ bool pin, bool interruptible)
|
|
+{
|
|
+ struct ttm_buffer_object *bo = &buf->base;
|
|
+ struct ttm_placement *overlay_placement = &vmw_vram_placement;
|
|
+ int ret;
|
|
+
|
|
+ ret = ttm_read_lock(&dev_priv->active_master->lock, interruptible);
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+
|
|
+ ret = ttm_bo_reserve(bo, interruptible, false, false, 0);
|
|
+ if (unlikely(ret != 0))
|
|
+ goto err;
|
|
+
|
|
+ if (pin)
|
|
+ overlay_placement = &vmw_vram_ne_placement;
|
|
+
|
|
+ ret = ttm_bo_validate(bo, overlay_placement, interruptible, false);
|
|
+
|
|
+ ttm_bo_unreserve(bo);
|
|
+
|
|
+err:
|
|
+ ttm_read_unlock(&dev_priv->active_master->lock);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Send put command to hw.
|
|
+ *
|
|
+ * Returns
|
|
+ * -ERESTARTSYS if interrupted by a signal.
|
|
+ */
|
|
+static int vmw_overlay_send_put(struct vmw_private *dev_priv,
|
|
+ struct vmw_dma_buffer *buf,
|
|
+ struct drm_vmw_control_stream_arg *arg,
|
|
+ bool interruptible)
|
|
+{
|
|
+ struct {
|
|
+ struct vmw_escape_header escape;
|
|
+ struct {
|
|
+ struct {
|
|
+ uint32_t cmdType;
|
|
+ uint32_t streamId;
|
|
+ } header;
|
|
+ struct {
|
|
+ uint32_t registerId;
|
|
+ uint32_t value;
|
|
+ } items[SVGA_VIDEO_PITCH_3 + 1];
|
|
+ } body;
|
|
+ struct vmw_escape_video_flush flush;
|
|
+ } *cmds;
|
|
+ uint32_t offset;
|
|
+ int i, ret;
|
|
+
|
|
+ for (;;) {
|
|
+ cmds = vmw_fifo_reserve(dev_priv, sizeof(*cmds));
|
|
+ if (cmds)
|
|
+ break;
|
|
+
|
|
+ ret = vmw_fallback_wait(dev_priv, false, true, 0,
|
|
+ interruptible, 3*HZ);
|
|
+ if (interruptible && ret == -ERESTARTSYS)
|
|
+ return ret;
|
|
+ else
|
|
+ BUG_ON(ret != 0);
|
|
+ }
|
|
+
|
|
+ fill_escape(&cmds->escape, sizeof(cmds->body));
|
|
+ cmds->body.header.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS;
|
|
+ cmds->body.header.streamId = arg->stream_id;
|
|
+
|
|
+ for (i = 0; i <= SVGA_VIDEO_PITCH_3; i++)
|
|
+ cmds->body.items[i].registerId = i;
|
|
+
|
|
+ offset = buf->base.offset + arg->offset;
|
|
+
|
|
+ cmds->body.items[SVGA_VIDEO_ENABLED].value = true;
|
|
+ cmds->body.items[SVGA_VIDEO_FLAGS].value = arg->flags;
|
|
+ cmds->body.items[SVGA_VIDEO_DATA_OFFSET].value = offset;
|
|
+ cmds->body.items[SVGA_VIDEO_FORMAT].value = arg->format;
|
|
+ cmds->body.items[SVGA_VIDEO_COLORKEY].value = arg->color_key;
|
|
+ cmds->body.items[SVGA_VIDEO_SIZE].value = arg->size;
|
|
+ cmds->body.items[SVGA_VIDEO_WIDTH].value = arg->width;
|
|
+ cmds->body.items[SVGA_VIDEO_HEIGHT].value = arg->height;
|
|
+ cmds->body.items[SVGA_VIDEO_SRC_X].value = arg->src.x;
|
|
+ cmds->body.items[SVGA_VIDEO_SRC_Y].value = arg->src.y;
|
|
+ cmds->body.items[SVGA_VIDEO_SRC_WIDTH].value = arg->src.w;
|
|
+ cmds->body.items[SVGA_VIDEO_SRC_HEIGHT].value = arg->src.h;
|
|
+ cmds->body.items[SVGA_VIDEO_DST_X].value = arg->dst.x;
|
|
+ cmds->body.items[SVGA_VIDEO_DST_Y].value = arg->dst.y;
|
|
+ cmds->body.items[SVGA_VIDEO_DST_WIDTH].value = arg->dst.w;
|
|
+ cmds->body.items[SVGA_VIDEO_DST_HEIGHT].value = arg->dst.h;
|
|
+ cmds->body.items[SVGA_VIDEO_PITCH_1].value = arg->pitch[0];
|
|
+ cmds->body.items[SVGA_VIDEO_PITCH_2].value = arg->pitch[1];
|
|
+ cmds->body.items[SVGA_VIDEO_PITCH_3].value = arg->pitch[2];
|
|
+
|
|
+ fill_flush(&cmds->flush, arg->stream_id);
|
|
+
|
|
+ vmw_fifo_commit(dev_priv, sizeof(*cmds));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Send stop command to hw.
|
|
+ *
|
|
+ * Returns
|
|
+ * -ERESTARTSYS if interrupted by a signal.
|
|
+ */
|
|
+static int vmw_overlay_send_stop(struct vmw_private *dev_priv,
|
|
+ uint32_t stream_id,
|
|
+ bool interruptible)
|
|
+{
|
|
+ struct {
|
|
+ struct vmw_escape_header escape;
|
|
+ SVGAEscapeVideoSetRegs body;
|
|
+ struct vmw_escape_video_flush flush;
|
|
+ } *cmds;
|
|
+ int ret;
|
|
+
|
|
+ for (;;) {
|
|
+ cmds = vmw_fifo_reserve(dev_priv, sizeof(*cmds));
|
|
+ if (cmds)
|
|
+ break;
|
|
+
|
|
+ ret = vmw_fallback_wait(dev_priv, false, true, 0,
|
|
+ interruptible, 3*HZ);
|
|
+ if (interruptible && ret == -ERESTARTSYS)
|
|
+ return ret;
|
|
+ else
|
|
+ BUG_ON(ret != 0);
|
|
+ }
|
|
+
|
|
+ fill_escape(&cmds->escape, sizeof(cmds->body));
|
|
+ cmds->body.header.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS;
|
|
+ cmds->body.header.streamId = stream_id;
|
|
+ cmds->body.items[0].registerId = SVGA_VIDEO_ENABLED;
|
|
+ cmds->body.items[0].value = false;
|
|
+ fill_flush(&cmds->flush, stream_id);
|
|
+
|
|
+ vmw_fifo_commit(dev_priv, sizeof(*cmds));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Stop or pause a stream.
|
|
+ *
|
|
+ * If the stream is paused the no evict flag is removed from the buffer
|
|
+ * but left in vram. This allows for instance mode_set to evict it
|
|
+ * should it need to.
|
|
+ *
|
|
+ * The caller must hold the overlay lock.
|
|
+ *
|
|
+ * @stream_id which stream to stop/pause.
|
|
+ * @pause true to pause, false to stop completely.
|
|
+ */
|
|
+static int vmw_overlay_stop(struct vmw_private *dev_priv,
|
|
+ uint32_t stream_id, bool pause,
|
|
+ bool interruptible)
|
|
+{
|
|
+ struct vmw_overlay *overlay = dev_priv->overlay_priv;
|
|
+ struct vmw_stream *stream = &overlay->stream[stream_id];
|
|
+ int ret;
|
|
+
|
|
+ /* no buffer attached the stream is completely stopped */
|
|
+ if (!stream->buf)
|
|
+ return 0;
|
|
+
|
|
+ /* If the stream is paused this is already done */
|
|
+ if (!stream->paused) {
|
|
+ ret = vmw_overlay_send_stop(dev_priv, stream_id,
|
|
+ interruptible);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* We just remove the NO_EVICT flag so no -ENOMEM */
|
|
+ ret = vmw_dmabuf_pin_in_vram(dev_priv, stream->buf, false,
|
|
+ interruptible);
|
|
+ if (interruptible && ret == -ERESTARTSYS)
|
|
+ return ret;
|
|
+ else
|
|
+ BUG_ON(ret != 0);
|
|
+ }
|
|
+
|
|
+ if (!pause) {
|
|
+ vmw_dmabuf_unreference(&stream->buf);
|
|
+ stream->paused = false;
|
|
+ } else {
|
|
+ stream->paused = true;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Update a stream and send any put or stop fifo commands needed.
|
|
+ *
|
|
+ * The caller must hold the overlay lock.
|
|
+ *
|
|
+ * Returns
|
|
+ * -ENOMEM if buffer doesn't fit in vram.
|
|
+ * -ERESTARTSYS if interrupted.
|
|
+ */
|
|
+static int vmw_overlay_update_stream(struct vmw_private *dev_priv,
|
|
+ struct vmw_dma_buffer *buf,
|
|
+ struct drm_vmw_control_stream_arg *arg,
|
|
+ bool interruptible)
|
|
+{
|
|
+ struct vmw_overlay *overlay = dev_priv->overlay_priv;
|
|
+ struct vmw_stream *stream = &overlay->stream[arg->stream_id];
|
|
+ int ret = 0;
|
|
+
|
|
+ if (!buf)
|
|
+ return -EINVAL;
|
|
+
|
|
+ DRM_DEBUG(" %s: old %p, new %p, %spaused\n", __func__,
|
|
+ stream->buf, buf, stream->paused ? "" : "not ");
|
|
+
|
|
+ if (stream->buf != buf) {
|
|
+ ret = vmw_overlay_stop(dev_priv, arg->stream_id,
|
|
+ false, interruptible);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ } else if (!stream->paused) {
|
|
+ /* If the buffers match and not paused then just send
|
|
+ * the put command, no need to do anything else.
|
|
+ */
|
|
+ ret = vmw_overlay_send_put(dev_priv, buf, arg, interruptible);
|
|
+ if (ret == 0)
|
|
+ stream->saved = *arg;
|
|
+ else
|
|
+ BUG_ON(!interruptible);
|
|
+
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /* We don't start the old stream if we are interrupted.
|
|
+ * Might return -ENOMEM if it can't fit the buffer in vram.
|
|
+ */
|
|
+ ret = vmw_dmabuf_pin_in_vram(dev_priv, buf, true, interruptible);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = vmw_overlay_send_put(dev_priv, buf, arg, interruptible);
|
|
+ if (ret) {
|
|
+ /* This one needs to happen no matter what. We only remove
|
|
+ * the NO_EVICT flag so this is safe from -ENOMEM.
|
|
+ */
|
|
+ BUG_ON(vmw_dmabuf_pin_in_vram(dev_priv, buf, false, false) != 0);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ if (stream->buf != buf)
|
|
+ stream->buf = vmw_dmabuf_reference(buf);
|
|
+ stream->saved = *arg;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Stop all streams.
|
|
+ *
|
|
+ * Used by the fb code when starting.
|
|
+ *
|
|
+ * Takes the overlay lock.
|
|
+ */
|
|
+int vmw_overlay_stop_all(struct vmw_private *dev_priv)
|
|
+{
|
|
+ struct vmw_overlay *overlay = dev_priv->overlay_priv;
|
|
+ int i, ret;
|
|
+
|
|
+ if (!overlay)
|
|
+ return 0;
|
|
+
|
|
+ mutex_lock(&overlay->mutex);
|
|
+
|
|
+ for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
|
|
+ struct vmw_stream *stream = &overlay->stream[i];
|
|
+ if (!stream->buf)
|
|
+ continue;
|
|
+
|
|
+ ret = vmw_overlay_stop(dev_priv, i, false, false);
|
|
+ WARN_ON(ret != 0);
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&overlay->mutex);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Try to resume all paused streams.
|
|
+ *
|
|
+ * Used by the kms code after moving a new scanout buffer to vram.
|
|
+ *
|
|
+ * Takes the overlay lock.
|
|
+ */
|
|
+int vmw_overlay_resume_all(struct vmw_private *dev_priv)
|
|
+{
|
|
+ struct vmw_overlay *overlay = dev_priv->overlay_priv;
|
|
+ int i, ret;
|
|
+
|
|
+ if (!overlay)
|
|
+ return 0;
|
|
+
|
|
+ mutex_lock(&overlay->mutex);
|
|
+
|
|
+ for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
|
|
+ struct vmw_stream *stream = &overlay->stream[i];
|
|
+ if (!stream->paused)
|
|
+ continue;
|
|
+
|
|
+ ret = vmw_overlay_update_stream(dev_priv, stream->buf,
|
|
+ &stream->saved, false);
|
|
+ if (ret != 0)
|
|
+ DRM_INFO("%s: *warning* failed to resume stream %i\n",
|
|
+ __func__, i);
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&overlay->mutex);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Pauses all active streams.
|
|
+ *
|
|
+ * Used by the kms code when moving a new scanout buffer to vram.
|
|
+ *
|
|
+ * Takes the overlay lock.
|
|
+ */
|
|
+int vmw_overlay_pause_all(struct vmw_private *dev_priv)
|
|
+{
|
|
+ struct vmw_overlay *overlay = dev_priv->overlay_priv;
|
|
+ int i, ret;
|
|
+
|
|
+ if (!overlay)
|
|
+ return 0;
|
|
+
|
|
+ mutex_lock(&overlay->mutex);
|
|
+
|
|
+ for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
|
|
+ if (overlay->stream[i].paused)
|
|
+ DRM_INFO("%s: *warning* stream %i already paused\n",
|
|
+ __func__, i);
|
|
+ ret = vmw_overlay_stop(dev_priv, i, true, false);
|
|
+ WARN_ON(ret != 0);
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&overlay->mutex);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int vmw_overlay_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
|
|
+ struct vmw_private *dev_priv = vmw_priv(dev);
|
|
+ struct vmw_overlay *overlay = dev_priv->overlay_priv;
|
|
+ struct drm_vmw_control_stream_arg *arg =
|
|
+ (struct drm_vmw_control_stream_arg *)data;
|
|
+ struct vmw_dma_buffer *buf;
|
|
+ struct vmw_resource *res;
|
|
+ int ret;
|
|
+
|
|
+ if (!overlay)
|
|
+ return -ENOSYS;
|
|
+
|
|
+ ret = vmw_user_stream_lookup(dev_priv, tfile, &arg->stream_id, &res);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ mutex_lock(&overlay->mutex);
|
|
+
|
|
+ if (!arg->enabled) {
|
|
+ ret = vmw_overlay_stop(dev_priv, arg->stream_id, false, true);
|
|
+ goto out_unlock;
|
|
+ }
|
|
+
|
|
+ ret = vmw_user_dmabuf_lookup(tfile, arg->handle, &buf);
|
|
+ if (ret)
|
|
+ goto out_unlock;
|
|
+
|
|
+ ret = vmw_overlay_update_stream(dev_priv, buf, arg, true);
|
|
+
|
|
+ vmw_dmabuf_unreference(&buf);
|
|
+
|
|
+out_unlock:
|
|
+ mutex_unlock(&overlay->mutex);
|
|
+ vmw_resource_unreference(&res);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int vmw_overlay_num_overlays(struct vmw_private *dev_priv)
|
|
+{
|
|
+ if (!dev_priv->overlay_priv)
|
|
+ return 0;
|
|
+
|
|
+ return VMW_MAX_NUM_STREAMS;
|
|
+}
|
|
+
|
|
+int vmw_overlay_num_free_overlays(struct vmw_private *dev_priv)
|
|
+{
|
|
+ struct vmw_overlay *overlay = dev_priv->overlay_priv;
|
|
+ int i, k;
|
|
+
|
|
+ if (!overlay)
|
|
+ return 0;
|
|
+
|
|
+ mutex_lock(&overlay->mutex);
|
|
+
|
|
+ for (i = 0, k = 0; i < VMW_MAX_NUM_STREAMS; i++)
|
|
+ if (!overlay->stream[i].claimed)
|
|
+ k++;
|
|
+
|
|
+ mutex_unlock(&overlay->mutex);
|
|
+
|
|
+ return k;
|
|
+}
|
|
+
|
|
+int vmw_overlay_claim(struct vmw_private *dev_priv, uint32_t *out)
|
|
+{
|
|
+ struct vmw_overlay *overlay = dev_priv->overlay_priv;
|
|
+ int i;
|
|
+
|
|
+ if (!overlay)
|
|
+ return -ENOSYS;
|
|
+
|
|
+ mutex_lock(&overlay->mutex);
|
|
+
|
|
+ for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
|
|
+
|
|
+ if (overlay->stream[i].claimed)
|
|
+ continue;
|
|
+
|
|
+ overlay->stream[i].claimed = true;
|
|
+ *out = i;
|
|
+ mutex_unlock(&overlay->mutex);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&overlay->mutex);
|
|
+ return -ESRCH;
|
|
+}
|
|
+
|
|
+int vmw_overlay_unref(struct vmw_private *dev_priv, uint32_t stream_id)
|
|
+{
|
|
+ struct vmw_overlay *overlay = dev_priv->overlay_priv;
|
|
+
|
|
+ BUG_ON(stream_id >= VMW_MAX_NUM_STREAMS);
|
|
+
|
|
+ if (!overlay)
|
|
+ return -ENOSYS;
|
|
+
|
|
+ mutex_lock(&overlay->mutex);
|
|
+
|
|
+ WARN_ON(!overlay->stream[stream_id].claimed);
|
|
+ vmw_overlay_stop(dev_priv, stream_id, false, false);
|
|
+ overlay->stream[stream_id].claimed = false;
|
|
+
|
|
+ mutex_unlock(&overlay->mutex);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int vmw_overlay_init(struct vmw_private *dev_priv)
|
|
+{
|
|
+ struct vmw_overlay *overlay;
|
|
+ int i;
|
|
+
|
|
+ if (dev_priv->overlay_priv)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (!(dev_priv->fifo.capabilities & SVGA_FIFO_CAP_VIDEO) &&
|
|
+ (dev_priv->fifo.capabilities & SVGA_FIFO_CAP_ESCAPE)) {
|
|
+ DRM_INFO("hardware doesn't support overlays\n");
|
|
+ return -ENOSYS;
|
|
+ }
|
|
+
|
|
+ overlay = kmalloc(GFP_KERNEL, sizeof(*overlay));
|
|
+ if (!overlay)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ memset(overlay, 0, sizeof(*overlay));
|
|
+ mutex_init(&overlay->mutex);
|
|
+ for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
|
|
+ overlay->stream[i].buf = NULL;
|
|
+ overlay->stream[i].paused = false;
|
|
+ overlay->stream[i].claimed = false;
|
|
+ }
|
|
+
|
|
+ dev_priv->overlay_priv = overlay;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int vmw_overlay_close(struct vmw_private *dev_priv)
|
|
+{
|
|
+ struct vmw_overlay *overlay = dev_priv->overlay_priv;
|
|
+ bool forgotten_buffer = false;
|
|
+ int i;
|
|
+
|
|
+ if (!overlay)
|
|
+ return -ENOSYS;
|
|
+
|
|
+ for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
|
|
+ if (overlay->stream[i].buf) {
|
|
+ forgotten_buffer = true;
|
|
+ vmw_overlay_stop(dev_priv, i, false, false);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ WARN_ON(forgotten_buffer);
|
|
+
|
|
+ dev_priv->overlay_priv = NULL;
|
|
+ kfree(overlay);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_reg.h b/drivers/gpu/drm/vmwgfx/vmwgfx_reg.h
|
|
new file mode 100644
|
|
index 0000000..9d0dd3a
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_reg.h
|
|
@@ -0,0 +1,57 @@
|
|
+/**************************************************************************
|
|
+ *
|
|
+ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sub license, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial portions
|
|
+ * of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ **************************************************************************/
|
|
+
|
|
+/**
|
|
+ * This file contains virtual hardware defines for kernel space.
|
|
+ */
|
|
+
|
|
+#ifndef _VMWGFX_REG_H_
|
|
+#define _VMWGFX_REG_H_
|
|
+
|
|
+#include <linux/types.h>
|
|
+
|
|
+#define VMWGFX_INDEX_PORT 0x0
|
|
+#define VMWGFX_VALUE_PORT 0x1
|
|
+#define VMWGFX_IRQSTATUS_PORT 0x8
|
|
+
|
|
+struct svga_guest_mem_descriptor {
|
|
+ __le32 ppn;
|
|
+ __le32 num_pages;
|
|
+};
|
|
+
|
|
+struct svga_fifo_cmd_fence {
|
|
+ __le32 fence;
|
|
+};
|
|
+
|
|
+#define SVGA_SYNC_GENERIC 1
|
|
+#define SVGA_SYNC_FIFOFULL 2
|
|
+
|
|
+#include "svga_types.h"
|
|
+
|
|
+#include "svga3d_reg.h"
|
|
+
|
|
+#endif
|
|
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
|
|
new file mode 100644
|
|
index 0000000..f8fbbc6
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
|
|
@@ -0,0 +1,1187 @@
|
|
+/**************************************************************************
|
|
+ *
|
|
+ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sub license, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial portions
|
|
+ * of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ **************************************************************************/
|
|
+
|
|
+#include "vmwgfx_drv.h"
|
|
+#include "vmwgfx_drm.h"
|
|
+#include "ttm/ttm_object.h"
|
|
+#include "ttm/ttm_placement.h"
|
|
+#include "drmP.h"
|
|
+
|
|
+#define VMW_RES_CONTEXT ttm_driver_type0
|
|
+#define VMW_RES_SURFACE ttm_driver_type1
|
|
+#define VMW_RES_STREAM ttm_driver_type2
|
|
+
|
|
+struct vmw_user_context {
|
|
+ struct ttm_base_object base;
|
|
+ struct vmw_resource res;
|
|
+};
|
|
+
|
|
+struct vmw_user_surface {
|
|
+ struct ttm_base_object base;
|
|
+ struct vmw_surface srf;
|
|
+};
|
|
+
|
|
+struct vmw_user_dma_buffer {
|
|
+ struct ttm_base_object base;
|
|
+ struct vmw_dma_buffer dma;
|
|
+};
|
|
+
|
|
+struct vmw_bo_user_rep {
|
|
+ uint32_t handle;
|
|
+ uint64_t map_handle;
|
|
+};
|
|
+
|
|
+struct vmw_stream {
|
|
+ struct vmw_resource res;
|
|
+ uint32_t stream_id;
|
|
+};
|
|
+
|
|
+struct vmw_user_stream {
|
|
+ struct ttm_base_object base;
|
|
+ struct vmw_stream stream;
|
|
+};
|
|
+
|
|
+static inline struct vmw_dma_buffer *
|
|
+vmw_dma_buffer(struct ttm_buffer_object *bo)
|
|
+{
|
|
+ return container_of(bo, struct vmw_dma_buffer, base);
|
|
+}
|
|
+
|
|
+static inline struct vmw_user_dma_buffer *
|
|
+vmw_user_dma_buffer(struct ttm_buffer_object *bo)
|
|
+{
|
|
+ struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo);
|
|
+ return container_of(vmw_bo, struct vmw_user_dma_buffer, dma);
|
|
+}
|
|
+
|
|
+struct vmw_resource *vmw_resource_reference(struct vmw_resource *res)
|
|
+{
|
|
+ kref_get(&res->kref);
|
|
+ return res;
|
|
+}
|
|
+
|
|
+static void vmw_resource_release(struct kref *kref)
|
|
+{
|
|
+ struct vmw_resource *res =
|
|
+ container_of(kref, struct vmw_resource, kref);
|
|
+ struct vmw_private *dev_priv = res->dev_priv;
|
|
+
|
|
+ idr_remove(res->idr, res->id);
|
|
+ write_unlock(&dev_priv->resource_lock);
|
|
+
|
|
+ if (likely(res->hw_destroy != NULL))
|
|
+ res->hw_destroy(res);
|
|
+
|
|
+ if (res->res_free != NULL)
|
|
+ res->res_free(res);
|
|
+ else
|
|
+ kfree(res);
|
|
+
|
|
+ write_lock(&dev_priv->resource_lock);
|
|
+}
|
|
+
|
|
+void vmw_resource_unreference(struct vmw_resource **p_res)
|
|
+{
|
|
+ struct vmw_resource *res = *p_res;
|
|
+ struct vmw_private *dev_priv = res->dev_priv;
|
|
+
|
|
+ *p_res = NULL;
|
|
+ write_lock(&dev_priv->resource_lock);
|
|
+ kref_put(&res->kref, vmw_resource_release);
|
|
+ write_unlock(&dev_priv->resource_lock);
|
|
+}
|
|
+
|
|
+static int vmw_resource_init(struct vmw_private *dev_priv,
|
|
+ struct vmw_resource *res,
|
|
+ struct idr *idr,
|
|
+ enum ttm_object_type obj_type,
|
|
+ void (*res_free) (struct vmw_resource *res))
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ kref_init(&res->kref);
|
|
+ res->hw_destroy = NULL;
|
|
+ res->res_free = res_free;
|
|
+ res->res_type = obj_type;
|
|
+ res->idr = idr;
|
|
+ res->avail = false;
|
|
+ res->dev_priv = dev_priv;
|
|
+
|
|
+ do {
|
|
+ if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0))
|
|
+ return -ENOMEM;
|
|
+
|
|
+ write_lock(&dev_priv->resource_lock);
|
|
+ ret = idr_get_new_above(idr, res, 1, &res->id);
|
|
+ write_unlock(&dev_priv->resource_lock);
|
|
+
|
|
+ } while (ret == -EAGAIN);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * vmw_resource_activate
|
|
+ *
|
|
+ * @res: Pointer to the newly created resource
|
|
+ * @hw_destroy: Destroy function. NULL if none.
|
|
+ *
|
|
+ * Activate a resource after the hardware has been made aware of it.
|
|
+ * Set tye destroy function to @destroy. Typically this frees the
|
|
+ * resource and destroys the hardware resources associated with it.
|
|
+ * Activate basically means that the function vmw_resource_lookup will
|
|
+ * find it.
|
|
+ */
|
|
+
|
|
+static void vmw_resource_activate(struct vmw_resource *res,
|
|
+ void (*hw_destroy) (struct vmw_resource *))
|
|
+{
|
|
+ struct vmw_private *dev_priv = res->dev_priv;
|
|
+
|
|
+ write_lock(&dev_priv->resource_lock);
|
|
+ res->avail = true;
|
|
+ res->hw_destroy = hw_destroy;
|
|
+ write_unlock(&dev_priv->resource_lock);
|
|
+}
|
|
+
|
|
+struct vmw_resource *vmw_resource_lookup(struct vmw_private *dev_priv,
|
|
+ struct idr *idr, int id)
|
|
+{
|
|
+ struct vmw_resource *res;
|
|
+
|
|
+ read_lock(&dev_priv->resource_lock);
|
|
+ res = idr_find(idr, id);
|
|
+ if (res && res->avail)
|
|
+ kref_get(&res->kref);
|
|
+ else
|
|
+ res = NULL;
|
|
+ read_unlock(&dev_priv->resource_lock);
|
|
+
|
|
+ if (unlikely(res == NULL))
|
|
+ return NULL;
|
|
+
|
|
+ return res;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Context management:
|
|
+ */
|
|
+
|
|
+static void vmw_hw_context_destroy(struct vmw_resource *res)
|
|
+{
|
|
+
|
|
+ struct vmw_private *dev_priv = res->dev_priv;
|
|
+ struct {
|
|
+ SVGA3dCmdHeader header;
|
|
+ SVGA3dCmdDestroyContext body;
|
|
+ } *cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd));
|
|
+
|
|
+ if (unlikely(cmd == NULL)) {
|
|
+ DRM_ERROR("Failed reserving FIFO space for surface "
|
|
+ "destruction.\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ cmd->header.id = cpu_to_le32(SVGA_3D_CMD_CONTEXT_DESTROY);
|
|
+ cmd->header.size = cpu_to_le32(sizeof(cmd->body));
|
|
+ cmd->body.cid = cpu_to_le32(res->id);
|
|
+
|
|
+ vmw_fifo_commit(dev_priv, sizeof(*cmd));
|
|
+}
|
|
+
|
|
+static int vmw_context_init(struct vmw_private *dev_priv,
|
|
+ struct vmw_resource *res,
|
|
+ void (*res_free) (struct vmw_resource *res))
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ struct {
|
|
+ SVGA3dCmdHeader header;
|
|
+ SVGA3dCmdDefineContext body;
|
|
+ } *cmd;
|
|
+
|
|
+ ret = vmw_resource_init(dev_priv, res, &dev_priv->context_idr,
|
|
+ VMW_RES_CONTEXT, res_free);
|
|
+
|
|
+ if (unlikely(ret != 0)) {
|
|
+ if (res_free == NULL)
|
|
+ kfree(res);
|
|
+ else
|
|
+ res_free(res);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd));
|
|
+ if (unlikely(cmd == NULL)) {
|
|
+ DRM_ERROR("Fifo reserve failed.\n");
|
|
+ vmw_resource_unreference(&res);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ cmd->header.id = cpu_to_le32(SVGA_3D_CMD_CONTEXT_DEFINE);
|
|
+ cmd->header.size = cpu_to_le32(sizeof(cmd->body));
|
|
+ cmd->body.cid = cpu_to_le32(res->id);
|
|
+
|
|
+ vmw_fifo_commit(dev_priv, sizeof(*cmd));
|
|
+ vmw_resource_activate(res, vmw_hw_context_destroy);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+struct vmw_resource *vmw_context_alloc(struct vmw_private *dev_priv)
|
|
+{
|
|
+ struct vmw_resource *res = kmalloc(sizeof(*res), GFP_KERNEL);
|
|
+ int ret;
|
|
+
|
|
+ if (unlikely(res == NULL))
|
|
+ return NULL;
|
|
+
|
|
+ ret = vmw_context_init(dev_priv, res, NULL);
|
|
+ return (ret == 0) ? res : NULL;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * User-space context management:
|
|
+ */
|
|
+
|
|
+static void vmw_user_context_free(struct vmw_resource *res)
|
|
+{
|
|
+ struct vmw_user_context *ctx =
|
|
+ container_of(res, struct vmw_user_context, res);
|
|
+
|
|
+ kfree(ctx);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * This function is called when user space has no more references on the
|
|
+ * base object. It releases the base-object's reference on the resource object.
|
|
+ */
|
|
+
|
|
+static void vmw_user_context_base_release(struct ttm_base_object **p_base)
|
|
+{
|
|
+ struct ttm_base_object *base = *p_base;
|
|
+ struct vmw_user_context *ctx =
|
|
+ container_of(base, struct vmw_user_context, base);
|
|
+ struct vmw_resource *res = &ctx->res;
|
|
+
|
|
+ *p_base = NULL;
|
|
+ vmw_resource_unreference(&res);
|
|
+}
|
|
+
|
|
+int vmw_context_destroy_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct vmw_private *dev_priv = vmw_priv(dev);
|
|
+ struct vmw_resource *res;
|
|
+ struct vmw_user_context *ctx;
|
|
+ struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data;
|
|
+ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
|
|
+ int ret = 0;
|
|
+
|
|
+ res = vmw_resource_lookup(dev_priv, &dev_priv->context_idr, arg->cid);
|
|
+ if (unlikely(res == NULL))
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (res->res_free != &vmw_user_context_free) {
|
|
+ ret = -EINVAL;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ ctx = container_of(res, struct vmw_user_context, res);
|
|
+ if (ctx->base.tfile != tfile && !ctx->base.shareable) {
|
|
+ ret = -EPERM;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ ttm_ref_object_base_unref(tfile, ctx->base.hash.key, TTM_REF_USAGE);
|
|
+out:
|
|
+ vmw_resource_unreference(&res);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int vmw_context_define_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct vmw_private *dev_priv = vmw_priv(dev);
|
|
+ struct vmw_user_context *ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
|
|
+ struct vmw_resource *res;
|
|
+ struct vmw_resource *tmp;
|
|
+ struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data;
|
|
+ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
|
|
+ int ret;
|
|
+
|
|
+ if (unlikely(ctx == NULL))
|
|
+ return -ENOMEM;
|
|
+
|
|
+ res = &ctx->res;
|
|
+ ctx->base.shareable = false;
|
|
+ ctx->base.tfile = NULL;
|
|
+
|
|
+ ret = vmw_context_init(dev_priv, res, vmw_user_context_free);
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+
|
|
+ tmp = vmw_resource_reference(&ctx->res);
|
|
+ ret = ttm_base_object_init(tfile, &ctx->base, false, VMW_RES_CONTEXT,
|
|
+ &vmw_user_context_base_release, NULL);
|
|
+
|
|
+ if (unlikely(ret != 0)) {
|
|
+ vmw_resource_unreference(&tmp);
|
|
+ goto out_err;
|
|
+ }
|
|
+
|
|
+ arg->cid = res->id;
|
|
+out_err:
|
|
+ vmw_resource_unreference(&res);
|
|
+ return ret;
|
|
+
|
|
+}
|
|
+
|
|
+int vmw_context_check(struct vmw_private *dev_priv,
|
|
+ struct ttm_object_file *tfile,
|
|
+ int id)
|
|
+{
|
|
+ struct vmw_resource *res;
|
|
+ int ret = 0;
|
|
+
|
|
+ read_lock(&dev_priv->resource_lock);
|
|
+ res = idr_find(&dev_priv->context_idr, id);
|
|
+ if (res && res->avail) {
|
|
+ struct vmw_user_context *ctx =
|
|
+ container_of(res, struct vmw_user_context, res);
|
|
+ if (ctx->base.tfile != tfile && !ctx->base.shareable)
|
|
+ ret = -EPERM;
|
|
+ } else
|
|
+ ret = -EINVAL;
|
|
+ read_unlock(&dev_priv->resource_lock);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * Surface management.
|
|
+ */
|
|
+
|
|
+static void vmw_hw_surface_destroy(struct vmw_resource *res)
|
|
+{
|
|
+
|
|
+ struct vmw_private *dev_priv = res->dev_priv;
|
|
+ struct {
|
|
+ SVGA3dCmdHeader header;
|
|
+ SVGA3dCmdDestroySurface body;
|
|
+ } *cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd));
|
|
+
|
|
+ if (unlikely(cmd == NULL)) {
|
|
+ DRM_ERROR("Failed reserving FIFO space for surface "
|
|
+ "destruction.\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ cmd->header.id = cpu_to_le32(SVGA_3D_CMD_SURFACE_DESTROY);
|
|
+ cmd->header.size = cpu_to_le32(sizeof(cmd->body));
|
|
+ cmd->body.sid = cpu_to_le32(res->id);
|
|
+
|
|
+ vmw_fifo_commit(dev_priv, sizeof(*cmd));
|
|
+}
|
|
+
|
|
+void vmw_surface_res_free(struct vmw_resource *res)
|
|
+{
|
|
+ struct vmw_surface *srf = container_of(res, struct vmw_surface, res);
|
|
+
|
|
+ kfree(srf->sizes);
|
|
+ kfree(srf->snooper.image);
|
|
+ kfree(srf);
|
|
+}
|
|
+
|
|
+int vmw_surface_init(struct vmw_private *dev_priv,
|
|
+ struct vmw_surface *srf,
|
|
+ void (*res_free) (struct vmw_resource *res))
|
|
+{
|
|
+ int ret;
|
|
+ struct {
|
|
+ SVGA3dCmdHeader header;
|
|
+ SVGA3dCmdDefineSurface body;
|
|
+ } *cmd;
|
|
+ SVGA3dSize *cmd_size;
|
|
+ struct vmw_resource *res = &srf->res;
|
|
+ struct drm_vmw_size *src_size;
|
|
+ size_t submit_size;
|
|
+ uint32_t cmd_len;
|
|
+ int i;
|
|
+
|
|
+ BUG_ON(res_free == NULL);
|
|
+ ret = vmw_resource_init(dev_priv, res, &dev_priv->surface_idr,
|
|
+ VMW_RES_SURFACE, res_free);
|
|
+
|
|
+ if (unlikely(ret != 0)) {
|
|
+ res_free(res);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ submit_size = sizeof(*cmd) + srf->num_sizes * sizeof(SVGA3dSize);
|
|
+ cmd_len = sizeof(cmd->body) + srf->num_sizes * sizeof(SVGA3dSize);
|
|
+
|
|
+ cmd = vmw_fifo_reserve(dev_priv, submit_size);
|
|
+ if (unlikely(cmd == NULL)) {
|
|
+ DRM_ERROR("Fifo reserve failed for create surface.\n");
|
|
+ vmw_resource_unreference(&res);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ cmd->header.id = cpu_to_le32(SVGA_3D_CMD_SURFACE_DEFINE);
|
|
+ cmd->header.size = cpu_to_le32(cmd_len);
|
|
+ cmd->body.sid = cpu_to_le32(res->id);
|
|
+ cmd->body.surfaceFlags = cpu_to_le32(srf->flags);
|
|
+ cmd->body.format = cpu_to_le32(srf->format);
|
|
+ for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) {
|
|
+ cmd->body.face[i].numMipLevels =
|
|
+ cpu_to_le32(srf->mip_levels[i]);
|
|
+ }
|
|
+
|
|
+ cmd += 1;
|
|
+ cmd_size = (SVGA3dSize *) cmd;
|
|
+ src_size = srf->sizes;
|
|
+
|
|
+ for (i = 0; i < srf->num_sizes; ++i, cmd_size++, src_size++) {
|
|
+ cmd_size->width = cpu_to_le32(src_size->width);
|
|
+ cmd_size->height = cpu_to_le32(src_size->height);
|
|
+ cmd_size->depth = cpu_to_le32(src_size->depth);
|
|
+ }
|
|
+
|
|
+ vmw_fifo_commit(dev_priv, submit_size);
|
|
+ vmw_resource_activate(res, vmw_hw_surface_destroy);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void vmw_user_surface_free(struct vmw_resource *res)
|
|
+{
|
|
+ struct vmw_surface *srf = container_of(res, struct vmw_surface, res);
|
|
+ struct vmw_user_surface *user_srf =
|
|
+ container_of(srf, struct vmw_user_surface, srf);
|
|
+
|
|
+ kfree(srf->sizes);
|
|
+ kfree(srf->snooper.image);
|
|
+ kfree(user_srf);
|
|
+}
|
|
+
|
|
+int vmw_user_surface_lookup_handle(struct vmw_private *dev_priv,
|
|
+ struct ttm_object_file *tfile,
|
|
+ uint32_t handle, struct vmw_surface **out)
|
|
+{
|
|
+ struct vmw_resource *res;
|
|
+ struct vmw_surface *srf;
|
|
+ struct vmw_user_surface *user_srf;
|
|
+ struct ttm_base_object *base;
|
|
+ int ret = -EINVAL;
|
|
+
|
|
+ base = ttm_base_object_lookup(tfile, handle);
|
|
+ if (unlikely(base == NULL))
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (unlikely(base->object_type != VMW_RES_SURFACE))
|
|
+ goto out_bad_resource;
|
|
+
|
|
+ user_srf = container_of(base, struct vmw_user_surface, base);
|
|
+ srf = &user_srf->srf;
|
|
+ res = &srf->res;
|
|
+
|
|
+ read_lock(&dev_priv->resource_lock);
|
|
+
|
|
+ if (!res->avail || res->res_free != &vmw_user_surface_free) {
|
|
+ read_unlock(&dev_priv->resource_lock);
|
|
+ goto out_bad_resource;
|
|
+ }
|
|
+
|
|
+ kref_get(&res->kref);
|
|
+ read_unlock(&dev_priv->resource_lock);
|
|
+
|
|
+ *out = srf;
|
|
+ ret = 0;
|
|
+
|
|
+out_bad_resource:
|
|
+ ttm_base_object_unref(&base);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void vmw_user_surface_base_release(struct ttm_base_object **p_base)
|
|
+{
|
|
+ struct ttm_base_object *base = *p_base;
|
|
+ struct vmw_user_surface *user_srf =
|
|
+ container_of(base, struct vmw_user_surface, base);
|
|
+ struct vmw_resource *res = &user_srf->srf.res;
|
|
+
|
|
+ *p_base = NULL;
|
|
+ vmw_resource_unreference(&res);
|
|
+}
|
|
+
|
|
+int vmw_surface_destroy_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_vmw_surface_arg *arg = (struct drm_vmw_surface_arg *)data;
|
|
+ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
|
|
+
|
|
+ return ttm_ref_object_base_unref(tfile, arg->sid, TTM_REF_USAGE);
|
|
+}
|
|
+
|
|
+int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct vmw_private *dev_priv = vmw_priv(dev);
|
|
+ struct vmw_user_surface *user_srf =
|
|
+ kmalloc(sizeof(*user_srf), GFP_KERNEL);
|
|
+ struct vmw_surface *srf;
|
|
+ struct vmw_resource *res;
|
|
+ struct vmw_resource *tmp;
|
|
+ union drm_vmw_surface_create_arg *arg =
|
|
+ (union drm_vmw_surface_create_arg *)data;
|
|
+ struct drm_vmw_surface_create_req *req = &arg->req;
|
|
+ struct drm_vmw_surface_arg *rep = &arg->rep;
|
|
+ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
|
|
+ struct drm_vmw_size __user *user_sizes;
|
|
+ int ret;
|
|
+ int i;
|
|
+
|
|
+ if (unlikely(user_srf == NULL))
|
|
+ return -ENOMEM;
|
|
+
|
|
+ srf = &user_srf->srf;
|
|
+ res = &srf->res;
|
|
+
|
|
+ srf->flags = req->flags;
|
|
+ srf->format = req->format;
|
|
+ srf->scanout = req->scanout;
|
|
+ memcpy(srf->mip_levels, req->mip_levels, sizeof(srf->mip_levels));
|
|
+ srf->num_sizes = 0;
|
|
+ for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i)
|
|
+ srf->num_sizes += srf->mip_levels[i];
|
|
+
|
|
+ if (srf->num_sizes > DRM_VMW_MAX_SURFACE_FACES *
|
|
+ DRM_VMW_MAX_MIP_LEVELS) {
|
|
+ ret = -EINVAL;
|
|
+ goto out_err0;
|
|
+ }
|
|
+
|
|
+ srf->sizes = kmalloc(srf->num_sizes * sizeof(*srf->sizes), GFP_KERNEL);
|
|
+ if (unlikely(srf->sizes == NULL)) {
|
|
+ ret = -ENOMEM;
|
|
+ goto out_err0;
|
|
+ }
|
|
+
|
|
+ user_sizes = (struct drm_vmw_size __user *)(unsigned long)
|
|
+ req->size_addr;
|
|
+
|
|
+ ret = copy_from_user(srf->sizes, user_sizes,
|
|
+ srf->num_sizes * sizeof(*srf->sizes));
|
|
+ if (unlikely(ret != 0))
|
|
+ goto out_err1;
|
|
+
|
|
+ if (srf->scanout &&
|
|
+ srf->num_sizes == 1 &&
|
|
+ srf->sizes[0].width == 64 &&
|
|
+ srf->sizes[0].height == 64 &&
|
|
+ srf->format == SVGA3D_A8R8G8B8) {
|
|
+
|
|
+ srf->snooper.image = kmalloc(64 * 64 * 4, GFP_KERNEL);
|
|
+ /* clear the image */
|
|
+ if (srf->snooper.image) {
|
|
+ memset(srf->snooper.image, 0x00, 64 * 64 * 4);
|
|
+ } else {
|
|
+ DRM_ERROR("Failed to allocate cursor_image\n");
|
|
+ ret = -ENOMEM;
|
|
+ goto out_err1;
|
|
+ }
|
|
+ } else {
|
|
+ srf->snooper.image = NULL;
|
|
+ }
|
|
+ srf->snooper.crtc = NULL;
|
|
+
|
|
+ user_srf->base.shareable = false;
|
|
+ user_srf->base.tfile = NULL;
|
|
+
|
|
+ /**
|
|
+ * From this point, the generic resource management functions
|
|
+ * destroy the object on failure.
|
|
+ */
|
|
+
|
|
+ ret = vmw_surface_init(dev_priv, srf, vmw_user_surface_free);
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+
|
|
+ tmp = vmw_resource_reference(&srf->res);
|
|
+ ret = ttm_base_object_init(tfile, &user_srf->base,
|
|
+ req->shareable, VMW_RES_SURFACE,
|
|
+ &vmw_user_surface_base_release, NULL);
|
|
+
|
|
+ if (unlikely(ret != 0)) {
|
|
+ vmw_resource_unreference(&tmp);
|
|
+ vmw_resource_unreference(&res);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ rep->sid = user_srf->base.hash.key;
|
|
+ if (rep->sid == SVGA3D_INVALID_ID)
|
|
+ DRM_ERROR("Created bad Surface ID.\n");
|
|
+
|
|
+ vmw_resource_unreference(&res);
|
|
+ return 0;
|
|
+out_err1:
|
|
+ kfree(srf->sizes);
|
|
+out_err0:
|
|
+ kfree(user_srf);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int vmw_surface_reference_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ union drm_vmw_surface_reference_arg *arg =
|
|
+ (union drm_vmw_surface_reference_arg *)data;
|
|
+ struct drm_vmw_surface_arg *req = &arg->req;
|
|
+ struct drm_vmw_surface_create_req *rep = &arg->rep;
|
|
+ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
|
|
+ struct vmw_surface *srf;
|
|
+ struct vmw_user_surface *user_srf;
|
|
+ struct drm_vmw_size __user *user_sizes;
|
|
+ struct ttm_base_object *base;
|
|
+ int ret = -EINVAL;
|
|
+
|
|
+ base = ttm_base_object_lookup(tfile, req->sid);
|
|
+ if (unlikely(base == NULL)) {
|
|
+ DRM_ERROR("Could not find surface to reference.\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (unlikely(base->object_type != VMW_RES_SURFACE))
|
|
+ goto out_bad_resource;
|
|
+
|
|
+ user_srf = container_of(base, struct vmw_user_surface, base);
|
|
+ srf = &user_srf->srf;
|
|
+
|
|
+ ret = ttm_ref_object_add(tfile, &user_srf->base, TTM_REF_USAGE, NULL);
|
|
+ if (unlikely(ret != 0)) {
|
|
+ DRM_ERROR("Could not add a reference to a surface.\n");
|
|
+ goto out_no_reference;
|
|
+ }
|
|
+
|
|
+ rep->flags = srf->flags;
|
|
+ rep->format = srf->format;
|
|
+ memcpy(rep->mip_levels, srf->mip_levels, sizeof(srf->mip_levels));
|
|
+ user_sizes = (struct drm_vmw_size __user *)(unsigned long)
|
|
+ rep->size_addr;
|
|
+
|
|
+ if (user_sizes)
|
|
+ ret = copy_to_user(user_sizes, srf->sizes,
|
|
+ srf->num_sizes * sizeof(*srf->sizes));
|
|
+ if (unlikely(ret != 0))
|
|
+ DRM_ERROR("copy_to_user failed %p %u\n",
|
|
+ user_sizes, srf->num_sizes);
|
|
+out_bad_resource:
|
|
+out_no_reference:
|
|
+ ttm_base_object_unref(&base);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int vmw_surface_check(struct vmw_private *dev_priv,
|
|
+ struct ttm_object_file *tfile,
|
|
+ uint32_t handle, int *id)
|
|
+{
|
|
+ struct ttm_base_object *base;
|
|
+ struct vmw_user_surface *user_srf;
|
|
+
|
|
+ int ret = -EPERM;
|
|
+
|
|
+ base = ttm_base_object_lookup(tfile, handle);
|
|
+ if (unlikely(base == NULL))
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (unlikely(base->object_type != VMW_RES_SURFACE))
|
|
+ goto out_bad_surface;
|
|
+
|
|
+ user_srf = container_of(base, struct vmw_user_surface, base);
|
|
+ *id = user_srf->srf.res.id;
|
|
+ ret = 0;
|
|
+
|
|
+out_bad_surface:
|
|
+ /**
|
|
+ * FIXME: May deadlock here when called from the
|
|
+ * command parsing code.
|
|
+ */
|
|
+
|
|
+ ttm_base_object_unref(&base);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Buffer management.
|
|
+ */
|
|
+
|
|
+static size_t vmw_dmabuf_acc_size(struct ttm_bo_global *glob,
|
|
+ unsigned long num_pages)
|
|
+{
|
|
+ static size_t bo_user_size = ~0;
|
|
+
|
|
+ size_t page_array_size =
|
|
+ (num_pages * sizeof(void *) + PAGE_SIZE - 1) & PAGE_MASK;
|
|
+
|
|
+ if (unlikely(bo_user_size == ~0)) {
|
|
+ bo_user_size = glob->ttm_bo_extra_size +
|
|
+ ttm_round_pot(sizeof(struct vmw_dma_buffer));
|
|
+ }
|
|
+
|
|
+ return bo_user_size + page_array_size;
|
|
+}
|
|
+
|
|
+void vmw_dmabuf_gmr_unbind(struct ttm_buffer_object *bo)
|
|
+{
|
|
+ struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo);
|
|
+ struct ttm_bo_global *glob = bo->glob;
|
|
+ struct vmw_private *dev_priv =
|
|
+ container_of(bo->bdev, struct vmw_private, bdev);
|
|
+
|
|
+ if (vmw_bo->gmr_bound) {
|
|
+ vmw_gmr_unbind(dev_priv, vmw_bo->gmr_id);
|
|
+ spin_lock(&glob->lru_lock);
|
|
+ ida_remove(&dev_priv->gmr_ida, vmw_bo->gmr_id);
|
|
+ spin_unlock(&glob->lru_lock);
|
|
+ vmw_bo->gmr_bound = false;
|
|
+ }
|
|
+}
|
|
+
|
|
+void vmw_dmabuf_bo_free(struct ttm_buffer_object *bo)
|
|
+{
|
|
+ struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo);
|
|
+ struct ttm_bo_global *glob = bo->glob;
|
|
+
|
|
+ vmw_dmabuf_gmr_unbind(bo);
|
|
+ ttm_mem_global_free(glob->mem_glob, bo->acc_size);
|
|
+ kfree(vmw_bo);
|
|
+}
|
|
+
|
|
+int vmw_dmabuf_init(struct vmw_private *dev_priv,
|
|
+ struct vmw_dma_buffer *vmw_bo,
|
|
+ size_t size, struct ttm_placement *placement,
|
|
+ bool interruptible,
|
|
+ void (*bo_free) (struct ttm_buffer_object *bo))
|
|
+{
|
|
+ struct ttm_bo_device *bdev = &dev_priv->bdev;
|
|
+ struct ttm_mem_global *mem_glob = bdev->glob->mem_glob;
|
|
+ size_t acc_size;
|
|
+ int ret;
|
|
+
|
|
+ BUG_ON(!bo_free);
|
|
+
|
|
+ acc_size =
|
|
+ vmw_dmabuf_acc_size(bdev->glob,
|
|
+ (size + PAGE_SIZE - 1) >> PAGE_SHIFT);
|
|
+
|
|
+ ret = ttm_mem_global_alloc(mem_glob, acc_size, false, false);
|
|
+ if (unlikely(ret != 0)) {
|
|
+ /* we must free the bo here as
|
|
+ * ttm_buffer_object_init does so as well */
|
|
+ bo_free(&vmw_bo->base);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ memset(vmw_bo, 0, sizeof(*vmw_bo));
|
|
+
|
|
+ INIT_LIST_HEAD(&vmw_bo->gmr_lru);
|
|
+ INIT_LIST_HEAD(&vmw_bo->validate_list);
|
|
+ vmw_bo->gmr_id = 0;
|
|
+ vmw_bo->gmr_bound = false;
|
|
+
|
|
+ ret = ttm_bo_init(bdev, &vmw_bo->base, size,
|
|
+ ttm_bo_type_device, placement,
|
|
+ 0, 0, interruptible,
|
|
+ NULL, acc_size, bo_free);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void vmw_user_dmabuf_destroy(struct ttm_buffer_object *bo)
|
|
+{
|
|
+ struct vmw_user_dma_buffer *vmw_user_bo = vmw_user_dma_buffer(bo);
|
|
+ struct ttm_bo_global *glob = bo->glob;
|
|
+
|
|
+ vmw_dmabuf_gmr_unbind(bo);
|
|
+ ttm_mem_global_free(glob->mem_glob, bo->acc_size);
|
|
+ kfree(vmw_user_bo);
|
|
+}
|
|
+
|
|
+static void vmw_user_dmabuf_release(struct ttm_base_object **p_base)
|
|
+{
|
|
+ struct vmw_user_dma_buffer *vmw_user_bo;
|
|
+ struct ttm_base_object *base = *p_base;
|
|
+ struct ttm_buffer_object *bo;
|
|
+
|
|
+ *p_base = NULL;
|
|
+
|
|
+ if (unlikely(base == NULL))
|
|
+ return;
|
|
+
|
|
+ vmw_user_bo = container_of(base, struct vmw_user_dma_buffer, base);
|
|
+ bo = &vmw_user_bo->dma.base;
|
|
+ ttm_bo_unref(&bo);
|
|
+}
|
|
+
|
|
+int vmw_dmabuf_alloc_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct vmw_private *dev_priv = vmw_priv(dev);
|
|
+ union drm_vmw_alloc_dmabuf_arg *arg =
|
|
+ (union drm_vmw_alloc_dmabuf_arg *)data;
|
|
+ struct drm_vmw_alloc_dmabuf_req *req = &arg->req;
|
|
+ struct drm_vmw_dmabuf_rep *rep = &arg->rep;
|
|
+ struct vmw_user_dma_buffer *vmw_user_bo;
|
|
+ struct ttm_buffer_object *tmp;
|
|
+ struct vmw_master *vmaster = vmw_master(file_priv->master);
|
|
+ int ret;
|
|
+
|
|
+ vmw_user_bo = kzalloc(sizeof(*vmw_user_bo), GFP_KERNEL);
|
|
+ if (unlikely(vmw_user_bo == NULL))
|
|
+ return -ENOMEM;
|
|
+
|
|
+ ret = ttm_read_lock(&vmaster->lock, true);
|
|
+ if (unlikely(ret != 0)) {
|
|
+ kfree(vmw_user_bo);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = vmw_dmabuf_init(dev_priv, &vmw_user_bo->dma, req->size,
|
|
+ &vmw_vram_sys_placement, true,
|
|
+ &vmw_user_dmabuf_destroy);
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+
|
|
+ tmp = ttm_bo_reference(&vmw_user_bo->dma.base);
|
|
+ ret = ttm_base_object_init(vmw_fpriv(file_priv)->tfile,
|
|
+ &vmw_user_bo->base,
|
|
+ false,
|
|
+ ttm_buffer_type,
|
|
+ &vmw_user_dmabuf_release, NULL);
|
|
+ if (unlikely(ret != 0)) {
|
|
+ ttm_bo_unref(&tmp);
|
|
+ } else {
|
|
+ rep->handle = vmw_user_bo->base.hash.key;
|
|
+ rep->map_handle = vmw_user_bo->dma.base.addr_space_offset;
|
|
+ rep->cur_gmr_id = vmw_user_bo->base.hash.key;
|
|
+ rep->cur_gmr_offset = 0;
|
|
+ }
|
|
+ ttm_bo_unref(&tmp);
|
|
+
|
|
+ ttm_read_unlock(&vmaster->lock);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int vmw_dmabuf_unref_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct drm_vmw_unref_dmabuf_arg *arg =
|
|
+ (struct drm_vmw_unref_dmabuf_arg *)data;
|
|
+
|
|
+ return ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile,
|
|
+ arg->handle,
|
|
+ TTM_REF_USAGE);
|
|
+}
|
|
+
|
|
+uint32_t vmw_dmabuf_validate_node(struct ttm_buffer_object *bo,
|
|
+ uint32_t cur_validate_node)
|
|
+{
|
|
+ struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo);
|
|
+
|
|
+ if (likely(vmw_bo->on_validate_list))
|
|
+ return vmw_bo->cur_validate_node;
|
|
+
|
|
+ vmw_bo->cur_validate_node = cur_validate_node;
|
|
+ vmw_bo->on_validate_list = true;
|
|
+
|
|
+ return cur_validate_node;
|
|
+}
|
|
+
|
|
+void vmw_dmabuf_validate_clear(struct ttm_buffer_object *bo)
|
|
+{
|
|
+ struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo);
|
|
+
|
|
+ vmw_bo->on_validate_list = false;
|
|
+}
|
|
+
|
|
+uint32_t vmw_dmabuf_gmr(struct ttm_buffer_object *bo)
|
|
+{
|
|
+ struct vmw_dma_buffer *vmw_bo;
|
|
+
|
|
+ if (bo->mem.mem_type == TTM_PL_VRAM)
|
|
+ return SVGA_GMR_FRAMEBUFFER;
|
|
+
|
|
+ vmw_bo = vmw_dma_buffer(bo);
|
|
+
|
|
+ return (vmw_bo->gmr_bound) ? vmw_bo->gmr_id : SVGA_GMR_NULL;
|
|
+}
|
|
+
|
|
+void vmw_dmabuf_set_gmr(struct ttm_buffer_object *bo, uint32_t id)
|
|
+{
|
|
+ struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo);
|
|
+ vmw_bo->gmr_bound = true;
|
|
+ vmw_bo->gmr_id = id;
|
|
+}
|
|
+
|
|
+int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile,
|
|
+ uint32_t handle, struct vmw_dma_buffer **out)
|
|
+{
|
|
+ struct vmw_user_dma_buffer *vmw_user_bo;
|
|
+ struct ttm_base_object *base;
|
|
+
|
|
+ base = ttm_base_object_lookup(tfile, handle);
|
|
+ if (unlikely(base == NULL)) {
|
|
+ printk(KERN_ERR "Invalid buffer object handle 0x%08lx.\n",
|
|
+ (unsigned long)handle);
|
|
+ return -ESRCH;
|
|
+ }
|
|
+
|
|
+ if (unlikely(base->object_type != ttm_buffer_type)) {
|
|
+ ttm_base_object_unref(&base);
|
|
+ printk(KERN_ERR "Invalid buffer object handle 0x%08lx.\n",
|
|
+ (unsigned long)handle);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ vmw_user_bo = container_of(base, struct vmw_user_dma_buffer, base);
|
|
+ (void)ttm_bo_reference(&vmw_user_bo->dma.base);
|
|
+ ttm_base_object_unref(&base);
|
|
+ *out = &vmw_user_bo->dma;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * TODO: Implement a gmr id eviction mechanism. Currently we just fail
|
|
+ * when we're out of ids, causing GMR space to be allocated
|
|
+ * out of VRAM.
|
|
+ */
|
|
+
|
|
+int vmw_gmr_id_alloc(struct vmw_private *dev_priv, uint32_t *p_id)
|
|
+{
|
|
+ struct ttm_bo_global *glob = dev_priv->bdev.glob;
|
|
+ int id;
|
|
+ int ret;
|
|
+
|
|
+ do {
|
|
+ if (unlikely(ida_pre_get(&dev_priv->gmr_ida, GFP_KERNEL) == 0))
|
|
+ return -ENOMEM;
|
|
+
|
|
+ spin_lock(&glob->lru_lock);
|
|
+ ret = ida_get_new(&dev_priv->gmr_ida, &id);
|
|
+ spin_unlock(&glob->lru_lock);
|
|
+ } while (ret == -EAGAIN);
|
|
+
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+
|
|
+ if (unlikely(id >= dev_priv->max_gmr_ids)) {
|
|
+ spin_lock(&glob->lru_lock);
|
|
+ ida_remove(&dev_priv->gmr_ida, id);
|
|
+ spin_unlock(&glob->lru_lock);
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ *p_id = (uint32_t) id;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Stream managment
|
|
+ */
|
|
+
|
|
+static void vmw_stream_destroy(struct vmw_resource *res)
|
|
+{
|
|
+ struct vmw_private *dev_priv = res->dev_priv;
|
|
+ struct vmw_stream *stream;
|
|
+ int ret;
|
|
+
|
|
+ DRM_INFO("%s: unref\n", __func__);
|
|
+ stream = container_of(res, struct vmw_stream, res);
|
|
+
|
|
+ ret = vmw_overlay_unref(dev_priv, stream->stream_id);
|
|
+ WARN_ON(ret != 0);
|
|
+}
|
|
+
|
|
+static int vmw_stream_init(struct vmw_private *dev_priv,
|
|
+ struct vmw_stream *stream,
|
|
+ void (*res_free) (struct vmw_resource *res))
|
|
+{
|
|
+ struct vmw_resource *res = &stream->res;
|
|
+ int ret;
|
|
+
|
|
+ ret = vmw_resource_init(dev_priv, res, &dev_priv->stream_idr,
|
|
+ VMW_RES_STREAM, res_free);
|
|
+
|
|
+ if (unlikely(ret != 0)) {
|
|
+ if (res_free == NULL)
|
|
+ kfree(stream);
|
|
+ else
|
|
+ res_free(&stream->res);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = vmw_overlay_claim(dev_priv, &stream->stream_id);
|
|
+ if (ret) {
|
|
+ vmw_resource_unreference(&res);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ DRM_INFO("%s: claimed\n", __func__);
|
|
+
|
|
+ vmw_resource_activate(&stream->res, vmw_stream_destroy);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * User-space context management:
|
|
+ */
|
|
+
|
|
+static void vmw_user_stream_free(struct vmw_resource *res)
|
|
+{
|
|
+ struct vmw_user_stream *stream =
|
|
+ container_of(res, struct vmw_user_stream, stream.res);
|
|
+
|
|
+ kfree(stream);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * This function is called when user space has no more references on the
|
|
+ * base object. It releases the base-object's reference on the resource object.
|
|
+ */
|
|
+
|
|
+static void vmw_user_stream_base_release(struct ttm_base_object **p_base)
|
|
+{
|
|
+ struct ttm_base_object *base = *p_base;
|
|
+ struct vmw_user_stream *stream =
|
|
+ container_of(base, struct vmw_user_stream, base);
|
|
+ struct vmw_resource *res = &stream->stream.res;
|
|
+
|
|
+ *p_base = NULL;
|
|
+ vmw_resource_unreference(&res);
|
|
+}
|
|
+
|
|
+int vmw_stream_unref_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct vmw_private *dev_priv = vmw_priv(dev);
|
|
+ struct vmw_resource *res;
|
|
+ struct vmw_user_stream *stream;
|
|
+ struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data;
|
|
+ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
|
|
+ int ret = 0;
|
|
+
|
|
+ res = vmw_resource_lookup(dev_priv, &dev_priv->stream_idr, arg->stream_id);
|
|
+ if (unlikely(res == NULL))
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (res->res_free != &vmw_user_stream_free) {
|
|
+ ret = -EINVAL;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ stream = container_of(res, struct vmw_user_stream, stream.res);
|
|
+ if (stream->base.tfile != tfile) {
|
|
+ ret = -EINVAL;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ ttm_ref_object_base_unref(tfile, stream->base.hash.key, TTM_REF_USAGE);
|
|
+out:
|
|
+ vmw_resource_unreference(&res);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int vmw_stream_claim_ioctl(struct drm_device *dev, void *data,
|
|
+ struct drm_file *file_priv)
|
|
+{
|
|
+ struct vmw_private *dev_priv = vmw_priv(dev);
|
|
+ struct vmw_user_stream *stream = kmalloc(sizeof(*stream), GFP_KERNEL);
|
|
+ struct vmw_resource *res;
|
|
+ struct vmw_resource *tmp;
|
|
+ struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data;
|
|
+ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
|
|
+ int ret;
|
|
+
|
|
+ if (unlikely(stream == NULL))
|
|
+ return -ENOMEM;
|
|
+
|
|
+ res = &stream->stream.res;
|
|
+ stream->base.shareable = false;
|
|
+ stream->base.tfile = NULL;
|
|
+
|
|
+ ret = vmw_stream_init(dev_priv, &stream->stream, vmw_user_stream_free);
|
|
+ if (unlikely(ret != 0))
|
|
+ return ret;
|
|
+
|
|
+ tmp = vmw_resource_reference(res);
|
|
+ ret = ttm_base_object_init(tfile, &stream->base, false, VMW_RES_STREAM,
|
|
+ &vmw_user_stream_base_release, NULL);
|
|
+
|
|
+ if (unlikely(ret != 0)) {
|
|
+ vmw_resource_unreference(&tmp);
|
|
+ goto out_err;
|
|
+ }
|
|
+
|
|
+ arg->stream_id = res->id;
|
|
+out_err:
|
|
+ vmw_resource_unreference(&res);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int vmw_user_stream_lookup(struct vmw_private *dev_priv,
|
|
+ struct ttm_object_file *tfile,
|
|
+ uint32_t *inout_id, struct vmw_resource **out)
|
|
+{
|
|
+ struct vmw_user_stream *stream;
|
|
+ struct vmw_resource *res;
|
|
+ int ret;
|
|
+
|
|
+ res = vmw_resource_lookup(dev_priv, &dev_priv->stream_idr, *inout_id);
|
|
+ if (unlikely(res == NULL))
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (res->res_free != &vmw_user_stream_free) {
|
|
+ ret = -EINVAL;
|
|
+ goto err_ref;
|
|
+ }
|
|
+
|
|
+ stream = container_of(res, struct vmw_user_stream, stream.res);
|
|
+ if (stream->base.tfile != tfile) {
|
|
+ ret = -EPERM;
|
|
+ goto err_ref;
|
|
+ }
|
|
+
|
|
+ *inout_id = stream->stream.stream_id;
|
|
+ *out = res;
|
|
+ return 0;
|
|
+err_ref:
|
|
+ vmw_resource_unreference(&res);
|
|
+ return ret;
|
|
+}
|
|
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c
|
|
new file mode 100644
|
|
index 0000000..e3df4ad
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c
|
|
@@ -0,0 +1,99 @@
|
|
+/**************************************************************************
|
|
+ *
|
|
+ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sub license, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial portions
|
|
+ * of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ **************************************************************************/
|
|
+
|
|
+#include "drmP.h"
|
|
+#include "vmwgfx_drv.h"
|
|
+
|
|
+int vmw_mmap(struct file *filp, struct vm_area_struct *vma)
|
|
+{
|
|
+ struct drm_file *file_priv;
|
|
+ struct vmw_private *dev_priv;
|
|
+
|
|
+ if (unlikely(vma->vm_pgoff < VMWGFX_FILE_PAGE_OFFSET)) {
|
|
+ if (vmw_fifo_mmap(filp, vma) == 0)
|
|
+ return 0;
|
|
+ return drm_mmap(filp, vma);
|
|
+ }
|
|
+
|
|
+ file_priv = (struct drm_file *)filp->private_data;
|
|
+ dev_priv = vmw_priv(file_priv->minor->dev);
|
|
+ return ttm_bo_mmap(filp, vma, &dev_priv->bdev);
|
|
+}
|
|
+
|
|
+static int vmw_ttm_mem_global_init(struct ttm_global_reference *ref)
|
|
+{
|
|
+ DRM_INFO("global init.\n");
|
|
+ return ttm_mem_global_init(ref->object);
|
|
+}
|
|
+
|
|
+static void vmw_ttm_mem_global_release(struct ttm_global_reference *ref)
|
|
+{
|
|
+ ttm_mem_global_release(ref->object);
|
|
+}
|
|
+
|
|
+int vmw_ttm_global_init(struct vmw_private *dev_priv)
|
|
+{
|
|
+ struct ttm_global_reference *global_ref;
|
|
+ int ret;
|
|
+
|
|
+ global_ref = &dev_priv->mem_global_ref;
|
|
+ global_ref->global_type = TTM_GLOBAL_TTM_MEM;
|
|
+ global_ref->size = sizeof(struct ttm_mem_global);
|
|
+ global_ref->init = &vmw_ttm_mem_global_init;
|
|
+ global_ref->release = &vmw_ttm_mem_global_release;
|
|
+
|
|
+ ret = ttm_global_item_ref(global_ref);
|
|
+ if (unlikely(ret != 0)) {
|
|
+ DRM_ERROR("Failed setting up TTM memory accounting.\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ dev_priv->bo_global_ref.mem_glob =
|
|
+ dev_priv->mem_global_ref.object;
|
|
+ global_ref = &dev_priv->bo_global_ref.ref;
|
|
+ global_ref->global_type = TTM_GLOBAL_TTM_BO;
|
|
+ global_ref->size = sizeof(struct ttm_bo_global);
|
|
+ global_ref->init = &ttm_bo_global_init;
|
|
+ global_ref->release = &ttm_bo_global_release;
|
|
+ ret = ttm_global_item_ref(global_ref);
|
|
+
|
|
+ if (unlikely(ret != 0)) {
|
|
+ DRM_ERROR("Failed setting up TTM buffer objects.\n");
|
|
+ goto out_no_bo;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+out_no_bo:
|
|
+ ttm_global_item_unref(&dev_priv->mem_global_ref);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void vmw_ttm_global_release(struct vmw_private *dev_priv)
|
|
+{
|
|
+ ttm_global_item_unref(&dev_priv->bo_global_ref.ref);
|
|
+ ttm_global_item_unref(&dev_priv->mem_global_ref);
|
|
+}
|
|
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
|
|
index d21b346..260a581 100644
|
|
--- a/drivers/staging/Kconfig
|
|
+++ b/drivers/staging/Kconfig
|
|
@@ -101,8 +101,6 @@ source "drivers/staging/p9auth/Kconfig"
|
|
|
|
source "drivers/staging/line6/Kconfig"
|
|
|
|
-source "drivers/gpu/drm/radeon/Kconfig"
|
|
-
|
|
source "drivers/staging/octeon/Kconfig"
|
|
|
|
source "drivers/staging/serqt_usb2/Kconfig"
|
|
diff --git a/fs/ubifs/gc.c b/fs/ubifs/gc.c
|
|
index 618c270..e5a3d8e 100644
|
|
--- a/fs/ubifs/gc.c
|
|
+++ b/fs/ubifs/gc.c
|
|
@@ -54,6 +54,7 @@
|
|
*/
|
|
|
|
#include <linux/pagemap.h>
|
|
+#include <linux/list_sort.h>
|
|
#include "ubifs.h"
|
|
|
|
/*
|
|
@@ -108,101 +109,6 @@ static int switch_gc_head(struct ubifs_info *c)
|
|
}
|
|
|
|
/**
|
|
- * list_sort - sort a list.
|
|
- * @priv: private data, passed to @cmp
|
|
- * @head: the list to sort
|
|
- * @cmp: the elements comparison function
|
|
- *
|
|
- * This function has been implemented by Mark J Roberts <mjr@znex.org>. It
|
|
- * implements "merge sort" which has O(nlog(n)) complexity. The list is sorted
|
|
- * in ascending order.
|
|
- *
|
|
- * The comparison function @cmp is supposed to return a negative value if @a is
|
|
- * than @b, and a positive value if @a is greater than @b. If @a and @b are
|
|
- * equivalent, then it does not matter what this function returns.
|
|
- */
|
|
-static void list_sort(void *priv, struct list_head *head,
|
|
- int (*cmp)(void *priv, struct list_head *a,
|
|
- struct list_head *b))
|
|
-{
|
|
- struct list_head *p, *q, *e, *list, *tail, *oldhead;
|
|
- int insize, nmerges, psize, qsize, i;
|
|
-
|
|
- if (list_empty(head))
|
|
- return;
|
|
-
|
|
- list = head->next;
|
|
- list_del(head);
|
|
- insize = 1;
|
|
- for (;;) {
|
|
- p = oldhead = list;
|
|
- list = tail = NULL;
|
|
- nmerges = 0;
|
|
-
|
|
- while (p) {
|
|
- nmerges++;
|
|
- q = p;
|
|
- psize = 0;
|
|
- for (i = 0; i < insize; i++) {
|
|
- psize++;
|
|
- q = q->next == oldhead ? NULL : q->next;
|
|
- if (!q)
|
|
- break;
|
|
- }
|
|
-
|
|
- qsize = insize;
|
|
- while (psize > 0 || (qsize > 0 && q)) {
|
|
- if (!psize) {
|
|
- e = q;
|
|
- q = q->next;
|
|
- qsize--;
|
|
- if (q == oldhead)
|
|
- q = NULL;
|
|
- } else if (!qsize || !q) {
|
|
- e = p;
|
|
- p = p->next;
|
|
- psize--;
|
|
- if (p == oldhead)
|
|
- p = NULL;
|
|
- } else if (cmp(priv, p, q) <= 0) {
|
|
- e = p;
|
|
- p = p->next;
|
|
- psize--;
|
|
- if (p == oldhead)
|
|
- p = NULL;
|
|
- } else {
|
|
- e = q;
|
|
- q = q->next;
|
|
- qsize--;
|
|
- if (q == oldhead)
|
|
- q = NULL;
|
|
- }
|
|
- if (tail)
|
|
- tail->next = e;
|
|
- else
|
|
- list = e;
|
|
- e->prev = tail;
|
|
- tail = e;
|
|
- }
|
|
- p = q;
|
|
- }
|
|
-
|
|
- tail->next = list;
|
|
- list->prev = tail;
|
|
-
|
|
- if (nmerges <= 1)
|
|
- break;
|
|
-
|
|
- insize *= 2;
|
|
- }
|
|
-
|
|
- head->next = list;
|
|
- head->prev = list->prev;
|
|
- list->prev->next = head;
|
|
- list->prev = head;
|
|
-}
|
|
-
|
|
-/**
|
|
* data_nodes_cmp - compare 2 data nodes.
|
|
* @priv: UBIFS file-system description object
|
|
* @a: first data node
|
|
diff --git a/include/drm/Kbuild b/include/drm/Kbuild
|
|
index b940fdf..bd3a1c2 100644
|
|
--- a/include/drm/Kbuild
|
|
+++ b/include/drm/Kbuild
|
|
@@ -7,4 +7,6 @@ unifdef-y += r128_drm.h
|
|
unifdef-y += radeon_drm.h
|
|
unifdef-y += sis_drm.h
|
|
unifdef-y += savage_drm.h
|
|
+unifdef-y += vmwgfx_drm.h
|
|
unifdef-y += via_drm.h
|
|
+unifdef-y += nouveau_drm.h
|
|
diff --git a/include/drm/drm.h b/include/drm/drm.h
|
|
index 7cb50bd..e3f46e0 100644
|
|
--- a/include/drm/drm.h
|
|
+++ b/include/drm/drm.h
|
|
@@ -36,17 +36,27 @@
|
|
#ifndef _DRM_H_
|
|
#define _DRM_H_
|
|
|
|
+#if defined(__linux__)
|
|
+
|
|
#include <linux/types.h>
|
|
-#include <asm/ioctl.h> /* For _IO* macros */
|
|
-#define DRM_IOCTL_NR(n) _IOC_NR(n)
|
|
-#define DRM_IOC_VOID _IOC_NONE
|
|
-#define DRM_IOC_READ _IOC_READ
|
|
-#define DRM_IOC_WRITE _IOC_WRITE
|
|
-#define DRM_IOC_READWRITE _IOC_READ|_IOC_WRITE
|
|
-#define DRM_IOC(dir, group, nr, size) _IOC(dir, group, nr, size)
|
|
+#include <asm/ioctl.h>
|
|
+typedef unsigned int drm_handle_t;
|
|
|
|
-#define DRM_MAJOR 226
|
|
-#define DRM_MAX_MINOR 15
|
|
+#else /* One of the BSDs */
|
|
+
|
|
+#include <sys/ioccom.h>
|
|
+#include <sys/types.h>
|
|
+typedef int8_t __s8;
|
|
+typedef uint8_t __u8;
|
|
+typedef int16_t __s16;
|
|
+typedef uint16_t __u16;
|
|
+typedef int32_t __s32;
|
|
+typedef uint32_t __u32;
|
|
+typedef int64_t __s64;
|
|
+typedef uint64_t __u64;
|
|
+typedef unsigned long drm_handle_t;
|
|
+
|
|
+#endif
|
|
|
|
#define DRM_NAME "drm" /**< Name in kernel, /dev, and /proc */
|
|
#define DRM_MIN_ORDER 5 /**< At least 2^5 bytes = 32 bytes */
|
|
@@ -59,7 +69,6 @@
|
|
#define _DRM_LOCK_IS_CONT(lock) ((lock) & _DRM_LOCK_CONT)
|
|
#define _DRM_LOCKING_CONTEXT(lock) ((lock) & ~(_DRM_LOCK_HELD|_DRM_LOCK_CONT))
|
|
|
|
-typedef unsigned int drm_handle_t;
|
|
typedef unsigned int drm_context_t;
|
|
typedef unsigned int drm_drawable_t;
|
|
typedef unsigned int drm_magic_t;
|
|
@@ -454,6 +463,7 @@ struct drm_irq_busid {
|
|
enum drm_vblank_seq_type {
|
|
_DRM_VBLANK_ABSOLUTE = 0x0, /**< Wait for specific vblank sequence number */
|
|
_DRM_VBLANK_RELATIVE = 0x1, /**< Wait for given number of vblanks */
|
|
+ _DRM_VBLANK_EVENT = 0x4000000, /**< Send event instead of blocking */
|
|
_DRM_VBLANK_FLIP = 0x8000000, /**< Scheduled buffer swap should flip */
|
|
_DRM_VBLANK_NEXTONMISS = 0x10000000, /**< If missed, wait for next vblank */
|
|
_DRM_VBLANK_SECONDARY = 0x20000000, /**< Secondary display controller */
|
|
@@ -461,8 +471,8 @@ enum drm_vblank_seq_type {
|
|
};
|
|
|
|
#define _DRM_VBLANK_TYPES_MASK (_DRM_VBLANK_ABSOLUTE | _DRM_VBLANK_RELATIVE)
|
|
-#define _DRM_VBLANK_FLAGS_MASK (_DRM_VBLANK_SIGNAL | _DRM_VBLANK_SECONDARY | \
|
|
- _DRM_VBLANK_NEXTONMISS)
|
|
+#define _DRM_VBLANK_FLAGS_MASK (_DRM_VBLANK_EVENT | _DRM_VBLANK_SIGNAL | \
|
|
+ _DRM_VBLANK_SECONDARY | _DRM_VBLANK_NEXTONMISS)
|
|
|
|
struct drm_wait_vblank_request {
|
|
enum drm_vblank_seq_type type;
|
|
@@ -686,6 +696,8 @@ struct drm_gem_open {
|
|
#define DRM_IOCTL_MODE_GETFB DRM_IOWR(0xAD, struct drm_mode_fb_cmd)
|
|
#define DRM_IOCTL_MODE_ADDFB DRM_IOWR(0xAE, struct drm_mode_fb_cmd)
|
|
#define DRM_IOCTL_MODE_RMFB DRM_IOWR(0xAF, unsigned int)
|
|
+#define DRM_IOCTL_MODE_PAGE_FLIP DRM_IOWR(0xB0, struct drm_mode_crtc_page_flip)
|
|
+#define DRM_IOCTL_MODE_DIRTYFB DRM_IOWR(0xB1, struct drm_mode_fb_dirty_cmd)
|
|
|
|
/**
|
|
* Device specific ioctls should only be in their respective headers
|
|
@@ -698,6 +710,35 @@ struct drm_gem_open {
|
|
#define DRM_COMMAND_BASE 0x40
|
|
#define DRM_COMMAND_END 0xA0
|
|
|
|
+/**
|
|
+ * Header for events written back to userspace on the drm fd. The
|
|
+ * type defines the type of event, the length specifies the total
|
|
+ * length of the event (including the header), and user_data is
|
|
+ * typically a 64 bit value passed with the ioctl that triggered the
|
|
+ * event. A read on the drm fd will always only return complete
|
|
+ * events, that is, if for example the read buffer is 100 bytes, and
|
|
+ * there are two 64 byte events pending, only one will be returned.
|
|
+ *
|
|
+ * Event types 0 - 0x7fffffff are generic drm events, 0x80000000 and
|
|
+ * up are chipset specific.
|
|
+ */
|
|
+struct drm_event {
|
|
+ __u32 type;
|
|
+ __u32 length;
|
|
+};
|
|
+
|
|
+#define DRM_EVENT_VBLANK 0x01
|
|
+#define DRM_EVENT_FLIP_COMPLETE 0x02
|
|
+
|
|
+struct drm_event_vblank {
|
|
+ struct drm_event base;
|
|
+ __u64 user_data;
|
|
+ __u32 tv_sec;
|
|
+ __u32 tv_usec;
|
|
+ __u32 sequence;
|
|
+ __u32 reserved;
|
|
+};
|
|
+
|
|
/* typedef area */
|
|
#ifndef __KERNEL__
|
|
typedef struct drm_clip_rect drm_clip_rect_t;
|
|
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
|
|
index 7ad3faa..ffac157 100644
|
|
--- a/include/drm/drmP.h
|
|
+++ b/include/drm/drmP.h
|
|
@@ -245,16 +245,6 @@ extern void drm_ut_debug_printk(unsigned int request_level,
|
|
|
|
#endif
|
|
|
|
-#define DRM_PROC_LIMIT (PAGE_SIZE-80)
|
|
-
|
|
-#define DRM_PROC_PRINT(fmt, arg...) \
|
|
- len += sprintf(&buf[len], fmt , ##arg); \
|
|
- if (len > DRM_PROC_LIMIT) { *eof = 1; return len - offset; }
|
|
-
|
|
-#define DRM_PROC_PRINT_RET(ret, fmt, arg...) \
|
|
- len += sprintf(&buf[len], fmt , ##arg); \
|
|
- if (len > DRM_PROC_LIMIT) { ret; *eof = 1; return len - offset; }
|
|
-
|
|
/*@}*/
|
|
|
|
/***********************************************************************/
|
|
@@ -265,19 +255,8 @@ extern void drm_ut_debug_printk(unsigned int request_level,
|
|
|
|
#define DRM_LEFTCOUNT(x) (((x)->rp + (x)->count - (x)->wp) % ((x)->count + 1))
|
|
#define DRM_BUFCOUNT(x) ((x)->count - DRM_LEFTCOUNT(x))
|
|
-#define DRM_WAITCOUNT(dev,idx) DRM_BUFCOUNT(&dev->queuelist[idx]->waitlist)
|
|
|
|
#define DRM_IF_VERSION(maj, min) (maj << 16 | min)
|
|
-/**
|
|
- * Get the private SAREA mapping.
|
|
- *
|
|
- * \param _dev DRM device.
|
|
- * \param _ctx context number.
|
|
- * \param _map output mapping.
|
|
- */
|
|
-#define DRM_GET_PRIV_SAREA(_dev, _ctx, _map) do { \
|
|
- (_map) = (_dev)->context_sareas[_ctx]; \
|
|
-} while(0)
|
|
|
|
/**
|
|
* Test that the hardware lock is held by the caller, returning otherwise.
|
|
@@ -297,18 +276,6 @@ do { \
|
|
} while (0)
|
|
|
|
/**
|
|
- * Copy and IOCTL return string to user space
|
|
- */
|
|
-#define DRM_COPY( name, value ) \
|
|
- len = strlen( value ); \
|
|
- if ( len > name##_len ) len = name##_len; \
|
|
- name##_len = strlen( value ); \
|
|
- if ( len && name ) { \
|
|
- if ( copy_to_user( name, value, len ) ) \
|
|
- return -EFAULT; \
|
|
- }
|
|
-
|
|
-/**
|
|
* Ioctl function type.
|
|
*
|
|
* \param inode device inode.
|
|
@@ -322,10 +289,14 @@ typedef int drm_ioctl_t(struct drm_device *dev, void *data,
|
|
typedef int drm_ioctl_compat_t(struct file *filp, unsigned int cmd,
|
|
unsigned long arg);
|
|
|
|
+#define DRM_IOCTL_NR(n) _IOC_NR(n)
|
|
+#define DRM_MAJOR 226
|
|
+
|
|
#define DRM_AUTH 0x1
|
|
#define DRM_MASTER 0x2
|
|
#define DRM_ROOT_ONLY 0x4
|
|
#define DRM_CONTROL_ALLOW 0x8
|
|
+#define DRM_UNLOCKED 0x10
|
|
|
|
struct drm_ioctl_desc {
|
|
unsigned int cmd;
|
|
@@ -426,6 +397,14 @@ struct drm_buf_entry {
|
|
struct drm_freelist freelist;
|
|
};
|
|
|
|
+/* Event queued up for userspace to read */
|
|
+struct drm_pending_event {
|
|
+ struct drm_event *event;
|
|
+ struct list_head link;
|
|
+ struct drm_file *file_priv;
|
|
+ void (*destroy)(struct drm_pending_event *event);
|
|
+};
|
|
+
|
|
/** File private data */
|
|
struct drm_file {
|
|
int authenticated;
|
|
@@ -449,6 +428,10 @@ struct drm_file {
|
|
struct drm_master *master; /* master this node is currently associated with
|
|
N.B. not always minor->master */
|
|
struct list_head fbs;
|
|
+
|
|
+ wait_queue_head_t event_wait;
|
|
+ struct list_head event_list;
|
|
+ int event_space;
|
|
};
|
|
|
|
/** Wait queue */
|
|
@@ -795,6 +778,15 @@ struct drm_driver {
|
|
/* Master routines */
|
|
int (*master_create)(struct drm_device *dev, struct drm_master *master);
|
|
void (*master_destroy)(struct drm_device *dev, struct drm_master *master);
|
|
+ /**
|
|
+ * master_set is called whenever the minor master is set.
|
|
+ * master_drop is called whenever the minor master is dropped.
|
|
+ */
|
|
+
|
|
+ int (*master_set)(struct drm_device *dev, struct drm_file *file_priv,
|
|
+ bool from_open);
|
|
+ void (*master_drop)(struct drm_device *dev, struct drm_file *file_priv,
|
|
+ bool from_release);
|
|
|
|
int (*proc_init)(struct drm_minor *minor);
|
|
void (*proc_cleanup)(struct drm_minor *minor);
|
|
@@ -900,6 +892,12 @@ struct drm_minor {
|
|
struct drm_mode_group mode_group;
|
|
};
|
|
|
|
+struct drm_pending_vblank_event {
|
|
+ struct drm_pending_event base;
|
|
+ int pipe;
|
|
+ struct drm_event_vblank event;
|
|
+};
|
|
+
|
|
/**
|
|
* DRM device structure. This structure represent a complete card that
|
|
* may contain multiple heads.
|
|
@@ -999,6 +997,12 @@ struct drm_device {
|
|
|
|
u32 max_vblank_count; /**< size of vblank counter register */
|
|
|
|
+ /**
|
|
+ * List of events
|
|
+ */
|
|
+ struct list_head vblank_event_list;
|
|
+ spinlock_t event_lock;
|
|
+
|
|
/*@} */
|
|
cycles_t ctx_start;
|
|
cycles_t lck_start;
|
|
@@ -1125,8 +1129,8 @@ static inline int drm_mtrr_del(int handle, unsigned long offset,
|
|
/* Driver support (drm_drv.h) */
|
|
extern int drm_init(struct drm_driver *driver);
|
|
extern void drm_exit(struct drm_driver *driver);
|
|
-extern int drm_ioctl(struct inode *inode, struct file *filp,
|
|
- unsigned int cmd, unsigned long arg);
|
|
+extern long drm_ioctl(struct file *filp,
|
|
+ unsigned int cmd, unsigned long arg);
|
|
extern long drm_compat_ioctl(struct file *filp,
|
|
unsigned int cmd, unsigned long arg);
|
|
extern int drm_lastclose(struct drm_device *dev);
|
|
@@ -1135,6 +1139,8 @@ extern int drm_lastclose(struct drm_device *dev);
|
|
extern int drm_open(struct inode *inode, struct file *filp);
|
|
extern int drm_stub_open(struct inode *inode, struct file *filp);
|
|
extern int drm_fasync(int fd, struct file *filp, int on);
|
|
+extern ssize_t drm_read(struct file *filp, char __user *buffer,
|
|
+ size_t count, loff_t *offset);
|
|
extern int drm_release(struct inode *inode, struct file *filp);
|
|
|
|
/* Mapping support (drm_vm.h) */
|
|
@@ -1520,14 +1526,27 @@ static __inline__ void drm_core_dropmap(struct drm_local_map *map)
|
|
|
|
static __inline__ void *drm_calloc_large(size_t nmemb, size_t size)
|
|
{
|
|
+ if (size != 0 && nmemb > ULONG_MAX / size)
|
|
+ return NULL;
|
|
+
|
|
if (size * nmemb <= PAGE_SIZE)
|
|
return kcalloc(nmemb, size, GFP_KERNEL);
|
|
|
|
+ return __vmalloc(size * nmemb,
|
|
+ GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO, PAGE_KERNEL);
|
|
+}
|
|
+
|
|
+/* Modeled after cairo's malloc_ab, it's like calloc but without the zeroing. */
|
|
+static __inline__ void *drm_malloc_ab(size_t nmemb, size_t size)
|
|
+{
|
|
if (size != 0 && nmemb > ULONG_MAX / size)
|
|
return NULL;
|
|
|
|
+ if (size * nmemb <= PAGE_SIZE)
|
|
+ return kmalloc(nmemb * size, GFP_KERNEL);
|
|
+
|
|
return __vmalloc(size * nmemb,
|
|
- GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO, PAGE_KERNEL);
|
|
+ GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL);
|
|
}
|
|
|
|
static __inline void drm_free_large(void *ptr)
|
|
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
|
|
index b69347b..1347524 100644
|
|
--- a/include/drm/drm_crtc.h
|
|
+++ b/include/drm/drm_crtc.h
|
|
@@ -123,7 +123,7 @@ struct drm_display_mode {
|
|
int type;
|
|
|
|
/* Proposed mode values */
|
|
- int clock;
|
|
+ int clock; /* in kHz */
|
|
int hdisplay;
|
|
int hsync_start;
|
|
int hsync_end;
|
|
@@ -164,8 +164,8 @@ struct drm_display_mode {
|
|
int *private;
|
|
int private_flags;
|
|
|
|
- int vrefresh;
|
|
- float hsync;
|
|
+ int vrefresh; /* in Hz */
|
|
+ int hsync; /* in kHz */
|
|
};
|
|
|
|
enum drm_connector_status {
|
|
@@ -242,6 +242,21 @@ struct drm_framebuffer_funcs {
|
|
int (*create_handle)(struct drm_framebuffer *fb,
|
|
struct drm_file *file_priv,
|
|
unsigned int *handle);
|
|
+ /**
|
|
+ * Optinal callback for the dirty fb ioctl.
|
|
+ *
|
|
+ * Userspace can notify the driver via this callback
|
|
+ * that a area of the framebuffer has changed and should
|
|
+ * be flushed to the display hardware.
|
|
+ *
|
|
+ * See documentation in drm_mode.h for the struct
|
|
+ * drm_mode_fb_dirty_cmd for more information as all
|
|
+ * the semantics and arguments have a one to one mapping
|
|
+ * on this function.
|
|
+ */
|
|
+ int (*dirty)(struct drm_framebuffer *framebuffer, unsigned flags,
|
|
+ unsigned color, struct drm_clip_rect *clips,
|
|
+ unsigned num_clips);
|
|
};
|
|
|
|
struct drm_framebuffer {
|
|
@@ -256,7 +271,7 @@ struct drm_framebuffer {
|
|
unsigned int depth;
|
|
int bits_per_pixel;
|
|
int flags;
|
|
- void *fbdev;
|
|
+ struct fb_info *fbdev;
|
|
u32 pseudo_palette[17];
|
|
struct list_head filp_head;
|
|
/* if you are using the helper */
|
|
@@ -290,6 +305,7 @@ struct drm_property {
|
|
struct drm_crtc;
|
|
struct drm_connector;
|
|
struct drm_encoder;
|
|
+struct drm_pending_vblank_event;
|
|
|
|
/**
|
|
* drm_crtc_funcs - control CRTCs for a given device
|
|
@@ -333,6 +349,19 @@ struct drm_crtc_funcs {
|
|
void (*destroy)(struct drm_crtc *crtc);
|
|
|
|
int (*set_config)(struct drm_mode_set *set);
|
|
+
|
|
+ /*
|
|
+ * Flip to the given framebuffer. This implements the page
|
|
+ * flip ioctl descibed in drm_mode.h, specifically, the
|
|
+ * implementation must return immediately and block all
|
|
+ * rendering to the current fb until the flip has completed.
|
|
+ * If userspace set the event flag in the ioctl, the event
|
|
+ * argument will point to an event to send back when the flip
|
|
+ * completes, otherwise it will be NULL.
|
|
+ */
|
|
+ int (*page_flip)(struct drm_crtc *crtc,
|
|
+ struct drm_framebuffer *fb,
|
|
+ struct drm_pending_vblank_event *event);
|
|
};
|
|
|
|
/**
|
|
@@ -596,6 +625,7 @@ struct drm_mode_config {
|
|
/* Optional properties */
|
|
struct drm_property *scaling_mode_property;
|
|
struct drm_property *dithering_mode_property;
|
|
+ struct drm_property *dirty_info_property;
|
|
};
|
|
|
|
#define obj_to_crtc(x) container_of(x, struct drm_crtc, base)
|
|
@@ -667,6 +697,7 @@ extern void drm_mode_validate_size(struct drm_device *dev,
|
|
extern void drm_mode_prune_invalid(struct drm_device *dev,
|
|
struct list_head *mode_list, bool verbose);
|
|
extern void drm_mode_sort(struct list_head *mode_list);
|
|
+extern int drm_mode_hsync(struct drm_display_mode *mode);
|
|
extern int drm_mode_vrefresh(struct drm_display_mode *mode);
|
|
extern void drm_mode_set_crtcinfo(struct drm_display_mode *p,
|
|
int adjust_flags);
|
|
@@ -703,6 +734,7 @@ extern int drm_mode_create_tv_properties(struct drm_device *dev, int num_formats
|
|
char *formats[]);
|
|
extern int drm_mode_create_scaling_mode_property(struct drm_device *dev);
|
|
extern int drm_mode_create_dithering_property(struct drm_device *dev);
|
|
+extern int drm_mode_create_dirty_info_property(struct drm_device *dev);
|
|
extern char *drm_get_encoder_name(struct drm_encoder *encoder);
|
|
|
|
extern int drm_mode_connector_attach_encoder(struct drm_connector *connector,
|
|
@@ -711,7 +743,8 @@ extern void drm_mode_connector_detach_encoder(struct drm_connector *connector,
|
|
struct drm_encoder *encoder);
|
|
extern bool drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
|
|
int gamma_size);
|
|
-extern void *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type);
|
|
+extern struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
|
|
+ uint32_t id, uint32_t type);
|
|
/* IOCTLs */
|
|
extern int drm_mode_getresources(struct drm_device *dev,
|
|
void *data, struct drm_file *file_priv);
|
|
@@ -730,6 +763,8 @@ extern int drm_mode_rmfb(struct drm_device *dev,
|
|
void *data, struct drm_file *file_priv);
|
|
extern int drm_mode_getfb(struct drm_device *dev,
|
|
void *data, struct drm_file *file_priv);
|
|
+extern int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
|
|
+ void *data, struct drm_file *file_priv);
|
|
extern int drm_mode_addmode_ioctl(struct drm_device *dev,
|
|
void *data, struct drm_file *file_priv);
|
|
extern int drm_mode_rmmode_ioctl(struct drm_device *dev,
|
|
@@ -756,6 +791,8 @@ extern int drm_mode_gamma_get_ioctl(struct drm_device *dev,
|
|
extern int drm_mode_gamma_set_ioctl(struct drm_device *dev,
|
|
void *data, struct drm_file *file_priv);
|
|
extern bool drm_detect_hdmi_monitor(struct edid *edid);
|
|
+extern int drm_mode_page_flip_ioctl(struct drm_device *dev,
|
|
+ void *data, struct drm_file *file_priv);
|
|
extern struct drm_display_mode *drm_cvt_mode(struct drm_device *dev,
|
|
int hdisplay, int vdisplay, int vrefresh,
|
|
bool reduced, bool interlaced, bool margins);
|
|
@@ -764,4 +801,6 @@ extern struct drm_display_mode *drm_gtf_mode(struct drm_device *dev,
|
|
bool interlaced, int margins);
|
|
extern int drm_add_modes_noedid(struct drm_connector *connector,
|
|
int hdisplay, int vdisplay);
|
|
+
|
|
+extern bool drm_edid_is_valid(struct edid *edid);
|
|
#endif /* __DRM_CRTC_H__ */
|
|
diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
|
|
new file mode 100644
|
|
index 0000000..a49e791
|
|
--- /dev/null
|
|
+++ b/include/drm/drm_dp_helper.h
|
|
@@ -0,0 +1,180 @@
|
|
+/*
|
|
+ * Copyright © 2008 Keith Packard
|
|
+ *
|
|
+ * Permission to use, copy, modify, distribute, and sell this software and its
|
|
+ * documentation for any purpose is hereby granted without fee, provided that
|
|
+ * the above copyright notice appear in all copies and that both that copyright
|
|
+ * notice and this permission notice appear in supporting documentation, and
|
|
+ * that the name of the copyright holders not be used in advertising or
|
|
+ * publicity pertaining to distribution of the software without specific,
|
|
+ * written prior permission. The copyright holders make no representations
|
|
+ * about the suitability of this software for any purpose. It is provided "as
|
|
+ * is" without express or implied warranty.
|
|
+ *
|
|
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
|
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
|
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
|
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
|
+ * OF THIS SOFTWARE.
|
|
+ */
|
|
+
|
|
+#ifndef _DRM_DP_HELPER_H_
|
|
+#define _DRM_DP_HELPER_H_
|
|
+
|
|
+/* From the VESA DisplayPort spec */
|
|
+
|
|
+#define AUX_NATIVE_WRITE 0x8
|
|
+#define AUX_NATIVE_READ 0x9
|
|
+#define AUX_I2C_WRITE 0x0
|
|
+#define AUX_I2C_READ 0x1
|
|
+#define AUX_I2C_STATUS 0x2
|
|
+#define AUX_I2C_MOT 0x4
|
|
+
|
|
+#define AUX_NATIVE_REPLY_ACK (0x0 << 4)
|
|
+#define AUX_NATIVE_REPLY_NACK (0x1 << 4)
|
|
+#define AUX_NATIVE_REPLY_DEFER (0x2 << 4)
|
|
+#define AUX_NATIVE_REPLY_MASK (0x3 << 4)
|
|
+
|
|
+#define AUX_I2C_REPLY_ACK (0x0 << 6)
|
|
+#define AUX_I2C_REPLY_NACK (0x1 << 6)
|
|
+#define AUX_I2C_REPLY_DEFER (0x2 << 6)
|
|
+#define AUX_I2C_REPLY_MASK (0x3 << 6)
|
|
+
|
|
+/* AUX CH addresses */
|
|
+/* DPCD */
|
|
+#define DP_DPCD_REV 0x000
|
|
+
|
|
+#define DP_MAX_LINK_RATE 0x001
|
|
+
|
|
+#define DP_MAX_LANE_COUNT 0x002
|
|
+# define DP_MAX_LANE_COUNT_MASK 0x1f
|
|
+# define DP_ENHANCED_FRAME_CAP (1 << 7)
|
|
+
|
|
+#define DP_MAX_DOWNSPREAD 0x003
|
|
+# define DP_NO_AUX_HANDSHAKE_LINK_TRAINING (1 << 6)
|
|
+
|
|
+#define DP_NORP 0x004
|
|
+
|
|
+#define DP_DOWNSTREAMPORT_PRESENT 0x005
|
|
+# define DP_DWN_STRM_PORT_PRESENT (1 << 0)
|
|
+# define DP_DWN_STRM_PORT_TYPE_MASK 0x06
|
|
+/* 00b = DisplayPort */
|
|
+/* 01b = Analog */
|
|
+/* 10b = TMDS or HDMI */
|
|
+/* 11b = Other */
|
|
+# define DP_FORMAT_CONVERSION (1 << 3)
|
|
+
|
|
+#define DP_MAIN_LINK_CHANNEL_CODING 0x006
|
|
+
|
|
+/* link configuration */
|
|
+#define DP_LINK_BW_SET 0x100
|
|
+# define DP_LINK_BW_1_62 0x06
|
|
+# define DP_LINK_BW_2_7 0x0a
|
|
+
|
|
+#define DP_LANE_COUNT_SET 0x101
|
|
+# define DP_LANE_COUNT_MASK 0x0f
|
|
+# define DP_LANE_COUNT_ENHANCED_FRAME_EN (1 << 7)
|
|
+
|
|
+#define DP_TRAINING_PATTERN_SET 0x102
|
|
+# define DP_TRAINING_PATTERN_DISABLE 0
|
|
+# define DP_TRAINING_PATTERN_1 1
|
|
+# define DP_TRAINING_PATTERN_2 2
|
|
+# define DP_TRAINING_PATTERN_MASK 0x3
|
|
+
|
|
+# define DP_LINK_QUAL_PATTERN_DISABLE (0 << 2)
|
|
+# define DP_LINK_QUAL_PATTERN_D10_2 (1 << 2)
|
|
+# define DP_LINK_QUAL_PATTERN_ERROR_RATE (2 << 2)
|
|
+# define DP_LINK_QUAL_PATTERN_PRBS7 (3 << 2)
|
|
+# define DP_LINK_QUAL_PATTERN_MASK (3 << 2)
|
|
+
|
|
+# define DP_RECOVERED_CLOCK_OUT_EN (1 << 4)
|
|
+# define DP_LINK_SCRAMBLING_DISABLE (1 << 5)
|
|
+
|
|
+# define DP_SYMBOL_ERROR_COUNT_BOTH (0 << 6)
|
|
+# define DP_SYMBOL_ERROR_COUNT_DISPARITY (1 << 6)
|
|
+# define DP_SYMBOL_ERROR_COUNT_SYMBOL (2 << 6)
|
|
+# define DP_SYMBOL_ERROR_COUNT_MASK (3 << 6)
|
|
+
|
|
+#define DP_TRAINING_LANE0_SET 0x103
|
|
+#define DP_TRAINING_LANE1_SET 0x104
|
|
+#define DP_TRAINING_LANE2_SET 0x105
|
|
+#define DP_TRAINING_LANE3_SET 0x106
|
|
+
|
|
+# define DP_TRAIN_VOLTAGE_SWING_MASK 0x3
|
|
+# define DP_TRAIN_VOLTAGE_SWING_SHIFT 0
|
|
+# define DP_TRAIN_MAX_SWING_REACHED (1 << 2)
|
|
+# define DP_TRAIN_VOLTAGE_SWING_400 (0 << 0)
|
|
+# define DP_TRAIN_VOLTAGE_SWING_600 (1 << 0)
|
|
+# define DP_TRAIN_VOLTAGE_SWING_800 (2 << 0)
|
|
+# define DP_TRAIN_VOLTAGE_SWING_1200 (3 << 0)
|
|
+
|
|
+# define DP_TRAIN_PRE_EMPHASIS_MASK (3 << 3)
|
|
+# define DP_TRAIN_PRE_EMPHASIS_0 (0 << 3)
|
|
+# define DP_TRAIN_PRE_EMPHASIS_3_5 (1 << 3)
|
|
+# define DP_TRAIN_PRE_EMPHASIS_6 (2 << 3)
|
|
+# define DP_TRAIN_PRE_EMPHASIS_9_5 (3 << 3)
|
|
+
|
|
+# define DP_TRAIN_PRE_EMPHASIS_SHIFT 3
|
|
+# define DP_TRAIN_MAX_PRE_EMPHASIS_REACHED (1 << 5)
|
|
+
|
|
+#define DP_DOWNSPREAD_CTRL 0x107
|
|
+# define DP_SPREAD_AMP_0_5 (1 << 4)
|
|
+
|
|
+#define DP_MAIN_LINK_CHANNEL_CODING_SET 0x108
|
|
+# define DP_SET_ANSI_8B10B (1 << 0)
|
|
+
|
|
+#define DP_LANE0_1_STATUS 0x202
|
|
+#define DP_LANE2_3_STATUS 0x203
|
|
+# define DP_LANE_CR_DONE (1 << 0)
|
|
+# define DP_LANE_CHANNEL_EQ_DONE (1 << 1)
|
|
+# define DP_LANE_SYMBOL_LOCKED (1 << 2)
|
|
+
|
|
+#define DP_CHANNEL_EQ_BITS (DP_LANE_CR_DONE | \
|
|
+ DP_LANE_CHANNEL_EQ_DONE | \
|
|
+ DP_LANE_SYMBOL_LOCKED)
|
|
+
|
|
+#define DP_LANE_ALIGN_STATUS_UPDATED 0x204
|
|
+
|
|
+#define DP_INTERLANE_ALIGN_DONE (1 << 0)
|
|
+#define DP_DOWNSTREAM_PORT_STATUS_CHANGED (1 << 6)
|
|
+#define DP_LINK_STATUS_UPDATED (1 << 7)
|
|
+
|
|
+#define DP_SINK_STATUS 0x205
|
|
+
|
|
+#define DP_RECEIVE_PORT_0_STATUS (1 << 0)
|
|
+#define DP_RECEIVE_PORT_1_STATUS (1 << 1)
|
|
+
|
|
+#define DP_ADJUST_REQUEST_LANE0_1 0x206
|
|
+#define DP_ADJUST_REQUEST_LANE2_3 0x207
|
|
+# define DP_ADJUST_VOLTAGE_SWING_LANE0_MASK 0x03
|
|
+# define DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT 0
|
|
+# define DP_ADJUST_PRE_EMPHASIS_LANE0_MASK 0x0c
|
|
+# define DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT 2
|
|
+# define DP_ADJUST_VOLTAGE_SWING_LANE1_MASK 0x30
|
|
+# define DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT 4
|
|
+# define DP_ADJUST_PRE_EMPHASIS_LANE1_MASK 0xc0
|
|
+# define DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT 6
|
|
+
|
|
+#define DP_SET_POWER 0x600
|
|
+# define DP_SET_POWER_D0 0x1
|
|
+# define DP_SET_POWER_D3 0x2
|
|
+
|
|
+#define MODE_I2C_START 1
|
|
+#define MODE_I2C_WRITE 2
|
|
+#define MODE_I2C_READ 4
|
|
+#define MODE_I2C_STOP 8
|
|
+
|
|
+struct i2c_algo_dp_aux_data {
|
|
+ bool running;
|
|
+ u16 address;
|
|
+ int (*aux_ch) (struct i2c_adapter *adapter,
|
|
+ int mode, uint8_t write_byte,
|
|
+ uint8_t *read_byte);
|
|
+};
|
|
+
|
|
+int
|
|
+i2c_dp_aux_add_bus(struct i2c_adapter *adapter);
|
|
+
|
|
+#endif /* _DRM_DP_HELPER_H_ */
|
|
diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
|
|
index 7d6c9a2..b420989 100644
|
|
--- a/include/drm/drm_edid.h
|
|
+++ b/include/drm/drm_edid.h
|
|
@@ -106,6 +106,10 @@ struct detailed_data_color_point {
|
|
u8 wpindex2[3];
|
|
} __attribute__((packed));
|
|
|
|
+struct cvt_timing {
|
|
+ u8 code[3];
|
|
+} __attribute__((packed));
|
|
+
|
|
struct detailed_non_pixel {
|
|
u8 pad1;
|
|
u8 type; /* ff=serial, fe=string, fd=monitor range, fc=monitor name
|
|
@@ -117,9 +121,13 @@ struct detailed_non_pixel {
|
|
struct detailed_data_monitor_range range;
|
|
struct detailed_data_wpindex color;
|
|
struct std_timing timings[5];
|
|
+ struct cvt_timing cvt[4];
|
|
} data;
|
|
} __attribute__((packed));
|
|
|
|
+#define EDID_DETAIL_EST_TIMINGS 0xf7
|
|
+#define EDID_DETAIL_CVT_3BYTE 0xf8
|
|
+#define EDID_DETAIL_COLOR_MGMT_DATA 0xf9
|
|
#define EDID_DETAIL_STD_MODES 0xfa
|
|
#define EDID_DETAIL_MONITOR_CPDATA 0xfb
|
|
#define EDID_DETAIL_MONITOR_NAME 0xfc
|
|
@@ -193,4 +201,7 @@ struct edid {
|
|
|
|
#define EDID_PRODUCT_ID(e) ((e)->prod_code[0] | ((e)->prod_code[1] << 8))
|
|
|
|
+/* define the number of Extension EDID block */
|
|
+#define DRM_MAX_EDID_EXT_NUM 4
|
|
+
|
|
#endif /* __DRM_EDID_H__ */
|
|
diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h
|
|
index 62329f9..4c10be3 100644
|
|
--- a/include/drm/drm_mm.h
|
|
+++ b/include/drm/drm_mm.h
|
|
@@ -66,6 +66,13 @@ extern struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *node,
|
|
unsigned long size,
|
|
unsigned alignment,
|
|
int atomic);
|
|
+extern struct drm_mm_node *drm_mm_get_block_range_generic(
|
|
+ struct drm_mm_node *node,
|
|
+ unsigned long size,
|
|
+ unsigned alignment,
|
|
+ unsigned long start,
|
|
+ unsigned long end,
|
|
+ int atomic);
|
|
static inline struct drm_mm_node *drm_mm_get_block(struct drm_mm_node *parent,
|
|
unsigned long size,
|
|
unsigned alignment)
|
|
@@ -78,11 +85,38 @@ static inline struct drm_mm_node *drm_mm_get_block_atomic(struct drm_mm_node *pa
|
|
{
|
|
return drm_mm_get_block_generic(parent, size, alignment, 1);
|
|
}
|
|
+static inline struct drm_mm_node *drm_mm_get_block_range(
|
|
+ struct drm_mm_node *parent,
|
|
+ unsigned long size,
|
|
+ unsigned alignment,
|
|
+ unsigned long start,
|
|
+ unsigned long end)
|
|
+{
|
|
+ return drm_mm_get_block_range_generic(parent, size, alignment,
|
|
+ start, end, 0);
|
|
+}
|
|
+static inline struct drm_mm_node *drm_mm_get_block_atomic_range(
|
|
+ struct drm_mm_node *parent,
|
|
+ unsigned long size,
|
|
+ unsigned alignment,
|
|
+ unsigned long start,
|
|
+ unsigned long end)
|
|
+{
|
|
+ return drm_mm_get_block_range_generic(parent, size, alignment,
|
|
+ start, end, 1);
|
|
+}
|
|
extern void drm_mm_put_block(struct drm_mm_node *cur);
|
|
extern struct drm_mm_node *drm_mm_search_free(const struct drm_mm *mm,
|
|
unsigned long size,
|
|
unsigned alignment,
|
|
int best_match);
|
|
+extern struct drm_mm_node *drm_mm_search_free_in_range(
|
|
+ const struct drm_mm *mm,
|
|
+ unsigned long size,
|
|
+ unsigned alignment,
|
|
+ unsigned long start,
|
|
+ unsigned long end,
|
|
+ int best_match);
|
|
extern int drm_mm_init(struct drm_mm *mm, unsigned long start,
|
|
unsigned long size);
|
|
extern void drm_mm_takedown(struct drm_mm *mm);
|
|
@@ -99,6 +133,7 @@ static inline struct drm_mm *drm_get_mm(struct drm_mm_node *block)
|
|
return block->mm;
|
|
}
|
|
|
|
+extern void drm_mm_debug_table(struct drm_mm *mm, const char *prefix);
|
|
#ifdef CONFIG_DEBUG_FS
|
|
int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm);
|
|
#endif
|
|
diff --git a/include/drm/drm_mode.h b/include/drm/drm_mode.h
|
|
index 1f90841..c5ba163 100644
|
|
--- a/include/drm/drm_mode.h
|
|
+++ b/include/drm/drm_mode.h
|
|
@@ -27,9 +27,6 @@
|
|
#ifndef _DRM_MODE_H
|
|
#define _DRM_MODE_H
|
|
|
|
-#include <linux/kernel.h>
|
|
-#include <linux/types.h>
|
|
-
|
|
#define DRM_DISPLAY_INFO_LEN 32
|
|
#define DRM_CONNECTOR_NAME_LEN 32
|
|
#define DRM_DISPLAY_MODE_LEN 32
|
|
@@ -78,12 +75,17 @@
|
|
#define DRM_MODE_DITHERING_OFF 0
|
|
#define DRM_MODE_DITHERING_ON 1
|
|
|
|
+/* Dirty info options */
|
|
+#define DRM_MODE_DIRTY_OFF 0
|
|
+#define DRM_MODE_DIRTY_ON 1
|
|
+#define DRM_MODE_DIRTY_ANNOTATE 2
|
|
+
|
|
struct drm_mode_modeinfo {
|
|
__u32 clock;
|
|
__u16 hdisplay, hsync_start, hsync_end, htotal, hskew;
|
|
__u16 vdisplay, vsync_start, vsync_end, vtotal, vscan;
|
|
|
|
- __u32 vrefresh; /* vertical refresh * 1000 */
|
|
+ __u32 vrefresh;
|
|
|
|
__u32 flags;
|
|
__u32 type;
|
|
@@ -158,6 +160,7 @@ struct drm_mode_get_encoder {
|
|
#define DRM_MODE_CONNECTOR_HDMIA 11
|
|
#define DRM_MODE_CONNECTOR_HDMIB 12
|
|
#define DRM_MODE_CONNECTOR_TV 13
|
|
+#define DRM_MODE_CONNECTOR_eDP 14
|
|
|
|
struct drm_mode_get_connector {
|
|
|
|
@@ -225,6 +228,45 @@ struct drm_mode_fb_cmd {
|
|
__u32 handle;
|
|
};
|
|
|
|
+#define DRM_MODE_FB_DIRTY_ANNOTATE_COPY 0x01
|
|
+#define DRM_MODE_FB_DIRTY_ANNOTATE_FILL 0x02
|
|
+#define DRM_MODE_FB_DIRTY_FLAGS 0x03
|
|
+
|
|
+/*
|
|
+ * Mark a region of a framebuffer as dirty.
|
|
+ *
|
|
+ * Some hardware does not automatically update display contents
|
|
+ * as a hardware or software draw to a framebuffer. This ioctl
|
|
+ * allows userspace to tell the kernel and the hardware what
|
|
+ * regions of the framebuffer have changed.
|
|
+ *
|
|
+ * The kernel or hardware is free to update more then just the
|
|
+ * region specified by the clip rects. The kernel or hardware
|
|
+ * may also delay and/or coalesce several calls to dirty into a
|
|
+ * single update.
|
|
+ *
|
|
+ * Userspace may annotate the updates, the annotates are a
|
|
+ * promise made by the caller that the change is either a copy
|
|
+ * of pixels or a fill of a single color in the region specified.
|
|
+ *
|
|
+ * If the DRM_MODE_FB_DIRTY_ANNOTATE_COPY flag is given then
|
|
+ * the number of updated regions are half of num_clips given,
|
|
+ * where the clip rects are paired in src and dst. The width and
|
|
+ * height of each one of the pairs must match.
|
|
+ *
|
|
+ * If the DRM_MODE_FB_DIRTY_ANNOTATE_FILL flag is given the caller
|
|
+ * promises that the region specified of the clip rects is filled
|
|
+ * completely with a single color as given in the color argument.
|
|
+ */
|
|
+
|
|
+struct drm_mode_fb_dirty_cmd {
|
|
+ __u32 fb_id;
|
|
+ __u32 flags;
|
|
+ __u32 color;
|
|
+ __u32 num_clips;
|
|
+ __u64 clips_ptr;
|
|
+};
|
|
+
|
|
struct drm_mode_mode_cmd {
|
|
__u32 connector_id;
|
|
struct drm_mode_modeinfo mode;
|
|
@@ -268,4 +310,37 @@ struct drm_mode_crtc_lut {
|
|
__u64 blue;
|
|
};
|
|
|
|
+#define DRM_MODE_PAGE_FLIP_EVENT 0x01
|
|
+#define DRM_MODE_PAGE_FLIP_FLAGS DRM_MODE_PAGE_FLIP_EVENT
|
|
+
|
|
+/*
|
|
+ * Request a page flip on the specified crtc.
|
|
+ *
|
|
+ * This ioctl will ask KMS to schedule a page flip for the specified
|
|
+ * crtc. Once any pending rendering targeting the specified fb (as of
|
|
+ * ioctl time) has completed, the crtc will be reprogrammed to display
|
|
+ * that fb after the next vertical refresh. The ioctl returns
|
|
+ * immediately, but subsequent rendering to the current fb will block
|
|
+ * in the execbuffer ioctl until the page flip happens. If a page
|
|
+ * flip is already pending as the ioctl is called, EBUSY will be
|
|
+ * returned.
|
|
+ *
|
|
+ * The ioctl supports one flag, DRM_MODE_PAGE_FLIP_EVENT, which will
|
|
+ * request that drm sends back a vblank event (see drm.h: struct
|
|
+ * drm_event_vblank) when the page flip is done. The user_data field
|
|
+ * passed in with this ioctl will be returned as the user_data field
|
|
+ * in the vblank event struct.
|
|
+ *
|
|
+ * The reserved field must be zero until we figure out something
|
|
+ * clever to use it for.
|
|
+ */
|
|
+
|
|
+struct drm_mode_crtc_page_flip {
|
|
+ __u32 crtc_id;
|
|
+ __u32 fb_id;
|
|
+ __u32 flags;
|
|
+ __u32 reserved;
|
|
+ __u64 user_data;
|
|
+};
|
|
+
|
|
#endif
|
|
diff --git a/include/drm/drm_os_linux.h b/include/drm/drm_os_linux.h
|
|
index 26641e9..3933691 100644
|
|
--- a/include/drm/drm_os_linux.h
|
|
+++ b/include/drm/drm_os_linux.h
|
|
@@ -123,5 +123,5 @@ do { \
|
|
remove_wait_queue(&(queue), &entry); \
|
|
} while (0)
|
|
|
|
-#define DRM_WAKEUP( queue ) wake_up_interruptible( queue )
|
|
+#define DRM_WAKEUP( queue ) wake_up( queue )
|
|
#define DRM_INIT_WAITQUEUE( queue ) init_waitqueue_head( queue )
|
|
diff --git a/include/drm/i2c/ch7006.h b/include/drm/i2c/ch7006.h
|
|
new file mode 100644
|
|
index 0000000..8390b43
|
|
--- /dev/null
|
|
+++ b/include/drm/i2c/ch7006.h
|
|
@@ -0,0 +1,86 @@
|
|
+/*
|
|
+ * Copyright (C) 2009 Francisco Jerez.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
+ * a copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial
|
|
+ * portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef __DRM_I2C_CH7006_H__
|
|
+#define __DRM_I2C_CH7006_H__
|
|
+
|
|
+/**
|
|
+ * struct ch7006_encoder_params
|
|
+ *
|
|
+ * Describes how the ch7006 is wired up with the GPU. It should be
|
|
+ * used as the @params parameter of its @set_config method.
|
|
+ *
|
|
+ * See "http://www.chrontel.com/pdf/7006.pdf" for their precise
|
|
+ * meaning.
|
|
+ */
|
|
+struct ch7006_encoder_params {
|
|
+ enum {
|
|
+ CH7006_FORMAT_RGB16 = 0,
|
|
+ CH7006_FORMAT_YCrCb24m16,
|
|
+ CH7006_FORMAT_RGB24m16,
|
|
+ CH7006_FORMAT_RGB15,
|
|
+ CH7006_FORMAT_RGB24m12C,
|
|
+ CH7006_FORMAT_RGB24m12I,
|
|
+ CH7006_FORMAT_RGB24m8,
|
|
+ CH7006_FORMAT_RGB16m8,
|
|
+ CH7006_FORMAT_RGB15m8,
|
|
+ CH7006_FORMAT_YCrCb24m8,
|
|
+ } input_format;
|
|
+
|
|
+ enum {
|
|
+ CH7006_CLOCK_SLAVE = 0,
|
|
+ CH7006_CLOCK_MASTER,
|
|
+ } clock_mode;
|
|
+
|
|
+ enum {
|
|
+ CH7006_CLOCK_EDGE_NEG = 0,
|
|
+ CH7006_CLOCK_EDGE_POS,
|
|
+ } clock_edge;
|
|
+
|
|
+ int xcm, pcm;
|
|
+
|
|
+ enum {
|
|
+ CH7006_SYNC_SLAVE = 0,
|
|
+ CH7006_SYNC_MASTER,
|
|
+ } sync_direction;
|
|
+
|
|
+ enum {
|
|
+ CH7006_SYNC_SEPARATED = 0,
|
|
+ CH7006_SYNC_EMBEDDED,
|
|
+ } sync_encoding;
|
|
+
|
|
+ enum {
|
|
+ CH7006_POUT_1_8V = 0,
|
|
+ CH7006_POUT_3_3V,
|
|
+ } pout_level;
|
|
+
|
|
+ enum {
|
|
+ CH7006_ACTIVE_HSYNC = 0,
|
|
+ CH7006_ACTIVE_DSTART,
|
|
+ } active_detect;
|
|
+};
|
|
+
|
|
+#endif
|
|
diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h
|
|
index 7e0cb1d..b64a8d7 100644
|
|
--- a/include/drm/i915_drm.h
|
|
+++ b/include/drm/i915_drm.h
|
|
@@ -27,11 +27,11 @@
|
|
#ifndef _I915_DRM_H_
|
|
#define _I915_DRM_H_
|
|
|
|
+#include "drm.h"
|
|
+
|
|
/* Please note that modifications to all structs defined here are
|
|
* subject to backwards-compatibility constraints.
|
|
*/
|
|
-#include <linux/types.h>
|
|
-#include "drm.h"
|
|
|
|
/* Each region is a minimum of 16k, and there are at most 255 of them.
|
|
*/
|
|
@@ -186,6 +186,9 @@ typedef struct _drm_i915_sarea {
|
|
#define DRM_I915_GEM_MMAP_GTT 0x24
|
|
#define DRM_I915_GET_PIPE_FROM_CRTC_ID 0x25
|
|
#define DRM_I915_GEM_MADVISE 0x26
|
|
+#define DRM_I915_OVERLAY_PUT_IMAGE 0x27
|
|
+#define DRM_I915_OVERLAY_ATTRS 0x28
|
|
+#define DRM_I915_GEM_EXECBUFFER2 0x29
|
|
|
|
#define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t)
|
|
#define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH)
|
|
@@ -205,6 +208,7 @@ typedef struct _drm_i915_sarea {
|
|
#define DRM_IOCTL_I915_VBLANK_SWAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_VBLANK_SWAP, drm_i915_vblank_swap_t)
|
|
#define DRM_IOCTL_I915_GEM_INIT DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_INIT, struct drm_i915_gem_init)
|
|
#define DRM_IOCTL_I915_GEM_EXECBUFFER DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER, struct drm_i915_gem_execbuffer)
|
|
+#define DRM_IOCTL_I915_GEM_EXECBUFFER2 DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER2, struct drm_i915_gem_execbuffer2)
|
|
#define DRM_IOCTL_I915_GEM_PIN DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_PIN, struct drm_i915_gem_pin)
|
|
#define DRM_IOCTL_I915_GEM_UNPIN DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_UNPIN, struct drm_i915_gem_unpin)
|
|
#define DRM_IOCTL_I915_GEM_BUSY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_BUSY, struct drm_i915_gem_busy)
|
|
@@ -221,8 +225,10 @@ typedef struct _drm_i915_sarea {
|
|
#define DRM_IOCTL_I915_GEM_SET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_SET_TILING, struct drm_i915_gem_set_tiling)
|
|
#define DRM_IOCTL_I915_GEM_GET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_TILING, struct drm_i915_gem_get_tiling)
|
|
#define DRM_IOCTL_I915_GEM_GET_APERTURE DRM_IOR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_APERTURE, struct drm_i915_gem_get_aperture)
|
|
-#define DRM_IOCTL_I915_GET_PIPE_FROM_CRTC_ID DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GET_PIPE_FROM_CRTC_ID, struct drm_intel_get_pipe_from_crtc_id)
|
|
+#define DRM_IOCTL_I915_GET_PIPE_FROM_CRTC_ID DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GET_PIPE_FROM_CRTC_ID, struct drm_i915_get_pipe_from_crtc_id)
|
|
#define DRM_IOCTL_I915_GEM_MADVISE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MADVISE, struct drm_i915_gem_madvise)
|
|
+#define DRM_IOCTL_I915_OVERLAY_PUT_IMAGE DRM_IOW(DRM_COMMAND_BASE + DRM_IOCTL_I915_OVERLAY_ATTRS, struct drm_intel_overlay_put_image)
|
|
+#define DRM_IOCTL_I915_OVERLAY_ATTRS DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_OVERLAY_ATTRS, struct drm_intel_overlay_attrs)
|
|
|
|
/* Allow drivers to submit batchbuffers directly to hardware, relying
|
|
* on the security mechanisms provided by hardware.
|
|
@@ -266,6 +272,9 @@ typedef struct drm_i915_irq_wait {
|
|
#define I915_PARAM_CHIPSET_ID 4
|
|
#define I915_PARAM_HAS_GEM 5
|
|
#define I915_PARAM_NUM_FENCES_AVAIL 6
|
|
+#define I915_PARAM_HAS_OVERLAY 7
|
|
+#define I915_PARAM_HAS_PAGEFLIPPING 8
|
|
+#define I915_PARAM_HAS_EXECBUF2 9
|
|
|
|
typedef struct drm_i915_getparam {
|
|
int param;
|
|
@@ -561,6 +570,57 @@ struct drm_i915_gem_execbuffer {
|
|
__u64 cliprects_ptr;
|
|
};
|
|
|
|
+struct drm_i915_gem_exec_object2 {
|
|
+ /**
|
|
+ * User's handle for a buffer to be bound into the GTT for this
|
|
+ * operation.
|
|
+ */
|
|
+ __u32 handle;
|
|
+
|
|
+ /** Number of relocations to be performed on this buffer */
|
|
+ __u32 relocation_count;
|
|
+ /**
|
|
+ * Pointer to array of struct drm_i915_gem_relocation_entry containing
|
|
+ * the relocations to be performed in this buffer.
|
|
+ */
|
|
+ __u64 relocs_ptr;
|
|
+
|
|
+ /** Required alignment in graphics aperture */
|
|
+ __u64 alignment;
|
|
+
|
|
+ /**
|
|
+ * Returned value of the updated offset of the object, for future
|
|
+ * presumed_offset writes.
|
|
+ */
|
|
+ __u64 offset;
|
|
+
|
|
+#define EXEC_OBJECT_NEEDS_FENCE (1<<0)
|
|
+ __u64 flags;
|
|
+ __u64 rsvd1;
|
|
+ __u64 rsvd2;
|
|
+};
|
|
+
|
|
+struct drm_i915_gem_execbuffer2 {
|
|
+ /**
|
|
+ * List of gem_exec_object2 structs
|
|
+ */
|
|
+ __u64 buffers_ptr;
|
|
+ __u32 buffer_count;
|
|
+
|
|
+ /** Offset in the batchbuffer to start execution from. */
|
|
+ __u32 batch_start_offset;
|
|
+ /** Bytes used in batchbuffer from batch_start_offset */
|
|
+ __u32 batch_len;
|
|
+ __u32 DR1;
|
|
+ __u32 DR4;
|
|
+ __u32 num_cliprects;
|
|
+ /** This is a struct drm_clip_rect *cliprects */
|
|
+ __u64 cliprects_ptr;
|
|
+ __u64 flags; /* currently unused */
|
|
+ __u64 rsvd1;
|
|
+ __u64 rsvd2;
|
|
+};
|
|
+
|
|
struct drm_i915_gem_pin {
|
|
/** Handle of the buffer to be pinned. */
|
|
__u32 handle;
|
|
@@ -686,4 +746,70 @@ struct drm_i915_gem_madvise {
|
|
__u32 retained;
|
|
};
|
|
|
|
+/* flags */
|
|
+#define I915_OVERLAY_TYPE_MASK 0xff
|
|
+#define I915_OVERLAY_YUV_PLANAR 0x01
|
|
+#define I915_OVERLAY_YUV_PACKED 0x02
|
|
+#define I915_OVERLAY_RGB 0x03
|
|
+
|
|
+#define I915_OVERLAY_DEPTH_MASK 0xff00
|
|
+#define I915_OVERLAY_RGB24 0x1000
|
|
+#define I915_OVERLAY_RGB16 0x2000
|
|
+#define I915_OVERLAY_RGB15 0x3000
|
|
+#define I915_OVERLAY_YUV422 0x0100
|
|
+#define I915_OVERLAY_YUV411 0x0200
|
|
+#define I915_OVERLAY_YUV420 0x0300
|
|
+#define I915_OVERLAY_YUV410 0x0400
|
|
+
|
|
+#define I915_OVERLAY_SWAP_MASK 0xff0000
|
|
+#define I915_OVERLAY_NO_SWAP 0x000000
|
|
+#define I915_OVERLAY_UV_SWAP 0x010000
|
|
+#define I915_OVERLAY_Y_SWAP 0x020000
|
|
+#define I915_OVERLAY_Y_AND_UV_SWAP 0x030000
|
|
+
|
|
+#define I915_OVERLAY_FLAGS_MASK 0xff000000
|
|
+#define I915_OVERLAY_ENABLE 0x01000000
|
|
+
|
|
+struct drm_intel_overlay_put_image {
|
|
+ /* various flags and src format description */
|
|
+ __u32 flags;
|
|
+ /* source picture description */
|
|
+ __u32 bo_handle;
|
|
+ /* stride values and offsets are in bytes, buffer relative */
|
|
+ __u16 stride_Y; /* stride for packed formats */
|
|
+ __u16 stride_UV;
|
|
+ __u32 offset_Y; /* offset for packet formats */
|
|
+ __u32 offset_U;
|
|
+ __u32 offset_V;
|
|
+ /* in pixels */
|
|
+ __u16 src_width;
|
|
+ __u16 src_height;
|
|
+ /* to compensate the scaling factors for partially covered surfaces */
|
|
+ __u16 src_scan_width;
|
|
+ __u16 src_scan_height;
|
|
+ /* output crtc description */
|
|
+ __u32 crtc_id;
|
|
+ __u16 dst_x;
|
|
+ __u16 dst_y;
|
|
+ __u16 dst_width;
|
|
+ __u16 dst_height;
|
|
+};
|
|
+
|
|
+/* flags */
|
|
+#define I915_OVERLAY_UPDATE_ATTRS (1<<0)
|
|
+#define I915_OVERLAY_UPDATE_GAMMA (1<<1)
|
|
+struct drm_intel_overlay_attrs {
|
|
+ __u32 flags;
|
|
+ __u32 color_key;
|
|
+ __s32 brightness;
|
|
+ __u32 contrast;
|
|
+ __u32 saturation;
|
|
+ __u32 gamma0;
|
|
+ __u32 gamma1;
|
|
+ __u32 gamma2;
|
|
+ __u32 gamma3;
|
|
+ __u32 gamma4;
|
|
+ __u32 gamma5;
|
|
+};
|
|
+
|
|
#endif /* _I915_DRM_H_ */
|
|
diff --git a/include/drm/mga_drm.h b/include/drm/mga_drm.h
|
|
index 325fd6f..3ffbc47 100644
|
|
--- a/include/drm/mga_drm.h
|
|
+++ b/include/drm/mga_drm.h
|
|
@@ -35,7 +35,7 @@
|
|
#ifndef __MGA_DRM_H__
|
|
#define __MGA_DRM_H__
|
|
|
|
-#include <linux/types.h>
|
|
+#include "drm.h"
|
|
|
|
/* WARNING: If you change any of these defines, make sure to change the
|
|
* defines in the Xserver file (mga_sarea.h)
|
|
diff --git a/include/drm/nouveau_drm.h b/include/drm/nouveau_drm.h
|
|
new file mode 100644
|
|
index 0000000..f745948
|
|
--- /dev/null
|
|
+++ b/include/drm/nouveau_drm.h
|
|
@@ -0,0 +1,221 @@
|
|
+/*
|
|
+ * Copyright 2005 Stephane Marchesin.
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the next
|
|
+ * paragraph) shall be included in all copies or substantial portions of the
|
|
+ * Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
+ * OTHER DEALINGS IN THE SOFTWARE.
|
|
+ */
|
|
+
|
|
+#ifndef __NOUVEAU_DRM_H__
|
|
+#define __NOUVEAU_DRM_H__
|
|
+
|
|
+#define NOUVEAU_DRM_HEADER_PATCHLEVEL 15
|
|
+
|
|
+struct drm_nouveau_channel_alloc {
|
|
+ uint32_t fb_ctxdma_handle;
|
|
+ uint32_t tt_ctxdma_handle;
|
|
+
|
|
+ int channel;
|
|
+
|
|
+ /* Notifier memory */
|
|
+ uint32_t notifier_handle;
|
|
+
|
|
+ /* DRM-enforced subchannel assignments */
|
|
+ struct {
|
|
+ uint32_t handle;
|
|
+ uint32_t grclass;
|
|
+ } subchan[8];
|
|
+ uint32_t nr_subchan;
|
|
+};
|
|
+
|
|
+struct drm_nouveau_channel_free {
|
|
+ int channel;
|
|
+};
|
|
+
|
|
+struct drm_nouveau_grobj_alloc {
|
|
+ int channel;
|
|
+ uint32_t handle;
|
|
+ int class;
|
|
+};
|
|
+
|
|
+struct drm_nouveau_notifierobj_alloc {
|
|
+ uint32_t channel;
|
|
+ uint32_t handle;
|
|
+ uint32_t size;
|
|
+ uint32_t offset;
|
|
+};
|
|
+
|
|
+struct drm_nouveau_gpuobj_free {
|
|
+ int channel;
|
|
+ uint32_t handle;
|
|
+};
|
|
+
|
|
+/* FIXME : maybe unify {GET,SET}PARAMs */
|
|
+#define NOUVEAU_GETPARAM_PCI_VENDOR 3
|
|
+#define NOUVEAU_GETPARAM_PCI_DEVICE 4
|
|
+#define NOUVEAU_GETPARAM_BUS_TYPE 5
|
|
+#define NOUVEAU_GETPARAM_FB_PHYSICAL 6
|
|
+#define NOUVEAU_GETPARAM_AGP_PHYSICAL 7
|
|
+#define NOUVEAU_GETPARAM_FB_SIZE 8
|
|
+#define NOUVEAU_GETPARAM_AGP_SIZE 9
|
|
+#define NOUVEAU_GETPARAM_PCI_PHYSICAL 10
|
|
+#define NOUVEAU_GETPARAM_CHIPSET_ID 11
|
|
+#define NOUVEAU_GETPARAM_VM_VRAM_BASE 12
|
|
+#define NOUVEAU_GETPARAM_GRAPH_UNITS 13
|
|
+struct drm_nouveau_getparam {
|
|
+ uint64_t param;
|
|
+ uint64_t value;
|
|
+};
|
|
+
|
|
+struct drm_nouveau_setparam {
|
|
+ uint64_t param;
|
|
+ uint64_t value;
|
|
+};
|
|
+
|
|
+#define NOUVEAU_GEM_DOMAIN_CPU (1 << 0)
|
|
+#define NOUVEAU_GEM_DOMAIN_VRAM (1 << 1)
|
|
+#define NOUVEAU_GEM_DOMAIN_GART (1 << 2)
|
|
+#define NOUVEAU_GEM_DOMAIN_MAPPABLE (1 << 3)
|
|
+
|
|
+struct drm_nouveau_gem_info {
|
|
+ uint32_t handle;
|
|
+ uint32_t domain;
|
|
+ uint64_t size;
|
|
+ uint64_t offset;
|
|
+ uint64_t map_handle;
|
|
+ uint32_t tile_mode;
|
|
+ uint32_t tile_flags;
|
|
+};
|
|
+
|
|
+struct drm_nouveau_gem_new {
|
|
+ struct drm_nouveau_gem_info info;
|
|
+ uint32_t channel_hint;
|
|
+ uint32_t align;
|
|
+};
|
|
+
|
|
+struct drm_nouveau_gem_pushbuf_bo {
|
|
+ uint64_t user_priv;
|
|
+ uint32_t handle;
|
|
+ uint32_t read_domains;
|
|
+ uint32_t write_domains;
|
|
+ uint32_t valid_domains;
|
|
+ uint32_t presumed_ok;
|
|
+ uint32_t presumed_domain;
|
|
+ uint64_t presumed_offset;
|
|
+};
|
|
+
|
|
+#define NOUVEAU_GEM_RELOC_LOW (1 << 0)
|
|
+#define NOUVEAU_GEM_RELOC_HIGH (1 << 1)
|
|
+#define NOUVEAU_GEM_RELOC_OR (1 << 2)
|
|
+struct drm_nouveau_gem_pushbuf_reloc {
|
|
+ uint32_t bo_index;
|
|
+ uint32_t reloc_index;
|
|
+ uint32_t flags;
|
|
+ uint32_t data;
|
|
+ uint32_t vor;
|
|
+ uint32_t tor;
|
|
+};
|
|
+
|
|
+#define NOUVEAU_GEM_MAX_BUFFERS 1024
|
|
+#define NOUVEAU_GEM_MAX_RELOCS 1024
|
|
+
|
|
+struct drm_nouveau_gem_pushbuf {
|
|
+ uint32_t channel;
|
|
+ uint32_t nr_dwords;
|
|
+ uint32_t nr_buffers;
|
|
+ uint32_t nr_relocs;
|
|
+ uint64_t dwords;
|
|
+ uint64_t buffers;
|
|
+ uint64_t relocs;
|
|
+};
|
|
+
|
|
+struct drm_nouveau_gem_pushbuf_call {
|
|
+ uint32_t channel;
|
|
+ uint32_t handle;
|
|
+ uint32_t offset;
|
|
+ uint32_t nr_buffers;
|
|
+ uint32_t nr_relocs;
|
|
+ uint32_t nr_dwords;
|
|
+ uint64_t buffers;
|
|
+ uint64_t relocs;
|
|
+ uint32_t suffix0;
|
|
+ uint32_t suffix1;
|
|
+ /* below only accessed for CALL2 */
|
|
+ uint64_t vram_available;
|
|
+ uint64_t gart_available;
|
|
+};
|
|
+
|
|
+struct drm_nouveau_gem_pin {
|
|
+ uint32_t handle;
|
|
+ uint32_t domain;
|
|
+ uint64_t offset;
|
|
+};
|
|
+
|
|
+struct drm_nouveau_gem_unpin {
|
|
+ uint32_t handle;
|
|
+};
|
|
+
|
|
+#define NOUVEAU_GEM_CPU_PREP_NOWAIT 0x00000001
|
|
+#define NOUVEAU_GEM_CPU_PREP_NOBLOCK 0x00000002
|
|
+#define NOUVEAU_GEM_CPU_PREP_WRITE 0x00000004
|
|
+struct drm_nouveau_gem_cpu_prep {
|
|
+ uint32_t handle;
|
|
+ uint32_t flags;
|
|
+};
|
|
+
|
|
+struct drm_nouveau_gem_cpu_fini {
|
|
+ uint32_t handle;
|
|
+};
|
|
+
|
|
+struct drm_nouveau_gem_tile {
|
|
+ uint32_t handle;
|
|
+ uint32_t offset;
|
|
+ uint32_t size;
|
|
+ uint32_t tile_mode;
|
|
+ uint32_t tile_flags;
|
|
+};
|
|
+
|
|
+enum nouveau_bus_type {
|
|
+ NV_AGP = 0,
|
|
+ NV_PCI = 1,
|
|
+ NV_PCIE = 2,
|
|
+};
|
|
+
|
|
+struct drm_nouveau_sarea {
|
|
+};
|
|
+
|
|
+#define DRM_NOUVEAU_CARD_INIT 0x00
|
|
+#define DRM_NOUVEAU_GETPARAM 0x01
|
|
+#define DRM_NOUVEAU_SETPARAM 0x02
|
|
+#define DRM_NOUVEAU_CHANNEL_ALLOC 0x03
|
|
+#define DRM_NOUVEAU_CHANNEL_FREE 0x04
|
|
+#define DRM_NOUVEAU_GROBJ_ALLOC 0x05
|
|
+#define DRM_NOUVEAU_NOTIFIEROBJ_ALLOC 0x06
|
|
+#define DRM_NOUVEAU_GPUOBJ_FREE 0x07
|
|
+#define DRM_NOUVEAU_GEM_NEW 0x40
|
|
+#define DRM_NOUVEAU_GEM_PUSHBUF 0x41
|
|
+#define DRM_NOUVEAU_GEM_PUSHBUF_CALL 0x42
|
|
+#define DRM_NOUVEAU_GEM_PIN 0x43 /* !KMS only */
|
|
+#define DRM_NOUVEAU_GEM_UNPIN 0x44 /* !KMS only */
|
|
+#define DRM_NOUVEAU_GEM_CPU_PREP 0x45
|
|
+#define DRM_NOUVEAU_GEM_CPU_FINI 0x46
|
|
+#define DRM_NOUVEAU_GEM_INFO 0x47
|
|
+#define DRM_NOUVEAU_GEM_PUSHBUF_CALL2 0x48
|
|
+
|
|
+#endif /* __NOUVEAU_DRM_H__ */
|
|
diff --git a/include/drm/radeon_drm.h b/include/drm/radeon_drm.h
|
|
index 3b9932a..39537f3 100644
|
|
--- a/include/drm/radeon_drm.h
|
|
+++ b/include/drm/radeon_drm.h
|
|
@@ -33,7 +33,7 @@
|
|
#ifndef __RADEON_DRM_H__
|
|
#define __RADEON_DRM_H__
|
|
|
|
-#include <linux/types.h>
|
|
+#include "drm.h"
|
|
|
|
/* WARNING: If you change any of these defines, make sure to change the
|
|
* defines in the X server file (radeon_sarea.h)
|
|
diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h
|
|
index 4911461..81eb9f4 100644
|
|
--- a/include/drm/ttm/ttm_bo_api.h
|
|
+++ b/include/drm/ttm/ttm_bo_api.h
|
|
@@ -44,6 +44,29 @@ struct ttm_bo_device;
|
|
|
|
struct drm_mm_node;
|
|
|
|
+
|
|
+/**
|
|
+ * struct ttm_placement
|
|
+ *
|
|
+ * @fpfn: first valid page frame number to put the object
|
|
+ * @lpfn: last valid page frame number to put the object
|
|
+ * @num_placement: number of prefered placements
|
|
+ * @placement: prefered placements
|
|
+ * @num_busy_placement: number of prefered placements when need to evict buffer
|
|
+ * @busy_placement: prefered placements when need to evict buffer
|
|
+ *
|
|
+ * Structure indicating the placement you request for an object.
|
|
+ */
|
|
+struct ttm_placement {
|
|
+ unsigned fpfn;
|
|
+ unsigned lpfn;
|
|
+ unsigned num_placement;
|
|
+ const uint32_t *placement;
|
|
+ unsigned num_busy_placement;
|
|
+ const uint32_t *busy_placement;
|
|
+};
|
|
+
|
|
+
|
|
/**
|
|
* struct ttm_mem_reg
|
|
*
|
|
@@ -109,10 +132,6 @@ struct ttm_tt;
|
|
* the object is destroyed.
|
|
* @event_queue: Queue for processes waiting on buffer object status change.
|
|
* @lock: spinlock protecting mostly synchronization members.
|
|
- * @proposed_placement: Proposed placement for the buffer. Changed only by the
|
|
- * creator prior to validation as opposed to bo->mem.proposed_flags which is
|
|
- * changed by the implementation prior to a buffer move if it wants to outsmart
|
|
- * the buffer creator / user. This latter happens, for example, at eviction.
|
|
* @mem: structure describing current placement.
|
|
* @persistant_swap_storage: Usually the swap storage is deleted for buffers
|
|
* pinned in physical memory. If this behaviour is not desired, this member
|
|
@@ -177,7 +196,6 @@ struct ttm_buffer_object {
|
|
* Members protected by the bo::reserved lock.
|
|
*/
|
|
|
|
- uint32_t proposed_placement;
|
|
struct ttm_mem_reg mem;
|
|
struct file *persistant_swap_storage;
|
|
struct ttm_tt *ttm;
|
|
@@ -285,29 +303,30 @@ ttm_bo_reference(struct ttm_buffer_object *bo)
|
|
* Note: It might be necessary to block validations before the
|
|
* wait by reserving the buffer.
|
|
* Returns -EBUSY if no_wait is true and the buffer is busy.
|
|
- * Returns -ERESTART if interrupted by a signal.
|
|
+ * Returns -ERESTARTSYS if interrupted by a signal.
|
|
*/
|
|
extern int ttm_bo_wait(struct ttm_buffer_object *bo, bool lazy,
|
|
bool interruptible, bool no_wait);
|
|
/**
|
|
- * ttm_buffer_object_validate
|
|
+ * ttm_bo_validate
|
|
*
|
|
* @bo: The buffer object.
|
|
- * @proposed_placement: Proposed_placement for the buffer object.
|
|
+ * @placement: Proposed placement for the buffer object.
|
|
* @interruptible: Sleep interruptible if sleeping.
|
|
* @no_wait: Return immediately if the buffer is busy.
|
|
*
|
|
* Changes placement and caching policy of the buffer object
|
|
- * according to bo::proposed_flags.
|
|
+ * according proposed placement.
|
|
* Returns
|
|
- * -EINVAL on invalid proposed_flags.
|
|
+ * -EINVAL on invalid proposed placement.
|
|
* -ENOMEM on out-of-memory condition.
|
|
* -EBUSY if no_wait is true and buffer busy.
|
|
- * -ERESTART if interrupted by a signal.
|
|
+ * -ERESTARTSYS if interrupted by a signal.
|
|
*/
|
|
-extern int ttm_buffer_object_validate(struct ttm_buffer_object *bo,
|
|
- uint32_t proposed_placement,
|
|
- bool interruptible, bool no_wait);
|
|
+extern int ttm_bo_validate(struct ttm_buffer_object *bo,
|
|
+ struct ttm_placement *placement,
|
|
+ bool interruptible, bool no_wait);
|
|
+
|
|
/**
|
|
* ttm_bo_unref
|
|
*
|
|
@@ -328,7 +347,7 @@ extern void ttm_bo_unref(struct ttm_buffer_object **bo);
|
|
* waiting for buffer idle. This lock is recursive.
|
|
* Returns
|
|
* -EBUSY if the buffer is busy and no_wait is true.
|
|
- * -ERESTART if interrupted by a signal.
|
|
+ * -ERESTARTSYS if interrupted by a signal.
|
|
*/
|
|
|
|
extern int
|
|
@@ -343,7 +362,7 @@ ttm_bo_synccpu_write_grab(struct ttm_buffer_object *bo, bool no_wait);
|
|
extern void ttm_bo_synccpu_write_release(struct ttm_buffer_object *bo);
|
|
|
|
/**
|
|
- * ttm_buffer_object_init
|
|
+ * ttm_bo_init
|
|
*
|
|
* @bdev: Pointer to a ttm_bo_device struct.
|
|
* @bo: Pointer to a ttm_buffer_object to be initialized.
|
|
@@ -371,20 +390,20 @@ extern void ttm_bo_synccpu_write_release(struct ttm_buffer_object *bo);
|
|
* Returns
|
|
* -ENOMEM: Out of memory.
|
|
* -EINVAL: Invalid placement flags.
|
|
- * -ERESTART: Interrupted by signal while sleeping waiting for resources.
|
|
+ * -ERESTARTSYS: Interrupted by signal while sleeping waiting for resources.
|
|
*/
|
|
|
|
-extern int ttm_buffer_object_init(struct ttm_bo_device *bdev,
|
|
- struct ttm_buffer_object *bo,
|
|
- unsigned long size,
|
|
- enum ttm_bo_type type,
|
|
- uint32_t flags,
|
|
- uint32_t page_alignment,
|
|
- unsigned long buffer_start,
|
|
- bool interrubtible,
|
|
- struct file *persistant_swap_storage,
|
|
- size_t acc_size,
|
|
- void (*destroy) (struct ttm_buffer_object *));
|
|
+extern int ttm_bo_init(struct ttm_bo_device *bdev,
|
|
+ struct ttm_buffer_object *bo,
|
|
+ unsigned long size,
|
|
+ enum ttm_bo_type type,
|
|
+ struct ttm_placement *placement,
|
|
+ uint32_t page_alignment,
|
|
+ unsigned long buffer_start,
|
|
+ bool interrubtible,
|
|
+ struct file *persistant_swap_storage,
|
|
+ size_t acc_size,
|
|
+ void (*destroy) (struct ttm_buffer_object *));
|
|
/**
|
|
* ttm_bo_synccpu_object_init
|
|
*
|
|
@@ -405,47 +424,43 @@ extern int ttm_buffer_object_init(struct ttm_bo_device *bdev,
|
|
* GEM user interface.
|
|
* @p_bo: On successful completion *p_bo points to the created object.
|
|
*
|
|
- * This function allocates a ttm_buffer_object, and then calls
|
|
- * ttm_buffer_object_init on that object.
|
|
- * The destroy function is set to kfree().
|
|
+ * This function allocates a ttm_buffer_object, and then calls ttm_bo_init
|
|
+ * on that object. The destroy function is set to kfree().
|
|
* Returns
|
|
* -ENOMEM: Out of memory.
|
|
* -EINVAL: Invalid placement flags.
|
|
- * -ERESTART: Interrupted by signal while waiting for resources.
|
|
+ * -ERESTARTSYS: Interrupted by signal while waiting for resources.
|
|
*/
|
|
|
|
-extern int ttm_buffer_object_create(struct ttm_bo_device *bdev,
|
|
- unsigned long size,
|
|
- enum ttm_bo_type type,
|
|
- uint32_t flags,
|
|
- uint32_t page_alignment,
|
|
- unsigned long buffer_start,
|
|
- bool interruptible,
|
|
- struct file *persistant_swap_storage,
|
|
- struct ttm_buffer_object **p_bo);
|
|
+extern int ttm_bo_create(struct ttm_bo_device *bdev,
|
|
+ unsigned long size,
|
|
+ enum ttm_bo_type type,
|
|
+ struct ttm_placement *placement,
|
|
+ uint32_t page_alignment,
|
|
+ unsigned long buffer_start,
|
|
+ bool interruptible,
|
|
+ struct file *persistant_swap_storage,
|
|
+ struct ttm_buffer_object **p_bo);
|
|
|
|
/**
|
|
* ttm_bo_check_placement
|
|
*
|
|
- * @bo: the buffer object.
|
|
- * @set_flags: placement flags to set.
|
|
- * @clr_flags: placement flags to clear.
|
|
+ * @bo: the buffer object.
|
|
+ * @placement: placements
|
|
*
|
|
* Performs minimal validity checking on an intended change of
|
|
* placement flags.
|
|
* Returns
|
|
* -EINVAL: Intended change is invalid or not allowed.
|
|
*/
|
|
-
|
|
extern int ttm_bo_check_placement(struct ttm_buffer_object *bo,
|
|
- uint32_t set_flags, uint32_t clr_flags);
|
|
+ struct ttm_placement *placement);
|
|
|
|
/**
|
|
* ttm_bo_init_mm
|
|
*
|
|
* @bdev: Pointer to a ttm_bo_device struct.
|
|
* @mem_type: The memory type.
|
|
- * @p_offset: offset for managed area in pages.
|
|
* @p_size: size managed area in pages.
|
|
*
|
|
* Initialize a manager for a given memory type.
|
|
@@ -458,7 +473,7 @@ extern int ttm_bo_check_placement(struct ttm_buffer_object *bo,
|
|
*/
|
|
|
|
extern int ttm_bo_init_mm(struct ttm_bo_device *bdev, unsigned type,
|
|
- unsigned long p_offset, unsigned long p_size);
|
|
+ unsigned long p_size);
|
|
/**
|
|
* ttm_bo_clean_mm
|
|
*
|
|
@@ -503,7 +518,7 @@ extern int ttm_bo_clean_mm(struct ttm_bo_device *bdev, unsigned mem_type);
|
|
*
|
|
* Returns:
|
|
* -EINVAL: Invalid or uninitialized memory type.
|
|
- * -ERESTART: The call was interrupted by a signal while waiting to
|
|
+ * -ERESTARTSYS: The call was interrupted by a signal while waiting to
|
|
* evict a buffer.
|
|
*/
|
|
|
|
@@ -606,7 +621,7 @@ extern int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma,
|
|
* be called from the fops::read and fops::write method.
|
|
* Returns:
|
|
* See man (2) write, man(2) read. In particular,
|
|
- * the function may return -EINTR if
|
|
+ * the function may return -ERESTARTSYS if
|
|
* interrupted by a signal.
|
|
*/
|
|
|
|
diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h
|
|
index e8cd6d2..4c4e0f8 100644
|
|
--- a/include/drm/ttm/ttm_bo_driver.h
|
|
+++ b/include/drm/ttm/ttm_bo_driver.h
|
|
@@ -242,12 +242,6 @@ struct ttm_mem_type_manager {
|
|
/**
|
|
* struct ttm_bo_driver
|
|
*
|
|
- * @mem_type_prio: Priority array of memory types to place a buffer object in
|
|
- * if it fits without evicting buffers from any of these memory types.
|
|
- * @mem_busy_prio: Priority array of memory types to place a buffer object in
|
|
- * if it needs to evict buffers to make room.
|
|
- * @num_mem_type_prio: Number of elements in the @mem_type_prio array.
|
|
- * @num_mem_busy_prio: Number of elements in the @num_mem_busy_prio array.
|
|
* @create_ttm_backend_entry: Callback to create a struct ttm_backend.
|
|
* @invalidate_caches: Callback to invalidate read caches when a buffer object
|
|
* has been evicted.
|
|
@@ -265,11 +259,6 @@ struct ttm_mem_type_manager {
|
|
*/
|
|
|
|
struct ttm_bo_driver {
|
|
- const uint32_t *mem_type_prio;
|
|
- const uint32_t *mem_busy_prio;
|
|
- uint32_t num_mem_type_prio;
|
|
- uint32_t num_mem_busy_prio;
|
|
-
|
|
/**
|
|
* struct ttm_bo_driver member create_ttm_backend_entry
|
|
*
|
|
@@ -306,7 +295,8 @@ struct ttm_bo_driver {
|
|
* finished, they'll end up in bo->mem.flags
|
|
*/
|
|
|
|
- uint32_t(*evict_flags) (struct ttm_buffer_object *bo);
|
|
+ void(*evict_flags) (struct ttm_buffer_object *bo,
|
|
+ struct ttm_placement *placement);
|
|
/**
|
|
* struct ttm_bo_driver member move:
|
|
*
|
|
@@ -363,6 +353,11 @@ struct ttm_bo_driver {
|
|
/* notify the driver we are taking a fault on this BO
|
|
* and have reserved it */
|
|
void (*fault_reserve_notify)(struct ttm_buffer_object *bo);
|
|
+
|
|
+ /**
|
|
+ * notify the driver that we're about to swap out this bo
|
|
+ */
|
|
+ void (*swap_notify) (struct ttm_buffer_object *bo);
|
|
};
|
|
|
|
/**
|
|
@@ -545,6 +540,15 @@ extern int ttm_tt_set_user(struct ttm_tt *ttm,
|
|
extern int ttm_tt_bind(struct ttm_tt *ttm, struct ttm_mem_reg *bo_mem);
|
|
|
|
/**
|
|
+ * ttm_tt_populate:
|
|
+ *
|
|
+ * @ttm: The struct ttm_tt to contain the backing pages.
|
|
+ *
|
|
+ * Add backing pages to all of @ttm
|
|
+ */
|
|
+extern int ttm_tt_populate(struct ttm_tt *ttm);
|
|
+
|
|
+/**
|
|
* ttm_ttm_destroy:
|
|
*
|
|
* @ttm: The struct ttm_tt.
|
|
@@ -639,12 +643,12 @@ extern bool ttm_mem_reg_is_pci(struct ttm_bo_device *bdev,
|
|
* -EBUSY: No space available (only if no_wait == 1).
|
|
* -ENOMEM: Could not allocate memory for the buffer object, either due to
|
|
* fragmentation or concurrent allocators.
|
|
- * -ERESTART: An interruptible sleep was interrupted by a signal.
|
|
+ * -ERESTARTSYS: An interruptible sleep was interrupted by a signal.
|
|
*/
|
|
extern int ttm_bo_mem_space(struct ttm_buffer_object *bo,
|
|
- uint32_t proposed_placement,
|
|
- struct ttm_mem_reg *mem,
|
|
- bool interruptible, bool no_wait);
|
|
+ struct ttm_placement *placement,
|
|
+ struct ttm_mem_reg *mem,
|
|
+ bool interruptible, bool no_wait);
|
|
/**
|
|
* ttm_bo_wait_for_cpu
|
|
*
|
|
@@ -654,7 +658,7 @@ extern int ttm_bo_mem_space(struct ttm_buffer_object *bo,
|
|
* Wait until a buffer object is no longer sync'ed for CPU access.
|
|
* Returns:
|
|
* -EBUSY: Buffer object was sync'ed for CPU access. (only if no_wait == 1).
|
|
- * -ERESTART: An interruptible sleep was interrupted by a signal.
|
|
+ * -ERESTARTSYS: An interruptible sleep was interrupted by a signal.
|
|
*/
|
|
|
|
extern int ttm_bo_wait_cpu(struct ttm_buffer_object *bo, bool no_wait);
|
|
@@ -758,7 +762,7 @@ extern void ttm_bo_unmap_virtual(struct ttm_buffer_object *bo);
|
|
* -EAGAIN: The reservation may cause a deadlock.
|
|
* Release all buffer reservations, wait for @bo to become unreserved and
|
|
* try again. (only if use_sequence == 1).
|
|
- * -ERESTART: A wait for the buffer to become unreserved was interrupted by
|
|
+ * -ERESTARTSYS: A wait for the buffer to become unreserved was interrupted by
|
|
* a signal. Release all buffer reservations and return to user-space.
|
|
*/
|
|
extern int ttm_bo_reserve(struct ttm_buffer_object *bo,
|
|
@@ -799,7 +803,7 @@ extern int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo,
|
|
*
|
|
* Returns:
|
|
* -EBUSY: If no_wait == 1 and the buffer is already reserved.
|
|
- * -ERESTART: If interruptible == 1 and the process received a signal
|
|
+ * -ERESTARTSYS: If interruptible == 1 and the process received a signal
|
|
* while sleeping.
|
|
*/
|
|
extern int ttm_bo_block_reservation(struct ttm_buffer_object *bo,
|
|
diff --git a/include/drm/ttm/ttm_execbuf_util.h b/include/drm/ttm/ttm_execbuf_util.h
|
|
new file mode 100644
|
|
index 0000000..cd2c475
|
|
--- /dev/null
|
|
+++ b/include/drm/ttm/ttm_execbuf_util.h
|
|
@@ -0,0 +1,107 @@
|
|
+/**************************************************************************
|
|
+ *
|
|
+ * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sub license, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial portions
|
|
+ * of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ **************************************************************************/
|
|
+/*
|
|
+ * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
|
|
+ */
|
|
+
|
|
+#ifndef _TTM_EXECBUF_UTIL_H_
|
|
+#define _TTM_EXECBUF_UTIL_H_
|
|
+
|
|
+#include "ttm/ttm_bo_api.h"
|
|
+#include <linux/list.h>
|
|
+
|
|
+/**
|
|
+ * struct ttm_validate_buffer
|
|
+ *
|
|
+ * @head: list head for thread-private list.
|
|
+ * @bo: refcounted buffer object pointer.
|
|
+ * @new_sync_obj_arg: New sync_obj_arg for @bo, to be used once
|
|
+ * adding a new sync object.
|
|
+ * @reservied: Indicates whether @bo has been reserved for validation.
|
|
+ */
|
|
+
|
|
+struct ttm_validate_buffer {
|
|
+ struct list_head head;
|
|
+ struct ttm_buffer_object *bo;
|
|
+ void *new_sync_obj_arg;
|
|
+ bool reserved;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * function ttm_eu_backoff_reservation
|
|
+ *
|
|
+ * @list: thread private list of ttm_validate_buffer structs.
|
|
+ *
|
|
+ * Undoes all buffer validation reservations for bos pointed to by
|
|
+ * the list entries.
|
|
+ */
|
|
+
|
|
+extern void ttm_eu_backoff_reservation(struct list_head *list);
|
|
+
|
|
+/**
|
|
+ * function ttm_eu_reserve_buffers
|
|
+ *
|
|
+ * @list: thread private list of ttm_validate_buffer structs.
|
|
+ * @val_seq: A unique sequence number.
|
|
+ *
|
|
+ * Tries to reserve bos pointed to by the list entries for validation.
|
|
+ * If the function returns 0, all buffers are marked as "unfenced",
|
|
+ * taken off the lru lists and are not synced for write CPU usage.
|
|
+ *
|
|
+ * If the function detects a deadlock due to multiple threads trying to
|
|
+ * reserve the same buffers in reverse order, all threads except one will
|
|
+ * back off and retry. This function may sleep while waiting for
|
|
+ * CPU write reservations to be cleared, and for other threads to
|
|
+ * unreserve their buffers.
|
|
+ *
|
|
+ * This function may return -ERESTART or -EAGAIN if the calling process
|
|
+ * receives a signal while waiting. In that case, no buffers on the list
|
|
+ * will be reserved upon return.
|
|
+ *
|
|
+ * Buffers reserved by this function should be unreserved by
|
|
+ * a call to either ttm_eu_backoff_reservation() or
|
|
+ * ttm_eu_fence_buffer_objects() when command submission is complete or
|
|
+ * has failed.
|
|
+ */
|
|
+
|
|
+extern int ttm_eu_reserve_buffers(struct list_head *list, uint32_t val_seq);
|
|
+
|
|
+/**
|
|
+ * function ttm_eu_fence_buffer_objects.
|
|
+ *
|
|
+ * @list: thread private list of ttm_validate_buffer structs.
|
|
+ * @sync_obj: The new sync object for the buffers.
|
|
+ *
|
|
+ * This function should be called when command submission is complete, and
|
|
+ * it will add a new sync object to bos pointed to by entries on @list.
|
|
+ * It also unreserves all buffers, putting them on lru lists.
|
|
+ *
|
|
+ */
|
|
+
|
|
+extern void ttm_eu_fence_buffer_objects(struct list_head *list, void *sync_obj);
|
|
+
|
|
+#endif
|
|
diff --git a/include/drm/ttm/ttm_lock.h b/include/drm/ttm/ttm_lock.h
|
|
new file mode 100644
|
|
index 0000000..81ba0b0
|
|
--- /dev/null
|
|
+++ b/include/drm/ttm/ttm_lock.h
|
|
@@ -0,0 +1,247 @@
|
|
+/**************************************************************************
|
|
+ *
|
|
+ * Copyright (c) 2007-2009 VMware, Inc., Palo Alto, CA., USA
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sub license, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial portions
|
|
+ * of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ **************************************************************************/
|
|
+/*
|
|
+ * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
|
|
+ */
|
|
+
|
|
+/** @file ttm_lock.h
|
|
+ * This file implements a simple replacement for the buffer manager use
|
|
+ * of the DRM heavyweight hardware lock.
|
|
+ * The lock is a read-write lock. Taking it in read mode and write mode
|
|
+ * is relatively fast, and intended for in-kernel use only.
|
|
+ *
|
|
+ * The vt mode is used only when there is a need to block all
|
|
+ * user-space processes from validating buffers.
|
|
+ * It's allowed to leave kernel space with the vt lock held.
|
|
+ * If a user-space process dies while having the vt-lock,
|
|
+ * it will be released during the file descriptor release. The vt lock
|
|
+ * excludes write lock and read lock.
|
|
+ *
|
|
+ * The suspend mode is used to lock out all TTM users when preparing for
|
|
+ * and executing suspend operations.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef _TTM_LOCK_H_
|
|
+#define _TTM_LOCK_H_
|
|
+
|
|
+#include "ttm/ttm_object.h"
|
|
+#include <linux/wait.h>
|
|
+#include <asm/atomic.h>
|
|
+
|
|
+/**
|
|
+ * struct ttm_lock
|
|
+ *
|
|
+ * @base: ttm base object used solely to release the lock if the client
|
|
+ * holding the lock dies.
|
|
+ * @queue: Queue for processes waiting for lock change-of-status.
|
|
+ * @lock: Spinlock protecting some lock members.
|
|
+ * @rw: Read-write lock counter. Protected by @lock.
|
|
+ * @flags: Lock state. Protected by @lock.
|
|
+ * @kill_takers: Boolean whether to kill takers of the lock.
|
|
+ * @signal: Signal to send when kill_takers is true.
|
|
+ */
|
|
+
|
|
+struct ttm_lock {
|
|
+ struct ttm_base_object base;
|
|
+ wait_queue_head_t queue;
|
|
+ spinlock_t lock;
|
|
+ int32_t rw;
|
|
+ uint32_t flags;
|
|
+ bool kill_takers;
|
|
+ int signal;
|
|
+ struct ttm_object_file *vt_holder;
|
|
+};
|
|
+
|
|
+
|
|
+/**
|
|
+ * ttm_lock_init
|
|
+ *
|
|
+ * @lock: Pointer to a struct ttm_lock
|
|
+ * Initializes the lock.
|
|
+ */
|
|
+extern void ttm_lock_init(struct ttm_lock *lock);
|
|
+
|
|
+/**
|
|
+ * ttm_read_unlock
|
|
+ *
|
|
+ * @lock: Pointer to a struct ttm_lock
|
|
+ *
|
|
+ * Releases a read lock.
|
|
+ */
|
|
+extern void ttm_read_unlock(struct ttm_lock *lock);
|
|
+
|
|
+/**
|
|
+ * ttm_read_lock
|
|
+ *
|
|
+ * @lock: Pointer to a struct ttm_lock
|
|
+ * @interruptible: Interruptible sleeping while waiting for a lock.
|
|
+ *
|
|
+ * Takes the lock in read mode.
|
|
+ * Returns:
|
|
+ * -ERESTARTSYS If interrupted by a signal and interruptible is true.
|
|
+ */
|
|
+extern int ttm_read_lock(struct ttm_lock *lock, bool interruptible);
|
|
+
|
|
+/**
|
|
+ * ttm_read_trylock
|
|
+ *
|
|
+ * @lock: Pointer to a struct ttm_lock
|
|
+ * @interruptible: Interruptible sleeping while waiting for a lock.
|
|
+ *
|
|
+ * Tries to take the lock in read mode. If the lock is already held
|
|
+ * in write mode, the function will return -EBUSY. If the lock is held
|
|
+ * in vt or suspend mode, the function will sleep until these modes
|
|
+ * are unlocked.
|
|
+ *
|
|
+ * Returns:
|
|
+ * -EBUSY The lock was already held in write mode.
|
|
+ * -ERESTARTSYS If interrupted by a signal and interruptible is true.
|
|
+ */
|
|
+extern int ttm_read_trylock(struct ttm_lock *lock, bool interruptible);
|
|
+
|
|
+/**
|
|
+ * ttm_write_unlock
|
|
+ *
|
|
+ * @lock: Pointer to a struct ttm_lock
|
|
+ *
|
|
+ * Releases a write lock.
|
|
+ */
|
|
+extern void ttm_write_unlock(struct ttm_lock *lock);
|
|
+
|
|
+/**
|
|
+ * ttm_write_lock
|
|
+ *
|
|
+ * @lock: Pointer to a struct ttm_lock
|
|
+ * @interruptible: Interruptible sleeping while waiting for a lock.
|
|
+ *
|
|
+ * Takes the lock in write mode.
|
|
+ * Returns:
|
|
+ * -ERESTARTSYS If interrupted by a signal and interruptible is true.
|
|
+ */
|
|
+extern int ttm_write_lock(struct ttm_lock *lock, bool interruptible);
|
|
+
|
|
+/**
|
|
+ * ttm_lock_downgrade
|
|
+ *
|
|
+ * @lock: Pointer to a struct ttm_lock
|
|
+ *
|
|
+ * Downgrades a write lock to a read lock.
|
|
+ */
|
|
+extern void ttm_lock_downgrade(struct ttm_lock *lock);
|
|
+
|
|
+/**
|
|
+ * ttm_suspend_lock
|
|
+ *
|
|
+ * @lock: Pointer to a struct ttm_lock
|
|
+ *
|
|
+ * Takes the lock in suspend mode. Excludes read and write mode.
|
|
+ */
|
|
+extern void ttm_suspend_lock(struct ttm_lock *lock);
|
|
+
|
|
+/**
|
|
+ * ttm_suspend_unlock
|
|
+ *
|
|
+ * @lock: Pointer to a struct ttm_lock
|
|
+ *
|
|
+ * Releases a suspend lock
|
|
+ */
|
|
+extern void ttm_suspend_unlock(struct ttm_lock *lock);
|
|
+
|
|
+/**
|
|
+ * ttm_vt_lock
|
|
+ *
|
|
+ * @lock: Pointer to a struct ttm_lock
|
|
+ * @interruptible: Interruptible sleeping while waiting for a lock.
|
|
+ * @tfile: Pointer to a struct ttm_object_file to register the lock with.
|
|
+ *
|
|
+ * Takes the lock in vt mode.
|
|
+ * Returns:
|
|
+ * -ERESTARTSYS If interrupted by a signal and interruptible is true.
|
|
+ * -ENOMEM: Out of memory when locking.
|
|
+ */
|
|
+extern int ttm_vt_lock(struct ttm_lock *lock, bool interruptible,
|
|
+ struct ttm_object_file *tfile);
|
|
+
|
|
+/**
|
|
+ * ttm_vt_unlock
|
|
+ *
|
|
+ * @lock: Pointer to a struct ttm_lock
|
|
+ *
|
|
+ * Releases a vt lock.
|
|
+ * Returns:
|
|
+ * -EINVAL If the lock was not held.
|
|
+ */
|
|
+extern int ttm_vt_unlock(struct ttm_lock *lock);
|
|
+
|
|
+/**
|
|
+ * ttm_write_unlock
|
|
+ *
|
|
+ * @lock: Pointer to a struct ttm_lock
|
|
+ *
|
|
+ * Releases a write lock.
|
|
+ */
|
|
+extern void ttm_write_unlock(struct ttm_lock *lock);
|
|
+
|
|
+/**
|
|
+ * ttm_write_lock
|
|
+ *
|
|
+ * @lock: Pointer to a struct ttm_lock
|
|
+ * @interruptible: Interruptible sleeping while waiting for a lock.
|
|
+ *
|
|
+ * Takes the lock in write mode.
|
|
+ * Returns:
|
|
+ * -ERESTARTSYS If interrupted by a signal and interruptible is true.
|
|
+ */
|
|
+extern int ttm_write_lock(struct ttm_lock *lock, bool interruptible);
|
|
+
|
|
+/**
|
|
+ * ttm_lock_set_kill
|
|
+ *
|
|
+ * @lock: Pointer to a struct ttm_lock
|
|
+ * @val: Boolean whether to kill processes taking the lock.
|
|
+ * @signal: Signal to send to the process taking the lock.
|
|
+ *
|
|
+ * The kill-when-taking-lock functionality is used to kill processes that keep
|
|
+ * on using the TTM functionality when its resources has been taken down, for
|
|
+ * example when the X server exits. A typical sequence would look like this:
|
|
+ * - X server takes lock in write mode.
|
|
+ * - ttm_lock_set_kill() is called with @val set to true.
|
|
+ * - As part of X server exit, TTM resources are taken down.
|
|
+ * - X server releases the lock on file release.
|
|
+ * - Another dri client wants to render, takes the lock and is killed.
|
|
+ *
|
|
+ */
|
|
+static inline void ttm_lock_set_kill(struct ttm_lock *lock, bool val,
|
|
+ int signal)
|
|
+{
|
|
+ lock->kill_takers = val;
|
|
+ if (val)
|
|
+ lock->signal = signal;
|
|
+}
|
|
+
|
|
+#endif
|
|
diff --git a/include/drm/ttm/ttm_object.h b/include/drm/ttm/ttm_object.h
|
|
new file mode 100644
|
|
index 0000000..0d9db09
|
|
--- /dev/null
|
|
+++ b/include/drm/ttm/ttm_object.h
|
|
@@ -0,0 +1,271 @@
|
|
+/**************************************************************************
|
|
+ *
|
|
+ * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sub license, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial portions
|
|
+ * of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ **************************************************************************/
|
|
+/*
|
|
+ * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
|
|
+ */
|
|
+/** @file ttm_object.h
|
|
+ *
|
|
+ * Base- and reference object implementation for the various
|
|
+ * ttm objects. Implements reference counting, minimal security checks
|
|
+ * and release on file close.
|
|
+ */
|
|
+
|
|
+#ifndef _TTM_OBJECT_H_
|
|
+#define _TTM_OBJECT_H_
|
|
+
|
|
+#include <linux/list.h>
|
|
+#include "drm_hashtab.h"
|
|
+#include <linux/kref.h>
|
|
+#include <ttm/ttm_memory.h>
|
|
+
|
|
+/**
|
|
+ * enum ttm_ref_type
|
|
+ *
|
|
+ * Describes what type of reference a ref object holds.
|
|
+ *
|
|
+ * TTM_REF_USAGE is a simple refcount on a base object.
|
|
+ *
|
|
+ * TTM_REF_SYNCCPU_READ is a SYNCCPU_READ reference on a
|
|
+ * buffer object.
|
|
+ *
|
|
+ * TTM_REF_SYNCCPU_WRITE is a SYNCCPU_WRITE reference on a
|
|
+ * buffer object.
|
|
+ *
|
|
+ */
|
|
+
|
|
+enum ttm_ref_type {
|
|
+ TTM_REF_USAGE,
|
|
+ TTM_REF_SYNCCPU_READ,
|
|
+ TTM_REF_SYNCCPU_WRITE,
|
|
+ TTM_REF_NUM
|
|
+};
|
|
+
|
|
+/**
|
|
+ * enum ttm_object_type
|
|
+ *
|
|
+ * One entry per ttm object type.
|
|
+ * Device-specific types should use the
|
|
+ * ttm_driver_typex types.
|
|
+ */
|
|
+
|
|
+enum ttm_object_type {
|
|
+ ttm_fence_type,
|
|
+ ttm_buffer_type,
|
|
+ ttm_lock_type,
|
|
+ ttm_driver_type0 = 256,
|
|
+ ttm_driver_type1,
|
|
+ ttm_driver_type2,
|
|
+ ttm_driver_type3,
|
|
+ ttm_driver_type4,
|
|
+ ttm_driver_type5
|
|
+};
|
|
+
|
|
+struct ttm_object_file;
|
|
+struct ttm_object_device;
|
|
+
|
|
+/**
|
|
+ * struct ttm_base_object
|
|
+ *
|
|
+ * @hash: hash entry for the per-device object hash.
|
|
+ * @type: derived type this object is base class for.
|
|
+ * @shareable: Other ttm_object_files can access this object.
|
|
+ *
|
|
+ * @tfile: Pointer to ttm_object_file of the creator.
|
|
+ * NULL if the object was not created by a user request.
|
|
+ * (kernel object).
|
|
+ *
|
|
+ * @refcount: Number of references to this object, not
|
|
+ * including the hash entry. A reference to a base object can
|
|
+ * only be held by a ref object.
|
|
+ *
|
|
+ * @refcount_release: A function to be called when there are
|
|
+ * no more references to this object. This function should
|
|
+ * destroy the object (or make sure destruction eventually happens),
|
|
+ * and when it is called, the object has
|
|
+ * already been taken out of the per-device hash. The parameter
|
|
+ * "base" should be set to NULL by the function.
|
|
+ *
|
|
+ * @ref_obj_release: A function to be called when a reference object
|
|
+ * with another ttm_ref_type than TTM_REF_USAGE is deleted.
|
|
+ * this function may, for example, release a lock held by a user-space
|
|
+ * process.
|
|
+ *
|
|
+ * This struct is intended to be used as a base struct for objects that
|
|
+ * are visible to user-space. It provides a global name, race-safe
|
|
+ * access and refcounting, minimal access contol and hooks for unref actions.
|
|
+ */
|
|
+
|
|
+struct ttm_base_object {
|
|
+ struct drm_hash_item hash;
|
|
+ enum ttm_object_type object_type;
|
|
+ bool shareable;
|
|
+ struct ttm_object_file *tfile;
|
|
+ struct kref refcount;
|
|
+ void (*refcount_release) (struct ttm_base_object **base);
|
|
+ void (*ref_obj_release) (struct ttm_base_object *base,
|
|
+ enum ttm_ref_type ref_type);
|
|
+};
|
|
+
|
|
+/**
|
|
+ * ttm_base_object_init
|
|
+ *
|
|
+ * @tfile: Pointer to a struct ttm_object_file.
|
|
+ * @base: The struct ttm_base_object to initialize.
|
|
+ * @shareable: This object is shareable with other applcations.
|
|
+ * (different @tfile pointers.)
|
|
+ * @type: The object type.
|
|
+ * @refcount_release: See the struct ttm_base_object description.
|
|
+ * @ref_obj_release: See the struct ttm_base_object description.
|
|
+ *
|
|
+ * Initializes a struct ttm_base_object.
|
|
+ */
|
|
+
|
|
+extern int ttm_base_object_init(struct ttm_object_file *tfile,
|
|
+ struct ttm_base_object *base,
|
|
+ bool shareable,
|
|
+ enum ttm_object_type type,
|
|
+ void (*refcount_release) (struct ttm_base_object
|
|
+ **),
|
|
+ void (*ref_obj_release) (struct ttm_base_object
|
|
+ *,
|
|
+ enum ttm_ref_type
|
|
+ ref_type));
|
|
+
|
|
+/**
|
|
+ * ttm_base_object_lookup
|
|
+ *
|
|
+ * @tfile: Pointer to a struct ttm_object_file.
|
|
+ * @key: Hash key
|
|
+ *
|
|
+ * Looks up a struct ttm_base_object with the key @key.
|
|
+ * Also verifies that the object is visible to the application, by
|
|
+ * comparing the @tfile argument and checking the object shareable flag.
|
|
+ */
|
|
+
|
|
+extern struct ttm_base_object *ttm_base_object_lookup(struct ttm_object_file
|
|
+ *tfile, uint32_t key);
|
|
+
|
|
+/**
|
|
+ * ttm_base_object_unref
|
|
+ *
|
|
+ * @p_base: Pointer to a pointer referncing a struct ttm_base_object.
|
|
+ *
|
|
+ * Decrements the base object refcount and clears the pointer pointed to by
|
|
+ * p_base.
|
|
+ */
|
|
+
|
|
+extern void ttm_base_object_unref(struct ttm_base_object **p_base);
|
|
+
|
|
+/**
|
|
+ * ttm_ref_object_add.
|
|
+ *
|
|
+ * @tfile: A struct ttm_object_file representing the application owning the
|
|
+ * ref_object.
|
|
+ * @base: The base object to reference.
|
|
+ * @ref_type: The type of reference.
|
|
+ * @existed: Upon completion, indicates that an identical reference object
|
|
+ * already existed, and the refcount was upped on that object instead.
|
|
+ *
|
|
+ * Adding a ref object to a base object is basically like referencing the
|
|
+ * base object, but a user-space application holds the reference. When the
|
|
+ * file corresponding to @tfile is closed, all its reference objects are
|
|
+ * deleted. A reference object can have different types depending on what
|
|
+ * it's intended for. It can be refcounting to prevent object destruction,
|
|
+ * When user-space takes a lock, it can add a ref object to that lock to
|
|
+ * make sure the lock is released if the application dies. A ref object
|
|
+ * will hold a single reference on a base object.
|
|
+ */
|
|
+extern int ttm_ref_object_add(struct ttm_object_file *tfile,
|
|
+ struct ttm_base_object *base,
|
|
+ enum ttm_ref_type ref_type, bool *existed);
|
|
+/**
|
|
+ * ttm_ref_object_base_unref
|
|
+ *
|
|
+ * @key: Key representing the base object.
|
|
+ * @ref_type: Ref type of the ref object to be dereferenced.
|
|
+ *
|
|
+ * Unreference a ref object with type @ref_type
|
|
+ * on the base object identified by @key. If there are no duplicate
|
|
+ * references, the ref object will be destroyed and the base object
|
|
+ * will be unreferenced.
|
|
+ */
|
|
+extern int ttm_ref_object_base_unref(struct ttm_object_file *tfile,
|
|
+ unsigned long key,
|
|
+ enum ttm_ref_type ref_type);
|
|
+
|
|
+/**
|
|
+ * ttm_object_file_init - initialize a struct ttm_object file
|
|
+ *
|
|
+ * @tdev: A struct ttm_object device this file is initialized on.
|
|
+ * @hash_order: Order of the hash table used to hold the reference objects.
|
|
+ *
|
|
+ * This is typically called by the file_ops::open function.
|
|
+ */
|
|
+
|
|
+extern struct ttm_object_file *ttm_object_file_init(struct ttm_object_device
|
|
+ *tdev,
|
|
+ unsigned int hash_order);
|
|
+
|
|
+/**
|
|
+ * ttm_object_file_release - release data held by a ttm_object_file
|
|
+ *
|
|
+ * @p_tfile: Pointer to pointer to the ttm_object_file object to release.
|
|
+ * *p_tfile will be set to NULL by this function.
|
|
+ *
|
|
+ * Releases all data associated by a ttm_object_file.
|
|
+ * Typically called from file_ops::release. The caller must
|
|
+ * ensure that there are no concurrent users of tfile.
|
|
+ */
|
|
+
|
|
+extern void ttm_object_file_release(struct ttm_object_file **p_tfile);
|
|
+
|
|
+/**
|
|
+ * ttm_object device init - initialize a struct ttm_object_device
|
|
+ *
|
|
+ * @hash_order: Order of hash table used to hash the base objects.
|
|
+ *
|
|
+ * This function is typically called on device initialization to prepare
|
|
+ * data structures needed for ttm base and ref objects.
|
|
+ */
|
|
+
|
|
+extern struct ttm_object_device *ttm_object_device_init
|
|
+ (struct ttm_mem_global *mem_glob, unsigned int hash_order);
|
|
+
|
|
+/**
|
|
+ * ttm_object_device_release - release data held by a ttm_object_device
|
|
+ *
|
|
+ * @p_tdev: Pointer to pointer to the ttm_object_device object to release.
|
|
+ * *p_tdev will be set to NULL by this function.
|
|
+ *
|
|
+ * Releases all data associated by a ttm_object_device.
|
|
+ * Typically called from driver::unload before the destruction of the
|
|
+ * device private data structure.
|
|
+ */
|
|
+
|
|
+extern void ttm_object_device_release(struct ttm_object_device **p_tdev);
|
|
+
|
|
+#endif
|
|
diff --git a/include/drm/via_drm.h b/include/drm/via_drm.h
|
|
index 170786e..fd11a5b 100644
|
|
--- a/include/drm/via_drm.h
|
|
+++ b/include/drm/via_drm.h
|
|
@@ -24,7 +24,7 @@
|
|
#ifndef _VIA_DRM_H_
|
|
#define _VIA_DRM_H_
|
|
|
|
-#include <linux/types.h>
|
|
+#include "drm.h"
|
|
|
|
/* WARNING: These defines must be the same as what the Xserver uses.
|
|
* if you change them, you must change the defines in the Xserver.
|
|
diff --git a/include/drm/vmwgfx_drm.h b/include/drm/vmwgfx_drm.h
|
|
new file mode 100644
|
|
index 0000000..c7645f4
|
|
--- /dev/null
|
|
+++ b/include/drm/vmwgfx_drm.h
|
|
@@ -0,0 +1,588 @@
|
|
+/**************************************************************************
|
|
+ *
|
|
+ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
|
|
+ * All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the
|
|
+ * "Software"), to deal in the Software without restriction, including
|
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
+ * distribute, sub license, and/or sell copies of the Software, and to
|
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice (including the
|
|
+ * next paragraph) shall be included in all copies or substantial portions
|
|
+ * of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
+ *
|
|
+ **************************************************************************/
|
|
+
|
|
+#ifndef __VMWGFX_DRM_H__
|
|
+#define __VMWGFX_DRM_H__
|
|
+
|
|
+#define DRM_VMW_MAX_SURFACE_FACES 6
|
|
+#define DRM_VMW_MAX_MIP_LEVELS 24
|
|
+
|
|
+#define DRM_VMW_EXT_NAME_LEN 128
|
|
+
|
|
+#define DRM_VMW_GET_PARAM 0
|
|
+#define DRM_VMW_ALLOC_DMABUF 1
|
|
+#define DRM_VMW_UNREF_DMABUF 2
|
|
+#define DRM_VMW_CURSOR_BYPASS 3
|
|
+/* guarded by DRM_VMW_PARAM_NUM_STREAMS != 0*/
|
|
+#define DRM_VMW_CONTROL_STREAM 4
|
|
+#define DRM_VMW_CLAIM_STREAM 5
|
|
+#define DRM_VMW_UNREF_STREAM 6
|
|
+/* guarded by DRM_VMW_PARAM_3D == 1 */
|
|
+#define DRM_VMW_CREATE_CONTEXT 7
|
|
+#define DRM_VMW_UNREF_CONTEXT 8
|
|
+#define DRM_VMW_CREATE_SURFACE 9
|
|
+#define DRM_VMW_UNREF_SURFACE 10
|
|
+#define DRM_VMW_REF_SURFACE 11
|
|
+#define DRM_VMW_EXECBUF 12
|
|
+#define DRM_VMW_FIFO_DEBUG 13
|
|
+#define DRM_VMW_FENCE_WAIT 14
|
|
+
|
|
+
|
|
+/*************************************************************************/
|
|
+/**
|
|
+ * DRM_VMW_GET_PARAM - get device information.
|
|
+ *
|
|
+ * DRM_VMW_PARAM_FIFO_OFFSET:
|
|
+ * Offset to use to map the first page of the FIFO read-only.
|
|
+ * The fifo is mapped using the mmap() system call on the drm device.
|
|
+ *
|
|
+ * DRM_VMW_PARAM_OVERLAY_IOCTL:
|
|
+ * Does the driver support the overlay ioctl.
|
|
+ */
|
|
+
|
|
+#define DRM_VMW_PARAM_NUM_STREAMS 0
|
|
+#define DRM_VMW_PARAM_NUM_FREE_STREAMS 1
|
|
+#define DRM_VMW_PARAM_3D 2
|
|
+#define DRM_VMW_PARAM_FIFO_OFFSET 3
|
|
+#define DRM_VMW_PARAM_HW_CAPS 4
|
|
+#define DRM_VMW_PARAM_FIFO_CAPS 5
|
|
+
|
|
+/**
|
|
+ * struct drm_vmw_getparam_arg
|
|
+ *
|
|
+ * @value: Returned value. //Out
|
|
+ * @param: Parameter to query. //In.
|
|
+ *
|
|
+ * Argument to the DRM_VMW_GET_PARAM Ioctl.
|
|
+ */
|
|
+
|
|
+struct drm_vmw_getparam_arg {
|
|
+ uint64_t value;
|
|
+ uint32_t param;
|
|
+ uint32_t pad64;
|
|
+};
|
|
+
|
|
+/*************************************************************************/
|
|
+/**
|
|
+ * DRM_VMW_EXTENSION - Query device extensions.
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * struct drm_vmw_extension_rep
|
|
+ *
|
|
+ * @exists: The queried extension exists.
|
|
+ * @driver_ioctl_offset: Ioctl number of the first ioctl in the extension.
|
|
+ * @driver_sarea_offset: Offset to any space in the DRI SAREA
|
|
+ * used by the extension.
|
|
+ * @major: Major version number of the extension.
|
|
+ * @minor: Minor version number of the extension.
|
|
+ * @pl: Patch level version number of the extension.
|
|
+ *
|
|
+ * Output argument to the DRM_VMW_EXTENSION Ioctl.
|
|
+ */
|
|
+
|
|
+struct drm_vmw_extension_rep {
|
|
+ int32_t exists;
|
|
+ uint32_t driver_ioctl_offset;
|
|
+ uint32_t driver_sarea_offset;
|
|
+ uint32_t major;
|
|
+ uint32_t minor;
|
|
+ uint32_t pl;
|
|
+ uint32_t pad64;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * union drm_vmw_extension_arg
|
|
+ *
|
|
+ * @extension - Ascii name of the extension to be queried. //In
|
|
+ * @rep - Reply as defined above. //Out
|
|
+ *
|
|
+ * Argument to the DRM_VMW_EXTENSION Ioctl.
|
|
+ */
|
|
+
|
|
+union drm_vmw_extension_arg {
|
|
+ char extension[DRM_VMW_EXT_NAME_LEN];
|
|
+ struct drm_vmw_extension_rep rep;
|
|
+};
|
|
+
|
|
+/*************************************************************************/
|
|
+/**
|
|
+ * DRM_VMW_CREATE_CONTEXT - Create a host context.
|
|
+ *
|
|
+ * Allocates a device unique context id, and queues a create context command
|
|
+ * for the host. Does not wait for host completion.
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * struct drm_vmw_context_arg
|
|
+ *
|
|
+ * @cid: Device unique context ID.
|
|
+ *
|
|
+ * Output argument to the DRM_VMW_CREATE_CONTEXT Ioctl.
|
|
+ * Input argument to the DRM_VMW_UNREF_CONTEXT Ioctl.
|
|
+ */
|
|
+
|
|
+struct drm_vmw_context_arg {
|
|
+ int32_t cid;
|
|
+ uint32_t pad64;
|
|
+};
|
|
+
|
|
+/*************************************************************************/
|
|
+/**
|
|
+ * DRM_VMW_UNREF_CONTEXT - Create a host context.
|
|
+ *
|
|
+ * Frees a global context id, and queues a destroy host command for the host.
|
|
+ * Does not wait for host completion. The context ID can be used directly
|
|
+ * in the command stream and shows up as the same context ID on the host.
|
|
+ */
|
|
+
|
|
+/*************************************************************************/
|
|
+/**
|
|
+ * DRM_VMW_CREATE_SURFACE - Create a host suface.
|
|
+ *
|
|
+ * Allocates a device unique surface id, and queues a create surface command
|
|
+ * for the host. Does not wait for host completion. The surface ID can be
|
|
+ * used directly in the command stream and shows up as the same surface
|
|
+ * ID on the host.
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * struct drm_wmv_surface_create_req
|
|
+ *
|
|
+ * @flags: Surface flags as understood by the host.
|
|
+ * @format: Surface format as understood by the host.
|
|
+ * @mip_levels: Number of mip levels for each face.
|
|
+ * An unused face should have 0 encoded.
|
|
+ * @size_addr: Address of a user-space array of sruct drm_vmw_size
|
|
+ * cast to an uint64_t for 32-64 bit compatibility.
|
|
+ * The size of the array should equal the total number of mipmap levels.
|
|
+ * @shareable: Boolean whether other clients (as identified by file descriptors)
|
|
+ * may reference this surface.
|
|
+ * @scanout: Boolean whether the surface is intended to be used as a
|
|
+ * scanout.
|
|
+ *
|
|
+ * Input data to the DRM_VMW_CREATE_SURFACE Ioctl.
|
|
+ * Output data from the DRM_VMW_REF_SURFACE Ioctl.
|
|
+ */
|
|
+
|
|
+struct drm_vmw_surface_create_req {
|
|
+ uint32_t flags;
|
|
+ uint32_t format;
|
|
+ uint32_t mip_levels[DRM_VMW_MAX_SURFACE_FACES];
|
|
+ uint64_t size_addr;
|
|
+ int32_t shareable;
|
|
+ int32_t scanout;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct drm_wmv_surface_arg
|
|
+ *
|
|
+ * @sid: Surface id of created surface or surface to destroy or reference.
|
|
+ *
|
|
+ * Output data from the DRM_VMW_CREATE_SURFACE Ioctl.
|
|
+ * Input argument to the DRM_VMW_UNREF_SURFACE Ioctl.
|
|
+ * Input argument to the DRM_VMW_REF_SURFACE Ioctl.
|
|
+ */
|
|
+
|
|
+struct drm_vmw_surface_arg {
|
|
+ int32_t sid;
|
|
+ uint32_t pad64;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct drm_vmw_size ioctl.
|
|
+ *
|
|
+ * @width - mip level width
|
|
+ * @height - mip level height
|
|
+ * @depth - mip level depth
|
|
+ *
|
|
+ * Description of a mip level.
|
|
+ * Input data to the DRM_WMW_CREATE_SURFACE Ioctl.
|
|
+ */
|
|
+
|
|
+struct drm_vmw_size {
|
|
+ uint32_t width;
|
|
+ uint32_t height;
|
|
+ uint32_t depth;
|
|
+ uint32_t pad64;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * union drm_vmw_surface_create_arg
|
|
+ *
|
|
+ * @rep: Output data as described above.
|
|
+ * @req: Input data as described above.
|
|
+ *
|
|
+ * Argument to the DRM_VMW_CREATE_SURFACE Ioctl.
|
|
+ */
|
|
+
|
|
+union drm_vmw_surface_create_arg {
|
|
+ struct drm_vmw_surface_arg rep;
|
|
+ struct drm_vmw_surface_create_req req;
|
|
+};
|
|
+
|
|
+/*************************************************************************/
|
|
+/**
|
|
+ * DRM_VMW_REF_SURFACE - Reference a host surface.
|
|
+ *
|
|
+ * Puts a reference on a host surface with a give sid, as previously
|
|
+ * returned by the DRM_VMW_CREATE_SURFACE ioctl.
|
|
+ * A reference will make sure the surface isn't destroyed while we hold
|
|
+ * it and will allow the calling client to use the surface ID in the command
|
|
+ * stream.
|
|
+ *
|
|
+ * On successful return, the Ioctl returns the surface information given
|
|
+ * in the DRM_VMW_CREATE_SURFACE ioctl.
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * union drm_vmw_surface_reference_arg
|
|
+ *
|
|
+ * @rep: Output data as described above.
|
|
+ * @req: Input data as described above.
|
|
+ *
|
|
+ * Argument to the DRM_VMW_REF_SURFACE Ioctl.
|
|
+ */
|
|
+
|
|
+union drm_vmw_surface_reference_arg {
|
|
+ struct drm_vmw_surface_create_req rep;
|
|
+ struct drm_vmw_surface_arg req;
|
|
+};
|
|
+
|
|
+/*************************************************************************/
|
|
+/**
|
|
+ * DRM_VMW_UNREF_SURFACE - Unreference a host surface.
|
|
+ *
|
|
+ * Clear a reference previously put on a host surface.
|
|
+ * When all references are gone, including the one implicitly placed
|
|
+ * on creation,
|
|
+ * a destroy surface command will be queued for the host.
|
|
+ * Does not wait for completion.
|
|
+ */
|
|
+
|
|
+/*************************************************************************/
|
|
+/**
|
|
+ * DRM_VMW_EXECBUF
|
|
+ *
|
|
+ * Submit a command buffer for execution on the host, and return a
|
|
+ * fence sequence that when signaled, indicates that the command buffer has
|
|
+ * executed.
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * struct drm_vmw_execbuf_arg
|
|
+ *
|
|
+ * @commands: User-space address of a command buffer cast to an uint64_t.
|
|
+ * @command-size: Size in bytes of the command buffer.
|
|
+ * @throttle-us: Sleep until software is less than @throttle_us
|
|
+ * microseconds ahead of hardware. The driver may round this value
|
|
+ * to the nearest kernel tick.
|
|
+ * @fence_rep: User-space address of a struct drm_vmw_fence_rep cast to an
|
|
+ * uint64_t.
|
|
+ * @version: Allows expanding the execbuf ioctl parameters without breaking
|
|
+ * backwards compatibility, since user-space will always tell the kernel
|
|
+ * which version it uses.
|
|
+ * @flags: Execbuf flags. None currently.
|
|
+ *
|
|
+ * Argument to the DRM_VMW_EXECBUF Ioctl.
|
|
+ */
|
|
+
|
|
+#define DRM_VMW_EXECBUF_VERSION 0
|
|
+
|
|
+struct drm_vmw_execbuf_arg {
|
|
+ uint64_t commands;
|
|
+ uint32_t command_size;
|
|
+ uint32_t throttle_us;
|
|
+ uint64_t fence_rep;
|
|
+ uint32_t version;
|
|
+ uint32_t flags;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct drm_vmw_fence_rep
|
|
+ *
|
|
+ * @fence_seq: Fence sequence associated with a command submission.
|
|
+ * @error: This member should've been set to -EFAULT on submission.
|
|
+ * The following actions should be take on completion:
|
|
+ * error == -EFAULT: Fence communication failed. The host is synchronized.
|
|
+ * Use the last fence id read from the FIFO fence register.
|
|
+ * error != 0 && error != -EFAULT:
|
|
+ * Fence submission failed. The host is synchronized. Use the fence_seq member.
|
|
+ * error == 0: All is OK, The host may not be synchronized.
|
|
+ * Use the fence_seq member.
|
|
+ *
|
|
+ * Input / Output data to the DRM_VMW_EXECBUF Ioctl.
|
|
+ */
|
|
+
|
|
+struct drm_vmw_fence_rep {
|
|
+ uint64_t fence_seq;
|
|
+ int32_t error;
|
|
+ uint32_t pad64;
|
|
+};
|
|
+
|
|
+/*************************************************************************/
|
|
+/**
|
|
+ * DRM_VMW_ALLOC_DMABUF
|
|
+ *
|
|
+ * Allocate a DMA buffer that is visible also to the host.
|
|
+ * NOTE: The buffer is
|
|
+ * identified by a handle and an offset, which are private to the guest, but
|
|
+ * useable in the command stream. The guest kernel may translate these
|
|
+ * and patch up the command stream accordingly. In the future, the offset may
|
|
+ * be zero at all times, or it may disappear from the interface before it is
|
|
+ * fixed.
|
|
+ *
|
|
+ * The DMA buffer may stay user-space mapped in the guest at all times,
|
|
+ * and is thus suitable for sub-allocation.
|
|
+ *
|
|
+ * DMA buffers are mapped using the mmap() syscall on the drm device.
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * struct drm_vmw_alloc_dmabuf_req
|
|
+ *
|
|
+ * @size: Required minimum size of the buffer.
|
|
+ *
|
|
+ * Input data to the DRM_VMW_ALLOC_DMABUF Ioctl.
|
|
+ */
|
|
+
|
|
+struct drm_vmw_alloc_dmabuf_req {
|
|
+ uint32_t size;
|
|
+ uint32_t pad64;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct drm_vmw_dmabuf_rep
|
|
+ *
|
|
+ * @map_handle: Offset to use in the mmap() call used to map the buffer.
|
|
+ * @handle: Handle unique to this buffer. Used for unreferencing.
|
|
+ * @cur_gmr_id: GMR id to use in the command stream when this buffer is
|
|
+ * referenced. See not above.
|
|
+ * @cur_gmr_offset: Offset to use in the command stream when this buffer is
|
|
+ * referenced. See note above.
|
|
+ *
|
|
+ * Output data from the DRM_VMW_ALLOC_DMABUF Ioctl.
|
|
+ */
|
|
+
|
|
+struct drm_vmw_dmabuf_rep {
|
|
+ uint64_t map_handle;
|
|
+ uint32_t handle;
|
|
+ uint32_t cur_gmr_id;
|
|
+ uint32_t cur_gmr_offset;
|
|
+ uint32_t pad64;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * union drm_vmw_dmabuf_arg
|
|
+ *
|
|
+ * @req: Input data as described above.
|
|
+ * @rep: Output data as described above.
|
|
+ *
|
|
+ * Argument to the DRM_VMW_ALLOC_DMABUF Ioctl.
|
|
+ */
|
|
+
|
|
+union drm_vmw_alloc_dmabuf_arg {
|
|
+ struct drm_vmw_alloc_dmabuf_req req;
|
|
+ struct drm_vmw_dmabuf_rep rep;
|
|
+};
|
|
+
|
|
+/*************************************************************************/
|
|
+/**
|
|
+ * DRM_VMW_UNREF_DMABUF - Free a DMA buffer.
|
|
+ *
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * struct drm_vmw_unref_dmabuf_arg
|
|
+ *
|
|
+ * @handle: Handle indicating what buffer to free. Obtained from the
|
|
+ * DRM_VMW_ALLOC_DMABUF Ioctl.
|
|
+ *
|
|
+ * Argument to the DRM_VMW_UNREF_DMABUF Ioctl.
|
|
+ */
|
|
+
|
|
+struct drm_vmw_unref_dmabuf_arg {
|
|
+ uint32_t handle;
|
|
+ uint32_t pad64;
|
|
+};
|
|
+
|
|
+/*************************************************************************/
|
|
+/**
|
|
+ * DRM_VMW_FIFO_DEBUG - Get last FIFO submission.
|
|
+ *
|
|
+ * This IOCTL copies the last FIFO submission directly out of the FIFO buffer.
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * struct drm_vmw_fifo_debug_arg
|
|
+ *
|
|
+ * @debug_buffer: User space address of a debug_buffer cast to an uint64_t //In
|
|
+ * @debug_buffer_size: Size in bytes of debug buffer //In
|
|
+ * @used_size: Number of bytes copied to the buffer // Out
|
|
+ * @did_not_fit: Boolean indicating that the fifo contents did not fit. //Out
|
|
+ *
|
|
+ * Argument to the DRM_VMW_FIFO_DEBUG Ioctl.
|
|
+ */
|
|
+
|
|
+struct drm_vmw_fifo_debug_arg {
|
|
+ uint64_t debug_buffer;
|
|
+ uint32_t debug_buffer_size;
|
|
+ uint32_t used_size;
|
|
+ int32_t did_not_fit;
|
|
+ uint32_t pad64;
|
|
+};
|
|
+
|
|
+struct drm_vmw_fence_wait_arg {
|
|
+ uint64_t sequence;
|
|
+ uint64_t kernel_cookie;
|
|
+ int32_t cookie_valid;
|
|
+ int32_t pad64;
|
|
+};
|
|
+
|
|
+/*************************************************************************/
|
|
+/**
|
|
+ * DRM_VMW_CONTROL_STREAM - Control overlays, aka streams.
|
|
+ *
|
|
+ * This IOCTL controls the overlay units of the svga device.
|
|
+ * The SVGA overlay units does not work like regular hardware units in
|
|
+ * that they do not automaticaly read back the contents of the given dma
|
|
+ * buffer. But instead only read back for each call to this ioctl, and
|
|
+ * at any point between this call being made and a following call that
|
|
+ * either changes the buffer or disables the stream.
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * struct drm_vmw_rect
|
|
+ *
|
|
+ * Defines a rectangle. Used in the overlay ioctl to define
|
|
+ * source and destination rectangle.
|
|
+ */
|
|
+
|
|
+struct drm_vmw_rect {
|
|
+ int32_t x;
|
|
+ int32_t y;
|
|
+ uint32_t w;
|
|
+ uint32_t h;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct drm_vmw_control_stream_arg
|
|
+ *
|
|
+ * @stream_id: Stearm to control
|
|
+ * @enabled: If false all following arguments are ignored.
|
|
+ * @handle: Handle to buffer for getting data from.
|
|
+ * @format: Format of the overlay as understood by the host.
|
|
+ * @width: Width of the overlay.
|
|
+ * @height: Height of the overlay.
|
|
+ * @size: Size of the overlay in bytes.
|
|
+ * @pitch: Array of pitches, the two last are only used for YUV12 formats.
|
|
+ * @offset: Offset from start of dma buffer to overlay.
|
|
+ * @src: Source rect, must be within the defined area above.
|
|
+ * @dst: Destination rect, x and y may be negative.
|
|
+ *
|
|
+ * Argument to the DRM_VMW_CONTROL_STREAM Ioctl.
|
|
+ */
|
|
+
|
|
+struct drm_vmw_control_stream_arg {
|
|
+ uint32_t stream_id;
|
|
+ uint32_t enabled;
|
|
+
|
|
+ uint32_t flags;
|
|
+ uint32_t color_key;
|
|
+
|
|
+ uint32_t handle;
|
|
+ uint32_t offset;
|
|
+ int32_t format;
|
|
+ uint32_t size;
|
|
+ uint32_t width;
|
|
+ uint32_t height;
|
|
+ uint32_t pitch[3];
|
|
+
|
|
+ uint32_t pad64;
|
|
+ struct drm_vmw_rect src;
|
|
+ struct drm_vmw_rect dst;
|
|
+};
|
|
+
|
|
+/*************************************************************************/
|
|
+/**
|
|
+ * DRM_VMW_CURSOR_BYPASS - Give extra information about cursor bypass.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#define DRM_VMW_CURSOR_BYPASS_ALL (1 << 0)
|
|
+#define DRM_VMW_CURSOR_BYPASS_FLAGS (1)
|
|
+
|
|
+/**
|
|
+ * struct drm_vmw_cursor_bypass_arg
|
|
+ *
|
|
+ * @flags: Flags.
|
|
+ * @crtc_id: Crtc id, only used if DMR_CURSOR_BYPASS_ALL isn't passed.
|
|
+ * @xpos: X position of cursor.
|
|
+ * @ypos: Y position of cursor.
|
|
+ * @xhot: X hotspot.
|
|
+ * @yhot: Y hotspot.
|
|
+ *
|
|
+ * Argument to the DRM_VMW_CURSOR_BYPASS Ioctl.
|
|
+ */
|
|
+
|
|
+struct drm_vmw_cursor_bypass_arg {
|
|
+ uint32_t flags;
|
|
+ uint32_t crtc_id;
|
|
+ int32_t xpos;
|
|
+ int32_t ypos;
|
|
+ int32_t xhot;
|
|
+ int32_t yhot;
|
|
+};
|
|
+
|
|
+/*************************************************************************/
|
|
+/**
|
|
+ * DRM_VMW_CLAIM_STREAM - Claim a single stream.
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * struct drm_vmw_context_arg
|
|
+ *
|
|
+ * @stream_id: Device unique context ID.
|
|
+ *
|
|
+ * Output argument to the DRM_VMW_CREATE_CONTEXT Ioctl.
|
|
+ * Input argument to the DRM_VMW_UNREF_CONTEXT Ioctl.
|
|
+ */
|
|
+
|
|
+struct drm_vmw_stream_arg {
|
|
+ uint32_t stream_id;
|
|
+ uint32_t pad64;
|
|
+};
|
|
+
|
|
+/*************************************************************************/
|
|
+/**
|
|
+ * DRM_VMW_UNREF_STREAM - Unclaim a stream.
|
|
+ *
|
|
+ * Return a single stream that was claimed by this process. Also makes
|
|
+ * sure that the stream has been stopped.
|
|
+ */
|
|
+
|
|
+#endif
|
|
diff --git a/include/linux/list_sort.h b/include/linux/list_sort.h
|
|
new file mode 100644
|
|
index 0000000..1a2df2e
|
|
--- /dev/null
|
|
+++ b/include/linux/list_sort.h
|
|
@@ -0,0 +1,11 @@
|
|
+#ifndef _LINUX_LIST_SORT_H
|
|
+#define _LINUX_LIST_SORT_H
|
|
+
|
|
+#include <linux/types.h>
|
|
+
|
|
+struct list_head;
|
|
+
|
|
+void list_sort(void *priv, struct list_head *head,
|
|
+ int (*cmp)(void *priv, struct list_head *a,
|
|
+ struct list_head *b));
|
|
+#endif
|
|
diff --git a/lib/Makefile b/lib/Makefile
|
|
index 2e78277..bae1a76 100644
|
|
--- a/lib/Makefile
|
|
+++ b/lib/Makefile
|
|
@@ -21,7 +21,7 @@ lib-y += kobject.o kref.o klist.o
|
|
|
|
obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \
|
|
bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \
|
|
- string_helpers.o gcd.o lcm.o
|
|
+ string_helpers.o gcd.o lcm.o list_sort.o
|
|
|
|
ifeq ($(CONFIG_DEBUG_KOBJECT),y)
|
|
CFLAGS_kobject.o += -DDEBUG
|
|
diff --git a/lib/list_sort.c b/lib/list_sort.c
|
|
new file mode 100644
|
|
index 0000000..19d11e0
|
|
--- /dev/null
|
|
+++ b/lib/list_sort.c
|
|
@@ -0,0 +1,102 @@
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/list_sort.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/list.h>
|
|
+
|
|
+/**
|
|
+ * list_sort - sort a list.
|
|
+ * @priv: private data, passed to @cmp
|
|
+ * @head: the list to sort
|
|
+ * @cmp: the elements comparison function
|
|
+ *
|
|
+ * This function has been implemented by Mark J Roberts <mjr@znex.org>. It
|
|
+ * implements "merge sort" which has O(nlog(n)) complexity. The list is sorted
|
|
+ * in ascending order.
|
|
+ *
|
|
+ * The comparison function @cmp is supposed to return a negative value if @a is
|
|
+ * less than @b, and a positive value if @a is greater than @b. If @a and @b
|
|
+ * are equivalent, then it does not matter what this function returns.
|
|
+ */
|
|
+void list_sort(void *priv, struct list_head *head,
|
|
+ int (*cmp)(void *priv, struct list_head *a,
|
|
+ struct list_head *b))
|
|
+{
|
|
+ struct list_head *p, *q, *e, *list, *tail, *oldhead;
|
|
+ int insize, nmerges, psize, qsize, i;
|
|
+
|
|
+ if (list_empty(head))
|
|
+ return;
|
|
+
|
|
+ list = head->next;
|
|
+ list_del(head);
|
|
+ insize = 1;
|
|
+ for (;;) {
|
|
+ p = oldhead = list;
|
|
+ list = tail = NULL;
|
|
+ nmerges = 0;
|
|
+
|
|
+ while (p) {
|
|
+ nmerges++;
|
|
+ q = p;
|
|
+ psize = 0;
|
|
+ for (i = 0; i < insize; i++) {
|
|
+ psize++;
|
|
+ q = q->next == oldhead ? NULL : q->next;
|
|
+ if (!q)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ qsize = insize;
|
|
+ while (psize > 0 || (qsize > 0 && q)) {
|
|
+ if (!psize) {
|
|
+ e = q;
|
|
+ q = q->next;
|
|
+ qsize--;
|
|
+ if (q == oldhead)
|
|
+ q = NULL;
|
|
+ } else if (!qsize || !q) {
|
|
+ e = p;
|
|
+ p = p->next;
|
|
+ psize--;
|
|
+ if (p == oldhead)
|
|
+ p = NULL;
|
|
+ } else if (cmp(priv, p, q) <= 0) {
|
|
+ e = p;
|
|
+ p = p->next;
|
|
+ psize--;
|
|
+ if (p == oldhead)
|
|
+ p = NULL;
|
|
+ } else {
|
|
+ e = q;
|
|
+ q = q->next;
|
|
+ qsize--;
|
|
+ if (q == oldhead)
|
|
+ q = NULL;
|
|
+ }
|
|
+ if (tail)
|
|
+ tail->next = e;
|
|
+ else
|
|
+ list = e;
|
|
+ e->prev = tail;
|
|
+ tail = e;
|
|
+ }
|
|
+ p = q;
|
|
+ }
|
|
+
|
|
+ tail->next = list;
|
|
+ list->prev = tail;
|
|
+
|
|
+ if (nmerges <= 1)
|
|
+ break;
|
|
+
|
|
+ insize *= 2;
|
|
+ }
|
|
+
|
|
+ head->next = list;
|
|
+ head->prev = list->prev;
|
|
+ list->prev->next = head;
|
|
+ list->prev = head;
|
|
+}
|
|
+
|
|
+EXPORT_SYMBOL(list_sort);
|