diff options
Diffstat (limited to 'drivers/gpu')
301 files changed, 10668 insertions, 4995 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index ee38a3db1890..0bc374459440 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -7,6 +7,7 @@ menuconfig DRM tristate "Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)" depends on (AGP || AGP=n) && !EMULATED_CMPXCHG && HAS_DMA + select DRM_PANEL_ORIENTATION_QUIRKS select HDMI select FB_CMDLINE select I2C @@ -26,6 +27,10 @@ config DRM_MIPI_DSI bool depends on DRM +# Separate option because drm_panel_orientation_quirks.c is shared with fbdev +config DRM_PANEL_ORIENTATION_QUIRKS + tristate + config DRM_DP_AUX_CHARDEV bool "DRM DP AUX Interface" depends on DRM diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 1f6ba9e34e31..dd5ae67f8e2b 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_DRM_DEBUG_MM_SELFTEST) += selftests/ obj-$(CONFIG_DRM) += drm.o obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o +obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) += drm_panel_orientation_quirks.o obj-$(CONFIG_DRM_ARM) += arm/ obj-$(CONFIG_DRM_TTM) += ttm/ obj-$(CONFIG_DRM_TDFX) += tdfx/ diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c index 91f2b0191368..3d82712d8002 100644 --- a/drivers/gpu/drm/arm/malidp_drv.c +++ b/drivers/gpu/drm/arm/malidp_drv.c @@ -13,7 +13,6 @@ #include <linux/module.h> #include <linux/clk.h> #include <linux/component.h> -#include <linux/console.h> #include <linux/of_device.h> #include <linux/of_graph.h> #include <linux/of_reserved_mem.h> @@ -24,9 +23,11 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_helper.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_modeset_helper.h> #include <drm/drm_of.h> #include "malidp_drv.h" @@ -182,13 +183,6 @@ static int malidp_set_and_wait_config_valid(struct drm_device *drm) return (ret > 0) ? 0 : -ETIMEDOUT; } -static void malidp_output_poll_changed(struct drm_device *drm) -{ - struct malidp_drm *malidp = drm->dev_private; - - drm_fbdev_cma_hotplug_event(malidp->fbdev); -} - static void malidp_atomic_commit_hw_done(struct drm_atomic_state *state) { struct drm_pending_vblank_event *event; @@ -251,7 +245,7 @@ static const struct drm_mode_config_helper_funcs malidp_mode_config_helpers = { static const struct drm_mode_config_funcs malidp_mode_config_funcs = { .fb_create = drm_gem_fb_create, - .output_poll_changed = malidp_output_poll_changed, + .output_poll_changed = drm_fb_helper_output_poll_changed, .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, }; @@ -316,19 +310,12 @@ static int malidp_irq_init(struct platform_device *pdev) return 0; } -static void malidp_lastclose(struct drm_device *drm) -{ - struct malidp_drm *malidp = drm->dev_private; - - drm_fbdev_cma_restore_mode(malidp->fbdev); -} - DEFINE_DRM_GEM_CMA_FOPS(fops); static struct drm_driver malidp_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_PRIME, - .lastclose = malidp_lastclose, + .lastclose = drm_fb_helper_lastclose, .gem_free_object_unlocked = drm_gem_cma_free_object, .gem_vm_ops = &drm_gem_cma_vm_ops, .dumb_create = drm_gem_cma_dumb_create, @@ -622,14 +609,9 @@ static int malidp_bind(struct device *dev) drm_mode_config_reset(drm); - malidp->fbdev = drm_fbdev_cma_init(drm, 32, - drm->mode_config.num_connector); - - if (IS_ERR(malidp->fbdev)) { - ret = PTR_ERR(malidp->fbdev); - malidp->fbdev = NULL; + ret = drm_fb_cma_fbdev_init(drm, 32, 0); + if (ret) goto fbdev_fail; - } drm_kms_helper_poll_init(drm); @@ -640,10 +622,7 @@ static int malidp_bind(struct device *dev) return 0; register_fail: - if (malidp->fbdev) { - drm_fbdev_cma_fini(malidp->fbdev); - malidp->fbdev = NULL; - } + drm_fb_cma_fbdev_fini(drm); drm_kms_helper_poll_fini(drm); fbdev_fail: pm_runtime_get_sync(dev); @@ -680,10 +659,7 @@ static void malidp_unbind(struct device *dev) struct malidp_drm *malidp = drm->dev_private; drm_dev_unregister(drm); - if (malidp->fbdev) { - drm_fbdev_cma_fini(malidp->fbdev); - malidp->fbdev = NULL; - } + drm_fb_cma_fbdev_fini(drm); drm_kms_helper_poll_fini(drm); pm_runtime_get_sync(dev); malidp_se_irq_fini(drm); @@ -745,34 +721,15 @@ static int malidp_platform_remove(struct platform_device *pdev) static int __maybe_unused malidp_pm_suspend(struct device *dev) { struct drm_device *drm = dev_get_drvdata(dev); - struct malidp_drm *malidp = drm->dev_private; - - drm_kms_helper_poll_disable(drm); - console_lock(); - drm_fbdev_cma_set_suspend(malidp->fbdev, 1); - console_unlock(); - malidp->pm_state = drm_atomic_helper_suspend(drm); - if (IS_ERR(malidp->pm_state)) { - console_lock(); - drm_fbdev_cma_set_suspend(malidp->fbdev, 0); - console_unlock(); - drm_kms_helper_poll_enable(drm); - return PTR_ERR(malidp->pm_state); - } - return 0; + return drm_mode_config_helper_suspend(drm); } static int __maybe_unused malidp_pm_resume(struct device *dev) { struct drm_device *drm = dev_get_drvdata(dev); - struct malidp_drm *malidp = drm->dev_private; - drm_atomic_helper_resume(drm, malidp->pm_state); - console_lock(); - drm_fbdev_cma_set_suspend(malidp->fbdev, 0); - console_unlock(); - drm_kms_helper_poll_enable(drm); + drm_mode_config_helper_resume(drm); return 0; } diff --git a/drivers/gpu/drm/arm/malidp_drv.h b/drivers/gpu/drm/arm/malidp_drv.h index 2e2033140efc..e0d12c9fc6b8 100644 --- a/drivers/gpu/drm/arm/malidp_drv.h +++ b/drivers/gpu/drm/arm/malidp_drv.h @@ -20,11 +20,9 @@ struct malidp_drm { struct malidp_hw_device *dev; - struct drm_fbdev_cma *fbdev; struct drm_crtc crtc; wait_queue_head_t wq; atomic_t config_valid; - struct drm_atomic_state *pm_state; u32 core_id; }; diff --git a/drivers/gpu/drm/armada/armada_drm.h b/drivers/gpu/drm/armada/armada_drm.h index b064879ecdbd..cc4c557c9f66 100644 --- a/drivers/gpu/drm/armada/armada_drm.h +++ b/drivers/gpu/drm/armada/armada_drm.h @@ -84,7 +84,6 @@ void armada_drm_queue_unref_work(struct drm_device *, extern const struct drm_mode_config_funcs armada_drm_mode_config_funcs; int armada_fbdev_init(struct drm_device *); -void armada_fbdev_lastclose(struct drm_device *); void armada_fbdev_fini(struct drm_device *); int armada_overlay_plane_create(struct drm_device *, unsigned long); diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index e857b88a9799..4b11b6b52f1d 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -10,6 +10,7 @@ #include <linux/module.h> #include <linux/of_graph.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_helper.h> #include <drm/drm_of.h> #include "armada_crtc.h" #include "armada_drm.h" @@ -54,15 +55,10 @@ static struct drm_ioctl_desc armada_ioctls[] = { DRM_IOCTL_DEF_DRV(ARMADA_GEM_PWRITE, armada_gem_pwrite_ioctl, 0), }; -static void armada_drm_lastclose(struct drm_device *dev) -{ - armada_fbdev_lastclose(dev); -} - DEFINE_DRM_GEM_FOPS(armada_drm_fops); static struct drm_driver armada_drm_driver = { - .lastclose = armada_drm_lastclose, + .lastclose = drm_fb_helper_lastclose, .gem_free_object_unlocked = armada_gem_free_object, .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, diff --git a/drivers/gpu/drm/armada/armada_fb.c b/drivers/gpu/drm/armada/armada_fb.c index a38d5a0892a9..ac92bce07ecd 100644 --- a/drivers/gpu/drm/armada/armada_fb.c +++ b/drivers/gpu/drm/armada/armada_fb.c @@ -154,16 +154,7 @@ static struct drm_framebuffer *armada_fb_create(struct drm_device *dev, return ERR_PTR(ret); } -static void armada_output_poll_changed(struct drm_device *dev) -{ - struct armada_private *priv = dev->dev_private; - struct drm_fb_helper *fbh = priv->fbdev; - - if (fbh) - drm_fb_helper_hotplug_event(fbh); -} - const struct drm_mode_config_funcs armada_drm_mode_config_funcs = { .fb_create = armada_fb_create, - .output_poll_changed = armada_output_poll_changed, + .output_poll_changed = drm_fb_helper_output_poll_changed, }; diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c index a2ce83f84800..2a59db0994b2 100644 --- a/drivers/gpu/drm/armada/armada_fbdev.c +++ b/drivers/gpu/drm/armada/armada_fbdev.c @@ -159,14 +159,6 @@ int armada_fbdev_init(struct drm_device *dev) return ret; } -void armada_fbdev_lastclose(struct drm_device *dev) -{ - struct armada_private *priv = dev->dev_private; - - if (priv->fbdev) - drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev); -} - void armada_fbdev_fini(struct drm_device *dev) { struct armada_private *priv = dev->dev_private; diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index c6e8061ffcfc..c1ea5c36b006 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -461,13 +461,6 @@ static struct drm_framebuffer *atmel_hlcdc_fb_create(struct drm_device *dev, return drm_gem_fb_create(dev, file_priv, mode_cmd); } -static void atmel_hlcdc_fb_output_poll_changed(struct drm_device *dev) -{ - struct atmel_hlcdc_dc *dc = dev->dev_private; - - drm_fbdev_cma_hotplug_event(dc->fbdev); -} - struct atmel_hlcdc_dc_commit { struct work_struct work; struct drm_device *dev; @@ -563,7 +556,7 @@ error: static const struct drm_mode_config_funcs mode_config_funcs = { .fb_create = atmel_hlcdc_fb_create, - .output_poll_changed = atmel_hlcdc_fb_output_poll_changed, + .output_poll_changed = drm_fb_helper_output_poll_changed, .atomic_check = drm_atomic_helper_check, .atomic_commit = atmel_hlcdc_dc_atomic_commit, }; @@ -665,10 +658,7 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev) platform_set_drvdata(pdev, dev); - dc->fbdev = drm_fbdev_cma_init(dev, 24, - dev->mode_config.num_connector); - if (IS_ERR(dc->fbdev)) - dc->fbdev = NULL; + drm_fb_cma_fbdev_init(dev, 24, 0); drm_kms_helper_poll_init(dev); @@ -688,8 +678,7 @@ static void atmel_hlcdc_dc_unload(struct drm_device *dev) { struct atmel_hlcdc_dc *dc = dev->dev_private; - if (dc->fbdev) - drm_fbdev_cma_fini(dc->fbdev); + drm_fb_cma_fbdev_fini(dev); flush_workqueue(dc->wq); drm_kms_helper_poll_fini(dev); drm_mode_config_cleanup(dev); @@ -705,13 +694,6 @@ static void atmel_hlcdc_dc_unload(struct drm_device *dev) destroy_workqueue(dc->wq); } -static void atmel_hlcdc_dc_lastclose(struct drm_device *dev) -{ - struct atmel_hlcdc_dc *dc = dev->dev_private; - - drm_fbdev_cma_restore_mode(dc->fbdev); -} - static int atmel_hlcdc_dc_irq_postinstall(struct drm_device *dev) { struct atmel_hlcdc_dc *dc = dev->dev_private; @@ -744,7 +726,7 @@ static struct drm_driver atmel_hlcdc_dc_driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, - .lastclose = atmel_hlcdc_dc_lastclose, + .lastclose = drm_fb_helper_lastclose, .irq_handler = atmel_hlcdc_dc_irq_handler, .irq_preinstall = atmel_hlcdc_dc_irq_uninstall, .irq_postinstall = atmel_hlcdc_dc_irq_postinstall, diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h index 6833ee253cfa..ab32d5b268d2 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h @@ -32,6 +32,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_helper.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> @@ -374,7 +375,6 @@ struct atmel_hlcdc_dc { const struct atmel_hlcdc_dc_desc *desc; struct dma_pool *dscrpool; struct atmel_hlcdc *hlcdc; - struct drm_fbdev_cma *fbdev; struct drm_crtc *crtc; struct atmel_hlcdc_layer *layers[ATMEL_HLCDC_MAX_LAYERS]; struct workqueue_struct *wq; diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index 5dd3f1cd074a..a8905049b9da 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -946,7 +946,9 @@ static int analogix_dp_get_modes(struct drm_connector *connector) return 0; } + pm_runtime_get_sync(dp->dev); edid = drm_get_edid(connector, &dp->aux.ddc); + pm_runtime_put(dp->dev); if (edid) { drm_mode_connector_update_edid_property(&dp->connector, edid); diff --git a/drivers/gpu/drm/drm_blend.c b/drivers/gpu/drm/drm_blend.c index 2e5e089dd912..4c62dff14893 100644 --- a/drivers/gpu/drm/drm_blend.c +++ b/drivers/gpu/drm/drm_blend.c @@ -214,9 +214,11 @@ EXPORT_SYMBOL(drm_rotation_simplify); * This function initializes generic mutable zpos property and enables support * for it in drm core. Drivers can then attach this property to planes to enable * support for configurable planes arrangement during blending operation. - * Once mutable zpos property has been enabled, the DRM core will automatically - * calculate &drm_plane_state.normalized_zpos values. Usually min should be set - * to 0 and max to maximal number of planes for given crtc - 1. + * Drivers that attach a mutable zpos property to any plane should call the + * drm_atomic_normalize_zpos() helper during their implementation of + * &drm_mode_config_funcs.atomic_check(), which will update the normalized zpos + * values and store them in &drm_plane_state.normalized_zpos. Usually min + * should be set to 0 and max to maximal number of planes for given crtc - 1. * * If zpos of some planes cannot be changed (like fixed background or * cursor/topmost planes), driver should adjust min/max values and assign those diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 25f4b2e9a44f..e6a21e69059c 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -24,6 +24,7 @@ #include <drm/drm_connector.h> #include <drm/drm_edid.h> #include <drm/drm_encoder.h> +#include <drm/drm_utils.h> #include "drm_crtc_internal.h" #include "drm_internal.h" @@ -152,6 +153,25 @@ static void drm_connector_free(struct kref *kref) connector->funcs->destroy(connector); } +void drm_connector_free_work_fn(struct work_struct *work) +{ + struct drm_connector *connector, *n; + struct drm_device *dev = + container_of(work, struct drm_device, mode_config.connector_free_work); + struct drm_mode_config *config = &dev->mode_config; + unsigned long flags; + struct llist_node *freed; + + spin_lock_irqsave(&config->connector_list_lock, flags); + freed = llist_del_all(&config->connector_free_list); + spin_unlock_irqrestore(&config->connector_list_lock, flags); + + llist_for_each_entry_safe(connector, n, freed, free_node) { + drm_mode_object_unregister(dev, &connector->base); + connector->funcs->destroy(connector); + } +} + /** * drm_connector_init - Init a preallocated connector * @dev: DRM device @@ -212,6 +232,8 @@ int drm_connector_init(struct drm_device *dev, mutex_init(&connector->mutex); connector->edid_blob_ptr = NULL; connector->status = connector_status_unknown; + connector->display_info.panel_orientation = + DRM_MODE_PANEL_ORIENTATION_UNKNOWN; drm_connector_get_cmdline_mode(connector); @@ -529,6 +551,25 @@ void drm_connector_list_iter_begin(struct drm_device *dev, } EXPORT_SYMBOL(drm_connector_list_iter_begin); +/* + * Extra-safe connector put function that works in any context. Should only be + * used from the connector_iter functions, where we never really expect to + * actually release the connector when dropping our final reference. + */ +static void +__drm_connector_put_safe(struct drm_connector *conn) +{ + struct drm_mode_config *config = &conn->dev->mode_config; + + lockdep_assert_held(&config->connector_list_lock); + + if (!refcount_dec_and_test(&conn->base.refcount.refcount)) + return; + + llist_add(&conn->free_node, &config->connector_free_list); + schedule_work(&config->connector_free_work); +} + /** * drm_connector_list_iter_next - return next connector * @iter: connectr_list iterator @@ -558,10 +599,10 @@ drm_connector_list_iter_next(struct drm_connector_list_iter *iter) /* loop until it's not a zombie connector */ } while (!kref_get_unless_zero(&iter->conn->base.refcount)); - spin_unlock_irqrestore(&config->connector_list_lock, flags); if (old_conn) - drm_connector_put(old_conn); + __drm_connector_put_safe(old_conn); + spin_unlock_irqrestore(&config->connector_list_lock, flags); return iter->conn; } @@ -578,9 +619,15 @@ EXPORT_SYMBOL(drm_connector_list_iter_next); */ void drm_connector_list_iter_end(struct drm_connector_list_iter *iter) { + struct drm_mode_config *config = &iter->dev->mode_config; + unsigned long flags; + iter->dev = NULL; - if (iter->conn) - drm_connector_put(iter->conn); + if (iter->conn) { + spin_lock_irqsave(&config->connector_list_lock, flags); + __drm_connector_put_safe(iter->conn); + spin_unlock_irqrestore(&config->connector_list_lock, flags); + } lock_release(&connector_list_iter_dep_map, 0, _RET_IP_); } EXPORT_SYMBOL(drm_connector_list_iter_end); @@ -668,6 +715,13 @@ static const struct drm_prop_enum_list drm_aspect_ratio_enum_list[] = { { DRM_MODE_PICTURE_ASPECT_16_9, "16:9" }, }; +static const struct drm_prop_enum_list drm_panel_orientation_enum_list[] = { + { DRM_MODE_PANEL_ORIENTATION_NORMAL, "Normal" }, + { DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP, "Upside Down" }, + { DRM_MODE_PANEL_ORIENTATION_LEFT_UP, "Left Side Up" }, + { DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, "Right Side Up" }, +}; + static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = { { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ @@ -776,6 +830,18 @@ DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name, * * CRTC_ID: * Mode object ID of the &drm_crtc this connector should be connected to. + * + * Connectors for LCD panels may also have one standardized property: + * + * panel orientation: + * On some devices the LCD panel is mounted in the casing in such a way + * that the up/top side of the panel does not match with the top side of + * the device. Userspace can use this property to check for this. + * Note that input coordinates from touchscreens (input devices with + * INPUT_PROP_DIRECT) will still map 1:1 to the actual LCD panel + * coordinates, so if userspace rotates the picture to adjust for + * the orientation it must also apply the same transformation to the + * touchscreen input coordinates. */ int drm_connector_create_standard_properties(struct drm_device *dev) @@ -1207,6 +1273,19 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector, if (edid) size = EDID_LENGTH * (1 + edid->extensions); + /* Set the display info, using edid if available, otherwise + * reseting the values to defaults. This duplicates the work + * done in drm_add_edid_modes, but that function is not + * consistently called before this one in all drivers and the + * computation is cheap enough that it seems better to + * duplicate it rather than attempt to ensure some arbitrary + * ordering of calls. + */ + if (edid) + drm_add_display_info(connector, edid); + else + drm_reset_display_info(connector); + drm_object_property_set_value(&connector->base, dev->mode_config.non_desktop_property, connector->display_info.non_desktop); @@ -1251,6 +1330,57 @@ void drm_mode_connector_set_link_status_property(struct drm_connector *connector } EXPORT_SYMBOL(drm_mode_connector_set_link_status_property); +/** + * drm_connector_init_panel_orientation_property - + * initialize the connecters panel_orientation property + * @connector: connector for which to init the panel-orientation property. + * @width: width in pixels of the panel, used for panel quirk detection + * @height: height in pixels of the panel, used for panel quirk detection + * + * This function should only be called for built-in panels, after setting + * connector->display_info.panel_orientation first (if known). + * + * This function will check for platform specific (e.g. DMI based) quirks + * overriding display_info.panel_orientation first, then if panel_orientation + * is not DRM_MODE_PANEL_ORIENTATION_UNKNOWN it will attach the + * "panel orientation" property to the connector. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_connector_init_panel_orientation_property( + struct drm_connector *connector, int width, int height) +{ + struct drm_device *dev = connector->dev; + struct drm_display_info *info = &connector->display_info; + struct drm_property *prop; + int orientation_quirk; + + orientation_quirk = drm_get_panel_orientation_quirk(width, height); + if (orientation_quirk != DRM_MODE_PANEL_ORIENTATION_UNKNOWN) + info->panel_orientation = orientation_quirk; + + if (info->panel_orientation == DRM_MODE_PANEL_ORIENTATION_UNKNOWN) + return 0; + + prop = dev->mode_config.panel_orientation_property; + if (!prop) { + prop = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, + "panel orientation", + drm_panel_orientation_enum_list, + ARRAY_SIZE(drm_panel_orientation_enum_list)); + if (!prop) + return -ENOMEM; + + dev->mode_config.panel_orientation_property = prop; + } + + drm_object_attach_property(&connector->base, prop, + info->panel_orientation); + return 0; +} +EXPORT_SYMBOL(drm_connector_init_panel_orientation_property); + int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj, struct drm_property *property, uint64_t value) diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h index 9ebb8841778c..af00f42ba269 100644 --- a/drivers/gpu/drm/drm_crtc_internal.h +++ b/drivers/gpu/drm/drm_crtc_internal.h @@ -142,6 +142,7 @@ int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj, uint64_t value); int drm_connector_create_standard_properties(struct drm_device *dev); const char *drm_get_connector_force_name(enum drm_connector_force force); +void drm_connector_free_work_fn(struct work_struct *work); /* IOCTL */ int drm_mode_connector_property_set_ioctl(struct drm_device *dev, diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 524eace3d460..365901c1c33c 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1731,7 +1731,7 @@ EXPORT_SYMBOL(drm_edid_duplicate); * * Returns true if @vendor is in @edid, false otherwise */ -static bool edid_vendor(struct edid *edid, const char *vendor) +static bool edid_vendor(const struct edid *edid, const char *vendor) { char edid_vendor[3]; @@ -1749,7 +1749,7 @@ static bool edid_vendor(struct edid *edid, const char *vendor) * * This tells subsequent routines what fixes they need to apply. */ -static u32 edid_get_quirks(struct edid *edid) +static u32 edid_get_quirks(const struct edid *edid) { const struct edid_quirk *quirk; int i; @@ -2813,7 +2813,7 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid, /* * Search EDID for CEA extension block. */ -static u8 *drm_find_edid_extension(struct edid *edid, int ext_id) +static u8 *drm_find_edid_extension(const struct edid *edid, int ext_id) { u8 *edid_ext = NULL; int i; @@ -2835,12 +2835,12 @@ static u8 *drm_find_edid_extension(struct edid *edid, int ext_id) return edid_ext; } -static u8 *drm_find_cea_extension(struct edid *edid) +static u8 *drm_find_cea_extension(const struct edid *edid) { return drm_find_edid_extension(edid, CEA_EXT); } -static u8 *drm_find_displayid_extension(struct edid *edid) +static u8 *drm_find_displayid_extension(const struct edid *edid) { return drm_find_edid_extension(edid, DISPLAYID_EXT); } @@ -4378,7 +4378,7 @@ drm_parse_hdmi_vsdb_video(struct drm_connector *connector, const u8 *db) } static void drm_parse_cea_ext(struct drm_connector *connector, - struct edid *edid) + const struct edid *edid) { struct drm_display_info *info = &connector->display_info; const u8 *edid_ext; @@ -4412,11 +4412,34 @@ static void drm_parse_cea_ext(struct drm_connector *connector, } } -static void drm_add_display_info(struct drm_connector *connector, - struct edid *edid, u32 quirks) +/* A connector has no EDID information, so we've got no EDID to compute quirks from. Reset + * all of the values which would have been set from EDID + */ +void +drm_reset_display_info(struct drm_connector *connector) +{ + struct drm_display_info *info = &connector->display_info; + + info->width_mm = 0; + info->height_mm = 0; + + info->bpc = 0; + info->color_formats = 0; + info->cea_rev = 0; + info->max_tmds_clock = 0; + info->dvi_dual = false; + info->has_hdmi_infoframe = false; + + info->non_desktop = 0; +} +EXPORT_SYMBOL_GPL(drm_reset_display_info); + +u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edid) { struct drm_display_info *info = &connector->display_info; + u32 quirks = edid_get_quirks(edid); + info->width_mm = edid->width_cm * 10; info->height_mm = edid->height_cm * 10; @@ -4430,11 +4453,13 @@ static void drm_add_display_info(struct drm_connector *connector, info->non_desktop = !!(quirks & EDID_QUIRK_NON_DESKTOP); + DRM_DEBUG_KMS("non_desktop set to %d\n", info->non_desktop); + if (edid->revision < 3) - return; + return quirks; if (!(edid->input & DRM_EDID_INPUT_DIGITAL)) - return; + return quirks; drm_parse_cea_ext(connector, edid); @@ -4454,7 +4479,7 @@ static void drm_add_display_info(struct drm_connector *connector, /* Only defined for 1.4 with digital displays */ if (edid->revision < 4) - return; + return quirks; switch (edid->input & DRM_EDID_DIGITAL_DEPTH_MASK) { case DRM_EDID_DIGITAL_DEPTH_6: @@ -4489,7 +4514,9 @@ static void drm_add_display_info(struct drm_connector *connector, info->color_formats |= DRM_COLOR_FORMAT_YCRCB444; if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB422) info->color_formats |= DRM_COLOR_FORMAT_YCRCB422; + return quirks; } +EXPORT_SYMBOL_GPL(drm_add_display_info); static int validate_displayid(u8 *displayid, int length, int idx) { @@ -4645,8 +4672,6 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) return 0; } - quirks = edid_get_quirks(edid); - drm_edid_to_eld(connector, edid); /* @@ -4654,7 +4679,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) * To avoid multiple parsing of same block, lets parse that map * from sink info, before parsing CEA modes. */ - drm_add_display_info(connector, edid, quirks); + quirks = drm_add_display_info(connector, edid); /* * EDID spec says modes should be preferred in this order: diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index 35b56dfba929..186d00adfb5f 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -23,6 +23,7 @@ #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_fb_cma_helper.h> +#include <drm/drm_print.h> #include <linux/module.h> #define DEFAULT_FBDEFIO_DELAY_MS 50 @@ -42,7 +43,7 @@ struct drm_fbdev_cma { * callback function to create a cma backed framebuffer. * * An fbdev framebuffer backed by cma is also available by calling - * drm_fbdev_cma_init(). drm_fbdev_cma_fini() tears it down. + * drm_fb_cma_fbdev_init(). drm_fb_cma_fbdev_fini() tears it down. * If the &drm_framebuffer_funcs.dirty callback is set, fb_deferred_io will be * set up automatically. &drm_framebuffer_funcs.dirty is called by * drm_fb_helper_deferred_io() in process context (&struct delayed_work). @@ -68,7 +69,7 @@ struct drm_fbdev_cma { * * Initialize:: * - * fbdev = drm_fbdev_cma_init_with_funcs(dev, 16, + * fbdev = drm_fb_cma_fbdev_init_with_funcs(dev, 16, * dev->mode_config.num_crtc, * dev->mode_config.num_connector, * &driver_fb_funcs); @@ -256,7 +257,7 @@ drm_fbdev_cma_create(struct drm_fb_helper *helper, fbi->screen_size = size; fbi->fix.smem_len = size; - if (fbdev_cma->fb_funcs->dirty) { + if (fb->funcs->dirty) { ret = drm_fbdev_cma_defio_init(fbi, obj); if (ret) goto err_cma_destroy; @@ -278,6 +279,118 @@ static const struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = { }; /** + * drm_fb_cma_fbdev_init_with_funcs() - Allocate and initialize fbdev emulation + * @dev: DRM device + * @preferred_bpp: Preferred bits per pixel for the device. + * @dev->mode_config.preferred_depth is used if this is zero. + * @max_conn_count: Maximum number of connectors. + * @dev->mode_config.num_connector is used if this is zero. + * @funcs: Framebuffer functions, in particular a custom dirty() callback. + * Can be NULL. + * + * Returns: + * Zero on success or negative error code on failure. + */ +int drm_fb_cma_fbdev_init_with_funcs(struct drm_device *dev, + unsigned int preferred_bpp, unsigned int max_conn_count, + const struct drm_framebuffer_funcs *funcs) +{ + struct drm_fbdev_cma *fbdev_cma; + struct drm_fb_helper *fb_helper; + int ret; + + if (!preferred_bpp) + preferred_bpp = dev->mode_config.preferred_depth; + if (!preferred_bpp) + preferred_bpp = 32; + + if (!max_conn_count) + max_conn_count = dev->mode_config.num_connector; + + fbdev_cma = kzalloc(sizeof(*fbdev_cma), GFP_KERNEL); + if (!fbdev_cma) + return -ENOMEM; + + fbdev_cma->fb_funcs = funcs; + fb_helper = &fbdev_cma->fb_helper; + + drm_fb_helper_prepare(dev, fb_helper, &drm_fb_cma_helper_funcs); + + ret = drm_fb_helper_init(dev, fb_helper, max_conn_count); + if (ret < 0) { + DRM_DEV_ERROR(dev->dev, "Failed to initialize fbdev helper.\n"); + goto err_free; + } + + ret = drm_fb_helper_single_add_all_connectors(fb_helper); + if (ret < 0) { + DRM_DEV_ERROR(dev->dev, "Failed to add connectors.\n"); + goto err_drm_fb_helper_fini; + } + + ret = drm_fb_helper_initial_config(fb_helper, preferred_bpp); + if (ret < 0) { + DRM_DEV_ERROR(dev->dev, "Failed to set fbdev configuration.\n"); + goto err_drm_fb_helper_fini; + } + + return 0; + +err_drm_fb_helper_fini: + drm_fb_helper_fini(fb_helper); +err_free: + kfree(fbdev_cma); + + return ret; +} +EXPORT_SYMBOL_GPL(drm_fb_cma_fbdev_init_with_funcs); + +/** + * drm_fb_cma_fbdev_init() - Allocate and initialize fbdev emulation + * @dev: DRM device + * @preferred_bpp: Preferred bits per pixel for the device. + * @dev->mode_config.preferred_depth is used if this is zero. + * @max_conn_count: Maximum number of connectors. + * @dev->mode_config.num_connector is used if this is zero. + * + * Returns: + * Zero on success or negative error code on failure. + */ +int drm_fb_cma_fbdev_init(struct drm_device *dev, unsigned int preferred_bpp, + unsigned int max_conn_count) +{ + return drm_fb_cma_fbdev_init_with_funcs(dev, preferred_bpp, + max_conn_count, NULL); +} +EXPORT_SYMBOL_GPL(drm_fb_cma_fbdev_init); + +/** + * drm_fb_cma_fbdev_fini() - Teardown fbdev emulation + * @dev: DRM device + */ +void drm_fb_cma_fbdev_fini(struct drm_device *dev) +{ + struct drm_fb_helper *fb_helper = dev->fb_helper; + + if (!fb_helper) + return; + + /* Unregister if it hasn't been done already */ + if (fb_helper->fbdev && fb_helper->fbdev->dev) + drm_fb_helper_unregister_fbi(fb_helper); + + if (fb_helper->fbdev) + drm_fbdev_cma_defio_fini(fb_helper->fbdev); + + if (fb_helper->fb) + drm_framebuffer_remove(fb_helper->fb); + + drm_fb_helper_fini(fb_helper); + kfree(to_fbdev_cma(fb_helper)); +} +EXPORT_SYMBOL_GPL(drm_fb_cma_fbdev_fini); + +/** * drm_fbdev_cma_init_with_funcs() - Allocate and initializes a drm_fbdev_cma struct * @dev: DRM device * @preferred_bpp: Preferred bits per pixel for the device diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 09919e8d67f9..04a3a5ce370a 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -41,6 +41,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include "drm_crtc_internal.h" #include "drm_crtc_helper_internal.h" static bool drm_fbdev_emulation = true; @@ -177,7 +178,7 @@ EXPORT_SYMBOL(drm_fb_helper_add_one_connector); */ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) { - struct drm_device *dev = fb_helper->dev; + struct drm_device *dev; struct drm_connector *connector; struct drm_connector_list_iter conn_iter; int i, ret = 0; @@ -185,6 +186,8 @@ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) if (!drm_fbdev_emulation || !fb_helper) return 0; + dev = fb_helper->dev; + mutex_lock(&fb_helper->lock); drm_connector_list_iter_begin(dev, &conn_iter); drm_for_each_connector_iter(connector, &conn_iter) { @@ -356,6 +359,7 @@ EXPORT_SYMBOL(drm_fb_helper_debug_leave); static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper, bool active) { struct drm_device *dev = fb_helper->dev; + struct drm_plane_state *plane_state; struct drm_plane *plane; struct drm_atomic_state *state; int i, ret; @@ -374,8 +378,6 @@ static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper, bool activ retry: plane_mask = 0; drm_for_each_plane(plane, dev) { - struct drm_plane_state *plane_state; - plane_state = drm_atomic_get_plane_state(state, plane); if (IS_ERR(plane_state)) { ret = PTR_ERR(plane_state); @@ -398,6 +400,11 @@ retry: for (i = 0; i < fb_helper->crtc_count; i++) { struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; + struct drm_plane *primary = mode_set->crtc->primary; + + /* Cannot fail as we've already gotten the plane state above */ + plane_state = drm_atomic_get_new_plane_state(state, primary); + plane_state->rotation = fb_helper->crtc_info[i].rotation; ret = __drm_atomic_helper_set_config(mode_set, state); if (ret != 0) @@ -829,6 +836,7 @@ int drm_fb_helper_init(struct drm_device *dev, if (!fb_helper->crtc_info[i].mode_set.connectors) goto out_free; fb_helper->crtc_info[i].mode_set.num_connectors = 0; + fb_helper->crtc_info[i].rotation = DRM_MODE_ROTATE_0; } i = 0; @@ -2357,6 +2365,62 @@ out: return best_score; } +/* + * This function checks if rotation is necessary because of panel orientation + * and if it is, if it is supported. + * If rotation is necessary and supported, its gets set in fb_crtc.rotation. + * If rotation is necessary but not supported, a DRM_MODE_ROTATE_* flag gets + * or-ed into fb_helper->sw_rotations. In drm_setup_crtcs_fb() we check if only + * one bit is set and then we set fb_info.fbcon_rotate_hint to make fbcon do + * the unsupported rotation. + */ +static void drm_setup_crtc_rotation(struct drm_fb_helper *fb_helper, + struct drm_fb_helper_crtc *fb_crtc, + struct drm_connector *connector) +{ + struct drm_plane *plane = fb_crtc->mode_set.crtc->primary; + uint64_t valid_mask = 0; + int i, rotation; + + fb_crtc->rotation = DRM_MODE_ROTATE_0; + + switch (connector->display_info.panel_orientation) { + case DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP: + rotation = DRM_MODE_ROTATE_180; + break; + case DRM_MODE_PANEL_ORIENTATION_LEFT_UP: + rotation = DRM_MODE_ROTATE_90; + break; + case DRM_MODE_PANEL_ORIENTATION_RIGHT_UP: + rotation = DRM_MODE_ROTATE_270; + break; + default: + rotation = DRM_MODE_ROTATE_0; + } + + /* + * TODO: support 90 / 270 degree hardware rotation, + * depending on the hardware this may require the framebuffer + * to be in a specific tiling format. + */ + if (rotation != DRM_MODE_ROTATE_180 || !plane->rotation_property) { + fb_helper->sw_rotations |= rotation; + return; + } + + for (i = 0; i < plane->rotation_property->num_values; i++) + valid_mask |= (1ULL << plane->rotation_property->values[i]); + + if (!(rotation & valid_mask)) { + fb_helper->sw_rotations |= rotation; + return; + } + + fb_crtc->rotation = rotation; + /* Rotating in hardware, fbcon should not rotate */ + fb_helper->sw_rotations |= DRM_MODE_ROTATE_0; +} + static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, u32 width, u32 height) { @@ -2416,6 +2480,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, drm_fb_helper_modeset_release(fb_helper, &fb_helper->crtc_info[i].mode_set); + fb_helper->sw_rotations = 0; drm_fb_helper_for_each_connector(fb_helper, i) { struct drm_display_mode *mode = modes[i]; struct drm_fb_helper_crtc *fb_crtc = crtcs[i]; @@ -2435,6 +2500,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, modeset->mode = drm_mode_duplicate(dev, fb_crtc->desired_mode); drm_connector_get(connector); + drm_setup_crtc_rotation(fb_helper, fb_crtc, connector); modeset->connectors[modeset->num_connectors++] = connector; modeset->x = offset->x; modeset->y = offset->y; @@ -2476,6 +2542,28 @@ static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper) } } mutex_unlock(&fb_helper->dev->mode_config.mutex); + + switch (fb_helper->sw_rotations) { + case DRM_MODE_ROTATE_0: + info->fbcon_rotate_hint = FB_ROTATE_UR; + break; + case DRM_MODE_ROTATE_90: + info->fbcon_rotate_hint = FB_ROTATE_CCW; + break; + case DRM_MODE_ROTATE_180: + info->fbcon_rotate_hint = FB_ROTATE_UD; + break; + case DRM_MODE_ROTATE_270: + info->fbcon_rotate_hint = FB_ROTATE_CW; + break; + default: + /* + * Multiple bits are set / multiple rotations requested + * fbcon cannot handle separate rotation settings per + * output, so fallback to unrotated. + */ + info->fbcon_rotate_hint = FB_ROTATE_UR; + } } /* Note: Drops fb_helper->lock before returning. */ diff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index aa8cb9bfa499..4d682a6e8bcb 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -272,7 +272,8 @@ EXPORT_SYMBOL_GPL(drm_gem_fb_prepare_fb); * @sizes: fbdev size description * @pitch_align: Optional pitch alignment * @obj: GEM object backing the framebuffer - * @funcs: vtable to be used for the new framebuffer object + * @funcs: Optional vtable to be used for the new framebuffer object when the + * dirty callback is needed. * * This function creates a framebuffer from a &drm_fb_helper_surface_size * description for use in the &drm_fb_helper_funcs.fb_probe callback. @@ -300,6 +301,9 @@ drm_gem_fbdev_fb_create(struct drm_device *dev, if (obj->size < mode_cmd.pitches[0] * mode_cmd.height) return ERR_PTR(-EINVAL); + if (!funcs) + funcs = &drm_gem_fb_funcs; + return drm_gem_fb_alloc(dev, &mode_cmd, &obj, 1, funcs); } EXPORT_SYMBOL(drm_gem_fbdev_fb_create); diff --git a/drivers/gpu/drm/drm_lease.c b/drivers/gpu/drm/drm_lease.c index d1eb56a1eff4..59849f02e2ad 100644 --- a/drivers/gpu/drm/drm_lease.c +++ b/drivers/gpu/drm/drm_lease.c @@ -254,10 +254,10 @@ static struct drm_master *drm_lease_create(struct drm_master *lessor, struct idr return lessee; out_lessee: - drm_master_put(&lessee); - mutex_unlock(&dev->mode_config.idr_mutex); + drm_master_put(&lessee); + return ERR_PTR(error); } diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index eb86bc3f753b..186c4e90cc1c 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -575,21 +575,23 @@ EXPORT_SYMBOL(drm_mm_remove_node); */ void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new) { + struct drm_mm *mm = old->mm; + DRM_MM_BUG_ON(!old->allocated); *new = *old; list_replace(&old->node_list, &new->node_list); - rb_replace_node(&old->rb, &new->rb, &old->mm->interval_tree.rb_root); + rb_replace_node_cached(&old->rb, &new->rb, &mm->interval_tree); if (drm_mm_hole_follows(old)) { list_replace(&old->hole_stack, &new->hole_stack); rb_replace_node(&old->rb_hole_size, &new->rb_hole_size, - &old->mm->holes_size); + &mm->holes_size); rb_replace_node(&old->rb_hole_addr, &new->rb_hole_addr, - &old->mm->holes_addr); + &mm->holes_addr); } old->allocated = false; diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index cda8bfab6d3b..e5c653357024 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -382,6 +382,9 @@ void drm_mode_config_init(struct drm_device *dev) ida_init(&dev->mode_config.connector_ida); spin_lock_init(&dev->mode_config.connector_list_lock); + init_llist_head(&dev->mode_config.connector_free_list); + INIT_WORK(&dev->mode_config.connector_free_work, drm_connector_free_work_fn); + drm_mode_create_standard_properties(dev); /* Just to be sure */ @@ -431,6 +434,8 @@ void drm_mode_config_cleanup(struct drm_device *dev) drm_connector_put(connector); } drm_connector_list_iter_end(&conn_iter); + /* connector_iter drops references in a work item. */ + flush_work(&dev->mode_config.connector_free_work); if (WARN_ON(!list_empty(&dev->mode_config.connector_list))) { drm_connector_list_iter_begin(dev, &conn_iter); drm_for_each_connector_iter(connector, &conn_iter) @@ -467,6 +472,9 @@ void drm_mode_config_cleanup(struct drm_device *dev) */ WARN_ON(!list_empty(&dev->mode_config.fb_list)); list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { + struct drm_printer p = drm_debug_printer("[leaked fb]"); + drm_printf(&p, "framebuffer[%u]:\n", fb->base.id); + drm_framebuffer_print_info(&p, 1, fb); drm_framebuffer_free(&fb->base.refcount); } diff --git a/drivers/gpu/drm/drm_modeset_helper.c b/drivers/gpu/drm/drm_modeset_helper.c index 9cb1eede0b4d..f1c24ab0ef09 100644 --- a/drivers/gpu/drm/drm_modeset_helper.c +++ b/drivers/gpu/drm/drm_modeset_helper.c @@ -20,6 +20,9 @@ * OF THIS SOFTWARE. */ +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_helper.h> #include <drm/drm_modeset_helper.h> #include <drm/drm_plane_helper.h> @@ -156,3 +159,76 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, NULL); } EXPORT_SYMBOL(drm_crtc_init); + +/** + * drm_mode_config_helper_suspend - Modeset suspend helper + * @dev: DRM device + * + * This helper function takes care of suspending the modeset side. It disables + * output polling if initialized, suspends fbdev if used and finally calls + * drm_atomic_helper_suspend(). + * If suspending fails, fbdev and polling is re-enabled. + * + * Returns: + * Zero on success, negative error code on error. + * + * See also: + * drm_kms_helper_poll_disable() and drm_fb_helper_set_suspend_unlocked(). + */ +int drm_mode_config_helper_suspend(struct drm_device *dev) +{ + struct drm_atomic_state *state; + + if (!dev) + return 0; + + drm_kms_helper_poll_disable(dev); + drm_fb_helper_set_suspend_unlocked(dev->fb_helper, 1); + state = drm_atomic_helper_suspend(dev); + if (IS_ERR(state)) { + drm_fb_helper_set_suspend_unlocked(dev->fb_helper, 0); + drm_kms_helper_poll_enable(dev); + return PTR_ERR(state); + } + + dev->mode_config.suspend_state = state; + + return 0; +} +EXPORT_SYMBOL(drm_mode_config_helper_suspend); + +/** + * drm_mode_config_helper_resume - Modeset resume helper + * @dev: DRM device + * + * This helper function takes care of resuming the modeset side. It calls + * drm_atomic_helper_resume(), resumes fbdev if used and enables output polling + * if initiaized. + * + * Returns: + * Zero on success, negative error code on error. + * + * See also: + * drm_fb_helper_set_suspend_unlocked() and drm_kms_helper_poll_enable(). + */ +int drm_mode_config_helper_resume(struct drm_device *dev) +{ + int ret; + + if (!dev) + return 0; + + if (WARN_ON(!dev->mode_config.suspend_state)) + return -EINVAL; + + ret = drm_atomic_helper_resume(dev, dev->mode_config.suspend_state); + if (ret) + DRM_ERROR("Failed to resume (%d)\n", ret); + dev->mode_config.suspend_state = NULL; + + drm_fb_helper_set_suspend_unlocked(dev->fb_helper, 0); + drm_kms_helper_poll_enable(dev); + + return ret; +} +EXPORT_SYMBOL(drm_mode_config_helper_resume); diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c new file mode 100644 index 000000000000..901a4e9a87a3 --- /dev/null +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -0,0 +1,174 @@ +/* SPDX-License-Identifier: MIT */ +/* + * drm_panel_orientation_quirks.c -- Quirks for non-normal panel orientation + * + * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com> + * + * Note the quirks in this file are shared with fbdev/efifb and as such + * must not depend on other drm code. + */ + +#include <linux/dmi.h> +#include <drm/drm_connector.h> + +#ifdef CONFIG_DMI + +/* + * Some x86 clamshell design devices use portrait tablet screens and a display + * engine which cannot rotate in hardware, so we need to rotate the fbcon to + * compensate. Unfortunately these (cheap) devices also typically have quite + * generic DMI data, so we match on a combination of DMI data, screen resolution + * and a list of known BIOS dates to avoid false positives. + */ + +struct drm_dmi_panel_orientation_data { + int width; + int height; + const char * const *bios_dates; + int orientation; +}; + +static const struct drm_dmi_panel_orientation_data asus_t100ha = { + .width = 800, + .height = 1280, + .orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP, +}; + +static const struct drm_dmi_panel_orientation_data gpd_pocket = { + .width = 1200, + .height = 1920, + .bios_dates = (const char * const []){ "05/26/2017", "06/28/2017", + "07/05/2017", "08/07/2017", NULL }, + .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, +}; + +static const struct drm_dmi_panel_orientation_data gpd_win = { + .width = 720, + .height = 1280, + .bios_dates = (const char * const []){ + "10/25/2016", "11/18/2016", "12/23/2016", "12/26/2016", + "02/21/2017", "03/20/2017", "05/25/2017", NULL }, + .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, +}; + +static const struct drm_dmi_panel_orientation_data itworks_tw891 = { + .width = 800, + .height = 1280, + .bios_dates = (const char * const []){ "10/16/2015", NULL }, + .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, +}; + +static const struct drm_dmi_panel_orientation_data vios_lth17 = { + .width = 800, + .height = 1280, + .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, +}; + +static const struct dmi_system_id orientation_data[] = { + { /* Asus T100HA */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100HAN"), + }, + .driver_data = (void *)&asus_t100ha, + }, { /* + * GPD Pocket, note that the the DMI data is less generic then + * it seems, devices with a board-vendor of "AMI Corporation" + * are quite rare, as are devices which have both board- *and* + * product-id set to "Default String" + */ + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"), + DMI_EXACT_MATCH(DMI_BOARD_SERIAL, "Default string"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"), + }, + .driver_data = (void *)&gpd_pocket, + }, { /* GPD Win (same note on DMI match as GPD Pocket) */ + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"), + DMI_EXACT_MATCH(DMI_BOARD_SERIAL, "Default string"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"), + }, + .driver_data = (void *)&gpd_win, + }, { /* I.T.Works TW891 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "To be filled by O.E.M."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "TW891"), + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "To be filled by O.E.M."), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "TW891"), + }, + .driver_data = (void *)&itworks_tw891, + }, { /* VIOS LTH17 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "VIOS"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "LTH17"), + }, + .driver_data = (void *)&vios_lth17, + }, + {} +}; + +/** + * drm_get_panel_orientation_quirk - Check for panel orientation quirks + * @width: width in pixels of the panel + * @height: height in pixels of the panel + * + * This function checks for platform specific (e.g. DMI based) quirks + * providing info on panel_orientation for systems where this cannot be + * probed from the hard-/firm-ware. To avoid false-positive this function + * takes the panel resolution as argument and checks that against the + * resolution expected by the quirk-table entry. + * + * Note this function is also used outside of the drm-subsys, by for example + * the efifb code. Because of this this function gets compiled into its own + * kernel-module when built as a module. + * + * Returns: + * A DRM_MODE_PANEL_ORIENTATION_* value if there is a quirk for this system, + * or DRM_MODE_PANEL_ORIENTATION_UNKNOWN if there is no quirk. + */ +int drm_get_panel_orientation_quirk(int width, int height) +{ + const struct dmi_system_id *match; + const struct drm_dmi_panel_orientation_data *data; + const char *bios_date; + int i; + + for (match = dmi_first_match(orientation_data); + match; + match = dmi_first_match(match + 1)) { + data = match->driver_data; + + if (data->width != width || + data->height != height) + continue; + + if (!data->bios_dates) + return data->orientation; + + bios_date = dmi_get_system_info(DMI_BIOS_DATE); + if (!bios_date) + continue; + + for (i = 0; data->bios_dates[i]; i++) { + if (!strcmp(data->bios_dates[i], bios_date)) + return data->orientation; + } + } + + return DRM_MODE_PANEL_ORIENTATION_UNKNOWN; +} +EXPORT_SYMBOL(drm_get_panel_orientation_quirk); + +#else + +/* There are no quirks for non x86 devices yet */ +int drm_get_panel_orientation_quirk(int width, int height) +{ + return DRM_MODE_PANEL_ORIENTATION_UNKNOWN; +} +EXPORT_SYMBOL(drm_get_panel_orientation_quirk); + +#endif diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 8de93a226c24..9a17725b0f7a 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -218,8 +218,9 @@ static void drm_gem_map_detach(struct dma_buf *dma_buf, sgt = prime_attach->sgt; if (sgt) { if (prime_attach->dir != DMA_NONE) - dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, - prime_attach->dir); + dma_unmap_sg_attrs(attach->dev, sgt->sgl, sgt->nents, + prime_attach->dir, + DMA_ATTR_SKIP_CPU_SYNC); sg_free_table(sgt); } @@ -277,7 +278,8 @@ static struct sg_table *drm_gem_map_dma_buf(struct dma_buf_attachment *attach, sgt = obj->dev->driver->gem_prime_get_sg_table(obj); if (!IS_ERR(sgt)) { - if (!dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir)) { + if (!dma_map_sg_attrs(attach->dev, sgt->sgl, sgt->nents, dir, + DMA_ATTR_SKIP_CPU_SYNC)) { sg_free_table(sgt); kfree(sgt); sgt = ERR_PTR(-ENOMEM); diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 6dc2dde5b672..555fbe54d6e2 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -216,8 +216,7 @@ enum drm_mode_status drm_connector_mode_valid(struct drm_connector *connector, * suspend/resume. * * Drivers can call this helper from their device resume implementation. It is - * an error to call this when the output polling support has not yet been set - * up. + * not an error to call this even when output polling isn't enabled. * * Note that calls to enable and disable polling must be strictly ordered, which * is automatically the case when they're only call from suspend/resume diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 82b72425a42f..b96bd5a781b2 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -16,6 +16,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_helper.h> #include <linux/component.h> @@ -37,8 +38,6 @@ #define DRIVER_MAJOR 1 #define DRIVER_MINOR 0 -static struct device *exynos_drm_get_dma_device(void); - int exynos_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) { @@ -89,11 +88,6 @@ static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file) file->driver_priv = NULL; } -static void exynos_drm_lastclose(struct drm_device *dev) -{ - exynos_drm_fbdev_restore_mode(dev); -} - static const struct vm_operations_struct exynos_drm_gem_vm_ops = { .fault = exynos_drm_gem_fault, .open = drm_gem_vm_open, @@ -140,7 +134,7 @@ static struct drm_driver exynos_drm_driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC | DRIVER_RENDER, .open = exynos_drm_open, - .lastclose = exynos_drm_lastclose, + .lastclose = drm_fb_helper_lastclose, .postclose = exynos_drm_postclose, .gem_free_object_unlocked = exynos_drm_gem_free_object, .gem_vm_ops = &exynos_drm_gem_vm_ops, @@ -148,7 +142,7 @@ static struct drm_driver exynos_drm_driver = { .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_export = drm_gem_prime_export, - .gem_prime_import = drm_gem_prime_import, + .gem_prime_import = exynos_drm_gem_prime_import, .gem_prime_get_sg_table = exynos_drm_gem_prime_get_sg_table, .gem_prime_import_sg_table = exynos_drm_gem_prime_import_sg_table, .gem_prime_vmap = exynos_drm_gem_prime_vmap, @@ -301,6 +295,27 @@ static struct component_match *exynos_drm_match_add(struct device *dev) return match ?: ERR_PTR(-ENODEV); } +static struct device *exynos_drm_get_dma_device(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(exynos_drm_drivers); ++i) { + struct exynos_drm_driver_info *info = &exynos_drm_drivers[i]; + struct device *dev; + + if (!info->driver || !(info->flags & DRM_DMA_DEVICE)) + continue; + + while ((dev = bus_find_device(&platform_bus_type, NULL, + &info->driver->driver, + (void *)platform_bus_type.match))) { + put_device(dev); + return dev; + } + } + return NULL; +} + static int exynos_drm_bind(struct device *dev) { struct exynos_drm_private *private; @@ -469,27 +484,6 @@ static struct platform_driver exynos_drm_platform_driver = { }, }; -static struct device *exynos_drm_get_dma_device(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(exynos_drm_drivers); ++i) { - struct exynos_drm_driver_info *info = &exynos_drm_drivers[i]; - struct device *dev; - - if (!info->driver || !(info->flags & DRM_DMA_DEVICE)) - continue; - - while ((dev = bus_find_device(&platform_bus_type, NULL, - &info->driver->driver, - (void *)platform_bus_type.match))) { - put_device(dev); - return dev; - } - } - return NULL; -} - static void exynos_drm_unregister_devices(void) { int i; diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index c6847fa708fa..589d465a7f88 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -194,11 +194,6 @@ struct drm_exynos_file_private { /* * Exynos drm private structure. * - * @da_start: start address to device address space. - * with iommu, device address space starts from this address - * otherwise default one. - * @da_space_size: size of device address space. - * if 0 then default value is used for it. * @pending: the crtcs that have pending updates to finish * @lock: protect access to @pending * @wait: wait an atomic commit to finish diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index 8208df56a88f..0faaf829f5bf 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c @@ -205,7 +205,7 @@ static struct drm_mode_config_helper_funcs exynos_drm_mode_config_helpers = { static const struct drm_mode_config_funcs exynos_drm_mode_config_funcs = { .fb_create = exynos_user_fb_create, - .output_poll_changed = exynos_drm_output_poll_changed, + .output_poll_changed = drm_fb_helper_output_poll_changed, .atomic_check = exynos_atomic_check, .atomic_commit = drm_atomic_helper_commit, }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index dfb66ecf417b..132dd52d0ac7 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -270,24 +270,6 @@ void exynos_drm_fbdev_fini(struct drm_device *dev) private->fb_helper = NULL; } -void exynos_drm_fbdev_restore_mode(struct drm_device *dev) -{ - struct exynos_drm_private *private = dev->dev_private; - - if (!private || !private->fb_helper) - return; - - drm_fb_helper_restore_fbdev_mode_unlocked(private->fb_helper); -} - -void exynos_drm_output_poll_changed(struct drm_device *dev) -{ - struct exynos_drm_private *private = dev->dev_private; - struct drm_fb_helper *fb_helper = private->fb_helper; - - drm_fb_helper_hotplug_event(fb_helper); -} - void exynos_drm_fbdev_suspend(struct drm_device *dev) { struct exynos_drm_private *private = dev->dev_private; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.h b/drivers/gpu/drm/exynos/exynos_drm_fbdev.h index 645d1bb7f665..b33847223a85 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.h +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.h @@ -19,8 +19,6 @@ int exynos_drm_fbdev_init(struct drm_device *dev); void exynos_drm_fbdev_fini(struct drm_device *dev); -void exynos_drm_fbdev_restore_mode(struct drm_device *dev); -void exynos_drm_output_poll_changed(struct drm_device *dev); void exynos_drm_fbdev_suspend(struct drm_device *drm); void exynos_drm_fbdev_resume(struct drm_device *drm); diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index 077de014d610..11cc01b47bc0 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -247,6 +247,15 @@ struct exynos_drm_gem *exynos_drm_gem_create(struct drm_device *dev, if (IS_ERR(exynos_gem)) return exynos_gem; + if (!is_drm_iommu_supported(dev) && (flags & EXYNOS_BO_NONCONTIG)) { + /* + * when no IOMMU is available, all allocated buffers are + * contiguous anyway, so drop EXYNOS_BO_NONCONTIG flag + */ + flags &= ~EXYNOS_BO_NONCONTIG; + DRM_WARN("Non-contiguous allocation is not supported without IOMMU, falling back to contiguous buffer\n"); + } + /* set memory type and cache attribute from user side. */ exynos_gem->flags = flags; @@ -506,6 +515,12 @@ int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) } /* low-level interface prime helpers */ +struct drm_gem_object *exynos_drm_gem_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf) +{ + return drm_gem_prime_import_dev(dev, dma_buf, to_dma_dev(dev)); +} + struct sg_table *exynos_drm_gem_prime_get_sg_table(struct drm_gem_object *obj) { struct exynos_drm_gem *exynos_gem = to_exynos_gem(obj); diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h index e86d1a9518c3..5a4c7de80f65 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.h +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h @@ -117,6 +117,8 @@ int exynos_drm_gem_fault(struct vm_fault *vmf); int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); /* low-level interface prime helpers */ +struct drm_gem_object *exynos_drm_gem_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf); struct sg_table *exynos_drm_gem_prime_get_sg_table(struct drm_gem_object *obj); struct drm_gem_object * exynos_drm_gem_prime_import_sg_table(struct drm_device *dev, diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c index faf17b83b910..80232321a244 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c @@ -27,6 +27,7 @@ #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h> +#include <drm/drm_modeset_helper.h> #include "fsl_dcu_drm_crtc.h" #include "fsl_dcu_drm_drv.h" @@ -188,26 +189,17 @@ static struct drm_driver fsl_dcu_drm_driver = { static int fsl_dcu_drm_pm_suspend(struct device *dev) { struct fsl_dcu_drm_device *fsl_dev = dev_get_drvdata(dev); + int ret; if (!fsl_dev) return 0; disable_irq(fsl_dev->irq); - drm_kms_helper_poll_disable(fsl_dev->drm); - - console_lock(); - drm_fbdev_cma_set_suspend(fsl_dev->fbdev, 1); - console_unlock(); - - fsl_dev->state = drm_atomic_helper_suspend(fsl_dev->drm); - if (IS_ERR(fsl_dev->state)) { - console_lock(); - drm_fbdev_cma_set_suspend(fsl_dev->fbdev, 0); - console_unlock(); - drm_kms_helper_poll_enable(fsl_dev->drm); + ret = drm_mode_config_helper_suspend(fsl_dev->drm); + if (ret) { enable_irq(fsl_dev->irq); - return PTR_ERR(fsl_dev->state); + return ret; } clk_disable_unprepare(fsl_dev->clk); @@ -233,13 +225,8 @@ static int fsl_dcu_drm_pm_resume(struct device *dev) fsl_tcon_bypass_enable(fsl_dev->tcon); fsl_dcu_drm_init_planes(fsl_dev->drm); enable_irq(fsl_dev->irq); - drm_atomic_helper_resume(fsl_dev->drm, fsl_dev->state); - - console_lock(); - drm_fbdev_cma_set_suspend(fsl_dev->fbdev, 0); - console_unlock(); - drm_kms_helper_poll_enable(fsl_dev->drm); + drm_mode_config_helper_resume(fsl_dev->drm); return 0; } diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h index da9bfd432ca6..93bfb98012d4 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h @@ -196,7 +196,6 @@ struct fsl_dcu_drm_device { struct drm_encoder encoder; struct fsl_dcu_drm_connector connector; const struct fsl_dcu_soc_data *soc; - struct drm_atomic_state *state; }; int fsl_dcu_drm_modeset_init(struct fsl_dcu_drm_device *fsl_dev); diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index 2570c7f647a6..cb0a2ae916e0 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -576,13 +576,6 @@ static void psb_fbdev_fini(struct drm_device *dev) dev_priv->fbdev = NULL; } -static void psbfb_output_poll_changed(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct psb_fbdev *fbdev = (struct psb_fbdev *)dev_priv->fbdev; - drm_fb_helper_hotplug_event(&fbdev->psb_fb_helper); -} - /** * psb_user_framebuffer_create_handle - add hamdle to a framebuffer * @fb: framebuffer @@ -623,7 +616,7 @@ static void psb_user_framebuffer_destroy(struct drm_framebuffer *fb) static const struct drm_mode_config_funcs psb_mode_funcs = { .fb_create = psb_user_framebuffer_create, - .output_poll_changed = psbfb_output_poll_changed, + .output_poll_changed = drm_fb_helper_output_poll_changed, }; static void psb_setup_outputs(struct drm_device *dev) diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index 8f5cc1f471cd..38d09d4b3ed5 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -107,19 +107,6 @@ MODULE_DEVICE_TABLE(pci, pciidlist); static const struct drm_ioctl_desc psb_ioctls[] = { }; -static void psb_driver_lastclose(struct drm_device *dev) -{ - int ret; - struct drm_psb_private *dev_priv = dev->dev_private; - struct psb_fbdev *fbdev = dev_priv->fbdev; - - ret = drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev->psb_fb_helper); - if (ret) - DRM_DEBUG("failed to restore crtc mode\n"); - - return; -} - static int psb_do_init(struct drm_device *dev) { struct drm_psb_private *dev_priv = dev->dev_private; @@ -479,7 +466,7 @@ static struct drm_driver driver = { DRIVER_MODESET | DRIVER_GEM, .load = psb_driver_load, .unload = psb_driver_unload, - .lastclose = psb_driver_lastclose, + .lastclose = drm_fb_helper_lastclose, .num_ioctls = ARRAY_SIZE(psb_ioctls), .irq_preinstall = psb_irq_preinstall, diff --git a/drivers/gpu/drm/i915/Kconfig.debug b/drivers/gpu/drm/i915/Kconfig.debug index 9e53edbc713b..fa36491495b1 100644 --- a/drivers/gpu/drm/i915/Kconfig.debug +++ b/drivers/gpu/drm/i915/Kconfig.debug @@ -18,6 +18,7 @@ config DRM_I915_WERROR config DRM_I915_DEBUG bool "Enable additional driver debugging" depends on DRM_I915 + select DEBUG_FS select PREEMPT_COUNT select I2C_CHARDEV select DRM_DP_AUX_CHARDEV diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 49b9535e40d1..091aef281963 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -46,6 +46,7 @@ i915-y := i915_drv.o \ i915-$(CONFIG_COMPAT) += i915_ioc32.o i915-$(CONFIG_DEBUG_FS) += i915_debugfs.o intel_pipe_crc.o +i915-$(CONFIG_PERF_EVENTS) += i915_pmu.o # GEM code i915-y += i915_cmd_parser.o \ diff --git a/drivers/gpu/drm/i915/gvt/Makefile b/drivers/gpu/drm/i915/gvt/Makefile index 18e1c172e792..347116faa558 100644 --- a/drivers/gpu/drm/i915/gvt/Makefile +++ b/drivers/gpu/drm/i915/gvt/Makefile @@ -2,7 +2,8 @@ GVT_DIR := gvt GVT_SOURCE := gvt.o aperture_gm.o handlers.o vgpu.o trace_points.o firmware.o \ interrupt.o gtt.o cfg_space.o opregion.o mmio.o display.o edid.o \ - execlist.o scheduler.o sched_policy.o render.o cmd_parser.o debugfs.o + execlist.o scheduler.o sched_policy.o mmio_context.o cmd_parser.o debugfs.o \ + fb_decoder.o dmabuf.o ccflags-y += -I$(src) -I$(src)/$(GVT_DIR) i915-y += $(addprefix $(GVT_DIR)/, $(GVT_SOURCE)) diff --git a/drivers/gpu/drm/i915/gvt/cfg_space.c b/drivers/gpu/drm/i915/gvt/cfg_space.c index 4ce2e6bd0680..97bfc00d2a82 100644 --- a/drivers/gpu/drm/i915/gvt/cfg_space.c +++ b/drivers/gpu/drm/i915/gvt/cfg_space.c @@ -335,7 +335,8 @@ int intel_vgpu_emulate_cfg_write(struct intel_vgpu *vgpu, unsigned int offset, case INTEL_GVT_PCI_OPREGION: if (WARN_ON(!IS_ALIGNED(offset, 4))) return -EINVAL; - ret = intel_vgpu_init_opregion(vgpu, *(u32 *)p_data); + ret = intel_vgpu_opregion_base_write_handler(vgpu, + *(u32 *)p_data); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/gvt/display.c b/drivers/gpu/drm/i915/gvt/display.c index 355120865efd..09185036bac8 100644 --- a/drivers/gpu/drm/i915/gvt/display.c +++ b/drivers/gpu/drm/i915/gvt/display.c @@ -67,7 +67,7 @@ static int edp_pipe_is_enabled(struct intel_vgpu *vgpu) return 1; } -static int pipe_is_enabled(struct intel_vgpu *vgpu, int pipe) +int pipe_is_enabled(struct intel_vgpu *vgpu, int pipe) { struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; diff --git a/drivers/gpu/drm/i915/gvt/display.h b/drivers/gpu/drm/i915/gvt/display.h index d73de22102e2..b46b86892d58 100644 --- a/drivers/gpu/drm/i915/gvt/display.h +++ b/drivers/gpu/drm/i915/gvt/display.h @@ -179,4 +179,6 @@ int intel_vgpu_init_display(struct intel_vgpu *vgpu, u64 resolution); void intel_vgpu_reset_display(struct intel_vgpu *vgpu); void intel_vgpu_clean_display(struct intel_vgpu *vgpu); +int pipe_is_enabled(struct intel_vgpu *vgpu, int pipe); + #endif diff --git a/drivers/gpu/drm/i915/gvt/dmabuf.c b/drivers/gpu/drm/i915/gvt/dmabuf.c new file mode 100644 index 000000000000..2ab584f97dfb --- /dev/null +++ b/drivers/gpu/drm/i915/gvt/dmabuf.c @@ -0,0 +1,537 @@ +/* + * Copyright 2017 Intel 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 (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Zhiyuan Lv <zhiyuan.lv@intel.com> + * + * Contributors: + * Xiaoguang Chen + * Tina Zhang <tina.zhang@intel.com> + */ + +#include <linux/dma-buf.h> +#include <drm/drmP.h> +#include <linux/vfio.h> + +#include "i915_drv.h" +#include "gvt.h" + +#define GEN8_DECODE_PTE(pte) (pte & GENMASK_ULL(63, 12)) + +static int vgpu_gem_get_pages( + struct drm_i915_gem_object *obj) +{ + struct drm_i915_private *dev_priv = to_i915(obj->base.dev); + struct sg_table *st; + struct scatterlist *sg; + int i, ret; + gen8_pte_t __iomem *gtt_entries; + struct intel_vgpu_fb_info *fb_info; + + fb_info = (struct intel_vgpu_fb_info *)obj->gvt_info; + if (WARN_ON(!fb_info)) + return -ENODEV; + + st = kmalloc(sizeof(*st), GFP_KERNEL); + if (unlikely(!st)) + return -ENOMEM; + + ret = sg_alloc_table(st, fb_info->size, GFP_KERNEL); + if (ret) { + kfree(st); + return ret; + } + gtt_entries = (gen8_pte_t __iomem *)dev_priv->ggtt.gsm + + (fb_info->start >> PAGE_SHIFT); + for_each_sg(st->sgl, sg, fb_info->size, i) { + sg->offset = 0; + sg->length = PAGE_SIZE; + sg_dma_address(sg) = + GEN8_DECODE_PTE(readq(>t_entries[i])); + sg_dma_len(sg) = PAGE_SIZE; + } + + __i915_gem_object_set_pages(obj, st, PAGE_SIZE); + + return 0; +} + +static void vgpu_gem_put_pages(struct drm_i915_gem_object *obj, + struct sg_table *pages) +{ + sg_free_table(pages); + kfree(pages); +} + +static void dmabuf_gem_object_free(struct kref *kref) +{ + struct intel_vgpu_dmabuf_obj *obj = + container_of(kref, struct intel_vgpu_dmabuf_obj, kref); + struct intel_vgpu *vgpu = obj->vgpu; + struct list_head *pos; + struct intel_vgpu_dmabuf_obj *dmabuf_obj; + + if (vgpu && vgpu->active && !list_empty(&vgpu->dmabuf_obj_list_head)) { + list_for_each(pos, &vgpu->dmabuf_obj_list_head) { + dmabuf_obj = container_of(pos, + struct intel_vgpu_dmabuf_obj, list); + if (dmabuf_obj == obj) { + intel_gvt_hypervisor_put_vfio_device(vgpu); + idr_remove(&vgpu->object_idr, + dmabuf_obj->dmabuf_id); + kfree(dmabuf_obj->info); + kfree(dmabuf_obj); + list_del(pos); + break; + } + } + } else { + /* Free the orphan dmabuf_objs here */ + kfree(obj->info); + kfree(obj); + } +} + + +static inline void dmabuf_obj_get(struct intel_vgpu_dmabuf_obj *obj) +{ + kref_get(&obj->kref); +} + +static inline void dmabuf_obj_put(struct intel_vgpu_dmabuf_obj *obj) +{ + kref_put(&obj->kref, dmabuf_gem_object_free); +} + +static void vgpu_gem_release(struct drm_i915_gem_object *gem_obj) +{ + + struct intel_vgpu_fb_info *fb_info = gem_obj->gvt_info; + struct intel_vgpu_dmabuf_obj *obj = fb_info->obj; + struct intel_vgpu *vgpu = obj->vgpu; + + if (vgpu) { + mutex_lock(&vgpu->dmabuf_lock); + gem_obj->base.dma_buf = NULL; + dmabuf_obj_put(obj); + mutex_unlock(&vgpu->dmabuf_lock); + } else { + /* vgpu is NULL, as it has been removed already */ + gem_obj->base.dma_buf = NULL; + dmabuf_obj_put(obj); + } +} + +static const struct drm_i915_gem_object_ops intel_vgpu_gem_ops = { + .flags = I915_GEM_OBJECT_IS_PROXY, + .get_pages = vgpu_gem_get_pages, + .put_pages = vgpu_gem_put_pages, + .release = vgpu_gem_release, +}; + +static struct drm_i915_gem_object *vgpu_create_gem(struct drm_device *dev, + struct intel_vgpu_fb_info *info) +{ + struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_gem_object *obj; + + obj = i915_gem_object_alloc(dev_priv); + if (obj == NULL) + return NULL; + + drm_gem_private_object_init(dev, &obj->base, + info->size << PAGE_SHIFT); + i915_gem_object_init(obj, &intel_vgpu_gem_ops); + + obj->base.read_domains = I915_GEM_DOMAIN_GTT; + obj->base.write_domain = 0; + if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) { + unsigned int tiling_mode = 0; + unsigned int stride = 0; + + switch (info->drm_format_mod << 10) { + case PLANE_CTL_TILED_LINEAR: + tiling_mode = I915_TILING_NONE; + break; + case PLANE_CTL_TILED_X: + tiling_mode = I915_TILING_X; + stride = info->stride; + break; + case PLANE_CTL_TILED_Y: + tiling_mode = I915_TILING_Y; + stride = info->stride; + break; + default: + gvt_dbg_core("not supported tiling mode\n"); + } + obj->tiling_and_stride = tiling_mode | stride; + } else { + obj->tiling_and_stride = info->drm_format_mod ? + I915_TILING_X : 0; + } + + return obj; +} + +static int vgpu_get_plane_info(struct drm_device *dev, + struct intel_vgpu *vgpu, + struct intel_vgpu_fb_info *info, + int plane_id) +{ + struct drm_i915_private *dev_priv = to_i915(dev); + struct intel_vgpu_primary_plane_format p; + struct intel_vgpu_cursor_plane_format c; + int ret; + + if (plane_id == DRM_PLANE_TYPE_PRIMARY) { + ret = intel_vgpu_decode_primary_plane(vgpu, &p); + if (ret) + return ret; + info->start = p.base; + info->start_gpa = p.base_gpa; + info->width = p.width; + info->height = p.height; + info->stride = p.stride; + info->drm_format = p.drm_format; + info->drm_format_mod = p.tiled; + info->size = (((p.stride * p.height * p.bpp) / 8) + + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + } else if (plane_id == DRM_PLANE_TYPE_CURSOR) { + ret = intel_vgpu_decode_cursor_plane(vgpu, &c); + if (ret) + return ret; + info->start = c.base; + info->start_gpa = c.base_gpa; + info->width = c.width; + info->height = c.height; + info->stride = c.width * (c.bpp / 8); + info->drm_format = c.drm_format; + info->drm_format_mod = 0; + info->x_pos = c.x_pos; + info->y_pos = c.y_pos; + + /* The invalid cursor hotspot value is delivered to host + * until we find a way to get the cursor hotspot info of + * guest OS. + */ + info->x_hot = UINT_MAX; + info->y_hot = UINT_MAX; + info->size = (((info->stride * c.height * c.bpp) / 8) + + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + } else { + gvt_vgpu_err("invalid plane id:%d\n", plane_id); + return -EINVAL; + } + + if (info->size == 0) { + gvt_vgpu_err("fb size is zero\n"); + return -EINVAL; + } + + if (info->start & (PAGE_SIZE - 1)) { + gvt_vgpu_err("Not aligned fb address:0x%llx\n", info->start); + return -EFAULT; + } + if (((info->start >> PAGE_SHIFT) + info->size) > + ggtt_total_entries(&dev_priv->ggtt)) { + gvt_vgpu_err("Invalid GTT offset or size\n"); + return -EFAULT; + } + + if (!intel_gvt_ggtt_validate_range(vgpu, info->start, info->size)) { + gvt_vgpu_err("invalid gma addr\n"); + return -EFAULT; + } + + return 0; +} + +static struct intel_vgpu_dmabuf_obj * +pick_dmabuf_by_info(struct intel_vgpu *vgpu, + struct intel_vgpu_fb_info *latest_info) +{ + struct list_head *pos; + struct intel_vgpu_fb_info *fb_info; + struct intel_vgpu_dmabuf_obj *dmabuf_obj = NULL; + struct intel_vgpu_dmabuf_obj *ret = NULL; + + list_for_each(pos, &vgpu->dmabuf_obj_list_head) { + dmabuf_obj = container_of(pos, struct intel_vgpu_dmabuf_obj, + list); + if ((dmabuf_obj == NULL) || + (dmabuf_obj->info == NULL)) + continue; + + fb_info = (struct intel_vgpu_fb_info *)dmabuf_obj->info; + if ((fb_info->start == latest_info->start) && + (fb_info->start_gpa == latest_info->start_gpa) && + (fb_info->size == latest_info->size) && + (fb_info->drm_format_mod == latest_info->drm_format_mod) && + (fb_info->drm_format == latest_info->drm_format) && + (fb_info->width == latest_info->width) && + (fb_info->height == latest_info->height)) { + ret = dmabuf_obj; + break; + } + } + + return ret; +} + +static struct intel_vgpu_dmabuf_obj * +pick_dmabuf_by_num(struct intel_vgpu *vgpu, u32 id) +{ + struct list_head *pos; + struct intel_vgpu_dmabuf_obj *dmabuf_obj = NULL; + struct intel_vgpu_dmabuf_obj *ret = NULL; + + list_for_each(pos, &vgpu->dmabuf_obj_list_head) { + dmabuf_obj = container_of(pos, struct intel_vgpu_dmabuf_obj, + list); + if (!dmabuf_obj) + continue; + + if (dmabuf_obj->dmabuf_id == id) { + ret = dmabuf_obj; + break; + } + } + + return ret; +} + +static void update_fb_info(struct vfio_device_gfx_plane_info *gvt_dmabuf, + struct intel_vgpu_fb_info *fb_info) +{ + gvt_dmabuf->drm_format = fb_info->drm_format; + gvt_dmabuf->width = fb_info->width; + gvt_dmabuf->height = fb_info->height; + gvt_dmabuf->stride = fb_info->stride; + gvt_dmabuf->size = fb_info->size; + gvt_dmabuf->x_pos = fb_info->x_pos; + gvt_dmabuf->y_pos = fb_info->y_pos; + gvt_dmabuf->x_hot = fb_info->x_hot; + gvt_dmabuf->y_hot = fb_info->y_hot; +} + +int intel_vgpu_query_plane(struct intel_vgpu *vgpu, void *args) +{ + struct drm_device *dev = &vgpu->gvt->dev_priv->drm; + struct vfio_device_gfx_plane_info *gfx_plane_info = args; + struct intel_vgpu_dmabuf_obj *dmabuf_obj; + struct intel_vgpu_fb_info fb_info; + int ret = 0; + + if (gfx_plane_info->flags == (VFIO_GFX_PLANE_TYPE_DMABUF | + VFIO_GFX_PLANE_TYPE_PROBE)) + return ret; + else if ((gfx_plane_info->flags & ~VFIO_GFX_PLANE_TYPE_DMABUF) || + (!gfx_plane_info->flags)) + return -EINVAL; + + ret = vgpu_get_plane_info(dev, vgpu, &fb_info, + gfx_plane_info->drm_plane_type); + if (ret != 0) + goto out; + + mutex_lock(&vgpu->dmabuf_lock); + /* If exists, pick up the exposed dmabuf_obj */ + dmabuf_obj = pick_dmabuf_by_info(vgpu, &fb_info); + if (dmabuf_obj) { + update_fb_info(gfx_plane_info, &fb_info); + gfx_plane_info->dmabuf_id = dmabuf_obj->dmabuf_id; + + /* This buffer may be released between query_plane ioctl and + * get_dmabuf ioctl. Add the refcount to make sure it won't + * be released between the two ioctls. + */ + if (!dmabuf_obj->initref) { + dmabuf_obj->initref = true; + dmabuf_obj_get(dmabuf_obj); + } + ret = 0; + gvt_dbg_dpy("vgpu%d: re-use dmabuf_obj ref %d, id %d\n", + vgpu->id, kref_read(&dmabuf_obj->kref), + gfx_plane_info->dmabuf_id); + mutex_unlock(&vgpu->dmabuf_lock); + goto out; + } + + mutex_unlock(&vgpu->dmabuf_lock); + + /* Need to allocate a new one*/ + dmabuf_obj = kmalloc(sizeof(struct intel_vgpu_dmabuf_obj), GFP_KERNEL); + if (unlikely(!dmabuf_obj)) { + gvt_vgpu_err("alloc dmabuf_obj failed\n"); + ret = -ENOMEM; + goto out; + } + + dmabuf_obj->info = kmalloc(sizeof(struct intel_vgpu_fb_info), + GFP_KERNEL); + if (unlikely(!dmabuf_obj->info)) { + gvt_vgpu_err("allocate intel vgpu fb info failed\n"); + ret = -ENOMEM; + goto out_free_dmabuf; + } + memcpy(dmabuf_obj->info, &fb_info, sizeof(struct intel_vgpu_fb_info)); + + ((struct intel_vgpu_fb_info *)dmabuf_obj->info)->obj = dmabuf_obj; + + dmabuf_obj->vgpu = vgpu; + + ret = idr_alloc(&vgpu->object_idr, dmabuf_obj, 1, 0, GFP_NOWAIT); + if (ret < 0) + goto out_free_info; + gfx_plane_info->dmabuf_id = ret; + dmabuf_obj->dmabuf_id = ret; + + dmabuf_obj->initref = true; + + kref_init(&dmabuf_obj->kref); + + mutex_lock(&vgpu->dmabuf_lock); + if (intel_gvt_hypervisor_get_vfio_device(vgpu)) { + gvt_vgpu_err("get vfio device failed\n"); + mutex_unlock(&vgpu->dmabuf_lock); + goto out_free_info; + } + mutex_unlock(&vgpu->dmabuf_lock); + + update_fb_info(gfx_plane_info, &fb_info); + + INIT_LIST_HEAD(&dmabuf_obj->list); + mutex_lock(&vgpu->dmabuf_lock); + list_add_tail(&dmabuf_obj->list, &vgpu->dmabuf_obj_list_head); + mutex_unlock(&vgpu->dmabuf_lock); + + gvt_dbg_dpy("vgpu%d: %s new dmabuf_obj ref %d, id %d\n", vgpu->id, + __func__, kref_read(&dmabuf_obj->kref), ret); + + return 0; + +out_free_info: + kfree(dmabuf_obj->info); +out_free_dmabuf: + kfree(dmabuf_obj); +out: + /* ENODEV means plane isn't ready, which might be a normal case. */ + return (ret == -ENODEV) ? 0 : ret; +} + +/* To associate an exposed dmabuf with the dmabuf_obj */ +int intel_vgpu_get_dmabuf(struct intel_vgpu *vgpu, unsigned int dmabuf_id) +{ + struct drm_device *dev = &vgpu->gvt->dev_priv->drm; + struct intel_vgpu_dmabuf_obj *dmabuf_obj; + struct drm_i915_gem_object *obj; + struct dma_buf *dmabuf; + int dmabuf_fd; + int ret = 0; + + mutex_lock(&vgpu->dmabuf_lock); + + dmabuf_obj = pick_dmabuf_by_num(vgpu, dmabuf_id); + if (dmabuf_obj == NULL) { + gvt_vgpu_err("invalid dmabuf id:%d\n", dmabuf_id); + ret = -EINVAL; + goto out; + } + + obj = vgpu_create_gem(dev, dmabuf_obj->info); + if (obj == NULL) { + gvt_vgpu_err("create gvt gem obj failed:%d\n", vgpu->id); + ret = -ENOMEM; + goto out; + } + + obj->gvt_info = dmabuf_obj->info; + + dmabuf = i915_gem_prime_export(dev, &obj->base, DRM_CLOEXEC | DRM_RDWR); + if (IS_ERR(dmabuf)) { + gvt_vgpu_err("export dma-buf failed\n"); + ret = PTR_ERR(dmabuf); + goto out_free_gem; + } + obj->base.dma_buf = dmabuf; + + i915_gem_object_put(obj); + + ret = dma_buf_fd(dmabuf, DRM_CLOEXEC | DRM_RDWR); + if (ret < 0) { + gvt_vgpu_err("create dma-buf fd failed ret:%d\n", ret); + goto out_free_dmabuf; + } + dmabuf_fd = ret; + + dmabuf_obj_get(dmabuf_obj); + + if (dmabuf_obj->initref) { + dmabuf_obj->initref = false; + dmabuf_obj_put(dmabuf_obj); + } + + mutex_unlock(&vgpu->dmabuf_lock); + + gvt_dbg_dpy("vgpu%d: dmabuf:%d, dmabuf ref %d, fd:%d\n" + " file count: %ld, GEM ref: %d\n", + vgpu->id, dmabuf_obj->dmabuf_id, + kref_read(&dmabuf_obj->kref), + dmabuf_fd, + file_count(dmabuf->file), + kref_read(&obj->base.refcount)); + + return dmabuf_fd; + +out_free_dmabuf: + dma_buf_put(dmabuf); +out_free_gem: + i915_gem_object_put(obj); +out: + mutex_unlock(&vgpu->dmabuf_lock); + return ret; +} + +void intel_vgpu_dmabuf_cleanup(struct intel_vgpu *vgpu) +{ + struct list_head *pos, *n; + struct intel_vgpu_dmabuf_obj *dmabuf_obj; + + mutex_lock(&vgpu->dmabuf_lock); + list_for_each_safe(pos, n, &vgpu->dmabuf_obj_list_head) { + dmabuf_obj = container_of(pos, struct intel_vgpu_dmabuf_obj, + list); + dmabuf_obj->vgpu = NULL; + + idr_remove(&vgpu->object_idr, dmabuf_obj->dmabuf_id); + intel_gvt_hypervisor_put_vfio_device(vgpu); + list_del(pos); + + /* dmabuf_obj might be freed in dmabuf_obj_put */ + if (dmabuf_obj->initref) { + dmabuf_obj->initref = false; + dmabuf_obj_put(dmabuf_obj); + } + + } + mutex_unlock(&vgpu->dmabuf_lock); +} diff --git a/drivers/gpu/drm/i915/gvt/dmabuf.h b/drivers/gpu/drm/i915/gvt/dmabuf.h new file mode 100644 index 000000000000..5f8f03fb1d1b --- /dev/null +++ b/drivers/gpu/drm/i915/gvt/dmabuf.h @@ -0,0 +1,67 @@ +/* + * Copyright(c) 2017 Intel 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 (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Zhiyuan Lv <zhiyuan.lv@intel.com> + * + * Contributors: + * Xiaoguang Chen + * Tina Zhang <tina.zhang@intel.com> + */ + +#ifndef _GVT_DMABUF_H_ +#define _GVT_DMABUF_H_ +#include <linux/vfio.h> + +struct intel_vgpu_fb_info { + __u64 start; + __u64 start_gpa; + __u64 drm_format_mod; + __u32 drm_format; /* drm format of plane */ + __u32 width; /* width of plane */ + __u32 height; /* height of plane */ + __u32 stride; /* stride of plane */ + __u32 size; /* size of plane in bytes, align on page */ + __u32 x_pos; /* horizontal position of cursor plane */ + __u32 y_pos; /* vertical position of cursor plane */ + __u32 x_hot; /* horizontal position of cursor hotspot */ + __u32 y_hot; /* vertical position of cursor hotspot */ + struct intel_vgpu_dmabuf_obj *obj; +}; + +/** + * struct intel_vgpu_dmabuf_obj- Intel vGPU device buffer object + */ +struct intel_vgpu_dmabuf_obj { + struct intel_vgpu *vgpu; + struct intel_vgpu_fb_info *info; + __u32 dmabuf_id; + struct kref kref; + bool initref; + struct list_head list; +}; + +int intel_vgpu_query_plane(struct intel_vgpu *vgpu, void *args); +int intel_vgpu_get_dmabuf(struct intel_vgpu *vgpu, unsigned int dmabuf_id); +void intel_vgpu_dmabuf_cleanup(struct intel_vgpu *vgpu); + +#endif diff --git a/drivers/gpu/drm/i915/gvt/execlist.c b/drivers/gpu/drm/i915/gvt/execlist.c index c9fa0fb488d3..769c1c24ae75 100644 --- a/drivers/gpu/drm/i915/gvt/execlist.c +++ b/drivers/gpu/drm/i915/gvt/execlist.c @@ -458,7 +458,7 @@ static int submit_context(struct intel_vgpu *vgpu, int ring_id, gvt_dbg_el("workload %p emulate schedule_in %d\n", workload, emulate_schedule_in); - queue_workload(workload); + intel_vgpu_queue_workload(workload); return 0; } @@ -528,7 +528,7 @@ static void init_vgpu_execlist(struct intel_vgpu *vgpu, int ring_id) vgpu_vreg(vgpu, ctx_status_ptr_reg) = ctx_status_ptr.dw; } -void clean_execlist(struct intel_vgpu *vgpu) +static void clean_execlist(struct intel_vgpu *vgpu) { enum intel_engine_id i; struct intel_engine_cs *engine; @@ -542,7 +542,7 @@ void clean_execlist(struct intel_vgpu *vgpu) } } -void reset_execlist(struct intel_vgpu *vgpu, +static void reset_execlist(struct intel_vgpu *vgpu, unsigned long engine_mask) { struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; @@ -553,7 +553,7 @@ void reset_execlist(struct intel_vgpu *vgpu, init_vgpu_execlist(vgpu, engine->id); } -int init_execlist(struct intel_vgpu *vgpu) +static int init_execlist(struct intel_vgpu *vgpu) { reset_execlist(vgpu, ALL_ENGINES); return 0; diff --git a/drivers/gpu/drm/i915/gvt/fb_decoder.c b/drivers/gpu/drm/i915/gvt/fb_decoder.c new file mode 100644 index 000000000000..6cc99543693f --- /dev/null +++ b/drivers/gpu/drm/i915/gvt/fb_decoder.c @@ -0,0 +1,514 @@ +/* + * Copyright(c) 2011-2016 Intel 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 (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Kevin Tian <kevin.tian@intel.com> + * + * Contributors: + * Bing Niu <bing.niu@intel.com> + * Xu Han <xu.han@intel.com> + * Ping Gao <ping.a.gao@intel.com> + * Xiaoguang Chen <xiaoguang.chen@intel.com> + * Yang Liu <yang2.liu@intel.com> + * Tina Zhang <tina.zhang@intel.com> + * + */ + +#include <uapi/drm/drm_fourcc.h> +#include "i915_drv.h" +#include "gvt.h" + +#define PRIMARY_FORMAT_NUM 16 +struct pixel_format { + int drm_format; /* Pixel format in DRM definition */ + int bpp; /* Bits per pixel, 0 indicates invalid */ + char *desc; /* The description */ +}; + +static struct pixel_format bdw_pixel_formats[] = { + {DRM_FORMAT_C8, 8, "8-bit Indexed"}, + {DRM_FORMAT_RGB565, 16, "16-bit BGRX (5:6:5 MSB-R:G:B)"}, + {DRM_FORMAT_XRGB8888, 32, "32-bit BGRX (8:8:8:8 MSB-X:R:G:B)"}, + {DRM_FORMAT_XBGR2101010, 32, "32-bit RGBX (2:10:10:10 MSB-X:B:G:R)"}, + + {DRM_FORMAT_XRGB2101010, 32, "32-bit BGRX (2:10:10:10 MSB-X:R:G:B)"}, + {DRM_FORMAT_XBGR8888, 32, "32-bit RGBX (8:8:8:8 MSB-X:B:G:R)"}, + + /* non-supported format has bpp default to 0 */ + {0, 0, NULL}, +}; + +static struct pixel_format skl_pixel_formats[] = { + {DRM_FORMAT_YUYV, 16, "16-bit packed YUYV (8:8:8:8 MSB-V:Y2:U:Y1)"}, + {DRM_FORMAT_UYVY, 16, "16-bit packed UYVY (8:8:8:8 MSB-Y2:V:Y1:U)"}, + {DRM_FORMAT_YVYU, 16, "16-bit packed YVYU (8:8:8:8 MSB-U:Y2:V:Y1)"}, + {DRM_FORMAT_VYUY, 16, "16-bit packed VYUY (8:8:8:8 MSB-Y2:U:Y1:V)"}, + + {DRM_FORMAT_C8, 8, "8-bit Indexed"}, + {DRM_FORMAT_RGB565, 16, "16-bit BGRX (5:6:5 MSB-R:G:B)"}, + {DRM_FORMAT_ABGR8888, 32, "32-bit RGBA (8:8:8:8 MSB-A:B:G:R)"}, + {DRM_FORMAT_XBGR8888, 32, "32-bit RGBX (8:8:8:8 MSB-X:B:G:R)"}, + + {DRM_FORMAT_ARGB8888, 32, "32-bit BGRA (8:8:8:8 MSB-A:R:G:B)"}, + {DRM_FORMAT_XRGB8888, 32, "32-bit BGRX (8:8:8:8 MSB-X:R:G:B)"}, + {DRM_FORMAT_XBGR2101010, 32, "32-bit RGBX (2:10:10:10 MSB-X:B:G:R)"}, + {DRM_FORMAT_XRGB2101010, 32, "32-bit BGRX (2:10:10:10 MSB-X:R:G:B)"}, + + /* non-supported format has bpp default to 0 */ + {0, 0, NULL}, +}; + +static int bdw_format_to_drm(int format) +{ + int bdw_pixel_formats_index = 6; + + switch (format) { + case DISPPLANE_8BPP: + bdw_pixel_formats_index = 0; + break; + case DISPPLANE_BGRX565: + bdw_pixel_formats_index = 1; + break; + case DISPPLANE_BGRX888: + bdw_pixel_formats_index = 2; + break; + case DISPPLANE_RGBX101010: + bdw_pixel_formats_index = 3; + break; + case DISPPLANE_BGRX101010: + bdw_pixel_formats_index = 4; + break; + case DISPPLANE_RGBX888: + bdw_pixel_formats_index = 5; + break; + + default: + break; + } + + return bdw_pixel_formats_index; +} + +static int skl_format_to_drm(int format, bool rgb_order, bool alpha, + int yuv_order) +{ + int skl_pixel_formats_index = 12; + + switch (format) { + case PLANE_CTL_FORMAT_INDEXED: + skl_pixel_formats_index = 4; + break; + case PLANE_CTL_FORMAT_RGB_565: + skl_pixel_formats_index = 5; + break; + case PLANE_CTL_FORMAT_XRGB_8888: + if (rgb_order) + skl_pixel_formats_index = alpha ? 6 : 7; + else + skl_pixel_formats_index = alpha ? 8 : 9; + break; + case PLANE_CTL_FORMAT_XRGB_2101010: + skl_pixel_formats_index = rgb_order ? 10 : 11; + break; + case PLANE_CTL_FORMAT_YUV422: + skl_pixel_formats_index = yuv_order >> 16; + if (skl_pixel_formats_index > 3) + return -EINVAL; + break; + + default: + break; + } + + return skl_pixel_formats_index; +} + +static u32 intel_vgpu_get_stride(struct intel_vgpu *vgpu, int pipe, + u32 tiled, int stride_mask, int bpp) +{ + struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; + + u32 stride_reg = vgpu_vreg(vgpu, DSPSTRIDE(pipe)) & stride_mask; + u32 stride = stride_reg; + + if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) { + switch (tiled) { + case PLANE_CTL_TILED_LINEAR: + stride = stride_reg * 64; + break; + case PLANE_CTL_TILED_X: + stride = stride_reg * 512; + break; + case PLANE_CTL_TILED_Y: + stride = stride_reg * 128; + break; + case PLANE_CTL_TILED_YF: + if (bpp == 8) + stride = stride_reg * 64; + else if (bpp == 16 || bpp == 32 || bpp == 64) + stride = stride_reg * 128; + else + gvt_dbg_core("skl: unsupported bpp:%d\n", bpp); + break; + default: + gvt_dbg_core("skl: unsupported tile format:%x\n", + tiled); + } + } + + return stride; +} + +static int get_active_pipe(struct intel_vgpu *vgpu) +{ + int i; + + for (i = 0; i < I915_MAX_PIPES; i++) + if (pipe_is_enabled(vgpu, i)) + break; + + return i; +} + +/** + * intel_vgpu_decode_primary_plane - Decode primary plane + * @vgpu: input vgpu + * @plane: primary plane to save decoded info + * This function is called for decoding plane + * + * Returns: + * 0 on success, non-zero if failed. + */ +int intel_vgpu_decode_primary_plane(struct intel_vgpu *vgpu, + struct intel_vgpu_primary_plane_format *plane) +{ + u32 val, fmt; + struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; + int pipe; + + pipe = get_active_pipe(vgpu); + if (pipe >= I915_MAX_PIPES) + return -ENODEV; + + val = vgpu_vreg(vgpu, DSPCNTR(pipe)); + plane->enabled = !!(val & DISPLAY_PLANE_ENABLE); + if (!plane->enabled) + return -ENODEV; + + if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) { + plane->tiled = (val & PLANE_CTL_TILED_MASK) >> + _PLANE_CTL_TILED_SHIFT; + fmt = skl_format_to_drm( + val & PLANE_CTL_FORMAT_MASK, + val & PLANE_CTL_ORDER_RGBX, + val & PLANE_CTL_ALPHA_MASK, + val & PLANE_CTL_YUV422_ORDER_MASK); + + if (fmt >= ARRAY_SIZE(skl_pixel_formats)) { + gvt_vgpu_err("Out-of-bounds pixel format index\n"); + return -EINVAL; + } + + plane->bpp = skl_pixel_formats[fmt].bpp; + plane->drm_format = skl_pixel_formats[fmt].drm_format; + } else { + plane->tiled = !!(val & DISPPLANE_TILED); + fmt = bdw_format_to_drm(val & DISPPLANE_PIXFORMAT_MASK); + plane->bpp = bdw_pixel_formats[fmt].bpp; + plane->drm_format = bdw_pixel_formats[fmt].drm_format; + } + + if (!plane->bpp) { + gvt_vgpu_err("Non-supported pixel format (0x%x)\n", fmt); + return -EINVAL; + } + + plane->hw_format = fmt; + + plane->base = vgpu_vreg(vgpu, DSPSURF(pipe)) & I915_GTT_PAGE_MASK; + if (!intel_gvt_ggtt_validate_range(vgpu, plane->base, 0)) { + gvt_vgpu_err("invalid gma address: %lx\n", + (unsigned long)plane->base); + return -EINVAL; + } + + plane->base_gpa = intel_vgpu_gma_to_gpa(vgpu->gtt.ggtt_mm, plane->base); + if (plane->base_gpa == INTEL_GVT_INVALID_ADDR) { + gvt_vgpu_err("invalid gma address: %lx\n", + (unsigned long)plane->base); + return -EINVAL; + } + + plane->stride = intel_vgpu_get_stride(vgpu, pipe, (plane->tiled << 10), + (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) ? + (_PRI_PLANE_STRIDE_MASK >> 6) : + _PRI_PLANE_STRIDE_MASK, plane->bpp); + + plane->width = (vgpu_vreg(vgpu, PIPESRC(pipe)) & _PIPE_H_SRCSZ_MASK) >> + _PIPE_H_SRCSZ_SHIFT; + plane->width += 1; + plane->height = (vgpu_vreg(vgpu, PIPESRC(pipe)) & + _PIPE_V_SRCSZ_MASK) >> _PIPE_V_SRCSZ_SHIFT; + plane->height += 1; /* raw height is one minus the real value */ + + val = vgpu_vreg(vgpu, DSPTILEOFF(pipe)); + plane->x_offset = (val & _PRI_PLANE_X_OFF_MASK) >> + _PRI_PLANE_X_OFF_SHIFT; + plane->y_offset = (val & _PRI_PLANE_Y_OFF_MASK) >> + _PRI_PLANE_Y_OFF_SHIFT; + + return 0; +} + +#define CURSOR_FORMAT_NUM (1 << 6) +struct cursor_mode_format { + int drm_format; /* Pixel format in DRM definition */ + u8 bpp; /* Bits per pixel; 0 indicates invalid */ + u32 width; /* In pixel */ + u32 height; /* In lines */ + char *desc; /* The description */ +}; + +static struct cursor_mode_format cursor_pixel_formats[] = { + {DRM_FORMAT_ARGB8888, 32, 128, 128, "128x128 32bpp ARGB"}, + {DRM_FORMAT_ARGB8888, 32, 256, 256, "256x256 32bpp ARGB"}, + {DRM_FORMAT_ARGB8888, 32, 64, 64, "64x64 32bpp ARGB"}, + {DRM_FORMAT_ARGB8888, 32, 64, 64, "64x64 32bpp ARGB"}, + + /* non-supported format has bpp default to 0 */ + {0, 0, 0, 0, NULL}, +}; + +static int cursor_mode_to_drm(int mode) +{ + int cursor_pixel_formats_index = 4; + + switch (mode) { + case CURSOR_MODE_128_ARGB_AX: + cursor_pixel_formats_index = 0; + break; + case CURSOR_MODE_256_ARGB_AX: + cursor_pixel_formats_index = 1; + break; + case CURSOR_MODE_64_ARGB_AX: + cursor_pixel_formats_index = 2; + break; + case CURSOR_MODE_64_32B_AX: + cursor_pixel_formats_index = 3; + break; + + default: + break; + } + + return cursor_pixel_formats_index; +} + +/** + * intel_vgpu_decode_cursor_plane - Decode sprite plane + * @vgpu: input vgpu + * @plane: cursor plane to save decoded info + * This function is called for decoding plane + * + * Returns: + * 0 on success, non-zero if failed. + */ +int intel_vgpu_decode_cursor_plane(struct intel_vgpu *vgpu, + struct intel_vgpu_cursor_plane_format *plane) +{ + u32 val, mode, index; + u32 alpha_plane, alpha_force; + struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; + int pipe; + + pipe = get_active_pipe(vgpu); + if (pipe >= I915_MAX_PIPES) + return -ENODEV; + + val = vgpu_vreg(vgpu, CURCNTR(pipe)); + mode = val & CURSOR_MODE; + plane->enabled = (mode != CURSOR_MODE_DISABLE); + if (!plane->enabled) + return -ENODEV; + + index = cursor_mode_to_drm(mode); + + if (!cursor_pixel_formats[index].bpp) { + gvt_vgpu_err("Non-supported cursor mode (0x%x)\n", mode); + return -EINVAL; + } + plane->mode = mode; + plane->bpp = cursor_pixel_formats[index].bpp; + plane->drm_format = cursor_pixel_formats[index].drm_format; + plane->width = cursor_pixel_formats[index].width; + plane->height = cursor_pixel_formats[index].height; + + alpha_plane = (val & _CURSOR_ALPHA_PLANE_MASK) >> + _CURSOR_ALPHA_PLANE_SHIFT; + alpha_force = (val & _CURSOR_ALPHA_FORCE_MASK) >> + _CURSOR_ALPHA_FORCE_SHIFT; + if (alpha_plane || alpha_force) + gvt_dbg_core("alpha_plane=0x%x, alpha_force=0x%x\n", + alpha_plane, alpha_force); + + plane->base = vgpu_vreg(vgpu, CURBASE(pipe)) & I915_GTT_PAGE_MASK; + if (!intel_gvt_ggtt_validate_range(vgpu, plane->base, 0)) { + gvt_vgpu_err("invalid gma address: %lx\n", + (unsigned long)plane->base); + return -EINVAL; + } + + plane->base_gpa = intel_vgpu_gma_to_gpa(vgpu->gtt.ggtt_mm, plane->base); + if (plane->base_gpa == INTEL_GVT_INVALID_ADDR) { + gvt_vgpu_err("invalid gma address: %lx\n", + (unsigned long)plane->base); + return -EINVAL; + } + + val = vgpu_vreg(vgpu, CURPOS(pipe)); + plane->x_pos = (val & _CURSOR_POS_X_MASK) >> _CURSOR_POS_X_SHIFT; + plane->x_sign = (val & _CURSOR_SIGN_X_MASK) >> _CURSOR_SIGN_X_SHIFT; + plane->y_pos = (val & _CURSOR_POS_Y_MASK) >> _CURSOR_POS_Y_SHIFT; + plane->y_sign = (val & _CURSOR_SIGN_Y_MASK) >> _CURSOR_SIGN_Y_SHIFT; + + return 0; +} + +#define SPRITE_FORMAT_NUM (1 << 3) + +static struct pixel_format sprite_pixel_formats[SPRITE_FORMAT_NUM] = { + [0x0] = {DRM_FORMAT_YUV422, 16, "YUV 16-bit 4:2:2 packed"}, + [0x1] = {DRM_FORMAT_XRGB2101010, 32, "RGB 32-bit 2:10:10:10"}, + [0x2] = {DRM_FORMAT_XRGB8888, 32, "RGB 32-bit 8:8:8:8"}, + [0x4] = {DRM_FORMAT_AYUV, 32, + "YUV 32-bit 4:4:4 packed (8:8:8:8 MSB-X:Y:U:V)"}, +}; + +/** + * intel_vgpu_decode_sprite_plane - Decode sprite plane + * @vgpu: input vgpu + * @plane: sprite plane to save decoded info + * This function is called for decoding plane + * + * Returns: + * 0 on success, non-zero if failed. + */ +int intel_vgpu_decode_sprite_plane(struct intel_vgpu *vgpu, + struct intel_vgpu_sprite_plane_format *plane) +{ + u32 val, fmt; + u32 color_order, yuv_order; + int drm_format; + int pipe; + + pipe = get_active_pipe(vgpu); + if (pipe >= I915_MAX_PIPES) + return -ENODEV; + + val = vgpu_vreg(vgpu, SPRCTL(pipe)); + plane->enabled = !!(val & SPRITE_ENABLE); + if (!plane->enabled) + return -ENODEV; + + plane->tiled = !!(val & SPRITE_TILED); + color_order = !!(val & SPRITE_RGB_ORDER_RGBX); + yuv_order = (val & SPRITE_YUV_BYTE_ORDER_MASK) >> + _SPRITE_YUV_ORDER_SHIFT; + + fmt = (val & SPRITE_PIXFORMAT_MASK) >> _SPRITE_FMT_SHIFT; + if (!sprite_pixel_formats[fmt].bpp) { + gvt_vgpu_err("Non-supported pixel format (0x%x)\n", fmt); + return -EINVAL; + } + plane->hw_format = fmt; + plane->bpp = sprite_pixel_formats[fmt].bpp; + drm_format = sprite_pixel_formats[fmt].drm_format; + + /* Order of RGB values in an RGBxxx buffer may be ordered RGB or + * BGR depending on the state of the color_order field + */ + if (!color_order) { + if (drm_format == DRM_FORMAT_XRGB2101010) + drm_format = DRM_FORMAT_XBGR2101010; + else if (drm_format == DRM_FORMAT_XRGB8888) + drm_format = DRM_FORMAT_XBGR8888; + } + + if (drm_format == DRM_FORMAT_YUV422) { + switch (yuv_order) { + case 0: + drm_format = DRM_FORMAT_YUYV; + break; + case 1: + drm_format = DRM_FORMAT_UYVY; + break; + case 2: + drm_format = DRM_FORMAT_YVYU; + break; + case 3: + drm_format = DRM_FORMAT_VYUY; + break; + default: + /* yuv_order has only 2 bits */ + break; + } + } + + plane->drm_format = drm_format; + + plane->base = vgpu_vreg(vgpu, SPRSURF(pipe)) & I915_GTT_PAGE_MASK; + if (!intel_gvt_ggtt_validate_range(vgpu, plane->base, 0)) { + gvt_vgpu_err("invalid gma address: %lx\n", + (unsigned long)plane->base); + return -EINVAL; + } + + plane->base_gpa = intel_vgpu_gma_to_gpa(vgpu->gtt.ggtt_mm, plane->base); + if (plane->base_gpa == INTEL_GVT_INVALID_ADDR) { + gvt_vgpu_err("invalid gma address: %lx\n", + (unsigned long)plane->base); + return -EINVAL; + } + + plane->stride = vgpu_vreg(vgpu, SPRSTRIDE(pipe)) & + _SPRITE_STRIDE_MASK; + + val = vgpu_vreg(vgpu, SPRSIZE(pipe)); + plane->height = (val & _SPRITE_SIZE_HEIGHT_MASK) >> + _SPRITE_SIZE_HEIGHT_SHIFT; + plane->width = (val & _SPRITE_SIZE_WIDTH_MASK) >> + _SPRITE_SIZE_WIDTH_SHIFT; + plane->height += 1; /* raw height is one minus the real value */ + plane->width += 1; /* raw width is one minus the real value */ + + val = vgpu_vreg(vgpu, SPRPOS(pipe)); + plane->x_pos = (val & _SPRITE_POS_X_MASK) >> _SPRITE_POS_X_SHIFT; + plane->y_pos = (val & _SPRITE_POS_Y_MASK) >> _SPRITE_POS_Y_SHIFT; + + val = vgpu_vreg(vgpu, SPROFFSET(pipe)); + plane->x_offset = (val & _SPRITE_OFFSET_START_X_MASK) >> + _SPRITE_OFFSET_START_X_SHIFT; + plane->y_offset = (val & _SPRITE_OFFSET_START_Y_MASK) >> + _SPRITE_OFFSET_START_Y_SHIFT; + + return 0; +} diff --git a/drivers/gpu/drm/i915/gvt/fb_decoder.h b/drivers/gpu/drm/i915/gvt/fb_decoder.h new file mode 100644 index 000000000000..cb055f3c81a2 --- /dev/null +++ b/drivers/gpu/drm/i915/gvt/fb_decoder.h @@ -0,0 +1,169 @@ +/* + * Copyright(c) 2011-2016 Intel 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 (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Kevin Tian <kevin.tian@intel.com> + * + * Contributors: + * Bing Niu <bing.niu@intel.com> + * Xu Han <xu.han@intel.com> + * Ping Gao <ping.a.gao@intel.com> + * Xiaoguang Chen <xiaoguang.chen@intel.com> + * Yang Liu <yang2.liu@intel.com> + * Tina Zhang <tina.zhang@intel.com> + * + */ + +#ifndef _GVT_FB_DECODER_H_ +#define _GVT_FB_DECODER_H_ + +#define _PLANE_CTL_FORMAT_SHIFT 24 +#define _PLANE_CTL_TILED_SHIFT 10 +#define _PIPE_V_SRCSZ_SHIFT 0 +#define _PIPE_V_SRCSZ_MASK (0xfff << _PIPE_V_SRCSZ_SHIFT) +#define _PIPE_H_SRCSZ_SHIFT 16 +#define _PIPE_H_SRCSZ_MASK (0x1fff << _PIPE_H_SRCSZ_SHIFT) + +#define _PRI_PLANE_FMT_SHIFT 26 +#define _PRI_PLANE_STRIDE_MASK (0x3ff << 6) +#define _PRI_PLANE_X_OFF_SHIFT 0 +#define _PRI_PLANE_X_OFF_MASK (0x1fff << _PRI_PLANE_X_OFF_SHIFT) +#define _PRI_PLANE_Y_OFF_SHIFT 16 +#define _PRI_PLANE_Y_OFF_MASK (0xfff << _PRI_PLANE_Y_OFF_SHIFT) + +#define _CURSOR_MODE 0x3f +#define _CURSOR_ALPHA_FORCE_SHIFT 8 +#define _CURSOR_ALPHA_FORCE_MASK (0x3 << _CURSOR_ALPHA_FORCE_SHIFT) +#define _CURSOR_ALPHA_PLANE_SHIFT 10 +#define _CURSOR_ALPHA_PLANE_MASK (0x3 << _CURSOR_ALPHA_PLANE_SHIFT) +#define _CURSOR_POS_X_SHIFT 0 +#define _CURSOR_POS_X_MASK (0x1fff << _CURSOR_POS_X_SHIFT) +#define _CURSOR_SIGN_X_SHIFT 15 +#define _CURSOR_SIGN_X_MASK (1 << _CURSOR_SIGN_X_SHIFT) +#define _CURSOR_POS_Y_SHIFT 16 +#define _CURSOR_POS_Y_MASK (0xfff << _CURSOR_POS_Y_SHIFT) +#define _CURSOR_SIGN_Y_SHIFT 31 +#define _CURSOR_SIGN_Y_MASK (1 << _CURSOR_SIGN_Y_SHIFT) + +#define _SPRITE_FMT_SHIFT 25 +#define _SPRITE_COLOR_ORDER_SHIFT 20 +#define _SPRITE_YUV_ORDER_SHIFT 16 +#define _SPRITE_STRIDE_SHIFT 6 +#define _SPRITE_STRIDE_MASK (0x1ff << _SPRITE_STRIDE_SHIFT) +#define _SPRITE_SIZE_WIDTH_SHIFT 0 +#define _SPRITE_SIZE_HEIGHT_SHIFT 16 +#define _SPRITE_SIZE_WIDTH_MASK (0x1fff << _SPRITE_SIZE_WIDTH_SHIFT) +#define _SPRITE_SIZE_HEIGHT_MASK (0xfff << _SPRITE_SIZE_HEIGHT_SHIFT) +#define _SPRITE_POS_X_SHIFT 0 +#define _SPRITE_POS_Y_SHIFT 16 +#define _SPRITE_POS_X_MASK (0x1fff << _SPRITE_POS_X_SHIFT) +#define _SPRITE_POS_Y_MASK (0xfff << _SPRITE_POS_Y_SHIFT) +#define _SPRITE_OFFSET_START_X_SHIFT 0 +#define _SPRITE_OFFSET_START_Y_SHIFT 16 +#define _SPRITE_OFFSET_START_X_MASK (0x1fff << _SPRITE_OFFSET_START_X_SHIFT) +#define _SPRITE_OFFSET_START_Y_MASK (0xfff << _SPRITE_OFFSET_START_Y_SHIFT) + +enum GVT_FB_EVENT { + FB_MODE_SET_START = 1, + FB_MODE_SET_END, + FB_DISPLAY_FLIP, +}; + +enum DDI_PORT { + DDI_PORT_NONE = 0, + DDI_PORT_B = 1, + DDI_PORT_C = 2, + DDI_PORT_D = 3, + DDI_PORT_E = 4 +}; + +struct intel_gvt; + +/* color space conversion and gamma correction are not included */ +struct intel_vgpu_primary_plane_format { + u8 enabled; /* plane is enabled */ + u8 tiled; /* X-tiled */ + u8 bpp; /* bits per pixel */ + u32 hw_format; /* format field in the PRI_CTL register */ + u32 drm_format; /* format in DRM definition */ + u32 base; /* framebuffer base in graphics memory */ + u64 base_gpa; + u32 x_offset; /* in pixels */ + u32 y_offset; /* in lines */ + u32 width; /* in pixels */ + u32 height; /* in lines */ + u32 stride; /* in bytes */ +}; + +struct intel_vgpu_sprite_plane_format { + u8 enabled; /* plane is enabled */ + u8 tiled; /* X-tiled */ + u8 bpp; /* bits per pixel */ + u32 hw_format; /* format field in the SPR_CTL register */ + u32 drm_format; /* format in DRM definition */ + u32 base; /* sprite base in graphics memory */ + u64 base_gpa; + u32 x_pos; /* in pixels */ + u32 y_pos; /* in lines */ + u32 x_offset; /* in pixels */ + u32 y_offset; /* in lines */ + u32 width; /* in pixels */ + u32 height; /* in lines */ + u32 stride; /* in bytes */ +}; + +struct intel_vgpu_cursor_plane_format { + u8 enabled; + u8 mode; /* cursor mode select */ + u8 bpp; /* bits per pixel */ + u32 drm_format; /* format in DRM definition */ + u32 base; /* cursor base in graphics memory */ + u64 base_gpa; + u32 x_pos; /* in pixels */ + u32 y_pos; /* in lines */ + u8 x_sign; /* X Position Sign */ + u8 y_sign; /* Y Position Sign */ + u32 width; /* in pixels */ + u32 height; /* in lines */ + u32 x_hot; /* in pixels */ + u32 y_hot; /* in pixels */ +}; + +struct intel_vgpu_pipe_format { + struct intel_vgpu_primary_plane_format primary; + struct intel_vgpu_sprite_plane_format sprite; + struct intel_vgpu_cursor_plane_format cursor; + enum DDI_PORT ddi_port; /* the DDI port that pipe is connected to */ +}; + +struct intel_vgpu_fb_format { + struct intel_vgpu_pipe_format pipes[I915_MAX_PIPES]; +}; + +int intel_vgpu_decode_primary_plane(struct intel_vgpu *vgpu, + struct intel_vgpu_primary_plane_format *plane); +int intel_vgpu_decode_cursor_plane(struct intel_vgpu *vgpu, + struct intel_vgpu_cursor_plane_format *plane); +int intel_vgpu_decode_sprite_plane(struct intel_vgpu *vgpu, + struct intel_vgpu_sprite_plane_format *plane); + +#endif diff --git a/drivers/gpu/drm/i915/gvt/gvt.c b/drivers/gpu/drm/i915/gvt/gvt.c index 3a74a408a966..643bb961d40d 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.c +++ b/drivers/gpu/drm/i915/gvt/gvt.c @@ -181,6 +181,8 @@ static const struct intel_gvt_ops intel_gvt_ops = { .vgpu_deactivate = intel_gvt_deactivate_vgpu, .gvt_find_vgpu_type = intel_gvt_find_vgpu_type, .get_gvt_attrs = intel_get_gvt_attrs, + .vgpu_query_plane = intel_vgpu_query_plane, + .vgpu_get_dmabuf = intel_vgpu_get_dmabuf, }; /** @@ -384,6 +386,8 @@ int intel_gvt_init_device(struct drm_i915_private *dev_priv) if (ret) goto out_clean_idr; + intel_gvt_init_engine_mmio_context(gvt); + ret = intel_gvt_load_firmware(gvt); if (ret) goto out_clean_mmio_info; diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index 393066726993..1e9f11c8b7bb 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -44,8 +44,10 @@ #include "execlist.h" #include "scheduler.h" #include "sched_policy.h" -#include "render.h" +#include "mmio_context.h" #include "cmd_parser.h" +#include "fb_decoder.h" +#include "dmabuf.h" #define GVT_MAX_VGPU 8 @@ -123,7 +125,9 @@ struct intel_vgpu_irq { }; struct intel_vgpu_opregion { + bool mapped; void *va; + void *va_gopregion; u32 gfn[INTEL_GVT_OPREGION_PAGES]; }; @@ -206,8 +210,16 @@ struct intel_vgpu { struct kvm *kvm; struct work_struct release_work; atomic_t released; + struct vfio_device *vfio_device; } vdev; #endif + + struct list_head dmabuf_obj_list_head; + struct mutex dmabuf_lock; + struct idr object_idr; + + struct completion vblank_done; + }; /* validating GM healthy status*/ @@ -298,6 +310,8 @@ struct intel_gvt { wait_queue_head_t service_thread_wq; unsigned long service_request; + struct engine_mmio *engine_mmio_list; + struct dentry *debugfs_root; }; @@ -336,7 +350,7 @@ int intel_gvt_load_firmware(struct intel_gvt *gvt); /* Aperture/GM space definitions for GVT device */ #define gvt_aperture_sz(gvt) (gvt->dev_priv->ggtt.mappable_end) -#define gvt_aperture_pa_base(gvt) (gvt->dev_priv->ggtt.mappable_base) +#define gvt_aperture_pa_base(gvt) (gvt->dev_priv->ggtt.gmadr.start) #define gvt_ggtt_gm_sz(gvt) (gvt->dev_priv->ggtt.base.total) #define gvt_ggtt_sz(gvt) \ @@ -505,7 +519,8 @@ static inline u64 intel_vgpu_get_bar_gpa(struct intel_vgpu *vgpu, int bar) } void intel_vgpu_clean_opregion(struct intel_vgpu *vgpu); -int intel_vgpu_init_opregion(struct intel_vgpu *vgpu, u32 gpa); +int intel_vgpu_init_opregion(struct intel_vgpu *vgpu); +int intel_vgpu_opregion_base_write_handler(struct intel_vgpu *vgpu, u32 gpa); int intel_vgpu_emulate_opregion_request(struct intel_vgpu *vgpu, u32 swsci); void populate_pvinfo_page(struct intel_vgpu *vgpu); @@ -532,6 +547,8 @@ struct intel_gvt_ops { const char *name); bool (*get_gvt_attrs)(struct attribute ***type_attrs, struct attribute_group ***intel_vgpu_type_groups); + int (*vgpu_query_plane)(struct intel_vgpu *vgpu, void *); + int (*vgpu_get_dmabuf)(struct intel_vgpu *vgpu, unsigned int); }; diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index 94fc04210bac..c982867e7c2b 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -174,8 +174,10 @@ void enter_failsafe_mode(struct intel_vgpu *vgpu, int reason) break; case GVT_FAILSAFE_INSUFFICIENT_RESOURCE: pr_err("Graphics resource is not enough for the guest\n"); + break; case GVT_FAILSAFE_GUEST_ERR: pr_err("GVT Internal error for the guest\n"); + break; default: break; } @@ -1396,7 +1398,7 @@ static int hws_pga_write(struct intel_vgpu *vgpu, unsigned int offset, * update the VM CSB status correctly. Here listed registers can * support BDW, SKL or other platforms with same HWSP registers. */ - if (unlikely(ring_id < 0 || ring_id > I915_NUM_ENGINES)) { + if (unlikely(ring_id < 0 || ring_id >= I915_NUM_ENGINES)) { gvt_vgpu_err("VM(%d) access unknown hardware status page register:0x%x\n", vgpu->id, offset); return -EINVAL; @@ -1471,7 +1473,7 @@ static int elsp_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, u32 data = *(u32 *)p_data; int ret = 0; - if (WARN_ON(ring_id < 0 || ring_id > I915_NUM_ENGINES - 1)) + if (WARN_ON(ring_id < 0 || ring_id >= I915_NUM_ENGINES)) return -EINVAL; execlist = &vgpu->submission.execlist[ring_id]; diff --git a/drivers/gpu/drm/i915/gvt/hypercall.h b/drivers/gpu/drm/i915/gvt/hypercall.h index df7f33abd393..a1bd82feb827 100644 --- a/drivers/gpu/drm/i915/gvt/hypercall.h +++ b/drivers/gpu/drm/i915/gvt/hypercall.h @@ -55,6 +55,9 @@ struct intel_gvt_mpt { unsigned long mfn, unsigned int nr, bool map); int (*set_trap_area)(unsigned long handle, u64 start, u64 end, bool map); + int (*set_opregion)(void *vgpu); + int (*get_vfio_device)(void *vgpu); + void (*put_vfio_device)(void *vgpu); }; extern struct intel_gvt_mpt xengt_mpt; diff --git a/drivers/gpu/drm/i915/gvt/kvmgt.c b/drivers/gpu/drm/i915/gvt/kvmgt.c index 110f07e8bcfb..f86983d6655b 100644 --- a/drivers/gpu/drm/i915/gvt/kvmgt.c +++ b/drivers/gpu/drm/i915/gvt/kvmgt.c @@ -53,11 +53,23 @@ static const struct intel_gvt_ops *intel_gvt_ops; #define VFIO_PCI_INDEX_TO_OFFSET(index) ((u64)(index) << VFIO_PCI_OFFSET_SHIFT) #define VFIO_PCI_OFFSET_MASK (((u64)(1) << VFIO_PCI_OFFSET_SHIFT) - 1) +#define OPREGION_SIGNATURE "IntelGraphicsMem" + +struct vfio_region; +struct intel_vgpu_regops { + size_t (*rw)(struct intel_vgpu *vgpu, char *buf, + size_t count, loff_t *ppos, bool iswrite); + void (*release)(struct intel_vgpu *vgpu, + struct vfio_region *region); +}; + struct vfio_region { u32 type; u32 subtype; size_t size; u32 flags; + const struct intel_vgpu_regops *ops; + void *data; }; struct kvmgt_pgfn { @@ -316,6 +328,108 @@ static void kvmgt_protect_table_del(struct kvmgt_guest_info *info, } } +static size_t intel_vgpu_reg_rw_opregion(struct intel_vgpu *vgpu, char *buf, + size_t count, loff_t *ppos, bool iswrite) +{ + unsigned int i = VFIO_PCI_OFFSET_TO_INDEX(*ppos) - + VFIO_PCI_NUM_REGIONS; + void *base = vgpu->vdev.region[i].data; + loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; + + if (pos >= vgpu->vdev.region[i].size || iswrite) { + gvt_vgpu_err("invalid op or offset for Intel vgpu OpRegion\n"); + return -EINVAL; + } + count = min(count, (size_t)(vgpu->vdev.region[i].size - pos)); + memcpy(buf, base + pos, count); + + return count; +} + +static void intel_vgpu_reg_release_opregion(struct intel_vgpu *vgpu, + struct vfio_region *region) +{ +} + +static const struct intel_vgpu_regops intel_vgpu_regops_opregion = { + .rw = intel_vgpu_reg_rw_opregion, + .release = intel_vgpu_reg_release_opregion, +}; + +static int intel_vgpu_register_reg(struct intel_vgpu *vgpu, + unsigned int type, unsigned int subtype, + const struct intel_vgpu_regops *ops, + size_t size, u32 flags, void *data) +{ + struct vfio_region *region; + + region = krealloc(vgpu->vdev.region, + (vgpu->vdev.num_regions + 1) * sizeof(*region), + GFP_KERNEL); + if (!region) + return -ENOMEM; + + vgpu->vdev.region = region; + vgpu->vdev.region[vgpu->vdev.num_regions].type = type; + vgpu->vdev.region[vgpu->vdev.num_regions].subtype = subtype; + vgpu->vdev.region[vgpu->vdev.num_regions].ops = ops; + vgpu->vdev.region[vgpu->vdev.num_regions].size = size; + vgpu->vdev.region[vgpu->vdev.num_regions].flags = flags; + vgpu->vdev.region[vgpu->vdev.num_regions].data = data; + vgpu->vdev.num_regions++; + return 0; +} + +static int kvmgt_get_vfio_device(void *p_vgpu) +{ + struct intel_vgpu *vgpu = (struct intel_vgpu *)p_vgpu; + + vgpu->vdev.vfio_device = vfio_device_get_from_dev( + mdev_dev(vgpu->vdev.mdev)); + if (!vgpu->vdev.vfio_device) { + gvt_vgpu_err("failed to get vfio device\n"); + return -ENODEV; + } + return 0; +} + + +static int kvmgt_set_opregion(void *p_vgpu) +{ + struct intel_vgpu *vgpu = (struct intel_vgpu *)p_vgpu; + void *base; + int ret; + + /* Each vgpu has its own opregion, although VFIO would create another + * one later. This one is used to expose opregion to VFIO. And the + * other one created by VFIO later, is used by guest actually. + */ + base = vgpu_opregion(vgpu)->va; + if (!base) + return -ENOMEM; + + if (memcmp(base, OPREGION_SIGNATURE, 16)) { + memunmap(base); + return -EINVAL; + } + + ret = intel_vgpu_register_reg(vgpu, + PCI_VENDOR_ID_INTEL | VFIO_REGION_TYPE_PCI_VENDOR_TYPE, + VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION, + &intel_vgpu_regops_opregion, OPREGION_SIZE, + VFIO_REGION_INFO_FLAG_READ, base); + + return ret; +} + +static void kvmgt_put_vfio_device(void *vgpu) +{ + if (WARN_ON(!((struct intel_vgpu *)vgpu)->vdev.vfio_device)) + return; + + vfio_device_put(((struct intel_vgpu *)vgpu)->vdev.vfio_device); +} + static int intel_vgpu_create(struct kobject *kobj, struct mdev_device *mdev) { struct intel_vgpu *vgpu = NULL; @@ -546,7 +660,7 @@ static ssize_t intel_vgpu_rw(struct mdev_device *mdev, char *buf, int ret = -EINVAL; - if (index >= VFIO_PCI_NUM_REGIONS) { + if (index >= VFIO_PCI_NUM_REGIONS + vgpu->vdev.num_regions) { gvt_vgpu_err("invalid index: %u\n", index); return -EINVAL; } @@ -574,8 +688,14 @@ static ssize_t intel_vgpu_rw(struct mdev_device *mdev, char *buf, case VFIO_PCI_BAR5_REGION_INDEX: case VFIO_PCI_VGA_REGION_INDEX: case VFIO_PCI_ROM_REGION_INDEX: + break; default: - gvt_vgpu_err("unsupported region: %u\n", index); + if (index >= VFIO_PCI_NUM_REGIONS + vgpu->vdev.num_regions) + return -EINVAL; + + index -= VFIO_PCI_NUM_REGIONS; + return vgpu->vdev.region[index].ops->rw(vgpu, buf, count, + ppos, is_write); } return ret == 0 ? count : ret; @@ -838,7 +958,8 @@ static long intel_vgpu_ioctl(struct mdev_device *mdev, unsigned int cmd, info.flags = VFIO_DEVICE_FLAGS_PCI; info.flags |= VFIO_DEVICE_FLAGS_RESET; - info.num_regions = VFIO_PCI_NUM_REGIONS; + info.num_regions = VFIO_PCI_NUM_REGIONS + + vgpu->vdev.num_regions; info.num_irqs = VFIO_PCI_NUM_IRQS; return copy_to_user((void __user *)arg, &info, minsz) ? @@ -908,13 +1029,17 @@ static long intel_vgpu_ioctl(struct mdev_device *mdev, unsigned int cmd, case VFIO_PCI_BAR3_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX: info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index); info.size = 0; - info.flags = 0; + gvt_dbg_core("get region info bar:%d\n", info.index); break; case VFIO_PCI_ROM_REGION_INDEX: case VFIO_PCI_VGA_REGION_INDEX: + info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index); + info.size = 0; + info.flags = 0; + gvt_dbg_core("get region info index:%d\n", info.index); break; default: @@ -959,6 +1084,7 @@ static long intel_vgpu_ioctl(struct mdev_device *mdev, unsigned int cmd, } if (caps.size) { + info.flags |= VFIO_REGION_INFO_FLAG_CAPS; if (info.argsz < sizeof(info) + caps.size) { info.argsz = sizeof(info) + caps.size; info.cap_offset = 0; @@ -1045,6 +1171,33 @@ static long intel_vgpu_ioctl(struct mdev_device *mdev, unsigned int cmd, } else if (cmd == VFIO_DEVICE_RESET) { intel_gvt_ops->vgpu_reset(vgpu); return 0; + } else if (cmd == VFIO_DEVICE_QUERY_GFX_PLANE) { + struct vfio_device_gfx_plane_info dmabuf; + int ret = 0; + + minsz = offsetofend(struct vfio_device_gfx_plane_info, + dmabuf_id); + if (copy_from_user(&dmabuf, (void __user *)arg, minsz)) + return -EFAULT; + if (dmabuf.argsz < minsz) + return -EINVAL; + + ret = intel_gvt_ops->vgpu_query_plane(vgpu, &dmabuf); + if (ret != 0) + return ret; + + return copy_to_user((void __user *)arg, &dmabuf, minsz) ? + -EFAULT : 0; + } else if (cmd == VFIO_DEVICE_GET_GFX_DMABUF) { + __u32 dmabuf_id; + __s32 dmabuf_fd; + + if (get_user(dmabuf_id, (__u32 __user *)arg)) + return -EFAULT; + + dmabuf_fd = intel_gvt_ops->vgpu_get_dmabuf(vgpu, dmabuf_id); + return dmabuf_fd; + } return 0; @@ -1286,6 +1439,9 @@ static int kvmgt_guest_init(struct mdev_device *mdev) kvmgt_protect_table_init(info); gvt_cache_init(vgpu); + mutex_init(&vgpu->dmabuf_lock); + init_completion(&vgpu->vblank_done); + info->track_node.track_write = kvmgt_page_track_write; info->track_node.track_flush_slot = kvmgt_page_track_flush_slot; kvm_page_track_register_notifier(kvm, &info->track_node); @@ -1426,6 +1582,9 @@ struct intel_gvt_mpt kvmgt_mpt = { .read_gpa = kvmgt_read_gpa, .write_gpa = kvmgt_write_gpa, .gfn_to_mfn = kvmgt_gfn_to_pfn, + .set_opregion = kvmgt_set_opregion, + .get_vfio_device = kvmgt_get_vfio_device, + .put_vfio_device = kvmgt_put_vfio_device, }; EXPORT_SYMBOL_GPL(kvmgt_mpt); diff --git a/drivers/gpu/drm/i915/gvt/mmio.c b/drivers/gpu/drm/i915/gvt/mmio.c index 4ea0feb5f04d..f7227a3ad469 100644 --- a/drivers/gpu/drm/i915/gvt/mmio.c +++ b/drivers/gpu/drm/i915/gvt/mmio.c @@ -157,7 +157,6 @@ int intel_vgpu_emulate_mmio_read(struct intel_vgpu *vgpu, uint64_t pa, unsigned int offset = 0; int ret = -EINVAL; - if (vgpu->failsafe) { failsafe_emulate_mmio_rw(vgpu, pa, p_data, bytes, true); return 0; @@ -166,8 +165,7 @@ int intel_vgpu_emulate_mmio_read(struct intel_vgpu *vgpu, uint64_t pa, if (vgpu_gpa_is_aperture(vgpu, pa)) { ret = vgpu_aperture_rw(vgpu, pa, p_data, bytes, true); - mutex_unlock(&gvt->lock); - return ret; + goto out; } if (atomic_read(&vgpu->gtt.n_tracked_guest_page)) { @@ -183,8 +181,7 @@ int intel_vgpu_emulate_mmio_read(struct intel_vgpu *vgpu, uint64_t pa, ret, t->gfn, pa, *(u32 *)p_data, bytes); } - mutex_unlock(&gvt->lock); - return ret; + goto out; } } @@ -205,14 +202,12 @@ int intel_vgpu_emulate_mmio_read(struct intel_vgpu *vgpu, uint64_t pa, p_data, bytes); if (ret) goto err; - mutex_unlock(&gvt->lock); - return ret; + goto out; } if (WARN_ON_ONCE(!reg_is_mmio(gvt, offset))) { ret = intel_gvt_hypervisor_read_gpa(vgpu, pa, p_data, bytes); - mutex_unlock(&gvt->lock); - return ret; + goto out; } if (WARN_ON(!reg_is_mmio(gvt, offset + bytes - 1))) @@ -228,11 +223,13 @@ int intel_vgpu_emulate_mmio_read(struct intel_vgpu *vgpu, uint64_t pa, goto err; intel_gvt_mmio_set_accessed(gvt, offset); - mutex_unlock(&gvt->lock); - return 0; + ret = 0; + goto out; + err: gvt_vgpu_err("fail to emulate MMIO read %08x len %d\n", offset, bytes); +out: mutex_unlock(&gvt->lock); return ret; } @@ -263,8 +260,7 @@ int intel_vgpu_emulate_mmio_write(struct intel_vgpu *vgpu, uint64_t pa, if (vgpu_gpa_is_aperture(vgpu, pa)) { ret = vgpu_aperture_rw(vgpu, pa, p_data, bytes, false); - mutex_unlock(&gvt->lock); - return ret; + goto out; } if (atomic_read(&vgpu->gtt.n_tracked_guest_page)) { @@ -280,8 +276,7 @@ int intel_vgpu_emulate_mmio_write(struct intel_vgpu *vgpu, uint64_t pa, ret, t->gfn, pa, *(u32 *)p_data, bytes); } - mutex_unlock(&gvt->lock); - return ret; + goto out; } } @@ -302,14 +297,12 @@ int intel_vgpu_emulate_mmio_write(struct intel_vgpu *vgpu, uint64_t pa, p_data, bytes); if (ret) goto err; - mutex_unlock(&gvt->lock); - return ret; + goto out; } if (WARN_ON_ONCE(!reg_is_mmio(gvt, offset))) { ret = intel_gvt_hypervisor_write_gpa(vgpu, pa, p_data, bytes); - mutex_unlock(&gvt->lock); - return ret; + goto out; } ret = intel_vgpu_mmio_reg_rw(vgpu, offset, p_data, bytes, false); @@ -317,11 +310,12 @@ int intel_vgpu_emulate_mmio_write(struct intel_vgpu *vgpu, uint64_t pa, goto err; intel_gvt_mmio_set_accessed(gvt, offset); - mutex_unlock(&gvt->lock); - return 0; + ret = 0; + goto out; err: gvt_vgpu_err("fail to emulate MMIO write %08x len %d\n", offset, bytes); +out: mutex_unlock(&gvt->lock); return ret; } diff --git a/drivers/gpu/drm/i915/gvt/render.c b/drivers/gpu/drm/i915/gvt/mmio_context.c index 0672178548ef..8a52b56f0e86 100644 --- a/drivers/gpu/drm/i915/gvt/render.c +++ b/drivers/gpu/drm/i915/gvt/mmio_context.c @@ -37,108 +37,116 @@ #include "gvt.h" #include "trace.h" -struct render_mmio { - int ring_id; - i915_reg_t reg; - u32 mask; - bool in_context; - u32 value; -}; - -static struct render_mmio gen8_render_mmio_list[] __cacheline_aligned = { - {RCS, _MMIO(0x229c), 0xffff, false}, - {RCS, _MMIO(0x2248), 0x0, false}, - {RCS, _MMIO(0x2098), 0x0, false}, - {RCS, _MMIO(0x20c0), 0xffff, true}, - {RCS, _MMIO(0x24d0), 0, false}, - {RCS, _MMIO(0x24d4), 0, false}, - {RCS, _MMIO(0x24d8), 0, false}, - {RCS, _MMIO(0x24dc), 0, false}, - {RCS, _MMIO(0x24e0), 0, false}, - {RCS, _MMIO(0x24e4), 0, false}, - {RCS, _MMIO(0x24e8), 0, false}, - {RCS, _MMIO(0x24ec), 0, false}, - {RCS, _MMIO(0x24f0), 0, false}, - {RCS, _MMIO(0x24f4), 0, false}, - {RCS, _MMIO(0x24f8), 0, false}, - {RCS, _MMIO(0x24fc), 0, false}, - {RCS, _MMIO(0x7004), 0xffff, true}, - {RCS, _MMIO(0x7008), 0xffff, true}, - {RCS, _MMIO(0x7000), 0xffff, true}, - {RCS, _MMIO(0x7010), 0xffff, true}, - {RCS, _MMIO(0x7300), 0xffff, true}, - {RCS, _MMIO(0x83a4), 0xffff, true}, - - {BCS, _MMIO(0x2229c), 0xffff, false}, - {BCS, _MMIO(0x2209c), 0xffff, false}, - {BCS, _MMIO(0x220c0), 0xffff, false}, - {BCS, _MMIO(0x22098), 0x0, false}, - {BCS, _MMIO(0x22028), 0x0, false}, +/** + * Defined in Intel Open Source PRM. + * Ref: https://01.org/linuxgraphics/documentation/hardware-specification-prms + */ +#define TRVATTL3PTRDW(i) _MMIO(0x4de0 + (i)*4) +#define TRNULLDETCT _MMIO(0x4de8) +#define TRINVTILEDETCT _MMIO(0x4dec) +#define TRVADR _MMIO(0x4df0) +#define TRTTE _MMIO(0x4df4) +#define RING_EXCC(base) _MMIO((base) + 0x28) +#define RING_GFX_MODE(base) _MMIO((base) + 0x29c) +#define VF_GUARDBAND _MMIO(0x83a4) + +/* Raw offset is appened to each line for convenience. */ +static struct engine_mmio gen8_engine_mmio_list[] __cacheline_aligned = { + {RCS, GFX_MODE_GEN7, 0xffff, false}, /* 0x229c */ + {RCS, GEN9_CTX_PREEMPT_REG, 0x0, false}, /* 0x2248 */ + {RCS, HWSTAM, 0x0, false}, /* 0x2098 */ + {RCS, INSTPM, 0xffff, true}, /* 0x20c0 */ + {RCS, RING_FORCE_TO_NONPRIV(RENDER_RING_BASE, 0), 0, false}, /* 0x24d0 */ + {RCS, RING_FORCE_TO_NONPRIV(RENDER_RING_BASE, 1), 0, false}, /* 0x24d4 */ + {RCS, RING_FORCE_TO_NONPRIV(RENDER_RING_BASE, 2), 0, false}, /* 0x24d8 */ + {RCS, RING_FORCE_TO_NONPRIV(RENDER_RING_BASE, 3), 0, false}, /* 0x24dc */ + {RCS, RING_FORCE_TO_NONPRIV(RENDER_RING_BASE, 4), 0, false}, /* 0x24e0 */ + {RCS, RING_FORCE_TO_NONPRIV(RENDER_RING_BASE, 5), 0, false}, /* 0x24e4 */ + {RCS, RING_FORCE_TO_NONPRIV(RENDER_RING_BASE, 6), 0, false}, /* 0x24e8 */ + {RCS, RING_FORCE_TO_NONPRIV(RENDER_RING_BASE, 7), 0, false}, /* 0x24ec */ + {RCS, RING_FORCE_TO_NONPRIV(RENDER_RING_BASE, 8), 0, false}, /* 0x24f0 */ + {RCS, RING_FORCE_TO_NONPRIV(RENDER_RING_BASE, 9), 0, false}, /* 0x24f4 */ + {RCS, RING_FORCE_TO_NONPRIV(RENDER_RING_BASE, 10), 0, false}, /* 0x24f8 */ + {RCS, RING_FORCE_TO_NONPRIV(RENDER_RING_BASE, 11), 0, false}, /* 0x24fc */ + {RCS, CACHE_MODE_1, 0xffff, true}, /* 0x7004 */ + {RCS, GEN7_GT_MODE, 0xffff, true}, /* 0x7008 */ + {RCS, CACHE_MODE_0_GEN7, 0xffff, true}, /* 0x7000 */ + {RCS, GEN7_COMMON_SLICE_CHICKEN1, 0xffff, true}, /* 0x7010 */ + {RCS, HDC_CHICKEN0, 0xffff, true}, /* 0x7300 */ + {RCS, VF_GUARDBAND, 0xffff, true}, /* 0x83a4 */ + + {BCS, RING_GFX_MODE(BLT_RING_BASE), 0xffff, false}, /* 0x2229c */ + {BCS, RING_MI_MODE(BLT_RING_BASE), 0xffff, false}, /* 0x2209c */ + {BCS, RING_INSTPM(BLT_RING_BASE), 0xffff, false}, /* 0x220c0 */ + {BCS, RING_HWSTAM(BLT_RING_BASE), 0x0, false}, /* 0x22098 */ + {BCS, RING_EXCC(BLT_RING_BASE), 0x0, false}, /* 0x22028 */ + { /* Terminated */ } }; -static struct render_mmio gen9_render_mmio_list[] __cacheline_aligned = { - {RCS, _MMIO(0x229c), 0xffff, false}, - {RCS, _MMIO(0x2248), 0x0, false}, - {RCS, _MMIO(0x2098), 0x0, false}, - {RCS, _MMIO(0x20c0), 0xffff, true}, - {RCS, _MMIO(0x24d0), 0, false}, - {RCS, _MMIO(0x24d4), 0, false}, - {RCS, _MMIO(0x24d8), 0, false}, - {RCS, _MMIO(0x24dc), 0, false}, - {RCS, _MMIO(0x24e0), 0, false}, - {RCS, _MMIO(0x24e4), 0, false}, - {RCS, _MMIO(0x24e8), 0, false}, - {RCS, _MMIO(0x24ec), 0, false}, - {RCS, _MMIO(0x24f0), 0, false}, - {RCS, _MMIO(0x24f4), 0, false}, - {RCS, _MMIO(0x24f8), 0, false}, - {RCS, _MMIO(0x24fc), 0, false}, - {RCS, _MMIO(0x7004), 0xffff, true}, - {RCS, _MMIO(0x7008), 0xffff, true}, - {RCS, _MMIO(0x7000), 0xffff, true}, - {RCS, _MMIO(0x7010), 0xffff, true}, - {RCS, _MMIO(0x7300), 0xffff, true}, - {RCS, _MMIO(0x83a4), 0xffff, true}, - - {RCS, _MMIO(0x40e0), 0, false}, - {RCS, _MMIO(0x40e4), 0, false}, - {RCS, _MMIO(0x2580), 0xffff, true}, - {RCS, _MMIO(0x7014), 0xffff, true}, - {RCS, _MMIO(0x20ec), 0xffff, false}, - {RCS, _MMIO(0xb118), 0, false}, - {RCS, _MMIO(0xe100), 0xffff, true}, - {RCS, _MMIO(0xe180), 0xffff, true}, - {RCS, _MMIO(0xe184), 0xffff, true}, - {RCS, _MMIO(0xe188), 0xffff, true}, - {RCS, _MMIO(0xe194), 0xffff, true}, - {RCS, _MMIO(0x4de0), 0, false}, - {RCS, _MMIO(0x4de4), 0, false}, - {RCS, _MMIO(0x4de8), 0, false}, - {RCS, _MMIO(0x4dec), 0, false}, - {RCS, _MMIO(0x4df0), 0, false}, - {RCS, _MMIO(0x4df4), 0, false}, - - {BCS, _MMIO(0x2229c), 0xffff, false}, - {BCS, _MMIO(0x2209c), 0xffff, false}, - {BCS, _MMIO(0x220c0), 0xffff, false}, - {BCS, _MMIO(0x22098), 0x0, false}, - {BCS, _MMIO(0x22028), 0x0, false}, - - {VCS2, _MMIO(0x1c028), 0xffff, false}, - - {VECS, _MMIO(0x1a028), 0xffff, false}, - - {RCS, _MMIO(0x7304), 0xffff, true}, - {RCS, _MMIO(0x2248), 0x0, false}, - {RCS, _MMIO(0x940c), 0x0, false}, - {RCS, _MMIO(0x4ab8), 0x0, false}, - - {RCS, _MMIO(0x4ab0), 0x0, false}, - {RCS, _MMIO(0x20d4), 0x0, false}, - - {RCS, _MMIO(0xb004), 0x0, false}, - {RCS, _MMIO(0x20a0), 0x0, false}, - {RCS, _MMIO(0x20e4), 0xffff, false}, +static struct engine_mmio gen9_engine_mmio_list[] __cacheline_aligned = { + {RCS, GFX_MODE_GEN7, 0xffff, false}, /* 0x229c */ + {RCS, GEN9_CTX_PREEMPT_REG, 0x0, false}, /* 0x2248 */ + {RCS, HWSTAM, 0x0, false}, /* 0x2098 */ + {RCS, INSTPM, 0xffff, true}, /* 0x20c0 */ + {RCS, RING_FORCE_TO_NONPRIV(RENDER_RING_BASE, 0), 0, false}, /* 0x24d0 */ + {RCS, RING_FORCE_TO_NONPRIV(RENDER_RING_BASE, 1), 0, false}, /* 0x24d4 */ + {RCS, RING_FORCE_TO_NONPRIV(RENDER_RING_BASE, 2), 0, false}, /* 0x24d8 */ + {RCS, RING_FORCE_TO_NONPRIV(RENDER_RING_BASE, 3), 0, false}, /* 0x24dc */ + {RCS, RING_FORCE_TO_NONPRIV(RENDER_RING_BASE, 4), 0, false}, /* 0x24e0 */ + {RCS, RING_FORCE_TO_NONPRIV(RENDER_RING_BASE, 5), 0, false}, /* 0x24e4 */ + {RCS, RING_FORCE_TO_NONPRIV(RENDER_RING_BASE, 6), 0, false}, /* 0x24e8 */ + {RCS, RING_FORCE_TO_NONPRIV(RENDER_RING_BASE, 7), 0, false}, /* 0x24ec */ + {RCS, RING_FORCE_TO_NONPRIV(RENDER_RING_BASE, 8), 0, false}, /* 0x24f0 */ + {RCS, RING_FORCE_TO_NONPRIV(RENDER_RING_BASE, 9), 0, false}, /* 0x24f4 */ + {RCS, RING_FORCE_TO_NONPRIV(RENDER_RING_BASE, 10), 0, false}, /* 0x24f8 */ + {RCS, RING_FORCE_TO_NONPRIV(RENDER_RING_BASE, 11), 0, false}, /* 0x24fc */ + {RCS, CACHE_MODE_1, 0xffff, true}, /* 0x7004 */ + {RCS, GEN7_GT_MODE, 0xffff, true}, /* 0x7008 */ + {RCS, CACHE_MODE_0_GEN7, 0xffff, true}, /* 0x7000 */ + {RCS, GEN7_COMMON_SLICE_CHICKEN1, 0xffff, true}, /* 0x7010 */ + {RCS, HDC_CHICKEN0, 0xffff, true}, /* 0x7300 */ + {RCS, VF_GUARDBAND, 0xffff, true}, /* 0x83a4 */ + + {RCS, GEN8_PRIVATE_PAT_LO, 0, false}, /* 0x40e0 */ + {RCS, GEN8_PRIVATE_PAT_HI, 0, false}, /* 0x40e4 */ + {RCS, GEN8_CS_CHICKEN1, 0xffff, true}, /* 0x2580 */ + {RCS, COMMON_SLICE_CHICKEN2, 0xffff, true}, /* 0x7014 */ + {RCS, GEN9_CS_DEBUG_MODE1, 0xffff, false}, /* 0x20ec */ + {RCS, GEN8_L3SQCREG4, 0, false}, /* 0xb118 */ + {RCS, GEN7_HALF_SLICE_CHICKEN1, 0xffff, true}, /* 0xe100 */ + {RCS, HALF_SLICE_CHICKEN2, 0xffff, true}, /* 0xe180 */ + {RCS, HALF_SLICE_CHICKEN3, 0xffff, true}, /* 0xe184 */ + {RCS, GEN9_HALF_SLICE_CHICKEN5, 0xffff, true}, /* 0xe188 */ + {RCS, GEN9_HALF_SLICE_CHICKEN7, 0xffff, true}, /* 0xe194 */ + {RCS, TRVATTL3PTRDW(0), 0, false}, /* 0x4de0 */ + {RCS, TRVATTL3PTRDW(1), 0, false}, /* 0x4de4 */ + {RCS, TRNULLDETCT, 0, false}, /* 0x4de8 */ + {RCS, TRINVTILEDETCT, 0, false}, /* 0x4dec */ + {RCS, TRVADR, 0, false}, /* 0x4df0 */ + {RCS, TRTTE, 0, false}, /* 0x4df4 */ + + {BCS, RING_GFX_MODE(BLT_RING_BASE), 0xffff, false}, /* 0x2229c */ + {BCS, RING_MI_MODE(BLT_RING_BASE), 0xffff, false}, /* 0x2209c */ + {BCS, RING_INSTPM(BLT_RING_BASE), 0xffff, false}, /* 0x220c0 */ + {BCS, RING_HWSTAM(BLT_RING_BASE), 0x0, false}, /* 0x22098 */ + {BCS, RING_EXCC(BLT_RING_BASE), 0x0, false}, /* 0x22028 */ + + {VCS2, RING_EXCC(GEN8_BSD2_RING_BASE), 0xffff, false}, /* 0x1c028 */ + + {VECS, RING_EXCC(VEBOX_RING_BASE), 0xffff, false}, /* 0x1a028 */ + + {RCS, GEN8_HDC_CHICKEN1, 0xffff, true}, /* 0x7304 */ + {RCS, GEN9_CTX_PREEMPT_REG, 0x0, false}, /* 0x2248 */ + {RCS, GEN7_UCGCTL4, 0x0, false}, /* 0x940c */ + {RCS, GAMT_CHKN_BIT_REG, 0x0, false}, /* 0x4ab8 */ + + {RCS, GEN9_GAMT_ECO_REG_RW_IA, 0x0, false}, /* 0x4ab0 */ + {RCS, GEN9_CSFE_CHICKEN1_RCS, 0x0, false}, /* 0x20d4 */ + + {RCS, GEN8_GARBCNTL, 0x0, false}, /* 0xb004 */ + {RCS, GEN7_FF_THREAD_MODE, 0x0, false}, /* 0x20a0 */ + {RCS, FF_SLICE_CS_CHICKEN2, 0xffff, false}, /* 0x20e4 */ + { /* Terminated */ } }; static u32 gen9_render_mocs[I915_NUM_ENGINES][64]; @@ -267,22 +275,14 @@ static void switch_mmio_to_vgpu(struct intel_vgpu *vgpu, int ring_id) u32 ctx_ctrl = reg_state[CTX_CONTEXT_CONTROL_VAL]; u32 inhibit_mask = _MASKED_BIT_ENABLE(CTX_CTRL_ENGINE_CTX_RESTORE_INHIBIT); - i915_reg_t last_reg = _MMIO(0); - struct render_mmio *mmio; + struct engine_mmio *mmio; u32 v; - int i, array_size; - if (IS_SKYLAKE(vgpu->gvt->dev_priv) - || IS_KABYLAKE(vgpu->gvt->dev_priv)) { - mmio = gen9_render_mmio_list; - array_size = ARRAY_SIZE(gen9_render_mmio_list); + if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) load_mocs(vgpu, ring_id); - } else { - mmio = gen8_render_mmio_list; - array_size = ARRAY_SIZE(gen8_render_mmio_list); - } - for (i = 0; i < array_size; i++, mmio++) { + mmio = vgpu->gvt->engine_mmio_list; + while (i915_mmio_reg_offset((mmio++)->reg)) { if (mmio->ring_id != ring_id) continue; @@ -294,8 +294,7 @@ static void switch_mmio_to_vgpu(struct intel_vgpu *vgpu, int ring_id) * write. */ if (mmio->in_context && - ((ctx_ctrl & inhibit_mask) != inhibit_mask) && - i915_modparams.enable_execlists) + (ctx_ctrl & inhibit_mask) != inhibit_mask) continue; if (mmio->mask) @@ -304,17 +303,12 @@ static void switch_mmio_to_vgpu(struct intel_vgpu *vgpu, int ring_id) v = vgpu_vreg(vgpu, mmio->reg); I915_WRITE_FW(mmio->reg, v); - last_reg = mmio->reg; trace_render_mmio(vgpu->id, "load", i915_mmio_reg_offset(mmio->reg), mmio->value, v); } - /* Make sure the swiched MMIOs has taken effect. */ - if (likely(INTEL_GVT_MMIO_OFFSET(last_reg))) - I915_READ_FW(last_reg); - handle_tlb_pending_event(vgpu, ring_id); } @@ -322,21 +316,14 @@ static void switch_mmio_to_vgpu(struct intel_vgpu *vgpu, int ring_id) static void switch_mmio_to_host(struct intel_vgpu *vgpu, int ring_id) { struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; - struct render_mmio *mmio; - i915_reg_t last_reg = _MMIO(0); + struct engine_mmio *mmio; u32 v; - int i, array_size; - if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) { - mmio = gen9_render_mmio_list; - array_size = ARRAY_SIZE(gen9_render_mmio_list); + if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) restore_mocs(vgpu, ring_id); - } else { - mmio = gen8_render_mmio_list; - array_size = ARRAY_SIZE(gen8_render_mmio_list); - } - for (i = 0; i < array_size; i++, mmio++) { + mmio = vgpu->gvt->engine_mmio_list; + while (i915_mmio_reg_offset((mmio++)->reg)) { if (mmio->ring_id != ring_id) continue; @@ -352,16 +339,11 @@ static void switch_mmio_to_host(struct intel_vgpu *vgpu, int ring_id) continue; I915_WRITE_FW(mmio->reg, v); - last_reg = mmio->reg; trace_render_mmio(vgpu->id, "restore", i915_mmio_reg_offset(mmio->reg), mmio->value, v); } - - /* Make sure the swiched MMIOs has taken effect. */ - if (likely(INTEL_GVT_MMIO_OFFSET(last_reg))) - I915_READ_FW(last_reg); } /** @@ -405,3 +387,16 @@ void intel_gvt_switch_mmio(struct intel_vgpu *pre, intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); } + +/** + * intel_gvt_init_engine_mmio_context - Initiate the engine mmio list + * @gvt: GVT device + * + */ +void intel_gvt_init_engine_mmio_context(struct intel_gvt *gvt) +{ + if (IS_SKYLAKE(gvt->dev_priv) || IS_KABYLAKE(gvt->dev_priv)) + gvt->engine_mmio_list = gen9_engine_mmio_list; + else + gvt->engine_mmio_list = gen8_engine_mmio_list; +} diff --git a/drivers/gpu/drm/i915/gvt/render.h b/drivers/gpu/drm/i915/gvt/mmio_context.h index 91db1d39d28f..ca2c6a745673 100644 --- a/drivers/gpu/drm/i915/gvt/render.h +++ b/drivers/gpu/drm/i915/gvt/mmio_context.h @@ -36,8 +36,17 @@ #ifndef __GVT_RENDER_H__ #define __GVT_RENDER_H__ +struct engine_mmio { + int ring_id; + i915_reg_t reg; + u32 mask; + bool in_context; + u32 value; +}; + void intel_gvt_switch_mmio(struct intel_vgpu *pre, struct intel_vgpu *next, int ring_id); +void intel_gvt_init_engine_mmio_context(struct intel_gvt *gvt); #endif diff --git a/drivers/gpu/drm/i915/gvt/mpt.h b/drivers/gpu/drm/i915/gvt/mpt.h index c436e20ea59e..ca8005a6d5fa 100644 --- a/drivers/gpu/drm/i915/gvt/mpt.h +++ b/drivers/gpu/drm/i915/gvt/mpt.h @@ -294,4 +294,49 @@ static inline int intel_gvt_hypervisor_set_trap_area( return intel_gvt_host.mpt->set_trap_area(vgpu->handle, start, end, map); } +/** + * intel_gvt_hypervisor_set_opregion - Set opregion for guest + * @vgpu: a vGPU + * + * Returns: + * Zero on success, negative error code if failed. + */ +static inline int intel_gvt_hypervisor_set_opregion(struct intel_vgpu *vgpu) +{ + if (!intel_gvt_host.mpt->set_opregion) + return 0; + + return intel_gvt_host.mpt->set_opregion(vgpu); +} + +/** + * intel_gvt_hypervisor_get_vfio_device - increase vfio device ref count + * @vgpu: a vGPU + * + * Returns: + * Zero on success, negative error code if failed. + */ +static inline int intel_gvt_hypervisor_get_vfio_device(struct intel_vgpu *vgpu) +{ + if (!intel_gvt_host.mpt->get_vfio_device) + return 0; + + return intel_gvt_host.mpt->get_vfio_device(vgpu); +} + +/** + * intel_gvt_hypervisor_put_vfio_device - decrease vfio device ref count + * @vgpu: a vGPU + * + * Returns: + * Zero on success, negative error code if failed. + */ +static inline void intel_gvt_hypervisor_put_vfio_device(struct intel_vgpu *vgpu) +{ + if (!intel_gvt_host.mpt->put_vfio_device) + return; + + intel_gvt_host.mpt->put_vfio_device(vgpu); +} + #endif /* _GVT_MPT_H_ */ diff --git a/drivers/gpu/drm/i915/gvt/opregion.c b/drivers/gpu/drm/i915/gvt/opregion.c index 80720e59723a..8420d1fc3ddb 100644 --- a/drivers/gpu/drm/i915/gvt/opregion.c +++ b/drivers/gpu/drm/i915/gvt/opregion.c @@ -213,11 +213,20 @@ static void virt_vbt_generation(struct vbt *v) v->driver_features.lvds_config = BDB_DRIVER_FEATURE_NO_LVDS; } -static int alloc_and_init_virt_opregion(struct intel_vgpu *vgpu) +/** + * intel_vgpu_init_opregion - initialize the stuff used to emulate opregion + * @vgpu: a vGPU + * @gpa: guest physical address of opregion + * + * Returns: + * Zero on success, negative error code if failed. + */ +int intel_vgpu_init_opregion(struct intel_vgpu *vgpu) { u8 *buf; struct opregion_header *header; struct vbt v; + const char opregion_signature[16] = OPREGION_SIGNATURE; gvt_dbg_core("init vgpu%d opregion\n", vgpu->id); vgpu_opregion(vgpu)->va = (void *)__get_free_pages(GFP_KERNEL | @@ -231,8 +240,8 @@ static int alloc_and_init_virt_opregion(struct intel_vgpu *vgpu) /* emulated opregion with VBT mailbox only */ buf = (u8 *)vgpu_opregion(vgpu)->va; header = (struct opregion_header *)buf; - memcpy(header->signature, OPREGION_SIGNATURE, - sizeof(OPREGION_SIGNATURE)); + memcpy(header->signature, opregion_signature, + sizeof(opregion_signature)); header->size = 0x8; header->opregion_ver = 0x02000000; header->mboxes = MBOX_VBT; @@ -250,25 +259,6 @@ static int alloc_and_init_virt_opregion(struct intel_vgpu *vgpu) return 0; } -static int init_vgpu_opregion(struct intel_vgpu *vgpu, u32 gpa) -{ - int i, ret; - - if (WARN((vgpu_opregion(vgpu)->va), - "vgpu%d: opregion has been initialized already.\n", - vgpu->id)) - return -EINVAL; - - ret = alloc_and_init_virt_opregion(vgpu); - if (ret < 0) - return ret; - - for (i = 0; i < INTEL_GVT_OPREGION_PAGES; i++) - vgpu_opregion(vgpu)->gfn[i] = (gpa >> PAGE_SHIFT) + i; - - return 0; -} - static int map_vgpu_opregion(struct intel_vgpu *vgpu, bool map) { u64 mfn; @@ -290,59 +280,91 @@ static int map_vgpu_opregion(struct intel_vgpu *vgpu, bool map) return ret; } } + + vgpu_opregion(vgpu)->mapped = map; + return 0; } /** - * intel_vgpu_clean_opregion - clean the stuff used to emulate opregion + * intel_vgpu_opregion_base_write_handler - Opregion base register write handler + * * @vgpu: a vGPU + * @gpa: guest physical address of opregion * + * Returns: + * Zero on success, negative error code if failed. */ -void intel_vgpu_clean_opregion(struct intel_vgpu *vgpu) +int intel_vgpu_opregion_base_write_handler(struct intel_vgpu *vgpu, u32 gpa) { - gvt_dbg_core("vgpu%d: clean vgpu opregion\n", vgpu->id); - if (!vgpu_opregion(vgpu)->va) - return; + int i, ret = 0; + unsigned long pfn; - if (intel_gvt_host.hypervisor_type == INTEL_GVT_HYPERVISOR_XEN) { - map_vgpu_opregion(vgpu, false); - free_pages((unsigned long)vgpu_opregion(vgpu)->va, - get_order(INTEL_GVT_OPREGION_SIZE)); + gvt_dbg_core("emulate opregion from kernel\n"); + + switch (intel_gvt_host.hypervisor_type) { + case INTEL_GVT_HYPERVISOR_KVM: + pfn = intel_gvt_hypervisor_gfn_to_mfn(vgpu, gpa >> PAGE_SHIFT); + vgpu_opregion(vgpu)->va_gopregion = memremap(pfn << PAGE_SHIFT, + INTEL_GVT_OPREGION_SIZE, + MEMREMAP_WB); + if (!vgpu_opregion(vgpu)->va_gopregion) { + gvt_vgpu_err("failed to map guest opregion\n"); + ret = -EFAULT; + } + vgpu_opregion(vgpu)->mapped = true; + break; + case INTEL_GVT_HYPERVISOR_XEN: + /** + * Wins guest on Xengt will write this register twice: xen + * hvmloader and windows graphic driver. + */ + if (vgpu_opregion(vgpu)->mapped) + map_vgpu_opregion(vgpu, false); + + for (i = 0; i < INTEL_GVT_OPREGION_PAGES; i++) + vgpu_opregion(vgpu)->gfn[i] = (gpa >> PAGE_SHIFT) + i; - vgpu_opregion(vgpu)->va = NULL; + ret = map_vgpu_opregion(vgpu, true); + break; + default: + ret = -EINVAL; + gvt_vgpu_err("not supported hypervisor\n"); } + + return ret; } /** - * intel_vgpu_init_opregion - initialize the stuff used to emulate opregion + * intel_vgpu_clean_opregion - clean the stuff used to emulate opregion * @vgpu: a vGPU - * @gpa: guest physical address of opregion * - * Returns: - * Zero on success, negative error code if failed. */ -int intel_vgpu_init_opregion(struct intel_vgpu *vgpu, u32 gpa) +void intel_vgpu_clean_opregion(struct intel_vgpu *vgpu) { - int ret; + gvt_dbg_core("vgpu%d: clean vgpu opregion\n", vgpu->id); - gvt_dbg_core("vgpu%d: init vgpu opregion\n", vgpu->id); + if (!vgpu_opregion(vgpu)->va) + return; if (intel_gvt_host.hypervisor_type == INTEL_GVT_HYPERVISOR_XEN) { - gvt_dbg_core("emulate opregion from kernel\n"); - - ret = init_vgpu_opregion(vgpu, gpa); - if (ret) - return ret; - - ret = map_vgpu_opregion(vgpu, true); - if (ret) - return ret; + if (vgpu_opregion(vgpu)->mapped) + map_vgpu_opregion(vgpu, false); + } else if (intel_gvt_host.hypervisor_type == INTEL_GVT_HYPERVISOR_KVM) { + if (vgpu_opregion(vgpu)->mapped) { + memunmap(vgpu_opregion(vgpu)->va_gopregion); + vgpu_opregion(vgpu)->va_gopregion = NULL; + } } + free_pages((unsigned long)vgpu_opregion(vgpu)->va, + get_order(INTEL_GVT_OPREGION_SIZE)); + + vgpu_opregion(vgpu)->va = NULL; - return 0; } + #define GVT_OPREGION_FUNC(scic) \ ({ \ u32 __ret; \ @@ -461,8 +483,21 @@ int intel_vgpu_emulate_opregion_request(struct intel_vgpu *vgpu, u32 swsci) u32 *scic, *parm; u32 func, subfunc; - scic = vgpu_opregion(vgpu)->va + INTEL_GVT_OPREGION_SCIC; - parm = vgpu_opregion(vgpu)->va + INTEL_GVT_OPREGION_PARM; + switch (intel_gvt_host.hypervisor_type) { + case INTEL_GVT_HYPERVISOR_XEN: + scic = vgpu_opregion(vgpu)->va + INTEL_GVT_OPREGION_SCIC; + parm = vgpu_opregion(vgpu)->va + INTEL_GVT_OPREGION_PARM; + break; + case INTEL_GVT_HYPERVISOR_KVM: + scic = vgpu_opregion(vgpu)->va_gopregion + + INTEL_GVT_OPREGION_SCIC; + parm = vgpu_opregion(vgpu)->va_gopregion + + INTEL_GVT_OPREGION_PARM; + break; + default: + gvt_vgpu_err("not supported hypervisor\n"); + return -EINVAL; + } if (!(swsci & SWSCI_SCI_SELECT)) { gvt_vgpu_err("requesting SMI service\n"); diff --git a/drivers/gpu/drm/i915/gvt/sched_policy.c b/drivers/gpu/drm/i915/gvt/sched_policy.c index 03532dfc0cd5..eea1a2f92099 100644 --- a/drivers/gpu/drm/i915/gvt/sched_policy.c +++ b/drivers/gpu/drm/i915/gvt/sched_policy.c @@ -372,6 +372,11 @@ void intel_vgpu_start_schedule(struct intel_vgpu *vgpu) vgpu->gvt->scheduler.sched_ops->start_schedule(vgpu); } +void intel_gvt_kick_schedule(struct intel_gvt *gvt) +{ + intel_gvt_request_service(gvt, INTEL_GVT_REQUEST_EVENT_SCHED); +} + void intel_vgpu_stop_schedule(struct intel_vgpu *vgpu) { struct intel_gvt_workload_scheduler *scheduler = diff --git a/drivers/gpu/drm/i915/gvt/sched_policy.h b/drivers/gpu/drm/i915/gvt/sched_policy.h index ba00a5f7455f..7b59e3e88b8b 100644 --- a/drivers/gpu/drm/i915/gvt/sched_policy.h +++ b/drivers/gpu/drm/i915/gvt/sched_policy.h @@ -57,4 +57,6 @@ void intel_vgpu_start_schedule(struct intel_vgpu *vgpu); void intel_vgpu_stop_schedule(struct intel_vgpu *vgpu); +void intel_gvt_kick_schedule(struct intel_gvt *gvt); + #endif diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c index d6177a0baeec..0056638b0c16 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.c +++ b/drivers/gpu/drm/i915/gvt/scheduler.c @@ -189,10 +189,12 @@ static int shadow_context_status_change(struct notifier_block *nb, atomic_set(&workload->shadow_ctx_active, 1); break; case INTEL_CONTEXT_SCHEDULE_OUT: - case INTEL_CONTEXT_SCHEDULE_PREEMPTED: save_ring_hw_state(workload->vgpu, ring_id); atomic_set(&workload->shadow_ctx_active, 0); break; + case INTEL_CONTEXT_SCHEDULE_PREEMPTED: + save_ring_hw_state(workload->vgpu, ring_id); + break; default: WARN_ON(1); return NOTIFY_OK; @@ -246,7 +248,7 @@ static int copy_workload_to_ring_buffer(struct intel_vgpu_workload *workload) return 0; } -void release_shadow_wa_ctx(struct intel_shadow_wa_ctx *wa_ctx) +static void release_shadow_wa_ctx(struct intel_shadow_wa_ctx *wa_ctx) { if (!wa_ctx->indirect_ctx.obj) return; @@ -1037,6 +1039,9 @@ int intel_vgpu_setup_submission(struct intel_vgpu *vgpu) if (IS_ERR(s->shadow_ctx)) return PTR_ERR(s->shadow_ctx); + if (HAS_LOGICAL_RING_PREEMPTION(vgpu->gvt->dev_priv)) + s->shadow_ctx->priority = INT_MAX; + bitmap_zero(s->shadow_ctx_desc_updated, I915_NUM_ENGINES); s->workloads = kmem_cache_create("gvt-g_vgpu_workload", @@ -1329,3 +1334,15 @@ intel_vgpu_create_workload(struct intel_vgpu *vgpu, int ring_id, return workload; } + +/** + * intel_vgpu_queue_workload - Qeue a vGPU workload + * @workload: the workload to queue in + */ +void intel_vgpu_queue_workload(struct intel_vgpu_workload *workload) +{ + list_add_tail(&workload->list, + workload_q_head(workload->vgpu, workload->ring_id)); + intel_gvt_kick_schedule(workload->vgpu->gvt); + wake_up(&workload->vgpu->gvt->scheduler.waitq[workload->ring_id]); +} diff --git a/drivers/gpu/drm/i915/gvt/scheduler.h b/drivers/gpu/drm/i915/gvt/scheduler.h index e4a9f9acd4a9..3de77dfa7c59 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.h +++ b/drivers/gpu/drm/i915/gvt/scheduler.h @@ -125,12 +125,7 @@ struct intel_vgpu_shadow_bb { #define workload_q_head(vgpu, ring_id) \ (&(vgpu->submission.workload_q_head[ring_id])) -#define queue_workload(workload) do { \ - list_add_tail(&workload->list, \ - workload_q_head(workload->vgpu, workload->ring_id)); \ - wake_up(&workload->vgpu->gvt-> \ - scheduler.waitq[workload->ring_id]); \ -} while (0) +void intel_vgpu_queue_workload(struct intel_vgpu_workload *workload); int intel_gvt_init_workload_scheduler(struct intel_gvt *gvt); diff --git a/drivers/gpu/drm/i915/gvt/vgpu.c b/drivers/gpu/drm/i915/gvt/vgpu.c index c6b82d1ba7de..39926176fbeb 100644 --- a/drivers/gpu/drm/i915/gvt/vgpu.c +++ b/drivers/gpu/drm/i915/gvt/vgpu.c @@ -236,6 +236,7 @@ void intel_gvt_deactivate_vgpu(struct intel_vgpu *vgpu) } intel_vgpu_stop_schedule(vgpu); + intel_vgpu_dmabuf_cleanup(vgpu); mutex_unlock(&gvt->lock); } @@ -265,6 +266,7 @@ void intel_gvt_destroy_vgpu(struct intel_vgpu *vgpu) intel_gvt_hypervisor_detach_vgpu(vgpu); intel_vgpu_free_resource(vgpu); intel_vgpu_clean_mmio(vgpu); + intel_vgpu_dmabuf_cleanup(vgpu); vfree(vgpu); intel_gvt_update_vgpu_types(gvt); @@ -349,7 +351,8 @@ static struct intel_vgpu *__intel_gvt_create_vgpu(struct intel_gvt *gvt, vgpu->handle = param->handle; vgpu->gvt = gvt; vgpu->sched_ctl.weight = param->weight; - + INIT_LIST_HEAD(&vgpu->dmabuf_obj_list_head); + idr_init(&vgpu->object_idr); intel_vgpu_init_cfg_space(vgpu, param->primary); ret = intel_vgpu_init_mmio(vgpu); @@ -370,10 +373,14 @@ static struct intel_vgpu *__intel_gvt_create_vgpu(struct intel_gvt *gvt, if (ret) goto out_detach_hypervisor_vgpu; - ret = intel_vgpu_init_display(vgpu, param->resolution); + ret = intel_vgpu_init_opregion(vgpu); if (ret) goto out_clean_gtt; + ret = intel_vgpu_init_display(vgpu, param->resolution); + if (ret) + goto out_clean_opregion; + ret = intel_vgpu_setup_submission(vgpu); if (ret) goto out_clean_display; @@ -386,6 +393,10 @@ static struct intel_vgpu *__intel_gvt_create_vgpu(struct intel_gvt *gvt, if (ret) goto out_clean_sched_policy; + ret = intel_gvt_hypervisor_set_opregion(vgpu); + if (ret) + goto out_clean_sched_policy; + mutex_unlock(&gvt->lock); return vgpu; @@ -396,6 +407,8 @@ out_clean_submission: intel_vgpu_clean_submission(vgpu); out_clean_display: intel_vgpu_clean_display(vgpu); +out_clean_opregion: + intel_vgpu_clean_opregion(vgpu); out_clean_gtt: intel_vgpu_clean_gtt(vgpu); out_detach_hypervisor_vgpu: diff --git a/drivers/gpu/drm/i915/i915_cmd_parser.c b/drivers/gpu/drm/i915/i915_cmd_parser.c index b11629beeb63..ccb5ba043b63 100644 --- a/drivers/gpu/drm/i915/i915_cmd_parser.c +++ b/drivers/gpu/drm/i915/i915_cmd_parser.c @@ -26,6 +26,7 @@ */ #include "i915_drv.h" +#include "intel_ringbuffer.h" /** * DOC: batch buffer command parser @@ -940,7 +941,7 @@ void intel_engine_init_cmd_parser(struct intel_engine_cs *engine) return; } - engine->needs_cmd_parser = true; + engine->flags |= I915_ENGINE_NEEDS_CMD_PARSER; } /** @@ -952,7 +953,7 @@ void intel_engine_init_cmd_parser(struct intel_engine_cs *engine) */ void intel_engine_cleanup_cmd_parser(struct intel_engine_cs *engine) { - if (!engine->needs_cmd_parser) + if (!intel_engine_needs_cmd_parser(engine)) return; fini_hash_table(engine); @@ -1350,7 +1351,7 @@ int i915_cmd_parser_get_version(struct drm_i915_private *dev_priv) /* If the command parser is not enabled, report 0 - unsupported */ for_each_engine(engine, dev_priv, id) { - if (engine->needs_cmd_parser) { + if (intel_engine_needs_cmd_parser(engine)) { active = true; break; } diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index df3852c02a35..d8c6ec3cca71 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -111,8 +111,8 @@ static u64 i915_gem_obj_total_ggtt_size(struct drm_i915_gem_object *obj) u64 size = 0; struct i915_vma *vma; - list_for_each_entry(vma, &obj->vma_list, obj_link) { - if (i915_vma_is_ggtt(vma) && drm_mm_node_allocated(&vma->node)) + for_each_ggtt_vma(vma, obj) { + if (drm_mm_node_allocated(&vma->node)) size += vma->node.size; } @@ -522,8 +522,8 @@ static int i915_gem_object_info(struct seq_file *m, void *data) seq_printf(m, "%u display objects (globally pinned), %llu bytes\n", dpy_count, dpy_size); - seq_printf(m, "%llu [%llu] gtt total\n", - ggtt->base.total, ggtt->mappable_end); + seq_printf(m, "%llu [%pa] gtt total\n", + ggtt->base.total, &ggtt->mappable_end); seq_printf(m, "Supported page sizes: %s\n", stringify_page_sizes(INTEL_INFO(dev_priv)->page_sizes, buf, sizeof(buf))); @@ -664,38 +664,6 @@ static int i915_gem_batch_pool_info(struct seq_file *m, void *data) return 0; } -static void i915_ring_seqno_info(struct seq_file *m, - struct intel_engine_cs *engine) -{ - struct intel_breadcrumbs *b = &engine->breadcrumbs; - struct rb_node *rb; - - seq_printf(m, "Current sequence (%s): %x\n", - engine->name, intel_engine_get_seqno(engine)); - - spin_lock_irq(&b->rb_lock); - for (rb = rb_first(&b->waiters); rb; rb = rb_next(rb)) { - struct intel_wait *w = rb_entry(rb, typeof(*w), node); - - seq_printf(m, "Waiting (%s): %s [%d] on %x\n", - engine->name, w->tsk->comm, w->tsk->pid, w->seqno); - } - spin_unlock_irq(&b->rb_lock); -} - -static int i915_gem_seqno_info(struct seq_file *m, void *data) -{ - struct drm_i915_private *dev_priv = node_to_i915(m->private); - struct intel_engine_cs *engine; - enum intel_engine_id id; - - for_each_engine(engine, dev_priv, id) - i915_ring_seqno_info(m, engine); - - return 0; -} - - static int i915_interrupt_info(struct seq_file *m, void *data) { struct drm_i915_private *dev_priv = node_to_i915(m->private); @@ -896,13 +864,12 @@ static int i915_interrupt_info(struct seq_file *m, void *data) seq_printf(m, "Graphics Interrupt mask: %08x\n", I915_READ(GTIMR)); } - for_each_engine(engine, dev_priv, id) { - if (INTEL_GEN(dev_priv) >= 6) { + if (INTEL_GEN(dev_priv) >= 6) { + for_each_engine(engine, dev_priv, id) { seq_printf(m, "Graphics Interrupt mask (%s): %08x\n", engine->name, I915_READ_IMR(engine)); } - i915_ring_seqno_info(m, engine); } intel_runtime_pm_put(dev_priv); @@ -1151,13 +1118,8 @@ static int i915_frequency_info(struct seq_file *m, void *unused) rpdownei = I915_READ(GEN6_RP_CUR_DOWN_EI) & GEN6_CURIAVG_MASK; rpcurdown = I915_READ(GEN6_RP_CUR_DOWN) & GEN6_CURBSYTAVG_MASK; rpprevdown = I915_READ(GEN6_RP_PREV_DOWN) & GEN6_CURBSYTAVG_MASK; - if (INTEL_GEN(dev_priv) >= 9) - cagf = (rpstat & GEN9_CAGF_MASK) >> GEN9_CAGF_SHIFT; - else if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) - cagf = (rpstat & HSW_CAGF_MASK) >> HSW_CAGF_SHIFT; - else - cagf = (rpstat & GEN6_CAGF_MASK) >> GEN6_CAGF_SHIFT; - cagf = intel_gpu_freq(dev_priv, cagf); + cagf = intel_gpu_freq(dev_priv, + intel_get_cagf(dev_priv, rpstat)); intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); @@ -1989,75 +1951,6 @@ static int i915_context_status(struct seq_file *m, void *unused) return 0; } -static void i915_dump_lrc_obj(struct seq_file *m, - struct i915_gem_context *ctx, - struct intel_engine_cs *engine) -{ - struct i915_vma *vma = ctx->engine[engine->id].state; - struct page *page; - int j; - - seq_printf(m, "CONTEXT: %s %u\n", engine->name, ctx->hw_id); - - if (!vma) { - seq_puts(m, "\tFake context\n"); - return; - } - - if (vma->flags & I915_VMA_GLOBAL_BIND) - seq_printf(m, "\tBound in GGTT at 0x%08x\n", - i915_ggtt_offset(vma)); - - if (i915_gem_object_pin_pages(vma->obj)) { - seq_puts(m, "\tFailed to get pages for context object\n\n"); - return; - } - - page = i915_gem_object_get_page(vma->obj, LRC_STATE_PN); - if (page) { - u32 *reg_state = kmap_atomic(page); - - for (j = 0; j < 0x600 / sizeof(u32) / 4; j += 4) { - seq_printf(m, - "\t[0x%04x] 0x%08x 0x%08x 0x%08x 0x%08x\n", - j * 4, - reg_state[j], reg_state[j + 1], - reg_state[j + 2], reg_state[j + 3]); - } - kunmap_atomic(reg_state); - } - - i915_gem_object_unpin_pages(vma->obj); - seq_putc(m, '\n'); -} - -static int i915_dump_lrc(struct seq_file *m, void *unused) -{ - struct drm_i915_private *dev_priv = node_to_i915(m->private); - struct drm_device *dev = &dev_priv->drm; - struct intel_engine_cs *engine; - struct i915_gem_context *ctx; - enum intel_engine_id id; - int ret; - - if (!i915_modparams.enable_execlists) { - seq_printf(m, "Logical Ring Contexts are disabled\n"); - return 0; - } - - ret = mutex_lock_interruptible(&dev->struct_mutex); - if (ret) - return ret; - - list_for_each_entry(ctx, &dev_priv->contexts.list, link) - for_each_engine(engine, dev_priv, id) - i915_dump_lrc_obj(m, ctx, engine); - - mutex_unlock(&dev->struct_mutex); - - return 0; -} - static const char *swizzle_string(unsigned swizzle) { switch (swizzle) { @@ -3287,7 +3180,7 @@ static int i915_engine_info(struct seq_file *m, void *unused) p = drm_seq_file_printer(m); for_each_engine(engine, dev_priv, id) - intel_engine_dump(engine, &p); + intel_engine_dump(engine, &p, "%s\n", engine->name); intel_runtime_pm_put(dev_priv); @@ -3304,69 +3197,6 @@ static int i915_shrinker_info(struct seq_file *m, void *unused) return 0; } -static int i915_semaphore_status(struct seq_file *m, void *unused) -{ - struct drm_i915_private *dev_priv = node_to_i915(m->private); - struct drm_device *dev = &dev_priv->drm; - struct intel_engine_cs *engine; - int num_rings = INTEL_INFO(dev_priv)->num_rings; - enum intel_engine_id id; - int j, ret; - - if (!i915_modparams.semaphores) { - seq_puts(m, "Semaphores are disabled\n"); - return 0; - } - - ret = mutex_lock_interruptible(&dev->struct_mutex); - if (ret) - return ret; - intel_runtime_pm_get(dev_priv); - - if (IS_BROADWELL(dev_priv)) { - struct page *page; - uint64_t *seqno; - - page = i915_gem_object_get_page(dev_priv->semaphore->obj, 0); - - seqno = (uint64_t *)kmap_atomic(page); - for_each_engine(engine, dev_priv, id) { - uint64_t offset; - - seq_printf(m, "%s\n", engine->name); - - seq_puts(m, " Last signal:"); - for (j = 0; j < num_rings; j++) { - offset = id * I915_NUM_ENGINES + j; - seq_printf(m, "0x%08llx (0x%02llx) ", - seqno[offset], offset * 8); - } - seq_putc(m, '\n'); - - seq_puts(m, " Last wait: "); - for (j = 0; j < num_rings; j++) { - offset = id + (j * I915_NUM_ENGINES); - seq_printf(m, "0x%08llx (0x%02llx) ", - seqno[offset], offset * 8); - } - seq_putc(m, '\n'); - - } - kunmap_atomic(seqno); - } else { - seq_puts(m, " Last signal:"); - for_each_engine(engine, dev_priv, id) - for (j = 0; j < num_rings; j++) - seq_printf(m, "0x%08x\n", - I915_READ(engine->semaphore.mbox.signal[j])); - seq_putc(m, '\n'); - } - - intel_runtime_pm_put(dev_priv); - mutex_unlock(&dev->struct_mutex); - return 0; -} - static int i915_shared_dplls_info(struct seq_file *m, void *unused) { struct drm_i915_private *dev_priv = node_to_i915(m->private); @@ -4809,7 +4639,6 @@ static const struct drm_info_list i915_debugfs_list[] = { {"i915_gem_objects", i915_gem_object_info, 0}, {"i915_gem_gtt", i915_gem_gtt_info, 0}, {"i915_gem_stolen", i915_gem_stolen_list_info }, - {"i915_gem_seqno", i915_gem_seqno_info, 0}, {"i915_gem_fence_regs", i915_gem_fence_regs_info, 0}, {"i915_gem_interrupt", i915_interrupt_info, 0}, {"i915_gem_batch_pool", i915_gem_batch_pool_info, 0}, @@ -4833,7 +4662,6 @@ static const struct drm_info_list i915_debugfs_list[] = { {"i915_vbt", i915_vbt, 0}, {"i915_gem_framebuffer", i915_gem_framebuffer_info, 0}, {"i915_context_status", i915_context_status, 0}, - {"i915_dump_lrc", i915_dump_lrc, 0}, {"i915_forcewake_domains", i915_forcewake_domains, 0}, {"i915_swizzle_info", i915_swizzle_info, 0}, {"i915_ppgtt_info", i915_ppgtt_info, 0}, @@ -4847,7 +4675,6 @@ static const struct drm_info_list i915_debugfs_list[] = { {"i915_display_info", i915_display_info, 0}, {"i915_engine_info", i915_engine_info, 0}, {"i915_shrinker_info", i915_shrinker_info, 0}, - {"i915_semaphore_status", i915_semaphore_status, 0}, {"i915_shared_dplls_info", i915_shared_dplls_info, 0}, {"i915_dp_mst_info", i915_dp_mst_info, 0}, {"i915_wa_registers", i915_wa_registers, 0}, diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 5170a8ea83d4..ca9f4b2862eb 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -48,6 +48,7 @@ #include "i915_drv.h" #include "i915_trace.h" +#include "i915_pmu.h" #include "i915_vgpu.h" #include "intel_drv.h" #include "intel_uc.h" @@ -321,7 +322,7 @@ static int i915_getparam(struct drm_device *dev, void *data, value = USES_PPGTT(dev_priv); break; case I915_PARAM_HAS_SEMAPHORES: - value = i915_modparams.semaphores; + value = HAS_LEGACY_SEMAPHORES(dev_priv); break; case I915_PARAM_HAS_SECURE_BATCHES: value = capable(CAP_SYS_ADMIN); @@ -371,9 +372,7 @@ static int i915_getparam(struct drm_device *dev, void *data, if (dev_priv->engine[RCS] && dev_priv->engine[RCS]->schedule) { value |= I915_SCHEDULER_CAP_ENABLED; value |= I915_SCHEDULER_CAP_PRIORITY; - - if (HAS_LOGICAL_RING_PREEMPTION(dev_priv) && - i915_modparams.enable_execlists) + if (HAS_LOGICAL_RING_PREEMPTION(dev_priv)) value |= I915_SCHEDULER_CAP_PREEMPTION; } break; @@ -618,10 +617,12 @@ static void i915_gem_fini(struct drm_i915_private *dev_priv) mutex_lock(&dev_priv->drm.struct_mutex); intel_uc_fini_hw(dev_priv); + intel_uc_fini(dev_priv); i915_gem_cleanup_engines(dev_priv); i915_gem_contexts_fini(dev_priv); mutex_unlock(&dev_priv->drm.struct_mutex); + intel_uc_fini_wq(dev_priv); i915_gem_cleanup_userptr(dev_priv); i915_gem_drain_freed_objects(dev_priv); @@ -694,8 +695,6 @@ static int i915_load_modeset_init(struct drm_device *dev) /* Only enable hotplug handling once the fbdev is fully set up. */ intel_hpd_init(dev_priv); - drm_kms_helper_poll_init(dev); - return 0; cleanup_gem: @@ -729,7 +728,7 @@ static int i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) if (!ap) return -ENOMEM; - ap->ranges[0].base = ggtt->mappable_base; + ap->ranges[0].base = ggtt->gmadr.start; ap->ranges[0].size = ggtt->mappable_end; primary = @@ -936,8 +935,6 @@ static int i915_driver_init_early(struct drm_i915_private *dev_priv, intel_detect_preproduction_hw(dev_priv); - i915_perf_init(dev_priv); - return 0; err_irq: @@ -954,7 +951,6 @@ err_engines: */ static void i915_driver_cleanup_early(struct drm_i915_private *dev_priv) { - i915_perf_fini(dev_priv); i915_gem_load_cleanup(dev_priv); intel_irq_fini(dev_priv); i915_workqueues_cleanup(dev_priv); @@ -1057,10 +1053,6 @@ static void i915_driver_cleanup_mmio(struct drm_i915_private *dev_priv) static void intel_sanitize_options(struct drm_i915_private *dev_priv) { - i915_modparams.enable_execlists = - intel_sanitize_enable_execlists(dev_priv, - i915_modparams.enable_execlists); - /* * i915.enable_ppgtt is read-only, so do an early pass to validate the * user's requested state against the hardware/driver capabilities. We @@ -1072,11 +1064,6 @@ static void intel_sanitize_options(struct drm_i915_private *dev_priv) i915_modparams.enable_ppgtt); DRM_DEBUG_DRIVER("ppgtt mode: %i\n", i915_modparams.enable_ppgtt); - i915_modparams.semaphores = - intel_sanitize_semaphores(dev_priv, i915_modparams.semaphores); - DRM_DEBUG_DRIVER("use GPU semaphores? %s\n", - yesno(i915_modparams.semaphores)); - intel_uc_sanitize_options(dev_priv); intel_gvt_sanitize_options(dev_priv); @@ -1101,6 +1088,8 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv) intel_sanitize_options(dev_priv); + i915_perf_init(dev_priv); + ret = i915_ggtt_probe_hw(dev_priv); if (ret) return ret; @@ -1206,6 +1195,8 @@ static void i915_driver_cleanup_hw(struct drm_i915_private *dev_priv) { struct pci_dev *pdev = dev_priv->drm.pdev; + i915_perf_fini(dev_priv); + if (pdev->msi_enabled) pci_disable_msi(pdev); @@ -1224,7 +1215,8 @@ static void i915_driver_register(struct drm_i915_private *dev_priv) { struct drm_device *dev = &dev_priv->drm; - i915_gem_shrinker_init(dev_priv); + i915_gem_shrinker_register(dev_priv); + i915_pmu_register(dev_priv); /* * Notify a valid surface after modesetting, @@ -1263,6 +1255,13 @@ static void i915_driver_register(struct drm_i915_private *dev_priv) * cannot run before the connectors are registered. */ intel_fbdev_initial_config_async(dev); + + /* + * We need to coordinate the hotplugs with the asynchronous fbdev + * configuration, for which we use the fbdev->async_cookie. + */ + if (INTEL_INFO(dev_priv)->num_pipes) + drm_kms_helper_poll_init(dev); } /** @@ -1274,17 +1273,25 @@ static void i915_driver_unregister(struct drm_i915_private *dev_priv) intel_fbdev_unregister(dev_priv); intel_audio_deinit(dev_priv); + /* + * After flushing the fbdev (incl. a late async config which will + * have delayed queuing of a hotplug event), then flush the hotplug + * events. + */ + drm_kms_helper_poll_fini(&dev_priv->drm); + intel_gpu_ips_teardown(); acpi_video_unregister(); intel_opregion_unregister(dev_priv); i915_perf_unregister(dev_priv); + i915_pmu_unregister(dev_priv); i915_teardown_sysfs(dev_priv); i915_guc_log_unregister(dev_priv); drm_dev_unregister(&dev_priv->drm); - i915_gem_shrinker_cleanup(dev_priv); + i915_gem_shrinker_unregister(dev_priv); } /** @@ -1872,7 +1879,9 @@ void i915_reset(struct drm_i915_private *i915, unsigned int flags) { struct i915_gpu_error *error = &i915->gpu_error; int ret; + int i; + might_sleep(); lockdep_assert_held(&i915->drm.struct_mutex); GEM_BUG_ON(!test_bit(I915_RESET_BACKOFF, &error->flags)); @@ -1890,20 +1899,31 @@ void i915_reset(struct drm_i915_private *i915, unsigned int flags) disable_irq(i915->drm.irq); ret = i915_gem_reset_prepare(i915); if (ret) { - DRM_ERROR("GPU recovery failed\n"); + dev_err(i915->drm.dev, "GPU recovery failed\n"); intel_gpu_reset(i915, ALL_ENGINES); - goto error; + goto taint; } - ret = intel_gpu_reset(i915, ALL_ENGINES); - if (ret) { - if (ret != -ENODEV) - DRM_ERROR("Failed to reset chip: %i\n", ret); + if (!intel_has_gpu_reset(i915)) { + if (i915_modparams.reset) + dev_err(i915->drm.dev, "GPU reset not supported\n"); else DRM_DEBUG_DRIVER("GPU reset disabled\n"); goto error; } + for (i = 0; i < 3; i++) { + ret = intel_gpu_reset(i915, ALL_ENGINES); + if (ret == 0) + break; + + msleep(100); + } + if (ret) { + dev_err(i915->drm.dev, "Failed to reset chip\n"); + goto taint; + } + i915_gem_reset(i915); intel_overlay_reset(i915); @@ -1944,6 +1964,20 @@ wakeup: wake_up_bit(&error->flags, I915_RESET_HANDOFF); return; +taint: + /* + * History tells us that if we cannot reset the GPU now, we + * never will. This then impacts everything that is run + * subsequently. On failing the reset, we mark the driver + * as wedged, preventing further execution on the GPU. + * We also want to go one step further and add a taint to the + * kernel so that any subsequent faults can be traced back to + * this failure. This is important for CI, where if the + * GPU/driver fails we would like to reboot and restart testing + * rather than continue on into oblivion. For everyone else, + * the system should still plod along, but they have been warned! + */ + add_taint(TAINT_WARN, LOCKDEP_STILL_OK); error: i915_gem_set_wedged(i915); i915_gem_retire_requests(i915); @@ -2512,7 +2546,7 @@ static int intel_runtime_suspend(struct device *kdev) struct drm_i915_private *dev_priv = to_i915(dev); int ret; - if (WARN_ON_ONCE(!(dev_priv->gt_pm.rc6.enabled && intel_rc6_enabled()))) + if (WARN_ON_ONCE(!(dev_priv->gt_pm.rc6.enabled && HAS_RC6(dev_priv)))) return -ENODEV; if (WARN_ON_ONCE(!HAS_RUNTIME_PM(dev_priv))) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 36bb4927484a..1aba5657f5f0 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -40,6 +40,7 @@ #include <linux/hash.h> #include <linux/intel-iommu.h> #include <linux/kref.h> +#include <linux/perf_event.h> #include <linux/pm_qos.h> #include <linux/reservation.h> #include <linux/shmem_fs.h> @@ -79,8 +80,8 @@ #define DRIVER_NAME "i915" #define DRIVER_DESC "Intel Graphics" -#define DRIVER_DATE "20171117" -#define DRIVER_TIMESTAMP 1510958822 +#define DRIVER_DATE "20171214" +#define DRIVER_TIMESTAMP 1513282202 /* Use I915_STATE_WARN(x) and I915_STATE_WARN_ON() (rather than WARN() and * WARN_ON()) for hw state sanity checks to check for unexpected conditions @@ -304,9 +305,9 @@ static inline bool transcoder_is_dsi(enum transcoder transcoder) /* * Global legacy plane identifier. Valid only for primary/sprite - * planes on pre-g4x, and only for primary planes on g4x+. + * planes on pre-g4x, and only for primary planes on g4x-bdw. */ -enum plane { +enum i9xx_plane_id { PLANE_A, PLANE_B, PLANE_C, @@ -397,6 +398,7 @@ enum intel_display_power_domain { POWER_DOMAIN_AUX_D, POWER_DOMAIN_GMBUS, POWER_DOMAIN_MODESET, + POWER_DOMAIN_GT_IRQ, POWER_DOMAIN_INIT, POWER_DOMAIN_NUM, @@ -560,13 +562,13 @@ struct i915_hotplug { for_each_power_well_rev(__dev_priv, __power_well) \ for_each_if ((__power_well)->domains & (__domain_mask)) -#define for_each_intel_plane_in_state(__state, plane, plane_state, __i) \ +#define for_each_new_intel_plane_in_state(__state, plane, new_plane_state, __i) \ for ((__i) = 0; \ (__i) < (__state)->base.dev->mode_config.num_total_plane && \ ((plane) = to_intel_plane((__state)->base.planes[__i].ptr), \ - (plane_state) = to_intel_plane_state((__state)->base.planes[__i].state), 1); \ + (new_plane_state) = to_intel_plane_state((__state)->base.planes[__i].new_state), 1); \ (__i)++) \ - for_each_if (plane_state) + for_each_if (plane) #define for_each_new_intel_crtc_in_state(__state, crtc, new_crtc_state, __i) \ for ((__i) = 0; \ @@ -576,7 +578,6 @@ struct i915_hotplug { (__i)++) \ for_each_if (crtc) - #define for_each_oldnew_intel_plane_in_state(__state, plane, old_plane_state, new_plane_state, __i) \ for ((__i) = 0; \ (__i) < (__state)->base.dev->mode_config.num_total_plane && \ @@ -698,7 +699,8 @@ struct drm_i915_display_funcs { struct intel_cdclk_state *cdclk_state); void (*set_cdclk)(struct drm_i915_private *dev_priv, const struct intel_cdclk_state *cdclk_state); - int (*get_fifo_size)(struct drm_i915_private *dev_priv, int plane); + int (*get_fifo_size)(struct drm_i915_private *dev_priv, + enum i9xx_plane_id i9xx_plane); int (*compute_pipe_wm)(struct intel_crtc_state *cstate); int (*compute_intermediate_wm)(struct drm_device *dev, struct intel_crtc *intel_crtc, @@ -942,7 +944,6 @@ struct i915_gpu_state { u64 fence[I915_MAX_NUM_FENCES]; struct intel_overlay_error_state *overlay; struct intel_display_error_state *display; - struct drm_i915_error_object *semaphore; struct drm_i915_error_engine { int engine_id; @@ -1009,6 +1010,7 @@ struct i915_gpu_state { long user_bo_count; struct drm_i915_error_object *wa_ctx; + struct drm_i915_error_object *default_state; struct drm_i915_error_request { long jiffies; @@ -1145,7 +1147,7 @@ struct intel_fbc { struct { enum pipe pipe; - enum plane plane; + enum i9xx_plane_id i9xx_plane; unsigned int fence_y_offset; } crtc; @@ -1536,9 +1538,6 @@ struct i915_gem_mm { */ struct pagevec wc_stash; - /** Usable portion of the GTT for GEM */ - dma_addr_t stolen_base; /* limited to low memory (32-bit) */ - /** * tmpfs instance used for shmem backed objects */ @@ -1587,6 +1586,8 @@ struct drm_i915_error_state_buf { loff_t pos; }; +#define I915_IDLE_ENGINES_TIMEOUT (200) /* in ms */ + #define I915_RESET_TIMEOUT (10 * HZ) /* 10s */ #define I915_FENCE_TIMEOUT (10 * HZ) /* 10s */ @@ -2252,6 +2253,30 @@ struct drm_i915_private { const struct intel_device_info info; + /** + * Data Stolen Memory - aka "i915 stolen memory" gives us the start and + * end of stolen which we can optionally use to create GEM objects + * backed by stolen memory. Note that stolen_usable_size tells us + * exactly how much of this we are actually allowed to use, given that + * some portion of it is in fact reserved for use by hardware functions. + */ + struct resource dsm; + /** + * Reseved portion of Data Stolen Memory + */ + struct resource dsm_reserved; + + /* + * Stolen memory is segmented in hardware with different portions + * offlimits to certain functions. + * + * The drm_mm is initialised to the total accessible range, as found + * from the PCI config. On Broadwell+, this is further restricted to + * avoid the first page! The upper end of stolen memory is reserved for + * hardware functions and similarly removed from the accessible range. + */ + resource_size_t stolen_usable_size; /* Total size minus reserved ranges */ + void __iomem *regs; struct intel_uncore uncore; @@ -2291,7 +2316,8 @@ struct drm_i915_private { struct i915_gem_context *kernel_context; /* Context only to be used for injecting preemption commands */ struct i915_gem_context *preempt_context; - struct i915_vma *semaphore; + struct intel_engine_cs *engine_class[MAX_ENGINE_CLASS + 1] + [MAX_ENGINE_INSTANCE + 1]; struct drm_dma_handle *status_page_dmah; struct resource mch_res; @@ -2619,7 +2645,6 @@ struct drm_i915_private { bool periodic; int period_exponent; - int timestamp_frequency; struct i915_oa_config test_config; @@ -2764,6 +2789,8 @@ struct drm_i915_private { int irq; } lpe_audio; + struct i915_pmu pmu; + /* * NOTE: This is the dri1/ums dungeon, don't add stuff here. Your patch * will be rejected. Instead look for a better place. @@ -3142,6 +3169,8 @@ intel_info(const struct drm_i915_private *dev_priv) #define HAS_BLT(dev_priv) HAS_ENGINE(dev_priv, BCS) #define HAS_VEBOX(dev_priv) HAS_ENGINE(dev_priv, VECS) +#define HAS_LEGACY_SEMAPHORES(dev_priv) IS_GEN7(dev_priv) + #define HAS_LLC(dev_priv) ((dev_priv)->info.has_llc) #define HAS_SNOOP(dev_priv) ((dev_priv)->info.has_snoop) #define HAS_EDRAM(dev_priv) (!!((dev_priv)->edram_cap & EDRAM_ENABLED)) @@ -3154,6 +3183,9 @@ intel_info(const struct drm_i915_private *dev_priv) ((dev_priv)->info.has_logical_ring_contexts) #define HAS_LOGICAL_RING_PREEMPTION(dev_priv) \ ((dev_priv)->info.has_logical_ring_preemption) + +#define HAS_EXECLISTS(dev_priv) HAS_LOGICAL_RING_CONTEXTS(dev_priv) + #define USES_PPGTT(dev_priv) (i915_modparams.enable_ppgtt) #define USES_FULL_PPGTT(dev_priv) (i915_modparams.enable_ppgtt >= 2) #define USES_FULL_48BIT_PPGTT(dev_priv) (i915_modparams.enable_ppgtt == 3) @@ -3205,8 +3237,10 @@ intel_info(const struct drm_i915_private *dev_priv) #define HAS_DDI(dev_priv) ((dev_priv)->info.has_ddi) #define HAS_FPGA_DBG_UNCLAIMED(dev_priv) ((dev_priv)->info.has_fpga_dbg) #define HAS_PSR(dev_priv) ((dev_priv)->info.has_psr) + #define HAS_RC6(dev_priv) ((dev_priv)->info.has_rc6) #define HAS_RC6p(dev_priv) ((dev_priv)->info.has_rc6p) +#define HAS_RC6pp(dev_priv) (false) /* HW was never validated */ #define HAS_CSR(dev_priv) ((dev_priv)->info.has_csr) @@ -3224,8 +3258,16 @@ intel_info(const struct drm_i915_private *dev_priv) #define HAS_GUC_CT(dev_priv) ((dev_priv)->info.has_guc_ct) #define HAS_GUC_UCODE(dev_priv) (HAS_GUC(dev_priv)) #define HAS_GUC_SCHED(dev_priv) (HAS_GUC(dev_priv)) + +/* For now, anything with a GuC has also HuC */ +#define HAS_HUC(dev_priv) (HAS_GUC(dev_priv)) #define HAS_HUC_UCODE(dev_priv) (HAS_GUC(dev_priv)) +/* Having a GuC is not the same as using a GuC */ +#define USES_GUC(dev_priv) intel_uc_is_using_guc() +#define USES_GUC_SUBMISSION(dev_priv) intel_uc_is_using_guc_submission() +#define USES_HUC(dev_priv) intel_uc_is_using_huc() + #define HAS_RESOURCE_STREAMER(dev_priv) ((dev_priv)->info.has_resource_streamer) #define HAS_POOLED_EU(dev_priv) ((dev_priv)->info.has_pooled_eu) @@ -3302,8 +3344,6 @@ intel_ggtt_update_needs_vtd_wa(struct drm_i915_private *dev_priv) int intel_sanitize_enable_ppgtt(struct drm_i915_private *dev_priv, int enable_ppgtt); -bool intel_sanitize_semaphores(struct drm_i915_private *dev_priv, int value); - /* i915_drv.c */ void __printf(3, 4) __i915_printk(struct drm_i915_private *dev_priv, const char *level, @@ -3871,6 +3911,8 @@ int __must_check i915_gem_evict_for_node(struct i915_address_space *vm, unsigned int flags); int i915_gem_evict_vm(struct i915_address_space *vm); +void i915_gem_flush_ggtt_writes(struct drm_i915_private *dev_priv); + /* belongs in i915_gem_gtt.h */ static inline void i915_gem_chipset_flush(struct drm_i915_private *dev_priv) { @@ -3892,12 +3934,13 @@ void i915_gem_stolen_remove_node(struct drm_i915_private *dev_priv, int i915_gem_init_stolen(struct drm_i915_private *dev_priv); void i915_gem_cleanup_stolen(struct drm_device *dev); struct drm_i915_gem_object * -i915_gem_object_create_stolen(struct drm_i915_private *dev_priv, u32 size); +i915_gem_object_create_stolen(struct drm_i915_private *dev_priv, + resource_size_t size); struct drm_i915_gem_object * i915_gem_object_create_stolen_for_preallocated(struct drm_i915_private *dev_priv, - u32 stolen_offset, - u32 gtt_offset, - u32 size); + resource_size_t stolen_offset, + resource_size_t gtt_offset, + resource_size_t size); /* i915_gem_internal.c */ struct drm_i915_gem_object * @@ -3905,7 +3948,7 @@ i915_gem_object_create_internal(struct drm_i915_private *dev_priv, phys_addr_t size); /* i915_gem_shrinker.c */ -unsigned long i915_gem_shrink(struct drm_i915_private *dev_priv, +unsigned long i915_gem_shrink(struct drm_i915_private *i915, unsigned long target, unsigned long *nr_scanned, unsigned flags); @@ -3914,9 +3957,9 @@ unsigned long i915_gem_shrink(struct drm_i915_private *dev_priv, #define I915_SHRINK_BOUND 0x4 #define I915_SHRINK_ACTIVE 0x8 #define I915_SHRINK_VMAPS 0x10 -unsigned long i915_gem_shrink_all(struct drm_i915_private *dev_priv); -void i915_gem_shrinker_init(struct drm_i915_private *dev_priv); -void i915_gem_shrinker_cleanup(struct drm_i915_private *dev_priv); +unsigned long i915_gem_shrink_all(struct drm_i915_private *i915); +void i915_gem_shrinker_register(struct drm_i915_private *i915); +void i915_gem_shrinker_unregister(struct drm_i915_private *i915); /* i915_gem_tiling.c */ @@ -4223,9 +4266,17 @@ void vlv_phy_reset_lanes(struct intel_encoder *encoder, int intel_gpu_freq(struct drm_i915_private *dev_priv, int val); int intel_freq_opcode(struct drm_i915_private *dev_priv, int val); -u64 intel_rc6_residency_us(struct drm_i915_private *dev_priv, +u64 intel_rc6_residency_ns(struct drm_i915_private *dev_priv, const i915_reg_t reg); +u32 intel_get_cagf(struct drm_i915_private *dev_priv, u32 rpstat1); + +static inline u64 intel_rc6_residency_us(struct drm_i915_private *dev_priv, + const i915_reg_t reg) +{ + return DIV_ROUND_UP_ULL(intel_rc6_residency_ns(dev_priv, reg), 1000); +} + #define I915_READ8(reg) dev_priv->uncore.funcs.mmio_readb(dev_priv, (reg), true) #define I915_WRITE8(reg, val) dev_priv->uncore.funcs.mmio_writeb(dev_priv, (reg), (val), true) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 61ba321e9970..4a7f5579a7a5 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -330,17 +330,10 @@ int i915_gem_object_unbind(struct drm_i915_gem_object *obj) * must wait for all rendering to complete to the object (as unbinding * must anyway), and retire the requests. */ - ret = i915_gem_object_wait(obj, - I915_WAIT_INTERRUPTIBLE | - I915_WAIT_LOCKED | - I915_WAIT_ALL, - MAX_SCHEDULE_TIMEOUT, - NULL); + ret = i915_gem_object_set_to_cpu_domain(obj, false); if (ret) return ret; - i915_gem_retire_requests(to_i915(obj->base.dev)); - while ((vma = list_first_entry_or_null(&obj->vma_list, struct i915_vma, obj_link))) { @@ -673,17 +666,13 @@ fb_write_origin(struct drm_i915_gem_object *obj, unsigned int domain) obj->frontbuffer_ggtt_origin : ORIGIN_CPU); } -static void -flush_write_domain(struct drm_i915_gem_object *obj, unsigned int flush_domains) +void i915_gem_flush_ggtt_writes(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(obj->base.dev); - - if (!(obj->base.write_domain & flush_domains)) - return; - - /* No actual flushing is required for the GTT write domain. Writes - * to it "immediately" go to main memory as far as we know, so there's - * no chipset flush. It also doesn't land in render cache. + /* + * No actual flushing is required for the GTT write domain for reads + * from the GTT domain. Writes to it "immediately" go to main memory + * as far as we know, so there's no chipset flush. It also doesn't + * land in the GPU render cache. * * However, we do have to enforce the order so that all writes through * the GTT land before any writes to the device, such as updates to @@ -694,22 +683,43 @@ flush_write_domain(struct drm_i915_gem_object *obj, unsigned int flush_domains) * timing. This issue has only been observed when switching quickly * between GTT writes and CPU reads from inside the kernel on recent hw, * and it appears to only affect discrete GTT blocks (i.e. on LLC - * system agents we cannot reproduce this behaviour). + * system agents we cannot reproduce this behaviour, until Cannonlake + * that was!). */ + wmb(); + intel_runtime_pm_get(dev_priv); + spin_lock_irq(&dev_priv->uncore.lock); + + POSTING_READ_FW(RING_HEAD(RENDER_RING_BASE)); + + spin_unlock_irq(&dev_priv->uncore.lock); + intel_runtime_pm_put(dev_priv); +} + +static void +flush_write_domain(struct drm_i915_gem_object *obj, unsigned int flush_domains) +{ + struct drm_i915_private *dev_priv = to_i915(obj->base.dev); + struct i915_vma *vma; + + if (!(obj->base.write_domain & flush_domains)) + return; + switch (obj->base.write_domain) { case I915_GEM_DOMAIN_GTT: - if (!HAS_LLC(dev_priv)) { - intel_runtime_pm_get(dev_priv); - spin_lock_irq(&dev_priv->uncore.lock); - POSTING_READ_FW(RING_HEAD(dev_priv->engine[RCS]->mmio_base)); - spin_unlock_irq(&dev_priv->uncore.lock); - intel_runtime_pm_put(dev_priv); - } + i915_gem_flush_ggtt_writes(dev_priv); intel_fb_obj_flush(obj, fb_write_origin(obj, I915_GEM_DOMAIN_GTT)); + + for_each_ggtt_vma(vma, obj) { + if (vma->iomap) + continue; + + i915_vma_unset_ggtt_write(vma); + } break; case I915_GEM_DOMAIN_CPU: @@ -1106,7 +1116,7 @@ i915_gem_gtt_pread(struct drm_i915_gem_object *obj, page_base += offset & PAGE_MASK; } - if (gtt_user_read(&ggtt->mappable, page_base, page_offset, + if (gtt_user_read(&ggtt->iomap, page_base, page_offset, user_data, page_length)) { ret = -EFAULT; break; @@ -1314,7 +1324,7 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_gem_object *obj, * If the object is non-shmem backed, we retry again with the * path that handles page fault. */ - if (ggtt_write(&ggtt->mappable, page_base, page_offset, + if (ggtt_write(&ggtt->iomap, page_base, page_offset, user_data, page_length)) { ret = -EFAULT; break; @@ -1556,10 +1566,7 @@ static void i915_gem_object_bump_inactive_ggtt(struct drm_i915_gem_object *obj) GEM_BUG_ON(!i915_gem_object_has_pinned_pages(obj)); - list_for_each_entry(vma, &obj->vma_list, obj_link) { - if (!i915_vma_is_ggtt(vma)) - break; - + for_each_ggtt_vma(vma, obj) { if (i915_vma_is_active(vma)) continue; @@ -1960,9 +1967,9 @@ int i915_gem_fault(struct vm_fault *vmf) /* Finally, remap it using the new GTT offset */ ret = remap_io_mapping(area, area->vm_start + (vma->ggtt_view.partial.offset << PAGE_SHIFT), - (ggtt->mappable_base + vma->node.start) >> PAGE_SHIFT, + (ggtt->gmadr.start + vma->node.start) >> PAGE_SHIFT, min_t(u64, vma->size, area->vm_end - area->vm_start), - &ggtt->mappable); + &ggtt->iomap); if (ret) goto err_fence; @@ -1972,6 +1979,8 @@ int i915_gem_fault(struct vm_fault *vmf) list_add(&obj->userfault_link, &dev_priv->mm.userfault_list); GEM_BUG_ON(!obj->userfault_count); + i915_vma_set_ggtt_write(vma); + err_fence: i915_vma_unpin_fence(vma); err_unpin: @@ -2036,12 +2045,8 @@ static void __i915_gem_object_release_mmap(struct drm_i915_gem_object *obj) drm_vma_node_unmap(&obj->base.vma_node, obj->base.dev->anon_inode->i_mapping); - list_for_each_entry(vma, &obj->vma_list, obj_link) { - if (!i915_vma_is_ggtt(vma)) - break; - + for_each_ggtt_vma(vma, obj) i915_vma_unset_userfault(vma); - } } /** @@ -3328,7 +3333,7 @@ i915_gem_idle_work_handler(struct work_struct *work) * Wait for last execlists context complete, but bail out in case a * new request is submitted. */ - end = ktime_add_ms(ktime_get(), 200); + end = ktime_add_ms(ktime_get(), I915_IDLE_ENGINES_TIMEOUT); do { if (new_requests_since_last_retire(dev_priv)) return; @@ -3371,7 +3376,9 @@ i915_gem_idle_work_handler(struct work_struct *work) synchronize_irq(dev_priv->drm.irq); intel_engines_park(dev_priv); - i915_gem_timelines_mark_idle(dev_priv); + i915_gem_timelines_park(dev_priv); + + i915_pmu_gt_parked(dev_priv); GEM_BUG_ON(!dev_priv->gt.awake); dev_priv->gt.awake = false; @@ -3379,6 +3386,9 @@ i915_gem_idle_work_handler(struct work_struct *work) if (INTEL_GEN(dev_priv) >= 6) gen6_rps_idle(dev_priv); + + intel_display_power_put(dev_priv, POWER_DOMAIN_GT_IRQ); + intel_runtime_pm_put(dev_priv); out_unlock: mutex_unlock(&dev_priv->drm.struct_mutex); @@ -3523,8 +3533,19 @@ static int wait_for_timeline(struct i915_gem_timeline *tl, unsigned int flags) static int wait_for_engines(struct drm_i915_private *i915) { - if (wait_for(intel_engines_are_idle(i915), 50)) { - DRM_ERROR("Failed to idle engines, declaring wedged!\n"); + if (wait_for(intel_engines_are_idle(i915), I915_IDLE_ENGINES_TIMEOUT)) { + dev_err(i915->drm.dev, + "Failed to idle engines, declaring wedged!\n"); + if (drm_debug & DRM_UT_DRIVER) { + struct drm_printer p = drm_debug_printer(__func__); + struct intel_engine_cs *engine; + enum intel_engine_id id; + + for_each_engine(engine, i915, id) + intel_engine_dump(engine, &p, + "%s", engine->name); + } + i915_gem_set_wedged(i915); return -EIO; } @@ -3550,9 +3571,7 @@ int i915_gem_wait_for_idle(struct drm_i915_private *i915, unsigned int flags) if (ret) return ret; } - i915_gem_retire_requests(i915); - GEM_BUG_ON(i915->gt.active_requests); ret = wait_for_engines(i915); } else { @@ -3751,7 +3770,8 @@ restart: return -EBUSY; } - if (i915_gem_valid_gtt_space(vma, cache_level)) + if (!i915_vma_is_closed(vma) && + i915_gem_valid_gtt_space(vma, cache_level)) continue; ret = i915_vma_unbind(vma); @@ -3804,7 +3824,7 @@ restart: * dropped the fence as all snoopable access is * supposed to be linear. */ - list_for_each_entry(vma, &obj->vma_list, obj_link) { + for_each_ggtt_vma(vma, obj) { ret = i915_vma_put_fence(vma); if (ret) return ret; @@ -4772,17 +4792,19 @@ int i915_gem_suspend(struct drm_i915_private *dev_priv) * state. Fortunately, the kernel_context is disposable and we do * not rely on its state. */ - ret = i915_gem_switch_to_kernel_context(dev_priv); - if (ret) - goto err_unlock; + if (!i915_terminally_wedged(&dev_priv->gpu_error)) { + ret = i915_gem_switch_to_kernel_context(dev_priv); + if (ret) + goto err_unlock; - ret = i915_gem_wait_for_idle(dev_priv, - I915_WAIT_INTERRUPTIBLE | - I915_WAIT_LOCKED); - if (ret && ret != -EIO) - goto err_unlock; + ret = i915_gem_wait_for_idle(dev_priv, + I915_WAIT_INTERRUPTIBLE | + I915_WAIT_LOCKED); + if (ret && ret != -EIO) + goto err_unlock; - assert_kernel_context_is_current(dev_priv); + assert_kernel_context_is_current(dev_priv); + } i915_gem_contexts_lost(dev_priv); mutex_unlock(&dev->struct_mutex); @@ -4843,7 +4865,8 @@ void i915_gem_resume(struct drm_i915_private *i915) i915_gem_restore_gtt_mappings(i915); i915_gem_restore_fences(i915); - /* As we didn't flush the kernel context before suspend, we cannot + /* + * As we didn't flush the kernel context before suspend, we cannot * guarantee that the context image is complete. So let's just reset * it and start again. */ @@ -4864,8 +4887,10 @@ out_unlock: return; err_wedged: - DRM_ERROR("failed to re-initialize GPU, declaring wedged!\n"); - i915_gem_set_wedged(i915); + if (!i915_terminally_wedged(&i915->gpu_error)) { + DRM_ERROR("failed to re-initialize GPU, declaring wedged!\n"); + i915_gem_set_wedged(i915); + } goto out_unlock; } @@ -4997,25 +5022,6 @@ out: return ret; } -bool intel_sanitize_semaphores(struct drm_i915_private *dev_priv, int value) -{ - if (INTEL_INFO(dev_priv)->gen < 6) - return false; - - /* TODO: make semaphores and Execlists play nicely together */ - if (i915_modparams.enable_execlists) - return false; - - if (value >= 0) - return value; - - /* Enable semaphores on SNB when IO remapping is off */ - if (IS_GEN6(dev_priv) && intel_vtd_active()) - return false; - - return true; -} - static int __intel_engines_record_defaults(struct drm_i915_private *i915) { struct i915_gem_context *ctx; @@ -5045,7 +5051,7 @@ static int __intel_engines_record_defaults(struct drm_i915_private *i915) goto out_ctx; } - err = i915_switch_context(rq); + err = 0; if (engine->init_context) err = engine->init_context(rq); @@ -5134,8 +5140,6 @@ int i915_gem_init(struct drm_i915_private *dev_priv) { int ret; - mutex_lock(&dev_priv->drm.struct_mutex); - /* * We need to fallback to 4K pages since gvt gtt handling doesn't * support huge page entries - we will need to check either hypervisor @@ -5147,43 +5151,58 @@ int i915_gem_init(struct drm_i915_private *dev_priv) dev_priv->mm.unordered_timeline = dma_fence_context_alloc(1); - if (!i915_modparams.enable_execlists) { - dev_priv->gt.resume = intel_legacy_submission_resume; - dev_priv->gt.cleanup_engine = intel_engine_cleanup; - } else { + if (HAS_LOGICAL_RING_CONTEXTS(dev_priv)) { dev_priv->gt.resume = intel_lr_context_resume; dev_priv->gt.cleanup_engine = intel_logical_ring_cleanup; + } else { + dev_priv->gt.resume = intel_legacy_submission_resume; + dev_priv->gt.cleanup_engine = intel_engine_cleanup; } + ret = i915_gem_init_userptr(dev_priv); + if (ret) + return ret; + + ret = intel_uc_init_wq(dev_priv); + if (ret) + return ret; + /* This is just a security blanket to placate dragons. * On some systems, we very sporadically observe that the first TLBs * used by the CS may be stale, despite us poking the TLB reset. If * we hold the forcewake during initialisation these problems * just magically go away. */ + mutex_lock(&dev_priv->drm.struct_mutex); intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); - ret = i915_gem_init_userptr(dev_priv); - if (ret) - goto out_unlock; - ret = i915_gem_init_ggtt(dev_priv); - if (ret) - goto out_unlock; + if (ret) { + GEM_BUG_ON(ret == -EIO); + goto err_unlock; + } ret = i915_gem_contexts_init(dev_priv); - if (ret) - goto out_unlock; + if (ret) { + GEM_BUG_ON(ret == -EIO); + goto err_ggtt; + } ret = intel_engines_init(dev_priv); - if (ret) - goto out_unlock; + if (ret) { + GEM_BUG_ON(ret == -EIO); + goto err_context; + } intel_init_gt_powersave(dev_priv); + ret = intel_uc_init(dev_priv); + if (ret) + goto err_pm; + ret = i915_gem_init_hw(dev_priv); if (ret) - goto out_unlock; + goto err_uc_init; /* * Despite its name intel_init_clock_gating applies both display @@ -5197,9 +5216,55 @@ int i915_gem_init(struct drm_i915_private *dev_priv) intel_init_clock_gating(dev_priv); ret = __intel_engines_record_defaults(dev_priv); -out_unlock: + if (ret) + goto err_init_hw; + + if (i915_inject_load_failure()) { + ret = -ENODEV; + goto err_init_hw; + } + + if (i915_inject_load_failure()) { + ret = -EIO; + goto err_init_hw; + } + + intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); + mutex_unlock(&dev_priv->drm.struct_mutex); + + return 0; + + /* + * Unwinding is complicated by that we want to handle -EIO to mean + * disable GPU submission but keep KMS alive. We want to mark the + * HW as irrevisibly wedged, but keep enough state around that the + * driver doesn't explode during runtime. + */ +err_init_hw: + i915_gem_wait_for_idle(dev_priv, I915_WAIT_LOCKED); + i915_gem_contexts_lost(dev_priv); + intel_uc_fini_hw(dev_priv); +err_uc_init: + intel_uc_fini(dev_priv); +err_pm: + if (ret != -EIO) { + intel_cleanup_gt_powersave(dev_priv); + i915_gem_cleanup_engines(dev_priv); + } +err_context: + if (ret != -EIO) + i915_gem_contexts_fini(dev_priv); +err_ggtt: +err_unlock: + intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); + mutex_unlock(&dev_priv->drm.struct_mutex); + + if (ret != -EIO) + i915_gem_cleanup_userptr(dev_priv); + if (ret == -EIO) { - /* Allow engine initialisation to fail by marking the GPU as + /* + * Allow engine initialisation to fail by marking the GPU as * wedged. But we only want to do this where the GPU is angry, * for all other failure, such as an allocation failure, bail. */ @@ -5209,9 +5274,8 @@ out_unlock: } ret = 0; } - intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); - mutex_unlock(&dev_priv->drm.struct_mutex); + i915_gem_drain_freed_objects(dev_priv); return ret; } diff --git a/drivers/gpu/drm/i915/i915_gem_clflush.c b/drivers/gpu/drm/i915/i915_gem_clflush.c index f663cd919795..b9b53ac14176 100644 --- a/drivers/gpu/drm/i915/i915_gem_clflush.c +++ b/drivers/gpu/drm/i915/i915_gem_clflush.c @@ -167,7 +167,7 @@ bool i915_gem_clflush_object(struct drm_i915_gem_object *obj, i915_sw_fence_await_reservation(&clflush->wait, obj->resv, NULL, true, I915_FENCE_TIMEOUT, - GFP_KERNEL); + I915_FENCE_GFP); reservation_object_lock(obj->resv, NULL); reservation_object_add_excl_fence(obj->resv, &clflush->dma); diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 2db040695035..648e7536ff51 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -316,7 +316,7 @@ __create_hw_context(struct drm_i915_private *dev_priv, * present or not in use we still need a small bias as ring wraparound * at offset 0 sometimes hangs. No idea why. */ - if (HAS_GUC(dev_priv) && i915_modparams.enable_guc_loading) + if (USES_GUC(dev_priv)) ctx->ggtt_offset_bias = GUC_WOPCM_TOP; else ctx->ggtt_offset_bias = I915_GTT_PAGE_SIZE; @@ -409,7 +409,7 @@ i915_gem_context_create_gvt(struct drm_device *dev) i915_gem_context_set_closed(ctx); /* not user accessible */ i915_gem_context_clear_bannable(ctx); i915_gem_context_set_force_single_submission(ctx); - if (!i915_modparams.enable_guc_submission) + if (!USES_GUC_SUBMISSION(to_i915(dev))) ctx->ring_size = 512 * PAGE_SIZE; /* Max ring buffer size */ GEM_BUG_ON(i915_gem_context_is_kernel(ctx)); @@ -460,14 +460,6 @@ int i915_gem_contexts_init(struct drm_i915_private *dev_priv) INIT_WORK(&dev_priv->contexts.free_work, contexts_free_worker); init_llist_head(&dev_priv->contexts.free_list); - if (intel_vgpu_active(dev_priv) && - HAS_LOGICAL_RING_CONTEXTS(dev_priv)) { - if (!i915_modparams.enable_execlists) { - DRM_INFO("Only EXECLIST mode is supported in vgpu.\n"); - return -EINVAL; - } - } - /* Using the simple ida interface, the max is limited by sizeof(int) */ BUILD_BUG_ON(MAX_CONTEXT_HW_ID > INT_MAX); ida_init(&dev_priv->contexts.hw_ida); @@ -515,6 +507,7 @@ void i915_gem_contexts_lost(struct drm_i915_private *dev_priv) for_each_engine(engine, dev_priv, id) { engine->legacy_active_context = NULL; + engine->legacy_active_ppgtt = NULL; if (!engine->last_retired_context) continue; @@ -574,300 +567,6 @@ void i915_gem_context_close(struct drm_file *file) idr_destroy(&file_priv->context_idr); } -static inline int -mi_set_context(struct drm_i915_gem_request *req, u32 flags) -{ - struct drm_i915_private *dev_priv = req->i915; - struct intel_engine_cs *engine = req->engine; - enum intel_engine_id id; - const int num_rings = - /* Use an extended w/a on gen7 if signalling from other rings */ - (i915_modparams.semaphores && INTEL_GEN(dev_priv) == 7) ? - INTEL_INFO(dev_priv)->num_rings - 1 : - 0; - int len; - u32 *cs; - - flags |= MI_MM_SPACE_GTT; - if (IS_HASWELL(dev_priv) || INTEL_GEN(dev_priv) >= 8) - /* These flags are for resource streamer on HSW+ */ - flags |= HSW_MI_RS_SAVE_STATE_EN | HSW_MI_RS_RESTORE_STATE_EN; - else - flags |= MI_SAVE_EXT_STATE_EN | MI_RESTORE_EXT_STATE_EN; - - len = 4; - if (INTEL_GEN(dev_priv) >= 7) - len += 2 + (num_rings ? 4*num_rings + 6 : 0); - - cs = intel_ring_begin(req, len); - if (IS_ERR(cs)) - return PTR_ERR(cs); - - /* WaProgramMiArbOnOffAroundMiSetContext:ivb,vlv,hsw,bdw,chv */ - if (INTEL_GEN(dev_priv) >= 7) { - *cs++ = MI_ARB_ON_OFF | MI_ARB_DISABLE; - if (num_rings) { - struct intel_engine_cs *signaller; - - *cs++ = MI_LOAD_REGISTER_IMM(num_rings); - for_each_engine(signaller, dev_priv, id) { - if (signaller == engine) - continue; - - *cs++ = i915_mmio_reg_offset( - RING_PSMI_CTL(signaller->mmio_base)); - *cs++ = _MASKED_BIT_ENABLE( - GEN6_PSMI_SLEEP_MSG_DISABLE); - } - } - } - - *cs++ = MI_NOOP; - *cs++ = MI_SET_CONTEXT; - *cs++ = i915_ggtt_offset(req->ctx->engine[RCS].state) | flags; - /* - * w/a: MI_SET_CONTEXT must always be followed by MI_NOOP - * WaMiSetContext_Hang:snb,ivb,vlv - */ - *cs++ = MI_NOOP; - - if (INTEL_GEN(dev_priv) >= 7) { - if (num_rings) { - struct intel_engine_cs *signaller; - i915_reg_t last_reg = {}; /* keep gcc quiet */ - - *cs++ = MI_LOAD_REGISTER_IMM(num_rings); - for_each_engine(signaller, dev_priv, id) { - if (signaller == engine) - continue; - - last_reg = RING_PSMI_CTL(signaller->mmio_base); - *cs++ = i915_mmio_reg_offset(last_reg); - *cs++ = _MASKED_BIT_DISABLE( - GEN6_PSMI_SLEEP_MSG_DISABLE); - } - - /* Insert a delay before the next switch! */ - *cs++ = MI_STORE_REGISTER_MEM | MI_SRM_LRM_GLOBAL_GTT; - *cs++ = i915_mmio_reg_offset(last_reg); - *cs++ = i915_ggtt_offset(engine->scratch); - *cs++ = MI_NOOP; - } - *cs++ = MI_ARB_ON_OFF | MI_ARB_ENABLE; - } - - intel_ring_advance(req, cs); - - return 0; -} - -static int remap_l3(struct drm_i915_gem_request *req, int slice) -{ - u32 *cs, *remap_info = req->i915->l3_parity.remap_info[slice]; - int i; - - if (!remap_info) - return 0; - - cs = intel_ring_begin(req, GEN7_L3LOG_SIZE/4 * 2 + 2); - if (IS_ERR(cs)) - return PTR_ERR(cs); - - /* - * Note: We do not worry about the concurrent register cacheline hang - * here because no other code should access these registers other than - * at initialization time. - */ - *cs++ = MI_LOAD_REGISTER_IMM(GEN7_L3LOG_SIZE/4); - for (i = 0; i < GEN7_L3LOG_SIZE/4; i++) { - *cs++ = i915_mmio_reg_offset(GEN7_L3LOG(slice, i)); - *cs++ = remap_info[i]; - } - *cs++ = MI_NOOP; - intel_ring_advance(req, cs); - - return 0; -} - -static inline bool skip_rcs_switch(struct i915_hw_ppgtt *ppgtt, - struct intel_engine_cs *engine, - struct i915_gem_context *to) -{ - if (to->remap_slice) - return false; - - if (ppgtt && (intel_engine_flag(engine) & ppgtt->pd_dirty_rings)) - return false; - - return to == engine->legacy_active_context; -} - -static bool -needs_pd_load_pre(struct i915_hw_ppgtt *ppgtt, struct intel_engine_cs *engine) -{ - struct i915_gem_context *from = engine->legacy_active_context; - - if (!ppgtt) - return false; - - /* Always load the ppgtt on first use */ - if (!from) - return true; - - /* Same context without new entries, skip */ - if ((!from->ppgtt || from->ppgtt == ppgtt) && - !(intel_engine_flag(engine) & ppgtt->pd_dirty_rings)) - return false; - - if (engine->id != RCS) - return true; - - if (INTEL_GEN(engine->i915) < 8) - return true; - - return false; -} - -static bool -needs_pd_load_post(struct i915_hw_ppgtt *ppgtt, - struct i915_gem_context *to, - u32 hw_flags) -{ - if (!ppgtt) - return false; - - if (!IS_GEN8(to->i915)) - return false; - - if (hw_flags & MI_RESTORE_INHIBIT) - return true; - - return false; -} - -static int do_rcs_switch(struct drm_i915_gem_request *req) -{ - struct i915_gem_context *to = req->ctx; - struct intel_engine_cs *engine = req->engine; - struct i915_hw_ppgtt *ppgtt = to->ppgtt ?: req->i915->mm.aliasing_ppgtt; - struct i915_gem_context *from = engine->legacy_active_context; - u32 hw_flags; - int ret, i; - - GEM_BUG_ON(engine->id != RCS); - - if (skip_rcs_switch(ppgtt, engine, to)) - return 0; - - if (needs_pd_load_pre(ppgtt, engine)) { - /* Older GENs and non render rings still want the load first, - * "PP_DCLV followed by PP_DIR_BASE register through Load - * Register Immediate commands in Ring Buffer before submitting - * a context."*/ - trace_switch_mm(engine, to); - ret = ppgtt->switch_mm(ppgtt, req); - if (ret) - return ret; - } - - if (i915_gem_context_is_kernel(to)) - /* - * The kernel context(s) is treated as pure scratch and is not - * expected to retain any state (as we sacrifice it during - * suspend and on resume it may be corrupted). This is ok, - * as nothing actually executes using the kernel context; it - * is purely used for flushing user contexts. - */ - hw_flags = MI_RESTORE_INHIBIT; - else if (ppgtt && intel_engine_flag(engine) & ppgtt->pd_dirty_rings) - hw_flags = MI_FORCE_RESTORE; - else - hw_flags = 0; - - if (to != from || (hw_flags & MI_FORCE_RESTORE)) { - ret = mi_set_context(req, hw_flags); - if (ret) - return ret; - - engine->legacy_active_context = to; - } - - /* GEN8 does *not* require an explicit reload if the PDPs have been - * setup, and we do not wish to move them. - */ - if (needs_pd_load_post(ppgtt, to, hw_flags)) { - trace_switch_mm(engine, to); - ret = ppgtt->switch_mm(ppgtt, req); - /* The hardware context switch is emitted, but we haven't - * actually changed the state - so it's probably safe to bail - * here. Still, let the user know something dangerous has - * happened. - */ - if (ret) - return ret; - } - - if (ppgtt) - ppgtt->pd_dirty_rings &= ~intel_engine_flag(engine); - - for (i = 0; i < MAX_L3_SLICES; i++) { - if (!(to->remap_slice & (1<<i))) - continue; - - ret = remap_l3(req, i); - if (ret) - return ret; - - to->remap_slice &= ~(1<<i); - } - - return 0; -} - -/** - * i915_switch_context() - perform a GPU context switch. - * @req: request for which we'll execute the context switch - * - * The context life cycle is simple. The context refcount is incremented and - * decremented by 1 and create and destroy. If the context is in use by the GPU, - * it will have a refcount > 1. This allows us to destroy the context abstract - * object while letting the normal object tracking destroy the backing BO. - * - * This function should not be used in execlists mode. Instead the context is - * switched by writing to the ELSP and requests keep a reference to their - * context. - */ -int i915_switch_context(struct drm_i915_gem_request *req) -{ - struct intel_engine_cs *engine = req->engine; - - lockdep_assert_held(&req->i915->drm.struct_mutex); - if (i915_modparams.enable_execlists) - return 0; - - if (!req->ctx->engine[engine->id].state) { - struct i915_gem_context *to = req->ctx; - struct i915_hw_ppgtt *ppgtt = - to->ppgtt ?: req->i915->mm.aliasing_ppgtt; - - if (needs_pd_load_pre(ppgtt, engine)) { - int ret; - - trace_switch_mm(engine, to); - ret = ppgtt->switch_mm(ppgtt, req); - if (ret) - return ret; - - ppgtt->pd_dirty_rings &= ~intel_engine_flag(engine); - } - - engine->legacy_active_context = to; - return 0; - } - - return do_rcs_switch(req); -} - static bool engine_has_idle_kernel_context(struct intel_engine_cs *engine) { struct i915_gem_timeline *timeline; @@ -899,7 +598,6 @@ int i915_gem_switch_to_kernel_context(struct drm_i915_private *dev_priv) for_each_engine(engine, dev_priv, id) { struct drm_i915_gem_request *req; - int ret; if (engine_has_idle_kernel_context(engine)) continue; @@ -919,13 +617,17 @@ int i915_gem_switch_to_kernel_context(struct drm_i915_private *dev_priv) if (prev) i915_sw_fence_await_sw_fence_gfp(&req->submit, &prev->submit, - GFP_KERNEL); + I915_FENCE_GFP); } - ret = i915_switch_context(req); - i915_add_request(req); - if (ret) - return ret; + /* + * Force a flush after the switch to ensure that all rendering + * and operations prior to switching to the kernel context hits + * memory. This should be guaranteed by the previous request, + * but an extra layer of paranoia before we declare the system + * idle (on suspend etc) is advisable! + */ + __i915_add_request(req, true); } return 0; diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 53ccb27bfe91..4401068ff468 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -271,7 +271,7 @@ static inline u64 gen8_noncanonical_addr(u64 address) static inline bool eb_use_cmdparser(const struct i915_execbuffer *eb) { - return eb->engine->needs_cmd_parser && eb->batch_len; + return intel_engine_needs_cmd_parser(eb->engine) && eb->batch_len; } static int eb_create(struct i915_execbuffer *eb) @@ -1012,7 +1012,7 @@ static void *reloc_iomap(struct drm_i915_gem_object *obj, offset += page << PAGE_SHIFT; } - vaddr = (void __force *)io_mapping_map_atomic_wc(&ggtt->mappable, + vaddr = (void __force *)io_mapping_map_atomic_wc(&ggtt->iomap, offset); cache->page = page; cache->vaddr = (unsigned long)vaddr; @@ -1111,14 +1111,6 @@ static int __reloc_gpu_alloc(struct i915_execbuffer *eb, if (err) goto err_request; - err = eb->engine->emit_flush(rq, EMIT_INVALIDATE); - if (err) - goto err_request; - - err = i915_switch_context(rq); - if (err) - goto err_request; - err = eb->engine->emit_bb_start(rq, batch->node.start, PAGE_SIZE, cache->gen > 5 ? 0 : I915_DISPATCH_SECURE); @@ -1818,8 +1810,7 @@ static int eb_move_to_gpu(struct i915_execbuffer *eb) /* Unconditionally flush any chipset caches (for streaming writes). */ i915_gem_chipset_flush(eb->i915); - /* Unconditionally invalidate GPU caches and TLBs. */ - return eb->engine->emit_flush(eb->request, EMIT_INVALIDATE); + return 0; } static bool i915_gem_check_execbuffer(struct drm_i915_gem_execbuffer2 *exec) @@ -1965,10 +1956,6 @@ static int eb_submit(struct i915_execbuffer *eb) if (err) return err; - err = i915_switch_context(eb->request); - if (err) - return err; - if (eb->args->flags & I915_EXEC_GEN7_SOL_RESET) { err = i915_reset_gen7_sol_offsets(eb->request); if (err) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 64e8ae1fd832..c5f393870532 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -178,7 +178,7 @@ int intel_sanitize_enable_ppgtt(struct drm_i915_private *dev_priv, return 0; } - if (INTEL_GEN(dev_priv) >= 8 && i915_modparams.enable_execlists) { + if (HAS_LOGICAL_RING_CONTEXTS(dev_priv)) { if (has_full_48bit_ppgtt) return 3; @@ -2162,7 +2162,7 @@ int i915_ppgtt_init_hw(struct drm_i915_private *dev_priv) /* In the case of execlists, PPGTT is enabled by the context descriptor * and the PDPs are contained within the context itself. We don't * need to do anything here. */ - if (i915_modparams.enable_execlists) + if (HAS_LOGICAL_RING_CONTEXTS(dev_priv)) return 0; if (!USES_PPGTT(dev_priv)) @@ -2912,7 +2912,7 @@ void i915_ggtt_cleanup_hw(struct drm_i915_private *dev_priv) mutex_unlock(&dev_priv->drm.struct_mutex); arch_phys_wc_del(ggtt->mtrr); - io_mapping_fini(&ggtt->mappable); + io_mapping_fini(&ggtt->iomap); } static unsigned int gen6_get_total_gtt_size(u16 snb_gmch_ctl) @@ -2949,50 +2949,6 @@ static unsigned int chv_get_total_gtt_size(u16 gmch_ctrl) return 0; } -static size_t gen6_get_stolen_size(u16 snb_gmch_ctl) -{ - snb_gmch_ctl >>= SNB_GMCH_GMS_SHIFT; - snb_gmch_ctl &= SNB_GMCH_GMS_MASK; - return (size_t)snb_gmch_ctl << 25; /* 32 MB units */ -} - -static size_t gen8_get_stolen_size(u16 bdw_gmch_ctl) -{ - bdw_gmch_ctl >>= BDW_GMCH_GMS_SHIFT; - bdw_gmch_ctl &= BDW_GMCH_GMS_MASK; - return (size_t)bdw_gmch_ctl << 25; /* 32 MB units */ -} - -static size_t chv_get_stolen_size(u16 gmch_ctrl) -{ - gmch_ctrl >>= SNB_GMCH_GMS_SHIFT; - gmch_ctrl &= SNB_GMCH_GMS_MASK; - - /* - * 0x0 to 0x10: 32MB increments starting at 0MB - * 0x11 to 0x16: 4MB increments starting at 8MB - * 0x17 to 0x1d: 4MB increments start at 36MB - */ - if (gmch_ctrl < 0x11) - return (size_t)gmch_ctrl << 25; - else if (gmch_ctrl < 0x17) - return (size_t)(gmch_ctrl - 0x11 + 2) << 22; - else - return (size_t)(gmch_ctrl - 0x17 + 9) << 22; -} - -static size_t gen9_get_stolen_size(u16 gen9_gmch_ctl) -{ - gen9_gmch_ctl >>= BDW_GMCH_GMS_SHIFT; - gen9_gmch_ctl &= BDW_GMCH_GMS_MASK; - - if (gen9_gmch_ctl < 0xf0) - return (size_t)gen9_gmch_ctl << 25; /* 32 MB units */ - else - /* 4MB increments starting at 0xf0 for 4MB */ - return (size_t)(gen9_gmch_ctl - 0xf0 + 1) << 22; -} - static int ggtt_probe_common(struct i915_ggtt *ggtt, u64 size) { struct drm_i915_private *dev_priv = ggtt->base.i915; @@ -3332,8 +3288,10 @@ static int gen8_gmch_probe(struct i915_ggtt *ggtt) int err; /* TODO: We're not aware of mappable constraints on gen8 yet */ - ggtt->mappable_base = pci_resource_start(pdev, 2); - ggtt->mappable_end = pci_resource_len(pdev, 2); + ggtt->gmadr = + (struct resource) DEFINE_RES_MEM(pci_resource_start(pdev, 2), + pci_resource_len(pdev, 2)); + ggtt->mappable_end = resource_size(&ggtt->gmadr); err = pci_set_dma_mask(pdev, DMA_BIT_MASK(39)); if (!err) @@ -3344,13 +3302,10 @@ static int gen8_gmch_probe(struct i915_ggtt *ggtt) pci_read_config_word(pdev, SNB_GMCH_CTRL, &snb_gmch_ctl); if (INTEL_GEN(dev_priv) >= 9) { - ggtt->stolen_size = gen9_get_stolen_size(snb_gmch_ctl); size = gen8_get_total_gtt_size(snb_gmch_ctl); } else if (IS_CHERRYVIEW(dev_priv)) { - ggtt->stolen_size = chv_get_stolen_size(snb_gmch_ctl); size = chv_get_total_gtt_size(snb_gmch_ctl); } else { - ggtt->stolen_size = gen8_get_stolen_size(snb_gmch_ctl); size = gen8_get_total_gtt_size(snb_gmch_ctl); } @@ -3390,14 +3345,16 @@ static int gen6_gmch_probe(struct i915_ggtt *ggtt) u16 snb_gmch_ctl; int err; - ggtt->mappable_base = pci_resource_start(pdev, 2); - ggtt->mappable_end = pci_resource_len(pdev, 2); + ggtt->gmadr = + (struct resource) DEFINE_RES_MEM(pci_resource_start(pdev, 2), + pci_resource_len(pdev, 2)); + ggtt->mappable_end = resource_size(&ggtt->gmadr); /* 64/512MB is the current min/max we actually know of, but this is just * a coarse sanity check. */ if (ggtt->mappable_end < (64<<20) || ggtt->mappable_end > (512<<20)) { - DRM_ERROR("Unknown GMADR size (%llx)\n", ggtt->mappable_end); + DRM_ERROR("Unknown GMADR size (%pa)\n", &ggtt->mappable_end); return -ENXIO; } @@ -3408,8 +3365,6 @@ static int gen6_gmch_probe(struct i915_ggtt *ggtt) DRM_ERROR("Can't set DMA mask/consistent mask (%d)\n", err); pci_read_config_word(pdev, SNB_GMCH_CTRL, &snb_gmch_ctl); - ggtt->stolen_size = gen6_get_stolen_size(snb_gmch_ctl); - size = gen6_get_total_gtt_size(snb_gmch_ctl); ggtt->base.total = (size / sizeof(gen6_pte_t)) << PAGE_SHIFT; @@ -3446,6 +3401,7 @@ static void i915_gmch_remove(struct i915_address_space *vm) static int i915_gmch_probe(struct i915_ggtt *ggtt) { struct drm_i915_private *dev_priv = ggtt->base.i915; + phys_addr_t gmadr_base; int ret; ret = intel_gmch_probe(dev_priv->bridge_dev, dev_priv->drm.pdev, NULL); @@ -3455,10 +3411,13 @@ static int i915_gmch_probe(struct i915_ggtt *ggtt) } intel_gtt_get(&ggtt->base.total, - &ggtt->stolen_size, - &ggtt->mappable_base, + &gmadr_base, &ggtt->mappable_end); + ggtt->gmadr = + (struct resource) DEFINE_RES_MEM(gmadr_base, + ggtt->mappable_end); + ggtt->do_idle_maps = needs_idle_maps(dev_priv); ggtt->base.insert_page = i915_ggtt_insert_page; ggtt->base.insert_entries = i915_ggtt_insert_entries; @@ -3503,9 +3462,9 @@ int i915_ggtt_probe_hw(struct drm_i915_private *dev_priv) * currently don't have any bits spare to pass in this upper * restriction! */ - if (HAS_GUC(dev_priv) && i915_modparams.enable_guc_loading) { + if (USES_GUC(dev_priv)) { ggtt->base.total = min_t(u64, ggtt->base.total, GUC_GGTT_TOP); - ggtt->mappable_end = min(ggtt->mappable_end, ggtt->base.total); + ggtt->mappable_end = min_t(u64, ggtt->mappable_end, ggtt->base.total); } if ((ggtt->base.total - 1) >> 32) { @@ -3513,21 +3472,21 @@ int i915_ggtt_probe_hw(struct drm_i915_private *dev_priv) " of address space! Found %lldM!\n", ggtt->base.total >> 20); ggtt->base.total = 1ULL << 32; - ggtt->mappable_end = min(ggtt->mappable_end, ggtt->base.total); + ggtt->mappable_end = min_t(u64, ggtt->mappable_end, ggtt->base.total); } if (ggtt->mappable_end > ggtt->base.total) { DRM_ERROR("mappable aperture extends past end of GGTT," - " aperture=%llx, total=%llx\n", - ggtt->mappable_end, ggtt->base.total); + " aperture=%pa, total=%llx\n", + &ggtt->mappable_end, ggtt->base.total); ggtt->mappable_end = ggtt->base.total; } /* GMADR is the PCI mmio aperture into the global GTT. */ - DRM_INFO("Memory usable by graphics device = %lluM\n", - ggtt->base.total >> 20); - DRM_DEBUG_DRIVER("GMADR size = %lldM\n", ggtt->mappable_end >> 20); - DRM_DEBUG_DRIVER("GTT stolen size = %uM\n", ggtt->stolen_size >> 20); + DRM_DEBUG_DRIVER("GGTT size = %lluM\n", ggtt->base.total >> 20); + DRM_DEBUG_DRIVER("GMADR size = %lluM\n", (u64)ggtt->mappable_end >> 20); + DRM_DEBUG_DRIVER("DSM size = %lluM\n", + (u64)resource_size(&intel_graphics_stolen_res) >> 20); if (intel_vtd_active()) DRM_INFO("VT-d active for gfx access\n"); @@ -3556,14 +3515,14 @@ int i915_ggtt_init_hw(struct drm_i915_private *dev_priv) ggtt->base.mm.color_adjust = i915_gtt_color_adjust; mutex_unlock(&dev_priv->drm.struct_mutex); - if (!io_mapping_init_wc(&dev_priv->ggtt.mappable, - dev_priv->ggtt.mappable_base, + if (!io_mapping_init_wc(&dev_priv->ggtt.iomap, + dev_priv->ggtt.gmadr.start, dev_priv->ggtt.mappable_end)) { ret = -EIO; goto out_gtt_cleanup; } - ggtt->mtrr = arch_phys_wc_add(ggtt->mappable_base, ggtt->mappable_end); + ggtt->mtrr = arch_phys_wc_add(ggtt->gmadr.start, ggtt->mappable_end); /* * Initialise stolen early so that we may reserve preallocated @@ -3593,6 +3552,8 @@ void i915_ggtt_enable_guc(struct drm_i915_private *i915) GEM_BUG_ON(i915->ggtt.invalidate != gen6_ggtt_invalidate); i915->ggtt.invalidate = guc_ggtt_invalidate; + + i915_ggtt_invalidate(i915); } void i915_ggtt_disable_guc(struct drm_i915_private *i915) @@ -3601,6 +3562,8 @@ void i915_ggtt_disable_guc(struct drm_i915_private *i915) GEM_BUG_ON(i915->ggtt.invalidate != guc_ggtt_invalidate); i915->ggtt.invalidate = gen6_ggtt_invalidate; + + i915_ggtt_invalidate(i915); } void i915_gem_restore_gtt_mappings(struct drm_i915_private *dev_priv) @@ -3620,10 +3583,7 @@ void i915_gem_restore_gtt_mappings(struct drm_i915_private *dev_priv) bool ggtt_bound = false; struct i915_vma *vma; - list_for_each_entry(vma, &obj->vma_list, obj_link) { - if (vma->vm != &ggtt->base) - continue; - + for_each_ggtt_vma(vma, obj) { if (!i915_vma_unbind(vma)) continue; @@ -3737,9 +3697,6 @@ intel_rotate_pages(struct intel_rotation_info *rot_info, rot_info->plane[i].stride, st, sg); } - DRM_DEBUG_KMS("Created rotated page mapping for object size %zu (%ux%u tiles, %u pages)\n", - obj->base.size, rot_info->plane[0].width, rot_info->plane[0].height, size); - kvfree(page_addr_list); return st; @@ -3749,8 +3706,8 @@ err_sg_alloc: err_st_alloc: kvfree(page_addr_list); - DRM_DEBUG_KMS("Failed to create rotated mapping for object size %zu! (%ux%u tiles, %u pages)\n", - obj->base.size, rot_info->plane[0].width, rot_info->plane[0].height, size); + DRM_DEBUG_DRIVER("Failed to create rotated mapping for object size %zu! (%ux%u tiles, %u pages)\n", + obj->base.size, rot_info->plane[0].width, rot_info->plane[0].height, size); return ERR_PTR(ret); } diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h index 93211a96fdad..a42890d9af38 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.h +++ b/drivers/gpu/drm/i915/i915_gem_gtt.h @@ -368,23 +368,10 @@ i915_vm_has_scratch_64K(struct i915_address_space *vm) */ struct i915_ggtt { struct i915_address_space base; - struct io_mapping mappable; /* Mapping to our CPU mappable region */ - phys_addr_t mappable_base; /* PA of our GMADR */ - u64 mappable_end; /* End offset that we can CPU map */ - - /* Stolen memory is segmented in hardware with different portions - * offlimits to certain functions. - * - * The drm_mm is initialised to the total accessible range, as found - * from the PCI config. On Broadwell+, this is further restricted to - * avoid the first page! The upper end of stolen memory is reserved for - * hardware functions and similarly removed from the accessible range. - */ - u32 stolen_size; /* Total size of stolen memory */ - u32 stolen_usable_size; /* Total size minus reserved ranges */ - u32 stolen_reserved_base; - u32 stolen_reserved_size; + struct io_mapping iomap; /* Mapping to our CPU mappable region */ + struct resource gmadr; /* GMADR resource */ + resource_size_t mappable_end; /* End offset that we can CPU map */ /** "Graphics Stolen Memory" holds the global PTEs */ void __iomem *gsm; diff --git a/drivers/gpu/drm/i915/i915_gem_object.h b/drivers/gpu/drm/i915/i915_gem_object.h index 19fb28c177d8..05e89e1c0a08 100644 --- a/drivers/gpu/drm/i915/i915_gem_object.h +++ b/drivers/gpu/drm/i915/i915_gem_object.h @@ -261,6 +261,8 @@ struct drm_i915_gem_object { } userptr; unsigned long scratch; + + void *gvt_info; }; /** for phys allocated objects */ diff --git a/drivers/gpu/drm/i915/i915_gem_render_state.c b/drivers/gpu/drm/i915/i915_gem_render_state.c index c2723a06fbb4..f7fc0df251ac 100644 --- a/drivers/gpu/drm/i915/i915_gem_render_state.c +++ b/drivers/gpu/drm/i915/i915_gem_render_state.c @@ -208,10 +208,6 @@ int i915_gem_render_state_emit(struct drm_i915_gem_request *rq) if (err) goto err_unpin; - err = engine->emit_flush(rq, EMIT_INVALIDATE); - if (err) - goto err_unpin; - err = engine->emit_bb_start(rq, so.batch_offset, so.batch_size, I915_DISPATCH_SECURE); diff --git a/drivers/gpu/drm/i915/i915_gem_request.c b/drivers/gpu/drm/i915/i915_gem_request.c index e0d6221022a8..59f023bb7015 100644 --- a/drivers/gpu/drm/i915/i915_gem_request.c +++ b/drivers/gpu/drm/i915/i915_gem_request.c @@ -252,12 +252,27 @@ static void mark_busy(struct drm_i915_private *i915) GEM_BUG_ON(!i915->gt.active_requests); intel_runtime_pm_get_noresume(i915); + + /* + * It seems that the DMC likes to transition between the DC states a lot + * when there are no connected displays (no active power domains) during + * command submission. + * + * This activity has negative impact on the performance of the chip with + * huge latencies observed in the interrupt handler and elsewhere. + * + * Work around it by grabbing a GT IRQ power domain whilst there is any + * GT activity, preventing any DC state transitions. + */ + intel_display_power_get(i915, POWER_DOMAIN_GT_IRQ); + i915->gt.awake = true; intel_enable_gt_powersave(i915); i915_update_gfx_val(i915); if (INTEL_GEN(i915) >= 6) gen6_rps_busy(i915); + i915_pmu_gt_unparked(i915); intel_engines_unpark(i915); @@ -624,6 +639,10 @@ i915_gem_request_alloc(struct intel_engine_cs *engine, if (ret) goto err_unpin; + ret = intel_ring_wait_for_space(ring, MIN_SPACE_FOR_ADD_REQUEST); + if (ret) + goto err_unreserve; + /* Move the oldest request to the slab-cache (if not in use!) */ req = list_first_entry_or_null(&engine->timeline->requests, typeof(*req), link); @@ -658,10 +677,21 @@ i915_gem_request_alloc(struct intel_engine_cs *engine, * * Do not use kmem_cache_zalloc() here! */ - req = kmem_cache_alloc(dev_priv->requests, GFP_KERNEL); - if (!req) { - ret = -ENOMEM; - goto err_unreserve; + req = kmem_cache_alloc(dev_priv->requests, + GFP_KERNEL | __GFP_RETRY_MAYFAIL | __GFP_NOWARN); + if (unlikely(!req)) { + /* Ratelimit ourselves to prevent oom from malicious clients */ + ret = i915_gem_wait_for_idle(dev_priv, + I915_WAIT_LOCKED | + I915_WAIT_INTERRUPTIBLE); + if (ret) + goto err_unreserve; + + req = kmem_cache_alloc(dev_priv->requests, GFP_KERNEL); + if (!req) { + ret = -ENOMEM; + goto err_unreserve; + } } req->timeline = i915_gem_context_lookup_timeline(ctx, engine); @@ -703,22 +733,30 @@ i915_gem_request_alloc(struct intel_engine_cs *engine, req->reserved_space = MIN_SPACE_FOR_ADD_REQUEST; GEM_BUG_ON(req->reserved_space < engine->emit_breadcrumb_sz); - ret = engine->request_alloc(req); - if (ret) - goto err_ctx; - - /* Record the position of the start of the request so that + /* + * Record the position of the start of the request so that * should we detect the updated seqno part-way through the * GPU processing the request, we never over-estimate the * position of the head. */ req->head = req->ring->emit; + /* Unconditionally invalidate GPU caches and TLBs. */ + ret = engine->emit_flush(req, EMIT_INVALIDATE); + if (ret) + goto err_unwind; + + ret = engine->request_alloc(req); + if (ret) + goto err_unwind; + /* Check that we didn't interrupt ourselves with a new request */ GEM_BUG_ON(req->timeline->seqno != req->fence.seqno); return req; -err_ctx: +err_unwind: + req->ring->emit = req->head; + /* Make sure we didn't add ourselves to external state before freeing */ GEM_BUG_ON(!list_empty(&req->active_list)); GEM_BUG_ON(!list_empty(&req->priotree.signalers_list)); @@ -755,7 +793,7 @@ i915_gem_request_await_request(struct drm_i915_gem_request *to, if (to->engine == from->engine) { ret = i915_sw_fence_await_sw_fence_gfp(&to->submit, &from->submit, - GFP_KERNEL); + I915_FENCE_GFP); return ret < 0 ? ret : 0; } @@ -783,7 +821,7 @@ i915_gem_request_await_request(struct drm_i915_gem_request *to, await_dma_fence: ret = i915_sw_fence_await_dma_fence(&to->submit, &from->fence, 0, - GFP_KERNEL); + I915_FENCE_GFP); return ret < 0 ? ret : 0; } @@ -834,7 +872,7 @@ i915_gem_request_await_dma_fence(struct drm_i915_gem_request *req, else ret = i915_sw_fence_await_dma_fence(&req->submit, fence, I915_FENCE_TIMEOUT, - GFP_KERNEL); + I915_FENCE_GFP); if (ret < 0) return ret; diff --git a/drivers/gpu/drm/i915/i915_gem_request.h b/drivers/gpu/drm/i915/i915_gem_request.h index 26249f39de67..0d6d39f19506 100644 --- a/drivers/gpu/drm/i915/i915_gem_request.h +++ b/drivers/gpu/drm/i915/i915_gem_request.h @@ -204,6 +204,8 @@ struct drm_i915_gem_request { struct list_head client_link; }; +#define I915_FENCE_GFP (GFP_KERNEL | __GFP_RETRY_MAYFAIL | __GFP_NOWARN) + extern const struct dma_fence_ops i915_fence_ops; static inline bool dma_fence_is_i915(const struct dma_fence *fence) diff --git a/drivers/gpu/drm/i915/i915_gem_shrinker.c b/drivers/gpu/drm/i915/i915_gem_shrinker.c index 3770e3323fc8..9029ed04879c 100644 --- a/drivers/gpu/drm/i915/i915_gem_shrinker.c +++ b/drivers/gpu/drm/i915/i915_gem_shrinker.c @@ -35,9 +35,9 @@ #include "i915_drv.h" #include "i915_trace.h" -static bool shrinker_lock(struct drm_i915_private *dev_priv, bool *unlock) +static bool shrinker_lock(struct drm_i915_private *i915, bool *unlock) { - switch (mutex_trylock_recursive(&dev_priv->drm.struct_mutex)) { + switch (mutex_trylock_recursive(&i915->drm.struct_mutex)) { case MUTEX_TRYLOCK_RECURSIVE: *unlock = false; return true; @@ -47,7 +47,7 @@ static bool shrinker_lock(struct drm_i915_private *dev_priv, bool *unlock) preempt_disable(); do { cpu_relax(); - if (mutex_trylock(&dev_priv->drm.struct_mutex)) { + if (mutex_trylock(&i915->drm.struct_mutex)) { *unlock = true; break; } @@ -63,12 +63,12 @@ static bool shrinker_lock(struct drm_i915_private *dev_priv, bool *unlock) BUG(); } -static void shrinker_unlock(struct drm_i915_private *dev_priv, bool unlock) +static void shrinker_unlock(struct drm_i915_private *i915, bool unlock) { if (!unlock) return; - mutex_unlock(&dev_priv->drm.struct_mutex); + mutex_unlock(&i915->drm.struct_mutex); } static bool swap_available(void) @@ -118,7 +118,7 @@ static bool unsafe_drop_pages(struct drm_i915_gem_object *obj) /** * i915_gem_shrink - Shrink buffer object caches - * @dev_priv: i915 device + * @i915: i915 device * @target: amount of memory to make available, in pages * @nr_scanned: optional output for number of pages scanned (incremental) * @flags: control flags for selecting cache types @@ -142,7 +142,7 @@ static bool unsafe_drop_pages(struct drm_i915_gem_object *obj) * The number of pages of backing storage actually released. */ unsigned long -i915_gem_shrink(struct drm_i915_private *dev_priv, +i915_gem_shrink(struct drm_i915_private *i915, unsigned long target, unsigned long *nr_scanned, unsigned flags) @@ -151,15 +151,15 @@ i915_gem_shrink(struct drm_i915_private *dev_priv, struct list_head *list; unsigned int bit; } phases[] = { - { &dev_priv->mm.unbound_list, I915_SHRINK_UNBOUND }, - { &dev_priv->mm.bound_list, I915_SHRINK_BOUND }, + { &i915->mm.unbound_list, I915_SHRINK_UNBOUND }, + { &i915->mm.bound_list, I915_SHRINK_BOUND }, { NULL, 0 }, }, *phase; unsigned long count = 0; unsigned long scanned = 0; bool unlock; - if (!shrinker_lock(dev_priv, &unlock)) + if (!shrinker_lock(i915, &unlock)) return 0; /* @@ -172,10 +172,10 @@ i915_gem_shrink(struct drm_i915_private *dev_priv, * we will free as much as we can and hope to get a second chance. */ if (flags & I915_SHRINK_ACTIVE) - i915_gem_wait_for_idle(dev_priv, I915_WAIT_LOCKED); + i915_gem_wait_for_idle(i915, I915_WAIT_LOCKED); - trace_i915_gem_shrink(dev_priv, target, flags); - i915_gem_retire_requests(dev_priv); + trace_i915_gem_shrink(i915, target, flags); + i915_gem_retire_requests(i915); /* * Unbinding of objects will require HW access; Let us not wake the @@ -183,7 +183,7 @@ i915_gem_shrink(struct drm_i915_private *dev_priv, * we will force the wake during oom-notifier. */ if ((flags & I915_SHRINK_BOUND) && - !intel_runtime_pm_get_if_in_use(dev_priv)) + !intel_runtime_pm_get_if_in_use(i915)) flags &= ~I915_SHRINK_BOUND; /* @@ -221,7 +221,7 @@ i915_gem_shrink(struct drm_i915_private *dev_priv, * to be able to shrink their pages, so they remain on * the unbound/bound list until actually freed. */ - spin_lock(&dev_priv->mm.obj_lock); + spin_lock(&i915->mm.obj_lock); while (count < target && (obj = list_first_entry_or_null(phase->list, typeof(*obj), @@ -244,7 +244,7 @@ i915_gem_shrink(struct drm_i915_private *dev_priv, if (!can_release_pages(obj)) continue; - spin_unlock(&dev_priv->mm.obj_lock); + spin_unlock(&i915->mm.obj_lock); if (unsafe_drop_pages(obj)) { /* May arrive from get_pages on another bo */ @@ -258,18 +258,18 @@ i915_gem_shrink(struct drm_i915_private *dev_priv, } scanned += obj->base.size >> PAGE_SHIFT; - spin_lock(&dev_priv->mm.obj_lock); + spin_lock(&i915->mm.obj_lock); } list_splice_tail(&still_in_list, phase->list); - spin_unlock(&dev_priv->mm.obj_lock); + spin_unlock(&i915->mm.obj_lock); } if (flags & I915_SHRINK_BOUND) - intel_runtime_pm_put(dev_priv); + intel_runtime_pm_put(i915); - i915_gem_retire_requests(dev_priv); + i915_gem_retire_requests(i915); - shrinker_unlock(dev_priv, unlock); + shrinker_unlock(i915, unlock); if (nr_scanned) *nr_scanned += scanned; @@ -278,7 +278,7 @@ i915_gem_shrink(struct drm_i915_private *dev_priv, /** * i915_gem_shrink_all - Shrink buffer object caches completely - * @dev_priv: i915 device + * @i915: i915 device * * This is a simple wraper around i915_gem_shrink() to aggressively shrink all * caches completely. It also first waits for and retires all outstanding @@ -290,16 +290,16 @@ i915_gem_shrink(struct drm_i915_private *dev_priv, * Returns: * The number of pages of backing storage actually released. */ -unsigned long i915_gem_shrink_all(struct drm_i915_private *dev_priv) +unsigned long i915_gem_shrink_all(struct drm_i915_private *i915) { unsigned long freed; - intel_runtime_pm_get(dev_priv); - freed = i915_gem_shrink(dev_priv, -1UL, NULL, + intel_runtime_pm_get(i915); + freed = i915_gem_shrink(i915, -1UL, NULL, I915_SHRINK_BOUND | I915_SHRINK_UNBOUND | I915_SHRINK_ACTIVE); - intel_runtime_pm_put(dev_priv); + intel_runtime_pm_put(i915); return freed; } @@ -347,53 +347,53 @@ i915_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc) static unsigned long i915_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc) { - struct drm_i915_private *dev_priv = + struct drm_i915_private *i915 = container_of(shrinker, struct drm_i915_private, mm.shrinker); unsigned long freed; bool unlock; sc->nr_scanned = 0; - if (!shrinker_lock(dev_priv, &unlock)) + if (!shrinker_lock(i915, &unlock)) return SHRINK_STOP; - freed = i915_gem_shrink(dev_priv, + freed = i915_gem_shrink(i915, sc->nr_to_scan, &sc->nr_scanned, I915_SHRINK_BOUND | I915_SHRINK_UNBOUND | I915_SHRINK_PURGEABLE); if (freed < sc->nr_to_scan) - freed += i915_gem_shrink(dev_priv, + freed += i915_gem_shrink(i915, sc->nr_to_scan - sc->nr_scanned, &sc->nr_scanned, I915_SHRINK_BOUND | I915_SHRINK_UNBOUND); if (freed < sc->nr_to_scan && current_is_kswapd()) { - intel_runtime_pm_get(dev_priv); - freed += i915_gem_shrink(dev_priv, + intel_runtime_pm_get(i915); + freed += i915_gem_shrink(i915, sc->nr_to_scan - sc->nr_scanned, &sc->nr_scanned, I915_SHRINK_ACTIVE | I915_SHRINK_BOUND | I915_SHRINK_UNBOUND); - intel_runtime_pm_put(dev_priv); + intel_runtime_pm_put(i915); } - shrinker_unlock(dev_priv, unlock); + shrinker_unlock(i915, unlock); return sc->nr_scanned ? freed : SHRINK_STOP; } static bool -shrinker_lock_uninterruptible(struct drm_i915_private *dev_priv, bool *unlock, +shrinker_lock_uninterruptible(struct drm_i915_private *i915, bool *unlock, int timeout_ms) { unsigned long timeout = jiffies + msecs_to_jiffies_timeout(timeout_ms); do { - if (i915_gem_wait_for_idle(dev_priv, 0) == 0 && - shrinker_lock(dev_priv, unlock)) + if (i915_gem_wait_for_idle(i915, 0) == 0 && + shrinker_lock(i915, unlock)) break; schedule_timeout_killable(1); @@ -412,32 +412,32 @@ shrinker_lock_uninterruptible(struct drm_i915_private *dev_priv, bool *unlock, static int i915_gem_shrinker_oom(struct notifier_block *nb, unsigned long event, void *ptr) { - struct drm_i915_private *dev_priv = + struct drm_i915_private *i915 = container_of(nb, struct drm_i915_private, mm.oom_notifier); struct drm_i915_gem_object *obj; unsigned long unevictable, bound, unbound, freed_pages; - freed_pages = i915_gem_shrink_all(dev_priv); + freed_pages = i915_gem_shrink_all(i915); /* Because we may be allocating inside our own driver, we cannot * assert that there are no objects with pinned pages that are not * being pointed to by hardware. */ unbound = bound = unevictable = 0; - spin_lock(&dev_priv->mm.obj_lock); - list_for_each_entry(obj, &dev_priv->mm.unbound_list, mm.link) { + spin_lock(&i915->mm.obj_lock); + list_for_each_entry(obj, &i915->mm.unbound_list, mm.link) { if (!can_release_pages(obj)) unevictable += obj->base.size >> PAGE_SHIFT; else unbound += obj->base.size >> PAGE_SHIFT; } - list_for_each_entry(obj, &dev_priv->mm.bound_list, mm.link) { + list_for_each_entry(obj, &i915->mm.bound_list, mm.link) { if (!can_release_pages(obj)) unevictable += obj->base.size >> PAGE_SHIFT; else bound += obj->base.size >> PAGE_SHIFT; } - spin_unlock(&dev_priv->mm.obj_lock); + spin_unlock(&i915->mm.obj_lock); if (freed_pages || unbound || bound) pr_info("Purging GPU memory, %lu pages freed, " @@ -455,74 +455,74 @@ i915_gem_shrinker_oom(struct notifier_block *nb, unsigned long event, void *ptr) static int i915_gem_shrinker_vmap(struct notifier_block *nb, unsigned long event, void *ptr) { - struct drm_i915_private *dev_priv = + struct drm_i915_private *i915 = container_of(nb, struct drm_i915_private, mm.vmap_notifier); struct i915_vma *vma, *next; unsigned long freed_pages = 0; bool unlock; int ret; - if (!shrinker_lock_uninterruptible(dev_priv, &unlock, 5000)) + if (!shrinker_lock_uninterruptible(i915, &unlock, 5000)) return NOTIFY_DONE; /* Force everything onto the inactive lists */ - ret = i915_gem_wait_for_idle(dev_priv, I915_WAIT_LOCKED); + ret = i915_gem_wait_for_idle(i915, I915_WAIT_LOCKED); if (ret) goto out; - intel_runtime_pm_get(dev_priv); - freed_pages += i915_gem_shrink(dev_priv, -1UL, NULL, + intel_runtime_pm_get(i915); + freed_pages += i915_gem_shrink(i915, -1UL, NULL, I915_SHRINK_BOUND | I915_SHRINK_UNBOUND | I915_SHRINK_ACTIVE | I915_SHRINK_VMAPS); - intel_runtime_pm_put(dev_priv); + intel_runtime_pm_put(i915); /* We also want to clear any cached iomaps as they wrap vmap */ list_for_each_entry_safe(vma, next, - &dev_priv->ggtt.base.inactive_list, vm_link) { + &i915->ggtt.base.inactive_list, vm_link) { unsigned long count = vma->node.size >> PAGE_SHIFT; if (vma->iomap && i915_vma_unbind(vma) == 0) freed_pages += count; } out: - shrinker_unlock(dev_priv, unlock); + shrinker_unlock(i915, unlock); *(unsigned long *)ptr += freed_pages; return NOTIFY_DONE; } /** - * i915_gem_shrinker_init - Initialize i915 shrinker - * @dev_priv: i915 device + * i915_gem_shrinker_register - Register the i915 shrinker + * @i915: i915 device * * This function registers and sets up the i915 shrinker and OOM handler. */ -void i915_gem_shrinker_init(struct drm_i915_private *dev_priv) +void i915_gem_shrinker_register(struct drm_i915_private *i915) { - dev_priv->mm.shrinker.scan_objects = i915_gem_shrinker_scan; - dev_priv->mm.shrinker.count_objects = i915_gem_shrinker_count; - dev_priv->mm.shrinker.seeks = DEFAULT_SEEKS; - dev_priv->mm.shrinker.batch = 4096; - WARN_ON(register_shrinker(&dev_priv->mm.shrinker)); + i915->mm.shrinker.scan_objects = i915_gem_shrinker_scan; + i915->mm.shrinker.count_objects = i915_gem_shrinker_count; + i915->mm.shrinker.seeks = DEFAULT_SEEKS; + i915->mm.shrinker.batch = 4096; + WARN_ON(register_shrinker(&i915->mm.shrinker)); - dev_priv->mm.oom_notifier.notifier_call = i915_gem_shrinker_oom; - WARN_ON(register_oom_notifier(&dev_priv->mm.oom_notifier)); + i915->mm.oom_notifier.notifier_call = i915_gem_shrinker_oom; + WARN_ON(register_oom_notifier(&i915->mm.oom_notifier)); - dev_priv->mm.vmap_notifier.notifier_call = i915_gem_shrinker_vmap; - WARN_ON(register_vmap_purge_notifier(&dev_priv->mm.vmap_notifier)); + i915->mm.vmap_notifier.notifier_call = i915_gem_shrinker_vmap; + WARN_ON(register_vmap_purge_notifier(&i915->mm.vmap_notifier)); } /** - * i915_gem_shrinker_cleanup - Clean up i915 shrinker - * @dev_priv: i915 device + * i915_gem_shrinker_unregister - Unregisters the i915 shrinker + * @i915: i915 device * * This function unregisters the i915 shrinker and OOM handler. */ -void i915_gem_shrinker_cleanup(struct drm_i915_private *dev_priv) +void i915_gem_shrinker_unregister(struct drm_i915_private *i915) { - WARN_ON(unregister_vmap_purge_notifier(&dev_priv->mm.vmap_notifier)); - WARN_ON(unregister_oom_notifier(&dev_priv->mm.oom_notifier)); - unregister_shrinker(&dev_priv->mm.shrinker); + WARN_ON(unregister_vmap_purge_notifier(&i915->mm.vmap_notifier)); + WARN_ON(unregister_oom_notifier(&i915->mm.oom_notifier)); + unregister_shrinker(&i915->mm.shrinker); } diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index 1877ae9a1d9b..d3f222fa6356 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -30,9 +30,6 @@ #include <drm/i915_drm.h> #include "i915_drv.h" -#define KB(x) ((x) * 1024) -#define MB(x) (KB(x) * 1024) - /* * The BIOS typically reserves some of the system's memory for the exclusive * use of the integrated graphics. This memory is no longer available for @@ -79,129 +76,26 @@ void i915_gem_stolen_remove_node(struct drm_i915_private *dev_priv, mutex_unlock(&dev_priv->mm.stolen_lock); } -static dma_addr_t i915_stolen_to_dma(struct drm_i915_private *dev_priv) +static int i915_adjust_stolen(struct drm_i915_private *dev_priv, + struct resource *dsm) { - struct pci_dev *pdev = dev_priv->drm.pdev; struct i915_ggtt *ggtt = &dev_priv->ggtt; struct resource *r; - dma_addr_t base; - - /* Almost universally we can find the Graphics Base of Stolen Memory - * at register BSM (0x5c) in the igfx configuration space. On a few - * (desktop) machines this is also mirrored in the bridge device at - * different locations, or in the MCHBAR. - * - * On 865 we just check the TOUD register. - * - * On 830/845/85x the stolen memory base isn't available in any - * register. We need to calculate it as TOM-TSEG_SIZE-stolen_size. - * - */ - base = 0; - if (INTEL_GEN(dev_priv) >= 3) { - u32 bsm; - - pci_read_config_dword(pdev, INTEL_BSM, &bsm); - - base = bsm & INTEL_BSM_MASK; - } else if (IS_I865G(dev_priv)) { - u32 tseg_size = 0; - u16 toud = 0; - u8 tmp; - - pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 0), - I845_ESMRAMC, &tmp); - - if (tmp & TSEG_ENABLE) { - switch (tmp & I845_TSEG_SIZE_MASK) { - case I845_TSEG_SIZE_512K: - tseg_size = KB(512); - break; - case I845_TSEG_SIZE_1M: - tseg_size = MB(1); - break; - } - } - - pci_bus_read_config_word(pdev->bus, PCI_DEVFN(0, 0), - I865_TOUD, &toud); - - base = (toud << 16) + tseg_size; - } else if (IS_I85X(dev_priv)) { - u32 tseg_size = 0; - u32 tom; - u8 tmp; - - pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 0), - I85X_ESMRAMC, &tmp); - - if (tmp & TSEG_ENABLE) - tseg_size = MB(1); - - pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 1), - I85X_DRB3, &tmp); - tom = tmp * MB(32); - - base = tom - tseg_size - ggtt->stolen_size; - } else if (IS_I845G(dev_priv)) { - u32 tseg_size = 0; - u32 tom; - u8 tmp; - - pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 0), - I845_ESMRAMC, &tmp); - - if (tmp & TSEG_ENABLE) { - switch (tmp & I845_TSEG_SIZE_MASK) { - case I845_TSEG_SIZE_512K: - tseg_size = KB(512); - break; - case I845_TSEG_SIZE_1M: - tseg_size = MB(1); - break; - } - } - - pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 0), - I830_DRB3, &tmp); - tom = tmp * MB(32); - - base = tom - tseg_size - ggtt->stolen_size; - } else if (IS_I830(dev_priv)) { - u32 tseg_size = 0; - u32 tom; - u8 tmp; - - pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 0), - I830_ESMRAMC, &tmp); - if (tmp & TSEG_ENABLE) { - if (tmp & I830_TSEG_SIZE_1M) - tseg_size = MB(1); - else - tseg_size = KB(512); - } - - pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 0), - I830_DRB3, &tmp); - tom = tmp * MB(32); - - base = tom - tseg_size - ggtt->stolen_size; - } + if (dsm->start == 0 || dsm->end <= dsm->start) + return -EINVAL; - if (base == 0 || add_overflows(base, ggtt->stolen_size)) - return 0; + /* + * TODO: We have yet too encounter the case where the GTT wasn't at the + * end of stolen. With that assumption we could simplify this. + */ - /* make sure we don't clobber the GTT if it's within stolen memory */ + /* Make sure we don't clobber the GTT if it's within stolen memory */ if (INTEL_GEN(dev_priv) <= 4 && !IS_G33(dev_priv) && !IS_PINEVIEW(dev_priv) && !IS_G4X(dev_priv)) { - struct { - dma_addr_t start, end; - } stolen[2] = { - { .start = base, .end = base + ggtt->stolen_size, }, - { .start = base, .end = base + ggtt->stolen_size, }, - }; - u64 ggtt_start, ggtt_end; + struct resource stolen[2] = {*dsm, *dsm}; + struct resource ggtt_res; + resource_size_t ggtt_start; ggtt_start = I915_READ(PGTBL_CTL); if (IS_GEN4(dev_priv)) @@ -209,70 +103,64 @@ static dma_addr_t i915_stolen_to_dma(struct drm_i915_private *dev_priv) (ggtt_start & PGTBL_ADDRESS_HI_MASK) << 28; else ggtt_start &= PGTBL_ADDRESS_LO_MASK; - ggtt_end = ggtt_start + ggtt_total_entries(ggtt) * 4; - - if (ggtt_start >= stolen[0].start && ggtt_start < stolen[0].end) - stolen[0].end = ggtt_start; - if (ggtt_end > stolen[1].start && ggtt_end <= stolen[1].end) - stolen[1].start = ggtt_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; - ggtt->stolen_size = stolen[0].end - stolen[0].start; - } else { - base = stolen[1].start; - ggtt->stolen_size = stolen[1].end - stolen[1].start; - } + + ggtt_res = + (struct resource) DEFINE_RES_MEM(ggtt_start, + ggtt_total_entries(ggtt) * 4); + + if (ggtt_res.start >= stolen[0].start && ggtt_res.start < stolen[0].end) + stolen[0].end = ggtt_res.start; + if (ggtt_res.end > stolen[1].start && ggtt_res.end <= stolen[1].end) + stolen[1].start = ggtt_res.end; + + /* Pick the larger of the two chunks */ + if (resource_size(&stolen[0]) > resource_size(&stolen[1])) + *dsm = stolen[0]; + else + *dsm = stolen[1]; if (stolen[0].start != stolen[1].start || stolen[0].end != stolen[1].end) { - dma_addr_t end = base + ggtt->stolen_size - 1; - - DRM_DEBUG_KMS("GTT within stolen memory at 0x%llx-0x%llx\n", - (unsigned long long)ggtt_start, - (unsigned long long)ggtt_end - 1); - DRM_DEBUG_KMS("Stolen memory adjusted to %pad - %pad\n", - &base, &end); + DRM_DEBUG_KMS("GTT within stolen memory at %pR\n", &ggtt_res); + DRM_DEBUG_KMS("Stolen memory adjusted to %pR\n", dsm); } } - - /* Verify that nothing else uses this physical address. Stolen + /* + * 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 * is seriously wrong. */ - r = devm_request_mem_region(dev_priv->drm.dev, base, ggtt->stolen_size, + r = devm_request_mem_region(dev_priv->drm.dev, dsm->start, + resource_size(dsm), "Graphics Stolen Memory"); if (r == NULL) { /* * One more attempt but this time requesting region from - * base + 1, as we have seen that this resolves the region + * start + 1, as we have seen that this resolves the region * conflict with the PCI Bus. * This is a BIOS w/a: Some BIOS wrap stolen in the root * PCI bus, but have an off-by-one error. Hence retry the * reservation starting from 1 instead of 0. * There's also BIOS with off-by-one on the other end. */ - r = devm_request_mem_region(dev_priv->drm.dev, base + 1, - ggtt->stolen_size - 2, + r = devm_request_mem_region(dev_priv->drm.dev, dsm->start + 1, + resource_size(dsm) - 2, "Graphics Stolen Memory"); /* * GEN3 firmware likes to smash pci bridges into the stolen * range. Apparently this works. */ if (r == NULL && !IS_GEN3(dev_priv)) { - dma_addr_t end = base + ggtt->stolen_size; + DRM_ERROR("conflict detected with stolen region: %pR\n", + dsm); - DRM_ERROR("conflict detected with stolen region: [%pad - %pad]\n", - &base, &end); - base = 0; + return -EBUSY; } } - return base; + return 0; } void i915_gem_cleanup_stolen(struct drm_device *dev) @@ -286,13 +174,12 @@ void i915_gem_cleanup_stolen(struct drm_device *dev) } static void g4x_get_stolen_reserved(struct drm_i915_private *dev_priv, - dma_addr_t *base, u32 *size) + resource_size_t *base, resource_size_t *size) { - struct i915_ggtt *ggtt = &dev_priv->ggtt; uint32_t reg_val = I915_READ(IS_GM45(dev_priv) ? CTG_STOLEN_RESERVED : ELK_STOLEN_RESERVED); - dma_addr_t stolen_top = dev_priv->mm.stolen_base + ggtt->stolen_size; + resource_size_t stolen_top = dev_priv->dsm.end + 1; if ((reg_val & G4X_STOLEN_RESERVED_ENABLE) == 0) { *base = 0; @@ -321,7 +208,7 @@ static void g4x_get_stolen_reserved(struct drm_i915_private *dev_priv, } static void gen6_get_stolen_reserved(struct drm_i915_private *dev_priv, - dma_addr_t *base, u32 *size) + resource_size_t *base, resource_size_t *size) { uint32_t reg_val = I915_READ(GEN6_STOLEN_RESERVED); @@ -353,7 +240,7 @@ static void gen6_get_stolen_reserved(struct drm_i915_private *dev_priv, } static void gen7_get_stolen_reserved(struct drm_i915_private *dev_priv, - dma_addr_t *base, u32 *size) + resource_size_t *base, resource_size_t *size) { uint32_t reg_val = I915_READ(GEN6_STOLEN_RESERVED); @@ -379,7 +266,7 @@ static void gen7_get_stolen_reserved(struct drm_i915_private *dev_priv, } static void chv_get_stolen_reserved(struct drm_i915_private *dev_priv, - dma_addr_t *base, u32 *size) + resource_size_t *base, resource_size_t *size) { uint32_t reg_val = I915_READ(GEN6_STOLEN_RESERVED); @@ -411,11 +298,10 @@ static void chv_get_stolen_reserved(struct drm_i915_private *dev_priv, } static void bdw_get_stolen_reserved(struct drm_i915_private *dev_priv, - dma_addr_t *base, u32 *size) + resource_size_t *base, resource_size_t *size) { - struct i915_ggtt *ggtt = &dev_priv->ggtt; uint32_t reg_val = I915_READ(GEN6_STOLEN_RESERVED); - dma_addr_t stolen_top; + resource_size_t stolen_top; if ((reg_val & GEN6_STOLEN_RESERVED_ENABLE) == 0) { *base = 0; @@ -423,7 +309,7 @@ static void bdw_get_stolen_reserved(struct drm_i915_private *dev_priv, return; } - stolen_top = dev_priv->mm.stolen_base + ggtt->stolen_size; + stolen_top = dev_priv->dsm.end + 1; *base = reg_val & GEN6_STOLEN_RESERVED_ADDR_MASK; @@ -439,10 +325,9 @@ static void bdw_get_stolen_reserved(struct drm_i915_private *dev_priv, int i915_gem_init_stolen(struct drm_i915_private *dev_priv) { - struct i915_ggtt *ggtt = &dev_priv->ggtt; - dma_addr_t reserved_base, stolen_top; - u32 reserved_total, reserved_size; - u32 stolen_usable_start; + resource_size_t reserved_base, stolen_top; + resource_size_t reserved_total, reserved_size; + resource_size_t stolen_usable_start; mutex_init(&dev_priv->mm.stolen_lock); @@ -456,14 +341,18 @@ int i915_gem_init_stolen(struct drm_i915_private *dev_priv) return 0; } - if (ggtt->stolen_size == 0) + if (resource_size(&intel_graphics_stolen_res) == 0) return 0; - dev_priv->mm.stolen_base = i915_stolen_to_dma(dev_priv); - if (dev_priv->mm.stolen_base == 0) + dev_priv->dsm = intel_graphics_stolen_res; + + if (i915_adjust_stolen(dev_priv, &dev_priv->dsm)) return 0; - stolen_top = dev_priv->mm.stolen_base + ggtt->stolen_size; + GEM_BUG_ON(dev_priv->dsm.start == 0); + GEM_BUG_ON(dev_priv->dsm.end <= dev_priv->dsm.start); + + stolen_top = dev_priv->dsm.end + 1; reserved_base = 0; reserved_size = 0; @@ -504,50 +393,47 @@ int i915_gem_init_stolen(struct drm_i915_private *dev_priv) reserved_base = stolen_top; } - if (reserved_base < dev_priv->mm.stolen_base || - reserved_base + reserved_size > stolen_top) { - dma_addr_t reserved_top = reserved_base + reserved_size; - DRM_ERROR("Stolen reserved area [%pad - %pad] outside stolen memory [%pad - %pad]\n", - &reserved_base, &reserved_top, - &dev_priv->mm.stolen_base, &stolen_top); + dev_priv->dsm_reserved = + (struct resource) DEFINE_RES_MEM(reserved_base, reserved_size); + + if (!resource_contains(&dev_priv->dsm, &dev_priv->dsm_reserved)) { + DRM_ERROR("Stolen reserved area %pR outside stolen memory %pR\n", + &dev_priv->dsm_reserved, &dev_priv->dsm); return 0; } - ggtt->stolen_reserved_base = reserved_base; - ggtt->stolen_reserved_size = reserved_size; - /* It is possible for the reserved area to end before the end of stolen * memory, so just consider the start. */ reserved_total = stolen_top - reserved_base; - DRM_DEBUG_KMS("Memory reserved for graphics device: %uK, usable: %uK\n", - ggtt->stolen_size >> 10, - (ggtt->stolen_size - reserved_total) >> 10); + DRM_DEBUG_KMS("Memory reserved for graphics device: %lluK, usable: %lluK\n", + (u64)resource_size(&dev_priv->dsm) >> 10, + ((u64)resource_size(&dev_priv->dsm) - reserved_total) >> 10); stolen_usable_start = 0; /* WaSkipStolenMemoryFirstPage:bdw+ */ if (INTEL_GEN(dev_priv) >= 8) stolen_usable_start = 4096; - ggtt->stolen_usable_size = - ggtt->stolen_size - reserved_total - stolen_usable_start; + dev_priv->stolen_usable_size = + resource_size(&dev_priv->dsm) - reserved_total - stolen_usable_start; /* Basic memrange allocator for stolen space. */ drm_mm_init(&dev_priv->mm.stolen, stolen_usable_start, - ggtt->stolen_usable_size); + dev_priv->stolen_usable_size); return 0; } static struct sg_table * i915_pages_create_for_stolen(struct drm_device *dev, - u32 offset, u32 size) + resource_size_t offset, resource_size_t size) { struct drm_i915_private *dev_priv = to_i915(dev); struct sg_table *st; struct scatterlist *sg; - GEM_BUG_ON(range_overflows(offset, size, dev_priv->ggtt.stolen_size)); + GEM_BUG_ON(range_overflows(offset, size, resource_size(&dev_priv->dsm))); /* We hide that we have no struct page backing our stolen object * by wrapping the contiguous physical allocation with a fake @@ -567,7 +453,7 @@ i915_pages_create_for_stolen(struct drm_device *dev, sg->offset = 0; sg->length = size; - sg_dma_address(sg) = (dma_addr_t)dev_priv->mm.stolen_base + offset; + sg_dma_address(sg) = (dma_addr_t)dev_priv->dsm.start + offset; sg_dma_len(sg) = size; return st; @@ -645,7 +531,8 @@ cleanup: } struct drm_i915_gem_object * -i915_gem_object_create_stolen(struct drm_i915_private *dev_priv, u32 size) +i915_gem_object_create_stolen(struct drm_i915_private *dev_priv, + resource_size_t size) { struct drm_i915_gem_object *obj; struct drm_mm_node *stolen; @@ -678,9 +565,9 @@ i915_gem_object_create_stolen(struct drm_i915_private *dev_priv, u32 size) struct drm_i915_gem_object * i915_gem_object_create_stolen_for_preallocated(struct drm_i915_private *dev_priv, - u32 stolen_offset, - u32 gtt_offset, - u32 size) + resource_size_t stolen_offset, + resource_size_t gtt_offset, + resource_size_t size) { struct i915_ggtt *ggtt = &dev_priv->ggtt; struct drm_i915_gem_object *obj; @@ -693,8 +580,8 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_i915_private *dev_priv lockdep_assert_held(&dev_priv->drm.struct_mutex); - DRM_DEBUG_KMS("creating preallocated stolen object: stolen_offset=%x, gtt_offset=%x, size=%x\n", - stolen_offset, gtt_offset, size); + DRM_DEBUG_KMS("creating preallocated stolen object: stolen_offset=%pa, gtt_offset=%pa, size=%pa\n", + &stolen_offset, >t_offset, &size); /* KISS and expect everything to be page-aligned */ if (WARN_ON(size == 0) || diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index b85d7ebd9bee..d9dc9df523b5 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -205,10 +205,7 @@ i915_gem_object_fence_prepare(struct drm_i915_gem_object *obj, if (tiling_mode == I915_TILING_NONE) return 0; - list_for_each_entry(vma, &obj->vma_list, obj_link) { - if (!i915_vma_is_ggtt(vma)) - break; - + for_each_ggtt_vma(vma, obj) { if (i915_vma_fence_prepare(vma, tiling_mode, stride)) continue; @@ -285,10 +282,7 @@ i915_gem_object_set_tiling(struct drm_i915_gem_object *obj, } mutex_unlock(&obj->mm.lock); - list_for_each_entry(vma, &obj->vma_list, obj_link) { - if (!i915_vma_is_ggtt(vma)) - break; - + for_each_ggtt_vma(vma, obj) { vma->fence_size = i915_gem_fence_size(i915, vma->size, tiling, stride); vma->fence_alignment = diff --git a/drivers/gpu/drm/i915/i915_gem_timeline.c b/drivers/gpu/drm/i915/i915_gem_timeline.c index c597ce277a04..e9fd87604067 100644 --- a/drivers/gpu/drm/i915/i915_gem_timeline.c +++ b/drivers/gpu/drm/i915/i915_gem_timeline.c @@ -33,11 +33,8 @@ static void __intel_timeline_init(struct intel_timeline *tl, { tl->fence_context = context; tl->common = parent; -#ifdef CONFIG_DEBUG_SPINLOCK - __raw_spin_lock_init(&tl->lock.rlock, lockname, lockclass); -#else spin_lock_init(&tl->lock); -#endif + lockdep_set_class_and_name(&tl->lock, lockclass, lockname); init_request_active(&tl->last_request, NULL); INIT_LIST_HEAD(&tl->requests); i915_syncmap_init(&tl->sync); @@ -107,8 +104,8 @@ int i915_gem_timeline_init__global(struct drm_i915_private *i915) } /** - * i915_gem_timelines_mark_idle -- called when the driver idles - * @i915 - the drm_i915_private device + * i915_gem_timelines_park - called when the driver idles + * @i915: the drm_i915_private device * * When the driver is completely idle, we know that all of our sync points * have been signaled and our tracking is then entirely redundant. Any request @@ -116,7 +113,7 @@ int i915_gem_timeline_init__global(struct drm_i915_private *i915) * the fence is signaled and therefore we will not even look them up in the * sync point map. */ -void i915_gem_timelines_mark_idle(struct drm_i915_private *i915) +void i915_gem_timelines_park(struct drm_i915_private *i915) { struct i915_gem_timeline *timeline; int i; diff --git a/drivers/gpu/drm/i915/i915_gem_timeline.h b/drivers/gpu/drm/i915/i915_gem_timeline.h index bfb5eb94c64d..b5a22400a01f 100644 --- a/drivers/gpu/drm/i915/i915_gem_timeline.h +++ b/drivers/gpu/drm/i915/i915_gem_timeline.h @@ -93,7 +93,7 @@ int i915_gem_timeline_init(struct drm_i915_private *i915, struct i915_gem_timeline *tl, const char *name); int i915_gem_timeline_init__global(struct drm_i915_private *i915); -void i915_gem_timelines_mark_idle(struct drm_i915_private *i915); +void i915_gem_timelines_park(struct drm_i915_private *i915); void i915_gem_timeline_fini(struct i915_gem_timeline *tl); static inline int __intel_timeline_sync_set(struct intel_timeline *tl, diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c index 7481c8e1b5a8..aba50aa613f1 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.c +++ b/drivers/gpu/drm/i915/i915_gpu_error.c @@ -791,9 +791,10 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m, print_error_obj(m, dev_priv->engine[i], "WA batchbuffer", ee->wa_batchbuffer); - } - print_error_obj(m, NULL, "Semaphores", error->semaphore); + print_error_obj(m, dev_priv->engine[i], + "NULL context", ee->default_state); + } if (error->overlay) intel_overlay_print_error_state(m, error->overlay); @@ -903,8 +904,6 @@ void __i915_gpu_state_free(struct kref *error_ref) kfree(ee->waiters); } - i915_error_object_free(error->semaphore); - for (i = 0; i < ARRAY_SIZE(error->active_bo); i++) kfree(error->active_bo[i]); kfree(error->pinned_bo); @@ -957,7 +956,7 @@ i915_error_object_create(struct drm_i915_private *i915, ggtt->base.insert_page(&ggtt->base, dma, slot, I915_CACHE_NONE, 0); - s = io_mapping_map_atomic_wc(&ggtt->mappable, slot); + s = io_mapping_map_atomic_wc(&ggtt->iomap, slot); ret = compress_page(&compress, (void __force *)s, dst); io_mapping_unmap_atomic(s); @@ -1116,34 +1115,6 @@ gen8_engine_sync_index(struct intel_engine_cs *engine, return idx; } -static void gen8_record_semaphore_state(struct i915_gpu_state *error, - struct intel_engine_cs *engine, - struct drm_i915_error_engine *ee) -{ - struct drm_i915_private *dev_priv = engine->i915; - struct intel_engine_cs *to; - enum intel_engine_id id; - - if (!error->semaphore) - return; - - for_each_engine(to, dev_priv, id) { - int idx; - u16 signal_offset; - u32 *tmp; - - if (engine == to) - continue; - - signal_offset = - (GEN8_SIGNAL_OFFSET(engine, id) & (PAGE_SIZE - 1)) / 4; - tmp = error->semaphore->pages[0]; - idx = gen8_engine_sync_index(engine, to); - - ee->semaphore_mboxes[idx] = tmp[signal_offset]; - } -} - static void gen6_record_semaphore_state(struct intel_engine_cs *engine, struct drm_i915_error_engine *ee) { @@ -1218,7 +1189,6 @@ static void error_record_engine_registers(struct i915_gpu_state *error, if (INTEL_GEN(dev_priv) >= 6) { ee->rc_psmi = I915_READ(RING_PSMI_CTL(engine->mmio_base)); if (INTEL_GEN(dev_priv) >= 8) { - gen8_record_semaphore_state(error, engine, ee); ee->fault_reg = I915_READ(GEN8_RING_FAULT_REG); } else { gen6_record_semaphore_state(engine, ee); @@ -1447,15 +1417,30 @@ static void request_record_user_bo(struct drm_i915_gem_request *request, ee->user_bo_count = count; } +static struct drm_i915_error_object * +capture_object(struct drm_i915_private *dev_priv, + struct drm_i915_gem_object *obj) +{ + if (obj && i915_gem_object_has_pages(obj)) { + struct i915_vma fake = { + .node = { .start = U64_MAX, .size = obj->base.size }, + .size = obj->base.size, + .pages = obj->mm.pages, + .obj = obj, + }; + + return i915_error_object_create(dev_priv, &fake); + } else { + return NULL; + } +} + static void i915_gem_record_rings(struct drm_i915_private *dev_priv, struct i915_gpu_state *error) { struct i915_ggtt *ggtt = &dev_priv->ggtt; int i; - error->semaphore = - i915_error_object_create(dev_priv, dev_priv->semaphore); - for (i = 0; i < I915_NUM_ENGINES; i++) { struct intel_engine_cs *engine = dev_priv->engine[i]; struct drm_i915_error_engine *ee = &error->engine[i]; @@ -1521,6 +1506,9 @@ static void i915_gem_record_rings(struct drm_i915_private *dev_priv, ee->wa_ctx = i915_error_object_create(dev_priv, engine->wa_ctx.vma); + + ee->default_state = + capture_object(dev_priv, engine->default_state); } } diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 4fb183ae7a07..3517c6548e2c 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1400,7 +1400,7 @@ gen8_cs_irq_handler(struct intel_engine_cs *engine, u32 iir, int test_shift) if (iir & (GT_RENDER_USER_INTERRUPT << test_shift)) { notify_ring(engine); - tasklet |= i915_modparams.enable_guc_submission; + tasklet |= USES_GUC_SUBMISSION(engine->i915); } if (tasklet) @@ -3068,7 +3068,7 @@ static void vlv_display_irq_reset(struct drm_i915_private *dev_priv) i9xx_pipestat_irq_reset(dev_priv); GEN3_IRQ_RESET(VLV_); - dev_priv->irq_mask = ~0; + dev_priv->irq_mask = ~0u; } static void vlv_display_irq_postinstall(struct drm_i915_private *dev_priv) @@ -3093,7 +3093,7 @@ static void vlv_display_irq_postinstall(struct drm_i915_private *dev_priv) enable_mask |= I915_DISPLAY_PIPE_C_EVENT_INTERRUPT | I915_LPE_PIPE_C_INTERRUPT; - WARN_ON(dev_priv->irq_mask != ~0); + WARN_ON(dev_priv->irq_mask != ~0u); dev_priv->irq_mask = ~enable_mask; diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c index b4faeb6aa2bd..8dfea0320c2f 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -46,17 +46,6 @@ i915_param_named_unsafe(panel_ignore_lid, int, 0600, "Override lid status (0=autodetect, 1=autodetect disabled [default], " "-1=force lid closed, -2=force lid open)"); -i915_param_named_unsafe(semaphores, int, 0400, - "Use semaphores for inter-ring sync " - "(default: -1 (use per-chip defaults))"); - -i915_param_named_unsafe(enable_rc6, int, 0400, - "Enable power-saving render C-state 6. " - "Different stages can be selected via bitmask values " - "(0 = disable; 1 = enable rc6; 2 = enable deep rc6; 4 = enable deepest rc6). " - "For example, 3 would enable rc6 and deep rc6, and 7 would enable everything. " - "default: -1 (use per-chip default)"); - i915_param_named_unsafe(enable_dc, int, 0400, "Enable power-saving display C-states. " "(-1=auto [default]; 0=disable; 1=up to DC5; 2=up to DC6)"); @@ -99,10 +88,6 @@ i915_param_named_unsafe(enable_ppgtt, int, 0400, "Override PPGTT usage. " "(-1=auto [default], 0=disabled, 1=aliasing, 2=full, 3=full with extended address space)"); -i915_param_named_unsafe(enable_execlists, int, 0400, - "Override execlists usage. " - "(-1=auto [default], 0=disabled, 1=enabled)"); - i915_param_named_unsafe(enable_psr, int, 0600, "Enable PSR " "(0=disabled, 1=enabled - link mode chosen per-platform, 2=force link-standby mode, 3=force link-off mode) " @@ -162,13 +147,10 @@ i915_param_named_unsafe(edp_vswing, int, 0400, "(0=use value from vbt [default], 1=low power swing(200mV)," "2=default swing(400mV))"); -i915_param_named_unsafe(enable_guc_loading, int, 0400, - "Enable GuC firmware loading " - "(-1=auto, 0=never [default], 1=if available, 2=required)"); - -i915_param_named_unsafe(enable_guc_submission, int, 0400, - "Enable GuC submission " - "(-1=auto, 0=never [default], 1=if available, 2=required)"); +i915_param_named_unsafe(enable_guc, int, 0400, + "Enable GuC load for GuC submission and/or HuC load. " + "Required functionality can be selected using bitmask values. " + "(-1=auto, 0=disable [default], 1=GuC submission, 2=HuC load)"); i915_param_named(guc_log_level, int, 0400, "GuC firmware logging level (-1:disabled (default), 0-3:enabled)"); diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h index c7292268ed43..792ce26d7449 100644 --- a/drivers/gpu/drm/i915/i915_params.h +++ b/drivers/gpu/drm/i915/i915_params.h @@ -25,27 +25,27 @@ #ifndef _I915_PARAMS_H_ #define _I915_PARAMS_H_ +#include <linux/bitops.h> #include <linux/cache.h> /* for __read_mostly */ +#define ENABLE_GUC_SUBMISSION BIT(0) +#define ENABLE_GUC_LOAD_HUC BIT(1) + #define I915_PARAMS_FOR_EACH(param) \ param(char *, vbt_firmware, NULL) \ param(int, modeset, -1) \ param(int, panel_ignore_lid, 1) \ - param(int, semaphores, -1) \ param(int, lvds_channel_mode, 0) \ param(int, panel_use_ssc, -1) \ param(int, vbt_sdvo_panel_type, -1) \ - param(int, enable_rc6, -1) \ param(int, enable_dc, -1) \ param(int, enable_fbc, -1) \ param(int, enable_ppgtt, -1) \ - param(int, enable_execlists, -1) \ param(int, enable_psr, -1) \ param(int, disable_power_well, -1) \ param(int, enable_ips, 1) \ param(int, invert_brightness, 0) \ - param(int, enable_guc_loading, 0) \ - param(int, enable_guc_submission, 0) \ + param(int, enable_guc, 0) \ param(int, guc_log_level, -1) \ param(char *, guc_firmware_path, NULL) \ param(char *, huc_firmware_path, NULL) \ diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c index 6458c309c039..fa67d3dde20e 100644 --- a/drivers/gpu/drm/i915/i915_pci.c +++ b/drivers/gpu/drm/i915/i915_pci.c @@ -209,6 +209,8 @@ static const struct intel_device_info intel_gm45_info __initconst = { .has_hotplug = 1, \ .ring_mask = RENDER_RING | BSD_RING, \ .has_snoop = true, \ + /* ilk does support rc6, but we do not implement [power] contexts */ \ + .has_rc6 = 0, \ GEN_DEFAULT_PIPEOFFSETS, \ GEN_DEFAULT_PAGE_SIZES, \ CURSOR_OFFSETS diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c index 00be015e01df..f0cfdece14ae 100644 --- a/drivers/gpu/drm/i915/i915_perf.c +++ b/drivers/gpu/drm/i915/i915_perf.c @@ -1216,9 +1216,9 @@ static int oa_get_render_ctx_id(struct i915_perf_stream *stream) { struct drm_i915_private *dev_priv = stream->dev_priv; - if (i915_modparams.enable_execlists) + if (HAS_LOGICAL_RING_CONTEXTS(dev_priv)) { dev_priv->perf.oa.specific_ctx_id = stream->ctx->hw_id; - else { + } else { struct intel_engine_cs *engine = dev_priv->engine[RCS]; struct intel_ring *ring; int ret; @@ -1262,7 +1262,7 @@ static void oa_put_render_ctx_id(struct i915_perf_stream *stream) { struct drm_i915_private *dev_priv = stream->dev_priv; - if (i915_modparams.enable_execlists) { + if (HAS_LOGICAL_RING_CONTEXTS(dev_priv)) { dev_priv->perf.oa.specific_ctx_id = INVALID_CTX_ID; } else { struct intel_engine_cs *engine = dev_priv->engine[RCS]; @@ -1726,10 +1726,9 @@ static int gen8_switch_to_updated_kernel_context(struct drm_i915_private *dev_pr GFP_KERNEL); } - ret = i915_switch_context(req); i915_add_request(req); - return ret; + return 0; } /* @@ -2691,8 +2690,8 @@ err: static u64 oa_exponent_to_ns(struct drm_i915_private *dev_priv, int exponent) { - return div_u64(1000000000ULL * (2ULL << exponent), - dev_priv->perf.oa.timestamp_frequency); + return div64_u64(1000000000ULL * (2ULL << exponent), + 1000ULL * INTEL_INFO(dev_priv)->cs_timestamp_frequency_khz); } /** @@ -3007,7 +3006,7 @@ static bool gen8_is_valid_flex_addr(struct drm_i915_private *dev_priv, u32 addr) int i; for (i = 0; i < ARRAY_SIZE(flex_eu_regs); i++) { - if (flex_eu_regs[i].reg == addr) + if (i915_mmio_reg_offset(flex_eu_regs[i]) == addr) return true; } return false; @@ -3015,38 +3014,47 @@ static bool gen8_is_valid_flex_addr(struct drm_i915_private *dev_priv, u32 addr) static bool gen7_is_valid_b_counter_addr(struct drm_i915_private *dev_priv, u32 addr) { - return (addr >= OASTARTTRIG1.reg && addr <= OASTARTTRIG8.reg) || - (addr >= OAREPORTTRIG1.reg && addr <= OAREPORTTRIG8.reg) || - (addr >= OACEC0_0.reg && addr <= OACEC7_1.reg); + return (addr >= i915_mmio_reg_offset(OASTARTTRIG1) && + addr <= i915_mmio_reg_offset(OASTARTTRIG8)) || + (addr >= i915_mmio_reg_offset(OAREPORTTRIG1) && + addr <= i915_mmio_reg_offset(OAREPORTTRIG8)) || + (addr >= i915_mmio_reg_offset(OACEC0_0) && + addr <= i915_mmio_reg_offset(OACEC7_1)); } static bool gen7_is_valid_mux_addr(struct drm_i915_private *dev_priv, u32 addr) { - return addr == HALF_SLICE_CHICKEN2.reg || - (addr >= MICRO_BP0_0.reg && addr <= NOA_WRITE.reg) || - (addr >= OA_PERFCNT1_LO.reg && addr <= OA_PERFCNT2_HI.reg) || - (addr >= OA_PERFMATRIX_LO.reg && addr <= OA_PERFMATRIX_HI.reg); + return addr == i915_mmio_reg_offset(HALF_SLICE_CHICKEN2) || + (addr >= i915_mmio_reg_offset(MICRO_BP0_0) && + addr <= i915_mmio_reg_offset(NOA_WRITE)) || + (addr >= i915_mmio_reg_offset(OA_PERFCNT1_LO) && + addr <= i915_mmio_reg_offset(OA_PERFCNT2_HI)) || + (addr >= i915_mmio_reg_offset(OA_PERFMATRIX_LO) && + addr <= i915_mmio_reg_offset(OA_PERFMATRIX_HI)); } static bool gen8_is_valid_mux_addr(struct drm_i915_private *dev_priv, u32 addr) { return gen7_is_valid_mux_addr(dev_priv, addr) || - addr == WAIT_FOR_RC6_EXIT.reg || - (addr >= RPM_CONFIG0.reg && addr <= NOA_CONFIG(8).reg); + addr == i915_mmio_reg_offset(WAIT_FOR_RC6_EXIT) || + (addr >= i915_mmio_reg_offset(RPM_CONFIG0) && + addr <= i915_mmio_reg_offset(NOA_CONFIG(8))); } static bool gen10_is_valid_mux_addr(struct drm_i915_private *dev_priv, u32 addr) { return gen8_is_valid_mux_addr(dev_priv, addr) || - (addr >= OA_PERFCNT3_LO.reg && addr <= OA_PERFCNT4_HI.reg); + (addr >= i915_mmio_reg_offset(OA_PERFCNT3_LO) && + addr <= i915_mmio_reg_offset(OA_PERFCNT4_HI)); } static bool hsw_is_valid_mux_addr(struct drm_i915_private *dev_priv, u32 addr) { return gen7_is_valid_mux_addr(dev_priv, addr) || (addr >= 0x25100 && addr <= 0x2FF90) || - (addr >= HSW_MBVID2_NOA0.reg && addr <= HSW_MBVID2_NOA9.reg) || - addr == HSW_MBVID2_MISR0.reg; + (addr >= i915_mmio_reg_offset(HSW_MBVID2_NOA0) && + addr <= i915_mmio_reg_offset(HSW_MBVID2_NOA9)) || + addr == i915_mmio_reg_offset(HSW_MBVID2_MISR0); } static bool chv_is_valid_mux_addr(struct drm_i915_private *dev_priv, u32 addr) @@ -3061,14 +3069,14 @@ static uint32_t mask_reg_value(u32 reg, u32 val) * WaDisableSTUnitPowerOptimization workaround. Make sure the value * programmed by userspace doesn't change this. */ - if (HALF_SLICE_CHICKEN2.reg == reg) + if (i915_mmio_reg_offset(HALF_SLICE_CHICKEN2) == reg) val = val & ~_MASKED_BIT_ENABLE(GEN8_ST_PO_DISABLE); /* WAIT_FOR_RC6_EXIT has only one bit fullfilling the function * indicated by its name and a bunch of selection fields used by OA * configs. */ - if (WAIT_FOR_RC6_EXIT.reg == reg) + if (i915_mmio_reg_offset(WAIT_FOR_RC6_EXIT) == reg) val = val & ~_MASKED_BIT_ENABLE(HSW_WAIT_FOR_RC6_EXIT_ENABLE); return val; @@ -3415,8 +3423,6 @@ static struct ctl_table dev_root[] = { */ void i915_perf_init(struct drm_i915_private *dev_priv) { - dev_priv->perf.oa.timestamp_frequency = 0; - if (IS_HASWELL(dev_priv)) { dev_priv->perf.oa.ops.is_valid_b_counter_reg = gen7_is_valid_b_counter_addr; @@ -3432,10 +3438,8 @@ void i915_perf_init(struct drm_i915_private *dev_priv) dev_priv->perf.oa.ops.oa_hw_tail_read = gen7_oa_hw_tail_read; - dev_priv->perf.oa.timestamp_frequency = 12500000; - dev_priv->perf.oa.oa_formats = hsw_oa_formats; - } else if (i915_modparams.enable_execlists) { + } else if (HAS_LOGICAL_RING_CONTEXTS(dev_priv)) { /* Note: that although we could theoretically also support the * legacy ringbuffer mode on BDW (and earlier iterations of * this driver, before upstreaming did this) it didn't seem @@ -3477,23 +3481,6 @@ void i915_perf_init(struct drm_i915_private *dev_priv) dev_priv->perf.oa.gen8_valid_ctx_bit = (1<<16); } - - switch (dev_priv->info.platform) { - case INTEL_BROADWELL: - dev_priv->perf.oa.timestamp_frequency = 12500000; - break; - case INTEL_BROXTON: - case INTEL_GEMINILAKE: - dev_priv->perf.oa.timestamp_frequency = 19200000; - break; - case INTEL_SKYLAKE: - case INTEL_KABYLAKE: - case INTEL_COFFEELAKE: - dev_priv->perf.oa.timestamp_frequency = 12000000; - break; - default: - break; - } } else if (IS_GEN10(dev_priv)) { dev_priv->perf.oa.ops.is_valid_b_counter_reg = gen7_is_valid_b_counter_addr; @@ -3509,15 +3496,10 @@ void i915_perf_init(struct drm_i915_private *dev_priv) dev_priv->perf.oa.ctx_flexeu0_offset = 0x3de; dev_priv->perf.oa.gen8_valid_ctx_bit = (1<<16); - - /* Default frequency, although we need to read it from - * the register as it might vary between parts. - */ - dev_priv->perf.oa.timestamp_frequency = 12000000; } } - if (dev_priv->perf.oa.timestamp_frequency) { + if (dev_priv->perf.oa.ops.enable_metric_set) { hrtimer_init(&dev_priv->perf.oa.poll_check_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); dev_priv->perf.oa.poll_check_timer.function = oa_poll_check_timer_cb; @@ -3527,8 +3509,8 @@ void i915_perf_init(struct drm_i915_private *dev_priv) mutex_init(&dev_priv->perf.lock); spin_lock_init(&dev_priv->perf.oa.oa_buffer.ptr_lock); - oa_sample_rate_hard_limit = - dev_priv->perf.oa.timestamp_frequency / 2; + oa_sample_rate_hard_limit = 1000 * + (INTEL_INFO(dev_priv)->cs_timestamp_frequency_khz / 2); dev_priv->perf.sysctl_header = register_sysctl_table(dev_root); mutex_init(&dev_priv->perf.metrics_lock); diff --git a/drivers/gpu/drm/i915/i915_pmu.c b/drivers/gpu/drm/i915/i915_pmu.c new file mode 100644 index 000000000000..55a8a1e29424 --- /dev/null +++ b/drivers/gpu/drm/i915/i915_pmu.c @@ -0,0 +1,865 @@ +/* + * Copyright © 2017 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. + * + */ + +#include <linux/perf_event.h> +#include <linux/pm_runtime.h> + +#include "i915_drv.h" +#include "i915_pmu.h" +#include "intel_ringbuffer.h" + +/* Frequency for the sampling timer for events which need it. */ +#define FREQUENCY 200 +#define PERIOD max_t(u64, 10000, NSEC_PER_SEC / FREQUENCY) + +#define ENGINE_SAMPLE_MASK \ + (BIT(I915_SAMPLE_BUSY) | \ + BIT(I915_SAMPLE_WAIT) | \ + BIT(I915_SAMPLE_SEMA)) + +#define ENGINE_SAMPLE_BITS (1 << I915_PMU_SAMPLE_BITS) + +static cpumask_t i915_pmu_cpumask; + +static u8 engine_config_sample(u64 config) +{ + return config & I915_PMU_SAMPLE_MASK; +} + +static u8 engine_event_sample(struct perf_event *event) +{ + return engine_config_sample(event->attr.config); +} + +static u8 engine_event_class(struct perf_event *event) +{ + return (event->attr.config >> I915_PMU_CLASS_SHIFT) & 0xff; +} + +static u8 engine_event_instance(struct perf_event *event) +{ + return (event->attr.config >> I915_PMU_SAMPLE_BITS) & 0xff; +} + +static bool is_engine_config(u64 config) +{ + return config < __I915_PMU_OTHER(0); +} + +static unsigned int config_enabled_bit(u64 config) +{ + if (is_engine_config(config)) + return engine_config_sample(config); + else + return ENGINE_SAMPLE_BITS + (config - __I915_PMU_OTHER(0)); +} + +static u64 config_enabled_mask(u64 config) +{ + return BIT_ULL(config_enabled_bit(config)); +} + +static bool is_engine_event(struct perf_event *event) +{ + return is_engine_config(event->attr.config); +} + +static unsigned int event_enabled_bit(struct perf_event *event) +{ + return config_enabled_bit(event->attr.config); +} + +static bool pmu_needs_timer(struct drm_i915_private *i915, bool gpu_active) +{ + u64 enable; + + /* + * Only some counters need the sampling timer. + * + * We start with a bitmask of all currently enabled events. + */ + enable = i915->pmu.enable; + + /* + * Mask out all the ones which do not need the timer, or in + * other words keep all the ones that could need the timer. + */ + enable &= config_enabled_mask(I915_PMU_ACTUAL_FREQUENCY) | + config_enabled_mask(I915_PMU_REQUESTED_FREQUENCY) | + ENGINE_SAMPLE_MASK; + + /* + * When the GPU is idle per-engine counters do not need to be + * running so clear those bits out. + */ + if (!gpu_active) + enable &= ~ENGINE_SAMPLE_MASK; + /* + * Also there is software busyness tracking available we do not + * need the timer for I915_SAMPLE_BUSY counter. + * + * Use RCS as proxy for all engines. + */ + else if (intel_engine_supports_stats(i915->engine[RCS])) + enable &= ~BIT(I915_SAMPLE_BUSY); + + /* + * If some bits remain it means we need the sampling timer running. + */ + return enable; +} + +void i915_pmu_gt_parked(struct drm_i915_private *i915) +{ + if (!i915->pmu.base.event_init) + return; + + spin_lock_irq(&i915->pmu.lock); + /* + * Signal sampling timer to stop if only engine events are enabled and + * GPU went idle. + */ + i915->pmu.timer_enabled = pmu_needs_timer(i915, false); + spin_unlock_irq(&i915->pmu.lock); +} + +static void __i915_pmu_maybe_start_timer(struct drm_i915_private *i915) +{ + if (!i915->pmu.timer_enabled && pmu_needs_timer(i915, true)) { + i915->pmu.timer_enabled = true; + hrtimer_start_range_ns(&i915->pmu.timer, + ns_to_ktime(PERIOD), 0, + HRTIMER_MODE_REL_PINNED); + } +} + +void i915_pmu_gt_unparked(struct drm_i915_private *i915) +{ + if (!i915->pmu.base.event_init) + return; + + spin_lock_irq(&i915->pmu.lock); + /* + * Re-enable sampling timer when GPU goes active. + */ + __i915_pmu_maybe_start_timer(i915); + spin_unlock_irq(&i915->pmu.lock); +} + +static bool grab_forcewake(struct drm_i915_private *i915, bool fw) +{ + if (!fw) + intel_uncore_forcewake_get(i915, FORCEWAKE_ALL); + + return true; +} + +static void +update_sample(struct i915_pmu_sample *sample, u32 unit, u32 val) +{ + sample->cur += mul_u32_u32(val, unit); +} + +static void engines_sample(struct drm_i915_private *dev_priv) +{ + struct intel_engine_cs *engine; + enum intel_engine_id id; + bool fw = false; + + if ((dev_priv->pmu.enable & ENGINE_SAMPLE_MASK) == 0) + return; + + if (!dev_priv->gt.awake) + return; + + if (!intel_runtime_pm_get_if_in_use(dev_priv)) + return; + + for_each_engine(engine, dev_priv, id) { + u32 current_seqno = intel_engine_get_seqno(engine); + u32 last_seqno = intel_engine_last_submit(engine); + u32 val; + + val = !i915_seqno_passed(current_seqno, last_seqno); + + update_sample(&engine->pmu.sample[I915_SAMPLE_BUSY], + PERIOD, val); + + if (val && (engine->pmu.enable & + (BIT(I915_SAMPLE_WAIT) | BIT(I915_SAMPLE_SEMA)))) { + fw = grab_forcewake(dev_priv, fw); + + val = I915_READ_FW(RING_CTL(engine->mmio_base)); + } else { + val = 0; + } + + update_sample(&engine->pmu.sample[I915_SAMPLE_WAIT], + PERIOD, !!(val & RING_WAIT)); + + update_sample(&engine->pmu.sample[I915_SAMPLE_SEMA], + PERIOD, !!(val & RING_WAIT_SEMAPHORE)); + } + + if (fw) + intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); + + intel_runtime_pm_put(dev_priv); +} + +static void frequency_sample(struct drm_i915_private *dev_priv) +{ + if (dev_priv->pmu.enable & + config_enabled_mask(I915_PMU_ACTUAL_FREQUENCY)) { + u32 val; + + val = dev_priv->gt_pm.rps.cur_freq; + if (dev_priv->gt.awake && + intel_runtime_pm_get_if_in_use(dev_priv)) { + val = intel_get_cagf(dev_priv, + I915_READ_NOTRACE(GEN6_RPSTAT1)); + intel_runtime_pm_put(dev_priv); + } + + update_sample(&dev_priv->pmu.sample[__I915_SAMPLE_FREQ_ACT], + 1, intel_gpu_freq(dev_priv, val)); + } + + if (dev_priv->pmu.enable & + config_enabled_mask(I915_PMU_REQUESTED_FREQUENCY)) { + update_sample(&dev_priv->pmu.sample[__I915_SAMPLE_FREQ_REQ], 1, + intel_gpu_freq(dev_priv, + dev_priv->gt_pm.rps.cur_freq)); + } +} + +static enum hrtimer_restart i915_sample(struct hrtimer *hrtimer) +{ + struct drm_i915_private *i915 = + container_of(hrtimer, struct drm_i915_private, pmu.timer); + + if (!READ_ONCE(i915->pmu.timer_enabled)) + return HRTIMER_NORESTART; + + engines_sample(i915); + frequency_sample(i915); + + hrtimer_forward_now(hrtimer, ns_to_ktime(PERIOD)); + return HRTIMER_RESTART; +} + +static u64 count_interrupts(struct drm_i915_private *i915) +{ + /* open-coded kstat_irqs() */ + struct irq_desc *desc = irq_to_desc(i915->drm.pdev->irq); + u64 sum = 0; + int cpu; + + if (!desc || !desc->kstat_irqs) + return 0; + + for_each_possible_cpu(cpu) + sum += *per_cpu_ptr(desc->kstat_irqs, cpu); + + return sum; +} + +static void i915_pmu_event_destroy(struct perf_event *event) +{ + WARN_ON(event->parent); +} + +static int engine_event_init(struct perf_event *event) +{ + struct drm_i915_private *i915 = + container_of(event->pmu, typeof(*i915), pmu.base); + + if (!intel_engine_lookup_user(i915, engine_event_class(event), + engine_event_instance(event))) + return -ENODEV; + + switch (engine_event_sample(event)) { + case I915_SAMPLE_BUSY: + case I915_SAMPLE_WAIT: + break; + case I915_SAMPLE_SEMA: + if (INTEL_GEN(i915) < 6) + return -ENODEV; + break; + default: + return -ENOENT; + } + + return 0; +} + +static int i915_pmu_event_init(struct perf_event *event) +{ + struct drm_i915_private *i915 = + container_of(event->pmu, typeof(*i915), pmu.base); + int ret; + + if (event->attr.type != event->pmu->type) + return -ENOENT; + + /* unsupported modes and filters */ + if (event->attr.sample_period) /* no sampling */ + return -EINVAL; + + if (has_branch_stack(event)) + return -EOPNOTSUPP; + + if (event->cpu < 0) + return -EINVAL; + + /* only allow running on one cpu at a time */ + if (!cpumask_test_cpu(event->cpu, &i915_pmu_cpumask)) + return -EINVAL; + + if (is_engine_event(event)) { + ret = engine_event_init(event); + } else { + ret = 0; + switch (event->attr.config) { + case I915_PMU_ACTUAL_FREQUENCY: + if (IS_VALLEYVIEW(i915) || IS_CHERRYVIEW(i915)) + /* Requires a mutex for sampling! */ + ret = -ENODEV; + case I915_PMU_REQUESTED_FREQUENCY: + if (INTEL_GEN(i915) < 6) + ret = -ENODEV; + break; + case I915_PMU_INTERRUPTS: + break; + case I915_PMU_RC6_RESIDENCY: + if (!HAS_RC6(i915)) + ret = -ENODEV; + break; + default: + ret = -ENOENT; + break; + } + } + if (ret) + return ret; + + if (!event->parent) + event->destroy = i915_pmu_event_destroy; + + return 0; +} + +static u64 __i915_pmu_event_read(struct perf_event *event) +{ + struct drm_i915_private *i915 = + container_of(event->pmu, typeof(*i915), pmu.base); + u64 val = 0; + + if (is_engine_event(event)) { + u8 sample = engine_event_sample(event); + struct intel_engine_cs *engine; + + engine = intel_engine_lookup_user(i915, + engine_event_class(event), + engine_event_instance(event)); + + if (WARN_ON_ONCE(!engine)) { + /* Do nothing */ + } else if (sample == I915_SAMPLE_BUSY && + engine->pmu.busy_stats) { + val = ktime_to_ns(intel_engine_get_busy_time(engine)); + } else { + val = engine->pmu.sample[sample].cur; + } + } else { + switch (event->attr.config) { + case I915_PMU_ACTUAL_FREQUENCY: + val = + div_u64(i915->pmu.sample[__I915_SAMPLE_FREQ_ACT].cur, + FREQUENCY); + break; + case I915_PMU_REQUESTED_FREQUENCY: + val = + div_u64(i915->pmu.sample[__I915_SAMPLE_FREQ_REQ].cur, + FREQUENCY); + break; + case I915_PMU_INTERRUPTS: + val = count_interrupts(i915); + break; + case I915_PMU_RC6_RESIDENCY: + intel_runtime_pm_get(i915); + val = intel_rc6_residency_ns(i915, + IS_VALLEYVIEW(i915) ? + VLV_GT_RENDER_RC6 : + GEN6_GT_GFX_RC6); + if (HAS_RC6p(i915)) + val += intel_rc6_residency_ns(i915, + GEN6_GT_GFX_RC6p); + if (HAS_RC6pp(i915)) + val += intel_rc6_residency_ns(i915, + GEN6_GT_GFX_RC6pp); + intel_runtime_pm_put(i915); + break; + } + } + + return val; +} + +static void i915_pmu_event_read(struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + u64 prev, new; + +again: + prev = local64_read(&hwc->prev_count); + new = __i915_pmu_event_read(event); + + if (local64_cmpxchg(&hwc->prev_count, prev, new) != prev) + goto again; + + local64_add(new - prev, &event->count); +} + +static bool engine_needs_busy_stats(struct intel_engine_cs *engine) +{ + return intel_engine_supports_stats(engine) && + (engine->pmu.enable & BIT(I915_SAMPLE_BUSY)); +} + +static void i915_pmu_enable(struct perf_event *event) +{ + struct drm_i915_private *i915 = + container_of(event->pmu, typeof(*i915), pmu.base); + unsigned int bit = event_enabled_bit(event); + unsigned long flags; + + spin_lock_irqsave(&i915->pmu.lock, flags); + + /* + * Update the bitmask of enabled events and increment + * the event reference counter. + */ + GEM_BUG_ON(bit >= I915_PMU_MASK_BITS); + GEM_BUG_ON(i915->pmu.enable_count[bit] == ~0); + i915->pmu.enable |= BIT_ULL(bit); + i915->pmu.enable_count[bit]++; + + /* + * Start the sampling timer if needed and not already enabled. + */ + __i915_pmu_maybe_start_timer(i915); + + /* + * For per-engine events the bitmask and reference counting + * is stored per engine. + */ + if (is_engine_event(event)) { + u8 sample = engine_event_sample(event); + struct intel_engine_cs *engine; + + engine = intel_engine_lookup_user(i915, + engine_event_class(event), + engine_event_instance(event)); + GEM_BUG_ON(!engine); + engine->pmu.enable |= BIT(sample); + + GEM_BUG_ON(sample >= I915_PMU_SAMPLE_BITS); + GEM_BUG_ON(engine->pmu.enable_count[sample] == ~0); + if (engine->pmu.enable_count[sample]++ == 0) { + /* + * Enable engine busy stats tracking if needed or + * alternatively cancel the scheduled disable. + * + * If the delayed disable was pending, cancel it and + * in this case do not enable since it already is. + */ + if (engine_needs_busy_stats(engine) && + !engine->pmu.busy_stats) { + engine->pmu.busy_stats = true; + if (!cancel_delayed_work(&engine->pmu.disable_busy_stats)) + intel_enable_engine_stats(engine); + } + } + } + + /* + * Store the current counter value so we can report the correct delta + * for all listeners. Even when the event was already enabled and has + * an existing non-zero value. + */ + local64_set(&event->hw.prev_count, __i915_pmu_event_read(event)); + + spin_unlock_irqrestore(&i915->pmu.lock, flags); +} + +static void __disable_busy_stats(struct work_struct *work) +{ + struct intel_engine_cs *engine = + container_of(work, typeof(*engine), pmu.disable_busy_stats.work); + + intel_disable_engine_stats(engine); +} + +static void i915_pmu_disable(struct perf_event *event) +{ + struct drm_i915_private *i915 = + container_of(event->pmu, typeof(*i915), pmu.base); + unsigned int bit = event_enabled_bit(event); + unsigned long flags; + + spin_lock_irqsave(&i915->pmu.lock, flags); + + if (is_engine_event(event)) { + u8 sample = engine_event_sample(event); + struct intel_engine_cs *engine; + + engine = intel_engine_lookup_user(i915, + engine_event_class(event), + engine_event_instance(event)); + GEM_BUG_ON(!engine); + GEM_BUG_ON(sample >= I915_PMU_SAMPLE_BITS); + GEM_BUG_ON(engine->pmu.enable_count[sample] == 0); + /* + * Decrement the reference count and clear the enabled + * bitmask when the last listener on an event goes away. + */ + if (--engine->pmu.enable_count[sample] == 0) { + engine->pmu.enable &= ~BIT(sample); + if (!engine_needs_busy_stats(engine) && + engine->pmu.busy_stats) { + engine->pmu.busy_stats = false; + /* + * We request a delayed disable to handle the + * rapid on/off cycles on events, which can + * happen when tools like perf stat start, in a + * nicer way. + * + * In addition, this also helps with busy stats + * accuracy with background CPU offline/online + * migration events. + */ + queue_delayed_work(system_wq, + &engine->pmu.disable_busy_stats, + round_jiffies_up_relative(HZ)); + } + } + } + + GEM_BUG_ON(bit >= I915_PMU_MASK_BITS); + GEM_BUG_ON(i915->pmu.enable_count[bit] == 0); + /* + * Decrement the reference count and clear the enabled + * bitmask when the last listener on an event goes away. + */ + if (--i915->pmu.enable_count[bit] == 0) { + i915->pmu.enable &= ~BIT_ULL(bit); + i915->pmu.timer_enabled &= pmu_needs_timer(i915, true); + } + + spin_unlock_irqrestore(&i915->pmu.lock, flags); +} + +static void i915_pmu_event_start(struct perf_event *event, int flags) +{ + i915_pmu_enable(event); + event->hw.state = 0; +} + +static void i915_pmu_event_stop(struct perf_event *event, int flags) +{ + if (flags & PERF_EF_UPDATE) + i915_pmu_event_read(event); + i915_pmu_disable(event); + event->hw.state = PERF_HES_STOPPED; +} + +static int i915_pmu_event_add(struct perf_event *event, int flags) +{ + if (flags & PERF_EF_START) + i915_pmu_event_start(event, flags); + + return 0; +} + +static void i915_pmu_event_del(struct perf_event *event, int flags) +{ + i915_pmu_event_stop(event, PERF_EF_UPDATE); +} + +static int i915_pmu_event_event_idx(struct perf_event *event) +{ + return 0; +} + +struct i915_str_attribute { + struct device_attribute attr; + const char *str; +}; + +static ssize_t i915_pmu_format_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i915_str_attribute *eattr; + + eattr = container_of(attr, struct i915_str_attribute, attr); + return sprintf(buf, "%s\n", eattr->str); +} + +#define I915_PMU_FORMAT_ATTR(_name, _config) \ + (&((struct i915_str_attribute[]) { \ + { .attr = __ATTR(_name, 0444, i915_pmu_format_show, NULL), \ + .str = _config, } \ + })[0].attr.attr) + +static struct attribute *i915_pmu_format_attrs[] = { + I915_PMU_FORMAT_ATTR(i915_eventid, "config:0-20"), + NULL, +}; + +static const struct attribute_group i915_pmu_format_attr_group = { + .name = "format", + .attrs = i915_pmu_format_attrs, +}; + +struct i915_ext_attribute { + struct device_attribute attr; + unsigned long val; +}; + +static ssize_t i915_pmu_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i915_ext_attribute *eattr; + + eattr = container_of(attr, struct i915_ext_attribute, attr); + return sprintf(buf, "config=0x%lx\n", eattr->val); +} + +#define I915_EVENT_ATTR(_name, _config) \ + (&((struct i915_ext_attribute[]) { \ + { .attr = __ATTR(_name, 0444, i915_pmu_event_show, NULL), \ + .val = _config, } \ + })[0].attr.attr) + +#define I915_EVENT_STR(_name, _str) \ + (&((struct perf_pmu_events_attr[]) { \ + { .attr = __ATTR(_name, 0444, perf_event_sysfs_show, NULL), \ + .id = 0, \ + .event_str = _str, } \ + })[0].attr.attr) + +#define I915_EVENT(_name, _config, _unit) \ + I915_EVENT_ATTR(_name, _config), \ + I915_EVENT_STR(_name.unit, _unit) + +#define I915_ENGINE_EVENT(_name, _class, _instance, _sample) \ + I915_EVENT_ATTR(_name, __I915_PMU_ENGINE(_class, _instance, _sample)), \ + I915_EVENT_STR(_name.unit, "ns") + +#define I915_ENGINE_EVENTS(_name, _class, _instance) \ + I915_ENGINE_EVENT(_name##_instance-busy, _class, _instance, I915_SAMPLE_BUSY), \ + I915_ENGINE_EVENT(_name##_instance-sema, _class, _instance, I915_SAMPLE_SEMA), \ + I915_ENGINE_EVENT(_name##_instance-wait, _class, _instance, I915_SAMPLE_WAIT) + +static struct attribute *i915_pmu_events_attrs[] = { + I915_ENGINE_EVENTS(rcs, I915_ENGINE_CLASS_RENDER, 0), + I915_ENGINE_EVENTS(bcs, I915_ENGINE_CLASS_COPY, 0), + I915_ENGINE_EVENTS(vcs, I915_ENGINE_CLASS_VIDEO, 0), + I915_ENGINE_EVENTS(vcs, I915_ENGINE_CLASS_VIDEO, 1), + I915_ENGINE_EVENTS(vecs, I915_ENGINE_CLASS_VIDEO_ENHANCE, 0), + + I915_EVENT(actual-frequency, I915_PMU_ACTUAL_FREQUENCY, "MHz"), + I915_EVENT(requested-frequency, I915_PMU_REQUESTED_FREQUENCY, "MHz"), + + I915_EVENT_ATTR(interrupts, I915_PMU_INTERRUPTS), + + I915_EVENT(rc6-residency, I915_PMU_RC6_RESIDENCY, "ns"), + + NULL, +}; + +static const struct attribute_group i915_pmu_events_attr_group = { + .name = "events", + .attrs = i915_pmu_events_attrs, +}; + +static ssize_t +i915_pmu_get_attr_cpumask(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return cpumap_print_to_pagebuf(true, buf, &i915_pmu_cpumask); +} + +static DEVICE_ATTR(cpumask, 0444, i915_pmu_get_attr_cpumask, NULL); + +static struct attribute *i915_cpumask_attrs[] = { + &dev_attr_cpumask.attr, + NULL, +}; + +static struct attribute_group i915_pmu_cpumask_attr_group = { + .attrs = i915_cpumask_attrs, +}; + +static const struct attribute_group *i915_pmu_attr_groups[] = { + &i915_pmu_format_attr_group, + &i915_pmu_events_attr_group, + &i915_pmu_cpumask_attr_group, + NULL +}; + +static int i915_pmu_cpu_online(unsigned int cpu, struct hlist_node *node) +{ + struct i915_pmu *pmu = hlist_entry_safe(node, typeof(*pmu), node); + + GEM_BUG_ON(!pmu->base.event_init); + + /* Select the first online CPU as a designated reader. */ + if (!cpumask_weight(&i915_pmu_cpumask)) + cpumask_set_cpu(cpu, &i915_pmu_cpumask); + + return 0; +} + +static int i915_pmu_cpu_offline(unsigned int cpu, struct hlist_node *node) +{ + struct i915_pmu *pmu = hlist_entry_safe(node, typeof(*pmu), node); + unsigned int target; + + GEM_BUG_ON(!pmu->base.event_init); + + if (cpumask_test_and_clear_cpu(cpu, &i915_pmu_cpumask)) { + target = cpumask_any_but(topology_sibling_cpumask(cpu), cpu); + /* Migrate events if there is a valid target */ + if (target < nr_cpu_ids) { + cpumask_set_cpu(target, &i915_pmu_cpumask); + perf_pmu_migrate_context(&pmu->base, cpu, target); + } + } + + return 0; +} + +static enum cpuhp_state cpuhp_slot = CPUHP_INVALID; + +static int i915_pmu_register_cpuhp_state(struct drm_i915_private *i915) +{ + enum cpuhp_state slot; + int ret; + + ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, + "perf/x86/intel/i915:online", + i915_pmu_cpu_online, + i915_pmu_cpu_offline); + if (ret < 0) + return ret; + + slot = ret; + ret = cpuhp_state_add_instance(slot, &i915->pmu.node); + if (ret) { + cpuhp_remove_multi_state(slot); + return ret; + } + + cpuhp_slot = slot; + return 0; +} + +static void i915_pmu_unregister_cpuhp_state(struct drm_i915_private *i915) +{ + WARN_ON(cpuhp_slot == CPUHP_INVALID); + WARN_ON(cpuhp_state_remove_instance(cpuhp_slot, &i915->pmu.node)); + cpuhp_remove_multi_state(cpuhp_slot); +} + +void i915_pmu_register(struct drm_i915_private *i915) +{ + struct intel_engine_cs *engine; + enum intel_engine_id id; + int ret; + + if (INTEL_GEN(i915) <= 2) { + DRM_INFO("PMU not supported for this GPU."); + return; + } + + i915->pmu.base.attr_groups = i915_pmu_attr_groups; + i915->pmu.base.task_ctx_nr = perf_invalid_context; + i915->pmu.base.event_init = i915_pmu_event_init; + i915->pmu.base.add = i915_pmu_event_add; + i915->pmu.base.del = i915_pmu_event_del; + i915->pmu.base.start = i915_pmu_event_start; + i915->pmu.base.stop = i915_pmu_event_stop; + i915->pmu.base.read = i915_pmu_event_read; + i915->pmu.base.event_idx = i915_pmu_event_event_idx; + + spin_lock_init(&i915->pmu.lock); + hrtimer_init(&i915->pmu.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + i915->pmu.timer.function = i915_sample; + + for_each_engine(engine, i915, id) + INIT_DELAYED_WORK(&engine->pmu.disable_busy_stats, + __disable_busy_stats); + + ret = perf_pmu_register(&i915->pmu.base, "i915", -1); + if (ret) + goto err; + + ret = i915_pmu_register_cpuhp_state(i915); + if (ret) + goto err_unreg; + + return; + +err_unreg: + perf_pmu_unregister(&i915->pmu.base); +err: + i915->pmu.base.event_init = NULL; + DRM_NOTE("Failed to register PMU! (err=%d)\n", ret); +} + +void i915_pmu_unregister(struct drm_i915_private *i915) +{ + struct intel_engine_cs *engine; + enum intel_engine_id id; + + if (!i915->pmu.base.event_init) + return; + + WARN_ON(i915->pmu.enable); + + hrtimer_cancel(&i915->pmu.timer); + + for_each_engine(engine, i915, id) { + GEM_BUG_ON(engine->pmu.busy_stats); + flush_delayed_work(&engine->pmu.disable_busy_stats); + } + + i915_pmu_unregister_cpuhp_state(i915); + + perf_pmu_unregister(&i915->pmu.base); + i915->pmu.base.event_init = NULL; +} diff --git a/drivers/gpu/drm/i915/i915_pmu.h b/drivers/gpu/drm/i915/i915_pmu.h new file mode 100644 index 000000000000..40c154d13565 --- /dev/null +++ b/drivers/gpu/drm/i915/i915_pmu.h @@ -0,0 +1,111 @@ +/* + * Copyright © 2017 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. + * + */ +#ifndef __I915_PMU_H__ +#define __I915_PMU_H__ + +enum { + __I915_SAMPLE_FREQ_ACT = 0, + __I915_SAMPLE_FREQ_REQ, + __I915_NUM_PMU_SAMPLERS +}; + +/** + * How many different events we track in the global PMU mask. + * + * It is also used to know to needed number of event reference counters. + */ +#define I915_PMU_MASK_BITS \ + ((1 << I915_PMU_SAMPLE_BITS) + \ + (I915_PMU_LAST + 1 - __I915_PMU_OTHER(0))) + +struct i915_pmu_sample { + u64 cur; +}; + +struct i915_pmu { + /** + * @node: List node for CPU hotplug handling. + */ + struct hlist_node node; + /** + * @base: PMU base. + */ + struct pmu base; + /** + * @lock: Lock protecting enable mask and ref count handling. + */ + spinlock_t lock; + /** + * @timer: Timer for internal i915 PMU sampling. + */ + struct hrtimer timer; + /** + * @enable: Bitmask of all currently enabled events. + * + * Bits are derived from uAPI event numbers in a way that low 16 bits + * correspond to engine event _sample_ _type_ (I915_SAMPLE_QUEUED is + * bit 0), and higher bits correspond to other events (for instance + * I915_PMU_ACTUAL_FREQUENCY is bit 16 etc). + * + * In other words, low 16 bits are not per engine but per engine + * sampler type, while the upper bits are directly mapped to other + * event types. + */ + u64 enable; + /** + * @enable_count: Reference counts for the enabled events. + * + * Array indices are mapped in the same way as bits in the @enable field + * and they are used to control sampling on/off when multiple clients + * are using the PMU API. + */ + unsigned int enable_count[I915_PMU_MASK_BITS]; + /** + * @timer_enabled: Should the internal sampling timer be running. + */ + bool timer_enabled; + /** + * @sample: Current and previous (raw) counters for sampling events. + * + * These counters are updated from the i915 PMU sampling timer. + * + * Only global counters are held here, while the per-engine ones are in + * struct intel_engine_cs. + */ + struct i915_pmu_sample sample[__I915_NUM_PMU_SAMPLERS]; +}; + +#ifdef CONFIG_PERF_EVENTS +void i915_pmu_register(struct drm_i915_private *i915); +void i915_pmu_unregister(struct drm_i915_private *i915); +void i915_pmu_gt_parked(struct drm_i915_private *i915); +void i915_pmu_gt_unparked(struct drm_i915_private *i915); +#else +static inline void i915_pmu_register(struct drm_i915_private *i915) {} +static inline void i915_pmu_unregister(struct drm_i915_private *i915) {} +static inline void i915_pmu_gt_parked(struct drm_i915_private *i915) {} +static inline void i915_pmu_gt_unparked(struct drm_i915_private *i915) {} +#endif + +#endif diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 96c80fa0fcac..09bf043c1c2e 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -186,6 +186,9 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg) #define VIDEO_ENHANCEMENT_CLASS 2 #define COPY_ENGINE_CLASS 3 #define OTHER_CLASS 4 +#define MAX_ENGINE_CLASS 4 + +#define MAX_ENGINE_INSTANCE 1 /* PCI config space */ diff --git a/drivers/gpu/drm/i915/i915_sw_fence.c b/drivers/gpu/drm/i915/i915_sw_fence.c index e8ca67a129d2..3669f5eeb91e 100644 --- a/drivers/gpu/drm/i915/i915_sw_fence.c +++ b/drivers/gpu/drm/i915/i915_sw_fence.c @@ -303,6 +303,7 @@ static int __i915_sw_fence_await_sw_fence(struct i915_sw_fence *fence, int pending; debug_fence_assert(fence); + might_sleep_if(gfpflags_allow_blocking(gfp)); if (i915_sw_fence_done(signaler)) return 0; @@ -367,6 +368,7 @@ struct i915_sw_dma_fence_cb { struct dma_fence *dma; struct timer_list timer; struct irq_work work; + struct rcu_head rcu; }; static void timer_i915_sw_fence_wake(struct timer_list *t) @@ -406,7 +408,7 @@ static void irq_i915_sw_fence_work(struct irq_work *wrk) del_timer_sync(&cb->timer); dma_fence_put(cb->dma); - kfree(cb); + kfree_rcu(cb, rcu); } int i915_sw_fence_await_dma_fence(struct i915_sw_fence *fence, @@ -418,6 +420,7 @@ int i915_sw_fence_await_dma_fence(struct i915_sw_fence *fence, int ret; debug_fence_assert(fence); + might_sleep_if(gfpflags_allow_blocking(gfp)); if (dma_fence_is_signaled(dma)) return 0; @@ -464,6 +467,7 @@ int i915_sw_fence_await_reservation(struct i915_sw_fence *fence, int ret = 0, pending; debug_fence_assert(fence); + might_sleep_if(gfpflags_allow_blocking(gfp)); if (write) { struct dma_fence **shared; diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c index 791759f632e1..c74a20b80182 100644 --- a/drivers/gpu/drm/i915/i915_sysfs.c +++ b/drivers/gpu/drm/i915/i915_sysfs.c @@ -42,14 +42,30 @@ static inline struct drm_i915_private *kdev_minor_to_i915(struct device *kdev) static u32 calc_residency(struct drm_i915_private *dev_priv, i915_reg_t reg) { - return DIV_ROUND_CLOSEST_ULL(intel_rc6_residency_us(dev_priv, reg), - 1000); + u64 res; + + intel_runtime_pm_get(dev_priv); + res = intel_rc6_residency_us(dev_priv, reg); + intel_runtime_pm_put(dev_priv); + + return DIV_ROUND_CLOSEST_ULL(res, 1000); } static ssize_t show_rc6_mask(struct device *kdev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "%x\n", intel_rc6_enabled()); + struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev); + unsigned int mask; + + mask = 0; + if (HAS_RC6(dev_priv)) + mask |= BIT(0); + if (HAS_RC6p(dev_priv)) + mask |= BIT(1); + if (HAS_RC6pp(dev_priv)) + mask |= BIT(2); + + return snprintf(buf, PAGE_SIZE, "%x\n", mask); } static ssize_t @@ -252,14 +268,9 @@ static ssize_t gt_act_freq_mhz_show(struct device *kdev, freq = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS); ret = intel_gpu_freq(dev_priv, (freq >> 8) & 0xff); } else { - u32 rpstat = I915_READ(GEN6_RPSTAT1); - if (INTEL_GEN(dev_priv) >= 9) - ret = (rpstat & GEN9_CAGF_MASK) >> GEN9_CAGF_SHIFT; - else if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) - ret = (rpstat & HSW_CAGF_MASK) >> HSW_CAGF_SHIFT; - else - ret = (rpstat & GEN6_CAGF_MASK) >> GEN6_CAGF_SHIFT; - ret = intel_gpu_freq(dev_priv, ret); + ret = intel_gpu_freq(dev_priv, + intel_get_cagf(dev_priv, + I915_READ(GEN6_RPSTAT1))); } mutex_unlock(&dev_priv->pcu_lock); diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c index bf6d8d1eaabe..e0e7c48f45dc 100644 --- a/drivers/gpu/drm/i915/i915_vma.c +++ b/drivers/gpu/drm/i915/i915_vma.c @@ -142,6 +142,12 @@ vma_create(struct drm_i915_gem_object *obj, i915_gem_object_get_stride(obj)); GEM_BUG_ON(!is_power_of_2(vma->fence_alignment)); + /* + * We put the GGTT vma at the start of the vma-list, followed + * by the ppGGTT vma. This allows us to break early when + * iterating over only the GGTT vma for an object, see + * for_each_ggtt_vma() + */ vma->flags |= I915_VMA_GGTT; list_add(&vma->obj_link, &obj->vma_list); } else { @@ -305,7 +311,7 @@ void __iomem *i915_vma_pin_iomap(struct i915_vma *vma) ptr = vma->iomap; if (ptr == NULL) { - ptr = io_mapping_map_wc(&i915_vm_to_ggtt(vma->vm)->mappable, + ptr = io_mapping_map_wc(&i915_vm_to_ggtt(vma->vm)->iomap, vma->node.start, vma->node.size); if (ptr == NULL) { @@ -322,6 +328,7 @@ void __iomem *i915_vma_pin_iomap(struct i915_vma *vma) if (err) goto err_unpin; + i915_vma_set_ggtt_write(vma); return ptr; err_unpin: @@ -330,12 +337,24 @@ err: return IO_ERR_PTR(err); } +void i915_vma_flush_writes(struct i915_vma *vma) +{ + if (!i915_vma_has_ggtt_write(vma)) + return; + + i915_gem_flush_ggtt_writes(vma->vm->i915); + + i915_vma_unset_ggtt_write(vma); +} + void i915_vma_unpin_iomap(struct i915_vma *vma) { lockdep_assert_held(&vma->obj->base.dev->struct_mutex); GEM_BUG_ON(vma->iomap == NULL); + i915_vma_flush_writes(vma); + i915_vma_unpin_fence(vma); i915_vma_unpin(vma); } @@ -466,6 +485,7 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags) u64 start, end; int ret; + GEM_BUG_ON(i915_vma_is_closed(vma)); GEM_BUG_ON(vma->flags & (I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND)); GEM_BUG_ON(drm_mm_node_allocated(&vma->node)); @@ -678,7 +698,9 @@ static void i915_vma_destroy(struct i915_vma *vma) GEM_BUG_ON(i915_gem_active_isset(&vma->last_read[i])); GEM_BUG_ON(i915_gem_active_isset(&vma->last_fence)); + list_del(&vma->obj_link); list_del(&vma->vm_link); + if (!i915_vma_is_ggtt(vma)) i915_ppgtt_put(i915_vm_to_ppgtt(vma->vm)); @@ -690,7 +712,6 @@ void i915_vma_close(struct i915_vma *vma) GEM_BUG_ON(i915_vma_is_closed(vma)); vma->flags |= I915_VMA_CLOSED; - list_del(&vma->obj_link); rb_erase(&vma->obj_node, &vma->obj->vma_tree); if (!i915_vma_is_active(vma) && !i915_vma_is_pinned(vma)) @@ -790,6 +811,15 @@ int i915_vma_unbind(struct i915_vma *vma) GEM_BUG_ON(!i915_gem_object_has_pinned_pages(obj)); if (i915_vma_is_map_and_fenceable(vma)) { + /* + * Check that we have flushed all writes through the GGTT + * before the unbind, other due to non-strict nature of those + * indirect writes they may end up referencing the GGTT PTE + * after the unbind. + */ + i915_vma_flush_writes(vma); + GEM_BUG_ON(i915_vma_has_ggtt_write(vma)); + /* release the fence reg _after_ flushing */ ret = i915_vma_put_fence(vma); if (ret) diff --git a/drivers/gpu/drm/i915/i915_vma.h b/drivers/gpu/drm/i915/i915_vma.h index 1e2bc9b3c3ac..fd5b84904f7c 100644 --- a/drivers/gpu/drm/i915/i915_vma.h +++ b/drivers/gpu/drm/i915/i915_vma.h @@ -90,6 +90,7 @@ struct i915_vma { #define I915_VMA_CLOSED BIT(10) #define I915_VMA_USERFAULT_BIT 11 #define I915_VMA_USERFAULT BIT(I915_VMA_USERFAULT_BIT) +#define I915_VMA_GGTT_WRITE BIT(12) unsigned int active; struct i915_gem_active last_read[I915_NUM_ENGINES]; @@ -138,6 +139,24 @@ static inline bool i915_vma_is_ggtt(const struct i915_vma *vma) return vma->flags & I915_VMA_GGTT; } +static inline bool i915_vma_has_ggtt_write(const struct i915_vma *vma) +{ + return vma->flags & I915_VMA_GGTT_WRITE; +} + +static inline void i915_vma_set_ggtt_write(struct i915_vma *vma) +{ + GEM_BUG_ON(!i915_vma_is_ggtt(vma)); + vma->flags |= I915_VMA_GGTT_WRITE; +} + +static inline void i915_vma_unset_ggtt_write(struct i915_vma *vma) +{ + vma->flags &= ~I915_VMA_GGTT_WRITE; +} + +void i915_vma_flush_writes(struct i915_vma *vma); + static inline bool i915_vma_is_map_and_fenceable(const struct i915_vma *vma) { return vma->flags & I915_VMA_CAN_FENCE; @@ -389,5 +408,19 @@ i915_vma_unpin_fence(struct i915_vma *vma) __i915_vma_unpin_fence(vma); } -#endif +#define for_each_until(cond) if (cond) break; else + +/** + * for_each_ggtt_vma - Iterate over the GGTT VMA belonging to an object. + * @V: the #i915_vma iterator + * @OBJ: the #drm_i915_gem_object + * + * GGTT VMA are placed at the being of the object's vma_list, see + * vma_create(), so we can stop our walk as soon as we see a ppgtt VMA, + * or the list is empty ofc. + */ +#define for_each_ggtt_vma(V, OBJ) \ + list_for_each_entry(V, &(OBJ)->vma_list, obj_link) \ + for_each_until(!i915_vma_is_ggtt(V)) +#endif diff --git a/drivers/gpu/drm/i915/intel_breadcrumbs.c b/drivers/gpu/drm/i915/intel_breadcrumbs.c index 5ae2d276f7f3..58c624f982d9 100644 --- a/drivers/gpu/drm/i915/intel_breadcrumbs.c +++ b/drivers/gpu/drm/i915/intel_breadcrumbs.c @@ -27,6 +27,12 @@ #include "i915_drv.h" +#ifdef CONFIG_SMP +#define task_asleep(tsk) ((tsk)->state & TASK_NORMAL && !(tsk)->on_cpu) +#else +#define task_asleep(tsk) ((tsk)->state & TASK_NORMAL) +#endif + static unsigned int __intel_breadcrumbs_wakeup(struct intel_breadcrumbs *b) { struct intel_wait *wait; @@ -36,8 +42,20 @@ static unsigned int __intel_breadcrumbs_wakeup(struct intel_breadcrumbs *b) wait = b->irq_wait; if (wait) { + /* + * N.B. Since task_asleep() and ttwu are not atomic, the + * waiter may actually go to sleep after the check, causing + * us to suppress a valid wakeup. We prefer to reduce the + * number of false positive missed_breadcrumb() warnings + * at the expense of a few false negatives, as it it easy + * to trigger a false positive under heavy load. Enough + * signal should remain from genuine missed_breadcrumb() + * for us to detect in CI. + */ + bool was_asleep = task_asleep(wait->tsk); + result = ENGINE_WAKEUP_WAITER; - if (wake_up_process(wait->tsk)) + if (wake_up_process(wait->tsk) && was_asleep) result |= ENGINE_WAKEUP_ASLEEP; } @@ -64,20 +82,21 @@ static unsigned long wait_timeout(void) static noinline void missed_breadcrumb(struct intel_engine_cs *engine) { - DRM_DEBUG_DRIVER("%s missed breadcrumb at %pS, irq posted? %s, current seqno=%x, last=%x\n", - engine->name, __builtin_return_address(0), - yesno(test_bit(ENGINE_IRQ_BREADCRUMB, - &engine->irq_posted)), - intel_engine_get_seqno(engine), - intel_engine_last_submit(engine)); + if (drm_debug & DRM_UT_DRIVER) { + struct drm_printer p = drm_debug_printer(__func__); + + intel_engine_dump(engine, &p, + "%s missed breadcrumb at %pS\n", + engine->name, __builtin_return_address(0)); + } set_bit(engine->id, &engine->i915->gpu_error.missed_irq_rings); } static void intel_breadcrumbs_hangcheck(struct timer_list *t) { - struct intel_engine_cs *engine = from_timer(engine, t, - breadcrumbs.hangcheck); + struct intel_engine_cs *engine = + from_timer(engine, t, breadcrumbs.hangcheck); struct intel_breadcrumbs *b = &engine->breadcrumbs; if (!b->irq_armed) @@ -103,7 +122,7 @@ static void intel_breadcrumbs_hangcheck(struct timer_list *t) */ if (intel_engine_wakeup(engine) & ENGINE_WAKEUP_ASLEEP) { missed_breadcrumb(engine); - mod_timer(&engine->breadcrumbs.fake_irq, jiffies + 1); + mod_timer(&b->fake_irq, jiffies + 1); } else { mod_timer(&b->hangcheck, wait_timeout()); } @@ -213,32 +232,42 @@ void intel_engine_unpin_breadcrumbs_irq(struct intel_engine_cs *engine) void intel_engine_disarm_breadcrumbs(struct intel_engine_cs *engine) { struct intel_breadcrumbs *b = &engine->breadcrumbs; - struct intel_wait *wait, *n, *first; + struct intel_wait *wait, *n; if (!b->irq_armed) - return; + goto wakeup_signaler; - /* We only disarm the irq when we are idle (all requests completed), + /* + * We only disarm the irq when we are idle (all requests completed), * so if the bottom-half remains asleep, it missed the request * completion. */ + if (intel_engine_wakeup(engine) & ENGINE_WAKEUP_ASLEEP) + missed_breadcrumb(engine); spin_lock_irq(&b->rb_lock); spin_lock(&b->irq_lock); - first = fetch_and_zero(&b->irq_wait); + b->irq_wait = NULL; if (b->irq_armed) __intel_engine_disarm_breadcrumbs(engine); spin_unlock(&b->irq_lock); rbtree_postorder_for_each_entry_safe(wait, n, &b->waiters, node) { RB_CLEAR_NODE(&wait->node); - if (wake_up_process(wait->tsk) && wait == first) - missed_breadcrumb(engine); + wake_up_process(wait->tsk); } b->waiters = RB_ROOT; spin_unlock_irq(&b->rb_lock); + + /* + * The signaling thread may be asleep holding a reference to a request, + * that had its signaling cancelled prior to being preempted. We need + * to kick the signaler, just in case, to release any such reference. + */ +wakeup_signaler: + wake_up_process(b->signaler); } static bool use_fake_irq(const struct intel_breadcrumbs *b) @@ -683,23 +712,15 @@ static int intel_breadcrumbs_signaler(void *arg) } if (unlikely(do_schedule)) { - DEFINE_WAIT(exec); - if (kthread_should_park()) kthread_parkme(); - if (kthread_should_stop()) { - GEM_BUG_ON(request); + if (unlikely(kthread_should_stop())) { + i915_gem_request_put(request); break; } - if (request) - add_wait_queue(&request->execute, &exec); - schedule(); - - if (request) - remove_wait_queue(&request->execute, &exec); } i915_gem_request_put(request); } while (1); diff --git a/drivers/gpu/drm/i915/intel_cdclk.c b/drivers/gpu/drm/i915/intel_cdclk.c index e8884c2ade98..9c5ceb98d48f 100644 --- a/drivers/gpu/drm/i915/intel_cdclk.c +++ b/drivers/gpu/drm/i915/intel_cdclk.c @@ -1896,7 +1896,7 @@ int intel_crtc_compute_min_cdclk(const struct intel_crtc_state *crtc_state) min_cdclk = intel_pixel_rate_to_cdclk(dev_priv, crtc_state->pixel_rate); /* pixel rate mustn't exceed 95% of cdclk with IPS on BDW */ - if (IS_BROADWELL(dev_priv) && crtc_state->ips_enabled) + if (IS_BROADWELL(dev_priv) && hsw_crtc_state_ips_capable(crtc_state)) min_cdclk = DIV_ROUND_UP(min_cdclk * 100, 95); /* BSpec says "Do not use DisplayPort with CDCLK less than 432 MHz, diff --git a/drivers/gpu/drm/i915/intel_csr.c b/drivers/gpu/drm/i915/intel_csr.c index 77d8b3d483ca..7fe4aac0facc 100644 --- a/drivers/gpu/drm/i915/intel_csr.c +++ b/drivers/gpu/drm/i915/intel_csr.c @@ -40,13 +40,13 @@ #define I915_CSR_CNL "i915/cnl_dmc_ver1_06.bin" #define CNL_CSR_VERSION_REQUIRED CSR_VERSION(1, 6) -#define I915_CSR_KBL "i915/kbl_dmc_ver1_01.bin" +#define I915_CSR_KBL "i915/kbl_dmc_ver1_04.bin" MODULE_FIRMWARE(I915_CSR_KBL); -#define KBL_CSR_VERSION_REQUIRED CSR_VERSION(1, 1) +#define KBL_CSR_VERSION_REQUIRED CSR_VERSION(1, 4) -#define I915_CSR_SKL "i915/skl_dmc_ver1_26.bin" +#define I915_CSR_SKL "i915/skl_dmc_ver1_27.bin" MODULE_FIRMWARE(I915_CSR_SKL); -#define SKL_CSR_VERSION_REQUIRED CSR_VERSION(1, 26) +#define SKL_CSR_VERSION_REQUIRED CSR_VERSION(1, 27) #define I915_CSR_BXT "i915/bxt_dmc_ver1_07.bin" MODULE_FIRMWARE(I915_CSR_BXT); diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index eff3b51872eb..369f780588fb 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -2098,6 +2098,7 @@ static void intel_ddi_clk_select(struct intel_encoder *encoder, if (IS_CANNONLAKE(dev_priv)) { /* Configure DPCLKA_CFGCR0 to map the DPLL to the DDI. */ val = I915_READ(DPCLKA_CFGCR0); + val &= ~DPCLKA_CFGCR0_DDI_CLK_SEL_MASK(port); val |= DPCLKA_CFGCR0_DDI_CLK_SEL(pll->id, port); I915_WRITE(DPCLKA_CFGCR0, val); @@ -2513,17 +2514,17 @@ void intel_ddi_prepare_link_retrain(struct intel_dp *intel_dp) udelay(600); } -bool intel_ddi_is_audio_enabled(struct drm_i915_private *dev_priv, - struct intel_crtc *intel_crtc) +static bool intel_ddi_is_audio_enabled(struct drm_i915_private *dev_priv, + enum transcoder cpu_transcoder) { - u32 temp; + if (cpu_transcoder == TRANSCODER_EDP) + return false; - if (intel_display_power_is_enabled(dev_priv, POWER_DOMAIN_AUDIO)) { - temp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD); - if (temp & AUDIO_OUTPUT_ENABLE(intel_crtc->pipe)) - return true; - } - return false; + if (!intel_display_power_is_enabled(dev_priv, POWER_DOMAIN_AUDIO)) + return false; + + return I915_READ(HSW_AUD_PIN_ELD_CP_VLD) & + AUDIO_OUTPUT_ENABLE(cpu_transcoder); } void intel_ddi_compute_min_voltage_level(struct drm_i915_private *dev_priv, @@ -2616,7 +2617,7 @@ void intel_ddi_get_config(struct intel_encoder *encoder, } pipe_config->has_audio = - intel_ddi_is_audio_enabled(dev_priv, intel_crtc); + intel_ddi_is_audio_enabled(dev_priv, cpu_transcoder); if (encoder->type == INTEL_OUTPUT_EDP && dev_priv->vbt.edp.bpp && pipe_config->pipe_bpp > dev_priv->vbt.edp.bpp) { diff --git a/drivers/gpu/drm/i915/intel_device_info.c b/drivers/gpu/drm/i915/intel_device_info.c index 02f8bf101ccd..f478be3ae0ba 100644 --- a/drivers/gpu/drm/i915/intel_device_info.c +++ b/drivers/gpu/drm/i915/intel_device_info.c @@ -403,20 +403,20 @@ static u32 read_timestamp_frequency(struct drm_i915_private *dev_priv) freq = f24_mhz; break; } - } - /* Now figure out how the command stream's timestamp register - * increments from this frequency (it might increment only - * every few clock cycle). - */ - freq >>= 3 - ((rpm_config_reg & - GEN10_RPM_CONFIG0_CTC_SHIFT_PARAMETER_MASK) >> - GEN10_RPM_CONFIG0_CTC_SHIFT_PARAMETER_SHIFT); + /* Now figure out how the command stream's timestamp + * register increments from this frequency (it might + * increment only every few clock cycle). + */ + freq >>= 3 - ((rpm_config_reg & + GEN10_RPM_CONFIG0_CTC_SHIFT_PARAMETER_MASK) >> + GEN10_RPM_CONFIG0_CTC_SHIFT_PARAMETER_SHIFT); + } return freq; } - DRM_ERROR("Unknown gen, unable to compute command stream timestamp frequency\n"); + MISSING_CASE("Unknown gen, unable to read command streamer timestamp frequency\n"); return 0; } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2007c69468b9..efa6c6d19664 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -489,7 +489,7 @@ static const struct intel_limit intel_limits_bxt = { }; static bool -needs_modeset(struct drm_crtc_state *state) +needs_modeset(const struct drm_crtc_state *state) { return drm_atomic_crtc_needs_modeset(state); } @@ -998,7 +998,8 @@ enum transcoder intel_pipe_to_cpu_transcoder(struct drm_i915_private *dev_priv, return crtc->config->cpu_transcoder; } -static bool pipe_dsl_stopped(struct drm_i915_private *dev_priv, enum pipe pipe) +static bool pipe_scanline_is_moving(struct drm_i915_private *dev_priv, + enum pipe pipe) { i915_reg_t reg = PIPEDSL(pipe); u32 line1, line2; @@ -1013,32 +1014,38 @@ static bool pipe_dsl_stopped(struct drm_i915_private *dev_priv, enum pipe pipe) msleep(5); line2 = I915_READ(reg) & line_mask; - return line1 == line2; + return line1 != line2; } -/* - * intel_wait_for_pipe_off - wait for pipe to turn off - * @crtc: crtc whose pipe to wait for - * - * After disabling a pipe, we can't wait for vblank in the usual way, - * spinning on the vblank interrupt status bit, since we won't actually - * see an interrupt when the pipe is disabled. - * - * On Gen4 and above: - * wait for the pipe register state bit to turn off - * - * Otherwise: - * wait for the display line value to settle (it usually - * ends up stopping at the start of the next frame). - * - */ -static void intel_wait_for_pipe_off(struct intel_crtc *crtc) +static void wait_for_pipe_scanline_moving(struct intel_crtc *crtc, bool state) { struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - enum transcoder cpu_transcoder = crtc->config->cpu_transcoder; enum pipe pipe = crtc->pipe; + /* Wait for the display line to settle/start moving */ + if (wait_for(pipe_scanline_is_moving(dev_priv, pipe) == state, 100)) + DRM_ERROR("pipe %c scanline %s wait timed out\n", + pipe_name(pipe), onoff(state)); +} + +static void intel_wait_for_pipe_scanline_stopped(struct intel_crtc *crtc) +{ + wait_for_pipe_scanline_moving(crtc, false); +} + +static void intel_wait_for_pipe_scanline_moving(struct intel_crtc *crtc) +{ + wait_for_pipe_scanline_moving(crtc, true); +} + +static void +intel_wait_for_pipe_off(const struct intel_crtc_state *old_crtc_state) +{ + struct intel_crtc *crtc = to_intel_crtc(old_crtc_state->base.crtc); + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + if (INTEL_GEN(dev_priv) >= 4) { + enum transcoder cpu_transcoder = old_crtc_state->cpu_transcoder; i915_reg_t reg = PIPECONF(cpu_transcoder); /* Wait for the Pipe State to go off */ @@ -1047,9 +1054,7 @@ static void intel_wait_for_pipe_off(struct intel_crtc *crtc) 100)) WARN(1, "pipe_off wait timed out\n"); } else { - /* Wait for the display line to settle */ - if (wait_for(pipe_dsl_stopped(dev_priv, pipe), 100)) - WARN(1, "pipe_off wait timed out\n"); + intel_wait_for_pipe_scanline_stopped(crtc); } } @@ -1190,23 +1195,6 @@ void assert_panel_unlocked(struct drm_i915_private *dev_priv, enum pipe pipe) pipe_name(pipe)); } -static void assert_cursor(struct drm_i915_private *dev_priv, - enum pipe pipe, bool state) -{ - bool cur_state; - - if (IS_I845G(dev_priv) || IS_I865G(dev_priv)) - cur_state = I915_READ(CURCNTR(PIPE_A)) & CURSOR_ENABLE; - else - cur_state = I915_READ(CURCNTR(pipe)) & CURSOR_MODE; - - I915_STATE_WARN(cur_state != state, - "cursor on pipe %c assertion failure (expected %s, current %s)\n", - pipe_name(pipe), onoff(state), onoff(cur_state)); -} -#define assert_cursor_enabled(d, p) assert_cursor(d, p, true) -#define assert_cursor_disabled(d, p) assert_cursor(d, p, false) - void assert_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, bool state) { @@ -1234,77 +1222,25 @@ void assert_pipe(struct drm_i915_private *dev_priv, pipe_name(pipe), onoff(state), onoff(cur_state)); } -static void assert_plane(struct drm_i915_private *dev_priv, - enum plane plane, bool state) +static void assert_plane(struct intel_plane *plane, bool state) { - u32 val; - bool cur_state; + bool cur_state = plane->get_hw_state(plane); - val = I915_READ(DSPCNTR(plane)); - cur_state = !!(val & DISPLAY_PLANE_ENABLE); I915_STATE_WARN(cur_state != state, - "plane %c assertion failure (expected %s, current %s)\n", - plane_name(plane), onoff(state), onoff(cur_state)); + "%s assertion failure (expected %s, current %s)\n", + plane->base.name, onoff(state), onoff(cur_state)); } -#define assert_plane_enabled(d, p) assert_plane(d, p, true) -#define assert_plane_disabled(d, p) assert_plane(d, p, false) - -static void assert_planes_disabled(struct drm_i915_private *dev_priv, - enum pipe pipe) -{ - int i; - - /* Primary planes are fixed to pipes on gen4+ */ - if (INTEL_GEN(dev_priv) >= 4) { - u32 val = I915_READ(DSPCNTR(pipe)); - I915_STATE_WARN(val & DISPLAY_PLANE_ENABLE, - "plane %c assertion failure, should be disabled but not\n", - plane_name(pipe)); - return; - } - - /* Need to check both planes against the pipe */ - for_each_pipe(dev_priv, i) { - u32 val = I915_READ(DSPCNTR(i)); - enum pipe cur_pipe = (val & DISPPLANE_SEL_PIPE_MASK) >> - DISPPLANE_SEL_PIPE_SHIFT; - I915_STATE_WARN((val & DISPLAY_PLANE_ENABLE) && pipe == cur_pipe, - "plane %c assertion failure, should be off on pipe %c but is still active\n", - plane_name(i), pipe_name(pipe)); - } -} +#define assert_plane_enabled(p) assert_plane(p, true) +#define assert_plane_disabled(p) assert_plane(p, false) -static void assert_sprites_disabled(struct drm_i915_private *dev_priv, - enum pipe pipe) +static void assert_planes_disabled(struct intel_crtc *crtc) { - int sprite; + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + struct intel_plane *plane; - if (INTEL_GEN(dev_priv) >= 9) { - for_each_sprite(dev_priv, pipe, sprite) { - u32 val = I915_READ(PLANE_CTL(pipe, sprite)); - I915_STATE_WARN(val & PLANE_CTL_ENABLE, - "plane %d assertion failure, should be off on pipe %c but is still active\n", - sprite, pipe_name(pipe)); - } - } else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { - for_each_sprite(dev_priv, pipe, sprite) { - u32 val = I915_READ(SPCNTR(pipe, PLANE_SPRITE0 + sprite)); - I915_STATE_WARN(val & SP_ENABLE, - "sprite %c assertion failure, should be off on pipe %c but is still active\n", - sprite_name(pipe, sprite), pipe_name(pipe)); - } - } else if (INTEL_GEN(dev_priv) >= 7) { - u32 val = I915_READ(SPRCTL(pipe)); - I915_STATE_WARN(val & SPRITE_ENABLE, - "sprite %c assertion failure, should be off on pipe %c but is still active\n", - plane_name(pipe), pipe_name(pipe)); - } else if (INTEL_GEN(dev_priv) >= 5 || IS_G4X(dev_priv)) { - u32 val = I915_READ(DVSCNTR(pipe)); - I915_STATE_WARN(val & DVS_ENABLE, - "sprite %c assertion failure, should be off on pipe %c but is still active\n", - plane_name(pipe), pipe_name(pipe)); - } + for_each_intel_plane_on_crtc(&dev_priv->drm, crtc, plane) + assert_plane_disabled(plane); } static void assert_vblank_disabled(struct drm_crtc *crtc) @@ -1878,27 +1814,18 @@ enum pipe intel_crtc_pch_transcoder(struct intel_crtc *crtc) return crtc->pipe; } -/** - * intel_enable_pipe - enable a pipe, asserting requirements - * @crtc: crtc responsible for the pipe - * - * Enable @crtc's pipe, making sure that various hardware specific requirements - * are met, if applicable, e.g. PLL enabled, LVDS pairs enabled, etc. - */ -static void intel_enable_pipe(struct intel_crtc *crtc) +static void intel_enable_pipe(const struct intel_crtc_state *new_crtc_state) { - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct intel_crtc *crtc = to_intel_crtc(new_crtc_state->base.crtc); + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + enum transcoder cpu_transcoder = new_crtc_state->cpu_transcoder; enum pipe pipe = crtc->pipe; - enum transcoder cpu_transcoder = crtc->config->cpu_transcoder; i915_reg_t reg; u32 val; DRM_DEBUG_KMS("enabling pipe %c\n", pipe_name(pipe)); - assert_planes_disabled(dev_priv, pipe); - assert_cursor_disabled(dev_priv, pipe); - assert_sprites_disabled(dev_priv, pipe); + assert_planes_disabled(crtc); /* * A pipe without a PLL won't actually be able to drive bits from @@ -1906,12 +1833,12 @@ static void intel_enable_pipe(struct intel_crtc *crtc) * need the check. */ if (HAS_GMCH_DISPLAY(dev_priv)) { - if (intel_crtc_has_type(crtc->config, INTEL_OUTPUT_DSI)) + if (intel_crtc_has_type(new_crtc_state, INTEL_OUTPUT_DSI)) assert_dsi_pll_enabled(dev_priv); else assert_pll_enabled(dev_priv, pipe); } else { - if (crtc->config->has_pch_encoder) { + if (new_crtc_state->has_pch_encoder) { /* if driving the PCH, we need FDI enabled */ assert_fdi_rx_pll_enabled(dev_priv, intel_crtc_pch_transcoder(crtc)); @@ -1933,31 +1860,21 @@ static void intel_enable_pipe(struct intel_crtc *crtc) POSTING_READ(reg); /* - * Until the pipe starts DSL will read as 0, which would cause - * an apparent vblank timestamp jump, which messes up also the - * frame count when it's derived from the timestamps. So let's - * wait for the pipe to start properly before we call - * drm_crtc_vblank_on() + * Until the pipe starts PIPEDSL reads will return a stale value, + * which causes an apparent vblank timestamp jump when PIPEDSL + * resets to its proper value. That also messes up the frame count + * when it's derived from the timestamps. So let's wait for the + * pipe to start properly before we call drm_crtc_vblank_on() */ - if (dev->max_vblank_count == 0 && - wait_for(intel_get_crtc_scanline(crtc) != crtc->scanline_offset, 50)) - DRM_ERROR("pipe %c didn't start\n", pipe_name(pipe)); + if (dev_priv->drm.max_vblank_count == 0) + intel_wait_for_pipe_scanline_moving(crtc); } -/** - * intel_disable_pipe - disable a pipe, asserting requirements - * @crtc: crtc whose pipes is to be disabled - * - * Disable the pipe of @crtc, making sure that various hardware - * specific requirements are met, if applicable, e.g. plane - * disabled, panel fitter off, etc. - * - * Will wait until the pipe has shut down before returning. - */ -static void intel_disable_pipe(struct intel_crtc *crtc) +static void intel_disable_pipe(const struct intel_crtc_state *old_crtc_state) { + struct intel_crtc *crtc = to_intel_crtc(old_crtc_state->base.crtc); struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - enum transcoder cpu_transcoder = crtc->config->cpu_transcoder; + enum transcoder cpu_transcoder = old_crtc_state->cpu_transcoder; enum pipe pipe = crtc->pipe; i915_reg_t reg; u32 val; @@ -1968,9 +1885,7 @@ static void intel_disable_pipe(struct intel_crtc *crtc) * Make sure planes won't keep trying to pump pixels to us, * or we might hang the display. */ - assert_planes_disabled(dev_priv, pipe); - assert_cursor_disabled(dev_priv, pipe); - assert_sprites_disabled(dev_priv, pipe); + assert_planes_disabled(crtc); reg = PIPECONF(cpu_transcoder); val = I915_READ(reg); @@ -1981,7 +1896,7 @@ static void intel_disable_pipe(struct intel_crtc *crtc) * Double wide has implications for planes * so best keep it disabled when not needed. */ - if (crtc->config->double_wide) + if (old_crtc_state->double_wide) val &= ~PIPECONF_DOUBLE_WIDE; /* Don't disable pipe or pipe PLLs if needed */ @@ -1990,7 +1905,7 @@ static void intel_disable_pipe(struct intel_crtc *crtc) I915_WRITE(reg, val); if ((val & PIPECONF_ENABLE) == 0) - intel_wait_for_pipe_off(crtc); + intel_wait_for_pipe_off(old_crtc_state); } static unsigned int intel_tile_size(const struct drm_i915_private *dev_priv) @@ -2724,7 +2639,6 @@ intel_alloc_initial_plane_obj(struct intel_crtc *crtc, { struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); - struct i915_ggtt *ggtt = &dev_priv->ggtt; struct drm_i915_gem_object *obj = NULL; struct drm_mode_fb_cmd2 mode_cmd = { 0 }; struct drm_framebuffer *fb = &plane_config->fb->base; @@ -2740,7 +2654,7 @@ intel_alloc_initial_plane_obj(struct intel_crtc *crtc, /* If the FB is too big, just don't use it since fbdev is not very * important and we should probably use that space with FBC or other * features. */ - if (size_aligned * 2 > ggtt->stolen_usable_size) + if (size_aligned * 2 > dev_priv->stolen_usable_size) return false; mutex_lock(&dev->struct_mutex); @@ -2799,6 +2713,23 @@ intel_set_plane_visible(struct intel_crtc_state *crtc_state, crtc_state->active_planes); } +static void intel_plane_disable_noatomic(struct intel_crtc *crtc, + struct intel_plane *plane) +{ + struct intel_crtc_state *crtc_state = + to_intel_crtc_state(crtc->base.state); + struct intel_plane_state *plane_state = + to_intel_plane_state(plane->base.state); + + intel_set_plane_visible(crtc_state, plane_state, false); + + if (plane->id == PLANE_PRIMARY) + intel_pre_disable_primary_noatomic(&crtc->base); + + trace_intel_disable_plane(&plane->base, crtc); + plane->disable_plane(plane, crtc); +} + static void intel_find_initial_plane_obj(struct intel_crtc *intel_crtc, struct intel_initial_plane_config *plane_config) @@ -2856,12 +2787,7 @@ intel_find_initial_plane_obj(struct intel_crtc *intel_crtc, * simplest solution is to just disable the primary plane now and * pretend the BIOS never had it enabled. */ - intel_set_plane_visible(to_intel_crtc_state(crtc_state), - to_intel_plane_state(plane_state), - false); - intel_pre_disable_primary_noatomic(&intel_crtc->base); - trace_intel_disable_plane(primary, intel_crtc); - intel_plane->disable_plane(intel_plane, intel_crtc); + intel_plane_disable_noatomic(intel_crtc, intel_plane); return; @@ -3281,16 +3207,16 @@ int i9xx_check_plane_surface(struct intel_plane_state *plane_state) return 0; } -static void i9xx_update_primary_plane(struct intel_plane *primary, - const struct intel_crtc_state *crtc_state, - const struct intel_plane_state *plane_state) +static void i9xx_update_plane(struct intel_plane *plane, + const struct intel_crtc_state *crtc_state, + const struct intel_plane_state *plane_state) { - struct drm_i915_private *dev_priv = to_i915(primary->base.dev); + struct drm_i915_private *dev_priv = to_i915(plane->base.dev); const struct drm_framebuffer *fb = plane_state->base.fb; - enum plane plane = primary->plane; + enum i9xx_plane_id i9xx_plane = plane->i9xx_plane; u32 linear_offset; u32 dspcntr = plane_state->ctl; - i915_reg_t reg = DSPCNTR(plane); + i915_reg_t reg = DSPCNTR(i9xx_plane); int x = plane_state->main.x; int y = plane_state->main.y; unsigned long irqflags; @@ -3309,34 +3235,34 @@ static void i9xx_update_primary_plane(struct intel_plane *primary, /* pipesrc and dspsize control the size that is scaled from, * which should always be the user's requested size. */ - I915_WRITE_FW(DSPSIZE(plane), + I915_WRITE_FW(DSPSIZE(i9xx_plane), ((crtc_state->pipe_src_h - 1) << 16) | (crtc_state->pipe_src_w - 1)); - I915_WRITE_FW(DSPPOS(plane), 0); - } else if (IS_CHERRYVIEW(dev_priv) && plane == PLANE_B) { - I915_WRITE_FW(PRIMSIZE(plane), + I915_WRITE_FW(DSPPOS(i9xx_plane), 0); + } else if (IS_CHERRYVIEW(dev_priv) && i9xx_plane == PLANE_B) { + I915_WRITE_FW(PRIMSIZE(i9xx_plane), ((crtc_state->pipe_src_h - 1) << 16) | (crtc_state->pipe_src_w - 1)); - I915_WRITE_FW(PRIMPOS(plane), 0); - I915_WRITE_FW(PRIMCNSTALPHA(plane), 0); + I915_WRITE_FW(PRIMPOS(i9xx_plane), 0); + I915_WRITE_FW(PRIMCNSTALPHA(i9xx_plane), 0); } I915_WRITE_FW(reg, dspcntr); - I915_WRITE_FW(DSPSTRIDE(plane), fb->pitches[0]); + I915_WRITE_FW(DSPSTRIDE(i9xx_plane), fb->pitches[0]); if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) { - I915_WRITE_FW(DSPSURF(plane), + I915_WRITE_FW(DSPSURF(i9xx_plane), intel_plane_ggtt_offset(plane_state) + dspaddr_offset); - I915_WRITE_FW(DSPOFFSET(plane), (y << 16) | x); + I915_WRITE_FW(DSPOFFSET(i9xx_plane), (y << 16) | x); } else if (INTEL_GEN(dev_priv) >= 4) { - I915_WRITE_FW(DSPSURF(plane), + I915_WRITE_FW(DSPSURF(i9xx_plane), intel_plane_ggtt_offset(plane_state) + dspaddr_offset); - I915_WRITE_FW(DSPTILEOFF(plane), (y << 16) | x); - I915_WRITE_FW(DSPLINOFF(plane), linear_offset); + I915_WRITE_FW(DSPTILEOFF(i9xx_plane), (y << 16) | x); + I915_WRITE_FW(DSPLINOFF(i9xx_plane), linear_offset); } else { - I915_WRITE_FW(DSPADDR(plane), + I915_WRITE_FW(DSPADDR(i9xx_plane), intel_plane_ggtt_offset(plane_state) + dspaddr_offset); } @@ -3345,25 +3271,49 @@ static void i9xx_update_primary_plane(struct intel_plane *primary, spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } -static void i9xx_disable_primary_plane(struct intel_plane *primary, - struct intel_crtc *crtc) +static void i9xx_disable_plane(struct intel_plane *plane, + struct intel_crtc *crtc) { - struct drm_i915_private *dev_priv = to_i915(primary->base.dev); - enum plane plane = primary->plane; + struct drm_i915_private *dev_priv = to_i915(plane->base.dev); + enum i9xx_plane_id i9xx_plane = plane->i9xx_plane; unsigned long irqflags; spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); - I915_WRITE_FW(DSPCNTR(plane), 0); - if (INTEL_INFO(dev_priv)->gen >= 4) - I915_WRITE_FW(DSPSURF(plane), 0); + I915_WRITE_FW(DSPCNTR(i9xx_plane), 0); + if (INTEL_GEN(dev_priv) >= 4) + I915_WRITE_FW(DSPSURF(i9xx_plane), 0); else - I915_WRITE_FW(DSPADDR(plane), 0); - POSTING_READ_FW(DSPCNTR(plane)); + I915_WRITE_FW(DSPADDR(i9xx_plane), 0); + POSTING_READ_FW(DSPCNTR(i9xx_plane)); spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } +static bool i9xx_plane_get_hw_state(struct intel_plane *plane) +{ + struct drm_i915_private *dev_priv = to_i915(plane->base.dev); + enum intel_display_power_domain power_domain; + enum i9xx_plane_id i9xx_plane = plane->i9xx_plane; + enum pipe pipe = plane->pipe; + bool ret; + + /* + * Not 100% correct for planes that can move between pipes, + * but that's only the case for gen2-4 which don't have any + * display power wells. + */ + power_domain = POWER_DOMAIN_PIPE(pipe); + if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) + return false; + + ret = I915_READ(DSPCNTR(i9xx_plane)) & DISPLAY_PLANE_ENABLE; + + intel_display_power_put(dev_priv, power_domain); + + return ret; +} + static u32 intel_fb_stride_alignment(const struct drm_framebuffer *fb, int plane) { @@ -4870,7 +4820,7 @@ void hsw_enable_ips(const struct intel_crtc_state *crtc_state) struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); - if (!crtc->config->ips_enabled) + if (!crtc_state->ips_enabled) return; /* @@ -4878,8 +4828,8 @@ void hsw_enable_ips(const struct intel_crtc_state *crtc_state) * This function is called from post_plane_update, which is run after * a vblank wait. */ + WARN_ON(!(crtc_state->active_planes & ~BIT(PLANE_CURSOR))); - assert_plane_enabled(dev_priv, crtc->plane); if (IS_BROADWELL(dev_priv)) { mutex_lock(&dev_priv->pcu_lock); WARN_ON(sandybridge_pcode_write(dev_priv, DISPLAY_IPS_CONTROL, @@ -4913,7 +4863,6 @@ void hsw_disable_ips(const struct intel_crtc_state *crtc_state) if (!crtc_state->ips_enabled) return; - assert_plane_enabled(dev_priv, crtc->plane); if (IS_BROADWELL(dev_priv)) { mutex_lock(&dev_priv->pcu_lock); WARN_ON(sandybridge_pcode_write(dev_priv, DISPLAY_IPS_CONTROL, 0)); @@ -4967,14 +4916,6 @@ intel_post_enable_primary(struct drm_crtc *crtc, int pipe = 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 - * versa. - */ - hsw_enable_ips(new_crtc_state); - - /* * Gen2 reports pipe underruns whenever all planes are disabled. * So don't enable underrun reporting before at least some planes * are enabled. @@ -4989,10 +4930,9 @@ intel_post_enable_primary(struct drm_crtc *crtc, intel_check_pch_fifo_underruns(dev_priv); } -/* FIXME move all this to pre_plane_update() with proper state tracking */ +/* FIXME get rid of this and use pre_plane_update */ static void -intel_pre_disable_primary(struct drm_crtc *crtc, - const struct intel_crtc_state *old_crtc_state) +intel_pre_disable_primary_noatomic(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = to_i915(dev); @@ -5001,32 +4941,12 @@ intel_pre_disable_primary(struct drm_crtc *crtc, /* * Gen2 reports pipe underruns whenever all planes are disabled. - * So diasble underrun reporting before all the planes get disabled. - * FIXME: Need to fix the logic to work when we turn off all planes - * but leave the pipe running. + * So disable underrun reporting before all the planes get disabled. */ if (IS_GEN2(dev_priv)) intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, false); - /* - * 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 - * versa. - */ - hsw_disable_ips(old_crtc_state); -} - -/* FIXME get rid of this and use pre_plane_update */ -static void -intel_pre_disable_primary_noatomic(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - int pipe = intel_crtc->pipe; - - intel_pre_disable_primary(crtc, to_intel_crtc_state(crtc->state)); + hsw_disable_ips(to_intel_crtc_state(crtc->state)); /* * Vblank time updates from the shadow to live plane control register @@ -5042,6 +4962,38 @@ intel_pre_disable_primary_noatomic(struct drm_crtc *crtc) intel_wait_for_vblank(dev_priv, pipe); } +static bool hsw_pre_update_disable_ips(const struct intel_crtc_state *old_crtc_state, + const struct intel_crtc_state *new_crtc_state) +{ + if (!old_crtc_state->ips_enabled) + return false; + + if (needs_modeset(&new_crtc_state->base)) + return true; + + return !new_crtc_state->ips_enabled; +} + +static bool hsw_post_update_enable_ips(const struct intel_crtc_state *old_crtc_state, + const struct intel_crtc_state *new_crtc_state) +{ + if (!new_crtc_state->ips_enabled) + return false; + + if (needs_modeset(&new_crtc_state->base)) + return true; + + /* + * We can't read out IPS on broadwell, assume the worst and + * forcibly enable IPS on the first fastset. + */ + if (new_crtc_state->update_pipe && + old_crtc_state->base.adjusted_mode.private_flags & I915_MODE_FLAG_INHERITED) + return true; + + return !old_crtc_state->ips_enabled; +} + static void intel_post_plane_update(struct intel_crtc_state *old_crtc_state) { struct intel_crtc *crtc = to_intel_crtc(old_crtc_state->base.crtc); @@ -5058,6 +5010,9 @@ static void intel_post_plane_update(struct intel_crtc_state *old_crtc_state) if (pipe_config->update_wm_post && pipe_config->base.active) intel_update_watermarks(crtc); + if (hsw_post_update_enable_ips(old_crtc_state, pipe_config)) + hsw_enable_ips(pipe_config); + if (old_pri_state) { struct intel_plane_state *primary_state = intel_atomic_get_new_plane_state(to_intel_atomic_state(old_state), @@ -5088,6 +5043,9 @@ static void intel_pre_plane_update(struct intel_crtc_state *old_crtc_state, struct intel_atomic_state *old_intel_state = to_intel_atomic_state(old_state); + if (hsw_pre_update_disable_ips(old_crtc_state, pipe_config)) + hsw_disable_ips(old_crtc_state); + if (old_pri_state) { struct intel_plane_state *primary_state = intel_atomic_get_new_plane_state(old_intel_state, @@ -5096,10 +5054,13 @@ static void intel_pre_plane_update(struct intel_crtc_state *old_crtc_state, to_intel_plane_state(old_pri_state); intel_fbc_pre_update(crtc, pipe_config, primary_state); - - if (old_primary_state->base.visible && + /* + * Gen2 reports pipe underruns whenever all planes are disabled. + * So disable underrun reporting before all the planes get disabled. + */ + if (IS_GEN2(dev_priv) && old_primary_state->base.visible && (modeset || !primary_state->base.visible)) - intel_pre_disable_primary(&crtc->base, old_crtc_state); + intel_set_cpu_fifo_underrun_reporting(dev_priv, crtc->pipe, false); } /* @@ -5362,7 +5323,7 @@ static void ironlake_crtc_enable(struct intel_crtc_state *pipe_config, if (dev_priv->display.initial_watermarks != NULL) dev_priv->display.initial_watermarks(old_intel_state, intel_crtc->config); - intel_enable_pipe(intel_crtc); + intel_enable_pipe(pipe_config); if (intel_crtc->config->has_pch_encoder) ironlake_pch_enable(pipe_config); @@ -5481,7 +5442,7 @@ static void haswell_crtc_enable(struct intel_crtc_state *pipe_config, /* XXX: Do the pipe assertions at the right place for BXT DSI. */ if (!transcoder_is_dsi(cpu_transcoder)) - intel_enable_pipe(intel_crtc); + intel_enable_pipe(pipe_config); if (intel_crtc->config->has_pch_encoder) lpt_pch_enable(pipe_config); @@ -5547,7 +5508,7 @@ static void ironlake_crtc_disable(struct intel_crtc_state *old_crtc_state, drm_crtc_vblank_off(crtc); assert_vblank_disabled(crtc); - intel_disable_pipe(intel_crtc); + intel_disable_pipe(old_crtc_state); ironlake_pfit_disable(intel_crtc, false); @@ -5599,7 +5560,7 @@ static void haswell_crtc_disable(struct intel_crtc_state *old_crtc_state, /* XXX: Do the pipe assertions at the right place for BXT DSI. */ if (!transcoder_is_dsi(cpu_transcoder)) - intel_disable_pipe(intel_crtc); + intel_disable_pipe(old_crtc_state); if (intel_crtc_has_type(intel_crtc->config, INTEL_OUTPUT_DP_MST)) intel_ddi_set_vc_payload_alloc(intel_crtc->config, false); @@ -5777,7 +5738,7 @@ static void valleyview_crtc_enable(struct intel_crtc_state *pipe_config, dev_priv->display.initial_watermarks(old_intel_state, pipe_config); - intel_enable_pipe(intel_crtc); + intel_enable_pipe(pipe_config); assert_vblank_disabled(crtc); drm_crtc_vblank_on(crtc); @@ -5836,7 +5797,7 @@ static void i9xx_crtc_enable(struct intel_crtc_state *pipe_config, intel_crtc->config); else intel_update_watermarks(intel_crtc); - intel_enable_pipe(intel_crtc); + intel_enable_pipe(pipe_config); assert_vblank_disabled(crtc); drm_crtc_vblank_on(crtc); @@ -5880,7 +5841,7 @@ static void i9xx_crtc_disable(struct intel_crtc_state *old_crtc_state, drm_crtc_vblank_off(crtc); assert_vblank_disabled(crtc); - intel_disable_pipe(intel_crtc); + intel_disable_pipe(old_crtc_state); i9xx_pfit_disable(intel_crtc); @@ -5915,6 +5876,7 @@ static void intel_crtc_disable_noatomic(struct drm_crtc *crtc, struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct drm_i915_private *dev_priv = to_i915(crtc->dev); enum intel_display_power_domain domain; + struct intel_plane *plane; u64 domains; struct drm_atomic_state *state; struct intel_crtc_state *crtc_state; @@ -5923,11 +5885,12 @@ static void intel_crtc_disable_noatomic(struct drm_crtc *crtc, if (!intel_crtc->active) return; - if (crtc->primary->state->visible) { - intel_pre_disable_primary_noatomic(crtc); + for_each_intel_plane_on_crtc(&dev_priv->drm, intel_crtc, plane) { + const struct intel_plane_state *plane_state = + to_intel_plane_state(plane->base.state); - intel_crtc_disable_planes(crtc, 1 << drm_plane_index(crtc->primary)); - crtc->primary->state->visible = false; + if (plane_state->base.visible) + intel_plane_disable_noatomic(intel_crtc, plane); } state = drm_atomic_state_alloc(crtc->dev); @@ -6228,18 +6191,20 @@ retry: return ret; } -static bool pipe_config_supports_ips(struct drm_i915_private *dev_priv, - struct intel_crtc_state *pipe_config) +bool hsw_crtc_state_ips_capable(const struct intel_crtc_state *crtc_state) { - if (pipe_config->ips_force_disable) + struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc); + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + + /* IPS only exists on ULT machines and is tied to pipe A. */ + if (!hsw_crtc_supports_ips(crtc)) return false; - if (pipe_config->pipe_bpp > 24) + if (!i915_modparams.enable_ips) return false; - /* HSW can handle pixel rate up to cdclk? */ - if (IS_HASWELL(dev_priv)) - return true; + if (crtc_state->pipe_bpp > 24) + return false; /* * We compare against max which means we must take @@ -6248,19 +6213,36 @@ static bool pipe_config_supports_ips(struct drm_i915_private *dev_priv, * * Should measure whether using a lower cdclk w/o IPS */ - return pipe_config->pixel_rate <= - dev_priv->max_cdclk_freq * 95 / 100; + if (IS_BROADWELL(dev_priv) && + crtc_state->pixel_rate > dev_priv->max_cdclk_freq * 95 / 100) + return false; + + return true; } -static void hsw_compute_ips_config(struct intel_crtc *crtc, - struct intel_crtc_state *pipe_config) +static bool hsw_compute_ips_config(struct intel_crtc_state *crtc_state) { - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = + to_i915(crtc_state->base.crtc->dev); + struct intel_atomic_state *intel_state = + to_intel_atomic_state(crtc_state->base.state); - pipe_config->ips_enabled = i915_modparams.enable_ips && - hsw_crtc_supports_ips(crtc) && - pipe_config_supports_ips(dev_priv, pipe_config); + if (!hsw_crtc_state_ips_capable(crtc_state)) + return false; + + if (crtc_state->ips_force_disable) + return false; + + /* IPS should be fine as long as at least one plane is enabled. */ + if (!(crtc_state->active_planes & ~BIT(PLANE_CURSOR))) + return false; + + /* pixel rate mustn't exceed 95% of cdclk with IPS on BDW */ + if (IS_BROADWELL(dev_priv) && + crtc_state->pixel_rate > intel_state->cdclk.logical.cdclk * 95 / 100) + return false; + + return true; } static bool intel_crtc_supports_double_wide(const struct intel_crtc *crtc) @@ -6378,9 +6360,6 @@ static int intel_crtc_compute_config(struct intel_crtc *crtc, intel_crtc_compute_pixel_rate(pipe_config); - if (HAS_IPS(dev_priv)) - hsw_compute_ips_config(crtc, pipe_config); - if (pipe_config->has_pch_encoder) return ironlake_fdi_compute_config(crtc, pipe_config); @@ -7437,15 +7416,16 @@ i9xx_get_initial_plane_config(struct intel_crtc *crtc, { struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); + struct intel_plane *plane = to_intel_plane(crtc->base.primary); + enum i9xx_plane_id i9xx_plane = plane->i9xx_plane; + enum pipe pipe = crtc->pipe; u32 val, base, offset; - int pipe = crtc->pipe, plane = crtc->plane; int fourcc, pixel_format; unsigned int aligned_height; struct drm_framebuffer *fb; struct intel_framebuffer *intel_fb; - val = I915_READ(DSPCNTR(plane)); - if (!(val & DISPLAY_PLANE_ENABLE)) + if (!plane->get_hw_state(plane)) return; intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL); @@ -7458,6 +7438,8 @@ i9xx_get_initial_plane_config(struct intel_crtc *crtc, fb->dev = dev; + val = I915_READ(DSPCNTR(i9xx_plane)); + if (INTEL_GEN(dev_priv) >= 4) { if (val & DISPPLANE_TILED) { plane_config->tiling = I915_TILING_X; @@ -7469,14 +7451,17 @@ i9xx_get_initial_plane_config(struct intel_crtc *crtc, fourcc = i9xx_format_to_fourcc(pixel_format); fb->format = drm_format_info(fourcc); - if (INTEL_GEN(dev_priv) >= 4) { + if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) { + offset = I915_READ(DSPOFFSET(i9xx_plane)); + base = I915_READ(DSPSURF(i9xx_plane)) & 0xfffff000; + } else if (INTEL_GEN(dev_priv) >= 4) { if (plane_config->tiling) - offset = I915_READ(DSPTILEOFF(plane)); + offset = I915_READ(DSPTILEOFF(i9xx_plane)); else - offset = I915_READ(DSPLINOFF(plane)); - base = I915_READ(DSPSURF(plane)) & 0xfffff000; + offset = I915_READ(DSPLINOFF(i9xx_plane)); + base = I915_READ(DSPSURF(i9xx_plane)) & 0xfffff000; } else { - base = I915_READ(DSPADDR(plane)); + base = I915_READ(DSPADDR(i9xx_plane)); } plane_config->base = base; @@ -7484,15 +7469,15 @@ i9xx_get_initial_plane_config(struct intel_crtc *crtc, fb->width = ((val >> 16) & 0xfff) + 1; fb->height = ((val >> 0) & 0xfff) + 1; - val = I915_READ(DSPSTRIDE(pipe)); + val = I915_READ(DSPSTRIDE(i9xx_plane)); fb->pitches[0] = val & 0xffffffc0; aligned_height = intel_fb_align_height(fb, 0, fb->height); plane_config->size = fb->pitches[0] * aligned_height; - DRM_DEBUG_KMS("pipe/plane %c/%d with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n", - pipe_name(pipe), plane, fb->width, fb->height, + DRM_DEBUG_KMS("%s/%s with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n", + crtc->base.name, plane->base.name, fb->width, fb->height, fb->format->cpp[0] * 8, base, fb->pitches[0], plane_config->size); @@ -8461,13 +8446,18 @@ skylake_get_initial_plane_config(struct intel_crtc *crtc, { struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); + struct intel_plane *plane = to_intel_plane(crtc->base.primary); + enum plane_id plane_id = plane->id; + enum pipe pipe = crtc->pipe; u32 val, base, offset, stride_mult, tiling, alpha; - int pipe = crtc->pipe; int fourcc, pixel_format; unsigned int aligned_height; struct drm_framebuffer *fb; struct intel_framebuffer *intel_fb; + if (!plane->get_hw_state(plane)) + return; + intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL); if (!intel_fb) { DRM_DEBUG_KMS("failed to alloc fb\n"); @@ -8478,14 +8468,12 @@ skylake_get_initial_plane_config(struct intel_crtc *crtc, fb->dev = dev; - val = I915_READ(PLANE_CTL(pipe, 0)); - if (!(val & PLANE_CTL_ENABLE)) - goto error; + val = I915_READ(PLANE_CTL(pipe, plane_id)); pixel_format = val & PLANE_CTL_FORMAT_MASK; if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv)) { - alpha = I915_READ(PLANE_COLOR_CTL(pipe, 0)); + alpha = I915_READ(PLANE_COLOR_CTL(pipe, plane_id)); alpha &= PLANE_COLOR_ALPHA_MASK; } else { alpha = val & PLANE_CTL_ALPHA_MASK; @@ -8521,16 +8509,16 @@ skylake_get_initial_plane_config(struct intel_crtc *crtc, goto error; } - base = I915_READ(PLANE_SURF(pipe, 0)) & 0xfffff000; + base = I915_READ(PLANE_SURF(pipe, plane_id)) & 0xfffff000; plane_config->base = base; - offset = I915_READ(PLANE_OFFSET(pipe, 0)); + offset = I915_READ(PLANE_OFFSET(pipe, plane_id)); - val = I915_READ(PLANE_SIZE(pipe, 0)); + val = I915_READ(PLANE_SIZE(pipe, plane_id)); fb->height = ((val >> 16) & 0xfff) + 1; fb->width = ((val >> 0) & 0x1fff) + 1; - val = I915_READ(PLANE_STRIDE(pipe, 0)); + val = I915_READ(PLANE_STRIDE(pipe, plane_id)); stride_mult = intel_fb_stride_alignment(fb, 0); fb->pitches[0] = (val & 0x3ff) * stride_mult; @@ -8538,8 +8526,8 @@ skylake_get_initial_plane_config(struct intel_crtc *crtc, plane_config->size = fb->pitches[0] * aligned_height; - DRM_DEBUG_KMS("pipe %c with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n", - pipe_name(pipe), fb->width, fb->height, + DRM_DEBUG_KMS("%s/%s with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n", + crtc->base.name, plane->base.name, fb->width, fb->height, fb->format->cpp[0] * 8, base, fb->pitches[0], plane_config->size); @@ -8574,74 +8562,6 @@ static void ironlake_get_pfit_config(struct intel_crtc *crtc, } } -static void -ironlake_get_initial_plane_config(struct intel_crtc *crtc, - struct intel_initial_plane_config *plane_config) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - u32 val, base, offset; - int pipe = crtc->pipe; - int fourcc, pixel_format; - unsigned int aligned_height; - struct drm_framebuffer *fb; - struct intel_framebuffer *intel_fb; - - val = I915_READ(DSPCNTR(pipe)); - if (!(val & DISPLAY_PLANE_ENABLE)) - return; - - intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL); - if (!intel_fb) { - DRM_DEBUG_KMS("failed to alloc fb\n"); - return; - } - - fb = &intel_fb->base; - - fb->dev = dev; - - if (INTEL_GEN(dev_priv) >= 4) { - if (val & DISPPLANE_TILED) { - plane_config->tiling = I915_TILING_X; - fb->modifier = I915_FORMAT_MOD_X_TILED; - } - } - - pixel_format = val & DISPPLANE_PIXFORMAT_MASK; - fourcc = i9xx_format_to_fourcc(pixel_format); - fb->format = drm_format_info(fourcc); - - base = I915_READ(DSPSURF(pipe)) & 0xfffff000; - if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) { - offset = I915_READ(DSPOFFSET(pipe)); - } else { - if (plane_config->tiling) - offset = I915_READ(DSPTILEOFF(pipe)); - else - offset = I915_READ(DSPLINOFF(pipe)); - } - plane_config->base = base; - - val = I915_READ(PIPESRC(pipe)); - fb->width = ((val >> 16) & 0xfff) + 1; - fb->height = ((val >> 0) & 0xfff) + 1; - - val = I915_READ(DSPSTRIDE(pipe)); - fb->pitches[0] = val & 0xffffffc0; - - aligned_height = intel_fb_align_height(fb, 0, fb->height); - - plane_config->size = fb->pitches[0] * aligned_height; - - DRM_DEBUG_KMS("pipe %c with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n", - pipe_name(pipe), fb->width, fb->height, - fb->format->cpp[0] * 8, base, fb->pitches[0], - plane_config->size); - - plane_config->fb = intel_fb; -} - static bool ironlake_get_pipe_config(struct intel_crtc *crtc, struct intel_crtc_state *pipe_config) { @@ -9275,6 +9195,19 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc, ironlake_get_pfit_config(crtc, pipe_config); } + if (hsw_crtc_supports_ips(crtc)) { + if (IS_HASWELL(dev_priv)) + pipe_config->ips_enabled = I915_READ(IPS_CTL) & IPS_ENABLE; + else { + /* + * We cannot readout IPS state on broadwell, set to + * true so we can set it to a defined state on first + * commit. + */ + pipe_config->ips_enabled = true; + } + } + if (pipe_config->cpu_transcoder != TRANSCODER_EDP && !transcoder_is_dsi(pipe_config->cpu_transcoder)) { pipe_config->pixel_multiplier = @@ -9500,6 +9433,23 @@ static void i845_disable_cursor(struct intel_plane *plane, i845_update_cursor(plane, NULL, NULL); } +static bool i845_cursor_get_hw_state(struct intel_plane *plane) +{ + struct drm_i915_private *dev_priv = to_i915(plane->base.dev); + enum intel_display_power_domain power_domain; + bool ret; + + power_domain = POWER_DOMAIN_PIPE(PIPE_A); + if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) + return false; + + ret = I915_READ(CURCNTR(PIPE_A)) & CURSOR_ENABLE; + + intel_display_power_put(dev_priv, power_domain); + + return ret; +} + static u32 i9xx_cursor_ctl(const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state) { @@ -9693,6 +9643,28 @@ static void i9xx_disable_cursor(struct intel_plane *plane, i9xx_update_cursor(plane, NULL, NULL); } +static bool i9xx_cursor_get_hw_state(struct intel_plane *plane) +{ + struct drm_i915_private *dev_priv = to_i915(plane->base.dev); + enum intel_display_power_domain power_domain; + enum pipe pipe = plane->pipe; + bool ret; + + /* + * Not 100% correct for planes that can move between pipes, + * but that's only the case for gen2-3 which don't have any + * display power wells. + */ + power_domain = POWER_DOMAIN_PIPE(pipe); + if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) + return false; + + ret = I915_READ(CURCNTR(pipe)) & CURSOR_MODE; + + intel_display_power_put(dev_priv, power_domain); + + return ret; +} /* VESA 640x480x72Hz mode to set on the pipe */ static const struct drm_display_mode load_detect_mode = { @@ -9967,11 +9939,10 @@ found: } ret = intel_modeset_setup_plane_state(state, crtc, mode, fb, 0, 0); + drm_framebuffer_put(fb); if (ret) goto fail; - drm_framebuffer_put(fb); - ret = drm_atomic_set_mode_for_crtc(&crtc_state->base, mode); if (ret) goto fail; @@ -10489,6 +10460,9 @@ static int intel_crtc_atomic_check(struct drm_crtc *crtc, pipe_config); } + if (HAS_IPS(dev_priv)) + pipe_config->ips_enabled = hsw_compute_ips_config(pipe_config); + return ret; } @@ -10991,31 +10965,6 @@ fail: return ret; } -static void -intel_modeset_update_crtc_state(struct drm_atomic_state *state) -{ - struct drm_crtc *crtc; - struct drm_crtc_state *new_crtc_state; - int i; - - /* Double check state. */ - for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { - to_intel_crtc(crtc)->config = to_intel_crtc_state(new_crtc_state); - - /* - * Update legacy state to satisfy fbc code. This can - * be removed when fbc uses the atomic state. - */ - if (drm_atomic_get_existing_plane_state(state, crtc->primary)) { - struct drm_plane_state *plane_state = crtc->primary->state; - - crtc->primary->fb = plane_state->fb; - crtc->x = plane_state->src_x >> 16; - crtc->y = plane_state->src_y >> 16; - } - } -} - static bool intel_fuzzy_clock_check(int clock1, int clock2) { int diff; @@ -11655,6 +11604,18 @@ verify_crtc_state(struct drm_crtc *crtc, } static void +intel_verify_planes(struct intel_atomic_state *state) +{ + struct intel_plane *plane; + const struct intel_plane_state *plane_state; + int i; + + for_each_new_intel_plane_in_state(state, plane, + plane_state, i) + assert_plane(plane, plane_state->base.visible); +} + +static void verify_single_dpll_state(struct drm_i915_private *dev_priv, struct intel_shared_dpll *pll, struct drm_crtc *crtc, @@ -12136,7 +12097,7 @@ static int intel_atomic_check(struct drm_device *dev, if (ret) return ret; - intel_fbc_choose_crtc(dev_priv, state); + intel_fbc_choose_crtc(dev_priv, intel_state); return calc_watermark_data(state); } @@ -12376,9 +12337,9 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state) } } - /* Only after disabling all output pipelines that will be changed can we - * update the the output configuration. */ - intel_modeset_update_crtc_state(state); + /* FIXME: Eventually get rid of our intel_crtc->config pointer */ + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) + to_intel_crtc(crtc)->config = to_intel_crtc_state(new_crtc_state); if (intel_state->modeset) { drm_atomic_helper_update_legacy_modeset_state(state->dev, state); @@ -12447,6 +12408,9 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state) intel_modeset_verify_crtc(crtc, state, old_crtc_state, new_crtc_state); } + if (intel_state->modeset) + intel_verify_planes(intel_state); + if (intel_state->modeset && intel_can_enable_sagv(state)) intel_enable_sagv(dev_priv); @@ -13267,9 +13231,9 @@ intel_primary_plane_create(struct drm_i915_private *dev_priv, enum pipe pipe) * port is hooked to pipe B. Hence we want plane A feeding pipe B. */ if (HAS_FBC(dev_priv) && INTEL_GEN(dev_priv) < 4) - primary->plane = (enum plane) !pipe; + primary->i9xx_plane = (enum i9xx_plane_id) !pipe; else - primary->plane = (enum plane) pipe; + primary->i9xx_plane = (enum i9xx_plane_id) pipe; primary->id = PLANE_PRIMARY; primary->frontbuffer_bit = INTEL_FRONTBUFFER_PRIMARY(pipe); primary->check_plane = intel_check_primary_plane; @@ -13281,6 +13245,7 @@ intel_primary_plane_create(struct drm_i915_private *dev_priv, enum pipe pipe) primary->update_plane = skl_update_plane; primary->disable_plane = skl_disable_plane; + primary->get_hw_state = skl_plane_get_hw_state; } else if (INTEL_GEN(dev_priv) >= 9) { intel_primary_formats = skl_primary_formats; num_formats = ARRAY_SIZE(skl_primary_formats); @@ -13291,20 +13256,23 @@ intel_primary_plane_create(struct drm_i915_private *dev_priv, enum pipe pipe) primary->update_plane = skl_update_plane; primary->disable_plane = skl_disable_plane; + primary->get_hw_state = skl_plane_get_hw_state; } else if (INTEL_GEN(dev_priv) >= 4) { intel_primary_formats = i965_primary_formats; num_formats = ARRAY_SIZE(i965_primary_formats); modifiers = i9xx_format_modifiers; - primary->update_plane = i9xx_update_primary_plane; - primary->disable_plane = i9xx_disable_primary_plane; + primary->update_plane = i9xx_update_plane; + primary->disable_plane = i9xx_disable_plane; + primary->get_hw_state = i9xx_plane_get_hw_state; } else { intel_primary_formats = i8xx_primary_formats; num_formats = ARRAY_SIZE(i8xx_primary_formats); modifiers = i9xx_format_modifiers; - primary->update_plane = i9xx_update_primary_plane; - primary->disable_plane = i9xx_disable_primary_plane; + primary->update_plane = i9xx_update_plane; + primary->disable_plane = i9xx_disable_plane; + primary->get_hw_state = i9xx_plane_get_hw_state; } if (INTEL_GEN(dev_priv) >= 9) @@ -13327,7 +13295,8 @@ intel_primary_plane_create(struct drm_i915_private *dev_priv, enum pipe pipe) intel_primary_formats, num_formats, modifiers, DRM_PLANE_TYPE_PRIMARY, - "plane %c", plane_name(primary->plane)); + "plane %c", + plane_name(primary->i9xx_plane)); if (ret) goto fail; @@ -13387,17 +13356,19 @@ intel_cursor_plane_create(struct drm_i915_private *dev_priv, cursor->can_scale = false; cursor->max_downscale = 1; cursor->pipe = pipe; - cursor->plane = pipe; + cursor->i9xx_plane = (enum i9xx_plane_id) pipe; cursor->id = PLANE_CURSOR; cursor->frontbuffer_bit = INTEL_FRONTBUFFER_CURSOR(pipe); if (IS_I845G(dev_priv) || IS_I865G(dev_priv)) { cursor->update_plane = i845_update_cursor; cursor->disable_plane = i845_disable_cursor; + cursor->get_hw_state = i845_cursor_get_hw_state; cursor->check_plane = i845_check_cursor; } else { cursor->update_plane = i9xx_update_cursor; cursor->disable_plane = i9xx_disable_cursor; + cursor->get_hw_state = i9xx_cursor_get_hw_state; cursor->check_plane = i9xx_check_cursor; } @@ -13513,14 +13484,13 @@ static int intel_crtc_init(struct drm_i915_private *dev_priv, enum pipe pipe) goto fail; intel_crtc->pipe = pipe; - intel_crtc->plane = primary->plane; /* initialize shared scalers */ intel_crtc_init_scalers(intel_crtc, crtc_state); BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) || - dev_priv->plane_to_crtc_mapping[intel_crtc->plane] != NULL); - dev_priv->plane_to_crtc_mapping[intel_crtc->plane] = intel_crtc; + dev_priv->plane_to_crtc_mapping[primary->i9xx_plane] != NULL); + dev_priv->plane_to_crtc_mapping[primary->i9xx_plane] = intel_crtc; dev_priv->pipe_to_crtc_mapping[intel_crtc->pipe] = intel_crtc; drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs); @@ -14200,7 +14170,7 @@ void intel_init_display_hooks(struct drm_i915_private *dev_priv) } else if (HAS_DDI(dev_priv)) { dev_priv->display.get_pipe_config = haswell_get_pipe_config; dev_priv->display.get_initial_plane_config = - ironlake_get_initial_plane_config; + i9xx_get_initial_plane_config; dev_priv->display.crtc_compute_clock = haswell_crtc_compute_clock; dev_priv->display.crtc_enable = haswell_crtc_enable; @@ -14208,7 +14178,7 @@ void intel_init_display_hooks(struct drm_i915_private *dev_priv) } else if (HAS_PCH_SPLIT(dev_priv)) { dev_priv->display.get_pipe_config = ironlake_get_pipe_config; dev_priv->display.get_initial_plane_config = - ironlake_get_initial_plane_config; + i9xx_get_initial_plane_config; dev_priv->display.crtc_compute_clock = ironlake_crtc_compute_clock; dev_priv->display.crtc_enable = ironlake_crtc_enable; @@ -14624,7 +14594,7 @@ int intel_modeset_init(struct drm_device *dev) dev->mode_config.cursor_height = MAX_CURSOR_HEIGHT; } - dev->mode_config.fb_base = ggtt->mappable_base; + dev->mode_config.fb_base = ggtt->gmadr.start; DRM_DEBUG_KMS("%d display pipe%s available.\n", INTEL_INFO(dev_priv)->num_pipes, @@ -14693,6 +14663,7 @@ int intel_modeset_init(struct drm_device *dev) void i830_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe) { + struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv, pipe); /* 640x480@60Hz, ~25175 kHz */ struct dpll clock = { .m1 = 18, @@ -14756,42 +14727,62 @@ void i830_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe) I915_WRITE(PIPECONF(pipe), PIPECONF_ENABLE | PIPECONF_PROGRESSIVE); POSTING_READ(PIPECONF(pipe)); + + intel_wait_for_pipe_scanline_moving(crtc); } void i830_disable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe) { + struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv, pipe); + DRM_DEBUG_KMS("disabling pipe %c due to force quirk\n", pipe_name(pipe)); - assert_plane_disabled(dev_priv, PLANE_A); - assert_plane_disabled(dev_priv, PLANE_B); + WARN_ON(I915_READ(DSPCNTR(PLANE_A)) & DISPLAY_PLANE_ENABLE); + WARN_ON(I915_READ(DSPCNTR(PLANE_B)) & DISPLAY_PLANE_ENABLE); + WARN_ON(I915_READ(DSPCNTR(PLANE_C)) & DISPLAY_PLANE_ENABLE); + WARN_ON(I915_READ(CURCNTR(PIPE_A)) & CURSOR_MODE); + WARN_ON(I915_READ(CURCNTR(PIPE_B)) & CURSOR_MODE); I915_WRITE(PIPECONF(pipe), 0); POSTING_READ(PIPECONF(pipe)); - if (wait_for(pipe_dsl_stopped(dev_priv, pipe), 100)) - DRM_ERROR("pipe %c off wait timed out\n", pipe_name(pipe)); + intel_wait_for_pipe_scanline_stopped(crtc); I915_WRITE(DPLL(pipe), DPLL_VGA_MODE_DIS); POSTING_READ(DPLL(pipe)); } -static bool -intel_check_plane_mapping(struct intel_crtc *crtc) +static bool intel_plane_mapping_ok(struct intel_crtc *crtc, + struct intel_plane *plane) { struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - u32 val; + enum i9xx_plane_id i9xx_plane = plane->i9xx_plane; + u32 val = I915_READ(DSPCNTR(i9xx_plane)); - if (INTEL_INFO(dev_priv)->num_pipes == 1) - return true; + return (val & DISPLAY_PLANE_ENABLE) == 0 || + (val & DISPPLANE_SEL_PIPE_MASK) == DISPPLANE_SEL_PIPE(crtc->pipe); +} - val = I915_READ(DSPCNTR(!crtc->plane)); +static void +intel_sanitize_plane_mapping(struct drm_i915_private *dev_priv) +{ + struct intel_crtc *crtc; - if ((val & DISPLAY_PLANE_ENABLE) && - (!!(val & DISPPLANE_SEL_PIPE_MASK) == crtc->pipe)) - return false; + if (INTEL_GEN(dev_priv) >= 4) + return; - return true; + for_each_intel_crtc(&dev_priv->drm, crtc) { + struct intel_plane *plane = + to_intel_plane(crtc->base.primary); + + if (intel_plane_mapping_ok(crtc, plane)) + continue; + + DRM_DEBUG_KMS("%s attached to the wrong pipe, disabling plane\n", + plane->base.name); + intel_plane_disable_noatomic(crtc, plane); + } } static bool intel_crtc_has_encoders(struct intel_crtc *crtc) @@ -14847,33 +14838,15 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc, /* Disable everything but the primary plane */ for_each_intel_plane_on_crtc(dev, crtc, plane) { - if (plane->base.type == DRM_PLANE_TYPE_PRIMARY) - continue; + const struct intel_plane_state *plane_state = + to_intel_plane_state(plane->base.state); - trace_intel_disable_plane(&plane->base, crtc); - plane->disable_plane(plane, crtc); + if (plane_state->base.visible && + plane->base.type != DRM_PLANE_TYPE_PRIMARY) + intel_plane_disable_noatomic(crtc, plane); } } - /* We need to sanitize the plane -> pipe mapping first because this will - * disable the crtc (and hence change the state) if it is wrong. Note - * that gen4+ has a fixed plane -> pipe mapping. */ - if (INTEL_GEN(dev_priv) < 4 && !intel_check_plane_mapping(crtc)) { - bool plane; - - DRM_DEBUG_KMS("[CRTC:%d:%s] wrong plane connection detected!\n", - crtc->base.base.id, crtc->base.name); - - /* Pipe has the wrong plane attached and the plane is active. - * Temporarily change the plane mapping and disable everything - * ... */ - plane = crtc->plane; - crtc->base.primary->state->visible = true; - crtc->plane = !plane; - intel_crtc_disable_noatomic(&crtc->base, ctx); - crtc->plane = plane; - } - /* Adjust the state of the output pipe according to whether we * have active connectors/encoders. */ if (crtc->active && !intel_crtc_has_encoders(crtc)) @@ -14947,8 +14920,6 @@ static void intel_sanitize_encoder(struct intel_encoder *encoder) connector->base.dpms = DRM_MODE_DPMS_OFF; connector->base.encoder = NULL; } - /* Enabled encoders without active connectors will be fixed in - * the crtc fixup. */ } void i915_redisable_vga_power_on(struct drm_i915_private *dev_priv) @@ -14978,24 +14949,21 @@ void i915_redisable_vga(struct drm_i915_private *dev_priv) intel_display_power_put(dev_priv, POWER_DOMAIN_VGA); } -static bool primary_get_hw_state(struct intel_plane *plane) -{ - struct drm_i915_private *dev_priv = to_i915(plane->base.dev); - - return I915_READ(DSPCNTR(plane->plane)) & DISPLAY_PLANE_ENABLE; -} - /* FIXME read out full plane state for all planes */ static void readout_plane_state(struct intel_crtc *crtc) { - struct intel_plane *primary = to_intel_plane(crtc->base.primary); - bool visible; + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + struct intel_crtc_state *crtc_state = + to_intel_crtc_state(crtc->base.state); + struct intel_plane *plane; - visible = crtc->active && primary_get_hw_state(primary); + for_each_intel_plane_on_crtc(&dev_priv->drm, crtc, plane) { + struct intel_plane_state *plane_state = + to_intel_plane_state(plane->base.state); + bool visible = plane->get_hw_state(plane); - intel_set_plane_visible(to_intel_crtc_state(crtc->base.state), - to_intel_plane_state(primary->base.state), - visible); + intel_set_plane_visible(crtc_state, plane_state, visible); + } } static void intel_modeset_readout_hw_state(struct drm_device *dev) @@ -15203,6 +15171,8 @@ intel_modeset_setup_hw_state(struct drm_device *dev, /* HW state is read out, now we need to sanitize this mess. */ get_encoder_power_domains(dev_priv); + intel_sanitize_plane_mapping(dev_priv); + for_each_intel_encoder(dev, encoder) { intel_sanitize_encoder(encoder); } @@ -15317,10 +15287,7 @@ static void intel_hpd_poll_fini(struct drm_device *dev) struct intel_connector *connector; struct drm_connector_list_iter conn_iter; - /* First disable polling... */ - drm_kms_helper_poll_fini(dev); - - /* Then kill the work that may have been queued by hpd. */ + /* Kill all the work that may have been queued by hpd. */ drm_connector_list_iter_begin(dev, &conn_iter); for_each_intel_connector_iter(connector, &conn_iter) { if (connector->modeset_retry_work.func) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index bbf2256ba574..35c5299feab6 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1643,7 +1643,7 @@ intel_dp_compute_config(struct intel_encoder *encoder, pipe_config->has_pch_encoder = true; pipe_config->has_drrs = false; - if (port == PORT_A) + if (IS_G4X(dev_priv) || port == PORT_A) pipe_config->has_audio = false; else if (intel_conn_state->force_audio == HDMI_AUDIO_AUTO) pipe_config->has_audio = intel_dp->has_audio; @@ -1677,6 +1677,10 @@ intel_dp_compute_config(struct intel_encoder *encoder, conn_state->scaling_mode); } + if ((IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) && + adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) + return false; + if (adjusted_mode->flags & DRM_MODE_FLAG_DBLCLK) return false; @@ -4277,6 +4281,8 @@ intel_dp_check_link_status(struct intel_dp *intel_dp) { struct drm_i915_private *dev_priv = to_i915(intel_dp_to_dev(intel_dp)); struct intel_encoder *intel_encoder = &dp_to_dig_port(intel_dp)->base; + struct drm_connector_state *conn_state = + intel_dp->attached_connector->base.state; u8 link_status[DP_LINK_STATUS_SIZE]; WARN_ON(!drm_modeset_is_locked(&dev_priv->drm.mode_config.connection_mutex)); @@ -4286,10 +4292,16 @@ intel_dp_check_link_status(struct intel_dp *intel_dp) return; } - if (!intel_encoder->base.crtc) + if (!conn_state->crtc) + return; + + WARN_ON(!drm_modeset_is_locked(&conn_state->crtc->mutex)); + + if (!conn_state->crtc->state->active) return; - if (!to_intel_crtc(intel_encoder->base.crtc)->active) + if (conn_state->commit && + !try_wait_for_completion(&conn_state->commit->hw_done)) return; /* @@ -4364,9 +4376,8 @@ intel_dp_short_pulse(struct intel_dp *intel_dp) DRM_DEBUG_DRIVER("CP or sink specific irq unhandled\n"); } - drm_modeset_lock(&dev_priv->drm.mode_config.connection_mutex, NULL); intel_dp_check_link_status(intel_dp); - drm_modeset_unlock(&dev_priv->drm.mode_config.connection_mutex); + if (intel_dp->compliance.test_type == DP_TEST_LINK_TRAINING) { DRM_DEBUG_KMS("Link Training Compliance Test requested\n"); /* Send a Hotplug Uevent to userspace to start modeset */ @@ -4814,8 +4825,19 @@ intel_dp_detect(struct drm_connector *connector, connector->base.id, connector->name); /* If full detect is not performed yet, do a full detect */ - if (!intel_dp->detect_done) + if (!intel_dp->detect_done) { + struct drm_crtc *crtc; + int ret; + + crtc = connector->state->crtc; + if (crtc) { + ret = drm_modeset_lock(&crtc->mutex, ctx); + if (ret) + return ret; + } + status = intel_dp_long_pulse(intel_dp->attached_connector); + } intel_dp->detect_done = false; @@ -5097,7 +5119,38 @@ intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd) } if (!intel_dp->is_mst) { - if (!intel_dp_short_pulse(intel_dp)) { + struct drm_modeset_acquire_ctx ctx; + struct drm_connector *connector = &intel_dp->attached_connector->base; + struct drm_crtc *crtc; + int iret; + bool handled = false; + + drm_modeset_acquire_init(&ctx, 0); +retry: + iret = drm_modeset_lock(&dev_priv->drm.mode_config.connection_mutex, &ctx); + if (iret) + goto err; + + crtc = connector->state->crtc; + if (crtc) { + iret = drm_modeset_lock(&crtc->mutex, &ctx); + if (iret) + goto err; + } + + handled = intel_dp_short_pulse(intel_dp); + +err: + if (iret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry; + } + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + WARN(iret, "Acquiring modeset locks failed with %i\n", iret); + + if (!handled) { intel_dp->detect_done = false; goto put_power; } @@ -5131,8 +5184,11 @@ static void intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connector) { struct drm_i915_private *dev_priv = to_i915(connector->dev); + enum port port = dp_to_dig_port(intel_dp)->base.port; + + if (!IS_G4X(dev_priv) && port != PORT_A) + intel_attach_force_audio_property(connector); - intel_attach_force_audio_property(connector); intel_attach_broadcast_rgb_property(connector); if (intel_dp_is_edp(intel_dp)) { @@ -5306,6 +5362,12 @@ intel_dp_init_panel_power_sequencer(struct intel_dp *intel_dp) */ final->t8 = 1; final->t9 = 1; + + /* + * HW has only a 100msec granularity for t11_t12 so round it up + * accordingly. + */ + final->t11_t12 = roundup(final->t11_t12, 100 * 10); } static void @@ -6034,7 +6096,8 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, drm_connector_init(dev, connector, &intel_dp_connector_funcs, type); drm_connector_helper_add(connector, &intel_dp_connector_helper_funcs); - connector->interlace_allowed = true; + if (!IS_VALLEYVIEW(dev_priv) && !IS_CHERRYVIEW(dev_priv)) + connector->interlace_allowed = true; connector->doublescan_allowed = 0; intel_dp_init_connector_port_info(intel_dig_port); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index bf8b057f72a6..30f791f89d64 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -48,8 +48,9 @@ * having timed out, since the timeout could be due to preemption or similar and * we've never had a chance to check the condition before the timeout. */ -#define _wait_for(COND, US, W) ({ \ +#define _wait_for(COND, US, Wmin, Wmax) ({ \ unsigned long timeout__ = jiffies + usecs_to_jiffies(US) + 1; \ + long wait__ = (Wmin); /* recommended min for usleep is 10 us */ \ int ret__; \ might_sleep(); \ for (;;) { \ @@ -62,12 +63,14 @@ ret__ = -ETIMEDOUT; \ break; \ } \ - usleep_range((W), (W) * 2); \ + usleep_range(wait__, wait__ * 2); \ + if (wait__ < (Wmax)) \ + wait__ <<= 1; \ } \ ret__; \ }) -#define wait_for(COND, MS) _wait_for((COND), (MS) * 1000, 1000) +#define wait_for(COND, MS) _wait_for((COND), (MS) * 1000, 10, 1000) /* If CONFIG_PREEMPT_COUNT is disabled, in_atomic() always reports false. */ #if defined(CONFIG_DRM_I915_DEBUG) && defined(CONFIG_PREEMPT_COUNT) @@ -116,7 +119,7 @@ int ret__; \ BUILD_BUG_ON(!__builtin_constant_p(US)); \ if ((US) > 10) \ - ret__ = _wait_for((COND), (US), 10); \ + ret__ = _wait_for((COND), (US), 10, 10); \ else \ ret__ = _wait_for_atomic((COND), (US), 0); \ ret__; \ @@ -799,7 +802,6 @@ struct intel_crtc_state { struct intel_crtc { struct drm_crtc base; enum pipe pipe; - enum plane plane; /* * Whether the crtc and the connected output pipeline is active. Implies * that crtc->enabled is set, i.e. the current mode configuration has @@ -844,7 +846,7 @@ struct intel_crtc { struct intel_plane { struct drm_plane base; - u8 plane; + enum i9xx_plane_id i9xx_plane; enum plane_id id; enum pipe pipe; bool can_scale; @@ -866,6 +868,7 @@ struct intel_plane { const struct intel_plane_state *plane_state); void (*disable_plane)(struct intel_plane *plane, struct intel_crtc *crtc); + bool (*get_hw_state)(struct intel_plane *plane); int (*check_plane)(struct intel_plane *plane, struct intel_crtc_state *crtc_state, struct intel_plane_state *state); @@ -1129,7 +1132,7 @@ intel_get_crtc_for_pipe(struct drm_i915_private *dev_priv, enum pipe pipe) } static inline struct intel_crtc * -intel_get_crtc_for_plane(struct drm_i915_private *dev_priv, enum plane plane) +intel_get_crtc_for_plane(struct drm_i915_private *dev_priv, enum i9xx_plane_id plane) { return dev_priv->plane_to_crtc_mapping[plane]; } @@ -1285,8 +1288,6 @@ intel_ddi_get_crtc_new_encoder(struct intel_crtc_state *crtc_state); void intel_ddi_set_pipe_settings(const struct intel_crtc_state *crtc_state); void intel_ddi_prepare_link_retrain(struct intel_dp *intel_dp); bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector); -bool intel_ddi_is_audio_enabled(struct drm_i915_private *dev_priv, - struct intel_crtc *intel_crtc); void intel_ddi_get_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config); @@ -1485,6 +1486,7 @@ bool bxt_find_best_dpll(struct intel_crtc_state *crtc_state, int target_clock, int chv_calc_dpll_params(int refclk, struct dpll *pll_clock); bool intel_crtc_active(struct intel_crtc *crtc); +bool hsw_crtc_state_ips_capable(const struct intel_crtc_state *crtc_state); void hsw_enable_ips(const struct intel_crtc_state *crtc_state); void hsw_disable_ips(const struct intel_crtc_state *crtc_state); enum intel_display_power_domain intel_port_to_power_domain(enum port port); @@ -1651,7 +1653,7 @@ static inline void intel_fbdev_restore_mode(struct drm_device *dev) /* intel_fbc.c */ void intel_fbc_choose_crtc(struct drm_i915_private *dev_priv, - struct drm_atomic_state *state); + struct intel_atomic_state *state); bool intel_fbc_is_active(struct drm_i915_private *dev_priv); void intel_fbc_pre_update(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state, @@ -1906,15 +1908,10 @@ bool skl_ddb_allocation_overlaps(struct drm_i915_private *dev_priv, const struct skl_ddb_entry *ddb, int ignore); bool ilk_disable_lp_wm(struct drm_device *dev); -int sanitize_rc6_option(struct drm_i915_private *dev_priv, int enable_rc6); int skl_check_pipe_max_pixel_rate(struct intel_crtc *intel_crtc, struct intel_crtc_state *cstate); void intel_init_ipc(struct drm_i915_private *dev_priv); void intel_enable_ipc(struct drm_i915_private *dev_priv); -static inline int intel_rc6_enabled(void) -{ - return i915_modparams.enable_rc6; -} /* intel_sdvo.c */ bool intel_sdvo_init(struct drm_i915_private *dev_priv, @@ -1934,6 +1931,7 @@ void skl_update_plane(struct intel_plane *plane, const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state); void skl_disable_plane(struct intel_plane *plane, struct intel_crtc *crtc); +bool skl_plane_get_hw_state(struct intel_plane *plane); /* intel_tv.c */ void intel_tv_init(struct drm_i915_private *dev_priv); diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c index f09474b0c4d3..f67d321376e4 100644 --- a/drivers/gpu/drm/i915/intel_dsi.c +++ b/drivers/gpu/drm/i915/intel_dsi.c @@ -1666,6 +1666,27 @@ static const struct drm_connector_funcs intel_dsi_connector_funcs = { .atomic_duplicate_state = intel_digital_connector_duplicate_state, }; +static int intel_dsi_get_panel_orientation(struct intel_connector *connector) +{ + struct drm_i915_private *dev_priv = to_i915(connector->base.dev); + int orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL; + enum i9xx_plane_id plane; + u32 val; + + if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { + if (connector->encoder->crtc_mask == BIT(PIPE_B)) + plane = PLANE_B; + else + plane = PLANE_A; + + val = I915_READ(DSPCNTR(plane)); + if (val & DISPPLANE_ROTATE_180) + orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP; + } + + return orientation; +} + static void intel_dsi_add_properties(struct intel_connector *connector) { struct drm_i915_private *dev_priv = to_i915(connector->base.dev); @@ -1681,6 +1702,13 @@ static void intel_dsi_add_properties(struct intel_connector *connector) allowed_scalers); connector->base.state->scaling_mode = DRM_MODE_SCALE_ASPECT; + + connector->base.display_info.panel_orientation = + intel_dsi_get_panel_orientation(connector); + drm_connector_init_panel_orientation_property( + &connector->base, + connector->panel.fixed_mode->hdisplay, + connector->panel.fixed_mode->vdisplay); } } diff --git a/drivers/gpu/drm/i915/intel_engine_cs.c b/drivers/gpu/drm/i915/intel_engine_cs.c index 9897c7f78c51..510e0bc3a377 100644 --- a/drivers/gpu/drm/i915/intel_engine_cs.c +++ b/drivers/gpu/drm/i915/intel_engine_cs.c @@ -37,8 +37,6 @@ * Resource Streamer, is 66944 bytes, which rounds to 17 pages. */ #define HSW_CXT_TOTAL_SIZE (17 * PAGE_SIZE) -/* Same as Haswell, but 72064 bytes now. */ -#define GEN8_CXT_TOTAL_SIZE (18 * PAGE_SIZE) #define GEN8_LR_CONTEXT_RENDER_SIZE (20 * PAGE_SIZE) #define GEN9_LR_CONTEXT_RENDER_SIZE (22 * PAGE_SIZE) @@ -164,9 +162,7 @@ __intel_engine_context_size(struct drm_i915_private *dev_priv, u8 class) case 9: return GEN9_LR_CONTEXT_RENDER_SIZE; case 8: - return i915_modparams.enable_execlists ? - GEN8_LR_CONTEXT_RENDER_SIZE : - GEN8_CXT_TOTAL_SIZE; + return GEN8_LR_CONTEXT_RENDER_SIZE; case 7: if (IS_HASWELL(dev_priv)) return HSW_CXT_TOTAL_SIZE; @@ -209,6 +205,15 @@ intel_engine_setup(struct drm_i915_private *dev_priv, GEM_BUG_ON(info->class >= ARRAY_SIZE(intel_engine_classes)); class_info = &intel_engine_classes[info->class]; + if (GEM_WARN_ON(info->class > MAX_ENGINE_CLASS)) + return -EINVAL; + + if (GEM_WARN_ON(info->instance > MAX_ENGINE_INSTANCE)) + return -EINVAL; + + if (GEM_WARN_ON(dev_priv->engine_class[info->class][info->instance])) + return -EINVAL; + GEM_BUG_ON(dev_priv->engine[id]); engine = kzalloc(sizeof(*engine), GFP_KERNEL); if (!engine) @@ -236,8 +241,11 @@ intel_engine_setup(struct drm_i915_private *dev_priv, /* Nothing to do here, execute in order of dependencies */ engine->schedule = NULL; + spin_lock_init(&engine->stats.lock); + ATOMIC_INIT_NOTIFIER_HEAD(&engine->context_status_notifier); + dev_priv->engine_class[info->class][info->instance] = engine; dev_priv->engine[id] = engine; return 0; } @@ -316,7 +324,7 @@ int intel_engines_init(struct drm_i915_private *dev_priv) &intel_engine_classes[engine->class]; int (*init)(struct intel_engine_cs *engine); - if (i915_modparams.enable_execlists) + if (HAS_EXECLISTS(dev_priv)) init = class_info->init_execlists; else init = class_info->init_legacy; @@ -366,18 +374,6 @@ void intel_engine_init_global_seqno(struct intel_engine_cs *engine, u32 seqno) if (HAS_VEBOX(dev_priv)) I915_WRITE(RING_SYNC_2(engine->mmio_base), 0); } - if (dev_priv->semaphore) { - struct page *page = i915_vma_first_page(dev_priv->semaphore); - void *semaphores; - - /* Semaphores are in noncoherent memory, flush to be safe */ - semaphores = kmap_atomic(page); - memset(semaphores + GEN8_SEMAPHORE_OFFSET(engine->id, 0), - 0, I915_NUM_ENGINES * gen8_semaphore_seqno_size); - drm_clflush_virt_range(semaphores + GEN8_SEMAPHORE_OFFSET(engine->id, 0), - I915_NUM_ENGINES * gen8_semaphore_seqno_size); - kunmap_atomic(semaphores); - } intel_write_status_page(engine, I915_GEM_HWS_INDEX, seqno); clear_bit(ENGINE_IRQ_BREADCRUMB, &engine->irq_posted); @@ -1071,6 +1067,15 @@ static int gen9_init_workarounds(struct intel_engine_cs *engine) /* WaDisableSTUnitPowerOptimization:skl,bxt,kbl,glk,cfl */ WA_SET_BIT_MASKED(HALF_SLICE_CHICKEN2, GEN8_ST_PO_DISABLE); + /* WaProgramL3SqcReg1DefaultForPerf:bxt,glk */ + if (IS_GEN9_LP(dev_priv)) { + u32 val = I915_READ(GEN8_L3SQCREG1); + + val &= ~L3_PRIO_CREDITS_MASK; + val |= L3_GENERAL_PRIO_CREDITS(62) | L3_HIGH_PRIO_CREDITS(2); + I915_WRITE(GEN8_L3SQCREG1, val); + } + /* WaOCLCoherentLineFlush:skl,bxt,kbl,cfl */ I915_WRITE(GEN8_L3SQCREG4, (I915_READ(GEN8_L3SQCREG4) | GEN8_LQSC_FLUSH_COHERENT_LINES)); @@ -1188,7 +1193,6 @@ static int skl_init_workarounds(struct intel_engine_cs *engine) static int bxt_init_workarounds(struct intel_engine_cs *engine) { struct drm_i915_private *dev_priv = engine->i915; - u32 val; int ret; ret = gen9_init_workarounds(engine); @@ -1203,12 +1207,6 @@ static int bxt_init_workarounds(struct intel_engine_cs *engine) I915_WRITE(FF_SLICE_CS_CHICKEN2, _MASKED_BIT_ENABLE(GEN9_POOLED_EU_LOAD_BALANCING_FIX_DISABLE)); - /* WaProgramL3SqcReg1DefaultForPerf:bxt */ - val = I915_READ(GEN8_L3SQCREG1); - val &= ~L3_PRIO_CREDITS_MASK; - val |= L3_GENERAL_PRIO_CREDITS(62) | L3_HIGH_PRIO_CREDITS(2); - I915_WRITE(GEN8_L3SQCREG1, val); - /* WaToEnableHwFixForPushConstHWBug:bxt */ WA_SET_BIT_MASKED(COMMON_SLICE_CHICKEN2, GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION); @@ -1515,10 +1513,8 @@ bool intel_engines_are_idle(struct drm_i915_private *dev_priv) struct intel_engine_cs *engine; enum intel_engine_id id; - if (READ_ONCE(dev_priv->gt.active_requests)) - return false; - - /* If the driver is wedged, HW state may be very inconsistent and + /* + * If the driver is wedged, HW state may be very inconsistent and * report that it is still busy, even though we have stopped using it. */ if (i915_terminally_wedged(&dev_priv->gpu_error)) @@ -1598,7 +1594,7 @@ void intel_engines_park(struct drm_i915_private *i915) dev_err(i915->drm.dev, "%s is not idle before parking\n", engine->name); - intel_engine_dump(engine, &p); + intel_engine_dump(engine, &p, NULL); } if (engine->park) @@ -1668,7 +1664,9 @@ static void print_request(struct drm_printer *m, rq->timeline->common->name); } -void intel_engine_dump(struct intel_engine_cs *engine, struct drm_printer *m) +void intel_engine_dump(struct intel_engine_cs *engine, + struct drm_printer *m, + const char *header, ...) { struct intel_breadcrumbs * const b = &engine->breadcrumbs; const struct intel_engine_execlists * const execlists = &engine->execlists; @@ -1676,17 +1674,29 @@ void intel_engine_dump(struct intel_engine_cs *engine, struct drm_printer *m) struct drm_i915_private *dev_priv = engine->i915; struct drm_i915_gem_request *rq; struct rb_node *rb; + char hdr[80]; u64 addr; - drm_printf(m, "%s\n", engine->name); + if (header) { + va_list ap; + + va_start(ap, header); + drm_vprintf(m, header, &ap); + va_end(ap); + } + + if (i915_terminally_wedged(&engine->i915->gpu_error)) + drm_printf(m, "*** WEDGED ***\n"); + drm_printf(m, "\tcurrent seqno %x, last %x, hangcheck %x [%d ms], inflight %d\n", intel_engine_get_seqno(engine), intel_engine_last_submit(engine), engine->hangcheck.seqno, jiffies_to_msecs(jiffies - engine->hangcheck.action_timestamp), engine->timeline->inflight_seqnos); - drm_printf(m, "\tReset count: %d\n", - i915_reset_engine_count(error, engine)); + drm_printf(m, "\tReset count: %d (global %d)\n", + i915_reset_engine_count(error, engine), + i915_reset_count(error)); rcu_read_lock(); @@ -1729,6 +1739,15 @@ void intel_engine_dump(struct intel_engine_cs *engine, struct drm_printer *m) I915_READ(RING_MI_MODE(engine->mmio_base)), I915_READ(RING_MI_MODE(engine->mmio_base)) & (MODE_IDLE) ? " [idle]" : ""); } + if (HAS_LEGACY_SEMAPHORES(dev_priv)) { + drm_printf(m, "\tSYNC_0: 0x%08x\n", + I915_READ(RING_SYNC_0(engine->mmio_base))); + drm_printf(m, "\tSYNC_1: 0x%08x\n", + I915_READ(RING_SYNC_1(engine->mmio_base))); + if (HAS_VEBOX(dev_priv)) + drm_printf(m, "\tSYNC_2: 0x%08x\n", + I915_READ(RING_SYNC_2(engine->mmio_base))); + } rcu_read_unlock(); @@ -1739,7 +1758,7 @@ void intel_engine_dump(struct intel_engine_cs *engine, struct drm_printer *m) drm_printf(m, "\tBBADDR: 0x%08x_%08x\n", upper_32_bits(addr), lower_32_bits(addr)); - if (i915_modparams.enable_execlists) { + if (HAS_EXECLISTS(dev_priv)) { const u32 *hws = &engine->status_page.page_addr[I915_HWS_CSB_BUF0_INDEX]; u32 ptr, read, write; unsigned int idx; @@ -1779,12 +1798,12 @@ void intel_engine_dump(struct intel_engine_cs *engine, struct drm_printer *m) rq = port_unpack(&execlists->port[idx], &count); if (rq) { - drm_printf(m, "\t\tELSP[%d] count=%d, ", - idx, count); - print_request(m, rq, "rq: "); + snprintf(hdr, sizeof(hdr), + "\t\tELSP[%d] count=%d, rq: ", + idx, count); + print_request(m, rq, hdr); } else { - drm_printf(m, "\t\tELSP[%d] idle\n", - idx); + drm_printf(m, "\t\tELSP[%d] idle\n", idx); } } drm_printf(m, "\t\tHW active? 0x%x\n", execlists->active); @@ -1819,10 +1838,128 @@ void intel_engine_dump(struct intel_engine_cs *engine, struct drm_printer *m) } spin_unlock_irq(&b->rb_lock); + if (INTEL_GEN(dev_priv) >= 6) { + drm_printf(m, "\tRING_IMR: %08x\n", I915_READ_IMR(engine)); + } + + drm_printf(m, "IRQ? 0x%lx (breadcrumbs? %s) (execlists? %s)\n", + engine->irq_posted, + yesno(test_bit(ENGINE_IRQ_BREADCRUMB, + &engine->irq_posted)), + yesno(test_bit(ENGINE_IRQ_EXECLIST, + &engine->irq_posted))); drm_printf(m, "Idle? %s\n", yesno(intel_engine_is_idle(engine))); drm_printf(m, "\n"); } +static u8 user_class_map[] = { + [I915_ENGINE_CLASS_RENDER] = RENDER_CLASS, + [I915_ENGINE_CLASS_COPY] = COPY_ENGINE_CLASS, + [I915_ENGINE_CLASS_VIDEO] = VIDEO_DECODE_CLASS, + [I915_ENGINE_CLASS_VIDEO_ENHANCE] = VIDEO_ENHANCEMENT_CLASS, +}; + +struct intel_engine_cs * +intel_engine_lookup_user(struct drm_i915_private *i915, u8 class, u8 instance) +{ + if (class >= ARRAY_SIZE(user_class_map)) + return NULL; + + class = user_class_map[class]; + + GEM_BUG_ON(class > MAX_ENGINE_CLASS); + + if (instance > MAX_ENGINE_INSTANCE) + return NULL; + + return i915->engine_class[class][instance]; +} + +/** + * intel_enable_engine_stats() - Enable engine busy tracking on engine + * @engine: engine to enable stats collection + * + * Start collecting the engine busyness data for @engine. + * + * Returns 0 on success or a negative error code. + */ +int intel_enable_engine_stats(struct intel_engine_cs *engine) +{ + unsigned long flags; + + if (!intel_engine_supports_stats(engine)) + return -ENODEV; + + spin_lock_irqsave(&engine->stats.lock, flags); + if (engine->stats.enabled == ~0) + goto busy; + if (engine->stats.enabled++ == 0) + engine->stats.enabled_at = ktime_get(); + spin_unlock_irqrestore(&engine->stats.lock, flags); + + return 0; + +busy: + spin_unlock_irqrestore(&engine->stats.lock, flags); + + return -EBUSY; +} + +static ktime_t __intel_engine_get_busy_time(struct intel_engine_cs *engine) +{ + ktime_t total = engine->stats.total; + + /* + * If the engine is executing something at the moment + * add it to the total. + */ + if (engine->stats.active) + total = ktime_add(total, + ktime_sub(ktime_get(), engine->stats.start)); + + return total; +} + +/** + * intel_engine_get_busy_time() - Return current accumulated engine busyness + * @engine: engine to report on + * + * Returns accumulated time @engine was busy since engine stats were enabled. + */ +ktime_t intel_engine_get_busy_time(struct intel_engine_cs *engine) +{ + ktime_t total; + unsigned long flags; + + spin_lock_irqsave(&engine->stats.lock, flags); + total = __intel_engine_get_busy_time(engine); + spin_unlock_irqrestore(&engine->stats.lock, flags); + + return total; +} + +/** + * intel_disable_engine_stats() - Disable engine busy tracking on engine + * @engine: engine to disable stats collection + * + * Stops collecting the engine busyness data for @engine. + */ +void intel_disable_engine_stats(struct intel_engine_cs *engine) +{ + unsigned long flags; + + if (!intel_engine_supports_stats(engine)) + return; + + spin_lock_irqsave(&engine->stats.lock, flags); + WARN_ON_ONCE(engine->stats.enabled == 0); + if (--engine->stats.enabled == 0) { + engine->stats.total = __intel_engine_get_busy_time(engine); + engine->stats.active = 0; + } + spin_unlock_irqrestore(&engine->stats.lock, flags); +} + #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST) #include "selftests/mock_engine.c" #endif diff --git a/drivers/gpu/drm/i915/intel_fbc.c b/drivers/gpu/drm/i915/intel_fbc.c index 1a0f5e0c8d10..f88c1b5dae4c 100644 --- a/drivers/gpu/drm/i915/intel_fbc.c +++ b/drivers/gpu/drm/i915/intel_fbc.c @@ -151,7 +151,7 @@ static void i8xx_fbc_activate(struct drm_i915_private *dev_priv) /* Set it up... */ fbc_ctl2 = FBC_CTL_FENCE_DBL | FBC_CTL_IDLE_IMM | FBC_CTL_CPU_FENCE; - fbc_ctl2 |= FBC_CTL_PLANE(params->crtc.plane); + fbc_ctl2 |= FBC_CTL_PLANE(params->crtc.i9xx_plane); I915_WRITE(FBC_CONTROL2, fbc_ctl2); I915_WRITE(FBC_FENCE_OFF, params->crtc.fence_y_offset); } @@ -177,7 +177,7 @@ static void g4x_fbc_activate(struct drm_i915_private *dev_priv) struct intel_fbc_reg_params *params = &dev_priv->fbc.params; u32 dpfc_ctl; - dpfc_ctl = DPFC_CTL_PLANE(params->crtc.plane) | DPFC_SR_EN; + dpfc_ctl = DPFC_CTL_PLANE(params->crtc.i9xx_plane) | DPFC_SR_EN; if (params->fb.format->cpp[0] == 2) dpfc_ctl |= DPFC_CTL_LIMIT_2X; else @@ -224,7 +224,7 @@ static void ilk_fbc_activate(struct drm_i915_private *dev_priv) u32 dpfc_ctl; int threshold = dev_priv->fbc.threshold; - dpfc_ctl = DPFC_CTL_PLANE(params->crtc.plane); + dpfc_ctl = DPFC_CTL_PLANE(params->crtc.i9xx_plane); if (params->fb.format->cpp[0] == 2) threshold++; @@ -306,7 +306,7 @@ static void gen7_fbc_activate(struct drm_i915_private *dev_priv) dpfc_ctl = 0; if (IS_IVYBRIDGE(dev_priv)) - dpfc_ctl |= IVB_DPFC_CTL_PLANE(params->crtc.plane); + dpfc_ctl |= IVB_DPFC_CTL_PLANE(params->crtc.i9xx_plane); if (params->fb.format->cpp[0] == 2) threshold++; @@ -531,7 +531,6 @@ static int find_compression_threshold(struct drm_i915_private *dev_priv, int size, int fb_cpp) { - struct i915_ggtt *ggtt = &dev_priv->ggtt; int compression_threshold = 1; int ret; u64 end; @@ -541,7 +540,7 @@ static int find_compression_threshold(struct drm_i915_private *dev_priv, * If we enable FBC using a CFB on that memory range we'll get FIFO * underruns, even if that range is not reserved by the BIOS. */ if (IS_BROADWELL(dev_priv) || IS_GEN9_BC(dev_priv)) - end = ggtt->stolen_size - 8 * 1024 * 1024; + end = resource_size(&dev_priv->dsm) - 8 * 1024 * 1024; else end = U64_MAX; @@ -615,10 +614,16 @@ static int intel_fbc_alloc_cfb(struct intel_crtc *crtc) fbc->compressed_llb = compressed_llb; + GEM_BUG_ON(range_overflows_t(u64, dev_priv->dsm.start, + fbc->compressed_fb.start, + U32_MAX)); + GEM_BUG_ON(range_overflows_t(u64, dev_priv->dsm.start, + fbc->compressed_llb->start, + U32_MAX)); I915_WRITE(FBC_CFB_BASE, - dev_priv->mm.stolen_base + fbc->compressed_fb.start); + dev_priv->dsm.start + fbc->compressed_fb.start); I915_WRITE(FBC_LL_BASE, - dev_priv->mm.stolen_base + compressed_llb->start); + dev_priv->dsm.start + compressed_llb->start); } DRM_DEBUG_KMS("reserved %llu bytes of contiguous stolen space for FBC, threshold: %d\n", @@ -890,7 +895,7 @@ static void intel_fbc_get_reg_params(struct intel_crtc *crtc, params->vma = cache->vma; params->crtc.pipe = crtc->pipe; - params->crtc.plane = crtc->plane; + params->crtc.i9xx_plane = to_intel_plane(crtc->base.primary)->i9xx_plane; params->crtc.fence_y_offset = get_crtc_fence_y_offset(fbc); params->fb.format = cache->fb.format; @@ -1054,11 +1059,11 @@ out: * enable FBC for the chosen CRTC. If it does, it will set dev_priv->fbc.crtc. */ void intel_fbc_choose_crtc(struct drm_i915_private *dev_priv, - struct drm_atomic_state *state) + struct intel_atomic_state *state) { struct intel_fbc *fbc = &dev_priv->fbc; - struct drm_plane *plane; - struct drm_plane_state *plane_state; + struct intel_plane *plane; + struct intel_plane_state *plane_state; bool crtc_chosen = false; int i; @@ -1066,7 +1071,7 @@ void intel_fbc_choose_crtc(struct drm_i915_private *dev_priv, /* Does this atomic commit involve the CRTC currently tied to FBC? */ if (fbc->crtc && - !drm_atomic_get_existing_crtc_state(state, &fbc->crtc->base)) + !intel_atomic_get_new_crtc_state(state, fbc->crtc)) goto out; if (!intel_fbc_can_enable(dev_priv)) @@ -1076,25 +1081,22 @@ void intel_fbc_choose_crtc(struct drm_i915_private *dev_priv, * plane. We could go for fancier schemes such as checking the plane * size, but this would just affect the few platforms that don't tie FBC * to pipe or plane A. */ - for_each_new_plane_in_state(state, plane, plane_state, i) { - struct intel_plane_state *intel_plane_state = - to_intel_plane_state(plane_state); - struct intel_crtc_state *intel_crtc_state; - struct intel_crtc *crtc = to_intel_crtc(plane_state->crtc); + for_each_new_intel_plane_in_state(state, plane, plane_state, i) { + struct intel_crtc_state *crtc_state; + struct intel_crtc *crtc = to_intel_crtc(plane_state->base.crtc); - if (!intel_plane_state->base.visible) + if (!plane_state->base.visible) continue; if (fbc_on_pipe_a_only(dev_priv) && crtc->pipe != PIPE_A) continue; - if (fbc_on_plane_a_only(dev_priv) && crtc->plane != PLANE_A) + if (fbc_on_plane_a_only(dev_priv) && plane->i9xx_plane != PLANE_A) continue; - intel_crtc_state = to_intel_crtc_state( - drm_atomic_get_existing_crtc_state(state, &crtc->base)); + crtc_state = intel_atomic_get_new_crtc_state(state, crtc); - intel_crtc_state->enable_fbc = true; + crtc_state->enable_fbc = true; crtc_chosen = true; break; } diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index ea96682568e8..da48af11eb6b 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -115,7 +115,6 @@ static int intelfb_alloc(struct drm_fb_helper *helper, struct drm_framebuffer *fb; struct drm_device *dev = helper->dev; struct drm_i915_private *dev_priv = to_i915(dev); - struct i915_ggtt *ggtt = &dev_priv->ggtt; struct drm_mode_fb_cmd2 mode_cmd = {}; struct drm_i915_gem_object *obj; int size, ret; @@ -139,7 +138,7 @@ static int intelfb_alloc(struct drm_fb_helper *helper, * important and we should probably use that space with FBC or other * features. */ obj = NULL; - if (size * 2 < ggtt->stolen_usable_size) + if (size * 2 < dev_priv->stolen_usable_size) obj = i915_gem_object_create_stolen(dev_priv, size); if (obj == NULL) obj = i915_gem_object_create(dev_priv, size); diff --git a/drivers/gpu/drm/i915/intel_guc.c b/drivers/gpu/drm/i915/intel_guc.c index 823d0c2e9ad2..3c6bf5a34c3c 100644 --- a/drivers/gpu/drm/i915/intel_guc.c +++ b/drivers/gpu/drm/i915/intel_guc.c @@ -61,6 +61,7 @@ void intel_guc_init_send_regs(struct intel_guc *guc) void intel_guc_init_early(struct intel_guc *guc) { + intel_guc_fw_init_early(guc); intel_guc_ct_init_early(&guc->ct); mutex_init(&guc->send_mutex); @@ -68,6 +69,114 @@ void intel_guc_init_early(struct intel_guc *guc) guc->notify = gen8_guc_raise_irq; } +int intel_guc_init_wq(struct intel_guc *guc) +{ + struct drm_i915_private *dev_priv = guc_to_i915(guc); + + /* + * GuC log buffer flush work item has to do register access to + * send the ack to GuC and this work item, if not synced before + * suspend, can potentially get executed after the GFX device is + * suspended. + * By marking the WQ as freezable, we don't have to bother about + * flushing of this work item from the suspend hooks, the pending + * work item if any will be either executed before the suspend + * or scheduled later on resume. This way the handling of work + * item can be kept same between system suspend & rpm suspend. + */ + guc->log.runtime.flush_wq = alloc_ordered_workqueue("i915-guc_log", + WQ_HIGHPRI | WQ_FREEZABLE); + if (!guc->log.runtime.flush_wq) + return -ENOMEM; + + /* + * Even though both sending GuC action, and adding a new workitem to + * GuC workqueue are serialized (each with its own locking), since + * we're using mutliple engines, it's possible that we're going to + * issue a preempt request with two (or more - each for different + * engine) workitems in GuC queue. In this situation, GuC may submit + * all of them, which will make us very confused. + * Our preemption contexts may even already be complete - before we + * even had the chance to sent the preempt action to GuC!. Rather + * than introducing yet another lock, we can just use ordered workqueue + * to make sure we're always sending a single preemption request with a + * single workitem. + */ + if (HAS_LOGICAL_RING_PREEMPTION(dev_priv) && + USES_GUC_SUBMISSION(dev_priv)) { + guc->preempt_wq = alloc_ordered_workqueue("i915-guc_preempt", + WQ_HIGHPRI); + if (!guc->preempt_wq) { + destroy_workqueue(guc->log.runtime.flush_wq); + return -ENOMEM; + } + } + + return 0; +} + +void intel_guc_fini_wq(struct intel_guc *guc) +{ + struct drm_i915_private *dev_priv = guc_to_i915(guc); + + if (HAS_LOGICAL_RING_PREEMPTION(dev_priv) && + USES_GUC_SUBMISSION(dev_priv)) + destroy_workqueue(guc->preempt_wq); + + destroy_workqueue(guc->log.runtime.flush_wq); +} + +static int guc_shared_data_create(struct intel_guc *guc) +{ + struct i915_vma *vma; + void *vaddr; + + vma = intel_guc_allocate_vma(guc, PAGE_SIZE); + if (IS_ERR(vma)) + return PTR_ERR(vma); + + vaddr = i915_gem_object_pin_map(vma->obj, I915_MAP_WB); + if (IS_ERR(vaddr)) { + i915_vma_unpin_and_release(&vma); + return PTR_ERR(vaddr); + } + + guc->shared_data = vma; + guc->shared_data_vaddr = vaddr; + + return 0; +} + +static void guc_shared_data_destroy(struct intel_guc *guc) +{ + i915_gem_object_unpin_map(guc->shared_data->obj); + i915_vma_unpin_and_release(&guc->shared_data); +} + +int intel_guc_init(struct intel_guc *guc) +{ + struct drm_i915_private *dev_priv = guc_to_i915(guc); + int ret; + + ret = guc_shared_data_create(guc); + if (ret) + return ret; + GEM_BUG_ON(!guc->shared_data); + + /* We need to notify the guc whenever we change the GGTT */ + i915_ggtt_enable_guc(dev_priv); + + return 0; +} + +void intel_guc_fini(struct intel_guc *guc) +{ + struct drm_i915_private *dev_priv = guc_to_i915(guc); + + i915_ggtt_disable_guc(dev_priv); + guc_shared_data_destroy(guc); +} + static u32 get_gt_type(struct drm_i915_private *dev_priv) { /* XXX: GT type based on PCI device ID? field seems unused by fw */ @@ -128,7 +237,7 @@ void intel_guc_init_params(struct intel_guc *guc) } /* If GuC submission is enabled, set up additional parameters here */ - if (i915_modparams.enable_guc_submission) { + if (USES_GUC_SUBMISSION(dev_priv)) { u32 ads = guc_ggtt_offset(guc->ads_vma) >> PAGE_SHIFT; u32 pgs = guc_ggtt_offset(dev_priv->guc.stage_desc_pool); u32 ctx_in_16 = GUC_MAX_STAGE_DESCRIPTORS / 16; @@ -231,8 +340,7 @@ int intel_guc_sample_forcewake(struct intel_guc *guc) action[0] = INTEL_GUC_ACTION_SAMPLE_FORCEWAKE; /* WaRsDisableCoarsePowerGating:skl,bxt */ - if (!intel_rc6_enabled() || - NEEDS_WaRsDisableCoarsePowerGating(dev_priv)) + if (!HAS_RC6(dev_priv) || NEEDS_WaRsDisableCoarsePowerGating(dev_priv)) action[1] = 0; else /* bit 0 and 1 are for Render and Media domain separately */ diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h index 75c4cfefdaff..52856a97477d 100644 --- a/drivers/gpu/drm/i915/intel_guc.h +++ b/drivers/gpu/drm/i915/intel_guc.h @@ -30,8 +30,8 @@ #include "intel_guc_fwif.h" #include "intel_guc_ct.h" #include "intel_guc_log.h" +#include "intel_guc_reg.h" #include "intel_uc_fw.h" -#include "i915_guc_reg.h" #include "i915_vma.h" struct guc_preempt_work { @@ -119,6 +119,10 @@ static inline u32 guc_ggtt_offset(struct i915_vma *vma) void intel_guc_init_early(struct intel_guc *guc); void intel_guc_init_send_regs(struct intel_guc *guc); void intel_guc_init_params(struct intel_guc *guc); +int intel_guc_init_wq(struct intel_guc *guc); +void intel_guc_fini_wq(struct intel_guc *guc); +int intel_guc_init(struct intel_guc *guc); +void intel_guc_fini(struct intel_guc *guc); int intel_guc_send_nop(struct intel_guc *guc, const u32 *action, u32 len); int intel_guc_send_mmio(struct intel_guc *guc, const u32 *action, u32 len); int intel_guc_sample_forcewake(struct intel_guc *guc); diff --git a/drivers/gpu/drm/i915/intel_guc_fw.c b/drivers/gpu/drm/i915/intel_guc_fw.c index 69ba01599575..cbc51c960425 100644 --- a/drivers/gpu/drm/i915/intel_guc_fw.c +++ b/drivers/gpu/drm/i915/intel_guc_fw.c @@ -30,14 +30,14 @@ #include "intel_guc_fw.h" #include "i915_drv.h" -#define SKL_FW_MAJOR 6 -#define SKL_FW_MINOR 1 +#define SKL_FW_MAJOR 9 +#define SKL_FW_MINOR 33 -#define BXT_FW_MAJOR 8 -#define BXT_FW_MINOR 7 +#define BXT_FW_MAJOR 9 +#define BXT_FW_MINOR 29 #define KBL_FW_MAJOR 9 -#define KBL_FW_MINOR 14 +#define KBL_FW_MINOR 39 #define GLK_FW_MAJOR 10 #define GLK_FW_MINOR 56 @@ -56,45 +56,54 @@ MODULE_FIRMWARE(I915_KBL_GUC_UCODE); #define I915_GLK_GUC_UCODE GUC_FW_PATH(glk, GLK_FW_MAJOR, GLK_FW_MINOR) -/** - * intel_guc_fw_select() - selects GuC firmware for uploading - * - * @guc: intel_guc struct - * - * Return: zero when we know firmware, non-zero in other case - */ -int intel_guc_fw_select(struct intel_guc *guc) +static void guc_fw_select(struct intel_uc_fw *guc_fw) { + struct intel_guc *guc = container_of(guc_fw, struct intel_guc, fw); struct drm_i915_private *dev_priv = guc_to_i915(guc); - intel_uc_fw_init(&guc->fw, INTEL_UC_FW_TYPE_GUC); + GEM_BUG_ON(guc_fw->type != INTEL_UC_FW_TYPE_GUC); + + if (!HAS_GUC(dev_priv)) + return; if (i915_modparams.guc_firmware_path) { - guc->fw.path = i915_modparams.guc_firmware_path; - guc->fw.major_ver_wanted = 0; - guc->fw.minor_ver_wanted = 0; + guc_fw->path = i915_modparams.guc_firmware_path; + guc_fw->major_ver_wanted = 0; + guc_fw->minor_ver_wanted = 0; } else if (IS_SKYLAKE(dev_priv)) { - guc->fw.path = I915_SKL_GUC_UCODE; - guc->fw.major_ver_wanted = SKL_FW_MAJOR; - guc->fw.minor_ver_wanted = SKL_FW_MINOR; + guc_fw->path = I915_SKL_GUC_UCODE; + guc_fw->major_ver_wanted = SKL_FW_MAJOR; + guc_fw->minor_ver_wanted = SKL_FW_MINOR; } else if (IS_BROXTON(dev_priv)) { - guc->fw.path = I915_BXT_GUC_UCODE; - guc->fw.major_ver_wanted = BXT_FW_MAJOR; - guc->fw.minor_ver_wanted = BXT_FW_MINOR; + guc_fw->path = I915_BXT_GUC_UCODE; + guc_fw->major_ver_wanted = BXT_FW_MAJOR; + guc_fw->minor_ver_wanted = BXT_FW_MINOR; } else if (IS_KABYLAKE(dev_priv) || IS_COFFEELAKE(dev_priv)) { - guc->fw.path = I915_KBL_GUC_UCODE; - guc->fw.major_ver_wanted = KBL_FW_MAJOR; - guc->fw.minor_ver_wanted = KBL_FW_MINOR; + guc_fw->path = I915_KBL_GUC_UCODE; + guc_fw->major_ver_wanted = KBL_FW_MAJOR; + guc_fw->minor_ver_wanted = KBL_FW_MINOR; } else if (IS_GEMINILAKE(dev_priv)) { - guc->fw.path = I915_GLK_GUC_UCODE; - guc->fw.major_ver_wanted = GLK_FW_MAJOR; - guc->fw.minor_ver_wanted = GLK_FW_MINOR; + guc_fw->path = I915_GLK_GUC_UCODE; + guc_fw->major_ver_wanted = GLK_FW_MAJOR; + guc_fw->minor_ver_wanted = GLK_FW_MINOR; } else { - DRM_ERROR("No GuC firmware known for platform with GuC!\n"); - return -ENOENT; + DRM_WARN("%s: No firmware known for this platform!\n", + intel_uc_fw_type_repr(guc_fw->type)); } +} - return 0; +/** + * intel_guc_fw_init_early() - initializes GuC firmware struct + * @guc: intel_guc struct + * + * On platforms with GuC selects firmware for uploading + */ +void intel_guc_fw_init_early(struct intel_guc *guc) +{ + struct intel_uc_fw *guc_fw = &guc->fw; + + intel_uc_fw_init(guc_fw, INTEL_UC_FW_TYPE_GUC); + guc_fw_select(guc_fw); } static void guc_prepare_xfer(struct intel_guc *guc) @@ -130,14 +139,14 @@ static int guc_xfer_rsa(struct intel_guc *guc, struct i915_vma *vma) struct drm_i915_private *dev_priv = guc_to_i915(guc); struct intel_uc_fw *guc_fw = &guc->fw; struct sg_table *sg = vma->pages; - u32 rsa[UOS_RSA_SCRATCH_MAX_COUNT]; + u32 rsa[UOS_RSA_SCRATCH_COUNT]; int i; if (sg_pcopy_to_buffer(sg->sgl, sg->nents, rsa, sizeof(rsa), guc_fw->rsa_offset) != sizeof(rsa)) return -EINVAL; - for (i = 0; i < UOS_RSA_SCRATCH_MAX_COUNT; i++) + for (i = 0; i < UOS_RSA_SCRATCH_COUNT; i++) I915_WRITE(UOS_RSA_SCRATCH(i), rsa[i]); return 0; diff --git a/drivers/gpu/drm/i915/intel_guc_fw.h b/drivers/gpu/drm/i915/intel_guc_fw.h index 023f5baa9dd6..4ec5d3d9e2b0 100644 --- a/drivers/gpu/drm/i915/intel_guc_fw.h +++ b/drivers/gpu/drm/i915/intel_guc_fw.h @@ -27,7 +27,7 @@ struct intel_guc; -int intel_guc_fw_select(struct intel_guc *guc); +void intel_guc_fw_init_early(struct intel_guc *guc); int intel_guc_fw_upload(struct intel_guc *guc); #endif diff --git a/drivers/gpu/drm/i915/intel_guc_log.c b/drivers/gpu/drm/i915/intel_guc_log.c index 76d3eb1e4614..eaedd63e3819 100644 --- a/drivers/gpu/drm/i915/intel_guc_log.c +++ b/drivers/gpu/drm/i915/intel_guc_log.c @@ -411,30 +411,8 @@ static int guc_log_runtime_create(struct intel_guc *guc) guc->log.runtime.relay_chan = guc_log_relay_chan; INIT_WORK(&guc->log.runtime.flush_work, capture_logs_work); - - /* - * GuC log buffer flush work item has to do register access to - * send the ack to GuC and this work item, if not synced before - * suspend, can potentially get executed after the GFX device is - * suspended. - * By marking the WQ as freezable, we don't have to bother about - * flushing of this work item from the suspend hooks, the pending - * work item if any will be either executed before the suspend - * or scheduled later on resume. This way the handling of work - * item can be kept same between system suspend & rpm suspend. - */ - guc->log.runtime.flush_wq = alloc_ordered_workqueue("i915-guc_log", - WQ_HIGHPRI | WQ_FREEZABLE); - if (!guc->log.runtime.flush_wq) { - DRM_ERROR("Couldn't allocate the wq for GuC logging\n"); - ret = -ENOMEM; - goto err_relaychan; - } - return 0; -err_relaychan: - relay_close(guc->log.runtime.relay_chan); err_vaddr: i915_gem_object_unpin_map(guc->log.vma->obj); guc->log.runtime.buf_addr = NULL; @@ -450,7 +428,6 @@ static void guc_log_runtime_destroy(struct intel_guc *guc) if (!guc_log_has_runtime(guc)) return; - destroy_workqueue(guc->log.runtime.flush_wq); relay_close(guc->log.runtime.relay_chan); i915_gem_object_unpin_map(guc->log.vma->obj); guc->log.runtime.buf_addr = NULL; @@ -505,7 +482,7 @@ static void guc_flush_logs(struct intel_guc *guc) { struct drm_i915_private *dev_priv = guc_to_i915(guc); - if (!i915_modparams.enable_guc_submission || + if (!USES_GUC_SUBMISSION(dev_priv) || (i915_modparams.guc_log_level < 0)) return; @@ -646,7 +623,7 @@ int i915_guc_log_control(struct drm_i915_private *dev_priv, u64 control_val) void i915_guc_log_register(struct drm_i915_private *dev_priv) { - if (!i915_modparams.enable_guc_submission || + if (!USES_GUC_SUBMISSION(dev_priv) || (i915_modparams.guc_log_level < 0)) return; @@ -657,7 +634,7 @@ void i915_guc_log_register(struct drm_i915_private *dev_priv) void i915_guc_log_unregister(struct drm_i915_private *dev_priv) { - if (!i915_modparams.enable_guc_submission) + if (!USES_GUC_SUBMISSION(dev_priv)) return; mutex_lock(&dev_priv->drm.struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_guc_reg.h b/drivers/gpu/drm/i915/intel_guc_reg.h index bc1ae7d8f424..19a9247c5664 100644 --- a/drivers/gpu/drm/i915/i915_guc_reg.h +++ b/drivers/gpu/drm/i915/intel_guc_reg.h @@ -21,8 +21,8 @@ * IN THE SOFTWARE. * */ -#ifndef _I915_GUC_REG_H_ -#define _I915_GUC_REG_H_ +#ifndef _INTEL_GUC_REG_H_ +#define _INTEL_GUC_REG_H_ /* Definitions of GuC H/W registers, bits, etc */ @@ -52,7 +52,8 @@ #define SOFT_SCRATCH_COUNT 16 #define UOS_RSA_SCRATCH(i) _MMIO(0xc200 + (i) * 4) -#define UOS_RSA_SCRATCH_MAX_COUNT 64 +#define UOS_RSA_SCRATCH_COUNT 64 + #define DMA_ADDR_0_LOW _MMIO(0xc300) #define DMA_ADDR_0_HIGH _MMIO(0xc304) #define DMA_ADDR_1_LOW _MMIO(0xc308) diff --git a/drivers/gpu/drm/i915/intel_guc_submission.c b/drivers/gpu/drm/i915/intel_guc_submission.c index cbf5a96f5806..4d2409466a3a 100644 --- a/drivers/gpu/drm/i915/intel_guc_submission.c +++ b/drivers/gpu/drm/i915/intel_guc_submission.c @@ -88,7 +88,7 @@ static inline bool is_high_priority(struct intel_guc_client *client) client->priority == GUC_CLIENT_PRIORITY_HIGH); } -static int __reserve_doorbell(struct intel_guc_client *client) +static int reserve_doorbell(struct intel_guc_client *client) { unsigned long offset; unsigned long end; @@ -120,7 +120,7 @@ static int __reserve_doorbell(struct intel_guc_client *client) return 0; } -static void __unreserve_doorbell(struct intel_guc_client *client) +static void unreserve_doorbell(struct intel_guc_client *client) { GEM_BUG_ON(client->doorbell_id == GUC_DOORBELL_INVALID); @@ -188,32 +188,21 @@ static bool has_doorbell(struct intel_guc_client *client) return test_bit(client->doorbell_id, client->guc->doorbell_bitmap); } -static int __create_doorbell(struct intel_guc_client *client) +static void __create_doorbell(struct intel_guc_client *client) { struct guc_doorbell_info *doorbell; - int err; doorbell = __get_doorbell(client); doorbell->db_status = GUC_DOORBELL_ENABLED; doorbell->cookie = 0; - - err = __guc_allocate_doorbell(client->guc, client->stage_id); - if (err) { - doorbell->db_status = GUC_DOORBELL_DISABLED; - DRM_ERROR("Couldn't create client %u doorbell: %d\n", - client->stage_id, err); - } - - return err; } -static int __destroy_doorbell(struct intel_guc_client *client) +static void __destroy_doorbell(struct intel_guc_client *client) { struct drm_i915_private *dev_priv = guc_to_i915(client->guc); struct guc_doorbell_info *doorbell; u16 db_id = client->doorbell_id; - GEM_BUG_ON(db_id >= GUC_DOORBELL_INVALID); doorbell = __get_doorbell(client); doorbell->db_status = GUC_DOORBELL_DISABLED; @@ -225,50 +214,42 @@ static int __destroy_doorbell(struct intel_guc_client *client) */ if (wait_for_us(!(I915_READ(GEN8_DRBREGL(db_id)) & GEN8_DRB_VALID), 10)) WARN_ONCE(true, "Doorbell never became invalid after disable\n"); - - return __guc_deallocate_doorbell(client->guc, client->stage_id); } static int create_doorbell(struct intel_guc_client *client) { int ret; - ret = __reserve_doorbell(client); - if (ret) - return ret; - __update_doorbell_desc(client, client->doorbell_id); + __create_doorbell(client); - ret = __create_doorbell(client); - if (ret) - goto err; + ret = __guc_allocate_doorbell(client->guc, client->stage_id); + if (ret) { + __destroy_doorbell(client); + __update_doorbell_desc(client, GUC_DOORBELL_INVALID); + DRM_ERROR("Couldn't create client %u doorbell: %d\n", + client->stage_id, ret); + return ret; + } return 0; - -err: - __update_doorbell_desc(client, GUC_DOORBELL_INVALID); - __unreserve_doorbell(client); - return ret; } static int destroy_doorbell(struct intel_guc_client *client) { - int err; + int ret; GEM_BUG_ON(!has_doorbell(client)); - /* XXX: wait for any interrupts */ - /* XXX: wait for workqueue to drain */ - - err = __destroy_doorbell(client); - if (err) - return err; + __destroy_doorbell(client); + ret = __guc_deallocate_doorbell(client->guc, client->stage_id); + if (ret) + DRM_ERROR("Couldn't destroy client %u doorbell: %d\n", + client->stage_id, ret); __update_doorbell_desc(client, GUC_DOORBELL_INVALID); - __unreserve_doorbell(client); - - return 0; + return ret; } static unsigned long __select_cacheline(struct intel_guc *guc) @@ -447,33 +428,6 @@ static void guc_stage_desc_fini(struct intel_guc *guc, memset(desc, 0, sizeof(*desc)); } -static int guc_shared_data_create(struct intel_guc *guc) -{ - struct i915_vma *vma; - void *vaddr; - - vma = intel_guc_allocate_vma(guc, PAGE_SIZE); - if (IS_ERR(vma)) - return PTR_ERR(vma); - - vaddr = i915_gem_object_pin_map(vma->obj, I915_MAP_WB); - if (IS_ERR(vaddr)) { - i915_vma_unpin_and_release(&vma); - return PTR_ERR(vaddr); - } - - guc->shared_data = vma; - guc->shared_data_vaddr = vaddr; - - return 0; -} - -static void guc_shared_data_destroy(struct intel_guc *guc) -{ - i915_gem_object_unpin_map(guc->shared_data->obj); - i915_vma_unpin_and_release(&guc->shared_data); -} - /* Construct a Work Item and append it to the GuC's Work Queue */ static void guc_wq_item_append(struct intel_guc_client *client, u32 target_engine, u32 context_desc, @@ -718,12 +672,9 @@ static void guc_submit(struct intel_engine_cs *engine) static void port_assign(struct execlist_port *port, struct drm_i915_gem_request *rq) { - GEM_BUG_ON(rq == port_request(port)); - - if (port_isset(port)) - i915_gem_request_put(port_request(port)); + GEM_BUG_ON(port_isset(port)); - port_set(port, port_pack(i915_gem_request_get(rq), port_count(port))); + port_set(port, i915_gem_request_get(rq)); } static void guc_dequeue(struct intel_engine_cs *engine) @@ -743,23 +694,26 @@ static void guc_dequeue(struct intel_engine_cs *engine) if (!rb) goto unlock; - if (HAS_LOGICAL_RING_PREEMPTION(engine->i915) && port_isset(port)) { - struct guc_preempt_work *preempt_work = - &engine->i915->guc.preempt_work[engine->id]; - - if (rb_entry(rb, struct i915_priolist, node)->priority > - max(port_request(port)->priotree.priority, 0)) { - execlists_set_active(execlists, - EXECLISTS_ACTIVE_PREEMPT); - queue_work(engine->i915->guc.preempt_wq, - &preempt_work->work); - goto unlock; - } else if (port_isset(last_port)) { - goto unlock; + if (port_isset(port)) { + if (HAS_LOGICAL_RING_PREEMPTION(engine->i915)) { + struct guc_preempt_work *preempt_work = + &engine->i915->guc.preempt_work[engine->id]; + + if (rb_entry(rb, struct i915_priolist, node)->priority > + max(port_request(port)->priotree.priority, 0)) { + execlists_set_active(execlists, + EXECLISTS_ACTIVE_PREEMPT); + queue_work(engine->i915->guc.preempt_wq, + &preempt_work->work); + goto unlock; + } } port++; + if (port_isset(port)) + goto unlock; } + GEM_BUG_ON(port_isset(port)); do { struct i915_priolist *p = rb_entry(rb, typeof(*p), node); @@ -866,83 +820,47 @@ static bool doorbell_ok(struct intel_guc *guc, u16 db_id) return false; } -/* - * If the GuC thinks that the doorbell is unassigned (e.g. because we reset and - * reloaded the GuC FW) we can use this function to tell the GuC to reassign the - * doorbell to the rightful owner. - */ -static int __reset_doorbell(struct intel_guc_client *client, u16 db_id) +static bool guc_verify_doorbells(struct intel_guc *guc) { - int err; + u16 db_id; - __update_doorbell_desc(client, db_id); - err = __create_doorbell(client); - if (!err) - err = __destroy_doorbell(client); + for (db_id = 0; db_id < GUC_NUM_DOORBELLS; ++db_id) + if (!doorbell_ok(guc, db_id)) + return false; - return err; + return true; } -/* - * Set up & tear down each unused doorbell in turn, to ensure that all doorbell - * HW is (re)initialised. For that end, we might have to borrow the first - * client. Also, tell GuC about all the doorbells in use by all clients. - * We do this because the KMD, the GuC and the doorbell HW can easily go out of - * sync (e.g. we can reset the GuC, but not the doorbel HW). - */ -static int guc_init_doorbell_hw(struct intel_guc *guc) +static int guc_clients_doorbell_init(struct intel_guc *guc) { - struct intel_guc_client *client = guc->execbuf_client; - bool recreate_first_client = false; - u16 db_id; int ret; - /* For unused doorbells, make sure they are disabled */ - for_each_clear_bit(db_id, guc->doorbell_bitmap, GUC_NUM_DOORBELLS) { - if (doorbell_ok(guc, db_id)) - continue; - - if (has_doorbell(client)) { - /* Borrow execbuf_client (we will recreate it later) */ - destroy_doorbell(client); - recreate_first_client = true; - } - - ret = __reset_doorbell(client, db_id); - WARN(ret, "Doorbell %u reset failed, err %d\n", db_id, ret); - } - - if (recreate_first_client) { - ret = __reserve_doorbell(client); - if (unlikely(ret)) { - DRM_ERROR("Couldn't re-reserve first client db: %d\n", - ret); - return ret; - } - - __update_doorbell_desc(client, client->doorbell_id); - } - - /* Now for every client (and not only execbuf_client) make sure their - * doorbells are known by the GuC - */ - ret = __create_doorbell(guc->execbuf_client); + ret = create_doorbell(guc->execbuf_client); if (ret) return ret; - ret = __create_doorbell(guc->preempt_client); + ret = create_doorbell(guc->preempt_client); if (ret) { - __destroy_doorbell(guc->execbuf_client); + destroy_doorbell(guc->execbuf_client); return ret; } - /* Read back & verify all (used & unused) doorbell registers */ - for (db_id = 0; db_id < GUC_NUM_DOORBELLS; ++db_id) - WARN_ON(!doorbell_ok(guc, db_id)); - return 0; } +static void guc_clients_doorbell_fini(struct intel_guc *guc) +{ + /* + * By the time we're here, GuC has already been reset. + * Instead of trying (in vain) to communicate with it, let's just + * cleanup the doorbell HW and our internal state. + */ + __destroy_doorbell(guc->preempt_client); + __update_doorbell_desc(guc->preempt_client, GUC_DOORBELL_INVALID); + __destroy_doorbell(guc->execbuf_client); + __update_doorbell_desc(guc->execbuf_client, GUC_DOORBELL_INVALID); +} + /** * guc_client_alloc() - Allocate an intel_guc_client * @dev_priv: driver private data structure @@ -1018,7 +936,7 @@ guc_client_alloc(struct drm_i915_private *dev_priv, guc_proc_desc_init(guc, client); guc_stage_desc_init(guc, client); - ret = create_doorbell(client); + ret = reserve_doorbell(client); if (ret) goto err_vaddr; @@ -1042,16 +960,7 @@ err_client: static void guc_client_free(struct intel_guc_client *client) { - /* - * XXX: wait for any outstanding submissions before freeing memory. - * Be sure to drop any locks - */ - - /* FIXME: in many cases, by the time we get here the GuC has been - * reset, so we cannot destroy the doorbell properly. Ignore the - * error message for now - */ - destroy_doorbell(client); + unreserve_doorbell(client); guc_stage_desc_fini(client->guc, client); i915_gem_object_unpin_map(client->vma->obj); i915_vma_unpin_and_release(&client->vma); @@ -1214,57 +1123,15 @@ static void guc_ads_destroy(struct intel_guc *guc) i915_vma_unpin_and_release(&guc->ads_vma); } -static int guc_preempt_work_create(struct intel_guc *guc) -{ - struct drm_i915_private *dev_priv = guc_to_i915(guc); - struct intel_engine_cs *engine; - enum intel_engine_id id; - - /* - * Even though both sending GuC action, and adding a new workitem to - * GuC workqueue are serialized (each with its own locking), since - * we're using mutliple engines, it's possible that we're going to - * issue a preempt request with two (or more - each for different - * engine) workitems in GuC queue. In this situation, GuC may submit - * all of them, which will make us very confused. - * Our preemption contexts may even already be complete - before we - * even had the chance to sent the preempt action to GuC!. Rather - * than introducing yet another lock, we can just use ordered workqueue - * to make sure we're always sending a single preemption request with a - * single workitem. - */ - guc->preempt_wq = alloc_ordered_workqueue("i915-guc_preempt", - WQ_HIGHPRI); - if (!guc->preempt_wq) - return -ENOMEM; - - for_each_engine(engine, dev_priv, id) { - guc->preempt_work[id].engine = engine; - INIT_WORK(&guc->preempt_work[id].work, inject_preempt_context); - } - - return 0; -} - -static void guc_preempt_work_destroy(struct intel_guc *guc) -{ - struct drm_i915_private *dev_priv = guc_to_i915(guc); - struct intel_engine_cs *engine; - enum intel_engine_id id; - - for_each_engine(engine, dev_priv, id) - cancel_work_sync(&guc->preempt_work[id].work); - - destroy_workqueue(guc->preempt_wq); - guc->preempt_wq = NULL; -} - /* * Set up the memory resources to be shared with the GuC (via the GGTT) * at firmware loading time. */ int intel_guc_submission_init(struct intel_guc *guc) { + struct drm_i915_private *dev_priv = guc_to_i915(guc); + struct intel_engine_cs *engine; + enum intel_engine_id id; int ret; if (guc->stage_desc_pool) @@ -1279,33 +1146,29 @@ int intel_guc_submission_init(struct intel_guc *guc) */ GEM_BUG_ON(!guc->stage_desc_pool); - ret = guc_shared_data_create(guc); - if (ret) - goto err_stage_desc_pool; - GEM_BUG_ON(!guc->shared_data); - ret = intel_guc_log_create(guc); if (ret < 0) - goto err_shared_data; - - ret = guc_preempt_work_create(guc); - if (ret) - goto err_log; - GEM_BUG_ON(!guc->preempt_wq); + goto err_stage_desc_pool; ret = guc_ads_create(guc); if (ret < 0) - goto err_wq; + goto err_log; GEM_BUG_ON(!guc->ads_vma); + WARN_ON(!guc_verify_doorbells(guc)); + ret = guc_clients_create(guc); + if (ret) + return ret; + + for_each_engine(engine, dev_priv, id) { + guc->preempt_work[id].engine = engine; + INIT_WORK(&guc->preempt_work[id].work, inject_preempt_context); + } + return 0; -err_wq: - guc_preempt_work_destroy(guc); err_log: intel_guc_log_destroy(guc); -err_shared_data: - guc_shared_data_destroy(guc); err_stage_desc_pool: guc_stage_desc_pool_destroy(guc); return ret; @@ -1313,10 +1176,18 @@ err_stage_desc_pool: void intel_guc_submission_fini(struct intel_guc *guc) { + struct drm_i915_private *dev_priv = guc_to_i915(guc); + struct intel_engine_cs *engine; + enum intel_engine_id id; + + for_each_engine(engine, dev_priv, id) + cancel_work_sync(&guc->preempt_work[id].work); + + guc_clients_destroy(guc); + WARN_ON(!guc_verify_doorbells(guc)); + guc_ads_destroy(guc); - guc_preempt_work_destroy(guc); intel_guc_log_destroy(guc); - guc_shared_data_destroy(guc); guc_stage_desc_pool_destroy(guc); } @@ -1420,28 +1291,18 @@ int intel_guc_submission_enable(struct intel_guc *guc) sizeof(struct guc_wq_item) * I915_NUM_ENGINES > GUC_WQ_SIZE); - /* - * We're being called on both module initialization and on reset, - * until this flow is changed, we're using regular client presence to - * determine which case are we in, and whether we should allocate new - * clients or just reset their workqueues. - */ - if (!guc->execbuf_client) { - err = guc_clients_create(guc); - if (err) - return err; - } else { - guc_reset_wq(guc->execbuf_client); - guc_reset_wq(guc->preempt_client); - } + GEM_BUG_ON(!guc->execbuf_client); + + guc_reset_wq(guc->execbuf_client); + guc_reset_wq(guc->preempt_client); err = intel_guc_sample_forcewake(guc); if (err) - goto err_free_clients; + return err; - err = guc_init_doorbell_hw(guc); + err = guc_clients_doorbell_init(guc); if (err) - goto err_free_clients; + return err; /* Take over from manual control of ELSP (execlists) */ guc_interrupts_capture(dev_priv); @@ -1453,13 +1314,11 @@ int intel_guc_submission_enable(struct intel_guc *guc) execlists->tasklet.func = guc_submission_tasklet; engine->park = guc_submission_park; engine->unpark = guc_submission_unpark; + + engine->flags &= ~I915_ENGINE_SUPPORTS_STATS; } return 0; - -err_free_clients: - guc_clients_destroy(guc); - return err; } void intel_guc_submission_disable(struct intel_guc *guc) @@ -1469,11 +1328,10 @@ void intel_guc_submission_disable(struct intel_guc *guc) GEM_BUG_ON(dev_priv->gt.awake); /* GT should be parked first */ guc_interrupts_release(dev_priv); + guc_clients_doorbell_fini(guc); /* Revert back to manual ELSP submission */ intel_engines_reset_default_submission(dev_priv); - - guc_clients_destroy(guc); } #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST) diff --git a/drivers/gpu/drm/i915/intel_guc_submission.h b/drivers/gpu/drm/i915/intel_guc_submission.h index e901192ee469..fb081cefef93 100644 --- a/drivers/gpu/drm/i915/intel_guc_submission.h +++ b/drivers/gpu/drm/i915/intel_guc_submission.h @@ -22,8 +22,8 @@ * */ -#ifndef _I915_GUC_SUBMISSION_H_ -#define _I915_GUC_SUBMISSION_H_ +#ifndef _INTEL_GUC_SUBMISSION_H_ +#define _INTEL_GUC_SUBMISSION_H_ #include <linux/spinlock.h> @@ -77,5 +77,7 @@ int intel_guc_submission_init(struct intel_guc *guc); int intel_guc_submission_enable(struct intel_guc *guc); void intel_guc_submission_disable(struct intel_guc *guc); void intel_guc_submission_fini(struct intel_guc *guc); +int intel_guc_preempt_work_create(struct intel_guc *guc); +void intel_guc_preempt_work_destroy(struct intel_guc *guc); #endif diff --git a/drivers/gpu/drm/i915/intel_gvt.c b/drivers/gpu/drm/i915/intel_gvt.c index b4a7f31f0214..a2fe7c8d4477 100644 --- a/drivers/gpu/drm/i915/intel_gvt.c +++ b/drivers/gpu/drm/i915/intel_gvt.c @@ -95,12 +95,7 @@ int intel_gvt_init(struct drm_i915_private *dev_priv) return 0; } - if (!i915_modparams.enable_execlists) { - DRM_ERROR("i915 GVT-g loading failed due to disabled execlists mode\n"); - return -EIO; - } - - if (i915_modparams.enable_guc_submission) { + if (USES_GUC_SUBMISSION(dev_priv)) { DRM_ERROR("i915 GVT-g loading failed due to Graphics virtualization is not yet supported with GuC submission\n"); return -EIO; } diff --git a/drivers/gpu/drm/i915/intel_hangcheck.c b/drivers/gpu/drm/i915/intel_hangcheck.c index 12ac270a5f93..0acd9dd3ed5c 100644 --- a/drivers/gpu/drm/i915/intel_hangcheck.c +++ b/drivers/gpu/drm/i915/intel_hangcheck.c @@ -27,13 +27,9 @@ static bool ipehr_is_semaphore_wait(struct intel_engine_cs *engine, u32 ipehr) { - if (INTEL_GEN(engine->i915) >= 8) { - return (ipehr >> 23) == 0x1c; - } else { - ipehr &= ~MI_SEMAPHORE_SYNC_MASK; - return ipehr == (MI_SEMAPHORE_MBOX | MI_SEMAPHORE_COMPARE | - MI_SEMAPHORE_REGISTER); - } + ipehr &= ~MI_SEMAPHORE_SYNC_MASK; + return ipehr == (MI_SEMAPHORE_MBOX | MI_SEMAPHORE_COMPARE | + MI_SEMAPHORE_REGISTER); } static struct intel_engine_cs * @@ -41,31 +37,20 @@ semaphore_wait_to_signaller_ring(struct intel_engine_cs *engine, u32 ipehr, u64 offset) { struct drm_i915_private *dev_priv = engine->i915; + u32 sync_bits = ipehr & MI_SEMAPHORE_SYNC_MASK; struct intel_engine_cs *signaller; enum intel_engine_id id; - if (INTEL_GEN(dev_priv) >= 8) { - for_each_engine(signaller, dev_priv, id) { - if (engine == signaller) - continue; - - if (offset == signaller->semaphore.signal_ggtt[engine->hw_id]) - return signaller; - } - } else { - u32 sync_bits = ipehr & MI_SEMAPHORE_SYNC_MASK; - - for_each_engine(signaller, dev_priv, id) { - if(engine == signaller) - continue; + for_each_engine(signaller, dev_priv, id) { + if (engine == signaller) + continue; - if (sync_bits == signaller->semaphore.mbox.wait[engine->hw_id]) - return signaller; - } + if (sync_bits == signaller->semaphore.mbox.wait[engine->hw_id]) + return signaller; } - DRM_DEBUG_DRIVER("No signaller ring found for %s, ipehr 0x%08x, offset 0x%016llx\n", - engine->name, ipehr, offset); + DRM_DEBUG_DRIVER("No signaller ring found for %s, ipehr 0x%08x\n", + engine->name, ipehr); return ERR_PTR(-ENODEV); } @@ -135,11 +120,6 @@ semaphore_waits_for(struct intel_engine_cs *engine, u32 *seqno) return NULL; *seqno = ioread32(vaddr + head + 4) + 1; - if (INTEL_GEN(dev_priv) >= 8) { - offset = ioread32(vaddr + head + 12); - offset <<= 32; - offset |= ioread32(vaddr + head + 8); - } return semaphore_wait_to_signaller_ring(engine, ipehr, offset); } @@ -273,7 +253,7 @@ engine_stuck(struct intel_engine_cs *engine, u64 acthd) return ENGINE_WAIT_KICK; } - if (INTEL_GEN(dev_priv) >= 6 && tmp & RING_WAIT_SEMAPHORE) { + if (IS_GEN(dev_priv, 6, 7) && tmp & RING_WAIT_SEMAPHORE) { switch (semaphore_passed(engine)) { default: return ENGINE_DEAD; @@ -369,13 +349,18 @@ static void hangcheck_accumulate_sample(struct intel_engine_cs *engine, case ENGINE_ACTIVE_HEAD: case ENGINE_ACTIVE_SUBUNITS: - /* Seqno stuck with still active engine gets leeway, + /* + * Seqno stuck with still active engine gets leeway, * in hopes that it is just a long shader. */ timeout = I915_SEQNO_DEAD_TIMEOUT; break; case ENGINE_DEAD: + if (drm_debug & DRM_UT_DRIVER) { + struct drm_printer p = drm_debug_printer("hangcheck"); + intel_engine_dump(engine, &p, "%s", engine->name); + } break; default: diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index a40f35af225c..bced7b954d93 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -1383,7 +1383,7 @@ static bool hdmi_12bpc_possible(const struct intel_crtc_state *crtc_state) } } - /* Display Wa #1139 */ + /* Display WA #1139: glk */ if (IS_GLK_REVID(dev_priv, 0, GLK_REVID_A1) && crtc_state->base.adjusted_mode.htotal > 5460) return false; diff --git a/drivers/gpu/drm/i915/intel_huc.c b/drivers/gpu/drm/i915/intel_huc.c index 98d17254593c..974be3defa70 100644 --- a/drivers/gpu/drm/i915/intel_huc.c +++ b/drivers/gpu/drm/i915/intel_huc.c @@ -77,43 +77,57 @@ MODULE_FIRMWARE(I915_KBL_HUC_UCODE); #define I915_GLK_HUC_UCODE HUC_FW_PATH(glk, GLK_HUC_FW_MAJOR, \ GLK_HUC_FW_MINOR, GLK_BLD_NUM) -/** - * intel_huc_select_fw() - selects HuC firmware for loading - * @huc: intel_huc struct - */ -void intel_huc_select_fw(struct intel_huc *huc) +static void huc_fw_select(struct intel_uc_fw *huc_fw) { + struct intel_huc *huc = container_of(huc_fw, struct intel_huc, fw); struct drm_i915_private *dev_priv = huc_to_i915(huc); - intel_uc_fw_init(&huc->fw, INTEL_UC_FW_TYPE_HUC); + GEM_BUG_ON(huc_fw->type != INTEL_UC_FW_TYPE_HUC); + + if (!HAS_HUC(dev_priv)) + return; if (i915_modparams.huc_firmware_path) { - huc->fw.path = i915_modparams.huc_firmware_path; - huc->fw.major_ver_wanted = 0; - huc->fw.minor_ver_wanted = 0; + huc_fw->path = i915_modparams.huc_firmware_path; + huc_fw->major_ver_wanted = 0; + huc_fw->minor_ver_wanted = 0; } else if (IS_SKYLAKE(dev_priv)) { - huc->fw.path = I915_SKL_HUC_UCODE; - huc->fw.major_ver_wanted = SKL_HUC_FW_MAJOR; - huc->fw.minor_ver_wanted = SKL_HUC_FW_MINOR; + huc_fw->path = I915_SKL_HUC_UCODE; + huc_fw->major_ver_wanted = SKL_HUC_FW_MAJOR; + huc_fw->minor_ver_wanted = SKL_HUC_FW_MINOR; } else if (IS_BROXTON(dev_priv)) { - huc->fw.path = I915_BXT_HUC_UCODE; - huc->fw.major_ver_wanted = BXT_HUC_FW_MAJOR; - huc->fw.minor_ver_wanted = BXT_HUC_FW_MINOR; + huc_fw->path = I915_BXT_HUC_UCODE; + huc_fw->major_ver_wanted = BXT_HUC_FW_MAJOR; + huc_fw->minor_ver_wanted = BXT_HUC_FW_MINOR; } else if (IS_KABYLAKE(dev_priv) || IS_COFFEELAKE(dev_priv)) { - huc->fw.path = I915_KBL_HUC_UCODE; - huc->fw.major_ver_wanted = KBL_HUC_FW_MAJOR; - huc->fw.minor_ver_wanted = KBL_HUC_FW_MINOR; + huc_fw->path = I915_KBL_HUC_UCODE; + huc_fw->major_ver_wanted = KBL_HUC_FW_MAJOR; + huc_fw->minor_ver_wanted = KBL_HUC_FW_MINOR; } else if (IS_GEMINILAKE(dev_priv)) { - huc->fw.path = I915_GLK_HUC_UCODE; - huc->fw.major_ver_wanted = GLK_HUC_FW_MAJOR; - huc->fw.minor_ver_wanted = GLK_HUC_FW_MINOR; + huc_fw->path = I915_GLK_HUC_UCODE; + huc_fw->major_ver_wanted = GLK_HUC_FW_MAJOR; + huc_fw->minor_ver_wanted = GLK_HUC_FW_MINOR; } else { - DRM_ERROR("No HuC firmware known for platform with HuC!\n"); - return; + DRM_WARN("%s: No firmware known for this platform!\n", + intel_uc_fw_type_repr(huc_fw->type)); } } /** + * intel_huc_init_early() - initializes HuC struct + * @huc: intel_huc struct + * + * On platforms with HuC selects firmware for uploading + */ +void intel_huc_init_early(struct intel_huc *huc) +{ + struct intel_uc_fw *huc_fw = &huc->fw; + + intel_uc_fw_init(huc_fw, INTEL_UC_FW_TYPE_HUC); + huc_fw_select(huc_fw); +} + +/** * huc_ucode_xfer() - DMA's the firmware * @dev_priv: the drm_i915_private device * @@ -167,17 +181,17 @@ static int huc_ucode_xfer(struct intel_uc_fw *huc_fw, struct i915_vma *vma) * intel_huc_init_hw() - load HuC uCode to device * @huc: intel_huc structure * - * Called from guc_setup() during driver loading and also after a GPU reset. - * Be note that HuC loading must be done before GuC loading. + * Called from intel_uc_init_hw() during driver loading and also after a GPU + * reset. Be note that HuC loading must be done before GuC loading. * * The firmware image should have already been fetched into memory by the - * earlier call to intel_huc_init(), so here we need only check that + * earlier call to intel_uc_init_fw(), so here we need only check that * is succeeded, and then transfer the image to the h/w. * */ -void intel_huc_init_hw(struct intel_huc *huc) +int intel_huc_init_hw(struct intel_huc *huc) { - intel_uc_fw_upload(&huc->fw, huc_ucode_xfer); + return intel_uc_fw_upload(&huc->fw, huc_ucode_xfer); } /** @@ -191,7 +205,7 @@ void intel_huc_init_hw(struct intel_huc *huc) * signature through intel_guc_auth_huc(). It then waits for 50ms for * firmware verification ACK and unpins the object. */ -void intel_huc_auth(struct intel_huc *huc) +int intel_huc_auth(struct intel_huc *huc) { struct drm_i915_private *i915 = huc_to_i915(huc); struct intel_guc *guc = &i915->guc; @@ -199,14 +213,14 @@ void intel_huc_auth(struct intel_huc *huc) int ret; if (huc->fw.load_status != INTEL_UC_FIRMWARE_SUCCESS) - return; + return -ENOEXEC; vma = i915_gem_object_ggtt_pin(huc->fw.obj, NULL, 0, 0, PIN_OFFSET_BIAS | GUC_WOPCM_TOP); if (IS_ERR(vma)) { - DRM_ERROR("failed to pin huc fw object %d\n", - (int)PTR_ERR(vma)); - return; + ret = PTR_ERR(vma); + DRM_ERROR("HuC: Failed to pin huc fw object %d\n", ret); + return ret; } ret = intel_guc_auth_huc(guc, @@ -229,4 +243,5 @@ void intel_huc_auth(struct intel_huc *huc) out: i915_vma_unpin(vma); + return ret; } diff --git a/drivers/gpu/drm/i915/intel_huc.h b/drivers/gpu/drm/i915/intel_huc.h index aaa38b9e5817..40039db59e04 100644 --- a/drivers/gpu/drm/i915/intel_huc.h +++ b/drivers/gpu/drm/i915/intel_huc.h @@ -34,8 +34,8 @@ struct intel_huc { /* HuC-specific additions */ }; -void intel_huc_select_fw(struct intel_huc *huc); -void intel_huc_init_hw(struct intel_huc *huc); -void intel_huc_auth(struct intel_huc *huc); +void intel_huc_init_early(struct intel_huc *huc); +int intel_huc_init_hw(struct intel_huc *huc); +int intel_huc_auth(struct intel_huc *huc); #endif diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index be6c39adebdf..2e38fbfdf08f 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -154,9 +154,7 @@ #define GEN8_CTX_STATUS_LITE_RESTORE (1 << 15) #define GEN8_CTX_STATUS_COMPLETED_MASK \ - (GEN8_CTX_STATUS_ACTIVE_IDLE | \ - GEN8_CTX_STATUS_PREEMPTED | \ - GEN8_CTX_STATUS_ELEMENT_SWITCH) + (GEN8_CTX_STATUS_COMPLETE | GEN8_CTX_STATUS_PREEMPTED) #define CTX_LRI_HEADER_0 0x01 #define CTX_CONTEXT_CONTROL 0x02 @@ -221,37 +219,6 @@ static void execlists_init_reg_state(u32 *reg_state, struct intel_ring *ring); /** - * intel_sanitize_enable_execlists() - sanitize i915.enable_execlists - * @dev_priv: i915 device private - * @enable_execlists: value of i915.enable_execlists module parameter. - * - * Only certain platforms support Execlists (the prerequisites being - * support for Logical Ring Contexts and Aliasing PPGTT or better). - * - * Return: 1 if Execlists is supported and has to be enabled. - */ -int intel_sanitize_enable_execlists(struct drm_i915_private *dev_priv, int enable_execlists) -{ - /* On platforms with execlist available, vGPU will only - * support execlist mode, no ring buffer mode. - */ - if (HAS_LOGICAL_RING_CONTEXTS(dev_priv) && intel_vgpu_active(dev_priv)) - return 1; - - if (INTEL_GEN(dev_priv) >= 9) - return 1; - - if (enable_execlists == 0) - return 0; - - if (HAS_LOGICAL_RING_CONTEXTS(dev_priv) && - USES_PPGTT(dev_priv)) - return 1; - - return 0; -} - -/** * intel_lr_context_descriptor_update() - calculate & cache the descriptor * descriptor for a pinned context * @ctx: Context to work on @@ -412,6 +379,20 @@ execlists_context_status_change(struct drm_i915_gem_request *rq, status, rq); } +static inline void +execlists_context_schedule_in(struct drm_i915_gem_request *rq) +{ + execlists_context_status_change(rq, INTEL_CONTEXT_SCHEDULE_IN); + intel_engine_context_in(rq->engine); +} + +static inline void +execlists_context_schedule_out(struct drm_i915_gem_request *rq) +{ + intel_engine_context_out(rq->engine); + execlists_context_status_change(rq, INTEL_CONTEXT_SCHEDULE_OUT); +} + static void execlists_update_context_pdps(struct i915_hw_ppgtt *ppgtt, u32 *reg_state) { @@ -450,8 +431,6 @@ static inline void elsp_write(u64 desc, u32 __iomem *elsp) static void execlists_submit_ports(struct intel_engine_cs *engine) { struct execlist_port *port = engine->execlists.port; - u32 __iomem *elsp = - engine->i915->regs + i915_mmio_reg_offset(RING_ELSP(engine)); unsigned int n; for (n = execlists_num_ports(&engine->execlists); n--; ) { @@ -463,7 +442,7 @@ static void execlists_submit_ports(struct intel_engine_cs *engine) if (rq) { GEM_BUG_ON(count > !n); if (!count++) - execlists_context_status_change(rq, INTEL_CONTEXT_SCHEDULE_IN); + execlists_context_schedule_in(rq); port_set(&port[n], port_pack(rq, count)); desc = execlists_update_context(rq); GEM_DEBUG_EXEC(port[n].context_id = upper_32_bits(desc)); @@ -477,8 +456,9 @@ static void execlists_submit_ports(struct intel_engine_cs *engine) desc = 0; } - elsp_write(desc, elsp); + elsp_write(desc, engine->execlists.elsp); } + execlists_clear_active(&engine->execlists, EXECLISTS_ACTIVE_HWACK); } static bool ctx_single_port_submission(const struct i915_gem_context *ctx) @@ -514,8 +494,6 @@ static void inject_preempt_context(struct intel_engine_cs *engine) { struct intel_context *ce = &engine->i915->preempt_context->engine[engine->id]; - u32 __iomem *elsp = - engine->i915->regs + i915_mmio_reg_offset(RING_ELSP(engine)); unsigned int n; GEM_BUG_ON(engine->i915->preempt_context->hw_id != PREEMPT_ID); @@ -528,9 +506,10 @@ static void inject_preempt_context(struct intel_engine_cs *engine) GEM_TRACE("\n"); for (n = execlists_num_ports(&engine->execlists); --n; ) - elsp_write(0, elsp); + elsp_write(0, engine->execlists.elsp); - elsp_write(ce->lrc_desc, elsp); + elsp_write(ce->lrc_desc, engine->execlists.elsp); + execlists_clear_active(&engine->execlists, EXECLISTS_ACTIVE_HWACK); } static void execlists_dequeue(struct intel_engine_cs *engine) @@ -577,9 +556,20 @@ static void execlists_dequeue(struct intel_engine_cs *engine) * know the next preemption status we see corresponds * to this ELSP update. */ + GEM_BUG_ON(!port_count(&port[0])); if (port_count(&port[0]) > 1) goto unlock; + /* + * If we write to ELSP a second time before the HW has had + * a chance to respond to the previous write, we can confuse + * the HW and hit "undefined behaviour". After writing to ELSP, + * we must then wait until we see a context-switch event from + * the HW to indicate that it has had a chance to respond. + */ + if (!execlists_is_active(execlists, EXECLISTS_ACTIVE_HWACK)) + goto unlock; + if (HAS_LOGICAL_RING_PREEMPTION(engine->i915) && rb_entry(rb, struct i915_priolist, node)->priority > max(last->priotree.priority, 0)) { @@ -713,6 +703,7 @@ execlists_cancel_port_requests(struct intel_engine_execlists * const execlists) struct drm_i915_gem_request *rq = port_request(port); GEM_BUG_ON(!execlists->active); + intel_engine_context_out(rq->engine); execlists_context_status_change(rq, INTEL_CONTEXT_SCHEDULE_PREEMPTED); i915_gem_request_put(rq); @@ -873,10 +864,22 @@ static void execlists_submission_tasklet(unsigned long data) GEM_TRACE("%s csb[%dd]: status=0x%08x:0x%08x\n", engine->name, head, status, buf[2*head + 1]); + + if (status & (GEN8_CTX_STATUS_IDLE_ACTIVE | + GEN8_CTX_STATUS_PREEMPTED)) + execlists_set_active(execlists, + EXECLISTS_ACTIVE_HWACK); + if (status & GEN8_CTX_STATUS_ACTIVE_IDLE) + execlists_clear_active(execlists, + EXECLISTS_ACTIVE_HWACK); + if (!(status & GEN8_CTX_STATUS_COMPLETED_MASK)) continue; - if (status & GEN8_CTX_STATUS_ACTIVE_IDLE && + /* We should never get a COMPLETED | IDLE_ACTIVE! */ + GEM_BUG_ON(status & GEN8_CTX_STATUS_IDLE_ACTIVE); + + if (status & GEN8_CTX_STATUS_COMPLETE && buf[2*head + 1] == PREEMPT_ID) { execlists_cancel_port_requests(execlists); execlists_unwind_incomplete_requests(execlists); @@ -907,9 +910,10 @@ static void execlists_submission_tasklet(unsigned long data) GEM_BUG_ON(count == 0); if (--count == 0) { GEM_BUG_ON(status & GEN8_CTX_STATUS_PREEMPTED); + GEM_BUG_ON(port_isset(&port[1]) && + !(status & GEN8_CTX_STATUS_ELEMENT_SWITCH)); GEM_BUG_ON(!i915_gem_request_completed(rq)); - execlists_context_status_change(rq, INTEL_CONTEXT_SCHEDULE_OUT); - + execlists_context_schedule_out(rq); trace_i915_gem_request_out(rq); i915_gem_request_put(rq); @@ -1501,6 +1505,9 @@ static int gen8_init_common_ring(struct intel_engine_cs *engine) execlists->csb_head = -1; execlists->active = 0; + execlists->elsp = + dev_priv->regs + i915_mmio_reg_offset(RING_ELSP(engine)); + /* After a GPU reset, we may have requests to replay */ if (execlists->first) tasklet_schedule(&execlists->tasklet); @@ -1911,6 +1918,8 @@ static void execlists_set_default_submission(struct intel_engine_cs *engine) engine->park = NULL; engine->unpark = NULL; + + engine->flags |= I915_ENGINE_SUPPORTS_STATS; } static void diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h index 17182ce29674..6d4f9b995a11 100644 --- a/drivers/gpu/drm/i915/intel_lrc.h +++ b/drivers/gpu/drm/i915/intel_lrc.h @@ -107,8 +107,4 @@ intel_lr_context_descriptor(struct i915_gem_context *ctx, return ctx->engine[engine->id].lrc_desc; } -/* Execlists */ -int intel_sanitize_enable_execlists(struct drm_i915_private *dev_priv, - int enable_execlists); - #endif /* _INTEL_LRC_H_ */ diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index 1b397b41cb4f..41e9465d44a8 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -219,7 +219,7 @@ intel_overlay_map_regs(struct intel_overlay *overlay) if (OVERLAY_NEEDS_PHYSICAL(dev_priv)) regs = (struct overlay_registers __iomem *)overlay->reg_bo->phys_handle->vaddr; else - regs = io_mapping_map_wc(&dev_priv->ggtt.mappable, + regs = io_mapping_map_wc(&dev_priv->ggtt.iomap, overlay->flip_addr, PAGE_SIZE); @@ -1508,7 +1508,7 @@ intel_overlay_map_regs_atomic(struct intel_overlay *overlay) regs = (struct overlay_registers __iomem *) overlay->reg_bo->phys_handle->vaddr; else - regs = io_mapping_map_atomic_wc(&dev_priv->ggtt.mappable, + regs = io_mapping_map_atomic_wc(&dev_priv->ggtt.iomap, overlay->flip_addr); return regs; diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index adc51e452e3e..fa6831f8c004 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -543,25 +543,6 @@ static u32 pwm_get_backlight(struct intel_connector *connector) return DIV_ROUND_UP(duty_ns * 100, CRC_PMIC_PWM_PERIOD_NS); } -static u32 intel_panel_get_backlight(struct intel_connector *connector) -{ - struct drm_i915_private *dev_priv = to_i915(connector->base.dev); - struct intel_panel *panel = &connector->panel; - u32 val = 0; - - mutex_lock(&dev_priv->backlight_lock); - - if (panel->backlight.enabled) { - val = panel->backlight.get(connector); - val = intel_panel_compute_brightness(connector, val); - } - - mutex_unlock(&dev_priv->backlight_lock); - - DRM_DEBUG_DRIVER("get backlight PWM = %d\n", val); - return val; -} - static void lpt_set_backlight(const struct drm_connector_state *conn_state, u32 level) { struct intel_connector *connector = to_intel_connector(conn_state->connector); @@ -649,31 +630,6 @@ intel_panel_actually_set_backlight(const struct drm_connector_state *conn_state, panel->backlight.set(conn_state, level); } -/* set backlight brightness to level in range [0..max], scaling wrt hw min */ -static void intel_panel_set_backlight(const struct drm_connector_state *conn_state, - u32 user_level, u32 user_max) -{ - struct intel_connector *connector = to_intel_connector(conn_state->connector); - struct drm_i915_private *dev_priv = to_i915(connector->base.dev); - struct intel_panel *panel = &connector->panel; - u32 hw_level; - - if (!panel->backlight.present) - return; - - mutex_lock(&dev_priv->backlight_lock); - - WARN_ON(panel->backlight.max == 0); - - hw_level = scale_user_to_hw(connector, user_level, user_max); - panel->backlight.level = hw_level; - - if (panel->backlight.enabled) - intel_panel_actually_set_backlight(conn_state, hw_level); - - mutex_unlock(&dev_priv->backlight_lock); -} - /* set backlight brightness to level in range [0..max], assuming hw min is * respected. */ @@ -1182,6 +1138,50 @@ void intel_panel_enable_backlight(const struct intel_crtc_state *crtc_state, } #if IS_ENABLED(CONFIG_BACKLIGHT_CLASS_DEVICE) +static u32 intel_panel_get_backlight(struct intel_connector *connector) +{ + struct drm_i915_private *dev_priv = to_i915(connector->base.dev); + struct intel_panel *panel = &connector->panel; + u32 val = 0; + + mutex_lock(&dev_priv->backlight_lock); + + if (panel->backlight.enabled) { + val = panel->backlight.get(connector); + val = intel_panel_compute_brightness(connector, val); + } + + mutex_unlock(&dev_priv->backlight_lock); + + DRM_DEBUG_DRIVER("get backlight PWM = %d\n", val); + return val; +} + +/* set backlight brightness to level in range [0..max], scaling wrt hw min */ +static void intel_panel_set_backlight(const struct drm_connector_state *conn_state, + u32 user_level, u32 user_max) +{ + struct intel_connector *connector = to_intel_connector(conn_state->connector); + struct drm_i915_private *dev_priv = to_i915(connector->base.dev); + struct intel_panel *panel = &connector->panel; + u32 hw_level; + + if (!panel->backlight.present) + return; + + mutex_lock(&dev_priv->backlight_lock); + + WARN_ON(panel->backlight.max == 0); + + hw_level = scale_user_to_hw(connector, user_level, user_max); + panel->backlight.level = hw_level; + + if (panel->backlight.enabled) + intel_panel_actually_set_backlight(conn_state, hw_level); + + mutex_unlock(&dev_priv->backlight_lock); +} + static int intel_backlight_device_update_status(struct backlight_device *bd) { struct intel_connector *connector = bl_get_data(bd); diff --git a/drivers/gpu/drm/i915/intel_pipe_crc.c b/drivers/gpu/drm/i915/intel_pipe_crc.c index 61641d479b93..1f5cd572a7ff 100644 --- a/drivers/gpu/drm/i915/intel_pipe_crc.c +++ b/drivers/gpu/drm/i915/intel_pipe_crc.c @@ -541,8 +541,6 @@ retry: * completely disable it. */ pipe_config->ips_force_disable = enable; - if (pipe_config->ips_enabled == enable) - pipe_config->base.connectors_changed = true; } if (IS_HASWELL(dev_priv)) { diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 4d2cd432f739..18779c6eb4bf 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -52,16 +52,13 @@ * which brings the most power savings; deeper states save more power, but * require higher latency to switch to and wake up. */ -#define INTEL_RC6_ENABLE (1<<0) -#define INTEL_RC6p_ENABLE (1<<1) -#define INTEL_RC6pp_ENABLE (1<<2) static void gen9_init_clock_gating(struct drm_i915_private *dev_priv) { if (HAS_LLC(dev_priv)) { /* * WaCompressedResourceDisplayNewHashMode:skl,kbl - * Display WA#0390: skl,kbl + * Display WA #0390: skl,kbl * * Must match Sampler, Pixel Back End, and Media. See * WaCompressedResourceSamplerPbeMediaNewHashMode. @@ -512,38 +509,41 @@ static void vlv_get_fifo_size(struct intel_crtc_state *crtc_state) fifo_state->plane[PLANE_CURSOR] = 63; } -static int i9xx_get_fifo_size(struct drm_i915_private *dev_priv, int plane) +static int i9xx_get_fifo_size(struct drm_i915_private *dev_priv, + enum i9xx_plane_id i9xx_plane) { uint32_t dsparb = I915_READ(DSPARB); int size; size = dsparb & 0x7f; - if (plane) + if (i9xx_plane == PLANE_B) size = ((dsparb >> DSPARB_CSTART_SHIFT) & 0x7f) - size; - DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb, - plane ? "B" : "A", size); + DRM_DEBUG_KMS("FIFO size - (0x%08x) %c: %d\n", + dsparb, plane_name(i9xx_plane), size); return size; } -static int i830_get_fifo_size(struct drm_i915_private *dev_priv, int plane) +static int i830_get_fifo_size(struct drm_i915_private *dev_priv, + enum i9xx_plane_id i9xx_plane) { uint32_t dsparb = I915_READ(DSPARB); int size; size = dsparb & 0x1ff; - if (plane) + if (i9xx_plane == PLANE_B) size = ((dsparb >> DSPARB_BEND_SHIFT) & 0x1ff) - size; size >>= 1; /* Convert to cachelines */ - DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb, - plane ? "B" : "A", size); + DRM_DEBUG_KMS("FIFO size - (0x%08x) %c: %d\n", + dsparb, plane_name(i9xx_plane), size); return size; } -static int i845_get_fifo_size(struct drm_i915_private *dev_priv, int plane) +static int i845_get_fifo_size(struct drm_i915_private *dev_priv, + enum i9xx_plane_id i9xx_plane) { uint32_t dsparb = I915_READ(DSPARB); int size; @@ -551,9 +551,8 @@ static int i845_get_fifo_size(struct drm_i915_private *dev_priv, int plane) size = dsparb & 0x7f; size >>= 2; /* Convert to cachelines */ - DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb, - plane ? "B" : "A", - size); + DRM_DEBUG_KMS("FIFO size - (0x%08x) %c: %d\n", + dsparb, plane_name(i9xx_plane), size); return size; } @@ -2277,8 +2276,8 @@ static void i9xx_update_wm(struct intel_crtc *unused_crtc) else wm_info = &i830_a_wm_info; - fifo_size = dev_priv->display.get_fifo_size(dev_priv, 0); - crtc = intel_get_crtc_for_plane(dev_priv, 0); + fifo_size = dev_priv->display.get_fifo_size(dev_priv, PLANE_A); + crtc = intel_get_crtc_for_plane(dev_priv, PLANE_A); if (intel_crtc_active(crtc)) { const struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode; @@ -2304,8 +2303,8 @@ static void i9xx_update_wm(struct intel_crtc *unused_crtc) if (IS_GEN2(dev_priv)) wm_info = &i830_bc_wm_info; - fifo_size = dev_priv->display.get_fifo_size(dev_priv, 1); - crtc = intel_get_crtc_for_plane(dev_priv, 1); + fifo_size = dev_priv->display.get_fifo_size(dev_priv, PLANE_B); + crtc = intel_get_crtc_for_plane(dev_priv, PLANE_B); if (intel_crtc_active(crtc)) { const struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode; @@ -2417,7 +2416,7 @@ static void i845_update_wm(struct intel_crtc *unused_crtc) adjusted_mode = &crtc->config->base.adjusted_mode; planea_wm = intel_calculate_wm(adjusted_mode->crtc_clock, &i845_wm_info, - dev_priv->display.get_fifo_size(dev_priv, 0), + dev_priv->display.get_fifo_size(dev_priv, PLANE_A), 4, pessimal_latency_ns); fwater_lo = I915_READ(FW_BLC) & ~0xfff; fwater_lo |= (3<<8) | planea_wm; @@ -6415,29 +6414,8 @@ static void valleyview_disable_rps(struct drm_i915_private *dev_priv) I915_WRITE(GEN6_RP_CONTROL, 0); } -static void intel_print_rc6_info(struct drm_i915_private *dev_priv, u32 mode) -{ - if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { - if (mode & (GEN7_RC_CTL_TO_MODE | GEN6_RC_CTL_EI_MODE(1))) - mode = GEN6_RC_CTL_RC6_ENABLE; - else - mode = 0; - } - if (HAS_RC6p(dev_priv)) - DRM_DEBUG_DRIVER("Enabling RC6 states: " - "RC6 %s RC6p %s RC6pp %s\n", - onoff(mode & GEN6_RC_CTL_RC6_ENABLE), - onoff(mode & GEN6_RC_CTL_RC6p_ENABLE), - onoff(mode & GEN6_RC_CTL_RC6pp_ENABLE)); - - else - DRM_DEBUG_DRIVER("Enabling RC6 states: RC6 %s\n", - onoff(mode & GEN6_RC_CTL_RC6_ENABLE)); -} - static bool bxt_check_bios_rc6_setup(struct drm_i915_private *dev_priv) { - struct i915_ggtt *ggtt = &dev_priv->ggtt; bool enable_rc6 = true; unsigned long rc6_ctx_base; u32 rc_ctl; @@ -6462,9 +6440,8 @@ static bool bxt_check_bios_rc6_setup(struct drm_i915_private *dev_priv) * for this check. */ rc6_ctx_base = I915_READ(RC6_CTX_BASE) & RC6_CTX_BASE_MASK; - if (!((rc6_ctx_base >= ggtt->stolen_reserved_base) && - (rc6_ctx_base + PAGE_SIZE <= ggtt->stolen_reserved_base + - ggtt->stolen_reserved_size))) { + if (!((rc6_ctx_base >= dev_priv->dsm_reserved.start) && + (rc6_ctx_base + PAGE_SIZE < dev_priv->dsm_reserved.end))) { DRM_DEBUG_DRIVER("RC6 Base address not as expected.\n"); enable_rc6 = false; } @@ -6497,42 +6474,30 @@ static bool bxt_check_bios_rc6_setup(struct drm_i915_private *dev_priv) return enable_rc6; } -int sanitize_rc6_option(struct drm_i915_private *dev_priv, int enable_rc6) +static bool sanitize_rc6(struct drm_i915_private *i915) { - /* No RC6 before Ironlake and code is gone for ilk. */ - if (INTEL_INFO(dev_priv)->gen < 6) - return 0; + struct intel_device_info *info = mkwrite_device_info(i915); - if (!enable_rc6) - return 0; + /* Powersaving is controlled by the host when inside a VM */ + if (intel_vgpu_active(i915)) + info->has_rc6 = 0; - if (IS_GEN9_LP(dev_priv) && !bxt_check_bios_rc6_setup(dev_priv)) { + if (info->has_rc6 && + IS_GEN9_LP(i915) && !bxt_check_bios_rc6_setup(i915)) { DRM_INFO("RC6 disabled by BIOS\n"); - return 0; + info->has_rc6 = 0; } - /* Respect the kernel parameter if it is set */ - if (enable_rc6 >= 0) { - int mask; - - if (HAS_RC6p(dev_priv)) - mask = INTEL_RC6_ENABLE | INTEL_RC6p_ENABLE | - INTEL_RC6pp_ENABLE; - else - mask = INTEL_RC6_ENABLE; - - if ((enable_rc6 & mask) != enable_rc6) - DRM_DEBUG_DRIVER("Adjusting RC6 mask to %d " - "(requested %d, valid %d)\n", - enable_rc6 & mask, enable_rc6, mask); - - return enable_rc6 & mask; - } - - if (IS_IVYBRIDGE(dev_priv)) - return (INTEL_RC6_ENABLE | INTEL_RC6p_ENABLE); + /* + * We assume that we do not have any deep rc6 levels if we don't have + * have the previous rc6 level supported, i.e. we use HAS_RC6() + * as the initial coarse check for rc6 in general, moving on to + * progressively finer/deeper levels. + */ + if (!info->has_rc6 && info->has_rc6p) + info->has_rc6p = 0; - return INTEL_RC6_ENABLE; + return info->has_rc6; } static void gen6_init_rps_frequencies(struct drm_i915_private *dev_priv) @@ -6602,9 +6567,10 @@ static void gen9_enable_rps(struct drm_i915_private *dev_priv) { intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); - /* Program defaults and thresholds for RPS*/ - I915_WRITE(GEN6_RC_VIDEO_FREQ, - GEN9_FREQUENCY(dev_priv->gt_pm.rps.rp1_freq)); + /* Program defaults and thresholds for RPS */ + if (IS_GEN9(dev_priv)) + I915_WRITE(GEN6_RC_VIDEO_FREQ, + GEN9_FREQUENCY(dev_priv->gt_pm.rps.rp1_freq)); /* 1 second timeout*/ I915_WRITE(GEN6_RP_DOWN_TIMEOUT, @@ -6624,7 +6590,7 @@ static void gen9_enable_rc6(struct drm_i915_private *dev_priv) { struct intel_engine_cs *engine; enum intel_engine_id id; - u32 rc6_mode, rc6_mask = 0; + u32 rc6_mode; /* 1a: Software RC state - RC0 */ I915_WRITE(GEN6_RC_STATE, 0); @@ -6665,9 +6631,6 @@ static void gen9_enable_rc6(struct drm_i915_private *dev_priv) I915_WRITE(GEN9_RENDER_PG_IDLE_HYSTERESIS, 25); /* 3a: Enable RC6 */ - if (intel_rc6_enabled() & INTEL_RC6_ENABLE) - rc6_mask = GEN6_RC_CTL_RC6_ENABLE; - DRM_INFO("RC6 %s\n", onoff(rc6_mask & GEN6_RC_CTL_RC6_ENABLE)); I915_WRITE(GEN6_RC6_THRESHOLD, 37500); /* 37.5/125ms per EI */ /* WaRsUseTimeoutMode:cnl (pre-prod) */ @@ -6677,7 +6640,9 @@ static void gen9_enable_rc6(struct drm_i915_private *dev_priv) rc6_mode = GEN6_RC_CTL_EI_MODE(1); I915_WRITE(GEN6_RC_CONTROL, - GEN6_RC_CTL_HW_ENABLE | rc6_mode | rc6_mask); + GEN6_RC_CTL_HW_ENABLE | + GEN6_RC_CTL_RC6_ENABLE | + rc6_mode); /* * 3b: Enable Coarse Power Gating only when RC6 is enabled. @@ -6686,8 +6651,8 @@ static void gen9_enable_rc6(struct drm_i915_private *dev_priv) if (NEEDS_WaRsDisableCoarsePowerGating(dev_priv)) I915_WRITE(GEN9_PG_ENABLE, 0); else - I915_WRITE(GEN9_PG_ENABLE, (rc6_mask & GEN6_RC_CTL_RC6_ENABLE) ? - (GEN9_RENDER_PG_ENABLE | GEN9_MEDIA_PG_ENABLE) : 0); + I915_WRITE(GEN9_PG_ENABLE, + GEN9_RENDER_PG_ENABLE | GEN9_MEDIA_PG_ENABLE); intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); } @@ -6696,7 +6661,6 @@ static void gen8_enable_rc6(struct drm_i915_private *dev_priv) { struct intel_engine_cs *engine; enum intel_engine_id id; - uint32_t rc6_mask = 0; /* 1a: Software RC state - RC0 */ I915_WRITE(GEN6_RC_STATE, 0); @@ -6718,13 +6682,11 @@ static void gen8_enable_rc6(struct drm_i915_private *dev_priv) I915_WRITE(GEN6_RC6_THRESHOLD, 625); /* 800us/1.28 for TO */ /* 3: Enable RC6 */ - if (intel_rc6_enabled() & INTEL_RC6_ENABLE) - rc6_mask = GEN6_RC_CTL_RC6_ENABLE; - intel_print_rc6_info(dev_priv, rc6_mask); - I915_WRITE(GEN6_RC_CONTROL, GEN6_RC_CTL_HW_ENABLE | - GEN7_RC_CTL_TO_MODE | - rc6_mask); + I915_WRITE(GEN6_RC_CONTROL, + GEN6_RC_CTL_HW_ENABLE | + GEN7_RC_CTL_TO_MODE | + GEN6_RC_CTL_RC6_ENABLE); intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); } @@ -6773,9 +6735,8 @@ static void gen6_enable_rc6(struct drm_i915_private *dev_priv) { struct intel_engine_cs *engine; enum intel_engine_id id; - u32 rc6vids, rc6_mask = 0; + u32 rc6vids, rc6_mask; u32 gtfifodbg; - int rc6_mode; int ret; I915_WRITE(GEN6_RC_STATE, 0); @@ -6810,22 +6771,12 @@ static void gen6_enable_rc6(struct drm_i915_private *dev_priv) I915_WRITE(GEN6_RC6p_THRESHOLD, 150000); I915_WRITE(GEN6_RC6pp_THRESHOLD, 64000); /* unused */ - /* Check if we are enabling RC6 */ - rc6_mode = intel_rc6_enabled(); - if (rc6_mode & INTEL_RC6_ENABLE) - rc6_mask |= GEN6_RC_CTL_RC6_ENABLE; - /* We don't use those on Haswell */ - if (!IS_HASWELL(dev_priv)) { - if (rc6_mode & INTEL_RC6p_ENABLE) - rc6_mask |= GEN6_RC_CTL_RC6p_ENABLE; - - if (rc6_mode & INTEL_RC6pp_ENABLE) - rc6_mask |= GEN6_RC_CTL_RC6pp_ENABLE; - } - - intel_print_rc6_info(dev_priv, rc6_mask); - + rc6_mask = GEN6_RC_CTL_RC6_ENABLE; + if (HAS_RC6p(dev_priv)) + rc6_mask |= GEN6_RC_CTL_RC6p_ENABLE; + if (HAS_RC6pp(dev_priv)) + rc6_mask |= GEN6_RC_CTL_RC6pp_ENABLE; I915_WRITE(GEN6_RC_CONTROL, rc6_mask | GEN6_RC_CTL_EI_MODE(1) | @@ -7067,7 +7018,7 @@ static void valleyview_check_pctx(struct drm_i915_private *dev_priv) { unsigned long pctx_addr = I915_READ(VLV_PCBR) & ~4095; - WARN_ON(pctx_addr != dev_priv->mm.stolen_base + + WARN_ON(pctx_addr != dev_priv->dsm.start + dev_priv->vlv_pctx->stolen->start); } @@ -7082,16 +7033,15 @@ static void cherryview_check_pctx(struct drm_i915_private *dev_priv) static void cherryview_setup_pctx(struct drm_i915_private *dev_priv) { - struct i915_ggtt *ggtt = &dev_priv->ggtt; - unsigned long pctx_paddr, paddr; + resource_size_t pctx_paddr, paddr; + resource_size_t pctx_size = 32*1024; u32 pcbr; - int pctx_size = 32*1024; pcbr = I915_READ(VLV_PCBR); if ((pcbr >> VLV_PCBR_ADDR_SHIFT) == 0) { DRM_DEBUG_DRIVER("BIOS didn't set up PCBR, fixing up\n"); - paddr = (dev_priv->mm.stolen_base + - (ggtt->stolen_size - pctx_size)); + paddr = dev_priv->dsm.end + 1 - pctx_size; + GEM_BUG_ON(paddr > U32_MAX); pctx_paddr = (paddr & (~4095)); I915_WRITE(VLV_PCBR, pctx_paddr); @@ -7103,16 +7053,16 @@ static void cherryview_setup_pctx(struct drm_i915_private *dev_priv) static void valleyview_setup_pctx(struct drm_i915_private *dev_priv) { struct drm_i915_gem_object *pctx; - unsigned long pctx_paddr; + resource_size_t pctx_paddr; + resource_size_t pctx_size = 24*1024; u32 pcbr; - int pctx_size = 24*1024; pcbr = I915_READ(VLV_PCBR); if (pcbr) { /* BIOS set it up already, grab the pre-alloc'd space */ - int pcbr_offset; + resource_size_t pcbr_offset; - pcbr_offset = (pcbr & (~4095)) - dev_priv->mm.stolen_base; + pcbr_offset = (pcbr & (~4095)) - dev_priv->dsm.start; pctx = i915_gem_object_create_stolen_for_preallocated(dev_priv, pcbr_offset, I915_GTT_OFFSET_NONE, @@ -7136,7 +7086,11 @@ static void valleyview_setup_pctx(struct drm_i915_private *dev_priv) goto out; } - pctx_paddr = dev_priv->mm.stolen_base + pctx->stolen->start; + GEM_BUG_ON(range_overflows_t(u64, + dev_priv->dsm.start, + pctx->stolen->start, + U32_MAX)); + pctx_paddr = dev_priv->dsm.start + pctx->stolen->start; I915_WRITE(VLV_PCBR, pctx_paddr); out: @@ -7268,7 +7222,7 @@ static void cherryview_enable_rc6(struct drm_i915_private *dev_priv) { struct intel_engine_cs *engine; enum intel_engine_id id; - u32 gtfifodbg, rc6_mode = 0, pcbr; + u32 gtfifodbg, rc6_mode, pcbr; gtfifodbg = I915_READ(GTFIFODBG) & ~(GT_FIFO_SBDEDICATE_FREE_ENTRY_CHV | GT_FIFO_FREE_ENTRIES_CHV); @@ -7309,10 +7263,9 @@ static void cherryview_enable_rc6(struct drm_i915_private *dev_priv) pcbr = I915_READ(VLV_PCBR); /* 3: Enable RC6 */ - if ((intel_rc6_enabled() & INTEL_RC6_ENABLE) && - (pcbr >> VLV_PCBR_ADDR_SHIFT)) + rc6_mode = 0; + if (pcbr >> VLV_PCBR_ADDR_SHIFT) rc6_mode = GEN7_RC_CTL_TO_MODE; - I915_WRITE(GEN6_RC_CONTROL, rc6_mode); intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); @@ -7364,7 +7317,7 @@ static void valleyview_enable_rc6(struct drm_i915_private *dev_priv) { struct intel_engine_cs *engine; enum intel_engine_id id; - u32 gtfifodbg, rc6_mode = 0; + u32 gtfifodbg; valleyview_check_pctx(dev_priv); @@ -7397,12 +7350,8 @@ static void valleyview_enable_rc6(struct drm_i915_private *dev_priv) VLV_MEDIA_RC6_COUNT_EN | VLV_RENDER_RC6_COUNT_EN)); - if (intel_rc6_enabled() & INTEL_RC6_ENABLE) - rc6_mode = GEN7_RC_CTL_TO_MODE | VLV_RC_CTL_CTX_RST_PARALLEL; - - intel_print_rc6_info(dev_priv, rc6_mode); - - I915_WRITE(GEN6_RC_CONTROL, rc6_mode); + I915_WRITE(GEN6_RC_CONTROL, + GEN7_RC_CTL_TO_MODE | VLV_RC_CTL_CTX_RST_PARALLEL); intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); } @@ -7929,7 +7878,7 @@ void intel_init_gt_powersave(struct drm_i915_private *dev_priv) * RPM depends on RC6 to save restore the GT HW context, so make RC6 a * requirement. */ - if (!i915_modparams.enable_rc6) { + if (!sanitize_rc6(dev_priv)) { DRM_INFO("RC6 disabled, disabling runtime PM support\n"); intel_runtime_pm_get(dev_priv); } @@ -7982,7 +7931,7 @@ void intel_cleanup_gt_powersave(struct drm_i915_private *dev_priv) if (IS_VALLEYVIEW(dev_priv)) valleyview_cleanup_gt_powersave(dev_priv); - if (!i915_modparams.enable_rc6) + if (!HAS_RC6(dev_priv)) intel_runtime_pm_put(dev_priv); } @@ -8149,7 +8098,8 @@ void intel_enable_gt_powersave(struct drm_i915_private *dev_priv) mutex_lock(&dev_priv->pcu_lock); - intel_enable_rc6(dev_priv); + if (HAS_RC6(dev_priv)) + intel_enable_rc6(dev_priv); intel_enable_rps(dev_priv); if (HAS_LLC(dev_priv)) intel_enable_llc_pstate(dev_priv); @@ -8468,7 +8418,7 @@ static void cnp_init_clock_gating(struct drm_i915_private *dev_priv) if (!HAS_PCH_CNP(dev_priv)) return; - /* Wa #1181 */ + /* Display WA #1181: cnp */ I915_WRITE(SOUTH_DSPCLK_GATE_D, I915_READ(SOUTH_DSPCLK_GATE_D) | CNP_PWM_CGE_GATING_DISABLE); } @@ -9291,7 +9241,7 @@ int skl_pcode_request(struct drm_i915_private *dev_priv, u32 mbox, u32 request, ret = 0; goto out; } - ret = _wait_for(COND, timeout_base_ms * 1000, 10); + ret = _wait_for(COND, timeout_base_ms * 1000, 10, 10); if (!ret) goto out; @@ -9393,12 +9343,13 @@ static u64 vlv_residency_raw(struct drm_i915_private *dev_priv, const i915_reg_t reg) { u32 lower, upper, tmp; + unsigned long flags; int loop = 2; /* The register accessed do not need forcewake. We borrow * uncore lock to prevent concurrent access to range reg. */ - spin_lock_irq(&dev_priv->uncore.lock); + spin_lock_irqsave(&dev_priv->uncore.lock, flags); /* vlv and chv residency counters are 40 bits in width. * With a control bit, we can choose between upper or lower @@ -9429,39 +9380,51 @@ static u64 vlv_residency_raw(struct drm_i915_private *dev_priv, * now. */ - spin_unlock_irq(&dev_priv->uncore.lock); + spin_unlock_irqrestore(&dev_priv->uncore.lock, flags); return lower | (u64)upper << 8; } -u64 intel_rc6_residency_us(struct drm_i915_private *dev_priv, +u64 intel_rc6_residency_ns(struct drm_i915_private *dev_priv, const i915_reg_t reg) { - u64 time_hw, units, div; + u64 time_hw; + u32 mul, div; - if (!intel_rc6_enabled()) + if (!HAS_RC6(dev_priv)) return 0; - intel_runtime_pm_get(dev_priv); - /* On VLV and CHV, residency time is in CZ units rather than 1.28us */ if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { - units = 1000; + mul = 1000000; div = dev_priv->czclk_freq; - time_hw = vlv_residency_raw(dev_priv, reg); - } else if (IS_GEN9_LP(dev_priv)) { - units = 1000; - div = 1200; /* 833.33ns */ - - time_hw = I915_READ(reg); } else { - units = 128000; /* 1.28us */ - div = 100000; + /* 833.33ns units on Gen9LP, 1.28us elsewhere. */ + if (IS_GEN9_LP(dev_priv)) { + mul = 10000; + div = 12; + } else { + mul = 1280; + div = 1; + } time_hw = I915_READ(reg); } - intel_runtime_pm_put(dev_priv); - return DIV_ROUND_UP_ULL(time_hw * units, div); + return DIV_ROUND_UP_ULL(time_hw * mul, div); +} + +u32 intel_get_cagf(struct drm_i915_private *dev_priv, u32 rpstat) +{ + u32 cagf; + + if (INTEL_GEN(dev_priv) >= 9) + cagf = (rpstat & GEN9_CAGF_MASK) >> GEN9_CAGF_SHIFT; + else if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) + cagf = (rpstat & HSW_CAGF_MASK) >> HSW_CAGF_SHIFT; + else + cagf = (rpstat & GEN6_CAGF_MASK) >> GEN6_CAGF_SHIFT; + + return cagf; } diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 12e734b29463..e2085820b586 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -340,50 +340,6 @@ gen7_render_ring_flush(struct drm_i915_gem_request *req, u32 mode) return 0; } -static int -gen8_render_ring_flush(struct drm_i915_gem_request *req, u32 mode) -{ - u32 flags; - u32 *cs; - - cs = intel_ring_begin(req, mode & EMIT_INVALIDATE ? 12 : 6); - if (IS_ERR(cs)) - return PTR_ERR(cs); - - flags = PIPE_CONTROL_CS_STALL; - - if (mode & EMIT_FLUSH) { - flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH; - flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH; - flags |= PIPE_CONTROL_DC_FLUSH_ENABLE; - flags |= PIPE_CONTROL_FLUSH_ENABLE; - } - if (mode & EMIT_INVALIDATE) { - flags |= PIPE_CONTROL_TLB_INVALIDATE; - flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE; - flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE; - flags |= PIPE_CONTROL_VF_CACHE_INVALIDATE; - flags |= PIPE_CONTROL_CONST_CACHE_INVALIDATE; - flags |= PIPE_CONTROL_STATE_CACHE_INVALIDATE; - flags |= PIPE_CONTROL_QW_WRITE; - flags |= PIPE_CONTROL_GLOBAL_GTT_IVB; - - /* WaCsStallBeforeStateCacheInvalidate:bdw,chv */ - cs = gen8_emit_pipe_control(cs, - PIPE_CONTROL_CS_STALL | - PIPE_CONTROL_STALL_AT_SCOREBOARD, - 0); - } - - cs = gen8_emit_pipe_control(cs, flags, - i915_ggtt_offset(req->engine->scratch) + - 2 * CACHELINE_BYTES); - - intel_ring_advance(req, cs); - - return 0; -} - static void ring_setup_phys_status_page(struct intel_engine_cs *engine) { struct drm_i915_private *dev_priv = engine->i915; @@ -427,7 +383,6 @@ static void intel_ring_setup_status_page(struct intel_engine_cs *engine) } else if (IS_GEN6(dev_priv)) { mmio = RING_HWS_PGA_GEN6(engine->mmio_base); } else { - /* XXX: gen8 returns to sanity */ mmio = RING_HWS_PGA(engine->mmio_base); } @@ -437,13 +392,7 @@ static void intel_ring_setup_status_page(struct intel_engine_cs *engine) I915_WRITE(mmio, engine->status_page.ggtt_offset); POSTING_READ(mmio); - /* - * Flush the TLB for this page - * - * FIXME: These two bits have disappeared on gen8, so a question - * arises: do we still need this and if so how should we go about - * invalidating the TLB? - */ + /* Flush the TLB for this page */ if (IS_GEN(dev_priv, 6, 7)) { i915_reg_t reg = RING_INSTPM(engine->mmio_base); @@ -611,8 +560,6 @@ static void reset_ring_common(struct intel_engine_cs *engine, struct intel_context *ce = &request->ctx->engine[engine->id]; struct i915_hw_ppgtt *ppgtt; - /* FIXME consider gen8 reset */ - if (ce->state) { I915_WRITE(CCID, i915_ggtt_offset(ce->state) | @@ -644,6 +591,7 @@ static void reset_ring_common(struct intel_engine_cs *engine, request->ring->head = request->postfix; } else { engine->legacy_active_context = NULL; + engine->legacy_active_ppgtt = NULL; } } @@ -713,62 +661,6 @@ static int init_render_ring(struct intel_engine_cs *engine) return init_workarounds_ring(engine); } -static void render_ring_cleanup(struct intel_engine_cs *engine) -{ - struct drm_i915_private *dev_priv = engine->i915; - - i915_vma_unpin_and_release(&dev_priv->semaphore); -} - -static u32 *gen8_rcs_signal(struct drm_i915_gem_request *req, u32 *cs) -{ - struct drm_i915_private *dev_priv = req->i915; - struct intel_engine_cs *waiter; - enum intel_engine_id id; - - for_each_engine(waiter, dev_priv, id) { - u64 gtt_offset = req->engine->semaphore.signal_ggtt[id]; - if (gtt_offset == MI_SEMAPHORE_SYNC_INVALID) - continue; - - *cs++ = GFX_OP_PIPE_CONTROL(6); - *cs++ = PIPE_CONTROL_GLOBAL_GTT_IVB | PIPE_CONTROL_QW_WRITE | - PIPE_CONTROL_CS_STALL; - *cs++ = lower_32_bits(gtt_offset); - *cs++ = upper_32_bits(gtt_offset); - *cs++ = req->global_seqno; - *cs++ = 0; - *cs++ = MI_SEMAPHORE_SIGNAL | - MI_SEMAPHORE_TARGET(waiter->hw_id); - *cs++ = 0; - } - - return cs; -} - -static u32 *gen8_xcs_signal(struct drm_i915_gem_request *req, u32 *cs) -{ - struct drm_i915_private *dev_priv = req->i915; - struct intel_engine_cs *waiter; - enum intel_engine_id id; - - for_each_engine(waiter, dev_priv, id) { - u64 gtt_offset = req->engine->semaphore.signal_ggtt[id]; - if (gtt_offset == MI_SEMAPHORE_SYNC_INVALID) - continue; - - *cs++ = (MI_FLUSH_DW + 1) | MI_FLUSH_DW_OP_STOREDW; - *cs++ = lower_32_bits(gtt_offset) | MI_FLUSH_DW_USE_GTT; - *cs++ = upper_32_bits(gtt_offset); - *cs++ = req->global_seqno; - *cs++ = MI_SEMAPHORE_SIGNAL | - MI_SEMAPHORE_TARGET(waiter->hw_id); - *cs++ = 0; - } - - return cs; -} - static u32 *gen6_signal(struct drm_i915_gem_request *req, u32 *cs) { struct drm_i915_private *dev_priv = req->i915; @@ -851,70 +743,6 @@ static void gen6_sema_emit_breadcrumb(struct drm_i915_gem_request *req, u32 *cs) req->engine->semaphore.signal(req, cs)); } -static void gen8_render_emit_breadcrumb(struct drm_i915_gem_request *req, - u32 *cs) -{ - struct intel_engine_cs *engine = req->engine; - - if (engine->semaphore.signal) - cs = engine->semaphore.signal(req, cs); - - *cs++ = GFX_OP_PIPE_CONTROL(6); - *cs++ = PIPE_CONTROL_GLOBAL_GTT_IVB | PIPE_CONTROL_CS_STALL | - PIPE_CONTROL_QW_WRITE; - *cs++ = intel_hws_seqno_address(engine); - *cs++ = 0; - *cs++ = req->global_seqno; - /* We're thrashing one dword of HWS. */ - *cs++ = 0; - *cs++ = MI_USER_INTERRUPT; - *cs++ = MI_NOOP; - - req->tail = intel_ring_offset(req, cs); - assert_ring_tail_valid(req->ring, req->tail); -} - -static const int gen8_render_emit_breadcrumb_sz = 8; - -/** - * intel_ring_sync - sync the waiter to the signaller on seqno - * - * @waiter - ring that is waiting - * @signaller - ring which has, or will signal - * @seqno - seqno which the waiter will block on - */ - -static int -gen8_ring_sync_to(struct drm_i915_gem_request *req, - struct drm_i915_gem_request *signal) -{ - struct drm_i915_private *dev_priv = req->i915; - u64 offset = GEN8_WAIT_OFFSET(req->engine, signal->engine->id); - struct i915_hw_ppgtt *ppgtt; - u32 *cs; - - cs = intel_ring_begin(req, 4); - if (IS_ERR(cs)) - return PTR_ERR(cs); - - *cs++ = MI_SEMAPHORE_WAIT | MI_SEMAPHORE_GLOBAL_GTT | - MI_SEMAPHORE_SAD_GTE_SDD; - *cs++ = signal->global_seqno; - *cs++ = lower_32_bits(offset); - *cs++ = upper_32_bits(offset); - intel_ring_advance(req, cs); - - /* When the !RCS engines idle waiting upon a semaphore, they lose their - * pagetables and we must reload them before executing the batch. - * We do this on the i915_switch_context() following the wait and - * before the dispatch. - */ - ppgtt = req->ctx->ppgtt; - if (ppgtt && req->engine->id != RCS) - ppgtt->pd_dirty_rings |= intel_engine_flag(req->engine); - return 0; -} - static int gen6_ring_sync_to(struct drm_i915_gem_request *req, struct drm_i915_gem_request *signal) @@ -1090,25 +918,6 @@ hsw_vebox_irq_disable(struct intel_engine_cs *engine) gen6_mask_pm_irq(dev_priv, engine->irq_enable_mask); } -static void -gen8_irq_enable(struct intel_engine_cs *engine) -{ - struct drm_i915_private *dev_priv = engine->i915; - - I915_WRITE_IMR(engine, - ~(engine->irq_enable_mask | - engine->irq_keep_mask)); - POSTING_READ_FW(RING_IMR(engine->mmio_base)); -} - -static void -gen8_irq_disable(struct intel_engine_cs *engine) -{ - struct drm_i915_private *dev_priv = engine->i915; - - I915_WRITE_IMR(engine, ~engine->irq_keep_mask); -} - static int i965_emit_bb_start(struct drm_i915_gem_request *req, u64 offset, u32 length, @@ -1576,6 +1385,190 @@ void intel_legacy_submission_resume(struct drm_i915_private *dev_priv) intel_ring_reset(engine->buffer, 0); } +static inline int mi_set_context(struct drm_i915_gem_request *rq, u32 flags) +{ + struct drm_i915_private *i915 = rq->i915; + struct intel_engine_cs *engine = rq->engine; + enum intel_engine_id id; + const int num_rings = + /* Use an extended w/a on gen7 if signalling from other rings */ + (HAS_LEGACY_SEMAPHORES(i915) && IS_GEN7(i915)) ? + INTEL_INFO(i915)->num_rings - 1 : + 0; + int len; + u32 *cs; + + flags |= MI_MM_SPACE_GTT; + if (IS_HASWELL(i915)) + /* These flags are for resource streamer on HSW+ */ + flags |= HSW_MI_RS_SAVE_STATE_EN | HSW_MI_RS_RESTORE_STATE_EN; + else + flags |= MI_SAVE_EXT_STATE_EN | MI_RESTORE_EXT_STATE_EN; + + len = 4; + if (IS_GEN7(i915)) + len += 2 + (num_rings ? 4*num_rings + 6 : 0); + + cs = intel_ring_begin(rq, len); + if (IS_ERR(cs)) + return PTR_ERR(cs); + + /* WaProgramMiArbOnOffAroundMiSetContext:ivb,vlv,hsw,bdw,chv */ + if (IS_GEN7(i915)) { + *cs++ = MI_ARB_ON_OFF | MI_ARB_DISABLE; + if (num_rings) { + struct intel_engine_cs *signaller; + + *cs++ = MI_LOAD_REGISTER_IMM(num_rings); + for_each_engine(signaller, i915, id) { + if (signaller == engine) + continue; + + *cs++ = i915_mmio_reg_offset( + RING_PSMI_CTL(signaller->mmio_base)); + *cs++ = _MASKED_BIT_ENABLE( + GEN6_PSMI_SLEEP_MSG_DISABLE); + } + } + } + + *cs++ = MI_NOOP; + *cs++ = MI_SET_CONTEXT; + *cs++ = i915_ggtt_offset(rq->ctx->engine[RCS].state) | flags; + /* + * w/a: MI_SET_CONTEXT must always be followed by MI_NOOP + * WaMiSetContext_Hang:snb,ivb,vlv + */ + *cs++ = MI_NOOP; + + if (IS_GEN7(i915)) { + if (num_rings) { + struct intel_engine_cs *signaller; + i915_reg_t last_reg = {}; /* keep gcc quiet */ + + *cs++ = MI_LOAD_REGISTER_IMM(num_rings); + for_each_engine(signaller, i915, id) { + if (signaller == engine) + continue; + + last_reg = RING_PSMI_CTL(signaller->mmio_base); + *cs++ = i915_mmio_reg_offset(last_reg); + *cs++ = _MASKED_BIT_DISABLE( + GEN6_PSMI_SLEEP_MSG_DISABLE); + } + + /* Insert a delay before the next switch! */ + *cs++ = MI_STORE_REGISTER_MEM | MI_SRM_LRM_GLOBAL_GTT; + *cs++ = i915_mmio_reg_offset(last_reg); + *cs++ = i915_ggtt_offset(engine->scratch); + *cs++ = MI_NOOP; + } + *cs++ = MI_ARB_ON_OFF | MI_ARB_ENABLE; + } + + intel_ring_advance(rq, cs); + + return 0; +} + +static int remap_l3(struct drm_i915_gem_request *rq, int slice) +{ + u32 *cs, *remap_info = rq->i915->l3_parity.remap_info[slice]; + int i; + + if (!remap_info) + return 0; + + cs = intel_ring_begin(rq, GEN7_L3LOG_SIZE/4 * 2 + 2); + if (IS_ERR(cs)) + return PTR_ERR(cs); + + /* + * Note: We do not worry about the concurrent register cacheline hang + * here because no other code should access these registers other than + * at initialization time. + */ + *cs++ = MI_LOAD_REGISTER_IMM(GEN7_L3LOG_SIZE/4); + for (i = 0; i < GEN7_L3LOG_SIZE/4; i++) { + *cs++ = i915_mmio_reg_offset(GEN7_L3LOG(slice, i)); + *cs++ = remap_info[i]; + } + *cs++ = MI_NOOP; + intel_ring_advance(rq, cs); + + return 0; +} + +static int switch_context(struct drm_i915_gem_request *rq) +{ + struct intel_engine_cs *engine = rq->engine; + struct i915_gem_context *to_ctx = rq->ctx; + struct i915_hw_ppgtt *to_mm = + to_ctx->ppgtt ?: rq->i915->mm.aliasing_ppgtt; + struct i915_gem_context *from_ctx = engine->legacy_active_context; + struct i915_hw_ppgtt *from_mm = engine->legacy_active_ppgtt; + u32 hw_flags = 0; + int ret, i; + + lockdep_assert_held(&rq->i915->drm.struct_mutex); + GEM_BUG_ON(HAS_EXECLISTS(rq->i915)); + + if (to_mm != from_mm || + (to_mm && intel_engine_flag(engine) & to_mm->pd_dirty_rings)) { + trace_switch_mm(engine, to_ctx); + ret = to_mm->switch_mm(to_mm, rq); + if (ret) + goto err; + + to_mm->pd_dirty_rings &= ~intel_engine_flag(engine); + engine->legacy_active_ppgtt = to_mm; + hw_flags = MI_FORCE_RESTORE; + } + + if (to_ctx->engine[engine->id].state && + (to_ctx != from_ctx || hw_flags & MI_FORCE_RESTORE)) { + GEM_BUG_ON(engine->id != RCS); + + /* + * The kernel context(s) is treated as pure scratch and is not + * expected to retain any state (as we sacrifice it during + * suspend and on resume it may be corrupted). This is ok, + * as nothing actually executes using the kernel context; it + * is purely used for flushing user contexts. + */ + if (i915_gem_context_is_kernel(to_ctx)) + hw_flags = MI_RESTORE_INHIBIT; + + ret = mi_set_context(rq, hw_flags); + if (ret) + goto err_mm; + + engine->legacy_active_context = to_ctx; + } + + if (to_ctx->remap_slice) { + for (i = 0; i < MAX_L3_SLICES; i++) { + if (!(to_ctx->remap_slice & BIT(i))) + continue; + + ret = remap_l3(rq, i); + if (ret) + goto err_ctx; + } + + to_ctx->remap_slice = 0; + } + + return 0; + +err_ctx: + engine->legacy_active_context = from_ctx; +err_mm: + engine->legacy_active_ppgtt = from_mm; +err: + return ret; +} + static int ring_request_alloc(struct drm_i915_gem_request *request) { int ret; @@ -1592,6 +1585,10 @@ static int ring_request_alloc(struct drm_i915_gem_request *request) if (ret) return ret; + ret = switch_context(request); + if (ret) + return ret; + request->reserved_space -= LEGACY_REQUEST_SIZE; return 0; } @@ -1792,8 +1789,6 @@ static int gen6_bsd_ring_flush(struct drm_i915_gem_request *req, u32 mode) return PTR_ERR(cs); cmd = MI_FLUSH_DW; - if (INTEL_GEN(req->i915) >= 8) - cmd += 1; /* We always require a command barrier so that subsequent * commands, such as breadcrumb interrupts, are strictly ordered @@ -1813,38 +1808,9 @@ static int gen6_bsd_ring_flush(struct drm_i915_gem_request *req, u32 mode) *cs++ = cmd; *cs++ = I915_GEM_HWS_SCRATCH_ADDR | MI_FLUSH_DW_USE_GTT; - if (INTEL_GEN(req->i915) >= 8) { - *cs++ = 0; /* upper addr */ - *cs++ = 0; /* value */ - } else { - *cs++ = 0; - *cs++ = MI_NOOP; - } - intel_ring_advance(req, cs); - return 0; -} - -static int -gen8_emit_bb_start(struct drm_i915_gem_request *req, - u64 offset, u32 len, - unsigned int dispatch_flags) -{ - bool ppgtt = USES_PPGTT(req->i915) && - !(dispatch_flags & I915_DISPATCH_SECURE); - u32 *cs; - - cs = intel_ring_begin(req, 4); - if (IS_ERR(cs)) - return PTR_ERR(cs); - - /* FIXME(BDW): Address space and security selectors. */ - *cs++ = MI_BATCH_BUFFER_START_GEN8 | (ppgtt << 8) | (dispatch_flags & - I915_DISPATCH_RS ? MI_BATCH_RESOURCE_STREAMER : 0); - *cs++ = lower_32_bits(offset); - *cs++ = upper_32_bits(offset); + *cs++ = 0; *cs++ = MI_NOOP; intel_ring_advance(req, cs); - return 0; } @@ -1901,8 +1867,6 @@ static int gen6_ring_flush(struct drm_i915_gem_request *req, u32 mode) return PTR_ERR(cs); cmd = MI_FLUSH_DW; - if (INTEL_GEN(req->i915) >= 8) - cmd += 1; /* We always require a command barrier so that subsequent * commands, such as breadcrumb interrupts, are strictly ordered @@ -1921,13 +1885,8 @@ static int gen6_ring_flush(struct drm_i915_gem_request *req, u32 mode) cmd |= MI_INVALIDATE_TLB; *cs++ = cmd; *cs++ = I915_GEM_HWS_SCRATCH_ADDR | MI_FLUSH_DW_USE_GTT; - if (INTEL_GEN(req->i915) >= 8) { - *cs++ = 0; /* upper addr */ - *cs++ = 0; /* value */ - } else { - *cs++ = 0; - *cs++ = MI_NOOP; - } + *cs++ = 0; + *cs++ = MI_NOOP; intel_ring_advance(req, cs); return 0; @@ -1936,110 +1895,61 @@ static int gen6_ring_flush(struct drm_i915_gem_request *req, u32 mode) static void intel_ring_init_semaphores(struct drm_i915_private *dev_priv, struct intel_engine_cs *engine) { - struct drm_i915_gem_object *obj; - int ret, i; + int i; - if (!i915_modparams.semaphores) + if (!HAS_LEGACY_SEMAPHORES(dev_priv)) return; - if (INTEL_GEN(dev_priv) >= 8 && !dev_priv->semaphore) { - struct i915_vma *vma; - - obj = i915_gem_object_create(dev_priv, PAGE_SIZE); - if (IS_ERR(obj)) - goto err; - - vma = i915_vma_instance(obj, &dev_priv->ggtt.base, NULL); - if (IS_ERR(vma)) - goto err_obj; - - ret = i915_gem_object_set_to_gtt_domain(obj, false); - if (ret) - goto err_obj; - - ret = i915_vma_pin(vma, 0, 0, PIN_GLOBAL | PIN_HIGH); - if (ret) - goto err_obj; - - dev_priv->semaphore = vma; - } - - if (INTEL_GEN(dev_priv) >= 8) { - u32 offset = i915_ggtt_offset(dev_priv->semaphore); + GEM_BUG_ON(INTEL_GEN(dev_priv) < 6); + engine->semaphore.sync_to = gen6_ring_sync_to; + engine->semaphore.signal = gen6_signal; - engine->semaphore.sync_to = gen8_ring_sync_to; - engine->semaphore.signal = gen8_xcs_signal; - - for (i = 0; i < I915_NUM_ENGINES; i++) { - u32 ring_offset; - - if (i != engine->id) - ring_offset = offset + GEN8_SEMAPHORE_OFFSET(engine->id, i); - else - ring_offset = MI_SEMAPHORE_SYNC_INVALID; - - engine->semaphore.signal_ggtt[i] = ring_offset; - } - } else if (INTEL_GEN(dev_priv) >= 6) { - engine->semaphore.sync_to = gen6_ring_sync_to; - engine->semaphore.signal = gen6_signal; - - /* - * The current semaphore is only applied on pre-gen8 - * platform. And there is no VCS2 ring on the pre-gen8 - * platform. So the semaphore between RCS and VCS2 is - * initialized as INVALID. Gen8 will initialize the - * sema between VCS2 and RCS later. - */ - for (i = 0; i < GEN6_NUM_SEMAPHORES; i++) { - static const struct { - u32 wait_mbox; - i915_reg_t mbox_reg; - } sem_data[GEN6_NUM_SEMAPHORES][GEN6_NUM_SEMAPHORES] = { - [RCS_HW] = { - [VCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_RV, .mbox_reg = GEN6_VRSYNC }, - [BCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_RB, .mbox_reg = GEN6_BRSYNC }, - [VECS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_RVE, .mbox_reg = GEN6_VERSYNC }, - }, - [VCS_HW] = { - [RCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_VR, .mbox_reg = GEN6_RVSYNC }, - [BCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_VB, .mbox_reg = GEN6_BVSYNC }, - [VECS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_VVE, .mbox_reg = GEN6_VEVSYNC }, - }, - [BCS_HW] = { - [RCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_BR, .mbox_reg = GEN6_RBSYNC }, - [VCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_BV, .mbox_reg = GEN6_VBSYNC }, - [VECS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_BVE, .mbox_reg = GEN6_VEBSYNC }, - }, - [VECS_HW] = { - [RCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_VER, .mbox_reg = GEN6_RVESYNC }, - [VCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_VEV, .mbox_reg = GEN6_VVESYNC }, - [BCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_VEB, .mbox_reg = GEN6_BVESYNC }, - }, - }; + /* + * The current semaphore is only applied on pre-gen8 + * platform. And there is no VCS2 ring on the pre-gen8 + * platform. So the semaphore between RCS and VCS2 is + * initialized as INVALID. + */ + for (i = 0; i < GEN6_NUM_SEMAPHORES; i++) { + static const struct { u32 wait_mbox; i915_reg_t mbox_reg; + } sem_data[GEN6_NUM_SEMAPHORES][GEN6_NUM_SEMAPHORES] = { + [RCS_HW] = { + [VCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_RV, .mbox_reg = GEN6_VRSYNC }, + [BCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_RB, .mbox_reg = GEN6_BRSYNC }, + [VECS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_RVE, .mbox_reg = GEN6_VERSYNC }, + }, + [VCS_HW] = { + [RCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_VR, .mbox_reg = GEN6_RVSYNC }, + [BCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_VB, .mbox_reg = GEN6_BVSYNC }, + [VECS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_VVE, .mbox_reg = GEN6_VEVSYNC }, + }, + [BCS_HW] = { + [RCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_BR, .mbox_reg = GEN6_RBSYNC }, + [VCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_BV, .mbox_reg = GEN6_VBSYNC }, + [VECS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_BVE, .mbox_reg = GEN6_VEBSYNC }, + }, + [VECS_HW] = { + [RCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_VER, .mbox_reg = GEN6_RVESYNC }, + [VCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_VEV, .mbox_reg = GEN6_VVESYNC }, + [BCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_VEB, .mbox_reg = GEN6_BVESYNC }, + }, + }; + u32 wait_mbox; + i915_reg_t mbox_reg; - if (i == engine->hw_id) { - wait_mbox = MI_SEMAPHORE_SYNC_INVALID; - mbox_reg = GEN6_NOSYNC; - } else { - wait_mbox = sem_data[engine->hw_id][i].wait_mbox; - mbox_reg = sem_data[engine->hw_id][i].mbox_reg; - } - - engine->semaphore.mbox.wait[i] = wait_mbox; - engine->semaphore.mbox.signal[i] = mbox_reg; + if (i == engine->hw_id) { + wait_mbox = MI_SEMAPHORE_SYNC_INVALID; + mbox_reg = GEN6_NOSYNC; + } else { + wait_mbox = sem_data[engine->hw_id][i].wait_mbox; + mbox_reg = sem_data[engine->hw_id][i].mbox_reg; } - } - return; - -err_obj: - i915_gem_object_put(obj); -err: - DRM_DEBUG_DRIVER("Failed to allocate space for semaphores, disabling\n"); - i915_modparams.semaphores = 0; + engine->semaphore.mbox.wait[i] = wait_mbox; + engine->semaphore.mbox.signal[i] = mbox_reg; + } } static void intel_ring_init_irq(struct drm_i915_private *dev_priv, @@ -2047,11 +1957,7 @@ static void intel_ring_init_irq(struct drm_i915_private *dev_priv, { engine->irq_enable_mask = GT_RENDER_USER_INTERRUPT << engine->irq_shift; - if (INTEL_GEN(dev_priv) >= 8) { - engine->irq_enable = gen8_irq_enable; - engine->irq_disable = gen8_irq_disable; - engine->irq_seqno_barrier = gen6_seqno_barrier; - } else if (INTEL_GEN(dev_priv) >= 6) { + if (INTEL_GEN(dev_priv) >= 6) { engine->irq_enable = gen6_irq_enable; engine->irq_disable = gen6_irq_disable; engine->irq_seqno_barrier = gen6_seqno_barrier; @@ -2086,6 +1992,9 @@ static void gen6_bsd_set_default_submission(struct intel_engine_cs *engine) static void intel_ring_default_vfuncs(struct drm_i915_private *dev_priv, struct intel_engine_cs *engine) { + /* gen8+ are only supported with execlists */ + GEM_BUG_ON(INTEL_GEN(dev_priv) >= 8); + intel_ring_init_irq(dev_priv, engine); intel_ring_init_semaphores(dev_priv, engine); @@ -2099,26 +2008,20 @@ static void intel_ring_default_vfuncs(struct drm_i915_private *dev_priv, engine->emit_breadcrumb = i9xx_emit_breadcrumb; engine->emit_breadcrumb_sz = i9xx_emit_breadcrumb_sz; - if (i915_modparams.semaphores) { + if (HAS_LEGACY_SEMAPHORES(dev_priv)) { int num_rings; engine->emit_breadcrumb = gen6_sema_emit_breadcrumb; num_rings = INTEL_INFO(dev_priv)->num_rings - 1; - if (INTEL_GEN(dev_priv) >= 8) { - engine->emit_breadcrumb_sz += num_rings * 6; - } else { - engine->emit_breadcrumb_sz += num_rings * 3; - if (num_rings & 1) - engine->emit_breadcrumb_sz++; - } + engine->emit_breadcrumb_sz += num_rings * 3; + if (num_rings & 1) + engine->emit_breadcrumb_sz++; } engine->set_default_submission = i9xx_set_default_submission; - if (INTEL_GEN(dev_priv) >= 8) - engine->emit_bb_start = gen8_emit_bb_start; - else if (INTEL_GEN(dev_priv) >= 6) + if (INTEL_GEN(dev_priv) >= 6) engine->emit_bb_start = gen6_emit_bb_start; else if (INTEL_GEN(dev_priv) >= 4) engine->emit_bb_start = i965_emit_bb_start; @@ -2138,20 +2041,7 @@ int intel_init_render_ring_buffer(struct intel_engine_cs *engine) if (HAS_L3_DPF(dev_priv)) engine->irq_keep_mask = GT_RENDER_L3_PARITY_ERROR_INTERRUPT; - if (INTEL_GEN(dev_priv) >= 8) { - engine->init_context = intel_rcs_ctx_init; - engine->emit_breadcrumb = gen8_render_emit_breadcrumb; - engine->emit_breadcrumb_sz = gen8_render_emit_breadcrumb_sz; - engine->emit_flush = gen8_render_ring_flush; - if (i915_modparams.semaphores) { - int num_rings; - - engine->semaphore.signal = gen8_rcs_signal; - - num_rings = INTEL_INFO(dev_priv)->num_rings - 1; - engine->emit_breadcrumb_sz += num_rings * 8; - } - } else if (INTEL_GEN(dev_priv) >= 6) { + if (INTEL_GEN(dev_priv) >= 6) { engine->init_context = intel_rcs_ctx_init; engine->emit_flush = gen7_render_ring_flush; if (IS_GEN6(dev_priv)) @@ -2170,7 +2060,6 @@ int intel_init_render_ring_buffer(struct intel_engine_cs *engine) engine->emit_bb_start = hsw_emit_bb_start; engine->init_hw = init_render_ring; - engine->cleanup = render_ring_cleanup; ret = intel_init_ring_buffer(engine); if (ret) @@ -2200,8 +2089,7 @@ int intel_init_bsd_ring_buffer(struct intel_engine_cs *engine) if (IS_GEN6(dev_priv)) engine->set_default_submission = gen6_bsd_set_default_submission; engine->emit_flush = gen6_bsd_ring_flush; - if (INTEL_GEN(dev_priv) < 8) - engine->irq_enable_mask = GT_BSD_USER_INTERRUPT; + engine->irq_enable_mask = GT_BSD_USER_INTERRUPT; } else { engine->mmio_base = BSD_RING_BASE; engine->emit_flush = bsd_ring_flush; @@ -2221,8 +2109,7 @@ int intel_init_blt_ring_buffer(struct intel_engine_cs *engine) intel_ring_default_vfuncs(dev_priv, engine); engine->emit_flush = gen6_ring_flush; - if (INTEL_GEN(dev_priv) < 8) - engine->irq_enable_mask = GT_BLT_USER_INTERRUPT; + engine->irq_enable_mask = GT_BLT_USER_INTERRUPT; return intel_init_ring_buffer(engine); } @@ -2234,12 +2121,9 @@ int intel_init_vebox_ring_buffer(struct intel_engine_cs *engine) intel_ring_default_vfuncs(dev_priv, engine); engine->emit_flush = gen6_ring_flush; - - if (INTEL_GEN(dev_priv) < 8) { - engine->irq_enable_mask = PM_VEBOX_USER_INTERRUPT; - engine->irq_enable = hsw_vebox_irq_enable; - engine->irq_disable = hsw_vebox_irq_disable; - } + engine->irq_enable_mask = PM_VEBOX_USER_INTERRUPT; + engine->irq_enable = hsw_vebox_irq_enable; + engine->irq_disable = hsw_vebox_irq_disable; return intel_init_ring_buffer(engine); } diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index f867aa6c31fc..c5ff203e42d6 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -6,6 +6,7 @@ #include "i915_gem_batch_pool.h" #include "i915_gem_request.h" #include "i915_gem_timeline.h" +#include "i915_pmu.h" #include "i915_selftest.h" struct drm_printer; @@ -47,16 +48,6 @@ struct intel_hw_status_page { /* seqno size is actually only a uint32, but since we plan to use MI_FLUSH_DW to * do the writes, and that must have qw aligned offsets, simply pretend it's 8b. */ -#define gen8_semaphore_seqno_size sizeof(uint64_t) -#define GEN8_SEMAPHORE_OFFSET(__from, __to) \ - (((__from) * I915_NUM_ENGINES + (__to)) * gen8_semaphore_seqno_size) -#define GEN8_SIGNAL_OFFSET(__ring, to) \ - (dev_priv->semaphore->node.start + \ - GEN8_SEMAPHORE_OFFSET((__ring)->id, (to))) -#define GEN8_WAIT_OFFSET(__ring, from) \ - (dev_priv->semaphore->node.start + \ - GEN8_SEMAPHORE_OFFSET(from, (__ring)->id)) - enum intel_engine_hangcheck_action { ENGINE_IDLE = 0, ENGINE_WAIT, @@ -209,6 +200,11 @@ struct intel_engine_execlists { bool no_priolist; /** + * @elsp: the ExecList Submission Port register + */ + u32 __iomem *elsp; + + /** * @port: execlist port states * * For each hardware ELSP (ExecList Submission Port) we keep @@ -252,6 +248,7 @@ struct intel_engine_execlists { unsigned int active; #define EXECLISTS_ACTIVE_USER 0 #define EXECLISTS_ACTIVE_PREEMPT 1 +#define EXECLISTS_ACTIVE_HWACK 2 /** * @port_mask: number of execlist ports - 1 @@ -348,6 +345,43 @@ struct intel_engine_cs { I915_SELFTEST_DECLARE(bool mock : 1); } breadcrumbs; + struct { + /** + * @enable: Bitmask of enable sample events on this engine. + * + * Bits correspond to sample event types, for instance + * I915_SAMPLE_QUEUED is bit 0 etc. + */ + u32 enable; + /** + * @enable_count: Reference count for the enabled samplers. + * + * Index number corresponds to the bit number from @enable. + */ + unsigned int enable_count[I915_PMU_SAMPLE_BITS]; + /** + * @sample: Counter values for sampling events. + * + * Our internal timer stores the current counters in this field. + */ +#define I915_ENGINE_SAMPLE_MAX (I915_SAMPLE_SEMA + 1) + struct i915_pmu_sample sample[I915_ENGINE_SAMPLE_MAX]; + /** + * @busy_stats: Has enablement of engine stats tracking been + * requested. + */ + bool busy_stats; + /** + * @disable_busy_stats: Work item for busy stats disabling. + * + * Same as with @enable_busy_stats action, with the difference + * that we delay it in case there are rapid enable-disable + * actions, which can happen during tool startup (like perf + * stat). + */ + struct delayed_work disable_busy_stats; + } pmu; + /* * A pool of objects to use as shadow copies of client batch buffers * when the command parser is enabled. Prevents the client from @@ -467,18 +501,15 @@ struct intel_engine_cs { * ie. transpose of f(x, y) */ struct { - union { #define GEN6_SEMAPHORE_LAST VECS_HW #define GEN6_NUM_SEMAPHORES (GEN6_SEMAPHORE_LAST + 1) #define GEN6_SEMAPHORES_MASK GENMASK(GEN6_SEMAPHORE_LAST, 0) - struct { - /* our mbox written by others */ - u32 wait[GEN6_NUM_SEMAPHORES]; - /* mboxes this ring signals to */ - i915_reg_t signal[GEN6_NUM_SEMAPHORES]; - } mbox; - u64 signal_ggtt[I915_NUM_ENGINES]; - }; + struct { + /* our mbox written by others */ + u32 wait[GEN6_NUM_SEMAPHORES]; + /* mboxes this ring signals to */ + i915_reg_t signal[GEN6_NUM_SEMAPHORES]; + } mbox; /* AKA wait() */ int (*sync_to)(struct drm_i915_gem_request *req, @@ -506,13 +537,16 @@ struct intel_engine_cs { * stream (ring). */ struct i915_gem_context *legacy_active_context; + struct i915_hw_ppgtt *legacy_active_ppgtt; /* status_notifier: list of callbacks for context-switch changes */ struct atomic_notifier_head context_status_notifier; struct intel_engine_hangcheck hangcheck; - bool needs_cmd_parser; +#define I915_ENGINE_NEEDS_CMD_PARSER BIT(0) +#define I915_ENGINE_SUPPORTS_STATS BIT(1) + unsigned int flags; /* * Table of commands the command parser needs to know about @@ -537,8 +571,50 @@ struct intel_engine_cs { * certain bits to encode the command length in the header). */ u32 (*get_cmd_length_mask)(u32 cmd_header); + + struct { + /** + * @lock: Lock protecting the below fields. + */ + spinlock_t lock; + /** + * @enabled: Reference count indicating number of listeners. + */ + unsigned int enabled; + /** + * @active: Number of contexts currently scheduled in. + */ + unsigned int active; + /** + * @enabled_at: Timestamp when busy stats were enabled. + */ + ktime_t enabled_at; + /** + * @start: Timestamp of the last idle to active transition. + * + * Idle is defined as active == 0, active is active > 0. + */ + ktime_t start; + /** + * @total: Total time this engine was busy. + * + * Accumulated time not counting the most recent block in cases + * where engine is currently busy (active > 0). + */ + ktime_t total; + } stats; }; +static inline bool intel_engine_needs_cmd_parser(struct intel_engine_cs *engine) +{ + return engine->flags & I915_ENGINE_NEEDS_CMD_PARSER; +} + +static inline bool intel_engine_supports_stats(struct intel_engine_cs *engine) +{ + return engine->flags & I915_ENGINE_SUPPORTS_STATS; +} + static inline void execlists_set_active(struct intel_engine_execlists *execlists, unsigned int bit) @@ -937,6 +1013,72 @@ unsigned int intel_engines_has_context_isolation(struct drm_i915_private *i915); bool intel_engine_can_store_dword(struct intel_engine_cs *engine); -void intel_engine_dump(struct intel_engine_cs *engine, struct drm_printer *p); +__printf(3, 4) +void intel_engine_dump(struct intel_engine_cs *engine, + struct drm_printer *m, + const char *header, ...); + +struct intel_engine_cs * +intel_engine_lookup_user(struct drm_i915_private *i915, u8 class, u8 instance); + +static inline void intel_engine_context_in(struct intel_engine_cs *engine) +{ + unsigned long flags; + + if (READ_ONCE(engine->stats.enabled) == 0) + return; + + spin_lock_irqsave(&engine->stats.lock, flags); + + if (engine->stats.enabled > 0) { + if (engine->stats.active++ == 0) + engine->stats.start = ktime_get(); + GEM_BUG_ON(engine->stats.active == 0); + } + + spin_unlock_irqrestore(&engine->stats.lock, flags); +} + +static inline void intel_engine_context_out(struct intel_engine_cs *engine) +{ + unsigned long flags; + + if (READ_ONCE(engine->stats.enabled) == 0) + return; + + spin_lock_irqsave(&engine->stats.lock, flags); + + if (engine->stats.enabled > 0) { + ktime_t last; + + if (engine->stats.active && --engine->stats.active == 0) { + /* + * Decrement the active context count and in case GPU + * is now idle add up to the running total. + */ + last = ktime_sub(ktime_get(), engine->stats.start); + + engine->stats.total = ktime_add(engine->stats.total, + last); + } else if (engine->stats.active == 0) { + /* + * After turning on engine stats, context out might be + * the first event in which case we account from the + * time stats gathering was turned on. + */ + last = ktime_sub(ktime_get(), engine->stats.enabled_at); + + engine->stats.total = ktime_add(engine->stats.total, + last); + } + } + + spin_unlock_irqrestore(&engine->stats.lock, flags); +} + +int intel_enable_engine_stats(struct intel_engine_cs *engine); +void intel_disable_engine_stats(struct intel_engine_cs *engine); + +ktime_t intel_engine_get_busy_time(struct intel_engine_cs *engine); #endif /* _INTEL_RINGBUFFER_H_ */ diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c index 8315499452dc..96ab74f3d101 100644 --- a/drivers/gpu/drm/i915/intel_runtime_pm.c +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c @@ -130,6 +130,8 @@ intel_display_power_domain_str(enum intel_display_power_domain domain) return "INIT"; case POWER_DOMAIN_MODESET: return "MODESET"; + case POWER_DOMAIN_GT_IRQ: + return "GT_IRQ"; default: MISSING_CASE(domain); return "?"; @@ -1705,6 +1707,7 @@ void intel_display_power_put(struct drm_i915_private *dev_priv, BIT_ULL(POWER_DOMAIN_INIT)) #define SKL_DISPLAY_DC_OFF_POWER_DOMAINS ( \ SKL_DISPLAY_POWERWELL_2_POWER_DOMAINS | \ + BIT_ULL(POWER_DOMAIN_GT_IRQ) | \ BIT_ULL(POWER_DOMAIN_MODESET) | \ BIT_ULL(POWER_DOMAIN_AUX_A) | \ BIT_ULL(POWER_DOMAIN_INIT)) @@ -1727,6 +1730,7 @@ void intel_display_power_put(struct drm_i915_private *dev_priv, BIT_ULL(POWER_DOMAIN_INIT)) #define BXT_DISPLAY_DC_OFF_POWER_DOMAINS ( \ BXT_DISPLAY_POWERWELL_2_POWER_DOMAINS | \ + BIT_ULL(POWER_DOMAIN_GT_IRQ) | \ BIT_ULL(POWER_DOMAIN_MODESET) | \ BIT_ULL(POWER_DOMAIN_AUX_A) | \ BIT_ULL(POWER_DOMAIN_INIT)) @@ -1785,6 +1789,7 @@ void intel_display_power_put(struct drm_i915_private *dev_priv, BIT_ULL(POWER_DOMAIN_INIT)) #define GLK_DISPLAY_DC_OFF_POWER_DOMAINS ( \ GLK_DISPLAY_POWERWELL_2_POWER_DOMAINS | \ + BIT_ULL(POWER_DOMAIN_GT_IRQ) | \ BIT_ULL(POWER_DOMAIN_MODESET) | \ BIT_ULL(POWER_DOMAIN_AUX_A) | \ BIT_ULL(POWER_DOMAIN_INIT)) diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index ce615704982a..dd485f59eb1d 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -325,6 +325,26 @@ skl_disable_plane(struct intel_plane *plane, struct intel_crtc *crtc) spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } +bool +skl_plane_get_hw_state(struct intel_plane *plane) +{ + struct drm_i915_private *dev_priv = to_i915(plane->base.dev); + enum intel_display_power_domain power_domain; + enum plane_id plane_id = plane->id; + enum pipe pipe = plane->pipe; + bool ret; + + power_domain = POWER_DOMAIN_PIPE(pipe); + if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) + return false; + + ret = I915_READ(PLANE_CTL(pipe, plane_id)) & PLANE_CTL_ENABLE; + + intel_display_power_put(dev_priv, power_domain); + + return ret; +} + static void chv_update_csc(struct intel_plane *plane, uint32_t format) { @@ -502,6 +522,26 @@ vlv_disable_plane(struct intel_plane *plane, struct intel_crtc *crtc) spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } +static bool +vlv_plane_get_hw_state(struct intel_plane *plane) +{ + struct drm_i915_private *dev_priv = to_i915(plane->base.dev); + enum intel_display_power_domain power_domain; + enum plane_id plane_id = plane->id; + enum pipe pipe = plane->pipe; + bool ret; + + power_domain = POWER_DOMAIN_PIPE(pipe); + if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) + return false; + + ret = I915_READ(SPCNTR(pipe, plane_id)) & SP_ENABLE; + + intel_display_power_put(dev_priv, power_domain); + + return ret; +} + static u32 ivb_sprite_ctl(const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state) { @@ -642,6 +682,25 @@ ivb_disable_plane(struct intel_plane *plane, struct intel_crtc *crtc) spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } +static bool +ivb_plane_get_hw_state(struct intel_plane *plane) +{ + struct drm_i915_private *dev_priv = to_i915(plane->base.dev); + enum intel_display_power_domain power_domain; + enum pipe pipe = plane->pipe; + bool ret; + + power_domain = POWER_DOMAIN_PIPE(pipe); + if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) + return false; + + ret = I915_READ(SPRCTL(pipe)) & SPRITE_ENABLE; + + intel_display_power_put(dev_priv, power_domain); + + return ret; +} + static u32 g4x_sprite_ctl(const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state) { @@ -773,6 +832,25 @@ g4x_disable_plane(struct intel_plane *plane, struct intel_crtc *crtc) spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } +static bool +g4x_plane_get_hw_state(struct intel_plane *plane) +{ + struct drm_i915_private *dev_priv = to_i915(plane->base.dev); + enum intel_display_power_domain power_domain; + enum pipe pipe = plane->pipe; + bool ret; + + power_domain = POWER_DOMAIN_PIPE(pipe); + if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) + return false; + + ret = I915_READ(DVSCNTR(pipe)) & DVS_ENABLE; + + intel_display_power_put(dev_priv, power_domain); + + return ret; +} + static int intel_check_sprite_plane(struct intel_plane *plane, struct intel_crtc_state *crtc_state, @@ -1231,6 +1309,7 @@ intel_sprite_plane_create(struct drm_i915_private *dev_priv, intel_plane->update_plane = skl_update_plane; intel_plane->disable_plane = skl_disable_plane; + intel_plane->get_hw_state = skl_plane_get_hw_state; plane_formats = skl_plane_formats; num_plane_formats = ARRAY_SIZE(skl_plane_formats); @@ -1241,6 +1320,7 @@ intel_sprite_plane_create(struct drm_i915_private *dev_priv, intel_plane->update_plane = skl_update_plane; intel_plane->disable_plane = skl_disable_plane; + intel_plane->get_hw_state = skl_plane_get_hw_state; plane_formats = skl_plane_formats; num_plane_formats = ARRAY_SIZE(skl_plane_formats); @@ -1251,6 +1331,7 @@ intel_sprite_plane_create(struct drm_i915_private *dev_priv, intel_plane->update_plane = vlv_update_plane; intel_plane->disable_plane = vlv_disable_plane; + intel_plane->get_hw_state = vlv_plane_get_hw_state; plane_formats = vlv_plane_formats; num_plane_formats = ARRAY_SIZE(vlv_plane_formats); @@ -1266,6 +1347,7 @@ intel_sprite_plane_create(struct drm_i915_private *dev_priv, intel_plane->update_plane = ivb_update_plane; intel_plane->disable_plane = ivb_disable_plane; + intel_plane->get_hw_state = ivb_plane_get_hw_state; plane_formats = snb_plane_formats; num_plane_formats = ARRAY_SIZE(snb_plane_formats); @@ -1276,6 +1358,7 @@ intel_sprite_plane_create(struct drm_i915_private *dev_priv, intel_plane->update_plane = g4x_update_plane; intel_plane->disable_plane = g4x_disable_plane; + intel_plane->get_hw_state = g4x_plane_get_hw_state; modifiers = i9xx_plane_format_modifiers; if (IS_GEN6(dev_priv)) { @@ -1301,7 +1384,7 @@ intel_sprite_plane_create(struct drm_i915_private *dev_priv, } intel_plane->pipe = pipe; - intel_plane->plane = plane; + intel_plane->i9xx_plane = plane; intel_plane->id = PLANE_SPRITE0 + plane; intel_plane->frontbuffer_bit = INTEL_FRONTBUFFER_SPRITE(pipe, plane); intel_plane->check_plane = intel_check_sprite_plane; diff --git a/drivers/gpu/drm/i915/intel_uc.c b/drivers/gpu/drm/i915/intel_uc.c index 1e2a30a40ede..907deac6e3fa 100644 --- a/drivers/gpu/drm/i915/intel_uc.c +++ b/drivers/gpu/drm/i915/intel_uc.c @@ -24,6 +24,7 @@ #include "intel_uc.h" #include "intel_guc_submission.h" +#include "intel_guc.h" #include "i915_drv.h" /* Reset GuC providing us with fresh state for both GuC and HuC. @@ -47,55 +48,93 @@ static int __intel_uc_reset_hw(struct drm_i915_private *dev_priv) return ret; } -void intel_uc_sanitize_options(struct drm_i915_private *dev_priv) +static int __get_platform_enable_guc(struct drm_i915_private *dev_priv) { - if (!HAS_GUC(dev_priv)) { - if (i915_modparams.enable_guc_loading > 0 || - i915_modparams.enable_guc_submission > 0) - DRM_INFO("Ignoring GuC options, no hardware\n"); + struct intel_uc_fw *guc_fw = &dev_priv->guc.fw; + struct intel_uc_fw *huc_fw = &dev_priv->huc.fw; + int enable_guc = 0; - i915_modparams.enable_guc_loading = 0; - i915_modparams.enable_guc_submission = 0; - return; - } + /* Default is to enable GuC/HuC if we know their firmwares */ + if (intel_uc_fw_is_selected(guc_fw)) + enable_guc |= ENABLE_GUC_SUBMISSION; + if (intel_uc_fw_is_selected(huc_fw)) + enable_guc |= ENABLE_GUC_LOAD_HUC; - /* A negative value means "use platform default" */ - if (i915_modparams.enable_guc_loading < 0) - i915_modparams.enable_guc_loading = HAS_GUC_UCODE(dev_priv); + /* Any platform specific fine-tuning can be done here */ + + return enable_guc; +} - /* Verify firmware version */ - if (i915_modparams.enable_guc_loading) { - if (HAS_HUC_UCODE(dev_priv)) - intel_huc_select_fw(&dev_priv->huc); +/** + * intel_uc_sanitize_options - sanitize uC related modparam options + * @dev_priv: device private + * + * In case of "enable_guc" option this function will attempt to modify + * it only if it was initially set to "auto(-1)". Default value for this + * modparam varies between platforms and it is hardcoded in driver code. + * Any other modparam value is only monitored against availability of the + * related hardware or firmware definitions. + */ +void intel_uc_sanitize_options(struct drm_i915_private *dev_priv) +{ + struct intel_uc_fw *guc_fw = &dev_priv->guc.fw; + struct intel_uc_fw *huc_fw = &dev_priv->huc.fw; - if (intel_guc_fw_select(&dev_priv->guc)) - i915_modparams.enable_guc_loading = 0; + /* A negative value means "use platform default" */ + if (i915_modparams.enable_guc < 0) + i915_modparams.enable_guc = __get_platform_enable_guc(dev_priv); + + DRM_DEBUG_DRIVER("enable_guc=%d (submission:%s huc:%s)\n", + i915_modparams.enable_guc, + yesno(intel_uc_is_using_guc_submission()), + yesno(intel_uc_is_using_huc())); + + /* Verify GuC firmware availability */ + if (intel_uc_is_using_guc() && !intel_uc_fw_is_selected(guc_fw)) { + DRM_WARN("Incompatible option detected: enable_guc=%d, %s!\n", + i915_modparams.enable_guc, + !HAS_GUC(dev_priv) ? "no GuC hardware" : + "no GuC firmware"); } - /* Can't enable guc submission without guc loaded */ - if (!i915_modparams.enable_guc_loading) - i915_modparams.enable_guc_submission = 0; + /* Verify HuC firmware availability */ + if (intel_uc_is_using_huc() && !intel_uc_fw_is_selected(huc_fw)) { + DRM_WARN("Incompatible option detected: enable_guc=%d, %s!\n", + i915_modparams.enable_guc, + !HAS_HUC(dev_priv) ? "no HuC hardware" : + "no HuC firmware"); + } - /* A negative value means "use platform default" */ - if (i915_modparams.enable_guc_submission < 0) - i915_modparams.enable_guc_submission = HAS_GUC_SCHED(dev_priv); + /* Make sure that sanitization was done */ + GEM_BUG_ON(i915_modparams.enable_guc < 0); } void intel_uc_init_early(struct drm_i915_private *dev_priv) { intel_guc_init_early(&dev_priv->guc); + intel_huc_init_early(&dev_priv->huc); } void intel_uc_init_fw(struct drm_i915_private *dev_priv) { - intel_uc_fw_fetch(dev_priv, &dev_priv->huc.fw); + if (!USES_GUC(dev_priv)) + return; + + if (USES_HUC(dev_priv)) + intel_uc_fw_fetch(dev_priv, &dev_priv->huc.fw); + intel_uc_fw_fetch(dev_priv, &dev_priv->guc.fw); } void intel_uc_fini_fw(struct drm_i915_private *dev_priv) { + if (!USES_GUC(dev_priv)) + return; + intel_uc_fw_fini(&dev_priv->guc.fw); - intel_uc_fw_fini(&dev_priv->huc.fw); + + if (USES_HUC(dev_priv)) + intel_uc_fw_fini(&dev_priv->huc.fw); } /** @@ -149,30 +188,91 @@ static void guc_disable_communication(struct intel_guc *guc) guc->send = intel_guc_send_nop; } -int intel_uc_init_hw(struct drm_i915_private *dev_priv) +int intel_uc_init_wq(struct drm_i915_private *dev_priv) +{ + int ret; + + if (!USES_GUC(dev_priv)) + return 0; + + ret = intel_guc_init_wq(&dev_priv->guc); + if (ret) { + DRM_ERROR("Couldn't allocate workqueues for GuC\n"); + return ret; + } + + return 0; +} + +void intel_uc_fini_wq(struct drm_i915_private *dev_priv) +{ + if (!USES_GUC(dev_priv)) + return; + + GEM_BUG_ON(!HAS_GUC(dev_priv)); + + intel_guc_fini_wq(&dev_priv->guc); +} + +int intel_uc_init(struct drm_i915_private *dev_priv) { struct intel_guc *guc = &dev_priv->guc; - int ret, attempts; + int ret; - if (!i915_modparams.enable_guc_loading) + if (!USES_GUC(dev_priv)) return 0; - guc_disable_communication(guc); - gen9_reset_guc_interrupts(dev_priv); + if (!HAS_GUC(dev_priv)) + return -ENODEV; - /* We need to notify the guc whenever we change the GGTT */ - i915_ggtt_enable_guc(dev_priv); + ret = intel_guc_init(guc); + if (ret) + return ret; - if (i915_modparams.enable_guc_submission) { + if (USES_GUC_SUBMISSION(dev_priv)) { /* * This is stuff we need to have available at fw load time * if we are planning to enable submission later */ ret = intel_guc_submission_init(guc); - if (ret) - goto err_guc; + if (ret) { + intel_guc_fini(guc); + return ret; + } } + return 0; +} + +void intel_uc_fini(struct drm_i915_private *dev_priv) +{ + struct intel_guc *guc = &dev_priv->guc; + + if (!USES_GUC(dev_priv)) + return; + + GEM_BUG_ON(!HAS_GUC(dev_priv)); + + if (USES_GUC_SUBMISSION(dev_priv)) + intel_guc_submission_fini(guc); + + intel_guc_fini(guc); +} + +int intel_uc_init_hw(struct drm_i915_private *dev_priv) +{ + struct intel_guc *guc = &dev_priv->guc; + struct intel_huc *huc = &dev_priv->huc; + int ret, attempts; + + if (!USES_GUC(dev_priv)) + return 0; + + GEM_BUG_ON(!HAS_GUC(dev_priv)); + + guc_disable_communication(guc); + gen9_reset_guc_interrupts(dev_priv); + /* init WOPCM */ I915_WRITE(GUC_WOPCM_SIZE, intel_guc_wopcm_size(dev_priv)); I915_WRITE(DMA_GUC_WOPCM_OFFSET, @@ -192,9 +292,14 @@ int intel_uc_init_hw(struct drm_i915_private *dev_priv) */ ret = __intel_uc_reset_hw(dev_priv); if (ret) - goto err_submission; + goto err_out; + + if (USES_HUC(dev_priv)) { + ret = intel_huc_init_hw(huc); + if (ret) + goto err_out; + } - intel_huc_init_hw(&dev_priv->huc); intel_guc_init_params(guc); ret = intel_guc_fw_upload(guc); if (ret == 0 || ret != -EAGAIN) @@ -212,8 +317,13 @@ int intel_uc_init_hw(struct drm_i915_private *dev_priv) if (ret) goto err_log_capture; - intel_huc_auth(&dev_priv->huc); - if (i915_modparams.enable_guc_submission) { + if (USES_HUC(dev_priv)) { + ret = intel_huc_auth(huc); + if (ret) + goto err_communication; + } + + if (USES_GUC_SUBMISSION(dev_priv)) { if (i915_modparams.guc_log_level >= 0) gen9_enable_guc_interrupts(dev_priv); @@ -222,50 +332,33 @@ int intel_uc_init_hw(struct drm_i915_private *dev_priv) goto err_interrupts; } - dev_info(dev_priv->drm.dev, "GuC %s (firmware %s [version %u.%u])\n", - i915_modparams.enable_guc_submission ? "submission enabled" : - "loaded", - guc->fw.path, + dev_info(dev_priv->drm.dev, "GuC firmware version %u.%u\n", guc->fw.major_ver_found, guc->fw.minor_ver_found); + dev_info(dev_priv->drm.dev, "GuC submission %s\n", + enableddisabled(USES_GUC_SUBMISSION(dev_priv))); + dev_info(dev_priv->drm.dev, "HuC %s\n", + enableddisabled(USES_HUC(dev_priv))); return 0; /* * We've failed to load the firmware :( - * - * Decide whether to disable GuC submission and fall back to - * execlist mode, and whether to hide the error by returning - * zero or to return -EIO, which the caller will treat as a - * nonfatal error (i.e. it doesn't prevent driver load, but - * marks the GPU as wedged until reset). */ err_interrupts: - guc_disable_communication(guc); gen9_disable_guc_interrupts(dev_priv); +err_communication: + guc_disable_communication(guc); err_log_capture: guc_capture_load_err_log(guc); -err_submission: - if (i915_modparams.enable_guc_submission) - intel_guc_submission_fini(guc); -err_guc: - i915_ggtt_disable_guc(dev_priv); - - if (i915_modparams.enable_guc_loading > 1 || - i915_modparams.enable_guc_submission > 1) { - DRM_ERROR("GuC init failed. Firmware loading disabled.\n"); - ret = -EIO; - } else { - DRM_NOTE("GuC init failed. Firmware loading disabled.\n"); - ret = 0; - } - - if (i915_modparams.enable_guc_submission) { - i915_modparams.enable_guc_submission = 0; - DRM_NOTE("Falling back from GuC submission to execlist mode\n"); - } - - i915_modparams.enable_guc_loading = 0; +err_out: + /* + * Note that there is no fallback as either user explicitly asked for + * the GuC or driver default option was to run with the GuC enabled. + */ + if (GEM_WARN_ON(ret == -EIO)) + ret = -EINVAL; + dev_err(dev_priv->drm.dev, "GuC initialization failed %d\n", ret); return ret; } @@ -275,18 +368,16 @@ void intel_uc_fini_hw(struct drm_i915_private *dev_priv) guc_free_load_err_log(guc); - if (!i915_modparams.enable_guc_loading) + if (!USES_GUC(dev_priv)) return; - if (i915_modparams.enable_guc_submission) + GEM_BUG_ON(!HAS_GUC(dev_priv)); + + if (USES_GUC_SUBMISSION(dev_priv)) intel_guc_submission_disable(guc); guc_disable_communication(guc); - if (i915_modparams.enable_guc_submission) { + if (USES_GUC_SUBMISSION(dev_priv)) gen9_disable_guc_interrupts(dev_priv); - intel_guc_submission_fini(guc); - } - - i915_ggtt_disable_guc(dev_priv); } diff --git a/drivers/gpu/drm/i915/intel_uc.h b/drivers/gpu/drm/i915/intel_uc.h index e18d3bb02088..8a7249722ef1 100644 --- a/drivers/gpu/drm/i915/intel_uc.h +++ b/drivers/gpu/drm/i915/intel_uc.h @@ -26,13 +26,36 @@ #include "intel_guc.h" #include "intel_huc.h" +#include "i915_params.h" void intel_uc_sanitize_options(struct drm_i915_private *dev_priv); void intel_uc_init_early(struct drm_i915_private *dev_priv); void intel_uc_init_mmio(struct drm_i915_private *dev_priv); void intel_uc_init_fw(struct drm_i915_private *dev_priv); void intel_uc_fini_fw(struct drm_i915_private *dev_priv); +int intel_uc_init_wq(struct drm_i915_private *dev_priv); +void intel_uc_fini_wq(struct drm_i915_private *dev_priv); int intel_uc_init_hw(struct drm_i915_private *dev_priv); void intel_uc_fini_hw(struct drm_i915_private *dev_priv); +int intel_uc_init(struct drm_i915_private *dev_priv); +void intel_uc_fini(struct drm_i915_private *dev_priv); + +static inline bool intel_uc_is_using_guc(void) +{ + GEM_BUG_ON(i915_modparams.enable_guc < 0); + return i915_modparams.enable_guc > 0; +} + +static inline bool intel_uc_is_using_guc_submission(void) +{ + GEM_BUG_ON(i915_modparams.enable_guc < 0); + return i915_modparams.enable_guc & ENABLE_GUC_SUBMISSION; +} + +static inline bool intel_uc_is_using_huc(void) +{ + GEM_BUG_ON(i915_modparams.enable_guc < 0); + return i915_modparams.enable_guc & ENABLE_GUC_LOAD_HUC; +} #endif diff --git a/drivers/gpu/drm/i915/intel_uc_fw.c b/drivers/gpu/drm/i915/intel_uc_fw.c index 4bc82d3005ff..784eff9cdfc8 100644 --- a/drivers/gpu/drm/i915/intel_uc_fw.c +++ b/drivers/gpu/drm/i915/intel_uc_fw.c @@ -105,7 +105,7 @@ void intel_uc_fw_fetch(struct drm_i915_private *dev_priv, } /* now RSA */ - if (css->key_size_dw != UOS_RSA_SCRATCH_MAX_COUNT) { + if (css->key_size_dw != UOS_RSA_SCRATCH_COUNT) { DRM_WARN("%s: Mismatched firmware RSA key size (%u)\n", intel_uc_fw_type_repr(uc_fw->type), css->key_size_dw); err = -ENOEXEC; @@ -214,7 +214,7 @@ int intel_uc_fw_upload(struct intel_uc_fw *uc_fw, intel_uc_fw_type_repr(uc_fw->type), uc_fw->path); if (uc_fw->fetch_status != INTEL_UC_FIRMWARE_SUCCESS) - return -EIO; + return -ENOEXEC; uc_fw->load_status = INTEL_UC_FIRMWARE_PENDING; DRM_DEBUG_DRIVER("%s fw load %s\n", diff --git a/drivers/gpu/drm/i915/intel_uc_fw.h b/drivers/gpu/drm/i915/intel_uc_fw.h index 5394d9d1e683..d5fd4609c785 100644 --- a/drivers/gpu/drm/i915/intel_uc_fw.h +++ b/drivers/gpu/drm/i915/intel_uc_fw.h @@ -110,6 +110,11 @@ void intel_uc_fw_init(struct intel_uc_fw *uc_fw, enum intel_uc_fw_type type) uc_fw->type = type; } +static inline bool intel_uc_fw_is_selected(struct intel_uc_fw *uc_fw) +{ + return uc_fw->path != NULL; +} + void intel_uc_fw_fetch(struct drm_i915_private *dev_priv, struct intel_uc_fw *uc_fw); int intel_uc_fw_upload(struct intel_uc_fw *uc_fw, diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index b4621271e7a2..89547b614aa6 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -565,9 +565,6 @@ void intel_uncore_runtime_resume(struct drm_i915_private *dev_priv) void intel_uncore_sanitize(struct drm_i915_private *dev_priv) { - i915_modparams.enable_rc6 = - sanitize_rc6_option(dev_priv, i915_modparams.enable_rc6); - /* BIOS often leaves RC6 enabled, but disable it for hw init */ intel_sanitize_gt_powersave(dev_priv); } diff --git a/drivers/gpu/drm/i915/selftests/huge_pages.c b/drivers/gpu/drm/i915/selftests/huge_pages.c index 01af540b6ef9..e6b31041cc88 100644 --- a/drivers/gpu/drm/i915/selftests/huge_pages.c +++ b/drivers/gpu/drm/i915/selftests/huge_pages.c @@ -27,6 +27,7 @@ #include <linux/prime_numbers.h> #include "mock_drm.h" +#include "i915_random.h" static const unsigned int page_sizes[] = { I915_GTT_PAGE_SIZE_2M, @@ -989,17 +990,9 @@ static int gpu_write(struct i915_vma *vma, i915_vma_unpin(batch); i915_vma_close(batch); - err = rq->engine->emit_flush(rq, EMIT_INVALIDATE); - if (err) - goto err_request; - - err = i915_switch_context(rq); - if (err) - goto err_request; - - err = rq->engine->emit_bb_start(rq, - batch->node.start, batch->node.size, - flags); + err = engine->emit_bb_start(rq, + batch->node.start, batch->node.size, + flags); if (err) goto err_request; @@ -1047,19 +1040,78 @@ static int cpu_check(struct drm_i915_gem_object *obj, u32 dword, u32 val) return err; } +static int __igt_write_huge(struct i915_gem_context *ctx, + struct intel_engine_cs *engine, + struct drm_i915_gem_object *obj, + u64 size, u64 offset, + u32 dword, u32 val) +{ + struct drm_i915_private *i915 = to_i915(obj->base.dev); + struct i915_address_space *vm = ctx->ppgtt ? &ctx->ppgtt->base : &i915->ggtt.base; + unsigned int flags = PIN_USER | PIN_OFFSET_FIXED; + struct i915_vma *vma; + int err; + + vma = i915_vma_instance(obj, vm, NULL); + if (IS_ERR(vma)) + return PTR_ERR(vma); + + err = i915_vma_unbind(vma); + if (err) + goto out_vma_close; + + err = i915_vma_pin(vma, size, 0, flags | offset); + if (err) { + /* + * The ggtt may have some pages reserved so + * refrain from erroring out. + */ + if (err == -ENOSPC && i915_is_ggtt(vm)) + err = 0; + + goto out_vma_close; + } + + err = igt_check_page_sizes(vma); + if (err) + goto out_vma_unpin; + + err = gpu_write(vma, ctx, engine, dword, val); + if (err) { + pr_err("gpu-write failed at offset=%llx\n", offset); + goto out_vma_unpin; + } + + err = cpu_check(obj, dword, val); + if (err) { + pr_err("cpu-check failed at offset=%llx\n", offset); + goto out_vma_unpin; + } + +out_vma_unpin: + i915_vma_unpin(vma); +out_vma_close: + i915_vma_close(vma); + + return err; +} + static int igt_write_huge(struct i915_gem_context *ctx, struct drm_i915_gem_object *obj) { struct drm_i915_private *i915 = to_i915(obj->base.dev); struct i915_address_space *vm = ctx->ppgtt ? &ctx->ppgtt->base : &i915->ggtt.base; + static struct intel_engine_cs *engines[I915_NUM_ENGINES]; struct intel_engine_cs *engine; - struct i915_vma *vma; - unsigned int flags = PIN_USER | PIN_OFFSET_FIXED; + I915_RND_STATE(prng); + IGT_TIMEOUT(end_time); unsigned int max_page_size; unsigned int id; u64 max; u64 num; u64 size; + int *order; + int i, n; int err = 0; GEM_BUG_ON(!i915_gem_object_has_pinned_pages(obj)); @@ -1071,78 +1123,56 @@ static int igt_write_huge(struct i915_gem_context *ctx, max_page_size = rounddown_pow_of_two(obj->mm.page_sizes.sg); max = div_u64((vm->total - size), max_page_size); - vma = i915_vma_instance(obj, vm, NULL); - if (IS_ERR(vma)) - return PTR_ERR(vma); - + n = 0; for_each_engine(engine, i915, id) { - IGT_TIMEOUT(end_time); - if (!intel_engine_can_store_dword(engine)) { - pr_info("store-dword-imm not supported on engine=%u\n", - id); + pr_info("store-dword-imm not supported on engine=%u\n", id); continue; } + engines[n++] = engine; + } - /* - * Try various offsets until we timeout -- we want to avoid - * issues hidden by effectively always using offset = 0. - */ - for_each_prime_number_from(num, 0, max) { - u64 offset = num * max_page_size; - u32 dword; - - err = i915_vma_unbind(vma); - if (err) - goto out_vma_close; - - err = i915_vma_pin(vma, size, max_page_size, flags | offset); - if (err) { - /* - * The ggtt may have some pages reserved so - * refrain from erroring out. - */ - if (err == -ENOSPC && i915_is_ggtt(vm)) { - err = 0; - continue; - } - - goto out_vma_close; - } + if (!n) + return 0; - err = igt_check_page_sizes(vma); - if (err) - goto out_vma_unpin; + /* + * To keep things interesting when alternating between engines in our + * randomized order, lets also make feeding to the same engine a few + * times in succession a possibility by enlarging the permutation array. + */ + order = i915_random_order(n * I915_NUM_ENGINES, &prng); + if (!order) + return -ENOMEM; - dword = offset_in_page(num) / 4; + /* + * Try various offsets in an ascending/descending fashion until we + * timeout -- we want to avoid issues hidden by effectively always using + * offset = 0. + */ + i = 0; + for_each_prime_number_from(num, 0, max) { + u64 offset_low = num * max_page_size; + u64 offset_high = (max - num) * max_page_size; + u32 dword = offset_in_page(num) / 4; - err = gpu_write(vma, ctx, engine, dword, num + 1); - if (err) { - pr_err("gpu-write failed at offset=%llx", offset); - goto out_vma_unpin; - } + engine = engines[order[i] % n]; + i = (i + 1) % (n * I915_NUM_ENGINES); - err = cpu_check(obj, dword, num + 1); - if (err) { - pr_err("cpu-check failed at offset=%llx", offset); - goto out_vma_unpin; - } + err = __igt_write_huge(ctx, engine, obj, size, offset_low, dword, num + 1); + if (err) + break; - i915_vma_unpin(vma); + err = __igt_write_huge(ctx, engine, obj, size, offset_high, dword, num + 1); + if (err) + break; - if (num > 0 && - igt_timeout(end_time, - "%s timed out on engine=%u at offset=%llx, max_page_size=%x\n", - __func__, id, offset, max_page_size)) - break; - } + if (igt_timeout(end_time, + "%s timed out on engine=%u, offset_low=%llx offset_high=%llx, max_page_size=%x\n", + __func__, engine->id, offset_low, offset_high, max_page_size)) + break; } -out_vma_unpin: - if (i915_vma_is_pinned(vma)) - i915_vma_unpin(vma); -out_vma_close: - i915_vma_close(vma); + kfree(order); return err; } @@ -1719,6 +1749,7 @@ int i915_gem_huge_page_live_selftests(struct drm_i915_private *dev_priv) return PTR_ERR(file); mutex_lock(&dev_priv->drm.struct_mutex); + intel_runtime_pm_get(dev_priv); ctx = live_context(dev_priv, file); if (IS_ERR(ctx)) { @@ -1729,6 +1760,7 @@ int i915_gem_huge_page_live_selftests(struct drm_i915_private *dev_priv) err = i915_subtests(tests, ctx); out_unlock: + intel_runtime_pm_put(dev_priv); mutex_unlock(&dev_priv->drm.struct_mutex); mock_file_free(dev_priv, file); diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_context.c b/drivers/gpu/drm/i915/selftests/i915_gem_context.c index c82780a9d455..56a803d11916 100644 --- a/drivers/gpu/drm/i915/selftests/i915_gem_context.c +++ b/drivers/gpu/drm/i915/selftests/i915_gem_context.c @@ -158,14 +158,6 @@ static int gpu_fill(struct drm_i915_gem_object *obj, goto err_batch; } - err = engine->emit_flush(rq, EMIT_INVALIDATE); - if (err) - goto err_request; - - err = i915_switch_context(rq); - if (err) - goto err_request; - flags = 0; if (INTEL_GEN(vm->i915) <= 5) flags |= I915_DISPATCH_SECURE; @@ -272,6 +264,23 @@ out_unmap: return err; } +static int file_add_object(struct drm_file *file, + struct drm_i915_gem_object *obj) +{ + int err; + + GEM_BUG_ON(obj->base.handle_count); + + /* tie the object to the drm_file for easy reaping */ + err = idr_alloc(&file->object_idr, &obj->base, 1, 0, GFP_KERNEL); + if (err < 0) + return err; + + i915_gem_object_get(obj); + obj->base.handle_count++; + return 0; +} + static struct drm_i915_gem_object * create_test_object(struct i915_gem_context *ctx, struct drm_file *file, @@ -281,7 +290,6 @@ create_test_object(struct i915_gem_context *ctx, struct i915_address_space *vm = ctx->ppgtt ? &ctx->ppgtt->base : &ctx->i915->ggtt.base; u64 size; - u32 handle; int err; size = min(vm->total / 2, 1024ull * DW_PER_PAGE * PAGE_SIZE); @@ -291,8 +299,7 @@ create_test_object(struct i915_gem_context *ctx, if (IS_ERR(obj)) return obj; - /* tie the handle to the drm_file for easy reaping */ - err = drm_gem_handle_create(file, &obj->base, &handle); + err = file_add_object(file, obj); i915_gem_object_put(obj); if (err) return ERR_PTR(err); @@ -369,7 +376,9 @@ static int igt_ctx_exec(void *arg) } } + intel_runtime_pm_get(i915); err = gpu_fill(obj, ctx, engine, dw); + intel_runtime_pm_put(i915); if (err) { pr_err("Failed to fill dword %lu [%lu/%lu] with gpu (%s) in ctx %u [full-ppgtt? %s], err=%d\n", ndwords, dw, max_dwords(obj), diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_evict.c b/drivers/gpu/drm/i915/selftests/i915_gem_evict.c index f463105ff48d..e1ddad635d73 100644 --- a/drivers/gpu/drm/i915/selftests/i915_gem_evict.c +++ b/drivers/gpu/drm/i915/selftests/i915_gem_evict.c @@ -355,6 +355,7 @@ static int igt_evict_contexts(void *arg) return 0; mutex_lock(&i915->drm.struct_mutex); + intel_runtime_pm_get(i915); /* Reserve a block so that we know we have enough to fit a few rq */ memset(&hole, 0, sizeof(hole)); @@ -463,6 +464,7 @@ out_locked: } if (drm_mm_node_allocated(&hole)) drm_mm_remove_node(&hole); + intel_runtime_pm_put(i915); mutex_unlock(&i915->drm.struct_mutex); return err; diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c index 6491cf0a4f46..4a28d713a7d8 100644 --- a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c @@ -1074,7 +1074,7 @@ static int igt_ggtt_page(void *arg) i915_gem_object_get_dma_address(obj, 0), offset, I915_CACHE_NONE, 0); - vaddr = io_mapping_map_atomic_wc(&ggtt->mappable, offset); + vaddr = io_mapping_map_atomic_wc(&ggtt->iomap, offset); iowrite32(n, vaddr + n); io_mapping_unmap_atomic(vaddr); @@ -1092,7 +1092,7 @@ static int igt_ggtt_page(void *arg) i915_gem_object_get_dma_address(obj, 0), offset, I915_CACHE_NONE, 0); - vaddr = io_mapping_map_atomic_wc(&ggtt->mappable, offset); + vaddr = io_mapping_map_atomic_wc(&ggtt->iomap, offset); val = ioread32(vaddr + n); io_mapping_unmap_atomic(vaddr); diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_request.c b/drivers/gpu/drm/i915/selftests/i915_gem_request.c index 6bce99050e94..647bf2bbd799 100644 --- a/drivers/gpu/drm/i915/selftests/i915_gem_request.c +++ b/drivers/gpu/drm/i915/selftests/i915_gem_request.c @@ -459,14 +459,6 @@ empty_request(struct intel_engine_cs *engine, if (IS_ERR(request)) return request; - err = engine->emit_flush(request, EMIT_INVALIDATE); - if (err) - goto out_request; - - err = i915_switch_context(request); - if (err) - goto out_request; - err = engine->emit_bb_start(request, batch->node.start, batch->node.size, @@ -675,12 +667,6 @@ static int live_all_engines(void *arg) goto out_request; } - err = engine->emit_flush(request[id], EMIT_INVALIDATE); - GEM_BUG_ON(err); - - err = i915_switch_context(request[id]); - GEM_BUG_ON(err); - err = engine->emit_bb_start(request[id], batch->node.start, batch->node.size, @@ -797,12 +783,6 @@ static int live_sequential_engines(void *arg) } } - err = engine->emit_flush(request[id], EMIT_INVALIDATE); - GEM_BUG_ON(err); - - err = i915_switch_context(request[id]); - GEM_BUG_ON(err); - err = engine->emit_bb_start(request[id], batch->node.start, batch->node.size, diff --git a/drivers/gpu/drm/i915/selftests/i915_random.c b/drivers/gpu/drm/i915/selftests/i915_random.c index b85872cc7fbe..2088ae57aa89 100644 --- a/drivers/gpu/drm/i915/selftests/i915_random.c +++ b/drivers/gpu/drm/i915/selftests/i915_random.c @@ -57,7 +57,7 @@ unsigned int *i915_random_order(unsigned int count, struct rnd_state *state) { unsigned int *order, i; - order = kmalloc_array(count, sizeof(*order), GFP_KERNEL); + order = kmalloc_array(count, sizeof(*order), GFP_KERNEL | __GFP_NOWARN); if (!order) return order; diff --git a/drivers/gpu/drm/i915/selftests/intel_guc.c b/drivers/gpu/drm/i915/selftests/intel_guc.c index f10029e18820..3f9016466dea 100644 --- a/drivers/gpu/drm/i915/selftests/intel_guc.c +++ b/drivers/gpu/drm/i915/selftests/intel_guc.c @@ -27,7 +27,7 @@ /* max doorbell number + negative test for each client type */ #define ATTEMPTS (GUC_NUM_DOORBELLS + GUC_CLIENT_PRIORITY_NUM) -struct intel_guc_client *clients[ATTEMPTS]; +static struct intel_guc_client *clients[ATTEMPTS]; static bool available_dbs(struct intel_guc *guc, u32 priority) { @@ -85,21 +85,26 @@ static int validate_client(struct intel_guc_client *client, return 0; } +static bool client_doorbell_in_sync(struct intel_guc_client *client) +{ + return doorbell_ok(client->guc, client->doorbell_id); +} + /* - * Check that guc_init_doorbell_hw is doing what it should. + * Check that we're able to synchronize guc_clients with their doorbells * - * During GuC submission enable, we create GuC clients and their doorbells, - * but after resetting the microcontroller (resume & gpu reset), these - * GuC clients are still around, but the status of their doorbells may be - * incorrect. This is the reason behind validating that the doorbells status - * expected by the driver matches what the GuC/HW have. + * We're creating clients and reserving doorbells once, at module load. During + * module lifetime, GuC, doorbell HW, and i915 state may go out of sync due to + * GuC being reset. In other words - GuC clients are still around, but the + * status of their doorbells may be incorrect. This is the reason behind + * validating that the doorbells status expected by the driver matches what the + * GuC/HW have. */ -static int igt_guc_init_doorbell_hw(void *args) +static int igt_guc_clients(void *args) { struct drm_i915_private *dev_priv = args; struct intel_guc *guc; - DECLARE_BITMAP(db_bitmap_bk, GUC_NUM_DOORBELLS); - int i, err = 0; + int err = 0; GEM_BUG_ON(!HAS_GUC(dev_priv)); mutex_lock(&dev_priv->drm.struct_mutex); @@ -131,6 +136,8 @@ static int igt_guc_init_doorbell_hw(void *args) pr_err("Failed to create clients\n"); goto unlock; } + GEM_BUG_ON(!guc->execbuf_client); + GEM_BUG_ON(!guc->preempt_client); err = validate_client(guc->execbuf_client, GUC_CLIENT_PRIORITY_KMD_NORMAL, false); @@ -146,10 +153,21 @@ static int igt_guc_init_doorbell_hw(void *args) goto out; } - /* each client should have received a doorbell during alloc */ + /* each client should now have reserved a doorbell */ if (!has_doorbell(guc->execbuf_client) || !has_doorbell(guc->preempt_client)) { - pr_err("guc_clients_create didn't create doorbells\n"); + pr_err("guc_clients_create didn't reserve doorbells\n"); + err = -EINVAL; + goto out; + } + + /* Now create the doorbells */ + guc_clients_doorbell_init(guc); + + /* each client should now have received a doorbell */ + if (!client_doorbell_in_sync(guc->execbuf_client) || + !client_doorbell_in_sync(guc->preempt_client)) { + pr_err("failed to initialize the doorbells\n"); err = -EINVAL; goto out; } @@ -158,25 +176,26 @@ static int igt_guc_init_doorbell_hw(void *args) * Basic test - an attempt to reallocate a valid doorbell to the * client it is currently assigned should not cause a failure. */ - err = guc_init_doorbell_hw(guc); + err = guc_clients_doorbell_init(guc); if (err) goto out; /* * Negative test - a client with no doorbell (invalid db id). - * Each client gets a doorbell when it is created, after destroying - * the doorbell, the db id is changed to GUC_DOORBELL_INVALID and the - * firmware will reject any attempt to allocate a doorbell with an - * invalid id (db has to be reserved before allocation). + * After destroying the doorbell, the db id is changed to + * GUC_DOORBELL_INVALID and the firmware will reject any attempt to + * allocate a doorbell with an invalid id (db has to be reserved before + * allocation). */ destroy_doorbell(guc->execbuf_client); - if (has_doorbell(guc->execbuf_client)) { + if (client_doorbell_in_sync(guc->execbuf_client)) { pr_err("destroy db did not work\n"); err = -EINVAL; goto out; } - err = guc_init_doorbell_hw(guc); + unreserve_doorbell(guc->execbuf_client); + err = guc_clients_doorbell_init(guc); if (err != -EIO) { pr_err("unexpected (err = %d)", err); goto out; @@ -189,33 +208,13 @@ static int igt_guc_init_doorbell_hw(void *args) } /* clean after test */ - err = create_doorbell(guc->execbuf_client); - if (err) { - pr_err("recreate doorbell failed\n"); - goto out; - } - - /* - * Negative test - doorbell_bitmap out of sync, will trigger a few of - * WARN_ON(!doorbell_ok(guc, db_id)) but that's ok as long as the - * doorbells from our clients don't fail. - */ - bitmap_copy(db_bitmap_bk, guc->doorbell_bitmap, GUC_NUM_DOORBELLS); - for (i = 0; i < GUC_NUM_DOORBELLS; i++) - if (i % 2) - test_and_change_bit(i, guc->doorbell_bitmap); - - err = guc_init_doorbell_hw(guc); + err = reserve_doorbell(guc->execbuf_client); if (err) { - pr_err("out of sync doorbell caused an error\n"); - goto out; + pr_err("failed to reserve back the doorbell back\n"); } - - /* restore 'correct' db bitmap */ - bitmap_copy(guc->doorbell_bitmap, db_bitmap_bk, GUC_NUM_DOORBELLS); - err = guc_init_doorbell_hw(guc); + err = create_doorbell(guc->execbuf_client); if (err) { - pr_err("restored doorbell caused an error\n"); + pr_err("recreate doorbell failed\n"); goto out; } @@ -224,8 +223,11 @@ out: * Leave clean state for other test, plus the driver always destroy the * clients during unload. */ + destroy_doorbell(guc->execbuf_client); + destroy_doorbell(guc->preempt_client); guc_clients_destroy(guc); guc_clients_create(guc); + guc_clients_doorbell_init(guc); unlock: mutex_unlock(&dev_priv->drm.struct_mutex); return err; @@ -307,25 +309,7 @@ static int igt_guc_doorbells(void *arg) db_id = clients[i]->doorbell_id; - /* - * Client alloc gives us a doorbell, but we want to exercise - * this ourselves (this resembles guc_init_doorbell_hw) - */ - destroy_doorbell(clients[i]); - if (clients[i]->doorbell_id != GUC_DOORBELL_INVALID) { - pr_err("[%d] destroy db did not work!\n", i); - err = -EINVAL; - goto out; - } - - err = __reserve_doorbell(clients[i]); - if (err) { - pr_err("[%d] Failed to reserve a doorbell\n", i); - goto out; - } - - __update_doorbell_desc(clients[i], clients[i]->doorbell_id); - err = __create_doorbell(clients[i]); + err = create_doorbell(clients[i]); if (err) { pr_err("[%d] Failed to create a doorbell\n", i); goto out; @@ -346,8 +330,10 @@ static int igt_guc_doorbells(void *arg) out: for (i = 0; i < ATTEMPTS; i++) - if (!IS_ERR_OR_NULL(clients[i])) + if (!IS_ERR_OR_NULL(clients[i])) { + destroy_doorbell(clients[i]); guc_client_free(clients[i]); + } unlock: mutex_unlock(&dev_priv->drm.struct_mutex); return err; @@ -356,11 +342,11 @@ unlock: int intel_guc_live_selftest(struct drm_i915_private *dev_priv) { static const struct i915_subtest tests[] = { - SUBTEST(igt_guc_init_doorbell_hw), + SUBTEST(igt_guc_clients), SUBTEST(igt_guc_doorbells), }; - if (!i915_modparams.enable_guc_submission) + if (!USES_GUC_SUBMISSION(dev_priv)) return 0; return i915_subtests(tests, dev_priv); diff --git a/drivers/gpu/drm/i915/selftests/intel_hangcheck.c b/drivers/gpu/drm/i915/selftests/intel_hangcheck.c index 71ce06680d66..f98546b8a7fa 100644 --- a/drivers/gpu/drm/i915/selftests/intel_hangcheck.c +++ b/drivers/gpu/drm/i915/selftests/intel_hangcheck.c @@ -114,14 +114,6 @@ static int emit_recurse_batch(struct hang *h, if (err) goto unpin_vma; - err = rq->engine->emit_flush(rq, EMIT_INVALIDATE); - if (err) - goto unpin_hws; - - err = i915_switch_context(rq); - if (err) - goto unpin_hws; - i915_vma_move_to_active(vma, rq, 0); if (!i915_gem_object_has_active_reference(vma->obj)) { i915_gem_object_get(vma->obj); @@ -173,7 +165,6 @@ static int emit_recurse_batch(struct hang *h, err = rq->engine->emit_bb_start(rq, vma->node.start, PAGE_SIZE, flags); -unpin_hws: i915_vma_unpin(hws); unpin_vma: i915_vma_unpin(vma); @@ -628,7 +619,7 @@ static int igt_wait_reset(void *arg) pr_err("Failed to start request %x, at %x\n", rq->fence.seqno, hws_seqno(&h, rq)); - intel_engine_dump(rq->engine, &p); + intel_engine_dump(rq->engine, &p, "%s\n", rq->engine->name); i915_reset(i915, 0); i915_gem_set_wedged(i915); @@ -723,7 +714,8 @@ static int igt_reset_queue(void *arg) pr_err("Failed to start request %x, at %x\n", prev->fence.seqno, hws_seqno(&h, prev)); - intel_engine_dump(rq->engine, &p); + intel_engine_dump(prev->engine, &p, + "%s\n", prev->engine->name); i915_gem_request_put(rq); i915_gem_request_put(prev); @@ -829,7 +821,7 @@ static int igt_handle_error(void *arg) pr_err("Failed to start request %x, at %x\n", rq->fence.seqno, hws_seqno(&h, rq)); - intel_engine_dump(rq->engine, &p); + intel_engine_dump(rq->engine, &p, "%s\n", rq->engine->name); i915_reset(i915, 0); i915_gem_set_wedged(i915); diff --git a/drivers/gpu/drm/i915/selftests/mock_gem_device.c b/drivers/gpu/drm/i915/selftests/mock_gem_device.c index 80f152aaedf9..1bc61f3f76fc 100644 --- a/drivers/gpu/drm/i915/selftests/mock_gem_device.c +++ b/drivers/gpu/drm/i915/selftests/mock_gem_device.c @@ -85,6 +85,8 @@ static void mock_device_release(struct drm_device *dev) i915_gemfs_fini(i915); + drm_mode_config_cleanup(&i915->drm); + drm_dev_fini(&i915->drm); put_device(&i915->drm.pdev->dev); } @@ -187,7 +189,7 @@ struct drm_i915_private *mock_gem_device(void) i915->wq = alloc_ordered_workqueue("mock", 0); if (!i915->wq) - goto put_device; + goto err_drv; mock_init_contexts(i915); @@ -266,6 +268,9 @@ err_objects: kmem_cache_destroy(i915->objects); err_wq: destroy_workqueue(i915->wq); +err_drv: + drm_mode_config_cleanup(&i915->drm); + drm_dev_fini(&i915->drm); put_device: put_device(&pdev->dev); err: diff --git a/drivers/gpu/drm/i915/selftests/mock_gtt.c b/drivers/gpu/drm/i915/selftests/mock_gtt.c index 336e1afb250f..e96873f96116 100644 --- a/drivers/gpu/drm/i915/selftests/mock_gtt.c +++ b/drivers/gpu/drm/i915/selftests/mock_gtt.c @@ -110,8 +110,8 @@ void mock_init_ggtt(struct drm_i915_private *i915) ggtt->base.i915 = i915; - ggtt->mappable_base = 0; - ggtt->mappable_end = 2048 * PAGE_SIZE; + ggtt->gmadr = (struct resource) DEFINE_RES_MEM(0, 2048 * PAGE_SIZE); + ggtt->mappable_end = resource_size(&ggtt->gmadr); ggtt->base.total = 4096 * PAGE_SIZE; ggtt->base.clear_range = nop_clear_range; diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c index 17d2f3a1c562..3f2b4afcb8a7 100644 --- a/drivers/gpu/drm/imx/imx-drm-core.c +++ b/drivers/gpu/drm/imx/imx-drm-core.c @@ -38,7 +38,6 @@ struct imx_drm_device { struct drm_device *drm; unsigned int pipes; - struct drm_fbdev_cma *fbhelper; struct drm_atomic_state *state; }; @@ -47,13 +46,6 @@ static int legacyfb_depth = 16; module_param(legacyfb_depth, int, 0444); #endif -static void imx_drm_driver_lastclose(struct drm_device *drm) -{ - struct imx_drm_device *imxdrm = drm->dev_private; - - drm_fbdev_cma_restore_mode(imxdrm->fbhelper); -} - DEFINE_DRM_GEM_CMA_FOPS(imx_drm_driver_fops); void imx_drm_connector_destroy(struct drm_connector *connector) @@ -69,13 +61,6 @@ void imx_drm_encoder_destroy(struct drm_encoder *encoder) } EXPORT_SYMBOL_GPL(imx_drm_encoder_destroy); -static void imx_drm_output_poll_changed(struct drm_device *drm) -{ - struct imx_drm_device *imxdrm = drm->dev_private; - - drm_fbdev_cma_hotplug_event(imxdrm->fbhelper); -} - static int imx_drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) { @@ -107,7 +92,7 @@ static int imx_drm_atomic_check(struct drm_device *dev, static const struct drm_mode_config_funcs imx_drm_mode_config_funcs = { .fb_create = drm_gem_fb_create, - .output_poll_changed = imx_drm_output_poll_changed, + .output_poll_changed = drm_fb_helper_output_poll_changed, .atomic_check = imx_drm_atomic_check, .atomic_commit = drm_atomic_helper_commit, }; @@ -186,7 +171,7 @@ static const struct drm_ioctl_desc imx_drm_ioctls[] = { static struct drm_driver imx_drm_driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC, - .lastclose = imx_drm_driver_lastclose, + .lastclose = drm_fb_helper_lastclose, .gem_free_object_unlocked = drm_gem_cma_free_object, .gem_vm_ops = &drm_gem_cma_vm_ops, .dumb_create = drm_gem_cma_dumb_create, @@ -298,12 +283,9 @@ static int imx_drm_bind(struct device *dev) dev_warn(dev, "Invalid legacyfb_depth. Defaulting to 16bpp\n"); legacyfb_depth = 16; } - imxdrm->fbhelper = drm_fbdev_cma_init(drm, legacyfb_depth, MAX_CRTC); - if (IS_ERR(imxdrm->fbhelper)) { - ret = PTR_ERR(imxdrm->fbhelper); - imxdrm->fbhelper = NULL; + ret = drm_fb_cma_fbdev_init(drm, legacyfb_depth, MAX_CRTC); + if (ret) goto err_unbind; - } #endif drm_kms_helper_poll_init(drm); @@ -317,8 +299,7 @@ static int imx_drm_bind(struct device *dev) err_fbhelper: drm_kms_helper_poll_fini(drm); #if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION) - if (imxdrm->fbhelper) - drm_fbdev_cma_fini(imxdrm->fbhelper); + drm_fb_cma_fbdev_fini(drm); err_unbind: #endif component_unbind_all(drm->dev, drm); @@ -333,14 +314,12 @@ err_unref: static void imx_drm_unbind(struct device *dev) { struct drm_device *drm = dev_get_drvdata(dev); - struct imx_drm_device *imxdrm = drm->dev_private; drm_dev_unregister(drm); drm_kms_helper_poll_fini(drm); - if (imxdrm->fbhelper) - drm_fbdev_cma_fini(imxdrm->fbhelper); + drm_fb_cma_fbdev_fini(drm); drm_mode_config_cleanup(drm); diff --git a/drivers/gpu/drm/imx/imx-drm.h b/drivers/gpu/drm/imx/imx-drm.h index f0b7556c0857..15c2bec47a04 100644 --- a/drivers/gpu/drm/imx/imx-drm.h +++ b/drivers/gpu/drm/imx/imx-drm.h @@ -8,7 +8,6 @@ struct drm_connector; struct drm_device; struct drm_display_mode; struct drm_encoder; -struct drm_fbdev_cma; struct drm_framebuffer; struct drm_plane; struct imx_drm_crtc; diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c index 3b804fdaf7a0..f9ad0e960263 100644 --- a/drivers/gpu/drm/meson/meson_drv.c +++ b/drivers/gpu/drm/meson/meson_drv.c @@ -151,6 +151,14 @@ static struct regmap_config meson_regmap_config = { .max_register = 0x1000, }; +static void meson_vpu_init(struct meson_drm *priv) +{ + writel_relaxed(0x210000, priv->io_base + _REG(VPU_RDARB_MODE_L1C1)); + writel_relaxed(0x10000, priv->io_base + _REG(VPU_RDARB_MODE_L1C2)); + writel_relaxed(0x900000, priv->io_base + _REG(VPU_RDARB_MODE_L2C1)); + writel_relaxed(0x20000, priv->io_base + _REG(VPU_WRARB_MODE_L2C1)); +} + static int meson_drv_bind_master(struct device *dev, bool has_components) { struct platform_device *pdev = to_platform_device(dev); @@ -222,6 +230,7 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) /* Hardware Initialization */ + meson_vpu_init(priv); meson_venc_init(priv); meson_vpp_init(priv); meson_viu_init(priv); diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c index cef414466f9f..17de3afd98f6 100644 --- a/drivers/gpu/drm/meson/meson_dw_hdmi.c +++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c @@ -23,6 +23,7 @@ #include <linux/of_graph.h> #include <linux/reset.h> #include <linux/clk.h> +#include <linux/regulator/consumer.h> #include <drm/drmP.h> #include <drm/drm_edid.h> @@ -137,6 +138,7 @@ struct meson_dw_hdmi { struct reset_control *hdmitx_phy; struct clk *hdmi_pclk; struct clk *venci_clk; + struct regulator *hdmi_supply; u32 irq_stat; }; #define encoder_to_meson_dw_hdmi(x) \ @@ -751,6 +753,17 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master, dw_plat_data = &meson_dw_hdmi->dw_plat_data; encoder = &meson_dw_hdmi->encoder; + meson_dw_hdmi->hdmi_supply = devm_regulator_get_optional(dev, "hdmi"); + if (IS_ERR(meson_dw_hdmi->hdmi_supply)) { + if (PTR_ERR(meson_dw_hdmi->hdmi_supply) == -EPROBE_DEFER) + return -EPROBE_DEFER; + meson_dw_hdmi->hdmi_supply = NULL; + } else { + ret = regulator_enable(meson_dw_hdmi->hdmi_supply); + if (ret) + return ret; + } + meson_dw_hdmi->hdmitx_apb = devm_reset_control_get_exclusive(dev, "hdmitx_apb"); if (IS_ERR(meson_dw_hdmi->hdmitx_apb)) { diff --git a/drivers/gpu/drm/meson/meson_registers.h b/drivers/gpu/drm/meson/meson_registers.h index 284738196af9..bca87143e548 100644 --- a/drivers/gpu/drm/meson/meson_registers.h +++ b/drivers/gpu/drm/meson/meson_registers.h @@ -1363,6 +1363,10 @@ #define VPU_PROT3_STAT_1 0x277a #define VPU_PROT3_STAT_2 0x277b #define VPU_PROT3_REQ_ONOFF 0x277c +#define VPU_RDARB_MODE_L1C1 0x2790 +#define VPU_RDARB_MODE_L1C2 0x2799 +#define VPU_RDARB_MODE_L2C1 0x279d +#define VPU_WRARB_MODE_L2C1 0x27a2 /* osd super scale */ #define OSDSR_HV_SIZEIN 0x3130 diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 0a3ea3034e39..d90ef1d78a1b 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -37,16 +37,9 @@ #define MSM_VERSION_MINOR 3 #define MSM_VERSION_PATCHLEVEL 0 -static void msm_fb_output_poll_changed(struct drm_device *dev) -{ - struct msm_drm_private *priv = dev->dev_private; - if (priv->fbdev) - drm_fb_helper_hotplug_event(priv->fbdev); -} - static const struct drm_mode_config_funcs mode_config_funcs = { .fb_create = msm_framebuffer_create, - .output_poll_changed = msm_fb_output_poll_changed, + .output_poll_changed = drm_fb_helper_output_poll_changed, .atomic_check = drm_atomic_helper_check, .atomic_commit = msm_atomic_commit, .atomic_state_alloc = msm_atomic_state_alloc, @@ -551,13 +544,6 @@ static void msm_postclose(struct drm_device *dev, struct drm_file *file) context_close(ctx); } -static void msm_lastclose(struct drm_device *dev) -{ - struct msm_drm_private *priv = dev->dev_private; - if (priv->fbdev) - drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev); -} - static irqreturn_t msm_irq(int irq, void *arg) { struct drm_device *dev = arg; @@ -866,7 +852,7 @@ static struct drm_driver msm_driver = { DRIVER_MODESET, .open = msm_open, .postclose = msm_postclose, - .lastclose = msm_lastclose, + .lastclose = drm_fb_helper_lastclose, .irq_handler = msm_irq, .irq_preinstall = msm_irq_preinstall, .irq_postinstall = msm_irq_postinstall, diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 2e7785f49e6d..009713404cc4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -29,6 +29,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_helper.h> #include <nvif/class.h> @@ -292,7 +293,7 @@ nouveau_user_framebuffer_create(struct drm_device *dev, static const struct drm_mode_config_funcs nouveau_mode_config_funcs = { .fb_create = nouveau_user_framebuffer_create, - .output_poll_changed = nouveau_fbcon_output_poll_changed, + .output_poll_changed = drm_fb_helper_output_poll_changed, }; diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index c533d8e04afc..45a4572cd2fb 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -413,14 +413,6 @@ out: return ret; } -void -nouveau_fbcon_output_poll_changed(struct drm_device *dev) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - if (drm->fbcon) - drm_fb_helper_hotplug_event(&drm->fbcon->helper); -} - static int nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbdev *fbcon) { diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.h b/drivers/gpu/drm/nouveau/nouveau_fbcon.h index e2bca729721e..a6f192ea3fa6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.h +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.h @@ -68,8 +68,6 @@ void nouveau_fbcon_set_suspend(struct drm_device *dev, int state); void nouveau_fbcon_accel_save_disable(struct drm_device *dev); void nouveau_fbcon_accel_restore(struct drm_device *dev); -void nouveau_fbcon_output_poll_changed(struct drm_device *dev); - extern int nouveau_nofbaccel; #endif /* __NV50_FBCON_H__ */ diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c index 52e52a360fb1..3da5a4305aa4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vga.c +++ b/drivers/gpu/drm/nouveau/nouveau_vga.c @@ -4,6 +4,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_helper.h> #include "nouveau_drv.h" #include "nouveau_acpi.h" @@ -61,7 +62,7 @@ static void nouveau_switcheroo_reprobe(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - nouveau_fbcon_output_poll_changed(dev); + drm_fb_helper_output_poll_changed(dev); } static bool diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 65336948e807..b22c37bde13f 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -4311,7 +4311,7 @@ nv50_disp_atomic_state_alloc(struct drm_device *dev) static const struct drm_mode_config_funcs nv50_disp_func = { .fb_create = nouveau_user_framebuffer_create, - .output_poll_changed = nouveau_fbcon_output_poll_changed, + .output_poll_changed = drm_fb_helper_output_poll_changed, .atomic_check = nv50_disp_atomic_check, .atomic_commit = nv50_disp_atomic_commit, .atomic_state_alloc = nv50_disp_atomic_state_alloc, diff --git a/drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c b/drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c index 542a76503fbd..95ea6abae914 100644 --- a/drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c +++ b/drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c @@ -1,7 +1,7 @@ /* * Analog TV Connector driver * - * Copyright (C) 2013 Texas Instruments + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * This program is free software; you can redistribute it and/or modify it diff --git a/drivers/gpu/drm/omapdrm/displays/connector-dvi.c b/drivers/gpu/drm/omapdrm/displays/connector-dvi.c index 05fa24a518c8..10b4b97d3595 100644 --- a/drivers/gpu/drm/omapdrm/displays/connector-dvi.c +++ b/drivers/gpu/drm/omapdrm/displays/connector-dvi.c @@ -1,7 +1,7 @@ /* * Generic DVI Connector driver * - * Copyright (C) 2013 Texas Instruments + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * This program is free software; you can redistribute it and/or modify it diff --git a/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c b/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c index 4600d3841c25..2867476419dc 100644 --- a/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c +++ b/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c @@ -1,7 +1,7 @@ /* * HDMI Connector driver * - * Copyright (C) 2013 Texas Instruments + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * This program is free software; you can redistribute it and/or modify it diff --git a/drivers/gpu/drm/omapdrm/displays/encoder-opa362.c b/drivers/gpu/drm/omapdrm/displays/encoder-opa362.c index b1f6aa09f699..d523c67a3ae3 100644 --- a/drivers/gpu/drm/omapdrm/displays/encoder-opa362.c +++ b/drivers/gpu/drm/omapdrm/displays/encoder-opa362.c @@ -6,7 +6,7 @@ * * based on encoder-tfp410 * - * Copyright (C) 2013 Texas Instruments + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * This program is free software; you can redistribute it and/or modify it diff --git a/drivers/gpu/drm/omapdrm/displays/encoder-tfp410.c b/drivers/gpu/drm/omapdrm/displays/encoder-tfp410.c index 947295f9e30f..e01ab3db6d86 100644 --- a/drivers/gpu/drm/omapdrm/displays/encoder-tfp410.c +++ b/drivers/gpu/drm/omapdrm/displays/encoder-tfp410.c @@ -1,7 +1,7 @@ /* * TFP410 DPI-to-DVI encoder driver * - * Copyright (C) 2013 Texas Instruments + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * This program is free software; you can redistribute it and/or modify it @@ -173,7 +173,8 @@ static int tfp410_probe_of(struct platform_device *pdev) if (gpio_is_valid(gpio) || gpio == -ENOENT) { ddata->pd_gpio = gpio; } else { - dev_err(&pdev->dev, "failed to parse PD gpio\n"); + if (gpio != -EPROBE_DEFER) + dev_err(&pdev->dev, "failed to parse PD gpio\n"); return gpio; } diff --git a/drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c b/drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c index e3d98d78fc40..1fd493e5fa3d 100644 --- a/drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c +++ b/drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c @@ -1,7 +1,7 @@ /* * TPD12S015 HDMI ESD protection & level shifter chip driver * - * Copyright (C) 2013 Texas Instruments + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * This program is free software; you can redistribute it and/or modify it diff --git a/drivers/gpu/drm/omapdrm/displays/panel-dpi.c b/drivers/gpu/drm/omapdrm/displays/panel-dpi.c index e065f7e10cca..efff6dbbb86f 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-dpi.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-dpi.c @@ -1,7 +1,7 @@ /* * Generic MIPI DPI Panel Driver * - * Copyright (C) 2013 Texas Instruments + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * This program is free software; you can redistribute it and/or modify it diff --git a/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c b/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c index 92c556ac22c7..15399a1a666b 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c @@ -1,7 +1,7 @@ /* * Generic DSI Command Mode panel driver * - * Copyright (C) 2013 Texas Instruments + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * This program is free software; you can redistribute it and/or modify it @@ -22,9 +22,10 @@ #include <linux/slab.h> #include <linux/workqueue.h> #include <linux/of_device.h> -#include <linux/of_gpio.h> +#include <linux/regulator/consumer.h> #include <video/mipi_display.h> +#include <video/of_display_timing.h> #include "../dss/omapdss.h" @@ -49,6 +50,7 @@ struct panel_drv_data { struct mutex lock; struct backlight_device *bldev; + struct backlight_device *extbldev; unsigned long hw_guard_end; /* next value of jiffies when we can * issue the next sleep in/out command @@ -56,11 +58,17 @@ struct panel_drv_data { unsigned long hw_guard_wait; /* max guard time in jiffies */ /* panel HW configuration from DT or platform data */ - int reset_gpio; - int ext_te_gpio; + struct gpio_desc *reset_gpio; + struct gpio_desc *ext_te_gpio; + + struct regulator *vpnl; + struct regulator *vddi; bool use_dsi_backlight; + int width_mm; + int height_mm; + struct omap_dsi_pin_config pin_config; /* runtime variables */ @@ -92,6 +100,30 @@ static int dsicm_panel_reset(struct panel_drv_data *ddata); static void dsicm_ulps_work(struct work_struct *work); +static void dsicm_bl_power(struct panel_drv_data *ddata, bool enable) +{ + struct backlight_device *backlight; + + if (ddata->bldev) + backlight = ddata->bldev; + else if (ddata->extbldev) + backlight = ddata->extbldev; + else + return; + + if (enable) { + backlight->props.fb_blank = FB_BLANK_UNBLANK; + backlight->props.state = ~(BL_CORE_FBBLANK | BL_CORE_SUSPENDED); + backlight->props.power = FB_BLANK_UNBLANK; + } else { + backlight->props.fb_blank = FB_BLANK_NORMAL; + backlight->props.power = FB_BLANK_POWERDOWN; + backlight->props.state |= BL_CORE_FBBLANK | BL_CORE_SUSPENDED; + } + + backlight_update_status(backlight); +} + static void hw_guard_start(struct panel_drv_data *ddata, int guard_msec) { ddata->hw_guard_wait = msecs_to_jiffies(guard_msec); @@ -255,8 +287,8 @@ static int dsicm_enter_ulps(struct panel_drv_data *ddata) if (r) goto err; - if (gpio_is_valid(ddata->ext_te_gpio)) - disable_irq(gpio_to_irq(ddata->ext_te_gpio)); + if (ddata->ext_te_gpio) + disable_irq(gpiod_to_irq(ddata->ext_te_gpio)); in->ops.dsi->disable(in, false, true); @@ -297,8 +329,8 @@ static int dsicm_exit_ulps(struct panel_drv_data *ddata) goto err2; } - if (gpio_is_valid(ddata->ext_te_gpio)) - enable_irq(gpio_to_irq(ddata->ext_te_gpio)); + if (ddata->ext_te_gpio) + enable_irq(gpiod_to_irq(ddata->ext_te_gpio)); dsicm_queue_ulps_work(ddata); @@ -311,8 +343,8 @@ err2: r = dsicm_panel_reset(ddata); if (!r) { - if (gpio_is_valid(ddata->ext_te_gpio)) - enable_irq(gpio_to_irq(ddata->ext_te_gpio)); + if (ddata->ext_te_gpio) + enable_irq(gpiod_to_irq(ddata->ext_te_gpio)); ddata->ulps_enabled = false; } err1: @@ -335,7 +367,7 @@ static int dsicm_bl_update_status(struct backlight_device *dev) { struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev); struct omap_dss_device *in = ddata->in; - int r; + int r = 0; int level; if (dev->props.fb_blank == FB_BLANK_UNBLANK && @@ -356,8 +388,6 @@ static int dsicm_bl_update_status(struct backlight_device *dev) r = dsicm_dcs_write_1(ddata, DCS_BRIGHTNESS, level); in->ops.dsi->bus_unlock(in); - } else { - r = 0; } mutex_unlock(&ddata->lock); @@ -560,16 +590,13 @@ static const struct attribute_group dsicm_attr_group = { static void dsicm_hw_reset(struct panel_drv_data *ddata) { - if (!gpio_is_valid(ddata->reset_gpio)) - return; - - gpio_set_value(ddata->reset_gpio, 1); + gpiod_set_value(ddata->reset_gpio, 1); udelay(10); /* reset the panel */ - gpio_set_value(ddata->reset_gpio, 0); + gpiod_set_value(ddata->reset_gpio, 0); /* assert reset */ udelay(10); - gpio_set_value(ddata->reset_gpio, 1); + gpiod_set_value(ddata->reset_gpio, 1); /* wait after releasing reset */ usleep_range(5000, 10000); } @@ -589,25 +616,43 @@ static int dsicm_power_on(struct panel_drv_data *ddata) .lp_clk_max = 10000000, }; + if (ddata->vpnl) { + r = regulator_enable(ddata->vpnl); + if (r) { + dev_err(&ddata->pdev->dev, + "failed to enable VPNL: %d\n", r); + return r; + } + } + + if (ddata->vddi) { + r = regulator_enable(ddata->vddi); + if (r) { + dev_err(&ddata->pdev->dev, + "failed to enable VDDI: %d\n", r); + goto err_vpnl; + } + } + if (ddata->pin_config.num_pins > 0) { r = in->ops.dsi->configure_pins(in, &ddata->pin_config); if (r) { dev_err(&ddata->pdev->dev, "failed to configure DSI pins\n"); - goto err0; + goto err_vddi; } } r = in->ops.dsi->set_config(in, &dsi_config); if (r) { dev_err(&ddata->pdev->dev, "failed to configure DSI\n"); - goto err0; + goto err_vddi; } r = in->ops.dsi->enable(in); if (r) { dev_err(&ddata->pdev->dev, "failed to enable DSI\n"); - goto err0; + goto err_vddi; } dsicm_hw_reset(ddata); @@ -665,7 +710,13 @@ err: dsicm_hw_reset(ddata); in->ops.dsi->disable(in, true, false); -err0: +err_vddi: + if (ddata->vddi) + regulator_disable(ddata->vddi); +err_vpnl: + if (ddata->vpnl) + regulator_disable(ddata->vpnl); + return r; } @@ -688,6 +739,11 @@ static void dsicm_power_off(struct panel_drv_data *ddata) in->ops.dsi->disable(in, true, false); + if (ddata->vddi) + regulator_disable(ddata->vddi); + if (ddata->vpnl) + regulator_disable(ddata->vpnl); + ddata->enabled = 0; } @@ -782,6 +838,8 @@ static int dsicm_enable(struct omap_dss_device *dssdev) mutex_unlock(&ddata->lock); + dsicm_bl_power(ddata, true); + return 0; err: dev_dbg(&ddata->pdev->dev, "enable failed\n"); @@ -797,6 +855,8 @@ static void dsicm_disable(struct omap_dss_device *dssdev) dev_dbg(&ddata->pdev->dev, "disable\n"); + dsicm_bl_power(ddata, false); + mutex_lock(&ddata->lock); dsicm_cancel_ulps_work(ddata); @@ -890,7 +950,7 @@ static int dsicm_update(struct omap_dss_device *dssdev, if (r) goto err; - if (ddata->te_enabled && gpio_is_valid(ddata->ext_te_gpio)) { + if (ddata->te_enabled && ddata->ext_te_gpio) { schedule_delayed_work(&ddata->te_timeout_work, msecs_to_jiffies(250)); atomic_set(&ddata->do_update, 1); @@ -937,7 +997,7 @@ static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable) else r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_TEAR_OFF); - if (!gpio_is_valid(ddata->ext_te_gpio)) + if (!ddata->ext_te_gpio) in->ops.dsi->enable_te(in, enable); /* possible panel bug */ @@ -1099,6 +1159,45 @@ static void dsicm_ulps_work(struct work_struct *work) mutex_unlock(&ddata->lock); } +static void dsicm_get_timings(struct omap_dss_device *dssdev, + struct videomode *vm) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + + *vm = ddata->vm; +} + +static int dsicm_check_timings(struct omap_dss_device *dssdev, + struct videomode *vm) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + int ret = 0; + + if (vm->hactive != ddata->vm.hactive) + ret = -EINVAL; + + if (vm->vactive != ddata->vm.vactive) + ret = -EINVAL; + + if (ret) { + dev_warn(dssdev->dev, "wrong resolution: %d x %d", + vm->hactive, vm->vactive); + dev_warn(dssdev->dev, "panel resolution: %d x %d", + ddata->vm.hactive, ddata->vm.vactive); + } + + return ret; +} + +static void dsicm_get_size(struct omap_dss_device *dssdev, + unsigned int *width, unsigned int *height) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + + *width = ddata->width_mm; + *height = ddata->height_mm; +} + static struct omap_dss_driver dsicm_ops = { .connect = dsicm_connect, .disconnect = dsicm_disconnect, @@ -1109,6 +1208,10 @@ static struct omap_dss_driver dsicm_ops = { .update = dsicm_update, .sync = dsicm_sync, + .get_timings = dsicm_get_timings, + .check_timings = dsicm_check_timings, + .get_size = dsicm_get_size, + .enable_te = dsicm_enable_te, .get_te = dsicm_get_te, @@ -1118,41 +1221,87 @@ static struct omap_dss_driver dsicm_ops = { static int dsicm_probe_of(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; + struct device_node *backlight; struct panel_drv_data *ddata = platform_get_drvdata(pdev); struct omap_dss_device *in; - int gpio; + struct display_timing timing; + int err; + + ddata->reset_gpio = devm_gpiod_get(&pdev->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ddata->reset_gpio)) { + err = PTR_ERR(ddata->reset_gpio); + dev_err(&pdev->dev, "reset gpio request failed: %d", err); + return err; + } - gpio = of_get_named_gpio(node, "reset-gpios", 0); - if (!gpio_is_valid(gpio)) { - dev_err(&pdev->dev, "failed to parse reset gpio\n"); - return gpio; + ddata->ext_te_gpio = devm_gpiod_get_optional(&pdev->dev, "te", + GPIOD_IN); + if (IS_ERR(ddata->ext_te_gpio)) { + err = PTR_ERR(ddata->ext_te_gpio); + dev_err(&pdev->dev, "TE gpio request failed: %d", err); + return err; } - ddata->reset_gpio = gpio; - gpio = of_get_named_gpio(node, "te-gpios", 0); - if (gpio_is_valid(gpio) || gpio == -ENOENT) { - ddata->ext_te_gpio = gpio; + err = of_get_display_timing(node, "panel-timing", &timing); + if (!err) { + videomode_from_timing(&timing, &ddata->vm); + if (!ddata->vm.pixelclock) + ddata->vm.pixelclock = + ddata->vm.hactive * ddata->vm.vactive * 60; } else { - dev_err(&pdev->dev, "failed to parse TE gpio\n"); - return gpio; + dev_warn(&pdev->dev, + "failed to get video timing, using defaults\n"); } + ddata->width_mm = 0; + of_property_read_u32(node, "width-mm", &ddata->width_mm); + + ddata->height_mm = 0; + of_property_read_u32(node, "height-mm", &ddata->height_mm); + in = omapdss_of_find_source_for_first_ep(node); if (IS_ERR(in)) { dev_err(&pdev->dev, "failed to find video source\n"); return PTR_ERR(in); } + ddata->vpnl = devm_regulator_get_optional(&pdev->dev, "vpnl"); + if (IS_ERR(ddata->vpnl)) { + err = PTR_ERR(ddata->vpnl); + if (err == -EPROBE_DEFER) + return err; + ddata->vpnl = NULL; + } + + ddata->vddi = devm_regulator_get_optional(&pdev->dev, "vddi"); + if (IS_ERR(ddata->vddi)) { + err = PTR_ERR(ddata->vddi); + if (err == -EPROBE_DEFER) + return err; + ddata->vddi = NULL; + } + ddata->in = in; - /* TODO: ulps, backlight */ + backlight = of_parse_phandle(node, "backlight", 0); + if (backlight) { + ddata->extbldev = of_find_backlight_by_node(backlight); + of_node_put(backlight); + + if (!ddata->extbldev) + return -EPROBE_DEFER; + } else { + /* assume native backlight support */ + ddata->use_dsi_backlight = true; + } + + /* TODO: ulps */ return 0; } static int dsicm_probe(struct platform_device *pdev) { - struct backlight_properties props; struct panel_drv_data *ddata; struct backlight_device *bldev = NULL; struct device *dev = &pdev->dev; @@ -1171,14 +1320,14 @@ static int dsicm_probe(struct platform_device *pdev) if (!pdev->dev.of_node) return -ENODEV; - r = dsicm_probe_of(pdev); - if (r) - return r; - ddata->vm.hactive = 864; ddata->vm.vactive = 480; ddata->vm.pixelclock = 864 * 480 * 60; + r = dsicm_probe_of(pdev); + if (r) + return r; + dssdev = &ddata->dssdev; dssdev->dev = dev; dssdev->driver = &dsicm_ops; @@ -1200,31 +1349,15 @@ static int dsicm_probe(struct platform_device *pdev) atomic_set(&ddata->do_update, 0); - if (gpio_is_valid(ddata->reset_gpio)) { - r = devm_gpio_request_one(dev, ddata->reset_gpio, - GPIOF_OUT_INIT_LOW, "taal rst"); - if (r) { - dev_err(dev, "failed to request reset gpio\n"); - return r; - } - } - - if (gpio_is_valid(ddata->ext_te_gpio)) { - r = devm_gpio_request_one(dev, ddata->ext_te_gpio, - GPIOF_IN, "taal irq"); - if (r) { - dev_err(dev, "GPIO request failed\n"); - return r; - } - - r = devm_request_irq(dev, gpio_to_irq(ddata->ext_te_gpio), + if (ddata->ext_te_gpio) { + r = devm_request_irq(dev, gpiod_to_irq(ddata->ext_te_gpio), dsicm_te_isr, IRQF_TRIGGER_RISING, "taal vsync", ddata); if (r) { dev_err(dev, "IRQ request failed\n"); - return r; + goto err_reg; } INIT_DEFERRABLE_WORK(&ddata->te_timeout_work, @@ -1234,48 +1367,43 @@ static int dsicm_probe(struct platform_device *pdev) } ddata->workqueue = create_singlethread_workqueue("dsicm_wq"); - if (ddata->workqueue == NULL) { - dev_err(dev, "can't create workqueue\n"); - return -ENOMEM; + if (!ddata->workqueue) { + r = -ENOMEM; + goto err_reg; } INIT_DELAYED_WORK(&ddata->ulps_work, dsicm_ulps_work); dsicm_hw_reset(ddata); if (ddata->use_dsi_backlight) { - memset(&props, 0, sizeof(props)); + struct backlight_properties props = { 0 }; props.max_brightness = 255; - props.type = BACKLIGHT_RAW; - bldev = backlight_device_register(dev_name(dev), - dev, ddata, &dsicm_bl_ops, &props); + + bldev = devm_backlight_device_register(dev, dev_name(dev), + dev, ddata, &dsicm_bl_ops, &props); if (IS_ERR(bldev)) { r = PTR_ERR(bldev); goto err_bl; } ddata->bldev = bldev; - - bldev->props.fb_blank = FB_BLANK_UNBLANK; - bldev->props.power = FB_BLANK_UNBLANK; - bldev->props.brightness = 255; - - dsicm_bl_update_status(bldev); } r = sysfs_create_group(&dev->kobj, &dsicm_attr_group); if (r) { dev_err(dev, "failed to create sysfs files\n"); - goto err_sysfs_create; + goto err_bl; } return 0; -err_sysfs_create: - backlight_device_unregister(bldev); err_bl: destroy_workqueue(ddata->workqueue); err_reg: + if (ddata->extbldev) + put_device(&ddata->extbldev->dev); + return r; } @@ -1283,7 +1411,6 @@ static int __exit dsicm_remove(struct platform_device *pdev) { struct panel_drv_data *ddata = platform_get_drvdata(pdev); struct omap_dss_device *dssdev = &ddata->dssdev; - struct backlight_device *bldev; dev_dbg(&pdev->dev, "remove\n"); @@ -1294,12 +1421,8 @@ static int __exit dsicm_remove(struct platform_device *pdev) sysfs_remove_group(&pdev->dev.kobj, &dsicm_attr_group); - bldev = ddata->bldev; - if (bldev != NULL) { - bldev->props.power = FB_BLANK_POWERDOWN; - dsicm_bl_update_status(bldev); - backlight_device_unregister(bldev); - } + if (ddata->extbldev) + put_device(&ddata->extbldev->dev); omap_dss_put_device(ddata->in); diff --git a/drivers/gpu/drm/omapdrm/displays/panel-lgphilips-lb035q02.c b/drivers/gpu/drm/omapdrm/displays/panel-lgphilips-lb035q02.c index 74d13969b9ca..57af22ce87c5 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-lgphilips-lb035q02.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-lgphilips-lb035q02.c @@ -1,7 +1,7 @@ /* * LG.Philips LB035Q02 LCD Panel driver * - * Copyright (C) 2013 Texas Instruments + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * Based on a driver by: Steve Sakoman <steve@sakoman.com> * diff --git a/drivers/gpu/drm/omapdrm/displays/panel-nec-nl8048hl11.c b/drivers/gpu/drm/omapdrm/displays/panel-nec-nl8048hl11.c index df8132d3b9c6..bf53676263ad 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-nec-nl8048hl11.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-nec-nl8048hl11.c @@ -1,7 +1,7 @@ /* * NEC NL8048HL11 Panel driver * - * Copyright (C) 2010 Texas Instruments Inc. + * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ * Author: Erik Gilling <konkers@android.com> * Converted to new DSS device model: Tomi Valkeinen <tomi.valkeinen@ti.com> * diff --git a/drivers/gpu/drm/omapdrm/displays/panel-sharp-ls037v7dw01.c b/drivers/gpu/drm/omapdrm/displays/panel-sharp-ls037v7dw01.c index 98d170aecaba..34555801fa4c 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-sharp-ls037v7dw01.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-sharp-ls037v7dw01.c @@ -1,7 +1,7 @@ /* * LCD panel driver for Sharp LS037V7DW01 * - * Copyright (C) 2013 Texas Instruments + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * This program is free software; you can redistribute it and/or modify it diff --git a/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c b/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c index 0a38a0e8c925..2721a86ac5e7 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c @@ -452,15 +452,27 @@ static int td028ttec1_panel_remove(struct spi_device *spi) } static const struct of_device_id td028ttec1_of_match[] = { + { .compatible = "omapdss,tpo,td028ttec1", }, + /* keep to not break older DTB */ { .compatible = "omapdss,toppoly,td028ttec1", }, {}, }; MODULE_DEVICE_TABLE(of, td028ttec1_of_match); +static const struct spi_device_id td028ttec1_ids[] = { + { "toppoly,td028ttec1", 0 }, + { "tpo,td028ttec1", 0}, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(spi, td028ttec1_ids); + + static struct spi_driver td028ttec1_spi_driver = { .probe = td028ttec1_panel_probe, .remove = td028ttec1_panel_remove, + .id_table = td028ttec1_ids, .driver = { .name = "panel-tpo-td028ttec1", @@ -471,7 +483,6 @@ static struct spi_driver td028ttec1_spi_driver = { module_spi_driver(td028ttec1_spi_driver); -MODULE_ALIAS("spi:toppoly,td028ttec1"); MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>"); MODULE_DESCRIPTION("Toppoly TD028TTEC1 panel driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/omapdrm/dss/base.c b/drivers/gpu/drm/omapdrm/dss/base.c index 13e91faaf7a6..67cc87a4f1f6 100644 --- a/drivers/gpu/drm/omapdrm/dss/base.c +++ b/drivers/gpu/drm/omapdrm/dss/base.c @@ -1,3 +1,18 @@ +/* + * OMAP Display Subsystem Base + * + * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> diff --git a/drivers/gpu/drm/omapdrm/dss/core.c b/drivers/gpu/drm/omapdrm/dss/core.c index 197ddbc1512b..acef7ece5783 100644 --- a/drivers/gpu/drm/omapdrm/dss/core.c +++ b/drivers/gpu/drm/omapdrm/dss/core.c @@ -1,6 +1,4 @@ /* - * linux/drivers/video/omap2/dss/core.c - * * Copyright (C) 2009 Nokia Corporation * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> * @@ -30,38 +28,21 @@ #include "dss.h" /* INIT */ -static int (*dss_output_drv_reg_funcs[])(void) __initdata = { - dss_init_platform_driver, - dispc_init_platform_driver, +static struct platform_driver * const omap_dss_drivers[] = { + &omap_dsshw_driver, + &omap_dispchw_driver, #ifdef CONFIG_OMAP2_DSS_DSI - dsi_init_platform_driver, + &omap_dsihw_driver, #endif #ifdef CONFIG_OMAP2_DSS_VENC - venc_init_platform_driver, + &omap_venchw_driver, #endif #ifdef CONFIG_OMAP4_DSS_HDMI - hdmi4_init_platform_driver, + &omapdss_hdmi4hw_driver, #endif #ifdef CONFIG_OMAP5_DSS_HDMI - hdmi5_init_platform_driver, -#endif -}; - -static void (*dss_output_drv_unreg_funcs[])(void) = { -#ifdef CONFIG_OMAP5_DSS_HDMI - hdmi5_uninit_platform_driver, -#endif -#ifdef CONFIG_OMAP4_DSS_HDMI - hdmi4_uninit_platform_driver, + &omapdss_hdmi5hw_driver, #endif -#ifdef CONFIG_OMAP2_DSS_VENC - venc_uninit_platform_driver, -#endif -#ifdef CONFIG_OMAP2_DSS_DSI - dsi_uninit_platform_driver, -#endif - dispc_uninit_platform_driver, - dss_uninit_platform_driver, }; static struct platform_device *omap_drm_device; @@ -69,13 +50,11 @@ static struct platform_device *omap_drm_device; static int __init omap_dss_init(void) { int r; - int i; - for (i = 0; i < ARRAY_SIZE(dss_output_drv_reg_funcs); ++i) { - r = dss_output_drv_reg_funcs[i](); - if (r) - goto err_reg; - } + r = platform_register_drivers(omap_dss_drivers, + ARRAY_SIZE(omap_dss_drivers)); + if (r) + goto err_reg; omap_drm_device = platform_device_register_simple("omapdrm", 0, NULL, 0); if (IS_ERR(omap_drm_device)) { @@ -86,22 +65,18 @@ static int __init omap_dss_init(void) return 0; err_reg: - for (i = ARRAY_SIZE(dss_output_drv_reg_funcs) - i; - i < ARRAY_SIZE(dss_output_drv_reg_funcs); - ++i) - dss_output_drv_unreg_funcs[i](); + platform_unregister_drivers(omap_dss_drivers, + ARRAY_SIZE(omap_dss_drivers)); return r; } static void __exit omap_dss_exit(void) { - int i; - platform_device_unregister(omap_drm_device); - for (i = 0; i < ARRAY_SIZE(dss_output_drv_unreg_funcs); ++i) - dss_output_drv_unreg_funcs[i](); + platform_unregister_drivers(omap_dss_drivers, + ARRAY_SIZE(omap_dss_drivers)); } module_init(omap_dss_init); diff --git a/drivers/gpu/drm/omapdrm/dss/dispc.c b/drivers/gpu/drm/omapdrm/dss/dispc.c index 0f4fdb221498..4e8f68efd169 100644 --- a/drivers/gpu/drm/omapdrm/dss/dispc.c +++ b/drivers/gpu/drm/omapdrm/dss/dispc.c @@ -1,6 +1,4 @@ /* - * linux/drivers/video/omap2/dss/dispc.c - * * Copyright (C) 2009 Nokia Corporation * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> * @@ -4325,6 +4323,17 @@ static void dispc_free_irq(void *dev_id) dispc.user_data = NULL; } +static u32 dispc_get_memory_bandwidth_limit(void) +{ + u32 limit = 0; + + /* Optional maximum memory bandwidth */ + of_property_read_u32(dispc.pdev->dev.of_node, "max-memory-bandwidth", + &limit); + + return limit; +} + /* * Workaround for errata i734 in DSS dispc * - LCD1 Gamma Correction Is Not Working When GFX Pipe Is Disabled @@ -4497,6 +4506,8 @@ static const struct dispc_ops dispc_ops = { .get_num_ovls = dispc_get_num_ovls, .get_num_mgrs = dispc_get_num_mgrs, + .get_memory_bandwidth_limit = dispc_get_memory_bandwidth_limit, + .mgr_enable = dispc_mgr_enable, .mgr_is_enabled = dispc_mgr_is_enabled, .mgr_get_vsync_irq = dispc_mgr_get_vsync_irq, @@ -4685,7 +4696,7 @@ static const struct dev_pm_ops dispc_pm_ops = { .runtime_resume = dispc_runtime_resume, }; -static struct platform_driver omap_dispchw_driver = { +struct platform_driver omap_dispchw_driver = { .probe = dispc_probe, .remove = dispc_remove, .driver = { @@ -4695,13 +4706,3 @@ static struct platform_driver omap_dispchw_driver = { .suppress_bind_attrs = true, }, }; - -int __init dispc_init_platform_driver(void) -{ - return platform_driver_register(&omap_dispchw_driver); -} - -void dispc_uninit_platform_driver(void) -{ - platform_driver_unregister(&omap_dispchw_driver); -} diff --git a/drivers/gpu/drm/omapdrm/dss/dispc.h b/drivers/gpu/drm/omapdrm/dss/dispc.h index 003adce532f4..e901dd1e4365 100644 --- a/drivers/gpu/drm/omapdrm/dss/dispc.h +++ b/drivers/gpu/drm/omapdrm/dss/dispc.h @@ -1,10 +1,7 @@ /* - * linux/drivers/video/omap2/dss/dispc.h - * - * Copyright (C) 2011 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ * Author: Archit Taneja <archit@ti.com> * - * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. diff --git a/drivers/gpu/drm/omapdrm/dss/dispc_coefs.c b/drivers/gpu/drm/omapdrm/dss/dispc_coefs.c index 34fad2376f8d..44804c8c8777 100644 --- a/drivers/gpu/drm/omapdrm/dss/dispc_coefs.c +++ b/drivers/gpu/drm/omapdrm/dss/dispc_coefs.c @@ -1,7 +1,5 @@ /* - * linux/drivers/video/omap2/dss/dispc_coefs.c - * - * Copyright (C) 2011 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ * Author: Chandrabhanu Mahapatra <cmahapatra@ti.com> * * This program is free software; you can redistribute it and/or modify it diff --git a/drivers/gpu/drm/omapdrm/dss/display.c b/drivers/gpu/drm/omapdrm/dss/display.c index 42279933790e..0c9480ba85c0 100644 --- a/drivers/gpu/drm/omapdrm/dss/display.c +++ b/drivers/gpu/drm/omapdrm/dss/display.c @@ -1,6 +1,4 @@ /* - * linux/drivers/video/omap2/dss/display.c - * * Copyright (C) 2009 Nokia Corporation * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> * @@ -175,17 +173,3 @@ out: return dssdev; } EXPORT_SYMBOL(omap_dss_get_next_device); - -struct omap_dss_device *omap_dss_find_device(void *data, - int (*match)(struct omap_dss_device *dssdev, void *data)) -{ - struct omap_dss_device *dssdev = NULL; - - while ((dssdev = omap_dss_get_next_device(dssdev)) != NULL) { - if (match(dssdev, data)) - return dssdev; - } - - return NULL; -} -EXPORT_SYMBOL(omap_dss_find_device); diff --git a/drivers/gpu/drm/omapdrm/dss/dpi.c b/drivers/gpu/drm/omapdrm/dss/dpi.c index ca1e3b489540..ea44137ed08c 100644 --- a/drivers/gpu/drm/omapdrm/dss/dpi.c +++ b/drivers/gpu/drm/omapdrm/dss/dpi.c @@ -1,6 +1,4 @@ /* - * linux/drivers/video/omap2/dss/dpi.c - * * Copyright (C) 2009 Nokia Corporation * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> * @@ -52,8 +50,6 @@ struct dpi_data { int data_lines; struct omap_dss_device output; - - bool port_initialized; }; static struct dpi_data *dpi_get_data_from_dssdev(struct omap_dss_device *dssdev) @@ -786,8 +782,6 @@ int dpi_init_port(struct platform_device *pdev, struct device_node *port, dpi_init_output_port(dpi, port); - dpi->port_initialized = true; - return 0; err_datalines: @@ -800,7 +794,7 @@ void dpi_uninit_port(struct device_node *port) { struct dpi_data *dpi = port->data; - if (!dpi->port_initialized) + if (!dpi) return; dpi_uninit_output_port(port); diff --git a/drivers/gpu/drm/omapdrm/dss/dsi.c b/drivers/gpu/drm/omapdrm/dss/dsi.c index c2cf6d98e577..80f1f3679a3c 100644 --- a/drivers/gpu/drm/omapdrm/dss/dsi.c +++ b/drivers/gpu/drm/omapdrm/dss/dsi.c @@ -1,6 +1,4 @@ /* - * linux/drivers/video/omap2/dss/dsi.c - * * Copyright (C) 2009 Nokia Corporation * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> * @@ -5660,7 +5658,7 @@ static const struct dev_pm_ops dsi_pm_ops = { .runtime_resume = dsi_runtime_resume, }; -static struct platform_driver omap_dsihw_driver = { +struct platform_driver omap_dsihw_driver = { .probe = dsi_probe, .remove = dsi_remove, .driver = { @@ -5670,13 +5668,3 @@ static struct platform_driver omap_dsihw_driver = { .suppress_bind_attrs = true, }, }; - -int __init dsi_init_platform_driver(void) -{ - return platform_driver_register(&omap_dsihw_driver); -} - -void dsi_uninit_platform_driver(void) -{ - platform_driver_unregister(&omap_dsihw_driver); -} diff --git a/drivers/gpu/drm/omapdrm/dss/dss-of.c b/drivers/gpu/drm/omapdrm/dss/dss-of.c index c6b86f348a5c..967d9e1b34e5 100644 --- a/drivers/gpu/drm/omapdrm/dss/dss-of.c +++ b/drivers/gpu/drm/omapdrm/dss/dss-of.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Texas Instruments + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * This program is free software; you can redistribute it and/or modify it diff --git a/drivers/gpu/drm/omapdrm/dss/dss.c b/drivers/gpu/drm/omapdrm/dss/dss.c index d1755f12236b..04300b2da1b1 100644 --- a/drivers/gpu/drm/omapdrm/dss/dss.c +++ b/drivers/gpu/drm/omapdrm/dss/dss.c @@ -1,6 +1,4 @@ /* - * linux/drivers/video/omap2/dss/dss.c - * * Copyright (C) 2009 Nokia Corporation * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> * @@ -23,6 +21,7 @@ #define DSS_SUBSYS_NAME "DSS" #include <linux/debugfs.h> +#include <linux/dma-mapping.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/io.h> @@ -367,7 +366,8 @@ const char *dss_get_clk_source_name(enum dss_clk_source clk_src) return dss_generic_clk_source_names[clk_src]; } -void dss_dump_clocks(struct seq_file *s) +#if defined(CONFIG_OMAP2_DSS_DEBUGFS) +static void dss_dump_clocks(struct seq_file *s) { const char *fclk_name; unsigned long fclk_rate; @@ -386,6 +386,7 @@ void dss_dump_clocks(struct seq_file *s) dss_runtime_put(); } +#endif static void dss_dump_regs(struct seq_file *s) { @@ -1441,6 +1442,12 @@ static int dss_probe(struct platform_device *pdev) dss.pdev = pdev; + r = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (r) { + dev_err(&pdev->dev, "Failed to set the DMA mask\n"); + return r; + } + /* * The various OMAP3-based SoCs can't be told apart using the compatible * string, use SoC device matching. @@ -1527,7 +1534,7 @@ static const struct dev_pm_ops dss_pm_ops = { .runtime_resume = dss_runtime_resume, }; -static struct platform_driver omap_dsshw_driver = { +struct platform_driver omap_dsshw_driver = { .probe = dss_probe, .remove = dss_remove, .shutdown = dss_shutdown, @@ -1538,13 +1545,3 @@ static struct platform_driver omap_dsshw_driver = { .suppress_bind_attrs = true, }, }; - -int __init dss_init_platform_driver(void) -{ - return platform_driver_register(&omap_dsshw_driver); -} - -void dss_uninit_platform_driver(void) -{ - platform_driver_unregister(&omap_dsshw_driver); -} diff --git a/drivers/gpu/drm/omapdrm/dss/dss.h b/drivers/gpu/drm/omapdrm/dss/dss.h index ed465572491e..6374e57ed9da 100644 --- a/drivers/gpu/drm/omapdrm/dss/dss.h +++ b/drivers/gpu/drm/omapdrm/dss/dss.h @@ -1,6 +1,4 @@ /* - * linux/drivers/video/omap2/dss/dss.h - * * Copyright (C) 2009 Nokia Corporation * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> * @@ -264,9 +262,6 @@ static inline int dss_debugfs_create_file(const char *name, } #endif /* CONFIG_OMAP2_DSS_DEBUGFS */ -int dss_init_platform_driver(void) __init; -void dss_uninit_platform_driver(void); - int dss_runtime_get(void); void dss_runtime_put(void); @@ -277,7 +272,6 @@ int dss_dpi_select_source(int port, enum omap_channel channel); void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select); enum dss_hdmi_venc_clk_source_select dss_get_hdmi_venc_clk_source(void); const char *dss_get_clk_source_name(enum dss_clk_source clk_src); -void dss_dump_clocks(struct seq_file *s); /* DSS VIDEO PLL */ struct dss_pll *dss_video_pll_init(struct platform_device *pdev, int id, @@ -329,9 +323,6 @@ static inline void sdi_uninit_port(struct device_node *port) struct dentry; struct file_operations; -int dsi_init_platform_driver(void) __init; -void dsi_uninit_platform_driver(void); - void dsi_dump_clocks(struct seq_file *s); void dsi_irq_handler(void); @@ -355,8 +346,6 @@ static inline void dpi_uninit_port(struct device_node *port) #endif /* DISPC */ -int dispc_init_platform_driver(void) __init; -void dispc_uninit_platform_driver(void); void dispc_dump_clocks(struct seq_file *s); int dispc_runtime_get(void); @@ -400,18 +389,6 @@ void dispc_wb_set_channel_in(enum dss_writeback_channel channel); int dispc_wb_setup(const struct omap_dss_writeback_info *wi, bool mem_to_mem, const struct videomode *vm); -/* VENC */ -int venc_init_platform_driver(void) __init; -void venc_uninit_platform_driver(void); - -/* HDMI */ -int hdmi4_init_platform_driver(void) __init; -void hdmi4_uninit_platform_driver(void); - -int hdmi5_init_platform_driver(void) __init; -void hdmi5_uninit_platform_driver(void); - - #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS static inline void dss_collect_irq_stats(u32 irqstatus, unsigned *irq_arr) { @@ -455,4 +432,19 @@ int dss_pll_write_config_type_b(struct dss_pll *pll, const struct dss_pll_clock_info *cinfo); int dss_pll_wait_reset_done(struct dss_pll *pll); +extern struct platform_driver omap_dsshw_driver; +extern struct platform_driver omap_dispchw_driver; +#ifdef CONFIG_OMAP2_DSS_DSI +extern struct platform_driver omap_dsihw_driver; +#endif +#ifdef CONFIG_OMAP2_DSS_VENC +extern struct platform_driver omap_venchw_driver; +#endif +#ifdef CONFIG_OMAP4_DSS_HDMI +extern struct platform_driver omapdss_hdmi4hw_driver; +#endif +#ifdef CONFIG_OMAP5_DSS_HDMI +extern struct platform_driver omapdss_hdmi5hw_driver; +#endif + #endif diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c index a598dfdeb585..bf914f2ac99e 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c @@ -1,5 +1,6 @@ /* * HDMI interface DSS driver for TI's OMAP4 family of SoCs. + * * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ * Authors: Yong Zhi * Mythri pk <mythripk@ti.com> @@ -844,7 +845,7 @@ static const struct of_device_id hdmi_of_match[] = { {}, }; -static struct platform_driver omapdss_hdmihw_driver = { +struct platform_driver omapdss_hdmi4hw_driver = { .probe = hdmi4_probe, .remove = hdmi4_remove, .driver = { @@ -854,13 +855,3 @@ static struct platform_driver omapdss_hdmihw_driver = { .suppress_bind_attrs = true, }, }; - -int __init hdmi4_init_platform_driver(void) -{ - return platform_driver_register(&omapdss_hdmihw_driver); -} - -void hdmi4_uninit_platform_driver(void) -{ - platform_driver_unregister(&omapdss_hdmihw_driver); -} diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4_core.c b/drivers/gpu/drm/omapdrm/dss/hdmi4_core.c index b06f9956e733..35ed2add6189 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4_core.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4_core.c @@ -1,7 +1,6 @@ /* - * ti_hdmi_4xxx_ip.c - * * HDMI TI81xx, TI38xx, TI OMAP4 etc IP driver Library + * * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ * Authors: Yong Zhi * Mythri pk <mythripk@ti.com> diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c b/drivers/gpu/drm/omapdrm/dss/hdmi5.c index b3221ca5bcd8..689cda41858b 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c @@ -1,7 +1,7 @@ /* * HDMI driver for OMAP5 * - * Copyright (C) 2014 Texas Instruments Incorporated + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ * * Authors: * Yong Zhi @@ -841,7 +841,7 @@ static const struct of_device_id hdmi_of_match[] = { {}, }; -static struct platform_driver omapdss_hdmihw_driver = { +struct platform_driver omapdss_hdmi5hw_driver = { .probe = hdmi5_probe, .remove = hdmi5_remove, .driver = { @@ -851,13 +851,3 @@ static struct platform_driver omapdss_hdmihw_driver = { .suppress_bind_attrs = true, }, }; - -int __init hdmi5_init_platform_driver(void) -{ - return platform_driver_register(&omapdss_hdmihw_driver); -} - -void hdmi5_uninit_platform_driver(void) -{ - platform_driver_unregister(&omapdss_hdmihw_driver); -} diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5_core.c b/drivers/gpu/drm/omapdrm/dss/hdmi5_core.c index ab179ec133c0..09759f8ea7bc 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5_core.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5_core.c @@ -1,8 +1,7 @@ /* * OMAP5 HDMI CORE IP driver library * - * Copyright (C) 2014 Texas Instruments Incorporated - * + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ * Authors: * Yong Zhi * Mythri pk diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi_phy.c b/drivers/gpu/drm/omapdrm/dss/hdmi_phy.c index a156292b1820..5c14ed851609 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi_phy.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi_phy.c @@ -1,7 +1,7 @@ /* * HDMI PHY * - * Copyright (C) 2013 Texas Instruments Incorporated + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi_pll.c b/drivers/gpu/drm/omapdrm/dss/hdmi_pll.c index 55bee81f4dd5..08885d7de1e8 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi_pll.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi_pll.c @@ -1,7 +1,7 @@ /* * HDMI PLL * - * Copyright (C) 2013 Texas Instruments Incorporated + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi_wp.c b/drivers/gpu/drm/omapdrm/dss/hdmi_wp.c index 88034fbe0e9f..806e5fdcfe52 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi_wp.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi_wp.c @@ -1,7 +1,7 @@ /* * HDMI wrapper * - * Copyright (C) 2013 Texas Instruments Incorporated + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss-boot-init.c b/drivers/gpu/drm/omapdrm/dss/omapdss-boot-init.c index bf626acae271..3bfb95d230e0 100644 --- a/drivers/gpu/drm/omapdrm/dss/omapdss-boot-init.c +++ b/drivers/gpu/drm/omapdrm/dss/omapdss-boot-init.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Texas Instruments + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * This program is free software; you can redistribute it and/or modify it diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h b/drivers/gpu/drm/omapdrm/dss/omapdss.h index 990422b35784..f8f83e826a56 100644 --- a/drivers/gpu/drm/omapdrm/dss/omapdss.h +++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Texas Instruments + * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * This program is free software; you can redistribute it and/or modify it @@ -563,6 +563,8 @@ struct omap_dss_driver { struct videomode *vm); void (*get_timings)(struct omap_dss_device *dssdev, struct videomode *vm); + void (*get_size)(struct omap_dss_device *dssdev, + unsigned int *width, unsigned int *height); int (*set_wss)(struct omap_dss_device *dssdev, u32 wss); u32 (*get_wss)(struct omap_dss_device *dssdev); @@ -585,9 +587,6 @@ struct omap_dss_driver { bool omapdss_is_initialized(void); -int omap_dss_register_driver(struct omap_dss_driver *); -void omap_dss_unregister_driver(struct omap_dss_driver *); - int omapdss_register_display(struct omap_dss_device *dssdev); void omapdss_unregister_display(struct omap_dss_device *dssdev); @@ -595,9 +594,6 @@ struct omap_dss_device *omap_dss_get_device(struct omap_dss_device *dssdev); void omap_dss_put_device(struct omap_dss_device *dssdev); #define for_each_dss_dev(d) while ((d = omap_dss_get_next_device(d)) != NULL) struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from); -struct omap_dss_device *omap_dss_find_device(void *data, - int (*match)(struct omap_dss_device *dssdev, void *data)); - int omap_dss_get_num_overlay_managers(void); @@ -695,6 +691,8 @@ struct dispc_ops { int (*get_num_ovls)(void); int (*get_num_mgrs)(void); + u32 (*get_memory_bandwidth_limit)(void); + void (*mgr_enable)(enum omap_channel channel, bool enable); bool (*mgr_is_enabled)(enum omap_channel channel); u32 (*mgr_get_vsync_irq)(enum omap_channel channel); diff --git a/drivers/gpu/drm/omapdrm/dss/output.c b/drivers/gpu/drm/omapdrm/dss/output.c index 3c572b699ed3..b9afd80ae385 100644 --- a/drivers/gpu/drm/omapdrm/dss/output.c +++ b/drivers/gpu/drm/omapdrm/dss/output.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Texas Instruments Ltd + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ * Author: Archit Taneja <archit@ti.com> * * This program is free software; you can redistribute it and/or modify it diff --git a/drivers/gpu/drm/omapdrm/dss/pll.c b/drivers/gpu/drm/omapdrm/dss/pll.c index 9d9d9d42009b..058714b1eb56 100644 --- a/drivers/gpu/drm/omapdrm/dss/pll.c +++ b/drivers/gpu/drm/omapdrm/dss/pll.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Texas Instruments Incorporated + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by diff --git a/drivers/gpu/drm/omapdrm/dss/sdi.c b/drivers/gpu/drm/omapdrm/dss/sdi.c index d18ad58c5a19..d8ab31f3a813 100644 --- a/drivers/gpu/drm/omapdrm/dss/sdi.c +++ b/drivers/gpu/drm/omapdrm/dss/sdi.c @@ -1,6 +1,4 @@ /* - * linux/drivers/video/omap2/dss/sdi.c - * * Copyright (C) 2009 Nokia Corporation * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> * diff --git a/drivers/gpu/drm/omapdrm/dss/venc.c b/drivers/gpu/drm/omapdrm/dss/venc.c index d58da6f32693..6de9d734ddb9 100644 --- a/drivers/gpu/drm/omapdrm/dss/venc.c +++ b/drivers/gpu/drm/omapdrm/dss/venc.c @@ -1,6 +1,4 @@ /* - * linux/drivers/video/omap2/dss/venc.c - * * Copyright (C) 2009 Nokia Corporation * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> * @@ -857,10 +855,10 @@ static int venc_probe_of(struct platform_device *pdev) of_node_put(ep); return 0; + err: of_node_put(ep); - - return 0; + return r; } /* VENC HW IP initialisation */ @@ -986,7 +984,7 @@ static const struct of_device_id venc_of_match[] = { {}, }; -static struct platform_driver omap_venchw_driver = { +struct platform_driver omap_venchw_driver = { .probe = venc_probe, .remove = venc_remove, .driver = { @@ -996,13 +994,3 @@ static struct platform_driver omap_venchw_driver = { .suppress_bind_attrs = true, }, }; - -int __init venc_init_platform_driver(void) -{ - return platform_driver_register(&omap_venchw_driver); -} - -void venc_uninit_platform_driver(void) -{ - platform_driver_unregister(&omap_venchw_driver); -} diff --git a/drivers/gpu/drm/omapdrm/dss/video-pll.c b/drivers/gpu/drm/omapdrm/dss/video-pll.c index 38a239cc5e04..bbedac797927 100644 --- a/drivers/gpu/drm/omapdrm/dss/video-pll.c +++ b/drivers/gpu/drm/omapdrm/dss/video-pll.c @@ -1,13 +1,15 @@ /* -* Copyright (C) 2014 Texas Instruments Ltd -* -* 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. -* -* You should have received a copy of the GNU General Public License along with -* this program. If not, see <http://www.gnu.org/licenses/>. -*/ + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ #include <linux/clk.h> #include <linux/delay.h> diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c index aa5ba9ae2191..a0d7b1d905e8 100644 --- a/drivers/gpu/drm/omapdrm/omap_connector.c +++ b/drivers/gpu/drm/omapdrm/omap_connector.c @@ -1,7 +1,5 @@ /* - * drivers/gpu/drm/omapdrm/omap_connector.c - * - * Copyright (C) 2011 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ * Author: Rob Clark <rob@ti.com> * * This program is free software; you can redistribute it and/or modify it @@ -149,6 +147,12 @@ static int omap_connector_get_modes(struct drm_connector *connector) drm_mode_set_name(mode); drm_mode_probed_add(connector, mode); + if (dssdrv->get_size) { + dssdrv->get_size(dssdev, + &connector->display_info.width_mm, + &connector->display_info.height_mm); + } + n = 1; } diff --git a/drivers/gpu/drm/omapdrm/omap_connector.h b/drivers/gpu/drm/omapdrm/omap_connector.h new file mode 100644 index 000000000000..98bbc779b302 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_connector.h @@ -0,0 +1,37 @@ +/* + * omap_connector.h -- OMAP DRM Connector + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark <rob@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __OMAPDRM_CONNECTOR_H__ +#define __OMAPDRM_CONNECTOR_H__ + +#include <linux/types.h> + +struct drm_connector; +struct drm_device; +struct drm_encoder; +struct omap_dss_device; + +struct drm_connector *omap_connector_init(struct drm_device *dev, + int connector_type, struct omap_dss_device *dssdev, + struct drm_encoder *encoder); +struct drm_encoder *omap_connector_attached_encoder( + struct drm_connector *connector); +bool omap_connector_get_hdmi_mode(struct drm_connector *connector); + +#endif /* __OMAPDRM_CONNECTOR_H__ */ diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index cc85c16cbc2a..1b8154e58d18 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -1,7 +1,5 @@ /* - * drivers/gpu/drm/omapdrm/omap_crtc.c - * - * Copyright (C) 2011 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ * Author: Rob Clark <rob@ti.com> * * This program is free software; you can redistribute it and/or modify it @@ -23,6 +21,7 @@ #include <drm/drm_crtc_helper.h> #include <drm/drm_mode.h> #include <drm/drm_plane_helper.h> +#include <linux/math64.h> #include "omap_drv.h" @@ -400,6 +399,41 @@ static void omap_crtc_atomic_disable(struct drm_crtc *crtc, drm_crtc_vblank_off(crtc); } +static enum drm_mode_status omap_crtc_mode_valid(struct drm_crtc *crtc, + const struct drm_display_mode *mode) +{ + struct omap_drm_private *priv = crtc->dev->dev_private; + + /* Check for bandwidth limit */ + if (priv->max_bandwidth) { + /* + * Estimation for the bandwidth need of a given mode with one + * full screen plane: + * bandwidth = resolution * 32bpp * (pclk / (vtotal * htotal)) + * ^^ Refresh rate ^^ + * + * The interlaced mode is taken into account by using the + * pixelclock in the calculation. + * + * The equation is rearranged for 64bit arithmetic. + */ + uint64_t bandwidth = mode->clock * 1000; + unsigned int bpp = 4; + + bandwidth = bandwidth * mode->hdisplay * mode->vdisplay * bpp; + bandwidth = div_u64(bandwidth, mode->htotal * mode->vtotal); + + /* + * Reject modes which would need more bandwidth if used with one + * full resolution plane (most common use case). + */ + if (priv->max_bandwidth < bandwidth) + return MODE_BAD; + } + + return MODE_OK; +} + static void omap_crtc_mode_set_nofb(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); @@ -621,6 +655,7 @@ static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = { .atomic_flush = omap_crtc_atomic_flush, .atomic_enable = omap_crtc_atomic_enable, .atomic_disable = omap_crtc_atomic_disable, + .mode_valid = omap_crtc_mode_valid, }; /* ----------------------------------------------------------------------------- diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.h b/drivers/gpu/drm/omapdrm/omap_crtc.h new file mode 100644 index 000000000000..ad7b007c6174 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_crtc.h @@ -0,0 +1,43 @@ +/* + * omap_crtc.h -- OMAP DRM CRTC + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark <rob@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __OMAPDRM_CRTC_H__ +#define __OMAPDRM_CRTC_H__ + +#include <linux/types.h> + +enum omap_channel; + +struct drm_crtc; +struct drm_device; +struct drm_plane; +struct omap_dss_device; +struct videomode; + +struct videomode *omap_crtc_timings(struct drm_crtc *crtc); +enum omap_channel omap_crtc_channel(struct drm_crtc *crtc); +void omap_crtc_pre_init(void); +void omap_crtc_pre_uninit(void); +struct drm_crtc *omap_crtc_init(struct drm_device *dev, + struct drm_plane *plane, struct omap_dss_device *dssdev); +int omap_crtc_wait_pending(struct drm_crtc *crtc); +void omap_crtc_error_irq(struct drm_crtc *crtc, uint32_t irqstatus); +void omap_crtc_vblank_irq(struct drm_crtc *crtc); + +#endif /* __OMAPDRM_CRTC_H__ */ diff --git a/drivers/gpu/drm/omapdrm/omap_debugfs.c b/drivers/gpu/drm/omapdrm/omap_debugfs.c index 19b716745623..b42e286616b0 100644 --- a/drivers/gpu/drm/omapdrm/omap_debugfs.c +++ b/drivers/gpu/drm/omapdrm/omap_debugfs.c @@ -1,7 +1,5 @@ /* - * drivers/gpu/drm/omapdrm/omap_debugfs.c - * - * Copyright (C) 2011 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ * Author: Rob Clark <rob.clark@linaro.org> * * This program is free software; you can redistribute it and/or modify it diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_priv.h b/drivers/gpu/drm/omapdrm/omap_dmm_priv.h index 9f32a83ca507..600064d5c25b 100644 --- a/drivers/gpu/drm/omapdrm/omap_dmm_priv.h +++ b/drivers/gpu/drm/omapdrm/omap_dmm_priv.h @@ -1,5 +1,4 @@ /* - * * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ * Author: Rob Clark <rob@ti.com> * Andy Gross <andy.gross@ti.com> @@ -13,6 +12,7 @@ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ + #ifndef OMAP_DMM_PRIV_H #define OMAP_DMM_PRIV_H @@ -59,12 +59,12 @@ #define DMM_IRQSTAT_ERR_UPD_DATA (1<<6) #define DMM_IRQSTAT_ERR_LUT_MISS (1<<7) -#define DMM_IRQSTAT_ERR_MASK (DMM_IRQ_STAT_ERR_INV_DSC | \ - DMM_IRQ_STAT_ERR_INV_DATA | \ - DMM_IRQ_STAT_ERR_UPD_AREA | \ - DMM_IRQ_STAT_ERR_UPD_CTRL | \ - DMM_IRQ_STAT_ERR_UPD_DATA | \ - DMM_IRQ_STAT_ERR_LUT_MISS) +#define DMM_IRQSTAT_ERR_MASK (DMM_IRQSTAT_ERR_INV_DSC | \ + DMM_IRQSTAT_ERR_INV_DATA | \ + DMM_IRQSTAT_ERR_UPD_AREA | \ + DMM_IRQSTAT_ERR_UPD_CTRL | \ + DMM_IRQSTAT_ERR_UPD_DATA | \ + DMM_IRQSTAT_ERR_LUT_MISS) #define DMM_PATSTATUS_READY (1<<0) #define DMM_PATSTATUS_VALID (1<<1) diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c index c60a85e82c6d..4be0c94673f5 100644 --- a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c +++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c @@ -1,11 +1,10 @@ /* * DMM IOMMU driver support functions for TI OMAP processors. * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ * Author: Rob Clark <rob@ti.com> * Andy Gross <andy.gross@ti.com> * - * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation version 2. @@ -121,14 +120,22 @@ static int wait_status(struct refill_engine *engine, uint32_t wait_mask) while (true) { r = dmm_read(dmm, reg[PAT_STATUS][engine->id]); err = r & DMM_PATSTATUS_ERR; - if (err) + if (err) { + dev_err(dmm->dev, + "%s: error (engine%d). PAT_STATUS: 0x%08x\n", + __func__, engine->id, r); return -EFAULT; + } if ((r & wait_mask) == wait_mask) break; - if (--i == 0) + if (--i == 0) { + dev_err(dmm->dev, + "%s: timeout (engine%d). PAT_STATUS: 0x%08x\n", + __func__, engine->id, r); return -ETIMEDOUT; + } udelay(1); } @@ -158,6 +165,11 @@ static irqreturn_t omap_dmm_irq_handler(int irq, void *arg) dmm_write(dmm, status, DMM_PAT_IRQSTATUS); for (i = 0; i < dmm->num_engines; i++) { + if (status & DMM_IRQSTAT_ERR_MASK) + dev_err(dmm->dev, + "irq error(engine%d): IRQSTAT 0x%02x\n", + i, status & 0xff); + if (status & DMM_IRQSTAT_LST) { if (dmm->engines[i].async) release_engine(&dmm->engines[i]); @@ -298,7 +310,12 @@ static int dmm_txn_commit(struct dmm_txn *txn, bool wait) msecs_to_jiffies(100))) { dev_err(dmm->dev, "timed out waiting for done\n"); ret = -ETIMEDOUT; + goto cleanup; } + + /* Check the engine status before continue */ + ret = wait_status(engine, DMM_PATSTATUS_READY | + DMM_PATSTATUS_VALID | DMM_PATSTATUS_DONE); } cleanup: diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.h b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.h index e83c78372db8..cc78ba4fe6ab 100644 --- a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.h +++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.h @@ -1,5 +1,4 @@ /* - * * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ * Author: Rob Clark <rob@ti.com> * Andy Gross <andy.gross@ti.com> diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index cdf5b0601eba..dd68b2556f5b 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -1,7 +1,5 @@ /* - * drivers/gpu/drm/omapdrm/omap_drv.c - * - * Copyright (C) 2011 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ * Author: Rob Clark <rob@ti.com> * * This program is free software; you can redistribute it and/or modify it @@ -46,14 +44,6 @@ * devices */ -static void omap_fb_output_poll_changed(struct drm_device *dev) -{ - struct omap_drm_private *priv = dev->dev_private; - DBG("dev=%p", dev); - if (priv->fbdev) - drm_fb_helper_hotplug_event(priv->fbdev); -} - static void omap_atomic_wait_for_completion(struct drm_device *dev, struct drm_atomic_state *old_state) { @@ -132,7 +122,7 @@ static const struct drm_mode_config_helper_funcs omap_mode_config_helper_funcs = static const struct drm_mode_config_funcs omap_mode_config_funcs = { .fb_create = omap_framebuffer_create, - .output_poll_changed = omap_fb_output_poll_changed, + .output_poll_changed = drm_fb_helper_output_poll_changed, .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, }; @@ -467,28 +457,6 @@ static int dev_open(struct drm_device *dev, struct drm_file *file) return 0; } -/** - * lastclose - clean up after all DRM clients have exited - * @dev: DRM device - * - * Take care of cleaning up after all DRM clients have exited. In the - * mode setting case, we want to restore the kernel's initial mode (just - * in case the last client left us in a bad state). - */ -static void dev_lastclose(struct drm_device *dev) -{ - struct omap_drm_private *priv = dev->dev_private; - int ret; - - DBG("lastclose: dev=%p", dev); - - if (priv->fbdev) { - ret = drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev); - if (ret) - DBG("failed to restore crtc mode"); - } -} - static const struct vm_operations_struct omap_gem_vm_ops = { .fault = omap_gem_fault, .open = drm_gem_vm_open, @@ -511,7 +479,7 @@ static struct drm_driver omap_drm_driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC | DRIVER_RENDER, .open = dev_open, - .lastclose = dev_lastclose, + .lastclose = drm_fb_helper_lastclose, #ifdef CONFIG_DEBUG_FS .debugfs_init = omap_debugfs_init, #endif @@ -593,6 +561,11 @@ static int pdev_probe(struct platform_device *pdev) ddev->dev_private = priv; platform_set_drvdata(pdev, ddev); + /* Get memory bandwidth limits */ + if (priv->dispc_ops->get_memory_bandwidth_limit) + priv->max_bandwidth = + priv->dispc_ops->get_memory_bandwidth_limit(); + omap_gem_init(ddev); ret = omap_modeset_init(ddev); @@ -740,7 +713,7 @@ static int omap_drm_resume(struct device *dev) drm_kms_helper_poll_enable(drm_dev); - return omap_gem_resume(dev); + return omap_gem_resume(drm_dev); } #endif diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h index 4bd1e9070b31..0ac97fe09f9b 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -1,7 +1,5 @@ /* - * drivers/gpu/drm/omapdrm/omap_drv.h - * - * Copyright (C) 2011 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ * Author: Rob Clark <rob@ti.com> * * This program is free software; you can redistribute it and/or modify it @@ -17,8 +15,8 @@ * this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef __OMAP_DRV_H__ -#define __OMAP_DRV_H__ +#ifndef __OMAPDRM_DRV_H__ +#define __OMAPDRM_DRV_H__ #include <linux/module.h> #include <linux/types.h> @@ -31,6 +29,15 @@ #include "dss/omapdss.h" +#include "omap_connector.h" +#include "omap_crtc.h" +#include "omap_encoder.h" +#include "omap_fb.h" +#include "omap_fbdev.h" +#include "omap_gem.h" +#include "omap_irq.h" +#include "omap_plane.h" + #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) #define VERB(fmt, ...) if (0) DRM_DEBUG(fmt, ##__VA_ARGS__) /* verbose debug */ @@ -38,14 +45,6 @@ struct omap_drm_usergart; -/* For KMS code that needs to wait for a certain # of IRQs: - */ -struct omap_irq_wait; -struct omap_irq_wait * omap_irq_wait_init(struct drm_device *dev, - uint32_t irqmask, int count); -int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait, - unsigned long timeout); - struct omap_drm_private { uint32_t omaprev; @@ -83,117 +82,12 @@ struct omap_drm_private { spinlock_t wait_lock; /* protects the wait_list */ struct list_head wait_list; /* list of omap_irq_wait */ uint32_t irq_mask; /* enabled irqs in addition to wait_list */ + + /* memory bandwidth limit if it is needed on the platform */ + unsigned int max_bandwidth; }; -#ifdef CONFIG_DEBUG_FS int omap_debugfs_init(struct drm_minor *minor); -void omap_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m); -void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m); -void omap_gem_describe_objects(struct list_head *list, struct seq_file *m); -#endif - -#ifdef CONFIG_PM -int omap_gem_resume(struct device *dev); -#endif - -int omap_irq_enable_vblank(struct drm_crtc *crtc); -void omap_irq_disable_vblank(struct drm_crtc *crtc); -void omap_drm_irq_uninstall(struct drm_device *dev); -int omap_drm_irq_install(struct drm_device *dev); - -#ifdef CONFIG_DRM_FBDEV_EMULATION -struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev); -void omap_fbdev_free(struct drm_device *dev); -#else -static inline struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev) -{ - return NULL; -} -static inline void omap_fbdev_free(struct drm_device *dev) -{ -} -#endif - -struct videomode *omap_crtc_timings(struct drm_crtc *crtc); -enum omap_channel omap_crtc_channel(struct drm_crtc *crtc); -void omap_crtc_pre_init(void); -void omap_crtc_pre_uninit(void); -struct drm_crtc *omap_crtc_init(struct drm_device *dev, - struct drm_plane *plane, struct omap_dss_device *dssdev); -int omap_crtc_wait_pending(struct drm_crtc *crtc); -void omap_crtc_error_irq(struct drm_crtc *crtc, uint32_t irqstatus); -void omap_crtc_vblank_irq(struct drm_crtc *crtc); - -struct drm_plane *omap_plane_init(struct drm_device *dev, - int idx, enum drm_plane_type type, - u32 possible_crtcs); -void omap_plane_install_properties(struct drm_plane *plane, - struct drm_mode_object *obj); - -struct drm_encoder *omap_encoder_init(struct drm_device *dev, - struct omap_dss_device *dssdev); - -struct drm_connector *omap_connector_init(struct drm_device *dev, - int connector_type, struct omap_dss_device *dssdev, - struct drm_encoder *encoder); -struct drm_encoder *omap_connector_attached_encoder( - struct drm_connector *connector); -bool omap_connector_get_hdmi_mode(struct drm_connector *connector); - -struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, - struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd); -struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, - const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos); -int omap_framebuffer_pin(struct drm_framebuffer *fb); -void omap_framebuffer_unpin(struct drm_framebuffer *fb); -void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, - struct drm_plane_state *state, struct omap_overlay_info *info); -struct drm_connector *omap_framebuffer_get_next_connector( - struct drm_framebuffer *fb, struct drm_connector *from); -bool omap_framebuffer_supports_rotation(struct drm_framebuffer *fb); - -void omap_gem_init(struct drm_device *dev); -void omap_gem_deinit(struct drm_device *dev); - -struct drm_gem_object *omap_gem_new(struct drm_device *dev, - union omap_gem_size gsize, uint32_t flags); -struct drm_gem_object *omap_gem_new_dmabuf(struct drm_device *dev, size_t size, - struct sg_table *sgt); -int omap_gem_new_handle(struct drm_device *dev, struct drm_file *file, - union omap_gem_size gsize, uint32_t flags, uint32_t *handle); -void omap_gem_free_object(struct drm_gem_object *obj); -void *omap_gem_vaddr(struct drm_gem_object *obj); -int omap_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev, - uint32_t handle, uint64_t *offset); -int omap_gem_dumb_create(struct drm_file *file, struct drm_device *dev, - struct drm_mode_create_dumb *args); -int omap_gem_mmap(struct file *filp, struct vm_area_struct *vma); -int omap_gem_mmap_obj(struct drm_gem_object *obj, - struct vm_area_struct *vma); -int omap_gem_fault(struct vm_fault *vmf); -int omap_gem_roll(struct drm_gem_object *obj, uint32_t roll); -void omap_gem_cpu_sync_page(struct drm_gem_object *obj, int pgoff); -void omap_gem_dma_sync_buffer(struct drm_gem_object *obj, - enum dma_data_direction dir); -int omap_gem_pin(struct drm_gem_object *obj, dma_addr_t *dma_addr); -void omap_gem_unpin(struct drm_gem_object *obj); -int omap_gem_get_pages(struct drm_gem_object *obj, struct page ***pages, - bool remap); -int omap_gem_put_pages(struct drm_gem_object *obj); -uint32_t omap_gem_flags(struct drm_gem_object *obj); -int omap_gem_rotated_dma_addr(struct drm_gem_object *obj, uint32_t orient, - int x, int y, dma_addr_t *dma_addr); -uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj); -size_t omap_gem_mmap_size(struct drm_gem_object *obj); -int omap_gem_tiled_stride(struct drm_gem_object *obj, uint32_t orient); - -struct dma_buf *omap_gem_prime_export(struct drm_device *dev, - struct drm_gem_object *obj, int flags); -struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev, - struct dma_buf *buffer); - -/* map crtc to vblank mask */ -struct omap_dss_device *omap_encoder_get_dssdev(struct drm_encoder *encoder); - -#endif /* __OMAP_DRV_H__ */ + +#endif /* __OMAPDRM_DRV_H__ */ diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c index 624f5b50b755..fcdf4b0a8eec 100644 --- a/drivers/gpu/drm/omapdrm/omap_encoder.c +++ b/drivers/gpu/drm/omapdrm/omap_encoder.c @@ -1,7 +1,5 @@ /* - * drivers/gpu/drm/omapdrm/omap_encoder.c - * - * Copyright (C) 2011 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ * Author: Rob Clark <rob@ti.com> * * This program is free software; you can redistribute it and/or modify it diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.h b/drivers/gpu/drm/omapdrm/omap_encoder.h new file mode 100644 index 000000000000..d2f308bec494 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_encoder.h @@ -0,0 +1,33 @@ +/* + * omap_encoder.h -- OMAP DRM Encoder + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark <rob@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __OMAPDRM_ENCODER_H__ +#define __OMAPDRM_ENCODER_H__ + +struct drm_device; +struct drm_encoder; +struct omap_dss_device; + +struct drm_encoder *omap_encoder_init(struct drm_device *dev, + struct omap_dss_device *dssdev); + +/* map crtc to vblank mask */ +struct omap_dss_device *omap_encoder_get_dssdev(struct drm_encoder *encoder); + +#endif /* __OMAPDRM_ENCODER_H__ */ diff --git a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c index b1a762b70cbf..b2539a90e1a4 100644 --- a/drivers/gpu/drm/omapdrm/omap_fb.c +++ b/drivers/gpu/drm/omapdrm/omap_fb.c @@ -1,7 +1,5 @@ /* - * drivers/gpu/drm/omapdrm/omap_fb.c - * - * Copyright (C) 2011 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ * Author: Rob Clark <rob@ti.com> * * This program is free software; you can redistribute it and/or modify it diff --git a/drivers/gpu/drm/omapdrm/omap_fb.h b/drivers/gpu/drm/omapdrm/omap_fb.h new file mode 100644 index 000000000000..94ad5f9e4404 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_fb.h @@ -0,0 +1,46 @@ +/* + * omap_fb.h -- OMAP DRM Framebuffer + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark <rob@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __OMAPDRM_FB_H__ +#define __OMAPDRM_FB_H__ + +struct drm_connector; +struct drm_device; +struct drm_file; +struct drm_framebuffer; +struct drm_gem_object; +struct drm_mode_fb_cmd2; +struct drm_plane_state; +struct omap_overlay_info; +struct seq_file; + +struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, + struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd); +struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, + const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos); +int omap_framebuffer_pin(struct drm_framebuffer *fb); +void omap_framebuffer_unpin(struct drm_framebuffer *fb); +void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, + struct drm_plane_state *state, struct omap_overlay_info *info); +struct drm_connector *omap_framebuffer_get_next_connector( + struct drm_framebuffer *fb, struct drm_connector *from); +bool omap_framebuffer_supports_rotation(struct drm_framebuffer *fb); +void omap_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m); + +#endif /* __OMAPDRM_FB_H__ */ diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c index 9273118040b7..fb309d19ca1b 100644 --- a/drivers/gpu/drm/omapdrm/omap_fbdev.c +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c @@ -1,7 +1,5 @@ /* - * drivers/gpu/drm/omapdrm/omap_fbdev.c - * - * Copyright (C) 2011 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ * Author: Rob Clark <rob@ti.com> * * This program is free software; you can redistribute it and/or modify it @@ -84,9 +82,6 @@ static struct fb_ops omap_fb_ops = { .owner = THIS_MODULE, DRM_FB_HELPER_DEFAULT_OPS, - /* Note: to properly handle manual update displays, we wrap the - * basic fbdev ops which write to the framebuffer - */ .fb_read = drm_fb_helper_sys_read, .fb_write = drm_fb_helper_sys_write, .fb_fillrect = drm_fb_helper_sys_fillrect, diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.h b/drivers/gpu/drm/omapdrm/omap_fbdev.h new file mode 100644 index 000000000000..1f5ba0996a1a --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.h @@ -0,0 +1,39 @@ +/* + * omap_fbdev.h -- OMAP DRM FBDEV Compatibility + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark <rob@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __OMAPDRM_FBDEV_H__ +#define __OMAPDRM_FBDEV_H__ + +struct drm_device; +struct drm_fb_helper; + +#ifdef CONFIG_DRM_FBDEV_EMULATION +struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev); +void omap_fbdev_free(struct drm_device *dev); +#else +static inline struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev) +{ + return NULL; +} +static inline void omap_fbdev_free(struct drm_device *dev) +{ +} +#endif + +#endif /* __OMAPDRM_FBDEV_H__ */ diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c index 5c5c86ddd6f4..443469d4fa46 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem.c +++ b/drivers/gpu/drm/omapdrm/omap_gem.c @@ -1,7 +1,5 @@ /* - * drivers/gpu/drm/omapdrm/omap_gem.c - * - * Copyright (C) 2011 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ * Author: Rob Clark <rob.clark@linaro.org> * * This program is free software; you can redistribute it and/or modify it @@ -996,10 +994,9 @@ void *omap_gem_vaddr(struct drm_gem_object *obj) #ifdef CONFIG_PM /* re-pin objects in DMM in resume path: */ -int omap_gem_resume(struct device *dev) +int omap_gem_resume(struct drm_device *dev) { - struct drm_device *drm_dev = dev_get_drvdata(dev); - struct omap_drm_private *priv = drm_dev->dev_private; + struct omap_drm_private *priv = dev->dev_private; struct omap_gem_object *omap_obj; int ret = 0; @@ -1012,7 +1009,7 @@ int omap_gem_resume(struct device *dev) omap_obj->pages, npages, omap_obj->roll, true); if (ret) { - dev_err(dev, "could not repin: %d\n", ret); + dev_err(dev->dev, "could not repin: %d\n", ret); return ret; } } diff --git a/drivers/gpu/drm/omapdrm/omap_gem.h b/drivers/gpu/drm/omapdrm/omap_gem.h new file mode 100644 index 000000000000..35fa690b3d90 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_gem.h @@ -0,0 +1,99 @@ +/* + * omap_gem.h -- OMAP DRM GEM Object Management + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark <rob@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __OMAPDRM_GEM_H__ +#define __OMAPDRM_GEM_H__ + +#include <linux/types.h> + +enum dma_data_direction; + +struct dma_buf; +struct drm_device; +struct drm_file; +struct drm_gem_object; +struct drm_mode_create_dumb; +struct file; +struct list_head; +struct page; +struct seq_file; +struct vm_area_struct; +struct vm_fault; + +union omap_gem_size; + +/* Initialization and Cleanup */ +void omap_gem_init(struct drm_device *dev); +void omap_gem_deinit(struct drm_device *dev); + +#ifdef CONFIG_PM +int omap_gem_resume(struct drm_device *dev); +#endif + +#ifdef CONFIG_DEBUG_FS +void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m); +void omap_gem_describe_objects(struct list_head *list, struct seq_file *m); +#endif + +/* GEM Object Creation and Deletion */ +struct drm_gem_object *omap_gem_new(struct drm_device *dev, + union omap_gem_size gsize, uint32_t flags); +struct drm_gem_object *omap_gem_new_dmabuf(struct drm_device *dev, size_t size, + struct sg_table *sgt); +int omap_gem_new_handle(struct drm_device *dev, struct drm_file *file, + union omap_gem_size gsize, uint32_t flags, uint32_t *handle); +void omap_gem_free_object(struct drm_gem_object *obj); +void *omap_gem_vaddr(struct drm_gem_object *obj); + +/* Dumb Buffers Interface */ +int omap_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev, + uint32_t handle, uint64_t *offset); +int omap_gem_dumb_create(struct drm_file *file, struct drm_device *dev, + struct drm_mode_create_dumb *args); + +/* mmap() Interface */ +int omap_gem_mmap(struct file *filp, struct vm_area_struct *vma); +int omap_gem_mmap_obj(struct drm_gem_object *obj, + struct vm_area_struct *vma); +uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj); +size_t omap_gem_mmap_size(struct drm_gem_object *obj); + +/* PRIME Interface */ +struct dma_buf *omap_gem_prime_export(struct drm_device *dev, + struct drm_gem_object *obj, int flags); +struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev, + struct dma_buf *buffer); + +int omap_gem_fault(struct vm_fault *vmf); +int omap_gem_roll(struct drm_gem_object *obj, uint32_t roll); +void omap_gem_cpu_sync_page(struct drm_gem_object *obj, int pgoff); +void omap_gem_dma_sync_buffer(struct drm_gem_object *obj, + enum dma_data_direction dir); +int omap_gem_pin(struct drm_gem_object *obj, dma_addr_t *dma_addr); +void omap_gem_unpin(struct drm_gem_object *obj); +int omap_gem_get_pages(struct drm_gem_object *obj, struct page ***pages, + bool remap); +int omap_gem_put_pages(struct drm_gem_object *obj); + +uint32_t omap_gem_flags(struct drm_gem_object *obj); +int omap_gem_rotated_dma_addr(struct drm_gem_object *obj, uint32_t orient, + int x, int y, dma_addr_t *dma_addr); +int omap_gem_tiled_stride(struct drm_gem_object *obj, uint32_t orient); + +#endif /* __OMAPDRM_GEM_H__ */ diff --git a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c index afdbad5c866a..8e41d649e248 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c +++ b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c @@ -1,7 +1,5 @@ /* - * drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c - * - * Copyright (C) 2011 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ * Author: Rob Clark <rob.clark@linaro.org> * * This program is free software; you can redistribute it and/or modify it diff --git a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c index 013b0bba712f..53ba424823b2 100644 --- a/drivers/gpu/drm/omapdrm/omap_irq.c +++ b/drivers/gpu/drm/omapdrm/omap_irq.c @@ -1,7 +1,5 @@ /* - * drivers/gpu/drm/omapdrm/omap_irq.c - * - * Copyright (C) 2012 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ * Author: Rob Clark <rob.clark@linaro.org> * * This program is free software; you can redistribute it and/or modify it diff --git a/drivers/gpu/drm/omapdrm/omap_irq.h b/drivers/gpu/drm/omapdrm/omap_irq.h new file mode 100644 index 000000000000..606c09932bc0 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_irq.h @@ -0,0 +1,39 @@ +/* + * omap_irq.h -- OMAP DRM IRQ Handling + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark <rob@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __OMAPDRM_IRQ_H__ +#define __OMAPDRM_IRQ_H__ + +#include <linux/types.h> + +struct drm_crtc; +struct drm_device; +struct omap_irq_wait; + +int omap_irq_enable_vblank(struct drm_crtc *crtc); +void omap_irq_disable_vblank(struct drm_crtc *crtc); +void omap_drm_irq_uninstall(struct drm_device *dev); +int omap_drm_irq_install(struct drm_device *dev); + +struct omap_irq_wait *omap_irq_wait_init(struct drm_device *dev, + uint32_t irqmask, int count); +int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait, + unsigned long timeout); + +#endif /* __OMAPDRM_IRQ_H__ */ diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c index 15e5d5d325c6..7d789d1551a1 100644 --- a/drivers/gpu/drm/omapdrm/omap_plane.c +++ b/drivers/gpu/drm/omapdrm/omap_plane.c @@ -1,7 +1,5 @@ /* - * drivers/gpu/drm/omapdrm/omap_plane.c - * - * Copyright (C) 2011 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ * Author: Rob Clark <rob.clark@linaro.org> * * This program is free software; you can redistribute it and/or modify it diff --git a/drivers/gpu/drm/omapdrm/omap_plane.h b/drivers/gpu/drm/omapdrm/omap_plane.h new file mode 100644 index 000000000000..dc5e82ad061d --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_plane.h @@ -0,0 +1,37 @@ +/* + * omap_plane.h -- OMAP DRM Plane + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark <rob@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __OMAPDRM_PLANE_H__ +#define __OMAPDRM_PLANE_H__ + +#include <linux/types.h> + +enum drm_plane_type; + +struct drm_device; +struct drm_mode_object; +struct drm_plane; + +struct drm_plane *omap_plane_init(struct drm_device *dev, + int idx, enum drm_plane_type type, + u32 possible_crtcs); +void omap_plane_install_properties(struct drm_plane *plane, + struct drm_mode_object *obj); + +#endif /* __OMAPDRM_PLANE_H__ */ diff --git a/drivers/gpu/drm/omapdrm/tcm-sita.c b/drivers/gpu/drm/omapdrm/tcm-sita.c index c10fdfc0930f..661362d072f7 100644 --- a/drivers/gpu/drm/omapdrm/tcm-sita.c +++ b/drivers/gpu/drm/omapdrm/tcm-sita.c @@ -1,13 +1,11 @@ /* - * tcm-sita.c - * * SImple Tiler Allocator (SiTA): 2D and 1D allocation(reservation) algorithm * * Authors: Ravi Ramachandra <r.ramachandra@ti.com>, * Lajos Molnar <molnar@ti.com> * Andy Gross <andy.gross@ti.com> * - * Copyright (C) 2012 Texas Instruments, Inc. + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ * * This package is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/gpu/drm/omapdrm/tcm-sita.h b/drivers/gpu/drm/omapdrm/tcm-sita.h index 0444f868671c..460e63dbf825 100644 --- a/drivers/gpu/drm/omapdrm/tcm-sita.h +++ b/drivers/gpu/drm/omapdrm/tcm-sita.h @@ -1,11 +1,9 @@ /* - * tcm_sita.h - * * SImple Tiler Allocator (SiTA) private structures. * + * Copyright (C) 2009-2011 Texas Instruments Incorporated - http://www.ti.com/ * Author: Ravi Ramachandra <r.ramachandra@ti.com> * - * Copyright (C) 2009-2011 Texas Instruments, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/gpu/drm/omapdrm/tcm.h b/drivers/gpu/drm/omapdrm/tcm.h index ef7df7d6fc84..d8a369a4f269 100644 --- a/drivers/gpu/drm/omapdrm/tcm.h +++ b/drivers/gpu/drm/omapdrm/tcm.h @@ -1,6 +1,4 @@ /* - * tcm.h - * * TILER container manager specification and support functions for TI * TILER driver. * diff --git a/drivers/gpu/drm/pl111/pl111_drm.h b/drivers/gpu/drm/pl111/pl111_drm.h index 440f53ebee8c..07fa2cdb364a 100644 --- a/drivers/gpu/drm/pl111/pl111_drm.h +++ b/drivers/gpu/drm/pl111/pl111_drm.h @@ -53,7 +53,6 @@ struct pl111_drm_dev_private { struct drm_panel *panel; struct drm_bridge *bridge; struct drm_simple_display_pipe pipe; - struct drm_fbdev_cma *fbdev; void *regs; u32 ienb; diff --git a/drivers/gpu/drm/pl111/pl111_drv.c b/drivers/gpu/drm/pl111/pl111_drv.c index 201d57d5cb54..acb738c69873 100644 --- a/drivers/gpu/drm/pl111/pl111_drv.c +++ b/drivers/gpu/drm/pl111/pl111_drv.c @@ -64,6 +64,7 @@ #include <drm/drm_crtc_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_fb_helper.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_of.h> #include <drm/drm_bridge.h> @@ -137,8 +138,7 @@ static int pl111_modeset_init(struct drm_device *dev) drm_mode_config_reset(dev); - priv->fbdev = drm_fbdev_cma_init(dev, 32, - dev->mode_config.num_connector); + drm_fb_cma_fbdev_init(dev, 32, 0); drm_kms_helper_poll_init(dev); @@ -155,17 +155,10 @@ finish: DEFINE_DRM_GEM_CMA_FOPS(drm_fops); -static void pl111_lastclose(struct drm_device *dev) -{ - struct pl111_drm_dev_private *priv = dev->dev_private; - - drm_fbdev_cma_restore_mode(priv->fbdev); -} - static struct drm_driver pl111_drm_driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC, - .lastclose = pl111_lastclose, + .lastclose = drm_fb_helper_lastclose, .ioctls = NULL, .fops = &drm_fops, .name = "pl111", @@ -281,8 +274,7 @@ static int pl111_amba_remove(struct amba_device *amba_dev) struct pl111_drm_dev_private *priv = drm->dev_private; drm_dev_unregister(drm); - if (priv->fbdev) - drm_fbdev_cma_fini(priv->fbdev); + drm_fb_cma_fbdev_fini(drm); if (priv->panel) drm_panel_bridge_remove(priv->bridge); drm_mode_config_cleanup(drm); diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index 4756b3c9bf2c..9a9214ae0fb5 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -289,6 +289,7 @@ static void qxl_crtc_destroy(struct drm_crtc *crtc) { struct qxl_crtc *qxl_crtc = to_qxl_crtc(crtc); + qxl_bo_unref(&qxl_crtc->cursor_bo); drm_crtc_cleanup(crtc); kfree(qxl_crtc); } @@ -495,6 +496,53 @@ static int qxl_primary_atomic_check(struct drm_plane *plane, return 0; } +static int qxl_primary_apply_cursor(struct drm_plane *plane) +{ + struct drm_device *dev = plane->dev; + struct qxl_device *qdev = dev->dev_private; + struct drm_framebuffer *fb = plane->state->fb; + struct qxl_crtc *qcrtc = to_qxl_crtc(plane->state->crtc); + struct qxl_cursor_cmd *cmd; + struct qxl_release *release; + int ret = 0; + + if (!qcrtc->cursor_bo) + return 0; + + ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd), + QXL_RELEASE_CURSOR_CMD, + &release, NULL); + if (ret) + return ret; + + ret = qxl_release_list_add(release, qcrtc->cursor_bo); + if (ret) + goto out_free_release; + + ret = qxl_release_reserve_list(release, false); + if (ret) + goto out_free_release; + + cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release); + cmd->type = QXL_CURSOR_SET; + cmd->u.set.position.x = plane->state->crtc_x + fb->hot_x; + cmd->u.set.position.y = plane->state->crtc_y + fb->hot_y; + + cmd->u.set.shape = qxl_bo_physical_address(qdev, qcrtc->cursor_bo, 0); + + cmd->u.set.visible = 1; + qxl_release_unmap(qdev, release, &cmd->release_info); + + qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false); + qxl_release_fence_buffer_objects(release); + + return ret; + +out_free_release: + qxl_release_free(qdev, release); + return ret; +} + static void qxl_primary_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_state) { @@ -510,6 +558,7 @@ static void qxl_primary_atomic_update(struct drm_plane *plane, .x2 = qfb->base.width, .y2 = qfb->base.height }; + int ret; bool same_shadow = false; if (old_state->fb) { @@ -531,6 +580,11 @@ static void qxl_primary_atomic_update(struct drm_plane *plane, if (!same_shadow) qxl_io_destroy_primary(qdev); bo_old->is_primary = false; + + ret = qxl_primary_apply_cursor(plane); + if (ret) + DRM_ERROR( + "could not set cursor after creating primary"); } if (!bo->is_primary) { @@ -571,11 +625,12 @@ static void qxl_cursor_atomic_update(struct drm_plane *plane, struct drm_device *dev = plane->dev; struct qxl_device *qdev = dev->dev_private; struct drm_framebuffer *fb = plane->state->fb; + struct qxl_crtc *qcrtc = to_qxl_crtc(plane->state->crtc); struct qxl_release *release; struct qxl_cursor_cmd *cmd; struct qxl_cursor *cursor; struct drm_gem_object *obj; - struct qxl_bo *cursor_bo, *user_bo = NULL; + struct qxl_bo *cursor_bo = NULL, *user_bo = NULL; int ret; void *user_ptr; int size = 64*64*4; @@ -628,6 +683,10 @@ static void qxl_cursor_atomic_update(struct drm_plane *plane, cmd->u.set.shape = qxl_bo_physical_address(qdev, cursor_bo, 0); cmd->type = QXL_CURSOR_SET; + + qxl_bo_unref(&qcrtc->cursor_bo); + qcrtc->cursor_bo = cursor_bo; + cursor_bo = NULL; } else { ret = qxl_release_reserve_list(release, true); @@ -645,6 +704,8 @@ static void qxl_cursor_atomic_update(struct drm_plane *plane, qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false); qxl_release_fence_buffer_objects(release); + qxl_bo_unref(&cursor_bo); + return; out_backoff: diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h index 08752c0ffb35..00a1a66b052a 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.h +++ b/drivers/gpu/drm/qxl/qxl_drv.h @@ -111,6 +111,8 @@ struct qxl_bo_list { struct qxl_crtc { struct drm_crtc base; int index; + + struct qxl_bo *cursor_bo; }; struct qxl_output { diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index 76d63de5921d..d85431400a0d 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -207,13 +207,6 @@ static void rockchip_drm_unbind(struct device *dev) drm_dev_unref(drm_dev); } -static void rockchip_drm_lastclose(struct drm_device *dev) -{ - struct rockchip_drm_private *priv = dev->dev_private; - - drm_fb_helper_restore_fbdev_mode_unlocked(&priv->fbdev_helper); -} - static const struct file_operations rockchip_drm_driver_fops = { .owner = THIS_MODULE, .open = drm_open, @@ -228,7 +221,7 @@ static const struct file_operations rockchip_drm_driver_fops = { static struct drm_driver rockchip_drm_driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC, - .lastclose = rockchip_drm_lastclose, + .lastclose = drm_fb_helper_lastclose, .gem_vm_ops = &drm_gem_cma_vm_ops, .gem_free_object_unlocked = rockchip_gem_free_object, .dumb_create = rockchip_gem_dumb_create, diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index cd2ace0c3caa..e266539e04e5 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -167,20 +167,13 @@ err_gem_object_unreference: return ERR_PTR(ret); } -static void rockchip_drm_output_poll_changed(struct drm_device *dev) -{ - struct rockchip_drm_private *private = dev->dev_private; - - drm_fb_helper_hotplug_event(&private->fbdev_helper); -} - static const struct drm_mode_config_helper_funcs rockchip_mode_config_helpers = { .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, }; static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = { .fb_create = rockchip_user_fb_create, - .output_poll_changed = rockchip_drm_output_poll_changed, + .output_poll_changed = drm_fb_helper_output_poll_changed, .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, }; diff --git a/drivers/gpu/drm/sti/sti_awg_utils.c b/drivers/gpu/drm/sti/sti_awg_utils.c index 2da7d6866d5d..7c5a7830b6e8 100644 --- a/drivers/gpu/drm/sti/sti_awg_utils.c +++ b/drivers/gpu/drm/sti/sti_awg_utils.c @@ -1,7 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics SA 2014 * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #include "sti_awg_utils.h" diff --git a/drivers/gpu/drm/sti/sti_awg_utils.h b/drivers/gpu/drm/sti/sti_awg_utils.h index 45d599bd570a..258a568f050b 100644 --- a/drivers/gpu/drm/sti/sti_awg_utils.h +++ b/drivers/gpu/drm/sti/sti_awg_utils.h @@ -1,7 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) STMicroelectronics SA 2014 * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #ifndef _STI_AWG_UTILS_H_ diff --git a/drivers/gpu/drm/sti/sti_compositor.c b/drivers/gpu/drm/sti/sti_compositor.c index 6e4bf68262db..021b8fcaa0b9 100644 --- a/drivers/gpu/drm/sti/sti_compositor.c +++ b/drivers/gpu/drm/sti/sti_compositor.c @@ -1,9 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics SA 2014 * Authors: Benjamin Gaignard <benjamin.gaignard@st.com> * Fabien Dessenne <fabien.dessenne@st.com> * for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #include <linux/component.h> diff --git a/drivers/gpu/drm/sti/sti_compositor.h b/drivers/gpu/drm/sti/sti_compositor.h index 2952a2d25a52..ac4bb3834810 100644 --- a/drivers/gpu/drm/sti/sti_compositor.h +++ b/drivers/gpu/drm/sti/sti_compositor.h @@ -1,9 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) STMicroelectronics SA 2014 * Authors: Benjamin Gaignard <benjamin.gaignard@st.com> * Fabien Dessenne <fabien.dessenne@st.com> * for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #ifndef _STI_COMPOSITOR_H_ diff --git a/drivers/gpu/drm/sti/sti_crtc.c b/drivers/gpu/drm/sti/sti_crtc.c index e8a4d48e985a..21e50d7b1f86 100644 --- a/drivers/gpu/drm/sti/sti_crtc.c +++ b/drivers/gpu/drm/sti/sti_crtc.c @@ -1,9 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics SA 2014 * Authors: Benjamin Gaignard <benjamin.gaignard@st.com> * Fabien Dessenne <fabien.dessenne@st.com> * for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #include <linux/clk.h> diff --git a/drivers/gpu/drm/sti/sti_crtc.h b/drivers/gpu/drm/sti/sti_crtc.h index 3f2d89a3634d..d87c488212d6 100644 --- a/drivers/gpu/drm/sti/sti_crtc.h +++ b/drivers/gpu/drm/sti/sti_crtc.h @@ -1,7 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) STMicroelectronics SA 2014 * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #ifndef _STI_CRTC_H_ diff --git a/drivers/gpu/drm/sti/sti_cursor.c b/drivers/gpu/drm/sti/sti_cursor.c index b709ebbec095..df0a282b9615 100644 --- a/drivers/gpu/drm/sti/sti_cursor.c +++ b/drivers/gpu/drm/sti/sti_cursor.c @@ -1,9 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics SA 2014 * Authors: Vincent Abriou <vincent.abriou@st.com> * Fabien Dessenne <fabien.dessenne@st.com> * for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #include <linux/seq_file.h> diff --git a/drivers/gpu/drm/sti/sti_cursor.h b/drivers/gpu/drm/sti/sti_cursor.h index 2ee5c10e8b33..067feda5226c 100644 --- a/drivers/gpu/drm/sti/sti_cursor.h +++ b/drivers/gpu/drm/sti/sti_cursor.h @@ -1,7 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) STMicroelectronics SA 2013 * Authors: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #ifndef _STI_CURSOR_H_ diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c index 9e9343101738..55b6967d27e1 100644 --- a/drivers/gpu/drm/sti/sti_drv.c +++ b/drivers/gpu/drm/sti/sti_drv.c @@ -1,7 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics SA 2014 * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #include <drm/drmP.h> @@ -17,6 +17,7 @@ #include <drm/drm_crtc_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_fb_helper.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_of.h> @@ -138,16 +139,9 @@ static int sti_atomic_check(struct drm_device *dev, return ret; } -static void sti_output_poll_changed(struct drm_device *ddev) -{ - struct sti_private *private = ddev->dev_private; - - drm_fbdev_cma_hotplug_event(private->fbdev); -} - static const struct drm_mode_config_funcs sti_mode_config_funcs = { .fb_create = drm_gem_fb_create, - .output_poll_changed = sti_output_poll_changed, + .output_poll_changed = drm_fb_helper_output_poll_changed, .atomic_check = sti_atomic_check, .atomic_commit = drm_atomic_helper_commit, }; @@ -230,11 +224,7 @@ static void sti_cleanup(struct drm_device *ddev) { struct sti_private *private = ddev->dev_private; - if (private->fbdev) { - drm_fbdev_cma_fini(private->fbdev); - private->fbdev = NULL; - } - + drm_fb_cma_fbdev_fini(ddev); drm_kms_helper_poll_fini(ddev); component_unbind_all(ddev->dev, ddev); kfree(private); @@ -244,8 +234,6 @@ static void sti_cleanup(struct drm_device *ddev) static int sti_bind(struct device *dev) { struct drm_device *ddev; - struct sti_private *private; - struct drm_fbdev_cma *fbdev; int ret; ddev = drm_dev_alloc(&sti_driver, dev); @@ -266,15 +254,10 @@ static int sti_bind(struct device *dev) drm_mode_config_reset(ddev); - private = ddev->dev_private; if (ddev->mode_config.num_connector) { - fbdev = drm_fbdev_cma_init(ddev, 32, - ddev->mode_config.num_connector); - if (IS_ERR(fbdev)) { + ret = drm_fb_cma_fbdev_init(ddev, 32, 0); + if (ret) DRM_DEBUG_DRIVER("Warning: fails to create fbdev\n"); - fbdev = NULL; - } - private->fbdev = fbdev; } return 0; diff --git a/drivers/gpu/drm/sti/sti_drv.h b/drivers/gpu/drm/sti/sti_drv.h index 6502ed2d3351..4b41142a22e4 100644 --- a/drivers/gpu/drm/sti/sti_drv.h +++ b/drivers/gpu/drm/sti/sti_drv.h @@ -1,7 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) STMicroelectronics SA 2014 * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #ifndef _STI_DRV_H_ @@ -24,7 +24,6 @@ struct sti_private { struct sti_compositor *compo; struct drm_property *plane_zorder_property; struct drm_device *drm_dev; - struct drm_fbdev_cma *fbdev; }; extern struct platform_driver sti_tvout_driver; diff --git a/drivers/gpu/drm/sti/sti_dvo.c b/drivers/gpu/drm/sti/sti_dvo.c index 83314aee65cb..a5979cd25cc7 100644 --- a/drivers/gpu/drm/sti/sti_dvo.c +++ b/drivers/gpu/drm/sti/sti_dvo.c @@ -1,7 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics SA 2014 * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #include <linux/clk.h> diff --git a/drivers/gpu/drm/sti/sti_gdp.c b/drivers/gpu/drm/sti/sti_gdp.c index b65eea4f2c97..9b2c47051b51 100644 --- a/drivers/gpu/drm/sti/sti_gdp.c +++ b/drivers/gpu/drm/sti/sti_gdp.c @@ -1,9 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics SA 2014 * Authors: Benjamin Gaignard <benjamin.gaignard@st.com> * Fabien Dessenne <fabien.dessenne@st.com> * for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #include <linux/seq_file.h> diff --git a/drivers/gpu/drm/sti/sti_gdp.h b/drivers/gpu/drm/sti/sti_gdp.h index 73947a4a8004..d3e8ebfe2e66 100644 --- a/drivers/gpu/drm/sti/sti_gdp.h +++ b/drivers/gpu/drm/sti/sti_gdp.h @@ -1,9 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) STMicroelectronics SA 2014 * Authors: Benjamin Gaignard <benjamin.gaignard@st.com> * Fabien Dessenne <fabien.dessenne@st.com> * for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #ifndef _STI_GDP_H_ diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c index cf65e32b5090..67bbdb49fffc 100644 --- a/drivers/gpu/drm/sti/sti_hda.c +++ b/drivers/gpu/drm/sti/sti_hda.c @@ -1,7 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics SA 2014 * Author: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #include <linux/clk.h> diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c index 4ea1cc1c032e..58f431102512 100644 --- a/drivers/gpu/drm/sti/sti_hdmi.c +++ b/drivers/gpu/drm/sti/sti_hdmi.c @@ -1,7 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics SA 2014 * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #include <linux/clk.h> diff --git a/drivers/gpu/drm/sti/sti_hdmi.h b/drivers/gpu/drm/sti/sti_hdmi.h index c6469b56ce7e..63a24941db3b 100644 --- a/drivers/gpu/drm/sti/sti_hdmi.h +++ b/drivers/gpu/drm/sti/sti_hdmi.h @@ -1,7 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) STMicroelectronics SA 2014 * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #ifndef _STI_HDMI_H_ diff --git a/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c b/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c index 8e0ceb0ced33..01699af6a768 100644 --- a/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c +++ b/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c @@ -1,7 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics SA 2014 * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #include "sti_hdmi_tx3g4c28phy.h" diff --git a/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.h b/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.h index f99a7ff281ef..d261947ef30a 100644 --- a/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.h +++ b/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.h @@ -1,7 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) STMicroelectronics SA 2014 * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #ifndef _STI_HDMI_TX3G4C28PHY_H_ diff --git a/drivers/gpu/drm/sti/sti_hqvdp.c b/drivers/gpu/drm/sti/sti_hqvdp.c index b19b3430b296..106be8c4e58b 100644 --- a/drivers/gpu/drm/sti/sti_hqvdp.c +++ b/drivers/gpu/drm/sti/sti_hqvdp.c @@ -1,7 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics SA 2014 * Authors: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #include <linux/component.h> diff --git a/drivers/gpu/drm/sti/sti_hqvdp_lut.h b/drivers/gpu/drm/sti/sti_hqvdp_lut.h index 619af7f4384e..57cccd9546a3 100644 --- a/drivers/gpu/drm/sti/sti_hqvdp_lut.h +++ b/drivers/gpu/drm/sti/sti_hqvdp_lut.h @@ -1,7 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) STMicroelectronics SA 2014 * Authors: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #ifndef _STI_HQVDP_LUT_H_ diff --git a/drivers/gpu/drm/sti/sti_mixer.c b/drivers/gpu/drm/sti/sti_mixer.c index 2bd1d46fe1cd..a4f45c74d678 100644 --- a/drivers/gpu/drm/sti/sti_mixer.c +++ b/drivers/gpu/drm/sti/sti_mixer.c @@ -1,9 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics SA 2014 * Authors: Benjamin Gaignard <benjamin.gaignard@st.com> * Fabien Dessenne <fabien.dessenne@st.com> * for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #include <linux/seq_file.h> diff --git a/drivers/gpu/drm/sti/sti_mixer.h b/drivers/gpu/drm/sti/sti_mixer.h index e64a00e61049..4cb3cfddc03a 100644 --- a/drivers/gpu/drm/sti/sti_mixer.h +++ b/drivers/gpu/drm/sti/sti_mixer.h @@ -1,9 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) STMicroelectronics SA 2014 * Authors: Benjamin Gaignard <benjamin.gaignard@st.com> * Fabien Dessenne <fabien.dessenne@st.com> * for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #ifndef _STI_MIXER_H_ diff --git a/drivers/gpu/drm/sti/sti_plane.c b/drivers/gpu/drm/sti/sti_plane.c index 427d8f58c6b1..b074609c960a 100644 --- a/drivers/gpu/drm/sti/sti_plane.c +++ b/drivers/gpu/drm/sti/sti_plane.c @@ -1,9 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics SA 2014 * Authors: Benjamin Gaignard <benjamin.gaignard@st.com> * Fabien Dessenne <fabien.dessenne@st.com> * for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #include <drm/drmP.h> diff --git a/drivers/gpu/drm/sti/sti_plane.h b/drivers/gpu/drm/sti/sti_plane.h index c36c13faaa18..b8d7fae2a014 100644 --- a/drivers/gpu/drm/sti/sti_plane.h +++ b/drivers/gpu/drm/sti/sti_plane.h @@ -1,7 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) STMicroelectronics SA 2014 * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #ifndef _STI_PLANE_H_ diff --git a/drivers/gpu/drm/sti/sti_tvout.c b/drivers/gpu/drm/sti/sti_tvout.c index 8959fcc743a8..ea4a3b87fa55 100644 --- a/drivers/gpu/drm/sti/sti_tvout.c +++ b/drivers/gpu/drm/sti/sti_tvout.c @@ -1,9 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics SA 2014 * Authors: Benjamin Gaignard <benjamin.gaignard@st.com> * Vincent Abriou <vincent.abriou@st.com> * for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #include <linux/clk.h> diff --git a/drivers/gpu/drm/sti/sti_vid.c b/drivers/gpu/drm/sti/sti_vid.c index 577a3341d3c1..2aac36c95835 100644 --- a/drivers/gpu/drm/sti/sti_vid.c +++ b/drivers/gpu/drm/sti/sti_vid.c @@ -1,7 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics SA 2014 * Author: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #include <linux/seq_file.h> diff --git a/drivers/gpu/drm/sti/sti_vid.h b/drivers/gpu/drm/sti/sti_vid.h index fdc90f922a05..9dbd78461de1 100644 --- a/drivers/gpu/drm/sti/sti_vid.h +++ b/drivers/gpu/drm/sti/sti_vid.h @@ -1,7 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) STMicroelectronics SA 2014 * Author: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #ifndef _STI_VID_H_ diff --git a/drivers/gpu/drm/sti/sti_vtg.c b/drivers/gpu/drm/sti/sti_vtg.c index 3aa2fa6f2228..6c421644de18 100644 --- a/drivers/gpu/drm/sti/sti_vtg.c +++ b/drivers/gpu/drm/sti/sti_vtg.c @@ -1,10 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics SA 2014 * Authors: Benjamin Gaignard <benjamin.gaignard@st.com> * Fabien Dessenne <fabien.dessenne@st.com> * Vincent Abriou <vincent.abriou@st.com> * for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #include <linux/module.h> diff --git a/drivers/gpu/drm/sti/sti_vtg.h b/drivers/gpu/drm/sti/sti_vtg.h index f1dcdf9c2342..d177129e5bcb 100644 --- a/drivers/gpu/drm/sti/sti_vtg.h +++ b/drivers/gpu/drm/sti/sti_vtg.h @@ -1,7 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) STMicroelectronics SA 2014 * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 */ #ifndef _STI_VTG_H_ diff --git a/drivers/gpu/drm/stm/drv.c b/drivers/gpu/drm/stm/drv.c index c857663eafc2..8fe954c27fba 100644 --- a/drivers/gpu/drm/stm/drv.c +++ b/drivers/gpu/drm/stm/drv.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics SA 2017 * @@ -5,8 +6,6 @@ * Yannick Fertre <yannick.fertre@st.com> * Fabien Dessenne <fabien.dessenne@st.com> * Mickael Reulier <mickael.reulier@st.com> - * - * License terms: GNU General Public License (GPL), version 2 */ #include <linux/component.h> @@ -15,6 +14,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_helper.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> @@ -24,35 +24,19 @@ #define STM_MAX_FB_WIDTH 2048 #define STM_MAX_FB_HEIGHT 2048 /* same as width to handle orientation */ -static void drv_output_poll_changed(struct drm_device *ddev) -{ - struct ltdc_device *ldev = ddev->dev_private; - - drm_fbdev_cma_hotplug_event(ldev->fbdev); -} - static const struct drm_mode_config_funcs drv_mode_config_funcs = { .fb_create = drm_gem_fb_create, - .output_poll_changed = drv_output_poll_changed, + .output_poll_changed = drm_fb_helper_output_poll_changed, .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, }; -static void drv_lastclose(struct drm_device *ddev) -{ - struct ltdc_device *ldev = ddev->dev_private; - - DRM_DEBUG("%s\n", __func__); - - drm_fbdev_cma_restore_mode(ldev->fbdev); -} - DEFINE_DRM_GEM_CMA_FOPS(drv_driver_fops); static struct drm_driver drv_driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC, - .lastclose = drv_lastclose, + .lastclose = drm_fb_helper_lastclose, .name = "stm", .desc = "STMicroelectronics SoC DRM", .date = "20170330", @@ -79,7 +63,6 @@ static struct drm_driver drv_driver = { static int drv_load(struct drm_device *ddev) { struct platform_device *pdev = to_platform_device(ddev->dev); - struct drm_fbdev_cma *fbdev; struct ltdc_device *ldev; int ret; @@ -112,14 +95,9 @@ static int drv_load(struct drm_device *ddev) drm_kms_helper_poll_init(ddev); if (ddev->mode_config.num_connector) { - ldev = ddev->dev_private; - fbdev = drm_fbdev_cma_init(ddev, 16, - ddev->mode_config.num_connector); - if (IS_ERR(fbdev)) { + ret = drm_fb_cma_fbdev_init(ddev, 16, 0); + if (ret) DRM_DEBUG("Warning: fails to create fbdev\n"); - fbdev = NULL; - } - ldev->fbdev = fbdev; } platform_set_drvdata(pdev, ddev); @@ -132,14 +110,9 @@ err: static void drv_unload(struct drm_device *ddev) { - struct ltdc_device *ldev = ddev->dev_private; - DRM_DEBUG("%s\n", __func__); - if (ldev->fbdev) { - drm_fbdev_cma_fini(ldev->fbdev); - ldev->fbdev = NULL; - } + drm_fb_cma_fbdev_fini(ddev); drm_kms_helper_poll_fini(ddev); ltdc_unload(ddev); drm_mode_config_cleanup(ddev); diff --git a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c index 3e8b9ed7e8b3..82dcb20cdaa3 100644 --- a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c +++ b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c @@ -1,10 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics SA 2017 * * Authors: Philippe Cornu <philippe.cornu@st.com> * Yannick Fertre <yannick.fertre@st.com> - * - * License terms: GNU General Public License (GPL), version 2 */ #include <linux/clk.h> diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c index 35e884239f60..394613b0fd46 100644 --- a/drivers/gpu/drm/stm/ltdc.c +++ b/drivers/gpu/drm/stm/ltdc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics SA 2017 * @@ -5,8 +6,6 @@ * Yannick Fertre <yannick.fertre@st.com> * Fabien Dessenne <fabien.dessenne@st.com> * Mickael Reulier <mickael.reulier@st.com> - * - * License terms: GNU General Public License (GPL), version 2 */ #include <linux/clk.h> diff --git a/drivers/gpu/drm/stm/ltdc.h b/drivers/gpu/drm/stm/ltdc.h index ae437557d715..edd1c0a446d1 100644 --- a/drivers/gpu/drm/stm/ltdc.h +++ b/drivers/gpu/drm/stm/ltdc.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) STMicroelectronics SA 2017 * @@ -5,8 +6,6 @@ * Yannick Fertre <yannick.fertre@st.com> * Fabien Dessenne <fabien.dessenne@st.com> * Mickael Reulier <mickael.reulier@st.com> - * - * License terms: GNU General Public License (GPL), version 2 */ #ifndef _LTDC_H_ @@ -21,7 +20,6 @@ struct ltdc_caps { }; struct ltdc_device { - struct drm_fbdev_cma *fbdev; void __iomem *regs; struct clk *pixel_clk; /* lcd pixel clock */ struct mutex err_lock; /* protecting error_status */ diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile index 0c2f8c7facae..82a6ac57fbe3 100644 --- a/drivers/gpu/drm/sun4i/Makefile +++ b/drivers/gpu/drm/sun4i/Makefile @@ -9,7 +9,9 @@ sun4i-drm-hdmi-y += sun4i_hdmi_enc.o sun4i-drm-hdmi-y += sun4i_hdmi_i2c.o sun4i-drm-hdmi-y += sun4i_hdmi_tmds_clk.o -sun8i-mixer-y += sun8i_mixer.o sun8i_layer.o +sun8i-mixer-y += sun8i_mixer.o sun8i_ui_layer.o \ + sun8i_vi_layer.o sun8i_ui_scaler.o \ + sun8i_vi_scaler.o sun8i_csc.o sun4i-tcon-y += sun4i_crtc.o sun4i-tcon-y += sun4i_dotclock.o diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c index 75c76cdd82bc..2b4717604eb3 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c @@ -26,20 +26,13 @@ #include "sun4i_framebuffer.h" #include "sun4i_tcon.h" -static void sun4i_drv_lastclose(struct drm_device *dev) -{ - struct sun4i_drv *drv = dev->dev_private; - - drm_fbdev_cma_restore_mode(drv->fbdev); -} - DEFINE_DRM_GEM_CMA_FOPS(sun4i_drv_fops); static struct drm_driver sun4i_drv_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, /* Generic Operations */ - .lastclose = sun4i_drv_lastclose, + .lastclose = drm_fb_helper_lastclose, .fops = &sun4i_drv_fops, .name = "sun4i-drm", .desc = "Allwinner sun4i Display Engine", @@ -126,10 +119,9 @@ static int sun4i_drv_bind(struct device *dev) sun4i_remove_framebuffers(); /* Create our framebuffer */ - drv->fbdev = sun4i_framebuffer_init(drm); - if (IS_ERR(drv->fbdev)) { + ret = sun4i_framebuffer_init(drm); + if (ret) { dev_err(drm->dev, "Couldn't create our framebuffer\n"); - ret = PTR_ERR(drv->fbdev); goto cleanup_mode_config; } @@ -187,13 +179,7 @@ static bool sun4i_drv_node_is_frontend(struct device_node *node) static bool sun4i_drv_node_is_tcon(struct device_node *node) { - return of_device_is_compatible(node, "allwinner,sun4i-a10-tcon") || - of_device_is_compatible(node, "allwinner,sun5i-a13-tcon") || - of_device_is_compatible(node, "allwinner,sun6i-a31-tcon") || - of_device_is_compatible(node, "allwinner,sun6i-a31s-tcon") || - of_device_is_compatible(node, "allwinner,sun7i-a20-tcon") || - of_device_is_compatible(node, "allwinner,sun8i-a33-tcon") || - of_device_is_compatible(node, "allwinner,sun8i-v3s-tcon"); + return !!of_match_node(sun4i_tcon_of_table, node); } static int compare_of(struct device *dev, void *data) diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.h b/drivers/gpu/drm/sun4i/sun4i_drv.h index a960c89270cc..2825f140da54 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.h +++ b/drivers/gpu/drm/sun4i/sun4i_drv.h @@ -20,8 +20,6 @@ struct sun4i_drv { struct list_head engine_list; struct list_head tcon_list; - - struct drm_fbdev_cma *fbdev; }; #endif /* _SUN4I_DRV_H_ */ diff --git a/drivers/gpu/drm/sun4i/sun4i_framebuffer.c b/drivers/gpu/drm/sun4i/sun4i_framebuffer.c index 2992f0a6b349..38a36c0dfa2f 100644 --- a/drivers/gpu/drm/sun4i/sun4i_framebuffer.c +++ b/drivers/gpu/drm/sun4i/sun4i_framebuffer.c @@ -11,6 +11,7 @@ */ #include <drm/drm_atomic_helper.h> +#include <drm/drm_fb_helper.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drmP.h> @@ -18,21 +19,14 @@ #include "sun4i_drv.h" #include "sun4i_framebuffer.h" -static void sun4i_de_output_poll_changed(struct drm_device *drm) -{ - struct sun4i_drv *drv = drm->dev_private; - - drm_fbdev_cma_hotplug_event(drv->fbdev); -} - static const struct drm_mode_config_funcs sun4i_de_mode_config_funcs = { - .output_poll_changed = sun4i_de_output_poll_changed, + .output_poll_changed = drm_fb_helper_output_poll_changed, .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, .fb_create = drm_gem_fb_create, }; -struct drm_fbdev_cma *sun4i_framebuffer_init(struct drm_device *drm) +int sun4i_framebuffer_init(struct drm_device *drm) { drm_mode_config_reset(drm); @@ -41,12 +35,10 @@ struct drm_fbdev_cma *sun4i_framebuffer_init(struct drm_device *drm) drm->mode_config.funcs = &sun4i_de_mode_config_funcs; - return drm_fbdev_cma_init(drm, 32, drm->mode_config.num_connector); + return drm_fb_cma_fbdev_init(drm, 32, 0); } void sun4i_framebuffer_free(struct drm_device *drm) { - struct sun4i_drv *drv = drm->dev_private; - - drm_fbdev_cma_fini(drv->fbdev); + drm_fb_cma_fbdev_fini(drm); } diff --git a/drivers/gpu/drm/sun4i/sun4i_framebuffer.h b/drivers/gpu/drm/sun4i/sun4i_framebuffer.h index 3afd65252ee0..7ef0aed8384c 100644 --- a/drivers/gpu/drm/sun4i/sun4i_framebuffer.h +++ b/drivers/gpu/drm/sun4i/sun4i_framebuffer.h @@ -13,7 +13,7 @@ #ifndef _SUN4I_FRAMEBUFFER_H_ #define _SUN4I_FRAMEBUFFER_H_ -struct drm_fbdev_cma *sun4i_framebuffer_init(struct drm_device *drm); +int sun4i_framebuffer_init(struct drm_device *drm); void sun4i_framebuffer_free(struct drm_device *drm); #endif /* _SUN4I_FRAMEBUFFER_H_ */ diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index e122f5b2a395..a1ed462c2430 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -900,7 +900,8 @@ static const struct sun4i_tcon_quirks sun8i_v3s_quirks = { /* nothing is supported */ }; -static const struct of_device_id sun4i_tcon_of_table[] = { +/* sun4i_drv uses this list to check if a device node is a TCON */ +const struct of_device_id sun4i_tcon_of_table[] = { { .compatible = "allwinner,sun4i-a10-tcon", .data = &sun4i_a10_quirks }, { .compatible = "allwinner,sun5i-a13-tcon", .data = &sun5i_a13_quirks }, { .compatible = "allwinner,sun6i-a31-tcon", .data = &sun6i_a31_quirks }, @@ -911,6 +912,7 @@ static const struct of_device_id sun4i_tcon_of_table[] = { { } }; MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table); +EXPORT_SYMBOL(sun4i_tcon_of_table); static struct platform_driver sun4i_tcon_platform_driver = { .probe = sun4i_tcon_probe, diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.h b/drivers/gpu/drm/sun4i/sun4i_tcon.h index f61bf6d83b4a..839266a38505 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.h +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.h @@ -197,4 +197,6 @@ void sun4i_tcon_mode_set(struct sun4i_tcon *tcon, void sun4i_tcon_set_status(struct sun4i_tcon *crtc, const struct drm_encoder *encoder, bool enable); +extern const struct of_device_id sun4i_tcon_of_table[]; + #endif /* __SUN4I_TCON_H__ */ diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.c b/drivers/gpu/drm/sun4i/sun8i_csc.c new file mode 100644 index 000000000000..b14925b40ccf --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun8i_csc.c @@ -0,0 +1,93 @@ +/* + * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <drm/drmP.h> + +#include "sun8i_csc.h" +#include "sun8i_mixer.h" + +static const u32 ccsc_base[2][2] = { + {CCSC00_OFFSET, CCSC01_OFFSET}, + {CCSC10_OFFSET, CCSC11_OFFSET}, +}; + +/* + * Factors are in two's complement format, 10 bits for fractinal part. + * First tree values in each line are multiplication factor and last + * value is constant, which is added at the end. + */ +static const u32 yuv2rgb[] = { + 0x000004A8, 0x00000000, 0x00000662, 0xFFFC845A, + 0x000004A8, 0xFFFFFE6F, 0xFFFFFCBF, 0x00021DF4, + 0x000004A8, 0x00000813, 0x00000000, 0xFFFBAC4A, +}; + +static const u32 yvu2rgb[] = { + 0x000004A8, 0x00000662, 0x00000000, 0xFFFC845A, + 0x000004A8, 0xFFFFFCBF, 0xFFFFFE6F, 0x00021DF4, + 0x000004A8, 0x00000000, 0x00000813, 0xFFFBAC4A, +}; + +static void sun8i_csc_set_coefficients(struct regmap *map, u32 base, + enum sun8i_csc_mode mode) +{ + const u32 *table; + int i, data; + + switch (mode) { + case SUN8I_CSC_MODE_YUV2RGB: + table = yuv2rgb; + break; + case SUN8I_CSC_MODE_YVU2RGB: + table = yvu2rgb; + break; + default: + DRM_WARN("Wrong CSC mode specified.\n"); + return; + } + + for (i = 0; i < 12; i++) { + data = table[i]; + /* For some reason, 0x200 must be added to constant parts */ + if (((i + 1) & 3) == 0) + data += 0x200; + regmap_write(map, SUN8I_CSC_COEFF(base, i), data); + } +} + +static void sun8i_csc_enable(struct regmap *map, u32 base, bool enable) +{ + u32 val; + + if (enable) + val = SUN8I_CSC_CTRL_EN; + else + val = 0; + + regmap_update_bits(map, SUN8I_CSC_CTRL(base), SUN8I_CSC_CTRL_EN, val); +} + +void sun8i_csc_set_ccsc_coefficients(struct sun8i_mixer *mixer, int layer, + enum sun8i_csc_mode mode) +{ + u32 base; + + base = ccsc_base[mixer->cfg->ccsc][layer]; + + sun8i_csc_set_coefficients(mixer->engine.regs, base, mode); +} + +void sun8i_csc_enable_ccsc(struct sun8i_mixer *mixer, int layer, bool enable) +{ + u32 base; + + base = ccsc_base[mixer->cfg->ccsc][layer]; + + sun8i_csc_enable(mixer->engine.regs, base, enable); +} diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.h b/drivers/gpu/drm/sun4i/sun8i_csc.h new file mode 100644 index 000000000000..880e8fbb0855 --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun8i_csc.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#ifndef _SUN8I_CSC_H_ +#define _SUN8I_CSC_H_ + +struct sun8i_mixer; + +/* VI channel CSC units offsets */ +#define CCSC00_OFFSET 0xAA050 +#define CCSC01_OFFSET 0xFA000 +#define CCSC10_OFFSET 0xA0000 +#define CCSC11_OFFSET 0xF0000 + +#define SUN8I_CSC_CTRL(base) (base + 0x0) +#define SUN8I_CSC_COEFF(base, i) (base + 0x10 + 4 * i) + +#define SUN8I_CSC_CTRL_EN BIT(0) + +enum sun8i_csc_mode { + SUN8I_CSC_MODE_OFF, + SUN8I_CSC_MODE_YUV2RGB, + SUN8I_CSC_MODE_YVU2RGB, +}; + +void sun8i_csc_set_ccsc_coefficients(struct sun8i_mixer *mixer, int layer, + enum sun8i_csc_mode mode); +void sun8i_csc_enable_ccsc(struct sun8i_mixer *mixer, int layer, bool enable); + +#endif diff --git a/drivers/gpu/drm/sun4i/sun8i_layer.c b/drivers/gpu/drm/sun4i/sun8i_layer.c deleted file mode 100644 index 23810ff72684..000000000000 --- a/drivers/gpu/drm/sun4i/sun8i_layer.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) Icenowy Zheng <icenowy@aosc.io> - * - * Based on sun4i_layer.h, which is: - * Copyright (C) 2015 Free Electrons - * Copyright (C) 2015 NextThing Co - * - * Maxime Ripard <maxime.ripard@free-electrons.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - */ - -#include <drm/drm_atomic_helper.h> -#include <drm/drm_plane_helper.h> -#include <drm/drmP.h> - -#include "sun8i_layer.h" -#include "sun8i_mixer.h" - -struct sun8i_plane_desc { - enum drm_plane_type type; - const uint32_t *formats; - uint32_t nformats; -}; - -static void sun8i_mixer_layer_atomic_disable(struct drm_plane *plane, - struct drm_plane_state *old_state) -{ - struct sun8i_layer *layer = plane_to_sun8i_layer(plane); - struct sun8i_mixer *mixer = layer->mixer; - - sun8i_mixer_layer_enable(mixer, layer->id, false); -} - -static void sun8i_mixer_layer_atomic_update(struct drm_plane *plane, - struct drm_plane_state *old_state) -{ - struct sun8i_layer *layer = plane_to_sun8i_layer(plane); - struct sun8i_mixer *mixer = layer->mixer; - - sun8i_mixer_update_layer_coord(mixer, layer->id, plane); - sun8i_mixer_update_layer_formats(mixer, layer->id, plane); - sun8i_mixer_update_layer_buffer(mixer, layer->id, plane); - sun8i_mixer_layer_enable(mixer, layer->id, true); -} - -static struct drm_plane_helper_funcs sun8i_mixer_layer_helper_funcs = { - .atomic_disable = sun8i_mixer_layer_atomic_disable, - .atomic_update = sun8i_mixer_layer_atomic_update, -}; - -static const struct drm_plane_funcs sun8i_mixer_layer_funcs = { - .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, - .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, - .destroy = drm_plane_cleanup, - .disable_plane = drm_atomic_helper_disable_plane, - .reset = drm_atomic_helper_plane_reset, - .update_plane = drm_atomic_helper_update_plane, -}; - -static const uint32_t sun8i_mixer_layer_formats[] = { - DRM_FORMAT_RGB888, - DRM_FORMAT_ARGB8888, - DRM_FORMAT_XRGB8888, -}; - -static const struct sun8i_plane_desc sun8i_mixer_planes[] = { - { - .type = DRM_PLANE_TYPE_PRIMARY, - .formats = sun8i_mixer_layer_formats, - .nformats = ARRAY_SIZE(sun8i_mixer_layer_formats), - }, -}; - -static struct sun8i_layer *sun8i_layer_init_one(struct drm_device *drm, - struct sun8i_mixer *mixer, - const struct sun8i_plane_desc *plane) -{ - struct sun8i_layer *layer; - int ret; - - layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL); - if (!layer) - return ERR_PTR(-ENOMEM); - - /* possible crtcs are set later */ - ret = drm_universal_plane_init(drm, &layer->plane, 0, - &sun8i_mixer_layer_funcs, - plane->formats, plane->nformats, - NULL, plane->type, NULL); - if (ret) { - dev_err(drm->dev, "Couldn't initialize layer\n"); - return ERR_PTR(ret); - } - - drm_plane_helper_add(&layer->plane, - &sun8i_mixer_layer_helper_funcs); - layer->mixer = mixer; - - return layer; -} - -struct drm_plane **sun8i_layers_init(struct drm_device *drm, - struct sunxi_engine *engine) -{ - struct drm_plane **planes; - struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine); - int i; - - planes = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes) + 1, - sizeof(*planes), GFP_KERNEL); - if (!planes) - return ERR_PTR(-ENOMEM); - - for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) { - const struct sun8i_plane_desc *plane = &sun8i_mixer_planes[i]; - struct sun8i_layer *layer; - - layer = sun8i_layer_init_one(drm, mixer, plane); - if (IS_ERR(layer)) { - dev_err(drm->dev, "Couldn't initialize %s plane\n", - i ? "overlay" : "primary"); - return ERR_CAST(layer); - }; - - layer->id = i; - planes[i] = &layer->plane; - }; - - return planes; -} diff --git a/drivers/gpu/drm/sun4i/sun8i_layer.h b/drivers/gpu/drm/sun4i/sun8i_layer.h deleted file mode 100644 index e5eccd27cff0..000000000000 --- a/drivers/gpu/drm/sun4i/sun8i_layer.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) Icenowy Zheng <icenowy@aosc.io> - * - * Based on sun4i_layer.h, which is: - * Copyright (C) 2015 Free Electrons - * Copyright (C) 2015 NextThing Co - * - * Maxime Ripard <maxime.ripard@free-electrons.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - */ - -#ifndef _SUN8I_LAYER_H_ -#define _SUN8I_LAYER_H_ - -struct sunxi_engine; - -struct sun8i_layer { - struct drm_plane plane; - struct sun4i_drv *drv; - struct sun8i_mixer *mixer; - int id; -}; - -static inline struct sun8i_layer * -plane_to_sun8i_layer(struct drm_plane *plane) -{ - return container_of(plane, struct sun8i_layer, plane); -} - -struct drm_plane **sun8i_layers_init(struct drm_device *drm, - struct sunxi_engine *engine); -#endif /* _SUN8I_LAYER_H_ */ diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c index cb193c5f1686..29ceeb016d72 100644 --- a/drivers/gpu/drm/sun4i/sun8i_mixer.c +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c @@ -26,204 +26,288 @@ #include "sun4i_drv.h" #include "sun8i_mixer.h" -#include "sun8i_layer.h" +#include "sun8i_ui_layer.h" +#include "sun8i_vi_layer.h" #include "sunxi_engine.h" -static void sun8i_mixer_commit(struct sunxi_engine *engine) -{ - DRM_DEBUG_DRIVER("Committing changes\n"); - - regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF, - SUN8I_MIXER_GLOBAL_DBUFF_ENABLE); -} - -void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer, - int layer, bool enable) -{ - u32 val; - /* Currently the first UI channel is used */ - int chan = mixer->cfg->vi_num; - - DRM_DEBUG_DRIVER("Enabling layer %d in channel %d\n", layer, chan); - - if (enable) - val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN; - else - val = 0; - - regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer), - SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val); - - /* Set the alpha configuration */ - regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer), - SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK, - SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF); - regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer), - SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK, - SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF); -} - -static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane, - u32 format, u32 *mode) -{ - switch (format) { - case DRM_FORMAT_ARGB8888: - *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888; - break; - - case DRM_FORMAT_XRGB8888: - *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888; - break; - - case DRM_FORMAT_RGB888: - *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888; - break; - - default: - return -EINVAL; - } - - return 0; -} +static const struct de2_fmt_info de2_formats[] = { + { + .drm_fmt = DRM_FORMAT_ARGB8888, + .de2_fmt = SUN8I_MIXER_FBFMT_ARGB8888, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_ABGR8888, + .de2_fmt = SUN8I_MIXER_FBFMT_ABGR8888, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_RGBA8888, + .de2_fmt = SUN8I_MIXER_FBFMT_RGBA8888, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_BGRA8888, + .de2_fmt = SUN8I_MIXER_FBFMT_BGRA8888, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_XRGB8888, + .de2_fmt = SUN8I_MIXER_FBFMT_XRGB8888, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_XBGR8888, + .de2_fmt = SUN8I_MIXER_FBFMT_XBGR8888, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_RGBX8888, + .de2_fmt = SUN8I_MIXER_FBFMT_RGBX8888, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_BGRX8888, + .de2_fmt = SUN8I_MIXER_FBFMT_BGRX8888, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_RGB888, + .de2_fmt = SUN8I_MIXER_FBFMT_RGB888, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_BGR888, + .de2_fmt = SUN8I_MIXER_FBFMT_BGR888, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_RGB565, + .de2_fmt = SUN8I_MIXER_FBFMT_RGB565, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_BGR565, + .de2_fmt = SUN8I_MIXER_FBFMT_BGR565, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_ARGB4444, + .de2_fmt = SUN8I_MIXER_FBFMT_ARGB4444, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_ABGR4444, + .de2_fmt = SUN8I_MIXER_FBFMT_ABGR4444, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_RGBA4444, + .de2_fmt = SUN8I_MIXER_FBFMT_RGBA4444, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_BGRA4444, + .de2_fmt = SUN8I_MIXER_FBFMT_BGRA4444, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_ARGB1555, + .de2_fmt = SUN8I_MIXER_FBFMT_ARGB1555, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_ABGR1555, + .de2_fmt = SUN8I_MIXER_FBFMT_ABGR1555, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_RGBA5551, + .de2_fmt = SUN8I_MIXER_FBFMT_RGBA5551, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_BGRA5551, + .de2_fmt = SUN8I_MIXER_FBFMT_BGRA5551, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_UYVY, + .de2_fmt = SUN8I_MIXER_FBFMT_UYVY, + .rgb = false, + .csc = SUN8I_CSC_MODE_YUV2RGB, + }, + { + .drm_fmt = DRM_FORMAT_VYUY, + .de2_fmt = SUN8I_MIXER_FBFMT_VYUY, + .rgb = false, + .csc = SUN8I_CSC_MODE_YUV2RGB, + }, + { + .drm_fmt = DRM_FORMAT_YUYV, + .de2_fmt = SUN8I_MIXER_FBFMT_YUYV, + .rgb = false, + .csc = SUN8I_CSC_MODE_YUV2RGB, + }, + { + .drm_fmt = DRM_FORMAT_YVYU, + .de2_fmt = SUN8I_MIXER_FBFMT_YVYU, + .rgb = false, + .csc = SUN8I_CSC_MODE_YUV2RGB, + }, + { + .drm_fmt = DRM_FORMAT_NV16, + .de2_fmt = SUN8I_MIXER_FBFMT_NV16, + .rgb = false, + .csc = SUN8I_CSC_MODE_YUV2RGB, + }, + { + .drm_fmt = DRM_FORMAT_NV61, + .de2_fmt = SUN8I_MIXER_FBFMT_NV61, + .rgb = false, + .csc = SUN8I_CSC_MODE_YUV2RGB, + }, + { + .drm_fmt = DRM_FORMAT_NV12, + .de2_fmt = SUN8I_MIXER_FBFMT_NV12, + .rgb = false, + .csc = SUN8I_CSC_MODE_YUV2RGB, + }, + { + .drm_fmt = DRM_FORMAT_NV21, + .de2_fmt = SUN8I_MIXER_FBFMT_NV21, + .rgb = false, + .csc = SUN8I_CSC_MODE_YUV2RGB, + }, + { + .drm_fmt = DRM_FORMAT_YUV444, + .de2_fmt = SUN8I_MIXER_FBFMT_RGB888, + .rgb = true, + .csc = SUN8I_CSC_MODE_YUV2RGB, + }, + { + .drm_fmt = DRM_FORMAT_YUV422, + .de2_fmt = SUN8I_MIXER_FBFMT_YUV422, + .rgb = false, + .csc = SUN8I_CSC_MODE_YUV2RGB, + }, + { + .drm_fmt = DRM_FORMAT_YUV420, + .de2_fmt = SUN8I_MIXER_FBFMT_YUV420, + .rgb = false, + .csc = SUN8I_CSC_MODE_YUV2RGB, + }, + { + .drm_fmt = DRM_FORMAT_YUV411, + .de2_fmt = SUN8I_MIXER_FBFMT_YUV411, + .rgb = false, + .csc = SUN8I_CSC_MODE_YUV2RGB, + }, + { + .drm_fmt = DRM_FORMAT_YVU444, + .de2_fmt = SUN8I_MIXER_FBFMT_RGB888, + .rgb = true, + .csc = SUN8I_CSC_MODE_YVU2RGB, + }, + { + .drm_fmt = DRM_FORMAT_YVU422, + .de2_fmt = SUN8I_MIXER_FBFMT_YUV422, + .rgb = false, + .csc = SUN8I_CSC_MODE_YVU2RGB, + }, + { + .drm_fmt = DRM_FORMAT_YVU420, + .de2_fmt = SUN8I_MIXER_FBFMT_YUV420, + .rgb = false, + .csc = SUN8I_CSC_MODE_YVU2RGB, + }, + { + .drm_fmt = DRM_FORMAT_YVU411, + .de2_fmt = SUN8I_MIXER_FBFMT_YUV411, + .rgb = false, + .csc = SUN8I_CSC_MODE_YVU2RGB, + }, +}; -int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer, - int layer, struct drm_plane *plane) +const struct de2_fmt_info *sun8i_mixer_format_info(u32 format) { - struct drm_plane_state *state = plane->state; - struct drm_framebuffer *fb = state->fb; - /* Currently the first UI channel is used */ - int chan = mixer->cfg->vi_num; - - DRM_DEBUG_DRIVER("Updating layer %d\n", layer); - - if (plane->type == DRM_PLANE_TYPE_PRIMARY) { - DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n", - state->crtc_w, state->crtc_h); - regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_SIZE, - SUN8I_MIXER_SIZE(state->crtc_w, - state->crtc_h)); - DRM_DEBUG_DRIVER("Updating blender size\n"); - regmap_write(mixer->engine.regs, - SUN8I_MIXER_BLEND_ATTR_INSIZE(0), - SUN8I_MIXER_SIZE(state->crtc_w, - state->crtc_h)); - regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTSIZE, - SUN8I_MIXER_SIZE(state->crtc_w, - state->crtc_h)); - DRM_DEBUG_DRIVER("Updating channel size\n"); - regmap_write(mixer->engine.regs, - SUN8I_MIXER_CHAN_UI_OVL_SIZE(chan), - SUN8I_MIXER_SIZE(state->crtc_w, - state->crtc_h)); - } + unsigned int i; - /* Set the line width */ - DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]); - regmap_write(mixer->engine.regs, - SUN8I_MIXER_CHAN_UI_LAYER_PITCH(chan, layer), - fb->pitches[0]); - - /* Set height and width */ - DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n", - state->crtc_w, state->crtc_h); - regmap_write(mixer->engine.regs, - SUN8I_MIXER_CHAN_UI_LAYER_SIZE(chan, layer), - SUN8I_MIXER_SIZE(state->crtc_w, state->crtc_h)); - - /* Set base coordinates */ - DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n", - state->crtc_x, state->crtc_y); - regmap_write(mixer->engine.regs, - SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer), - SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y)); + for (i = 0; i < ARRAY_SIZE(de2_formats); ++i) + if (de2_formats[i].drm_fmt == format) + return &de2_formats[i]; - return 0; + return NULL; } -int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer, - int layer, struct drm_plane *plane) +static void sun8i_mixer_commit(struct sunxi_engine *engine) { - struct drm_plane_state *state = plane->state; - struct drm_framebuffer *fb = state->fb; - bool interlaced = false; - u32 val; - /* Currently the first UI channel is used */ - int chan = mixer->cfg->vi_num; - int ret; - - if (plane->state->crtc) - interlaced = plane->state->crtc->state->adjusted_mode.flags - & DRM_MODE_FLAG_INTERLACE; - - regmap_update_bits(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTCTL, - SUN8I_MIXER_BLEND_OUTCTL_INTERLACED, - interlaced ? - SUN8I_MIXER_BLEND_OUTCTL_INTERLACED : 0); - - DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n", - interlaced ? "on" : "off"); - - ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format, - &val); - if (ret) { - DRM_DEBUG_DRIVER("Invalid format\n"); - return ret; - } - - regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer), - SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val); + DRM_DEBUG_DRIVER("Committing changes\n"); - return 0; + regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF, + SUN8I_MIXER_GLOBAL_DBUFF_ENABLE); } -int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer, - int layer, struct drm_plane *plane) +static struct drm_plane **sun8i_layers_init(struct drm_device *drm, + struct sunxi_engine *engine) { - struct drm_plane_state *state = plane->state; - struct drm_framebuffer *fb = state->fb; - struct drm_gem_cma_object *gem; - dma_addr_t paddr; - /* Currently the first UI channel is used */ - int chan = mixer->cfg->vi_num; - int bpp; - - /* Get the physical address of the buffer in memory */ - gem = drm_fb_cma_get_gem_obj(fb, 0); - - DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr); - - /* Compute the start of the displayed memory */ - bpp = fb->format->cpp[0]; - paddr = gem->paddr + fb->offsets[0]; - - /* Fixup framebuffer address for src coordinates */ - paddr += (state->src_x >> 16) * bpp; - paddr += (state->src_y >> 16) * fb->pitches[0]; - - /* - * The hardware cannot correctly deal with negative crtc - * coordinates, the display is cropped to the requested size, - * but the display content is not moved. - * Manually move the display content by fixup the framebuffer - * address when crtc_x or crtc_y is negative, like what we - * have did for src_x and src_y. - */ - if (state->crtc_x < 0) - paddr += -state->crtc_x * bpp; - if (state->crtc_y < 0) - paddr += -state->crtc_y * fb->pitches[0]; - - DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr); - - regmap_write(mixer->engine.regs, - SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer), - lower_32_bits(paddr)); - - return 0; + struct drm_plane **planes; + struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine); + int i; + + planes = devm_kcalloc(drm->dev, + mixer->cfg->vi_num + mixer->cfg->ui_num + 1, + sizeof(*planes), GFP_KERNEL); + if (!planes) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < mixer->cfg->vi_num; i++) { + struct sun8i_vi_layer *layer; + + layer = sun8i_vi_layer_init_one(drm, mixer, i); + if (IS_ERR(layer)) { + dev_err(drm->dev, + "Couldn't initialize overlay plane\n"); + return ERR_CAST(layer); + }; + + planes[i] = &layer->plane; + }; + + for (i = 0; i < mixer->cfg->ui_num; i++) { + struct sun8i_ui_layer *layer; + + layer = sun8i_ui_layer_init_one(drm, mixer, i); + if (IS_ERR(layer)) { + dev_err(drm->dev, "Couldn't initialize %s plane\n", + i ? "overlay" : "primary"); + return ERR_CAST(layer); + }; + + planes[mixer->cfg->vi_num + i] = &layer->plane; + }; + + return planes; } static const struct sunxi_engine_ops sun8i_engine_ops = { @@ -247,6 +331,7 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, struct sun8i_mixer *mixer; struct resource *res; void __iomem *regs; + int plane_cnt; int i, ret; /* @@ -325,27 +410,26 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_CTL, SUN8I_MIXER_GLOBAL_CTL_RT_EN); - /* Initialize blender */ - regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_FCOLOR_CTL, - SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF); - regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PREMULTIPLY, - SUN8I_MIXER_BLEND_PREMULTIPLY_DEF); + /* Set background color to black */ regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR, - SUN8I_MIXER_BLEND_BKCOLOR_DEF); - regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_MODE(0), - SUN8I_MIXER_BLEND_MODE_DEF); - regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_CK_CTL, - SUN8I_MIXER_BLEND_CK_CTL_DEF); - - regmap_write(mixer->engine.regs, - SUN8I_MIXER_BLEND_ATTR_FCOLOR(0), - SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF); - - /* Select the first UI channel */ - DRM_DEBUG_DRIVER("Selecting channel %d (first UI channel)\n", - mixer->cfg->vi_num); - regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ROUTE, - mixer->cfg->vi_num); + SUN8I_MIXER_BLEND_COLOR_BLACK); + + /* + * Set fill color of bottom plane to black. Generally not needed + * except when VI plane is at bottom (zpos = 0) and enabled. + */ + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL, + SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0)); + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ATTR_FCOLOR(0), + SUN8I_MIXER_BLEND_COLOR_BLACK); + + /* Fixed zpos for now */ + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ROUTE, 0x43210); + + plane_cnt = mixer->cfg->vi_num + mixer->cfg->ui_num; + for (i = 0; i < plane_cnt; i++) + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_MODE(i), + SUN8I_MIXER_BLEND_MODE_DEF); return 0; @@ -388,6 +472,8 @@ static int sun8i_mixer_remove(struct platform_device *pdev) static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = { .vi_num = 2, .ui_num = 1, + .scaler_mask = 0x3, + .ccsc = 0, }; static const struct of_device_id sun8i_mixer_of_table[] = { diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h index 4785ac090b8c..bc58040a88f9 100644 --- a/drivers/gpu/drm/sun4i/sun8i_mixer.h +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h @@ -14,10 +14,9 @@ #include <linux/regmap.h> #include <linux/reset.h> +#include "sun8i_csc.h" #include "sunxi_engine.h" -#define SUN8I_MIXER_MAX_CHAN_COUNT 4 - #define SUN8I_MIXER_SIZE(w, h) (((h) - 1) << 16 | ((w) - 1)) #define SUN8I_MIXER_COORD(x, y) ((y) << 16 | (x)) @@ -26,14 +25,14 @@ #define SUN8I_MIXER_GLOBAL_DBUFF 0x8 #define SUN8I_MIXER_GLOBAL_SIZE 0xc -#define SUN8I_MIXER_GLOBAL_CTL_RT_EN 0x1 +#define SUN8I_MIXER_GLOBAL_CTL_RT_EN BIT(0) -#define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE 0x1 +#define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE BIT(0) -#define SUN8I_MIXER_BLEND_FCOLOR_CTL 0x1000 +#define SUN8I_MIXER_BLEND_PIPE_CTL 0x1000 #define SUN8I_MIXER_BLEND_ATTR_FCOLOR(x) (0x1004 + 0x10 * (x) + 0x0) #define SUN8I_MIXER_BLEND_ATTR_INSIZE(x) (0x1004 + 0x10 * (x) + 0x4) -#define SUN8I_MIXER_BLEND_ATTR_OFFSET(x) (0x1004 + 0x10 * (x) + 0x8) +#define SUN8I_MIXER_BLEND_ATTR_COORD(x) (0x1004 + 0x10 * (x) + 0x8) #define SUN8I_MIXER_BLEND_ROUTE 0x1080 #define SUN8I_MIXER_BLEND_PREMULTIPLY 0x1084 #define SUN8I_MIXER_BLEND_BKCOLOR 0x1088 @@ -45,57 +44,56 @@ #define SUN8I_MIXER_BLEND_CK_MIN(x) (0x10e0 + 0x04 * (x)) #define SUN8I_MIXER_BLEND_OUTCTL 0x10fc +#define SUN8I_MIXER_BLEND_PIPE_CTL_EN(pipe) BIT(8 + pipe) +#define SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(pipe) BIT(pipe) +/* colors are always in AARRGGBB format */ +#define SUN8I_MIXER_BLEND_COLOR_BLACK 0xff000000 /* The following numbers are some still unknown magic numbers */ -#define SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF 0xff000000 -#define SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF 0x00000101 -#define SUN8I_MIXER_BLEND_PREMULTIPLY_DEF 0x0 -#define SUN8I_MIXER_BLEND_BKCOLOR_DEF 0xff000000 #define SUN8I_MIXER_BLEND_MODE_DEF 0x03010301 -#define SUN8I_MIXER_BLEND_CK_CTL_DEF 0x0 #define SUN8I_MIXER_BLEND_OUTCTL_INTERLACED BIT(1) -/* - * VI channels are not used now, but the support of them may be introduced in - * the future. - */ - -#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch, layer) \ - (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x0) -#define SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch, layer) \ - (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x4) -#define SUN8I_MIXER_CHAN_UI_LAYER_COORD(ch, layer) \ - (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x8) -#define SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch, layer) \ - (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0xc) -#define SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch, layer) \ - (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x10) -#define SUN8I_MIXER_CHAN_UI_LAYER_BOT_LADDR(ch, layer) \ - (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x14) -#define SUN8I_MIXER_CHAN_UI_LAYER_FCOLOR(ch, layer) \ - (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x18) -#define SUN8I_MIXER_CHAN_UI_TOP_HADDR(ch) (0x2000 + 0x1000 * (ch) + 0x80) -#define SUN8I_MIXER_CHAN_UI_BOT_HADDR(ch) (0x2000 + 0x1000 * (ch) + 0x84) -#define SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch) (0x2000 + 0x1000 * (ch) + 0x88) - -#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN BIT(0) -#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK GENMASK(2, 1) -#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK GENMASK(11, 8) -#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK GENMASK(31, 24) -#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF (1 << 1) -#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888 (0 << 8) -#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888 (4 << 8) -#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888 (8 << 8) -#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF (0xff << 24) +#define SUN8I_MIXER_FBFMT_ARGB8888 0 +#define SUN8I_MIXER_FBFMT_ABGR8888 1 +#define SUN8I_MIXER_FBFMT_RGBA8888 2 +#define SUN8I_MIXER_FBFMT_BGRA8888 3 +#define SUN8I_MIXER_FBFMT_XRGB8888 4 +#define SUN8I_MIXER_FBFMT_XBGR8888 5 +#define SUN8I_MIXER_FBFMT_RGBX8888 6 +#define SUN8I_MIXER_FBFMT_BGRX8888 7 +#define SUN8I_MIXER_FBFMT_RGB888 8 +#define SUN8I_MIXER_FBFMT_BGR888 9 +#define SUN8I_MIXER_FBFMT_RGB565 10 +#define SUN8I_MIXER_FBFMT_BGR565 11 +#define SUN8I_MIXER_FBFMT_ARGB4444 12 +#define SUN8I_MIXER_FBFMT_ABGR4444 13 +#define SUN8I_MIXER_FBFMT_RGBA4444 14 +#define SUN8I_MIXER_FBFMT_BGRA4444 15 +#define SUN8I_MIXER_FBFMT_ARGB1555 16 +#define SUN8I_MIXER_FBFMT_ABGR1555 17 +#define SUN8I_MIXER_FBFMT_RGBA5551 18 +#define SUN8I_MIXER_FBFMT_BGRA5551 19 + +#define SUN8I_MIXER_FBFMT_YUYV 0 +#define SUN8I_MIXER_FBFMT_UYVY 1 +#define SUN8I_MIXER_FBFMT_YVYU 2 +#define SUN8I_MIXER_FBFMT_VYUY 3 +#define SUN8I_MIXER_FBFMT_NV16 4 +#define SUN8I_MIXER_FBFMT_NV61 5 +#define SUN8I_MIXER_FBFMT_YUV422 6 +/* format 7 doesn't exist */ +#define SUN8I_MIXER_FBFMT_NV12 8 +#define SUN8I_MIXER_FBFMT_NV21 9 +#define SUN8I_MIXER_FBFMT_YUV420 10 +/* format 11 doesn't exist */ +/* format 12 is semi-planar YUV411 UVUV */ +/* format 13 is semi-planar YUV411 VUVU */ +#define SUN8I_MIXER_FBFMT_YUV411 14 /* * These sub-engines are still unknown now, the EN registers are here only to * be used to disable these sub-engines. */ -#define SUN8I_MIXER_VSU_EN 0x20000 -#define SUN8I_MIXER_GSU1_EN 0x30000 -#define SUN8I_MIXER_GSU2_EN 0x40000 -#define SUN8I_MIXER_GSU3_EN 0x50000 #define SUN8I_MIXER_FCE_EN 0xa0000 #define SUN8I_MIXER_BWS_EN 0xa2000 #define SUN8I_MIXER_LTI_EN 0xa4000 @@ -104,9 +102,31 @@ #define SUN8I_MIXER_FCC_EN 0xaa000 #define SUN8I_MIXER_DCSC_EN 0xb0000 +struct de2_fmt_info { + u32 drm_fmt; + u32 de2_fmt; + bool rgb; + enum sun8i_csc_mode csc; +}; + +/** + * struct sun8i_mixer_cfg - mixer HW configuration + * @vi_num: number of VI channels + * @ui_num: number of UI channels + * @scaler_mask: bitmask which tells which channel supports scaling + * First, scaler supports for VI channels is defined and after that, scaler + * support for UI channels. For example, if mixer has 2 VI channels without + * scaler and 2 UI channels with scaler, bitmask would be 0xC. + * @ccsc: select set of CCSC base addresses + * Set value to 0 if this is first mixer or second mixer with VEP support. + * Set value to 1 if this is second mixer without VEP support. Other values + * are invalid. + */ struct sun8i_mixer_cfg { int vi_num; int ui_num; + int scaler_mask; + int ccsc; }; struct sun8i_mixer { @@ -126,12 +146,5 @@ engine_to_sun8i_mixer(struct sunxi_engine *engine) return container_of(engine, struct sun8i_mixer, engine); } -void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer, - int layer, bool enable); -int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer, - int layer, struct drm_plane *plane); -int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer, - int layer, struct drm_plane *plane); -int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer, - int layer, struct drm_plane *plane); +const struct de2_fmt_info *sun8i_mixer_format_info(u32 format); #endif /* _SUN8I_MIXER_H_ */ diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c new file mode 100644 index 000000000000..28d7c48d50fe --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c @@ -0,0 +1,349 @@ +/* + * Copyright (C) Icenowy Zheng <icenowy@aosc.io> + * + * Based on sun4i_layer.h, which is: + * Copyright (C) 2015 Free Electrons + * Copyright (C) 2015 NextThing Co + * + * Maxime Ripard <maxime.ripard@free-electrons.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_plane_helper.h> +#include <drm/drmP.h> + +#include "sun8i_ui_layer.h" +#include "sun8i_mixer.h" +#include "sun8i_ui_scaler.h" + +static void sun8i_ui_layer_enable(struct sun8i_mixer *mixer, int channel, + int overlay, bool enable) +{ + u32 val; + + DRM_DEBUG_DRIVER("%sabling channel %d overlay %d\n", + enable ? "En" : "Dis", channel, overlay); + + if (enable) + val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN; + else + val = 0; + + regmap_update_bits(mixer->engine.regs, + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(channel, overlay), + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val); + + if (enable) + val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel); + else + val = 0; + + regmap_update_bits(mixer->engine.regs, + SUN8I_MIXER_BLEND_PIPE_CTL, + SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel), val); +} + +static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel, + int overlay, struct drm_plane *plane) +{ + struct drm_plane_state *state = plane->state; + u32 src_w, src_h, dst_w, dst_h; + u32 outsize, insize; + u32 hphase, vphase; + + DRM_DEBUG_DRIVER("Updating UI channel %d overlay %d\n", + channel, overlay); + + src_w = drm_rect_width(&state->src) >> 16; + src_h = drm_rect_height(&state->src) >> 16; + dst_w = drm_rect_width(&state->dst); + dst_h = drm_rect_height(&state->dst); + + hphase = state->src.x1 & 0xffff; + vphase = state->src.y1 & 0xffff; + + insize = SUN8I_MIXER_SIZE(src_w, src_h); + outsize = SUN8I_MIXER_SIZE(dst_w, dst_h); + + if (plane->type == DRM_PLANE_TYPE_PRIMARY) { + bool interlaced = false; + u32 val; + + DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n", + dst_w, dst_h); + regmap_write(mixer->engine.regs, + SUN8I_MIXER_GLOBAL_SIZE, + outsize); + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTSIZE, + outsize); + + if (state->crtc) + interlaced = state->crtc->state->adjusted_mode.flags + & DRM_MODE_FLAG_INTERLACE; + + if (interlaced) + val = SUN8I_MIXER_BLEND_OUTCTL_INTERLACED; + else + val = 0; + + regmap_update_bits(mixer->engine.regs, + SUN8I_MIXER_BLEND_OUTCTL, + SUN8I_MIXER_BLEND_OUTCTL_INTERLACED, + val); + + DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n", + interlaced ? "on" : "off"); + } + + /* Set height and width */ + DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n", + state->src.x1 >> 16, state->src.y1 >> 16); + DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h); + regmap_write(mixer->engine.regs, + SUN8I_MIXER_CHAN_UI_LAYER_SIZE(channel, overlay), + insize); + regmap_write(mixer->engine.regs, + SUN8I_MIXER_CHAN_UI_OVL_SIZE(channel), + insize); + + if (insize != outsize || hphase || vphase) { + u32 hscale, vscale; + + DRM_DEBUG_DRIVER("HW scaling is enabled\n"); + + hscale = state->src_w / state->crtc_w; + vscale = state->src_h / state->crtc_h; + + sun8i_ui_scaler_setup(mixer, channel, src_w, src_h, dst_w, + dst_h, hscale, vscale, hphase, vphase); + sun8i_ui_scaler_enable(mixer, channel, true); + } else { + DRM_DEBUG_DRIVER("HW scaling is not needed\n"); + sun8i_ui_scaler_enable(mixer, channel, false); + } + + /* Set base coordinates */ + DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n", + state->dst.x1, state->dst.y1); + DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h); + regmap_write(mixer->engine.regs, + SUN8I_MIXER_BLEND_ATTR_COORD(channel), + SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1)); + regmap_write(mixer->engine.regs, + SUN8I_MIXER_BLEND_ATTR_INSIZE(channel), + outsize); + + return 0; +} + +static int sun8i_ui_layer_update_formats(struct sun8i_mixer *mixer, int channel, + int overlay, struct drm_plane *plane) +{ + struct drm_plane_state *state = plane->state; + const struct de2_fmt_info *fmt_info; + u32 val; + + fmt_info = sun8i_mixer_format_info(state->fb->format->format); + if (!fmt_info || !fmt_info->rgb) { + DRM_DEBUG_DRIVER("Invalid format\n"); + return -EINVAL; + } + + val = fmt_info->de2_fmt << SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_OFFSET; + regmap_update_bits(mixer->engine.regs, + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(channel, overlay), + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val); + + return 0; +} + +static int sun8i_ui_layer_update_buffer(struct sun8i_mixer *mixer, int channel, + int overlay, struct drm_plane *plane) +{ + struct drm_plane_state *state = plane->state; + struct drm_framebuffer *fb = state->fb; + struct drm_gem_cma_object *gem; + dma_addr_t paddr; + int bpp; + + /* Get the physical address of the buffer in memory */ + gem = drm_fb_cma_get_gem_obj(fb, 0); + + DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr); + + /* Compute the start of the displayed memory */ + bpp = fb->format->cpp[0]; + paddr = gem->paddr + fb->offsets[0]; + + /* Fixup framebuffer address for src coordinates */ + paddr += (state->src.x1 >> 16) * bpp; + paddr += (state->src.y1 >> 16) * fb->pitches[0]; + + /* Set the line width */ + DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]); + regmap_write(mixer->engine.regs, + SUN8I_MIXER_CHAN_UI_LAYER_PITCH(channel, overlay), + fb->pitches[0]); + + DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr); + + regmap_write(mixer->engine.regs, + SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(channel, overlay), + lower_32_bits(paddr)); + + return 0; +} + +static int sun8i_ui_layer_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane); + struct drm_crtc *crtc = state->crtc; + struct drm_crtc_state *crtc_state; + int min_scale, max_scale; + struct drm_rect clip; + + if (!crtc) + return 0; + + crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); + if (WARN_ON(!crtc_state)) + return -EINVAL; + + clip.x1 = 0; + clip.y1 = 0; + clip.x2 = crtc_state->adjusted_mode.hdisplay; + clip.y2 = crtc_state->adjusted_mode.vdisplay; + + min_scale = DRM_PLANE_HELPER_NO_SCALING; + max_scale = DRM_PLANE_HELPER_NO_SCALING; + + if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) { + min_scale = SUN8I_UI_SCALER_SCALE_MIN; + max_scale = SUN8I_UI_SCALER_SCALE_MAX; + } + + return drm_atomic_helper_check_plane_state(state, crtc_state, &clip, + min_scale, max_scale, + true, true); +} + +static void sun8i_ui_layer_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane); + struct sun8i_mixer *mixer = layer->mixer; + + sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay, false); +} + +static void sun8i_ui_layer_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane); + struct sun8i_mixer *mixer = layer->mixer; + + if (!plane->state->visible) { + sun8i_ui_layer_enable(mixer, layer->channel, + layer->overlay, false); + return; + } + + sun8i_ui_layer_update_coord(mixer, layer->channel, + layer->overlay, plane); + sun8i_ui_layer_update_formats(mixer, layer->channel, + layer->overlay, plane); + sun8i_ui_layer_update_buffer(mixer, layer->channel, + layer->overlay, plane); + sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay, true); +} + +static struct drm_plane_helper_funcs sun8i_ui_layer_helper_funcs = { + .atomic_check = sun8i_ui_layer_atomic_check, + .atomic_disable = sun8i_ui_layer_atomic_disable, + .atomic_update = sun8i_ui_layer_atomic_update, +}; + +static const struct drm_plane_funcs sun8i_ui_layer_funcs = { + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .destroy = drm_plane_cleanup, + .disable_plane = drm_atomic_helper_disable_plane, + .reset = drm_atomic_helper_plane_reset, + .update_plane = drm_atomic_helper_update_plane, +}; + +static const u32 sun8i_ui_layer_formats[] = { + DRM_FORMAT_ABGR1555, + DRM_FORMAT_ABGR4444, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_ARGB4444, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_BGR565, + DRM_FORMAT_BGR888, + DRM_FORMAT_BGRA5551, + DRM_FORMAT_BGRA4444, + DRM_FORMAT_BGRA8888, + DRM_FORMAT_BGRX8888, + DRM_FORMAT_RGB565, + DRM_FORMAT_RGB888, + DRM_FORMAT_RGBA4444, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_RGBX8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_XRGB8888, +}; + +struct sun8i_ui_layer *sun8i_ui_layer_init_one(struct drm_device *drm, + struct sun8i_mixer *mixer, + int index) +{ + enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY; + int channel = mixer->cfg->vi_num + index; + struct sun8i_ui_layer *layer; + int ret; + + layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL); + if (!layer) + return ERR_PTR(-ENOMEM); + + if (index == 0) + type = DRM_PLANE_TYPE_PRIMARY; + + /* possible crtcs are set later */ + ret = drm_universal_plane_init(drm, &layer->plane, 0, + &sun8i_ui_layer_funcs, + sun8i_ui_layer_formats, + ARRAY_SIZE(sun8i_ui_layer_formats), + NULL, type, NULL); + if (ret) { + dev_err(drm->dev, "Couldn't initialize layer\n"); + return ERR_PTR(ret); + } + + /* fixed zpos for now */ + ret = drm_plane_create_zpos_immutable_property(&layer->plane, channel); + if (ret) { + dev_err(drm->dev, "Couldn't add zpos property\n"); + return ERR_PTR(ret); + } + + drm_plane_helper_add(&layer->plane, &sun8i_ui_layer_helper_funcs); + layer->mixer = mixer; + layer->channel = channel; + layer->overlay = 0; + + return layer; +} diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.h b/drivers/gpu/drm/sun4i/sun8i_ui_layer.h new file mode 100644 index 000000000000..123b15ea9918 --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) Icenowy Zheng <icenowy@aosc.io> + * + * Based on sun4i_layer.h, which is: + * Copyright (C) 2015 Free Electrons + * Copyright (C) 2015 NextThing Co + * + * Maxime Ripard <maxime.ripard@free-electrons.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#ifndef _SUN8I_UI_LAYER_H_ +#define _SUN8I_UI_LAYER_H_ + +#include <drm/drm_plane.h> + +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch, layer) \ + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x0) +#define SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch, layer) \ + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x4) +#define SUN8I_MIXER_CHAN_UI_LAYER_COORD(ch, layer) \ + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x8) +#define SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch, layer) \ + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0xc) +#define SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch, layer) \ + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x10) +#define SUN8I_MIXER_CHAN_UI_LAYER_BOT_LADDR(ch, layer) \ + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x14) +#define SUN8I_MIXER_CHAN_UI_LAYER_FCOLOR(ch, layer) \ + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x18) +#define SUN8I_MIXER_CHAN_UI_TOP_HADDR(ch) (0x2000 + 0x1000 * (ch) + 0x80) +#define SUN8I_MIXER_CHAN_UI_BOT_HADDR(ch) (0x2000 + 0x1000 * (ch) + 0x84) +#define SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch) (0x2000 + 0x1000 * (ch) + 0x88) + +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN BIT(0) +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK GENMASK(2, 1) +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK GENMASK(12, 8) +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_OFFSET 8 +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK GENMASK(31, 24) + +struct sun8i_mixer; + +struct sun8i_ui_layer { + struct drm_plane plane; + struct sun8i_mixer *mixer; + int channel; + int overlay; +}; + +static inline struct sun8i_ui_layer * +plane_to_sun8i_ui_layer(struct drm_plane *plane) +{ + return container_of(plane, struct sun8i_ui_layer, plane); +} + +struct sun8i_ui_layer *sun8i_ui_layer_init_one(struct drm_device *drm, + struct sun8i_mixer *mixer, + int index); +#endif /* _SUN8I_UI_LAYER_H_ */ diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_scaler.c b/drivers/gpu/drm/sun4i/sun8i_ui_scaler.c new file mode 100644 index 000000000000..6bb2aa164c8e --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun8i_ui_scaler.c @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2017 Jernej Skrabec <jernej.skrabec@siol.net> + * + * Coefficients are taken from BSP driver, which is: + * Copyright (C) 2014-2015 Allwinner + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include "sun8i_ui_scaler.h" + +static const u32 lan2coefftab16[240] = { + 0x00004000, 0x00033ffe, 0x00063efc, 0x000a3bfb, + 0xff0f37fb, 0xfe1433fb, 0xfd192ffb, 0xfd1f29fb, + 0xfc2424fc, 0xfb291ffd, 0xfb2f19fd, 0xfb3314fe, + 0xfb370fff, 0xfb3b0a00, 0xfc3e0600, 0xfe3f0300, + + 0xff053804, 0xff083801, 0xff0a3700, 0xff0e34ff, + 0xff1232fd, 0xfe162ffd, 0xfd1b2cfc, 0xfd1f28fc, + 0xfd2323fd, 0xfc281ffd, 0xfc2c1bfd, 0xfd2f16fe, + 0xfd3212ff, 0xff340eff, 0x00360a00, 0x02370700, + + 0xff083207, 0xff0a3205, 0xff0d3103, 0xfe113001, + 0xfe142e00, 0xfe182bff, 0xfe1b29fe, 0xfe1f25fe, + 0xfe2222fe, 0xfe251ffe, 0xfe291bfe, 0xff2b18fe, + 0x002e14fe, 0x013010ff, 0x03310dff, 0x05310a00, + + 0xff0a2e09, 0xff0c2e07, 0xff0f2d05, 0xff122c03, + 0xfe152b02, 0xfe182901, 0xfe1b2700, 0xff1e24ff, + 0xff2121ff, 0xff241eff, 0x00261bff, 0x012818ff, + 0x022a15ff, 0x032c12ff, 0x052d0fff, 0x072d0c00, + + 0xff0c2a0b, 0xff0e2a09, 0xff102a07, 0xff132905, + 0xff162803, 0xff182702, 0xff1b2501, 0xff1e2300, + 0x00202000, 0x01221d00, 0x01251bff, 0x032618ff, + 0x042815ff, 0x052913ff, 0x072a10ff, 0x092a0d00, + + 0xff0d280c, 0xff0f280a, 0xff112808, 0xff142706, + 0xff162605, 0xff192503, 0x001b2302, 0x001d2201, + 0x011f1f01, 0x01221d00, 0x02231b00, 0x04241800, + 0x052616ff, 0x072713ff, 0x08271100, 0x0a280e00, + + 0xff0e260d, 0xff10260b, 0xff122609, 0xff142508, + 0x00152506, 0x00182305, 0x001b2203, 0x011d2002, + 0x011f1f01, 0x02201d01, 0x03221b00, 0x04231801, + 0x06241600, 0x08251300, 0x09261100, 0x0b260f00, + + 0xff0e250e, 0xff10250c, 0x0011250a, 0x00142408, + 0x00162307, 0x00182206, 0x011a2104, 0x011c2003, + 0x021e1e02, 0x03201c01, 0x04211a01, 0x05221801, + 0x07231600, 0x08241400, 0x0a241200, 0x0c241000, + + 0x000e240e, 0x0010240c, 0x0013230a, 0x00142309, + 0x00162208, 0x01182106, 0x011a2005, 0x021b1f04, + 0x031d1d03, 0x041e1c02, 0x05201a01, 0x06211801, + 0x07221601, 0x09231400, 0x0a231300, 0x0c231100, + + 0x000f220f, 0x0011220d, 0x0013220b, 0x0015210a, + 0x01162108, 0x01182007, 0x02191f06, 0x031a1e05, + 0x041c1c04, 0x051d1b03, 0x061f1902, 0x07201801, + 0x08211601, 0x0a211500, 0x0b221300, 0x0d221100, + + 0x0010210f, 0x0011210e, 0x0013210c, 0x0114200b, + 0x01161f0a, 0x02171f08, 0x03181e07, 0x031a1d06, + 0x041c1c04, 0x051d1a04, 0x071d1903, 0x081e1802, + 0x091f1602, 0x0b1f1501, 0x0c211300, 0x0e201200, + + 0x00102010, 0x0012200e, 0x0013200d, 0x01151f0b, + 0x01161f0a, 0x02171e09, 0x03191d07, 0x041a1c06, + 0x051b1b05, 0x061c1a04, 0x071d1903, 0x081e1703, + 0x0a1f1601, 0x0b1f1501, 0x0d201300, 0x0e201200, + + 0x00102010, 0x00121f0f, 0x00141f0d, 0x01141f0c, + 0x02161e0a, 0x03171d09, 0x03181d08, 0x041a1c06, + 0x051b1b05, 0x061c1a04, 0x081c1903, 0x091d1703, + 0x0a1e1602, 0x0c1e1501, 0x0d1f1400, 0x0e1f1201, + + 0x00111e11, 0x00131e0f, 0x01131e0e, 0x02151d0c, + 0x02161d0b, 0x03171c0a, 0x04181b09, 0x05191b07, + 0x061a1a06, 0x071b1905, 0x091b1804, 0x0a1c1703, + 0x0b1d1602, 0x0c1d1502, 0x0e1d1401, 0x0f1e1300, + + 0x00111e11, 0x00131d10, 0x01141d0e, 0x02151c0d, + 0x03161c0b, 0x04171b0a, 0x05171b09, 0x06181a08, + 0x07191907, 0x081a1806, 0x091a1805, 0x0a1b1704, + 0x0b1c1603, 0x0d1c1502, 0x0e1d1401, 0x0f1d1301, +}; + +static int sun8i_ui_scaler_coef_index(unsigned int step) +{ + unsigned int scale, int_part, float_part; + + scale = step >> (SUN8I_UI_SCALER_SCALE_FRAC - 3); + int_part = scale >> 3; + float_part = scale & 0x7; + + switch (int_part) { + case 0: + return 0; + case 1: + return float_part; + case 2: + return 8 + (float_part >> 1); + case 3: + return 12; + case 4: + return 13; + default: + return 14; + } +} + +void sun8i_ui_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable) +{ + int vi_cnt = mixer->cfg->vi_num; + u32 val; + + if (WARN_ON(layer < vi_cnt)) + return; + + if (enable) + val = SUN8I_SCALER_GSU_CTRL_EN | + SUN8I_SCALER_GSU_CTRL_COEFF_RDY; + else + val = 0; + + regmap_write(mixer->engine.regs, + SUN8I_SCALER_GSU_CTRL(vi_cnt, layer - vi_cnt), val); +} + +void sun8i_ui_scaler_setup(struct sun8i_mixer *mixer, int layer, + u32 src_w, u32 src_h, u32 dst_w, u32 dst_h, + u32 hscale, u32 vscale, u32 hphase, u32 vphase) +{ + int vi_cnt = mixer->cfg->vi_num; + u32 insize, outsize; + int i, offset; + + if (WARN_ON(layer < vi_cnt)) + return; + + hphase <<= SUN8I_UI_SCALER_PHASE_FRAC - 16; + vphase <<= SUN8I_UI_SCALER_PHASE_FRAC - 16; + hscale <<= SUN8I_UI_SCALER_SCALE_FRAC - 16; + vscale <<= SUN8I_UI_SCALER_SCALE_FRAC - 16; + + insize = SUN8I_UI_SCALER_SIZE(src_w, src_h); + outsize = SUN8I_UI_SCALER_SIZE(dst_w, dst_h); + + layer -= vi_cnt; + + regmap_write(mixer->engine.regs, + SUN8I_SCALER_GSU_OUTSIZE(vi_cnt, layer), outsize); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_GSU_INSIZE(vi_cnt, layer), insize); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_GSU_HSTEP(vi_cnt, layer), hscale); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_GSU_VSTEP(vi_cnt, layer), vscale); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_GSU_HPHASE(vi_cnt, layer), hphase); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_GSU_VPHASE(vi_cnt, layer), vphase); + offset = sun8i_ui_scaler_coef_index(hscale) * + SUN8I_UI_SCALER_COEFF_COUNT; + for (i = 0; i < SUN8I_UI_SCALER_COEFF_COUNT; i++) + regmap_write(mixer->engine.regs, + SUN8I_SCALER_GSU_HCOEFF(vi_cnt, layer, i), + lan2coefftab16[offset + i]); +} diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_scaler.h b/drivers/gpu/drm/sun4i/sun8i_ui_scaler.h new file mode 100644 index 000000000000..86295be8be78 --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun8i_ui_scaler.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2017 Jernej Skrabec <jernej.skrabec@siol.net> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef _SUN8I_UI_SCALER_H_ +#define _SUN8I_UI_SCALER_H_ + +#include "sun8i_mixer.h" + +/* this two macros assumes 16 fractional bits which is standard in DRM */ +#define SUN8I_UI_SCALER_SCALE_MIN 1 +#define SUN8I_UI_SCALER_SCALE_MAX ((1UL << 20) - 1) + +#define SUN8I_UI_SCALER_SCALE_FRAC 20 +#define SUN8I_UI_SCALER_PHASE_FRAC 20 +#define SUN8I_UI_SCALER_COEFF_COUNT 16 +#define SUN8I_UI_SCALER_SIZE(w, h) (((h) - 1) << 16 | ((w) - 1)) + +#define SUN8I_SCALER_GSU_CTRL(vi_cnt, ui_idx) \ + (0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x0) +#define SUN8I_SCALER_GSU_OUTSIZE(vi_cnt, ui_idx) \ + (0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x40) +#define SUN8I_SCALER_GSU_INSIZE(vi_cnt, ui_idx) \ + (0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x80) +#define SUN8I_SCALER_GSU_HSTEP(vi_cnt, ui_idx) \ + (0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x88) +#define SUN8I_SCALER_GSU_VSTEP(vi_cnt, ui_idx) \ + (0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x8c) +#define SUN8I_SCALER_GSU_HPHASE(vi_cnt, ui_idx) \ + (0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x90) +#define SUN8I_SCALER_GSU_VPHASE(vi_cnt, ui_idx) \ + (0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x98) +#define SUN8I_SCALER_GSU_HCOEFF(vi_cnt, ui_idx, index) \ + (0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x200 + \ + 0x4 * (index)) + +#define SUN8I_SCALER_GSU_CTRL_EN BIT(0) +#define SUN8I_SCALER_GSU_CTRL_COEFF_RDY BIT(4) + +void sun8i_ui_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable); +void sun8i_ui_scaler_setup(struct sun8i_mixer *mixer, int layer, + u32 src_w, u32 src_h, u32 dst_w, u32 dst_h, + u32 hscale, u32 vscale, u32 hphase, u32 vphase); + +#endif diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c new file mode 100644 index 000000000000..40c3b303068a --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c @@ -0,0 +1,390 @@ +/* + * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_plane_helper.h> +#include <drm/drmP.h> + +#include "sun8i_vi_layer.h" +#include "sun8i_mixer.h" +#include "sun8i_vi_scaler.h" + +static void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel, + int overlay, bool enable) +{ + u32 val; + + DRM_DEBUG_DRIVER("%sabling VI channel %d overlay %d\n", + enable ? "En" : "Dis", channel, overlay); + + if (enable) + val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN; + else + val = 0; + + regmap_update_bits(mixer->engine.regs, + SUN8I_MIXER_CHAN_VI_LAYER_ATTR(channel, overlay), + SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN, val); + + if (enable) + val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel); + else + val = 0; + + regmap_update_bits(mixer->engine.regs, + SUN8I_MIXER_BLEND_PIPE_CTL, + SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel), val); +} + +static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, + int overlay, struct drm_plane *plane) +{ + struct drm_plane_state *state = plane->state; + const struct drm_format_info *format = state->fb->format; + u32 src_w, src_h, dst_w, dst_h; + u32 outsize, insize; + u32 hphase, vphase; + bool subsampled; + + DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n", + channel, overlay); + + src_w = drm_rect_width(&state->src) >> 16; + src_h = drm_rect_height(&state->src) >> 16; + dst_w = drm_rect_width(&state->dst); + dst_h = drm_rect_height(&state->dst); + + hphase = state->src.x1 & 0xffff; + vphase = state->src.y1 & 0xffff; + + /* make coordinates dividable by subsampling factor */ + if (format->hsub > 1) { + int mask, remainder; + + mask = format->hsub - 1; + remainder = (state->src.x1 >> 16) & mask; + src_w = (src_w + remainder) & ~mask; + hphase += remainder << 16; + } + + if (format->vsub > 1) { + int mask, remainder; + + mask = format->vsub - 1; + remainder = (state->src.y1 >> 16) & mask; + src_h = (src_h + remainder) & ~mask; + vphase += remainder << 16; + } + + insize = SUN8I_MIXER_SIZE(src_w, src_h); + outsize = SUN8I_MIXER_SIZE(dst_w, dst_h); + + /* Set height and width */ + DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n", + (state->src.x1 >> 16) & ~(format->hsub - 1), + (state->src.y1 >> 16) & ~(format->vsub - 1)); + DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h); + regmap_write(mixer->engine.regs, + SUN8I_MIXER_CHAN_VI_LAYER_SIZE(channel, overlay), + insize); + regmap_write(mixer->engine.regs, + SUN8I_MIXER_CHAN_VI_OVL_SIZE(channel), + insize); + + /* + * Scaler must be enabled for subsampled formats, so it scales + * chroma to same size as luma. + */ + subsampled = format->hsub > 1 || format->vsub > 1; + + if (insize != outsize || subsampled || hphase || vphase) { + u32 hscale, vscale; + + DRM_DEBUG_DRIVER("HW scaling is enabled\n"); + + hscale = state->src_w / state->crtc_w; + vscale = state->src_h / state->crtc_h; + + sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, dst_w, + dst_h, hscale, vscale, hphase, vphase, + format); + sun8i_vi_scaler_enable(mixer, channel, true); + } else { + DRM_DEBUG_DRIVER("HW scaling is not needed\n"); + sun8i_vi_scaler_enable(mixer, channel, false); + } + + /* Set base coordinates */ + DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n", + state->dst.x1, state->dst.y1); + DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h); + regmap_write(mixer->engine.regs, + SUN8I_MIXER_BLEND_ATTR_COORD(channel), + SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1)); + regmap_write(mixer->engine.regs, + SUN8I_MIXER_BLEND_ATTR_INSIZE(channel), + outsize); + + return 0; +} + +static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel, + int overlay, struct drm_plane *plane) +{ + struct drm_plane_state *state = plane->state; + const struct de2_fmt_info *fmt_info; + u32 val; + + fmt_info = sun8i_mixer_format_info(state->fb->format->format); + if (!fmt_info) { + DRM_DEBUG_DRIVER("Invalid format\n"); + return -EINVAL; + } + + val = fmt_info->de2_fmt << SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET; + regmap_update_bits(mixer->engine.regs, + SUN8I_MIXER_CHAN_VI_LAYER_ATTR(channel, overlay), + SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK, val); + + if (fmt_info->csc != SUN8I_CSC_MODE_OFF) { + sun8i_csc_set_ccsc_coefficients(mixer, channel, fmt_info->csc); + sun8i_csc_enable_ccsc(mixer, channel, true); + } else { + sun8i_csc_enable_ccsc(mixer, channel, false); + } + + if (fmt_info->rgb) + val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE; + else + val = 0; + + regmap_update_bits(mixer->engine.regs, + SUN8I_MIXER_CHAN_VI_LAYER_ATTR(channel, overlay), + SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE, val); + + return 0; +} + +static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel, + int overlay, struct drm_plane *plane) +{ + struct drm_plane_state *state = plane->state; + struct drm_framebuffer *fb = state->fb; + const struct drm_format_info *format = fb->format; + struct drm_gem_cma_object *gem; + u32 dx, dy, src_x, src_y; + dma_addr_t paddr; + int i; + + /* Adjust x and y to be dividable by subsampling factor */ + src_x = (state->src.x1 >> 16) & ~(format->hsub - 1); + src_y = (state->src.y1 >> 16) & ~(format->vsub - 1); + + for (i = 0; i < format->num_planes; i++) { + /* Get the physical address of the buffer in memory */ + gem = drm_fb_cma_get_gem_obj(fb, i); + + DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr); + + /* Compute the start of the displayed memory */ + paddr = gem->paddr + fb->offsets[i]; + + dx = src_x; + dy = src_y; + + if (i > 0) { + dx /= format->hsub; + dy /= format->vsub; + } + + /* Fixup framebuffer address for src coordinates */ + paddr += dx * format->cpp[i]; + paddr += dy * fb->pitches[i]; + + /* Set the line width */ + DRM_DEBUG_DRIVER("Layer %d. line width: %d bytes\n", + i + 1, fb->pitches[i]); + regmap_write(mixer->engine.regs, + SUN8I_MIXER_CHAN_VI_LAYER_PITCH(channel, + overlay, i), + fb->pitches[i]); + + DRM_DEBUG_DRIVER("Setting %d. buffer address to %pad\n", + i + 1, &paddr); + + regmap_write(mixer->engine.regs, + SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(channel, + overlay, i), + lower_32_bits(paddr)); + } + + return 0; +} + +static int sun8i_vi_layer_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); + struct drm_crtc *crtc = state->crtc; + struct drm_crtc_state *crtc_state; + int min_scale, max_scale; + struct drm_rect clip; + + if (!crtc) + return 0; + + crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); + if (WARN_ON(!crtc_state)) + return -EINVAL; + + clip.x1 = 0; + clip.y1 = 0; + clip.x2 = crtc_state->adjusted_mode.hdisplay; + clip.y2 = crtc_state->adjusted_mode.vdisplay; + + min_scale = DRM_PLANE_HELPER_NO_SCALING; + max_scale = DRM_PLANE_HELPER_NO_SCALING; + + if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) { + min_scale = SUN8I_VI_SCALER_SCALE_MIN; + max_scale = SUN8I_VI_SCALER_SCALE_MAX; + } + + return drm_atomic_helper_check_plane_state(state, crtc_state, &clip, + min_scale, max_scale, + true, true); +} + +static void sun8i_vi_layer_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); + struct sun8i_mixer *mixer = layer->mixer; + + sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, false); +} + +static void sun8i_vi_layer_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); + struct sun8i_mixer *mixer = layer->mixer; + + if (!plane->state->visible) { + sun8i_vi_layer_enable(mixer, layer->channel, + layer->overlay, false); + return; + } + + sun8i_vi_layer_update_coord(mixer, layer->channel, + layer->overlay, plane); + sun8i_vi_layer_update_formats(mixer, layer->channel, + layer->overlay, plane); + sun8i_vi_layer_update_buffer(mixer, layer->channel, + layer->overlay, plane); + sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, true); +} + +static struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = { + .atomic_check = sun8i_vi_layer_atomic_check, + .atomic_disable = sun8i_vi_layer_atomic_disable, + .atomic_update = sun8i_vi_layer_atomic_update, +}; + +static const struct drm_plane_funcs sun8i_vi_layer_funcs = { + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .destroy = drm_plane_cleanup, + .disable_plane = drm_atomic_helper_disable_plane, + .reset = drm_atomic_helper_plane_reset, + .update_plane = drm_atomic_helper_update_plane, +}; + +/* + * While all RGB formats are supported, VI planes don't support + * alpha blending, so there is no point having formats with alpha + * channel if their opaque analog exist. + */ +static const u32 sun8i_vi_layer_formats[] = { + DRM_FORMAT_ABGR1555, + DRM_FORMAT_ABGR4444, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_ARGB4444, + DRM_FORMAT_BGR565, + DRM_FORMAT_BGR888, + DRM_FORMAT_BGRA5551, + DRM_FORMAT_BGRA4444, + DRM_FORMAT_BGRX8888, + DRM_FORMAT_RGB565, + DRM_FORMAT_RGB888, + DRM_FORMAT_RGBA4444, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_RGBX8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_XRGB8888, + + DRM_FORMAT_NV16, + DRM_FORMAT_NV12, + DRM_FORMAT_NV21, + DRM_FORMAT_NV61, + DRM_FORMAT_UYVY, + DRM_FORMAT_VYUY, + DRM_FORMAT_YUYV, + DRM_FORMAT_YVYU, + DRM_FORMAT_YUV411, + DRM_FORMAT_YUV420, + DRM_FORMAT_YUV422, + DRM_FORMAT_YUV444, + DRM_FORMAT_YVU411, + DRM_FORMAT_YVU420, + DRM_FORMAT_YVU422, + DRM_FORMAT_YVU444, +}; + +struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm, + struct sun8i_mixer *mixer, + int index) +{ + struct sun8i_vi_layer *layer; + int ret; + + layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL); + if (!layer) + return ERR_PTR(-ENOMEM); + + /* possible crtcs are set later */ + ret = drm_universal_plane_init(drm, &layer->plane, 0, + &sun8i_vi_layer_funcs, + sun8i_vi_layer_formats, + ARRAY_SIZE(sun8i_vi_layer_formats), + NULL, DRM_PLANE_TYPE_OVERLAY, NULL); + if (ret) { + dev_err(drm->dev, "Couldn't initialize layer\n"); + return ERR_PTR(ret); + } + + /* fixed zpos for now */ + ret = drm_plane_create_zpos_immutable_property(&layer->plane, index); + if (ret) { + dev_err(drm->dev, "Couldn't add zpos property\n"); + return ERR_PTR(ret); + } + + drm_plane_helper_add(&layer->plane, &sun8i_vi_layer_helper_funcs); + layer->mixer = mixer; + layer->channel = index; + layer->overlay = 0; + + return layer; +} diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.h b/drivers/gpu/drm/sun4i/sun8i_vi_layer.h new file mode 100644 index 000000000000..6996627a0a76 --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#ifndef _SUN8I_VI_LAYER_H_ +#define _SUN8I_VI_LAYER_H_ + +#include <drm/drm_plane.h> + +#define SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch, layer) \ + (0x2000 + 0x1000 * (ch) + 0x30 * (layer) + 0x0) +#define SUN8I_MIXER_CHAN_VI_LAYER_SIZE(ch, layer) \ + (0x2000 + 0x1000 * (ch) + 0x30 * (layer) + 0x4) +#define SUN8I_MIXER_CHAN_VI_LAYER_COORD(ch, layer) \ + (0x2000 + 0x1000 * (ch) + 0x30 * (layer) + 0x8) +#define SUN8I_MIXER_CHAN_VI_LAYER_PITCH(ch, layer, plane) \ + (0x2000 + 0x1000 * (ch) + 0x30 * (layer) + 0xc + 4 * (plane)) +#define SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(ch, layer, plane) \ + (0x2000 + 0x1000 * (ch) + 0x30 * (layer) + 0x18 + 4 * (plane)) +#define SUN8I_MIXER_CHAN_VI_OVL_SIZE(ch) (0x2000 + 0x1000 * (ch) + 0xe8) + +#define SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN BIT(0) +/* RGB mode should be set for RGB formats and cleared for YCbCr */ +#define SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE BIT(15) +#define SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET 8 +#define SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK GENMASK(12, 8) + +struct sun8i_mixer; + +struct sun8i_vi_layer { + struct drm_plane plane; + struct sun8i_mixer *mixer; + int channel; + int overlay; +}; + +static inline struct sun8i_vi_layer * +plane_to_sun8i_vi_layer(struct drm_plane *plane) +{ + return container_of(plane, struct sun8i_vi_layer, plane); +} + +struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm, + struct sun8i_mixer *mixer, + int index); +#endif /* _SUN8I_VI_LAYER_H_ */ diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c new file mode 100644 index 000000000000..d3f1acb234b7 --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c @@ -0,0 +1,971 @@ +/* + * Copyright (C) 2017 Jernej Skrabec <jernej.skrabec@siol.net> + * + * Coefficients are taken from BSP driver, which is: + * Copyright (C) 2014-2015 Allwinner + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include "sun8i_vi_scaler.h" + +static const u32 lan3coefftab32_left[480] = { + 0x40000000, 0x40fe0000, 0x3ffd0100, 0x3efc0100, + 0x3efb0100, 0x3dfa0200, 0x3cf90200, 0x3bf80200, + 0x39f70200, 0x37f70200, 0x35f70200, 0x33f70200, + 0x31f70200, 0x2ef70200, 0x2cf70200, 0x2af70200, + 0x27f70200, 0x24f80100, 0x22f80100, 0x1ef90100, + 0x1cf90100, 0x19fa0100, 0x17fa0100, 0x14fb0100, + 0x11fc0000, 0x0ffc0000, 0x0cfd0000, 0x0afd0000, + 0x08fe0000, 0x05ff0000, 0x03ff0000, 0x02000000, + + 0x3806fc02, 0x3805fc02, 0x3803fd01, 0x3801fe01, + 0x3700fe01, 0x35ffff01, 0x35fdff01, 0x34fc0001, + 0x34fb0000, 0x33fa0000, 0x31fa0100, 0x2ff90100, + 0x2df80200, 0x2bf80200, 0x2af70200, 0x28f70200, + 0x27f70200, 0x24f70300, 0x22f70300, 0x1ff70300, + 0x1ef70300, 0x1cf70300, 0x1af70300, 0x18f70300, + 0x16f80300, 0x13f80300, 0x11f90300, 0x0ef90300, + 0x0efa0200, 0x0cfa0200, 0x0afb0200, 0x08fb0200, + + 0x320bfa02, 0x3309fa02, 0x3208fb02, 0x3206fb02, + 0x3205fb02, 0x3104fc02, 0x3102fc01, 0x3001fd01, + 0x3000fd01, 0x2ffffd01, 0x2efefe01, 0x2dfdfe01, + 0x2bfcff01, 0x29fcff01, 0x28fbff01, 0x27fa0001, + 0x26fa0000, 0x24f90000, 0x22f90100, 0x20f90100, + 0x1ff80100, 0x1ef80100, 0x1cf80100, 0x1af80200, + 0x18f80200, 0x17f80200, 0x15f80200, 0x12f80200, + 0x11f90200, 0x0ff90200, 0x0df90200, 0x0cfa0200, + + 0x2e0efa01, 0x2f0dfa01, 0x2f0bfa01, 0x2e0afa01, + 0x2e09fa01, 0x2e07fb01, 0x2d06fb01, 0x2d05fb01, + 0x2c04fb01, 0x2b03fc01, 0x2a02fc01, 0x2a01fc01, + 0x2800fd01, 0x28fffd01, 0x26fefd01, 0x25fefe01, + 0x24fdfe01, 0x23fcfe01, 0x21fcff01, 0x20fbff01, + 0x1efbff01, 0x1efbff00, 0x1cfa0000, 0x1bfa0000, + 0x19fa0000, 0x18fa0000, 0x17f90000, 0x15f90100, + 0x14f90100, 0x12f90100, 0x11f90100, 0x0ff90100, + + 0x2b10fa00, 0x2b0ffa00, 0x2b0efa00, 0x2b0cfa00, + 0x2b0bfa00, 0x2a0afb01, 0x2a09fb01, 0x2908fb01, + 0x2807fb01, 0x2806fb01, 0x2805fb01, 0x2604fc01, + 0x2503fc01, 0x2502fc01, 0x2401fc01, 0x2301fc01, + 0x2100fd01, 0x21fffd01, 0x21fffd01, 0x20fefd01, + 0x1dfefe01, 0x1cfdfe01, 0x1cfdfe00, 0x1bfcfe00, + 0x19fcff00, 0x19fbff00, 0x17fbff00, 0x16fbff00, + 0x15fbff00, 0x14fb0000, 0x13fa0000, 0x11fa0000, + + 0x2811fcff, 0x2810fcff, 0x280ffbff, 0x280efbff, + 0x270dfb00, 0x270cfb00, 0x270bfb00, 0x260afb00, + 0x2609fb00, 0x2508fb00, 0x2507fb00, 0x2407fb00, + 0x2406fc00, 0x2305fc00, 0x2204fc00, 0x2203fc00, + 0x2103fc00, 0x2002fc00, 0x1f01fd00, 0x1e01fd00, + 0x1d00fd00, 0x1dfffd00, 0x1cfffd00, 0x1bfefd00, + 0x1afefe00, 0x19fefe00, 0x18fdfe00, 0x17fdfe00, + 0x16fdfe00, 0x15fcff00, 0x13fcff00, 0x12fcff00, + + 0x2512fdfe, 0x2511fdff, 0x2410fdff, 0x240ffdff, + 0x240efcff, 0x240dfcff, 0x240dfcff, 0x240cfcff, + 0x230bfcff, 0x230afc00, 0x2209fc00, 0x2108fc00, + 0x2108fc00, 0x2007fc00, 0x2006fc00, 0x2005fc00, + 0x1f05fc00, 0x1e04fc00, 0x1e03fc00, 0x1c03fd00, + 0x1c02fd00, 0x1b02fd00, 0x1b01fd00, 0x1a00fd00, + 0x1900fd00, 0x1800fd00, 0x17fffe00, 0x16fffe00, + 0x16fefe00, 0x14fefe00, 0x13fefe00, 0x13fdfe00, + + 0x2212fffe, 0x2211fefe, 0x2211fefe, 0x2110fefe, + 0x210ffeff, 0x220efdff, 0x210dfdff, 0x210dfdff, + 0x210cfdff, 0x210bfdff, 0x200afdff, 0x200afdff, + 0x1f09fdff, 0x1f08fdff, 0x1d08fd00, 0x1c07fd00, + 0x1d06fd00, 0x1b06fd00, 0x1b05fd00, 0x1c04fd00, + 0x1b04fd00, 0x1a03fd00, 0x1a03fd00, 0x1902fd00, + 0x1802fd00, 0x1801fd00, 0x1701fd00, 0x1600fd00, + 0x1400fe00, 0x1400fe00, 0x14fffe00, 0x13fffe00, + + 0x201200fe, 0x201100fe, 0x1f11fffe, 0x2010fffe, + 0x1f0ffffe, 0x1e0ffffe, 0x1f0efeff, 0x1f0dfeff, + 0x1f0dfeff, 0x1e0cfeff, 0x1e0bfeff, 0x1d0bfeff, + 0x1d0afeff, 0x1d09fdff, 0x1d09fdff, 0x1c08fdff, + 0x1c07fdff, 0x1b07fd00, 0x1b06fd00, 0x1a06fd00, + 0x1a05fd00, 0x1805fd00, 0x1904fd00, 0x1804fd00, + 0x1703fd00, 0x1703fd00, 0x1602fe00, 0x1502fe00, + 0x1501fe00, 0x1401fe00, 0x1301fe00, 0x1300fe00, + + 0x1c1202fe, 0x1c1102fe, 0x1b1102fe, 0x1c1001fe, + 0x1b1001fe, 0x1b0f01ff, 0x1b0e00ff, 0x1b0e00ff, + 0x1b0d00ff, 0x1a0d00ff, 0x1a0c00ff, 0x1a0cffff, + 0x1a0bffff, 0x1a0bffff, 0x1a0affff, 0x180affff, + 0x1909ffff, 0x1809ffff, 0x1808ffff, 0x1808feff, + 0x1807feff, 0x1707fe00, 0x1606fe00, 0x1506fe00, + 0x1605fe00, 0x1505fe00, 0x1504fe00, 0x1304fe00, + 0x1304fe00, 0x1303fe00, 0x1203fe00, 0x1203fe00, + + 0x181104ff, 0x191103ff, 0x191003ff, 0x181003ff, + 0x180f03ff, 0x190f02ff, 0x190e02ff, 0x180e02ff, + 0x180d02ff, 0x180d01ff, 0x180d01ff, 0x180c01ff, + 0x180c01ff, 0x180b00ff, 0x170b00ff, 0x170a00ff, + 0x170a00ff, 0x170900ff, 0x160900ff, 0x160900ff, + 0x1608ffff, 0x1508ffff, 0x1507ff00, 0x1507ff00, + 0x1407ff00, 0x1306ff00, 0x1306ff00, 0x1305ff00, + 0x1205ff00, 0x1105ff00, 0x1204ff00, 0x1104ff00, + + 0x171005ff, 0x171005ff, 0x171004ff, 0x170f04ff, + 0x160f04ff, 0x170f03ff, 0x170e03ff, 0x160e03ff, + 0x160d03ff, 0x160d02ff, 0x160d02ff, 0x160c02ff, + 0x160c02ff, 0x160c02ff, 0x160b01ff, 0x150b01ff, + 0x150a01ff, 0x150a01ff, 0x150a01ff, 0x140901ff, + 0x14090000, 0x14090000, 0x14080000, 0x13080000, + 0x13070000, 0x12070000, 0x12070000, 0x12060000, + 0x11060000, 0x11060000, 0x11050000, 0x1105ff00, + + 0x14100600, 0x15100500, 0x150f0500, 0x150f0500, + 0x140f0500, 0x150e0400, 0x140e0400, 0x130e0400, + 0x140d0400, 0x150d0300, 0x130d0300, 0x140c0300, + 0x140c0300, 0x140c0200, 0x140b0200, 0x130b0200, + 0x120b0200, 0x130a0200, 0x130a0200, 0x130a0100, + 0x13090100, 0x12090100, 0x11090100, 0x12080100, + 0x11080100, 0x10080100, 0x11070100, 0x11070000, + 0x10070000, 0x11060000, 0x10060000, 0x10060000, + + 0x140f0600, 0x140f0600, 0x130f0600, 0x140f0500, + 0x140e0500, 0x130e0500, 0x130e0500, 0x140d0400, + 0x140d0400, 0x130d0400, 0x120d0400, 0x130c0400, + 0x130c0300, 0x130c0300, 0x130b0300, 0x130b0300, + 0x110b0300, 0x130a0200, 0x120a0200, 0x120a0200, + 0x120a0200, 0x12090200, 0x10090200, 0x11090100, + 0x11080100, 0x11080100, 0x10080100, 0x10080100, + 0x10070100, 0x10070100, 0x0f070100, 0x10060100, + + 0x120f0701, 0x130f0601, 0x130e0601, 0x130e0601, + 0x120e0601, 0x130e0501, 0x130e0500, 0x130d0500, + 0x120d0500, 0x120d0500, 0x130c0400, 0x130c0400, + 0x120c0400, 0x110c0400, 0x120b0400, 0x120b0300, + 0x120b0300, 0x120b0300, 0x120a0300, 0x110a0300, + 0x110a0200, 0x11090200, 0x11090200, 0x10090200, + 0x10090200, 0x10080200, 0x10080200, 0x10080100, + 0x0f080100, 0x10070100, 0x0f070100, 0x0f070100 +}; + +static const u32 lan3coefftab32_right[480] = { + 0x00000000, 0x00000002, 0x0000ff04, 0x0000ff06, + 0x0000fe08, 0x0000fd0a, 0x0000fd0c, 0x0000fc0f, + 0x0000fc12, 0x0001fb14, 0x0001fa17, 0x0001fa19, + 0x0001f91c, 0x0001f91f, 0x0001f822, 0x0001f824, + 0x0002f727, 0x0002f72a, 0x0002f72c, 0x0002f72f, + 0x0002f731, 0x0002f733, 0x0002f735, 0x0002f737, + 0x0002f73a, 0x0002f83b, 0x0002f93c, 0x0002fa3d, + 0x0001fb3e, 0x0001fc3f, 0x0001fd40, 0x0000fe40, + + 0x0002fc06, 0x0002fb08, 0x0002fb0a, 0x0002fa0c, + 0x0002fa0e, 0x0003f910, 0x0003f912, 0x0003f814, + 0x0003f816, 0x0003f719, 0x0003f71a, 0x0003f71d, + 0x0003f71f, 0x0003f721, 0x0003f723, 0x0003f725, + 0x0002f727, 0x0002f729, 0x0002f72b, 0x0002f82d, + 0x0002f82e, 0x0001f930, 0x0001fa31, 0x0000fa34, + 0x0000fb34, 0x0100fc35, 0x01fffd36, 0x01ffff37, + 0x01fe0037, 0x01fe0138, 0x01fd0338, 0x02fc0538, + + 0x0002fa0b, 0x0002fa0c, 0x0002f90e, 0x0002f910, + 0x0002f911, 0x0002f813, 0x0002f816, 0x0002f817, + 0x0002f818, 0x0002f81a, 0x0001f81c, 0x0001f81e, + 0x0001f820, 0x0001f921, 0x0001f923, 0x0000f925, + 0x0000fa26, 0x0100fa28, 0x01fffb29, 0x01fffc2a, + 0x01fffc2c, 0x01fefd2d, 0x01fefe2e, 0x01fdff2f, + 0x01fd0030, 0x01fd0130, 0x01fc0232, 0x02fc0432, + 0x02fb0532, 0x02fb0633, 0x02fb0833, 0x02fa0933, + + 0x0001fa0e, 0x0001f90f, 0x0001f911, 0x0001f913, + 0x0001f914, 0x0001f915, 0x0000f918, 0x0000fa18, + 0x0000fa1a, 0x0000fa1b, 0x0000fa1d, 0x00fffb1e, + 0x01fffb1f, 0x01fffb20, 0x01fffc22, 0x01fefc23, + 0x01fefd24, 0x01fefe25, 0x01fdfe27, 0x01fdff28, + 0x01fd0029, 0x01fc012a, 0x01fc022b, 0x01fc032b, + 0x01fb042d, 0x01fb052d, 0x01fb062e, 0x01fb072e, + 0x01fa092e, 0x01fa0a2f, 0x01fa0b2f, 0x01fa0d2f, + + 0x0000fa11, 0x0000fa12, 0x0000fa13, 0x0000fb14, + 0x00fffb16, 0x00fffb16, 0x00fffb17, 0x00fffb19, + 0x00fffc1a, 0x00fefc1c, 0x00fefd1c, 0x01fefd1d, + 0x01fefe1e, 0x01fdfe20, 0x01fdff21, 0x01fdff22, + 0x01fd0023, 0x01fc0124, 0x01fc0124, 0x01fc0225, + 0x01fc0326, 0x01fc0427, 0x01fb0528, 0x01fb0629, + 0x01fb0729, 0x01fb0829, 0x01fb092a, 0x01fb0a2a, + 0x00fa0b2c, 0x00fa0c2b, 0x00fa0e2b, 0x00fa0f2c, + + 0x00fffc11, 0x00fffc12, 0x00fffc14, 0x00fffc15, + 0x00fefd16, 0x00fefd17, 0x00fefd18, 0x00fefe19, + 0x00fefe1a, 0x00fdfe1d, 0x00fdff1d, 0x00fdff1e, + 0x00fd001d, 0x00fd011e, 0x00fd0120, 0x00fc0221, + 0x00fc0321, 0x00fc0323, 0x00fc0423, 0x00fc0523, + 0x00fc0624, 0x00fb0725, 0x00fb0726, 0x00fb0827, + 0x00fb0926, 0x00fb0a26, 0x00fb0b27, 0x00fb0c27, + 0x00fb0d27, 0xfffb0e28, 0xfffb0f29, 0xfffc1028, + + 0x00fefd13, 0x00fefd13, 0x00fefe14, 0x00fefe15, + 0x00fefe17, 0x00feff17, 0x00feff17, 0x00fd0018, + 0x00fd001a, 0x00fd001a, 0x00fd011b, 0x00fd021c, + 0x00fd021c, 0x00fd031d, 0x00fc031f, 0x00fc041f, + 0x00fc051f, 0x00fc0521, 0x00fc0621, 0x00fc0721, + 0x00fc0821, 0x00fc0822, 0x00fc0922, 0x00fc0a23, + 0xfffc0b24, 0xfffc0c24, 0xfffc0d24, 0xfffc0d25, + 0xfffc0e25, 0xfffd0f25, 0xfffd1025, 0xfffd1125, + + 0x00feff12, 0x00feff14, 0x00feff14, 0x00fe0015, + 0x00fe0015, 0x00fd0017, 0x00fd0118, 0x00fd0118, + 0x00fd0218, 0x00fd0219, 0x00fd031a, 0x00fd031a, + 0x00fd041b, 0x00fd041c, 0x00fd051c, 0x00fd061d, + 0x00fd061d, 0x00fd071e, 0x00fd081e, 0xfffd081f, + 0xfffd091f, 0xfffd0a20, 0xfffd0a20, 0xfffd0b21, + 0xfffd0c21, 0xfffd0d21, 0xfffd0d22, 0xfffd0e23, + 0xfffe0f22, 0xfefe1022, 0xfefe1122, 0xfefe1123, + + 0x00fe0012, 0x00fe0013, 0x00fe0114, 0x00fe0114, + 0x00fe0116, 0x00fe0216, 0x00fe0216, 0x00fd0317, + 0x00fd0317, 0x00fd0418, 0x00fd0419, 0x00fd0519, + 0x00fd051a, 0x00fd061b, 0x00fd061b, 0x00fd071c, + 0xfffd071e, 0xfffd081d, 0xfffd091d, 0xfffd091e, + 0xfffe0a1d, 0xfffe0b1e, 0xfffe0b1e, 0xfffe0c1e, + 0xfffe0d1f, 0xfffe0d1f, 0xfffe0e1f, 0xfeff0f1f, + 0xfeff0f20, 0xfeff1020, 0xfeff1120, 0xfe001120, + + 0x00fe0212, 0x00fe0312, 0x00fe0313, 0x00fe0314, + 0x00fe0414, 0x00fe0414, 0x00fe0416, 0x00fe0515, + 0x00fe0516, 0x00fe0616, 0x00fe0617, 0x00fe0717, + 0xfffe0719, 0xfffe0818, 0xffff0818, 0xffff0919, + 0xffff0919, 0xffff0a19, 0xffff0a1a, 0xffff0b1a, + 0xffff0b1b, 0xffff0c1a, 0xff000c1b, 0xff000d1b, + 0xff000d1b, 0xff000e1b, 0xff000e1c, 0xff010f1c, + 0xfe01101c, 0xfe01101d, 0xfe02111c, 0xfe02111c, + + 0x00ff0411, 0x00ff0411, 0x00ff0412, 0x00ff0512, + 0x00ff0513, 0x00ff0513, 0x00ff0613, 0x00ff0614, + 0x00ff0714, 0x00ff0715, 0x00ff0715, 0xffff0816, + 0xffff0816, 0xff000916, 0xff000917, 0xff000918, + 0xff000a17, 0xff000a18, 0xff000b18, 0xff000b18, + 0xff010c18, 0xff010c19, 0xff010d18, 0xff010d18, + 0xff020d18, 0xff020e19, 0xff020e19, 0xff020f19, + 0xff030f19, 0xff031019, 0xff031019, 0xff031119, + + 0x00ff0511, 0x00ff0511, 0x00000511, 0x00000611, + 0x00000612, 0x00000612, 0x00000712, 0x00000713, + 0x00000714, 0x00000814, 0x00000814, 0x00000914, + 0x00000914, 0xff010914, 0xff010a15, 0xff010a16, + 0xff010a17, 0xff010b16, 0xff010b16, 0xff020c16, + 0xff020c16, 0xff020c16, 0xff020d16, 0xff020d17, + 0xff030d17, 0xff030e17, 0xff030e17, 0xff030f17, + 0xff040f17, 0xff040f17, 0xff041017, 0xff051017, + + 0x00000610, 0x00000610, 0x00000611, 0x00000611, + 0x00000711, 0x00000712, 0x00010712, 0x00010812, + 0x00010812, 0x00010812, 0x00010913, 0x00010913, + 0x00010913, 0x00010a13, 0x00020a13, 0x00020a14, + 0x00020b14, 0x00020b14, 0x00020b14, 0x00020c14, + 0x00030c14, 0x00030c15, 0x00030d15, 0x00030d15, + 0x00040d15, 0x00040e15, 0x00040e15, 0x00040e16, + 0x00050f15, 0x00050f15, 0x00050f16, 0x00051015, + + 0x00000611, 0x00010610, 0x00010710, 0x00010710, + 0x00010711, 0x00010811, 0x00010811, 0x00010812, + 0x00010812, 0x00010912, 0x00020912, 0x00020912, + 0x00020a12, 0x00020a12, 0x00020a13, 0x00020a13, + 0x00030b13, 0x00030b13, 0x00030b14, 0x00030c13, + 0x00030c13, 0x00040c13, 0x00040d14, 0x00040d14, + 0x00040d15, 0x00040d15, 0x00050e14, 0x00050e14, + 0x00050e15, 0x00050f14, 0x00060f14, 0x00060f14, + + 0x0001070f, 0x0001070f, 0x00010710, 0x00010710, + 0x00010810, 0x00010810, 0x00020810, 0x00020811, + 0x00020911, 0x00020911, 0x00020912, 0x00020912, + 0x00020a12, 0x00030a12, 0x00030a12, 0x00030b12, + 0x00030b12, 0x00030b12, 0x00040b12, 0x00040c12, + 0x00040c13, 0x00040c14, 0x00040c14, 0x00050d13, + 0x00050d13, 0x00050d14, 0x00050e13, 0x01050e13, + 0x01060e13, 0x01060e13, 0x01060e14, 0x01060f13 +}; + +static const u32 lan2coefftab32[480] = { + 0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd, + 0x00063efc, 0xff083dfc, 0x000a3bfb, 0xff0d39fb, + 0xff0f37fb, 0xff1136fa, 0xfe1433fb, 0xfe1631fb, + 0xfd192ffb, 0xfd1c2cfb, 0xfd1f29fb, 0xfc2127fc, + 0xfc2424fc, 0xfc2721fc, 0xfb291ffd, 0xfb2c1cfd, + 0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfa3611ff, + 0xfb370fff, 0xfb390dff, 0xfb3b0a00, 0xfc3d08ff, + 0xfc3e0600, 0xfd3f0400, 0xfe3f0300, 0xff400100, + + 0xff053804, 0xff063803, 0xff083801, 0xff093701, + 0xff0a3700, 0xff0c3500, 0xff0e34ff, 0xff1033fe, + 0xff1232fd, 0xfe1431fd, 0xfe162ffd, 0xfe182dfd, + 0xfd1b2cfc, 0xfd1d2afc, 0xfd1f28fc, 0xfd2126fc, + 0xfd2323fd, 0xfc2621fd, 0xfc281ffd, 0xfc2a1dfd, + 0xfc2c1bfd, 0xfd2d18fe, 0xfd2f16fe, 0xfd3114fe, + 0xfd3212ff, 0xfe3310ff, 0xff340eff, 0x00350cff, + 0x00360a00, 0x01360900, 0x02370700, 0x03370600, + + 0xff083207, 0xff093206, 0xff0a3205, 0xff0c3203, + 0xff0d3103, 0xff0e3102, 0xfe113001, 0xfe132f00, + 0xfe142e00, 0xfe162dff, 0xfe182bff, 0xfe192aff, + 0xfe1b29fe, 0xfe1d27fe, 0xfe1f25fe, 0xfd2124fe, + 0xfe2222fe, 0xfe2421fd, 0xfe251ffe, 0xfe271dfe, + 0xfe291bfe, 0xff2a19fe, 0xff2b18fe, 0xff2d16fe, + 0x002e14fe, 0x002f12ff, 0x013010ff, 0x02300fff, + 0x03310dff, 0x04310cff, 0x05310a00, 0x06310900, + + 0xff0a2e09, 0xff0b2e08, 0xff0c2e07, 0xff0e2d06, + 0xff0f2d05, 0xff102d04, 0xff122c03, 0xfe142c02, + 0xfe152b02, 0xfe172a01, 0xfe182901, 0xfe1a2800, + 0xfe1b2700, 0xfe1d2500, 0xff1e24ff, 0xfe2023ff, + 0xff2121ff, 0xff2320fe, 0xff241eff, 0x00251dfe, + 0x00261bff, 0x00281afe, 0x012818ff, 0x012a16ff, + 0x022a15ff, 0x032b13ff, 0x032c12ff, 0x052c10ff, + 0x052d0fff, 0x062d0d00, 0x072d0c00, 0x082d0b00, + + 0xff0c2a0b, 0xff0d2a0a, 0xff0e2a09, 0xff0f2a08, + 0xff102a07, 0xff112a06, 0xff132905, 0xff142904, + 0xff162803, 0xff172703, 0xff182702, 0xff1a2601, + 0xff1b2501, 0xff1c2401, 0xff1e2300, 0xff1f2200, + 0x00202000, 0x00211f00, 0x01221d00, 0x01231c00, + 0x01251bff, 0x02251aff, 0x032618ff, 0x032717ff, + 0x042815ff, 0x052814ff, 0x052913ff, 0x06291100, + 0x072a10ff, 0x082a0e00, 0x092a0d00, 0x0a2a0c00, + + 0xff0d280c, 0xff0e280b, 0xff0f280a, 0xff102809, + 0xff112808, 0xff122708, 0xff142706, 0xff152705, + 0xff162605, 0xff172604, 0xff192503, 0xff1a2403, + 0x001b2302, 0x001c2202, 0x001d2201, 0x001e2101, + 0x011f1f01, 0x01211e00, 0x01221d00, 0x02221c00, + 0x02231b00, 0x03241900, 0x04241800, 0x04251700, + 0x052616ff, 0x06261400, 0x072713ff, 0x08271100, + 0x08271100, 0x09271000, 0x0a280e00, 0x0b280d00, + + 0xff0e260d, 0xff0f260c, 0xff10260b, 0xff11260a, + 0xff122609, 0xff132608, 0xff142508, 0xff152507, + 0x00152506, 0x00172405, 0x00182305, 0x00192304, + 0x001b2203, 0x001c2103, 0x011d2002, 0x011d2002, + 0x011f1f01, 0x021f1e01, 0x02201d01, 0x03211c00, + 0x03221b00, 0x04221a00, 0x04231801, 0x05241700, + 0x06241600, 0x07241500, 0x08251300, 0x09251200, + 0x09261100, 0x0a261000, 0x0b260f00, 0x0c260e00, + + 0xff0e250e, 0xff0f250d, 0xff10250c, 0xff11250b, + 0x0011250a, 0x00132409, 0x00142408, 0x00152407, + 0x00162307, 0x00172306, 0x00182206, 0x00192205, + 0x011a2104, 0x011b2004, 0x011c2003, 0x021c1f03, + 0x021e1e02, 0x031e1d02, 0x03201c01, 0x04201b01, + 0x04211a01, 0x05221900, 0x05221801, 0x06231700, + 0x07231600, 0x07241500, 0x08241400, 0x09241300, + 0x0a241200, 0x0b241100, 0x0c241000, 0x0d240f00, + + 0x000e240e, 0x000f240d, 0x0010240c, 0x0011240b, + 0x0013230a, 0x0013230a, 0x00142309, 0x00152308, + 0x00162208, 0x00172207, 0x01182106, 0x01192105, + 0x011a2005, 0x021b1f04, 0x021b1f04, 0x021d1e03, + 0x031d1d03, 0x031e1d02, 0x041e1c02, 0x041f1b02, + 0x05201a01, 0x05211901, 0x06211801, 0x07221700, + 0x07221601, 0x08231500, 0x09231400, 0x0a231300, + 0x0a231300, 0x0b231200, 0x0c231100, 0x0d231000, + + 0x000f220f, 0x0010220e, 0x0011220d, 0x0012220c, + 0x0013220b, 0x0013220b, 0x0015210a, 0x0015210a, + 0x01162108, 0x01172008, 0x01182007, 0x02191f06, + 0x02191f06, 0x021a1e06, 0x031a1e05, 0x031c1d04, + 0x041c1c04, 0x041d1c03, 0x051d1b03, 0x051e1a03, + 0x061f1902, 0x061f1902, 0x07201801, 0x08201701, + 0x08211601, 0x09211501, 0x0a211500, 0x0b211400, + 0x0b221300, 0x0c221200, 0x0d221100, 0x0e221000, + + 0x0010210f, 0x0011210e, 0x0011210e, 0x0012210d, + 0x0013210c, 0x0014200c, 0x0114200b, 0x0115200a, + 0x01161f0a, 0x01171f09, 0x02171f08, 0x02181e08, + 0x03181e07, 0x031a1d06, 0x031a1d06, 0x041b1c05, + 0x041c1c04, 0x051c1b04, 0x051d1a04, 0x061d1a03, + 0x071d1903, 0x071e1803, 0x081e1802, 0x081f1702, + 0x091f1602, 0x0a201501, 0x0b1f1501, 0x0b201401, + 0x0c211300, 0x0d211200, 0x0e201200, 0x0e211100, + + 0x00102010, 0x0011200f, 0x0012200e, 0x0013200d, + 0x0013200d, 0x01141f0c, 0x01151f0b, 0x01151f0b, + 0x01161f0a, 0x02171e09, 0x02171e09, 0x03181d08, + 0x03191d07, 0x03191d07, 0x041a1c06, 0x041b1c05, + 0x051b1b05, 0x051c1b04, 0x061c1a04, 0x071d1903, + 0x071d1903, 0x081d1803, 0x081e1703, 0x091e1702, + 0x0a1f1601, 0x0a1f1502, 0x0b1f1501, 0x0c1f1401, + 0x0d201300, 0x0d201300, 0x0e201200, 0x0f201100, + + 0x00102010, 0x0011200f, 0x00121f0f, 0x00131f0e, + 0x00141f0d, 0x01141f0c, 0x01141f0c, 0x01151e0c, + 0x02161e0a, 0x02171e09, 0x03171d09, 0x03181d08, + 0x03181d08, 0x04191c07, 0x041a1c06, 0x051a1b06, + 0x051b1b05, 0x061b1a05, 0x061c1a04, 0x071c1904, + 0x081c1903, 0x081d1803, 0x091d1703, 0x091e1702, + 0x0a1e1602, 0x0b1e1502, 0x0c1e1501, 0x0c1f1401, + 0x0d1f1400, 0x0e1f1300, 0x0e1f1201, 0x0f1f1200, + + 0x00111e11, 0x00121e10, 0x00131e0f, 0x00131e0f, + 0x01131e0e, 0x01141d0e, 0x02151d0c, 0x02151d0c, + 0x02161d0b, 0x03161c0b, 0x03171c0a, 0x04171c09, + 0x04181b09, 0x05181b08, 0x05191b07, 0x06191a07, + 0x061a1a06, 0x071a1906, 0x071b1905, 0x081b1805, + 0x091b1804, 0x091c1704, 0x0a1c1703, 0x0a1c1604, + 0x0b1d1602, 0x0c1d1502, 0x0c1d1502, 0x0d1d1402, + 0x0e1d1401, 0x0e1e1301, 0x0f1e1300, 0x101e1200, + + 0x00111e11, 0x00121e10, 0x00131d10, 0x01131d0f, + 0x01141d0e, 0x01141d0e, 0x02151c0d, 0x02151c0d, + 0x03161c0b, 0x03161c0b, 0x04171b0a, 0x04171b0a, + 0x05171b09, 0x05181a09, 0x06181a08, 0x06191a07, + 0x07191907, 0x071a1906, 0x081a1806, 0x081a1806, + 0x091a1805, 0x0a1b1704, 0x0a1b1704, 0x0b1c1603, + 0x0b1c1603, 0x0c1c1503, 0x0d1c1502, 0x0d1d1402, + 0x0e1d1401, 0x0f1d1301, 0x0f1d1301, 0x101e1200, +}; + +static const u32 bicubic8coefftab32_left[480] = { + 0x40000000, 0x40ff0000, 0x3ffe0000, 0x3efe0000, + 0x3dfd0000, 0x3cfc0000, 0x3bfc0000, 0x39fc0000, + 0x36fc0000, 0x35fb0000, 0x33fb0000, 0x31fb0000, + 0x2ffb0000, 0x2cfb0000, 0x29fc0000, 0x27fc0000, + 0x24fc0000, 0x21fc0000, 0x1efd0000, 0x1cfd0000, + 0x19fd0000, 0x16fe0000, 0x14fe0000, 0x11fe0000, + 0x0dff0000, 0x0cff0000, 0x0aff0000, 0x08ff0000, + 0x05000000, 0x03000000, 0x02000000, 0x01000000, + + 0x3904ff00, 0x3903ff00, 0x3902ff00, 0x38010000, + 0x37000000, 0x36ff0000, 0x35ff0000, 0x34fe0000, + 0x32fe0000, 0x31fd0000, 0x30fd0000, 0x2efc0000, + 0x2cfc0000, 0x2afc0000, 0x28fc0000, 0x26fc0000, + 0x24fc0000, 0x22fc0000, 0x20fc0000, 0x1efc0000, + 0x1cfc0000, 0x19fc0000, 0x17fc0000, 0x15fd0000, + 0x12fd0000, 0x11fd0000, 0x0ffd0000, 0x0dfe0000, + 0x0bfe0000, 0x09fe0000, 0x08fe0000, 0x06ff0000, + + 0x3209fe00, 0x3407fe00, 0x3306fe00, 0x3305fe00, + 0x3204fe00, 0x3102ff00, 0x3102ff00, 0x3001ff00, + 0x2f00ff00, 0x2effff00, 0x2cff0000, 0x2bfe0000, + 0x29fe0000, 0x28fe0000, 0x26fd0000, 0x24fd0000, + 0x23fd0000, 0x21fd0000, 0x20fc0000, 0x1efc0000, + 0x1dfc0000, 0x1bfc0000, 0x19fc0000, 0x17fc0000, + 0x16fc0000, 0x14fc0000, 0x12fc0000, 0x10fd0000, + 0x0ffd0000, 0x0dfd0000, 0x0cfd0000, 0x0afd0000, + + 0x2e0cfd00, 0x2e0bfd00, 0x2e09fd00, 0x2e08fd00, + 0x2e07fd00, 0x2c06fe00, 0x2c05fe00, 0x2b04fe00, + 0x2b03fe00, 0x2a02fe00, 0x2901fe00, 0x2701ff00, + 0x2700ff00, 0x26ffff00, 0x24ffff00, 0x23ffff00, + 0x22feff00, 0x20fe0000, 0x1ffe0000, 0x1efd0000, + 0x1dfd0000, 0x1bfd0000, 0x1afd0000, 0x19fd0000, + 0x17fd0000, 0x15fd0000, 0x13fd0000, 0x12fd0000, + 0x11fd0000, 0x10fd0000, 0x0ffd0000, 0x0cfd0000, + + 0x2a0efd00, 0x2a0dfd00, 0x2a0cfd00, 0x290bfd00, + 0x290afd00, 0x2909fd00, 0x2908fd00, 0x2807fd00, + 0x2706fd00, 0x2705fd00, 0x2604fe00, 0x2603fe00, + 0x2502fe00, 0x2402fe00, 0x2401fe00, 0x2200fe00, + 0x2200fe00, 0x2000ff00, 0x1fffff00, 0x1effff00, + 0x1dfeff00, 0x1cfeff00, 0x1afeff00, 0x19feff00, + 0x17fe0000, 0x16fd0000, 0x15fd0000, 0x14fd0000, + 0x12fd0000, 0x11fd0000, 0x10fd0000, 0x0ffd0000, + + 0x2610fd00, 0x260ffd00, 0x260efd00, 0x260dfd00, + 0x260cfd00, 0x260bfd00, 0x260afd00, 0x2609fd00, + 0x2508fd00, 0x2507fd00, 0x2406fd00, 0x2406fd00, + 0x2305fd00, 0x2304fd00, 0x2203fe00, 0x2103fe00, + 0x2002fe00, 0x1f01fe00, 0x1e01fe00, 0x1e00fe00, + 0x1c00fe00, 0x1b00fe00, 0x1afffe00, 0x19ffff00, + 0x18ffff00, 0x17feff00, 0x16feff00, 0x15feff00, + 0x14feff00, 0x13feff00, 0x11feff00, 0x10fd0000, + + 0x2411feff, 0x2410feff, 0x240ffeff, 0x230efeff, + 0x240dfeff, 0x240cfeff, 0x230cfd00, 0x230bfd00, + 0x230afd00, 0x2309fd00, 0x2208fd00, 0x2108fd00, + 0x2007fd00, 0x2106fd00, 0x2005fd00, 0x1f05fd00, + 0x1f04fd00, 0x1e03fd00, 0x1d03fe00, 0x1c02fe00, + 0x1b02fe00, 0x1a01fe00, 0x1a01fe00, 0x1900fe00, + 0x1800fe00, 0x1700fe00, 0x16fffe00, 0x15fffe00, + 0x13ffff00, 0x12ffff00, 0x12feff00, 0x11feff00, + + 0x2212fffe, 0x2211fffe, 0x2210ffff, 0x220ffeff, + 0x220efeff, 0x210efeff, 0x210dfeff, 0x210cfeff, + 0x210bfeff, 0x200bfeff, 0x200afeff, 0x1f09feff, + 0x1f08feff, 0x1d08fe00, 0x1e07fd00, 0x1e06fd00, + 0x1d06fd00, 0x1c05fd00, 0x1b04fe00, 0x1a04fe00, + 0x1a03fe00, 0x1903fe00, 0x1802fe00, 0x1802fe00, + 0x1701fe00, 0x1601fe00, 0x1501fe00, 0x1500fe00, + 0x1400fe00, 0x1400fe00, 0x13fffe00, 0x12fffe00, + + 0x201200fe, 0x201100fe, 0x1f1100fe, 0x2010fffe, + 0x200ffffe, 0x1f0ffffe, 0x1f0efffe, 0x1e0dffff, + 0x1f0cfeff, 0x1e0cfeff, 0x1e0bfeff, 0x1e0afeff, + 0x1d0afeff, 0x1d09feff, 0x1c08feff, 0x1b08feff, + 0x1b07feff, 0x1a07feff, 0x1a06feff, 0x1a05feff, + 0x1805fe00, 0x1904fe00, 0x1704fe00, 0x1703fe00, + 0x1603fe00, 0x1602fe00, 0x1402fe00, 0x1402fe00, + 0x1401fe00, 0x1301fe00, 0x1201fe00, 0x1200fe00, + + 0x1c1202fe, 0x1c1102fe, 0x1b1102fe, 0x1c1001fe, + 0x1b1001fe, 0x1c0f01fe, 0x1b0f00fe, 0x1b0e00fe, + 0x1b0e00fe, 0x1b0d00fe, 0x1b0c00fe, 0x1a0cfffe, + 0x1a0bfffe, 0x1a0bfffe, 0x190afffe, 0x190afffe, + 0x1909fffe, 0x1709ffff, 0x1808ffff, 0x1708feff, + 0x1707feff, 0x1707feff, 0x1606feff, 0x1506feff, + 0x1505feff, 0x1505feff, 0x1404feff, 0x1404feff, + 0x1404feff, 0x1303feff, 0x1203feff, 0x1202feff, + + 0x191104fe, 0x191104fe, 0x191003fe, 0x191003fe, + 0x171003fe, 0x180f03fe, 0x180f02fe, 0x180e02fe, + 0x180e02fe, 0x180d01fe, 0x180d01fe, 0x180d01fe, + 0x170c01fe, 0x160c01fe, 0x170b00fe, 0x170b00fe, + 0x160a00fe, 0x160a00fe, 0x160a00fe, 0x150900fe, + 0x1509fffe, 0x1508fffe, 0x1508fffe, 0x1408fffe, + 0x1407fffe, 0x1307ffff, 0x1306ffff, 0x1206ffff, + 0x1206ffff, 0x1205ffff, 0x1205ffff, 0x1104feff, + + 0x161006ff, 0x161005ff, 0x161005ff, 0x160f05ff, + 0x160f04ff, 0x150f04ff, 0x150e04ff, 0x150e04ff, + 0x150e03ff, 0x150d03ff, 0x150d03ff, 0x150d02ff, + 0x140c02ff, 0x150c02fe, 0x150c02fe, 0x150b02fe, + 0x140b01fe, 0x140b01fe, 0x140a01fe, 0x140a01fe, + 0x140a01fe, 0x130900fe, 0x130900fe, 0x130900fe, + 0x130800fe, 0x120800fe, 0x120800fe, 0x120700fe, + 0x120700fe, 0x1107fffe, 0x1106fffe, 0x1106fffe, + + 0x140f0700, 0x140f0600, 0x140f0600, 0x140f0600, + 0x140e0600, 0x130e0500, 0x140e05ff, 0x130e05ff, + 0x140d05ff, 0x130d04ff, 0x130d04ff, 0x120d04ff, + 0x130c04ff, 0x130c03ff, 0x130c03ff, 0x120c03ff, + 0x120b03ff, 0x120b02ff, 0x120b02ff, 0x120a02ff, + 0x120a02ff, 0x110a02ff, 0x110a01ff, 0x120901ff, + 0x100901ff, 0x100901ff, 0x110801ff, 0x110801ff, + 0x100800ff, 0x100800ff, 0x100700ff, 0x100700fe, + + 0x120f0701, 0x120e0701, 0x120e0701, 0x120e0701, + 0x120e0600, 0x110e0600, 0x120d0600, 0x120d0600, + 0x120d0500, 0x120d0500, 0x110d0500, 0x110c0500, + 0x110c0500, 0x110c0400, 0x110c0400, 0x110b04ff, + 0x110b04ff, 0x110b04ff, 0x110b03ff, 0x110b03ff, + 0x110a03ff, 0x110a03ff, 0x100a03ff, 0x110a02ff, + 0x100902ff, 0x100902ff, 0x100902ff, 0x0f0902ff, + 0x0e0902ff, 0x100801ff, 0x0f0801ff, 0x0f0801ff, + + 0x100e0802, 0x100e0802, 0x110e0702, 0x110d0701, + 0x110d0701, 0x100d0701, 0x100d0701, 0x110d0601, + 0x110d0601, 0x110c0601, 0x110c0601, 0x100c0600, + 0x100c0500, 0x100c0500, 0x100c0500, 0x100b0500, + 0x100b0500, 0x100b0400, 0x100b0400, 0x0f0b0400, + 0x100a0400, 0x0f0a0400, 0x0f0a0400, 0x0f0a0300, + 0x0f0a03ff, 0x0f0903ff, 0x0f0903ff, 0x0f0903ff, + 0x0f0903ff, 0x0f0902ff, 0x0f0902ff, 0x0f0802ff +}; + +static const u32 bicubic8coefftab32_right[480] = { + 0x00000000, 0x00000001, 0x00000003, 0x00000004, + 0x00000006, 0x0000ff09, 0x0000ff0a, 0x0000ff0c, + 0x0000ff0f, 0x0000fe12, 0x0000fe14, 0x0000fe16, + 0x0000fd19, 0x0000fd1c, 0x0000fd1e, 0x0000fc21, + 0x0000fc24, 0x0000fc27, 0x0000fc29, 0x0000fb2c, + 0x0000fb2f, 0x0000fb31, 0x0000fb33, 0x0000fb36, + 0x0000fc38, 0x0000fc39, 0x0000fc3b, 0x0000fc3d, + 0x0000fd3e, 0x0000fe3f, 0x0000fe40, 0x0000ff40, + + 0x0000ff05, 0x0000ff06, 0x0000fe08, 0x0000fe09, + 0x0000fe0b, 0x0000fe0d, 0x0000fd0f, 0x0000fd11, + 0x0000fd13, 0x0000fd15, 0x0000fc17, 0x0000fc1a, + 0x0000fc1c, 0x0000fc1e, 0x0000fc20, 0x0000fc22, + 0x0000fc24, 0x0000fc26, 0x0000fc28, 0x0000fc2a, + 0x0000fc2c, 0x0000fc2f, 0x0000fd30, 0x0000fd31, + 0x0000fe33, 0x0000fe34, 0x0000ff35, 0x0000ff36, + 0x00000037, 0x00000138, 0x00ff0239, 0x00ff0339, + + 0x0000fe09, 0x0000fd0a, 0x0000fd0c, 0x0000fd0d, + 0x0000fd0f, 0x0000fd11, 0x0000fc12, 0x0000fc14, + 0x0000fc16, 0x0000fc18, 0x0000fc19, 0x0000fc1b, + 0x0000fc1d, 0x0000fc1e, 0x0000fc21, 0x0000fd22, + 0x0000fd23, 0x0000fd25, 0x0000fd27, 0x0000fe28, + 0x0000fe29, 0x0000fe2b, 0x0000ff2c, 0x00ffff2f, + 0x00ff002f, 0x00ff0130, 0x00ff0231, 0x00ff0232, + 0x00fe0432, 0x00fe0533, 0x00fe0633, 0x00fe0734, + + 0x0000fd0c, 0x0000fd0d, 0x0000fd0f, 0x0000fd10, + 0x0000fd11, 0x0000fd13, 0x0000fd14, 0x0000fd16, + 0x0000fd17, 0x0000fd19, 0x0000fd1b, 0x0000fd1c, + 0x0000fd1d, 0x0000fd1f, 0x0000fe20, 0x0000fe21, + 0x00fffe24, 0x00ffff24, 0x00ffff25, 0x00ffff27, + 0x00ff0027, 0x00ff0128, 0x00fe012a, 0x00fe022a, + 0x00fe032b, 0x00fe042c, 0x00fe052d, 0x00fe062d, + 0x00fd072e, 0x00fd082e, 0x00fd092e, 0x00fd0b2f, + + 0x0000fd0e, 0x0000fd0f, 0x0000fd10, 0x0000fd12, + 0x0000fd13, 0x0000fd14, 0x0000fd15, 0x0000fd17, + 0x0000fe18, 0x00fffe1a, 0x00fffe1b, 0x00fffe1c, + 0x00fffe1e, 0x00ffff1e, 0x00ffff1f, 0x00ff0021, + 0x00fe0022, 0x00fe0023, 0x00fe0124, 0x00fe0224, + 0x00fe0226, 0x00fe0326, 0x00fe0427, 0x00fd0528, + 0x00fd0628, 0x00fd0729, 0x00fd0829, 0x00fd0929, + 0x00fd0a2a, 0x00fd0b2a, 0x00fd0c2a, 0x00fd0d2a, + + 0x0000fd10, 0x0000fd11, 0x00fffe12, 0x00fffe13, + 0x00fffe14, 0x00fffe15, 0x00fffe16, 0x00fffe17, + 0x00ffff18, 0x00ffff19, 0x00feff1c, 0x00fe001b, + 0x00fe001d, 0x00fe001e, 0x00fe011e, 0x00fe011f, + 0x00fe0220, 0x00fe0321, 0x00fe0322, 0x00fd0423, + 0x00fd0524, 0x00fd0624, 0x00fd0626, 0x00fd0725, + 0x00fd0825, 0x00fd0926, 0x00fd0a26, 0x00fd0b26, + 0x00fd0c26, 0x00fd0d26, 0x00fd0e27, 0x00fd0f27, + + 0x00fffe11, 0x00fffe12, 0x00fffe13, 0x00ffff14, + 0x00ffff14, 0x00feff16, 0x00feff17, 0x00fe0017, + 0x00fe0018, 0x00fe0019, 0x00fe011a, 0x00fe011b, + 0x00fe021c, 0x00fe021c, 0x00fe031d, 0x00fd031f, + 0x00fd041f, 0x00fd0520, 0x00fd0520, 0x00fd0621, + 0x00fd0721, 0x00fd0822, 0x00fd0822, 0x00fd0923, + 0x00fd0a23, 0x00fd0b23, 0x00fd0b25, 0x00fe0c24, + 0x00fe0d24, 0x00fe0e24, 0x00fe0f24, 0x00fe1024, + + 0x00feff12, 0x00feff13, 0x00feff13, 0x00fe0014, + 0x00fe0015, 0x00fe0016, 0x00fe0116, 0x00fe0117, + 0x00fe0118, 0x00fe0218, 0x00fe0219, 0x00fe031a, + 0x00fe031b, 0x00fe041b, 0x00fd041d, 0x00fd051d, + 0x00fd061d, 0x00fd061f, 0x00fe071e, 0x00fe081e, + 0x00fe081f, 0x00fe091f, 0x00fe0a20, 0x00fe0a20, + 0x00fe0b21, 0x00fe0c21, 0x00fe0d21, 0x00fe0d22, + 0x00fe0e22, 0x00fe0f21, 0x00ff1021, 0x00ff1022, + + 0x00fe0012, 0x00fe0013, 0x00fe0113, 0x00fe0114, + 0x00fe0115, 0x00fe0215, 0x00fe0216, 0x00fe0217, + 0x00fe0317, 0x00fe0318, 0x00fe0418, 0x00fe0419, + 0x00fe0519, 0x00fe051a, 0x00fe061b, 0x00fe071b, + 0x00fe071c, 0x00fe081c, 0x00fe081d, 0x00fe091d, + 0x00fe0a1d, 0x00fe0a1d, 0x00fe0b1e, 0x00fe0c1e, + 0x00ff0c1e, 0x00ff0d1e, 0x00ff0e1f, 0x00ff0e1f, + 0x00ff0f1f, 0x00ff0f20, 0x0000101f, 0x0000111f, + + 0x00fe0212, 0x00fe0312, 0x00fe0313, 0x00fe0314, + 0x00fe0414, 0x00fe0414, 0x00fe0515, 0x00fe0516, + 0x00fe0516, 0x00fe0616, 0x00fe0617, 0x00fe0718, + 0x00fe0719, 0x00fe0818, 0x00ff0819, 0x00ff0918, + 0x00ff0919, 0x00ff0a19, 0x00ff0a19, 0x00ff0b1a, + 0x00ff0b1b, 0x00ff0c1a, 0x00000c1b, 0x00000d1b, + 0x00000d1c, 0x00000e1b, 0x00000e1d, 0x00010f1b, + 0x00010f1b, 0x0001101c, 0x0001101d, 0x0002111c, + + 0x00fe0412, 0x00fe0412, 0x00ff0512, 0x00ff0512, + 0x00ff0613, 0x00ff0613, 0x00ff0614, 0x00ff0714, + 0x00ff0714, 0x00ff0815, 0x00ff0815, 0x00ff0815, + 0x00ff0916, 0x00000916, 0x00000a16, 0x00000a16, + 0x00000a18, 0x00000b17, 0x00000b17, 0x00010c17, + 0x00010c18, 0x00010d18, 0x00010d18, 0x00010d19, + 0x00020e18, 0x00020e18, 0x00020f18, 0x00030f18, + 0x00030f18, 0x00031018, 0x00031018, 0x00041119, + + 0x00ff0610, 0x00ff0611, 0x00ff0611, 0x00ff0711, + 0x00000711, 0x00000712, 0x00000812, 0x00000812, + 0x00000813, 0x00000913, 0x00000913, 0x00000914, + 0x00010a14, 0x00010a14, 0x00010a14, 0x00010b14, + 0x00010b16, 0x00020b15, 0x00020c15, 0x00020c15, + 0x00020c15, 0x00020d17, 0x00030d16, 0x00030d16, + 0x00030e16, 0x00040e16, 0x00040e16, 0x00040f16, + 0x00040f16, 0x00050f17, 0x00051017, 0x00051017, + + 0x0000070f, 0x00000710, 0x00000710, 0x00000710, + 0x00000810, 0x00010811, 0x00010811, 0x00010911, + 0x00010911, 0x00010913, 0x00010913, 0x00020a12, + 0x00020a12, 0x00020a13, 0x00020b12, 0x00020b13, + 0x00030b13, 0x00030c13, 0x00030c13, 0x00030c14, + 0x00040c13, 0x00040d13, 0x00040d14, 0x00040d14, + 0x00050e14, 0x00050e14, 0x00050e14, 0x00050e14, + 0x00060f14, 0x00060f14, 0x00060f15, 0x00061015, + + 0x0001070f, 0x0001080f, 0x0001080f, 0x0001080f, + 0x00010811, 0x00020910, 0x00020910, 0x00020910, + 0x00020911, 0x00020a10, 0x00030a10, 0x00030a11, + 0x00030a11, 0x00030b11, 0x00030b11, 0x00040b12, + 0x00040b12, 0x00040c11, 0x00040c12, 0x00040c12, + 0x00050c12, 0x00050c12, 0x00050d12, 0x00050d12, + 0x00060d13, 0x00060d13, 0x00060e12, 0x00060e13, + 0x00070e13, 0x00070e13, 0x00070f13, 0x00070f13, + + 0x0002080e, 0x0002080e, 0x0002080e, 0x00020810, + 0x0002090f, 0x0003090f, 0x0003090f, 0x0003090f, + 0x0003090f, 0x00030a0f, 0x00030a0f, 0x00040a10, + 0x00040a11, 0x00040b10, 0x00040b10, 0x00040b11, + 0x00050b10, 0x00050b11, 0x00050c10, 0x00050c11, + 0x00050c11, 0x00060c11, 0x00060c11, 0x00060d11, + 0x00060d12, 0x00070d12, 0x00070d12, 0x00070e11, + 0x00070e11, 0x00070e12, 0x00080e11, 0x00080e12 +}; + +static const u32 bicubic4coefftab32[480] = { + 0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd, + 0x00063dfd, 0xff083dfc, 0xff0a3bfc, 0xff0c39fc, + 0xff0e37fc, 0xfe1136fb, 0xfe1433fb, 0xfe1631fb, + 0xfd192ffb, 0xfd1c2cfb, 0xfd1e29fc, 0xfc2127fc, + 0xfc2424fc, 0xfc2721fc, 0xfc291efd, 0xfb2c1cfd, + 0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfb3611fe, + 0xfc370eff, 0xfc390cff, 0xfc3b0aff, 0xfc3d08ff, + 0xfd3d0600, 0xfd3f0400, 0xfe3f0300, 0xff400100, + + 0xfe053904, 0xfe063903, 0xfe083901, 0xfe0a3800, + 0xfd0b3800, 0xfe0d36ff, 0xfd0f35ff, 0xfd1134fe, + 0xfd1332fe, 0xfd1531fd, 0xfc1730fd, 0xfc1a2efc, + 0xfc1c2cfc, 0xfc1e2afc, 0xfc2028fc, 0xfc2226fc, + 0xfc2424fc, 0xfc2622fc, 0xfc2820fc, 0xfc2a1efc, + 0xfc2c1cfc, 0xfc2e1afc, 0xfd3017fc, 0xfd3115fd, + 0xfe3213fd, 0xfe3411fd, 0xff350ffd, 0xff360dfe, + 0x00370bfe, 0x013809fe, 0x023808fe, 0x033806ff, + + 0xfd093208, 0xfd0a3207, 0xfd0c3205, 0xfd0d3204, + 0xfc0f3203, 0xfc113102, 0xfc123002, 0xfc143000, + 0xfc152f00, 0xfc172d00, 0xfc192cff, 0xfc1b2bfe, + 0xfc1d29fe, 0xfc1e28fe, 0xfc2027fd, 0xfd2125fd, + 0xfd2323fd, 0xfd2521fd, 0xfd2720fc, 0xfe281efc, + 0xfe291dfc, 0xfe2b1bfc, 0xff2c19fc, 0x002d17fc, + 0x002e16fc, 0x012f14fc, 0x022f12fd, 0x023110fd, + 0x03310ffd, 0x05310dfd, 0x06320bfd, 0x07320afd, + + 0xfc0c2d0b, 0xfc0d2d0a, 0xfc0e2d09, 0xfc102d07, + 0xfc112c07, 0xfc132c05, 0xfc142c04, 0xfc162b03, + 0xfc172a03, 0xfc192a01, 0xfc1a2901, 0xfd1b2800, + 0xfd1c2700, 0xfd1e2500, 0xfe1f24ff, 0xfe2023ff, + 0xfe2222fe, 0xff2320fe, 0xff241ffe, 0x00251efd, + 0x00271cfd, 0x01271bfd, 0x01281afd, 0x022918fd, + 0x032a16fd, 0x032b15fd, 0x042b14fd, 0x052c12fd, + 0x072c10fd, 0x082c0ffd, 0x092c0efd, 0x0a2c0dfd, + + 0xfd0d290d, 0xfd0e290c, 0xfd0f290b, 0xfd11280a, + 0xfd122809, 0xfd132808, 0xfd142807, 0xfd162706, + 0xfd172705, 0xfd192604, 0xfe1a2503, 0xfe1b2502, + 0xfe1c2402, 0xfe1d2302, 0xff1e2201, 0xff1f2101, + 0x00202000, 0x00211f00, 0x01221eff, 0x02221dff, + 0x02241cfe, 0x03241bfe, 0x042519fe, 0x042618fe, + 0x052617fe, 0x062716fd, 0x072714fe, 0x082713fe, + 0x092812fd, 0x0a2811fd, 0x0b2810fd, 0x0c280ffd, + + 0xfd0f250f, 0xfd10250e, 0xfd11250d, 0xfd12250c, + 0xfd13250b, 0xfe13250a, 0xfe152409, 0xfe162408, + 0xfe172308, 0xff182306, 0xff192305, 0xff1a2205, + 0x001b2104, 0x001c2103, 0x001d2003, 0x011e1f02, + 0x011f1f01, 0x021f1e01, 0x03201d00, 0x03211c00, + 0x04211b00, 0x05221aff, 0x062219ff, 0x062318ff, + 0x082316ff, 0x082316ff, 0x092415fe, 0x0a2414fe, + 0x0b2413fe, 0x0c2412fe, 0x0d2411fe, 0x0e2410fe, + + 0xfe10230f, 0xfe11230e, 0xfe12220e, 0xfe13220d, + 0xfe14220c, 0xff14220b, 0xff15220a, 0xff16210a, + 0x00162109, 0x00172108, 0x00182008, 0x01192006, + 0x011a1f06, 0x021a1f05, 0x021b1e05, 0x031c1d04, + 0x031d1d03, 0x041d1c03, 0x041e1b03, 0x051e1b02, + 0x061f1a01, 0x06201901, 0x07201801, 0x08201800, + 0x09201700, 0x0a211500, 0x0b2115ff, 0x0c2114ff, + 0x0c2213ff, 0x0d2212ff, 0x0e2211ff, 0x0f2211fe, + + 0xff112010, 0xff12200f, 0xff12200f, 0xff13200e, + 0x0013200d, 0x0014200c, 0x00151f0c, 0x00161f0b, + 0x01161f0a, 0x01171e0a, 0x02171e09, 0x02181e08, + 0x03191d07, 0x03191d07, 0x041a1c06, 0x041b1c05, + 0x051b1b05, 0x051c1b04, 0x061c1a04, 0x071c1a03, + 0x071d1903, 0x081e1802, 0x091d1802, 0x091e1702, + 0x0a1f1601, 0x0b1f1600, 0x0b1f1501, 0x0c201400, + 0x0d1f1400, 0x0e2013ff, 0x0f1f1200, 0x102011ff, + + 0x00111f10, 0x00121e10, 0x00131e0f, 0x00131e0f, + 0x01131e0e, 0x01141e0d, 0x01151d0d, 0x02151d0c, + 0x02161d0b, 0x03161d0a, 0x03171c0a, 0x04171c09, + 0x04181c08, 0x05181b08, 0x05191b07, 0x06191a07, + 0x061a1a06, 0x071a1906, 0x071b1905, 0x081b1805, + 0x081c1804, 0x091c1704, 0x0a1c1703, 0x0a1d1603, + 0x0b1d1602, 0x0c1d1502, 0x0c1d1502, 0x0d1e1401, + 0x0e1d1401, 0x0e1e1301, 0x0f1e1300, 0x101e1200, + + 0x02111c11, 0x02121c10, 0x02131b10, 0x03131b0f, + 0x03131b0f, 0x03141b0e, 0x04141b0d, 0x04151a0d, + 0x05151a0c, 0x05151a0c, 0x05161a0b, 0x0616190b, + 0x0616190b, 0x0716190a, 0x0717180a, 0x08171809, + 0x08181808, 0x09181708, 0x09181708, 0x0a181707, + 0x0a191607, 0x0b191606, 0x0b1a1605, 0x0c1a1505, + 0x0c1a1505, 0x0d1a1504, 0x0d1b1404, 0x0e1b1403, + 0x0f1b1303, 0x0f1b1303, 0x101b1302, 0x101c1202, + + 0x04111a11, 0x04121911, 0x04131910, 0x0513190f, + 0x0513190f, 0x0513190f, 0x0613190e, 0x0614180e, + 0x0714180d, 0x0714180d, 0x0715180c, 0x0814180c, + 0x0815170c, 0x0816170b, 0x0916170a, 0x0916170a, + 0x0a16160a, 0x0a171609, 0x0a171609, 0x0b171608, + 0x0b171509, 0x0c171508, 0x0c181507, 0x0d171507, + 0x0d181407, 0x0e181406, 0x0e181406, 0x0e191306, + 0x0f191305, 0x0f191305, 0x10191304, 0x10191205, + + 0x05121811, 0x06121810, 0x06121810, 0x06131710, + 0x0713170f, 0x0713170f, 0x0713170f, 0x0813170e, + 0x0813170e, 0x0814170d, 0x0914160d, 0x0914160d, + 0x0914160d, 0x0a14160c, 0x0a15160b, 0x0a15150c, + 0x0b15150b, 0x0b15150b, 0x0b16150a, 0x0c15150a, + 0x0c16140a, 0x0d161409, 0x0d161409, 0x0d171408, + 0x0e161408, 0x0e171308, 0x0e171308, 0x0f171307, + 0x0f171307, 0x10171306, 0x10181206, 0x10181206, + + 0x07111711, 0x07121710, 0x07121611, 0x08121610, + 0x08121610, 0x0813160f, 0x0912160f, 0x0913160e, + 0x0913160e, 0x0913160e, 0x0a14150d, 0x0a14150d, + 0x0a14150d, 0x0b14150c, 0x0b14150c, 0x0b14150c, + 0x0c14140c, 0x0c15140b, 0x0c15140b, 0x0c15140b, + 0x0d15140a, 0x0d15140a, 0x0d15140a, 0x0e161309, + 0x0e161309, 0x0e161309, 0x0f151309, 0x0f161308, + 0x0f161209, 0x10161208, 0x10161208, 0x10171207, + + 0x0a111411, 0x0b111410, 0x0b111410, 0x0b111410, + 0x0b111410, 0x0b12140f, 0x0b12140f, 0x0c12130f, + 0x0c12130f, 0x0c12130f, 0x0c12130f, 0x0c12130f, + 0x0d12130e, 0x0d12130e, 0x0d12130e, 0x0d13130d, + 0x0d13130d, 0x0d13130d, 0x0e12130d, 0x0e13120d, + 0x0e13120d, 0x0e13120d, 0x0e13120d, 0x0f13120c, + 0x0f13120c, 0x0f13120c, 0x0f14120b, 0x0f14120b, + 0x1013120b, 0x1013120b, 0x1013120b, 0x1014110b, + + 0x0c111310, 0x0c111310, 0x0c111310, 0x0d101310, + 0x0d101310, 0x0d111210, 0x0d111210, 0x0d111210, + 0x0d12120f, 0x0d12120f, 0x0d12120f, 0x0d12120f, + 0x0e11120f, 0x0e12120e, 0x0e12120e, 0x0e12120e, + 0x0e12120e, 0x0e12120e, 0x0e12120e, 0x0e12120e, + 0x0f11120e, 0x0f12120d, 0x0f12120d, 0x0f12120d, + 0x0f12120d, 0x0f12110e, 0x0f12110e, 0x0f12110e, + 0x1012110d, 0x1012110d, 0x1013110c, 0x1013110c, +}; + +static int sun8i_vi_scaler_coef_index(unsigned int step) +{ + unsigned int scale, int_part, float_part; + + scale = step >> (SUN8I_VI_SCALER_SCALE_FRAC - 3); + int_part = scale >> 3; + float_part = scale & 0x7; + + switch (int_part) { + case 0: + return 0; + case 1: + return float_part; + case 2: + return 8 + (float_part >> 1); + case 3: + return 12; + case 4: + return 13; + default: + return 14; + } +} + +static void sun8i_vi_scaler_set_coeff(struct regmap *map, int layer, + u32 hstep, u32 vstep, + const struct drm_format_info *format) +{ + const u32 *ch_left, *ch_right, *cy; + int offset, i; + + if (format->hsub == 1 && format->vsub == 1) { + ch_left = lan3coefftab32_left; + ch_right = lan3coefftab32_right; + cy = lan2coefftab32; + } else { + ch_left = bicubic8coefftab32_left; + ch_right = bicubic8coefftab32_right; + cy = bicubic4coefftab32; + } + + offset = sun8i_vi_scaler_coef_index(hstep) * + SUN8I_VI_SCALER_COEFF_COUNT; + for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++) { + regmap_write(map, SUN8I_SCALER_VSU_YHCOEFF0(layer, i), + lan3coefftab32_left[offset + i]); + regmap_write(map, SUN8I_SCALER_VSU_YHCOEFF1(layer, i), + lan3coefftab32_right[offset + i]); + regmap_write(map, SUN8I_SCALER_VSU_CHCOEFF0(layer, i), + ch_left[offset + i]); + regmap_write(map, SUN8I_SCALER_VSU_CHCOEFF1(layer, i), + ch_right[offset + i]); + } + + offset = sun8i_vi_scaler_coef_index(hstep) * + SUN8I_VI_SCALER_COEFF_COUNT; + for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++) { + regmap_write(map, SUN8I_SCALER_VSU_YVCOEFF(layer, i), + lan2coefftab32[offset + i]); + regmap_write(map, SUN8I_SCALER_VSU_CVCOEFF(layer, i), + cy[offset + i]); + } +} + +void sun8i_vi_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable) +{ + u32 val; + + if (enable) + val = SUN8I_SCALER_VSU_CTRL_EN | + SUN8I_SCALER_VSU_CTRL_COEFF_RDY; + else + val = 0; + + regmap_write(mixer->engine.regs, SUN8I_SCALER_VSU_CTRL(layer), val); +} + +void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer, + u32 src_w, u32 src_h, u32 dst_w, u32 dst_h, + u32 hscale, u32 vscale, u32 hphase, u32 vphase, + const struct drm_format_info *format) +{ + u32 chphase, cvphase; + u32 insize, outsize; + + hphase <<= SUN8I_VI_SCALER_PHASE_FRAC - 16; + vphase <<= SUN8I_VI_SCALER_PHASE_FRAC - 16; + hscale <<= SUN8I_VI_SCALER_SCALE_FRAC - 16; + vscale <<= SUN8I_VI_SCALER_SCALE_FRAC - 16; + + insize = SUN8I_VI_SCALER_SIZE(src_w, src_h); + outsize = SUN8I_VI_SCALER_SIZE(dst_w, dst_h); + + /* + * This is chroma V/H phase calculation as it appears in + * BSP driver. There is no detailed explanation. YUV 420 + * chroma is threated specialy for some reason. + */ + if (format->hsub == 2 && format->vsub == 2) { + chphase = hphase >> 1; + cvphase = (vphase >> 1) - + (1UL << (SUN8I_VI_SCALER_SCALE_FRAC - 2)); + } else { + chphase = hphase; + cvphase = vphase; + } + + regmap_write(mixer->engine.regs, + SUN8I_SCALER_VSU_OUTSIZE(layer), outsize); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_VSU_YINSIZE(layer), insize); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_VSU_YHSTEP(layer), hscale); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_VSU_YVSTEP(layer), vscale); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_VSU_YHPHASE(layer), hphase); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_VSU_YVPHASE(layer), vphase); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_VSU_CINSIZE(layer), + SUN8I_VI_SCALER_SIZE(src_w / format->hsub, + src_h / format->vsub)); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_VSU_CHSTEP(layer), + hscale / format->hsub); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_VSU_CVSTEP(layer), + vscale / format->vsub); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_VSU_CHPHASE(layer), chphase); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_VSU_CVPHASE(layer), cvphase); + sun8i_vi_scaler_set_coeff(mixer->engine.regs, layer, + hscale, vscale, format); +} diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h new file mode 100644 index 000000000000..a595ab643a5a --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2017 Jernej Skrabec <jernej.skrabec@siol.net> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef _SUN8I_VI_SCALER_H_ +#define _SUN8I_VI_SCALER_H_ + +#include <drm/drm_fourcc.h> +#include "sun8i_mixer.h" + +/* this two macros assumes 16 fractional bits which is standard in DRM */ +#define SUN8I_VI_SCALER_SCALE_MIN 1 +#define SUN8I_VI_SCALER_SCALE_MAX ((1UL << 20) - 1) + +#define SUN8I_VI_SCALER_SCALE_FRAC 20 +#define SUN8I_VI_SCALER_PHASE_FRAC 20 +#define SUN8I_VI_SCALER_COEFF_COUNT 32 +#define SUN8I_VI_SCALER_SIZE(w, h) (((h) - 1) << 16 | ((w) - 1)) + +#define SUN8I_SCALER_VSU_CTRL(ch) (0x20000 + 0x20000 * (ch) + 0x0) +#define SUN8I_SCALER_VSU_OUTSIZE(ch) (0x20000 + 0x20000 * (ch) + 0x40) +#define SUN8I_SCALER_VSU_YINSIZE(ch) (0x20000 + 0x20000 * (ch) + 0x80) +#define SUN8I_SCALER_VSU_YHSTEP(ch) (0x20000 + 0x20000 * (ch) + 0x88) +#define SUN8I_SCALER_VSU_YVSTEP(ch) (0x20000 + 0x20000 * (ch) + 0x8c) +#define SUN8I_SCALER_VSU_YHPHASE(ch) (0x20000 + 0x20000 * (ch) + 0x90) +#define SUN8I_SCALER_VSU_YVPHASE(ch) (0x20000 + 0x20000 * (ch) + 0x98) +#define SUN8I_SCALER_VSU_CINSIZE(ch) (0x20000 + 0x20000 * (ch) + 0xc0) +#define SUN8I_SCALER_VSU_CHSTEP(ch) (0x20000 + 0x20000 * (ch) + 0xc8) +#define SUN8I_SCALER_VSU_CVSTEP(ch) (0x20000 + 0x20000 * (ch) + 0xcc) +#define SUN8I_SCALER_VSU_CHPHASE(ch) (0x20000 + 0x20000 * (ch) + 0xd0) +#define SUN8I_SCALER_VSU_CVPHASE(ch) (0x20000 + 0x20000 * (ch) + 0xd8) +#define SUN8I_SCALER_VSU_YHCOEFF0(ch, i) \ + (0x20000 + 0x20000 * (ch) + 0x200 + 0x4 * (i)) +#define SUN8I_SCALER_VSU_YHCOEFF1(ch, i) \ + (0x20000 + 0x20000 * (ch) + 0x300 + 0x4 * (i)) +#define SUN8I_SCALER_VSU_YVCOEFF(ch, i) \ + (0x20000 + 0x20000 * (ch) + 0x400 + 0x4 * (i)) +#define SUN8I_SCALER_VSU_CHCOEFF0(ch, i) \ + (0x20000 + 0x20000 * (ch) + 0x600 + 0x4 * (i)) +#define SUN8I_SCALER_VSU_CHCOEFF1(ch, i) \ + (0x20000 + 0x20000 * (ch) + 0x700 + 0x4 * (i)) +#define SUN8I_SCALER_VSU_CVCOEFF(ch, i) \ + (0x20000 + 0x20000 * (ch) + 0x800 + 0x4 * (i)) + +#define SUN8I_SCALER_VSU_CTRL_EN BIT(0) +#define SUN8I_SCALER_VSU_CTRL_COEFF_RDY BIT(4) + +void sun8i_vi_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable); +void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer, + u32 src_w, u32 src_h, u32 dst_w, u32 dst_h, + u32 hscale, u32 vscale, u32 hphase, u32 vphase, + const struct drm_format_info *format); + +#endif diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 52552b9b89ef..f157bc675269 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -120,7 +120,7 @@ static int tegra_atomic_commit(struct drm_device *drm, static const struct drm_mode_config_funcs tegra_drm_mode_funcs = { .fb_create = tegra_fb_create, #ifdef CONFIG_DRM_FBDEV_EMULATION - .output_poll_changed = tegra_fb_output_poll_changed, + .output_poll_changed = drm_fb_helper_output_poll_changed, #endif .atomic_check = drm_atomic_helper_check, .atomic_commit = tegra_atomic_commit, @@ -286,15 +286,6 @@ static void tegra_drm_context_free(struct tegra_drm_context *context) kfree(context); } -static void tegra_drm_lastclose(struct drm_device *drm) -{ -#ifdef CONFIG_DRM_FBDEV_EMULATION - struct tegra_drm *tegra = drm->dev_private; - - tegra_fbdev_restore_mode(tegra->fbdev); -#endif -} - static struct host1x_bo * host1x_bo_lookup(struct drm_file *file, u32 handle) { @@ -1100,7 +1091,7 @@ static struct drm_driver tegra_drm_driver = { .unload = tegra_drm_unload, .open = tegra_drm_open, .postclose = tegra_drm_postclose, - .lastclose = tegra_drm_lastclose, + .lastclose = drm_fb_helper_lastclose, #if defined(CONFIG_DEBUG_FS) .debugfs_init = tegra_debugfs_init, diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index ddae331ad8b6..0009f6ea21b6 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -188,10 +188,6 @@ int tegra_drm_fb_init(struct drm_device *drm); void tegra_drm_fb_exit(struct drm_device *drm); void tegra_drm_fb_suspend(struct drm_device *drm); void tegra_drm_fb_resume(struct drm_device *drm); -#ifdef CONFIG_DRM_FBDEV_EMULATION -void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev); -void tegra_fb_output_poll_changed(struct drm_device *drm); -#endif extern struct platform_driver tegra_dc_driver; extern struct platform_driver tegra_hdmi_driver; diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index 80540c1c66dc..8dfe3c6c217e 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -361,20 +361,6 @@ static void tegra_fbdev_exit(struct tegra_fbdev *fbdev) drm_fb_helper_fini(&fbdev->base); tegra_fbdev_free(fbdev); } - -void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev) -{ - if (fbdev) - drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev->base); -} - -void tegra_fb_output_poll_changed(struct drm_device *drm) -{ - struct tegra_drm *tegra = drm->dev_private; - - if (tegra->fbdev) - drm_fb_helper_hotplug_event(&tegra->fbdev->base); -} #endif int tegra_drm_fb_prepare(struct drm_device *drm) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index 6ef4d1a1e3a9..8bf6bb93dc79 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -51,12 +51,8 @@ struct tilcdc_crtc { ktime_t last_vblank; unsigned int hvtotal_us; - struct drm_framebuffer *curr_fb; struct drm_framebuffer *next_fb; - /* for deferred fb unref's: */ - struct drm_flip_work unref_work; - /* Only set if an external encoder is connected */ bool simulate_vesa_sync; @@ -70,20 +66,8 @@ struct tilcdc_crtc { }; #define to_tilcdc_crtc(x) container_of(x, struct tilcdc_crtc, base) -static void unref_worker(struct drm_flip_work *work, void *val) -{ - struct tilcdc_crtc *tilcdc_crtc = - container_of(work, struct tilcdc_crtc, unref_work); - struct drm_device *dev = tilcdc_crtc->base.dev; - - mutex_lock(&dev->mode_config.mutex); - drm_framebuffer_put(val); - mutex_unlock(&dev->mode_config.mutex); -} - static void set_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb) { - struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); struct drm_device *dev = crtc->dev; struct tilcdc_drm_private *priv = dev->dev_private; struct drm_gem_cma_object *gem; @@ -108,12 +92,6 @@ static void set_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb) dma_base_and_ceiling = (u64)end << 32 | start; tilcdc_write64(dev, LCDC_DMA_FB_BASE_ADDR_0_REG, dma_base_and_ceiling); - - if (tilcdc_crtc->curr_fb) - drm_flip_work_queue(&tilcdc_crtc->unref_work, - tilcdc_crtc->curr_fb); - - tilcdc_crtc->curr_fb = fb; } /* @@ -294,7 +272,7 @@ static void tilcdc_crtc_set_clk(struct drm_crtc *crtc) LCDC_V2_CORE_CLK_EN); } -uint tilcdc_mode_hvtotal(const struct drm_display_mode *mode) +static uint tilcdc_mode_hvtotal(const struct drm_display_mode *mode) { return (uint) div_u64(1000llu * mode->htotal * mode->vtotal, mode->clock); @@ -464,8 +442,6 @@ static void tilcdc_crtc_set_mode(struct drm_crtc *crtc) set_scanout(crtc, fb); - drm_framebuffer_get(fb); - crtc->hwmode = crtc->state->adjusted_mode; tilcdc_crtc->hvtotal_us = @@ -524,7 +500,6 @@ static void tilcdc_crtc_off(struct drm_crtc *crtc, bool shutdown) { struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); struct drm_device *dev = crtc->dev; - struct tilcdc_drm_private *priv = dev->dev_private; int ret; mutex_lock(&tilcdc_crtc->enable_lock); @@ -554,20 +529,6 @@ static void tilcdc_crtc_off(struct drm_crtc *crtc, bool shutdown) pm_runtime_put_sync(dev->dev); - if (tilcdc_crtc->next_fb) { - drm_flip_work_queue(&tilcdc_crtc->unref_work, - tilcdc_crtc->next_fb); - tilcdc_crtc->next_fb = NULL; - } - - if (tilcdc_crtc->curr_fb) { - drm_flip_work_queue(&tilcdc_crtc->unref_work, - tilcdc_crtc->curr_fb); - tilcdc_crtc->curr_fb = NULL; - } - - drm_flip_work_commit(&tilcdc_crtc->unref_work, priv->wq); - tilcdc_crtc->enabled = false; mutex_unlock(&tilcdc_crtc->enable_lock); } @@ -614,7 +575,6 @@ out: static void tilcdc_crtc_destroy(struct drm_crtc *crtc) { - struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); struct tilcdc_drm_private *priv = crtc->dev->dev_private; tilcdc_crtc_shutdown(crtc); @@ -623,7 +583,6 @@ static void tilcdc_crtc_destroy(struct drm_crtc *crtc) of_node_put(crtc->port); drm_crtc_cleanup(crtc); - drm_flip_work_cleanup(&tilcdc_crtc->unref_work); } int tilcdc_crtc_update_fb(struct drm_crtc *crtc, @@ -638,9 +597,6 @@ int tilcdc_crtc_update_fb(struct drm_crtc *crtc, return -EBUSY; } - drm_framebuffer_get(fb); - - crtc->primary->fb = fb; tilcdc_crtc->event = event; mutex_lock(&tilcdc_crtc->enable_lock); @@ -936,8 +892,6 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc) now = ktime_get(); - drm_flip_work_commit(&tilcdc_crtc->unref_work, priv->wq); - spin_lock_irqsave(&tilcdc_crtc->irq_lock, flags); tilcdc_crtc->last_vblank = now; @@ -1064,9 +1018,6 @@ int tilcdc_crtc_create(struct drm_device *dev) init_waitqueue_head(&tilcdc_crtc->frame_done_wq); - drm_flip_work_init(&tilcdc_crtc->unref_work, - "unref", unref_worker); - spin_lock_init(&tilcdc_crtc->irq_lock); INIT_WORK(&tilcdc_crtc->recover_work, tilcdc_crtc_recover_work); diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index bc4feb3a84b9..1afde61f1247 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -69,12 +69,6 @@ static struct drm_framebuffer *tilcdc_fb_create(struct drm_device *dev, return drm_gem_fb_create(dev, file_priv, mode_cmd); } -static void tilcdc_fb_output_poll_changed(struct drm_device *dev) -{ - struct tilcdc_drm_private *priv = dev->dev_private; - drm_fbdev_cma_hotplug_event(priv->fbdev); -} - static int tilcdc_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) { @@ -146,7 +140,7 @@ static int tilcdc_commit(struct drm_device *dev, static const struct drm_mode_config_funcs mode_config_funcs = { .fb_create = tilcdc_fb_create, - .output_poll_changed = tilcdc_fb_output_poll_changed, + .output_poll_changed = drm_fb_helper_output_poll_changed, .atomic_check = tilcdc_atomic_check, .atomic_commit = tilcdc_commit, }; @@ -198,8 +192,7 @@ static void tilcdc_fini(struct drm_device *dev) drm_kms_helper_poll_fini(dev); - if (priv->fbdev) - drm_fbdev_cma_fini(priv->fbdev); + drm_fb_cma_fbdev_fini(dev); drm_irq_uninstall(dev); drm_mode_config_cleanup(dev); @@ -405,12 +398,9 @@ static int tilcdc_init(struct drm_driver *ddrv, struct device *dev) drm_mode_config_reset(ddev); - priv->fbdev = drm_fbdev_cma_init(ddev, bpp, - ddev->mode_config.num_connector); - if (IS_ERR(priv->fbdev)) { - ret = PTR_ERR(priv->fbdev); + ret = drm_fb_cma_fbdev_init(ddev, bpp, 0); + if (ret) goto init_failed; - } drm_kms_helper_poll_init(ddev); @@ -427,12 +417,6 @@ init_failed: return ret; } -static void tilcdc_lastclose(struct drm_device *dev) -{ - struct tilcdc_drm_private *priv = dev->dev_private; - drm_fbdev_cma_restore_mode(priv->fbdev); -} - static irqreturn_t tilcdc_irq(int irq, void *arg) { struct drm_device *dev = arg; @@ -537,7 +521,7 @@ DEFINE_DRM_GEM_CMA_FOPS(fops); static struct drm_driver tilcdc_driver = { .driver_features = (DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC), - .lastclose = tilcdc_lastclose, + .lastclose = drm_fb_helper_lastclose, .irq_handler = tilcdc_irq, .gem_free_object_unlocked = drm_gem_cma_free_object, .gem_print_info = drm_gem_cma_print_info, diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.h b/drivers/gpu/drm/tilcdc/tilcdc_drv.h index 8caa11bc7aec..ead512216669 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.h +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.h @@ -79,8 +79,6 @@ struct tilcdc_drm_private { struct workqueue_struct *wq; - struct drm_fbdev_cma *fbdev; - struct drm_crtc *crtc; unsigned int num_encoders; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_regs.h b/drivers/gpu/drm/tilcdc/tilcdc_regs.h index 9d528c0a67a4..5048ebb86835 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_regs.h +++ b/drivers/gpu/drm/tilcdc/tilcdc_regs.h @@ -133,7 +133,7 @@ static inline void tilcdc_write64(struct drm_device *dev, u32 reg, u64 data) struct tilcdc_drm_private *priv = dev->dev_private; volatile void __iomem *addr = priv->mmio + reg; -#ifdef iowrite64 +#if defined(iowrite64) && !defined(iowrite64_is_nonatomic) iowrite64(data, addr); #else __iowmb(); diff --git a/drivers/gpu/drm/tinydrm/Kconfig b/drivers/gpu/drm/tinydrm/Kconfig index 2e790e7dced5..90c5bd5ef81b 100644 --- a/drivers/gpu/drm/tinydrm/Kconfig +++ b/drivers/gpu/drm/tinydrm/Kconfig @@ -12,6 +12,16 @@ menuconfig DRM_TINYDRM config TINYDRM_MIPI_DBI tristate +config TINYDRM_ILI9225 + tristate "DRM support for ILI9225 display panels" + depends on DRM_TINYDRM && SPI + select TINYDRM_MIPI_DBI + help + DRM driver for the following Ilitek ILI9225 panels: + * No-name 2.2" color screen module + + If M is selected the module will be called ili9225. + config TINYDRM_MI0283QT tristate "DRM support for MI0283QT" depends on DRM_TINYDRM && SPI diff --git a/drivers/gpu/drm/tinydrm/Makefile b/drivers/gpu/drm/tinydrm/Makefile index 0c184bd1bb59..8aeee532474f 100644 --- a/drivers/gpu/drm/tinydrm/Makefile +++ b/drivers/gpu/drm/tinydrm/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_DRM_TINYDRM) += core/ obj-$(CONFIG_TINYDRM_MIPI_DBI) += mipi-dbi.o # Displays +obj-$(CONFIG_TINYDRM_ILI9225) += ili9225.o obj-$(CONFIG_TINYDRM_MI0283QT) += mi0283qt.o obj-$(CONFIG_TINYDRM_REPAPER) += repaper.o obj-$(CONFIG_TINYDRM_ST7586) += st7586.o diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c b/drivers/gpu/drm/tinydrm/core/tinydrm-core.c index 1a8a57cad431..4c6616278c48 100644 --- a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c +++ b/drivers/gpu/drm/tinydrm/core/tinydrm-core.c @@ -10,6 +10,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/tinydrm/tinydrm.h> #include <linux/device.h> @@ -36,23 +37,6 @@ */ /** - * tinydrm_lastclose - DRM lastclose helper - * @drm: DRM device - * - * This function ensures that fbdev is restored when drm_lastclose() is called - * on the last drm_release(). Drivers can use this as their - * &drm_driver->lastclose callback. - */ -void tinydrm_lastclose(struct drm_device *drm) -{ - struct tinydrm_device *tdev = drm->dev_private; - - DRM_DEBUG_KMS("\n"); - drm_fbdev_cma_restore_mode(tdev->fbdev_cma); -} -EXPORT_SYMBOL(tinydrm_lastclose); - -/** * tinydrm_gem_cma_prime_import_sg_table - Produce a CMA GEM object from * another driver's scatter/gather table of pinned pages * @drm: DRM device to import into @@ -214,35 +198,24 @@ EXPORT_SYMBOL(devm_tinydrm_init); static int tinydrm_register(struct tinydrm_device *tdev) { struct drm_device *drm = tdev->drm; - int bpp = drm->mode_config.preferred_depth; - struct drm_fbdev_cma *fbdev; int ret; ret = drm_dev_register(tdev->drm, 0); if (ret) return ret; - fbdev = drm_fbdev_cma_init_with_funcs(drm, bpp ? bpp : 32, - drm->mode_config.num_connector, - tdev->fb_funcs); - if (IS_ERR(fbdev)) - DRM_ERROR("Failed to initialize fbdev: %ld\n", PTR_ERR(fbdev)); - else - tdev->fbdev_cma = fbdev; + ret = drm_fb_cma_fbdev_init_with_funcs(drm, 0, 0, tdev->fb_funcs); + if (ret) + DRM_ERROR("Failed to initialize fbdev: %d\n", ret); return 0; } static void tinydrm_unregister(struct tinydrm_device *tdev) { - struct drm_fbdev_cma *fbdev_cma = tdev->fbdev_cma; - drm_atomic_helper_shutdown(tdev->drm); - /* don't restore fbdev in lastclose, keep pipeline disabled */ - tdev->fbdev_cma = NULL; + drm_fb_cma_fbdev_fini(tdev->drm); drm_dev_unregister(tdev->drm); - if (fbdev_cma) - drm_fbdev_cma_fini(fbdev_cma); } static void devm_tinydrm_register_release(void *data) @@ -292,71 +265,4 @@ void tinydrm_shutdown(struct tinydrm_device *tdev) } EXPORT_SYMBOL(tinydrm_shutdown); -/** - * tinydrm_suspend - Suspend tinydrm - * @tdev: tinydrm device - * - * Used in driver PM operations to suspend tinydrm. - * Suspends fbdev and DRM. - * Resume with tinydrm_resume(). - * - * Returns: - * Zero on success, negative error code on failure. - */ -int tinydrm_suspend(struct tinydrm_device *tdev) -{ - struct drm_atomic_state *state; - - if (tdev->suspend_state) { - DRM_ERROR("Failed to suspend: state already set\n"); - return -EINVAL; - } - - drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 1); - state = drm_atomic_helper_suspend(tdev->drm); - if (IS_ERR(state)) { - drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 0); - return PTR_ERR(state); - } - - tdev->suspend_state = state; - - return 0; -} -EXPORT_SYMBOL(tinydrm_suspend); - -/** - * tinydrm_resume - Resume tinydrm - * @tdev: tinydrm device - * - * Used in driver PM operations to resume tinydrm. - * Suspend with tinydrm_suspend(). - * - * Returns: - * Zero on success, negative error code on failure. - */ -int tinydrm_resume(struct tinydrm_device *tdev) -{ - struct drm_atomic_state *state = tdev->suspend_state; - int ret; - - if (!state) { - DRM_ERROR("Failed to resume: state is not set\n"); - return -EINVAL; - } - - tdev->suspend_state = NULL; - - ret = drm_atomic_helper_resume(tdev->drm, state); - if (ret) { - DRM_ERROR("Error resuming state: %d\n", ret); - return ret; - } - - drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 0); - - return 0; -} -EXPORT_SYMBOL(tinydrm_resume); - MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/tinydrm/ili9225.c b/drivers/gpu/drm/tinydrm/ili9225.c new file mode 100644 index 000000000000..e8f1b3af3852 --- /dev/null +++ b/drivers/gpu/drm/tinydrm/ili9225.c @@ -0,0 +1,469 @@ +/* + * DRM driver for Ilitek ILI9225 panels + * + * Copyright 2017 David Lechner <david@lechnology.com> + * + * Some code copied from mipi-dbi.c + * Copyright 2016 Noralf Trønnes + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/delay.h> +#include <linux/dma-buf.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/spi/spi.h> +#include <video/mipi_display.h> + +#include <drm/drm_fb_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/tinydrm/mipi-dbi.h> +#include <drm/tinydrm/tinydrm-helpers.h> + +#define ILI9225_DRIVER_READ_CODE 0x00 +#define ILI9225_DRIVER_OUTPUT_CONTROL 0x01 +#define ILI9225_LCD_AC_DRIVING_CONTROL 0x02 +#define ILI9225_ENTRY_MODE 0x03 +#define ILI9225_DISPLAY_CONTROL_1 0x07 +#define ILI9225_BLANK_PERIOD_CONTROL_1 0x08 +#define ILI9225_FRAME_CYCLE_CONTROL 0x0b +#define ILI9225_INTERFACE_CONTROL 0x0c +#define ILI9225_OSCILLATION_CONTROL 0x0f +#define ILI9225_POWER_CONTROL_1 0x10 +#define ILI9225_POWER_CONTROL_2 0x11 +#define ILI9225_POWER_CONTROL_3 0x12 +#define ILI9225_POWER_CONTROL_4 0x13 +#define ILI9225_POWER_CONTROL_5 0x14 +#define ILI9225_VCI_RECYCLING 0x15 +#define ILI9225_RAM_ADDRESS_SET_1 0x20 +#define ILI9225_RAM_ADDRESS_SET_2 0x21 +#define ILI9225_WRITE_DATA_TO_GRAM 0x22 +#define ILI9225_SOFTWARE_RESET 0x28 +#define ILI9225_GATE_SCAN_CONTROL 0x30 +#define ILI9225_VERTICAL_SCROLL_1 0x31 +#define ILI9225_VERTICAL_SCROLL_2 0x32 +#define ILI9225_VERTICAL_SCROLL_3 0x33 +#define ILI9225_PARTIAL_DRIVING_POS_1 0x34 +#define ILI9225_PARTIAL_DRIVING_POS_2 0x35 +#define ILI9225_HORIZ_WINDOW_ADDR_1 0x36 +#define ILI9225_HORIZ_WINDOW_ADDR_2 0x37 +#define ILI9225_VERT_WINDOW_ADDR_1 0x38 +#define ILI9225_VERT_WINDOW_ADDR_2 0x39 +#define ILI9225_GAMMA_CONTROL_1 0x50 +#define ILI9225_GAMMA_CONTROL_2 0x51 +#define ILI9225_GAMMA_CONTROL_3 0x52 +#define ILI9225_GAMMA_CONTROL_4 0x53 +#define ILI9225_GAMMA_CONTROL_5 0x54 +#define ILI9225_GAMMA_CONTROL_6 0x55 +#define ILI9225_GAMMA_CONTROL_7 0x56 +#define ILI9225_GAMMA_CONTROL_8 0x57 +#define ILI9225_GAMMA_CONTROL_9 0x58 +#define ILI9225_GAMMA_CONTROL_10 0x59 + +static inline int ili9225_command(struct mipi_dbi *mipi, u8 cmd, u16 data) +{ + u8 par[2] = { data >> 8, data & 0xff }; + + return mipi_dbi_command_buf(mipi, cmd, par, 2); +} + +static int ili9225_fb_dirty(struct drm_framebuffer *fb, + struct drm_file *file_priv, unsigned int flags, + unsigned int color, struct drm_clip_rect *clips, + unsigned int num_clips) +{ + struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0); + struct tinydrm_device *tdev = fb->dev->dev_private; + struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev); + bool swap = mipi->swap_bytes; + struct drm_clip_rect clip; + u16 x_start, y_start; + u16 x1, x2, y1, y2; + int ret = 0; + bool full; + void *tr; + + mutex_lock(&tdev->dirty_lock); + + if (!mipi->enabled) + goto out_unlock; + + /* fbdev can flush even when we're not interested */ + if (tdev->pipe.plane.fb != fb) + goto out_unlock; + + full = tinydrm_merge_clips(&clip, clips, num_clips, flags, + fb->width, fb->height); + + DRM_DEBUG("Flushing [FB:%d] x1=%u, x2=%u, y1=%u, y2=%u\n", fb->base.id, + clip.x1, clip.x2, clip.y1, clip.y2); + + if (!mipi->dc || !full || swap || + fb->format->format == DRM_FORMAT_XRGB8888) { + tr = mipi->tx_buf; + ret = mipi_dbi_buf_copy(mipi->tx_buf, fb, &clip, swap); + if (ret) + goto out_unlock; + } else { + tr = cma_obj->vaddr; + } + + switch (mipi->rotation) { + default: + x1 = clip.x1; + x2 = clip.x2 - 1; + y1 = clip.y1; + y2 = clip.y2 - 1; + x_start = x1; + y_start = y1; + break; + case 90: + x1 = clip.y1; + x2 = clip.y2 - 1; + y1 = fb->width - clip.x2; + y2 = fb->width - clip.x1 - 1; + x_start = x1; + y_start = y2; + break; + case 180: + x1 = fb->width - clip.x2; + x2 = fb->width - clip.x1 - 1; + y1 = fb->height - clip.y2; + y2 = fb->height - clip.y1 - 1; + x_start = x2; + y_start = y2; + break; + case 270: + x1 = fb->height - clip.y2; + x2 = fb->height - clip.y1 - 1; + y1 = clip.x1; + y2 = clip.x2 - 1; + x_start = x2; + y_start = y1; + break; + } + + ili9225_command(mipi, ILI9225_HORIZ_WINDOW_ADDR_1, x2); + ili9225_command(mipi, ILI9225_HORIZ_WINDOW_ADDR_2, x1); + ili9225_command(mipi, ILI9225_VERT_WINDOW_ADDR_1, y2); + ili9225_command(mipi, ILI9225_VERT_WINDOW_ADDR_2, y1); + + ili9225_command(mipi, ILI9225_RAM_ADDRESS_SET_1, x_start); + ili9225_command(mipi, ILI9225_RAM_ADDRESS_SET_2, y_start); + + ret = mipi_dbi_command_buf(mipi, ILI9225_WRITE_DATA_TO_GRAM, tr, + (clip.x2 - clip.x1) * (clip.y2 - clip.y1) * 2); + +out_unlock: + mutex_unlock(&tdev->dirty_lock); + + if (ret) + dev_err_once(fb->dev->dev, "Failed to update display %d\n", + ret); + + return ret; +} + +static const struct drm_framebuffer_funcs ili9225_fb_funcs = { + .destroy = drm_gem_fb_destroy, + .create_handle = drm_gem_fb_create_handle, + .dirty = ili9225_fb_dirty, +}; + +static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe, + struct drm_crtc_state *crtc_state) +{ + struct tinydrm_device *tdev = pipe_to_tinydrm(pipe); + struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev); + struct drm_framebuffer *fb = pipe->plane.fb; + struct device *dev = tdev->drm->dev; + int ret; + u8 am_id; + + DRM_DEBUG_KMS("\n"); + + mipi_dbi_hw_reset(mipi); + + /* + * There don't seem to be two example init sequences that match, so + * using the one from the popular Arduino library for this display. + * https://github.com/Nkawu/TFT_22_ILI9225/blob/master/src/TFT_22_ILI9225.cpp + */ + + ret = ili9225_command(mipi, ILI9225_POWER_CONTROL_1, 0x0000); + if (ret) { + DRM_DEV_ERROR(dev, "Error sending command %d\n", ret); + return; + } + ili9225_command(mipi, ILI9225_POWER_CONTROL_2, 0x0000); + ili9225_command(mipi, ILI9225_POWER_CONTROL_3, 0x0000); + ili9225_command(mipi, ILI9225_POWER_CONTROL_4, 0x0000); + ili9225_command(mipi, ILI9225_POWER_CONTROL_5, 0x0000); + + msleep(40); + + ili9225_command(mipi, ILI9225_POWER_CONTROL_2, 0x0018); + ili9225_command(mipi, ILI9225_POWER_CONTROL_3, 0x6121); + ili9225_command(mipi, ILI9225_POWER_CONTROL_4, 0x006f); + ili9225_command(mipi, ILI9225_POWER_CONTROL_5, 0x495f); + ili9225_command(mipi, ILI9225_POWER_CONTROL_1, 0x0800); + + msleep(10); + + ili9225_command(mipi, ILI9225_POWER_CONTROL_2, 0x103b); + + msleep(50); + + switch (mipi->rotation) { + default: + am_id = 0x30; + break; + case 90: + am_id = 0x18; + break; + case 180: + am_id = 0x00; + break; + case 270: + am_id = 0x28; + break; + } + ili9225_command(mipi, ILI9225_DRIVER_OUTPUT_CONTROL, 0x011c); + ili9225_command(mipi, ILI9225_LCD_AC_DRIVING_CONTROL, 0x0100); + ili9225_command(mipi, ILI9225_ENTRY_MODE, 0x1000 | am_id); + ili9225_command(mipi, ILI9225_DISPLAY_CONTROL_1, 0x0000); + ili9225_command(mipi, ILI9225_BLANK_PERIOD_CONTROL_1, 0x0808); + ili9225_command(mipi, ILI9225_FRAME_CYCLE_CONTROL, 0x1100); + ili9225_command(mipi, ILI9225_INTERFACE_CONTROL, 0x0000); + ili9225_command(mipi, ILI9225_OSCILLATION_CONTROL, 0x0d01); + ili9225_command(mipi, ILI9225_VCI_RECYCLING, 0x0020); + ili9225_command(mipi, ILI9225_RAM_ADDRESS_SET_1, 0x0000); + ili9225_command(mipi, ILI9225_RAM_ADDRESS_SET_2, 0x0000); + + ili9225_command(mipi, ILI9225_GATE_SCAN_CONTROL, 0x0000); + ili9225_command(mipi, ILI9225_VERTICAL_SCROLL_1, 0x00db); + ili9225_command(mipi, ILI9225_VERTICAL_SCROLL_2, 0x0000); + ili9225_command(mipi, ILI9225_VERTICAL_SCROLL_3, 0x0000); + ili9225_command(mipi, ILI9225_PARTIAL_DRIVING_POS_1, 0x00db); + ili9225_command(mipi, ILI9225_PARTIAL_DRIVING_POS_2, 0x0000); + + ili9225_command(mipi, ILI9225_GAMMA_CONTROL_1, 0x0000); + ili9225_command(mipi, ILI9225_GAMMA_CONTROL_2, 0x0808); + ili9225_command(mipi, ILI9225_GAMMA_CONTROL_3, 0x080a); + ili9225_command(mipi, ILI9225_GAMMA_CONTROL_4, 0x000a); + ili9225_command(mipi, ILI9225_GAMMA_CONTROL_5, 0x0a08); + ili9225_command(mipi, ILI9225_GAMMA_CONTROL_6, 0x0808); + ili9225_command(mipi, ILI9225_GAMMA_CONTROL_7, 0x0000); + ili9225_command(mipi, ILI9225_GAMMA_CONTROL_8, 0x0a00); + ili9225_command(mipi, ILI9225_GAMMA_CONTROL_9, 0x0710); + ili9225_command(mipi, ILI9225_GAMMA_CONTROL_10, 0x0710); + + ili9225_command(mipi, ILI9225_DISPLAY_CONTROL_1, 0x0012); + + msleep(50); + + ili9225_command(mipi, ILI9225_DISPLAY_CONTROL_1, 0x1017); + + mipi->enabled = true; + + if (fb) + fb->funcs->dirty(fb, NULL, 0, 0, NULL, 0); +} + +static void ili9225_pipe_disable(struct drm_simple_display_pipe *pipe) +{ + struct tinydrm_device *tdev = pipe_to_tinydrm(pipe); + struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev); + + DRM_DEBUG_KMS("\n"); + + if (!mipi->enabled) + return; + + ili9225_command(mipi, ILI9225_DISPLAY_CONTROL_1, 0x0000); + msleep(50); + ili9225_command(mipi, ILI9225_POWER_CONTROL_2, 0x0007); + msleep(50); + ili9225_command(mipi, ILI9225_POWER_CONTROL_1, 0x0a02); + + mipi->enabled = false; +} + +static int ili9225_dbi_command(struct mipi_dbi *mipi, u8 cmd, u8 *par, + size_t num) +{ + struct spi_device *spi = mipi->spi; + unsigned int bpw = 8; + u32 speed_hz; + int ret; + + gpiod_set_value_cansleep(mipi->dc, 0); + speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1); + ret = tinydrm_spi_transfer(spi, speed_hz, NULL, 8, &cmd, 1); + if (ret || !num) + return ret; + + if (cmd == ILI9225_WRITE_DATA_TO_GRAM && !mipi->swap_bytes) + bpw = 16; + + gpiod_set_value_cansleep(mipi->dc, 1); + speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num); + + return tinydrm_spi_transfer(spi, speed_hz, NULL, bpw, par, num); +} + +static const u32 ili9225_formats[] = { + DRM_FORMAT_RGB565, + DRM_FORMAT_XRGB8888, +}; + +static int ili9225_init(struct device *dev, struct mipi_dbi *mipi, + const struct drm_simple_display_pipe_funcs *pipe_funcs, + struct drm_driver *driver, + const struct drm_display_mode *mode, + unsigned int rotation) +{ + size_t bufsize = mode->vdisplay * mode->hdisplay * sizeof(u16); + struct tinydrm_device *tdev = &mipi->tinydrm; + int ret; + + if (!mipi->command) + return -EINVAL; + + mutex_init(&mipi->cmdlock); + + mipi->tx_buf = devm_kmalloc(dev, bufsize, GFP_KERNEL); + if (!mipi->tx_buf) + return -ENOMEM; + + ret = devm_tinydrm_init(dev, tdev, &ili9225_fb_funcs, driver); + if (ret) + return ret; + + ret = tinydrm_display_pipe_init(tdev, pipe_funcs, + DRM_MODE_CONNECTOR_VIRTUAL, + ili9225_formats, + ARRAY_SIZE(ili9225_formats), mode, + rotation); + if (ret) + return ret; + + tdev->drm->mode_config.preferred_depth = 16; + mipi->rotation = rotation; + + drm_mode_config_reset(tdev->drm); + + DRM_DEBUG_KMS("preferred_depth=%u, rotation = %u\n", + tdev->drm->mode_config.preferred_depth, rotation); + + return 0; +} + +static const struct drm_simple_display_pipe_funcs ili9225_pipe_funcs = { + .enable = ili9225_pipe_enable, + .disable = ili9225_pipe_disable, + .update = tinydrm_display_pipe_update, + .prepare_fb = tinydrm_display_pipe_prepare_fb, +}; + +static const struct drm_display_mode ili9225_mode = { + TINYDRM_MODE(176, 220, 35, 44), +}; + +DEFINE_DRM_GEM_CMA_FOPS(ili9225_fops); + +static struct drm_driver ili9225_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | + DRIVER_ATOMIC, + .fops = &ili9225_fops, + TINYDRM_GEM_DRIVER_OPS, + .lastclose = drm_fb_helper_lastclose, + .name = "ili9225", + .desc = "Ilitek ILI9225", + .date = "20171106", + .major = 1, + .minor = 0, +}; + +static const struct of_device_id ili9225_of_match[] = { + { .compatible = "ilitek,ili9225-2.2in-176x220" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ili9225_of_match); + +static const struct spi_device_id ili9225_id[] = { + { "ili9225-2.2in-176x220", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(spi, ili9225_id); + +static int ili9225_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct mipi_dbi *mipi; + struct gpio_desc *rs; + u32 rotation = 0; + int ret; + + mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL); + if (!mipi) + return -ENOMEM; + + mipi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(mipi->reset)) { + DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n"); + return PTR_ERR(mipi->reset); + } + + rs = devm_gpiod_get(dev, "rs", GPIOD_OUT_LOW); + if (IS_ERR(rs)) { + DRM_DEV_ERROR(dev, "Failed to get gpio 'rs'\n"); + return PTR_ERR(rs); + } + + device_property_read_u32(dev, "rotation", &rotation); + + ret = mipi_dbi_spi_init(spi, mipi, rs); + if (ret) + return ret; + + /* override the command function set in mipi_dbi_spi_init() */ + mipi->command = ili9225_dbi_command; + + ret = ili9225_init(&spi->dev, mipi, &ili9225_pipe_funcs, + &ili9225_driver, &ili9225_mode, rotation); + if (ret) + return ret; + + spi_set_drvdata(spi, mipi); + + return devm_tinydrm_register(&mipi->tinydrm); +} + +static void ili9225_shutdown(struct spi_device *spi) +{ + struct mipi_dbi *mipi = spi_get_drvdata(spi); + + tinydrm_shutdown(&mipi->tinydrm); +} + +static struct spi_driver ili9225_spi_driver = { + .driver = { + .name = "ili9225", + .owner = THIS_MODULE, + .of_match_table = ili9225_of_match, + }, + .id_table = ili9225_id, + .probe = ili9225_probe, + .shutdown = ili9225_shutdown, +}; +module_spi_driver(ili9225_spi_driver); + +MODULE_DESCRIPTION("Ilitek ILI9225 DRM driver"); +MODULE_AUTHOR("David Lechner <david@lechnology.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/tinydrm/mi0283qt.c b/drivers/gpu/drm/tinydrm/mi0283qt.c index 6a83b3093254..674d407640be 100644 --- a/drivers/gpu/drm/tinydrm/mi0283qt.c +++ b/drivers/gpu/drm/tinydrm/mi0283qt.c @@ -9,6 +9,8 @@ * (at your option) any later version. */ +#include <drm/drm_fb_helper.h> +#include <drm/drm_modeset_helper.h> #include <drm/tinydrm/ili9341.h> #include <drm/tinydrm/mipi-dbi.h> #include <drm/tinydrm/tinydrm-helpers.h> @@ -139,7 +141,7 @@ static struct drm_driver mi0283qt_driver = { DRIVER_ATOMIC, .fops = &mi0283qt_fops, TINYDRM_GEM_DRIVER_OPS, - .lastclose = tinydrm_lastclose, + .lastclose = drm_fb_helper_lastclose, .debugfs_init = mipi_dbi_debugfs_init, .name = "mi0283qt", .desc = "Multi-Inno MI0283QT", @@ -231,7 +233,7 @@ static int __maybe_unused mi0283qt_pm_suspend(struct device *dev) struct mipi_dbi *mipi = dev_get_drvdata(dev); int ret; - ret = tinydrm_suspend(&mipi->tinydrm); + ret = drm_mode_config_helper_suspend(mipi->tinydrm.drm); if (ret) return ret; @@ -249,7 +251,9 @@ static int __maybe_unused mi0283qt_pm_resume(struct device *dev) if (ret) return ret; - return tinydrm_resume(&mipi->tinydrm); + drm_mode_config_helper_resume(mipi->tinydrm.drm); + + return 0; } static const struct dev_pm_ops mi0283qt_pm_ops = { diff --git a/drivers/gpu/drm/tinydrm/mipi-dbi.c b/drivers/gpu/drm/tinydrm/mipi-dbi.c index 347f9b226f26..aa6b6ce56891 100644 --- a/drivers/gpu/drm/tinydrm/mipi-dbi.c +++ b/drivers/gpu/drm/tinydrm/mipi-dbi.c @@ -154,8 +154,18 @@ int mipi_dbi_command_buf(struct mipi_dbi *mipi, u8 cmd, u8 *data, size_t len) } EXPORT_SYMBOL(mipi_dbi_command_buf); -static int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb, - struct drm_clip_rect *clip, bool swap) +/** + * mipi_dbi_buf_copy - Copy a framebuffer, transforming it if necessary + * @dst: The destination buffer + * @fb: The source framebuffer + * @clip: Clipping rectangle of the area to be copied + * @swap: When true, swap MSB/LSB of 16-bit values + * + * Returns: + * Zero on success, negative error code on failure. + */ +int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb, + struct drm_clip_rect *clip, bool swap) { struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0); struct dma_buf_attachment *import_attach = cma_obj->base.import_attach; @@ -192,6 +202,7 @@ static int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb, DMA_FROM_DEVICE); return ret; } +EXPORT_SYMBOL(mipi_dbi_buf_copy); static int mipi_dbi_fb_dirty(struct drm_framebuffer *fb, struct drm_file *file_priv, @@ -444,18 +455,23 @@ EXPORT_SYMBOL(mipi_dbi_display_is_on); #if IS_ENABLED(CONFIG_SPI) -/* +/** + * mipi_dbi_spi_cmd_max_speed - get the maximum SPI bus speed + * @spi: SPI device + * @len: The transfer buffer length. + * * Many controllers have a max speed of 10MHz, but can be pushed way beyond * that. Increase reliability by running pixel data at max speed and the rest * at 10MHz, preventing transfer glitches from messing up the init settings. */ -static u32 mipi_dbi_spi_cmd_max_speed(struct spi_device *spi, size_t len) +u32 mipi_dbi_spi_cmd_max_speed(struct spi_device *spi, size_t len) { if (len > 64) return 0; /* use default */ return min_t(u32, 10000000, spi->max_speed_hz); } +EXPORT_SYMBOL(mipi_dbi_spi_cmd_max_speed); /* * MIPI DBI Type C Option 1 diff --git a/drivers/gpu/drm/tinydrm/st7586.c b/drivers/gpu/drm/tinydrm/st7586.c index 0a2c60da5c0e..5aebfceb740e 100644 --- a/drivers/gpu/drm/tinydrm/st7586.c +++ b/drivers/gpu/drm/tinydrm/st7586.c @@ -17,6 +17,7 @@ #include <linux/spi/spi.h> #include <video/mipi_display.h> +#include <drm/drm_fb_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/tinydrm/mipi-dbi.h> #include <drm/tinydrm/tinydrm-helpers.h> @@ -320,7 +321,7 @@ static struct drm_driver st7586_driver = { DRIVER_ATOMIC, .fops = &st7586_fops, TINYDRM_GEM_DRIVER_OPS, - .lastclose = tinydrm_lastclose, + .lastclose = drm_fb_helper_lastclose, .debugfs_init = mipi_dbi_debugfs_init, .name = "st7586", .desc = "Sitronix ST7586", diff --git a/drivers/gpu/drm/tve200/tve200_drm.h b/drivers/gpu/drm/tve200/tve200_drm.h index 628b79324c48..5c270055bd58 100644 --- a/drivers/gpu/drm/tve200/tve200_drm.h +++ b/drivers/gpu/drm/tve200/tve200_drm.h @@ -103,7 +103,6 @@ struct tve200_drm_dev_private { struct drm_panel *panel; struct drm_bridge *bridge; struct drm_simple_display_pipe pipe; - struct drm_fbdev_cma *fbdev; void *regs; struct clk *pclk; diff --git a/drivers/gpu/drm/tve200/tve200_drv.c b/drivers/gpu/drm/tve200/tve200_drv.c index bd6c9454d767..44911d921864 100644 --- a/drivers/gpu/drm/tve200/tve200_drv.c +++ b/drivers/gpu/drm/tve200/tve200_drv.c @@ -46,6 +46,7 @@ #include <drm/drm_crtc_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_fb_helper.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_panel.h> #include <drm/drm_of.h> @@ -130,8 +131,7 @@ static int tve200_modeset_init(struct drm_device *dev) * Passing in 16 here will make the RGB656 mode the default * Passing in 32 will use XRGB8888 mode */ - priv->fbdev = drm_fbdev_cma_init(dev, 16, - dev->mode_config.num_connector); + drm_fb_cma_fbdev_init(dev, 16, 0); drm_kms_helper_poll_init(dev); goto finish; @@ -146,17 +146,10 @@ finish: DEFINE_DRM_GEM_CMA_FOPS(drm_fops); -static void tve200_lastclose(struct drm_device *dev) -{ - struct tve200_drm_dev_private *priv = dev->dev_private; - - drm_fbdev_cma_restore_mode(priv->fbdev); -} - static struct drm_driver tve200_drm_driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC, - .lastclose = tve200_lastclose, + .lastclose = drm_fb_helper_lastclose, .ioctls = NULL, .fops = &drm_fops, .name = "tve200", @@ -270,8 +263,7 @@ static int tve200_remove(struct platform_device *pdev) struct tve200_drm_dev_private *priv = drm->dev_private; drm_dev_unregister(drm); - if (priv->fbdev) - drm_fbdev_cma_fini(priv->fbdev); + drm_fb_cma_fbdev_fini(drm); if (priv->panel) drm_panel_bridge_remove(priv->bridge); drm_mode_config_cleanup(drm); diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index 4ae45d7dac42..2decc8e2c79f 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -637,7 +637,8 @@ int vc4_bo_inc_usecnt(struct vc4_bo *bo) mutex_lock(&bo->madv_lock); switch (bo->madv) { case VC4_MADV_WILLNEED: - refcount_inc(&bo->usecnt); + if (!refcount_inc_not_zero(&bo->usecnt)) + refcount_set(&bo->usecnt, 1); ret = 0; break; case VC4_MADV_DONTNEED: diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index e3c29729da2e..ceb385fd69c5 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -111,13 +111,6 @@ static int vc4_get_param_ioctl(struct drm_device *dev, void *data, return 0; } -static void vc4_lastclose(struct drm_device *dev) -{ - struct vc4_dev *vc4 = to_vc4_dev(dev); - - drm_fbdev_cma_restore_mode(vc4->fbdev); -} - static const struct vm_operations_struct vc4_vm_ops = { .fault = vc4_fault, .open = drm_gem_vm_open, @@ -159,7 +152,7 @@ static struct drm_driver vc4_drm_driver = { DRIVER_HAVE_IRQ | DRIVER_RENDER | DRIVER_PRIME), - .lastclose = vc4_lastclose, + .lastclose = drm_fb_helper_lastclose, .irq_handler = vc4_irq, .irq_preinstall = vc4_irq_preinstall, .irq_postinstall = vc4_irq_postinstall, @@ -301,12 +294,10 @@ static void vc4_drm_unbind(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct drm_device *drm = platform_get_drvdata(pdev); - struct vc4_dev *vc4 = to_vc4_dev(drm); drm_dev_unregister(drm); - if (vc4->fbdev) - drm_fbdev_cma_fini(vc4->fbdev); + drm_fb_cma_fbdev_fini(drm); drm_mode_config_cleanup(drm); diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 9c0d380c96f2..3af22936d9b3 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -39,8 +39,6 @@ struct vc4_dev { struct vc4_dsi *dsi1; struct vc4_vec *vec; - struct drm_fbdev_cma *fbdev; - struct vc4_hang_state *hang_state; /* The kernel-space BO cache. Tracks buffers that have been diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 6c32c89a83a9..638540943c61 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -888,8 +888,10 @@ vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec) /* If we got force-completed because of GPU reset rather than * through our IRQ handler, signal the fence now. */ - if (exec->fence) + if (exec->fence) { dma_fence_signal(exec->fence); + dma_fence_put(exec->fence); + } if (exec->bo) { for (i = 0; i < exec->bo_count; i++) { diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c index 61b2e5377993..26eddbb62893 100644 --- a/drivers/gpu/drm/vc4/vc4_irq.c +++ b/drivers/gpu/drm/vc4/vc4_irq.c @@ -139,6 +139,7 @@ vc4_irq_finish_render_job(struct drm_device *dev) list_move_tail(&exec->head, &vc4->job_done_list); if (exec->fence) { dma_fence_signal_locked(exec->fence); + dma_fence_put(exec->fence); exec->fence = NULL; } vc4_submit_next_render_job(dev); diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index 50c4959b5bd3..4256f294c346 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -19,17 +19,11 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_plane_helper.h> +#include <drm/drm_fb_helper.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include "vc4_drv.h" -static void vc4_output_poll_changed(struct drm_device *dev) -{ - struct vc4_dev *vc4 = to_vc4_dev(dev); - - drm_fbdev_cma_hotplug_event(vc4->fbdev); -} - static void vc4_atomic_complete_commit(struct drm_atomic_state *state) { @@ -194,7 +188,7 @@ static struct drm_framebuffer *vc4_fb_create(struct drm_device *dev, } static const struct drm_mode_config_funcs vc4_mode_funcs = { - .output_poll_changed = vc4_output_poll_changed, + .output_poll_changed = drm_fb_helper_output_poll_changed, .atomic_check = drm_atomic_helper_check, .atomic_commit = vc4_atomic_commit, .fb_create = vc4_fb_create, @@ -224,12 +218,8 @@ int vc4_kms_load(struct drm_device *dev) drm_mode_config_reset(dev); - if (dev->mode_config.num_connector) { - vc4->fbdev = drm_fbdev_cma_init(dev, 32, - dev->mode_config.num_connector); - if (IS_ERR(vc4->fbdev)) - vc4->fbdev = NULL; - } + if (dev->mode_config.num_connector) + drm_fb_cma_fbdev_init(dev, 32, 0); drm_kms_helper_poll_init(dev); diff --git a/drivers/gpu/drm/zte/zx_drm_drv.c b/drivers/gpu/drm/zte/zx_drm_drv.c index e8b8266c0cde..6f4205e80378 100644 --- a/drivers/gpu/drm/zte/zx_drm_drv.c +++ b/drivers/gpu/drm/zte/zx_drm_drv.c @@ -29,37 +29,19 @@ #include "zx_drm_drv.h" #include "zx_vou.h" -struct zx_drm_private { - struct drm_fbdev_cma *fbdev; -}; - -static void zx_drm_fb_output_poll_changed(struct drm_device *drm) -{ - struct zx_drm_private *priv = drm->dev_private; - - drm_fbdev_cma_hotplug_event(priv->fbdev); -} - static const struct drm_mode_config_funcs zx_drm_mode_config_funcs = { .fb_create = drm_gem_fb_create, - .output_poll_changed = zx_drm_fb_output_poll_changed, + .output_poll_changed = drm_fb_helper_output_poll_changed, .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, }; -static void zx_drm_lastclose(struct drm_device *drm) -{ - struct zx_drm_private *priv = drm->dev_private; - - drm_fbdev_cma_restore_mode(priv->fbdev); -} - DEFINE_DRM_GEM_CMA_FOPS(zx_drm_fops); static struct drm_driver zx_drm_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, - .lastclose = zx_drm_lastclose, + .lastclose = drm_fb_helper_lastclose, .gem_free_object_unlocked = drm_gem_cma_free_object, .gem_vm_ops = &drm_gem_cma_vm_ops, .dumb_create = drm_gem_cma_dumb_create, @@ -83,18 +65,12 @@ static struct drm_driver zx_drm_driver = { static int zx_drm_bind(struct device *dev) { struct drm_device *drm; - struct zx_drm_private *priv; int ret; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - drm = drm_dev_alloc(&zx_drm_driver, dev); if (IS_ERR(drm)) return PTR_ERR(drm); - drm->dev_private = priv; dev_set_drvdata(dev, drm); drm_mode_config_init(drm); @@ -125,12 +101,9 @@ static int zx_drm_bind(struct device *dev) drm_mode_config_reset(drm); drm_kms_helper_poll_init(drm); - priv->fbdev = drm_fbdev_cma_init(drm, 32, - drm->mode_config.num_connector); - if (IS_ERR(priv->fbdev)) { - ret = PTR_ERR(priv->fbdev); + ret = drm_fb_cma_fbdev_init(drm, 32, 0); + if (ret) { DRM_DEV_ERROR(dev, "failed to init cma fbdev: %d\n", ret); - priv->fbdev = NULL; goto out_poll_fini; } @@ -141,10 +114,7 @@ static int zx_drm_bind(struct device *dev) return 0; out_fbdev_fini: - if (priv->fbdev) { - drm_fbdev_cma_fini(priv->fbdev); - priv->fbdev = NULL; - } + drm_fb_cma_fbdev_fini(drm); out_poll_fini: drm_kms_helper_poll_fini(drm); drm_mode_config_cleanup(drm); @@ -152,7 +122,6 @@ out_unbind: component_unbind_all(dev, drm); out_unregister: dev_set_drvdata(dev, NULL); - drm->dev_private = NULL; drm_dev_unref(drm); return ret; } @@ -160,18 +129,13 @@ out_unregister: static void zx_drm_unbind(struct device *dev) { struct drm_device *drm = dev_get_drvdata(dev); - struct zx_drm_private *priv = drm->dev_private; drm_dev_unregister(drm); - if (priv->fbdev) { - drm_fbdev_cma_fini(priv->fbdev); - priv->fbdev = NULL; - } + drm_fb_cma_fbdev_fini(drm); drm_kms_helper_poll_fini(drm); drm_mode_config_cleanup(drm); component_unbind_all(dev, drm); dev_set_drvdata(dev, NULL); - drm->dev_private = NULL; drm_dev_unref(drm); } |