diff options
Diffstat (limited to 'drivers/gpu/drm')
173 files changed, 12826 insertions, 3921 deletions
diff --git a/drivers/gpu/drm/ast/ast_ttm.c b/drivers/gpu/drm/ast/ast_ttm.c index aad12f74717..6cf2adea66b 100644 --- a/drivers/gpu/drm/ast/ast_ttm.c +++ b/drivers/gpu/drm/ast/ast_ttm.c @@ -357,7 +357,7 @@ int ast_bo_create(struct drm_device *dev, int size, int align, ret = ttm_bo_init(&ast->ttm.bdev, &astbo->bo, size, ttm_bo_type_device, &astbo->placement, align >> PAGE_SHIFT, 0, false, NULL, acc_size, - ast_bo_ttm_destroy); + NULL, ast_bo_ttm_destroy); if (ret) return ret; diff --git a/drivers/gpu/drm/cirrus/cirrus_ttm.c b/drivers/gpu/drm/cirrus/cirrus_ttm.c index ae2f08ff380..2ebcd11a502 100644 --- a/drivers/gpu/drm/cirrus/cirrus_ttm.c +++ b/drivers/gpu/drm/cirrus/cirrus_ttm.c @@ -357,7 +357,7 @@ int cirrus_bo_create(struct drm_device *dev, int size, int align, ret = ttm_bo_init(&cirrus->ttm.bdev, &cirrusbo->bo, size, ttm_bo_type_device, &cirrusbo->placement, align >> PAGE_SHIFT, 0, false, NULL, acc_size, - cirrus_bo_ttm_destroy); + NULL, cirrus_bo_ttm_destroy); if (ret) return ret; diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index a177d0abb8b..08a7aa722d6 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -363,7 +363,7 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup); * @funcs: callbacks for the new CRTC * * LOCKING: - * Caller must hold mode config lock. + * Takes mode_config lock. * * Inits a new object created as base part of an driver crtc object. * @@ -608,6 +608,7 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, if (ret) goto out; + plane->base.properties = &plane->properties; plane->dev = dev; plane->funcs = funcs; plane->format_types = kmalloc(sizeof(uint32_t) * format_count, @@ -1829,7 +1830,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, struct drm_display_mode *mode = NULL; struct drm_mode_set set; uint32_t __user *set_connectors_ptr; - int ret = 0; + int ret; int i; if (!drm_core_check_feature(dev, DRIVER_MODESET)) @@ -2115,7 +2116,7 @@ out: return ret; } -static int format_check(struct drm_mode_fb_cmd2 *r) +static int format_check(const struct drm_mode_fb_cmd2 *r) { uint32_t format = r->pixel_format & ~DRM_FORMAT_BIG_ENDIAN; @@ -2184,7 +2185,7 @@ static int format_check(struct drm_mode_fb_cmd2 *r) } } -static int framebuffer_check(struct drm_mode_fb_cmd2 *r) +static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) { int ret, hsub, vsub, num_planes, i; @@ -2248,7 +2249,7 @@ int drm_mode_addfb2(struct drm_device *dev, struct drm_mode_fb_cmd2 *r = data; struct drm_mode_config *config = &dev->mode_config; struct drm_framebuffer *fb; - int ret = 0; + int ret; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; @@ -2403,7 +2404,7 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, struct drm_framebuffer *fb; unsigned flags; int num_clips; - int ret = 0; + int ret; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; @@ -2602,7 +2603,7 @@ int drm_mode_attachmode_ioctl(struct drm_device *dev, struct drm_display_mode *mode; struct drm_mode_object *obj; struct drm_mode_modeinfo *umode = &mode_cmd->mode; - int ret = 0; + int ret; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; @@ -2656,7 +2657,7 @@ int drm_mode_detachmode_ioctl(struct drm_device *dev, struct drm_connector *connector; struct drm_display_mode mode; struct drm_mode_modeinfo *umode = &mode_cmd->mode; - int ret = 0; + int ret; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; @@ -2748,6 +2749,34 @@ struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, } EXPORT_SYMBOL(drm_property_create_enum); +struct drm_property *drm_property_create_bitmask(struct drm_device *dev, + int flags, const char *name, + const struct drm_prop_enum_list *props, + int num_values) +{ + struct drm_property *property; + int i, ret; + + flags |= DRM_MODE_PROP_BITMASK; + + property = drm_property_create(dev, flags, name, num_values); + if (!property) + return NULL; + + for (i = 0; i < num_values; i++) { + ret = drm_property_add_enum(property, i, + props[i].type, + props[i].name); + if (ret) { + drm_property_destroy(dev, property); + return NULL; + } + } + + return property; +} +EXPORT_SYMBOL(drm_property_create_bitmask); + struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, const char *name, uint64_t min, uint64_t max) @@ -2772,7 +2801,14 @@ int drm_property_add_enum(struct drm_property *property, int index, { struct drm_property_enum *prop_enum; - if (!(property->flags & DRM_MODE_PROP_ENUM)) + if (!(property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK))) + return -EINVAL; + + /* + * Bitmask enum properties have the additional constraint of values + * from 0 to 63 + */ + if ((property->flags & DRM_MODE_PROP_BITMASK) && (value > 63)) return -EINVAL; if (!list_empty(&property->enum_blob_list)) { @@ -2918,7 +2954,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, } property = obj_to_property(obj); - if (property->flags & DRM_MODE_PROP_ENUM) { + if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) { list_for_each_entry(prop_enum, &property->enum_blob_list, head) enum_count++; } else if (property->flags & DRM_MODE_PROP_BLOB) { @@ -2943,7 +2979,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, } out_resp->count_values = value_count; - if (property->flags & DRM_MODE_PROP_ENUM) { + if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) { if ((out_resp->count_enum_blobs >= enum_count) && enum_count) { copied = 0; enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr; @@ -3065,7 +3101,7 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector, struct edid *edid) { struct drm_device *dev = connector->dev; - int ret = 0, size; + int ret, size; if (connector->edid_blob_ptr) drm_property_destroy_blob(dev, connector->edid_blob_ptr); @@ -3090,7 +3126,7 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector, EXPORT_SYMBOL(drm_mode_connector_update_edid_property); static bool drm_property_change_is_valid(struct drm_property *property, - __u64 value) + uint64_t value) { if (property->flags & DRM_MODE_PROP_IMMUTABLE) return false; @@ -3098,6 +3134,12 @@ static bool drm_property_change_is_valid(struct drm_property *property, if (value < property->values[0] || value > property->values[1]) return false; return true; + } else if (property->flags & DRM_MODE_PROP_BITMASK) { + int i; + uint64_t valid_mask = 0; + for (i = 0; i < property->num_values; i++) + valid_mask |= (1ULL << property->values[i]); + return !(value & ~valid_mask); } else { int i; for (i = 0; i < property->num_values; i++) @@ -3158,6 +3200,21 @@ static int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj, return ret; } +static int drm_mode_plane_set_obj_prop(struct drm_mode_object *obj, + struct drm_property *property, + uint64_t value) +{ + int ret = -EINVAL; + struct drm_plane *plane = obj_to_plane(obj); + + if (plane->funcs->set_property) + ret = plane->funcs->set_property(plane, property, value); + if (!ret) + drm_object_property_set_value(obj, property, value); + + return ret; +} + int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -3259,6 +3316,9 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, case DRM_MODE_OBJECT_CRTC: ret = drm_mode_crtc_set_obj_prop(arg_obj, property, arg->value); break; + case DRM_MODE_OBJECT_PLANE: + ret = drm_mode_plane_set_obj_prop(arg_obj, property, arg->value); + break; } out: @@ -3332,6 +3392,11 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev, } crtc = obj_to_crtc(obj); + if (crtc->funcs->gamma_set == NULL) { + ret = -ENOSYS; + goto out; + } + /* memcpy into gamma store */ if (crtc_lut->gamma_size != crtc->gamma_size) { ret = -EINVAL; diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 974196ab7b2..3252e7067d8 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -518,7 +518,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) int count = 0, ro, fail = 0; struct drm_crtc_helper_funcs *crtc_funcs; struct drm_mode_set save_set; - int ret = 0; + int ret; int i; DRM_DEBUG_KMS("\n"); diff --git a/drivers/gpu/drm/drm_edid_load.c b/drivers/gpu/drm/drm_edid_load.c index 48c927c3704..66d4a28ad5a 100644 --- a/drivers/gpu/drm/drm_edid_load.c +++ b/drivers/gpu/drm/drm_edid_load.c @@ -220,18 +220,18 @@ int drm_load_edid_firmware(struct drm_connector *connector) { char *connector_name = drm_get_connector_name(connector); char *edidname = edid_firmware, *last, *colon; - int ret = 0; + int ret; if (*edidname == '\0') - return ret; + return 0; colon = strchr(edidname, ':'); if (colon != NULL) { if (strncmp(connector_name, edidname, colon - edidname)) - return ret; + return 0; edidname = colon + 1; if (*edidname == '\0') - return ret; + return 0; } last = edidname + strlen(edidname) - 1; diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 6e19dd156be..5683b7fdd74 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -136,6 +136,9 @@ static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc) { uint16_t *r_base, *g_base, *b_base; + if (crtc->funcs->gamma_set == NULL) + return; + r_base = crtc->gamma_store; g_base = r_base + crtc->gamma_size; b_base = g_base + crtc->gamma_size; @@ -383,7 +386,6 @@ int drm_fb_helper_init(struct drm_device *dev, int crtc_count, int max_conn_count) { struct drm_crtc *crtc; - int ret = 0; int i; fb_helper->dev = dev; @@ -408,10 +410,8 @@ int drm_fb_helper_init(struct drm_device *dev, sizeof(struct drm_connector *), GFP_KERNEL); - if (!fb_helper->crtc_info[i].mode_set.connectors) { - ret = -ENOMEM; + if (!fb_helper->crtc_info[i].mode_set.connectors) goto out_free; - } fb_helper->crtc_info[i].mode_set.num_connectors = 0; } diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 1ab29a7345c..d58e69da1fb 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -201,6 +201,19 @@ free: } EXPORT_SYMBOL(drm_gem_object_alloc); +static void +drm_gem_remove_prime_handles(struct drm_gem_object *obj, struct drm_file *filp) +{ + if (obj->import_attach) { + drm_prime_remove_imported_buf_handle(&filp->prime, + obj->import_attach->dmabuf); + } + if (obj->export_dma_buf) { + drm_prime_remove_imported_buf_handle(&filp->prime, + obj->export_dma_buf); + } +} + /** * Removes the mapping from handle to filp for this object. */ @@ -233,9 +246,7 @@ drm_gem_handle_delete(struct drm_file *filp, u32 handle) idr_remove(&filp->object_idr, handle); spin_unlock(&filp->table_lock); - if (obj->import_attach) - drm_prime_remove_imported_buf_handle(&filp->prime, - obj->import_attach->dmabuf); + drm_gem_remove_prime_handles(obj, filp); if (dev->driver->gem_close_object) dev->driver->gem_close_object(obj, filp); @@ -328,7 +339,7 @@ drm_gem_create_mmap_offset(struct drm_gem_object *obj) struct drm_gem_mm *mm = dev->mm_private; struct drm_map_list *list; struct drm_local_map *map; - int ret = 0; + int ret; /* Set the object up for mmap'ing */ list = &obj->map_list; @@ -530,9 +541,7 @@ drm_gem_object_release_handle(int id, void *ptr, void *data) struct drm_gem_object *obj = ptr; struct drm_device *dev = obj->dev; - if (obj->import_attach) - drm_prime_remove_imported_buf_handle(&file_priv->prime, - obj->import_attach->dmabuf); + drm_gem_remove_prime_handles(obj, file_priv); if (dev->driver->gem_close_object) dev->driver->gem_close_object(obj, file_priv); diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index c869436e238..c798eeae0a0 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -189,7 +189,7 @@ void drm_vblank_cleanup(struct drm_device *dev) if (dev->num_crtcs == 0) return; - del_timer(&dev->vblank_disable_timer); + del_timer_sync(&dev->vblank_disable_timer); vblank_disable_fn((unsigned long)dev); @@ -310,7 +310,7 @@ static void drm_irq_vgaarb_nokms(void *cookie, bool state) */ int drm_irq_install(struct drm_device *dev) { - int ret = 0; + int ret; unsigned long sh_flags = 0; char *irqname; @@ -731,7 +731,7 @@ EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos); u32 drm_get_last_vbltimestamp(struct drm_device *dev, int crtc, struct timeval *tvblank, unsigned flags) { - int ret = 0; + int ret; /* Define requested maximum error on timestamps (nanoseconds). */ int max_error = (int) drm_timestamp_precision * 1000; @@ -1031,18 +1031,15 @@ int drm_modeset_ctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_modeset_ctl *modeset = data; - int ret = 0; unsigned int crtc; /* If drm_vblank_init() hasn't been called yet, just no-op */ if (!dev->num_crtcs) - goto out; + return 0; crtc = modeset->crtc; - if (crtc >= dev->num_crtcs) { - ret = -EINVAL; - goto out; - } + if (crtc >= dev->num_crtcs) + return -EINVAL; switch (modeset->cmd) { case _DRM_PRE_MODESET: @@ -1052,12 +1049,10 @@ int drm_modeset_ctl(struct drm_device *dev, void *data, drm_vblank_post_modeset(dev, crtc); break; default: - ret = -EINVAL; - break; + return -EINVAL; } -out: - return ret; + return 0; } static int drm_queue_vblank_event(struct drm_device *dev, int pipe, @@ -1154,7 +1149,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_priv) { union drm_wait_vblank *vblwait = data; - int ret = 0; + int ret; unsigned int flags, seq, crtc, high_crtc; if ((!drm_dev_to_irq(dev)) || (!dev->irq_enabled)) diff --git a/drivers/gpu/drm/drm_lock.c b/drivers/gpu/drm/drm_lock.c index c79c713eeba..52115204169 100644 --- a/drivers/gpu/drm/drm_lock.c +++ b/drivers/gpu/drm/drm_lock.c @@ -331,7 +331,7 @@ static int drm_notifier(void *priv) void drm_idlelock_take(struct drm_lock_data *lock_data) { - int ret = 0; + int ret; spin_lock_bh(&lock_data->spinlock); lock_data->kernel_waiters++; diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 1bdf2b54eaf..f546ff98a11 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -68,6 +68,7 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev, { struct drm_gem_object *obj; void *buf; + int ret; obj = drm_gem_object_lookup(dev, file_priv, handle); if (!obj) @@ -100,6 +101,17 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev, obj->export_dma_buf = buf; *prime_fd = dma_buf_fd(buf, flags); } + /* if we've exported this buffer the cheat and add it to the import list + * so we get the correct handle back + */ + ret = drm_prime_add_imported_buf_handle(&file_priv->prime, + obj->export_dma_buf, handle); + if (ret) { + drm_gem_object_unreference_unlocked(obj); + mutex_unlock(&file_priv->prime.lock); + return ret; + } + mutex_unlock(&file_priv->prime.lock); return 0; } @@ -227,6 +239,42 @@ out: } EXPORT_SYMBOL(drm_prime_pages_to_sg); +/* export an sg table into an array of pages and addresses + this is currently required by the TTM driver in order to do correct fault + handling */ +int drm_prime_sg_to_page_addr_arrays(struct sg_table *sgt, struct page **pages, + dma_addr_t *addrs, int max_pages) +{ + unsigned count; + struct scatterlist *sg; + struct page *page; + u32 len, offset; + int pg_index; + dma_addr_t addr; + + pg_index = 0; + for_each_sg(sgt->sgl, sg, sgt->nents, count) { + len = sg->length; + offset = sg->offset; + page = sg_page(sg); + addr = sg_dma_address(sg); + + while (len > 0) { + if (WARN_ON(pg_index >= max_pages)) + return -1; + pages[pg_index] = page; + if (addrs) + addrs[pg_index] = addr; + + page++; + addr += PAGE_SIZE; + len -= PAGE_SIZE; + pg_index++; + } + } + return 0; +} +EXPORT_SYMBOL(drm_prime_sg_to_page_addr_arrays); /* helper function to cleanup a GEM/prime object */ void drm_prime_gem_destroy(struct drm_gem_object *obj, struct sg_table *sg) { diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index ae1ccf1d5d9..21bcd4a555d 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -210,7 +210,7 @@ EXPORT_SYMBOL(drm_master_put); int drm_setmaster_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { - int ret = 0; + int ret; if (file_priv->is_master) return 0; diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index 5a7bd51fc3d..45cf1dd3eb9 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -347,17 +347,17 @@ static struct bin_attribute edid_attr = { }; /** - * drm_sysfs_connector_add - add an connector to sysfs + * drm_sysfs_connector_add - add a connector to sysfs * @connector: connector to add * - * Create an connector device in sysfs, along with its associated connector + * Create a connector device in sysfs, along with its associated connector * properties (so far, connection status, dpms, mode list & edid) and * generate a hotplug event so userspace knows there's a new connector * available. * * Note: - * This routine should only be called *once* for each DRM minor registered. - * A second call for an already registered device will trigger the BUG_ON + * This routine should only be called *once* for each registered connector. + * A second call for an already registered connector will trigger the BUG_ON * below. */ int drm_sysfs_connector_add(struct drm_connector *connector) @@ -366,7 +366,7 @@ int drm_sysfs_connector_add(struct drm_connector *connector) int attr_cnt = 0; int opt_cnt = 0; int i; - int ret = 0; + int ret; /* We shouldn't get called more than once for the same connector */ BUG_ON(device_is_registered(&connector->kdev)); diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 3343ac437fe..7f5096763b7 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -10,6 +10,12 @@ config DRM_EXYNOS Choose this option if you have a Samsung SoC EXYNOS chipset. If M is selected the module will be called exynosdrm. +config DRM_EXYNOS_DMABUF + bool "EXYNOS DRM DMABUF" + depends on DRM_EXYNOS + help + Choose this option if you want to use DMABUF feature for DRM. + config DRM_EXYNOS_FIMD bool "Exynos DRM FIMD" depends on DRM_EXYNOS && !FB_S3C @@ -27,3 +33,9 @@ config DRM_EXYNOS_VIDI depends on DRM_EXYNOS help Choose this option if you want to use Exynos VIDI for DRM. + +config DRM_EXYNOS_G2D + bool "Exynos DRM G2D" + depends on DRM_EXYNOS + help + Choose this option if you want to use Exynos G2D for DRM. diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile index 9e0bff8badf..eb651ca8e2a 100644 --- a/drivers/gpu/drm/exynos/Makefile +++ b/drivers/gpu/drm/exynos/Makefile @@ -8,10 +8,12 @@ exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o exynos_drm_connector.o \ exynos_drm_buf.o exynos_drm_gem.o exynos_drm_core.o \ exynos_drm_plane.o +exynosdrm-$(CONFIG_DRM_EXYNOS_DMABUF) += exynos_drm_dmabuf.o exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o \ exynos_ddc.o exynos_hdmiphy.o \ exynos_drm_hdmi.o exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o +exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.c b/drivers/gpu/drm/exynos/exynos_drm_buf.c index de8d2090bce..b3cb0a69fbf 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_buf.c +++ b/drivers/gpu/drm/exynos/exynos_drm_buf.c @@ -35,7 +35,7 @@ static int lowlevel_buffer_allocate(struct drm_device *dev, unsigned int flags, struct exynos_drm_gem_buf *buf) { dma_addr_t start_addr; - unsigned int npages, page_size, i = 0; + unsigned int npages, i = 0; struct scatterlist *sgl; int ret = 0; @@ -53,13 +53,13 @@ static int lowlevel_buffer_allocate(struct drm_device *dev, if (buf->size >= SZ_1M) { npages = buf->size >> SECTION_SHIFT; - page_size = SECTION_SIZE; + buf->page_size = SECTION_SIZE; } else if (buf->size >= SZ_64K) { npages = buf->size >> 16; - page_size = SZ_64K; + buf->page_size = SZ_64K; } else { npages = buf->size >> PAGE_SHIFT; - page_size = PAGE_SIZE; + buf->page_size = PAGE_SIZE; } buf->sgt = kzalloc(sizeof(struct sg_table), GFP_KERNEL); @@ -96,9 +96,9 @@ static int lowlevel_buffer_allocate(struct drm_device *dev, while (i < npages) { buf->pages[i] = phys_to_page(start_addr); - sg_set_page(sgl, buf->pages[i], page_size, 0); + sg_set_page(sgl, buf->pages[i], buf->page_size, 0); sg_dma_address(sgl) = start_addr; - start_addr += page_size; + start_addr += buf->page_size; sgl = sg_next(sgl); i++; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 3486ffed0bf..4afb625128d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -105,6 +105,8 @@ int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay, overlay->fb_y = pos->fb_y; overlay->fb_width = fb->width; overlay->fb_height = fb->height; + overlay->src_width = pos->src_w; + overlay->src_height = pos->src_h; overlay->bpp = fb->bits_per_pixel; overlay->pitch = fb->pitches[0]; overlay->pixel_format = fb->pixel_format; @@ -153,6 +155,8 @@ static int exynos_drm_crtc_update(struct drm_crtc *crtc) pos.crtc_y = 0; pos.crtc_w = fb->width - crtc->x; pos.crtc_h = fb->height - crtc->y; + pos.src_w = pos.crtc_w; + pos.src_h = pos.crtc_h; return exynos_drm_overlay_update(overlay, crtc->fb, mode, &pos); } diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/drivers/gpu/drm/exynos/exynos_drm_crtc.h index 25f72a62cb8..16b8e2195a0 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.h +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.h @@ -42,6 +42,8 @@ void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc); * - the unit is screen coordinates. * @fb_y: offset y on a framebuffer to be displayed * - the unit is screen coordinates. + * @src_w: width of source area to be displayed from a framebuffer. + * @src_h: height of source area to be displayed from a framebuffer. * @crtc_x: offset x on hardware screen. * @crtc_y: offset y on hardware screen. * @crtc_w: width of hardware screen. @@ -50,6 +52,8 @@ void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc); struct exynos_drm_crtc_pos { unsigned int fb_x; unsigned int fb_y; + unsigned int src_w; + unsigned int src_h; unsigned int crtc_x; unsigned int crtc_y; unsigned int crtc_w; diff --git a/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c b/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c new file mode 100644 index 00000000000..274909271c3 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c @@ -0,0 +1,272 @@ +/* exynos_drm_dmabuf.c + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * Author: Inki Dae <inki.dae@samsung.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 + * 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 "drmP.h" +#include "drm.h" +#include "exynos_drm_drv.h" +#include "exynos_drm_gem.h" + +#include <linux/dma-buf.h> + +static struct sg_table *exynos_pages_to_sg(struct page **pages, int nr_pages, + unsigned int page_size) +{ + struct sg_table *sgt = NULL; + struct scatterlist *sgl; + int i, ret; + + sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); + if (!sgt) + goto out; + + ret = sg_alloc_table(sgt, nr_pages, GFP_KERNEL); + if (ret) + goto err_free_sgt; + + if (page_size < PAGE_SIZE) + page_size = PAGE_SIZE; + + for_each_sg(sgt->sgl, sgl, nr_pages, i) + sg_set_page(sgl, pages[i], page_size, 0); + + return sgt; + +err_free_sgt: + kfree(sgt); + sgt = NULL; +out: + return NULL; +} + +static struct sg_table * + exynos_gem_map_dma_buf(struct dma_buf_attachment *attach, + enum dma_data_direction dir) +{ + struct exynos_drm_gem_obj *gem_obj = attach->dmabuf->priv; + struct drm_device *dev = gem_obj->base.dev; + struct exynos_drm_gem_buf *buf; + struct sg_table *sgt = NULL; + unsigned int npages; + int nents; + + DRM_DEBUG_PRIME("%s\n", __FILE__); + + mutex_lock(&dev->struct_mutex); + + buf = gem_obj->buffer; + + /* there should always be pages allocated. */ + if (!buf->pages) { + DRM_ERROR("pages is null.\n"); + goto err_unlock; + } + + npages = buf->size / buf->page_size; + + sgt = exynos_pages_to_sg(buf->pages, npages, buf->page_size); + nents = dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir); + + DRM_DEBUG_PRIME("npages = %d buffer size = 0x%lx page_size = 0x%lx\n", + npages, buf->size, buf->page_size); + +err_unlock: + mutex_unlock(&dev->struct_mutex); + return sgt; +} + +static void exynos_gem_unmap_dma_buf(struct dma_buf_attachment *attach, + struct sg_table *sgt, + enum dma_data_direction dir) +{ + dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, dir); + sg_free_table(sgt); + kfree(sgt); + sgt = NULL; +} + +static void exynos_dmabuf_release(struct dma_buf *dmabuf) +{ + struct exynos_drm_gem_obj *exynos_gem_obj = dmabuf->priv; + + DRM_DEBUG_PRIME("%s\n", __FILE__); + + /* + * exynos_dmabuf_release() call means that file object's + * f_count is 0 and it calls drm_gem_object_handle_unreference() + * to drop the references that these values had been increased + * at drm_prime_handle_to_fd() + */ + if (exynos_gem_obj->base.export_dma_buf == dmabuf) { + exynos_gem_obj->base.export_dma_buf = NULL; + + /* + * drop this gem object refcount to release allocated buffer + * and resources. + */ + drm_gem_object_unreference_unlocked(&exynos_gem_obj->base); + } +} + +static void *exynos_gem_dmabuf_kmap_atomic(struct dma_buf *dma_buf, + unsigned long page_num) +{ + /* TODO */ + + return NULL; +} + +static void exynos_gem_dmabuf_kunmap_atomic(struct dma_buf *dma_buf, + unsigned long page_num, + void *addr) +{ + /* TODO */ +} + +static void *exynos_gem_dmabuf_kmap(struct dma_buf *dma_buf, + unsigned long page_num) +{ + /* TODO */ + + return NULL; +} + +static void exynos_gem_dmabuf_kunmap(struct dma_buf *dma_buf, + unsigned long page_num, void *addr) +{ + /* TODO */ +} + +static struct dma_buf_ops exynos_dmabuf_ops = { + .map_dma_buf = exynos_gem_map_dma_buf, + .unmap_dma_buf = exynos_gem_unmap_dma_buf, + .kmap = exynos_gem_dmabuf_kmap, + .kmap_atomic = exynos_gem_dmabuf_kmap_atomic, + .kunmap = exynos_gem_dmabuf_kunmap, + .kunmap_atomic = exynos_gem_dmabuf_kunmap_atomic, + .release = exynos_dmabuf_release, +}; + +struct dma_buf *exynos_dmabuf_prime_export(struct drm_device *drm_dev, + struct drm_gem_object *obj, int flags) +{ + struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); + + return dma_buf_export(exynos_gem_obj, &exynos_dmabuf_ops, + exynos_gem_obj->base.size, 0600); +} + +struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev, + struct dma_buf *dma_buf) +{ + struct dma_buf_attachment *attach; + struct sg_table *sgt; + struct scatterlist *sgl; + struct exynos_drm_gem_obj *exynos_gem_obj; + struct exynos_drm_gem_buf *buffer; + struct page *page; + int ret, i = 0; + + DRM_DEBUG_PRIME("%s\n", __FILE__); + + /* is this one of own objects? */ + if (dma_buf->ops == &exynos_dmabuf_ops) { + struct drm_gem_object *obj; + + exynos_gem_obj = dma_buf->priv; + obj = &exynos_gem_obj->base; + + /* is it from our device? */ + if (obj->dev == drm_dev) { + drm_gem_object_reference(obj); + return obj; + } + } + + attach = dma_buf_attach(dma_buf, drm_dev->dev); + if (IS_ERR(attach)) + return ERR_PTR(-EINVAL); + + + sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); + if (IS_ERR(sgt)) { + ret = PTR_ERR(sgt); + goto err_buf_detach; + } + + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + if (!buffer) { + DRM_ERROR("failed to allocate exynos_drm_gem_buf.\n"); + ret = -ENOMEM; + goto err_unmap_attach; + } + + buffer->pages = kzalloc(sizeof(*page) * sgt->nents, GFP_KERNEL); + if (!buffer->pages) { + DRM_ERROR("failed to allocate pages.\n"); + ret = -ENOMEM; + goto err_free_buffer; + } + + exynos_gem_obj = exynos_drm_gem_init(drm_dev, dma_buf->size); + if (!exynos_gem_obj) { + ret = -ENOMEM; + goto err_free_pages; + } + + sgl = sgt->sgl; + buffer->dma_addr = sg_dma_address(sgl); + + while (i < sgt->nents) { + buffer->pages[i] = sg_page(sgl); + buffer->size += sg_dma_len(sgl); + sgl = sg_next(sgl); + i++; + } + + exynos_gem_obj->buffer = buffer; + buffer->sgt = sgt; + exynos_gem_obj->base.import_attach = attach; + + DRM_DEBUG_PRIME("dma_addr = 0x%x, size = 0x%lx\n", buffer->dma_addr, + buffer->size); + + return &exynos_gem_obj->base; + +err_free_pages: + kfree(buffer->pages); + buffer->pages = NULL; +err_free_buffer: + kfree(buffer); + buffer = NULL; +err_unmap_attach: + dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL); +err_buf_detach: + dma_buf_detach(dma_buf, attach); + return ERR_PTR(ret); +} + +MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); +MODULE_DESCRIPTION("Samsung SoC DRM DMABUF Module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_dmabuf.h b/drivers/gpu/drm/exynos/exynos_drm_dmabuf.h new file mode 100644 index 00000000000..662a8f98ccd --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_dmabuf.h @@ -0,0 +1,39 @@ +/* exynos_drm_dmabuf.h + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * Author: Inki Dae <inki.dae@samsung.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 + * 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 _EXYNOS_DRM_DMABUF_H_ +#define _EXYNOS_DRM_DMABUF_H_ + +#ifdef CONFIG_DRM_EXYNOS_DMABUF +struct dma_buf *exynos_dmabuf_prime_export(struct drm_device *drm_dev, + struct drm_gem_object *obj, int flags); + +struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev, + struct dma_buf *dma_buf); +#else +#define exynos_dmabuf_prime_export NULL +#define exynos_dmabuf_prime_import NULL +#endif +#endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index a6819b5f842..420953197d0 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -39,6 +39,8 @@ #include "exynos_drm_gem.h" #include "exynos_drm_plane.h" #include "exynos_drm_vidi.h" +#include "exynos_drm_dmabuf.h" +#include "exynos_drm_g2d.h" #define DRIVER_NAME "exynos" #define DRIVER_DESC "Samsung SoC DRM" @@ -147,8 +149,17 @@ static int exynos_drm_unload(struct drm_device *dev) static int exynos_drm_open(struct drm_device *dev, struct drm_file *file) { + struct drm_exynos_file_private *file_priv; + DRM_DEBUG_DRIVER("%s\n", __FILE__); + file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL); + if (!file_priv) + return -ENOMEM; + + drm_prime_init_file_private(&file->prime); + file->driver_priv = file_priv; + return exynos_drm_subdrv_open(dev, file); } @@ -170,6 +181,7 @@ static void exynos_drm_preclose(struct drm_device *dev, e->base.destroy(&e->base); } } + drm_prime_destroy_file_private(&file->prime); spin_unlock_irqrestore(&dev->event_lock, flags); exynos_drm_subdrv_close(dev, file); @@ -193,7 +205,7 @@ static void exynos_drm_lastclose(struct drm_device *dev) exynos_drm_fbdev_restore_mode(dev); } -static struct vm_operations_struct exynos_drm_gem_vm_ops = { +static const struct vm_operations_struct exynos_drm_gem_vm_ops = { .fault = exynos_drm_gem_fault, .open = drm_gem_vm_open, .close = drm_gem_vm_close, @@ -207,10 +219,18 @@ static struct drm_ioctl_desc exynos_ioctls[] = { DRM_AUTH), DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MMAP, exynos_drm_gem_mmap_ioctl, DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(EXYNOS_GEM_GET, + exynos_drm_gem_get_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(EXYNOS_PLANE_SET_ZPOS, exynos_plane_set_zpos_ioctl, DRM_UNLOCKED | DRM_AUTH), DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION, vidi_connection_ioctl, DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(EXYNOS_G2D_GET_VER, + exynos_g2d_get_ver_ioctl, DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(EXYNOS_G2D_SET_CMDLIST, + exynos_g2d_set_cmdlist_ioctl, DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC, + exynos_g2d_exec_ioctl, DRM_UNLOCKED | DRM_AUTH), }; static const struct file_operations exynos_drm_driver_fops = { @@ -225,7 +245,7 @@ static const struct file_operations exynos_drm_driver_fops = { static struct drm_driver exynos_drm_driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_BUS_PLATFORM | - DRIVER_MODESET | DRIVER_GEM, + DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, .load = exynos_drm_load, .unload = exynos_drm_unload, .open = exynos_drm_open, @@ -241,6 +261,10 @@ static struct drm_driver exynos_drm_driver = { .dumb_create = exynos_drm_gem_dumb_create, .dumb_map_offset = exynos_drm_gem_dumb_map_offset, .dumb_destroy = exynos_drm_gem_dumb_destroy, + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_export = exynos_dmabuf_prime_export, + .gem_prime_import = exynos_dmabuf_prime_import, .ioctls = exynos_ioctls, .fops = &exynos_drm_driver_fops, .name = DRIVER_NAME, @@ -307,6 +331,12 @@ static int __init exynos_drm_init(void) goto out_vidi; #endif +#ifdef CONFIG_DRM_EXYNOS_G2D + ret = platform_driver_register(&g2d_driver); + if (ret < 0) + goto out_g2d; +#endif + ret = platform_driver_register(&exynos_drm_platform_driver); if (ret < 0) goto out; @@ -314,6 +344,11 @@ static int __init exynos_drm_init(void) return 0; out: +#ifdef CONFIG_DRM_EXYNOS_G2D + platform_driver_unregister(&g2d_driver); +out_g2d: +#endif + #ifdef CONFIG_DRM_EXYNOS_VIDI out_vidi: platform_driver_unregister(&vidi_driver); @@ -341,6 +376,10 @@ static void __exit exynos_drm_exit(void) platform_driver_unregister(&exynos_drm_platform_driver); +#ifdef CONFIG_DRM_EXYNOS_G2D + platform_driver_unregister(&g2d_driver); +#endif + #ifdef CONFIG_DRM_EXYNOS_HDMI platform_driver_unregister(&exynos_drm_common_hdmi_driver); platform_driver_unregister(&mixer_driver); diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 1d814175cd4..c82c90c443e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -77,6 +77,8 @@ struct exynos_drm_overlay_ops { * - the unit is screen coordinates. * @fb_width: width of a framebuffer. * @fb_height: height of a framebuffer. + * @src_width: width of a partial image to be displayed from framebuffer. + * @src_height: height of a partial image to be displayed from framebuffer. * @crtc_x: offset x on hardware screen. * @crtc_y: offset y on hardware screen. * @crtc_width: window width to be displayed (hardware screen). @@ -108,6 +110,8 @@ struct exynos_drm_overlay { unsigned int fb_y; unsigned int fb_width; unsigned int fb_height; + unsigned int src_width; + unsigned int src_height; unsigned int crtc_x; unsigned int crtc_y; unsigned int crtc_width; @@ -205,6 +209,18 @@ struct exynos_drm_manager { struct exynos_drm_display_ops *display_ops; }; +struct exynos_drm_g2d_private { + struct device *dev; + struct list_head inuse_cmdlist; + struct list_head event_list; + struct list_head gem_list; + unsigned int gem_nr; +}; + +struct drm_exynos_file_private { + struct exynos_drm_g2d_private *g2d_priv; +}; + /* * Exynos drm private structure. */ @@ -287,4 +303,5 @@ extern struct platform_driver hdmi_driver; extern struct platform_driver mixer_driver; extern struct platform_driver exynos_drm_common_hdmi_driver; extern struct platform_driver vidi_driver; +extern struct platform_driver g2d_driver; #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index c38c8f468fa..f82a299553f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c @@ -191,7 +191,7 @@ static void exynos_drm_output_poll_changed(struct drm_device *dev) drm_fb_helper_hotplug_event(fb_helper); } -static struct drm_mode_config_funcs exynos_drm_mode_config_funcs = { +static const struct drm_mode_config_funcs exynos_drm_mode_config_funcs = { .fb_create = exynos_user_fb_create, .output_poll_changed = exynos_drm_output_poll_changed, }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c new file mode 100644 index 00000000000..d2d88f22a03 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -0,0 +1,937 @@ +/* + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * Authors: Joonyoung Shim <jy0922.shim@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundationr + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/workqueue.h> + +#include "drmP.h" +#include "exynos_drm.h" +#include "exynos_drm_drv.h" +#include "exynos_drm_gem.h" + +#define G2D_HW_MAJOR_VER 4 +#define G2D_HW_MINOR_VER 1 + +/* vaild register range set from user: 0x0104 ~ 0x0880 */ +#define G2D_VALID_START 0x0104 +#define G2D_VALID_END 0x0880 + +/* general registers */ +#define G2D_SOFT_RESET 0x0000 +#define G2D_INTEN 0x0004 +#define G2D_INTC_PEND 0x000C +#define G2D_DMA_SFR_BASE_ADDR 0x0080 +#define G2D_DMA_COMMAND 0x0084 +#define G2D_DMA_STATUS 0x008C +#define G2D_DMA_HOLD_CMD 0x0090 + +/* command registers */ +#define G2D_BITBLT_START 0x0100 + +/* registers for base address */ +#define G2D_SRC_BASE_ADDR 0x0304 +#define G2D_SRC_PLANE2_BASE_ADDR 0x0318 +#define G2D_DST_BASE_ADDR 0x0404 +#define G2D_DST_PLANE2_BASE_ADDR 0x0418 +#define G2D_PAT_BASE_ADDR 0x0500 +#define G2D_MSK_BASE_ADDR 0x0520 + +/* G2D_SOFT_RESET */ +#define G2D_SFRCLEAR (1 << 1) +#define G2D_R (1 << 0) + +/* G2D_INTEN */ +#define G2D_INTEN_ACF (1 << 3) +#define G2D_INTEN_UCF (1 << 2) +#define G2D_INTEN_GCF (1 << 1) +#define G2D_INTEN_SCF (1 << 0) + +/* G2D_INTC_PEND */ +#define G2D_INTP_ACMD_FIN (1 << 3) +#define G2D_INTP_UCMD_FIN (1 << 2) +#define G2D_INTP_GCMD_FIN (1 << 1) +#define G2D_INTP_SCMD_FIN (1 << 0) + +/* G2D_DMA_COMMAND */ +#define G2D_DMA_HALT (1 << 2) +#define G2D_DMA_CONTINUE (1 << 1) +#define G2D_DMA_START (1 << 0) + +/* G2D_DMA_STATUS */ +#define G2D_DMA_LIST_DONE_COUNT (0xFF << 17) +#define G2D_DMA_BITBLT_DONE_COUNT (0xFFFF << 1) +#define G2D_DMA_DONE (1 << 0) +#define G2D_DMA_LIST_DONE_COUNT_OFFSET 17 + +/* G2D_DMA_HOLD_CMD */ +#define G2D_USET_HOLD (1 << 2) +#define G2D_LIST_HOLD (1 << 1) +#define G2D_BITBLT_HOLD (1 << 0) + +/* G2D_BITBLT_START */ +#define G2D_START_CASESEL (1 << 2) +#define G2D_START_NHOLT (1 << 1) +#define G2D_START_BITBLT (1 << 0) + +#define G2D_CMDLIST_SIZE (PAGE_SIZE / 4) +#define G2D_CMDLIST_NUM 64 +#define G2D_CMDLIST_POOL_SIZE (G2D_CMDLIST_SIZE * G2D_CMDLIST_NUM) +#define G2D_CMDLIST_DATA_NUM (G2D_CMDLIST_SIZE / sizeof(u32) - 2) + +/* cmdlist data structure */ +struct g2d_cmdlist { + u32 head; + u32 data[G2D_CMDLIST_DATA_NUM]; + u32 last; /* last data offset */ +}; + +struct drm_exynos_pending_g2d_event { + struct drm_pending_event base; + struct drm_exynos_g2d_event event; +}; + +struct g2d_gem_node { + struct list_head list; + unsigned int handle; +}; + +struct g2d_cmdlist_node { + struct list_head list; + struct g2d_cmdlist *cmdlist; + unsigned int gem_nr; + dma_addr_t dma_addr; + + struct drm_exynos_pending_g2d_event *event; +}; + +struct g2d_runqueue_node { + struct list_head list; + struct list_head run_cmdlist; + struct list_head event_list; + struct completion complete; + int async; +}; + +struct g2d_data { + struct device *dev; + struct clk *gate_clk; + struct resource *regs_res; + void __iomem *regs; + int irq; + struct workqueue_struct *g2d_workq; + struct work_struct runqueue_work; + struct exynos_drm_subdrv subdrv; + bool suspended; + + /* cmdlist */ + struct g2d_cmdlist_node *cmdlist_node; + struct list_head free_cmdlist; + struct mutex cmdlist_mutex; + dma_addr_t cmdlist_pool; + void *cmdlist_pool_virt; + + /* runqueue*/ + struct g2d_runqueue_node *runqueue_node; + struct list_head runqueue; + struct mutex runqueue_mutex; + struct kmem_cache *runqueue_slab; +}; + +static int g2d_init_cmdlist(struct g2d_data *g2d) +{ + struct device *dev = g2d->dev; + struct g2d_cmdlist_node *node = g2d->cmdlist_node; + int nr; + int ret; + + g2d->cmdlist_pool_virt = dma_alloc_coherent(dev, G2D_CMDLIST_POOL_SIZE, + &g2d->cmdlist_pool, GFP_KERNEL); + if (!g2d->cmdlist_pool_virt) { + dev_err(dev, "failed to allocate dma memory\n"); + return -ENOMEM; + } + + node = kcalloc(G2D_CMDLIST_NUM, G2D_CMDLIST_NUM * sizeof(*node), + GFP_KERNEL); + if (!node) { + dev_err(dev, "failed to allocate memory\n"); + ret = -ENOMEM; + goto err; + } + + for (nr = 0; nr < G2D_CMDLIST_NUM; nr++) { + node[nr].cmdlist = + g2d->cmdlist_pool_virt + nr * G2D_CMDLIST_SIZE; + node[nr].dma_addr = + g2d->cmdlist_pool + nr * G2D_CMDLIST_SIZE; + + list_add_tail(&node[nr].list, &g2d->free_cmdlist); + } + + return 0; + +err: + dma_free_coherent(dev, G2D_CMDLIST_POOL_SIZE, g2d->cmdlist_pool_virt, + g2d->cmdlist_pool); + return ret; +} + +static void g2d_fini_cmdlist(struct g2d_data *g2d) +{ + struct device *dev = g2d->dev; + + kfree(g2d->cmdlist_node); + dma_free_coherent(dev, G2D_CMDLIST_POOL_SIZE, g2d->cmdlist_pool_virt, + g2d->cmdlist_pool); +} + +static struct g2d_cmdlist_node *g2d_get_cmdlist(struct g2d_data *g2d) +{ + struct device *dev = g2d->dev; + struct g2d_cmdlist_node *node; + + mutex_lock(&g2d->cmdlist_mutex); + if (list_empty(&g2d->free_cmdlist)) { + dev_err(dev, "there is no free cmdlist\n"); + mutex_unlock(&g2d->cmdlist_mutex); + return NULL; + } + + node = list_first_entry(&g2d->free_cmdlist, struct g2d_cmdlist_node, + list); + list_del_init(&node->list); + mutex_unlock(&g2d->cmdlist_mutex); + + return node; +} + +static void g2d_put_cmdlist(struct g2d_data *g2d, struct g2d_cmdlist_node *node) +{ + mutex_lock(&g2d->cmdlist_mutex); + list_move_tail(&node->list, &g2d->free_cmdlist); + mutex_unlock(&g2d->cmdlist_mutex); +} + +static void g2d_add_cmdlist_to_inuse(struct exynos_drm_g2d_private *g2d_priv, + struct g2d_cmdlist_node *node) +{ + struct g2d_cmdlist_node *lnode; + + if (list_empty(&g2d_priv->inuse_cmdlist)) + goto add_to_list; + + /* this links to base address of new cmdlist */ + lnode = list_entry(g2d_priv->inuse_cmdlist.prev, + struct g2d_cmdlist_node, list); + lnode->cmdlist->data[lnode->cmdlist->last] = node->dma_addr; + +add_to_list: + list_add_tail(&node->list, &g2d_priv->inuse_cmdlist); + + if (node->event) + list_add_tail(&node->event->base.link, &g2d_priv->event_list); +} + +static int g2d_get_cmdlist_gem(struct drm_device *drm_dev, + struct drm_file *file, + struct g2d_cmdlist_node *node) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; + struct g2d_cmdlist *cmdlist = node->cmdlist; + dma_addr_t *addr; + int offset; + int i; + + for (i = 0; i < node->gem_nr; i++) { + struct g2d_gem_node *gem_node; + + gem_node = kzalloc(sizeof(*gem_node), GFP_KERNEL); + if (!gem_node) { + dev_err(g2d_priv->dev, "failed to allocate gem node\n"); + return -ENOMEM; + } + + offset = cmdlist->last - (i * 2 + 1); + gem_node->handle = cmdlist->data[offset]; + + addr = exynos_drm_gem_get_dma_addr(drm_dev, gem_node->handle, + file); + if (IS_ERR(addr)) { + node->gem_nr = i; + kfree(gem_node); + return PTR_ERR(addr); + } + + cmdlist->data[offset] = *addr; + list_add_tail(&gem_node->list, &g2d_priv->gem_list); + g2d_priv->gem_nr++; + } + + return 0; +} + +static void g2d_put_cmdlist_gem(struct drm_device *drm_dev, + struct drm_file *file, + unsigned int nr) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; + struct g2d_gem_node *node, *n; + + list_for_each_entry_safe_reverse(node, n, &g2d_priv->gem_list, list) { + if (!nr) + break; + + exynos_drm_gem_put_dma_addr(drm_dev, node->handle, file); + list_del_init(&node->list); + kfree(node); + nr--; + } +} + +static void g2d_dma_start(struct g2d_data *g2d, + struct g2d_runqueue_node *runqueue_node) +{ + struct g2d_cmdlist_node *node = + list_first_entry(&runqueue_node->run_cmdlist, + struct g2d_cmdlist_node, list); + + pm_runtime_get_sync(g2d->dev); + clk_enable(g2d->gate_clk); + + /* interrupt enable */ + writel_relaxed(G2D_INTEN_ACF | G2D_INTEN_UCF | G2D_INTEN_GCF, + g2d->regs + G2D_INTEN); + + writel_relaxed(node->dma_addr, g2d->regs + G2D_DMA_SFR_BASE_ADDR); + writel_relaxed(G2D_DMA_START, g2d->regs + G2D_DMA_COMMAND); +} + +static struct g2d_runqueue_node *g2d_get_runqueue_node(struct g2d_data *g2d) +{ + struct g2d_runqueue_node *runqueue_node; + + if (list_empty(&g2d->runqueue)) + return NULL; + + runqueue_node = list_first_entry(&g2d->runqueue, + struct g2d_runqueue_node, list); + list_del_init(&runqueue_node->list); + return runqueue_node; +} + +static void g2d_free_runqueue_node(struct g2d_data *g2d, + struct g2d_runqueue_node *runqueue_node) +{ + if (!runqueue_node) + return; + + mutex_lock(&g2d->cmdlist_mutex); + list_splice_tail_init(&runqueue_node->run_cmdlist, &g2d->free_cmdlist); + mutex_unlock(&g2d->cmdlist_mutex); + + kmem_cache_free(g2d->runqueue_slab, runqueue_node); +} + +static void g2d_exec_runqueue(struct g2d_data *g2d) +{ + g2d->runqueue_node = g2d_get_runqueue_node(g2d); + if (g2d->runqueue_node) + g2d_dma_start(g2d, g2d->runqueue_node); +} + +static void g2d_runqueue_worker(struct work_struct *work) +{ + struct g2d_data *g2d = container_of(work, struct g2d_data, + runqueue_work); + + + mutex_lock(&g2d->runqueue_mutex); + clk_disable(g2d->gate_clk); + pm_runtime_put_sync(g2d->dev); + + complete(&g2d->runqueue_node->complete); + if (g2d->runqueue_node->async) + g2d_free_runqueue_node(g2d, g2d->runqueue_node); + + if (g2d->suspended) + g2d->runqueue_node = NULL; + else + g2d_exec_runqueue(g2d); + mutex_unlock(&g2d->runqueue_mutex); +} + +static void g2d_finish_event(struct g2d_data *g2d, u32 cmdlist_no) +{ + struct drm_device *drm_dev = g2d->subdrv.drm_dev; + struct g2d_runqueue_node *runqueue_node = g2d->runqueue_node; + struct drm_exynos_pending_g2d_event *e; + struct timeval now; + unsigned long flags; + + if (list_empty(&runqueue_node->event_list)) + return; + + e = list_first_entry(&runqueue_node->event_list, + struct drm_exynos_pending_g2d_event, base.link); + + do_gettimeofday(&now); + e->event.tv_sec = now.tv_sec; + e->event.tv_usec = now.tv_usec; + e->event.cmdlist_no = cmdlist_no; + + spin_lock_irqsave(&drm_dev->event_lock, flags); + list_move_tail(&e->base.link, &e->base.file_priv->event_list); + wake_up_interruptible(&e->base.file_priv->event_wait); + spin_unlock_irqrestore(&drm_dev->event_lock, flags); +} + +static irqreturn_t g2d_irq_handler(int irq, void *dev_id) +{ + struct g2d_data *g2d = dev_id; + u32 pending; + + pending = readl_relaxed(g2d->regs + G2D_INTC_PEND); + if (pending) + writel_relaxed(pending, g2d->regs + G2D_INTC_PEND); + + if (pending & G2D_INTP_GCMD_FIN) { + u32 cmdlist_no = readl_relaxed(g2d->regs + G2D_DMA_STATUS); + + cmdlist_no = (cmdlist_no & G2D_DMA_LIST_DONE_COUNT) >> + G2D_DMA_LIST_DONE_COUNT_OFFSET; + + g2d_finish_event(g2d, cmdlist_no); + + writel_relaxed(0, g2d->regs + G2D_DMA_HOLD_CMD); + if (!(pending & G2D_INTP_ACMD_FIN)) { + writel_relaxed(G2D_DMA_CONTINUE, + g2d->regs + G2D_DMA_COMMAND); + } + } + + if (pending & G2D_INTP_ACMD_FIN) + queue_work(g2d->g2d_workq, &g2d->runqueue_work); + + return IRQ_HANDLED; +} + +static int g2d_check_reg_offset(struct device *dev, struct g2d_cmdlist *cmdlist, + int nr, bool for_addr) +{ + int reg_offset; + int index; + int i; + + for (i = 0; i < nr; i++) { + index = cmdlist->last - 2 * (i + 1); + reg_offset = cmdlist->data[index] & ~0xfffff000; + + if (reg_offset < G2D_VALID_START || reg_offset > G2D_VALID_END) + goto err; + if (reg_offset % 4) + goto err; + + switch (reg_offset) { + case G2D_SRC_BASE_ADDR: + case G2D_SRC_PLANE2_BASE_ADDR: + case G2D_DST_BASE_ADDR: + case G2D_DST_PLANE2_BASE_ADDR: + case G2D_PAT_BASE_ADDR: + case G2D_MSK_BASE_ADDR: + if (!for_addr) + goto err; + break; + default: + if (for_addr) + goto err; + break; + } + } + + return 0; + +err: + dev_err(dev, "Bad register offset: 0x%x\n", cmdlist->data[index]); + return -EINVAL; +} + +/* ioctl functions */ +int exynos_g2d_get_ver_ioctl(struct drm_device *drm_dev, void *data, + struct drm_file *file) +{ + struct drm_exynos_g2d_get_ver *ver = data; + + ver->major = G2D_HW_MAJOR_VER; + ver->minor = G2D_HW_MINOR_VER; + + return 0; +} +EXPORT_SYMBOL_GPL(exynos_g2d_get_ver_ioctl); + +int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data, + struct drm_file *file) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; + struct device *dev = g2d_priv->dev; + struct g2d_data *g2d; + struct drm_exynos_g2d_set_cmdlist *req = data; + struct drm_exynos_g2d_cmd *cmd; + struct drm_exynos_pending_g2d_event *e; + struct g2d_cmdlist_node *node; + struct g2d_cmdlist *cmdlist; + unsigned long flags; + int size; + int ret; + + if (!dev) + return -ENODEV; + + g2d = dev_get_drvdata(dev); + if (!g2d) + return -EFAULT; + + node = g2d_get_cmdlist(g2d); + if (!node) + return -ENOMEM; + + node->event = NULL; + + if (req->event_type != G2D_EVENT_NOT) { + spin_lock_irqsave(&drm_dev->event_lock, flags); + if (file->event_space < sizeof(e->event)) { + spin_unlock_irqrestore(&drm_dev->event_lock, flags); + ret = -ENOMEM; + goto err; + } + file->event_space -= sizeof(e->event); + spin_unlock_irqrestore(&drm_dev->event_lock, flags); + + e = kzalloc(sizeof(*node->event), GFP_KERNEL); + if (!e) { + dev_err(dev, "failed to allocate event\n"); + + spin_lock_irqsave(&drm_dev->event_lock, flags); + file->event_space += sizeof(e->event); + spin_unlock_irqrestore(&drm_dev->event_lock, flags); + + ret = -ENOMEM; + goto err; + } + + e->event.base.type = DRM_EXYNOS_G2D_EVENT; + e->event.base.length = sizeof(e->event); + e->event.user_data = req->user_data; + e->base.event = &e->event.base; + e->base.file_priv = file; + e->base.destroy = (void (*) (struct drm_pending_event *)) kfree; + + node->event = e; + } + + cmdlist = node->cmdlist; + + cmdlist->last = 0; + + /* + * If don't clear SFR registers, the cmdlist is affected by register + * values of previous cmdlist. G2D hw executes SFR clear command and + * a next command at the same time then the next command is ignored and + * is executed rightly from next next command, so needs a dummy command + * to next command of SFR clear command. + */ + cmdlist->data[cmdlist->last++] = G2D_SOFT_RESET; + cmdlist->data[cmdlist->last++] = G2D_SFRCLEAR; + cmdlist->data[cmdlist->last++] = G2D_SRC_BASE_ADDR; + cmdlist->data[cmdlist->last++] = 0; + + if (node->event) { + cmdlist->data[cmdlist->last++] = G2D_DMA_HOLD_CMD; + cmdlist->data[cmdlist->last++] = G2D_LIST_HOLD; + } + + /* Check size of cmdlist: last 2 is about G2D_BITBLT_START */ + size = cmdlist->last + req->cmd_nr * 2 + req->cmd_gem_nr * 2 + 2; + if (size > G2D_CMDLIST_DATA_NUM) { + dev_err(dev, "cmdlist size is too big\n"); + ret = -EINVAL; + goto err_free_event; + } + + cmd = (struct drm_exynos_g2d_cmd *)(uint32_t)req->cmd; + + if (copy_from_user(cmdlist->data + cmdlist->last, + (void __user *)cmd, + sizeof(*cmd) * req->cmd_nr)) { + ret = -EFAULT; + goto err_free_event; + } + cmdlist->last += req->cmd_nr * 2; + + ret = g2d_check_reg_offset(dev, cmdlist, req->cmd_nr, false); + if (ret < 0) + goto err_free_event; + + node->gem_nr = req->cmd_gem_nr; + if (req->cmd_gem_nr) { + struct drm_exynos_g2d_cmd *cmd_gem; + + cmd_gem = (struct drm_exynos_g2d_cmd *)(uint32_t)req->cmd_gem; + + if (copy_from_user(cmdlist->data + cmdlist->last, + (void __user *)cmd_gem, + sizeof(*cmd_gem) * req->cmd_gem_nr)) { + ret = -EFAULT; + goto err_free_event; + } + cmdlist->last += req->cmd_gem_nr * 2; + + ret = g2d_check_reg_offset(dev, cmdlist, req->cmd_gem_nr, true); + if (ret < 0) + goto err_free_event; + + ret = g2d_get_cmdlist_gem(drm_dev, file, node); + if (ret < 0) + goto err_unmap; + } + + cmdlist->data[cmdlist->last++] = G2D_BITBLT_START; + cmdlist->data[cmdlist->last++] = G2D_START_BITBLT; + + /* head */ + cmdlist->head = cmdlist->last / 2; + + /* tail */ + cmdlist->data[cmdlist->last] = 0; + + g2d_add_cmdlist_to_inuse(g2d_priv, node); + + return 0; + +err_unmap: + g2d_put_cmdlist_gem(drm_dev, file, node->gem_nr); +err_free_event: + if (node->event) { + spin_lock_irqsave(&drm_dev->event_lock, flags); + file->event_space += sizeof(e->event); + spin_unlock_irqrestore(&drm_dev->event_lock, flags); + kfree(node->event); + } +err: + g2d_put_cmdlist(g2d, node); + return ret; +} +EXPORT_SYMBOL_GPL(exynos_g2d_set_cmdlist_ioctl); + +int exynos_g2d_exec_ioctl(struct drm_device *drm_dev, void *data, + struct drm_file *file) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; + struct device *dev = g2d_priv->dev; + struct g2d_data *g2d; + struct drm_exynos_g2d_exec *req = data; + struct g2d_runqueue_node *runqueue_node; + struct list_head *run_cmdlist; + struct list_head *event_list; + + if (!dev) + return -ENODEV; + + g2d = dev_get_drvdata(dev); + if (!g2d) + return -EFAULT; + + runqueue_node = kmem_cache_alloc(g2d->runqueue_slab, GFP_KERNEL); + if (!runqueue_node) { + dev_err(dev, "failed to allocate memory\n"); + return -ENOMEM; + } + run_cmdlist = &runqueue_node->run_cmdlist; + event_list = &runqueue_node->event_list; + INIT_LIST_HEAD(run_cmdlist); + INIT_LIST_HEAD(event_list); + init_completion(&runqueue_node->complete); + runqueue_node->async = req->async; + + list_splice_init(&g2d_priv->inuse_cmdlist, run_cmdlist); + list_splice_init(&g2d_priv->event_list, event_list); + + if (list_empty(run_cmdlist)) { + dev_err(dev, "there is no inuse cmdlist\n"); + kmem_cache_free(g2d->runqueue_slab, runqueue_node); + return -EPERM; + } + + mutex_lock(&g2d->runqueue_mutex); + list_add_tail(&runqueue_node->list, &g2d->runqueue); + if (!g2d->runqueue_node) + g2d_exec_runqueue(g2d); + mutex_unlock(&g2d->runqueue_mutex); + + if (runqueue_node->async) + goto out; + + wait_for_completion(&runqueue_node->complete); + g2d_free_runqueue_node(g2d, runqueue_node); + +out: + return 0; +} +EXPORT_SYMBOL_GPL(exynos_g2d_exec_ioctl); + +static int g2d_open(struct drm_device *drm_dev, struct device *dev, + struct drm_file *file) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_g2d_private *g2d_priv; + + g2d_priv = kzalloc(sizeof(*g2d_priv), GFP_KERNEL); + if (!g2d_priv) { + dev_err(dev, "failed to allocate g2d private data\n"); + return -ENOMEM; + } + + g2d_priv->dev = dev; + file_priv->g2d_priv = g2d_priv; + + INIT_LIST_HEAD(&g2d_priv->inuse_cmdlist); + INIT_LIST_HEAD(&g2d_priv->event_list); + INIT_LIST_HEAD(&g2d_priv->gem_list); + + return 0; +} + +static void g2d_close(struct drm_device *drm_dev, struct device *dev, + struct drm_file *file) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; + struct g2d_data *g2d; + struct g2d_cmdlist_node *node, *n; + + if (!dev) + return; + + g2d = dev_get_drvdata(dev); + if (!g2d) + return; + + mutex_lock(&g2d->cmdlist_mutex); + list_for_each_entry_safe(node, n, &g2d_priv->inuse_cmdlist, list) + list_move_tail(&node->list, &g2d->free_cmdlist); + mutex_unlock(&g2d->cmdlist_mutex); + + g2d_put_cmdlist_gem(drm_dev, file, g2d_priv->gem_nr); + + kfree(file_priv->g2d_priv); +} + +static int __devinit g2d_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct g2d_data *g2d; + struct exynos_drm_subdrv *subdrv; + int ret; + + g2d = kzalloc(sizeof(*g2d), GFP_KERNEL); + if (!g2d) { + dev_err(dev, "failed to allocate driver data\n"); + return -ENOMEM; + } + + g2d->runqueue_slab = kmem_cache_create("g2d_runqueue_slab", + sizeof(struct g2d_runqueue_node), 0, 0, NULL); + if (!g2d->runqueue_slab) { + ret = -ENOMEM; + goto err_free_mem; + } + + g2d->dev = dev; + + g2d->g2d_workq = create_singlethread_workqueue("g2d"); + if (!g2d->g2d_workq) { + dev_err(dev, "failed to create workqueue\n"); + ret = -EINVAL; + goto err_destroy_slab; + } + + INIT_WORK(&g2d->runqueue_work, g2d_runqueue_worker); + INIT_LIST_HEAD(&g2d->free_cmdlist); + INIT_LIST_HEAD(&g2d->runqueue); + + mutex_init(&g2d->cmdlist_mutex); + mutex_init(&g2d->runqueue_mutex); + + ret = g2d_init_cmdlist(g2d); + if (ret < 0) + goto err_destroy_workqueue; + + g2d->gate_clk = clk_get(dev, "fimg2d"); + if (IS_ERR(g2d->gate_clk)) { + dev_err(dev, "failed to get gate clock\n"); + ret = PTR_ERR(g2d->gate_clk); + goto err_fini_cmdlist; + } + + pm_runtime_enable(dev); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "failed to get I/O memory\n"); + ret = -ENOENT; + goto err_put_clk; + } + + g2d->regs_res = request_mem_region(res->start, resource_size(res), + dev_name(dev)); + if (!g2d->regs_res) { + dev_err(dev, "failed to request I/O memory\n"); + ret = -ENOENT; + goto err_put_clk; + } + + g2d->regs = ioremap(res->start, resource_size(res)); + if (!g2d->regs) { + dev_err(dev, "failed to remap I/O memory\n"); + ret = -ENXIO; + goto err_release_res; + } + + g2d->irq = platform_get_irq(pdev, 0); + if (g2d->irq < 0) { + dev_err(dev, "failed to get irq\n"); + ret = g2d->irq; + goto err_unmap_base; + } + + ret = request_irq(g2d->irq, g2d_irq_handler, 0, "drm_g2d", g2d); + if (ret < 0) { + dev_err(dev, "irq request failed\n"); + goto err_unmap_base; + } + + platform_set_drvdata(pdev, g2d); + + subdrv = &g2d->subdrv; + subdrv->dev = dev; + subdrv->open = g2d_open; + subdrv->close = g2d_close; + + ret = exynos_drm_subdrv_register(subdrv); + if (ret < 0) { + dev_err(dev, "failed to register drm g2d device\n"); + goto err_free_irq; + } + + dev_info(dev, "The exynos g2d(ver %d.%d) successfully probed\n", + G2D_HW_MAJOR_VER, G2D_HW_MINOR_VER); + + return 0; + +err_free_irq: + free_irq(g2d->irq, g2d); +err_unmap_base: + iounmap(g2d->regs); +err_release_res: + release_resource(g2d->regs_res); + kfree(g2d->regs_res); +err_put_clk: + pm_runtime_disable(dev); + clk_put(g2d->gate_clk); +err_fini_cmdlist: + g2d_fini_cmdlist(g2d); +err_destroy_workqueue: + destroy_workqueue(g2d->g2d_workq); +err_destroy_slab: + kmem_cache_destroy(g2d->runqueue_slab); +err_free_mem: + kfree(g2d); + return ret; +} + +static int __devexit g2d_remove(struct platform_device *pdev) +{ + struct g2d_data *g2d = platform_get_drvdata(pdev); + + cancel_work_sync(&g2d->runqueue_work); + exynos_drm_subdrv_unregister(&g2d->subdrv); + free_irq(g2d->irq, g2d); + + while (g2d->runqueue_node) { + g2d_free_runqueue_node(g2d, g2d->runqueue_node); + g2d->runqueue_node = g2d_get_runqueue_node(g2d); + } + + iounmap(g2d->regs); + release_resource(g2d->regs_res); + kfree(g2d->regs_res); + + pm_runtime_disable(&pdev->dev); + clk_put(g2d->gate_clk); + + g2d_fini_cmdlist(g2d); + destroy_workqueue(g2d->g2d_workq); + kmem_cache_destroy(g2d->runqueue_slab); + kfree(g2d); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int g2d_suspend(struct device *dev) +{ + struct g2d_data *g2d = dev_get_drvdata(dev); + + mutex_lock(&g2d->runqueue_mutex); + g2d->suspended = true; + mutex_unlock(&g2d->runqueue_mutex); + + while (g2d->runqueue_node) + /* FIXME: good range? */ + usleep_range(500, 1000); + + flush_work_sync(&g2d->runqueue_work); + + return 0; +} + +static int g2d_resume(struct device *dev) +{ + struct g2d_data *g2d = dev_get_drvdata(dev); + + g2d->suspended = false; + g2d_exec_runqueue(g2d); + + return 0; +} +#endif + +SIMPLE_DEV_PM_OPS(g2d_pm_ops, g2d_suspend, g2d_resume); + +struct platform_driver g2d_driver = { + .probe = g2d_probe, + .remove = __devexit_p(g2d_remove), + .driver = { + .name = "s5p-g2d", + .owner = THIS_MODULE, + .pm = &g2d_pm_ops, + }, +}; diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.h b/drivers/gpu/drm/exynos/exynos_drm_g2d.h new file mode 100644 index 00000000000..1a9c7ca8c15 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * Authors: Joonyoung Shim <jy0922.shim@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundationr + */ + +#ifdef CONFIG_DRM_EXYNOS_G2D +extern int exynos_g2d_get_ver_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int exynos_g2d_set_cmdlist_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int exynos_g2d_exec_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +#else +static inline int exynos_g2d_get_ver_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return -ENODEV; +} + +static inline int exynos_g2d_set_cmdlist_ioctl(struct drm_device *dev, + void *data, + struct drm_file *file_priv) +{ + return -ENODEV; +} + +static inline int exynos_g2d_exec_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return -ENODEV; +} +#endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index 1dffa8359f8..fc91293c456 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -66,6 +66,22 @@ static int check_gem_flags(unsigned int flags) return 0; } +static void update_vm_cache_attr(struct exynos_drm_gem_obj *obj, + struct vm_area_struct *vma) +{ + DRM_DEBUG_KMS("flags = 0x%x\n", obj->flags); + + /* non-cachable as default. */ + if (obj->flags & EXYNOS_BO_CACHABLE) + vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); + else if (obj->flags & EXYNOS_BO_WC) + vma->vm_page_prot = + pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); + else + vma->vm_page_prot = + pgprot_noncached(vm_get_page_prot(vma->vm_flags)); +} + static unsigned long roundup_gem_size(unsigned long size, unsigned int flags) { if (!IS_NONCONTIG_BUFFER(flags)) { @@ -80,7 +96,7 @@ out: return roundup(size, PAGE_SIZE); } -static struct page **exynos_gem_get_pages(struct drm_gem_object *obj, +struct page **exynos_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask) { struct inode *inode; @@ -180,6 +196,7 @@ static int exynos_drm_gem_get_pages(struct drm_gem_object *obj) } npages = obj->size >> PAGE_SHIFT; + buf->page_size = PAGE_SIZE; buf->sgt = kzalloc(sizeof(struct sg_table), GFP_KERNEL); if (!buf->sgt) { @@ -262,24 +279,24 @@ static int exynos_drm_gem_handle_create(struct drm_gem_object *obj, void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj) { struct drm_gem_object *obj; + struct exynos_drm_gem_buf *buf; DRM_DEBUG_KMS("%s\n", __FILE__); - if (!exynos_gem_obj) - return; - obj = &exynos_gem_obj->base; + buf = exynos_gem_obj->buffer; DRM_DEBUG_KMS("handle count = %d\n", atomic_read(&obj->handle_count)); - if ((exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) && - exynos_gem_obj->buffer->pages) + if (!buf->pages) + return; + + if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) exynos_drm_gem_put_pages(obj); else - exynos_drm_free_buf(obj->dev, exynos_gem_obj->flags, - exynos_gem_obj->buffer); + exynos_drm_free_buf(obj->dev, exynos_gem_obj->flags, buf); - exynos_drm_fini_buf(obj->dev, exynos_gem_obj->buffer); + exynos_drm_fini_buf(obj->dev, buf); exynos_gem_obj->buffer = NULL; if (obj->map_list.map) @@ -292,7 +309,7 @@ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj) exynos_gem_obj = NULL; } -static struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev, +struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev, unsigned long size) { struct exynos_drm_gem_obj *exynos_gem_obj; @@ -493,8 +510,7 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp, vma->vm_flags |= (VM_IO | VM_RESERVED); - /* in case of direct mapping, always having non-cachable attribute */ - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + update_vm_cache_attr(exynos_gem_obj, vma); vm_size = usize = vma->vm_end - vma->vm_start; @@ -588,6 +604,32 @@ int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, return 0; } +int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ struct exynos_drm_gem_obj *exynos_gem_obj; + struct drm_exynos_gem_info *args = data; + struct drm_gem_object *obj; + + mutex_lock(&dev->struct_mutex); + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (!obj) { + DRM_ERROR("failed to lookup gem object.\n"); + mutex_unlock(&dev->struct_mutex); + return -EINVAL; + } + + exynos_gem_obj = to_exynos_gem_obj(obj); + + args->flags = exynos_gem_obj->flags; + args->size = exynos_gem_obj->size; + + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + + return 0; +} + int exynos_drm_gem_init_object(struct drm_gem_object *obj) { DRM_DEBUG_KMS("%s\n", __FILE__); @@ -597,8 +639,17 @@ int exynos_drm_gem_init_object(struct drm_gem_object *obj) void exynos_drm_gem_free_object(struct drm_gem_object *obj) { + struct exynos_drm_gem_obj *exynos_gem_obj; + struct exynos_drm_gem_buf *buf; + DRM_DEBUG_KMS("%s\n", __FILE__); + exynos_gem_obj = to_exynos_gem_obj(obj); + buf = exynos_gem_obj->buffer; + + if (obj->import_attach) + drm_prime_gem_destroy(obj, buf->sgt); + exynos_drm_gem_destroy(to_exynos_gem_obj(obj)); } @@ -724,6 +775,8 @@ int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) { + struct exynos_drm_gem_obj *exynos_gem_obj; + struct drm_gem_object *obj; int ret; DRM_DEBUG_KMS("%s\n", __FILE__); @@ -735,8 +788,20 @@ int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) return ret; } + obj = vma->vm_private_data; + exynos_gem_obj = to_exynos_gem_obj(obj); + + ret = check_gem_flags(exynos_gem_obj->flags); + if (ret) { + drm_gem_vm_close(vma); + drm_gem_free_mmap_offset(obj); + return ret; + } + vma->vm_flags &= ~VM_PFNMAP; vma->vm_flags |= VM_MIXEDMAP; + update_vm_cache_attr(exynos_gem_obj, vma); + return ret; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h index 4ed84203950..14d038b6cb0 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.h +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h @@ -40,6 +40,7 @@ * device address with IOMMU. * @sgt: sg table to transfer page data. * @pages: contain all pages to allocated memory region. + * @page_size: could be 4K, 64K or 1MB. * @size: size of allocated memory region. */ struct exynos_drm_gem_buf { @@ -47,6 +48,7 @@ struct exynos_drm_gem_buf { dma_addr_t dma_addr; struct sg_table *sgt; struct page **pages; + unsigned long page_size; unsigned long size; }; @@ -74,9 +76,15 @@ struct exynos_drm_gem_obj { unsigned int flags; }; +struct page **exynos_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask); + /* destroy a buffer with gem object */ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj); +/* create a private gem object and initialize it. */ +struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev, + unsigned long size); + /* create a new buffer with gem object */ struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev, unsigned int flags, @@ -119,6 +127,10 @@ int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data, int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +/* get buffer information to memory region allocated by gem. */ +int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + /* initialize gem object. */ int exynos_drm_gem_init_object(struct drm_gem_object *obj); diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c index 3424463676e..5d9d2c2f8f3 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c @@ -37,6 +37,8 @@ struct drm_hdmi_context { struct exynos_drm_subdrv subdrv; struct exynos_drm_hdmi_context *hdmi_ctx; struct exynos_drm_hdmi_context *mixer_ctx; + + bool enabled[MIXER_WIN_NR]; }; void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops) @@ -189,23 +191,34 @@ static void drm_hdmi_dpms(struct device *subdrv_dev, int mode) DRM_DEBUG_KMS("%s\n", __FILE__); - switch (mode) { - case DRM_MODE_DPMS_ON: - break; - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - case DRM_MODE_DPMS_OFF: - if (hdmi_ops && hdmi_ops->disable) - hdmi_ops->disable(ctx->hdmi_ctx->ctx); - break; - default: - DRM_DEBUG_KMS("unkown dps mode: %d\n", mode); - break; + if (mixer_ops && mixer_ops->dpms) + mixer_ops->dpms(ctx->mixer_ctx->ctx, mode); + + if (hdmi_ops && hdmi_ops->dpms) + hdmi_ops->dpms(ctx->hdmi_ctx->ctx, mode); +} + +static void drm_hdmi_apply(struct device *subdrv_dev) +{ + struct drm_hdmi_context *ctx = to_context(subdrv_dev); + int i; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + for (i = 0; i < MIXER_WIN_NR; i++) { + if (!ctx->enabled[i]) + continue; + if (mixer_ops && mixer_ops->win_commit) + mixer_ops->win_commit(ctx->mixer_ctx->ctx, i); } + + if (hdmi_ops && hdmi_ops->commit) + hdmi_ops->commit(ctx->hdmi_ctx->ctx); } static struct exynos_drm_manager_ops drm_hdmi_manager_ops = { .dpms = drm_hdmi_dpms, + .apply = drm_hdmi_apply, .enable_vblank = drm_hdmi_enable_vblank, .disable_vblank = drm_hdmi_disable_vblank, .mode_fixup = drm_hdmi_mode_fixup, @@ -228,21 +241,37 @@ static void drm_mixer_mode_set(struct device *subdrv_dev, static void drm_mixer_commit(struct device *subdrv_dev, int zpos) { struct drm_hdmi_context *ctx = to_context(subdrv_dev); + int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos; DRM_DEBUG_KMS("%s\n", __FILE__); + if (win < 0 || win > MIXER_WIN_NR) { + DRM_ERROR("mixer window[%d] is wrong\n", win); + return; + } + if (mixer_ops && mixer_ops->win_commit) - mixer_ops->win_commit(ctx->mixer_ctx->ctx, zpos); + mixer_ops->win_commit(ctx->mixer_ctx->ctx, win); + + ctx->enabled[win] = true; } static void drm_mixer_disable(struct device *subdrv_dev, int zpos) { struct drm_hdmi_context *ctx = to_context(subdrv_dev); + int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos; DRM_DEBUG_KMS("%s\n", __FILE__); + if (win < 0 || win > MIXER_WIN_NR) { + DRM_ERROR("mixer window[%d] is wrong\n", win); + return; + } + if (mixer_ops && mixer_ops->win_disable) - mixer_ops->win_disable(ctx->mixer_ctx->ctx, zpos); + mixer_ops->win_disable(ctx->mixer_ctx->ctx, win); + + ctx->enabled[win] = false; } static struct exynos_drm_overlay_ops drm_hdmi_overlay_ops = { @@ -335,25 +364,6 @@ static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev) return 0; } -static int hdmi_runtime_suspend(struct device *dev) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - return 0; -} - -static int hdmi_runtime_resume(struct device *dev) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - return 0; -} - -static const struct dev_pm_ops hdmi_pm_ops = { - .runtime_suspend = hdmi_runtime_suspend, - .runtime_resume = hdmi_runtime_resume, -}; - static int __devexit exynos_drm_hdmi_remove(struct platform_device *pdev) { struct drm_hdmi_context *ctx = platform_get_drvdata(pdev); @@ -372,6 +382,5 @@ struct platform_driver exynos_drm_common_hdmi_driver = { .driver = { .name = "exynos-drm-hdmi", .owner = THIS_MODULE, - .pm = &hdmi_pm_ops, }, }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h index f3ae192c8dc..bd8126996e5 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h @@ -26,6 +26,9 @@ #ifndef _EXYNOS_DRM_HDMI_H_ #define _EXYNOS_DRM_HDMI_H_ +#define MIXER_WIN_NR 3 +#define MIXER_DEFAULT_WIN 0 + /* * exynos hdmi common context structure. * @@ -54,13 +57,14 @@ struct exynos_hdmi_ops { void (*get_max_resol)(void *ctx, unsigned int *width, unsigned int *height); void (*commit)(void *ctx); - void (*disable)(void *ctx); + void (*dpms)(void *ctx, int mode); }; struct exynos_mixer_ops { /* manager */ int (*enable_vblank)(void *ctx, int pipe); void (*disable_vblank)(void *ctx); + void (*dpms)(void *ctx, int mode); /* overlay */ void (*win_mode_set)(void *ctx, struct exynos_drm_overlay *overlay); diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index f92fe4c6174..c4c6525d465 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c @@ -41,8 +41,6 @@ exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, container_of(plane, struct exynos_plane, base); struct exynos_drm_overlay *overlay = &exynos_plane->overlay; struct exynos_drm_crtc_pos pos; - unsigned int x = src_x >> 16; - unsigned int y = src_y >> 16; int ret; DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); @@ -53,10 +51,12 @@ exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, pos.crtc_w = crtc_w; pos.crtc_h = crtc_h; - pos.fb_x = x; - pos.fb_y = y; + /* considering 16.16 fixed point of source values */ + pos.fb_x = src_x >> 16; + pos.fb_y = src_y >> 16; + pos.src_w = src_w >> 16; + pos.src_h = src_h >> 16; - /* TODO: scale feature */ ret = exynos_drm_overlay_update(overlay, fb, &crtc->mode, &pos); if (ret < 0) return ret; diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index b0035387645..a137e9e39a3 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -57,18 +57,16 @@ struct hdmi_resources { struct hdmi_context { struct device *dev; struct drm_device *drm_dev; - struct fb_videomode *default_timing; - unsigned int is_v13:1; - unsigned int default_win; - unsigned int default_bpp; - bool hpd_handle; - bool enabled; + bool hpd; + bool powered; + bool is_v13; + bool dvi_mode; + struct mutex hdmi_mutex; struct resource *regs_res; void __iomem *regs; - unsigned int irq; - struct workqueue_struct *wq; - struct work_struct hotplug_work; + unsigned int external_irq; + unsigned int internal_irq; struct i2c_client *ddc_port; struct i2c_client *hdmiphy_port; @@ -78,6 +76,9 @@ struct hdmi_context { struct hdmi_resources res; void *parent_ctx; + + void (*cfg_hpd)(bool external); + int (*get_hpd)(void); }; /* HDMI Version 1.3 */ @@ -361,6 +362,13 @@ static const u8 hdmiphy_conf27_027[32] = { 0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, }; +static const u8 hdmiphy_conf74_176[32] = { + 0x01, 0xd1, 0x1f, 0x10, 0x40, 0x5b, 0xef, 0x08, + 0x81, 0xa0, 0xb9, 0xd8, 0x45, 0xa0, 0xac, 0x80, + 0x5a, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86, + 0x54, 0xa6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, +}; + static const u8 hdmiphy_conf74_25[32] = { 0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08, 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80, @@ -750,6 +758,63 @@ static const struct hdmi_preset_conf hdmi_conf_1080i60 = { }, }; +static const struct hdmi_preset_conf hdmi_conf_1080p30 = { + .core = { + .h_blank = {0x18, 0x01}, + .v2_blank = {0x65, 0x04}, + .v1_blank = {0x2d, 0x00}, + .v_line = {0x65, 0x04}, + .h_line = {0x98, 0x08}, + .hsync_pol = {0x00}, + .vsync_pol = {0x00}, + .int_pro_mode = {0x00}, + .v_blank_f0 = {0xff, 0xff}, + .v_blank_f1 = {0xff, 0xff}, + .h_sync_start = {0x56, 0x00}, + .h_sync_end = {0x82, 0x00}, + .v_sync_line_bef_2 = {0x09, 0x00}, + .v_sync_line_bef_1 = {0x04, 0x00}, + .v_sync_line_aft_2 = {0xff, 0xff}, + .v_sync_line_aft_1 = {0xff, 0xff}, + .v_sync_line_aft_pxl_2 = {0xff, 0xff}, + .v_sync_line_aft_pxl_1 = {0xff, 0xff}, + .v_blank_f2 = {0xff, 0xff}, + .v_blank_f3 = {0xff, 0xff}, + .v_blank_f4 = {0xff, 0xff}, + .v_blank_f5 = {0xff, 0xff}, + .v_sync_line_aft_3 = {0xff, 0xff}, + .v_sync_line_aft_4 = {0xff, 0xff}, + .v_sync_line_aft_5 = {0xff, 0xff}, + .v_sync_line_aft_6 = {0xff, 0xff}, + .v_sync_line_aft_pxl_3 = {0xff, 0xff}, + .v_sync_line_aft_pxl_4 = {0xff, 0xff}, + .v_sync_line_aft_pxl_5 = {0xff, 0xff}, + .v_sync_line_aft_pxl_6 = {0xff, 0xff}, + .vact_space_1 = {0xff, 0xff}, + .vact_space_2 = {0xff, 0xff}, + .vact_space_3 = {0xff, 0xff}, + .vact_space_4 = {0xff, 0xff}, + .vact_space_5 = {0xff, 0xff}, + .vact_space_6 = {0xff, 0xff}, + /* other don't care */ + }, + .tg = { + 0x00, /* cmd */ + 0x98, 0x08, /* h_fsz */ + 0x18, 0x01, 0x80, 0x07, /* hact */ + 0x65, 0x04, /* v_fsz */ + 0x01, 0x00, 0x33, 0x02, /* vsync */ + 0x2d, 0x00, 0x38, 0x04, /* vact */ + 0x33, 0x02, /* field_chg */ + 0x48, 0x02, /* vact_st2 */ + 0x00, 0x00, /* vact_st3 */ + 0x00, 0x00, /* vact_st4 */ + 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ + 0x01, 0x00, 0x33, 0x02, /* field top/bot */ + 0x00, /* 3d FP */ + }, +}; + static const struct hdmi_preset_conf hdmi_conf_1080p50 = { .core = { .h_blank = {0xd0, 0x02}, @@ -864,6 +929,7 @@ static const struct hdmi_conf hdmi_confs[] = { { 1280, 720, 60, false, hdmiphy_conf74_25, &hdmi_conf_720p60 }, { 1920, 1080, 50, true, hdmiphy_conf74_25, &hdmi_conf_1080i50 }, { 1920, 1080, 60, true, hdmiphy_conf74_25, &hdmi_conf_1080i60 }, + { 1920, 1080, 30, false, hdmiphy_conf74_176, &hdmi_conf_1080p30 }, { 1920, 1080, 50, false, hdmiphy_conf148_5, &hdmi_conf_1080p50 }, { 1920, 1080, 60, false, hdmiphy_conf148_5, &hdmi_conf_1080p60 }, }; @@ -1194,12 +1260,8 @@ static int hdmi_conf_index(struct hdmi_context *hdata, static bool hdmi_is_connected(void *ctx) { struct hdmi_context *hdata = ctx; - u32 val = hdmi_reg_read(hdata, HDMI_HPD_STATUS); - - if (val) - return true; - return false; + return hdata->hpd; } static int hdmi_get_edid(void *ctx, struct drm_connector *connector, @@ -1215,10 +1277,12 @@ static int hdmi_get_edid(void *ctx, struct drm_connector *connector, raw_edid = drm_get_edid(connector, hdata->ddc_port->adapter); if (raw_edid) { + hdata->dvi_mode = !drm_detect_hdmi_monitor(raw_edid); memcpy(edid, raw_edid, min((1 + raw_edid->extensions) * EDID_LENGTH, len)); - DRM_DEBUG_KMS("width[%d] x height[%d]\n", - raw_edid->width_cm, raw_edid->height_cm); + DRM_DEBUG_KMS("%s : width[%d] x height[%d]\n", + (hdata->dvi_mode ? "dvi monitor" : "hdmi monitor"), + raw_edid->width_cm, raw_edid->height_cm); } else { return -ENODEV; } @@ -1289,28 +1353,6 @@ static int hdmi_check_timing(void *ctx, void *timing) return hdmi_v14_check_timing(check_timing); } -static int hdmi_display_power_on(void *ctx, int mode) -{ - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - switch (mode) { - case DRM_MODE_DPMS_ON: - DRM_DEBUG_KMS("hdmi [on]\n"); - break; - case DRM_MODE_DPMS_STANDBY: - break; - case DRM_MODE_DPMS_SUSPEND: - break; - case DRM_MODE_DPMS_OFF: - DRM_DEBUG_KMS("hdmi [off]\n"); - break; - default: - break; - } - - return 0; -} - static void hdmi_set_acr(u32 freq, u8 *acr) { u32 n, cts; @@ -1463,10 +1505,7 @@ static void hdmi_audio_init(struct hdmi_context *hdata) static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff) { - u32 mod; - - mod = hdmi_reg_read(hdata, HDMI_MODE_SEL); - if (mod & HDMI_DVI_MODE_EN) + if (hdata->dvi_mode) return; hdmi_reg_writeb(hdata, HDMI_AUI_CON, onoff ? 2 : 0); @@ -1478,9 +1517,6 @@ static void hdmi_conf_reset(struct hdmi_context *hdata) { u32 reg; - /* disable hpd handle for drm */ - hdata->hpd_handle = false; - if (hdata->is_v13) reg = HDMI_V13_CORE_RSTOUT; else @@ -1491,16 +1527,10 @@ static void hdmi_conf_reset(struct hdmi_context *hdata) mdelay(10); hdmi_reg_writemask(hdata, reg, ~0, HDMI_CORE_SW_RSTOUT); mdelay(10); - - /* enable hpd handle for drm */ - hdata->hpd_handle = true; } static void hdmi_conf_init(struct hdmi_context *hdata) { - /* disable hpd handle for drm */ - hdata->hpd_handle = false; - /* enable HPD interrupts */ hdmi_reg_writemask(hdata, HDMI_INTC_CON, 0, HDMI_INTC_EN_GLOBAL | HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG); @@ -1514,6 +1544,14 @@ static void hdmi_conf_init(struct hdmi_context *hdata) /* disable bluescreen */ hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN); + if (hdata->dvi_mode) { + /* choose DVI mode */ + hdmi_reg_writemask(hdata, HDMI_MODE_SEL, + HDMI_MODE_DVI_EN, HDMI_MODE_MASK); + hdmi_reg_writeb(hdata, HDMI_CON_2, + HDMI_VID_PREAMBLE_DIS | HDMI_GUARD_BAND_DIS); + } + if (hdata->is_v13) { /* choose bluescreen (fecal) color */ hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_0, 0x12); @@ -1535,9 +1573,6 @@ static void hdmi_conf_init(struct hdmi_context *hdata) hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(1), 2 << 5); hdmi_reg_writemask(hdata, HDMI_CON_1, 2, 3 << 5); } - - /* enable hpd handle for drm */ - hdata->hpd_handle = true; } static void hdmi_v13_timing_apply(struct hdmi_context *hdata) @@ -1890,8 +1925,11 @@ static void hdmi_conf_apply(struct hdmi_context *hdata) hdmiphy_conf_reset(hdata); hdmiphy_conf_apply(hdata); + mutex_lock(&hdata->hdmi_mutex); hdmi_conf_reset(hdata); hdmi_conf_init(hdata); + mutex_unlock(&hdata->hdmi_mutex); + hdmi_audio_init(hdata); /* setting core registers */ @@ -1971,20 +2009,86 @@ static void hdmi_commit(void *ctx) DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); hdmi_conf_apply(hdata); +} + +static void hdmi_poweron(struct hdmi_context *hdata) +{ + struct hdmi_resources *res = &hdata->res; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + mutex_lock(&hdata->hdmi_mutex); + if (hdata->powered) { + mutex_unlock(&hdata->hdmi_mutex); + return; + } + + hdata->powered = true; + + if (hdata->cfg_hpd) + hdata->cfg_hpd(true); + mutex_unlock(&hdata->hdmi_mutex); + + pm_runtime_get_sync(hdata->dev); + + regulator_bulk_enable(res->regul_count, res->regul_bulk); + clk_enable(res->hdmiphy); + clk_enable(res->hdmi); + clk_enable(res->sclk_hdmi); +} + +static void hdmi_poweroff(struct hdmi_context *hdata) +{ + struct hdmi_resources *res = &hdata->res; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + mutex_lock(&hdata->hdmi_mutex); + if (!hdata->powered) + goto out; + mutex_unlock(&hdata->hdmi_mutex); + + /* + * The TV power domain needs any condition of hdmiphy to turn off and + * its reset state seems to meet the condition. + */ + hdmiphy_conf_reset(hdata); + + clk_disable(res->sclk_hdmi); + clk_disable(res->hdmi); + clk_disable(res->hdmiphy); + regulator_bulk_disable(res->regul_count, res->regul_bulk); + + pm_runtime_put_sync(hdata->dev); - hdata->enabled = true; + mutex_lock(&hdata->hdmi_mutex); + if (hdata->cfg_hpd) + hdata->cfg_hpd(false); + + hdata->powered = false; + +out: + mutex_unlock(&hdata->hdmi_mutex); } -static void hdmi_disable(void *ctx) +static void hdmi_dpms(void *ctx, int mode) { struct hdmi_context *hdata = ctx; DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - if (hdata->enabled) { - hdmi_audio_control(hdata, false); - hdmiphy_conf_reset(hdata); - hdmi_conf_reset(hdata); + switch (mode) { + case DRM_MODE_DPMS_ON: + hdmi_poweron(hdata); + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + hdmi_poweroff(hdata); + break; + default: + DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode); + break; } } @@ -1993,30 +2097,35 @@ static struct exynos_hdmi_ops hdmi_ops = { .is_connected = hdmi_is_connected, .get_edid = hdmi_get_edid, .check_timing = hdmi_check_timing, - .power_on = hdmi_display_power_on, /* manager */ .mode_fixup = hdmi_mode_fixup, .mode_set = hdmi_mode_set, .get_max_resol = hdmi_get_max_resol, .commit = hdmi_commit, - .disable = hdmi_disable, + .dpms = hdmi_dpms, }; -/* - * Handle hotplug events outside the interrupt handler proper. - */ -static void hdmi_hotplug_func(struct work_struct *work) +static irqreturn_t hdmi_external_irq_thread(int irq, void *arg) { - struct hdmi_context *hdata = - container_of(work, struct hdmi_context, hotplug_work); - struct exynos_drm_hdmi_context *ctx = - (struct exynos_drm_hdmi_context *)hdata->parent_ctx; + struct exynos_drm_hdmi_context *ctx = arg; + struct hdmi_context *hdata = ctx->ctx; + + if (!hdata->get_hpd) + goto out; + + mutex_lock(&hdata->hdmi_mutex); + hdata->hpd = hdata->get_hpd(); + mutex_unlock(&hdata->hdmi_mutex); - drm_helper_hpd_irq_event(ctx->drm_dev); + if (ctx->drm_dev) + drm_helper_hpd_irq_event(ctx->drm_dev); + +out: + return IRQ_HANDLED; } -static irqreturn_t hdmi_irq_handler(int irq, void *arg) +static irqreturn_t hdmi_internal_irq_thread(int irq, void *arg) { struct exynos_drm_hdmi_context *ctx = arg; struct hdmi_context *hdata = ctx->ctx; @@ -2025,19 +2134,28 @@ static irqreturn_t hdmi_irq_handler(int irq, void *arg) intc_flag = hdmi_reg_read(hdata, HDMI_INTC_FLAG); /* clearing flags for HPD plug/unplug */ if (intc_flag & HDMI_INTC_FLAG_HPD_UNPLUG) { - DRM_DEBUG_KMS("unplugged, handling:%d\n", hdata->hpd_handle); + DRM_DEBUG_KMS("unplugged\n"); hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0, HDMI_INTC_FLAG_HPD_UNPLUG); } if (intc_flag & HDMI_INTC_FLAG_HPD_PLUG) { - DRM_DEBUG_KMS("plugged, handling:%d\n", hdata->hpd_handle); + DRM_DEBUG_KMS("plugged\n"); hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0, HDMI_INTC_FLAG_HPD_PLUG); } - if (ctx->drm_dev && hdata->hpd_handle) - queue_work(hdata->wq, &hdata->hotplug_work); + mutex_lock(&hdata->hdmi_mutex); + hdata->hpd = hdmi_reg_read(hdata, HDMI_HPD_STATUS); + if (hdata->powered && hdata->hpd) { + mutex_unlock(&hdata->hdmi_mutex); + goto out; + } + mutex_unlock(&hdata->hdmi_mutex); + + if (ctx->drm_dev) + drm_helper_hpd_irq_event(ctx->drm_dev); +out: return IRQ_HANDLED; } @@ -2131,68 +2249,6 @@ static int hdmi_resources_cleanup(struct hdmi_context *hdata) return 0; } -static void hdmi_resource_poweron(struct hdmi_context *hdata) -{ - struct hdmi_resources *res = &hdata->res; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - /* turn HDMI power on */ - regulator_bulk_enable(res->regul_count, res->regul_bulk); - /* power-on hdmi physical interface */ - clk_enable(res->hdmiphy); - /* turn clocks on */ - clk_enable(res->hdmi); - clk_enable(res->sclk_hdmi); - - hdmiphy_conf_reset(hdata); - hdmi_conf_reset(hdata); - hdmi_conf_init(hdata); - hdmi_audio_init(hdata); -} - -static void hdmi_resource_poweroff(struct hdmi_context *hdata) -{ - struct hdmi_resources *res = &hdata->res; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - /* turn clocks off */ - clk_disable(res->sclk_hdmi); - clk_disable(res->hdmi); - /* power-off hdmiphy */ - clk_disable(res->hdmiphy); - /* turn HDMI power off */ - regulator_bulk_disable(res->regul_count, res->regul_bulk); -} - -static int hdmi_runtime_suspend(struct device *dev) -{ - struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); - - DRM_DEBUG_KMS("%s\n", __func__); - - hdmi_resource_poweroff(ctx->ctx); - - return 0; -} - -static int hdmi_runtime_resume(struct device *dev) -{ - struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); - - DRM_DEBUG_KMS("%s\n", __func__); - - hdmi_resource_poweron(ctx->ctx); - - return 0; -} - -static const struct dev_pm_ops hdmi_pm_ops = { - .runtime_suspend = hdmi_runtime_suspend, - .runtime_resume = hdmi_runtime_resume, -}; - static struct i2c_client *hdmi_ddc, *hdmi_hdmiphy; void hdmi_attach_ddc_client(struct i2c_client *ddc) @@ -2237,15 +2293,16 @@ static int __devinit hdmi_probe(struct platform_device *pdev) return -ENOMEM; } + mutex_init(&hdata->hdmi_mutex); + drm_hdmi_ctx->ctx = (void *)hdata; hdata->parent_ctx = (void *)drm_hdmi_ctx; platform_set_drvdata(pdev, drm_hdmi_ctx); hdata->is_v13 = pdata->is_v13; - hdata->default_win = pdata->default_win; - hdata->default_timing = &pdata->timing; - hdata->default_bpp = pdata->bpp; + hdata->cfg_hpd = pdata->cfg_hpd; + hdata->get_hpd = pdata->get_hpd; hdata->dev = dev; ret = hdmi_resources_init(hdata); @@ -2294,41 +2351,49 @@ static int __devinit hdmi_probe(struct platform_device *pdev) hdata->hdmiphy_port = hdmi_hdmiphy; - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (res == NULL) { - DRM_ERROR("get interrupt resource failed.\n"); - ret = -ENXIO; + hdata->external_irq = platform_get_irq_byname(pdev, "external_irq"); + if (hdata->external_irq < 0) { + DRM_ERROR("failed to get platform irq\n"); + ret = hdata->external_irq; goto err_hdmiphy; } - /* create workqueue and hotplug work */ - hdata->wq = alloc_workqueue("exynos-drm-hdmi", - WQ_UNBOUND | WQ_NON_REENTRANT, 1); - if (hdata->wq == NULL) { - DRM_ERROR("Failed to create workqueue.\n"); - ret = -ENOMEM; + hdata->internal_irq = platform_get_irq_byname(pdev, "internal_irq"); + if (hdata->internal_irq < 0) { + DRM_ERROR("failed to get platform internal irq\n"); + ret = hdata->internal_irq; goto err_hdmiphy; } - INIT_WORK(&hdata->hotplug_work, hdmi_hotplug_func); - /* register hpd interrupt */ - ret = request_irq(res->start, hdmi_irq_handler, 0, "drm_hdmi", - drm_hdmi_ctx); + ret = request_threaded_irq(hdata->external_irq, NULL, + hdmi_external_irq_thread, IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "hdmi_external", drm_hdmi_ctx); if (ret) { - DRM_ERROR("request interrupt failed.\n"); - goto err_workqueue; + DRM_ERROR("failed to register hdmi internal interrupt\n"); + goto err_hdmiphy; + } + + if (hdata->cfg_hpd) + hdata->cfg_hpd(false); + + ret = request_threaded_irq(hdata->internal_irq, NULL, + hdmi_internal_irq_thread, IRQF_ONESHOT, + "hdmi_internal", drm_hdmi_ctx); + if (ret) { + DRM_ERROR("failed to register hdmi internal interrupt\n"); + goto err_free_irq; } - hdata->irq = res->start; /* register specific callbacks to common hdmi. */ exynos_hdmi_ops_register(&hdmi_ops); - hdmi_resource_poweron(hdata); + pm_runtime_enable(dev); return 0; -err_workqueue: - destroy_workqueue(hdata->wq); +err_free_irq: + free_irq(hdata->external_irq, drm_hdmi_ctx); err_hdmiphy: i2c_del_driver(&hdmiphy_driver); err_ddc: @@ -2348,18 +2413,15 @@ err_data: static int __devexit hdmi_remove(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct exynos_drm_hdmi_context *ctx = platform_get_drvdata(pdev); struct hdmi_context *hdata = ctx->ctx; DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - hdmi_resource_poweroff(hdata); + pm_runtime_disable(dev); - disable_irq(hdata->irq); - free_irq(hdata->irq, hdata); - - cancel_work_sync(&hdata->hotplug_work); - destroy_workqueue(hdata->wq); + free_irq(hdata->internal_irq, hdata); hdmi_resources_cleanup(hdata); @@ -2378,12 +2440,43 @@ static int __devexit hdmi_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int hdmi_suspend(struct device *dev) +{ + struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); + struct hdmi_context *hdata = ctx->ctx; + + disable_irq(hdata->internal_irq); + disable_irq(hdata->external_irq); + + hdata->hpd = false; + if (ctx->drm_dev) + drm_helper_hpd_irq_event(ctx->drm_dev); + + hdmi_poweroff(hdata); + + return 0; +} + +static int hdmi_resume(struct device *dev) +{ + struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); + struct hdmi_context *hdata = ctx->ctx; + + enable_irq(hdata->external_irq); + enable_irq(hdata->internal_irq); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(hdmi_pm_ops, hdmi_suspend, hdmi_resume); + struct platform_driver hdmi_driver = { .probe = hdmi_probe, .remove = __devexit_p(hdmi_remove), .driver = { .name = "exynos4-hdmi", .owner = THIS_MODULE, - .pm = &hdmi_pm_ops, + .pm = &hdmi_pm_ops, }, }; diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index e15438c0112..68ef0102837 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -37,9 +37,6 @@ #include "exynos_drm_drv.h" #include "exynos_drm_hdmi.h" -#define MIXER_WIN_NR 3 -#define MIXER_DEFAULT_WIN 0 - #define get_mixer_context(dev) platform_get_drvdata(to_platform_device(dev)) struct hdmi_win_data { @@ -57,13 +54,14 @@ struct hdmi_win_data { unsigned int fb_y; unsigned int fb_width; unsigned int fb_height; + unsigned int src_width; + unsigned int src_height; unsigned int mode_width; unsigned int mode_height; unsigned int scan_flags; }; struct mixer_resources { - struct device *dev; int irq; void __iomem *mixer_regs; void __iomem *vp_regs; @@ -76,10 +74,13 @@ struct mixer_resources { }; struct mixer_context { - unsigned int irq; + struct device *dev; int pipe; bool interlace; + bool powered; + u32 int_en; + struct mutex mixer_mutex; struct mixer_resources mixer_res; struct hdmi_win_data win_data[MIXER_WIN_NR]; }; @@ -352,10 +353,7 @@ static void vp_video_buffer(struct mixer_context *ctx, int win) struct mixer_resources *res = &ctx->mixer_res; unsigned long flags; struct hdmi_win_data *win_data; - unsigned int full_width, full_height, width, height; unsigned int x_ratio, y_ratio; - unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset; - unsigned int mode_width, mode_height; unsigned int buf_num; dma_addr_t luma_addr[2], chroma_addr[2]; bool tiled_mode = false; @@ -382,21 +380,9 @@ static void vp_video_buffer(struct mixer_context *ctx, int win) return; } - full_width = win_data->fb_width; - full_height = win_data->fb_height; - width = win_data->crtc_width; - height = win_data->crtc_height; - mode_width = win_data->mode_width; - mode_height = win_data->mode_height; - /* scaling feature: (src << 16) / dst */ - x_ratio = (width << 16) / width; - y_ratio = (height << 16) / height; - - src_x_offset = win_data->fb_x; - src_y_offset = win_data->fb_y; - dst_x_offset = win_data->crtc_x; - dst_y_offset = win_data->crtc_y; + x_ratio = (win_data->src_width << 16) / win_data->crtc_width; + y_ratio = (win_data->src_height << 16) / win_data->crtc_height; if (buf_num == 2) { luma_addr[0] = win_data->dma_addr; @@ -404,7 +390,7 @@ static void vp_video_buffer(struct mixer_context *ctx, int win) } else { luma_addr[0] = win_data->dma_addr; chroma_addr[0] = win_data->dma_addr - + (full_width * full_height); + + (win_data->fb_width * win_data->fb_height); } if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE) { @@ -413,8 +399,8 @@ static void vp_video_buffer(struct mixer_context *ctx, int win) luma_addr[1] = luma_addr[0] + 0x40; chroma_addr[1] = chroma_addr[0] + 0x40; } else { - luma_addr[1] = luma_addr[0] + full_width; - chroma_addr[1] = chroma_addr[0] + full_width; + luma_addr[1] = luma_addr[0] + win_data->fb_width; + chroma_addr[1] = chroma_addr[0] + win_data->fb_width; } } else { ctx->interlace = false; @@ -435,26 +421,26 @@ static void vp_video_buffer(struct mixer_context *ctx, int win) vp_reg_writemask(res, VP_MODE, val, VP_MODE_FMT_MASK); /* setting size of input image */ - vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(full_width) | - VP_IMG_VSIZE(full_height)); + vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(win_data->fb_width) | + VP_IMG_VSIZE(win_data->fb_height)); /* chroma height has to reduced by 2 to avoid chroma distorions */ - vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(full_width) | - VP_IMG_VSIZE(full_height / 2)); + vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(win_data->fb_width) | + VP_IMG_VSIZE(win_data->fb_height / 2)); - vp_reg_write(res, VP_SRC_WIDTH, width); - vp_reg_write(res, VP_SRC_HEIGHT, height); + vp_reg_write(res, VP_SRC_WIDTH, win_data->src_width); + vp_reg_write(res, VP_SRC_HEIGHT, win_data->src_height); vp_reg_write(res, VP_SRC_H_POSITION, - VP_SRC_H_POSITION_VAL(src_x_offset)); - vp_reg_write(res, VP_SRC_V_POSITION, src_y_offset); + VP_SRC_H_POSITION_VAL(win_data->fb_x)); + vp_reg_write(res, VP_SRC_V_POSITION, win_data->fb_y); - vp_reg_write(res, VP_DST_WIDTH, width); - vp_reg_write(res, VP_DST_H_POSITION, dst_x_offset); + vp_reg_write(res, VP_DST_WIDTH, win_data->crtc_width); + vp_reg_write(res, VP_DST_H_POSITION, win_data->crtc_x); if (ctx->interlace) { - vp_reg_write(res, VP_DST_HEIGHT, height / 2); - vp_reg_write(res, VP_DST_V_POSITION, dst_y_offset / 2); + vp_reg_write(res, VP_DST_HEIGHT, win_data->crtc_height / 2); + vp_reg_write(res, VP_DST_V_POSITION, win_data->crtc_y / 2); } else { - vp_reg_write(res, VP_DST_HEIGHT, height); - vp_reg_write(res, VP_DST_V_POSITION, dst_y_offset); + vp_reg_write(res, VP_DST_HEIGHT, win_data->crtc_height); + vp_reg_write(res, VP_DST_V_POSITION, win_data->crtc_y); } vp_reg_write(res, VP_H_RATIO, x_ratio); @@ -468,8 +454,8 @@ static void vp_video_buffer(struct mixer_context *ctx, int win) vp_reg_write(res, VP_TOP_C_PTR, chroma_addr[0]); vp_reg_write(res, VP_BOT_C_PTR, chroma_addr[1]); - mixer_cfg_scan(ctx, mode_height); - mixer_cfg_rgb_fmt(ctx, mode_height); + mixer_cfg_scan(ctx, win_data->mode_height); + mixer_cfg_rgb_fmt(ctx, win_data->mode_height); mixer_cfg_layer(ctx, win, true); mixer_run(ctx); @@ -484,10 +470,8 @@ static void mixer_graph_buffer(struct mixer_context *ctx, int win) struct mixer_resources *res = &ctx->mixer_res; unsigned long flags; struct hdmi_win_data *win_data; - unsigned int full_width, width, height; unsigned int x_ratio, y_ratio; unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset; - unsigned int mode_width, mode_height; dma_addr_t dma_addr; unsigned int fmt; u32 val; @@ -510,26 +494,17 @@ static void mixer_graph_buffer(struct mixer_context *ctx, int win) fmt = ARGB8888; } - dma_addr = win_data->dma_addr; - full_width = win_data->fb_width; - width = win_data->crtc_width; - height = win_data->crtc_height; - mode_width = win_data->mode_width; - mode_height = win_data->mode_height; - /* 2x scaling feature */ x_ratio = 0; y_ratio = 0; - src_x_offset = win_data->fb_x; - src_y_offset = win_data->fb_y; dst_x_offset = win_data->crtc_x; dst_y_offset = win_data->crtc_y; /* converting dma address base and source offset */ - dma_addr = dma_addr - + (src_x_offset * win_data->bpp >> 3) - + (src_y_offset * full_width * win_data->bpp >> 3); + dma_addr = win_data->dma_addr + + (win_data->fb_x * win_data->bpp >> 3) + + (win_data->fb_y * win_data->fb_width * win_data->bpp >> 3); src_x_offset = 0; src_y_offset = 0; @@ -546,10 +521,10 @@ static void mixer_graph_buffer(struct mixer_context *ctx, int win) MXR_GRP_CFG_FORMAT_VAL(fmt), MXR_GRP_CFG_FORMAT_MASK); /* setup geometry */ - mixer_reg_write(res, MXR_GRAPHIC_SPAN(win), full_width); + mixer_reg_write(res, MXR_GRAPHIC_SPAN(win), win_data->fb_width); - val = MXR_GRP_WH_WIDTH(width); - val |= MXR_GRP_WH_HEIGHT(height); + val = MXR_GRP_WH_WIDTH(win_data->crtc_width); + val |= MXR_GRP_WH_HEIGHT(win_data->crtc_height); val |= MXR_GRP_WH_H_SCALE(x_ratio); val |= MXR_GRP_WH_V_SCALE(y_ratio); mixer_reg_write(res, MXR_GRAPHIC_WH(win), val); @@ -567,8 +542,8 @@ static void mixer_graph_buffer(struct mixer_context *ctx, int win) /* set buffer address to mixer */ mixer_reg_write(res, MXR_GRAPHIC_BASE(win), dma_addr); - mixer_cfg_scan(ctx, mode_height); - mixer_cfg_rgb_fmt(ctx, mode_height); + mixer_cfg_scan(ctx, win_data->mode_height); + mixer_cfg_rgb_fmt(ctx, win_data->mode_height); mixer_cfg_layer(ctx, win, true); mixer_run(ctx); @@ -591,6 +566,116 @@ static void vp_win_reset(struct mixer_context *ctx) WARN(tries == 0, "failed to reset Video Processor\n"); } +static void mixer_win_reset(struct mixer_context *ctx) +{ + struct mixer_resources *res = &ctx->mixer_res; + unsigned long flags; + u32 val; /* value stored to register */ + + spin_lock_irqsave(&res->reg_slock, flags); + mixer_vsync_set_update(ctx, false); + + mixer_reg_writemask(res, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK); + + /* set output in RGB888 mode */ + mixer_reg_writemask(res, MXR_CFG, MXR_CFG_OUT_RGB888, MXR_CFG_OUT_MASK); + + /* 16 beat burst in DMA */ + mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST, + MXR_STATUS_BURST_MASK); + + /* setting default layer priority: layer1 > layer0 > video + * because typical usage scenario would be + * layer1 - OSD + * layer0 - framebuffer + * video - video overlay + */ + val = MXR_LAYER_CFG_GRP1_VAL(3); + val |= MXR_LAYER_CFG_GRP0_VAL(2); + val |= MXR_LAYER_CFG_VP_VAL(1); + mixer_reg_write(res, MXR_LAYER_CFG, val); + + /* setting background color */ + mixer_reg_write(res, MXR_BG_COLOR0, 0x008080); + mixer_reg_write(res, MXR_BG_COLOR1, 0x008080); + mixer_reg_write(res, MXR_BG_COLOR2, 0x008080); + + /* setting graphical layers */ + + val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */ + val |= MXR_GRP_CFG_WIN_BLEND_EN; + val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */ + + /* the same configuration for both layers */ + mixer_reg_write(res, MXR_GRAPHIC_CFG(0), val); + + val |= MXR_GRP_CFG_BLEND_PRE_MUL; + val |= MXR_GRP_CFG_PIXEL_BLEND_EN; + mixer_reg_write(res, MXR_GRAPHIC_CFG(1), val); + + /* configuration of Video Processor Registers */ + vp_win_reset(ctx); + vp_default_filter(res); + + /* disable all layers */ + mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP0_ENABLE); + mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP1_ENABLE); + mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE); + + mixer_vsync_set_update(ctx, true); + spin_unlock_irqrestore(&res->reg_slock, flags); +} + +static void mixer_poweron(struct mixer_context *ctx) +{ + struct mixer_resources *res = &ctx->mixer_res; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + mutex_lock(&ctx->mixer_mutex); + if (ctx->powered) { + mutex_unlock(&ctx->mixer_mutex); + return; + } + ctx->powered = true; + mutex_unlock(&ctx->mixer_mutex); + + pm_runtime_get_sync(ctx->dev); + + clk_enable(res->mixer); + clk_enable(res->vp); + clk_enable(res->sclk_mixer); + + mixer_reg_write(res, MXR_INT_EN, ctx->int_en); + mixer_win_reset(ctx); +} + +static void mixer_poweroff(struct mixer_context *ctx) +{ + struct mixer_resources *res = &ctx->mixer_res; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + mutex_lock(&ctx->mixer_mutex); + if (!ctx->powered) + goto out; + mutex_unlock(&ctx->mixer_mutex); + + ctx->int_en = mixer_reg_read(res, MXR_INT_EN); + + clk_disable(res->mixer); + clk_disable(res->vp); + clk_disable(res->sclk_mixer); + + pm_runtime_put_sync(ctx->dev); + + mutex_lock(&ctx->mixer_mutex); + ctx->powered = false; + +out: + mutex_unlock(&ctx->mixer_mutex); +} + static int mixer_enable_vblank(void *ctx, int pipe) { struct mixer_context *mixer_ctx = ctx; @@ -618,6 +703,27 @@ static void mixer_disable_vblank(void *ctx) mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC); } +static void mixer_dpms(void *ctx, int mode) +{ + struct mixer_context *mixer_ctx = ctx; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + switch (mode) { + case DRM_MODE_DPMS_ON: + mixer_poweron(mixer_ctx); + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + mixer_poweroff(mixer_ctx); + break; + default: + DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode); + break; + } +} + static void mixer_win_mode_set(void *ctx, struct exynos_drm_overlay *overlay) { @@ -643,7 +749,7 @@ static void mixer_win_mode_set(void *ctx, win = MIXER_DEFAULT_WIN; if (win < 0 || win > MIXER_WIN_NR) { - DRM_ERROR("overlay plane[%d] is wrong\n", win); + DRM_ERROR("mixer window[%d] is wrong\n", win); return; } @@ -665,6 +771,8 @@ static void mixer_win_mode_set(void *ctx, win_data->fb_y = overlay->fb_y; win_data->fb_width = overlay->fb_width; win_data->fb_height = overlay->fb_height; + win_data->src_width = overlay->src_width; + win_data->src_height = overlay->src_height; win_data->mode_width = overlay->mode_width; win_data->mode_height = overlay->mode_height; @@ -672,44 +780,26 @@ static void mixer_win_mode_set(void *ctx, win_data->scan_flags = overlay->scan_flag; } -static void mixer_win_commit(void *ctx, int zpos) +static void mixer_win_commit(void *ctx, int win) { struct mixer_context *mixer_ctx = ctx; - int win = zpos; DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win); - if (win == DEFAULT_ZPOS) - win = MIXER_DEFAULT_WIN; - - if (win < 0 || win > MIXER_WIN_NR) { - DRM_ERROR("overlay plane[%d] is wrong\n", win); - return; - } - if (win > 1) vp_video_buffer(mixer_ctx, win); else mixer_graph_buffer(mixer_ctx, win); } -static void mixer_win_disable(void *ctx, int zpos) +static void mixer_win_disable(void *ctx, int win) { struct mixer_context *mixer_ctx = ctx; struct mixer_resources *res = &mixer_ctx->mixer_res; unsigned long flags; - int win = zpos; DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win); - if (win == DEFAULT_ZPOS) - win = MIXER_DEFAULT_WIN; - - if (win < 0 || win > MIXER_WIN_NR) { - DRM_ERROR("overlay plane[%d] is wrong\n", win); - return; - } - spin_lock_irqsave(&res->reg_slock, flags); mixer_vsync_set_update(mixer_ctx, false); @@ -723,6 +813,7 @@ static struct exynos_mixer_ops mixer_ops = { /* manager */ .enable_vblank = mixer_enable_vblank, .disable_vblank = mixer_disable_vblank, + .dpms = mixer_dpms, /* overlay */ .win_mode_set = mixer_win_mode_set, @@ -773,7 +864,7 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg) struct exynos_drm_hdmi_context *drm_hdmi_ctx = arg; struct mixer_context *ctx = drm_hdmi_ctx->ctx; struct mixer_resources *res = &ctx->mixer_res; - u32 val, val_base; + u32 val, base, shadow; spin_lock(&res->reg_slock); @@ -784,12 +875,14 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg) if (val & MXR_INT_STATUS_VSYNC) { /* interlace scan need to check shadow register */ if (ctx->interlace) { - val_base = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0)); - if (ctx->win_data[0].dma_addr != val_base) + base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0)); + shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0)); + if (base != shadow) goto out; - val_base = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(1)); - if (ctx->win_data[1].dma_addr != val_base) + base = mixer_reg_read(res, MXR_GRAPHIC_BASE(1)); + shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(1)); + if (base != shadow) goto out; } @@ -811,117 +904,6 @@ out: return IRQ_HANDLED; } -static void mixer_win_reset(struct mixer_context *ctx) -{ - struct mixer_resources *res = &ctx->mixer_res; - unsigned long flags; - u32 val; /* value stored to register */ - - spin_lock_irqsave(&res->reg_slock, flags); - mixer_vsync_set_update(ctx, false); - - mixer_reg_writemask(res, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK); - - /* set output in RGB888 mode */ - mixer_reg_writemask(res, MXR_CFG, MXR_CFG_OUT_RGB888, MXR_CFG_OUT_MASK); - - /* 16 beat burst in DMA */ - mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST, - MXR_STATUS_BURST_MASK); - - /* setting default layer priority: layer1 > layer0 > video - * because typical usage scenario would be - * layer1 - OSD - * layer0 - framebuffer - * video - video overlay - */ - val = MXR_LAYER_CFG_GRP1_VAL(3); - val |= MXR_LAYER_CFG_GRP0_VAL(2); - val |= MXR_LAYER_CFG_VP_VAL(1); - mixer_reg_write(res, MXR_LAYER_CFG, val); - - /* setting background color */ - mixer_reg_write(res, MXR_BG_COLOR0, 0x008080); - mixer_reg_write(res, MXR_BG_COLOR1, 0x008080); - mixer_reg_write(res, MXR_BG_COLOR2, 0x008080); - - /* setting graphical layers */ - - val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */ - val |= MXR_GRP_CFG_WIN_BLEND_EN; - val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */ - - /* the same configuration for both layers */ - mixer_reg_write(res, MXR_GRAPHIC_CFG(0), val); - - val |= MXR_GRP_CFG_BLEND_PRE_MUL; - val |= MXR_GRP_CFG_PIXEL_BLEND_EN; - mixer_reg_write(res, MXR_GRAPHIC_CFG(1), val); - - /* configuration of Video Processor Registers */ - vp_win_reset(ctx); - vp_default_filter(res); - - /* disable all layers */ - mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP0_ENABLE); - mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP1_ENABLE); - mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE); - - mixer_vsync_set_update(ctx, true); - spin_unlock_irqrestore(&res->reg_slock, flags); -} - -static void mixer_resource_poweron(struct mixer_context *ctx) -{ - struct mixer_resources *res = &ctx->mixer_res; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - clk_enable(res->mixer); - clk_enable(res->vp); - clk_enable(res->sclk_mixer); - - mixer_win_reset(ctx); -} - -static void mixer_resource_poweroff(struct mixer_context *ctx) -{ - struct mixer_resources *res = &ctx->mixer_res; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - clk_disable(res->mixer); - clk_disable(res->vp); - clk_disable(res->sclk_mixer); -} - -static int mixer_runtime_resume(struct device *dev) -{ - struct exynos_drm_hdmi_context *ctx = get_mixer_context(dev); - - DRM_DEBUG_KMS("resume - start\n"); - - mixer_resource_poweron(ctx->ctx); - - return 0; -} - -static int mixer_runtime_suspend(struct device *dev) -{ - struct exynos_drm_hdmi_context *ctx = get_mixer_context(dev); - - DRM_DEBUG_KMS("suspend - start\n"); - - mixer_resource_poweroff(ctx->ctx); - - return 0; -} - -static const struct dev_pm_ops mixer_pm_ops = { - .runtime_suspend = mixer_runtime_suspend, - .runtime_resume = mixer_runtime_resume, -}; - static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx, struct platform_device *pdev) { @@ -931,7 +913,6 @@ static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx, struct resource *res; int ret; - mixer_res->dev = dev; spin_lock_init(&mixer_res->reg_slock); mixer_res->mixer = clk_get(dev, "mixer"); @@ -1027,7 +1008,6 @@ fail: clk_put(mixer_res->vp); if (!IS_ERR_OR_NULL(mixer_res->mixer)) clk_put(mixer_res->mixer); - mixer_res->dev = NULL; return ret; } @@ -1035,7 +1015,6 @@ static void mixer_resources_cleanup(struct mixer_context *ctx) { struct mixer_resources *res = &ctx->mixer_res; - disable_irq(res->irq); free_irq(res->irq, ctx); iounmap(res->vp_regs); @@ -1064,6 +1043,9 @@ static int __devinit mixer_probe(struct platform_device *pdev) return -ENOMEM; } + mutex_init(&ctx->mixer_mutex); + + ctx->dev = &pdev->dev; drm_hdmi_ctx->ctx = (void *)ctx; platform_set_drvdata(pdev, drm_hdmi_ctx); @@ -1076,7 +1058,7 @@ static int __devinit mixer_probe(struct platform_device *pdev) /* register specific callback point to common hdmi. */ exynos_mixer_ops_register(&mixer_ops); - mixer_resource_poweron(ctx); + pm_runtime_enable(dev); return 0; @@ -1095,12 +1077,27 @@ static int mixer_remove(struct platform_device *pdev) dev_info(dev, "remove successful\n"); - mixer_resource_poweroff(ctx); + pm_runtime_disable(&pdev->dev); + mixer_resources_cleanup(ctx); return 0; } +#ifdef CONFIG_PM_SLEEP +static int mixer_suspend(struct device *dev) +{ + struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev); + struct mixer_context *ctx = drm_hdmi_ctx->ctx; + + mixer_poweroff(ctx); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(mixer_pm_ops, mixer_suspend, NULL); + struct platform_driver mixer_driver = { .driver = { .name = "s5p-mixer", diff --git a/drivers/gpu/drm/exynos/regs-hdmi.h b/drivers/gpu/drm/exynos/regs-hdmi.h index 3c04bea842c..9cc7c5e9718 100644 --- a/drivers/gpu/drm/exynos/regs-hdmi.h +++ b/drivers/gpu/drm/exynos/regs-hdmi.h @@ -138,14 +138,16 @@ #define HDMI_ASP_MASK (1 << 2) #define HDMI_EN (1 << 0) +/* HDMI_CON_2 */ +#define HDMI_VID_PREAMBLE_DIS (1 << 5) +#define HDMI_GUARD_BAND_DIS (1 << 1) + /* HDMI_PHY_STATUS */ #define HDMI_PHY_STATUS_READY (1 << 0) /* HDMI_MODE_SEL */ #define HDMI_MODE_HDMI_EN (1 << 1) #define HDMI_MODE_DVI_EN (1 << 0) -#define HDMI_DVI_MODE_EN (1) -#define HDMI_DVI_MODE_DIS (0) #define HDMI_MODE_MASK (3 << 0) /* HDMI_TG_CMD */ diff --git a/drivers/gpu/drm/gma500/cdv_device.c b/drivers/gpu/drm/gma500/cdv_device.c index 8e393303f53..9764045428c 100644 --- a/drivers/gpu/drm/gma500/cdv_device.c +++ b/drivers/gpu/drm/gma500/cdv_device.c @@ -563,6 +563,7 @@ const struct psb_ops cdv_chip_ops = { .crtcs = 2, .hdmi_mask = (1 << 0) | (1 << 1), .lvds_mask = (1 << 1), + .cursor_needs_phys = 0, .sgx_offset = MRST_SGX_OFFSET, .chip_setup = cdv_chip_setup, .errata = cdv_errata, diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index 8d77224afc3..5732b5702e1 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -153,7 +153,7 @@ static void psbfb_vm_close(struct vm_area_struct *vma) { } -static struct vm_operations_struct psbfb_vm_ops = { +static const struct vm_operations_struct psbfb_vm_ops = { .fault = psbfb_vm_fault, .open = psbfb_vm_open, .close = psbfb_vm_close @@ -782,7 +782,7 @@ void psb_modeset_init(struct drm_device *dev) dev->mode_config.min_width = 0; dev->mode_config.min_height = 0; - dev->mode_config.funcs = (void *) &psb_mode_funcs; + dev->mode_config.funcs = &psb_mode_funcs; /* set memory base */ /* Oaktrail and Poulsbo should use BAR 2*/ diff --git a/drivers/gpu/drm/gma500/mdfld_device.c b/drivers/gpu/drm/gma500/mdfld_device.c index 2d8e741e06d..265ad0de44a 100644 --- a/drivers/gpu/drm/gma500/mdfld_device.c +++ b/drivers/gpu/drm/gma500/mdfld_device.c @@ -531,6 +531,7 @@ const struct psb_ops mdfld_chip_ops = { .crtcs = 3, .lvds_mask = (1 << 1), .hdmi_mask = (1 << 1), + .cursor_needs_phys = 0, .sgx_offset = MRST_SGX_OFFSET, .chip_setup = mdfld_chip_setup, diff --git a/drivers/gpu/drm/gma500/oaktrail_device.c b/drivers/gpu/drm/gma500/oaktrail_device.c index 7a8ff8e2dfc..0f9b7db80f6 100644 --- a/drivers/gpu/drm/gma500/oaktrail_device.c +++ b/drivers/gpu/drm/gma500/oaktrail_device.c @@ -544,6 +544,7 @@ const struct psb_ops oaktrail_chip_ops = { .crtcs = 2, .hdmi_mask = (1 << 0), .lvds_mask = (1 << 0), + .cursor_needs_phys = 0, .sgx_offset = MRST_SGX_OFFSET, .chip_setup = oaktrail_chip_setup, diff --git a/drivers/gpu/drm/gma500/psb_device.c b/drivers/gpu/drm/gma500/psb_device.c index 1726c04dedd..eff039bf92d 100644 --- a/drivers/gpu/drm/gma500/psb_device.c +++ b/drivers/gpu/drm/gma500/psb_device.c @@ -197,7 +197,8 @@ static int psb_save_display_registers(struct drm_device *dev) } list_for_each_entry(connector, &dev->mode_config.connector_list, head) - connector->funcs->save(connector); + if (connector->funcs->save) + connector->funcs->save(connector); mutex_unlock(&dev->mode_config.mutex); return 0; @@ -235,7 +236,8 @@ static int psb_restore_display_registers(struct drm_device *dev) crtc->funcs->restore(crtc); list_for_each_entry(connector, &dev->mode_config.connector_list, head) - connector->funcs->restore(connector); + if (connector->funcs->restore) + connector->funcs->restore(connector); mutex_unlock(&dev->mode_config.mutex); return 0; @@ -373,6 +375,7 @@ const struct psb_ops psb_chip_ops = { .crtcs = 2, .hdmi_mask = (1 << 0), .lvds_mask = (1 << 1), + .cursor_needs_phys = 1, .sgx_offset = PSB_SGX_OFFSET, .chip_setup = psb_chip_setup, .chip_teardown = psb_chip_teardown, diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index 0c995ba0f2e..caba6e08693 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -595,7 +595,7 @@ static const struct dev_pm_ops psb_pm_ops = { .runtime_idle = psb_runtime_idle, }; -static struct vm_operations_struct psb_gem_vm_ops = { +static const struct vm_operations_struct psb_gem_vm_ops = { .fault = psb_gem_fault, .open = drm_gem_vm_open, .close = drm_gem_vm_close, diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h index a1b0c0bce97..1bd115ecefe 100644 --- a/drivers/gpu/drm/gma500/psb_drv.h +++ b/drivers/gpu/drm/gma500/psb_drv.h @@ -655,6 +655,7 @@ struct psb_ops { int sgx_offset; /* Base offset of SGX device */ int hdmi_mask; /* Mask of HDMI CRTCs */ int lvds_mask; /* Mask of LVDS CRTCs */ + int cursor_needs_phys; /* If cursor base reg need physical address */ /* Sub functions */ struct drm_crtc_helper_funcs const *crtc_helper; diff --git a/drivers/gpu/drm/gma500/psb_intel_display.c b/drivers/gpu/drm/gma500/psb_intel_display.c index f3a3160aafd..36c3c99612f 100644 --- a/drivers/gpu/drm/gma500/psb_intel_display.c +++ b/drivers/gpu/drm/gma500/psb_intel_display.c @@ -928,6 +928,7 @@ static int psb_intel_crtc_cursor_set(struct drm_crtc *crtc, uint32_t width, uint32_t height) { struct drm_device *dev = crtc->dev; + struct drm_psb_private *dev_priv = dev->dev_private; struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); int pipe = psb_intel_crtc->pipe; uint32_t control = (pipe == 0) ? CURACNTR : CURBCNTR; @@ -935,8 +936,10 @@ static int psb_intel_crtc_cursor_set(struct drm_crtc *crtc, uint32_t temp; size_t addr = 0; struct gtt_range *gt; + struct gtt_range *cursor_gt = psb_intel_crtc->cursor_gt; struct drm_gem_object *obj; - int ret; + void *tmp_dst, *tmp_src; + int ret, i, cursor_pages; /* if we want to turn of the cursor ignore width and height */ if (!handle) { @@ -985,10 +988,32 @@ static int psb_intel_crtc_cursor_set(struct drm_crtc *crtc, return ret; } + if (dev_priv->ops->cursor_needs_phys) { + if (cursor_gt == NULL) { + dev_err(dev->dev, "No hardware cursor mem available"); + return -ENOMEM; + } - addr = gt->offset; /* Or resource.start ??? */ + /* Prevent overflow */ + if (gt->npage > 4) + cursor_pages = 4; + else + cursor_pages = gt->npage; + + /* Copy the cursor to cursor mem */ + tmp_dst = dev_priv->vram_addr + cursor_gt->offset; + for (i = 0; i < cursor_pages; i++) { + tmp_src = kmap(gt->pages[i]); + memcpy(tmp_dst, tmp_src, PAGE_SIZE); + kunmap(gt->pages[i]); + tmp_dst += PAGE_SIZE; + } - psb_intel_crtc->cursor_addr = addr; + addr = psb_intel_crtc->cursor_addr; + } else { + addr = gt->offset; /* Or resource.start ??? */ + psb_intel_crtc->cursor_addr = addr; + } temp = 0; /* set the pipe for the cursor */ @@ -1213,6 +1238,9 @@ void psb_intel_crtc_destroy(struct drm_crtc *crtc) drm_gem_object_unreference(psb_intel_crtc->cursor_obj); psb_intel_crtc->cursor_obj = NULL; } + + if (psb_intel_crtc->cursor_gt != NULL) + psb_gtt_free_range(crtc->dev, psb_intel_crtc->cursor_gt); kfree(psb_intel_crtc->crtc_state); drm_crtc_cleanup(crtc); kfree(psb_intel_crtc); @@ -1241,13 +1269,33 @@ const struct drm_crtc_funcs psb_intel_crtc_funcs = { * Set the default value of cursor control and base register * to zero. This is a workaround for h/w defect on Oaktrail */ -static void psb_intel_cursor_init(struct drm_device *dev, int pipe) +static void psb_intel_cursor_init(struct drm_device *dev, + struct psb_intel_crtc *psb_intel_crtc) { + struct drm_psb_private *dev_priv = dev->dev_private; u32 control[3] = { CURACNTR, CURBCNTR, CURCCNTR }; u32 base[3] = { CURABASE, CURBBASE, CURCBASE }; + struct gtt_range *cursor_gt; + + if (dev_priv->ops->cursor_needs_phys) { + /* Allocate 4 pages of stolen mem for a hardware cursor. That + * is enough for the 64 x 64 ARGB cursors we support. + */ + cursor_gt = psb_gtt_alloc_range(dev, 4 * PAGE_SIZE, "cursor", 1); + if (!cursor_gt) { + psb_intel_crtc->cursor_gt = NULL; + goto out; + } + psb_intel_crtc->cursor_gt = cursor_gt; + psb_intel_crtc->cursor_addr = dev_priv->stolen_base + + cursor_gt->offset; + } else { + psb_intel_crtc->cursor_gt = NULL; + } - REG_WRITE(control[pipe], 0); - REG_WRITE(base[pipe], 0); +out: + REG_WRITE(control[psb_intel_crtc->pipe], 0); + REG_WRITE(base[psb_intel_crtc->pipe], 0); } void psb_intel_crtc_init(struct drm_device *dev, int pipe, @@ -1313,7 +1361,7 @@ void psb_intel_crtc_init(struct drm_device *dev, int pipe, psb_intel_crtc->mode_set.connectors = (struct drm_connector **) (psb_intel_crtc + 1); psb_intel_crtc->mode_set.num_connectors = 0; - psb_intel_cursor_init(dev, pipe); + psb_intel_cursor_init(dev, psb_intel_crtc); } int psb_intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, diff --git a/drivers/gpu/drm/gma500/psb_intel_drv.h b/drivers/gpu/drm/gma500/psb_intel_drv.h index 81852b48654..2515f83248c 100644 --- a/drivers/gpu/drm/gma500/psb_intel_drv.h +++ b/drivers/gpu/drm/gma500/psb_intel_drv.h @@ -106,11 +106,6 @@ struct psb_intel_mode_device { size_t(*bo_offset) (struct drm_device *dev, void *bo); /* - * Cursor (Can go ?) - */ - int cursor_needs_physical; - - /* * LVDS info */ int backlight_duty_cycle; /* restore backlight to this value */ @@ -176,6 +171,7 @@ struct psb_intel_crtc { int pipe; int plane; uint32_t cursor_addr; + struct gtt_range *cursor_gt; u8 lut_r[256], lut_g[256], lut_b[256]; u8 lut_adj[256]; struct psb_intel_framebuffer *fbdev_fb; diff --git a/drivers/gpu/drm/gma500/psb_lid.c b/drivers/gpu/drm/gma500/psb_lid.c index 7ff8bb2bdc2..1d2ebb5e530 100644 --- a/drivers/gpu/drm/gma500/psb_lid.c +++ b/drivers/gpu/drm/gma500/psb_lid.c @@ -40,10 +40,16 @@ static void psb_lid_timer_func(unsigned long data) REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) | POWER_TARGET_ON); do { pp_status = REG_READ(PP_STATUS); - } while ((pp_status & PP_ON) == 0); + } while ((pp_status & PP_ON) == 0 && + (pp_status & PP_SEQUENCE_MASK) != 0); - /*FIXME: should be backlight level before*/ - psb_intel_lvds_set_brightness(dev, 100); + if (REG_READ(PP_STATUS) & PP_ON) { + /*FIXME: should be backlight level before*/ + psb_intel_lvds_set_brightness(dev, 100); + } else { + DRM_DEBUG("LVDS panel never powered up"); + return; + } } else { psb_intel_lvds_set_brightness(dev, 0); diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 0ca7f7646ab..2e9268da58d 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -38,7 +38,8 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \ dvo_ch7017.o \ dvo_ivch.o \ dvo_tfp410.o \ - dvo_sil164.o + dvo_sil164.o \ + i915_gem_dmabuf.o i915-$(CONFIG_COMPAT) += i915_ioc32.o diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index d3e19485306..238a5216583 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -1012,7 +1012,7 @@ static const struct dev_pm_ops i915_pm_ops = { .restore = i915_pm_resume, }; -static struct vm_operations_struct i915_gem_vm_ops = { +static const struct vm_operations_struct i915_gem_vm_ops = { .fault = i915_gem_fault, .open = drm_gem_vm_open, .close = drm_gem_vm_close, @@ -1039,7 +1039,7 @@ static struct drm_driver driver = { */ .driver_features = DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | /* DRIVER_USE_MTRR |*/ - DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM, + DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM | DRIVER_PRIME, .load = i915_driver_load, .unload = i915_driver_unload, .open = i915_driver_open, @@ -1062,6 +1062,12 @@ static struct drm_driver driver = { .gem_init_object = i915_gem_init_object, .gem_free_object = i915_gem_free_object, .gem_vm_ops = &i915_gem_vm_ops, + + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_export = i915_gem_prime_export, + .gem_prime_import = i915_gem_prime_import, + .dumb_create = i915_gem_dumb_create, .dumb_map_offset = i915_gem_mmap_gtt, .dumb_destroy = i915_gem_dumb_destroy, diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 11c7a6a330c..377c21f531e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -940,6 +940,8 @@ struct drm_i915_gem_object { struct scatterlist *sg_list; int num_sg; + /* prime dma-buf support */ + struct sg_table *sg_table; /** * Used for performing relocations during execbuffer insertion. */ @@ -1245,6 +1247,8 @@ int __must_check i915_gem_object_unbind(struct drm_i915_gem_object *obj); void i915_gem_release_mmap(struct drm_i915_gem_object *obj); void i915_gem_lastclose(struct drm_device *dev); +int i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj, + gfp_t gfpmask); int __must_check i915_mutex_lock_interruptible(struct drm_device *dev); int __must_check i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj); int i915_gem_object_sync(struct drm_i915_gem_object *obj, @@ -1342,6 +1346,13 @@ i915_gem_get_unfenced_gtt_alignment(struct drm_device *dev, int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, enum i915_cache_level cache_level); +struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf); + +struct dma_buf *i915_gem_prime_export(struct drm_device *dev, + struct drm_gem_object *gem_obj, int flags); + + /* i915_gem_gtt.c */ int __must_check i915_gem_init_aliasing_ppgtt(struct drm_device *dev); void i915_gem_cleanup_aliasing_ppgtt(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 465e775b8cd..288d7b8f49a 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -35,6 +35,7 @@ #include <linux/slab.h> #include <linux/swap.h> #include <linux/pci.h> +#include <linux/dma-buf.h> static __must_check int i915_gem_object_flush_gpu_write_domain(struct drm_i915_gem_object *obj); static void i915_gem_object_flush_gtt_write_domain(struct drm_i915_gem_object *obj); @@ -538,6 +539,14 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data, goto out; } + /* prime objects have no backing filp to GEM pread/pwrite + * pages from. + */ + if (!obj->base.filp) { + ret = -EINVAL; + goto out; + } + trace_i915_gem_object_pread(obj, args->offset, args->size); ret = i915_gem_shmem_pread(dev, obj, args, file); @@ -880,6 +889,14 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, goto out; } + /* prime objects have no backing filp to GEM pread/pwrite + * pages from. + */ + if (!obj->base.filp) { + ret = -EINVAL; + goto out; + } + trace_i915_gem_object_pwrite(obj, args->offset, args->size); ret = -EFAULT; @@ -1021,6 +1038,14 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data, if (obj == NULL) return -ENOENT; + /* prime objects have no backing filp to GEM mmap + * pages from. + */ + if (!obj->filp) { + drm_gem_object_unreference_unlocked(obj); + return -EINVAL; + } + addr = vm_mmap(obj->filp, 0, args->size, PROT_READ | PROT_WRITE, MAP_SHARED, args->offset); @@ -1302,8 +1327,7 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data, return i915_gem_mmap_gtt(file, dev, args->handle, &args->offset); } - -static int +int i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj, gfp_t gfpmask) { @@ -1312,6 +1336,9 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj, struct inode *inode; struct page *page; + if (obj->pages || obj->sg_table) + return 0; + /* Get the list of pages out of our struct file. They'll be pinned * at this point until we release them. */ @@ -1353,6 +1380,9 @@ i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj) int page_count = obj->base.size / PAGE_SIZE; int i; + if (!obj->pages) + return; + BUG_ON(obj->madv == __I915_MADV_PURGED); if (i915_gem_object_needs_bit17_swizzle(obj)) @@ -3333,6 +3363,9 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj) trace_i915_gem_object_destroy(obj); + if (gem_obj->import_attach) + drm_prime_gem_destroy(gem_obj, obj->sg_table); + if (obj->phys_obj) i915_gem_detach_phys_object(dev, obj); diff --git a/drivers/gpu/drm/i915/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/i915_gem_dmabuf.c new file mode 100644 index 00000000000..8e269178d6a --- /dev/null +++ b/drivers/gpu/drm/i915/i915_gem_dmabuf.c @@ -0,0 +1,171 @@ +/* + * Copyright 2012 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 (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Dave Airlie <airlied@redhat.com> + */ +#include "drmP.h" +#include "i915_drv.h" +#include <linux/dma-buf.h> + +static struct sg_table *i915_gem_map_dma_buf(struct dma_buf_attachment *attachment, + enum dma_data_direction dir) +{ + struct drm_i915_gem_object *obj = attachment->dmabuf->priv; + struct drm_device *dev = obj->base.dev; + int npages = obj->base.size / PAGE_SIZE; + struct sg_table *sg = NULL; + int ret; + int nents; + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + return ERR_PTR(ret); + + if (!obj->pages) { + ret = i915_gem_object_get_pages_gtt(obj, __GFP_NORETRY | __GFP_NOWARN); + if (ret) + goto out; + } + + /* link the pages into an SG then map the sg */ + sg = drm_prime_pages_to_sg(obj->pages, npages); + nents = dma_map_sg(attachment->dev, sg->sgl, sg->nents, dir); +out: + mutex_unlock(&dev->struct_mutex); + return sg; +} + +static void i915_gem_unmap_dma_buf(struct dma_buf_attachment *attachment, + struct sg_table *sg, enum dma_data_direction dir) +{ + dma_unmap_sg(attachment->dev, sg->sgl, sg->nents, dir); + sg_free_table(sg); + kfree(sg); +} + +static void i915_gem_dmabuf_release(struct dma_buf *dma_buf) +{ + struct drm_i915_gem_object *obj = dma_buf->priv; + + if (obj->base.export_dma_buf == dma_buf) { + /* drop the reference on the export fd holds */ + obj->base.export_dma_buf = NULL; + drm_gem_object_unreference_unlocked(&obj->base); + } +} + +static void *i915_gem_dmabuf_kmap_atomic(struct dma_buf *dma_buf, unsigned long page_num) +{ + return NULL; +} + +static void i915_gem_dmabuf_kunmap_atomic(struct dma_buf *dma_buf, unsigned long page_num, void *addr) +{ + +} +static void *i915_gem_dmabuf_kmap(struct dma_buf *dma_buf, unsigned long page_num) +{ + return NULL; +} + +static void i915_gem_dmabuf_kunmap(struct dma_buf *dma_buf, unsigned long page_num, void *addr) +{ + +} + +static const struct dma_buf_ops i915_dmabuf_ops = { + .map_dma_buf = i915_gem_map_dma_buf, + .unmap_dma_buf = i915_gem_unmap_dma_buf, + .release = i915_gem_dmabuf_release, + .kmap = i915_gem_dmabuf_kmap, + .kmap_atomic = i915_gem_dmabuf_kmap_atomic, + .kunmap = i915_gem_dmabuf_kunmap, + .kunmap_atomic = i915_gem_dmabuf_kunmap_atomic, +}; + +struct dma_buf *i915_gem_prime_export(struct drm_device *dev, + struct drm_gem_object *gem_obj, int flags) +{ + struct drm_i915_gem_object *obj = to_intel_bo(gem_obj); + + return dma_buf_export(obj, &i915_dmabuf_ops, + obj->base.size, 0600); +} + +struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf) +{ + struct dma_buf_attachment *attach; + struct sg_table *sg; + struct drm_i915_gem_object *obj; + int npages; + int size; + int ret; + + /* is this one of own objects? */ + if (dma_buf->ops == &i915_dmabuf_ops) { + obj = dma_buf->priv; + /* is it from our device? */ + if (obj->base.dev == dev) { + drm_gem_object_reference(&obj->base); + return &obj->base; + } + } + + /* need to attach */ + attach = dma_buf_attach(dma_buf, dev->dev); + if (IS_ERR(attach)) + return ERR_CAST(attach); + + sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); + if (IS_ERR(sg)) { + ret = PTR_ERR(sg); + goto fail_detach; + } + + size = dma_buf->size; + npages = size / PAGE_SIZE; + + obj = kzalloc(sizeof(*obj), GFP_KERNEL); + if (obj == NULL) { + ret = -ENOMEM; + goto fail_unmap; + } + + ret = drm_gem_private_object_init(dev, &obj->base, size); + if (ret) { + kfree(obj); + goto fail_unmap; + } + + obj->sg_table = sg; + obj->base.import_attach = attach; + + return &obj->base; + +fail_unmap: + dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL); +fail_detach: + dma_buf_detach(dma_buf, attach); + return ERR_PTR(ret); +} diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 29d573c27b3..9fd25a43553 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -267,7 +267,13 @@ void i915_ppgtt_bind_object(struct i915_hw_ppgtt *ppgtt, BUG(); } - if (dev_priv->mm.gtt->needs_dmar) { + if (obj->sg_table) { + i915_ppgtt_insert_sg_entries(ppgtt, + obj->sg_table->sgl, + obj->sg_table->nents, + obj->gtt_space->start >> PAGE_SHIFT, + pte_flags); + } else if (dev_priv->mm.gtt->needs_dmar) { BUG_ON(!obj->sg_list); i915_ppgtt_insert_sg_entries(ppgtt, @@ -371,7 +377,12 @@ void i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj, struct drm_i915_private *dev_priv = dev->dev_private; unsigned int agp_type = cache_level_to_agp_type(dev, cache_level); - if (dev_priv->mm.gtt->needs_dmar) { + if (obj->sg_table) { + intel_gtt_insert_sg_entries(obj->sg_table->sgl, + obj->sg_table->nents, + obj->gtt_space->start >> PAGE_SHIFT, + agp_type); + } else if (dev_priv->mm.gtt->needs_dmar) { BUG_ON(!obj->sg_list); intel_gtt_insert_sg_entries(obj->sg_list, diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 67ed819220a..91478942090 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6951,7 +6951,7 @@ void intel_modeset_init(struct drm_device *dev) dev->mode_config.preferred_depth = 24; dev->mode_config.prefer_shadow = 1; - dev->mode_config.funcs = (void *)&intel_mode_funcs; + dev->mode_config.funcs = &intel_mode_funcs; intel_init_quirks(dev); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index eb57ec7b36f..296cfc201a8 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1965,6 +1965,23 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) return false; } +static void +intel_dp_probe_oui(struct intel_dp *intel_dp) +{ + u8 buf[3]; + + if (!(intel_dp->dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_OUI_SUPPORT)) + return; + + if (intel_dp_aux_native_read_retry(intel_dp, DP_SINK_OUI, buf, 3)) + DRM_DEBUG_KMS("Sink OUI: %02hx%02hx%02hx\n", + buf[0], buf[1], buf[2]); + + if (intel_dp_aux_native_read_retry(intel_dp, DP_BRANCH_OUI, buf, 3)) + DRM_DEBUG_KMS("Branch OUI: %02hx%02hx%02hx\n", + buf[0], buf[1], buf[2]); +} + static bool intel_dp_get_sink_irq(struct intel_dp *intel_dp, u8 *sink_irq_vector) { @@ -2148,6 +2165,8 @@ intel_dp_detect(struct drm_connector *connector, bool force) if (status != connector_status_connected) return status; + intel_dp_probe_oui(intel_dp); + if (intel_dp->force_audio != HDMI_AUDIO_AUTO) { intel_dp->has_audio = (intel_dp->force_audio == HDMI_AUDIO_ON); } else { diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 2b2e011e905..2a1625d84a6 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -368,6 +368,7 @@ int intel_panel_setup_backlight(struct drm_device *dev) else return -ENODEV; + memset(&props, 0, sizeof(props)); props.type = BACKLIGHT_RAW; props.max_brightness = intel_panel_get_max_backlight(dev); dev_priv->backlight = diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index a6582079134..b6a9d45fc3c 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1245,8 +1245,14 @@ static bool intel_sdvo_get_capabilities(struct intel_sdvo *intel_sdvo, struct in static int intel_sdvo_supports_hotplug(struct intel_sdvo *intel_sdvo) { + struct drm_device *dev = intel_sdvo->base.base.dev; u8 response[2]; + /* HW Erratum: SDVO Hotplug is broken on all i945G chips, there's noise + * on the line. */ + if (IS_I945G(dev) || IS_I945GM(dev)) + return false; + return intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_HOT_PLUG_SUPPORT, &response, 2) && response[0]; } diff --git a/drivers/gpu/drm/mgag200/mgag200_ttm.c b/drivers/gpu/drm/mgag200/mgag200_ttm.c index 12264793108..b223dcb7a71 100644 --- a/drivers/gpu/drm/mgag200/mgag200_ttm.c +++ b/drivers/gpu/drm/mgag200/mgag200_ttm.c @@ -356,7 +356,7 @@ int mgag200_bo_create(struct drm_device *dev, int size, int align, ret = ttm_bo_init(&mdev->ttm.bdev, &mgabo->bo, size, ttm_bo_type_device, &mgabo->placement, align >> PAGE_SHIFT, 0, false, NULL, acc_size, - mgag200_bo_ttm_destroy); + NULL, mgag200_bo_ttm_destroy); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 1a2ad7eb173..fe5267d06ab 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -16,10 +16,13 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ nv04_mc.o nv40_mc.o nv50_mc.o \ nv04_fb.o nv10_fb.o nv20_fb.o nv30_fb.o nv40_fb.o \ nv50_fb.o nvc0_fb.o \ - nv04_fifo.o nv10_fifo.o nv40_fifo.o nv50_fifo.o nvc0_fifo.o \ + nv04_fifo.o nv10_fifo.o nv17_fifo.o nv40_fifo.o nv50_fifo.o \ + nv84_fifo.o nvc0_fifo.o nve0_fifo.o \ + nv04_fence.o nv10_fence.o nv84_fence.o nvc0_fence.o \ + nv04_software.o nv50_software.o nvc0_software.o \ nv04_graph.o nv10_graph.o nv20_graph.o \ - nv40_graph.o nv50_graph.o nvc0_graph.o \ - nv40_grctx.o nv50_grctx.o nvc0_grctx.o \ + nv40_graph.o nv50_graph.o nvc0_graph.o nve0_graph.o \ + nv40_grctx.o nv50_grctx.o nvc0_grctx.o nve0_grctx.o \ nv84_crypt.o nv98_crypt.o \ nva3_copy.o nvc0_copy.o \ nv31_mpeg.o nv50_mpeg.o \ @@ -37,7 +40,7 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ nv50_calc.o \ nv04_pm.o nv40_pm.o nv50_pm.o nva3_pm.o nvc0_pm.o \ nv50_vram.o nvc0_vram.o \ - nv50_vm.o nvc0_vm.o + nv50_vm.o nvc0_vm.o nouveau_prime.o nouveau-$(CONFIG_DRM_NOUVEAU_DEBUG) += nouveau_debugfs.o nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c index 284bd25d5d2..fc841e87b34 100644 --- a/drivers/gpu/drm/nouveau/nouveau_acpi.c +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c @@ -338,7 +338,8 @@ void nouveau_switcheroo_optimus_dsm(void) void nouveau_unregister_dsm_handler(void) { - vga_switcheroo_unregister_handler(); + if (nouveau_dsm_priv.optimus_detected || nouveau_dsm_priv.dsm_detected) + vga_switcheroo_unregister_handler(); } /* retrieve the ROM in 4k blocks */ diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 0be4a815e70..2f11e16a81a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -30,6 +30,7 @@ #include "nouveau_gpio.h" #include <linux/io-mapping.h> +#include <linux/firmware.h> /* these defines are made up */ #define NV_CIO_CRE_44_HEADA 0x0 @@ -195,35 +196,24 @@ static void bios_shadow_acpi(struct nvbios *bios) { struct pci_dev *pdev = bios->dev->pdev; - int ptr, len, ret; - u8 data[3]; + int cnt = 65536 / ROM_BIOS_PAGE; + int ret; if (!nouveau_acpi_rom_supported(pdev)) return; - ret = nouveau_acpi_get_bios_chunk(data, 0, sizeof(data)); - if (ret != sizeof(data)) - return; - - bios->length = min(data[2] * 512, 65536); - bios->data = kmalloc(bios->length, GFP_KERNEL); + bios->data = kmalloc(cnt * ROM_BIOS_PAGE, GFP_KERNEL); if (!bios->data) return; - len = bios->length; - ptr = 0; - while (len) { - int size = (len > ROM_BIOS_PAGE) ? ROM_BIOS_PAGE : len; - - ret = nouveau_acpi_get_bios_chunk(bios->data, ptr, size); - if (ret != size) { - kfree(bios->data); - bios->data = NULL; + bios->length = 0; + while (cnt--) { + ret = nouveau_acpi_get_bios_chunk(bios->data, bios->length, + ROM_BIOS_PAGE); + if (ret != ROM_BIOS_PAGE) return; - } - len -= size; - ptr += size; + bios->length += ROM_BIOS_PAGE; } } @@ -249,8 +239,12 @@ bios_shadow(struct drm_device *dev) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nvbios *bios = &dev_priv->vbios; struct methods *mthd, *best; + const struct firmware *fw; + char fname[32]; + int ret; if (nouveau_vbios) { + /* try to match one of the built-in methods */ mthd = shadow_methods; do { if (strcasecmp(nouveau_vbios, mthd->desc)) @@ -263,6 +257,22 @@ bios_shadow(struct drm_device *dev) return true; } while ((++mthd)->shadow); + /* attempt to load firmware image */ + snprintf(fname, sizeof(fname), "nouveau/%s", nouveau_vbios); + ret = request_firmware(&fw, fname, &dev->pdev->dev); + if (ret == 0) { + bios->length = fw->size; + bios->data = kmemdup(fw->data, fw->size, GFP_KERNEL); + release_firmware(fw); + + NV_INFO(dev, "VBIOS image: %s\n", nouveau_vbios); + if (score_vbios(bios, 1)) + return true; + + kfree(bios->data); + bios->data = NULL; + } + NV_ERROR(dev, "VBIOS source \'%s\' invalid\n", nouveau_vbios); } @@ -273,6 +283,7 @@ bios_shadow(struct drm_device *dev) mthd->score = score_vbios(bios, mthd->rw); mthd->size = bios->length; mthd->data = bios->data; + bios->data = NULL; } while (mthd->score != 3 && (++mthd)->shadow); mthd = shadow_methods; diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 7d15a774f9c..7f80ed52356 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -35,6 +35,8 @@ #include "nouveau_dma.h" #include "nouveau_mm.h" #include "nouveau_vm.h" +#include "nouveau_fence.h" +#include "nouveau_ramht.h" #include <linux/log2.h> #include <linux/slab.h> @@ -89,12 +91,17 @@ nouveau_bo_fixup_align(struct nouveau_bo *nvbo, u32 flags, int nouveau_bo_new(struct drm_device *dev, int size, int align, uint32_t flags, uint32_t tile_mode, uint32_t tile_flags, + struct sg_table *sg, struct nouveau_bo **pnvbo) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_bo *nvbo; size_t acc_size; int ret; + int type = ttm_bo_type_device; + + if (sg) + type = ttm_bo_type_sg; nvbo = kzalloc(sizeof(struct nouveau_bo), GFP_KERNEL); if (!nvbo) @@ -120,8 +127,8 @@ nouveau_bo_new(struct drm_device *dev, int size, int align, sizeof(struct nouveau_bo)); ret = ttm_bo_init(&dev_priv->ttm.bdev, &nvbo->bo, size, - ttm_bo_type_device, &nvbo->placement, - align >> PAGE_SHIFT, 0, false, NULL, acc_size, + type, &nvbo->placement, + align >> PAGE_SHIFT, 0, false, NULL, acc_size, sg, nouveau_bo_del_ttm); if (ret) { /* ttm will call nouveau_bo_del_ttm if it fails.. */ @@ -473,7 +480,7 @@ nouveau_bo_move_accel_cleanup(struct nouveau_channel *chan, struct nouveau_fence *fence = NULL; int ret; - ret = nouveau_fence_new(chan, &fence, true); + ret = nouveau_fence_new(chan, &fence); if (ret) return ret; @@ -484,6 +491,76 @@ nouveau_bo_move_accel_cleanup(struct nouveau_channel *chan, } static int +nve0_bo_move_copy(struct nouveau_channel *chan, struct ttm_buffer_object *bo, + struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) +{ + struct nouveau_mem *node = old_mem->mm_node; + int ret = RING_SPACE(chan, 10); + if (ret == 0) { + BEGIN_NVC0(chan, NvSubCopy, 0x0400, 8); + OUT_RING (chan, upper_32_bits(node->vma[0].offset)); + OUT_RING (chan, lower_32_bits(node->vma[0].offset)); + OUT_RING (chan, upper_32_bits(node->vma[1].offset)); + OUT_RING (chan, lower_32_bits(node->vma[1].offset)); + OUT_RING (chan, PAGE_SIZE); + OUT_RING (chan, PAGE_SIZE); + OUT_RING (chan, PAGE_SIZE); + OUT_RING (chan, new_mem->num_pages); + BEGIN_IMC0(chan, NvSubCopy, 0x0300, 0x0386); + } + return ret; +} + +static int +nvc0_bo_move_init(struct nouveau_channel *chan, u32 handle) +{ + int ret = RING_SPACE(chan, 2); + if (ret == 0) { + BEGIN_NVC0(chan, NvSubCopy, 0x0000, 1); + OUT_RING (chan, handle); + } + return ret; +} + +static int +nvc0_bo_move_copy(struct nouveau_channel *chan, struct ttm_buffer_object *bo, + struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) +{ + struct nouveau_mem *node = old_mem->mm_node; + u64 src_offset = node->vma[0].offset; + u64 dst_offset = node->vma[1].offset; + u32 page_count = new_mem->num_pages; + int ret; + + page_count = new_mem->num_pages; + while (page_count) { + int line_count = (page_count > 8191) ? 8191 : page_count; + + ret = RING_SPACE(chan, 11); + if (ret) + return ret; + + BEGIN_NVC0(chan, NvSubCopy, 0x030c, 8); + OUT_RING (chan, upper_32_bits(src_offset)); + OUT_RING (chan, lower_32_bits(src_offset)); + OUT_RING (chan, upper_32_bits(dst_offset)); + OUT_RING (chan, lower_32_bits(dst_offset)); + OUT_RING (chan, PAGE_SIZE); + OUT_RING (chan, PAGE_SIZE); + OUT_RING (chan, PAGE_SIZE); + OUT_RING (chan, line_count); + BEGIN_NVC0(chan, NvSubCopy, 0x0300, 1); + OUT_RING (chan, 0x00000110); + + page_count -= line_count; + src_offset += (PAGE_SIZE * line_count); + dst_offset += (PAGE_SIZE * line_count); + } + + return 0; +} + +static int nvc0_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) { @@ -501,17 +578,17 @@ nvc0_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, if (ret) return ret; - BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0238, 2); + BEGIN_NVC0(chan, NvSubCopy, 0x0238, 2); OUT_RING (chan, upper_32_bits(dst_offset)); OUT_RING (chan, lower_32_bits(dst_offset)); - BEGIN_NVC0(chan, 2, NvSubM2MF, 0x030c, 6); + BEGIN_NVC0(chan, NvSubCopy, 0x030c, 6); OUT_RING (chan, upper_32_bits(src_offset)); OUT_RING (chan, lower_32_bits(src_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); - BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0300, 1); + BEGIN_NVC0(chan, NvSubCopy, 0x0300, 1); OUT_RING (chan, 0x00100110); page_count -= line_count; @@ -523,6 +600,102 @@ nvc0_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, } static int +nva3_bo_move_copy(struct nouveau_channel *chan, struct ttm_buffer_object *bo, + struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) +{ + struct nouveau_mem *node = old_mem->mm_node; + u64 src_offset = node->vma[0].offset; + u64 dst_offset = node->vma[1].offset; + u32 page_count = new_mem->num_pages; + int ret; + + page_count = new_mem->num_pages; + while (page_count) { + int line_count = (page_count > 8191) ? 8191 : page_count; + + ret = RING_SPACE(chan, 11); + if (ret) + return ret; + + BEGIN_NV04(chan, NvSubCopy, 0x030c, 8); + OUT_RING (chan, upper_32_bits(src_offset)); + OUT_RING (chan, lower_32_bits(src_offset)); + OUT_RING (chan, upper_32_bits(dst_offset)); + OUT_RING (chan, lower_32_bits(dst_offset)); + OUT_RING (chan, PAGE_SIZE); + OUT_RING (chan, PAGE_SIZE); + OUT_RING (chan, PAGE_SIZE); + OUT_RING (chan, line_count); + BEGIN_NV04(chan, NvSubCopy, 0x0300, 1); + OUT_RING (chan, 0x00000110); + + page_count -= line_count; + src_offset += (PAGE_SIZE * line_count); + dst_offset += (PAGE_SIZE * line_count); + } + + return 0; +} + +static int +nv98_bo_move_exec(struct nouveau_channel *chan, struct ttm_buffer_object *bo, + struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) +{ + struct nouveau_mem *node = old_mem->mm_node; + int ret = RING_SPACE(chan, 7); + if (ret == 0) { + BEGIN_NV04(chan, NvSubCopy, 0x0320, 6); + OUT_RING (chan, upper_32_bits(node->vma[0].offset)); + OUT_RING (chan, lower_32_bits(node->vma[0].offset)); + OUT_RING (chan, upper_32_bits(node->vma[1].offset)); + OUT_RING (chan, lower_32_bits(node->vma[1].offset)); + OUT_RING (chan, 0x00000000 /* COPY */); + OUT_RING (chan, new_mem->num_pages << PAGE_SHIFT); + } + return ret; +} + +static int +nv84_bo_move_exec(struct nouveau_channel *chan, struct ttm_buffer_object *bo, + struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) +{ + struct nouveau_mem *node = old_mem->mm_node; + int ret = RING_SPACE(chan, 7); + if (ret == 0) { + BEGIN_NV04(chan, NvSubCopy, 0x0304, 6); + OUT_RING (chan, new_mem->num_pages << PAGE_SHIFT); + OUT_RING (chan, upper_32_bits(node->vma[0].offset)); + OUT_RING (chan, lower_32_bits(node->vma[0].offset)); + OUT_RING (chan, upper_32_bits(node->vma[1].offset)); + OUT_RING (chan, lower_32_bits(node->vma[1].offset)); + OUT_RING (chan, 0x00000000 /* MODE_COPY, QUERY_NONE */); + } + return ret; +} + +static int +nv50_bo_move_init(struct nouveau_channel *chan, u32 handle) +{ + int ret = nouveau_notifier_alloc(chan, NvNotify0, 32, 0xfe0, 0x1000, + &chan->m2mf_ntfy); + if (ret == 0) { + ret = RING_SPACE(chan, 6); + if (ret == 0) { + BEGIN_NV04(chan, NvSubCopy, 0x0000, 1); + OUT_RING (chan, handle); + BEGIN_NV04(chan, NvSubCopy, 0x0180, 3); + OUT_RING (chan, NvNotify0); + OUT_RING (chan, NvDmaFB); + OUT_RING (chan, NvDmaFB); + } else { + nouveau_ramht_remove(chan, NvNotify0); + } + } + + return ret; +} + +static int nv50_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) { @@ -546,7 +719,7 @@ nv50_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, if (ret) return ret; - BEGIN_RING(chan, NvSubM2MF, 0x0200, 7); + BEGIN_NV04(chan, NvSubCopy, 0x0200, 7); OUT_RING (chan, 0); OUT_RING (chan, 0); OUT_RING (chan, stride); @@ -559,7 +732,7 @@ nv50_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, if (ret) return ret; - BEGIN_RING(chan, NvSubM2MF, 0x0200, 1); + BEGIN_NV04(chan, NvSubCopy, 0x0200, 1); OUT_RING (chan, 1); } if (old_mem->mem_type == TTM_PL_VRAM && @@ -568,7 +741,7 @@ nv50_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, if (ret) return ret; - BEGIN_RING(chan, NvSubM2MF, 0x021c, 7); + BEGIN_NV04(chan, NvSubCopy, 0x021c, 7); OUT_RING (chan, 0); OUT_RING (chan, 0); OUT_RING (chan, stride); @@ -581,7 +754,7 @@ nv50_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, if (ret) return ret; - BEGIN_RING(chan, NvSubM2MF, 0x021c, 1); + BEGIN_NV04(chan, NvSubCopy, 0x021c, 1); OUT_RING (chan, 1); } @@ -589,10 +762,10 @@ nv50_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, if (ret) return ret; - BEGIN_RING(chan, NvSubM2MF, 0x0238, 2); + BEGIN_NV04(chan, NvSubCopy, 0x0238, 2); OUT_RING (chan, upper_32_bits(src_offset)); OUT_RING (chan, upper_32_bits(dst_offset)); - BEGIN_RING(chan, NvSubM2MF, 0x030c, 8); + BEGIN_NV04(chan, NvSubCopy, 0x030c, 8); OUT_RING (chan, lower_32_bits(src_offset)); OUT_RING (chan, lower_32_bits(dst_offset)); OUT_RING (chan, stride); @@ -601,7 +774,7 @@ nv50_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, OUT_RING (chan, height); OUT_RING (chan, 0x00000101); OUT_RING (chan, 0x00000000); - BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NOP, 1); + BEGIN_NV04(chan, NvSubCopy, NV_MEMORY_TO_MEMORY_FORMAT_NOP, 1); OUT_RING (chan, 0); length -= amount; @@ -612,6 +785,24 @@ nv50_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, return 0; } +static int +nv04_bo_move_init(struct nouveau_channel *chan, u32 handle) +{ + int ret = nouveau_notifier_alloc(chan, NvNotify0, 32, 0xfe0, 0x1000, + &chan->m2mf_ntfy); + if (ret == 0) { + ret = RING_SPACE(chan, 4); + if (ret == 0) { + BEGIN_NV04(chan, NvSubCopy, 0x0000, 1); + OUT_RING (chan, handle); + BEGIN_NV04(chan, NvSubCopy, 0x0180, 1); + OUT_RING (chan, NvNotify0); + } + } + + return ret; +} + static inline uint32_t nouveau_bo_mem_ctxdma(struct ttm_buffer_object *bo, struct nouveau_channel *chan, struct ttm_mem_reg *mem) @@ -634,7 +825,7 @@ nv04_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, if (ret) return ret; - BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_SOURCE, 2); + BEGIN_NV04(chan, NvSubCopy, NV_MEMORY_TO_MEMORY_FORMAT_DMA_SOURCE, 2); OUT_RING (chan, nouveau_bo_mem_ctxdma(bo, chan, old_mem)); OUT_RING (chan, nouveau_bo_mem_ctxdma(bo, chan, new_mem)); @@ -646,7 +837,7 @@ nv04_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, if (ret) return ret; - BEGIN_RING(chan, NvSubM2MF, + BEGIN_NV04(chan, NvSubCopy, NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8); OUT_RING (chan, src_offset); OUT_RING (chan, dst_offset); @@ -656,7 +847,7 @@ nv04_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, OUT_RING (chan, line_count); OUT_RING (chan, 0x00000101); OUT_RING (chan, 0x00000000); - BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NOP, 1); + BEGIN_NV04(chan, NvSubCopy, NV_MEMORY_TO_MEMORY_FORMAT_NOP, 1); OUT_RING (chan, 0); page_count -= line_count; @@ -716,13 +907,7 @@ nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr, goto out; } - if (dev_priv->card_type < NV_50) - ret = nv04_bo_move_m2mf(chan, bo, &bo->mem, new_mem); - else - if (dev_priv->card_type < NV_C0) - ret = nv50_bo_move_m2mf(chan, bo, &bo->mem, new_mem); - else - ret = nvc0_bo_move_m2mf(chan, bo, &bo->mem, new_mem); + ret = dev_priv->ttm.move(chan, bo, &bo->mem, new_mem); if (ret == 0) { ret = nouveau_bo_move_accel_cleanup(chan, nvbo, evict, no_wait_reserve, @@ -734,6 +919,49 @@ out: return ret; } +void +nouveau_bo_move_init(struct nouveau_channel *chan) +{ + struct drm_nouveau_private *dev_priv = chan->dev->dev_private; + static const struct { + const char *name; + int engine; + u32 oclass; + int (*exec)(struct nouveau_channel *, + struct ttm_buffer_object *, + struct ttm_mem_reg *, struct ttm_mem_reg *); + int (*init)(struct nouveau_channel *, u32 handle); + } _methods[] = { + { "COPY", 0, 0xa0b5, nve0_bo_move_copy, nvc0_bo_move_init }, + { "COPY1", 5, 0x90b8, nvc0_bo_move_copy, nvc0_bo_move_init }, + { "COPY0", 4, 0x90b5, nvc0_bo_move_copy, nvc0_bo_move_init }, + { "COPY", 0, 0x85b5, nva3_bo_move_copy, nv50_bo_move_init }, + { "CRYPT", 0, 0x74c1, nv84_bo_move_exec, nv50_bo_move_init }, + { "M2MF", 0, 0x9039, nvc0_bo_move_m2mf, nvc0_bo_move_init }, + { "M2MF", 0, 0x5039, nv50_bo_move_m2mf, nv50_bo_move_init }, + { "M2MF", 0, 0x0039, nv04_bo_move_m2mf, nv04_bo_move_init }, + {}, + { "CRYPT", 0, 0x88b4, nv98_bo_move_exec, nv50_bo_move_init }, + }, *mthd = _methods; + const char *name = "CPU"; + int ret; + + do { + u32 handle = (mthd->engine << 16) | mthd->oclass; + ret = nouveau_gpuobj_gr_new(chan, handle, mthd->oclass); + if (ret == 0) { + ret = mthd->init(chan, handle); + if (ret == 0) { + dev_priv->ttm.move = mthd->exec; + name = mthd->name; + break; + } + } + } while ((++mthd)->exec); + + NV_INFO(chan->dev, "MM: using %s for buffer copies\n", name); +} + static int nouveau_bo_move_flipd(struct ttm_buffer_object *bo, bool evict, bool intr, bool no_wait_reserve, bool no_wait_gpu, @@ -817,9 +1045,14 @@ nouveau_bo_move_ntfy(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem) } else if (new_mem && new_mem->mem_type == TTM_PL_TT && nvbo->page_shift == vma->vm->spg_shift) { - nouveau_vm_map_sg(vma, 0, new_mem-> - num_pages << PAGE_SHIFT, - new_mem->mm_node); + if (((struct nouveau_mem *)new_mem->mm_node)->sg) + nouveau_vm_map_sg_table(vma, 0, new_mem-> + num_pages << PAGE_SHIFT, + new_mem->mm_node); + else + nouveau_vm_map_sg(vma, 0, new_mem-> + num_pages << PAGE_SHIFT, + new_mem->mm_node); } else { nouveau_vm_unmap(vma); } @@ -885,8 +1118,8 @@ nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, bool intr, goto out; } - /* Software copy if the card isn't up and running yet. */ - if (!dev_priv->channel) { + /* CPU copy if we have no accelerated method available */ + if (!dev_priv->ttm.move) { ret = ttm_bo_move_memcpy(bo, evict, no_wait_reserve, no_wait_gpu, new_mem); goto out; } @@ -1030,26 +1263,10 @@ nouveau_ttm_fault_reserve_notify(struct ttm_buffer_object *bo) nvbo->placement.fpfn = 0; nvbo->placement.lpfn = dev_priv->fb_mappable_pages; - nouveau_bo_placement_set(nvbo, TTM_PL_VRAM, 0); + nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_VRAM, 0); return nouveau_bo_validate(nvbo, false, true, false); } -void -nouveau_bo_fence(struct nouveau_bo *nvbo, struct nouveau_fence *fence) -{ - struct nouveau_fence *old_fence; - - if (likely(fence)) - nouveau_fence_ref(fence); - - spin_lock(&nvbo->bo.bdev->fence_lock); - old_fence = nvbo->bo.sync_obj; - nvbo->bo.sync_obj = fence; - spin_unlock(&nvbo->bo.bdev->fence_lock); - - nouveau_fence_unref(&old_fence); -} - static int nouveau_ttm_tt_populate(struct ttm_tt *ttm) { @@ -1058,10 +1275,19 @@ nouveau_ttm_tt_populate(struct ttm_tt *ttm) struct drm_device *dev; unsigned i; int r; + bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG); if (ttm->state != tt_unpopulated) return 0; + if (slave && ttm->sg) { + /* make userspace faulting work */ + drm_prime_sg_to_page_addr_arrays(ttm->sg, ttm->pages, + ttm_dma->dma_address, ttm->num_pages); + ttm->state = tt_unbound; + return 0; + } + dev_priv = nouveau_bdev(ttm->bdev); dev = dev_priv->dev; @@ -1106,6 +1332,10 @@ nouveau_ttm_tt_unpopulate(struct ttm_tt *ttm) struct drm_nouveau_private *dev_priv; struct drm_device *dev; unsigned i; + bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG); + + if (slave) + return; dev_priv = nouveau_bdev(ttm->bdev); dev = dev_priv->dev; @@ -1134,6 +1364,52 @@ nouveau_ttm_tt_unpopulate(struct ttm_tt *ttm) ttm_pool_unpopulate(ttm); } +void +nouveau_bo_fence(struct nouveau_bo *nvbo, struct nouveau_fence *fence) +{ + struct nouveau_fence *old_fence = NULL; + + if (likely(fence)) + nouveau_fence_ref(fence); + + spin_lock(&nvbo->bo.bdev->fence_lock); + old_fence = nvbo->bo.sync_obj; + nvbo->bo.sync_obj = fence; + spin_unlock(&nvbo->bo.bdev->fence_lock); + + nouveau_fence_unref(&old_fence); +} + +static void +nouveau_bo_fence_unref(void **sync_obj) +{ + nouveau_fence_unref((struct nouveau_fence **)sync_obj); +} + +static void * +nouveau_bo_fence_ref(void *sync_obj) +{ + return nouveau_fence_ref(sync_obj); +} + +static bool +nouveau_bo_fence_signalled(void *sync_obj, void *sync_arg) +{ + return nouveau_fence_done(sync_obj); +} + +static int +nouveau_bo_fence_wait(void *sync_obj, void *sync_arg, bool lazy, bool intr) +{ + return nouveau_fence_wait(sync_obj, lazy, intr); +} + +static int +nouveau_bo_fence_flush(void *sync_obj, void *sync_arg) +{ + return 0; +} + struct ttm_bo_driver nouveau_bo_driver = { .ttm_tt_create = &nouveau_ttm_tt_create, .ttm_tt_populate = &nouveau_ttm_tt_populate, @@ -1144,11 +1420,11 @@ struct ttm_bo_driver nouveau_bo_driver = { .move_notify = nouveau_bo_move_ntfy, .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, + .sync_obj_signaled = nouveau_bo_fence_signalled, + .sync_obj_wait = nouveau_bo_fence_wait, + .sync_obj_flush = nouveau_bo_fence_flush, + .sync_obj_unref = nouveau_bo_fence_unref, + .sync_obj_ref = nouveau_bo_fence_ref, .fault_reserve_notify = &nouveau_ttm_fault_reserve_notify, .io_mem_reserve = &nouveau_ttm_io_mem_reserve, .io_mem_free = &nouveau_ttm_io_mem_free, @@ -1181,9 +1457,12 @@ nouveau_bo_vma_add(struct nouveau_bo *nvbo, struct nouveau_vm *vm, if (nvbo->bo.mem.mem_type == TTM_PL_VRAM) nouveau_vm_map(vma, nvbo->bo.mem.mm_node); - else - if (nvbo->bo.mem.mem_type == TTM_PL_TT) - nouveau_vm_map_sg(vma, 0, size, node); + else if (nvbo->bo.mem.mem_type == TTM_PL_TT) { + if (node->sg) + nouveau_vm_map_sg_table(vma, 0, size, node); + else + nouveau_vm_map_sg(vma, 0, size, node); + } list_add_tail(&vma->head, &nvbo->vma_list); vma->refcount = 1; diff --git a/drivers/gpu/drm/nouveau/nouveau_channel.c b/drivers/gpu/drm/nouveau/nouveau_channel.c index 846afb0bfef..629d8a2df5b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_channel.c +++ b/drivers/gpu/drm/nouveau/nouveau_channel.c @@ -27,7 +27,10 @@ #include "nouveau_drv.h" #include "nouveau_drm.h" #include "nouveau_dma.h" +#include "nouveau_fifo.h" #include "nouveau_ramht.h" +#include "nouveau_fence.h" +#include "nouveau_software.h" static int nouveau_channel_pushbuf_init(struct nouveau_channel *chan) @@ -38,7 +41,7 @@ nouveau_channel_pushbuf_init(struct nouveau_channel *chan) int ret; /* allocate buffer object */ - ret = nouveau_bo_new(dev, 65536, 0, mem, 0, 0, &chan->pushbuf_bo); + ret = nouveau_bo_new(dev, 65536, 0, mem, 0, 0, NULL, &chan->pushbuf_bo); if (ret) goto out; @@ -117,8 +120,9 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret, struct drm_file *file_priv, uint32_t vram_handle, uint32_t gart_handle) { + struct nouveau_exec_engine *fence = nv_engine(dev, NVOBJ_ENGINE_FENCE); + struct nouveau_fifo_priv *pfifo = nv_engine(dev, NVOBJ_ENGINE_FIFO); struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; struct nouveau_fpriv *fpriv = nouveau_fpriv(file_priv); struct nouveau_channel *chan; unsigned long flags; @@ -155,10 +159,6 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret, } NV_DEBUG(dev, "initialising channel %d\n", chan->id); - INIT_LIST_HEAD(&chan->nvsw.vbl_wait); - INIT_LIST_HEAD(&chan->nvsw.flip); - INIT_LIST_HEAD(&chan->fence.pending); - spin_lock_init(&chan->fence.lock); /* setup channel's memory and vm */ ret = nouveau_gpuobj_channel_init(chan, vram_handle, gart_handle); @@ -188,20 +188,15 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret, chan->user_put = 0x40; chan->user_get = 0x44; if (dev_priv->card_type >= NV_50) - chan->user_get_hi = 0x60; + chan->user_get_hi = 0x60; - /* disable the fifo caches */ - pfifo->reassign(dev, false); - - /* Construct initial RAMFC for new channel */ - ret = pfifo->create_context(chan); + /* create fifo context */ + ret = pfifo->base.context_new(chan, NVOBJ_ENGINE_FIFO); if (ret) { nouveau_channel_put(&chan); return ret; } - pfifo->reassign(dev, true); - /* Insert NOPs for NOUVEAU_DMA_SKIPS */ ret = RING_SPACE(chan, NOUVEAU_DMA_SKIPS); if (ret) { @@ -211,9 +206,28 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret, for (i = 0; i < NOUVEAU_DMA_SKIPS; i++) OUT_RING (chan, 0x00000000); + + ret = nouveau_gpuobj_gr_new(chan, NvSw, nouveau_software_class(dev)); + if (ret) { + nouveau_channel_put(&chan); + return ret; + } + + if (dev_priv->card_type < NV_C0) { + ret = RING_SPACE(chan, 2); + if (ret) { + nouveau_channel_put(&chan); + return ret; + } + + BEGIN_NV04(chan, NvSubSw, NV01_SUBCHAN_OBJECT, 1); + OUT_RING (chan, NvSw); + FIRE_RING (chan); + } + FIRE_RING(chan); - ret = nouveau_fence_channel_init(chan); + ret = fence->context_new(chan, NVOBJ_ENGINE_FENCE); if (ret) { nouveau_channel_put(&chan); return ret; @@ -268,7 +282,6 @@ nouveau_channel_put_unlocked(struct nouveau_channel **pchan) struct nouveau_channel *chan = *pchan; struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; unsigned long flags; int i; @@ -285,24 +298,12 @@ nouveau_channel_put_unlocked(struct nouveau_channel **pchan) /* give it chance to idle */ nouveau_channel_idle(chan); - /* 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_channel_fini(chan); - - /* boot it off the hardware */ - pfifo->reassign(dev, false); - /* destroy the engine specific contexts */ - pfifo->destroy_context(chan); - for (i = 0; i < NVOBJ_ENGINE_NR; i++) { + for (i = NVOBJ_ENGINE_NR - 1; i >= 0; i--) { if (chan->engctx[i]) dev_priv->eng[i]->context_del(chan, i); } - pfifo->reassign(dev, true); - /* aside from its resources, the channel should now be dead, * remove it from the channel list */ @@ -354,38 +355,37 @@ nouveau_channel_ref(struct nouveau_channel *chan, *pchan = chan; } -void +int nouveau_channel_idle(struct nouveau_channel *chan) { struct drm_device *dev = chan->dev; struct nouveau_fence *fence = NULL; int ret; - nouveau_fence_update(chan); - - if (chan->fence.sequence != chan->fence.sequence_ack) { - ret = nouveau_fence_new(chan, &fence, true); - if (!ret) { - ret = nouveau_fence_wait(fence, false, false); - nouveau_fence_unref(&fence); - } - - if (ret) - NV_ERROR(dev, "Failed to idle channel %d.\n", chan->id); + ret = nouveau_fence_new(chan, &fence); + if (!ret) { + ret = nouveau_fence_wait(fence, false, false); + nouveau_fence_unref(&fence); } + + if (ret) + NV_ERROR(dev, "Failed to idle channel %d.\n", chan->id); + return ret; } /* 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; + struct nouveau_fifo_priv *pfifo = nv_engine(dev, NVOBJ_ENGINE_FIFO); struct nouveau_channel *chan; int i; + if (!pfifo) + return; + NV_DEBUG(dev, "clearing FIFO enables from file_priv\n"); - for (i = 0; i < engine->fifo.channels; i++) { + for (i = 0; i < pfifo->channels; i++) { chan = nouveau_channel_get(file_priv, i); if (IS_ERR(chan)) continue; diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index fa860358add..7b11edb077d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -654,7 +654,13 @@ nouveau_connector_detect_depth(struct drm_connector *connector) if (nv_connector->edid && connector->display_info.bpc) return; - /* if not, we're out of options unless we're LVDS, default to 8bpc */ + /* EDID 1.4 is *supposed* to be supported on eDP, but, Apple... */ + if (nv_connector->type == DCB_CONNECTOR_eDP) { + connector->display_info.bpc = 6; + return; + } + + /* we're out of options unless we're LVDS, default to 8bpc */ if (nv_encoder->dcb->type != OUTPUT_LVDS) { connector->display_info.bpc = 8; return; diff --git a/drivers/gpu/drm/nouveau/nouveau_debugfs.c b/drivers/gpu/drm/nouveau/nouveau_debugfs.c index fa2ec491f6a..188c92b327e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_debugfs.c +++ b/drivers/gpu/drm/nouveau/nouveau_debugfs.c @@ -67,8 +67,6 @@ nouveau_debugfs_channel_info(struct seq_file *m, void *data) nvchan_rd32(chan, 0x8c)); } - seq_printf(m, "last fence : %d\n", chan->fence.sequence); - seq_printf(m, "last signalled: %d\n", chan->fence.sequence_ack); return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index a85e112863d..69688ef5cf4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -33,7 +33,9 @@ #include "nouveau_crtc.h" #include "nouveau_dma.h" #include "nouveau_connector.h" +#include "nouveau_software.h" #include "nouveau_gpio.h" +#include "nouveau_fence.h" #include "nv50_display.h" static void @@ -300,7 +302,7 @@ nouveau_display_create(struct drm_device *dev) disp->color_vibrance_property->values[1] = 200; /* -100..+100 */ } - dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs; + dev->mode_config.funcs = &nouveau_mode_config_funcs; dev->mode_config.fb_base = pci_resource_start(dev->pdev, 1); dev->mode_config.min_width = 0; @@ -325,14 +327,21 @@ nouveau_display_create(struct drm_device *dev) ret = disp->create(dev); if (ret) - return ret; + goto disp_create_err; if (dev->mode_config.num_crtc) { ret = drm_vblank_init(dev, dev->mode_config.num_crtc); if (ret) - return ret; + goto vblank_err; } + return 0; + +vblank_err: + disp->destroy(dev); +disp_create_err: + drm_kms_helper_poll_fini(dev); + drm_mode_config_cleanup(dev); return ret; } @@ -425,6 +434,7 @@ nouveau_page_flip_emit(struct nouveau_channel *chan, struct nouveau_page_flip_state *s, struct nouveau_fence **pfence) { + struct nouveau_software_chan *swch = chan->engctx[NVOBJ_ENGINE_SW]; struct drm_nouveau_private *dev_priv = chan->dev->dev_private; struct drm_device *dev = chan->dev; unsigned long flags; @@ -432,7 +442,7 @@ nouveau_page_flip_emit(struct nouveau_channel *chan, /* Queue it to the pending list */ spin_lock_irqsave(&dev->event_lock, flags); - list_add_tail(&s->head, &chan->nvsw.flip); + list_add_tail(&s->head, &swch->flip); spin_unlock_irqrestore(&dev->event_lock, flags); /* Synchronize with the old framebuffer */ @@ -446,17 +456,17 @@ nouveau_page_flip_emit(struct nouveau_channel *chan, goto fail; if (dev_priv->card_type < NV_C0) { - BEGIN_RING(chan, NvSubSw, NV_SW_PAGE_FLIP, 1); + BEGIN_NV04(chan, NvSubSw, NV_SW_PAGE_FLIP, 1); OUT_RING (chan, 0x00000000); OUT_RING (chan, 0x00000000); } else { - BEGIN_NVC0(chan, 2, 0, NV10_SUBCHAN_REF_CNT, 1); - OUT_RING (chan, ++chan->fence.sequence); - BEGIN_NVC0(chan, 8, 0, NVSW_SUBCHAN_PAGE_FLIP, 0x0000); + BEGIN_NVC0(chan, 0, NV10_SUBCHAN_REF_CNT, 1); + OUT_RING (chan, 0); + BEGIN_IMC0(chan, 0, NVSW_SUBCHAN_PAGE_FLIP, 0x0000); } FIRE_RING (chan); - ret = nouveau_fence_new(chan, pfence, true); + ret = nouveau_fence_new(chan, pfence); if (ret) goto fail; @@ -477,7 +487,7 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct nouveau_bo *old_bo = nouveau_framebuffer(crtc->fb)->nvbo; struct nouveau_bo *new_bo = nouveau_framebuffer(fb)->nvbo; struct nouveau_page_flip_state *s; - struct nouveau_channel *chan; + struct nouveau_channel *chan = NULL; struct nouveau_fence *fence; int ret; @@ -500,7 +510,9 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, new_bo->bo.offset }; /* Choose the channel the flip will be handled in */ - chan = nouveau_fence_channel(new_bo->bo.sync_obj); + fence = new_bo->bo.sync_obj; + if (fence) + chan = nouveau_channel_get_unlocked(fence->channel); if (!chan) chan = nouveau_channel_get_unlocked(dev_priv->channel); mutex_lock(&chan->mutex); @@ -540,20 +552,20 @@ int nouveau_finish_page_flip(struct nouveau_channel *chan, struct nouveau_page_flip_state *ps) { + struct nouveau_software_chan *swch = chan->engctx[NVOBJ_ENGINE_SW]; struct drm_device *dev = chan->dev; struct nouveau_page_flip_state *s; unsigned long flags; spin_lock_irqsave(&dev->event_lock, flags); - if (list_empty(&chan->nvsw.flip)) { + if (list_empty(&swch->flip)) { NV_ERROR(dev, "Unexpected pageflip in channel %d.\n", chan->id); spin_unlock_irqrestore(&dev->event_lock, flags); return -EINVAL; } - s = list_first_entry(&chan->nvsw.flip, - struct nouveau_page_flip_state, head); + s = list_first_entry(&swch->flip, struct nouveau_page_flip_state, head); if (s->event) { struct drm_pending_vblank_event *e = s->event; struct timeval now; diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.h b/drivers/gpu/drm/nouveau/nouveau_dma.h index 23d4edf992b..8db68be9544 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dma.h +++ b/drivers/gpu/drm/nouveau/nouveau_dma.h @@ -48,12 +48,12 @@ void nv50_dma_push(struct nouveau_channel *, struct nouveau_bo *, /* Hardcoded object assignments to subchannels (subchannel id). */ enum { - NvSubM2MF = 0, + NvSubCtxSurf2D = 0, NvSubSw = 1, - NvSub2D = 2, - NvSubCtxSurf2D = 2, + NvSubImageBlit = 2, + NvSub2D = 3, NvSubGdiRect = 3, - NvSubImageBlit = 4 + NvSubCopy = 4, }; /* Object handles. */ @@ -73,6 +73,7 @@ enum { NvSema = 0x8000000f, NvEvoSema0 = 0x80000010, NvEvoSema1 = 0x80000011, + NvNotify1 = 0x80000012, /* G80+ display objects */ NvEvoVRAM = 0x01000000, @@ -127,15 +128,33 @@ extern void OUT_RINGp(struct nouveau_channel *chan, const void *data, unsigned nr_dwords); static inline void -BEGIN_NVC0(struct nouveau_channel *chan, int op, int subc, int mthd, int size) +BEGIN_NV04(struct nouveau_channel *chan, int subc, int mthd, int size) { - OUT_RING(chan, (op << 28) | (size << 16) | (subc << 13) | (mthd >> 2)); + OUT_RING(chan, 0x00000000 | (subc << 13) | (size << 18) | mthd); } static inline void -BEGIN_RING(struct nouveau_channel *chan, int subc, int mthd, int size) +BEGIN_NI04(struct nouveau_channel *chan, int subc, int mthd, int size) { - OUT_RING(chan, (subc << 13) | (size << 18) | mthd); + OUT_RING(chan, 0x40000000 | (subc << 13) | (size << 18) | mthd); +} + +static inline void +BEGIN_NVC0(struct nouveau_channel *chan, int subc, int mthd, int size) +{ + OUT_RING(chan, 0x20000000 | (size << 16) | (subc << 13) | (mthd >> 2)); +} + +static inline void +BEGIN_NIC0(struct nouveau_channel *chan, int subc, int mthd, int size) +{ + OUT_RING(chan, 0x60000000 | (size << 16) | (subc << 13) | (mthd >> 2)); +} + +static inline void +BEGIN_IMC0(struct nouveau_channel *chan, int subc, int mthd, u16 data) +{ + OUT_RING(chan, 0x80000000 | (data << 16) | (subc << 13) | (mthd >> 2)); } #define WRITE_PUT(val) do { \ diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index d996134b1b2..7e289d2ad8e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -510,6 +510,25 @@ nouveau_dp_dpms(struct drm_encoder *encoder, int mode, u32 datarate, nouveau_dp_link_train(encoder, datarate, func); } +static void +nouveau_dp_probe_oui(struct drm_device *dev, struct nouveau_i2c_chan *auxch, + u8 *dpcd) +{ + u8 buf[3]; + + if (!(dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_OUI_SUPPORT)) + return; + + if (!auxch_tx(dev, auxch->drive, 9, DP_SINK_OUI, buf, 3)) + NV_DEBUG_KMS(dev, "Sink OUI: %02hx%02hx%02hx\n", + buf[0], buf[1], buf[2]); + + if (!auxch_tx(dev, auxch->drive, 9, DP_BRANCH_OUI, buf, 3)) + NV_DEBUG_KMS(dev, "Branch OUI: %02hx%02hx%02hx\n", + buf[0], buf[1], buf[2]); + +} + bool nouveau_dp_detect(struct drm_encoder *encoder) { @@ -544,6 +563,8 @@ nouveau_dp_detect(struct drm_encoder *encoder) NV_DEBUG_KMS(dev, "maximum: %dx%d\n", nv_encoder->dp.link_nr, nv_encoder->dp.link_bw); + nouveau_dp_probe_oui(dev, auxch, dpcd); + return true; } diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c index 4f2030bd567..cad254c8e38 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -33,6 +33,7 @@ #include "nouveau_fb.h" #include "nouveau_fbcon.h" #include "nouveau_pm.h" +#include "nouveau_fifo.h" #include "nv50_display.h" #include "drm_pciids.h" @@ -175,7 +176,7 @@ 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_fifo_engine *pfifo = &dev_priv->engine.fifo; + struct nouveau_fifo_priv *pfifo = nv_engine(dev, NVOBJ_ENGINE_FIFO); struct nouveau_channel *chan; struct drm_crtc *crtc; int ret, i, e; @@ -214,17 +215,13 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) ttm_bo_evict_mm(&dev_priv->ttm.bdev, TTM_PL_VRAM); NV_INFO(dev, "Idling channels...\n"); - for (i = 0; i < pfifo->channels; i++) { + for (i = 0; i < (pfifo ? pfifo->channels : 0); i++) { chan = dev_priv->channels.ptr[i]; if (chan && chan->pushbuf_bo) nouveau_channel_idle(chan); } - pfifo->reassign(dev, false); - pfifo->disable(dev); - pfifo->unload_context(dev); - for (e = NVOBJ_ENGINE_NR - 1; e >= 0; e--) { if (!dev_priv->eng[e]) continue; @@ -265,8 +262,6 @@ out_abort: if (dev_priv->eng[e]) dev_priv->eng[e]->init(dev, e); } - pfifo->enable(dev); - pfifo->reassign(dev, true); return ret; } @@ -274,6 +269,7 @@ int nouveau_pci_resume(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); + struct nouveau_fifo_priv *pfifo = nv_engine(dev, NVOBJ_ENGINE_FIFO); struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_engine *engine = &dev_priv->engine; struct drm_crtc *crtc; @@ -321,7 +317,6 @@ nouveau_pci_resume(struct pci_dev *pdev) if (dev_priv->eng[i]) dev_priv->eng[i]->init(dev, i); } - engine->fifo.init(dev); nouveau_irq_postinstall(dev); @@ -330,7 +325,7 @@ nouveau_pci_resume(struct pci_dev *pdev) struct nouveau_channel *chan; int j; - for (i = 0; i < dev_priv->engine.fifo.channels; i++) { + for (i = 0; i < (pfifo ? pfifo->channels : 0); i++) { chan = dev_priv->channels.ptr[i]; if (!chan || !chan->pushbuf_bo) continue; @@ -408,7 +403,7 @@ static struct drm_driver driver = { .driver_features = DRIVER_USE_AGP | DRIVER_PCI_DMA | DRIVER_SG | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM | - DRIVER_MODESET, + DRIVER_MODESET | DRIVER_PRIME, .load = nouveau_load, .firstopen = nouveau_firstopen, .lastclose = nouveau_lastclose, @@ -430,6 +425,12 @@ static struct drm_driver driver = { .reclaim_buffers = drm_core_reclaim_buffers, .ioctls = nouveau_ioctls, .fops = &nouveau_driver_fops, + + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_export = nouveau_gem_prime_export, + .gem_prime_import = nouveau_gem_prime_import, + .gem_init_object = nouveau_gem_object_new, .gem_free_object = nouveau_gem_object_del, .gem_open_object = nouveau_gem_object_open, diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 3aef353a926..634d222c93d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -70,7 +70,7 @@ struct nouveau_mem; #define MAX_NUM_DCB_ENTRIES 16 -#define NOUVEAU_MAX_CHANNEL_NR 128 +#define NOUVEAU_MAX_CHANNEL_NR 4096 #define NOUVEAU_MAX_TILE_NR 15 struct nouveau_mem { @@ -86,6 +86,7 @@ struct nouveau_mem { u32 memtype; u64 offset; u64 size; + struct sg_table *sg; }; struct nouveau_tile_reg { @@ -164,8 +165,10 @@ enum nouveau_flags { #define NVOBJ_ENGINE_PPP NVOBJ_ENGINE_MPEG #define NVOBJ_ENGINE_BSP 6 #define NVOBJ_ENGINE_VP 7 -#define NVOBJ_ENGINE_DISPLAY 15 +#define NVOBJ_ENGINE_FIFO 14 +#define NVOBJ_ENGINE_FENCE 15 #define NVOBJ_ENGINE_NR 16 +#define NVOBJ_ENGINE_DISPLAY (NVOBJ_ENGINE_NR + 0) /*XXX*/ #define NVOBJ_FLAG_DONT_MAP (1 << 0) #define NVOBJ_FLAG_ZERO_ALLOC (1 << 1) @@ -233,17 +236,6 @@ struct nouveau_channel { uint32_t user_get_hi; 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; - atomic_t last_sequence_irq; - struct nouveau_vma vma; - } fence; - /* DMA push buffer */ struct nouveau_gpuobj *pushbuf; struct nouveau_bo *pushbuf_bo; @@ -257,8 +249,6 @@ struct nouveau_channel { /* PFIFO context */ struct nouveau_gpuobj *ramfc; - struct nouveau_gpuobj *cache; - void *fifo_priv; /* Execution engine contexts */ void *engctx[NVOBJ_ENGINE_NR]; @@ -292,18 +282,6 @@ struct nouveau_channel { int ib_put; } dma; - uint32_t sw_subchannel[8]; - - struct nouveau_vma dispc_vma[4]; - struct { - struct nouveau_gpuobj *vblsem; - uint32_t vblsem_head; - uint32_t vblsem_offset; - uint32_t vblsem_rval; - struct list_head vbl_wait; - struct list_head flip; - } nvsw; - struct { bool active; char name[32]; @@ -366,30 +344,6 @@ struct nouveau_fb_engine { void (*free_tile_region)(struct drm_device *dev, int i); }; -struct nouveau_fifo_engine { - void *priv; - int channels; - - struct nouveau_gpuobj *playlist[2]; - int cur_playlist; - - 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_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 *); - void (*tlb_flush)(struct drm_device *dev); -}; - struct nouveau_display_engine { void *priv; int (*early_init)(struct drm_device *); @@ -597,7 +551,6 @@ struct nouveau_engine { struct nouveau_mc_engine mc; struct nouveau_timer_engine timer; struct nouveau_fb_engine fb; - struct nouveau_fifo_engine fifo; struct nouveau_display_engine display; struct nouveau_gpio_engine gpio; struct nouveau_pm_engine pm; @@ -740,6 +693,9 @@ struct drm_nouveau_private { struct ttm_bo_global_ref bo_global_ref; struct ttm_bo_device bdev; atomic_t validate_sequence; + int (*move)(struct nouveau_channel *, + struct ttm_buffer_object *, + struct ttm_mem_reg *, struct ttm_mem_reg *); } ttm; struct { @@ -977,7 +933,7 @@ extern void nouveau_channel_put_unlocked(struct nouveau_channel **); extern void nouveau_channel_put(struct nouveau_channel **); extern void nouveau_channel_ref(struct nouveau_channel *chan, struct nouveau_channel **pchan); -extern void nouveau_channel_idle(struct nouveau_channel *chan); +extern int nouveau_channel_idle(struct nouveau_channel *chan); /* nouveau_object.c */ #define NVOBJ_ENGINE_ADD(d, e, p) do { \ @@ -1209,56 +1165,6 @@ extern void nv50_fb_vm_trap(struct drm_device *, int display); extern int nvc0_fb_init(struct drm_device *); extern void nvc0_fb_takedown(struct drm_device *); -/* nv04_fifo.c */ -extern int nv04_fifo_init(struct drm_device *); -extern void nv04_fifo_fini(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_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 *); -extern void nv04_fifo_isr(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 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 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 *); -extern void nv50_fifo_tlb_flush(struct drm_device *dev); - -/* nvc0_fifo.c */ -extern int nvc0_fifo_init(struct drm_device *); -extern void nvc0_fifo_takedown(struct drm_device *); -extern void nvc0_fifo_disable(struct drm_device *); -extern void nvc0_fifo_enable(struct drm_device *); -extern bool nvc0_fifo_reassign(struct drm_device *, bool); -extern bool nvc0_fifo_cache_pull(struct drm_device *, bool); -extern int nvc0_fifo_channel_id(struct drm_device *); -extern int nvc0_fifo_create_context(struct nouveau_channel *); -extern void nvc0_fifo_destroy_context(struct nouveau_channel *); -extern int nvc0_fifo_load_context(struct nouveau_channel *); -extern int nvc0_fifo_unload_context(struct drm_device *); - /* nv04_graph.c */ extern int nv04_graph_create(struct drm_device *); extern int nv04_graph_object_new(struct nouveau_channel *, int, u32, u16); @@ -1277,18 +1183,23 @@ extern int nv20_graph_create(struct drm_device *); /* nv40_graph.c */ extern int nv40_graph_create(struct drm_device *); -extern void nv40_grctx_init(struct nouveau_grctx *); +extern void nv40_grctx_init(struct drm_device *, u32 *size); +extern void nv40_grctx_fill(struct drm_device *, struct nouveau_gpuobj *); /* nv50_graph.c */ extern int nv50_graph_create(struct drm_device *); -extern int nv50_grctx_init(struct nouveau_grctx *); extern struct nouveau_enum nv50_data_error_names[]; extern int nv50_graph_isr_chid(struct drm_device *dev, u64 inst); +extern int nv50_grctx_init(struct drm_device *, u32 *, u32, u32 *, u32 *); +extern void nv50_grctx_fill(struct drm_device *, struct nouveau_gpuobj *); /* nvc0_graph.c */ extern int nvc0_graph_create(struct drm_device *); extern int nvc0_graph_isr_chid(struct drm_device *dev, u64 inst); +/* nve0_graph.c */ +extern int nve0_graph_create(struct drm_device *); + /* nv84_crypt.c */ extern int nv84_crypt_create(struct drm_device *); @@ -1414,9 +1325,12 @@ extern int nv04_crtc_create(struct drm_device *, int index); /* nouveau_bo.c */ extern struct ttm_bo_driver nouveau_bo_driver; +extern void nouveau_bo_move_init(struct nouveau_channel *); extern int nouveau_bo_new(struct drm_device *, int size, int align, uint32_t flags, uint32_t tile_mode, - uint32_t tile_flags, struct nouveau_bo **); + uint32_t tile_flags, + struct sg_table *sg, + 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 *); @@ -1437,50 +1351,6 @@ extern int nouveau_bo_vma_add(struct nouveau_bo *, struct nouveau_vm *, struct nouveau_vma *); extern void nouveau_bo_vma_del(struct nouveau_bo *, struct nouveau_vma *); -/* nouveau_fence.c */ -struct nouveau_fence; -extern int nouveau_fence_init(struct drm_device *); -extern void nouveau_fence_fini(struct drm_device *); -extern int nouveau_fence_channel_init(struct nouveau_channel *); -extern void nouveau_fence_channel_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 *); -extern void nouveau_fence_work(struct nouveau_fence *fence, - void (*work)(void *priv, bool signalled), - void *priv); -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); - -static inline bool nouveau_fence_signalled(struct nouveau_fence *obj) -{ - return __nouveau_fence_signalled(obj, NULL); -} -static inline int -nouveau_fence_wait(struct nouveau_fence *obj, bool lazy, bool intr) -{ - return __nouveau_fence_wait(obj, NULL, lazy, intr); -} -extern int nouveau_fence_sync(struct nouveau_fence *, struct nouveau_channel *); -static inline int nouveau_fence_flush(struct nouveau_fence *obj) -{ - return __nouveau_fence_flush(obj, NULL); -} -static inline void nouveau_fence_unref(struct nouveau_fence **obj) -{ - __nouveau_fence_unref((void **)obj); -} -static inline struct nouveau_fence *nouveau_fence_ref(struct nouveau_fence *obj) -{ - return __nouveau_fence_ref(obj); -} - /* nouveau_gem.c */ extern int nouveau_gem_new(struct drm_device *, int size, int align, uint32_t domain, uint32_t tile_mode, @@ -1501,6 +1371,11 @@ extern int nouveau_gem_ioctl_cpu_fini(struct drm_device *, void *, extern int nouveau_gem_ioctl_info(struct drm_device *, void *, struct drm_file *); +extern struct dma_buf *nouveau_gem_prime_export(struct drm_device *dev, + struct drm_gem_object *obj, int flags); +extern struct drm_gem_object *nouveau_gem_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf); + /* nouveau_display.c */ int nouveau_display_create(struct drm_device *dev); void nouveau_display_destroy(struct drm_device *dev); @@ -1772,6 +1647,7 @@ nv44_graph_class(struct drm_device *dev) #define NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_EQUAL 0x00000001 #define NV84_SUBCHAN_SEMAPHORE_TRIGGER_WRITE_LONG 0x00000002 #define NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_GEQUAL 0x00000004 +#define NVC0_SUBCHAN_SEMAPHORE_TRIGGER_YIELD 0x00001000 #define NV84_SUBCHAN_NOTIFY_INTR 0x00000020 #define NV84_SUBCHAN_WRCACHE_FLUSH 0x00000024 #define NV10_SUBCHAN_REF_CNT 0x00000050 diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index 8113e9201ed..153b9a15469 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -153,7 +153,7 @@ nouveau_fbcon_sync(struct fb_info *info) struct drm_device *dev = nfbdev->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_channel *chan = dev_priv->channel; - int ret, i; + int ret; if (!chan || !chan->accel_done || in_interrupt() || info->state != FBINFO_STATE_RUNNING || @@ -163,38 +163,8 @@ nouveau_fbcon_sync(struct fb_info *info) if (!mutex_trylock(&chan->mutex)) return 0; - ret = RING_SPACE(chan, 4); - if (ret) { - mutex_unlock(&chan->mutex); - nouveau_fbcon_gpu_lockup(info); - return 0; - } - - if (dev_priv->card_type >= NV_C0) { - BEGIN_NVC0(chan, 2, NvSub2D, 0x010c, 1); - OUT_RING (chan, 0); - BEGIN_NVC0(chan, 2, NvSub2D, 0x0100, 1); - OUT_RING (chan, 0); - } else { - 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/4 + 3, 0xffffffff); - FIRE_RING(chan); + ret = nouveau_channel_idle(chan); mutex_unlock(&chan->mutex); - - ret = -EBUSY; - for (i = 0; i < 100000; i++) { - if (!nouveau_bo_rd32(chan->notifier_bo, chan->m2mf_ntfy/4 + 3)) { - ret = 0; - break; - } - DRM_UDELAY(1); - } - if (ret) { nouveau_fbcon_gpu_lockup(info); return 0; diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index c1dc20f6cb8..3c180493dab 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -32,220 +32,100 @@ #include "nouveau_drv.h" #include "nouveau_ramht.h" +#include "nouveau_fence.h" +#include "nouveau_software.h" #include "nouveau_dma.h" -#define USE_REFCNT(dev) (nouveau_private(dev)->chipset >= 0x10) -#define USE_SEMA(dev) (nouveau_private(dev)->chipset >= 0x17) - -struct nouveau_fence { - struct nouveau_channel *channel; - struct kref refcount; - struct list_head entry; - - uint32_t sequence; - bool signalled; - - void (*work)(void *priv, bool signalled); - void *priv; -}; - -struct nouveau_semaphore { - struct kref ref; - struct drm_device *dev; - struct drm_mm_node *mem; -}; - -static inline struct nouveau_fence * -nouveau_fence(void *sync_obj) +void +nouveau_fence_context_del(struct nouveau_fence_chan *fctx) { - return (struct nouveau_fence *)sync_obj; + struct nouveau_fence *fence, *fnext; + spin_lock(&fctx->lock); + list_for_each_entry_safe(fence, fnext, &fctx->pending, head) { + if (fence->work) + fence->work(fence->priv, false); + fence->channel = NULL; + list_del(&fence->head); + nouveau_fence_unref(&fence); + } + spin_unlock(&fctx->lock); } -static void -nouveau_fence_del(struct kref *ref) +void +nouveau_fence_context_new(struct nouveau_fence_chan *fctx) { - struct nouveau_fence *fence = - container_of(ref, struct nouveau_fence, refcount); - - nouveau_channel_ref(NULL, &fence->channel); - kfree(fence); + INIT_LIST_HEAD(&fctx->pending); + spin_lock_init(&fctx->lock); } void nouveau_fence_update(struct nouveau_channel *chan) { struct drm_device *dev = chan->dev; - struct nouveau_fence *tmp, *fence; - uint32_t sequence; + struct nouveau_fence_priv *priv = nv_engine(dev, NVOBJ_ENGINE_FENCE); + struct nouveau_fence_chan *fctx = chan->engctx[NVOBJ_ENGINE_FENCE]; + struct nouveau_fence *fence, *fnext; - spin_lock(&chan->fence.lock); - - /* Fetch the last sequence if the channel is still up and running */ - if (likely(!list_empty(&chan->fence.pending))) { - if (USE_REFCNT(dev)) - sequence = nvchan_rd32(chan, 0x48); - else - sequence = atomic_read(&chan->fence.last_sequence_irq); - - if (chan->fence.sequence_ack == sequence) - goto out; - chan->fence.sequence_ack = sequence; - } - - list_for_each_entry_safe(fence, tmp, &chan->fence.pending, entry) { - if (fence->sequence > chan->fence.sequence_ack) + spin_lock(&fctx->lock); + list_for_each_entry_safe(fence, fnext, &fctx->pending, head) { + if (priv->read(chan) < fence->sequence) break; - fence->signalled = true; - list_del(&fence->entry); if (fence->work) fence->work(fence->priv, true); - - kref_put(&fence->refcount, nouveau_fence_del); - } - -out: - spin_unlock(&chan->fence.lock); -} - -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); - nouveau_channel_ref(chan, &fence->channel); - - if (emit) - ret = nouveau_fence_emit(fence); - - if (ret) + fence->channel = NULL; + list_del(&fence->head); nouveau_fence_unref(&fence); - *pfence = fence; - return ret; -} - -struct nouveau_channel * -nouveau_fence_channel(struct nouveau_fence *fence) -{ - return fence ? nouveau_channel_get_unlocked(fence->channel) : NULL; + } + spin_unlock(&fctx->lock); } int -nouveau_fence_emit(struct nouveau_fence *fence) +nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan) { - struct nouveau_channel *chan = fence->channel; struct drm_device *dev = chan->dev; - struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_fence_priv *priv = nv_engine(dev, NVOBJ_ENGINE_FENCE); + struct nouveau_fence_chan *fctx = chan->engctx[NVOBJ_ENGINE_FENCE]; int ret; - ret = RING_SPACE(chan, 2); - if (ret) - return ret; - - if (unlikely(chan->fence.sequence == chan->fence.sequence_ack - 1)) { - nouveau_fence_update(chan); + fence->channel = chan; + fence->timeout = jiffies + (3 * DRM_HZ); + fence->sequence = ++fctx->sequence; - BUG_ON(chan->fence.sequence == - chan->fence.sequence_ack - 1); + ret = priv->emit(fence); + if (!ret) { + kref_get(&fence->kref); + spin_lock(&fctx->lock); + list_add_tail(&fence->head, &fctx->pending); + spin_unlock(&fctx->lock); } - fence->sequence = ++chan->fence.sequence; - - kref_get(&fence->refcount); - spin_lock(&chan->fence.lock); - list_add_tail(&fence->entry, &chan->fence.pending); - spin_unlock(&chan->fence.lock); - - if (USE_REFCNT(dev)) { - if (dev_priv->card_type < NV_C0) - BEGIN_RING(chan, 0, NV10_SUBCHAN_REF_CNT, 1); - else - BEGIN_NVC0(chan, 2, 0, NV10_SUBCHAN_REF_CNT, 1); - } else { - BEGIN_RING(chan, NvSubSw, 0x0150, 1); - } - OUT_RING (chan, fence->sequence); - FIRE_RING(chan); - - return 0; -} - -void -nouveau_fence_work(struct nouveau_fence *fence, - void (*work)(void *priv, bool signalled), - void *priv) -{ - BUG_ON(fence->work); - - spin_lock(&fence->channel->fence.lock); - - if (fence->signalled) { - work(priv, true); - } else { - fence->work = work; - fence->priv = priv; - } - - spin_unlock(&fence->channel->fence.lock); -} - -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; + return ret; } bool -__nouveau_fence_signalled(void *sync_obj, void *sync_arg) +nouveau_fence_done(struct nouveau_fence *fence) { - struct nouveau_fence *fence = nouveau_fence(sync_obj); - struct nouveau_channel *chan = fence->channel; - - if (fence->signalled) - return true; - - nouveau_fence_update(chan); - return fence->signalled; + if (fence->channel) + nouveau_fence_update(fence->channel); + return !fence->channel; } int -__nouveau_fence_wait(void *sync_obj, void *sync_arg, bool lazy, bool intr) +nouveau_fence_wait(struct nouveau_fence *fence, bool lazy, bool intr) { - unsigned long timeout = jiffies + (3 * DRM_HZ); unsigned long sleep_time = NSEC_PER_MSEC / 1000; ktime_t t; int ret = 0; - while (1) { - if (__nouveau_fence_signalled(sync_obj, sync_arg)) - break; - - if (time_after_eq(jiffies, timeout)) { + while (!nouveau_fence_done(fence)) { + if (fence->timeout && time_after_eq(jiffies, fence->timeout)) { ret = -EBUSY; break; } - __set_current_state(intr ? TASK_INTERRUPTIBLE - : TASK_UNINTERRUPTIBLE); + __set_current_state(intr ? TASK_INTERRUPTIBLE : + TASK_UNINTERRUPTIBLE); if (lazy) { t = ktime_set(0, sleep_time); schedule_hrtimeout(&t, HRTIMER_MODE_REL); @@ -261,354 +141,72 @@ __nouveau_fence_wait(void *sync_obj, void *sync_arg, bool lazy, bool intr) } __set_current_state(TASK_RUNNING); - return ret; } -static struct nouveau_semaphore * -semaphore_alloc(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_semaphore *sema; - int size = (dev_priv->chipset < 0x84) ? 4 : 16; - int ret, i; - - if (!USE_SEMA(dev)) - return NULL; - - sema = kmalloc(sizeof(*sema), GFP_KERNEL); - if (!sema) - goto fail; - - ret = drm_mm_pre_get(&dev_priv->fence.heap); - if (ret) - goto fail; - - spin_lock(&dev_priv->fence.lock); - sema->mem = drm_mm_search_free(&dev_priv->fence.heap, size, 0, 0); - if (sema->mem) - sema->mem = drm_mm_get_block_atomic(sema->mem, size, 0); - spin_unlock(&dev_priv->fence.lock); - - if (!sema->mem) - goto fail; - - kref_init(&sema->ref); - sema->dev = dev; - for (i = sema->mem->start; i < sema->mem->start + size; i += 4) - nouveau_bo_wr32(dev_priv->fence.bo, i / 4, 0); - - return sema; -fail: - kfree(sema); - return NULL; -} - -static void -semaphore_free(struct kref *ref) -{ - struct nouveau_semaphore *sema = - container_of(ref, struct nouveau_semaphore, ref); - struct drm_nouveau_private *dev_priv = sema->dev->dev_private; - - spin_lock(&dev_priv->fence.lock); - drm_mm_put_block(sema->mem); - spin_unlock(&dev_priv->fence.lock); - - kfree(sema); -} - -static void -semaphore_work(void *priv, bool signalled) -{ - struct nouveau_semaphore *sema = priv; - struct drm_nouveau_private *dev_priv = sema->dev->dev_private; - - if (unlikely(!signalled)) - nouveau_bo_wr32(dev_priv->fence.bo, sema->mem->start / 4, 1); - - kref_put(&sema->ref, semaphore_free); -} - -static int -semaphore_acquire(struct nouveau_channel *chan, struct nouveau_semaphore *sema) -{ - struct drm_nouveau_private *dev_priv = chan->dev->dev_private; - struct nouveau_fence *fence = NULL; - u64 offset = chan->fence.vma.offset + sema->mem->start; - int ret; - - if (dev_priv->chipset < 0x84) { - ret = RING_SPACE(chan, 4); - if (ret) - return ret; - - BEGIN_RING(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 3); - OUT_RING (chan, NvSema); - OUT_RING (chan, offset); - OUT_RING (chan, 1); - } else - if (dev_priv->chipset < 0xc0) { - ret = RING_SPACE(chan, 7); - if (ret) - return ret; - - BEGIN_RING(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1); - OUT_RING (chan, chan->vram_handle); - BEGIN_RING(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); - OUT_RING (chan, upper_32_bits(offset)); - OUT_RING (chan, lower_32_bits(offset)); - OUT_RING (chan, 1); - OUT_RING (chan, 1); /* ACQUIRE_EQ */ - } else { - ret = RING_SPACE(chan, 5); - if (ret) - return ret; - - BEGIN_NVC0(chan, 2, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); - OUT_RING (chan, upper_32_bits(offset)); - OUT_RING (chan, lower_32_bits(offset)); - OUT_RING (chan, 1); - OUT_RING (chan, 0x1001); /* ACQUIRE_EQ */ - } - - /* Delay semaphore destruction until its work is done */ - ret = nouveau_fence_new(chan, &fence, true); - if (ret) - return ret; - - kref_get(&sema->ref); - nouveau_fence_work(fence, semaphore_work, sema); - nouveau_fence_unref(&fence); - return 0; -} - -static int -semaphore_release(struct nouveau_channel *chan, struct nouveau_semaphore *sema) -{ - struct drm_nouveau_private *dev_priv = chan->dev->dev_private; - struct nouveau_fence *fence = NULL; - u64 offset = chan->fence.vma.offset + sema->mem->start; - int ret; - - if (dev_priv->chipset < 0x84) { - ret = RING_SPACE(chan, 5); - if (ret) - return ret; - - BEGIN_RING(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 2); - OUT_RING (chan, NvSema); - OUT_RING (chan, offset); - BEGIN_RING(chan, 0, NV11_SUBCHAN_SEMAPHORE_RELEASE, 1); - OUT_RING (chan, 1); - } else - if (dev_priv->chipset < 0xc0) { - ret = RING_SPACE(chan, 7); - if (ret) - return ret; - - BEGIN_RING(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1); - OUT_RING (chan, chan->vram_handle); - BEGIN_RING(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); - OUT_RING (chan, upper_32_bits(offset)); - OUT_RING (chan, lower_32_bits(offset)); - OUT_RING (chan, 1); - OUT_RING (chan, 2); /* RELEASE */ - } else { - ret = RING_SPACE(chan, 5); - if (ret) - return ret; - - BEGIN_NVC0(chan, 2, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); - OUT_RING (chan, upper_32_bits(offset)); - OUT_RING (chan, lower_32_bits(offset)); - OUT_RING (chan, 1); - OUT_RING (chan, 0x1002); /* RELEASE */ - } - - /* Delay semaphore destruction until its work is done */ - ret = nouveau_fence_new(chan, &fence, true); - if (ret) - return ret; - - kref_get(&sema->ref); - nouveau_fence_work(fence, semaphore_work, sema); - nouveau_fence_unref(&fence); - return 0; -} - int -nouveau_fence_sync(struct nouveau_fence *fence, - struct nouveau_channel *wchan) +nouveau_fence_sync(struct nouveau_fence *fence, struct nouveau_channel *chan) { - struct nouveau_channel *chan = nouveau_fence_channel(fence); - struct drm_device *dev = wchan->dev; - struct nouveau_semaphore *sema; + struct drm_device *dev = chan->dev; + struct nouveau_fence_priv *priv = nv_engine(dev, NVOBJ_ENGINE_FENCE); + struct nouveau_channel *prev; int ret = 0; - if (likely(!chan || chan == wchan || - nouveau_fence_signalled(fence))) - goto out; - - sema = semaphore_alloc(dev); - if (!sema) { - /* Early card or broken userspace, fall back to - * software sync. */ - ret = nouveau_fence_wait(fence, true, false); - goto out; - } - - /* try to take chan's mutex, if we can't take it right away - * we have to fallback to software sync to prevent locking - * order issues - */ - if (!mutex_trylock(&chan->mutex)) { - ret = nouveau_fence_wait(fence, true, false); - goto out_unref; + prev = fence ? nouveau_channel_get_unlocked(fence->channel) : NULL; + if (prev) { + if (unlikely(prev != chan && !nouveau_fence_done(fence))) { + ret = priv->sync(fence, prev, chan); + if (unlikely(ret)) + ret = nouveau_fence_wait(fence, true, false); + } + nouveau_channel_put_unlocked(&prev); } - /* Make wchan wait until it gets signalled */ - ret = semaphore_acquire(wchan, sema); - if (ret) - goto out_unlock; - - /* Signal the semaphore from chan */ - ret = semaphore_release(chan, sema); - -out_unlock: - mutex_unlock(&chan->mutex); -out_unref: - kref_put(&sema->ref, semaphore_free); -out: - if (chan) - nouveau_channel_put_unlocked(&chan); return ret; } -int -__nouveau_fence_flush(void *sync_obj, void *sync_arg) +static void +nouveau_fence_del(struct kref *kref) { - return 0; + struct nouveau_fence *fence = container_of(kref, typeof(*fence), kref); + kfree(fence); } -int -nouveau_fence_channel_init(struct nouveau_channel *chan) +void +nouveau_fence_unref(struct nouveau_fence **pfence) { - struct drm_device *dev = chan->dev; - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpuobj *obj = NULL; - int ret; - - if (dev_priv->card_type < NV_C0) { - /* Create an NV_SW object for various sync purposes */ - ret = nouveau_gpuobj_gr_new(chan, NvSw, NV_SW); - if (ret) - return ret; - - ret = RING_SPACE(chan, 2); - if (ret) - return ret; - - BEGIN_RING(chan, NvSubSw, NV01_SUBCHAN_OBJECT, 1); - OUT_RING (chan, NvSw); - FIRE_RING (chan); - } - - /* Setup area of memory shared between all channels for x-chan sync */ - if (USE_SEMA(dev) && dev_priv->chipset < 0x84) { - struct ttm_mem_reg *mem = &dev_priv->fence.bo->bo.mem; - - ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_FROM_MEMORY, - mem->start << PAGE_SHIFT, - mem->size, NV_MEM_ACCESS_RW, - NV_MEM_TARGET_VRAM, &obj); - if (ret) - return ret; - - ret = nouveau_ramht_insert(chan, NvSema, obj); - nouveau_gpuobj_ref(NULL, &obj); - if (ret) - return ret; - } else - if (USE_SEMA(dev)) { - /* map fence bo into channel's vm */ - ret = nouveau_bo_vma_add(dev_priv->fence.bo, chan->vm, - &chan->fence.vma); - if (ret) - return ret; - } - - atomic_set(&chan->fence.last_sequence_irq, 0); - return 0; + if (*pfence) + kref_put(&(*pfence)->kref, nouveau_fence_del); + *pfence = NULL; } -void -nouveau_fence_channel_fini(struct nouveau_channel *chan) +struct nouveau_fence * +nouveau_fence_ref(struct nouveau_fence *fence) { - struct drm_nouveau_private *dev_priv = chan->dev->dev_private; - struct nouveau_fence *tmp, *fence; - - spin_lock(&chan->fence.lock); - list_for_each_entry_safe(fence, tmp, &chan->fence.pending, entry) { - fence->signalled = true; - list_del(&fence->entry); - - if (unlikely(fence->work)) - fence->work(fence->priv, false); - - kref_put(&fence->refcount, nouveau_fence_del); - } - spin_unlock(&chan->fence.lock); - - nouveau_bo_vma_del(dev_priv->fence.bo, &chan->fence.vma); + kref_get(&fence->kref); + return fence; } int -nouveau_fence_init(struct drm_device *dev) +nouveau_fence_new(struct nouveau_channel *chan, struct nouveau_fence **pfence) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - int size = (dev_priv->chipset < 0x84) ? 4096 : 16384; - int ret; - - /* Create a shared VRAM heap for cross-channel sync. */ - if (USE_SEMA(dev)) { - ret = nouveau_bo_new(dev, size, 0, TTM_PL_FLAG_VRAM, - 0, 0, &dev_priv->fence.bo); - if (ret) - return ret; + struct nouveau_fence *fence; + int ret = 0; - ret = nouveau_bo_pin(dev_priv->fence.bo, TTM_PL_FLAG_VRAM); - if (ret) - goto fail; + if (unlikely(!chan->engctx[NVOBJ_ENGINE_FENCE])) + return -ENODEV; - ret = nouveau_bo_map(dev_priv->fence.bo); - if (ret) - goto fail; + fence = kzalloc(sizeof(*fence), GFP_KERNEL); + if (!fence) + return -ENOMEM; + kref_init(&fence->kref); - ret = drm_mm_init(&dev_priv->fence.heap, 0, - dev_priv->fence.bo->bo.mem.size); + if (chan) { + ret = nouveau_fence_emit(fence, chan); if (ret) - goto fail; - - spin_lock_init(&dev_priv->fence.lock); + nouveau_fence_unref(&fence); } - return 0; -fail: - nouveau_bo_unmap(dev_priv->fence.bo); - nouveau_bo_ref(NULL, &dev_priv->fence.bo); + *pfence = fence; return ret; } - -void -nouveau_fence_fini(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - - if (USE_SEMA(dev)) { - drm_mm_takedown(&dev_priv->fence.heap); - nouveau_bo_unmap(dev_priv->fence.bo); - nouveau_bo_unpin(dev_priv->fence.bo); - nouveau_bo_ref(NULL, &dev_priv->fence.bo); - } -} diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.h b/drivers/gpu/drm/nouveau/nouveau_fence.h new file mode 100644 index 00000000000..82ba733393a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_fence.h @@ -0,0 +1,52 @@ +#ifndef __NOUVEAU_FENCE_H__ +#define __NOUVEAU_FENCE_H__ + +struct nouveau_fence { + struct list_head head; + struct kref kref; + + struct nouveau_channel *channel; + unsigned long timeout; + u32 sequence; + + void (*work)(void *priv, bool signalled); + void *priv; +}; + +int nouveau_fence_new(struct nouveau_channel *, struct nouveau_fence **); +struct nouveau_fence * +nouveau_fence_ref(struct nouveau_fence *); +void nouveau_fence_unref(struct nouveau_fence **); + +int nouveau_fence_emit(struct nouveau_fence *, struct nouveau_channel *); +bool nouveau_fence_done(struct nouveau_fence *); +int nouveau_fence_wait(struct nouveau_fence *, bool lazy, bool intr); +int nouveau_fence_sync(struct nouveau_fence *, struct nouveau_channel *); +void nouveau_fence_idle(struct nouveau_channel *); +void nouveau_fence_update(struct nouveau_channel *); + +struct nouveau_fence_chan { + struct list_head pending; + spinlock_t lock; + u32 sequence; +}; + +struct nouveau_fence_priv { + struct nouveau_exec_engine engine; + int (*emit)(struct nouveau_fence *); + int (*sync)(struct nouveau_fence *, struct nouveau_channel *, + struct nouveau_channel *); + u32 (*read)(struct nouveau_channel *); +}; + +void nouveau_fence_context_new(struct nouveau_fence_chan *); +void nouveau_fence_context_del(struct nouveau_fence_chan *); + +int nv04_fence_create(struct drm_device *dev); +int nv04_fence_mthd(struct nouveau_channel *, u32, u32, u32); + +int nv10_fence_create(struct drm_device *dev); +int nv84_fence_create(struct drm_device *dev); +int nvc0_fence_create(struct drm_device *dev); + +#endif diff --git a/drivers/gpu/drm/nouveau/nouveau_fifo.h b/drivers/gpu/drm/nouveau/nouveau_fifo.h new file mode 100644 index 00000000000..ce99cab2f25 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_fifo.h @@ -0,0 +1,32 @@ +#ifndef __NOUVEAU_FIFO_H__ +#define __NOUVEAU_FIFO_H__ + +struct nouveau_fifo_priv { + struct nouveau_exec_engine base; + u32 channels; +}; + +struct nouveau_fifo_chan { +}; + +bool nv04_fifo_cache_pull(struct drm_device *, bool); +void nv04_fifo_context_del(struct nouveau_channel *, int); +int nv04_fifo_fini(struct drm_device *, int, bool); +int nv04_fifo_init(struct drm_device *, int); +void nv04_fifo_isr(struct drm_device *); +void nv04_fifo_destroy(struct drm_device *, int); + +void nv50_fifo_playlist_update(struct drm_device *); +void nv50_fifo_destroy(struct drm_device *, int); +void nv50_fifo_tlb_flush(struct drm_device *, int); + +int nv04_fifo_create(struct drm_device *); +int nv10_fifo_create(struct drm_device *); +int nv17_fifo_create(struct drm_device *); +int nv40_fifo_create(struct drm_device *); +int nv50_fifo_create(struct drm_device *); +int nv84_fifo_create(struct drm_device *); +int nvc0_fifo_create(struct drm_device *); +int nve0_fifo_create(struct drm_device *); + +#endif diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index ed52a6f4161..30f54231694 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -23,12 +23,14 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ +#include <linux/dma-buf.h> #include "drmP.h" #include "drm.h" #include "nouveau_drv.h" #include "nouveau_drm.h" #include "nouveau_dma.h" +#include "nouveau_fence.h" #define nouveau_gem_pushbuf_sync(chan) 0 @@ -53,6 +55,9 @@ nouveau_gem_object_del(struct drm_gem_object *gem) nouveau_bo_unpin(nvbo); } + if (gem->import_attach) + drm_prime_gem_destroy(gem, nvbo->bo.sg); + ttm_bo_unref(&bo); drm_gem_object_release(gem); @@ -139,7 +144,7 @@ nouveau_gem_new(struct drm_device *dev, int size, int align, uint32_t domain, flags |= TTM_PL_FLAG_SYSTEM; ret = nouveau_bo_new(dev, size, align, flags, tile_mode, - tile_flags, pnvbo); + tile_flags, NULL, pnvbo); if (ret) return ret; nvbo = *pnvbo; @@ -704,7 +709,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data, } if (chan->dma.ib_max) { - ret = nouveau_dma_wait(chan, req->nr_push + 1, 6); + ret = nouveau_dma_wait(chan, req->nr_push + 1, 16); if (ret) { NV_INFO(dev, "nv50cal_space: %d\n", ret); goto out; @@ -774,7 +779,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data, } } - ret = nouveau_fence_new(chan, &fence, true); + ret = nouveau_fence_new(chan, &fence); if (ret) { NV_ERROR(dev, "error fencing pushbuf: %d\n", ret); WIND_RING(chan); diff --git a/drivers/gpu/drm/nouveau/nouveau_gpio.c b/drivers/gpu/drm/nouveau/nouveau_gpio.c index a580cc62337..82c19e82ff0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gpio.c +++ b/drivers/gpu/drm/nouveau/nouveau_gpio.c @@ -387,7 +387,7 @@ nouveau_gpio_reset(struct drm_device *dev) if (dev_priv->card_type >= NV_D0) { nv_mask(dev, 0x00d610 + (line * 4), 0xff, unk0); if (unk1--) - nv_mask(dev, 0x00d640 + (unk1 * 4), 0xff, line); + nv_mask(dev, 0x00d740 + (unk1 * 4), 0xff, line); } else if (dev_priv->card_type >= NV_50) { static const u32 regs[] = { 0xe100, 0xe28c }; diff --git a/drivers/gpu/drm/nouveau/nouveau_grctx.h b/drivers/gpu/drm/nouveau/nouveau_grctx.h index 86c2e374e93..b0795ececbd 100644 --- a/drivers/gpu/drm/nouveau/nouveau_grctx.h +++ b/drivers/gpu/drm/nouveau/nouveau_grctx.h @@ -18,7 +18,6 @@ struct nouveau_grctx { uint32_t ctxvals_base; }; -#ifdef CP_CTX static inline void cp_out(struct nouveau_grctx *ctx, uint32_t inst) { @@ -88,10 +87,8 @@ _cp_bra(struct nouveau_grctx *ctx, u32 mod, int flag, int state, int name) (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) @@ -128,6 +125,5 @@ gr_def(struct nouveau_grctx *ctx, uint32_t reg, uint32_t val) nv_wo32(ctx->data, reg * 4, val); } -#endif #endif diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.c b/drivers/gpu/drm/nouveau/nouveau_hw.c index ba896e54b79..b87ad3bd773 100644 --- a/drivers/gpu/drm/nouveau/nouveau_hw.c +++ b/drivers/gpu/drm/nouveau/nouveau_hw.c @@ -1018,11 +1018,6 @@ nv_load_state_ext(struct drm_device *dev, int head, } NVWriteCRTC(dev, head, NV_PCRTC_START, regp->fb_start); - - /* Enable vblank interrupts. */ - NVWriteCRTC(dev, head, NV_PCRTC_INTR_EN_0, - (dev->vblank_enabled[head] ? 1 : 0)); - NVWriteCRTC(dev, head, NV_PCRTC_INTR_0, NV_PCRTC_INTR_0_VBLANK); } static void diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c index e2be95af2e5..77e564667b5 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.c +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c @@ -29,10 +29,6 @@ #include "nouveau_i2c.h" #include "nouveau_hw.h" -#define T_TIMEOUT 2200000 -#define T_RISEFALL 1000 -#define T_HOLD 5000 - static void i2c_drive_scl(void *data, int state) { @@ -113,175 +109,6 @@ i2c_sense_sda(void *data) return 0; } -static void -i2c_delay(struct nouveau_i2c_chan *port, u32 nsec) -{ - udelay((nsec + 500) / 1000); -} - -static bool -i2c_raise_scl(struct nouveau_i2c_chan *port) -{ - u32 timeout = T_TIMEOUT / T_RISEFALL; - - i2c_drive_scl(port, 1); - do { - i2c_delay(port, T_RISEFALL); - } while (!i2c_sense_scl(port) && --timeout); - - return timeout != 0; -} - -static int -i2c_start(struct nouveau_i2c_chan *port) -{ - int ret = 0; - - port->state = i2c_sense_scl(port); - port->state |= i2c_sense_sda(port) << 1; - if (port->state != 3) { - i2c_drive_scl(port, 0); - i2c_drive_sda(port, 1); - if (!i2c_raise_scl(port)) - ret = -EBUSY; - } - - i2c_drive_sda(port, 0); - i2c_delay(port, T_HOLD); - i2c_drive_scl(port, 0); - i2c_delay(port, T_HOLD); - return ret; -} - -static void -i2c_stop(struct nouveau_i2c_chan *port) -{ - i2c_drive_scl(port, 0); - i2c_drive_sda(port, 0); - i2c_delay(port, T_RISEFALL); - - i2c_drive_scl(port, 1); - i2c_delay(port, T_HOLD); - i2c_drive_sda(port, 1); - i2c_delay(port, T_HOLD); -} - -static int -i2c_bitw(struct nouveau_i2c_chan *port, int sda) -{ - i2c_drive_sda(port, sda); - i2c_delay(port, T_RISEFALL); - - if (!i2c_raise_scl(port)) - return -ETIMEDOUT; - i2c_delay(port, T_HOLD); - - i2c_drive_scl(port, 0); - i2c_delay(port, T_HOLD); - return 0; -} - -static int -i2c_bitr(struct nouveau_i2c_chan *port) -{ - int sda; - - i2c_drive_sda(port, 1); - i2c_delay(port, T_RISEFALL); - - if (!i2c_raise_scl(port)) - return -ETIMEDOUT; - i2c_delay(port, T_HOLD); - - sda = i2c_sense_sda(port); - - i2c_drive_scl(port, 0); - i2c_delay(port, T_HOLD); - return sda; -} - -static int -i2c_get_byte(struct nouveau_i2c_chan *port, u8 *byte, bool last) -{ - int i, bit; - - *byte = 0; - for (i = 7; i >= 0; i--) { - bit = i2c_bitr(port); - if (bit < 0) - return bit; - *byte |= bit << i; - } - - return i2c_bitw(port, last ? 1 : 0); -} - -static int -i2c_put_byte(struct nouveau_i2c_chan *port, u8 byte) -{ - int i, ret; - for (i = 7; i >= 0; i--) { - ret = i2c_bitw(port, !!(byte & (1 << i))); - if (ret < 0) - return ret; - } - - ret = i2c_bitr(port); - if (ret == 1) /* nack */ - ret = -EIO; - return ret; -} - -static int -i2c_addr(struct nouveau_i2c_chan *port, struct i2c_msg *msg) -{ - u32 addr = msg->addr << 1; - if (msg->flags & I2C_M_RD) - addr |= 1; - return i2c_put_byte(port, addr); -} - -static int -i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) -{ - struct nouveau_i2c_chan *port = (struct nouveau_i2c_chan *)adap; - struct i2c_msg *msg = msgs; - int ret = 0, mcnt = num; - - while (!ret && mcnt--) { - u8 remaining = msg->len; - u8 *ptr = msg->buf; - - ret = i2c_start(port); - if (ret == 0) - ret = i2c_addr(port, msg); - - if (msg->flags & I2C_M_RD) { - while (!ret && remaining--) - ret = i2c_get_byte(port, ptr++, !remaining); - } else { - while (!ret && remaining--) - ret = i2c_put_byte(port, *ptr++); - } - - msg++; - } - - i2c_stop(port); - return (ret < 0) ? ret : num; -} - -static u32 -i2c_bit_func(struct i2c_adapter *adap) -{ - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; -} - -const struct i2c_algorithm nouveau_i2c_bit_algo = { - .master_xfer = i2c_bit_xfer, - .functionality = i2c_bit_func -}; - static const uint32_t nv50_i2c_port[] = { 0x00e138, 0x00e150, 0x00e168, 0x00e180, 0x00e254, 0x00e274, 0x00e764, 0x00e780, @@ -384,12 +211,10 @@ nouveau_i2c_init(struct drm_device *dev) case 0: /* NV04:NV50 */ port->drive = entry[0]; port->sense = entry[1]; - port->adapter.algo = &nouveau_i2c_bit_algo; break; case 4: /* NV4E */ port->drive = 0x600800 + entry[1]; port->sense = port->drive; - port->adapter.algo = &nouveau_i2c_bit_algo; break; case 5: /* NV50- */ port->drive = entry[0] & 0x0f; @@ -402,7 +227,6 @@ nouveau_i2c_init(struct drm_device *dev) port->drive = 0x00d014 + (port->drive * 0x20); port->sense = port->drive; } - port->adapter.algo = &nouveau_i2c_bit_algo; break; case 6: /* NV50- DP AUX */ port->drive = entry[0]; @@ -413,7 +237,7 @@ nouveau_i2c_init(struct drm_device *dev) break; } - if (!port->adapter.algo) { + if (!port->adapter.algo && !port->drive) { NV_ERROR(dev, "I2C%d: type %d index %x/%x unknown\n", i, port->type, port->drive, port->sense); kfree(port); @@ -429,7 +253,26 @@ nouveau_i2c_init(struct drm_device *dev) port->dcb = ROM32(entry[0]); i2c_set_adapdata(&port->adapter, i2c); - ret = i2c_add_adapter(&port->adapter); + if (port->adapter.algo != &nouveau_dp_i2c_algo) { + port->adapter.algo_data = &port->bit; + port->bit.udelay = 10; + port->bit.timeout = usecs_to_jiffies(2200); + port->bit.data = port; + port->bit.setsda = i2c_drive_sda; + port->bit.setscl = i2c_drive_scl; + port->bit.getsda = i2c_sense_sda; + port->bit.getscl = i2c_sense_scl; + + i2c_drive_scl(port, 0); + i2c_drive_sda(port, 1); + i2c_drive_scl(port, 1); + + ret = i2c_bit_add_bus(&port->adapter); + } else { + port->adapter.algo = &nouveau_dp_i2c_algo; + ret = i2c_add_adapter(&port->adapter); + } + if (ret) { NV_ERROR(dev, "I2C%d: failed register: %d\n", i, ret); kfree(port); diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.h b/drivers/gpu/drm/nouveau/nouveau_i2c.h index 4d2e4e9031b..1d083893a4d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.h +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.h @@ -34,6 +34,7 @@ struct nouveau_i2c_chan { struct i2c_adapter adapter; struct drm_device *dev; + struct i2c_algo_bit_data bit; struct list_head head; u8 index; u8 type; diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index b08065f981d..5b498ea32e1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -39,6 +39,8 @@ #include "nouveau_pm.h" #include "nouveau_mm.h" #include "nouveau_vm.h" +#include "nouveau_fifo.h" +#include "nouveau_fence.h" /* * NV10-NV40 tiling helpers @@ -50,7 +52,6 @@ nv10_mem_update_tile_region(struct drm_device *dev, uint32_t size, uint32_t pitch, uint32_t flags) { 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; int i = tile - dev_priv->tile.reg, j; unsigned long save; @@ -64,8 +65,8 @@ nv10_mem_update_tile_region(struct drm_device *dev, pfb->init_tile_region(dev, i, addr, size, pitch, flags); spin_lock_irqsave(&dev_priv->context_switch_lock, save); - pfifo->reassign(dev, false); - pfifo->cache_pull(dev, false); + nv_wr32(dev, NV03_PFIFO_CACHES, 0); + nv04_fifo_cache_pull(dev, false); nouveau_wait_for_idle(dev); @@ -75,8 +76,8 @@ nv10_mem_update_tile_region(struct drm_device *dev, dev_priv->eng[j]->set_tile_region(dev, i); } - pfifo->cache_pull(dev, true); - pfifo->reassign(dev, true); + nv04_fifo_cache_pull(dev, true); + nv_wr32(dev, NV03_PFIFO_CACHES, 1); spin_unlock_irqrestore(&dev_priv->context_switch_lock, save); } @@ -89,7 +90,7 @@ nv10_mem_get_tile_region(struct drm_device *dev, int i) spin_lock(&dev_priv->tile.lock); if (!tile->used && - (!tile->fence || nouveau_fence_signalled(tile->fence))) + (!tile->fence || nouveau_fence_done(tile->fence))) tile->used = true; else tile = NULL; @@ -416,7 +417,7 @@ nouveau_mem_vram_init(struct drm_device *dev) if (dev_priv->card_type < NV_50) { ret = nouveau_bo_new(dev, 256*1024, 0, TTM_PL_FLAG_VRAM, - 0, 0, &dev_priv->vga_ram); + 0, 0, NULL, &dev_priv->vga_ram); if (ret == 0) ret = nouveau_bo_pin(dev_priv->vga_ram, TTM_PL_FLAG_VRAM); @@ -843,6 +844,7 @@ nouveau_mem_timing_calc(struct drm_device *dev, u32 freq, ret = nv50_mem_timing_calc(dev, freq, e, len, boot, t); break; case NV_C0: + case NV_D0: ret = nvc0_mem_timing_calc(dev, freq, e, len, boot, t); break; default: @@ -977,6 +979,8 @@ nouveau_mem_exec(struct nouveau_mem_exec_func *exec, break; case NV_MEM_TYPE_DDR3: tDLLK = 12000; + tCKSRE = 2000; + tXS = 1000; mr1_dlloff = 0x00000001; break; case NV_MEM_TYPE_GDDR3: @@ -1023,6 +1027,7 @@ nouveau_mem_exec(struct nouveau_mem_exec_func *exec, exec->refresh_self(exec, false); exec->refresh_auto(exec, true); exec->wait(exec, tXS); + exec->wait(exec, tXS); /* update MRs */ if (mr[2] != info->mr[2]) { diff --git a/drivers/gpu/drm/nouveau/nouveau_object.c b/drivers/gpu/drm/nouveau/nouveau_object.c index cc419fae794..b190cc01c82 100644 --- a/drivers/gpu/drm/nouveau/nouveau_object.c +++ b/drivers/gpu/drm/nouveau/nouveau_object.c @@ -34,9 +34,10 @@ #include "drm.h" #include "nouveau_drv.h" #include "nouveau_drm.h" +#include "nouveau_fifo.h" #include "nouveau_ramht.h" +#include "nouveau_software.h" #include "nouveau_vm.h" -#include "nv50_display.h" struct nouveau_gpuobj_method { struct list_head head; @@ -120,12 +121,13 @@ nouveau_gpuobj_mthd_call2(struct drm_device *dev, int chid, u32 class, u32 mthd, u32 data) { struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_fifo_priv *pfifo = nv_engine(dev, NVOBJ_ENGINE_FIFO); struct nouveau_channel *chan = NULL; unsigned long flags; int ret = -EINVAL; spin_lock_irqsave(&dev_priv->channels.lock, flags); - if (chid >= 0 && chid < dev_priv->engine.fifo.channels) + if (chid >= 0 && chid < pfifo->channels) chan = dev_priv->channels.ptr[chid]; if (chan) ret = nouveau_gpuobj_mthd_call(chan, class, mthd, data); @@ -133,37 +135,6 @@ nouveau_gpuobj_mthd_call2(struct drm_device *dev, int chid, return ret; } -/* 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: -*/ - int nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan, uint32_t size, int align, uint32_t flags, @@ -267,7 +238,7 @@ nouveau_gpuobj_takedown(struct drm_device *dev) kfree(oc); } - BUG_ON(!list_empty(&dev_priv->gpuobj_list)); + WARN_ON(!list_empty(&dev_priv->gpuobj_list)); } @@ -361,34 +332,6 @@ nouveau_gpuobj_new_fake(struct drm_device *dev, u32 pinst, u64 vinst, return 0; } -/* - 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. -*/ - void nv50_gpuobj_dma_init(struct nouveau_gpuobj *obj, u32 offset, int class, u64 base, u64 size, int target, int access, @@ -540,82 +483,6 @@ nouveau_gpuobj_dma_new(struct nouveau_channel *chan, int class, u64 base, return 0; } -/* 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? -*/ -static int -nouveau_gpuobj_sw_new(struct nouveau_channel *chan, u32 handle, u16 class) -{ - struct drm_nouveau_private *dev_priv = chan->dev->dev_private; - struct nouveau_gpuobj *gpuobj; - int ret; - - gpuobj = kzalloc(sizeof(*gpuobj), GFP_KERNEL); - if (!gpuobj) - return -ENOMEM; - gpuobj->dev = chan->dev; - gpuobj->engine = NVOBJ_ENGINE_SW; - gpuobj->class = class; - kref_init(&gpuobj->refcount); - gpuobj->cinst = 0x40; - - spin_lock(&dev_priv->ramin_lock); - list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list); - spin_unlock(&dev_priv->ramin_lock); - - ret = nouveau_ramht_insert(chan, handle, gpuobj); - nouveau_gpuobj_ref(NULL, &gpuobj); - return ret; -} - int nouveau_gpuobj_gr_new(struct nouveau_channel *chan, u32 handle, int class) { @@ -632,9 +499,6 @@ nouveau_gpuobj_gr_new(struct nouveau_channel *chan, u32 handle, int class) if (oc->id != class) continue; - if (oc->engine == NVOBJ_ENGINE_SW) - return nouveau_gpuobj_sw_new(chan, handle, class); - if (!chan->engctx[oc->engine]) { ret = eng->context_new(chan, oc->engine); if (ret) @@ -644,7 +508,6 @@ nouveau_gpuobj_gr_new(struct nouveau_channel *chan, u32 handle, int class) return eng->object_new(chan, oc->engine, handle, class); } - NV_ERROR(dev, "illegal object class: 0x%x\n", class); return -EINVAL; } @@ -693,11 +556,10 @@ nouveau_gpuobj_channel_init_pramin(struct nouveau_channel *chan) static int nvc0_gpuobj_channel_init(struct nouveau_channel *chan, struct nouveau_vm *vm) { - struct drm_nouveau_private *dev_priv = chan->dev->dev_private; struct drm_device *dev = chan->dev; struct nouveau_gpuobj *pgd = NULL; struct nouveau_vm_pgd *vpgd; - int ret, i; + int ret; ret = nouveau_gpuobj_new(dev, NULL, 4096, 0x1000, 0, &chan->ramin); if (ret) @@ -722,19 +584,6 @@ nvc0_gpuobj_channel_init(struct nouveau_channel *chan, struct nouveau_vm *vm) nv_wo32(chan->ramin, 0x0208, 0xffffffff); nv_wo32(chan->ramin, 0x020c, 0x000000ff); - /* map display semaphore buffers into channel's vm */ - for (i = 0; i < dev->mode_config.num_crtc; i++) { - struct nouveau_bo *bo; - if (dev_priv->card_type >= NV_D0) - bo = nvd0_display_crtc_sema(dev, i); - else - bo = nv50_display(dev)->crtc[i].sem.bo; - - ret = nouveau_bo_vma_add(bo, chan->vm, &chan->dispc_vma[i]); - if (ret) - return ret; - } - return 0; } @@ -747,7 +596,7 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan, struct nouveau_fpriv *fpriv = nouveau_fpriv(chan->file_priv); struct nouveau_vm *vm = fpriv ? fpriv->vm : dev_priv->chan_vm; struct nouveau_gpuobj *vram = NULL, *tt = NULL; - int ret, i; + int ret; NV_DEBUG(dev, "ch%d vram=0x%08x tt=0x%08x\n", chan->id, vram_h, tt_h); if (dev_priv->card_type >= NV_C0) @@ -795,25 +644,6 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan, nouveau_gpuobj_ref(NULL, &ramht); if (ret) return ret; - - /* dma objects for display sync channel semaphore blocks */ - for (i = 0; i < dev->mode_config.num_crtc; i++) { - struct nouveau_gpuobj *sem = NULL; - struct nv50_display_crtc *dispc = - &nv50_display(dev)->crtc[i]; - u64 offset = dispc->sem.bo->bo.offset; - - ret = nouveau_gpuobj_dma_new(chan, 0x3d, offset, 0xfff, - NV_MEM_ACCESS_RW, - NV_MEM_TARGET_VRAM, &sem); - if (ret) - return ret; - - ret = nouveau_ramht_insert(chan, NvEvoSema0 + i, sem); - nouveau_gpuobj_ref(NULL, &sem); - if (ret) - return ret; - } } /* VRAM ctxdma */ @@ -873,25 +703,7 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan, void nouveau_gpuobj_channel_takedown(struct nouveau_channel *chan) { - struct drm_device *dev = chan->dev; - struct drm_nouveau_private *dev_priv = dev->dev_private; - int i; - - NV_DEBUG(dev, "ch%d\n", chan->id); - - if (dev_priv->card_type >= NV_D0) { - for (i = 0; i < dev->mode_config.num_crtc; i++) { - struct nouveau_bo *bo = nvd0_display_crtc_sema(dev, i); - nouveau_bo_vma_del(bo, &chan->dispc_vma[i]); - } - } else - if (dev_priv->card_type >= NV_50) { - struct nv50_display *disp = nv50_display(dev); - for (i = 0; i < dev->mode_config.num_crtc; i++) { - struct nv50_display_crtc *dispc = &disp->crtc[i]; - nouveau_bo_vma_del(dispc->sem.bo, &chan->dispc_vma[i]); - } - } + NV_DEBUG(chan->dev, "ch%d\n", chan->id); nouveau_vm_ref(NULL, &chan->vm, chan->vm_pd); nouveau_gpuobj_ref(NULL, &chan->vm_pd); @@ -956,6 +768,17 @@ int nouveau_ioctl_grobj_alloc(struct drm_device *dev, void *data, if (init->handle == ~0) return -EINVAL; + /* compatibility with userspace that assumes 506e for all chipsets */ + if (init->class == 0x506e) { + init->class = nouveau_software_class(dev); + if (init->class == 0x906e) + return 0; + } else + if (init->class == 0x906e) { + NV_ERROR(dev, "906e not supported yet\n"); + return -EINVAL; + } + chan = nouveau_channel_get(file_priv, init->channel); if (IS_ERR(chan)) return PTR_ERR(chan); diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index 69a528d106e..ea6acf1c4a7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c @@ -83,7 +83,7 @@ nouveau_perf_entry(struct drm_device *dev, int idx, return NULL; } -static u8 * +u8 * nouveau_perf_rammap(struct drm_device *dev, u32 freq, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) { diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h index 3f82dfea61d..07cac72c72b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.h +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h @@ -61,8 +61,10 @@ int nouveau_voltage_gpio_set(struct drm_device *, int voltage); /* nouveau_perf.c */ void nouveau_perf_init(struct drm_device *); void nouveau_perf_fini(struct drm_device *); -u8 *nouveau_perf_timing(struct drm_device *, u32 freq, u8 *ver, u8 *len); +u8 *nouveau_perf_rammap(struct drm_device *, u32 freq, u8 *ver, + u8 *hdr, u8 *cnt, u8 *len); u8 *nouveau_perf_ramcfg(struct drm_device *, u32 freq, u8 *ver, u8 *len); +u8 *nouveau_perf_timing(struct drm_device *, u32 freq, u8 *ver, u8 *len); /* nouveau_mem.c */ void nouveau_mem_timing_init(struct drm_device *); diff --git a/drivers/gpu/drm/nouveau/nouveau_prime.c b/drivers/gpu/drm/nouveau/nouveau_prime.c new file mode 100644 index 00000000000..c58aab7370c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_prime.c @@ -0,0 +1,163 @@ + +#include "drmP.h" +#include "drm.h" + +#include "nouveau_drv.h" +#include "nouveau_drm.h" +#include "nouveau_dma.h" + +#include <linux/dma-buf.h> + +static struct sg_table *nouveau_gem_map_dma_buf(struct dma_buf_attachment *attachment, + enum dma_data_direction dir) +{ + struct nouveau_bo *nvbo = attachment->dmabuf->priv; + struct drm_device *dev = nvbo->gem->dev; + int npages = nvbo->bo.num_pages; + struct sg_table *sg; + int nents; + + mutex_lock(&dev->struct_mutex); + sg = drm_prime_pages_to_sg(nvbo->bo.ttm->pages, npages); + nents = dma_map_sg(attachment->dev, sg->sgl, sg->nents, dir); + mutex_unlock(&dev->struct_mutex); + return sg; +} + +static void nouveau_gem_unmap_dma_buf(struct dma_buf_attachment *attachment, + struct sg_table *sg, enum dma_data_direction dir) +{ + dma_unmap_sg(attachment->dev, sg->sgl, sg->nents, dir); + sg_free_table(sg); + kfree(sg); +} + +static void nouveau_gem_dmabuf_release(struct dma_buf *dma_buf) +{ + struct nouveau_bo *nvbo = dma_buf->priv; + + if (nvbo->gem->export_dma_buf == dma_buf) { + nvbo->gem->export_dma_buf = NULL; + drm_gem_object_unreference_unlocked(nvbo->gem); + } +} + +static void *nouveau_gem_kmap_atomic(struct dma_buf *dma_buf, unsigned long page_num) +{ + return NULL; +} + +static void nouveau_gem_kunmap_atomic(struct dma_buf *dma_buf, unsigned long page_num, void *addr) +{ + +} +static void *nouveau_gem_kmap(struct dma_buf *dma_buf, unsigned long page_num) +{ + return NULL; +} + +static void nouveau_gem_kunmap(struct dma_buf *dma_buf, unsigned long page_num, void *addr) +{ + +} + +static const struct dma_buf_ops nouveau_dmabuf_ops = { + .map_dma_buf = nouveau_gem_map_dma_buf, + .unmap_dma_buf = nouveau_gem_unmap_dma_buf, + .release = nouveau_gem_dmabuf_release, + .kmap = nouveau_gem_kmap, + .kmap_atomic = nouveau_gem_kmap_atomic, + .kunmap = nouveau_gem_kunmap, + .kunmap_atomic = nouveau_gem_kunmap_atomic, +}; + +static int +nouveau_prime_new(struct drm_device *dev, + size_t size, + struct sg_table *sg, + struct nouveau_bo **pnvbo) +{ + struct nouveau_bo *nvbo; + u32 flags = 0; + int ret; + + flags = TTM_PL_FLAG_TT; + + ret = nouveau_bo_new(dev, size, 0, flags, 0, 0, + sg, pnvbo); + if (ret) + return ret; + nvbo = *pnvbo; + + /* we restrict allowed domains on nv50+ to only the types + * that were requested at creation time. not possibly on + * earlier chips without busting the ABI. + */ + nvbo->valid_domains = NOUVEAU_GEM_DOMAIN_GART; + nvbo->gem = drm_gem_object_alloc(dev, nvbo->bo.mem.size); + if (!nvbo->gem) { + nouveau_bo_ref(NULL, pnvbo); + return -ENOMEM; + } + + nvbo->gem->driver_private = nvbo; + return 0; +} + +struct dma_buf *nouveau_gem_prime_export(struct drm_device *dev, + struct drm_gem_object *obj, int flags) +{ + struct nouveau_bo *nvbo = nouveau_gem_object(obj); + int ret = 0; + + /* pin buffer into GTT */ + ret = nouveau_bo_pin(nvbo, TTM_PL_FLAG_TT); + if (ret) + return ERR_PTR(-EINVAL); + + return dma_buf_export(nvbo, &nouveau_dmabuf_ops, obj->size, flags); +} + +struct drm_gem_object *nouveau_gem_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf) +{ + struct dma_buf_attachment *attach; + struct sg_table *sg; + struct nouveau_bo *nvbo; + int ret; + + if (dma_buf->ops == &nouveau_dmabuf_ops) { + nvbo = dma_buf->priv; + if (nvbo->gem) { + if (nvbo->gem->dev == dev) { + drm_gem_object_reference(nvbo->gem); + return nvbo->gem; + } + } + } + /* need to attach */ + attach = dma_buf_attach(dma_buf, dev->dev); + if (IS_ERR(attach)) + return ERR_PTR(PTR_ERR(attach)); + + sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); + if (IS_ERR(sg)) { + ret = PTR_ERR(sg); + goto fail_detach; + } + + ret = nouveau_prime_new(dev, dma_buf->size, sg, &nvbo); + if (ret) + goto fail_unmap; + + nvbo->gem->import_attach = attach; + + return nvbo->gem; + +fail_unmap: + dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL); +fail_detach: + dma_buf_detach(dma_buf, attach); + return ERR_PTR(ret); +} + diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c index 47f245edf53..38483a042bc 100644 --- a/drivers/gpu/drm/nouveau/nouveau_sgdma.c +++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c @@ -290,7 +290,10 @@ nv50_sgdma_bind(struct ttm_tt *ttm, struct ttm_mem_reg *mem) struct nouveau_mem *node = mem->mm_node; /* noop: bound in move_notify() */ - node->pages = nvbe->ttm.dma_address; + if (ttm->sg) { + node->sg = ttm->sg; + } else + node->pages = nvbe->ttm.dma_address; return 0; } @@ -338,10 +341,10 @@ nouveau_sgdma_init(struct drm_device *dev) u32 aper_size, align; int ret; - if (dev_priv->card_type >= NV_40 && pci_is_pcie(dev->pdev)) + if (dev_priv->card_type >= NV_40) aper_size = 512 * 1024 * 1024; else - aper_size = 64 * 1024 * 1024; + aper_size = 128 * 1024 * 1024; /* Dear NVIDIA, NV44+ would like proper present bits in PTEs for * christmas. The cards before it have them, the cards after diff --git a/drivers/gpu/drm/nouveau/nouveau_software.h b/drivers/gpu/drm/nouveau/nouveau_software.h new file mode 100644 index 00000000000..e60bc6ce900 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_software.h @@ -0,0 +1,69 @@ +#ifndef __NOUVEAU_SOFTWARE_H__ +#define __NOUVEAU_SOFTWARE_H__ + +struct nouveau_software_priv { + struct nouveau_exec_engine base; + struct list_head vblank; +}; + +struct nouveau_software_chan { + struct list_head flip; + struct { + struct list_head list; + struct nouveau_bo *bo; + u32 offset; + u32 value; + u32 head; + } vblank; +}; + +static inline void +nouveau_software_vblank(struct drm_device *dev, int crtc) +{ + struct nouveau_software_priv *psw = nv_engine(dev, NVOBJ_ENGINE_SW); + struct nouveau_software_chan *pch, *tmp; + + list_for_each_entry_safe(pch, tmp, &psw->vblank, vblank.list) { + if (pch->vblank.head != crtc) + continue; + + nouveau_bo_wr32(pch->vblank.bo, pch->vblank.offset, + pch->vblank.value); + list_del(&pch->vblank.list); + drm_vblank_put(dev, crtc); + } +} + +static inline void +nouveau_software_context_new(struct nouveau_software_chan *pch) +{ + INIT_LIST_HEAD(&pch->flip); +} + +static inline void +nouveau_software_create(struct nouveau_software_priv *psw) +{ + INIT_LIST_HEAD(&psw->vblank); +} + +static inline u16 +nouveau_software_class(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + if (dev_priv->card_type <= NV_04) + return 0x006e; + if (dev_priv->card_type <= NV_40) + return 0x016e; + if (dev_priv->card_type <= NV_50) + return 0x506e; + if (dev_priv->card_type <= NV_E0) + return 0x906e; + return 0x0000; +} + +int nv04_software_create(struct drm_device *); +int nv50_software_create(struct drm_device *); +int nvc0_software_create(struct drm_device *); +u64 nvc0_software_crtc(struct nouveau_channel *, int crtc); + +#endif diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 298c09b7556..19706f0532e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -39,6 +39,9 @@ #include "nouveau_gpio.h" #include "nouveau_pm.h" #include "nv50_display.h" +#include "nouveau_fifo.h" +#include "nouveau_fence.h" +#include "nouveau_software.h" static void nouveau_stub_takedown(struct drm_device *dev) {} static int nouveau_stub_init(struct drm_device *dev) { return 0; } @@ -66,18 +69,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->timer.takedown = nv04_timer_takedown; engine->fb.init = nv04_fb_init; engine->fb.takedown = nv04_fb_takedown; - engine->fifo.channels = 16; - engine->fifo.init = nv04_fifo_init; - engine->fifo.takedown = nv04_fifo_fini; - engine->fifo.disable = nv04_fifo_disable; - engine->fifo.enable = nv04_fifo_enable; - engine->fifo.reassign = nv04_fifo_reassign; - 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; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -111,18 +102,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fb.init_tile_region = nv10_fb_init_tile_region; engine->fb.set_tile_region = nv10_fb_set_tile_region; engine->fb.free_tile_region = nv10_fb_free_tile_region; - engine->fifo.channels = 32; - engine->fifo.init = nv10_fifo_init; - engine->fifo.takedown = nv04_fifo_fini; - engine->fifo.disable = nv04_fifo_disable; - engine->fifo.enable = nv04_fifo_enable; - engine->fifo.reassign = nv04_fifo_reassign; - 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 = nv04_fifo_destroy_context; - engine->fifo.load_context = nv10_fifo_load_context; - engine->fifo.unload_context = nv10_fifo_unload_context; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -162,18 +141,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fb.init_tile_region = nv20_fb_init_tile_region; engine->fb.set_tile_region = nv20_fb_set_tile_region; engine->fb.free_tile_region = nv20_fb_free_tile_region; - engine->fifo.channels = 32; - engine->fifo.init = nv10_fifo_init; - engine->fifo.takedown = nv04_fifo_fini; - engine->fifo.disable = nv04_fifo_disable; - engine->fifo.enable = nv04_fifo_enable; - engine->fifo.reassign = nv04_fifo_reassign; - 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 = nv04_fifo_destroy_context; - engine->fifo.load_context = nv10_fifo_load_context; - engine->fifo.unload_context = nv10_fifo_unload_context; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -209,18 +176,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fb.init_tile_region = nv30_fb_init_tile_region; engine->fb.set_tile_region = nv10_fb_set_tile_region; engine->fb.free_tile_region = nv30_fb_free_tile_region; - engine->fifo.channels = 32; - engine->fifo.init = nv10_fifo_init; - engine->fifo.takedown = nv04_fifo_fini; - engine->fifo.disable = nv04_fifo_disable; - engine->fifo.enable = nv04_fifo_enable; - engine->fifo.reassign = nv04_fifo_reassign; - 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 = nv04_fifo_destroy_context; - engine->fifo.load_context = nv10_fifo_load_context; - engine->fifo.unload_context = nv10_fifo_unload_context; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -259,18 +214,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fb.init_tile_region = nv30_fb_init_tile_region; engine->fb.set_tile_region = nv40_fb_set_tile_region; engine->fb.free_tile_region = nv30_fb_free_tile_region; - engine->fifo.channels = 32; - engine->fifo.init = nv40_fifo_init; - engine->fifo.takedown = nv04_fifo_fini; - engine->fifo.disable = nv04_fifo_disable; - engine->fifo.enable = nv04_fifo_enable; - engine->fifo.reassign = nv04_fifo_reassign; - 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 = nv04_fifo_destroy_context; - engine->fifo.load_context = nv40_fifo_load_context; - engine->fifo.unload_context = nv40_fifo_unload_context; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -317,18 +260,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->timer.takedown = nv04_timer_takedown; engine->fb.init = nv50_fb_init; engine->fb.takedown = nv50_fb_takedown; - 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; - engine->fifo.tlb_flush = nv50_fifo_tlb_flush; engine->display.early_init = nv50_display_early_init; engine->display.late_takedown = nv50_display_late_takedown; engine->display.create = nv50_display_create; @@ -392,17 +323,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->timer.takedown = nv04_timer_takedown; engine->fb.init = nvc0_fb_init; engine->fb.takedown = nvc0_fb_takedown; - engine->fifo.channels = 128; - engine->fifo.init = nvc0_fifo_init; - engine->fifo.takedown = nvc0_fifo_takedown; - engine->fifo.disable = nvc0_fifo_disable; - engine->fifo.enable = nvc0_fifo_enable; - engine->fifo.reassign = nvc0_fifo_reassign; - engine->fifo.channel_id = nvc0_fifo_channel_id; - engine->fifo.create_context = nvc0_fifo_create_context; - engine->fifo.destroy_context = nvc0_fifo_destroy_context; - engine->fifo.load_context = nvc0_fifo_load_context; - engine->fifo.unload_context = nvc0_fifo_unload_context; engine->display.early_init = nv50_display_early_init; engine->display.late_takedown = nv50_display_late_takedown; engine->display.create = nv50_display_create; @@ -445,17 +365,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->timer.takedown = nv04_timer_takedown; engine->fb.init = nvc0_fb_init; engine->fb.takedown = nvc0_fb_takedown; - engine->fifo.channels = 128; - engine->fifo.init = nvc0_fifo_init; - engine->fifo.takedown = nvc0_fifo_takedown; - engine->fifo.disable = nvc0_fifo_disable; - engine->fifo.enable = nvc0_fifo_enable; - engine->fifo.reassign = nvc0_fifo_reassign; - engine->fifo.channel_id = nvc0_fifo_channel_id; - engine->fifo.create_context = nvc0_fifo_create_context; - engine->fifo.destroy_context = nvc0_fifo_destroy_context; - engine->fifo.load_context = nvc0_fifo_load_context; - engine->fifo.unload_context = nvc0_fifo_unload_context; engine->display.early_init = nouveau_stub_init; engine->display.late_takedown = nouveau_stub_takedown; engine->display.create = nvd0_display_create; @@ -496,13 +405,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->timer.takedown = nv04_timer_takedown; engine->fb.init = nvc0_fb_init; engine->fb.takedown = nvc0_fb_takedown; - engine->fifo.channels = 0; - engine->fifo.init = nouveau_stub_init; - engine->fifo.takedown = nouveau_stub_takedown; - engine->fifo.disable = nvc0_fifo_disable; - engine->fifo.enable = nvc0_fifo_enable; - engine->fifo.reassign = nvc0_fifo_reassign; - engine->fifo.unload_context = nouveau_stub_init; engine->display.early_init = nouveau_stub_init; engine->display.late_takedown = nouveau_stub_takedown; engine->display.create = nvd0_display_create; @@ -607,59 +509,16 @@ nouveau_card_channel_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_channel *chan; - int ret, oclass; + int ret; ret = nouveau_channel_alloc(dev, &chan, NULL, NvDmaFB, NvDmaTT); dev_priv->channel = chan; if (ret) return ret; - mutex_unlock(&dev_priv->channel->mutex); - if (dev_priv->card_type <= NV_50) { - if (dev_priv->card_type < NV_50) - oclass = 0x0039; - else - oclass = 0x5039; - - ret = nouveau_gpuobj_gr_new(chan, NvM2MF, oclass); - if (ret) - goto error; - - ret = nouveau_notifier_alloc(chan, NvNotify0, 32, 0xfe0, 0x1000, - &chan->m2mf_ntfy); - if (ret) - goto error; - - ret = RING_SPACE(chan, 6); - if (ret) - goto error; - - 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, 3); - OUT_RING (chan, NvNotify0); - OUT_RING (chan, chan->vram_handle); - OUT_RING (chan, chan->gart_handle); - } else - if (dev_priv->card_type <= NV_D0) { - ret = nouveau_gpuobj_gr_new(chan, 0x9039, 0x9039); - if (ret) - goto error; - - ret = RING_SPACE(chan, 2); - if (ret) - goto error; - - BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0000, 1); - OUT_RING (chan, 0x00009039); - } - - FIRE_RING (chan); -error: - if (ret) - nouveau_card_channel_fini(dev); - return ret; + nouveau_bo_move_init(chan); + return 0; } static const struct vga_switcheroo_client_ops nouveau_switcheroo_ops = { @@ -749,6 +608,81 @@ nouveau_card_init(struct drm_device *dev) if (!dev_priv->noaccel) { switch (dev_priv->card_type) { case NV_04: + nv04_fifo_create(dev); + break; + case NV_10: + case NV_20: + case NV_30: + if (dev_priv->chipset < 0x17) + nv10_fifo_create(dev); + else + nv17_fifo_create(dev); + break; + case NV_40: + nv40_fifo_create(dev); + break; + case NV_50: + if (dev_priv->chipset == 0x50) + nv50_fifo_create(dev); + else + nv84_fifo_create(dev); + break; + case NV_C0: + case NV_D0: + nvc0_fifo_create(dev); + break; + case NV_E0: + nve0_fifo_create(dev); + break; + default: + break; + } + + switch (dev_priv->card_type) { + case NV_04: + nv04_fence_create(dev); + break; + case NV_10: + case NV_20: + case NV_30: + case NV_40: + case NV_50: + if (dev_priv->chipset < 0x84) + nv10_fence_create(dev); + else + nv84_fence_create(dev); + break; + case NV_C0: + case NV_D0: + case NV_E0: + nvc0_fence_create(dev); + break; + default: + break; + } + + switch (dev_priv->card_type) { + case NV_04: + case NV_10: + case NV_20: + case NV_30: + case NV_40: + nv04_software_create(dev); + break; + case NV_50: + nv50_software_create(dev); + break; + case NV_C0: + case NV_D0: + case NV_E0: + nvc0_software_create(dev); + break; + default: + break; + } + + switch (dev_priv->card_type) { + case NV_04: nv04_graph_create(dev); break; case NV_10: @@ -768,6 +702,9 @@ nouveau_card_init(struct drm_device *dev) case NV_D0: nvc0_graph_create(dev); break; + case NV_E0: + nve0_graph_create(dev); + break; default: break; } @@ -800,8 +737,9 @@ nouveau_card_init(struct drm_device *dev) } break; case NV_C0: - nvc0_copy_create(dev, 0); nvc0_copy_create(dev, 1); + case NV_D0: + nvc0_copy_create(dev, 0); break; default: break; @@ -834,16 +772,11 @@ nouveau_card_init(struct drm_device *dev) goto out_engine; } } - - /* PFIFO */ - ret = engine->fifo.init(dev); - if (ret) - goto out_engine; } ret = nouveau_irq_init(dev); if (ret) - goto out_fifo; + goto out_engine; ret = nouveau_display_create(dev); if (ret) @@ -852,14 +785,10 @@ nouveau_card_init(struct drm_device *dev) nouveau_backlight_init(dev); nouveau_pm_init(dev); - ret = nouveau_fence_init(dev); - if (ret) - goto out_pm; - if (dev_priv->eng[NVOBJ_ENGINE_GR]) { ret = nouveau_card_channel_init(dev); if (ret) - goto out_fence; + goto out_pm; } if (dev->mode_config.num_crtc) { @@ -874,17 +803,12 @@ nouveau_card_init(struct drm_device *dev) out_chan: nouveau_card_channel_fini(dev); -out_fence: - nouveau_fence_fini(dev); out_pm: nouveau_pm_fini(dev); nouveau_backlight_exit(dev); nouveau_display_destroy(dev); out_irq: nouveau_irq_fini(dev); -out_fifo: - if (!dev_priv->noaccel) - engine->fifo.takedown(dev); out_engine: if (!dev_priv->noaccel) { for (e = e - 1; e >= 0; e--) { @@ -916,6 +840,7 @@ out_bios: out_display_early: engine->display.late_takedown(dev); out: + vga_switcheroo_unregister_client(dev->pdev); vga_client_register(dev->pdev, NULL, NULL, NULL); return ret; } @@ -932,13 +857,11 @@ static void nouveau_card_takedown(struct drm_device *dev) } nouveau_card_channel_fini(dev); - nouveau_fence_fini(dev); nouveau_pm_fini(dev); nouveau_backlight_exit(dev); nouveau_display_destroy(dev); if (!dev_priv->noaccel) { - engine->fifo.takedown(dev); for (e = NVOBJ_ENGINE_NR - 1; e >= 0; e--) { if (dev_priv->eng[e]) { dev_priv->eng[e]->fini(dev, e, false); @@ -973,6 +896,7 @@ static void nouveau_card_takedown(struct drm_device *dev) nouveau_irq_fini(dev); + vga_switcheroo_unregister_client(dev->pdev); vga_client_register(dev->pdev, NULL, NULL, NULL); } @@ -1180,7 +1104,7 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) goto err_priv; } - NV_INFO(dev, "Detected an NV%2x generation card (0x%08x)\n", + NV_INFO(dev, "Detected an NV%02x generation card (0x%08x)\n", dev_priv->card_type, reg0); /* map the mmio regs, limiting the amount to preserve vmap space */ @@ -1223,6 +1147,8 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) if (nouveau_noaccel == -1) { switch (dev_priv->chipset) { case 0xd9: /* known broken */ + case 0xe4: /* needs binary driver firmware */ + case 0xe7: /* needs binary driver firmware */ NV_INFO(dev, "acceleration disabled by default, pass " "noaccel=0 to force enable\n"); dev_priv->noaccel = true; diff --git a/drivers/gpu/drm/nouveau/nouveau_vm.c b/drivers/gpu/drm/nouveau/nouveau_vm.c index 2bf6c0350b4..11edd5e91a0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vm.c +++ b/drivers/gpu/drm/nouveau/nouveau_vm.c @@ -77,6 +77,63 @@ nouveau_vm_map(struct nouveau_vma *vma, struct nouveau_mem *node) } void +nouveau_vm_map_sg_table(struct nouveau_vma *vma, u64 delta, u64 length, + struct nouveau_mem *mem) +{ + struct nouveau_vm *vm = vma->vm; + int big = vma->node->type != vm->spg_shift; + u32 offset = vma->node->offset + (delta >> 12); + u32 bits = vma->node->type - 12; + u32 num = length >> vma->node->type; + u32 pde = (offset >> vm->pgt_bits) - vm->fpde; + u32 pte = (offset & ((1 << vm->pgt_bits) - 1)) >> bits; + u32 max = 1 << (vm->pgt_bits - bits); + unsigned m, sglen; + u32 end, len; + int i; + struct scatterlist *sg; + + for_each_sg(mem->sg->sgl, sg, mem->sg->nents, i) { + struct nouveau_gpuobj *pgt = vm->pgt[pde].obj[big]; + sglen = sg_dma_len(sg) >> PAGE_SHIFT; + + end = pte + sglen; + if (unlikely(end >= max)) + end = max; + len = end - pte; + + for (m = 0; m < len; m++) { + dma_addr_t addr = sg_dma_address(sg) + (m << PAGE_SHIFT); + + vm->map_sg(vma, pgt, mem, pte, 1, &addr); + num--; + pte++; + + if (num == 0) + goto finish; + } + if (unlikely(end >= max)) { + pde++; + pte = 0; + } + if (m < sglen) { + for (; m < sglen; m++) { + dma_addr_t addr = sg_dma_address(sg) + (m << PAGE_SHIFT); + + vm->map_sg(vma, pgt, mem, pte, 1, &addr); + num--; + pte++; + if (num == 0) + goto finish; + } + } + + } +finish: + vm->flush(vm); +} + +void nouveau_vm_map_sg(struct nouveau_vma *vma, u64 delta, u64 length, struct nouveau_mem *mem) { diff --git a/drivers/gpu/drm/nouveau/nouveau_vm.h b/drivers/gpu/drm/nouveau/nouveau_vm.h index 4fb6e728734..a8246e7e4a8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vm.h +++ b/drivers/gpu/drm/nouveau/nouveau_vm.h @@ -72,6 +72,9 @@ struct nouveau_vm { u64 phys, u64 delta); void (*map_sg)(struct nouveau_vma *, struct nouveau_gpuobj *, struct nouveau_mem *, u32 pte, u32 cnt, dma_addr_t *); + + void (*map_sg_table)(struct nouveau_vma *, struct nouveau_gpuobj *, + struct nouveau_mem *, u32 pte, u32 cnt, dma_addr_t *); void (*unmap)(struct nouveau_gpuobj *pgt, u32 pte, u32 cnt); void (*flush)(struct nouveau_vm *); }; @@ -90,7 +93,8 @@ void nouveau_vm_unmap(struct nouveau_vma *); void nouveau_vm_unmap_at(struct nouveau_vma *, u64 offset, u64 length); void nouveau_vm_map_sg(struct nouveau_vma *, u64 offset, u64 length, struct nouveau_mem *); - +void nouveau_vm_map_sg_table(struct nouveau_vma *vma, u64 delta, u64 length, + struct nouveau_mem *mem); /* nv50_vm.c */ void nv50_vm_map_pgt(struct nouveau_gpuobj *pgd, u32 pde, struct nouveau_gpuobj *pgt[2]); diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c b/drivers/gpu/drm/nouveau/nv04_crtc.c index 728d07584d3..4c31c63e552 100644 --- a/drivers/gpu/drm/nouveau/nv04_crtc.c +++ b/drivers/gpu/drm/nouveau/nv04_crtc.c @@ -1047,7 +1047,7 @@ nv04_crtc_create(struct drm_device *dev, int crtc_num) drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256); ret = nouveau_bo_new(dev, 64*64*4, 0x100, TTM_PL_FLAG_VRAM, - 0, 0x0000, &nv_crtc->cursor.nvbo); + 0, 0x0000, NULL, &nv_crtc->cursor.nvbo); if (!ret) { ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM); if (!ret) diff --git a/drivers/gpu/drm/nouveau/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c index 7047d37e8da..44488e3a257 100644 --- a/drivers/gpu/drm/nouveau/nv04_display.c +++ b/drivers/gpu/drm/nouveau/nv04_display.c @@ -98,6 +98,13 @@ nv04_display_early_init(struct drm_device *dev) NVSetOwner(dev, 0); } + /* ensure vblank interrupts are off, they can't be enabled until + * drm_vblank has been initialised + */ + NVWriteCRTC(dev, 0, NV_PCRTC_INTR_EN_0, 0); + if (nv_two_heads(dev)) + NVWriteCRTC(dev, 1, NV_PCRTC_INTR_EN_0, 0); + return 0; } @@ -246,6 +253,10 @@ nv04_display_init(struct drm_device *dev) void nv04_display_fini(struct drm_device *dev) { + /* disable vblank interrupts */ + NVWriteCRTC(dev, 0, NV_PCRTC_INTR_EN_0, 0); + if (nv_two_heads(dev)) + NVWriteCRTC(dev, 1, NV_PCRTC_INTR_EN_0, 0); } static void diff --git a/drivers/gpu/drm/nouveau/nv04_fbcon.c b/drivers/gpu/drm/nouveau/nv04_fbcon.c index 7a118937109..7cd7857347e 100644 --- a/drivers/gpu/drm/nouveau/nv04_fbcon.c +++ b/drivers/gpu/drm/nouveau/nv04_fbcon.c @@ -41,7 +41,7 @@ nv04_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region) if (ret) return ret; - BEGIN_RING(chan, NvSubImageBlit, 0x0300, 3); + BEGIN_NV04(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); @@ -62,15 +62,15 @@ nv04_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect) if (ret) return ret; - BEGIN_RING(chan, NvSubGdiRect, 0x02fc, 1); + BEGIN_NV04(chan, NvSubGdiRect, 0x02fc, 1); OUT_RING(chan, (rect->rop != ROP_COPY) ? 1 : 3); - BEGIN_RING(chan, NvSubGdiRect, 0x03fc, 1); + BEGIN_NV04(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); + BEGIN_NV04(chan, NvSubGdiRect, 0x0400, 2); OUT_RING(chan, (rect->dx << 16) | rect->dy); OUT_RING(chan, (rect->width << 16) | rect->height); FIRE_RING(chan); @@ -110,7 +110,7 @@ nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image) bg = image->bg_color; } - BEGIN_RING(chan, NvSubGdiRect, 0x0be4, 7); + BEGIN_NV04(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)); @@ -127,7 +127,7 @@ nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image) if (ret) return ret; - BEGIN_RING(chan, NvSubGdiRect, 0x0c00, iter_len); + BEGIN_NV04(chan, NvSubGdiRect, 0x0c00, iter_len); OUT_RINGp(chan, data, iter_len); data += iter_len; dsize -= iter_len; @@ -209,25 +209,25 @@ nv04_fbcon_accel_init(struct fb_info *info) return 0; } - BEGIN_RING(chan, sub, 0x0000, 1); + BEGIN_NV04(chan, sub, 0x0000, 1); OUT_RING(chan, NvCtxSurf2D); - BEGIN_RING(chan, sub, 0x0184, 2); + BEGIN_NV04(chan, sub, 0x0184, 2); OUT_RING(chan, NvDmaFB); OUT_RING(chan, NvDmaFB); - BEGIN_RING(chan, sub, 0x0300, 4); + BEGIN_NV04(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); + BEGIN_NV04(chan, sub, 0x0000, 1); OUT_RING(chan, NvRop); - BEGIN_RING(chan, sub, 0x0300, 1); + BEGIN_NV04(chan, sub, 0x0300, 1); OUT_RING(chan, 0x55); - BEGIN_RING(chan, sub, 0x0000, 1); + BEGIN_NV04(chan, sub, 0x0000, 1); OUT_RING(chan, NvImagePatt); - BEGIN_RING(chan, sub, 0x0300, 8); + BEGIN_NV04(chan, sub, 0x0300, 8); OUT_RING(chan, pattern_fmt); #ifdef __BIG_ENDIAN OUT_RING(chan, 2); @@ -241,31 +241,31 @@ nv04_fbcon_accel_init(struct fb_info *info) OUT_RING(chan, ~0); OUT_RING(chan, ~0); - BEGIN_RING(chan, sub, 0x0000, 1); + BEGIN_NV04(chan, sub, 0x0000, 1); OUT_RING(chan, NvClipRect); - BEGIN_RING(chan, sub, 0x0300, 2); + BEGIN_NV04(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); + BEGIN_NV04(chan, NvSubImageBlit, 0x0000, 1); OUT_RING(chan, NvImageBlit); - BEGIN_RING(chan, NvSubImageBlit, 0x019c, 1); + BEGIN_NV04(chan, NvSubImageBlit, 0x019c, 1); OUT_RING(chan, NvCtxSurf2D); - BEGIN_RING(chan, NvSubImageBlit, 0x02fc, 1); + BEGIN_NV04(chan, NvSubImageBlit, 0x02fc, 1); OUT_RING(chan, 3); - BEGIN_RING(chan, NvSubGdiRect, 0x0000, 1); + BEGIN_NV04(chan, NvSubGdiRect, 0x0000, 1); OUT_RING(chan, NvGdiRect); - BEGIN_RING(chan, NvSubGdiRect, 0x0198, 1); + BEGIN_NV04(chan, NvSubGdiRect, 0x0198, 1); OUT_RING(chan, NvCtxSurf2D); - BEGIN_RING(chan, NvSubGdiRect, 0x0188, 2); + BEGIN_NV04(chan, NvSubGdiRect, 0x0188, 2); OUT_RING(chan, NvImagePatt); OUT_RING(chan, NvRop); - BEGIN_RING(chan, NvSubGdiRect, 0x0304, 1); + BEGIN_NV04(chan, NvSubGdiRect, 0x0304, 1); OUT_RING(chan, 1); - BEGIN_RING(chan, NvSubGdiRect, 0x0300, 1); + BEGIN_NV04(chan, NvSubGdiRect, 0x0300, 1); OUT_RING(chan, rect_fmt); - BEGIN_RING(chan, NvSubGdiRect, 0x02fc, 1); + BEGIN_NV04(chan, NvSubGdiRect, 0x02fc, 1); OUT_RING(chan, 3); FIRE_RING(chan); diff --git a/drivers/gpu/drm/nouveau/nv04_fence.c b/drivers/gpu/drm/nouveau/nv04_fence.c new file mode 100644 index 00000000000..abe89db6de2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv04_fence.c @@ -0,0 +1,140 @@ +/* + * Copyright 2012 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_dma.h" +#include "nouveau_ramht.h" +#include "nouveau_fence.h" + +struct nv04_fence_chan { + struct nouveau_fence_chan base; + atomic_t sequence; +}; + +struct nv04_fence_priv { + struct nouveau_fence_priv base; +}; + +static int +nv04_fence_emit(struct nouveau_fence *fence) +{ + struct nouveau_channel *chan = fence->channel; + int ret = RING_SPACE(chan, 2); + if (ret == 0) { + BEGIN_NV04(chan, NvSubSw, 0x0150, 1); + OUT_RING (chan, fence->sequence); + FIRE_RING (chan); + } + return ret; +} + +static int +nv04_fence_sync(struct nouveau_fence *fence, + struct nouveau_channel *prev, struct nouveau_channel *chan) +{ + return -ENODEV; +} + +int +nv04_fence_mthd(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data) +{ + struct nv04_fence_chan *fctx = chan->engctx[NVOBJ_ENGINE_FENCE]; + atomic_set(&fctx->sequence, data); + return 0; +} + +static u32 +nv04_fence_read(struct nouveau_channel *chan) +{ + struct nv04_fence_chan *fctx = chan->engctx[NVOBJ_ENGINE_FENCE]; + return atomic_read(&fctx->sequence); +} + +static void +nv04_fence_context_del(struct nouveau_channel *chan, int engine) +{ + struct nv04_fence_chan *fctx = chan->engctx[engine]; + nouveau_fence_context_del(&fctx->base); + chan->engctx[engine] = NULL; + kfree(fctx); +} + +static int +nv04_fence_context_new(struct nouveau_channel *chan, int engine) +{ + struct nv04_fence_chan *fctx = kzalloc(sizeof(*fctx), GFP_KERNEL); + if (fctx) { + nouveau_fence_context_new(&fctx->base); + atomic_set(&fctx->sequence, 0); + chan->engctx[engine] = fctx; + return 0; + } + return -ENOMEM; +} + +static int +nv04_fence_fini(struct drm_device *dev, int engine, bool suspend) +{ + return 0; +} + +static int +nv04_fence_init(struct drm_device *dev, int engine) +{ + return 0; +} + +static void +nv04_fence_destroy(struct drm_device *dev, int engine) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv04_fence_priv *priv = nv_engine(dev, engine); + + dev_priv->eng[engine] = NULL; + kfree(priv); +} + +int +nv04_fence_create(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv04_fence_priv *priv; + int ret; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base.engine.destroy = nv04_fence_destroy; + priv->base.engine.init = nv04_fence_init; + priv->base.engine.fini = nv04_fence_fini; + priv->base.engine.context_new = nv04_fence_context_new; + priv->base.engine.context_del = nv04_fence_context_del; + priv->base.emit = nv04_fence_emit; + priv->base.sync = nv04_fence_sync; + priv->base.read = nv04_fence_read; + dev_priv->eng[NVOBJ_ENGINE_FENCE] = &priv->base.engine; + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nv04_fifo.c b/drivers/gpu/drm/nouveau/nv04_fifo.c index db465a3ee1b..a6295cd00ec 100644 --- a/drivers/gpu/drm/nouveau/nv04_fifo.c +++ b/drivers/gpu/drm/nouveau/nv04_fifo.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Ben Skeggs. + * Copyright (C) 2012 Ben Skeggs. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining @@ -27,49 +27,38 @@ #include "drmP.h" #include "drm.h" #include "nouveau_drv.h" -#include "nouveau_ramht.h" +#include "nouveau_fifo.h" #include "nouveau_util.h" - -#define NV04_RAMFC(c) (dev_priv->ramfc->pinst + ((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(chan->ramfc, NV04_RAMFC_##offset, (val)) -#define RAMFC_RD(offset) nv_ro32(chan->ramfc, NV04_RAMFC_##offset) - -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); -} +#include "nouveau_ramht.h" +#include "nouveau_software.h" + +static struct ramfc_desc { + unsigned bits:6; + unsigned ctxs:5; + unsigned ctxp:8; + unsigned regs:5; + unsigned regp; +} nv04_ramfc[] = { + { 32, 0, 0x00, 0, NV04_PFIFO_CACHE1_DMA_PUT }, + { 32, 0, 0x04, 0, NV04_PFIFO_CACHE1_DMA_GET }, + { 16, 0, 0x08, 0, NV04_PFIFO_CACHE1_DMA_INSTANCE }, + { 16, 16, 0x08, 0, NV04_PFIFO_CACHE1_DMA_DCOUNT }, + { 32, 0, 0x0c, 0, NV04_PFIFO_CACHE1_DMA_STATE }, + { 32, 0, 0x10, 0, NV04_PFIFO_CACHE1_DMA_FETCH }, + { 32, 0, 0x14, 0, NV04_PFIFO_CACHE1_ENGINE }, + { 32, 0, 0x18, 0, NV04_PFIFO_CACHE1_PULL1 }, + {} +}; + +struct nv04_fifo_priv { + struct nouveau_fifo_priv base; + struct ramfc_desc *ramfc_desc; +}; + +struct nv04_fifo_chan { + struct nouveau_fifo_chan base; + struct nouveau_gpuobj *ramfc; +}; bool nv04_fifo_cache_pull(struct drm_device *dev, bool enable) @@ -86,13 +75,13 @@ nv04_fifo_cache_pull(struct drm_device *dev, bool enable) * invalidate the most recently calculated instance. */ if (!nv_wait(dev, NV04_PFIFO_CACHE1_PULL0, - NV04_PFIFO_CACHE1_PULL0_HASH_BUSY, 0)) + NV04_PFIFO_CACHE1_PULL0_HASH_BUSY, 0)) NV_ERROR(dev, "Timeout idling the PFIFO puller.\n"); if (nv_rd32(dev, NV04_PFIFO_CACHE1_PULL0) & - NV04_PFIFO_CACHE1_PULL0_HASH_FAILED) + NV04_PFIFO_CACHE1_PULL0_HASH_FAILED) nv_wr32(dev, NV03_PFIFO_INTR_0, - NV_PFIFO_INTR_CACHE_ERROR); + NV_PFIFO_INTR_CACHE_ERROR); nv_wr32(dev, NV04_PFIFO_CACHE1_HASH, 0); } @@ -100,242 +89,182 @@ nv04_fifo_cache_pull(struct drm_device *dev, bool enable) 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; -} - -#ifdef __BIG_ENDIAN -#define DMA_FETCH_ENDIANNESS NV_PFIFO_CACHE1_BIG_ENDIAN -#else -#define DMA_FETCH_ENDIANNESS 0 -#endif - -int -nv04_fifo_create_context(struct nouveau_channel *chan) +static int +nv04_fifo_context_new(struct nouveau_channel *chan, int engine) { struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv04_fifo_priv *priv = nv_engine(dev, engine); + struct nv04_fifo_chan *fctx; unsigned long flags; int ret; - ret = nouveau_gpuobj_new_fake(dev, NV04_RAMFC(chan->id), ~0, - NV04_RAMFC__SIZE, - NVOBJ_FLAG_ZERO_ALLOC | - NVOBJ_FLAG_ZERO_FREE, - &chan->ramfc); - if (ret) - return ret; + fctx = chan->engctx[engine] = kzalloc(sizeof(*fctx), GFP_KERNEL); + if (!fctx) + return -ENOMEM; + /* map channel control registers */ chan->user = ioremap(pci_resource_start(dev->pdev, 0) + NV03_USER(chan->id), PAGE_SIZE); - if (!chan->user) - return -ENOMEM; - - spin_lock_irqsave(&dev_priv->context_switch_lock, flags); - - /* Setup initial state */ - RAMFC_WR(DMA_PUT, chan->pushbuf_base); - RAMFC_WR(DMA_GET, chan->pushbuf_base); - RAMFC_WR(DMA_INSTANCE, chan->pushbuf->pinst >> 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 | - DMA_FETCH_ENDIANNESS)); + if (!chan->user) { + ret = -ENOMEM; + goto error; + } - /* enable the fifo dma operation */ - nv_wr32(dev, NV04_PFIFO_MODE, - nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id)); + /* initialise default fifo context */ + ret = nouveau_gpuobj_new_fake(dev, dev_priv->ramfc->pinst + + chan->id * 32, ~0, 32, + NVOBJ_FLAG_ZERO_FREE, &fctx->ramfc); + if (ret) + goto error; + + nv_wo32(fctx->ramfc, 0x00, chan->pushbuf_base); + nv_wo32(fctx->ramfc, 0x04, chan->pushbuf_base); + nv_wo32(fctx->ramfc, 0x08, chan->pushbuf->pinst >> 4); + nv_wo32(fctx->ramfc, 0x0c, 0x00000000); + nv_wo32(fctx->ramfc, 0x10, NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES | + NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES | +#ifdef __BIG_ENDIAN + NV_PFIFO_CACHE1_BIG_ENDIAN | +#endif + NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8); + nv_wo32(fctx->ramfc, 0x14, 0x00000000); + nv_wo32(fctx->ramfc, 0x18, 0x00000000); + nv_wo32(fctx->ramfc, 0x1c, 0x00000000); + /* enable dma mode on the channel */ + spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + nv_mask(dev, NV04_PFIFO_MODE, (1 << chan->id), (1 << chan->id)); spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); - return 0; + +error: + if (ret) + priv->base.base.context_del(chan, engine); + return ret; } void -nv04_fifo_destroy_context(struct nouveau_channel *chan) +nv04_fifo_context_del(struct nouveau_channel *chan, int engine) { struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; + struct nv04_fifo_priv *priv = nv_engine(chan->dev, engine); + struct nv04_fifo_chan *fctx = chan->engctx[engine]; + struct ramfc_desc *c = priv->ramfc_desc; unsigned long flags; + int chid; + /* prevent fifo context switches */ spin_lock_irqsave(&dev_priv->context_switch_lock, flags); - pfifo->reassign(dev, false); - - /* Unload the context if it's the currently active one */ - if (pfifo->channel_id(dev) == chan->id) { - pfifo->disable(dev); - pfifo->unload_context(dev); - pfifo->enable(dev); + nv_wr32(dev, NV03_PFIFO_CACHES, 0); + + /* if this channel is active, replace it with a null context */ + chid = nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH1) & priv->base.channels; + if (chid == chan->id) { + nv_mask(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 0x00000001, 0); + nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, 0); + nv_mask(dev, NV04_PFIFO_CACHE1_PULL0, 0x00000001, 0); + + do { + u32 mask = ((1ULL << c->bits) - 1) << c->regs; + nv_mask(dev, c->regp, mask, 0x00000000); + } while ((++c)->bits); + + nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0); + nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0); + nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, priv->base.channels); + nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, 1); + nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1); } - /* Keep it from being rescheduled */ + /* restore normal operation, after disabling dma mode */ nv_mask(dev, NV04_PFIFO_MODE, 1 << chan->id, 0); - - pfifo->reassign(dev, true); + nv_wr32(dev, NV03_PFIFO_CACHES, 1); spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); - /* Free the channel resources */ + /* clean up */ + nouveau_gpuobj_ref(NULL, &fctx->ramfc); + nouveau_gpuobj_ref(NULL, &chan->ramfc); /*XXX: nv40 */ if (chan->user) { iounmap(chan->user); chan->user = NULL; } - nouveau_gpuobj_ref(NULL, &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; - - 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)); - - 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) +nv04_fifo_init(struct drm_device *dev, int engine) { 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->channels.ptr[chid]; - if (!chan) { - NV_ERROR(dev, "Inactive channel on PFIFO: %d\n", chid); - return -EINVAL; - } - - 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)); - - nv04_fifo_do_load_context(dev, pfifo->channels - 1); - nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1); - return 0; -} + struct nv04_fifo_priv *priv = nv_engine(dev, engine); + int i; -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); -} + nv_mask(dev, NV03_PMC_ENABLE, NV_PMC_ENABLE_PFIFO, 0); + nv_mask(dev, NV03_PMC_ENABLE, NV_PMC_ENABLE_PFIFO, NV_PMC_ENABLE_PFIFO); -static void -nv04_fifo_init_ramxx(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; + nv_wr32(dev, NV04_PFIFO_DELAY_0, 0x000000ff); + nv_wr32(dev, NV04_PFIFO_DMA_TIMESLICE, 0x0101ffff); nv_wr32(dev, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ | ((dev_priv->ramht->bits - 9) << 16) | (dev_priv->ramht->gpuobj->pinst >> 8)); nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro->pinst >> 8); nv_wr32(dev, NV03_PFIFO_RAMFC, dev_priv->ramfc->pinst >> 8); -} -static void -nv04_fifo_init_intr(struct drm_device *dev) -{ - nouveau_irq_register(dev, 8, nv04_fifo_isr); - nv_wr32(dev, 0x002100, 0xffffffff); - nv_wr32(dev, 0x002140, 0xffffffff); + nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, priv->base.channels); + + nv_wr32(dev, NV03_PFIFO_INTR_0, 0xffffffff); + nv_wr32(dev, NV03_PFIFO_INTR_EN_0, 0xffffffff); + + nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, 1); + nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1); + nv_wr32(dev, NV03_PFIFO_CACHES, 1); + + for (i = 0; i < priv->base.channels; i++) { + if (dev_priv->channels.ptr[i]) + nv_mask(dev, NV04_PFIFO_MODE, (1 << i), (1 << i)); + } + + return 0; } int -nv04_fifo_init(struct drm_device *dev) +nv04_fifo_fini(struct drm_device *dev, int engine, bool suspend) { 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); + struct nv04_fifo_priv *priv = nv_engine(dev, engine); + struct nouveau_channel *chan; + int chid; - nv04_fifo_init_intr(dev); - pfifo->enable(dev); - pfifo->reassign(dev, true); + /* prevent context switches and halt fifo operation */ + nv_wr32(dev, NV03_PFIFO_CACHES, 0); + nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 0); + nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, 0); + nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 0); - for (i = 0; i < dev_priv->engine.fifo.channels; i++) { - if (dev_priv->channels.ptr[i]) { - uint32_t mode = nv_rd32(dev, NV04_PFIFO_MODE); - nv_wr32(dev, NV04_PFIFO_MODE, mode | (1 << i)); - } + /* store current fifo context in ramfc */ + chid = nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH1) & priv->base.channels; + chan = dev_priv->channels.ptr[chid]; + if (suspend && chid != priv->base.channels && chan) { + struct nv04_fifo_chan *fctx = chan->engctx[engine]; + struct nouveau_gpuobj *ctx = fctx->ramfc; + struct ramfc_desc *c = priv->ramfc_desc; + do { + u32 rm = ((1ULL << c->bits) - 1) << c->regs; + u32 cm = ((1ULL << c->bits) - 1) << c->ctxs; + u32 rv = (nv_rd32(dev, c->regp) & rm) >> c->regs; + u32 cv = (nv_ro32(ctx, c->ctxp) & ~cm); + nv_wo32(ctx, c->ctxp, cv | (rv << c->ctxs)); + } while ((++c)->bits); } + nv_wr32(dev, NV03_PFIFO_INTR_EN_0, 0x00000000); return 0; } -void -nv04_fifo_fini(struct drm_device *dev) -{ - nv_wr32(dev, 0x2140, 0x00000000); - nouveau_irq_unregister(dev, 8); -} - static bool nouveau_fifo_swmthd(struct drm_device *dev, u32 chid, u32 addr, u32 data) { + struct nouveau_fifo_priv *pfifo = nv_engine(dev, NVOBJ_ENGINE_FIFO); struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_channel *chan = NULL; struct nouveau_gpuobj *obj; @@ -346,7 +275,7 @@ nouveau_fifo_swmthd(struct drm_device *dev, u32 chid, u32 addr, u32 data) u32 engine; spin_lock_irqsave(&dev_priv->channels.lock, flags); - if (likely(chid >= 0 && chid < dev_priv->engine.fifo.channels)) + if (likely(chid >= 0 && chid < pfifo->channels)) chan = dev_priv->channels.ptr[chid]; if (unlikely(!chan)) goto out; @@ -357,7 +286,6 @@ nouveau_fifo_swmthd(struct drm_device *dev, u32 chid, u32 addr, u32 data) if (unlikely(!obj || obj->engine != NVOBJ_ENGINE_SW)) break; - chan->sw_subchannel[subc] = obj->class; engine = 0x0000000f << (subc * 4); nv_mask(dev, NV04_PFIFO_CACHE1_ENGINE, engine, 0x00000000); @@ -368,7 +296,7 @@ nouveau_fifo_swmthd(struct drm_device *dev, u32 chid, u32 addr, u32 data) if (unlikely(((engine >> (subc * 4)) & 0xf) != 0)) break; - if (!nouveau_gpuobj_mthd_call(chan, chan->sw_subchannel[subc], + if (!nouveau_gpuobj_mthd_call(chan, nouveau_software_class(dev), mthd, data)) handled = true; break; @@ -391,8 +319,8 @@ static const char *nv_dma_state_err(u32 state) void nv04_fifo_isr(struct drm_device *dev) { + struct nouveau_fifo_priv *pfifo = nv_engine(dev, NVOBJ_ENGINE_FIFO); struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_engine *engine = &dev_priv->engine; uint32_t status, reassign; int cnt = 0; @@ -402,7 +330,7 @@ nv04_fifo_isr(struct drm_device *dev) nv_wr32(dev, NV03_PFIFO_CACHES, 0); - chid = engine->fifo.channel_id(dev); + chid = nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH1) & pfifo->channels; get = nv_rd32(dev, NV03_PFIFO_CACHE1_GET); if (status & NV_PFIFO_INTR_CACHE_ERROR) { @@ -541,3 +469,38 @@ nv04_fifo_isr(struct drm_device *dev) nv_wr32(dev, NV03_PMC_INTR_0, NV_PMC_INTR_0_PFIFO_PENDING); } + +void +nv04_fifo_destroy(struct drm_device *dev, int engine) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv04_fifo_priv *priv = nv_engine(dev, engine); + + nouveau_irq_unregister(dev, 8); + + dev_priv->eng[engine] = NULL; + kfree(priv); +} + +int +nv04_fifo_create(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv04_fifo_priv *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base.base.destroy = nv04_fifo_destroy; + priv->base.base.init = nv04_fifo_init; + priv->base.base.fini = nv04_fifo_fini; + priv->base.base.context_new = nv04_fifo_context_new; + priv->base.base.context_del = nv04_fifo_context_del; + priv->base.channels = 15; + priv->ramfc_desc = nv04_ramfc; + dev_priv->eng[NVOBJ_ENGINE_FIFO] = &priv->base.base; + + nouveau_irq_register(dev, 8, nv04_fifo_isr); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nv04_graph.c b/drivers/gpu/drm/nouveau/nv04_graph.c index dbdea8ed392..72f1a62903b 100644 --- a/drivers/gpu/drm/nouveau/nv04_graph.c +++ b/drivers/gpu/drm/nouveau/nv04_graph.c @@ -356,12 +356,12 @@ static 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; + int chid = 15; 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) + if (chid > 15) return NULL; return dev_priv->channels.ptr[chid]; @@ -404,7 +404,6 @@ nv04_graph_load_context(struct nouveau_channel *chan) static int nv04_graph_unload_context(struct drm_device *dev) { - struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_channel *chan = NULL; struct graph_state *ctx; uint32_t tmp; @@ -420,7 +419,7 @@ nv04_graph_unload_context(struct drm_device *dev) 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; + tmp |= 15 << 24; nv_wr32(dev, NV04_PGRAPH_CTX_USER, tmp); return 0; } @@ -495,7 +494,6 @@ nv04_graph_object_new(struct nouveau_channel *chan, int engine, static int nv04_graph_init(struct drm_device *dev, int engine) { - struct drm_nouveau_private *dev_priv = dev->dev_private; uint32_t tmp; nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) & @@ -527,7 +525,7 @@ nv04_graph_init(struct drm_device *dev, int engine) 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; + tmp |= 15 << 24; nv_wr32(dev, NV04_PGRAPH_CTX_USER, tmp); /* These don't belong here, they're part of a per-channel context */ @@ -550,28 +548,6 @@ nv04_graph_fini(struct drm_device *dev, int engine, bool suspend) return 0; } -static int -nv04_graph_mthd_set_ref(struct nouveau_channel *chan, - u32 class, u32 mthd, u32 data) -{ - atomic_set(&chan->fence.last_sequence_irq, data); - return 0; -} - -int -nv04_graph_mthd_page_flip(struct nouveau_channel *chan, - u32 class, u32 mthd, u32 data) -{ - struct drm_device *dev = chan->dev; - struct nouveau_page_flip_state s; - - if (!nouveau_finish_page_flip(chan, &s)) - nv_set_crtc_base(dev, s.crtc, - s.offset + s.y * s.pitch + s.x * s.bpp / 8); - - return 0; -} - /* * Software methods, why they are needed, and how they all work: * @@ -1020,7 +996,8 @@ nv04_graph_context_switch(struct drm_device *dev) nv04_graph_unload_context(dev); /* Load context for next channel */ - chid = dev_priv->engine.fifo.channel_id(dev); + chid = nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH1) & + NV03_PFIFO_CACHE1_PUSH1_CHID_MASK; chan = dev_priv->channels.ptr[chid]; if (chan) nv04_graph_load_context(chan); @@ -1345,9 +1322,5 @@ nv04_graph_create(struct drm_device *dev) NVOBJ_MTHD (dev, 0x005e, 0x0198, nv04_graph_mthd_bind_surf2d); NVOBJ_MTHD (dev, 0x005e, 0x02fc, nv04_graph_mthd_set_operation); - /* nvsw */ - NVOBJ_CLASS(dev, 0x506e, SW); - NVOBJ_MTHD (dev, 0x506e, 0x0150, nv04_graph_mthd_set_ref); - NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip); return 0; } diff --git a/drivers/gpu/drm/nouveau/nv04_instmem.c b/drivers/gpu/drm/nouveau/nv04_instmem.c index c1248e0740a..ef7a934a499 100644 --- a/drivers/gpu/drm/nouveau/nv04_instmem.c +++ b/drivers/gpu/drm/nouveau/nv04_instmem.c @@ -1,6 +1,8 @@ #include "drmP.h" #include "drm.h" + #include "nouveau_drv.h" +#include "nouveau_fifo.h" #include "nouveau_ramht.h" /* returns the size of fifo context */ @@ -10,12 +12,15 @@ nouveau_fifo_ctx_size(struct drm_device *dev) struct drm_nouveau_private *dev_priv = dev->dev_private; if (dev_priv->chipset >= 0x40) - return 128; + return 128 * 32; else if (dev_priv->chipset >= 0x17) - return 64; + return 64 * 32; + else + if (dev_priv->chipset >= 0x10) + return 32 * 32; - return 32; + return 32 * 16; } int nv04_instmem_init(struct drm_device *dev) @@ -39,14 +44,10 @@ int nv04_instmem_init(struct drm_device *dev) else if (nv44_graph_class(dev)) rsvd = 0x4980 * vs; else rsvd = 0x4a40 * vs; rsvd += 16 * 1024; - rsvd *= dev_priv->engine.fifo.channels; - - /* pciegart table */ - if (pci_is_pcie(dev->pdev)) - rsvd += 512 * 1024; + rsvd *= 32; /* per-channel */ - /* object storage */ - rsvd += 512 * 1024; + rsvd += 512 * 1024; /* pci(e)gart table */ + rsvd += 512 * 1024; /* object storage */ dev_priv->ramin_rsvd_vram = round_up(rsvd, 4096); } else { @@ -71,7 +72,7 @@ int nv04_instmem_init(struct drm_device *dev) return ret; /* And RAMFC */ - length = dev_priv->engine.fifo.channels * nouveau_fifo_ctx_size(dev); + length = nouveau_fifo_ctx_size(dev); switch (dev_priv->card_type) { case NV_40: offset = 0x20000; diff --git a/drivers/gpu/drm/nouveau/nv04_software.c b/drivers/gpu/drm/nouveau/nv04_software.c new file mode 100644 index 00000000000..0c41abf4877 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv04_software.c @@ -0,0 +1,147 @@ +/* + * Copyright 2012 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_ramht.h" +#include "nouveau_fence.h" +#include "nouveau_software.h" +#include "nouveau_hw.h" + +struct nv04_software_priv { + struct nouveau_software_priv base; +}; + +struct nv04_software_chan { + struct nouveau_software_chan base; +}; + +static int +mthd_flip(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data) +{ + + struct nouveau_page_flip_state state; + + if (!nouveau_finish_page_flip(chan, &state)) { + nv_set_crtc_base(chan->dev, state.crtc, state.offset + + state.y * state.pitch + + state.x * state.bpp / 8); + } + + return 0; +} + +static int +nv04_software_context_new(struct nouveau_channel *chan, int engine) +{ + struct nv04_software_chan *pch; + + pch = kzalloc(sizeof(*pch), GFP_KERNEL); + if (!pch) + return -ENOMEM; + + nouveau_software_context_new(&pch->base); + chan->engctx[engine] = pch; + return 0; +} + +static void +nv04_software_context_del(struct nouveau_channel *chan, int engine) +{ + struct nv04_software_chan *pch = chan->engctx[engine]; + chan->engctx[engine] = NULL; + kfree(pch); +} + +static int +nv04_software_object_new(struct nouveau_channel *chan, int engine, + u32 handle, u16 class) +{ + struct drm_device *dev = chan->dev; + struct nouveau_gpuobj *obj = NULL; + int ret; + + ret = nouveau_gpuobj_new(dev, chan, 16, 16, 0, &obj); + if (ret) + return ret; + obj->engine = 0; + obj->class = class; + + ret = nouveau_ramht_insert(chan, handle, obj); + nouveau_gpuobj_ref(NULL, &obj); + return ret; +} + +static int +nv04_software_init(struct drm_device *dev, int engine) +{ + return 0; +} + +static int +nv04_software_fini(struct drm_device *dev, int engine, bool suspend) +{ + return 0; +} + +static void +nv04_software_destroy(struct drm_device *dev, int engine) +{ + struct nv04_software_priv *psw = nv_engine(dev, engine); + + NVOBJ_ENGINE_DEL(dev, SW); + kfree(psw); +} + +int +nv04_software_create(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv04_software_priv *psw; + + psw = kzalloc(sizeof(*psw), GFP_KERNEL); + if (!psw) + return -ENOMEM; + + psw->base.base.destroy = nv04_software_destroy; + psw->base.base.init = nv04_software_init; + psw->base.base.fini = nv04_software_fini; + psw->base.base.context_new = nv04_software_context_new; + psw->base.base.context_del = nv04_software_context_del; + psw->base.base.object_new = nv04_software_object_new; + nouveau_software_create(&psw->base); + + NVOBJ_ENGINE_ADD(dev, SW, &psw->base.base); + if (dev_priv->card_type <= NV_04) { + NVOBJ_CLASS(dev, 0x006e, SW); + NVOBJ_MTHD (dev, 0x006e, 0x0150, nv04_fence_mthd); + NVOBJ_MTHD (dev, 0x006e, 0x0500, mthd_flip); + } else { + NVOBJ_CLASS(dev, 0x016e, SW); + NVOBJ_MTHD (dev, 0x016e, 0x0500, mthd_flip); + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nv10_fence.c b/drivers/gpu/drm/nouveau/nv10_fence.c new file mode 100644 index 00000000000..8a1b7500918 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv10_fence.c @@ -0,0 +1,214 @@ +/* + * Copyright 2012 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 <bskeggs@redhat.com> + */ + +#include "drmP.h" +#include "nouveau_drv.h" +#include "nouveau_dma.h" +#include "nouveau_ramht.h" +#include "nouveau_fence.h" + +struct nv10_fence_chan { + struct nouveau_fence_chan base; +}; + +struct nv10_fence_priv { + struct nouveau_fence_priv base; + struct nouveau_bo *bo; + spinlock_t lock; + u32 sequence; +}; + +static int +nv10_fence_emit(struct nouveau_fence *fence) +{ + struct nouveau_channel *chan = fence->channel; + int ret = RING_SPACE(chan, 2); + if (ret == 0) { + BEGIN_NV04(chan, 0, NV10_SUBCHAN_REF_CNT, 1); + OUT_RING (chan, fence->sequence); + FIRE_RING (chan); + } + return ret; +} + + +static int +nv10_fence_sync(struct nouveau_fence *fence, + struct nouveau_channel *prev, struct nouveau_channel *chan) +{ + return -ENODEV; +} + +static int +nv17_fence_sync(struct nouveau_fence *fence, + struct nouveau_channel *prev, struct nouveau_channel *chan) +{ + struct nv10_fence_priv *priv = nv_engine(chan->dev, NVOBJ_ENGINE_FENCE); + u32 value; + int ret; + + if (!mutex_trylock(&prev->mutex)) + return -EBUSY; + + spin_lock(&priv->lock); + value = priv->sequence; + priv->sequence += 2; + spin_unlock(&priv->lock); + + ret = RING_SPACE(prev, 5); + if (!ret) { + BEGIN_NV04(prev, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 4); + OUT_RING (prev, NvSema); + OUT_RING (prev, 0); + OUT_RING (prev, value + 0); + OUT_RING (prev, value + 1); + FIRE_RING (prev); + } + + if (!ret && !(ret = RING_SPACE(chan, 5))) { + BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 4); + OUT_RING (chan, NvSema); + OUT_RING (chan, 0); + OUT_RING (chan, value + 1); + OUT_RING (chan, value + 2); + FIRE_RING (chan); + } + + mutex_unlock(&prev->mutex); + return 0; +} + +static u32 +nv10_fence_read(struct nouveau_channel *chan) +{ + return nvchan_rd32(chan, 0x0048); +} + +static void +nv10_fence_context_del(struct nouveau_channel *chan, int engine) +{ + struct nv10_fence_chan *fctx = chan->engctx[engine]; + nouveau_fence_context_del(&fctx->base); + chan->engctx[engine] = NULL; + kfree(fctx); +} + +static int +nv10_fence_context_new(struct nouveau_channel *chan, int engine) +{ + struct nv10_fence_priv *priv = nv_engine(chan->dev, engine); + struct nv10_fence_chan *fctx; + struct nouveau_gpuobj *obj; + int ret = 0; + + fctx = chan->engctx[engine] = kzalloc(sizeof(*fctx), GFP_KERNEL); + if (!fctx) + return -ENOMEM; + + nouveau_fence_context_new(&fctx->base); + + if (priv->bo) { + struct ttm_mem_reg *mem = &priv->bo->bo.mem; + + ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_FROM_MEMORY, + mem->start * PAGE_SIZE, mem->size, + NV_MEM_ACCESS_RW, + NV_MEM_TARGET_VRAM, &obj); + if (!ret) { + ret = nouveau_ramht_insert(chan, NvSema, obj); + nouveau_gpuobj_ref(NULL, &obj); + } + } + + if (ret) + nv10_fence_context_del(chan, engine); + return ret; +} + +static int +nv10_fence_fini(struct drm_device *dev, int engine, bool suspend) +{ + return 0; +} + +static int +nv10_fence_init(struct drm_device *dev, int engine) +{ + return 0; +} + +static void +nv10_fence_destroy(struct drm_device *dev, int engine) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv10_fence_priv *priv = nv_engine(dev, engine); + + nouveau_bo_ref(NULL, &priv->bo); + dev_priv->eng[engine] = NULL; + kfree(priv); +} + +int +nv10_fence_create(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv10_fence_priv *priv; + int ret = 0; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base.engine.destroy = nv10_fence_destroy; + priv->base.engine.init = nv10_fence_init; + priv->base.engine.fini = nv10_fence_fini; + priv->base.engine.context_new = nv10_fence_context_new; + priv->base.engine.context_del = nv10_fence_context_del; + priv->base.emit = nv10_fence_emit; + priv->base.read = nv10_fence_read; + priv->base.sync = nv10_fence_sync; + dev_priv->eng[NVOBJ_ENGINE_FENCE] = &priv->base.engine; + spin_lock_init(&priv->lock); + + if (dev_priv->chipset >= 0x17) { + ret = nouveau_bo_new(dev, 4096, 0x1000, TTM_PL_FLAG_VRAM, + 0, 0x0000, NULL, &priv->bo); + if (!ret) { + ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM); + if (!ret) + ret = nouveau_bo_map(priv->bo); + if (ret) + nouveau_bo_ref(NULL, &priv->bo); + } + + if (ret == 0) { + nouveau_bo_wr32(priv->bo, 0x000, 0x00000000); + priv->base.sync = nv17_fence_sync; + } + } + + if (ret) + nv10_fence_destroy(dev, NVOBJ_ENGINE_FENCE); + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nv10_fifo.c b/drivers/gpu/drm/nouveau/nv10_fifo.c index d2ecbff4bee..f1fe7d75824 100644 --- a/drivers/gpu/drm/nouveau/nv10_fifo.c +++ b/drivers/gpu/drm/nouveau/nv10_fifo.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Ben Skeggs. + * Copyright (C) 2012 Ben Skeggs. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining @@ -27,220 +27,112 @@ #include "drmP.h" #include "drm.h" #include "nouveau_drv.h" +#include "nouveau_fifo.h" +#include "nouveau_util.h" #include "nouveau_ramht.h" -#define NV10_RAMFC(c) (dev_priv->ramfc->pinst + ((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) +static struct ramfc_desc { + unsigned bits:6; + unsigned ctxs:5; + unsigned ctxp:8; + unsigned regs:5; + unsigned regp; +} nv10_ramfc[] = { + { 32, 0, 0x00, 0, NV04_PFIFO_CACHE1_DMA_PUT }, + { 32, 0, 0x04, 0, NV04_PFIFO_CACHE1_DMA_GET }, + { 32, 0, 0x08, 0, NV10_PFIFO_CACHE1_REF_CNT }, + { 16, 0, 0x0c, 0, NV04_PFIFO_CACHE1_DMA_INSTANCE }, + { 16, 16, 0x0c, 0, NV04_PFIFO_CACHE1_DMA_DCOUNT }, + { 32, 0, 0x10, 0, NV04_PFIFO_CACHE1_DMA_STATE }, + { 32, 0, 0x14, 0, NV04_PFIFO_CACHE1_DMA_FETCH }, + { 32, 0, 0x18, 0, NV04_PFIFO_CACHE1_ENGINE }, + { 32, 0, 0x1c, 0, NV04_PFIFO_CACHE1_PULL1 }, + {} +}; + +struct nv10_fifo_priv { + struct nouveau_fifo_priv base; + struct ramfc_desc *ramfc_desc; +}; + +struct nv10_fifo_chan { + struct nouveau_fifo_chan base; + struct nouveau_gpuobj *ramfc; +}; + +static int +nv10_fifo_context_new(struct nouveau_channel *chan, int engine) { - struct drm_nouveau_private *dev_priv = chan->dev->dev_private; struct drm_device *dev = chan->dev; - uint32_t fc = NV10_RAMFC(chan->id); + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv10_fifo_priv *priv = nv_engine(dev, engine); + struct nv10_fifo_chan *fctx; + unsigned long flags; int ret; - ret = nouveau_gpuobj_new_fake(dev, NV10_RAMFC(chan->id), ~0, - NV10_RAMFC__SIZE, NVOBJ_FLAG_ZERO_ALLOC | - NVOBJ_FLAG_ZERO_FREE, &chan->ramfc); - if (ret) - return ret; + fctx = chan->engctx[engine] = kzalloc(sizeof(*fctx), GFP_KERNEL); + if (!fctx) + return -ENOMEM; + /* map channel control registers */ chan->user = ioremap(pci_resource_start(dev->pdev, 0) + NV03_USER(chan->id), PAGE_SIZE); - if (!chan->user) - return -ENOMEM; + if (!chan->user) { + ret = -ENOMEM; + goto error; + } - /* Fill entries that are seen filled in dumps of nvidia driver just - * after channel's is put into DMA mode - */ - nv_wi32(dev, fc + 0, chan->pushbuf_base); - nv_wi32(dev, fc + 4, chan->pushbuf_base); - nv_wi32(dev, fc + 12, chan->pushbuf->pinst >> 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 | + /* initialise default fifo context */ + ret = nouveau_gpuobj_new_fake(dev, dev_priv->ramfc->pinst + + chan->id * 32, ~0, 32, + NVOBJ_FLAG_ZERO_FREE, &fctx->ramfc); + if (ret) + goto error; + + nv_wo32(fctx->ramfc, 0x00, chan->pushbuf_base); + nv_wo32(fctx->ramfc, 0x04, chan->pushbuf_base); + nv_wo32(fctx->ramfc, 0x08, 0x00000000); + nv_wo32(fctx->ramfc, 0x0c, chan->pushbuf->pinst >> 4); + nv_wo32(fctx->ramfc, 0x10, 0x00000000); + nv_wo32(fctx->ramfc, 0x14, NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES | + NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES | #ifdef __BIG_ENDIAN - NV_PFIFO_CACHE1_BIG_ENDIAN | + NV_PFIFO_CACHE1_BIG_ENDIAN | #endif - 0); - - /* enable the fifo dma operation */ - nv_wr32(dev, NV04_PFIFO_MODE, - nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id)); - return 0; -} - -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; - - 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: - 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_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8); + nv_wo32(fctx->ramfc, 0x18, 0x00000000); + nv_wo32(fctx->ramfc, 0x1c, 0x00000000); - nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, - NV03_PFIFO_CACHE1_PUSH1_DMA | chan->id); - nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 1); + /* enable dma mode on the channel */ + spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + nv_mask(dev, NV04_PFIFO_MODE, (1 << chan->id), (1 << chan->id)); + spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); - /* 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; +error: + if (ret) + priv->base.base.context_del(chan, engine); + return ret; } 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); - - 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: - 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) +nv10_fifo_create(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv10_fifo_priv *priv; - nv_wr32(dev, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ | - ((dev_priv->ramht->bits - 9) << 16) | - (dev_priv->ramht->gpuobj->pinst >> 8)); - nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro->pinst >> 8); + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; - if (dev_priv->chipset < 0x17) { - nv_wr32(dev, NV03_PFIFO_RAMFC, dev_priv->ramfc->pinst >> 8); - } else { - nv_wr32(dev, NV03_PFIFO_RAMFC, (dev_priv->ramfc->pinst >> 8) | - (1 << 16) /* 64 Bytes entry*/); - /* XXX nvidia blob set bit 18, 21,23 for nv20 & nv30 */ - } -} + priv->base.base.destroy = nv04_fifo_destroy; + priv->base.base.init = nv04_fifo_init; + priv->base.base.fini = nv04_fifo_fini; + priv->base.base.context_new = nv10_fifo_context_new; + priv->base.base.context_del = nv04_fifo_context_del; + priv->base.channels = 31; + priv->ramfc_desc = nv10_ramfc; + dev_priv->eng[NVOBJ_ENGINE_FIFO] = &priv->base.base; -static void -nv10_fifo_init_intr(struct drm_device *dev) -{ nouveau_irq_register(dev, 8, nv04_fifo_isr); - 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->channels.ptr[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 index 7255e4a4d3f..fb1d88a951d 100644 --- a/drivers/gpu/drm/nouveau/nv10_graph.c +++ b/drivers/gpu/drm/nouveau/nv10_graph.c @@ -759,7 +759,6 @@ static int nv10_graph_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; struct graph_state *ctx; uint32_t tmp; @@ -782,7 +781,7 @@ nv10_graph_unload_context(struct drm_device *dev) nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000000); tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff; - tmp |= (pfifo->channels - 1) << 24; + tmp |= 31 << 24; nv_wr32(dev, NV10_PGRAPH_CTX_USER, tmp); return 0; } @@ -822,12 +821,12 @@ 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; + int chid = 31; 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) + if (chid >= 31) return NULL; return dev_priv->channels.ptr[chid]; @@ -948,7 +947,7 @@ nv10_graph_init(struct drm_device *dev, int engine) nv_wr32(dev, NV10_PGRAPH_STATE, 0xFFFFFFFF); tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff; - tmp |= (dev_priv->engine.fifo.channels - 1) << 24; + tmp |= 31 << 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); @@ -1153,10 +1152,6 @@ nv10_graph_create(struct drm_device *dev) NVOBJ_ENGINE_ADD(dev, GR, &pgraph->base); nouveau_irq_register(dev, 12, nv10_graph_isr); - /* nvsw */ - NVOBJ_CLASS(dev, 0x506e, SW); - NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip); - NVOBJ_CLASS(dev, 0x0030, GR); /* null */ NVOBJ_CLASS(dev, 0x0039, GR); /* m2mf */ NVOBJ_CLASS(dev, 0x004a, GR); /* gdirect */ diff --git a/drivers/gpu/drm/nouveau/nv17_fifo.c b/drivers/gpu/drm/nouveau/nv17_fifo.c new file mode 100644 index 00000000000..d9e482e4abe --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv17_fifo.c @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2012 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_fifo.h" +#include "nouveau_util.h" +#include "nouveau_ramht.h" + +static struct ramfc_desc { + unsigned bits:6; + unsigned ctxs:5; + unsigned ctxp:8; + unsigned regs:5; + unsigned regp; +} nv17_ramfc[] = { + { 32, 0, 0x00, 0, NV04_PFIFO_CACHE1_DMA_PUT }, + { 32, 0, 0x04, 0, NV04_PFIFO_CACHE1_DMA_GET }, + { 32, 0, 0x08, 0, NV10_PFIFO_CACHE1_REF_CNT }, + { 16, 0, 0x0c, 0, NV04_PFIFO_CACHE1_DMA_INSTANCE }, + { 16, 16, 0x0c, 0, NV04_PFIFO_CACHE1_DMA_DCOUNT }, + { 32, 0, 0x10, 0, NV04_PFIFO_CACHE1_DMA_STATE }, + { 32, 0, 0x14, 0, NV04_PFIFO_CACHE1_DMA_FETCH }, + { 32, 0, 0x18, 0, NV04_PFIFO_CACHE1_ENGINE }, + { 32, 0, 0x1c, 0, NV04_PFIFO_CACHE1_PULL1 }, + { 32, 0, 0x20, 0, NV10_PFIFO_CACHE1_ACQUIRE_VALUE }, + { 32, 0, 0x24, 0, NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP }, + { 32, 0, 0x28, 0, NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT }, + { 32, 0, 0x2c, 0, NV10_PFIFO_CACHE1_SEMAPHORE }, + { 32, 0, 0x30, 0, NV10_PFIFO_CACHE1_DMA_SUBROUTINE }, + {} +}; + +struct nv17_fifo_priv { + struct nouveau_fifo_priv base; + struct ramfc_desc *ramfc_desc; +}; + +struct nv17_fifo_chan { + struct nouveau_fifo_chan base; + struct nouveau_gpuobj *ramfc; +}; + +static int +nv17_fifo_context_new(struct nouveau_channel *chan, int engine) +{ + struct drm_device *dev = chan->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv17_fifo_priv *priv = nv_engine(dev, engine); + struct nv17_fifo_chan *fctx; + unsigned long flags; + int ret; + + fctx = chan->engctx[engine] = kzalloc(sizeof(*fctx), GFP_KERNEL); + if (!fctx) + return -ENOMEM; + + /* map channel control registers */ + chan->user = ioremap(pci_resource_start(dev->pdev, 0) + + NV03_USER(chan->id), PAGE_SIZE); + if (!chan->user) { + ret = -ENOMEM; + goto error; + } + + /* initialise default fifo context */ + ret = nouveau_gpuobj_new_fake(dev, dev_priv->ramfc->pinst + + chan->id * 64, ~0, 64, + NVOBJ_FLAG_ZERO_ALLOC | + NVOBJ_FLAG_ZERO_FREE, &fctx->ramfc); + if (ret) + goto error; + + nv_wo32(fctx->ramfc, 0x00, chan->pushbuf_base); + nv_wo32(fctx->ramfc, 0x04, chan->pushbuf_base); + nv_wo32(fctx->ramfc, 0x0c, chan->pushbuf->pinst >> 4); + nv_wo32(fctx->ramfc, 0x14, NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES | + NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES | +#ifdef __BIG_ENDIAN + NV_PFIFO_CACHE1_BIG_ENDIAN | +#endif + NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8); + + /* enable dma mode on the channel */ + spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + nv_mask(dev, NV04_PFIFO_MODE, (1 << chan->id), (1 << chan->id)); + spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); + +error: + if (ret) + priv->base.base.context_del(chan, engine); + return ret; +} + +static int +nv17_fifo_init(struct drm_device *dev, int engine) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv17_fifo_priv *priv = nv_engine(dev, engine); + int i; + + nv_mask(dev, NV03_PMC_ENABLE, NV_PMC_ENABLE_PFIFO, 0); + nv_mask(dev, NV03_PMC_ENABLE, NV_PMC_ENABLE_PFIFO, NV_PMC_ENABLE_PFIFO); + + nv_wr32(dev, NV04_PFIFO_DELAY_0, 0x000000ff); + nv_wr32(dev, NV04_PFIFO_DMA_TIMESLICE, 0x0101ffff); + + nv_wr32(dev, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ | + ((dev_priv->ramht->bits - 9) << 16) | + (dev_priv->ramht->gpuobj->pinst >> 8)); + nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro->pinst >> 8); + nv_wr32(dev, NV03_PFIFO_RAMFC, 0x00010000 | + dev_priv->ramfc->pinst >> 8); + + nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, priv->base.channels); + + nv_wr32(dev, NV03_PFIFO_INTR_0, 0xffffffff); + nv_wr32(dev, NV03_PFIFO_INTR_EN_0, 0xffffffff); + + nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, 1); + nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1); + nv_wr32(dev, NV03_PFIFO_CACHES, 1); + + for (i = 0; i < priv->base.channels; i++) { + if (dev_priv->channels.ptr[i]) + nv_mask(dev, NV04_PFIFO_MODE, (1 << i), (1 << i)); + } + + return 0; +} + +int +nv17_fifo_create(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv17_fifo_priv *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base.base.destroy = nv04_fifo_destroy; + priv->base.base.init = nv17_fifo_init; + priv->base.base.fini = nv04_fifo_fini; + priv->base.base.context_new = nv17_fifo_context_new; + priv->base.base.context_del = nv04_fifo_context_del; + priv->base.channels = 31; + priv->ramfc_desc = nv17_ramfc; + dev_priv->eng[NVOBJ_ENGINE_FIFO] = &priv->base.base; + + nouveau_irq_register(dev, 8, nv04_fifo_isr); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nv20_graph.c b/drivers/gpu/drm/nouveau/nv20_graph.c index 183e37512ef..e34ea30758f 100644 --- a/drivers/gpu/drm/nouveau/nv20_graph.c +++ b/drivers/gpu/drm/nouveau/nv20_graph.c @@ -43,8 +43,6 @@ struct nv20_graph_engine { int nv20_graph_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; struct nouveau_gpuobj *grctx; u32 tmp; @@ -62,7 +60,7 @@ nv20_graph_unload_context(struct drm_device *dev) nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000000); tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff; - tmp |= (pfifo->channels - 1) << 24; + tmp |= 31 << 24; nv_wr32(dev, NV10_PGRAPH_CTX_USER, tmp); return 0; } @@ -796,10 +794,6 @@ nv20_graph_create(struct drm_device *dev) NVOBJ_ENGINE_ADD(dev, GR, &pgraph->base); nouveau_irq_register(dev, 12, nv20_graph_isr); - /* nvsw */ - NVOBJ_CLASS(dev, 0x506e, SW); - NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip); - NVOBJ_CLASS(dev, 0x0030, GR); /* null */ NVOBJ_CLASS(dev, 0x0039, GR); /* m2mf */ NVOBJ_CLASS(dev, 0x004a, GR); /* gdirect */ diff --git a/drivers/gpu/drm/nouveau/nv31_mpeg.c b/drivers/gpu/drm/nouveau/nv31_mpeg.c index 6f06a0713f0..5f239bf658c 100644 --- a/drivers/gpu/drm/nouveau/nv31_mpeg.c +++ b/drivers/gpu/drm/nouveau/nv31_mpeg.c @@ -24,6 +24,7 @@ #include "drmP.h" #include "nouveau_drv.h" +#include "nouveau_fifo.h" #include "nouveau_ramht.h" struct nv31_mpeg_engine { @@ -208,6 +209,7 @@ nv31_mpeg_mthd_dma(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data) static int nv31_mpeg_isr_chid(struct drm_device *dev, u32 inst) { + struct nouveau_fifo_priv *pfifo = nv_engine(dev, NVOBJ_ENGINE_FIFO); struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_gpuobj *ctx; unsigned long flags; @@ -218,7 +220,7 @@ nv31_mpeg_isr_chid(struct drm_device *dev, u32 inst) return 0; spin_lock_irqsave(&dev_priv->channels.lock, flags); - for (i = 0; i < dev_priv->engine.fifo.channels; i++) { + for (i = 0; i < pfifo->channels; i++) { if (!dev_priv->channels.ptr[i]) continue; diff --git a/drivers/gpu/drm/nouveau/nv40_fifo.c b/drivers/gpu/drm/nouveau/nv40_fifo.c index 68cb2d991c8..cdc818479b0 100644 --- a/drivers/gpu/drm/nouveau/nv40_fifo.c +++ b/drivers/gpu/drm/nouveau/nv40_fifo.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Ben Skeggs. + * Copyright (C) 2012 Ben Skeggs. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining @@ -25,215 +25,123 @@ */ #include "drmP.h" +#include "drm.h" #include "nouveau_drv.h" -#include "nouveau_drm.h" +#include "nouveau_fifo.h" +#include "nouveau_util.h" #include "nouveau_ramht.h" -#define NV40_RAMFC(c) (dev_priv->ramfc->pinst + ((c) * NV40_RAMFC__SIZE)) -#define NV40_RAMFC__SIZE 128 - -int -nv40_fifo_create_context(struct nouveau_channel *chan) +static struct ramfc_desc { + unsigned bits:6; + unsigned ctxs:5; + unsigned ctxp:8; + unsigned regs:5; + unsigned regp; +} nv40_ramfc[] = { + { 32, 0, 0x00, 0, NV04_PFIFO_CACHE1_DMA_PUT }, + { 32, 0, 0x04, 0, NV04_PFIFO_CACHE1_DMA_GET }, + { 32, 0, 0x08, 0, NV10_PFIFO_CACHE1_REF_CNT }, + { 32, 0, 0x0c, 0, NV04_PFIFO_CACHE1_DMA_INSTANCE }, + { 32, 0, 0x10, 0, NV04_PFIFO_CACHE1_DMA_DCOUNT }, + { 32, 0, 0x14, 0, NV04_PFIFO_CACHE1_DMA_STATE }, + { 28, 0, 0x18, 0, NV04_PFIFO_CACHE1_DMA_FETCH }, + { 2, 28, 0x18, 28, 0x002058 }, + { 32, 0, 0x1c, 0, NV04_PFIFO_CACHE1_ENGINE }, + { 32, 0, 0x20, 0, NV04_PFIFO_CACHE1_PULL1 }, + { 32, 0, 0x24, 0, NV10_PFIFO_CACHE1_ACQUIRE_VALUE }, + { 32, 0, 0x28, 0, NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP }, + { 32, 0, 0x2c, 0, NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT }, + { 32, 0, 0x30, 0, NV10_PFIFO_CACHE1_SEMAPHORE }, + { 32, 0, 0x34, 0, NV10_PFIFO_CACHE1_DMA_SUBROUTINE }, + { 32, 0, 0x38, 0, NV40_PFIFO_GRCTX_INSTANCE }, + { 17, 0, 0x3c, 0, NV04_PFIFO_DMA_TIMESLICE }, + { 32, 0, 0x40, 0, 0x0032e4 }, + { 32, 0, 0x44, 0, 0x0032e8 }, + { 32, 0, 0x4c, 0, 0x002088 }, + { 32, 0, 0x50, 0, 0x003300 }, + { 32, 0, 0x54, 0, 0x00330c }, + {} +}; + +struct nv40_fifo_priv { + struct nouveau_fifo_priv base; + struct ramfc_desc *ramfc_desc; +}; + +struct nv40_fifo_chan { + struct nouveau_fifo_chan base; + struct nouveau_gpuobj *ramfc; +}; + +static int +nv40_fifo_context_new(struct nouveau_channel *chan, int engine) { struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; - uint32_t fc = NV40_RAMFC(chan->id); + struct nv40_fifo_priv *priv = nv_engine(dev, engine); + struct nv40_fifo_chan *fctx; unsigned long flags; int ret; - ret = nouveau_gpuobj_new_fake(dev, NV40_RAMFC(chan->id), ~0, - NV40_RAMFC__SIZE, NVOBJ_FLAG_ZERO_ALLOC | - NVOBJ_FLAG_ZERO_FREE, &chan->ramfc); - if (ret) - return ret; - - chan->user = ioremap(pci_resource_start(dev->pdev, 0) + - NV40_USER(chan->id), PAGE_SIZE); - if (!chan->user) + fctx = chan->engctx[engine] = kzalloc(sizeof(*fctx), GFP_KERNEL); + if (!fctx) return -ENOMEM; - spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + /* map channel control registers */ + chan->user = ioremap(pci_resource_start(dev->pdev, 0) + + NV03_USER(chan->id), PAGE_SIZE); + if (!chan->user) { + ret = -ENOMEM; + goto error; + } - nv_wi32(dev, fc + 0, chan->pushbuf_base); - nv_wi32(dev, fc + 4, chan->pushbuf_base); - nv_wi32(dev, fc + 12, chan->pushbuf->pinst >> 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 | + /* initialise default fifo context */ + ret = nouveau_gpuobj_new_fake(dev, dev_priv->ramfc->pinst + + chan->id * 128, ~0, 128, + NVOBJ_FLAG_ZERO_ALLOC | + NVOBJ_FLAG_ZERO_FREE, &fctx->ramfc); + if (ret) + goto error; + + nv_wo32(fctx->ramfc, 0x00, chan->pushbuf_base); + nv_wo32(fctx->ramfc, 0x04, chan->pushbuf_base); + nv_wo32(fctx->ramfc, 0x0c, chan->pushbuf->pinst >> 4); + nv_wo32(fctx->ramfc, 0x18, 0x30000000 | + NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES | + NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES | #ifdef __BIG_ENDIAN - NV_PFIFO_CACHE1_BIG_ENDIAN | + NV_PFIFO_CACHE1_BIG_ENDIAN | #endif - 0x30000000 /* no idea.. */); - nv_wi32(dev, fc + 60, 0x0001FFFF); - - /* enable the fifo dma operation */ - nv_wr32(dev, NV04_PFIFO_MODE, - nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id)); + NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8); + nv_wo32(fctx->ramfc, 0x3c, 0x0001ffff); + /* enable dma mode on the channel */ + spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + nv_mask(dev, NV04_PFIFO_MODE, (1 << chan->id), (1 << chan->id)); spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); - return 0; -} - -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; - - 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)); + /*XXX: remove this later, need fifo engine context commit hook */ + nouveau_gpuobj_ref(fctx->ramfc, &chan->ramfc); - /* 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)); - nv_wr32(dev, 0x330c, nv_ri32(dev, fc + 84)); - - 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; +error: + if (ret) + priv->base.base.context_del(chan, engine); + return ret; } -int -nv40_fifo_unload_context(struct drm_device *dev) +static int +nv40_fifo_init(struct drm_device *dev, int engine) { 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); - - 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 - nv_wi32(dev, fc + 84, nv_rd32(dev, 0x330c)); - - 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) -{ + struct nv40_fifo_priv *priv = nv_engine(dev, engine); 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_mask(dev, NV03_PMC_ENABLE, NV_PMC_ENABLE_PFIFO, 0); + nv_mask(dev, NV03_PMC_ENABLE, NV_PMC_ENABLE_PFIFO, 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, 0x002044, 0x2101ffff); + nv_wr32(dev, 0x002058, 0x00000001); nv_wr32(dev, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ | ((dev_priv->ramht->bits - 9) << 16) | @@ -244,64 +152,59 @@ nv40_fifo_init_ramxx(struct drm_device *dev) case 0x47: case 0x49: case 0x4b: - nv_wr32(dev, 0x2230, 1); - break; - default: - break; - } - - switch (dev_priv->chipset) { + nv_wr32(dev, 0x002230, 0x00000001); 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); + nv_wr32(dev, 0x002220, 0x00030002); break; default: - nv_wr32(dev, 0x2230, 0); - nv_wr32(dev, NV40_PFIFO_RAMFC, - ((dev_priv->vram_size - 512 * 1024 + - dev_priv->ramfc->pinst) >> 16) | (3 << 16)); + nv_wr32(dev, 0x002230, 0x00000000); + nv_wr32(dev, 0x002220, ((dev_priv->vram_size - 512 * 1024 + + dev_priv->ramfc->pinst) >> 16) | + 0x00030000); break; } -} -static void -nv40_fifo_init_intr(struct drm_device *dev) -{ - nouveau_irq_register(dev, 8, nv04_fifo_isr); - nv_wr32(dev, 0x002100, 0xffffffff); - nv_wr32(dev, 0x002140, 0xffffffff); + nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, priv->base.channels); + + nv_wr32(dev, NV03_PFIFO_INTR_0, 0xffffffff); + nv_wr32(dev, NV03_PFIFO_INTR_EN_0, 0xffffffff); + + nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, 1); + nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1); + nv_wr32(dev, NV03_PFIFO_CACHES, 1); + + for (i = 0; i < priv->base.channels; i++) { + if (dev_priv->channels.ptr[i]) + nv_mask(dev, NV04_PFIFO_MODE, (1 << i), (1 << i)); + } + + return 0; } int -nv40_fifo_init(struct drm_device *dev) +nv40_fifo_create(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); + struct nv40_fifo_priv *priv; - 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); + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; - for (i = 0; i < dev_priv->engine.fifo.channels; i++) { - if (dev_priv->channels.ptr[i]) { - uint32_t mode = nv_rd32(dev, NV04_PFIFO_MODE); - nv_wr32(dev, NV04_PFIFO_MODE, mode | (1 << i)); - } - } + priv->base.base.destroy = nv04_fifo_destroy; + priv->base.base.init = nv40_fifo_init; + priv->base.base.fini = nv04_fifo_fini; + priv->base.base.context_new = nv40_fifo_context_new; + priv->base.base.context_del = nv04_fifo_context_del; + priv->base.channels = 31; + priv->ramfc_desc = nv40_ramfc; + dev_priv->eng[NVOBJ_ENGINE_FIFO] = &priv->base.base; + nouveau_irq_register(dev, 8, nv04_fifo_isr); return 0; } diff --git a/drivers/gpu/drm/nouveau/nv40_graph.c b/drivers/gpu/drm/nouveau/nv40_graph.c index ba14a93d8af..aa9e2df64a2 100644 --- a/drivers/gpu/drm/nouveau/nv40_graph.c +++ b/drivers/gpu/drm/nouveau/nv40_graph.c @@ -27,7 +27,7 @@ #include "drmP.h" #include "drm.h" #include "nouveau_drv.h" -#include "nouveau_grctx.h" +#include "nouveau_fifo.h" #include "nouveau_ramht.h" struct nv40_graph_engine { @@ -42,7 +42,6 @@ nv40_graph_context_new(struct nouveau_channel *chan, int engine) struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_gpuobj *grctx = NULL; - struct nouveau_grctx ctx = {}; unsigned long flags; int ret; @@ -52,11 +51,7 @@ nv40_graph_context_new(struct nouveau_channel *chan, int engine) return ret; /* Initialise default context values */ - ctx.dev = chan->dev; - ctx.mode = NOUVEAU_GRCTX_VALS; - ctx.data = grctx; - nv40_grctx_init(&ctx); - + nv40_grctx_fill(dev, grctx); nv_wo32(grctx, 0, grctx->vinst); /* init grctx pointer in ramfc, and on PFIFO if channel is @@ -184,8 +179,7 @@ nv40_graph_init(struct drm_device *dev, int engine) struct nv40_graph_engine *pgraph = nv_engine(dev, engine); struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_fb_engine *pfb = &dev_priv->engine.fb; - struct nouveau_grctx ctx = {}; - uint32_t vramsz, *cp; + uint32_t vramsz; int i, j; nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) & @@ -193,22 +187,8 @@ nv40_graph_init(struct drm_device *dev, int engine) nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PGRAPH); - cp = kmalloc(sizeof(*cp) * 256, GFP_KERNEL); - if (!cp) - return -ENOMEM; - - ctx.dev = dev; - ctx.mode = NOUVEAU_GRCTX_PROG; - ctx.data = cp; - ctx.ctxprog_max = 256; - nv40_grctx_init(&ctx); - pgraph->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]); - - kfree(cp); + /* generate and upload context program */ + nv40_grctx_init(dev, &pgraph->grctx_size); /* No context present currently */ nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, 0x00000000); @@ -366,13 +346,14 @@ nv40_graph_fini(struct drm_device *dev, int engine, bool suspend) static int nv40_graph_isr_chid(struct drm_device *dev, u32 inst) { + struct nouveau_fifo_priv *pfifo = nv_engine(dev, NVOBJ_ENGINE_FIFO); struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_gpuobj *grctx; unsigned long flags; int i; spin_lock_irqsave(&dev_priv->channels.lock, flags); - for (i = 0; i < dev_priv->engine.fifo.channels; i++) { + for (i = 0; i < pfifo->channels; i++) { if (!dev_priv->channels.ptr[i]) continue; grctx = dev_priv->channels.ptr[i]->engctx[NVOBJ_ENGINE_GR]; @@ -460,7 +441,6 @@ nv40_graph_create(struct drm_device *dev) NVOBJ_ENGINE_ADD(dev, GR, &pgraph->base); nouveau_irq_register(dev, 12, nv40_graph_isr); - NVOBJ_CLASS(dev, 0x506e, SW); /* nvsw */ NVOBJ_CLASS(dev, 0x0030, GR); /* null */ NVOBJ_CLASS(dev, 0x0039, GR); /* m2mf */ NVOBJ_CLASS(dev, 0x004a, GR); /* gdirect */ @@ -483,8 +463,5 @@ nv40_graph_create(struct drm_device *dev) else NVOBJ_CLASS(dev, 0x4097, GR); - /* nvsw */ - NVOBJ_CLASS(dev, 0x506e, SW); - NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip); return 0; } diff --git a/drivers/gpu/drm/nouveau/nv40_grctx.c b/drivers/gpu/drm/nouveau/nv40_grctx.c index f70447d131d..be0a74750fb 100644 --- a/drivers/gpu/drm/nouveau/nv40_grctx.c +++ b/drivers/gpu/drm/nouveau/nv40_grctx.c @@ -595,8 +595,8 @@ nv40_graph_construct_shader(struct nouveau_grctx *ctx) } } -void -nv40_grctx_init(struct nouveau_grctx *ctx) +static void +nv40_grctx_generate(struct nouveau_grctx *ctx) { /* decide whether we're loading/unloading the context */ cp_bra (ctx, AUTO_SAVE, PENDING, cp_setup_save); @@ -660,3 +660,31 @@ nv40_grctx_init(struct nouveau_grctx *ctx) cp_out (ctx, CP_END); } +void +nv40_grctx_fill(struct drm_device *dev, struct nouveau_gpuobj *mem) +{ + nv40_grctx_generate(&(struct nouveau_grctx) { + .dev = dev, + .mode = NOUVEAU_GRCTX_VALS, + .data = mem, + }); +} + +void +nv40_grctx_init(struct drm_device *dev, u32 *size) +{ + u32 ctxprog[256], i; + struct nouveau_grctx ctx = { + .dev = dev, + .mode = NOUVEAU_GRCTX_PROG, + .data = ctxprog, + .ctxprog_max = ARRAY_SIZE(ctxprog) + }; + + nv40_grctx_generate(&ctx); + + 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, ctxprog[i]); + *size = ctx.ctxvals_pos * 4; +} diff --git a/drivers/gpu/drm/nouveau/nv40_pm.c b/drivers/gpu/drm/nouveau/nv40_pm.c index c7615381c5d..e66273aff49 100644 --- a/drivers/gpu/drm/nouveau/nv40_pm.c +++ b/drivers/gpu/drm/nouveau/nv40_pm.c @@ -27,6 +27,7 @@ #include "nouveau_bios.h" #include "nouveau_pm.h" #include "nouveau_hw.h" +#include "nouveau_fifo.h" #define min2(a,b) ((a) < (b) ? (a) : (b)) diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c index 701b927998b..97a477b3d52 100644 --- a/drivers/gpu/drm/nouveau/nv50_crtc.c +++ b/drivers/gpu/drm/nouveau/nv50_crtc.c @@ -79,15 +79,15 @@ nv50_crtc_blank(struct nouveau_crtc *nv_crtc, bool blanked) NV_ERROR(dev, "no space while blanking crtc\n"); return ret; } - BEGIN_RING(evo, 0, NV50_EVO_CRTC(index, CLUT_MODE), 2); + BEGIN_NV04(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); + BEGIN_NV04(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); + BEGIN_NV04(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) @@ -100,20 +100,20 @@ nv50_crtc_blank(struct nouveau_crtc *nv_crtc, bool blanked) NV_ERROR(dev, "no space while unblanking crtc\n"); return ret; } - BEGIN_RING(evo, 0, NV50_EVO_CRTC(index, CLUT_MODE), 2); + BEGIN_NV04(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.offset >> 8); if (dev_priv->chipset != 0x50) { - BEGIN_RING(evo, 0, NV84_EVO_CRTC(index, CLUT_DMA), 1); + BEGIN_NV04(evo, 0, NV84_EVO_CRTC(index, CLUT_DMA), 1); OUT_RING(evo, NvEvoVRAM); } - BEGIN_RING(evo, 0, NV50_EVO_CRTC(index, FB_OFFSET), 2); + BEGIN_NV04(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); + BEGIN_NV04(evo, 0, NV50_EVO_CRTC(index, FB_DMA), 1); if (dev_priv->chipset != 0x50) if (nv_crtc->fb.tile_flags == 0x7a00 || nv_crtc->fb.tile_flags == 0xfe00) @@ -158,10 +158,10 @@ nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update) ret = RING_SPACE(evo, 2 + (update ? 2 : 0)); if (ret == 0) { - BEGIN_RING(evo, 0, NV50_EVO_CRTC(head, DITHER_CTRL), 1); + BEGIN_NV04(evo, 0, NV50_EVO_CRTC(head, DITHER_CTRL), 1); OUT_RING (evo, mode); if (update) { - BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1); + BEGIN_NV04(evo, 0, NV50_EVO_UPDATE, 1); OUT_RING (evo, 0); FIRE_RING (evo); } @@ -193,11 +193,11 @@ nv50_crtc_set_color_vibrance(struct nouveau_crtc *nv_crtc, bool update) hue = ((nv_crtc->vibrant_hue * 2047) / 100) & 0xfff; - BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, COLOR_CTRL), 1); + BEGIN_NV04(evo, 0, NV50_EVO_CRTC(nv_crtc->index, COLOR_CTRL), 1); OUT_RING (evo, (hue << 20) | (vib << 8)); if (update) { - BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1); + BEGIN_NV04(evo, 0, NV50_EVO_UPDATE, 1); OUT_RING (evo, 0); FIRE_RING (evo); } @@ -311,9 +311,9 @@ nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update) if (ret) return ret; - BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_CTRL), 1); + BEGIN_NV04(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_CTRL), 1); OUT_RING (evo, ctrl); - BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_RES1), 2); + BEGIN_NV04(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_RES1), 2); OUT_RING (evo, oY << 16 | oX); OUT_RING (evo, oY << 16 | oX); @@ -383,23 +383,15 @@ nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk) 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"); + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - drm_crtc_cleanup(&nv_crtc->base); + NV_DEBUG_KMS(crtc->dev, "\n"); nouveau_bo_unmap(nv_crtc->lut.nvbo); nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo); nouveau_bo_unmap(nv_crtc->cursor.nvbo); nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); + drm_crtc_cleanup(&nv_crtc->base); kfree(nv_crtc); } @@ -593,7 +585,7 @@ nv50_crtc_do_mode_set_base(struct drm_crtc *crtc, if (ret) return ret; - BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_DMA), 1); + BEGIN_NV04(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_DMA), 1); OUT_RING (evo, fb->r_dma); } @@ -601,18 +593,18 @@ nv50_crtc_do_mode_set_base(struct drm_crtc *crtc, if (ret) return ret; - BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_OFFSET), 5); + BEGIN_NV04(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); OUT_RING (evo, fb->r_pitch); OUT_RING (evo, fb->r_format); - BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CLUT_MODE), 1); + BEGIN_NV04(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, FB_POS), 1); + BEGIN_NV04(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) { @@ -672,23 +664,23 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, ret = RING_SPACE(evo, 18); if (ret == 0) { - BEGIN_RING(evo, 0, 0x0804 + head, 2); + BEGIN_NV04(evo, 0, 0x0804 + head, 2); OUT_RING (evo, 0x00800000 | mode->clock); OUT_RING (evo, (ilace == 2) ? 2 : 0); - BEGIN_RING(evo, 0, 0x0810 + head, 6); + BEGIN_NV04(evo, 0, 0x0810 + head, 6); OUT_RING (evo, 0x00000000); /* border colour */ OUT_RING (evo, (vactive << 16) | hactive); OUT_RING (evo, ( vsynce << 16) | hsynce); OUT_RING (evo, (vblanke << 16) | hblanke); OUT_RING (evo, (vblanks << 16) | hblanks); OUT_RING (evo, (vblan2e << 16) | vblan2s); - BEGIN_RING(evo, 0, 0x082c + head, 1); + BEGIN_NV04(evo, 0, 0x082c + head, 1); OUT_RING (evo, 0x00000000); - BEGIN_RING(evo, 0, 0x0900 + head, 1); + BEGIN_NV04(evo, 0, 0x0900 + head, 1); OUT_RING (evo, 0x00000311); /* makes sync channel work */ - BEGIN_RING(evo, 0, 0x08c8 + head, 1); + BEGIN_NV04(evo, 0, 0x08c8 + head, 1); OUT_RING (evo, (umode->vdisplay << 16) | umode->hdisplay); - BEGIN_RING(evo, 0, 0x08d4 + head, 1); + BEGIN_NV04(evo, 0, 0x08d4 + head, 1); OUT_RING (evo, 0x00000000); /* screen position */ } @@ -755,21 +747,25 @@ nv50_crtc_create(struct drm_device *dev, int index) if (!nv_crtc) return -ENOMEM; + nv_crtc->index = index; + nv_crtc->set_dither = nv50_crtc_set_dither; + nv_crtc->set_scale = nv50_crtc_set_scale; + nv_crtc->set_color_vibrance = nv50_crtc_set_color_vibrance; nv_crtc->color_vibrance = 50; nv_crtc->vibrant_hue = 0; - - /* Default CLUT parameters, will be activated on the hw upon - * first mode set. - */ + nv_crtc->lut.depth = 0; 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; + + 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, 4096, 0x100, TTM_PL_FLAG_VRAM, - 0, 0x0000, &nv_crtc->lut.nvbo); + 0, 0x0000, NULL, &nv_crtc->lut.nvbo); if (!ret) { ret = nouveau_bo_pin(nv_crtc->lut.nvbo, TTM_PL_FLAG_VRAM); if (!ret) @@ -778,24 +774,12 @@ nv50_crtc_create(struct drm_device *dev, int index) nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo); } - if (ret) { - kfree(nv_crtc); - return ret; - } - - nv_crtc->index = index; + if (ret) + goto out; - /* set function pointers */ - nv_crtc->set_dither = nv50_crtc_set_dither; - nv_crtc->set_scale = nv50_crtc_set_scale; - nv_crtc->set_color_vibrance = nv50_crtc_set_color_vibrance; - - 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, 64*64*4, 0x100, TTM_PL_FLAG_VRAM, - 0, 0x0000, &nv_crtc->cursor.nvbo); + 0, 0x0000, NULL, &nv_crtc->cursor.nvbo); if (!ret) { ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM); if (!ret) @@ -804,6 +788,12 @@ nv50_crtc_create(struct drm_device *dev, int index) nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); } + if (ret) + goto out; + nv50_cursor_init(nv_crtc); - return 0; +out: + if (ret) + nv50_crtc_destroy(&nv_crtc->base); + return ret; } diff --git a/drivers/gpu/drm/nouveau/nv50_cursor.c b/drivers/gpu/drm/nouveau/nv50_cursor.c index adfc9b607a5..af4ec7bf367 100644 --- a/drivers/gpu/drm/nouveau/nv50_cursor.c +++ b/drivers/gpu/drm/nouveau/nv50_cursor.c @@ -53,15 +53,15 @@ nv50_cursor_show(struct nouveau_crtc *nv_crtc, bool update) } if (dev_priv->chipset != 0x50) { - BEGIN_RING(evo, 0, NV84_EVO_CRTC(nv_crtc->index, CURSOR_DMA), 1); + BEGIN_NV04(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); + BEGIN_NV04(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); + BEGIN_NV04(evo, 0, NV50_EVO_UPDATE, 1); OUT_RING(evo, 0); FIRE_RING(evo); nv_crtc->cursor.visible = true; @@ -86,16 +86,16 @@ nv50_cursor_hide(struct nouveau_crtc *nv_crtc, bool update) NV_ERROR(dev, "no space while hiding cursor\n"); return; } - BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CURSOR_CTRL), 2); + BEGIN_NV04(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); + BEGIN_NV04(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); + BEGIN_NV04(evo, 0, NV50_EVO_UPDATE, 1); OUT_RING(evo, 0); FIRE_RING(evo); nv_crtc->cursor.visible = false; diff --git a/drivers/gpu/drm/nouveau/nv50_dac.c b/drivers/gpu/drm/nouveau/nv50_dac.c index 55c56330be6..eb216a446b8 100644 --- a/drivers/gpu/drm/nouveau/nv50_dac.c +++ b/drivers/gpu/drm/nouveau/nv50_dac.c @@ -55,9 +55,9 @@ nv50_dac_disconnect(struct drm_encoder *encoder) NV_ERROR(dev, "no space while disconnecting DAC\n"); return; } - BEGIN_RING(evo, 0, NV50_EVO_DAC(nv_encoder->or, MODE_CTRL), 1); + BEGIN_NV04(evo, 0, NV50_EVO_DAC(nv_encoder->or, MODE_CTRL), 1); OUT_RING (evo, 0); - BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1); + BEGIN_NV04(evo, 0, NV50_EVO_UPDATE, 1); OUT_RING (evo, 0); nv_encoder->crtc = NULL; @@ -240,7 +240,7 @@ nv50_dac_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, NV_ERROR(dev, "no space while connecting DAC\n"); return; } - BEGIN_RING(evo, 0, NV50_EVO_DAC(nv_encoder->or, MODE_CTRL), 2); + BEGIN_NV04(evo, 0, NV50_EVO_DAC(nv_encoder->or, MODE_CTRL), 2); OUT_RING(evo, mode_ctl); OUT_RING(evo, mode_ctl2); diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 8b78b9cfa38..5c41612723b 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -32,6 +32,7 @@ #include "nouveau_fb.h" #include "nouveau_fbcon.h" #include "nouveau_ramht.h" +#include "nouveau_software.h" #include "drm_crtc_helper.h" static void nv50_display_isr(struct drm_device *); @@ -140,11 +141,11 @@ nv50_display_sync(struct drm_device *dev) ret = RING_SPACE(evo, 6); if (ret == 0) { - BEGIN_RING(evo, 0, 0x0084, 1); + BEGIN_NV04(evo, 0, 0x0084, 1); OUT_RING (evo, 0x80000000); - BEGIN_RING(evo, 0, 0x0080, 1); + BEGIN_NV04(evo, 0, 0x0080, 1); OUT_RING (evo, 0); - BEGIN_RING(evo, 0, 0x0084, 1); + BEGIN_NV04(evo, 0, 0x0084, 1); OUT_RING (evo, 0x00000000); nv_wo32(disp->ntfy, 0x000, 0x00000000); @@ -267,7 +268,7 @@ nv50_display_init(struct drm_device *dev) ret = RING_SPACE(evo, 3); if (ret) return ret; - BEGIN_RING(evo, 0, NV50_EVO_UNK84, 2); + BEGIN_NV04(evo, 0, NV50_EVO_UNK84, 2); OUT_RING (evo, NV50_EVO_UNK84_NOTIFY_DISABLED); OUT_RING (evo, NvEvoSync); @@ -292,7 +293,7 @@ nv50_display_fini(struct drm_device *dev) ret = RING_SPACE(evo, 2); if (ret == 0) { - BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1); + BEGIN_NV04(evo, 0, NV50_EVO_UPDATE, 1); OUT_RING(evo, 0); } FIRE_RING(evo); @@ -358,8 +359,11 @@ nv50_display_create(struct drm_device *dev) dev_priv->engine.display.priv = priv; /* Create CRTC objects */ - for (i = 0; i < 2; i++) - nv50_crtc_create(dev, i); + for (i = 0; i < 2; i++) { + ret = nv50_crtc_create(dev, i); + if (ret) + return ret; + } /* We setup the encoders from the BIOS table */ for (i = 0 ; i < dcb->entries; i++) { @@ -438,13 +442,13 @@ nv50_display_flip_stop(struct drm_crtc *crtc) return; } - BEGIN_RING(evo, 0, 0x0084, 1); + BEGIN_NV04(evo, 0, 0x0084, 1); OUT_RING (evo, 0x00000000); - BEGIN_RING(evo, 0, 0x0094, 1); + BEGIN_NV04(evo, 0, 0x0094, 1); OUT_RING (evo, 0x00000000); - BEGIN_RING(evo, 0, 0x00c0, 1); + BEGIN_NV04(evo, 0, 0x00c0, 1); OUT_RING (evo, 0x00000000); - BEGIN_RING(evo, 0, 0x0080, 1); + BEGIN_NV04(evo, 0, 0x0080, 1); OUT_RING (evo, 0x00000000); FIRE_RING (evo); } @@ -474,28 +478,28 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, } if (dev_priv->chipset < 0xc0) { - BEGIN_RING(chan, 0, 0x0060, 2); + BEGIN_NV04(chan, 0, 0x0060, 2); OUT_RING (chan, NvEvoSema0 + nv_crtc->index); OUT_RING (chan, dispc->sem.offset); - BEGIN_RING(chan, 0, 0x006c, 1); + BEGIN_NV04(chan, 0, 0x006c, 1); OUT_RING (chan, 0xf00d0000 | dispc->sem.value); - BEGIN_RING(chan, 0, 0x0064, 2); + BEGIN_NV04(chan, 0, 0x0064, 2); OUT_RING (chan, dispc->sem.offset ^ 0x10); OUT_RING (chan, 0x74b1e000); - BEGIN_RING(chan, 0, 0x0060, 1); + BEGIN_NV04(chan, 0, 0x0060, 1); if (dev_priv->chipset < 0x84) OUT_RING (chan, NvSema); else OUT_RING (chan, chan->vram_handle); } else { - u64 offset = chan->dispc_vma[nv_crtc->index].offset; + u64 offset = nvc0_software_crtc(chan, nv_crtc->index); offset += dispc->sem.offset; - BEGIN_NVC0(chan, 2, 0, 0x0010, 4); + BEGIN_NVC0(chan, 0, 0x0010, 4); OUT_RING (chan, upper_32_bits(offset)); OUT_RING (chan, lower_32_bits(offset)); OUT_RING (chan, 0xf00d0000 | dispc->sem.value); OUT_RING (chan, 0x1002); - BEGIN_NVC0(chan, 2, 0, 0x0010, 4); + BEGIN_NVC0(chan, 0, 0x0010, 4); OUT_RING (chan, upper_32_bits(offset)); OUT_RING (chan, lower_32_bits(offset ^ 0x10)); OUT_RING (chan, 0x74b1e000); @@ -508,40 +512,40 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, } /* queue the flip on the crtc's "display sync" channel */ - BEGIN_RING(evo, 0, 0x0100, 1); + BEGIN_NV04(evo, 0, 0x0100, 1); OUT_RING (evo, 0xfffe0000); if (chan) { - BEGIN_RING(evo, 0, 0x0084, 1); + BEGIN_NV04(evo, 0, 0x0084, 1); OUT_RING (evo, 0x00000100); } else { - BEGIN_RING(evo, 0, 0x0084, 1); + BEGIN_NV04(evo, 0, 0x0084, 1); OUT_RING (evo, 0x00000010); /* allows gamma somehow, PDISP will bitch at you if * you don't wait for vblank before changing this.. */ - BEGIN_RING(evo, 0, 0x00e0, 1); + BEGIN_NV04(evo, 0, 0x00e0, 1); OUT_RING (evo, 0x40000000); } - BEGIN_RING(evo, 0, 0x0088, 4); + BEGIN_NV04(evo, 0, 0x0088, 4); OUT_RING (evo, dispc->sem.offset); OUT_RING (evo, 0xf00d0000 | dispc->sem.value); OUT_RING (evo, 0x74b1e000); OUT_RING (evo, NvEvoSync); - BEGIN_RING(evo, 0, 0x00a0, 2); + BEGIN_NV04(evo, 0, 0x00a0, 2); OUT_RING (evo, 0x00000000); OUT_RING (evo, 0x00000000); - BEGIN_RING(evo, 0, 0x00c0, 1); + BEGIN_NV04(evo, 0, 0x00c0, 1); OUT_RING (evo, nv_fb->r_dma); - BEGIN_RING(evo, 0, 0x0110, 2); + BEGIN_NV04(evo, 0, 0x0110, 2); OUT_RING (evo, 0x00000000); OUT_RING (evo, 0x00000000); - BEGIN_RING(evo, 0, 0x0800, 5); + BEGIN_NV04(evo, 0, 0x0800, 5); OUT_RING (evo, nv_fb->nvbo->bo.offset >> 8); OUT_RING (evo, 0); OUT_RING (evo, (fb->height << 16) | fb->width); OUT_RING (evo, nv_fb->r_pitch); OUT_RING (evo, nv_fb->r_format); - BEGIN_RING(evo, 0, 0x0080, 1); + BEGIN_NV04(evo, 0, 0x0080, 1); OUT_RING (evo, 0x00000000); FIRE_RING (evo); @@ -642,20 +646,7 @@ nv50_display_script_select(struct drm_device *dev, struct dcb_entry *dcb, 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, *tmp; - - list_for_each_entry_safe(chan, tmp, &dev_priv->vbl_waiting, - nvsw.vbl_wait) { - if (chan->nvsw.vblsem_head != crtc) - continue; - - nouveau_bo_wr32(chan->notifier_bo, chan->nvsw.vblsem_offset, - chan->nvsw.vblsem_rval); - list_del(&chan->nvsw.vbl_wait); - drm_vblank_put(dev, crtc); - } - + nouveau_software_vblank(dev, crtc); drm_handle_vblank(dev, crtc); } diff --git a/drivers/gpu/drm/nouveau/nv50_display.h b/drivers/gpu/drm/nouveau/nv50_display.h index 5d3dd14d283..e9db9b97f04 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.h +++ b/drivers/gpu/drm/nouveau/nv50_display.h @@ -33,6 +33,7 @@ #include "nouveau_dma.h" #include "nouveau_reg.h" #include "nouveau_crtc.h" +#include "nouveau_software.h" #include "nv50_evo.h" struct nv50_display_crtc { diff --git a/drivers/gpu/drm/nouveau/nv50_evo.c b/drivers/gpu/drm/nouveau/nv50_evo.c index 9b962e989d7..ddcd5559582 100644 --- a/drivers/gpu/drm/nouveau/nv50_evo.c +++ b/drivers/gpu/drm/nouveau/nv50_evo.c @@ -117,7 +117,7 @@ nv50_evo_channel_new(struct drm_device *dev, int chid, evo->user_get = 4; evo->user_put = 0; - ret = nouveau_bo_new(dev, 4096, 0, TTM_PL_FLAG_VRAM, 0, 0, + ret = nouveau_bo_new(dev, 4096, 0, TTM_PL_FLAG_VRAM, 0, 0, NULL, &evo->pushbuf_bo); if (ret == 0) ret = nouveau_bo_pin(evo->pushbuf_bo, TTM_PL_FLAG_VRAM); @@ -333,7 +333,7 @@ nv50_evo_create(struct drm_device *dev) goto err; ret = nouveau_bo_new(dev, 4096, 0x1000, TTM_PL_FLAG_VRAM, - 0, 0x0000, &dispc->sem.bo); + 0, 0x0000, NULL, &dispc->sem.bo); if (!ret) { ret = nouveau_bo_pin(dispc->sem.bo, TTM_PL_FLAG_VRAM); if (!ret) diff --git a/drivers/gpu/drm/nouveau/nv50_fb.c b/drivers/gpu/drm/nouveau/nv50_fb.c index bdd2afe2920..f1e4b9e07d1 100644 --- a/drivers/gpu/drm/nouveau/nv50_fb.c +++ b/drivers/gpu/drm/nouveau/nv50_fb.c @@ -2,6 +2,7 @@ #include "drm.h" #include "nouveau_drv.h" #include "nouveau_drm.h" +#include "nouveau_fifo.h" struct nv50_fb_priv { struct page *r100c08_page; @@ -212,6 +213,7 @@ static struct nouveau_enum vm_fault[] = { void nv50_fb_vm_trap(struct drm_device *dev, int display) { + struct nouveau_fifo_priv *pfifo = nv_engine(dev, NVOBJ_ENGINE_FIFO); struct drm_nouveau_private *dev_priv = dev->dev_private; const struct nouveau_enum *en, *cl; unsigned long flags; @@ -236,7 +238,7 @@ nv50_fb_vm_trap(struct drm_device *dev, int display) /* lookup channel id */ chinst = (trap[2] << 16) | trap[1]; spin_lock_irqsave(&dev_priv->channels.lock, flags); - for (ch = 0; ch < dev_priv->engine.fifo.channels; ch++) { + for (ch = 0; ch < pfifo->channels; ch++) { struct nouveau_channel *chan = dev_priv->channels.ptr[ch]; if (!chan || !chan->ramin) diff --git a/drivers/gpu/drm/nouveau/nv50_fbcon.c b/drivers/gpu/drm/nouveau/nv50_fbcon.c index dc75a720652..e3c8b05dcae 100644 --- a/drivers/gpu/drm/nouveau/nv50_fbcon.c +++ b/drivers/gpu/drm/nouveau/nv50_fbcon.c @@ -43,22 +43,22 @@ nv50_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect) return ret; if (rect->rop != ROP_COPY) { - BEGIN_RING(chan, NvSub2D, 0x02ac, 1); + BEGIN_NV04(chan, NvSub2D, 0x02ac, 1); OUT_RING(chan, 1); } - BEGIN_RING(chan, NvSub2D, 0x0588, 1); + BEGIN_NV04(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); + BEGIN_NV04(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); + BEGIN_NV04(chan, NvSub2D, 0x02ac, 1); OUT_RING(chan, 3); } FIRE_RING(chan); @@ -78,14 +78,14 @@ nv50_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region) if (ret) return ret; - BEGIN_RING(chan, NvSub2D, 0x0110, 1); + BEGIN_NV04(chan, NvSub2D, 0x0110, 1); OUT_RING(chan, 0); - BEGIN_RING(chan, NvSub2D, 0x08b0, 4); + BEGIN_NV04(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); + BEGIN_NV04(chan, NvSub2D, 0x08d0, 4); OUT_RING(chan, 0); OUT_RING(chan, region->sx); OUT_RING(chan, 0); @@ -116,7 +116,7 @@ nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image) width = ALIGN(image->width, 32); dwords = (width * image->height) >> 5; - BEGIN_RING(chan, NvSub2D, 0x0814, 2); + BEGIN_NV04(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); @@ -125,10 +125,10 @@ nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image) OUT_RING(chan, image->bg_color); OUT_RING(chan, image->fg_color); } - BEGIN_RING(chan, NvSub2D, 0x0838, 2); + BEGIN_NV04(chan, NvSub2D, 0x0838, 2); OUT_RING(chan, image->width); OUT_RING(chan, image->height); - BEGIN_RING(chan, NvSub2D, 0x0850, 4); + BEGIN_NV04(chan, NvSub2D, 0x0850, 4); OUT_RING(chan, 0); OUT_RING(chan, image->dx); OUT_RING(chan, 0); @@ -143,7 +143,7 @@ nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image) dwords -= push; - BEGIN_RING(chan, NvSub2D, 0x40000860, push); + BEGIN_NI04(chan, NvSub2D, 0x0860, push); OUT_RINGp(chan, data, push); data += push; } @@ -199,60 +199,59 @@ nv50_fbcon_accel_init(struct fb_info *info) return ret; } - BEGIN_RING(chan, NvSub2D, 0x0000, 1); + BEGIN_NV04(chan, NvSub2D, 0x0000, 1); OUT_RING(chan, Nv2D); - BEGIN_RING(chan, NvSub2D, 0x0180, 4); - OUT_RING(chan, NvNotify0); + BEGIN_NV04(chan, NvSub2D, 0x0184, 3); OUT_RING(chan, chan->vram_handle); OUT_RING(chan, chan->vram_handle); OUT_RING(chan, chan->vram_handle); - BEGIN_RING(chan, NvSub2D, 0x0290, 1); + BEGIN_NV04(chan, NvSub2D, 0x0290, 1); OUT_RING(chan, 0); - BEGIN_RING(chan, NvSub2D, 0x0888, 1); + BEGIN_NV04(chan, NvSub2D, 0x0888, 1); OUT_RING(chan, 1); - BEGIN_RING(chan, NvSub2D, 0x02ac, 1); + BEGIN_NV04(chan, NvSub2D, 0x02ac, 1); OUT_RING(chan, 3); - BEGIN_RING(chan, NvSub2D, 0x02a0, 1); + BEGIN_NV04(chan, NvSub2D, 0x02a0, 1); OUT_RING(chan, 0x55); - BEGIN_RING(chan, NvSub2D, 0x08c0, 4); + BEGIN_NV04(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); + BEGIN_NV04(chan, NvSub2D, 0x0580, 2); OUT_RING(chan, 4); OUT_RING(chan, format); - BEGIN_RING(chan, NvSub2D, 0x02e8, 2); + BEGIN_NV04(chan, NvSub2D, 0x02e8, 2); OUT_RING(chan, 2); OUT_RING(chan, 1); - BEGIN_RING(chan, NvSub2D, 0x0804, 1); + BEGIN_NV04(chan, NvSub2D, 0x0804, 1); OUT_RING(chan, format); - BEGIN_RING(chan, NvSub2D, 0x0800, 1); + BEGIN_NV04(chan, NvSub2D, 0x0800, 1); OUT_RING(chan, 1); - BEGIN_RING(chan, NvSub2D, 0x0808, 3); + BEGIN_NV04(chan, NvSub2D, 0x0808, 3); OUT_RING(chan, 0); OUT_RING(chan, 0); OUT_RING(chan, 1); - BEGIN_RING(chan, NvSub2D, 0x081c, 1); + BEGIN_NV04(chan, NvSub2D, 0x081c, 1); OUT_RING(chan, 1); - BEGIN_RING(chan, NvSub2D, 0x0840, 4); + BEGIN_NV04(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); + BEGIN_NV04(chan, NvSub2D, 0x0200, 2); OUT_RING(chan, format); OUT_RING(chan, 1); - BEGIN_RING(chan, NvSub2D, 0x0214, 5); + BEGIN_NV04(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, upper_32_bits(fb->vma.offset)); OUT_RING(chan, lower_32_bits(fb->vma.offset)); - BEGIN_RING(chan, NvSub2D, 0x0230, 2); + BEGIN_NV04(chan, NvSub2D, 0x0230, 2); OUT_RING(chan, format); OUT_RING(chan, 1); - BEGIN_RING(chan, NvSub2D, 0x0244, 5); + BEGIN_NV04(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); diff --git a/drivers/gpu/drm/nouveau/nv50_fifo.c b/drivers/gpu/drm/nouveau/nv50_fifo.c index 3bc2a565c20..55383b85db0 100644 --- a/drivers/gpu/drm/nouveau/nv50_fifo.c +++ b/drivers/gpu/drm/nouveau/nv50_fifo.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Ben Skeggs. + * Copyright (C) 2012 Ben Skeggs. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining @@ -27,480 +27,268 @@ #include "drmP.h" #include "drm.h" #include "nouveau_drv.h" +#include "nouveau_fifo.h" #include "nouveau_ramht.h" #include "nouveau_vm.h" -static void +struct nv50_fifo_priv { + struct nouveau_fifo_priv base; + struct nouveau_gpuobj *playlist[2]; + int cur_playlist; +}; + +struct nv50_fifo_chan { + struct nouveau_fifo_chan base; +}; + +void nv50_fifo_playlist_update(struct drm_device *dev) { + struct nv50_fifo_priv *priv = nv_engine(dev, NVOBJ_ENGINE_FIFO); struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; struct nouveau_gpuobj *cur; - int i, nr; - - NV_DEBUG(dev, "\n"); + int i, p; - cur = pfifo->playlist[pfifo->cur_playlist]; - pfifo->cur_playlist = !pfifo->cur_playlist; + cur = priv->playlist[priv->cur_playlist]; + priv->cur_playlist = !priv->cur_playlist; - /* We never schedule channel 0 or 127 */ - for (i = 1, nr = 0; i < 127; i++) { - if (dev_priv->channels.ptr[i] && - dev_priv->channels.ptr[i]->ramfc) { - nv_wo32(cur, (nr * 4), i); - nr++; - } + for (i = 0, p = 0; i < priv->base.channels; i++) { + if (nv_rd32(dev, 0x002600 + (i * 4)) & 0x80000000) + nv_wo32(cur, p++ * 4, i); } - dev_priv->engine.instmem.flush(dev); - - nv_wr32(dev, 0x32f4, cur->vinst >> 12); - nv_wr32(dev, 0x32ec, nr); - nv_wr32(dev, 0x2500, 0x101); -} -static void -nv50_fifo_channel_enable(struct drm_device *dev, int channel) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_channel *chan = dev_priv->channels.ptr[channel]; - uint32_t inst; - - NV_DEBUG(dev, "ch%d\n", channel); - - if (dev_priv->chipset == 0x50) - inst = chan->ramfc->vinst >> 12; - else - inst = chan->ramfc->vinst >> 8; + dev_priv->engine.instmem.flush(dev); - nv_wr32(dev, NV50_PFIFO_CTX_TABLE(channel), inst | - NV50_PFIFO_CTX_TABLE_CHANNEL_ENABLED); + nv_wr32(dev, 0x0032f4, cur->vinst >> 12); + nv_wr32(dev, 0x0032ec, p); + nv_wr32(dev, 0x002500, 0x00000101); } -static void -nv50_fifo_channel_disable(struct drm_device *dev, int channel) +static int +nv50_fifo_context_new(struct nouveau_channel *chan, int engine) { + struct nv50_fifo_priv *priv = nv_engine(chan->dev, engine); + struct nv50_fifo_chan *fctx; + struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; - uint32_t inst; - - NV_DEBUG(dev, "ch%d\n", channel); + u64 ib_offset = chan->pushbuf_base + chan->dma.ib_base * 4; + u64 instance = chan->ramin->vinst >> 12; + unsigned long flags; + int ret = 0, i; - if (dev_priv->chipset == 0x50) - 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); -} + fctx = chan->engctx[engine] = kzalloc(sizeof(*fctx), GFP_KERNEL); + if (!fctx) + return -ENOMEM; + atomic_inc(&chan->vm->engref[engine]); -static void -nv50_fifo_init_reset(struct drm_device *dev) -{ - uint32_t pmc_e = NV_PMC_ENABLE_PFIFO; + chan->user = ioremap(pci_resource_start(dev->pdev, 0) + + NV50_USER(chan->id), PAGE_SIZE); + if (!chan->user) { + ret = -ENOMEM; + goto error; + } - NV_DEBUG(dev, "\n"); + for (i = 0; i < 0x100; i += 4) + nv_wo32(chan->ramin, i, 0x00000000); + nv_wo32(chan->ramin, 0x3c, 0x403f6078); + nv_wo32(chan->ramin, 0x40, 0x00000000); + nv_wo32(chan->ramin, 0x44, 0x01003fff); + nv_wo32(chan->ramin, 0x48, chan->pushbuf->cinst >> 4); + nv_wo32(chan->ramin, 0x50, lower_32_bits(ib_offset)); + nv_wo32(chan->ramin, 0x54, upper_32_bits(ib_offset) | + drm_order(chan->dma.ib_max + 1) << 16); + nv_wo32(chan->ramin, 0x60, 0x7fffffff); + nv_wo32(chan->ramin, 0x78, 0x00000000); + nv_wo32(chan->ramin, 0x7c, 0x30000001); + nv_wo32(chan->ramin, 0x80, ((chan->ramht->bits - 9) << 27) | + (4 << 24) /* SEARCH_FULL */ | + (chan->ramht->gpuobj->cinst >> 4)); - 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); -} + dev_priv->engine.instmem.flush(dev); -static void -nv50_fifo_init_intr(struct drm_device *dev) -{ - NV_DEBUG(dev, "\n"); + spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + nv_wr32(dev, 0x002600 + (chan->id * 4), 0x80000000 | instance); + nv50_fifo_playlist_update(dev); + spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); - nouveau_irq_register(dev, 8, nv04_fifo_isr); - nv_wr32(dev, NV03_PFIFO_INTR_0, 0xFFFFFFFF); - nv_wr32(dev, NV03_PFIFO_INTR_EN_0, 0xFFFFFFFF); +error: + if (ret) + priv->base.base.context_del(chan, engine); + return ret; } -static void -nv50_fifo_init_context_table(struct drm_device *dev) +static bool +nv50_fifo_kickoff(struct nouveau_channel *chan) { - 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->channels.ptr[i]) - nv50_fifo_channel_enable(dev, i); - else - nv50_fifo_channel_disable(dev, i); + struct drm_device *dev = chan->dev; + bool done = true; + u32 me; + + /* HW bug workaround: + * + * PFIFO will hang forever if the connected engines don't report + * that they've processed the context switch request. + * + * In order for the kickoff to work, we need to ensure all the + * connected engines are in a state where they can answer. + * + * Newer chipsets don't seem to suffer from this issue, and well, + * there's also a "ignore these engines" bitmask reg we can use + * if we hit the issue there.. + */ + + /* PME: make sure engine is enabled */ + me = nv_mask(dev, 0x00b860, 0x00000001, 0x00000001); + + /* do the kickoff... */ + nv_wr32(dev, 0x0032fc, chan->ramin->vinst >> 12); + if (!nv_wait_ne(dev, 0x0032fc, 0xffffffff, 0xffffffff)) { + NV_INFO(dev, "PFIFO: channel %d unload timeout\n", chan->id); + done = false; } - nv50_fifo_playlist_update(dev); + /* restore any engine states we changed, and exit */ + nv_wr32(dev, 0x00b860, me); + return done; } 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); - nv_wr32(dev, 0x2044, 0x01003fff); - - /* Enable dummy channels setup by nv50_instmem.c */ - nv50_fifo_channel_enable(dev, 0); - nv50_fifo_channel_enable(dev, 127); -} - -int -nv50_fifo_init(struct drm_device *dev) +nv50_fifo_context_del(struct nouveau_channel *chan, int engine) { + struct nv50_fifo_chan *fctx = chan->engctx[engine]; + struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; - int ret; + unsigned long flags; - NV_DEBUG(dev, "\n"); + /* remove channel from playlist, will context switch if active */ + spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + nv_mask(dev, 0x002600 + (chan->id * 4), 0x80000000, 0x00000000); + nv50_fifo_playlist_update(dev); - if (pfifo->playlist[0]) { - pfifo->cur_playlist = !pfifo->cur_playlist; - goto just_reset; - } + /* tell any engines on this channel to unload their contexts */ + nv50_fifo_kickoff(chan); - ret = nouveau_gpuobj_new(dev, NULL, 128*4, 0x1000, - NVOBJ_FLAG_ZERO_ALLOC, - &pfifo->playlist[0]); - if (ret) { - NV_ERROR(dev, "error creating playlist 0: %d\n", ret); - return ret; - } + nv_wr32(dev, 0x002600 + (chan->id * 4), 0x00000000); + spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); - ret = nouveau_gpuobj_new(dev, NULL, 128*4, 0x1000, - NVOBJ_FLAG_ZERO_ALLOC, - &pfifo->playlist[1]); - if (ret) { - nouveau_gpuobj_ref(NULL, &pfifo->playlist[0]); - NV_ERROR(dev, "error creating playlist 1: %d\n", ret); - return ret; + /* clean up */ + if (chan->user) { + iounmap(chan->user); + chan->user = NULL; } -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; + atomic_dec(&chan->vm->engref[engine]); + chan->engctx[engine] = NULL; + kfree(fctx); } -void -nv50_fifo_takedown(struct drm_device *dev) +static int +nv50_fifo_init(struct drm_device *dev, int engine) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; + u32 instance; + int i; - NV_DEBUG(dev, "\n"); + nv_mask(dev, 0x000200, 0x00000100, 0x00000000); + nv_mask(dev, 0x000200, 0x00000100, 0x00000100); + nv_wr32(dev, 0x00250c, 0x6f3cfc34); + nv_wr32(dev, 0x002044, 0x01003fff); - if (!pfifo->playlist[0]) - return; + nv_wr32(dev, 0x002100, 0xffffffff); + nv_wr32(dev, 0x002140, 0xffffffff); - nv_wr32(dev, 0x2140, 0x00000000); - nouveau_irq_unregister(dev, 8); + for (i = 0; i < 128; i++) { + struct nouveau_channel *chan = dev_priv->channels.ptr[i]; + if (chan && chan->engctx[engine]) + instance = 0x80000000 | chan->ramin->vinst >> 12; + else + instance = 0x00000000; + nv_wr32(dev, 0x002600 + (i * 4), instance); + } - nouveau_gpuobj_ref(NULL, &pfifo->playlist[0]); - nouveau_gpuobj_ref(NULL, &pfifo->playlist[1]); -} + nv50_fifo_playlist_update(dev); -int -nv50_fifo_channel_id(struct drm_device *dev) -{ - return nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH1) & - NV50_PFIFO_CACHE1_PUSH1_CHID_MASK; + nv_wr32(dev, 0x003200, 1); + nv_wr32(dev, 0x003250, 1); + nv_wr32(dev, 0x002500, 1); + return 0; } -int -nv50_fifo_create_context(struct nouveau_channel *chan) +static int +nv50_fifo_fini(struct drm_device *dev, int engine, bool suspend) { - struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpuobj *ramfc = NULL; - uint64_t ib_offset = chan->pushbuf_base + chan->dma.ib_base * 4; - unsigned long flags; - int ret; - - NV_DEBUG(dev, "ch%d\n", chan->id); - - if (dev_priv->chipset == 0x50) { - ret = nouveau_gpuobj_new_fake(dev, chan->ramin->pinst, - chan->ramin->vinst, 0x100, - NVOBJ_FLAG_ZERO_ALLOC | - NVOBJ_FLAG_ZERO_FREE, - &chan->ramfc); - if (ret) - return ret; - - ret = nouveau_gpuobj_new_fake(dev, chan->ramin->pinst + 0x0400, - chan->ramin->vinst + 0x0400, - 4096, 0, &chan->cache); - if (ret) - return ret; - } else { - ret = nouveau_gpuobj_new(dev, chan, 0x100, 256, - NVOBJ_FLAG_ZERO_ALLOC | - NVOBJ_FLAG_ZERO_FREE, &chan->ramfc); - if (ret) - return ret; - - ret = nouveau_gpuobj_new(dev, chan, 4096, 1024, - 0, &chan->cache); - if (ret) - return ret; - } - ramfc = chan->ramfc; + struct nv50_fifo_priv *priv = nv_engine(dev, engine); + int i; - chan->user = ioremap(pci_resource_start(dev->pdev, 0) + - NV50_USER(chan->id), PAGE_SIZE); - if (!chan->user) - return -ENOMEM; + /* set playlist length to zero, fifo will unload context */ + nv_wr32(dev, 0x0032ec, 0); - spin_lock_irqsave(&dev_priv->context_switch_lock, flags); - - nv_wo32(ramfc, 0x48, chan->pushbuf->cinst >> 4); - nv_wo32(ramfc, 0x80, ((chan->ramht->bits - 9) << 27) | - (4 << 24) /* SEARCH_FULL */ | - (chan->ramht->gpuobj->cinst >> 4)); - nv_wo32(ramfc, 0x44, 0x01003fff); - nv_wo32(ramfc, 0x60, 0x7fffffff); - nv_wo32(ramfc, 0x40, 0x00000000); - nv_wo32(ramfc, 0x7c, 0x30000001); - nv_wo32(ramfc, 0x78, 0x00000000); - nv_wo32(ramfc, 0x3c, 0x403f6078); - nv_wo32(ramfc, 0x50, lower_32_bits(ib_offset)); - nv_wo32(ramfc, 0x54, upper_32_bits(ib_offset) | - drm_order(chan->dma.ib_max + 1) << 16); - - if (dev_priv->chipset != 0x50) { - nv_wo32(chan->ramin, 0, chan->id); - nv_wo32(chan->ramin, 4, chan->ramfc->vinst >> 8); - - nv_wo32(ramfc, 0x88, chan->cache->vinst >> 10); - nv_wo32(ramfc, 0x98, chan->ramin->vinst >> 12); + /* tell all connected engines to unload their contexts */ + for (i = 0; i < priv->base.channels; i++) { + struct nouveau_channel *chan = dev_priv->channels.ptr[i]; + if (chan && !nv50_fifo_kickoff(chan)) + return -EBUSY; } - dev_priv->engine.instmem.flush(dev); - - nv50_fifo_channel_enable(dev, chan->id); - nv50_fifo_playlist_update(dev); - spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); + nv_wr32(dev, 0x002140, 0); return 0; } void -nv50_fifo_destroy_context(struct nouveau_channel *chan) +nv50_fifo_tlb_flush(struct drm_device *dev, int engine) { - struct drm_device *dev = chan->dev; - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; - struct nouveau_gpuobj *ramfc = NULL; - unsigned long flags; - - NV_DEBUG(dev, "ch%d\n", chan->id); - - spin_lock_irqsave(&dev_priv->context_switch_lock, flags); - pfifo->reassign(dev, false); - - /* Unload the context if it's the currently active one */ - if (pfifo->channel_id(dev) == chan->id) { - pfifo->disable(dev); - pfifo->unload_context(dev); - pfifo->enable(dev); - } - - /* This will ensure the channel is seen as disabled. */ - nouveau_gpuobj_ref(chan->ramfc, &ramfc); - nouveau_gpuobj_ref(NULL, &chan->ramfc); - nv50_fifo_channel_disable(dev, chan->id); - - /* Dummy channel, also used on ch 127 */ - if (chan->id == 0) - nv50_fifo_channel_disable(dev, 127); - nv50_fifo_playlist_update(dev); - - pfifo->reassign(dev, true); - spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); - - /* Free the channel resources */ - if (chan->user) { - iounmap(chan->user); - chan->user = NULL; - } - nouveau_gpuobj_ref(NULL, &ramfc); - nouveau_gpuobj_ref(NULL, &chan->cache); + nv50_vm_flush_engine(dev, 5); } -int -nv50_fifo_load_context(struct nouveau_channel *chan) +void +nv50_fifo_destroy(struct drm_device *dev, int engine) { - struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpuobj *ramfc = chan->ramfc; - struct nouveau_gpuobj *cache = chan->cache; - int ptr, cnt; - - NV_DEBUG(dev, "ch%d\n", chan->id); - - nv_wr32(dev, 0x3330, nv_ro32(ramfc, 0x00)); - nv_wr32(dev, 0x3334, nv_ro32(ramfc, 0x04)); - nv_wr32(dev, 0x3240, nv_ro32(ramfc, 0x08)); - nv_wr32(dev, 0x3320, nv_ro32(ramfc, 0x0c)); - nv_wr32(dev, 0x3244, nv_ro32(ramfc, 0x10)); - nv_wr32(dev, 0x3328, nv_ro32(ramfc, 0x14)); - nv_wr32(dev, 0x3368, nv_ro32(ramfc, 0x18)); - nv_wr32(dev, 0x336c, nv_ro32(ramfc, 0x1c)); - nv_wr32(dev, 0x3370, nv_ro32(ramfc, 0x20)); - nv_wr32(dev, 0x3374, nv_ro32(ramfc, 0x24)); - nv_wr32(dev, 0x3378, nv_ro32(ramfc, 0x28)); - nv_wr32(dev, 0x337c, nv_ro32(ramfc, 0x2c)); - nv_wr32(dev, 0x3228, nv_ro32(ramfc, 0x30)); - nv_wr32(dev, 0x3364, nv_ro32(ramfc, 0x34)); - nv_wr32(dev, 0x32a0, nv_ro32(ramfc, 0x38)); - nv_wr32(dev, 0x3224, nv_ro32(ramfc, 0x3c)); - nv_wr32(dev, 0x324c, nv_ro32(ramfc, 0x40)); - nv_wr32(dev, 0x2044, nv_ro32(ramfc, 0x44)); - nv_wr32(dev, 0x322c, nv_ro32(ramfc, 0x48)); - nv_wr32(dev, 0x3234, nv_ro32(ramfc, 0x4c)); - nv_wr32(dev, 0x3340, nv_ro32(ramfc, 0x50)); - nv_wr32(dev, 0x3344, nv_ro32(ramfc, 0x54)); - nv_wr32(dev, 0x3280, nv_ro32(ramfc, 0x58)); - nv_wr32(dev, 0x3254, nv_ro32(ramfc, 0x5c)); - nv_wr32(dev, 0x3260, nv_ro32(ramfc, 0x60)); - nv_wr32(dev, 0x3264, nv_ro32(ramfc, 0x64)); - nv_wr32(dev, 0x3268, nv_ro32(ramfc, 0x68)); - nv_wr32(dev, 0x326c, nv_ro32(ramfc, 0x6c)); - nv_wr32(dev, 0x32e4, nv_ro32(ramfc, 0x70)); - nv_wr32(dev, 0x3248, nv_ro32(ramfc, 0x74)); - nv_wr32(dev, 0x2088, nv_ro32(ramfc, 0x78)); - nv_wr32(dev, 0x2058, nv_ro32(ramfc, 0x7c)); - nv_wr32(dev, 0x2210, nv_ro32(ramfc, 0x80)); - - cnt = nv_ro32(ramfc, 0x84); - for (ptr = 0; ptr < cnt; ptr++) { - nv_wr32(dev, NV40_PFIFO_CACHE1_METHOD(ptr), - nv_ro32(cache, (ptr * 8) + 0)); - nv_wr32(dev, NV40_PFIFO_CACHE1_DATA(ptr), - nv_ro32(cache, (ptr * 8) + 4)); - } - 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 (dev_priv->chipset != 0x50) { - nv_wr32(dev, 0x340c, nv_ro32(ramfc, 0x88)); - nv_wr32(dev, 0x3400, nv_ro32(ramfc, 0x8c)); - nv_wr32(dev, 0x3404, nv_ro32(ramfc, 0x90)); - nv_wr32(dev, 0x3408, nv_ro32(ramfc, 0x94)); - nv_wr32(dev, 0x3410, nv_ro32(ramfc, 0x98)); - } + struct nv50_fifo_priv *priv = nv_engine(dev, engine); - nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, chan->id | (1<<16)); - return 0; + nouveau_irq_unregister(dev, 8); + + nouveau_gpuobj_ref(NULL, &priv->playlist[0]); + nouveau_gpuobj_ref(NULL, &priv->playlist[1]); + + dev_priv->eng[engine] = NULL; + kfree(priv); } int -nv50_fifo_unload_context(struct drm_device *dev) +nv50_fifo_create(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->channels.ptr[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; - cache = chan->cache; - - nv_wo32(ramfc, 0x00, nv_rd32(dev, 0x3330)); - nv_wo32(ramfc, 0x04, nv_rd32(dev, 0x3334)); - nv_wo32(ramfc, 0x08, nv_rd32(dev, 0x3240)); - nv_wo32(ramfc, 0x0c, nv_rd32(dev, 0x3320)); - nv_wo32(ramfc, 0x10, nv_rd32(dev, 0x3244)); - nv_wo32(ramfc, 0x14, nv_rd32(dev, 0x3328)); - nv_wo32(ramfc, 0x18, nv_rd32(dev, 0x3368)); - nv_wo32(ramfc, 0x1c, nv_rd32(dev, 0x336c)); - nv_wo32(ramfc, 0x20, nv_rd32(dev, 0x3370)); - nv_wo32(ramfc, 0x24, nv_rd32(dev, 0x3374)); - nv_wo32(ramfc, 0x28, nv_rd32(dev, 0x3378)); - nv_wo32(ramfc, 0x2c, nv_rd32(dev, 0x337c)); - nv_wo32(ramfc, 0x30, nv_rd32(dev, 0x3228)); - nv_wo32(ramfc, 0x34, nv_rd32(dev, 0x3364)); - nv_wo32(ramfc, 0x38, nv_rd32(dev, 0x32a0)); - nv_wo32(ramfc, 0x3c, nv_rd32(dev, 0x3224)); - nv_wo32(ramfc, 0x40, nv_rd32(dev, 0x324c)); - nv_wo32(ramfc, 0x44, nv_rd32(dev, 0x2044)); - nv_wo32(ramfc, 0x48, nv_rd32(dev, 0x322c)); - nv_wo32(ramfc, 0x4c, nv_rd32(dev, 0x3234)); - nv_wo32(ramfc, 0x50, nv_rd32(dev, 0x3340)); - nv_wo32(ramfc, 0x54, nv_rd32(dev, 0x3344)); - nv_wo32(ramfc, 0x58, nv_rd32(dev, 0x3280)); - nv_wo32(ramfc, 0x5c, nv_rd32(dev, 0x3254)); - nv_wo32(ramfc, 0x60, nv_rd32(dev, 0x3260)); - nv_wo32(ramfc, 0x64, nv_rd32(dev, 0x3264)); - nv_wo32(ramfc, 0x68, nv_rd32(dev, 0x3268)); - nv_wo32(ramfc, 0x6c, nv_rd32(dev, 0x326c)); - nv_wo32(ramfc, 0x70, nv_rd32(dev, 0x32e4)); - nv_wo32(ramfc, 0x74, nv_rd32(dev, 0x3248)); - nv_wo32(ramfc, 0x78, nv_rd32(dev, 0x2088)); - nv_wo32(ramfc, 0x7c, nv_rd32(dev, 0x2058)); - nv_wo32(ramfc, 0x80, 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(cache, ptr + 0, - nv_rd32(dev, NV40_PFIFO_CACHE1_METHOD(get))); - nv_wo32(cache, ptr + 4, - nv_rd32(dev, NV40_PFIFO_CACHE1_DATA(get))); - get = (get + 1) & 0x1ff; - ptr += 8; - } - - /* guessing that all the 0x34xx regs aren't on NV50 */ - if (dev_priv->chipset != 0x50) { - nv_wo32(ramfc, 0x84, ptr >> 3); - nv_wo32(ramfc, 0x88, nv_rd32(dev, 0x340c)); - nv_wo32(ramfc, 0x8c, nv_rd32(dev, 0x3400)); - nv_wo32(ramfc, 0x90, nv_rd32(dev, 0x3404)); - nv_wo32(ramfc, 0x94, nv_rd32(dev, 0x3408)); - nv_wo32(ramfc, 0x98, nv_rd32(dev, 0x3410)); - } + struct nv50_fifo_priv *priv; + int ret; - dev_priv->engine.instmem.flush(dev); + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; - /*XXX: probably reload ch127 (NULL) state back too */ - nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, 127); - return 0; -} + priv->base.base.destroy = nv50_fifo_destroy; + priv->base.base.init = nv50_fifo_init; + priv->base.base.fini = nv50_fifo_fini; + priv->base.base.context_new = nv50_fifo_context_new; + priv->base.base.context_del = nv50_fifo_context_del; + priv->base.base.tlb_flush = nv50_fifo_tlb_flush; + priv->base.channels = 127; + dev_priv->eng[NVOBJ_ENGINE_FIFO] = &priv->base.base; + + ret = nouveau_gpuobj_new(dev, NULL, priv->base.channels * 4, 0x1000, + NVOBJ_FLAG_ZERO_ALLOC, &priv->playlist[0]); + if (ret) + goto error; + + ret = nouveau_gpuobj_new(dev, NULL, priv->base.channels * 4, 0x1000, + NVOBJ_FLAG_ZERO_ALLOC, &priv->playlist[1]); + if (ret) + goto error; -void -nv50_fifo_tlb_flush(struct drm_device *dev) -{ - nv50_vm_flush_engine(dev, 5); + nouveau_irq_register(dev, 8, nv04_fifo_isr); +error: + if (ret) + priv->base.base.destroy(dev, NVOBJ_ENGINE_FIFO); + return ret; } diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c index 33d5711a918..d9cc2f2638d 100644 --- a/drivers/gpu/drm/nouveau/nv50_graph.c +++ b/drivers/gpu/drm/nouveau/nv50_graph.c @@ -27,8 +27,8 @@ #include "drmP.h" #include "drm.h" #include "nouveau_drv.h" +#include "nouveau_fifo.h" #include "nouveau_ramht.h" -#include "nouveau_grctx.h" #include "nouveau_dma.h" #include "nouveau_vm.h" #include "nv50_evo.h" @@ -40,86 +40,6 @@ struct nv50_graph_engine { u32 grctx_size; }; -static 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); -} - -static 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(dev, 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->channels.ptr[i]; - - if (chan && chan->ramin && chan->ramin->vinst == inst) - return chan; - } - - return NULL; -} - -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; -} - -static 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; -} - static int nv50_graph_init(struct drm_device *dev, int engine) { @@ -211,12 +131,6 @@ nv50_graph_init(struct drm_device *dev, int engine) static int nv50_graph_fini(struct drm_device *dev, int engine, bool suspend) { - nv_mask(dev, 0x400500, 0x00010001, 0x00000000); - if (!nv_wait(dev, 0x400700, ~0, 0) && suspend) { - nv_mask(dev, 0x400500, 0x00010001, 0x00010001); - return -EBUSY; - } - nv50_graph_unload_context(dev); nv_wr32(dev, 0x40013c, 0x00000000); return 0; } @@ -229,7 +143,6 @@ nv50_graph_context_new(struct nouveau_channel *chan, int engine) struct nouveau_gpuobj *ramin = chan->ramin; struct nouveau_gpuobj *grctx = NULL; struct nv50_graph_engine *pgraph = nv_engine(dev, engine); - struct nouveau_grctx ctx = {}; int hdr, ret; NV_DEBUG(dev, "ch%d\n", chan->id); @@ -248,11 +161,7 @@ nv50_graph_context_new(struct nouveau_channel *chan, int engine) nv_wo32(ramin, hdr + 0x10, 0); nv_wo32(ramin, hdr + 0x14, 0x00010000); - ctx.dev = chan->dev; - ctx.mode = NOUVEAU_GRCTX_VALS; - ctx.data = grctx; - nv50_grctx_init(&ctx); - + nv50_grctx_fill(dev, grctx); nv_wo32(grctx, 0x00000, chan->ramin->vinst >> 12); dev_priv->engine.instmem.flush(dev); @@ -268,33 +177,14 @@ nv50_graph_context_del(struct nouveau_channel *chan, int engine) struct nouveau_gpuobj *grctx = chan->engctx[engine]; struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; int i, hdr = (dev_priv->chipset == 0x50) ? 0x200 : 0x20; - unsigned long flags; - - NV_DEBUG(dev, "ch%d\n", chan->id); - - if (!chan->ramin) - return; - - spin_lock_irqsave(&dev_priv->context_switch_lock, flags); - pfifo->reassign(dev, false); - nv50_graph_fifo_access(dev, false); - - if (nv50_graph_channel(dev) == chan) - nv50_graph_unload_context(dev); for (i = hdr; i < hdr + 24; i += 4) nv_wo32(chan->ramin, i, 0); dev_priv->engine.instmem.flush(dev); - nv50_graph_fifo_access(dev, true); - pfifo->reassign(dev, true); - spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); - - nouveau_gpuobj_ref(NULL, &grctx); - atomic_dec(&chan->vm->engref[engine]); + nouveau_gpuobj_ref(NULL, &grctx); chan->engctx[engine] = NULL; } @@ -325,85 +215,6 @@ nv50_graph_object_new(struct nouveau_channel *chan, int engine, } static 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, - u32 class, u32 mthd, u32 data) -{ - struct nouveau_gpuobj *gpuobj; - - gpuobj = nouveau_ramht_find(chan, data); - if (!gpuobj) - return -ENOENT; - - if (nouveau_notifier_offset(gpuobj, NULL)) - return -EINVAL; - - chan->nvsw.vblsem = gpuobj; - chan->nvsw.vblsem_offset = ~0; - return 0; -} - -static int -nv50_graph_nvsw_vblsem_offset(struct nouveau_channel *chan, - u32 class, u32 mthd, u32 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, - u32 class, u32 mthd, u32 data) -{ - chan->nvsw.vblsem_rval = data; - return 0; -} - -static int -nv50_graph_nvsw_vblsem_release(struct nouveau_channel *chan, - u32 class, u32 mthd, u32 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; - - drm_vblank_get(dev, data); - - chan->nvsw.vblsem_head = data; - list_add(&chan->nvsw.vbl_wait, &dev_priv->vbl_waiting); - - return 0; -} - -static int -nv50_graph_nvsw_mthd_page_flip(struct nouveau_channel *chan, - u32 class, u32 mthd, u32 data) -{ - nouveau_finish_page_flip(chan, NULL); - return 0; -} - - -static void nv50_graph_tlb_flush(struct drm_device *dev, int engine) { nv50_vm_flush_engine(dev, 0); @@ -514,6 +325,7 @@ struct nouveau_enum nv50_data_error_names[] = { { 0x0000001f, "RT_BPP128_WITH_MS8", NULL }, { 0x00000021, "Z_OUT_OF_BOUNDS", NULL }, { 0x00000023, "XY_OUT_OF_BOUNDS", NULL }, + { 0x00000024, "VP_ZERO_INPUTS", NULL }, { 0x00000027, "CP_MORE_PARAMS_THAN_SHARED", NULL }, { 0x00000028, "CP_NO_REG_SPACE_STRIPED", NULL }, { 0x00000029, "CP_NO_REG_SPACE_PACKED", NULL }, @@ -900,13 +712,14 @@ nv50_pgraph_trap_handler(struct drm_device *dev, u32 display, u64 inst, u32 chid int nv50_graph_isr_chid(struct drm_device *dev, u64 inst) { + struct nouveau_fifo_priv *pfifo = nv_engine(dev, NVOBJ_ENGINE_FIFO); struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_channel *chan; unsigned long flags; int i; spin_lock_irqsave(&dev_priv->channels.lock, flags); - for (i = 0; i < dev_priv->engine.fifo.channels; i++) { + for (i = 0; i < pfifo->channels; i++) { chan = dev_priv->channels.ptr[i]; if (!chan || !chan->ramin) continue; @@ -939,15 +752,6 @@ nv50_graph_isr(struct drm_device *dev) show &= ~0x00000010; } - if (stat & 0x00001000) { - nv_wr32(dev, 0x400500, 0x00000000); - nv_wr32(dev, 0x400100, 0x00001000); - nv_mask(dev, 0x40013c, 0x00001000, 0x00000000); - nv50_graph_context_switch(dev); - stat &= ~0x00001000; - show &= ~0x00001000; - } - show = (show && nouveau_ratelimit()) ? show : 0; if (show & 0x00100000) { @@ -996,28 +800,21 @@ nv50_graph_create(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nv50_graph_engine *pgraph; - struct nouveau_grctx ctx = {}; int ret; pgraph = kzalloc(sizeof(*pgraph),GFP_KERNEL); if (!pgraph) return -ENOMEM; - ctx.dev = dev; - ctx.mode = NOUVEAU_GRCTX_PROG; - ctx.data = pgraph->ctxprog; - ctx.ctxprog_max = ARRAY_SIZE(pgraph->ctxprog); - - ret = nv50_grctx_init(&ctx); + ret = nv50_grctx_init(dev, pgraph->ctxprog, ARRAY_SIZE(pgraph->ctxprog), + &pgraph->ctxprog_size, + &pgraph->grctx_size); if (ret) { NV_ERROR(dev, "PGRAPH: ctxprog build failed\n"); kfree(pgraph); return 0; } - pgraph->grctx_size = ctx.ctxvals_pos * 4; - pgraph->ctxprog_size = ctx.ctxprog_len; - pgraph->base.destroy = nv50_graph_destroy; pgraph->base.init = nv50_graph_init; pgraph->base.fini = nv50_graph_fini; @@ -1031,14 +828,6 @@ nv50_graph_create(struct drm_device *dev) nouveau_irq_register(dev, 12, nv50_graph_isr); - /* NVSW really doesn't live here... */ - NVOBJ_CLASS(dev, 0x506e, SW); /* nvsw */ - NVOBJ_MTHD (dev, 0x506e, 0x018c, nv50_graph_nvsw_dma_vblsem); - NVOBJ_MTHD (dev, 0x506e, 0x0400, nv50_graph_nvsw_vblsem_offset); - NVOBJ_MTHD (dev, 0x506e, 0x0404, nv50_graph_nvsw_vblsem_release_val); - NVOBJ_MTHD (dev, 0x506e, 0x0408, nv50_graph_nvsw_vblsem_release); - NVOBJ_MTHD (dev, 0x506e, 0x0500, nv50_graph_nvsw_mthd_page_flip); - NVOBJ_ENGINE_ADD(dev, GR, &pgraph->base); NVOBJ_CLASS(dev, 0x0030, GR); /* null */ NVOBJ_CLASS(dev, 0x5039, GR); /* m2mf */ diff --git a/drivers/gpu/drm/nouveau/nv50_grctx.c b/drivers/gpu/drm/nouveau/nv50_grctx.c index 4b46d696856..881e22b249f 100644 --- a/drivers/gpu/drm/nouveau/nv50_grctx.c +++ b/drivers/gpu/drm/nouveau/nv50_grctx.c @@ -172,8 +172,8 @@ static void nv50_graph_construct_xfer2(struct nouveau_grctx *ctx); /* Main function: construct the ctxprog skeleton, call the other functions. */ -int -nv50_grctx_init(struct nouveau_grctx *ctx) +static int +nv50_grctx_generate(struct nouveau_grctx *ctx) { struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; @@ -210,7 +210,7 @@ nv50_grctx_init(struct nouveau_grctx *ctx) 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); + cp_bra (ctx, ALWAYS, TRUE, cp_prepare_exit); /* setup for context load */ cp_name(ctx, cp_setup_auto_load); @@ -277,6 +277,33 @@ nv50_grctx_init(struct nouveau_grctx *ctx) return 0; } +void +nv50_grctx_fill(struct drm_device *dev, struct nouveau_gpuobj *mem) +{ + nv50_grctx_generate(&(struct nouveau_grctx) { + .dev = dev, + .mode = NOUVEAU_GRCTX_VALS, + .data = mem, + }); +} + +int +nv50_grctx_init(struct drm_device *dev, u32 *data, u32 max, u32 *len, u32 *cnt) +{ + struct nouveau_grctx ctx = { + .dev = dev, + .mode = NOUVEAU_GRCTX_PROG, + .data = data, + .ctxprog_max = max + }; + int ret; + + ret = nv50_grctx_generate(&ctx); + *cnt = ctx.ctxvals_pos * 4; + *len = ctx.ctxprog_len; + return ret; +} + /* * Constructs MMIO part of ctxprog and ctxvals. Just a matter of knowing which * registers to save/restore and the default values for them. diff --git a/drivers/gpu/drm/nouveau/nv50_instmem.c b/drivers/gpu/drm/nouveau/nv50_instmem.c index a7c12c94a5a..0bba54f1180 100644 --- a/drivers/gpu/drm/nouveau/nv50_instmem.c +++ b/drivers/gpu/drm/nouveau/nv50_instmem.c @@ -83,7 +83,7 @@ nv50_channel_new(struct drm_device *dev, u32 size, struct nouveau_vm *vm, return ret; } - ret = drm_mm_init(&chan->ramin_heap, 0x6000, chan->ramin->size); + ret = drm_mm_init(&chan->ramin_heap, 0x6000, chan->ramin->size - 0x6000); if (ret) { nv50_channel_del(&chan); return ret; diff --git a/drivers/gpu/drm/nouveau/nv50_mpeg.c b/drivers/gpu/drm/nouveau/nv50_mpeg.c index b57a2d180ad..90e8ed22cfc 100644 --- a/drivers/gpu/drm/nouveau/nv50_mpeg.c +++ b/drivers/gpu/drm/nouveau/nv50_mpeg.c @@ -77,27 +77,13 @@ nv50_mpeg_context_new(struct nouveau_channel *chan, int engine) static void nv50_mpeg_context_del(struct nouveau_channel *chan, int engine) { - struct drm_nouveau_private *dev_priv = chan->dev->dev_private; struct nouveau_gpuobj *ctx = chan->engctx[engine]; struct drm_device *dev = chan->dev; - unsigned long flags; - u32 inst, i; - - if (!chan->ramin) - return; - - inst = chan->ramin->vinst >> 12; - inst |= 0x80000000; - - spin_lock_irqsave(&dev_priv->context_switch_lock, flags); - nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000); - if (nv_rd32(dev, 0x00b318) == inst) - nv_mask(dev, 0x00b318, 0x80000000, 0x00000000); - nv_mask(dev, 0x00b32c, 0x00000001, 0x00000001); - spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); + int i; for (i = 0x00; i <= 0x14; i += 4) nv_wo32(chan->ramin, CTX_PTR(dev, i), 0x00000000); + nouveau_gpuobj_ref(NULL, &ctx); chan->engctx[engine] = NULL; } @@ -162,7 +148,6 @@ nv50_mpeg_init(struct drm_device *dev, int engine) static int nv50_mpeg_fini(struct drm_device *dev, int engine, bool suspend) { - /*XXX: context save for s/r */ nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000); nv_wr32(dev, 0x00b140, 0x00000000); return 0; diff --git a/drivers/gpu/drm/nouveau/nv50_software.c b/drivers/gpu/drm/nouveau/nv50_software.c new file mode 100644 index 00000000000..114d2517d4a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv50_software.c @@ -0,0 +1,214 @@ +/* + * Copyright 2012 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_ramht.h" +#include "nouveau_software.h" + +#include "nv50_display.h" + +struct nv50_software_priv { + struct nouveau_software_priv base; +}; + +struct nv50_software_chan { + struct nouveau_software_chan base; + struct { + struct nouveau_gpuobj *object; + } vblank; +}; + +static int +mthd_dma_vblsem(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data) +{ + struct nv50_software_chan *pch = chan->engctx[NVOBJ_ENGINE_SW]; + struct nouveau_gpuobj *gpuobj; + + gpuobj = nouveau_ramht_find(chan, data); + if (!gpuobj) + return -ENOENT; + + if (nouveau_notifier_offset(gpuobj, NULL)) + return -EINVAL; + + pch->vblank.object = gpuobj; + pch->base.vblank.offset = ~0; + return 0; +} + +static int +mthd_vblsem_offset(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data) +{ + struct nv50_software_chan *pch = chan->engctx[NVOBJ_ENGINE_SW]; + + if (nouveau_notifier_offset(pch->vblank.object, &data)) + return -ERANGE; + + pch->base.vblank.offset = data >> 2; + return 0; +} + +static int +mthd_vblsem_value(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data) +{ + struct nv50_software_chan *pch = chan->engctx[NVOBJ_ENGINE_SW]; + pch->base.vblank.value = data; + return 0; +} + +static int +mthd_vblsem_release(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data) +{ + struct nv50_software_priv *psw = nv_engine(chan->dev, NVOBJ_ENGINE_SW); + struct nv50_software_chan *pch = chan->engctx[NVOBJ_ENGINE_SW]; + struct drm_device *dev = chan->dev; + + if (!pch->vblank.object || pch->base.vblank.offset == ~0 || data > 1) + return -EINVAL; + + drm_vblank_get(dev, data); + + pch->base.vblank.head = data; + list_add(&pch->base.vblank.list, &psw->base.vblank); + return 0; +} + +static int +mthd_flip(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data) +{ + nouveau_finish_page_flip(chan, NULL); + return 0; +} + +static int +nv50_software_context_new(struct nouveau_channel *chan, int engine) +{ + struct nv50_software_priv *psw = nv_engine(chan->dev, NVOBJ_ENGINE_SW); + struct nv50_display *pdisp = nv50_display(chan->dev); + struct nv50_software_chan *pch; + int ret = 0, i; + + pch = kzalloc(sizeof(*pch), GFP_KERNEL); + if (!pch) + return -ENOMEM; + + nouveau_software_context_new(&pch->base); + pch->base.vblank.bo = chan->notifier_bo; + chan->engctx[engine] = pch; + + /* dma objects for display sync channel semaphore blocks */ + for (i = 0; i < chan->dev->mode_config.num_crtc; i++) { + struct nv50_display_crtc *dispc = &pdisp->crtc[i]; + struct nouveau_gpuobj *obj = NULL; + + ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, + dispc->sem.bo->bo.offset, 0x1000, + NV_MEM_ACCESS_RW, + NV_MEM_TARGET_VRAM, &obj); + if (ret) + break; + + ret = nouveau_ramht_insert(chan, NvEvoSema0 + i, obj); + nouveau_gpuobj_ref(NULL, &obj); + } + + if (ret) + psw->base.base.context_del(chan, engine); + return ret; +} + +static void +nv50_software_context_del(struct nouveau_channel *chan, int engine) +{ + struct nv50_software_chan *pch = chan->engctx[engine]; + chan->engctx[engine] = NULL; + kfree(pch); +} + +static int +nv50_software_object_new(struct nouveau_channel *chan, int engine, + u32 handle, u16 class) +{ + struct drm_device *dev = chan->dev; + struct nouveau_gpuobj *obj = NULL; + int ret; + + ret = nouveau_gpuobj_new(dev, chan, 16, 16, 0, &obj); + if (ret) + return ret; + obj->engine = 0; + obj->class = class; + + ret = nouveau_ramht_insert(chan, handle, obj); + nouveau_gpuobj_ref(NULL, &obj); + return ret; +} + +static int +nv50_software_init(struct drm_device *dev, int engine) +{ + return 0; +} + +static int +nv50_software_fini(struct drm_device *dev, int engine, bool suspend) +{ + return 0; +} + +static void +nv50_software_destroy(struct drm_device *dev, int engine) +{ + struct nv50_software_priv *psw = nv_engine(dev, engine); + + NVOBJ_ENGINE_DEL(dev, SW); + kfree(psw); +} + +int +nv50_software_create(struct drm_device *dev) +{ + struct nv50_software_priv *psw = kzalloc(sizeof(*psw), GFP_KERNEL); + if (!psw) + return -ENOMEM; + + psw->base.base.destroy = nv50_software_destroy; + psw->base.base.init = nv50_software_init; + psw->base.base.fini = nv50_software_fini; + psw->base.base.context_new = nv50_software_context_new; + psw->base.base.context_del = nv50_software_context_del; + psw->base.base.object_new = nv50_software_object_new; + nouveau_software_create(&psw->base); + + NVOBJ_ENGINE_ADD(dev, SW, &psw->base.base); + NVOBJ_CLASS(dev, 0x506e, SW); + NVOBJ_MTHD (dev, 0x506e, 0x018c, mthd_dma_vblsem); + NVOBJ_MTHD (dev, 0x506e, 0x0400, mthd_vblsem_offset); + NVOBJ_MTHD (dev, 0x506e, 0x0404, mthd_vblsem_value); + NVOBJ_MTHD (dev, 0x506e, 0x0408, mthd_vblsem_release); + NVOBJ_MTHD (dev, 0x506e, 0x0500, mthd_flip); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c index 27464021247..a9514eaa74c 100644 --- a/drivers/gpu/drm/nouveau/nv50_sor.c +++ b/drivers/gpu/drm/nouveau/nv50_sor.c @@ -242,9 +242,9 @@ nv50_sor_disconnect(struct drm_encoder *encoder) NV_ERROR(dev, "no space while disconnecting SOR\n"); return; } - BEGIN_RING(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1); + BEGIN_NV04(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1); OUT_RING (evo, 0); - BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1); + BEGIN_NV04(evo, 0, NV50_EVO_UPDATE, 1); OUT_RING (evo, 0); nouveau_hdmi_mode_set(encoder, NULL); @@ -430,7 +430,7 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, nv_encoder->crtc = NULL; return; } - BEGIN_RING(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1); + BEGIN_NV04(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1); OUT_RING(evo, mode_ctl); } diff --git a/drivers/gpu/drm/nouveau/nv50_vm.c b/drivers/gpu/drm/nouveau/nv50_vm.c index 44fbac9c7d9..179bb42a635 100644 --- a/drivers/gpu/drm/nouveau/nv50_vm.c +++ b/drivers/gpu/drm/nouveau/nv50_vm.c @@ -147,7 +147,6 @@ nv50_vm_flush(struct nouveau_vm *vm) { struct drm_nouveau_private *dev_priv = vm->dev->dev_private; struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem; - struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; int i; pinstmem->flush(vm->dev); @@ -158,7 +157,6 @@ nv50_vm_flush(struct nouveau_vm *vm) return; } - pfifo->tlb_flush(vm->dev); for (i = 0; i < NVOBJ_ENGINE_NR; i++) { if (atomic_read(&vm->engref[i])) dev_priv->eng[i]->tlb_flush(vm->dev, i); diff --git a/drivers/gpu/drm/nouveau/nv84_fence.c b/drivers/gpu/drm/nouveau/nv84_fence.c new file mode 100644 index 00000000000..c2f889b0d34 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv84_fence.c @@ -0,0 +1,177 @@ +/* + * Copyright 2012 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_dma.h" +#include "nouveau_fifo.h" +#include "nouveau_ramht.h" +#include "nouveau_fence.h" + +struct nv84_fence_chan { + struct nouveau_fence_chan base; +}; + +struct nv84_fence_priv { + struct nouveau_fence_priv base; + struct nouveau_gpuobj *mem; +}; + +static int +nv84_fence_emit(struct nouveau_fence *fence) +{ + struct nouveau_channel *chan = fence->channel; + int ret = RING_SPACE(chan, 7); + if (ret == 0) { + BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1); + OUT_RING (chan, NvSema); + BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); + OUT_RING (chan, upper_32_bits(chan->id * 16)); + OUT_RING (chan, lower_32_bits(chan->id * 16)); + OUT_RING (chan, fence->sequence); + OUT_RING (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_WRITE_LONG); + FIRE_RING (chan); + } + return ret; +} + + +static int +nv84_fence_sync(struct nouveau_fence *fence, + struct nouveau_channel *prev, struct nouveau_channel *chan) +{ + int ret = RING_SPACE(chan, 7); + if (ret == 0) { + BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1); + OUT_RING (chan, NvSema); + BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); + OUT_RING (chan, upper_32_bits(prev->id * 16)); + OUT_RING (chan, lower_32_bits(prev->id * 16)); + OUT_RING (chan, fence->sequence); + OUT_RING (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_GEQUAL); + FIRE_RING (chan); + } + return ret; +} + +static u32 +nv84_fence_read(struct nouveau_channel *chan) +{ + struct nv84_fence_priv *priv = nv_engine(chan->dev, NVOBJ_ENGINE_FENCE); + return nv_ro32(priv->mem, chan->id * 16); +} + +static void +nv84_fence_context_del(struct nouveau_channel *chan, int engine) +{ + struct nv84_fence_chan *fctx = chan->engctx[engine]; + nouveau_fence_context_del(&fctx->base); + chan->engctx[engine] = NULL; + kfree(fctx); +} + +static int +nv84_fence_context_new(struct nouveau_channel *chan, int engine) +{ + struct nv84_fence_priv *priv = nv_engine(chan->dev, engine); + struct nv84_fence_chan *fctx; + struct nouveau_gpuobj *obj; + int ret; + + fctx = chan->engctx[engine] = kzalloc(sizeof(*fctx), GFP_KERNEL); + if (!fctx) + return -ENOMEM; + + nouveau_fence_context_new(&fctx->base); + + ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_FROM_MEMORY, + priv->mem->vinst, priv->mem->size, + NV_MEM_ACCESS_RW, + NV_MEM_TARGET_VRAM, &obj); + if (ret == 0) { + ret = nouveau_ramht_insert(chan, NvSema, obj); + nouveau_gpuobj_ref(NULL, &obj); + nv_wo32(priv->mem, chan->id * 16, 0x00000000); + } + + if (ret) + nv84_fence_context_del(chan, engine); + return ret; +} + +static int +nv84_fence_fini(struct drm_device *dev, int engine, bool suspend) +{ + return 0; +} + +static int +nv84_fence_init(struct drm_device *dev, int engine) +{ + return 0; +} + +static void +nv84_fence_destroy(struct drm_device *dev, int engine) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv84_fence_priv *priv = nv_engine(dev, engine); + + nouveau_gpuobj_ref(NULL, &priv->mem); + dev_priv->eng[engine] = NULL; + kfree(priv); +} + +int +nv84_fence_create(struct drm_device *dev) +{ + struct nouveau_fifo_priv *pfifo = nv_engine(dev, NVOBJ_ENGINE_FIFO); + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv84_fence_priv *priv; + int ret; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base.engine.destroy = nv84_fence_destroy; + priv->base.engine.init = nv84_fence_init; + priv->base.engine.fini = nv84_fence_fini; + priv->base.engine.context_new = nv84_fence_context_new; + priv->base.engine.context_del = nv84_fence_context_del; + priv->base.emit = nv84_fence_emit; + priv->base.sync = nv84_fence_sync; + priv->base.read = nv84_fence_read; + dev_priv->eng[NVOBJ_ENGINE_FENCE] = &priv->base.engine; + + ret = nouveau_gpuobj_new(dev, NULL, 16 * pfifo->channels, + 0x1000, 0, &priv->mem); + if (ret) + goto out; + +out: + if (ret) + nv84_fence_destroy(dev, NVOBJ_ENGINE_FENCE); + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nv84_fifo.c b/drivers/gpu/drm/nouveau/nv84_fifo.c new file mode 100644 index 00000000000..cc82d799fc3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv84_fifo.c @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2012 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_fifo.h" +#include "nouveau_ramht.h" +#include "nouveau_vm.h" + +struct nv84_fifo_priv { + struct nouveau_fifo_priv base; + struct nouveau_gpuobj *playlist[2]; + int cur_playlist; +}; + +struct nv84_fifo_chan { + struct nouveau_fifo_chan base; + struct nouveau_gpuobj *ramfc; + struct nouveau_gpuobj *cache; +}; + +static int +nv84_fifo_context_new(struct nouveau_channel *chan, int engine) +{ + struct nv84_fifo_priv *priv = nv_engine(chan->dev, engine); + struct nv84_fifo_chan *fctx; + struct drm_device *dev = chan->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + u64 ib_offset = chan->pushbuf_base + chan->dma.ib_base * 4; + u64 instance; + unsigned long flags; + int ret; + + fctx = chan->engctx[engine] = kzalloc(sizeof(*fctx), GFP_KERNEL); + if (!fctx) + return -ENOMEM; + atomic_inc(&chan->vm->engref[engine]); + + chan->user = ioremap(pci_resource_start(dev->pdev, 0) + + NV50_USER(chan->id), PAGE_SIZE); + if (!chan->user) { + ret = -ENOMEM; + goto error; + } + + ret = nouveau_gpuobj_new(dev, chan, 256, 256, NVOBJ_FLAG_ZERO_ALLOC | + NVOBJ_FLAG_ZERO_FREE, &fctx->ramfc); + if (ret) + goto error; + + instance = fctx->ramfc->vinst >> 8; + + ret = nouveau_gpuobj_new(dev, chan, 4096, 1024, 0, &fctx->cache); + if (ret) + goto error; + + nv_wo32(fctx->ramfc, 0x3c, 0x403f6078); + nv_wo32(fctx->ramfc, 0x40, 0x00000000); + nv_wo32(fctx->ramfc, 0x44, 0x01003fff); + nv_wo32(fctx->ramfc, 0x48, chan->pushbuf->cinst >> 4); + nv_wo32(fctx->ramfc, 0x50, lower_32_bits(ib_offset)); + nv_wo32(fctx->ramfc, 0x54, upper_32_bits(ib_offset) | + drm_order(chan->dma.ib_max + 1) << 16); + nv_wo32(fctx->ramfc, 0x60, 0x7fffffff); + nv_wo32(fctx->ramfc, 0x78, 0x00000000); + nv_wo32(fctx->ramfc, 0x7c, 0x30000001); + nv_wo32(fctx->ramfc, 0x80, ((chan->ramht->bits - 9) << 27) | + (4 << 24) /* SEARCH_FULL */ | + (chan->ramht->gpuobj->cinst >> 4)); + nv_wo32(fctx->ramfc, 0x88, fctx->cache->vinst >> 10); + nv_wo32(fctx->ramfc, 0x98, chan->ramin->vinst >> 12); + + nv_wo32(chan->ramin, 0x00, chan->id); + nv_wo32(chan->ramin, 0x04, fctx->ramfc->vinst >> 8); + + dev_priv->engine.instmem.flush(dev); + + spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + nv_wr32(dev, 0x002600 + (chan->id * 4), 0x80000000 | instance); + nv50_fifo_playlist_update(dev); + spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); + +error: + if (ret) + priv->base.base.context_del(chan, engine); + return ret; +} + +static void +nv84_fifo_context_del(struct nouveau_channel *chan, int engine) +{ + struct nv84_fifo_chan *fctx = chan->engctx[engine]; + struct drm_device *dev = chan->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + unsigned long flags; + + /* remove channel from playlist, will context switch if active */ + spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + nv_mask(dev, 0x002600 + (chan->id * 4), 0x80000000, 0x00000000); + nv50_fifo_playlist_update(dev); + + /* tell any engines on this channel to unload their contexts */ + nv_wr32(dev, 0x0032fc, chan->ramin->vinst >> 12); + if (!nv_wait_ne(dev, 0x0032fc, 0xffffffff, 0xffffffff)) + NV_INFO(dev, "PFIFO: channel %d unload timeout\n", chan->id); + + nv_wr32(dev, 0x002600 + (chan->id * 4), 0x00000000); + spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); + + /* clean up */ + if (chan->user) { + iounmap(chan->user); + chan->user = NULL; + } + + nouveau_gpuobj_ref(NULL, &fctx->ramfc); + nouveau_gpuobj_ref(NULL, &fctx->cache); + + atomic_dec(&chan->vm->engref[engine]); + chan->engctx[engine] = NULL; + kfree(fctx); +} + +static int +nv84_fifo_init(struct drm_device *dev, int engine) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv84_fifo_chan *fctx; + u32 instance; + int i; + + nv_mask(dev, 0x000200, 0x00000100, 0x00000000); + nv_mask(dev, 0x000200, 0x00000100, 0x00000100); + nv_wr32(dev, 0x00250c, 0x6f3cfc34); + nv_wr32(dev, 0x002044, 0x01003fff); + + nv_wr32(dev, 0x002100, 0xffffffff); + nv_wr32(dev, 0x002140, 0xffffffff); + + for (i = 0; i < 128; i++) { + struct nouveau_channel *chan = dev_priv->channels.ptr[i]; + if (chan && (fctx = chan->engctx[engine])) + instance = 0x80000000 | fctx->ramfc->vinst >> 8; + else + instance = 0x00000000; + nv_wr32(dev, 0x002600 + (i * 4), instance); + } + + nv50_fifo_playlist_update(dev); + + nv_wr32(dev, 0x003200, 1); + nv_wr32(dev, 0x003250, 1); + nv_wr32(dev, 0x002500, 1); + return 0; +} + +static int +nv84_fifo_fini(struct drm_device *dev, int engine, bool suspend) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv84_fifo_priv *priv = nv_engine(dev, engine); + int i; + + /* set playlist length to zero, fifo will unload context */ + nv_wr32(dev, 0x0032ec, 0); + + /* tell all connected engines to unload their contexts */ + for (i = 0; i < priv->base.channels; i++) { + struct nouveau_channel *chan = dev_priv->channels.ptr[i]; + if (chan) + nv_wr32(dev, 0x0032fc, chan->ramin->vinst >> 12); + if (!nv_wait_ne(dev, 0x0032fc, 0xffffffff, 0xffffffff)) { + NV_INFO(dev, "PFIFO: channel %d unload timeout\n", i); + return -EBUSY; + } + } + + nv_wr32(dev, 0x002140, 0); + return 0; +} + +int +nv84_fifo_create(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv84_fifo_priv *priv; + int ret; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base.base.destroy = nv50_fifo_destroy; + priv->base.base.init = nv84_fifo_init; + priv->base.base.fini = nv84_fifo_fini; + priv->base.base.context_new = nv84_fifo_context_new; + priv->base.base.context_del = nv84_fifo_context_del; + priv->base.base.tlb_flush = nv50_fifo_tlb_flush; + priv->base.channels = 127; + dev_priv->eng[NVOBJ_ENGINE_FIFO] = &priv->base.base; + + ret = nouveau_gpuobj_new(dev, NULL, priv->base.channels * 4, 0x1000, + NVOBJ_FLAG_ZERO_ALLOC, &priv->playlist[0]); + if (ret) + goto error; + + ret = nouveau_gpuobj_new(dev, NULL, priv->base.channels * 4, 0x1000, + NVOBJ_FLAG_ZERO_ALLOC, &priv->playlist[1]); + if (ret) + goto error; + + nouveau_irq_register(dev, 8, nv04_fifo_isr); +error: + if (ret) + priv->base.base.destroy(dev, NVOBJ_ENGINE_FIFO); + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nv98_crypt.c b/drivers/gpu/drm/nouveau/nv98_crypt.c index db94ff0a9fa..e25e13fb894 100644 --- a/drivers/gpu/drm/nouveau/nv98_crypt.c +++ b/drivers/gpu/drm/nouveau/nv98_crypt.c @@ -23,21 +23,93 @@ */ #include "drmP.h" + #include "nouveau_drv.h" #include "nouveau_util.h" #include "nouveau_vm.h" #include "nouveau_ramht.h" -struct nv98_crypt_engine { +#include "nv98_crypt.fuc.h" + +struct nv98_crypt_priv { struct nouveau_exec_engine base; }; +struct nv98_crypt_chan { + struct nouveau_gpuobj *mem; +}; + static int -nv98_crypt_fini(struct drm_device *dev, int engine, bool suspend) +nv98_crypt_context_new(struct nouveau_channel *chan, int engine) +{ + struct drm_device *dev = chan->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv98_crypt_priv *priv = nv_engine(dev, engine); + struct nv98_crypt_chan *cctx; + int ret; + + cctx = chan->engctx[engine] = kzalloc(sizeof(*cctx), GFP_KERNEL); + if (!cctx) + return -ENOMEM; + + atomic_inc(&chan->vm->engref[engine]); + + ret = nouveau_gpuobj_new(dev, chan, 256, 0, NVOBJ_FLAG_ZERO_ALLOC | + NVOBJ_FLAG_ZERO_FREE, &cctx->mem); + if (ret) + goto error; + + nv_wo32(chan->ramin, 0xa0, 0x00190000); + nv_wo32(chan->ramin, 0xa4, cctx->mem->vinst + cctx->mem->size - 1); + nv_wo32(chan->ramin, 0xa8, cctx->mem->vinst); + nv_wo32(chan->ramin, 0xac, 0x00000000); + nv_wo32(chan->ramin, 0xb0, 0x00000000); + nv_wo32(chan->ramin, 0xb4, 0x00000000); + dev_priv->engine.instmem.flush(dev); + +error: + if (ret) + priv->base.context_del(chan, engine); + return ret; +} + +static void +nv98_crypt_context_del(struct nouveau_channel *chan, int engine) +{ + struct nv98_crypt_chan *cctx = chan->engctx[engine]; + int i; + + for (i = 0xa0; i < 0xb4; i += 4) + nv_wo32(chan->ramin, i, 0x00000000); + + nouveau_gpuobj_ref(NULL, &cctx->mem); + + atomic_dec(&chan->vm->engref[engine]); + chan->engctx[engine] = NULL; + kfree(cctx); +} + +static int +nv98_crypt_object_new(struct nouveau_channel *chan, int engine, + u32 handle, u16 class) { - if (!(nv_rd32(dev, 0x000200) & 0x00004000)) - return 0; + struct nv98_crypt_chan *cctx = chan->engctx[engine]; + + /* fuc engine doesn't need an object, our ramht code does.. */ + cctx->mem->engine = 5; + cctx->mem->class = class; + return nouveau_ramht_insert(chan, handle, cctx->mem); +} +static void +nv98_crypt_tlb_flush(struct drm_device *dev, int engine) +{ + nv50_vm_flush_engine(dev, 0x0a); +} + +static int +nv98_crypt_fini(struct drm_device *dev, int engine, bool suspend) +{ nv_mask(dev, 0x000200, 0x00004000, 0x00000000); return 0; } @@ -45,34 +117,100 @@ nv98_crypt_fini(struct drm_device *dev, int engine, bool suspend) static int nv98_crypt_init(struct drm_device *dev, int engine) { + int i; + + /* reset! */ nv_mask(dev, 0x000200, 0x00004000, 0x00000000); nv_mask(dev, 0x000200, 0x00004000, 0x00004000); + + /* wait for exit interrupt to signal */ + nv_wait(dev, 0x087008, 0x00000010, 0x00000010); + nv_wr32(dev, 0x087004, 0x00000010); + + /* upload microcode code and data segments */ + nv_wr32(dev, 0x087ff8, 0x00100000); + for (i = 0; i < ARRAY_SIZE(nv98_pcrypt_code); i++) + nv_wr32(dev, 0x087ff4, nv98_pcrypt_code[i]); + + nv_wr32(dev, 0x087ff8, 0x00000000); + for (i = 0; i < ARRAY_SIZE(nv98_pcrypt_data); i++) + nv_wr32(dev, 0x087ff4, nv98_pcrypt_data[i]); + + /* start it running */ + nv_wr32(dev, 0x08710c, 0x00000000); + nv_wr32(dev, 0x087104, 0x00000000); /* ENTRY */ + nv_wr32(dev, 0x087100, 0x00000002); /* TRIGGER */ return 0; } +static struct nouveau_enum nv98_crypt_isr_error_name[] = { + { 0x0000, "ILLEGAL_MTHD" }, + { 0x0001, "INVALID_BITFIELD" }, + { 0x0002, "INVALID_ENUM" }, + { 0x0003, "QUERY" }, + {} +}; + +static void +nv98_crypt_isr(struct drm_device *dev) +{ + u32 disp = nv_rd32(dev, 0x08701c); + u32 stat = nv_rd32(dev, 0x087008) & disp & ~(disp >> 16); + u32 inst = nv_rd32(dev, 0x087050) & 0x3fffffff; + u32 ssta = nv_rd32(dev, 0x087040) & 0x0000ffff; + u32 addr = nv_rd32(dev, 0x087040) >> 16; + u32 mthd = (addr & 0x07ff) << 2; + u32 subc = (addr & 0x3800) >> 11; + u32 data = nv_rd32(dev, 0x087044); + int chid = nv50_graph_isr_chid(dev, inst); + + if (stat & 0x00000040) { + NV_INFO(dev, "PCRYPT: DISPATCH_ERROR ["); + nouveau_enum_print(nv98_crypt_isr_error_name, ssta); + printk("] ch %d [0x%08x] subc %d mthd 0x%04x data 0x%08x\n", + chid, inst, subc, mthd, data); + nv_wr32(dev, 0x087004, 0x00000040); + stat &= ~0x00000040; + } + + if (stat) { + NV_INFO(dev, "PCRYPT: unhandled intr 0x%08x\n", stat); + nv_wr32(dev, 0x087004, stat); + } + + nv50_fb_vm_trap(dev, 1); +} + static void nv98_crypt_destroy(struct drm_device *dev, int engine) { - struct nv98_crypt_engine *pcrypt = nv_engine(dev, engine); + struct nv98_crypt_priv *priv = nv_engine(dev, engine); + nouveau_irq_unregister(dev, 14); NVOBJ_ENGINE_DEL(dev, CRYPT); - - kfree(pcrypt); + kfree(priv); } int nv98_crypt_create(struct drm_device *dev) { - struct nv98_crypt_engine *pcrypt; + struct nv98_crypt_priv *priv; - pcrypt = kzalloc(sizeof(*pcrypt), GFP_KERNEL); - if (!pcrypt) + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) return -ENOMEM; - pcrypt->base.destroy = nv98_crypt_destroy; - pcrypt->base.init = nv98_crypt_init; - pcrypt->base.fini = nv98_crypt_fini; + priv->base.destroy = nv98_crypt_destroy; + priv->base.init = nv98_crypt_init; + priv->base.fini = nv98_crypt_fini; + priv->base.context_new = nv98_crypt_context_new; + priv->base.context_del = nv98_crypt_context_del; + priv->base.object_new = nv98_crypt_object_new; + priv->base.tlb_flush = nv98_crypt_tlb_flush; + + nouveau_irq_register(dev, 14, nv98_crypt_isr); - NVOBJ_ENGINE_ADD(dev, CRYPT, &pcrypt->base); + NVOBJ_ENGINE_ADD(dev, CRYPT, &priv->base); + NVOBJ_CLASS(dev, 0x88b4, CRYPT); return 0; } diff --git a/drivers/gpu/drm/nouveau/nv98_crypt.fuc b/drivers/gpu/drm/nouveau/nv98_crypt.fuc new file mode 100644 index 00000000000..7393813044d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv98_crypt.fuc @@ -0,0 +1,698 @@ +/* + * fuc microcode for nv98 pcrypt engine + * Copyright (C) 2010 Marcin Kościelnicki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +.section #nv98_pcrypt_data + +ctx_dma: +ctx_dma_query: .b32 0 +ctx_dma_src: .b32 0 +ctx_dma_dst: .b32 0 +.equ #dma_count 3 +ctx_query_address_high: .b32 0 +ctx_query_address_low: .b32 0 +ctx_query_counter: .b32 0 +ctx_cond_address_high: .b32 0 +ctx_cond_address_low: .b32 0 +ctx_cond_off: .b32 0 +ctx_src_address_high: .b32 0 +ctx_src_address_low: .b32 0 +ctx_dst_address_high: .b32 0 +ctx_dst_address_low: .b32 0 +ctx_mode: .b32 0 +.align 16 +ctx_key: .skip 16 +ctx_iv: .skip 16 + +.align 0x80 +swap: +.skip 32 + +.align 8 +common_cmd_dtable: +.b32 #ctx_query_address_high + 0x20000 ~0xff +.b32 #ctx_query_address_low + 0x20000 ~0xfffffff0 +.b32 #ctx_query_counter + 0x20000 ~0xffffffff +.b32 #cmd_query_get + 0x00000 ~1 +.b32 #ctx_cond_address_high + 0x20000 ~0xff +.b32 #ctx_cond_address_low + 0x20000 ~0xfffffff0 +.b32 #cmd_cond_mode + 0x00000 ~7 +.b32 #cmd_wrcache_flush + 0x00000 ~0 +.equ #common_cmd_max 0x88 + + +.align 8 +engine_cmd_dtable: +.b32 #ctx_key + 0x0 + 0x20000 ~0xffffffff +.b32 #ctx_key + 0x4 + 0x20000 ~0xffffffff +.b32 #ctx_key + 0x8 + 0x20000 ~0xffffffff +.b32 #ctx_key + 0xc + 0x20000 ~0xffffffff +.b32 #ctx_iv + 0x0 + 0x20000 ~0xffffffff +.b32 #ctx_iv + 0x4 + 0x20000 ~0xffffffff +.b32 #ctx_iv + 0x8 + 0x20000 ~0xffffffff +.b32 #ctx_iv + 0xc + 0x20000 ~0xffffffff +.b32 #ctx_src_address_high + 0x20000 ~0xff +.b32 #ctx_src_address_low + 0x20000 ~0xfffffff0 +.b32 #ctx_dst_address_high + 0x20000 ~0xff +.b32 #ctx_dst_address_low + 0x20000 ~0xfffffff0 +.b32 #crypt_cmd_mode + 0x00000 ~0xf +.b32 #crypt_cmd_length + 0x10000 ~0x0ffffff0 +.equ #engine_cmd_max 0xce + +.align 4 +crypt_dtable: +.b16 #crypt_copy_prep #crypt_do_inout +.b16 #crypt_store_prep #crypt_do_out +.b16 #crypt_ecb_e_prep #crypt_do_inout +.b16 #crypt_ecb_d_prep #crypt_do_inout +.b16 #crypt_cbc_e_prep #crypt_do_inout +.b16 #crypt_cbc_d_prep #crypt_do_inout +.b16 #crypt_pcbc_e_prep #crypt_do_inout +.b16 #crypt_pcbc_d_prep #crypt_do_inout +.b16 #crypt_cfb_e_prep #crypt_do_inout +.b16 #crypt_cfb_d_prep #crypt_do_inout +.b16 #crypt_ofb_prep #crypt_do_inout +.b16 #crypt_ctr_prep #crypt_do_inout +.b16 #crypt_cbc_mac_prep #crypt_do_in +.b16 #crypt_cmac_finish_complete_prep #crypt_do_in +.b16 #crypt_cmac_finish_partial_prep #crypt_do_in + +.align 0x100 + +.section #nv98_pcrypt_code + + // $r0 is always set to 0 in our code - this allows some space savings. + clear b32 $r0 + + // set up the interrupt handler + mov $r1 #ih + mov $iv0 $r1 + + // init stack pointer + mov $sp $r0 + + // set interrupt dispatch - route timer, fifo, ctxswitch to i0, others to host + movw $r1 0xfff0 + sethi $r1 0 + mov $r2 0x400 + iowr I[$r2 + 0x300] $r1 + + // enable the interrupts + or $r1 0xc + iowr I[$r2] $r1 + + // enable fifo access and context switching + mov $r1 3 + mov $r2 0x1200 + iowr I[$r2] $r1 + + // enable i0 delivery + bset $flags ie0 + + // sleep forver, waking only for interrupts. + bset $flags $p0 + spin: + sleep $p0 + bra #spin + +// i0 handler +ih: + // see which interrupts we got + iord $r1 I[$r0 + 0x200] + + and $r2 $r1 0x8 + cmpu b32 $r2 0 + bra e #noctx + + // context switch... prepare the regs for xfer + mov $r2 0x7700 + mov $xtargets $r2 + mov $xdbase $r0 + // 128-byte context. + mov $r2 0 + sethi $r2 0x50000 + + // read current channel + mov $r3 0x1400 + iord $r4 I[$r3] + // if bit 30 set, it's active, so we have to unload it first. + shl b32 $r5 $r4 1 + cmps b32 $r5 0 + bra nc #ctxload + + // unload the current channel - save the context + xdst $r0 $r2 + xdwait + // and clear bit 30, then write back + bclr $r4 0x1e + iowr I[$r3] $r4 + // tell PFIFO we unloaded + mov $r4 1 + iowr I[$r3 + 0x200] $r4 + + bra #noctx + + ctxload: + // no channel loaded - perhaps we're requested to load one + iord $r4 I[$r3 + 0x100] + shl b32 $r15 $r4 1 + cmps b32 $r15 0 + // if bit 30 of next channel not set, probably PFIFO is just + // killing a context. do a faux load, without the active bit. + bra nc #dummyload + + // ok, do a real context load. + xdld $r0 $r2 + xdwait + mov $r5 #ctx_dma + mov $r6 #dma_count - 1 + ctxload_dma_loop: + ld b32 $r7 D[$r5 + $r6 * 4] + add b32 $r8 $r6 0x180 + shl b32 $r8 8 + iowr I[$r8] $r7 + sub b32 $r6 1 + bra nc #ctxload_dma_loop + + dummyload: + // tell PFIFO we're done + mov $r5 2 + iowr I[$r3 + 0x200] $r5 + + noctx: + and $r2 $r1 0x4 + cmpu b32 $r2 0 + bra e #nocmd + + // incoming fifo command. + mov $r3 0x1900 + iord $r2 I[$r3 + 0x100] + iord $r3 I[$r3] + // extract the method + and $r4 $r2 0x7ff + // shift the addr to proper position if we need to interrupt later + shl b32 $r2 0x10 + + // mthd 0 and 0x100 [NAME, NOP]: ignore + and $r5 $r4 0x7bf + cmpu b32 $r5 0 + bra e #cmddone + + mov $r5 #engine_cmd_dtable - 0xc0 * 8 + mov $r6 #engine_cmd_max + cmpu b32 $r4 0xc0 + bra nc #dtable_cmd + mov $r5 #common_cmd_dtable - 0x80 * 8 + mov $r6 #common_cmd_max + cmpu b32 $r4 0x80 + bra nc #dtable_cmd + cmpu b32 $r4 0x60 + bra nc #dma_cmd + cmpu b32 $r4 0x50 + bra ne #illegal_mthd + + // mthd 0x140: PM_TRIGGER + mov $r2 0x2200 + clear b32 $r3 + sethi $r3 0x20000 + iowr I[$r2] $r3 + bra #cmddone + + dma_cmd: + // mthd 0x180...: DMA_* + cmpu b32 $r4 0x60+#dma_count + bra nc #illegal_mthd + shl b32 $r5 $r4 2 + add b32 $r5 (#ctx_dma - 0x60 * 4) & 0xffff + bset $r3 0x1e + st b32 D[$r5] $r3 + add b32 $r4 0x180 - 0x60 + shl b32 $r4 8 + iowr I[$r4] $r3 + bra #cmddone + + dtable_cmd: + cmpu b32 $r4 $r6 + bra nc #illegal_mthd + shl b32 $r4 3 + add b32 $r4 $r5 + ld b32 $r5 D[$r4 + 4] + and $r5 $r3 + cmpu b32 $r5 0 + bra ne #invalid_bitfield + ld b16 $r5 D[$r4] + ld b16 $r6 D[$r4 + 2] + cmpu b32 $r6 2 + bra e #cmd_setctx + ld b32 $r7 D[$r0 + #ctx_cond_off] + and $r6 $r7 + cmpu b32 $r6 1 + bra e #cmddone + call $r5 + bra $p1 #dispatch_error + bra #cmddone + + cmd_setctx: + st b32 D[$r5] $r3 + bra #cmddone + + + invalid_bitfield: + or $r2 1 + dispatch_error: + illegal_mthd: + mov $r4 0x1000 + iowr I[$r4] $r2 + iowr I[$r4 + 0x100] $r3 + mov $r4 0x40 + iowr I[$r0] $r4 + + im_loop: + iord $r4 I[$r0 + 0x200] + and $r4 0x40 + cmpu b32 $r4 0 + bra ne #im_loop + + cmddone: + // remove the command from FIFO + mov $r3 0x1d00 + mov $r4 1 + iowr I[$r3] $r4 + + nocmd: + // ack the processed interrupts + and $r1 $r1 0xc + iowr I[$r0 + 0x100] $r1 +iret + +cmd_query_get: + // if bit 0 of param set, trigger interrupt afterwards. + setp $p1 $r3 + or $r2 3 + + // read PTIMER, beware of races... + mov $r4 0xb00 + ptimer_retry: + iord $r6 I[$r4 + 0x100] + iord $r5 I[$r4] + iord $r7 I[$r4 + 0x100] + cmpu b32 $r6 $r7 + bra ne #ptimer_retry + + // prepare the query structure + ld b32 $r4 D[$r0 + #ctx_query_counter] + st b32 D[$r0 + #swap + 0x0] $r4 + st b32 D[$r0 + #swap + 0x4] $r0 + st b32 D[$r0 + #swap + 0x8] $r5 + st b32 D[$r0 + #swap + 0xc] $r6 + + // will use target 0, DMA_QUERY. + mov $xtargets $r0 + + ld b32 $r4 D[$r0 + #ctx_query_address_high] + shl b32 $r4 0x18 + mov $xdbase $r4 + + ld b32 $r4 D[$r0 + #ctx_query_address_low] + mov $r5 #swap + sethi $r5 0x20000 + xdst $r4 $r5 + xdwait + + ret + +cmd_cond_mode: + // if >= 5, INVALID_ENUM + bset $flags $p1 + or $r2 2 + cmpu b32 $r3 5 + bra nc #return + + // otherwise, no error. + bclr $flags $p1 + + // if < 2, no QUERY object is involved + cmpu b32 $r3 2 + bra nc #cmd_cond_mode_queryful + + xor $r3 1 + st b32 D[$r0 + #ctx_cond_off] $r3 + return: + ret + + cmd_cond_mode_queryful: + // ok, will need to pull a QUERY object, prepare offsets + ld b32 $r4 D[$r0 + #ctx_cond_address_high] + ld b32 $r5 D[$r0 + #ctx_cond_address_low] + and $r6 $r5 0xff + shr b32 $r5 8 + shl b32 $r4 0x18 + or $r4 $r5 + mov $xdbase $r4 + mov $xtargets $r0 + + // pull the first one + mov $r5 #swap + sethi $r5 0x20000 + xdld $r6 $r5 + + // if == 2, only a single QUERY is involved... + cmpu b32 $r3 2 + bra ne #cmd_cond_mode_double + + xdwait + ld b32 $r4 D[$r0 + #swap + 4] + cmpu b32 $r4 0 + xbit $r4 $flags z + st b32 D[$r0 + #ctx_cond_off] $r4 + ret + + // ok, we'll need to pull second one too + cmd_cond_mode_double: + add b32 $r6 0x10 + add b32 $r5 0x10 + xdld $r6 $r5 + xdwait + + // compare COUNTERs + ld b32 $r5 D[$r0 + #swap + 0x00] + ld b32 $r6 D[$r0 + #swap + 0x10] + cmpu b32 $r5 $r6 + xbit $r4 $flags z + + // compare RESen + ld b32 $r5 D[$r0 + #swap + 0x04] + ld b32 $r6 D[$r0 + #swap + 0x14] + cmpu b32 $r5 $r6 + xbit $r5 $flags z + and $r4 $r5 + + // and negate or not, depending on mode + cmpu b32 $r3 3 + xbit $r5 $flags z + xor $r4 $r5 + st b32 D[$r0 + #ctx_cond_off] $r4 + ret + +cmd_wrcache_flush: + bclr $flags $p1 + mov $r2 0x2200 + clear b32 $r3 + sethi $r3 0x10000 + iowr I[$r2] $r3 + ret + +crypt_cmd_mode: + // if >= 0xf, INVALID_ENUM + bset $flags $p1 + or $r2 2 + cmpu b32 $r3 0xf + bra nc #crypt_cmd_mode_return + + bclr $flags $p1 + st b32 D[$r0 + #ctx_mode] $r3 + + crypt_cmd_mode_return: + ret + +crypt_cmd_length: + // nop if length == 0 + cmpu b32 $r3 0 + bra e #crypt_cmd_mode_return + + // init key, IV + cxset 3 + mov $r4 #ctx_key + sethi $r4 0x70000 + xdst $r0 $r4 + mov $r4 #ctx_iv + sethi $r4 0x60000 + xdst $r0 $r4 + xdwait + ckeyreg $c7 + + // prepare the targets + mov $r4 0x2100 + mov $xtargets $r4 + + // prepare src address + ld b32 $r4 D[$r0 + #ctx_src_address_high] + ld b32 $r5 D[$r0 + #ctx_src_address_low] + shr b32 $r8 $r5 8 + shl b32 $r4 0x18 + or $r4 $r8 + and $r5 $r5 0xff + + // prepare dst address + ld b32 $r6 D[$r0 + #ctx_dst_address_high] + ld b32 $r7 D[$r0 + #ctx_dst_address_low] + shr b32 $r8 $r7 8 + shl b32 $r6 0x18 + or $r6 $r8 + and $r7 $r7 0xff + + // find the proper prep & do functions + ld b32 $r8 D[$r0 + #ctx_mode] + shl b32 $r8 2 + + // run prep + ld b16 $r9 D[$r8 + #crypt_dtable] + call $r9 + + // do it + ld b16 $r9 D[$r8 + #crypt_dtable + 2] + call $r9 + cxset 1 + xdwait + cxset 0x61 + xdwait + xdwait + + // update src address + shr b32 $r8 $r4 0x18 + shl b32 $r9 $r4 8 + add b32 $r9 $r5 + adc b32 $r8 0 + st b32 D[$r0 + #ctx_src_address_high] $r8 + st b32 D[$r0 + #ctx_src_address_low] $r9 + + // update dst address + shr b32 $r8 $r6 0x18 + shl b32 $r9 $r6 8 + add b32 $r9 $r7 + adc b32 $r8 0 + st b32 D[$r0 + #ctx_dst_address_high] $r8 + st b32 D[$r0 + #ctx_dst_address_low] $r9 + + // pull updated IV + cxset 2 + mov $r4 #ctx_iv + sethi $r4 0x60000 + xdld $r0 $r4 + xdwait + + ret + + +crypt_copy_prep: + cs0begin 2 + cxsin $c0 + cxsout $c0 + ret + +crypt_store_prep: + cs0begin 1 + cxsout $c6 + ret + +crypt_ecb_e_prep: + cs0begin 3 + cxsin $c0 + cenc $c0 $c0 + cxsout $c0 + ret + +crypt_ecb_d_prep: + ckexp $c7 $c7 + cs0begin 3 + cxsin $c0 + cdec $c0 $c0 + cxsout $c0 + ret + +crypt_cbc_e_prep: + cs0begin 4 + cxsin $c0 + cxor $c6 $c0 + cenc $c6 $c6 + cxsout $c6 + ret + +crypt_cbc_d_prep: + ckexp $c7 $c7 + cs0begin 5 + cmov $c2 $c6 + cxsin $c6 + cdec $c0 $c6 + cxor $c0 $c2 + cxsout $c0 + ret + +crypt_pcbc_e_prep: + cs0begin 5 + cxsin $c0 + cxor $c6 $c0 + cenc $c6 $c6 + cxsout $c6 + cxor $c6 $c0 + ret + +crypt_pcbc_d_prep: + ckexp $c7 $c7 + cs0begin 5 + cxsin $c0 + cdec $c1 $c0 + cxor $c6 $c1 + cxsout $c6 + cxor $c6 $c0 + ret + +crypt_cfb_e_prep: + cs0begin 4 + cenc $c6 $c6 + cxsin $c0 + cxor $c6 $c0 + cxsout $c6 + ret + +crypt_cfb_d_prep: + cs0begin 4 + cenc $c0 $c6 + cxsin $c6 + cxor $c0 $c6 + cxsout $c0 + ret + +crypt_ofb_prep: + cs0begin 4 + cenc $c6 $c6 + cxsin $c0 + cxor $c0 $c6 + cxsout $c0 + ret + +crypt_ctr_prep: + cs0begin 5 + cenc $c1 $c6 + cadd $c6 1 + cxsin $c0 + cxor $c0 $c1 + cxsout $c0 + ret + +crypt_cbc_mac_prep: + cs0begin 3 + cxsin $c0 + cxor $c6 $c0 + cenc $c6 $c6 + ret + +crypt_cmac_finish_complete_prep: + cs0begin 7 + cxsin $c0 + cxor $c6 $c0 + cxor $c0 $c0 + cenc $c0 $c0 + cprecmac $c0 $c0 + cxor $c6 $c0 + cenc $c6 $c6 + ret + +crypt_cmac_finish_partial_prep: + cs0begin 8 + cxsin $c0 + cxor $c6 $c0 + cxor $c0 $c0 + cenc $c0 $c0 + cprecmac $c0 $c0 + cprecmac $c0 $c0 + cxor $c6 $c0 + cenc $c6 $c6 + ret + +// TODO +crypt_do_in: + add b32 $r3 $r5 + mov $xdbase $r4 + mov $r9 #swap + sethi $r9 0x20000 + crypt_do_in_loop: + xdld $r5 $r9 + xdwait + cxset 0x22 + xdst $r0 $r9 + cs0exec 1 + xdwait + add b32 $r5 0x10 + cmpu b32 $r5 $r3 + bra ne #crypt_do_in_loop + cxset 1 + xdwait + ret + +crypt_do_out: + add b32 $r3 $r7 + mov $xdbase $r6 + mov $r9 #swap + sethi $r9 0x20000 + crypt_do_out_loop: + cs0exec 1 + cxset 0x61 + xdld $r7 $r9 + xdst $r7 $r9 + cxset 1 + xdwait + add b32 $r7 0x10 + cmpu b32 $r7 $r3 + bra ne #crypt_do_out_loop + ret + +crypt_do_inout: + add b32 $r3 $r5 + mov $r9 #swap + sethi $r9 0x20000 + crypt_do_inout_loop: + mov $xdbase $r4 + xdld $r5 $r9 + xdwait + cxset 0x21 + xdst $r0 $r9 + cs0exec 1 + cxset 0x61 + mov $xdbase $r6 + xdld $r7 $r9 + xdst $r7 $r9 + cxset 1 + xdwait + add b32 $r5 0x10 + add b32 $r7 0x10 + cmpu b32 $r5 $r3 + bra ne #crypt_do_inout_loop + ret + +.align 0x100 diff --git a/drivers/gpu/drm/nouveau/nv98_crypt.fuc.h b/drivers/gpu/drm/nouveau/nv98_crypt.fuc.h new file mode 100644 index 00000000000..38676c74e6e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv98_crypt.fuc.h @@ -0,0 +1,584 @@ +uint32_t nv98_pcrypt_data[] = { +/* 0x0000: ctx_dma */ +/* 0x0000: ctx_dma_query */ + 0x00000000, +/* 0x0004: ctx_dma_src */ + 0x00000000, +/* 0x0008: ctx_dma_dst */ + 0x00000000, +/* 0x000c: ctx_query_address_high */ + 0x00000000, +/* 0x0010: ctx_query_address_low */ + 0x00000000, +/* 0x0014: ctx_query_counter */ + 0x00000000, +/* 0x0018: ctx_cond_address_high */ + 0x00000000, +/* 0x001c: ctx_cond_address_low */ + 0x00000000, +/* 0x0020: ctx_cond_off */ + 0x00000000, +/* 0x0024: ctx_src_address_high */ + 0x00000000, +/* 0x0028: ctx_src_address_low */ + 0x00000000, +/* 0x002c: ctx_dst_address_high */ + 0x00000000, +/* 0x0030: ctx_dst_address_low */ + 0x00000000, +/* 0x0034: ctx_mode */ + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0040: ctx_key */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0050: ctx_iv */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0080: swap */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x00a0: common_cmd_dtable */ + 0x0002000c, + 0xffffff00, + 0x00020010, + 0x0000000f, + 0x00020014, + 0x00000000, + 0x00000192, + 0xfffffffe, + 0x00020018, + 0xffffff00, + 0x0002001c, + 0x0000000f, + 0x000001d7, + 0xfffffff8, + 0x00000260, + 0xffffffff, +/* 0x00e0: engine_cmd_dtable */ + 0x00020040, + 0x00000000, + 0x00020044, + 0x00000000, + 0x00020048, + 0x00000000, + 0x0002004c, + 0x00000000, + 0x00020050, + 0x00000000, + 0x00020054, + 0x00000000, + 0x00020058, + 0x00000000, + 0x0002005c, + 0x00000000, + 0x00020024, + 0xffffff00, + 0x00020028, + 0x0000000f, + 0x0002002c, + 0xffffff00, + 0x00020030, + 0x0000000f, + 0x00000271, + 0xfffffff0, + 0x00010285, + 0xf000000f, +/* 0x0150: crypt_dtable */ + 0x04db0321, + 0x04b1032f, + 0x04db0339, + 0x04db034b, + 0x04db0361, + 0x04db0377, + 0x04db0395, + 0x04db03af, + 0x04db03cd, + 0x04db03e3, + 0x04db03f9, + 0x04db040f, + 0x04830429, + 0x0483043b, + 0x0483045d, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +uint32_t nv98_pcrypt_code[] = { + 0x17f004bd, + 0x0010fe35, + 0xf10004fe, + 0xf0fff017, + 0x27f10013, + 0x21d00400, + 0x0c15f0c0, + 0xf00021d0, + 0x27f10317, + 0x21d01200, + 0x1031f400, +/* 0x002f: spin */ + 0xf40031f4, + 0x0ef40028, +/* 0x0035: ih */ + 0x8001cffd, + 0xb00812c4, + 0x0bf40024, + 0x0027f167, + 0x002bfe77, + 0xf00007fe, + 0x23f00027, + 0x0037f105, + 0x0034cf14, + 0xb0014594, + 0x18f40055, + 0x0602fa17, + 0x4af003f8, + 0x0034d01e, + 0xd00147f0, + 0x0ef48034, +/* 0x0075: ctxload */ + 0x4034cf33, + 0xb0014f94, + 0x18f400f5, + 0x0502fa21, + 0x57f003f8, + 0x0267f000, +/* 0x008c: ctxload_dma_loop */ + 0xa07856bc, + 0xb6018068, + 0x87d00884, + 0x0162b600, +/* 0x009f: dummyload */ + 0xf0f018f4, + 0x35d00257, +/* 0x00a5: noctx */ + 0x0412c480, + 0xf50024b0, + 0xf100df0b, + 0xcf190037, + 0x33cf4032, + 0xff24e400, + 0x1024b607, + 0x07bf45e4, + 0xf50054b0, + 0xf100b90b, + 0xf1fae057, + 0xb000ce67, + 0x18f4c044, + 0xa057f14d, + 0x8867f1fc, + 0x8044b000, + 0xb03f18f4, + 0x18f46044, + 0x5044b019, + 0xf1741bf4, + 0xbd220027, + 0x0233f034, + 0xf50023d0, +/* 0x0103: dma_cmd */ + 0xb000810e, + 0x18f46344, + 0x0245945e, + 0xfe8050b7, + 0x801e39f0, + 0x40b70053, + 0x44b60120, + 0x0043d008, +/* 0x0123: dtable_cmd */ + 0xb8600ef4, + 0x18f40446, + 0x0344b63e, + 0x980045bb, + 0x53fd0145, + 0x0054b004, + 0x58291bf4, + 0x46580045, + 0x0264b001, + 0x98170bf4, + 0x67fd0807, + 0x0164b004, + 0xf9300bf4, + 0x0f01f455, +/* 0x015b: cmd_setctx */ + 0x80280ef4, + 0x0ef40053, +/* 0x0161: invalid_bitfield */ + 0x0125f022, +/* 0x0164: dispatch_error */ +/* 0x0164: illegal_mthd */ + 0x100047f1, + 0xd00042d0, + 0x47f04043, + 0x0004d040, +/* 0x0174: im_loop */ + 0xf08004cf, + 0x44b04044, + 0xf71bf400, +/* 0x0180: cmddone */ + 0x1d0037f1, + 0xd00147f0, +/* 0x018a: nocmd */ + 0x11c40034, + 0x4001d00c, +/* 0x0192: cmd_query_get */ + 0x38f201f8, + 0x0325f001, + 0x0b0047f1, +/* 0x019c: ptimer_retry */ + 0xcf4046cf, + 0x47cf0045, + 0x0467b840, + 0x98f41bf4, + 0x04800504, + 0x21008020, + 0x80220580, + 0x0bfe2306, + 0x03049800, + 0xfe1844b6, + 0x04980047, + 0x8057f104, + 0x0253f000, + 0xf80645fa, +/* 0x01d7: cmd_cond_mode */ + 0xf400f803, + 0x25f00131, + 0x0534b002, + 0xf41218f4, + 0x34b00132, + 0x0b18f402, + 0x800136f0, +/* 0x01f2: return */ + 0x00f80803, +/* 0x01f4: cmd_cond_mode_queryful */ + 0x98060498, + 0x56c40705, + 0x0855b6ff, + 0xfd1844b6, + 0x47fe0545, + 0x000bfe00, + 0x008057f1, + 0xfa0253f0, + 0x34b00565, + 0x131bf402, + 0x049803f8, + 0x0044b021, + 0x800b4cf0, + 0x00f80804, +/* 0x022c: cmd_cond_mode_double */ + 0xb61060b6, + 0x65fa1050, + 0x9803f805, + 0x06982005, + 0x0456b824, + 0x980b4cf0, + 0x06982105, + 0x0456b825, + 0xfd0b5cf0, + 0x34b00445, + 0x0b5cf003, + 0x800645fd, + 0x00f80804, +/* 0x0260: cmd_wrcache_flush */ + 0xf10132f4, + 0xbd220027, + 0x0133f034, + 0xf80023d0, +/* 0x0271: crypt_cmd_mode */ + 0x0131f400, + 0xb00225f0, + 0x18f40f34, + 0x0132f409, +/* 0x0283: crypt_cmd_mode_return */ + 0xf80d0380, +/* 0x0285: crypt_cmd_length */ + 0x0034b000, + 0xf4fb0bf4, + 0x47f0033c, + 0x0743f040, + 0xf00604fa, + 0x43f05047, + 0x0604fa06, + 0x3cf503f8, + 0x47f1c407, + 0x4bfe2100, + 0x09049800, + 0x950a0598, + 0x44b60858, + 0x0548fd18, + 0x98ff55c4, + 0x07980b06, + 0x0878950c, + 0xfd1864b6, + 0x77c40568, + 0x0d0898ff, + 0x580284b6, + 0x95f9a889, + 0xf9a98958, + 0x013cf495, + 0x3cf403f8, + 0xf803f861, + 0x18489503, + 0xbb084994, + 0x81b60095, + 0x09088000, + 0x950a0980, + 0x69941868, + 0x0097bb08, + 0x800081b6, + 0x09800b08, + 0x023cf40c, + 0xf05047f0, + 0x04fa0643, + 0xf803f805, +/* 0x0321: crypt_copy_prep */ + 0x203cf500, + 0x003cf594, + 0x003cf588, +/* 0x032f: crypt_store_prep */ + 0xf500f88c, + 0xf594103c, + 0xf88c063c, +/* 0x0339: crypt_ecb_e_prep */ + 0x303cf500, + 0x003cf594, + 0x003cf588, + 0x003cf5d0, +/* 0x034b: crypt_ecb_d_prep */ + 0xf500f88c, + 0xf5c8773c, + 0xf594303c, + 0xf588003c, + 0xf5d4003c, + 0xf88c003c, +/* 0x0361: crypt_cbc_e_prep */ + 0x403cf500, + 0x003cf594, + 0x063cf588, + 0x663cf5ac, + 0x063cf5d0, +/* 0x0377: crypt_cbc_d_prep */ + 0xf500f88c, + 0xf5c8773c, + 0xf594503c, + 0xf584623c, + 0xf588063c, + 0xf5d4603c, + 0xf5ac203c, + 0xf88c003c, +/* 0x0395: crypt_pcbc_e_prep */ + 0x503cf500, + 0x003cf594, + 0x063cf588, + 0x663cf5ac, + 0x063cf5d0, + 0x063cf58c, +/* 0x03af: crypt_pcbc_d_prep */ + 0xf500f8ac, + 0xf5c8773c, + 0xf594503c, + 0xf588003c, + 0xf5d4013c, + 0xf5ac163c, + 0xf58c063c, + 0xf8ac063c, +/* 0x03cd: crypt_cfb_e_prep */ + 0x403cf500, + 0x663cf594, + 0x003cf5d0, + 0x063cf588, + 0x063cf5ac, +/* 0x03e3: crypt_cfb_d_prep */ + 0xf500f88c, + 0xf594403c, + 0xf5d0603c, + 0xf588063c, + 0xf5ac603c, + 0xf88c003c, +/* 0x03f9: crypt_ofb_prep */ + 0x403cf500, + 0x663cf594, + 0x003cf5d0, + 0x603cf588, + 0x003cf5ac, +/* 0x040f: crypt_ctr_prep */ + 0xf500f88c, + 0xf594503c, + 0xf5d0613c, + 0xf5b0163c, + 0xf588003c, + 0xf5ac103c, + 0xf88c003c, +/* 0x0429: crypt_cbc_mac_prep */ + 0x303cf500, + 0x003cf594, + 0x063cf588, + 0x663cf5ac, +/* 0x043b: crypt_cmac_finish_complete_prep */ + 0xf500f8d0, + 0xf594703c, + 0xf588003c, + 0xf5ac063c, + 0xf5ac003c, + 0xf5d0003c, + 0xf5bc003c, + 0xf5ac063c, + 0xf8d0663c, +/* 0x045d: crypt_cmac_finish_partial_prep */ + 0x803cf500, + 0x003cf594, + 0x063cf588, + 0x003cf5ac, + 0x003cf5ac, + 0x003cf5d0, + 0x003cf5bc, + 0x063cf5bc, + 0x663cf5ac, +/* 0x0483: crypt_do_in */ + 0xbb00f8d0, + 0x47fe0035, + 0x8097f100, + 0x0293f000, +/* 0x0490: crypt_do_in_loop */ + 0xf80559fa, + 0x223cf403, + 0xf50609fa, + 0xf898103c, + 0x1050b603, + 0xf40453b8, + 0x3cf4e91b, + 0xf803f801, +/* 0x04b1: crypt_do_out */ + 0x0037bb00, + 0xf10067fe, + 0xf0008097, +/* 0x04be: crypt_do_out_loop */ + 0x3cf50293, + 0x3cf49810, + 0x0579fa61, + 0xf40679fa, + 0x03f8013c, + 0xb81070b6, + 0x1bf40473, +/* 0x04db: crypt_do_inout */ + 0xbb00f8e8, + 0x97f10035, + 0x93f00080, +/* 0x04e5: crypt_do_inout_loop */ + 0x0047fe02, + 0xf80559fa, + 0x213cf403, + 0xf50609fa, + 0xf498103c, + 0x67fe613c, + 0x0579fa00, + 0xf40679fa, + 0x03f8013c, + 0xb61050b6, + 0x53b81070, + 0xd41bf404, + 0x000000f8, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/drivers/gpu/drm/nouveau/nva3_copy.c b/drivers/gpu/drm/nouveau/nva3_copy.c index 8f356d58e40..0387dc7f4f4 100644 --- a/drivers/gpu/drm/nouveau/nva3_copy.c +++ b/drivers/gpu/drm/nouveau/nva3_copy.c @@ -79,29 +79,13 @@ static void nva3_copy_context_del(struct nouveau_channel *chan, int engine) { struct nouveau_gpuobj *ctx = chan->engctx[engine]; - struct drm_device *dev = chan->dev; - u32 inst; - - inst = (chan->ramin->vinst >> 12); - inst |= 0x40000000; - - /* disable fifo access */ - nv_wr32(dev, 0x104048, 0x00000000); - /* mark channel as unloaded if it's currently active */ - if (nv_rd32(dev, 0x104050) == inst) - nv_mask(dev, 0x104050, 0x40000000, 0x00000000); - /* mark next channel as invalid if it's about to be loaded */ - if (nv_rd32(dev, 0x104054) == inst) - nv_mask(dev, 0x104054, 0x40000000, 0x00000000); - /* restore fifo access */ - nv_wr32(dev, 0x104048, 0x00000003); + int i; - for (inst = 0xc0; inst <= 0xd4; inst += 4) - nv_wo32(chan->ramin, inst, 0x00000000); - - nouveau_gpuobj_ref(NULL, &ctx); + for (i = 0xc0; i <= 0xd4; i += 4) + nv_wo32(chan->ramin, i, 0x00000000); atomic_dec(&chan->vm->engref[engine]); + nouveau_gpuobj_ref(NULL, &ctx); chan->engctx[engine] = ctx; } @@ -143,13 +127,6 @@ static int nva3_copy_fini(struct drm_device *dev, int engine, bool suspend) { nv_mask(dev, 0x104048, 0x00000003, 0x00000000); - - /* trigger fuc context unload */ - nv_wait(dev, 0x104008, 0x0000000c, 0x00000000); - nv_mask(dev, 0x104054, 0x40000000, 0x00000000); - nv_wr32(dev, 0x104000, 0x00000008); - nv_wait(dev, 0x104008, 0x00000008, 0x00000000); - nv_wr32(dev, 0x104014, 0xffffffff); return 0; } diff --git a/drivers/gpu/drm/nouveau/nva3_pm.c b/drivers/gpu/drm/nouveau/nva3_pm.c index 9e636e6ef6d..798829353fb 100644 --- a/drivers/gpu/drm/nouveau/nva3_pm.c +++ b/drivers/gpu/drm/nouveau/nva3_pm.c @@ -98,7 +98,9 @@ read_pll(struct drm_device *dev, int clk, u32 pll) sclk = read_clk(dev, 0x10 + clk, false); } - return sclk * N / (M * P); + if (M * P) + return sclk * N / (M * P); + return 0; } struct creg { @@ -182,23 +184,26 @@ prog_pll(struct drm_device *dev, int clk, u32 pll, struct creg *reg) const u32 src1 = 0x004160 + (clk * 4); const u32 ctrl = pll + 0; const u32 coef = pll + 4; - u32 cntl; if (!reg->clk && !reg->pll) { NV_DEBUG(dev, "no clock for %02x\n", clk); return; } - cntl = nv_rd32(dev, ctrl) & 0xfffffff2; if (reg->pll) { nv_mask(dev, src0, 0x00000101, 0x00000101); nv_wr32(dev, coef, reg->pll); - nv_wr32(dev, ctrl, cntl | 0x00000015); + nv_mask(dev, ctrl, 0x00000015, 0x00000015); + nv_mask(dev, ctrl, 0x00000010, 0x00000000); + nv_wait(dev, ctrl, 0x00020000, 0x00020000); + nv_mask(dev, ctrl, 0x00000010, 0x00000010); + nv_mask(dev, ctrl, 0x00000008, 0x00000000); nv_mask(dev, src1, 0x00000100, 0x00000000); nv_mask(dev, src1, 0x00000001, 0x00000000); } else { nv_mask(dev, src1, 0x003f3141, 0x00000101 | reg->clk); - nv_wr32(dev, ctrl, cntl | 0x0000001d); + nv_mask(dev, ctrl, 0x00000018, 0x00000018); + udelay(20); nv_mask(dev, ctrl, 0x00000001, 0x00000000); nv_mask(dev, src0, 0x00000100, 0x00000000); nv_mask(dev, src0, 0x00000001, 0x00000000); @@ -230,17 +235,28 @@ nva3_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) } struct nva3_pm_state { + struct nouveau_pm_level *perflvl; + struct creg nclk; struct creg sclk; - struct creg mclk; struct creg vdec; struct creg unka0; + + struct creg mclk; + u8 *rammap; + u8 rammap_ver; + u8 rammap_len; + u8 *ramcfg; + u8 ramcfg_len; + u32 r004018; + u32 r100760; }; void * nva3_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) { struct nva3_pm_state *info; + u8 ramcfg_cnt; int ret; info = kzalloc(sizeof(*info), GFP_KERNEL); @@ -267,6 +283,20 @@ nva3_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) if (ret < 0) goto out; + info->rammap = nouveau_perf_rammap(dev, perflvl->memory, + &info->rammap_ver, + &info->rammap_len, + &ramcfg_cnt, &info->ramcfg_len); + if (info->rammap_ver != 0x10 || info->rammap_len < 5) + info->rammap = NULL; + + info->ramcfg = nouveau_perf_ramcfg(dev, perflvl->memory, + &info->rammap_ver, + &info->ramcfg_len); + if (info->rammap_ver != 0x10) + info->ramcfg = NULL; + + info->perflvl = perflvl; out: if (ret < 0) { kfree(info); @@ -287,6 +317,240 @@ nva3_pm_grcp_idle(void *data) return false; } +static void +mclk_precharge(struct nouveau_mem_exec_func *exec) +{ + nv_wr32(exec->dev, 0x1002d4, 0x00000001); +} + +static void +mclk_refresh(struct nouveau_mem_exec_func *exec) +{ + nv_wr32(exec->dev, 0x1002d0, 0x00000001); +} + +static void +mclk_refresh_auto(struct nouveau_mem_exec_func *exec, bool enable) +{ + nv_wr32(exec->dev, 0x100210, enable ? 0x80000000 : 0x00000000); +} + +static void +mclk_refresh_self(struct nouveau_mem_exec_func *exec, bool enable) +{ + nv_wr32(exec->dev, 0x1002dc, enable ? 0x00000001 : 0x00000000); +} + +static void +mclk_wait(struct nouveau_mem_exec_func *exec, u32 nsec) +{ + volatile u32 post = nv_rd32(exec->dev, 0); (void)post; + udelay((nsec + 500) / 1000); +} + +static u32 +mclk_mrg(struct nouveau_mem_exec_func *exec, int mr) +{ + if (mr <= 1) + return nv_rd32(exec->dev, 0x1002c0 + ((mr - 0) * 4)); + if (mr <= 3) + return nv_rd32(exec->dev, 0x1002e0 + ((mr - 2) * 4)); + return 0; +} + +static void +mclk_mrs(struct nouveau_mem_exec_func *exec, int mr, u32 data) +{ + struct drm_nouveau_private *dev_priv = exec->dev->dev_private; + + if (mr <= 1) { + if (dev_priv->vram_rank_B) + nv_wr32(exec->dev, 0x1002c8 + ((mr - 0) * 4), data); + nv_wr32(exec->dev, 0x1002c0 + ((mr - 0) * 4), data); + } else + if (mr <= 3) { + if (dev_priv->vram_rank_B) + nv_wr32(exec->dev, 0x1002e8 + ((mr - 2) * 4), data); + nv_wr32(exec->dev, 0x1002e0 + ((mr - 2) * 4), data); + } +} + +static void +mclk_clock_set(struct nouveau_mem_exec_func *exec) +{ + struct drm_device *dev = exec->dev; + struct nva3_pm_state *info = exec->priv; + u32 ctrl; + + ctrl = nv_rd32(dev, 0x004000); + if (!(ctrl & 0x00000008) && info->mclk.pll) { + nv_wr32(dev, 0x004000, (ctrl |= 0x00000008)); + nv_mask(dev, 0x1110e0, 0x00088000, 0x00088000); + nv_wr32(dev, 0x004018, 0x00001000); + nv_wr32(dev, 0x004000, (ctrl &= ~0x00000001)); + nv_wr32(dev, 0x004004, info->mclk.pll); + nv_wr32(dev, 0x004000, (ctrl |= 0x00000001)); + udelay(64); + nv_wr32(dev, 0x004018, 0x00005000 | info->r004018); + udelay(20); + } else + if (!info->mclk.pll) { + nv_mask(dev, 0x004168, 0x003f3040, info->mclk.clk); + nv_wr32(dev, 0x004000, (ctrl |= 0x00000008)); + nv_mask(dev, 0x1110e0, 0x00088000, 0x00088000); + nv_wr32(dev, 0x004018, 0x0000d000 | info->r004018); + } + + if (info->rammap) { + if (info->ramcfg && (info->rammap[4] & 0x08)) { + u32 unk5a0 = (ROM16(info->ramcfg[5]) << 8) | + info->ramcfg[5]; + u32 unk5a4 = ROM16(info->ramcfg[7]); + u32 unk804 = (info->ramcfg[9] & 0xf0) << 16 | + (info->ramcfg[3] & 0x0f) << 16 | + (info->ramcfg[9] & 0x0f) | + 0x80000000; + nv_wr32(dev, 0x1005a0, unk5a0); + nv_wr32(dev, 0x1005a4, unk5a4); + nv_wr32(dev, 0x10f804, unk804); + nv_mask(dev, 0x10053c, 0x00001000, 0x00000000); + } else { + nv_mask(dev, 0x10053c, 0x00001000, 0x00001000); + nv_mask(dev, 0x10f804, 0x80000000, 0x00000000); + nv_mask(dev, 0x100760, 0x22222222, info->r100760); + nv_mask(dev, 0x1007a0, 0x22222222, info->r100760); + nv_mask(dev, 0x1007e0, 0x22222222, info->r100760); + } + } + + if (info->mclk.pll) { + nv_mask(dev, 0x1110e0, 0x00088000, 0x00011000); + nv_wr32(dev, 0x004000, (ctrl &= ~0x00000008)); + } +} + +static void +mclk_timing_set(struct nouveau_mem_exec_func *exec) +{ + struct drm_device *dev = exec->dev; + struct nva3_pm_state *info = exec->priv; + struct nouveau_pm_level *perflvl = info->perflvl; + int i; + + for (i = 0; i < 9; i++) + nv_wr32(dev, 0x100220 + (i * 4), perflvl->timing.reg[i]); + + if (info->ramcfg) { + u32 data = (info->ramcfg[2] & 0x08) ? 0x00000000 : 0x00001000; + nv_mask(dev, 0x100200, 0x00001000, data); + } + + if (info->ramcfg) { + u32 unk714 = nv_rd32(dev, 0x100714) & ~0xf0000010; + u32 unk718 = nv_rd32(dev, 0x100718) & ~0x00000100; + u32 unk71c = nv_rd32(dev, 0x10071c) & ~0x00000100; + if ( (info->ramcfg[2] & 0x20)) + unk714 |= 0xf0000000; + if (!(info->ramcfg[2] & 0x04)) + unk714 |= 0x00000010; + nv_wr32(dev, 0x100714, unk714); + + if (info->ramcfg[2] & 0x01) + unk71c |= 0x00000100; + nv_wr32(dev, 0x10071c, unk71c); + + if (info->ramcfg[2] & 0x02) + unk718 |= 0x00000100; + nv_wr32(dev, 0x100718, unk718); + + if (info->ramcfg[2] & 0x10) + nv_wr32(dev, 0x111100, 0x48000000); /*XXX*/ + } +} + +static void +prog_mem(struct drm_device *dev, struct nva3_pm_state *info) +{ + struct nouveau_mem_exec_func exec = { + .dev = dev, + .precharge = mclk_precharge, + .refresh = mclk_refresh, + .refresh_auto = mclk_refresh_auto, + .refresh_self = mclk_refresh_self, + .wait = mclk_wait, + .mrg = mclk_mrg, + .mrs = mclk_mrs, + .clock_set = mclk_clock_set, + .timing_set = mclk_timing_set, + .priv = info + }; + u32 ctrl; + + /* XXX: where the fuck does 750MHz come from? */ + if (info->perflvl->memory <= 750000) { + info->r004018 = 0x10000000; + info->r100760 = 0x22222222; + } + + ctrl = nv_rd32(dev, 0x004000); + if (ctrl & 0x00000008) { + if (info->mclk.pll) { + nv_mask(dev, 0x004128, 0x00000101, 0x00000101); + nv_wr32(dev, 0x004004, info->mclk.pll); + nv_wr32(dev, 0x004000, (ctrl |= 0x00000001)); + nv_wr32(dev, 0x004000, (ctrl &= 0xffffffef)); + nv_wait(dev, 0x004000, 0x00020000, 0x00020000); + nv_wr32(dev, 0x004000, (ctrl |= 0x00000010)); + nv_wr32(dev, 0x004018, 0x00005000 | info->r004018); + nv_wr32(dev, 0x004000, (ctrl |= 0x00000004)); + } + } else { + u32 ssel = 0x00000101; + if (info->mclk.clk) + ssel |= info->mclk.clk; + else + ssel |= 0x00080000; /* 324MHz, shouldn't matter... */ + nv_mask(dev, 0x004168, 0x003f3141, ctrl); + } + + if (info->ramcfg) { + if (info->ramcfg[2] & 0x10) { + nv_mask(dev, 0x111104, 0x00000600, 0x00000000); + } else { + nv_mask(dev, 0x111100, 0x40000000, 0x40000000); + nv_mask(dev, 0x111104, 0x00000180, 0x00000000); + } + } + if (info->rammap && !(info->rammap[4] & 0x02)) + nv_mask(dev, 0x100200, 0x00000800, 0x00000000); + nv_wr32(dev, 0x611200, 0x00003300); + if (!(info->ramcfg[2] & 0x10)) + nv_wr32(dev, 0x111100, 0x4c020000); /*XXX*/ + + nouveau_mem_exec(&exec, info->perflvl); + + nv_wr32(dev, 0x611200, 0x00003330); + if (info->rammap && (info->rammap[4] & 0x02)) + nv_mask(dev, 0x100200, 0x00000800, 0x00000800); + if (info->ramcfg) { + if (info->ramcfg[2] & 0x10) { + nv_mask(dev, 0x111104, 0x00000180, 0x00000180); + nv_mask(dev, 0x111100, 0x40000000, 0x00000000); + } else { + nv_mask(dev, 0x111104, 0x00000600, 0x00000600); + } + } + + if (info->mclk.pll) { + nv_mask(dev, 0x004168, 0x00000001, 0x00000000); + nv_mask(dev, 0x004168, 0x00000100, 0x00000000); + } else { + nv_mask(dev, 0x004000, 0x00000001, 0x00000000); + nv_mask(dev, 0x004128, 0x00000001, 0x00000000); + nv_mask(dev, 0x004128, 0x00000100, 0x00000000); + } +} + int nva3_pm_clocks_set(struct drm_device *dev, void *pre_state) { @@ -316,18 +580,8 @@ nva3_pm_clocks_set(struct drm_device *dev, void *pre_state) prog_clk(dev, 0x20, &info->unka0); prog_clk(dev, 0x21, &info->vdec); - if (info->mclk.clk || info->mclk.pll) { - nv_wr32(dev, 0x100210, 0); - nv_wr32(dev, 0x1002dc, 1); - nv_wr32(dev, 0x004018, 0x00001000); - prog_pll(dev, 0x02, 0x004000, &info->mclk); - if (nv_rd32(dev, 0x4000) & 0x00000008) - nv_wr32(dev, 0x004018, 0x1000d000); - else - nv_wr32(dev, 0x004018, 0x10005000); - nv_wr32(dev, 0x1002dc, 0); - nv_wr32(dev, 0x100210, 0x80000000); - } + if (info->mclk.clk || info->mclk.pll) + prog_mem(dev, info); ret = 0; diff --git a/drivers/gpu/drm/nouveau/nvc0_fbcon.c b/drivers/gpu/drm/nouveau/nvc0_fbcon.c index a495e48197c..797159e7b7a 100644 --- a/drivers/gpu/drm/nouveau/nvc0_fbcon.c +++ b/drivers/gpu/drm/nouveau/nvc0_fbcon.c @@ -43,22 +43,22 @@ nvc0_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect) return ret; if (rect->rop != ROP_COPY) { - BEGIN_NVC0(chan, 2, NvSub2D, 0x02ac, 1); + BEGIN_NVC0(chan, NvSub2D, 0x02ac, 1); OUT_RING (chan, 1); } - BEGIN_NVC0(chan, 2, NvSub2D, 0x0588, 1); + BEGIN_NVC0(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_NVC0(chan, 2, NvSub2D, 0x0600, 4); + BEGIN_NVC0(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_NVC0(chan, 2, NvSub2D, 0x02ac, 1); + BEGIN_NVC0(chan, NvSub2D, 0x02ac, 1); OUT_RING (chan, 3); } FIRE_RING(chan); @@ -78,14 +78,14 @@ nvc0_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region) if (ret) return ret; - BEGIN_NVC0(chan, 2, NvSub2D, 0x0110, 1); + BEGIN_NVC0(chan, NvSub2D, 0x0110, 1); OUT_RING (chan, 0); - BEGIN_NVC0(chan, 2, NvSub2D, 0x08b0, 4); + BEGIN_NVC0(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_NVC0(chan, 2, NvSub2D, 0x08d0, 4); + BEGIN_NVC0(chan, NvSub2D, 0x08d0, 4); OUT_RING (chan, 0); OUT_RING (chan, region->sx); OUT_RING (chan, 0); @@ -116,7 +116,7 @@ nvc0_fbcon_imageblit(struct fb_info *info, const struct fb_image *image) width = ALIGN(image->width, 32); dwords = (width * image->height) >> 5; - BEGIN_NVC0(chan, 2, NvSub2D, 0x0814, 2); + BEGIN_NVC0(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); @@ -125,10 +125,10 @@ nvc0_fbcon_imageblit(struct fb_info *info, const struct fb_image *image) OUT_RING (chan, image->bg_color); OUT_RING (chan, image->fg_color); } - BEGIN_NVC0(chan, 2, NvSub2D, 0x0838, 2); + BEGIN_NVC0(chan, NvSub2D, 0x0838, 2); OUT_RING (chan, image->width); OUT_RING (chan, image->height); - BEGIN_NVC0(chan, 2, NvSub2D, 0x0850, 4); + BEGIN_NVC0(chan, NvSub2D, 0x0850, 4); OUT_RING (chan, 0); OUT_RING (chan, image->dx); OUT_RING (chan, 0); @@ -143,7 +143,7 @@ nvc0_fbcon_imageblit(struct fb_info *info, const struct fb_image *image) dwords -= push; - BEGIN_NVC0(chan, 6, NvSub2D, 0x0860, push); + BEGIN_NIC0(chan, NvSub2D, 0x0860, push); OUT_RINGp(chan, data, push); data += push; } @@ -200,47 +200,47 @@ nvc0_fbcon_accel_init(struct fb_info *info) return ret; } - BEGIN_NVC0(chan, 2, NvSub2D, 0x0000, 1); + BEGIN_NVC0(chan, NvSub2D, 0x0000, 1); OUT_RING (chan, 0x0000902d); - BEGIN_NVC0(chan, 2, NvSub2D, 0x0104, 2); + BEGIN_NVC0(chan, NvSub2D, 0x0104, 2); OUT_RING (chan, upper_32_bits(chan->notifier_vma.offset)); OUT_RING (chan, lower_32_bits(chan->notifier_vma.offset)); - BEGIN_NVC0(chan, 2, NvSub2D, 0x0290, 1); + BEGIN_NVC0(chan, NvSub2D, 0x0290, 1); OUT_RING (chan, 0); - BEGIN_NVC0(chan, 2, NvSub2D, 0x0888, 1); + BEGIN_NVC0(chan, NvSub2D, 0x0888, 1); OUT_RING (chan, 1); - BEGIN_NVC0(chan, 2, NvSub2D, 0x02ac, 1); + BEGIN_NVC0(chan, NvSub2D, 0x02ac, 1); OUT_RING (chan, 3); - BEGIN_NVC0(chan, 2, NvSub2D, 0x02a0, 1); + BEGIN_NVC0(chan, NvSub2D, 0x02a0, 1); OUT_RING (chan, 0x55); - BEGIN_NVC0(chan, 2, NvSub2D, 0x08c0, 4); + BEGIN_NVC0(chan, NvSub2D, 0x08c0, 4); OUT_RING (chan, 0); OUT_RING (chan, 1); OUT_RING (chan, 0); OUT_RING (chan, 1); - BEGIN_NVC0(chan, 2, NvSub2D, 0x0580, 2); + BEGIN_NVC0(chan, NvSub2D, 0x0580, 2); OUT_RING (chan, 4); OUT_RING (chan, format); - BEGIN_NVC0(chan, 2, NvSub2D, 0x02e8, 2); + BEGIN_NVC0(chan, NvSub2D, 0x02e8, 2); OUT_RING (chan, 2); OUT_RING (chan, 1); - BEGIN_NVC0(chan, 2, NvSub2D, 0x0804, 1); + BEGIN_NVC0(chan, NvSub2D, 0x0804, 1); OUT_RING (chan, format); - BEGIN_NVC0(chan, 2, NvSub2D, 0x0800, 1); + BEGIN_NVC0(chan, NvSub2D, 0x0800, 1); OUT_RING (chan, 1); - BEGIN_NVC0(chan, 2, NvSub2D, 0x0808, 3); + BEGIN_NVC0(chan, NvSub2D, 0x0808, 3); OUT_RING (chan, 0); OUT_RING (chan, 0); OUT_RING (chan, 1); - BEGIN_NVC0(chan, 2, NvSub2D, 0x081c, 1); + BEGIN_NVC0(chan, NvSub2D, 0x081c, 1); OUT_RING (chan, 1); - BEGIN_NVC0(chan, 2, NvSub2D, 0x0840, 4); + BEGIN_NVC0(chan, NvSub2D, 0x0840, 4); OUT_RING (chan, 0); OUT_RING (chan, 1); OUT_RING (chan, 0); OUT_RING (chan, 1); - BEGIN_NVC0(chan, 2, NvSub2D, 0x0200, 10); + BEGIN_NVC0(chan, NvSub2D, 0x0200, 10); OUT_RING (chan, format); OUT_RING (chan, 1); OUT_RING (chan, 0); @@ -251,7 +251,7 @@ nvc0_fbcon_accel_init(struct fb_info *info) OUT_RING (chan, info->var.yres_virtual); OUT_RING (chan, upper_32_bits(fb->vma.offset)); OUT_RING (chan, lower_32_bits(fb->vma.offset)); - BEGIN_NVC0(chan, 2, NvSub2D, 0x0230, 10); + BEGIN_NVC0(chan, NvSub2D, 0x0230, 10); OUT_RING (chan, format); OUT_RING (chan, 1); OUT_RING (chan, 0); diff --git a/drivers/gpu/drm/nouveau/nvc0_fence.c b/drivers/gpu/drm/nouveau/nvc0_fence.c new file mode 100644 index 00000000000..47ab388a606 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvc0_fence.c @@ -0,0 +1,184 @@ +/* + * Copyright 2012 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_dma.h" +#include "nouveau_fifo.h" +#include "nouveau_ramht.h" +#include "nouveau_fence.h" + +struct nvc0_fence_priv { + struct nouveau_fence_priv base; + struct nouveau_bo *bo; +}; + +struct nvc0_fence_chan { + struct nouveau_fence_chan base; + struct nouveau_vma vma; +}; + +static int +nvc0_fence_emit(struct nouveau_fence *fence) +{ + struct nouveau_channel *chan = fence->channel; + struct nvc0_fence_chan *fctx = chan->engctx[NVOBJ_ENGINE_FENCE]; + u64 addr = fctx->vma.offset + chan->id * 16; + int ret; + + ret = RING_SPACE(chan, 5); + if (ret == 0) { + BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); + OUT_RING (chan, upper_32_bits(addr)); + OUT_RING (chan, lower_32_bits(addr)); + OUT_RING (chan, fence->sequence); + OUT_RING (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_WRITE_LONG); + FIRE_RING (chan); + } + + return ret; +} + +static int +nvc0_fence_sync(struct nouveau_fence *fence, + struct nouveau_channel *prev, struct nouveau_channel *chan) +{ + struct nvc0_fence_chan *fctx = chan->engctx[NVOBJ_ENGINE_FENCE]; + u64 addr = fctx->vma.offset + prev->id * 16; + int ret; + + ret = RING_SPACE(chan, 5); + if (ret == 0) { + BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); + OUT_RING (chan, upper_32_bits(addr)); + OUT_RING (chan, lower_32_bits(addr)); + OUT_RING (chan, fence->sequence); + OUT_RING (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_GEQUAL | + NVC0_SUBCHAN_SEMAPHORE_TRIGGER_YIELD); + FIRE_RING (chan); + } + + return ret; +} + +static u32 +nvc0_fence_read(struct nouveau_channel *chan) +{ + struct nvc0_fence_priv *priv = nv_engine(chan->dev, NVOBJ_ENGINE_FENCE); + return nouveau_bo_rd32(priv->bo, chan->id * 16/4); +} + +static void +nvc0_fence_context_del(struct nouveau_channel *chan, int engine) +{ + struct nvc0_fence_priv *priv = nv_engine(chan->dev, engine); + struct nvc0_fence_chan *fctx = chan->engctx[engine]; + + nouveau_bo_vma_del(priv->bo, &fctx->vma); + nouveau_fence_context_del(&fctx->base); + chan->engctx[engine] = NULL; + kfree(fctx); +} + +static int +nvc0_fence_context_new(struct nouveau_channel *chan, int engine) +{ + struct nvc0_fence_priv *priv = nv_engine(chan->dev, engine); + struct nvc0_fence_chan *fctx; + int ret; + + fctx = chan->engctx[engine] = kzalloc(sizeof(*fctx), GFP_KERNEL); + if (!fctx) + return -ENOMEM; + + nouveau_fence_context_new(&fctx->base); + + ret = nouveau_bo_vma_add(priv->bo, chan->vm, &fctx->vma); + if (ret) + nvc0_fence_context_del(chan, engine); + + nouveau_bo_wr32(priv->bo, chan->id * 16/4, 0x00000000); + return ret; +} + +static int +nvc0_fence_fini(struct drm_device *dev, int engine, bool suspend) +{ + return 0; +} + +static int +nvc0_fence_init(struct drm_device *dev, int engine) +{ + return 0; +} + +static void +nvc0_fence_destroy(struct drm_device *dev, int engine) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nvc0_fence_priv *priv = nv_engine(dev, engine); + + nouveau_bo_unmap(priv->bo); + nouveau_bo_ref(NULL, &priv->bo); + dev_priv->eng[engine] = NULL; + kfree(priv); +} + +int +nvc0_fence_create(struct drm_device *dev) +{ + struct nouveau_fifo_priv *pfifo = nv_engine(dev, NVOBJ_ENGINE_FIFO); + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nvc0_fence_priv *priv; + int ret; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base.engine.destroy = nvc0_fence_destroy; + priv->base.engine.init = nvc0_fence_init; + priv->base.engine.fini = nvc0_fence_fini; + priv->base.engine.context_new = nvc0_fence_context_new; + priv->base.engine.context_del = nvc0_fence_context_del; + priv->base.emit = nvc0_fence_emit; + priv->base.sync = nvc0_fence_sync; + priv->base.read = nvc0_fence_read; + dev_priv->eng[NVOBJ_ENGINE_FENCE] = &priv->base.engine; + + ret = nouveau_bo_new(dev, 16 * pfifo->channels, 0, TTM_PL_FLAG_VRAM, + 0, 0, NULL, &priv->bo); + if (ret == 0) { + ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM); + if (ret == 0) + ret = nouveau_bo_map(priv->bo); + if (ret) + nouveau_bo_ref(NULL, &priv->bo); + } + + if (ret) + nvc0_fence_destroy(dev, NVOBJ_ENGINE_FENCE); + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvc0_fifo.c b/drivers/gpu/drm/nouveau/nvc0_fifo.c index 50d68a7a137..7d85553d518 100644 --- a/drivers/gpu/drm/nouveau/nvc0_fifo.c +++ b/drivers/gpu/drm/nouveau/nvc0_fifo.c @@ -26,10 +26,12 @@ #include "nouveau_drv.h" #include "nouveau_mm.h" +#include "nouveau_fifo.h" static void nvc0_fifo_isr(struct drm_device *); struct nvc0_fifo_priv { + struct nouveau_fifo_priv base; struct nouveau_gpuobj *playlist[2]; int cur_playlist; struct nouveau_vma user_vma; @@ -37,8 +39,8 @@ struct nvc0_fifo_priv { }; struct nvc0_fifo_chan { + struct nouveau_fifo_chan base; struct nouveau_gpuobj *user; - struct nouveau_gpuobj *ramfc; }; static void @@ -46,8 +48,7 @@ nvc0_fifo_playlist_update(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem; - struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; - struct nvc0_fifo_priv *priv = pfifo->priv; + struct nvc0_fifo_priv *priv = nv_engine(dev, NVOBJ_ENGINE_FIFO); struct nouveau_gpuobj *cur; int i, p; @@ -69,59 +70,20 @@ nvc0_fifo_playlist_update(struct drm_device *dev) NV_ERROR(dev, "PFIFO - playlist update failed\n"); } -void -nvc0_fifo_disable(struct drm_device *dev) -{ -} - -void -nvc0_fifo_enable(struct drm_device *dev) -{ -} - -bool -nvc0_fifo_reassign(struct drm_device *dev, bool enable) -{ - return false; -} - -bool -nvc0_fifo_cache_pull(struct drm_device *dev, bool enable) -{ - return false; -} - -int -nvc0_fifo_channel_id(struct drm_device *dev) -{ - return 127; -} - -int -nvc0_fifo_create_context(struct nouveau_channel *chan) +static int +nvc0_fifo_context_new(struct nouveau_channel *chan, int engine) { struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem; - struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; - struct nvc0_fifo_priv *priv = pfifo->priv; - struct nvc0_fifo_chan *fifoch; + struct nvc0_fifo_priv *priv = nv_engine(dev, engine); + struct nvc0_fifo_chan *fctx; u64 ib_virt = chan->pushbuf_base + chan->dma.ib_base * 4; - int ret; + int ret, i; - chan->fifo_priv = kzalloc(sizeof(*fifoch), GFP_KERNEL); - if (!chan->fifo_priv) + fctx = chan->engctx[engine] = kzalloc(sizeof(*fctx), GFP_KERNEL); + if (!fctx) return -ENOMEM; - fifoch = chan->fifo_priv; - - /* allocate vram for control regs, map into polling area */ - ret = nouveau_gpuobj_new(dev, NULL, 0x1000, 0x1000, - NVOBJ_FLAG_ZERO_ALLOC, &fifoch->user); - if (ret) - goto error; - - nouveau_vm_map_at(&priv->user_vma, chan->id * 0x1000, - *(struct nouveau_mem **)fifoch->user->node); chan->user = ioremap_wc(pci_resource_start(dev->pdev, 1) + priv->user_vma.offset + (chan->id * 0x1000), @@ -131,176 +93,77 @@ nvc0_fifo_create_context(struct nouveau_channel *chan) goto error; } - /* ramfc */ - ret = nouveau_gpuobj_new_fake(dev, chan->ramin->pinst, - chan->ramin->vinst, 0x100, - NVOBJ_FLAG_ZERO_ALLOC, &fifoch->ramfc); + /* allocate vram for control regs, map into polling area */ + ret = nouveau_gpuobj_new(dev, NULL, 0x1000, 0x1000, + NVOBJ_FLAG_ZERO_ALLOC, &fctx->user); if (ret) goto error; - nv_wo32(fifoch->ramfc, 0x08, lower_32_bits(fifoch->user->vinst)); - nv_wo32(fifoch->ramfc, 0x0c, upper_32_bits(fifoch->user->vinst)); - nv_wo32(fifoch->ramfc, 0x10, 0x0000face); - nv_wo32(fifoch->ramfc, 0x30, 0xfffff902); - nv_wo32(fifoch->ramfc, 0x48, lower_32_bits(ib_virt)); - nv_wo32(fifoch->ramfc, 0x4c, drm_order(chan->dma.ib_max + 1) << 16 | + nouveau_vm_map_at(&priv->user_vma, chan->id * 0x1000, + *(struct nouveau_mem **)fctx->user->node); + + for (i = 0; i < 0x100; i += 4) + nv_wo32(chan->ramin, i, 0x00000000); + nv_wo32(chan->ramin, 0x08, lower_32_bits(fctx->user->vinst)); + nv_wo32(chan->ramin, 0x0c, upper_32_bits(fctx->user->vinst)); + nv_wo32(chan->ramin, 0x10, 0x0000face); + nv_wo32(chan->ramin, 0x30, 0xfffff902); + nv_wo32(chan->ramin, 0x48, lower_32_bits(ib_virt)); + nv_wo32(chan->ramin, 0x4c, drm_order(chan->dma.ib_max + 1) << 16 | upper_32_bits(ib_virt)); - nv_wo32(fifoch->ramfc, 0x54, 0x00000002); - nv_wo32(fifoch->ramfc, 0x84, 0x20400000); - nv_wo32(fifoch->ramfc, 0x94, 0x30000001); - nv_wo32(fifoch->ramfc, 0x9c, 0x00000100); - nv_wo32(fifoch->ramfc, 0xa4, 0x1f1f1f1f); - nv_wo32(fifoch->ramfc, 0xa8, 0x1f1f1f1f); - nv_wo32(fifoch->ramfc, 0xac, 0x0000001f); - nv_wo32(fifoch->ramfc, 0xb8, 0xf8000000); - nv_wo32(fifoch->ramfc, 0xf8, 0x10003080); /* 0x002310 */ - nv_wo32(fifoch->ramfc, 0xfc, 0x10000010); /* 0x002350 */ + nv_wo32(chan->ramin, 0x54, 0x00000002); + nv_wo32(chan->ramin, 0x84, 0x20400000); + nv_wo32(chan->ramin, 0x94, 0x30000001); + nv_wo32(chan->ramin, 0x9c, 0x00000100); + nv_wo32(chan->ramin, 0xa4, 0x1f1f1f1f); + nv_wo32(chan->ramin, 0xa8, 0x1f1f1f1f); + nv_wo32(chan->ramin, 0xac, 0x0000001f); + nv_wo32(chan->ramin, 0xb8, 0xf8000000); + nv_wo32(chan->ramin, 0xf8, 0x10003080); /* 0x002310 */ + nv_wo32(chan->ramin, 0xfc, 0x10000010); /* 0x002350 */ pinstmem->flush(dev); nv_wr32(dev, 0x003000 + (chan->id * 8), 0xc0000000 | (chan->ramin->vinst >> 12)); nv_wr32(dev, 0x003004 + (chan->id * 8), 0x001f0001); nvc0_fifo_playlist_update(dev); - return 0; error: - pfifo->destroy_context(chan); + if (ret) + priv->base.base.context_del(chan, engine); return ret; } -void -nvc0_fifo_destroy_context(struct nouveau_channel *chan) +static void +nvc0_fifo_context_del(struct nouveau_channel *chan, int engine) { + struct nvc0_fifo_chan *fctx = chan->engctx[engine]; struct drm_device *dev = chan->dev; - struct nvc0_fifo_chan *fifoch; nv_mask(dev, 0x003004 + (chan->id * 8), 0x00000001, 0x00000000); nv_wr32(dev, 0x002634, chan->id); if (!nv_wait(dev, 0x0002634, 0xffffffff, chan->id)) NV_WARN(dev, "0x2634 != chid: 0x%08x\n", nv_rd32(dev, 0x2634)); - nvc0_fifo_playlist_update(dev); - nv_wr32(dev, 0x003000 + (chan->id * 8), 0x00000000); + nouveau_gpuobj_ref(NULL, &fctx->user); if (chan->user) { iounmap(chan->user); chan->user = NULL; } - fifoch = chan->fifo_priv; - chan->fifo_priv = NULL; - if (!fifoch) - return; - - nouveau_gpuobj_ref(NULL, &fifoch->ramfc); - nouveau_gpuobj_ref(NULL, &fifoch->user); - kfree(fifoch); -} - -int -nvc0_fifo_load_context(struct nouveau_channel *chan) -{ - return 0; -} - -int -nvc0_fifo_unload_context(struct drm_device *dev) -{ - int i; - - for (i = 0; i < 128; i++) { - if (!(nv_rd32(dev, 0x003004 + (i * 8)) & 1)) - continue; - - nv_mask(dev, 0x003004 + (i * 8), 0x00000001, 0x00000000); - nv_wr32(dev, 0x002634, i); - if (!nv_wait(dev, 0x002634, 0xffffffff, i)) { - NV_INFO(dev, "PFIFO: kick ch %d failed: 0x%08x\n", - i, nv_rd32(dev, 0x002634)); - return -EBUSY; - } - } - - return 0; -} - -static void -nvc0_fifo_destroy(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; - struct nvc0_fifo_priv *priv; - - priv = pfifo->priv; - if (!priv) - return; - - nouveau_vm_put(&priv->user_vma); - nouveau_gpuobj_ref(NULL, &priv->playlist[1]); - nouveau_gpuobj_ref(NULL, &priv->playlist[0]); - kfree(priv); -} - -void -nvc0_fifo_takedown(struct drm_device *dev) -{ - nv_wr32(dev, 0x002140, 0x00000000); - nvc0_fifo_destroy(dev); + chan->engctx[engine] = NULL; + kfree(fctx); } static int -nvc0_fifo_create(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; - struct nvc0_fifo_priv *priv; - int ret; - - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - pfifo->priv = priv; - - ret = nouveau_gpuobj_new(dev, NULL, 0x1000, 0x1000, 0, - &priv->playlist[0]); - if (ret) - goto error; - - ret = nouveau_gpuobj_new(dev, NULL, 0x1000, 0x1000, 0, - &priv->playlist[1]); - if (ret) - goto error; - - ret = nouveau_vm_get(dev_priv->bar1_vm, pfifo->channels * 0x1000, - 12, NV_MEM_ACCESS_RW, &priv->user_vma); - if (ret) - goto error; - - nouveau_irq_register(dev, 8, nvc0_fifo_isr); - NVOBJ_CLASS(dev, 0x506e, SW); /* nvsw */ - return 0; - -error: - nvc0_fifo_destroy(dev); - return ret; -} - -int -nvc0_fifo_init(struct drm_device *dev) +nvc0_fifo_init(struct drm_device *dev, int engine) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; + struct nvc0_fifo_priv *priv = nv_engine(dev, engine); struct nouveau_channel *chan; - struct nvc0_fifo_priv *priv; - int ret, i; - - if (!pfifo->priv) { - ret = nvc0_fifo_create(dev); - if (ret) - return ret; - } - priv = pfifo->priv; + int i; /* reset PFIFO, enable all available PSUBFIFO areas */ nv_mask(dev, 0x000200, 0x00000100, 0x00000000); @@ -338,7 +201,7 @@ nvc0_fifo_init(struct drm_device *dev) /* restore PFIFO context table */ for (i = 0; i < 128; i++) { chan = dev_priv->channels.ptr[i]; - if (!chan || !chan->fifo_priv) + if (!chan || !chan->engctx[engine]) continue; nv_wr32(dev, 0x003000 + (i * 8), 0xc0000000 | @@ -350,6 +213,29 @@ nvc0_fifo_init(struct drm_device *dev) return 0; } +static int +nvc0_fifo_fini(struct drm_device *dev, int engine, bool suspend) +{ + int i; + + for (i = 0; i < 128; i++) { + if (!(nv_rd32(dev, 0x003004 + (i * 8)) & 1)) + continue; + + nv_mask(dev, 0x003004 + (i * 8), 0x00000001, 0x00000000); + nv_wr32(dev, 0x002634, i); + if (!nv_wait(dev, 0x002634, 0xffffffff, i)) { + NV_INFO(dev, "PFIFO: kick ch %d failed: 0x%08x\n", + i, nv_rd32(dev, 0x002634)); + return -EBUSY; + } + } + + nv_wr32(dev, 0x002140, 0x00000000); + return 0; +} + + struct nouveau_enum nvc0_fifo_fault_unit[] = { { 0x00, "PGRAPH" }, { 0x03, "PEEPHOLE" }, @@ -439,13 +325,14 @@ nvc0_fifo_isr_vm_fault(struct drm_device *dev, int unit) static int nvc0_fifo_page_flip(struct drm_device *dev, u32 chid) { + struct nvc0_fifo_priv *priv = nv_engine(dev, NVOBJ_ENGINE_FIFO); struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_channel *chan = NULL; unsigned long flags; int ret = -EINVAL; spin_lock_irqsave(&dev_priv->channels.lock, flags); - if (likely(chid >= 0 && chid < dev_priv->engine.fifo.channels)) { + if (likely(chid >= 0 && chid < priv->base.channels)) { chan = dev_priv->channels.ptr[chid]; if (likely(chan)) ret = nouveau_finish_page_flip(chan, NULL); @@ -534,3 +421,56 @@ nvc0_fifo_isr(struct drm_device *dev) nv_wr32(dev, 0x002140, 0); } } + +static void +nvc0_fifo_destroy(struct drm_device *dev, int engine) +{ + struct nvc0_fifo_priv *priv = nv_engine(dev, NVOBJ_ENGINE_FIFO); + struct drm_nouveau_private *dev_priv = dev->dev_private; + + nouveau_vm_put(&priv->user_vma); + nouveau_gpuobj_ref(NULL, &priv->playlist[1]); + nouveau_gpuobj_ref(NULL, &priv->playlist[0]); + + dev_priv->eng[engine] = NULL; + kfree(priv); +} + +int +nvc0_fifo_create(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nvc0_fifo_priv *priv; + int ret; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base.base.destroy = nvc0_fifo_destroy; + priv->base.base.init = nvc0_fifo_init; + priv->base.base.fini = nvc0_fifo_fini; + priv->base.base.context_new = nvc0_fifo_context_new; + priv->base.base.context_del = nvc0_fifo_context_del; + priv->base.channels = 128; + dev_priv->eng[NVOBJ_ENGINE_FIFO] = &priv->base.base; + + ret = nouveau_gpuobj_new(dev, NULL, 4096, 4096, 0, &priv->playlist[0]); + if (ret) + goto error; + + ret = nouveau_gpuobj_new(dev, NULL, 4096, 4096, 0, &priv->playlist[1]); + if (ret) + goto error; + + ret = nouveau_vm_get(dev_priv->bar1_vm, priv->base.channels * 0x1000, + 12, NV_MEM_ACCESS_RW, &priv->user_vma); + if (ret) + goto error; + + nouveau_irq_register(dev, 8, nvc0_fifo_isr); +error: + if (ret) + priv->base.base.destroy(dev, NVOBJ_ENGINE_FIFO); + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.c b/drivers/gpu/drm/nouveau/nvc0_graph.c index 9066102d115..2a01e6e4772 100644 --- a/drivers/gpu/drm/nouveau/nvc0_graph.c +++ b/drivers/gpu/drm/nouveau/nvc0_graph.c @@ -29,6 +29,7 @@ #include "nouveau_drv.h" #include "nouveau_mm.h" +#include "nouveau_fifo.h" #include "nvc0_graph.h" #include "nvc0_grhub.fuc.h" @@ -620,13 +621,14 @@ nvc0_graph_init(struct drm_device *dev, int engine) int nvc0_graph_isr_chid(struct drm_device *dev, u64 inst) { + struct nouveau_fifo_priv *pfifo = nv_engine(dev, NVOBJ_ENGINE_FIFO); struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_channel *chan; unsigned long flags; int i; spin_lock_irqsave(&dev_priv->channels.lock, flags); - for (i = 0; i < dev_priv->engine.fifo.channels; i++) { + for (i = 0; i < pfifo->channels; i++) { chan = dev_priv->channels.ptr[i]; if (!chan || !chan->ramin) continue; diff --git a/drivers/gpu/drm/nouveau/nvc0_pm.c b/drivers/gpu/drm/nouveau/nvc0_pm.c index ce65f81bb87..7c95c44e288 100644 --- a/drivers/gpu/drm/nouveau/nvc0_pm.c +++ b/drivers/gpu/drm/nouveau/nvc0_pm.c @@ -164,7 +164,9 @@ struct nvc0_pm_clock { }; struct nvc0_pm_state { + struct nouveau_pm_level *perflvl; struct nvc0_pm_clock eng[16]; + struct nvc0_pm_clock mem; }; static u32 @@ -303,6 +305,48 @@ calc_clk(struct drm_device *dev, int clk, struct nvc0_pm_clock *info, u32 freq) return 0; } +static int +calc_mem(struct drm_device *dev, struct nvc0_pm_clock *info, u32 freq) +{ + struct pll_lims pll; + int N, M, P, ret; + u32 ctrl; + + /* mclk pll input freq comes from another pll, make sure it's on */ + ctrl = nv_rd32(dev, 0x132020); + if (!(ctrl & 0x00000001)) { + /* if not, program it to 567MHz. nfi where this value comes + * from - it looks like it's in the pll limits table for + * 132000 but the binary driver ignores all my attempts to + * change this value. + */ + nv_wr32(dev, 0x137320, 0x00000103); + nv_wr32(dev, 0x137330, 0x81200606); + nv_wait(dev, 0x132020, 0x00010000, 0x00010000); + nv_wr32(dev, 0x132024, 0x0001150f); + nv_mask(dev, 0x132020, 0x00000001, 0x00000001); + nv_wait(dev, 0x137390, 0x00020000, 0x00020000); + nv_mask(dev, 0x132020, 0x00000004, 0x00000004); + } + + /* for the moment, until the clock tree is better understood, use + * pll mode for all clock frequencies + */ + ret = get_pll_limits(dev, 0x132000, &pll); + if (ret == 0) { + pll.refclk = read_pll(dev, 0x132020); + if (pll.refclk) { + ret = nva3_calc_pll(dev, &pll, freq, &N, NULL, &M, &P); + if (ret > 0) { + info->coef = (P << 16) | (N << 8) | M; + return 0; + } + } + } + + return -EINVAL; +} + void * nvc0_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) { @@ -335,6 +379,15 @@ nvc0_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) return ERR_PTR(ret); } + if (perflvl->memory) { + ret = calc_mem(dev, &info->mem, perflvl->memory); + if (ret) { + kfree(info); + return ERR_PTR(ret); + } + } + + info->perflvl = perflvl; return info; } @@ -375,12 +428,148 @@ prog_clk(struct drm_device *dev, int clk, struct nvc0_pm_clock *info) nv_mask(dev, 0x137250 + (clk * 0x04), 0x00003f3f, info->mdiv); } +static void +mclk_precharge(struct nouveau_mem_exec_func *exec) +{ +} + +static void +mclk_refresh(struct nouveau_mem_exec_func *exec) +{ +} + +static void +mclk_refresh_auto(struct nouveau_mem_exec_func *exec, bool enable) +{ + nv_wr32(exec->dev, 0x10f210, enable ? 0x80000000 : 0x00000000); +} + +static void +mclk_refresh_self(struct nouveau_mem_exec_func *exec, bool enable) +{ +} + +static void +mclk_wait(struct nouveau_mem_exec_func *exec, u32 nsec) +{ + udelay((nsec + 500) / 1000); +} + +static u32 +mclk_mrg(struct nouveau_mem_exec_func *exec, int mr) +{ + struct drm_device *dev = exec->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + if (dev_priv->vram_type != NV_MEM_TYPE_GDDR5) { + if (mr <= 1) + return nv_rd32(dev, 0x10f300 + ((mr - 0) * 4)); + return nv_rd32(dev, 0x10f320 + ((mr - 2) * 4)); + } else { + if (mr == 0) + return nv_rd32(dev, 0x10f300 + (mr * 4)); + else + if (mr <= 7) + return nv_rd32(dev, 0x10f32c + (mr * 4)); + return nv_rd32(dev, 0x10f34c); + } +} + +static void +mclk_mrs(struct nouveau_mem_exec_func *exec, int mr, u32 data) +{ + struct drm_device *dev = exec->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + if (dev_priv->vram_type != NV_MEM_TYPE_GDDR5) { + if (mr <= 1) { + nv_wr32(dev, 0x10f300 + ((mr - 0) * 4), data); + if (dev_priv->vram_rank_B) + nv_wr32(dev, 0x10f308 + ((mr - 0) * 4), data); + } else + if (mr <= 3) { + nv_wr32(dev, 0x10f320 + ((mr - 2) * 4), data); + if (dev_priv->vram_rank_B) + nv_wr32(dev, 0x10f328 + ((mr - 2) * 4), data); + } + } else { + if (mr == 0) nv_wr32(dev, 0x10f300 + (mr * 4), data); + else if (mr <= 7) nv_wr32(dev, 0x10f32c + (mr * 4), data); + else if (mr == 15) nv_wr32(dev, 0x10f34c, data); + } +} + +static void +mclk_clock_set(struct nouveau_mem_exec_func *exec) +{ + struct nvc0_pm_state *info = exec->priv; + struct drm_device *dev = exec->dev; + u32 ctrl = nv_rd32(dev, 0x132000); + + nv_wr32(dev, 0x137360, 0x00000001); + nv_wr32(dev, 0x137370, 0x00000000); + nv_wr32(dev, 0x137380, 0x00000000); + if (ctrl & 0x00000001) + nv_wr32(dev, 0x132000, (ctrl &= ~0x00000001)); + + nv_wr32(dev, 0x132004, info->mem.coef); + nv_wr32(dev, 0x132000, (ctrl |= 0x00000001)); + nv_wait(dev, 0x137390, 0x00000002, 0x00000002); + nv_wr32(dev, 0x132018, 0x00005000); + + nv_wr32(dev, 0x137370, 0x00000001); + nv_wr32(dev, 0x137380, 0x00000001); + nv_wr32(dev, 0x137360, 0x00000000); +} + +static void +mclk_timing_set(struct nouveau_mem_exec_func *exec) +{ + struct nvc0_pm_state *info = exec->priv; + struct nouveau_pm_level *perflvl = info->perflvl; + int i; + + for (i = 0; i < 5; i++) + nv_wr32(exec->dev, 0x10f290 + (i * 4), perflvl->timing.reg[i]); +} + +static void +prog_mem(struct drm_device *dev, struct nvc0_pm_state *info) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_mem_exec_func exec = { + .dev = dev, + .precharge = mclk_precharge, + .refresh = mclk_refresh, + .refresh_auto = mclk_refresh_auto, + .refresh_self = mclk_refresh_self, + .wait = mclk_wait, + .mrg = mclk_mrg, + .mrs = mclk_mrs, + .clock_set = mclk_clock_set, + .timing_set = mclk_timing_set, + .priv = info + }; + + if (dev_priv->chipset < 0xd0) + nv_wr32(dev, 0x611200, 0x00003300); + else + nv_wr32(dev, 0x62c000, 0x03030000); + + nouveau_mem_exec(&exec, info->perflvl); + + if (dev_priv->chipset < 0xd0) + nv_wr32(dev, 0x611200, 0x00003300); + else + nv_wr32(dev, 0x62c000, 0x03030300); +} int nvc0_pm_clocks_set(struct drm_device *dev, void *data) { struct nvc0_pm_state *info = data; int i; + if (info->mem.coef) + prog_mem(dev, info); + for (i = 0; i < 16; i++) { if (!info->eng[i].freq) continue; diff --git a/drivers/gpu/drm/nouveau/nvc0_software.c b/drivers/gpu/drm/nouveau/nvc0_software.c new file mode 100644 index 00000000000..93e8c164fec --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvc0_software.c @@ -0,0 +1,153 @@ +/* + * Copyright 2012 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_ramht.h" +#include "nouveau_software.h" + +#include "nv50_display.h" + +struct nvc0_software_priv { + struct nouveau_software_priv base; +}; + +struct nvc0_software_chan { + struct nouveau_software_chan base; + struct nouveau_vma dispc_vma[4]; +}; + +u64 +nvc0_software_crtc(struct nouveau_channel *chan, int crtc) +{ + struct nvc0_software_chan *pch = chan->engctx[NVOBJ_ENGINE_SW]; + return pch->dispc_vma[crtc].offset; +} + +static int +nvc0_software_context_new(struct nouveau_channel *chan, int engine) +{ + struct drm_device *dev = chan->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nvc0_software_priv *psw = nv_engine(dev, NVOBJ_ENGINE_SW); + struct nvc0_software_chan *pch; + int ret = 0, i; + + pch = kzalloc(sizeof(*pch), GFP_KERNEL); + if (!pch) + return -ENOMEM; + + nouveau_software_context_new(&pch->base); + chan->engctx[engine] = pch; + + /* map display semaphore buffers into channel's vm */ + for (i = 0; !ret && i < dev->mode_config.num_crtc; i++) { + struct nouveau_bo *bo; + if (dev_priv->card_type >= NV_D0) + bo = nvd0_display_crtc_sema(dev, i); + else + bo = nv50_display(dev)->crtc[i].sem.bo; + + ret = nouveau_bo_vma_add(bo, chan->vm, &pch->dispc_vma[i]); + } + + if (ret) + psw->base.base.context_del(chan, engine); + return ret; +} + +static void +nvc0_software_context_del(struct nouveau_channel *chan, int engine) +{ + struct drm_device *dev = chan->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nvc0_software_chan *pch = chan->engctx[engine]; + int i; + + if (dev_priv->card_type >= NV_D0) { + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct nouveau_bo *bo = nvd0_display_crtc_sema(dev, i); + nouveau_bo_vma_del(bo, &pch->dispc_vma[i]); + } + } else + if (dev_priv->card_type >= NV_50) { + struct nv50_display *disp = nv50_display(dev); + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct nv50_display_crtc *dispc = &disp->crtc[i]; + nouveau_bo_vma_del(dispc->sem.bo, &pch->dispc_vma[i]); + } + } + + chan->engctx[engine] = NULL; + kfree(pch); +} + +static int +nvc0_software_object_new(struct nouveau_channel *chan, int engine, + u32 handle, u16 class) +{ + return 0; +} + +static int +nvc0_software_init(struct drm_device *dev, int engine) +{ + return 0; +} + +static int +nvc0_software_fini(struct drm_device *dev, int engine, bool suspend) +{ + return 0; +} + +static void +nvc0_software_destroy(struct drm_device *dev, int engine) +{ + struct nvc0_software_priv *psw = nv_engine(dev, engine); + + NVOBJ_ENGINE_DEL(dev, SW); + kfree(psw); +} + +int +nvc0_software_create(struct drm_device *dev) +{ + struct nvc0_software_priv *psw = kzalloc(sizeof(*psw), GFP_KERNEL); + if (!psw) + return -ENOMEM; + + psw->base.base.destroy = nvc0_software_destroy; + psw->base.base.init = nvc0_software_init; + psw->base.base.fini = nvc0_software_fini; + psw->base.base.context_new = nvc0_software_context_new; + psw->base.base.context_del = nvc0_software_context_del; + psw->base.base.object_new = nvc0_software_object_new; + nouveau_software_create(&psw->base); + + NVOBJ_ENGINE_ADD(dev, SW, &psw->base.base); + NVOBJ_CLASS(dev, 0x906e, SW); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 0247250939e..c486d3ce3c2 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -33,6 +33,7 @@ #include "nouveau_crtc.h" #include "nouveau_dma.h" #include "nouveau_fb.h" +#include "nouveau_software.h" #include "nv50_display.h" #define EVO_DMA_NR 9 @@ -284,8 +285,6 @@ nvd0_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, u32 *push; int ret; - evo_sync(crtc->dev, EVO_MASTER); - swap_interval <<= 4; if (swap_interval == 0) swap_interval |= 0x100; @@ -300,15 +299,16 @@ nvd0_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, if (ret) return ret; - offset = chan->dispc_vma[nv_crtc->index].offset; + + offset = nvc0_software_crtc(chan, nv_crtc->index); offset += evo->sem.offset; - BEGIN_NVC0(chan, 2, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); + BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); OUT_RING (chan, upper_32_bits(offset)); OUT_RING (chan, lower_32_bits(offset)); OUT_RING (chan, 0xf00d0000 | evo->sem.value); OUT_RING (chan, 0x1002); - BEGIN_NVC0(chan, 2, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); + BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); OUT_RING (chan, upper_32_bits(offset)); OUT_RING (chan, lower_32_bits(offset ^ 0x10)); OUT_RING (chan, 0x74b1e000); @@ -882,7 +882,7 @@ nvd0_crtc_create(struct drm_device *dev, int index) drm_mode_crtc_set_gamma_size(crtc, 256); ret = nouveau_bo_new(dev, 64 * 64 * 4, 0x100, TTM_PL_FLAG_VRAM, - 0, 0x0000, &nv_crtc->cursor.nvbo); + 0, 0x0000, NULL, &nv_crtc->cursor.nvbo); if (!ret) { ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM); if (!ret) @@ -895,7 +895,7 @@ nvd0_crtc_create(struct drm_device *dev, int index) goto out; ret = nouveau_bo_new(dev, 8192, 0x100, TTM_PL_FLAG_VRAM, - 0, 0x0000, &nv_crtc->lut.nvbo); + 0, 0x0000, NULL, &nv_crtc->lut.nvbo); if (!ret) { ret = nouveau_bo_pin(nv_crtc->lut.nvbo, TTM_PL_FLAG_VRAM); if (!ret) @@ -2030,7 +2030,7 @@ nvd0_display_create(struct drm_device *dev) /* small shared memory area we use for notifiers and semaphores */ ret = nouveau_bo_new(dev, 4096, 0x1000, TTM_PL_FLAG_VRAM, - 0, 0x0000, &disp->sync); + 0, 0x0000, NULL, &disp->sync); if (!ret) { ret = nouveau_bo_pin(disp->sync, TTM_PL_FLAG_VRAM); if (!ret) diff --git a/drivers/gpu/drm/nouveau/nve0_fifo.c b/drivers/gpu/drm/nouveau/nve0_fifo.c new file mode 100644 index 00000000000..1855ecbd843 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nve0_fifo.c @@ -0,0 +1,423 @@ +/* + * Copyright 2010 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_mm.h" +#include "nouveau_fifo.h" + +#define NVE0_FIFO_ENGINE_NUM 32 + +static void nve0_fifo_isr(struct drm_device *); + +struct nve0_fifo_engine { + struct nouveau_gpuobj *playlist[2]; + int cur_playlist; +}; + +struct nve0_fifo_priv { + struct nouveau_fifo_priv base; + struct nve0_fifo_engine engine[NVE0_FIFO_ENGINE_NUM]; + struct { + struct nouveau_gpuobj *mem; + struct nouveau_vma bar; + } user; + int spoon_nr; +}; + +struct nve0_fifo_chan { + struct nouveau_fifo_chan base; + u32 engine; +}; + +static void +nve0_fifo_playlist_update(struct drm_device *dev, u32 engine) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem; + struct nve0_fifo_priv *priv = nv_engine(dev, NVOBJ_ENGINE_FIFO); + struct nve0_fifo_engine *peng = &priv->engine[engine]; + struct nouveau_gpuobj *cur; + u32 match = (engine << 16) | 0x00000001; + int ret, i, p; + + cur = peng->playlist[peng->cur_playlist]; + if (unlikely(cur == NULL)) { + ret = nouveau_gpuobj_new(dev, NULL, 0x8000, 0x1000, 0, &cur); + if (ret) { + NV_ERROR(dev, "PFIFO: playlist alloc failed\n"); + return; + } + + peng->playlist[peng->cur_playlist] = cur; + } + + peng->cur_playlist = !peng->cur_playlist; + + for (i = 0, p = 0; i < priv->base.channels; i++) { + u32 ctrl = nv_rd32(dev, 0x800004 + (i * 8)) & 0x001f0001; + if (ctrl != match) + continue; + nv_wo32(cur, p + 0, i); + nv_wo32(cur, p + 4, 0x00000000); + p += 8; + } + pinstmem->flush(dev); + + nv_wr32(dev, 0x002270, cur->vinst >> 12); + nv_wr32(dev, 0x002274, (engine << 20) | (p >> 3)); + if (!nv_wait(dev, 0x002284 + (engine * 4), 0x00100000, 0x00000000)) + NV_ERROR(dev, "PFIFO: playlist %d update timeout\n", engine); +} + +static int +nve0_fifo_context_new(struct nouveau_channel *chan, int engine) +{ + struct drm_device *dev = chan->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem; + struct nve0_fifo_priv *priv = nv_engine(dev, engine); + struct nve0_fifo_chan *fctx; + u64 usermem = priv->user.mem->vinst + chan->id * 512; + u64 ib_virt = chan->pushbuf_base + chan->dma.ib_base * 4; + int ret = 0, i; + + fctx = chan->engctx[engine] = kzalloc(sizeof(*fctx), GFP_KERNEL); + if (!fctx) + return -ENOMEM; + + fctx->engine = 0; /* PGRAPH */ + + /* allocate vram for control regs, map into polling area */ + chan->user = ioremap_wc(pci_resource_start(dev->pdev, 1) + + priv->user.bar.offset + (chan->id * 512), 512); + if (!chan->user) { + ret = -ENOMEM; + goto error; + } + + for (i = 0; i < 0x100; i += 4) + nv_wo32(chan->ramin, i, 0x00000000); + nv_wo32(chan->ramin, 0x08, lower_32_bits(usermem)); + nv_wo32(chan->ramin, 0x0c, upper_32_bits(usermem)); + nv_wo32(chan->ramin, 0x10, 0x0000face); + nv_wo32(chan->ramin, 0x30, 0xfffff902); + nv_wo32(chan->ramin, 0x48, lower_32_bits(ib_virt)); + nv_wo32(chan->ramin, 0x4c, drm_order(chan->dma.ib_max + 1) << 16 | + upper_32_bits(ib_virt)); + nv_wo32(chan->ramin, 0x84, 0x20400000); + nv_wo32(chan->ramin, 0x94, 0x30000001); + nv_wo32(chan->ramin, 0x9c, 0x00000100); + nv_wo32(chan->ramin, 0xac, 0x0000001f); + nv_wo32(chan->ramin, 0xe4, 0x00000000); + nv_wo32(chan->ramin, 0xe8, chan->id); + nv_wo32(chan->ramin, 0xf8, 0x10003080); /* 0x002310 */ + nv_wo32(chan->ramin, 0xfc, 0x10000010); /* 0x002350 */ + pinstmem->flush(dev); + + nv_wr32(dev, 0x800000 + (chan->id * 8), 0x80000000 | + (chan->ramin->vinst >> 12)); + nv_mask(dev, 0x800004 + (chan->id * 8), 0x00000400, 0x00000400); + nve0_fifo_playlist_update(dev, fctx->engine); + nv_mask(dev, 0x800004 + (chan->id * 8), 0x00000400, 0x00000400); + +error: + if (ret) + priv->base.base.context_del(chan, engine); + return ret; +} + +static void +nve0_fifo_context_del(struct nouveau_channel *chan, int engine) +{ + struct nve0_fifo_chan *fctx = chan->engctx[engine]; + struct drm_device *dev = chan->dev; + + nv_mask(dev, 0x800004 + (chan->id * 8), 0x00000800, 0x00000800); + nv_wr32(dev, 0x002634, chan->id); + if (!nv_wait(dev, 0x0002634, 0xffffffff, chan->id)) + NV_WARN(dev, "0x2634 != chid: 0x%08x\n", nv_rd32(dev, 0x2634)); + nve0_fifo_playlist_update(dev, fctx->engine); + nv_wr32(dev, 0x800000 + (chan->id * 8), 0x00000000); + + if (chan->user) { + iounmap(chan->user); + chan->user = NULL; + } + + chan->engctx[NVOBJ_ENGINE_FIFO] = NULL; + kfree(fctx); +} + +static int +nve0_fifo_init(struct drm_device *dev, int engine) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nve0_fifo_priv *priv = nv_engine(dev, engine); + struct nve0_fifo_chan *fctx; + int i; + + /* reset PFIFO, enable all available PSUBFIFO areas */ + nv_mask(dev, 0x000200, 0x00000100, 0x00000000); + nv_mask(dev, 0x000200, 0x00000100, 0x00000100); + nv_wr32(dev, 0x000204, 0xffffffff); + + priv->spoon_nr = hweight32(nv_rd32(dev, 0x000204)); + NV_DEBUG(dev, "PFIFO: %d subfifo(s)\n", priv->spoon_nr); + + /* PSUBFIFO[n] */ + for (i = 0; i < priv->spoon_nr; i++) { + nv_mask(dev, 0x04013c + (i * 0x2000), 0x10000100, 0x00000000); + nv_wr32(dev, 0x040108 + (i * 0x2000), 0xffffffff); /* INTR */ + nv_wr32(dev, 0x04010c + (i * 0x2000), 0xfffffeff); /* INTR_EN */ + } + + nv_wr32(dev, 0x002254, 0x10000000 | priv->user.bar.offset >> 12); + + nv_wr32(dev, 0x002a00, 0xffffffff); + nv_wr32(dev, 0x002100, 0xffffffff); + nv_wr32(dev, 0x002140, 0xbfffffff); + + /* restore PFIFO context table */ + for (i = 0; i < priv->base.channels; i++) { + struct nouveau_channel *chan = dev_priv->channels.ptr[i]; + if (!chan || !(fctx = chan->engctx[engine])) + continue; + + nv_wr32(dev, 0x800000 + (i * 8), 0x80000000 | + (chan->ramin->vinst >> 12)); + nv_mask(dev, 0x800004 + (i * 8), 0x00000400, 0x00000400); + nve0_fifo_playlist_update(dev, fctx->engine); + nv_mask(dev, 0x800004 + (i * 8), 0x00000400, 0x00000400); + } + + return 0; +} + +static int +nve0_fifo_fini(struct drm_device *dev, int engine, bool suspend) +{ + struct nve0_fifo_priv *priv = nv_engine(dev, engine); + int i; + + for (i = 0; i < priv->base.channels; i++) { + if (!(nv_rd32(dev, 0x800004 + (i * 8)) & 1)) + continue; + + nv_mask(dev, 0x800004 + (i * 8), 0x00000800, 0x00000800); + nv_wr32(dev, 0x002634, i); + if (!nv_wait(dev, 0x002634, 0xffffffff, i)) { + NV_INFO(dev, "PFIFO: kick ch %d failed: 0x%08x\n", + i, nv_rd32(dev, 0x002634)); + return -EBUSY; + } + } + + nv_wr32(dev, 0x002140, 0x00000000); + return 0; +} + +struct nouveau_enum nve0_fifo_fault_unit[] = { + {} +}; + +struct nouveau_enum nve0_fifo_fault_reason[] = { + { 0x00, "PT_NOT_PRESENT" }, + { 0x01, "PT_TOO_SHORT" }, + { 0x02, "PAGE_NOT_PRESENT" }, + { 0x03, "VM_LIMIT_EXCEEDED" }, + { 0x04, "NO_CHANNEL" }, + { 0x05, "PAGE_SYSTEM_ONLY" }, + { 0x06, "PAGE_READ_ONLY" }, + { 0x0a, "COMPRESSED_SYSRAM" }, + { 0x0c, "INVALID_STORAGE_TYPE" }, + {} +}; + +struct nouveau_enum nve0_fifo_fault_hubclient[] = { + {} +}; + +struct nouveau_enum nve0_fifo_fault_gpcclient[] = { + {} +}; + +struct nouveau_bitfield nve0_fifo_subfifo_intr[] = { + { 0x00200000, "ILLEGAL_MTHD" }, + { 0x00800000, "EMPTY_SUBC" }, + {} +}; + +static void +nve0_fifo_isr_vm_fault(struct drm_device *dev, int unit) +{ + u32 inst = nv_rd32(dev, 0x2800 + (unit * 0x10)); + u32 valo = nv_rd32(dev, 0x2804 + (unit * 0x10)); + u32 vahi = nv_rd32(dev, 0x2808 + (unit * 0x10)); + u32 stat = nv_rd32(dev, 0x280c + (unit * 0x10)); + u32 client = (stat & 0x00001f00) >> 8; + + NV_INFO(dev, "PFIFO: %s fault at 0x%010llx [", + (stat & 0x00000080) ? "write" : "read", (u64)vahi << 32 | valo); + nouveau_enum_print(nve0_fifo_fault_reason, stat & 0x0000000f); + printk("] from "); + nouveau_enum_print(nve0_fifo_fault_unit, unit); + if (stat & 0x00000040) { + printk("/"); + nouveau_enum_print(nve0_fifo_fault_hubclient, client); + } else { + printk("/GPC%d/", (stat & 0x1f000000) >> 24); + nouveau_enum_print(nve0_fifo_fault_gpcclient, client); + } + printk(" on channel 0x%010llx\n", (u64)inst << 12); +} + +static void +nve0_fifo_isr_subfifo_intr(struct drm_device *dev, int unit) +{ + u32 stat = nv_rd32(dev, 0x040108 + (unit * 0x2000)); + u32 addr = nv_rd32(dev, 0x0400c0 + (unit * 0x2000)); + u32 data = nv_rd32(dev, 0x0400c4 + (unit * 0x2000)); + u32 chid = nv_rd32(dev, 0x040120 + (unit * 0x2000)) & 0x7f; + u32 subc = (addr & 0x00070000); + u32 mthd = (addr & 0x00003ffc); + + NV_INFO(dev, "PSUBFIFO %d:", unit); + nouveau_bitfield_print(nve0_fifo_subfifo_intr, stat); + NV_INFO(dev, "PSUBFIFO %d: ch %d subc %d mthd 0x%04x data 0x%08x\n", + unit, chid, subc, mthd, data); + + nv_wr32(dev, 0x0400c0 + (unit * 0x2000), 0x80600008); + nv_wr32(dev, 0x040108 + (unit * 0x2000), stat); +} + +static void +nve0_fifo_isr(struct drm_device *dev) +{ + u32 stat = nv_rd32(dev, 0x002100); + + if (stat & 0x00000100) { + NV_INFO(dev, "PFIFO: unknown status 0x00000100\n"); + nv_wr32(dev, 0x002100, 0x00000100); + stat &= ~0x00000100; + } + + if (stat & 0x10000000) { + u32 units = nv_rd32(dev, 0x00259c); + u32 u = units; + + while (u) { + int i = ffs(u) - 1; + nve0_fifo_isr_vm_fault(dev, i); + u &= ~(1 << i); + } + + nv_wr32(dev, 0x00259c, units); + stat &= ~0x10000000; + } + + if (stat & 0x20000000) { + u32 units = nv_rd32(dev, 0x0025a0); + u32 u = units; + + while (u) { + int i = ffs(u) - 1; + nve0_fifo_isr_subfifo_intr(dev, i); + u &= ~(1 << i); + } + + nv_wr32(dev, 0x0025a0, units); + stat &= ~0x20000000; + } + + if (stat & 0x40000000) { + NV_INFO(dev, "PFIFO: unknown status 0x40000000\n"); + nv_mask(dev, 0x002a00, 0x00000000, 0x00000000); + stat &= ~0x40000000; + } + + if (stat) { + NV_INFO(dev, "PFIFO: unhandled status 0x%08x\n", stat); + nv_wr32(dev, 0x002100, stat); + nv_wr32(dev, 0x002140, 0); + } +} + +static void +nve0_fifo_destroy(struct drm_device *dev, int engine) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nve0_fifo_priv *priv = nv_engine(dev, engine); + int i; + + nouveau_vm_put(&priv->user.bar); + nouveau_gpuobj_ref(NULL, &priv->user.mem); + + for (i = 0; i < NVE0_FIFO_ENGINE_NUM; i++) { + nouveau_gpuobj_ref(NULL, &priv->engine[i].playlist[0]); + nouveau_gpuobj_ref(NULL, &priv->engine[i].playlist[1]); + } + + dev_priv->eng[engine] = NULL; + kfree(priv); +} + +int +nve0_fifo_create(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nve0_fifo_priv *priv; + int ret; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base.base.destroy = nve0_fifo_destroy; + priv->base.base.init = nve0_fifo_init; + priv->base.base.fini = nve0_fifo_fini; + priv->base.base.context_new = nve0_fifo_context_new; + priv->base.base.context_del = nve0_fifo_context_del; + priv->base.channels = 4096; + dev_priv->eng[NVOBJ_ENGINE_FIFO] = &priv->base.base; + + ret = nouveau_gpuobj_new(dev, NULL, priv->base.channels * 512, 0x1000, + NVOBJ_FLAG_ZERO_ALLOC, &priv->user.mem); + if (ret) + goto error; + + ret = nouveau_vm_get(dev_priv->bar1_vm, priv->user.mem->size, + 12, NV_MEM_ACCESS_RW, &priv->user.bar); + if (ret) + goto error; + + nouveau_vm_map(&priv->user.bar, *(struct nouveau_mem **)priv->user.mem->node); + + nouveau_irq_register(dev, 8, nve0_fifo_isr); +error: + if (ret) + priv->base.base.destroy(dev, NVOBJ_ENGINE_FIFO); + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nve0_graph.c b/drivers/gpu/drm/nouveau/nve0_graph.c new file mode 100644 index 00000000000..8a8051b68f1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nve0_graph.c @@ -0,0 +1,831 @@ +/* + * Copyright 2010 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 <linux/module.h> + +#include "drmP.h" + +#include "nouveau_drv.h" +#include "nouveau_mm.h" +#include "nouveau_fifo.h" + +#include "nve0_graph.h" + +static void +nve0_graph_ctxctl_debug_unit(struct drm_device *dev, u32 base) +{ + NV_INFO(dev, "PGRAPH: %06x - done 0x%08x\n", base, + nv_rd32(dev, base + 0x400)); + NV_INFO(dev, "PGRAPH: %06x - stat 0x%08x 0x%08x 0x%08x 0x%08x\n", base, + nv_rd32(dev, base + 0x800), nv_rd32(dev, base + 0x804), + nv_rd32(dev, base + 0x808), nv_rd32(dev, base + 0x80c)); + NV_INFO(dev, "PGRAPH: %06x - stat 0x%08x 0x%08x 0x%08x 0x%08x\n", base, + nv_rd32(dev, base + 0x810), nv_rd32(dev, base + 0x814), + nv_rd32(dev, base + 0x818), nv_rd32(dev, base + 0x81c)); +} + +static void +nve0_graph_ctxctl_debug(struct drm_device *dev) +{ + u32 gpcnr = nv_rd32(dev, 0x409604) & 0xffff; + u32 gpc; + + nve0_graph_ctxctl_debug_unit(dev, 0x409000); + for (gpc = 0; gpc < gpcnr; gpc++) + nve0_graph_ctxctl_debug_unit(dev, 0x502000 + (gpc * 0x8000)); +} + +static int +nve0_graph_load_context(struct nouveau_channel *chan) +{ + struct drm_device *dev = chan->dev; + + nv_wr32(dev, 0x409840, 0x00000030); + nv_wr32(dev, 0x409500, 0x80000000 | chan->ramin->vinst >> 12); + nv_wr32(dev, 0x409504, 0x00000003); + if (!nv_wait(dev, 0x409800, 0x00000010, 0x00000010)) + NV_ERROR(dev, "PGRAPH: load_ctx timeout\n"); + + return 0; +} + +static int +nve0_graph_unload_context_to(struct drm_device *dev, u64 chan) +{ + nv_wr32(dev, 0x409840, 0x00000003); + nv_wr32(dev, 0x409500, 0x80000000 | chan >> 12); + nv_wr32(dev, 0x409504, 0x00000009); + if (!nv_wait(dev, 0x409800, 0x00000001, 0x00000000)) { + NV_ERROR(dev, "PGRAPH: unload_ctx timeout\n"); + return -EBUSY; + } + + return 0; +} + +static int +nve0_graph_construct_context(struct nouveau_channel *chan) +{ + struct drm_nouveau_private *dev_priv = chan->dev->dev_private; + struct nve0_graph_priv *priv = nv_engine(chan->dev, NVOBJ_ENGINE_GR); + struct nve0_graph_chan *grch = chan->engctx[NVOBJ_ENGINE_GR]; + struct drm_device *dev = chan->dev; + int ret, i; + u32 *ctx; + + ctx = kmalloc(priv->grctx_size, GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + nve0_graph_load_context(chan); + + nv_wo32(grch->grctx, 0x1c, 1); + nv_wo32(grch->grctx, 0x20, 0); + nv_wo32(grch->grctx, 0x28, 0); + nv_wo32(grch->grctx, 0x2c, 0); + dev_priv->engine.instmem.flush(dev); + + ret = nve0_grctx_generate(chan); + if (ret) + goto err; + + ret = nve0_graph_unload_context_to(dev, chan->ramin->vinst); + if (ret) + goto err; + + for (i = 0; i < priv->grctx_size; i += 4) + ctx[i / 4] = nv_ro32(grch->grctx, i); + + priv->grctx_vals = ctx; + return 0; + +err: + kfree(ctx); + return ret; +} + +static int +nve0_graph_create_context_mmio_list(struct nouveau_channel *chan) +{ + struct nve0_graph_priv *priv = nv_engine(chan->dev, NVOBJ_ENGINE_GR); + struct nve0_graph_chan *grch = chan->engctx[NVOBJ_ENGINE_GR]; + struct drm_device *dev = chan->dev; + u32 magic[GPC_MAX][2]; + u16 offset = 0x0000; + int gpc; + int ret; + + ret = nouveau_gpuobj_new(dev, chan, 0x3000, 256, NVOBJ_FLAG_VM, + &grch->unk408004); + if (ret) + return ret; + + ret = nouveau_gpuobj_new(dev, chan, 0x8000, 256, NVOBJ_FLAG_VM, + &grch->unk40800c); + if (ret) + return ret; + + ret = nouveau_gpuobj_new(dev, chan, 384 * 1024, 4096, + NVOBJ_FLAG_VM | NVOBJ_FLAG_VM_USER, + &grch->unk418810); + if (ret) + return ret; + + ret = nouveau_gpuobj_new(dev, chan, 0x1000, 0, NVOBJ_FLAG_VM, + &grch->mmio); + if (ret) + return ret; + +#define mmio(r,v) do { \ + nv_wo32(grch->mmio, (grch->mmio_nr * 8) + 0, (r)); \ + nv_wo32(grch->mmio, (grch->mmio_nr * 8) + 4, (v)); \ + grch->mmio_nr++; \ +} while (0) + mmio(0x40800c, grch->unk40800c->linst >> 8); + mmio(0x408010, 0x80000000); + mmio(0x419004, grch->unk40800c->linst >> 8); + mmio(0x419008, 0x00000000); + mmio(0x4064cc, 0x80000000); + mmio(0x408004, grch->unk408004->linst >> 8); + mmio(0x408008, 0x80000030); + mmio(0x418808, grch->unk408004->linst >> 8); + mmio(0x41880c, 0x80000030); + mmio(0x4064c8, 0x01800600); + mmio(0x418810, 0x80000000 | grch->unk418810->linst >> 12); + mmio(0x419848, 0x10000000 | grch->unk418810->linst >> 12); + mmio(0x405830, 0x02180648); + mmio(0x4064c4, 0x0192ffff); + + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + u16 magic0 = 0x0218 * priv->tpc_nr[gpc]; + u16 magic1 = 0x0648 * priv->tpc_nr[gpc]; + magic[gpc][0] = 0x10000000 | (magic0 << 16) | offset; + magic[gpc][1] = 0x00000000 | (magic1 << 16); + offset += 0x0324 * priv->tpc_nr[gpc]; + } + + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + mmio(GPC_UNIT(gpc, 0x30c0), magic[gpc][0]); + mmio(GPC_UNIT(gpc, 0x30e4), magic[gpc][1] | offset); + offset += 0x07ff * priv->tpc_nr[gpc]; + } + + mmio(0x17e91c, 0x06060609); + mmio(0x17e920, 0x00090a05); +#undef mmio + return 0; +} + +static int +nve0_graph_context_new(struct nouveau_channel *chan, int engine) +{ + struct drm_device *dev = chan->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem; + struct nve0_graph_priv *priv = nv_engine(dev, engine); + struct nve0_graph_chan *grch; + struct nouveau_gpuobj *grctx; + int ret, i; + + grch = kzalloc(sizeof(*grch), GFP_KERNEL); + if (!grch) + return -ENOMEM; + chan->engctx[NVOBJ_ENGINE_GR] = grch; + + ret = nouveau_gpuobj_new(dev, chan, priv->grctx_size, 256, + NVOBJ_FLAG_VM | NVOBJ_FLAG_ZERO_ALLOC, + &grch->grctx); + if (ret) + goto error; + grctx = grch->grctx; + + ret = nve0_graph_create_context_mmio_list(chan); + if (ret) + goto error; + + nv_wo32(chan->ramin, 0x0210, lower_32_bits(grctx->linst) | 4); + nv_wo32(chan->ramin, 0x0214, upper_32_bits(grctx->linst)); + pinstmem->flush(dev); + + if (!priv->grctx_vals) { + ret = nve0_graph_construct_context(chan); + if (ret) + goto error; + } + + for (i = 0; i < priv->grctx_size; i += 4) + nv_wo32(grctx, i, priv->grctx_vals[i / 4]); + nv_wo32(grctx, 0xf4, 0); + nv_wo32(grctx, 0xf8, 0); + nv_wo32(grctx, 0x10, grch->mmio_nr); + nv_wo32(grctx, 0x14, lower_32_bits(grch->mmio->linst)); + nv_wo32(grctx, 0x18, upper_32_bits(grch->mmio->linst)); + nv_wo32(grctx, 0x1c, 1); + nv_wo32(grctx, 0x20, 0); + nv_wo32(grctx, 0x28, 0); + nv_wo32(grctx, 0x2c, 0); + + pinstmem->flush(dev); + return 0; + +error: + priv->base.context_del(chan, engine); + return ret; +} + +static void +nve0_graph_context_del(struct nouveau_channel *chan, int engine) +{ + struct nve0_graph_chan *grch = chan->engctx[engine]; + + nouveau_gpuobj_ref(NULL, &grch->mmio); + nouveau_gpuobj_ref(NULL, &grch->unk418810); + nouveau_gpuobj_ref(NULL, &grch->unk40800c); + nouveau_gpuobj_ref(NULL, &grch->unk408004); + nouveau_gpuobj_ref(NULL, &grch->grctx); + chan->engctx[engine] = NULL; +} + +static int +nve0_graph_object_new(struct nouveau_channel *chan, int engine, + u32 handle, u16 class) +{ + return 0; +} + +static int +nve0_graph_fini(struct drm_device *dev, int engine, bool suspend) +{ + return 0; +} + +static void +nve0_graph_init_obj418880(struct drm_device *dev) +{ + struct nve0_graph_priv *priv = nv_engine(dev, NVOBJ_ENGINE_GR); + int i; + + nv_wr32(dev, GPC_BCAST(0x0880), 0x00000000); + nv_wr32(dev, GPC_BCAST(0x08a4), 0x00000000); + for (i = 0; i < 4; i++) + nv_wr32(dev, GPC_BCAST(0x0888) + (i * 4), 0x00000000); + nv_wr32(dev, GPC_BCAST(0x08b4), priv->unk4188b4->vinst >> 8); + nv_wr32(dev, GPC_BCAST(0x08b8), priv->unk4188b8->vinst >> 8); +} + +static void +nve0_graph_init_regs(struct drm_device *dev) +{ + nv_wr32(dev, 0x400080, 0x003083c2); + nv_wr32(dev, 0x400088, 0x0001ffe7); + nv_wr32(dev, 0x40008c, 0x00000000); + nv_wr32(dev, 0x400090, 0x00000030); + nv_wr32(dev, 0x40013c, 0x003901f7); + nv_wr32(dev, 0x400140, 0x00000100); + nv_wr32(dev, 0x400144, 0x00000000); + nv_wr32(dev, 0x400148, 0x00000110); + nv_wr32(dev, 0x400138, 0x00000000); + nv_wr32(dev, 0x400130, 0x00000000); + nv_wr32(dev, 0x400134, 0x00000000); + nv_wr32(dev, 0x400124, 0x00000002); +} + +static void +nve0_graph_init_units(struct drm_device *dev) +{ + nv_wr32(dev, 0x409ffc, 0x00000000); + nv_wr32(dev, 0x409c14, 0x00003e3e); + nv_wr32(dev, 0x409c24, 0x000f0000); + + nv_wr32(dev, 0x404000, 0xc0000000); + nv_wr32(dev, 0x404600, 0xc0000000); + nv_wr32(dev, 0x408030, 0xc0000000); + nv_wr32(dev, 0x404490, 0xc0000000); + nv_wr32(dev, 0x406018, 0xc0000000); + nv_wr32(dev, 0x407020, 0xc0000000); + nv_wr32(dev, 0x405840, 0xc0000000); + nv_wr32(dev, 0x405844, 0x00ffffff); + + nv_mask(dev, 0x419cc0, 0x00000008, 0x00000008); + nv_mask(dev, 0x419eb4, 0x00001000, 0x00001000); + +} + +static void +nve0_graph_init_gpc_0(struct drm_device *dev) +{ + struct nve0_graph_priv *priv = nv_engine(dev, NVOBJ_ENGINE_GR); + const u32 magicgpc918 = DIV_ROUND_UP(0x00800000, priv->tpc_total); + u32 data[TPC_MAX / 8]; + u8 tpcnr[GPC_MAX]; + int i, gpc, tpc; + + nv_wr32(dev, GPC_UNIT(0, 0x3018), 0x00000001); + + memset(data, 0x00, sizeof(data)); + memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); + for (i = 0, gpc = -1; i < priv->tpc_total; i++) { + do { + gpc = (gpc + 1) % priv->gpc_nr; + } while (!tpcnr[gpc]); + tpc = priv->tpc_nr[gpc] - tpcnr[gpc]--; + + data[i / 8] |= tpc << ((i % 8) * 4); + } + + nv_wr32(dev, GPC_BCAST(0x0980), data[0]); + nv_wr32(dev, GPC_BCAST(0x0984), data[1]); + nv_wr32(dev, GPC_BCAST(0x0988), data[2]); + nv_wr32(dev, GPC_BCAST(0x098c), data[3]); + + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + nv_wr32(dev, GPC_UNIT(gpc, 0x0914), priv->magic_not_rop_nr << 8 | + priv->tpc_nr[gpc]); + nv_wr32(dev, GPC_UNIT(gpc, 0x0910), 0x00040000 | priv->tpc_total); + nv_wr32(dev, GPC_UNIT(gpc, 0x0918), magicgpc918); + } + + nv_wr32(dev, GPC_BCAST(0x1bd4), magicgpc918); + nv_wr32(dev, GPC_BCAST(0x08ac), nv_rd32(dev, 0x100800)); +} + +static void +nve0_graph_init_gpc_1(struct drm_device *dev) +{ + struct nve0_graph_priv *priv = nv_engine(dev, NVOBJ_ENGINE_GR); + int gpc, tpc; + + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + nv_wr32(dev, GPC_UNIT(gpc, 0x3038), 0xc0000000); + nv_wr32(dev, GPC_UNIT(gpc, 0x0420), 0xc0000000); + nv_wr32(dev, GPC_UNIT(gpc, 0x0900), 0xc0000000); + nv_wr32(dev, GPC_UNIT(gpc, 0x1028), 0xc0000000); + nv_wr32(dev, GPC_UNIT(gpc, 0x0824), 0xc0000000); + for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) { + nv_wr32(dev, TPC_UNIT(gpc, tpc, 0x508), 0xffffffff); + nv_wr32(dev, TPC_UNIT(gpc, tpc, 0x50c), 0xffffffff); + nv_wr32(dev, TPC_UNIT(gpc, tpc, 0x224), 0xc0000000); + nv_wr32(dev, TPC_UNIT(gpc, tpc, 0x48c), 0xc0000000); + nv_wr32(dev, TPC_UNIT(gpc, tpc, 0x084), 0xc0000000); + nv_wr32(dev, TPC_UNIT(gpc, tpc, 0x644), 0x001ffffe); + nv_wr32(dev, TPC_UNIT(gpc, tpc, 0x64c), 0x0000000f); + } + nv_wr32(dev, GPC_UNIT(gpc, 0x2c90), 0xffffffff); + nv_wr32(dev, GPC_UNIT(gpc, 0x2c94), 0xffffffff); + } +} + +static void +nve0_graph_init_rop(struct drm_device *dev) +{ + struct nve0_graph_priv *priv = nv_engine(dev, NVOBJ_ENGINE_GR); + int rop; + + for (rop = 0; rop < priv->rop_nr; rop++) { + nv_wr32(dev, ROP_UNIT(rop, 0x144), 0xc0000000); + nv_wr32(dev, ROP_UNIT(rop, 0x070), 0xc0000000); + nv_wr32(dev, ROP_UNIT(rop, 0x204), 0xffffffff); + nv_wr32(dev, ROP_UNIT(rop, 0x208), 0xffffffff); + } +} + +static void +nve0_graph_init_fuc(struct drm_device *dev, u32 fuc_base, + struct nve0_graph_fuc *code, struct nve0_graph_fuc *data) +{ + int i; + + nv_wr32(dev, fuc_base + 0x01c0, 0x01000000); + for (i = 0; i < data->size / 4; i++) + nv_wr32(dev, fuc_base + 0x01c4, data->data[i]); + + nv_wr32(dev, fuc_base + 0x0180, 0x01000000); + for (i = 0; i < code->size / 4; i++) { + if ((i & 0x3f) == 0) + nv_wr32(dev, fuc_base + 0x0188, i >> 6); + nv_wr32(dev, fuc_base + 0x0184, code->data[i]); + } +} + +static int +nve0_graph_init_ctxctl(struct drm_device *dev) +{ + struct nve0_graph_priv *priv = nv_engine(dev, NVOBJ_ENGINE_GR); + u32 r000260; + + /* load fuc microcode */ + r000260 = nv_mask(dev, 0x000260, 0x00000001, 0x00000000); + nve0_graph_init_fuc(dev, 0x409000, &priv->fuc409c, &priv->fuc409d); + nve0_graph_init_fuc(dev, 0x41a000, &priv->fuc41ac, &priv->fuc41ad); + nv_wr32(dev, 0x000260, r000260); + + /* start both of them running */ + nv_wr32(dev, 0x409840, 0xffffffff); + nv_wr32(dev, 0x41a10c, 0x00000000); + nv_wr32(dev, 0x40910c, 0x00000000); + nv_wr32(dev, 0x41a100, 0x00000002); + nv_wr32(dev, 0x409100, 0x00000002); + if (!nv_wait(dev, 0x409800, 0x00000001, 0x00000001)) + NV_INFO(dev, "0x409800 wait failed\n"); + + nv_wr32(dev, 0x409840, 0xffffffff); + nv_wr32(dev, 0x409500, 0x7fffffff); + nv_wr32(dev, 0x409504, 0x00000021); + + nv_wr32(dev, 0x409840, 0xffffffff); + nv_wr32(dev, 0x409500, 0x00000000); + nv_wr32(dev, 0x409504, 0x00000010); + if (!nv_wait_ne(dev, 0x409800, 0xffffffff, 0x00000000)) { + NV_ERROR(dev, "fuc09 req 0x10 timeout\n"); + return -EBUSY; + } + priv->grctx_size = nv_rd32(dev, 0x409800); + + nv_wr32(dev, 0x409840, 0xffffffff); + nv_wr32(dev, 0x409500, 0x00000000); + nv_wr32(dev, 0x409504, 0x00000016); + if (!nv_wait_ne(dev, 0x409800, 0xffffffff, 0x00000000)) { + NV_ERROR(dev, "fuc09 req 0x16 timeout\n"); + return -EBUSY; + } + + nv_wr32(dev, 0x409840, 0xffffffff); + nv_wr32(dev, 0x409500, 0x00000000); + nv_wr32(dev, 0x409504, 0x00000025); + if (!nv_wait_ne(dev, 0x409800, 0xffffffff, 0x00000000)) { + NV_ERROR(dev, "fuc09 req 0x25 timeout\n"); + return -EBUSY; + } + + nv_wr32(dev, 0x409800, 0x00000000); + nv_wr32(dev, 0x409500, 0x00000001); + nv_wr32(dev, 0x409504, 0x00000030); + if (!nv_wait_ne(dev, 0x409800, 0xffffffff, 0x00000000)) { + NV_ERROR(dev, "fuc09 req 0x30 timeout\n"); + return -EBUSY; + } + + nv_wr32(dev, 0x409810, 0xb00095c8); + nv_wr32(dev, 0x409800, 0x00000000); + nv_wr32(dev, 0x409500, 0x00000001); + nv_wr32(dev, 0x409504, 0x00000031); + if (!nv_wait_ne(dev, 0x409800, 0xffffffff, 0x00000000)) { + NV_ERROR(dev, "fuc09 req 0x31 timeout\n"); + return -EBUSY; + } + + nv_wr32(dev, 0x409810, 0x00080420); + nv_wr32(dev, 0x409800, 0x00000000); + nv_wr32(dev, 0x409500, 0x00000001); + nv_wr32(dev, 0x409504, 0x00000032); + if (!nv_wait_ne(dev, 0x409800, 0xffffffff, 0x00000000)) { + NV_ERROR(dev, "fuc09 req 0x32 timeout\n"); + return -EBUSY; + } + + nv_wr32(dev, 0x409614, 0x00000070); + nv_wr32(dev, 0x409614, 0x00000770); + nv_wr32(dev, 0x40802c, 0x00000001); + return 0; +} + +static int +nve0_graph_init(struct drm_device *dev, int engine) +{ + int ret; + + nv_mask(dev, 0x000200, 0x18001000, 0x00000000); + nv_mask(dev, 0x000200, 0x18001000, 0x18001000); + + nve0_graph_init_obj418880(dev); + nve0_graph_init_regs(dev); + nve0_graph_init_gpc_0(dev); + + nv_wr32(dev, 0x400500, 0x00010001); + nv_wr32(dev, 0x400100, 0xffffffff); + nv_wr32(dev, 0x40013c, 0xffffffff); + + nve0_graph_init_units(dev); + nve0_graph_init_gpc_1(dev); + nve0_graph_init_rop(dev); + + nv_wr32(dev, 0x400108, 0xffffffff); + nv_wr32(dev, 0x400138, 0xffffffff); + nv_wr32(dev, 0x400118, 0xffffffff); + nv_wr32(dev, 0x400130, 0xffffffff); + nv_wr32(dev, 0x40011c, 0xffffffff); + nv_wr32(dev, 0x400134, 0xffffffff); + nv_wr32(dev, 0x400054, 0x34ce3464); + + ret = nve0_graph_init_ctxctl(dev); + if (ret) + return ret; + + return 0; +} + +int +nve0_graph_isr_chid(struct drm_device *dev, u64 inst) +{ + struct nouveau_fifo_priv *pfifo = nv_engine(dev, NVOBJ_ENGINE_FIFO); + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_channel *chan; + unsigned long flags; + int i; + + spin_lock_irqsave(&dev_priv->channels.lock, flags); + for (i = 0; i < pfifo->channels; i++) { + chan = dev_priv->channels.ptr[i]; + if (!chan || !chan->ramin) + continue; + + if (inst == chan->ramin->vinst) + break; + } + spin_unlock_irqrestore(&dev_priv->channels.lock, flags); + return i; +} + +static void +nve0_graph_ctxctl_isr(struct drm_device *dev) +{ + u32 ustat = nv_rd32(dev, 0x409c18); + + if (ustat & 0x00000001) + NV_INFO(dev, "PGRAPH: CTXCTRL ucode error\n"); + if (ustat & 0x00080000) + NV_INFO(dev, "PGRAPH: CTXCTRL watchdog timeout\n"); + if (ustat & ~0x00080001) + NV_INFO(dev, "PGRAPH: CTXCTRL 0x%08x\n", ustat); + + nve0_graph_ctxctl_debug(dev); + nv_wr32(dev, 0x409c20, ustat); +} + +static void +nve0_graph_trap_isr(struct drm_device *dev, int chid) +{ + struct nve0_graph_priv *priv = nv_engine(dev, NVOBJ_ENGINE_GR); + u32 trap = nv_rd32(dev, 0x400108); + int rop; + + if (trap & 0x00000001) { + u32 stat = nv_rd32(dev, 0x404000); + NV_INFO(dev, "PGRAPH: DISPATCH ch %d 0x%08x\n", chid, stat); + nv_wr32(dev, 0x404000, 0xc0000000); + nv_wr32(dev, 0x400108, 0x00000001); + trap &= ~0x00000001; + } + + if (trap & 0x00000010) { + u32 stat = nv_rd32(dev, 0x405840); + NV_INFO(dev, "PGRAPH: SHADER ch %d 0x%08x\n", chid, stat); + nv_wr32(dev, 0x405840, 0xc0000000); + nv_wr32(dev, 0x400108, 0x00000010); + trap &= ~0x00000010; + } + + if (trap & 0x02000000) { + for (rop = 0; rop < priv->rop_nr; rop++) { + u32 statz = nv_rd32(dev, ROP_UNIT(rop, 0x070)); + u32 statc = nv_rd32(dev, ROP_UNIT(rop, 0x144)); + NV_INFO(dev, "PGRAPH: ROP%d ch %d 0x%08x 0x%08x\n", + rop, chid, statz, statc); + nv_wr32(dev, ROP_UNIT(rop, 0x070), 0xc0000000); + nv_wr32(dev, ROP_UNIT(rop, 0x144), 0xc0000000); + } + nv_wr32(dev, 0x400108, 0x02000000); + trap &= ~0x02000000; + } + + if (trap) { + NV_INFO(dev, "PGRAPH: TRAP ch %d 0x%08x\n", chid, trap); + nv_wr32(dev, 0x400108, trap); + } +} + +static void +nve0_graph_isr(struct drm_device *dev) +{ + u64 inst = (u64)(nv_rd32(dev, 0x409b00) & 0x0fffffff) << 12; + u32 chid = nve0_graph_isr_chid(dev, inst); + u32 stat = nv_rd32(dev, 0x400100); + u32 addr = nv_rd32(dev, 0x400704); + u32 mthd = (addr & 0x00003ffc); + u32 subc = (addr & 0x00070000) >> 16; + u32 data = nv_rd32(dev, 0x400708); + u32 code = nv_rd32(dev, 0x400110); + u32 class = nv_rd32(dev, 0x404200 + (subc * 4)); + + if (stat & 0x00000010) { + if (nouveau_gpuobj_mthd_call2(dev, chid, class, mthd, data)) { + NV_INFO(dev, "PGRAPH: ILLEGAL_MTHD ch %d [0x%010llx] " + "subc %d class 0x%04x mthd 0x%04x " + "data 0x%08x\n", + chid, inst, subc, class, mthd, data); + } + nv_wr32(dev, 0x400100, 0x00000010); + stat &= ~0x00000010; + } + + if (stat & 0x00000020) { + NV_INFO(dev, "PGRAPH: ILLEGAL_CLASS ch %d [0x%010llx] subc %d " + "class 0x%04x mthd 0x%04x data 0x%08x\n", + chid, inst, subc, class, mthd, data); + nv_wr32(dev, 0x400100, 0x00000020); + stat &= ~0x00000020; + } + + if (stat & 0x00100000) { + NV_INFO(dev, "PGRAPH: DATA_ERROR ["); + nouveau_enum_print(nv50_data_error_names, code); + printk("] ch %d [0x%010llx] subc %d class 0x%04x " + "mthd 0x%04x data 0x%08x\n", + chid, inst, subc, class, mthd, data); + nv_wr32(dev, 0x400100, 0x00100000); + stat &= ~0x00100000; + } + + if (stat & 0x00200000) { + nve0_graph_trap_isr(dev, chid); + nv_wr32(dev, 0x400100, 0x00200000); + stat &= ~0x00200000; + } + + if (stat & 0x00080000) { + nve0_graph_ctxctl_isr(dev); + nv_wr32(dev, 0x400100, 0x00080000); + stat &= ~0x00080000; + } + + if (stat) { + NV_INFO(dev, "PGRAPH: unknown stat 0x%08x\n", stat); + nv_wr32(dev, 0x400100, stat); + } + + nv_wr32(dev, 0x400500, 0x00010001); +} + +static int +nve0_graph_create_fw(struct drm_device *dev, const char *fwname, + struct nve0_graph_fuc *fuc) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + const struct firmware *fw; + char f[32]; + int ret; + + snprintf(f, sizeof(f), "nouveau/nv%02x_%s", dev_priv->chipset, fwname); + ret = request_firmware(&fw, f, &dev->pdev->dev); + if (ret) + return ret; + + fuc->size = fw->size; + fuc->data = kmemdup(fw->data, fuc->size, GFP_KERNEL); + release_firmware(fw); + return (fuc->data != NULL) ? 0 : -ENOMEM; +} + +static void +nve0_graph_destroy_fw(struct nve0_graph_fuc *fuc) +{ + if (fuc->data) { + kfree(fuc->data); + fuc->data = NULL; + } +} + +static void +nve0_graph_destroy(struct drm_device *dev, int engine) +{ + struct nve0_graph_priv *priv = nv_engine(dev, engine); + + nve0_graph_destroy_fw(&priv->fuc409c); + nve0_graph_destroy_fw(&priv->fuc409d); + nve0_graph_destroy_fw(&priv->fuc41ac); + nve0_graph_destroy_fw(&priv->fuc41ad); + + nouveau_irq_unregister(dev, 12); + + nouveau_gpuobj_ref(NULL, &priv->unk4188b8); + nouveau_gpuobj_ref(NULL, &priv->unk4188b4); + + if (priv->grctx_vals) + kfree(priv->grctx_vals); + + NVOBJ_ENGINE_DEL(dev, GR); + kfree(priv); +} + +int +nve0_graph_create(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nve0_graph_priv *priv; + int ret, gpc, i; + u32 kepler; + + kepler = nve0_graph_class(dev); + if (!kepler) { + NV_ERROR(dev, "PGRAPH: unsupported chipset, please report!\n"); + return 0; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base.destroy = nve0_graph_destroy; + priv->base.init = nve0_graph_init; + priv->base.fini = nve0_graph_fini; + priv->base.context_new = nve0_graph_context_new; + priv->base.context_del = nve0_graph_context_del; + priv->base.object_new = nve0_graph_object_new; + + NVOBJ_ENGINE_ADD(dev, GR, &priv->base); + nouveau_irq_register(dev, 12, nve0_graph_isr); + + NV_INFO(dev, "PGRAPH: using external firmware\n"); + if (nve0_graph_create_fw(dev, "fuc409c", &priv->fuc409c) || + nve0_graph_create_fw(dev, "fuc409d", &priv->fuc409d) || + nve0_graph_create_fw(dev, "fuc41ac", &priv->fuc41ac) || + nve0_graph_create_fw(dev, "fuc41ad", &priv->fuc41ad)) { + ret = 0; + goto error; + } + + ret = nouveau_gpuobj_new(dev, NULL, 0x1000, 256, 0, &priv->unk4188b4); + if (ret) + goto error; + + ret = nouveau_gpuobj_new(dev, NULL, 0x1000, 256, 0, &priv->unk4188b8); + if (ret) + goto error; + + for (i = 0; i < 0x1000; i += 4) { + nv_wo32(priv->unk4188b4, i, 0x00000010); + nv_wo32(priv->unk4188b8, i, 0x00000010); + } + + priv->gpc_nr = nv_rd32(dev, 0x409604) & 0x0000001f; + priv->rop_nr = (nv_rd32(dev, 0x409604) & 0x001f0000) >> 16; + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + priv->tpc_nr[gpc] = nv_rd32(dev, GPC_UNIT(gpc, 0x2608)); + priv->tpc_total += priv->tpc_nr[gpc]; + } + + switch (dev_priv->chipset) { + case 0xe4: + if (priv->tpc_total == 8) + priv->magic_not_rop_nr = 3; + else + if (priv->tpc_total == 7) + priv->magic_not_rop_nr = 1; + break; + case 0xe7: + priv->magic_not_rop_nr = 1; + break; + default: + break; + } + + if (!priv->magic_not_rop_nr) { + NV_ERROR(dev, "PGRAPH: unknown config: %d/%d/%d/%d, %d\n", + priv->tpc_nr[0], priv->tpc_nr[1], priv->tpc_nr[2], + priv->tpc_nr[3], priv->rop_nr); + priv->magic_not_rop_nr = 0x00; + } + + NVOBJ_CLASS(dev, 0xa097, GR); /* subc 0: 3D */ + NVOBJ_CLASS(dev, 0xa0c0, GR); /* subc 1: COMPUTE */ + NVOBJ_CLASS(dev, 0xa040, GR); /* subc 2: P2MF */ + NVOBJ_CLASS(dev, 0x902d, GR); /* subc 3: 2D */ + NVOBJ_CLASS(dev, 0xa0b5, GR); /* subc 4: COPY */ + return 0; + +error: + nve0_graph_destroy(dev, NVOBJ_ENGINE_GR); + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nve0_graph.h b/drivers/gpu/drm/nouveau/nve0_graph.h new file mode 100644 index 00000000000..2ba70449ba0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nve0_graph.h @@ -0,0 +1,89 @@ +/* + * Copyright 2012 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 + */ + +#ifndef __NVE0_GRAPH_H__ +#define __NVE0_GRAPH_H__ + +#define GPC_MAX 4 +#define TPC_MAX 32 + +#define ROP_BCAST(r) (0x408800 + (r)) +#define ROP_UNIT(u, r) (0x410000 + (u) * 0x400 + (r)) +#define GPC_BCAST(r) (0x418000 + (r)) +#define GPC_UNIT(t, r) (0x500000 + (t) * 0x8000 + (r)) +#define TPC_UNIT(t, m, r) (0x504000 + (t) * 0x8000 + (m) * 0x800 + (r)) + +struct nve0_graph_fuc { + u32 *data; + u32 size; +}; + +struct nve0_graph_priv { + struct nouveau_exec_engine base; + + struct nve0_graph_fuc fuc409c; + struct nve0_graph_fuc fuc409d; + struct nve0_graph_fuc fuc41ac; + struct nve0_graph_fuc fuc41ad; + + u8 gpc_nr; + u8 rop_nr; + u8 tpc_nr[GPC_MAX]; + u8 tpc_total; + + u32 grctx_size; + u32 *grctx_vals; + struct nouveau_gpuobj *unk4188b4; + struct nouveau_gpuobj *unk4188b8; + + u8 magic_not_rop_nr; +}; + +struct nve0_graph_chan { + struct nouveau_gpuobj *grctx; + struct nouveau_gpuobj *unk408004; /* 0x418810 too */ + struct nouveau_gpuobj *unk40800c; /* 0x419004 too */ + struct nouveau_gpuobj *unk418810; /* 0x419848 too */ + struct nouveau_gpuobj *mmio; + int mmio_nr; +}; + +int nve0_grctx_generate(struct nouveau_channel *); + +/* nve0_graph.c uses this also to determine supported chipsets */ +static inline u32 +nve0_graph_class(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + + switch (dev_priv->chipset) { + case 0xe4: + case 0xe7: + return 0xa097; + default: + return 0; + } +} + +#endif diff --git a/drivers/gpu/drm/nouveau/nve0_grctx.c b/drivers/gpu/drm/nouveau/nve0_grctx.c new file mode 100644 index 00000000000..d8cb360e92c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nve0_grctx.c @@ -0,0 +1,2777 @@ +/* + * Copyright 2010 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_mm.h" +#include "nve0_graph.h" + +static void +nv_icmd(struct drm_device *dev, u32 icmd, u32 data) +{ + nv_wr32(dev, 0x400204, data); + nv_wr32(dev, 0x400200, icmd); + while (nv_rd32(dev, 0x400700) & 0x00000002) {} +} + +static void +nve0_grctx_generate_icmd(struct drm_device *dev) +{ + nv_wr32(dev, 0x400208, 0x80000000); + nv_icmd(dev, 0x001000, 0x00000004); + nv_icmd(dev, 0x000039, 0x00000000); + nv_icmd(dev, 0x00003a, 0x00000000); + nv_icmd(dev, 0x00003b, 0x00000000); + nv_icmd(dev, 0x0000a9, 0x0000ffff); + nv_icmd(dev, 0x000038, 0x0fac6881); + nv_icmd(dev, 0x00003d, 0x00000001); + nv_icmd(dev, 0x0000e8, 0x00000400); + nv_icmd(dev, 0x0000e9, 0x00000400); + nv_icmd(dev, 0x0000ea, 0x00000400); + nv_icmd(dev, 0x0000eb, 0x00000400); + nv_icmd(dev, 0x0000ec, 0x00000400); + nv_icmd(dev, 0x0000ed, 0x00000400); + nv_icmd(dev, 0x0000ee, 0x00000400); + nv_icmd(dev, 0x0000ef, 0x00000400); + nv_icmd(dev, 0x000078, 0x00000300); + nv_icmd(dev, 0x000079, 0x00000300); + nv_icmd(dev, 0x00007a, 0x00000300); + nv_icmd(dev, 0x00007b, 0x00000300); + nv_icmd(dev, 0x00007c, 0x00000300); + nv_icmd(dev, 0x00007d, 0x00000300); + nv_icmd(dev, 0x00007e, 0x00000300); + nv_icmd(dev, 0x00007f, 0x00000300); + nv_icmd(dev, 0x000050, 0x00000011); + nv_icmd(dev, 0x000058, 0x00000008); + nv_icmd(dev, 0x000059, 0x00000008); + nv_icmd(dev, 0x00005a, 0x00000008); + nv_icmd(dev, 0x00005b, 0x00000008); + nv_icmd(dev, 0x00005c, 0x00000008); + nv_icmd(dev, 0x00005d, 0x00000008); + nv_icmd(dev, 0x00005e, 0x00000008); + nv_icmd(dev, 0x00005f, 0x00000008); + nv_icmd(dev, 0x000208, 0x00000001); + nv_icmd(dev, 0x000209, 0x00000001); + nv_icmd(dev, 0x00020a, 0x00000001); + nv_icmd(dev, 0x00020b, 0x00000001); + nv_icmd(dev, 0x00020c, 0x00000001); + nv_icmd(dev, 0x00020d, 0x00000001); + nv_icmd(dev, 0x00020e, 0x00000001); + nv_icmd(dev, 0x00020f, 0x00000001); + nv_icmd(dev, 0x000081, 0x00000001); + nv_icmd(dev, 0x000085, 0x00000004); + nv_icmd(dev, 0x000088, 0x00000400); + nv_icmd(dev, 0x000090, 0x00000300); + nv_icmd(dev, 0x000098, 0x00001001); + nv_icmd(dev, 0x0000e3, 0x00000001); + nv_icmd(dev, 0x0000da, 0x00000001); + nv_icmd(dev, 0x0000f8, 0x00000003); + nv_icmd(dev, 0x0000fa, 0x00000001); + nv_icmd(dev, 0x00009f, 0x0000ffff); + nv_icmd(dev, 0x0000a0, 0x0000ffff); + nv_icmd(dev, 0x0000a1, 0x0000ffff); + nv_icmd(dev, 0x0000a2, 0x0000ffff); + nv_icmd(dev, 0x0000b1, 0x00000001); + nv_icmd(dev, 0x0000ad, 0x0000013e); + nv_icmd(dev, 0x0000e1, 0x00000010); + nv_icmd(dev, 0x000290, 0x00000000); + nv_icmd(dev, 0x000291, 0x00000000); + nv_icmd(dev, 0x000292, 0x00000000); + nv_icmd(dev, 0x000293, 0x00000000); + nv_icmd(dev, 0x000294, 0x00000000); + nv_icmd(dev, 0x000295, 0x00000000); + nv_icmd(dev, 0x000296, 0x00000000); + nv_icmd(dev, 0x000297, 0x00000000); + nv_icmd(dev, 0x000298, 0x00000000); + nv_icmd(dev, 0x000299, 0x00000000); + nv_icmd(dev, 0x00029a, 0x00000000); + nv_icmd(dev, 0x00029b, 0x00000000); + nv_icmd(dev, 0x00029c, 0x00000000); + nv_icmd(dev, 0x00029d, 0x00000000); + nv_icmd(dev, 0x00029e, 0x00000000); + nv_icmd(dev, 0x00029f, 0x00000000); + nv_icmd(dev, 0x0003b0, 0x00000000); + nv_icmd(dev, 0x0003b1, 0x00000000); + nv_icmd(dev, 0x0003b2, 0x00000000); + nv_icmd(dev, 0x0003b3, 0x00000000); + nv_icmd(dev, 0x0003b4, 0x00000000); + nv_icmd(dev, 0x0003b5, 0x00000000); + nv_icmd(dev, 0x0003b6, 0x00000000); + nv_icmd(dev, 0x0003b7, 0x00000000); + nv_icmd(dev, 0x0003b8, 0x00000000); + nv_icmd(dev, 0x0003b9, 0x00000000); + nv_icmd(dev, 0x0003ba, 0x00000000); + nv_icmd(dev, 0x0003bb, 0x00000000); + nv_icmd(dev, 0x0003bc, 0x00000000); + nv_icmd(dev, 0x0003bd, 0x00000000); + nv_icmd(dev, 0x0003be, 0x00000000); + nv_icmd(dev, 0x0003bf, 0x00000000); + nv_icmd(dev, 0x0002a0, 0x00000000); + nv_icmd(dev, 0x0002a1, 0x00000000); + nv_icmd(dev, 0x0002a2, 0x00000000); + nv_icmd(dev, 0x0002a3, 0x00000000); + nv_icmd(dev, 0x0002a4, 0x00000000); + nv_icmd(dev, 0x0002a5, 0x00000000); + nv_icmd(dev, 0x0002a6, 0x00000000); + nv_icmd(dev, 0x0002a7, 0x00000000); + nv_icmd(dev, 0x0002a8, 0x00000000); + nv_icmd(dev, 0x0002a9, 0x00000000); + nv_icmd(dev, 0x0002aa, 0x00000000); + nv_icmd(dev, 0x0002ab, 0x00000000); + nv_icmd(dev, 0x0002ac, 0x00000000); + nv_icmd(dev, 0x0002ad, 0x00000000); + nv_icmd(dev, 0x0002ae, 0x00000000); + nv_icmd(dev, 0x0002af, 0x00000000); + nv_icmd(dev, 0x000420, 0x00000000); + nv_icmd(dev, 0x000421, 0x00000000); + nv_icmd(dev, 0x000422, 0x00000000); + nv_icmd(dev, 0x000423, 0x00000000); + nv_icmd(dev, 0x000424, 0x00000000); + nv_icmd(dev, 0x000425, 0x00000000); + nv_icmd(dev, 0x000426, 0x00000000); + nv_icmd(dev, 0x000427, 0x00000000); + nv_icmd(dev, 0x000428, 0x00000000); + nv_icmd(dev, 0x000429, 0x00000000); + nv_icmd(dev, 0x00042a, 0x00000000); + nv_icmd(dev, 0x00042b, 0x00000000); + nv_icmd(dev, 0x00042c, 0x00000000); + nv_icmd(dev, 0x00042d, 0x00000000); + nv_icmd(dev, 0x00042e, 0x00000000); + nv_icmd(dev, 0x00042f, 0x00000000); + nv_icmd(dev, 0x0002b0, 0x00000000); + nv_icmd(dev, 0x0002b1, 0x00000000); + nv_icmd(dev, 0x0002b2, 0x00000000); + nv_icmd(dev, 0x0002b3, 0x00000000); + nv_icmd(dev, 0x0002b4, 0x00000000); + nv_icmd(dev, 0x0002b5, 0x00000000); + nv_icmd(dev, 0x0002b6, 0x00000000); + nv_icmd(dev, 0x0002b7, 0x00000000); + nv_icmd(dev, 0x0002b8, 0x00000000); + nv_icmd(dev, 0x0002b9, 0x00000000); + nv_icmd(dev, 0x0002ba, 0x00000000); + nv_icmd(dev, 0x0002bb, 0x00000000); + nv_icmd(dev, 0x0002bc, 0x00000000); + nv_icmd(dev, 0x0002bd, 0x00000000); + nv_icmd(dev, 0x0002be, 0x00000000); + nv_icmd(dev, 0x0002bf, 0x00000000); + nv_icmd(dev, 0x000430, 0x00000000); + nv_icmd(dev, 0x000431, 0x00000000); + nv_icmd(dev, 0x000432, 0x00000000); + nv_icmd(dev, 0x000433, 0x00000000); + nv_icmd(dev, 0x000434, 0x00000000); + nv_icmd(dev, 0x000435, 0x00000000); + nv_icmd(dev, 0x000436, 0x00000000); + nv_icmd(dev, 0x000437, 0x00000000); + nv_icmd(dev, 0x000438, 0x00000000); + nv_icmd(dev, 0x000439, 0x00000000); + nv_icmd(dev, 0x00043a, 0x00000000); + nv_icmd(dev, 0x00043b, 0x00000000); + nv_icmd(dev, 0x00043c, 0x00000000); + nv_icmd(dev, 0x00043d, 0x00000000); + nv_icmd(dev, 0x00043e, 0x00000000); + nv_icmd(dev, 0x00043f, 0x00000000); + nv_icmd(dev, 0x0002c0, 0x00000000); + nv_icmd(dev, 0x0002c1, 0x00000000); + nv_icmd(dev, 0x0002c2, 0x00000000); + nv_icmd(dev, 0x0002c3, 0x00000000); + nv_icmd(dev, 0x0002c4, 0x00000000); + nv_icmd(dev, 0x0002c5, 0x00000000); + nv_icmd(dev, 0x0002c6, 0x00000000); + nv_icmd(dev, 0x0002c7, 0x00000000); + nv_icmd(dev, 0x0002c8, 0x00000000); + nv_icmd(dev, 0x0002c9, 0x00000000); + nv_icmd(dev, 0x0002ca, 0x00000000); + nv_icmd(dev, 0x0002cb, 0x00000000); + nv_icmd(dev, 0x0002cc, 0x00000000); + nv_icmd(dev, 0x0002cd, 0x00000000); + nv_icmd(dev, 0x0002ce, 0x00000000); + nv_icmd(dev, 0x0002cf, 0x00000000); + nv_icmd(dev, 0x0004d0, 0x00000000); + nv_icmd(dev, 0x0004d1, 0x00000000); + nv_icmd(dev, 0x0004d2, 0x00000000); + nv_icmd(dev, 0x0004d3, 0x00000000); + nv_icmd(dev, 0x0004d4, 0x00000000); + nv_icmd(dev, 0x0004d5, 0x00000000); + nv_icmd(dev, 0x0004d6, 0x00000000); + nv_icmd(dev, 0x0004d7, 0x00000000); + nv_icmd(dev, 0x0004d8, 0x00000000); + nv_icmd(dev, 0x0004d9, 0x00000000); + nv_icmd(dev, 0x0004da, 0x00000000); + nv_icmd(dev, 0x0004db, 0x00000000); + nv_icmd(dev, 0x0004dc, 0x00000000); + nv_icmd(dev, 0x0004dd, 0x00000000); + nv_icmd(dev, 0x0004de, 0x00000000); + nv_icmd(dev, 0x0004df, 0x00000000); + nv_icmd(dev, 0x000720, 0x00000000); + nv_icmd(dev, 0x000721, 0x00000000); + nv_icmd(dev, 0x000722, 0x00000000); + nv_icmd(dev, 0x000723, 0x00000000); + nv_icmd(dev, 0x000724, 0x00000000); + nv_icmd(dev, 0x000725, 0x00000000); + nv_icmd(dev, 0x000726, 0x00000000); + nv_icmd(dev, 0x000727, 0x00000000); + nv_icmd(dev, 0x000728, 0x00000000); + nv_icmd(dev, 0x000729, 0x00000000); + nv_icmd(dev, 0x00072a, 0x00000000); + nv_icmd(dev, 0x00072b, 0x00000000); + nv_icmd(dev, 0x00072c, 0x00000000); + nv_icmd(dev, 0x00072d, 0x00000000); + nv_icmd(dev, 0x00072e, 0x00000000); + nv_icmd(dev, 0x00072f, 0x00000000); + nv_icmd(dev, 0x0008c0, 0x00000000); + nv_icmd(dev, 0x0008c1, 0x00000000); + nv_icmd(dev, 0x0008c2, 0x00000000); + nv_icmd(dev, 0x0008c3, 0x00000000); + nv_icmd(dev, 0x0008c4, 0x00000000); + nv_icmd(dev, 0x0008c5, 0x00000000); + nv_icmd(dev, 0x0008c6, 0x00000000); + nv_icmd(dev, 0x0008c7, 0x00000000); + nv_icmd(dev, 0x0008c8, 0x00000000); + nv_icmd(dev, 0x0008c9, 0x00000000); + nv_icmd(dev, 0x0008ca, 0x00000000); + nv_icmd(dev, 0x0008cb, 0x00000000); + nv_icmd(dev, 0x0008cc, 0x00000000); + nv_icmd(dev, 0x0008cd, 0x00000000); + nv_icmd(dev, 0x0008ce, 0x00000000); + nv_icmd(dev, 0x0008cf, 0x00000000); + nv_icmd(dev, 0x000890, 0x00000000); + nv_icmd(dev, 0x000891, 0x00000000); + nv_icmd(dev, 0x000892, 0x00000000); + nv_icmd(dev, 0x000893, 0x00000000); + nv_icmd(dev, 0x000894, 0x00000000); + nv_icmd(dev, 0x000895, 0x00000000); + nv_icmd(dev, 0x000896, 0x00000000); + nv_icmd(dev, 0x000897, 0x00000000); + nv_icmd(dev, 0x000898, 0x00000000); + nv_icmd(dev, 0x000899, 0x00000000); + nv_icmd(dev, 0x00089a, 0x00000000); + nv_icmd(dev, 0x00089b, 0x00000000); + nv_icmd(dev, 0x00089c, 0x00000000); + nv_icmd(dev, 0x00089d, 0x00000000); + nv_icmd(dev, 0x00089e, 0x00000000); + nv_icmd(dev, 0x00089f, 0x00000000); + nv_icmd(dev, 0x0008e0, 0x00000000); + nv_icmd(dev, 0x0008e1, 0x00000000); + nv_icmd(dev, 0x0008e2, 0x00000000); + nv_icmd(dev, 0x0008e3, 0x00000000); + nv_icmd(dev, 0x0008e4, 0x00000000); + nv_icmd(dev, 0x0008e5, 0x00000000); + nv_icmd(dev, 0x0008e6, 0x00000000); + nv_icmd(dev, 0x0008e7, 0x00000000); + nv_icmd(dev, 0x0008e8, 0x00000000); + nv_icmd(dev, 0x0008e9, 0x00000000); + nv_icmd(dev, 0x0008ea, 0x00000000); + nv_icmd(dev, 0x0008eb, 0x00000000); + nv_icmd(dev, 0x0008ec, 0x00000000); + nv_icmd(dev, 0x0008ed, 0x00000000); + nv_icmd(dev, 0x0008ee, 0x00000000); + nv_icmd(dev, 0x0008ef, 0x00000000); + nv_icmd(dev, 0x0008a0, 0x00000000); + nv_icmd(dev, 0x0008a1, 0x00000000); + nv_icmd(dev, 0x0008a2, 0x00000000); + nv_icmd(dev, 0x0008a3, 0x00000000); + nv_icmd(dev, 0x0008a4, 0x00000000); + nv_icmd(dev, 0x0008a5, 0x00000000); + nv_icmd(dev, 0x0008a6, 0x00000000); + nv_icmd(dev, 0x0008a7, 0x00000000); + nv_icmd(dev, 0x0008a8, 0x00000000); + nv_icmd(dev, 0x0008a9, 0x00000000); + nv_icmd(dev, 0x0008aa, 0x00000000); + nv_icmd(dev, 0x0008ab, 0x00000000); + nv_icmd(dev, 0x0008ac, 0x00000000); + nv_icmd(dev, 0x0008ad, 0x00000000); + nv_icmd(dev, 0x0008ae, 0x00000000); + nv_icmd(dev, 0x0008af, 0x00000000); + nv_icmd(dev, 0x0008f0, 0x00000000); + nv_icmd(dev, 0x0008f1, 0x00000000); + nv_icmd(dev, 0x0008f2, 0x00000000); + nv_icmd(dev, 0x0008f3, 0x00000000); + nv_icmd(dev, 0x0008f4, 0x00000000); + nv_icmd(dev, 0x0008f5, 0x00000000); + nv_icmd(dev, 0x0008f6, 0x00000000); + nv_icmd(dev, 0x0008f7, 0x00000000); + nv_icmd(dev, 0x0008f8, 0x00000000); + nv_icmd(dev, 0x0008f9, 0x00000000); + nv_icmd(dev, 0x0008fa, 0x00000000); + nv_icmd(dev, 0x0008fb, 0x00000000); + nv_icmd(dev, 0x0008fc, 0x00000000); + nv_icmd(dev, 0x0008fd, 0x00000000); + nv_icmd(dev, 0x0008fe, 0x00000000); + nv_icmd(dev, 0x0008ff, 0x00000000); + nv_icmd(dev, 0x00094c, 0x000000ff); + nv_icmd(dev, 0x00094d, 0xffffffff); + nv_icmd(dev, 0x00094e, 0x00000002); + nv_icmd(dev, 0x0002ec, 0x00000001); + nv_icmd(dev, 0x000303, 0x00000001); + nv_icmd(dev, 0x0002e6, 0x00000001); + nv_icmd(dev, 0x000466, 0x00000052); + nv_icmd(dev, 0x000301, 0x3f800000); + nv_icmd(dev, 0x000304, 0x30201000); + nv_icmd(dev, 0x000305, 0x70605040); + nv_icmd(dev, 0x000306, 0xb8a89888); + nv_icmd(dev, 0x000307, 0xf8e8d8c8); + nv_icmd(dev, 0x00030a, 0x00ffff00); + nv_icmd(dev, 0x00030b, 0x0000001a); + nv_icmd(dev, 0x00030c, 0x00000001); + nv_icmd(dev, 0x000318, 0x00000001); + nv_icmd(dev, 0x000340, 0x00000000); + nv_icmd(dev, 0x000375, 0x00000001); + nv_icmd(dev, 0x00037d, 0x00000006); + nv_icmd(dev, 0x0003a0, 0x00000002); + nv_icmd(dev, 0x0003aa, 0x00000001); + nv_icmd(dev, 0x0003a9, 0x00000001); + nv_icmd(dev, 0x000380, 0x00000001); + nv_icmd(dev, 0x000383, 0x00000011); + nv_icmd(dev, 0x000360, 0x00000040); + nv_icmd(dev, 0x000366, 0x00000000); + nv_icmd(dev, 0x000367, 0x00000000); + nv_icmd(dev, 0x000368, 0x00000fff); + nv_icmd(dev, 0x000370, 0x00000000); + nv_icmd(dev, 0x000371, 0x00000000); + nv_icmd(dev, 0x000372, 0x000fffff); + nv_icmd(dev, 0x00037a, 0x00000012); + nv_icmd(dev, 0x000619, 0x00000003); + nv_icmd(dev, 0x000811, 0x00000003); + nv_icmd(dev, 0x000812, 0x00000004); + nv_icmd(dev, 0x000813, 0x00000006); + nv_icmd(dev, 0x000814, 0x00000008); + nv_icmd(dev, 0x000815, 0x0000000b); + nv_icmd(dev, 0x000800, 0x00000001); + nv_icmd(dev, 0x000801, 0x00000001); + nv_icmd(dev, 0x000802, 0x00000001); + nv_icmd(dev, 0x000803, 0x00000001); + nv_icmd(dev, 0x000804, 0x00000001); + nv_icmd(dev, 0x000805, 0x00000001); + nv_icmd(dev, 0x000632, 0x00000001); + nv_icmd(dev, 0x000633, 0x00000002); + nv_icmd(dev, 0x000634, 0x00000003); + nv_icmd(dev, 0x000635, 0x00000004); + nv_icmd(dev, 0x000654, 0x3f800000); + nv_icmd(dev, 0x000657, 0x3f800000); + nv_icmd(dev, 0x000655, 0x3f800000); + nv_icmd(dev, 0x000656, 0x3f800000); + nv_icmd(dev, 0x0006cd, 0x3f800000); + nv_icmd(dev, 0x0007f5, 0x3f800000); + nv_icmd(dev, 0x0007dc, 0x39291909); + nv_icmd(dev, 0x0007dd, 0x79695949); + nv_icmd(dev, 0x0007de, 0xb9a99989); + nv_icmd(dev, 0x0007df, 0xf9e9d9c9); + nv_icmd(dev, 0x0007e8, 0x00003210); + nv_icmd(dev, 0x0007e9, 0x00007654); + nv_icmd(dev, 0x0007ea, 0x00000098); + nv_icmd(dev, 0x0007ec, 0x39291909); + nv_icmd(dev, 0x0007ed, 0x79695949); + nv_icmd(dev, 0x0007ee, 0xb9a99989); + nv_icmd(dev, 0x0007ef, 0xf9e9d9c9); + nv_icmd(dev, 0x0007f0, 0x00003210); + nv_icmd(dev, 0x0007f1, 0x00007654); + nv_icmd(dev, 0x0007f2, 0x00000098); + nv_icmd(dev, 0x0005a5, 0x00000001); + nv_icmd(dev, 0x000980, 0x00000000); + nv_icmd(dev, 0x000981, 0x00000000); + nv_icmd(dev, 0x000982, 0x00000000); + nv_icmd(dev, 0x000983, 0x00000000); + nv_icmd(dev, 0x000984, 0x00000000); + nv_icmd(dev, 0x000985, 0x00000000); + nv_icmd(dev, 0x000986, 0x00000000); + nv_icmd(dev, 0x000987, 0x00000000); + nv_icmd(dev, 0x000988, 0x00000000); + nv_icmd(dev, 0x000989, 0x00000000); + nv_icmd(dev, 0x00098a, 0x00000000); + nv_icmd(dev, 0x00098b, 0x00000000); + nv_icmd(dev, 0x00098c, 0x00000000); + nv_icmd(dev, 0x00098d, 0x00000000); + nv_icmd(dev, 0x00098e, 0x00000000); + nv_icmd(dev, 0x00098f, 0x00000000); + nv_icmd(dev, 0x000990, 0x00000000); + nv_icmd(dev, 0x000991, 0x00000000); + nv_icmd(dev, 0x000992, 0x00000000); + nv_icmd(dev, 0x000993, 0x00000000); + nv_icmd(dev, 0x000994, 0x00000000); + nv_icmd(dev, 0x000995, 0x00000000); + nv_icmd(dev, 0x000996, 0x00000000); + nv_icmd(dev, 0x000997, 0x00000000); + nv_icmd(dev, 0x000998, 0x00000000); + nv_icmd(dev, 0x000999, 0x00000000); + nv_icmd(dev, 0x00099a, 0x00000000); + nv_icmd(dev, 0x00099b, 0x00000000); + nv_icmd(dev, 0x00099c, 0x00000000); + nv_icmd(dev, 0x00099d, 0x00000000); + nv_icmd(dev, 0x00099e, 0x00000000); + nv_icmd(dev, 0x00099f, 0x00000000); + nv_icmd(dev, 0x0009a0, 0x00000000); + nv_icmd(dev, 0x0009a1, 0x00000000); + nv_icmd(dev, 0x0009a2, 0x00000000); + nv_icmd(dev, 0x0009a3, 0x00000000); + nv_icmd(dev, 0x0009a4, 0x00000000); + nv_icmd(dev, 0x0009a5, 0x00000000); + nv_icmd(dev, 0x0009a6, 0x00000000); + nv_icmd(dev, 0x0009a7, 0x00000000); + nv_icmd(dev, 0x0009a8, 0x00000000); + nv_icmd(dev, 0x0009a9, 0x00000000); + nv_icmd(dev, 0x0009aa, 0x00000000); + nv_icmd(dev, 0x0009ab, 0x00000000); + nv_icmd(dev, 0x0009ac, 0x00000000); + nv_icmd(dev, 0x0009ad, 0x00000000); + nv_icmd(dev, 0x0009ae, 0x00000000); + nv_icmd(dev, 0x0009af, 0x00000000); + nv_icmd(dev, 0x0009b0, 0x00000000); + nv_icmd(dev, 0x0009b1, 0x00000000); + nv_icmd(dev, 0x0009b2, 0x00000000); + nv_icmd(dev, 0x0009b3, 0x00000000); + nv_icmd(dev, 0x0009b4, 0x00000000); + nv_icmd(dev, 0x0009b5, 0x00000000); + nv_icmd(dev, 0x0009b6, 0x00000000); + nv_icmd(dev, 0x0009b7, 0x00000000); + nv_icmd(dev, 0x0009b8, 0x00000000); + nv_icmd(dev, 0x0009b9, 0x00000000); + nv_icmd(dev, 0x0009ba, 0x00000000); + nv_icmd(dev, 0x0009bb, 0x00000000); + nv_icmd(dev, 0x0009bc, 0x00000000); + nv_icmd(dev, 0x0009bd, 0x00000000); + nv_icmd(dev, 0x0009be, 0x00000000); + nv_icmd(dev, 0x0009bf, 0x00000000); + nv_icmd(dev, 0x0009c0, 0x00000000); + nv_icmd(dev, 0x0009c1, 0x00000000); + nv_icmd(dev, 0x0009c2, 0x00000000); + nv_icmd(dev, 0x0009c3, 0x00000000); + nv_icmd(dev, 0x0009c4, 0x00000000); + nv_icmd(dev, 0x0009c5, 0x00000000); + nv_icmd(dev, 0x0009c6, 0x00000000); + nv_icmd(dev, 0x0009c7, 0x00000000); + nv_icmd(dev, 0x0009c8, 0x00000000); + nv_icmd(dev, 0x0009c9, 0x00000000); + nv_icmd(dev, 0x0009ca, 0x00000000); + nv_icmd(dev, 0x0009cb, 0x00000000); + nv_icmd(dev, 0x0009cc, 0x00000000); + nv_icmd(dev, 0x0009cd, 0x00000000); + nv_icmd(dev, 0x0009ce, 0x00000000); + nv_icmd(dev, 0x0009cf, 0x00000000); + nv_icmd(dev, 0x0009d0, 0x00000000); + nv_icmd(dev, 0x0009d1, 0x00000000); + nv_icmd(dev, 0x0009d2, 0x00000000); + nv_icmd(dev, 0x0009d3, 0x00000000); + nv_icmd(dev, 0x0009d4, 0x00000000); + nv_icmd(dev, 0x0009d5, 0x00000000); + nv_icmd(dev, 0x0009d6, 0x00000000); + nv_icmd(dev, 0x0009d7, 0x00000000); + nv_icmd(dev, 0x0009d8, 0x00000000); + nv_icmd(dev, 0x0009d9, 0x00000000); + nv_icmd(dev, 0x0009da, 0x00000000); + nv_icmd(dev, 0x0009db, 0x00000000); + nv_icmd(dev, 0x0009dc, 0x00000000); + nv_icmd(dev, 0x0009dd, 0x00000000); + nv_icmd(dev, 0x0009de, 0x00000000); + nv_icmd(dev, 0x0009df, 0x00000000); + nv_icmd(dev, 0x0009e0, 0x00000000); + nv_icmd(dev, 0x0009e1, 0x00000000); + nv_icmd(dev, 0x0009e2, 0x00000000); + nv_icmd(dev, 0x0009e3, 0x00000000); + nv_icmd(dev, 0x0009e4, 0x00000000); + nv_icmd(dev, 0x0009e5, 0x00000000); + nv_icmd(dev, 0x0009e6, 0x00000000); + nv_icmd(dev, 0x0009e7, 0x00000000); + nv_icmd(dev, 0x0009e8, 0x00000000); + nv_icmd(dev, 0x0009e9, 0x00000000); + nv_icmd(dev, 0x0009ea, 0x00000000); + nv_icmd(dev, 0x0009eb, 0x00000000); + nv_icmd(dev, 0x0009ec, 0x00000000); + nv_icmd(dev, 0x0009ed, 0x00000000); + nv_icmd(dev, 0x0009ee, 0x00000000); + nv_icmd(dev, 0x0009ef, 0x00000000); + nv_icmd(dev, 0x0009f0, 0x00000000); + nv_icmd(dev, 0x0009f1, 0x00000000); + nv_icmd(dev, 0x0009f2, 0x00000000); + nv_icmd(dev, 0x0009f3, 0x00000000); + nv_icmd(dev, 0x0009f4, 0x00000000); + nv_icmd(dev, 0x0009f5, 0x00000000); + nv_icmd(dev, 0x0009f6, 0x00000000); + nv_icmd(dev, 0x0009f7, 0x00000000); + nv_icmd(dev, 0x0009f8, 0x00000000); + nv_icmd(dev, 0x0009f9, 0x00000000); + nv_icmd(dev, 0x0009fa, 0x00000000); + nv_icmd(dev, 0x0009fb, 0x00000000); + nv_icmd(dev, 0x0009fc, 0x00000000); + nv_icmd(dev, 0x0009fd, 0x00000000); + nv_icmd(dev, 0x0009fe, 0x00000000); + nv_icmd(dev, 0x0009ff, 0x00000000); + nv_icmd(dev, 0x000468, 0x00000004); + nv_icmd(dev, 0x00046c, 0x00000001); + nv_icmd(dev, 0x000470, 0x00000000); + nv_icmd(dev, 0x000471, 0x00000000); + nv_icmd(dev, 0x000472, 0x00000000); + nv_icmd(dev, 0x000473, 0x00000000); + nv_icmd(dev, 0x000474, 0x00000000); + nv_icmd(dev, 0x000475, 0x00000000); + nv_icmd(dev, 0x000476, 0x00000000); + nv_icmd(dev, 0x000477, 0x00000000); + nv_icmd(dev, 0x000478, 0x00000000); + nv_icmd(dev, 0x000479, 0x00000000); + nv_icmd(dev, 0x00047a, 0x00000000); + nv_icmd(dev, 0x00047b, 0x00000000); + nv_icmd(dev, 0x00047c, 0x00000000); + nv_icmd(dev, 0x00047d, 0x00000000); + nv_icmd(dev, 0x00047e, 0x00000000); + nv_icmd(dev, 0x00047f, 0x00000000); + nv_icmd(dev, 0x000480, 0x00000000); + nv_icmd(dev, 0x000481, 0x00000000); + nv_icmd(dev, 0x000482, 0x00000000); + nv_icmd(dev, 0x000483, 0x00000000); + nv_icmd(dev, 0x000484, 0x00000000); + nv_icmd(dev, 0x000485, 0x00000000); + nv_icmd(dev, 0x000486, 0x00000000); + nv_icmd(dev, 0x000487, 0x00000000); + nv_icmd(dev, 0x000488, 0x00000000); + nv_icmd(dev, 0x000489, 0x00000000); + nv_icmd(dev, 0x00048a, 0x00000000); + nv_icmd(dev, 0x00048b, 0x00000000); + nv_icmd(dev, 0x00048c, 0x00000000); + nv_icmd(dev, 0x00048d, 0x00000000); + nv_icmd(dev, 0x00048e, 0x00000000); + nv_icmd(dev, 0x00048f, 0x00000000); + nv_icmd(dev, 0x000490, 0x00000000); + nv_icmd(dev, 0x000491, 0x00000000); + nv_icmd(dev, 0x000492, 0x00000000); + nv_icmd(dev, 0x000493, 0x00000000); + nv_icmd(dev, 0x000494, 0x00000000); + nv_icmd(dev, 0x000495, 0x00000000); + nv_icmd(dev, 0x000496, 0x00000000); + nv_icmd(dev, 0x000497, 0x00000000); + nv_icmd(dev, 0x000498, 0x00000000); + nv_icmd(dev, 0x000499, 0x00000000); + nv_icmd(dev, 0x00049a, 0x00000000); + nv_icmd(dev, 0x00049b, 0x00000000); + nv_icmd(dev, 0x00049c, 0x00000000); + nv_icmd(dev, 0x00049d, 0x00000000); + nv_icmd(dev, 0x00049e, 0x00000000); + nv_icmd(dev, 0x00049f, 0x00000000); + nv_icmd(dev, 0x0004a0, 0x00000000); + nv_icmd(dev, 0x0004a1, 0x00000000); + nv_icmd(dev, 0x0004a2, 0x00000000); + nv_icmd(dev, 0x0004a3, 0x00000000); + nv_icmd(dev, 0x0004a4, 0x00000000); + nv_icmd(dev, 0x0004a5, 0x00000000); + nv_icmd(dev, 0x0004a6, 0x00000000); + nv_icmd(dev, 0x0004a7, 0x00000000); + nv_icmd(dev, 0x0004a8, 0x00000000); + nv_icmd(dev, 0x0004a9, 0x00000000); + nv_icmd(dev, 0x0004aa, 0x00000000); + nv_icmd(dev, 0x0004ab, 0x00000000); + nv_icmd(dev, 0x0004ac, 0x00000000); + nv_icmd(dev, 0x0004ad, 0x00000000); + nv_icmd(dev, 0x0004ae, 0x00000000); + nv_icmd(dev, 0x0004af, 0x00000000); + nv_icmd(dev, 0x0004b0, 0x00000000); + nv_icmd(dev, 0x0004b1, 0x00000000); + nv_icmd(dev, 0x0004b2, 0x00000000); + nv_icmd(dev, 0x0004b3, 0x00000000); + nv_icmd(dev, 0x0004b4, 0x00000000); + nv_icmd(dev, 0x0004b5, 0x00000000); + nv_icmd(dev, 0x0004b6, 0x00000000); + nv_icmd(dev, 0x0004b7, 0x00000000); + nv_icmd(dev, 0x0004b8, 0x00000000); + nv_icmd(dev, 0x0004b9, 0x00000000); + nv_icmd(dev, 0x0004ba, 0x00000000); + nv_icmd(dev, 0x0004bb, 0x00000000); + nv_icmd(dev, 0x0004bc, 0x00000000); + nv_icmd(dev, 0x0004bd, 0x00000000); + nv_icmd(dev, 0x0004be, 0x00000000); + nv_icmd(dev, 0x0004bf, 0x00000000); + nv_icmd(dev, 0x0004c0, 0x00000000); + nv_icmd(dev, 0x0004c1, 0x00000000); + nv_icmd(dev, 0x0004c2, 0x00000000); + nv_icmd(dev, 0x0004c3, 0x00000000); + nv_icmd(dev, 0x0004c4, 0x00000000); + nv_icmd(dev, 0x0004c5, 0x00000000); + nv_icmd(dev, 0x0004c6, 0x00000000); + nv_icmd(dev, 0x0004c7, 0x00000000); + nv_icmd(dev, 0x0004c8, 0x00000000); + nv_icmd(dev, 0x0004c9, 0x00000000); + nv_icmd(dev, 0x0004ca, 0x00000000); + nv_icmd(dev, 0x0004cb, 0x00000000); + nv_icmd(dev, 0x0004cc, 0x00000000); + nv_icmd(dev, 0x0004cd, 0x00000000); + nv_icmd(dev, 0x0004ce, 0x00000000); + nv_icmd(dev, 0x0004cf, 0x00000000); + nv_icmd(dev, 0x000510, 0x3f800000); + nv_icmd(dev, 0x000511, 0x3f800000); + nv_icmd(dev, 0x000512, 0x3f800000); + nv_icmd(dev, 0x000513, 0x3f800000); + nv_icmd(dev, 0x000514, 0x3f800000); + nv_icmd(dev, 0x000515, 0x3f800000); + nv_icmd(dev, 0x000516, 0x3f800000); + nv_icmd(dev, 0x000517, 0x3f800000); + nv_icmd(dev, 0x000518, 0x3f800000); + nv_icmd(dev, 0x000519, 0x3f800000); + nv_icmd(dev, 0x00051a, 0x3f800000); + nv_icmd(dev, 0x00051b, 0x3f800000); + nv_icmd(dev, 0x00051c, 0x3f800000); + nv_icmd(dev, 0x00051d, 0x3f800000); + nv_icmd(dev, 0x00051e, 0x3f800000); + nv_icmd(dev, 0x00051f, 0x3f800000); + nv_icmd(dev, 0x000520, 0x000002b6); + nv_icmd(dev, 0x000529, 0x00000001); + nv_icmd(dev, 0x000530, 0xffff0000); + nv_icmd(dev, 0x000531, 0xffff0000); + nv_icmd(dev, 0x000532, 0xffff0000); + nv_icmd(dev, 0x000533, 0xffff0000); + nv_icmd(dev, 0x000534, 0xffff0000); + nv_icmd(dev, 0x000535, 0xffff0000); + nv_icmd(dev, 0x000536, 0xffff0000); + nv_icmd(dev, 0x000537, 0xffff0000); + nv_icmd(dev, 0x000538, 0xffff0000); + nv_icmd(dev, 0x000539, 0xffff0000); + nv_icmd(dev, 0x00053a, 0xffff0000); + nv_icmd(dev, 0x00053b, 0xffff0000); + nv_icmd(dev, 0x00053c, 0xffff0000); + nv_icmd(dev, 0x00053d, 0xffff0000); + nv_icmd(dev, 0x00053e, 0xffff0000); + nv_icmd(dev, 0x00053f, 0xffff0000); + nv_icmd(dev, 0x000585, 0x0000003f); + nv_icmd(dev, 0x000576, 0x00000003); + nv_icmd(dev, 0x00057b, 0x00000059); + nv_icmd(dev, 0x000586, 0x00000040); + nv_icmd(dev, 0x000582, 0x00000080); + nv_icmd(dev, 0x000583, 0x00000080); + nv_icmd(dev, 0x0005c2, 0x00000001); + nv_icmd(dev, 0x000638, 0x00000001); + nv_icmd(dev, 0x000639, 0x00000001); + nv_icmd(dev, 0x00063a, 0x00000002); + nv_icmd(dev, 0x00063b, 0x00000001); + nv_icmd(dev, 0x00063c, 0x00000001); + nv_icmd(dev, 0x00063d, 0x00000002); + nv_icmd(dev, 0x00063e, 0x00000001); + nv_icmd(dev, 0x0008b8, 0x00000001); + nv_icmd(dev, 0x0008b9, 0x00000001); + nv_icmd(dev, 0x0008ba, 0x00000001); + nv_icmd(dev, 0x0008bb, 0x00000001); + nv_icmd(dev, 0x0008bc, 0x00000001); + nv_icmd(dev, 0x0008bd, 0x00000001); + nv_icmd(dev, 0x0008be, 0x00000001); + nv_icmd(dev, 0x0008bf, 0x00000001); + nv_icmd(dev, 0x000900, 0x00000001); + nv_icmd(dev, 0x000901, 0x00000001); + nv_icmd(dev, 0x000902, 0x00000001); + nv_icmd(dev, 0x000903, 0x00000001); + nv_icmd(dev, 0x000904, 0x00000001); + nv_icmd(dev, 0x000905, 0x00000001); + nv_icmd(dev, 0x000906, 0x00000001); + nv_icmd(dev, 0x000907, 0x00000001); + nv_icmd(dev, 0x000908, 0x00000002); + nv_icmd(dev, 0x000909, 0x00000002); + nv_icmd(dev, 0x00090a, 0x00000002); + nv_icmd(dev, 0x00090b, 0x00000002); + nv_icmd(dev, 0x00090c, 0x00000002); + nv_icmd(dev, 0x00090d, 0x00000002); + nv_icmd(dev, 0x00090e, 0x00000002); + nv_icmd(dev, 0x00090f, 0x00000002); + nv_icmd(dev, 0x000910, 0x00000001); + nv_icmd(dev, 0x000911, 0x00000001); + nv_icmd(dev, 0x000912, 0x00000001); + nv_icmd(dev, 0x000913, 0x00000001); + nv_icmd(dev, 0x000914, 0x00000001); + nv_icmd(dev, 0x000915, 0x00000001); + nv_icmd(dev, 0x000916, 0x00000001); + nv_icmd(dev, 0x000917, 0x00000001); + nv_icmd(dev, 0x000918, 0x00000001); + nv_icmd(dev, 0x000919, 0x00000001); + nv_icmd(dev, 0x00091a, 0x00000001); + nv_icmd(dev, 0x00091b, 0x00000001); + nv_icmd(dev, 0x00091c, 0x00000001); + nv_icmd(dev, 0x00091d, 0x00000001); + nv_icmd(dev, 0x00091e, 0x00000001); + nv_icmd(dev, 0x00091f, 0x00000001); + nv_icmd(dev, 0x000920, 0x00000002); + nv_icmd(dev, 0x000921, 0x00000002); + nv_icmd(dev, 0x000922, 0x00000002); + nv_icmd(dev, 0x000923, 0x00000002); + nv_icmd(dev, 0x000924, 0x00000002); + nv_icmd(dev, 0x000925, 0x00000002); + nv_icmd(dev, 0x000926, 0x00000002); + nv_icmd(dev, 0x000927, 0x00000002); + nv_icmd(dev, 0x000928, 0x00000001); + nv_icmd(dev, 0x000929, 0x00000001); + nv_icmd(dev, 0x00092a, 0x00000001); + nv_icmd(dev, 0x00092b, 0x00000001); + nv_icmd(dev, 0x00092c, 0x00000001); + nv_icmd(dev, 0x00092d, 0x00000001); + nv_icmd(dev, 0x00092e, 0x00000001); + nv_icmd(dev, 0x00092f, 0x00000001); + nv_icmd(dev, 0x000648, 0x00000001); + nv_icmd(dev, 0x000649, 0x00000001); + nv_icmd(dev, 0x00064a, 0x00000001); + nv_icmd(dev, 0x00064b, 0x00000001); + nv_icmd(dev, 0x00064c, 0x00000001); + nv_icmd(dev, 0x00064d, 0x00000001); + nv_icmd(dev, 0x00064e, 0x00000001); + nv_icmd(dev, 0x00064f, 0x00000001); + nv_icmd(dev, 0x000650, 0x00000001); + nv_icmd(dev, 0x000658, 0x0000000f); + nv_icmd(dev, 0x0007ff, 0x0000000a); + nv_icmd(dev, 0x00066a, 0x40000000); + nv_icmd(dev, 0x00066b, 0x10000000); + nv_icmd(dev, 0x00066c, 0xffff0000); + nv_icmd(dev, 0x00066d, 0xffff0000); + nv_icmd(dev, 0x0007af, 0x00000008); + nv_icmd(dev, 0x0007b0, 0x00000008); + nv_icmd(dev, 0x0007f6, 0x00000001); + nv_icmd(dev, 0x0006b2, 0x00000055); + nv_icmd(dev, 0x0007ad, 0x00000003); + nv_icmd(dev, 0x000937, 0x00000001); + nv_icmd(dev, 0x000971, 0x00000008); + nv_icmd(dev, 0x000972, 0x00000040); + nv_icmd(dev, 0x000973, 0x0000012c); + nv_icmd(dev, 0x00097c, 0x00000040); + nv_icmd(dev, 0x000979, 0x00000003); + nv_icmd(dev, 0x000975, 0x00000020); + nv_icmd(dev, 0x000976, 0x00000001); + nv_icmd(dev, 0x000977, 0x00000020); + nv_icmd(dev, 0x000978, 0x00000001); + nv_icmd(dev, 0x000957, 0x00000003); + nv_icmd(dev, 0x00095e, 0x20164010); + nv_icmd(dev, 0x00095f, 0x00000020); + nv_icmd(dev, 0x00097d, 0x00000020); + nv_icmd(dev, 0x000683, 0x00000006); + nv_icmd(dev, 0x000685, 0x003fffff); + nv_icmd(dev, 0x000687, 0x003fffff); + nv_icmd(dev, 0x0006a0, 0x00000005); + nv_icmd(dev, 0x000840, 0x00400008); + nv_icmd(dev, 0x000841, 0x08000080); + nv_icmd(dev, 0x000842, 0x00400008); + nv_icmd(dev, 0x000843, 0x08000080); + nv_icmd(dev, 0x000818, 0x00000000); + nv_icmd(dev, 0x000819, 0x00000000); + nv_icmd(dev, 0x00081a, 0x00000000); + nv_icmd(dev, 0x00081b, 0x00000000); + nv_icmd(dev, 0x00081c, 0x00000000); + nv_icmd(dev, 0x00081d, 0x00000000); + nv_icmd(dev, 0x00081e, 0x00000000); + nv_icmd(dev, 0x00081f, 0x00000000); + nv_icmd(dev, 0x000848, 0x00000000); + nv_icmd(dev, 0x000849, 0x00000000); + nv_icmd(dev, 0x00084a, 0x00000000); + nv_icmd(dev, 0x00084b, 0x00000000); + nv_icmd(dev, 0x00084c, 0x00000000); + nv_icmd(dev, 0x00084d, 0x00000000); + nv_icmd(dev, 0x00084e, 0x00000000); + nv_icmd(dev, 0x00084f, 0x00000000); + nv_icmd(dev, 0x000850, 0x00000000); + nv_icmd(dev, 0x000851, 0x00000000); + nv_icmd(dev, 0x000852, 0x00000000); + nv_icmd(dev, 0x000853, 0x00000000); + nv_icmd(dev, 0x000854, 0x00000000); + nv_icmd(dev, 0x000855, 0x00000000); + nv_icmd(dev, 0x000856, 0x00000000); + nv_icmd(dev, 0x000857, 0x00000000); + nv_icmd(dev, 0x000738, 0x00000000); + nv_icmd(dev, 0x0006aa, 0x00000001); + nv_icmd(dev, 0x0006ab, 0x00000002); + nv_icmd(dev, 0x0006ac, 0x00000080); + nv_icmd(dev, 0x0006ad, 0x00000100); + nv_icmd(dev, 0x0006ae, 0x00000100); + nv_icmd(dev, 0x0006b1, 0x00000011); + nv_icmd(dev, 0x0006bb, 0x000000cf); + nv_icmd(dev, 0x0006ce, 0x2a712488); + nv_icmd(dev, 0x000739, 0x4085c000); + nv_icmd(dev, 0x00073a, 0x00000080); + nv_icmd(dev, 0x000786, 0x80000100); + nv_icmd(dev, 0x00073c, 0x00010100); + nv_icmd(dev, 0x00073d, 0x02800000); + nv_icmd(dev, 0x000787, 0x000000cf); + nv_icmd(dev, 0x00078c, 0x00000008); + nv_icmd(dev, 0x000792, 0x00000001); + nv_icmd(dev, 0x000794, 0x00000001); + nv_icmd(dev, 0x000795, 0x00000001); + nv_icmd(dev, 0x000796, 0x00000001); + nv_icmd(dev, 0x000797, 0x000000cf); + nv_icmd(dev, 0x000836, 0x00000001); + nv_icmd(dev, 0x00079a, 0x00000002); + nv_icmd(dev, 0x000833, 0x04444480); + nv_icmd(dev, 0x0007a1, 0x00000001); + nv_icmd(dev, 0x0007a3, 0x00000001); + nv_icmd(dev, 0x0007a4, 0x00000001); + nv_icmd(dev, 0x0007a5, 0x00000001); + nv_icmd(dev, 0x000831, 0x00000004); + nv_icmd(dev, 0x000b07, 0x00000002); + nv_icmd(dev, 0x000b08, 0x00000100); + nv_icmd(dev, 0x000b09, 0x00000100); + nv_icmd(dev, 0x000b0a, 0x00000001); + nv_icmd(dev, 0x000a04, 0x000000ff); + nv_icmd(dev, 0x000a0b, 0x00000040); + nv_icmd(dev, 0x00097f, 0x00000100); + nv_icmd(dev, 0x000a02, 0x00000001); + nv_icmd(dev, 0x000809, 0x00000007); + nv_icmd(dev, 0x00c221, 0x00000040); + nv_icmd(dev, 0x00c1b0, 0x0000000f); + nv_icmd(dev, 0x00c1b1, 0x0000000f); + nv_icmd(dev, 0x00c1b2, 0x0000000f); + nv_icmd(dev, 0x00c1b3, 0x0000000f); + nv_icmd(dev, 0x00c1b4, 0x0000000f); + nv_icmd(dev, 0x00c1b5, 0x0000000f); + nv_icmd(dev, 0x00c1b6, 0x0000000f); + nv_icmd(dev, 0x00c1b7, 0x0000000f); + nv_icmd(dev, 0x00c1b8, 0x0fac6881); + nv_icmd(dev, 0x00c1b9, 0x00fac688); + nv_icmd(dev, 0x00c401, 0x00000001); + nv_icmd(dev, 0x00c402, 0x00010001); + nv_icmd(dev, 0x00c403, 0x00000001); + nv_icmd(dev, 0x00c404, 0x00000001); + nv_icmd(dev, 0x00c40e, 0x00000020); + nv_icmd(dev, 0x00c500, 0x00000003); + nv_icmd(dev, 0x01e100, 0x00000001); + nv_icmd(dev, 0x001000, 0x00000002); + nv_icmd(dev, 0x0006aa, 0x00000001); + nv_icmd(dev, 0x0006ad, 0x00000100); + nv_icmd(dev, 0x0006ae, 0x00000100); + nv_icmd(dev, 0x0006b1, 0x00000011); + nv_icmd(dev, 0x00078c, 0x00000008); + nv_icmd(dev, 0x000792, 0x00000001); + nv_icmd(dev, 0x000794, 0x00000001); + nv_icmd(dev, 0x000795, 0x00000001); + nv_icmd(dev, 0x000796, 0x00000001); + nv_icmd(dev, 0x000797, 0x000000cf); + nv_icmd(dev, 0x00079a, 0x00000002); + nv_icmd(dev, 0x000833, 0x04444480); + nv_icmd(dev, 0x0007a1, 0x00000001); + nv_icmd(dev, 0x0007a3, 0x00000001); + nv_icmd(dev, 0x0007a4, 0x00000001); + nv_icmd(dev, 0x0007a5, 0x00000001); + nv_icmd(dev, 0x000831, 0x00000004); + nv_icmd(dev, 0x01e100, 0x00000001); + nv_icmd(dev, 0x001000, 0x00000008); + nv_icmd(dev, 0x000039, 0x00000000); + nv_icmd(dev, 0x00003a, 0x00000000); + nv_icmd(dev, 0x00003b, 0x00000000); + nv_icmd(dev, 0x000380, 0x00000001); + nv_icmd(dev, 0x000366, 0x00000000); + nv_icmd(dev, 0x000367, 0x00000000); + nv_icmd(dev, 0x000368, 0x00000fff); + nv_icmd(dev, 0x000370, 0x00000000); + nv_icmd(dev, 0x000371, 0x00000000); + nv_icmd(dev, 0x000372, 0x000fffff); + nv_icmd(dev, 0x000813, 0x00000006); + nv_icmd(dev, 0x000814, 0x00000008); + nv_icmd(dev, 0x000957, 0x00000003); + nv_icmd(dev, 0x000818, 0x00000000); + nv_icmd(dev, 0x000819, 0x00000000); + nv_icmd(dev, 0x00081a, 0x00000000); + nv_icmd(dev, 0x00081b, 0x00000000); + nv_icmd(dev, 0x00081c, 0x00000000); + nv_icmd(dev, 0x00081d, 0x00000000); + nv_icmd(dev, 0x00081e, 0x00000000); + nv_icmd(dev, 0x00081f, 0x00000000); + nv_icmd(dev, 0x000848, 0x00000000); + nv_icmd(dev, 0x000849, 0x00000000); + nv_icmd(dev, 0x00084a, 0x00000000); + nv_icmd(dev, 0x00084b, 0x00000000); + nv_icmd(dev, 0x00084c, 0x00000000); + nv_icmd(dev, 0x00084d, 0x00000000); + nv_icmd(dev, 0x00084e, 0x00000000); + nv_icmd(dev, 0x00084f, 0x00000000); + nv_icmd(dev, 0x000850, 0x00000000); + nv_icmd(dev, 0x000851, 0x00000000); + nv_icmd(dev, 0x000852, 0x00000000); + nv_icmd(dev, 0x000853, 0x00000000); + nv_icmd(dev, 0x000854, 0x00000000); + nv_icmd(dev, 0x000855, 0x00000000); + nv_icmd(dev, 0x000856, 0x00000000); + nv_icmd(dev, 0x000857, 0x00000000); + nv_icmd(dev, 0x000738, 0x00000000); + nv_icmd(dev, 0x000b07, 0x00000002); + nv_icmd(dev, 0x000b08, 0x00000100); + nv_icmd(dev, 0x000b09, 0x00000100); + nv_icmd(dev, 0x000b0a, 0x00000001); + nv_icmd(dev, 0x000a04, 0x000000ff); + nv_icmd(dev, 0x00097f, 0x00000100); + nv_icmd(dev, 0x000a02, 0x00000001); + nv_icmd(dev, 0x000809, 0x00000007); + nv_icmd(dev, 0x00c221, 0x00000040); + nv_icmd(dev, 0x00c401, 0x00000001); + nv_icmd(dev, 0x00c402, 0x00010001); + nv_icmd(dev, 0x00c403, 0x00000001); + nv_icmd(dev, 0x00c404, 0x00000001); + nv_icmd(dev, 0x00c40e, 0x00000020); + nv_icmd(dev, 0x00c500, 0x00000003); + nv_icmd(dev, 0x01e100, 0x00000001); + nv_icmd(dev, 0x001000, 0x00000001); + nv_icmd(dev, 0x000b07, 0x00000002); + nv_icmd(dev, 0x000b08, 0x00000100); + nv_icmd(dev, 0x000b09, 0x00000100); + nv_icmd(dev, 0x000b0a, 0x00000001); + nv_icmd(dev, 0x01e100, 0x00000001); + nv_wr32(dev, 0x400208, 0x00000000); +} + +static void +nv_mthd(struct drm_device *dev, u32 class, u32 mthd, u32 data) +{ + nv_wr32(dev, 0x40448c, data); + nv_wr32(dev, 0x404488, 0x80000000 | (mthd << 14) | class); +} + +static void +nve0_grctx_generate_a097(struct drm_device *dev) +{ + nv_mthd(dev, 0xa097, 0x0800, 0x00000000); + nv_mthd(dev, 0xa097, 0x0840, 0x00000000); + nv_mthd(dev, 0xa097, 0x0880, 0x00000000); + nv_mthd(dev, 0xa097, 0x08c0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0900, 0x00000000); + nv_mthd(dev, 0xa097, 0x0940, 0x00000000); + nv_mthd(dev, 0xa097, 0x0980, 0x00000000); + nv_mthd(dev, 0xa097, 0x09c0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0804, 0x00000000); + nv_mthd(dev, 0xa097, 0x0844, 0x00000000); + nv_mthd(dev, 0xa097, 0x0884, 0x00000000); + nv_mthd(dev, 0xa097, 0x08c4, 0x00000000); + nv_mthd(dev, 0xa097, 0x0904, 0x00000000); + nv_mthd(dev, 0xa097, 0x0944, 0x00000000); + nv_mthd(dev, 0xa097, 0x0984, 0x00000000); + nv_mthd(dev, 0xa097, 0x09c4, 0x00000000); + nv_mthd(dev, 0xa097, 0x0808, 0x00000400); + nv_mthd(dev, 0xa097, 0x0848, 0x00000400); + nv_mthd(dev, 0xa097, 0x0888, 0x00000400); + nv_mthd(dev, 0xa097, 0x08c8, 0x00000400); + nv_mthd(dev, 0xa097, 0x0908, 0x00000400); + nv_mthd(dev, 0xa097, 0x0948, 0x00000400); + nv_mthd(dev, 0xa097, 0x0988, 0x00000400); + nv_mthd(dev, 0xa097, 0x09c8, 0x00000400); + nv_mthd(dev, 0xa097, 0x080c, 0x00000300); + nv_mthd(dev, 0xa097, 0x084c, 0x00000300); + nv_mthd(dev, 0xa097, 0x088c, 0x00000300); + nv_mthd(dev, 0xa097, 0x08cc, 0x00000300); + nv_mthd(dev, 0xa097, 0x090c, 0x00000300); + nv_mthd(dev, 0xa097, 0x094c, 0x00000300); + nv_mthd(dev, 0xa097, 0x098c, 0x00000300); + nv_mthd(dev, 0xa097, 0x09cc, 0x00000300); + nv_mthd(dev, 0xa097, 0x0810, 0x000000cf); + nv_mthd(dev, 0xa097, 0x0850, 0x00000000); + nv_mthd(dev, 0xa097, 0x0890, 0x00000000); + nv_mthd(dev, 0xa097, 0x08d0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0910, 0x00000000); + nv_mthd(dev, 0xa097, 0x0950, 0x00000000); + nv_mthd(dev, 0xa097, 0x0990, 0x00000000); + nv_mthd(dev, 0xa097, 0x09d0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0814, 0x00000040); + nv_mthd(dev, 0xa097, 0x0854, 0x00000040); + nv_mthd(dev, 0xa097, 0x0894, 0x00000040); + nv_mthd(dev, 0xa097, 0x08d4, 0x00000040); + nv_mthd(dev, 0xa097, 0x0914, 0x00000040); + nv_mthd(dev, 0xa097, 0x0954, 0x00000040); + nv_mthd(dev, 0xa097, 0x0994, 0x00000040); + nv_mthd(dev, 0xa097, 0x09d4, 0x00000040); + nv_mthd(dev, 0xa097, 0x0818, 0x00000001); + nv_mthd(dev, 0xa097, 0x0858, 0x00000001); + nv_mthd(dev, 0xa097, 0x0898, 0x00000001); + nv_mthd(dev, 0xa097, 0x08d8, 0x00000001); + nv_mthd(dev, 0xa097, 0x0918, 0x00000001); + nv_mthd(dev, 0xa097, 0x0958, 0x00000001); + nv_mthd(dev, 0xa097, 0x0998, 0x00000001); + nv_mthd(dev, 0xa097, 0x09d8, 0x00000001); + nv_mthd(dev, 0xa097, 0x081c, 0x00000000); + nv_mthd(dev, 0xa097, 0x085c, 0x00000000); + nv_mthd(dev, 0xa097, 0x089c, 0x00000000); + nv_mthd(dev, 0xa097, 0x08dc, 0x00000000); + nv_mthd(dev, 0xa097, 0x091c, 0x00000000); + nv_mthd(dev, 0xa097, 0x095c, 0x00000000); + nv_mthd(dev, 0xa097, 0x099c, 0x00000000); + nv_mthd(dev, 0xa097, 0x09dc, 0x00000000); + nv_mthd(dev, 0xa097, 0x0820, 0x00000000); + nv_mthd(dev, 0xa097, 0x0860, 0x00000000); + nv_mthd(dev, 0xa097, 0x08a0, 0x00000000); + nv_mthd(dev, 0xa097, 0x08e0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0920, 0x00000000); + nv_mthd(dev, 0xa097, 0x0960, 0x00000000); + nv_mthd(dev, 0xa097, 0x09a0, 0x00000000); + nv_mthd(dev, 0xa097, 0x09e0, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c00, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c10, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c20, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c30, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c40, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c50, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c60, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c70, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c80, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c90, 0x00000000); + nv_mthd(dev, 0xa097, 0x1ca0, 0x00000000); + nv_mthd(dev, 0xa097, 0x1cb0, 0x00000000); + nv_mthd(dev, 0xa097, 0x1cc0, 0x00000000); + nv_mthd(dev, 0xa097, 0x1cd0, 0x00000000); + nv_mthd(dev, 0xa097, 0x1ce0, 0x00000000); + nv_mthd(dev, 0xa097, 0x1cf0, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c04, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c14, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c24, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c34, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c44, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c54, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c64, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c74, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c84, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c94, 0x00000000); + nv_mthd(dev, 0xa097, 0x1ca4, 0x00000000); + nv_mthd(dev, 0xa097, 0x1cb4, 0x00000000); + nv_mthd(dev, 0xa097, 0x1cc4, 0x00000000); + nv_mthd(dev, 0xa097, 0x1cd4, 0x00000000); + nv_mthd(dev, 0xa097, 0x1ce4, 0x00000000); + nv_mthd(dev, 0xa097, 0x1cf4, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c08, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c18, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c28, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c38, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c48, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c58, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c68, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c78, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c88, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c98, 0x00000000); + nv_mthd(dev, 0xa097, 0x1ca8, 0x00000000); + nv_mthd(dev, 0xa097, 0x1cb8, 0x00000000); + nv_mthd(dev, 0xa097, 0x1cc8, 0x00000000); + nv_mthd(dev, 0xa097, 0x1cd8, 0x00000000); + nv_mthd(dev, 0xa097, 0x1ce8, 0x00000000); + nv_mthd(dev, 0xa097, 0x1cf8, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c0c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c1c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c2c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c3c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c4c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c5c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c6c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c7c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c8c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1c9c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1cac, 0x00000000); + nv_mthd(dev, 0xa097, 0x1cbc, 0x00000000); + nv_mthd(dev, 0xa097, 0x1ccc, 0x00000000); + nv_mthd(dev, 0xa097, 0x1cdc, 0x00000000); + nv_mthd(dev, 0xa097, 0x1cec, 0x00000000); + nv_mthd(dev, 0xa097, 0x1cfc, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d00, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d10, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d20, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d30, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d40, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d50, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d60, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d70, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d80, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d90, 0x00000000); + nv_mthd(dev, 0xa097, 0x1da0, 0x00000000); + nv_mthd(dev, 0xa097, 0x1db0, 0x00000000); + nv_mthd(dev, 0xa097, 0x1dc0, 0x00000000); + nv_mthd(dev, 0xa097, 0x1dd0, 0x00000000); + nv_mthd(dev, 0xa097, 0x1de0, 0x00000000); + nv_mthd(dev, 0xa097, 0x1df0, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d04, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d14, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d24, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d34, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d44, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d54, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d64, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d74, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d84, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d94, 0x00000000); + nv_mthd(dev, 0xa097, 0x1da4, 0x00000000); + nv_mthd(dev, 0xa097, 0x1db4, 0x00000000); + nv_mthd(dev, 0xa097, 0x1dc4, 0x00000000); + nv_mthd(dev, 0xa097, 0x1dd4, 0x00000000); + nv_mthd(dev, 0xa097, 0x1de4, 0x00000000); + nv_mthd(dev, 0xa097, 0x1df4, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d08, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d18, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d28, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d38, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d48, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d58, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d68, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d78, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d88, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d98, 0x00000000); + nv_mthd(dev, 0xa097, 0x1da8, 0x00000000); + nv_mthd(dev, 0xa097, 0x1db8, 0x00000000); + nv_mthd(dev, 0xa097, 0x1dc8, 0x00000000); + nv_mthd(dev, 0xa097, 0x1dd8, 0x00000000); + nv_mthd(dev, 0xa097, 0x1de8, 0x00000000); + nv_mthd(dev, 0xa097, 0x1df8, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d0c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d1c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d2c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d3c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d4c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d5c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d6c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d7c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d8c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1d9c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1dac, 0x00000000); + nv_mthd(dev, 0xa097, 0x1dbc, 0x00000000); + nv_mthd(dev, 0xa097, 0x1dcc, 0x00000000); + nv_mthd(dev, 0xa097, 0x1ddc, 0x00000000); + nv_mthd(dev, 0xa097, 0x1dec, 0x00000000); + nv_mthd(dev, 0xa097, 0x1dfc, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f00, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f08, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f10, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f18, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f20, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f28, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f30, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f38, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f40, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f48, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f50, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f58, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f60, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f68, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f70, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f78, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f04, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f0c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f14, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f1c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f24, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f2c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f34, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f3c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f44, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f4c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f54, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f5c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f64, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f6c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f74, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f7c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f80, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f88, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f90, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f98, 0x00000000); + nv_mthd(dev, 0xa097, 0x1fa0, 0x00000000); + nv_mthd(dev, 0xa097, 0x1fa8, 0x00000000); + nv_mthd(dev, 0xa097, 0x1fb0, 0x00000000); + nv_mthd(dev, 0xa097, 0x1fb8, 0x00000000); + nv_mthd(dev, 0xa097, 0x1fc0, 0x00000000); + nv_mthd(dev, 0xa097, 0x1fc8, 0x00000000); + nv_mthd(dev, 0xa097, 0x1fd0, 0x00000000); + nv_mthd(dev, 0xa097, 0x1fd8, 0x00000000); + nv_mthd(dev, 0xa097, 0x1fe0, 0x00000000); + nv_mthd(dev, 0xa097, 0x1fe8, 0x00000000); + nv_mthd(dev, 0xa097, 0x1ff0, 0x00000000); + nv_mthd(dev, 0xa097, 0x1ff8, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f84, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f8c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f94, 0x00000000); + nv_mthd(dev, 0xa097, 0x1f9c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1fa4, 0x00000000); + nv_mthd(dev, 0xa097, 0x1fac, 0x00000000); + nv_mthd(dev, 0xa097, 0x1fb4, 0x00000000); + nv_mthd(dev, 0xa097, 0x1fbc, 0x00000000); + nv_mthd(dev, 0xa097, 0x1fc4, 0x00000000); + nv_mthd(dev, 0xa097, 0x1fcc, 0x00000000); + nv_mthd(dev, 0xa097, 0x1fd4, 0x00000000); + nv_mthd(dev, 0xa097, 0x1fdc, 0x00000000); + nv_mthd(dev, 0xa097, 0x1fe4, 0x00000000); + nv_mthd(dev, 0xa097, 0x1fec, 0x00000000); + nv_mthd(dev, 0xa097, 0x1ff4, 0x00000000); + nv_mthd(dev, 0xa097, 0x1ffc, 0x00000000); + nv_mthd(dev, 0xa097, 0x2000, 0x00000000); + nv_mthd(dev, 0xa097, 0x2040, 0x00000011); + nv_mthd(dev, 0xa097, 0x2080, 0x00000020); + nv_mthd(dev, 0xa097, 0x20c0, 0x00000030); + nv_mthd(dev, 0xa097, 0x2100, 0x00000040); + nv_mthd(dev, 0xa097, 0x2140, 0x00000051); + nv_mthd(dev, 0xa097, 0x200c, 0x00000001); + nv_mthd(dev, 0xa097, 0x204c, 0x00000001); + nv_mthd(dev, 0xa097, 0x208c, 0x00000001); + nv_mthd(dev, 0xa097, 0x20cc, 0x00000001); + nv_mthd(dev, 0xa097, 0x210c, 0x00000001); + nv_mthd(dev, 0xa097, 0x214c, 0x00000001); + nv_mthd(dev, 0xa097, 0x2010, 0x00000000); + nv_mthd(dev, 0xa097, 0x2050, 0x00000000); + nv_mthd(dev, 0xa097, 0x2090, 0x00000001); + nv_mthd(dev, 0xa097, 0x20d0, 0x00000002); + nv_mthd(dev, 0xa097, 0x2110, 0x00000003); + nv_mthd(dev, 0xa097, 0x2150, 0x00000004); + nv_mthd(dev, 0xa097, 0x0380, 0x00000000); + nv_mthd(dev, 0xa097, 0x03a0, 0x00000000); + nv_mthd(dev, 0xa097, 0x03c0, 0x00000000); + nv_mthd(dev, 0xa097, 0x03e0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0384, 0x00000000); + nv_mthd(dev, 0xa097, 0x03a4, 0x00000000); + nv_mthd(dev, 0xa097, 0x03c4, 0x00000000); + nv_mthd(dev, 0xa097, 0x03e4, 0x00000000); + nv_mthd(dev, 0xa097, 0x0388, 0x00000000); + nv_mthd(dev, 0xa097, 0x03a8, 0x00000000); + nv_mthd(dev, 0xa097, 0x03c8, 0x00000000); + nv_mthd(dev, 0xa097, 0x03e8, 0x00000000); + nv_mthd(dev, 0xa097, 0x038c, 0x00000000); + nv_mthd(dev, 0xa097, 0x03ac, 0x00000000); + nv_mthd(dev, 0xa097, 0x03cc, 0x00000000); + nv_mthd(dev, 0xa097, 0x03ec, 0x00000000); + nv_mthd(dev, 0xa097, 0x0700, 0x00000000); + nv_mthd(dev, 0xa097, 0x0710, 0x00000000); + nv_mthd(dev, 0xa097, 0x0720, 0x00000000); + nv_mthd(dev, 0xa097, 0x0730, 0x00000000); + nv_mthd(dev, 0xa097, 0x0704, 0x00000000); + nv_mthd(dev, 0xa097, 0x0714, 0x00000000); + nv_mthd(dev, 0xa097, 0x0724, 0x00000000); + nv_mthd(dev, 0xa097, 0x0734, 0x00000000); + nv_mthd(dev, 0xa097, 0x0708, 0x00000000); + nv_mthd(dev, 0xa097, 0x0718, 0x00000000); + nv_mthd(dev, 0xa097, 0x0728, 0x00000000); + nv_mthd(dev, 0xa097, 0x0738, 0x00000000); + nv_mthd(dev, 0xa097, 0x2800, 0x00000000); + nv_mthd(dev, 0xa097, 0x2804, 0x00000000); + nv_mthd(dev, 0xa097, 0x2808, 0x00000000); + nv_mthd(dev, 0xa097, 0x280c, 0x00000000); + nv_mthd(dev, 0xa097, 0x2810, 0x00000000); + nv_mthd(dev, 0xa097, 0x2814, 0x00000000); + nv_mthd(dev, 0xa097, 0x2818, 0x00000000); + nv_mthd(dev, 0xa097, 0x281c, 0x00000000); + nv_mthd(dev, 0xa097, 0x2820, 0x00000000); + nv_mthd(dev, 0xa097, 0x2824, 0x00000000); + nv_mthd(dev, 0xa097, 0x2828, 0x00000000); + nv_mthd(dev, 0xa097, 0x282c, 0x00000000); + nv_mthd(dev, 0xa097, 0x2830, 0x00000000); + nv_mthd(dev, 0xa097, 0x2834, 0x00000000); + nv_mthd(dev, 0xa097, 0x2838, 0x00000000); + nv_mthd(dev, 0xa097, 0x283c, 0x00000000); + nv_mthd(dev, 0xa097, 0x2840, 0x00000000); + nv_mthd(dev, 0xa097, 0x2844, 0x00000000); + nv_mthd(dev, 0xa097, 0x2848, 0x00000000); + nv_mthd(dev, 0xa097, 0x284c, 0x00000000); + nv_mthd(dev, 0xa097, 0x2850, 0x00000000); + nv_mthd(dev, 0xa097, 0x2854, 0x00000000); + nv_mthd(dev, 0xa097, 0x2858, 0x00000000); + nv_mthd(dev, 0xa097, 0x285c, 0x00000000); + nv_mthd(dev, 0xa097, 0x2860, 0x00000000); + nv_mthd(dev, 0xa097, 0x2864, 0x00000000); + nv_mthd(dev, 0xa097, 0x2868, 0x00000000); + nv_mthd(dev, 0xa097, 0x286c, 0x00000000); + nv_mthd(dev, 0xa097, 0x2870, 0x00000000); + nv_mthd(dev, 0xa097, 0x2874, 0x00000000); + nv_mthd(dev, 0xa097, 0x2878, 0x00000000); + nv_mthd(dev, 0xa097, 0x287c, 0x00000000); + nv_mthd(dev, 0xa097, 0x2880, 0x00000000); + nv_mthd(dev, 0xa097, 0x2884, 0x00000000); + nv_mthd(dev, 0xa097, 0x2888, 0x00000000); + nv_mthd(dev, 0xa097, 0x288c, 0x00000000); + nv_mthd(dev, 0xa097, 0x2890, 0x00000000); + nv_mthd(dev, 0xa097, 0x2894, 0x00000000); + nv_mthd(dev, 0xa097, 0x2898, 0x00000000); + nv_mthd(dev, 0xa097, 0x289c, 0x00000000); + nv_mthd(dev, 0xa097, 0x28a0, 0x00000000); + nv_mthd(dev, 0xa097, 0x28a4, 0x00000000); + nv_mthd(dev, 0xa097, 0x28a8, 0x00000000); + nv_mthd(dev, 0xa097, 0x28ac, 0x00000000); + nv_mthd(dev, 0xa097, 0x28b0, 0x00000000); + nv_mthd(dev, 0xa097, 0x28b4, 0x00000000); + nv_mthd(dev, 0xa097, 0x28b8, 0x00000000); + nv_mthd(dev, 0xa097, 0x28bc, 0x00000000); + nv_mthd(dev, 0xa097, 0x28c0, 0x00000000); + nv_mthd(dev, 0xa097, 0x28c4, 0x00000000); + nv_mthd(dev, 0xa097, 0x28c8, 0x00000000); + nv_mthd(dev, 0xa097, 0x28cc, 0x00000000); + nv_mthd(dev, 0xa097, 0x28d0, 0x00000000); + nv_mthd(dev, 0xa097, 0x28d4, 0x00000000); + nv_mthd(dev, 0xa097, 0x28d8, 0x00000000); + nv_mthd(dev, 0xa097, 0x28dc, 0x00000000); + nv_mthd(dev, 0xa097, 0x28e0, 0x00000000); + nv_mthd(dev, 0xa097, 0x28e4, 0x00000000); + nv_mthd(dev, 0xa097, 0x28e8, 0x00000000); + nv_mthd(dev, 0xa097, 0x28ec, 0x00000000); + nv_mthd(dev, 0xa097, 0x28f0, 0x00000000); + nv_mthd(dev, 0xa097, 0x28f4, 0x00000000); + nv_mthd(dev, 0xa097, 0x28f8, 0x00000000); + nv_mthd(dev, 0xa097, 0x28fc, 0x00000000); + nv_mthd(dev, 0xa097, 0x2900, 0x00000000); + nv_mthd(dev, 0xa097, 0x2904, 0x00000000); + nv_mthd(dev, 0xa097, 0x2908, 0x00000000); + nv_mthd(dev, 0xa097, 0x290c, 0x00000000); + nv_mthd(dev, 0xa097, 0x2910, 0x00000000); + nv_mthd(dev, 0xa097, 0x2914, 0x00000000); + nv_mthd(dev, 0xa097, 0x2918, 0x00000000); + nv_mthd(dev, 0xa097, 0x291c, 0x00000000); + nv_mthd(dev, 0xa097, 0x2920, 0x00000000); + nv_mthd(dev, 0xa097, 0x2924, 0x00000000); + nv_mthd(dev, 0xa097, 0x2928, 0x00000000); + nv_mthd(dev, 0xa097, 0x292c, 0x00000000); + nv_mthd(dev, 0xa097, 0x2930, 0x00000000); + nv_mthd(dev, 0xa097, 0x2934, 0x00000000); + nv_mthd(dev, 0xa097, 0x2938, 0x00000000); + nv_mthd(dev, 0xa097, 0x293c, 0x00000000); + nv_mthd(dev, 0xa097, 0x2940, 0x00000000); + nv_mthd(dev, 0xa097, 0x2944, 0x00000000); + nv_mthd(dev, 0xa097, 0x2948, 0x00000000); + nv_mthd(dev, 0xa097, 0x294c, 0x00000000); + nv_mthd(dev, 0xa097, 0x2950, 0x00000000); + nv_mthd(dev, 0xa097, 0x2954, 0x00000000); + nv_mthd(dev, 0xa097, 0x2958, 0x00000000); + nv_mthd(dev, 0xa097, 0x295c, 0x00000000); + nv_mthd(dev, 0xa097, 0x2960, 0x00000000); + nv_mthd(dev, 0xa097, 0x2964, 0x00000000); + nv_mthd(dev, 0xa097, 0x2968, 0x00000000); + nv_mthd(dev, 0xa097, 0x296c, 0x00000000); + nv_mthd(dev, 0xa097, 0x2970, 0x00000000); + nv_mthd(dev, 0xa097, 0x2974, 0x00000000); + nv_mthd(dev, 0xa097, 0x2978, 0x00000000); + nv_mthd(dev, 0xa097, 0x297c, 0x00000000); + nv_mthd(dev, 0xa097, 0x2980, 0x00000000); + nv_mthd(dev, 0xa097, 0x2984, 0x00000000); + nv_mthd(dev, 0xa097, 0x2988, 0x00000000); + nv_mthd(dev, 0xa097, 0x298c, 0x00000000); + nv_mthd(dev, 0xa097, 0x2990, 0x00000000); + nv_mthd(dev, 0xa097, 0x2994, 0x00000000); + nv_mthd(dev, 0xa097, 0x2998, 0x00000000); + nv_mthd(dev, 0xa097, 0x299c, 0x00000000); + nv_mthd(dev, 0xa097, 0x29a0, 0x00000000); + nv_mthd(dev, 0xa097, 0x29a4, 0x00000000); + nv_mthd(dev, 0xa097, 0x29a8, 0x00000000); + nv_mthd(dev, 0xa097, 0x29ac, 0x00000000); + nv_mthd(dev, 0xa097, 0x29b0, 0x00000000); + nv_mthd(dev, 0xa097, 0x29b4, 0x00000000); + nv_mthd(dev, 0xa097, 0x29b8, 0x00000000); + nv_mthd(dev, 0xa097, 0x29bc, 0x00000000); + nv_mthd(dev, 0xa097, 0x29c0, 0x00000000); + nv_mthd(dev, 0xa097, 0x29c4, 0x00000000); + nv_mthd(dev, 0xa097, 0x29c8, 0x00000000); + nv_mthd(dev, 0xa097, 0x29cc, 0x00000000); + nv_mthd(dev, 0xa097, 0x29d0, 0x00000000); + nv_mthd(dev, 0xa097, 0x29d4, 0x00000000); + nv_mthd(dev, 0xa097, 0x29d8, 0x00000000); + nv_mthd(dev, 0xa097, 0x29dc, 0x00000000); + nv_mthd(dev, 0xa097, 0x29e0, 0x00000000); + nv_mthd(dev, 0xa097, 0x29e4, 0x00000000); + nv_mthd(dev, 0xa097, 0x29e8, 0x00000000); + nv_mthd(dev, 0xa097, 0x29ec, 0x00000000); + nv_mthd(dev, 0xa097, 0x29f0, 0x00000000); + nv_mthd(dev, 0xa097, 0x29f4, 0x00000000); + nv_mthd(dev, 0xa097, 0x29f8, 0x00000000); + nv_mthd(dev, 0xa097, 0x29fc, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a00, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a20, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a40, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a60, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a80, 0x00000000); + nv_mthd(dev, 0xa097, 0x0aa0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0ac0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0ae0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b00, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b20, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b40, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b60, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b80, 0x00000000); + nv_mthd(dev, 0xa097, 0x0ba0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0bc0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0be0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a04, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a24, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a44, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a64, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a84, 0x00000000); + nv_mthd(dev, 0xa097, 0x0aa4, 0x00000000); + nv_mthd(dev, 0xa097, 0x0ac4, 0x00000000); + nv_mthd(dev, 0xa097, 0x0ae4, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b04, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b24, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b44, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b64, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b84, 0x00000000); + nv_mthd(dev, 0xa097, 0x0ba4, 0x00000000); + nv_mthd(dev, 0xa097, 0x0bc4, 0x00000000); + nv_mthd(dev, 0xa097, 0x0be4, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a08, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a28, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a48, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a68, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a88, 0x00000000); + nv_mthd(dev, 0xa097, 0x0aa8, 0x00000000); + nv_mthd(dev, 0xa097, 0x0ac8, 0x00000000); + nv_mthd(dev, 0xa097, 0x0ae8, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b08, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b28, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b48, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b68, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b88, 0x00000000); + nv_mthd(dev, 0xa097, 0x0ba8, 0x00000000); + nv_mthd(dev, 0xa097, 0x0bc8, 0x00000000); + nv_mthd(dev, 0xa097, 0x0be8, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a0c, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a2c, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a4c, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a6c, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a8c, 0x00000000); + nv_mthd(dev, 0xa097, 0x0aac, 0x00000000); + nv_mthd(dev, 0xa097, 0x0acc, 0x00000000); + nv_mthd(dev, 0xa097, 0x0aec, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b0c, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b2c, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b4c, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b6c, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b8c, 0x00000000); + nv_mthd(dev, 0xa097, 0x0bac, 0x00000000); + nv_mthd(dev, 0xa097, 0x0bcc, 0x00000000); + nv_mthd(dev, 0xa097, 0x0bec, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a10, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a30, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a50, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a70, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a90, 0x00000000); + nv_mthd(dev, 0xa097, 0x0ab0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0ad0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0af0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b10, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b30, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b50, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b70, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b90, 0x00000000); + nv_mthd(dev, 0xa097, 0x0bb0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0bd0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0bf0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a14, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a34, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a54, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a74, 0x00000000); + nv_mthd(dev, 0xa097, 0x0a94, 0x00000000); + nv_mthd(dev, 0xa097, 0x0ab4, 0x00000000); + nv_mthd(dev, 0xa097, 0x0ad4, 0x00000000); + nv_mthd(dev, 0xa097, 0x0af4, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b14, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b34, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b54, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b74, 0x00000000); + nv_mthd(dev, 0xa097, 0x0b94, 0x00000000); + nv_mthd(dev, 0xa097, 0x0bb4, 0x00000000); + nv_mthd(dev, 0xa097, 0x0bd4, 0x00000000); + nv_mthd(dev, 0xa097, 0x0bf4, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c00, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c10, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c20, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c30, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c40, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c50, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c60, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c70, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c80, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c90, 0x00000000); + nv_mthd(dev, 0xa097, 0x0ca0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0cb0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0cc0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0cd0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0ce0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0cf0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c04, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c14, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c24, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c34, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c44, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c54, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c64, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c74, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c84, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c94, 0x00000000); + nv_mthd(dev, 0xa097, 0x0ca4, 0x00000000); + nv_mthd(dev, 0xa097, 0x0cb4, 0x00000000); + nv_mthd(dev, 0xa097, 0x0cc4, 0x00000000); + nv_mthd(dev, 0xa097, 0x0cd4, 0x00000000); + nv_mthd(dev, 0xa097, 0x0ce4, 0x00000000); + nv_mthd(dev, 0xa097, 0x0cf4, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c08, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c18, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c28, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c38, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c48, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c58, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c68, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c78, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c88, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c98, 0x00000000); + nv_mthd(dev, 0xa097, 0x0ca8, 0x00000000); + nv_mthd(dev, 0xa097, 0x0cb8, 0x00000000); + nv_mthd(dev, 0xa097, 0x0cc8, 0x00000000); + nv_mthd(dev, 0xa097, 0x0cd8, 0x00000000); + nv_mthd(dev, 0xa097, 0x0ce8, 0x00000000); + nv_mthd(dev, 0xa097, 0x0cf8, 0x00000000); + nv_mthd(dev, 0xa097, 0x0c0c, 0x3f800000); + nv_mthd(dev, 0xa097, 0x0c1c, 0x3f800000); + nv_mthd(dev, 0xa097, 0x0c2c, 0x3f800000); + nv_mthd(dev, 0xa097, 0x0c3c, 0x3f800000); + nv_mthd(dev, 0xa097, 0x0c4c, 0x3f800000); + nv_mthd(dev, 0xa097, 0x0c5c, 0x3f800000); + nv_mthd(dev, 0xa097, 0x0c6c, 0x3f800000); + nv_mthd(dev, 0xa097, 0x0c7c, 0x3f800000); + nv_mthd(dev, 0xa097, 0x0c8c, 0x3f800000); + nv_mthd(dev, 0xa097, 0x0c9c, 0x3f800000); + nv_mthd(dev, 0xa097, 0x0cac, 0x3f800000); + nv_mthd(dev, 0xa097, 0x0cbc, 0x3f800000); + nv_mthd(dev, 0xa097, 0x0ccc, 0x3f800000); + nv_mthd(dev, 0xa097, 0x0cdc, 0x3f800000); + nv_mthd(dev, 0xa097, 0x0cec, 0x3f800000); + nv_mthd(dev, 0xa097, 0x0cfc, 0x3f800000); + nv_mthd(dev, 0xa097, 0x0d00, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0d08, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0d10, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0d18, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0d20, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0d28, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0d30, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0d38, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0d04, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0d0c, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0d14, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0d1c, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0d24, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0d2c, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0d34, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0d3c, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0e00, 0x00000000); + nv_mthd(dev, 0xa097, 0x0e10, 0x00000000); + nv_mthd(dev, 0xa097, 0x0e20, 0x00000000); + nv_mthd(dev, 0xa097, 0x0e30, 0x00000000); + nv_mthd(dev, 0xa097, 0x0e40, 0x00000000); + nv_mthd(dev, 0xa097, 0x0e50, 0x00000000); + nv_mthd(dev, 0xa097, 0x0e60, 0x00000000); + nv_mthd(dev, 0xa097, 0x0e70, 0x00000000); + nv_mthd(dev, 0xa097, 0x0e80, 0x00000000); + nv_mthd(dev, 0xa097, 0x0e90, 0x00000000); + nv_mthd(dev, 0xa097, 0x0ea0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0eb0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0ec0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0ed0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0ee0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0ef0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0e04, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0e14, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0e24, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0e34, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0e44, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0e54, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0e64, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0e74, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0e84, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0e94, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0ea4, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0eb4, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0ec4, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0ed4, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0ee4, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0ef4, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0e08, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0e18, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0e28, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0e38, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0e48, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0e58, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0e68, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0e78, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0e88, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0e98, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0ea8, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0eb8, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0ec8, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0ed8, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0ee8, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0ef8, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0d40, 0x00000000); + nv_mthd(dev, 0xa097, 0x0d48, 0x00000000); + nv_mthd(dev, 0xa097, 0x0d50, 0x00000000); + nv_mthd(dev, 0xa097, 0x0d58, 0x00000000); + nv_mthd(dev, 0xa097, 0x0d44, 0x00000000); + nv_mthd(dev, 0xa097, 0x0d4c, 0x00000000); + nv_mthd(dev, 0xa097, 0x0d54, 0x00000000); + nv_mthd(dev, 0xa097, 0x0d5c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1e00, 0x00000001); + nv_mthd(dev, 0xa097, 0x1e20, 0x00000001); + nv_mthd(dev, 0xa097, 0x1e40, 0x00000001); + nv_mthd(dev, 0xa097, 0x1e60, 0x00000001); + nv_mthd(dev, 0xa097, 0x1e80, 0x00000001); + nv_mthd(dev, 0xa097, 0x1ea0, 0x00000001); + nv_mthd(dev, 0xa097, 0x1ec0, 0x00000001); + nv_mthd(dev, 0xa097, 0x1ee0, 0x00000001); + nv_mthd(dev, 0xa097, 0x1e04, 0x00000001); + nv_mthd(dev, 0xa097, 0x1e24, 0x00000001); + nv_mthd(dev, 0xa097, 0x1e44, 0x00000001); + nv_mthd(dev, 0xa097, 0x1e64, 0x00000001); + nv_mthd(dev, 0xa097, 0x1e84, 0x00000001); + nv_mthd(dev, 0xa097, 0x1ea4, 0x00000001); + nv_mthd(dev, 0xa097, 0x1ec4, 0x00000001); + nv_mthd(dev, 0xa097, 0x1ee4, 0x00000001); + nv_mthd(dev, 0xa097, 0x1e08, 0x00000002); + nv_mthd(dev, 0xa097, 0x1e28, 0x00000002); + nv_mthd(dev, 0xa097, 0x1e48, 0x00000002); + nv_mthd(dev, 0xa097, 0x1e68, 0x00000002); + nv_mthd(dev, 0xa097, 0x1e88, 0x00000002); + nv_mthd(dev, 0xa097, 0x1ea8, 0x00000002); + nv_mthd(dev, 0xa097, 0x1ec8, 0x00000002); + nv_mthd(dev, 0xa097, 0x1ee8, 0x00000002); + nv_mthd(dev, 0xa097, 0x1e0c, 0x00000001); + nv_mthd(dev, 0xa097, 0x1e2c, 0x00000001); + nv_mthd(dev, 0xa097, 0x1e4c, 0x00000001); + nv_mthd(dev, 0xa097, 0x1e6c, 0x00000001); + nv_mthd(dev, 0xa097, 0x1e8c, 0x00000001); + nv_mthd(dev, 0xa097, 0x1eac, 0x00000001); + nv_mthd(dev, 0xa097, 0x1ecc, 0x00000001); + nv_mthd(dev, 0xa097, 0x1eec, 0x00000001); + nv_mthd(dev, 0xa097, 0x1e10, 0x00000001); + nv_mthd(dev, 0xa097, 0x1e30, 0x00000001); + nv_mthd(dev, 0xa097, 0x1e50, 0x00000001); + nv_mthd(dev, 0xa097, 0x1e70, 0x00000001); + nv_mthd(dev, 0xa097, 0x1e90, 0x00000001); + nv_mthd(dev, 0xa097, 0x1eb0, 0x00000001); + nv_mthd(dev, 0xa097, 0x1ed0, 0x00000001); + nv_mthd(dev, 0xa097, 0x1ef0, 0x00000001); + nv_mthd(dev, 0xa097, 0x1e14, 0x00000002); + nv_mthd(dev, 0xa097, 0x1e34, 0x00000002); + nv_mthd(dev, 0xa097, 0x1e54, 0x00000002); + nv_mthd(dev, 0xa097, 0x1e74, 0x00000002); + nv_mthd(dev, 0xa097, 0x1e94, 0x00000002); + nv_mthd(dev, 0xa097, 0x1eb4, 0x00000002); + nv_mthd(dev, 0xa097, 0x1ed4, 0x00000002); + nv_mthd(dev, 0xa097, 0x1ef4, 0x00000002); + nv_mthd(dev, 0xa097, 0x1e18, 0x00000001); + nv_mthd(dev, 0xa097, 0x1e38, 0x00000001); + nv_mthd(dev, 0xa097, 0x1e58, 0x00000001); + nv_mthd(dev, 0xa097, 0x1e78, 0x00000001); + nv_mthd(dev, 0xa097, 0x1e98, 0x00000001); + nv_mthd(dev, 0xa097, 0x1eb8, 0x00000001); + nv_mthd(dev, 0xa097, 0x1ed8, 0x00000001); + nv_mthd(dev, 0xa097, 0x1ef8, 0x00000001); + nv_mthd(dev, 0xa097, 0x3400, 0x00000000); + nv_mthd(dev, 0xa097, 0x3404, 0x00000000); + nv_mthd(dev, 0xa097, 0x3408, 0x00000000); + nv_mthd(dev, 0xa097, 0x340c, 0x00000000); + nv_mthd(dev, 0xa097, 0x3410, 0x00000000); + nv_mthd(dev, 0xa097, 0x3414, 0x00000000); + nv_mthd(dev, 0xa097, 0x3418, 0x00000000); + nv_mthd(dev, 0xa097, 0x341c, 0x00000000); + nv_mthd(dev, 0xa097, 0x3420, 0x00000000); + nv_mthd(dev, 0xa097, 0x3424, 0x00000000); + nv_mthd(dev, 0xa097, 0x3428, 0x00000000); + nv_mthd(dev, 0xa097, 0x342c, 0x00000000); + nv_mthd(dev, 0xa097, 0x3430, 0x00000000); + nv_mthd(dev, 0xa097, 0x3434, 0x00000000); + nv_mthd(dev, 0xa097, 0x3438, 0x00000000); + nv_mthd(dev, 0xa097, 0x343c, 0x00000000); + nv_mthd(dev, 0xa097, 0x3440, 0x00000000); + nv_mthd(dev, 0xa097, 0x3444, 0x00000000); + nv_mthd(dev, 0xa097, 0x3448, 0x00000000); + nv_mthd(dev, 0xa097, 0x344c, 0x00000000); + nv_mthd(dev, 0xa097, 0x3450, 0x00000000); + nv_mthd(dev, 0xa097, 0x3454, 0x00000000); + nv_mthd(dev, 0xa097, 0x3458, 0x00000000); + nv_mthd(dev, 0xa097, 0x345c, 0x00000000); + nv_mthd(dev, 0xa097, 0x3460, 0x00000000); + nv_mthd(dev, 0xa097, 0x3464, 0x00000000); + nv_mthd(dev, 0xa097, 0x3468, 0x00000000); + nv_mthd(dev, 0xa097, 0x346c, 0x00000000); + nv_mthd(dev, 0xa097, 0x3470, 0x00000000); + nv_mthd(dev, 0xa097, 0x3474, 0x00000000); + nv_mthd(dev, 0xa097, 0x3478, 0x00000000); + nv_mthd(dev, 0xa097, 0x347c, 0x00000000); + nv_mthd(dev, 0xa097, 0x3480, 0x00000000); + nv_mthd(dev, 0xa097, 0x3484, 0x00000000); + nv_mthd(dev, 0xa097, 0x3488, 0x00000000); + nv_mthd(dev, 0xa097, 0x348c, 0x00000000); + nv_mthd(dev, 0xa097, 0x3490, 0x00000000); + nv_mthd(dev, 0xa097, 0x3494, 0x00000000); + nv_mthd(dev, 0xa097, 0x3498, 0x00000000); + nv_mthd(dev, 0xa097, 0x349c, 0x00000000); + nv_mthd(dev, 0xa097, 0x34a0, 0x00000000); + nv_mthd(dev, 0xa097, 0x34a4, 0x00000000); + nv_mthd(dev, 0xa097, 0x34a8, 0x00000000); + nv_mthd(dev, 0xa097, 0x34ac, 0x00000000); + nv_mthd(dev, 0xa097, 0x34b0, 0x00000000); + nv_mthd(dev, 0xa097, 0x34b4, 0x00000000); + nv_mthd(dev, 0xa097, 0x34b8, 0x00000000); + nv_mthd(dev, 0xa097, 0x34bc, 0x00000000); + nv_mthd(dev, 0xa097, 0x34c0, 0x00000000); + nv_mthd(dev, 0xa097, 0x34c4, 0x00000000); + nv_mthd(dev, 0xa097, 0x34c8, 0x00000000); + nv_mthd(dev, 0xa097, 0x34cc, 0x00000000); + nv_mthd(dev, 0xa097, 0x34d0, 0x00000000); + nv_mthd(dev, 0xa097, 0x34d4, 0x00000000); + nv_mthd(dev, 0xa097, 0x34d8, 0x00000000); + nv_mthd(dev, 0xa097, 0x34dc, 0x00000000); + nv_mthd(dev, 0xa097, 0x34e0, 0x00000000); + nv_mthd(dev, 0xa097, 0x34e4, 0x00000000); + nv_mthd(dev, 0xa097, 0x34e8, 0x00000000); + nv_mthd(dev, 0xa097, 0x34ec, 0x00000000); + nv_mthd(dev, 0xa097, 0x34f0, 0x00000000); + nv_mthd(dev, 0xa097, 0x34f4, 0x00000000); + nv_mthd(dev, 0xa097, 0x34f8, 0x00000000); + nv_mthd(dev, 0xa097, 0x34fc, 0x00000000); + nv_mthd(dev, 0xa097, 0x3500, 0x00000000); + nv_mthd(dev, 0xa097, 0x3504, 0x00000000); + nv_mthd(dev, 0xa097, 0x3508, 0x00000000); + nv_mthd(dev, 0xa097, 0x350c, 0x00000000); + nv_mthd(dev, 0xa097, 0x3510, 0x00000000); + nv_mthd(dev, 0xa097, 0x3514, 0x00000000); + nv_mthd(dev, 0xa097, 0x3518, 0x00000000); + nv_mthd(dev, 0xa097, 0x351c, 0x00000000); + nv_mthd(dev, 0xa097, 0x3520, 0x00000000); + nv_mthd(dev, 0xa097, 0x3524, 0x00000000); + nv_mthd(dev, 0xa097, 0x3528, 0x00000000); + nv_mthd(dev, 0xa097, 0x352c, 0x00000000); + nv_mthd(dev, 0xa097, 0x3530, 0x00000000); + nv_mthd(dev, 0xa097, 0x3534, 0x00000000); + nv_mthd(dev, 0xa097, 0x3538, 0x00000000); + nv_mthd(dev, 0xa097, 0x353c, 0x00000000); + nv_mthd(dev, 0xa097, 0x3540, 0x00000000); + nv_mthd(dev, 0xa097, 0x3544, 0x00000000); + nv_mthd(dev, 0xa097, 0x3548, 0x00000000); + nv_mthd(dev, 0xa097, 0x354c, 0x00000000); + nv_mthd(dev, 0xa097, 0x3550, 0x00000000); + nv_mthd(dev, 0xa097, 0x3554, 0x00000000); + nv_mthd(dev, 0xa097, 0x3558, 0x00000000); + nv_mthd(dev, 0xa097, 0x355c, 0x00000000); + nv_mthd(dev, 0xa097, 0x3560, 0x00000000); + nv_mthd(dev, 0xa097, 0x3564, 0x00000000); + nv_mthd(dev, 0xa097, 0x3568, 0x00000000); + nv_mthd(dev, 0xa097, 0x356c, 0x00000000); + nv_mthd(dev, 0xa097, 0x3570, 0x00000000); + nv_mthd(dev, 0xa097, 0x3574, 0x00000000); + nv_mthd(dev, 0xa097, 0x3578, 0x00000000); + nv_mthd(dev, 0xa097, 0x357c, 0x00000000); + nv_mthd(dev, 0xa097, 0x3580, 0x00000000); + nv_mthd(dev, 0xa097, 0x3584, 0x00000000); + nv_mthd(dev, 0xa097, 0x3588, 0x00000000); + nv_mthd(dev, 0xa097, 0x358c, 0x00000000); + nv_mthd(dev, 0xa097, 0x3590, 0x00000000); + nv_mthd(dev, 0xa097, 0x3594, 0x00000000); + nv_mthd(dev, 0xa097, 0x3598, 0x00000000); + nv_mthd(dev, 0xa097, 0x359c, 0x00000000); + nv_mthd(dev, 0xa097, 0x35a0, 0x00000000); + nv_mthd(dev, 0xa097, 0x35a4, 0x00000000); + nv_mthd(dev, 0xa097, 0x35a8, 0x00000000); + nv_mthd(dev, 0xa097, 0x35ac, 0x00000000); + nv_mthd(dev, 0xa097, 0x35b0, 0x00000000); + nv_mthd(dev, 0xa097, 0x35b4, 0x00000000); + nv_mthd(dev, 0xa097, 0x35b8, 0x00000000); + nv_mthd(dev, 0xa097, 0x35bc, 0x00000000); + nv_mthd(dev, 0xa097, 0x35c0, 0x00000000); + nv_mthd(dev, 0xa097, 0x35c4, 0x00000000); + nv_mthd(dev, 0xa097, 0x35c8, 0x00000000); + nv_mthd(dev, 0xa097, 0x35cc, 0x00000000); + nv_mthd(dev, 0xa097, 0x35d0, 0x00000000); + nv_mthd(dev, 0xa097, 0x35d4, 0x00000000); + nv_mthd(dev, 0xa097, 0x35d8, 0x00000000); + nv_mthd(dev, 0xa097, 0x35dc, 0x00000000); + nv_mthd(dev, 0xa097, 0x35e0, 0x00000000); + nv_mthd(dev, 0xa097, 0x35e4, 0x00000000); + nv_mthd(dev, 0xa097, 0x35e8, 0x00000000); + nv_mthd(dev, 0xa097, 0x35ec, 0x00000000); + nv_mthd(dev, 0xa097, 0x35f0, 0x00000000); + nv_mthd(dev, 0xa097, 0x35f4, 0x00000000); + nv_mthd(dev, 0xa097, 0x35f8, 0x00000000); + nv_mthd(dev, 0xa097, 0x35fc, 0x00000000); + nv_mthd(dev, 0xa097, 0x030c, 0x00000001); + nv_mthd(dev, 0xa097, 0x1944, 0x00000000); + nv_mthd(dev, 0xa097, 0x1514, 0x00000000); + nv_mthd(dev, 0xa097, 0x0d68, 0x0000ffff); + nv_mthd(dev, 0xa097, 0x121c, 0x0fac6881); + nv_mthd(dev, 0xa097, 0x0fac, 0x00000001); + nv_mthd(dev, 0xa097, 0x1538, 0x00000001); + nv_mthd(dev, 0xa097, 0x0fe0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0fe4, 0x00000000); + nv_mthd(dev, 0xa097, 0x0fe8, 0x00000014); + nv_mthd(dev, 0xa097, 0x0fec, 0x00000040); + nv_mthd(dev, 0xa097, 0x0ff0, 0x00000000); + nv_mthd(dev, 0xa097, 0x179c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1228, 0x00000400); + nv_mthd(dev, 0xa097, 0x122c, 0x00000300); + nv_mthd(dev, 0xa097, 0x1230, 0x00010001); + nv_mthd(dev, 0xa097, 0x07f8, 0x00000000); + nv_mthd(dev, 0xa097, 0x15b4, 0x00000001); + nv_mthd(dev, 0xa097, 0x15cc, 0x00000000); + nv_mthd(dev, 0xa097, 0x1534, 0x00000000); + nv_mthd(dev, 0xa097, 0x0fb0, 0x00000000); + nv_mthd(dev, 0xa097, 0x15d0, 0x00000000); + nv_mthd(dev, 0xa097, 0x153c, 0x00000000); + nv_mthd(dev, 0xa097, 0x16b4, 0x00000003); + nv_mthd(dev, 0xa097, 0x0fbc, 0x0000ffff); + nv_mthd(dev, 0xa097, 0x0fc0, 0x0000ffff); + nv_mthd(dev, 0xa097, 0x0fc4, 0x0000ffff); + nv_mthd(dev, 0xa097, 0x0fc8, 0x0000ffff); + nv_mthd(dev, 0xa097, 0x0df8, 0x00000000); + nv_mthd(dev, 0xa097, 0x0dfc, 0x00000000); + nv_mthd(dev, 0xa097, 0x1948, 0x00000000); + nv_mthd(dev, 0xa097, 0x1970, 0x00000001); + nv_mthd(dev, 0xa097, 0x161c, 0x000009f0); + nv_mthd(dev, 0xa097, 0x0dcc, 0x00000010); + nv_mthd(dev, 0xa097, 0x163c, 0x00000000); + nv_mthd(dev, 0xa097, 0x15e4, 0x00000000); + nv_mthd(dev, 0xa097, 0x1160, 0x25e00040); + nv_mthd(dev, 0xa097, 0x1164, 0x25e00040); + nv_mthd(dev, 0xa097, 0x1168, 0x25e00040); + nv_mthd(dev, 0xa097, 0x116c, 0x25e00040); + nv_mthd(dev, 0xa097, 0x1170, 0x25e00040); + nv_mthd(dev, 0xa097, 0x1174, 0x25e00040); + nv_mthd(dev, 0xa097, 0x1178, 0x25e00040); + nv_mthd(dev, 0xa097, 0x117c, 0x25e00040); + nv_mthd(dev, 0xa097, 0x1180, 0x25e00040); + nv_mthd(dev, 0xa097, 0x1184, 0x25e00040); + nv_mthd(dev, 0xa097, 0x1188, 0x25e00040); + nv_mthd(dev, 0xa097, 0x118c, 0x25e00040); + nv_mthd(dev, 0xa097, 0x1190, 0x25e00040); + nv_mthd(dev, 0xa097, 0x1194, 0x25e00040); + nv_mthd(dev, 0xa097, 0x1198, 0x25e00040); + nv_mthd(dev, 0xa097, 0x119c, 0x25e00040); + nv_mthd(dev, 0xa097, 0x11a0, 0x25e00040); + nv_mthd(dev, 0xa097, 0x11a4, 0x25e00040); + nv_mthd(dev, 0xa097, 0x11a8, 0x25e00040); + nv_mthd(dev, 0xa097, 0x11ac, 0x25e00040); + nv_mthd(dev, 0xa097, 0x11b0, 0x25e00040); + nv_mthd(dev, 0xa097, 0x11b4, 0x25e00040); + nv_mthd(dev, 0xa097, 0x11b8, 0x25e00040); + nv_mthd(dev, 0xa097, 0x11bc, 0x25e00040); + nv_mthd(dev, 0xa097, 0x11c0, 0x25e00040); + nv_mthd(dev, 0xa097, 0x11c4, 0x25e00040); + nv_mthd(dev, 0xa097, 0x11c8, 0x25e00040); + nv_mthd(dev, 0xa097, 0x11cc, 0x25e00040); + nv_mthd(dev, 0xa097, 0x11d0, 0x25e00040); + nv_mthd(dev, 0xa097, 0x11d4, 0x25e00040); + nv_mthd(dev, 0xa097, 0x11d8, 0x25e00040); + nv_mthd(dev, 0xa097, 0x11dc, 0x25e00040); + nv_mthd(dev, 0xa097, 0x1880, 0x00000000); + nv_mthd(dev, 0xa097, 0x1884, 0x00000000); + nv_mthd(dev, 0xa097, 0x1888, 0x00000000); + nv_mthd(dev, 0xa097, 0x188c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1890, 0x00000000); + nv_mthd(dev, 0xa097, 0x1894, 0x00000000); + nv_mthd(dev, 0xa097, 0x1898, 0x00000000); + nv_mthd(dev, 0xa097, 0x189c, 0x00000000); + nv_mthd(dev, 0xa097, 0x18a0, 0x00000000); + nv_mthd(dev, 0xa097, 0x18a4, 0x00000000); + nv_mthd(dev, 0xa097, 0x18a8, 0x00000000); + nv_mthd(dev, 0xa097, 0x18ac, 0x00000000); + nv_mthd(dev, 0xa097, 0x18b0, 0x00000000); + nv_mthd(dev, 0xa097, 0x18b4, 0x00000000); + nv_mthd(dev, 0xa097, 0x18b8, 0x00000000); + nv_mthd(dev, 0xa097, 0x18bc, 0x00000000); + nv_mthd(dev, 0xa097, 0x18c0, 0x00000000); + nv_mthd(dev, 0xa097, 0x18c4, 0x00000000); + nv_mthd(dev, 0xa097, 0x18c8, 0x00000000); + nv_mthd(dev, 0xa097, 0x18cc, 0x00000000); + nv_mthd(dev, 0xa097, 0x18d0, 0x00000000); + nv_mthd(dev, 0xa097, 0x18d4, 0x00000000); + nv_mthd(dev, 0xa097, 0x18d8, 0x00000000); + nv_mthd(dev, 0xa097, 0x18dc, 0x00000000); + nv_mthd(dev, 0xa097, 0x18e0, 0x00000000); + nv_mthd(dev, 0xa097, 0x18e4, 0x00000000); + nv_mthd(dev, 0xa097, 0x18e8, 0x00000000); + nv_mthd(dev, 0xa097, 0x18ec, 0x00000000); + nv_mthd(dev, 0xa097, 0x18f0, 0x00000000); + nv_mthd(dev, 0xa097, 0x18f4, 0x00000000); + nv_mthd(dev, 0xa097, 0x18f8, 0x00000000); + nv_mthd(dev, 0xa097, 0x18fc, 0x00000000); + nv_mthd(dev, 0xa097, 0x0f84, 0x00000000); + nv_mthd(dev, 0xa097, 0x0f88, 0x00000000); + nv_mthd(dev, 0xa097, 0x17c8, 0x00000000); + nv_mthd(dev, 0xa097, 0x17cc, 0x00000000); + nv_mthd(dev, 0xa097, 0x17d0, 0x000000ff); + nv_mthd(dev, 0xa097, 0x17d4, 0xffffffff); + nv_mthd(dev, 0xa097, 0x17d8, 0x00000002); + nv_mthd(dev, 0xa097, 0x17dc, 0x00000000); + nv_mthd(dev, 0xa097, 0x15f4, 0x00000000); + nv_mthd(dev, 0xa097, 0x15f8, 0x00000000); + nv_mthd(dev, 0xa097, 0x1434, 0x00000000); + nv_mthd(dev, 0xa097, 0x1438, 0x00000000); + nv_mthd(dev, 0xa097, 0x0d74, 0x00000000); + nv_mthd(dev, 0xa097, 0x0dec, 0x00000001); + nv_mthd(dev, 0xa097, 0x13a4, 0x00000000); + nv_mthd(dev, 0xa097, 0x1318, 0x00000001); + nv_mthd(dev, 0xa097, 0x1644, 0x00000000); + nv_mthd(dev, 0xa097, 0x0748, 0x00000000); + nv_mthd(dev, 0xa097, 0x0de8, 0x00000000); + nv_mthd(dev, 0xa097, 0x1648, 0x00000000); + nv_mthd(dev, 0xa097, 0x12a4, 0x00000000); + nv_mthd(dev, 0xa097, 0x1120, 0x00000000); + nv_mthd(dev, 0xa097, 0x1124, 0x00000000); + nv_mthd(dev, 0xa097, 0x1128, 0x00000000); + nv_mthd(dev, 0xa097, 0x112c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1118, 0x00000000); + nv_mthd(dev, 0xa097, 0x164c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1658, 0x00000000); + nv_mthd(dev, 0xa097, 0x1910, 0x00000290); + nv_mthd(dev, 0xa097, 0x1518, 0x00000000); + nv_mthd(dev, 0xa097, 0x165c, 0x00000001); + nv_mthd(dev, 0xa097, 0x1520, 0x00000000); + nv_mthd(dev, 0xa097, 0x1604, 0x00000000); + nv_mthd(dev, 0xa097, 0x1570, 0x00000000); + nv_mthd(dev, 0xa097, 0x13b0, 0x3f800000); + nv_mthd(dev, 0xa097, 0x13b4, 0x3f800000); + nv_mthd(dev, 0xa097, 0x020c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1670, 0x30201000); + nv_mthd(dev, 0xa097, 0x1674, 0x70605040); + nv_mthd(dev, 0xa097, 0x1678, 0xb8a89888); + nv_mthd(dev, 0xa097, 0x167c, 0xf8e8d8c8); + nv_mthd(dev, 0xa097, 0x166c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1680, 0x00ffff00); + nv_mthd(dev, 0xa097, 0x12d0, 0x00000003); + nv_mthd(dev, 0xa097, 0x12d4, 0x00000002); + nv_mthd(dev, 0xa097, 0x1684, 0x00000000); + nv_mthd(dev, 0xa097, 0x1688, 0x00000000); + nv_mthd(dev, 0xa097, 0x0dac, 0x00001b02); + nv_mthd(dev, 0xa097, 0x0db0, 0x00001b02); + nv_mthd(dev, 0xa097, 0x0db4, 0x00000000); + nv_mthd(dev, 0xa097, 0x168c, 0x00000000); + nv_mthd(dev, 0xa097, 0x15bc, 0x00000000); + nv_mthd(dev, 0xa097, 0x156c, 0x00000000); + nv_mthd(dev, 0xa097, 0x187c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1110, 0x00000001); + nv_mthd(dev, 0xa097, 0x0dc0, 0x00000000); + nv_mthd(dev, 0xa097, 0x0dc4, 0x00000000); + nv_mthd(dev, 0xa097, 0x0dc8, 0x00000000); + nv_mthd(dev, 0xa097, 0x1234, 0x00000000); + nv_mthd(dev, 0xa097, 0x1690, 0x00000000); + nv_mthd(dev, 0xa097, 0x12ac, 0x00000001); + nv_mthd(dev, 0xa097, 0x0790, 0x00000000); + nv_mthd(dev, 0xa097, 0x0794, 0x00000000); + nv_mthd(dev, 0xa097, 0x0798, 0x00000000); + nv_mthd(dev, 0xa097, 0x079c, 0x00000000); + nv_mthd(dev, 0xa097, 0x07a0, 0x00000000); + nv_mthd(dev, 0xa097, 0x077c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1000, 0x00000010); + nv_mthd(dev, 0xa097, 0x10fc, 0x00000000); + nv_mthd(dev, 0xa097, 0x1290, 0x00000000); + nv_mthd(dev, 0xa097, 0x0218, 0x00000010); + nv_mthd(dev, 0xa097, 0x12d8, 0x00000000); + nv_mthd(dev, 0xa097, 0x12dc, 0x00000010); + nv_mthd(dev, 0xa097, 0x0d94, 0x00000001); + nv_mthd(dev, 0xa097, 0x155c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1560, 0x00000000); + nv_mthd(dev, 0xa097, 0x1564, 0x00000fff); + nv_mthd(dev, 0xa097, 0x1574, 0x00000000); + nv_mthd(dev, 0xa097, 0x1578, 0x00000000); + nv_mthd(dev, 0xa097, 0x157c, 0x000fffff); + nv_mthd(dev, 0xa097, 0x1354, 0x00000000); + nv_mthd(dev, 0xa097, 0x1610, 0x00000012); + nv_mthd(dev, 0xa097, 0x1608, 0x00000000); + nv_mthd(dev, 0xa097, 0x160c, 0x00000000); + nv_mthd(dev, 0xa097, 0x260c, 0x00000000); + nv_mthd(dev, 0xa097, 0x07ac, 0x00000000); + nv_mthd(dev, 0xa097, 0x162c, 0x00000003); + nv_mthd(dev, 0xa097, 0x0210, 0x00000000); + nv_mthd(dev, 0xa097, 0x0320, 0x00000000); + nv_mthd(dev, 0xa097, 0x0324, 0x3f800000); + nv_mthd(dev, 0xa097, 0x0328, 0x3f800000); + nv_mthd(dev, 0xa097, 0x032c, 0x3f800000); + nv_mthd(dev, 0xa097, 0x0330, 0x3f800000); + nv_mthd(dev, 0xa097, 0x0334, 0x3f800000); + nv_mthd(dev, 0xa097, 0x0338, 0x3f800000); + nv_mthd(dev, 0xa097, 0x0750, 0x00000000); + nv_mthd(dev, 0xa097, 0x0760, 0x39291909); + nv_mthd(dev, 0xa097, 0x0764, 0x79695949); + nv_mthd(dev, 0xa097, 0x0768, 0xb9a99989); + nv_mthd(dev, 0xa097, 0x076c, 0xf9e9d9c9); + nv_mthd(dev, 0xa097, 0x0770, 0x30201000); + nv_mthd(dev, 0xa097, 0x0774, 0x70605040); + nv_mthd(dev, 0xa097, 0x0778, 0x00009080); + nv_mthd(dev, 0xa097, 0x0780, 0x39291909); + nv_mthd(dev, 0xa097, 0x0784, 0x79695949); + nv_mthd(dev, 0xa097, 0x0788, 0xb9a99989); + nv_mthd(dev, 0xa097, 0x078c, 0xf9e9d9c9); + nv_mthd(dev, 0xa097, 0x07d0, 0x30201000); + nv_mthd(dev, 0xa097, 0x07d4, 0x70605040); + nv_mthd(dev, 0xa097, 0x07d8, 0x00009080); + nv_mthd(dev, 0xa097, 0x037c, 0x00000001); + nv_mthd(dev, 0xa097, 0x0740, 0x00000000); + nv_mthd(dev, 0xa097, 0x0744, 0x00000000); + nv_mthd(dev, 0xa097, 0x2600, 0x00000000); + nv_mthd(dev, 0xa097, 0x1918, 0x00000000); + nv_mthd(dev, 0xa097, 0x191c, 0x00000900); + nv_mthd(dev, 0xa097, 0x1920, 0x00000405); + nv_mthd(dev, 0xa097, 0x1308, 0x00000001); + nv_mthd(dev, 0xa097, 0x1924, 0x00000000); + nv_mthd(dev, 0xa097, 0x13ac, 0x00000000); + nv_mthd(dev, 0xa097, 0x192c, 0x00000001); + nv_mthd(dev, 0xa097, 0x193c, 0x00002c1c); + nv_mthd(dev, 0xa097, 0x0d7c, 0x00000000); + nv_mthd(dev, 0xa097, 0x0f8c, 0x00000000); + nv_mthd(dev, 0xa097, 0x02c0, 0x00000001); + nv_mthd(dev, 0xa097, 0x1510, 0x00000000); + nv_mthd(dev, 0xa097, 0x1940, 0x00000000); + nv_mthd(dev, 0xa097, 0x0ff4, 0x00000000); + nv_mthd(dev, 0xa097, 0x0ff8, 0x00000000); + nv_mthd(dev, 0xa097, 0x194c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1950, 0x00000000); + nv_mthd(dev, 0xa097, 0x1968, 0x00000000); + nv_mthd(dev, 0xa097, 0x1590, 0x0000003f); + nv_mthd(dev, 0xa097, 0x07e8, 0x00000000); + nv_mthd(dev, 0xa097, 0x07ec, 0x00000000); + nv_mthd(dev, 0xa097, 0x07f0, 0x00000000); + nv_mthd(dev, 0xa097, 0x07f4, 0x00000000); + nv_mthd(dev, 0xa097, 0x196c, 0x00000011); + nv_mthd(dev, 0xa097, 0x02e4, 0x0000b001); + nv_mthd(dev, 0xa097, 0x036c, 0x00000000); + nv_mthd(dev, 0xa097, 0x0370, 0x00000000); + nv_mthd(dev, 0xa097, 0x197c, 0x00000000); + nv_mthd(dev, 0xa097, 0x0fcc, 0x00000000); + nv_mthd(dev, 0xa097, 0x0fd0, 0x00000000); + nv_mthd(dev, 0xa097, 0x02d8, 0x00000040); + nv_mthd(dev, 0xa097, 0x1980, 0x00000080); + nv_mthd(dev, 0xa097, 0x1504, 0x00000080); + nv_mthd(dev, 0xa097, 0x1984, 0x00000000); + nv_mthd(dev, 0xa097, 0x0300, 0x00000001); + nv_mthd(dev, 0xa097, 0x13a8, 0x00000000); + nv_mthd(dev, 0xa097, 0x12ec, 0x00000000); + nv_mthd(dev, 0xa097, 0x1310, 0x00000000); + nv_mthd(dev, 0xa097, 0x1314, 0x00000001); + nv_mthd(dev, 0xa097, 0x1380, 0x00000000); + nv_mthd(dev, 0xa097, 0x1384, 0x00000001); + nv_mthd(dev, 0xa097, 0x1388, 0x00000001); + nv_mthd(dev, 0xa097, 0x138c, 0x00000001); + nv_mthd(dev, 0xa097, 0x1390, 0x00000001); + nv_mthd(dev, 0xa097, 0x1394, 0x00000000); + nv_mthd(dev, 0xa097, 0x139c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1398, 0x00000000); + nv_mthd(dev, 0xa097, 0x1594, 0x00000000); + nv_mthd(dev, 0xa097, 0x1598, 0x00000001); + nv_mthd(dev, 0xa097, 0x159c, 0x00000001); + nv_mthd(dev, 0xa097, 0x15a0, 0x00000001); + nv_mthd(dev, 0xa097, 0x15a4, 0x00000001); + nv_mthd(dev, 0xa097, 0x0f54, 0x00000000); + nv_mthd(dev, 0xa097, 0x0f58, 0x00000000); + nv_mthd(dev, 0xa097, 0x0f5c, 0x00000000); + nv_mthd(dev, 0xa097, 0x19bc, 0x00000000); + nv_mthd(dev, 0xa097, 0x0f9c, 0x00000000); + nv_mthd(dev, 0xa097, 0x0fa0, 0x00000000); + nv_mthd(dev, 0xa097, 0x12cc, 0x00000000); + nv_mthd(dev, 0xa097, 0x12e8, 0x00000000); + nv_mthd(dev, 0xa097, 0x130c, 0x00000001); + nv_mthd(dev, 0xa097, 0x1360, 0x00000000); + nv_mthd(dev, 0xa097, 0x1364, 0x00000000); + nv_mthd(dev, 0xa097, 0x1368, 0x00000000); + nv_mthd(dev, 0xa097, 0x136c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1370, 0x00000000); + nv_mthd(dev, 0xa097, 0x1374, 0x00000000); + nv_mthd(dev, 0xa097, 0x1378, 0x00000000); + nv_mthd(dev, 0xa097, 0x137c, 0x00000000); + nv_mthd(dev, 0xa097, 0x133c, 0x00000001); + nv_mthd(dev, 0xa097, 0x1340, 0x00000001); + nv_mthd(dev, 0xa097, 0x1344, 0x00000002); + nv_mthd(dev, 0xa097, 0x1348, 0x00000001); + nv_mthd(dev, 0xa097, 0x134c, 0x00000001); + nv_mthd(dev, 0xa097, 0x1350, 0x00000002); + nv_mthd(dev, 0xa097, 0x1358, 0x00000001); + nv_mthd(dev, 0xa097, 0x12e4, 0x00000000); + nv_mthd(dev, 0xa097, 0x131c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1320, 0x00000000); + nv_mthd(dev, 0xa097, 0x1324, 0x00000000); + nv_mthd(dev, 0xa097, 0x1328, 0x00000000); + nv_mthd(dev, 0xa097, 0x19c0, 0x00000000); + nv_mthd(dev, 0xa097, 0x1140, 0x00000000); + nv_mthd(dev, 0xa097, 0x19c4, 0x00000000); + nv_mthd(dev, 0xa097, 0x19c8, 0x00001500); + nv_mthd(dev, 0xa097, 0x135c, 0x00000000); + nv_mthd(dev, 0xa097, 0x0f90, 0x00000000); + nv_mthd(dev, 0xa097, 0x19e0, 0x00000001); + nv_mthd(dev, 0xa097, 0x19e4, 0x00000001); + nv_mthd(dev, 0xa097, 0x19e8, 0x00000001); + nv_mthd(dev, 0xa097, 0x19ec, 0x00000001); + nv_mthd(dev, 0xa097, 0x19f0, 0x00000001); + nv_mthd(dev, 0xa097, 0x19f4, 0x00000001); + nv_mthd(dev, 0xa097, 0x19f8, 0x00000001); + nv_mthd(dev, 0xa097, 0x19fc, 0x00000001); + nv_mthd(dev, 0xa097, 0x19cc, 0x00000001); + nv_mthd(dev, 0xa097, 0x15b8, 0x00000000); + nv_mthd(dev, 0xa097, 0x1a00, 0x00001111); + nv_mthd(dev, 0xa097, 0x1a04, 0x00000000); + nv_mthd(dev, 0xa097, 0x1a08, 0x00000000); + nv_mthd(dev, 0xa097, 0x1a0c, 0x00000000); + nv_mthd(dev, 0xa097, 0x1a10, 0x00000000); + nv_mthd(dev, 0xa097, 0x1a14, 0x00000000); + nv_mthd(dev, 0xa097, 0x1a18, 0x00000000); + nv_mthd(dev, 0xa097, 0x1a1c, 0x00000000); + nv_mthd(dev, 0xa097, 0x0d6c, 0xffff0000); + nv_mthd(dev, 0xa097, 0x0d70, 0xffff0000); + nv_mthd(dev, 0xa097, 0x10f8, 0x00001010); + nv_mthd(dev, 0xa097, 0x0d80, 0x00000000); + nv_mthd(dev, 0xa097, 0x0d84, 0x00000000); + nv_mthd(dev, 0xa097, 0x0d88, 0x00000000); + nv_mthd(dev, 0xa097, 0x0d8c, 0x00000000); + nv_mthd(dev, 0xa097, 0x0d90, 0x00000000); + nv_mthd(dev, 0xa097, 0x0da0, 0x00000000); + nv_mthd(dev, 0xa097, 0x07a4, 0x00000000); + nv_mthd(dev, 0xa097, 0x07a8, 0x00000000); + nv_mthd(dev, 0xa097, 0x1508, 0x80000000); + nv_mthd(dev, 0xa097, 0x150c, 0x40000000); + nv_mthd(dev, 0xa097, 0x1668, 0x00000000); + nv_mthd(dev, 0xa097, 0x0318, 0x00000008); + nv_mthd(dev, 0xa097, 0x031c, 0x00000008); + nv_mthd(dev, 0xa097, 0x0d9c, 0x00000001); + nv_mthd(dev, 0xa097, 0x0374, 0x00000000); + nv_mthd(dev, 0xa097, 0x0378, 0x00000020); + nv_mthd(dev, 0xa097, 0x07dc, 0x00000000); + nv_mthd(dev, 0xa097, 0x074c, 0x00000055); + nv_mthd(dev, 0xa097, 0x1420, 0x00000003); + nv_mthd(dev, 0xa097, 0x17bc, 0x00000000); + nv_mthd(dev, 0xa097, 0x17c0, 0x00000000); + nv_mthd(dev, 0xa097, 0x17c4, 0x00000001); + nv_mthd(dev, 0xa097, 0x1008, 0x00000008); + nv_mthd(dev, 0xa097, 0x100c, 0x00000040); + nv_mthd(dev, 0xa097, 0x1010, 0x0000012c); + nv_mthd(dev, 0xa097, 0x0d60, 0x00000040); + nv_mthd(dev, 0xa097, 0x075c, 0x00000003); + nv_mthd(dev, 0xa097, 0x1018, 0x00000020); + nv_mthd(dev, 0xa097, 0x101c, 0x00000001); + nv_mthd(dev, 0xa097, 0x1020, 0x00000020); + nv_mthd(dev, 0xa097, 0x1024, 0x00000001); + nv_mthd(dev, 0xa097, 0x1444, 0x00000000); + nv_mthd(dev, 0xa097, 0x1448, 0x00000000); + nv_mthd(dev, 0xa097, 0x144c, 0x00000000); + nv_mthd(dev, 0xa097, 0x0360, 0x20164010); + nv_mthd(dev, 0xa097, 0x0364, 0x00000020); + nv_mthd(dev, 0xa097, 0x0368, 0x00000000); + nv_mthd(dev, 0xa097, 0x0de4, 0x00000000); + nv_mthd(dev, 0xa097, 0x0204, 0x00000006); + nv_mthd(dev, 0xa097, 0x0208, 0x00000000); + nv_mthd(dev, 0xa097, 0x02cc, 0x003fffff); + nv_mthd(dev, 0xa097, 0x02d0, 0x003fffff); + nv_mthd(dev, 0xa097, 0x1220, 0x00000005); + nv_mthd(dev, 0xa097, 0x0fdc, 0x00000000); + nv_mthd(dev, 0xa097, 0x0f98, 0x00400008); + nv_mthd(dev, 0xa097, 0x1284, 0x08000080); + nv_mthd(dev, 0xa097, 0x1450, 0x00400008); + nv_mthd(dev, 0xa097, 0x1454, 0x08000080); + nv_mthd(dev, 0xa097, 0x0214, 0x00000000); +} + +static void +nve0_grctx_generate_902d(struct drm_device *dev) +{ + nv_mthd(dev, 0x902d, 0x0200, 0x000000cf); + nv_mthd(dev, 0x902d, 0x0204, 0x00000001); + nv_mthd(dev, 0x902d, 0x0208, 0x00000020); + nv_mthd(dev, 0x902d, 0x020c, 0x00000001); + nv_mthd(dev, 0x902d, 0x0210, 0x00000000); + nv_mthd(dev, 0x902d, 0x0214, 0x00000080); + nv_mthd(dev, 0x902d, 0x0218, 0x00000100); + nv_mthd(dev, 0x902d, 0x021c, 0x00000100); + nv_mthd(dev, 0x902d, 0x0220, 0x00000000); + nv_mthd(dev, 0x902d, 0x0224, 0x00000000); + nv_mthd(dev, 0x902d, 0x0230, 0x000000cf); + nv_mthd(dev, 0x902d, 0x0234, 0x00000001); + nv_mthd(dev, 0x902d, 0x0238, 0x00000020); + nv_mthd(dev, 0x902d, 0x023c, 0x00000001); + nv_mthd(dev, 0x902d, 0x0244, 0x00000080); + nv_mthd(dev, 0x902d, 0x0248, 0x00000100); + nv_mthd(dev, 0x902d, 0x024c, 0x00000100); + nv_mthd(dev, 0x902d, 0x3410, 0x00000000); +} + +static void +nve0_graph_generate_unk40xx(struct drm_device *dev) +{ + nv_wr32(dev, 0x404010, 0x0); + nv_wr32(dev, 0x404014, 0x0); + nv_wr32(dev, 0x404018, 0x0); + nv_wr32(dev, 0x40401c, 0x0); + nv_wr32(dev, 0x404020, 0x0); + nv_wr32(dev, 0x404024, 0xe000); + nv_wr32(dev, 0x404028, 0x0); + nv_wr32(dev, 0x4040a8, 0x0); + nv_wr32(dev, 0x4040ac, 0x0); + nv_wr32(dev, 0x4040b0, 0x0); + nv_wr32(dev, 0x4040b4, 0x0); + nv_wr32(dev, 0x4040b8, 0x0); + nv_wr32(dev, 0x4040bc, 0x0); + nv_wr32(dev, 0x4040c0, 0x0); + nv_wr32(dev, 0x4040c4, 0x0); + nv_wr32(dev, 0x4040c8, 0xf800008f); + nv_wr32(dev, 0x4040d0, 0x0); + nv_wr32(dev, 0x4040d4, 0x0); + nv_wr32(dev, 0x4040d8, 0x0); + nv_wr32(dev, 0x4040dc, 0x0); + nv_wr32(dev, 0x4040e0, 0x0); + nv_wr32(dev, 0x4040e4, 0x0); + nv_wr32(dev, 0x4040e8, 0x1000); + nv_wr32(dev, 0x4040f8, 0x0); + nv_wr32(dev, 0x404130, 0x0); + nv_wr32(dev, 0x404134, 0x0); + nv_wr32(dev, 0x404138, 0x20000040); + nv_wr32(dev, 0x404150, 0x2e); + nv_wr32(dev, 0x404154, 0x400); + nv_wr32(dev, 0x404158, 0x200); + nv_wr32(dev, 0x404164, 0x55); + nv_wr32(dev, 0x4041a0, 0x0); + nv_wr32(dev, 0x4041a4, 0x0); + nv_wr32(dev, 0x4041a8, 0x0); + nv_wr32(dev, 0x4041ac, 0x0); + nv_wr32(dev, 0x404200, 0x0); + nv_wr32(dev, 0x404204, 0x0); + nv_wr32(dev, 0x404208, 0x0); + nv_wr32(dev, 0x40420c, 0x0); +} + +static void +nve0_graph_generate_unk44xx(struct drm_device *dev) +{ + nv_wr32(dev, 0x404404, 0x0); + nv_wr32(dev, 0x404408, 0x0); + nv_wr32(dev, 0x40440c, 0x0); + nv_wr32(dev, 0x404410, 0x0); + nv_wr32(dev, 0x404414, 0x0); + nv_wr32(dev, 0x404418, 0x0); + nv_wr32(dev, 0x40441c, 0x0); + nv_wr32(dev, 0x404420, 0x0); + nv_wr32(dev, 0x404424, 0x0); + nv_wr32(dev, 0x404428, 0x0); + nv_wr32(dev, 0x40442c, 0x0); + nv_wr32(dev, 0x404430, 0x0); + nv_wr32(dev, 0x404434, 0x0); + nv_wr32(dev, 0x404438, 0x0); + nv_wr32(dev, 0x404460, 0x0); + nv_wr32(dev, 0x404464, 0x0); + nv_wr32(dev, 0x404468, 0xffffff); + nv_wr32(dev, 0x40446c, 0x0); + nv_wr32(dev, 0x404480, 0x1); + nv_wr32(dev, 0x404498, 0x1); +} + +static void +nve0_graph_generate_unk46xx(struct drm_device *dev) +{ + nv_wr32(dev, 0x404604, 0x14); + nv_wr32(dev, 0x404608, 0x0); + nv_wr32(dev, 0x40460c, 0x3fff); + nv_wr32(dev, 0x404610, 0x100); + nv_wr32(dev, 0x404618, 0x0); + nv_wr32(dev, 0x40461c, 0x0); + nv_wr32(dev, 0x404620, 0x0); + nv_wr32(dev, 0x404624, 0x0); + nv_wr32(dev, 0x40462c, 0x0); + nv_wr32(dev, 0x404630, 0x0); + nv_wr32(dev, 0x404640, 0x0); + nv_wr32(dev, 0x404654, 0x0); + nv_wr32(dev, 0x404660, 0x0); + nv_wr32(dev, 0x404678, 0x0); + nv_wr32(dev, 0x40467c, 0x2); + nv_wr32(dev, 0x404680, 0x0); + nv_wr32(dev, 0x404684, 0x0); + nv_wr32(dev, 0x404688, 0x0); + nv_wr32(dev, 0x40468c, 0x0); + nv_wr32(dev, 0x404690, 0x0); + nv_wr32(dev, 0x404694, 0x0); + nv_wr32(dev, 0x404698, 0x0); + nv_wr32(dev, 0x40469c, 0x0); + nv_wr32(dev, 0x4046a0, 0x7f0080); + nv_wr32(dev, 0x4046a4, 0x0); + nv_wr32(dev, 0x4046a8, 0x0); + nv_wr32(dev, 0x4046ac, 0x0); + nv_wr32(dev, 0x4046b0, 0x0); + nv_wr32(dev, 0x4046b4, 0x0); + nv_wr32(dev, 0x4046b8, 0x0); + nv_wr32(dev, 0x4046bc, 0x0); + nv_wr32(dev, 0x4046c0, 0x0); + nv_wr32(dev, 0x4046c8, 0x0); + nv_wr32(dev, 0x4046cc, 0x0); + nv_wr32(dev, 0x4046d0, 0x0); +} + +static void +nve0_graph_generate_unk47xx(struct drm_device *dev) +{ + nv_wr32(dev, 0x404700, 0x0); + nv_wr32(dev, 0x404704, 0x0); + nv_wr32(dev, 0x404708, 0x0); + nv_wr32(dev, 0x404718, 0x0); + nv_wr32(dev, 0x40471c, 0x0); + nv_wr32(dev, 0x404720, 0x0); + nv_wr32(dev, 0x404724, 0x0); + nv_wr32(dev, 0x404728, 0x0); + nv_wr32(dev, 0x40472c, 0x0); + nv_wr32(dev, 0x404730, 0x0); + nv_wr32(dev, 0x404734, 0x100); + nv_wr32(dev, 0x404738, 0x0); + nv_wr32(dev, 0x40473c, 0x0); + nv_wr32(dev, 0x404744, 0x0); + nv_wr32(dev, 0x404748, 0x0); + nv_wr32(dev, 0x404754, 0x0); +} + +static void +nve0_graph_generate_unk58xx(struct drm_device *dev) +{ + nv_wr32(dev, 0x405800, 0xf8000bf); + nv_wr32(dev, 0x405830, 0x2180648); + nv_wr32(dev, 0x405834, 0x8000000); + nv_wr32(dev, 0x405838, 0x0); + nv_wr32(dev, 0x405854, 0x0); + nv_wr32(dev, 0x405870, 0x1); + nv_wr32(dev, 0x405874, 0x1); + nv_wr32(dev, 0x405878, 0x1); + nv_wr32(dev, 0x40587c, 0x1); + nv_wr32(dev, 0x405a00, 0x0); + nv_wr32(dev, 0x405a04, 0x0); + nv_wr32(dev, 0x405a18, 0x0); + nv_wr32(dev, 0x405b00, 0x0); + nv_wr32(dev, 0x405b10, 0x1000); +} + +static void +nve0_graph_generate_unk60xx(struct drm_device *dev) +{ + nv_wr32(dev, 0x406020, 0x4103c1); + nv_wr32(dev, 0x406028, 0x1); + nv_wr32(dev, 0x40602c, 0x1); + nv_wr32(dev, 0x406030, 0x1); + nv_wr32(dev, 0x406034, 0x1); +} + +static void +nve0_graph_generate_unk64xx(struct drm_device *dev) +{ + nv_wr32(dev, 0x4064a8, 0x0); + nv_wr32(dev, 0x4064ac, 0x3fff); + nv_wr32(dev, 0x4064b4, 0x0); + nv_wr32(dev, 0x4064b8, 0x0); + nv_wr32(dev, 0x4064c0, 0x801a00f0); + nv_wr32(dev, 0x4064c4, 0x192ffff); + nv_wr32(dev, 0x4064c8, 0x1800600); + nv_wr32(dev, 0x4064cc, 0x0); + nv_wr32(dev, 0x4064d0, 0x0); + nv_wr32(dev, 0x4064d4, 0x0); + nv_wr32(dev, 0x4064d8, 0x0); + nv_wr32(dev, 0x4064dc, 0x0); + nv_wr32(dev, 0x4064e0, 0x0); + nv_wr32(dev, 0x4064e4, 0x0); + nv_wr32(dev, 0x4064e8, 0x0); + nv_wr32(dev, 0x4064ec, 0x0); + nv_wr32(dev, 0x4064fc, 0x22a); +} + +static void +nve0_graph_generate_unk70xx(struct drm_device *dev) +{ + nv_wr32(dev, 0x407040, 0x0); +} + +static void +nve0_graph_generate_unk78xx(struct drm_device *dev) +{ + nv_wr32(dev, 0x407804, 0x23); + nv_wr32(dev, 0x40780c, 0xa418820); + nv_wr32(dev, 0x407810, 0x62080e6); + nv_wr32(dev, 0x407814, 0x20398a4); + nv_wr32(dev, 0x407818, 0xe629062); + nv_wr32(dev, 0x40781c, 0xa418820); + nv_wr32(dev, 0x407820, 0xe6); + nv_wr32(dev, 0x4078bc, 0x103); +} + +static void +nve0_graph_generate_unk80xx(struct drm_device *dev) +{ + nv_wr32(dev, 0x408000, 0x0); + nv_wr32(dev, 0x408004, 0x0); + nv_wr32(dev, 0x408008, 0x30); + nv_wr32(dev, 0x40800c, 0x0); + nv_wr32(dev, 0x408010, 0x0); + nv_wr32(dev, 0x408014, 0x69); + nv_wr32(dev, 0x408018, 0xe100e100); + nv_wr32(dev, 0x408064, 0x0); +} + +static void +nve0_graph_generate_unk88xx(struct drm_device *dev) +{ + nv_wr32(dev, 0x408800, 0x2802a3c); + nv_wr32(dev, 0x408804, 0x40); + nv_wr32(dev, 0x408808, 0x1043e005); + nv_wr32(dev, 0x408840, 0xb); + nv_wr32(dev, 0x408900, 0x3080b801); + nv_wr32(dev, 0x408904, 0x62000001); + nv_wr32(dev, 0x408908, 0xc8102f); + nv_wr32(dev, 0x408980, 0x11d); +} + +static void +nve0_graph_generate_gpc(struct drm_device *dev) +{ + nv_wr32(dev, 0x418380, 0x16); + nv_wr32(dev, 0x418400, 0x38004e00); + nv_wr32(dev, 0x418404, 0x71e0ffff); + nv_wr32(dev, 0x41840c, 0x1008); + nv_wr32(dev, 0x418410, 0xfff0fff); + nv_wr32(dev, 0x418414, 0x2200fff); + nv_wr32(dev, 0x418450, 0x0); + nv_wr32(dev, 0x418454, 0x0); + nv_wr32(dev, 0x418458, 0x0); + nv_wr32(dev, 0x41845c, 0x0); + nv_wr32(dev, 0x418460, 0x0); + nv_wr32(dev, 0x418464, 0x0); + nv_wr32(dev, 0x418468, 0x1); + nv_wr32(dev, 0x41846c, 0x0); + nv_wr32(dev, 0x418470, 0x0); + nv_wr32(dev, 0x418600, 0x1f); + nv_wr32(dev, 0x418684, 0xf); + nv_wr32(dev, 0x418700, 0x2); + nv_wr32(dev, 0x418704, 0x80); + nv_wr32(dev, 0x418708, 0x0); + nv_wr32(dev, 0x41870c, 0x0); + nv_wr32(dev, 0x418710, 0x0); + nv_wr32(dev, 0x418800, 0x7006860a); + nv_wr32(dev, 0x418808, 0x0); + nv_wr32(dev, 0x41880c, 0x0); + nv_wr32(dev, 0x418810, 0x0); + nv_wr32(dev, 0x418828, 0x44); + nv_wr32(dev, 0x418830, 0x10000001); + nv_wr32(dev, 0x4188d8, 0x8); + nv_wr32(dev, 0x4188e0, 0x1000000); + nv_wr32(dev, 0x4188e8, 0x0); + nv_wr32(dev, 0x4188ec, 0x0); + nv_wr32(dev, 0x4188f0, 0x0); + nv_wr32(dev, 0x4188f4, 0x0); + nv_wr32(dev, 0x4188f8, 0x0); + nv_wr32(dev, 0x4188fc, 0x20100018); + nv_wr32(dev, 0x41891c, 0xff00ff); + nv_wr32(dev, 0x418924, 0x0); + nv_wr32(dev, 0x418928, 0xffff00); + nv_wr32(dev, 0x41892c, 0xff00); + nv_wr32(dev, 0x418a00, 0x0); + nv_wr32(dev, 0x418a04, 0x0); + nv_wr32(dev, 0x418a08, 0x0); + nv_wr32(dev, 0x418a0c, 0x10000); + nv_wr32(dev, 0x418a10, 0x0); + nv_wr32(dev, 0x418a14, 0x0); + nv_wr32(dev, 0x418a18, 0x0); + nv_wr32(dev, 0x418a20, 0x0); + nv_wr32(dev, 0x418a24, 0x0); + nv_wr32(dev, 0x418a28, 0x0); + nv_wr32(dev, 0x418a2c, 0x10000); + nv_wr32(dev, 0x418a30, 0x0); + nv_wr32(dev, 0x418a34, 0x0); + nv_wr32(dev, 0x418a38, 0x0); + nv_wr32(dev, 0x418a40, 0x0); + nv_wr32(dev, 0x418a44, 0x0); + nv_wr32(dev, 0x418a48, 0x0); + nv_wr32(dev, 0x418a4c, 0x10000); + nv_wr32(dev, 0x418a50, 0x0); + nv_wr32(dev, 0x418a54, 0x0); + nv_wr32(dev, 0x418a58, 0x0); + nv_wr32(dev, 0x418a60, 0x0); + nv_wr32(dev, 0x418a64, 0x0); + nv_wr32(dev, 0x418a68, 0x0); + nv_wr32(dev, 0x418a6c, 0x10000); + nv_wr32(dev, 0x418a70, 0x0); + nv_wr32(dev, 0x418a74, 0x0); + nv_wr32(dev, 0x418a78, 0x0); + nv_wr32(dev, 0x418a80, 0x0); + nv_wr32(dev, 0x418a84, 0x0); + nv_wr32(dev, 0x418a88, 0x0); + nv_wr32(dev, 0x418a8c, 0x10000); + nv_wr32(dev, 0x418a90, 0x0); + nv_wr32(dev, 0x418a94, 0x0); + nv_wr32(dev, 0x418a98, 0x0); + nv_wr32(dev, 0x418aa0, 0x0); + nv_wr32(dev, 0x418aa4, 0x0); + nv_wr32(dev, 0x418aa8, 0x0); + nv_wr32(dev, 0x418aac, 0x10000); + nv_wr32(dev, 0x418ab0, 0x0); + nv_wr32(dev, 0x418ab4, 0x0); + nv_wr32(dev, 0x418ab8, 0x0); + nv_wr32(dev, 0x418ac0, 0x0); + nv_wr32(dev, 0x418ac4, 0x0); + nv_wr32(dev, 0x418ac8, 0x0); + nv_wr32(dev, 0x418acc, 0x10000); + nv_wr32(dev, 0x418ad0, 0x0); + nv_wr32(dev, 0x418ad4, 0x0); + nv_wr32(dev, 0x418ad8, 0x0); + nv_wr32(dev, 0x418ae0, 0x0); + nv_wr32(dev, 0x418ae4, 0x0); + nv_wr32(dev, 0x418ae8, 0x0); + nv_wr32(dev, 0x418aec, 0x10000); + nv_wr32(dev, 0x418af0, 0x0); + nv_wr32(dev, 0x418af4, 0x0); + nv_wr32(dev, 0x418af8, 0x0); + nv_wr32(dev, 0x418b00, 0x6); + nv_wr32(dev, 0x418b08, 0xa418820); + nv_wr32(dev, 0x418b0c, 0x62080e6); + nv_wr32(dev, 0x418b10, 0x20398a4); + nv_wr32(dev, 0x418b14, 0xe629062); + nv_wr32(dev, 0x418b18, 0xa418820); + nv_wr32(dev, 0x418b1c, 0xe6); + nv_wr32(dev, 0x418bb8, 0x103); + nv_wr32(dev, 0x418c08, 0x1); + nv_wr32(dev, 0x418c10, 0x0); + nv_wr32(dev, 0x418c14, 0x0); + nv_wr32(dev, 0x418c18, 0x0); + nv_wr32(dev, 0x418c1c, 0x0); + nv_wr32(dev, 0x418c20, 0x0); + nv_wr32(dev, 0x418c24, 0x0); + nv_wr32(dev, 0x418c28, 0x0); + nv_wr32(dev, 0x418c2c, 0x0); + nv_wr32(dev, 0x418c40, 0xffffffff); + nv_wr32(dev, 0x418c6c, 0x1); + nv_wr32(dev, 0x418c80, 0x20200004); + nv_wr32(dev, 0x418c8c, 0x1); + nv_wr32(dev, 0x419000, 0x780); + nv_wr32(dev, 0x419004, 0x0); + nv_wr32(dev, 0x419008, 0x0); + nv_wr32(dev, 0x419014, 0x4); +} + +static void +nve0_graph_generate_tpc(struct drm_device *dev) +{ + nv_wr32(dev, 0x419848, 0x0); + nv_wr32(dev, 0x419864, 0x129); + nv_wr32(dev, 0x419888, 0x0); + nv_wr32(dev, 0x419a00, 0xf0); + nv_wr32(dev, 0x419a04, 0x1); + nv_wr32(dev, 0x419a08, 0x21); + nv_wr32(dev, 0x419a0c, 0x20000); + nv_wr32(dev, 0x419a10, 0x0); + nv_wr32(dev, 0x419a14, 0x200); + nv_wr32(dev, 0x419a1c, 0xc000); + nv_wr32(dev, 0x419a20, 0x800); + nv_wr32(dev, 0x419a30, 0x1); + nv_wr32(dev, 0x419ac4, 0x37f440); + nv_wr32(dev, 0x419c00, 0xa); + nv_wr32(dev, 0x419c04, 0x80000006); + nv_wr32(dev, 0x419c08, 0x2); + nv_wr32(dev, 0x419c20, 0x0); + nv_wr32(dev, 0x419c24, 0x84210); + nv_wr32(dev, 0x419c28, 0x3efbefbe); + nv_wr32(dev, 0x419ce8, 0x0); + nv_wr32(dev, 0x419cf4, 0x3203); + nv_wr32(dev, 0x419e04, 0x0); + nv_wr32(dev, 0x419e08, 0x0); + nv_wr32(dev, 0x419e0c, 0x0); + nv_wr32(dev, 0x419e10, 0x402); + nv_wr32(dev, 0x419e44, 0x13eff2); + nv_wr32(dev, 0x419e48, 0x0); + nv_wr32(dev, 0x419e4c, 0x7f); + nv_wr32(dev, 0x419e50, 0x0); + nv_wr32(dev, 0x419e54, 0x0); + nv_wr32(dev, 0x419e58, 0x0); + nv_wr32(dev, 0x419e5c, 0x0); + nv_wr32(dev, 0x419e60, 0x0); + nv_wr32(dev, 0x419e64, 0x0); + nv_wr32(dev, 0x419e68, 0x0); + nv_wr32(dev, 0x419e6c, 0x0); + nv_wr32(dev, 0x419e70, 0x0); + nv_wr32(dev, 0x419e74, 0x0); + nv_wr32(dev, 0x419e78, 0x0); + nv_wr32(dev, 0x419e7c, 0x0); + nv_wr32(dev, 0x419e80, 0x0); + nv_wr32(dev, 0x419e84, 0x0); + nv_wr32(dev, 0x419e88, 0x0); + nv_wr32(dev, 0x419e8c, 0x0); + nv_wr32(dev, 0x419e90, 0x0); + nv_wr32(dev, 0x419e94, 0x0); + nv_wr32(dev, 0x419e98, 0x0); + nv_wr32(dev, 0x419eac, 0x1fcf); + nv_wr32(dev, 0x419eb0, 0xd3f); + nv_wr32(dev, 0x419ec8, 0x1304f); + nv_wr32(dev, 0x419f30, 0x0); + nv_wr32(dev, 0x419f34, 0x0); + nv_wr32(dev, 0x419f38, 0x0); + nv_wr32(dev, 0x419f3c, 0x0); + nv_wr32(dev, 0x419f40, 0x0); + nv_wr32(dev, 0x419f44, 0x0); + nv_wr32(dev, 0x419f48, 0x0); + nv_wr32(dev, 0x419f4c, 0x0); + nv_wr32(dev, 0x419f58, 0x0); + nv_wr32(dev, 0x419f78, 0xb); +} + +static void +nve0_graph_generate_tpcunk(struct drm_device *dev) +{ + nv_wr32(dev, 0x41be24, 0x6); + nv_wr32(dev, 0x41bec0, 0x12180000); + nv_wr32(dev, 0x41bec4, 0x37f7f); + nv_wr32(dev, 0x41bee4, 0x6480430); + nv_wr32(dev, 0x41bf00, 0xa418820); + nv_wr32(dev, 0x41bf04, 0x62080e6); + nv_wr32(dev, 0x41bf08, 0x20398a4); + nv_wr32(dev, 0x41bf0c, 0xe629062); + nv_wr32(dev, 0x41bf10, 0xa418820); + nv_wr32(dev, 0x41bf14, 0xe6); + nv_wr32(dev, 0x41bfd0, 0x900103); + nv_wr32(dev, 0x41bfe0, 0x400001); + nv_wr32(dev, 0x41bfe4, 0x0); +} + +int +nve0_grctx_generate(struct nouveau_channel *chan) +{ + struct nve0_graph_priv *priv = nv_engine(chan->dev, NVOBJ_ENGINE_GR); + struct nve0_graph_chan *grch = chan->engctx[NVOBJ_ENGINE_GR]; + struct drm_device *dev = chan->dev; + u32 data[6] = {}, data2[2] = {}, tmp; + u32 tpc_set = 0, tpc_mask = 0; + u8 tpcnr[GPC_MAX], a, b; + u8 shift, ntpcv; + int i, gpc, tpc, id; + + nv_mask(dev, 0x000260, 0x00000001, 0x00000000); + nv_wr32(dev, 0x400204, 0x00000000); + nv_wr32(dev, 0x400208, 0x00000000); + + nve0_graph_generate_unk40xx(dev); + nve0_graph_generate_unk44xx(dev); + nve0_graph_generate_unk46xx(dev); + nve0_graph_generate_unk47xx(dev); + nve0_graph_generate_unk58xx(dev); + nve0_graph_generate_unk60xx(dev); + nve0_graph_generate_unk64xx(dev); + nve0_graph_generate_unk70xx(dev); + nve0_graph_generate_unk78xx(dev); + nve0_graph_generate_unk80xx(dev); + nve0_graph_generate_unk88xx(dev); + nve0_graph_generate_gpc(dev); + nve0_graph_generate_tpc(dev); + nve0_graph_generate_tpcunk(dev); + + nv_wr32(dev, 0x404154, 0x0); + + for (i = 0; i < grch->mmio_nr * 8; i += 8) { + u32 reg = nv_ro32(grch->mmio, i + 0); + u32 val = nv_ro32(grch->mmio, i + 4); + nv_wr32(dev, reg, val); + } + + nv_wr32(dev, 0x418c6c, 0x1); + nv_wr32(dev, 0x41980c, 0x10); + nv_wr32(dev, 0x41be08, 0x4); + nv_wr32(dev, 0x4064c0, 0x801a00f0); + nv_wr32(dev, 0x405800, 0xf8000bf); + nv_wr32(dev, 0x419c00, 0xa); + + for (tpc = 0, id = 0; tpc < 4; tpc++) { + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + if (tpc < priv->tpc_nr[gpc]) { + nv_wr32(dev, TPC_UNIT(gpc, tpc, 0x0698), id); + nv_wr32(dev, TPC_UNIT(gpc, tpc, 0x04e8), id); + nv_wr32(dev, GPC_UNIT(gpc, 0x0c10 + tpc * 4), id); + nv_wr32(dev, TPC_UNIT(gpc, tpc, 0x0088), id++); + } + + nv_wr32(dev, GPC_UNIT(gpc, 0x0c08), priv->tpc_nr[gpc]); + nv_wr32(dev, GPC_UNIT(gpc, 0x0c8c), priv->tpc_nr[gpc]); + } + } + + tmp = 0; + for (i = 0; i < priv->gpc_nr; i++) + tmp |= priv->tpc_nr[i] << (i * 4); + nv_wr32(dev, 0x406028, tmp); + nv_wr32(dev, 0x405870, tmp); + + nv_wr32(dev, 0x40602c, 0x0); + nv_wr32(dev, 0x405874, 0x0); + nv_wr32(dev, 0x406030, 0x0); + nv_wr32(dev, 0x405878, 0x0); + nv_wr32(dev, 0x406034, 0x0); + nv_wr32(dev, 0x40587c, 0x0); + + /* calculate first set of magics */ + memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); + + gpc = -1; + for (tpc = 0; tpc < priv->tpc_total; tpc++) { + do { + gpc = (gpc + 1) % priv->gpc_nr; + } while (!tpcnr[gpc]); + tpcnr[gpc]--; + + data[tpc / 6] |= gpc << ((tpc % 6) * 5); + } + + for (; tpc < 32; tpc++) + data[tpc / 6] |= 7 << ((tpc % 6) * 5); + + /* and the second... */ + shift = 0; + ntpcv = priv->tpc_total; + while (!(ntpcv & (1 << 4))) { + ntpcv <<= 1; + shift++; + } + + data2[0] = ntpcv << 16; + data2[0] |= shift << 21; + data2[0] |= (((1 << (0 + 5)) % ntpcv) << 24); + data2[0] |= priv->tpc_total << 8; + data2[0] |= priv->magic_not_rop_nr; + for (i = 1; i < 7; i++) + data2[1] |= ((1 << (i + 5)) % ntpcv) << ((i - 1) * 5); + + /* and write it all the various parts of PGRAPH */ + nv_wr32(dev, 0x418bb8, (priv->tpc_total << 8) | priv->magic_not_rop_nr); + for (i = 0; i < 6; i++) + nv_wr32(dev, 0x418b08 + (i * 4), data[i]); + + nv_wr32(dev, 0x41bfd0, data2[0]); + nv_wr32(dev, 0x41bfe4, data2[1]); + for (i = 0; i < 6; i++) + nv_wr32(dev, 0x41bf00 + (i * 4), data[i]); + + nv_wr32(dev, 0x4078bc, (priv->tpc_total << 8) | priv->magic_not_rop_nr); + for (i = 0; i < 6; i++) + nv_wr32(dev, 0x40780c + (i * 4), data[i]); + + + memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); + for (gpc = 0; gpc < priv->gpc_nr; gpc++) + tpc_mask |= ((1 << priv->tpc_nr[gpc]) - 1) << (gpc * 8); + + for (i = 0, gpc = -1, b = -1; i < 32; i++) { + a = (i * (priv->tpc_total - 1)) / 32; + if (a != b) { + b = a; + do { + gpc = (gpc + 1) % priv->gpc_nr; + } while (!tpcnr[gpc]); + tpc = priv->tpc_nr[gpc] - tpcnr[gpc]--; + + tpc_set |= 1 << ((gpc * 8) + tpc); + } + + nv_wr32(dev, 0x406800 + (i * 0x20), tpc_set); + nv_wr32(dev, 0x406c00 + (i * 0x20), tpc_set ^ tpc_mask); + } + + for (i = 0; i < 8; i++) + nv_wr32(dev, 0x4064d0 + (i * 0x04), 0x00000000); + + nv_wr32(dev, 0x405b00, 0x201); + nv_wr32(dev, 0x408850, 0x2); + nv_wr32(dev, 0x408958, 0x2); + nv_wr32(dev, 0x419f78, 0xa); + + nve0_grctx_generate_icmd(dev); + nve0_grctx_generate_a097(dev); + nve0_grctx_generate_902d(dev); + + nv_mask(dev, 0x000260, 0x00000001, 0x00000001); + nv_wr32(dev, 0x418800, 0x7026860a); //XXX + nv_wr32(dev, 0x41be10, 0x00bb8bc7); //XXX + return 0; +} diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index 1efb6eba3c2..a6598fd6642 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -72,7 +72,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ evergreen.o evergreen_cs.o evergreen_blit_shaders.o evergreen_blit_kms.o \ evergreen_hdmi.o radeon_trace_points.o ni.o cayman_blit_shaders.o \ atombios_encoders.o radeon_semaphore.o radeon_sa.o atombios_i2c.o si.o \ - si_blit_shaders.o + si_blit_shaders.o radeon_prime.o radeon-$(CONFIG_COMPAT) += radeon_ioc32.o radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index cadbb107c80..5131b3b0f7d 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -530,6 +530,23 @@ u8 radeon_dp_getsinktype(struct radeon_connector *radeon_connector) dig_connector->dp_i2c_bus->rec.i2c_id, 0); } +static void radeon_dp_probe_oui(struct radeon_connector *radeon_connector) +{ + struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; + u8 buf[3]; + + if (!(dig_connector->dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_OUI_SUPPORT)) + return; + + if (radeon_dp_aux_native_read(radeon_connector, DP_SINK_OUI, buf, 3, 0)) + DRM_DEBUG_KMS("Sink OUI: %02hx%02hx%02hx\n", + buf[0], buf[1], buf[2]); + + if (radeon_dp_aux_native_read(radeon_connector, DP_BRANCH_OUI, buf, 3, 0)) + DRM_DEBUG_KMS("Branch OUI: %02hx%02hx%02hx\n", + buf[0], buf[1], buf[2]); +} + bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector) { struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; @@ -543,6 +560,9 @@ bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector) for (i = 0; i < 8; i++) DRM_DEBUG_KMS("%02x ", msg[i]); DRM_DEBUG_KMS("\n"); + + radeon_dp_probe_oui(radeon_connector); + return true; } dig_connector->dpcd[0] = 0; diff --git a/drivers/gpu/drm/radeon/evergreen_blit_kms.c b/drivers/gpu/drm/radeon/evergreen_blit_kms.c index 30f04800b38..1e96bd458cf 100644 --- a/drivers/gpu/drm/radeon/evergreen_blit_kms.c +++ b/drivers/gpu/drm/radeon/evergreen_blit_kms.c @@ -668,7 +668,7 @@ int evergreen_blit_init(struct radeon_device *rdev) obj_size = ALIGN(obj_size, 256); r = radeon_bo_create(rdev, obj_size, PAGE_SIZE, true, RADEON_GEM_DOMAIN_VRAM, - &rdev->r600_blit.shader_obj); + NULL, &rdev->r600_blit.shader_obj); if (r) { DRM_ERROR("evergreen failed to allocate shader\n"); return r; diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c index b01c2dd627b..ce4e7cc6c90 100644 --- a/drivers/gpu/drm/radeon/ni.c +++ b/drivers/gpu/drm/radeon/ni.c @@ -865,7 +865,7 @@ static void cayman_gpu_init(struct radeon_device *rdev) /* num banks is 8 on all fusion asics. 0 = 4, 1 = 8, 2 = 16 */ if (rdev->flags & RADEON_IS_IGP) - rdev->config.evergreen.tile_config |= 1 << 4; + rdev->config.cayman.tile_config |= 1 << 4; else rdev->config.cayman.tile_config |= ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) << 4; diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index ab5d6f2a06c..f388a1d73b6 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -1231,7 +1231,7 @@ int r600_vram_scratch_init(struct radeon_device *rdev) if (rdev->vram_scratch.robj == NULL) { r = radeon_bo_create(rdev, RADEON_GPU_PAGE_SIZE, PAGE_SIZE, true, RADEON_GEM_DOMAIN_VRAM, - &rdev->vram_scratch.robj); + NULL, &rdev->vram_scratch.robj); if (r) { return r; } @@ -2769,7 +2769,7 @@ int r600_ih_ring_alloc(struct radeon_device *rdev) r = radeon_bo_create(rdev, rdev->ih.ring_size, PAGE_SIZE, true, RADEON_GEM_DOMAIN_GTT, - &rdev->ih.ring_obj); + NULL, &rdev->ih.ring_obj); if (r) { DRM_ERROR("radeon: failed to create ih ring buffer (%d).\n", r); return r; diff --git a/drivers/gpu/drm/radeon/r600_audio.c b/drivers/gpu/drm/radeon/r600_audio.c index b5a602e71c1..7c4fa77f018 100644 --- a/drivers/gpu/drm/radeon/r600_audio.c +++ b/drivers/gpu/drm/radeon/r600_audio.c @@ -63,67 +63,56 @@ static int r600_audio_chipset_supported(struct radeon_device *rdev) || rdev->family == CHIP_RS740; } -/* - * current number of channels - */ -int r600_audio_channels(struct radeon_device *rdev) +struct r600_audio r600_audio_status(struct radeon_device *rdev) { - return (RREG32(R600_AUDIO_RATE_BPS_CHANNEL) & 0x7) + 1; -} + struct r600_audio status; + uint32_t value; -/* - * current bits per sample - */ -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; - } + value = RREG32(R600_AUDIO_RATE_BPS_CHANNEL); - dev_err(rdev->dev, "Unknown bits per sample 0x%x using 16 instead\n", - (int)value); + /* number of channels */ + status.channels = (value & 0x7) + 1; - return 16; -} - -/* - * current sampling rate in HZ - */ -int r600_audio_rate(struct radeon_device *rdev) -{ - uint32_t value = RREG32(R600_AUDIO_RATE_BPS_CHANNEL); - uint32_t result; + /* bits per sample */ + switch ((value & 0xF0) >> 4) { + case 0x0: + status.bits_per_sample = 8; + break; + case 0x1: + status.bits_per_sample = 16; + break; + case 0x2: + status.bits_per_sample = 20; + break; + case 0x3: + status.bits_per_sample = 24; + break; + case 0x4: + status.bits_per_sample = 32; + break; + default: + dev_err(rdev->dev, "Unknown bits per sample 0x%x, using 16\n", + (int)value); + status.bits_per_sample = 16; + } + /* current sampling rate in HZ */ if (value & 0x4000) - result = 44100; + status.rate = 44100; else - result = 48000; + status.rate = 48000; + status.rate *= ((value >> 11) & 0x7) + 1; + status.rate /= ((value >> 8) & 0x7) + 1; - result *= ((value >> 11) & 0x7) + 1; - result /= ((value >> 8) & 0x7) + 1; + value = RREG32(R600_AUDIO_STATUS_BITS); - return result; -} + /* iec 60958 status bits */ + status.status_bits = value & 0xff; -/* - * iec 60958 status bits - */ -uint8_t r600_audio_status_bits(struct radeon_device *rdev) -{ - return RREG32(R600_AUDIO_STATUS_BITS) & 0xff; -} + /* iec 60958 category code */ + status.category_code = (value >> 8) & 0xff; -/* - * iec 60958 category code - */ -uint8_t r600_audio_category_code(struct radeon_device *rdev) -{ - return (RREG32(R600_AUDIO_STATUS_BITS) >> 8) & 0xff; + return status; } /* @@ -134,33 +123,23 @@ void r600_audio_update_hdmi(struct work_struct *work) struct radeon_device *rdev = container_of(work, struct radeon_device, audio_work); 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 r600_audio audio_status = r600_audio_status(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; + bool changed = false; + + if (rdev->audio_status.channels != audio_status.channels || + rdev->audio_status.rate != audio_status.rate || + rdev->audio_status.bits_per_sample != audio_status.bits_per_sample || + rdev->audio_status.status_bits != audio_status.status_bits || + rdev->audio_status.category_code != audio_status.category_code) { + rdev->audio_status = audio_status; + changed = true; } list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { if (!radeon_dig_encoder(encoder)) continue; - if (changes || r600_hdmi_buffer_status_changed(encoder)) + if (changed || r600_hdmi_buffer_status_changed(encoder)) r600_hdmi_update_audio_settings(encoder); } } @@ -182,7 +161,7 @@ static void r600_audio_engine_enable(struct radeon_device *rdev, bool enable) WREG32_P(R600_AUDIO_ENABLE, enable ? 0x81000000 : 0x0, ~0x81000000); } - rdev->audio.enabled = enable; + rdev->audio_enabled = enable; } /* @@ -195,11 +174,11 @@ int r600_audio_init(struct radeon_device *rdev) r600_audio_engine_enable(rdev, true); - rdev->audio.channels = -1; - rdev->audio.rate = -1; - rdev->audio.bits_per_sample = -1; - rdev->audio.status_bits = 0; - rdev->audio.category_code = 0; + rdev->audio_status.channels = -1; + rdev->audio_status.rate = -1; + rdev->audio_status.bits_per_sample = -1; + rdev->audio_status.status_bits = 0; + rdev->audio_status.category_code = 0; return 0; } @@ -268,7 +247,7 @@ void r600_audio_set_clock(struct drm_encoder *encoder, int clock) */ void r600_audio_fini(struct radeon_device *rdev) { - if (!rdev->audio.enabled) + if (!rdev->audio_enabled) return; r600_audio_engine_enable(rdev, false); diff --git a/drivers/gpu/drm/radeon/r600_blit_kms.c b/drivers/gpu/drm/radeon/r600_blit_kms.c index ef20822a158..03b6e0d3d50 100644 --- a/drivers/gpu/drm/radeon/r600_blit_kms.c +++ b/drivers/gpu/drm/radeon/r600_blit_kms.c @@ -552,7 +552,7 @@ int r600_blit_init(struct radeon_device *rdev) obj_size = ALIGN(obj_size, 256); r = radeon_bo_create(rdev, obj_size, PAGE_SIZE, true, RADEON_GEM_DOMAIN_VRAM, - &rdev->r600_blit.shader_obj); + NULL, &rdev->r600_blit.shader_obj); if (r) { DRM_ERROR("r600 failed to allocate shader\n"); return r; diff --git a/drivers/gpu/drm/radeon/r600_hdmi.c b/drivers/gpu/drm/radeon/r600_hdmi.c index 31d19509e1d..226379e00ac 100644 --- a/drivers/gpu/drm/radeon/r600_hdmi.c +++ b/drivers/gpu/drm/radeon/r600_hdmi.c @@ -398,14 +398,8 @@ void r600_hdmi_update_audio_settings(struct drm_encoder *encoder) struct radeon_device *rdev = dev->dev_private; struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + struct r600_audio audio = r600_audio_status(rdev); uint32_t offset; - - 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); - uint32_t iec; if (!dig->afmt || !dig->afmt->enabled) @@ -414,23 +408,23 @@ void r600_hdmi_update_audio_settings(struct drm_encoder *encoder) 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); + audio.channels, audio.rate, audio.bits_per_sample); DRM_DEBUG("0x%02X IEC60958 status bits and 0x%02X category code\n", - (int)status_bits, (int)category_code); + (int)audio.status_bits, (int)audio.category_code); iec = 0; - if (status_bits & AUDIO_STATUS_PROFESSIONAL) + if (audio.status_bits & AUDIO_STATUS_PROFESSIONAL) iec |= 1 << 0; - if (status_bits & AUDIO_STATUS_NONAUDIO) + if (audio.status_bits & AUDIO_STATUS_NONAUDIO) iec |= 1 << 1; - if (status_bits & AUDIO_STATUS_COPYRIGHT) + if (audio.status_bits & AUDIO_STATUS_COPYRIGHT) iec |= 1 << 2; - if (status_bits & AUDIO_STATUS_EMPHASIS) + if (audio.status_bits & AUDIO_STATUS_EMPHASIS) iec |= 1 << 3; - iec |= HDMI0_60958_CS_CATEGORY_CODE(category_code); + iec |= HDMI0_60958_CS_CATEGORY_CODE(audio.category_code); - switch (rate) { + switch (audio.rate) { case 32000: iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0x3); break; @@ -457,7 +451,7 @@ void r600_hdmi_update_audio_settings(struct drm_encoder *encoder) WREG32(HDMI0_60958_0 + offset, iec); iec = 0; - switch (bps) { + switch (audio.bits_per_sample) { case 16: iec |= HDMI0_60958_CS_WORD_LENGTH(0x2); break; @@ -468,11 +462,12 @@ void r600_hdmi_update_audio_settings(struct drm_encoder *encoder) iec |= HDMI0_60958_CS_WORD_LENGTH(0xb); break; } - if (status_bits & AUDIO_STATUS_V) + if (audio.status_bits & AUDIO_STATUS_V) iec |= 0x5 << 16; WREG32_P(HDMI0_60958_1 + offset, iec, ~0x5000f); - r600_hdmi_audioinfoframe(encoder, channels - 1, 0, 0, 0, 0, 0, 0, 0); + r600_hdmi_audioinfoframe(encoder, audio.channels - 1, 0, 0, 0, 0, 0, 0, + 0); r600_hdmi_audio_workaround(encoder); } diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 9783178a8ff..492654f8ee7 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -848,7 +848,6 @@ struct radeon_cs_parser { s32 priority; }; -extern int radeon_cs_update_pages(struct radeon_cs_parser *p, int pg_idx); extern int radeon_cs_finish_pages(struct radeon_cs_parser *p); extern u32 radeon_get_ib_value(struct radeon_cs_parser *p, int idx); @@ -1094,7 +1093,6 @@ int radeon_pm_get_type_index(struct radeon_device *rdev, int instance); struct r600_audio { - bool enabled; int channels; int rate; int bits_per_sample; @@ -1535,7 +1533,8 @@ struct radeon_device { int num_crtc; /* number of crtcs */ struct mutex dc_hw_i2c_mutex; /* display controller hw i2c mutex */ struct mutex vram_mutex; - struct r600_audio audio; /* audio stuff */ + bool audio_enabled; + struct r600_audio audio_status; /* audio stuff */ struct notifier_block acpi_nb; /* only one userspace can use Hyperz features or CMASK at a time */ struct drm_file *hyperz_filp; diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index ef9ccb4def4..e76a941ef14 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -356,13 +356,8 @@ void r600_disable_interrupts(struct radeon_device *rdev); void r600_rlc_stop(struct radeon_device *rdev); /* r600 audio */ int r600_audio_init(struct radeon_device *rdev); -int r600_audio_tmds_index(struct drm_encoder *encoder); void r600_audio_set_clock(struct drm_encoder *encoder, int clock); -int r600_audio_channels(struct radeon_device *rdev); -int r600_audio_bits_per_sample(struct radeon_device *rdev); -int r600_audio_rate(struct radeon_device *rdev); -uint8_t r600_audio_status_bits(struct radeon_device *rdev); -uint8_t r600_audio_category_code(struct radeon_device *rdev); +struct r600_audio r600_audio_status(struct radeon_device *rdev); void r600_audio_fini(struct radeon_device *rdev); int r600_hdmi_buffer_status_changed(struct drm_encoder *encoder); void r600_hdmi_update_audio_settings(struct drm_encoder *encoder); diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index f6e69b8c06c..b1e3820df36 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -444,7 +444,9 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev, */ if ((dev->pdev->device == 0x9498) && (dev->pdev->subsystem_vendor == 0x1682) && - (dev->pdev->subsystem_device == 0x2452)) { + (dev->pdev->subsystem_device == 0x2452) && + (i2c_bus->valid == false) && + !(supported_device & (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT))) { struct radeon_device *rdev = dev->dev_private; *i2c_bus = radeon_lookup_i2c_gpio(rdev, 0x93); } diff --git a/drivers/gpu/drm/radeon/radeon_benchmark.c b/drivers/gpu/drm/radeon/radeon_benchmark.c index fef7b722b05..364f5b1a04b 100644 --- a/drivers/gpu/drm/radeon/radeon_benchmark.c +++ b/drivers/gpu/drm/radeon/radeon_benchmark.c @@ -103,7 +103,7 @@ static void radeon_benchmark_move(struct radeon_device *rdev, unsigned size, int time; n = RADEON_BENCHMARK_ITERATIONS; - r = radeon_bo_create(rdev, size, PAGE_SIZE, true, sdomain, &sobj); + r = radeon_bo_create(rdev, size, PAGE_SIZE, true, sdomain, NULL, &sobj); if (r) { goto out_cleanup; } @@ -115,7 +115,7 @@ static void radeon_benchmark_move(struct radeon_device *rdev, unsigned size, if (r) { goto out_cleanup; } - r = radeon_bo_create(rdev, size, PAGE_SIZE, true, ddomain, &dobj); + r = radeon_bo_create(rdev, size, PAGE_SIZE, true, ddomain, NULL, &dobj); if (r) { goto out_cleanup; } diff --git a/drivers/gpu/drm/radeon/radeon_cp.c b/drivers/gpu/drm/radeon/radeon_cp.c index 0ebb7d4796f..ef67e181377 100644 --- a/drivers/gpu/drm/radeon/radeon_cp.c +++ b/drivers/gpu/drm/radeon/radeon_cp.c @@ -1827,14 +1827,10 @@ void radeon_do_release(struct drm_device * dev) r600_do_cleanup_cp(dev); else radeon_do_cleanup_cp(dev); - if (dev_priv->me_fw) { - release_firmware(dev_priv->me_fw); - dev_priv->me_fw = NULL; - } - if (dev_priv->pfp_fw) { - release_firmware(dev_priv->pfp_fw); - dev_priv->pfp_fw = NULL; - } + release_firmware(dev_priv->me_fw); + dev_priv->me_fw = NULL; + release_firmware(dev_priv->pfp_fw); + dev_priv->pfp_fw = NULL; } } diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index c7d64a73903..0137689ed46 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -580,7 +580,7 @@ int radeon_cs_finish_pages(struct radeon_cs_parser *p) return 0; } -int radeon_cs_update_pages(struct radeon_cs_parser *p, int pg_idx) +static int radeon_cs_update_pages(struct radeon_cs_parser *p, int pg_idx) { int new_page; struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx]; @@ -623,3 +623,28 @@ int radeon_cs_update_pages(struct radeon_cs_parser *p, int pg_idx) return new_page; } + +u32 radeon_get_ib_value(struct radeon_cs_parser *p, int idx) +{ + struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx]; + u32 pg_idx, pg_offset; + u32 idx_value = 0; + int new_page; + + pg_idx = (idx * 4) / PAGE_SIZE; + pg_offset = (idx * 4) % PAGE_SIZE; + + if (ibc->kpage_idx[0] == pg_idx) + return ibc->kpage[0][pg_offset/4]; + if (ibc->kpage_idx[1] == pg_idx) + return ibc->kpage[1][pg_offset/4]; + + new_page = radeon_cs_update_pages(p, pg_idx); + if (new_page < 0) { + p->parser_error = new_page; + return 0; + } + + idx_value = ibc->kpage[new_page][pg_offset/4]; + return idx_value; +} diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 57637017eed..066c98b888a 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -193,7 +193,7 @@ int radeon_wb_init(struct radeon_device *rdev) if (rdev->wb.wb_obj == NULL) { r = radeon_bo_create(rdev, RADEON_GPU_PAGE_SIZE, PAGE_SIZE, true, - RADEON_GEM_DOMAIN_GTT, &rdev->wb.wb_obj); + RADEON_GEM_DOMAIN_GTT, NULL, &rdev->wb.wb_obj); if (r) { dev_warn(rdev->dev, "(%d) create WB bo failed\n", r); return r; diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index da3fe8a68f8..64a008d1449 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -1320,7 +1320,7 @@ int radeon_modeset_init(struct radeon_device *rdev) drm_mode_config_init(rdev->ddev); rdev->mode_info.mode_config_initialized = true; - rdev->ddev->mode_config.funcs = (void *)&radeon_mode_funcs; + rdev->ddev->mode_config.funcs = &radeon_mode_funcs; if (ASIC_IS_DCE5(rdev)) { rdev->ddev->mode_config.max_width = 16384; diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index e62e56a57ee..f0bb2b543b1 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -105,6 +105,11 @@ int radeon_mode_dumb_create(struct drm_file *file_priv, int radeon_mode_dumb_destroy(struct drm_file *file_priv, struct drm_device *dev, uint32_t handle); +struct dma_buf *radeon_gem_prime_export(struct drm_device *dev, + struct drm_gem_object *obj, + int flags); +struct drm_gem_object *radeon_gem_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf); #if defined(CONFIG_DEBUG_FS) int radeon_debugfs_init(struct drm_minor *minor); @@ -333,7 +338,8 @@ static const struct file_operations radeon_driver_kms_fops = { static struct drm_driver kms_driver = { .driver_features = DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG | - DRIVER_HAVE_IRQ | DRIVER_HAVE_DMA | DRIVER_IRQ_SHARED | DRIVER_GEM, + DRIVER_HAVE_IRQ | DRIVER_HAVE_DMA | DRIVER_IRQ_SHARED | DRIVER_GEM | + DRIVER_PRIME, .dev_priv_size = 0, .load = radeon_driver_load_kms, .firstopen = radeon_driver_firstopen_kms, @@ -368,6 +374,12 @@ static struct drm_driver kms_driver = { .dumb_map_offset = radeon_mode_dumb_mmap, .dumb_destroy = radeon_mode_dumb_destroy, .fops = &radeon_driver_kms_fops, + + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_export = radeon_gem_prime_export, + .gem_prime_import = radeon_gem_prime_import, + .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = DRIVER_DATE, diff --git a/drivers/gpu/drm/radeon/radeon_gart.c b/drivers/gpu/drm/radeon/radeon_gart.c index 8e9ef3403ac..79db56e6c2a 100644 --- a/drivers/gpu/drm/radeon/radeon_gart.c +++ b/drivers/gpu/drm/radeon/radeon_gart.c @@ -80,7 +80,7 @@ int radeon_gart_table_vram_alloc(struct radeon_device *rdev) if (rdev->gart.robj == NULL) { r = radeon_bo_create(rdev, rdev->gart.table_size, PAGE_SIZE, true, RADEON_GEM_DOMAIN_VRAM, - &rdev->gart.robj); + NULL, &rdev->gart.robj); if (r) { return r; } @@ -549,7 +549,7 @@ int radeon_vm_bo_update_pte(struct radeon_device *rdev, /* nothing to do if vm isn't bound */ if (vm->id == -1) - return 0;; + return 0; bo_va = radeon_bo_va(bo, vm); if (bo_va == NULL) { diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c index e15cb1fe2c3..f28bd4b7ef9 100644 --- a/drivers/gpu/drm/radeon/radeon_gem.c +++ b/drivers/gpu/drm/radeon/radeon_gem.c @@ -42,6 +42,8 @@ void radeon_gem_object_free(struct drm_gem_object *gobj) struct radeon_bo *robj = gem_to_radeon_bo(gobj); if (robj) { + if (robj->gem_base.import_attach) + drm_prime_gem_destroy(&robj->gem_base, robj->tbo.sg); radeon_bo_unref(&robj); } } @@ -59,7 +61,7 @@ int radeon_gem_object_create(struct radeon_device *rdev, int size, if (alignment < PAGE_SIZE) { alignment = PAGE_SIZE; } - r = radeon_bo_create(rdev, size, alignment, kernel, initial_domain, &robj); + r = radeon_bo_create(rdev, size, alignment, kernel, initial_domain, NULL, &robj); if (r) { if (r != -ERESTARTSYS) DRM_ERROR("Failed to allocate GEM object (%d, %d, %u, %d)\n", @@ -91,7 +93,7 @@ int radeon_gem_set_domain(struct drm_gem_object *gobj, } if (!domain) { /* Do nothings */ - printk(KERN_WARNING "Set domain withou domain !\n"); + printk(KERN_WARNING "Set domain without domain !\n"); return 0; } if (domain == RADEON_GEM_DOMAIN_CPU) { diff --git a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c index 42db254f6bb..a0c82229e8f 100644 --- a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c +++ b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c @@ -369,6 +369,7 @@ void radeon_legacy_backlight_init(struct radeon_encoder *radeon_encoder, goto error; } + memset(&props, 0, sizeof(props)); props.max_brightness = MAX_RADEON_LEVEL; props.type = BACKLIGHT_RAW; bd = backlight_device_register("radeon_bl", &drm_connector->kdev, diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index df6a4dbd93f..830f1a7b486 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -104,7 +104,7 @@ void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain) int radeon_bo_create(struct radeon_device *rdev, unsigned long size, int byte_align, bool kernel, u32 domain, - struct radeon_bo **bo_ptr) + struct sg_table *sg, struct radeon_bo **bo_ptr) { struct radeon_bo *bo; enum ttm_bo_type type; @@ -120,6 +120,8 @@ int radeon_bo_create(struct radeon_device *rdev, } if (kernel) { type = ttm_bo_type_kernel; + } else if (sg) { + type = ttm_bo_type_sg; } else { type = ttm_bo_type_device; } @@ -155,7 +157,7 @@ retry: mutex_lock(&rdev->vram_mutex); r = ttm_bo_init(&rdev->mman.bdev, &bo->tbo, size, type, &bo->placement, page_align, 0, !kernel, NULL, - acc_size, &radeon_ttm_bo_destroy); + acc_size, sg, &radeon_ttm_bo_destroy); mutex_unlock(&rdev->vram_mutex); if (unlikely(r != 0)) { if (r != -ERESTARTSYS) { diff --git a/drivers/gpu/drm/radeon/radeon_object.h b/drivers/gpu/drm/radeon/radeon_object.h index befec7d1213..17fb99f177c 100644 --- a/drivers/gpu/drm/radeon/radeon_object.h +++ b/drivers/gpu/drm/radeon/radeon_object.h @@ -111,9 +111,10 @@ extern int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type, bool no_wait); extern int radeon_bo_create(struct radeon_device *rdev, - unsigned long size, int byte_align, - bool kernel, u32 domain, - struct radeon_bo **bo_ptr); + unsigned long size, int byte_align, + bool kernel, u32 domain, + struct sg_table *sg, + 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); diff --git a/drivers/gpu/drm/radeon/radeon_prime.c b/drivers/gpu/drm/radeon/radeon_prime.c new file mode 100644 index 00000000000..b8f835d8ecb --- /dev/null +++ b/drivers/gpu/drm/radeon/radeon_prime.c @@ -0,0 +1,176 @@ +/* + * Copyright 2012 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * based on nouveau_prime.c + * + * Authors: Alex Deucher + */ +#include "drmP.h" +#include "drm.h" + +#include "radeon.h" +#include "radeon_drm.h" + +#include <linux/dma-buf.h> + +static struct sg_table *radeon_gem_map_dma_buf(struct dma_buf_attachment *attachment, + enum dma_data_direction dir) +{ + struct radeon_bo *bo = attachment->dmabuf->priv; + struct drm_device *dev = bo->rdev->ddev; + int npages = bo->tbo.num_pages; + struct sg_table *sg; + int nents; + + mutex_lock(&dev->struct_mutex); + sg = drm_prime_pages_to_sg(bo->tbo.ttm->pages, npages); + nents = dma_map_sg(attachment->dev, sg->sgl, sg->nents, dir); + mutex_unlock(&dev->struct_mutex); + return sg; +} + +static void radeon_gem_unmap_dma_buf(struct dma_buf_attachment *attachment, + struct sg_table *sg, enum dma_data_direction dir) +{ + dma_unmap_sg(attachment->dev, sg->sgl, sg->nents, dir); + sg_free_table(sg); + kfree(sg); +} + +static void radeon_gem_dmabuf_release(struct dma_buf *dma_buf) +{ + struct radeon_bo *bo = dma_buf->priv; + + if (bo->gem_base.export_dma_buf == dma_buf) { + DRM_ERROR("unreference dmabuf %p\n", &bo->gem_base); + bo->gem_base.export_dma_buf = NULL; + drm_gem_object_unreference_unlocked(&bo->gem_base); + } +} + +static void *radeon_gem_kmap_atomic(struct dma_buf *dma_buf, unsigned long page_num) +{ + return NULL; +} + +static void radeon_gem_kunmap_atomic(struct dma_buf *dma_buf, unsigned long page_num, void *addr) +{ + +} +static void *radeon_gem_kmap(struct dma_buf *dma_buf, unsigned long page_num) +{ + return NULL; +} + +static void radeon_gem_kunmap(struct dma_buf *dma_buf, unsigned long page_num, void *addr) +{ + +} + +const static struct dma_buf_ops radeon_dmabuf_ops = { + .map_dma_buf = radeon_gem_map_dma_buf, + .unmap_dma_buf = radeon_gem_unmap_dma_buf, + .release = radeon_gem_dmabuf_release, + .kmap = radeon_gem_kmap, + .kmap_atomic = radeon_gem_kmap_atomic, + .kunmap = radeon_gem_kunmap, + .kunmap_atomic = radeon_gem_kunmap_atomic, +}; + +static int radeon_prime_create(struct drm_device *dev, + size_t size, + struct sg_table *sg, + struct radeon_bo **pbo) +{ + struct radeon_device *rdev = dev->dev_private; + struct radeon_bo *bo; + int ret; + + ret = radeon_bo_create(rdev, size, PAGE_SIZE, false, + RADEON_GEM_DOMAIN_GTT, sg, pbo); + if (ret) + return ret; + bo = *pbo; + bo->gem_base.driver_private = bo; + + mutex_lock(&rdev->gem.mutex); + list_add_tail(&bo->list, &rdev->gem.objects); + mutex_unlock(&rdev->gem.mutex); + + return 0; +} + +struct dma_buf *radeon_gem_prime_export(struct drm_device *dev, + struct drm_gem_object *obj, + int flags) +{ + struct radeon_bo *bo = gem_to_radeon_bo(obj); + int ret = 0; + + /* pin buffer into GTT */ + ret = radeon_bo_pin(bo, RADEON_GEM_DOMAIN_GTT, NULL); + if (ret) + return ERR_PTR(ret); + + return dma_buf_export(bo, &radeon_dmabuf_ops, obj->size, flags); +} + +struct drm_gem_object *radeon_gem_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf) +{ + struct dma_buf_attachment *attach; + struct sg_table *sg; + struct radeon_bo *bo; + int ret; + + if (dma_buf->ops == &radeon_dmabuf_ops) { + bo = dma_buf->priv; + if (bo->gem_base.dev == dev) { + drm_gem_object_reference(&bo->gem_base); + return &bo->gem_base; + } + } + + /* need to attach */ + attach = dma_buf_attach(dma_buf, dev->dev); + if (IS_ERR(attach)) + return ERR_CAST(attach); + + sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); + if (IS_ERR(sg)) { + ret = PTR_ERR(sg); + goto fail_detach; + } + + ret = radeon_prime_create(dev, dma_buf->size, sg, &bo); + if (ret) + goto fail_unmap; + + bo->gem_base.import_attach = attach; + + return &bo->gem_base; + +fail_unmap: + dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL); +fail_detach: + dma_buf_detach(dma_buf, attach); + return ERR_PTR(ret); +} diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c index a5dee76f4eb..983658c9135 100644 --- a/drivers/gpu/drm/radeon/radeon_ring.c +++ b/drivers/gpu/drm/radeon/radeon_ring.c @@ -39,31 +39,6 @@ */ int radeon_debugfs_sa_init(struct radeon_device *rdev); -u32 radeon_get_ib_value(struct radeon_cs_parser *p, int idx) -{ - struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx]; - u32 pg_idx, pg_offset; - u32 idx_value = 0; - int new_page; - - pg_idx = (idx * 4) / PAGE_SIZE; - pg_offset = (idx * 4) % PAGE_SIZE; - - if (ibc->kpage_idx[0] == pg_idx) - return ibc->kpage[0][pg_offset/4]; - if (ibc->kpage_idx[1] == pg_idx) - return ibc->kpage[1][pg_offset/4]; - - new_page = radeon_cs_update_pages(p, pg_idx); - if (new_page < 0) { - p->parser_error = new_page; - return 0; - } - - idx_value = ibc->kpage[new_page][pg_offset/4]; - return idx_value; -} - int radeon_ib_get(struct radeon_device *rdev, int ring, struct radeon_ib *ib, unsigned size) { @@ -390,8 +365,8 @@ int radeon_ring_init(struct radeon_device *rdev, struct radeon_ring *ring, unsig /* Allocate ring buffer */ if (ring->ring_obj == NULL) { r = radeon_bo_create(rdev, ring->ring_size, PAGE_SIZE, true, - RADEON_GEM_DOMAIN_GTT, - &ring->ring_obj); + RADEON_GEM_DOMAIN_GTT, + NULL, &ring->ring_obj); if (r) { dev_err(rdev->dev, "(%d) ring create failed\n", r); return r; diff --git a/drivers/gpu/drm/radeon/radeon_sa.c b/drivers/gpu/drm/radeon/radeon_sa.c index c3ac7f4c7b7..32059b74572 100644 --- a/drivers/gpu/drm/radeon/radeon_sa.c +++ b/drivers/gpu/drm/radeon/radeon_sa.c @@ -65,7 +65,7 @@ int radeon_sa_bo_manager_init(struct radeon_device *rdev, } r = radeon_bo_create(rdev, size, RADEON_GPU_PAGE_SIZE, true, - RADEON_GEM_DOMAIN_CPU, &sa_manager->bo); + RADEON_GEM_DOMAIN_CPU, NULL, &sa_manager->bo); if (r) { dev_err(rdev->dev, "(%d) failed to allocate bo for manager\n", r); return r; diff --git a/drivers/gpu/drm/radeon/radeon_test.c b/drivers/gpu/drm/radeon/radeon_test.c index b0573876279..efff929ea49 100644 --- a/drivers/gpu/drm/radeon/radeon_test.c +++ b/drivers/gpu/drm/radeon/radeon_test.c @@ -59,7 +59,7 @@ void radeon_test_moves(struct radeon_device *rdev) } r = radeon_bo_create(rdev, size, PAGE_SIZE, true, RADEON_GEM_DOMAIN_VRAM, - &vram_obj); + NULL, &vram_obj); if (r) { DRM_ERROR("Failed to create VRAM object\n"); goto out_cleanup; @@ -78,7 +78,7 @@ void radeon_test_moves(struct radeon_device *rdev) void **vram_start, **vram_end; r = radeon_bo_create(rdev, size, PAGE_SIZE, true, - RADEON_GEM_DOMAIN_GTT, gtt_obj + i); + RADEON_GEM_DOMAIN_GTT, NULL, gtt_obj + i); if (r) { DRM_ERROR("Failed to create GTT object %d\n", i); goto out_cleanup; diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index 0f6aee8aa15..c94a2257761 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -611,10 +611,18 @@ static int radeon_ttm_tt_populate(struct ttm_tt *ttm) struct radeon_ttm_tt *gtt = (void *)ttm; unsigned i; int r; + bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG); if (ttm->state != tt_unpopulated) return 0; + if (slave && ttm->sg) { + drm_prime_sg_to_page_addr_arrays(ttm->sg, ttm->pages, + gtt->ttm.dma_address, ttm->num_pages); + ttm->state = tt_unbound; + return 0; + } + rdev = radeon_get_rdev(ttm->bdev); #if __OS_HAS_AGP if (rdev->flags & RADEON_IS_AGP) { @@ -655,6 +663,10 @@ static void radeon_ttm_tt_unpopulate(struct ttm_tt *ttm) struct radeon_device *rdev; struct radeon_ttm_tt *gtt = (void *)ttm; unsigned i; + bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG); + + if (slave) + return; rdev = radeon_get_rdev(ttm->bdev); #if __OS_HAS_AGP @@ -726,8 +738,8 @@ int radeon_ttm_init(struct radeon_device *rdev) return r; } r = radeon_bo_create(rdev, 256 * 1024, PAGE_SIZE, true, - RADEON_GEM_DOMAIN_VRAM, - &rdev->stollen_vga_memory); + RADEON_GEM_DOMAIN_VRAM, + NULL, &rdev->stollen_vga_memory); if (r) { return r; } diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index 63ae41dac5c..549732e56ca 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -2976,7 +2976,8 @@ int si_rlc_init(struct radeon_device *rdev) /* save restore block */ if (rdev->rlc.save_restore_obj == NULL) { r = radeon_bo_create(rdev, RADEON_GPU_PAGE_SIZE, PAGE_SIZE, true, - RADEON_GEM_DOMAIN_VRAM, &rdev->rlc.save_restore_obj); + RADEON_GEM_DOMAIN_VRAM, NULL, + &rdev->rlc.save_restore_obj); if (r) { dev_warn(rdev->dev, "(%d) create RLC sr bo failed\n", r); return r; @@ -3000,7 +3001,8 @@ int si_rlc_init(struct radeon_device *rdev) /* clear state block */ if (rdev->rlc.clear_state_obj == NULL) { r = radeon_bo_create(rdev, RADEON_GPU_PAGE_SIZE, PAGE_SIZE, true, - RADEON_GEM_DOMAIN_VRAM, &rdev->rlc.clear_state_obj); + RADEON_GEM_DOMAIN_VRAM, NULL, + &rdev->rlc.clear_state_obj); if (r) { dev_warn(rdev->dev, "(%d) create RLC c bo failed\n", r); si_rlc_fini(rdev); diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 1f5c67c579c..36792bd4da7 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -343,6 +343,16 @@ static int ttm_bo_add_ttm(struct ttm_buffer_object *bo, bool zero_alloc) if (unlikely(bo->ttm == NULL)) ret = -ENOMEM; break; + case ttm_bo_type_sg: + bo->ttm = bdev->driver->ttm_tt_create(bdev, bo->num_pages << PAGE_SHIFT, + page_flags | TTM_PAGE_FLAG_SG, + glob->dummy_read_page); + if (unlikely(bo->ttm == NULL)) { + ret = -ENOMEM; + break; + } + bo->ttm->sg = bo->sg; + break; default: pr_err("Illegal buffer object type\n"); ret = -EINVAL; @@ -1169,6 +1179,7 @@ int ttm_bo_init(struct ttm_bo_device *bdev, bool interruptible, struct file *persistent_swap_storage, size_t acc_size, + struct sg_table *sg, void (*destroy) (struct ttm_buffer_object *)) { int ret = 0; @@ -1223,6 +1234,7 @@ int ttm_bo_init(struct ttm_bo_device *bdev, bo->seq_valid = false; bo->persistent_swap_storage = persistent_swap_storage; bo->acc_size = acc_size; + bo->sg = sg; atomic_inc(&bo->glob->bo_count); ret = ttm_bo_check_placement(bo, placement); @@ -1233,7 +1245,8 @@ int ttm_bo_init(struct ttm_bo_device *bdev, * For ttm_bo_type_device buffers, allocate * address space from the device. */ - if (bo->type == ttm_bo_type_device) { + if (bo->type == ttm_bo_type_device || + bo->type == ttm_bo_type_sg) { ret = ttm_bo_setup_vm(bo); if (ret) goto out_err; @@ -1312,7 +1325,7 @@ int ttm_bo_create(struct ttm_bo_device *bdev, ret = ttm_bo_init(bdev, bo, size, type, placement, page_alignment, buffer_start, interruptible, - persistent_swap_storage, acc_size, NULL); + persistent_swap_storage, acc_size, NULL, NULL); if (likely(ret == 0)) *p_bo = bo; diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c index 53673907a6a..4d02c46a942 100644 --- a/drivers/gpu/drm/udl/udl_drv.c +++ b/drivers/gpu/drm/udl/udl_drv.c @@ -38,7 +38,7 @@ static void udl_usb_disconnect(struct usb_interface *interface) drm_unplug_dev(dev); } -static struct vm_operations_struct udl_gem_vm_ops = { +static const struct vm_operations_struct udl_gem_vm_ops = { .fault = udl_gem_fault, .open = drm_gem_vm_open, .close = drm_gem_vm_close, @@ -57,7 +57,7 @@ static const struct file_operations udl_driver_fops = { }; static struct drm_driver driver = { - .driver_features = DRIVER_MODESET | DRIVER_GEM, + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, .load = udl_driver_load, .unload = udl_driver_unload, @@ -70,6 +70,10 @@ static struct drm_driver driver = { .dumb_map_offset = udl_gem_mmap, .dumb_destroy = udl_dumb_destroy, .fops = &udl_driver_fops, + + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_import = udl_gem_prime_import, + .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = DRIVER_DATE, diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h index 96820d03a30..fccd361f7b5 100644 --- a/drivers/gpu/drm/udl/udl_drv.h +++ b/drivers/gpu/drm/udl/udl_drv.h @@ -66,6 +66,7 @@ struct udl_gem_object { struct drm_gem_object base; struct page **pages; void *vmapping; + struct sg_table *sg; }; #define to_udl_bo(x) container_of(x, struct udl_gem_object, base) @@ -118,6 +119,8 @@ int udl_gem_init_object(struct drm_gem_object *obj); void udl_gem_free_object(struct drm_gem_object *gem_obj); struct udl_gem_object *udl_gem_alloc_object(struct drm_device *dev, size_t size); +struct drm_gem_object *udl_gem_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf); int udl_gem_vmap(struct udl_gem_object *obj); void udl_gem_vunmap(struct udl_gem_object *obj); diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index 4d9c3a5d8a4..a029ee39b0c 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -593,11 +593,20 @@ udl_fb_user_fb_create(struct drm_device *dev, struct drm_gem_object *obj; struct udl_framebuffer *ufb; int ret; + uint32_t size; obj = drm_gem_object_lookup(dev, file, mode_cmd->handles[0]); if (obj == NULL) return ERR_PTR(-ENOENT); + size = mode_cmd->pitches[0] * mode_cmd->height; + size = ALIGN(size, PAGE_SIZE); + + if (size > obj->size) { + DRM_ERROR("object size not sufficient for fb %d %zu %d %d\n", size, obj->size, mode_cmd->pitches[0], mode_cmd->height); + return ERR_PTR(-ENOMEM); + } + ufb = kzalloc(sizeof(*ufb), GFP_KERNEL); if (ufb == NULL) return ERR_PTR(-ENOMEM); diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c index 92f19ef329b..d9609e2a4d8 100644 --- a/drivers/gpu/drm/udl/udl_gem.c +++ b/drivers/gpu/drm/udl/udl_gem.c @@ -9,6 +9,7 @@ #include "drmP.h" #include "udl_drv.h" #include <linux/shmem_fs.h> +#include <linux/dma-buf.h> struct udl_gem_object *udl_gem_alloc_object(struct drm_device *dev, size_t size) @@ -161,6 +162,12 @@ static void udl_gem_put_pages(struct udl_gem_object *obj) int page_count = obj->base.size / PAGE_SIZE; int i; + if (obj->base.import_attach) { + drm_free_large(obj->pages); + obj->pages = NULL; + return; + } + for (i = 0; i < page_count; i++) page_cache_release(obj->pages[i]); @@ -195,6 +202,9 @@ void udl_gem_free_object(struct drm_gem_object *gem_obj) { struct udl_gem_object *obj = to_udl_bo(gem_obj); + if (gem_obj->import_attach) + drm_prime_gem_destroy(gem_obj, obj->sg); + if (obj->vmapping) udl_gem_vunmap(obj); @@ -224,7 +234,7 @@ int udl_gem_mmap(struct drm_file *file, struct drm_device *dev, ret = udl_gem_get_pages(gobj, GFP_KERNEL); if (ret) - return ret; + goto out; if (!gobj->base.map_list.map) { ret = drm_gem_create_mmap_offset(obj); if (ret) @@ -239,3 +249,68 @@ unlock: mutex_unlock(&dev->struct_mutex); return ret; } + +static int udl_prime_create(struct drm_device *dev, + size_t size, + struct sg_table *sg, + struct udl_gem_object **obj_p) +{ + struct udl_gem_object *obj; + int npages; + int i; + struct scatterlist *iter; + + npages = size / PAGE_SIZE; + + *obj_p = NULL; + obj = udl_gem_alloc_object(dev, npages * PAGE_SIZE); + if (!obj) + return -ENOMEM; + + obj->sg = sg; + obj->pages = drm_malloc_ab(npages, sizeof(struct page *)); + if (obj->pages == NULL) { + DRM_ERROR("obj pages is NULL %d\n", npages); + return -ENOMEM; + } + + drm_prime_sg_to_page_addr_arrays(sg, obj->pages, NULL, npages); + + *obj_p = obj; + return 0; +} + +struct drm_gem_object *udl_gem_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf) +{ + struct dma_buf_attachment *attach; + struct sg_table *sg; + struct udl_gem_object *uobj; + int ret; + + /* need to attach */ + attach = dma_buf_attach(dma_buf, dev->dev); + if (IS_ERR(attach)) + return ERR_PTR(PTR_ERR(attach)); + + sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); + if (IS_ERR(sg)) { + ret = PTR_ERR(sg); + goto fail_detach; + } + + ret = udl_prime_create(dev, dma_buf->size, sg, &uobj); + if (ret) { + goto fail_unmap; + } + + uobj->base.import_attach = attach; + + return &uobj->base; + +fail_unmap: + dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL); +fail_detach: + dma_buf_detach(dma_buf, attach); + return ERR_PTR(ret); +} diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c index b3ecb3d12a1..0d7816789da 100644 --- a/drivers/gpu/drm/udl/udl_modeset.c +++ b/drivers/gpu/drm/udl/udl_modeset.c @@ -395,7 +395,7 @@ int udl_modeset_init(struct drm_device *dev) dev->mode_config.prefer_shadow = 0; dev->mode_config.preferred_depth = 24; - dev->mode_config.funcs = (void *)&udl_mode_funcs; + dev->mode_config.funcs = &udl_mode_funcs; drm_mode_create_dirty_info_property(dev); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 2286d47e502..6b0078ffa76 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -1178,7 +1178,7 @@ err_out: return &vfb->base; } -static struct drm_mode_config_funcs vmw_kms_funcs = { +static const struct drm_mode_config_funcs vmw_kms_funcs = { .fb_create = vmw_kms_fb_create, }; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index a37abb581cb..22bf9a21ec7 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -1567,7 +1567,7 @@ int vmw_dmabuf_init(struct vmw_private *dev_priv, ret = ttm_bo_init(bdev, &vmw_bo->base, size, ttm_bo_type_device, placement, 0, 0, interruptible, - NULL, acc_size, bo_free); + NULL, acc_size, NULL, bo_free); return ret; } |