summaryrefslogtreecommitdiff
path: root/usr/src/uts/intel/io/agpgart/agpgart.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/intel/io/agpgart/agpgart.c')
-rw-r--r--usr/src/uts/intel/io/agpgart/agpgart.c264
1 files changed, 259 insertions, 5 deletions
diff --git a/usr/src/uts/intel/io/agpgart/agpgart.c b/usr/src/uts/intel/io/agpgart/agpgart.c
index 005dc57ac0..0f7ce74458 100644
--- a/usr/src/uts/intel/io/agpgart/agpgart.c
+++ b/usr/src/uts/intel/io/agpgart/agpgart.c
@@ -1,5 +1,10 @@
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright (c) 2009, Intel Corporation.
+ * All Rights Reserved.
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
@@ -59,8 +64,55 @@ static void *agpgart_glob_soft_handle;
#define IS_TRUE_AGP(type) (((type) == ARC_INTELAGP) || \
((type) == ARC_AMD64AGP))
+#define AGP_HASH_NODE 1024
+
+static void
+list_head_init(struct list_head *head) {
+ struct list_head *entry, *tmp;
+ /* HASH for accelerate */
+ entry = kmem_zalloc(AGP_HASH_NODE *
+ sizeof (struct list_head), KM_NOSLEEP);
+ head->next = entry;
+ for (int i = 0; i < AGP_HASH_NODE; i++) {
+ tmp = &entry[i];
+ tmp->next = tmp;
+ tmp->prev = tmp;
+ tmp->gttseg = NULL;
+ }
+}
+
+static void
+list_head_add_new(struct list_head *head,
+ igd_gtt_seg_t *gttseg)
+{
+ struct list_head *entry, *tmp;
+ int key;
+ entry = kmem_zalloc(sizeof (*entry), KM_NOSLEEP);
+ key = gttseg->igs_pgstart % AGP_HASH_NODE;
+ tmp = &head->next[key];
+ tmp->next->prev = entry;
+ entry->next = tmp->next;
+ entry->prev = tmp;
+ tmp->next = entry;
+ entry->gttseg = gttseg;
+}
+
+static void
+list_head_del(struct list_head *entry) {
+ (entry)->next->prev = (entry)->prev; \
+ (entry)->prev->next = (entry)->next; \
+ (entry)->gttseg = NULL; \
+}
+
+#define list_head_for_each_safe(entry, temp, head) \
+ for (int key = 0; key < AGP_HASH_NODE; key++) \
+ for (entry = (&(head)->next[key])->next, temp = (entry)->next; \
+ entry != &(head)->next[key]; \
+ entry = temp, temp = temp->next)
+
+
#define agpinfo_default_to_32(v, v32) \
- { \
+ { \
(v32).agpi32_version = (v).agpi_version; \
(v32).agpi32_devid = (v).agpi_devid; \
(v32).agpi32_mode = (v).agpi_mode; \
@@ -1134,7 +1186,7 @@ lyr_flush_gart_cache(agp_registered_dev_t *agp_regdev)
* based on the ammount of physical pages.
* The algorithm is: compare the aperture size with 1/4 of total
* physical pages, and use the smaller one to for the max available
- * pages.
+ * pages. But the minimum video memory should be 192M.
*
* Arguments:
* aper_size system agp aperture size (in MB)
@@ -1145,14 +1197,18 @@ lyr_flush_gart_cache(agp_registered_dev_t *agp_regdev)
static uint32_t
get_max_pages(uint32_t aper_size)
{
- uint32_t i, j;
+ uint32_t i, j, size;
ASSERT(aper_size <= MAXAPERMEGAS);
i = AGP_MB2PAGES(aper_size);
j = (physmem >> 2);
- return ((i < j) ? i : j);
+ size = ((i < j) ? i : j);
+
+ if (size < AGP_MB2PAGES(MINAPERMEGAS))
+ size = AGP_MB2PAGES(MINAPERMEGAS);
+ return (size);
}
/*
@@ -2438,6 +2494,8 @@ agpgart_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
AGP_MAXKEYS * (sizeof (keytable_ent_t)),
KM_SLEEP);
+ list_head_init(&softstate->mapped_list);
+
return (DDI_SUCCESS);
err4:
agp_fini_kstats(softstate);
@@ -2484,6 +2542,21 @@ agpgart_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
st->asoft_table = 0;
}
+ struct list_head *entry, *temp, *head;
+ igd_gtt_seg_t *gttseg;
+ list_head_for_each_safe(entry, temp, &st->mapped_list) {
+ gttseg = entry->gttseg;
+ list_head_del(entry);
+ kmem_free(entry, sizeof (*entry));
+ kmem_free(gttseg->igs_phyaddr,
+ sizeof (uint32_t) * gttseg->igs_npage);
+ kmem_free(gttseg, sizeof (igd_gtt_seg_t));
+ }
+ head = &st->mapped_list;
+ kmem_free(head->next,
+ AGP_HASH_NODE * sizeof (struct list_head));
+ head->next = NULL;
+
ddi_remove_minor_node(dip, AGPGART_DEVNODE);
agp_fini_kstats(st);
ldi_ident_release(st->asoft_li);
@@ -2557,6 +2630,7 @@ agpgart_open(dev_t *dev, int openflags, int otyp, cred_t *credp)
int instance = AGP_DEV2INST(*dev);
agpgart_softstate_t *softstate;
int rc = 0;
+ uint32_t devid;
if (secpolicy_gart_access(credp)) {
AGPDB_PRINT2((CE_WARN, "agpgart_open: permission denied"));
@@ -2612,6 +2686,21 @@ agpgart_open(dev_t *dev, int openflags, int otyp, cred_t *credp)
return (EIO);
}
+ devid = softstate->asoft_info.agpki_mdevid;
+ if (IS_INTEL_915(devid) ||
+ IS_INTEL_965(devid) ||
+ IS_INTEL_X33(devid) ||
+ IS_INTEL_G4X(devid)) {
+ rc = ldi_ioctl(softstate->asoft_devreg.agprd_targethdl,
+ INTEL_CHIPSET_FLUSH_SETUP, 0, FKIOCTL, kcred, 0);
+ }
+ if (rc) {
+ AGPDB_PRINT2((CE_WARN,
+ "agpgart_open: Intel chipset flush setup error"));
+ lyr_end(&softstate->asoft_devreg);
+ mutex_exit(&softstate->asoft_instmutex);
+ return (EIO);
+ }
mutex_exit(&softstate->asoft_instmutex);
return (0);
}
@@ -2698,6 +2787,8 @@ agpgart_close(dev_t dev, int flag, int otyp, cred_t *credp)
{
int instance = AGP_DEV2INST(dev);
agpgart_softstate_t *softstate;
+ int rc = 0;
+ uint32_t devid;
softstate = ddi_get_soft_state(agpgart_glob_soft_handle, instance);
if (softstate == NULL) {
@@ -2721,6 +2812,19 @@ agpgart_close(dev_t dev, int flag, int otyp, cred_t *credp)
release_control(softstate);
}
+ devid = softstate->asoft_info.agpki_mdevid;
+ if (IS_INTEL_915(devid) ||
+ IS_INTEL_965(devid) ||
+ IS_INTEL_X33(devid) ||
+ IS_INTEL_G4X(devid)) {
+ rc = ldi_ioctl(softstate->asoft_devreg.agprd_targethdl,
+ INTEL_CHIPSET_FLUSH_FREE, 0, FKIOCTL, kcred, 0);
+ }
+ if (rc) {
+ AGPDB_PRINT2((CE_WARN,
+ "agpgart_open: Intel chipset flush free error"));
+ }
+
if (lyr_unconfig_devices(&softstate->asoft_devreg)) {
AGPDB_PRINT2((CE_WARN,
"agpgart_close: lyr_unconfig_device error"));
@@ -3020,6 +3124,144 @@ ioctl_agpgart_unbind(agpgart_softstate_t *st, void *arg, int flags)
return (0);
}
+static int
+ioctl_agpgart_flush_chipset(agpgart_softstate_t *st)
+{
+ ldi_handle_t hdl;
+ uint32_t devid;
+ int rc = 0;
+ devid = st->asoft_info.agpki_mdevid;
+ hdl = st->asoft_devreg.agprd_targethdl;
+ if (IS_INTEL_915(devid) ||
+ IS_INTEL_965(devid) ||
+ IS_INTEL_X33(devid) ||
+ IS_INTEL_G4X(devid)) {
+ rc = ldi_ioctl(hdl, INTEL_CHIPSET_FLUSH, 0, FKIOCTL, kcred, 0);
+ }
+ return (rc);
+}
+
+static int
+ioctl_agpgart_pages_bind(agpgart_softstate_t *st, void *arg, int flags)
+{
+ agp_bind_pages_t bind_info;
+ uint32_t pg_offset;
+ int err = 0;
+ ldi_handle_t hdl;
+ uint32_t npages;
+ igd_gtt_seg_t *gttseg;
+ uint32_t i;
+ int rval;
+ if (ddi_copyin(arg, &bind_info,
+ sizeof (agp_bind_pages_t), flags) != 0) {
+ return (EFAULT);
+ }
+
+ gttseg = (igd_gtt_seg_t *)kmem_zalloc(sizeof (igd_gtt_seg_t),
+ KM_SLEEP);
+
+ pg_offset = bind_info.agpb_pgstart;
+
+ gttseg->igs_pgstart = pg_offset;
+ npages = (uint32_t)bind_info.agpb_pgcount;
+ gttseg->igs_npage = npages;
+
+ gttseg->igs_type = AGP_NORMAL;
+ gttseg->igs_phyaddr = (uint32_t *)kmem_zalloc
+ (sizeof (uint32_t) * gttseg->igs_npage, KM_SLEEP);
+
+ for (i = 0; i < npages; i++) {
+ gttseg->igs_phyaddr[i] = bind_info.agpb_pages[i] <<
+ GTT_PAGE_SHIFT;
+ }
+
+ hdl = st->asoft_devreg.agprd_masterhdl;
+ if (ldi_ioctl(hdl, I8XX_ADD2GTT, (intptr_t)gttseg, FKIOCTL,
+ kcred, &rval)) {
+ AGPDB_PRINT2((CE_WARN, "ioctl_agpgart_pages_bind: start0x%x",
+ gttseg->igs_pgstart));
+ AGPDB_PRINT2((CE_WARN, "ioctl_agpgart_pages_bind: pages=0x%x",
+ gttseg->igs_npage));
+ AGPDB_PRINT2((CE_WARN, "ioctl_agpgart_pages_bind: type=0x%x",
+ gttseg->igs_type));
+ err = -1;
+ }
+
+ list_head_add_new(&st->mapped_list, gttseg);
+ return (err);
+}
+
+static int
+ioctl_agpgart_pages_unbind(agpgart_softstate_t *st, void *arg, int flags)
+{
+ agp_unbind_pages_t unbind_info;
+ int rval;
+ ldi_handle_t hdl;
+ igd_gtt_seg_t *gttseg;
+
+ if (ddi_copyin(arg, &unbind_info, sizeof (unbind_info), flags) != 0) {
+ return (EFAULT);
+ }
+
+ struct list_head *entry, *temp;
+ list_head_for_each_safe(entry, temp, &st->mapped_list) {
+ if (entry->gttseg->igs_pgstart == unbind_info.agpb_pgstart) {
+ gttseg = entry->gttseg;
+ /* not unbind if VT switch */
+ if (unbind_info.agpb_type) {
+ list_head_del(entry);
+ kmem_free(entry, sizeof (*entry));
+ }
+ break;
+ }
+ }
+ ASSERT(gttseg != NULL);
+ gttseg->igs_pgstart = unbind_info.agpb_pgstart;
+ ASSERT(gttseg->igs_npage == unbind_info.agpb_pgcount);
+
+ hdl = st->asoft_devreg.agprd_masterhdl;
+ if (ldi_ioctl(hdl, I8XX_REM_GTT, (intptr_t)gttseg, FKIOCTL,
+ kcred, &rval))
+ return (-1);
+
+ if (unbind_info.agpb_type) {
+ kmem_free(gttseg->igs_phyaddr, sizeof (uint32_t) *
+ gttseg->igs_npage);
+ kmem_free(gttseg, sizeof (igd_gtt_seg_t));
+ }
+
+ return (0);
+}
+
+static int
+ioctl_agpgart_pages_rebind(agpgart_softstate_t *st)
+{
+ int rval;
+ ldi_handle_t hdl;
+ igd_gtt_seg_t *gttseg;
+ int err = 0;
+
+ hdl = st->asoft_devreg.agprd_masterhdl;
+ struct list_head *entry, *temp;
+ list_head_for_each_safe(entry, temp, &st->mapped_list) {
+ gttseg = entry->gttseg;
+ list_head_del(entry);
+ kmem_free(entry, sizeof (*entry));
+ if (ldi_ioctl(hdl, I8XX_ADD2GTT, (intptr_t)gttseg, FKIOCTL,
+ kcred, &rval)) {
+ AGPDB_PRINT2((CE_WARN, "agpgart_pages_rebind errori"));
+ err = -1;
+ break;
+ }
+ kmem_free(gttseg->igs_phyaddr, sizeof (uint32_t) *
+ gttseg->igs_npage);
+ kmem_free(gttseg, sizeof (igd_gtt_seg_t));
+
+ }
+ return (err);
+
+}
+
/*ARGSUSED*/
static int
agpgart_ioctl(dev_t dev, int cmd, intptr_t intarg, int flags,
@@ -3065,6 +3307,18 @@ agpgart_ioctl(dev_t dev, int cmd, intptr_t intarg, int flags,
case AGPIOC_UNBIND:
retval = ioctl_agpgart_unbind(softstate, arg, flags);
break;
+ case AGPIOC_FLUSHCHIPSET:
+ retval = ioctl_agpgart_flush_chipset(softstate);
+ break;
+ case AGPIOC_PAGES_BIND:
+ retval = ioctl_agpgart_pages_bind(softstate, arg, flags);
+ break;
+ case AGPIOC_PAGES_UNBIND:
+ retval = ioctl_agpgart_pages_unbind(softstate, arg, flags);
+ break;
+ case AGPIOC_PAGES_REBIND:
+ retval = ioctl_agpgart_pages_rebind(softstate);
+ break;
default:
AGPDB_PRINT2((CE_WARN, "agpgart_ioctl: wrong argument"));
retval = ENXIO;