diff options
Diffstat (limited to 'drivers/gpu/drm')
330 files changed, 10484 insertions, 4437 deletions
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 863db8415c2..dd2ba426974 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -13,7 +13,8 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \ drm_crtc.o drm_modes.o drm_edid.o \ drm_info.o drm_debugfs.o drm_encoder_slave.o \ drm_trace_points.o drm_global.o drm_prime.o \ - drm_rect.o drm_vma_manager.o drm_flip_work.o + drm_rect.o drm_vma_manager.o drm_flip_work.o \ + drm_modeset_lock.o drm-$(CONFIG_COMPAT) += drm_ioc32.o drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index 567cfbde088..8ab3cd1a8cd 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -402,7 +402,7 @@ static struct platform_driver armada_drm_platform_driver = { static int __init armada_drm_init(void) { - armada_drm_driver.num_ioctls = DRM_ARRAY_SIZE(armada_ioctls); + armada_drm_driver.num_ioctls = ARRAY_SIZE(armada_ioctls); return platform_driver_register(&armada_drm_platform_driver); } module_init(armada_drm_init); diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c index 948cb14c561..fd166f532ab 100644 --- a/drivers/gpu/drm/armada/armada_fbdev.c +++ b/drivers/gpu/drm/armada/armada_fbdev.c @@ -181,10 +181,8 @@ void armada_fbdev_lastclose(struct drm_device *dev) { struct armada_private *priv = dev->dev_private; - drm_modeset_lock_all(dev); if (priv->fbdev) - drm_fb_helper_restore_fbdev_mode(priv->fbdev); - drm_modeset_unlock_all(dev); + drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev); } void armada_fbdev_fini(struct drm_device *dev) diff --git a/drivers/gpu/drm/ast/ast_post.c b/drivers/gpu/drm/ast/ast_post.c index 4e5ea3898e7..38d437f3a26 100644 --- a/drivers/gpu/drm/ast/ast_post.c +++ b/drivers/gpu/drm/ast/ast_post.c @@ -1083,7 +1083,7 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa case AST_DRAM_4Gx16: param->dram_config = 0x133; break; - }; /* switch size */ + } /* switch size */ switch (param->vram_size) { default: @@ -1454,7 +1454,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa case AST_DRAM_4Gx16: param->dram_config = 0x123; break; - }; /* switch size */ + } /* switch size */ switch (param->vram_size) { default: diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index f3b98d4b6f4..fe94cc10cd3 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -37,6 +37,7 @@ #include <drm/drm_crtc.h> #include <drm/drm_edid.h> #include <drm/drm_fourcc.h> +#include <drm/drm_modeset_lock.h> #include "drm_crtc_internal.h" @@ -50,14 +51,42 @@ */ void drm_modeset_lock_all(struct drm_device *dev) { - struct drm_crtc *crtc; + struct drm_mode_config *config = &dev->mode_config; + struct drm_modeset_acquire_ctx *ctx; + int ret; - mutex_lock(&dev->mode_config.mutex); + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (WARN_ON(!ctx)) + return; - mutex_lock(&dev->mode_config.connection_mutex); + mutex_lock(&config->mutex); - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) - mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex); + drm_modeset_acquire_init(ctx, 0); + +retry: + ret = drm_modeset_lock(&config->connection_mutex, ctx); + if (ret) + goto fail; + ret = drm_modeset_lock_all_crtcs(dev, ctx); + if (ret) + goto fail; + + WARN_ON(config->acquire_ctx); + + /* now we hold the locks, so now that it is safe, stash the + * ctx for drm_modeset_unlock_all(): + */ + config->acquire_ctx = ctx; + + drm_warn_on_modeset_not_all_locked(dev); + + return; + +fail: + if (ret == -EDEADLK) { + drm_modeset_backoff(ctx); + goto retry; + } } EXPORT_SYMBOL(drm_modeset_lock_all); @@ -69,12 +98,17 @@ EXPORT_SYMBOL(drm_modeset_lock_all); */ void drm_modeset_unlock_all(struct drm_device *dev) { - struct drm_crtc *crtc; + struct drm_mode_config *config = &dev->mode_config; + struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx; - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) - mutex_unlock(&crtc->mutex); + if (WARN_ON(!ctx)) + return; + + config->acquire_ctx = NULL; + drm_modeset_drop_locks(ctx); + drm_modeset_acquire_fini(ctx); - mutex_unlock(&dev->mode_config.connection_mutex); + kfree(ctx); mutex_unlock(&dev->mode_config.mutex); } @@ -95,9 +129,9 @@ void drm_warn_on_modeset_not_all_locked(struct drm_device *dev) return; list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) - WARN_ON(!mutex_is_locked(&crtc->mutex)); + WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); - WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex)); + WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); } EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked); @@ -671,6 +705,8 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) } EXPORT_SYMBOL(drm_framebuffer_remove); +DEFINE_WW_CLASS(crtc_ww_class); + /** * drm_crtc_init_with_planes - Initialise a new CRTC object with * specified primary and cursor planes. @@ -690,6 +726,7 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, void *cursor, const struct drm_crtc_funcs *funcs) { + struct drm_mode_config *config = &dev->mode_config; int ret; crtc->dev = dev; @@ -697,8 +734,9 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, crtc->invert_dimensions = false; drm_modeset_lock_all(dev); - mutex_init(&crtc->mutex); - mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex); + drm_modeset_lock_init(&crtc->mutex); + /* dropped by _unlock_all(): */ + drm_modeset_lock(&crtc->mutex, config->acquire_ctx); ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); if (ret) @@ -706,8 +744,8 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, crtc->base.properties = &crtc->properties; - list_add_tail(&crtc->head, &dev->mode_config.crtc_list); - dev->mode_config.num_crtc++; + list_add_tail(&crtc->head, &config->crtc_list); + config->num_crtc++; crtc->primary = primary; if (primary) @@ -735,6 +773,8 @@ void drm_crtc_cleanup(struct drm_crtc *crtc) kfree(crtc->gamma_store); crtc->gamma_store = NULL; + drm_modeset_lock_fini(&crtc->mutex); + drm_mode_object_put(dev, &crtc->base); list_del(&crtc->head); dev->mode_config.num_crtc--; @@ -1798,7 +1838,6 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id); mutex_lock(&dev->mode_config.mutex); - mutex_lock(&dev->mode_config.connection_mutex); connector = drm_connector_find(dev, out_resp->connector_id); if (!connector) { @@ -1832,10 +1871,12 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, out_resp->mm_height = connector->display_info.height_mm; out_resp->subpixel = connector->display_info.subpixel_order; out_resp->connection = connector->status; + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); if (connector->encoder) out_resp->encoder_id = connector->encoder->base.id; else out_resp->encoder_id = 0; + drm_modeset_unlock(&dev->mode_config.connection_mutex); /* * This ioctl is called twice, once to determine how much space is @@ -1897,7 +1938,6 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, out_resp->count_encoders = encoders_count; out: - mutex_unlock(&dev->mode_config.connection_mutex); mutex_unlock(&dev->mode_config.mutex); return ret; @@ -2138,6 +2178,13 @@ int drm_mode_setplane(struct drm_device *dev, void *data, goto out; } + /* Check whether this plane is usable on this CRTC */ + if (!(plane->possible_crtcs & drm_crtc_mask(crtc))) { + DRM_DEBUG_KMS("Invalid crtc for plane\n"); + ret = -EINVAL; + goto out; + } + fb = drm_framebuffer_lookup(dev, plane_req->fb_id); if (!fb) { DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", @@ -2481,7 +2528,7 @@ static int drm_mode_cursor_common(struct drm_device *dev, return -ENOENT; } - mutex_lock(&crtc->mutex); + drm_modeset_lock(&crtc->mutex, NULL); if (req->flags & DRM_MODE_CURSOR_BO) { if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) { ret = -ENXIO; @@ -2505,7 +2552,7 @@ static int drm_mode_cursor_common(struct drm_device *dev, } } out: - mutex_unlock(&crtc->mutex); + drm_modeset_unlock(&crtc->mutex); return ret; @@ -4198,7 +4245,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, if (!crtc) return -ENOENT; - mutex_lock(&crtc->mutex); + drm_modeset_lock(&crtc->mutex, NULL); if (crtc->primary->fb == NULL) { /* The framebuffer is currently unbound, presumably * due to a hotplug event, that userspace has not @@ -4282,7 +4329,7 @@ out: drm_framebuffer_unreference(fb); if (old_fb) drm_framebuffer_unreference(old_fb); - mutex_unlock(&crtc->mutex); + drm_modeset_unlock(&crtc->mutex); return ret; } @@ -4647,7 +4694,7 @@ EXPORT_SYMBOL(drm_format_vert_chroma_subsampling); void drm_mode_config_init(struct drm_device *dev) { mutex_init(&dev->mode_config.mutex); - mutex_init(&dev->mode_config.connection_mutex); + drm_modeset_lock_init(&dev->mode_config.connection_mutex); mutex_init(&dev->mode_config.idr_mutex); mutex_init(&dev->mode_config.fb_lock); INIT_LIST_HEAD(&dev->mode_config.fb_list); @@ -4747,5 +4794,6 @@ void drm_mode_config_cleanup(struct drm_device *dev) } idr_destroy(&dev->mode_config.crtc_idr); + drm_modeset_lock_fini(&dev->mode_config.connection_mutex); } EXPORT_SYMBOL(drm_mode_config_cleanup); diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index b55d27c872f..78b37f3febd 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -29,6 +29,7 @@ * Jesse Barnes <jesse.barnes@intel.com> */ +#include <linux/kernel.h> #include <linux/export.h> #include <linux/moduleparam.h> @@ -88,8 +89,14 @@ bool drm_helper_encoder_in_use(struct drm_encoder *encoder) struct drm_connector *connector; struct drm_device *dev = encoder->dev; - WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); - WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex)); + /* + * We can expect this mutex to be locked if we are not panicking. + * Locking is currently fubar in the panic handler. + */ + if (!oops_in_progress) { + WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); + WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); + } list_for_each_entry(connector, &dev->mode_config.connector_list, head) if (connector->encoder == encoder) @@ -114,7 +121,13 @@ bool drm_helper_crtc_in_use(struct drm_crtc *crtc) struct drm_encoder *encoder; struct drm_device *dev = crtc->dev; - WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); + /* + * We can expect this mutex to be locked if we are not panicking. + * Locking is currently fubar in the panic handler. + */ + if (!oops_in_progress) + WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) if (encoder->crtc == crtc && drm_helper_encoder_in_use(encoder)) return true; diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 494219ccdf9..08e33b8b13a 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -216,7 +216,7 @@ i2c_dp_aux_prepare_bus(struct i2c_adapter *adapter) * * IMPORTANT: * This interface is deprecated, please switch to the new dp aux helpers and - * drm_dp_aux_register_i2c_bus(). + * drm_dp_aux_register(). */ int i2c_dp_aux_add_bus(struct i2c_adapter *adapter) @@ -382,7 +382,10 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request, * transactions. */ for (retry = 0; retry < 7; retry++) { + + mutex_lock(&aux->hw_mutex); err = aux->transfer(aux, &msg); + mutex_unlock(&aux->hw_mutex); if (err < 0) { if (err == -EBUSY) continue; @@ -596,7 +599,9 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) * before giving up the AUX transaction. */ for (retry = 0; retry < 7; retry++) { + mutex_lock(&aux->hw_mutex); err = aux->transfer(aux, msg); + mutex_unlock(&aux->hw_mutex); if (err < 0) { if (err == -EBUSY) continue; @@ -729,13 +734,15 @@ static const struct i2c_algorithm drm_dp_i2c_algo = { }; /** - * drm_dp_aux_register_i2c_bus() - register an I2C adapter for I2C-over-AUX + * drm_dp_aux_register() - initialise and register aux channel * @aux: DisplayPort AUX channel * * Returns 0 on success or a negative error code on failure. */ -int drm_dp_aux_register_i2c_bus(struct drm_dp_aux *aux) +int drm_dp_aux_register(struct drm_dp_aux *aux) { + mutex_init(&aux->hw_mutex); + aux->ddc.algo = &drm_dp_i2c_algo; aux->ddc.algo_data = aux; aux->ddc.retries = 3; @@ -750,14 +757,14 @@ int drm_dp_aux_register_i2c_bus(struct drm_dp_aux *aux) return i2c_add_adapter(&aux->ddc); } -EXPORT_SYMBOL(drm_dp_aux_register_i2c_bus); +EXPORT_SYMBOL(drm_dp_aux_register); /** - * drm_dp_aux_unregister_i2c_bus() - unregister an I2C-over-AUX adapter + * drm_dp_aux_unregister() - unregister an AUX adapter * @aux: DisplayPort AUX channel */ -void drm_dp_aux_unregister_i2c_bus(struct drm_dp_aux *aux) +void drm_dp_aux_unregister(struct drm_dp_aux *aux) { i2c_del_adapter(&aux->ddc); } -EXPORT_SYMBOL(drm_dp_aux_unregister_i2c_bus); +EXPORT_SYMBOL(drm_dp_aux_unregister); diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 03711d00aaa..8218078b613 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -419,8 +419,9 @@ long drm_ioctl(struct file *filp, retcode = -EFAULT; goto err_i1; } - } else + } else if (cmd & IOC_OUT) { memset(kdata, 0, usize); + } if (ioctl->flags & DRM_UNLOCKED) retcode = func(dev, kdata, file_priv); diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 7be21781d3b..dfa9769b26b 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -3471,18 +3471,21 @@ static bool drm_assign_hdmi_deep_color_info(struct edid *edid, if (hdmi[6] & DRM_EDID_HDMI_DC_30) { dc_bpc = 10; + info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_30; DRM_DEBUG("%s: HDMI sink does deep color 30.\n", connector->name); } if (hdmi[6] & DRM_EDID_HDMI_DC_36) { dc_bpc = 12; + info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_36; DRM_DEBUG("%s: HDMI sink does deep color 36.\n", connector->name); } if (hdmi[6] & DRM_EDID_HDMI_DC_48) { dc_bpc = 16; + info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_48; DRM_DEBUG("%s: HDMI sink does deep color 48.\n", connector->name); } diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index 61b5a47ad23..f27c883be39 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -429,13 +429,8 @@ EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini); */ void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma) { - if (fbdev_cma) { - struct drm_device *dev = fbdev_cma->fb_helper.dev; - - drm_modeset_lock_all(dev); - drm_fb_helper_restore_fbdev_mode(&fbdev_cma->fb_helper); - drm_modeset_unlock_all(dev); - } + if (fbdev_cma) + drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev_cma->fb_helper); } EXPORT_SYMBOL_GPL(drm_fbdev_cma_restore_mode); diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 1ddc1746425..d5d8cea1a67 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -273,15 +273,7 @@ int drm_fb_helper_debug_leave(struct fb_info *info) } EXPORT_SYMBOL(drm_fb_helper_debug_leave); -/** - * drm_fb_helper_restore_fbdev_mode - restore fbdev configuration - * @fb_helper: fbcon to restore - * - * This should be called from driver's drm ->lastclose callback - * when implementing an fbcon on top of kms using this helper. This ensures that - * the user isn't greeted with a black screen when e.g. X dies. - */ -bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) +static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper) { struct drm_device *dev = fb_helper->dev; struct drm_plane *plane; @@ -311,7 +303,40 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) } return error; } -EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode); +/** + * drm_fb_helper_restore_fbdev_mode - restore fbdev configuration + * @fb_helper: fbcon to restore + * + * This should be called from driver's drm ->lastclose callback + * when implementing an fbcon on top of kms using this helper. This ensures that + * the user isn't greeted with a black screen when e.g. X dies. + * + * Use this variant if you need to bypass locking (panic), or already + * hold all modeset locks. Otherwise use drm_fb_helper_restore_fbdev_mode_unlocked() + */ +static bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) +{ + return restore_fbdev_mode(fb_helper); +} + +/** + * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration + * @fb_helper: fbcon to restore + * + * This should be called from driver's drm ->lastclose callback + * when implementing an fbcon on top of kms using this helper. This ensures that + * the user isn't greeted with a black screen when e.g. X dies. + */ +bool drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper) +{ + struct drm_device *dev = fb_helper->dev; + bool ret; + drm_modeset_lock_all(dev); + ret = restore_fbdev_mode(fb_helper); + drm_modeset_unlock_all(dev); + return ret; +} +EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked); /* * restore fbcon display for all kms driver's using this helper, used for sysrq @@ -331,6 +356,10 @@ static bool drm_fb_helper_force_kernel_mode(void) if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) continue; + /* NOTE: we use lockless flag below to avoid grabbing other + * modeset locks. So just trylock the underlying mutex + * directly: + */ if (!mutex_trylock(&dev->mode_config.mutex)) { error = true; continue; @@ -820,7 +849,6 @@ EXPORT_SYMBOL(drm_fb_helper_check_var); int drm_fb_helper_set_par(struct fb_info *info) { struct drm_fb_helper *fb_helper = info->par; - struct drm_device *dev = fb_helper->dev; struct fb_var_screeninfo *var = &info->var; if (var->pixclock != 0) { @@ -828,9 +856,7 @@ int drm_fb_helper_set_par(struct fb_info *info) return -EINVAL; } - drm_modeset_lock_all(dev); - drm_fb_helper_restore_fbdev_mode(fb_helper); - drm_modeset_unlock_all(dev); + drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper); if (fb_helper->delayed_hotplug) { fb_helper->delayed_hotplug = false; diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 38269d5aa33..69c61f392e6 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -131,13 +131,25 @@ static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv) if (master->unique != NULL) drm_unset_busid(dev, master); - ret = dev->driver->bus->set_busid(dev, master); - if (ret) - goto err; + if (dev->driver->bus && dev->driver->bus->set_busid) { + ret = dev->driver->bus->set_busid(dev, master); + if (ret) { + drm_unset_busid(dev, master); + return ret; + } + } else { + if (WARN(dev->unique == NULL, + "No drm_bus.set_busid() implementation provided by " + "%ps. Use drm_dev_set_unique() to set the unique " + "name explicitly.", dev->driver)) + return -EINVAL; + + master->unique = kstrdup(dev->unique, GFP_KERNEL); + if (master->unique) + master->unique_len = strlen(dev->unique); + } + return 0; -err: - drm_unset_busid(dev, master); - return ret; } /** diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index b565372a91f..0de123afdb3 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -128,7 +128,7 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc) */ if ((vblrc > 0) && (abs64(diff_ns) > 1000000)) { atomic_inc(&dev->vblank[crtc].count); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); } /* Invalidate all timestamps while vblank irq's are off. */ @@ -868,9 +868,9 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc) vblanktimestamp(dev, crtc, tslot) = t_vblank; } - smp_mb__before_atomic_inc(); + smp_mb__before_atomic(); atomic_add(diff, &dev->vblank[crtc].count); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); } /** @@ -1479,9 +1479,9 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc) /* Increment cooked vblank count. This also atomically commits * the timestamp computed above. */ - smp_mb__before_atomic_inc(); + smp_mb__before_atomic(); atomic_inc(&dev->vblank[crtc].count); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); } else { DRM_DEBUG("crtc %d: Redundant vblirq ignored. diff_ns = %d\n", crtc, (int) diff_ns); diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index 09821f46d76..e633df2f68d 100644 --- a/drivers/gpu/drm/drm_mipi_dsi.c +++ b/drivers/gpu/drm/drm_mipi_dsi.c @@ -282,6 +282,14 @@ static int mipi_dsi_drv_remove(struct device *dev) return drv->remove(dsi); } +static void mipi_dsi_drv_shutdown(struct device *dev) +{ + struct mipi_dsi_driver *drv = to_mipi_dsi_driver(dev->driver); + struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev); + + drv->shutdown(dsi); +} + /** * mipi_dsi_driver_register - register a driver for DSI devices * @drv: DSI driver structure @@ -293,6 +301,8 @@ int mipi_dsi_driver_register(struct mipi_dsi_driver *drv) drv->driver.probe = mipi_dsi_drv_probe; if (drv->remove) drv->driver.remove = mipi_dsi_drv_remove; + if (drv->shutdown) + drv->driver.shutdown = mipi_dsi_drv_shutdown; return driver_register(&drv->driver); } diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c new file mode 100644 index 00000000000..0dc57d5ecd1 --- /dev/null +++ b/drivers/gpu/drm/drm_modeset_lock.c @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2014 Red Hat + * Author: Rob Clark <robdclark@gmail.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 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. + */ + +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_modeset_lock.h> + +/** + * DOC: kms locking + * + * As KMS moves toward more fine grained locking, and atomic ioctl where + * userspace can indirectly control locking order, it becomes necessary + * to use ww_mutex and acquire-contexts to avoid deadlocks. But because + * the locking is more distributed around the driver code, we want a bit + * of extra utility/tracking out of our acquire-ctx. This is provided + * by drm_modeset_lock / drm_modeset_acquire_ctx. + * + * For basic principles of ww_mutex, see: Documentation/ww-mutex-design.txt + * + * The basic usage pattern is to: + * + * drm_modeset_acquire_init(&ctx) + * retry: + * foreach (lock in random_ordered_set_of_locks) { + * ret = drm_modeset_lock(lock, &ctx) + * if (ret == -EDEADLK) { + * drm_modeset_backoff(&ctx); + * goto retry; + * } + * } + * + * ... do stuff ... + * + * drm_modeset_drop_locks(&ctx); + * drm_modeset_acquire_fini(&ctx); + */ + + +/** + * drm_modeset_acquire_init - initialize acquire context + * @ctx: the acquire context + * @flags: for future + */ +void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx, + uint32_t flags) +{ + memset(ctx, 0, sizeof(*ctx)); + ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class); + INIT_LIST_HEAD(&ctx->locked); +} +EXPORT_SYMBOL(drm_modeset_acquire_init); + +/** + * drm_modeset_acquire_fini - cleanup acquire context + * @ctx: the acquire context + */ +void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx) +{ + ww_acquire_fini(&ctx->ww_ctx); +} +EXPORT_SYMBOL(drm_modeset_acquire_fini); + +/** + * drm_modeset_drop_locks - drop all locks + * @ctx: the acquire context + * + * Drop all locks currently held against this acquire context. + */ +void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx) +{ + WARN_ON(ctx->contended); + while (!list_empty(&ctx->locked)) { + struct drm_modeset_lock *lock; + + lock = list_first_entry(&ctx->locked, + struct drm_modeset_lock, head); + + drm_modeset_unlock(lock); + } +} +EXPORT_SYMBOL(drm_modeset_drop_locks); + +static inline int modeset_lock(struct drm_modeset_lock *lock, + struct drm_modeset_acquire_ctx *ctx, + bool interruptible, bool slow) +{ + int ret; + + WARN_ON(ctx->contended); + + if (interruptible && slow) { + ret = ww_mutex_lock_slow_interruptible(&lock->mutex, &ctx->ww_ctx); + } else if (interruptible) { + ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx); + } else if (slow) { + ww_mutex_lock_slow(&lock->mutex, &ctx->ww_ctx); + ret = 0; + } else { + ret = ww_mutex_lock(&lock->mutex, &ctx->ww_ctx); + } + if (!ret) { + WARN_ON(!list_empty(&lock->head)); + list_add(&lock->head, &ctx->locked); + } else if (ret == -EALREADY) { + /* we already hold the lock.. this is fine. For atomic + * we will need to be able to drm_modeset_lock() things + * without having to keep track of what is already locked + * or not. + */ + ret = 0; + } else if (ret == -EDEADLK) { + ctx->contended = lock; + } + + return ret; +} + +static int modeset_backoff(struct drm_modeset_acquire_ctx *ctx, + bool interruptible) +{ + struct drm_modeset_lock *contended = ctx->contended; + + ctx->contended = NULL; + + if (WARN_ON(!contended)) + return 0; + + drm_modeset_drop_locks(ctx); + + return modeset_lock(contended, ctx, interruptible, true); +} + +/** + * drm_modeset_backoff - deadlock avoidance backoff + * @ctx: the acquire context + * + * If deadlock is detected (ie. drm_modeset_lock() returns -EDEADLK), + * you must call this function to drop all currently held locks and + * block until the contended lock becomes available. + */ +void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx) +{ + modeset_backoff(ctx, false); +} +EXPORT_SYMBOL(drm_modeset_backoff); + +/** + * drm_modeset_backoff_interruptible - deadlock avoidance backoff + * @ctx: the acquire context + * + * Interruptible version of drm_modeset_backoff() + */ +int drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx) +{ + return modeset_backoff(ctx, true); +} +EXPORT_SYMBOL(drm_modeset_backoff_interruptible); + +/** + * drm_modeset_lock - take modeset lock + * @lock: lock to take + * @ctx: acquire ctx + * + * If ctx is not NULL, then its ww acquire context is used and the + * lock will be tracked by the context and can be released by calling + * drm_modeset_drop_locks(). If -EDEADLK is returned, this means a + * deadlock scenario has been detected and it is an error to attempt + * to take any more locks without first calling drm_modeset_backoff(). + */ +int drm_modeset_lock(struct drm_modeset_lock *lock, + struct drm_modeset_acquire_ctx *ctx) +{ + if (ctx) + return modeset_lock(lock, ctx, false, false); + + ww_mutex_lock(&lock->mutex, NULL); + return 0; +} +EXPORT_SYMBOL(drm_modeset_lock); + +/** + * drm_modeset_lock_interruptible - take modeset lock + * @lock: lock to take + * @ctx: acquire ctx + * + * Interruptible version of drm_modeset_lock() + */ +int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock, + struct drm_modeset_acquire_ctx *ctx) +{ + if (ctx) + return modeset_lock(lock, ctx, true, false); + + return ww_mutex_lock_interruptible(&lock->mutex, NULL); +} +EXPORT_SYMBOL(drm_modeset_lock_interruptible); + +/** + * drm_modeset_unlock - drop modeset lock + * @lock: lock to release + */ +void drm_modeset_unlock(struct drm_modeset_lock *lock) +{ + list_del_init(&lock->head); + ww_mutex_unlock(&lock->mutex); +} +EXPORT_SYMBOL(drm_modeset_unlock); + +/* Temporary.. until we have sufficiently fine grained locking, there + * are a couple scenarios where it is convenient to grab all crtc locks. + * It is planned to remove this: + */ +int drm_modeset_lock_all_crtcs(struct drm_device *dev, + struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_mode_config *config = &dev->mode_config; + struct drm_crtc *crtc; + int ret = 0; + + list_for_each_entry(crtc, &config->crtc_list, head) { + ret = drm_modeset_lock(&crtc->mutex, ctx); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL(drm_modeset_lock_all_crtcs); diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index d237de36a07..020cfd93485 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -1,17 +1,3 @@ -/* drm_pci.h -- PCI DMA memory management wrappers for DRM -*- linux-c -*- */ -/** - * \file drm_pci.c - * \brief Functions and ioctls to manage PCI memory - * - * \warning These interfaces aren't stable yet. - * - * \todo Implement the remaining ioctl's for the PCI pools. - * \todo The wrappers here are so thin that they would be better off inlined.. - * - * \author José Fonseca <jrfonseca@tungstengraphics.com> - * \author Leif Delgass <ldelgass@retinalburn.net> - */ - /* * Copyright 2003 José Fonseca. * Copyright 2003 Leif Delgass. @@ -42,12 +28,14 @@ #include <linux/export.h> #include <drm/drmP.h> -/**********************************************************************/ -/** \name PCI memory */ -/*@{*/ - /** - * \brief Allocate a PCI consistent memory block, for DMA. + * drm_pci_alloc - Allocate a PCI consistent memory block, for DMA. + * @dev: DRM device + * @size: size of block to allocate + * @align: alignment of block + * + * Return: A handle to the allocated memory block on success or NULL on + * failure. */ drm_dma_handle_t *drm_pci_alloc(struct drm_device * dev, size_t size, size_t align) { @@ -88,8 +76,8 @@ drm_dma_handle_t *drm_pci_alloc(struct drm_device * dev, size_t size, size_t ali EXPORT_SYMBOL(drm_pci_alloc); -/** - * \brief Free a PCI consistent memory block without freeing its descriptor. +/* + * Free a PCI consistent memory block without freeing its descriptor. * * This function is for internal use in the Linux-specific DRM core code. */ @@ -111,7 +99,9 @@ void __drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah) } /** - * \brief Free a PCI consistent memory block + * drm_pci_free - Free a PCI consistent memory block + * @dev: DRM device + * @dmah: handle to memory block */ void drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah) { @@ -226,17 +216,16 @@ static int drm_pci_irq_by_busid(struct drm_device *dev, struct drm_irq_busid *p) } /** - * Get interrupt from bus id. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg user argument, pointing to a drm_irq_busid structure. - * \return zero on success or a negative number on failure. + * drm_irq_by_busid - Get interrupt from bus ID + * @dev: DRM device + * @data: IOCTL parameter pointing to a drm_irq_busid structure + * @file_priv: DRM file private. * * Finds the PCI device with the specified bus id and gets its IRQ number. * This IOCTL is deprecated, and will now return EINVAL for any busid not equal * to that of the device that this DRM instance attached to. + * + * Return: 0 on success or a negative error code on failure. */ int drm_irq_by_busid(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -285,15 +274,16 @@ static struct drm_bus drm_pci_bus = { }; /** - * Register. - * - * \param pdev - PCI device structure - * \param ent entry from the PCI ID table with device type flags - * \return zero on success or a negative number on failure. + * drm_get_pci_dev - Register a PCI device with the DRM subsystem + * @pdev: PCI device + * @ent: entry from the PCI ID table that matches @pdev + * @driver: DRM device driver * * Attempt to gets inter module "drm" information. If we are first * then register the character device and inter module information. * Try and register, if we fail to register, backout previous work. + * + * Return: 0 on success or a negative error code on failure. */ int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent, struct drm_driver *driver) @@ -346,15 +336,14 @@ err_free: EXPORT_SYMBOL(drm_get_pci_dev); /** - * PCI device initialization. Called direct from modules at load time. + * drm_pci_init - Register matching PCI devices with the DRM subsystem + * @driver: DRM device driver + * @pdriver: PCI device driver * - * \return zero on success or a negative number on failure. + * Initializes a drm_device structures, registering the stubs and initializing + * the AGP device. * - * Initializes a drm_device structures,registering the - * stubs and initializing the AGP device. - * - * Expands the \c DRIVER_PREINIT and \c DRIVER_POST_INIT macros before and - * after the initialization for driver customization. + * Return: 0 on success or a negative error code on failure. */ int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver) { @@ -458,7 +447,14 @@ int drm_pci_set_unique(struct drm_device *dev, EXPORT_SYMBOL(drm_pci_init); -/*@}*/ +/** + * drm_pci_exit - Unregister matching PCI devices from the DRM subsystem + * @driver: DRM device driver + * @pdriver: PCI device driver + * + * Unregisters one or more devices matched by a PCI driver from the DRM + * subsystem. + */ void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver) { struct drm_device *dev, *tmp; diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index f3e0a23abf4..6d133149cc7 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -25,6 +25,7 @@ #include <linux/list.h> #include <drm/drmP.h> +#include <drm/drm_plane_helper.h> #include <drm/drm_rect.h> #include <drm/drm_plane_helper.h> @@ -60,7 +61,7 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc, * need to grab the connection_mutex here to be able to make these * checks. */ - WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex)); + WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); list_for_each_entry(connector, &dev->mode_config.connector_list, head) if (connector->encoder && connector->encoder->crtc == crtc) { @@ -74,6 +75,79 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc, } /** + * drm_plane_helper_check_update() - Check plane update for validity + * @plane: plane object to update + * @crtc: owning CRTC of owning plane + * @fb: framebuffer to flip onto plane + * @src: source coordinates in 16.16 fixed point + * @dest: integer destination coordinates + * @clip: integer clipping coordinates + * @min_scale: minimum @src:@dest scaling factor in 16.16 fixed point + * @max_scale: maximum @src:@dest scaling factor in 16.16 fixed point + * @can_position: is it legal to position the plane such that it + * doesn't cover the entire crtc? This will generally + * only be false for primary planes. + * @can_update_disabled: can the plane be updated while the crtc + * is disabled? + * @visible: output parameter indicating whether plane is still visible after + * clipping + * + * Checks that a desired plane update is valid. Drivers that provide + * their own plane handling rather than helper-provided implementations may + * still wish to call this function to avoid duplication of error checking + * code. + * + * RETURNS: + * Zero if update appears valid, error code on failure + */ +int drm_plane_helper_check_update(struct drm_plane *plane, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_rect *src, + struct drm_rect *dest, + const struct drm_rect *clip, + int min_scale, + int max_scale, + bool can_position, + bool can_update_disabled, + bool *visible) +{ + int hscale, vscale; + + if (!crtc->enabled && !can_update_disabled) { + DRM_DEBUG_KMS("Cannot update plane of a disabled CRTC.\n"); + return -EINVAL; + } + + /* Check scaling */ + hscale = drm_rect_calc_hscale(src, dest, min_scale, max_scale); + vscale = drm_rect_calc_vscale(src, dest, min_scale, max_scale); + if (hscale < 0 || vscale < 0) { + DRM_DEBUG_KMS("Invalid scaling of plane\n"); + return -ERANGE; + } + + *visible = drm_rect_clip_scaled(src, dest, clip, hscale, vscale); + if (!*visible) + /* + * Plane isn't visible; some drivers can handle this + * so we just return success here. Drivers that can't + * (including those that use the primary plane helper's + * update function) will return an error from their + * update_plane handler. + */ + return 0; + + if (!can_position && !drm_rect_equals(dest, clip)) { + DRM_DEBUG_KMS("Plane must cover entire CRTC\n"); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(drm_plane_helper_check_update); + +/** * drm_primary_helper_update() - Helper for primary plane update * @plane: plane object to update * @crtc: owning CRTC of owning plane @@ -121,57 +195,42 @@ int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc, .x = src_x >> 16, .y = src_y >> 16, }; + struct drm_rect src = { + .x1 = src_x, + .y1 = src_y, + .x2 = src_x + src_w, + .y2 = src_y + src_h, + }; struct drm_rect dest = { .x1 = crtc_x, .y1 = crtc_y, .x2 = crtc_x + crtc_w, .y2 = crtc_y + crtc_h, }; - struct drm_rect clip = { + const struct drm_rect clip = { .x2 = crtc->mode.hdisplay, .y2 = crtc->mode.vdisplay, }; struct drm_connector **connector_list; int num_connectors, ret; + bool visible; - if (!crtc->enabled) { - DRM_DEBUG_KMS("Cannot update primary plane of a disabled CRTC.\n"); - return -EINVAL; - } - - /* Disallow subpixel positioning */ - if ((src_x | src_y | src_w | src_h) & SUBPIXEL_MASK) { - DRM_DEBUG_KMS("Primary plane does not support subpixel positioning\n"); - return -EINVAL; - } - - /* Primary planes are locked to their owning CRTC */ - if (plane->possible_crtcs != drm_crtc_mask(crtc)) { - DRM_DEBUG_KMS("Cannot change primary plane CRTC\n"); - return -EINVAL; - } - - /* Disallow scaling */ - src_w >>= 16; - src_h >>= 16; - if (crtc_w != src_w || crtc_h != src_h) { - DRM_DEBUG_KMS("Can't scale primary plane\n"); - return -EINVAL; - } - - /* Make sure primary plane covers entire CRTC */ - drm_rect_intersect(&dest, &clip); - if (dest.x1 != 0 || dest.y1 != 0 || - dest.x2 != crtc->mode.hdisplay || dest.y2 != crtc->mode.vdisplay) { - DRM_DEBUG_KMS("Primary plane must cover entire CRTC\n"); - return -EINVAL; - } - - /* Framebuffer must be big enough to cover entire plane */ - ret = drm_crtc_check_viewport(crtc, crtc_x, crtc_y, &crtc->mode, fb); + ret = drm_plane_helper_check_update(plane, crtc, fb, + &src, &dest, &clip, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + false, false, &visible); if (ret) return ret; + if (!visible) + /* + * Primary plane isn't visible. Note that unless a driver + * provides their own disable function, this will just + * wind up returning -EINVAL to userspace. + */ + return plane->funcs->disable_plane(plane); + /* Find current connectors for CRTC */ num_connectors = get_connectors_for_crtc(crtc, NULL, 0); BUG_ON(num_connectors == 0); diff --git a/drivers/gpu/drm/drm_platform.c b/drivers/gpu/drm/drm_platform.c index 234e0bc1ae5..d5b76f148c1 100644 --- a/drivers/gpu/drm/drm_platform.c +++ b/drivers/gpu/drm/drm_platform.c @@ -106,17 +106,16 @@ static struct drm_bus drm_platform_bus = { }; /** - * Platform device initialization. Called direct from modules. + * drm_platform_init - Register a platform device with the DRM subsystem + * @driver: DRM device driver + * @platform_device: platform device to register * - * \return zero on success or a negative number on failure. - * - * Initializes a drm_device structures,registering the - * stubs + * Registers the specified DRM device driver and platform device with the DRM + * subsystem, initializing a drm_device structure and calling the driver's + * .load() function. * - * Expands the \c DRIVER_PREINIT and \c DRIVER_POST_INIT macros before and - * after the initialization for driver customization. + * Return: 0 on success or a negative error code on failure. */ - int drm_platform_init(struct drm_driver *driver, struct platform_device *platform_device) { DRM_DEBUG("\n"); diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 3727ac8bc31..14d16464000 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -1,16 +1,11 @@ -/** - * \file drm_stub.h - * Stub support - * - * \author Rickard E. (Rik) Faith <faith@valinux.com> - */ - /* * Created: Fri Jan 19 10:48:35 2001 by faith@acm.org * * Copyright 2001 VA Linux Systems, Inc., Sunnyvale, California. * All Rights Reserved. * + * Author Rickard E. (Rik) Faith <faith@valinux.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 @@ -425,11 +420,15 @@ void drm_minor_release(struct drm_minor *minor) } /** - * Called via drm_exit() at module unload time or when pci device is - * unplugged. + * drm_put_dev - Unregister and release a DRM device + * @dev: DRM device * - * Cleans up all DRM device, calling drm_lastclose(). + * Called at module unload time or when a PCI device is unplugged. + * + * Use of this function is discouraged. It will eventually go away completely. + * Please use drm_dev_unregister() and drm_dev_unref() explicitly instead. * + * Cleans up all DRM device, calling drm_lastclose(). */ void drm_put_dev(struct drm_device *dev) { @@ -536,7 +535,7 @@ static void drm_fs_inode_free(struct inode *inode) } /** - * drm_dev_alloc - Allocate new drm device + * drm_dev_alloc - Allocate new DRM device * @driver: DRM driver to allocate device for * @parent: Parent device object * @@ -650,6 +649,7 @@ static void drm_dev_release(struct kref *ref) drm_minor_free(dev, DRM_MINOR_CONTROL); mutex_destroy(&dev->master_mutex); + kfree(dev->unique); kfree(dev); } @@ -689,6 +689,7 @@ EXPORT_SYMBOL(drm_dev_unref); /** * drm_dev_register - Register DRM device * @dev: Device to register + * @flags: Flags passed to the driver's .load() function * * Register the DRM device @dev with the system, advertise device to user-space * and start normal device operation. @dev must be allocated via drm_dev_alloc() @@ -777,3 +778,28 @@ void drm_dev_unregister(struct drm_device *dev) drm_minor_unregister(dev, DRM_MINOR_CONTROL); } EXPORT_SYMBOL(drm_dev_unregister); + +/** + * drm_dev_set_unique - Set the unique name of a DRM device + * @dev: device of which to set the unique name + * @fmt: format string for unique name + * + * Sets the unique name of a DRM device using the specified format string and + * a variable list of arguments. Drivers can use this at driver probe time if + * the unique name of the devices they drive is static. + * + * Return: 0 on success or a negative error code on failure. + */ +int drm_dev_set_unique(struct drm_device *dev, const char *fmt, ...) +{ + va_list ap; + + kfree(dev->unique); + + va_start(ap, fmt); + dev->unique = kvasprintf(GFP_KERNEL, fmt, ap); + va_end(ap); + + return dev->unique ? 0 : -ENOMEM; +} +EXPORT_SYMBOL(drm_dev_set_unique); diff --git a/drivers/gpu/drm/drm_usb.c b/drivers/gpu/drm/drm_usb.c index c6c7c29ad46..f2fe94aab90 100644 --- a/drivers/gpu/drm/drm_usb.c +++ b/drivers/gpu/drm/drm_usb.c @@ -45,7 +45,17 @@ static int drm_usb_set_busid(struct drm_device *dev, static struct drm_bus drm_usb_bus = { .set_busid = drm_usb_set_busid, }; - + +/** + * drm_usb_init - Register matching USB devices with the DRM subsystem + * @driver: DRM device driver + * @udriver: USB device driver + * + * Registers one or more devices matched by a USB driver with the DRM + * subsystem. + * + * Return: 0 on success or a negative error code on failure. + */ int drm_usb_init(struct drm_driver *driver, struct usb_driver *udriver) { int res; @@ -58,6 +68,14 @@ int drm_usb_init(struct drm_driver *driver, struct usb_driver *udriver) } EXPORT_SYMBOL(drm_usb_init); +/** + * drm_usb_exit - Unregister matching USB devices from the DRM subsystem + * @driver: DRM device driver + * @udriver: USB device driver + * + * Unregisters one or more devices matched by a USB driver from the DRM + * subsystem. + */ void drm_usb_exit(struct drm_driver *driver, struct usb_driver *udriver) { diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c index 5e05dbc6008..a8ffc8c1477 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.c +++ b/drivers/gpu/drm/exynos/exynos_dp_core.c @@ -1087,7 +1087,7 @@ static void exynos_dp_dpms(struct exynos_drm_display *display, int mode) break; default: break; - }; + } dp->dpms_mode = mode; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c index f1b8587cc63..9e530f205ad 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c @@ -40,7 +40,7 @@ exynos_dpi_detect(struct drm_connector *connector, bool force) { struct exynos_dpi *ctx = connector_to_dpi(connector); - if (!ctx->panel->connector) + if (ctx->panel && !ctx->panel->connector) drm_panel_attach(ctx->panel, &ctx->connector); return connector_status_connected; @@ -152,7 +152,7 @@ static void exynos_dpi_dpms(struct exynos_drm_display *display, int mode) break; default: break; - }; + } ctx->dpms_mode = mode; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 5d225dd58a8..ab7d182063c 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -573,7 +573,7 @@ static int exynos_drm_platform_probe(struct platform_device *pdev) int ret; pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); - exynos_drm_driver.num_ioctls = DRM_ARRAY_SIZE(exynos_ioctls); + exynos_drm_driver.num_ioctls = ARRAY_SIZE(exynos_ioctls); #ifdef CONFIG_DRM_EXYNOS_FIMD ret = platform_driver_register(&fimd_driver); @@ -765,24 +765,24 @@ static int exynos_drm_init(void) return 0; -err_unregister_pd: - platform_device_unregister(exynos_drm_pdev); - err_remove_vidi: #ifdef CONFIG_DRM_EXYNOS_VIDI exynos_drm_remove_vidi(); + +err_unregister_pd: #endif + platform_device_unregister(exynos_drm_pdev); return ret; } static void exynos_drm_exit(void) { + platform_driver_unregister(&exynos_drm_platform_driver); #ifdef CONFIG_DRM_EXYNOS_VIDI exynos_drm_remove_vidi(); #endif platform_device_unregister(exynos_drm_pdev); - platform_driver_unregister(&exynos_drm_platform_driver); } module_init(exynos_drm_init); diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 36535f39884..06cde450627 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -343,7 +343,7 @@ struct exynos_drm_display * exynos_dpi_probe(struct device *dev); int exynos_dpi_remove(struct device *dev); #else static inline struct exynos_drm_display * -exynos_dpi_probe(struct device *dev) { return 0; } +exynos_dpi_probe(struct device *dev) { return NULL; } static inline int exynos_dpi_remove(struct device *dev) { return 0; } #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 14bbaa33b47..d771b467cf0 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -367,7 +367,5 @@ void exynos_drm_fbdev_restore_mode(struct drm_device *dev) if (!private || !private->fb_helper) return; - drm_modeset_lock_all(dev); - drm_fb_helper_restore_fbdev_mode(private->fb_helper); - drm_modeset_unlock_all(dev); + drm_fb_helper_restore_fbdev_mode_unlocked(private->fb_helper); } diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index bb45ab2e738..33161ad3820 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -741,6 +741,8 @@ static void fimd_apply(struct exynos_drm_manager *mgr) win_data = &ctx->win_data[i]; if (win_data->enabled) fimd_win_commit(mgr, i); + else + fimd_win_disable(mgr, i); } fimd_commit(mgr); diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index 6c1885eedfd..80015871447 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -467,14 +467,17 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, goto err_free; } + down_read(¤t->mm->mmap_sem); vma = find_vma(current->mm, userptr); if (!vma) { + up_read(¤t->mm->mmap_sem); DRM_ERROR("failed to get vm region.\n"); ret = -EFAULT; goto err_free_pages; } if (vma->vm_end < userptr + size) { + up_read(¤t->mm->mmap_sem); DRM_ERROR("vma is too small.\n"); ret = -EFAULT; goto err_free_pages; @@ -482,6 +485,7 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, g2d_userptr->vma = exynos_gem_get_vma(vma); if (!g2d_userptr->vma) { + up_read(¤t->mm->mmap_sem); DRM_ERROR("failed to copy vma.\n"); ret = -ENOMEM; goto err_free_pages; @@ -492,10 +496,12 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, ret = exynos_gem_get_pages_from_userptr(start & PAGE_MASK, npages, pages, vma); if (ret < 0) { + up_read(¤t->mm->mmap_sem); DRM_ERROR("failed to get user pages from userptr.\n"); goto err_put_vma; } + up_read(¤t->mm->mmap_sem); g2d_userptr->pages = pages; sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.c b/drivers/gpu/drm/exynos/exynos_drm_ipp.c index 603a79602f3..a1888e128f1 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_ipp.c +++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.c @@ -286,7 +286,7 @@ static struct exynos_drm_ippdrv *ipp_find_drv_by_handle(u32 prop_id) /* * This case is search ipp driver by prop_id handle. * sometimes, ipp subsystem find driver by prop_id. - * e.g PAUSE state, queue buf, command contro. + * e.g PAUSE state, queue buf, command control. */ list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]\n", count++, (int)ippdrv); diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index c104d0c9b38..aa259b0a873 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -2090,6 +2090,11 @@ out: static void hdmi_dpms(struct exynos_drm_display *display, int mode) { + struct hdmi_context *hdata = display->ctx; + struct drm_encoder *encoder = hdata->encoder; + struct drm_crtc *crtc = encoder->crtc; + struct drm_crtc_helper_funcs *funcs = NULL; + DRM_DEBUG_KMS("mode %d\n", mode); switch (mode) { @@ -2099,6 +2104,20 @@ static void hdmi_dpms(struct exynos_drm_display *display, int mode) case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: case DRM_MODE_DPMS_OFF: + /* + * The SFRs of VP and Mixer are updated by Vertical Sync of + * Timing generator which is a part of HDMI so the sequence + * to disable TV Subsystem should be as following, + * VP -> Mixer -> HDMI + * + * Below codes will try to disable Mixer and VP(if used) + * prior to disabling HDMI. + */ + if (crtc) + funcs = crtc->helper_private; + if (funcs && funcs->dpms) + (*funcs->dpms)(crtc, mode); + hdmi_poweroff(display); break; default: diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index 4c5aed7e54c..7529946d0a7 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -377,6 +377,20 @@ static void mixer_run(struct mixer_context *ctx) mixer_regs_dump(ctx); } +static void mixer_stop(struct mixer_context *ctx) +{ + struct mixer_resources *res = &ctx->mixer_res; + int timeout = 20; + + mixer_reg_writemask(res, MXR_STATUS, 0, MXR_STATUS_REG_RUN); + + while (!(mixer_reg_read(res, MXR_STATUS) & MXR_STATUS_REG_IDLE) && + --timeout) + usleep_range(10000, 12000); + + mixer_regs_dump(ctx); +} + static void vp_video_buffer(struct mixer_context *ctx, int win) { struct mixer_resources *res = &ctx->mixer_res; @@ -497,13 +511,8 @@ static void vp_video_buffer(struct mixer_context *ctx, int win) static void mixer_layer_update(struct mixer_context *ctx) { struct mixer_resources *res = &ctx->mixer_res; - u32 val; - - val = mixer_reg_read(res, MXR_CFG); - /* allow one update per vsync only */ - if (!(val & MXR_CFG_LAYER_UPDATE_COUNT_MASK)) - mixer_reg_writemask(res, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE); + mixer_reg_writemask(res, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE); } static void mixer_graph_buffer(struct mixer_context *ctx, int win) @@ -1010,6 +1019,8 @@ static void mixer_wait_for_vblank(struct exynos_drm_manager *mgr) } mutex_unlock(&mixer_ctx->mixer_mutex); + drm_vblank_get(mgr->crtc->dev, mixer_ctx->pipe); + atomic_set(&mixer_ctx->wait_vsync_event, 1); /* @@ -1020,6 +1031,8 @@ static void mixer_wait_for_vblank(struct exynos_drm_manager *mgr) !atomic_read(&mixer_ctx->wait_vsync_event), HZ/20)) DRM_DEBUG_KMS("vblank wait timed out.\n"); + + drm_vblank_put(mgr->crtc->dev, mixer_ctx->pipe); } static void mixer_window_suspend(struct exynos_drm_manager *mgr) @@ -1061,7 +1074,7 @@ static void mixer_poweron(struct exynos_drm_manager *mgr) mutex_unlock(&ctx->mixer_mutex); return; } - ctx->powered = true; + mutex_unlock(&ctx->mixer_mutex); pm_runtime_get_sync(ctx->dev); @@ -1072,6 +1085,12 @@ static void mixer_poweron(struct exynos_drm_manager *mgr) clk_prepare_enable(res->sclk_mixer); } + mutex_lock(&ctx->mixer_mutex); + ctx->powered = true; + mutex_unlock(&ctx->mixer_mutex); + + mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET); + mixer_reg_write(res, MXR_INT_EN, ctx->int_en); mixer_win_reset(ctx); @@ -1084,14 +1103,21 @@ static void mixer_poweroff(struct exynos_drm_manager *mgr) struct mixer_resources *res = &ctx->mixer_res; mutex_lock(&ctx->mixer_mutex); - if (!ctx->powered) - goto out; + if (!ctx->powered) { + mutex_unlock(&ctx->mixer_mutex); + return; + } mutex_unlock(&ctx->mixer_mutex); + mixer_stop(ctx); mixer_window_suspend(mgr); ctx->int_en = mixer_reg_read(res, MXR_INT_EN); + mutex_lock(&ctx->mixer_mutex); + ctx->powered = false; + mutex_unlock(&ctx->mixer_mutex); + clk_disable_unprepare(res->mixer); if (ctx->vp_enabled) { clk_disable_unprepare(res->vp); @@ -1099,12 +1125,6 @@ static void mixer_poweroff(struct exynos_drm_manager *mgr) } pm_runtime_put_sync(ctx->dev); - - mutex_lock(&ctx->mixer_mutex); - ctx->powered = false; - -out: - mutex_unlock(&ctx->mixer_mutex); } static void mixer_dpms(struct exynos_drm_manager *mgr, int mode) diff --git a/drivers/gpu/drm/exynos/regs-mixer.h b/drivers/gpu/drm/exynos/regs-mixer.h index 4537026bc38..5f32e1a2941 100644 --- a/drivers/gpu/drm/exynos/regs-mixer.h +++ b/drivers/gpu/drm/exynos/regs-mixer.h @@ -78,6 +78,7 @@ #define MXR_STATUS_BIG_ENDIAN (1 << 3) #define MXR_STATUS_ENDIAN_MASK (1 << 3) #define MXR_STATUS_SYNC_ENABLE (1 << 2) +#define MXR_STATUS_REG_IDLE (1 << 1) #define MXR_STATUS_REG_RUN (1 << 0) /* bits for MXR_CFG */ diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c b/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c index 489ffd2c66e..87885d8c06e 100644 --- a/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c +++ b/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c @@ -148,7 +148,7 @@ static int handle_dsi_error(struct mdfld_dsi_pkg_sender *sender, u32 mask) break; case BIT(14): /*wait for all fifo empty*/ - /*wait_for_all_fifos_empty(sender)*/; + /*wait_for_all_fifos_empty(sender)*/ break; case BIT(15): dev_dbg(sender->dev->dev, "No Action required\n"); diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index 0a3101a3db1..6e8fe9ec02b 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -112,11 +112,9 @@ static void psb_driver_lastclose(struct drm_device *dev) struct drm_psb_private *dev_priv = dev->dev_private; struct psb_fbdev *fbdev = dev_priv->fbdev; - drm_modeset_lock_all(dev); - ret = drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper); + ret = drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev->psb_fb_helper); if (ret) DRM_DEBUG("failed to restore crtc mode\n"); - drm_modeset_unlock_all(dev); return; } @@ -479,7 +477,7 @@ static struct drm_driver driver = { .lastclose = psb_driver_lastclose, .preclose = psb_driver_preclose, - .num_ioctls = DRM_ARRAY_SIZE(psb_ioctls), + .num_ioctls = ARRAY_SIZE(psb_ioctls), .device_is_agp = psb_driver_device_is_agp, .irq_preinstall = psb_irq_preinstall, .irq_postinstall = psb_irq_postinstall, diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c index 240c331405b..ac357b02bd3 100644 --- a/drivers/gpu/drm/i2c/tda998x_drv.c +++ b/drivers/gpu/drm/i2c/tda998x_drv.c @@ -810,6 +810,12 @@ static int tda998x_encoder_mode_valid(struct drm_encoder *encoder, struct drm_display_mode *mode) { + if (mode->clock > 150000) + return MODE_CLOCK_HIGH; + if (mode->htotal >= BIT(13)) + return MODE_BAD_HVALUE; + if (mode->vtotal >= BIT(11)) + return MODE_BAD_VVALUE; return MODE_OK; } @@ -1048,8 +1054,8 @@ read_edid_block(struct drm_encoder *encoder, uint8_t *buf, int blk) return i; } } else { - for (i = 10; i > 0; i--) { - msleep(10); + for (i = 100; i > 0; i--) { + msleep(1); ret = reg_read(priv, REG_INT_FLAGS_2); if (ret < 0) return ret; @@ -1183,7 +1189,6 @@ static void tda998x_encoder_destroy(struct drm_encoder *encoder) { struct tda998x_priv *priv = to_tda998x_priv(encoder); - drm_i2c_encoder_destroy(encoder); /* disable all IRQs and free the IRQ handler */ cec_write(priv, REG_CEC_RXSHPDINTENA, 0); @@ -1193,6 +1198,7 @@ tda998x_encoder_destroy(struct drm_encoder *encoder) if (priv->cec) i2c_unregister_device(priv->cec); + drm_i2c_encoder_destroy(encoder); kfree(priv); } diff --git a/drivers/gpu/drm/i810/i810_dma.c b/drivers/gpu/drm/i810/i810_dma.c index aeace37415a..e88bac1d781 100644 --- a/drivers/gpu/drm/i810/i810_dma.c +++ b/drivers/gpu/drm/i810/i810_dma.c @@ -1251,7 +1251,7 @@ const struct drm_ioctl_desc i810_ioctls[] = { DRM_IOCTL_DEF_DRV(I810_FLIP, i810_flip_bufs, DRM_AUTH|DRM_UNLOCKED), }; -int i810_max_ioctl = DRM_ARRAY_SIZE(i810_ioctls); +int i810_max_ioctl = ARRAY_SIZE(i810_ioctls); /** * Determine if the device really is AGP or not. diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 7b2f3bee351..cad1683d8bb 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -62,6 +62,7 @@ i915-y += dvo_ch7017.o \ intel_dsi_cmd.o \ intel_dsi.o \ intel_dsi_pll.o \ + intel_dsi_panel_vbt.o \ intel_dvo.o \ intel_hdmi.o \ intel_i2c.o \ diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 169fc2d8c55..b8c689202c4 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -446,7 +446,9 @@ static int i915_gem_object_info(struct seq_file *m, void* data) memset(&stats, 0, sizeof(stats)); stats.file_priv = file->driver_priv; + spin_lock(&file->table_lock); idr_for_each(&file->object_idr, per_file_stats, &stats); + spin_unlock(&file->table_lock); /* * Although we have a valid reference on file->pid, that does * not guarantee that the task_struct who called get_pid() is @@ -2353,10 +2355,14 @@ static int i915_display_info(struct seq_file *m, void *unused) active = cursor_position(dev, crtc->pipe, &x, &y); seq_printf(m, "\tcursor visible? %s, position (%d, %d), addr 0x%08x, active? %s\n", - yesno(crtc->cursor_visible), + yesno(crtc->cursor_base), x, y, crtc->cursor_addr, yesno(active)); } + + seq_printf(m, "\tunderrun reporting: cpu=%s pch=%s \n", + yesno(!crtc->cpu_fifo_underrun_disabled), + yesno(!crtc->pch_fifo_underrun_disabled)); } seq_printf(m, "\n"); diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 4e70de6ed46..d4434414062 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -36,6 +36,8 @@ #include "i915_drv.h" #include "i915_trace.h" #include <linux/pci.h> +#include <linux/console.h> +#include <linux/vt.h> #include <linux/vgaarb.h> #include <linux/acpi.h> #include <linux/pnp.h> @@ -1386,7 +1388,6 @@ cleanup_gem: i915_gem_context_fini(dev); mutex_unlock(&dev->struct_mutex); WARN_ON(dev_priv->mm.aliasing_ppgtt); - drm_mm_takedown(&dev_priv->gtt.base.mm); cleanup_irq: drm_irq_uninstall(dev); cleanup_gem_stolen: @@ -1450,6 +1451,39 @@ static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) } #endif +#if !defined(CONFIG_VGA_CONSOLE) +static int i915_kick_out_vgacon(struct drm_i915_private *dev_priv) +{ + return 0; +} +#elif !defined(CONFIG_DUMMY_CONSOLE) +static int i915_kick_out_vgacon(struct drm_i915_private *dev_priv) +{ + return -ENODEV; +} +#else +static int i915_kick_out_vgacon(struct drm_i915_private *dev_priv) +{ + int ret = 0; + + DRM_INFO("Replacing VGA console driver\n"); + + console_lock(); + if (con_is_bound(&vga_con)) + ret = do_take_over_console(&dummy_con, 0, MAX_NR_CONSOLES - 1, 1); + if (ret == 0) { + ret = do_unregister_con_driver(&vga_con); + + /* Ignore "already unregistered". */ + if (ret == -ENODEV) + ret = 0; + } + console_unlock(); + + return ret; +} +#endif + static void i915_dump_device_info(struct drm_i915_private *dev_priv) { const struct intel_device_info *info = &dev_priv->info; @@ -1623,8 +1657,15 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) if (ret) goto out_regs; - if (drm_core_check_feature(dev, DRIVER_MODESET)) + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + ret = i915_kick_out_vgacon(dev_priv); + if (ret) { + DRM_ERROR("failed to remove conflicting VGA console\n"); + goto out_gtt; + } + i915_kick_out_firmware_fb(dev_priv); + } pci_set_master(dev->pdev); @@ -1756,8 +1797,6 @@ out_mtrrfree: arch_phys_wc_del(dev_priv->gtt.mtrr); io_mapping_free(dev_priv->gtt.mappable); out_gtt: - list_del(&dev_priv->gtt.base.global_link); - drm_mm_takedown(&dev_priv->gtt.base.mm); dev_priv->gtt.base.cleanup(&dev_priv->gtt.base); out_regs: intel_uncore_fini(dev); @@ -1836,7 +1875,6 @@ int i915_driver_unload(struct drm_device *dev) flush_workqueue(dev_priv->wq); mutex_lock(&dev->struct_mutex); - i915_gem_free_all_phys_object(dev); i915_gem_cleanup_ringbuffer(dev); i915_gem_context_fini(dev); WARN_ON(dev_priv->mm.aliasing_ppgtt); @@ -1847,7 +1885,6 @@ int i915_driver_unload(struct drm_device *dev) i915_free_hws(dev); } - list_del(&dev_priv->gtt.base.global_link); WARN_ON(!list_empty(&dev_priv->vm_list)); drm_vblank_cleanup(dev); @@ -1987,7 +2024,7 @@ const struct drm_ioctl_desc i915_ioctls[] = { DRM_IOCTL_DEF_DRV(I915_GEM_USERPTR, i915_gem_userptr_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), }; -int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls); +int i915_max_ioctl = ARRAY_SIZE(i915_ioctls); /* * This is really ugly: Because old userspace abused the linux agp interface to diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 5b5b82c3f57..651e65e051c 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -321,6 +321,7 @@ static const struct intel_device_info intel_broadwell_m_info = { .has_ddi = 1, .has_fbc = 1, GEN_DEFAULT_PIPEOFFSETS, + IVB_CURSOR_OFFSETS, }; static const struct intel_device_info intel_broadwell_gt3d_info = { @@ -331,6 +332,7 @@ static const struct intel_device_info intel_broadwell_gt3d_info = { .has_ddi = 1, .has_fbc = 1, GEN_DEFAULT_PIPEOFFSETS, + IVB_CURSOR_OFFSETS, }; static const struct intel_device_info intel_broadwell_gt3m_info = { @@ -811,17 +813,17 @@ int i915_reset(struct drm_device *dev) } /* - * FIXME: This is horribly race against concurrent pageflip and - * vblank wait ioctls since they can observe dev->irqs_disabled - * being false when they shouldn't be able to. + * FIXME: This races pretty badly against concurrent holders of + * ring interrupts. This is possible since we've started to drop + * dev->struct_mutex in select places when waiting for the gpu. */ - drm_irq_uninstall(dev); - drm_irq_install(dev, dev->pdev->irq); - /* rps/rc6 re-init is necessary to restore state lost after the - * reset and the re-install of drm irq. Skip for ironlake per + /* + * rps/rc6 re-init is necessary to restore state lost after the + * reset and the re-install of gt irqs. Skip for ironlake per * previous concerns that it doesn't respond well to some forms - * of re-init after reset. */ + * of re-init after reset. + */ if (INTEL_INFO(dev)->gen > 5) intel_reset_gt_powersave(dev); @@ -1583,6 +1585,7 @@ static int __init i915_init(void) driver.get_vblank_timestamp = NULL; #ifndef CONFIG_DRM_I915_UMS /* Silently fail loading to not upset userspace. */ + DRM_DEBUG_DRIVER("KMS and UMS disabled.\n"); return 0; #endif } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 8f68678f361..374f964323a 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -251,18 +251,6 @@ struct intel_ddi_plls { #define WATCH_LISTS 0 #define WATCH_GTT 0 -#define I915_GEM_PHYS_CURSOR_0 1 -#define I915_GEM_PHYS_CURSOR_1 2 -#define I915_GEM_PHYS_OVERLAY_REGS 3 -#define I915_MAX_PHYS_OBJECT (I915_GEM_PHYS_OVERLAY_REGS) - -struct drm_i915_gem_phys_object { - int id; - struct page **page_list; - drm_dma_handle_t *handle; - struct drm_i915_gem_object *cur_obj; -}; - struct opregion_header; struct opregion_acpi; struct opregion_swsci; @@ -668,6 +656,7 @@ enum intel_sbi_destination { #define QUIRK_PIPEA_FORCE (1<<0) #define QUIRK_LVDS_SSC_DISABLE (1<<1) #define QUIRK_INVERT_BRIGHTNESS (1<<2) +#define QUIRK_BACKLIGHT_PRESENT (1<<3) struct intel_fbdev; struct intel_fbc_work; @@ -989,6 +978,8 @@ struct i915_power_well { bool always_on; /* power well enable/disable usage count */ int count; + /* cached hw enabled state */ + bool hw_enabled; unsigned long domains; unsigned long data; const struct i915_power_well_ops *ops; @@ -1106,9 +1097,6 @@ struct i915_gem_mm { /** Bit 6 swizzling required for Y tiling */ uint32_t bit_6_swizzle_y; - /* storage for physical objects */ - struct drm_i915_gem_phys_object *phys_objs[I915_MAX_PHYS_OBJECT]; - /* accounting, useful for userland debugging */ spinlock_t object_stat_lock; size_t object_memory; @@ -1222,6 +1210,7 @@ struct intel_vbt_data { unsigned int lvds_use_ssc:1; unsigned int display_clock_mode:1; unsigned int fdi_rx_polarity_inverted:1; + unsigned int has_mipi:1; int lvds_ssc_freq; unsigned int bios_lvds_val; /* initial [PCH_]LVDS reg val in VBIOS */ @@ -1245,6 +1234,7 @@ struct intel_vbt_data { /* MIPI DSI */ struct { + u16 port; u16 panel_id; struct mipi_config *config; struct mipi_pps_data *pps; @@ -1712,7 +1702,7 @@ struct drm_i915_gem_object { struct drm_file *pin_filp; /** for phy allocated objects */ - struct drm_i915_gem_phys_object *phys_obj; + drm_dma_handle_t *phys_handle; union { struct i915_gem_userptr { @@ -1916,6 +1906,9 @@ struct drm_i915_cmd_table { #define IS_ULT(dev) (IS_HSW_ULT(dev) || IS_BDW_ULT(dev)) #define IS_HSW_GT3(dev) (IS_HASWELL(dev) && \ ((dev)->pdev->device & 0x00F0) == 0x0020) +/* ULX machines are also considered ULT. */ +#define IS_HSW_ULX(dev) ((dev)->pdev->device == 0x0A0E || \ + (dev)->pdev->device == 0x0A1E) #define IS_PRELIMINARY_HW(intel_info) ((intel_info)->is_preliminary) /* @@ -2172,10 +2165,12 @@ void i915_gem_vma_destroy(struct i915_vma *vma); #define PIN_MAPPABLE 0x1 #define PIN_NONBLOCK 0x2 #define PIN_GLOBAL 0x4 +#define PIN_OFFSET_BIAS 0x8 +#define PIN_OFFSET_MASK (~4095) int __must_check i915_gem_object_pin(struct drm_i915_gem_object *obj, struct i915_address_space *vm, uint32_t alignment, - unsigned flags); + uint64_t flags); int __must_check i915_vma_unbind(struct i915_vma *vma); int i915_gem_object_put_pages(struct drm_i915_gem_object *obj); void i915_gem_release_all_mmaps(struct drm_i915_private *dev_priv); @@ -2297,13 +2292,8 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj, u32 alignment, struct intel_engine_cs *pipelined); void i915_gem_object_unpin_from_display_plane(struct drm_i915_gem_object *obj); -int i915_gem_attach_phys_object(struct drm_device *dev, - struct drm_i915_gem_object *obj, - int id, +int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj, int align); -void i915_gem_detach_phys_object(struct drm_device *dev, - struct drm_i915_gem_object *obj); -void i915_gem_free_all_phys_object(struct drm_device *dev); int i915_gem_open(struct drm_device *dev, struct drm_file *file); void i915_gem_release(struct drm_device *dev, struct drm_file *file); @@ -2430,6 +2420,8 @@ int __must_check i915_gem_evict_something(struct drm_device *dev, int min_size, unsigned alignment, unsigned cache_level, + unsigned long start, + unsigned long end, unsigned flags); int i915_gem_evict_vm(struct i915_address_space *vm, bool do_idle); int i915_gem_evict_everything(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 87e9b349ebe..d893e4da5dc 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -47,11 +47,6 @@ i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj, static void i915_gem_object_retire(struct drm_i915_gem_object *obj); -static int i915_gem_phys_pwrite(struct drm_device *dev, - struct drm_i915_gem_object *obj, - struct drm_i915_gem_pwrite *args, - struct drm_file *file); - static void i915_gem_write_fence(struct drm_device *dev, int reg, struct drm_i915_gem_object *obj); static void i915_gem_object_update_fence(struct drm_i915_gem_object *obj, @@ -214,6 +209,128 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, return 0; } +static void i915_gem_object_detach_phys(struct drm_i915_gem_object *obj) +{ + drm_dma_handle_t *phys = obj->phys_handle; + + if (!phys) + return; + + if (obj->madv == I915_MADV_WILLNEED) { + struct address_space *mapping = file_inode(obj->base.filp)->i_mapping; + char *vaddr = phys->vaddr; + int i; + + for (i = 0; i < obj->base.size / PAGE_SIZE; i++) { + struct page *page = shmem_read_mapping_page(mapping, i); + if (!IS_ERR(page)) { + char *dst = kmap_atomic(page); + memcpy(dst, vaddr, PAGE_SIZE); + drm_clflush_virt_range(dst, PAGE_SIZE); + kunmap_atomic(dst); + + set_page_dirty(page); + mark_page_accessed(page); + page_cache_release(page); + } + vaddr += PAGE_SIZE; + } + i915_gem_chipset_flush(obj->base.dev); + } + +#ifdef CONFIG_X86 + set_memory_wb((unsigned long)phys->vaddr, phys->size / PAGE_SIZE); +#endif + drm_pci_free(obj->base.dev, phys); + obj->phys_handle = NULL; +} + +int +i915_gem_object_attach_phys(struct drm_i915_gem_object *obj, + int align) +{ + drm_dma_handle_t *phys; + struct address_space *mapping; + char *vaddr; + int i; + + if (obj->phys_handle) { + if ((unsigned long)obj->phys_handle->vaddr & (align -1)) + return -EBUSY; + + return 0; + } + + if (obj->madv != I915_MADV_WILLNEED) + return -EFAULT; + + if (obj->base.filp == NULL) + return -EINVAL; + + /* create a new object */ + phys = drm_pci_alloc(obj->base.dev, obj->base.size, align); + if (!phys) + return -ENOMEM; + + vaddr = phys->vaddr; +#ifdef CONFIG_X86 + set_memory_wc((unsigned long)vaddr, phys->size / PAGE_SIZE); +#endif + mapping = file_inode(obj->base.filp)->i_mapping; + for (i = 0; i < obj->base.size / PAGE_SIZE; i++) { + struct page *page; + char *src; + + page = shmem_read_mapping_page(mapping, i); + if (IS_ERR(page)) { +#ifdef CONFIG_X86 + set_memory_wb((unsigned long)phys->vaddr, phys->size / PAGE_SIZE); +#endif + drm_pci_free(obj->base.dev, phys); + return PTR_ERR(page); + } + + src = kmap_atomic(page); + memcpy(vaddr, src, PAGE_SIZE); + kunmap_atomic(src); + + mark_page_accessed(page); + page_cache_release(page); + + vaddr += PAGE_SIZE; + } + + obj->phys_handle = phys; + return 0; +} + +static int +i915_gem_phys_pwrite(struct drm_i915_gem_object *obj, + struct drm_i915_gem_pwrite *args, + struct drm_file *file_priv) +{ + struct drm_device *dev = obj->base.dev; + void *vaddr = obj->phys_handle->vaddr + args->offset; + char __user *user_data = to_user_ptr(args->data_ptr); + + if (__copy_from_user_inatomic_nocache(vaddr, user_data, args->size)) { + unsigned long unwritten; + + /* The physical object once assigned is fixed for the lifetime + * of the obj, so we can safely drop the lock and continue + * to access vaddr. + */ + mutex_unlock(&dev->struct_mutex); + unwritten = copy_from_user(vaddr, user_data, args->size); + mutex_lock(&dev->struct_mutex); + if (unwritten) + return -EFAULT; + } + + i915_gem_chipset_flush(dev); + return 0; +} + void *i915_gem_object_alloc(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -930,8 +1047,8 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, * pread/pwrite currently are reading and writing from the CPU * perspective, requiring manual detiling by the client. */ - if (obj->phys_obj) { - ret = i915_gem_phys_pwrite(dev, obj, args, file); + if (obj->phys_handle) { + ret = i915_gem_phys_pwrite(obj, args, file); goto out; } @@ -1427,7 +1544,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) /* Access to snoopable pages through the GTT is incoherent. */ if (obj->cache_level != I915_CACHE_NONE && !HAS_LLC(dev)) { - ret = -EINVAL; + ret = -EFAULT; goto unlock; } @@ -1499,22 +1616,6 @@ out: return ret; } -void i915_gem_release_all_mmaps(struct drm_i915_private *dev_priv) -{ - struct i915_vma *vma; - - /* - * Only the global gtt is relevant for gtt memory mappings, so restrict - * list traversal to objects bound into the global address space. Note - * that the active list should be empty, but better safe than sorry. - */ - WARN_ON(!list_empty(&dev_priv->gtt.base.active_list)); - list_for_each_entry(vma, &dev_priv->gtt.base.active_list, mm_list) - i915_gem_release_mmap(vma->obj); - list_for_each_entry(vma, &dev_priv->gtt.base.inactive_list, mm_list) - i915_gem_release_mmap(vma->obj); -} - /** * i915_gem_release_mmap - remove physical page mappings * @obj: obj in question @@ -1540,6 +1641,15 @@ i915_gem_release_mmap(struct drm_i915_gem_object *obj) obj->fault_mappable = false; } +void +i915_gem_release_all_mmaps(struct drm_i915_private *dev_priv) +{ + struct drm_i915_gem_object *obj; + + list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) + i915_gem_release_mmap(obj); +} + uint32_t i915_gem_get_gtt_size(struct drm_device *dev, uint32_t size, int tiling_mode) { @@ -3257,12 +3367,14 @@ static struct i915_vma * i915_gem_object_bind_to_vm(struct drm_i915_gem_object *obj, struct i915_address_space *vm, unsigned alignment, - unsigned flags) + uint64_t flags) { struct drm_device *dev = obj->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 size, fence_size, fence_alignment, unfenced_alignment; - size_t gtt_max = + unsigned long start = + flags & PIN_OFFSET_BIAS ? flags & PIN_OFFSET_MASK : 0; + unsigned long end = flags & PIN_MAPPABLE ? dev_priv->gtt.mappable_end : vm->total; struct i915_vma *vma; int ret; @@ -3291,11 +3403,11 @@ i915_gem_object_bind_to_vm(struct drm_i915_gem_object *obj, /* If the object is bigger than the entire aperture, reject it early * before evicting everything in a vain attempt to find space. */ - if (obj->base.size > gtt_max) { - DRM_DEBUG("Attempting to bind an object larger than the aperture: object=%zd > %s aperture=%zu\n", + if (obj->base.size > end) { + DRM_DEBUG("Attempting to bind an object larger than the aperture: object=%zd > %s aperture=%lu\n", obj->base.size, flags & PIN_MAPPABLE ? "mappable" : "total", - gtt_max); + end); return ERR_PTR(-E2BIG); } @@ -3312,12 +3424,15 @@ i915_gem_object_bind_to_vm(struct drm_i915_gem_object *obj, search_free: ret = drm_mm_insert_node_in_range_generic(&vm->mm, &vma->node, size, alignment, - obj->cache_level, 0, gtt_max, + obj->cache_level, + start, end, DRM_MM_SEARCH_DEFAULT, DRM_MM_CREATE_DEFAULT); if (ret) { ret = i915_gem_evict_something(dev, vm, size, alignment, - obj->cache_level, flags); + obj->cache_level, + start, end, + flags); if (ret == 0) goto search_free; @@ -3892,11 +4007,30 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file) return ret; } +static bool +i915_vma_misplaced(struct i915_vma *vma, uint32_t alignment, uint64_t flags) +{ + struct drm_i915_gem_object *obj = vma->obj; + + if (alignment && + vma->node.start & (alignment - 1)) + return true; + + if (flags & PIN_MAPPABLE && !obj->map_and_fenceable) + return true; + + if (flags & PIN_OFFSET_BIAS && + vma->node.start < (flags & PIN_OFFSET_MASK)) + return true; + + return false; +} + int i915_gem_object_pin(struct drm_i915_gem_object *obj, struct i915_address_space *vm, uint32_t alignment, - unsigned flags) + uint64_t flags) { struct drm_i915_private *dev_priv = obj->base.dev->dev_private; struct i915_vma *vma; @@ -3913,15 +4047,13 @@ i915_gem_object_pin(struct drm_i915_gem_object *obj, if (WARN_ON(vma->pin_count == DRM_I915_GEM_OBJECT_MAX_PIN_COUNT)) return -EBUSY; - if ((alignment && - vma->node.start & (alignment - 1)) || - (flags & PIN_MAPPABLE && !obj->map_and_fenceable)) { + if (i915_vma_misplaced(vma, alignment, flags)) { WARN(vma->pin_count, "bo is already pinned with incorrect alignment:" " offset=%lx, req.alignment=%x, req.map_and_fenceable=%d," " obj->map_and_fenceable=%d\n", i915_gem_obj_offset(obj, vm), alignment, - flags & PIN_MAPPABLE, + !!(flags & PIN_MAPPABLE), obj->map_and_fenceable); ret = i915_vma_unbind(vma); if (ret) @@ -4281,9 +4413,6 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj) trace_i915_gem_object_destroy(obj); - if (obj->phys_obj) - i915_gem_detach_phys_object(dev, obj); - list_for_each_entry_safe(vma, next, &obj->vma_list, vma_link) { int ret; @@ -4301,6 +4430,8 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj) } } + i915_gem_object_detach_phys(obj); + /* Stolen objects don't hold a ref, but do hold pin count. Fix that up * before progressing. */ if (obj->stolen) @@ -4756,7 +4887,7 @@ i915_gem_load(struct drm_device *dev) init_waitqueue_head(&dev_priv->gpu_error.reset_queue); /* On GEN3 we really need to make sure the ARB C3 LP bit is set */ - if (IS_GEN3(dev)) { + if (!drm_core_check_feature(dev, DRIVER_MODESET) && IS_GEN3(dev)) { I915_WRITE(MI_ARB_STATE, _MASKED_BIT_ENABLE(MI_ARB_C3_LP_WRITE_ENABLE)); } @@ -4792,190 +4923,6 @@ i915_gem_load(struct drm_device *dev) register_oom_notifier(&dev_priv->mm.oom_notifier); } -/* - * Create a physically contiguous memory object for this object - * e.g. for cursor + overlay regs - */ -static int i915_gem_init_phys_object(struct drm_device *dev, - int id, int size, int align) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_i915_gem_phys_object *phys_obj; - int ret; - - if (dev_priv->mm.phys_objs[id - 1] || !size) - return 0; - - phys_obj = kzalloc(sizeof(*phys_obj), GFP_KERNEL); - if (!phys_obj) - return -ENOMEM; - - phys_obj->id = id; - - phys_obj->handle = drm_pci_alloc(dev, size, align); - if (!phys_obj->handle) { - ret = -ENOMEM; - goto kfree_obj; - } -#ifdef CONFIG_X86 - set_memory_wc((unsigned long)phys_obj->handle->vaddr, phys_obj->handle->size / PAGE_SIZE); -#endif - - dev_priv->mm.phys_objs[id - 1] = phys_obj; - - return 0; -kfree_obj: - kfree(phys_obj); - return ret; -} - -static void i915_gem_free_phys_object(struct drm_device *dev, int id) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_i915_gem_phys_object *phys_obj; - - if (!dev_priv->mm.phys_objs[id - 1]) - return; - - phys_obj = dev_priv->mm.phys_objs[id - 1]; - if (phys_obj->cur_obj) { - i915_gem_detach_phys_object(dev, phys_obj->cur_obj); - } - -#ifdef CONFIG_X86 - set_memory_wb((unsigned long)phys_obj->handle->vaddr, phys_obj->handle->size / PAGE_SIZE); -#endif - drm_pci_free(dev, phys_obj->handle); - kfree(phys_obj); - dev_priv->mm.phys_objs[id - 1] = NULL; -} - -void i915_gem_free_all_phys_object(struct drm_device *dev) -{ - int i; - - for (i = I915_GEM_PHYS_CURSOR_0; i <= I915_MAX_PHYS_OBJECT; i++) - i915_gem_free_phys_object(dev, i); -} - -void i915_gem_detach_phys_object(struct drm_device *dev, - struct drm_i915_gem_object *obj) -{ - struct address_space *mapping = file_inode(obj->base.filp)->i_mapping; - char *vaddr; - int i; - int page_count; - - if (!obj->phys_obj) - return; - vaddr = obj->phys_obj->handle->vaddr; - - page_count = obj->base.size / PAGE_SIZE; - for (i = 0; i < page_count; i++) { - struct page *page = shmem_read_mapping_page(mapping, i); - if (!IS_ERR(page)) { - char *dst = kmap_atomic(page); - memcpy(dst, vaddr + i*PAGE_SIZE, PAGE_SIZE); - kunmap_atomic(dst); - - drm_clflush_pages(&page, 1); - - set_page_dirty(page); - mark_page_accessed(page); - page_cache_release(page); - } - } - i915_gem_chipset_flush(dev); - - obj->phys_obj->cur_obj = NULL; - obj->phys_obj = NULL; -} - -int -i915_gem_attach_phys_object(struct drm_device *dev, - struct drm_i915_gem_object *obj, - int id, - int align) -{ - struct address_space *mapping = file_inode(obj->base.filp)->i_mapping; - struct drm_i915_private *dev_priv = dev->dev_private; - int ret = 0; - int page_count; - int i; - - if (id > I915_MAX_PHYS_OBJECT) - return -EINVAL; - - if (obj->phys_obj) { - if (obj->phys_obj->id == id) - return 0; - i915_gem_detach_phys_object(dev, obj); - } - - /* create a new object */ - if (!dev_priv->mm.phys_objs[id - 1]) { - ret = i915_gem_init_phys_object(dev, id, - obj->base.size, align); - if (ret) { - DRM_ERROR("failed to init phys object %d size: %zu\n", - id, obj->base.size); - return ret; - } - } - - /* bind to the object */ - obj->phys_obj = dev_priv->mm.phys_objs[id - 1]; - obj->phys_obj->cur_obj = obj; - - page_count = obj->base.size / PAGE_SIZE; - - for (i = 0; i < page_count; i++) { - struct page *page; - char *dst, *src; - - page = shmem_read_mapping_page(mapping, i); - if (IS_ERR(page)) - return PTR_ERR(page); - - src = kmap_atomic(page); - dst = obj->phys_obj->handle->vaddr + (i * PAGE_SIZE); - memcpy(dst, src, PAGE_SIZE); - kunmap_atomic(src); - - mark_page_accessed(page); - page_cache_release(page); - } - - return 0; -} - -static int -i915_gem_phys_pwrite(struct drm_device *dev, - struct drm_i915_gem_object *obj, - struct drm_i915_gem_pwrite *args, - struct drm_file *file_priv) -{ - void *vaddr = obj->phys_obj->handle->vaddr + args->offset; - char __user *user_data = to_user_ptr(args->data_ptr); - - if (__copy_from_user_inatomic_nocache(vaddr, user_data, args->size)) { - unsigned long unwritten; - - /* The physical object once assigned is fixed for the lifetime - * of the obj, so we can safely drop the lock and continue - * to access vaddr. - */ - mutex_unlock(&dev->struct_mutex); - unwritten = copy_from_user(vaddr, user_data, args->size); - mutex_lock(&dev->struct_mutex); - if (unwritten) - return -EFAULT; - } - - i915_gem_chipset_flush(dev); - return 0; -} - void i915_gem_release(struct drm_device *dev, struct drm_file *file) { struct drm_i915_file_private *file_priv = file->driver_priv; diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 3ffe308d589..a5ddf3bce9c 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -598,6 +598,7 @@ static int do_switch(struct intel_engine_cs *ring, struct intel_context *from = ring->last_context; struct i915_hw_ppgtt *ppgtt = ctx_to_ppgtt(to); u32 hw_flags = 0; + bool uninitialized = false; int ret, i; if (from != NULL && ring == &dev_priv->ring[RCS]) { @@ -696,19 +697,20 @@ static int do_switch(struct intel_engine_cs *ring, i915_gem_context_unreference(from); } + uninitialized = !to->is_initialized && from == NULL; + to->is_initialized = true; + done: i915_gem_context_reference(to); ring->last_context = to; to->last_ring = ring; - if (ring->id == RCS && !to->is_initialized && from == NULL) { + if (uninitialized) { ret = i915_gem_render_state_init(ring); if (ret) DRM_ERROR("init render state: %d\n", ret); } - to->is_initialized = true; - return 0; unpin_out: diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c index 75fca63dc8c..bbf4b12d842 100644 --- a/drivers/gpu/drm/i915/i915_gem_evict.c +++ b/drivers/gpu/drm/i915/i915_gem_evict.c @@ -68,9 +68,9 @@ mark_free(struct i915_vma *vma, struct list_head *unwind) int i915_gem_evict_something(struct drm_device *dev, struct i915_address_space *vm, int min_size, unsigned alignment, unsigned cache_level, + unsigned long start, unsigned long end, unsigned flags) { - struct drm_i915_private *dev_priv = dev->dev_private; struct list_head eviction_list, unwind_list; struct i915_vma *vma; int ret = 0; @@ -102,11 +102,10 @@ i915_gem_evict_something(struct drm_device *dev, struct i915_address_space *vm, */ INIT_LIST_HEAD(&unwind_list); - if (flags & PIN_MAPPABLE) { - BUG_ON(!i915_is_ggtt(vm)); + if (start != 0 || end != vm->total) { drm_mm_init_scan_with_range(&vm->mm, min_size, - alignment, cache_level, 0, - dev_priv->gtt.mappable_end); + alignment, cache_level, + start, end); } else drm_mm_init_scan(&vm->mm, min_size, alignment, cache_level); diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 008e208e9a3..3a30133f93e 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -35,6 +35,9 @@ #define __EXEC_OBJECT_HAS_PIN (1<<31) #define __EXEC_OBJECT_HAS_FENCE (1<<30) +#define __EXEC_OBJECT_NEEDS_BIAS (1<<28) + +#define BATCH_OFFSET_BIAS (256*1024) struct eb_vmas { struct list_head vmas; @@ -548,7 +551,7 @@ i915_gem_execbuffer_reserve_vma(struct i915_vma *vma, struct drm_i915_gem_exec_object2 *entry = vma->exec_entry; bool has_fenced_gpu_access = INTEL_INFO(ring->dev)->gen < 4; bool need_fence; - unsigned flags; + uint64_t flags; int ret; flags = 0; @@ -562,6 +565,8 @@ i915_gem_execbuffer_reserve_vma(struct i915_vma *vma, if (entry->flags & EXEC_OBJECT_NEEDS_GTT) flags |= PIN_GLOBAL; + if (entry->flags & __EXEC_OBJECT_NEEDS_BIAS) + flags |= BATCH_OFFSET_BIAS | PIN_OFFSET_BIAS; ret = i915_gem_object_pin(obj, vma->vm, entry->alignment, flags); if (ret) @@ -595,6 +600,36 @@ i915_gem_execbuffer_reserve_vma(struct i915_vma *vma, return 0; } +static bool +eb_vma_misplaced(struct i915_vma *vma, bool has_fenced_gpu_access) +{ + struct drm_i915_gem_exec_object2 *entry = vma->exec_entry; + struct drm_i915_gem_object *obj = vma->obj; + bool need_fence, need_mappable; + + need_fence = + has_fenced_gpu_access && + entry->flags & EXEC_OBJECT_NEEDS_FENCE && + obj->tiling_mode != I915_TILING_NONE; + need_mappable = need_fence || need_reloc_mappable(vma); + + WARN_ON((need_mappable || need_fence) && + !i915_is_ggtt(vma->vm)); + + if (entry->alignment && + vma->node.start & (entry->alignment - 1)) + return true; + + if (need_mappable && !obj->map_and_fenceable) + return true; + + if (entry->flags & __EXEC_OBJECT_NEEDS_BIAS && + vma->node.start < BATCH_OFFSET_BIAS) + return true; + + return false; +} + static int i915_gem_execbuffer_reserve(struct intel_engine_cs *ring, struct list_head *vmas, @@ -658,26 +693,10 @@ i915_gem_execbuffer_reserve(struct intel_engine_cs *ring, /* Unbind any ill-fitting objects or pin. */ list_for_each_entry(vma, vmas, exec_list) { - struct drm_i915_gem_exec_object2 *entry = vma->exec_entry; - bool need_fence, need_mappable; - - obj = vma->obj; - if (!drm_mm_node_allocated(&vma->node)) continue; - need_fence = - has_fenced_gpu_access && - entry->flags & EXEC_OBJECT_NEEDS_FENCE && - obj->tiling_mode != I915_TILING_NONE; - need_mappable = need_fence || need_reloc_mappable(vma); - - WARN_ON((need_mappable || need_fence) && - !i915_is_ggtt(vma->vm)); - - if ((entry->alignment && - vma->node.start & (entry->alignment - 1)) || - (need_mappable && !obj->map_and_fenceable)) + if (eb_vma_misplaced(vma, has_fenced_gpu_access)) ret = i915_vma_unbind(vma); else ret = i915_gem_execbuffer_reserve_vma(vma, ring, need_relocs); @@ -778,9 +797,9 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev, * relocations were valid. */ for (j = 0; j < exec[i].relocation_count; j++) { - if (copy_to_user(&user_relocs[j].presumed_offset, - &invalid_offset, - sizeof(invalid_offset))) { + if (__copy_to_user(&user_relocs[j].presumed_offset, + &invalid_offset, + sizeof(invalid_offset))) { ret = -EFAULT; mutex_lock(&dev->struct_mutex); goto err; @@ -1040,6 +1059,25 @@ static int gen8_dispatch_bsd_ring(struct drm_device *dev, } } +static struct drm_i915_gem_object * +eb_get_batch(struct eb_vmas *eb) +{ + struct i915_vma *vma = list_entry(eb->vmas.prev, typeof(*vma), exec_list); + + /* + * SNA is doing fancy tricks with compressing batch buffers, which leads + * to negative relocation deltas. Usually that works out ok since the + * relocate address is still positive, except when the batch is placed + * very low in the GTT. Ensure this doesn't happen. + * + * Note that actual hangs have only been observed on gen7, but for + * paranoia do it everywhere. + */ + vma->exec_entry->flags |= __EXEC_OBJECT_NEEDS_BIAS; + + return vma->obj; +} + static int i915_gem_do_execbuffer(struct drm_device *dev, void *data, struct drm_file *file, @@ -1220,7 +1258,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, goto err; /* take note of the batch buffer before we might reorder the lists */ - batch_obj = list_entry(eb->vmas.prev, struct i915_vma, exec_list)->obj; + batch_obj = eb_get_batch(eb); /* Move the objects en-masse into the GTT, evicting if necessary. */ need_relocs = (args->flags & I915_EXEC_NO_RELOC) == 0; @@ -1422,18 +1460,21 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, ret = i915_gem_do_execbuffer(dev, data, file, &exec2, exec2_list); if (!ret) { + struct drm_i915_gem_exec_object __user *user_exec_list = + to_user_ptr(args->buffers_ptr); + /* Copy the new buffer offsets back to the user's exec list. */ - for (i = 0; i < args->buffer_count; i++) - exec_list[i].offset = exec2_list[i].offset; - /* ... and back out to userspace */ - ret = copy_to_user(to_user_ptr(args->buffers_ptr), - exec_list, - sizeof(*exec_list) * args->buffer_count); - if (ret) { - ret = -EFAULT; - DRM_DEBUG("failed to copy %d exec entries " - "back to user (%d)\n", - args->buffer_count, ret); + for (i = 0; i < args->buffer_count; i++) { + ret = __copy_to_user(&user_exec_list[i].offset, + &exec2_list[i].offset, + sizeof(user_exec_list[i].offset)); + if (ret) { + ret = -EFAULT; + DRM_DEBUG("failed to copy %d exec entries " + "back to user (%d)\n", + args->buffer_count, ret); + break; + } } } @@ -1484,14 +1525,21 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data, ret = i915_gem_do_execbuffer(dev, data, file, args, exec2_list); if (!ret) { /* Copy the new buffer offsets back to the user's exec list. */ - ret = copy_to_user(to_user_ptr(args->buffers_ptr), - exec2_list, - sizeof(*exec2_list) * args->buffer_count); - if (ret) { - ret = -EFAULT; - DRM_DEBUG("failed to copy %d exec entries " - "back to user (%d)\n", - args->buffer_count, ret); + struct drm_i915_gem_exec_object2 *user_exec_list = + to_user_ptr(args->buffers_ptr); + int i; + + for (i = 0; i < args->buffer_count; i++) { + ret = __copy_to_user(&user_exec_list[i].offset, + &exec2_list[i].offset, + sizeof(user_exec_list[i].offset)); + if (ret) { + ret = -EFAULT; + DRM_DEBUG("failed to copy %d exec entries " + "back to user\n", + args->buffer_count); + break; + } } } diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 94916362b61..8b3cde70336 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -35,25 +35,35 @@ static void chv_setup_private_ppat(struct drm_i915_private *dev_priv); bool intel_enable_ppgtt(struct drm_device *dev, bool full) { - if (i915.enable_ppgtt == 0 || !HAS_ALIASING_PPGTT(dev)) + if (i915.enable_ppgtt == 0) return false; if (i915.enable_ppgtt == 1 && full) return false; + return true; +} + +static int sanitize_enable_ppgtt(struct drm_device *dev, int enable_ppgtt) +{ + if (enable_ppgtt == 0 || !HAS_ALIASING_PPGTT(dev)) + return 0; + + if (enable_ppgtt == 1) + return 1; + + if (enable_ppgtt == 2 && HAS_PPGTT(dev)) + return 2; + #ifdef CONFIG_INTEL_IOMMU /* Disable ppgtt on SNB if VT-d is on. */ if (INTEL_INFO(dev)->gen == 6 && intel_iommu_gfx_mapped) { DRM_INFO("Disabling PPGTT because VT-d is on\n"); - return false; + return 0; } #endif - /* Full ppgtt disabled by default for now due to issues. */ - if (full) - return HAS_PPGTT(dev) && (i915.enable_ppgtt == 2); - else - return HAS_ALIASING_PPGTT(dev); + return HAS_ALIASING_PPGTT(dev) ? 1 : 0; } @@ -1039,7 +1049,9 @@ alloc: if (ret == -ENOSPC && !retried) { ret = i915_gem_evict_something(dev, &dev_priv->gtt.base, GEN6_PD_SIZE, GEN6_PD_ALIGN, - I915_CACHE_NONE, 0); + I915_CACHE_NONE, + 0, dev_priv->gtt.base.total, + 0); if (ret) return ret; @@ -1763,6 +1775,13 @@ static inline unsigned int gen8_get_total_gtt_size(u16 bdw_gmch_ctl) bdw_gmch_ctl &= BDW_GMCH_GGMS_MASK; if (bdw_gmch_ctl) bdw_gmch_ctl = 1 << bdw_gmch_ctl; + +#ifdef CONFIG_X86_32 + /* Limit 32b platforms to a 2GB GGTT: 4 << 20 / pte size * PAGE_SIZE */ + if (bdw_gmch_ctl > 4) + bdw_gmch_ctl = 4; +#endif + return bdw_gmch_ctl << 20; } @@ -1973,7 +1992,10 @@ static void gen6_gmch_remove(struct i915_address_space *vm) struct i915_gtt *gtt = container_of(vm, struct i915_gtt, base); - drm_mm_takedown(&vm->mm); + if (drm_mm_initialized(&vm->mm)) { + drm_mm_takedown(&vm->mm); + list_del(&vm->global_link); + } iounmap(gtt->gsm); teardown_scratch_page(vm->dev); } @@ -2006,6 +2028,10 @@ static int i915_gmch_probe(struct drm_device *dev, static void i915_gmch_remove(struct i915_address_space *vm) { + if (drm_mm_initialized(&vm->mm)) { + drm_mm_takedown(&vm->mm); + list_del(&vm->global_link); + } intel_gmch_remove(); } @@ -2052,6 +2078,14 @@ int i915_gem_gtt_init(struct drm_device *dev) if (intel_iommu_gfx_mapped) DRM_INFO("VT-d active for gfx access\n"); #endif + /* + * i915.enable_ppgtt is read-only, so do an early pass to validate the + * user's requested state against the hardware/driver capabilities. We + * do this now so that we can print out any log messages once rather + * than every time we check intel_enable_ppgtt(). + */ + i915.enable_ppgtt = sanitize_enable_ppgtt(dev, i915.enable_ppgtt); + DRM_DEBUG_DRIVER("ppgtt mode: %i\n", i915.enable_ppgtt); return 0; } diff --git a/drivers/gpu/drm/i915/i915_gem_render_state.c b/drivers/gpu/drm/i915/i915_gem_render_state.c index 3521f998a17..34894b57306 100644 --- a/drivers/gpu/drm/i915/i915_gem_render_state.c +++ b/drivers/gpu/drm/i915/i915_gem_render_state.c @@ -31,7 +31,7 @@ struct i915_render_state { struct drm_i915_gem_object *obj; unsigned long ggtt_offset; - void *batch; + u32 *batch; u32 size; u32 len; }; @@ -80,7 +80,7 @@ free: static void render_state_free(struct i915_render_state *so) { - kunmap(so->batch); + kunmap(kmap_to_page(so->batch)); i915_gem_object_ggtt_unpin(so->obj); drm_gem_object_unreference(&so->obj->base); kfree(so); diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index 62ef55ba061..7465ab0fd39 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -74,6 +74,50 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev) if (base == 0) return 0; + /* make sure we don't clobber the GTT if it's within stolen memory */ + if (INTEL_INFO(dev)->gen <= 4 && !IS_G33(dev) && !IS_G4X(dev)) { + struct { + u32 start, end; + } stolen[2] = { + { .start = base, .end = base + dev_priv->gtt.stolen_size, }, + { .start = base, .end = base + dev_priv->gtt.stolen_size, }, + }; + u64 gtt_start, gtt_end; + + gtt_start = I915_READ(PGTBL_CTL); + if (IS_GEN4(dev)) + gtt_start = (gtt_start & PGTBL_ADDRESS_LO_MASK) | + (gtt_start & PGTBL_ADDRESS_HI_MASK) << 28; + else + gtt_start &= PGTBL_ADDRESS_LO_MASK; + gtt_end = gtt_start + gtt_total_entries(dev_priv->gtt) * 4; + + if (gtt_start >= stolen[0].start && gtt_start < stolen[0].end) + stolen[0].end = gtt_start; + if (gtt_end > stolen[1].start && gtt_end <= stolen[1].end) + stolen[1].start = gtt_end; + + /* pick the larger of the two chunks */ + if (stolen[0].end - stolen[0].start > + stolen[1].end - stolen[1].start) { + base = stolen[0].start; + dev_priv->gtt.stolen_size = stolen[0].end - stolen[0].start; + } else { + base = stolen[1].start; + dev_priv->gtt.stolen_size = stolen[1].end - stolen[1].start; + } + + if (stolen[0].start != stolen[1].start || + stolen[0].end != stolen[1].end) { + DRM_DEBUG_KMS("GTT within stolen memory at 0x%llx-0x%llx\n", + (unsigned long long) gtt_start, + (unsigned long long) gtt_end - 1); + DRM_DEBUG_KMS("Stolen memory adjusted to 0x%x-0x%x\n", + base, base + (u32) dev_priv->gtt.stolen_size - 1); + } + } + + /* Verify that nothing else uses this physical address. Stolen * memory should be reserved by the BIOS and hidden from the * kernel. So if the region is already marked as busy, something diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c index 87ec60e181a..66cf41765bf 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.c +++ b/drivers/gpu/drm/i915/i915_gpu_error.c @@ -888,6 +888,8 @@ static void i915_gem_record_rings(struct drm_device *dev, for (i = 0; i < I915_NUM_RINGS; i++) { struct intel_engine_cs *ring = &dev_priv->ring[i]; + error->ring[i].pid = -1; + if (ring->dev == NULL) continue; @@ -895,7 +897,6 @@ static void i915_gem_record_rings(struct drm_device *dev, i915_record_ring_state(dev, ring, &error->ring[i]); - error->ring[i].pid = -1; request = i915_gem_find_active_request(ring); if (request) { /* We need to copy these to an anonymous buffer diff --git a/drivers/gpu/drm/i915/i915_ioc32.c b/drivers/gpu/drm/i915/i915_ioc32.c index 3c59584161c..2e0613e2625 100644 --- a/drivers/gpu/drm/i915/i915_ioc32.c +++ b/drivers/gpu/drm/i915/i915_ioc32.c @@ -208,7 +208,7 @@ long i915_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (nr < DRM_COMMAND_BASE) return drm_compat_ioctl(filp, cmd, arg); - if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(i915_compat_ioctls)) + if (nr < DRM_COMMAND_BASE + ARRAY_SIZE(i915_compat_ioctls)) fn = i915_compat_ioctls[nr - DRM_COMMAND_BASE]; if (fn != NULL) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index cbf31cbfa08..c05c84f3f09 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -335,7 +335,8 @@ void i9xx_check_fifo_underruns(struct drm_device *dev) } static void i9xx_set_fifo_underrun_reporting(struct drm_device *dev, - enum pipe pipe, bool enable) + enum pipe pipe, + bool enable, bool old) { struct drm_i915_private *dev_priv = dev->dev_private; u32 reg = PIPESTAT(pipe); @@ -347,7 +348,7 @@ static void i9xx_set_fifo_underrun_reporting(struct drm_device *dev, I915_WRITE(reg, pipestat | PIPE_FIFO_UNDERRUN_STATUS); POSTING_READ(reg); } else { - if (pipestat & PIPE_FIFO_UNDERRUN_STATUS) + if (old && pipestat & PIPE_FIFO_UNDERRUN_STATUS) DRM_ERROR("pipe %c underrun\n", pipe_name(pipe)); } } @@ -366,7 +367,8 @@ static void ironlake_set_fifo_underrun_reporting(struct drm_device *dev, } static void ivybridge_set_fifo_underrun_reporting(struct drm_device *dev, - enum pipe pipe, bool enable) + enum pipe pipe, + bool enable, bool old) { struct drm_i915_private *dev_priv = dev->dev_private; if (enable) { @@ -379,7 +381,8 @@ static void ivybridge_set_fifo_underrun_reporting(struct drm_device *dev, } else { ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB); - if (I915_READ(GEN7_ERR_INT) & ERR_INT_FIFO_UNDERRUN(pipe)) { + if (old && + I915_READ(GEN7_ERR_INT) & ERR_INT_FIFO_UNDERRUN(pipe)) { DRM_ERROR("uncleared fifo underrun on pipe %c\n", pipe_name(pipe)); } @@ -444,7 +447,7 @@ static void ibx_set_fifo_underrun_reporting(struct drm_device *dev, static void cpt_set_fifo_underrun_reporting(struct drm_device *dev, enum transcoder pch_transcoder, - bool enable) + bool enable, bool old) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -459,7 +462,8 @@ static void cpt_set_fifo_underrun_reporting(struct drm_device *dev, } else { ibx_disable_display_interrupt(dev_priv, SDE_ERROR_CPT); - if (I915_READ(SERR_INT) & SERR_INT_TRANS_FIFO_UNDERRUN(pch_transcoder)) { + if (old && I915_READ(SERR_INT) & + SERR_INT_TRANS_FIFO_UNDERRUN(pch_transcoder)) { DRM_ERROR("uncleared pch fifo underrun on pch transcoder %c\n", transcoder_name(pch_transcoder)); } @@ -486,28 +490,23 @@ static bool __intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev, struct drm_i915_private *dev_priv = dev->dev_private; struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - bool ret; + bool old; assert_spin_locked(&dev_priv->irq_lock); - ret = !intel_crtc->cpu_fifo_underrun_disabled; - - if (enable == ret) - goto done; - + old = !intel_crtc->cpu_fifo_underrun_disabled; intel_crtc->cpu_fifo_underrun_disabled = !enable; if (INTEL_INFO(dev)->gen < 5 || IS_VALLEYVIEW(dev)) - i9xx_set_fifo_underrun_reporting(dev, pipe, enable); + i9xx_set_fifo_underrun_reporting(dev, pipe, enable, old); else if (IS_GEN5(dev) || IS_GEN6(dev)) ironlake_set_fifo_underrun_reporting(dev, pipe, enable); else if (IS_GEN7(dev)) - ivybridge_set_fifo_underrun_reporting(dev, pipe, enable); + ivybridge_set_fifo_underrun_reporting(dev, pipe, enable, old); else if (IS_GEN8(dev)) broadwell_set_fifo_underrun_reporting(dev, pipe, enable); -done: - return ret; + return old; } bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev, @@ -556,7 +555,7 @@ bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev, struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pch_transcoder]; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); unsigned long flags; - bool ret; + bool old; /* * NOTE: Pre-LPT has a fixed cpu pipe -> pch transcoder mapping, but LPT @@ -569,21 +568,16 @@ bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev, spin_lock_irqsave(&dev_priv->irq_lock, flags); - ret = !intel_crtc->pch_fifo_underrun_disabled; - - if (enable == ret) - goto done; - + old = !intel_crtc->pch_fifo_underrun_disabled; intel_crtc->pch_fifo_underrun_disabled = !enable; if (HAS_PCH_IBX(dev)) ibx_set_fifo_underrun_reporting(dev, pch_transcoder, enable); else - cpt_set_fifo_underrun_reporting(dev, pch_transcoder, enable); + cpt_set_fifo_underrun_reporting(dev, pch_transcoder, enable, old); -done: spin_unlock_irqrestore(&dev_priv->irq_lock, flags); - return ret; + return old; } @@ -2408,7 +2402,7 @@ static void i915_error_work_func(struct work_struct *work) * updates before * the counter increment. */ - smp_mb__before_atomic_inc(); + smp_mb__before_atomic(); atomic_inc(&dev_priv->gpu_error.reset_counter); kobject_uevent_env(&dev->primary->kdev->kobj, @@ -2634,10 +2628,6 @@ static int i915_enable_vblank(struct drm_device *dev, int pipe) else i915_enable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_STATUS); - - /* maintain vblank delivery even in deep C-states */ - if (INTEL_INFO(dev)->gen == 3) - I915_WRITE(INSTPM, _MASKED_BIT_DISABLE(INSTPM_AGPBUSY_DIS)); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); return 0; @@ -2701,9 +2691,6 @@ static void i915_disable_vblank(struct drm_device *dev, int pipe) unsigned long irqflags; spin_lock_irqsave(&dev_priv->irq_lock, irqflags); - if (INTEL_INFO(dev)->gen == 3) - I915_WRITE(INSTPM, _MASKED_BIT_ENABLE(INSTPM_AGPBUSY_DIS)); - i915_disable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_STATUS | PIPE_START_VBLANK_INTERRUPT_STATUS); @@ -2858,20 +2845,27 @@ static int semaphore_passed(struct intel_engine_cs *ring) { struct drm_i915_private *dev_priv = ring->dev->dev_private; struct intel_engine_cs *signaller; - u32 seqno, ctl; + u32 seqno; - ring->hangcheck.deadlock = true; + ring->hangcheck.deadlock++; signaller = semaphore_waits_for(ring, &seqno); - if (signaller == NULL || signaller->hangcheck.deadlock) + if (signaller == NULL) return -1; + /* Prevent pathological recursion due to driver bugs */ + if (signaller->hangcheck.deadlock >= I915_NUM_RINGS) + return -1; + + if (i915_seqno_passed(signaller->get_seqno(signaller, false), seqno)) + return 1; + /* cursory check for an unkickable deadlock */ - ctl = I915_READ_CTL(signaller); - if (ctl & RING_WAIT_SEMAPHORE && semaphore_passed(signaller) < 0) + if (I915_READ_CTL(signaller) & RING_WAIT_SEMAPHORE && + semaphore_passed(signaller) < 0) return -1; - return i915_seqno_passed(signaller->get_seqno(signaller, false), seqno); + return 0; } static void semaphore_clear_deadlocks(struct drm_i915_private *dev_priv) @@ -2880,7 +2874,7 @@ static void semaphore_clear_deadlocks(struct drm_i915_private *dev_priv) int i; for_each_ring(ring, dev_priv, i) - ring->hangcheck.deadlock = false; + ring->hangcheck.deadlock = 0; } static enum intel_ring_hangcheck_action @@ -3117,11 +3111,6 @@ static void ironlake_irq_reset(struct drm_device *dev) ibx_irq_reset(dev); } -static void ironlake_irq_preinstall(struct drm_device *dev) -{ - ironlake_irq_reset(dev); -} - static void valleyview_irq_preinstall(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -3151,6 +3140,14 @@ static void valleyview_irq_preinstall(struct drm_device *dev) POSTING_READ(VLV_IER); } +static void gen8_gt_irq_reset(struct drm_i915_private *dev_priv) +{ + GEN8_IRQ_RESET_NDX(GT, 0); + GEN8_IRQ_RESET_NDX(GT, 1); + GEN8_IRQ_RESET_NDX(GT, 2); + GEN8_IRQ_RESET_NDX(GT, 3); +} + static void gen8_irq_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -3159,10 +3156,7 @@ static void gen8_irq_reset(struct drm_device *dev) I915_WRITE(GEN8_MASTER_IRQ, 0); POSTING_READ(GEN8_MASTER_IRQ); - GEN8_IRQ_RESET_NDX(GT, 0); - GEN8_IRQ_RESET_NDX(GT, 1); - GEN8_IRQ_RESET_NDX(GT, 2); - GEN8_IRQ_RESET_NDX(GT, 3); + gen8_gt_irq_reset(dev_priv); for_each_pipe(pipe) GEN8_IRQ_RESET_NDX(DE_PIPE, pipe); @@ -3174,11 +3168,6 @@ static void gen8_irq_reset(struct drm_device *dev) ibx_irq_reset(dev); } -static void gen8_irq_preinstall(struct drm_device *dev) -{ - gen8_irq_reset(dev); -} - static void cherryview_irq_preinstall(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -3187,10 +3176,7 @@ static void cherryview_irq_preinstall(struct drm_device *dev) I915_WRITE(GEN8_MASTER_IRQ, 0); POSTING_READ(GEN8_MASTER_IRQ); - GEN8_IRQ_RESET_NDX(GT, 0); - GEN8_IRQ_RESET_NDX(GT, 1); - GEN8_IRQ_RESET_NDX(GT, 2); - GEN8_IRQ_RESET_NDX(GT, 3); + gen8_gt_irq_reset(dev_priv); GEN5_IRQ_RESET(GEN8_PCU_); @@ -4387,7 +4373,7 @@ void intel_irq_init(struct drm_device *dev) dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup; } else if (IS_GEN8(dev)) { dev->driver->irq_handler = gen8_irq_handler; - dev->driver->irq_preinstall = gen8_irq_preinstall; + dev->driver->irq_preinstall = gen8_irq_reset; dev->driver->irq_postinstall = gen8_irq_postinstall; dev->driver->irq_uninstall = gen8_irq_uninstall; dev->driver->enable_vblank = gen8_enable_vblank; @@ -4395,7 +4381,7 @@ void intel_irq_init(struct drm_device *dev) dev_priv->display.hpd_irq_setup = ibx_hpd_irq_setup; } else if (HAS_PCH_SPLIT(dev)) { dev->driver->irq_handler = ironlake_irq_handler; - dev->driver->irq_preinstall = ironlake_irq_preinstall; + dev->driver->irq_preinstall = ironlake_irq_reset; dev->driver->irq_postinstall = ironlake_irq_postinstall; dev->driver->irq_uninstall = ironlake_irq_uninstall; dev->driver->enable_vblank = ironlake_enable_vblank; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 5122254e721..a5bab61bfc0 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -942,6 +942,9 @@ enum punit_power_well { /* * Instruction and interrupt control regs */ +#define PGTBL_CTL 0x02020 +#define PGTBL_ADDRESS_LO_MASK 0xfffff000 /* bits [31:12] */ +#define PGTBL_ADDRESS_HI_MASK 0x000000f0 /* bits [35:32] (gen4) */ #define PGTBL_ER 0x02024 #define RENDER_RING_BASE 0x02000 #define BSD_RING_BASE 0x04000 @@ -1179,7 +1182,7 @@ enum punit_power_well { #define I915_ERROR_INSTRUCTION (1<<0) #define INSTPM 0x020c0 #define INSTPM_SELF_EN (1<<12) /* 915GM only */ -#define INSTPM_AGPBUSY_DIS (1<<11) /* gen3: when disabled, pending interrupts +#define INSTPM_AGPBUSY_INT_EN (1<<11) /* gen3: when disabled, pending interrupts will not assert AGPBUSY# and will only be delivered when out of C3. */ #define INSTPM_FORCE_ORDERING (1<<7) /* GEN6+ */ @@ -1260,6 +1263,10 @@ enum punit_power_well { #define MI_ARB_DISPLAY_PRIORITY_A_B (0 << 0) /* display A > display B */ #define MI_ARB_DISPLAY_PRIORITY_B_A (1 << 0) /* display B > display A */ +#define MI_STATE 0x020e4 /* gen2 only */ +#define MI_AGPBUSY_INT_EN (1 << 1) /* 85x only */ +#define MI_AGPBUSY_830_MODE (1 << 0) /* 85x only */ + #define CACHE_MODE_0 0x02120 /* 915+ only */ #define CM0_PIPELINED_RENDER_FLUSH_DISABLE (1<<8) #define CM0_IZ_OPT_DISABLE (1<<6) diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index 2945f57c53e..827498e081d 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -315,9 +315,6 @@ parse_lfp_backlight(struct drm_i915_private *dev_priv, struct bdb_header *bdb) const struct bdb_lfp_backlight_data *backlight_data; const struct bdb_lfp_backlight_data_entry *entry; - /* Err to enabling backlight if no backlight block. */ - dev_priv->vbt.backlight.present = true; - backlight_data = find_section(bdb, BDB_LVDS_BACKLIGHT); if (!backlight_data) return; @@ -598,47 +595,71 @@ parse_edp(struct drm_i915_private *dev_priv, struct bdb_header *bdb) dev_priv->vbt.edp_pps = *edp_pps; - dev_priv->vbt.edp_rate = edp_link_params->rate ? DP_LINK_BW_2_7 : - DP_LINK_BW_1_62; + switch (edp_link_params->rate) { + case EDP_RATE_1_62: + dev_priv->vbt.edp_rate = DP_LINK_BW_1_62; + break; + case EDP_RATE_2_7: + dev_priv->vbt.edp_rate = DP_LINK_BW_2_7; + break; + default: + DRM_DEBUG_KMS("VBT has unknown eDP link rate value %u\n", + edp_link_params->rate); + break; + } + switch (edp_link_params->lanes) { - case 0: + case EDP_LANE_1: dev_priv->vbt.edp_lanes = 1; break; - case 1: + case EDP_LANE_2: dev_priv->vbt.edp_lanes = 2; break; - case 3: - default: + case EDP_LANE_4: dev_priv->vbt.edp_lanes = 4; break; + default: + DRM_DEBUG_KMS("VBT has unknown eDP lane count value %u\n", + edp_link_params->lanes); + break; } + switch (edp_link_params->preemphasis) { - case 0: + case EDP_PREEMPHASIS_NONE: dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_0; break; - case 1: + case EDP_PREEMPHASIS_3_5dB: dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_3_5; break; - case 2: + case EDP_PREEMPHASIS_6dB: dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_6; break; - case 3: + case EDP_PREEMPHASIS_9_5dB: dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_9_5; break; + default: + DRM_DEBUG_KMS("VBT has unknown eDP pre-emphasis value %u\n", + edp_link_params->preemphasis); + break; } + switch (edp_link_params->vswing) { - case 0: + case EDP_VSWING_0_4V: dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_400; break; - case 1: + case EDP_VSWING_0_6V: dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_600; break; - case 2: + case EDP_VSWING_0_8V: dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_800; break; - case 3: + case EDP_VSWING_1_2V: dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_1200; break; + default: + DRM_DEBUG_KMS("VBT has unknown eDP voltage swing value %u\n", + edp_link_params->vswing); + break; } } @@ -720,6 +741,10 @@ parse_mipi(struct drm_i915_private *dev_priv, struct bdb_header *bdb) int i, panel_id, seq_size; u16 block_size; + /* parse MIPI blocks only if LFP type is MIPI */ + if (!dev_priv->vbt.has_mipi) + return; + /* Initialize this to undefined indicating no generic MIPI support */ dev_priv->vbt.dsi.panel_id = MIPI_DSI_UNDEFINED_PANEL_ID; @@ -1035,6 +1060,15 @@ parse_device_mapping(struct drm_i915_private *dev_priv, /* skip the device block if device type is invalid */ continue; } + + if (p_child->common.dvo_port >= DVO_PORT_MIPIA + && p_child->common.dvo_port <= DVO_PORT_MIPID + &&p_child->common.device_type & DEVICE_TYPE_MIPI_OUTPUT) { + DRM_DEBUG_KMS("Found MIPI as LFP\n"); + dev_priv->vbt.has_mipi = 1; + dev_priv->vbt.dsi.port = p_child->common.dvo_port; + } + child_dev_ptr = dev_priv->vbt.child_dev + count; count++; memcpy((void *)child_dev_ptr, (void *)p_child, @@ -1051,6 +1085,9 @@ init_vbt_defaults(struct drm_i915_private *dev_priv) dev_priv->vbt.crt_ddc_pin = GMBUS_PORT_VGADDC; + /* Default to having backlight */ + dev_priv->vbt.backlight.present = true; + /* LFP panel data */ dev_priv->vbt.lvds_dither = 1; dev_priv->vbt.lvds_vbt = 0; diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h index 6009debebaa..b9866779633 100644 --- a/drivers/gpu/drm/i915/intel_bios.h +++ b/drivers/gpu/drm/i915/intel_bios.h @@ -743,6 +743,10 @@ int intel_parse_bios(struct drm_device *dev); #define DVO_PORT_DPC 8 #define DVO_PORT_DPD 9 #define DVO_PORT_DPA 10 +#define DVO_PORT_MIPIA 21 +#define DVO_PORT_MIPIB 22 +#define DVO_PORT_MIPIC 23 +#define DVO_PORT_MIPID 24 /* Block 52 contains MIPI Panel info * 6 such enteries will there. Index into correct diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 1fc91df5829..5a045d3bd77 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -630,6 +630,7 @@ intel_crt_detect(struct drm_connector *connector, bool force) enum intel_display_power_domain power_domain; enum drm_connector_status status; struct intel_load_detect_pipe tmp; + struct drm_modeset_acquire_ctx ctx; intel_runtime_pm_get(dev_priv); @@ -673,12 +674,12 @@ intel_crt_detect(struct drm_connector *connector, bool force) } /* for pre-945g platforms use load detect */ - if (intel_get_load_detect_pipe(connector, NULL, &tmp)) { + if (intel_get_load_detect_pipe(connector, NULL, &tmp, &ctx)) { if (intel_crt_detect_ddc(connector)) status = connector_status_connected; else status = intel_crt_load_detect(crt); - intel_release_load_detect_pipe(connector, &tmp); + intel_release_load_detect_pipe(connector, &tmp, &ctx); } else status = connector_status_unknown; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index c2976ade823..f0be855ddf4 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1484,14 +1484,6 @@ static void intel_reset_dpio(struct drm_device *dev) if (!IS_VALLEYVIEW(dev)) return; - /* - * Enable the CRI clock source so we can get at the display and the - * reference clock for VGA hotplug / manual detection. - */ - I915_WRITE(DPLL(PIPE_B), I915_READ(DPLL(PIPE_B)) | - DPLL_REFA_CLK_ENABLE_VLV | - DPLL_INTEGRATED_CRI_CLK_VLV); - if (IS_CHERRYVIEW(dev)) { enum dpio_phy phy; u32 val; @@ -1516,17 +1508,23 @@ static void intel_reset_dpio(struct drm_device *dev) } else { /* - * From VLV2A0_DP_eDP_DPIO_driver_vbios_notes_10.docx - - * 6. De-assert cmn_reset/side_reset. Same as VLV X0. - * a. GUnit 0x2110 bit[0] set to 1 (def 0) - * b. The other bits such as sfr settings / modesel may all - * be set to 0. - * - * This should only be done on init and resume from S3 with - * both PLLs disabled, or we risk losing DPIO and PLL - * synchronization. + * If DPIO has already been reset, e.g. by BIOS, just skip all + * this. */ - I915_WRITE(DPIO_CTL, I915_READ(DPIO_CTL) | DPIO_CMNRST); + if (I915_READ(DPIO_CTL) & DPIO_CMNRST) + return; + + /* + * From VLV2A0_DP_eDP_HDMI_DPIO_driver_vbios_notes_11.docx: + * Need to assert and de-assert PHY SB reset by gating the + * common lane power, then un-gating it. + * Simply ungating isn't enough to reset the PHY enough to get + * ports and lanes running. + */ + __vlv_set_power_well(dev_priv, PUNIT_POWER_WELL_DPIO_CMN_BC, + false); + __vlv_set_power_well(dev_priv, PUNIT_POWER_WELL_DPIO_CMN_BC, + true); } } @@ -2089,6 +2087,7 @@ void intel_flush_primary_plane(struct drm_i915_private *dev_priv, static void intel_enable_primary_hw_plane(struct drm_i915_private *dev_priv, enum plane plane, enum pipe pipe) { + struct drm_device *dev = dev_priv->dev; struct intel_crtc *intel_crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); int reg; @@ -2108,6 +2107,14 @@ static void intel_enable_primary_hw_plane(struct drm_i915_private *dev_priv, I915_WRITE(reg, val | DISPLAY_PLANE_ENABLE); intel_flush_primary_plane(dev_priv, plane); + + /* + * BDW signals flip done immediately if the plane + * is disabled, even if the plane enable is already + * armed to occur at the next vblank :( + */ + if (IS_BROADWELL(dev)) + intel_wait_for_vblank(dev, intel_crtc->pipe); } /** @@ -2576,7 +2583,7 @@ void intel_display_handle_reset(struct drm_device *dev) for_each_crtc(dev, crtc) { struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - mutex_lock(&crtc->mutex); + drm_modeset_lock(&crtc->mutex, NULL); /* * FIXME: Once we have proper support for primary planes (and * disabling them without disabling the entire crtc) allow again @@ -2587,7 +2594,7 @@ void intel_display_handle_reset(struct drm_device *dev) crtc->primary->fb, crtc->x, crtc->y); - mutex_unlock(&crtc->mutex); + drm_modeset_unlock(&crtc->mutex); } } @@ -4566,7 +4573,10 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc) if (intel_crtc->active) return; - vlv_prepare_pll(intel_crtc); + is_dsi = intel_pipe_has_type(crtc, INTEL_OUTPUT_DSI); + + if (!is_dsi && !IS_CHERRYVIEW(dev)) + vlv_prepare_pll(intel_crtc); /* Set up the display plane register */ dspcntr = DISPPLANE_GAMMA_ENABLE; @@ -4600,8 +4610,6 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc) if (encoder->pre_pll_enable) encoder->pre_pll_enable(encoder); - is_dsi = intel_pipe_has_type(crtc, INTEL_OUTPUT_DSI); - if (!is_dsi) { if (IS_CHERRYVIEW(dev)) chv_enable_pll(intel_crtc); @@ -7868,29 +7876,33 @@ static void i845_update_cursor(struct drm_crtc *crtc, u32 base) struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - bool visible = base != 0; - u32 cntl; + uint32_t cntl; - if (intel_crtc->cursor_visible == visible) - return; - - cntl = I915_READ(_CURACNTR); - if (visible) { + if (base != intel_crtc->cursor_base) { /* On these chipsets we can only modify the base whilst * the cursor is disabled. */ + if (intel_crtc->cursor_cntl) { + I915_WRITE(_CURACNTR, 0); + POSTING_READ(_CURACNTR); + intel_crtc->cursor_cntl = 0; + } + I915_WRITE(_CURABASE, base); + POSTING_READ(_CURABASE); + } - cntl &= ~(CURSOR_FORMAT_MASK); - /* XXX width must be 64, stride 256 => 0x00 << 28 */ - cntl |= CURSOR_ENABLE | + /* XXX width must be 64, stride 256 => 0x00 << 28 */ + cntl = 0; + if (base) + cntl = (CURSOR_ENABLE | CURSOR_GAMMA_ENABLE | - CURSOR_FORMAT_ARGB; - } else - cntl &= ~(CURSOR_ENABLE | CURSOR_GAMMA_ENABLE); - I915_WRITE(_CURACNTR, cntl); - - intel_crtc->cursor_visible = visible; + CURSOR_FORMAT_ARGB); + if (intel_crtc->cursor_cntl != cntl) { + I915_WRITE(_CURACNTR, cntl); + POSTING_READ(_CURACNTR); + intel_crtc->cursor_cntl = cntl; + } } static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base) @@ -7899,16 +7911,12 @@ static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_crtc->pipe; - bool visible = base != 0; + uint32_t cntl; - if (intel_crtc->cursor_visible != visible) { - int16_t width = intel_crtc->cursor_width; - uint32_t cntl = I915_READ(CURCNTR(pipe)); - if (base) { - cntl &= ~(CURSOR_MODE | MCURSOR_PIPE_SELECT); - cntl |= MCURSOR_GAMMA_ENABLE; - - switch (width) { + cntl = 0; + if (base) { + cntl = MCURSOR_GAMMA_ENABLE; + switch (intel_crtc->cursor_width) { case 64: cntl |= CURSOR_MODE_64_ARGB_AX; break; @@ -7921,18 +7929,16 @@ static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base) default: WARN_ON(1); return; - } - cntl |= pipe << 28; /* Connect to correct pipe */ - } else { - cntl &= ~(CURSOR_MODE | MCURSOR_GAMMA_ENABLE); - cntl |= CURSOR_MODE_DISABLE; } + cntl |= pipe << 28; /* Connect to correct pipe */ + } + if (intel_crtc->cursor_cntl != cntl) { I915_WRITE(CURCNTR(pipe), cntl); - - intel_crtc->cursor_visible = visible; + POSTING_READ(CURCNTR(pipe)); + intel_crtc->cursor_cntl = cntl; } + /* and commit changes on next vblank */ - POSTING_READ(CURCNTR(pipe)); I915_WRITE(CURBASE(pipe), base); POSTING_READ(CURBASE(pipe)); } @@ -7943,15 +7949,12 @@ static void ivb_update_cursor(struct drm_crtc *crtc, u32 base) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_crtc->pipe; - bool visible = base != 0; - - if (intel_crtc->cursor_visible != visible) { - int16_t width = intel_crtc->cursor_width; - uint32_t cntl = I915_READ(CURCNTR(pipe)); - if (base) { - cntl &= ~CURSOR_MODE; - cntl |= MCURSOR_GAMMA_ENABLE; - switch (width) { + uint32_t cntl; + + cntl = 0; + if (base) { + cntl = MCURSOR_GAMMA_ENABLE; + switch (intel_crtc->cursor_width) { case 64: cntl |= CURSOR_MODE_64_ARGB_AX; break; @@ -7964,21 +7967,18 @@ static void ivb_update_cursor(struct drm_crtc *crtc, u32 base) default: WARN_ON(1); return; - } - } else { - cntl &= ~(CURSOR_MODE | MCURSOR_GAMMA_ENABLE); - cntl |= CURSOR_MODE_DISABLE; - } - if (IS_HASWELL(dev) || IS_BROADWELL(dev)) { - cntl |= CURSOR_PIPE_CSC_ENABLE; - cntl &= ~CURSOR_TRICKLE_FEED_DISABLE; } - I915_WRITE(CURCNTR(pipe), cntl); + } + if (IS_HASWELL(dev) || IS_BROADWELL(dev)) + cntl |= CURSOR_PIPE_CSC_ENABLE; - intel_crtc->cursor_visible = visible; + if (intel_crtc->cursor_cntl != cntl) { + I915_WRITE(CURCNTR(pipe), cntl); + POSTING_READ(CURCNTR(pipe)); + intel_crtc->cursor_cntl = cntl; } + /* and commit changes on next vblank */ - POSTING_READ(CURCNTR(pipe)); I915_WRITE(CURBASE(pipe), base); POSTING_READ(CURBASE(pipe)); } @@ -7994,7 +7994,6 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc, int x = intel_crtc->cursor_x; int y = intel_crtc->cursor_y; u32 base = 0, pos = 0; - bool visible; if (on) base = intel_crtc->cursor_addr; @@ -8023,8 +8022,7 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc, } pos |= y << CURSOR_Y_SHIFT; - visible = base != 0; - if (!visible && !intel_crtc->cursor_visible) + if (base == 0 && intel_crtc->cursor_base == 0) return; I915_WRITE(CURPOS(pipe), pos); @@ -8035,6 +8033,7 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc, i845_update_cursor(crtc, base); else i9xx_update_cursor(crtc, base); + intel_crtc->cursor_base = base; } static int intel_crtc_cursor_set(struct drm_crtc *crtc, @@ -8112,14 +8111,12 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, addr = i915_gem_obj_ggtt_offset(obj); } else { int align = IS_I830(dev) ? 16 * 1024 : 256; - ret = i915_gem_attach_phys_object(dev, obj, - (intel_crtc->pipe == 0) ? I915_GEM_PHYS_CURSOR_0 : I915_GEM_PHYS_CURSOR_1, - align); + ret = i915_gem_object_attach_phys(obj, align); if (ret) { DRM_DEBUG_KMS("failed to attach phys object\n"); goto fail_locked; } - addr = obj->phys_obj->handle->busaddr; + addr = obj->phys_handle->busaddr; } if (IS_GEN2(dev)) @@ -8127,10 +8124,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, finish: if (intel_crtc->cursor_bo) { - if (INTEL_INFO(dev)->cursor_needs_physical) { - if (intel_crtc->cursor_bo != obj) - i915_gem_detach_phys_object(dev, intel_crtc->cursor_bo); - } else + if (!INTEL_INFO(dev)->cursor_needs_physical) i915_gem_object_unpin_from_display_plane(intel_crtc->cursor_bo); drm_gem_object_unreference(&intel_crtc->cursor_bo->base); } @@ -8307,7 +8301,8 @@ mode_fits_in_fbdev(struct drm_device *dev, bool intel_get_load_detect_pipe(struct drm_connector *connector, struct drm_display_mode *mode, - struct intel_load_detect_pipe *old) + struct intel_load_detect_pipe *old, + struct drm_modeset_acquire_ctx *ctx) { struct intel_crtc *intel_crtc; struct intel_encoder *intel_encoder = @@ -8317,13 +8312,19 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, struct drm_crtc *crtc = NULL; struct drm_device *dev = encoder->dev; struct drm_framebuffer *fb; - int i = -1; + struct drm_mode_config *config = &dev->mode_config; + int ret, i = -1; DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n", connector->base.id, connector->name, encoder->base.id, encoder->name); - mutex_lock(&dev->mode_config.connection_mutex); + drm_modeset_acquire_init(ctx, 0); + +retry: + ret = drm_modeset_lock(&config->connection_mutex, ctx); + if (ret) + goto fail_unlock; /* * Algorithm gets a little messy: @@ -8339,7 +8340,9 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, if (encoder->crtc) { crtc = encoder->crtc; - mutex_lock(&crtc->mutex); + ret = drm_modeset_lock(&crtc->mutex, ctx); + if (ret) + goto fail_unlock; old->dpms_mode = connector->dpms; old->load_detect_temp = false; @@ -8367,10 +8370,12 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, */ if (!crtc) { DRM_DEBUG_KMS("no pipe available for load-detect\n"); - goto fail_unlock_connector; + goto fail_unlock; } - mutex_lock(&crtc->mutex); + ret = drm_modeset_lock(&crtc->mutex, ctx); + if (ret) + goto fail_unlock; intel_encoder->new_crtc = to_intel_crtc(crtc); to_intel_connector(connector)->new_encoder = intel_encoder; @@ -8420,15 +8425,21 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, intel_crtc->new_config = &intel_crtc->config; else intel_crtc->new_config = NULL; - mutex_unlock(&crtc->mutex); -fail_unlock_connector: - mutex_unlock(&dev->mode_config.connection_mutex); +fail_unlock: + if (ret == -EDEADLK) { + drm_modeset_backoff(ctx); + goto retry; + } + + drm_modeset_drop_locks(ctx); + drm_modeset_acquire_fini(ctx); return false; } void intel_release_load_detect_pipe(struct drm_connector *connector, - struct intel_load_detect_pipe *old) + struct intel_load_detect_pipe *old, + struct drm_modeset_acquire_ctx *ctx) { struct intel_encoder *intel_encoder = intel_attached_encoder(connector); @@ -8452,8 +8463,7 @@ void intel_release_load_detect_pipe(struct drm_connector *connector, drm_framebuffer_unreference(old->release_fb); } - mutex_unlock(&crtc->mutex); - mutex_unlock(&connector->dev->mode_config.connection_mutex); + goto unlock; return; } @@ -8461,8 +8471,9 @@ void intel_release_load_detect_pipe(struct drm_connector *connector, if (old->dpms_mode != DRM_MODE_DPMS_ON) connector->funcs->dpms(connector, old->dpms_mode); - mutex_unlock(&crtc->mutex); - mutex_unlock(&connector->dev->mode_config.connection_mutex); +unlock: + drm_modeset_drop_locks(ctx); + drm_modeset_acquire_fini(ctx); } static int i9xx_pll_refclk(struct drm_device *dev, @@ -10978,6 +10989,9 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) intel_crtc->plane = !pipe; } + intel_crtc->cursor_base = ~0; + intel_crtc->cursor_cntl = ~0; + init_waitqueue_head(&intel_crtc->vbl_wait); BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) || @@ -10995,7 +11009,7 @@ enum pipe intel_get_pipe_from_connector(struct intel_connector *connector) struct drm_encoder *encoder = connector->base.encoder; struct drm_device *dev = connector->base.dev; - WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex)); + WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); if (!encoder) return INVALID_PIPE; @@ -11083,6 +11097,22 @@ const char *intel_output_name(int output) return names[output]; } +static bool intel_crt_present(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (IS_ULT(dev)) + return false; + + if (IS_CHERRYVIEW(dev)) + return false; + + if (IS_VALLEYVIEW(dev) && !dev_priv->vbt.int_crt_support) + return false; + + return true; +} + static void intel_setup_outputs(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -11091,7 +11121,7 @@ static void intel_setup_outputs(struct drm_device *dev) intel_lvds_init(dev); - if (!IS_ULT(dev) && !IS_CHERRYVIEW(dev)) + if (intel_crt_present(dev)) intel_crt_init(dev); if (HAS_DDI(dev)) { @@ -11561,6 +11591,14 @@ static void quirk_invert_brightness(struct drm_device *dev) DRM_INFO("applying inverted panel brightness quirk\n"); } +/* Some VBT's incorrectly indicate no backlight is present */ +static void quirk_backlight_present(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + dev_priv->quirks |= QUIRK_BACKLIGHT_PRESENT; + DRM_INFO("applying backlight present quirk\n"); +} + struct intel_quirk { int device; int subsystem_vendor; @@ -11606,9 +11644,6 @@ static struct intel_quirk intel_quirks[] = { /* ThinkPad T60 needs pipe A force quirk (bug #16494) */ { 0x2782, 0x17aa, 0x201a, quirk_pipea_force }, - /* 830 needs to leave pipe A & dpll A up */ - { 0x3577, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force }, - /* Lenovo U160 cannot use SSC on LVDS */ { 0x0046, 0x17aa, 0x3920, quirk_ssc_force_disable }, @@ -11632,6 +11667,15 @@ static struct intel_quirk intel_quirks[] = { /* Acer Aspire 5336 */ { 0x2a42, 0x1025, 0x048a, quirk_invert_brightness }, + + /* Acer C720 and C720P Chromebooks (Celeron 2955U) have backlights */ + { 0x0a06, 0x1025, 0x0a11, quirk_backlight_present }, + + /* Toshiba CB35 Chromebook (Celeron 2955U) */ + { 0x0a06, 0x1179, 0x0a88, quirk_backlight_present }, + + /* HP Chromebook 14 (Celeron 2955U) */ + { 0x0a06, 0x103c, 0x21ed, quirk_backlight_present }, }; static void intel_init_quirks(struct drm_device *dev) @@ -11791,20 +11835,12 @@ void intel_modeset_init(struct drm_device *dev) } } -static void -intel_connector_break_all_links(struct intel_connector *connector) -{ - connector->base.dpms = DRM_MODE_DPMS_OFF; - connector->base.encoder = NULL; - connector->encoder->connectors_active = false; - connector->encoder->base.crtc = NULL; -} - static void intel_enable_pipe_a(struct drm_device *dev) { struct intel_connector *connector; struct drm_connector *crt = NULL; struct intel_load_detect_pipe load_detect_temp; + struct drm_modeset_acquire_ctx ctx; /* We can't just switch on the pipe A, we need to set things up with a * proper mode and output configuration. As a gross hack, enable pipe A @@ -11821,8 +11857,8 @@ static void intel_enable_pipe_a(struct drm_device *dev) if (!crt) return; - if (intel_get_load_detect_pipe(crt, NULL, &load_detect_temp)) - intel_release_load_detect_pipe(crt, &load_detect_temp); + if (intel_get_load_detect_pipe(crt, NULL, &load_detect_temp, &ctx)) + intel_release_load_detect_pipe(crt, &load_detect_temp, &ctx); } @@ -11878,6 +11914,7 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc) * ... */ plane = crtc->plane; crtc->plane = !plane; + crtc->primary_enabled = true; dev_priv->display.crtc_disable(&crtc->base); crtc->plane = plane; @@ -11887,8 +11924,17 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc) if (connector->encoder->base.crtc != &crtc->base) continue; - intel_connector_break_all_links(connector); + connector->base.dpms = DRM_MODE_DPMS_OFF; + connector->base.encoder = NULL; } + /* multiple connectors may have the same encoder: + * handle them and break crtc link separately */ + list_for_each_entry(connector, &dev->mode_config.connector_list, + base.head) + if (connector->encoder->base.crtc == &crtc->base) { + connector->encoder->base.crtc = NULL; + connector->encoder->connectors_active = false; + } WARN_ON(crtc->active); crtc->base.enabled = false; @@ -11979,6 +12025,8 @@ static void intel_sanitize_encoder(struct intel_encoder *encoder) encoder->base.name); encoder->disable(encoder); } + encoder->base.crtc = NULL; + encoder->connectors_active = false; /* Inconsistent output/port/pipe state happens presumably due to * a bug in one of the get_hw_state functions. Or someplace else @@ -11989,8 +12037,8 @@ static void intel_sanitize_encoder(struct intel_encoder *encoder) base.head) { if (connector->encoder != encoder) continue; - - intel_connector_break_all_links(connector); + connector->base.dpms = DRM_MODE_DPMS_OFF; + connector->base.encoder = NULL; } } /* Enabled encoders without active connectors will be fixed in @@ -12407,8 +12455,8 @@ intel_display_capture_error_state(struct drm_device *dev) for_each_pipe(i) { error->pipe[i].power_domain_on = - intel_display_power_enabled_sw(dev_priv, - POWER_DOMAIN_PIPE(i)); + intel_display_power_enabled_unlocked(dev_priv, + POWER_DOMAIN_PIPE(i)); if (!error->pipe[i].power_domain_on) continue; @@ -12443,7 +12491,7 @@ intel_display_capture_error_state(struct drm_device *dev) enum transcoder cpu_transcoder = transcoders[i]; error->transcoder[i].power_domain_on = - intel_display_power_enabled_sw(dev_priv, + intel_display_power_enabled_unlocked(dev_priv, POWER_DOMAIN_TRANSCODER(cpu_transcoder)); if (!error->transcoder[i].power_domain_on) continue; diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index c4d88392128..8a1a4fbc06a 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -28,6 +28,8 @@ #include <linux/i2c.h> #include <linux/slab.h> #include <linux/export.h> +#include <linux/notifier.h> +#include <linux/reboot.h> #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> @@ -123,7 +125,8 @@ intel_dp_max_link_bw(struct intel_dp *intel_dp) case DP_LINK_BW_2_7: break; case DP_LINK_BW_5_4: /* 1.2 capable displays may advertise higher bw */ - if ((IS_HASWELL(dev) || INTEL_INFO(dev)->gen >= 8) && + if (((IS_HASWELL(dev) && !IS_HSW_ULX(dev)) || + INTEL_INFO(dev)->gen >= 8) && intel_dp->dpcd[DP_DPCD_REV] >= 0x12) max_link_bw = DP_LINK_BW_5_4; else @@ -138,6 +141,22 @@ intel_dp_max_link_bw(struct intel_dp *intel_dp) return max_link_bw; } +static u8 intel_dp_max_lane_count(struct intel_dp *intel_dp) +{ + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = intel_dig_port->base.base.dev; + u8 source_max, sink_max; + + source_max = 4; + if (HAS_DDI(dev) && intel_dig_port->port == PORT_A && + (intel_dig_port->saved_port_bits & DDI_A_4_LANES) == 0) + source_max = 2; + + sink_max = drm_dp_max_lane_count(intel_dp->dpcd); + + return min(source_max, sink_max); +} + /* * The units on the numbers in the next two are... bizarre. Examples will * make it clearer; this one parallels an example in the eDP spec. @@ -188,7 +207,7 @@ intel_dp_mode_valid(struct drm_connector *connector, } max_link_clock = drm_dp_bw_code_to_link_rate(intel_dp_max_link_bw(intel_dp)); - max_lanes = drm_dp_max_lane_count(intel_dp->dpcd); + max_lanes = intel_dp_max_lane_count(intel_dp); max_rate = intel_dp_max_data_rate(max_link_clock, max_lanes); mode_rate = intel_dp_link_required(target_clock, 18); @@ -319,6 +338,37 @@ static u32 _pp_stat_reg(struct intel_dp *intel_dp) return VLV_PIPE_PP_STATUS(vlv_power_sequencer_pipe(intel_dp)); } +/* Reboot notifier handler to shutdown panel power to guarantee T12 timing + This function only applicable when panel PM state is not to be tracked */ +static int edp_notify_handler(struct notifier_block *this, unsigned long code, + void *unused) +{ + struct intel_dp *intel_dp = container_of(this, typeof(* intel_dp), + edp_notifier); + struct drm_device *dev = intel_dp_to_dev(intel_dp); + struct drm_i915_private *dev_priv = dev->dev_private; + u32 pp_div; + u32 pp_ctrl_reg, pp_div_reg; + enum pipe pipe = vlv_power_sequencer_pipe(intel_dp); + + if (!is_edp(intel_dp) || code != SYS_RESTART) + return 0; + + if (IS_VALLEYVIEW(dev)) { + pp_ctrl_reg = VLV_PIPE_PP_CONTROL(pipe); + pp_div_reg = VLV_PIPE_PP_DIVISOR(pipe); + pp_div = I915_READ(pp_div_reg); + pp_div &= PP_REFERENCE_DIVIDER_MASK; + + /* 0x1F write to PP_DIV_REG sets max cycle delay */ + I915_WRITE(pp_div_reg, pp_div | 0x1F); + I915_WRITE(pp_ctrl_reg, PANEL_UNLOCK_REGS | PANEL_POWER_OFF); + msleep(intel_dp->panel_power_cycle_delay); + } + + return 0; +} + static bool edp_have_panel_power(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); @@ -702,9 +752,9 @@ intel_dp_aux_init(struct intel_dp *intel_dp, struct intel_connector *connector) DRM_DEBUG_KMS("registering %s bus for %s\n", name, connector->base.kdev->kobj.name); - ret = drm_dp_aux_register_i2c_bus(&intel_dp->aux); + ret = drm_dp_aux_register(&intel_dp->aux); if (ret < 0) { - DRM_ERROR("drm_dp_aux_register_i2c_bus() for %s failed (%d)\n", + DRM_ERROR("drm_dp_aux_register() for %s failed (%d)\n", name, ret); return; } @@ -714,7 +764,7 @@ intel_dp_aux_init(struct intel_dp *intel_dp, struct intel_connector *connector) intel_dp->aux.ddc.dev.kobj.name); if (ret < 0) { DRM_ERROR("sysfs_create_link() for %s failed (%d)\n", name, ret); - drm_dp_aux_unregister_i2c_bus(&intel_dp->aux); + drm_dp_aux_unregister(&intel_dp->aux); } } @@ -789,8 +839,10 @@ intel_dp_compute_config(struct intel_encoder *encoder, struct intel_crtc *intel_crtc = encoder->new_crtc; struct intel_connector *intel_connector = intel_dp->attached_connector; int lane_count, clock; - int max_lane_count = drm_dp_max_lane_count(intel_dp->dpcd); + int min_lane_count = 1; + int max_lane_count = intel_dp_max_lane_count(intel_dp); /* Conveniently, the link BW constants become indices with a shift...*/ + int min_clock = 0; int max_clock = intel_dp_max_link_bw(intel_dp) >> 3; int bpp, mode_rate; static int bws[] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7, DP_LINK_BW_5_4 }; @@ -824,19 +876,38 @@ intel_dp_compute_config(struct intel_encoder *encoder, /* Walk through all bpp values. Luckily they're all nicely spaced with 2 * bpc in between. */ bpp = pipe_config->pipe_bpp; - if (is_edp(intel_dp) && dev_priv->vbt.edp_bpp && - dev_priv->vbt.edp_bpp < bpp) { - DRM_DEBUG_KMS("clamping bpp for eDP panel to BIOS-provided %i\n", - dev_priv->vbt.edp_bpp); - bpp = dev_priv->vbt.edp_bpp; + if (is_edp(intel_dp)) { + if (dev_priv->vbt.edp_bpp && dev_priv->vbt.edp_bpp < bpp) { + DRM_DEBUG_KMS("clamping bpp for eDP panel to BIOS-provided %i\n", + dev_priv->vbt.edp_bpp); + bpp = dev_priv->vbt.edp_bpp; + } + + if (IS_BROADWELL(dev)) { + /* Yes, it's an ugly hack. */ + min_lane_count = max_lane_count; + DRM_DEBUG_KMS("forcing lane count to max (%u) on BDW\n", + min_lane_count); + } else if (dev_priv->vbt.edp_lanes) { + min_lane_count = min(dev_priv->vbt.edp_lanes, + max_lane_count); + DRM_DEBUG_KMS("using min %u lanes per VBT\n", + min_lane_count); + } + + if (dev_priv->vbt.edp_rate) { + min_clock = min(dev_priv->vbt.edp_rate >> 3, max_clock); + DRM_DEBUG_KMS("using min %02x link bw per VBT\n", + bws[min_clock]); + } } for (; bpp >= 6*3; bpp -= 2*3) { mode_rate = intel_dp_link_required(adjusted_mode->crtc_clock, bpp); - for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) { - for (clock = 0; clock <= max_clock; clock++) { + for (clock = min_clock; clock <= max_clock; clock++) { + for (lane_count = min_lane_count; lane_count <= max_lane_count; lane_count <<= 1) { link_clock = drm_dp_bw_code_to_link_rate(bws[clock]); link_avail = intel_dp_max_data_rate(link_clock, lane_count); @@ -1154,7 +1225,7 @@ static void edp_panel_vdd_off_sync(struct intel_dp *intel_dp) u32 pp; u32 pp_stat_reg, pp_ctrl_reg; - WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex)); + WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); if (!intel_dp->want_panel_vdd && edp_have_panel_vdd(intel_dp)) { struct intel_digital_port *intel_dig_port = @@ -1191,9 +1262,9 @@ static void edp_panel_vdd_work(struct work_struct *__work) struct intel_dp, panel_vdd_work); struct drm_device *dev = intel_dp_to_dev(intel_dp); - mutex_lock(&dev->mode_config.connection_mutex); + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); edp_panel_vdd_off_sync(intel_dp); - mutex_unlock(&dev->mode_config.connection_mutex); + drm_modeset_unlock(&dev->mode_config.connection_mutex); } static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync) @@ -3662,13 +3733,17 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder) struct intel_dp *intel_dp = &intel_dig_port->dp; struct drm_device *dev = intel_dp_to_dev(intel_dp); - drm_dp_aux_unregister_i2c_bus(&intel_dp->aux); + drm_dp_aux_unregister(&intel_dp->aux); drm_encoder_cleanup(encoder); if (is_edp(intel_dp)) { cancel_delayed_work_sync(&intel_dp->panel_vdd_work); - mutex_lock(&dev->mode_config.connection_mutex); + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); edp_panel_vdd_off_sync(intel_dp); - mutex_unlock(&dev->mode_config.connection_mutex); + drm_modeset_unlock(&dev->mode_config.connection_mutex); + if (intel_dp->edp_notifier.notifier_call) { + unregister_reboot_notifier(&intel_dp->edp_notifier); + intel_dp->edp_notifier.notifier_call = NULL; + } } kfree(intel_dig_port); } @@ -4146,6 +4221,11 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, } mutex_unlock(&dev->mode_config.mutex); + if (IS_VALLEYVIEW(dev)) { + intel_dp->edp_notifier.notifier_call = edp_notify_handler; + register_reboot_notifier(&intel_dp->edp_notifier); + } + intel_panel_init(&intel_connector->panel, fixed_mode, downclock_mode); intel_panel_setup_backlight(connector); @@ -4244,12 +4324,12 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, intel_dp->psr_setup_done = false; if (!intel_edp_init_connector(intel_dp, intel_connector, &power_seq)) { - drm_dp_aux_unregister_i2c_bus(&intel_dp->aux); + drm_dp_aux_unregister(&intel_dp->aux); if (is_edp(intel_dp)) { cancel_delayed_work_sync(&intel_dp->panel_vdd_work); - mutex_lock(&dev->mode_config.connection_mutex); + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); edp_panel_vdd_off_sync(intel_dp); - mutex_unlock(&dev->mode_config.connection_mutex); + drm_modeset_unlock(&dev->mode_config.connection_mutex); } drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index f1d5897c96c..f67340ed2c1 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -386,7 +386,8 @@ struct intel_crtc { uint32_t cursor_addr; int16_t cursor_x, cursor_y; int16_t cursor_width, cursor_height; - bool cursor_visible; + uint32_t cursor_cntl; + uint32_t cursor_base; struct intel_plane_config plane_config; struct intel_crtc_config config; @@ -537,6 +538,8 @@ struct intel_dp { unsigned long last_power_on; unsigned long last_backlight_off; bool psr_setup_done; + struct notifier_block edp_notifier; + bool use_tps3; struct intel_connector *attached_connector; @@ -748,9 +751,11 @@ void vlv_wait_port_ready(struct drm_i915_private *dev_priv, struct intel_digital_port *dport); bool intel_get_load_detect_pipe(struct drm_connector *connector, struct drm_display_mode *mode, - struct intel_load_detect_pipe *old); + struct intel_load_detect_pipe *old, + struct drm_modeset_acquire_ctx *ctx); void intel_release_load_detect_pipe(struct drm_connector *connector, - struct intel_load_detect_pipe *old); + struct intel_load_detect_pipe *old, + struct drm_modeset_acquire_ctx *ctx); int intel_pin_and_fence_fb_obj(struct drm_device *dev, struct drm_i915_gem_object *obj, struct intel_engine_cs *pipelined); @@ -947,8 +952,8 @@ int intel_power_domains_init(struct drm_i915_private *); void intel_power_domains_remove(struct drm_i915_private *); bool intel_display_power_enabled(struct drm_i915_private *dev_priv, enum intel_display_power_domain domain); -bool intel_display_power_enabled_sw(struct drm_i915_private *dev_priv, - enum intel_display_power_domain domain); +bool intel_display_power_enabled_unlocked(struct drm_i915_private *dev_priv, + enum intel_display_power_domain domain); void intel_display_power_get(struct drm_i915_private *dev_priv, enum intel_display_power_domain domain); void intel_display_power_put(struct drm_i915_private *dev_priv, @@ -971,7 +976,8 @@ void intel_runtime_pm_put(struct drm_i915_private *dev_priv); void intel_init_runtime_pm(struct drm_i915_private *dev_priv); void intel_fini_runtime_pm(struct drm_i915_private *dev_priv); void ilk_wm_get_hw_state(struct drm_device *dev); - +void __vlv_set_power_well(struct drm_i915_private *dev_priv, + enum punit_power_well power_well_id, bool enable); /* intel_sdvo.c */ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob); diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c index 2525cdd5234..3fd082933c8 100644 --- a/drivers/gpu/drm/i915/intel_dsi.c +++ b/drivers/gpu/drm/i915/intel_dsi.c @@ -35,6 +35,11 @@ /* the sub-encoders aka panel drivers */ static const struct intel_dsi_device intel_dsi_devices[] = { + { + .panel_id = MIPI_DSI_GENERIC_PANEL_ID, + .name = "vbt-generic-dsi-vid-mode-display", + .dev_ops = &vbt_generic_dsi_display_ops, + }, }; static void band_gap_reset(struct drm_i915_private *dev_priv) @@ -112,17 +117,18 @@ static void intel_dsi_device_ready(struct intel_encoder *encoder) /* bandgap reset is needed after everytime we do power gate */ band_gap_reset(dev_priv); + I915_WRITE(MIPI_DEVICE_READY(pipe), ULPS_STATE_ENTER); + usleep_range(2500, 3000); + val = I915_READ(MIPI_PORT_CTRL(pipe)); I915_WRITE(MIPI_PORT_CTRL(pipe), val | LP_OUTPUT_HOLD); usleep_range(1000, 1500); - I915_WRITE(MIPI_DEVICE_READY(pipe), DEVICE_READY | ULPS_STATE_EXIT); - usleep_range(2000, 2500); - I915_WRITE(MIPI_DEVICE_READY(pipe), DEVICE_READY); - usleep_range(2000, 2500); - I915_WRITE(MIPI_DEVICE_READY(pipe), 0x00); - usleep_range(2000, 2500); + + I915_WRITE(MIPI_DEVICE_READY(pipe), ULPS_STATE_EXIT); + usleep_range(2500, 3000); + I915_WRITE(MIPI_DEVICE_READY(pipe), DEVICE_READY); - usleep_range(2000, 2500); + usleep_range(2500, 3000); } static void intel_dsi_enable(struct intel_encoder *encoder) @@ -201,6 +207,19 @@ static void intel_dsi_enable_nop(struct intel_encoder *encoder) */ } +static void intel_dsi_pre_disable(struct intel_encoder *encoder) +{ + struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base); + + DRM_DEBUG_KMS("\n"); + + if (is_vid_mode(intel_dsi)) { + /* Send Shutdown command to the panel in LP mode */ + dpi_send_cmd(intel_dsi, SHUTDOWN, DPI_LP_MODE_EN); + msleep(10); + } +} + static void intel_dsi_disable(struct intel_encoder *encoder) { struct drm_device *dev = encoder->base.dev; @@ -213,10 +232,6 @@ static void intel_dsi_disable(struct intel_encoder *encoder) DRM_DEBUG_KMS("\n"); if (is_vid_mode(intel_dsi)) { - /* Send Shutdown command to the panel in LP mode */ - dpi_send_cmd(intel_dsi, SHUTDOWN, DPI_LP_MODE_EN); - msleep(10); - /* de-assert ip_tg_enable signal */ temp = I915_READ(MIPI_PORT_CTRL(pipe)); I915_WRITE(MIPI_PORT_CTRL(pipe), temp & ~DPI_ENABLE); @@ -257,23 +272,23 @@ static void intel_dsi_clear_device_ready(struct intel_encoder *encoder) DRM_DEBUG_KMS("\n"); - I915_WRITE(MIPI_DEVICE_READY(pipe), ULPS_STATE_ENTER); + I915_WRITE(MIPI_DEVICE_READY(pipe), DEVICE_READY | ULPS_STATE_ENTER); usleep_range(2000, 2500); - I915_WRITE(MIPI_DEVICE_READY(pipe), ULPS_STATE_EXIT); + I915_WRITE(MIPI_DEVICE_READY(pipe), DEVICE_READY | ULPS_STATE_EXIT); usleep_range(2000, 2500); - I915_WRITE(MIPI_DEVICE_READY(pipe), ULPS_STATE_ENTER); + I915_WRITE(MIPI_DEVICE_READY(pipe), DEVICE_READY | ULPS_STATE_ENTER); usleep_range(2000, 2500); - val = I915_READ(MIPI_PORT_CTRL(pipe)); - I915_WRITE(MIPI_PORT_CTRL(pipe), val & ~LP_OUTPUT_HOLD); - usleep_range(1000, 1500); - if (wait_for(((I915_READ(MIPI_PORT_CTRL(pipe)) & AFE_LATCHOUT) == 0x00000), 30)) DRM_ERROR("DSI LP not going Low\n"); + val = I915_READ(MIPI_PORT_CTRL(pipe)); + I915_WRITE(MIPI_PORT_CTRL(pipe), val & ~LP_OUTPUT_HOLD); + usleep_range(1000, 1500); + I915_WRITE(MIPI_DEVICE_READY(pipe), 0x00); usleep_range(2000, 2500); @@ -288,6 +303,8 @@ static void intel_dsi_post_disable(struct intel_encoder *encoder) DRM_DEBUG_KMS("\n"); + intel_dsi_disable(encoder); + intel_dsi_clear_device_ready(encoder); val = I915_READ(DSPCLK_GATE_D); @@ -655,6 +672,10 @@ bool intel_dsi_init(struct drm_device *dev) DRM_DEBUG_KMS("\n"); + /* There is no detection method for MIPI so rely on VBT */ + if (!dev_priv->vbt.has_mipi) + return false; + intel_dsi = kzalloc(sizeof(*intel_dsi), GFP_KERNEL); if (!intel_dsi) return false; @@ -686,7 +707,7 @@ bool intel_dsi_init(struct drm_device *dev) intel_encoder->pre_pll_enable = intel_dsi_pre_pll_enable; intel_encoder->pre_enable = intel_dsi_pre_enable; intel_encoder->enable = intel_dsi_enable_nop; - intel_encoder->disable = intel_dsi_disable; + intel_encoder->disable = intel_dsi_pre_disable; intel_encoder->post_disable = intel_dsi_post_disable; intel_encoder->get_hw_state = intel_dsi_get_hw_state; intel_encoder->get_config = intel_dsi_get_config; diff --git a/drivers/gpu/drm/i915/intel_dsi.h b/drivers/gpu/drm/i915/intel_dsi.h index e3f4e91c526..31db33d3e5c 100644 --- a/drivers/gpu/drm/i915/intel_dsi.h +++ b/drivers/gpu/drm/i915/intel_dsi.h @@ -133,4 +133,6 @@ static inline struct intel_dsi *enc_to_intel_dsi(struct drm_encoder *encoder) extern void vlv_enable_dsi_pll(struct intel_encoder *encoder); extern void vlv_disable_dsi_pll(struct intel_encoder *encoder); +extern struct intel_dsi_dev_ops vbt_generic_dsi_display_ops; + #endif /* _INTEL_DSI_H */ diff --git a/drivers/gpu/drm/i915/intel_dsi_cmd.c b/drivers/gpu/drm/i915/intel_dsi_cmd.c index 3eeb21b9fdd..933c8630523 100644 --- a/drivers/gpu/drm/i915/intel_dsi_cmd.c +++ b/drivers/gpu/drm/i915/intel_dsi_cmd.c @@ -404,12 +404,6 @@ int dpi_send_cmd(struct intel_dsi *intel_dsi, u32 cmd, bool hs) else cmd |= DPI_LP_MODE; - /* DPI virtual channel?! */ - - mask = DPI_FIFO_EMPTY; - if (wait_for((I915_READ(MIPI_GEN_FIFO_STAT(pipe)) & mask) == mask, 50)) - DRM_ERROR("Timeout waiting for DPI FIFO empty.\n"); - /* clear bit */ I915_WRITE(MIPI_INTR_STAT(pipe), SPL_PKT_SENT_INTERRUPT); diff --git a/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c b/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c new file mode 100644 index 00000000000..21a0d348ced --- /dev/null +++ b/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c @@ -0,0 +1,589 @@ +/* + * Copyright © 2014 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Shobhit Kumar <shobhit.kumar@intel.com> + * + */ + +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_edid.h> +#include <drm/i915_drm.h> +#include <linux/slab.h> +#include <video/mipi_display.h> +#include <asm/intel-mid.h> +#include <video/mipi_display.h> +#include "i915_drv.h" +#include "intel_drv.h" +#include "intel_dsi.h" +#include "intel_dsi_cmd.h" + +#define MIPI_TRANSFER_MODE_SHIFT 0 +#define MIPI_VIRTUAL_CHANNEL_SHIFT 1 +#define MIPI_PORT_SHIFT 3 + +#define PREPARE_CNT_MAX 0x3F +#define EXIT_ZERO_CNT_MAX 0x3F +#define CLK_ZERO_CNT_MAX 0xFF +#define TRAIL_CNT_MAX 0x1F + +#define NS_KHZ_RATIO 1000000 + +#define GPI0_NC_0_HV_DDI0_HPD 0x4130 +#define GPIO_NC_0_HV_DDI0_PAD 0x4138 +#define GPIO_NC_1_HV_DDI0_DDC_SDA 0x4120 +#define GPIO_NC_1_HV_DDI0_DDC_SDA_PAD 0x4128 +#define GPIO_NC_2_HV_DDI0_DDC_SCL 0x4110 +#define GPIO_NC_2_HV_DDI0_DDC_SCL_PAD 0x4118 +#define GPIO_NC_3_PANEL0_VDDEN 0x4140 +#define GPIO_NC_3_PANEL0_VDDEN_PAD 0x4148 +#define GPIO_NC_4_PANEL0_BLKEN 0x4150 +#define GPIO_NC_4_PANEL0_BLKEN_PAD 0x4158 +#define GPIO_NC_5_PANEL0_BLKCTL 0x4160 +#define GPIO_NC_5_PANEL0_BLKCTL_PAD 0x4168 +#define GPIO_NC_6_PCONF0 0x4180 +#define GPIO_NC_6_PAD 0x4188 +#define GPIO_NC_7_PCONF0 0x4190 +#define GPIO_NC_7_PAD 0x4198 +#define GPIO_NC_8_PCONF0 0x4170 +#define GPIO_NC_8_PAD 0x4178 +#define GPIO_NC_9_PCONF0 0x4100 +#define GPIO_NC_9_PAD 0x4108 +#define GPIO_NC_10_PCONF0 0x40E0 +#define GPIO_NC_10_PAD 0x40E8 +#define GPIO_NC_11_PCONF0 0x40F0 +#define GPIO_NC_11_PAD 0x40F8 + +struct gpio_table { + u16 function_reg; + u16 pad_reg; + u8 init; +}; + +static struct gpio_table gtable[] = { + { GPI0_NC_0_HV_DDI0_HPD, GPIO_NC_0_HV_DDI0_PAD, 0 }, + { GPIO_NC_1_HV_DDI0_DDC_SDA, GPIO_NC_1_HV_DDI0_DDC_SDA_PAD, 0 }, + { GPIO_NC_2_HV_DDI0_DDC_SCL, GPIO_NC_2_HV_DDI0_DDC_SCL_PAD, 0 }, + { GPIO_NC_3_PANEL0_VDDEN, GPIO_NC_3_PANEL0_VDDEN_PAD, 0 }, + { GPIO_NC_4_PANEL0_BLKEN, GPIO_NC_4_PANEL0_BLKEN_PAD, 0 }, + { GPIO_NC_5_PANEL0_BLKCTL, GPIO_NC_5_PANEL0_BLKCTL_PAD, 0 }, + { GPIO_NC_6_PCONF0, GPIO_NC_6_PAD, 0 }, + { GPIO_NC_7_PCONF0, GPIO_NC_7_PAD, 0 }, + { GPIO_NC_8_PCONF0, GPIO_NC_8_PAD, 0 }, + { GPIO_NC_9_PCONF0, GPIO_NC_9_PAD, 0 }, + { GPIO_NC_10_PCONF0, GPIO_NC_10_PAD, 0}, + { GPIO_NC_11_PCONF0, GPIO_NC_11_PAD, 0} +}; + +static u8 *mipi_exec_send_packet(struct intel_dsi *intel_dsi, u8 *data) +{ + u8 type, byte, mode, vc, port; + u16 len; + + byte = *data++; + mode = (byte >> MIPI_TRANSFER_MODE_SHIFT) & 0x1; + vc = (byte >> MIPI_VIRTUAL_CHANNEL_SHIFT) & 0x3; + port = (byte >> MIPI_PORT_SHIFT) & 0x3; + + /* LP or HS mode */ + intel_dsi->hs = mode; + + /* get packet type and increment the pointer */ + type = *data++; + + len = *((u16 *) data); + data += 2; + + switch (type) { + case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM: + dsi_vc_generic_write_0(intel_dsi, vc); + break; + case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM: + dsi_vc_generic_write_1(intel_dsi, vc, *data); + break; + case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM: + dsi_vc_generic_write_2(intel_dsi, vc, *data, *(data + 1)); + break; + case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM: + case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM: + case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM: + DRM_DEBUG_DRIVER("Generic Read not yet implemented or used\n"); + break; + case MIPI_DSI_GENERIC_LONG_WRITE: + dsi_vc_generic_write(intel_dsi, vc, data, len); + break; + case MIPI_DSI_DCS_SHORT_WRITE: + dsi_vc_dcs_write_0(intel_dsi, vc, *data); + break; + case MIPI_DSI_DCS_SHORT_WRITE_PARAM: + dsi_vc_dcs_write_1(intel_dsi, vc, *data, *(data + 1)); + break; + case MIPI_DSI_DCS_READ: + DRM_DEBUG_DRIVER("DCS Read not yet implemented or used\n"); + break; + case MIPI_DSI_DCS_LONG_WRITE: + dsi_vc_dcs_write(intel_dsi, vc, data, len); + break; + }; + + data += len; + + return data; +} + +static u8 *mipi_exec_delay(struct intel_dsi *intel_dsi, u8 *data) +{ + u32 delay = *((u32 *) data); + + usleep_range(delay, delay + 10); + data += 4; + + return data; +} + +static u8 *mipi_exec_gpio(struct intel_dsi *intel_dsi, u8 *data) +{ + u8 gpio, action; + u16 function, pad; + u32 val; + struct drm_device *dev = intel_dsi->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + gpio = *data++; + + /* pull up/down */ + action = *data++; + + function = gtable[gpio].function_reg; + pad = gtable[gpio].pad_reg; + + mutex_lock(&dev_priv->dpio_lock); + if (!gtable[gpio].init) { + /* program the function */ + /* FIXME: remove constant below */ + vlv_gpio_nc_write(dev_priv, function, 0x2000CC00); + gtable[gpio].init = 1; + } + + val = 0x4 | action; + + /* pull up/down */ + vlv_gpio_nc_write(dev_priv, pad, val); + mutex_unlock(&dev_priv->dpio_lock); + + return data; +} + +typedef u8 * (*fn_mipi_elem_exec)(struct intel_dsi *intel_dsi, u8 *data); +static const fn_mipi_elem_exec exec_elem[] = { + NULL, /* reserved */ + mipi_exec_send_packet, + mipi_exec_delay, + mipi_exec_gpio, + NULL, /* status read; later */ +}; + +/* + * MIPI Sequence from VBT #53 parsing logic + * We have already separated each seqence during bios parsing + * Following is generic execution function for any sequence + */ + +static const char * const seq_name[] = { + "UNDEFINED", + "MIPI_SEQ_ASSERT_RESET", + "MIPI_SEQ_INIT_OTP", + "MIPI_SEQ_DISPLAY_ON", + "MIPI_SEQ_DISPLAY_OFF", + "MIPI_SEQ_DEASSERT_RESET" +}; + +static void generic_exec_sequence(struct intel_dsi *intel_dsi, char *sequence) +{ + u8 *data = sequence; + fn_mipi_elem_exec mipi_elem_exec; + int index; + + if (!sequence) + return; + + DRM_DEBUG_DRIVER("Starting MIPI sequence - %s\n", seq_name[*data]); + + /* go to the first element of the sequence */ + data++; + + /* parse each byte till we reach end of sequence byte - 0x00 */ + while (1) { + index = *data; + mipi_elem_exec = exec_elem[index]; + if (!mipi_elem_exec) { + DRM_ERROR("Unsupported MIPI element, skipping sequence execution\n"); + return; + } + + /* goto element payload */ + data++; + + /* execute the element specific rotines */ + data = mipi_elem_exec(intel_dsi, data); + + /* + * After processing the element, data should point to + * next element or end of sequence + * check if have we reached end of sequence + */ + if (*data == 0x00) + break; + } +} + +static bool generic_init(struct intel_dsi_device *dsi) +{ + struct intel_dsi *intel_dsi = container_of(dsi, struct intel_dsi, dev); + struct drm_device *dev = intel_dsi->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct mipi_config *mipi_config = dev_priv->vbt.dsi.config; + struct mipi_pps_data *pps = dev_priv->vbt.dsi.pps; + struct drm_display_mode *mode = dev_priv->vbt.lfp_lvds_vbt_mode; + u32 bits_per_pixel = 24; + u32 tlpx_ns, extra_byte_count, bitrate, tlpx_ui; + u32 ui_num, ui_den; + u32 prepare_cnt, exit_zero_cnt, clk_zero_cnt, trail_cnt; + u32 ths_prepare_ns, tclk_trail_ns; + u32 tclk_prepare_clkzero, ths_prepare_hszero; + u32 lp_to_hs_switch, hs_to_lp_switch; + + DRM_DEBUG_KMS("\n"); + + intel_dsi->eotp_pkt = mipi_config->eot_pkt_disabled ? 0 : 1; + intel_dsi->clock_stop = mipi_config->enable_clk_stop ? 1 : 0; + intel_dsi->lane_count = mipi_config->lane_cnt + 1; + intel_dsi->pixel_format = mipi_config->videomode_color_format << 7; + + if (intel_dsi->pixel_format == VID_MODE_FORMAT_RGB666) + bits_per_pixel = 18; + else if (intel_dsi->pixel_format == VID_MODE_FORMAT_RGB565) + bits_per_pixel = 16; + + bitrate = (mode->clock * bits_per_pixel) / intel_dsi->lane_count; + + intel_dsi->operation_mode = mipi_config->is_cmd_mode; + intel_dsi->video_mode_format = mipi_config->video_transfer_mode; + intel_dsi->escape_clk_div = mipi_config->byte_clk_sel; + intel_dsi->lp_rx_timeout = mipi_config->lp_rx_timeout; + intel_dsi->turn_arnd_val = mipi_config->turn_around_timeout; + intel_dsi->rst_timer_val = mipi_config->device_reset_timer; + intel_dsi->init_count = mipi_config->master_init_timer; + intel_dsi->bw_timer = mipi_config->dbi_bw_timer; + intel_dsi->video_frmt_cfg_bits = mipi_config->bta_enabled ? DISABLE_VIDEO_BTA : 0; + + switch (intel_dsi->escape_clk_div) { + case 0: + tlpx_ns = 50; + break; + case 1: + tlpx_ns = 100; + break; + + case 2: + tlpx_ns = 200; + break; + default: + tlpx_ns = 50; + break; + } + + switch (intel_dsi->lane_count) { + case 1: + case 2: + extra_byte_count = 2; + break; + case 3: + extra_byte_count = 4; + break; + case 4: + default: + extra_byte_count = 3; + break; + } + + /* + * ui(s) = 1/f [f in hz] + * ui(ns) = 10^9 / (f*10^6) [f in Mhz] -> 10^3/f(Mhz) + */ + + /* in Kbps */ + ui_num = NS_KHZ_RATIO; + ui_den = bitrate; + + tclk_prepare_clkzero = mipi_config->tclk_prepare_clkzero; + ths_prepare_hszero = mipi_config->ths_prepare_hszero; + + /* + * B060 + * LP byte clock = TLPX/ (8UI) + */ + intel_dsi->lp_byte_clk = DIV_ROUND_UP(tlpx_ns * ui_den, 8 * ui_num); + + /* count values in UI = (ns value) * (bitrate / (2 * 10^6)) + * + * Since txddrclkhs_i is 2xUI, all the count values programmed in + * DPHY param register are divided by 2 + * + * prepare count + */ + ths_prepare_ns = max(mipi_config->ths_prepare, mipi_config->tclk_prepare); + prepare_cnt = DIV_ROUND_UP(ths_prepare_ns * ui_den, ui_num * 2); + + /* exit zero count */ + exit_zero_cnt = DIV_ROUND_UP( + (ths_prepare_hszero - ths_prepare_ns) * ui_den, + ui_num * 2 + ); + + /* + * Exit zero is unified val ths_zero and ths_exit + * minimum value for ths_exit = 110ns + * min (exit_zero_cnt * 2) = 110/UI + * exit_zero_cnt = 55/UI + */ + if (exit_zero_cnt < (55 * ui_den / ui_num)) + if ((55 * ui_den) % ui_num) + exit_zero_cnt += 1; + + /* clk zero count */ + clk_zero_cnt = DIV_ROUND_UP( + (tclk_prepare_clkzero - ths_prepare_ns) + * ui_den, 2 * ui_num); + + /* trail count */ + tclk_trail_ns = max(mipi_config->tclk_trail, mipi_config->ths_trail); + trail_cnt = DIV_ROUND_UP(tclk_trail_ns * ui_den, 2 * ui_num); + + if (prepare_cnt > PREPARE_CNT_MAX || + exit_zero_cnt > EXIT_ZERO_CNT_MAX || + clk_zero_cnt > CLK_ZERO_CNT_MAX || + trail_cnt > TRAIL_CNT_MAX) + DRM_DEBUG_DRIVER("Values crossing maximum limits, restricting to max values\n"); + + if (prepare_cnt > PREPARE_CNT_MAX) + prepare_cnt = PREPARE_CNT_MAX; + + if (exit_zero_cnt > EXIT_ZERO_CNT_MAX) + exit_zero_cnt = EXIT_ZERO_CNT_MAX; + + if (clk_zero_cnt > CLK_ZERO_CNT_MAX) + clk_zero_cnt = CLK_ZERO_CNT_MAX; + + if (trail_cnt > TRAIL_CNT_MAX) + trail_cnt = TRAIL_CNT_MAX; + + /* B080 */ + intel_dsi->dphy_reg = exit_zero_cnt << 24 | trail_cnt << 16 | + clk_zero_cnt << 8 | prepare_cnt; + + /* + * LP to HS switch count = 4TLPX + PREP_COUNT * 2 + EXIT_ZERO_COUNT * 2 + * + 10UI + Extra Byte Count + * + * HS to LP switch count = THS-TRAIL + 2TLPX + Extra Byte Count + * Extra Byte Count is calculated according to number of lanes. + * High Low Switch Count is the Max of LP to HS and + * HS to LP switch count + * + */ + tlpx_ui = DIV_ROUND_UP(tlpx_ns * ui_den, ui_num); + + /* B044 */ + /* FIXME: + * The comment above does not match with the code */ + lp_to_hs_switch = DIV_ROUND_UP(4 * tlpx_ui + prepare_cnt * 2 + + exit_zero_cnt * 2 + 10, 8); + + hs_to_lp_switch = DIV_ROUND_UP(mipi_config->ths_trail + 2 * tlpx_ui, 8); + + intel_dsi->hs_to_lp_count = max(lp_to_hs_switch, hs_to_lp_switch); + intel_dsi->hs_to_lp_count += extra_byte_count; + + /* B088 */ + /* LP -> HS for clock lanes + * LP clk sync + LP11 + LP01 + tclk_prepare + tclk_zero + + * extra byte count + * 2TPLX + 1TLPX + 1 TPLX(in ns) + prepare_cnt * 2 + clk_zero_cnt * + * 2(in UI) + extra byte count + * In byteclks = (4TLPX + prepare_cnt * 2 + clk_zero_cnt *2 (in UI)) / + * 8 + extra byte count + */ + intel_dsi->clk_lp_to_hs_count = + DIV_ROUND_UP( + 4 * tlpx_ui + prepare_cnt * 2 + + clk_zero_cnt * 2, + 8); + + intel_dsi->clk_lp_to_hs_count += extra_byte_count; + + /* HS->LP for Clock Lanes + * Low Power clock synchronisations + 1Tx byteclk + tclk_trail + + * Extra byte count + * 2TLPX + 8UI + (trail_count*2)(in UI) + Extra byte count + * In byteclks = (2*TLpx(in UI) + trail_count*2 +8)(in UI)/8 + + * Extra byte count + */ + intel_dsi->clk_hs_to_lp_count = + DIV_ROUND_UP(2 * tlpx_ui + trail_cnt * 2 + 8, + 8); + intel_dsi->clk_hs_to_lp_count += extra_byte_count; + + DRM_DEBUG_KMS("Eot %s\n", intel_dsi->eotp_pkt ? "enabled" : "disabled"); + DRM_DEBUG_KMS("Clockstop %s\n", intel_dsi->clock_stop ? + "disabled" : "enabled"); + DRM_DEBUG_KMS("Mode %s\n", intel_dsi->operation_mode ? "command" : "video"); + DRM_DEBUG_KMS("Pixel Format %d\n", intel_dsi->pixel_format); + DRM_DEBUG_KMS("TLPX %d\n", intel_dsi->escape_clk_div); + DRM_DEBUG_KMS("LP RX Timeout 0x%x\n", intel_dsi->lp_rx_timeout); + DRM_DEBUG_KMS("Turnaround Timeout 0x%x\n", intel_dsi->turn_arnd_val); + DRM_DEBUG_KMS("Init Count 0x%x\n", intel_dsi->init_count); + DRM_DEBUG_KMS("HS to LP Count 0x%x\n", intel_dsi->hs_to_lp_count); + DRM_DEBUG_KMS("LP Byte Clock %d\n", intel_dsi->lp_byte_clk); + DRM_DEBUG_KMS("DBI BW Timer 0x%x\n", intel_dsi->bw_timer); + DRM_DEBUG_KMS("LP to HS Clock Count 0x%x\n", intel_dsi->clk_lp_to_hs_count); + DRM_DEBUG_KMS("HS to LP Clock Count 0x%x\n", intel_dsi->clk_hs_to_lp_count); + DRM_DEBUG_KMS("BTA %s\n", + intel_dsi->video_frmt_cfg_bits & DISABLE_VIDEO_BTA ? + "disabled" : "enabled"); + + /* delays in VBT are in unit of 100us, so need to convert + * here in ms + * Delay (100us) * 100 /1000 = Delay / 10 (ms) */ + intel_dsi->backlight_off_delay = pps->bl_disable_delay / 10; + intel_dsi->backlight_on_delay = pps->bl_enable_delay / 10; + intel_dsi->panel_on_delay = pps->panel_on_delay / 10; + intel_dsi->panel_off_delay = pps->panel_off_delay / 10; + intel_dsi->panel_pwr_cycle_delay = pps->panel_power_cycle_delay / 10; + + return true; +} + +static int generic_mode_valid(struct intel_dsi_device *dsi, + struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static bool generic_mode_fixup(struct intel_dsi_device *dsi, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) { + return true; +} + +static void generic_panel_reset(struct intel_dsi_device *dsi) +{ + struct intel_dsi *intel_dsi = container_of(dsi, struct intel_dsi, dev); + struct drm_device *dev = intel_dsi->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + char *sequence = dev_priv->vbt.dsi.sequence[MIPI_SEQ_ASSERT_RESET]; + + generic_exec_sequence(intel_dsi, sequence); +} + +static void generic_disable_panel_power(struct intel_dsi_device *dsi) +{ + struct intel_dsi *intel_dsi = container_of(dsi, struct intel_dsi, dev); + struct drm_device *dev = intel_dsi->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + char *sequence = dev_priv->vbt.dsi.sequence[MIPI_SEQ_DEASSERT_RESET]; + + generic_exec_sequence(intel_dsi, sequence); +} + +static void generic_send_otp_cmds(struct intel_dsi_device *dsi) +{ + struct intel_dsi *intel_dsi = container_of(dsi, struct intel_dsi, dev); + struct drm_device *dev = intel_dsi->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + char *sequence = dev_priv->vbt.dsi.sequence[MIPI_SEQ_INIT_OTP]; + + generic_exec_sequence(intel_dsi, sequence); +} + +static void generic_enable(struct intel_dsi_device *dsi) +{ + struct intel_dsi *intel_dsi = container_of(dsi, struct intel_dsi, dev); + struct drm_device *dev = intel_dsi->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + char *sequence = dev_priv->vbt.dsi.sequence[MIPI_SEQ_DISPLAY_ON]; + + generic_exec_sequence(intel_dsi, sequence); +} + +static void generic_disable(struct intel_dsi_device *dsi) +{ + struct intel_dsi *intel_dsi = container_of(dsi, struct intel_dsi, dev); + struct drm_device *dev = intel_dsi->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + char *sequence = dev_priv->vbt.dsi.sequence[MIPI_SEQ_DISPLAY_OFF]; + + generic_exec_sequence(intel_dsi, sequence); +} + +static enum drm_connector_status generic_detect(struct intel_dsi_device *dsi) +{ + return connector_status_connected; +} + +static bool generic_get_hw_state(struct intel_dsi_device *dev) +{ + return true; +} + +static struct drm_display_mode *generic_get_modes(struct intel_dsi_device *dsi) +{ + struct intel_dsi *intel_dsi = container_of(dsi, struct intel_dsi, dev); + struct drm_device *dev = intel_dsi->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + dev_priv->vbt.lfp_lvds_vbt_mode->type |= DRM_MODE_TYPE_PREFERRED; + return dev_priv->vbt.lfp_lvds_vbt_mode; +} + +static void generic_destroy(struct intel_dsi_device *dsi) { } + +/* Callbacks. We might not need them all. */ +struct intel_dsi_dev_ops vbt_generic_dsi_display_ops = { + .init = generic_init, + .mode_valid = generic_mode_valid, + .mode_fixup = generic_mode_fixup, + .panel_reset = generic_panel_reset, + .disable_panel_power = generic_disable_panel_power, + .send_otp_cmds = generic_send_otp_cmds, + .enable = generic_enable, + .disable = generic_disable, + .detect = generic_detect, + .get_hw_state = generic_get_hw_state, + .get_modes = generic_get_modes, + .destroy = generic_destroy, +}; diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index e2d416149f5..088fe9378a4 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -387,6 +387,15 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, height); } + /* No preferred mode marked by the EDID? Are there any modes? */ + if (!modes[i] && !list_empty(&connector->modes)) { + DRM_DEBUG_KMS("using first mode listed on connector %s\n", + connector->name); + modes[i] = list_first_entry(&connector->modes, + struct drm_display_mode, + head); + } + /* last resort: use current mode */ if (!modes[i]) { /* @@ -687,11 +696,7 @@ void intel_fbdev_restore_mode(struct drm_device *dev) if (!dev_priv->fbdev) return; - drm_modeset_lock_all(dev); - - ret = drm_fb_helper_restore_fbdev_mode(&dev_priv->fbdev->helper); + ret = drm_fb_helper_restore_fbdev_mode_unlocked(&dev_priv->fbdev->helper); if (ret) DRM_DEBUG("failed to restore crtc mode\n"); - - drm_modeset_unlock_all(dev); } diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 23126023aeb..5e5a72fca5f 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -111,6 +111,13 @@ static void intel_lvds_get_config(struct intel_encoder *encoder, pipe_config->adjusted_mode.flags |= flags; + /* gen2/3 store dither state in pfit control, needs to match */ + if (INTEL_INFO(dev)->gen < 4) { + tmp = I915_READ(PFIT_CONTROL); + + pipe_config->gmch_pfit.control |= tmp & PANEL_8TO6_DITHER_ENABLE; + } + dotclock = pipe_config->port_clock; if (HAS_PCH_SPLIT(dev_priv->dev)) diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index b812e9d39f3..4f6b53998d7 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -403,6 +403,15 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) DRM_DEBUG_DRIVER("bclp = 0x%08x\n", bclp); + /* + * If the acpi_video interface is not supposed to be used, don't + * bother processing backlight level change requests from firmware. + */ + if (!acpi_video_verify_backlight_support()) { + DRM_DEBUG_KMS("opregion backlight request ignored\n"); + return 0; + } + if (!(bclp & ASLE_BCLP_VALID)) return ASLC_BACKLIGHT_FAILED; @@ -410,7 +419,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) if (bclp > 255) return ASLC_BACKLIGHT_FAILED; - mutex_lock(&dev->mode_config.connection_mutex); + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); /* * Update backlight on all connectors that support backlight (usually @@ -421,7 +430,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) intel_panel_set_backlight(intel_connector, bclp, 255); iowrite32(DIV_ROUND_UP(bclp * 100, 255) | ASLE_CBLV_VALID, &asle->cblv); - mutex_unlock(&dev->mode_config.connection_mutex); + drm_modeset_unlock(&dev->mode_config.connection_mutex); return 0; diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index e97ea33e011..daa118978ee 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -193,7 +193,7 @@ intel_overlay_map_regs(struct intel_overlay *overlay) struct overlay_registers __iomem *regs; if (OVERLAY_NEEDS_PHYSICAL(overlay->dev)) - regs = (struct overlay_registers __iomem *)overlay->reg_bo->phys_obj->handle->vaddr; + regs = (struct overlay_registers __iomem *)overlay->reg_bo->phys_handle->vaddr; else regs = io_mapping_map_wc(dev_priv->gtt.mappable, i915_gem_obj_ggtt_offset(overlay->reg_bo)); @@ -688,7 +688,7 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay, u32 swidth, swidthsw, sheight, ostride; BUG_ON(!mutex_is_locked(&dev->struct_mutex)); - BUG_ON(!mutex_is_locked(&dev->mode_config.connection_mutex)); + BUG_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); BUG_ON(!overlay); ret = intel_overlay_release_old_vid(overlay); @@ -793,7 +793,7 @@ int intel_overlay_switch_off(struct intel_overlay *overlay) int ret; BUG_ON(!mutex_is_locked(&dev->struct_mutex)); - BUG_ON(!mutex_is_locked(&dev->mode_config.connection_mutex)); + BUG_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); ret = intel_overlay_recover_from_interrupt(overlay); if (ret != 0) @@ -1340,14 +1340,12 @@ void intel_setup_overlay(struct drm_device *dev) overlay->reg_bo = reg_bo; if (OVERLAY_NEEDS_PHYSICAL(dev)) { - ret = i915_gem_attach_phys_object(dev, reg_bo, - I915_GEM_PHYS_OVERLAY_REGS, - PAGE_SIZE); + ret = i915_gem_object_attach_phys(reg_bo, PAGE_SIZE); if (ret) { DRM_ERROR("failed to attach phys overlay regs\n"); goto out_free_bo; } - overlay->flip_addr = reg_bo->phys_obj->handle->busaddr; + overlay->flip_addr = reg_bo->phys_handle->busaddr; } else { ret = i915_gem_obj_ggtt_pin(reg_bo, PAGE_SIZE, PIN_MAPPABLE); if (ret) { @@ -1428,7 +1426,7 @@ intel_overlay_map_regs_atomic(struct intel_overlay *overlay) /* Cast to make sparse happy, but it's wc memory anyway, so * equivalent to the wc io mapping on X86. */ regs = (struct overlay_registers __iomem *) - overlay->reg_bo->phys_obj->handle->vaddr; + overlay->reg_bo->phys_handle->vaddr; else regs = io_mapping_map_atomic_wc(dev_priv->gtt.mappable, i915_gem_obj_ggtt_offset(overlay->reg_bo)); @@ -1462,7 +1460,7 @@ intel_overlay_capture_error_state(struct drm_device *dev) error->dovsta = I915_READ(DOVSTA); error->isr = I915_READ(ISR); if (OVERLAY_NEEDS_PHYSICAL(overlay->dev)) - error->base = (__force long)overlay->reg_bo->phys_obj->handle->vaddr; + error->base = (__force long)overlay->reg_bo->phys_handle->vaddr; else error->base = i915_gem_obj_ggtt_offset(overlay->reg_bo); diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index d4d41566547..12b02fe1d0a 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -361,16 +361,16 @@ void intel_gmch_panel_fitting(struct intel_crtc *intel_crtc, pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) | PFIT_FILTER_FUZZY); - /* Make sure pre-965 set dither correctly for 18bpp panels. */ - if (INTEL_INFO(dev)->gen < 4 && pipe_config->pipe_bpp == 18) - pfit_control |= PANEL_8TO6_DITHER_ENABLE; - out: if ((pfit_control & PFIT_ENABLE) == 0) { pfit_control = 0; pfit_pgm_ratios = 0; } + /* Make sure pre-965 set dither correctly for 18bpp panels. */ + if (INTEL_INFO(dev)->gen < 4 && pipe_config->pipe_bpp == 18) + pfit_control |= PANEL_8TO6_DITHER_ENABLE; + pipe_config->gmch_pfit.control = pfit_control; pipe_config->gmch_pfit.pgm_ratios = pfit_pgm_ratios; pipe_config->gmch_pfit.lvds_border_bits = border; @@ -567,6 +567,7 @@ void intel_panel_set_backlight(struct intel_connector *connector, u32 level, enum pipe pipe = intel_get_pipe_from_connector(connector); u32 freq; unsigned long flags; + u64 n; if (!panel->backlight.present || pipe == INVALID_PIPE) return; @@ -577,10 +578,9 @@ void intel_panel_set_backlight(struct intel_connector *connector, u32 level, /* scale to hardware max, but be careful to not overflow */ freq = panel->backlight.max; - if (freq < max) - level = level * freq / max; - else - level = freq / max * level; + n = (u64)level * freq; + do_div(n, max); + level = n; panel->backlight.level = level; if (panel->backlight.device) @@ -798,9 +798,6 @@ static void i965_enable_backlight(struct intel_connector *connector) ctl = freq << 16; I915_WRITE(BLC_PWM_CTL, ctl); - /* XXX: combine this into above write? */ - intel_panel_actually_set_backlight(connector, panel->backlight.level); - ctl2 = BLM_PIPE(pipe); if (panel->backlight.combination_mode) ctl2 |= BLM_COMBINATION_MODE; @@ -809,6 +806,8 @@ static void i965_enable_backlight(struct intel_connector *connector) I915_WRITE(BLC_PWM_CTL2, ctl2); POSTING_READ(BLC_PWM_CTL2); I915_WRITE(BLC_PWM_CTL2, ctl2 | BLM_PWM_ENABLE); + + intel_panel_actually_set_backlight(connector, panel->backlight.level); } static void vlv_enable_backlight(struct intel_connector *connector) @@ -876,12 +875,12 @@ static int intel_backlight_device_update_status(struct backlight_device *bd) struct intel_connector *connector = bl_get_data(bd); struct drm_device *dev = connector->base.dev; - mutex_lock(&dev->mode_config.connection_mutex); + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); DRM_DEBUG_KMS("updating intel_backlight, brightness=%d/%d\n", bd->props.brightness, bd->props.max_brightness); intel_panel_set_backlight(connector, bd->props.brightness, bd->props.max_brightness); - mutex_unlock(&dev->mode_config.connection_mutex); + drm_modeset_unlock(&dev->mode_config.connection_mutex); return 0; } @@ -893,9 +892,9 @@ static int intel_backlight_device_get_brightness(struct backlight_device *bd) int ret; intel_runtime_pm_get(dev_priv); - mutex_lock(&dev->mode_config.connection_mutex); + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); ret = intel_panel_get_backlight(connector); - mutex_unlock(&dev->mode_config.connection_mutex); + drm_modeset_unlock(&dev->mode_config.connection_mutex); intel_runtime_pm_put(dev_priv); return ret; @@ -1119,8 +1118,12 @@ int intel_panel_setup_backlight(struct drm_connector *connector) int ret; if (!dev_priv->vbt.backlight.present) { - DRM_DEBUG_KMS("native backlight control not available per VBT\n"); - return 0; + if (dev_priv->quirks & QUIRK_BACKLIGHT_PRESENT) { + DRM_DEBUG_KMS("no backlight present per VBT, but present per quirk\n"); + } else { + DRM_DEBUG_KMS("no backlight present per VBT\n"); + return 0; + } } /* set level and max in panel struct */ diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index b86b58c4422..ee72807069e 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -511,8 +511,7 @@ void intel_update_fbc(struct drm_device *dev) obj = intel_fb->obj; adjusted_mode = &intel_crtc->config.adjusted_mode; - if (i915.enable_fbc < 0 && - INTEL_INFO(dev)->gen <= 7 && !IS_HASWELL(dev)) { + if (i915.enable_fbc < 0) { if (set_no_fbc_reason(dev_priv, FBC_CHIP_DEFAULT)) DRM_DEBUG_KMS("disabled per chip default\n"); goto out_disable; @@ -2113,6 +2112,43 @@ static void intel_print_wm_latency(struct drm_device *dev, } } +static bool ilk_increase_wm_latency(struct drm_i915_private *dev_priv, + uint16_t wm[5], uint16_t min) +{ + int level, max_level = ilk_wm_max_level(dev_priv->dev); + + if (wm[0] >= min) + return false; + + wm[0] = max(wm[0], min); + for (level = 1; level <= max_level; level++) + wm[level] = max_t(uint16_t, wm[level], DIV_ROUND_UP(min, 5)); + + return true; +} + +static void snb_wm_latency_quirk(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + bool changed; + + /* + * The BIOS provided WM memory latency values are often + * inadequate for high resolution displays. Adjust them. + */ + changed = ilk_increase_wm_latency(dev_priv, dev_priv->wm.pri_latency, 12) | + ilk_increase_wm_latency(dev_priv, dev_priv->wm.spr_latency, 12) | + ilk_increase_wm_latency(dev_priv, dev_priv->wm.cur_latency, 12); + + if (!changed) + return; + + DRM_DEBUG_KMS("WM latency values increased to avoid potential underruns\n"); + intel_print_wm_latency(dev, "Primary", dev_priv->wm.pri_latency); + intel_print_wm_latency(dev, "Sprite", dev_priv->wm.spr_latency); + intel_print_wm_latency(dev, "Cursor", dev_priv->wm.cur_latency); +} + static void ilk_setup_wm_latency(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -2130,6 +2166,9 @@ static void ilk_setup_wm_latency(struct drm_device *dev) intel_print_wm_latency(dev, "Primary", dev_priv->wm.pri_latency); intel_print_wm_latency(dev, "Sprite", dev_priv->wm.spr_latency); intel_print_wm_latency(dev, "Cursor", dev_priv->wm.cur_latency); + + if (IS_GEN6(dev)) + snb_wm_latency_quirk(dev); } static void ilk_compute_wm_parameters(struct drm_crtc *crtc, @@ -3170,6 +3209,14 @@ void gen6_set_rps(struct drm_device *dev, u8 val) */ static void vlv_set_rps_idle(struct drm_i915_private *dev_priv) { + struct drm_device *dev = dev_priv->dev; + + /* Latest VLV doesn't need to force the gfx clock */ + if (dev->pdev->revision >= 0xd) { + valleyview_set_rps(dev_priv->dev, dev_priv->rps.min_freq_softlimit); + return; + } + /* * When we are idle. Drop to min voltage state. */ @@ -3466,15 +3513,11 @@ static void gen8_enable_rps(struct drm_device *dev) I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10); - /* WaDisablePwrmtrEvent:chv (pre-production hw) */ - I915_WRITE(0xA80C, I915_READ(0xA80C) & 0x00ffffff); - I915_WRITE(0xA810, I915_READ(0xA810) & 0xffffff00); - /* 5: Enable RPS */ I915_WRITE(GEN6_RP_CONTROL, GEN6_RP_MEDIA_TURBO | GEN6_RP_MEDIA_HW_NORMAL_MODE | - GEN6_RP_MEDIA_IS_GFX | /* WaSetMaskForGfxBusyness:chv (pre-production hw ?) */ + GEN6_RP_MEDIA_IS_GFX | GEN6_RP_ENABLE | GEN6_RP_UP_BUSY_AVG | GEN6_RP_DOWN_IDLE_AVG); @@ -5348,8 +5391,11 @@ static void valleyview_init_clock_gating(struct drm_device *dev) I915_WRITE(GEN6_UCGCTL2, GEN6_RCZUNIT_CLOCK_GATE_DISABLE); - /* WaDisableL3Bank2xClockGate:vlv */ - I915_WRITE(GEN7_UCGCTL4, GEN7_L3BANK2X_CLOCK_GATE_DISABLE); + /* WaDisableL3Bank2xClockGate:vlv + * Disabling L3 clock gating- MMIO 940c[25] = 1 + * Set bit 25, to disable L3_BANK_2x_CLK_GATING */ + I915_WRITE(GEN7_UCGCTL4, + I915_READ(GEN7_UCGCTL4) | GEN7_L3BANK2X_CLOCK_GATE_DISABLE); I915_WRITE(MI_ARB_VLV, MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE); @@ -5501,6 +5547,12 @@ static void gen3_init_clock_gating(struct drm_device *dev) /* IIR "flip pending" means done if this bit is set */ I915_WRITE(ECOSKPD, _MASKED_BIT_DISABLE(ECO_FLIP_DONE)); + + /* interrupts should cause a wake up from C3 */ + I915_WRITE(INSTPM, _MASKED_BIT_ENABLE(INSTPM_AGPBUSY_INT_EN)); + + /* On GEN3 we really need to make sure the ARB C3 LP bit is set */ + I915_WRITE(MI_ARB_STATE, _MASKED_BIT_ENABLE(MI_ARB_C3_LP_WRITE_ENABLE)); } static void i85x_init_clock_gating(struct drm_device *dev) @@ -5508,6 +5560,10 @@ static void i85x_init_clock_gating(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; I915_WRITE(RENCLK_GATE_D1, SV_CLOCK_GATE_DISABLE); + + /* interrupts should cause a wake up from C3 */ + I915_WRITE(MI_STATE, _MASKED_BIT_ENABLE(MI_AGPBUSY_INT_EN) | + _MASKED_BIT_DISABLE(MI_AGPBUSY_830_MODE)); } static void i830_init_clock_gating(struct drm_device *dev) @@ -5555,18 +5611,8 @@ static bool hsw_power_well_enabled(struct drm_i915_private *dev_priv, (HSW_PWR_WELL_ENABLE_REQUEST | HSW_PWR_WELL_STATE_ENABLED); } -bool intel_display_power_enabled_sw(struct drm_i915_private *dev_priv, - enum intel_display_power_domain domain) -{ - struct i915_power_domains *power_domains; - - power_domains = &dev_priv->power_domains; - - return power_domains->domain_use_count[domain]; -} - -bool intel_display_power_enabled(struct drm_i915_private *dev_priv, - enum intel_display_power_domain domain) +bool intel_display_power_enabled_unlocked(struct drm_i915_private *dev_priv, + enum intel_display_power_domain domain) { struct i915_power_domains *power_domains; struct i915_power_well *power_well; @@ -5580,21 +5626,34 @@ bool intel_display_power_enabled(struct drm_i915_private *dev_priv, is_enabled = true; - mutex_lock(&power_domains->lock); for_each_power_well_rev(i, power_well, BIT(domain), power_domains) { if (power_well->always_on) continue; - if (!power_well->ops->is_enabled(dev_priv, power_well)) { + if (!power_well->hw_enabled) { is_enabled = false; break; } } - mutex_unlock(&power_domains->lock); return is_enabled; } +bool intel_display_power_enabled(struct drm_i915_private *dev_priv, + enum intel_display_power_domain domain) +{ + struct i915_power_domains *power_domains; + bool ret; + + power_domains = &dev_priv->power_domains; + + mutex_lock(&power_domains->lock); + ret = intel_display_power_enabled_unlocked(dev_priv, domain); + mutex_unlock(&power_domains->lock); + + return ret; +} + /* * Starting with Haswell, we have a "Power Down Well" that can be turned off * when not needed anymore. We have 4 registers that can request the power well @@ -5705,13 +5764,34 @@ static bool i9xx_always_on_power_well_enabled(struct drm_i915_private *dev_priv, return true; } -static void vlv_set_power_well(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well, bool enable) +void __vlv_set_power_well(struct drm_i915_private *dev_priv, + enum punit_power_well power_well_id, bool enable) { - enum punit_power_well power_well_id = power_well->data; + struct drm_device *dev = dev_priv->dev; u32 mask; u32 state; u32 ctrl; + enum pipe pipe; + + if (power_well_id == PUNIT_POWER_WELL_DPIO_CMN_BC) { + if (enable) { + /* + * Enable the CRI clock source so we can get at the + * display and the reference clock for VGA + * hotplug / manual detection. + */ + I915_WRITE(DPLL(PIPE_B), I915_READ(DPLL(PIPE_B)) | + DPLL_REFA_CLK_ENABLE_VLV | + DPLL_INTEGRATED_CRI_CLK_VLV); + udelay(1); /* >10ns for cmnreset, >0ns for sidereset */ + } else { + for_each_pipe(pipe) + assert_pll_disabled(dev_priv, pipe); + /* Assert common reset */ + I915_WRITE(DPIO_CTL, I915_READ(DPIO_CTL) & + ~DPIO_CMNRST); + } + } mask = PUNIT_PWRGT_MASK(power_well_id); state = enable ? PUNIT_PWRGT_PWR_ON(power_well_id) : @@ -5739,6 +5819,28 @@ static void vlv_set_power_well(struct drm_i915_private *dev_priv, out: mutex_unlock(&dev_priv->rps.hw_lock); + + /* + * From VLV2A0_DP_eDP_DPIO_driver_vbios_notes_10.docx - + * 6. De-assert cmn_reset/side_reset. Same as VLV X0. + * a. GUnit 0x2110 bit[0] set to 1 (def 0) + * b. The other bits such as sfr settings / modesel may all + * be set to 0. + * + * This should only be done on init and resume from S3 with + * both PLLs disabled, or we risk losing DPIO and PLL + * synchronization. + */ + if (power_well_id == PUNIT_POWER_WELL_DPIO_CMN_BC && enable) + I915_WRITE(DPIO_CTL, I915_READ(DPIO_CTL) | DPIO_CMNRST); +} + +static void vlv_set_power_well(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well, bool enable) +{ + enum punit_power_well power_well_id = power_well->data; + + __vlv_set_power_well(dev_priv, power_well_id, enable); } static void vlv_power_well_sync_hw(struct drm_i915_private *dev_priv, @@ -5870,6 +5972,7 @@ void intel_display_power_get(struct drm_i915_private *dev_priv, if (!power_well->count++) { DRM_DEBUG_KMS("enabling %s\n", power_well->name); power_well->ops->enable(dev_priv, power_well); + power_well->hw_enabled = true; } check_power_well_state(dev_priv, power_well); @@ -5899,6 +6002,7 @@ void intel_display_power_put(struct drm_i915_private *dev_priv, if (!--power_well->count && i915.disable_power_well) { DRM_DEBUG_KMS("disabling %s\n", power_well->name); + power_well->hw_enabled = false; power_well->ops->disable(dev_priv, power_well); } @@ -5913,33 +6017,56 @@ void intel_display_power_put(struct drm_i915_private *dev_priv, static struct i915_power_domains *hsw_pwr; /* Display audio driver power well request */ -void i915_request_power_well(void) +int i915_request_power_well(void) { struct drm_i915_private *dev_priv; - if (WARN_ON(!hsw_pwr)) - return; + if (!hsw_pwr) + return -ENODEV; dev_priv = container_of(hsw_pwr, struct drm_i915_private, power_domains); intel_display_power_get(dev_priv, POWER_DOMAIN_AUDIO); + return 0; } EXPORT_SYMBOL_GPL(i915_request_power_well); /* Display audio driver power well release */ -void i915_release_power_well(void) +int i915_release_power_well(void) { struct drm_i915_private *dev_priv; - if (WARN_ON(!hsw_pwr)) - return; + if (!hsw_pwr) + return -ENODEV; dev_priv = container_of(hsw_pwr, struct drm_i915_private, power_domains); intel_display_power_put(dev_priv, POWER_DOMAIN_AUDIO); + return 0; } EXPORT_SYMBOL_GPL(i915_release_power_well); +/* + * Private interface for the audio driver to get CDCLK in kHz. + * + * Caller must request power well using i915_request_power_well() prior to + * making the call. + */ +int i915_get_cdclk_freq(void) +{ + struct drm_i915_private *dev_priv; + + if (!hsw_pwr) + return -ENODEV; + + dev_priv = container_of(hsw_pwr, struct drm_i915_private, + power_domains); + + return intel_ddi_get_cdclk_freq(dev_priv); +} +EXPORT_SYMBOL_GPL(i915_get_cdclk_freq); + + #define POWER_DOMAIN_MASK (BIT(POWER_DOMAIN_NUM) - 1) #define HSW_ALWAYS_ON_POWER_DOMAINS ( \ @@ -6074,12 +6201,6 @@ static struct i915_power_well vlv_power_wells[] = { .ops = &vlv_display_power_well_ops, }, { - .name = "dpio-common", - .domains = VLV_DPIO_CMN_BC_POWER_DOMAINS, - .data = PUNIT_POWER_WELL_DPIO_CMN_BC, - .ops = &vlv_dpio_power_well_ops, - }, - { .name = "dpio-tx-b-01", .domains = VLV_DPIO_TX_B_LANES_01_POWER_DOMAINS | VLV_DPIO_TX_B_LANES_23_POWER_DOMAINS | @@ -6115,6 +6236,12 @@ static struct i915_power_well vlv_power_wells[] = { .ops = &vlv_dpio_power_well_ops, .data = PUNIT_POWER_WELL_DPIO_TX_C_LANES_23, }, + { + .name = "dpio-common", + .domains = VLV_DPIO_CMN_BC_POWER_DOMAINS, + .data = PUNIT_POWER_WELL_DPIO_CMN_BC, + .ops = &vlv_dpio_power_well_ops, + }, }; #define set_power_wells(power_domains, __power_wells) ({ \ @@ -6159,8 +6286,11 @@ static void intel_power_domains_resume(struct drm_i915_private *dev_priv) int i; mutex_lock(&power_domains->lock); - for_each_power_well(i, power_well, POWER_DOMAIN_MASK, power_domains) + for_each_power_well(i, power_well, POWER_DOMAIN_MASK, power_domains) { power_well->ops->sync_hw(dev_priv, power_well); + power_well->hw_enabled = power_well->ops->is_enabled(dev_priv, + power_well); + } mutex_unlock(&power_domains->lock); } diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 3379722d0e6..279488addf3 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1494,7 +1494,7 @@ void intel_cleanup_ring_buffer(struct intel_engine_cs *ring) return; intel_stop_ring_buffer(ring); - WARN_ON((I915_READ_MODE(ring) & MODE_IDLE) == 0); + WARN_ON(!IS_GEN2(ring->dev) && (I915_READ_MODE(ring) & MODE_IDLE) == 0); iounmap(ringbuf->virtual_start); diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 910c83cf7d4..e72017bdcd7 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -55,7 +55,7 @@ struct intel_ring_hangcheck { u32 seqno; int score; enum intel_ring_hangcheck_action action; - bool deadlock; + int deadlock; }; struct intel_ringbuffer { diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 6a4d5bc1769..20375cc7f82 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1385,7 +1385,9 @@ static void intel_sdvo_get_config(struct intel_encoder *encoder, >> SDVO_PORT_MULTIPLY_SHIFT) + 1; } - dotclock = pipe_config->port_clock / pipe_config->pixel_multiplier; + dotclock = pipe_config->port_clock; + if (pipe_config->pixel_multiplier) + dotclock /= pipe_config->pixel_multiplier; if (HAS_PCH_SPLIT(dev)) ironlake_check_encoder_dotclock(pipe_config, dotclock); diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index d6acd6bd0bf..9a17b4e92ef 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -55,7 +55,7 @@ static bool intel_pipe_update_start(struct intel_crtc *crtc, uint32_t *start_vbl int scanline, min, max, vblank_start; DEFINE_WAIT(wait); - WARN_ON(!mutex_is_locked(&crtc->base.mutex)); + WARN_ON(!drm_modeset_is_locked(&crtc->base.mutex)); vblank_start = mode->crtc_vblank_start; if (mode->flags & DRM_MODE_FLAG_INTERLACE) @@ -691,6 +691,14 @@ intel_post_enable_primary(struct drm_crtc *crtc) struct intel_crtc *intel_crtc = to_intel_crtc(crtc); /* + * BDW signals flip done immediately if the plane + * is disabled, even if the plane enable is already + * armed to occur at the next vblank :( + */ + if (IS_BROADWELL(dev)) + intel_wait_for_vblank(dev, intel_crtc->pipe); + + /* * FIXME IPS should be fine as long as one plane is * enabled, but in practice it seems to have problems * when going from primary only to sprite only and vice diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 25850a86d70..67c6c9a2eb1 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1321,10 +1321,11 @@ intel_tv_detect(struct drm_connector *connector, bool force) if (force) { struct intel_load_detect_pipe tmp; + struct drm_modeset_acquire_ctx ctx; - if (intel_get_load_detect_pipe(connector, &mode, &tmp)) { + if (intel_get_load_detect_pipe(connector, &mode, &tmp, &ctx)) { type = intel_tv_detect_type(intel_tv, connector); - intel_release_load_detect_pipe(connector, &tmp); + intel_release_load_detect_pipe(connector, &tmp, &ctx); } else return connector_status_unknown; } else diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index 9cd99d9676f..4f6fef7ac06 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -185,6 +185,8 @@ static void vlv_force_wake_reset(struct drm_i915_private *dev_priv) { __raw_i915_write32(dev_priv, FORCEWAKE_VLV, _MASKED_BIT_DISABLE(0xffff)); + __raw_i915_write32(dev_priv, FORCEWAKE_MEDIA_VLV, + _MASKED_BIT_DISABLE(0xffff)); /* something from same cacheline, but !FORCEWAKE_VLV */ __raw_posting_read(dev_priv, FORCEWAKE_ACK_VLV); } @@ -318,7 +320,8 @@ static void intel_uncore_forcewake_reset(struct drm_device *dev, bool restore) struct drm_i915_private *dev_priv = dev->dev_private; unsigned long irqflags; - del_timer_sync(&dev_priv->uncore.force_wake_timer); + if (del_timer_sync(&dev_priv->uncore.force_wake_timer)) + gen6_force_wake_timer((unsigned long)dev_priv); /* Hold uncore.lock across reset to prevent any register access * with forcewake not set correctly @@ -391,26 +394,8 @@ void intel_uncore_early_sanitize(struct drm_device *dev) void intel_uncore_sanitize(struct drm_device *dev) { - struct drm_i915_private *dev_priv = dev->dev_private; - u32 reg_val; - /* BIOS often leaves RC6 enabled, but disable it for hw init */ intel_disable_gt_powersave(dev); - - /* Turn off power gate, require especially for the BIOS less system */ - if (IS_VALLEYVIEW(dev)) { - - mutex_lock(&dev_priv->rps.hw_lock); - reg_val = vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_STATUS); - - if (reg_val & (PUNIT_PWRGT_PWR_GATE(PUNIT_POWER_WELL_RENDER) | - PUNIT_PWRGT_PWR_GATE(PUNIT_POWER_WELL_MEDIA) | - PUNIT_PWRGT_PWR_GATE(PUNIT_POWER_WELL_DISP2D))) - vlv_punit_write(dev_priv, PUNIT_REG_PWRGT_CTRL, 0x0); - - mutex_unlock(&dev_priv->rps.hw_lock); - - } } /* @@ -965,6 +950,9 @@ static int i965_do_reset(struct drm_device *dev) { int ret; + /* FIXME: i965g/gm need a display save/restore for gpu reset. */ + return -ENODEV; + /* * Set the domains we want to reset (GRDOM/bits 2 and 3) as * well as the reset bit (GR/bit 0). Setting the GR bit diff --git a/drivers/gpu/drm/mga/mga_ioc32.c b/drivers/gpu/drm/mga/mga_ioc32.c index 86b4bb80485..729bfd56b55 100644 --- a/drivers/gpu/drm/mga/mga_ioc32.c +++ b/drivers/gpu/drm/mga/mga_ioc32.c @@ -214,7 +214,7 @@ long mga_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (nr < DRM_COMMAND_BASE) return drm_compat_ioctl(filp, cmd, arg); - if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(mga_compat_ioctls)) + if (nr < DRM_COMMAND_BASE + ARRAY_SIZE(mga_compat_ioctls)) fn = mga_compat_ioctls[nr - DRM_COMMAND_BASE]; if (fn != NULL) diff --git a/drivers/gpu/drm/mga/mga_state.c b/drivers/gpu/drm/mga/mga_state.c index 3cb58df5237..792f924496f 100644 --- a/drivers/gpu/drm/mga/mga_state.c +++ b/drivers/gpu/drm/mga/mga_state.c @@ -1099,4 +1099,4 @@ const struct drm_ioctl_desc mga_ioctls[] = { DRM_IOCTL_DEF_DRV(MGA_DMA_BOOTSTRAP, mga_dma_bootstrap, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), }; -int mga_max_ioctl = DRM_ARRAY_SIZE(mga_ioctls); +int mga_max_ioctl = ARRAY_SIZE(mga_ioctls); diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index ae750f6928c..7f7aadef8a8 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -277,6 +277,7 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data) static const char *hpd_reg_names[] = {"hpd-gdsc", "hpd-5v"}; static const char *pwr_reg_names[] = {"core-vdda", "core-vcc"}; static const char *hpd_clk_names[] = {"iface_clk", "core_clk", "mdp_core_clk"}; + static unsigned long hpd_clk_freq[] = {0, 19200000, 0}; static const char *pwr_clk_names[] = {"extp_clk", "alt_iface_clk"}; config.phy_init = hdmi_phy_8x74_init; @@ -286,6 +287,7 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data) config.pwr_reg_names = pwr_reg_names; config.pwr_reg_cnt = ARRAY_SIZE(pwr_reg_names); config.hpd_clk_names = hpd_clk_names; + config.hpd_freq = hpd_clk_freq; config.hpd_clk_cnt = ARRAY_SIZE(hpd_clk_names); config.pwr_clk_names = pwr_clk_names; config.pwr_clk_cnt = ARRAY_SIZE(pwr_clk_names); diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h index 9fafee6a3e4..9d7723c6528 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.h @@ -87,6 +87,7 @@ struct hdmi_platform_config { /* clks that need to be on for hpd: */ const char **hpd_clk_names; + const long unsigned *hpd_freq; int hpd_clk_cnt; /* clks that need to be on for screen pwr (ie pixel clk): */ diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c index e56a6196867..28f7e3ec6c2 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c @@ -127,6 +127,14 @@ static int hpd_enable(struct hdmi_connector *hdmi_connector) } for (i = 0; i < config->hpd_clk_cnt; i++) { + if (config->hpd_freq && config->hpd_freq[i]) { + ret = clk_set_rate(hdmi->hpd_clks[i], + config->hpd_freq[i]); + if (ret) + dev_warn(dev->dev, "failed to set clk %s (%d)\n", + config->hpd_clk_names[i], ret); + } + ret = clk_prepare_enable(hdmi->hpd_clks[i]); if (ret) { dev_err(dev->dev, "failed to enable hpd clk: %s (%d)\n", diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c index 42caf7fcb0b..71510ee26e9 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c @@ -20,6 +20,10 @@ #include "msm_mmu.h" #include "mdp5_kms.h" +static const char *iommu_ports[] = { + "mdp_0", +}; + static struct mdp5_platform_config *mdp5_get_config(struct platform_device *dev); static int mdp5_hw_init(struct msm_kms *kms) @@ -104,6 +108,12 @@ static void mdp5_preclose(struct msm_kms *kms, struct drm_file *file) static void mdp5_destroy(struct msm_kms *kms) { struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); + struct msm_mmu *mmu = mdp5_kms->mmu; + + if (mmu) { + mmu->funcs->detach(mmu, iommu_ports, ARRAY_SIZE(iommu_ports)); + mmu->funcs->destroy(mmu); + } kfree(mdp5_kms); } @@ -216,10 +226,6 @@ fail: return ret; } -static const char *iommu_ports[] = { - "mdp_0", -}; - static int get_clk(struct platform_device *pdev, struct clk **clkp, const char *name) { @@ -317,17 +323,23 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev) mmu = msm_iommu_new(dev, config->iommu); if (IS_ERR(mmu)) { ret = PTR_ERR(mmu); + dev_err(dev->dev, "failed to init iommu: %d\n", ret); goto fail; } + ret = mmu->funcs->attach(mmu, iommu_ports, ARRAY_SIZE(iommu_ports)); - if (ret) + if (ret) { + dev_err(dev->dev, "failed to attach iommu: %d\n", ret); + mmu->funcs->destroy(mmu); goto fail; + } } else { dev_info(dev->dev, "no iommu, fallback to phys " "contig buffers for scanout\n"); mmu = NULL; } + mdp5_kms->mmu = mmu; mdp5_kms->id = msm_register_mmu(dev, mmu); if (mdp5_kms->id < 0) { diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h index c8b1a2522c2..6e981b692d1 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h @@ -33,6 +33,7 @@ struct mdp5_kms { /* mapper-id used to request GEM buffer mapped for scanout: */ int id; + struct msm_mmu *mmu; /* for tracking smp allocation amongst pipes: */ mdp5_smp_state_t smp_state; diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index c071aacf275..9a5d87db5c2 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -159,7 +159,7 @@ static int msm_unload(struct drm_device *dev) static int get_mdp_ver(struct platform_device *pdev) { #ifdef CONFIG_OF - const static struct of_device_id match_types[] = { { + static const struct of_device_id match_types[] = { { .compatible = "qcom,mdss_mdp", .data = (void *)5, }, { @@ -386,11 +386,8 @@ static void msm_preclose(struct drm_device *dev, struct drm_file *file) static void msm_lastclose(struct drm_device *dev) { struct msm_drm_private *priv = dev->dev_private; - if (priv->fbdev) { - drm_modeset_lock_all(dev); - drm_fb_helper_restore_fbdev_mode(priv->fbdev); - drm_modeset_unlock_all(dev); - } + if (priv->fbdev) + drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev); } static irqreturn_t msm_irq(int irq, void *arg) diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c index a752ab83b81..5107fc4826b 100644 --- a/drivers/gpu/drm/msm/msm_fbdev.c +++ b/drivers/gpu/drm/msm/msm_fbdev.c @@ -59,7 +59,7 @@ static int msm_fbdev_create(struct drm_fb_helper *helper, struct drm_framebuffer *fb = NULL; struct fb_info *fbi = NULL; struct drm_mode_fb_cmd2 mode_cmd = {0}; - dma_addr_t paddr; + uint32_t paddr; int ret, size; sizes->surface_bpp = 32; diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index bb8026daebc..690d7e7b6d1 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -278,6 +278,7 @@ int msm_gem_get_iova_locked(struct drm_gem_object *obj, int id, uint32_t *iova) { struct msm_gem_object *msm_obj = to_msm_bo(obj); + struct drm_device *dev = obj->dev; int ret = 0; if (!msm_obj->domain[id].iova) { @@ -285,6 +286,11 @@ int msm_gem_get_iova_locked(struct drm_gem_object *obj, int id, struct msm_mmu *mmu = priv->mmus[id]; struct page **pages = get_pages(obj); + if (!mmu) { + dev_err(dev->dev, "null MMU pointer\n"); + return -EINVAL; + } + if (IS_ERR(pages)) return PTR_ERR(pages); diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c index 92b74598623..4b2ad9181ed 100644 --- a/drivers/gpu/drm/msm/msm_iommu.c +++ b/drivers/gpu/drm/msm/msm_iommu.c @@ -28,7 +28,7 @@ static int msm_fault_handler(struct iommu_domain *iommu, struct device *dev, unsigned long iova, int flags, void *arg) { DBG("*** fault: iova=%08lx, flags=%d", iova, flags); - return 0; + return -ENOSYS; } static int msm_iommu_attach(struct msm_mmu *mmu, const char **names, int cnt) @@ -40,8 +40,10 @@ static int msm_iommu_attach(struct msm_mmu *mmu, const char **names, int cnt) for (i = 0; i < cnt; i++) { struct device *msm_iommu_get_ctx(const char *ctx_name); struct device *ctx = msm_iommu_get_ctx(names[i]); - if (IS_ERR_OR_NULL(ctx)) + if (IS_ERR_OR_NULL(ctx)) { + dev_warn(dev->dev, "couldn't get %s context", names[i]); continue; + } ret = iommu_attach_device(iommu->domain, ctx); if (ret) { dev_warn(dev->dev, "could not attach iommu to %s", names[i]); @@ -52,6 +54,20 @@ static int msm_iommu_attach(struct msm_mmu *mmu, const char **names, int cnt) return 0; } +static void msm_iommu_detach(struct msm_mmu *mmu, const char **names, int cnt) +{ + struct msm_iommu *iommu = to_msm_iommu(mmu); + int i; + + for (i = 0; i < cnt; i++) { + struct device *msm_iommu_get_ctx(const char *ctx_name); + struct device *ctx = msm_iommu_get_ctx(names[i]); + if (IS_ERR_OR_NULL(ctx)) + continue; + iommu_detach_device(iommu->domain, ctx); + } +} + static int msm_iommu_map(struct msm_mmu *mmu, uint32_t iova, struct sg_table *sgt, unsigned len, int prot) { @@ -110,7 +126,7 @@ static int msm_iommu_unmap(struct msm_mmu *mmu, uint32_t iova, VERB("unmap[%d]: %08x(%x)", i, iova, bytes); - BUG_ON(!IS_ALIGNED(bytes, PAGE_SIZE)); + BUG_ON(!PAGE_ALIGNED(bytes)); da += bytes; } @@ -127,6 +143,7 @@ static void msm_iommu_destroy(struct msm_mmu *mmu) static const struct msm_mmu_funcs funcs = { .attach = msm_iommu_attach, + .detach = msm_iommu_detach, .map = msm_iommu_map, .unmap = msm_iommu_unmap, .destroy = msm_iommu_destroy, diff --git a/drivers/gpu/drm/msm/msm_mmu.h b/drivers/gpu/drm/msm/msm_mmu.h index 030324482b4..21da6d154f7 100644 --- a/drivers/gpu/drm/msm/msm_mmu.h +++ b/drivers/gpu/drm/msm/msm_mmu.h @@ -22,6 +22,7 @@ struct msm_mmu_funcs { int (*attach)(struct msm_mmu *mmu, const char **names, int cnt); + void (*detach)(struct msm_mmu *mmu, const char **names, int cnt); int (*map)(struct msm_mmu *mmu, uint32_t iova, struct sg_table *sgt, unsigned len, int prot); int (*unmap)(struct msm_mmu *mmu, uint32_t iova, struct sg_table *sgt, diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index b7d21626477..8b307e14363 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -102,6 +102,7 @@ nouveau-y += core/subdev/fb/nvaa.o nouveau-y += core/subdev/fb/nvaf.o nouveau-y += core/subdev/fb/nvc0.o nouveau-y += core/subdev/fb/nve0.o +nouveau-y += core/subdev/fb/gk20a.o nouveau-y += core/subdev/fb/gm107.o nouveau-y += core/subdev/fb/ramnv04.o nouveau-y += core/subdev/fb/ramnv10.o @@ -117,25 +118,33 @@ nouveau-y += core/subdev/fb/ramnva3.o nouveau-y += core/subdev/fb/ramnvaa.o nouveau-y += core/subdev/fb/ramnvc0.o nouveau-y += core/subdev/fb/ramnve0.o +nouveau-y += core/subdev/fb/ramgk20a.o nouveau-y += core/subdev/fb/ramgm107.o nouveau-y += core/subdev/fb/sddr3.o nouveau-y += core/subdev/fb/gddr5.o nouveau-y += core/subdev/gpio/base.o nouveau-y += core/subdev/gpio/nv10.o nouveau-y += core/subdev/gpio/nv50.o +nouveau-y += core/subdev/gpio/nv92.o nouveau-y += core/subdev/gpio/nvd0.o nouveau-y += core/subdev/gpio/nve0.o nouveau-y += core/subdev/i2c/base.o nouveau-y += core/subdev/i2c/anx9805.o nouveau-y += core/subdev/i2c/aux.o nouveau-y += core/subdev/i2c/bit.o +nouveau-y += core/subdev/i2c/pad.o +nouveau-y += core/subdev/i2c/padnv04.o +nouveau-y += core/subdev/i2c/padnv94.o nouveau-y += core/subdev/i2c/nv04.o nouveau-y += core/subdev/i2c/nv4e.o nouveau-y += core/subdev/i2c/nv50.o nouveau-y += core/subdev/i2c/nv94.o nouveau-y += core/subdev/i2c/nvd0.o +nouveau-y += core/subdev/i2c/gf117.o +nouveau-y += core/subdev/i2c/nve0.o nouveau-y += core/subdev/ibus/nvc0.o nouveau-y += core/subdev/ibus/nve0.o +nouveau-y += core/subdev/ibus/gk20a.o nouveau-y += core/subdev/instmem/base.o nouveau-y += core/subdev/instmem/nv04.o nouveau-y += core/subdev/instmem/nv40.o @@ -214,6 +223,9 @@ nouveau-y += core/engine/device/nvc0.o nouveau-y += core/engine/device/nve0.o nouveau-y += core/engine/device/gm100.o nouveau-y += core/engine/disp/base.o +nouveau-y += core/engine/disp/conn.o +nouveau-y += core/engine/disp/outp.o +nouveau-y += core/engine/disp/outpdp.o nouveau-y += core/engine/disp/nv04.o nouveau-y += core/engine/disp/nv50.o nouveau-y += core/engine/disp/nv84.o @@ -245,6 +257,7 @@ nouveau-y += core/engine/fifo/nv50.o nouveau-y += core/engine/fifo/nv84.o nouveau-y += core/engine/fifo/nvc0.o nouveau-y += core/engine/fifo/nve0.o +nouveau-y += core/engine/fifo/gk20a.o nouveau-y += core/engine/fifo/nv108.o nouveau-y += core/engine/graph/ctxnv40.o nouveau-y += core/engine/graph/ctxnv50.o @@ -255,6 +268,7 @@ nouveau-y += core/engine/graph/ctxnvc8.o nouveau-y += core/engine/graph/ctxnvd7.o nouveau-y += core/engine/graph/ctxnvd9.o nouveau-y += core/engine/graph/ctxnve4.o +nouveau-y += core/engine/graph/ctxgk20a.o nouveau-y += core/engine/graph/ctxnvf0.o nouveau-y += core/engine/graph/ctxnv108.o nouveau-y += core/engine/graph/ctxgm107.o @@ -275,6 +289,7 @@ nouveau-y += core/engine/graph/nvc8.o nouveau-y += core/engine/graph/nvd7.o nouveau-y += core/engine/graph/nvd9.o nouveau-y += core/engine/graph/nve4.o +nouveau-y += core/engine/graph/gk20a.o nouveau-y += core/engine/graph/nvf0.o nouveau-y += core/engine/graph/nv108.o nouveau-y += core/engine/graph/gm107.o diff --git a/drivers/gpu/drm/nouveau/core/core/event.c b/drivers/gpu/drm/nouveau/core/core/event.c index 3f3c76581a9..ae81d3b5d8b 100644 --- a/drivers/gpu/drm/nouveau/core/core/event.c +++ b/drivers/gpu/drm/nouveau/core/core/event.c @@ -28,14 +28,20 @@ nouveau_event_put(struct nouveau_eventh *handler) { struct nouveau_event *event = handler->event; unsigned long flags; - if (__test_and_clear_bit(NVKM_EVENT_ENABLE, &handler->flags)) { - spin_lock_irqsave(&event->refs_lock, flags); - if (!--event->index[handler->index].refs) { + u32 m, t; + + if (!__test_and_clear_bit(NVKM_EVENT_ENABLE, &handler->flags)) + return; + + spin_lock_irqsave(&event->refs_lock, flags); + for (m = handler->types; t = __ffs(m), m; m &= ~(1 << t)) { + if (!--event->refs[handler->index * event->types_nr + t]) { if (event->disable) - event->disable(event, handler->index); + event->disable(event, 1 << t, handler->index); } - spin_unlock_irqrestore(&event->refs_lock, flags); + } + spin_unlock_irqrestore(&event->refs_lock, flags); } void @@ -43,14 +49,20 @@ nouveau_event_get(struct nouveau_eventh *handler) { struct nouveau_event *event = handler->event; unsigned long flags; - if (!__test_and_set_bit(NVKM_EVENT_ENABLE, &handler->flags)) { - spin_lock_irqsave(&event->refs_lock, flags); - if (!event->index[handler->index].refs++) { + u32 m, t; + + if (__test_and_set_bit(NVKM_EVENT_ENABLE, &handler->flags)) + return; + + spin_lock_irqsave(&event->refs_lock, flags); + for (m = handler->types; t = __ffs(m), m; m &= ~(1 << t)) { + if (!event->refs[handler->index * event->types_nr + t]++) { if (event->enable) - event->enable(event, handler->index); + event->enable(event, 1 << t, handler->index); } - spin_unlock_irqrestore(&event->refs_lock, flags); + } + spin_unlock_irqrestore(&event->refs_lock, flags); } static void @@ -65,38 +77,47 @@ nouveau_event_fini(struct nouveau_eventh *handler) } static int -nouveau_event_init(struct nouveau_event *event, int index, - int (*func)(void *, int), void *priv, +nouveau_event_init(struct nouveau_event *event, u32 types, int index, + int (*func)(void *, u32, int), void *priv, struct nouveau_eventh *handler) { unsigned long flags; + if (types & ~((1 << event->types_nr) - 1)) + return -EINVAL; if (index >= event->index_nr) return -EINVAL; handler->event = event; handler->flags = 0; + handler->types = types; handler->index = index; handler->func = func; handler->priv = priv; spin_lock_irqsave(&event->list_lock, flags); - list_add_tail(&handler->head, &event->index[index].list); + list_add_tail(&handler->head, &event->list[index]); spin_unlock_irqrestore(&event->list_lock, flags); return 0; } int -nouveau_event_new(struct nouveau_event *event, int index, - int (*func)(void *, int), void *priv, +nouveau_event_new(struct nouveau_event *event, u32 types, int index, + int (*func)(void *, u32, int), void *priv, struct nouveau_eventh **phandler) { struct nouveau_eventh *handler; int ret = -ENOMEM; + if (event->check) { + ret = event->check(event, types, index); + if (ret) + return ret; + } + handler = *phandler = kmalloc(sizeof(*handler), GFP_KERNEL); if (handler) { - ret = nouveau_event_init(event, index, func, priv, handler); + ret = nouveau_event_init(event, types, index, func, priv, handler); if (ret) kfree(handler); } @@ -116,7 +137,7 @@ nouveau_event_ref(struct nouveau_eventh *handler, struct nouveau_eventh **ref) } void -nouveau_event_trigger(struct nouveau_event *event, int index) +nouveau_event_trigger(struct nouveau_event *event, u32 types, int index) { struct nouveau_eventh *handler; unsigned long flags; @@ -125,10 +146,15 @@ nouveau_event_trigger(struct nouveau_event *event, int index) return; spin_lock_irqsave(&event->list_lock, flags); - list_for_each_entry(handler, &event->index[index].list, head) { - if (test_bit(NVKM_EVENT_ENABLE, &handler->flags) && - handler->func(handler->priv, index) == NVKM_EVENT_DROP) - nouveau_event_put(handler); + list_for_each_entry(handler, &event->list[index], head) { + if (!test_bit(NVKM_EVENT_ENABLE, &handler->flags)) + continue; + if (!(handler->types & types)) + continue; + if (handler->func(handler->priv, handler->types & types, index) + != NVKM_EVENT_DROP) + continue; + nouveau_event_put(handler); } spin_unlock_irqrestore(&event->list_lock, flags); } @@ -144,20 +170,27 @@ nouveau_event_destroy(struct nouveau_event **pevent) } int -nouveau_event_create(int index_nr, struct nouveau_event **pevent) +nouveau_event_create(int types_nr, int index_nr, struct nouveau_event **pevent) { struct nouveau_event *event; int i; - event = *pevent = kzalloc(sizeof(*event) + index_nr * - sizeof(event->index[0]), GFP_KERNEL); + event = *pevent = kzalloc(sizeof(*event) + (index_nr * types_nr) * + sizeof(event->refs[0]), GFP_KERNEL); if (!event) return -ENOMEM; + event->list = kmalloc(sizeof(*event->list) * index_nr, GFP_KERNEL); + if (!event->list) { + kfree(event); + return -ENOMEM; + } + spin_lock_init(&event->list_lock); spin_lock_init(&event->refs_lock); for (i = 0; i < index_nr; i++) - INIT_LIST_HEAD(&event->index[i].list); + INIT_LIST_HEAD(&event->list[i]); + event->types_nr = types_nr; event->index_nr = index_nr; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/core/object.c b/drivers/gpu/drm/nouveau/core/core/object.c index 7f48e288215..12453855590 100644 --- a/drivers/gpu/drm/nouveau/core/core/object.c +++ b/drivers/gpu/drm/nouveau/core/core/object.c @@ -156,7 +156,7 @@ nouveau_object_ctor(struct nouveau_object *parent, } if (ret == 0) { - nv_debug(object, "created\n"); + nv_trace(object, "created\n"); atomic_set(&object->refcount, 1); } @@ -166,7 +166,7 @@ nouveau_object_ctor(struct nouveau_object *parent, static void nouveau_object_dtor(struct nouveau_object *object) { - nv_debug(object, "destroying\n"); + nv_trace(object, "destroying\n"); nv_ofuncs(object)->dtor(object); } @@ -337,7 +337,7 @@ nouveau_object_inc(struct nouveau_object *object) goto fail_self; } - nv_debug(object, "initialised\n"); + nv_trace(object, "initialised\n"); return 0; fail_self: @@ -375,7 +375,7 @@ nouveau_object_decf(struct nouveau_object *object) if (object->parent) nouveau_object_dec(object->parent, false); - nv_debug(object, "stopped\n"); + nv_trace(object, "stopped\n"); return 0; } @@ -411,7 +411,7 @@ nouveau_object_decs(struct nouveau_object *object) } } - nv_debug(object, "suspended\n"); + nv_trace(object, "suspended\n"); return 0; fail_parent: diff --git a/drivers/gpu/drm/nouveau/core/engine/device/gm100.c b/drivers/gpu/drm/nouveau/core/engine/device/gm100.c index d258c21c4a2..a520029e25d 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/gm100.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/gm100.c @@ -60,8 +60,8 @@ gm100_identify(struct nouveau_device *device) case 0x117: device->cname = "GM107"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nve0_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nvd0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nvd0_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; #if 0 device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv04.c b/drivers/gpu/drm/nouveau/core/engine/device/nv04.c index 0a51ff4e9e0..40b29d0214c 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nv04.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nv04.c @@ -47,7 +47,7 @@ nv04_identify(struct nouveau_device *device) case 0x04: device->cname = "NV04"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv04_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -65,7 +65,7 @@ nv04_identify(struct nouveau_device *device) case 0x05: device->cname = "NV05"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv05_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv10.c b/drivers/gpu/drm/nouveau/core/engine/device/nv10.c index e008de8b51b..5f7c25ff523 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nv10.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nv10.c @@ -48,8 +48,8 @@ nv10_identify(struct nouveau_device *device) case 0x10: device->cname = "NV10"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -65,8 +65,8 @@ nv10_identify(struct nouveau_device *device) case 0x15: device->cname = "NV15"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -84,8 +84,8 @@ nv10_identify(struct nouveau_device *device) case 0x16: device->cname = "NV16"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -103,8 +103,8 @@ nv10_identify(struct nouveau_device *device) case 0x1a: device->cname = "nForce"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -122,8 +122,8 @@ nv10_identify(struct nouveau_device *device) case 0x11: device->cname = "NV11"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -141,8 +141,8 @@ nv10_identify(struct nouveau_device *device) case 0x17: device->cname = "NV17"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -160,8 +160,8 @@ nv10_identify(struct nouveau_device *device) case 0x1f: device->cname = "nForce2"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -179,8 +179,8 @@ nv10_identify(struct nouveau_device *device) case 0x18: device->cname = "NV18"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv20.c b/drivers/gpu/drm/nouveau/core/engine/device/nv20.c index 7b629a3aed0..75fed11bba0 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nv20.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nv20.c @@ -49,8 +49,8 @@ nv20_identify(struct nouveau_device *device) case 0x20: device->cname = "NV20"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -68,8 +68,8 @@ nv20_identify(struct nouveau_device *device) case 0x25: device->cname = "NV25"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -87,8 +87,8 @@ nv20_identify(struct nouveau_device *device) case 0x28: device->cname = "NV28"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -106,8 +106,8 @@ nv20_identify(struct nouveau_device *device) case 0x2a: device->cname = "NV2A"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv30.c b/drivers/gpu/drm/nouveau/core/engine/device/nv30.c index 7dfddd5a190..36919d7db7c 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nv30.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nv30.c @@ -49,8 +49,8 @@ nv30_identify(struct nouveau_device *device) case 0x30: device->cname = "NV30"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -68,8 +68,8 @@ nv30_identify(struct nouveau_device *device) case 0x35: device->cname = "NV35"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -87,8 +87,8 @@ nv30_identify(struct nouveau_device *device) case 0x31: device->cname = "NV31"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -107,8 +107,8 @@ nv30_identify(struct nouveau_device *device) case 0x36: device->cname = "NV36"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -127,8 +127,8 @@ nv30_identify(struct nouveau_device *device) case 0x34: device->cname = "NV34"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv40.c b/drivers/gpu/drm/nouveau/core/engine/device/nv40.c index 7c1ce6cf4f1..1130a62be2c 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nv40.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nv40.c @@ -53,8 +53,8 @@ nv40_identify(struct nouveau_device *device) case 0x40: device->cname = "NV40"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -76,8 +76,8 @@ nv40_identify(struct nouveau_device *device) case 0x41: device->cname = "NV41"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -99,8 +99,8 @@ nv40_identify(struct nouveau_device *device) case 0x42: device->cname = "NV42"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -122,8 +122,8 @@ nv40_identify(struct nouveau_device *device) case 0x43: device->cname = "NV43"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -145,8 +145,8 @@ nv40_identify(struct nouveau_device *device) case 0x45: device->cname = "NV45"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -168,8 +168,8 @@ nv40_identify(struct nouveau_device *device) case 0x47: device->cname = "G70"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -191,8 +191,8 @@ nv40_identify(struct nouveau_device *device) case 0x49: device->cname = "G71"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -214,8 +214,8 @@ nv40_identify(struct nouveau_device *device) case 0x4b: device->cname = "G73"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -237,8 +237,8 @@ nv40_identify(struct nouveau_device *device) case 0x44: device->cname = "NV44"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -260,8 +260,8 @@ nv40_identify(struct nouveau_device *device) case 0x46: device->cname = "G72"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -283,8 +283,8 @@ nv40_identify(struct nouveau_device *device) case 0x4a: device->cname = "NV44A"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -306,8 +306,8 @@ nv40_identify(struct nouveau_device *device) case 0x4c: device->cname = "C61"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -329,8 +329,8 @@ nv40_identify(struct nouveau_device *device) case 0x4e: device->cname = "C51"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv4e_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv4e_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -352,8 +352,8 @@ nv40_identify(struct nouveau_device *device) case 0x63: device->cname = "C73"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -375,8 +375,8 @@ nv40_identify(struct nouveau_device *device) case 0x67: device->cname = "C67"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -398,8 +398,8 @@ nv40_identify(struct nouveau_device *device) case 0x68: device->cname = "C68"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv50.c b/drivers/gpu/drm/nouveau/core/engine/device/nv50.c index 66499fa0f75..ef0b0bde1a9 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nv50.c @@ -60,8 +60,8 @@ nv50_identify(struct nouveau_device *device) case 0x50: device->cname = "G80"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv50_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv50_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv50_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -85,8 +85,8 @@ nv50_identify(struct nouveau_device *device) case 0x84: device->cname = "G84"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv50_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv50_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -113,8 +113,8 @@ nv50_identify(struct nouveau_device *device) case 0x86: device->cname = "G86"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv50_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv50_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -141,8 +141,8 @@ nv50_identify(struct nouveau_device *device) case 0x92: device->cname = "G92"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv50_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -169,8 +169,8 @@ nv50_identify(struct nouveau_device *device) case 0x94: device->cname = "G94"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -197,8 +197,8 @@ nv50_identify(struct nouveau_device *device) case 0x96: device->cname = "G96"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -225,8 +225,8 @@ nv50_identify(struct nouveau_device *device) case 0x98: device->cname = "G98"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -253,8 +253,8 @@ nv50_identify(struct nouveau_device *device) case 0xa0: device->cname = "G200"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv50_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -281,8 +281,8 @@ nv50_identify(struct nouveau_device *device) case 0xaa: device->cname = "MCP77/MCP78"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nvaa_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -309,8 +309,8 @@ nv50_identify(struct nouveau_device *device) case 0xac: device->cname = "MCP79/MCP7A"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nvaa_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -337,8 +337,8 @@ nv50_identify(struct nouveau_device *device) case 0xa3: device->cname = "GT215"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -367,8 +367,8 @@ nv50_identify(struct nouveau_device *device) case 0xa5: device->cname = "GT216"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -396,8 +396,8 @@ nv50_identify(struct nouveau_device *device) case 0xa8: device->cname = "GT218"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -425,8 +425,8 @@ nv50_identify(struct nouveau_device *device) case 0xaf: device->cname = "MCP89"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c index 2075b302705..8d55ed633b1 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c @@ -60,8 +60,8 @@ nvc0_identify(struct nouveau_device *device) case 0xc0: device->cname = "GF100"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -92,8 +92,8 @@ nvc0_identify(struct nouveau_device *device) case 0xc4: device->cname = "GF104"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -124,8 +124,8 @@ nvc0_identify(struct nouveau_device *device) case 0xc3: device->cname = "GF106"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -155,8 +155,8 @@ nvc0_identify(struct nouveau_device *device) case 0xce: device->cname = "GF114"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -187,8 +187,8 @@ nvc0_identify(struct nouveau_device *device) case 0xcf: device->cname = "GF116"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -219,8 +219,8 @@ nvc0_identify(struct nouveau_device *device) case 0xc1: device->cname = "GF108"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -250,8 +250,8 @@ nvc0_identify(struct nouveau_device *device) case 0xc8: device->cname = "GF110"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -282,8 +282,8 @@ nvc0_identify(struct nouveau_device *device) case 0xd9: device->cname = "GF119"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nvd0_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nvd0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nvd0_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nvd0_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -313,8 +313,8 @@ nvc0_identify(struct nouveau_device *device) case 0xd7: device->cname = "GF117"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nvd0_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nvd0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nvd0_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = gf117_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c index 9784cbf8a9d..2d1e97d4264 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c @@ -60,8 +60,8 @@ nve0_identify(struct nouveau_device *device) case 0xe4: device->cname = "GK104"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nve0_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nvd0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -93,8 +93,8 @@ nve0_identify(struct nouveau_device *device) case 0xe7: device->cname = "GK107"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nve0_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nvd0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -126,8 +126,8 @@ nve0_identify(struct nouveau_device *device) case 0xe6: device->cname = "GK106"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nve0_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nvd0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -156,11 +156,61 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; device->oclass[NVDEV_ENGINE_PERFMON] = &nve0_perfmon_oclass; break; + case 0xea: + device->cname = "GK20A"; + device->oclass[NVDEV_SUBDEV_MC ] = nvc3_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &gk20a_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = gk20a_fb_oclass; + device->oclass[NVDEV_SUBDEV_IBUS ] = &gk20a_ibus_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = gk20a_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass; + device->oclass[NVDEV_ENGINE_GR ] = gk20a_graph_oclass; + device->oclass[NVDEV_ENGINE_COPY2 ] = &nve0_copy2_oclass; + device->oclass[NVDEV_ENGINE_PERFMON] = &nve0_perfmon_oclass; + break; case 0xf0: device->cname = "GK110"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nve0_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nvd0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nvc0_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = nvc3_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nve0_fb_oclass; + device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass; + device->oclass[NVDEV_SUBDEV_IBUS ] = &nve0_ibus_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass; + device->oclass[NVDEV_SUBDEV_PWR ] = &nvd0_pwr_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nve0_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass; + device->oclass[NVDEV_ENGINE_GR ] = nvf0_graph_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nvf0_disp_oclass; + device->oclass[NVDEV_ENGINE_COPY0 ] = &nve0_copy0_oclass; + device->oclass[NVDEV_ENGINE_COPY1 ] = &nve0_copy1_oclass; + device->oclass[NVDEV_ENGINE_COPY2 ] = &nve0_copy2_oclass; + device->oclass[NVDEV_ENGINE_BSP ] = &nve0_bsp_oclass; + device->oclass[NVDEV_ENGINE_VP ] = &nve0_vp_oclass; + device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; + device->oclass[NVDEV_ENGINE_PERFMON] = &nvf0_perfmon_oclass; + break; + case 0xf1: + device->cname = "GK110B"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nvd0_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -184,18 +234,16 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_COPY0 ] = &nve0_copy0_oclass; device->oclass[NVDEV_ENGINE_COPY1 ] = &nve0_copy1_oclass; device->oclass[NVDEV_ENGINE_COPY2 ] = &nve0_copy2_oclass; -#if 0 device->oclass[NVDEV_ENGINE_BSP ] = &nve0_bsp_oclass; device->oclass[NVDEV_ENGINE_VP ] = &nve0_vp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; -#endif device->oclass[NVDEV_ENGINE_PERFMON] = &nvf0_perfmon_oclass; break; case 0x108: device->cname = "GK208"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nve0_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nvd0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -219,11 +267,9 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_COPY0 ] = &nve0_copy0_oclass; device->oclass[NVDEV_ENGINE_COPY1 ] = &nve0_copy1_oclass; device->oclass[NVDEV_ENGINE_COPY2 ] = &nve0_copy2_oclass; -#if 0 device->oclass[NVDEV_ENGINE_BSP ] = &nve0_bsp_oclass; device->oclass[NVDEV_ENGINE_VP ] = &nve0_vp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; -#endif break; default: nv_fatal(device, "unknown Kepler chipset\n"); diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/base.c b/drivers/gpu/drm/nouveau/core/engine/disp/base.c index 7a5cae42834..9c38c5e4050 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/base.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/base.c @@ -22,13 +22,89 @@ * Authors: Ben Skeggs */ -#include <engine/disp.h> +#include "priv.h" +#include "outp.h" +#include "conn.h" + +static int +nouveau_disp_hpd_check(struct nouveau_event *event, u32 types, int index) +{ + struct nouveau_disp *disp = event->priv; + struct nvkm_output *outp; + list_for_each_entry(outp, &disp->outp, head) { + if (outp->conn->index == index) { + if (outp->conn->hpd.event) + return 0; + break; + } + } + return -ENOSYS; +} + +int +_nouveau_disp_fini(struct nouveau_object *object, bool suspend) +{ + struct nouveau_disp *disp = (void *)object; + struct nvkm_output *outp; + int ret; + + list_for_each_entry(outp, &disp->outp, head) { + ret = nv_ofuncs(outp)->fini(nv_object(outp), suspend); + if (ret && suspend) + goto fail_outp; + } + + return nouveau_engine_fini(&disp->base, suspend); + +fail_outp: + list_for_each_entry_continue_reverse(outp, &disp->outp, head) { + nv_ofuncs(outp)->init(nv_object(outp)); + } + + return ret; +} + +int +_nouveau_disp_init(struct nouveau_object *object) +{ + struct nouveau_disp *disp = (void *)object; + struct nvkm_output *outp; + int ret; + + ret = nouveau_engine_init(&disp->base); + if (ret) + return ret; + + list_for_each_entry(outp, &disp->outp, head) { + ret = nv_ofuncs(outp)->init(nv_object(outp)); + if (ret) + goto fail_outp; + } + + return ret; + +fail_outp: + list_for_each_entry_continue_reverse(outp, &disp->outp, head) { + nv_ofuncs(outp)->fini(nv_object(outp), false); + } + + return ret; +} void _nouveau_disp_dtor(struct nouveau_object *object) { struct nouveau_disp *disp = (void *)object; + struct nvkm_output *outp, *outt; + nouveau_event_destroy(&disp->vblank); + + if (disp->outp.next) { + list_for_each_entry_safe(outp, outt, &disp->outp, head) { + nouveau_object_ref(NULL, (struct nouveau_object **)&outp); + } + } + nouveau_engine_destroy(&disp->base); } @@ -39,8 +115,15 @@ nouveau_disp_create_(struct nouveau_object *parent, const char *intname, const char *extname, int length, void **pobject) { + struct nouveau_disp_impl *impl = (void *)oclass; + struct nouveau_bios *bios = nouveau_bios(parent); struct nouveau_disp *disp; - int ret; + struct nouveau_oclass **sclass; + struct nouveau_object *object; + struct dcb_output dcbE; + u8 hpd = 0, ver, hdr; + u32 data; + int ret, i; ret = nouveau_engine_create_(parent, engine, oclass, true, intname, extname, length, pobject); @@ -48,5 +131,42 @@ nouveau_disp_create_(struct nouveau_object *parent, if (ret) return ret; - return nouveau_event_create(heads, &disp->vblank); + INIT_LIST_HEAD(&disp->outp); + + /* create output objects for each display path in the vbios */ + i = -1; + while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE))) { + if (dcbE.type == DCB_OUTPUT_UNUSED) + continue; + if (dcbE.type == DCB_OUTPUT_EOL) + break; + data = dcbE.location << 4 | dcbE.type; + + oclass = nvkm_output_oclass; + sclass = impl->outp; + while (sclass && sclass[0]) { + if (sclass[0]->handle == data) { + oclass = sclass[0]; + break; + } + sclass++; + } + + nouveau_object_ctor(*pobject, *pobject, oclass, + &dcbE, i, &object); + hpd = max(hpd, (u8)(dcbE.connector + 1)); + } + + ret = nouveau_event_create(3, hpd, &disp->hpd); + if (ret) + return ret; + + disp->hpd->priv = disp; + disp->hpd->check = nouveau_disp_hpd_check; + + ret = nouveau_event_create(1, heads, &disp->vblank); + if (ret) + return ret; + + return 0; } diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/conn.c b/drivers/gpu/drm/nouveau/core/engine/disp/conn.c new file mode 100644 index 00000000000..4ffbc70ecf5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/conn.c @@ -0,0 +1,172 @@ +/* + * Copyright 2014 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 <subdev/gpio.h> + +#include "conn.h" +#include "outp.h" + +static void +nvkm_connector_hpd_work(struct work_struct *w) +{ + struct nvkm_connector *conn = container_of(w, typeof(*conn), hpd.work); + struct nouveau_disp *disp = nouveau_disp(conn); + struct nouveau_gpio *gpio = nouveau_gpio(conn); + u32 send = NVKM_HPD_UNPLUG; + if (gpio->get(gpio, 0, DCB_GPIO_UNUSED, conn->hpd.event->index)) + send = NVKM_HPD_PLUG; + nouveau_event_trigger(disp->hpd, send, conn->index); + nouveau_event_get(conn->hpd.event); +} + +static int +nvkm_connector_hpd(void *data, u32 type, int index) +{ + struct nvkm_connector *conn = data; + DBG("HPD: %d\n", type); + schedule_work(&conn->hpd.work); + return NVKM_EVENT_DROP; +} + +int +_nvkm_connector_fini(struct nouveau_object *object, bool suspend) +{ + struct nvkm_connector *conn = (void *)object; + if (conn->hpd.event) + nouveau_event_put(conn->hpd.event); + return nouveau_object_fini(&conn->base, suspend); +} + +int +_nvkm_connector_init(struct nouveau_object *object) +{ + struct nvkm_connector *conn = (void *)object; + int ret = nouveau_object_init(&conn->base); + if (ret == 0) { + if (conn->hpd.event) + nouveau_event_get(conn->hpd.event); + } + return ret; +} + +void +_nvkm_connector_dtor(struct nouveau_object *object) +{ + struct nvkm_connector *conn = (void *)object; + nouveau_event_ref(NULL, &conn->hpd.event); + nouveau_object_destroy(&conn->base); +} + +int +nvkm_connector_create_(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, + struct nvbios_connE *info, int index, + int length, void **pobject) +{ + static const u8 hpd[] = { 0x07, 0x08, 0x51, 0x52, 0x5e, 0x5f, 0x60 }; + struct nouveau_gpio *gpio = nouveau_gpio(parent); + struct nouveau_disp *disp = (void *)engine; + struct nvkm_connector *conn; + struct nvkm_output *outp; + struct dcb_gpio_func func; + int ret; + + list_for_each_entry(outp, &disp->outp, head) { + if (outp->conn && outp->conn->index == index) { + atomic_inc(&nv_object(outp->conn)->refcount); + *pobject = outp->conn; + return 1; + } + } + + ret = nouveau_object_create_(parent, engine, oclass, 0, length, pobject); + conn = *pobject; + if (ret) + return ret; + + conn->info = *info; + conn->index = index; + + DBG("type %02x loc %d hpd %02x dp %x di %x sr %x lcdid %x\n", + info->type, info->location, info->hpd, info->dp, + info->di, info->sr, info->lcdid); + + if ((info->hpd = ffs(info->hpd))) { + if (--info->hpd >= ARRAY_SIZE(hpd)) { + ERR("hpd %02x unknown\n", info->hpd); + goto done; + } + info->hpd = hpd[info->hpd]; + + ret = gpio->find(gpio, 0, info->hpd, DCB_GPIO_UNUSED, &func); + if (ret) { + ERR("func %02x lookup failed, %d\n", info->hpd, ret); + goto done; + } + + ret = nouveau_event_new(gpio->events, NVKM_GPIO_TOGGLED, + func.line, nvkm_connector_hpd, + conn, &conn->hpd.event); + if (ret) { + ERR("func %02x failed, %d\n", info->hpd, ret); + } else { + DBG("func %02x (HPD)\n", info->hpd); + } + } + +done: + INIT_WORK(&conn->hpd.work, nvkm_connector_hpd_work); + return 0; +} + +int +_nvkm_connector_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *info, u32 index, + struct nouveau_object **pobject) +{ + struct nvkm_connector *conn; + int ret; + + ret = nvkm_connector_create(parent, engine, oclass, info, index, &conn); + *pobject = nv_object(conn); + if (ret) + return ret; + + return 0; +} + +struct nouveau_oclass * +nvkm_connector_oclass = &(struct nvkm_connector_impl) { + .base = { + .handle = 0, + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nvkm_connector_ctor, + .dtor = _nvkm_connector_dtor, + .init = _nvkm_connector_init, + .fini = _nvkm_connector_fini, + }, + }, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/conn.h b/drivers/gpu/drm/nouveau/core/engine/disp/conn.h new file mode 100644 index 00000000000..035ebeacbb1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/conn.h @@ -0,0 +1,59 @@ +#ifndef __NVKM_DISP_CONN_H__ +#define __NVKM_DISP_CONN_H__ + +#include "priv.h" + +struct nvkm_connector { + struct nouveau_object base; + struct list_head head; + + struct nvbios_connE info; + int index; + + struct { + struct nouveau_eventh *event; + struct work_struct work; + } hpd; +}; + +#define nvkm_connector_create(p,e,c,b,i,d) \ + nvkm_connector_create_((p), (e), (c), (b), (i), sizeof(**d), (void **)d) +#define nvkm_connector_destroy(d) ({ \ + struct nvkm_connector *disp = (d); \ + _nvkm_connector_dtor(nv_object(disp)); \ +}) +#define nvkm_connector_init(d) ({ \ + struct nvkm_connector *disp = (d); \ + _nvkm_connector_init(nv_object(disp)); \ +}) +#define nvkm_connector_fini(d,s) ({ \ + struct nvkm_connector *disp = (d); \ + _nvkm_connector_fini(nv_object(disp), (s)); \ +}) + +int nvkm_connector_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, struct nvbios_connE *, + int, int, void **); + +int _nvkm_connector_ctor(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, void *, u32, + struct nouveau_object **); +void _nvkm_connector_dtor(struct nouveau_object *); +int _nvkm_connector_init(struct nouveau_object *); +int _nvkm_connector_fini(struct nouveau_object *, bool); + +struct nvkm_connector_impl { + struct nouveau_oclass base; +}; + +#ifndef MSG +#define MSG(l,f,a...) do { \ + struct nvkm_connector *_conn = (void *)conn; \ + nv_##l(nv_object(conn)->engine, "%02x:%02x%02x: "f, _conn->index, \ + _conn->info.location, _conn->info.type, ##a); \ +} while(0) +#define DBG(f,a...) MSG(debug, f, ##a) +#define ERR(f,a...) MSG(error, f, ##a) +#endif + +#endif diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/dport.c b/drivers/gpu/drm/nouveau/core/engine/disp/dport.c index 3ca2d25b7f5..5a5b59b2113 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/dport.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/dport.c @@ -30,42 +30,38 @@ #include <engine/disp.h> -#include "dport.h" +#include <core/class.h> -#define DBG(fmt, args...) nv_debug(dp->disp, "DP:%04x:%04x: " fmt, \ - dp->outp->hasht, dp->outp->hashm, ##args) -#define ERR(fmt, args...) nv_error(dp->disp, "DP:%04x:%04x: " fmt, \ - dp->outp->hasht, dp->outp->hashm, ##args) +#include "dport.h" +#include "outpdp.h" /****************************************************************************** * link training *****************************************************************************/ struct dp_state { - const struct nouveau_dp_func *func; - struct nouveau_disp *disp; - struct dcb_output *outp; - struct nvbios_dpout info; - u8 version; - struct nouveau_i2c_port *aux; - int head; - u8 dpcd[4]; + struct nvkm_output_dp *outp; int link_nr; u32 link_bw; u8 stat[6]; u8 conf[4]; + bool pc2; + u8 pc2stat; + u8 pc2conf[2]; }; static int dp_set_link_config(struct dp_state *dp) { - struct nouveau_disp *disp = dp->disp; + struct nvkm_output_dp_impl *impl = (void *)nv_oclass(dp->outp); + struct nvkm_output_dp *outp = dp->outp; + struct nouveau_disp *disp = nouveau_disp(outp); struct nouveau_bios *bios = nouveau_bios(disp); struct nvbios_init init = { - .subdev = nv_subdev(dp->disp), + .subdev = nv_subdev(disp), .bios = bios, .offset = 0x0000, - .outp = dp->outp, - .crtc = dp->head, + .outp = &outp->base.info, + .crtc = -1, .execute = 1, }; u32 lnkcmp; @@ -75,8 +71,8 @@ dp_set_link_config(struct dp_state *dp) DBG("%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw); /* set desired link configuration on the source */ - if ((lnkcmp = dp->info.lnkcmp)) { - if (dp->version < 0x30) { + if ((lnkcmp = dp->outp->info.lnkcmp)) { + if (outp->version < 0x30) { while ((dp->link_bw / 10) < nv_ro16(bios, lnkcmp)) lnkcmp += 4; init.offset = nv_ro16(bios, lnkcmp + 2); @@ -89,73 +85,112 @@ dp_set_link_config(struct dp_state *dp) nvbios_exec(&init); } - ret = dp->func->lnk_ctl(dp->disp, dp->outp, dp->head, - dp->link_nr, dp->link_bw / 27000, - dp->dpcd[DPCD_RC02] & - DPCD_RC02_ENHANCED_FRAME_CAP); + ret = impl->lnk_ctl(outp, dp->link_nr, dp->link_bw / 27000, + outp->dpcd[DPCD_RC02] & + DPCD_RC02_ENHANCED_FRAME_CAP); if (ret) { - ERR("lnk_ctl failed with %d\n", ret); + if (ret < 0) + ERR("lnk_ctl failed with %d\n", ret); return ret; } + impl->lnk_pwr(outp, dp->link_nr); + /* set desired link configuration on the sink */ sink[0] = dp->link_bw / 27000; sink[1] = dp->link_nr; - if (dp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP) + if (outp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP) sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN; - return nv_wraux(dp->aux, DPCD_LC00, sink, 2); + return nv_wraux(outp->base.edid, DPCD_LC00_LINK_BW_SET, sink, 2); } static void dp_set_training_pattern(struct dp_state *dp, u8 pattern) { + struct nvkm_output_dp_impl *impl = (void *)nv_oclass(dp->outp); + struct nvkm_output_dp *outp = dp->outp; u8 sink_tp; DBG("training pattern %d\n", pattern); - dp->func->pattern(dp->disp, dp->outp, dp->head, pattern); + impl->pattern(outp, pattern); - nv_rdaux(dp->aux, DPCD_LC02, &sink_tp, 1); + nv_rdaux(outp->base.edid, DPCD_LC02, &sink_tp, 1); sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET; sink_tp |= pattern; - nv_wraux(dp->aux, DPCD_LC02, &sink_tp, 1); + nv_wraux(outp->base.edid, DPCD_LC02, &sink_tp, 1); } static int -dp_link_train_commit(struct dp_state *dp) +dp_link_train_commit(struct dp_state *dp, bool pc) { - int i; + struct nvkm_output_dp_impl *impl = (void *)nv_oclass(dp->outp); + struct nvkm_output_dp *outp = dp->outp; + int ret, i; for (i = 0; i < dp->link_nr; i++) { u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; + u8 lpc2 = (dp->pc2stat >> (i * 2)) & 0x3; u8 lpre = (lane & 0x0c) >> 2; u8 lvsw = (lane & 0x03) >> 0; + u8 hivs = 3 - lpre; + u8 hipe = 3; + u8 hipc = 3; + + if (lpc2 >= hipc) + lpc2 = hipc | DPCD_LC0F_LANE0_MAX_POST_CURSOR2_REACHED; + if (lpre >= hipe) { + lpre = hipe | DPCD_LC03_MAX_SWING_REACHED; /* yes. */ + lvsw = hivs = 3 - (lpre & 3); + } else + if (lvsw >= hivs) { + lvsw = hivs | DPCD_LC03_MAX_SWING_REACHED; + } dp->conf[i] = (lpre << 3) | lvsw; - if (lvsw == 3) - dp->conf[i] |= DPCD_LC03_MAX_SWING_REACHED; - if (lpre == 3) - dp->conf[i] |= DPCD_LC03_MAX_PRE_EMPHASIS_REACHED; + dp->pc2conf[i >> 1] |= lpc2 << ((i & 1) * 4); + + DBG("config lane %d %02x %02x\n", i, dp->conf[i], lpc2); + impl->drv_ctl(outp, i, lvsw & 3, lpre & 3, lpc2 & 3); + } + + ret = nv_wraux(outp->base.edid, DPCD_LC03(0), dp->conf, 4); + if (ret) + return ret; - DBG("config lane %d %02x\n", i, dp->conf[i]); - dp->func->drv_ctl(dp->disp, dp->outp, dp->head, i, lvsw, lpre); + if (pc) { + ret = nv_wraux(outp->base.edid, DPCD_LC0F, dp->pc2conf, 2); + if (ret) + return ret; } - return nv_wraux(dp->aux, DPCD_LC03(0), dp->conf, 4); + return 0; } static int -dp_link_train_update(struct dp_state *dp, u32 delay) +dp_link_train_update(struct dp_state *dp, bool pc, u32 delay) { + struct nvkm_output_dp *outp = dp->outp; int ret; - udelay(delay); + if (outp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL]) + mdelay(outp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL] * 4); + else + udelay(delay); - ret = nv_rdaux(dp->aux, DPCD_LS02, dp->stat, 6); + ret = nv_rdaux(outp->base.edid, DPCD_LS02, dp->stat, 6); if (ret) return ret; - DBG("status %6ph\n", dp->stat); + if (pc) { + ret = nv_rdaux(outp->base.edid, DPCD_LS0C, &dp->pc2stat, 1); + if (ret) + dp->pc2stat = 0x00; + DBG("status %6ph pc2 %02x\n", dp->stat, dp->pc2stat); + } else { + DBG("status %6ph\n", dp->stat); + } + return 0; } @@ -169,8 +204,8 @@ dp_link_train_cr(struct dp_state *dp) dp_set_training_pattern(dp, 1); do { - if (dp_link_train_commit(dp) || - dp_link_train_update(dp, 100)) + if (dp_link_train_commit(dp, false) || + dp_link_train_update(dp, false, 100)) break; cr_done = true; @@ -196,13 +231,19 @@ dp_link_train_cr(struct dp_state *dp) static int dp_link_train_eq(struct dp_state *dp) { + struct nvkm_output_dp *outp = dp->outp; bool eq_done = false, cr_done = true; int tries = 0, i; - dp_set_training_pattern(dp, 2); + if (outp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED) + dp_set_training_pattern(dp, 3); + else + dp_set_training_pattern(dp, 2); do { - if (dp_link_train_update(dp, 400)) + if ((tries && + dp_link_train_commit(dp, dp->pc2)) || + dp_link_train_update(dp, dp->pc2, 400)) break; eq_done = !!(dp->stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE); @@ -214,9 +255,6 @@ dp_link_train_eq(struct dp_state *dp) !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) eq_done = false; } - - if (dp_link_train_commit(dp)) - break; } while (!eq_done && cr_done && ++tries <= 5); return eq_done ? 0 : -1; @@ -225,121 +263,109 @@ dp_link_train_eq(struct dp_state *dp) static void dp_link_train_init(struct dp_state *dp, bool spread) { + struct nvkm_output_dp *outp = dp->outp; + struct nouveau_disp *disp = nouveau_disp(outp); + struct nouveau_bios *bios = nouveau_bios(disp); struct nvbios_init init = { - .subdev = nv_subdev(dp->disp), - .bios = nouveau_bios(dp->disp), - .outp = dp->outp, - .crtc = dp->head, + .subdev = nv_subdev(disp), + .bios = bios, + .outp = &outp->base.info, + .crtc = -1, .execute = 1, }; /* set desired spread */ if (spread) - init.offset = dp->info.script[2]; + init.offset = outp->info.script[2]; else - init.offset = dp->info.script[3]; + init.offset = outp->info.script[3]; nvbios_exec(&init); /* pre-train script */ - init.offset = dp->info.script[0]; + init.offset = outp->info.script[0]; nvbios_exec(&init); } static void dp_link_train_fini(struct dp_state *dp) { + struct nvkm_output_dp *outp = dp->outp; + struct nouveau_disp *disp = nouveau_disp(outp); + struct nouveau_bios *bios = nouveau_bios(disp); struct nvbios_init init = { - .subdev = nv_subdev(dp->disp), - .bios = nouveau_bios(dp->disp), - .outp = dp->outp, - .crtc = dp->head, + .subdev = nv_subdev(disp), + .bios = bios, + .outp = &outp->base.info, + .crtc = -1, .execute = 1, }; /* post-train script */ - init.offset = dp->info.script[1], + init.offset = outp->info.script[1], nvbios_exec(&init); } -int -nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func, - struct dcb_output *outp, int head, u32 datarate) +static const struct dp_rates { + u32 rate; + u8 bw; + u8 nr; +} nouveau_dp_rates[] = { + { 2160000, 0x14, 4 }, + { 1080000, 0x0a, 4 }, + { 1080000, 0x14, 2 }, + { 648000, 0x06, 4 }, + { 540000, 0x0a, 2 }, + { 540000, 0x14, 1 }, + { 324000, 0x06, 2 }, + { 270000, 0x0a, 1 }, + { 162000, 0x06, 1 }, + {} +}; + +void +nouveau_dp_train(struct work_struct *w) { - struct nouveau_bios *bios = nouveau_bios(disp); - struct nouveau_i2c *i2c = nouveau_i2c(disp); + struct nvkm_output_dp *outp = container_of(w, typeof(*outp), lt.work); + struct nouveau_disp *disp = nouveau_disp(outp); + const struct dp_rates *cfg = nouveau_dp_rates; struct dp_state _dp = { - .disp = disp, - .func = func, .outp = outp, - .head = head, }, *dp = &_dp; - const u32 bw_list[] = { 540000, 270000, 162000, 0 }; - const u32 *link_bw = bw_list; - u8 hdr, cnt, len; - u32 data; + u32 datarate = 0; int ret; - /* find the bios displayport data relevant to this output */ - data = nvbios_dpout_match(bios, outp->hasht, outp->hashm, &dp->version, - &hdr, &cnt, &len, &dp->info); - if (!data) { - ERR("bios data not found\n"); - return -EINVAL; - } - - /* acquire the aux channel and fetch some info about the display */ - if (outp->location) - dp->aux = i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(outp->extdev)); - else - dp->aux = i2c->find(i2c, NV_I2C_TYPE_DCBI2C(outp->i2c_index)); - if (!dp->aux) { - ERR("no aux channel?!\n"); - return -ENODEV; - } - - ret = nv_rdaux(dp->aux, 0x00000, dp->dpcd, sizeof(dp->dpcd)); - if (ret) { - /* it's possible the display has been unplugged before we - * get here. we still need to execute the full set of - * vbios scripts, and program the OR at a high enough - * frequency to satisfy the target mode. failure to do - * so results at best in an UPDATE hanging, and at worst - * with PDISP running away to join the circus. - */ - dp->dpcd[1] = link_bw[0] / 27000; - dp->dpcd[2] = 4; - dp->dpcd[3] = 0x00; - ERR("failed to read DPCD\n"); - } - /* bring capabilities within encoder limits */ - if ((dp->dpcd[2] & 0x1f) > dp->outp->dpconf.link_nr) { - dp->dpcd[2] &= ~0x1f; - dp->dpcd[2] |= dp->outp->dpconf.link_nr; + if (nv_mclass(disp) < NVD0_DISP_CLASS) + outp->dpcd[2] &= ~DPCD_RC02_TPS3_SUPPORTED; + if ((outp->dpcd[2] & 0x1f) > outp->base.info.dpconf.link_nr) { + outp->dpcd[2] &= ~DPCD_RC02_MAX_LANE_COUNT; + outp->dpcd[2] |= outp->base.info.dpconf.link_nr; + } + if (outp->dpcd[1] > outp->base.info.dpconf.link_bw) + outp->dpcd[1] = outp->base.info.dpconf.link_bw; + dp->pc2 = outp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED; + + /* restrict link config to the lowest required rate, if requested */ + if (datarate) { + datarate = (datarate / 8) * 10; /* 8B/10B coding overhead */ + while (cfg[1].rate >= datarate) + cfg++; } - if (dp->dpcd[1] > dp->outp->dpconf.link_bw) - dp->dpcd[1] = dp->outp->dpconf.link_bw; + cfg--; - /* adjust required bandwidth for 8B/10B coding overhead */ - datarate = (datarate / 8) * 10; + /* disable link interrupt handling during link training */ + nouveau_event_put(outp->irq); /* enable down-spreading and execute pre-train script from vbios */ - dp_link_train_init(dp, dp->dpcd[3] & 0x01); + dp_link_train_init(dp, outp->dpcd[3] & 0x01); - /* start off at highest link rate supported by encoder and display */ - while (*link_bw > (dp->dpcd[1] * 27000)) - link_bw++; - - while ((ret = -EIO) && link_bw[0]) { - /* find minimum required lane count at this link rate */ - dp->link_nr = dp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT; - while ((dp->link_nr >> 1) * link_bw[0] > datarate) - dp->link_nr >>= 1; - - /* drop link rate to minimum with this lane count */ - while ((link_bw[1] * dp->link_nr) > datarate) - link_bw++; - dp->link_bw = link_bw[0]; + while (ret = -EIO, (++cfg)->rate) { + /* select next configuration supported by encoder and sink */ + while (cfg->nr > (outp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT) || + cfg->bw > (outp->dpcd[DPCD_RC01_MAX_LINK_RATE])) + cfg++; + dp->link_bw = cfg->bw * 27000; + dp->link_nr = cfg->nr; /* program selected link configuration */ ret = dp_set_link_config(dp); @@ -356,17 +382,18 @@ nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func, */ break; } - - /* retry at lower rate */ - link_bw++; } - /* finish link training */ + /* finish link training and execute post-train script from vbios */ dp_set_training_pattern(dp, 0); if (ret < 0) ERR("link training failed\n"); - /* execute post-train script from vbios */ dp_link_train_fini(dp); - return (ret < 0) ? false : true; + + /* signal completion and enable link interrupt handling */ + DBG("training complete\n"); + atomic_set(&outp->lt.done, 1); + wake_up(&outp->lt.wait); + nouveau_event_get(outp->irq); } diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/dport.h b/drivers/gpu/drm/nouveau/core/engine/disp/dport.h index 0e1bbd18ff6..5628d2d5ec7 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/dport.h +++ b/drivers/gpu/drm/nouveau/core/engine/disp/dport.h @@ -2,19 +2,18 @@ #define __NVKM_DISP_DPORT_H__ /* DPCD Receiver Capabilities */ -#define DPCD_RC00 0x00000 -#define DPCD_RC00_DPCD_REV 0xff -#define DPCD_RC01 0x00001 -#define DPCD_RC01_MAX_LINK_RATE 0xff +#define DPCD_RC00_DPCD_REV 0x00000 +#define DPCD_RC01_MAX_LINK_RATE 0x00001 #define DPCD_RC02 0x00002 #define DPCD_RC02_ENHANCED_FRAME_CAP 0x80 +#define DPCD_RC02_TPS3_SUPPORTED 0x40 #define DPCD_RC02_MAX_LANE_COUNT 0x1f #define DPCD_RC03 0x00003 #define DPCD_RC03_MAX_DOWNSPREAD 0x01 +#define DPCD_RC0E_AUX_RD_INTERVAL 0x0000e /* DPCD Link Configuration */ -#define DPCD_LC00 0x00100 -#define DPCD_LC00_LINK_BW_SET 0xff +#define DPCD_LC00_LINK_BW_SET 0x00100 #define DPCD_LC01 0x00101 #define DPCD_LC01_ENHANCED_FRAME_EN 0x80 #define DPCD_LC01_LANE_COUNT_SET 0x1f @@ -25,6 +24,16 @@ #define DPCD_LC03_PRE_EMPHASIS_SET 0x18 #define DPCD_LC03_MAX_SWING_REACHED 0x04 #define DPCD_LC03_VOLTAGE_SWING_SET 0x03 +#define DPCD_LC0F 0x0010f +#define DPCD_LC0F_LANE1_MAX_POST_CURSOR2_REACHED 0x40 +#define DPCD_LC0F_LANE1_POST_CURSOR2_SET 0x30 +#define DPCD_LC0F_LANE0_MAX_POST_CURSOR2_REACHED 0x04 +#define DPCD_LC0F_LANE0_POST_CURSOR2_SET 0x03 +#define DPCD_LC10 0x00110 +#define DPCD_LC10_LANE3_MAX_POST_CURSOR2_REACHED 0x40 +#define DPCD_LC10_LANE3_POST_CURSOR2_SET 0x30 +#define DPCD_LC10_LANE2_MAX_POST_CURSOR2_REACHED 0x04 +#define DPCD_LC10_LANE2_POST_CURSOR2_SET 0x03 /* DPCD Link/Sink Status */ #define DPCD_LS02 0x00202 @@ -55,24 +64,12 @@ #define DPCD_LS07_LANE3_VOLTAGE_SWING 0x30 #define DPCD_LS07_LANE2_PRE_EMPHASIS 0x0c #define DPCD_LS07_LANE2_VOLTAGE_SWING 0x03 +#define DPCD_LS0C 0x0020c +#define DPCD_LS0C_LANE3_POST_CURSOR2 0xc0 +#define DPCD_LS0C_LANE2_POST_CURSOR2 0x30 +#define DPCD_LS0C_LANE1_POST_CURSOR2 0x0c +#define DPCD_LS0C_LANE0_POST_CURSOR2 0x03 -struct nouveau_disp; -struct dcb_output; - -struct nouveau_dp_func { - int (*pattern)(struct nouveau_disp *, struct dcb_output *, - int head, int pattern); - int (*lnk_ctl)(struct nouveau_disp *, struct dcb_output *, int head, - int link_nr, int link_bw, bool enh_frame); - int (*drv_ctl)(struct nouveau_disp *, struct dcb_output *, int head, - int lane, int swing, int preem); -}; - -extern const struct nouveau_dp_func nv94_sor_dp_func; -extern const struct nouveau_dp_func nvd0_sor_dp_func; -extern const struct nouveau_dp_func nv50_pior_dp_func; - -int nouveau_dp_train(struct nouveau_disp *, const struct nouveau_dp_func *, - struct dcb_output *, int, u32); +void nouveau_dp_train(struct work_struct *); #endif diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/gm107.c b/drivers/gpu/drm/nouveau/core/engine/disp/gm107.c index cf6f59677b7..9fc7447fec9 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/gm107.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/gm107.c @@ -81,7 +81,6 @@ gm107_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->sor.power = nv50_sor_power; priv->sor.hda_eld = nvd0_hda_eld; priv->sor.hdmi = nvd0_hdmi_ctrl; - priv->sor.dp = &nvd0_sor_dp_func; return 0; } @@ -94,6 +93,7 @@ gm107_disp_oclass = &(struct nv50_disp_impl) { .init = _nouveau_disp_init, .fini = _nouveau_disp_fini, }, + .base.outp = nvd0_disp_outp_sclass, .mthd.core = &nve0_disp_mast_mthd_chan, .mthd.base = &nvd0_disp_sync_mthd_chan, .mthd.ovly = &nve0_disp_ovly_mthd_chan, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c index 6c89af79288..a32666ed0c4 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c @@ -51,6 +51,14 @@ nv04_disp_scanoutpos(struct nouveau_object *object, u32 mthd, args->htotal = nv_rd32(priv, 0x680824 + (head * 0x2000)) & 0xffff; args->hblanke = args->htotal - 1; + /* + * If output is vga instead of digital then vtotal/htotal is invalid + * so we have to give up and trigger the timestamping fallback in the + * drm core. + */ + if (!args->vtotal || !args->htotal) + return -ENOTSUPP; + args->time[0] = ktime_to_ns(ktime_get()); line = nv_rd32(priv, 0x600868 + (head * 0x2000)); args->time[1] = ktime_to_ns(ktime_get()); @@ -78,13 +86,13 @@ nv04_disp_sclass[] = { ******************************************************************************/ static void -nv04_disp_vblank_enable(struct nouveau_event *event, int head) +nv04_disp_vblank_enable(struct nouveau_event *event, int type, int head) { nv_wr32(event->priv, 0x600140 + (head * 0x2000) , 0x00000001); } static void -nv04_disp_vblank_disable(struct nouveau_event *event, int head) +nv04_disp_vblank_disable(struct nouveau_event *event, int type, int head) { nv_wr32(event->priv, 0x600140 + (head * 0x2000) , 0x00000000); } @@ -98,12 +106,12 @@ nv04_disp_intr(struct nouveau_subdev *subdev) u32 pvideo; if (crtc0 & 0x00000001) { - nouveau_event_trigger(priv->base.vblank, 0); + nouveau_event_trigger(priv->base.vblank, 1, 0); nv_wr32(priv, 0x600100, 0x00000001); } if (crtc1 & 0x00000001) { - nouveau_event_trigger(priv->base.vblank, 1); + nouveau_event_trigger(priv->base.vblank, 1, 1); nv_wr32(priv, 0x602100, 0x00000001); } diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c index 9a0cab9c3ad..2283c442a10 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c @@ -829,13 +829,13 @@ nv50_disp_base_scanoutpos(struct nouveau_object *object, u32 mthd, } static void -nv50_disp_base_vblank_enable(struct nouveau_event *event, int head) +nv50_disp_base_vblank_enable(struct nouveau_event *event, int type, int head) { nv_mask(event->priv, 0x61002c, (4 << head), (4 << head)); } static void -nv50_disp_base_vblank_disable(struct nouveau_event *event, int head) +nv50_disp_base_vblank_disable(struct nouveau_event *event, int type, int head) { nv_mask(event->priv, 0x61002c, (4 << head), 0); } @@ -1114,19 +1114,20 @@ nv50_disp_intr_error(struct nv50_disp_priv *priv, int chid) nv_wr32(priv, 0x610080 + (chid * 0x08), 0x90000000); } -static u16 -exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, - struct dcb_output *dcb, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, +static struct nvkm_output * +exec_lookup(struct nv50_disp_priv *priv, int head, int or, u32 ctrl, + u32 *data, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_outp *info) { struct nouveau_bios *bios = nouveau_bios(priv); - u16 mask, type, data; + struct nvkm_output *outp; + u16 mask, type; - if (outp < 4) { + if (or < 4) { type = DCB_OUTPUT_ANALOG; mask = 0; } else - if (outp < 8) { + if (or < 8) { switch (ctrl & 0x00000f00) { case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break; case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break; @@ -1136,45 +1137,48 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break; default: nv_error(priv, "unknown SOR mc 0x%08x\n", ctrl); - return 0x0000; + return NULL; } - outp -= 4; + or -= 4; } else { - outp = outp - 8; + or = or - 8; type = 0x0010; mask = 0; switch (ctrl & 0x00000f00) { - case 0x00000000: type |= priv->pior.type[outp]; break; + case 0x00000000: type |= priv->pior.type[or]; break; default: nv_error(priv, "unknown PIOR mc 0x%08x\n", ctrl); - return 0x0000; + return NULL; } } mask = 0x00c0 & (mask << 6); - mask |= 0x0001 << outp; + mask |= 0x0001 << or; mask |= 0x0100 << head; - data = dcb_outp_match(bios, type, mask, ver, hdr, dcb); - if (!data) - return 0x0000; - - /* off-chip encoders require matching the exact encoder type */ - if (dcb->location != 0) - type |= dcb->extdev << 8; + list_for_each_entry(outp, &priv->base.outp, head) { + if ((outp->info.hasht & 0xff) == type && + (outp->info.hashm & mask) == mask) { + *data = nvbios_outp_match(bios, outp->info.hasht, + outp->info.hashm, + ver, hdr, cnt, len, info); + if (!*data) + return NULL; + return outp; + } + } - return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info); + return NULL; } -static bool +static struct nvkm_output * exec_script(struct nv50_disp_priv *priv, int head, int id) { struct nouveau_bios *bios = nouveau_bios(priv); + struct nvkm_output *outp; struct nvbios_outp info; - struct dcb_output dcb; u8 ver, hdr, cnt, len; - u16 data; - u32 ctrl = 0x00000000; + u32 data, ctrl = 0; u32 reg; int i; @@ -1204,36 +1208,35 @@ exec_script(struct nv50_disp_priv *priv, int head, int id) } if (!(ctrl & (1 << head))) - return false; + return NULL; i--; - data = exec_lookup(priv, head, i, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info); - if (data) { + outp = exec_lookup(priv, head, i, ctrl, &data, &ver, &hdr, &cnt, &len, &info); + if (outp) { struct nvbios_init init = { .subdev = nv_subdev(priv), .bios = bios, .offset = info.script[id], - .outp = &dcb, + .outp = &outp->info, .crtc = head, .execute = 1, }; - return nvbios_exec(&init) == 0; + nvbios_exec(&init); } - return false; + return outp; } -static u32 -exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk, - struct dcb_output *outp) +static struct nvkm_output * +exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk, u32 *conf) { struct nouveau_bios *bios = nouveau_bios(priv); + struct nvkm_output *outp; struct nvbios_outp info1; struct nvbios_ocfg info2; u8 ver, hdr, cnt, len; - u32 ctrl = 0x00000000; - u32 data, conf = ~0; + u32 data, ctrl = 0; u32 reg; int i; @@ -1263,37 +1266,37 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk, } if (!(ctrl & (1 << head))) - return conf; + return NULL; i--; - data = exec_lookup(priv, head, i, ctrl, outp, &ver, &hdr, &cnt, &len, &info1); - if (!data) - return conf; + outp = exec_lookup(priv, head, i, ctrl, &data, &ver, &hdr, &cnt, &len, &info1); + if (!outp) + return NULL; - if (outp->location == 0) { - switch (outp->type) { + if (outp->info.location == 0) { + switch (outp->info.type) { case DCB_OUTPUT_TMDS: - conf = (ctrl & 0x00000f00) >> 8; + *conf = (ctrl & 0x00000f00) >> 8; if (pclk >= 165000) - conf |= 0x0100; + *conf |= 0x0100; break; case DCB_OUTPUT_LVDS: - conf = priv->sor.lvdsconf; + *conf = priv->sor.lvdsconf; break; case DCB_OUTPUT_DP: - conf = (ctrl & 0x00000f00) >> 8; + *conf = (ctrl & 0x00000f00) >> 8; break; case DCB_OUTPUT_ANALOG: default: - conf = 0x00ff; + *conf = 0x00ff; break; } } else { - conf = (ctrl & 0x00000f00) >> 8; + *conf = (ctrl & 0x00000f00) >> 8; pclk = pclk / 2; } - data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2); + data = nvbios_ocfg_match(bios, data, *conf, &ver, &hdr, &cnt, &len, &info2); if (data && id < 0xff) { data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk); if (data) { @@ -1301,7 +1304,7 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk, .subdev = nv_subdev(priv), .bios = bios, .offset = data, - .outp = outp, + .outp = &outp->info, .crtc = head, .execute = 1, }; @@ -1310,7 +1313,7 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk, } } - return conf; + return outp; } static void @@ -1322,7 +1325,35 @@ nv50_disp_intr_unk10_0(struct nv50_disp_priv *priv, int head) static void nv50_disp_intr_unk20_0(struct nv50_disp_priv *priv, int head) { - exec_script(priv, head, 2); + struct nvkm_output *outp = exec_script(priv, head, 2); + + /* the binary driver does this outside of the supervisor handling + * (after the third supervisor from a detach). we (currently?) + * allow both detach/attach to happen in the same set of + * supervisor interrupts, so it would make sense to execute this + * (full power down?) script after all the detach phases of the + * supervisor handling. like with training if needed from the + * second supervisor, nvidia doesn't do this, so who knows if it's + * entirely safe, but it does appear to work.. + * + * without this script being run, on some configurations i've + * seen, switching from DP to TMDS on a DP connector may result + * in a blank screen (SOR_PWR off/on can restore it) + */ + if (outp && outp->info.type == DCB_OUTPUT_DP) { + struct nvkm_output_dp *outpdp = (void *)outp; + struct nvbios_init init = { + .subdev = nv_subdev(priv), + .bios = nouveau_bios(priv), + .outp = &outp->info, + .crtc = head, + .offset = outpdp->info.script[4], + .execute = 1, + }; + + nvbios_exec(&init); + atomic_set(&outpdp->lt.done, 0); + } } static void @@ -1444,56 +1475,83 @@ nv50_disp_intr_unk20_2_dp(struct nv50_disp_priv *priv, static void nv50_disp_intr_unk20_2(struct nv50_disp_priv *priv, int head) { - struct dcb_output outp; + struct nvkm_output *outp; u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff; u32 hval, hreg = 0x614200 + (head * 0x800); u32 oval, oreg; - u32 mask; - u32 conf = exec_clkcmp(priv, head, 0xff, pclk, &outp); - if (conf != ~0) { - if (outp.location == 0 && outp.type == DCB_OUTPUT_DP) { - u32 soff = (ffs(outp.or) - 1) * 0x08; - u32 ctrl = nv_rd32(priv, 0x610794 + soff); - u32 datarate; - - switch ((ctrl & 0x000f0000) >> 16) { - case 6: datarate = pclk * 30 / 8; break; - case 5: datarate = pclk * 24 / 8; break; - case 2: - default: - datarate = pclk * 18 / 8; - break; - } + u32 mask, conf; - nouveau_dp_train(&priv->base, priv->sor.dp, - &outp, head, datarate); - } + outp = exec_clkcmp(priv, head, 0xff, pclk, &conf); + if (!outp) + return; + + /* we allow both encoder attach and detach operations to occur + * within a single supervisor (ie. modeset) sequence. the + * encoder detach scripts quite often switch off power to the + * lanes, which requires the link to be re-trained. + * + * this is not generally an issue as the sink "must" (heh) + * signal an irq when it's lost sync so the driver can + * re-train. + * + * however, on some boards, if one does not configure at least + * the gpu side of the link *before* attaching, then various + * things can go horribly wrong (PDISP disappearing from mmio, + * third supervisor never happens, etc). + * + * the solution is simply to retrain here, if necessary. last + * i checked, the binary driver userspace does not appear to + * trigger this situation (it forces an UPDATE between steps). + */ + if (outp->info.type == DCB_OUTPUT_DP) { + u32 soff = (ffs(outp->info.or) - 1) * 0x08; + u32 ctrl, datarate; - exec_clkcmp(priv, head, 0, pclk, &outp); - - if (!outp.location && outp.type == DCB_OUTPUT_ANALOG) { - oreg = 0x614280 + (ffs(outp.or) - 1) * 0x800; - oval = 0x00000000; - hval = 0x00000000; - mask = 0xffffffff; - } else - if (!outp.location) { - if (outp.type == DCB_OUTPUT_DP) - nv50_disp_intr_unk20_2_dp(priv, &outp, pclk); - oreg = 0x614300 + (ffs(outp.or) - 1) * 0x800; - oval = (conf & 0x0100) ? 0x00000101 : 0x00000000; - hval = 0x00000000; - mask = 0x00000707; + if (outp->info.location == 0) { + ctrl = nv_rd32(priv, 0x610794 + soff); + soff = 1; } else { - oreg = 0x614380 + (ffs(outp.or) - 1) * 0x800; - oval = 0x00000001; - hval = 0x00000001; - mask = 0x00000707; + ctrl = nv_rd32(priv, 0x610b80 + soff); + soff = 2; + } + + switch ((ctrl & 0x000f0000) >> 16) { + case 6: datarate = pclk * 30; break; + case 5: datarate = pclk * 24; break; + case 2: + default: + datarate = pclk * 18; + break; } - nv_mask(priv, hreg, 0x0000000f, hval); - nv_mask(priv, oreg, mask, oval); + if (nvkm_output_dp_train(outp, datarate / soff, true)) + ERR("link not trained before attach\n"); } + + exec_clkcmp(priv, head, 0, pclk, &conf); + + if (!outp->info.location && outp->info.type == DCB_OUTPUT_ANALOG) { + oreg = 0x614280 + (ffs(outp->info.or) - 1) * 0x800; + oval = 0x00000000; + hval = 0x00000000; + mask = 0xffffffff; + } else + if (!outp->info.location) { + if (outp->info.type == DCB_OUTPUT_DP) + nv50_disp_intr_unk20_2_dp(priv, &outp->info, pclk); + oreg = 0x614300 + (ffs(outp->info.or) - 1) * 0x800; + oval = (conf & 0x0100) ? 0x00000101 : 0x00000000; + hval = 0x00000000; + mask = 0x00000707; + } else { + oreg = 0x614380 + (ffs(outp->info.or) - 1) * 0x800; + oval = 0x00000001; + hval = 0x00000001; + mask = 0x00000707; + } + + nv_mask(priv, hreg, 0x0000000f, hval); + nv_mask(priv, oreg, mask, oval); } /* If programming a TMDS output on a SOR that can also be configured for @@ -1521,30 +1579,16 @@ nv50_disp_intr_unk40_0_tmds(struct nv50_disp_priv *priv, struct dcb_output *outp static void nv50_disp_intr_unk40_0(struct nv50_disp_priv *priv, int head) { - struct dcb_output outp; + struct nvkm_output *outp; u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff; - if (exec_clkcmp(priv, head, 1, pclk, &outp) != ~0) { - if (outp.location == 0 && outp.type == DCB_OUTPUT_TMDS) - nv50_disp_intr_unk40_0_tmds(priv, &outp); - else - if (outp.location == 1 && outp.type == DCB_OUTPUT_DP) { - u32 soff = (ffs(outp.or) - 1) * 0x08; - u32 ctrl = nv_rd32(priv, 0x610b84 + soff); - u32 datarate; - - switch ((ctrl & 0x000f0000) >> 16) { - case 6: datarate = pclk * 30 / 8; break; - case 5: datarate = pclk * 24 / 8; break; - case 2: - default: - datarate = pclk * 18 / 8; - break; - } + u32 conf; - nouveau_dp_train(&priv->base, priv->pior.dp, - &outp, head, datarate); - } - } + outp = exec_clkcmp(priv, head, 1, pclk, &conf); + if (!outp) + return; + + if (outp->info.location == 0 && outp->info.type == DCB_OUTPUT_TMDS) + nv50_disp_intr_unk40_0_tmds(priv, &outp->info); } void @@ -1610,13 +1654,13 @@ nv50_disp_intr(struct nouveau_subdev *subdev) } if (intr1 & 0x00000004) { - nouveau_event_trigger(priv->base.vblank, 0); + nouveau_event_trigger(priv->base.vblank, 1, 0); nv_wr32(priv, 0x610024, 0x00000004); intr1 &= ~0x00000004; } if (intr1 & 0x00000008) { - nouveau_event_trigger(priv->base.vblank, 1); + nouveau_event_trigger(priv->base.vblank, 1, 1); nv_wr32(priv, 0x610024, 0x00000008); intr1 &= ~0x00000008; } @@ -1656,11 +1700,16 @@ nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->dac.sense = nv50_dac_sense; priv->sor.power = nv50_sor_power; priv->pior.power = nv50_pior_power; - priv->pior.dp = &nv50_pior_dp_func; return 0; } struct nouveau_oclass * +nv50_disp_outp_sclass[] = { + &nv50_pior_dp_impl.base.base, + NULL +}; + +struct nouveau_oclass * nv50_disp_oclass = &(struct nv50_disp_impl) { .base.base.handle = NV_ENGINE(DISP, 0x50), .base.base.ofuncs = &(struct nouveau_ofuncs) { @@ -1669,6 +1718,7 @@ nv50_disp_oclass = &(struct nv50_disp_impl) { .init = _nouveau_disp_init, .fini = _nouveau_disp_fini, }, + .base.outp = nv50_disp_outp_sclass, .mthd.core = &nv50_disp_mast_mthd_chan, .mthd.base = &nv50_disp_sync_mthd_chan, .mthd.ovly = &nv50_disp_ovly_mthd_chan, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h index 48d59db47f0..1a886472b6f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h @@ -11,6 +11,8 @@ #include "dport.h" #include "priv.h" +#include "outp.h" +#include "outpdp.h" struct nv50_disp_impl { struct nouveau_disp_impl base; @@ -43,13 +45,11 @@ struct nv50_disp_priv { int (*hda_eld)(struct nv50_disp_priv *, int sor, u8 *, u32); int (*hdmi)(struct nv50_disp_priv *, int head, int sor, u32); u32 lvdsconf; - const struct nouveau_dp_func *dp; } sor; struct { int nr; int (*power)(struct nv50_disp_priv *, int ext, u32 data); u8 type[3]; - const struct nouveau_dp_func *dp; } pior; }; @@ -199,4 +199,14 @@ void nvd0_disp_intr(struct nouveau_subdev *); extern const struct nv50_disp_mthd_chan nve0_disp_mast_mthd_chan; extern const struct nv50_disp_mthd_chan nve0_disp_ovly_mthd_chan; +extern struct nvkm_output_dp_impl nv50_pior_dp_impl; +extern struct nouveau_oclass *nv50_disp_outp_sclass[]; + +extern struct nvkm_output_dp_impl nv94_sor_dp_impl; +int nv94_sor_dp_lnk_pwr(struct nvkm_output_dp *, int); +extern struct nouveau_oclass *nv94_disp_outp_sclass[]; + +extern struct nvkm_output_dp_impl nvd0_sor_dp_impl; +extern struct nouveau_oclass *nvd0_disp_outp_sclass[]; + #endif diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c index 98c5b19bc2b..1cc62e43468 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c @@ -264,7 +264,6 @@ nv84_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->sor.power = nv50_sor_power; priv->sor.hdmi = nv84_hdmi_ctrl; priv->pior.power = nv50_pior_power; - priv->pior.dp = &nv50_pior_dp_func; return 0; } @@ -277,6 +276,7 @@ nv84_disp_oclass = &(struct nv50_disp_impl) { .init = _nouveau_disp_init, .fini = _nouveau_disp_fini, }, + .base.outp = nv50_disp_outp_sclass, .mthd.core = &nv84_disp_mast_mthd_chan, .mthd.base = &nv84_disp_sync_mthd_chan, .mthd.ovly = &nv84_disp_ovly_mthd_chan, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c index 6844061c7e0..4f718a9f5ae 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c @@ -77,6 +77,7 @@ nv94_disp_base_omthds[] = { { SOR_MTHD(NV50_DISP_SOR_PWR) , nv50_sor_mthd }, { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd }, { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, + { SOR_MTHD(NV94_DISP_SOR_DP_PWR) , nv50_sor_mthd }, { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd }, { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd }, { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd }, @@ -122,13 +123,18 @@ nv94_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->dac.sense = nv50_dac_sense; priv->sor.power = nv50_sor_power; priv->sor.hdmi = nv84_hdmi_ctrl; - priv->sor.dp = &nv94_sor_dp_func; priv->pior.power = nv50_pior_power; - priv->pior.dp = &nv50_pior_dp_func; return 0; } struct nouveau_oclass * +nv94_disp_outp_sclass[] = { + &nv50_pior_dp_impl.base.base, + &nv94_sor_dp_impl.base.base, + NULL +}; + +struct nouveau_oclass * nv94_disp_oclass = &(struct nv50_disp_impl) { .base.base.handle = NV_ENGINE(DISP, 0x88), .base.base.ofuncs = &(struct nouveau_ofuncs) { @@ -137,6 +143,7 @@ nv94_disp_oclass = &(struct nv50_disp_impl) { .init = _nouveau_disp_init, .fini = _nouveau_disp_fini, }, + .base.outp = nv94_disp_outp_sclass, .mthd.core = &nv94_disp_mast_mthd_chan, .mthd.base = &nv84_disp_sync_mthd_chan, .mthd.ovly = &nv84_disp_ovly_mthd_chan, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c index 88c96241c02..6237a9a36f7 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c @@ -126,7 +126,6 @@ nva0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->sor.power = nv50_sor_power; priv->sor.hdmi = nv84_hdmi_ctrl; priv->pior.power = nv50_pior_power; - priv->pior.dp = &nv50_pior_dp_func; return 0; } @@ -139,6 +138,7 @@ nva0_disp_oclass = &(struct nv50_disp_impl) { .init = _nouveau_disp_init, .fini = _nouveau_disp_fini, }, + .base.outp = nv50_disp_outp_sclass, .mthd.core = &nv84_disp_mast_mthd_chan, .mthd.base = &nv84_disp_sync_mthd_chan, .mthd.ovly = &nva0_disp_ovly_mthd_chan, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c b/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c index 46cb2ce0e82..019124d4782 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c @@ -50,6 +50,7 @@ nva3_disp_base_omthds[] = { { SOR_MTHD(NVA3_DISP_SOR_HDA_ELD) , nv50_sor_mthd }, { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd }, { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, + { SOR_MTHD(NV94_DISP_SOR_DP_PWR) , nv50_sor_mthd }, { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd }, { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd }, { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd }, @@ -96,9 +97,7 @@ nva3_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->sor.power = nv50_sor_power; priv->sor.hda_eld = nva3_hda_eld; priv->sor.hdmi = nva3_hdmi_ctrl; - priv->sor.dp = &nv94_sor_dp_func; priv->pior.power = nv50_pior_power; - priv->pior.dp = &nv50_pior_dp_func; return 0; } @@ -111,6 +110,7 @@ nva3_disp_oclass = &(struct nv50_disp_impl) { .init = _nouveau_disp_init, .fini = _nouveau_disp_fini, }, + .base.outp = nv94_disp_outp_sclass, .mthd.core = &nv94_disp_mast_mthd_chan, .mthd.base = &nv84_disp_sync_mthd_chan, .mthd.ovly = &nv84_disp_ovly_mthd_chan, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c index 7762665ad8f..fa30d8196f3 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c @@ -748,13 +748,13 @@ nvd0_disp_base_scanoutpos(struct nouveau_object *object, u32 mthd, } static void -nvd0_disp_base_vblank_enable(struct nouveau_event *event, int head) +nvd0_disp_base_vblank_enable(struct nouveau_event *event, int type, int head) { nv_mask(event->priv, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000001); } static void -nvd0_disp_base_vblank_disable(struct nouveau_event *event, int head) +nvd0_disp_base_vblank_disable(struct nouveau_event *event, int type, int head) { nv_mask(event->priv, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000000); } @@ -887,6 +887,7 @@ nvd0_disp_base_omthds[] = { { SOR_MTHD(NVA3_DISP_SOR_HDA_ELD) , nv50_sor_mthd }, { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd }, { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, + { SOR_MTHD(NV94_DISP_SOR_DP_PWR) , nv50_sor_mthd }, { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd }, { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd }, { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd }, @@ -915,19 +916,20 @@ nvd0_disp_sclass[] = { * Display engine implementation ******************************************************************************/ -static u16 -exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, - struct dcb_output *dcb, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, +static struct nvkm_output * +exec_lookup(struct nv50_disp_priv *priv, int head, int or, u32 ctrl, + u32 *data, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_outp *info) { struct nouveau_bios *bios = nouveau_bios(priv); - u16 mask, type, data; + struct nvkm_output *outp; + u16 mask, type; - if (outp < 4) { + if (or < 4) { type = DCB_OUTPUT_ANALOG; mask = 0; } else { - outp -= 4; + or -= 4; switch (ctrl & 0x00000f00) { case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break; case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break; @@ -939,101 +941,106 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, nv_error(priv, "unknown SOR mc 0x%08x\n", ctrl); return 0x0000; } - dcb->sorconf.link = mask; } mask = 0x00c0 & (mask << 6); - mask |= 0x0001 << outp; + mask |= 0x0001 << or; mask |= 0x0100 << head; - data = dcb_outp_match(bios, type, mask, ver, hdr, dcb); - if (!data) - return 0x0000; + list_for_each_entry(outp, &priv->base.outp, head) { + if ((outp->info.hasht & 0xff) == type && + (outp->info.hashm & mask) == mask) { + *data = nvbios_outp_match(bios, outp->info.hasht, + outp->info.hashm, + ver, hdr, cnt, len, info); + if (!*data) + return NULL; + return outp; + } + } - return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info); + return NULL; } -static bool +static struct nvkm_output * exec_script(struct nv50_disp_priv *priv, int head, int id) { struct nouveau_bios *bios = nouveau_bios(priv); + struct nvkm_output *outp; struct nvbios_outp info; - struct dcb_output dcb; u8 ver, hdr, cnt, len; - u32 ctrl = 0x00000000; - u16 data; - int outp; + u32 data, ctrl = 0; + int or; - for (outp = 0; !(ctrl & (1 << head)) && outp < 8; outp++) { - ctrl = nv_rd32(priv, 0x640180 + (outp * 0x20)); + for (or = 0; !(ctrl & (1 << head)) && or < 8; or++) { + ctrl = nv_rd32(priv, 0x640180 + (or * 0x20)); if (ctrl & (1 << head)) break; } - if (outp == 8) - return false; + if (or == 8) + return NULL; - data = exec_lookup(priv, head, outp, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info); - if (data) { + outp = exec_lookup(priv, head, or, ctrl, &data, &ver, &hdr, &cnt, &len, &info); + if (outp) { struct nvbios_init init = { .subdev = nv_subdev(priv), .bios = bios, .offset = info.script[id], - .outp = &dcb, + .outp = &outp->info, .crtc = head, .execute = 1, }; - return nvbios_exec(&init) == 0; + nvbios_exec(&init); } - return false; + return outp; } -static u32 -exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, - u32 pclk, struct dcb_output *dcb) +static struct nvkm_output * +exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk, u32 *conf) { struct nouveau_bios *bios = nouveau_bios(priv); + struct nvkm_output *outp; struct nvbios_outp info1; struct nvbios_ocfg info2; u8 ver, hdr, cnt, len; - u32 ctrl = 0x00000000; - u32 data, conf = ~0; - int outp; + u32 data, ctrl = 0; + int or; - for (outp = 0; !(ctrl & (1 << head)) && outp < 8; outp++) { - ctrl = nv_rd32(priv, 0x660180 + (outp * 0x20)); + for (or = 0; !(ctrl & (1 << head)) && or < 8; or++) { + ctrl = nv_rd32(priv, 0x660180 + (or * 0x20)); if (ctrl & (1 << head)) break; } - if (outp == 8) - return false; + if (or == 8) + return NULL; - data = exec_lookup(priv, head, outp, ctrl, dcb, &ver, &hdr, &cnt, &len, &info1); - if (data == 0x0000) - return conf; + outp = exec_lookup(priv, head, or, ctrl, &data, &ver, &hdr, &cnt, &len, &info1); + if (!outp) + return NULL; - switch (dcb->type) { + switch (outp->info.type) { case DCB_OUTPUT_TMDS: - conf = (ctrl & 0x00000f00) >> 8; + *conf = (ctrl & 0x00000f00) >> 8; if (pclk >= 165000) - conf |= 0x0100; + *conf |= 0x0100; break; case DCB_OUTPUT_LVDS: - conf = priv->sor.lvdsconf; + *conf = priv->sor.lvdsconf; break; case DCB_OUTPUT_DP: - conf = (ctrl & 0x00000f00) >> 8; + *conf = (ctrl & 0x00000f00) >> 8; break; case DCB_OUTPUT_ANALOG: default: - conf = 0x00ff; + *conf = 0x00ff; break; } - data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2); + data = nvbios_ocfg_match(bios, data, *conf, &ver, &hdr, &cnt, &len, &info2); if (data && id < 0xff) { data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk); if (data) { @@ -1041,7 +1048,7 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, .subdev = nv_subdev(priv), .bios = bios, .offset = data, - .outp = dcb, + .outp = &outp->info, .crtc = head, .execute = 1, }; @@ -1050,7 +1057,7 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, } } - return conf; + return outp; } static void @@ -1062,7 +1069,23 @@ nvd0_disp_intr_unk1_0(struct nv50_disp_priv *priv, int head) static void nvd0_disp_intr_unk2_0(struct nv50_disp_priv *priv, int head) { - exec_script(priv, head, 2); + struct nvkm_output *outp = exec_script(priv, head, 2); + + /* see note in nv50_disp_intr_unk20_0() */ + if (outp && outp->info.type == DCB_OUTPUT_DP) { + struct nvkm_output_dp *outpdp = (void *)outp; + struct nvbios_init init = { + .subdev = nv_subdev(priv), + .bios = nouveau_bios(priv), + .outp = &outp->info, + .crtc = head, + .offset = outpdp->info.script[4], + .execute = 1, + }; + + nvbios_exec(&init); + atomic_set(&outpdp->lt.done, 0); + } } static void @@ -1124,49 +1147,52 @@ nvd0_disp_intr_unk2_2_tu(struct nv50_disp_priv *priv, int head, static void nvd0_disp_intr_unk2_2(struct nv50_disp_priv *priv, int head) { - struct dcb_output outp; + struct nvkm_output *outp; u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000; - u32 conf = exec_clkcmp(priv, head, 0xff, pclk, &outp); - if (conf != ~0) { - u32 addr, data; - - if (outp.type == DCB_OUTPUT_DP) { - u32 sync = nv_rd32(priv, 0x660404 + (head * 0x300)); - switch ((sync & 0x000003c0) >> 6) { - case 6: pclk = pclk * 30 / 8; break; - case 5: pclk = pclk * 24 / 8; break; - case 2: - default: - pclk = pclk * 18 / 8; - break; - } - - nouveau_dp_train(&priv->base, priv->sor.dp, - &outp, head, pclk); + u32 conf, addr, data; + + outp = exec_clkcmp(priv, head, 0xff, pclk, &conf); + if (!outp) + return; + + /* see note in nv50_disp_intr_unk20_2() */ + if (outp->info.type == DCB_OUTPUT_DP) { + u32 sync = nv_rd32(priv, 0x660404 + (head * 0x300)); + switch ((sync & 0x000003c0) >> 6) { + case 6: pclk = pclk * 30; break; + case 5: pclk = pclk * 24; break; + case 2: + default: + pclk = pclk * 18; + break; } - exec_clkcmp(priv, head, 0, pclk, &outp); + if (nvkm_output_dp_train(outp, pclk, true)) + ERR("link not trained before attach\n"); + } - if (outp.type == DCB_OUTPUT_ANALOG) { - addr = 0x612280 + (ffs(outp.or) - 1) * 0x800; - data = 0x00000000; - } else { - if (outp.type == DCB_OUTPUT_DP) - nvd0_disp_intr_unk2_2_tu(priv, head, &outp); - addr = 0x612300 + (ffs(outp.or) - 1) * 0x800; - data = (conf & 0x0100) ? 0x00000101 : 0x00000000; - } + exec_clkcmp(priv, head, 0, pclk, &conf); - nv_mask(priv, addr, 0x00000707, data); + if (outp->info.type == DCB_OUTPUT_ANALOG) { + addr = 0x612280 + (ffs(outp->info.or) - 1) * 0x800; + data = 0x00000000; + } else { + if (outp->info.type == DCB_OUTPUT_DP) + nvd0_disp_intr_unk2_2_tu(priv, head, &outp->info); + addr = 0x612300 + (ffs(outp->info.or) - 1) * 0x800; + data = (conf & 0x0100) ? 0x00000101 : 0x00000000; } + + nv_mask(priv, addr, 0x00000707, data); } static void nvd0_disp_intr_unk4_0(struct nv50_disp_priv *priv, int head) { - struct dcb_output outp; u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000; - exec_clkcmp(priv, head, 1, pclk, &outp); + u32 conf; + + exec_clkcmp(priv, head, 1, pclk, &conf); } void @@ -1240,7 +1266,7 @@ nvd0_disp_intr_error(struct nv50_disp_priv *priv, int chid) chid, (mthd & 0x0000ffc), data, mthd, unkn); if (chid == 0) { - switch (mthd) { + switch (mthd & 0xffc) { case 0x0080: nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 0, impl->mthd.core); @@ -1250,7 +1276,7 @@ nvd0_disp_intr_error(struct nv50_disp_priv *priv, int chid) } } else if (chid <= 4) { - switch (mthd) { + switch (mthd & 0xffc) { case 0x0080: nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 1, impl->mthd.base); @@ -1260,7 +1286,7 @@ nvd0_disp_intr_error(struct nv50_disp_priv *priv, int chid) } } else if (chid <= 8) { - switch (mthd) { + switch (mthd & 0xffc) { case 0x0080: nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 5, impl->mthd.ovly); @@ -1317,7 +1343,7 @@ nvd0_disp_intr(struct nouveau_subdev *subdev) if (mask & intr) { u32 stat = nv_rd32(priv, 0x6100bc + (i * 0x800)); if (stat & 0x00000001) - nouveau_event_trigger(priv->base.vblank, i); + nouveau_event_trigger(priv->base.vblank, 1, i); nv_mask(priv, 0x6100bc + (i * 0x800), 0, 0); nv_rd32(priv, 0x6100c0 + (i * 0x800)); } @@ -1352,11 +1378,16 @@ nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->sor.power = nv50_sor_power; priv->sor.hda_eld = nvd0_hda_eld; priv->sor.hdmi = nvd0_hdmi_ctrl; - priv->sor.dp = &nvd0_sor_dp_func; return 0; } struct nouveau_oclass * +nvd0_disp_outp_sclass[] = { + &nvd0_sor_dp_impl.base.base, + NULL +}; + +struct nouveau_oclass * nvd0_disp_oclass = &(struct nv50_disp_impl) { .base.base.handle = NV_ENGINE(DISP, 0x90), .base.base.ofuncs = &(struct nouveau_ofuncs) { @@ -1365,6 +1396,7 @@ nvd0_disp_oclass = &(struct nv50_disp_impl) { .init = _nouveau_disp_init, .fini = _nouveau_disp_fini, }, + .base.outp = nvd0_disp_outp_sclass, .mthd.core = &nvd0_disp_mast_mthd_chan, .mthd.base = &nvd0_disp_sync_mthd_chan, .mthd.ovly = &nvd0_disp_ovly_mthd_chan, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c index 44e0b8f34c1..11328e3f5df 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c @@ -246,7 +246,6 @@ nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->sor.power = nv50_sor_power; priv->sor.hda_eld = nvd0_hda_eld; priv->sor.hdmi = nvd0_hdmi_ctrl; - priv->sor.dp = &nvd0_sor_dp_func; return 0; } @@ -259,6 +258,7 @@ nve0_disp_oclass = &(struct nv50_disp_impl) { .init = _nouveau_disp_init, .fini = _nouveau_disp_fini, }, + .base.outp = nvd0_disp_outp_sclass, .mthd.core = &nve0_disp_mast_mthd_chan, .mthd.base = &nvd0_disp_sync_mthd_chan, .mthd.ovly = &nve0_disp_ovly_mthd_chan, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c index 482585d375f..104388081d7 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c @@ -81,7 +81,6 @@ nvf0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->sor.power = nv50_sor_power; priv->sor.hda_eld = nvd0_hda_eld; priv->sor.hdmi = nvd0_hdmi_ctrl; - priv->sor.dp = &nvd0_sor_dp_func; return 0; } @@ -94,6 +93,7 @@ nvf0_disp_oclass = &(struct nv50_disp_impl) { .init = _nouveau_disp_init, .fini = _nouveau_disp_fini, }, + .base.outp = nvd0_disp_outp_sclass, .mthd.core = &nve0_disp_mast_mthd_chan, .mthd.base = &nvd0_disp_sync_mthd_chan, .mthd.ovly = &nve0_disp_ovly_mthd_chan, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/outp.c b/drivers/gpu/drm/nouveau/core/engine/disp/outp.c new file mode 100644 index 00000000000..ad9ba7ccec7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/outp.c @@ -0,0 +1,137 @@ +/* + * Copyright 2014 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 <subdev/i2c.h> +#include <subdev/bios.h> +#include <subdev/bios/conn.h> + +#include "outp.h" + +int +_nvkm_output_fini(struct nouveau_object *object, bool suspend) +{ + struct nvkm_output *outp = (void *)object; + nv_ofuncs(outp->conn)->fini(nv_object(outp->conn), suspend); + return nouveau_object_fini(&outp->base, suspend); +} + +int +_nvkm_output_init(struct nouveau_object *object) +{ + struct nvkm_output *outp = (void *)object; + int ret = nouveau_object_init(&outp->base); + if (ret == 0) + nv_ofuncs(outp->conn)->init(nv_object(outp->conn)); + return 0; +} + +void +_nvkm_output_dtor(struct nouveau_object *object) +{ + struct nvkm_output *outp = (void *)object; + list_del(&outp->head); + nouveau_object_ref(NULL, (void *)&outp->conn); + nouveau_object_destroy(&outp->base); +} + +int +nvkm_output_create_(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, + struct dcb_output *dcbE, int index, + int length, void **pobject) +{ + struct nouveau_bios *bios = nouveau_bios(engine); + struct nouveau_i2c *i2c = nouveau_i2c(parent); + struct nouveau_disp *disp = (void *)engine; + struct nvbios_connE connE; + struct nvkm_output *outp; + u8 ver, hdr; + u32 data; + int ret; + + ret = nouveau_object_create_(parent, engine, oclass, 0, length, pobject); + outp = *pobject; + if (ret) + return ret; + + outp->info = *dcbE; + outp->index = index; + + DBG("type %02x loc %d or %d link %d con %x edid %x bus %d head %x\n", + dcbE->type, dcbE->location, dcbE->or, dcbE->type >= 2 ? + dcbE->sorconf.link : 0, dcbE->connector, dcbE->i2c_index, + dcbE->bus, dcbE->heads); + + outp->port = i2c->find(i2c, outp->info.i2c_index); + outp->edid = outp->port; + + data = nvbios_connEp(bios, outp->info.connector, &ver, &hdr, &connE); + if (!data) { + DBG("vbios connector data not found\n"); + memset(&connE, 0x00, sizeof(connE)); + connE.type = DCB_CONNECTOR_NONE; + } + + ret = nouveau_object_ctor(parent, engine, nvkm_connector_oclass, + &connE, outp->info.connector, + (struct nouveau_object **)&outp->conn); + if (ret < 0) { + ERR("error %d creating connector, disabling\n", ret); + return ret; + } + + list_add_tail(&outp->head, &disp->outp); + return 0; +} + +int +_nvkm_output_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *dcbE, u32 index, + struct nouveau_object **pobject) +{ + struct nvkm_output *outp; + int ret; + + ret = nvkm_output_create(parent, engine, oclass, dcbE, index, &outp); + *pobject = nv_object(outp); + if (ret) + return ret; + + return 0; +} + +struct nouveau_oclass * +nvkm_output_oclass = &(struct nvkm_output_impl) { + .base = { + .handle = 0, + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nvkm_output_ctor, + .dtor = _nvkm_output_dtor, + .init = _nvkm_output_init, + .fini = _nvkm_output_fini, + }, + }, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/outp.h b/drivers/gpu/drm/nouveau/core/engine/disp/outp.h new file mode 100644 index 00000000000..bc76fbf8571 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/outp.h @@ -0,0 +1,59 @@ +#ifndef __NVKM_DISP_OUTP_H__ +#define __NVKM_DISP_OUTP_H__ + +#include "priv.h" + +struct nvkm_output { + struct nouveau_object base; + struct list_head head; + + struct dcb_output info; + int index; + + struct nouveau_i2c_port *port; + struct nouveau_i2c_port *edid; + + struct nvkm_connector *conn; +}; + +#define nvkm_output_create(p,e,c,b,i,d) \ + nvkm_output_create_((p), (e), (c), (b), (i), sizeof(**d), (void **)d) +#define nvkm_output_destroy(d) ({ \ + struct nvkm_output *_outp = (d); \ + _nvkm_output_dtor(nv_object(_outp)); \ +}) +#define nvkm_output_init(d) ({ \ + struct nvkm_output *_outp = (d); \ + _nvkm_output_init(nv_object(_outp)); \ +}) +#define nvkm_output_fini(d,s) ({ \ + struct nvkm_output *_outp = (d); \ + _nvkm_output_fini(nv_object(_outp), (s)); \ +}) + +int nvkm_output_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, struct dcb_output *, + int, int, void **); + +int _nvkm_output_ctor(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, void *, u32, + struct nouveau_object **); +void _nvkm_output_dtor(struct nouveau_object *); +int _nvkm_output_init(struct nouveau_object *); +int _nvkm_output_fini(struct nouveau_object *, bool); + +struct nvkm_output_impl { + struct nouveau_oclass base; +}; + +#ifndef MSG +#define MSG(l,f,a...) do { \ + struct nvkm_output *_outp = (void *)outp; \ + nv_##l(nv_object(outp)->engine, "%02x:%04x:%04x: "f, _outp->index, \ + _outp->info.hasht, _outp->info.hashm, ##a); \ +} while(0) +#define DBG(f,a...) MSG(debug, f, ##a) +#define ERR(f,a...) MSG(error, f, ##a) +#endif + +#endif diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.c b/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.c new file mode 100644 index 00000000000..eb2d7789555 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.c @@ -0,0 +1,278 @@ +/* + * Copyright 2014 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 <subdev/i2c.h> + +#include "outpdp.h" +#include "conn.h" +#include "dport.h" + +int +nvkm_output_dp_train(struct nvkm_output *base, u32 datarate, bool wait) +{ + struct nvkm_output_dp *outp = (void *)base; + bool retrain = true; + u8 link[2], stat[3]; + u32 linkrate; + int ret, i; + + /* check that the link is trained at a high enough rate */ + ret = nv_rdaux(outp->base.edid, DPCD_LC00_LINK_BW_SET, link, 2); + if (ret) { + DBG("failed to read link config, assuming no sink\n"); + goto done; + } + + linkrate = link[0] * 27000 * (link[1] & DPCD_LC01_LANE_COUNT_SET); + linkrate = (linkrate * 8) / 10; /* 8B/10B coding overhead */ + datarate = (datarate + 9) / 10; /* -> decakilobits */ + if (linkrate < datarate) { + DBG("link not trained at sufficient rate\n"); + goto done; + } + + /* check that link is still trained */ + ret = nv_rdaux(outp->base.edid, DPCD_LS02, stat, 3); + if (ret) { + DBG("failed to read link status, assuming no sink\n"); + goto done; + } + + if (stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE) { + for (i = 0; i < (link[1] & DPCD_LC01_LANE_COUNT_SET); i++) { + u8 lane = (stat[i >> 1] >> ((i & 1) * 4)) & 0x0f; + if (!(lane & DPCD_LS02_LANE0_CR_DONE) || + !(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) || + !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) { + DBG("lane %d not equalised\n", lane); + goto done; + } + } + retrain = false; + } else { + DBG("no inter-lane alignment\n"); + } + +done: + if (retrain || !atomic_read(&outp->lt.done)) { + /* no sink, but still need to configure source */ + if (outp->dpcd[DPCD_RC00_DPCD_REV] == 0x00) { + outp->dpcd[DPCD_RC01_MAX_LINK_RATE] = + outp->base.info.dpconf.link_bw; + outp->dpcd[DPCD_RC02] = + outp->base.info.dpconf.link_nr; + } + atomic_set(&outp->lt.done, 0); + schedule_work(&outp->lt.work); + } else { + nouveau_event_get(outp->irq); + } + + if (wait) { + if (!wait_event_timeout(outp->lt.wait, + atomic_read(&outp->lt.done), + msecs_to_jiffies(2000))) + ret = -ETIMEDOUT; + } + + return ret; +} + +static void +nvkm_output_dp_enable(struct nvkm_output_dp *outp, bool present) +{ + struct nouveau_i2c_port *port = outp->base.edid; + if (present) { + if (!outp->present) { + nouveau_i2c(port)->acquire_pad(port, 0); + DBG("aux power -> always\n"); + outp->present = true; + } + nvkm_output_dp_train(&outp->base, 0, true); + } else { + if (outp->present) { + nouveau_i2c(port)->release_pad(port); + DBG("aux power -> demand\n"); + outp->present = false; + } + atomic_set(&outp->lt.done, 0); + } +} + +static void +nvkm_output_dp_detect(struct nvkm_output_dp *outp) +{ + struct nouveau_i2c_port *port = outp->base.edid; + int ret = nouveau_i2c(port)->acquire_pad(port, 0); + if (ret == 0) { + ret = nv_rdaux(outp->base.edid, DPCD_RC00_DPCD_REV, + outp->dpcd, sizeof(outp->dpcd)); + nvkm_output_dp_enable(outp, ret == 0); + nouveau_i2c(port)->release_pad(port); + } +} + +static void +nvkm_output_dp_service_work(struct work_struct *work) +{ + struct nvkm_output_dp *outp = container_of(work, typeof(*outp), work); + struct nouveau_disp *disp = nouveau_disp(outp); + int type = atomic_xchg(&outp->pending, 0); + u32 send = 0; + + if (type & (NVKM_I2C_PLUG | NVKM_I2C_UNPLUG)) { + nvkm_output_dp_detect(outp); + if (type & NVKM_I2C_UNPLUG) + send |= NVKM_HPD_UNPLUG; + if (type & NVKM_I2C_PLUG) + send |= NVKM_HPD_PLUG; + nouveau_event_get(outp->base.conn->hpd.event); + } + + if (type & NVKM_I2C_IRQ) { + nvkm_output_dp_train(&outp->base, 0, true); + send |= NVKM_HPD_IRQ; + } + + nouveau_event_trigger(disp->hpd, send, outp->base.info.connector); +} + +static int +nvkm_output_dp_service(void *data, u32 type, int index) +{ + struct nvkm_output_dp *outp = data; + DBG("HPD: %d\n", type); + atomic_or(type, &outp->pending); + schedule_work(&outp->work); + return NVKM_EVENT_DROP; +} + +int +_nvkm_output_dp_fini(struct nouveau_object *object, bool suspend) +{ + struct nvkm_output_dp *outp = (void *)object; + nouveau_event_put(outp->irq); + nvkm_output_dp_enable(outp, false); + return nvkm_output_fini(&outp->base, suspend); +} + +int +_nvkm_output_dp_init(struct nouveau_object *object) +{ + struct nvkm_output_dp *outp = (void *)object; + nvkm_output_dp_detect(outp); + return nvkm_output_init(&outp->base); +} + +void +_nvkm_output_dp_dtor(struct nouveau_object *object) +{ + struct nvkm_output_dp *outp = (void *)object; + nouveau_event_ref(NULL, &outp->irq); + nvkm_output_destroy(&outp->base); +} + +int +nvkm_output_dp_create_(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, + struct dcb_output *info, int index, + int length, void **pobject) +{ + struct nouveau_bios *bios = nouveau_bios(parent); + struct nouveau_i2c *i2c = nouveau_i2c(parent); + struct nvkm_output_dp *outp; + u8 hdr, cnt, len; + u32 data; + int ret; + + ret = nvkm_output_create_(parent, engine, oclass, info, index, + length, pobject); + outp = *pobject; + if (ret) + return ret; + + nouveau_event_ref(NULL, &outp->base.conn->hpd.event); + + /* access to the aux channel is not optional... */ + if (!outp->base.edid) { + ERR("aux channel not found\n"); + return -ENODEV; + } + + /* nor is the bios data for this output... */ + data = nvbios_dpout_match(bios, outp->base.info.hasht, + outp->base.info.hashm, &outp->version, + &hdr, &cnt, &len, &outp->info); + if (!data) { + ERR("no bios dp data\n"); + return -ENODEV; + } + + DBG("bios dp %02x %02x %02x %02x\n", outp->version, hdr, cnt, len); + + /* link training */ + INIT_WORK(&outp->lt.work, nouveau_dp_train); + init_waitqueue_head(&outp->lt.wait); + atomic_set(&outp->lt.done, 0); + + /* link maintenance */ + ret = nouveau_event_new(i2c->ntfy, NVKM_I2C_IRQ, outp->base.edid->index, + nvkm_output_dp_service, outp, &outp->irq); + if (ret) { + ERR("error monitoring aux irq event: %d\n", ret); + return ret; + } + + INIT_WORK(&outp->work, nvkm_output_dp_service_work); + + /* hotplug detect, replaces gpio-based mechanism with aux events */ + ret = nouveau_event_new(i2c->ntfy, NVKM_I2C_PLUG | NVKM_I2C_UNPLUG, + outp->base.edid->index, + nvkm_output_dp_service, outp, + &outp->base.conn->hpd.event); + if (ret) { + ERR("error monitoring aux hpd events: %d\n", ret); + return ret; + } + + return 0; +} + +int +_nvkm_output_dp_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *info, u32 index, + struct nouveau_object **pobject) +{ + struct nvkm_output_dp *outp; + int ret; + + ret = nvkm_output_dp_create(parent, engine, oclass, info, index, &outp); + *pobject = nv_object(outp); + if (ret) + return ret; + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.h b/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.h new file mode 100644 index 00000000000..ff33ba12cb6 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.h @@ -0,0 +1,65 @@ +#ifndef __NVKM_DISP_OUTP_DP_H__ +#define __NVKM_DISP_OUTP_DP_H__ + +#include <subdev/bios.h> +#include <subdev/bios/dp.h> + +#include "outp.h" + +struct nvkm_output_dp { + struct nvkm_output base; + + struct nvbios_dpout info; + u8 version; + + struct nouveau_eventh *irq; + struct nouveau_eventh *hpd; + struct work_struct work; + atomic_t pending; + bool present; + u8 dpcd[16]; + + struct { + struct work_struct work; + wait_queue_head_t wait; + atomic_t done; + } lt; +}; + +#define nvkm_output_dp_create(p,e,c,b,i,d) \ + nvkm_output_dp_create_((p), (e), (c), (b), (i), sizeof(**d), (void **)d) +#define nvkm_output_dp_destroy(d) ({ \ + struct nvkm_output_dp *_outp = (d); \ + _nvkm_output_dp_dtor(nv_object(_outp)); \ +}) +#define nvkm_output_dp_init(d) ({ \ + struct nvkm_output_dp *_outp = (d); \ + _nvkm_output_dp_init(nv_object(_outp)); \ +}) +#define nvkm_output_dp_fini(d,s) ({ \ + struct nvkm_output_dp *_outp = (d); \ + _nvkm_output_dp_fini(nv_object(_outp), (s)); \ +}) + +int nvkm_output_dp_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, struct dcb_output *, + int, int, void **); + +int _nvkm_output_dp_ctor(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, void *, u32, + struct nouveau_object **); +void _nvkm_output_dp_dtor(struct nouveau_object *); +int _nvkm_output_dp_init(struct nouveau_object *); +int _nvkm_output_dp_fini(struct nouveau_object *, bool); + +struct nvkm_output_dp_impl { + struct nvkm_output_impl base; + int (*pattern)(struct nvkm_output_dp *, int); + int (*lnk_pwr)(struct nvkm_output_dp *, int nr); + int (*lnk_ctl)(struct nvkm_output_dp *, int nr, int bw, bool ef); + int (*drv_ctl)(struct nvkm_output_dp *, int ln, int vs, int pe, int pc); +}; + +int nvkm_output_dp_train(struct nvkm_output *, u32 rate, bool wait); + +#endif diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c index 2c8ce351b52..fe0f256f11b 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c @@ -33,68 +33,107 @@ #include "nv50.h" /****************************************************************************** - * DisplayPort + * TMDS *****************************************************************************/ -static struct nouveau_i2c_port * -nv50_pior_dp_find(struct nouveau_disp *disp, struct dcb_output *outp) + +static int +nv50_pior_tmds_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *info, u32 index, + struct nouveau_object **pobject) { - struct nouveau_i2c *i2c = nouveau_i2c(disp); - return i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(outp->extdev)); + struct nouveau_i2c *i2c = nouveau_i2c(parent); + struct nvkm_output *outp; + int ret; + + ret = nvkm_output_create(parent, engine, oclass, info, index, &outp); + *pobject = nv_object(outp); + if (ret) + return ret; + + outp->edid = i2c->find_type(i2c, NV_I2C_TYPE_EXTDDC(outp->info.extdev)); + return 0; } +struct nvkm_output_impl +nv50_pior_tmds_impl = { + .base.handle = DCB_OUTPUT_TMDS | 0x0100, + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv50_pior_tmds_ctor, + .dtor = _nvkm_output_dtor, + .init = _nvkm_output_init, + .fini = _nvkm_output_fini, + }, +}; + +/****************************************************************************** + * DisplayPort + *****************************************************************************/ + static int -nv50_pior_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp, - int head, int pattern) +nv50_pior_dp_pattern(struct nvkm_output_dp *outp, int pattern) { - struct nouveau_i2c_port *port; - int ret = -EINVAL; - - port = nv50_pior_dp_find(disp, outp); - if (port) { - if (port->func->pattern) - ret = port->func->pattern(port, pattern); - else - ret = 0; - } - - return ret; + struct nouveau_i2c_port *port = outp->base.edid; + if (port && port->func->pattern) + return port->func->pattern(port, pattern); + return port ? 0 : -ENODEV; } static int -nv50_pior_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp, - int head, int lane_nr, int link_bw, bool enh) +nv50_pior_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr) { - struct nouveau_i2c_port *port; - int ret = -EINVAL; + return 0; +} - port = nv50_pior_dp_find(disp, outp); +static int +nv50_pior_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef) +{ + struct nouveau_i2c_port *port = outp->base.edid; if (port && port->func->lnk_ctl) - ret = port->func->lnk_ctl(port, lane_nr, link_bw, enh); + return port->func->lnk_ctl(port, nr, bw, ef); + return port ? 0 : -ENODEV; +} - return ret; +static int +nv50_pior_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc) +{ + struct nouveau_i2c_port *port = outp->base.edid; + if (port && port->func->drv_ctl) + return port->func->drv_ctl(port, ln, vs, pe); + return port ? 0 : -ENODEV; } static int -nv50_pior_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp, - int head, int lane, int vsw, int pre) +nv50_pior_dp_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *info, u32 index, + struct nouveau_object **pobject) { - struct nouveau_i2c_port *port; - int ret = -EINVAL; - - port = nv50_pior_dp_find(disp, outp); - if (port) { - if (port->func->drv_ctl) - ret = port->func->drv_ctl(port, lane, vsw, pre); - else - ret = 0; - } + struct nouveau_i2c *i2c = nouveau_i2c(parent); + struct nvkm_output_dp *outp; + int ret; - return ret; + ret = nvkm_output_dp_create(parent, engine, oclass, info, index, &outp); + *pobject = nv_object(outp); + if (ret) + return ret; + + outp->base.edid = i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX( + outp->base.info.extdev)); + return 0; } -const struct nouveau_dp_func -nv50_pior_dp_func = { +struct nvkm_output_dp_impl +nv50_pior_dp_impl = { + .base.base.handle = DCB_OUTPUT_DP | 0x0010, + .base.base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv50_pior_dp_ctor, + .dtor = _nvkm_output_dp_dtor, + .init = _nvkm_output_dp_init, + .fini = _nvkm_output_dp_fini, + }, .pattern = nv50_pior_dp_pattern, + .lnk_pwr = nv50_pior_dp_lnk_pwr, .lnk_ctl = nv50_pior_dp_lnk_ctl, .drv_ctl = nv50_pior_dp_drv_ctl, }; @@ -102,6 +141,7 @@ nv50_pior_dp_func = { /****************************************************************************** * General PIOR handling *****************************************************************************/ + int nv50_pior_power(struct nv50_disp_priv *priv, int or, u32 data) { diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/priv.h b/drivers/gpu/drm/nouveau/core/engine/disp/priv.h index cc3c7a4ca74..26e9a42569c 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/priv.h +++ b/drivers/gpu/drm/nouveau/core/engine/disp/priv.h @@ -1,10 +1,42 @@ #ifndef __NVKM_DISP_PRIV_H__ #define __NVKM_DISP_PRIV_H__ +#include <subdev/bios.h> +#include <subdev/bios/dcb.h> +#include <subdev/bios/conn.h> + #include <engine/disp.h> struct nouveau_disp_impl { struct nouveau_oclass base; + struct nouveau_oclass **outp; + struct nouveau_oclass **conn; }; +#define nouveau_disp_create(p,e,c,h,i,x,d) \ + nouveau_disp_create_((p), (e), (c), (h), (i), (x), \ + sizeof(**d), (void **)d) +#define nouveau_disp_destroy(d) ({ \ + struct nouveau_disp *disp = (d); \ + _nouveau_disp_dtor(nv_object(disp)); \ +}) +#define nouveau_disp_init(d) ({ \ + struct nouveau_disp *disp = (d); \ + _nouveau_disp_init(nv_object(disp)); \ +}) +#define nouveau_disp_fini(d,s) ({ \ + struct nouveau_disp *disp = (d); \ + _nouveau_disp_fini(nv_object(disp), (s)); \ +}) + +int nouveau_disp_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, int heads, + const char *, const char *, int, void **); +void _nouveau_disp_dtor(struct nouveau_object *); +int _nouveau_disp_init(struct nouveau_object *); +int _nouveau_disp_fini(struct nouveau_object *, bool); + +extern struct nouveau_oclass *nvkm_output_oclass; +extern struct nouveau_oclass *nvkm_connector_oclass; + #endif diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c index 526b7524289..7a1ebdfa9e1 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c @@ -47,8 +47,12 @@ int nv50_sor_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size) { struct nv50_disp_priv *priv = (void *)object->engine; + const u8 type = (mthd & NV50_DISP_SOR_MTHD_TYPE) >> 12; const u8 head = (mthd & NV50_DISP_SOR_MTHD_HEAD) >> 3; + const u8 link = (mthd & NV50_DISP_SOR_MTHD_LINK) >> 2; const u8 or = (mthd & NV50_DISP_SOR_MTHD_OR); + const u16 mask = (0x0100 << head) | (0x0040 << link) | (0x0001 << or); + struct nvkm_output *outp = NULL, *temp; u32 data; int ret = -EINVAL; @@ -56,6 +60,13 @@ nv50_sor_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size) return -EINVAL; data = *(u32 *)args; + list_for_each_entry(temp, &priv->base.outp, head) { + if ((temp->info.hasht & 0xff) == type && + (temp->info.hashm & mask) == mask) { + outp = temp; + break; + } + } switch (mthd & ~0x3f) { case NV50_DISP_SOR_PWR: @@ -71,6 +82,24 @@ nv50_sor_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size) priv->sor.lvdsconf = data & NV50_DISP_SOR_LVDS_SCRIPT_ID; ret = 0; break; + case NV94_DISP_SOR_DP_PWR: + if (outp) { + struct nvkm_output_dp *outpdp = (void *)outp; + switch (data) { + case NV94_DISP_SOR_DP_PWR_STATE_OFF: + nouveau_event_put(outpdp->irq); + ((struct nvkm_output_dp_impl *)nv_oclass(outp)) + ->lnk_pwr(outpdp, 0); + atomic_set(&outpdp->lt.done, 0); + break; + case NV94_DISP_SOR_DP_PWR_STATE_ON: + nvkm_output_dp_train(&outpdp->base, 0, true); + break; + default: + return -EINVAL; + } + } + break; default: BUG_ON(1); } diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c b/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c index eea3ef59693..05487cda84a 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c @@ -29,19 +29,21 @@ #include <subdev/bios/dcb.h> #include <subdev/bios/dp.h> #include <subdev/bios/init.h> +#include <subdev/timer.h> #include "nv50.h" +#include "outpdp.h" static inline u32 -nv94_sor_soff(struct dcb_output *outp) +nv94_sor_soff(struct nvkm_output_dp *outp) { - return (ffs(outp->or) - 1) * 0x800; + return (ffs(outp->base.info.or) - 1) * 0x800; } static inline u32 -nv94_sor_loff(struct dcb_output *outp) +nv94_sor_loff(struct nvkm_output_dp *outp) { - return nv94_sor_soff(outp) + !(outp->sorconf.link & 1) * 0x80; + return nv94_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80; } static inline u32 @@ -55,77 +57,96 @@ nv94_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane) } static int -nv94_sor_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp, - int head, int pattern) +nv94_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern) { - struct nv50_disp_priv *priv = (void *)disp; + struct nv50_disp_priv *priv = (void *)nouveau_disp(outp); const u32 loff = nv94_sor_loff(outp); nv_mask(priv, 0x61c10c + loff, 0x0f000000, pattern << 24); return 0; } +int +nv94_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr) +{ + struct nv50_disp_priv *priv = (void *)nouveau_disp(outp); + const u32 soff = nv94_sor_soff(outp); + const u32 loff = nv94_sor_loff(outp); + u32 mask = 0, i; + + for (i = 0; i < nr; i++) + mask |= 1 << (nv94_sor_dp_lane_map(priv, i) >> 3); + + nv_mask(priv, 0x61c130 + loff, 0x0000000f, mask); + nv_mask(priv, 0x61c034 + soff, 0x80000000, 0x80000000); + nv_wait(priv, 0x61c034 + soff, 0x80000000, 0x00000000); + return 0; +} + static int -nv94_sor_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp, - int head, int link_nr, int link_bw, bool enh_frame) +nv94_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef) { - struct nv50_disp_priv *priv = (void *)disp; + struct nv50_disp_priv *priv = (void *)nouveau_disp(outp); const u32 soff = nv94_sor_soff(outp); const u32 loff = nv94_sor_loff(outp); u32 dpctrl = 0x00000000; u32 clksor = 0x00000000; - u32 lane = 0; - int i; - dpctrl |= ((1 << link_nr) - 1) << 16; - if (enh_frame) + dpctrl |= ((1 << nr) - 1) << 16; + if (ef) dpctrl |= 0x00004000; - if (link_bw > 0x06) + if (bw > 0x06) clksor |= 0x00040000; - for (i = 0; i < link_nr; i++) - lane |= 1 << (nv94_sor_dp_lane_map(priv, i) >> 3); - nv_mask(priv, 0x614300 + soff, 0x000c0000, clksor); nv_mask(priv, 0x61c10c + loff, 0x001f4000, dpctrl); - nv_mask(priv, 0x61c130 + loff, 0x0000000f, lane); return 0; } static int -nv94_sor_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp, - int head, int lane, int swing, int preem) +nv94_sor_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc) { - struct nouveau_bios *bios = nouveau_bios(disp); - struct nv50_disp_priv *priv = (void *)disp; - const u32 shift = nv94_sor_dp_lane_map(priv, lane); + struct nv50_disp_priv *priv = (void *)nouveau_disp(outp); + struct nouveau_bios *bios = nouveau_bios(priv); + const u32 shift = nv94_sor_dp_lane_map(priv, ln); const u32 loff = nv94_sor_loff(outp); u32 addr, data[3]; u8 ver, hdr, cnt, len; struct nvbios_dpout info; struct nvbios_dpcfg ocfg; - addr = nvbios_dpout_match(bios, outp->hasht, outp->hashm, + addr = nvbios_dpout_match(bios, outp->base.info.hasht, + outp->base.info.hashm, &ver, &hdr, &cnt, &len, &info); if (!addr) return -ENODEV; - addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem, + addr = nvbios_dpcfg_match(bios, addr, 0, vs, pe, &ver, &hdr, &cnt, &len, &ocfg); if (!addr) return -EINVAL; data[0] = nv_rd32(priv, 0x61c118 + loff) & ~(0x000000ff << shift); data[1] = nv_rd32(priv, 0x61c120 + loff) & ~(0x000000ff << shift); - data[2] = nv_rd32(priv, 0x61c130 + loff) & ~(0x0000ff00); - nv_wr32(priv, 0x61c118 + loff, data[0] | (ocfg.drv << shift)); - nv_wr32(priv, 0x61c120 + loff, data[1] | (ocfg.pre << shift)); - nv_wr32(priv, 0x61c130 + loff, data[2] | (ocfg.unk << 8)); + data[2] = nv_rd32(priv, 0x61c130 + loff); + if ((data[2] & 0x0000ff00) < (ocfg.tx_pu << 8) || ln == 0) + data[2] = (data[2] & ~0x0000ff00) | (ocfg.tx_pu << 8); + nv_wr32(priv, 0x61c118 + loff, data[0] | (ocfg.dc << shift)); + nv_wr32(priv, 0x61c120 + loff, data[1] | (ocfg.pe << shift)); + nv_wr32(priv, 0x61c130 + loff, data[2] | (ocfg.tx_pu << 8)); return 0; } -const struct nouveau_dp_func -nv94_sor_dp_func = { +struct nvkm_output_dp_impl +nv94_sor_dp_impl = { + .base.base.handle = DCB_OUTPUT_DP, + .base.base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nvkm_output_dp_ctor, + .dtor = _nvkm_output_dp_dtor, + .init = _nvkm_output_dp_init, + .fini = _nvkm_output_dp_fini, + }, .pattern = nv94_sor_dp_pattern, + .lnk_pwr = nv94_sor_dp_lnk_pwr, .lnk_ctl = nv94_sor_dp_lnk_ctl, .drv_ctl = nv94_sor_dp_drv_ctl, }; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c index d2df572f16a..97f0e9cd3d4 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c @@ -29,19 +29,20 @@ #include <subdev/bios/dcb.h> #include <subdev/bios/dp.h> #include <subdev/bios/init.h> +#include <subdev/timer.h> #include "nv50.h" static inline u32 -nvd0_sor_soff(struct dcb_output *outp) +nvd0_sor_soff(struct nvkm_output_dp *outp) { - return (ffs(outp->or) - 1) * 0x800; + return (ffs(outp->base.info.or) - 1) * 0x800; } static inline u32 -nvd0_sor_loff(struct dcb_output *outp) +nvd0_sor_loff(struct nvkm_output_dp *outp) { - return nvd0_sor_soff(outp) + !(outp->sorconf.link & 1) * 0x80; + return nvd0_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80; } static inline u32 @@ -52,77 +53,80 @@ nvd0_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane) } static int -nvd0_sor_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp, - int head, int pattern) +nvd0_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern) { - struct nv50_disp_priv *priv = (void *)disp; + struct nv50_disp_priv *priv = (void *)nouveau_disp(outp); const u32 loff = nvd0_sor_loff(outp); nv_mask(priv, 0x61c110 + loff, 0x0f0f0f0f, 0x01010101 * pattern); return 0; } static int -nvd0_sor_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp, - int head, int link_nr, int link_bw, bool enh_frame) +nvd0_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef) { - struct nv50_disp_priv *priv = (void *)disp; + struct nv50_disp_priv *priv = (void *)nouveau_disp(outp); const u32 soff = nvd0_sor_soff(outp); const u32 loff = nvd0_sor_loff(outp); u32 dpctrl = 0x00000000; u32 clksor = 0x00000000; - u32 lane = 0; - int i; - clksor |= link_bw << 18; - dpctrl |= ((1 << link_nr) - 1) << 16; - if (enh_frame) + clksor |= bw << 18; + dpctrl |= ((1 << nr) - 1) << 16; + if (ef) dpctrl |= 0x00004000; - for (i = 0; i < link_nr; i++) - lane |= 1 << (nvd0_sor_dp_lane_map(priv, i) >> 3); - nv_mask(priv, 0x612300 + soff, 0x007c0000, clksor); nv_mask(priv, 0x61c10c + loff, 0x001f4000, dpctrl); - nv_mask(priv, 0x61c130 + loff, 0x0000000f, lane); return 0; } static int -nvd0_sor_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp, - int head, int lane, int swing, int preem) +nvd0_sor_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc) { - struct nouveau_bios *bios = nouveau_bios(disp); - struct nv50_disp_priv *priv = (void *)disp; - const u32 shift = nvd0_sor_dp_lane_map(priv, lane); + struct nv50_disp_priv *priv = (void *)nouveau_disp(outp); + struct nouveau_bios *bios = nouveau_bios(priv); + const u32 shift = nvd0_sor_dp_lane_map(priv, ln); const u32 loff = nvd0_sor_loff(outp); - u32 addr, data[3]; + u32 addr, data[4]; u8 ver, hdr, cnt, len; struct nvbios_dpout info; struct nvbios_dpcfg ocfg; - addr = nvbios_dpout_match(bios, outp->hasht, outp->hashm, + addr = nvbios_dpout_match(bios, outp->base.info.hasht, + outp->base.info.hashm, &ver, &hdr, &cnt, &len, &info); if (!addr) return -ENODEV; - addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem, + addr = nvbios_dpcfg_match(bios, addr, pc, vs, pe, &ver, &hdr, &cnt, &len, &ocfg); if (!addr) return -EINVAL; data[0] = nv_rd32(priv, 0x61c118 + loff) & ~(0x000000ff << shift); data[1] = nv_rd32(priv, 0x61c120 + loff) & ~(0x000000ff << shift); - data[2] = nv_rd32(priv, 0x61c130 + loff) & ~(0x0000ff00); - nv_wr32(priv, 0x61c118 + loff, data[0] | (ocfg.drv << shift)); - nv_wr32(priv, 0x61c120 + loff, data[1] | (ocfg.pre << shift)); - nv_wr32(priv, 0x61c130 + loff, data[2] | (ocfg.unk << 8)); - nv_mask(priv, 0x61c13c + loff, 0x00000000, 0x00000000); + data[2] = nv_rd32(priv, 0x61c130 + loff); + if ((data[2] & 0x0000ff00) < (ocfg.tx_pu << 8) || ln == 0) + data[2] = (data[2] & ~0x0000ff00) | (ocfg.tx_pu << 8); + nv_wr32(priv, 0x61c118 + loff, data[0] | (ocfg.dc << shift)); + nv_wr32(priv, 0x61c120 + loff, data[1] | (ocfg.pe << shift)); + nv_wr32(priv, 0x61c130 + loff, data[2] | (ocfg.tx_pu << 8)); + data[3] = nv_rd32(priv, 0x61c13c + loff) & ~(0x000000ff << shift); + nv_wr32(priv, 0x61c13c + loff, data[3] | (ocfg.pc << shift)); return 0; } -const struct nouveau_dp_func -nvd0_sor_dp_func = { +struct nvkm_output_dp_impl +nvd0_sor_dp_impl = { + .base.base.handle = DCB_OUTPUT_DP, + .base.base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nvkm_output_dp_ctor, + .dtor = _nvkm_output_dp_dtor, + .init = _nvkm_output_dp_init, + .fini = _nvkm_output_dp_fini, + }, .pattern = nvd0_sor_dp_pattern, + .lnk_pwr = nv94_sor_dp_lnk_pwr, .lnk_ctl = nvd0_sor_dp_lnk_ctl, .drv_ctl = nvd0_sor_dp_drv_ctl, }; diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/base.c b/drivers/gpu/drm/nouveau/core/engine/fifo/base.c index 6f9041ced9a..56ed3d73bf8 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/base.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/base.c @@ -91,7 +91,7 @@ nouveau_fifo_channel_create_(struct nouveau_object *parent, if (!chan->user) return -EFAULT; - nouveau_event_trigger(priv->cevent, 0); + nouveau_event_trigger(priv->cevent, 1, 0); chan->size = size; return 0; @@ -194,11 +194,11 @@ nouveau_fifo_create_(struct nouveau_object *parent, if (!priv->channel) return -ENOMEM; - ret = nouveau_event_create(1, &priv->cevent); + ret = nouveau_event_create(1, 1, &priv->cevent); if (ret) return ret; - ret = nouveau_event_create(1, &priv->uevent); + ret = nouveau_event_create(1, 1, &priv->uevent); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/gk20a.c b/drivers/gpu/drm/nouveau/core/engine/fifo/gk20a.c new file mode 100644 index 00000000000..327456eae96 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/gk20a.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "nve0.h" + +struct nouveau_oclass * +gk20a_fifo_oclass = &(struct nve0_fifo_impl) { + .base.handle = NV_ENGINE(FIFO, 0xea), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = nve0_fifo_ctor, + .dtor = nve0_fifo_dtor, + .init = nve0_fifo_init, + .fini = nve0_fifo_fini, + }, + .channels = 128, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c index 54f26cc801c..c61b16a6388 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c @@ -539,7 +539,7 @@ nv04_fifo_intr(struct nouveau_subdev *subdev) } if (status & 0x40000000) { - nouveau_event_trigger(priv->base.uevent, 0); + nouveau_event_trigger(priv->base.uevent, 1, 0); nv_wr32(priv, 0x002100, 0x40000000); status &= ~0x40000000; } diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c index fe0f41e65d9..6e5ac16e546 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c @@ -389,14 +389,14 @@ nv84_fifo_cclass = { ******************************************************************************/ static void -nv84_fifo_uevent_enable(struct nouveau_event *event, int index) +nv84_fifo_uevent_enable(struct nouveau_event *event, int type, int index) { struct nv84_fifo_priv *priv = event->priv; nv_mask(priv, 0x002140, 0x40000000, 0x40000000); } static void -nv84_fifo_uevent_disable(struct nouveau_event *event, int index) +nv84_fifo_uevent_disable(struct nouveau_event *event, int type, int index) { struct nv84_fifo_priv *priv = event->priv; nv_mask(priv, 0x002140, 0x40000000, 0x00000000); diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c index fa1e719872b..ae4a4dc5642 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c @@ -730,7 +730,7 @@ nvc0_fifo_intr_engine_unit(struct nvc0_fifo_priv *priv, int engn) for (unkn = 0; unkn < 8; unkn++) { u32 ints = (intr >> (unkn * 0x04)) & inte; if (ints & 0x1) { - nouveau_event_trigger(priv->base.uevent, 0); + nouveau_event_trigger(priv->base.uevent, 1, 0); ints &= ~1; } if (ints) { @@ -827,14 +827,14 @@ nvc0_fifo_intr(struct nouveau_subdev *subdev) } static void -nvc0_fifo_uevent_enable(struct nouveau_event *event, int index) +nvc0_fifo_uevent_enable(struct nouveau_event *event, int type, int index) { struct nvc0_fifo_priv *priv = event->priv; nv_mask(priv, 0x002140, 0x80000000, 0x80000000); } static void -nvc0_fifo_uevent_disable(struct nouveau_event *event, int index) +nvc0_fifo_uevent_disable(struct nouveau_event *event, int type, int index) { struct nvc0_fifo_priv *priv = event->priv; nv_mask(priv, 0x002140, 0x80000000, 0x00000000); diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c index a9a1a9c9f9f..298063edb92 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c @@ -859,7 +859,7 @@ nve0_fifo_intr_runlist(struct nve0_fifo_priv *priv) static void nve0_fifo_intr_engine(struct nve0_fifo_priv *priv) { - nouveau_event_trigger(priv->base.uevent, 0); + nouveau_event_trigger(priv->base.uevent, 1, 0); } static void @@ -952,14 +952,14 @@ nve0_fifo_intr(struct nouveau_subdev *subdev) } static void -nve0_fifo_uevent_enable(struct nouveau_event *event, int index) +nve0_fifo_uevent_enable(struct nouveau_event *event, int type, int index) { struct nve0_fifo_priv *priv = event->priv; nv_mask(priv, 0x002140, 0x80000000, 0x80000000); } static void -nve0_fifo_uevent_disable(struct nouveau_event *event, int index) +nve0_fifo_uevent_disable(struct nouveau_event *event, int type, int index) { struct nve0_fifo_priv *priv = event->priv; nv_mask(priv, 0x002140, 0x80000000, 0x00000000); diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.h b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.h index 014344ebee6..e96b32bb1bb 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.h +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.h @@ -8,6 +8,7 @@ int nve0_fifo_ctor(struct nouveau_object *, struct nouveau_object *, struct nouveau_object **); void nve0_fifo_dtor(struct nouveau_object *); int nve0_fifo_init(struct nouveau_object *); +int nve0_fifo_fini(struct nouveau_object *, bool); struct nve0_fifo_impl { struct nouveau_oclass base; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxgk20a.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxgk20a.c new file mode 100644 index 00000000000..224ee0287ab --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxgk20a.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "ctxnvc0.h" + +static const struct nvc0_graph_pack +gk20a_grctx_pack_mthd[] = { + { nve4_grctx_init_a097_0, 0xa297 }, + { nvc0_grctx_init_902d_0, 0x902d }, + {} +}; + +struct nouveau_oclass * +gk20a_grctx_oclass = &(struct nvc0_grctx_oclass) { + .base.handle = NV_ENGCTX(GR, 0xea), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvc0_graph_context_ctor, + .dtor = nvc0_graph_context_dtor, + .init = _nouveau_graph_context_init, + .fini = _nouveau_graph_context_fini, + .rd32 = _nouveau_graph_context_rd32, + .wr32 = _nouveau_graph_context_wr32, + }, + .main = nve4_grctx_generate_main, + .mods = nve4_grctx_generate_mods, + .unkn = nve4_grctx_generate_unkn, + .hub = nve4_grctx_pack_hub, + .gpc = nve4_grctx_pack_gpc, + .zcull = nvc0_grctx_pack_zcull, + .tpc = nve4_grctx_pack_tpc, + .ppc = nve4_grctx_pack_ppc, + .icmd = nve4_grctx_pack_icmd, + .mthd = gk20a_grctx_pack_mthd, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxgm107.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxgm107.c index 1dc37b1ddbf..b0d0fb2f4d0 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxgm107.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxgm107.c @@ -863,7 +863,7 @@ gm107_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) { mmio_data(0x003000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS); mmio_data(0x008000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS); - mmio_data(0x060000, 0x1000, NV_MEM_ACCESS_RW); + mmio_data(0x200000, 0x1000, NV_MEM_ACCESS_RW); mmio_list(0x40800c, 0x00000000, 8, 1); mmio_list(0x408010, 0x80000000, 0, 0); @@ -877,6 +877,8 @@ gm107_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) mmio_list(0x418e24, 0x00000000, 8, 0); mmio_list(0x418e28, 0x80000030, 0, 0); + mmio_list(0x4064c8, 0x018002c0, 0, 0); + mmio_list(0x418810, 0x80000000, 12, 2); mmio_list(0x419848, 0x10000000, 12, 2); mmio_list(0x419c2c, 0x10000000, 12, 2); diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv108.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv108.c index 48351b4d6d6..8de4a429154 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv108.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv108.c @@ -545,10 +545,12 @@ nv108_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) mmio_list(0x408010, 0x80000000, 0, 0); mmio_list(0x419004, 0x00000000, 8, 1); mmio_list(0x419008, 0x00000000, 0, 0); + mmio_list(0x4064cc, 0x80000000, 0, 0); mmio_list(0x408004, 0x00000000, 8, 0); mmio_list(0x408008, 0x80000030, 0, 0); mmio_list(0x418808, 0x00000000, 8, 0); mmio_list(0x41880c, 0x80000030, 0, 0); + mmio_list(0x4064c8, 0x00c20200, 0, 0); mmio_list(0x418810, 0x80000000, 12, 2); mmio_list(0x419848, 0x10000000, 12, 2); diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.h b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.h index 9c815d1f99e..8da8b627b9d 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.h @@ -69,7 +69,9 @@ extern struct nouveau_oclass *nvd7_grctx_oclass; extern struct nouveau_oclass *nvd9_grctx_oclass; extern struct nouveau_oclass *nve4_grctx_oclass; +extern struct nouveau_oclass *gk20a_grctx_oclass; void nve4_grctx_generate_main(struct nvc0_graph_priv *, struct nvc0_grctx *); +void nve4_grctx_generate_mods(struct nvc0_graph_priv *, struct nvc0_grctx *); void nve4_grctx_generate_unkn(struct nvc0_graph_priv *); void nve4_grctx_generate_r418bb8(struct nvc0_graph_priv *); @@ -151,6 +153,13 @@ extern const struct nvc0_graph_init nve4_grctx_init_gpm_0[]; extern const struct nvc0_graph_init nve4_grctx_init_pes_0[]; +extern const struct nvc0_graph_pack nve4_grctx_pack_hub[]; +extern const struct nvc0_graph_pack nve4_grctx_pack_gpc[]; +extern const struct nvc0_graph_pack nve4_grctx_pack_tpc[]; +extern const struct nvc0_graph_pack nve4_grctx_pack_ppc[]; +extern const struct nvc0_graph_pack nve4_grctx_pack_icmd[]; +extern const struct nvc0_graph_init nve4_grctx_init_a097_0[]; + extern const struct nvc0_graph_pack nvf0_grctx_pack_mthd[]; extern const struct nvc0_graph_init nvf0_grctx_init_pri_0[]; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c index 49a14b116a5..c5b24923858 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c @@ -272,13 +272,13 @@ nve4_grctx_init_icmd_0[] = { {} }; -static const struct nvc0_graph_pack +const struct nvc0_graph_pack nve4_grctx_pack_icmd[] = { { nve4_grctx_init_icmd_0 }, {} }; -static const struct nvc0_graph_init +const struct nvc0_graph_init nve4_grctx_init_a097_0[] = { { 0x000800, 8, 0x40, 0x00000000 }, { 0x000804, 8, 0x40, 0x00000000 }, @@ -697,7 +697,7 @@ nve4_grctx_init_be_0[] = { {} }; -static const struct nvc0_graph_pack +const struct nvc0_graph_pack nve4_grctx_pack_hub[] = { { nvc0_grctx_init_main_0 }, { nve4_grctx_init_fe_0 }, @@ -737,7 +737,7 @@ nve4_grctx_init_gpm_0[] = { {} }; -static const struct nvc0_graph_pack +const struct nvc0_graph_pack nve4_grctx_pack_gpc[] = { { nvc0_grctx_init_gpc_unk_0 }, { nvd9_grctx_init_prop_0 }, @@ -802,7 +802,7 @@ nve4_grctx_init_sm_0[] = { {} }; -static const struct nvc0_graph_pack +const struct nvc0_graph_pack nve4_grctx_pack_tpc[] = { { nvd7_grctx_init_pe_0 }, { nve4_grctx_init_tex_0 }, @@ -826,7 +826,7 @@ nve4_grctx_init_cbm_0[] = { {} }; -static const struct nvc0_graph_pack +const struct nvc0_graph_pack nve4_grctx_pack_ppc[] = { { nve4_grctx_init_pes_0 }, { nve4_grctx_init_cbm_0 }, @@ -838,7 +838,7 @@ nve4_grctx_pack_ppc[] = { * PGRAPH context implementation ******************************************************************************/ -static void +void nve4_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) { u32 magic[GPC_MAX][2]; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c index 0fab95e49f5..dec03f04114 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c @@ -842,7 +842,7 @@ nvf0_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) u16 magic3 = 0x0648; magic[gpc][0] = 0x10000000 | (magic0 << 16) | offset; magic[gpc][1] = 0x00000000 | (magic1 << 16); - offset += 0x0324 * (priv->tpc_nr[gpc] - 1);; + offset += 0x0324 * (priv->tpc_nr[gpc] - 1); magic[gpc][2] = 0x10000000 | (magic2 << 16) | offset; magic[gpc][3] = 0x00000000 | (magic3 << 16); offset += 0x0324; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc index 2f7345f7fe0..7445f12b1d9 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc @@ -54,7 +54,7 @@ mmio_list_base: #ifdef INCLUDE_CODE // reports an exception to the host // -// In: $r15 error code (see nvc0.fuc) +// In: $r15 error code (see os.h) // error: push $r14 diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hub.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hub.fuc index c8ddb8d71b9..b4ad18bf5a2 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hub.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hub.fuc @@ -49,7 +49,7 @@ hub_mmio_list_next: #ifdef INCLUDE_CODE // reports an exception to the host // -// In: $r15 error code (see nvc0.fuc) +// In: $r15 error code (see os.h) // error: nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_VAL(5), 0, $r15) @@ -343,13 +343,25 @@ ih: ih_no_ctxsw: and $r11 $r10 NV_PGRAPH_FECS_INTR_FWMTHD bra e #ih_no_fwmthd - // none we handle, ack, and fall-through to unhandled + // none we handle; report to host and ack + nv_rd32($r15, NV_PGRAPH_TRAPPED_DATA_LO) + nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_VAL(4), 0, $r15) + nv_rd32($r15, NV_PGRAPH_TRAPPED_ADDR) + nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_VAL(3), 0, $r15) + extr $r14 $r15 16:18 + shl b32 $r14 $r14 2 + imm32($r15, NV_PGRAPH_FE_OBJECT_TABLE(0)) + add b32 $r14 $r15 + call(nv_rd32) + nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_VAL(2), 0, $r15) + mov $r15 E_BAD_FWMTHD + call(error) mov $r11 0x100 nv_wr32(0x400144, $r11) // anything we didn't handle, bring it to the host's attention ih_no_fwmthd: - mov $r11 0x104 // FIFO | CHSW + mov $r11 0x504 // FIFO | CHSW | FWMTHD not b32 $r11 and $r11 $r10 $r11 bra e #ih_no_other diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubgm107.fuc5.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubgm107.fuc5.h index 214dd16ec56..5f953c5c20b 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubgm107.fuc5.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubgm107.fuc5.h @@ -478,10 +478,10 @@ uint32_t gm107_grhub_code[] = { 0x01040080, 0xbd0001f6, 0x01004104, - 0x627e020f, - 0x717e0006, + 0xa87e020f, + 0xb77e0006, 0x100f0006, - 0x0006b37e, + 0x0006f97e, 0x98000e98, 0x207e010f, 0x14950001, @@ -523,8 +523,8 @@ uint32_t gm107_grhub_code[] = { 0x800040b7, 0xf40132b6, 0x000fb41b, - 0x0006b37e, - 0x627e000f, + 0x0006f97e, + 0xa87e000f, 0x00800006, 0x01f60201, 0xbd04bd00, @@ -554,7 +554,7 @@ uint32_t gm107_grhub_code[] = { 0x0009f602, 0x32f404bd, 0x0231f401, - 0x0008367e, + 0x00087c7e, 0x99f094bd, 0x17008007, 0x0009f602, @@ -563,7 +563,7 @@ uint32_t gm107_grhub_code[] = { 0x37008006, 0x0009f602, 0x31f404bd, - 0x08367e01, + 0x087c7e01, 0xf094bd00, 0x00800699, 0x09f60217, @@ -572,7 +572,7 @@ uint32_t gm107_grhub_code[] = { 0x20f92f0e, 0x32f412b2, 0x0232f401, - 0x0008367e, + 0x00087c7e, 0x008020fc, 0x02f602c0, 0xf404bd00, @@ -580,7 +580,7 @@ uint32_t gm107_grhub_code[] = { 0x23c8130e, 0x0d0bf41f, 0xf40131f4, - 0x367e0232, + 0x7c7e0232, /* 0x054e: chsw_done */ 0x01020008, 0x02c30080, @@ -593,7 +593,7 @@ uint32_t gm107_grhub_code[] = { 0xb0ff2a0e, 0x1bf401e4, 0x7ef2b20c, - 0xf40007d6, + 0xf400081c, /* 0x057a: main_not_ctx_chan */ 0xe4b0400e, 0x2c1bf402, @@ -602,7 +602,7 @@ uint32_t gm107_grhub_code[] = { 0x0009f602, 0x32f404bd, 0x0232f401, - 0x0008367e, + 0x00087c7e, 0x99f094bd, 0x17008007, 0x0009f602, @@ -642,238 +642,238 @@ uint32_t gm107_grhub_code[] = { /* 0x061a: ih_no_ctxsw */ 0xabe40000, 0x0bf40400, - 0x01004b10, - 0x448ebfb2, - 0x8f7e4001, -/* 0x062e: ih_no_fwmthd */ - 0x044b0000, - 0xffb0bd01, - 0x0bf4b4ab, - 0x0700800c, - 0x000bf603, -/* 0x0642: ih_no_other */ - 0x004004bd, - 0x000af601, - 0xf0fc04bd, - 0xd0fce0fc, - 0xa0fcb0fc, - 0x80fc90fc, - 0xfc0088fe, - 0x0032f480, -/* 0x0662: ctx_4170s */ - 0xf5f001f8, - 0x8effb210, - 0x7e404170, - 0xf800008f, -/* 0x0671: ctx_4170w */ - 0x41708e00, + 0x07088e56, 0x00657e40, - 0xf0ffb200, - 0x1bf410f4, -/* 0x0683: ctx_redswitch */ - 0x4e00f8f3, - 0xe5f00200, - 0x20e5f040, - 0x8010e5f0, - 0xf6018500, - 0x04bd000e, -/* 0x069a: ctx_redswitch_delay */ - 0xf2b6080f, - 0xfd1bf401, - 0x0400e5f1, - 0x0100e5f1, - 0x01850080, - 0xbd000ef6, -/* 0x06b3: ctx_86c */ - 0x8000f804, - 0xf6022300, + 0x80ffb200, + 0xf6020400, 0x04bd000f, - 0x148effb2, - 0x8f7e408a, - 0xffb20000, - 0x41a88c8e, + 0x4007048e, + 0x0000657e, + 0x0080ffb2, + 0x0ff60203, + 0xc704bd00, + 0xee9450fe, + 0x07008f02, + 0x00efbb40, + 0x0000657e, + 0x02020080, + 0xbd000ff6, + 0x7e030f04, + 0x4b0002f8, + 0xbfb20100, + 0x4001448e, 0x00008f7e, -/* 0x06d2: ctx_mem */ - 0x008000f8, - 0x0ff60284, -/* 0x06db: ctx_mem_wait */ - 0x8f04bd00, - 0xcf028400, - 0xfffd00ff, - 0xf61bf405, -/* 0x06ea: ctx_load */ - 0x94bd00f8, - 0x800599f0, - 0xf6023700, - 0x04bd0009, - 0xb87e0c0a, - 0xf4bd0000, - 0x02890080, +/* 0x0674: ih_no_fwmthd */ + 0xbd05044b, + 0xb4abffb0, + 0x800c0bf4, + 0xf6030700, + 0x04bd000b, +/* 0x0688: ih_no_other */ + 0xf6010040, + 0x04bd000a, + 0xe0fcf0fc, + 0xb0fcd0fc, + 0x90fca0fc, + 0x88fe80fc, + 0xf480fc00, + 0x01f80032, +/* 0x06a8: ctx_4170s */ + 0xb210f5f0, + 0x41708eff, + 0x008f7e40, +/* 0x06b7: ctx_4170w */ + 0x8e00f800, + 0x7e404170, + 0xb2000065, + 0x10f4f0ff, + 0xf8f31bf4, +/* 0x06c9: ctx_redswitch */ + 0x02004e00, + 0xf040e5f0, + 0xe5f020e5, + 0x85008010, + 0x000ef601, + 0x080f04bd, +/* 0x06e0: ctx_redswitch_delay */ + 0xf401f2b6, + 0xe5f1fd1b, + 0xe5f10400, + 0x00800100, + 0x0ef60185, + 0xf804bd00, +/* 0x06f9: ctx_86c */ + 0x23008000, + 0x000ff602, + 0xffb204bd, + 0x408a148e, + 0x00008f7e, + 0x8c8effb2, + 0x8f7e41a8, + 0x00f80000, +/* 0x0718: ctx_mem */ + 0x02840080, 0xbd000ff6, - 0xc1008004, - 0x0002f602, - 0x008004bd, - 0x02f60283, - 0x0f04bd00, - 0x06d27e07, - 0xc0008000, - 0x0002f602, - 0x0bfe04bd, - 0x1f2af000, - 0xb60424b6, - 0x94bd0220, - 0x800899f0, - 0xf6023700, - 0x04bd0009, - 0x02810080, - 0xbd0002f6, - 0x0000d204, - 0x25f08000, - 0x88008002, - 0x0002f602, - 0x100104bd, - 0xf0020042, - 0x12fa0223, - 0xbd03f805, - 0x0899f094, - 0x02170080, - 0xbd0009f6, - 0x81019804, - 0x981814b6, - 0x25b68002, - 0x0512fd08, - 0xbd1601b5, - 0x0999f094, - 0x02370080, - 0xbd0009f6, - 0x81008004, - 0x0001f602, - 0x010204bd, - 0x02880080, +/* 0x0721: ctx_mem_wait */ + 0x84008f04, + 0x00ffcf02, + 0xf405fffd, + 0x00f8f61b, +/* 0x0730: ctx_load */ + 0x99f094bd, + 0x37008005, + 0x0009f602, + 0x0c0a04bd, + 0x0000b87e, + 0x0080f4bd, + 0x0ff60289, + 0x8004bd00, + 0xf602c100, + 0x04bd0002, + 0x02830080, 0xbd0002f6, - 0x01004104, - 0xfa0613f0, - 0x03f80501, + 0x7e070f04, + 0x80000718, + 0xf602c000, + 0x04bd0002, + 0xf0000bfe, + 0x24b61f2a, + 0x0220b604, 0x99f094bd, - 0x17008009, + 0x37008008, 0x0009f602, - 0x94bd04bd, - 0x800599f0, + 0x008004bd, + 0x02f60281, + 0xd204bd00, + 0x80000000, + 0x800225f0, + 0xf6028800, + 0x04bd0002, + 0x00421001, + 0x0223f002, + 0xf80512fa, + 0xf094bd03, + 0x00800899, + 0x09f60217, + 0x9804bd00, + 0x14b68101, + 0x80029818, + 0xfd0825b6, + 0x01b50512, + 0xf094bd16, + 0x00800999, + 0x09f60237, + 0x8004bd00, + 0xf6028100, + 0x04bd0001, + 0x00800102, + 0x02f60288, + 0x4104bd00, + 0x13f00100, + 0x0501fa06, + 0x94bd03f8, + 0x800999f0, 0xf6021700, 0x04bd0009, -/* 0x07d6: ctx_chan */ - 0xea7e00f8, - 0x0c0a0006, - 0x0000b87e, - 0xd27e050f, - 0x00f80006, -/* 0x07e8: ctx_mmio_exec */ - 0x80410398, + 0x99f094bd, + 0x17008005, + 0x0009f602, + 0x00f804bd, +/* 0x081c: ctx_chan */ + 0x0007307e, + 0xb87e0c0a, + 0x050f0000, + 0x0007187e, +/* 0x082e: ctx_mmio_exec */ + 0x039800f8, + 0x81008041, + 0x0003f602, + 0x34bd04bd, +/* 0x083c: ctx_mmio_loop */ + 0xf4ff34c4, + 0x00450e1b, + 0x0653f002, + 0xf80535fa, +/* 0x084d: ctx_mmio_pull */ + 0x804e9803, + 0x7e814f98, + 0xb600008f, + 0x12b60830, + 0xdf1bf401, +/* 0x0860: ctx_mmio_done */ + 0x80160398, 0xf6028100, 0x04bd0003, -/* 0x07f6: ctx_mmio_loop */ - 0x34c434bd, - 0x0e1bf4ff, - 0xf0020045, - 0x35fa0653, -/* 0x0807: ctx_mmio_pull */ - 0x9803f805, - 0x4f98804e, - 0x008f7e81, - 0x0830b600, - 0xf40112b6, -/* 0x081a: ctx_mmio_done */ - 0x0398df1b, - 0x81008016, - 0x0003f602, - 0x00b504bd, - 0x01004140, - 0xfa0613f0, - 0x03f80601, -/* 0x0836: ctx_xfer */ - 0x040e00f8, - 0x03020080, - 0xbd000ef6, -/* 0x0841: ctx_xfer_idle */ - 0x00008e04, - 0x00eecf03, - 0x2000e4f1, - 0xf4f51bf4, - 0x02f40611, -/* 0x0855: ctx_xfer_pre */ - 0x7e100f0c, - 0xf40006b3, -/* 0x085e: ctx_xfer_pre_load */ - 0x020f1b11, - 0x0006627e, - 0x0006717e, - 0x0006837e, - 0x627ef4bd, - 0xea7e0006, -/* 0x0876: ctx_xfer_exec */ - 0x01980006, - 0x8024bd16, - 0xf6010500, - 0x04bd0002, - 0x008e1fb2, - 0x8f7e41a5, - 0xfcf00000, - 0x022cf001, - 0xfd0124b6, - 0xffb205f2, - 0x41a5048e, + 0x414000b5, + 0x13f00100, + 0x0601fa06, + 0x00f803f8, +/* 0x087c: ctx_xfer */ + 0x0080040e, + 0x0ef60302, +/* 0x0887: ctx_xfer_idle */ + 0x8e04bd00, + 0xcf030000, + 0xe4f100ee, + 0x1bf42000, + 0x0611f4f5, +/* 0x089b: ctx_xfer_pre */ + 0x0f0c02f4, + 0x06f97e10, + 0x1b11f400, +/* 0x08a4: ctx_xfer_pre_load */ + 0xa87e020f, + 0xb77e0006, + 0xc97e0006, + 0xf4bd0006, + 0x0006a87e, + 0x0007307e, +/* 0x08bc: ctx_xfer_exec */ + 0xbd160198, + 0x05008024, + 0x0002f601, + 0x1fb204bd, + 0x41a5008e, 0x00008f7e, - 0x0002167e, - 0xfc8024bd, - 0x02f60247, - 0xf004bd00, - 0x20b6012c, - 0x4afc8003, - 0x0002f602, - 0xacf004bd, - 0x06a5f001, - 0x0c98000b, - 0x010d9800, - 0x3d7e000e, - 0x080a0001, - 0x0000ec7e, - 0x00020a7e, - 0x0a1201f4, - 0x00b87e0c, - 0x7e050f00, - 0xf40006d2, -/* 0x08f2: ctx_xfer_post */ - 0x020f2d02, - 0x0006627e, - 0xb37ef4bd, - 0x277e0006, - 0x717e0002, + 0xf001fcf0, + 0x24b6022c, + 0x05f2fd01, + 0x048effb2, + 0x8f7e41a5, + 0x167e0000, + 0x24bd0002, + 0x0247fc80, + 0xbd0002f6, + 0x012cf004, + 0x800320b6, + 0xf6024afc, + 0x04bd0002, + 0xf001acf0, + 0x000b06a5, + 0x98000c98, + 0x000e010d, + 0x00013d7e, + 0xec7e080a, + 0x0a7e0000, + 0x01f40002, + 0x7e0c0a12, + 0x0f0000b8, + 0x07187e05, + 0x2d02f400, +/* 0x0938: ctx_xfer_post */ + 0xa87e020f, 0xf4bd0006, - 0x0006627e, - 0x981011f4, - 0x11fd4001, - 0x070bf405, - 0x0007e87e, -/* 0x091c: ctx_xfer_no_post_mmio */ -/* 0x091c: ctx_xfer_done */ - 0x000000f8, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, + 0x0006f97e, + 0x0002277e, + 0x0006b77e, + 0xa87ef4bd, + 0x11f40006, + 0x40019810, + 0xf40511fd, + 0x2e7e070b, +/* 0x0962: ctx_xfer_no_post_mmio */ +/* 0x0962: ctx_xfer_done */ + 0x00f80008, 0x00000000, 0x00000000, 0x00000000, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnv108.fuc5.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnv108.fuc5.h index 64dfd75192b..e49b5a877ae 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnv108.fuc5.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnv108.fuc5.h @@ -478,10 +478,10 @@ uint32_t nv108_grhub_code[] = { 0x01040080, 0xbd0001f6, 0x01004104, - 0x627e020f, - 0x717e0006, + 0xa87e020f, + 0xb77e0006, 0x100f0006, - 0x0006b37e, + 0x0006f97e, 0x98000e98, 0x207e010f, 0x14950001, @@ -523,8 +523,8 @@ uint32_t nv108_grhub_code[] = { 0x800040b7, 0xf40132b6, 0x000fb41b, - 0x0006b37e, - 0x627e000f, + 0x0006f97e, + 0xa87e000f, 0x00800006, 0x01f60201, 0xbd04bd00, @@ -554,7 +554,7 @@ uint32_t nv108_grhub_code[] = { 0x0009f602, 0x32f404bd, 0x0231f401, - 0x0008367e, + 0x00087c7e, 0x99f094bd, 0x17008007, 0x0009f602, @@ -563,7 +563,7 @@ uint32_t nv108_grhub_code[] = { 0x37008006, 0x0009f602, 0x31f404bd, - 0x08367e01, + 0x087c7e01, 0xf094bd00, 0x00800699, 0x09f60217, @@ -572,7 +572,7 @@ uint32_t nv108_grhub_code[] = { 0x20f92f0e, 0x32f412b2, 0x0232f401, - 0x0008367e, + 0x00087c7e, 0x008020fc, 0x02f602c0, 0xf404bd00, @@ -580,7 +580,7 @@ uint32_t nv108_grhub_code[] = { 0x23c8130e, 0x0d0bf41f, 0xf40131f4, - 0x367e0232, + 0x7c7e0232, /* 0x054e: chsw_done */ 0x01020008, 0x02c30080, @@ -593,7 +593,7 @@ uint32_t nv108_grhub_code[] = { 0xb0ff2a0e, 0x1bf401e4, 0x7ef2b20c, - 0xf40007d6, + 0xf400081c, /* 0x057a: main_not_ctx_chan */ 0xe4b0400e, 0x2c1bf402, @@ -602,7 +602,7 @@ uint32_t nv108_grhub_code[] = { 0x0009f602, 0x32f404bd, 0x0232f401, - 0x0008367e, + 0x00087c7e, 0x99f094bd, 0x17008007, 0x0009f602, @@ -642,238 +642,238 @@ uint32_t nv108_grhub_code[] = { /* 0x061a: ih_no_ctxsw */ 0xabe40000, 0x0bf40400, - 0x01004b10, - 0x448ebfb2, - 0x8f7e4001, -/* 0x062e: ih_no_fwmthd */ - 0x044b0000, - 0xffb0bd01, - 0x0bf4b4ab, - 0x0700800c, - 0x000bf603, -/* 0x0642: ih_no_other */ - 0x004004bd, - 0x000af601, - 0xf0fc04bd, - 0xd0fce0fc, - 0xa0fcb0fc, - 0x80fc90fc, - 0xfc0088fe, - 0x0032f480, -/* 0x0662: ctx_4170s */ - 0xf5f001f8, - 0x8effb210, - 0x7e404170, - 0xf800008f, -/* 0x0671: ctx_4170w */ - 0x41708e00, + 0x07088e56, 0x00657e40, - 0xf0ffb200, - 0x1bf410f4, -/* 0x0683: ctx_redswitch */ - 0x4e00f8f3, - 0xe5f00200, - 0x20e5f040, - 0x8010e5f0, - 0xf6018500, - 0x04bd000e, -/* 0x069a: ctx_redswitch_delay */ - 0xf2b6080f, - 0xfd1bf401, - 0x0400e5f1, - 0x0100e5f1, - 0x01850080, - 0xbd000ef6, -/* 0x06b3: ctx_86c */ - 0x8000f804, - 0xf6022300, + 0x80ffb200, + 0xf6020400, 0x04bd000f, - 0x148effb2, - 0x8f7e408a, - 0xffb20000, - 0x41a88c8e, + 0x4007048e, + 0x0000657e, + 0x0080ffb2, + 0x0ff60203, + 0xc704bd00, + 0xee9450fe, + 0x07008f02, + 0x00efbb40, + 0x0000657e, + 0x02020080, + 0xbd000ff6, + 0x7e030f04, + 0x4b0002f8, + 0xbfb20100, + 0x4001448e, 0x00008f7e, -/* 0x06d2: ctx_mem */ - 0x008000f8, - 0x0ff60284, -/* 0x06db: ctx_mem_wait */ - 0x8f04bd00, - 0xcf028400, - 0xfffd00ff, - 0xf61bf405, -/* 0x06ea: ctx_load */ - 0x94bd00f8, - 0x800599f0, - 0xf6023700, - 0x04bd0009, - 0xb87e0c0a, - 0xf4bd0000, - 0x02890080, +/* 0x0674: ih_no_fwmthd */ + 0xbd05044b, + 0xb4abffb0, + 0x800c0bf4, + 0xf6030700, + 0x04bd000b, +/* 0x0688: ih_no_other */ + 0xf6010040, + 0x04bd000a, + 0xe0fcf0fc, + 0xb0fcd0fc, + 0x90fca0fc, + 0x88fe80fc, + 0xf480fc00, + 0x01f80032, +/* 0x06a8: ctx_4170s */ + 0xb210f5f0, + 0x41708eff, + 0x008f7e40, +/* 0x06b7: ctx_4170w */ + 0x8e00f800, + 0x7e404170, + 0xb2000065, + 0x10f4f0ff, + 0xf8f31bf4, +/* 0x06c9: ctx_redswitch */ + 0x02004e00, + 0xf040e5f0, + 0xe5f020e5, + 0x85008010, + 0x000ef601, + 0x080f04bd, +/* 0x06e0: ctx_redswitch_delay */ + 0xf401f2b6, + 0xe5f1fd1b, + 0xe5f10400, + 0x00800100, + 0x0ef60185, + 0xf804bd00, +/* 0x06f9: ctx_86c */ + 0x23008000, + 0x000ff602, + 0xffb204bd, + 0x408a148e, + 0x00008f7e, + 0x8c8effb2, + 0x8f7e41a8, + 0x00f80000, +/* 0x0718: ctx_mem */ + 0x02840080, 0xbd000ff6, - 0xc1008004, - 0x0002f602, - 0x008004bd, - 0x02f60283, - 0x0f04bd00, - 0x06d27e07, - 0xc0008000, - 0x0002f602, - 0x0bfe04bd, - 0x1f2af000, - 0xb60424b6, - 0x94bd0220, - 0x800899f0, - 0xf6023700, - 0x04bd0009, - 0x02810080, - 0xbd0002f6, - 0x0000d204, - 0x25f08000, - 0x88008002, - 0x0002f602, - 0x100104bd, - 0xf0020042, - 0x12fa0223, - 0xbd03f805, - 0x0899f094, - 0x02170080, - 0xbd0009f6, - 0x81019804, - 0x981814b6, - 0x25b68002, - 0x0512fd08, - 0xbd1601b5, - 0x0999f094, - 0x02370080, - 0xbd0009f6, - 0x81008004, - 0x0001f602, - 0x010204bd, - 0x02880080, +/* 0x0721: ctx_mem_wait */ + 0x84008f04, + 0x00ffcf02, + 0xf405fffd, + 0x00f8f61b, +/* 0x0730: ctx_load */ + 0x99f094bd, + 0x37008005, + 0x0009f602, + 0x0c0a04bd, + 0x0000b87e, + 0x0080f4bd, + 0x0ff60289, + 0x8004bd00, + 0xf602c100, + 0x04bd0002, + 0x02830080, 0xbd0002f6, - 0x01004104, - 0xfa0613f0, - 0x03f80501, + 0x7e070f04, + 0x80000718, + 0xf602c000, + 0x04bd0002, + 0xf0000bfe, + 0x24b61f2a, + 0x0220b604, 0x99f094bd, - 0x17008009, + 0x37008008, 0x0009f602, - 0x94bd04bd, - 0x800599f0, + 0x008004bd, + 0x02f60281, + 0xd204bd00, + 0x80000000, + 0x800225f0, + 0xf6028800, + 0x04bd0002, + 0x00421001, + 0x0223f002, + 0xf80512fa, + 0xf094bd03, + 0x00800899, + 0x09f60217, + 0x9804bd00, + 0x14b68101, + 0x80029818, + 0xfd0825b6, + 0x01b50512, + 0xf094bd16, + 0x00800999, + 0x09f60237, + 0x8004bd00, + 0xf6028100, + 0x04bd0001, + 0x00800102, + 0x02f60288, + 0x4104bd00, + 0x13f00100, + 0x0501fa06, + 0x94bd03f8, + 0x800999f0, 0xf6021700, 0x04bd0009, -/* 0x07d6: ctx_chan */ - 0xea7e00f8, - 0x0c0a0006, - 0x0000b87e, - 0xd27e050f, - 0x00f80006, -/* 0x07e8: ctx_mmio_exec */ - 0x80410398, + 0x99f094bd, + 0x17008005, + 0x0009f602, + 0x00f804bd, +/* 0x081c: ctx_chan */ + 0x0007307e, + 0xb87e0c0a, + 0x050f0000, + 0x0007187e, +/* 0x082e: ctx_mmio_exec */ + 0x039800f8, + 0x81008041, + 0x0003f602, + 0x34bd04bd, +/* 0x083c: ctx_mmio_loop */ + 0xf4ff34c4, + 0x00450e1b, + 0x0653f002, + 0xf80535fa, +/* 0x084d: ctx_mmio_pull */ + 0x804e9803, + 0x7e814f98, + 0xb600008f, + 0x12b60830, + 0xdf1bf401, +/* 0x0860: ctx_mmio_done */ + 0x80160398, 0xf6028100, 0x04bd0003, -/* 0x07f6: ctx_mmio_loop */ - 0x34c434bd, - 0x0e1bf4ff, - 0xf0020045, - 0x35fa0653, -/* 0x0807: ctx_mmio_pull */ - 0x9803f805, - 0x4f98804e, - 0x008f7e81, - 0x0830b600, - 0xf40112b6, -/* 0x081a: ctx_mmio_done */ - 0x0398df1b, - 0x81008016, - 0x0003f602, - 0x00b504bd, - 0x01004140, - 0xfa0613f0, - 0x03f80601, -/* 0x0836: ctx_xfer */ - 0x040e00f8, - 0x03020080, - 0xbd000ef6, -/* 0x0841: ctx_xfer_idle */ - 0x00008e04, - 0x00eecf03, - 0x2000e4f1, - 0xf4f51bf4, - 0x02f40611, -/* 0x0855: ctx_xfer_pre */ - 0x7e100f0c, - 0xf40006b3, -/* 0x085e: ctx_xfer_pre_load */ - 0x020f1b11, - 0x0006627e, - 0x0006717e, - 0x0006837e, - 0x627ef4bd, - 0xea7e0006, -/* 0x0876: ctx_xfer_exec */ - 0x01980006, - 0x8024bd16, - 0xf6010500, - 0x04bd0002, - 0x008e1fb2, - 0x8f7e41a5, - 0xfcf00000, - 0x022cf001, - 0xfd0124b6, - 0xffb205f2, - 0x41a5048e, + 0x414000b5, + 0x13f00100, + 0x0601fa06, + 0x00f803f8, +/* 0x087c: ctx_xfer */ + 0x0080040e, + 0x0ef60302, +/* 0x0887: ctx_xfer_idle */ + 0x8e04bd00, + 0xcf030000, + 0xe4f100ee, + 0x1bf42000, + 0x0611f4f5, +/* 0x089b: ctx_xfer_pre */ + 0x0f0c02f4, + 0x06f97e10, + 0x1b11f400, +/* 0x08a4: ctx_xfer_pre_load */ + 0xa87e020f, + 0xb77e0006, + 0xc97e0006, + 0xf4bd0006, + 0x0006a87e, + 0x0007307e, +/* 0x08bc: ctx_xfer_exec */ + 0xbd160198, + 0x05008024, + 0x0002f601, + 0x1fb204bd, + 0x41a5008e, 0x00008f7e, - 0x0002167e, - 0xfc8024bd, - 0x02f60247, - 0xf004bd00, - 0x20b6012c, - 0x4afc8003, - 0x0002f602, - 0xacf004bd, - 0x06a5f001, - 0x0c98000b, - 0x010d9800, - 0x3d7e000e, - 0x080a0001, - 0x0000ec7e, - 0x00020a7e, - 0x0a1201f4, - 0x00b87e0c, - 0x7e050f00, - 0xf40006d2, -/* 0x08f2: ctx_xfer_post */ - 0x020f2d02, - 0x0006627e, - 0xb37ef4bd, - 0x277e0006, - 0x717e0002, + 0xf001fcf0, + 0x24b6022c, + 0x05f2fd01, + 0x048effb2, + 0x8f7e41a5, + 0x167e0000, + 0x24bd0002, + 0x0247fc80, + 0xbd0002f6, + 0x012cf004, + 0x800320b6, + 0xf6024afc, + 0x04bd0002, + 0xf001acf0, + 0x000b06a5, + 0x98000c98, + 0x000e010d, + 0x00013d7e, + 0xec7e080a, + 0x0a7e0000, + 0x01f40002, + 0x7e0c0a12, + 0x0f0000b8, + 0x07187e05, + 0x2d02f400, +/* 0x0938: ctx_xfer_post */ + 0xa87e020f, 0xf4bd0006, - 0x0006627e, - 0x981011f4, - 0x11fd4001, - 0x070bf405, - 0x0007e87e, -/* 0x091c: ctx_xfer_no_post_mmio */ -/* 0x091c: ctx_xfer_done */ - 0x000000f8, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, + 0x0006f97e, + 0x0002277e, + 0x0006b77e, + 0xa87ef4bd, + 0x11f40006, + 0x40019810, + 0xf40511fd, + 0x2e7e070b, +/* 0x0962: ctx_xfer_no_post_mmio */ +/* 0x0962: ctx_xfer_done */ + 0x00f80008, 0x00000000, 0x00000000, 0x00000000, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h index f8f7b278a13..92dfe6a4ac8 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h @@ -528,10 +528,10 @@ uint32_t nvc0_grhub_code[] = { 0x0001d001, 0x17f104bd, 0xf7f00100, - 0xb521f502, - 0xc721f507, - 0x10f7f007, - 0x081421f5, + 0x0d21f502, + 0x1f21f508, + 0x10f7f008, + 0x086c21f5, 0x98000e98, 0x21f5010f, 0x14950150, @@ -574,9 +574,9 @@ uint32_t nvc0_grhub_code[] = { 0xb6800040, 0x1bf40132, 0x00f7f0be, - 0x081421f5, + 0x086c21f5, 0xf500f7f0, - 0xf107b521, + 0xf1080d21, 0xf0010007, 0x01d00203, 0xbd04bd00, @@ -610,8 +610,8 @@ uint32_t nvc0_grhub_code[] = { 0x09d00203, 0xf404bd00, 0x31f40132, - 0xe821f502, - 0xf094bd09, + 0x4021f502, + 0xf094bd0a, 0x07f10799, 0x03f01700, 0x0009d002, @@ -621,7 +621,7 @@ uint32_t nvc0_grhub_code[] = { 0x0203f00f, 0xbd0009d0, 0x0131f404, - 0x09e821f5, + 0x0a4021f5, 0x99f094bd, 0x0007f106, 0x0203f017, @@ -631,7 +631,7 @@ uint32_t nvc0_grhub_code[] = { 0x12b920f9, 0x0132f402, 0xf50232f4, - 0xfc09e821, + 0xfc0a4021, 0x0007f120, 0x0203f0c0, 0xbd0002d0, @@ -640,7 +640,7 @@ uint32_t nvc0_grhub_code[] = { 0xf41f23c8, 0x31f40d0b, 0x0232f401, - 0x09e821f5, + 0x0a4021f5, /* 0x063c: chsw_done */ 0xf10127f0, 0xf0c30007, @@ -654,7 +654,7 @@ uint32_t nvc0_grhub_code[] = { /* 0x0660: main_not_ctx_switch */ 0xf401e4b0, 0xf2b90d1b, - 0x7821f502, + 0xd021f502, 0x460ef409, /* 0x0670: main_not_ctx_chan */ 0xf402e4b0, @@ -664,8 +664,8 @@ uint32_t nvc0_grhub_code[] = { 0x09d00203, 0xf404bd00, 0x32f40132, - 0xe821f502, - 0xf094bd09, + 0x4021f502, + 0xf094bd0a, 0x07f10799, 0x03f01700, 0x0009d002, @@ -710,18 +710,40 @@ uint32_t nvc0_grhub_code[] = { /* 0x072b: ih_no_ctxsw */ 0xe40421f4, 0xf40400ab, - 0xb7f1140b, + 0xe7f16c0b, + 0xe3f00708, + 0x6821f440, + 0xf102ffb9, + 0xf0040007, + 0x0fd00203, + 0xf104bd00, + 0xf00704e7, + 0x21f440e3, + 0x02ffb968, + 0x030007f1, + 0xd00203f0, + 0x04bd000f, + 0x9450fec7, + 0xf7f102ee, + 0xf3f00700, + 0x00efbb40, + 0xf16821f4, + 0xf0020007, + 0x0fd00203, + 0xf004bd00, + 0x21f503f7, + 0xb7f1037e, 0xbfb90100, 0x44e7f102, 0x40e3f001, -/* 0x0743: ih_no_fwmthd */ +/* 0x079b: ih_no_fwmthd */ 0xf19d21f4, - 0xbd0104b7, + 0xbd0504b7, 0xb4abffb0, 0xf10f0bf4, 0xf0070007, 0x0bd00303, -/* 0x075b: ih_no_other */ +/* 0x07b3: ih_no_other */ 0xf104bd00, 0xf0010007, 0x0ad00003, @@ -731,36 +753,36 @@ uint32_t nvc0_grhub_code[] = { 0xfc90fca0, 0x0088fe80, 0x32f480fc, -/* 0x077f: ctx_4160s */ +/* 0x07d7: ctx_4160s */ 0xf001f800, 0xffb901f7, 0x60e7f102, 0x40e3f041, -/* 0x078f: ctx_4160s_wait */ +/* 0x07e7: ctx_4160s_wait */ 0xf19d21f4, 0xf04160e7, 0x21f440e3, 0x02ffb968, 0xf404ffc8, 0x00f8f00b, -/* 0x07a4: ctx_4160c */ +/* 0x07fc: ctx_4160c */ 0xffb9f4bd, 0x60e7f102, 0x40e3f041, 0xf89d21f4, -/* 0x07b5: ctx_4170s */ +/* 0x080d: ctx_4170s */ 0x10f5f000, 0xf102ffb9, 0xf04170e7, 0x21f440e3, -/* 0x07c7: ctx_4170w */ +/* 0x081f: ctx_4170w */ 0xf100f89d, 0xf04170e7, 0x21f440e3, 0x02ffb968, 0xf410f4f0, 0x00f8f01b, -/* 0x07dc: ctx_redswitch */ +/* 0x0834: ctx_redswitch */ 0x0200e7f1, 0xf040e5f0, 0xe5f020e5, @@ -768,7 +790,7 @@ uint32_t nvc0_grhub_code[] = { 0x0103f085, 0xbd000ed0, 0x08f7f004, -/* 0x07f8: ctx_redswitch_delay */ +/* 0x0850: ctx_redswitch_delay */ 0xf401f2b6, 0xe5f1fd1b, 0xe5f10400, @@ -776,7 +798,7 @@ uint32_t nvc0_grhub_code[] = { 0x03f08500, 0x000ed001, 0x00f804bd, -/* 0x0814: ctx_86c */ +/* 0x086c: ctx_86c */ 0x1b0007f1, 0xd00203f0, 0x04bd000f, @@ -787,16 +809,16 @@ uint32_t nvc0_grhub_code[] = { 0xa86ce7f1, 0xf441e3f0, 0x00f89d21, -/* 0x083c: ctx_mem */ +/* 0x0894: ctx_mem */ 0x840007f1, 0xd00203f0, 0x04bd000f, -/* 0x0848: ctx_mem_wait */ +/* 0x08a0: ctx_mem_wait */ 0x8400f7f1, 0xcf02f3f0, 0xfffd00ff, 0xf31bf405, -/* 0x085a: ctx_load */ +/* 0x08b2: ctx_load */ 0x94bd00f8, 0xf10599f0, 0xf00f0007, @@ -814,7 +836,7 @@ uint32_t nvc0_grhub_code[] = { 0x02d00203, 0xf004bd00, 0x21f507f7, - 0x07f1083c, + 0x07f10894, 0x03f0c000, 0x0002d002, 0x0bfe04bd, @@ -869,31 +891,31 @@ uint32_t nvc0_grhub_code[] = { 0x03f01700, 0x0009d002, 0x00f804bd, -/* 0x0978: ctx_chan */ - 0x077f21f5, - 0x085a21f5, +/* 0x09d0: ctx_chan */ + 0x07d721f5, + 0x08b221f5, 0xf40ca7f0, 0xf7f0d021, - 0x3c21f505, - 0xa421f508, -/* 0x0993: ctx_mmio_exec */ + 0x9421f505, + 0xfc21f508, +/* 0x09eb: ctx_mmio_exec */ 0x9800f807, 0x07f14103, 0x03f08100, 0x0003d002, 0x34bd04bd, -/* 0x09a4: ctx_mmio_loop */ +/* 0x09fc: ctx_mmio_loop */ 0xf4ff34c4, 0x57f10f1b, 0x53f00200, 0x0535fa06, -/* 0x09b6: ctx_mmio_pull */ +/* 0x0a0e: ctx_mmio_pull */ 0x4e9803f8, 0x814f9880, 0xb69d21f4, 0x12b60830, 0xdf1bf401, -/* 0x09c8: ctx_mmio_done */ +/* 0x0a20: ctx_mmio_done */ 0xf1160398, 0xf0810007, 0x03d00203, @@ -902,30 +924,30 @@ uint32_t nvc0_grhub_code[] = { 0x13f00100, 0x0601fa06, 0x00f803f8, -/* 0x09e8: ctx_xfer */ +/* 0x0a40: ctx_xfer */ 0xf104e7f0, 0xf0020007, 0x0ed00303, -/* 0x09f7: ctx_xfer_idle */ +/* 0x0a4f: ctx_xfer_idle */ 0xf104bd00, 0xf00000e7, 0xeecf03e3, 0x00e4f100, 0xf21bf420, 0xf40611f4, -/* 0x0a0e: ctx_xfer_pre */ +/* 0x0a66: ctx_xfer_pre */ 0xf7f01102, - 0x1421f510, - 0x7f21f508, + 0x6c21f510, + 0xd721f508, 0x1c11f407, -/* 0x0a1c: ctx_xfer_pre_load */ +/* 0x0a74: ctx_xfer_pre_load */ 0xf502f7f0, - 0xf507b521, - 0xf507c721, - 0xbd07dc21, - 0xb521f5f4, - 0x5a21f507, -/* 0x0a35: ctx_xfer_exec */ + 0xf5080d21, + 0xf5081f21, + 0xbd083421, + 0x0d21f5f4, + 0xb221f508, +/* 0x0a8d: ctx_xfer_exec */ 0x16019808, 0x07f124bd, 0x03f00500, @@ -960,23 +982,65 @@ uint32_t nvc0_grhub_code[] = { 0x1301f402, 0xf40ca7f0, 0xf7f0d021, - 0x3c21f505, + 0x9421f505, 0x3202f408, -/* 0x0ac4: ctx_xfer_post */ +/* 0x0b1c: ctx_xfer_post */ 0xf502f7f0, - 0xbd07b521, - 0x1421f5f4, + 0xbd080d21, + 0x6c21f5f4, 0x7f21f508, - 0xc721f502, - 0xf5f4bd07, - 0xf407b521, + 0x1f21f502, + 0xf5f4bd08, + 0xf4080d21, 0x01981011, 0x0511fd40, 0xf5070bf4, -/* 0x0aef: ctx_xfer_no_post_mmio */ - 0xf5099321, -/* 0x0af3: ctx_xfer_done */ - 0xf807a421, +/* 0x0b47: ctx_xfer_no_post_mmio */ + 0xf509eb21, +/* 0x0b4b: ctx_xfer_done */ + 0xf807fc21, + 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/core/engine/graph/fuc/hubnvd7.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc.h index 624215a005b..62b0c7601d8 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc.h @@ -528,10 +528,10 @@ uint32_t nvd7_grhub_code[] = { 0x0001d001, 0x17f104bd, 0xf7f00100, - 0xb521f502, - 0xc721f507, - 0x10f7f007, - 0x081421f5, + 0x0d21f502, + 0x1f21f508, + 0x10f7f008, + 0x086c21f5, 0x98000e98, 0x21f5010f, 0x14950150, @@ -574,9 +574,9 @@ uint32_t nvd7_grhub_code[] = { 0xb6800040, 0x1bf40132, 0x00f7f0be, - 0x081421f5, + 0x086c21f5, 0xf500f7f0, - 0xf107b521, + 0xf1080d21, 0xf0010007, 0x01d00203, 0xbd04bd00, @@ -610,8 +610,8 @@ uint32_t nvd7_grhub_code[] = { 0x09d00203, 0xf404bd00, 0x31f40132, - 0xe821f502, - 0xf094bd09, + 0x4021f502, + 0xf094bd0a, 0x07f10799, 0x03f01700, 0x0009d002, @@ -621,7 +621,7 @@ uint32_t nvd7_grhub_code[] = { 0x0203f00f, 0xbd0009d0, 0x0131f404, - 0x09e821f5, + 0x0a4021f5, 0x99f094bd, 0x0007f106, 0x0203f017, @@ -631,7 +631,7 @@ uint32_t nvd7_grhub_code[] = { 0x12b920f9, 0x0132f402, 0xf50232f4, - 0xfc09e821, + 0xfc0a4021, 0x0007f120, 0x0203f0c0, 0xbd0002d0, @@ -640,7 +640,7 @@ uint32_t nvd7_grhub_code[] = { 0xf41f23c8, 0x31f40d0b, 0x0232f401, - 0x09e821f5, + 0x0a4021f5, /* 0x063c: chsw_done */ 0xf10127f0, 0xf0c30007, @@ -654,7 +654,7 @@ uint32_t nvd7_grhub_code[] = { /* 0x0660: main_not_ctx_switch */ 0xf401e4b0, 0xf2b90d1b, - 0x7821f502, + 0xd021f502, 0x460ef409, /* 0x0670: main_not_ctx_chan */ 0xf402e4b0, @@ -664,8 +664,8 @@ uint32_t nvd7_grhub_code[] = { 0x09d00203, 0xf404bd00, 0x32f40132, - 0xe821f502, - 0xf094bd09, + 0x4021f502, + 0xf094bd0a, 0x07f10799, 0x03f01700, 0x0009d002, @@ -710,18 +710,40 @@ uint32_t nvd7_grhub_code[] = { /* 0x072b: ih_no_ctxsw */ 0xe40421f4, 0xf40400ab, - 0xb7f1140b, + 0xe7f16c0b, + 0xe3f00708, + 0x6821f440, + 0xf102ffb9, + 0xf0040007, + 0x0fd00203, + 0xf104bd00, + 0xf00704e7, + 0x21f440e3, + 0x02ffb968, + 0x030007f1, + 0xd00203f0, + 0x04bd000f, + 0x9450fec7, + 0xf7f102ee, + 0xf3f00700, + 0x00efbb40, + 0xf16821f4, + 0xf0020007, + 0x0fd00203, + 0xf004bd00, + 0x21f503f7, + 0xb7f1037e, 0xbfb90100, 0x44e7f102, 0x40e3f001, -/* 0x0743: ih_no_fwmthd */ +/* 0x079b: ih_no_fwmthd */ 0xf19d21f4, - 0xbd0104b7, + 0xbd0504b7, 0xb4abffb0, 0xf10f0bf4, 0xf0070007, 0x0bd00303, -/* 0x075b: ih_no_other */ +/* 0x07b3: ih_no_other */ 0xf104bd00, 0xf0010007, 0x0ad00003, @@ -731,36 +753,36 @@ uint32_t nvd7_grhub_code[] = { 0xfc90fca0, 0x0088fe80, 0x32f480fc, -/* 0x077f: ctx_4160s */ +/* 0x07d7: ctx_4160s */ 0xf001f800, 0xffb901f7, 0x60e7f102, 0x40e3f041, -/* 0x078f: ctx_4160s_wait */ +/* 0x07e7: ctx_4160s_wait */ 0xf19d21f4, 0xf04160e7, 0x21f440e3, 0x02ffb968, 0xf404ffc8, 0x00f8f00b, -/* 0x07a4: ctx_4160c */ +/* 0x07fc: ctx_4160c */ 0xffb9f4bd, 0x60e7f102, 0x40e3f041, 0xf89d21f4, -/* 0x07b5: ctx_4170s */ +/* 0x080d: ctx_4170s */ 0x10f5f000, 0xf102ffb9, 0xf04170e7, 0x21f440e3, -/* 0x07c7: ctx_4170w */ +/* 0x081f: ctx_4170w */ 0xf100f89d, 0xf04170e7, 0x21f440e3, 0x02ffb968, 0xf410f4f0, 0x00f8f01b, -/* 0x07dc: ctx_redswitch */ +/* 0x0834: ctx_redswitch */ 0x0200e7f1, 0xf040e5f0, 0xe5f020e5, @@ -768,7 +790,7 @@ uint32_t nvd7_grhub_code[] = { 0x0103f085, 0xbd000ed0, 0x08f7f004, -/* 0x07f8: ctx_redswitch_delay */ +/* 0x0850: ctx_redswitch_delay */ 0xf401f2b6, 0xe5f1fd1b, 0xe5f10400, @@ -776,7 +798,7 @@ uint32_t nvd7_grhub_code[] = { 0x03f08500, 0x000ed001, 0x00f804bd, -/* 0x0814: ctx_86c */ +/* 0x086c: ctx_86c */ 0x1b0007f1, 0xd00203f0, 0x04bd000f, @@ -787,16 +809,16 @@ uint32_t nvd7_grhub_code[] = { 0xa86ce7f1, 0xf441e3f0, 0x00f89d21, -/* 0x083c: ctx_mem */ +/* 0x0894: ctx_mem */ 0x840007f1, 0xd00203f0, 0x04bd000f, -/* 0x0848: ctx_mem_wait */ +/* 0x08a0: ctx_mem_wait */ 0x8400f7f1, 0xcf02f3f0, 0xfffd00ff, 0xf31bf405, -/* 0x085a: ctx_load */ +/* 0x08b2: ctx_load */ 0x94bd00f8, 0xf10599f0, 0xf00f0007, @@ -814,7 +836,7 @@ uint32_t nvd7_grhub_code[] = { 0x02d00203, 0xf004bd00, 0x21f507f7, - 0x07f1083c, + 0x07f10894, 0x03f0c000, 0x0002d002, 0x0bfe04bd, @@ -869,31 +891,31 @@ uint32_t nvd7_grhub_code[] = { 0x03f01700, 0x0009d002, 0x00f804bd, -/* 0x0978: ctx_chan */ - 0x077f21f5, - 0x085a21f5, +/* 0x09d0: ctx_chan */ + 0x07d721f5, + 0x08b221f5, 0xf40ca7f0, 0xf7f0d021, - 0x3c21f505, - 0xa421f508, -/* 0x0993: ctx_mmio_exec */ + 0x9421f505, + 0xfc21f508, +/* 0x09eb: ctx_mmio_exec */ 0x9800f807, 0x07f14103, 0x03f08100, 0x0003d002, 0x34bd04bd, -/* 0x09a4: ctx_mmio_loop */ +/* 0x09fc: ctx_mmio_loop */ 0xf4ff34c4, 0x57f10f1b, 0x53f00200, 0x0535fa06, -/* 0x09b6: ctx_mmio_pull */ +/* 0x0a0e: ctx_mmio_pull */ 0x4e9803f8, 0x814f9880, 0xb69d21f4, 0x12b60830, 0xdf1bf401, -/* 0x09c8: ctx_mmio_done */ +/* 0x0a20: ctx_mmio_done */ 0xf1160398, 0xf0810007, 0x03d00203, @@ -902,30 +924,30 @@ uint32_t nvd7_grhub_code[] = { 0x13f00100, 0x0601fa06, 0x00f803f8, -/* 0x09e8: ctx_xfer */ +/* 0x0a40: ctx_xfer */ 0xf104e7f0, 0xf0020007, 0x0ed00303, -/* 0x09f7: ctx_xfer_idle */ +/* 0x0a4f: ctx_xfer_idle */ 0xf104bd00, 0xf00000e7, 0xeecf03e3, 0x00e4f100, 0xf21bf420, 0xf40611f4, -/* 0x0a0e: ctx_xfer_pre */ +/* 0x0a66: ctx_xfer_pre */ 0xf7f01102, - 0x1421f510, - 0x7f21f508, + 0x6c21f510, + 0xd721f508, 0x1c11f407, -/* 0x0a1c: ctx_xfer_pre_load */ +/* 0x0a74: ctx_xfer_pre_load */ 0xf502f7f0, - 0xf507b521, - 0xf507c721, - 0xbd07dc21, - 0xb521f5f4, - 0x5a21f507, -/* 0x0a35: ctx_xfer_exec */ + 0xf5080d21, + 0xf5081f21, + 0xbd083421, + 0x0d21f5f4, + 0xb221f508, +/* 0x0a8d: ctx_xfer_exec */ 0x16019808, 0x07f124bd, 0x03f00500, @@ -960,23 +982,65 @@ uint32_t nvd7_grhub_code[] = { 0x1301f402, 0xf40ca7f0, 0xf7f0d021, - 0x3c21f505, + 0x9421f505, 0x3202f408, -/* 0x0ac4: ctx_xfer_post */ +/* 0x0b1c: ctx_xfer_post */ 0xf502f7f0, - 0xbd07b521, - 0x1421f5f4, + 0xbd080d21, + 0x6c21f5f4, 0x7f21f508, - 0xc721f502, - 0xf5f4bd07, - 0xf407b521, + 0x1f21f502, + 0xf5f4bd08, + 0xf4080d21, 0x01981011, 0x0511fd40, 0xf5070bf4, -/* 0x0aef: ctx_xfer_no_post_mmio */ - 0xf5099321, -/* 0x0af3: ctx_xfer_done */ - 0xf807a421, +/* 0x0b47: ctx_xfer_no_post_mmio */ + 0xf509eb21, +/* 0x0b4b: ctx_xfer_done */ + 0xf807fc21, + 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/core/engine/graph/fuc/hubnve0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc.h index 6547b3dfc7e..51c3797d853 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc.h @@ -528,10 +528,10 @@ uint32_t nve0_grhub_code[] = { 0x0001d001, 0x17f104bd, 0xf7f00100, - 0x7f21f502, - 0x9121f507, + 0xd721f502, + 0xe921f507, 0x10f7f007, - 0x07de21f5, + 0x083621f5, 0x98000e98, 0x21f5010f, 0x14950150, @@ -574,9 +574,9 @@ uint32_t nve0_grhub_code[] = { 0xb6800040, 0x1bf40132, 0x00f7f0be, - 0x07de21f5, + 0x083621f5, 0xf500f7f0, - 0xf1077f21, + 0xf107d721, 0xf0010007, 0x01d00203, 0xbd04bd00, @@ -610,8 +610,8 @@ uint32_t nve0_grhub_code[] = { 0x09d00203, 0xf404bd00, 0x31f40132, - 0xaa21f502, - 0xf094bd09, + 0x0221f502, + 0xf094bd0a, 0x07f10799, 0x03f01700, 0x0009d002, @@ -621,7 +621,7 @@ uint32_t nve0_grhub_code[] = { 0x0203f00f, 0xbd0009d0, 0x0131f404, - 0x09aa21f5, + 0x0a0221f5, 0x99f094bd, 0x0007f106, 0x0203f017, @@ -631,7 +631,7 @@ uint32_t nve0_grhub_code[] = { 0x12b920f9, 0x0132f402, 0xf50232f4, - 0xfc09aa21, + 0xfc0a0221, 0x0007f120, 0x0203f0c0, 0xbd0002d0, @@ -640,7 +640,7 @@ uint32_t nve0_grhub_code[] = { 0xf41f23c8, 0x31f40d0b, 0x0232f401, - 0x09aa21f5, + 0x0a0221f5, /* 0x063c: chsw_done */ 0xf10127f0, 0xf0c30007, @@ -654,7 +654,7 @@ uint32_t nve0_grhub_code[] = { /* 0x0660: main_not_ctx_switch */ 0xf401e4b0, 0xf2b90d1b, - 0x4221f502, + 0x9a21f502, 0x460ef409, /* 0x0670: main_not_ctx_chan */ 0xf402e4b0, @@ -664,8 +664,8 @@ uint32_t nve0_grhub_code[] = { 0x09d00203, 0xf404bd00, 0x32f40132, - 0xaa21f502, - 0xf094bd09, + 0x0221f502, + 0xf094bd0a, 0x07f10799, 0x03f01700, 0x0009d002, @@ -710,18 +710,40 @@ uint32_t nve0_grhub_code[] = { /* 0x072b: ih_no_ctxsw */ 0xe40421f4, 0xf40400ab, - 0xb7f1140b, + 0xe7f16c0b, + 0xe3f00708, + 0x6821f440, + 0xf102ffb9, + 0xf0040007, + 0x0fd00203, + 0xf104bd00, + 0xf00704e7, + 0x21f440e3, + 0x02ffb968, + 0x030007f1, + 0xd00203f0, + 0x04bd000f, + 0x9450fec7, + 0xf7f102ee, + 0xf3f00700, + 0x00efbb40, + 0xf16821f4, + 0xf0020007, + 0x0fd00203, + 0xf004bd00, + 0x21f503f7, + 0xb7f1037e, 0xbfb90100, 0x44e7f102, 0x40e3f001, -/* 0x0743: ih_no_fwmthd */ +/* 0x079b: ih_no_fwmthd */ 0xf19d21f4, - 0xbd0104b7, + 0xbd0504b7, 0xb4abffb0, 0xf10f0bf4, 0xf0070007, 0x0bd00303, -/* 0x075b: ih_no_other */ +/* 0x07b3: ih_no_other */ 0xf104bd00, 0xf0010007, 0x0ad00003, @@ -731,19 +753,19 @@ uint32_t nve0_grhub_code[] = { 0xfc90fca0, 0x0088fe80, 0x32f480fc, -/* 0x077f: ctx_4170s */ +/* 0x07d7: ctx_4170s */ 0xf001f800, 0xffb910f5, 0x70e7f102, 0x40e3f041, 0xf89d21f4, -/* 0x0791: ctx_4170w */ +/* 0x07e9: ctx_4170w */ 0x70e7f100, 0x40e3f041, 0xb96821f4, 0xf4f002ff, 0xf01bf410, -/* 0x07a6: ctx_redswitch */ +/* 0x07fe: ctx_redswitch */ 0xe7f100f8, 0xe5f00200, 0x20e5f040, @@ -751,7 +773,7 @@ uint32_t nve0_grhub_code[] = { 0xf0850007, 0x0ed00103, 0xf004bd00, -/* 0x07c2: ctx_redswitch_delay */ +/* 0x081a: ctx_redswitch_delay */ 0xf2b608f7, 0xfd1bf401, 0x0400e5f1, @@ -759,7 +781,7 @@ uint32_t nve0_grhub_code[] = { 0x850007f1, 0xd00103f0, 0x04bd000e, -/* 0x07de: ctx_86c */ +/* 0x0836: ctx_86c */ 0x07f100f8, 0x03f01b00, 0x000fd002, @@ -770,17 +792,17 @@ uint32_t nve0_grhub_code[] = { 0xe7f102ff, 0xe3f0a86c, 0x9d21f441, -/* 0x0806: ctx_mem */ +/* 0x085e: ctx_mem */ 0x07f100f8, 0x03f08400, 0x000fd002, -/* 0x0812: ctx_mem_wait */ +/* 0x086a: ctx_mem_wait */ 0xf7f104bd, 0xf3f08400, 0x00ffcf02, 0xf405fffd, 0x00f8f31b, -/* 0x0824: ctx_load */ +/* 0x087c: ctx_load */ 0x99f094bd, 0x0007f105, 0x0203f00f, @@ -797,7 +819,7 @@ uint32_t nve0_grhub_code[] = { 0x0203f083, 0xbd0002d0, 0x07f7f004, - 0x080621f5, + 0x085e21f5, 0xc00007f1, 0xd00203f0, 0x04bd0002, @@ -852,29 +874,29 @@ uint32_t nve0_grhub_code[] = { 0x170007f1, 0xd00203f0, 0x04bd0009, -/* 0x0942: ctx_chan */ +/* 0x099a: ctx_chan */ 0x21f500f8, - 0xa7f00824, + 0xa7f0087c, 0xd021f40c, 0xf505f7f0, - 0xf8080621, -/* 0x0955: ctx_mmio_exec */ + 0xf8085e21, +/* 0x09ad: ctx_mmio_exec */ 0x41039800, 0x810007f1, 0xd00203f0, 0x04bd0003, -/* 0x0966: ctx_mmio_loop */ +/* 0x09be: ctx_mmio_loop */ 0x34c434bd, 0x0f1bf4ff, 0x020057f1, 0xfa0653f0, 0x03f80535, -/* 0x0978: ctx_mmio_pull */ +/* 0x09d0: ctx_mmio_pull */ 0x98804e98, 0x21f4814f, 0x0830b69d, 0xf40112b6, -/* 0x098a: ctx_mmio_done */ +/* 0x09e2: ctx_mmio_done */ 0x0398df1b, 0x0007f116, 0x0203f081, @@ -883,30 +905,30 @@ uint32_t nve0_grhub_code[] = { 0x010017f1, 0xfa0613f0, 0x03f80601, -/* 0x09aa: ctx_xfer */ +/* 0x0a02: ctx_xfer */ 0xe7f000f8, 0x0007f104, 0x0303f002, 0xbd000ed0, -/* 0x09b9: ctx_xfer_idle */ +/* 0x0a11: ctx_xfer_idle */ 0x00e7f104, 0x03e3f000, 0xf100eecf, 0xf42000e4, 0x11f4f21b, 0x0d02f406, -/* 0x09d0: ctx_xfer_pre */ +/* 0x0a28: ctx_xfer_pre */ 0xf510f7f0, - 0xf407de21, -/* 0x09da: ctx_xfer_pre_load */ + 0xf4083621, +/* 0x0a32: ctx_xfer_pre_load */ 0xf7f01c11, - 0x7f21f502, - 0x9121f507, - 0xa621f507, + 0xd721f502, + 0xe921f507, + 0xfe21f507, 0xf5f4bd07, - 0xf5077f21, -/* 0x09f3: ctx_xfer_exec */ - 0x98082421, + 0xf507d721, +/* 0x0a4b: ctx_xfer_exec */ + 0x98087c21, 0x24bd1601, 0x050007f1, 0xd00103f0, @@ -941,21 +963,21 @@ uint32_t nve0_grhub_code[] = { 0xa7f01301, 0xd021f40c, 0xf505f7f0, - 0xf4080621, -/* 0x0a82: ctx_xfer_post */ + 0xf4085e21, +/* 0x0ada: ctx_xfer_post */ 0xf7f02e02, - 0x7f21f502, + 0xd721f502, 0xf5f4bd07, - 0xf507de21, + 0xf5083621, 0xf5027f21, - 0xbd079121, - 0x7f21f5f4, + 0xbd07e921, + 0xd721f5f4, 0x1011f407, 0xfd400198, 0x0bf40511, - 0x5521f507, -/* 0x0aad: ctx_xfer_no_post_mmio */ -/* 0x0aad: ctx_xfer_done */ + 0xad21f507, +/* 0x0b05: ctx_xfer_no_post_mmio */ +/* 0x0b05: ctx_xfer_done */ 0x0000f809, 0x00000000, 0x00000000, @@ -977,4 +999,46 @@ uint32_t nve0_grhub_code[] = { 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/core/engine/graph/fuc/hubnvf0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvf0.fuc.h index a5aee5a4302..a0af4b703a8 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvf0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvf0.fuc.h @@ -528,10 +528,10 @@ uint32_t nvf0_grhub_code[] = { 0x0001d001, 0x17f104bd, 0xf7f00100, - 0x7f21f502, - 0x9121f507, + 0xd721f502, + 0xe921f507, 0x10f7f007, - 0x07de21f5, + 0x083621f5, 0x98000e98, 0x21f5010f, 0x14950150, @@ -574,9 +574,9 @@ uint32_t nvf0_grhub_code[] = { 0xb6800040, 0x1bf40132, 0x00f7f0be, - 0x07de21f5, + 0x083621f5, 0xf500f7f0, - 0xf1077f21, + 0xf107d721, 0xf0010007, 0x01d00203, 0xbd04bd00, @@ -610,8 +610,8 @@ uint32_t nvf0_grhub_code[] = { 0x09d00203, 0xf404bd00, 0x31f40132, - 0xaa21f502, - 0xf094bd09, + 0x0221f502, + 0xf094bd0a, 0x07f10799, 0x03f01700, 0x0009d002, @@ -621,7 +621,7 @@ uint32_t nvf0_grhub_code[] = { 0x0203f037, 0xbd0009d0, 0x0131f404, - 0x09aa21f5, + 0x0a0221f5, 0x99f094bd, 0x0007f106, 0x0203f017, @@ -631,7 +631,7 @@ uint32_t nvf0_grhub_code[] = { 0x12b920f9, 0x0132f402, 0xf50232f4, - 0xfc09aa21, + 0xfc0a0221, 0x0007f120, 0x0203f0c0, 0xbd0002d0, @@ -640,7 +640,7 @@ uint32_t nvf0_grhub_code[] = { 0xf41f23c8, 0x31f40d0b, 0x0232f401, - 0x09aa21f5, + 0x0a0221f5, /* 0x063c: chsw_done */ 0xf10127f0, 0xf0c30007, @@ -654,7 +654,7 @@ uint32_t nvf0_grhub_code[] = { /* 0x0660: main_not_ctx_switch */ 0xf401e4b0, 0xf2b90d1b, - 0x4221f502, + 0x9a21f502, 0x460ef409, /* 0x0670: main_not_ctx_chan */ 0xf402e4b0, @@ -664,8 +664,8 @@ uint32_t nvf0_grhub_code[] = { 0x09d00203, 0xf404bd00, 0x32f40132, - 0xaa21f502, - 0xf094bd09, + 0x0221f502, + 0xf094bd0a, 0x07f10799, 0x03f01700, 0x0009d002, @@ -710,18 +710,40 @@ uint32_t nvf0_grhub_code[] = { /* 0x072b: ih_no_ctxsw */ 0xe40421f4, 0xf40400ab, - 0xb7f1140b, + 0xe7f16c0b, + 0xe3f00708, + 0x6821f440, + 0xf102ffb9, + 0xf0040007, + 0x0fd00203, + 0xf104bd00, + 0xf00704e7, + 0x21f440e3, + 0x02ffb968, + 0x030007f1, + 0xd00203f0, + 0x04bd000f, + 0x9450fec7, + 0xf7f102ee, + 0xf3f00700, + 0x00efbb40, + 0xf16821f4, + 0xf0020007, + 0x0fd00203, + 0xf004bd00, + 0x21f503f7, + 0xb7f1037e, 0xbfb90100, 0x44e7f102, 0x40e3f001, -/* 0x0743: ih_no_fwmthd */ +/* 0x079b: ih_no_fwmthd */ 0xf19d21f4, - 0xbd0104b7, + 0xbd0504b7, 0xb4abffb0, 0xf10f0bf4, 0xf0070007, 0x0bd00303, -/* 0x075b: ih_no_other */ +/* 0x07b3: ih_no_other */ 0xf104bd00, 0xf0010007, 0x0ad00003, @@ -731,19 +753,19 @@ uint32_t nvf0_grhub_code[] = { 0xfc90fca0, 0x0088fe80, 0x32f480fc, -/* 0x077f: ctx_4170s */ +/* 0x07d7: ctx_4170s */ 0xf001f800, 0xffb910f5, 0x70e7f102, 0x40e3f041, 0xf89d21f4, -/* 0x0791: ctx_4170w */ +/* 0x07e9: ctx_4170w */ 0x70e7f100, 0x40e3f041, 0xb96821f4, 0xf4f002ff, 0xf01bf410, -/* 0x07a6: ctx_redswitch */ +/* 0x07fe: ctx_redswitch */ 0xe7f100f8, 0xe5f00200, 0x20e5f040, @@ -751,7 +773,7 @@ uint32_t nvf0_grhub_code[] = { 0xf0850007, 0x0ed00103, 0xf004bd00, -/* 0x07c2: ctx_redswitch_delay */ +/* 0x081a: ctx_redswitch_delay */ 0xf2b608f7, 0xfd1bf401, 0x0400e5f1, @@ -759,7 +781,7 @@ uint32_t nvf0_grhub_code[] = { 0x850007f1, 0xd00103f0, 0x04bd000e, -/* 0x07de: ctx_86c */ +/* 0x0836: ctx_86c */ 0x07f100f8, 0x03f02300, 0x000fd002, @@ -770,17 +792,17 @@ uint32_t nvf0_grhub_code[] = { 0xe7f102ff, 0xe3f0a88c, 0x9d21f441, -/* 0x0806: ctx_mem */ +/* 0x085e: ctx_mem */ 0x07f100f8, 0x03f08400, 0x000fd002, -/* 0x0812: ctx_mem_wait */ +/* 0x086a: ctx_mem_wait */ 0xf7f104bd, 0xf3f08400, 0x00ffcf02, 0xf405fffd, 0x00f8f31b, -/* 0x0824: ctx_load */ +/* 0x087c: ctx_load */ 0x99f094bd, 0x0007f105, 0x0203f037, @@ -797,7 +819,7 @@ uint32_t nvf0_grhub_code[] = { 0x0203f083, 0xbd0002d0, 0x07f7f004, - 0x080621f5, + 0x085e21f5, 0xc00007f1, 0xd00203f0, 0x04bd0002, @@ -852,29 +874,29 @@ uint32_t nvf0_grhub_code[] = { 0x170007f1, 0xd00203f0, 0x04bd0009, -/* 0x0942: ctx_chan */ +/* 0x099a: ctx_chan */ 0x21f500f8, - 0xa7f00824, + 0xa7f0087c, 0xd021f40c, 0xf505f7f0, - 0xf8080621, -/* 0x0955: ctx_mmio_exec */ + 0xf8085e21, +/* 0x09ad: ctx_mmio_exec */ 0x41039800, 0x810007f1, 0xd00203f0, 0x04bd0003, -/* 0x0966: ctx_mmio_loop */ +/* 0x09be: ctx_mmio_loop */ 0x34c434bd, 0x0f1bf4ff, 0x020057f1, 0xfa0653f0, 0x03f80535, -/* 0x0978: ctx_mmio_pull */ +/* 0x09d0: ctx_mmio_pull */ 0x98804e98, 0x21f4814f, 0x0830b69d, 0xf40112b6, -/* 0x098a: ctx_mmio_done */ +/* 0x09e2: ctx_mmio_done */ 0x0398df1b, 0x0007f116, 0x0203f081, @@ -883,30 +905,30 @@ uint32_t nvf0_grhub_code[] = { 0x010017f1, 0xfa0613f0, 0x03f80601, -/* 0x09aa: ctx_xfer */ +/* 0x0a02: ctx_xfer */ 0xe7f000f8, 0x0007f104, 0x0303f002, 0xbd000ed0, -/* 0x09b9: ctx_xfer_idle */ +/* 0x0a11: ctx_xfer_idle */ 0x00e7f104, 0x03e3f000, 0xf100eecf, 0xf42000e4, 0x11f4f21b, 0x0d02f406, -/* 0x09d0: ctx_xfer_pre */ +/* 0x0a28: ctx_xfer_pre */ 0xf510f7f0, - 0xf407de21, -/* 0x09da: ctx_xfer_pre_load */ + 0xf4083621, +/* 0x0a32: ctx_xfer_pre_load */ 0xf7f01c11, - 0x7f21f502, - 0x9121f507, - 0xa621f507, + 0xd721f502, + 0xe921f507, + 0xfe21f507, 0xf5f4bd07, - 0xf5077f21, -/* 0x09f3: ctx_xfer_exec */ - 0x98082421, + 0xf507d721, +/* 0x0a4b: ctx_xfer_exec */ + 0x98087c21, 0x24bd1601, 0x050007f1, 0xd00103f0, @@ -941,21 +963,21 @@ uint32_t nvf0_grhub_code[] = { 0xa7f01301, 0xd021f40c, 0xf505f7f0, - 0xf4080621, -/* 0x0a82: ctx_xfer_post */ + 0xf4085e21, +/* 0x0ada: ctx_xfer_post */ 0xf7f02e02, - 0x7f21f502, + 0xd721f502, 0xf5f4bd07, - 0xf507de21, + 0xf5083621, 0xf5027f21, - 0xbd079121, - 0x7f21f5f4, + 0xbd07e921, + 0xd721f5f4, 0x1011f407, 0xfd400198, 0x0bf40511, - 0x5521f507, -/* 0x0aad: ctx_xfer_no_post_mmio */ -/* 0x0aad: ctx_xfer_done */ + 0xad21f507, +/* 0x0b05: ctx_xfer_no_post_mmio */ +/* 0x0b05: ctx_xfer_done */ 0x0000f809, 0x00000000, 0x00000000, @@ -977,4 +999,46 @@ uint32_t nvf0_grhub_code[] = { 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/core/engine/graph/fuc/macros.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/macros.fuc index a47d49db523..2a0b0f84429 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/macros.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/macros.fuc @@ -30,6 +30,12 @@ #define GK110 0xf0 #define GK208 0x108 +#define NV_PGRAPH_TRAPPED_ADDR 0x400704 +#define NV_PGRAPH_TRAPPED_DATA_LO 0x400708 +#define NV_PGRAPH_TRAPPED_DATA_HI 0x40070c + +#define NV_PGRAPH_FE_OBJECT_TABLE(n) ((n) * 4 + 0x400700) + #define NV_PGRAPH_FECS_INTR_ACK 0x409004 #define NV_PGRAPH_FECS_INTR 0x409008 #define NV_PGRAPH_FECS_INTR_FWMTHD 0x00000400 diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/os.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/os.h index fd1d380de09..1718ae4e822 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/os.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/os.h @@ -3,5 +3,6 @@ #define E_BAD_COMMAND 0x00000001 #define E_CMD_OVERFLOW 0x00000002 +#define E_BAD_FWMTHD 0x00000003 #endif diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/gk20a.c b/drivers/gpu/drm/nouveau/core/engine/graph/gk20a.c new file mode 100644 index 00000000000..83048a56430 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/gk20a.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "nvc0.h" +#include "ctxnvc0.h" + +static struct nouveau_oclass +gk20a_graph_sclass[] = { + { 0x902d, &nouveau_object_ofuncs }, + { 0xa040, &nouveau_object_ofuncs }, + { 0xa297, &nouveau_object_ofuncs }, + { 0xa0c0, &nouveau_object_ofuncs }, + {} +}; + +struct nouveau_oclass * +gk20a_graph_oclass = &(struct nvc0_graph_oclass) { + .base.handle = NV_ENGINE(GR, 0xea), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvc0_graph_ctor, + .dtor = nvc0_graph_dtor, + .init = nve4_graph_init, + .fini = nve4_graph_fini, + }, + .cclass = &gk20a_grctx_oclass, + .sclass = gk20a_graph_sclass, + .mmio = nve4_graph_pack_mmio, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c index 2c7809e1a09..20665c21d80 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c @@ -901,7 +901,7 @@ nv50_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine, nv_engine(priv)->sclass = nvaf_graph_sclass; break; - }; + } /* unfortunate hw bug workaround... */ if (nv_device(priv)->chipset != 0x50 && @@ -976,7 +976,6 @@ nv50_graph_init(struct nouveau_object *object) break; case 0xa0: default: - nv_wr32(priv, 0x402cc0, 0x00000000); if (nv_device(priv)->chipset == 0xa0 || nv_device(priv)->chipset == 0xaa || nv_device(priv)->chipset == 0xac) { @@ -991,10 +990,10 @@ nv50_graph_init(struct nouveau_object *object) /* zero out zcull regions */ for (i = 0; i < 8; i++) { - nv_wr32(priv, 0x402c20 + (i * 8), 0x00000000); - nv_wr32(priv, 0x402c24 + (i * 8), 0x00000000); - nv_wr32(priv, 0x402c28 + (i * 8), 0x00000000); - nv_wr32(priv, 0x402c2c + (i * 8), 0x00000000); + nv_wr32(priv, 0x402c20 + (i * 0x10), 0x00000000); + nv_wr32(priv, 0x402c24 + (i * 0x10), 0x00000000); + nv_wr32(priv, 0x402c28 + (i * 0x10), 0x00000000); + nv_wr32(priv, 0x402c2c + (i * 0x10), 0x00000000); } return 0; } diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c index f3c7329da0a..aa083891635 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c @@ -789,17 +789,40 @@ nvc0_graph_ctxctl_debug(struct nvc0_graph_priv *priv) static void nvc0_graph_ctxctl_isr(struct nvc0_graph_priv *priv) { - u32 ustat = nv_rd32(priv, 0x409c18); + u32 stat = nv_rd32(priv, 0x409c18); - if (ustat & 0x00000001) - nv_error(priv, "CTXCTL ucode error\n"); - if (ustat & 0x00080000) - nv_error(priv, "CTXCTL watchdog timeout\n"); - if (ustat & ~0x00080001) - nv_error(priv, "CTXCTL 0x%08x\n", ustat); + if (stat & 0x00000001) { + u32 code = nv_rd32(priv, 0x409814); + if (code == E_BAD_FWMTHD) { + u32 class = nv_rd32(priv, 0x409808); + u32 addr = nv_rd32(priv, 0x40980c); + u32 subc = (addr & 0x00070000) >> 16; + u32 mthd = (addr & 0x00003ffc); + u32 data = nv_rd32(priv, 0x409810); + + nv_error(priv, "FECS MTHD subc %d class 0x%04x " + "mthd 0x%04x data 0x%08x\n", + subc, class, mthd, data); + + nv_wr32(priv, 0x409c20, 0x00000001); + stat &= ~0x00000001; + } else { + nv_error(priv, "FECS ucode error %d\n", code); + } + } - nvc0_graph_ctxctl_debug(priv); - nv_wr32(priv, 0x409c20, ustat); + if (stat & 0x00080000) { + nv_error(priv, "FECS watchdog timeout\n"); + nvc0_graph_ctxctl_debug(priv); + nv_wr32(priv, 0x409c20, 0x00080000); + stat &= ~0x00080000; + } + + if (stat) { + nv_error(priv, "FECS 0x%08x\n", stat); + nvc0_graph_ctxctl_debug(priv); + nv_wr32(priv, 0x409c20, stat); + } } static void @@ -894,6 +917,10 @@ nvc0_graph_init_fw(struct nvc0_graph_priv *priv, u32 fuc_base, nv_wr32(priv, fuc_base + 0x0188, i >> 6); nv_wr32(priv, fuc_base + 0x0184, code->data[i]); } + + /* code must be padded to 0x40 words */ + for (; i & 0x3f; i++) + nv_wr32(priv, fuc_base + 0x0184, 0); } static void @@ -1259,10 +1286,14 @@ nvc0_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nvc0_graph_oclass *oclass = (void *)bclass; struct nouveau_device *device = nv_device(parent); struct nvc0_graph_priv *priv; + bool use_ext_fw, enable; int ret, i; - ret = nouveau_graph_create(parent, engine, bclass, - (oclass->fecs.ucode != NULL), &priv); + use_ext_fw = nouveau_boolopt(device->cfgopt, "NvGrUseFW", + oclass->fecs.ucode == NULL); + enable = use_ext_fw || oclass->fecs.ucode != NULL; + + ret = nouveau_graph_create(parent, engine, bclass, enable, &priv); *pobject = nv_object(priv); if (ret) return ret; @@ -1272,7 +1303,7 @@ nvc0_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->base.units = nvc0_graph_units; - if (nouveau_boolopt(device->cfgopt, "NvGrUseFW", false)) { + if (use_ext_fw) { nv_info(priv, "using external firmware\n"); if (nvc0_graph_ctor_fw(priv, "fuc409c", &priv->fuc409c) || nvc0_graph_ctor_fw(priv, "fuc409d", &priv->fuc409d) || diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h index 90d44616c87..ffc289198dd 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h @@ -38,6 +38,8 @@ #include <engine/fifo.h> #include <engine/graph.h> +#include "fuc/os.h" + #define GPC_MAX 32 #define TPC_MAX (GPC_MAX * 8) @@ -116,6 +118,7 @@ int nvc0_graph_ctor(struct nouveau_object *, struct nouveau_object *, struct nouveau_object **); void nvc0_graph_dtor(struct nouveau_object *); int nvc0_graph_init(struct nouveau_object *); +int nve4_graph_fini(struct nouveau_object *, bool); int nve4_graph_init(struct nouveau_object *); extern struct nouveau_oclass nvc0_graph_sclass[]; @@ -217,6 +220,7 @@ extern const struct nvc0_graph_init nve4_graph_init_main_0[]; extern const struct nvc0_graph_init nve4_graph_init_tpccs_0[]; extern const struct nvc0_graph_init nve4_graph_init_pe_0[]; extern const struct nvc0_graph_init nve4_graph_init_be_0[]; +extern const struct nvc0_graph_pack nve4_graph_pack_mmio[]; extern const struct nvc0_graph_init nvf0_graph_init_fe_0[]; extern const struct nvc0_graph_init nvf0_graph_init_sked_0[]; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nve4.c b/drivers/gpu/drm/nouveau/core/engine/graph/nve4.c index f7c01121717..51e0c075ad3 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nve4.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nve4.c @@ -151,7 +151,7 @@ nve4_graph_init_be_0[] = { {} }; -static const struct nvc0_graph_pack +const struct nvc0_graph_pack nve4_graph_pack_mmio[] = { { nve4_graph_init_main_0 }, { nvc0_graph_init_fe_0 }, @@ -189,7 +189,7 @@ nve4_graph_pack_mmio[] = { * PGRAPH engine/subdev functions ******************************************************************************/ -static int +int nve4_graph_fini(struct nouveau_object *object, bool suspend) { struct nvc0_graph_priv *priv = (void *)object; diff --git a/drivers/gpu/drm/nouveau/core/engine/software/nv50.c b/drivers/gpu/drm/nouveau/core/engine/software/nv50.c index 5ce686ee729..f3b4d9dbf23 100644 --- a/drivers/gpu/drm/nouveau/core/engine/software/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/software/nv50.c @@ -124,7 +124,7 @@ nv50_software_sclass[] = { ******************************************************************************/ static int -nv50_software_vblsem_release(void *data, int head) +nv50_software_vblsem_release(void *data, u32 type, int head) { struct nv50_software_chan *chan = data; struct nv50_software_priv *priv = (void *)nv_object(chan)->engine; @@ -183,7 +183,7 @@ nv50_software_context_ctor(struct nouveau_object *parent, return -ENOMEM; for (i = 0; i < chan->vblank.nr_event; i++) { - ret = nouveau_event_new(pdisp->vblank, i, pclass->vblank, + ret = nouveau_event_new(pdisp->vblank, 1, i, pclass->vblank, chan, &chan->vblank.event[i]); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/core/engine/software/nv50.h b/drivers/gpu/drm/nouveau/core/engine/software/nv50.h index 2de370c2127..bb49a7a2085 100644 --- a/drivers/gpu/drm/nouveau/core/engine/software/nv50.h +++ b/drivers/gpu/drm/nouveau/core/engine/software/nv50.h @@ -19,7 +19,7 @@ int nv50_software_ctor(struct nouveau_object *, struct nouveau_object *, struct nv50_software_cclass { struct nouveau_oclass base; - int (*vblank)(void *, int); + int (*vblank)(void *, u32, int); }; struct nv50_software_chan { diff --git a/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c index f9430c1bf3e..135c20f3835 100644 --- a/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c @@ -104,7 +104,7 @@ nvc0_software_sclass[] = { ******************************************************************************/ static int -nvc0_software_vblsem_release(void *data, int head) +nvc0_software_vblsem_release(void *data, u32 type, int head) { struct nv50_software_chan *chan = data; struct nv50_software_priv *priv = (void *)nv_object(chan)->engine; diff --git a/drivers/gpu/drm/nouveau/core/include/core/class.h b/drivers/gpu/drm/nouveau/core/include/core/class.h index 9c0cd73462d..e0c812bc884 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/class.h +++ b/drivers/gpu/drm/nouveau/core/include/core/class.h @@ -295,6 +295,10 @@ struct nv04_display_scanoutpos { #define NV84_DISP_SOR_HDMI_PWR_REKEY 0x0000007f #define NV50_DISP_SOR_LVDS_SCRIPT 0x00013000 #define NV50_DISP_SOR_LVDS_SCRIPT_ID 0x0000ffff +#define NV94_DISP_SOR_DP_PWR 0x00016000 +#define NV94_DISP_SOR_DP_PWR_STATE 0x00000001 +#define NV94_DISP_SOR_DP_PWR_STATE_OFF 0x00000000 +#define NV94_DISP_SOR_DP_PWR_STATE_ON 0x00000001 #define NV50_DISP_DAC_MTHD 0x00020000 #define NV50_DISP_DAC_MTHD_TYPE 0x0000f000 diff --git a/drivers/gpu/drm/nouveau/core/include/core/event.h b/drivers/gpu/drm/nouveau/core/include/core/event.h index 5d539ebff3e..ba3f1a76a81 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/event.h +++ b/drivers/gpu/drm/nouveau/core/include/core/event.h @@ -12,32 +12,33 @@ struct nouveau_eventh { struct nouveau_event *event; struct list_head head; unsigned long flags; + u32 types; int index; - int (*func)(void *, int); + int (*func)(void *, u32, int); void *priv; }; struct nouveau_event { - spinlock_t list_lock; - spinlock_t refs_lock; - void *priv; - void (*enable)(struct nouveau_event *, int index); - void (*disable)(struct nouveau_event *, int index); + int (*check)(struct nouveau_event *, u32 type, int index); + void (*enable)(struct nouveau_event *, int type, int index); + void (*disable)(struct nouveau_event *, int type, int index); + int types_nr; int index_nr; - struct { - struct list_head list; - int refs; - } index[]; + + spinlock_t list_lock; + struct list_head *list; + spinlock_t refs_lock; + int refs[]; }; -int nouveau_event_create(int index_nr, struct nouveau_event **); +int nouveau_event_create(int types_nr, int index_nr, struct nouveau_event **); void nouveau_event_destroy(struct nouveau_event **); -void nouveau_event_trigger(struct nouveau_event *, int index); +void nouveau_event_trigger(struct nouveau_event *, u32 types, int index); -int nouveau_event_new(struct nouveau_event *, int index, - int (*func)(void *, int), void *, +int nouveau_event_new(struct nouveau_event *, u32 types, int index, + int (*func)(void *, u32, int), void *, struct nouveau_eventh **); void nouveau_event_ref(struct nouveau_eventh *, struct nouveau_eventh **); void nouveau_event_get(struct nouveau_eventh *); diff --git a/drivers/gpu/drm/nouveau/core/include/engine/disp.h b/drivers/gpu/drm/nouveau/core/include/engine/disp.h index fd0c68804de..fde84289680 100644 --- a/drivers/gpu/drm/nouveau/core/include/engine/disp.h +++ b/drivers/gpu/drm/nouveau/core/include/engine/disp.h @@ -6,8 +6,19 @@ #include <core/device.h> #include <core/event.h> +enum nvkm_hpd_event { + NVKM_HPD_PLUG = 1, + NVKM_HPD_UNPLUG = 2, + NVKM_HPD_IRQ = 4, + NVKM_HPD = (NVKM_HPD_PLUG | NVKM_HPD_UNPLUG | NVKM_HPD_IRQ) +}; + struct nouveau_disp { struct nouveau_engine base; + + struct list_head outp; + struct nouveau_event *hpd; + struct nouveau_event *vblank; }; @@ -17,25 +28,6 @@ nouveau_disp(void *obj) return (void *)nv_device(obj)->subdev[NVDEV_ENGINE_DISP]; } -#define nouveau_disp_create(p,e,c,h,i,x,d) \ - nouveau_disp_create_((p), (e), (c), (h), (i), (x), \ - sizeof(**d), (void **)d) -#define nouveau_disp_destroy(d) ({ \ - struct nouveau_disp *disp = (d); \ - _nouveau_disp_dtor(nv_object(disp)); \ -}) -#define nouveau_disp_init(d) \ - nouveau_engine_init(&(d)->base) -#define nouveau_disp_fini(d,s) \ - nouveau_engine_fini(&(d)->base, (s)) - -int nouveau_disp_create_(struct nouveau_object *, struct nouveau_object *, - struct nouveau_oclass *, int heads, - const char *, const char *, int, void **); -void _nouveau_disp_dtor(struct nouveau_object *); -#define _nouveau_disp_init _nouveau_engine_init -#define _nouveau_disp_fini _nouveau_engine_fini - extern struct nouveau_oclass *nv04_disp_oclass; extern struct nouveau_oclass *nv50_disp_oclass; extern struct nouveau_oclass *nv84_disp_oclass; diff --git a/drivers/gpu/drm/nouveau/core/include/engine/fifo.h b/drivers/gpu/drm/nouveau/core/include/engine/fifo.h index 26b6b2bb111..b639eb2c74f 100644 --- a/drivers/gpu/drm/nouveau/core/include/engine/fifo.h +++ b/drivers/gpu/drm/nouveau/core/include/engine/fifo.h @@ -109,6 +109,7 @@ extern struct nouveau_oclass *nv50_fifo_oclass; extern struct nouveau_oclass *nv84_fifo_oclass; extern struct nouveau_oclass *nvc0_fifo_oclass; extern struct nouveau_oclass *nve0_fifo_oclass; +extern struct nouveau_oclass *gk20a_fifo_oclass; extern struct nouveau_oclass *nv108_fifo_oclass; void nv04_fifo_intr(struct nouveau_subdev *); diff --git a/drivers/gpu/drm/nouveau/core/include/engine/graph.h b/drivers/gpu/drm/nouveau/core/include/engine/graph.h index 871edfdf3d5..8c1d4772da0 100644 --- a/drivers/gpu/drm/nouveau/core/include/engine/graph.h +++ b/drivers/gpu/drm/nouveau/core/include/engine/graph.h @@ -68,6 +68,7 @@ extern struct nouveau_oclass *nvc8_graph_oclass; extern struct nouveau_oclass *nvd7_graph_oclass; extern struct nouveau_oclass *nvd9_graph_oclass; extern struct nouveau_oclass *nve4_graph_oclass; +extern struct nouveau_oclass *gk20a_graph_oclass; extern struct nouveau_oclass *nvf0_graph_oclass; extern struct nouveau_oclass *nv108_graph_oclass; extern struct nouveau_oclass *gm107_graph_oclass; diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/conn.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/conn.h index a32feb3f3fb..f3930c27cb7 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/bios/conn.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/conn.h @@ -22,7 +22,25 @@ enum dcb_connector_type { DCB_CONNECTOR_NONE = 0xff }; -u16 dcb_conntab(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len); -u16 dcb_conn(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len); +struct nvbios_connT { +}; + +u32 nvbios_connTe(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len); +u32 nvbios_connTp(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_connT *info); + +struct nvbios_connE { + u8 type; + u8 location; + u8 hpd; + u8 dp; + u8 di; + u8 sr; + u8 lcdid; +}; + +u32 nvbios_connEe(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *hdr); +u32 nvbios_connEp(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *hdr, + struct nvbios_connE *info); #endif diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/dp.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/dp.h index 6e54218b55f..728206e2177 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/bios/dp.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/dp.h @@ -17,9 +17,10 @@ u16 nvbios_dpout_match(struct nouveau_bios *, u16 type, u16 mask, struct nvbios_dpout *); struct nvbios_dpcfg { - u8 drv; - u8 pre; - u8 unk; + u8 pc; + u8 dc; + u8 pe; + u8 tx_pu; }; u16 @@ -27,7 +28,7 @@ nvbios_dpcfg_parse(struct nouveau_bios *, u16 outp, u8 idx, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_dpcfg *); u16 -nvbios_dpcfg_match(struct nouveau_bios *, u16 outp, u8 un, u8 vs, u8 pe, +nvbios_dpcfg_match(struct nouveau_bios *, u16 outp, u8 pc, u8 vs, u8 pe, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_dpcfg *); diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/clock.h b/drivers/gpu/drm/nouveau/core/include/subdev/clock.h index 8f4ced75444..c01e29c9f89 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/clock.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/clock.h @@ -77,6 +77,8 @@ struct nouveau_clock { int tstate; /* thermal adjustment (max-) */ int dstate; /* display adjustment (min+) */ + bool allow_reclock; + int (*read)(struct nouveau_clock *, enum nv_clk_src); int (*calc)(struct nouveau_clock *, struct nouveau_cstate *); int (*prog)(struct nouveau_clock *); @@ -106,8 +108,8 @@ struct nouveau_clocks { int mdiv; }; -#define nouveau_clock_create(p,e,o,i,d) \ - nouveau_clock_create_((p), (e), (o), (i), sizeof(**d), (void **)d) +#define nouveau_clock_create(p,e,o,i,r,d) \ + nouveau_clock_create_((p), (e), (o), (i), (r), sizeof(**d), (void **)d) #define nouveau_clock_destroy(p) ({ \ struct nouveau_clock *clk = (p); \ _nouveau_clock_dtor(nv_object(clk)); \ @@ -121,7 +123,7 @@ struct nouveau_clocks { int nouveau_clock_create_(struct nouveau_object *, struct nouveau_object *, struct nouveau_oclass *, - struct nouveau_clocks *, int, void **); + struct nouveau_clocks *, bool, int, void **); void _nouveau_clock_dtor(struct nouveau_object *); int _nouveau_clock_init(struct nouveau_object *); #define _nouveau_clock_fini _nouveau_subdev_fini diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/fb.h b/drivers/gpu/drm/nouveau/core/include/subdev/fb.h index 58c7ccdebb0..871e73914b2 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/fb.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/fb.h @@ -105,6 +105,7 @@ extern struct nouveau_oclass *nvaa_fb_oclass; extern struct nouveau_oclass *nvaf_fb_oclass; extern struct nouveau_oclass *nvc0_fb_oclass; extern struct nouveau_oclass *nve0_fb_oclass; +extern struct nouveau_oclass *gk20a_fb_oclass; extern struct nouveau_oclass *gm107_fb_oclass; #include <subdev/bios/ramcfg.h> diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h b/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h index c85b9f1579a..612d82ab683 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h @@ -8,17 +8,18 @@ #include <subdev/bios.h> #include <subdev/bios/gpio.h> +enum nvkm_gpio_event { + NVKM_GPIO_HI = 1, + NVKM_GPIO_LO = 2, + NVKM_GPIO_TOGGLED = (NVKM_GPIO_HI | NVKM_GPIO_LO), +}; + struct nouveau_gpio { struct nouveau_subdev base; struct nouveau_event *events; - /* hardware interfaces */ void (*reset)(struct nouveau_gpio *, u8 func); - int (*drive)(struct nouveau_gpio *, int line, int dir, int out); - int (*sense)(struct nouveau_gpio *, int line); - - /* software interfaces */ int (*find)(struct nouveau_gpio *, int idx, u8 tag, u8 line, struct dcb_gpio_func *); int (*set)(struct nouveau_gpio *, int idx, u8 tag, u8 line, int state); @@ -31,23 +32,10 @@ nouveau_gpio(void *obj) return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_GPIO]; } -#define nouveau_gpio_create(p,e,o,l,d) \ - nouveau_gpio_create_((p), (e), (o), (l), sizeof(**d), (void **)d) -#define nouveau_gpio_destroy(p) ({ \ - struct nouveau_gpio *gpio = (p); \ - _nouveau_gpio_dtor(nv_object(gpio)); \ -}) -#define nouveau_gpio_fini(p,s) \ - nouveau_subdev_fini(&(p)->base, (s)) - -int nouveau_gpio_create_(struct nouveau_object *, struct nouveau_object *, - struct nouveau_oclass *, int, int, void **); -void _nouveau_gpio_dtor(struct nouveau_object *); -int nouveau_gpio_init(struct nouveau_gpio *); - -extern struct nouveau_oclass nv10_gpio_oclass; -extern struct nouveau_oclass nv50_gpio_oclass; -extern struct nouveau_oclass nvd0_gpio_oclass; -extern struct nouveau_oclass nve0_gpio_oclass; +extern struct nouveau_oclass *nv10_gpio_oclass; +extern struct nouveau_oclass *nv50_gpio_oclass; +extern struct nouveau_oclass *nv92_gpio_oclass; +extern struct nouveau_oclass *nvd0_gpio_oclass; +extern struct nouveau_oclass *nve0_gpio_oclass; #endif diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h b/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h index 7f50a858b16..825f7bb46b6 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h @@ -14,52 +14,41 @@ #define NV_I2C_TYPE_EXTDDC(e) (0x0005 | (e) << 8) #define NV_I2C_TYPE_EXTAUX(e) (0x0006 | (e) << 8) +enum nvkm_i2c_event { + NVKM_I2C_PLUG = 1, + NVKM_I2C_UNPLUG = 2, + NVKM_I2C_IRQ = 4, + NVKM_I2C_DONE = 8, + NVKM_I2C_ANY = (NVKM_I2C_PLUG | + NVKM_I2C_UNPLUG | + NVKM_I2C_IRQ | + NVKM_I2C_DONE), +}; + struct nouveau_i2c_port { struct nouveau_object base; struct i2c_adapter adapter; + struct mutex mutex; struct list_head head; u8 index; + int aux; const struct nouveau_i2c_func *func; }; struct nouveau_i2c_func { - void (*acquire)(struct nouveau_i2c_port *); - void (*release)(struct nouveau_i2c_port *); - void (*drive_scl)(struct nouveau_i2c_port *, int); void (*drive_sda)(struct nouveau_i2c_port *, int); int (*sense_scl)(struct nouveau_i2c_port *); int (*sense_sda)(struct nouveau_i2c_port *); - int (*aux)(struct nouveau_i2c_port *, u8, u32, u8 *, u8); + int (*aux)(struct nouveau_i2c_port *, bool, u8, u32, u8 *, u8); int (*pattern)(struct nouveau_i2c_port *, int pattern); int (*lnk_ctl)(struct nouveau_i2c_port *, int nr, int bw, bool enh); int (*drv_ctl)(struct nouveau_i2c_port *, int lane, int sw, int pe); }; -#define nouveau_i2c_port_create(p,e,o,i,a,f,d) \ - nouveau_i2c_port_create_((p), (e), (o), (i), (a), (f), \ - sizeof(**d), (void **)d) -#define nouveau_i2c_port_destroy(p) ({ \ - struct nouveau_i2c_port *port = (p); \ - _nouveau_i2c_port_dtor(nv_object(i2c)); \ -}) -#define nouveau_i2c_port_init(p) \ - nouveau_object_init(&(p)->base) -#define nouveau_i2c_port_fini(p,s) \ - nouveau_object_fini(&(p)->base, (s)) - -int nouveau_i2c_port_create_(struct nouveau_object *, struct nouveau_object *, - struct nouveau_oclass *, u8, - const struct i2c_algorithm *, - const struct nouveau_i2c_func *, - int, void **); -void _nouveau_i2c_port_dtor(struct nouveau_object *); -#define _nouveau_i2c_port_init nouveau_object_init -#define _nouveau_i2c_port_fini nouveau_object_fini - struct nouveau_i2c_board_info { struct i2c_board_info dev; u8 udelay; /* set to 0 to use the standard delay */ @@ -67,13 +56,20 @@ struct nouveau_i2c_board_info { struct nouveau_i2c { struct nouveau_subdev base; + struct nouveau_event *ntfy; struct nouveau_i2c_port *(*find)(struct nouveau_i2c *, u8 index); struct nouveau_i2c_port *(*find_type)(struct nouveau_i2c *, u16 type); + int (*acquire_pad)(struct nouveau_i2c_port *, unsigned long timeout); + void (*release_pad)(struct nouveau_i2c_port *); + int (*acquire)(struct nouveau_i2c_port *, unsigned long timeout); + void (*release)(struct nouveau_i2c_port *); int (*identify)(struct nouveau_i2c *, int index, const char *what, struct nouveau_i2c_board_info *, bool (*match)(struct nouveau_i2c_port *, struct i2c_board_info *, void *), void *); + + wait_queue_head_t wait; struct list_head ports; }; @@ -83,37 +79,13 @@ nouveau_i2c(void *obj) return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_I2C]; } -#define nouveau_i2c_create(p,e,o,s,d) \ - nouveau_i2c_create_((p), (e), (o), (s), sizeof(**d), (void **)d) -#define nouveau_i2c_destroy(p) ({ \ - struct nouveau_i2c *i2c = (p); \ - _nouveau_i2c_dtor(nv_object(i2c)); \ -}) -#define nouveau_i2c_init(p) ({ \ - struct nouveau_i2c *i2c = (p); \ - _nouveau_i2c_init(nv_object(i2c)); \ -}) -#define nouveau_i2c_fini(p,s) ({ \ - struct nouveau_i2c *i2c = (p); \ - _nouveau_i2c_fini(nv_object(i2c), (s)); \ -}) - -int nouveau_i2c_create_(struct nouveau_object *, struct nouveau_object *, - struct nouveau_oclass *, struct nouveau_oclass *, - int, void **); -void _nouveau_i2c_dtor(struct nouveau_object *); -int _nouveau_i2c_init(struct nouveau_object *); -int _nouveau_i2c_fini(struct nouveau_object *, bool); - -extern struct nouveau_oclass nv04_i2c_oclass; -extern struct nouveau_oclass nv4e_i2c_oclass; -extern struct nouveau_oclass nv50_i2c_oclass; -extern struct nouveau_oclass nv94_i2c_oclass; -extern struct nouveau_oclass nvd0_i2c_oclass; -extern struct nouveau_oclass nouveau_anx9805_sclass[]; - -extern const struct i2c_algorithm nouveau_i2c_bit_algo; -extern const struct i2c_algorithm nouveau_i2c_aux_algo; +extern struct nouveau_oclass *nv04_i2c_oclass; +extern struct nouveau_oclass *nv4e_i2c_oclass; +extern struct nouveau_oclass *nv50_i2c_oclass; +extern struct nouveau_oclass *nv94_i2c_oclass; +extern struct nouveau_oclass *nvd0_i2c_oclass; +extern struct nouveau_oclass *gf117_i2c_oclass; +extern struct nouveau_oclass *nve0_i2c_oclass; static inline int nv_rdi2cr(struct nouveau_i2c_port *port, u8 addr, u8 reg) diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/ibus.h b/drivers/gpu/drm/nouveau/core/include/subdev/ibus.h index 88814f159d8..31df634c0fd 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/ibus.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/ibus.h @@ -30,5 +30,6 @@ nouveau_ibus(void *obj) extern struct nouveau_oclass nvc0_ibus_oclass; extern struct nouveau_oclass nve0_ibus_oclass; +extern struct nouveau_oclass gk20a_ibus_oclass; #endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/bar/base.c b/drivers/gpu/drm/nouveau/core/subdev/bar/base.c index bdf594116f3..73b1ed20c8d 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bar/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bar/base.c @@ -118,8 +118,10 @@ nouveau_bar_create_(struct nouveau_object *parent, if (ret) return ret; - bar->iomem = ioremap(nv_device_resource_start(device, 3), - nv_device_resource_len(device, 3)); + if (nv_device_resource_len(device, 3) != 0) + bar->iomem = ioremap(nv_device_resource_start(device, 3), + nv_device_resource_len(device, 3)); + return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/bar/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/bar/nvc0.c index 3f30db62e65..ca8139b9ab2 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bar/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bar/nvc0.c @@ -30,14 +30,16 @@ #include "priv.h" +struct nvc0_bar_priv_vm { + struct nouveau_gpuobj *mem; + struct nouveau_gpuobj *pgd; + struct nouveau_vm *vm; +}; + struct nvc0_bar_priv { struct nouveau_bar base; spinlock_t lock; - struct { - struct nouveau_gpuobj *mem; - struct nouveau_gpuobj *pgd; - struct nouveau_vm *vm; - } bar[2]; + struct nvc0_bar_priv_vm bar[2]; }; static int @@ -79,87 +81,87 @@ nvc0_bar_unmap(struct nouveau_bar *bar, struct nouveau_vma *vma) } static int -nvc0_bar_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) +nvc0_bar_init_vm(struct nvc0_bar_priv *priv, struct nvc0_bar_priv_vm *bar_vm, + int bar_nr) { - struct nouveau_device *device = nv_device(parent); - struct nvc0_bar_priv *priv; - struct nouveau_gpuobj *mem; + struct nouveau_device *device = nv_device(&priv->base); struct nouveau_vm *vm; + resource_size_t bar_len; int ret; - ret = nouveau_bar_create(parent, engine, oclass, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - /* BAR3 */ ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 0, 0, - &priv->bar[0].mem); - mem = priv->bar[0].mem; + &bar_vm->mem); if (ret) return ret; ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x8000, 0, 0, - &priv->bar[0].pgd); + &bar_vm->pgd); if (ret) return ret; - ret = nouveau_vm_new(device, 0, nv_device_resource_len(device, 3), 0, &vm); + bar_len = nv_device_resource_len(device, bar_nr); + + ret = nouveau_vm_new(device, 0, bar_len, 0, &vm); if (ret) return ret; atomic_inc(&vm->engref[NVDEV_SUBDEV_BAR]); - ret = nouveau_gpuobj_new(nv_object(priv), NULL, - (nv_device_resource_len(device, 3) >> 12) * 8, - 0x1000, NVOBJ_FLAG_ZERO_ALLOC, - &vm->pgt[0].obj[0]); - vm->pgt[0].refcount[0] = 1; - if (ret) - return ret; + /* + * Bootstrap page table lookup. + */ + if (bar_nr == 3) { + ret = nouveau_gpuobj_new(nv_object(priv), NULL, + (bar_len >> 12) * 8, 0x1000, + NVOBJ_FLAG_ZERO_ALLOC, + &vm->pgt[0].obj[0]); + vm->pgt[0].refcount[0] = 1; + if (ret) + return ret; + } - ret = nouveau_vm_ref(vm, &priv->bar[0].vm, priv->bar[0].pgd); + ret = nouveau_vm_ref(vm, &bar_vm->vm, bar_vm->pgd); nouveau_vm_ref(NULL, &vm, NULL); if (ret) return ret; - nv_wo32(mem, 0x0200, lower_32_bits(priv->bar[0].pgd->addr)); - nv_wo32(mem, 0x0204, upper_32_bits(priv->bar[0].pgd->addr)); - nv_wo32(mem, 0x0208, lower_32_bits(nv_device_resource_len(device, 3) - 1)); - nv_wo32(mem, 0x020c, upper_32_bits(nv_device_resource_len(device, 3) - 1)); + nv_wo32(bar_vm->mem, 0x0200, lower_32_bits(bar_vm->pgd->addr)); + nv_wo32(bar_vm->mem, 0x0204, upper_32_bits(bar_vm->pgd->addr)); + nv_wo32(bar_vm->mem, 0x0208, lower_32_bits(bar_len - 1)); + nv_wo32(bar_vm->mem, 0x020c, upper_32_bits(bar_len - 1)); - /* BAR1 */ - ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 0, 0, - &priv->bar[1].mem); - mem = priv->bar[1].mem; - if (ret) - return ret; + return 0; +} - ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x8000, 0, 0, - &priv->bar[1].pgd); - if (ret) - return ret; +static int +nvc0_bar_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nouveau_device *device = nv_device(parent); + struct nvc0_bar_priv *priv; + bool has_bar3 = nv_device_resource_len(device, 3) != 0; + int ret; - ret = nouveau_vm_new(device, 0, nv_device_resource_len(device, 1), 0, &vm); + ret = nouveau_bar_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); if (ret) return ret; - atomic_inc(&vm->engref[NVDEV_SUBDEV_BAR]); + /* BAR3 */ + if (has_bar3) { + ret = nvc0_bar_init_vm(priv, &priv->bar[0], 3); + if (ret) + return ret; + priv->base.alloc = nouveau_bar_alloc; + priv->base.kmap = nvc0_bar_kmap; + } - ret = nouveau_vm_ref(vm, &priv->bar[1].vm, priv->bar[1].pgd); - nouveau_vm_ref(NULL, &vm, NULL); + /* BAR1 */ + ret = nvc0_bar_init_vm(priv, &priv->bar[1], 1); if (ret) return ret; - nv_wo32(mem, 0x0200, lower_32_bits(priv->bar[1].pgd->addr)); - nv_wo32(mem, 0x0204, upper_32_bits(priv->bar[1].pgd->addr)); - nv_wo32(mem, 0x0208, lower_32_bits(nv_device_resource_len(device, 1) - 1)); - nv_wo32(mem, 0x020c, upper_32_bits(nv_device_resource_len(device, 1) - 1)); - - priv->base.alloc = nouveau_bar_alloc; - priv->base.kmap = nvc0_bar_kmap; priv->base.umap = nvc0_bar_umap; priv->base.unmap = nvc0_bar_unmap; priv->base.flush = nv84_bar_flush; @@ -201,7 +203,9 @@ nvc0_bar_init(struct nouveau_object *object) nv_mask(priv, 0x100c80, 0x00000001, 0x00000000); nv_wr32(priv, 0x001704, 0x80000000 | priv->bar[1].mem->addr >> 12); - nv_wr32(priv, 0x001714, 0xc0000000 | priv->bar[0].mem->addr >> 12); + if (priv->bar[0].mem) + nv_wr32(priv, 0x001714, + 0xc0000000 | priv->bar[0].mem->addr >> 12); return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/base.c b/drivers/gpu/drm/nouveau/core/subdev/bios/base.c index fb0b6b2d142..d45704a2c2d 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/base.c @@ -168,7 +168,8 @@ nouveau_bios_shadow_prom(struct nouveau_bios *bios) */ i = 16; do { - if ((nv_rd32(bios, 0x300000) & 0xffff) == 0xaa55) + u32 data = le32_to_cpu(nv_rd32(bios, 0x300000)) & 0xffff; + if (data == 0xaa55) break; } while (i--); @@ -176,15 +177,17 @@ nouveau_bios_shadow_prom(struct nouveau_bios *bios) goto out; /* read entire bios image to system memory */ - bios->size = ((nv_rd32(bios, 0x300000) >> 16) & 0xff) * 512; + bios->size = (le32_to_cpu(nv_rd32(bios, 0x300000)) >> 16) & 0xff; + bios->size = bios->size * 512; if (!bios->size) goto out; bios->data = kmalloc(bios->size, GFP_KERNEL); - if (bios->data) { - for (i = 0; i < bios->size; i+=4) - nv_wo32(bios, i, nv_rd32(bios, 0x300000 + i)); - } + if (!bios->data) + goto out; + + for (i = 0; i < bios->size; i += 4) + ((u32 *)bios->data)[i/4] = nv_rd32(bios, 0x300000 + i); /* check the PCI record header */ pcir = nv_ro16(bios, 0x0018); diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/conn.c b/drivers/gpu/drm/nouveau/core/subdev/bios/conn.c index 5ac010efd95..2ede3bcd96a 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/conn.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/conn.c @@ -28,12 +28,12 @@ #include <subdev/bios/dcb.h> #include <subdev/bios/conn.h> -u16 -dcb_conntab(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +u32 +nvbios_connTe(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) { - u16 dcb = dcb_table(bios, ver, hdr, cnt, len); + u32 dcb = dcb_table(bios, ver, hdr, cnt, len); if (dcb && *ver >= 0x30 && *hdr >= 0x16) { - u16 data = nv_ro16(bios, dcb + 0x14); + u32 data = nv_ro16(bios, dcb + 0x14); if (data) { *ver = nv_ro08(bios, data + 0); *hdr = nv_ro08(bios, data + 1); @@ -42,15 +42,59 @@ dcb_conntab(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) return data; } } - return 0x0000; + return 0x00000000; } -u16 -dcb_conn(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len) +u32 +nvbios_connTp(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_connT *info) +{ + u32 data = nvbios_connTe(bios, ver, hdr, cnt, len); + memset(info, 0x00, sizeof(*info)); + switch (!!data * *ver) { + case 0x30: + case 0x40: + return data; + default: + break; + } + return 0x00000000; +} + +u32 +nvbios_connEe(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len) { u8 hdr, cnt; - u16 data = dcb_conntab(bios, ver, &hdr, &cnt, len); + u32 data = nvbios_connTe(bios, ver, &hdr, &cnt, len); if (data && idx < cnt) return data + hdr + (idx * *len); - return 0x0000; + return 0x00000000; +} + +u32 +nvbios_connEp(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len, + struct nvbios_connE *info) +{ + u32 data = nvbios_connEe(bios, idx, ver, len); + memset(info, 0x00, sizeof(*info)); + switch (!!data * *ver) { + case 0x30: + case 0x40: + info->type = nv_ro08(bios, data + 0x00); + info->location = nv_ro08(bios, data + 0x01) & 0x0f; + info->hpd = (nv_ro08(bios, data + 0x01) & 0x30) >> 4; + info->dp = (nv_ro08(bios, data + 0x01) & 0xc0) >> 6; + if (*len < 4) + return data; + info->hpd |= (nv_ro08(bios, data + 0x02) & 0x03) << 2; + info->dp |= nv_ro08(bios, data + 0x02) & 0x0c; + info->di = (nv_ro08(bios, data + 0x02) & 0xf0) >> 4; + info->hpd |= (nv_ro08(bios, data + 0x03) & 0x07) << 4; + info->sr = (nv_ro08(bios, data + 0x03) & 0x08) >> 3; + info->lcdid = (nv_ro08(bios, data + 0x03) & 0x70) >> 4; + return data; + default: + break; + } + return 0x00000000; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/dp.c b/drivers/gpu/drm/nouveau/core/subdev/bios/dp.c index 7628fe75922..f309dd65725 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/dp.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/dp.c @@ -162,18 +162,20 @@ nvbios_dpcfg_parse(struct nouveau_bios *bios, u16 outp, u8 idx, struct nvbios_dpcfg *info) { u16 data = nvbios_dpcfg_entry(bios, outp, idx, ver, hdr, cnt, len); + memset(info, 0x00, sizeof(*info)); if (data) { switch (*ver) { case 0x21: - info->drv = nv_ro08(bios, data + 0x02); - info->pre = nv_ro08(bios, data + 0x03); - info->unk = nv_ro08(bios, data + 0x04); + info->dc = nv_ro08(bios, data + 0x02); + info->pe = nv_ro08(bios, data + 0x03); + info->tx_pu = nv_ro08(bios, data + 0x04); break; case 0x30: case 0x40: - info->drv = nv_ro08(bios, data + 0x01); - info->pre = nv_ro08(bios, data + 0x02); - info->unk = nv_ro08(bios, data + 0x03); + info->pc = nv_ro08(bios, data + 0x00); + info->dc = nv_ro08(bios, data + 0x01); + info->pe = nv_ro08(bios, data + 0x02); + info->tx_pu = nv_ro08(bios, data + 0x03); break; default: data = 0x0000; @@ -184,7 +186,7 @@ nvbios_dpcfg_parse(struct nouveau_bios *bios, u16 outp, u8 idx, } u16 -nvbios_dpcfg_match(struct nouveau_bios *bios, u16 outp, u8 un, u8 vs, u8 pe, +nvbios_dpcfg_match(struct nouveau_bios *bios, u16 outp, u8 pc, u8 vs, u8 pe, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_dpcfg *info) { @@ -193,16 +195,15 @@ nvbios_dpcfg_match(struct nouveau_bios *bios, u16 outp, u8 un, u8 vs, u8 pe, if (*ver >= 0x30) { const u8 vsoff[] = { 0, 4, 7, 9 }; - idx = (un * 10) + vsoff[vs] + pe; + idx = (pc * 10) + vsoff[vs] + pe; } else { - while ((data = nvbios_dpcfg_entry(bios, outp, idx, + while ((data = nvbios_dpcfg_entry(bios, outp, ++idx, ver, hdr, cnt, len))) { if (nv_ro08(bios, data + 0x00) == vs && nv_ro08(bios, data + 0x01) == pe) break; - idx++; } } - return nvbios_dpcfg_parse(bios, outp, pe, ver, hdr, cnt, len, info); + return nvbios_dpcfg_parse(bios, outp, idx, ver, hdr, cnt, len, info); } diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c index acaeaf79e3f..626380f9e4c 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c @@ -98,15 +98,16 @@ static u8 init_conn(struct nvbios_init *init) { struct nouveau_bios *bios = init->bios; - u8 ver, len; - u16 conn; + struct nvbios_connE connE; + u8 ver, hdr; + u32 conn; if (init_exec(init)) { if (init->outp) { conn = init->outp->connector; - conn = dcb_conn(bios, conn, &ver, &len); + conn = nvbios_connEp(bios, conn, &ver, &hdr, &connE); if (conn) - return nv_ro08(bios, conn); + return connE.type; } error("script needs connector type\n"); diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/base.c b/drivers/gpu/drm/nouveau/core/subdev/clock/base.c index dd62baead39..22351f594d2 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/base.c @@ -346,8 +346,8 @@ nouveau_clock_ustate_update(struct nouveau_clock *clk, int req) struct nouveau_pstate *pstate; int i = 0; - /* YKW repellant */ - return -ENOSYS; + if (!clk->allow_reclock) + return -ENOSYS; if (req != -1 && req != -2) { list_for_each_entry(pstate, &clk->states, head) { @@ -456,6 +456,7 @@ nouveau_clock_create_(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, struct nouveau_clocks *clocks, + bool allow_reclock, int length, void **object) { struct nouveau_device *device = nv_device(parent); @@ -478,6 +479,8 @@ nouveau_clock_create_(struct nouveau_object *parent, ret = nouveau_pstate_new(clk, idx++); } while (ret == 0); + clk->allow_reclock = allow_reclock; + mode = nouveau_stropt(device->cfgopt, "NvClkMode", &arglen); if (mode) { if (!strncasecmpz(mode, "disabled", arglen)) { diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c index b74db6cfc4e..eb2d4425a49 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c @@ -82,7 +82,8 @@ nv04_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv04_clock_priv *priv; int ret; - ret = nouveau_clock_create(parent, engine, oclass, nv04_domain, &priv); + ret = nouveau_clock_create(parent, engine, oclass, nv04_domain, false, + &priv); *pobject = nv_object(priv); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c index db7346f7908..8a9e1683979 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c @@ -213,7 +213,8 @@ nv40_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv40_clock_priv *priv; int ret; - ret = nouveau_clock_create(parent, engine, oclass, nv40_domain, &priv); + ret = nouveau_clock_create(parent, engine, oclass, nv40_domain, true, + &priv); *pobject = nv_object(priv); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c index 250a6d96016..8c132772ba9 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c @@ -507,7 +507,7 @@ nv50_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine, int ret; ret = nouveau_clock_create(parent, engine, oclass, pclass->domains, - &priv); + false, &priv); *pobject = nv_object(priv); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c index 4f5a1373f00..9fb58354a80 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c @@ -302,7 +302,8 @@ nva3_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nva3_clock_priv *priv; int ret; - ret = nouveau_clock_create(parent, engine, oclass, nva3_domain, &priv); + ret = nouveau_clock_create(parent, engine, oclass, nva3_domain, false, + &priv); *pobject = nv_object(priv); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nvaa.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nvaa.c index 7a723b4f564..6a65fc9e966 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/nvaa.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nvaa.c @@ -421,7 +421,8 @@ nvaa_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nvaa_clock_priv *priv; int ret; - ret = nouveau_clock_create(parent, engine, oclass, nvaa_domains, &priv); + ret = nouveau_clock_create(parent, engine, oclass, nvaa_domains, true, + &priv); *pobject = nv_object(priv); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c index c3105720ed2..dbf8517f54d 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c @@ -437,7 +437,8 @@ nvc0_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nvc0_clock_priv *priv; int ret; - ret = nouveau_clock_create(parent, engine, oclass, nvc0_domain, &priv); + ret = nouveau_clock_create(parent, engine, oclass, nvc0_domain, false, + &priv); *pobject = nv_object(priv); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nve0.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nve0.c index d3c37c96f0e..0e62a324014 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/nve0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nve0.c @@ -307,7 +307,6 @@ calc_clk(struct nve0_clock_priv *priv, info->dsrc = src0; if (div0) { info->ddiv |= 0x80000000; - info->ddiv |= div0 << 8; info->ddiv |= div0; } if (div1D) { @@ -352,7 +351,7 @@ nve0_clock_prog_0(struct nve0_clock_priv *priv, int clk) { struct nve0_clock_info *info = &priv->eng[clk]; if (!info->ssel) { - nv_mask(priv, 0x1371d0 + (clk * 0x04), 0x80003f3f, info->ddiv); + nv_mask(priv, 0x1371d0 + (clk * 0x04), 0x8000003f, info->ddiv); nv_wr32(priv, 0x137160 + (clk * 0x04), info->dsrc); } } @@ -389,7 +388,10 @@ static void nve0_clock_prog_3(struct nve0_clock_priv *priv, int clk) { struct nve0_clock_info *info = &priv->eng[clk]; - nv_mask(priv, 0x137250 + (clk * 0x04), 0x00003f3f, info->mdiv); + if (info->ssel) + nv_mask(priv, 0x137250 + (clk * 0x04), 0x00003f00, info->mdiv); + else + nv_mask(priv, 0x137250 + (clk * 0x04), 0x0000003f, info->mdiv); } static void @@ -473,7 +475,8 @@ nve0_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nve0_clock_priv *priv; int ret; - ret = nouveau_clock_create(parent, engine, oclass, nve0_domain, &priv); + ret = nouveau_clock_create(parent, engine, oclass, nve0_domain, true, + &priv); *pobject = nv_object(priv); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/gk20a.c b/drivers/gpu/drm/nouveau/core/subdev/fb/gk20a.c new file mode 100644 index 00000000000..a16024a7477 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/gk20a.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "nvc0.h" + +struct gk20a_fb_priv { + struct nouveau_fb base; +}; + +static int +gk20a_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct gk20a_fb_priv *priv; + int ret; + + ret = nouveau_fb_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + return 0; +} + +struct nouveau_oclass * +gk20a_fb_oclass = &(struct nouveau_fb_impl) { + .base.handle = NV_SUBDEV(FB, 0xea), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = gk20a_fb_ctor, + .dtor = _nouveau_fb_dtor, + .init = _nouveau_fb_init, + .fini = _nouveau_fb_fini, + }, + .memtype = nvc0_fb_memtype_valid, + .ram = &gk20a_ram_oclass, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h b/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h index da74c889aed..82273f832e4 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h @@ -32,6 +32,7 @@ extern struct nouveau_oclass nva3_ram_oclass; extern struct nouveau_oclass nvaa_ram_oclass; extern struct nouveau_oclass nvc0_ram_oclass; extern struct nouveau_oclass nve0_ram_oclass; +extern struct nouveau_oclass gk20a_ram_oclass; extern struct nouveau_oclass gm107_ram_oclass; int nouveau_sddr3_calc(struct nouveau_ram *ram); diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramfuc.h b/drivers/gpu/drm/nouveau/core/subdev/fb/ramfuc.h index 0f57fcfe0bb..2af9cfd2c60 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramfuc.h +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramfuc.h @@ -26,7 +26,7 @@ ramfuc_reg2(u32 addr1, u32 addr2) }; } -static inline struct ramfuc_reg +static noinline struct ramfuc_reg ramfuc_reg(u32 addr) { return ramfuc_reg2(addr, addr); @@ -107,7 +107,7 @@ ramfuc_nsec(struct ramfuc *ram, u32 nsec) #define ram_init(s,p) ramfuc_init(&(s)->base, (p)) #define ram_exec(s,e) ramfuc_exec(&(s)->base, (e)) -#define ram_have(s,r) ((s)->r_##r.addr != 0x000000) +#define ram_have(s,r) ((s)->r_##r.addr[0] != 0x000000) #define ram_rd32(s,r) ramfuc_rd32(&(s)->base, &(s)->r_##r) #define ram_wr32(s,r,d) ramfuc_wr32(&(s)->base, &(s)->r_##r, (d)) #define ram_nuke(s,r) ramfuc_nuke(&(s)->base, &(s)->r_##r) diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramgk20a.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramgk20a.c new file mode 100644 index 00000000000..4d77d75e467 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramgk20a.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "priv.h" + +#include <subdev/fb.h> + +struct gk20a_mem { + struct nouveau_mem base; + void *cpuaddr; + dma_addr_t handle; +}; +#define to_gk20a_mem(m) container_of(m, struct gk20a_mem, base) + +static void +gk20a_ram_put(struct nouveau_fb *pfb, struct nouveau_mem **pmem) +{ + struct device *dev = nv_device_base(nv_device(pfb)); + struct gk20a_mem *mem = to_gk20a_mem(*pmem); + + *pmem = NULL; + if (unlikely(mem == NULL)) + return; + + if (likely(mem->cpuaddr)) + dma_free_coherent(dev, mem->base.size << PAGE_SHIFT, + mem->cpuaddr, mem->handle); + + kfree(mem->base.pages); + kfree(mem); +} + +static int +gk20a_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin, + u32 memtype, struct nouveau_mem **pmem) +{ + struct device *dev = nv_device_base(nv_device(pfb)); + struct gk20a_mem *mem; + u32 type = memtype & 0xff; + u32 npages, order; + int i; + + nv_debug(pfb, "%s: size: %llx align: %x, ncmin: %x\n", __func__, size, + align, ncmin); + + npages = size >> PAGE_SHIFT; + if (npages == 0) + npages = 1; + + if (align == 0) + align = PAGE_SIZE; + align >>= PAGE_SHIFT; + + /* round alignment to the next power of 2, if needed */ + order = fls(align); + if ((align & (align - 1)) == 0) + order--; + align = BIT(order); + + /* ensure returned address is correctly aligned */ + npages = max(align, npages); + + mem = kzalloc(sizeof(*mem), GFP_KERNEL); + if (!mem) + return -ENOMEM; + + mem->base.size = npages; + mem->base.memtype = type; + + mem->base.pages = kzalloc(sizeof(dma_addr_t) * npages, GFP_KERNEL); + if (!mem->base.pages) { + kfree(mem); + return -ENOMEM; + } + + *pmem = &mem->base; + + mem->cpuaddr = dma_alloc_coherent(dev, npages << PAGE_SHIFT, + &mem->handle, GFP_KERNEL); + if (!mem->cpuaddr) { + nv_error(pfb, "%s: cannot allocate memory!\n", __func__); + gk20a_ram_put(pfb, pmem); + return -ENOMEM; + } + + align <<= PAGE_SHIFT; + + /* alignment check */ + if (unlikely(mem->handle & (align - 1))) + nv_warn(pfb, "memory not aligned as requested: %pad (0x%x)\n", + &mem->handle, align); + + nv_debug(pfb, "alloc size: 0x%x, align: 0x%x, paddr: %pad, vaddr: %p\n", + npages << PAGE_SHIFT, align, &mem->handle, mem->cpuaddr); + + for (i = 0; i < npages; i++) + mem->base.pages[i] = mem->handle + (PAGE_SIZE * i); + + mem->base.offset = (u64)mem->base.pages[0]; + + return 0; +} + +static int +gk20a_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 datasize, + struct nouveau_object **pobject) +{ + struct nouveau_ram *ram; + int ret; + + ret = nouveau_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + ram->type = NV_MEM_TYPE_STOLEN; + ram->size = get_num_physpages() << PAGE_SHIFT; + + ram->get = gk20a_ram_get; + ram->put = gk20a_ram_put; + + return 0; +} + +struct nouveau_oclass +gk20a_ram_oclass = { + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = gk20a_ram_ctor, + .dtor = _nouveau_ram_dtor, + .init = _nouveau_ram_init, + .fini = _nouveau_ram_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c index ef91b6e893af..e5d12c24cc4 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c @@ -211,7 +211,7 @@ nv50_ram_prog(struct nouveau_fb *pfb) struct nv50_ram *ram = (void *)pfb->ram; struct nv50_ramseq *hwsq = &ram->hwsq; - ram_exec(hwsq, nouveau_boolopt(device->cfgopt, "NvMemExec", false)); + ram_exec(hwsq, nouveau_boolopt(device->cfgopt, "NvMemExec", true)); return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c index 6eb97f16fbd..8076fb195dd 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c @@ -309,7 +309,7 @@ nva3_ram_prog(struct nouveau_fb *pfb) struct nouveau_device *device = nv_device(pfb); struct nva3_ram *ram = (void *)pfb->ram; struct nva3_ramfuc *fuc = &ram->fuc; - ram_exec(fuc, nouveau_boolopt(device->cfgopt, "NvMemExec", false)); + ram_exec(fuc, nouveau_boolopt(device->cfgopt, "NvMemExec", true)); return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c index 8edc92224c8..5a6a5027f74 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c @@ -408,7 +408,7 @@ nvc0_ram_prog(struct nouveau_fb *pfb) struct nouveau_device *device = nv_device(pfb); struct nvc0_ram *ram = (void *)pfb->ram; struct nvc0_ramfuc *fuc = &ram->fuc; - ram_exec(fuc, nouveau_boolopt(device->cfgopt, "NvMemExec", false)); + ram_exec(fuc, nouveau_boolopt(device->cfgopt, "NvMemExec", true)); return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c index 16752192cf8..c5b46e30231 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c @@ -200,6 +200,7 @@ r1373f4_init(struct nve0_ramfuc *fuc) /* (re)program mempll, if required */ if (ram->mode == 2) { ram_mask(fuc, 0x1373f4, 0x00010000, 0x00000000); + ram_mask(fuc, 0x132000, 0x80000000, 0x80000000); ram_mask(fuc, 0x132000, 0x00000001, 0x00000000); ram_mask(fuc, 0x132004, 0x103fffff, mcoef); ram_mask(fuc, 0x132000, 0x00000001, 0x00000001); @@ -262,8 +263,8 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq) struct nve0_ram *ram = (void *)pfb->ram; struct nve0_ramfuc *fuc = &ram->fuc; struct nouveau_ram_data *next = ram->base.next; - int vc = !(next->bios.ramcfg_11_02_08); - int mv = !(next->bios.ramcfg_11_02_04); + int vc = !next->bios.ramcfg_11_02_08; + int mv = !next->bios.ramcfg_11_02_04; u32 mask, data; ram_mask(fuc, 0x10f808, 0x40000000, 0x40000000); @@ -370,8 +371,8 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq) } } - if ( (next->bios.ramcfg_11_02_40) || - (next->bios.ramcfg_11_07_10)) { + if (next->bios.ramcfg_11_02_40 || + next->bios.ramcfg_11_07_10) { ram_mask(fuc, 0x132040, 0x00010000, 0x00010000); ram_nsec(fuc, 20000); } @@ -417,7 +418,7 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq) ram_mask(fuc, 0x10f694, 0xff00ff00, data); } - if (ram->mode == 2 && (next->bios.ramcfg_11_08_10)) + if (ram->mode == 2 && next->bios.ramcfg_11_08_10) data = 0x00000080; else data = 0x00000000; @@ -425,13 +426,13 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq) mask = 0x00070000; data = 0x00000000; - if (!(next->bios.ramcfg_11_02_80)) + if (!next->bios.ramcfg_11_02_80) data |= 0x03000000; - if (!(next->bios.ramcfg_11_02_40)) + if (!next->bios.ramcfg_11_02_40) data |= 0x00002000; - if (!(next->bios.ramcfg_11_07_10)) + if (!next->bios.ramcfg_11_07_10) data |= 0x00004000; - if (!(next->bios.ramcfg_11_07_08)) + if (!next->bios.ramcfg_11_07_08) data |= 0x00000003; else data |= 0x74000000; @@ -486,7 +487,7 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq) data = mask = 0x00000000; if (NOTE00(ramcfg_02_03 != 0)) { - data |= (next->bios.ramcfg_11_02_03) << 8; + data |= next->bios.ramcfg_11_02_03 << 8; mask |= 0x00000300; } if (NOTE00(ramcfg_01_10)) { @@ -498,7 +499,7 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq) data = mask = 0x00000000; if (NOTE00(timing_30_07 != 0)) { - data |= (next->bios.timing_20_30_07) << 28; + data |= next->bios.timing_20_30_07 << 28; mask |= 0x70000000; } if (NOTE00(ramcfg_01_01)) { @@ -510,7 +511,7 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq) data = mask = 0x00000000; if (NOTE00(timing_30_07 != 0)) { - data |= (next->bios.timing_20_30_07) << 28; + data |= next->bios.timing_20_30_07 << 28; mask |= 0x70000000; } if (NOTE00(ramcfg_01_02)) { @@ -522,16 +523,16 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq) mask = 0x33f00000; data = 0x00000000; - if (!(next->bios.ramcfg_11_01_04)) + if (!next->bios.ramcfg_11_01_04) data |= 0x20200000; - if (!(next->bios.ramcfg_11_07_80)) + if (!next->bios.ramcfg_11_07_80) data |= 0x12800000; /*XXX: see note above about there probably being some condition * for the 10f824 stuff that uses ramcfg 3... */ - if ( (next->bios.ramcfg_11_03_f0)) { + if (next->bios.ramcfg_11_03_f0) { if (next->bios.rammap_11_08_0c) { - if (!(next->bios.ramcfg_11_07_80)) + if (!next->bios.ramcfg_11_07_80) mask |= 0x00000020; else data |= 0x00000020; @@ -563,7 +564,7 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq) ram_wait(fuc, 0x100710, 0x80000000, 0x80000000, 200000); } - data = (next->bios.timing_20_30_07) << 8; + data = next->bios.timing_20_30_07 << 8; if (next->bios.ramcfg_11_01_01) data |= 0x80000000; ram_mask(fuc, 0x100778, 0x00000700, data); @@ -588,7 +589,7 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq) ram_wr32(fuc, 0x10f310, 0x00000001); /* REFRESH */ ram_wr32(fuc, 0x10f210, 0x80000000); /* REFRESH_AUTO = 1 */ - if ((next->bios.ramcfg_11_08_10) && (ram->mode == 2) /*XXX*/) { + if (next->bios.ramcfg_11_08_10 && (ram->mode == 2) /*XXX*/) { u32 temp = ram_mask(fuc, 0x10f294, 0xff000000, 0x24000000); nve0_ram_train(fuc, 0xbc0e0000, 0xa4010000); /*XXX*/ ram_nsec(fuc, 1000); @@ -621,8 +622,8 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq) data = ram_rd32(fuc, 0x10f978); data &= ~0x00046144; data |= 0x0000000b; - if (!(next->bios.ramcfg_11_07_08)) { - if (!(next->bios.ramcfg_11_07_04)) + if (!next->bios.ramcfg_11_07_08) { + if (!next->bios.ramcfg_11_07_04) data |= 0x0000200c; else data |= 0x00000000; @@ -636,11 +637,11 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq) ram_wr32(fuc, 0x10f830, data); } - if (!(next->bios.ramcfg_11_07_08)) { + if (!next->bios.ramcfg_11_07_08) { data = 0x88020000; - if ( (next->bios.ramcfg_11_07_04)) + if ( next->bios.ramcfg_11_07_04) data |= 0x10000000; - if (!(next->bios.rammap_11_08_10)) + if (!next->bios.rammap_11_08_10) data |= 0x00080000; } else { data = 0xa40e0000; @@ -689,8 +690,8 @@ nve0_ram_calc_sddr3(struct nouveau_fb *pfb, u32 freq) const u32 runk0 = ram->fN1 << 16; const u32 runk1 = ram->fN1; struct nouveau_ram_data *next = ram->base.next; - int vc = !(next->bios.ramcfg_11_02_08); - int mv = !(next->bios.ramcfg_11_02_04); + int vc = !next->bios.ramcfg_11_02_08; + int mv = !next->bios.ramcfg_11_02_04; u32 mask, data; ram_mask(fuc, 0x10f808, 0x40000000, 0x40000000); @@ -705,7 +706,7 @@ nve0_ram_calc_sddr3(struct nouveau_fb *pfb, u32 freq) } ram_mask(fuc, 0x10f200, 0x00000800, 0x00000000); - if ((next->bios.ramcfg_11_03_f0)) + if (next->bios.ramcfg_11_03_f0) ram_mask(fuc, 0x10f808, 0x04000000, 0x04000000); ram_wr32(fuc, 0x10f314, 0x00000001); /* PRECHARGE */ @@ -761,7 +762,7 @@ nve0_ram_calc_sddr3(struct nouveau_fb *pfb, u32 freq) ram_mask(fuc, 0x1373f4, 0x00000000, 0x00010010); data = ram_rd32(fuc, 0x1373ec) & ~0x00030000; - data |= (next->bios.ramcfg_11_03_30) << 12; + data |= next->bios.ramcfg_11_03_30 << 16; ram_wr32(fuc, 0x1373ec, data); ram_mask(fuc, 0x1373f4, 0x00000003, 0x00000000); ram_mask(fuc, 0x1373f4, 0x00000010, 0x00000000); @@ -793,8 +794,8 @@ nve0_ram_calc_sddr3(struct nouveau_fb *pfb, u32 freq) } } - if ( (next->bios.ramcfg_11_02_40) || - (next->bios.ramcfg_11_07_10)) { + if (next->bios.ramcfg_11_02_40 || + next->bios.ramcfg_11_07_10) { ram_mask(fuc, 0x132040, 0x00010000, 0x00010000); ram_nsec(fuc, 20000); } @@ -810,13 +811,13 @@ nve0_ram_calc_sddr3(struct nouveau_fb *pfb, u32 freq) mask = 0x00010000; data = 0x00000000; - if (!(next->bios.ramcfg_11_02_80)) + if (!next->bios.ramcfg_11_02_80) data |= 0x03000000; - if (!(next->bios.ramcfg_11_02_40)) + if (!next->bios.ramcfg_11_02_40) data |= 0x00002000; - if (!(next->bios.ramcfg_11_07_10)) + if (!next->bios.ramcfg_11_07_10) data |= 0x00004000; - if (!(next->bios.ramcfg_11_07_08)) + if (!next->bios.ramcfg_11_07_08) data |= 0x00000003; else data |= 0x14000000; @@ -844,16 +845,16 @@ nve0_ram_calc_sddr3(struct nouveau_fb *pfb, u32 freq) mask = 0x33f00000; data = 0x00000000; - if (!(next->bios.ramcfg_11_01_04)) + if (!next->bios.ramcfg_11_01_04) data |= 0x20200000; - if (!(next->bios.ramcfg_11_07_80)) + if (!next->bios.ramcfg_11_07_80) data |= 0x12800000; /*XXX: see note above about there probably being some condition * for the 10f824 stuff that uses ramcfg 3... */ - if ( (next->bios.ramcfg_11_03_f0)) { + if (next->bios.ramcfg_11_03_f0) { if (next->bios.rammap_11_08_0c) { - if (!(next->bios.ramcfg_11_07_80)) + if (!next->bios.ramcfg_11_07_80) mask |= 0x00000020; else data |= 0x00000020; @@ -876,7 +877,7 @@ nve0_ram_calc_sddr3(struct nouveau_fb *pfb, u32 freq) data = next->bios.timing_20_2c_1fc0; ram_mask(fuc, 0x10f24c, 0x7f000000, data << 24); - ram_mask(fuc, 0x10f224, 0x001f0000, next->bios.timing_20_30_f8); + ram_mask(fuc, 0x10f224, 0x001f0000, next->bios.timing_20_30_f8 << 16); ram_wr32(fuc, 0x10f090, 0x4000007f); ram_nsec(fuc, 1000); @@ -1111,7 +1112,7 @@ nve0_ram_prog(struct nouveau_fb *pfb) struct nouveau_device *device = nv_device(pfb); struct nve0_ram *ram = (void *)pfb->ram; struct nve0_ramfuc *fuc = &ram->fuc; - ram_exec(fuc, nouveau_boolopt(device->cfgopt, "NvMemExec", false)); + ram_exec(fuc, nouveau_boolopt(device->cfgopt, "NvMemExec", true)); return (ram->base.next == &ram->base.xition); } diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c index f572c2804c3..45e0202f315 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c @@ -22,21 +22,24 @@ * Authors: Ben Skeggs */ -#include <subdev/gpio.h> #include <subdev/bios.h> #include <subdev/bios/gpio.h> +#include "priv.h" + static int nouveau_gpio_drive(struct nouveau_gpio *gpio, int idx, int line, int dir, int out) { - return gpio->drive ? gpio->drive(gpio, line, dir, out) : -ENODEV; + const struct nouveau_gpio_impl *impl = (void *)nv_object(gpio)->oclass; + return impl->drive ? impl->drive(gpio, line, dir, out) : -ENODEV; } static int nouveau_gpio_sense(struct nouveau_gpio *gpio, int idx, int line) { - return gpio->sense ? gpio->sense(gpio, line) : -ENODEV; + const struct nouveau_gpio_impl *impl = (void *)nv_object(gpio)->oclass; + return impl->sense ? impl->sense(gpio, line) : -ENODEV; } static int @@ -102,6 +105,80 @@ nouveau_gpio_get(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line) return ret; } +static void +nouveau_gpio_intr_disable(struct nouveau_event *event, int type, int index) +{ + struct nouveau_gpio *gpio = nouveau_gpio(event->priv); + const struct nouveau_gpio_impl *impl = (void *)nv_object(gpio)->oclass; + impl->intr_mask(gpio, type, 1 << index, 0); +} + +static void +nouveau_gpio_intr_enable(struct nouveau_event *event, int type, int index) +{ + struct nouveau_gpio *gpio = nouveau_gpio(event->priv); + const struct nouveau_gpio_impl *impl = (void *)nv_object(gpio)->oclass; + impl->intr_mask(gpio, type, 1 << index, 1 << index); +} + +static void +nouveau_gpio_intr(struct nouveau_subdev *subdev) +{ + struct nouveau_gpio *gpio = nouveau_gpio(subdev); + const struct nouveau_gpio_impl *impl = (void *)nv_object(gpio)->oclass; + u32 hi, lo, e, i; + + impl->intr_stat(gpio, &hi, &lo); + + for (i = 0; e = 0, (hi | lo) && i < impl->lines; i++) { + if (hi & (1 << i)) + e |= NVKM_GPIO_HI; + if (lo & (1 << i)) + e |= NVKM_GPIO_LO; + nouveau_event_trigger(gpio->events, e, i); + } +} + +int +_nouveau_gpio_fini(struct nouveau_object *object, bool suspend) +{ + const struct nouveau_gpio_impl *impl = (void *)object->oclass; + struct nouveau_gpio *gpio = nouveau_gpio(object); + u32 mask = (1 << impl->lines) - 1; + + impl->intr_mask(gpio, NVKM_GPIO_TOGGLED, mask, 0); + impl->intr_stat(gpio, &mask, &mask); + + return nouveau_subdev_fini(&gpio->base, suspend); +} + +static struct dmi_system_id gpio_reset_ids[] = { + { + .ident = "Apple Macbook 10,1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro10,1"), + } + }, + { } +}; + +int +_nouveau_gpio_init(struct nouveau_object *object) +{ + struct nouveau_gpio *gpio = nouveau_gpio(object); + int ret; + + ret = nouveau_subdev_init(&gpio->base); + if (ret) + return ret; + + if (gpio->reset && dmi_check_system(gpio_reset_ids)) + gpio->reset(gpio, DCB_GPIO_UNUSED); + + return ret; +} + void _nouveau_gpio_dtor(struct nouveau_object *object) { @@ -113,9 +190,10 @@ _nouveau_gpio_dtor(struct nouveau_object *object) int nouveau_gpio_create_(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, int lines, + struct nouveau_oclass *oclass, int length, void **pobject) { + const struct nouveau_gpio_impl *impl = (void *)oclass; struct nouveau_gpio *gpio; int ret; @@ -125,34 +203,34 @@ nouveau_gpio_create_(struct nouveau_object *parent, if (ret) return ret; - ret = nouveau_event_create(lines, &gpio->events); - if (ret) - return ret; - gpio->find = nouveau_gpio_find; gpio->set = nouveau_gpio_set; gpio->get = nouveau_gpio_get; + gpio->reset = impl->reset; + + ret = nouveau_event_create(2, impl->lines, &gpio->events); + if (ret) + return ret; + + gpio->events->priv = gpio; + gpio->events->enable = nouveau_gpio_intr_enable; + gpio->events->disable = nouveau_gpio_intr_disable; + nv_subdev(gpio)->intr = nouveau_gpio_intr; return 0; } -static struct dmi_system_id gpio_reset_ids[] = { - { - .ident = "Apple Macbook 10,1", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro10,1"), - } - }, - { } -}; - int -nouveau_gpio_init(struct nouveau_gpio *gpio) +_nouveau_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) { - int ret = nouveau_subdev_init(&gpio->base); - if (ret == 0 && gpio->reset) { - if (dmi_check_system(gpio_reset_ids)) - gpio->reset(gpio, DCB_GPIO_UNUSED); - } - return ret; + struct nouveau_gpio *gpio; + int ret; + + ret = nouveau_gpio_create(parent, engine, oclass, &gpio); + *pobject = nv_object(gpio); + if (ret) + return ret; + + return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv10.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv10.c index 76d5d5465dd..27ad23eaf18 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv10.c +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv10.c @@ -26,10 +26,6 @@ #include "priv.h" -struct nv10_gpio_priv { - struct nouveau_gpio base; -}; - static int nv10_gpio_sense(struct nouveau_gpio *gpio, int line) { @@ -83,95 +79,38 @@ nv10_gpio_drive(struct nouveau_gpio *gpio, int line, int dir, int out) } static void -nv10_gpio_intr(struct nouveau_subdev *subdev) -{ - struct nv10_gpio_priv *priv = (void *)subdev; - u32 intr = nv_rd32(priv, 0x001104); - u32 hi = (intr & 0x0000ffff) >> 0; - u32 lo = (intr & 0xffff0000) >> 16; - int i; - - for (i = 0; (hi | lo) && i < 32; i++) { - if ((hi | lo) & (1 << i)) - nouveau_event_trigger(priv->base.events, i); - } - - nv_wr32(priv, 0x001104, intr); -} - -static void -nv10_gpio_intr_enable(struct nouveau_event *event, int line) -{ - nv_wr32(event->priv, 0x001104, 0x00010001 << line); - nv_mask(event->priv, 0x001144, 0x00010001 << line, 0x00010001 << line); -} - -static void -nv10_gpio_intr_disable(struct nouveau_event *event, int line) -{ - nv_wr32(event->priv, 0x001104, 0x00010001 << line); - nv_mask(event->priv, 0x001144, 0x00010001 << line, 0x00000000); -} - -static int -nv10_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) +nv10_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo) { - struct nv10_gpio_priv *priv; - int ret; - - ret = nouveau_gpio_create(parent, engine, oclass, 16, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - priv->base.drive = nv10_gpio_drive; - priv->base.sense = nv10_gpio_sense; - priv->base.events->priv = priv; - priv->base.events->enable = nv10_gpio_intr_enable; - priv->base.events->disable = nv10_gpio_intr_disable; - nv_subdev(priv)->intr = nv10_gpio_intr; - return 0; + u32 intr = nv_rd32(gpio, 0x001104); + u32 stat = nv_rd32(gpio, 0x001144) & intr; + *lo = (stat & 0xffff0000) >> 16; + *hi = (stat & 0x0000ffff); + nv_wr32(gpio, 0x001104, intr); } static void -nv10_gpio_dtor(struct nouveau_object *object) -{ - struct nv10_gpio_priv *priv = (void *)object; - nouveau_gpio_destroy(&priv->base); -} - -static int -nv10_gpio_init(struct nouveau_object *object) -{ - struct nv10_gpio_priv *priv = (void *)object; - int ret; - - ret = nouveau_gpio_init(&priv->base); - if (ret) - return ret; - - nv_wr32(priv, 0x001144, 0x00000000); - nv_wr32(priv, 0x001104, 0xffffffff); - return 0; -} - -static int -nv10_gpio_fini(struct nouveau_object *object, bool suspend) +nv10_gpio_intr_mask(struct nouveau_gpio *gpio, u32 type, u32 mask, u32 data) { - struct nv10_gpio_priv *priv = (void *)object; - nv_wr32(priv, 0x001144, 0x00000000); - return nouveau_gpio_fini(&priv->base, suspend); + u32 inte = nv_rd32(gpio, 0x001144); + if (type & NVKM_GPIO_LO) + inte = (inte & ~(mask << 16)) | (data << 16); + if (type & NVKM_GPIO_HI) + inte = (inte & ~mask) | data; + nv_wr32(gpio, 0x001144, inte); } -struct nouveau_oclass -nv10_gpio_oclass = { - .handle = NV_SUBDEV(GPIO, 0x10), - .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nv10_gpio_ctor, - .dtor = nv10_gpio_dtor, - .init = nv10_gpio_init, - .fini = nv10_gpio_fini, +struct nouveau_oclass * +nv10_gpio_oclass = &(struct nouveau_gpio_impl) { + .base.handle = NV_SUBDEV(GPIO, 0x10), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_gpio_ctor, + .dtor = _nouveau_gpio_dtor, + .init = _nouveau_gpio_init, + .fini = _nouveau_gpio_fini, }, -}; + .lines = 16, + .intr_stat = nv10_gpio_intr_stat, + .intr_mask = nv10_gpio_intr_mask, + .drive = nv10_gpio_drive, + .sense = nv10_gpio_sense, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c index 2ef77473162..1864fa98e6b 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c @@ -24,15 +24,10 @@ #include "priv.h" -struct nv50_gpio_priv { - struct nouveau_gpio base; -}; - -static void +void nv50_gpio_reset(struct nouveau_gpio *gpio, u8 match) { struct nouveau_bios *bios = nouveau_bios(gpio); - struct nv50_gpio_priv *priv = (void *)gpio; u8 ver, len; u16 entry; int ent = -1; @@ -55,7 +50,7 @@ nv50_gpio_reset(struct nouveau_gpio *gpio, u8 match) gpio->set(gpio, 0, func, line, defs); - nv_mask(priv, reg, 0x00010001 << lsh, val << lsh); + nv_mask(gpio, reg, 0x00010001 << lsh, val << lsh); } } @@ -72,7 +67,7 @@ nv50_gpio_location(int line, u32 *reg, u32 *shift) return 0; } -static int +int nv50_gpio_drive(struct nouveau_gpio *gpio, int line, int dir, int out) { u32 reg, shift; @@ -84,7 +79,7 @@ nv50_gpio_drive(struct nouveau_gpio *gpio, int line, int dir, int out) return 0; } -static int +int nv50_gpio_sense(struct nouveau_gpio *gpio, int line) { u32 reg, shift; @@ -95,119 +90,40 @@ nv50_gpio_sense(struct nouveau_gpio *gpio, int line) return !!(nv_rd32(gpio, reg) & (4 << shift)); } -void -nv50_gpio_intr(struct nouveau_subdev *subdev) -{ - struct nv50_gpio_priv *priv = (void *)subdev; - u32 intr0, intr1 = 0; - u32 hi, lo; - int i; - - intr0 = nv_rd32(priv, 0xe054) & nv_rd32(priv, 0xe050); - if (nv_device(priv)->chipset > 0x92) - intr1 = nv_rd32(priv, 0xe074) & nv_rd32(priv, 0xe070); - - hi = (intr0 & 0x0000ffff) | (intr1 << 16); - lo = (intr0 >> 16) | (intr1 & 0xffff0000); - - for (i = 0; (hi | lo) && i < 32; i++) { - if ((hi | lo) & (1 << i)) - nouveau_event_trigger(priv->base.events, i); - } - - nv_wr32(priv, 0xe054, intr0); - if (nv_device(priv)->chipset > 0x92) - nv_wr32(priv, 0xe074, intr1); -} - -void -nv50_gpio_intr_enable(struct nouveau_event *event, int line) -{ - const u32 addr = line < 16 ? 0xe050 : 0xe070; - const u32 mask = 0x00010001 << (line & 0xf); - nv_wr32(event->priv, addr + 0x04, mask); - nv_mask(event->priv, addr + 0x00, mask, mask); -} - -void -nv50_gpio_intr_disable(struct nouveau_event *event, int line) -{ - const u32 addr = line < 16 ? 0xe050 : 0xe070; - const u32 mask = 0x00010001 << (line & 0xf); - nv_wr32(event->priv, addr + 0x04, mask); - nv_mask(event->priv, addr + 0x00, mask, 0x00000000); -} - -static int -nv50_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nv50_gpio_priv *priv; - int ret; - - ret = nouveau_gpio_create(parent, engine, oclass, - nv_device(parent)->chipset > 0x92 ? 32 : 16, - &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - priv->base.reset = nv50_gpio_reset; - priv->base.drive = nv50_gpio_drive; - priv->base.sense = nv50_gpio_sense; - priv->base.events->priv = priv; - priv->base.events->enable = nv50_gpio_intr_enable; - priv->base.events->disable = nv50_gpio_intr_disable; - nv_subdev(priv)->intr = nv50_gpio_intr; - return 0; -} - -void -nv50_gpio_dtor(struct nouveau_object *object) -{ - struct nv50_gpio_priv *priv = (void *)object; - nouveau_gpio_destroy(&priv->base); -} - -int -nv50_gpio_init(struct nouveau_object *object) +static void +nv50_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo) { - struct nv50_gpio_priv *priv = (void *)object; - int ret; - - ret = nouveau_gpio_init(&priv->base); - if (ret) - return ret; - - /* disable, and ack any pending gpio interrupts */ - nv_wr32(priv, 0xe050, 0x00000000); - nv_wr32(priv, 0xe054, 0xffffffff); - if (nv_device(priv)->chipset > 0x92) { - nv_wr32(priv, 0xe070, 0x00000000); - nv_wr32(priv, 0xe074, 0xffffffff); - } - - return 0; + u32 intr = nv_rd32(gpio, 0x00e054); + u32 stat = nv_rd32(gpio, 0x00e050) & intr; + *lo = (stat & 0xffff0000) >> 16; + *hi = (stat & 0x0000ffff); + nv_wr32(gpio, 0x00e054, intr); } -int -nv50_gpio_fini(struct nouveau_object *object, bool suspend) +static void +nv50_gpio_intr_mask(struct nouveau_gpio *gpio, u32 type, u32 mask, u32 data) { - struct nv50_gpio_priv *priv = (void *)object; - nv_wr32(priv, 0xe050, 0x00000000); - if (nv_device(priv)->chipset > 0x92) - nv_wr32(priv, 0xe070, 0x00000000); - return nouveau_gpio_fini(&priv->base, suspend); + u32 inte = nv_rd32(gpio, 0x00e050); + if (type & NVKM_GPIO_LO) + inte = (inte & ~(mask << 16)) | (data << 16); + if (type & NVKM_GPIO_HI) + inte = (inte & ~mask) | data; + nv_wr32(gpio, 0x00e050, inte); } -struct nouveau_oclass -nv50_gpio_oclass = { - .handle = NV_SUBDEV(GPIO, 0x50), - .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nv50_gpio_ctor, - .dtor = nv50_gpio_dtor, - .init = nv50_gpio_init, - .fini = nv50_gpio_fini, +struct nouveau_oclass * +nv50_gpio_oclass = &(struct nouveau_gpio_impl) { + .base.handle = NV_SUBDEV(GPIO, 0x50), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_gpio_ctor, + .dtor = _nouveau_gpio_dtor, + .init = _nouveau_gpio_init, + .fini = _nouveau_gpio_fini, }, -}; + .lines = 16, + .intr_stat = nv50_gpio_intr_stat, + .intr_mask = nv50_gpio_intr_mask, + .drive = nv50_gpio_drive, + .sense = nv50_gpio_sense, + .reset = nv50_gpio_reset, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv92.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv92.c new file mode 100644 index 00000000000..252083d376f --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv92.c @@ -0,0 +1,74 @@ +/* + * 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 "priv.h" + +void +nv92_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo) +{ + u32 intr0 = nv_rd32(gpio, 0x00e054); + u32 intr1 = nv_rd32(gpio, 0x00e074); + u32 stat0 = nv_rd32(gpio, 0x00e050) & intr0; + u32 stat1 = nv_rd32(gpio, 0x00e070) & intr1; + *lo = (stat1 & 0xffff0000) | (stat0 >> 16); + *hi = (stat1 << 16) | (stat0 & 0x0000ffff); + nv_wr32(gpio, 0x00e054, intr0); + nv_wr32(gpio, 0x00e074, intr1); +} + +void +nv92_gpio_intr_mask(struct nouveau_gpio *gpio, u32 type, u32 mask, u32 data) +{ + u32 inte0 = nv_rd32(gpio, 0x00e050); + u32 inte1 = nv_rd32(gpio, 0x00e070); + if (type & NVKM_GPIO_LO) + inte0 = (inte0 & ~(mask << 16)) | (data << 16); + if (type & NVKM_GPIO_HI) + inte0 = (inte0 & ~(mask & 0xffff)) | (data & 0xffff); + mask >>= 16; + data >>= 16; + if (type & NVKM_GPIO_LO) + inte1 = (inte1 & ~(mask << 16)) | (data << 16); + if (type & NVKM_GPIO_HI) + inte1 = (inte1 & ~mask) | data; + nv_wr32(gpio, 0x00e050, inte0); + nv_wr32(gpio, 0x00e070, inte1); +} + +struct nouveau_oclass * +nv92_gpio_oclass = &(struct nouveau_gpio_impl) { + .base.handle = NV_SUBDEV(GPIO, 0x92), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_gpio_ctor, + .dtor = _nouveau_gpio_dtor, + .init = _nouveau_gpio_init, + .fini = _nouveau_gpio_fini, + }, + .lines = 32, + .intr_stat = nv92_gpio_intr_stat, + .intr_mask = nv92_gpio_intr_mask, + .drive = nv50_gpio_drive, + .sense = nv50_gpio_sense, + .reset = nv50_gpio_reset, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c index 010431e3ace..a4682b0956a 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c @@ -24,15 +24,10 @@ #include "priv.h" -struct nvd0_gpio_priv { - struct nouveau_gpio base; -}; - void nvd0_gpio_reset(struct nouveau_gpio *gpio, u8 match) { struct nouveau_bios *bios = nouveau_bios(gpio); - struct nvd0_gpio_priv *priv = (void *)gpio; u8 ver, len; u16 entry; int ent = -1; @@ -51,9 +46,9 @@ nvd0_gpio_reset(struct nouveau_gpio *gpio, u8 match) gpio->set(gpio, 0, func, line, defs); - nv_mask(priv, 0x00d610 + (line * 4), 0xff, unk0); + nv_mask(gpio, 0x00d610 + (line * 4), 0xff, unk0); if (unk1--) - nv_mask(priv, 0x00d740 + (unk1 * 4), 0xff, line); + nv_mask(gpio, 0x00d740 + (unk1 * 4), 0xff, line); } } @@ -72,36 +67,19 @@ nvd0_gpio_sense(struct nouveau_gpio *gpio, int line) return !!(nv_rd32(gpio, 0x00d610 + (line * 4)) & 0x00004000); } -static int -nvd0_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nvd0_gpio_priv *priv; - int ret; - - ret = nouveau_gpio_create(parent, engine, oclass, 32, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - priv->base.reset = nvd0_gpio_reset; - priv->base.drive = nvd0_gpio_drive; - priv->base.sense = nvd0_gpio_sense; - priv->base.events->priv = priv; - priv->base.events->enable = nv50_gpio_intr_enable; - priv->base.events->disable = nv50_gpio_intr_disable; - nv_subdev(priv)->intr = nv50_gpio_intr; - return 0; -} - -struct nouveau_oclass -nvd0_gpio_oclass = { - .handle = NV_SUBDEV(GPIO, 0xd0), - .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nvd0_gpio_ctor, - .dtor = nv50_gpio_dtor, - .init = nv50_gpio_init, - .fini = nv50_gpio_fini, +struct nouveau_oclass * +nvd0_gpio_oclass = &(struct nouveau_gpio_impl) { + .base.handle = NV_SUBDEV(GPIO, 0xd0), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_gpio_ctor, + .dtor = _nouveau_gpio_dtor, + .init = _nouveau_gpio_init, + .fini = _nouveau_gpio_fini, }, -}; + .lines = 32, + .intr_stat = nv92_gpio_intr_stat, + .intr_mask = nv92_gpio_intr_mask, + .drive = nvd0_gpio_drive, + .sense = nvd0_gpio_sense, + .reset = nvd0_gpio_reset, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nve0.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nve0.c index 16b8c5bf5ef..e1145b48c76 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/gpio/nve0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nve0.c @@ -24,108 +24,51 @@ #include "priv.h" -struct nve0_gpio_priv { - struct nouveau_gpio base; -}; - -void -nve0_gpio_intr(struct nouveau_subdev *subdev) +static void +nve0_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo) { - struct nve0_gpio_priv *priv = (void *)subdev; - u32 intr0 = nv_rd32(priv, 0xdc00) & nv_rd32(priv, 0xdc08); - u32 intr1 = nv_rd32(priv, 0xdc80) & nv_rd32(priv, 0xdc88); - u32 hi = (intr0 & 0x0000ffff) | (intr1 << 16); - u32 lo = (intr0 >> 16) | (intr1 & 0xffff0000); - int i; - - for (i = 0; (hi | lo) && i < 32; i++) { - if ((hi | lo) & (1 << i)) - nouveau_event_trigger(priv->base.events, i); - } - - nv_wr32(priv, 0xdc00, intr0); - nv_wr32(priv, 0xdc88, intr1); + u32 intr0 = nv_rd32(gpio, 0x00dc00); + u32 intr1 = nv_rd32(gpio, 0x00dc80); + u32 stat0 = nv_rd32(gpio, 0x00dc08) & intr0; + u32 stat1 = nv_rd32(gpio, 0x00dc88) & intr1; + *lo = (stat1 & 0xffff0000) | (stat0 >> 16); + *hi = (stat1 << 16) | (stat0 & 0x0000ffff); + nv_wr32(gpio, 0x00dc00, intr0); + nv_wr32(gpio, 0x00dc80, intr1); } void -nve0_gpio_intr_enable(struct nouveau_event *event, int line) +nve0_gpio_intr_mask(struct nouveau_gpio *gpio, u32 type, u32 mask, u32 data) { - const u32 addr = line < 16 ? 0xdc00 : 0xdc80; - const u32 mask = 0x00010001 << (line & 0xf); - nv_wr32(event->priv, addr + 0x08, mask); - nv_mask(event->priv, addr + 0x00, mask, mask); -} - -void -nve0_gpio_intr_disable(struct nouveau_event *event, int line) -{ - const u32 addr = line < 16 ? 0xdc00 : 0xdc80; - const u32 mask = 0x00010001 << (line & 0xf); - nv_wr32(event->priv, addr + 0x08, mask); - nv_mask(event->priv, addr + 0x00, mask, 0x00000000); -} - -int -nve0_gpio_fini(struct nouveau_object *object, bool suspend) -{ - struct nve0_gpio_priv *priv = (void *)object; - nv_wr32(priv, 0xdc08, 0x00000000); - nv_wr32(priv, 0xdc88, 0x00000000); - return nouveau_gpio_fini(&priv->base, suspend); -} - -int -nve0_gpio_init(struct nouveau_object *object) -{ - struct nve0_gpio_priv *priv = (void *)object; - int ret; - - ret = nouveau_gpio_init(&priv->base); - if (ret) - return ret; - - nv_wr32(priv, 0xdc00, 0xffffffff); - nv_wr32(priv, 0xdc80, 0xffffffff); - return 0; -} - -void -nve0_gpio_dtor(struct nouveau_object *object) -{ - struct nve0_gpio_priv *priv = (void *)object; - nouveau_gpio_destroy(&priv->base); -} - -static int -nve0_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nve0_gpio_priv *priv; - int ret; - - ret = nouveau_gpio_create(parent, engine, oclass, 32, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - priv->base.reset = nvd0_gpio_reset; - priv->base.drive = nvd0_gpio_drive; - priv->base.sense = nvd0_gpio_sense; - priv->base.events->priv = priv; - priv->base.events->enable = nve0_gpio_intr_enable; - priv->base.events->disable = nve0_gpio_intr_disable; - nv_subdev(priv)->intr = nve0_gpio_intr; - return 0; + u32 inte0 = nv_rd32(gpio, 0x00dc08); + u32 inte1 = nv_rd32(gpio, 0x00dc88); + if (type & NVKM_GPIO_LO) + inte0 = (inte0 & ~(mask << 16)) | (data << 16); + if (type & NVKM_GPIO_HI) + inte0 = (inte0 & ~(mask & 0xffff)) | (data & 0xffff); + mask >>= 16; + data >>= 16; + if (type & NVKM_GPIO_LO) + inte1 = (inte1 & ~(mask << 16)) | (data << 16); + if (type & NVKM_GPIO_HI) + inte1 = (inte1 & ~mask) | data; + nv_wr32(gpio, 0x00dc08, inte0); + nv_wr32(gpio, 0x00dc88, inte1); } -struct nouveau_oclass -nve0_gpio_oclass = { - .handle = NV_SUBDEV(GPIO, 0xe0), - .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nve0_gpio_ctor, - .dtor = nv50_gpio_dtor, - .init = nve0_gpio_init, - .fini = nve0_gpio_fini, +struct nouveau_oclass * +nve0_gpio_oclass = &(struct nouveau_gpio_impl) { + .base.handle = NV_SUBDEV(GPIO, 0xe0), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_gpio_ctor, + .dtor = _nouveau_gpio_dtor, + .init = _nouveau_gpio_init, + .fini = _nouveau_gpio_fini, }, -}; + .lines = 32, + .intr_stat = nve0_gpio_intr_stat, + .intr_mask = nve0_gpio_intr_mask, + .drive = nvd0_gpio_drive, + .sense = nvd0_gpio_sense, + .reset = nvd0_gpio_reset, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h b/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h index 2ee1c895c78..e1724dfc86a 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h @@ -3,15 +3,65 @@ #include <subdev/gpio.h> -void nv50_gpio_dtor(struct nouveau_object *); -int nv50_gpio_init(struct nouveau_object *); -int nv50_gpio_fini(struct nouveau_object *, bool); -void nv50_gpio_intr(struct nouveau_subdev *); -void nv50_gpio_intr_enable(struct nouveau_event *, int line); -void nv50_gpio_intr_disable(struct nouveau_event *, int line); +#define nouveau_gpio_create(p,e,o,d) \ + nouveau_gpio_create_((p), (e), (o), sizeof(**d), (void **)d) +#define nouveau_gpio_destroy(p) ({ \ + struct nouveau_gpio *gpio = (p); \ + _nouveau_gpio_dtor(nv_object(gpio)); \ +}) +#define nouveau_gpio_init(p) ({ \ + struct nouveau_gpio *gpio = (p); \ + _nouveau_gpio_init(nv_object(gpio)); \ +}) +#define nouveau_gpio_fini(p,s) ({ \ + struct nouveau_gpio *gpio = (p); \ + _nouveau_gpio_fini(nv_object(gpio), (s)); \ +}) + +int nouveau_gpio_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, int, void **); +int _nouveau_gpio_ctor(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, void *, u32, + struct nouveau_object **); +void _nouveau_gpio_dtor(struct nouveau_object *); +int _nouveau_gpio_init(struct nouveau_object *); +int _nouveau_gpio_fini(struct nouveau_object *, bool); + +struct nouveau_gpio_impl { + struct nouveau_oclass base; + int lines; + + /* read and ack pending interrupts, returning only data + * for lines that have not been masked off, while still + * performing the ack for anything that was pending. + */ + void (*intr_stat)(struct nouveau_gpio *, u32 *, u32 *); + + /* mask on/off interrupts for hi/lo transitions on a + * given set of gpio lines + */ + void (*intr_mask)(struct nouveau_gpio *, u32, u32, u32); + + /* configure gpio direction and output value */ + int (*drive)(struct nouveau_gpio *, int line, int dir, int out); + + /* sense current state of given gpio line */ + int (*sense)(struct nouveau_gpio *, int line); + + /*XXX*/ + void (*reset)(struct nouveau_gpio *, u8); +}; + +void nv50_gpio_reset(struct nouveau_gpio *, u8); +int nv50_gpio_drive(struct nouveau_gpio *, int, int, int); +int nv50_gpio_sense(struct nouveau_gpio *, int); + +void nv92_gpio_intr_stat(struct nouveau_gpio *, u32 *, u32 *); +void nv92_gpio_intr_mask(struct nouveau_gpio *, u32, u32, u32); void nvd0_gpio_reset(struct nouveau_gpio *, u8); int nvd0_gpio_drive(struct nouveau_gpio *, int, int, int); int nvd0_gpio_sense(struct nouveau_gpio *, int); + #endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/anx9805.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/anx9805.c index 4b195ac4da6..2c2731a6cf9 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/anx9805.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/anx9805.c @@ -22,7 +22,7 @@ * Authors: Ben Skeggs <bskeggs@redhat.com> */ -#include <subdev/i2c.h> +#include "port.h" struct anx9805_i2c_port { struct nouveau_i2c_port base; @@ -37,6 +37,8 @@ anx9805_train(struct nouveau_i2c_port *port, int link_nr, int link_bw, bool enh) struct nouveau_i2c_port *mast = (void *)nv_object(chan)->parent; u8 tmp, i; + DBG("ANX9805 train %d 0x%02x %d\n", link_nr, link_bw, enh); + nv_wri2cr(mast, chan->addr, 0xa0, link_bw); nv_wri2cr(mast, chan->addr, 0xa1, link_nr | (enh ? 0x80 : 0x00)); nv_wri2cr(mast, chan->addr, 0xa2, 0x01); @@ -60,21 +62,29 @@ anx9805_train(struct nouveau_i2c_port *port, int link_nr, int link_bw, bool enh) } static int -anx9805_aux(struct nouveau_i2c_port *port, u8 type, u32 addr, u8 *data, u8 size) +anx9805_aux(struct nouveau_i2c_port *port, bool retry, + u8 type, u32 addr, u8 *data, u8 size) { struct anx9805_i2c_port *chan = (void *)port; struct nouveau_i2c_port *mast = (void *)nv_object(chan)->parent; int i, ret = -ETIMEDOUT; + u8 buf[16] = {}; u8 tmp; + DBG("%02x %05x %d\n", type, addr, size); + tmp = nv_rdi2cr(mast, chan->ctrl, 0x07) & ~0x04; nv_wri2cr(mast, chan->ctrl, 0x07, tmp | 0x04); nv_wri2cr(mast, chan->ctrl, 0x07, tmp); nv_wri2cr(mast, chan->ctrl, 0xf7, 0x01); nv_wri2cr(mast, chan->addr, 0xe4, 0x80); - for (i = 0; !(type & 1) && i < size; i++) - nv_wri2cr(mast, chan->addr, 0xf0 + i, data[i]); + if (!(type & 1)) { + memcpy(buf, data, size); + DBG("%16ph", buf); + for (i = 0; i < size; i++) + nv_wri2cr(mast, chan->addr, 0xf0 + i, buf[i]); + } nv_wri2cr(mast, chan->addr, 0xe5, ((size - 1) << 4) | type); nv_wri2cr(mast, chan->addr, 0xe6, (addr & 0x000ff) >> 0); nv_wri2cr(mast, chan->addr, 0xe7, (addr & 0x0ff00) >> 8); @@ -93,8 +103,13 @@ anx9805_aux(struct nouveau_i2c_port *port, u8 type, u32 addr, u8 *data, u8 size) goto done; } - for (i = 0; (type & 1) && i < size; i++) - data[i] = nv_rdi2cr(mast, chan->addr, 0xf0 + i); + if (type & 1) { + for (i = 0; i < size; i++) + buf[i] = nv_rdi2cr(mast, chan->addr, 0xf0 + i); + DBG("%16ph", buf); + memcpy(data, buf, size); + } + ret = 0; done: nv_wri2cr(mast, chan->ctrl, 0xf7, 0x01); diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c index 5de074ad170..02eb42be2e9 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c @@ -22,15 +22,19 @@ * Authors: Ben Skeggs */ -#include <subdev/i2c.h> +#include "priv.h" int nv_rdaux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size) { + struct nouveau_i2c *i2c = nouveau_i2c(port); if (port->func->aux) { - if (port->func->acquire) - port->func->acquire(port); - return port->func->aux(port, 9, addr, data, size); + int ret = i2c->acquire(port, 0); + if (ret == 0) { + ret = port->func->aux(port, true, 9, addr, data, size); + i2c->release(port); + } + return ret; } return -ENODEV; } @@ -38,10 +42,14 @@ nv_rdaux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size) int nv_wraux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size) { + struct nouveau_i2c *i2c = nouveau_i2c(port); if (port->func->aux) { - if (port->func->acquire) - port->func->acquire(port); - return port->func->aux(port, 8, addr, data, size); + int ret = i2c->acquire(port, 0); + if (ret == 0) { + ret = port->func->aux(port, true, 8, addr, data, size); + i2c->release(port); + } + return ret; } return -ENODEV; } @@ -50,13 +58,16 @@ static int aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { struct nouveau_i2c_port *port = adap->algo_data; + struct nouveau_i2c *i2c = nouveau_i2c(port); struct i2c_msg *msg = msgs; int ret, mcnt = num; if (!port->func->aux) return -ENODEV; - if ( port->func->acquire) - port->func->acquire(port); + + ret = i2c->acquire(port, 0); + if (ret) + return ret; while (mcnt--) { u8 remaining = msg->len; @@ -74,9 +85,11 @@ aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) if (mcnt || remaining > 16) cmd |= 4; /* MOT */ - ret = port->func->aux(port, cmd, msg->addr, ptr, cnt); - if (ret < 0) + ret = port->func->aux(port, true, cmd, msg->addr, ptr, cnt); + if (ret < 0) { + i2c->release(port); return ret; + } ptr += cnt; remaining -= cnt; @@ -85,6 +98,7 @@ aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) msg++; } + i2c->release(port); return num; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c index 378e05b88e6..09ba2cc851c 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c @@ -23,13 +23,16 @@ */ #include <core/option.h> +#include <core/event.h> #include <subdev/bios.h> #include <subdev/bios/dcb.h> #include <subdev/bios/i2c.h> -#include <subdev/i2c.h> #include <subdev/vga.h> +#include "priv.h" +#include "pad.h" + /****************************************************************************** * interface to linux i2c bit-banging algorithm *****************************************************************************/ @@ -45,9 +48,15 @@ nouveau_i2c_pre_xfer(struct i2c_adapter *adap) { struct i2c_algo_bit_data *bit = adap->algo_data; struct nouveau_i2c_port *port = bit->data; - if (port->func->acquire) - port->func->acquire(port); - return 0; + return nouveau_i2c(port)->acquire(port, bit->timeout); +} + +static void +nouveau_i2c_post_xfer(struct i2c_adapter *adap) +{ + struct i2c_algo_bit_data *bit = adap->algo_data; + struct nouveau_i2c_port *port = bit->data; + return nouveau_i2c(port)->release(port); } static void @@ -82,6 +91,15 @@ nouveau_i2c_getsda(void *data) * base i2c "port" class implementation *****************************************************************************/ +int +_nouveau_i2c_port_fini(struct nouveau_object *object, bool suspend) +{ + struct nouveau_i2c_port *port = (void *)object; + struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port); + nv_ofuncs(pad)->fini(nv_object(pad), suspend); + return nouveau_object_fini(&port->base, suspend); +} + void _nouveau_i2c_port_dtor(struct nouveau_object *object) { @@ -98,7 +116,7 @@ nouveau_i2c_port_create_(struct nouveau_object *parent, const struct nouveau_i2c_func *func, int size, void **pobject) { - struct nouveau_device *device = nv_device(parent); + struct nouveau_device *device = nv_device(engine); struct nouveau_i2c *i2c = (void *)engine; struct nouveau_i2c_port *port; int ret; @@ -113,8 +131,9 @@ nouveau_i2c_port_create_(struct nouveau_object *parent, port->adapter.owner = THIS_MODULE; port->adapter.dev.parent = nv_device_base(device); port->index = index; + port->aux = -1; port->func = func; - i2c_set_adapdata(&port->adapter, i2c); + mutex_init(&port->mutex); if ( algo == &nouveau_i2c_bit_algo && !nouveau_boolopt(device->cfgopt, "NvI2C", CSTMSEL)) { @@ -128,6 +147,7 @@ nouveau_i2c_port_create_(struct nouveau_object *parent, bit->timeout = usecs_to_jiffies(2200); bit->data = port; bit->pre_xfer = nouveau_i2c_pre_xfer; + bit->post_xfer = nouveau_i2c_post_xfer; bit->setsda = nouveau_i2c_setsda; bit->setscl = nouveau_i2c_setscl; bit->getsda = nouveau_i2c_getsda; @@ -141,7 +161,6 @@ nouveau_i2c_port_create_(struct nouveau_object *parent, ret = i2c_add_adapter(&port->adapter); } - /* drop port's i2c subdev refcount, i2c handles this itself */ if (ret == 0) list_add_tail(&port->head, &i2c->ports); return ret; @@ -193,6 +212,75 @@ nouveau_i2c_find_type(struct nouveau_i2c *i2c, u16 type) return NULL; } +static void +nouveau_i2c_release_pad(struct nouveau_i2c_port *port) +{ + struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port); + struct nouveau_i2c *i2c = nouveau_i2c(port); + + if (atomic_dec_and_test(&nv_object(pad)->usecount)) { + nv_ofuncs(pad)->fini(nv_object(pad), false); + wake_up_all(&i2c->wait); + } +} + +static int +nouveau_i2c_try_acquire_pad(struct nouveau_i2c_port *port) +{ + struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port); + + if (atomic_add_return(1, &nv_object(pad)->usecount) != 1) { + struct nouveau_object *owner = (void *)pad->port; + do { + if (owner == (void *)port) + return 0; + owner = owner->parent; + } while(owner); + nouveau_i2c_release_pad(port); + return -EBUSY; + } + + pad->next = port; + nv_ofuncs(pad)->init(nv_object(pad)); + return 0; +} + +static int +nouveau_i2c_acquire_pad(struct nouveau_i2c_port *port, unsigned long timeout) +{ + struct nouveau_i2c *i2c = nouveau_i2c(port); + + if (timeout) { + if (wait_event_timeout(i2c->wait, + nouveau_i2c_try_acquire_pad(port) == 0, + timeout) == 0) + return -EBUSY; + } else { + wait_event(i2c->wait, nouveau_i2c_try_acquire_pad(port) == 0); + } + + return 0; +} + +static void +nouveau_i2c_release(struct nouveau_i2c_port *port) +__releases(pad->mutex) +{ + nouveau_i2c(port)->release_pad(port); + mutex_unlock(&port->mutex); +} + +static int +nouveau_i2c_acquire(struct nouveau_i2c_port *port, unsigned long timeout) +__acquires(pad->mutex) +{ + int ret; + mutex_lock(&port->mutex); + if ((ret = nouveau_i2c(port)->acquire_pad(port, timeout))) + mutex_unlock(&port->mutex); + return ret; +} + static int nouveau_i2c_identify(struct nouveau_i2c *i2c, int index, const char *what, struct nouveau_i2c_board_info *info, @@ -237,11 +325,59 @@ nouveau_i2c_identify(struct nouveau_i2c *i2c, int index, const char *what, return -ENODEV; } +static void +nouveau_i2c_intr_disable(struct nouveau_event *event, int type, int index) +{ + struct nouveau_i2c *i2c = nouveau_i2c(event->priv); + struct nouveau_i2c_port *port = i2c->find(i2c, index); + const struct nouveau_i2c_impl *impl = (void *)nv_object(i2c)->oclass; + if (port && port->aux >= 0) + impl->aux_mask(i2c, type, 1 << port->aux, 0); +} + +static void +nouveau_i2c_intr_enable(struct nouveau_event *event, int type, int index) +{ + struct nouveau_i2c *i2c = nouveau_i2c(event->priv); + struct nouveau_i2c_port *port = i2c->find(i2c, index); + const struct nouveau_i2c_impl *impl = (void *)nv_object(i2c)->oclass; + if (port && port->aux >= 0) + impl->aux_mask(i2c, type, 1 << port->aux, 1 << port->aux); +} + +static void +nouveau_i2c_intr(struct nouveau_subdev *subdev) +{ + struct nouveau_i2c_impl *impl = (void *)nv_oclass(subdev); + struct nouveau_i2c *i2c = nouveau_i2c(subdev); + struct nouveau_i2c_port *port; + u32 hi, lo, rq, tx, e; + + if (impl->aux_stat) { + impl->aux_stat(i2c, &hi, &lo, &rq, &tx); + if (hi || lo || rq || tx) { + list_for_each_entry(port, &i2c->ports, head) { + if (e = 0, port->aux < 0) + continue; + + if (hi & (1 << port->aux)) e |= NVKM_I2C_PLUG; + if (lo & (1 << port->aux)) e |= NVKM_I2C_UNPLUG; + if (rq & (1 << port->aux)) e |= NVKM_I2C_IRQ; + if (tx & (1 << port->aux)) e |= NVKM_I2C_DONE; + + nouveau_event_trigger(i2c->ntfy, e, port->index); + } + } + } +} + int _nouveau_i2c_fini(struct nouveau_object *object, bool suspend) { + struct nouveau_i2c_impl *impl = (void *)nv_oclass(object); struct nouveau_i2c *i2c = (void *)object; struct nouveau_i2c_port *port; + u32 mask; int ret; list_for_each_entry(port, &i2c->ports, head) { @@ -250,6 +386,11 @@ _nouveau_i2c_fini(struct nouveau_object *object, bool suspend) goto fail; } + if ((mask = (1 << impl->aux) - 1), impl->aux_stat) { + impl->aux_mask(i2c, NVKM_I2C_ANY, mask, 0); + impl->aux_stat(i2c, &mask, &mask, &mask, &mask); + } + return nouveau_subdev_fini(&i2c->base, suspend); fail: list_for_each_entry_continue_reverse(port, &i2c->ports, head) { @@ -290,6 +431,8 @@ _nouveau_i2c_dtor(struct nouveau_object *object) struct nouveau_i2c *i2c = (void *)object; struct nouveau_i2c_port *port, *temp; + nouveau_event_destroy(&i2c->ntfy); + list_for_each_entry_safe(port, temp, &i2c->ports, head) { nouveau_object_ref(NULL, (struct nouveau_object **)&port); } @@ -306,14 +449,14 @@ int nouveau_i2c_create_(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, - struct nouveau_oclass *sclass, int length, void **pobject) { + const struct nouveau_i2c_impl *impl = (void *)oclass; struct nouveau_bios *bios = nouveau_bios(parent); struct nouveau_i2c *i2c; struct nouveau_object *object; struct dcb_i2c_entry info; - int ret, i, j, index = -1; + int ret, i, j, index = -1, pad; struct dcb_output outp; u8 ver, hdr; u32 data; @@ -324,24 +467,48 @@ nouveau_i2c_create_(struct nouveau_object *parent, if (ret) return ret; + nv_subdev(i2c)->intr = nouveau_i2c_intr; i2c->find = nouveau_i2c_find; i2c->find_type = nouveau_i2c_find_type; + i2c->acquire_pad = nouveau_i2c_acquire_pad; + i2c->release_pad = nouveau_i2c_release_pad; + i2c->acquire = nouveau_i2c_acquire; + i2c->release = nouveau_i2c_release; i2c->identify = nouveau_i2c_identify; + init_waitqueue_head(&i2c->wait); INIT_LIST_HEAD(&i2c->ports); while (!dcb_i2c_parse(bios, ++index, &info)) { if (info.type == DCB_I2C_UNUSED) continue; - oclass = sclass; + if (info.share != DCB_I2C_UNUSED) { + if (info.type == DCB_I2C_NVIO_AUX) + pad = info.drive; + else + pad = info.share; + oclass = impl->pad_s; + } else { + pad = 0x100 + info.drive; + oclass = impl->pad_x; + } + + ret = nouveau_object_ctor(NULL, *pobject, oclass, + NULL, pad, &parent); + if (ret < 0) + continue; + + oclass = impl->sclass; do { ret = -EINVAL; if (oclass->handle == info.type) { - ret = nouveau_object_ctor(*pobject, *pobject, + ret = nouveau_object_ctor(parent, *pobject, oclass, &info, index, &object); } } while (ret && (++oclass)->handle); + + nouveau_object_ref(NULL, &parent); } /* in addition to the busses specified in the i2c table, there @@ -380,5 +547,28 @@ nouveau_i2c_create_(struct nouveau_object *parent, } } + ret = nouveau_event_create(4, index, &i2c->ntfy); + if (ret) + return ret; + + i2c->ntfy->priv = i2c; + i2c->ntfy->enable = nouveau_i2c_intr_enable; + i2c->ntfy->disable = nouveau_i2c_intr_disable; + return 0; +} + +int +_nouveau_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nouveau_i2c *i2c; + int ret; + + ret = nouveau_i2c_create(parent, engine, oclass, &i2c); + *pobject = nv_object(i2c); + if (ret) + return ret; + return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/bit.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/bit.c index a6e72d3b06b..813ffc96e86 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/bit.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/bit.c @@ -22,7 +22,7 @@ * Authors: Ben Skeggs */ -#include "subdev/i2c.h" +#include "priv.h" #ifdef CONFIG_NOUVEAU_I2C_INTERNAL #define T_TIMEOUT 2200000 @@ -187,8 +187,9 @@ i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) struct i2c_msg *msg = msgs; int ret = 0, mcnt = num; - if (port->func->acquire) - port->func->acquire(port); + ret = nouveau_i2c(port)->acquire(port, nsecs_to_jiffies(T_TIMEOUT)); + if (ret) + return ret; while (!ret && mcnt--) { u8 remaining = msg->len; @@ -210,6 +211,7 @@ i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) } i2c_stop(port); + nouveau_i2c(port)->release(port); return (ret < 0) ? ret : num; } #else diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/gf117.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/gf117.c new file mode 100644 index 00000000000..fa891c39866 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/gf117.c @@ -0,0 +1,39 @@ +/* + * 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 "nv50.h" + +struct nouveau_oclass * +gf117_i2c_oclass = &(struct nouveau_i2c_impl) { + .base.handle = NV_SUBDEV(I2C, 0xd7), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_i2c_ctor, + .dtor = _nouveau_i2c_dtor, + .init = _nouveau_i2c_init, + .fini = _nouveau_i2c_fini, + }, + .sclass = nvd0_i2c_sclass, + .pad_x = &nv04_i2c_pad_oclass, + .pad_s = &nv04_i2c_pad_oclass, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv04.c index 860d5d2365d..b1725bdea96 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv04.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv04.c @@ -22,9 +22,10 @@ * Authors: Ben Skeggs */ -#include <subdev/i2c.h> #include <subdev/vga.h> +#include "priv.h" + struct nv04_i2c_priv { struct nouveau_i2c base; }; @@ -115,29 +116,15 @@ nv04_i2c_sclass[] = { {} }; -static int -nv04_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nv04_i2c_priv *priv; - int ret; - - ret = nouveau_i2c_create(parent, engine, oclass, nv04_i2c_sclass, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - return 0; -} - -struct nouveau_oclass -nv04_i2c_oclass = { - .handle = NV_SUBDEV(I2C, 0x04), - .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nv04_i2c_ctor, +struct nouveau_oclass * +nv04_i2c_oclass = &(struct nouveau_i2c_impl) { + .base.handle = NV_SUBDEV(I2C, 0x04), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_i2c_ctor, .dtor = _nouveau_i2c_dtor, .init = _nouveau_i2c_init, .fini = _nouveau_i2c_fini, }, -}; + .sclass = nv04_i2c_sclass, + .pad_x = &nv04_i2c_pad_oclass, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv4e.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv4e.c index 0c2655a03bb..f16c87ce5ba 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv4e.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv4e.c @@ -22,9 +22,10 @@ * Authors: Ben Skeggs */ -#include <subdev/i2c.h> #include <subdev/vga.h> +#include "priv.h" + struct nv4e_i2c_priv { struct nouveau_i2c base; }; @@ -107,29 +108,15 @@ nv4e_i2c_sclass[] = { {} }; -static int -nv4e_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nv4e_i2c_priv *priv; - int ret; - - ret = nouveau_i2c_create(parent, engine, oclass, nv4e_i2c_sclass, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - return 0; -} - -struct nouveau_oclass -nv4e_i2c_oclass = { - .handle = NV_SUBDEV(I2C, 0x4e), - .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nv4e_i2c_ctor, +struct nouveau_oclass * +nv4e_i2c_oclass = &(struct nouveau_i2c_impl) { + .base.handle = NV_SUBDEV(I2C, 0x4e), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_i2c_ctor, .dtor = _nouveau_i2c_dtor, .init = _nouveau_i2c_init, .fini = _nouveau_i2c_fini, }, -}; + .sclass = nv4e_i2c_sclass, + .pad_x = &nv04_i2c_pad_oclass, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.c index a8d67a28770..7b8756d4df0 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.c @@ -121,29 +121,15 @@ nv50_i2c_sclass[] = { {} }; -static int -nv50_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nv50_i2c_priv *priv; - int ret; - - ret = nouveau_i2c_create(parent, engine, oclass, nv50_i2c_sclass, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - return 0; -} - -struct nouveau_oclass -nv50_i2c_oclass = { - .handle = NV_SUBDEV(I2C, 0x50), - .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nv50_i2c_ctor, +struct nouveau_oclass * +nv50_i2c_oclass = &(struct nouveau_i2c_impl) { + .base.handle = NV_SUBDEV(I2C, 0x50), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_i2c_ctor, .dtor = _nouveau_i2c_dtor, .init = _nouveau_i2c_init, .fini = _nouveau_i2c_fini, }, -}; + .sclass = nv50_i2c_sclass, + .pad_x = &nv04_i2c_pad_oclass, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.h b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.h index 4e5ba48ebf5..5d2a77421c7 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.h +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.h @@ -1,7 +1,7 @@ #ifndef __NV50_I2C_H__ #define __NV50_I2C_H__ -#include <subdev/i2c.h> +#include "priv.h" struct nv50_i2c_priv { struct nouveau_i2c base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c index df6d3e4b68b..f59c3a25546 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c @@ -24,6 +24,36 @@ #include "nv50.h" +void +nv94_aux_stat(struct nouveau_i2c *i2c, u32 *hi, u32 *lo, u32 *rq, u32 *tx) +{ + u32 intr = nv_rd32(i2c, 0x00e06c); + u32 stat = nv_rd32(i2c, 0x00e068) & intr, i; + for (i = 0, *hi = *lo = *rq = *tx = 0; i < 8; i++) { + if ((stat & (1 << (i * 4)))) *hi |= 1 << i; + if ((stat & (2 << (i * 4)))) *lo |= 1 << i; + if ((stat & (4 << (i * 4)))) *rq |= 1 << i; + if ((stat & (8 << (i * 4)))) *tx |= 1 << i; + } + nv_wr32(i2c, 0x00e06c, intr); +} + +void +nv94_aux_mask(struct nouveau_i2c *i2c, u32 type, u32 mask, u32 data) +{ + u32 temp = nv_rd32(i2c, 0x00e068), i; + for (i = 0; i < 8; i++) { + if (mask & (1 << i)) { + if (!(data & (1 << i))) { + temp &= ~(type << (i * 4)); + continue; + } + temp |= type << (i * 4); + } + } + nv_wr32(i2c, 0x00e068, temp); +} + #define AUX_DBG(fmt, args...) nv_debug(aux, "AUXCH(%d): " fmt, ch, ##args) #define AUX_ERR(fmt, args...) nv_error(aux, "AUXCH(%d): " fmt, ch, ##args) @@ -69,7 +99,8 @@ auxch_init(struct nouveau_i2c *aux, int ch) } int -nv94_aux(struct nouveau_i2c_port *base, u8 type, u32 addr, u8 *data, u8 size) +nv94_aux(struct nouveau_i2c_port *base, bool retry, + u8 type, u32 addr, u8 *data, u8 size) { struct nouveau_i2c *aux = nouveau_i2c(base); struct nv50_i2c_port *port = (void *)base; @@ -105,9 +136,8 @@ nv94_aux(struct nouveau_i2c_port *base, u8 type, u32 addr, u8 *data, u8 size) ctrl |= size - 1; nv_wr32(aux, 0x00e4e0 + (ch * 0x50), addr); - /* retry transaction a number of times on failure... */ - ret = -EREMOTEIO; - for (retries = 0; retries < 32; retries++) { + /* (maybe) retry transaction a number of times on failure... */ + for (retries = 0; !ret && retries < 32; retries++) { /* reset, and delay a while if this is a retry */ nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x80000000 | ctrl); nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x00000000 | ctrl); @@ -123,16 +153,21 @@ nv94_aux(struct nouveau_i2c_port *base, u8 type, u32 addr, u8 *data, u8 size) udelay(1); if (!timeout--) { AUX_ERR("tx req timeout 0x%08x\n", ctrl); + ret = -EIO; goto out; } } while (ctrl & 0x00010000); + ret = 1; /* read status, and check if transaction completed ok */ stat = nv_mask(aux, 0x00e4e8 + (ch * 0x50), 0, 0); - if (!(stat & 0x000f0f00)) { - ret = 0; - break; - } + if ((stat & 0x000f0000) == 0x00080000 || + (stat & 0x000f0000) == 0x00020000) + ret = retry ? 0 : 1; + if ((stat & 0x00000100)) + ret = -ETIMEDOUT; + if ((stat & 0x00000e00)) + ret = -EIO; AUX_DBG("%02d 0x%08x 0x%08x\n", retries, ctrl, stat); } @@ -147,29 +182,11 @@ nv94_aux(struct nouveau_i2c_port *base, u8 type, u32 addr, u8 *data, u8 size) out: auxch_fini(aux, ch); - return ret; -} - -void -nv94_i2c_acquire(struct nouveau_i2c_port *base) -{ - struct nv50_i2c_priv *priv = (void *)nv_object(base)->engine; - struct nv50_i2c_port *port = (void *)base; - if (port->ctrl) { - nv_mask(priv, port->ctrl + 0x0c, 0x00000001, 0x00000000); - nv_mask(priv, port->ctrl + 0x00, 0x0000f003, port->data); - } -} - -void -nv94_i2c_release(struct nouveau_i2c_port *base) -{ + return ret < 0 ? ret : (stat & 0x000f0000) >> 16; } static const struct nouveau_i2c_func nv94_i2c_func = { - .acquire = nv94_i2c_acquire, - .release = nv94_i2c_release, .drive_scl = nv50_i2c_drive_scl, .drive_sda = nv50_i2c_drive_sda, .sense_scl = nv50_i2c_sense_scl, @@ -206,8 +223,6 @@ nv94_i2c_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine, static const struct nouveau_i2c_func nv94_aux_func = { - .acquire = nv94_i2c_acquire, - .release = nv94_i2c_release, .aux = nv94_aux, }; @@ -227,6 +242,7 @@ nv94_aux_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; + port->base.aux = info->drive; port->addr = info->drive; if (info->share != DCB_I2C_UNUSED) { port->ctrl = 0x00e500 + (info->drive * 0x50); @@ -257,29 +273,19 @@ nv94_i2c_sclass[] = { {} }; -static int -nv94_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nv50_i2c_priv *priv; - int ret; - - ret = nouveau_i2c_create(parent, engine, oclass, nv94_i2c_sclass, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - return 0; -} - -struct nouveau_oclass -nv94_i2c_oclass = { - .handle = NV_SUBDEV(I2C, 0x94), - .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nv94_i2c_ctor, +struct nouveau_oclass * +nv94_i2c_oclass = &(struct nouveau_i2c_impl) { + .base.handle = NV_SUBDEV(I2C, 0x94), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_i2c_ctor, .dtor = _nouveau_i2c_dtor, .init = _nouveau_i2c_init, .fini = _nouveau_i2c_fini, }, -}; + .sclass = nv94_i2c_sclass, + .pad_x = &nv04_i2c_pad_oclass, + .pad_s = &nv94_i2c_pad_oclass, + .aux = 4, + .aux_stat = nv94_aux_stat, + .aux_mask = nv94_aux_mask, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nvd0.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nvd0.c index 29967d30f97..364ddb1c5f0 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/nvd0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nvd0.c @@ -42,8 +42,6 @@ nvd0_i2c_sense_sda(struct nouveau_i2c_port *base) static const struct nouveau_i2c_func nvd0_i2c_func = { - .acquire = nv94_i2c_acquire, - .release = nv94_i2c_release, .drive_scl = nv50_i2c_drive_scl, .drive_sda = nv50_i2c_drive_sda, .sense_scl = nvd0_i2c_sense_scl, @@ -75,7 +73,7 @@ nvd0_i2c_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine, return 0; } -static struct nouveau_oclass +struct nouveau_oclass nvd0_i2c_sclass[] = { { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT), .ofuncs = &(struct nouveau_ofuncs) { @@ -96,29 +94,19 @@ nvd0_i2c_sclass[] = { {} }; -static int -nvd0_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nv50_i2c_priv *priv; - int ret; - - ret = nouveau_i2c_create(parent, engine, oclass, nvd0_i2c_sclass, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - return 0; -} - -struct nouveau_oclass -nvd0_i2c_oclass = { - .handle = NV_SUBDEV(I2C, 0xd0), - .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nvd0_i2c_ctor, +struct nouveau_oclass * +nvd0_i2c_oclass = &(struct nouveau_i2c_impl) { + .base.handle = NV_SUBDEV(I2C, 0xd0), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_i2c_ctor, .dtor = _nouveau_i2c_dtor, .init = _nouveau_i2c_init, .fini = _nouveau_i2c_fini, }, -}; + .sclass = nvd0_i2c_sclass, + .pad_x = &nv04_i2c_pad_oclass, + .pad_s = &nv94_i2c_pad_oclass, + .aux = 4, + .aux_stat = nv94_aux_stat, + .aux_mask = nv94_aux_mask, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nve0.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nve0.c new file mode 100644 index 00000000000..cae77e1ad8d --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nve0.c @@ -0,0 +1,72 @@ +/* + * 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 "nv50.h" + +static void +nve0_aux_stat(struct nouveau_i2c *i2c, u32 *hi, u32 *lo, u32 *rq, u32 *tx) +{ + u32 intr = nv_rd32(i2c, 0x00dc60); + u32 stat = nv_rd32(i2c, 0x00dc68) & intr, i; + for (i = 0, *hi = *lo = *rq = *tx = 0; i < 8; i++) { + if ((stat & (1 << (i * 4)))) *hi |= 1 << i; + if ((stat & (2 << (i * 4)))) *lo |= 1 << i; + if ((stat & (4 << (i * 4)))) *rq |= 1 << i; + if ((stat & (8 << (i * 4)))) *tx |= 1 << i; + } + nv_wr32(i2c, 0x00dc60, intr); +} + +static void +nve0_aux_mask(struct nouveau_i2c *i2c, u32 type, u32 mask, u32 data) +{ + u32 temp = nv_rd32(i2c, 0x00dc68), i; + for (i = 0; i < 8; i++) { + if (mask & (1 << i)) { + if (!(data & (1 << i))) { + temp &= ~(type << (i * 4)); + continue; + } + temp |= type << (i * 4); + } + } + nv_wr32(i2c, 0x00dc68, temp); +} + +struct nouveau_oclass * +nve0_i2c_oclass = &(struct nouveau_i2c_impl) { + .base.handle = NV_SUBDEV(I2C, 0xe0), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_i2c_ctor, + .dtor = _nouveau_i2c_dtor, + .init = _nouveau_i2c_init, + .fini = _nouveau_i2c_fini, + }, + .sclass = nvd0_i2c_sclass, + .pad_x = &nv04_i2c_pad_oclass, + .pad_s = &nv94_i2c_pad_oclass, + .aux = 4, + .aux_stat = nve0_aux_stat, + .aux_mask = nve0_aux_mask, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/pad.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/pad.c new file mode 100644 index 00000000000..e9e412477c1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/pad.c @@ -0,0 +1,84 @@ +/* + * Copyright 2014 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 "pad.h" + +int +_nvkm_i2c_pad_fini(struct nouveau_object *object, bool suspend) +{ + struct nvkm_i2c_pad *pad = (void *)object; + DBG("-> NULL\n"); + pad->port = NULL; + return nouveau_object_fini(&pad->base, suspend); +} + +int +_nvkm_i2c_pad_init(struct nouveau_object *object) +{ + struct nvkm_i2c_pad *pad = (void *)object; + DBG("-> PORT:%02x\n", pad->next->index); + pad->port = pad->next; + return nouveau_object_init(&pad->base); +} + +int +nvkm_i2c_pad_create_(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, int index, + int size, void **pobject) +{ + struct nouveau_i2c *i2c = (void *)engine; + struct nouveau_i2c_port *port; + struct nvkm_i2c_pad *pad; + int ret; + + list_for_each_entry(port, &i2c->ports, head) { + pad = nvkm_i2c_pad(port); + if (pad->index == index) { + atomic_inc(&nv_object(pad)->refcount); + *pobject = pad; + return 1; + } + } + + ret = nouveau_object_create_(parent, engine, oclass, 0, size, pobject); + pad = *pobject; + if (ret) + return ret; + + pad->index = index; + return 0; +} + +int +_nvkm_i2c_pad_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 index, + struct nouveau_object **pobject) +{ + struct nvkm_i2c_pad *pad; + int ret; + ret = nvkm_i2c_pad_create(parent, engine, oclass, index, &pad); + *pobject = nv_object(pad); + return ret; +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/pad.h b/drivers/gpu/drm/nouveau/core/subdev/i2c/pad.h new file mode 100644 index 00000000000..452ac10c300 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/pad.h @@ -0,0 +1,58 @@ +#ifndef __NVKM_I2C_PAD_H__ +#define __NVKM_I2C_PAD_H__ + +#include "priv.h" + +struct nvkm_i2c_pad { + struct nouveau_object base; + int index; + struct nouveau_i2c_port *port; + struct nouveau_i2c_port *next; +}; + +static inline struct nvkm_i2c_pad * +nvkm_i2c_pad(struct nouveau_i2c_port *port) +{ + struct nouveau_object *pad = nv_object(port); + while (pad->parent) + pad = pad->parent; + return (void *)pad; +} + +#define nvkm_i2c_pad_create(p,e,o,i,d) \ + nvkm_i2c_pad_create_((p), (e), (o), (i), sizeof(**d), (void **)d) +#define nvkm_i2c_pad_destroy(p) ({ \ + struct nvkm_i2c_pad *_p = (p); \ + _nvkm_i2c_pad_dtor(nv_object(_p)); \ +}) +#define nvkm_i2c_pad_init(p) ({ \ + struct nvkm_i2c_pad *_p = (p); \ + _nvkm_i2c_pad_init(nv_object(_p)); \ +}) +#define nvkm_i2c_pad_fini(p,s) ({ \ + struct nvkm_i2c_pad *_p = (p); \ + _nvkm_i2c_pad_fini(nv_object(_p), (s)); \ +}) + +int nvkm_i2c_pad_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, int index, int, void **); + +int _nvkm_i2c_pad_ctor(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, void *, u32, + struct nouveau_object **); +#define _nvkm_i2c_pad_dtor nouveau_object_destroy +int _nvkm_i2c_pad_init(struct nouveau_object *); +int _nvkm_i2c_pad_fini(struct nouveau_object *, bool); + +#ifndef MSG +#define MSG(l,f,a...) do { \ + struct nvkm_i2c_pad *_pad = (void *)pad; \ + nv_##l(nv_object(_pad)->engine, "PAD:%c:%02x: "f, \ + _pad->index >= 0x100 ? 'X' : 'S', \ + _pad->index >= 0x100 ? _pad->index - 0x100 : _pad->index, ##a); \ +} while(0) +#define DBG(f,a...) MSG(debug, f, ##a) +#define ERR(f,a...) MSG(error, f, ##a) +#endif + +#endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/padnv04.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/padnv04.c new file mode 100644 index 00000000000..2c4b61296dd --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/padnv04.c @@ -0,0 +1,35 @@ +/* + * Copyright 2014 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 "pad.h" + +struct nouveau_oclass +nv04_i2c_pad_oclass = { + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nvkm_i2c_pad_ctor, + .dtor = _nvkm_i2c_pad_dtor, + .init = _nvkm_i2c_pad_init, + .fini = _nvkm_i2c_pad_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/padnv94.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/padnv94.c new file mode 100644 index 00000000000..0dc6753014f --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/padnv94.c @@ -0,0 +1,86 @@ +/* + * Copyright 2014 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 "pad.h" + +struct nv94_i2c_pad { + struct nvkm_i2c_pad base; + int addr; +}; + +static int +nv94_i2c_pad_fini(struct nouveau_object *object, bool suspend) +{ + struct nouveau_i2c *i2c = (void *)object->engine; + struct nv94_i2c_pad *pad = (void *)object; + nv_mask(i2c, 0x00e50c + pad->addr, 0x00000001, 0x00000001); + return nvkm_i2c_pad_fini(&pad->base, suspend); +} + +static int +nv94_i2c_pad_init(struct nouveau_object *object) +{ + struct nouveau_i2c *i2c = (void *)object->engine; + struct nv94_i2c_pad *pad = (void *)object; + + switch (nv_oclass(pad->base.next)->handle) { + case NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX): + nv_mask(i2c, 0x00e500 + pad->addr, 0x0000c003, 0x00000002); + break; + case NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT): + default: + nv_mask(i2c, 0x00e500 + pad->addr, 0x0000c003, 0x0000c001); + break; + } + + nv_mask(i2c, 0x00e50c + pad->addr, 0x00000001, 0x00000000); + return nvkm_i2c_pad_init(&pad->base); +} + +static int +nv94_i2c_pad_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 index, + struct nouveau_object **pobject) +{ + struct nv94_i2c_pad *pad; + int ret; + + ret = nvkm_i2c_pad_create(parent, engine, oclass, index, &pad); + *pobject = nv_object(pad); + if (ret) + return ret; + + pad->addr = index * 0x50;; + return 0; +} + +struct nouveau_oclass +nv94_i2c_pad_oclass = { + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv94_i2c_pad_ctor, + .dtor = _nvkm_i2c_pad_dtor, + .init = nv94_i2c_pad_init, + .fini = nv94_i2c_pad_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/port.h b/drivers/gpu/drm/nouveau/core/subdev/i2c/port.h new file mode 100644 index 00000000000..a8ff6e077af --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/port.h @@ -0,0 +1,15 @@ +#ifndef __NVKM_I2C_PORT_H__ +#define __NVKM_I2C_PORT_H__ + +#include "priv.h" + +#ifndef MSG +#define MSG(l,f,a...) do { \ + struct nouveau_i2c_port *_port = (void *)port; \ + nv_##l(nv_object(_port)->engine, "PORT:%02x: "f, _port->index, ##a); \ +} while(0) +#define DBG(f,a...) MSG(debug, f, ##a) +#define ERR(f,a...) MSG(error, f, ##a) +#endif + +#endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/priv.h b/drivers/gpu/drm/nouveau/core/subdev/i2c/priv.h new file mode 100644 index 00000000000..780090b6425 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/priv.h @@ -0,0 +1,85 @@ +#ifndef __NVKM_I2C_H__ +#define __NVKM_I2C_H__ + +#include <subdev/i2c.h> + +extern struct nouveau_oclass nv04_i2c_pad_oclass; +extern struct nouveau_oclass nv94_i2c_pad_oclass; + +#define nouveau_i2c_port_create(p,e,o,i,a,f,d) \ + nouveau_i2c_port_create_((p), (e), (o), (i), (a), (f), \ + sizeof(**d), (void **)d) +#define nouveau_i2c_port_destroy(p) ({ \ + struct nouveau_i2c_port *port = (p); \ + _nouveau_i2c_port_dtor(nv_object(i2c)); \ +}) +#define nouveau_i2c_port_init(p) \ + nouveau_object_init(&(p)->base) +#define nouveau_i2c_port_fini(p,s) \ + nouveau_object_fini(&(p)->base, (s)) + +int nouveau_i2c_port_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, u8, + const struct i2c_algorithm *, + const struct nouveau_i2c_func *, + int, void **); +void _nouveau_i2c_port_dtor(struct nouveau_object *); +#define _nouveau_i2c_port_init nouveau_object_init +int _nouveau_i2c_port_fini(struct nouveau_object *, bool); + +#define nouveau_i2c_create(p,e,o,d) \ + nouveau_i2c_create_((p), (e), (o), sizeof(**d), (void **)d) +#define nouveau_i2c_destroy(p) ({ \ + struct nouveau_i2c *i2c = (p); \ + _nouveau_i2c_dtor(nv_object(i2c)); \ +}) +#define nouveau_i2c_init(p) ({ \ + struct nouveau_i2c *i2c = (p); \ + _nouveau_i2c_init(nv_object(i2c)); \ +}) +#define nouveau_i2c_fini(p,s) ({ \ + struct nouveau_i2c *i2c = (p); \ + _nouveau_i2c_fini(nv_object(i2c), (s)); \ +}) + +int nouveau_i2c_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, int, void **); +int _nouveau_i2c_ctor(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, void *, u32, + struct nouveau_object **); +void _nouveau_i2c_dtor(struct nouveau_object *); +int _nouveau_i2c_init(struct nouveau_object *); +int _nouveau_i2c_fini(struct nouveau_object *, bool); + +extern struct nouveau_oclass nouveau_anx9805_sclass[]; +extern struct nouveau_oclass nvd0_i2c_sclass[]; + +extern const struct i2c_algorithm nouveau_i2c_bit_algo; +extern const struct i2c_algorithm nouveau_i2c_aux_algo; + +struct nouveau_i2c_impl { + struct nouveau_oclass base; + + /* supported i2c port classes */ + struct nouveau_oclass *sclass; + struct nouveau_oclass *pad_x; + struct nouveau_oclass *pad_s; + + /* number of native dp aux channels present */ + int aux; + + /* read and ack pending interrupts, returning only data + * for ports that have not been masked off, while still + * performing the ack for anything that was pending. + */ + void (*aux_stat)(struct nouveau_i2c *, u32 *, u32 *, u32 *, u32 *); + + /* mask on/off interrupt types for a given set of auxch + */ + void (*aux_mask)(struct nouveau_i2c *, u32, u32, u32); +}; + +void nv94_aux_stat(struct nouveau_i2c *, u32 *, u32 *, u32 *, u32 *); +void nv94_aux_mask(struct nouveau_i2c *, u32, u32, u32); + +#endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/ibus/gk20a.c b/drivers/gpu/drm/nouveau/core/subdev/ibus/gk20a.c new file mode 100644 index 00000000000..245f0ebaa6a --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/ibus/gk20a.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <subdev/ibus.h> +#include <subdev/timer.h> + +struct gk20a_ibus_priv { + struct nouveau_ibus base; +}; + +static void +gk20a_ibus_init_priv_ring(struct gk20a_ibus_priv *priv) +{ + nv_mask(priv, 0x137250, 0x3f, 0); + + nv_mask(priv, 0x000200, 0x20, 0); + usleep_range(20, 30); + nv_mask(priv, 0x000200, 0x20, 0x20); + + nv_wr32(priv, 0x12004c, 0x4); + nv_wr32(priv, 0x122204, 0x2); + nv_rd32(priv, 0x122204); +} + +static void +gk20a_ibus_intr(struct nouveau_subdev *subdev) +{ + struct gk20a_ibus_priv *priv = (void *)subdev; + u32 status0 = nv_rd32(priv, 0x120058); + + if (status0 & 0x7) { + nv_debug(priv, "resetting priv ring\n"); + gk20a_ibus_init_priv_ring(priv); + } + + /* Acknowledge interrupt */ + nv_mask(priv, 0x12004c, 0x2, 0x2); + + if (!nv_wait(subdev, 0x12004c, 0x3f, 0x00)) + nv_warn(priv, "timeout waiting for ringmaster ack\n"); +} + +static int +gk20a_ibus_init(struct nouveau_object *object) +{ + struct gk20a_ibus_priv *priv = (void *)object; + int ret; + + ret = _nouveau_ibus_init(object); + if (ret) + return ret; + + gk20a_ibus_init_priv_ring(priv); + + return 0; +} + +static int +gk20a_ibus_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct gk20a_ibus_priv *priv; + int ret; + + ret = nouveau_ibus_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->intr = gk20a_ibus_intr; + return 0; +} + +struct nouveau_oclass +gk20a_ibus_oclass = { + .handle = NV_SUBDEV(IBUS, 0xea), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = gk20a_ibus_ctor, + .dtor = _nouveau_ibus_dtor, + .init = gk20a_ibus_init, + .fini = _nouveau_ibus_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/ibus/nve0.c b/drivers/gpu/drm/nouveau/core/subdev/ibus/nve0.c index 7120124dcea..ebef970a064 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/ibus/nve0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/ibus/nve0.c @@ -95,6 +95,23 @@ nve0_ibus_intr(struct nouveau_subdev *subdev) } static int +nve0_ibus_init(struct nouveau_object *object) +{ + struct nve0_ibus_priv *priv = (void *)object; + int ret = nouveau_ibus_init(&priv->base); + if (ret == 0) { + nv_mask(priv, 0x122318, 0x0003ffff, 0x00001000); + nv_mask(priv, 0x12231c, 0x0003ffff, 0x00000200); + nv_mask(priv, 0x122310, 0x0003ffff, 0x00000800); + nv_mask(priv, 0x122348, 0x0003ffff, 0x00000100); + nv_mask(priv, 0x1223b0, 0x0003ffff, 0x00000fff); + nv_mask(priv, 0x122348, 0x0003ffff, 0x00000200); + nv_mask(priv, 0x122358, 0x0003ffff, 0x00002880); + } + return ret; +} + +static int nve0_ibus_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, struct nouveau_object **pobject) @@ -117,7 +134,7 @@ nve0_ibus_oclass = { .ofuncs = &(struct nouveau_ofuncs) { .ctor = nve0_ibus_ctor, .dtor = _nouveau_ibus_dtor, - .init = _nouveau_ibus_init, + .init = nve0_ibus_init, .fini = _nouveau_ibus_fini, }, }; diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c index e8822a934c4..9ca93e2718f 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c @@ -26,6 +26,7 @@ const struct nouveau_mc_intr nv50_mc_intr[] = { + { 0x04000000, NVDEV_ENGINE_DISP }, /* DISP before FIFO, so pageflip-timestamping works! */ { 0x00000001, NVDEV_ENGINE_MPEG }, { 0x00000100, NVDEV_ENGINE_FIFO }, { 0x00001000, NVDEV_ENGINE_GR }, @@ -33,8 +34,8 @@ nv50_mc_intr[] = { { 0x00008000, NVDEV_ENGINE_BSP }, /* NV84- */ { 0x00020000, NVDEV_ENGINE_VP }, /* NV84- */ { 0x00100000, NVDEV_SUBDEV_TIMER }, - { 0x00200000, NVDEV_SUBDEV_GPIO }, - { 0x04000000, NVDEV_ENGINE_DISP }, + { 0x00200000, NVDEV_SUBDEV_GPIO }, /* PMGR->GPIO */ + { 0x00200000, NVDEV_SUBDEV_I2C }, /* PMGR->I2C/AUX */ { 0x10000000, NVDEV_SUBDEV_BUS }, { 0x80000000, NVDEV_ENGINE_SW }, { 0x0002d101, NVDEV_SUBDEV_FB }, diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c index f8a6f18e2d3..3c76d9038f3 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c @@ -26,6 +26,7 @@ static const struct nouveau_mc_intr nv98_mc_intr[] = { + { 0x04000000, NVDEV_ENGINE_DISP }, /* DISP first, so pageflip timestamps work */ { 0x00000001, NVDEV_ENGINE_PPP }, { 0x00000100, NVDEV_ENGINE_FIFO }, { 0x00001000, NVDEV_ENGINE_GR }, @@ -35,9 +36,9 @@ nv98_mc_intr[] = { { 0x00040000, NVDEV_SUBDEV_PWR }, /* NVA3:NVC0 */ { 0x00080000, NVDEV_SUBDEV_THERM }, /* NVA3:NVC0 */ { 0x00100000, NVDEV_SUBDEV_TIMER }, - { 0x00200000, NVDEV_SUBDEV_GPIO }, + { 0x00200000, NVDEV_SUBDEV_GPIO }, /* PMGR->GPIO */ + { 0x00200000, NVDEV_SUBDEV_I2C }, /* PMGR->I2C/AUX */ { 0x00400000, NVDEV_ENGINE_COPY0 }, /* NVA3- */ - { 0x04000000, NVDEV_ENGINE_DISP }, { 0x10000000, NVDEV_SUBDEV_BUS }, { 0x80000000, NVDEV_ENGINE_SW }, { 0x0042d101, NVDEV_SUBDEV_FB }, diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c index 34472d31709..f9c6a678b47 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c @@ -26,6 +26,7 @@ const struct nouveau_mc_intr nvc0_mc_intr[] = { + { 0x04000000, NVDEV_ENGINE_DISP }, /* DISP first, so pageflip timestamps work. */ { 0x00000001, NVDEV_ENGINE_PPP }, { 0x00000020, NVDEV_ENGINE_COPY0 }, { 0x00000040, NVDEV_ENGINE_COPY1 }, @@ -37,10 +38,10 @@ nvc0_mc_intr[] = { { 0x00040000, NVDEV_SUBDEV_THERM }, { 0x00020000, NVDEV_ENGINE_VP }, { 0x00100000, NVDEV_SUBDEV_TIMER }, - { 0x00200000, NVDEV_SUBDEV_GPIO }, + { 0x00200000, NVDEV_SUBDEV_GPIO }, /* PMGR->GPIO */ + { 0x00200000, NVDEV_SUBDEV_I2C }, /* PMGR->I2C/AUX */ { 0x01000000, NVDEV_SUBDEV_PWR }, { 0x02000000, NVDEV_SUBDEV_LTCG }, - { 0x04000000, NVDEV_ENGINE_DISP }, { 0x08000000, NVDEV_SUBDEV_FB }, { 0x10000000, NVDEV_SUBDEV_BUS }, { 0x40000000, NVDEV_SUBDEV_IBUS }, diff --git a/drivers/gpu/drm/nouveau/core/subdev/mxm/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/mxm/nv50.c index 64f8b4702bf..fcaabe8456e 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mxm/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mxm/nv50.c @@ -150,7 +150,7 @@ mxm_dcb_sanitise_entry(struct nouveau_bios *bios, void *data, int idx, u16 pdcb) * common example is DP->eDP. */ conn = bios->data; - conn += dcb_conn(bios, (ctx.outp[0] & 0x0000f000) >> 12, &ver, &len); + conn += nvbios_connEe(bios, (ctx.outp[0] & 0x0000f000) >> 12, &ver, &len); type = conn[0]; switch (ctx.desc.conn_type) { case 0x01: /* LVDS */ diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/host.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/host.fuc index 2284ecb1c9b..c2bb616a8da 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/host.fuc +++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/host.fuc @@ -83,7 +83,7 @@ host_send: // increment GET add b32 $r1 0x1 and $r14 $r1 #fifo_qmaskf - nv_iowr(NV_PPWR_FIFO_GET(0), $r1) + nv_iowr(NV_PPWR_FIFO_GET(0), $r14) bra #host_send host_send_done: ret diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc.h b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc.h index 4bd43a99fdc..39a5dc150a0 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc.h +++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc.h @@ -1018,7 +1018,7 @@ uint32_t nv108_pwr_code[] = { 0xb600023f, 0x1ec40110, 0x04b0400f, - 0xbd0001f6, + 0xbd000ef6, 0xc70ef404, /* 0x0328: host_send_done */ /* 0x032a: host_recv */ diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc.h b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc.h index 5a73fa62097..254205cd516 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc.h +++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc.h @@ -1124,7 +1124,7 @@ uint32_t nva3_pwr_code[] = { 0x0f1ec401, 0x04b007f1, 0xd00604b6, - 0x04bd0001, + 0x04bd000e, /* 0x03cb: host_send_done */ 0xf8ba0ef4, /* 0x03cd: host_recv */ diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc.h b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc.h index 4dba00d2dd1..7ac87405d01 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc.h @@ -1124,7 +1124,7 @@ uint32_t nvc0_pwr_code[] = { 0x0f1ec401, 0x04b007f1, 0xd00604b6, - 0x04bd0001, + 0x04bd000e, /* 0x03cb: host_send_done */ 0xf8ba0ef4, /* 0x03cd: host_recv */ diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc.h b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc.h index 5e24c6bc041..cd9ff1a7328 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc.h @@ -1033,7 +1033,7 @@ uint32_t nvd0_pwr_code[] = { 0xb6026b21, 0x1ec40110, 0xb007f10f, - 0x0001d004, + 0x000ed004, 0x0ef404bd, /* 0x0365: host_send_done */ /* 0x0367: host_recv */ diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c b/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c index 7610fc5f8fa..ca9ad9fd47b 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c @@ -60,9 +60,9 @@ static struct nouveau_i2c_board_info nv_board_infos[] = { { { I2C_BOARD_INFO("w83l785ts", 0x2d) }, 0 }, { { I2C_BOARD_INFO("w83781d", 0x2d) }, 0 }, - { { I2C_BOARD_INFO("adt7473", 0x2e) }, 20 }, - { { I2C_BOARD_INFO("adt7473", 0x2d) }, 20 }, - { { I2C_BOARD_INFO("adt7473", 0x2c) }, 20 }, + { { I2C_BOARD_INFO("adt7473", 0x2e) }, 40 }, + { { I2C_BOARD_INFO("adt7473", 0x2d) }, 40 }, + { { I2C_BOARD_INFO("adt7473", 0x2c) }, 40 }, { { I2C_BOARD_INFO("f75375", 0x2e) }, 0 }, { { I2C_BOARD_INFO("lm99", 0x4c) }, 0 }, { { I2C_BOARD_INFO("lm90", 0x4c) }, 0 }, diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c index 3b2c4580098..0478b2e3fb1 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c @@ -36,7 +36,7 @@ nva3_therm_fan_sense(struct nouveau_therm *therm) u32 tach = nv_rd32(therm, 0x00e728) & 0x0000ffff; u32 ctrl = nv_rd32(therm, 0x00e720); if (ctrl & 0x00000001) - return tach * 60; + return tach * 60 / 2; return -ENODEV; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c index 43fec17ea54..bbf117be572 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c @@ -40,6 +40,7 @@ pwm_info(struct nouveau_therm *therm, int line) case 0x00: return 2; case 0x19: return 1; case 0x1c: return 0; + case 0x1e: return 2; default: break; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/temp.c b/drivers/gpu/drm/nouveau/core/subdev/therm/temp.c index cfde9eb44ad..6212537b90c 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/temp.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/temp.c @@ -192,11 +192,11 @@ alarm_timer_callback(struct nouveau_alarm *alarm) nouveau_therm_threshold_hyst_polling(therm, &sensor->thrs_shutdown, NOUVEAU_THERM_THRS_SHUTDOWN); + spin_unlock_irqrestore(&priv->sensor.alarm_program_lock, flags); + /* schedule the next poll in one second */ if (therm->temp_get(therm) >= 0 && list_empty(&alarm->head)) - ptimer->alarm(ptimer, 1000 * 1000 * 1000, alarm); - - spin_unlock_irqrestore(&priv->sensor.alarm_program_lock, flags); + ptimer->alarm(ptimer, 1000000000ULL, alarm); } void diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c index 83face3f608..279206997e5 100644 --- a/drivers/gpu/drm/nouveau/nouveau_acpi.c +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c @@ -389,9 +389,6 @@ bool nouveau_acpi_rom_supported(struct pci_dev *pdev) acpi_status status; acpi_handle dhandle, rom_handle; - if (!nouveau_dsm_priv.dsm_detected && !nouveau_dsm_priv.optimus_detected) - return false; - dhandle = ACPI_HANDLE(&pdev->dev); if (!dhandle) return false; diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c index 630f6e84fc0..2c1e4aad7da 100644 --- a/drivers/gpu/drm/nouveau/nouveau_backlight.c +++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c @@ -31,7 +31,6 @@ */ #include <linux/backlight.h> -#include <linux/acpi.h> #include "nouveau_drm.h" #include "nouveau_reg.h" @@ -222,14 +221,6 @@ nouveau_backlight_init(struct drm_device *dev) struct nouveau_device *device = nv_device(drm->device); struct drm_connector *connector; -#ifdef CONFIG_ACPI - if (acpi_video_backlight_support()) { - NV_INFO(drm, "ACPI backlight interface available, " - "not registering our own\n"); - return 0; - } -#endif - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS && connector->connector_type != DRM_MODE_CONNECTOR_eDP) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 6ecea9b2b15..1fa222e8f00 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -44,6 +44,7 @@ #include <subdev/i2c.h> #include <subdev/gpio.h> +#include <engine/disp.h> MODULE_PARM_DESC(tv_disable, "Disable TV-out detection"); static int nouveau_tv_disable = 0; @@ -75,7 +76,8 @@ find_encoder(struct drm_connector *connector, int type) continue; nv_encoder = nouveau_encoder(obj_to_encoder(obj)); - if (type == DCB_OUTPUT_ANY || nv_encoder->dcb->type == type) + if (type == DCB_OUTPUT_ANY || + (nv_encoder->dcb && nv_encoder->dcb->type == type)) return nv_encoder; } @@ -100,22 +102,24 @@ static void nouveau_connector_destroy(struct drm_connector *connector) { struct nouveau_connector *nv_connector = nouveau_connector(connector); - nouveau_event_ref(NULL, &nv_connector->hpd_func); + nouveau_event_ref(NULL, &nv_connector->hpd); kfree(nv_connector->edid); drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); + if (nv_connector->aux.transfer) + drm_dp_aux_unregister(&nv_connector->aux); kfree(connector); } -static struct nouveau_i2c_port * -nouveau_connector_ddc_detect(struct drm_connector *connector, - struct nouveau_encoder **pnv_encoder) +static struct nouveau_encoder * +nouveau_connector_ddc_detect(struct drm_connector *connector) { struct drm_device *dev = connector->dev; struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_gpio *gpio = nouveau_gpio(drm->device); - struct nouveau_i2c_port *port = NULL; + struct nouveau_encoder *nv_encoder; + struct drm_mode_object *obj; int i, panel = -ENODEV; /* eDP panels need powering on by us (if the VBIOS doesn't default it @@ -130,13 +134,9 @@ nouveau_connector_ddc_detect(struct drm_connector *connector, } } - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - struct nouveau_encoder *nv_encoder; - struct drm_mode_object *obj; - int id; - - id = connector->encoder_ids[i]; - if (!id) + for (i = 0; nv_encoder = NULL, i < DRM_CONNECTOR_MAX_ENCODER; i++) { + int id = connector->encoder_ids[i]; + if (id == 0) break; obj = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER); @@ -144,22 +144,24 @@ nouveau_connector_ddc_detect(struct drm_connector *connector, continue; nv_encoder = nouveau_encoder(obj_to_encoder(obj)); - port = nv_encoder->i2c; - if (port && nv_probe_i2c(port, 0x50)) { - *pnv_encoder = nv_encoder; - break; + if (nv_encoder->dcb->type == DCB_OUTPUT_DP) { + int ret = nouveau_dp_detect(nv_encoder); + if (ret == 0) + break; + } else + if (nv_encoder->i2c) { + if (nv_probe_i2c(nv_encoder->i2c, 0x50)) + break; } - - port = NULL; } /* eDP panel not detected, restore panel power GPIO to previous * state to avoid confusing the SOR for other output types. */ - if (!port && panel == 0) + if (!nv_encoder && panel == 0) gpio->set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, panel); - return port; + return nv_encoder; } static struct nouveau_encoder * @@ -258,8 +260,8 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) if (ret < 0 && ret != -EACCES) return conn_status; - i2c = nouveau_connector_ddc_detect(connector, &nv_encoder); - if (i2c) { + nv_encoder = nouveau_connector_ddc_detect(connector); + if (nv_encoder && (i2c = nv_encoder->i2c) != NULL) { nv_connector->edid = drm_get_edid(connector, &i2c->adapter); drm_mode_connector_update_edid_property(connector, nv_connector->edid); @@ -269,14 +271,6 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) goto detect_analog; } - if (nv_encoder->dcb->type == DCB_OUTPUT_DP && - !nouveau_dp_detect(to_drm_encoder(nv_encoder))) { - NV_ERROR(drm, "Detected %s, but failed init\n", - connector->name); - conn_status = connector_status_disconnected; - goto out; - } - /* Override encoder type for DVI-I based on whether EDID * says the display is digital or analog, both use the * same i2c channel so the value returned from ddc_detect @@ -912,33 +906,103 @@ nouveau_connector_funcs_lvds = { }; static void +nouveau_connector_dp_dpms(struct drm_connector *connector, int mode) +{ + struct nouveau_encoder *nv_encoder = NULL; + + if (connector->encoder) + nv_encoder = nouveau_encoder(connector->encoder); + if (nv_encoder && nv_encoder->dcb && + nv_encoder->dcb->type == DCB_OUTPUT_DP) { + if (mode == DRM_MODE_DPMS_ON) { + u8 data = DP_SET_POWER_D0; + nv_wraux(nv_encoder->i2c, DP_SET_POWER, &data, 1); + usleep_range(1000, 2000); + } else { + u8 data = DP_SET_POWER_D3; + nv_wraux(nv_encoder->i2c, DP_SET_POWER, &data, 1); + } + } + + drm_helper_connector_dpms(connector, mode); +} + +static const struct drm_connector_funcs +nouveau_connector_funcs_dp = { + .dpms = nouveau_connector_dp_dpms, + .save = NULL, + .restore = NULL, + .detect = nouveau_connector_detect, + .destroy = nouveau_connector_destroy, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = nouveau_connector_set_property, + .force = nouveau_connector_force +}; + +static void nouveau_connector_hotplug_work(struct work_struct *work) { struct nouveau_connector *nv_connector = - container_of(work, struct nouveau_connector, hpd_work); + container_of(work, typeof(*nv_connector), work); struct drm_connector *connector = &nv_connector->base; - struct drm_device *dev = connector->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_gpio *gpio = nouveau_gpio(drm->device); - bool plugged = gpio->get(gpio, 0, nv_connector->hpd.func, 0xff); + struct nouveau_drm *drm = nouveau_drm(connector->dev); + const char *name = connector->name; - NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", - connector->name); + if (nv_connector->status & NVKM_HPD_IRQ) { + } else { + bool plugged = (nv_connector->status != NVKM_HPD_UNPLUG); - if (plugged) - drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); - else - drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); + NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name); - drm_helper_hpd_irq_event(dev); + if (plugged) + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); + else + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); + drm_helper_hpd_irq_event(connector->dev); + } + + nouveau_event_get(nv_connector->hpd); } static int -nouveau_connector_hotplug(void *data, int index) +nouveau_connector_hotplug(void *data, u32 type, int index) { struct nouveau_connector *nv_connector = data; - schedule_work(&nv_connector->hpd_work); - return NVKM_EVENT_KEEP; + nv_connector->status = type; + schedule_work(&nv_connector->work); + return NVKM_EVENT_DROP; +} + +static ssize_t +nouveau_connector_aux_xfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) +{ + struct nouveau_connector *nv_connector = + container_of(aux, typeof(*nv_connector), aux); + struct nouveau_encoder *nv_encoder; + struct nouveau_i2c_port *port; + int ret; + + nv_encoder = find_encoder(&nv_connector->base, DCB_OUTPUT_DP); + if (!nv_encoder || !(port = nv_encoder->i2c)) + return -ENODEV; + if (WARN_ON(msg->size > 16)) + return -E2BIG; + if (msg->size == 0) + return msg->size; + + ret = nouveau_i2c(port)->acquire(port, 0); + if (ret) + return ret; + + ret = port->func->aux(port, false, msg->request, msg->address, + msg->buffer, msg->size); + nouveau_i2c(port)->release(port); + if (ret >= 0) { + msg->reply = ret; + return msg->size; + } + + return ret; } static int @@ -974,9 +1038,9 @@ nouveau_connector_create(struct drm_device *dev, int index) { const struct drm_connector_funcs *funcs = &nouveau_connector_funcs; struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_gpio *gpio = nouveau_gpio(drm->device); struct nouveau_display *disp = nouveau_display(dev); struct nouveau_connector *nv_connector = NULL; + struct nouveau_disp *pdisp = nouveau_disp(drm->device); struct drm_connector *connector; int type, ret = 0; bool dummy; @@ -992,33 +1056,15 @@ nouveau_connector_create(struct drm_device *dev, int index) return ERR_PTR(-ENOMEM); connector = &nv_connector->base; - INIT_WORK(&nv_connector->hpd_work, nouveau_connector_hotplug_work); nv_connector->index = index; /* attempt to parse vbios connector type and hotplug gpio */ nv_connector->dcb = olddcb_conn(dev, index); if (nv_connector->dcb) { - static const u8 hpd[16] = { - 0xff, 0x07, 0x08, 0xff, 0xff, 0x51, 0x52, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x5e, 0x5f, 0x60, - }; - u32 entry = ROM16(nv_connector->dcb[0]); if (olddcb_conntab(dev)[3] >= 4) entry |= (u32)ROM16(nv_connector->dcb[2]) << 16; - ret = gpio->find(gpio, 0, hpd[ffs((entry & 0x07033000) >> 12)], - DCB_GPIO_UNUSED, &nv_connector->hpd); - if (ret) - nv_connector->hpd.func = DCB_GPIO_UNUSED; - - if (nv_connector->hpd.func != DCB_GPIO_UNUSED) { - nouveau_event_new(gpio->events, nv_connector->hpd.line, - nouveau_connector_hotplug, - nv_connector, - &nv_connector->hpd_func); - } - nv_connector->type = nv_connector->dcb[0]; if (drm_conntype_from_dcb(nv_connector->type) == DRM_MODE_CONNECTOR_Unknown) { @@ -1040,7 +1086,6 @@ nouveau_connector_create(struct drm_device *dev, int index) } } else { nv_connector->type = DCB_CONNECTOR_NONE; - nv_connector->hpd.func = DCB_GPIO_UNUSED; } /* no vbios data, or an unknown dcb connector type - attempt to @@ -1080,8 +1125,8 @@ nouveau_connector_create(struct drm_device *dev, int index) } } - type = drm_conntype_from_dcb(nv_connector->type); - if (type == DRM_MODE_CONNECTOR_LVDS) { + switch ((type = drm_conntype_from_dcb(nv_connector->type))) { + case DRM_MODE_CONNECTOR_LVDS: ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &dummy); if (ret) { NV_ERROR(drm, "Error parsing LVDS table, disabling\n"); @@ -1090,8 +1135,23 @@ nouveau_connector_create(struct drm_device *dev, int index) } funcs = &nouveau_connector_funcs_lvds; - } else { + break; + case DRM_MODE_CONNECTOR_DisplayPort: + case DRM_MODE_CONNECTOR_eDP: + nv_connector->aux.dev = dev->dev; + nv_connector->aux.transfer = nouveau_connector_aux_xfer; + ret = drm_dp_aux_register(&nv_connector->aux); + if (ret) { + NV_ERROR(drm, "failed to register aux channel\n"); + kfree(nv_connector); + return ERR_PTR(ret); + } + + funcs = &nouveau_connector_funcs_dp; + break; + default: funcs = &nouveau_connector_funcs; + break; } /* defaults, will get overridden in detect() */ @@ -1166,10 +1226,16 @@ nouveau_connector_create(struct drm_device *dev, int index) break; } - connector->polled = DRM_CONNECTOR_POLL_CONNECT; - if (nv_connector->hpd.func != DCB_GPIO_UNUSED) + ret = nouveau_event_new(pdisp->hpd, NVKM_HPD, index, + nouveau_connector_hotplug, + nv_connector, &nv_connector->hpd); + if (ret) + connector->polled = DRM_CONNECTOR_POLL_CONNECT; + else connector->polled = DRM_CONNECTOR_POLL_HPD; + INIT_WORK(&nv_connector->work, nouveau_connector_hotplug_work); + drm_sysfs_connector_add(connector); return connector; } diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h index 264a778f473..8861b6c579a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.h +++ b/drivers/gpu/drm/nouveau/nouveau_connector.h @@ -28,12 +28,12 @@ #define __NOUVEAU_CONNECTOR_H__ #include <drm/drm_edid.h> +#include <drm/drm_dp_helper.h> #include "nouveau_crtc.h" #include <core/event.h> #include <subdev/bios.h> -#include <subdev/bios/gpio.h> struct nouveau_i2c_port; @@ -67,9 +67,11 @@ struct nouveau_connector { u8 index; u8 *dcb; - struct dcb_gpio_func hpd; - struct work_struct hpd_work; - struct nouveau_eventh *hpd_func; + struct nouveau_eventh *hpd; + u32 status; + struct work_struct work; + + struct drm_dp_aux aux; int dithering_mode; int dithering_depth; diff --git a/drivers/gpu/drm/nouveau/nouveau_crtc.h b/drivers/gpu/drm/nouveau/nouveau_crtc.h index d1e5890784d..a0534489d23 100644 --- a/drivers/gpu/drm/nouveau/nouveau_crtc.h +++ b/drivers/gpu/drm/nouveau/nouveau_crtc.h @@ -74,7 +74,7 @@ struct nouveau_crtc { static inline struct nouveau_crtc *nouveau_crtc(struct drm_crtc *crtc) { - return container_of(crtc, struct nouveau_crtc, base); + return crtc ? container_of(crtc, struct nouveau_crtc, base) : NULL; } static inline struct drm_crtc *to_drm_crtc(struct nouveau_crtc *crtc) diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 3ff030dc1ee..47ad74255bf 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -42,7 +42,7 @@ #include <core/class.h> static int -nouveau_display_vblank_handler(void *data, int head) +nouveau_display_vblank_handler(void *data, u32 type, int head) { struct nouveau_drm *drm = data; drm_handle_vblank(drm->dev, head); @@ -178,7 +178,7 @@ nouveau_display_vblank_init(struct drm_device *dev) return -ENOMEM; for (i = 0; i < dev->mode_config.num_crtc; i++) { - ret = nouveau_event_new(pdisp->vblank, i, + ret = nouveau_event_new(pdisp->vblank, 1, i, nouveau_display_vblank_handler, drm, &disp->vblank[i]); if (ret) { @@ -393,7 +393,7 @@ nouveau_display_init(struct drm_device *dev) /* enable hotplug interrupts */ list_for_each_entry(connector, &dev->mode_config.connector_list, head) { struct nouveau_connector *conn = nouveau_connector(connector); - if (conn->hpd_func) nouveau_event_get(conn->hpd_func); + if (conn->hpd) nouveau_event_get(conn->hpd); } return ret; @@ -408,7 +408,7 @@ nouveau_display_fini(struct drm_device *dev) /* disable hotplug interrupts */ list_for_each_entry(connector, &dev->mode_config.connector_list, head) { struct nouveau_connector *conn = nouveau_connector(connector); - if (conn->hpd_func) nouveau_event_put(conn->hpd_func); + if (conn->hpd) nouveau_event_put(conn->hpd); } drm_kms_helper_poll_disable(dev); @@ -736,6 +736,9 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, fb->bits_per_pixel, fb->pitches[0], crtc->x, crtc->y, new_bo->bo.offset }; + /* Keep vblanks on during flip, for the target crtc of this flip */ + drm_vblank_get(dev, nouveau_crtc(crtc)->index); + /* Emit a page flip */ if (nv_device(drm->device)->card_type >= NV_50) { ret = nv50_display_flip_next(crtc, fb, chan, swap_interval); @@ -764,9 +767,9 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, } ret = nouveau_page_flip_emit(chan, old_bo, new_bo, s, &fence); - mutex_unlock(&chan->cli->mutex); if (ret) goto fail_unreserve; + mutex_unlock(&chan->cli->mutex); /* Update the crtc struct and cleanup */ crtc->primary->fb = fb; @@ -779,6 +782,7 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, return 0; fail_unreserve: + drm_vblank_put(dev, nouveau_crtc(crtc)->index); ttm_bo_unreserve(&old_bo->bo); fail_unpin: mutex_unlock(&chan->cli->mutex); @@ -798,6 +802,7 @@ nouveau_finish_page_flip(struct nouveau_channel *chan, struct drm_device *dev = drm->dev; struct nouveau_page_flip_state *s; unsigned long flags; + int crtcid = -1; spin_lock_irqsave(&dev->event_lock, flags); @@ -808,8 +813,16 @@ nouveau_finish_page_flip(struct nouveau_channel *chan, } s = list_first_entry(&fctx->flip, struct nouveau_page_flip_state, head); - if (s->event) - drm_send_vblank_event(dev, s->crtc, s->event); + if (s->event) { + /* Vblank timestamps/counts are only correct on >= NV-50 */ + if (nv_device(drm->device)->card_type >= NV_50) + crtcid = s->crtc; + + drm_send_vblank_event(dev, crtcid, s->event); + } + + /* Give up ownership of vblank for page-flipped crtc */ + drm_vblank_put(dev, s->crtc); list_del(&s->head); if (ps) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 36fd2250056..5675ffc175a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -55,11 +55,10 @@ nouveau_dp_probe_oui(struct drm_device *dev, struct nouveau_i2c_port *auxch, } -bool -nouveau_dp_detect(struct drm_encoder *encoder) +int +nouveau_dp_detect(struct nouveau_encoder *nv_encoder) { - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_device *dev = encoder->dev; + struct drm_device *dev = nv_encoder->base.base.dev; struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_i2c_port *auxch; u8 *dpcd = nv_encoder->dp.dpcd; @@ -67,11 +66,11 @@ nouveau_dp_detect(struct drm_encoder *encoder) auxch = nv_encoder->i2c; if (!auxch) - return false; + return -ENODEV; ret = nv_rdaux(auxch, DP_DPCD_REV, dpcd, 8); if (ret) - return false; + return ret; nv_encoder->dp.link_bw = 27000 * dpcd[1]; nv_encoder->dp.link_nr = dpcd[2] & DP_MAX_LANE_COUNT_MASK; @@ -91,6 +90,5 @@ nouveau_dp_detect(struct drm_encoder *encoder) nv_encoder->dp.link_nr, nv_encoder->dp.link_bw); nouveau_dp_probe_oui(dev, auxch, dpcd); - - return true; + return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index ddd83756b9a..5425ffe3931 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -652,12 +652,12 @@ int nouveau_pmops_resume(struct device *dev) ret = nouveau_do_resume(drm_dev); if (ret) return ret; - if (drm_dev->mode_config.num_crtc) - nouveau_fbcon_set_suspend(drm_dev, 0); - nouveau_fbcon_zfill_all(drm_dev); - if (drm_dev->mode_config.num_crtc) + if (drm_dev->mode_config.num_crtc) { nouveau_display_resume(drm_dev); + nouveau_fbcon_set_suspend(drm_dev, 0); + } + return 0; } @@ -683,11 +683,12 @@ static int nouveau_pmops_thaw(struct device *dev) ret = nouveau_do_resume(drm_dev); if (ret) return ret; - if (drm_dev->mode_config.num_crtc) - nouveau_fbcon_set_suspend(drm_dev, 0); - nouveau_fbcon_zfill_all(drm_dev); - if (drm_dev->mode_config.num_crtc) + + if (drm_dev->mode_config.num_crtc) { nouveau_display_resume(drm_dev); + nouveau_fbcon_set_suspend(drm_dev, 0); + } + return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index 24660c0f713..5f0e37fc284 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -46,6 +46,7 @@ struct nouveau_encoder { /* different to drm_encoder.crtc, this reflects what's * actually programmed on the hw, not the proposed crtc */ struct drm_crtc *crtc; + u32 ctrl; struct drm_display_mode mode; int last_dpms; @@ -84,9 +85,7 @@ get_slave_funcs(struct drm_encoder *enc) } /* nouveau_dp.c */ -bool nouveau_dp_detect(struct drm_encoder *); -void nouveau_dp_dpms(struct drm_encoder *, int mode, u32 datarate, - struct nouveau_object *); +int nouveau_dp_detect(struct nouveau_encoder *); struct nouveau_connector * nouveau_encoder_connector_get(struct nouveau_encoder *encoder); diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index 64a42cfd371..191665ee7f5 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -531,17 +531,10 @@ nouveau_fbcon_set_suspend(struct drm_device *dev, int state) if (state == 1) nouveau_fbcon_save_disable_accel(dev); fb_set_suspend(drm->fbcon->helper.fbdev, state); - if (state == 0) + if (state == 0) { nouveau_fbcon_restore_accel(dev); + nouveau_fbcon_zfill(dev, drm->fbcon); + } console_unlock(); } } - -void -nouveau_fbcon_zfill_all(struct drm_device *dev) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - if (drm->fbcon) { - nouveau_fbcon_zfill(dev, drm->fbcon); - } -} diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.h b/drivers/gpu/drm/nouveau/nouveau_fbcon.h index fdfc0c94fbc..fcff797d208 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.h +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.h @@ -61,7 +61,6 @@ void nouveau_fbcon_gpu_lockup(struct fb_info *info); int nouveau_fbcon_init(struct drm_device *dev); void nouveau_fbcon_fini(struct drm_device *dev); void nouveau_fbcon_set_suspend(struct drm_device *dev, int state); -void nouveau_fbcon_zfill_all(struct drm_device *dev); void nouveau_fbcon_save_disable_accel(struct drm_device *dev); void nouveau_fbcon_restore_accel(struct drm_device *dev); diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index 90074d620e3..ab5ea3b0d66 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -166,7 +166,7 @@ nouveau_fence_done(struct nouveau_fence *fence) } static int -nouveau_fence_wait_uevent_handler(void *data, int index) +nouveau_fence_wait_uevent_handler(void *data, u32 type, int index) { struct nouveau_fence_priv *priv = data; wake_up_all(&priv->waiting); @@ -183,7 +183,7 @@ nouveau_fence_wait_uevent(struct nouveau_fence *fence, bool intr) struct nouveau_eventh *handler; int ret = 0; - ret = nouveau_event_new(pfifo->uevent, 0, + ret = nouveau_event_new(pfifo->uevent, 1, 0, nouveau_fence_wait_uevent_handler, priv, &handler); if (ret) diff --git a/drivers/gpu/drm/nouveau/nouveau_ioc32.c b/drivers/gpu/drm/nouveau/nouveau_ioc32.c index c1a7e5a73a2..462679a8fec 100644 --- a/drivers/gpu/drm/nouveau/nouveau_ioc32.c +++ b/drivers/gpu/drm/nouveau/nouveau_ioc32.c @@ -57,7 +57,7 @@ long nouveau_compat_ioctl(struct file *filp, unsigned int cmd, return drm_compat_ioctl(filp, cmd, arg); #if 0 - if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(mga_compat_ioctls)) + if (nr < DRM_COMMAND_BASE + ARRAY_SIZE(mga_compat_ioctls)) fn = nouveau_compat_ioctls[nr - DRM_COMMAND_BASE]; #endif if (fn != NULL) diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 79f6dc724a8..4c534b7b04d 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -1,4 +1,4 @@ - /* +/* * Copyright 2011 Red Hat Inc. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -26,6 +26,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_dp_helper.h> #include "nouveau_drm.h" #include "nouveau_dma.h" @@ -957,7 +958,7 @@ nv50_crtc_prepare(struct drm_crtc *crtc) nv50_display_flip_stop(crtc); - push = evo_wait(mast, 2); + push = evo_wait(mast, 6); if (push) { if (nv50_vers(mast) < NV84_DISP_MAST_CLASS) { evo_mthd(push, 0x0874 + (nv_crtc->index * 0x400), 1); @@ -1207,6 +1208,7 @@ static void nv50_crtc_disable(struct drm_crtc *crtc) { struct nv50_head *head = nv50_head(crtc); + evo_sync(crtc->dev); if (head->image) nouveau_bo_unpin(head->image); nouveau_bo_ref(NULL, &head->image); @@ -1700,10 +1702,9 @@ nv50_hdmi_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode) } static void -nv50_hdmi_disconnect(struct drm_encoder *encoder) +nv50_hdmi_disconnect(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc); struct nv50_disp *disp = nv50_disp(encoder->dev); const u32 moff = (nv_crtc->index << 3) | nv_encoder->or; @@ -1722,7 +1723,7 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode) struct drm_device *dev = encoder->dev; struct nv50_disp *disp = nv50_disp(dev); struct drm_encoder *partner; - int or = nv_encoder->or; + u32 mthd; nv_encoder->last_dpms = mode; @@ -1740,7 +1741,18 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode) } } - nv_call(disp->core, NV50_DISP_SOR_PWR + or, (mode == DRM_MODE_DPMS_ON)); + mthd = (ffs(nv_encoder->dcb->heads) - 1) << 3; + mthd |= (ffs(nv_encoder->dcb->sorconf.link) - 1) << 2; + mthd |= nv_encoder->or; + + if (nv_encoder->dcb->type == DCB_OUTPUT_DP) { + nv_call(disp->core, NV50_DISP_SOR_PWR | mthd, 1); + mthd |= NV94_DISP_SOR_DP_PWR; + } else { + mthd |= NV50_DISP_SOR_PWR; + } + + nv_call(disp->core, mthd, (mode == DRM_MODE_DPMS_ON)); } static bool @@ -1764,33 +1776,36 @@ nv50_sor_mode_fixup(struct drm_encoder *encoder, } static void -nv50_sor_disconnect(struct drm_encoder *encoder) +nv50_sor_ctrl(struct nouveau_encoder *nv_encoder, u32 mask, u32 data) { - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nv50_mast *mast = nv50_mast(encoder->dev); - const int or = nv_encoder->or; - u32 *push; - - if (nv_encoder->crtc) { - nv50_crtc_prepare(nv_encoder->crtc); - - push = evo_wait(mast, 4); - if (push) { - if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) { - evo_mthd(push, 0x0600 + (or * 0x40), 1); - evo_data(push, 0x00000000); - } else { - evo_mthd(push, 0x0200 + (or * 0x20), 1); - evo_data(push, 0x00000000); - } - evo_kick(push, mast); + struct nv50_mast *mast = nv50_mast(nv_encoder->base.base.dev); + u32 temp = (nv_encoder->ctrl & ~mask) | (data & mask), *push; + if (temp != nv_encoder->ctrl && (push = evo_wait(mast, 2))) { + if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) { + evo_mthd(push, 0x0600 + (nv_encoder->or * 0x40), 1); + evo_data(push, (nv_encoder->ctrl = temp)); + } else { + evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 1); + evo_data(push, (nv_encoder->ctrl = temp)); } - - nv50_hdmi_disconnect(encoder); + evo_kick(push, mast); } +} + +static void +nv50_sor_disconnect(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc); nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; nv_encoder->crtc = NULL; + + if (nv_crtc) { + nv50_crtc_prepare(&nv_crtc->base); + nv50_sor_ctrl(nv_encoder, 1 << nv_crtc->index, 0); + nv50_hdmi_disconnect(&nv_encoder->base.base, nv_crtc); + } } static void @@ -1810,12 +1825,14 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); struct nouveau_connector *nv_connector; struct nvbios *bios = &drm->vbios; - u32 *push, lvds = 0; + u32 lvds = 0, mask, ctrl; u8 owner = 1 << nv_crtc->index; u8 proto = 0xf; u8 depth = 0x0; nv_connector = nouveau_encoder_connector_get(nv_encoder); + nv_encoder->crtc = encoder->crtc; + switch (nv_encoder->dcb->type) { case DCB_OUTPUT_TMDS: if (nv_encoder->dcb->sorconf.link & 1) { @@ -1827,7 +1844,7 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, proto = 0x2; } - nv50_hdmi_mode_set(encoder, mode); + nv50_hdmi_mode_set(&nv_encoder->base.base, mode); break; case DCB_OUTPUT_LVDS: proto = 0x0; @@ -1883,19 +1900,11 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, break; } - nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON); + nv50_sor_dpms(&nv_encoder->base.base, DRM_MODE_DPMS_ON); - push = evo_wait(nv50_mast(dev), 8); - if (push) { - if (nv50_vers(mast) < NVD0_DISP_CLASS) { - u32 ctrl = (depth << 16) | (proto << 8) | owner; - if (mode->flags & DRM_MODE_FLAG_NHSYNC) - ctrl |= 0x00001000; - if (mode->flags & DRM_MODE_FLAG_NVSYNC) - ctrl |= 0x00002000; - evo_mthd(push, 0x0600 + (nv_encoder->or * 0x040), 1); - evo_data(push, ctrl); - } else { + if (nv50_vers(mast) >= NVD0_DISP_CLASS) { + u32 *push = evo_wait(mast, 3); + if (push) { u32 magic = 0x31ec6000 | (nv_crtc->index << 25); u32 syncs = 0x00000001; @@ -1910,14 +1919,21 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 2); evo_data(push, syncs | (depth << 6)); evo_data(push, magic); - evo_mthd(push, 0x0200 + (nv_encoder->or * 0x020), 1); - evo_data(push, owner | (proto << 8)); + evo_kick(push, mast); } - evo_kick(push, mast); + ctrl = proto << 8; + mask = 0x00000f00; + } else { + ctrl = (depth << 16) | (proto << 8); + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + ctrl |= 0x00001000; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + ctrl |= 0x00002000; + mask = 0x000f3f00; } - nv_encoder->crtc = encoder->crtc; + nv50_sor_ctrl(nv_encoder, mask | owner, ctrl | owner); } static void diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index e3c47a8005f..2d28dc337cf 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -319,13 +319,13 @@ static void page_flip_worker(struct work_struct *work) struct drm_display_mode *mode = &crtc->mode; struct drm_gem_object *bo; - mutex_lock(&crtc->mutex); + drm_modeset_lock(&crtc->mutex, NULL); omap_plane_mode_set(omap_crtc->plane, crtc, crtc->primary->fb, 0, 0, mode->hdisplay, mode->vdisplay, crtc->x << 16, crtc->y << 16, mode->hdisplay << 16, mode->vdisplay << 16, vblank_cb, crtc); - mutex_unlock(&crtc->mutex); + drm_modeset_unlock(&crtc->mutex); bo = omap_framebuffer_bo(crtc->primary->fb, 0); drm_gem_object_unreference_unlocked(bo); @@ -465,7 +465,7 @@ static void apply_worker(struct work_struct *work) * the callbacks and list modification all serialized * with respect to modesetting ioctls from userspace. */ - mutex_lock(&crtc->mutex); + drm_modeset_lock(&crtc->mutex, NULL); dispc_runtime_get(); /* @@ -510,7 +510,7 @@ static void apply_worker(struct work_struct *work) out: dispc_runtime_put(); - mutex_unlock(&crtc->mutex); + drm_modeset_unlock(&crtc->mutex); } int omap_crtc_apply(struct drm_crtc *crtc, @@ -518,7 +518,7 @@ int omap_crtc_apply(struct drm_crtc *crtc, { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - WARN_ON(!mutex_is_locked(&crtc->mutex)); + WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); /* no need to queue it again if it is already queued: */ if (apply->queued) diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index c8270e4b26f..002b9721e85 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -588,9 +588,7 @@ static void dev_lastclose(struct drm_device *dev) } } - drm_modeset_lock_all(dev); - ret = drm_fb_helper_restore_fbdev_mode(priv->fbdev); - drm_modeset_unlock_all(dev); + ret = drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev); if (ret) DBG("failed to restore crtc mode"); } diff --git a/drivers/gpu/drm/panel/panel-ld9040.c b/drivers/gpu/drm/panel/panel-ld9040.c index 1f1f8371a19..db1601fdbe2 100644 --- a/drivers/gpu/drm/panel/panel-ld9040.c +++ b/drivers/gpu/drm/panel/panel-ld9040.c @@ -27,6 +27,7 @@ #define MCS_ELVSS_ON 0xb1 #define MCS_USER_SETTING 0xf0 #define MCS_DISPCTL 0xf2 +#define MCS_POWER_CTRL 0xf4 #define MCS_GTCON 0xf7 #define MCS_PANEL_CONDITION 0xf8 #define MCS_GAMMA_SET1 0xf9 @@ -182,6 +183,8 @@ static void ld9040_init(struct ld9040 *ctx) ld9040_dcs_write_seq_static(ctx, MCS_DISPCTL, 0x02, 0x08, 0x08, 0x10, 0x10); ld9040_dcs_write_seq_static(ctx, MCS_MANPWR, 0x04); + ld9040_dcs_write_seq_static(ctx, MCS_POWER_CTRL, + 0x0a, 0x87, 0x25, 0x6a, 0x44, 0x02, 0x88); ld9040_dcs_write_seq_static(ctx, MCS_ELVSS_ON, 0x0d, 0x00, 0x16); ld9040_dcs_write_seq_static(ctx, MCS_GTCON, 0x09, 0x00, 0x00); ld9040_brightness_set(ctx); diff --git a/drivers/gpu/drm/panel/panel-s6e8aa0.c b/drivers/gpu/drm/panel/panel-s6e8aa0.c index 35941d2412b..06e57a26db7 100644 --- a/drivers/gpu/drm/panel/panel-s6e8aa0.c +++ b/drivers/gpu/drm/panel/panel-s6e8aa0.c @@ -847,6 +847,7 @@ static void s6e8aa0_read_mtp_id(struct s6e8aa0 *ctx) if (i >= ARRAY_SIZE(s6e8aa0_variants)) { dev_err(ctx->dev, "unsupported display version %d\n", id[1]); ctx->error = -EINVAL; + return; } ctx->variant = &s6e8aa0_variants[i]; diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 309f29e9234..a25136132c3 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -262,6 +262,13 @@ static int panel_simple_remove(struct device *dev) return 0; } +static void panel_simple_shutdown(struct device *dev) +{ + struct panel_simple *panel = dev_get_drvdata(dev); + + panel_simple_disable(&panel->base); +} + static const struct drm_display_mode auo_b101aw03_mode = { .clock = 51450, .hdisplay = 1024, @@ -284,6 +291,28 @@ static const struct panel_desc auo_b101aw03 = { }, }; +static const struct drm_display_mode auo_b133xtn01_mode = { + .clock = 69500, + .hdisplay = 1366, + .hsync_start = 1366 + 48, + .hsync_end = 1366 + 48 + 32, + .htotal = 1366 + 48 + 32 + 20, + .vdisplay = 768, + .vsync_start = 768 + 3, + .vsync_end = 768 + 3 + 6, + .vtotal = 768 + 3 + 6 + 13, + .vrefresh = 60, +}; + +static const struct panel_desc auo_b133xtn01 = { + .modes = &auo_b133xtn01_mode, + .num_modes = 1, + .size = { + .width = 293, + .height = 165, + }, +}; + static const struct drm_display_mode chunghwa_claa101wa01a_mode = { .clock = 72070, .hdisplay = 1366, @@ -328,6 +357,52 @@ static const struct panel_desc chunghwa_claa101wb01 = { }, }; +static const struct drm_display_mode edt_et057090dhu_mode = { + .clock = 25175, + .hdisplay = 640, + .hsync_start = 640 + 16, + .hsync_end = 640 + 16 + 30, + .htotal = 640 + 16 + 30 + 114, + .vdisplay = 480, + .vsync_start = 480 + 10, + .vsync_end = 480 + 10 + 3, + .vtotal = 480 + 10 + 3 + 32, + .vrefresh = 60, + .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, +}; + +static const struct panel_desc edt_et057090dhu = { + .modes = &edt_et057090dhu_mode, + .num_modes = 1, + .size = { + .width = 115, + .height = 86, + }, +}; + +static const struct drm_display_mode edt_etm0700g0dh6_mode = { + .clock = 33260, + .hdisplay = 800, + .hsync_start = 800 + 40, + .hsync_end = 800 + 40 + 128, + .htotal = 800 + 40 + 128 + 88, + .vdisplay = 480, + .vsync_start = 480 + 10, + .vsync_end = 480 + 10 + 2, + .vtotal = 480 + 10 + 2 + 33, + .vrefresh = 60, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, +}; + +static const struct panel_desc edt_etm0700g0dh6 = { + .modes = &edt_etm0700g0dh6_mode, + .num_modes = 1, + .size = { + .width = 152, + .height = 91, + }, +}; + static const struct drm_display_mode lg_lp129qe_mode = { .clock = 285250, .hdisplay = 2560, @@ -377,12 +452,24 @@ static const struct of_device_id platform_of_match[] = { .compatible = "auo,b101aw03", .data = &auo_b101aw03, }, { + .compatible = "auo,b133xtn01", + .data = &auo_b133xtn01, + }, { .compatible = "chunghwa,claa101wa01a", .data = &chunghwa_claa101wa01a }, { .compatible = "chunghwa,claa101wb01", .data = &chunghwa_claa101wb01 }, { + .compatible = "edt,et057090dhu", + .data = &edt_et057090dhu, + }, { + .compatible = "edt,et070080dh6", + .data = &edt_etm0700g0dh6, + }, { + .compatible = "edt,etm0700g0dh6", + .data = &edt_etm0700g0dh6, + }, { .compatible = "lg,lp129qe", .data = &lg_lp129qe, }, { @@ -412,6 +499,11 @@ static int panel_simple_platform_remove(struct platform_device *pdev) return panel_simple_remove(&pdev->dev); } +static void panel_simple_platform_shutdown(struct platform_device *pdev) +{ + panel_simple_shutdown(&pdev->dev); +} + static struct platform_driver panel_simple_platform_driver = { .driver = { .name = "panel-simple", @@ -420,6 +512,7 @@ static struct platform_driver panel_simple_platform_driver = { }, .probe = panel_simple_platform_probe, .remove = panel_simple_platform_remove, + .shutdown = panel_simple_platform_shutdown, }; struct panel_desc_dsi { @@ -561,6 +654,11 @@ static int panel_simple_dsi_remove(struct mipi_dsi_device *dsi) return panel_simple_remove(&dsi->dev); } +static void panel_simple_dsi_shutdown(struct mipi_dsi_device *dsi) +{ + panel_simple_shutdown(&dsi->dev); +} + static struct mipi_dsi_driver panel_simple_dsi_driver = { .driver = { .name = "panel-simple-dsi", @@ -569,6 +667,7 @@ static struct mipi_dsi_driver panel_simple_dsi_driver = { }, .probe = panel_simple_dsi_probe, .remove = panel_simple_dsi_remove, + .shutdown = panel_simple_dsi_shutdown, }; static int __init panel_simple_init(void) diff --git a/drivers/gpu/drm/qxl/qxl_ioctl.c b/drivers/gpu/drm/qxl/qxl_ioctl.c index 0bb86e6d41b..b110883f825 100644 --- a/drivers/gpu/drm/qxl/qxl_ioctl.c +++ b/drivers/gpu/drm/qxl/qxl_ioctl.c @@ -451,4 +451,4 @@ const struct drm_ioctl_desc qxl_ioctls[] = { DRM_AUTH|DRM_UNLOCKED), }; -int qxl_max_ioctls = DRM_ARRAY_SIZE(qxl_ioctls); +int qxl_max_ioctls = ARRAY_SIZE(qxl_ioctls); diff --git a/drivers/gpu/drm/qxl/qxl_irq.c b/drivers/gpu/drm/qxl/qxl_irq.c index 34d6a85e902..0bf1e20c6e4 100644 --- a/drivers/gpu/drm/qxl/qxl_irq.c +++ b/drivers/gpu/drm/qxl/qxl_irq.c @@ -33,6 +33,9 @@ irqreturn_t qxl_irq_handler(int irq, void *arg) pending = xchg(&qdev->ram_header->int_pending, 0); + if (!pending) + return IRQ_NONE; + atomic_inc(&qdev->irq_received); if (pending & QXL_INTERRUPT_DISPLAY) { diff --git a/drivers/gpu/drm/r128/r128_ioc32.c b/drivers/gpu/drm/r128/r128_ioc32.c index b0d0fd3e437..663f38c63ba 100644 --- a/drivers/gpu/drm/r128/r128_ioc32.c +++ b/drivers/gpu/drm/r128/r128_ioc32.c @@ -203,7 +203,7 @@ long r128_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (nr < DRM_COMMAND_BASE) return drm_compat_ioctl(filp, cmd, arg); - if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(r128_compat_ioctls)) + if (nr < DRM_COMMAND_BASE + ARRAY_SIZE(r128_compat_ioctls)) fn = r128_compat_ioctls[nr - DRM_COMMAND_BASE]; if (fn != NULL) diff --git a/drivers/gpu/drm/r128/r128_state.c b/drivers/gpu/drm/r128/r128_state.c index 97064dd434c..575e986f82a 100644 --- a/drivers/gpu/drm/r128/r128_state.c +++ b/drivers/gpu/drm/r128/r128_state.c @@ -1641,4 +1641,4 @@ const struct drm_ioctl_desc r128_ioctls[] = { DRM_IOCTL_DEF_DRV(R128_GETPARAM, r128_getparam, DRM_AUTH), }; -int r128_max_ioctl = DRM_ARRAY_SIZE(r128_ioctls); +int r128_max_ioctl = ARRAY_SIZE(r128_ioctls); diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 967d193d36d..30d242b2507 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -270,8 +270,6 @@ void atombios_crtc_dpms(struct drm_crtc *crtc, int mode) switch (mode) { case DRM_MODE_DPMS_ON: radeon_crtc->enabled = true; - /* adjust pm to dpms changes BEFORE enabling crtcs */ - radeon_pm_compute_clocks(rdev); atombios_enable_crtc(crtc, ATOM_ENABLE); if (ASIC_IS_DCE3(rdev) && !ASIC_IS_DCE6(rdev)) atombios_enable_crtc_memreq(crtc, ATOM_ENABLE); @@ -289,10 +287,10 @@ void atombios_crtc_dpms(struct drm_crtc *crtc, int mode) atombios_enable_crtc_memreq(crtc, ATOM_DISABLE); atombios_enable_crtc(crtc, ATOM_DISABLE); radeon_crtc->enabled = false; - /* adjust pm to dpms changes AFTER disabling crtcs */ - radeon_pm_compute_clocks(rdev); break; } + /* adjust pm to dpms */ + radeon_pm_compute_clocks(rdev); } static void @@ -964,6 +962,9 @@ static bool atombios_crtc_prepare_pll(struct drm_crtc *crtc, struct drm_display_ struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; int dp_clock; + + /* Assign mode clock for hdmi deep color max clock limit check */ + radeon_connector->pixelclock_for_modeset = mode->clock; radeon_crtc->bpc = radeon_get_monitor_bpc(connector); switch (encoder_mode) { @@ -1051,7 +1052,7 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode int encoder_mode = atombios_get_encoder_mode(radeon_crtc->encoder); /* pass the actual clock to atombios_crtc_program_pll for DCE5,6 for HDMI */ - if (ASIC_IS_DCE5(rdev) && !ASIC_IS_DCE8(rdev) && + if (ASIC_IS_DCE5(rdev) && (encoder_mode == ATOM_ENCODER_MODE_HDMI) && (radeon_crtc->bpc > 8)) clock = radeon_crtc->adjusted_clock; @@ -1135,6 +1136,7 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc, u32 fb_swap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_NONE); u32 tmp, viewport_w, viewport_h; int r; + bool bypass_lut = false; /* no fb bound */ if (!atomic && !crtc->primary->fb) { @@ -1173,33 +1175,73 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc, radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL); radeon_bo_unreserve(rbo); - switch (target_fb->bits_per_pixel) { - case 8: + switch (target_fb->pixel_format) { + case DRM_FORMAT_C8: fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_8BPP) | EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_INDEXED)); break; - case 15: + case DRM_FORMAT_XRGB4444: + case DRM_FORMAT_ARGB4444: + fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_16BPP) | + EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB4444)); +#ifdef __BIG_ENDIAN + fb_swap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_8IN16); +#endif + break; + case DRM_FORMAT_XRGB1555: + case DRM_FORMAT_ARGB1555: fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_16BPP) | EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB1555)); +#ifdef __BIG_ENDIAN + fb_swap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_8IN16); +#endif + break; + case DRM_FORMAT_BGRX5551: + case DRM_FORMAT_BGRA5551: + fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_16BPP) | + EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_BGRA5551)); +#ifdef __BIG_ENDIAN + fb_swap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_8IN16); +#endif break; - case 16: + case DRM_FORMAT_RGB565: fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_16BPP) | EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB565)); #ifdef __BIG_ENDIAN fb_swap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_8IN16); #endif break; - case 24: - case 32: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_32BPP) | EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB8888)); #ifdef __BIG_ENDIAN fb_swap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_8IN32); #endif break; + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_ARGB2101010: + fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_32BPP) | + EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB2101010)); +#ifdef __BIG_ENDIAN + fb_swap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_8IN32); +#endif + /* Greater 8 bpc fb needs to bypass hw-lut to retain precision */ + bypass_lut = true; + break; + case DRM_FORMAT_BGRX1010102: + case DRM_FORMAT_BGRA1010102: + fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_32BPP) | + EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_BGRA1010102)); +#ifdef __BIG_ENDIAN + fb_swap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_8IN32); +#endif + /* Greater 8 bpc fb needs to bypass hw-lut to retain precision */ + bypass_lut = true; + break; default: - DRM_ERROR("Unsupported screen depth %d\n", - target_fb->bits_per_pixel); + DRM_ERROR("Unsupported screen format %s\n", + drm_get_format_name(target_fb->pixel_format)); return -EINVAL; } @@ -1208,27 +1250,43 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc, /* Set NUM_BANKS. */ if (rdev->family >= CHIP_TAHITI) { - unsigned tileb, index, num_banks, tile_split_bytes; + unsigned index, num_banks; - /* Calculate the macrotile mode index. */ - tile_split_bytes = 64 << tile_split; - tileb = 8 * 8 * target_fb->bits_per_pixel / 8; - tileb = min(tile_split_bytes, tileb); + if (rdev->family >= CHIP_BONAIRE) { + unsigned tileb, tile_split_bytes; - for (index = 0; tileb > 64; index++) { - tileb >>= 1; - } + /* Calculate the macrotile mode index. */ + tile_split_bytes = 64 << tile_split; + tileb = 8 * 8 * target_fb->bits_per_pixel / 8; + tileb = min(tile_split_bytes, tileb); - if (index >= 16) { - DRM_ERROR("Wrong screen bpp (%u) or tile split (%u)\n", - target_fb->bits_per_pixel, tile_split); - return -EINVAL; - } + for (index = 0; tileb > 64; index++) + tileb >>= 1; + + if (index >= 16) { + DRM_ERROR("Wrong screen bpp (%u) or tile split (%u)\n", + target_fb->bits_per_pixel, tile_split); + return -EINVAL; + } - if (rdev->family >= CHIP_BONAIRE) num_banks = (rdev->config.cik.macrotile_mode_array[index] >> 6) & 0x3; - else + } else { + switch (target_fb->bits_per_pixel) { + case 8: + index = 10; + break; + case 16: + index = SI_TILE_MODE_COLOR_2D_SCANOUT_16BPP; + break; + default: + case 32: + index = SI_TILE_MODE_COLOR_2D_SCANOUT_32BPP; + break; + } + num_banks = (rdev->config.si.tile_mode_array[index] >> 20) & 0x3; + } + fb_format |= EVERGREEN_GRPH_NUM_BANKS(num_banks); } else { /* NI and older. */ @@ -1312,6 +1370,18 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc, WREG32(EVERGREEN_GRPH_CONTROL + radeon_crtc->crtc_offset, fb_format); WREG32(EVERGREEN_GRPH_SWAP_CONTROL + radeon_crtc->crtc_offset, fb_swap); + /* + * The LUT only has 256 slots for indexing by a 8 bpc fb. Bypass the LUT + * for > 8 bpc scanout to avoid truncation of fb indices to 8 msb's, to + * retain the full precision throughout the pipeline. + */ + WREG32_P(EVERGREEN_GRPH_LUT_10BIT_BYPASS_CONTROL + radeon_crtc->crtc_offset, + (bypass_lut ? EVERGREEN_LUT_10BIT_BYPASS_EN : 0), + ~EVERGREEN_LUT_10BIT_BYPASS_EN); + + if (bypass_lut) + DRM_DEBUG_KMS("Bypassing hardware LUT due to 10 bit fb scanout.\n"); + WREG32(EVERGREEN_GRPH_SURFACE_OFFSET_X + radeon_crtc->crtc_offset, 0); WREG32(EVERGREEN_GRPH_SURFACE_OFFSET_Y + radeon_crtc->crtc_offset, 0); WREG32(EVERGREEN_GRPH_X_START + radeon_crtc->crtc_offset, 0); @@ -1344,8 +1414,8 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc, tmp &= ~EVERGREEN_GRPH_SURFACE_UPDATE_H_RETRACE_EN; WREG32(EVERGREEN_GRPH_FLIP_CONTROL + radeon_crtc->crtc_offset, tmp); - /* set pageflip to happen anywhere in vblank interval */ - WREG32(EVERGREEN_MASTER_UPDATE_MODE + radeon_crtc->crtc_offset, 0); + /* set pageflip to happen only at start of vblank interval (front porch) */ + WREG32(EVERGREEN_MASTER_UPDATE_MODE + radeon_crtc->crtc_offset, 3); if (!atomic && fb && fb != crtc->primary->fb) { radeon_fb = to_radeon_framebuffer(fb); @@ -1379,6 +1449,7 @@ static int avivo_crtc_do_set_base(struct drm_crtc *crtc, u32 fb_swap = R600_D1GRPH_SWAP_ENDIAN_NONE; u32 tmp, viewport_w, viewport_h; int r; + bool bypass_lut = false; /* no fb bound */ if (!atomic && !crtc->primary->fb) { @@ -1416,18 +1487,30 @@ static int avivo_crtc_do_set_base(struct drm_crtc *crtc, radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL); radeon_bo_unreserve(rbo); - switch (target_fb->bits_per_pixel) { - case 8: + switch (target_fb->pixel_format) { + case DRM_FORMAT_C8: fb_format = AVIVO_D1GRPH_CONTROL_DEPTH_8BPP | AVIVO_D1GRPH_CONTROL_8BPP_INDEXED; break; - case 15: + case DRM_FORMAT_XRGB4444: + case DRM_FORMAT_ARGB4444: + fb_format = + AVIVO_D1GRPH_CONTROL_DEPTH_16BPP | + AVIVO_D1GRPH_CONTROL_16BPP_ARGB4444; +#ifdef __BIG_ENDIAN + fb_swap = R600_D1GRPH_SWAP_ENDIAN_16BIT; +#endif + break; + case DRM_FORMAT_XRGB1555: fb_format = AVIVO_D1GRPH_CONTROL_DEPTH_16BPP | AVIVO_D1GRPH_CONTROL_16BPP_ARGB1555; +#ifdef __BIG_ENDIAN + fb_swap = R600_D1GRPH_SWAP_ENDIAN_16BIT; +#endif break; - case 16: + case DRM_FORMAT_RGB565: fb_format = AVIVO_D1GRPH_CONTROL_DEPTH_16BPP | AVIVO_D1GRPH_CONTROL_16BPP_RGB565; @@ -1435,8 +1518,8 @@ static int avivo_crtc_do_set_base(struct drm_crtc *crtc, fb_swap = R600_D1GRPH_SWAP_ENDIAN_16BIT; #endif break; - case 24: - case 32: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: fb_format = AVIVO_D1GRPH_CONTROL_DEPTH_32BPP | AVIVO_D1GRPH_CONTROL_32BPP_ARGB8888; @@ -1444,9 +1527,20 @@ static int avivo_crtc_do_set_base(struct drm_crtc *crtc, fb_swap = R600_D1GRPH_SWAP_ENDIAN_32BIT; #endif break; + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_ARGB2101010: + fb_format = + AVIVO_D1GRPH_CONTROL_DEPTH_32BPP | + AVIVO_D1GRPH_CONTROL_32BPP_ARGB2101010; +#ifdef __BIG_ENDIAN + fb_swap = R600_D1GRPH_SWAP_ENDIAN_32BIT; +#endif + /* Greater 8 bpc fb needs to bypass hw-lut to retain precision */ + bypass_lut = true; + break; default: - DRM_ERROR("Unsupported screen depth %d\n", - target_fb->bits_per_pixel); + DRM_ERROR("Unsupported screen format %s\n", + drm_get_format_name(target_fb->pixel_format)); return -EINVAL; } @@ -1485,6 +1579,13 @@ static int avivo_crtc_do_set_base(struct drm_crtc *crtc, if (rdev->family >= CHIP_R600) WREG32(R600_D1GRPH_SWAP_CONTROL + radeon_crtc->crtc_offset, fb_swap); + /* LUT only has 256 slots for 8 bpc fb. Bypass for > 8 bpc scanout for precision */ + WREG32_P(AVIVO_D1GRPH_LUT_SEL + radeon_crtc->crtc_offset, + (bypass_lut ? AVIVO_LUT_10BIT_BYPASS_EN : 0), ~AVIVO_LUT_10BIT_BYPASS_EN); + + if (bypass_lut) + DRM_DEBUG_KMS("Bypassing hardware LUT due to 10 bit fb scanout.\n"); + WREG32(AVIVO_D1GRPH_SURFACE_OFFSET_X + radeon_crtc->crtc_offset, 0); WREG32(AVIVO_D1GRPH_SURFACE_OFFSET_Y + radeon_crtc->crtc_offset, 0); WREG32(AVIVO_D1GRPH_X_START + radeon_crtc->crtc_offset, 0); @@ -1513,8 +1614,8 @@ static int avivo_crtc_do_set_base(struct drm_crtc *crtc, tmp &= ~AVIVO_D1GRPH_SURFACE_UPDATE_H_RETRACE_EN; WREG32(AVIVO_D1GRPH_FLIP_CONTROL + radeon_crtc->crtc_offset, tmp); - /* set pageflip to happen anywhere in vblank interval */ - WREG32(AVIVO_D1MODE_MASTER_UPDATE_MODE + radeon_crtc->crtc_offset, 0); + /* set pageflip to happen only at start of vblank interval (front porch) */ + WREG32(AVIVO_D1MODE_MASTER_UPDATE_MODE + radeon_crtc->crtc_offset, 3); if (!atomic && fb && fb != crtc->primary->fb) { radeon_fb = to_radeon_framebuffer(fb); @@ -1751,8 +1852,9 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc) } /* otherwise, pick one of the plls */ if ((rdev->family == CHIP_KAVERI) || - (rdev->family == CHIP_KABINI)) { - /* KB/KV has PPLL1 and PPLL2 */ + (rdev->family == CHIP_KABINI) || + (rdev->family == CHIP_MULLINS)) { + /* KB/KV/ML has PPLL1 and PPLL2 */ pll_in_use = radeon_get_pll_use_mask(crtc); if (!(pll_in_use & (1 << ATOM_PPLL2))) return ATOM_PPLL2; @@ -1916,6 +2018,9 @@ int atombios_crtc_mode_set(struct drm_crtc *crtc, (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT)) is_tvcv = true; + if (!radeon_crtc->adjusted_clock) + return -EINVAL; + atombios_crtc_set_pll(crtc, adjusted_mode); if (ASIC_IS_DCE4(rdev)) diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index 225f6c66eff..b1e11f8434e 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -127,7 +127,7 @@ static int radeon_process_aux_ch(struct radeon_i2c_chan *chan, /* flags not zero */ if (args.v1.ucReplyStatus == 2) { DRM_DEBUG_KMS("dp_aux_ch flags not zero\n"); - r = -EBUSY; + r = -EIO; goto done; } @@ -222,11 +222,12 @@ void radeon_dp_aux_init(struct radeon_connector *radeon_connector) radeon_connector->ddc_bus->rec.hpd = radeon_connector->hpd.hpd; radeon_connector->ddc_bus->aux.dev = radeon_connector->base.kdev; radeon_connector->ddc_bus->aux.transfer = radeon_dp_aux_transfer; - ret = drm_dp_aux_register_i2c_bus(&radeon_connector->ddc_bus->aux); + + ret = drm_dp_aux_register(&radeon_connector->ddc_bus->aux); if (!ret) radeon_connector->ddc_bus->has_aux = true; - WARN(ret, "drm_dp_aux_register_i2c_bus() failed with error %d\n", ret); + WARN(ret, "drm_dp_aux_register() failed with error %d\n", ret); } /***** general DP utility functions *****/ @@ -389,11 +390,11 @@ static void radeon_dp_probe_oui(struct radeon_connector *radeon_connector) if (!(dig_connector->dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_OUI_SUPPORT)) return; - if (drm_dp_dpcd_read(&radeon_connector->ddc_bus->aux, DP_SINK_OUI, buf, 3)) + if (drm_dp_dpcd_read(&radeon_connector->ddc_bus->aux, DP_SINK_OUI, buf, 3) == 3) DRM_DEBUG_KMS("Sink OUI: %02hx%02hx%02hx\n", buf[0], buf[1], buf[2]); - if (drm_dp_dpcd_read(&radeon_connector->ddc_bus->aux, DP_BRANCH_OUI, buf, 3)) + if (drm_dp_dpcd_read(&radeon_connector->ddc_bus->aux, DP_BRANCH_OUI, buf, 3) == 3) DRM_DEBUG_KMS("Branch OUI: %02hx%02hx%02hx\n", buf[0], buf[1], buf[2]); } @@ -402,16 +403,18 @@ bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector) { struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; u8 msg[DP_DPCD_SIZE]; - int ret, i; + int ret; + + char dpcd_hex_dump[DP_DPCD_SIZE * 3]; ret = drm_dp_dpcd_read(&radeon_connector->ddc_bus->aux, DP_DPCD_REV, msg, DP_DPCD_SIZE); if (ret > 0) { memcpy(dig_connector->dpcd, msg, DP_DPCD_SIZE); - DRM_DEBUG_KMS("DPCD: "); - for (i = 0; i < DP_DPCD_SIZE; i++) - DRM_DEBUG_KMS("%02x ", msg[i]); - DRM_DEBUG_KMS("\n"); + + hex_dump_to_buffer(dig_connector->dpcd, sizeof(dig_connector->dpcd), + 32, 1, dpcd_hex_dump, sizeof(dpcd_hex_dump), false); + DRM_DEBUG_KMS("DPCD: %s\n", dpcd_hex_dump); radeon_dp_probe_oui(radeon_connector); @@ -442,21 +445,23 @@ int radeon_dp_get_panel_mode(struct drm_encoder *encoder, if (dp_bridge != ENCODER_OBJECT_ID_NONE) { /* DP bridge chips */ - drm_dp_dpcd_readb(&radeon_connector->ddc_bus->aux, - DP_EDP_CONFIGURATION_CAP, &tmp); - if (tmp & 1) - panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE; - else if ((dp_bridge == ENCODER_OBJECT_ID_NUTMEG) || - (dp_bridge == ENCODER_OBJECT_ID_TRAVIS)) - panel_mode = DP_PANEL_MODE_INTERNAL_DP1_MODE; - else - panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE; + if (drm_dp_dpcd_readb(&radeon_connector->ddc_bus->aux, + DP_EDP_CONFIGURATION_CAP, &tmp) == 1) { + if (tmp & 1) + panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE; + else if ((dp_bridge == ENCODER_OBJECT_ID_NUTMEG) || + (dp_bridge == ENCODER_OBJECT_ID_TRAVIS)) + panel_mode = DP_PANEL_MODE_INTERNAL_DP1_MODE; + else + panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE; + } } else if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) { /* eDP */ - drm_dp_dpcd_readb(&radeon_connector->ddc_bus->aux, - DP_EDP_CONFIGURATION_CAP, &tmp); - if (tmp & 1) - panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE; + if (drm_dp_dpcd_readb(&radeon_connector->ddc_bus->aux, + DP_EDP_CONFIGURATION_CAP, &tmp) == 1) { + if (tmp & 1) + panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE; + } } return panel_mode; @@ -832,11 +837,15 @@ void radeon_dp_link_train(struct drm_encoder *encoder, else dp_info.enc_id |= ATOM_DP_CONFIG_LINK_A; - drm_dp_dpcd_readb(&radeon_connector->ddc_bus->aux, DP_MAX_LANE_COUNT, &tmp); - if (ASIC_IS_DCE5(rdev) && (tmp & DP_TPS3_SUPPORTED)) - dp_info.tp3_supported = true; - else + if (drm_dp_dpcd_readb(&radeon_connector->ddc_bus->aux, DP_MAX_LANE_COUNT, &tmp) + == 1) { + if (ASIC_IS_DCE5(rdev) && (tmp & DP_TPS3_SUPPORTED)) + dp_info.tp3_supported = true; + else + dp_info.tp3_supported = false; + } else { dp_info.tp3_supported = false; + } memcpy(dp_info.dpcd, dig_connector->dpcd, DP_RECEIVER_CAP_SIZE); dp_info.rdev = rdev; diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c index 2b290844064..7d68203a373 100644 --- a/drivers/gpu/drm/radeon/atombios_encoders.c +++ b/drivers/gpu/drm/radeon/atombios_encoders.c @@ -183,7 +183,6 @@ void radeon_atom_backlight_init(struct radeon_encoder *radeon_encoder, struct backlight_properties props; struct radeon_backlight_privdata *pdata; struct radeon_encoder_atom_dig *dig; - u8 backlight_level; char bl_name[16]; /* Mac laptops with multiple GPUs use the gmux driver for backlight @@ -222,12 +221,17 @@ void radeon_atom_backlight_init(struct radeon_encoder *radeon_encoder, pdata->encoder = radeon_encoder; - backlight_level = radeon_atom_get_backlight_level_from_reg(rdev); - dig = radeon_encoder->enc_priv; dig->bl_dev = bd; bd->props.brightness = radeon_atom_backlight_get_brightness(bd); + /* Set a reasonable default here if the level is 0 otherwise + * fbdev will attempt to turn the backlight on after console + * unblanking and it will try and restore 0 which turns the backlight + * off again. + */ + if (bd->props.brightness == 0) + bd->props.brightness = RADEON_MAX_BL_LEVEL; bd->props.power = FB_BLANK_UNBLANK; backlight_update_status(bd); diff --git a/drivers/gpu/drm/radeon/ci_dpm.c b/drivers/gpu/drm/radeon/ci_dpm.c index 10dae4106c0..584090ac3eb 100644 --- a/drivers/gpu/drm/radeon/ci_dpm.c +++ b/drivers/gpu/drm/radeon/ci_dpm.c @@ -1179,7 +1179,7 @@ static int ci_stop_dpm(struct radeon_device *rdev) tmp &= ~GLOBAL_PWRMGT_EN; WREG32_SMC(GENERAL_PWRMGT, tmp); - tmp = RREG32(SCLK_PWRMGT_CNTL); + tmp = RREG32_SMC(SCLK_PWRMGT_CNTL); tmp &= ~DYNAMIC_PM_EN; WREG32_SMC(SCLK_PWRMGT_CNTL, tmp); diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index a5181404f13..c0ea66192fe 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -63,6 +63,12 @@ MODULE_FIRMWARE("radeon/KABINI_ce.bin"); MODULE_FIRMWARE("radeon/KABINI_mec.bin"); MODULE_FIRMWARE("radeon/KABINI_rlc.bin"); MODULE_FIRMWARE("radeon/KABINI_sdma.bin"); +MODULE_FIRMWARE("radeon/MULLINS_pfp.bin"); +MODULE_FIRMWARE("radeon/MULLINS_me.bin"); +MODULE_FIRMWARE("radeon/MULLINS_ce.bin"); +MODULE_FIRMWARE("radeon/MULLINS_mec.bin"); +MODULE_FIRMWARE("radeon/MULLINS_rlc.bin"); +MODULE_FIRMWARE("radeon/MULLINS_sdma.bin"); extern int r600_ih_ring_alloc(struct radeon_device *rdev); extern void r600_ih_ring_fini(struct radeon_device *rdev); @@ -74,6 +80,7 @@ extern int sumo_rlc_init(struct radeon_device *rdev); extern void si_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc); extern void si_rlc_reset(struct radeon_device *rdev); extern void si_init_uvd_internal_cg(struct radeon_device *rdev); +static u32 cik_get_cu_active_bitmap(struct radeon_device *rdev, u32 se, u32 sh); extern int cik_sdma_resume(struct radeon_device *rdev); extern void cik_sdma_enable(struct radeon_device *rdev, bool enable); extern void cik_sdma_fini(struct radeon_device *rdev); @@ -1473,6 +1480,43 @@ static const u32 hawaii_mgcg_cgcg_init[] = 0xd80c, 0xff000ff0, 0x00000100 }; +static const u32 godavari_golden_registers[] = +{ + 0x55e4, 0xff607fff, 0xfc000100, + 0x6ed8, 0x00010101, 0x00010000, + 0x9830, 0xffffffff, 0x00000000, + 0x98302, 0xf00fffff, 0x00000400, + 0x6130, 0xffffffff, 0x00010000, + 0x5bb0, 0x000000f0, 0x00000070, + 0x5bc0, 0xf0311fff, 0x80300000, + 0x98f8, 0x73773777, 0x12010001, + 0x98fc, 0xffffffff, 0x00000010, + 0x8030, 0x00001f0f, 0x0000100a, + 0x2f48, 0x73773777, 0x12010001, + 0x2408, 0x000fffff, 0x000c007f, + 0x8a14, 0xf000003f, 0x00000007, + 0x8b24, 0xffffffff, 0x00ff0fff, + 0x30a04, 0x0000ff0f, 0x00000000, + 0x28a4c, 0x07ffffff, 0x06000000, + 0x4d8, 0x00000fff, 0x00000100, + 0xd014, 0x00010000, 0x00810001, + 0xd814, 0x00010000, 0x00810001, + 0x3e78, 0x00000001, 0x00000002, + 0xc768, 0x00000008, 0x00000008, + 0xc770, 0x00000f00, 0x00000800, + 0xc774, 0x00000f00, 0x00000800, + 0xc798, 0x00ffffff, 0x00ff7fbf, + 0xc79c, 0x00ffffff, 0x00ff7faf, + 0x8c00, 0x000000ff, 0x00000001, + 0x214f8, 0x01ff01ff, 0x00000002, + 0x21498, 0x007ff800, 0x00200000, + 0x2015c, 0xffffffff, 0x00000f40, + 0x88c4, 0x001f3ae3, 0x00000082, + 0x88d4, 0x0000001f, 0x00000010, + 0x30934, 0xffffffff, 0x00000000 +}; + + static void cik_init_golden_registers(struct radeon_device *rdev) { switch (rdev->family) { @@ -1504,6 +1548,20 @@ static void cik_init_golden_registers(struct radeon_device *rdev) kalindi_golden_spm_registers, (const u32)ARRAY_SIZE(kalindi_golden_spm_registers)); break; + case CHIP_MULLINS: + radeon_program_register_sequence(rdev, + kalindi_mgcg_cgcg_init, + (const u32)ARRAY_SIZE(kalindi_mgcg_cgcg_init)); + radeon_program_register_sequence(rdev, + godavari_golden_registers, + (const u32)ARRAY_SIZE(godavari_golden_registers)); + radeon_program_register_sequence(rdev, + kalindi_golden_common_registers, + (const u32)ARRAY_SIZE(kalindi_golden_common_registers)); + radeon_program_register_sequence(rdev, + kalindi_golden_spm_registers, + (const u32)ARRAY_SIZE(kalindi_golden_spm_registers)); + break; case CHIP_KAVERI: radeon_program_register_sequence(rdev, spectre_mgcg_cgcg_init, @@ -1834,6 +1892,15 @@ static int cik_init_microcode(struct radeon_device *rdev) rlc_req_size = KB_RLC_UCODE_SIZE * 4; sdma_req_size = CIK_SDMA_UCODE_SIZE * 4; break; + case CHIP_MULLINS: + chip_name = "MULLINS"; + pfp_req_size = CIK_PFP_UCODE_SIZE * 4; + me_req_size = CIK_ME_UCODE_SIZE * 4; + ce_req_size = CIK_CE_UCODE_SIZE * 4; + mec_req_size = CIK_MEC_UCODE_SIZE * 4; + rlc_req_size = ML_RLC_UCODE_SIZE * 4; + sdma_req_size = CIK_SDMA_UCODE_SIZE * 4; + break; default: BUG(); } @@ -2224,6 +2291,7 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev) gb_tile_moden = 0; break; } + rdev->config.cik.macrotile_mode_array[reg_offset] = gb_tile_moden; WREG32(GB_MACROTILE_MODE0 + (reg_offset * 4), gb_tile_moden); } } else if (num_pipe_configs == 8) { @@ -3191,7 +3259,7 @@ static void cik_gpu_init(struct radeon_device *rdev) u32 mc_shared_chmap, mc_arb_ramcfg; u32 hdp_host_path_cntl; u32 tmp; - int i, j; + int i, j, k; switch (rdev->family) { case CHIP_BONAIRE: @@ -3272,6 +3340,7 @@ static void cik_gpu_init(struct radeon_device *rdev) gb_addr_config = BONAIRE_GB_ADDR_CONFIG_GOLDEN; break; case CHIP_KABINI: + case CHIP_MULLINS: default: rdev->config.cik.max_shader_engines = 1; rdev->config.cik.max_tile_pipes = 2; @@ -3379,6 +3448,15 @@ static void cik_gpu_init(struct radeon_device *rdev) rdev->config.cik.max_sh_per_se, rdev->config.cik.max_backends_per_se); + for (i = 0; i < rdev->config.cik.max_shader_engines; i++) { + for (j = 0; j < rdev->config.cik.max_sh_per_se; j++) { + for (k = 0; k < rdev->config.cik.max_cu_per_sh; k++) { + rdev->config.cik.active_cus += + hweight32(cik_get_cu_active_bitmap(rdev, i, j)); + } + } + } + /* set HW defaults for 3D engine */ WREG32(CP_MEQ_THRESHOLDS, MEQ1_START(0x30) | MEQ2_START(0x60)); @@ -3631,7 +3709,7 @@ bool cik_semaphore_ring_emit(struct radeon_device *rdev, unsigned sel = emit_wait ? PACKET3_SEM_SEL_WAIT : PACKET3_SEM_SEL_SIGNAL; radeon_ring_write(ring, PACKET3(PACKET3_MEM_SEMAPHORE, 1)); - radeon_ring_write(ring, addr & 0xffffffff); + radeon_ring_write(ring, lower_32_bits(addr)); radeon_ring_write(ring, (upper_32_bits(addr) & 0xffff) | sel); return true; @@ -3702,6 +3780,7 @@ int cik_copy_cpdma(struct radeon_device *rdev, r = radeon_fence_emit(rdev, fence, ring->idx); if (r) { radeon_ring_unlock_undo(rdev, ring); + radeon_semaphore_free(rdev, &sem, NULL); return r; } @@ -3750,7 +3829,7 @@ void cik_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib) radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); radeon_ring_write(ring, WRITE_DATA_DST_SEL(1)); radeon_ring_write(ring, ring->next_rptr_gpu_addr & 0xfffffffc); - radeon_ring_write(ring, upper_32_bits(ring->next_rptr_gpu_addr) & 0xffffffff); + radeon_ring_write(ring, upper_32_bits(ring->next_rptr_gpu_addr)); radeon_ring_write(ring, next_rptr); } @@ -5378,7 +5457,7 @@ static int cik_pcie_gart_enable(struct radeon_device *rdev) (u32)(rdev->dummy_page.addr >> 12)); WREG32(VM_CONTEXT1_CNTL2, 4); WREG32(VM_CONTEXT1_CNTL, ENABLE_CONTEXT | PAGE_TABLE_DEPTH(1) | - PAGE_TABLE_BLOCK_SIZE(RADEON_VM_BLOCK_SIZE - 9) | + PAGE_TABLE_BLOCK_SIZE(radeon_vm_block_size - 9) | RANGE_PROTECTION_FAULT_ENABLE_INTERRUPT | RANGE_PROTECTION_FAULT_ENABLE_DEFAULT | DUMMY_PAGE_PROTECTION_FAULT_ENABLE_INTERRUPT | @@ -5803,6 +5882,9 @@ static int cik_rlc_resume(struct radeon_device *rdev) case CHIP_KABINI: size = KB_RLC_UCODE_SIZE; break; + case CHIP_MULLINS: + size = ML_RLC_UCODE_SIZE; + break; } cik_rlc_stop(rdev); @@ -6551,6 +6633,7 @@ void cik_get_csb_buffer(struct radeon_device *rdev, volatile u32 *buffer) buffer[count++] = cpu_to_le32(0x00000000); break; case CHIP_KABINI: + case CHIP_MULLINS: buffer[count++] = cpu_to_le32(0x00000000); /* XXX */ buffer[count++] = cpu_to_le32(0x00000000); break; @@ -6696,6 +6779,19 @@ static void cik_disable_interrupt_state(struct radeon_device *rdev) WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC4_REGISTER_OFFSET, 0); WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC5_REGISTER_OFFSET, 0); } + /* pflip */ + if (rdev->num_crtc >= 2) { + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, 0); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, 0); + } + if (rdev->num_crtc >= 4) { + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, 0); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, 0); + } + if (rdev->num_crtc >= 6) { + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, 0); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, 0); + } /* dac hotplug */ WREG32(DAC_AUTODETECT_INT_CONTROL, 0); @@ -7052,6 +7148,25 @@ int cik_irq_set(struct radeon_device *rdev) WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC5_REGISTER_OFFSET, crtc6); } + if (rdev->num_crtc >= 2) { + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); + } + if (rdev->num_crtc >= 4) { + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); + } + if (rdev->num_crtc >= 6) { + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); + } + WREG32(DC_HPD1_INT_CONTROL, hpd1); WREG32(DC_HPD2_INT_CONTROL, hpd2); WREG32(DC_HPD3_INT_CONTROL, hpd3); @@ -7088,6 +7203,29 @@ static inline void cik_irq_ack(struct radeon_device *rdev) rdev->irq.stat_regs.cik.disp_int_cont5 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE5); rdev->irq.stat_regs.cik.disp_int_cont6 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE6); + rdev->irq.stat_regs.cik.d1grph_int = RREG32(GRPH_INT_STATUS + + EVERGREEN_CRTC0_REGISTER_OFFSET); + rdev->irq.stat_regs.cik.d2grph_int = RREG32(GRPH_INT_STATUS + + EVERGREEN_CRTC1_REGISTER_OFFSET); + if (rdev->num_crtc >= 4) { + rdev->irq.stat_regs.cik.d3grph_int = RREG32(GRPH_INT_STATUS + + EVERGREEN_CRTC2_REGISTER_OFFSET); + rdev->irq.stat_regs.cik.d4grph_int = RREG32(GRPH_INT_STATUS + + EVERGREEN_CRTC3_REGISTER_OFFSET); + } + if (rdev->num_crtc >= 6) { + rdev->irq.stat_regs.cik.d5grph_int = RREG32(GRPH_INT_STATUS + + EVERGREEN_CRTC4_REGISTER_OFFSET); + rdev->irq.stat_regs.cik.d6grph_int = RREG32(GRPH_INT_STATUS + + EVERGREEN_CRTC5_REGISTER_OFFSET); + } + + if (rdev->irq.stat_regs.cik.d1grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC0_REGISTER_OFFSET, + GRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.cik.d2grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC1_REGISTER_OFFSET, + GRPH_PFLIP_INT_CLEAR); if (rdev->irq.stat_regs.cik.disp_int & LB_D1_VBLANK_INTERRUPT) WREG32(LB_VBLANK_STATUS + EVERGREEN_CRTC0_REGISTER_OFFSET, VBLANK_ACK); if (rdev->irq.stat_regs.cik.disp_int & LB_D1_VLINE_INTERRUPT) @@ -7098,6 +7236,12 @@ static inline void cik_irq_ack(struct radeon_device *rdev) WREG32(LB_VLINE_STATUS + EVERGREEN_CRTC1_REGISTER_OFFSET, VLINE_ACK); if (rdev->num_crtc >= 4) { + if (rdev->irq.stat_regs.cik.d3grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC2_REGISTER_OFFSET, + GRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.cik.d4grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC3_REGISTER_OFFSET, + GRPH_PFLIP_INT_CLEAR); if (rdev->irq.stat_regs.cik.disp_int_cont2 & LB_D3_VBLANK_INTERRUPT) WREG32(LB_VBLANK_STATUS + EVERGREEN_CRTC2_REGISTER_OFFSET, VBLANK_ACK); if (rdev->irq.stat_regs.cik.disp_int_cont2 & LB_D3_VLINE_INTERRUPT) @@ -7109,6 +7253,12 @@ static inline void cik_irq_ack(struct radeon_device *rdev) } if (rdev->num_crtc >= 6) { + if (rdev->irq.stat_regs.cik.d5grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC4_REGISTER_OFFSET, + GRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.cik.d6grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC5_REGISTER_OFFSET, + GRPH_PFLIP_INT_CLEAR); if (rdev->irq.stat_regs.cik.disp_int_cont4 & LB_D5_VBLANK_INTERRUPT) WREG32(LB_VBLANK_STATUS + EVERGREEN_CRTC4_REGISTER_OFFSET, VBLANK_ACK); if (rdev->irq.stat_regs.cik.disp_int_cont4 & LB_D5_VLINE_INTERRUPT) @@ -7227,6 +7377,7 @@ static inline u32 cik_get_ih_wptr(struct radeon_device *rdev) tmp = RREG32(IH_RB_CNTL); tmp |= IH_WPTR_OVERFLOW_CLEAR; WREG32(IH_RB_CNTL, tmp); + wptr &= ~RB_OVERFLOW; } return (wptr & rdev->ih.ptr_mask); } @@ -7460,6 +7611,15 @@ restart_ih: break; } break; + case 8: /* D1 page flip */ + case 10: /* D2 page flip */ + case 12: /* D3 page flip */ + case 14: /* D4 page flip */ + case 16: /* D5 page flip */ + case 18: /* D6 page flip */ + DRM_DEBUG("IH: D%d flip\n", ((src_id - 8) >> 1) + 1); + radeon_crtc_handle_flip(rdev, (src_id - 8) >> 1); + break; case 42: /* HPD hotplug */ switch (src_data) { case 0: @@ -7518,14 +7678,16 @@ restart_ih: addr = RREG32(VM_CONTEXT1_PROTECTION_FAULT_ADDR); status = RREG32(VM_CONTEXT1_PROTECTION_FAULT_STATUS); mc_client = RREG32(VM_CONTEXT1_PROTECTION_FAULT_MCCLIENT); + /* reset addr and status */ + WREG32_P(VM_CONTEXT1_CNTL2, 1, ~1); + if (addr == 0x0 && status == 0x0) + break; dev_err(rdev->dev, "GPU fault detected: %d 0x%08x\n", src_id, src_data); dev_err(rdev->dev, " VM_CONTEXT1_PROTECTION_FAULT_ADDR 0x%08X\n", addr); dev_err(rdev->dev, " VM_CONTEXT1_PROTECTION_FAULT_STATUS 0x%08X\n", status); cik_vm_decode_fault(rdev, status, addr, mc_client); - /* reset addr and status */ - WREG32_P(VM_CONTEXT1_CNTL2, 1, ~1); break; case 167: /* VCE */ DRM_DEBUG("IH: VCE int: 0x%08x\n", src_data); diff --git a/drivers/gpu/drm/radeon/cik_sdma.c b/drivers/gpu/drm/radeon/cik_sdma.c index 3c2407bad1f..8e9d0f1d858 100644 --- a/drivers/gpu/drm/radeon/cik_sdma.c +++ b/drivers/gpu/drm/radeon/cik_sdma.c @@ -141,7 +141,7 @@ void cik_sdma_ring_ib_execute(struct radeon_device *rdev, next_rptr += 4; radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_WRITE, SDMA_WRITE_SUB_OPCODE_LINEAR, 0)); radeon_ring_write(ring, ring->next_rptr_gpu_addr & 0xfffffffc); - radeon_ring_write(ring, upper_32_bits(ring->next_rptr_gpu_addr) & 0xffffffff); + radeon_ring_write(ring, upper_32_bits(ring->next_rptr_gpu_addr)); radeon_ring_write(ring, 1); /* number of DWs to follow */ radeon_ring_write(ring, next_rptr); } @@ -151,7 +151,7 @@ void cik_sdma_ring_ib_execute(struct radeon_device *rdev, radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_NOP, 0, 0)); radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_INDIRECT_BUFFER, 0, extra_bits)); radeon_ring_write(ring, ib->gpu_addr & 0xffffffe0); /* base must be 32 byte aligned */ - radeon_ring_write(ring, upper_32_bits(ib->gpu_addr) & 0xffffffff); + radeon_ring_write(ring, upper_32_bits(ib->gpu_addr)); radeon_ring_write(ring, ib->length_dw); } @@ -203,8 +203,8 @@ void cik_sdma_fence_ring_emit(struct radeon_device *rdev, /* write the fence */ radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_FENCE, 0, 0)); - radeon_ring_write(ring, addr & 0xffffffff); - radeon_ring_write(ring, upper_32_bits(addr) & 0xffffffff); + radeon_ring_write(ring, lower_32_bits(addr)); + radeon_ring_write(ring, upper_32_bits(addr)); radeon_ring_write(ring, fence->seq); /* generate an interrupt */ radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_TRAP, 0, 0)); @@ -233,7 +233,7 @@ bool cik_sdma_semaphore_ring_emit(struct radeon_device *rdev, radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SEMAPHORE, 0, extra_bits)); radeon_ring_write(ring, addr & 0xfffffff8); - radeon_ring_write(ring, upper_32_bits(addr) & 0xffffffff); + radeon_ring_write(ring, upper_32_bits(addr)); return true; } @@ -551,10 +551,10 @@ int cik_copy_dma(struct radeon_device *rdev, radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_COPY, SDMA_COPY_SUB_OPCODE_LINEAR, 0)); radeon_ring_write(ring, cur_size_in_bytes); radeon_ring_write(ring, 0); /* src/dst endian swap */ - radeon_ring_write(ring, src_offset & 0xffffffff); - radeon_ring_write(ring, upper_32_bits(src_offset) & 0xffffffff); - radeon_ring_write(ring, dst_offset & 0xffffffff); - radeon_ring_write(ring, upper_32_bits(dst_offset) & 0xffffffff); + radeon_ring_write(ring, lower_32_bits(src_offset)); + radeon_ring_write(ring, upper_32_bits(src_offset)); + radeon_ring_write(ring, lower_32_bits(dst_offset)); + radeon_ring_write(ring, upper_32_bits(dst_offset)); src_offset += cur_size_in_bytes; dst_offset += cur_size_in_bytes; } @@ -562,6 +562,7 @@ int cik_copy_dma(struct radeon_device *rdev, r = radeon_fence_emit(rdev, fence, ring->idx); if (r) { radeon_ring_unlock_undo(rdev, ring); + radeon_semaphore_free(rdev, &sem, NULL); return r; } @@ -604,7 +605,7 @@ int cik_sdma_ring_test(struct radeon_device *rdev, } radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_WRITE, SDMA_WRITE_SUB_OPCODE_LINEAR, 0)); radeon_ring_write(ring, rdev->vram_scratch.gpu_addr & 0xfffffffc); - radeon_ring_write(ring, upper_32_bits(rdev->vram_scratch.gpu_addr) & 0xffffffff); + radeon_ring_write(ring, upper_32_bits(rdev->vram_scratch.gpu_addr)); radeon_ring_write(ring, 1); /* number of DWs to follow */ radeon_ring_write(ring, 0xDEADBEEF); radeon_ring_unlock_commit(rdev, ring); @@ -659,7 +660,7 @@ int cik_sdma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring) ib.ptr[0] = SDMA_PACKET(SDMA_OPCODE_WRITE, SDMA_WRITE_SUB_OPCODE_LINEAR, 0); ib.ptr[1] = rdev->vram_scratch.gpu_addr & 0xfffffffc; - ib.ptr[2] = upper_32_bits(rdev->vram_scratch.gpu_addr) & 0xffffffff; + ib.ptr[2] = upper_32_bits(rdev->vram_scratch.gpu_addr); ib.ptr[3] = 1; ib.ptr[4] = 0xDEADBEEF; ib.length_dw = 5; @@ -751,9 +752,9 @@ void cik_sdma_vm_set_page(struct radeon_device *rdev, ib->ptr[ib->length_dw++] = SDMA_PACKET(SDMA_OPCODE_COPY, SDMA_WRITE_SUB_OPCODE_LINEAR, 0); ib->ptr[ib->length_dw++] = bytes; ib->ptr[ib->length_dw++] = 0; /* src/dst endian swap */ - ib->ptr[ib->length_dw++] = src & 0xffffffff; + ib->ptr[ib->length_dw++] = lower_32_bits(src); ib->ptr[ib->length_dw++] = upper_32_bits(src); - ib->ptr[ib->length_dw++] = pe & 0xffffffff; + ib->ptr[ib->length_dw++] = lower_32_bits(pe); ib->ptr[ib->length_dw++] = upper_32_bits(pe); pe += bytes; diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h index 0b27ea08c29..0c6e1b55d96 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -889,6 +889,15 @@ # define DC_HPD6_RX_INTERRUPT (1 << 18) #define DISP_INTERRUPT_STATUS_CONTINUE6 0x6780 +/* 0x6858, 0x7458, 0x10058, 0x10c58, 0x11858, 0x12458 */ +#define GRPH_INT_STATUS 0x6858 +# define GRPH_PFLIP_INT_OCCURRED (1 << 0) +# define GRPH_PFLIP_INT_CLEAR (1 << 8) +/* 0x685c, 0x745c, 0x1005c, 0x10c5c, 0x1185c, 0x1245c */ +#define GRPH_INT_CONTROL 0x685c +# define GRPH_PFLIP_INT_MASK (1 << 0) +# define GRPH_PFLIP_INT_TYPE (1 << 8) + #define DAC_AUTODETECT_INT_CONTROL 0x67c8 #define DC_HPD1_INT_STATUS 0x601c @@ -1743,12 +1752,12 @@ #define EOP_TC_WB_ACTION_EN (1 << 15) /* L2 */ #define EOP_TCL1_ACTION_EN (1 << 16) #define EOP_TC_ACTION_EN (1 << 17) /* L2 */ +#define EOP_TCL2_VOLATILE (1 << 24) #define EOP_CACHE_POLICY(x) ((x) << 25) /* 0 - LRU * 1 - Stream * 2 - Bypass */ -#define EOP_TCL2_VOLATILE (1 << 27) #define DATA_SEL(x) ((x) << 29) /* 0 - discard * 1 - send low 32bit data diff --git a/drivers/gpu/drm/radeon/cypress_dpm.c b/drivers/gpu/drm/radeon/cypress_dpm.c index 5a9a5f4d788..47d31e91575 100644 --- a/drivers/gpu/drm/radeon/cypress_dpm.c +++ b/drivers/gpu/drm/radeon/cypress_dpm.c @@ -1551,7 +1551,7 @@ int cypress_populate_smc_voltage_tables(struct radeon_device *rdev, table->voltageMaskTable.highMask[RV770_SMC_VOLTAGEMASK_VDDCI] = 0; table->voltageMaskTable.lowMask[RV770_SMC_VOLTAGEMASK_VDDCI] = - cpu_to_be32(eg_pi->vddc_voltage_table.mask_low); + cpu_to_be32(eg_pi->vddci_voltage_table.mask_low); } return 0; diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 0318230ef27..15e4f28015e 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -189,7 +189,7 @@ static const u32 evergreen_golden_registers[] = 0x8c1c, 0xffffffff, 0x00001010, 0x28350, 0xffffffff, 0x00000000, 0xa008, 0xffffffff, 0x00010000, - 0x5cc, 0xffffffff, 0x00000001, + 0x5c4, 0xffffffff, 0x00000001, 0x9508, 0xffffffff, 0x00000002, 0x913c, 0x0000000f, 0x0000000a }; @@ -476,7 +476,7 @@ static const u32 cedar_golden_registers[] = 0x8c1c, 0xffffffff, 0x00001010, 0x28350, 0xffffffff, 0x00000000, 0xa008, 0xffffffff, 0x00010000, - 0x5cc, 0xffffffff, 0x00000001, + 0x5c4, 0xffffffff, 0x00000001, 0x9508, 0xffffffff, 0x00000002 }; @@ -635,7 +635,7 @@ static const u32 juniper_mgcg_init[] = static const u32 supersumo_golden_registers[] = { 0x5eb4, 0xffffffff, 0x00000002, - 0x5cc, 0xffffffff, 0x00000001, + 0x5c4, 0xffffffff, 0x00000001, 0x7030, 0xffffffff, 0x00000011, 0x7c30, 0xffffffff, 0x00000011, 0x6104, 0x01000300, 0x00000000, @@ -719,7 +719,7 @@ static const u32 sumo_golden_registers[] = static const u32 wrestler_golden_registers[] = { 0x5eb4, 0xffffffff, 0x00000002, - 0x5cc, 0xffffffff, 0x00000001, + 0x5c4, 0xffffffff, 0x00000001, 0x7030, 0xffffffff, 0x00000011, 0x7c30, 0xffffffff, 0x00000011, 0x6104, 0x01000300, 0x00000000, @@ -2642,8 +2642,9 @@ void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *s for (i = 0; i < rdev->num_crtc; i++) { if (save->crtc_enabled[i]) { tmp = RREG32(EVERGREEN_MASTER_UPDATE_MODE + crtc_offsets[i]); - if ((tmp & 0x3) != 0) { - tmp &= ~0x3; + if ((tmp & 0x7) != 3) { + tmp &= ~0x7; + tmp |= 0x3; WREG32(EVERGREEN_MASTER_UPDATE_MODE + crtc_offsets[i], tmp); } tmp = RREG32(EVERGREEN_GRPH_UPDATE + crtc_offsets[i]); @@ -3337,6 +3338,18 @@ static void evergreen_gpu_init(struct radeon_device *rdev) disabled_rb_mask &= ~(1 << i); } + for (i = 0; i < rdev->config.evergreen.num_ses; i++) { + u32 simd_disable_bitmap; + + WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_INDEX(i)); + WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_INDEX(i)); + simd_disable_bitmap = (RREG32(CC_GC_SHADER_PIPE_CONFIG) & 0xffff0000) >> 16; + simd_disable_bitmap |= 0xffffffff << rdev->config.evergreen.max_simds; + tmp <<= 16; + tmp |= simd_disable_bitmap; + } + rdev->config.evergreen.active_simds = hweight32(~tmp); + WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES); WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES); @@ -4355,7 +4368,6 @@ int evergreen_irq_set(struct radeon_device *rdev) u32 crtc1 = 0, crtc2 = 0, crtc3 = 0, crtc4 = 0, crtc5 = 0, crtc6 = 0; u32 hpd1, hpd2, hpd3, hpd4, hpd5, hpd6; u32 grbm_int_cntl = 0; - u32 grph1 = 0, grph2 = 0, grph3 = 0, grph4 = 0, grph5 = 0, grph6 = 0; u32 afmt1 = 0, afmt2 = 0, afmt3 = 0, afmt4 = 0, afmt5 = 0, afmt6 = 0; u32 dma_cntl, dma_cntl1 = 0; u32 thermal_int = 0; @@ -4538,15 +4550,21 @@ int evergreen_irq_set(struct radeon_device *rdev) WREG32(INT_MASK + EVERGREEN_CRTC5_REGISTER_OFFSET, crtc6); } - WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, grph1); - WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, grph2); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); if (rdev->num_crtc >= 4) { - WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, grph3); - WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, grph4); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); } if (rdev->num_crtc >= 6) { - WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, grph5); - WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, grph6); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); } WREG32(DC_HPD1_INT_CONTROL, hpd1); @@ -4738,6 +4756,7 @@ static u32 evergreen_get_ih_wptr(struct radeon_device *rdev) tmp = RREG32(IH_RB_CNTL); tmp |= IH_WPTR_OVERFLOW_CLEAR; WREG32(IH_RB_CNTL, tmp); + wptr &= ~RB_OVERFLOW; } return (wptr & rdev->ih.ptr_mask); } @@ -4935,6 +4954,15 @@ restart_ih: break; } break; + case 8: /* D1 page flip */ + case 10: /* D2 page flip */ + case 12: /* D3 page flip */ + case 14: /* D4 page flip */ + case 16: /* D5 page flip */ + case 18: /* D6 page flip */ + DRM_DEBUG("IH: D%d flip\n", ((src_id - 8) >> 1) + 1); + radeon_crtc_handle_flip(rdev, (src_id - 8) >> 1); + break; case 42: /* HPD hotplug */ switch (src_data) { case 0: @@ -5040,14 +5068,16 @@ restart_ih: case 147: addr = RREG32(VM_CONTEXT1_PROTECTION_FAULT_ADDR); status = RREG32(VM_CONTEXT1_PROTECTION_FAULT_STATUS); + /* reset addr and status */ + WREG32_P(VM_CONTEXT1_CNTL2, 1, ~1); + if (addr == 0x0 && status == 0x0) + break; dev_err(rdev->dev, "GPU fault detected: %d 0x%08x\n", src_id, src_data); dev_err(rdev->dev, " VM_CONTEXT1_PROTECTION_FAULT_ADDR 0x%08X\n", addr); dev_err(rdev->dev, " VM_CONTEXT1_PROTECTION_FAULT_STATUS 0x%08X\n", status); cayman_vm_decode_fault(rdev, status, addr); - /* reset addr and status */ - WREG32_P(VM_CONTEXT1_CNTL2, 1, ~1); break; case 176: /* CP_INT in ring buffer */ case 177: /* CP_INT in IB1 */ diff --git a/drivers/gpu/drm/radeon/evergreen_dma.c b/drivers/gpu/drm/radeon/evergreen_dma.c index 287fe966d7d..478caefe0fe 100644 --- a/drivers/gpu/drm/radeon/evergreen_dma.c +++ b/drivers/gpu/drm/radeon/evergreen_dma.c @@ -151,6 +151,7 @@ int evergreen_copy_dma(struct radeon_device *rdev, r = radeon_fence_emit(rdev, fence, ring->idx); if (r) { radeon_ring_unlock_undo(rdev, ring); + radeon_semaphore_free(rdev, &sem, NULL); return r; } diff --git a/drivers/gpu/drm/radeon/evergreen_reg.h b/drivers/gpu/drm/radeon/evergreen_reg.h index a0f63ff5a5e..23bff590fb6 100644 --- a/drivers/gpu/drm/radeon/evergreen_reg.h +++ b/drivers/gpu/drm/radeon/evergreen_reg.h @@ -116,6 +116,8 @@ # define EVERGREEN_GRPH_ARRAY_LINEAR_ALIGNED 1 # define EVERGREEN_GRPH_ARRAY_1D_TILED_THIN1 2 # define EVERGREEN_GRPH_ARRAY_2D_TILED_THIN1 4 +#define EVERGREEN_GRPH_LUT_10BIT_BYPASS_CONTROL 0x6808 +# define EVERGREEN_LUT_10BIT_BYPASS_EN (1 << 8) #define EVERGREEN_GRPH_SWAP_CONTROL 0x680c # define EVERGREEN_GRPH_ENDIAN_SWAP(x) (((x) & 0x3) << 0) # define EVERGREEN_GRPH_ENDIAN_NONE 0 @@ -237,7 +239,6 @@ # define EVERGREEN_CRTC_V_BLANK (1 << 0) #define EVERGREEN_CRTC_STATUS_POSITION 0x6e90 #define EVERGREEN_CRTC_STATUS_HV_COUNT 0x6ea0 -#define EVERGREEN_MASTER_UPDATE_MODE 0x6ef8 #define EVERGREEN_CRTC_UPDATE_LOCK 0x6ed4 #define EVERGREEN_MASTER_UPDATE_LOCK 0x6ef4 #define EVERGREEN_MASTER_UPDATE_MODE 0x6ef8 diff --git a/drivers/gpu/drm/radeon/kv_dpm.c b/drivers/gpu/drm/radeon/kv_dpm.c index 16ec9d56a23..9ef8c38f2d6 100644 --- a/drivers/gpu/drm/radeon/kv_dpm.c +++ b/drivers/gpu/drm/radeon/kv_dpm.c @@ -546,6 +546,52 @@ static int kv_set_divider_value(struct radeon_device *rdev, return 0; } +static u32 kv_convert_vid2_to_vid7(struct radeon_device *rdev, + struct sumo_vid_mapping_table *vid_mapping_table, + u32 vid_2bit) +{ + struct radeon_clock_voltage_dependency_table *vddc_sclk_table = + &rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk; + u32 i; + + if (vddc_sclk_table && vddc_sclk_table->count) { + if (vid_2bit < vddc_sclk_table->count) + return vddc_sclk_table->entries[vid_2bit].v; + else + return vddc_sclk_table->entries[vddc_sclk_table->count - 1].v; + } else { + for (i = 0; i < vid_mapping_table->num_entries; i++) { + if (vid_mapping_table->entries[i].vid_2bit == vid_2bit) + return vid_mapping_table->entries[i].vid_7bit; + } + return vid_mapping_table->entries[vid_mapping_table->num_entries - 1].vid_7bit; + } +} + +static u32 kv_convert_vid7_to_vid2(struct radeon_device *rdev, + struct sumo_vid_mapping_table *vid_mapping_table, + u32 vid_7bit) +{ + struct radeon_clock_voltage_dependency_table *vddc_sclk_table = + &rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk; + u32 i; + + if (vddc_sclk_table && vddc_sclk_table->count) { + for (i = 0; i < vddc_sclk_table->count; i++) { + if (vddc_sclk_table->entries[i].v == vid_7bit) + return i; + } + return vddc_sclk_table->count - 1; + } else { + for (i = 0; i < vid_mapping_table->num_entries; i++) { + if (vid_mapping_table->entries[i].vid_7bit == vid_7bit) + return vid_mapping_table->entries[i].vid_2bit; + } + + return vid_mapping_table->entries[vid_mapping_table->num_entries - 1].vid_2bit; + } +} + static u16 kv_convert_8bit_index_to_voltage(struct radeon_device *rdev, u16 voltage) { @@ -556,9 +602,9 @@ static u16 kv_convert_2bit_index_to_voltage(struct radeon_device *rdev, u32 vid_2bit) { struct kv_power_info *pi = kv_get_pi(rdev); - u32 vid_8bit = sumo_convert_vid2_to_vid7(rdev, - &pi->sys_info.vid_mapping_table, - vid_2bit); + u32 vid_8bit = kv_convert_vid2_to_vid7(rdev, + &pi->sys_info.vid_mapping_table, + vid_2bit); return kv_convert_8bit_index_to_voltage(rdev, (u16)vid_8bit); } @@ -639,7 +685,7 @@ static int kv_force_lowest_valid(struct radeon_device *rdev) static int kv_unforce_levels(struct radeon_device *rdev) { - if (rdev->family == CHIP_KABINI) + if (rdev->family == CHIP_KABINI || rdev->family == CHIP_MULLINS) return kv_notify_message_to_smu(rdev, PPSMC_MSG_NoForcedLevel); else return kv_set_enabled_levels(rdev); @@ -1362,13 +1408,20 @@ static int kv_update_uvd_dpm(struct radeon_device *rdev, bool gate) struct radeon_uvd_clock_voltage_dependency_table *table = &rdev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table; int ret; + u32 mask; if (!gate) { - if (!pi->caps_uvd_dpm || table->count || pi->caps_stable_p_state) + if (table->count) pi->uvd_boot_level = table->count - 1; else pi->uvd_boot_level = 0; + if (!pi->caps_uvd_dpm || pi->caps_stable_p_state) { + mask = 1 << pi->uvd_boot_level; + } else { + mask = 0x1f; + } + ret = kv_copy_bytes_to_smc(rdev, pi->dpm_table_start + offsetof(SMU7_Fusion_DpmTable, UvdBootLevel), @@ -1377,11 +1430,9 @@ static int kv_update_uvd_dpm(struct radeon_device *rdev, bool gate) if (ret) return ret; - if (!pi->caps_uvd_dpm || - pi->caps_stable_p_state) - kv_send_msg_to_smc_with_parameter(rdev, - PPSMC_MSG_UVDDPM_SetEnabledMask, - (1 << pi->uvd_boot_level)); + kv_send_msg_to_smc_with_parameter(rdev, + PPSMC_MSG_UVDDPM_SetEnabledMask, + mask); } return kv_enable_uvd_dpm(rdev, !gate); @@ -1617,7 +1668,7 @@ static void kv_dpm_powergate_acp(struct radeon_device *rdev, bool gate) if (pi->acp_power_gated == gate) return; - if (rdev->family == CHIP_KABINI) + if (rdev->family == CHIP_KABINI || rdev->family == CHIP_MULLINS) return; pi->acp_power_gated = gate; @@ -1786,7 +1837,7 @@ int kv_dpm_set_power_state(struct radeon_device *rdev) } } - if (rdev->family == CHIP_KABINI) { + if (rdev->family == CHIP_KABINI || rdev->family == CHIP_MULLINS) { if (pi->enable_dpm) { kv_set_valid_clock_range(rdev, new_ps); kv_update_dfs_bypass_settings(rdev, new_ps); @@ -1812,6 +1863,8 @@ int kv_dpm_set_power_state(struct radeon_device *rdev) return ret; } kv_update_sclk_t(rdev); + if (rdev->family == CHIP_MULLINS) + kv_enable_nb_dpm(rdev); } } else { if (pi->enable_dpm) { @@ -1862,7 +1915,7 @@ void kv_dpm_reset_asic(struct radeon_device *rdev) { struct kv_power_info *pi = kv_get_pi(rdev); - if (rdev->family == CHIP_KABINI) { + if (rdev->family == CHIP_KABINI || rdev->family == CHIP_MULLINS) { kv_force_lowest_valid(rdev); kv_init_graphics_levels(rdev); kv_program_bootup_state(rdev); @@ -1901,14 +1954,41 @@ static void kv_construct_max_power_limits_table(struct radeon_device *rdev, static void kv_patch_voltage_values(struct radeon_device *rdev) { int i; - struct radeon_uvd_clock_voltage_dependency_table *table = + struct radeon_uvd_clock_voltage_dependency_table *uvd_table = &rdev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table; + struct radeon_vce_clock_voltage_dependency_table *vce_table = + &rdev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table; + struct radeon_clock_voltage_dependency_table *samu_table = + &rdev->pm.dpm.dyn_state.samu_clock_voltage_dependency_table; + struct radeon_clock_voltage_dependency_table *acp_table = + &rdev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table; - if (table->count) { - for (i = 0; i < table->count; i++) - table->entries[i].v = + if (uvd_table->count) { + for (i = 0; i < uvd_table->count; i++) + uvd_table->entries[i].v = kv_convert_8bit_index_to_voltage(rdev, - table->entries[i].v); + uvd_table->entries[i].v); + } + + if (vce_table->count) { + for (i = 0; i < vce_table->count; i++) + vce_table->entries[i].v = + kv_convert_8bit_index_to_voltage(rdev, + vce_table->entries[i].v); + } + + if (samu_table->count) { + for (i = 0; i < samu_table->count; i++) + samu_table->entries[i].v = + kv_convert_8bit_index_to_voltage(rdev, + samu_table->entries[i].v); + } + + if (acp_table->count) { + for (i = 0; i < acp_table->count; i++) + acp_table->entries[i].v = + kv_convert_8bit_index_to_voltage(rdev, + acp_table->entries[i].v); } } @@ -1941,7 +2021,7 @@ static int kv_force_dpm_highest(struct radeon_device *rdev) break; } - if (rdev->family == CHIP_KABINI) + if (rdev->family == CHIP_KABINI || rdev->family == CHIP_MULLINS) return kv_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_DPM_ForceState, i); else return kv_set_enabled_level(rdev, i); @@ -1961,7 +2041,7 @@ static int kv_force_dpm_lowest(struct radeon_device *rdev) break; } - if (rdev->family == CHIP_KABINI) + if (rdev->family == CHIP_KABINI || rdev->family == CHIP_MULLINS) return kv_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_DPM_ForceState, i); else return kv_set_enabled_level(rdev, i); @@ -2118,7 +2198,7 @@ static void kv_apply_state_adjust_rules(struct radeon_device *rdev, else pi->battery_state = false; - if (rdev->family == CHIP_KABINI) { + if (rdev->family == CHIP_KABINI || rdev->family == CHIP_MULLINS) { ps->dpm0_pg_nb_ps_lo = 0x1; ps->dpm0_pg_nb_ps_hi = 0x0; ps->dpmx_nb_ps_lo = 0x1; @@ -2179,7 +2259,7 @@ static int kv_calculate_nbps_level_settings(struct radeon_device *rdev) if (pi->lowest_valid > pi->highest_valid) return -EINVAL; - if (rdev->family == CHIP_KABINI) { + if (rdev->family == CHIP_KABINI || rdev->family == CHIP_MULLINS) { for (i = pi->lowest_valid; i <= pi->highest_valid; i++) { pi->graphics_level[i].GnbSlow = 1; pi->graphics_level[i].ForceNbPs1 = 0; @@ -2253,9 +2333,9 @@ static void kv_init_graphics_levels(struct radeon_device *rdev) break; kv_set_divider_value(rdev, i, table->entries[i].clk); - vid_2bit = sumo_convert_vid7_to_vid2(rdev, - &pi->sys_info.vid_mapping_table, - table->entries[i].v); + vid_2bit = kv_convert_vid7_to_vid2(rdev, + &pi->sys_info.vid_mapping_table, + table->entries[i].v); kv_set_vid(rdev, i, vid_2bit); kv_set_at(rdev, i, pi->at[i]); kv_dpm_power_level_enabled_for_throttle(rdev, i, true); @@ -2324,7 +2404,7 @@ static void kv_program_nbps_index_settings(struct radeon_device *rdev, struct kv_power_info *pi = kv_get_pi(rdev); u32 nbdpmconfig1; - if (rdev->family == CHIP_KABINI) + if (rdev->family == CHIP_KABINI || rdev->family == CHIP_MULLINS) return; if (pi->sys_info.nb_dpm_enable) { @@ -2631,9 +2711,6 @@ int kv_dpm_init(struct radeon_device *rdev) pi->sram_end = SMC_RAM_END; - if (rdev->family == CHIP_KABINI) - pi->high_voltage_t = 4001; - pi->enable_nb_dpm = true; pi->caps_power_containment = true; @@ -2649,7 +2726,7 @@ int kv_dpm_init(struct radeon_device *rdev) pi->caps_sclk_ds = true; pi->enable_auto_thermal_throttling = true; pi->disable_nb_ps3_in_battery = false; - pi->bapm_enable = false; + pi->bapm_enable = true; pi->voltage_drop_t = 0; pi->caps_sclk_throttle_low_notification = false; pi->caps_fps = false; /* true? */ diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c index 1d3209ffbbd..5a33ca68186 100644 --- a/drivers/gpu/drm/radeon/ni.c +++ b/drivers/gpu/drm/radeon/ni.c @@ -1057,6 +1057,18 @@ static void cayman_gpu_init(struct radeon_device *rdev) disabled_rb_mask &= ~(1 << i); } + for (i = 0; i < rdev->config.cayman.max_shader_engines; i++) { + u32 simd_disable_bitmap; + + WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_INDEX(i)); + WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_INDEX(i)); + simd_disable_bitmap = (RREG32(CC_GC_SHADER_PIPE_CONFIG) & 0xffff0000) >> 16; + simd_disable_bitmap |= 0xffffffff << rdev->config.cayman.max_simds_per_se; + tmp <<= 16; + tmp |= simd_disable_bitmap; + } + rdev->config.cayman.active_simds = hweight32(~tmp); + WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES); WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES); @@ -1268,7 +1280,7 @@ static int cayman_pcie_gart_enable(struct radeon_device *rdev) (u32)(rdev->dummy_page.addr >> 12)); WREG32(VM_CONTEXT1_CNTL2, 4); WREG32(VM_CONTEXT1_CNTL, ENABLE_CONTEXT | PAGE_TABLE_DEPTH(1) | - PAGE_TABLE_BLOCK_SIZE(RADEON_VM_BLOCK_SIZE - 9) | + PAGE_TABLE_BLOCK_SIZE(radeon_vm_block_size - 9) | RANGE_PROTECTION_FAULT_ENABLE_INTERRUPT | RANGE_PROTECTION_FAULT_ENABLE_DEFAULT | DUMMY_PAGE_PROTECTION_FAULT_ENABLE_INTERRUPT | @@ -1346,7 +1358,7 @@ void cayman_fence_ring_emit(struct radeon_device *rdev, /* EVENT_WRITE_EOP - flush caches, send int */ radeon_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4)); radeon_ring_write(ring, EVENT_TYPE(CACHE_FLUSH_AND_INV_EVENT_TS) | EVENT_INDEX(5)); - radeon_ring_write(ring, addr & 0xffffffff); + radeon_ring_write(ring, lower_32_bits(addr)); radeon_ring_write(ring, (upper_32_bits(addr) & 0xff) | DATA_SEL(1) | INT_SEL(2)); radeon_ring_write(ring, fence->seq); radeon_ring_write(ring, 0); diff --git a/drivers/gpu/drm/radeon/ni_dpm.c b/drivers/gpu/drm/radeon/ni_dpm.c index 004c931606c..01fc4888e6f 100644 --- a/drivers/gpu/drm/radeon/ni_dpm.c +++ b/drivers/gpu/drm/radeon/ni_dpm.c @@ -1315,7 +1315,7 @@ static void ni_populate_smc_voltage_tables(struct radeon_device *rdev, table->voltageMaskTable.highMask[NISLANDS_SMC_VOLTAGEMASK_VDDCI] = 0; table->voltageMaskTable.lowMask[NISLANDS_SMC_VOLTAGEMASK_VDDCI] = - cpu_to_be32(eg_pi->vddc_voltage_table.mask_low); + cpu_to_be32(eg_pi->vddci_voltage_table.mask_low); } } diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index ad99813cfa8..1544efcf1c3 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -682,15 +682,11 @@ void r100_pci_gart_disable(struct radeon_device *rdev) WREG32(RADEON_AIC_HI_ADDR, 0); } -int r100_pci_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr) +void r100_pci_gart_set_page(struct radeon_device *rdev, unsigned i, + uint64_t addr) { u32 *gtt = rdev->gart.ptr; - - if (i < 0 || i > rdev->gart.num_gpu_pages) { - return -EINVAL; - } gtt[i] = cpu_to_le32(lower_32_bits(addr)); - return 0; } void r100_pci_gart_fini(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c index 206caf9700b..3c21d77a483 100644 --- a/drivers/gpu/drm/radeon/r300.c +++ b/drivers/gpu/drm/radeon/r300.c @@ -72,13 +72,11 @@ void rv370_pcie_gart_tlb_flush(struct radeon_device *rdev) #define R300_PTE_WRITEABLE (1 << 2) #define R300_PTE_READABLE (1 << 3) -int rv370_pcie_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr) +void rv370_pcie_gart_set_page(struct radeon_device *rdev, unsigned i, + uint64_t addr) { void __iomem *ptr = rdev->gart.ptr; - if (i < 0 || i > rdev->gart.num_gpu_pages) { - return -EINVAL; - } addr = (lower_32_bits(addr) >> 8) | ((upper_32_bits(addr) & 0xff) << 24) | R300_PTE_WRITEABLE | R300_PTE_READABLE; @@ -86,7 +84,6 @@ int rv370_pcie_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr) * on powerpc without HW swappers, it'll get swapped on way * into VRAM - so no need for cpu_to_le32 on VRAM tables */ writel(addr, ((void __iomem *)ptr) + (i * 4)); - return 0; } int rv370_pcie_gart_init(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/r500_reg.h b/drivers/gpu/drm/radeon/r500_reg.h index 1dd0d32993d..136b7bc7cd2 100644 --- a/drivers/gpu/drm/radeon/r500_reg.h +++ b/drivers/gpu/drm/radeon/r500_reg.h @@ -402,6 +402,7 @@ * block and vice versa. This applies to GRPH, CUR, etc. */ #define AVIVO_D1GRPH_LUT_SEL 0x6108 +# define AVIVO_LUT_10BIT_BYPASS_EN (1 << 8) #define AVIVO_D1GRPH_PRIMARY_SURFACE_ADDRESS 0x6110 #define R700_D1GRPH_PRIMARY_SURFACE_ADDRESS_HIGH 0x6914 #define R700_D2GRPH_PRIMARY_SURFACE_ADDRESS_HIGH 0x6114 diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 436e55092e9..3c69f58e46e 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -1958,6 +1958,9 @@ static void r600_gpu_init(struct radeon_device *rdev) if (tmp < rdev->config.r600.max_simds) { rdev->config.r600.max_simds = tmp; } + tmp = rdev->config.r600.max_simds - + r600_count_pipe_bits((cc_gc_shader_pipe_config >> 16) & R6XX_MAX_SIMDS_MASK); + rdev->config.r600.active_simds = tmp; disabled_rb_mask = (RREG32(CC_RB_BACKEND_DISABLE) >> 16) & R6XX_MAX_BACKENDS_MASK; tmp = (tiling_config & PIPE_TILING__MASK) >> PIPE_TILING__SHIFT; @@ -2724,7 +2727,7 @@ void r600_fence_ring_emit(struct radeon_device *rdev, /* EVENT_WRITE_EOP - flush caches, send int */ radeon_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4)); radeon_ring_write(ring, EVENT_TYPE(CACHE_FLUSH_AND_INV_EVENT_TS) | EVENT_INDEX(5)); - radeon_ring_write(ring, addr & 0xffffffff); + radeon_ring_write(ring, lower_32_bits(addr)); radeon_ring_write(ring, (upper_32_bits(addr) & 0xff) | DATA_SEL(1) | INT_SEL(2)); radeon_ring_write(ring, fence->seq); radeon_ring_write(ring, 0); @@ -2763,7 +2766,7 @@ bool r600_semaphore_ring_emit(struct radeon_device *rdev, sel |= PACKET3_SEM_WAIT_ON_SIGNAL; radeon_ring_write(ring, PACKET3(PACKET3_MEM_SEMAPHORE, 1)); - radeon_ring_write(ring, addr & 0xffffffff); + radeon_ring_write(ring, lower_32_bits(addr)); radeon_ring_write(ring, (upper_32_bits(addr) & 0xff) | sel); return true; @@ -2824,9 +2827,9 @@ int r600_copy_cpdma(struct radeon_device *rdev, if (size_in_bytes == 0) tmp |= PACKET3_CP_DMA_CP_SYNC; radeon_ring_write(ring, PACKET3(PACKET3_CP_DMA, 4)); - radeon_ring_write(ring, src_offset & 0xffffffff); + radeon_ring_write(ring, lower_32_bits(src_offset)); radeon_ring_write(ring, tmp); - radeon_ring_write(ring, dst_offset & 0xffffffff); + radeon_ring_write(ring, lower_32_bits(dst_offset)); radeon_ring_write(ring, upper_32_bits(dst_offset) & 0xff); radeon_ring_write(ring, cur_size_in_bytes); src_offset += cur_size_in_bytes; @@ -2839,6 +2842,7 @@ int r600_copy_cpdma(struct radeon_device *rdev, r = radeon_fence_emit(rdev, fence, ring->idx); if (r) { radeon_ring_unlock_undo(rdev, ring); + radeon_semaphore_free(rdev, &sem, NULL); return r; } @@ -3505,7 +3509,6 @@ int r600_irq_set(struct radeon_device *rdev) u32 hpd1, hpd2, hpd3, hpd4 = 0, hpd5 = 0, hpd6 = 0; u32 grbm_int_cntl = 0; u32 hdmi0, hdmi1; - u32 d1grph = 0, d2grph = 0; u32 dma_cntl; u32 thermal_int = 0; @@ -3614,8 +3617,8 @@ int r600_irq_set(struct radeon_device *rdev) WREG32(CP_INT_CNTL, cp_int_cntl); WREG32(DMA_CNTL, dma_cntl); WREG32(DxMODE_INT_MASK, mode_int); - WREG32(D1GRPH_INTERRUPT_CONTROL, d1grph); - WREG32(D2GRPH_INTERRUPT_CONTROL, d2grph); + WREG32(D1GRPH_INTERRUPT_CONTROL, DxGRPH_PFLIP_INT_MASK); + WREG32(D2GRPH_INTERRUPT_CONTROL, DxGRPH_PFLIP_INT_MASK); WREG32(GRBM_INT_CNTL, grbm_int_cntl); if (ASIC_IS_DCE3(rdev)) { WREG32(DC_HPD1_INT_CONTROL, hpd1); @@ -3792,6 +3795,7 @@ static u32 r600_get_ih_wptr(struct radeon_device *rdev) tmp = RREG32(IH_RB_CNTL); tmp |= IH_WPTR_OVERFLOW_CLEAR; WREG32(IH_RB_CNTL, tmp); + wptr &= ~RB_OVERFLOW; } return (wptr & rdev->ih.ptr_mask); } @@ -3918,6 +3922,14 @@ restart_ih: break; } break; + case 9: /* D1 pflip */ + DRM_DEBUG("IH: D1 flip\n"); + radeon_crtc_handle_flip(rdev, 0); + break; + case 11: /* D2 pflip */ + DRM_DEBUG("IH: D2 flip\n"); + radeon_crtc_handle_flip(rdev, 1); + break; case 19: /* HPD/DAC hotplug */ switch (src_data) { case 0: diff --git a/drivers/gpu/drm/radeon/r600_dma.c b/drivers/gpu/drm/radeon/r600_dma.c index 53fcb28f557..4969cef44a1 100644 --- a/drivers/gpu/drm/radeon/r600_dma.c +++ b/drivers/gpu/drm/radeon/r600_dma.c @@ -489,6 +489,7 @@ int r600_copy_dma(struct radeon_device *rdev, r = radeon_fence_emit(rdev, fence, ring->idx); if (r) { radeon_ring_unlock_undo(rdev, ring); + radeon_semaphore_free(rdev, &sem, NULL); return r; } diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index dd4da88b3ab..60c47f82912 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -100,6 +100,9 @@ extern int radeon_dpm; extern int radeon_aspm; extern int radeon_runtime_pm; extern int radeon_hard_reset; +extern int radeon_vm_size; +extern int radeon_vm_block_size; +extern int radeon_deep_color; /* * Copy from radeon_drv.h so we don't have to include both and have conflicting @@ -446,6 +449,7 @@ struct radeon_bo_va { /* protected by vm mutex */ struct list_head vm_list; + struct list_head vm_status; /* constant after initialization */ struct radeon_vm *vm; @@ -681,10 +685,9 @@ struct radeon_flip_work { struct work_struct unpin_work; struct radeon_device *rdev; int crtc_id; - struct drm_framebuffer *fb; + uint64_t base; struct drm_pending_vblank_event *event; struct radeon_bo *old_rbo; - struct radeon_bo *new_rbo; struct radeon_fence *fence; }; @@ -732,6 +735,12 @@ struct cik_irq_stat_regs { u32 disp_int_cont4; u32 disp_int_cont5; u32 disp_int_cont6; + u32 d1grph_int; + u32 d2grph_int; + u32 d3grph_int; + u32 d4grph_int; + u32 d5grph_int; + u32 d6grph_int; }; union radeon_irq_stat_regs { @@ -741,10 +750,6 @@ union radeon_irq_stat_regs { struct cik_irq_stat_regs cik; }; -#define RADEON_MAX_HPD_PINS 7 -#define RADEON_MAX_CRTCS 6 -#define RADEON_MAX_AFMT_BLOCKS 7 - struct radeon_irq { bool installed; spinlock_t lock; @@ -831,13 +836,8 @@ struct radeon_mec { /* maximum number of VMIDs */ #define RADEON_NUM_VM 16 -/* defines number of bits in page table versus page directory, - * a page is 4KB so we have 12 bits offset, 9 bits in the page - * table and the remaining 19 bits are in the page directory */ -#define RADEON_VM_BLOCK_SIZE 9 - /* number of entries in page table */ -#define RADEON_VM_PTE_COUNT (1 << RADEON_VM_BLOCK_SIZE) +#define RADEON_VM_PTE_COUNT (1 << radeon_vm_block_size) /* PTBs (Page Table Blocks) need to be aligned to 32K */ #define RADEON_VM_PTB_ALIGN_SIZE 32768 @@ -868,6 +868,9 @@ struct radeon_vm { struct list_head va; unsigned id; + /* BOs freed, but not yet updated in the PT */ + struct list_head freed; + /* contains the page directory */ struct radeon_bo *page_directory; uint64_t pd_gpu_addr; @@ -876,6 +879,8 @@ struct radeon_vm { /* array of page tables, one for each page directory entry */ struct radeon_vm_pt *page_tables; + struct radeon_bo_va *ib_bo_va; + struct mutex mutex; /* last fence for cs using this vm */ struct radeon_fence *fence; @@ -991,8 +996,8 @@ struct radeon_cs_reloc { struct radeon_bo *robj; struct ttm_validate_buffer tv; uint64_t gpu_offset; - unsigned domain; - unsigned alt_domain; + unsigned prefered_domains; + unsigned allowed_domains; uint32_t tiling_flags; uint32_t handle; }; @@ -1647,6 +1652,7 @@ struct radeon_vce { unsigned fb_version; atomic_t handles[RADEON_MAX_VCE_HANDLES]; struct drm_file *filp[RADEON_MAX_VCE_HANDLES]; + unsigned img_size[RADEON_MAX_VCE_HANDLES]; struct delayed_work idle_work; }; @@ -1660,7 +1666,7 @@ int radeon_vce_get_destroy_msg(struct radeon_device *rdev, int ring, uint32_t handle, struct radeon_fence **fence); void radeon_vce_free_handles(struct radeon_device *rdev, struct drm_file *filp); void radeon_vce_note_usage(struct radeon_device *rdev); -int radeon_vce_cs_reloc(struct radeon_cs_parser *p, int lo, int hi); +int radeon_vce_cs_reloc(struct radeon_cs_parser *p, int lo, int hi, unsigned size); int radeon_vce_cs_parse(struct radeon_cs_parser *p); bool radeon_vce_semaphore_emit(struct radeon_device *rdev, struct radeon_ring *ring, @@ -1775,7 +1781,8 @@ struct radeon_asic { /* gart */ struct { void (*tlb_flush)(struct radeon_device *rdev); - int (*set_page)(struct radeon_device *rdev, int i, uint64_t addr); + void (*set_page)(struct radeon_device *rdev, unsigned i, + uint64_t addr); } gart; struct { int (*init)(struct radeon_device *rdev); @@ -1927,6 +1934,7 @@ struct r600_asic { unsigned tiling_group_size; unsigned tile_config; unsigned backend_map; + unsigned active_simds; }; struct rv770_asic { @@ -1952,6 +1960,7 @@ struct rv770_asic { unsigned tiling_group_size; unsigned tile_config; unsigned backend_map; + unsigned active_simds; }; struct evergreen_asic { @@ -1978,6 +1987,7 @@ struct evergreen_asic { unsigned tiling_group_size; unsigned tile_config; unsigned backend_map; + unsigned active_simds; }; struct cayman_asic { @@ -2016,6 +2026,7 @@ struct cayman_asic { unsigned multi_gpu_tile_size; unsigned tile_config; + unsigned active_simds; }; struct si_asic { @@ -2046,6 +2057,7 @@ struct si_asic { unsigned tile_config; uint32_t tile_mode_array[32]; + uint32_t active_cus; }; struct cik_asic { @@ -2077,6 +2089,7 @@ struct cik_asic { unsigned tile_config; uint32_t tile_mode_array[32]; uint32_t macrotile_mode_array[16]; + uint32_t active_cus; }; union radeon_asic_config { @@ -2644,7 +2657,8 @@ void r100_pll_errata_after_index(struct radeon_device *rdev); #define ASIC_IS_DCE8(rdev) ((rdev->family >= CHIP_BONAIRE)) #define ASIC_IS_DCE81(rdev) ((rdev->family == CHIP_KAVERI)) #define ASIC_IS_DCE82(rdev) ((rdev->family == CHIP_BONAIRE)) -#define ASIC_IS_DCE83(rdev) ((rdev->family == CHIP_KABINI)) +#define ASIC_IS_DCE83(rdev) ((rdev->family == CHIP_KABINI) || \ + (rdev->family == CHIP_MULLINS)) #define ASIC_IS_LOMBOK(rdev) ((rdev->ddev->pdev->device == 0x6849) || \ (rdev->ddev->pdev->device == 0x6850) || \ @@ -2824,9 +2838,10 @@ void radeon_vm_fence(struct radeon_device *rdev, uint64_t radeon_vm_map_gart(struct radeon_device *rdev, uint64_t addr); int radeon_vm_update_page_directory(struct radeon_device *rdev, struct radeon_vm *vm); +int radeon_vm_clear_freed(struct radeon_device *rdev, + struct radeon_vm *vm); int radeon_vm_bo_update(struct radeon_device *rdev, - struct radeon_vm *vm, - struct radeon_bo *bo, + struct radeon_bo_va *bo_va, struct ttm_mem_reg *mem); void radeon_vm_bo_invalidate(struct radeon_device *rdev, struct radeon_bo *bo); @@ -2839,8 +2854,8 @@ int radeon_vm_bo_set_addr(struct radeon_device *rdev, struct radeon_bo_va *bo_va, uint64_t offset, uint32_t flags); -int radeon_vm_bo_rmv(struct radeon_device *rdev, - struct radeon_bo_va *bo_va); +void radeon_vm_bo_rmv(struct radeon_device *rdev, + struct radeon_bo_va *bo_va); /* audio */ void r600_audio_update_hdmi(struct work_struct *work); diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index d8e1587d89c..34b9aa9e3c0 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -2494,6 +2494,7 @@ int radeon_asic_init(struct radeon_device *rdev) break; case CHIP_KAVERI: case CHIP_KABINI: + case CHIP_MULLINS: rdev->asic = &kv_asic; /* set num crtcs */ if (rdev->family == CHIP_KAVERI) { diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 0eab015b2cf..01e7c0ad8f0 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -67,7 +67,8 @@ bool r100_gpu_is_lockup(struct radeon_device *rdev, struct radeon_ring *cp); int r100_asic_reset(struct radeon_device *rdev); u32 r100_get_vblank_counter(struct radeon_device *rdev, int crtc); void r100_pci_gart_tlb_flush(struct radeon_device *rdev); -int r100_pci_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr); +void r100_pci_gart_set_page(struct radeon_device *rdev, unsigned i, + uint64_t addr); void r100_ring_start(struct radeon_device *rdev, struct radeon_ring *ring); int r100_irq_set(struct radeon_device *rdev); int r100_irq_process(struct radeon_device *rdev); @@ -171,7 +172,8 @@ extern void r300_fence_ring_emit(struct radeon_device *rdev, struct radeon_fence *fence); extern int r300_cs_parse(struct radeon_cs_parser *p); extern void rv370_pcie_gart_tlb_flush(struct radeon_device *rdev); -extern int rv370_pcie_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr); +extern void rv370_pcie_gart_set_page(struct radeon_device *rdev, unsigned i, + uint64_t addr); extern void rv370_set_pcie_lanes(struct radeon_device *rdev, int lanes); extern int rv370_get_pcie_lanes(struct radeon_device *rdev); extern void r300_set_reg_safe(struct radeon_device *rdev); @@ -206,7 +208,8 @@ extern void rs400_fini(struct radeon_device *rdev); extern int rs400_suspend(struct radeon_device *rdev); extern int rs400_resume(struct radeon_device *rdev); void rs400_gart_tlb_flush(struct radeon_device *rdev); -int rs400_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr); +void rs400_gart_set_page(struct radeon_device *rdev, unsigned i, + uint64_t addr); uint32_t rs400_mc_rreg(struct radeon_device *rdev, uint32_t reg); void rs400_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); int rs400_gart_init(struct radeon_device *rdev); @@ -229,7 +232,8 @@ int rs600_irq_process(struct radeon_device *rdev); void rs600_irq_disable(struct radeon_device *rdev); u32 rs600_get_vblank_counter(struct radeon_device *rdev, int crtc); void rs600_gart_tlb_flush(struct radeon_device *rdev); -int rs600_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr); +void rs600_gart_set_page(struct radeon_device *rdev, unsigned i, + uint64_t addr); uint32_t rs600_mc_rreg(struct radeon_device *rdev, uint32_t reg); void rs600_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); void rs600_bandwidth_update(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index 30844814c25..173f378428a 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -1227,11 +1227,19 @@ bool radeon_atom_get_clock_info(struct drm_device *dev) rdev->clock.default_dispclk = le32_to_cpu(firmware_info->info_21.ulDefaultDispEngineClkFreq); if (rdev->clock.default_dispclk == 0) { - if (ASIC_IS_DCE5(rdev)) + if (ASIC_IS_DCE6(rdev)) + rdev->clock.default_dispclk = 60000; /* 600 Mhz */ + else if (ASIC_IS_DCE5(rdev)) rdev->clock.default_dispclk = 54000; /* 540 Mhz */ else rdev->clock.default_dispclk = 60000; /* 600 Mhz */ } + /* set a reasonable default for DP */ + if (ASIC_IS_DCE6(rdev) && (rdev->clock.default_dispclk < 53900)) { + DRM_INFO("Changing default dispclk from %dMhz to 600Mhz\n", + rdev->clock.default_dispclk / 100); + rdev->clock.default_dispclk = 60000; + } rdev->clock.dp_extclk = le16_to_cpu(firmware_info->info_21.usUniphyDPModeExtClkFreq); rdev->clock.current_dispclk = rdev->clock.default_dispclk; diff --git a/drivers/gpu/drm/radeon/radeon_bios.c b/drivers/gpu/drm/radeon/radeon_bios.c index b3633d9a531..6a03624fada 100644 --- a/drivers/gpu/drm/radeon/radeon_bios.c +++ b/drivers/gpu/drm/radeon/radeon_bios.c @@ -196,6 +196,20 @@ static bool radeon_atrm_get_bios(struct radeon_device *rdev) } } + if (!found) { + while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_OTHER << 8, pdev)) != NULL) { + dhandle = ACPI_HANDLE(&pdev->dev); + if (!dhandle) + continue; + + status = acpi_get_handle(dhandle, "ATRM", &atrm_handle); + if (!ACPI_FAILURE(status)) { + found = true; + break; + } + } + } + if (!found) return false; @@ -612,7 +626,7 @@ static bool radeon_acpi_vfct_bios(struct radeon_device *rdev) vhdr->DeviceID != rdev->pdev->device) { DRM_INFO("ACPI VFCT table is not for this card\n"); goto out_unmap; - }; + } if (vfct->VBIOSImageOffset + sizeof(VFCT_IMAGE_HEADER) + vhdr->ImageLength > tbl_size) { DRM_ERROR("ACPI VFCT image truncated\n"); diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 4522f7dce65..44831197e82 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -101,6 +101,7 @@ int radeon_get_monitor_bpc(struct drm_connector *connector) struct radeon_connector *radeon_connector = to_radeon_connector(connector); struct radeon_connector_atom_dig *dig_connector; int bpc = 8; + int mode_clock, max_tmds_clock; switch (connector->connector_type) { case DRM_MODE_CONNECTOR_DVII: @@ -166,8 +167,41 @@ int radeon_get_monitor_bpc(struct drm_connector *connector) connector->name, bpc); bpc = 12; } + + /* Any defined maximum tmds clock limit we must not exceed? */ + if (connector->max_tmds_clock > 0) { + /* mode_clock is clock in kHz for mode to be modeset on this connector */ + mode_clock = radeon_connector->pixelclock_for_modeset; + + /* Maximum allowable input clock in kHz */ + max_tmds_clock = connector->max_tmds_clock * 1000; + + DRM_DEBUG("%s: hdmi mode dotclock %d kHz, max tmds input clock %d kHz.\n", + connector->name, mode_clock, max_tmds_clock); + + /* Check if bpc is within clock limit. Try to degrade gracefully otherwise */ + if ((bpc == 12) && (mode_clock * 3/2 > max_tmds_clock)) { + if ((connector->display_info.edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_30) && + (mode_clock * 5/4 <= max_tmds_clock)) + bpc = 10; + else + bpc = 8; + + DRM_DEBUG("%s: HDMI deep color 12 bpc exceeds max tmds clock. Using %d bpc.\n", + connector->name, bpc); + } + + if ((bpc == 10) && (mode_clock * 5/4 > max_tmds_clock)) { + bpc = 8; + DRM_DEBUG("%s: HDMI deep color 10 bpc exceeds max tmds clock. Using %d bpc.\n", + connector->name, bpc); + } + } } + if ((radeon_deep_color == 0) && (bpc > 8)) + bpc = 8; + DRM_DEBUG("%s: Display bpc=%d, returned bpc=%d\n", connector->name, connector->display_info.bpc, bpc); @@ -1257,17 +1291,15 @@ static int radeon_dvi_mode_valid(struct drm_connector *connector, (radeon_connector->connector_object_id == CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D) || (radeon_connector->connector_object_id == CONNECTOR_OBJECT_ID_HDMI_TYPE_B)) return MODE_OK; - else if (radeon_connector->connector_object_id == CONNECTOR_OBJECT_ID_HDMI_TYPE_A) { - if (ASIC_IS_DCE6(rdev)) { - /* HDMI 1.3+ supports max clock of 340 Mhz */ - if (mode->clock > 340000) - return MODE_CLOCK_HIGH; - else - return MODE_OK; - } else + else if (ASIC_IS_DCE6(rdev) && drm_detect_hdmi_monitor(radeon_connector->edid)) { + /* HDMI 1.3+ supports max clock of 340 Mhz */ + if (mode->clock > 340000) return MODE_CLOCK_HIGH; - } else + else + return MODE_OK; + } else { return MODE_CLOCK_HIGH; + } } /* check against the max pixel clock */ @@ -1518,6 +1550,8 @@ out: static int radeon_dp_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { + struct drm_device *dev = connector->dev; + struct radeon_device *rdev = dev->dev_private; struct radeon_connector *radeon_connector = to_radeon_connector(connector); struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv; @@ -1548,14 +1582,23 @@ static int radeon_dp_mode_valid(struct drm_connector *connector, return MODE_PANEL; } } - return MODE_OK; } else { if ((radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) || - (radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) + (radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) { return radeon_dp_mode_valid_helper(connector, mode); - else - return MODE_OK; + } else { + if (ASIC_IS_DCE6(rdev) && drm_detect_hdmi_monitor(radeon_connector->edid)) { + /* HDMI 1.3+ supports max clock of 340 Mhz */ + if (mode->clock > 340000) + return MODE_CLOCK_HIGH; + } else { + if (mode->clock > 165000) + return MODE_CLOCK_HIGH; + } + } } + + return MODE_OK; } static const struct drm_connector_helper_funcs radeon_dp_connector_helper_funcs = { diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index 2b6e0ebcc13..ae763f60c8a 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -140,10 +140,10 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p) if (p->ring == R600_RING_TYPE_UVD_INDEX && (i == 0 || drm_pci_device_is_agp(p->rdev->ddev))) { /* TODO: is this still needed for NI+ ? */ - p->relocs[i].domain = + p->relocs[i].prefered_domains = RADEON_GEM_DOMAIN_VRAM; - p->relocs[i].alt_domain = + p->relocs[i].allowed_domains = RADEON_GEM_DOMAIN_VRAM; /* prioritize this over any other relocation */ @@ -152,10 +152,16 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p) uint32_t domain = r->write_domain ? r->write_domain : r->read_domains; - p->relocs[i].domain = domain; + if (domain & RADEON_GEM_DOMAIN_CPU) { + DRM_ERROR("RADEON_GEM_DOMAIN_CPU is not valid " + "for command submission\n"); + return -EINVAL; + } + + p->relocs[i].prefered_domains = domain; if (domain == RADEON_GEM_DOMAIN_VRAM) domain |= RADEON_GEM_DOMAIN_GTT; - p->relocs[i].alt_domain = domain; + p->relocs[i].allowed_domains = domain; } p->relocs[i].tv.bo = &p->relocs[i].robj->tbo; @@ -342,10 +348,17 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data) return -EINVAL; /* we only support VM on some SI+ rings */ - if ((p->rdev->asic->ring[p->ring]->cs_parse == NULL) && - ((p->cs_flags & RADEON_CS_USE_VM) == 0)) { - DRM_ERROR("Ring %d requires VM!\n", p->ring); - return -EINVAL; + if ((p->cs_flags & RADEON_CS_USE_VM) == 0) { + if (p->rdev->asic->ring[p->ring]->cs_parse == NULL) { + DRM_ERROR("Ring %d requires VM!\n", p->ring); + return -EINVAL; + } + } else { + if (p->rdev->asic->ring[p->ring]->ib_parse == NULL) { + DRM_ERROR("VM not supported on ring %d!\n", + p->ring); + return -EINVAL; + } } } @@ -448,13 +461,23 @@ static int radeon_bo_vm_update_pte(struct radeon_cs_parser *p, struct radeon_vm *vm) { struct radeon_device *rdev = p->rdev; + struct radeon_bo_va *bo_va; int i, r; r = radeon_vm_update_page_directory(rdev, vm); if (r) return r; - r = radeon_vm_bo_update(rdev, vm, rdev->ring_tmp_bo.bo, + r = radeon_vm_clear_freed(rdev, vm); + if (r) + return r; + + if (vm->ib_bo_va == NULL) { + DRM_ERROR("Tmp BO not in VM!\n"); + return -EINVAL; + } + + r = radeon_vm_bo_update(rdev, vm->ib_bo_va, &rdev->ring_tmp_bo.bo->tbo.mem); if (r) return r; @@ -467,7 +490,13 @@ static int radeon_bo_vm_update_pte(struct radeon_cs_parser *p, continue; bo = p->relocs[i].robj; - r = radeon_vm_bo_update(rdev, vm, bo, &bo->tbo.mem); + bo_va = radeon_vm_bo_find(vm, bo); + if (bo_va == NULL) { + dev_err(rdev->dev, "bo %p not in vm %p\n", bo, vm); + return -EINVAL; + } + + r = radeon_vm_bo_update(rdev, bo_va, &bo->tbo.mem); if (r) return r; } diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 9aa1afd1786..697add2cd4e 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -99,6 +99,7 @@ static const char radeon_family_name[][16] = { "KAVERI", "KABINI", "HAWAII", + "MULLINS", "LAST", }; @@ -1051,6 +1052,43 @@ static void radeon_check_arguments(struct radeon_device *rdev) radeon_agpmode = 0; break; } + + if (!radeon_check_pot_argument(radeon_vm_size)) { + dev_warn(rdev->dev, "VM size (%d) must be a power of 2\n", + radeon_vm_size); + radeon_vm_size = 4; + } + + if (radeon_vm_size < 1) { + dev_warn(rdev->dev, "VM size (%d) to small, min is 1GB\n", + radeon_vm_size); + radeon_vm_size = 4; + } + + /* + * Max GPUVM size for Cayman, SI and CI are 40 bits. + */ + if (radeon_vm_size > 1024) { + dev_warn(rdev->dev, "VM size (%d) too large, max is 1TB\n", + radeon_vm_size); + radeon_vm_size = 4; + } + + /* defines number of bits in page table versus page directory, + * a page is 4KB so we have 12 bits offset, minimum 9 bits in the + * page table and the remaining bits are in the page directory */ + if (radeon_vm_block_size < 9) { + dev_warn(rdev->dev, "VM page table size (%d) too small\n", + radeon_vm_block_size); + radeon_vm_block_size = 9; + } + + if (radeon_vm_block_size > 24 || + (radeon_vm_size * 1024) < (1ull << radeon_vm_block_size)) { + dev_warn(rdev->dev, "VM page table size (%d) too large\n", + radeon_vm_block_size); + radeon_vm_block_size = 9; + } } /** @@ -1196,17 +1234,16 @@ int radeon_device_init(struct radeon_device *rdev, if (r) return r; + radeon_check_arguments(rdev); /* Adjust VM size here. - * Currently set to 4GB ((1 << 20) 4k pages). - * Max GPUVM size for cayman and SI is 40 bits. + * Max GPUVM size for cayman+ is 40 bits. */ - rdev->vm_manager.max_pfn = 1 << 20; + rdev->vm_manager.max_pfn = radeon_vm_size << 18; /* Set asic functions */ r = radeon_asic_init(rdev); if (r) return r; - radeon_check_arguments(rdev); /* all of the newer IGP chips have an internal gart * However some rs4xx report as AGP, so remove that here. @@ -1533,11 +1570,6 @@ int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon) radeon_restore_bios_scratch_regs(rdev); - if (fbcon) { - radeon_fbdev_set_suspend(rdev, 0); - console_unlock(); - } - /* init dig PHYs, disp eng pll */ if (rdev->is_atom_bios) { radeon_atom_encoder_init(rdev); @@ -1562,6 +1594,16 @@ int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon) } drm_kms_helper_poll_enable(dev); + + /* set the power state here in case we are a PX system or headless */ + if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) + radeon_pm_compute_clocks(rdev); + + if (fbcon) { + radeon_fbdev_set_suspend(rdev, 0); + console_unlock(); + } + return 0; } diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index e330e762c36..bf25061c8ac 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -66,7 +66,8 @@ static void avivo_crtc_load_lut(struct drm_crtc *crtc) (radeon_crtc->lut_b[i] << 0)); } - WREG32(AVIVO_D1GRPH_LUT_SEL + radeon_crtc->crtc_offset, radeon_crtc->crtc_id); + /* Only change bit 0 of LUT_SEL, other bits are set elsewhere */ + WREG32_P(AVIVO_D1GRPH_LUT_SEL + radeon_crtc->crtc_offset, radeon_crtc->crtc_id, ~1); } static void dce4_crtc_load_lut(struct drm_crtc *crtc) @@ -284,14 +285,20 @@ static void radeon_unpin_work_func(struct work_struct *__work) void radeon_crtc_handle_vblank(struct radeon_device *rdev, int crtc_id) { struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id]; - struct radeon_flip_work *work; unsigned long flags; u32 update_pending; int vpos, hpos; + /* can happen during initialization */ + if (radeon_crtc == NULL) + return; + spin_lock_irqsave(&rdev->ddev->event_lock, flags); - work = radeon_crtc->flip_work; - if (work == NULL) { + if (radeon_crtc->flip_status != RADEON_FLIP_SUBMITTED) { + DRM_DEBUG_DRIVER("radeon_crtc->flip_status = %d != " + "RADEON_FLIP_SUBMITTED(%d)\n", + radeon_crtc->flip_status, + RADEON_FLIP_SUBMITTED); spin_unlock_irqrestore(&rdev->ddev->event_lock, flags); return; } @@ -339,12 +346,17 @@ void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id) spin_lock_irqsave(&rdev->ddev->event_lock, flags); work = radeon_crtc->flip_work; - if (work == NULL) { + if (radeon_crtc->flip_status != RADEON_FLIP_SUBMITTED) { + DRM_DEBUG_DRIVER("radeon_crtc->flip_status = %d != " + "RADEON_FLIP_SUBMITTED(%d)\n", + radeon_crtc->flip_status, + RADEON_FLIP_SUBMITTED); spin_unlock_irqrestore(&rdev->ddev->event_lock, flags); return; } /* Pageflip completed. Clean up. */ + radeon_crtc->flip_status = RADEON_FLIP_NONE; radeon_crtc->flip_work = NULL; /* wakeup userspace */ @@ -353,8 +365,8 @@ void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id) spin_unlock_irqrestore(&rdev->ddev->event_lock, flags); - radeon_fence_unref(&work->fence); - radeon_irq_kms_pflip_irq_get(rdev, work->crtc_id); + drm_vblank_put(rdev->ddev, radeon_crtc->crtc_id); + radeon_irq_kms_pflip_irq_put(rdev, work->crtc_id); queue_work(radeon_crtc->flip_queue, &work->unpin_work); } @@ -373,51 +385,108 @@ static void radeon_flip_work_func(struct work_struct *__work) struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[work->crtc_id]; struct drm_crtc *crtc = &radeon_crtc->base; - struct drm_framebuffer *fb = work->fb; - - uint32_t tiling_flags, pitch_pixels; - uint64_t base; - unsigned long flags; int r; down_read(&rdev->exclusive_lock); - while (work->fence) { + if (work->fence) { r = radeon_fence_wait(work->fence, false); if (r == -EDEADLK) { up_read(&rdev->exclusive_lock); r = radeon_gpu_reset(rdev); down_read(&rdev->exclusive_lock); } + if (r) + DRM_ERROR("failed to wait on page flip fence (%d)!\n", r); - if (r) { - DRM_ERROR("failed to wait on page flip fence (%d)!\n", - r); - goto cleanup; - } else - radeon_fence_unref(&work->fence); + /* We continue with the page flip even if we failed to wait on + * the fence, otherwise the DRM core and userspace will be + * confused about which BO the CRTC is scanning out + */ + + radeon_fence_unref(&work->fence); } + /* We borrow the event spin lock for protecting flip_status */ + spin_lock_irqsave(&crtc->dev->event_lock, flags); + + /* set the proper interrupt */ + radeon_irq_kms_pflip_irq_get(rdev, radeon_crtc->crtc_id); + + /* do the flip (mmio) */ + radeon_page_flip(rdev, radeon_crtc->crtc_id, work->base); + + radeon_crtc->flip_status = RADEON_FLIP_SUBMITTED; + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); + up_read(&rdev->exclusive_lock); +} + +static int radeon_crtc_page_flip(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event, + uint32_t page_flip_flags) +{ + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct radeon_framebuffer *old_radeon_fb; + struct radeon_framebuffer *new_radeon_fb; + struct drm_gem_object *obj; + struct radeon_flip_work *work; + struct radeon_bo *new_rbo; + uint32_t tiling_flags, pitch_pixels; + uint64_t base; + unsigned long flags; + int r; + + work = kzalloc(sizeof *work, GFP_KERNEL); + if (work == NULL) + return -ENOMEM; + + INIT_WORK(&work->flip_work, radeon_flip_work_func); + INIT_WORK(&work->unpin_work, radeon_unpin_work_func); + + work->rdev = rdev; + work->crtc_id = radeon_crtc->crtc_id; + work->event = event; + + /* schedule unpin of the old buffer */ + old_radeon_fb = to_radeon_framebuffer(crtc->primary->fb); + obj = old_radeon_fb->obj; + + /* take a reference to the old object */ + drm_gem_object_reference(obj); + work->old_rbo = gem_to_radeon_bo(obj); + + new_radeon_fb = to_radeon_framebuffer(fb); + obj = new_radeon_fb->obj; + new_rbo = gem_to_radeon_bo(obj); + + spin_lock(&new_rbo->tbo.bdev->fence_lock); + if (new_rbo->tbo.sync_obj) + work->fence = radeon_fence_ref(new_rbo->tbo.sync_obj); + spin_unlock(&new_rbo->tbo.bdev->fence_lock); + /* pin the new buffer */ - DRM_DEBUG_DRIVER("flip-ioctl() cur_fbo = %p, cur_bbo = %p\n", - work->old_rbo, work->new_rbo); + DRM_DEBUG_DRIVER("flip-ioctl() cur_rbo = %p, new_rbo = %p\n", + work->old_rbo, new_rbo); - r = radeon_bo_reserve(work->new_rbo, false); + r = radeon_bo_reserve(new_rbo, false); if (unlikely(r != 0)) { DRM_ERROR("failed to reserve new rbo buffer before flip\n"); goto cleanup; } /* Only 27 bit offset for legacy CRTC */ - r = radeon_bo_pin_restricted(work->new_rbo, RADEON_GEM_DOMAIN_VRAM, + r = radeon_bo_pin_restricted(new_rbo, RADEON_GEM_DOMAIN_VRAM, ASIC_IS_AVIVO(rdev) ? 0 : 1 << 27, &base); if (unlikely(r != 0)) { - radeon_bo_unreserve(work->new_rbo); + radeon_bo_unreserve(new_rbo); r = -EINVAL; DRM_ERROR("failed to pin new rbo buffer before flip\n"); goto cleanup; } - radeon_bo_get_tiling_flags(work->new_rbo, &tiling_flags, NULL); - radeon_bo_unreserve(work->new_rbo); + radeon_bo_get_tiling_flags(new_rbo, &tiling_flags, NULL); + radeon_bo_unreserve(new_rbo); if (!ASIC_IS_AVIVO(rdev)) { /* crtc offset is from display base addr not FB location */ @@ -454,92 +523,53 @@ static void radeon_flip_work_func(struct work_struct *__work) } base &= ~7; } + work->base = base; + + r = drm_vblank_get(crtc->dev, radeon_crtc->crtc_id); + if (r) { + DRM_ERROR("failed to get vblank before flip\n"); + goto pflip_cleanup; + } /* We borrow the event spin lock for protecting flip_work */ spin_lock_irqsave(&crtc->dev->event_lock, flags); + if (radeon_crtc->flip_status != RADEON_FLIP_NONE) { + DRM_DEBUG_DRIVER("flip queue: crtc already busy\n"); + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); + r = -EBUSY; + goto vblank_cleanup; + } + radeon_crtc->flip_status = RADEON_FLIP_PENDING; + radeon_crtc->flip_work = work; + /* update crtc fb */ crtc->primary->fb = fb; - /* set the proper interrupt */ - radeon_irq_kms_pflip_irq_get(rdev, radeon_crtc->crtc_id); + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); - /* do the flip (mmio) */ - radeon_page_flip(rdev, radeon_crtc->crtc_id, base); + queue_work(radeon_crtc->flip_queue, &work->flip_work); + return 0; - spin_unlock_irqrestore(&crtc->dev->event_lock, flags); - up_read(&rdev->exclusive_lock); +vblank_cleanup: + drm_vblank_put(crtc->dev, radeon_crtc->crtc_id); - return; +pflip_cleanup: + if (unlikely(radeon_bo_reserve(new_rbo, false) != 0)) { + DRM_ERROR("failed to reserve new rbo in error path\n"); + goto cleanup; + } + if (unlikely(radeon_bo_unpin(new_rbo) != 0)) { + DRM_ERROR("failed to unpin new rbo in error path\n"); + } + radeon_bo_unreserve(new_rbo); cleanup: drm_gem_object_unreference_unlocked(&work->old_rbo->gem_base); radeon_fence_unref(&work->fence); kfree(work); - up_read(&rdev->exclusive_lock); -} - -static int radeon_crtc_page_flip(struct drm_crtc *crtc, - struct drm_framebuffer *fb, - struct drm_pending_vblank_event *event, - uint32_t page_flip_flags) -{ - struct drm_device *dev = crtc->dev; - struct radeon_device *rdev = dev->dev_private; - struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); - struct radeon_framebuffer *old_radeon_fb; - struct radeon_framebuffer *new_radeon_fb; - struct drm_gem_object *obj; - struct radeon_flip_work *work; - unsigned long flags; - - work = kzalloc(sizeof *work, GFP_KERNEL); - if (work == NULL) - return -ENOMEM; - - INIT_WORK(&work->flip_work, radeon_flip_work_func); - INIT_WORK(&work->unpin_work, radeon_unpin_work_func); - - work->rdev = rdev; - work->crtc_id = radeon_crtc->crtc_id; - work->fb = fb; - work->event = event; - - /* schedule unpin of the old buffer */ - old_radeon_fb = to_radeon_framebuffer(crtc->primary->fb); - obj = old_radeon_fb->obj; - /* take a reference to the old object */ - drm_gem_object_reference(obj); - work->old_rbo = gem_to_radeon_bo(obj); - - new_radeon_fb = to_radeon_framebuffer(fb); - obj = new_radeon_fb->obj; - work->new_rbo = gem_to_radeon_bo(obj); - - spin_lock(&work->new_rbo->tbo.bdev->fence_lock); - if (work->new_rbo->tbo.sync_obj) - work->fence = radeon_fence_ref(work->new_rbo->tbo.sync_obj); - spin_unlock(&work->new_rbo->tbo.bdev->fence_lock); - - /* We borrow the event spin lock for protecting flip_work */ - spin_lock_irqsave(&crtc->dev->event_lock, flags); - - if (radeon_crtc->flip_work) { - DRM_DEBUG_DRIVER("flip queue: crtc already busy\n"); - spin_unlock_irqrestore(&crtc->dev->event_lock, flags); - drm_gem_object_unreference_unlocked(&work->old_rbo->gem_base); - radeon_fence_unref(&work->fence); - kfree(work); - return -EBUSY; - } - radeon_crtc->flip_work = work; - - spin_unlock_irqrestore(&crtc->dev->event_lock, flags); - - queue_work(radeon_crtc->flip_queue, &work->flip_work); - - return 0; + return r; } static int @@ -799,6 +829,10 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector) struct radeon_device *rdev = dev->dev_private; int ret = 0; + /* don't leak the edid if we already fetched it in detect() */ + if (radeon_connector->edid) + goto got_edid; + /* on hw with routers, select right port */ if (radeon_connector->router.ddc_valid) radeon_router_select_ddc_port(radeon_connector); @@ -837,6 +871,7 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector) radeon_connector->edid = radeon_bios_get_hardcoded_edid(rdev); } if (radeon_connector->edid) { +got_edid: drm_mode_connector_update_edid_property(&radeon_connector->base, radeon_connector->edid); ret = drm_add_edid_modes(&radeon_connector->base, radeon_connector->edid); drm_edid_to_eld(&radeon_connector->base, radeon_connector->edid); @@ -872,14 +907,14 @@ static void avivo_reduce_ratio(unsigned *nom, unsigned *den, /* make sure nominator is large enough */ if (*nom < nom_min) { - tmp = (nom_min + *nom - 1) / *nom; + tmp = DIV_ROUND_UP(nom_min, *nom); *nom *= tmp; *den *= tmp; } /* make sure the denominator is large enough */ if (*den < den_min) { - tmp = (den_min + *den - 1) / *den; + tmp = DIV_ROUND_UP(den_min, *den); *nom *= tmp; *den *= tmp; } @@ -904,7 +939,7 @@ static void avivo_get_fb_ref_div(unsigned nom, unsigned den, unsigned post_div, unsigned *fb_div, unsigned *ref_div) { /* limit reference * post divider to a maximum */ - ref_div_max = min(210 / post_div, ref_div_max); + ref_div_max = max(min(100 / post_div, ref_div_max), 1u); /* get matching reference and feedback divider */ *ref_div = min(max(DIV_ROUND_CLOSEST(den, post_div), 1u), ref_div_max); @@ -1039,6 +1074,16 @@ void radeon_compute_pll_avivo(struct radeon_pll *pll, /* this also makes sure that the reference divider is large enough */ avivo_reduce_ratio(&fb_div, &ref_div, fb_div_min, ref_div_min); + /* avoid high jitter with small fractional dividers */ + if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV && (fb_div % 10)) { + fb_div_min = max(fb_div_min, (9 - (fb_div % 10)) * 20 + 50); + if (fb_div < fb_div_min) { + unsigned tmp = DIV_ROUND_UP(fb_div_min, fb_div); + fb_div *= tmp; + ref_div *= tmp; + } + } + /* and finally save the result */ if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) { *fb_div_p = fb_div / 10; diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 15447a4119f..e9e36108424 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -81,9 +81,10 @@ * 2.37.0 - allow GS ring setup on r6xx/r7xx * 2.38.0 - RADEON_GEM_OP (GET_INITIAL_DOMAIN, SET_INITIAL_DOMAIN), * CIK: 1D and linear tiling modes contain valid PIPE_CONFIG + * 2.39.0 - Add INFO query for number of active CUs */ #define KMS_DRIVER_MAJOR 2 -#define KMS_DRIVER_MINOR 38 +#define KMS_DRIVER_MINOR 39 #define KMS_DRIVER_PATCHLEVEL 0 int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags); int radeon_driver_unload_kms(struct drm_device *dev); @@ -172,6 +173,9 @@ int radeon_dpm = -1; int radeon_aspm = -1; int radeon_runtime_pm = -1; int radeon_hard_reset = 0; +int radeon_vm_size = 4; +int radeon_vm_block_size = 9; +int radeon_deep_color = 0; MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers"); module_param_named(no_wb, radeon_no_wb, int, 0444); @@ -239,6 +243,15 @@ module_param_named(runpm, radeon_runtime_pm, int, 0444); MODULE_PARM_DESC(hard_reset, "PCI config reset (1 = force enable, 0 = disable (default))"); module_param_named(hard_reset, radeon_hard_reset, int, 0444); +MODULE_PARM_DESC(vm_size, "VM address space size in gigabytes (default 4GB)"); +module_param_named(vm_size, radeon_vm_size, int, 0444); + +MODULE_PARM_DESC(vm_block_size, "VM page table size in bits (default 9)"); +module_param_named(vm_block_size, radeon_vm_block_size, int, 0444); + +MODULE_PARM_DESC(deep_color, "Deep Color support (1 = enable, 0 = disable (default))"); +module_param_named(deep_color, radeon_deep_color, int, 0444); + static struct pci_device_id pciidlist[] = { radeon_PCI_IDS }; diff --git a/drivers/gpu/drm/radeon/radeon_family.h b/drivers/gpu/drm/radeon/radeon_family.h index 9da5da4ffd1..4b7b87f71a6 100644 --- a/drivers/gpu/drm/radeon/radeon_family.h +++ b/drivers/gpu/drm/radeon/radeon_family.h @@ -97,6 +97,7 @@ enum radeon_family { CHIP_KAVERI, CHIP_KABINI, CHIP_HAWAII, + CHIP_MULLINS, CHIP_LAST, }; diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c index a77b1c13ea4..913787085df 100644 --- a/drivers/gpu/drm/radeon/radeon_fence.c +++ b/drivers/gpu/drm/radeon/radeon_fence.c @@ -819,15 +819,35 @@ static int radeon_debugfs_fence_info(struct seq_file *m, void *data) return 0; } +/** + * radeon_debugfs_gpu_reset - manually trigger a gpu reset + * + * Manually trigger a gpu reset at the next fence wait. + */ +static int radeon_debugfs_gpu_reset(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct radeon_device *rdev = dev->dev_private; + + down_read(&rdev->exclusive_lock); + seq_printf(m, "%d\n", rdev->needs_reset); + rdev->needs_reset = true; + up_read(&rdev->exclusive_lock); + + return 0; +} + static struct drm_info_list radeon_debugfs_fence_list[] = { {"radeon_fence_info", &radeon_debugfs_fence_info, 0, NULL}, + {"radeon_gpu_reset", &radeon_debugfs_gpu_reset, 0, NULL} }; #endif int radeon_debugfs_fence_init(struct radeon_device *rdev) { #if defined(CONFIG_DEBUG_FS) - return radeon_debugfs_add_files(rdev, radeon_debugfs_fence_list, 1); + return radeon_debugfs_add_files(rdev, radeon_debugfs_fence_list, 2); #else return 0; #endif diff --git a/drivers/gpu/drm/radeon/radeon_i2c.c b/drivers/gpu/drm/radeon/radeon_i2c.c index 427ee4d6d0b..add62200840 100644 --- a/drivers/gpu/drm/radeon/radeon_i2c.c +++ b/drivers/gpu/drm/radeon/radeon_i2c.c @@ -988,7 +988,7 @@ void radeon_i2c_destroy(struct radeon_i2c_chan *i2c) return; i2c_del_adapter(&i2c->adapter); if (i2c->has_aux) - drm_dp_aux_unregister_i2c_bus(&i2c->aux); + drm_dp_aux_unregister(&i2c->aux); kfree(i2c); } diff --git a/drivers/gpu/drm/radeon/radeon_ioc32.c b/drivers/gpu/drm/radeon/radeon_ioc32.c index bdb0f93e73b..0b98ea13457 100644 --- a/drivers/gpu/drm/radeon/radeon_ioc32.c +++ b/drivers/gpu/drm/radeon/radeon_ioc32.c @@ -399,7 +399,7 @@ long radeon_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (nr < DRM_COMMAND_BASE) return drm_compat_ioctl(filp, cmd, arg); - if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(radeon_compat_ioctls)) + if (nr < DRM_COMMAND_BASE + ARRAY_SIZE(radeon_compat_ioctls)) fn = radeon_compat_ioctls[nr - DRM_COMMAND_BASE]; if (fn != NULL) diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 0cc47f12d99..d25ae6acfd5 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -513,6 +513,22 @@ static int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file value_size = sizeof(uint64_t); value64 = atomic64_read(&rdev->gtt_usage); break; + case RADEON_INFO_ACTIVE_CU_COUNT: + if (rdev->family >= CHIP_BONAIRE) + *value = rdev->config.cik.active_cus; + else if (rdev->family >= CHIP_TAHITI) + *value = rdev->config.si.active_cus; + else if (rdev->family >= CHIP_CAYMAN) + *value = rdev->config.cayman.active_simds; + else if (rdev->family >= CHIP_CEDAR) + *value = rdev->config.evergreen.active_simds; + else if (rdev->family >= CHIP_RV770) + *value = rdev->config.rv770.active_simds; + else if (rdev->family >= CHIP_R600) + *value = rdev->config.r600.active_simds; + else + *value = 1; + break; default: DRM_DEBUG_KMS("Invalid request %d\n", info->request); return -EINVAL; @@ -563,7 +579,7 @@ int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv) /* new gpu have virtual address space support */ if (rdev->family >= CHIP_CAYMAN) { struct radeon_fpriv *fpriv; - struct radeon_bo_va *bo_va; + struct radeon_vm *vm; int r; fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL); @@ -571,34 +587,37 @@ int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv) return -ENOMEM; } - r = radeon_vm_init(rdev, &fpriv->vm); + vm = &fpriv->vm; + r = radeon_vm_init(rdev, vm); if (r) { kfree(fpriv); return r; } - r = radeon_bo_reserve(rdev->ring_tmp_bo.bo, false); - if (r) { - radeon_vm_fini(rdev, &fpriv->vm); - kfree(fpriv); - return r; - } + if (rdev->accel_working) { + r = radeon_bo_reserve(rdev->ring_tmp_bo.bo, false); + if (r) { + radeon_vm_fini(rdev, vm); + kfree(fpriv); + return r; + } - /* map the ib pool buffer read only into - * virtual address space */ - bo_va = radeon_vm_bo_add(rdev, &fpriv->vm, - rdev->ring_tmp_bo.bo); - r = radeon_vm_bo_set_addr(rdev, bo_va, RADEON_VA_IB_OFFSET, - RADEON_VM_PAGE_READABLE | - RADEON_VM_PAGE_SNOOPED); + /* map the ib pool buffer read only into + * virtual address space */ + vm->ib_bo_va = radeon_vm_bo_add(rdev, vm, + rdev->ring_tmp_bo.bo); + r = radeon_vm_bo_set_addr(rdev, vm->ib_bo_va, + RADEON_VA_IB_OFFSET, + RADEON_VM_PAGE_READABLE | + RADEON_VM_PAGE_SNOOPED); - radeon_bo_unreserve(rdev->ring_tmp_bo.bo); - if (r) { - radeon_vm_fini(rdev, &fpriv->vm); - kfree(fpriv); - return r; + radeon_bo_unreserve(rdev->ring_tmp_bo.bo); + if (r) { + radeon_vm_fini(rdev, vm); + kfree(fpriv); + return r; + } } - file_priv->driver_priv = fpriv; } @@ -623,19 +642,19 @@ void radeon_driver_postclose_kms(struct drm_device *dev, /* new gpu have virtual address space support */ if (rdev->family >= CHIP_CAYMAN && file_priv->driver_priv) { struct radeon_fpriv *fpriv = file_priv->driver_priv; - struct radeon_bo_va *bo_va; + struct radeon_vm *vm = &fpriv->vm; int r; - r = radeon_bo_reserve(rdev->ring_tmp_bo.bo, false); - if (!r) { - bo_va = radeon_vm_bo_find(&fpriv->vm, - rdev->ring_tmp_bo.bo); - if (bo_va) - radeon_vm_bo_rmv(rdev, bo_va); - radeon_bo_unreserve(rdev->ring_tmp_bo.bo); + if (rdev->accel_working) { + r = radeon_bo_reserve(rdev->ring_tmp_bo.bo, false); + if (!r) { + if (vm->ib_bo_va) + radeon_vm_bo_rmv(rdev, vm->ib_bo_va); + radeon_bo_unreserve(rdev->ring_tmp_bo.bo); + } } - radeon_vm_fini(rdev, &fpriv->vm); + radeon_vm_fini(rdev, vm); kfree(fpriv); file_priv->driver_priv = NULL; } @@ -856,4 +875,4 @@ const struct drm_ioctl_desc radeon_ioctls_kms[] = { DRM_IOCTL_DEF_DRV(RADEON_GEM_VA, radeon_gem_va_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(RADEON_GEM_OP, radeon_gem_op_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), }; -int radeon_max_kms_ioctl = DRM_ARRAY_SIZE(radeon_ioctls_kms); +int radeon_max_kms_ioctl = ARRAY_SIZE(radeon_ioctls_kms); diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index ea72ad889a1..0592ddb0904 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -46,6 +46,10 @@ struct radeon_device; #define to_radeon_encoder(x) container_of(x, struct radeon_encoder, base) #define to_radeon_framebuffer(x) container_of(x, struct radeon_framebuffer, base) +#define RADEON_MAX_HPD_PINS 7 +#define RADEON_MAX_CRTCS 6 +#define RADEON_MAX_AFMT_BLOCKS 7 + enum radeon_rmx_type { RMX_OFF, RMX_FULL, @@ -233,8 +237,8 @@ struct radeon_mode_info { struct card_info *atom_card_info; enum radeon_connector_table connector_table; bool mode_config_initialized; - struct radeon_crtc *crtcs[6]; - struct radeon_afmt *afmt[7]; + struct radeon_crtc *crtcs[RADEON_MAX_CRTCS]; + struct radeon_afmt *afmt[RADEON_MAX_AFMT_BLOCKS]; /* DVI-I properties */ struct drm_property *coherent_mode_property; /* DAC enable load detect */ @@ -302,6 +306,12 @@ struct radeon_atom_ss { uint16_t amount; }; +enum radeon_flip_status { + RADEON_FLIP_NONE, + RADEON_FLIP_PENDING, + RADEON_FLIP_SUBMITTED +}; + struct radeon_crtc { struct drm_crtc base; int crtc_id; @@ -327,6 +337,7 @@ struct radeon_crtc { /* page flipping */ struct workqueue_struct *flip_queue; struct radeon_flip_work *flip_work; + enum radeon_flip_status flip_status; /* pll sharing */ struct radeon_atom_ss ss; bool ss_enabled; @@ -506,6 +517,7 @@ struct radeon_connector { struct radeon_i2c_chan *router_bus; enum radeon_connector_audio audio; enum radeon_connector_dither dither; + int pixelclock_for_modeset; }; struct radeon_framebuffer { diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index 95197aa4de4..6c717b257d6 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -446,7 +446,7 @@ int radeon_bo_list_validate(struct radeon_device *rdev, list_for_each_entry(lobj, head, tv.head) { bo = lobj->robj; if (!bo->pin_count) { - u32 domain = lobj->domain; + u32 domain = lobj->prefered_domains; u32 current_domain = radeon_mem_type_to_domain(bo->tbo.mem.mem_type); @@ -458,7 +458,7 @@ int radeon_bo_list_validate(struct radeon_device *rdev, * into account. We don't want to disallow buffer moves * completely. */ - if (current_domain != RADEON_GEM_DOMAIN_CPU && + if ((lobj->allowed_domains & current_domain) != 0 && (domain & current_domain) == 0 && /* will be moved */ bytes_moved > bytes_moved_threshold) { /* don't move it */ @@ -476,8 +476,9 @@ int radeon_bo_list_validate(struct radeon_device *rdev, initial_bytes_moved; if (unlikely(r)) { - if (r != -ERESTARTSYS && domain != lobj->alt_domain) { - domain = lobj->alt_domain; + if (r != -ERESTARTSYS && + domain != lobj->allowed_domains) { + domain = lobj->allowed_domains; goto retry; } ttm_eu_backoff_reservation(ticket, head); @@ -699,22 +700,30 @@ int radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo) rbo = container_of(bo, struct radeon_bo, tbo); radeon_bo_check_tiling(rbo, 0, 0); rdev = rbo->rdev; - if (bo->mem.mem_type == TTM_PL_VRAM) { - size = bo->mem.num_pages << PAGE_SHIFT; - offset = bo->mem.start << PAGE_SHIFT; - if ((offset + size) > rdev->mc.visible_vram_size) { - /* hurrah the memory is not visible ! */ - radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_VRAM); - rbo->placement.lpfn = rdev->mc.visible_vram_size >> PAGE_SHIFT; - r = ttm_bo_validate(bo, &rbo->placement, false, false); - if (unlikely(r != 0)) - return r; - offset = bo->mem.start << PAGE_SHIFT; - /* this should not happen */ - if ((offset + size) > rdev->mc.visible_vram_size) - return -EINVAL; - } + if (bo->mem.mem_type != TTM_PL_VRAM) + return 0; + + size = bo->mem.num_pages << PAGE_SHIFT; + offset = bo->mem.start << PAGE_SHIFT; + if ((offset + size) <= rdev->mc.visible_vram_size) + return 0; + + /* hurrah the memory is not visible ! */ + radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_VRAM); + rbo->placement.lpfn = rdev->mc.visible_vram_size >> PAGE_SHIFT; + r = ttm_bo_validate(bo, &rbo->placement, false, false); + if (unlikely(r == -ENOMEM)) { + radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_GTT); + return ttm_bo_validate(bo, &rbo->placement, false, false); + } else if (unlikely(r != 0)) { + return r; } + + offset = bo->mem.start << PAGE_SHIFT; + /* this should never happen */ + if ((offset + size) > rdev->mc.visible_vram_size) + return -EINVAL; + return 0; } diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 6fac8efe834..e447e390d09 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -73,8 +73,10 @@ void radeon_pm_acpi_event_handler(struct radeon_device *rdev) rdev->pm.dpm.ac_power = true; else rdev->pm.dpm.ac_power = false; - if (rdev->asic->dpm.enable_bapm) - radeon_dpm_enable_bapm(rdev, rdev->pm.dpm.ac_power); + if (rdev->family == CHIP_ARUBA) { + if (rdev->asic->dpm.enable_bapm) + radeon_dpm_enable_bapm(rdev, rdev->pm.dpm.ac_power); + } mutex_unlock(&rdev->pm.mutex); } else if (rdev->pm.pm_method == PM_METHOD_PROFILE) { if (rdev->pm.profile == PM_PROFILE_AUTO) { @@ -361,6 +363,11 @@ static ssize_t radeon_set_pm_profile(struct device *dev, struct drm_device *ddev = dev_get_drvdata(dev); struct radeon_device *rdev = ddev->dev_private; + /* Can't set profile when the card is off */ + if ((rdev->flags & RADEON_IS_PX) && + (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) + return -EINVAL; + mutex_lock(&rdev->pm.mutex); if (rdev->pm.pm_method == PM_METHOD_PROFILE) { if (strncmp("default", buf, strlen("default")) == 0) @@ -409,6 +416,13 @@ static ssize_t radeon_set_pm_method(struct device *dev, struct drm_device *ddev = dev_get_drvdata(dev); struct radeon_device *rdev = ddev->dev_private; + /* Can't set method when the card is off */ + if ((rdev->flags & RADEON_IS_PX) && + (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) { + count = -EINVAL; + goto fail; + } + /* we don't support the legacy modes with dpm */ if (rdev->pm.pm_method == PM_METHOD_DPM) { count = -EINVAL; @@ -446,6 +460,10 @@ static ssize_t radeon_get_dpm_state(struct device *dev, struct radeon_device *rdev = ddev->dev_private; enum radeon_pm_state_type pm = rdev->pm.dpm.user_state; + if ((rdev->flags & RADEON_IS_PX) && + (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) + return snprintf(buf, PAGE_SIZE, "off\n"); + return snprintf(buf, PAGE_SIZE, "%s\n", (pm == POWER_STATE_TYPE_BATTERY) ? "battery" : (pm == POWER_STATE_TYPE_BALANCED) ? "balanced" : "performance"); @@ -459,6 +477,11 @@ static ssize_t radeon_set_dpm_state(struct device *dev, struct drm_device *ddev = dev_get_drvdata(dev); struct radeon_device *rdev = ddev->dev_private; + /* Can't set dpm state when the card is off */ + if ((rdev->flags & RADEON_IS_PX) && + (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) + return -EINVAL; + mutex_lock(&rdev->pm.mutex); if (strncmp("battery", buf, strlen("battery")) == 0) rdev->pm.dpm.user_state = POWER_STATE_TYPE_BATTERY; @@ -485,6 +508,10 @@ static ssize_t radeon_get_dpm_forced_performance_level(struct device *dev, struct radeon_device *rdev = ddev->dev_private; enum radeon_dpm_forced_level level = rdev->pm.dpm.forced_level; + if ((rdev->flags & RADEON_IS_PX) && + (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) + return snprintf(buf, PAGE_SIZE, "off\n"); + return snprintf(buf, PAGE_SIZE, "%s\n", (level == RADEON_DPM_FORCED_LEVEL_AUTO) ? "auto" : (level == RADEON_DPM_FORCED_LEVEL_LOW) ? "low" : "high"); @@ -500,6 +527,11 @@ static ssize_t radeon_set_dpm_forced_performance_level(struct device *dev, enum radeon_dpm_forced_level level; int ret = 0; + /* Can't force performance level when the card is off */ + if ((rdev->flags & RADEON_IS_PX) && + (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) + return -EINVAL; + mutex_lock(&rdev->pm.mutex); if (strncmp("low", buf, strlen("low")) == 0) { level = RADEON_DPM_FORCED_LEVEL_LOW; @@ -538,8 +570,14 @@ static ssize_t radeon_hwmon_show_temp(struct device *dev, char *buf) { struct radeon_device *rdev = dev_get_drvdata(dev); + struct drm_device *ddev = rdev->ddev; int temp; + /* Can't get temperature when the card is off */ + if ((rdev->flags & RADEON_IS_PX) && + (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) + return -EINVAL; + if (rdev->asic->pm.get_temperature) temp = radeon_get_temperature(rdev); else @@ -948,6 +986,8 @@ void radeon_dpm_enable_uvd(struct radeon_device *rdev, bool enable) if (enable) { mutex_lock(&rdev->pm.mutex); rdev->pm.dpm.uvd_active = true; + /* disable this for now */ +#if 0 if ((rdev->pm.dpm.sd == 1) && (rdev->pm.dpm.hd == 0)) dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_SD; else if ((rdev->pm.dpm.sd == 2) && (rdev->pm.dpm.hd == 0)) @@ -957,6 +997,7 @@ void radeon_dpm_enable_uvd(struct radeon_device *rdev, bool enable) else if ((rdev->pm.dpm.sd == 0) && (rdev->pm.dpm.hd == 2)) dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD2; else +#endif dpm_state = POWER_STATE_TYPE_INTERNAL_UVD; rdev->pm.dpm.state = dpm_state; mutex_unlock(&rdev->pm.mutex); @@ -1068,7 +1109,6 @@ static void radeon_pm_resume_dpm(struct radeon_device *rdev) if (ret) goto dpm_resume_fail; rdev->pm.dpm_enabled = true; - radeon_pm_compute_clocks(rdev); return; dpm_resume_fail: @@ -1300,6 +1340,7 @@ int radeon_pm_init(struct radeon_device *rdev) case CHIP_KABINI: case CHIP_KAVERI: case CHIP_HAWAII: + case CHIP_MULLINS: /* DPM requires the RLC, RV770+ dGPU requires SMC */ if (!rdev->rlc_fw) rdev->pm.pm_method = PM_METHOD_PROFILE; @@ -1613,8 +1654,12 @@ static int radeon_debugfs_pm_info(struct seq_file *m, void *data) struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_device *dev = node->minor->dev; struct radeon_device *rdev = dev->dev_private; + struct drm_device *ddev = rdev->ddev; - if (rdev->pm.dpm_enabled) { + if ((rdev->flags & RADEON_IS_PX) && + (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) { + seq_printf(m, "PX asic powered off\n"); + } else if (rdev->pm.dpm_enabled) { mutex_lock(&rdev->pm.mutex); if (rdev->asic->dpm.debugfs_print_current_performance_level) radeon_dpm_debugfs_print_current_performance_level(rdev, m); diff --git a/drivers/gpu/drm/radeon/radeon_state.c b/drivers/gpu/drm/radeon/radeon_state.c index b576549fc78..23bb64fd775 100644 --- a/drivers/gpu/drm/radeon/radeon_state.c +++ b/drivers/gpu/drm/radeon/radeon_state.c @@ -3258,4 +3258,4 @@ struct drm_ioctl_desc radeon_ioctls[] = { DRM_IOCTL_DEF_DRV(RADEON_CS, r600_cs_legacy_ioctl, DRM_AUTH) }; -int radeon_max_ioctl = DRM_ARRAY_SIZE(radeon_ioctls); +int radeon_max_ioctl = ARRAY_SIZE(radeon_ioctls); diff --git a/drivers/gpu/drm/radeon/radeon_ucode.h b/drivers/gpu/drm/radeon/radeon_ucode.h index 58d12938c0b..4e7c3269b18 100644 --- a/drivers/gpu/drm/radeon/radeon_ucode.h +++ b/drivers/gpu/drm/radeon/radeon_ucode.h @@ -52,6 +52,7 @@ #define BONAIRE_RLC_UCODE_SIZE 2048 #define KB_RLC_UCODE_SIZE 2560 #define KV_RLC_UCODE_SIZE 2560 +#define ML_RLC_UCODE_SIZE 2560 /* MC */ #define BTC_MC_UCODE_SIZE 6024 diff --git a/drivers/gpu/drm/radeon/radeon_uvd.c b/drivers/gpu/drm/radeon/radeon_uvd.c index 5748bdaeacc..a4ad270e826 100644 --- a/drivers/gpu/drm/radeon/radeon_uvd.c +++ b/drivers/gpu/drm/radeon/radeon_uvd.c @@ -99,6 +99,7 @@ int radeon_uvd_init(struct radeon_device *rdev) case CHIP_KABINI: case CHIP_KAVERI: case CHIP_HAWAII: + case CHIP_MULLINS: fw_name = FIRMWARE_BONAIRE; break; @@ -465,6 +466,10 @@ static int radeon_uvd_cs_reloc(struct radeon_cs_parser *p, cmd = radeon_get_ib_value(p, p->idx) >> 1; if (cmd < 0x4) { + if (end <= start) { + DRM_ERROR("invalid reloc offset %X!\n", offset); + return -EINVAL; + } if ((end - start) < buf_sizes[cmd]) { DRM_ERROR("buffer (%d) to small (%d / %d)!\n", cmd, (unsigned)(end - start), buf_sizes[cmd]); @@ -807,7 +812,8 @@ void radeon_uvd_note_usage(struct radeon_device *rdev) (rdev->pm.dpm.hd != hd)) { rdev->pm.dpm.sd = sd; rdev->pm.dpm.hd = hd; - streams_changed = true; + /* disable this for now */ + /*streams_changed = true;*/ } } diff --git a/drivers/gpu/drm/radeon/radeon_vce.c b/drivers/gpu/drm/radeon/radeon_vce.c index ced53dd03e7..aa21c31a846 100644 --- a/drivers/gpu/drm/radeon/radeon_vce.c +++ b/drivers/gpu/drm/radeon/radeon_vce.c @@ -66,6 +66,8 @@ int radeon_vce_init(struct radeon_device *rdev) case CHIP_BONAIRE: case CHIP_KAVERI: case CHIP_KABINI: + case CHIP_HAWAII: + case CHIP_MULLINS: fw_name = FIRMWARE_BONAIRE; break; @@ -442,13 +444,16 @@ int radeon_vce_get_destroy_msg(struct radeon_device *rdev, int ring, * @p: parser context * @lo: address of lower dword * @hi: address of higher dword + * @size: size of checker for relocation buffer * * Patch relocation inside command stream with real buffer address */ -int radeon_vce_cs_reloc(struct radeon_cs_parser *p, int lo, int hi) +int radeon_vce_cs_reloc(struct radeon_cs_parser *p, int lo, int hi, + unsigned size) { struct radeon_cs_chunk *relocs_chunk; - uint64_t offset; + struct radeon_cs_reloc *reloc; + uint64_t start, end, offset; unsigned idx; relocs_chunk = &p->chunks[p->chunk_relocs_idx]; @@ -461,15 +466,60 @@ int radeon_vce_cs_reloc(struct radeon_cs_parser *p, int lo, int hi) return -EINVAL; } - offset += p->relocs_ptr[(idx / 4)]->gpu_offset; + reloc = p->relocs_ptr[(idx / 4)]; + start = reloc->gpu_offset; + end = start + radeon_bo_size(reloc->robj); + start += offset; - p->ib.ptr[lo] = offset & 0xFFFFFFFF; - p->ib.ptr[hi] = offset >> 32; + p->ib.ptr[lo] = start & 0xFFFFFFFF; + p->ib.ptr[hi] = start >> 32; + + if (end <= start) { + DRM_ERROR("invalid reloc offset %llX!\n", offset); + return -EINVAL; + } + if ((end - start) < size) { + DRM_ERROR("buffer to small (%d / %d)!\n", + (unsigned)(end - start), size); + return -EINVAL; + } return 0; } /** + * radeon_vce_validate_handle - validate stream handle + * + * @p: parser context + * @handle: handle to validate + * + * Validates the handle and return the found session index or -EINVAL + * we we don't have another free session index. + */ +int radeon_vce_validate_handle(struct radeon_cs_parser *p, uint32_t handle) +{ + unsigned i; + + /* validate the handle */ + for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) { + if (atomic_read(&p->rdev->vce.handles[i]) == handle) + return i; + } + + /* handle not found try to alloc a new one */ + for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) { + if (!atomic_cmpxchg(&p->rdev->vce.handles[i], 0, handle)) { + p->rdev->vce.filp[i] = p->filp; + p->rdev->vce.img_size[i] = 0; + return i; + } + } + + DRM_ERROR("No more free VCE handles!\n"); + return -EINVAL; +} + +/** * radeon_vce_cs_parse - parse and validate the command stream * * @p: parser context @@ -477,8 +527,10 @@ int radeon_vce_cs_reloc(struct radeon_cs_parser *p, int lo, int hi) */ int radeon_vce_cs_parse(struct radeon_cs_parser *p) { - uint32_t handle = 0; - bool destroy = false; + int session_idx = -1; + bool destroyed = false; + uint32_t tmp, handle = 0; + uint32_t *size = &tmp; int i, r; while (p->idx < p->chunks[p->chunk_ib_idx].length_dw) { @@ -490,13 +542,29 @@ int radeon_vce_cs_parse(struct radeon_cs_parser *p) return -EINVAL; } + if (destroyed) { + DRM_ERROR("No other command allowed after destroy!\n"); + return -EINVAL; + } + switch (cmd) { case 0x00000001: // session handle = radeon_get_ib_value(p, p->idx + 2); + session_idx = radeon_vce_validate_handle(p, handle); + if (session_idx < 0) + return session_idx; + size = &p->rdev->vce.img_size[session_idx]; break; case 0x00000002: // task info + break; + case 0x01000001: // create + *size = radeon_get_ib_value(p, p->idx + 8) * + radeon_get_ib_value(p, p->idx + 10) * + 8 * 3 / 2; + break; + case 0x04000001: // config extension case 0x04000002: // pic control case 0x04000005: // rate control @@ -505,23 +573,39 @@ int radeon_vce_cs_parse(struct radeon_cs_parser *p) break; case 0x03000001: // encode - r = radeon_vce_cs_reloc(p, p->idx + 10, p->idx + 9); + r = radeon_vce_cs_reloc(p, p->idx + 10, p->idx + 9, + *size); if (r) return r; - r = radeon_vce_cs_reloc(p, p->idx + 12, p->idx + 11); + r = radeon_vce_cs_reloc(p, p->idx + 12, p->idx + 11, + *size / 3); if (r) return r; break; case 0x02000001: // destroy - destroy = true; + destroyed = true; break; case 0x05000001: // context buffer + r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2, + *size * 2); + if (r) + return r; + break; + case 0x05000004: // video bitstream buffer + tmp = radeon_get_ib_value(p, p->idx + 4); + r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2, + tmp); + if (r) + return r; + break; + case 0x05000005: // feedback buffer - r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2); + r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2, + 4096); if (r) return r; break; @@ -531,33 +615,21 @@ int radeon_vce_cs_parse(struct radeon_cs_parser *p) return -EINVAL; } + if (session_idx == -1) { + DRM_ERROR("no session command at start of IB\n"); + return -EINVAL; + } + p->idx += len / 4; } - if (destroy) { + if (destroyed) { /* IB contains a destroy msg, free the handle */ for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) atomic_cmpxchg(&p->rdev->vce.handles[i], handle, 0); - - return 0; - } - - /* create or encode, validate the handle */ - for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) { - if (atomic_read(&p->rdev->vce.handles[i]) == handle) - return 0; } - /* handle not found try to alloc a new one */ - for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) { - if (!atomic_cmpxchg(&p->rdev->vce.handles[i], 0, handle)) { - p->rdev->vce.filp[i] = p->filp; - return 0; - } - } - - DRM_ERROR("No more free VCE handles!\n"); - return -EINVAL; + return 0; } /** diff --git a/drivers/gpu/drm/radeon/radeon_vm.c b/drivers/gpu/drm/radeon/radeon_vm.c index a128a4fd64b..725d3669014 100644 --- a/drivers/gpu/drm/radeon/radeon_vm.c +++ b/drivers/gpu/drm/radeon/radeon_vm.c @@ -59,7 +59,7 @@ */ static unsigned radeon_vm_num_pdes(struct radeon_device *rdev) { - return rdev->vm_manager.max_pfn >> RADEON_VM_BLOCK_SIZE; + return rdev->vm_manager.max_pfn >> radeon_vm_block_size; } /** @@ -130,18 +130,18 @@ struct radeon_cs_reloc *radeon_vm_get_bos(struct radeon_device *rdev, struct list_head *head) { struct radeon_cs_reloc *list; - unsigned i, idx, size; + unsigned i, idx; - size = (radeon_vm_num_pdes(rdev) + 1) * sizeof(struct radeon_cs_reloc); - list = kmalloc(size, GFP_KERNEL); + list = kmalloc_array(vm->max_pde_used + 2, + sizeof(struct radeon_cs_reloc), GFP_KERNEL); if (!list) return NULL; /* add the vm page table to the list */ list[0].gobj = NULL; list[0].robj = vm->page_directory; - list[0].domain = RADEON_GEM_DOMAIN_VRAM; - list[0].alt_domain = RADEON_GEM_DOMAIN_VRAM; + list[0].prefered_domains = RADEON_GEM_DOMAIN_VRAM; + list[0].allowed_domains = RADEON_GEM_DOMAIN_VRAM; list[0].tv.bo = &vm->page_directory->tbo; list[0].tiling_flags = 0; list[0].handle = 0; @@ -153,8 +153,8 @@ struct radeon_cs_reloc *radeon_vm_get_bos(struct radeon_device *rdev, list[idx].gobj = NULL; list[idx].robj = vm->page_tables[i].bo; - list[idx].domain = RADEON_GEM_DOMAIN_VRAM; - list[idx].alt_domain = RADEON_GEM_DOMAIN_VRAM; + list[idx].prefered_domains = RADEON_GEM_DOMAIN_VRAM; + list[idx].allowed_domains = RADEON_GEM_DOMAIN_VRAM; list[idx].tv.bo = &list[idx].robj->tbo; list[idx].tiling_flags = 0; list[idx].handle = 0; @@ -332,6 +332,7 @@ struct radeon_bo_va *radeon_vm_bo_add(struct radeon_device *rdev, bo_va->ref_count = 1; INIT_LIST_HEAD(&bo_va->bo_list); INIT_LIST_HEAD(&bo_va->vm_list); + INIT_LIST_HEAD(&bo_va->vm_status); mutex_lock(&vm->mutex); list_add(&bo_va->vm_list, &vm->va); @@ -468,14 +469,29 @@ int radeon_vm_bo_set_addr(struct radeon_device *rdev, head = &tmp->vm_list; } + if (bo_va->soffset) { + /* add a clone of the bo_va to clear the old address */ + tmp = kzalloc(sizeof(struct radeon_bo_va), GFP_KERNEL); + if (!tmp) { + mutex_unlock(&vm->mutex); + return -ENOMEM; + } + tmp->soffset = bo_va->soffset; + tmp->eoffset = bo_va->eoffset; + tmp->vm = vm; + list_add(&tmp->vm_status, &vm->freed); + } + bo_va->soffset = soffset; bo_va->eoffset = eoffset; bo_va->flags = flags; bo_va->valid = false; list_move(&bo_va->vm_list, head); - soffset = (soffset / RADEON_GPU_PAGE_SIZE) >> RADEON_VM_BLOCK_SIZE; - eoffset = (eoffset / RADEON_GPU_PAGE_SIZE) >> RADEON_VM_BLOCK_SIZE; + soffset = (soffset / RADEON_GPU_PAGE_SIZE) >> radeon_vm_block_size; + eoffset = (eoffset / RADEON_GPU_PAGE_SIZE) >> radeon_vm_block_size; + + BUG_ON(eoffset >= radeon_vm_num_pdes(rdev)); if (eoffset > vm->max_pde_used) vm->max_pde_used = eoffset; @@ -493,7 +509,7 @@ int radeon_vm_bo_set_addr(struct radeon_device *rdev, mutex_unlock(&vm->mutex); r = radeon_bo_create(rdev, RADEON_VM_PTE_COUNT * 8, - RADEON_GPU_PAGE_SIZE, false, + RADEON_GPU_PAGE_SIZE, true, RADEON_GEM_DOMAIN_VRAM, NULL, &pt); if (r) return r; @@ -583,9 +599,9 @@ static uint32_t radeon_vm_page_flags(uint32_t flags) int radeon_vm_update_page_directory(struct radeon_device *rdev, struct radeon_vm *vm) { - static const uint32_t incr = RADEON_VM_PTE_COUNT * 8; - - uint64_t pd_addr = radeon_bo_gpu_offset(vm->page_directory); + struct radeon_bo *pd = vm->page_directory; + uint64_t pd_addr = radeon_bo_gpu_offset(pd); + uint32_t incr = RADEON_VM_PTE_COUNT * 8; uint64_t last_pde = ~0, last_pt = ~0; unsigned count = 0, pt_idx, ndw; struct radeon_ib ib; @@ -595,7 +611,7 @@ int radeon_vm_update_page_directory(struct radeon_device *rdev, ndw = 64; /* assume the worst case */ - ndw += vm->max_pde_used * 12; + ndw += vm->max_pde_used * 16; /* update too big for an IB */ if (ndw > 0xfffff) @@ -642,6 +658,7 @@ int radeon_vm_update_page_directory(struct radeon_device *rdev, incr, R600_PTE_VALID); if (ib.length_dw != 0) { + radeon_semaphore_sync_to(ib.semaphore, pd->tbo.sync_obj); radeon_semaphore_sync_to(ib.semaphore, vm->last_id_use); r = radeon_ib_schedule(rdev, &ib, NULL); if (r) { @@ -755,8 +772,7 @@ static void radeon_vm_update_ptes(struct radeon_device *rdev, uint64_t start, uint64_t end, uint64_t dst, uint32_t flags) { - static const uint64_t mask = RADEON_VM_PTE_COUNT - 1; - + uint64_t mask = RADEON_VM_PTE_COUNT - 1; uint64_t last_pte = ~0, last_dst = ~0; unsigned count = 0; uint64_t addr; @@ -766,16 +782,19 @@ static void radeon_vm_update_ptes(struct radeon_device *rdev, /* walk over the address space and update the page tables */ for (addr = start; addr < end; ) { - uint64_t pt_idx = addr >> RADEON_VM_BLOCK_SIZE; + uint64_t pt_idx = addr >> radeon_vm_block_size; + struct radeon_bo *pt = vm->page_tables[pt_idx].bo; unsigned nptes; uint64_t pte; + radeon_semaphore_sync_to(ib->semaphore, pt->tbo.sync_obj); + if ((addr & ~mask) == (end & ~mask)) nptes = end - addr; else nptes = RADEON_VM_PTE_COUNT - (addr & mask); - pte = radeon_bo_gpu_offset(vm->page_tables[pt_idx].bo); + pte = radeon_bo_gpu_offset(pt); pte += (addr & mask) * 8; if ((last_pte + 8 * count) != pte) { @@ -818,25 +837,19 @@ static void radeon_vm_update_ptes(struct radeon_device *rdev, * Object have to be reserved and mutex must be locked! */ int radeon_vm_bo_update(struct radeon_device *rdev, - struct radeon_vm *vm, - struct radeon_bo *bo, + struct radeon_bo_va *bo_va, struct ttm_mem_reg *mem) { + struct radeon_vm *vm = bo_va->vm; struct radeon_ib ib; - struct radeon_bo_va *bo_va; unsigned nptes, ndw; uint64_t addr; int r; - bo_va = radeon_vm_bo_find(vm, bo); - if (bo_va == NULL) { - dev_err(rdev->dev, "bo %p not in vm %p\n", bo, vm); - return -EINVAL; - } if (!bo_va->soffset) { dev_err(rdev->dev, "bo %p don't has a mapping in vm %p\n", - bo, vm); + bo_va->bo, vm); return -EINVAL; } @@ -863,18 +876,18 @@ int radeon_vm_bo_update(struct radeon_device *rdev, trace_radeon_vm_bo_update(bo_va); - nptes = radeon_bo_ngpu_pages(bo); + nptes = (bo_va->eoffset - bo_va->soffset) / RADEON_GPU_PAGE_SIZE; /* padding, etc. */ ndw = 64; - if (RADEON_VM_BLOCK_SIZE > 11) + if (radeon_vm_block_size > 11) /* reserve space for one header for every 2k dwords */ ndw += (nptes >> 11) * 4; else /* reserve space for one header for every (1 << BLOCK_SIZE) entries */ - ndw += (nptes >> RADEON_VM_BLOCK_SIZE) * 4; + ndw += (nptes >> radeon_vm_block_size) * 4; /* reserve space for pte addresses */ ndw += nptes * 2; @@ -906,33 +919,61 @@ int radeon_vm_bo_update(struct radeon_device *rdev, } /** + * radeon_vm_clear_freed - clear freed BOs in the PT + * + * @rdev: radeon_device pointer + * @vm: requested vm + * + * Make sure all freed BOs are cleared in the PT. + * Returns 0 for success. + * + * PTs have to be reserved and mutex must be locked! + */ +int radeon_vm_clear_freed(struct radeon_device *rdev, + struct radeon_vm *vm) +{ + struct radeon_bo_va *bo_va, *tmp; + int r; + + list_for_each_entry_safe(bo_va, tmp, &vm->freed, vm_status) { + list_del(&bo_va->vm_status); + r = radeon_vm_bo_update(rdev, bo_va, NULL); + kfree(bo_va); + if (r) + return r; + } + return 0; + +} + +/** * radeon_vm_bo_rmv - remove a bo to a specific vm * * @rdev: radeon_device pointer * @bo_va: requested bo_va * * Remove @bo_va->bo from the requested vm (cayman+). - * Remove @bo_va->bo from the list of bos associated with the bo_va->vm and - * remove the ptes for @bo_va in the page table. - * Returns 0 for success. * * Object have to be reserved! */ -int radeon_vm_bo_rmv(struct radeon_device *rdev, - struct radeon_bo_va *bo_va) +void radeon_vm_bo_rmv(struct radeon_device *rdev, + struct radeon_bo_va *bo_va) { - int r = 0; + struct radeon_vm *vm = bo_va->vm; - mutex_lock(&bo_va->vm->mutex); - if (bo_va->soffset) - r = radeon_vm_bo_update(rdev, bo_va->vm, bo_va->bo, NULL); + list_del(&bo_va->bo_list); + mutex_lock(&vm->mutex); list_del(&bo_va->vm_list); - mutex_unlock(&bo_va->vm->mutex); - list_del(&bo_va->bo_list); - kfree(bo_va); - return r; + if (bo_va->soffset) { + bo_va->bo = NULL; + list_add(&bo_va->vm_status, &vm->freed); + } else { + kfree(bo_va); + } + + mutex_unlock(&vm->mutex); } /** @@ -970,11 +1011,13 @@ int radeon_vm_init(struct radeon_device *rdev, struct radeon_vm *vm) int r; vm->id = 0; + vm->ib_bo_va = NULL; vm->fence = NULL; vm->last_flush = NULL; vm->last_id_use = NULL; mutex_init(&vm->mutex); INIT_LIST_HEAD(&vm->va); + INIT_LIST_HEAD(&vm->freed); pd_size = radeon_vm_directory_size(rdev); pd_entries = radeon_vm_num_pdes(rdev); @@ -987,7 +1030,7 @@ int radeon_vm_init(struct radeon_device *rdev, struct radeon_vm *vm) return -ENOMEM; } - r = radeon_bo_create(rdev, pd_size, align, false, + r = radeon_bo_create(rdev, pd_size, align, true, RADEON_GEM_DOMAIN_VRAM, NULL, &vm->page_directory); if (r) @@ -1029,7 +1072,8 @@ void radeon_vm_fini(struct radeon_device *rdev, struct radeon_vm *vm) kfree(bo_va); } } - + list_for_each_entry_safe(bo_va, tmp, &vm->freed, vm_status) + kfree(bo_va); for (i = 0; i < radeon_vm_num_pdes(rdev); i++) radeon_bo_unref(&vm->page_tables[i].bo); diff --git a/drivers/gpu/drm/radeon/rs400.c b/drivers/gpu/drm/radeon/rs400.c index 130d5cc50d4..a0f96decece 100644 --- a/drivers/gpu/drm/radeon/rs400.c +++ b/drivers/gpu/drm/radeon/rs400.c @@ -212,21 +212,16 @@ void rs400_gart_fini(struct radeon_device *rdev) #define RS400_PTE_WRITEABLE (1 << 2) #define RS400_PTE_READABLE (1 << 3) -int rs400_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr) +void rs400_gart_set_page(struct radeon_device *rdev, unsigned i, uint64_t addr) { uint32_t entry; u32 *gtt = rdev->gart.ptr; - if (i < 0 || i > rdev->gart.num_gpu_pages) { - return -EINVAL; - } - entry = (lower_32_bits(addr) & PAGE_MASK) | ((upper_32_bits(addr) & 0xff) << 4) | RS400_PTE_WRITEABLE | RS400_PTE_READABLE; entry = cpu_to_le32(entry); gtt[i] = entry; - return 0; } int rs400_mc_wait_for_idle(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c index 0a8be63926d..d1a35cb1c91 100644 --- a/drivers/gpu/drm/radeon/rs600.c +++ b/drivers/gpu/drm/radeon/rs600.c @@ -626,17 +626,16 @@ static void rs600_gart_fini(struct radeon_device *rdev) radeon_gart_table_vram_free(rdev); } -int rs600_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr) +void rs600_gart_set_page(struct radeon_device *rdev, unsigned i, uint64_t addr) { void __iomem *ptr = (void *)rdev->gart.ptr; - if (i < 0 || i > rdev->gart.num_gpu_pages) { - return -EINVAL; - } addr = addr & 0xFFFFFFFFFFFFF000ULL; - addr |= R600_PTE_GART; + if (addr == rdev->dummy_page.addr) + addr |= R600_PTE_SYSTEM | R600_PTE_SNOOPED; + else + addr |= R600_PTE_GART; writeq(addr, ptr + (i * 8)); - return 0; } int rs600_irq_set(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/rv515.c b/drivers/gpu/drm/radeon/rv515.c index 237dd29d9f1..3e21e869015 100644 --- a/drivers/gpu/drm/radeon/rv515.c +++ b/drivers/gpu/drm/radeon/rv515.c @@ -406,8 +406,9 @@ void rv515_mc_resume(struct radeon_device *rdev, struct rv515_mc_save *save) for (i = 0; i < rdev->num_crtc; i++) { if (save->crtc_enabled[i]) { tmp = RREG32(AVIVO_D1MODE_MASTER_UPDATE_MODE + crtc_offsets[i]); - if ((tmp & 0x3) != 0) { - tmp &= ~0x3; + if ((tmp & 0x7) != 3) { + tmp &= ~0x7; + tmp |= 0x3; WREG32(AVIVO_D1MODE_MASTER_UPDATE_MODE + crtc_offsets[i], tmp); } tmp = RREG32(AVIVO_D1GRPH_UPDATE + crtc_offsets[i]); diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c index 97b776666b7..da8703d8d45 100644 --- a/drivers/gpu/drm/radeon/rv770.c +++ b/drivers/gpu/drm/radeon/rv770.c @@ -1327,6 +1327,9 @@ static void rv770_gpu_init(struct radeon_device *rdev) if (tmp < rdev->config.rv770.max_simds) { rdev->config.rv770.max_simds = tmp; } + tmp = rdev->config.rv770.max_simds - + r600_count_pipe_bits((cc_gc_shader_pipe_config >> 16) & R7XX_MAX_SIMDS_MASK); + rdev->config.rv770.active_simds = tmp; switch (rdev->config.rv770.max_tile_pipes) { case 1: diff --git a/drivers/gpu/drm/radeon/rv770_dma.c b/drivers/gpu/drm/radeon/rv770_dma.c index aca8cbe8a33..bbf2e076ee4 100644 --- a/drivers/gpu/drm/radeon/rv770_dma.c +++ b/drivers/gpu/drm/radeon/rv770_dma.c @@ -86,6 +86,7 @@ int rv770_copy_dma(struct radeon_device *rdev, r = radeon_fence_emit(rdev, fence, ring->idx); if (r) { radeon_ring_unlock_undo(rdev, ring); + radeon_semaphore_free(rdev, &sem, NULL); return r; } diff --git a/drivers/gpu/drm/radeon/rv770_dpm.c b/drivers/gpu/drm/radeon/rv770_dpm.c index da041a43d82..3c76e1dcdf0 100644 --- a/drivers/gpu/drm/radeon/rv770_dpm.c +++ b/drivers/gpu/drm/radeon/rv770_dpm.c @@ -2329,12 +2329,6 @@ void rv770_get_engine_memory_ss(struct radeon_device *rdev) pi->mclk_ss = radeon_atombios_get_asic_ss_info(rdev, &ss, ASIC_INTERNAL_MEMORY_SS, 0); - /* disable ss, causes hangs on some cayman boards */ - if (rdev->family == CHIP_CAYMAN) { - pi->sclk_ss = false; - pi->mclk_ss = false; - } - if (pi->sclk_ss || pi->mclk_ss) pi->dynamic_ss = true; else diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index 5c1c0c795e9..9e854fd016d 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -71,6 +71,7 @@ MODULE_FIRMWARE("radeon/HAINAN_mc2.bin"); MODULE_FIRMWARE("radeon/HAINAN_rlc.bin"); MODULE_FIRMWARE("radeon/HAINAN_smc.bin"); +static u32 si_get_cu_active_bitmap(struct radeon_device *rdev, u32 se, u32 sh); static void si_pcie_gen3_enable(struct radeon_device *rdev); static void si_program_aspm(struct radeon_device *rdev); extern void sumo_rlc_fini(struct radeon_device *rdev); @@ -2900,7 +2901,7 @@ static void si_gpu_init(struct radeon_device *rdev) u32 sx_debug_1; u32 hdp_host_path_cntl; u32 tmp; - int i, j; + int i, j, k; switch (rdev->family) { case CHIP_TAHITI: @@ -3098,6 +3099,14 @@ static void si_gpu_init(struct radeon_device *rdev) rdev->config.si.max_sh_per_se, rdev->config.si.max_cu_per_sh); + for (i = 0; i < rdev->config.si.max_shader_engines; i++) { + for (j = 0; j < rdev->config.si.max_sh_per_se; j++) { + for (k = 0; k < rdev->config.si.max_cu_per_sh; k++) { + rdev->config.si.active_cus += + hweight32(si_get_cu_active_bitmap(rdev, i, j)); + } + } + } /* set HW defaults for 3D engine */ WREG32(CP_QUEUE_THRESHOLDS, (ROQ_IB1_START(0x16) | @@ -3186,7 +3195,7 @@ void si_fence_ring_emit(struct radeon_device *rdev, /* EVENT_WRITE_EOP - flush caches, send int */ radeon_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4)); radeon_ring_write(ring, EVENT_TYPE(CACHE_FLUSH_AND_INV_TS_EVENT) | EVENT_INDEX(5)); - radeon_ring_write(ring, addr & 0xffffffff); + radeon_ring_write(ring, lower_32_bits(addr)); radeon_ring_write(ring, (upper_32_bits(addr) & 0xff) | DATA_SEL(1) | INT_SEL(2)); radeon_ring_write(ring, fence->seq); radeon_ring_write(ring, 0); @@ -3219,7 +3228,7 @@ void si_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib) radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); radeon_ring_write(ring, (1 << 8)); radeon_ring_write(ring, ring->next_rptr_gpu_addr & 0xfffffffc); - radeon_ring_write(ring, upper_32_bits(ring->next_rptr_gpu_addr) & 0xffffffff); + radeon_ring_write(ring, upper_32_bits(ring->next_rptr_gpu_addr)); radeon_ring_write(ring, next_rptr); } @@ -4095,7 +4104,7 @@ static int si_pcie_gart_enable(struct radeon_device *rdev) (u32)(rdev->dummy_page.addr >> 12)); WREG32(VM_CONTEXT1_CNTL2, 4); WREG32(VM_CONTEXT1_CNTL, ENABLE_CONTEXT | PAGE_TABLE_DEPTH(1) | - PAGE_TABLE_BLOCK_SIZE(RADEON_VM_BLOCK_SIZE - 9) | + PAGE_TABLE_BLOCK_SIZE(radeon_vm_block_size - 9) | RANGE_PROTECTION_FAULT_ENABLE_INTERRUPT | RANGE_PROTECTION_FAULT_ENABLE_DEFAULT | DUMMY_PAGE_PROTECTION_FAULT_ENABLE_INTERRUPT | @@ -5784,7 +5793,6 @@ int si_irq_set(struct radeon_device *rdev) u32 crtc1 = 0, crtc2 = 0, crtc3 = 0, crtc4 = 0, crtc5 = 0, crtc6 = 0; u32 hpd1 = 0, hpd2 = 0, hpd3 = 0, hpd4 = 0, hpd5 = 0, hpd6 = 0; u32 grbm_int_cntl = 0; - u32 grph1 = 0, grph2 = 0, grph3 = 0, grph4 = 0, grph5 = 0, grph6 = 0; u32 dma_cntl, dma_cntl1; u32 thermal_int = 0; @@ -5923,16 +5931,22 @@ int si_irq_set(struct radeon_device *rdev) } if (rdev->num_crtc >= 2) { - WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, grph1); - WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, grph2); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); } if (rdev->num_crtc >= 4) { - WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, grph3); - WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, grph4); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); } if (rdev->num_crtc >= 6) { - WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, grph5); - WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, grph6); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); } if (!ASIC_IS_NODCE(rdev)) { @@ -6089,6 +6103,7 @@ static inline u32 si_get_ih_wptr(struct radeon_device *rdev) tmp = RREG32(IH_RB_CNTL); tmp |= IH_WPTR_OVERFLOW_CLEAR; WREG32(IH_RB_CNTL, tmp); + wptr &= ~RB_OVERFLOW; } return (wptr & rdev->ih.ptr_mask); } @@ -6296,6 +6311,15 @@ restart_ih: break; } break; + case 8: /* D1 page flip */ + case 10: /* D2 page flip */ + case 12: /* D3 page flip */ + case 14: /* D4 page flip */ + case 16: /* D5 page flip */ + case 18: /* D6 page flip */ + DRM_DEBUG("IH: D%d flip\n", ((src_id - 8) >> 1) + 1); + radeon_crtc_handle_flip(rdev, (src_id - 8) >> 1); + break; case 42: /* HPD hotplug */ switch (src_data) { case 0: @@ -6353,14 +6377,16 @@ restart_ih: case 147: addr = RREG32(VM_CONTEXT1_PROTECTION_FAULT_ADDR); status = RREG32(VM_CONTEXT1_PROTECTION_FAULT_STATUS); + /* reset addr and status */ + WREG32_P(VM_CONTEXT1_CNTL2, 1, ~1); + if (addr == 0x0 && status == 0x0) + break; dev_err(rdev->dev, "GPU fault detected: %d 0x%08x\n", src_id, src_data); dev_err(rdev->dev, " VM_CONTEXT1_PROTECTION_FAULT_ADDR 0x%08X\n", addr); dev_err(rdev->dev, " VM_CONTEXT1_PROTECTION_FAULT_STATUS 0x%08X\n", status); si_vm_decode_fault(rdev, status, addr); - /* reset addr and status */ - WREG32_P(VM_CONTEXT1_CNTL2, 1, ~1); break; case 176: /* RINGID0 CP_INT */ radeon_fence_process(rdev, RADEON_RING_TYPE_GFX_INDEX); diff --git a/drivers/gpu/drm/radeon/si_dma.c b/drivers/gpu/drm/radeon/si_dma.c index 952166930fb..e24c94b6d14 100644 --- a/drivers/gpu/drm/radeon/si_dma.c +++ b/drivers/gpu/drm/radeon/si_dma.c @@ -88,8 +88,8 @@ void si_dma_vm_set_page(struct radeon_device *rdev, ib->ptr[ib->length_dw++] = DMA_PACKET(DMA_PACKET_COPY, 1, 0, 0, bytes); - ib->ptr[ib->length_dw++] = pe & 0xffffffff; - ib->ptr[ib->length_dw++] = src & 0xffffffff; + ib->ptr[ib->length_dw++] = lower_32_bits(pe); + ib->ptr[ib->length_dw++] = lower_32_bits(src); ib->ptr[ib->length_dw++] = upper_32_bits(pe) & 0xff; ib->ptr[ib->length_dw++] = upper_32_bits(src) & 0xff; @@ -220,8 +220,8 @@ int si_copy_dma(struct radeon_device *rdev, cur_size_in_bytes = 0xFFFFF; size_in_bytes -= cur_size_in_bytes; radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_COPY, 1, 0, 0, cur_size_in_bytes)); - radeon_ring_write(ring, dst_offset & 0xffffffff); - radeon_ring_write(ring, src_offset & 0xffffffff); + radeon_ring_write(ring, lower_32_bits(dst_offset)); + radeon_ring_write(ring, lower_32_bits(src_offset)); radeon_ring_write(ring, upper_32_bits(dst_offset) & 0xff); radeon_ring_write(ring, upper_32_bits(src_offset) & 0xff); src_offset += cur_size_in_bytes; @@ -231,6 +231,7 @@ int si_copy_dma(struct radeon_device *rdev, r = radeon_fence_emit(rdev, fence, ring->idx); if (r) { radeon_ring_unlock_undo(rdev, ring); + radeon_semaphore_free(rdev, &sem, NULL); return r; } diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c index 9a3567bedaa..58918868f89 100644 --- a/drivers/gpu/drm/radeon/si_dpm.c +++ b/drivers/gpu/drm/radeon/si_dpm.c @@ -1948,6 +1948,10 @@ static void si_initialize_powertune_defaults(struct radeon_device *rdev) si_pi->cac_weights = cac_weights_cape_verde_pro; si_pi->dte_data = dte_data_cape_verde; break; + case 0x682C: + si_pi->cac_weights = cac_weights_cape_verde_pro; + si_pi->dte_data = dte_data_sun_xt; + break; case 0x6825: case 0x6827: si_pi->cac_weights = cac_weights_heathrow; @@ -1971,10 +1975,9 @@ static void si_initialize_powertune_defaults(struct radeon_device *rdev) si_pi->dte_data = dte_data_venus_xt; break; case 0x6823: - si_pi->cac_weights = cac_weights_chelsea_pro; - si_pi->dte_data = dte_data_venus_pro; - break; case 0x682B: + case 0x6822: + case 0x682A: si_pi->cac_weights = cac_weights_chelsea_pro; si_pi->dte_data = dte_data_venus_pro; break; @@ -1988,6 +1991,7 @@ static void si_initialize_powertune_defaults(struct radeon_device *rdev) case 0x6601: case 0x6621: case 0x6603: + case 0x6605: si_pi->cac_weights = cac_weights_mars_pro; si_pi->lcac_config = lcac_mars_pro; si_pi->cac_override = cac_override_oland; @@ -1998,6 +2002,7 @@ static void si_initialize_powertune_defaults(struct radeon_device *rdev) case 0x6600: case 0x6606: case 0x6620: + case 0x6604: si_pi->cac_weights = cac_weights_mars_xt; si_pi->lcac_config = lcac_mars_pro; si_pi->cac_override = cac_override_oland; @@ -2006,6 +2011,8 @@ static void si_initialize_powertune_defaults(struct radeon_device *rdev) update_dte_from_pl2 = true; break; case 0x6611: + case 0x6613: + case 0x6608: si_pi->cac_weights = cac_weights_oland_pro; si_pi->lcac_config = lcac_mars_pro; si_pi->cac_override = cac_override_oland; diff --git a/drivers/gpu/drm/radeon/sid.h b/drivers/gpu/drm/radeon/sid.h index da8f8674a55..fd414d34d88 100644 --- a/drivers/gpu/drm/radeon/sid.h +++ b/drivers/gpu/drm/radeon/sid.h @@ -107,8 +107,8 @@ #define SPLL_CHG_STATUS (1 << 1) #define SPLL_CNTL_MODE 0x618 #define SPLL_SW_DIR_CONTROL (1 << 0) -# define SPLL_REFCLK_SEL(x) ((x) << 8) -# define SPLL_REFCLK_SEL_MASK 0xFF00 +# define SPLL_REFCLK_SEL(x) ((x) << 26) +# define SPLL_REFCLK_SEL_MASK (3 << 26) #define CG_SPLL_SPREAD_SPECTRUM 0x620 #define SSEN (1 << 0) diff --git a/drivers/gpu/drm/radeon/trinity_dpm.c b/drivers/gpu/drm/radeon/trinity_dpm.c index 2a2822c0332..32e50be9c4a 100644 --- a/drivers/gpu/drm/radeon/trinity_dpm.c +++ b/drivers/gpu/drm/radeon/trinity_dpm.c @@ -1874,7 +1874,16 @@ int trinity_dpm_init(struct radeon_device *rdev) for (i = 0; i < SUMO_MAX_HARDWARE_POWERLEVELS; i++) pi->at[i] = TRINITY_AT_DFLT; - pi->enable_bapm = false; + /* There are stability issues reported on with + * bapm enabled when switching between AC and battery + * power. At the same time, some MSI boards hang + * if it's not enabled and dpm is enabled. Just enable + * it for MSI boards right now. + */ + if (rdev->pdev->subsystem_vendor == 0x1462) + pi->enable_bapm = true; + else + pi->enable_bapm = false; pi->enable_nbps_policy = true; pi->enable_sclk_ds = true; pi->enable_gfx_power_gating = true; diff --git a/drivers/gpu/drm/radeon/uvd_v1_0.c b/drivers/gpu/drm/radeon/uvd_v1_0.c index 0a243f0e5d6..be42c812520 100644 --- a/drivers/gpu/drm/radeon/uvd_v1_0.c +++ b/drivers/gpu/drm/radeon/uvd_v1_0.c @@ -83,7 +83,10 @@ int uvd_v1_0_init(struct radeon_device *rdev) int r; /* raise clocks while booting up the VCPU */ - radeon_set_uvd_clocks(rdev, 53300, 40000); + if (rdev->family < CHIP_RV740) + radeon_set_uvd_clocks(rdev, 10000, 10000); + else + radeon_set_uvd_clocks(rdev, 53300, 40000); r = uvd_v1_0_start(rdev); if (r) @@ -407,7 +410,10 @@ int uvd_v1_0_ib_test(struct radeon_device *rdev, struct radeon_ring *ring) struct radeon_fence *fence = NULL; int r; - r = radeon_set_uvd_clocks(rdev, 53300, 40000); + if (rdev->family < CHIP_RV740) + r = radeon_set_uvd_clocks(rdev, 10000, 10000); + else + r = radeon_set_uvd_clocks(rdev, 53300, 40000); if (r) { DRM_ERROR("radeon: failed to raise UVD clocks (%d).\n", r); return r; diff --git a/drivers/gpu/drm/radeon/uvd_v2_2.c b/drivers/gpu/drm/radeon/uvd_v2_2.c index d1771004cb5..8bfdadd5659 100644 --- a/drivers/gpu/drm/radeon/uvd_v2_2.c +++ b/drivers/gpu/drm/radeon/uvd_v2_2.c @@ -45,7 +45,7 @@ void uvd_v2_2_fence_emit(struct radeon_device *rdev, radeon_ring_write(ring, PACKET0(UVD_CONTEXT_ID, 0)); radeon_ring_write(ring, fence->seq); radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_DATA0, 0)); - radeon_ring_write(ring, addr & 0xffffffff); + radeon_ring_write(ring, lower_32_bits(addr)); radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_DATA1, 0)); radeon_ring_write(ring, upper_32_bits(addr) & 0xff); radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_CMD, 0)); diff --git a/drivers/gpu/drm/savage/savage_bci.c b/drivers/gpu/drm/savage/savage_bci.c index d2b2df9e26f..c97cdc9ab23 100644 --- a/drivers/gpu/drm/savage/savage_bci.c +++ b/drivers/gpu/drm/savage/savage_bci.c @@ -1079,4 +1079,4 @@ const struct drm_ioctl_desc savage_ioctls[] = { DRM_IOCTL_DEF_DRV(SAVAGE_BCI_EVENT_WAIT, savage_bci_event_wait, DRM_AUTH), }; -int savage_max_ioctl = DRM_ARRAY_SIZE(savage_ioctls); +int savage_max_ioctl = ARRAY_SIZE(savage_ioctls); diff --git a/drivers/gpu/drm/sis/sis_mm.c b/drivers/gpu/drm/sis/sis_mm.c index 0573be0d293..77f288e4a0a 100644 --- a/drivers/gpu/drm/sis/sis_mm.c +++ b/drivers/gpu/drm/sis/sis_mm.c @@ -359,4 +359,4 @@ const struct drm_ioctl_desc sis_ioctls[] = { DRM_IOCTL_DEF_DRV(SIS_FB_INIT, sis_fb_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY), }; -int sis_max_ioctl = DRM_ARRAY_SIZE(sis_ioctls); +int sis_max_ioctl = ARRAY_SIZE(sis_ioctls); diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile index d43f21bb459..2c66a8db9da 100644 --- a/drivers/gpu/drm/tegra/Makefile +++ b/drivers/gpu/drm/tegra/Makefile @@ -1,7 +1,6 @@ ccflags-$(CONFIG_DRM_TEGRA_DEBUG) += -DDEBUG tegra-drm-y := \ - bus.o \ drm.o \ gem.o \ fb.o \ diff --git a/drivers/gpu/drm/tegra/bus.c b/drivers/gpu/drm/tegra/bus.c deleted file mode 100644 index b3a66d65cb5..00000000000 --- a/drivers/gpu/drm/tegra/bus.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2013 NVIDIA Corporation - * - * 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 Foundation. - */ - -#include "drm.h" - -static int drm_host1x_set_busid(struct drm_device *dev, - struct drm_master *master) -{ - const char *device = dev_name(dev->dev); - const char *bus = dev->dev->bus->name; - - master->unique_len = strlen(bus) + 1 + strlen(device); - master->unique_size = master->unique_len; - - master->unique = kmalloc(master->unique_len + 1, GFP_KERNEL); - if (!master->unique) - return -ENOMEM; - - snprintf(master->unique, master->unique_len + 1, "%s:%s", bus, device); - - return 0; -} - -static struct drm_bus drm_host1x_bus = { - .set_busid = drm_host1x_set_busid, -}; - -int drm_host1x_init(struct drm_driver *driver, struct host1x_device *device) -{ - struct drm_device *drm; - int ret; - - driver->bus = &drm_host1x_bus; - - drm = drm_dev_alloc(driver, &device->dev); - if (!drm) - return -ENOMEM; - - ret = drm_dev_register(drm, 0); - if (ret) - goto err_free; - - DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", driver->name, - driver->major, driver->minor, driver->patchlevel, - driver->date, drm->primary->index); - - return 0; - -err_free: - drm_dev_unref(drm); - return ret; -} - -void drm_host1x_exit(struct drm_driver *driver, struct host1x_device *device) -{ - struct tegra_drm *tegra = dev_get_drvdata(&device->dev); - - drm_put_dev(tegra->drm); -} diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index edb871d7d39..ef40381f390 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -17,6 +17,7 @@ struct tegra_dc_soc_info { bool supports_interlacing; + bool supports_cursor; }; struct tegra_plane { @@ -29,6 +30,254 @@ static inline struct tegra_plane *to_tegra_plane(struct drm_plane *plane) return container_of(plane, struct tegra_plane, base); } +static unsigned int tegra_dc_format(uint32_t format, uint32_t *swap) +{ + /* assume no swapping of fetched data */ + if (swap) + *swap = BYTE_SWAP_NOSWAP; + + switch (format) { + case DRM_FORMAT_XBGR8888: + return WIN_COLOR_DEPTH_R8G8B8A8; + + case DRM_FORMAT_XRGB8888: + return WIN_COLOR_DEPTH_B8G8R8A8; + + case DRM_FORMAT_RGB565: + return WIN_COLOR_DEPTH_B5G6R5; + + case DRM_FORMAT_UYVY: + return WIN_COLOR_DEPTH_YCbCr422; + + case DRM_FORMAT_YUYV: + if (swap) + *swap = BYTE_SWAP_SWAP2; + + return WIN_COLOR_DEPTH_YCbCr422; + + case DRM_FORMAT_YUV420: + return WIN_COLOR_DEPTH_YCbCr420P; + + case DRM_FORMAT_YUV422: + return WIN_COLOR_DEPTH_YCbCr422P; + + default: + break; + } + + WARN(1, "unsupported pixel format %u, using default\n", format); + return WIN_COLOR_DEPTH_B8G8R8A8; +} + +static bool tegra_dc_format_is_yuv(unsigned int format, bool *planar) +{ + switch (format) { + case WIN_COLOR_DEPTH_YCbCr422: + case WIN_COLOR_DEPTH_YUV422: + if (planar) + *planar = false; + + return true; + + case WIN_COLOR_DEPTH_YCbCr420P: + case WIN_COLOR_DEPTH_YUV420P: + case WIN_COLOR_DEPTH_YCbCr422P: + case WIN_COLOR_DEPTH_YUV422P: + case WIN_COLOR_DEPTH_YCbCr422R: + case WIN_COLOR_DEPTH_YUV422R: + case WIN_COLOR_DEPTH_YCbCr422RA: + case WIN_COLOR_DEPTH_YUV422RA: + if (planar) + *planar = true; + + return true; + } + + return false; +} + +static inline u32 compute_dda_inc(unsigned int in, unsigned int out, bool v, + unsigned int bpp) +{ + fixed20_12 outf = dfixed_init(out); + fixed20_12 inf = dfixed_init(in); + u32 dda_inc; + int max; + + if (v) + max = 15; + else { + switch (bpp) { + case 2: + max = 8; + break; + + default: + WARN_ON_ONCE(1); + /* fallthrough */ + case 4: + max = 4; + break; + } + } + + outf.full = max_t(u32, outf.full - dfixed_const(1), dfixed_const(1)); + inf.full -= dfixed_const(1); + + dda_inc = dfixed_div(inf, outf); + dda_inc = min_t(u32, dda_inc, dfixed_const(max)); + + return dda_inc; +} + +static inline u32 compute_initial_dda(unsigned int in) +{ + fixed20_12 inf = dfixed_init(in); + return dfixed_frac(inf); +} + +static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, + const struct tegra_dc_window *window) +{ + unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp; + unsigned long value; + bool yuv, planar; + + /* + * For YUV planar modes, the number of bytes per pixel takes into + * account only the luma component and therefore is 1. + */ + yuv = tegra_dc_format_is_yuv(window->format, &planar); + if (!yuv) + bpp = window->bits_per_pixel / 8; + else + bpp = planar ? 1 : 2; + + value = WINDOW_A_SELECT << index; + tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER); + + tegra_dc_writel(dc, window->format, DC_WIN_COLOR_DEPTH); + tegra_dc_writel(dc, window->swap, DC_WIN_BYTE_SWAP); + + value = V_POSITION(window->dst.y) | H_POSITION(window->dst.x); + tegra_dc_writel(dc, value, DC_WIN_POSITION); + + value = V_SIZE(window->dst.h) | H_SIZE(window->dst.w); + tegra_dc_writel(dc, value, DC_WIN_SIZE); + + h_offset = window->src.x * bpp; + v_offset = window->src.y; + h_size = window->src.w * bpp; + v_size = window->src.h; + + value = V_PRESCALED_SIZE(v_size) | H_PRESCALED_SIZE(h_size); + tegra_dc_writel(dc, value, DC_WIN_PRESCALED_SIZE); + + /* + * For DDA computations the number of bytes per pixel for YUV planar + * modes needs to take into account all Y, U and V components. + */ + if (yuv && planar) + bpp = 2; + + h_dda = compute_dda_inc(window->src.w, window->dst.w, false, bpp); + v_dda = compute_dda_inc(window->src.h, window->dst.h, true, bpp); + + value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda); + tegra_dc_writel(dc, value, DC_WIN_DDA_INC); + + h_dda = compute_initial_dda(window->src.x); + v_dda = compute_initial_dda(window->src.y); + + tegra_dc_writel(dc, h_dda, DC_WIN_H_INITIAL_DDA); + tegra_dc_writel(dc, v_dda, DC_WIN_V_INITIAL_DDA); + + tegra_dc_writel(dc, 0, DC_WIN_UV_BUF_STRIDE); + tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE); + + tegra_dc_writel(dc, window->base[0], DC_WINBUF_START_ADDR); + + if (yuv && planar) { + tegra_dc_writel(dc, window->base[1], DC_WINBUF_START_ADDR_U); + tegra_dc_writel(dc, window->base[2], DC_WINBUF_START_ADDR_V); + value = window->stride[1] << 16 | window->stride[0]; + tegra_dc_writel(dc, value, DC_WIN_LINE_STRIDE); + } else { + tegra_dc_writel(dc, window->stride[0], DC_WIN_LINE_STRIDE); + } + + if (window->bottom_up) + v_offset += window->src.h - 1; + + tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET); + tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET); + + if (window->tiled) { + value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV | + DC_WIN_BUFFER_ADDR_MODE_TILE; + } else { + value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV | + DC_WIN_BUFFER_ADDR_MODE_LINEAR; + } + + tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE); + + value = WIN_ENABLE; + + if (yuv) { + /* setup default colorspace conversion coefficients */ + tegra_dc_writel(dc, 0x00f0, DC_WIN_CSC_YOF); + tegra_dc_writel(dc, 0x012a, DC_WIN_CSC_KYRGB); + tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KUR); + tegra_dc_writel(dc, 0x0198, DC_WIN_CSC_KVR); + tegra_dc_writel(dc, 0x039b, DC_WIN_CSC_KUG); + tegra_dc_writel(dc, 0x032f, DC_WIN_CSC_KVG); + tegra_dc_writel(dc, 0x0204, DC_WIN_CSC_KUB); + tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KVB); + + value |= CSC_ENABLE; + } else if (window->bits_per_pixel < 24) { + value |= COLOR_EXPAND; + } + + if (window->bottom_up) + value |= V_DIRECTION; + + tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); + + /* + * Disable blending and assume Window A is the bottom-most window, + * Window C is the top-most window and Window B is in the middle. + */ + tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_NOKEY); + tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_1WIN); + + switch (index) { + case 0: + tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_X); + tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y); + tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY); + break; + + case 1: + tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X); + tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y); + tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY); + break; + + case 2: + tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X); + tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_Y); + tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_3WIN_XY); + break; + } + + tegra_dc_writel(dc, WIN_A_UPDATE << index, DC_CMD_STATE_CONTROL); + tegra_dc_writel(dc, WIN_A_ACT_REQ << index, DC_CMD_STATE_CONTROL); + + return 0; +} + static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, int crtc_x, int crtc_y, unsigned int crtc_w, @@ -49,7 +298,7 @@ static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, window.dst.y = crtc_y; window.dst.w = crtc_w; window.dst.h = crtc_h; - window.format = tegra_dc_format(fb->pixel_format); + window.format = tegra_dc_format(fb->pixel_format, &window.swap); window.bits_per_pixel = fb->bits_per_pixel; window.bottom_up = tegra_fb_is_bottom_up(fb); window.tiled = tegra_fb_is_tiled(fb); @@ -117,6 +366,7 @@ static const uint32_t plane_formats[] = { DRM_FORMAT_XRGB8888, DRM_FORMAT_RGB565, DRM_FORMAT_UYVY, + DRM_FORMAT_YUYV, DRM_FORMAT_YUV420, DRM_FORMAT_YUV422, }; @@ -150,9 +400,9 @@ static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc) static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y, struct drm_framebuffer *fb) { - unsigned int format = tegra_dc_format(fb->pixel_format); struct tegra_bo *bo = tegra_fb_get_plane(fb, 0); unsigned int h_offset = 0, v_offset = 0; + unsigned int format, swap; unsigned long value; tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER); @@ -162,7 +412,10 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y, tegra_dc_writel(dc, bo->paddr + value, DC_WINBUF_START_ADDR); tegra_dc_writel(dc, fb->pitches[0], DC_WIN_LINE_STRIDE); + + format = tegra_dc_format(fb->pixel_format, &swap); tegra_dc_writel(dc, format, DC_WIN_COLOR_DEPTH); + tegra_dc_writel(dc, swap, DC_WIN_BYTE_SWAP); if (tegra_fb_is_tiled(fb)) { value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV | @@ -177,13 +430,13 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y, /* make sure bottom-up buffers are properly displayed */ if (tegra_fb_is_bottom_up(fb)) { value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS); - value |= INVERT_V; + value |= V_DIRECTION; tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); v_offset += fb->height - 1; } else { value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS); - value &= ~INVERT_V; + value &= ~V_DIRECTION; tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); } @@ -225,6 +478,109 @@ void tegra_dc_disable_vblank(struct tegra_dc *dc) spin_unlock_irqrestore(&dc->lock, flags); } +static int tegra_dc_cursor_set2(struct drm_crtc *crtc, struct drm_file *file, + uint32_t handle, uint32_t width, + uint32_t height, int32_t hot_x, int32_t hot_y) +{ + unsigned long value = CURSOR_CLIP_DISPLAY; + struct tegra_dc *dc = to_tegra_dc(crtc); + struct drm_gem_object *gem; + struct tegra_bo *bo = NULL; + + if (!dc->soc->supports_cursor) + return -ENXIO; + + if (width != height) + return -EINVAL; + + switch (width) { + case 32: + value |= CURSOR_SIZE_32x32; + break; + + case 64: + value |= CURSOR_SIZE_64x64; + break; + + case 128: + value |= CURSOR_SIZE_128x128; + + case 256: + value |= CURSOR_SIZE_256x256; + break; + + default: + return -EINVAL; + } + + if (handle) { + gem = drm_gem_object_lookup(crtc->dev, file, handle); + if (!gem) + return -ENOENT; + + bo = to_tegra_bo(gem); + } + + if (bo) { + unsigned long addr = (bo->paddr & 0xfffffc00) >> 10; +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + unsigned long high = (bo->paddr & 0xfffffffc) >> 32; +#endif + + tegra_dc_writel(dc, value | addr, DC_DISP_CURSOR_START_ADDR); + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + tegra_dc_writel(dc, high, DC_DISP_CURSOR_START_ADDR_HI); +#endif + + value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); + value |= CURSOR_ENABLE; + tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); + + value = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL); + value &= ~CURSOR_DST_BLEND_MASK; + value &= ~CURSOR_SRC_BLEND_MASK; + value |= CURSOR_MODE_NORMAL; + value |= CURSOR_DST_BLEND_NEG_K1_TIMES_SRC; + value |= CURSOR_SRC_BLEND_K1_TIMES_SRC; + value |= CURSOR_ALPHA; + tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL); + } else { + value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); + value &= ~CURSOR_ENABLE; + tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); + } + + tegra_dc_writel(dc, CURSOR_ACT_REQ << 8, DC_CMD_STATE_CONTROL); + tegra_dc_writel(dc, CURSOR_ACT_REQ, DC_CMD_STATE_CONTROL); + + tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); + tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); + + return 0; +} + +static int tegra_dc_cursor_move(struct drm_crtc *crtc, int x, int y) +{ + struct tegra_dc *dc = to_tegra_dc(crtc); + unsigned long value; + + if (!dc->soc->supports_cursor) + return -ENXIO; + + value = ((y & 0x3fff) << 16) | (x & 0x3fff); + tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION); + + tegra_dc_writel(dc, CURSOR_ACT_REQ << 8, DC_CMD_STATE_CONTROL); + tegra_dc_writel(dc, CURSOR_ACT_REQ, DC_CMD_STATE_CONTROL); + + /* XXX: only required on generations earlier than Tegra124? */ + tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); + tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); + + return 0; +} + static void tegra_dc_finish_page_flip(struct tegra_dc *dc) { struct drm_device *drm = dc->base.dev; @@ -301,6 +657,8 @@ static void tegra_dc_destroy(struct drm_crtc *crtc) } static const struct drm_crtc_funcs tegra_crtc_funcs = { + .cursor_set2 = tegra_dc_cursor_set2, + .cursor_move = tegra_dc_cursor_move, .page_flip = tegra_dc_page_flip, .set_config = drm_crtc_helper_set_config, .destroy = tegra_dc_destroy, @@ -334,52 +692,11 @@ static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc, return true; } -static inline u32 compute_dda_inc(unsigned int in, unsigned int out, bool v, - unsigned int bpp) -{ - fixed20_12 outf = dfixed_init(out); - fixed20_12 inf = dfixed_init(in); - u32 dda_inc; - int max; - - if (v) - max = 15; - else { - switch (bpp) { - case 2: - max = 8; - break; - - default: - WARN_ON_ONCE(1); - /* fallthrough */ - case 4: - max = 4; - break; - } - } - - outf.full = max_t(u32, outf.full - dfixed_const(1), dfixed_const(1)); - inf.full -= dfixed_const(1); - - dda_inc = dfixed_div(inf, outf); - dda_inc = min_t(u32, dda_inc, dfixed_const(max)); - - return dda_inc; -} - -static inline u32 compute_initial_dda(unsigned int in) -{ - fixed20_12 inf = dfixed_init(in); - return dfixed_frac(inf); -} - static int tegra_dc_set_timings(struct tegra_dc *dc, struct drm_display_mode *mode) { - /* TODO: For HDMI compliance, h & v ref_to_sync should be set to 1 */ - unsigned int h_ref_to_sync = 0; - unsigned int v_ref_to_sync = 0; + unsigned int h_ref_to_sync = 1; + unsigned int v_ref_to_sync = 1; unsigned long value; tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS); @@ -406,13 +723,14 @@ static int tegra_dc_set_timings(struct tegra_dc *dc, } static int tegra_crtc_setup_clk(struct drm_crtc *crtc, - struct drm_display_mode *mode, - unsigned long *div) + struct drm_display_mode *mode) { - unsigned long pclk = mode->clock * 1000, rate; + unsigned long pclk = mode->clock * 1000; struct tegra_dc *dc = to_tegra_dc(crtc); struct tegra_output *output = NULL; struct drm_encoder *encoder; + unsigned int div; + u32 value; long err; list_for_each_entry(encoder, &crtc->dev->mode_config.encoder_list, head) @@ -425,221 +743,23 @@ static int tegra_crtc_setup_clk(struct drm_crtc *crtc, return -ENODEV; /* - * This assumes that the display controller will divide its parent - * clock by 2 to generate the pixel clock. + * This assumes that the parent clock is pll_d_out0 or pll_d2_out + * respectively, each of which divides the base pll_d by 2. */ - err = tegra_output_setup_clock(output, dc->clk, pclk * 2); + err = tegra_output_setup_clock(output, dc->clk, pclk, &div); if (err < 0) { dev_err(dc->dev, "failed to setup clock: %ld\n", err); return err; } - rate = clk_get_rate(dc->clk); - *div = (rate * 2 / pclk) - 2; - - DRM_DEBUG_KMS("rate: %lu, div: %lu\n", rate, *div); - - return 0; -} - -static bool tegra_dc_format_is_yuv(unsigned int format, bool *planar) -{ - switch (format) { - case WIN_COLOR_DEPTH_YCbCr422: - case WIN_COLOR_DEPTH_YUV422: - if (planar) - *planar = false; - - return true; - - case WIN_COLOR_DEPTH_YCbCr420P: - case WIN_COLOR_DEPTH_YUV420P: - case WIN_COLOR_DEPTH_YCbCr422P: - case WIN_COLOR_DEPTH_YUV422P: - case WIN_COLOR_DEPTH_YCbCr422R: - case WIN_COLOR_DEPTH_YUV422R: - case WIN_COLOR_DEPTH_YCbCr422RA: - case WIN_COLOR_DEPTH_YUV422RA: - if (planar) - *planar = true; - - return true; - } - - return false; -} - -int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, - const struct tegra_dc_window *window) -{ - unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp; - unsigned long value; - bool yuv, planar; - - /* - * For YUV planar modes, the number of bytes per pixel takes into - * account only the luma component and therefore is 1. - */ - yuv = tegra_dc_format_is_yuv(window->format, &planar); - if (!yuv) - bpp = window->bits_per_pixel / 8; - else - bpp = planar ? 1 : 2; - - value = WINDOW_A_SELECT << index; - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER); - - tegra_dc_writel(dc, window->format, DC_WIN_COLOR_DEPTH); - tegra_dc_writel(dc, 0, DC_WIN_BYTE_SWAP); - - value = V_POSITION(window->dst.y) | H_POSITION(window->dst.x); - tegra_dc_writel(dc, value, DC_WIN_POSITION); - - value = V_SIZE(window->dst.h) | H_SIZE(window->dst.w); - tegra_dc_writel(dc, value, DC_WIN_SIZE); - - h_offset = window->src.x * bpp; - v_offset = window->src.y; - h_size = window->src.w * bpp; - v_size = window->src.h; - - value = V_PRESCALED_SIZE(v_size) | H_PRESCALED_SIZE(h_size); - tegra_dc_writel(dc, value, DC_WIN_PRESCALED_SIZE); - - /* - * For DDA computations the number of bytes per pixel for YUV planar - * modes needs to take into account all Y, U and V components. - */ - if (yuv && planar) - bpp = 2; - - h_dda = compute_dda_inc(window->src.w, window->dst.w, false, bpp); - v_dda = compute_dda_inc(window->src.h, window->dst.h, true, bpp); - - value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda); - tegra_dc_writel(dc, value, DC_WIN_DDA_INC); - - h_dda = compute_initial_dda(window->src.x); - v_dda = compute_initial_dda(window->src.y); - - tegra_dc_writel(dc, h_dda, DC_WIN_H_INITIAL_DDA); - tegra_dc_writel(dc, v_dda, DC_WIN_V_INITIAL_DDA); - - tegra_dc_writel(dc, 0, DC_WIN_UV_BUF_STRIDE); - tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE); - - tegra_dc_writel(dc, window->base[0], DC_WINBUF_START_ADDR); - - if (yuv && planar) { - tegra_dc_writel(dc, window->base[1], DC_WINBUF_START_ADDR_U); - tegra_dc_writel(dc, window->base[2], DC_WINBUF_START_ADDR_V); - value = window->stride[1] << 16 | window->stride[0]; - tegra_dc_writel(dc, value, DC_WIN_LINE_STRIDE); - } else { - tegra_dc_writel(dc, window->stride[0], DC_WIN_LINE_STRIDE); - } - - if (window->bottom_up) - v_offset += window->src.h - 1; + DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk), div); - tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET); - tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET); - - if (window->tiled) { - value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV | - DC_WIN_BUFFER_ADDR_MODE_TILE; - } else { - value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV | - DC_WIN_BUFFER_ADDR_MODE_LINEAR; - } - - tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE); - - value = WIN_ENABLE; - - if (yuv) { - /* setup default colorspace conversion coefficients */ - tegra_dc_writel(dc, 0x00f0, DC_WIN_CSC_YOF); - tegra_dc_writel(dc, 0x012a, DC_WIN_CSC_KYRGB); - tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KUR); - tegra_dc_writel(dc, 0x0198, DC_WIN_CSC_KVR); - tegra_dc_writel(dc, 0x039b, DC_WIN_CSC_KUG); - tegra_dc_writel(dc, 0x032f, DC_WIN_CSC_KVG); - tegra_dc_writel(dc, 0x0204, DC_WIN_CSC_KUB); - tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KVB); - - value |= CSC_ENABLE; - } else if (window->bits_per_pixel < 24) { - value |= COLOR_EXPAND; - } - - if (window->bottom_up) - value |= INVERT_V; - - tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); - - /* - * Disable blending and assume Window A is the bottom-most window, - * Window C is the top-most window and Window B is in the middle. - */ - tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_NOKEY); - tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_1WIN); - - switch (index) { - case 0: - tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_X); - tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y); - tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY); - break; - - case 1: - tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X); - tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y); - tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY); - break; - - case 2: - tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X); - tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_Y); - tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_3WIN_XY); - break; - } - - tegra_dc_writel(dc, WIN_A_UPDATE << index, DC_CMD_STATE_CONTROL); - tegra_dc_writel(dc, WIN_A_ACT_REQ << index, DC_CMD_STATE_CONTROL); + value = SHIFT_CLK_DIVIDER(div) | PIXEL_CLK_DIVIDER_PCD1; + tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL); return 0; } -unsigned int tegra_dc_format(uint32_t format) -{ - switch (format) { - case DRM_FORMAT_XBGR8888: - return WIN_COLOR_DEPTH_R8G8B8A8; - - case DRM_FORMAT_XRGB8888: - return WIN_COLOR_DEPTH_B8G8R8A8; - - case DRM_FORMAT_RGB565: - return WIN_COLOR_DEPTH_B5G6R5; - - case DRM_FORMAT_UYVY: - return WIN_COLOR_DEPTH_YCbCr422; - - case DRM_FORMAT_YUV420: - return WIN_COLOR_DEPTH_YCbCr420P; - - case DRM_FORMAT_YUV422: - return WIN_COLOR_DEPTH_YCbCr422P; - - default: - break; - } - - WARN(1, "unsupported pixel format %u, using default\n", format); - return WIN_COLOR_DEPTH_B8G8R8A8; -} - static int tegra_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted, @@ -648,12 +768,12 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc, struct tegra_bo *bo = tegra_fb_get_plane(crtc->primary->fb, 0); struct tegra_dc *dc = to_tegra_dc(crtc); struct tegra_dc_window window; - unsigned long div, value; + u32 value; int err; drm_vblank_pre_modeset(crtc->dev, dc->pipe); - err = tegra_crtc_setup_clk(crtc, mode, &div); + err = tegra_crtc_setup_clk(crtc, mode); if (err) { dev_err(dc->dev, "failed to setup clock for CRTC: %d\n", err); return err; @@ -669,9 +789,6 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc, tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL); } - value = SHIFT_CLK_DIVIDER(div) | PIXEL_CLK_DIVIDER_PCD1; - tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL); - /* setup window parameters */ memset(&window, 0, sizeof(window)); window.src.x = 0; @@ -682,7 +799,8 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc, window.dst.y = 0; window.dst.w = mode->hdisplay; window.dst.h = mode->vdisplay; - window.format = tegra_dc_format(crtc->primary->fb->pixel_format); + window.format = tegra_dc_format(crtc->primary->fb->pixel_format, + &window.swap); window.bits_per_pixel = crtc->primary->fb->bits_per_pixel; window.stride[0] = crtc->primary->fb->pitches[0]; window.base[0] = bo->paddr; @@ -728,10 +846,6 @@ static void tegra_crtc_prepare(struct drm_crtc *crtc) WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY); - value = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | - PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); - /* initialize timer */ value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) | WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20); @@ -991,6 +1105,8 @@ static int tegra_dc_show_regs(struct seq_file *s, void *data) DUMP_REG(DC_DISP_SD_BL_CONTROL); DUMP_REG(DC_DISP_SD_HW_K_VALUES); DUMP_REG(DC_DISP_SD_MAN_K_VALUES); + DUMP_REG(DC_DISP_CURSOR_START_ADDR_HI); + DUMP_REG(DC_DISP_BLEND_CURSOR_CONTROL); DUMP_REG(DC_WIN_WIN_OPTIONS); DUMP_REG(DC_WIN_BYTE_SWAP); DUMP_REG(DC_WIN_BUFFER_CONTROL); @@ -1096,26 +1212,26 @@ static int tegra_dc_debugfs_exit(struct tegra_dc *dc) static int tegra_dc_init(struct host1x_client *client) { - struct tegra_drm *tegra = dev_get_drvdata(client->parent); + struct drm_device *drm = dev_get_drvdata(client->parent); struct tegra_dc *dc = host1x_client_to_dc(client); int err; - drm_crtc_init(tegra->drm, &dc->base, &tegra_crtc_funcs); + drm_crtc_init(drm, &dc->base, &tegra_crtc_funcs); drm_mode_crtc_set_gamma_size(&dc->base, 256); drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs); - err = tegra_dc_rgb_init(tegra->drm, dc); + err = tegra_dc_rgb_init(drm, dc); if (err < 0 && err != -ENODEV) { dev_err(dc->dev, "failed to initialize RGB output: %d\n", err); return err; } - err = tegra_dc_add_planes(tegra->drm, dc); + err = tegra_dc_add_planes(drm, dc); if (err < 0) return err; if (IS_ENABLED(CONFIG_DEBUG_FS)) { - err = tegra_dc_debugfs_init(dc, tegra->drm->primary); + err = tegra_dc_debugfs_init(dc, drm->primary); if (err < 0) dev_err(dc->dev, "debugfs setup failed: %d\n", err); } @@ -1160,14 +1276,17 @@ static const struct host1x_client_ops dc_client_ops = { static const struct tegra_dc_soc_info tegra20_dc_soc_info = { .supports_interlacing = false, + .supports_cursor = false, }; static const struct tegra_dc_soc_info tegra30_dc_soc_info = { .supports_interlacing = false, + .supports_cursor = false, }; static const struct tegra_dc_soc_info tegra124_dc_soc_info = { .supports_interlacing = true, + .supports_cursor = true, }; static const struct of_device_id tegra_dc_of_match[] = { diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index c9410149482..78c5feff95d 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -67,10 +67,12 @@ #define WIN_A_ACT_REQ (1 << 1) #define WIN_B_ACT_REQ (1 << 2) #define WIN_C_ACT_REQ (1 << 3) +#define CURSOR_ACT_REQ (1 << 7) #define GENERAL_UPDATE (1 << 8) #define WIN_A_UPDATE (1 << 9) #define WIN_B_UPDATE (1 << 10) #define WIN_C_UPDATE (1 << 11) +#define CURSOR_UPDATE (1 << 15) #define NC_HOST_TRIG (1 << 24) #define DC_CMD_DISPLAY_WINDOW_HEADER 0x042 @@ -116,9 +118,10 @@ #define DC_DISP_DISP_SIGNAL_OPTIONS1 0x401 #define DC_DISP_DISP_WIN_OPTIONS 0x402 -#define HDMI_ENABLE (1 << 30) -#define DSI_ENABLE (1 << 29) -#define SOR_ENABLE (1 << 25) +#define HDMI_ENABLE (1 << 30) +#define DSI_ENABLE (1 << 29) +#define SOR_ENABLE (1 << 25) +#define CURSOR_ENABLE (1 << 16) #define DC_DISP_DISP_MEM_HIGH_PRIORITY 0x403 #define CURSOR_THRESHOLD(x) (((x) & 0x03) << 24) @@ -266,6 +269,14 @@ #define DC_DISP_CURSOR_BACKGROUND 0x43d #define DC_DISP_CURSOR_START_ADDR 0x43e +#define CURSOR_CLIP_DISPLAY (0 << 28) +#define CURSOR_CLIP_WIN_A (1 << 28) +#define CURSOR_CLIP_WIN_B (2 << 28) +#define CURSOR_CLIP_WIN_C (3 << 28) +#define CURSOR_SIZE_32x32 (0 << 24) +#define CURSOR_SIZE_64x64 (1 << 24) +#define CURSOR_SIZE_128x128 (2 << 24) +#define CURSOR_SIZE_256x256 (3 << 24) #define DC_DISP_CURSOR_START_ADDR_NS 0x43f #define DC_DISP_CURSOR_POSITION 0x440 @@ -302,6 +313,19 @@ #define INTERLACE_START (1 << 1) #define INTERLACE_ENABLE (1 << 0) +#define DC_DISP_CURSOR_START_ADDR_HI 0x4ec +#define DC_DISP_BLEND_CURSOR_CONTROL 0x4f1 +#define CURSOR_MODE_LEGACY (0 << 24) +#define CURSOR_MODE_NORMAL (1 << 24) +#define CURSOR_DST_BLEND_ZERO (0 << 16) +#define CURSOR_DST_BLEND_K1 (1 << 16) +#define CURSOR_DST_BLEND_NEG_K1_TIMES_SRC (2 << 16) +#define CURSOR_DST_BLEND_MASK (3 << 16) +#define CURSOR_SRC_BLEND_K1 (0 << 8) +#define CURSOR_SRC_BLEND_K1_TIMES_SRC (1 << 8) +#define CURSOR_SRC_BLEND_MASK (3 << 8) +#define CURSOR_ALPHA 0xff + #define DC_WIN_CSC_YOF 0x611 #define DC_WIN_CSC_KYRGB 0x612 #define DC_WIN_CSC_KUR 0x613 @@ -312,7 +336,8 @@ #define DC_WIN_CSC_KVB 0x618 #define DC_WIN_WIN_OPTIONS 0x700 -#define INVERT_V (1 << 2) +#define H_DIRECTION (1 << 0) +#define V_DIRECTION (1 << 2) #define COLOR_EXPAND (1 << 6) #define CSC_ENABLE (1 << 18) #define WIN_ENABLE (1 << 30) diff --git a/drivers/gpu/drm/tegra/dpaux.c b/drivers/gpu/drm/tegra/dpaux.c index 005c19bd92d..3f132e356e9 100644 --- a/drivers/gpu/drm/tegra/dpaux.c +++ b/drivers/gpu/drm/tegra/dpaux.c @@ -15,6 +15,7 @@ #include <linux/platform_device.h> #include <linux/reset.h> #include <linux/regulator/consumer.h> +#include <linux/workqueue.h> #include <drm/drm_dp_helper.h> #include <drm/drm_panel.h> @@ -41,6 +42,7 @@ struct tegra_dpaux { struct regulator *vdd; struct completion complete; + struct work_struct work; struct list_head list; }; @@ -49,6 +51,11 @@ static inline struct tegra_dpaux *to_dpaux(struct drm_dp_aux *aux) return container_of(aux, struct tegra_dpaux, aux); } +static inline struct tegra_dpaux *work_to_dpaux(struct work_struct *work) +{ + return container_of(work, struct tegra_dpaux, work); +} + static inline unsigned long tegra_dpaux_readl(struct tegra_dpaux *dpaux, unsigned long offset) { @@ -231,6 +238,14 @@ static ssize_t tegra_dpaux_transfer(struct drm_dp_aux *aux, return ret; } +static void tegra_dpaux_hotplug(struct work_struct *work) +{ + struct tegra_dpaux *dpaux = work_to_dpaux(work); + + if (dpaux->output) + drm_helper_hpd_irq_event(dpaux->output->connector.dev); +} + static irqreturn_t tegra_dpaux_irq(int irq, void *data) { struct tegra_dpaux *dpaux = data; @@ -241,16 +256,8 @@ static irqreturn_t tegra_dpaux_irq(int irq, void *data) value = tegra_dpaux_readl(dpaux, DPAUX_INTR_AUX); tegra_dpaux_writel(dpaux, value, DPAUX_INTR_AUX); - if (value & DPAUX_INTR_PLUG_EVENT) { - if (dpaux->output) { - drm_helper_hpd_irq_event(dpaux->output->connector.dev); - } - } - - if (value & DPAUX_INTR_UNPLUG_EVENT) { - if (dpaux->output) - drm_helper_hpd_irq_event(dpaux->output->connector.dev); - } + if (value & (DPAUX_INTR_PLUG_EVENT | DPAUX_INTR_UNPLUG_EVENT)) + schedule_work(&dpaux->work); if (value & DPAUX_INTR_IRQ_EVENT) { /* TODO: handle this */ @@ -273,6 +280,7 @@ static int tegra_dpaux_probe(struct platform_device *pdev) if (!dpaux) return -ENOMEM; + INIT_WORK(&dpaux->work, tegra_dpaux_hotplug); init_completion(&dpaux->complete); INIT_LIST_HEAD(&dpaux->list); dpaux->dev = &pdev->dev; @@ -332,7 +340,7 @@ static int tegra_dpaux_probe(struct platform_device *pdev) dpaux->aux.transfer = tegra_dpaux_transfer; dpaux->aux.dev = &pdev->dev; - err = drm_dp_aux_register_i2c_bus(&dpaux->aux); + err = drm_dp_aux_register(&dpaux->aux); if (err < 0) return err; @@ -355,12 +363,14 @@ static int tegra_dpaux_remove(struct platform_device *pdev) { struct tegra_dpaux *dpaux = platform_get_drvdata(pdev); - drm_dp_aux_unregister_i2c_bus(&dpaux->aux); + drm_dp_aux_unregister(&dpaux->aux); mutex_lock(&dpaux_lock); list_del(&dpaux->list); mutex_unlock(&dpaux_lock); + cancel_work_sync(&dpaux->work); + clk_disable_unprepare(dpaux->clk_parent); reset_control_assert(dpaux->rst); clk_disable_unprepare(dpaux->clk); @@ -404,6 +414,7 @@ int tegra_dpaux_attach(struct tegra_dpaux *dpaux, struct tegra_output *output) unsigned long timeout; int err; + output->connector.polled = DRM_CONNECTOR_POLL_HPD; dpaux->output = output; err = regulator_enable(dpaux->vdd); diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 6f5b6e2f552..3396f9f6a9f 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -33,7 +33,6 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) if (!tegra) return -ENOMEM; - dev_set_drvdata(drm->dev, tegra); mutex_init(&tegra->clients_lock); INIT_LIST_HEAD(&tegra->clients); drm->dev_private = tegra; @@ -640,14 +639,40 @@ int tegra_drm_unregister_client(struct tegra_drm *tegra, return 0; } -static int host1x_drm_probe(struct host1x_device *device) +static int host1x_drm_probe(struct host1x_device *dev) { - return drm_host1x_init(&tegra_drm_driver, device); + struct drm_driver *driver = &tegra_drm_driver; + struct drm_device *drm; + int err; + + drm = drm_dev_alloc(driver, &dev->dev); + if (!drm) + return -ENOMEM; + + drm_dev_set_unique(drm, dev_name(&dev->dev)); + dev_set_drvdata(&dev->dev, drm); + + err = drm_dev_register(drm, 0); + if (err < 0) + goto unref; + + DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", driver->name, + driver->major, driver->minor, driver->patchlevel, + driver->date, drm->primary->index); + + return 0; + +unref: + drm_dev_unref(drm); + return err; } -static int host1x_drm_remove(struct host1x_device *device) +static int host1x_drm_remove(struct host1x_device *dev) { - drm_host1x_exit(&tegra_drm_driver, device); + struct drm_device *drm = dev_get_drvdata(&dev->dev); + + drm_dev_unregister(drm); + drm_dev_unref(drm); return 0; } @@ -666,6 +691,7 @@ static const struct of_device_id host1x_drm_subdevs[] = { { .compatible = "nvidia,tegra114-gr3d", }, { .compatible = "nvidia,tegra124-dc", }, { .compatible = "nvidia,tegra124-sor", }, + { .compatible = "nvidia,tegra124-hdmi", }, { /* sentinel */ } }; diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index 126332c3ecb..6b8fe9d86ed 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -80,13 +80,13 @@ host1x_to_drm_client(struct host1x_client *client) return container_of(client, struct tegra_drm_client, base); } -extern int tegra_drm_register_client(struct tegra_drm *tegra, - struct tegra_drm_client *client); -extern int tegra_drm_unregister_client(struct tegra_drm *tegra, - struct tegra_drm_client *client); +int tegra_drm_register_client(struct tegra_drm *tegra, + struct tegra_drm_client *client); +int tegra_drm_unregister_client(struct tegra_drm *tegra, + struct tegra_drm_client *client); -extern int tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm); -extern int tegra_drm_exit(struct tegra_drm *tegra); +int tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm); +int tegra_drm_exit(struct tegra_drm *tegra); struct tegra_dc_soc_info; struct tegra_output; @@ -156,6 +156,7 @@ struct tegra_dc_window { } dst; unsigned int bits_per_pixel; unsigned int format; + unsigned int swap; unsigned int stride[2]; unsigned long base[3]; bool bottom_up; @@ -163,19 +164,15 @@ struct tegra_dc_window { }; /* from dc.c */ -extern unsigned int tegra_dc_format(uint32_t format); -extern int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, - const struct tegra_dc_window *window); -extern void tegra_dc_enable_vblank(struct tegra_dc *dc); -extern void tegra_dc_disable_vblank(struct tegra_dc *dc); -extern void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, - struct drm_file *file); +void tegra_dc_enable_vblank(struct tegra_dc *dc); +void tegra_dc_disable_vblank(struct tegra_dc *dc); +void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file); struct tegra_output_ops { int (*enable)(struct tegra_output *output); int (*disable)(struct tegra_output *output); int (*setup_clock)(struct tegra_output *output, struct clk *clk, - unsigned long pclk); + unsigned long pclk, unsigned int *div); int (*check_mode)(struct tegra_output *output, struct drm_display_mode *mode, enum drm_mode_status *status); @@ -233,10 +230,11 @@ static inline int tegra_output_disable(struct tegra_output *output) } static inline int tegra_output_setup_clock(struct tegra_output *output, - struct clk *clk, unsigned long pclk) + struct clk *clk, unsigned long pclk, + unsigned int *div) { if (output && output->ops && output->ops->setup_clock) - return output->ops->setup_clock(output, clk, pclk); + return output->ops->setup_clock(output, clk, pclk, div); return output ? -ENOSYS : -EINVAL; } @@ -251,27 +249,21 @@ static inline int tegra_output_check_mode(struct tegra_output *output, return output ? -ENOSYS : -EINVAL; } -/* from bus.c */ -int drm_host1x_init(struct drm_driver *driver, struct host1x_device *device); -void drm_host1x_exit(struct drm_driver *driver, struct host1x_device *device); - /* from rgb.c */ -extern int tegra_dc_rgb_probe(struct tegra_dc *dc); -extern int tegra_dc_rgb_remove(struct tegra_dc *dc); -extern int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc); -extern int tegra_dc_rgb_exit(struct tegra_dc *dc); +int tegra_dc_rgb_probe(struct tegra_dc *dc); +int tegra_dc_rgb_remove(struct tegra_dc *dc); +int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc); +int tegra_dc_rgb_exit(struct tegra_dc *dc); /* from output.c */ -extern int tegra_output_probe(struct tegra_output *output); -extern int tegra_output_remove(struct tegra_output *output); -extern int tegra_output_init(struct drm_device *drm, struct tegra_output *output); -extern int tegra_output_exit(struct tegra_output *output); +int tegra_output_probe(struct tegra_output *output); +int tegra_output_remove(struct tegra_output *output); +int tegra_output_init(struct drm_device *drm, struct tegra_output *output); +int tegra_output_exit(struct tegra_output *output); /* from dpaux.c */ - struct tegra_dpaux; struct drm_dp_link; -struct drm_dp_aux; struct tegra_dpaux *tegra_dpaux_find_by_of_node(struct device_node *np); enum drm_connector_status tegra_dpaux_detect(struct tegra_dpaux *dpaux); @@ -288,10 +280,10 @@ struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer, unsigned int index); bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer); bool tegra_fb_is_tiled(struct drm_framebuffer *framebuffer); -extern int tegra_drm_fb_init(struct drm_device *drm); -extern void tegra_drm_fb_exit(struct drm_device *drm); +int tegra_drm_fb_init(struct drm_device *drm); +void tegra_drm_fb_exit(struct drm_device *drm); #ifdef CONFIG_DRM_TEGRA_FBDEV -extern void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev); +void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev); #endif extern struct platform_driver tegra_dc_driver; diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index 0e599f0417c..bd56f2affa7 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -14,6 +14,8 @@ #include <linux/platform_device.h> #include <linux/reset.h> +#include <linux/regulator/consumer.h> + #include <drm/drm_mipi_dsi.h> #include <drm/drm_panel.h> @@ -43,11 +45,15 @@ struct tegra_dsi { struct drm_minor *minor; struct dentry *debugfs; + unsigned long flags; enum mipi_dsi_pixel_format format; unsigned int lanes; struct tegra_mipi_device *mipi; struct mipi_dsi_host host; + + struct regulator *vdd; + bool enabled; }; static inline struct tegra_dsi * @@ -244,8 +250,10 @@ static int tegra_dsi_debugfs_exit(struct tegra_dsi *dsi) #define PKT_LP (1 << 30) #define NUM_PKT_SEQ 12 -/* non-burst mode with sync-end */ -static const u32 pkt_seq_vnb_syne[NUM_PKT_SEQ] = { +/* + * non-burst mode with sync pulses + */ +static const u32 pkt_seq_video_non_burst_sync_pulses[NUM_PKT_SEQ] = { [ 0] = PKT_ID0(MIPI_DSI_V_SYNC_START) | PKT_LEN0(0) | PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) | @@ -280,6 +288,36 @@ static const u32 pkt_seq_vnb_syne[NUM_PKT_SEQ] = { PKT_ID2(MIPI_DSI_BLANKING_PACKET) | PKT_LEN2(4), }; +/* + * non-burst mode with sync events + */ +static const u32 pkt_seq_video_non_burst_sync_events[NUM_PKT_SEQ] = { + [ 0] = PKT_ID0(MIPI_DSI_V_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) | + PKT_LP, + [ 1] = 0, + [ 2] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) | + PKT_LP, + [ 3] = 0, + [ 4] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) | + PKT_LP, + [ 5] = 0, + [ 6] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(2) | + PKT_ID2(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN2(3), + [ 7] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4), + [ 8] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) | + PKT_LP, + [ 9] = 0, + [10] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(2) | + PKT_ID2(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN2(3), + [11] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4), +}; + static int tegra_dsi_set_phy_timing(struct tegra_dsi *dsi) { struct mipi_dphy_timing timing; @@ -361,28 +399,70 @@ static int tegra_dsi_get_muldiv(enum mipi_dsi_pixel_format format, return 0; } +static int tegra_dsi_get_format(enum mipi_dsi_pixel_format format, + enum tegra_dsi_format *fmt) +{ + switch (format) { + case MIPI_DSI_FMT_RGB888: + *fmt = TEGRA_DSI_FORMAT_24P; + break; + + case MIPI_DSI_FMT_RGB666: + *fmt = TEGRA_DSI_FORMAT_18NP; + break; + + case MIPI_DSI_FMT_RGB666_PACKED: + *fmt = TEGRA_DSI_FORMAT_18P; + break; + + case MIPI_DSI_FMT_RGB565: + *fmt = TEGRA_DSI_FORMAT_16P; + break; + + default: + return -EINVAL; + } + + return 0; +} + static int tegra_output_dsi_enable(struct tegra_output *output) { struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); struct drm_display_mode *mode = &dc->base.mode; unsigned int hact, hsw, hbp, hfp, i, mul, div; struct tegra_dsi *dsi = to_dsi(output); - /* FIXME: don't hardcode this */ - const u32 *pkt_seq = pkt_seq_vnb_syne; + enum tegra_dsi_format format; unsigned long value; + const u32 *pkt_seq; int err; + if (dsi->enabled) + return 0; + + if (dsi->flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) { + DRM_DEBUG_KMS("Non-burst video mode with sync pulses\n"); + pkt_seq = pkt_seq_video_non_burst_sync_pulses; + } else { + DRM_DEBUG_KMS("Non-burst video mode with sync events\n"); + pkt_seq = pkt_seq_video_non_burst_sync_events; + } + err = tegra_dsi_get_muldiv(dsi->format, &mul, &div); if (err < 0) return err; + err = tegra_dsi_get_format(dsi->format, &format); + if (err < 0) + return err; + err = clk_enable(dsi->clk); if (err < 0) return err; reset_control_deassert(dsi->rst); - value = DSI_CONTROL_CHANNEL(0) | DSI_CONTROL_FORMAT(dsi->format) | + value = DSI_CONTROL_CHANNEL(0) | DSI_CONTROL_FORMAT(format) | DSI_CONTROL_LANES(dsi->lanes - 1) | DSI_CONTROL_SOURCE(dc->pipe); tegra_dsi_writel(dsi, value, DSI_CONTROL); @@ -454,6 +534,8 @@ static int tegra_output_dsi_enable(struct tegra_output *output) value |= DSI_POWER_CONTROL_ENABLE; tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL); + dsi->enabled = true; + return 0; } @@ -463,9 +545,12 @@ static int tegra_output_dsi_disable(struct tegra_output *output) struct tegra_dsi *dsi = to_dsi(output); unsigned long value; + if (!dsi->enabled) + return 0; + /* disable DSI controller */ value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL); - value &= DSI_POWER_CONTROL_ENABLE; + value &= ~DSI_POWER_CONTROL_ENABLE; tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL); /* @@ -492,30 +577,44 @@ static int tegra_output_dsi_disable(struct tegra_output *output) clk_disable(dsi->clk); + dsi->enabled = false; + return 0; } static int tegra_output_dsi_setup_clock(struct tegra_output *output, - struct clk *clk, unsigned long pclk) + struct clk *clk, unsigned long pclk, + unsigned int *divp) { struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); struct drm_display_mode *mode = &dc->base.mode; unsigned int timeout, mul, div, vrefresh; struct tegra_dsi *dsi = to_dsi(output); unsigned long bclk, plld, value; - struct clk *base; int err; err = tegra_dsi_get_muldiv(dsi->format, &mul, &div); if (err < 0) return err; + DRM_DEBUG_KMS("mul: %u, div: %u, lanes: %u\n", mul, div, dsi->lanes); vrefresh = drm_mode_vrefresh(mode); + DRM_DEBUG_KMS("vrefresh: %u\n", vrefresh); - pclk = mode->htotal * mode->vtotal * vrefresh; + /* compute byte clock */ bclk = (pclk * mul) / (div * dsi->lanes); - plld = DIV_ROUND_UP(bclk * 8, 1000000); - pclk = (plld * 1000000) / 2; + + /* + * Compute bit clock and round up to the next MHz. + */ + plld = DIV_ROUND_UP(bclk * 8, 1000000) * 1000000; + + /* + * We divide the frequency by two here, but we make up for that by + * setting the shift clock divider (further below) to half of the + * correct value. + */ + plld /= 2; err = clk_set_parent(clk, dsi->clk_parent); if (err < 0) { @@ -523,20 +622,26 @@ static int tegra_output_dsi_setup_clock(struct tegra_output *output, return err; } - base = clk_get_parent(dsi->clk_parent); - - /* - * This assumes that the parent clock is pll_d_out0 or pll_d2_out - * respectively, each of which divides the base pll_d by 2. - */ - err = clk_set_rate(base, pclk * 2); + err = clk_set_rate(dsi->clk_parent, plld); if (err < 0) { dev_err(dsi->dev, "failed to set base clock rate to %lu Hz\n", - pclk * 2); + plld); return err; } /* + * Derive pixel clock from bit clock using the shift clock divider. + * Note that this is only half of what we would expect, but we need + * that to make up for the fact that we divided the bit clock by a + * factor of two above. + * + * It's not clear exactly why this is necessary, but the display is + * not working properly otherwise. Perhaps the PLLs cannot generate + * frequencies sufficiently high. + */ + *divp = ((8 * mul) / (div * dsi->lanes)) - 2; + + /* * XXX: Move the below somewhere else so that we don't need to have * access to the vrefresh in this function? */ @@ -610,61 +715,32 @@ static int tegra_dsi_pad_calibrate(struct tegra_dsi *dsi) static int tegra_dsi_init(struct host1x_client *client) { - struct tegra_drm *tegra = dev_get_drvdata(client->parent); + struct drm_device *drm = dev_get_drvdata(client->parent); struct tegra_dsi *dsi = host1x_client_to_dsi(client); - unsigned long value, i; int err; dsi->output.type = TEGRA_OUTPUT_DSI; dsi->output.dev = client->dev; dsi->output.ops = &dsi_ops; - err = tegra_output_init(tegra->drm, &dsi->output); + err = tegra_output_init(drm, &dsi->output); if (err < 0) { dev_err(client->dev, "output setup failed: %d\n", err); return err; } if (IS_ENABLED(CONFIG_DEBUG_FS)) { - err = tegra_dsi_debugfs_init(dsi, tegra->drm->primary); + err = tegra_dsi_debugfs_init(dsi, drm->primary); if (err < 0) dev_err(dsi->dev, "debugfs setup failed: %d\n", err); } - /* - * enable high-speed mode, checksum generation, ECC generation and - * disable raw mode - */ - value = tegra_dsi_readl(dsi, DSI_HOST_CONTROL); - value |= DSI_HOST_CONTROL_ECC | DSI_HOST_CONTROL_CS | - DSI_HOST_CONTROL_HS; - value &= ~DSI_HOST_CONTROL_RAW; - tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL); - - tegra_dsi_writel(dsi, 0, DSI_SOL_DELAY); - tegra_dsi_writel(dsi, 0, DSI_MAX_THRESHOLD); - - tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_CONTROL); - - for (i = 0; i < 8; i++) { - tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_DATA_0 + i); - tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_DATA_8 + i); - } - - for (i = 0; i < 12; i++) - tegra_dsi_writel(dsi, 0, DSI_PKT_SEQ_0_LO + i); - - tegra_dsi_writel(dsi, 0, DSI_DCS_CMDS); - err = tegra_dsi_pad_calibrate(dsi); if (err < 0) { dev_err(dsi->dev, "MIPI calibration failed: %d\n", err); return err; } - tegra_dsi_writel(dsi, DSI_POWER_CONTROL_ENABLE, DSI_POWER_CONTROL); - usleep_range(300, 1000); - return 0; } @@ -715,66 +791,13 @@ static int tegra_dsi_setup_clocks(struct tegra_dsi *dsi) return 0; } -static void tegra_dsi_initialize(struct tegra_dsi *dsi) -{ - unsigned int i; - - tegra_dsi_writel(dsi, 0, DSI_POWER_CONTROL); - - tegra_dsi_writel(dsi, 0, DSI_INT_ENABLE); - tegra_dsi_writel(dsi, 0, DSI_INT_STATUS); - tegra_dsi_writel(dsi, 0, DSI_INT_MASK); - - tegra_dsi_writel(dsi, 0, DSI_HOST_CONTROL); - tegra_dsi_writel(dsi, 0, DSI_CONTROL); - - tegra_dsi_writel(dsi, 0, DSI_SOL_DELAY); - tegra_dsi_writel(dsi, 0, DSI_MAX_THRESHOLD); - - tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_CONTROL); - - for (i = 0; i < 8; i++) { - tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_DATA_0 + i); - tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_DATA_8 + i); - } - - for (i = 0; i < 12; i++) - tegra_dsi_writel(dsi, 0, DSI_PKT_SEQ_0_LO + i); - - tegra_dsi_writel(dsi, 0, DSI_DCS_CMDS); - - for (i = 0; i < 4; i++) - tegra_dsi_writel(dsi, 0, DSI_PKT_LEN_0_1 + i); - - tegra_dsi_writel(dsi, 0x00000000, DSI_PHY_TIMING_0); - tegra_dsi_writel(dsi, 0x00000000, DSI_PHY_TIMING_1); - tegra_dsi_writel(dsi, 0x000000ff, DSI_PHY_TIMING_2); - tegra_dsi_writel(dsi, 0x00000000, DSI_BTA_TIMING); - - tegra_dsi_writel(dsi, 0, DSI_TIMEOUT_0); - tegra_dsi_writel(dsi, 0, DSI_TIMEOUT_1); - tegra_dsi_writel(dsi, 0, DSI_TO_TALLY); - - tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_0); - tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_CD); - tegra_dsi_writel(dsi, 0, DSI_PAD_CD_STATUS); - tegra_dsi_writel(dsi, 0, DSI_VIDEO_MODE_CONTROL); - tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_1); - tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_2); - tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_3); - tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_4); - - tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_CONTROL); - tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_START); - tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_SIZE); -} - static int tegra_dsi_host_attach(struct mipi_dsi_host *host, struct mipi_dsi_device *device) { struct tegra_dsi *dsi = host_to_tegra(host); struct tegra_output *output = &dsi->output; + dsi->flags = device->mode_flags; dsi->format = device->format; dsi->lanes = device->lanes; @@ -829,6 +852,7 @@ static int tegra_dsi_probe(struct platform_device *pdev) * attaches to the DSI host, the parameters will be taken from * the attached device. */ + dsi->flags = MIPI_DSI_MODE_VIDEO; dsi->format = MIPI_DSI_FMT_RGB888; dsi->lanes = 4; @@ -872,6 +896,18 @@ static int tegra_dsi_probe(struct platform_device *pdev) return err; } + dsi->vdd = devm_regulator_get(&pdev->dev, "avdd-dsi-csi"); + if (IS_ERR(dsi->vdd)) { + dev_err(&pdev->dev, "cannot get VDD supply\n"); + return PTR_ERR(dsi->vdd); + } + + err = regulator_enable(dsi->vdd); + if (err < 0) { + dev_err(&pdev->dev, "cannot enable VDD supply\n"); + return err; + } + err = tegra_dsi_setup_clocks(dsi); if (err < 0) { dev_err(&pdev->dev, "cannot setup clocks\n"); @@ -883,8 +919,6 @@ static int tegra_dsi_probe(struct platform_device *pdev) if (IS_ERR(dsi->regs)) return PTR_ERR(dsi->regs); - tegra_dsi_initialize(dsi); - dsi->mipi = tegra_mipi_request(&pdev->dev); if (IS_ERR(dsi->mipi)) return PTR_ERR(dsi->mipi); @@ -929,9 +963,11 @@ static int tegra_dsi_remove(struct platform_device *pdev) mipi_dsi_host_unregister(&dsi->host); tegra_mipi_free(dsi->mipi); + regulator_disable(dsi->vdd); clk_disable_unprepare(dsi->clk_parent); clk_disable_unprepare(dsi->clk_lp); clk_disable_unprepare(dsi->clk); + reset_control_assert(dsi->rst); err = tegra_output_remove(&dsi->output); if (err < 0) { diff --git a/drivers/gpu/drm/tegra/dsi.h b/drivers/gpu/drm/tegra/dsi.h index 1db5cc24ea9..5ce610d08d7 100644 --- a/drivers/gpu/drm/tegra/dsi.h +++ b/drivers/gpu/drm/tegra/dsi.h @@ -117,4 +117,14 @@ #define DSI_INIT_SEQ_DATA_14 0x5e #define DSI_INIT_SEQ_DATA_15 0x5f +/* + * pixel format as used in the DSI_CONTROL_FORMAT field + */ +enum tegra_dsi_format { + TEGRA_DSI_FORMAT_16P, + TEGRA_DSI_FORMAT_18NP, + TEGRA_DSI_FORMAT_18P, + TEGRA_DSI_FORMAT_24P, +}; + #endif diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index f7fca09d492..9798a708032 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -346,11 +346,8 @@ static void tegra_fbdev_free(struct tegra_fbdev *fbdev) void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev) { - if (fbdev) { - drm_modeset_lock_all(fbdev->base.dev); - drm_fb_helper_restore_fbdev_mode(&fbdev->base); - drm_modeset_unlock_all(fbdev->base.dev); - } + if (fbdev) + drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev->base); } static void tegra_fb_output_poll_changed(struct drm_device *drm) diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index bcf9895cef9..aa85b7b26f1 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -169,7 +169,8 @@ err: return ERR_PTR(ret); } -struct tegra_bo *tegra_bo_import(struct drm_device *drm, struct dma_buf *buf) +static struct tegra_bo *tegra_bo_import(struct drm_device *drm, + struct dma_buf *buf) { struct dma_buf_attachment *attach; struct tegra_bo *bo; diff --git a/drivers/gpu/drm/tegra/gr2d.c b/drivers/gpu/drm/tegra/gr2d.c index 2c7ca748edf..7c53941f2a9 100644 --- a/drivers/gpu/drm/tegra/gr2d.c +++ b/drivers/gpu/drm/tegra/gr2d.c @@ -28,7 +28,7 @@ static inline struct gr2d *to_gr2d(struct tegra_drm_client *client) static int gr2d_init(struct host1x_client *client) { struct tegra_drm_client *drm = host1x_to_drm_client(client); - struct tegra_drm *tegra = dev_get_drvdata(client->parent); + struct drm_device *dev = dev_get_drvdata(client->parent); unsigned long flags = HOST1X_SYNCPT_HAS_BASE; struct gr2d *gr2d = to_gr2d(drm); @@ -42,17 +42,17 @@ static int gr2d_init(struct host1x_client *client) return -ENOMEM; } - return tegra_drm_register_client(tegra, drm); + return tegra_drm_register_client(dev->dev_private, drm); } static int gr2d_exit(struct host1x_client *client) { struct tegra_drm_client *drm = host1x_to_drm_client(client); - struct tegra_drm *tegra = dev_get_drvdata(client->parent); + struct drm_device *dev = dev_get_drvdata(client->parent); struct gr2d *gr2d = to_gr2d(drm); int err; - err = tegra_drm_unregister_client(tegra, drm); + err = tegra_drm_unregister_client(dev->dev_private, drm); if (err < 0) return err; diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c index 0cbb24b1ae0..30f5ba9bd6d 100644 --- a/drivers/gpu/drm/tegra/gr3d.c +++ b/drivers/gpu/drm/tegra/gr3d.c @@ -37,7 +37,7 @@ static inline struct gr3d *to_gr3d(struct tegra_drm_client *client) static int gr3d_init(struct host1x_client *client) { struct tegra_drm_client *drm = host1x_to_drm_client(client); - struct tegra_drm *tegra = dev_get_drvdata(client->parent); + struct drm_device *dev = dev_get_drvdata(client->parent); unsigned long flags = HOST1X_SYNCPT_HAS_BASE; struct gr3d *gr3d = to_gr3d(drm); @@ -51,17 +51,17 @@ static int gr3d_init(struct host1x_client *client) return -ENOMEM; } - return tegra_drm_register_client(tegra, drm); + return tegra_drm_register_client(dev->dev_private, drm); } static int gr3d_exit(struct host1x_client *client) { struct tegra_drm_client *drm = host1x_to_drm_client(client); - struct tegra_drm *tegra = dev_get_drvdata(client->parent); + struct drm_device *dev = dev_get_drvdata(client->parent); struct gr3d *gr3d = to_gr3d(drm); int err; - err = tegra_drm_unregister_client(tegra, drm); + err = tegra_drm_unregister_client(dev->dev_private, drm); if (err < 0) return err; diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index 6928015d11a..ba067bb767e 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -42,8 +42,9 @@ struct tegra_hdmi { struct device *dev; bool enabled; - struct regulator *vdd; + struct regulator *hdmi; struct regulator *pll; + struct regulator *vdd; void __iomem *regs; unsigned int irq; @@ -317,6 +318,85 @@ static const struct tmds_config tegra114_tmds_config[] = { }, }; +static const struct tmds_config tegra124_tmds_config[] = { + { /* 480p/576p / 25.2MHz/27MHz modes */ + .pclk = 27000000, + .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) | + SOR_PLL_VCOCAP(0) | SOR_PLL_RESISTORSEL, + .pll1 = SOR_PLL_LOADADJ(3) | SOR_PLL_TMDS_TERMADJ(0), + .pe_current = PE_CURRENT0(PE_CURRENT_0_mA_T114) | + PE_CURRENT1(PE_CURRENT_0_mA_T114) | + PE_CURRENT2(PE_CURRENT_0_mA_T114) | + PE_CURRENT3(PE_CURRENT_0_mA_T114), + .drive_current = + DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_10_400_mA_T114) | + DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_10_400_mA_T114) | + DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_10_400_mA_T114) | + DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_10_400_mA_T114), + .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_0_000_mA) | + PEAK_CURRENT_LANE1(PEAK_CURRENT_0_000_mA) | + PEAK_CURRENT_LANE2(PEAK_CURRENT_0_000_mA) | + PEAK_CURRENT_LANE3(PEAK_CURRENT_0_000_mA), + }, { /* 720p / 74.25MHz modes */ + .pclk = 74250000, + .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) | + SOR_PLL_VCOCAP(1) | SOR_PLL_RESISTORSEL, + .pll1 = SOR_PLL_PE_EN | SOR_PLL_LOADADJ(3) | + SOR_PLL_TMDS_TERMADJ(0), + .pe_current = PE_CURRENT0(PE_CURRENT_15_mA_T114) | + PE_CURRENT1(PE_CURRENT_15_mA_T114) | + PE_CURRENT2(PE_CURRENT_15_mA_T114) | + PE_CURRENT3(PE_CURRENT_15_mA_T114), + .drive_current = + DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_10_400_mA_T114) | + DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_10_400_mA_T114) | + DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_10_400_mA_T114) | + DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_10_400_mA_T114), + .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_0_000_mA) | + PEAK_CURRENT_LANE1(PEAK_CURRENT_0_000_mA) | + PEAK_CURRENT_LANE2(PEAK_CURRENT_0_000_mA) | + PEAK_CURRENT_LANE3(PEAK_CURRENT_0_000_mA), + }, { /* 1080p / 148.5MHz modes */ + .pclk = 148500000, + .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) | + SOR_PLL_VCOCAP(3) | SOR_PLL_RESISTORSEL, + .pll1 = SOR_PLL_PE_EN | SOR_PLL_LOADADJ(3) | + SOR_PLL_TMDS_TERMADJ(0), + .pe_current = PE_CURRENT0(PE_CURRENT_10_mA_T114) | + PE_CURRENT1(PE_CURRENT_10_mA_T114) | + PE_CURRENT2(PE_CURRENT_10_mA_T114) | + PE_CURRENT3(PE_CURRENT_10_mA_T114), + .drive_current = + DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_12_400_mA_T114) | + DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_12_400_mA_T114) | + DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_12_400_mA_T114) | + DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_12_400_mA_T114), + .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_0_000_mA) | + PEAK_CURRENT_LANE1(PEAK_CURRENT_0_000_mA) | + PEAK_CURRENT_LANE2(PEAK_CURRENT_0_000_mA) | + PEAK_CURRENT_LANE3(PEAK_CURRENT_0_000_mA), + }, { /* 225/297MHz modes */ + .pclk = UINT_MAX, + .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) | + SOR_PLL_VCOCAP(0xf) | SOR_PLL_RESISTORSEL, + .pll1 = SOR_PLL_LOADADJ(3) | SOR_PLL_TMDS_TERMADJ(7) + | SOR_PLL_TMDS_TERM_ENABLE, + .pe_current = PE_CURRENT0(PE_CURRENT_0_mA_T114) | + PE_CURRENT1(PE_CURRENT_0_mA_T114) | + PE_CURRENT2(PE_CURRENT_0_mA_T114) | + PE_CURRENT3(PE_CURRENT_0_mA_T114), + .drive_current = + DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_25_200_mA_T114) | + DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_25_200_mA_T114) | + DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_25_200_mA_T114) | + DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_19_200_mA_T114), + .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_3_000_mA) | + PEAK_CURRENT_LANE1(PEAK_CURRENT_3_000_mA) | + PEAK_CURRENT_LANE2(PEAK_CURRENT_3_000_mA) | + PEAK_CURRENT_LANE3(PEAK_CURRENT_0_800_mA), + }, +}; + static const struct tegra_hdmi_audio_config * tegra_hdmi_get_audio_config(unsigned int audio_freq, unsigned int pclk) { @@ -716,13 +796,9 @@ static int tegra_output_hdmi_enable(struct tegra_output *output) return err; } - /* - * This assumes that the display controller will divide its parent - * clock by 2 to generate the pixel clock. - */ - err = tegra_output_setup_clock(output, hdmi->clk, pclk * 2); + err = regulator_enable(hdmi->vdd); if (err < 0) { - dev_err(hdmi->dev, "failed to setup clock: %d\n", err); + dev_err(hdmi->dev, "failed to enable VDD regulator: %d\n", err); return err; } @@ -730,7 +806,7 @@ static int tegra_output_hdmi_enable(struct tegra_output *output) if (err < 0) return err; - err = clk_enable(hdmi->clk); + err = clk_prepare_enable(hdmi->clk); if (err < 0) { dev_err(hdmi->dev, "failed to enable clock: %d\n", err); return err; @@ -740,6 +816,17 @@ static int tegra_output_hdmi_enable(struct tegra_output *output) usleep_range(1000, 2000); reset_control_deassert(hdmi->rst); + /* power up sequence */ + value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_PLL0); + value &= ~SOR_PLL_PDBG; + tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_PLL0); + + usleep_range(10, 20); + + value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_PLL0); + value &= ~SOR_PLL_PWR; + tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_PLL0); + tegra_dc_writel(dc, VSYNC_H_POSITION(1), DC_DISP_DISP_TIMING_OPTIONS); tegra_dc_writel(dc, DITHER_CONTROL_DISABLE | BASE_COLOR_SIZE888, @@ -838,9 +925,13 @@ static int tegra_output_hdmi_enable(struct tegra_output *output) tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_SEQ_INST(0)); tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_SEQ_INST(8)); - value = 0x1c800; + value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_CSTM); value &= ~SOR_CSTM_ROTCLK(~0); value |= SOR_CSTM_ROTCLK(2); + value |= SOR_CSTM_PLLDIV; + value &= ~SOR_CSTM_LVDS_ENABLE; + value &= ~SOR_CSTM_MODE_MASK; + value |= SOR_CSTM_MODE_TMDS; tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_CSTM); /* start SOR */ @@ -930,10 +1021,18 @@ static int tegra_output_hdmi_disable(struct tegra_output *output) * sure it's only executed when the output is attached to one. */ if (dc) { + /* + * XXX: We can't do this here because it causes HDMI to go + * into an erroneous state with the result that HDMI won't + * properly work once disabled. See also a similar symptom + * for the SOR output. + */ + /* value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | PW4_ENABLE | PM0_ENABLE | PM1_ENABLE); tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); + */ value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); value &= ~DISP_CTRL_MODE_MASK; @@ -947,8 +1046,9 @@ static int tegra_output_hdmi_disable(struct tegra_output *output) tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); } + clk_disable_unprepare(hdmi->clk); reset_control_assert(hdmi->rst); - clk_disable(hdmi->clk); + regulator_disable(hdmi->vdd); regulator_disable(hdmi->pll); hdmi->enabled = false; @@ -957,10 +1057,10 @@ static int tegra_output_hdmi_disable(struct tegra_output *output) } static int tegra_output_hdmi_setup_clock(struct tegra_output *output, - struct clk *clk, unsigned long pclk) + struct clk *clk, unsigned long pclk, + unsigned int *div) { struct tegra_hdmi *hdmi = to_hdmi(output); - struct clk *base; int err; err = clk_set_parent(clk, hdmi->clk_parent); @@ -969,17 +1069,12 @@ static int tegra_output_hdmi_setup_clock(struct tegra_output *output, return err; } - base = clk_get_parent(hdmi->clk_parent); - - /* - * This assumes that the parent clock is pll_d_out0 or pll_d2_out - * respectively, each of which divides the base pll_d by 2. - */ - err = clk_set_rate(base, pclk * 2); + err = clk_set_rate(hdmi->clk_parent, pclk); if (err < 0) - dev_err(output->dev, - "failed to set base clock rate to %lu Hz\n", - pclk * 2); + dev_err(output->dev, "failed to set clock rate to %lu Hz\n", + pclk); + + *div = 0; return 0; } @@ -1017,7 +1112,7 @@ static int tegra_hdmi_show_regs(struct seq_file *s, void *data) struct tegra_hdmi *hdmi = node->info_ent->data; int err; - err = clk_enable(hdmi->clk); + err = clk_prepare_enable(hdmi->clk); if (err) return err; @@ -1186,7 +1281,7 @@ static int tegra_hdmi_show_regs(struct seq_file *s, void *data) #undef DUMP_REG - clk_disable(hdmi->clk); + clk_disable_unprepare(hdmi->clk); return 0; } @@ -1252,33 +1347,33 @@ static int tegra_hdmi_debugfs_exit(struct tegra_hdmi *hdmi) static int tegra_hdmi_init(struct host1x_client *client) { - struct tegra_drm *tegra = dev_get_drvdata(client->parent); + struct drm_device *drm = dev_get_drvdata(client->parent); struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client); int err; - err = regulator_enable(hdmi->vdd); - if (err < 0) { - dev_err(client->dev, "failed to enable VDD regulator: %d\n", - err); - return err; - } - hdmi->output.type = TEGRA_OUTPUT_HDMI; hdmi->output.dev = client->dev; hdmi->output.ops = &hdmi_ops; - err = tegra_output_init(tegra->drm, &hdmi->output); + err = tegra_output_init(drm, &hdmi->output); if (err < 0) { dev_err(client->dev, "output setup failed: %d\n", err); return err; } if (IS_ENABLED(CONFIG_DEBUG_FS)) { - err = tegra_hdmi_debugfs_init(hdmi, tegra->drm->primary); + err = tegra_hdmi_debugfs_init(hdmi, drm->primary); if (err < 0) dev_err(client->dev, "debugfs setup failed: %d\n", err); } + err = regulator_enable(hdmi->hdmi); + if (err < 0) { + dev_err(client->dev, "failed to enable HDMI regulator: %d\n", + err); + return err; + } + return 0; } @@ -1287,6 +1382,8 @@ static int tegra_hdmi_exit(struct host1x_client *client) struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client); int err; + regulator_disable(hdmi->hdmi); + if (IS_ENABLED(CONFIG_DEBUG_FS)) { err = tegra_hdmi_debugfs_exit(hdmi); if (err < 0) @@ -1306,8 +1403,6 @@ static int tegra_hdmi_exit(struct host1x_client *client) return err; } - regulator_disable(hdmi->vdd); - return 0; } @@ -1340,7 +1435,16 @@ static const struct tegra_hdmi_config tegra114_hdmi_config = { .has_sor_io_peak_current = true, }; +static const struct tegra_hdmi_config tegra124_hdmi_config = { + .tmds = tegra124_tmds_config, + .num_tmds = ARRAY_SIZE(tegra124_tmds_config), + .fuse_override_offset = HDMI_NV_PDISP_SOR_PAD_CTLS0, + .fuse_override_value = 1 << 31, + .has_sor_io_peak_current = true, +}; + static const struct of_device_id tegra_hdmi_of_match[] = { + { .compatible = "nvidia,tegra124-hdmi", .data = &tegra124_hdmi_config }, { .compatible = "nvidia,tegra114-hdmi", .data = &tegra114_hdmi_config }, { .compatible = "nvidia,tegra30-hdmi", .data = &tegra30_hdmi_config }, { .compatible = "nvidia,tegra20-hdmi", .data = &tegra20_hdmi_config }, @@ -1381,28 +1485,20 @@ static int tegra_hdmi_probe(struct platform_device *pdev) return PTR_ERR(hdmi->rst); } - err = clk_prepare(hdmi->clk); - if (err < 0) - return err; - hdmi->clk_parent = devm_clk_get(&pdev->dev, "parent"); if (IS_ERR(hdmi->clk_parent)) return PTR_ERR(hdmi->clk_parent); - err = clk_prepare(hdmi->clk_parent); - if (err < 0) - return err; - err = clk_set_parent(hdmi->clk, hdmi->clk_parent); if (err < 0) { dev_err(&pdev->dev, "failed to setup clocks: %d\n", err); return err; } - hdmi->vdd = devm_regulator_get(&pdev->dev, "vdd"); - if (IS_ERR(hdmi->vdd)) { - dev_err(&pdev->dev, "failed to get VDD regulator\n"); - return PTR_ERR(hdmi->vdd); + hdmi->hdmi = devm_regulator_get(&pdev->dev, "hdmi"); + if (IS_ERR(hdmi->hdmi)) { + dev_err(&pdev->dev, "failed to get HDMI regulator\n"); + return PTR_ERR(hdmi->hdmi); } hdmi->pll = devm_regulator_get(&pdev->dev, "pll"); @@ -1411,6 +1507,12 @@ static int tegra_hdmi_probe(struct platform_device *pdev) return PTR_ERR(hdmi->pll); } + hdmi->vdd = devm_regulator_get(&pdev->dev, "vdd"); + if (IS_ERR(hdmi->vdd)) { + dev_err(&pdev->dev, "failed to get VDD regulator\n"); + return PTR_ERR(hdmi->vdd); + } + hdmi->output.dev = &pdev->dev; err = tegra_output_probe(&hdmi->output); @@ -1462,8 +1564,8 @@ static int tegra_hdmi_remove(struct platform_device *pdev) return err; } - clk_unprepare(hdmi->clk_parent); - clk_unprepare(hdmi->clk); + clk_disable_unprepare(hdmi->clk_parent); + clk_disable_unprepare(hdmi->clk); return 0; } diff --git a/drivers/gpu/drm/tegra/hdmi.h b/drivers/gpu/drm/tegra/hdmi.h index 0aebc485f7f..919a19df4e1 100644 --- a/drivers/gpu/drm/tegra/hdmi.h +++ b/drivers/gpu/drm/tegra/hdmi.h @@ -190,6 +190,11 @@ #define HDMI_NV_PDISP_SOR_CSTM 0x5a #define SOR_CSTM_ROTCLK(x) (((x) & 0xf) << 24) +#define SOR_CSTM_PLLDIV (1 << 21) +#define SOR_CSTM_LVDS_ENABLE (1 << 16) +#define SOR_CSTM_MODE_LVDS (0 << 12) +#define SOR_CSTM_MODE_TMDS (1 << 12) +#define SOR_CSTM_MODE_MASK (3 << 12) #define HDMI_NV_PDISP_SOR_LVDS 0x5b #define HDMI_NV_PDISP_SOR_CRCA 0x5c diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c index 0266fb40479..d6af9be48f4 100644 --- a/drivers/gpu/drm/tegra/rgb.c +++ b/drivers/gpu/drm/tegra/rgb.c @@ -159,11 +159,38 @@ static int tegra_output_rgb_disable(struct tegra_output *output) } static int tegra_output_rgb_setup_clock(struct tegra_output *output, - struct clk *clk, unsigned long pclk) + struct clk *clk, unsigned long pclk, + unsigned int *div) { struct tegra_rgb *rgb = to_rgb(output); + int err; + + err = clk_set_parent(clk, rgb->clk_parent); + if (err < 0) { + dev_err(output->dev, "failed to set parent: %d\n", err); + return err; + } - return clk_set_parent(clk, rgb->clk_parent); + /* + * We may not want to change the frequency of the parent clock, since + * it may be a parent for other peripherals. This is due to the fact + * that on Tegra20 there's only a single clock dedicated to display + * (pll_d_out0), whereas later generations have a second one that can + * be used to independently drive a second output (pll_d2_out0). + * + * As a way to support multiple outputs on Tegra20 as well, pll_p is + * typically used as the parent clock for the display controllers. + * But this comes at a cost: pll_p is the parent of several other + * peripherals, so its frequency shouldn't change out of the blue. + * + * The best we can do at this point is to use the shift clock divider + * and hope that the desired frequency can be matched (or at least + * matched sufficiently close that the panel will still work). + */ + + *div = ((clk_get_rate(clk) * 2) / pclk) - 2; + + return 0; } static int tegra_output_rgb_check_mode(struct tegra_output *output, diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 49ef5729f43..27c979b5011 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -7,6 +7,7 @@ */ #include <linux/clk.h> +#include <linux/debugfs.h> #include <linux/io.h> #include <linux/platform_device.h> #include <linux/reset.h> @@ -33,7 +34,23 @@ struct tegra_sor { struct tegra_dpaux *dpaux; + struct mutex lock; bool enabled; + + struct dentry *debugfs; +}; + +struct tegra_sor_config { + u32 bits_per_pixel; + + u32 active_polarity; + u32 active_count; + u32 tu_size; + u32 active_frac; + u32 watermark; + + u32 hblank_symbols; + u32 vblank_symbols; }; static inline struct tegra_sor * @@ -289,34 +306,232 @@ static int tegra_sor_power_up(struct tegra_sor *sor, unsigned long timeout) return -ETIMEDOUT; } +struct tegra_sor_params { + /* number of link clocks per line */ + unsigned int num_clocks; + /* ratio between input and output */ + u64 ratio; + /* precision factor */ + u64 precision; + + unsigned int active_polarity; + unsigned int active_count; + unsigned int active_frac; + unsigned int tu_size; + unsigned int error; +}; + +static int tegra_sor_compute_params(struct tegra_sor *sor, + struct tegra_sor_params *params, + unsigned int tu_size) +{ + u64 active_sym, active_count, frac, approx; + u32 active_polarity, active_frac = 0; + const u64 f = params->precision; + s64 error; + + active_sym = params->ratio * tu_size; + active_count = div_u64(active_sym, f) * f; + frac = active_sym - active_count; + + /* fraction < 0.5 */ + if (frac >= (f / 2)) { + active_polarity = 1; + frac = f - frac; + } else { + active_polarity = 0; + } + + if (frac != 0) { + frac = div_u64(f * f, frac); /* 1/fraction */ + if (frac <= (15 * f)) { + active_frac = div_u64(frac, f); + + /* round up */ + if (active_polarity) + active_frac++; + } else { + active_frac = active_polarity ? 1 : 15; + } + } + + if (active_frac == 1) + active_polarity = 0; + + if (active_polarity == 1) { + if (active_frac) { + approx = active_count + (active_frac * (f - 1)) * f; + approx = div_u64(approx, active_frac * f); + } else { + approx = active_count + f; + } + } else { + if (active_frac) + approx = active_count + div_u64(f, active_frac); + else + approx = active_count; + } + + error = div_s64(active_sym - approx, tu_size); + error *= params->num_clocks; + + if (error <= 0 && abs64(error) < params->error) { + params->active_count = div_u64(active_count, f); + params->active_polarity = active_polarity; + params->active_frac = active_frac; + params->error = abs64(error); + params->tu_size = tu_size; + + if (error == 0) + return true; + } + + return false; +} + +static int tegra_sor_calc_config(struct tegra_sor *sor, + struct drm_display_mode *mode, + struct tegra_sor_config *config, + struct drm_dp_link *link) +{ + const u64 f = 100000, link_rate = link->rate * 1000; + const u64 pclk = mode->clock * 1000; + u64 input, output, watermark, num; + struct tegra_sor_params params; + u32 num_syms_per_line; + unsigned int i; + + if (!link_rate || !link->num_lanes || !pclk || !config->bits_per_pixel) + return -EINVAL; + + output = link_rate * 8 * link->num_lanes; + input = pclk * config->bits_per_pixel; + + if (input >= output) + return -ERANGE; + + memset(¶ms, 0, sizeof(params)); + params.ratio = div64_u64(input * f, output); + params.num_clocks = div_u64(link_rate * mode->hdisplay, pclk); + params.precision = f; + params.error = 64 * f; + params.tu_size = 64; + + for (i = params.tu_size; i >= 32; i--) + if (tegra_sor_compute_params(sor, ¶ms, i)) + break; + + if (params.active_frac == 0) { + config->active_polarity = 0; + config->active_count = params.active_count; + + if (!params.active_polarity) + config->active_count--; + + config->tu_size = params.tu_size; + config->active_frac = 1; + } else { + config->active_polarity = params.active_polarity; + config->active_count = params.active_count; + config->active_frac = params.active_frac; + config->tu_size = params.tu_size; + } + + dev_dbg(sor->dev, + "polarity: %d active count: %d tu size: %d active frac: %d\n", + config->active_polarity, config->active_count, + config->tu_size, config->active_frac); + + watermark = params.ratio * config->tu_size * (f - params.ratio); + watermark = div_u64(watermark, f); + + watermark = div_u64(watermark + params.error, f); + config->watermark = watermark + (config->bits_per_pixel / 8) + 2; + num_syms_per_line = (mode->hdisplay * config->bits_per_pixel) * + (link->num_lanes * 8); + + if (config->watermark > 30) { + config->watermark = 30; + dev_err(sor->dev, + "unable to compute TU size, forcing watermark to %u\n", + config->watermark); + } else if (config->watermark > num_syms_per_line) { + config->watermark = num_syms_per_line; + dev_err(sor->dev, "watermark too high, forcing to %u\n", + config->watermark); + } + + /* compute the number of symbols per horizontal blanking interval */ + num = ((mode->htotal - mode->hdisplay) - 7) * link_rate; + config->hblank_symbols = div_u64(num, pclk); + + if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING) + config->hblank_symbols -= 3; + + config->hblank_symbols -= 12 / link->num_lanes; + + /* compute the number of symbols per vertical blanking interval */ + num = (mode->hdisplay - 25) * link_rate; + config->vblank_symbols = div_u64(num, pclk); + config->vblank_symbols -= 36 / link->num_lanes + 4; + + dev_dbg(sor->dev, "blank symbols: H:%u V:%u\n", config->hblank_symbols, + config->vblank_symbols); + + return 0; +} + static int tegra_output_sor_enable(struct tegra_output *output) { struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); struct drm_display_mode *mode = &dc->base.mode; unsigned int vbe, vse, hbe, hse, vbs, hbs, i; struct tegra_sor *sor = to_sor(output); + struct tegra_sor_config config; + struct drm_dp_link link; + struct drm_dp_aux *aux; unsigned long value; - int err; + int err = 0; + + mutex_lock(&sor->lock); if (sor->enabled) - return 0; + goto unlock; err = clk_prepare_enable(sor->clk); if (err < 0) - return err; + goto unlock; reset_control_deassert(sor->rst); + /* FIXME: properly convert to struct drm_dp_aux */ + aux = (struct drm_dp_aux *)sor->dpaux; + if (sor->dpaux) { err = tegra_dpaux_enable(sor->dpaux); if (err < 0) dev_err(sor->dev, "failed to enable DP: %d\n", err); + + err = drm_dp_link_probe(aux, &link); + if (err < 0) { + dev_err(sor->dev, "failed to probe eDP link: %d\n", + err); + return err; + } } err = clk_set_parent(sor->clk, sor->clk_safe); if (err < 0) dev_err(sor->dev, "failed to set safe parent clock: %d\n", err); + memset(&config, 0, sizeof(config)); + config.bits_per_pixel = 24; /* XXX: don't hardcode? */ + + err = tegra_sor_calc_config(sor, mode, &config, &link); + if (err < 0) + dev_err(sor->dev, "failed to compute link configuration: %d\n", + err); + value = tegra_sor_readl(sor, SOR_CLK_CNTRL); value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK; value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK; @@ -385,7 +600,7 @@ static int tegra_output_sor_enable(struct tegra_output *output) err = tegra_io_rail_power_on(TEGRA_IO_RAIL_LVDS); if (err < 0) { dev_err(sor->dev, "failed to power on I/O rail: %d\n", err); - return err; + goto unlock; } usleep_range(5, 100); @@ -419,15 +634,29 @@ static int tegra_output_sor_enable(struct tegra_output *output) if (err < 0) dev_err(sor->dev, "failed to set DP parent clock: %d\n", err); - /* power dplanes (XXX parameterize based on link?) */ + /* power DP lanes */ value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); - value |= SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 | - SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2; + + if (link.num_lanes <= 2) + value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_2); + else + value |= SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_2; + + if (link.num_lanes <= 1) + value &= ~SOR_DP_PADCTL_PD_TXD_1; + else + value |= SOR_DP_PADCTL_PD_TXD_1; + + if (link.num_lanes == 0) + value &= ~SOR_DP_PADCTL_PD_TXD_0; + else + value |= SOR_DP_PADCTL_PD_TXD_0; + tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); value = tegra_sor_readl(sor, SOR_DP_LINKCTL_0); value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK; - value |= SOR_DP_LINKCTL_LANE_COUNT(4); + value |= SOR_DP_LINKCTL_LANE_COUNT(link.num_lanes); tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0); /* start lane sequencer */ @@ -443,10 +672,10 @@ static int tegra_output_sor_enable(struct tegra_output *output) usleep_range(250, 1000); } - /* set link bandwidth (2.7 GHz, XXX: parameterize based on link?) */ + /* set link bandwidth */ value = tegra_sor_readl(sor, SOR_CLK_CNTRL); value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK; - value |= SOR_CLK_CNTRL_DP_LINK_SPEED_G2_70; + value |= drm_dp_link_rate_to_bw_code(link.rate) << 2; tegra_sor_writel(sor, value, SOR_CLK_CNTRL); /* set linkctl */ @@ -454,7 +683,7 @@ static int tegra_output_sor_enable(struct tegra_output *output) value |= SOR_DP_LINKCTL_ENABLE; value &= ~SOR_DP_LINKCTL_TU_SIZE_MASK; - value |= SOR_DP_LINKCTL_TU_SIZE(59); /* XXX: don't hardcode? */ + value |= SOR_DP_LINKCTL_TU_SIZE(config.tu_size); value |= SOR_DP_LINKCTL_ENHANCED_FRAME; tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0); @@ -470,28 +699,31 @@ static int tegra_output_sor_enable(struct tegra_output *output) value = tegra_sor_readl(sor, SOR_DP_CONFIG_0); value &= ~SOR_DP_CONFIG_WATERMARK_MASK; - value |= SOR_DP_CONFIG_WATERMARK(14); /* XXX: don't hardcode? */ + value |= SOR_DP_CONFIG_WATERMARK(config.watermark); value &= ~SOR_DP_CONFIG_ACTIVE_SYM_COUNT_MASK; - value |= SOR_DP_CONFIG_ACTIVE_SYM_COUNT(47); /* XXX: don't hardcode? */ + value |= SOR_DP_CONFIG_ACTIVE_SYM_COUNT(config.active_count); value &= ~SOR_DP_CONFIG_ACTIVE_SYM_FRAC_MASK; - value |= SOR_DP_CONFIG_ACTIVE_SYM_FRAC(9); /* XXX: don't hardcode? */ + value |= SOR_DP_CONFIG_ACTIVE_SYM_FRAC(config.active_frac); - value &= ~SOR_DP_CONFIG_ACTIVE_SYM_POLARITY; /* XXX: don't hardcode? */ + if (config.active_polarity) + value |= SOR_DP_CONFIG_ACTIVE_SYM_POLARITY; + else + value &= ~SOR_DP_CONFIG_ACTIVE_SYM_POLARITY; value |= SOR_DP_CONFIG_ACTIVE_SYM_ENABLE; - value |= SOR_DP_CONFIG_DISPARITY_NEGATIVE; /* XXX: don't hardcode? */ + value |= SOR_DP_CONFIG_DISPARITY_NEGATIVE; tegra_sor_writel(sor, value, SOR_DP_CONFIG_0); value = tegra_sor_readl(sor, SOR_DP_AUDIO_HBLANK_SYMBOLS); value &= ~SOR_DP_AUDIO_HBLANK_SYMBOLS_MASK; - value |= 137; /* XXX: don't hardcode? */ + value |= config.hblank_symbols & 0xffff; tegra_sor_writel(sor, value, SOR_DP_AUDIO_HBLANK_SYMBOLS); value = tegra_sor_readl(sor, SOR_DP_AUDIO_VBLANK_SYMBOLS); value &= ~SOR_DP_AUDIO_VBLANK_SYMBOLS_MASK; - value |= 2368; /* XXX: don't hardcode? */ + value |= config.vblank_symbols & 0xffff; tegra_sor_writel(sor, value, SOR_DP_AUDIO_VBLANK_SYMBOLS); /* enable pad calibration logic */ @@ -500,30 +732,27 @@ static int tegra_output_sor_enable(struct tegra_output *output) tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); if (sor->dpaux) { - /* FIXME: properly convert to struct drm_dp_aux */ - struct drm_dp_aux *aux = (struct drm_dp_aux *)sor->dpaux; - struct drm_dp_link link; u8 rate, lanes; err = drm_dp_link_probe(aux, &link); if (err < 0) { dev_err(sor->dev, "failed to probe eDP link: %d\n", err); - return err; + goto unlock; } err = drm_dp_link_power_up(aux, &link); if (err < 0) { dev_err(sor->dev, "failed to power up eDP link: %d\n", err); - return err; + goto unlock; } err = drm_dp_link_configure(aux, &link); if (err < 0) { dev_err(sor->dev, "failed to configure eDP link: %d\n", err); - return err; + goto unlock; } rate = drm_dp_link_rate_to_bw_code(link.rate); @@ -558,7 +787,7 @@ static int tegra_output_sor_enable(struct tegra_output *output) if (err < 0) { dev_err(sor->dev, "DP fast link training failed: %d\n", err); - return err; + goto unlock; } dev_dbg(sor->dev, "fast link training succeeded\n"); @@ -567,7 +796,7 @@ static int tegra_output_sor_enable(struct tegra_output *output) err = tegra_sor_power_up(sor, 250); if (err < 0) { dev_err(sor->dev, "failed to power up SOR: %d\n", err); - return err; + goto unlock; } /* start display controller in continuous mode */ @@ -586,12 +815,26 @@ static int tegra_output_sor_enable(struct tegra_output *output) * configure panel (24bpp, vsync-, hsync-, DP-A protocol, complete * raster, associate with display controller) */ - value = SOR_STATE_ASY_PIXELDEPTH_BPP_24_444 | - SOR_STATE_ASY_VSYNCPOL | + value = SOR_STATE_ASY_VSYNCPOL | SOR_STATE_ASY_HSYNCPOL | SOR_STATE_ASY_PROTOCOL_DP_A | SOR_STATE_ASY_CRC_MODE_COMPLETE | SOR_STATE_ASY_OWNER(dc->pipe + 1); + + switch (config.bits_per_pixel) { + case 24: + value |= SOR_STATE_ASY_PIXELDEPTH_BPP_24_444; + break; + + case 18: + value |= SOR_STATE_ASY_PIXELDEPTH_BPP_18_444; + break; + + default: + BUG(); + break; + } + tegra_sor_writel(sor, value, SOR_STATE_1); /* @@ -620,11 +863,8 @@ static int tegra_output_sor_enable(struct tegra_output *output) value = ((vbs & 0x7fff) << 16) | (hbs & 0x7fff); tegra_sor_writel(sor, value, SOR_HEAD_STATE_4(0)); - /* XXX interlaced mode */ - tegra_sor_writel(sor, 0x00000001, SOR_HEAD_STATE_5(0)); - /* CSTM (LVDS, link A/B, upper) */ - value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_B | SOR_CSTM_LINK_ACT_B | + value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_A | SOR_CSTM_LINK_ACT_B | SOR_CSTM_UPPER; tegra_sor_writel(sor, value, SOR_CSTM); @@ -632,7 +872,7 @@ static int tegra_output_sor_enable(struct tegra_output *output) err = tegra_sor_setup_pwm(sor, 250); if (err < 0) { dev_err(sor->dev, "failed to setup PWM: %d\n", err); - return err; + goto unlock; } value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); @@ -644,18 +884,20 @@ static int tegra_output_sor_enable(struct tegra_output *output) err = tegra_sor_attach(sor); if (err < 0) { dev_err(sor->dev, "failed to attach SOR: %d\n", err); - return err; + goto unlock; } err = tegra_sor_wakeup(sor); if (err < 0) { dev_err(sor->dev, "failed to enable DC: %d\n", err); - return err; + goto unlock; } sor->enabled = true; - return 0; +unlock: + mutex_unlock(&sor->lock); + return err; } static int tegra_sor_detach(struct tegra_sor *sor) @@ -740,7 +982,7 @@ static int tegra_sor_power_down(struct tegra_sor *sor) tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); /* stop lane sequencer */ - value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_DOWN | + value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_UP | SOR_LANE_SEQ_CTL_POWER_STATE_DOWN; tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL); @@ -783,15 +1025,17 @@ static int tegra_output_sor_disable(struct tegra_output *output) struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); struct tegra_sor *sor = to_sor(output); unsigned long value; - int err; + int err = 0; + + mutex_lock(&sor->lock); if (!sor->enabled) - return 0; + goto unlock; err = tegra_sor_detach(sor); if (err < 0) { dev_err(sor->dev, "failed to detach SOR: %d\n", err); - return err; + goto unlock; } tegra_sor_writel(sor, 0, SOR_STATE_1); @@ -832,21 +1076,21 @@ static int tegra_output_sor_disable(struct tegra_output *output) err = tegra_sor_power_down(sor); if (err < 0) { dev_err(sor->dev, "failed to power down SOR: %d\n", err); - return err; + goto unlock; } if (sor->dpaux) { err = tegra_dpaux_disable(sor->dpaux); if (err < 0) { dev_err(sor->dev, "failed to disable DP: %d\n", err); - return err; + goto unlock; } } err = tegra_io_rail_power_off(TEGRA_IO_RAIL_LVDS); if (err < 0) { dev_err(sor->dev, "failed to power off I/O rail: %d\n", err); - return err; + goto unlock; } reset_control_assert(sor->rst); @@ -854,18 +1098,18 @@ static int tegra_output_sor_disable(struct tegra_output *output) sor->enabled = false; - return 0; +unlock: + mutex_unlock(&sor->lock); + return err; } static int tegra_output_sor_setup_clock(struct tegra_output *output, - struct clk *clk, unsigned long pclk) + struct clk *clk, unsigned long pclk, + unsigned int *div) { struct tegra_sor *sor = to_sor(output); int err; - /* round to next MHz */ - pclk = DIV_ROUND_UP(pclk / 2, 1000000) * 1000000; - err = clk_set_parent(clk, sor->clk_parent); if (err < 0) { dev_err(sor->dev, "failed to set parent clock: %d\n", err); @@ -874,11 +1118,12 @@ static int tegra_output_sor_setup_clock(struct tegra_output *output, err = clk_set_rate(sor->clk_parent, pclk); if (err < 0) { - dev_err(sor->dev, "failed to set base clock rate to %lu Hz\n", - pclk * 2); + dev_err(sor->dev, "failed to set clock rate to %lu Hz\n", pclk); return err; } + *div = 0; + return 0; } @@ -914,9 +1159,124 @@ static const struct tegra_output_ops sor_ops = { .detect = tegra_output_sor_detect, }; +static int tegra_sor_crc_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + + return 0; +} + +static int tegra_sor_crc_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static int tegra_sor_crc_wait(struct tegra_sor *sor, unsigned long timeout) +{ + u32 value; + + timeout = jiffies + msecs_to_jiffies(timeout); + + while (time_before(jiffies, timeout)) { + value = tegra_sor_readl(sor, SOR_CRC_A); + if (value & SOR_CRC_A_VALID) + return 0; + + usleep_range(100, 200); + } + + return -ETIMEDOUT; +} + +static ssize_t tegra_sor_crc_read(struct file *file, char __user *buffer, + size_t size, loff_t *ppos) +{ + struct tegra_sor *sor = file->private_data; + ssize_t num, err; + char buf[10]; + u32 value; + + mutex_lock(&sor->lock); + + if (!sor->enabled) { + err = -EAGAIN; + goto unlock; + } + + value = tegra_sor_readl(sor, SOR_STATE_1); + value &= ~SOR_STATE_ASY_CRC_MODE_MASK; + tegra_sor_writel(sor, value, SOR_STATE_1); + + value = tegra_sor_readl(sor, SOR_CRC_CNTRL); + value |= SOR_CRC_CNTRL_ENABLE; + tegra_sor_writel(sor, value, SOR_CRC_CNTRL); + + value = tegra_sor_readl(sor, SOR_TEST); + value &= ~SOR_TEST_CRC_POST_SERIALIZE; + tegra_sor_writel(sor, value, SOR_TEST); + + err = tegra_sor_crc_wait(sor, 100); + if (err < 0) + goto unlock; + + tegra_sor_writel(sor, SOR_CRC_A_RESET, SOR_CRC_A); + value = tegra_sor_readl(sor, SOR_CRC_B); + + num = scnprintf(buf, sizeof(buf), "%08x\n", value); + + err = simple_read_from_buffer(buffer, size, ppos, buf, num); + +unlock: + mutex_unlock(&sor->lock); + return err; +} + +static const struct file_operations tegra_sor_crc_fops = { + .owner = THIS_MODULE, + .open = tegra_sor_crc_open, + .read = tegra_sor_crc_read, + .release = tegra_sor_crc_release, +}; + +static int tegra_sor_debugfs_init(struct tegra_sor *sor, + struct drm_minor *minor) +{ + struct dentry *entry; + int err = 0; + + sor->debugfs = debugfs_create_dir("sor", minor->debugfs_root); + if (!sor->debugfs) + return -ENOMEM; + + entry = debugfs_create_file("crc", 0644, sor->debugfs, sor, + &tegra_sor_crc_fops); + if (!entry) { + dev_err(sor->dev, + "cannot create /sys/kernel/debug/dri/%s/sor/crc\n", + minor->debugfs_root->d_name.name); + err = -ENOMEM; + goto remove; + } + + return err; + +remove: + debugfs_remove(sor->debugfs); + sor->debugfs = NULL; + return err; +} + +static int tegra_sor_debugfs_exit(struct tegra_sor *sor) +{ + debugfs_remove_recursive(sor->debugfs); + sor->debugfs = NULL; + + return 0; +} + static int tegra_sor_init(struct host1x_client *client) { - struct tegra_drm *tegra = dev_get_drvdata(client->parent); + struct drm_device *drm = dev_get_drvdata(client->parent); struct tegra_sor *sor = host1x_client_to_sor(client); int err; @@ -928,12 +1288,18 @@ static int tegra_sor_init(struct host1x_client *client) sor->output.dev = sor->dev; sor->output.ops = &sor_ops; - err = tegra_output_init(tegra->drm, &sor->output); + err = tegra_output_init(drm, &sor->output); if (err < 0) { dev_err(sor->dev, "output setup failed: %d\n", err); return err; } + if (IS_ENABLED(CONFIG_DEBUG_FS)) { + err = tegra_sor_debugfs_init(sor, drm->primary); + if (err < 0) + dev_err(sor->dev, "debugfs setup failed: %d\n", err); + } + if (sor->dpaux) { err = tegra_dpaux_attach(sor->dpaux, &sor->output); if (err < 0) { @@ -964,6 +1330,12 @@ static int tegra_sor_exit(struct host1x_client *client) } } + if (IS_ENABLED(CONFIG_DEBUG_FS)) { + err = tegra_sor_debugfs_exit(sor); + if (err < 0) + dev_err(sor->dev, "debugfs cleanup failed: %d\n", err); + } + err = tegra_output_exit(&sor->output); if (err < 0) { dev_err(sor->dev, "output cleanup failed: %d\n", err); @@ -1045,6 +1417,8 @@ static int tegra_sor_probe(struct platform_device *pdev) sor->client.ops = &sor_client_ops; sor->client.dev = &pdev->dev; + mutex_init(&sor->lock); + err = host1x_client_register(&sor->client); if (err < 0) { dev_err(&pdev->dev, "failed to register host1x client: %d\n", diff --git a/drivers/gpu/drm/tegra/sor.h b/drivers/gpu/drm/tegra/sor.h index f4156d54cd0..a5f8853fedb 100644 --- a/drivers/gpu/drm/tegra/sor.h +++ b/drivers/gpu/drm/tegra/sor.h @@ -47,6 +47,7 @@ #define SOR_HEAD_STATE_4(x) (0x0d + (x)) #define SOR_HEAD_STATE_5(x) (0x0f + (x)) #define SOR_CRC_CNTRL 0x11 +#define SOR_CRC_CNTRL_ENABLE (1 << 0) #define SOR_DP_DEBUG_MVID 0x12 #define SOR_CLK_CNTRL 0x13 @@ -69,6 +70,7 @@ #define SOR_PWR_NORMAL_STATE_PU (1 << 0) #define SOR_TEST 0x16 +#define SOR_TEST_CRC_POST_SERIALIZE (1 << 23) #define SOR_TEST_ATTACHED (1 << 10) #define SOR_TEST_HEAD_MODE_MASK (3 << 8) #define SOR_TEST_HEAD_MODE_AWAKE (2 << 8) @@ -115,6 +117,8 @@ #define SOR_LVDS 0x1c #define SOR_CRC_A 0x1d +#define SOR_CRC_A_VALID (1 << 0) +#define SOR_CRC_A_RESET (1 << 0) #define SOR_CRC_B 0x1e #define SOR_BLANK 0x1f #define SOR_SEQ_CTL 0x20 diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c index e1038a945f4..7094b92d1ec 100644 --- a/drivers/gpu/drm/udl/udl_main.c +++ b/drivers/gpu/drm/udl/udl_main.c @@ -283,7 +283,7 @@ int udl_submit_urb(struct drm_device *dev, struct urb *urb, size_t len) int udl_driver_load(struct drm_device *dev, unsigned long flags) { struct udl_device *udl; - int ret; + int ret = -ENOMEM; DRM_DEBUG("\n"); udl = kzalloc(sizeof(struct udl_device), GFP_KERNEL); @@ -300,7 +300,6 @@ int udl_driver_load(struct drm_device *dev, unsigned long flags) } if (!udl_alloc_urb_list(dev, WRITES_IN_FLIGHT, MAX_TRANSFER)) { - ret = -ENOMEM; DRM_ERROR("udl_alloc_urb_list failed\n"); goto err; } diff --git a/drivers/gpu/drm/via/via_dma.c b/drivers/gpu/drm/via/via_dma.c index a18479c6b6d..6fc0648dd37 100644 --- a/drivers/gpu/drm/via/via_dma.c +++ b/drivers/gpu/drm/via/via_dma.c @@ -737,4 +737,4 @@ const struct drm_ioctl_desc via_ioctls[] = { DRM_IOCTL_DEF_DRV(VIA_BLIT_SYNC, via_dma_blit_sync, DRM_AUTH) }; -int via_max_ioctl = DRM_ARRAY_SIZE(via_ioctls); +int via_max_ioctl = ARRAY_SIZE(via_ioctls); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 6bdd15eea7e..246a62bab37 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -1417,7 +1417,7 @@ static struct drm_driver driver = { .enable_vblank = vmw_enable_vblank, .disable_vblank = vmw_disable_vblank, .ioctls = vmw_ioctls, - .num_ioctls = DRM_ARRAY_SIZE(vmw_ioctls), + .num_ioctls = ARRAY_SIZE(vmw_ioctls), .master_create = vmw_master_create, .master_destroy = vmw_master_destroy, .master_set = vmw_master_set, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c index a89ad938eac..b031b48dbb3 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c @@ -179,7 +179,6 @@ static int vmw_fb_set_par(struct fb_info *info) vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_Y, info->var.yoffset); vmw_write(vmw_priv, SVGA_REG_DISPLAY_WIDTH, info->var.xres); vmw_write(vmw_priv, SVGA_REG_DISPLAY_HEIGHT, info->var.yres); - vmw_write(vmw_priv, SVGA_REG_BYTES_PER_LINE, info->fix.line_length); vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID); } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index e7199b454ca..8f3edc4710f 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -187,7 +187,7 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, * can do this since the caller in the drm core doesn't check anything * which is protected by any looks. */ - mutex_unlock(&crtc->mutex); + drm_modeset_unlock(&crtc->mutex); drm_modeset_lock_all(dev_priv->dev); /* A lot of the code assumes this */ @@ -252,7 +252,7 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, ret = 0; out: drm_modeset_unlock_all(dev_priv->dev); - mutex_lock(&crtc->mutex); + drm_modeset_lock(&crtc->mutex, NULL); return ret; } @@ -273,7 +273,7 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) * can do this since the caller in the drm core doesn't check anything * which is protected by any looks. */ - mutex_unlock(&crtc->mutex); + drm_modeset_unlock(&crtc->mutex); drm_modeset_lock_all(dev_priv->dev); vmw_cursor_update_position(dev_priv, shown, @@ -281,7 +281,7 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) du->cursor_y + du->hotspot_y); drm_modeset_unlock_all(dev_priv->dev); - mutex_lock(&crtc->mutex); + drm_modeset_lock(&crtc->mutex, NULL); return 0; } |
